From f92975344b414cde24a64190b70918e9a495af69 Mon Sep 17 00:00:00 2001 From: TD-er Date: Sat, 13 Nov 2021 00:50:24 +0100 Subject: [PATCH 001/147] [ESP32] Update to IDF 4.4 Arduino-ESP32 2.0.1 Including lots of WiFi fixes to be able to get a stable WiFi connection on the new ESP32 SDK. --- platformio_core_defs.ini | 8 ++- platformio_esp32_envs.ini | 2 +- src/src/Commands/WiFi.cpp | 2 +- src/src/DataStructs/SettingsStruct.cpp | 4 ++ src/src/ESPEasyCore/ESPEasyNetwork.cpp | 2 + src/src/ESPEasyCore/ESPEasyWifi.cpp | 72 ++++++++++++------- src/src/ESPEasyCore/ESPEasyWifi.h | 4 +- .../ESPEasyCore/ESPEasyWifi_ProcessEvent.cpp | 3 +- src/src/WebServer/AdvancedConfigPage.cpp | 2 +- 9 files changed, 66 insertions(+), 33 deletions(-) diff --git a/platformio_core_defs.ini b/platformio_core_defs.ini index bedee8a98d..6ef11b9012 100644 --- a/platformio_core_defs.ini +++ b/platformio_core_defs.ini @@ -160,7 +160,8 @@ build_flags = -DESP32_STAGE [core_esp32_3_3_2_esp32s2] platform = https://github.com/platformio/platform-espressif32.git#feature/arduino-idf-master -platform_packages = framework-arduinoespressif32 @ https://github.com/Jason2866/arduino-esp32/releases/download/v.2.0-post/framework-arduinoespressif32_i2c.zip +platform_packages = framework-arduinoespressif32 @ https://github.com/tasmota/arduino-esp32/releases/download/2.0.1/framework-arduinoespressif32-release_IDF4.4.tar.gz + platformio/tool-esptoolpy @ https://github.com/tasmota/esptool/releases/download/v3.2/esptool-v3.2.zip build_flags = -DESP32_STAGE @@ -170,6 +171,7 @@ platform_packages = framework-arduinoespressif32 @ https://github.com/Ja [core_esp32_stage] -platform = https://github.com/platformio/platform-espressif32.git#feature/arduino-upstream -platform_packages = framework-arduinoespressif32 @ https://github.com/Jason2866/arduino-esp32/releases/download/v.2.0-post/framework-arduinoespressif32_i2c.zip +platform = https://github.com/platformio/platform-espressif32.git#feature/arduino-idf-master +platform_packages = framework-arduinoespressif32 @ https://github.com/tasmota/arduino-esp32/releases/download/2.0.1/framework-arduinoespressif32-release_IDF4.4.tar.gz + platformio/tool-esptoolpy @ https://github.com/tasmota/esptool/releases/download/v3.2/esptool-v3.2.zip build_flags = -DESP32_STAGE diff --git a/platformio_esp32_envs.ini b/platformio_esp32_envs.ini index 727f5d22bb..01d645fe80 100644 --- a/platformio_esp32_envs.ini +++ b/platformio_esp32_envs.ini @@ -9,7 +9,7 @@ lib_ignore = ESP8266WiFi, ESP8266Ping, ESP8266WebServer, ESP8266H [esp32_common] -extends = common, core_esp32_3_3_0 +extends = common, core_esp32_stage lib_deps = td-er/ESPeasySerial @ 2.0.7, adafruit/Adafruit ILI9341 @ ^1.5.6, Adafruit GFX Library, LOLIN_EPD, Adafruit BusIO, VL53L0X @ 1.3.0, SparkFun VL53L1X 4m Laser Distance Sensor @ 1.2.9, td-er/SparkFun MAX1704x Fuel Gauge Arduino Library @ ^1.0.1, ArduinoOTA, ESP32HTTPUpdateServer lib_ignore = ${esp32_always.lib_ignore}, ESP32_ping, IRremoteESP8266, HeatpumpIR board_build.f_flash = 80000000L diff --git a/src/src/Commands/WiFi.cpp b/src/src/Commands/WiFi.cpp index 9d27dbbb5d..cff91030e0 100644 --- a/src/src/Commands/WiFi.cpp +++ b/src/src/Commands/WiFi.cpp @@ -55,7 +55,7 @@ String Command_Wifi_Key2(struct EventStruct *event, const char *Line) const __FlashStringHelper * Command_Wifi_Scan(struct EventStruct *event, const char *Line) { - WifiScan(); + WiFiScan_log_to_serial(); return return_command_success(); } diff --git a/src/src/DataStructs/SettingsStruct.cpp b/src/src/DataStructs/SettingsStruct.cpp index d0aae1bfe8..5457f7d1b7 100644 --- a/src/src/DataStructs/SettingsStruct.cpp +++ b/src/src/DataStructs/SettingsStruct.cpp @@ -83,7 +83,11 @@ void SettingsStruct_tmpl::EcoPowerMode(bool value) { template bool SettingsStruct_tmpl::WifiNoneSleep() const { + #ifdef ESP32 + return true; + #else return bitRead(VariousBits1, 7); + #endif } template diff --git a/src/src/ESPEasyCore/ESPEasyNetwork.cpp b/src/src/ESPEasyCore/ESPEasyNetwork.cpp index 537993a9f0..a89921f2b6 100644 --- a/src/src/ESPEasyCore/ESPEasyNetwork.cpp +++ b/src/src/ESPEasyCore/ESPEasyNetwork.cpp @@ -222,10 +222,12 @@ String WifiSTAmacAddress() { void CheckRunningServices() { set_mDNS(); + #ifdef ESP8266 if (active_network_medium == NetworkMedium_t::WIFI) { SetWiFiTXpower(); } + #endif } #ifdef HAS_ETHERNET diff --git a/src/src/ESPEasyCore/ESPEasyWifi.cpp b/src/src/ESPEasyCore/ESPEasyWifi.cpp index 36a7b301a4..ffd9a44534 100644 --- a/src/src/ESPEasyCore/ESPEasyWifi.cpp +++ b/src/src/ESPEasyCore/ESPEasyWifi.cpp @@ -121,14 +121,11 @@ bool ESPEasyWiFi_t::connectSTA() { } WiFiEventData.warnedNoValidWiFiSettings = false; setSTA(true); - char hostname[40]; - safe_strncpy(hostname, NetworkCreateRFCCompliantHostname().c_str(), sizeof(hostname)); #if defined(ESP8266) - wifi_station_set_hostname(hostname); + wifi_station_set_hostname(NetworkCreateRFCCompliantHostname().c_str()); #endif // if defined(ESP8266) #if defined(ESP32) - WiFi.setHostname(hostname); WiFi.config(INADDR_NONE, INADDR_NONE, INADDR_NONE); #endif // if defined(ESP32) setConnectionSpeed(); @@ -277,7 +274,9 @@ bool WiFiConnected() { STOP_TIMER(WIFI_ISCONNECTED_STATS); recursiveCall = false; // Only return true after some time since it got connected. + #ifdef ESP8266 SetWiFiTXpower(); + #endif return WiFiEventData.wifi_considered_stable || WiFiEventData.lastConnectMoment.timeoutReached(100); } @@ -285,6 +284,7 @@ bool WiFiConnected() { // Timer reached, so enable AP mode. if (!WifiIsAP(WiFi.getMode())) { if (!Settings.DoNotStartAP()) { + WifiScan(false); setAP(true); } } @@ -397,10 +397,12 @@ void AttemptWiFiConnect() { RTC.clearLastWiFi(); float tx_pwr = 0; // Will be set higher based on RSSI when needed. // FIXME TD-er: Must check WiFiEventData.wifi_connect_attempt to increase TX power + #ifdef ESP8266 if (Settings.UseMaxTXpowerForSending()) { tx_pwr = Settings.getWiFi_TX_power(); } SetWiFiTXpower(tx_pwr, candidate.rssi); + #endif // Start connect attempt now, so no longer needed to attempt new connection. WiFiEventData.wifiConnectAttemptNeeded = false; if (candidate.allowQuickConnect() && !candidate.isHidden) { @@ -414,11 +416,14 @@ void AttemptWiFiConnect() { } else { if (!wifiAPmodeActivelyUsed() || WiFiEventData.wifiSetupConnect) { if (!prepareWiFi()) { - return; + //return; + } + + if (WiFiScanAllowed()) { + // Maybe not scan async to give the ESP some slack in power consumption? + const bool async = false; + WifiScan(async); } - // Maybe not scan async to give the ESP some slack in power consumption? - const bool async = false; - WifiScan(async); } } @@ -440,6 +445,7 @@ bool prepareWiFi() { // No need to wait longer to start AP mode. if (!Settings.DoNotStartAP()) { + WifiScan(false); setAP(true); } return false; @@ -447,14 +453,11 @@ bool prepareWiFi() { WiFiEventData.warnedNoValidWiFiSettings = false; setSTA(true); - char hostname[40]; - safe_strncpy(hostname, NetworkCreateRFCCompliantHostname().c_str(), sizeof(hostname)); #if defined(ESP8266) - wifi_station_set_hostname(hostname); + wifi_station_set_hostname(NetworkCreateRFCCompliantHostname().c_str()); #endif // if defined(ESP8266) #if defined(ESP32) - WiFi.setHostname(hostname); WiFi.config(INADDR_NONE, INADDR_NONE, INADDR_NONE); #endif // if defined(ESP32) setConnectionSpeed(); @@ -568,6 +571,7 @@ void initWiFi() // ******************************************************************************** // Configure WiFi TX power // ******************************************************************************** +#ifdef ESP8266 void SetWiFiTXpower() { SetWiFiTXpower(0.0f); // Just some minimal value, will be adjusted in SetWiFiTXpower } @@ -693,6 +697,7 @@ void SetWiFiTXpower(float dBm, float rssi) { } #endif } +#endif float GetRSSIthreshold(float& maxTXpwr) { maxTXpwr = Settings.getWiFi_TX_power(); @@ -773,6 +778,9 @@ void WifiDisconnect() #endif // if defined(ESP32) WiFiEventData.setWiFiDisconnected(); WiFiEventData.markDisconnect(WIFI_DISCONNECT_REASON_ASSOC_LEAVE); + if (!Settings.UseLastWiFiFromRTC()) { + RTC.clearLastWiFi(); + } delay(1); } @@ -842,6 +850,14 @@ void WifiScan(bool async, uint8_t channel) { if (!WiFiScanAllowed()) { return; } +#ifdef ESP32 + // TD-er: Don't run async scan on ESP32. + // Since IDF 4.4 it seems like the active channel may be messed up when running async scan + // Perform a disconnect after scanning. + // See: https://github.com/letscontrolit/ESPEasy/pull/3579#issuecomment-967021347 + async = false; +#endif + START_TIMER; WiFiEventData.lastScanMoment.setNow(); if (loglevelActiveFor(LOG_LEVEL_INFO)) { @@ -866,7 +882,7 @@ void WifiScan(bool async, uint8_t channel) { FeedSW_watchdog(); } --nrScans; - #ifdef ESP8266 +#ifdef ESP8266 /* { static bool FIRST_SCAN = true; @@ -891,24 +907,30 @@ void WifiScan(bool async, uint8_t channel) { } */ WiFi.scanNetworks(async, show_hidden, channel); - #endif - #ifdef ESP32 +#endif +#ifdef ESP32 const bool passive = false; const uint32_t max_ms_per_chan = 300; WiFi.scanNetworks(async, show_hidden, passive, max_ms_per_chan /*, channel */); - #endif +#endif if (!async) { FeedSW_watchdog(); processScanDone(); } } STOP_TIMER(async ? WIFI_SCAN_ASYNC : WIFI_SCAN_SYNC); + +#ifdef ESP32 + RTC.clearLastWiFi(); + WifiDisconnect(); +#endif + } // ******************************************************************************** // Scan all Wifi Access Points // ******************************************************************************** -void WifiScan() +void WiFiScan_log_to_serial() { // Direct Serial is allowed here, since this function will only be called from serial input. serialPrintln(F("WIFI : SSID Scan start")); @@ -977,7 +999,6 @@ void setAP(bool enable) { case WIFI_OFF: if (enable) { - WifiScan(false); setWifiMode(WIFI_AP); } break; @@ -1073,15 +1094,17 @@ void setWifiMode(WiFiMode_t wifimode) { if (cur_mode == WIFI_OFF) { WiFiEventData.markWiFiTurnOn(); - + } + if (wifimode != WIFI_OFF) { #if defined(ESP32) - esp_wifi_set_ps(WIFI_PS_NONE); + // Needs to be set before calling WiFi.mode() on ESP32 + WiFi.hostname(NetworkCreateRFCCompliantHostname()); #endif - #ifdef ESP8266 + #ifdef ESP8266 // See: https://github.com/esp8266/Arduino/issues/6172#issuecomment-500457407 WiFi.forceSleepWake(); // Make sure WiFi is really active. - #endif // ifdef ESP8266 + #endif delay(100); } @@ -1106,7 +1129,7 @@ void setWifiMode(WiFiMode_t wifimode) { WiFiEventData.markWiFiTurnOn(); delay(100); #if defined(ESP32) - esp_wifi_set_ps(WIFI_PS_MAX_MODEM); +// esp_wifi_set_ps(WIFI_PS_MAX_MODEM); #endif #ifdef ESP8266 WiFi.forceSleepBegin(); @@ -1146,8 +1169,9 @@ void setWifiMode(WiFiMode_t wifimode) { #endif } } - +#ifdef ESP8266 SetWiFiTXpower(); +#endif if (WifiIsSTA(wifimode)) { if (WiFi.getAutoConnect()) { WiFi.setAutoConnect(false); diff --git a/src/src/ESPEasyCore/ESPEasyWifi.h b/src/src/ESPEasyCore/ESPEasyWifi.h index e10bb144ab..3fb202445c 100644 --- a/src/src/ESPEasyCore/ESPEasyWifi.h +++ b/src/src/ESPEasyCore/ESPEasyWifi.h @@ -109,15 +109,17 @@ bool prepareWiFi(); bool checkAndResetWiFi(); void resetWiFi(); void initWiFi(); +#ifdef ESP8266 void SetWiFiTXpower(); void SetWiFiTXpower(float dBm); // 0-20.5 void SetWiFiTXpower(float dBm, float rssi); +#endif float GetRSSIthreshold(float& maxTXpwr); WiFiConnectionProtocol getConnectionProtocol(); void WifiDisconnect(); bool WiFiScanAllowed(); void WifiScan(bool async, uint8_t channel = 0); -void WifiScan(); +void WiFiScan_log_to_serial(); void setSTA(bool enable); void setAP(bool enable); const __FlashStringHelper * getWifiModeString(WiFiMode_t wifimode); diff --git a/src/src/ESPEasyCore/ESPEasyWifi_ProcessEvent.cpp b/src/src/ESPEasyCore/ESPEasyWifi_ProcessEvent.cpp index d12fa8e56a..3e7b8f59d0 100644 --- a/src/src/ESPEasyCore/ESPEasyWifi_ProcessEvent.cpp +++ b/src/src/ESPEasyCore/ESPEasyWifi_ProcessEvent.cpp @@ -252,6 +252,7 @@ void processDisconnect() { WifiDisconnect(); // Needed or else node may not reconnect reliably. if (mustRestartWiFi) { + WifiScan(false); delay(100); setWifiMode(WIFI_OFF); initWiFi(); @@ -259,8 +260,6 @@ void processDisconnect() { if (WiFiEventData.unprocessedWifiEvents()) { handle_unprocessedNetworkEvents(); } - - WifiScan(false); } logConnectionStatus(); WiFiEventData.processedDisconnect = true; diff --git a/src/src/WebServer/AdvancedConfigPage.cpp b/src/src/WebServer/AdvancedConfigPage.cpp index ea88175865..9a89a0b9cc 100644 --- a/src/src/WebServer/AdvancedConfigPage.cpp +++ b/src/src/WebServer/AdvancedConfigPage.cpp @@ -243,8 +243,8 @@ void handle_advanced() { addFormCheckBox(LabelType::RESTART_WIFI_LOST_CONN, Settings.WiFiRestart_connection_lost()); #ifdef ESP8266 addFormCheckBox(LabelType::FORCE_WIFI_NOSLEEP, Settings.WifiNoneSleep()); -#endif // ifdef ESP8266 addFormNote(F("Change WiFi sleep settings requires reboot to activate")); +#endif #ifdef SUPPORT_ARP addFormCheckBox(LabelType::PERIODICAL_GRAT_ARP, Settings.gratuitousARP()); #endif // ifdef SUPPORT_ARP From a888024bdfbd117bce84b33857207377d965c354 Mon Sep 17 00:00:00 2001 From: TD-er Date: Sat, 13 Nov 2021 01:47:57 +0100 Subject: [PATCH 002/147] [ESP32] Allow building LittleFS builds on ESP32 Still cannot build it on Windows. --- platformio.ini | 2 +- platformio_esp32_envs.ini | 25 +++++++++++++++++++++--- src/ESPEasy_common.h | 9 +++++++-- src/src/DataTypes/CPluginID.cpp | 2 +- src/src/DataTypes/ControllerIndex.cpp | 2 +- src/src/DataTypes/DeviceIndex.cpp | 2 +- src/src/DataTypes/ESPEasyTimeSource.cpp | 2 +- src/src/DataTypes/EthernetParameters.cpp | 2 +- src/src/DataTypes/NetworkMedium.cpp | 2 +- src/src/DataTypes/PluginID.cpp | 2 +- src/src/DataTypes/ProtocolIndex.cpp | 2 +- src/src/DataTypes/SettingsType.cpp | 2 +- src/src/DataTypes/TaskIndex.cpp | 2 +- tools/pio/concat_cpp_files.py | 1 + tools/pio/remove_concat_cpp_files.py | 1 + 15 files changed, 42 insertions(+), 16 deletions(-) diff --git a/platformio.ini b/platformio.ini index 4fe062eaef..8090915ae0 100644 --- a/platformio.ini +++ b/platformio.ini @@ -89,7 +89,7 @@ monitor_speed = 115200 targets = extra_scripts = pre:tools/pio/pre_default_check.py ${extra_scripts_esp8266.extra_scripts} -src_filter = +<*> -<.git/> -<.svn/> - - - - -<*/Commands/> -<*/ControllerQueue/> -<*/Globals/> -<*/Helpers/> -<*/PluginStructs/> -<*/WebServer/> +src_filter = +<*> -<.git/> -<.svn/> - - - - -<*/Commands/> -<*/ControllerQueue/> -<*/DataTypes/> -<*/Globals/> -<*/Helpers/> -<*/PluginStructs/> -<*/WebServer/> diff --git a/platformio_esp32_envs.ini b/platformio_esp32_envs.ini index 01d645fe80..614e7cd531 100644 --- a/platformio_esp32_envs.ini +++ b/platformio_esp32_envs.ini @@ -27,6 +27,15 @@ build_flags = ${core_esp32_3_3_0.build_flags} -DCORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_NONE monitor_filters = esp32_exception_decoder +[esp32_LittleFS] +extends = esp32_common +platform_packages = ${esp32_common.platform_packages} + platformio/tool-mklittlefs @ ~1.203.200522 +;lib_deps = ${esp32_common.lib_deps}, https://github.com/lorol/LITTLEFS.git +board_build.filesystem = littlefs + + + ; Custom: 4096k version -------------------------- [env:custom_ESP32_4M316k] @@ -336,9 +345,19 @@ board = lolin_d32_pro ; A Lolin D32 PRO with 16MB Flash, allowing 4MB sketch size, and file storage using LittleFS filesystem [max_ESP32_16M_LittleFS] -extends = max_ESP32_16M -lib_deps = ${max_ESP32_16M.lib_deps}, https://github.com/lorol/LITTLEFS.git -board_build.filesystem = littlefs +extends = esp32_LittleFS +lib_ignore = ${esp32_always.lib_ignore}, ESP32_ping +lib_deps = ${esp32_LittleFS.lib_deps}, VL53L0X +board_upload.maximum_size = 4194304 +build_flags = ${esp32_LittleFS.build_flags} + -DFEATURE_ARDUINO_OTA + -DPLUGIN_BUILD_MAX_ESP32 + -DPLUGIN_BUILD_IR_EXTENDED +; TODO: To enable PS-RAM Support needs more build flags than these 2, for now define ESP32_ENABLE_PSRAM is used to en/disable detecting PS-Ram size on Info page +; -DBOARD_HAS_PSRAM // both flags already enabled for Lolin D32 Pro board by PlatformIO +; -mfix-esp32-psram-cache-issue +board = lolin_d32_pro + ; A Lolin D32 PRO with 16MB Flash, allowing 4MB sketch size, and 1MB assigned to file storage using SPIFFS filesystem [env:max_ESP32_16M1M] diff --git a/src/ESPEasy_common.h b/src/ESPEasy_common.h index 99b619025c..216a007278 100644 --- a/src/ESPEasy_common.h +++ b/src/ESPEasy_common.h @@ -150,8 +150,13 @@ using namespace fs; #ifdef USE_LITTLEFS #ifdef ESP32 - #include - #define ESPEASY_FS LITTLEFS + #if ESP_IDF_VERSION_MAJOR >= 4 + #include + #define ESPEASY_FS LittleFS + #else + #include + #define ESPEASY_FS LITTLEFS + #endif #else #include #define ESPEASY_FS LittleFS diff --git a/src/src/DataTypes/CPluginID.cpp b/src/src/DataTypes/CPluginID.cpp index 9c55bc4887..bf5470fd2f 100644 --- a/src/src/DataTypes/CPluginID.cpp +++ b/src/src/DataTypes/CPluginID.cpp @@ -1,3 +1,3 @@ -#include "CPluginID.h" +#include "../DataTypes/CPluginID.h" cpluginID_t INVALID_C_PLUGIN_ID = 0; diff --git a/src/src/DataTypes/ControllerIndex.cpp b/src/src/DataTypes/ControllerIndex.cpp index ada9588cd6..ff5d61e1ed 100644 --- a/src/src/DataTypes/ControllerIndex.cpp +++ b/src/src/DataTypes/ControllerIndex.cpp @@ -1,4 +1,4 @@ -#include "ControllerIndex.h" +#include "../DataTypes/ControllerIndex.h" #include "../CustomBuild/ESPEasyLimits.h" diff --git a/src/src/DataTypes/DeviceIndex.cpp b/src/src/DataTypes/DeviceIndex.cpp index 3cefdd3892..1141c88e8f 100644 --- a/src/src/DataTypes/DeviceIndex.cpp +++ b/src/src/DataTypes/DeviceIndex.cpp @@ -1,4 +1,4 @@ -#include "DeviceIndex.h" +#include "../DataTypes/DeviceIndex.h" #include "../CustomBuild/ESPEasyLimits.h" diff --git a/src/src/DataTypes/ESPEasyTimeSource.cpp b/src/src/DataTypes/ESPEasyTimeSource.cpp index 1949453736..7ff7bd698b 100644 --- a/src/src/DataTypes/ESPEasyTimeSource.cpp +++ b/src/src/DataTypes/ESPEasyTimeSource.cpp @@ -1,4 +1,4 @@ -#include "ESPEasyTimeSource.h" +#include "../DataTypes/ESPEasyTimeSource.h" #include diff --git a/src/src/DataTypes/EthernetParameters.cpp b/src/src/DataTypes/EthernetParameters.cpp index b4cac0de46..cb25de9dda 100644 --- a/src/src/DataTypes/EthernetParameters.cpp +++ b/src/src/DataTypes/EthernetParameters.cpp @@ -1,4 +1,4 @@ -#include "EthernetParameters.h" +#include "../DataTypes/EthernetParameters.h" bool isValid(EthClockMode_t clockMode) { switch (clockMode) { diff --git a/src/src/DataTypes/NetworkMedium.cpp b/src/src/DataTypes/NetworkMedium.cpp index f8f7fc6af3..61fe0dee9a 100644 --- a/src/src/DataTypes/NetworkMedium.cpp +++ b/src/src/DataTypes/NetworkMedium.cpp @@ -1,4 +1,4 @@ -#include "NetworkMedium.h" +#include "../DataTypes/NetworkMedium.h" bool isValid(NetworkMedium_t medium) { switch (medium) { diff --git a/src/src/DataTypes/PluginID.cpp b/src/src/DataTypes/PluginID.cpp index 1ac2039797..72302df71f 100644 --- a/src/src/DataTypes/PluginID.cpp +++ b/src/src/DataTypes/PluginID.cpp @@ -1,3 +1,3 @@ -#include "PluginID.h" +#include "../DataTypes/PluginID.h" pluginID_t INVALID_PLUGIN_ID = 0; \ No newline at end of file diff --git a/src/src/DataTypes/ProtocolIndex.cpp b/src/src/DataTypes/ProtocolIndex.cpp index ceff2f5bf4..45ec8c3728 100644 --- a/src/src/DataTypes/ProtocolIndex.cpp +++ b/src/src/DataTypes/ProtocolIndex.cpp @@ -1,4 +1,4 @@ -#include "ProtocolIndex.h" +#include "../DataTypes/ProtocolIndex.h" #include "../CustomBuild/ESPEasyLimits.h" diff --git a/src/src/DataTypes/SettingsType.cpp b/src/src/DataTypes/SettingsType.cpp index b7c4850e31..2d2698917c 100644 --- a/src/src/DataTypes/SettingsType.cpp +++ b/src/src/DataTypes/SettingsType.cpp @@ -1,4 +1,4 @@ -#include "SettingsType.h" +#include "../DataTypes/SettingsType.h" #include "../CustomBuild/StorageLayout.h" #include "../DataStructs/NotificationSettingsStruct.h" diff --git a/src/src/DataTypes/TaskIndex.cpp b/src/src/DataTypes/TaskIndex.cpp index 989a32d2ee..7f68f61258 100644 --- a/src/src/DataTypes/TaskIndex.cpp +++ b/src/src/DataTypes/TaskIndex.cpp @@ -1,4 +1,4 @@ -#include "TaskIndex.h" +#include "../DataTypes/TaskIndex.h" diff --git a/tools/pio/concat_cpp_files.py b/tools/pio/concat_cpp_files.py index b33e06a1a2..ad4be8eb8b 100644 --- a/tools/pio/concat_cpp_files.py +++ b/tools/pio/concat_cpp_files.py @@ -52,6 +52,7 @@ def concat_cpp_files(path_to_concat): concat_cpp_files('./src/src/Commands') concat_cpp_files('./src/src/ControllerQueue') +concat_cpp_files('./src/src/DataTypes') concat_cpp_files('./src/src/Globals') concat_cpp_files('./src/src/Helpers') concat_cpp_files('./src/src/PluginStructs') diff --git a/tools/pio/remove_concat_cpp_files.py b/tools/pio/remove_concat_cpp_files.py index 32cc86dcd3..3dfbd6700e 100644 --- a/tools/pio/remove_concat_cpp_files.py +++ b/tools/pio/remove_concat_cpp_files.py @@ -19,6 +19,7 @@ def clear_all_concat_cpp_files(source, target, env): print("\u001b[32m Remove temp concatenated files \u001b[0m") clear_concat_cpp_files('./src/src/Commands') clear_concat_cpp_files('./src/src/ControllerQueue') + clear_concat_cpp_files('./src/src/DataTypes') clear_concat_cpp_files('./src/src/Globals') clear_concat_cpp_files('./src/src/Helpers') clear_concat_cpp_files('./src/src/PluginStructs') From 683d0acae30597605e0fa4931b9467ace675461d Mon Sep 17 00:00:00 2001 From: TD-er Date: Sat, 13 Nov 2021 01:58:35 +0100 Subject: [PATCH 003/147] [Build] Fix case sensitive include --- src/src/Helpers/FS_Helper.h | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/src/Helpers/FS_Helper.h b/src/src/Helpers/FS_Helper.h index 26d8755f79..16528f8683 100644 --- a/src/src/Helpers/FS_Helper.h +++ b/src/src/Helpers/FS_Helper.h @@ -26,12 +26,4 @@ #endif #endif -#if defined(ESP32) - #ifdef USE_LITTLEFS - #include "LITTLEFS.h" - #else - #include "SPIFFS.h" - #endif -#endif - #endif \ No newline at end of file From ea3418c75bf833b4f2d99bb8b644fac58a02db04 Mon Sep 17 00:00:00 2001 From: TD-er Date: Sat, 13 Nov 2021 02:50:35 +0100 Subject: [PATCH 004/147] [Windows Build] Convert IRremoteESP8266 to single cpp file --- .../examples/IRMQTTServer/IRMQTTServer.ino | 2 +- lib/IRremoteESP8266/src/IRac.cpp | 4319 ----------------- lib/IRremoteESP8266/src/IRac.h | 76 +- lib/IRremoteESP8266/src/IRrecv.cpp | 1937 -------- lib/IRremoteESP8266/src/IRrecv.h | 2 +- lib/IRremoteESP8266/src/IRsend.cpp | 1317 ----- lib/IRremoteESP8266/src/IRsend.h | 2 +- lib/IRremoteESP8266/src/IRtext.cpp | 383 -- lib/IRremoteESP8266/src/IRtimer.cpp | 78 - lib/IRremoteESP8266/src/IRutils.cpp | 1266 ----- lib/IRremoteESP8266/src/IRutils.h | 4 +- lib/IRremoteESP8266/src/i18n.h | 2 +- lib/IRremoteESP8266/src/ir_Airwell.cpp | 286 -- lib/IRremoteESP8266/src/ir_Airwell.h | 6 +- lib/IRremoteESP8266/src/ir_Aiwa.cpp | 105 - lib/IRremoteESP8266/src/ir_Amcor.cpp | 354 -- lib/IRremoteESP8266/src/ir_Amcor.h | 6 +- lib/IRremoteESP8266/src/ir_Argo.cpp | 470 -- lib/IRremoteESP8266/src/ir_Argo.h | 6 +- lib/IRremoteESP8266/src/ir_Arris.cpp | 123 - lib/IRremoteESP8266/src/ir_Bose.cpp | 69 - lib/IRremoteESP8266/src/ir_Carrier.cpp | 535 -- lib/IRremoteESP8266/src/ir_Carrier.h | 6 +- lib/IRremoteESP8266/src/ir_Coolix.cpp | 697 --- lib/IRremoteESP8266/src/ir_Coolix.h | 6 +- lib/IRremoteESP8266/src/ir_Corona.cpp | 575 --- lib/IRremoteESP8266/src/ir_Corona.h | 6 +- lib/IRremoteESP8266/src/ir_Daikin.cpp | 3735 -------------- lib/IRremoteESP8266/src/ir_Daikin.h | 8 +- lib/IRremoteESP8266/src/ir_Delonghi.cpp | 470 -- lib/IRremoteESP8266/src/ir_Delonghi.h | 6 +- lib/IRremoteESP8266/src/ir_Denon.cpp | 122 - lib/IRremoteESP8266/src/ir_Dish.cpp | 103 - lib/IRremoteESP8266/src/ir_Doshisha.cpp | 124 - lib/IRremoteESP8266/src/ir_Ecoclim.cpp | 423 -- lib/IRremoteESP8266/src/ir_Ecoclim.h | 6 +- lib/IRremoteESP8266/src/ir_Electra.cpp | 402 -- lib/IRremoteESP8266/src/ir_Electra.h | 6 +- lib/IRremoteESP8266/src/ir_EliteScreens.cpp | 89 - lib/IRremoteESP8266/src/ir_Epson.cpp | 111 - lib/IRremoteESP8266/src/ir_Fujitsu.cpp | 1043 ---- lib/IRremoteESP8266/src/ir_Fujitsu.h | 8 +- lib/IRremoteESP8266/src/ir_GICable.cpp | 95 - lib/IRremoteESP8266/src/ir_GlobalCache.cpp | 63 - lib/IRremoteESP8266/src/ir_Goodweather.cpp | 499 -- lib/IRremoteESP8266/src/ir_Goodweather.h | 6 +- lib/IRremoteESP8266/src/ir_Gree.cpp | 715 --- lib/IRremoteESP8266/src/ir_Gree.h | 6 +- lib/IRremoteESP8266/src/ir_Haier.cpp | 1241 ----- lib/IRremoteESP8266/src/ir_Haier.h | 6 +- lib/IRremoteESP8266/src/ir_Hitachi.cpp | 1580 ------ lib/IRremoteESP8266/src/ir_Hitachi.h | 6 +- lib/IRremoteESP8266/src/ir_Inax.cpp | 73 - lib/IRremoteESP8266/src/ir_JVC.cpp | 131 - lib/IRremoteESP8266/src/ir_Kelon.cpp | 502 -- lib/IRremoteESP8266/src/ir_Kelon.h | 8 +- lib/IRremoteESP8266/src/ir_Kelvinator.cpp | 525 -- lib/IRremoteESP8266/src/ir_Kelvinator.h | 6 +- lib/IRremoteESP8266/src/ir_LG.cpp | 838 ---- lib/IRremoteESP8266/src/ir_LG.h | 8 +- lib/IRremoteESP8266/src/ir_Lasertag.cpp | 116 - lib/IRremoteESP8266/src/ir_Lego.cpp | 106 - lib/IRremoteESP8266/src/ir_Lutron.cpp | 143 - lib/IRremoteESP8266/src/ir_MWM.cpp | 197 - lib/IRremoteESP8266/src/ir_Magiquest.cpp | 154 - lib/IRremoteESP8266/src/ir_Magiquest.h | 4 +- lib/IRremoteESP8266/src/ir_Metz.cpp | 101 - lib/IRremoteESP8266/src/ir_Midea.cpp | 796 --- lib/IRremoteESP8266/src/ir_Midea.h | 6 +- lib/IRremoteESP8266/src/ir_MilesTag2.cpp | 113 - lib/IRremoteESP8266/src/ir_Mirage.cpp | 70 - lib/IRremoteESP8266/src/ir_Mitsubishi.cpp | 1623 ------- lib/IRremoteESP8266/src/ir_Mitsubishi.h | 6 +- .../src/ir_MitsubishiHeavy.cpp | 1050 ---- lib/IRremoteESP8266/src/ir_MitsubishiHeavy.h | 6 +- lib/IRremoteESP8266/src/ir_Multibrackets.cpp | 115 - lib/IRremoteESP8266/src/ir_NEC.cpp | 140 - lib/IRremoteESP8266/src/ir_NEC.h | 2 +- lib/IRremoteESP8266/src/ir_Neoclima.cpp | 608 --- lib/IRremoteESP8266/src/ir_Neoclima.h | 6 +- lib/IRremoteESP8266/src/ir_Nikai.cpp | 74 - lib/IRremoteESP8266/src/ir_Panasonic.cpp | 1341 ----- lib/IRremoteESP8266/src/ir_Panasonic.h | 6 +- lib/IRremoteESP8266/src/ir_Pioneer.cpp | 138 - lib/IRremoteESP8266/src/ir_Pronto.cpp | 107 - lib/IRremoteESP8266/src/ir_RC5_RC6.cpp | 454 -- lib/IRremoteESP8266/src/ir_RCMM.cpp | 164 - lib/IRremoteESP8266/src/ir_Rhoss.cpp | 364 -- lib/IRremoteESP8266/src/ir_Rhoss.h | 6 +- lib/IRremoteESP8266/src/ir_Samsung.cpp | 832 ---- lib/IRremoteESP8266/src/ir_Samsung.h | 6 +- lib/IRremoteESP8266/src/ir_Sanyo.cpp | 978 ---- lib/IRremoteESP8266/src/ir_Sanyo.h | 6 +- lib/IRremoteESP8266/src/ir_Sharp.cpp | 976 ---- lib/IRremoteESP8266/src/ir_Sharp.h | 10 +- lib/IRremoteESP8266/src/ir_Sherwood.cpp | 24 - lib/IRremoteESP8266/src/ir_Sony.cpp | 191 - lib/IRremoteESP8266/src/ir_Symphony.cpp | 95 - lib/IRremoteESP8266/src/ir_Tcl.cpp | 529 -- lib/IRremoteESP8266/src/ir_Tcl.h | 8 +- lib/IRremoteESP8266/src/ir_Technibel.cpp | 408 -- lib/IRremoteESP8266/src/ir_Technibel.h | 6 +- lib/IRremoteESP8266/src/ir_Teco.cpp | 375 -- lib/IRremoteESP8266/src/ir_Teco.h | 6 +- lib/IRremoteESP8266/src/ir_Teknopoint.cpp | 75 - lib/IRremoteESP8266/src/ir_Toshiba.cpp | 529 -- lib/IRremoteESP8266/src/ir_Toshiba.h | 6 +- lib/IRremoteESP8266/src/ir_Transcold.cpp | 500 -- lib/IRremoteESP8266/src/ir_Transcold.h | 6 +- lib/IRremoteESP8266/src/ir_Trotec.cpp | 642 --- lib/IRremoteESP8266/src/ir_Trotec.h | 6 +- lib/IRremoteESP8266/src/ir_Truma.cpp | 340 -- lib/IRremoteESP8266/src/ir_Truma.h | 6 +- lib/IRremoteESP8266/src/ir_Vestel.cpp | 572 --- lib/IRremoteESP8266/src/ir_Vestel.h | 6 +- lib/IRremoteESP8266/src/ir_Voltas.cpp | 516 -- lib/IRremoteESP8266/src/ir_Voltas.h | 6 +- lib/IRremoteESP8266/src/ir_Whirlpool.cpp | 657 --- lib/IRremoteESP8266/src/ir_Whirlpool.h | 6 +- lib/IRremoteESP8266/src/ir_Whynter.cpp | 103 - lib/IRremoteESP8266/src/ir_Xmp.cpp | 226 - lib/IRremoteESP8266/src/ir_Zepeal.cpp | 94 - lib/IRremoteESP8266/test/IRac_test.cpp | 76 +- lib/IRremoteESP8266/test/IRrecv_test.cpp | 10 +- lib/IRremoteESP8266/test/IRrecv_test.h | 2 +- lib/IRremoteESP8266/test/IRsend_test.cpp | 8 +- lib/IRremoteESP8266/test/IRsend_test.h | 6 +- lib/IRremoteESP8266/test/IRutils_test.cpp | 10 +- lib/IRremoteESP8266/test/ir_Airwell_test.cpp | 12 +- lib/IRremoteESP8266/test/ir_Aiwa_test.cpp | 4 +- lib/IRremoteESP8266/test/ir_Amcor_test.cpp | 14 +- lib/IRremoteESP8266/test/ir_Argo_test.cpp | 12 +- lib/IRremoteESP8266/test/ir_Arris_test.cpp | 10 +- lib/IRremoteESP8266/test/ir_Bose_test.cpp | 10 +- lib/IRremoteESP8266/test/ir_Carrier_test.cpp | 10 +- lib/IRremoteESP8266/test/ir_Coolix_test.cpp | 8 +- lib/IRremoteESP8266/test/ir_Corona_test.cpp | 12 +- lib/IRremoteESP8266/test/ir_Daikin_test.cpp | 12 +- lib/IRremoteESP8266/test/ir_Delonghi_test.cpp | 14 +- lib/IRremoteESP8266/test/ir_Denon_test.cpp | 4 +- lib/IRremoteESP8266/test/ir_Dish_test.cpp | 4 +- lib/IRremoteESP8266/test/ir_Doshisha_test.cpp | 10 +- lib/IRremoteESP8266/test/ir_Ecoclim_test.cpp | 14 +- lib/IRremoteESP8266/test/ir_Electra_test.cpp | 12 +- .../test/ir_EliteScreens_test.cpp | 6 +- lib/IRremoteESP8266/test/ir_Epson_test.cpp | 10 +- lib/IRremoteESP8266/test/ir_Fujitsu_test.cpp | 10 +- lib/IRremoteESP8266/test/ir_GICable_test.cpp | 4 +- .../test/ir_GlobalCache_test.cpp | 4 +- .../test/ir_Goodweather_test.cpp | 12 +- lib/IRremoteESP8266/test/ir_Gree_test.cpp | 14 +- lib/IRremoteESP8266/test/ir_Haier_test.cpp | 12 +- lib/IRremoteESP8266/test/ir_Hitachi_test.cpp | 14 +- lib/IRremoteESP8266/test/ir_Inax_test.cpp | 6 +- lib/IRremoteESP8266/test/ir_JVC_test.cpp | 4 +- lib/IRremoteESP8266/test/ir_Kelon_test.cpp | 10 +- .../test/ir_Kelvinator_test.cpp | 14 +- lib/IRremoteESP8266/test/ir_LG_test.cpp | 8 +- lib/IRremoteESP8266/test/ir_Lasertag_test.cpp | 4 +- lib/IRremoteESP8266/test/ir_Lego_test.cpp | 8 +- lib/IRremoteESP8266/test/ir_Lutron_test.cpp | 4 +- lib/IRremoteESP8266/test/ir_MWM_test.cpp | 8 +- .../test/ir_Magiquest_test.cpp | 8 +- lib/IRremoteESP8266/test/ir_Metz_test.cpp | 10 +- lib/IRremoteESP8266/test/ir_Midea_test.cpp | 8 +- .../test/ir_Milestag2_test.cpp | 12 +- lib/IRremoteESP8266/test/ir_Mirage_test.cpp | 10 +- .../test/ir_MitsubishiHeavy_test.cpp | 14 +- .../test/ir_Mitsubishi_test.cpp | 10 +- .../test/ir_Multibrackets_test.cpp | 10 +- lib/IRremoteESP8266/test/ir_NEC_test.cpp | 4 +- lib/IRremoteESP8266/test/ir_Neoclima_test.cpp | 12 +- lib/IRremoteESP8266/test/ir_Nikai_test.cpp | 4 +- .../test/ir_Panasonic_test.cpp | 16 +- lib/IRremoteESP8266/test/ir_Pioneer_test.cpp | 6 +- lib/IRremoteESP8266/test/ir_Pronto_test.cpp | 4 +- lib/IRremoteESP8266/test/ir_RC5_RC6_test.cpp | 4 +- lib/IRremoteESP8266/test/ir_RCMM_test.cpp | 4 +- lib/IRremoteESP8266/test/ir_Rhoss_test.cpp | 14 +- lib/IRremoteESP8266/test/ir_Samsung_test.cpp | 12 +- lib/IRremoteESP8266/test/ir_Sanyo_test.cpp | 12 +- lib/IRremoteESP8266/test/ir_Sharp_test.cpp | 12 +- lib/IRremoteESP8266/test/ir_Sherwood_test.cpp | 4 +- lib/IRremoteESP8266/test/ir_Sony_test.cpp | 4 +- lib/IRremoteESP8266/test/ir_Symphony_test.cpp | 10 +- lib/IRremoteESP8266/test/ir_Tcl_test.cpp | 12 +- .../test/ir_Technibel_test.cpp | 14 +- lib/IRremoteESP8266/test/ir_Teco_test.cpp | 12 +- .../test/ir_Teknopoint_test.cpp | 10 +- lib/IRremoteESP8266/test/ir_Toshiba_test.cpp | 12 +- .../test/ir_Transcold_test.cpp | 10 +- lib/IRremoteESP8266/test/ir_Trotec_test.cpp | 12 +- lib/IRremoteESP8266/test/ir_Truma_test.cpp | 10 +- lib/IRremoteESP8266/test/ir_Vestel_test.cpp | 12 +- lib/IRremoteESP8266/test/ir_Voltas_test.cpp | 12 +- .../test/ir_Whirlpool_test.cpp | 12 +- lib/IRremoteESP8266/test/ir_Whynter_test.cpp | 4 +- lib/IRremoteESP8266/test/ir_Xmp_test.cpp | 10 +- lib/IRremoteESP8266/test/ir_Zepeal_test.cpp | 10 +- .../tools/auto_analyse_raw_data.py | 6 +- .../tools/auto_analyse_raw_data_test.py | 24 +- lib/IRremoteESP8266/tools/gc_decode.cpp | 8 +- lib/IRremoteESP8266/tools/mode2_decode.cpp | 6 +- platformio.ini | 2 +- platformio_esp32_envs.ini | 2 +- 205 files changed, 580 insertions(+), 42614 deletions(-) delete mode 100644 lib/IRremoteESP8266/src/IRac.cpp delete mode 100644 lib/IRremoteESP8266/src/IRrecv.cpp delete mode 100644 lib/IRremoteESP8266/src/IRsend.cpp delete mode 100644 lib/IRremoteESP8266/src/IRtext.cpp delete mode 100644 lib/IRremoteESP8266/src/IRtimer.cpp delete mode 100644 lib/IRremoteESP8266/src/IRutils.cpp delete mode 100644 lib/IRremoteESP8266/src/ir_Airwell.cpp delete mode 100644 lib/IRremoteESP8266/src/ir_Aiwa.cpp delete mode 100644 lib/IRremoteESP8266/src/ir_Amcor.cpp delete mode 100644 lib/IRremoteESP8266/src/ir_Argo.cpp delete mode 100644 lib/IRremoteESP8266/src/ir_Arris.cpp delete mode 100644 lib/IRremoteESP8266/src/ir_Bose.cpp delete mode 100644 lib/IRremoteESP8266/src/ir_Carrier.cpp delete mode 100644 lib/IRremoteESP8266/src/ir_Coolix.cpp delete mode 100644 lib/IRremoteESP8266/src/ir_Corona.cpp delete mode 100644 lib/IRremoteESP8266/src/ir_Daikin.cpp delete mode 100644 lib/IRremoteESP8266/src/ir_Delonghi.cpp delete mode 100644 lib/IRremoteESP8266/src/ir_Denon.cpp delete mode 100644 lib/IRremoteESP8266/src/ir_Dish.cpp delete mode 100644 lib/IRremoteESP8266/src/ir_Doshisha.cpp delete mode 100644 lib/IRremoteESP8266/src/ir_Ecoclim.cpp delete mode 100644 lib/IRremoteESP8266/src/ir_Electra.cpp delete mode 100644 lib/IRremoteESP8266/src/ir_EliteScreens.cpp delete mode 100644 lib/IRremoteESP8266/src/ir_Epson.cpp delete mode 100644 lib/IRremoteESP8266/src/ir_Fujitsu.cpp delete mode 100644 lib/IRremoteESP8266/src/ir_GICable.cpp delete mode 100644 lib/IRremoteESP8266/src/ir_GlobalCache.cpp delete mode 100644 lib/IRremoteESP8266/src/ir_Goodweather.cpp delete mode 100644 lib/IRremoteESP8266/src/ir_Gree.cpp delete mode 100644 lib/IRremoteESP8266/src/ir_Haier.cpp delete mode 100644 lib/IRremoteESP8266/src/ir_Hitachi.cpp delete mode 100644 lib/IRremoteESP8266/src/ir_Inax.cpp delete mode 100644 lib/IRremoteESP8266/src/ir_JVC.cpp delete mode 100644 lib/IRremoteESP8266/src/ir_Kelon.cpp delete mode 100644 lib/IRremoteESP8266/src/ir_Kelvinator.cpp delete mode 100644 lib/IRremoteESP8266/src/ir_LG.cpp delete mode 100644 lib/IRremoteESP8266/src/ir_Lasertag.cpp delete mode 100644 lib/IRremoteESP8266/src/ir_Lego.cpp delete mode 100644 lib/IRremoteESP8266/src/ir_Lutron.cpp delete mode 100644 lib/IRremoteESP8266/src/ir_MWM.cpp delete mode 100644 lib/IRremoteESP8266/src/ir_Magiquest.cpp delete mode 100644 lib/IRremoteESP8266/src/ir_Metz.cpp delete mode 100644 lib/IRremoteESP8266/src/ir_Midea.cpp delete mode 100644 lib/IRremoteESP8266/src/ir_MilesTag2.cpp delete mode 100644 lib/IRremoteESP8266/src/ir_Mirage.cpp delete mode 100644 lib/IRremoteESP8266/src/ir_Mitsubishi.cpp delete mode 100644 lib/IRremoteESP8266/src/ir_MitsubishiHeavy.cpp delete mode 100644 lib/IRremoteESP8266/src/ir_Multibrackets.cpp delete mode 100644 lib/IRremoteESP8266/src/ir_NEC.cpp delete mode 100644 lib/IRremoteESP8266/src/ir_Neoclima.cpp delete mode 100644 lib/IRremoteESP8266/src/ir_Nikai.cpp delete mode 100644 lib/IRremoteESP8266/src/ir_Panasonic.cpp delete mode 100644 lib/IRremoteESP8266/src/ir_Pioneer.cpp delete mode 100644 lib/IRremoteESP8266/src/ir_Pronto.cpp delete mode 100644 lib/IRremoteESP8266/src/ir_RC5_RC6.cpp delete mode 100644 lib/IRremoteESP8266/src/ir_RCMM.cpp delete mode 100644 lib/IRremoteESP8266/src/ir_Rhoss.cpp delete mode 100644 lib/IRremoteESP8266/src/ir_Samsung.cpp delete mode 100644 lib/IRremoteESP8266/src/ir_Sanyo.cpp delete mode 100644 lib/IRremoteESP8266/src/ir_Sharp.cpp delete mode 100644 lib/IRremoteESP8266/src/ir_Sherwood.cpp delete mode 100644 lib/IRremoteESP8266/src/ir_Sony.cpp delete mode 100644 lib/IRremoteESP8266/src/ir_Symphony.cpp delete mode 100644 lib/IRremoteESP8266/src/ir_Tcl.cpp delete mode 100644 lib/IRremoteESP8266/src/ir_Technibel.cpp delete mode 100644 lib/IRremoteESP8266/src/ir_Teco.cpp delete mode 100644 lib/IRremoteESP8266/src/ir_Teknopoint.cpp delete mode 100644 lib/IRremoteESP8266/src/ir_Toshiba.cpp delete mode 100644 lib/IRremoteESP8266/src/ir_Transcold.cpp delete mode 100644 lib/IRremoteESP8266/src/ir_Trotec.cpp delete mode 100644 lib/IRremoteESP8266/src/ir_Truma.cpp delete mode 100644 lib/IRremoteESP8266/src/ir_Vestel.cpp delete mode 100644 lib/IRremoteESP8266/src/ir_Voltas.cpp delete mode 100644 lib/IRremoteESP8266/src/ir_Whirlpool.cpp delete mode 100644 lib/IRremoteESP8266/src/ir_Whynter.cpp delete mode 100644 lib/IRremoteESP8266/src/ir_Xmp.cpp delete mode 100644 lib/IRremoteESP8266/src/ir_Zepeal.cpp diff --git a/lib/IRremoteESP8266/examples/IRMQTTServer/IRMQTTServer.ino b/lib/IRremoteESP8266/examples/IRMQTTServer/IRMQTTServer.ino index 180c4214c3..5a93dfa7a2 100644 --- a/lib/IRremoteESP8266/examples/IRMQTTServer/IRMQTTServer.ino +++ b/lib/IRremoteESP8266/examples/IRMQTTServer/IRMQTTServer.ino @@ -329,7 +329,7 @@ * */ -#include "IRMQTTServer.h" +#include "../src/IRMQTTServer.h" #include #include diff --git a/lib/IRremoteESP8266/src/IRac.cpp b/lib/IRremoteESP8266/src/IRac.cpp deleted file mode 100644 index e3e0874bae..0000000000 --- a/lib/IRremoteESP8266/src/IRac.cpp +++ /dev/null @@ -1,4319 +0,0 @@ -// Copyright 2019 David Conran - -// Provide a universal/standard interface for sending A/C nessages. -// It does not provide complete and maximum granular control but tries -// to offer most common functionality across all supported devices. - -#include "IRac.h" -#ifndef UNIT_TEST -#include -#endif -#include -#ifndef ARDUINO -#include -#endif -#include "IRsend.h" -#include "IRremoteESP8266.h" -#include "IRtext.h" -#include "IRutils.h" -#include "ir_Airwell.h" -#include "ir_Amcor.h" -#include "ir_Argo.h" -#include "ir_Carrier.h" -#include "ir_Coolix.h" -#include "ir_Corona.h" -#include "ir_Daikin.h" -#include "ir_Ecoclim.h" -#include "ir_Electra.h" -#include "ir_Fujitsu.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_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" - -// On the ESP8266 platform we need to use a special version of string handling -// functions to handle the strings stored in the flash address space. -#ifndef STRCASECMP -#if defined(ESP8266) -#define STRCASECMP(LHS, RHS) \ - strcasecmp_P(LHS, reinterpret_cast(RHS)) -#else // ESP8266 -#define STRCASECMP(LHS, RHS) strcasecmp(LHS, RHS) -#endif // ESP8266 -#endif // STRCASECMP - -/// Class constructor -/// @param[in] pin Gpio pin to use when transmitting IR messages. -/// @param[in] inverted true, gpio output defaults to high. false, to low. -/// @param[in] use_modulation true means use frequency modulation. false, don't. -IRac::IRac(const uint16_t pin, const bool inverted, const bool use_modulation) { - _pin = pin; - _inverted = inverted; - _modulation = use_modulation; - initState(&next); - this->markAsSent(); -} - -/// Initialise the given state with the supplied settings. -/// @param[out] state A Ptr to where the settings will be stored. -/// @param[in] vendor The vendor/protocol type. -/// @param[in] model The A/C model if applicable. -/// @param[in] power The power setting. -/// @param[in] mode The operation mode setting. -/// @param[in] degrees The temperature setting in degrees. -/// @param[in] celsius Temperature units. True is Celsius, False is Fahrenheit. -/// @param[in] fan The speed setting for the fan. -/// @param[in] swingv The vertical swing setting. -/// @param[in] swingh The horizontal swing setting. -/// @param[in] quiet Run the device in quiet/silent mode. -/// @param[in] turbo Run the device in turbo/powerful mode. -/// @param[in] econo Run the device in economical mode. -/// @param[in] light Turn on the LED/Display mode. -/// @param[in] filter Turn on the (ion/pollen/etc) filter mode. -/// @param[in] clean Turn on the self-cleaning mode. e.g. Mould, dry filters etc -/// @param[in] beep Enable/Disable beeps when receiving IR messages. -/// @param[in] sleep Nr. of minutes for sleep mode. -/// -1 is Off, >= 0 is on. Some devices it is the nr. of mins to run for. -/// Others it may be the time to enter/exit sleep mode. -/// i.e. Time in Nr. of mins since midnight. -/// @param[in] clock The time in Nr. of mins since midnight. < 0 is ignore. -void IRac::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) { - state->protocol = vendor; - state->model = model; - state->power = power; - state->mode = mode; - state->degrees = degrees; - state->celsius = celsius; - state->fanspeed = fan; - state->swingv = swingv; - state->swingh = swingh; - state->quiet = quiet; - state->turbo = turbo; - state->econo = econo; - state->light = light; - state->filter = filter; - state->clean = clean; - state->beep = beep; - state->sleep = sleep; - state->clock = clock; -} - -/// Initialise the given state with the supplied settings. -/// @param[out] state A Ptr to where the settings will be stored. -/// @note Sets all the parameters to reasonable base/automatic defaults. -void IRac::initState(stdAc::state_t *state) { - initState(state, decode_type_t::UNKNOWN, -1, false, stdAc::opmode_t::kOff, - 25, true, // 25 degrees Celsius - stdAc::fanspeed_t::kAuto, stdAc::swingv_t::kOff, - stdAc::swingh_t::kOff, false, false, false, false, false, false, - false, -1, -1); -} - -/// Get the current internal A/C climate state. -/// @return A Ptr to a state containing the current (to be sent) settings. -stdAc::state_t IRac::getState(void) { return next; } - -/// Get the previous internal A/C climate state that should have already been -/// sent to the device. i.e. What the A/C unit should already be set to. -/// @return A Ptr to a state containing the previously sent settings. -stdAc::state_t IRac::getStatePrev(void) { return _prev; } - -/// Is the given protocol supported by the IRac class? -/// @param[in] protocol The vendor/protocol type. -/// @return true if the protocol is supported by this class, otherwise false. -bool IRac::isProtocolSupported(const decode_type_t protocol) { - switch (protocol) { -#if SEND_AIRWELL - case decode_type_t::AIRWELL: -#endif -#if SEND_AMCOR - case decode_type_t::AMCOR: -#endif -#if SEND_ARGO - case decode_type_t::ARGO: -#endif -#if SEND_CARRIER_AC64 - case decode_type_t::CARRIER_AC64: -#endif // SEND_CARRIER_AC64 -#if SEND_COOLIX - case decode_type_t::COOLIX: -#endif -#if SEND_CORONA_AC - case decode_type_t::CORONA_AC: -#endif -#if SEND_DAIKIN - case decode_type_t::DAIKIN: -#endif -#if SEND_DAIKIN128 - case decode_type_t::DAIKIN128: -#endif -#if SEND_DAIKIN152 - case decode_type_t::DAIKIN152: -#endif -#if SEND_DAIKIN160 - case decode_type_t::DAIKIN160: -#endif -#if SEND_DAIKIN176 - case decode_type_t::DAIKIN176: -#endif -#if SEND_DAIKIN2 - case decode_type_t::DAIKIN2: -#endif -#if SEND_DAIKIN216 - case decode_type_t::DAIKIN216: -#endif -#if SEND_DAIKIN64 - case decode_type_t::DAIKIN64: -#endif -#if SEND_DELONGHI_AC - case decode_type_t::DELONGHI_AC: -#endif -#if SEND_ECOCLIM - case decode_type_t::ECOCLIM: -#endif -#if SEND_ELECTRA_AC - case decode_type_t::ELECTRA_AC: -#endif -#if SEND_FUJITSU_AC - case decode_type_t::FUJITSU_AC: -#endif -#if SEND_GOODWEATHER - case decode_type_t::GOODWEATHER: -#endif -#if SEND_GREE - case decode_type_t::GREE: -#endif -#if SEND_HAIER_AC - case decode_type_t::HAIER_AC: -#endif -#if SEND_HAIER_AC176 - case decode_type_t::HAIER_AC176: -#endif // SEND_HAIER_AC176 -#if SEND_HAIER_AC_YRW02 - case decode_type_t::HAIER_AC_YRW02: -#endif -#if SEND_HITACHI_AC - case decode_type_t::HITACHI_AC: -#endif -#if SEND_HITACHI_AC1 - case decode_type_t::HITACHI_AC1: -#endif -#if SEND_HITACHI_AC344 - case decode_type_t::HITACHI_AC344: -#endif -#if SEND_HITACHI_AC424 - case decode_type_t::HITACHI_AC424: -#endif -#if SEND_KELON - case decode_type_t::KELON: -#endif -#if SEND_KELVINATOR - case decode_type_t::KELVINATOR: -#endif -#if SEND_LG - case decode_type_t::LG: - case decode_type_t::LG2: -#endif -#if SEND_MIDEA - case decode_type_t::MIDEA: -#endif -#if SEND_MITSUBISHI_AC - case decode_type_t::MITSUBISHI_AC: -#endif -#if SEND_MITSUBISHI112 - case decode_type_t::MITSUBISHI112: -#endif -#if SEND_MITSUBISHI136 - case decode_type_t::MITSUBISHI136: -#endif -#if SEND_MITSUBISHIHEAVY - case decode_type_t::MITSUBISHI_HEAVY_88: - case decode_type_t::MITSUBISHI_HEAVY_152: -#endif -#if SEND_NEOCLIMA - case decode_type_t::NEOCLIMA: -#endif -#if SEND_PANASONIC_AC - case decode_type_t::PANASONIC_AC: -#endif -#if SEND_PANASONIC_AC32 - case decode_type_t::PANASONIC_AC32: -#endif -#if SEND_RHOSS - case decode_type_t::RHOSS: -#endif -#if SEND_SAMSUNG_AC - case decode_type_t::SAMSUNG_AC: -#endif -#if SEND_SANYO_AC - case decode_type_t::SANYO_AC: -#endif -#if SEND_SANYO_AC88 - case decode_type_t::SANYO_AC88: -#endif -#if SEND_SHARP_AC - case decode_type_t::SHARP_AC: -#endif -#if SEND_TCL112AC - case decode_type_t::TCL112AC: -#endif -#if SEND_TECHNIBEL_AC - case decode_type_t::TECHNIBEL_AC: -#endif -#if SEND_TECO - case decode_type_t::TECO: -#endif -#if SEND_TEKNOPOINT - case decode_type_t::TEKNOPOINT: -#endif // SEND_TEKNOPOINT -#if SEND_TOSHIBA_AC - case decode_type_t::TOSHIBA_AC: -#endif -#if SEND_TRANSCOLD - case decode_type_t::TRANSCOLD: -#endif -#if SEND_TROTEC - case decode_type_t::TROTEC: -#endif -#if SEND_TROTEC_3550 - case decode_type_t::TROTEC_3550: -#endif // SEND_TROTEC_3550 -#if SEND_TRUMA - case decode_type_t::TRUMA: -#endif // SEND_TRUMA -#if SEND_VESTEL_AC - case decode_type_t::VESTEL_AC: -#endif -#if SEND_VOLTAS - case decode_type_t::VOLTAS: -#endif - case decode_type_t::WHIRLPOOL_AC: - return true; - default: - return false; - } -} - -#if SEND_AIRWELL -/// Send an Airwell A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRAirwellAc object to use. -/// @param[in] on The power setting. -/// @param[in] mode The operation mode setting. -/// @param[in] degrees The temperature setting in degrees. -/// @param[in] fan The speed setting for the fan. -void IRac::airwell(IRAirwellAc *ac, - const bool on, const stdAc::opmode_t mode, - const float degrees, const stdAc::fanspeed_t fan) { - ac->begin(); - ac->setPowerToggle(on); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan)); - // No Swing setting available. - // No Quiet setting available. - // No Light setting available. - // No Filter setting available. - // No Turbo setting available. - // No Economy setting available. - // No Clean setting available. - // No Beep setting available. - // No Sleep setting available. - ac->send(); -} -#endif // SEND_AIRWELL - -#if SEND_AMCOR -/// Send an Amcor A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRAmcorAc object to use. -/// @param[in] on The power setting. -/// @param[in] mode The operation mode setting. -/// @param[in] degrees The temperature setting in degrees. -/// @param[in] fan The speed setting for the fan. -void IRac::amcor(IRAmcorAc *ac, - const bool on, const stdAc::opmode_t mode, const float degrees, - const stdAc::fanspeed_t fan) { - ac->begin(); - ac->setPower(on); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan)); - // No Swing setting available. - // No Quiet setting available. - // No Light setting available. - // No Filter setting available. - // No Turbo setting available. - // No Economy setting available. - // No Clean setting available. - // No Beep setting available. - // No Sleep setting available. - ac->send(); -} -#endif // SEND_AMCOR - -#if SEND_ARGO -/// Send an Argo A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRArgoAC object to use. -/// @param[in] on The power setting. -/// @param[in] mode The operation mode setting. -/// @param[in] degrees The temperature setting in degrees. -/// @param[in] fan The speed setting for the fan. -/// @param[in] swingv The vertical swing setting. -/// @param[in] turbo Run the device in turbo/powerful mode. -/// @param[in] sleep Nr. of minutes for sleep mode. -/// @note -1 is Off, >= 0 is on. -void IRac::argo(IRArgoAC *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 int16_t sleep) { - ac->begin(); - ac->setPower(on); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan)); - ac->setFlap(ac->convertSwingV(swingv)); - // No Quiet setting available. - // No Light setting available. - // No Filter setting available. - ac->setMax(turbo); - // No Economy setting available. - // No Clean setting available. - // No Beep setting available. - ac->setNight(sleep >= 0); // Convert to a boolean. - ac->send(); -} -#endif // SEND_ARGO - -#if SEND_CARRIER_AC64 -/// Send a Carrier 64-bit A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRCarrierAc64 object to use. -/// @param[in] on The power setting. -/// @param[in] mode The operation mode setting. -/// @param[in] degrees The temperature setting in degrees. -/// @param[in] fan The speed setting for the fan. -/// @param[in] swingv The vertical swing setting. -/// @param[in] sleep Nr. of minutes for sleep mode. -/// @note -1 is Off, >= 0 is on. -void IRac::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) { - ac->begin(); - ac->setPower(on); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan)); - ac->setSwingV((int8_t)swingv >= 0); - // No Quiet setting available. - // No Light setting available. - // No Filter setting available. - // No Turbo setting available. - // No Economy setting available. - // No Clean setting available. - // No Beep setting available. - ac->setSleep(sleep >= 0); // Convert to a boolean. - ac->send(); -} -#endif // SEND_CARRIER_AC64 - -#if SEND_COOLIX -/// Send a Coolix A/C message with the supplied settings. -/// @note May result in multiple messages being sent. -/// @param[in, out] ac A Ptr to an IRCoolixAC object to use. -/// @param[in] on The power setting. -/// @param[in] mode The operation mode setting. -/// @param[in] degrees The temperature setting in degrees. -/// @param[in] fan The speed setting for the fan. -/// @param[in] swingv The vertical swing setting. -/// @param[in] swingh The horizontal swing setting. -/// @param[in] turbo Run the device in turbo/powerful mode. -/// @param[in] light Turn on the LED/Display mode. -/// @param[in] clean Turn on the self-cleaning mode. e.g. Mould, dry filters etc -/// @param[in] sleep Nr. of minutes for sleep mode. -/// @note -1 is Off, >= 0 is on. -void IRac::coolix(IRCoolixAC *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 light, const bool clean, - const int16_t sleep) { - ac->begin(); - ac->setPower(on); - if (!on) { - // after turn off AC no more commands should - // be accepted - ac->send(); - return; - } - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan)); - // No Filter setting available. - // No Beep setting available. - // No Clock setting available. - // No Econo setting available. - // No Quiet setting available. - ac->send(); // Send the state, which will also power on the unit. - // The following are all options/settings that create their own special - // messages. Often they only make sense to be sent after the unit is turned - // on. For instance, assuming a person wants to have the a/c on and in turbo - // mode. If we send the turbo message, it is ignored if the unit is off. - // Hence we send the special mode/setting messages after a normal message - // which will turn on the device. - if (swingv != stdAc::swingv_t::kOff || swingh != stdAc::swingh_t::kOff) { - // Swing has a special command that needs to be sent independently. - ac->setSwing(); - ac->send(); - } - if (turbo) { - // Turbo has a special command that needs to be sent independently. - ac->setTurbo(); - ac->send(); - } - if (sleep >= 0) { - // Sleep has a special command that needs to be sent independently. - ac->setSleep(); - ac->send(); - } - if (light) { - // Light has a special command that needs to be sent independently. - ac->setLed(); - ac->send(); - } - if (clean) { - // Clean has a special command that needs to be sent independently. - ac->setClean(); - ac->send(); - } -} -#endif // SEND_COOLIX - -#if SEND_CORONA_AC -/// Send a Corona A/C message with the supplied settings. -/// @note May result in multiple messages being sent. -/// @param[in, out] ac A Ptr to an IRCoronaAc object to use. -/// @param[in] on The power setting. -/// @param[in] mode The operation mode setting. -/// @param[in] degrees The temperature setting in degrees. -/// @param[in] fan The speed setting for the fan. -/// @param[in] swingv The vertical swing setting. -/// @param[in] econo Run the device in economical mode. -void IRac::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) { - ac->begin(); - ac->setPower(on); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan)); - ac->setSwingVToggle(swingv != stdAc::swingv_t::kOff); - // No Quiet setting available. - // No Light setting available. - // No Filter setting available. - // No Turbo setting available. - ac->setEcono(econo); - // No Clean setting available. - // No Beep setting available. - // No Sleep setting available. - ac->send(); -} -#endif // SEND_CARRIER_AC64 - -#if SEND_DAIKIN -/// Send a Daikin A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRDaikinESP object to use. -/// @param[in] on The power setting. -/// @param[in] mode The operation mode setting. -/// @param[in] degrees The temperature setting in degrees. -/// @param[in] fan The speed setting for the fan. -/// @param[in] swingv The vertical swing setting. -/// @param[in] swingh The horizontal swing setting. -/// @param[in] quiet Run the device in quiet/silent mode. -/// @param[in] turbo Run the device in turbo/powerful mode. -/// @param[in] econo Run the device in economical mode. -/// @param[in] clean Turn on the self-cleaning mode. e.g. Mould, dry filters etc -void IRac::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) { - ac->begin(); - ac->setPower(on); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan)); - ac->setSwingVertical((int8_t)swingv >= 0); - ac->setSwingHorizontal((int8_t)swingh >= 0); - ac->setQuiet(quiet); - // No Light setting available. - // No Filter setting available. - ac->setPowerful(turbo); - ac->setEcono(econo); - ac->setMold(clean); - // No Beep setting available. - // No Sleep setting available. - // No Clock setting available. - ac->send(); -} -#endif // SEND_DAIKIN - -#if SEND_DAIKIN128 -/// Send a Daikin 128-bit A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRDaikin128 object to use. -/// @param[in] on The power setting. -/// @param[in] mode The operation mode setting. -/// @param[in] degrees The temperature setting in degrees. -/// @param[in] fan The speed setting for the fan. -/// @param[in] swingv The vertical swing setting. -/// @param[in] quiet Run the device in quiet/silent mode. -/// @param[in] turbo Run the device in turbo/powerful mode. -/// @param[in] light Turn on the LED/Display mode. -/// @param[in] econo Run the device in economical mode. -/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on. -/// @param[in] clock The time in Nr. of mins since midnight. < 0 is ignore. -void IRac::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, const int16_t clock) { - ac->begin(); - ac->setPowerToggle(on); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan)); - ac->setSwingVertical((int8_t)swingv >= 0); - // No Horizontal Swing setting avaliable. - ac->setQuiet(quiet); - ac->setLightToggle(light ? kDaikin128BitWall : 0); - // No Filter setting available. - ac->setPowerful(turbo); - ac->setEcono(econo); - // No Clean setting available. - // No Beep setting available. - ac->setSleep(sleep > 0); - if (clock >= 0) ac->setClock(clock); - ac->send(); -} -#endif // SEND_DAIKIN128 - -#if SEND_DAIKIN152 -/// Send a Daikin 152-bit A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRDaikin152 object to use. -/// @param[in] on The power setting. -/// @param[in] mode The operation mode setting. -/// @param[in] degrees The temperature setting in degrees. -/// @param[in] fan The speed setting for the fan. -/// @param[in] swingv The vertical swing setting. -/// @param[in] quiet Run the device in quiet/silent mode. -/// @param[in] turbo Run the device in turbo/powerful mode. -/// @param[in] econo Run the device in economical mode. -void IRac::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) { - ac->begin(); - ac->setPower(on); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan)); - ac->setSwingV((int8_t)swingv >= 0); - // No Horizontal Swing setting avaliable. - ac->setQuiet(quiet); - // No Light setting available. - // No Filter setting available. - ac->setPowerful(turbo); - ac->setEcono(econo); - // No Clean setting available. - // No Beep setting available. - // No Sleep setting available. - // No Clock setting available. - ac->send(); -} -#endif // SEND_DAIKIN152 - -#if SEND_DAIKIN160 -/// Send a Daikin 160-bit A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRDaikin160 object to use. -/// @param[in] on The power setting. -/// @param[in] mode The operation mode setting. -/// @param[in] degrees The temperature setting in degrees. -/// @param[in] fan The speed setting for the fan. -/// @param[in] swingv The vertical swing setting. -void IRac::daikin160(IRDaikin160 *ac, - const bool on, const stdAc::opmode_t mode, - const float degrees, const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv) { - ac->begin(); - ac->setPower(on); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan)); - ac->setSwingVertical(ac->convertSwingV(swingv)); - ac->send(); -} -#endif // SEND_DAIKIN160 - -#if SEND_DAIKIN176 -/// Send a Daikin 176-bit A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRDaikin176 object to use. -/// @param[in] on The power setting. -/// @param[in] mode The operation mode setting. -/// @param[in] degrees The temperature setting in degrees. -/// @param[in] fan The speed setting for the fan. -/// @param[in] swingh The horizontal swing setting. -void IRac::daikin176(IRDaikin176 *ac, - const bool on, const stdAc::opmode_t mode, - const float degrees, const stdAc::fanspeed_t fan, - const stdAc::swingh_t swingh) { - ac->begin(); - ac->setPower(on); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan)); - ac->setSwingHorizontal(ac->convertSwingH(swingh)); - ac->send(); -} -#endif // SEND_DAIKIN176 - -#if SEND_DAIKIN2 -/// Send a Daikin2 A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRDaikin2 object to use. -/// @param[in] on The power setting. -/// @param[in] mode The operation mode setting. -/// @param[in] degrees The temperature setting in degrees. -/// @param[in] fan The speed setting for the fan. -/// @param[in] swingv The vertical swing setting. -/// @param[in] swingh The horizontal swing setting. -/// @param[in] quiet Run the device in quiet/silent mode. -/// @param[in] turbo Run the device in turbo/powerful mode. -/// @param[in] light Turn on the LED/Display mode. -/// @param[in] econo Run the device in economical mode. -/// @param[in] filter Turn on the (ion/pollen/etc) filter mode. -/// @param[in] clean Turn on the self-cleaning mode. e.g. Mould, dry filters etc -/// @param[in] beep Enable/Disable beeps when receiving IR messages. -/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on. -/// @param[in] clock The time in Nr. of mins since midnight. < 0 is ignore. -void IRac::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, const int16_t clock) { - ac->begin(); - ac->setPower(on); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan)); - ac->setSwingVertical(ac->convertSwingV(swingv)); - ac->setSwingHorizontal(ac->convertSwingH(swingh)); - ac->setQuiet(quiet); - ac->setLight(light ? 1 : 3); // On/High is 1, Off is 3. - ac->setPowerful(turbo); - ac->setEcono(econo); - ac->setPurify(filter); - ac->setMold(clean); - ac->setClean(true); // Hardwire auto clean to be on per request (@sheppy99) - ac->setBeep(beep ? 2 : 3); // On/Loud is 2, Off is 3. - if (sleep > 0) ac->enableSleepTimer(sleep); - if (clock >= 0) ac->setCurrentTime(clock); - ac->send(); -} -#endif // SEND_DAIKIN2 - -#if SEND_DAIKIN216 -/// Send a Daikin 216-bit A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRDaikin216 object to use. -/// @param[in] on The power setting. -/// @param[in] mode The operation mode setting. -/// @param[in] degrees The temperature setting in degrees. -/// @param[in] fan The speed setting for the fan. -/// @param[in] swingv The vertical swing setting. -/// @param[in] swingh The horizontal swing setting. -/// @param[in] quiet Run the device in quiet/silent mode. -/// @param[in] turbo Run the device in turbo/powerful mode. -void IRac::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) { - ac->begin(); - ac->setPower(on); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan)); - ac->setSwingVertical((int8_t)swingv >= 0); - ac->setSwingHorizontal((int8_t)swingh >= 0); - ac->setQuiet(quiet); - ac->setPowerful(turbo); - ac->send(); -} -#endif // SEND_DAIKIN216 - -#if SEND_DAIKIN64 -/// Send a Daikin 64-bit A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRDaikin64 object to use. -/// @param[in] on The power setting. -/// @param[in] mode The operation mode setting. -/// @param[in] degrees The temperature setting in degrees. -/// @param[in] fan The speed setting for the fan. -/// @param[in] swingv The vertical swing setting. -/// @param[in] quiet Run the device in quiet/silent mode. -/// @param[in] turbo Run the device in turbo/powerful mode. -/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on. -/// @param[in] clock The time in Nr. of mins since midnight. < 0 is ignore. -void IRac::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, const int16_t clock) { - ac->begin(); - ac->setPowerToggle(on); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan)); - ac->setSwingVertical((int8_t)swingv >= 0); - ac->setTurbo(turbo); - ac->setQuiet(quiet); - ac->setSleep(sleep >= 0); - if (clock >= 0) ac->setClock(clock); - ac->send(); -} -#endif // SEND_DAIKIN64 - -#if SEND_DELONGHI_AC -/// Send a Delonghi A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRDelonghiAc object to use. -/// @param[in] on The power setting. -/// @param[in] mode The operation mode setting. -/// @param[in] celsius Temperature units. True is Celsius, False is Fahrenheit. -/// @param[in] degrees The temperature setting in degrees. -/// @param[in] fan The speed setting for the fan. -/// @param[in] turbo Run the device in turbo/powerful mode. -/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on. -void IRac::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) { - ac->begin(); - ac->setPower(on); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees, !celsius); - ac->setFan(ac->convertFan(fan)); - ac->setBoost(turbo); - ac->setSleep(sleep >= 0); - ac->send(); -} -#endif // SEND_DELONGHI_AC - -#if SEND_ECOCLIM -/// Send an EcoClim A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IREcoclimAc object to use. -/// @param[in] on The power setting. -/// @param[in] mode The operation mode setting. -/// @param[in] degrees The temperature setting in degrees. -/// @param[in] fan The speed setting for the fan. -/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on. -/// @param[in] clock The time in Nr. of mins since midnight. < 0 is ignore. -void IRac::ecoclim(IREcoclimAc *ac, - const bool on, const stdAc::opmode_t mode, - const float degrees, const stdAc::fanspeed_t fan, - const int16_t sleep, const int16_t clock) { - ac->begin(); - ac->setPower(on); - uint8_t new_mode; - if (sleep >= 0) // EcoClim has a descrete Sleep operation mode, not a setting - new_mode = kEcoclimSleep; // Override the requested operating mode. - else - new_mode = ac->convertMode(mode); // Not Sleep, so use the supplied mode. - ac->setMode(new_mode); - ac->setTemp(degrees); - ac->setSensorTemp(degrees); //< Set to the desired temp until we cab disable. - ac->setFan(ac->convertFan(fan)); - // No SwingV setting available - // No SwingH setting available - // No Quiet setting available. - // No Turbo setting available. - // No Light setting available. - // No Econo setting available. - // No Filter setting available. - // No Clean setting available - // No Beep setting available. - // No Sleep setting available. - if (clock >= 0) ac->setClock(clock); - ac->send(); -} -#endif // SEND_ECOCLIM - -#if SEND_ELECTRA_AC -/// Send an Electra A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRElectraAc object to use. -/// @param[in] on The power setting. -/// @param[in] mode The operation mode setting. -/// @param[in] degrees The temperature setting in degrees. -/// @param[in] fan The speed setting for the fan. -/// @param[in] swingv The vertical swing setting. -/// @param[in] swingh The horizontal swing setting. -/// @param[in] turbo Run the device in turbo/powerful mode. -/// @param[in] lighttoggle Should we toggle the LED/Display? -/// @param[in] clean Turn on the self-cleaning mode. e.g. Mould, dry filters etc -void IRac::electra(IRElectraAc *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 lighttoggle, const bool clean) { - ac->begin(); - ac->setPower(on); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan)); - ac->setSwingV(swingv != stdAc::swingv_t::kOff); - ac->setSwingH(swingh != stdAc::swingh_t::kOff); - // No Quiet setting available. - ac->setTurbo(turbo); - ac->setLightToggle(lighttoggle); - // No Econo setting available. - // No Filter setting available. - ac->setClean(clean); - // No Beep setting available. - // No Sleep setting available. - // No Clock setting available. - ac->send(); -} -#endif // SEND_ELECTRA_AC - -#if SEND_FUJITSU_AC -/// Send a Fujitsu A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRFujitsuAC object to use. -/// @param[in] model The A/C model to use. -/// @param[in] on The power setting. -/// @param[in] mode The operation mode setting. -/// @param[in] celsius Temperature units. True is Celsius, False is Fahrenheit. -/// @param[in] degrees The temperature setting in degrees. -/// @param[in] fan The speed setting for the fan. -/// @param[in] swingv The vertical swing setting. -/// @param[in] swingh The horizontal swing setting. -/// @param[in] quiet Run the device in quiet/silent mode. -/// @param[in] turbo Run the device in turbo/powerful mode. -/// @param[in] econo Run the device in economical mode. -/// @param[in] filter Turn on the (ion/pollen/etc) filter mode. -/// @param[in] clean Turn on the self-cleaning mode. e.g. Mould, dry filters etc -/// @param[in] sleep Nr. of minutes for sleep mode. <= 0 is Off, > 0 is on. -void IRac::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) { - ac->begin(); - ac->setModel(model); - if (on) { - // Do all special messages (except "Off") first, - // These need to be sent separately. - switch (ac->getModel()) { - // Some functions are only available on some models. - case fujitsu_ac_remote_model_t::ARREB1E: - if (turbo) { - ac->setCmd(kFujitsuAcCmdPowerful); - // Powerful is a separate command. - ac->send(); - } - if (econo) { - ac->setCmd(kFujitsuAcCmdEcono); - // Econo is a separate command. - ac->send(); - } - break; - default: - {}; - } - // Normal operation. - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees, celsius); - ac->setFanSpeed(ac->convertFan(fan)); - uint8_t swing = kFujitsuAcSwingOff; - if (swingv > stdAc::swingv_t::kOff) swing |= kFujitsuAcSwingVert; - if (swingh > stdAc::swingh_t::kOff) swing |= kFujitsuAcSwingHoriz; - ac->setSwing(swing); - if (quiet) ac->setFanSpeed(kFujitsuAcFanQuiet); - // No Light setting available. - ac->setFilter(filter); - ac->setClean(clean); - // No Beep setting available. - ac->setSleepTimer(sleep > 0 ? sleep : 0); - // No Sleep setting available. - // No Clock setting available. - ac->on(); // Ref: Issue #860 - } else { - // Off is special case/message. We don't need to send other messages. - ac->off(); - } - ac->send(); -} -#endif // SEND_FUJITSU_AC - -#if SEND_GOODWEATHER -/// Send a Goodweather A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRGoodweatherAc object to use. -/// @param[in] on The power setting. -/// @param[in] mode The operation mode setting. -/// @param[in] degrees The temperature setting in degrees. -/// @param[in] fan The speed setting for the fan. -/// @param[in] swingv The vertical swing setting. -/// @param[in] turbo Run the device in turbo/powerful mode. -/// @param[in] light Turn on the LED/Display mode. -/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on. -void IRac::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) { - ac->begin(); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan)); - ac->setSwing(swingv == stdAc::swingv_t::kOff ? kGoodweatherSwingOff - : kGoodweatherSwingSlow); - ac->setTurbo(turbo); - ac->setLight(light); - // No Clean setting available. - ac->setSleep(sleep >= 0); // Sleep on this A/C is either on or off. - // No Horizontal Swing setting available. - // No Econo setting available. - // No Filter setting available. - // No Beep setting available. - // No Quiet setting available. - // No Clock setting available. - ac->setPower(on); - ac->send(); -} -#endif // SEND_GOODWEATHER - -#if SEND_GREE -/// Send a Gree A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRGreeAC object to use. -/// @param[in] model The A/C model to use. -/// @param[in] on The power setting. -/// @param[in] mode The operation mode setting. -/// @param[in] celsius Temperature units. True is Celsius, False is Fahrenheit. -/// @param[in] degrees The temperature setting in degrees. -/// @param[in] fan The speed setting for the fan. -/// @param[in] swingv The vertical swing setting. -/// @param[in] turbo Run the device in turbo/powerful mode. -/// @param[in] light Turn on the LED/Display mode. -/// @param[in] clean Turn on the self-cleaning mode. e.g. Mould, dry filters etc -/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on. -void IRac::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 bool turbo, - const bool light, const bool clean, const int16_t sleep) { - ac->begin(); - ac->setModel(model); - ac->setPower(on); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees, !celsius); - ac->setFan(ac->convertFan(fan)); - ac->setSwingVertical(swingv == stdAc::swingv_t::kAuto, // Set auto flag. - ac->convertSwingV(swingv)); - ac->setLight(light); - ac->setTurbo(turbo); - ac->setXFan(clean); - ac->setSleep(sleep >= 0); // Sleep on this A/C is either on or off. - // No Horizontal Swing setting available. - // No Econo setting available. - // No Filter setting available. - // No Beep setting available. - // No Quiet setting available. - // No Clock setting available. - ac->send(); -} -#endif // SEND_GREE - -#if SEND_HAIER_AC -/// Send a Haier A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRGreeAC object to use. -/// @param[in] on The power setting. -/// @param[in] mode The operation mode setting. -/// @param[in] degrees The temperature setting in degrees. -/// @param[in] fan The speed setting for the fan. -/// @param[in] swingv The vertical swing setting. -/// @param[in] filter Turn on the (ion/pollen/etc) filter mode. -/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on. -/// @param[in] clock The time in Nr. of mins since midnight. < 0 is ignore. -void IRac::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, const int16_t clock) { - ac->begin(); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan)); - ac->setSwing(ac->convertSwingV(swingv)); - // No Horizontal Swing setting available. - // No Quiet setting available. - // No Turbo setting available. - // No Light setting available. - ac->setHealth(filter); - // No Clean setting available. - // No Beep setting available. - ac->setSleep(sleep >= 0); // Sleep on this A/C is either on or off. - if (clock >= 0) ac->setCurrTime(clock); - if (on) - ac->setCommand(kHaierAcCmdOn); - else - ac->setCommand(kHaierAcCmdOff); - ac->send(); -} -#endif // SEND_HAIER_AC - -#if SEND_HAIER_AC176 -/// Send a Haier 176 bit A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRHaierAC176 object to use. -/// @param[in] on The power setting. -/// @param[in] mode The operation mode setting. -/// @param[in] degrees The temperature setting in degrees. -/// @param[in] fan The speed setting for the fan. -/// @param[in] swingv The vertical swing setting. -/// @param[in] turbo Run the device in turbo/powerful mode. -/// @param[in] quiet Run the device in quiet mode. -/// @param[in] filter Turn on the (ion/pollen/etc) filter mode. -/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on. -void IRac::haier176(IRHaierAC176 *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 quiet, const bool filter, const int16_t sleep) { - ac->begin(); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan)); - ac->setSwing(ac->convertSwingV(swingv)); - // No Horizontal Swing setting available. - ac->setQuiet(quiet); - ac->setTurbo(turbo); - // No Light setting available. - ac->setHealth(filter); - // No Clean setting available. - // No Beep setting available. - ac->setSleep(sleep >= 0); // Sleep on this A/C is either on or off. - ac->setPower(on); - ac->send(); -} -#endif // SEND_HAIER_AC176 - -#if SEND_HAIER_AC_YRW02 -/// Send a Haier YRWO2 A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRHaierACYRW02 object to use. -/// @param[in] on The power setting. -/// @param[in] mode The operation mode setting. -/// @param[in] degrees The temperature setting in degrees. -/// @param[in] fan The speed setting for the fan. -/// @param[in] swingv The vertical swing setting. -/// @param[in] turbo Run the device in turbo/powerful mode. -/// @param[in] quiet Run the device in quiet mode. -/// @param[in] filter Turn on the (ion/pollen/etc) filter mode. -/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on. -void IRac::haierYrwo2(IRHaierACYRW02 *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 quiet, const bool filter, - const int16_t sleep) { - ac->begin(); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan)); - ac->setSwing(ac->convertSwingV(swingv)); - // No Horizontal Swing setting available. - ac->setQuiet(quiet); - ac->setTurbo(turbo); - // No Light setting available. - ac->setHealth(filter); - // No Clean setting available. - // No Beep setting available. - ac->setSleep(sleep >= 0); // Sleep on this A/C is either on or off. - ac->setPower(on); - ac->send(); -} -#endif // SEND_HAIER_AC_YRW02 - -#if SEND_HITACHI_AC -/// Send a Hitachi A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRHitachiAc object to use. -/// @param[in] on The power setting. -/// @param[in] mode The operation mode setting. -/// @param[in] degrees The temperature setting in degrees. -/// @param[in] fan The speed setting for the fan. -/// @param[in] swingv The vertical swing setting. -/// @param[in] swingh The horizontal swing setting. -void IRac::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) { - ac->begin(); - ac->setPower(on); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan)); - ac->setSwingVertical(swingv != stdAc::swingv_t::kOff); - ac->setSwingHorizontal(swingh != stdAc::swingh_t::kOff); - // No Quiet setting available. - // No Turbo setting available. - // No Light setting available. - // No Filter setting available. - // No Clean setting available. - // No Beep setting available. - // No Sleep setting available. - // No Clock setting available. - ac->send(); -} -#endif // SEND_HITACHI_AC - -#if SEND_HITACHI_AC1 -/// Send a Hitachi1 A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRHitachiAc1 object to use. -/// @param[in] model The A/C model to use. -/// @param[in] on The power setting. -/// @param[in] power_toggle The power toggle setting. -/// @param[in] mode The operation mode setting. -/// @param[in] degrees The temperature setting in degrees. -/// @param[in] fan The speed setting for the fan. -/// @param[in] swingv The vertical swing setting. -/// @param[in] swingh The horizontal swing setting. -/// @param[in] swing_toggle The swing_toggle setting. -/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on. -/// @note The sleep mode used is the "Sleep 2" setting. -void IRac::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) { - ac->begin(); - ac->setModel(model); - ac->setPower(on); - ac->setPowerToggle(power_toggle); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan)); - ac->setSwingV(swingv != stdAc::swingv_t::kOff); - ac->setSwingH(swingh != stdAc::swingh_t::kOff); - ac->setSwingToggle(swing_toggle); - ac->setSleep((sleep >= 0) ? kHitachiAc1Sleep2 : kHitachiAc1SleepOff); - // No Sleep setting available. - // No Swing(H) setting available. - // No Quiet setting available. - // No Turbo setting available. - // No Light setting available. - // No Filter setting available. - // No Clean setting available. - // No Beep setting available. - // No Clock setting available. - ac->send(); -} -#endif // SEND_HITACHI_AC1 - -#if SEND_HITACHI_AC344 -/// Send a Hitachi 344-bit A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRHitachiAc344 object to use. -/// @param[in] on The power setting. -/// @param[in] mode The operation mode setting. -/// @param[in] degrees The temperature setting in degrees. -/// @param[in] fan The speed setting for the fan. -/// @param[in] swingv The vertical swing setting. -/// @param[in] swingh The horizontal swing setting. -void IRac::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) { - ac->begin(); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan)); - ac->setSwingH(ac->convertSwingH(swingh)); - ac->setPower(on); - // No Quiet setting available. - // No Turbo setting available. - // No Light setting available. - // No Filter setting available. - // No Clean setting available. - // No Beep setting available. - // No Sleep setting available. - // No Clock setting available. - - // SwingVToggle is special. Needs to be last method called. - ac->setSwingVToggle(swingv != stdAc::swingv_t::kOff); - ac->send(); -} -#endif // SEND_HITACHI_AC344 - -#if SEND_HITACHI_AC424 -/// Send a Hitachi 424-bit A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRHitachiAc424 object to use. -/// @param[in] on The power setting. -/// @param[in] mode The operation mode setting. -/// @param[in] degrees The temperature setting in degrees. -/// @param[in] fan The speed setting for the fan. -/// @param[in] swingv The vertical swing setting. -void IRac::hitachi424(IRHitachiAc424 *ac, - const bool on, const stdAc::opmode_t mode, - const float degrees, const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv) { - ac->begin(); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan)); - ac->setPower(on); - // SwingVToggle is special. Needs to be last method called. - ac->setSwingVToggle(swingv != stdAc::swingv_t::kOff); - // No Swing(H) setting available. - // No Quiet setting available. - // No Turbo setting available. - // No Light setting available. - // No Filter setting available. - // No Clean setting available. - // No Beep setting available. - // No Sleep setting available. - // No Clock setting available. - ac->send(); -} -#endif // SEND_HITACHI_AC424 - -#if SEND_KELON -/// Send a Kelon A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRKelonAc object to use. -/// @param[in] togglePower Whether to toggle the unit's power -/// @param[in] mode The operation mode setting. -/// @param[in] dryGrade The dehumidification intensity grade -/// @param[in] degrees The temperature setting in degrees. -/// @param[in] fan The speed setting for the fan. -/// @param[in] toggleSwing Whether to toggle the swing setting -/// @param[in] superCool Run the device in Super cooling mode. -/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on -void IRac::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) { - ac->begin(); - ac->setMode(IRKelonAc::convertMode(mode)); - ac->setFan(IRKelonAc::convertFan(fan)); - ac->setTemp(static_cast(degrees)); - ac->setSleep(sleep >= 0); - ac->setSupercool(superCool); - ac->setDryGrade(dryGrade); - - ac->setTogglePower(togglePower); - ac->setToggleSwingVertical(toggleSwing); - - ac->send(); -} -#endif // SEND_KELON - -#if SEND_KELVINATOR -/// Send a Kelvinator A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRKelvinatorAC object to use. -/// @param[in] on The power setting. -/// @param[in] mode The operation mode setting. -/// @param[in] degrees The temperature setting in degrees. -/// @param[in] fan The speed setting for the fan. -/// @param[in] swingv The vertical swing setting. -/// @param[in] swingh The horizontal swing setting. -/// @param[in] quiet Run the device in quiet/silent mode. -/// @param[in] turbo Run the device in turbo/powerful mode. -/// @param[in] light Turn on the LED/Display mode. -/// @param[in] filter Turn on the (ion/pollen/etc) filter mode. -/// @param[in] clean Turn on the self-cleaning mode. e.g. XFan, dry filters etc -void IRac::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) { - ac->begin(); - ac->setPower(on); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - ac->setFan((uint8_t)fan); // No conversion needed. - ac->setSwingVertical((int8_t)swingv >= 0); - ac->setSwingHorizontal((int8_t)swingh >= 0); - ac->setQuiet(quiet); - ac->setTurbo(turbo); - ac->setLight(light); - ac->setIonFilter(filter); - ac->setXFan(clean); - // No Beep setting available. - // No Sleep setting available. - // No Clock setting available. - ac->send(); -} -#endif // SEND_KELVINATOR - -#if SEND_LG -/// Send a LG A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRLgAc object to use. -/// @param[in] model The A/C model to use. -/// @param[in] on The power setting. -/// @param[in] mode The operation mode setting. -/// @param[in] degrees The temperature setting in degrees. -/// @param[in] fan The speed setting for the fan. -/// @param[in] swingv The vertical swing setting. -/// @param[in] swingv_prev The previous vertical swing setting. -/// @param[in] swingh The horizontal swing setting. -/// @param[in] light Turn on the LED/Display mode. -void IRac::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) { - ac->begin(); - ac->setModel(model); - ac->setPower(on); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan)); - ac->setSwingV(ac->convertSwingV(swingv_prev)); - ac->updateSwingPrev(); - ac->setSwingV(ac->convertSwingV(swingv)); - const uint8_t pos = ac->convertVaneSwingV(swingv); - for (uint8_t vane = 0; vane < kLgAcSwingVMaxVanes; vane++) - ac->setVaneSwingV(vane, pos); - ac->setSwingH(swingh != stdAc::swingh_t::kOff); - // No Quiet setting available. - // No Turbo setting available. - ac->setLight(light); - // No Filter setting available. - // No Clean setting available. - // No Beep setting available. - // No Sleep setting available. - // No Clock setting available. - ac->send(); -} -#endif // SEND_LG - -#if SEND_MIDEA -/// Send a Midea A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRMideaAC object to use. -/// @param[in] on The power setting. -/// @param[in] mode The operation mode setting. -/// @param[in] celsius Temperature units. True is Celsius, False is Fahrenheit. -/// @param[in] degrees The temperature setting in degrees. -/// @param[in] fan The speed setting for the fan. -/// @param[in] swingv The vertical swing setting. -/// @param[in] turbo Toggle the device's turbo/powerful mode. -/// @param[in] econo Toggle the device's economical mode. -/// @param[in] light Toggle the LED/Display mode. -/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on. -/// @note On Danby A/C units, swingv controls the Ion Filter instead. -void IRac::midea(IRMideaAC *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 econo, const bool light, const int16_t sleep) { - ac->begin(); - ac->setPower(on); - ac->setMode(ac->convertMode(mode)); - ac->setUseCelsius(celsius); - ac->setTemp(degrees, celsius); - ac->setFan(ac->convertFan(fan)); - ac->setSwingVToggle(swingv != stdAc::swingv_t::kOff); - // No Horizontal swing setting available. - // No Quiet setting available. - ac->setTurboToggle(turbo); - ac->setEconoToggle(econo); - ac->setLightToggle(light); - // No Filter setting available. - // No Clean setting available. - // No Beep setting available. - ac->setSleep(sleep >= 0); // Sleep on this A/C is either on or off. - // No Clock setting available. - ac->send(); -} -#endif // SEND_MIDEA - -#if SEND_MITSUBISHI_AC -/// Send a Mitsubishi A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRMitsubishiAC object to use. -/// @param[in] on The power setting. -/// @param[in] mode The operation mode setting. -/// @param[in] degrees The temperature setting in degrees. -/// @param[in] fan The speed setting for the fan. -/// @param[in] swingv The vertical swing setting. -/// @param[in] swingh The horizontal swing setting. -/// @param[in] quiet Run the device in quiet/silent mode. -/// @param[in] clock The time in Nr. of mins since midnight. < 0 is ignore. -/// @note Clock can only be set in 10 minute increments. i.e. % 10. -void IRac::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) { - ac->begin(); - // Uncomment next line if you *really* need the weekly timer enabled via IRac. - // ac->setWeeklyTimerEnabled(true); // Weekly Timer is disabled by default. - ac->setPower(on); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan)); - ac->setVane(ac->convertSwingV(swingv)); - ac->setWideVane(ac->convertSwingH(swingh)); - if (quiet) ac->setFan(kMitsubishiAcFanSilent); - // No Turbo setting available. - // No Light setting available. - // No Filter setting available. - // No Clean setting available. - // No Beep setting available. - // No Sleep setting available. - if (clock >= 0) ac->setClock(clock / 10); // Clock is in 10 min increments. - ac->send(); -} -#endif // SEND_MITSUBISHI_AC - -#if SEND_MITSUBISHI112 -/// Send a Mitsubishi 112-bit A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRMitsubishi112 object to use. -/// @param[in] on The power setting. -/// @param[in] mode The operation mode setting. -/// @param[in] degrees The temperature setting in degrees. -/// @param[in] fan The speed setting for the fan. -/// @param[in] swingv The vertical swing setting. -/// @param[in] swingh The horizontal swing setting. -/// @param[in] quiet Run the device in quiet/silent mode. -void IRac::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) { - ac->begin(); - ac->setPower(on); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan)); - ac->setSwingV(ac->convertSwingV(swingv)); - ac->setSwingH(ac->convertSwingH(swingh)); - ac->setQuiet(quiet); - // FIXME - Econo - // ac->setEcono(econo); - // No Turbo setting available. - // No Light setting available. - // No Filter setting available. - // No Clean setting available. - // No Beep setting available. - // No Sleep setting available. - // No Clock setting available. - ac->send(); -} -#endif // SEND_MITSUBISHI112 - -#if SEND_MITSUBISHI136 -/// Send a Mitsubishi 136-bit A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRMitsubishi136 object to use. -/// @param[in] on The power setting. -/// @param[in] mode The operation mode setting. -/// @param[in] degrees The temperature setting in degrees. -/// @param[in] fan The speed setting for the fan. -/// @param[in] swingv The vertical swing setting. -/// @param[in] quiet Run the device in quiet/silent mode. -void IRac::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) { - ac->begin(); - ac->setPower(on); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan)); - ac->setSwingV(ac->convertSwingV(swingv)); - // No Horizontal Swing setting available. - ac->setQuiet(quiet); - // No Turbo setting available. - // No Light setting available. - // No Filter setting available. - // No Clean setting available. - // No Beep setting available. - // No Sleep setting available. - // No Clock setting available. - ac->send(); -} -#endif // SEND_MITSUBISHI136 - -#if SEND_MITSUBISHIHEAVY -/// Send a Mitsubishi Heavy 88-bit A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRMitsubishiHeavy88Ac object to use. -/// @param[in] on The power setting. -/// @param[in] mode The operation mode setting. -/// @param[in] degrees The temperature setting in degrees. -/// @param[in] fan The speed setting for the fan. -/// @param[in] swingv The vertical swing setting. -/// @param[in] swingh The horizontal swing setting. -/// @param[in] turbo Run the device in turbo/powerful mode. -/// @param[in] econo Run the device in economical mode. -/// @param[in] clean Turn on the self-cleaning mode. e.g. Mould, dry filters etc -void IRac::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) { - ac->begin(); - ac->setPower(on); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan)); - ac->setSwingVertical(ac->convertSwingV(swingv)); - ac->setSwingHorizontal(ac->convertSwingH(swingh)); - // No Quiet setting available. - ac->setTurbo(turbo); - // No Light setting available. - ac->setEcono(econo); - // No Filter setting available. - ac->setClean(clean); - // No Beep setting available. - // No Sleep setting available. - // No Clock setting available. - ac->send(); -} - -/// Send a Mitsubishi Heavy 152-bit A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRMitsubishiHeavy152Ac object to use. -/// @param[in] on The power setting. -/// @param[in] mode The operation mode setting. -/// @param[in] degrees The temperature setting in degrees. -/// @param[in] fan The speed setting for the fan. -/// @param[in] swingv The vertical swing setting. -/// @param[in] swingh The horizontal swing setting. -/// @param[in] quiet Run the device in quiet/silent mode. -/// @param[in] turbo Run the device in turbo/powerful mode. -/// @param[in] econo Run the device in economical mode. -/// @param[in] filter Turn on the (ion/pollen/etc) filter mode. -/// @param[in] clean Turn on the self-cleaning mode. e.g. Mould, dry filters etc -/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on. -void IRac::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) { - ac->begin(); - ac->setPower(on); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan)); - ac->setSwingVertical(ac->convertSwingV(swingv)); - ac->setSwingHorizontal(ac->convertSwingH(swingh)); - ac->setSilent(quiet); - ac->setTurbo(turbo); - // No Light setting available. - ac->setEcono(econo); - ac->setClean(clean); - ac->setFilter(filter); - // No Beep setting available. - ac->setNight(sleep >= 0); // Sleep is either on/off, so convert to boolean. - // No Clock setting available. - ac->send(); -} -#endif // SEND_MITSUBISHIHEAVY - -#if SEND_NEOCLIMA -/// Send a Neoclima A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRNeoclimaAc object to use. -/// @param[in] on The power setting. -/// @param[in] mode The operation mode setting. -/// @param[in] celsius Temperature units. True is Celsius, False is Fahrenheit. -/// @param[in] degrees The temperature setting in degrees. -/// @param[in] fan The speed setting for the fan. -/// @param[in] swingv The vertical swing setting. -/// @param[in] swingh The horizontal swing setting. -/// @param[in] turbo Run the device in turbo/powerful mode. -/// @param[in] econo Run the device in economical mode. -/// @param[in] light Turn on the LED/Display mode. -/// @param[in] filter Turn on the (ion/pollen/etc) filter mode. -/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on. -void IRac::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) { - ac->begin(); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees, celsius); - ac->setFan(ac->convertFan(fan)); - ac->setSwingV(swingv != stdAc::swingv_t::kOff); - ac->setSwingH(swingh != stdAc::swingh_t::kOff); - // No Quiet setting available. - ac->setTurbo(turbo); - ac->setLight(light); - ac->setEcono(econo); - ac->setIon(filter); - // No Clean setting available. - // No Beep setting available. - ac->setSleep(sleep >= 0); // Sleep is either on/off, so convert to boolean. - // No Clock setting available. - ac->setPower(on); - ac->send(); -} -#endif // SEND_NEOCLIMA - -#if SEND_PANASONIC_AC -/// Send a Panasonic A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRPanasonicAc object to use. -/// @param[in] model The A/C model to use. -/// @param[in] on The power setting. -/// @param[in] mode The operation mode setting. -/// @param[in] degrees The temperature setting in degrees. -/// @param[in] fan The speed setting for the fan. -/// @param[in] swingv The vertical swing setting. -/// @param[in] swingh The horizontal swing setting. -/// @param[in] quiet Run the device in quiet/silent mode. -/// @param[in] turbo Run the device in turbo/powerful mode. -/// @param[in] filter Turn on the (ion/pollen/etc) filter mode. -/// @param[in] clock The time in Nr. of mins since midnight. < 0 is ignore. -void IRac::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) { - ac->begin(); - ac->setModel(model); - ac->setPower(on); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan)); - ac->setSwingVertical(ac->convertSwingV(swingv)); - ac->setSwingHorizontal(ac->convertSwingH(swingh)); - ac->setQuiet(quiet); - ac->setPowerful(turbo); - ac->setIon(filter); - // No Light setting available. - // No Econo setting available. - // No Clean setting available. - // No Beep setting available. - // No Sleep setting available. - if (clock >= 0) ac->setClock(clock); - ac->send(); -} -#endif // SEND_PANASONIC_AC - -#if SEND_PANASONIC_AC32 -/// Send a Panasonic A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRPanasonicAc32 object to use. -/// @param[in] on The power setting. -/// @param[in] mode The operation mode setting. -/// @param[in] degrees The temperature setting in degrees. -/// @param[in] fan The speed setting for the fan. -/// @param[in] swingv The vertical swing setting. -/// @param[in] swingh The horizontal swing setting. -void IRac::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) { - ac->begin(); - ac->setPowerToggle(on); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan)); - ac->setSwingVertical(ac->convertSwingV(swingv)); - ac->setSwingHorizontal(swingh != stdAc::swingh_t::kOff); - // No Quiet setting available. - // No Turbo setting available. - // No Filter setting available. - // No Light setting available. - // No Econo setting available. - // No Clean setting available. - // No Beep setting available. - // No Sleep setting available. - // No Clock setting available. - ac->send(); -} -#endif // SEND_PANASONIC_AC32 - -#if SEND_SAMSUNG_AC -/// Send a Samsung A/C message with the supplied settings. -/// @note Multiple IR messages may be generated & sent. -/// @param[in, out] ac A Ptr to an IRSamsungAc object to use. -/// @param[in] on The power setting. -/// @param[in] mode The operation mode setting. -/// @param[in] degrees The temperature setting in degrees. -/// @param[in] fan The speed setting for the fan. -/// @param[in] swingv The vertical swing setting. -/// @param[in] quiet Run the device in quiet/silent mode. -/// @param[in] turbo Run the device in turbo/powerful mode. -/// @param[in] light Turn on the LED/Display mode. -/// @param[in] filter Turn on the (ion/pollen/etc) filter mode. -/// @param[in] clean Turn on the self-cleaning mode. e.g. Mould, dry filters etc -/// @param[in] beep Enable/Disable beeps when receiving IR messages. -/// @param[in] prevpower The power setting from the previous A/C state. -/// @param[in] forcepower Do we force send the special power message? -void IRac::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 bool quiet, const bool turbo, const bool light, - const bool filter, const bool clean, - const bool beep, const bool prevpower, - const bool forcepower) { - ac->begin(); - ac->stateReset(forcepower, prevpower); - ac->setPower(on); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan)); - ac->setSwing(swingv != stdAc::swingv_t::kOff); - // No Horizontal swing setting available. - ac->setQuiet(quiet); - ac->setPowerful(turbo); - ac->setDisplay(light); - // No Econo setting available. - ac->setIon(filter); - ac->setClean(clean); - ac->setBeep(beep); - // No Sleep setting available. - // No Clock setting available. - // Do setMode() again as it can affect fan speed. - ac->setMode(ac->convertMode(mode)); - ac->send(); -} -#endif // SEND_SAMSUNG_AC - -#if SEND_SANYO_AC -/// Send a Sanyo A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRSanyoAc object to use. -/// @param[in] on The power setting. -/// @param[in] mode The operation mode setting. -/// @param[in] degrees The temperature setting in degrees. -/// @param[in] fan The speed setting for the fan. -/// @param[in] swingv The vertical swing setting. -/// @param[in] beep Enable/Disable beeps when receiving IR messages. -/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on. -void IRac::sanyo(IRSanyoAc *ac, - const bool on, const stdAc::opmode_t mode, - const float degrees, const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, const bool beep, - const int16_t sleep) { - ac->begin(); - ac->setPower(on); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan)); - ac->setSwingV(ac->convertSwingV(swingv)); - // No Horizontal swing setting available. - // No Quiet setting available. - // No Turbo setting available. - // No Econo setting available. - // No Light setting available. - // No Filter setting available. - // No Clean setting available. - ac->setBeep(beep); - ac->setSleep(sleep >= 0); // Sleep is either on/off, so convert to boolean. - // No Clock setting available. - - // Extra - ac->setSensor(true); // Set the A/C to use the temp sensor in the Unit/Wall. - ac->setSensorTemp(degrees); // Set the sensor temp to the desired temp. - ac->send(); -} -#endif // SEND_SANYO_AC - -#if SEND_SANYO_AC88 -/// Send a Sanyo 88-bit A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRSanyoAc88 object to use. -/// @param[in] on The power setting. -/// @param[in] mode The operation mode setting. -/// @param[in] degrees The temperature setting in degrees. -/// @param[in] fan The speed setting for the fan. -/// @param[in] swingv The vertical swing setting. -/// @param[in] turbo Run the device in turbo/powerful mode. -/// @param[in] filter Turn on the (ion/pollen/etc) filter mode. -/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on. -/// @param[in] clock The time in Nr. of mins since midnight. < 0 is ignore. -void IRac::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, - const int16_t clock) { - ac->begin(); - ac->setPower(on); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan)); - ac->setSwingV(swingv != stdAc::swingv_t::kOff); - // No Horizontal swing setting available. - // No Quiet setting available. - ac->setTurbo(turbo); - // No Econo setting available. - // No Light setting available. - ac->setFilter(filter); - // No Clean setting available. - // No Beep setting available. - ac->setSleep(sleep >= 0); // Sleep is either on/off, so convert to boolean. - if (clock >= 0) ac->setClock(clock); - ac->send(); -} -#endif // SEND_SANYO_AC88 - -#if SEND_SHARP_AC -/// Send a Sharp A/C message with the supplied settings. -/// @note Multiple IR messages may be generated & sent. -/// @param[in, out] ac A Ptr to an IRSharpAc object to use. -/// @param[in] model The A/C model to use. -/// @param[in] on The power setting. -/// @param[in] prev_power The power setting from the previous A/C state. -/// @param[in] mode The operation mode setting. -/// @param[in] degrees The temperature setting in degrees. -/// @param[in] fan The speed setting for the fan. -/// @param[in] swingv The vertical swing setting. -/// @param[in] swingv_prev The previous vertical swing setting. -/// @param[in] turbo Run the device in turbo/powerful mode. -/// @param[in] light Turn on the LED/Display mode. -/// @param[in] filter Turn on the (ion/pollen/etc) filter mode. -/// @param[in] clean Turn on the self-cleaning mode. e.g. Mould, dry filters etc -void IRac::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) { - ac->begin(); - ac->setModel(model); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan, model)); - if (swingv != swingv_prev) ac->setSwingV(ac->convertSwingV(swingv)); - // Econo deliberately not used as it cycles through 3 modes uncontrollably. - // ac->setEconoToggle(econo); - ac->setIon(filter); - // No Horizontal swing setting available. - // No Quiet setting available. - ac->setLightToggle(light); - // No Beep setting available. - // No Sleep setting available. - // No Clock setting available. - // Do setMode() again as it can affect fan speed and temp. - ac->setMode(ac->convertMode(mode)); - // Clean after mode, as it can affect the mode, temp & fan speed. - if (clean) { - // A/C needs to be off before we can enter clean mode. - ac->setPower(false, prev_power); - ac->send(); - } - ac->setClean(clean); - ac->setPower(on, prev_power); - if (turbo) { - ac->send(); // Send the current state. - // Set up turbo mode as it needs to be sent after everything else. - ac->setTurbo(true); - } - ac->send(); -} -#endif // SEND_SHARP_AC - -#if SEND_TCL112AC -/// Send a TCL 112-bit A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRTcl112Ac object to use. -/// @param[in] model The A/C model to use. -/// @param[in] on The power setting. -/// @param[in] mode The operation mode setting. -/// @param[in] degrees The temperature setting in degrees. -/// @param[in] fan The speed setting for the fan. -/// @param[in] swingv The vertical swing setting. -/// @param[in] swingh The horizontal swing setting. -/// @param[in] quiet Run the device in quiet/silent mode. -/// @param[in] turbo Run the device in turbo/powerful mode. -/// @param[in] light Turn on the LED/Display mode. -/// @param[in] econo Run the device in economical mode. -/// @param[in] filter Turn on the (ion/pollen/etc) filter mode. -void IRac::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) { - ac->begin(); - ac->setModel(model); - ac->setPower(on); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan)); - ac->setSwingVertical(ac->convertSwingV(swingv)); - ac->setSwingHorizontal(swingh != stdAc::swingh_t::kOff); - ac->setQuiet(quiet); - ac->setTurbo(turbo); - ac->setLight(light); - ac->setEcono(econo); - ac->setHealth(filter); - // No Clean setting available. - // No Beep setting available. - // No Sleep setting available. - // No Clock setting available. - ac->send(); -} -#endif // SEND_TCL112AC - -#if SEND_TECHNIBEL_AC -/// Send a Technibel A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRTechnibelAc object to use. -/// @param[in] on The power setting. -/// @param[in] mode The operation mode setting. -/// @param[in] celsius Temperature units. True is Celsius, False is Fahrenheit. -/// @param[in] degrees The temperature setting in degrees. -/// @param[in] fan The speed setting for the fan. -/// @param[in] swingv The vertical swing setting. -/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on. -void IRac::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) { - ac->begin(); - ac->setPower(on); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees, !celsius); - ac->setFan(ac->convertFan(fan)); - ac->setSwing(swingv != stdAc::swingv_t::kOff); - // No Horizontal swing setting available. - // No Quiet setting available. - // No Turbo setting available. - // No Light setting available. - // No Filter setting available. - // No Clean setting available. - // No Beep setting available. - ac->setSleep(sleep >= 0); // Sleep is either on/off, so convert to boolean. - // No Clock setting available. - ac->send(); -} -#endif // SEND_TECHNIBEL_AC - -#if SEND_TECO -/// Send a Teco A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRTecoAc object to use. -/// @param[in] on The power setting. -/// @param[in] mode The operation mode setting. -/// @param[in] degrees The temperature setting in degrees. -/// @param[in] fan The speed setting for the fan. -/// @param[in] swingv The vertical swing setting. -/// @param[in] light Turn on the LED/Display mode. -/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on. -void IRac::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) { - ac->begin(); - ac->setPower(on); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan)); - ac->setSwing(swingv != stdAc::swingv_t::kOff); - // No Horizontal swing setting available. - // No Quiet setting available. - // No Turbo setting available. - ac->setLight(light); - // No Filter setting available. - // No Clean setting available. - // No Beep setting available. - ac->setSleep(sleep >= 0); // Sleep is either on/off, so convert to boolean. - // No Clock setting available. - ac->send(); -} -#endif // SEND_TECO - -#if SEND_TOSHIBA_AC -/// Send a Toshiba A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRToshibaAC object to use. -/// @param[in] on The power setting. -/// @param[in] mode The operation mode setting. -/// @param[in] degrees The temperature setting in degrees. -/// @param[in] fan The speed setting for the fan. -/// @param[in] swingv The vertical swing setting. -/// @param[in] turbo Run the device in turbo/powerful mode. -/// @param[in] econo Run the device in economical mode. -void IRac::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) { - ac->begin(); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan)); - // The API has no "step" option, so off is off, anything else is on. - ac->setSwing((swingv == stdAc::swingv_t::kOff) ? kToshibaAcSwingOff - : kToshibaAcSwingOn); - // No Horizontal swing setting available. - // No Quiet setting available. - ac->setTurbo(turbo); - ac->setEcono(econo); - // No Light setting available. - // No Filter setting available. - // No Clean setting available. - // No Beep setting available. - // No Sleep setting available. - // No Clock setting available. - // Do this last because Toshiba A/C has an odd quirk with how power off works. - ac->setPower(on); - ac->send(); -} -#endif // SEND_TOSHIBA_AC - -#if SEND_TROTEC -/// Send a Trotec A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRTrotecESP object to use. -/// @param[in] on The power setting. -/// @param[in] mode The operation mode setting. -/// @param[in] degrees The temperature setting in degrees. -/// @param[in] fan The speed setting for the fan. -/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on. -void IRac::trotec(IRTrotecESP *ac, - const bool on, const stdAc::opmode_t mode, - const float degrees, const stdAc::fanspeed_t fan, - const int16_t sleep) { - ac->begin(); - ac->setPower(on); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - ac->setSpeed(ac->convertFan(fan)); - // No Vertical swing setting available. - // No Horizontal swing setting available. - // No Quiet setting available. - // No Turbo setting available. - // No Light setting available. - // No Filter setting available. - // No Clean setting available. - // No Beep setting available. - ac->setSleep(sleep >= 0); // Sleep is either on/off, so convert to boolean. - // No Clock setting available. - ac->send(); -} -#endif // SEND_TROTEC - -#if SEND_TROTEC_3550 -/// Send a Trotec 3550 A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRTrotecESP object to use. -/// @param[in] on The power setting. -/// @param[in] mode The operation mode setting. -/// @param[in] celsius Temperature units. True is Celsius, False is Fahrenheit. -/// @param[in] degrees The temperature setting in degrees. -/// @param[in] fan The speed setting for the fan. -/// @param[in] swingv The vertical swing setting. -void IRac::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) { - ac->begin(); - ac->setPower(on); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees, celsius); - ac->setFan(ac->convertFan(fan)); - ac->setSwingV(swingv != stdAc::swingv_t::kOff); - // No Horizontal swing setting available. - // No Quiet setting available. - // No Turbo setting available. - // No Light setting available. - // No Filter setting available. - // No Clean setting available. - // No Beep setting available. - // No Sleep setting available. - // No Clock setting available. - ac->send(); -} -#endif // SEND_TROTEC_3550 - -#if SEND_TRUMA -/// Send a Truma A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRTrumaAc object to use. -/// @param[in] on The power setting. -/// @param[in] mode The operation mode setting. -/// @param[in] degrees The temperature setting in degrees. -/// @param[in] fan The speed setting for the fan. -/// @param[in] quiet Run the device quietly if we can. -void IRac::truma(IRTrumaAc *ac, - const bool on, const stdAc::opmode_t mode, - const float degrees, const stdAc::fanspeed_t fan, - const bool quiet) { - ac->begin(); - ac->setPower(on); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan)); - ac->setQuiet(quiet); // Only available in Cool mode. - // No Vertical swing setting available. - // No Horizontal swing setting available. - // No Turbo setting available. - // No Light setting available. - // No Filter setting available. - // No Clean setting available. - // No Beep setting available. - // No Sleep setting available. - // No Clock setting available. - ac->send(); -} -#endif // SEND_TRUMA - -#if SEND_VESTEL_AC -/// Send a Vestel A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRVestelAc object to use. -/// @param[in] on The power setting. -/// @param[in] mode The operation mode setting. -/// @param[in] degrees The temperature setting in degrees. -/// @param[in] fan The speed setting for the fan. -/// @param[in] swingv The vertical swing setting. -/// @param[in] turbo Run the device in turbo/powerful mode. -/// @param[in] filter Turn on the (ion/pollen/etc) filter mode. -/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on. -/// @param[in] clock The time in Nr. of mins since midnight. < 0 is ignore. -/// @param[in] sendNormal Do we send a Normal settings message at all? -/// i.e In addition to the clock/time/timer message -void IRac::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, - const int16_t clock, const bool sendNormal) { - ac->begin(); - ac->setPower(on); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan)); - ac->setSwing(swingv != stdAc::swingv_t::kOff); - // No Horizontal swing setting available. - // No Quiet setting available. - ac->setTurbo(turbo); - // No Light setting available. - ac->setIon(filter); - // No Clean setting available. - // No Beep setting available. - ac->setSleep(sleep >= 0); // Sleep is either on/off, so convert to boolean. - if (sendNormal) ac->send(); // Send the normal message. - if (clock >= 0) { - ac->setTime(clock); - ac->send(); // Setting the clock requires a different "timer" message. - } -} -#endif // SEND_VESTEL_AC - -#if SEND_VOLTAS -/// Send a Voltas A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRVoltas object to use. -/// @param[in] model The A/C model to use. -/// @param[in] on The power setting. -/// @param[in] mode The operation mode setting. -/// @param[in] degrees The temperature setting in degrees. -/// @param[in] fan The speed setting for the fan. -/// @param[in] swingv The vertical swing setting. -/// @param[in] swingh The horizontal swing setting. -/// @param[in] turbo Run the device in turbo/powerful mode. -/// @param[in] econo Run the device in economical mode. -/// @param[in] light Turn on the LED/Display mode. -/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on. -void IRac::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) { - ac->begin(); - ac->setModel(model); - ac->setPower(on); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan)); - ac->setSwingV(swingv != stdAc::swingv_t::kOff); - ac->setSwingH(swingh != stdAc::swingh_t::kOff); - // No Quiet setting available. - ac->setTurbo(turbo); - ac->setEcono(econo); - ac->setLight(light); - // No Filter setting available. - // No Clean setting available. - // No Beep setting available. - ac->setSleep(sleep >= 0); // Sleep is either on/off, so convert to boolean. - // No Clock setting available. - ac->send(); -} -#endif // SEND_VOLTAS - -#if SEND_WHIRLPOOL_AC -/// Send a Whirlpool A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRWhirlpoolAc object to use. -/// @param[in] model The A/C model to use. -/// @param[in] on The power setting. -/// @param[in] mode The operation mode setting. -/// @param[in] degrees The temperature setting in degrees. -/// @param[in] fan The speed setting for the fan. -/// @param[in] swingv The vertical swing setting. -/// @param[in] turbo Run the device in turbo/powerful mode. -/// @param[in] light Turn on the LED/Display mode. -/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on. -/// @param[in] clock The time in Nr. of mins since midnight. < 0 is ignore. -void IRac::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, const int16_t clock) { - ac->begin(); - ac->setModel(model); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan)); - ac->setSwing(swingv != stdAc::swingv_t::kOff); - // No Horizontal swing setting available. - // No Quiet setting available. - ac->setSuper(turbo); - ac->setLight(light); - // No Filter setting available - // No Clean setting available. - // No Beep setting available. - ac->setSleep(sleep >= 0); // Sleep is either on/off, so convert to boolean. - if (clock >= 0) ac->setClock(clock); - ac->setPowerToggle(on); - ac->send(); -} -#endif // SEND_WHIRLPOOL_AC - -#if SEND_TRANSCOLD -/// Send a Transcold A/C message with the supplied settings. -/// @note May result in multiple messages being sent. -/// @param[in, out] ac A Ptr to an IRTranscoldAc object to use. -/// @param[in] on The power setting. -/// @param[in] mode The operation mode setting. -/// @param[in] degrees The temperature setting in degrees. -/// @param[in] fan The speed setting for the fan. -/// @param[in] swingv The vertical swing setting. -/// @param[in] swingh The horizontal swing setting. -/// @note -1 is Off, >= 0 is on. -void IRac::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) { - ac->begin(); - ac->setPower(on); - if (!on) { - // after turn off AC no more commands should - // be accepted - ac->send(); - return; - } - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan)); - // No Filter setting available. - // No Beep setting available. - // No Clock setting available. - // No Econo setting available. - // No Quiet setting available. - if (swingv != stdAc::swingv_t::kOff || swingh != stdAc::swingh_t::kOff) { - // Swing has a special command that needs to be sent independently. - ac->setSwing(); - ac->send(); - } - - ac->send(); -} -#endif // SEND_TRANSCOLD - -#if SEND_RHOSS -/// Send an Rhoss A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRRhossAc object to use. -/// @param[in] on The power setting. -/// @param[in] mode The operation mode setting. -/// @param[in] degrees The temperature setting in degrees. -/// @param[in] fan The speed setting for the fan. -/// @param[in] swing The swing setting. -void IRac::rhoss(IRRhossAc *ac, - const bool on, const stdAc::opmode_t mode, const float degrees, - const stdAc::fanspeed_t fan, const stdAc::swingv_t swing) { - ac->begin(); - ac->setPower(on); - ac->setMode(ac->convertMode(mode)); - ac->setSwing(swing != stdAc::swingv_t::kOff); - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan)); - // No Quiet setting available. - // No Light setting available. - // No Filter setting available. - // No Turbo setting available. - // No Economy setting available. - // No Clean setting available. - // No Beep setting available. - // No Sleep setting available. - ac->send(); -} -#endif // SEND_RHOSS - -/// Create a new state base on the provided state that has been suitably fixed. -/// @note This is for use with Home Assistant, which requires mode to be off if -/// the power is off. -/// @param[in] state The state_t structure describing the desired a/c state. -/// @return A stdAc::state_t with the needed settings. -stdAc::state_t IRac::cleanState(const stdAc::state_t state) { - stdAc::state_t result = state; - // A hack for Home Assistant, it appears to need/want an Off opmode. - // So enforce the power is off if the mode is also off. - if (state.mode == stdAc::opmode_t::kOff) result.power = false; - return result; -} - -/// Create a new state base on desired & previous states but handle -/// any state changes for options that need to be toggled. -/// @param[in] desired The state_t structure describing the desired a/c state. -/// @param[in] prev A Ptr to the previous state_t structure. -/// @return A stdAc::state_t with the needed settings. -stdAc::state_t IRac::handleToggles(const stdAc::state_t desired, - const stdAc::state_t *prev) { - stdAc::state_t result = desired; - // If we've been given a previous state AND the it's the same A/C basically. - if (prev != NULL && desired.protocol == prev->protocol && - desired.model == prev->model) { - // Check if we have to handle toggle settings for specific A/C protocols. - switch (desired.protocol) { - case decode_type_t::COOLIX: - case decode_type_t::TRANSCOLD: - if ((desired.swingv == stdAc::swingv_t::kOff) ^ - (prev->swingv == stdAc::swingv_t::kOff)) // It changed, so toggle. - result.swingv = stdAc::swingv_t::kAuto; - else - result.swingv = stdAc::swingv_t::kOff; // No change, so no toggle. - result.turbo = desired.turbo ^ prev->turbo; - result.light = desired.light ^ prev->light; - result.clean = desired.clean ^ prev->clean; - result.sleep = ((desired.sleep >= 0) ^ (prev->sleep >= 0)) ? 0 : -1; - break; - case decode_type_t::DAIKIN128: - result.power = desired.power ^ prev->power; - result.light = desired.light ^ prev->light; - break; - case decode_type_t::ELECTRA_AC: - result.light = desired.light ^ prev->light; - break; - case decode_type_t::FUJITSU_AC: - result.turbo = desired.turbo ^ prev->turbo; - result.econo = desired.econo ^ prev->econo; - break; - case decode_type_t::MIDEA: - result.turbo = desired.turbo ^ prev->turbo; - result.econo = desired.econo ^ prev->econo; - result.light = desired.light ^ prev->light; - // FALL THRU - case decode_type_t::CORONA_AC: - case decode_type_t::HITACHI_AC344: - case decode_type_t::HITACHI_AC424: - if ((desired.swingv == stdAc::swingv_t::kOff) ^ - (prev->swingv == stdAc::swingv_t::kOff)) // It changed, so toggle. - result.swingv = stdAc::swingv_t::kAuto; - else - result.swingv = stdAc::swingv_t::kOff; // No change, so no toggle. - break; - case decode_type_t::SHARP_AC: - result.light = desired.light ^ prev->light; - if ((desired.swingv == stdAc::swingv_t::kOff) ^ - (prev->swingv == stdAc::swingv_t::kOff)) // It changed, so toggle. - result.swingv = stdAc::swingv_t::kAuto; - else - result.swingv = stdAc::swingv_t::kOff; // No change, so no toggle. - break; - case decode_type_t::KELON: - if ((desired.swingv == stdAc::swingv_t::kOff) ^ - (prev->swingv == stdAc::swingv_t::kOff)) // It changed, so toggle. - result.swingv = stdAc::swingv_t::kAuto; - else - result.swingv = stdAc::swingv_t::kOff; // No change, so no toggle. - // FALL-THRU - case decode_type_t::AIRWELL: - case decode_type_t::DAIKIN64: - case decode_type_t::PANASONIC_AC32: - case decode_type_t::WHIRLPOOL_AC: - result.power = desired.power ^ prev->power; - break; - case decode_type_t::PANASONIC_AC: - // CKP models use a power mode toggle. - if (desired.model == panasonic_ac_remote_model_t::kPanasonicCkp) - result.power = desired.power ^ prev->power; - break; - default: - {}; - } - } - return result; -} - -/// Send A/C message for a given device using common A/C settings. -/// @param[in] vendor The vendor/protocol type. -/// @param[in] model The A/C model if applicable. -/// @param[in] power The power setting. -/// @param[in] mode The operation mode setting. -/// @note Changing mode from "Off" to something else does NOT turn on a device. -/// You need to use `power` for that. -/// @param[in] degrees The temperature setting in degrees. -/// @param[in] celsius Temperature units. True is Celsius, False is Fahrenheit. -/// @param[in] fan The speed setting for the fan. -/// @note The following are all "if supported" by the underlying A/C classes. -/// @param[in] swingv The vertical swing setting. -/// @param[in] swingh The horizontal swing setting. -/// @param[in] quiet Run the device in quiet/silent mode. -/// @param[in] turbo Run the device in turbo/powerful mode. -/// @param[in] econo Run the device in economical mode. -/// @param[in] light Turn on the LED/Display mode. -/// @param[in] filter Turn on the (ion/pollen/etc) filter mode. -/// @param[in] clean Turn on the self-cleaning mode. e.g. Mould, dry filters etc -/// @param[in] beep Enable/Disable beeps when receiving IR messages. -/// @param[in] sleep Nr. of minutes for sleep mode. -/// -1 is Off, >= 0 is on. Some devices it is the nr. of mins to run for. -/// Others it may be the time to enter/exit sleep mode. -/// i.e. Time in Nr. of mins since midnight. -/// @param[in] clock The time in Nr. of mins since midnight. < 0 is ignore. -/// @return True, if accepted/converted/attempted etc. False, if unsupported. -bool IRac::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, const int16_t clock) { - stdAc::state_t to_send; - initState(&to_send, vendor, model, power, mode, degrees, celsius, fan, swingv, - swingh, quiet, turbo, econo, light, filter, clean, beep, sleep, - clock); - return this->sendAc(to_send, &to_send); -} - -/// Send A/C message for a given device using state_t structures. -/// @param[in] desired The state_t structure describing the desired new ac state -/// @param[in] prev A Ptr to the state_t structure containing the previous state -/// @note Changing mode from "Off" to something else does NOT turn on a device. -/// You need to use `power` for that. -/// @return True, if accepted/converted/attempted etc. False, if unsupported. -bool IRac::sendAc(const stdAc::state_t desired, const stdAc::state_t *prev) { - // Convert the temp from Fahrenheit to Celsius if we are not in Celsius mode. - float degC __attribute__((unused)) = - desired.celsius ? desired.degrees : fahrenheitToCelsius(desired.degrees); - // special `state_t` that is required to be sent based on that. - stdAc::state_t send = this->handleToggles(this->cleanState(desired), prev); - // Some protocols expect a previous state for power. - // Construct a pointer-safe previous power state incase prev is NULL/NULLPTR. -#if (SEND_HITACHI_AC1 || SEND_SAMSUNG_AC || SEND_SHARP_AC) - const bool prev_power = (prev != NULL) ? prev->power : !send.power; -#endif // (SEND_HITACHI_AC1 || SEND_SAMSUNG_AC || SEND_SHARP_AC) -#if (SEND_LG || SEND_SHARP_AC) - const stdAc::swingv_t prev_swingv = (prev != NULL) ? prev->swingv - : stdAc::swingv_t::kOff; -#endif // (SEND_LG || SEND_SHARP_AC) - // Per vendor settings & setup. - switch (send.protocol) { -#if SEND_AIRWELL - case AIRWELL: - { - IRAirwellAc ac(_pin, _inverted, _modulation); - airwell(&ac, send.power, send.mode, degC, send.fanspeed); - break; - } -#endif // SEND_AIRWELL -#if SEND_AMCOR - case AMCOR: - { - IRAmcorAc ac(_pin, _inverted, _modulation); - amcor(&ac, send.power, send.mode, degC, send.fanspeed); - break; - } -#endif // SEND_AMCOR -#if SEND_ARGO - case ARGO: - { - IRArgoAC ac(_pin, _inverted, _modulation); - argo(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv, - send.turbo, send.sleep); - break; - } -#endif // SEND_ARGO -#if SEND_CARRIER_AC64 - case CARRIER_AC64: - { - IRCarrierAc64 ac(_pin, _inverted, _modulation); - carrier64(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv, - send.sleep); - break; - } -#endif // SEND_CARRIER_AC64 -#if SEND_COOLIX - case COOLIX: - { - IRCoolixAC ac(_pin, _inverted, _modulation); - coolix(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv, - send.swingh, send.turbo, send.light, send.clean, send.sleep); - break; - } -#endif // SEND_COOLIX -#if SEND_CORONA_AC - case CORONA_AC: - { - IRCoronaAc ac(_pin, _inverted, _modulation); - corona(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv, - send.econo); - break; - } -#endif // SEND_CORONA_AC -#if SEND_DAIKIN - case DAIKIN: - { - IRDaikinESP ac(_pin, _inverted, _modulation); - daikin(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv, - send.swingh, send.quiet, send.turbo, send.econo, send.clean); - break; - } -#endif // SEND_DAIKIN -#if SEND_DAIKIN128 - case DAIKIN128: - { - IRDaikin128 ac(_pin, _inverted, _modulation); - daikin128(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv, - send.quiet, send.turbo, send.light, send.econo, send.sleep, - send.clock); - break; - } -#endif // SEND_DAIKIN2 -#if SEND_DAIKIN152 - case DAIKIN152: - { - IRDaikin152 ac(_pin, _inverted, _modulation); - daikin152(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv, - send.quiet, send.turbo, send.econo); - break; - } -#endif // SEND_DAIKIN152 -#if SEND_DAIKIN160 - case DAIKIN160: - { - IRDaikin160 ac(_pin, _inverted, _modulation); - daikin160(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv); - break; - } -#endif // SEND_DAIKIN160 -#if SEND_DAIKIN176 - case DAIKIN176: - { - IRDaikin176 ac(_pin, _inverted, _modulation); - daikin176(&ac, send.power, send.mode, degC, send.fanspeed, send.swingh); - break; - } -#endif // SEND_DAIKIN176 -#if SEND_DAIKIN2 - case DAIKIN2: - { - IRDaikin2 ac(_pin, _inverted, _modulation); - daikin2(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv, - send.swingh, send.quiet, send.turbo, send.light, send.econo, - send.filter, send.clean, send.beep, send.sleep, send.clock); - break; - } -#endif // SEND_DAIKIN2 -#if SEND_DAIKIN216 - case DAIKIN216: - { - IRDaikin216 ac(_pin, _inverted, _modulation); - daikin216(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv, - send.swingh, send.quiet, send.turbo); - break; - } -#endif // SEND_DAIKIN216 -#if SEND_DAIKIN64 - case DAIKIN64: - { - IRDaikin64 ac(_pin, _inverted, _modulation); - daikin64(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv, - send.quiet, send.turbo, send.sleep, send.clock); - break; - } -#endif // SEND_DAIKIN64 -#if SEND_DELONGHI_AC - case DELONGHI_AC: - { - IRDelonghiAc ac(_pin, _inverted, _modulation); - delonghiac(&ac, send.power, send.mode, send.celsius, degC, send.fanspeed, - send.turbo, send.sleep); - break; - } -#endif // SEND_DELONGHI_AC -#if SEND_ECOCLIM - case ECOCLIM: - { - IREcoclimAc ac(_pin, _inverted, _modulation); - ecoclim(&ac, send.power, send.mode, degC, send.fanspeed, send.clock); - break; - } -#endif // SEND_ECOCLIM -#if SEND_ELECTRA_AC - case ELECTRA_AC: - { - IRElectraAc ac(_pin, _inverted, _modulation); - electra(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv, - send.swingh, send.turbo, send.light, send.clean); - break; - } -#endif // SEND_ELECTRA_AC -#if SEND_FUJITSU_AC - case FUJITSU_AC: - { - IRFujitsuAC ac(_pin, (fujitsu_ac_remote_model_t)send.model, _inverted, - _modulation); - fujitsu(&ac, (fujitsu_ac_remote_model_t)send.model, send.power, send.mode, - send.celsius, send.degrees, send.fanspeed, - send.swingv, send.swingh, send.quiet, - send.turbo, send.econo, send.filter, send.clean); - break; - } -#endif // SEND_FUJITSU_AC -#if SEND_GOODWEATHER - case GOODWEATHER: - { - IRGoodweatherAc ac(_pin, _inverted, _modulation); - goodweather(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv, - send.turbo, send.light, send.sleep); - break; - } -#endif // SEND_GOODWEATHER -#if SEND_GREE - case GREE: - { - IRGreeAC ac(_pin, (gree_ac_remote_model_t)send.model, _inverted, - _modulation); - gree(&ac, (gree_ac_remote_model_t)send.model, send.power, send.mode, - send.celsius, send.degrees, send.fanspeed, send.swingv, send.turbo, - send.light, send.clean, send.sleep); - break; - } -#endif // SEND_GREE -#if SEND_HAIER_AC - case HAIER_AC: - { - IRHaierAC ac(_pin, _inverted, _modulation); - haier(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv, - send.filter, send.sleep, send.clock); - break; - } -#endif // SEND_HAIER_AC -#if SEND_HAIER_AC176 - case HAIER_AC176: - { - IRHaierAC176 ac(_pin, _inverted, _modulation); - haier176(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv, - send.turbo, send.filter, send.sleep); - break; - } -#endif // SEND_HAIER_AC176 -#if SEND_HAIER_AC_YRW02 - case HAIER_AC_YRW02: - { - IRHaierACYRW02 ac(_pin, _inverted, _modulation); - haierYrwo2(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv, - send.turbo, send.filter, send.sleep); - break; - } -#endif // SEND_HAIER_AC_YRW02 -#if SEND_HITACHI_AC - case HITACHI_AC: - { - IRHitachiAc ac(_pin, _inverted, _modulation); - hitachi(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv, - send.swingh); - break; - } -#endif // SEND_HITACHI_AC -#if SEND_HITACHI_AC1 - case HITACHI_AC1: - { - IRHitachiAc1 ac(_pin, _inverted, _modulation); - bool power_toggle = false; - bool swing_toggle = false; - if (prev != NULL) { - power_toggle = (send.power != prev->power); - swing_toggle = (send.swingv != prev->swingv) || - (send.swingh != prev->swingh); - } - hitachi1(&ac, (hitachi_ac1_remote_model_t)send.model, send.power, - power_toggle, send.mode, degC, send.fanspeed, send.swingv, - send.swingh, swing_toggle, send.sleep); - break; - } -#endif // SEND_HITACHI_AC1 -#if SEND_HITACHI_AC344 - case HITACHI_AC344: - { - IRHitachiAc344 ac(_pin, _inverted, _modulation); - hitachi344(&ac, send.power, send.mode, degC, send.fanspeed, - send.swingv, send.swingh); - break; - } -#endif // SEND_HITACHI_AC344 -#if SEND_HITACHI_AC424 - case HITACHI_AC424: - { - IRHitachiAc424 ac(_pin, _inverted, _modulation); - hitachi424(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv); - break; - } -#endif // SEND_HITACHI_AC424 -#if SEND_KELON - case KELON: { - IRKelonAc ac(_pin, _inverted, _modulation); - kelon(&ac, send.power, send.mode, 0, send.degrees, send.fanspeed, - send.swingv != stdAc::swingv_t::kOff, send.turbo, send.sleep); - break; - } -#endif -#if SEND_KELVINATOR - case KELVINATOR: - { - IRKelvinatorAC ac(_pin, _inverted, _modulation); - kelvinator(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv, - send.swingh, send.quiet, send.turbo, send.light, send.filter, - send.clean); - break; - } -#endif // SEND_KELVINATOR -#if SEND_LG - case LG: - case LG2: - { - IRLgAc ac(_pin, _inverted, _modulation); - lg(&ac, (lg_ac_remote_model_t)send.model, send.power, send.mode, - send.degrees, send.fanspeed, send.swingv, prev_swingv, send.swingh, - send.light); - break; - } -#endif // SEND_LG -#if SEND_MIDEA - case MIDEA: - { - IRMideaAC ac(_pin, _inverted, _modulation); - midea(&ac, send.power, send.mode, send.celsius, send.degrees, - send.fanspeed, send.swingv, send.turbo, send.econo, send.light, - send.sleep); - break; - } -#endif // SEND_MIDEA -#if SEND_MITSUBISHI_AC - case MITSUBISHI_AC: - { - IRMitsubishiAC ac(_pin, _inverted, _modulation); - mitsubishi(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv, - send.swingh, send.quiet, send.clock); - break; - } -#endif // SEND_MITSUBISHI_AC -#if SEND_MITSUBISHI112 - case MITSUBISHI112: - { - IRMitsubishi112 ac(_pin, _inverted, _modulation); - mitsubishi112(&ac, send.power, send.mode, degC, send.fanspeed, - send.swingv, send.swingh, send.quiet); - break; - } -#endif // SEND_MITSUBISHI112 -#if SEND_MITSUBISHI136 - case MITSUBISHI136: - { - IRMitsubishi136 ac(_pin, _inverted, _modulation); - mitsubishi136(&ac, send.power, send.mode, degC, send.fanspeed, - send.swingv, send.quiet); - break; - } -#endif // SEND_MITSUBISHI136 -#if SEND_MITSUBISHIHEAVY - case MITSUBISHI_HEAVY_88: - { - IRMitsubishiHeavy88Ac ac(_pin, _inverted, _modulation); - mitsubishiHeavy88(&ac, send.power, send.mode, degC, send.fanspeed, - send.swingv, send.swingh, send.turbo, send.econo, - send.clean); - break; - } - case MITSUBISHI_HEAVY_152: - { - IRMitsubishiHeavy152Ac ac(_pin, _inverted, _modulation); - mitsubishiHeavy152(&ac, send.power, send.mode, degC, send.fanspeed, - send.swingv, send.swingh, send.quiet, send.turbo, - send.econo, send.filter, send.clean, send.sleep); - break; - } -#endif // SEND_MITSUBISHIHEAVY -#if SEND_NEOCLIMA - case NEOCLIMA: - { - IRNeoclimaAc ac(_pin, _inverted, _modulation); - neoclima(&ac, send.power, send.mode, send.celsius, send.degrees, - send.fanspeed, send.swingv, send.swingh, send.turbo, - send.econo, send.light, send.filter, send.sleep); - break; - } -#endif // SEND_NEOCLIMA -#if SEND_PANASONIC_AC - case PANASONIC_AC: - { - IRPanasonicAc ac(_pin, _inverted, _modulation); - panasonic(&ac, (panasonic_ac_remote_model_t)send.model, send.power, - send.mode, degC, send.fanspeed, send.swingv, send.swingh, - send.quiet, send.turbo, send.clock); - break; - } -#endif // SEND_PANASONIC_AC -#if SEND_PANASONIC_AC32 - case PANASONIC_AC32: - { - IRPanasonicAc32 ac(_pin, _inverted, _modulation); - panasonic32(&ac, send.power, send.mode, degC, send.fanspeed, - send.swingv, send.swingh); - break; - } -#endif // SEND_PANASONIC_AC32 -#if SEND_RHOSS - case RHOSS: - { - IRRhossAc ac(_pin, _inverted, _modulation); - rhoss(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv); - break; - } -#endif // SEND_RHOSS -#if SEND_SAMSUNG_AC - case SAMSUNG_AC: - { - IRSamsungAc ac(_pin, _inverted, _modulation); - samsung(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv, - send.quiet, send.turbo, send.light, send.filter, send.clean, - send.beep, prev_power); - break; - } -#endif // SEND_SAMSUNG_AC -#if SEND_SANYO_AC - case SANYO_AC: - { - IRSanyoAc ac(_pin, _inverted, _modulation); - sanyo(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv, - send.beep, send.sleep); - break; - } -#endif // SEND_SANYO_AC -#if SEND_SANYO_AC88 - case SANYO_AC88: - { - IRSanyoAc88 ac(_pin, _inverted, _modulation); - sanyo88(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv, - send.turbo, send.filter, send.sleep, send.clock); - break; - } -#endif // SEND_SANYO_AC88 -#if SEND_SHARP_AC - case SHARP_AC: - { - IRSharpAc ac(_pin, _inverted, _modulation); - sharp(&ac, (sharp_ac_remote_model_t)send.model, send.power, prev_power, - send.mode, degC, send.fanspeed, send.swingv, prev_swingv, - send.turbo, send.light, send.filter, send.clean); - break; - } -#endif // SEND_SHARP_AC -#if (SEND_TCL112AC || SEND_TEKNOPOINT) - case TCL112AC: - case TEKNOPOINT: - { - IRTcl112Ac ac(_pin, _inverted, _modulation); - tcl_ac_remote_model_t model = (tcl_ac_remote_model_t)send.model; - if (send.protocol == decode_type_t::TEKNOPOINT) - model = tcl_ac_remote_model_t::GZ055BE1; - tcl112(&ac, model, send.power, send.mode, - degC, send.fanspeed, send.swingv, send.swingh, send.quiet, - send.turbo, send.light, send.econo, send.filter); - break; - } -#endif // (SEND_TCL112AC || SEND_TEKNOPOINT) -#if SEND_TECHNIBEL_AC - case TECHNIBEL_AC: - { - IRTechnibelAc ac(_pin, _inverted, _modulation); - technibel(&ac, send.power, send.mode, send.celsius, send.degrees, - send.fanspeed, send.swingv, send.sleep); - break; - } -#endif // SEND_TECHNIBEL_AC -#if SEND_TECO - case TECO: - { - IRTecoAc ac(_pin, _inverted, _modulation); - teco(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv, - send.light, send.sleep); - break; - } -#endif // SEND_TECO -#if SEND_TOSHIBA_AC - case TOSHIBA_AC: - { - IRToshibaAC ac(_pin, _inverted, _modulation); - toshiba(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv, - send.turbo, send.econo); - break; - } -#endif // SEND_TOSHIBA_AC -#if SEND_TROTEC - case TROTEC: - { - IRTrotecESP ac(_pin, _inverted, _modulation); - trotec(&ac, send.power, send.mode, degC, send.fanspeed, send.sleep); - break; - } -#endif // SEND_TROTEC -#if SEND_TROTEC_3550 - case TROTEC_3550: - { - IRTrotec3550 ac(_pin, _inverted, _modulation); - trotec3550(&ac, send.power, send.mode, send.celsius, send.degrees, - send.fanspeed, send.swingv); - break; - } -#endif // SEND_TROTEC_3550 -#if SEND_TRUMA - case TRUMA: - { - IRTrumaAc ac(_pin, _inverted, _modulation); - truma(&ac, send.power, send.mode, degC, send.fanspeed, send.quiet); - break; - } -#endif // SEND_TRUMA -#if SEND_VESTEL_AC - case VESTEL_AC: - { - IRVestelAc ac(_pin, _inverted, _modulation); - vestel(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv, - send.turbo, send.filter, send.sleep, send.clock); - break; - } -#endif // SEND_VESTEL_AC -#if SEND_VOLTAS - case VOLTAS: - { - IRVoltas ac(_pin, _inverted, _modulation); - voltas(&ac, (voltas_ac_remote_model_t)send.model, send.power, send.mode, - degC, send.fanspeed, send.swingv, send.swingh, send.turbo, - send.econo, send.light, send.sleep); - break; - } -#endif // SEND_VOLTAS -#if SEND_WHIRLPOOL_AC - case WHIRLPOOL_AC: - { - IRWhirlpoolAc ac(_pin, _inverted, _modulation); - whirlpool(&ac, (whirlpool_ac_remote_model_t)send.model, send.power, - send.mode, degC, send.fanspeed, send.swingv, send.turbo, - send.light, send.sleep, send.clock); - break; - } -#endif // SEND_WHIRLPOOL_AC -#if SEND_TRANSCOLD - case TRANSCOLD: - { - IRTranscoldAc ac(_pin, _inverted, _modulation); - transcold(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv, - send.swingh); - break; - } -#endif // SEND_TRANSCOLD_AC - default: - return false; // Fail, didn't match anything. - } - return true; // Success. -} // NOLINT(readability/fn_size) - -/// Update the previous state to the current one. -void IRac::markAsSent(void) { - _prev = next; -} - -/// Send an A/C message based soley on our internal state. -/// @return True, if accepted/converted/attempted. False, if unsupported. -bool IRac::sendAc(void) { - bool success = this->sendAc(next, &_prev); - if (success) this->markAsSent(); - return success; -} - -/// Compare two AirCon states. -/// @note The comparison excludes the clock. -/// @param a A state_t to be compared. -/// @param b A state_t to be compared. -/// @return True if they differ, False if they don't. -bool IRac::cmpStates(const stdAc::state_t a, const stdAc::state_t b) { - return a.protocol != b.protocol || a.model != b.model || a.power != b.power || - a.mode != b.mode || a.degrees != b.degrees || a.celsius != b.celsius || - a.fanspeed != b.fanspeed || a.swingv != b.swingv || - a.swingh != b.swingh || a.quiet != b.quiet || a.turbo != b.turbo || - a.econo != b.econo || a.light != b.light || a.filter != b.filter || - a.clean != b.clean || a.beep != b.beep || a.sleep != b.sleep; -} - -/// Check if the internal state has changed from what was previously sent. -/// @note The comparison excludes the clock. -/// @return True if it has changed, False if not. -bool IRac::hasStateChanged(void) { return cmpStates(next, _prev); } - -/// Convert the supplied str into the appropriate enum. -/// @param[in] str A Ptr to a C-style string to be converted. -/// @param[in] def The enum to return if no conversion was possible. -/// @return The equivalent enum. -stdAc::opmode_t IRac::strToOpmode(const char *str, - const stdAc::opmode_t def) { - if (!STRCASECMP(str, kAutoStr) || - !STRCASECMP(str, kAutomaticStr)) - return stdAc::opmode_t::kAuto; - else if (!STRCASECMP(str, kOffStr) || - !STRCASECMP(str, kStopStr)) - return stdAc::opmode_t::kOff; - else if (!STRCASECMP(str, kCoolStr) || - !STRCASECMP(str, kCoolingStr)) - return stdAc::opmode_t::kCool; - else if (!STRCASECMP(str, kHeatStr) || - !STRCASECMP(str, kHeatingStr)) - return stdAc::opmode_t::kHeat; - else if (!STRCASECMP(str, kDryStr) || - !STRCASECMP(str, kDryingStr) || - !STRCASECMP(str, kDehumidifyStr)) - return stdAc::opmode_t::kDry; - else if (!STRCASECMP(str, kFanStr) || - // 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 - !STRCASECMP(str, kFanOnlyStr) || - !STRCASECMP(str, kFan_OnlyStr) || - !STRCASECMP(str, kFanOnlyWithSpaceStr) || - !STRCASECMP(str, kFanOnlyNoSpaceStr)) - return stdAc::opmode_t::kFan; - else - return def; -} - -/// Convert the supplied str into the appropriate enum. -/// @param[in] str A Ptr to a C-style string to be converted. -/// @param[in] def The enum to return if no conversion was possible. -/// @return The equivalent enum. -stdAc::fanspeed_t IRac::strToFanspeed(const char *str, - const stdAc::fanspeed_t def) { - if (!STRCASECMP(str, kAutoStr) || - !STRCASECMP(str, kAutomaticStr)) - return stdAc::fanspeed_t::kAuto; - else if (!STRCASECMP(str, kMinStr) || - !STRCASECMP(str, kMinimumStr) || - !STRCASECMP(str, kLowestStr)) - return stdAc::fanspeed_t::kMin; - else if (!STRCASECMP(str, kLowStr) || - !STRCASECMP(str, kLoStr)) - return stdAc::fanspeed_t::kLow; - else if (!STRCASECMP(str, kMedStr) || - !STRCASECMP(str, kMediumStr) || - !STRCASECMP(str, kMidStr)) - return stdAc::fanspeed_t::kMedium; - else if (!STRCASECMP(str, kHighStr) || - !STRCASECMP(str, kHiStr)) - return stdAc::fanspeed_t::kHigh; - else if (!STRCASECMP(str, kMaxStr) || - !STRCASECMP(str, kMaximumStr) || - !STRCASECMP(str, kHighestStr)) - return stdAc::fanspeed_t::kMax; - else - return def; -} - -/// Convert the supplied str into the appropriate enum. -/// @param[in] str A Ptr to a C-style string to be converted. -/// @param[in] def The enum to return if no conversion was possible. -/// @return The equivalent enum. -stdAc::swingv_t IRac::strToSwingV(const char *str, - const stdAc::swingv_t def) { - if (!STRCASECMP(str, kAutoStr) || - !STRCASECMP(str, kAutomaticStr) || - !STRCASECMP(str, kOnStr) || - !STRCASECMP(str, kSwingStr)) - return stdAc::swingv_t::kAuto; - else if (!STRCASECMP(str, kOffStr) || - !STRCASECMP(str, kStopStr)) - return stdAc::swingv_t::kOff; - else if (!STRCASECMP(str, kMinStr) || - !STRCASECMP(str, kMinimumStr) || - !STRCASECMP(str, kLowestStr) || - !STRCASECMP(str, kBottomStr) || - !STRCASECMP(str, kDownStr)) - return stdAc::swingv_t::kLowest; - else if (!STRCASECMP(str, kLowStr)) - return stdAc::swingv_t::kLow; - else if (!STRCASECMP(str, kMidStr) || - !STRCASECMP(str, kMiddleStr) || - !STRCASECMP(str, kMedStr) || - !STRCASECMP(str, kMediumStr) || - !STRCASECMP(str, kCentreStr)) - return stdAc::swingv_t::kMiddle; - else if (!STRCASECMP(str, kHighStr) || - !STRCASECMP(str, kHiStr)) - return stdAc::swingv_t::kHigh; - else if (!STRCASECMP(str, kHighestStr) || - !STRCASECMP(str, kMaxStr) || - !STRCASECMP(str, kMaximumStr) || - !STRCASECMP(str, kTopStr) || - !STRCASECMP(str, kUpStr)) - return stdAc::swingv_t::kHighest; - else - return def; -} - -/// Convert the supplied str into the appropriate enum. -/// @param[in] str A Ptr to a C-style string to be converted. -/// @param[in] def The enum to return if no conversion was possible. -/// @return The equivalent enum. -stdAc::swingh_t IRac::strToSwingH(const char *str, - const stdAc::swingh_t def) { - if (!STRCASECMP(str, kAutoStr) || - !STRCASECMP(str, kAutomaticStr) || - !STRCASECMP(str, kOnStr) || !STRCASECMP(str, kSwingStr)) - return stdAc::swingh_t::kAuto; - else if (!STRCASECMP(str, kOffStr) || - !STRCASECMP(str, kStopStr)) - return stdAc::swingh_t::kOff; - else if (!STRCASECMP(str, kLeftMaxNoSpaceStr) || // "LeftMax" - !STRCASECMP(str, kLeftMaxStr) || // "Left Max" - !STRCASECMP(str, kMaxLeftNoSpaceStr) || // "MaxLeft" - !STRCASECMP(str, kMaxLeftStr)) // "Max Left" - return stdAc::swingh_t::kLeftMax; - else if (!STRCASECMP(str, kLeftStr)) - return stdAc::swingh_t::kLeft; - else if (!STRCASECMP(str, kMidStr) || - !STRCASECMP(str, kMiddleStr) || - !STRCASECMP(str, kMedStr) || - !STRCASECMP(str, kMediumStr) || - !STRCASECMP(str, kCentreStr)) - return stdAc::swingh_t::kMiddle; - else if (!STRCASECMP(str, kRightStr)) - return stdAc::swingh_t::kRight; - else if (!STRCASECMP(str, kRightMaxNoSpaceStr) || // "RightMax" - !STRCASECMP(str, kRightMaxStr) || // "Right Max" - !STRCASECMP(str, kMaxRightNoSpaceStr) || // "MaxRight" - !STRCASECMP(str, kMaxRightStr)) // "Max Right" - return stdAc::swingh_t::kRightMax; - else if (!STRCASECMP(str, kWideStr)) - return stdAc::swingh_t::kWide; - else - return def; -} - -/// Convert the supplied str into the appropriate enum. -/// @note Assumes str is the model code or an integer >= 1. -/// @param[in] str A Ptr to a C-style string to be converted. -/// @param[in] def The enum to return if no conversion was possible. -/// @return The equivalent enum. -int16_t IRac::strToModel(const char *str, const int16_t def) { - // Gree - if (!STRCASECMP(str, kYaw1fStr)) { - return gree_ac_remote_model_t::YAW1F; - } else if (!STRCASECMP(str, kYbofbStr)) { - return gree_ac_remote_model_t::YBOFB; - // HitachiAc1 models - } else if (!STRCASECMP(str, kRlt0541htaaStr)) { - return hitachi_ac1_remote_model_t::R_LT0541_HTA_A; - } else if (!STRCASECMP(str, kRlt0541htabStr)) { - return hitachi_ac1_remote_model_t::R_LT0541_HTA_B; - // Fujitsu A/C models - } else if (!STRCASECMP(str, kArrah2eStr)) { - return fujitsu_ac_remote_model_t::ARRAH2E; - } else if (!STRCASECMP(str, kArdb1Str)) { - return fujitsu_ac_remote_model_t::ARDB1; - } else if (!STRCASECMP(str, kArreb1eStr)) { - return fujitsu_ac_remote_model_t::ARREB1E; - } else if (!STRCASECMP(str, kArjw2Str)) { - return fujitsu_ac_remote_model_t::ARJW2; - } else if (!STRCASECMP(str, kArry4Str)) { - return fujitsu_ac_remote_model_t::ARRY4; - } else if (!STRCASECMP(str, kArrew4eStr)) { - return fujitsu_ac_remote_model_t::ARREW4E; - // LG A/C models - } else if (!STRCASECMP(str, kGe6711ar2853mStr)) { - return lg_ac_remote_model_t::GE6711AR2853M; - } else if (!STRCASECMP(str, kAkb75215403Str)) { - return lg_ac_remote_model_t::AKB75215403; - } else if (!STRCASECMP(str, kAkb74955603Str)) { - return lg_ac_remote_model_t::AKB74955603; - } else if (!STRCASECMP(str, kAkb73757604Str)) { - return lg_ac_remote_model_t::AKB73757604; - // Panasonic A/C families - } else if (!STRCASECMP(str, kLkeStr) || - !STRCASECMP(str, kPanasonicLkeStr)) { - return panasonic_ac_remote_model_t::kPanasonicLke; - } else if (!STRCASECMP(str, kNkeStr) || - !STRCASECMP(str, kPanasonicNkeStr)) { - return panasonic_ac_remote_model_t::kPanasonicNke; - } else if (!STRCASECMP(str, kDkeStr) || - !STRCASECMP(str, kPanasonicDkeStr) || - !STRCASECMP(str, kPkrStr) || - !STRCASECMP(str, kPanasonicPkrStr)) { - return panasonic_ac_remote_model_t::kPanasonicDke; - } else if (!STRCASECMP(str, kJkeStr) || - !STRCASECMP(str, kPanasonicJkeStr)) { - return panasonic_ac_remote_model_t::kPanasonicJke; - } else if (!STRCASECMP(str, kCkpStr) || - !STRCASECMP(str, kPanasonicCkpStr)) { - return panasonic_ac_remote_model_t::kPanasonicCkp; - } else if (!STRCASECMP(str, kRkrStr) || - !STRCASECMP(str, kPanasonicRkrStr)) { - return panasonic_ac_remote_model_t::kPanasonicRkr; - // Sharp A/C Models - } else if (!STRCASECMP(str, kA907Str)) { - return sharp_ac_remote_model_t::A907; - } else if (!STRCASECMP(str, kA705Str)) { - return sharp_ac_remote_model_t::A705; - } else if (!STRCASECMP(str, kA903Str)) { - return sharp_ac_remote_model_t::A903; - // TCL A/C Models - } else if (!STRCASECMP(str, kTac09chsdStr)) { - return tcl_ac_remote_model_t::TAC09CHSD; - } else if (!STRCASECMP(str, kGz055be1Str)) { - return tcl_ac_remote_model_t::GZ055BE1; - // Voltas A/C models - } else if (!STRCASECMP(str, k122lzfStr)) { - return voltas_ac_remote_model_t::kVoltas122LZF; - // Whirlpool A/C models - } else if (!STRCASECMP(str, kDg11j13aStr) || - !STRCASECMP(str, kDg11j104Str)) { - return whirlpool_ac_remote_model_t::DG11J13A; - } else if (!STRCASECMP(str, kDg11j191Str)) { - return whirlpool_ac_remote_model_t::DG11J191; - } else { - int16_t number = atoi(str); - if (number > 0) - return number; - else - return def; - } -} - -/// Convert the supplied str into the appropriate boolean value. -/// @param[in] str A Ptr to a C-style string to be converted. -/// @param[in] def The boolean value to return if no conversion was possible. -/// @return The equivalent boolean value. -bool IRac::strToBool(const char *str, const bool def) { - if (!STRCASECMP(str, kOnStr) || - !STRCASECMP(str, k1Str) || - !STRCASECMP(str, kYesStr) || - !STRCASECMP(str, kTrueStr)) - return true; - else if (!STRCASECMP(str, kOffStr) || - !STRCASECMP(str, k0Str) || - !STRCASECMP(str, kNoStr) || - !STRCASECMP(str, kFalseStr)) - return false; - else - return def; -} - -/// Convert the supplied boolean into the appropriate String. -/// @param[in] value The boolean value to be converted. -/// @return The equivalent String for the locale. -String IRac::boolToString(const bool value) { - return value ? kOnStr : kOffStr; -} - -/// Convert the supplied operation mode into the appropriate String. -/// @param[in] mode The enum to be converted. -/// @param[in] ha A flag to indicate we want GoogleHome/HomeAssistant output. -/// @return The equivalent String for the locale. -String IRac::opmodeToString(const stdAc::opmode_t mode, const bool ha) { - switch (mode) { - case stdAc::opmode_t::kOff: return kOffStr; - case stdAc::opmode_t::kAuto: return kAutoStr; - case stdAc::opmode_t::kCool: return kCoolStr; - case stdAc::opmode_t::kHeat: return kHeatStr; - case stdAc::opmode_t::kDry: return kDryStr; - case stdAc::opmode_t::kFan: return ha ? kFanOnlyStr : kFanStr; - default: return kUnknownStr; - } -} - -/// Convert the supplied fan speed enum into the appropriate String. -/// @param[in] speed The enum to be converted. -/// @return The equivalent String for the locale. -String IRac::fanspeedToString(const stdAc::fanspeed_t speed) { - switch (speed) { - case stdAc::fanspeed_t::kAuto: return kAutoStr; - case stdAc::fanspeed_t::kMax: return kMaxStr; - case stdAc::fanspeed_t::kHigh: return kHighStr; - case stdAc::fanspeed_t::kMedium: return kMediumStr; - case stdAc::fanspeed_t::kLow: return kLowStr; - case stdAc::fanspeed_t::kMin: return kMinStr; - default: return kUnknownStr; - } -} - -/// Convert the supplied enum into the appropriate String. -/// @param[in] swingv The enum to be converted. -/// @return The equivalent String for the locale. -String IRac::swingvToString(const stdAc::swingv_t swingv) { - switch (swingv) { - case stdAc::swingv_t::kOff: return kOffStr; - case stdAc::swingv_t::kAuto: return kAutoStr; - case stdAc::swingv_t::kHighest: return kHighestStr; - case stdAc::swingv_t::kHigh: return kHighStr; - case stdAc::swingv_t::kMiddle: return kMiddleStr; - case stdAc::swingv_t::kLow: return kLowStr; - case stdAc::swingv_t::kLowest: return kLowestStr; - default: return kUnknownStr; - } -} - -/// Convert the supplied enum into the appropriate String. -/// @param[in] swingh The enum to be converted. -/// @return The equivalent String for the locale. -String IRac::swinghToString(const stdAc::swingh_t swingh) { - switch (swingh) { - case stdAc::swingh_t::kOff: return kOffStr; - case stdAc::swingh_t::kAuto: return kAutoStr; - case stdAc::swingh_t::kLeftMax: return kLeftMaxStr; - case stdAc::swingh_t::kLeft: return kLeftStr; - case stdAc::swingh_t::kMiddle: return kMiddleStr; - case stdAc::swingh_t::kRight: return kRightStr; - case stdAc::swingh_t::kRightMax: return kRightMaxStr; - case stdAc::swingh_t::kWide: return kWideStr; - default: return kUnknownStr; - } -} - -namespace IRAcUtils { - /// Display the human readable state of an A/C message if we can. - /// @param[in] result A Ptr to the captured `decode_results` that contains an - /// A/C mesg. - /// @return A string with the human description of the A/C message. - /// An empty string if we can't. - String resultAcToString(const decode_results * const result) { - switch (result->decode_type) { -#if DECODE_AIRWELL - case decode_type_t::AIRWELL: { - IRAirwellAc ac(kGpioUnused); - ac.setRaw(result->value); // AIRWELL uses value instead of state. - return ac.toString(); - } -#endif // DECODE_AIRWELL -#if DECODE_AMCOR - case decode_type_t::AMCOR: { - IRAmcorAc ac(kGpioUnused); - ac.setRaw(result->state); - return ac.toString(); - } -#endif // DECODE_AMCOR -#if DECODE_ARGO - case decode_type_t::ARGO: { - IRArgoAC ac(kGpioUnused); - ac.setRaw(result->state); - return ac.toString(); - } -#endif // DECODE_ARGO -#if DECODE_CARRIER_AC64 - case decode_type_t::CARRIER_AC64: { - IRCarrierAc64 ac(kGpioUnused); - ac.setRaw(result->value); // CARRIER_AC64 uses value instead of state. - return ac.toString(); - } -#endif // DECODE_CARRIER_AC64 -#if DECODE_DAIKIN - case decode_type_t::DAIKIN: { - IRDaikinESP ac(kGpioUnused); - ac.setRaw(result->state); - return ac.toString(); - } -#endif // DECODE_DAIKIN -#if DECODE_DAIKIN128 - case decode_type_t::DAIKIN128: { - IRDaikin128 ac(kGpioUnused); - ac.setRaw(result->state); - return ac.toString(); - } -#endif // DECODE_DAIKIN128 -#if DECODE_DAIKIN152 - case decode_type_t::DAIKIN152: { - IRDaikin152 ac(kGpioUnused); - ac.setRaw(result->state); - return ac.toString(); - } -#endif // DECODE_DAIKIN152 -#if DECODE_DAIKIN160 - case decode_type_t::DAIKIN160: { - IRDaikin160 ac(kGpioUnused); - ac.setRaw(result->state); - return ac.toString(); - } -#endif // DECODE_DAIKIN160 -#if DECODE_DAIKIN176 - case decode_type_t::DAIKIN176: { - IRDaikin176 ac(kGpioUnused); - ac.setRaw(result->state); - return ac.toString(); - } -#endif // DECODE_DAIKIN160 -#if DECODE_DAIKIN2 - case decode_type_t::DAIKIN2: { - IRDaikin2 ac(kGpioUnused); - ac.setRaw(result->state); - return ac.toString(); - } -#endif // DECODE_DAIKIN2 -#if DECODE_DAIKIN216 - case decode_type_t::DAIKIN216: { - IRDaikin216 ac(kGpioUnused); - ac.setRaw(result->state); - return ac.toString(); - } -#endif // DECODE_DAIKIN216 -#if DECODE_DAIKIN64 - case decode_type_t::DAIKIN64: { - IRDaikin64 ac(kGpioUnused); - ac.setRaw(result->value); // Daikin64 uses value instead of state. - return ac.toString(); - } -#endif // DECODE_DAIKIN64 -#if DECODE_DELONGHI_AC - case decode_type_t::DELONGHI_AC: { - IRDelonghiAc ac(kGpioUnused); - ac.setRaw(result->value); // DelonghiAc uses value instead of state. - return ac.toString(); - } -#endif // DECODE_DELONGHI_AC -#if DECODE_ECOCLIM - case decode_type_t::ECOCLIM: { - if (result->bits == kEcoclimBits) { - IREcoclimAc ac(kGpioUnused); - ac.setRaw(result->value); // EcoClim uses value instead of state. - return ac.toString(); - } - return ""; - } -#endif // DECODE_ECOCLIM -#if DECODE_ELECTRA_AC - case decode_type_t::ELECTRA_AC: { - IRElectraAc ac(kGpioUnused); - ac.setRaw(result->state); - return ac.toString(); - } -#endif // DECODE_ELECTRA_AC -#if DECODE_FUJITSU_AC - case decode_type_t::FUJITSU_AC: { - IRFujitsuAC ac(kGpioUnused); - ac.setRaw(result->state, result->bits / 8); - return ac.toString(); - } -#endif // DECODE_FUJITSU_AC -#if DECODE_KELON - case decode_type_t::KELON: { - IRKelonAc ac(kGpioUnused); - ac.setRaw(result->value); - return ac.toString(); - } -#endif // DECODE_KELON -#if DECODE_KELVINATOR - case decode_type_t::KELVINATOR: { - IRKelvinatorAC ac(kGpioUnused); - ac.setRaw(result->state); - return ac.toString(); - } -#endif // DECODE_KELVINATOR -#if DECODE_MITSUBISHI_AC - case decode_type_t::MITSUBISHI_AC: { - IRMitsubishiAC ac(kGpioUnused); - ac.setRaw(result->state); - return ac.toString(); - } -#endif // DECODE_MITSUBISHI_AC -#if DECODE_MITSUBISHI112 - case decode_type_t::MITSUBISHI112: { - IRMitsubishi112 ac(kGpioUnused); - ac.setRaw(result->state); - return ac.toString(); - } -#endif // DECODE_MITSUBISHI112 -#if DECODE_MITSUBISHI136 - case decode_type_t::MITSUBISHI136: { - IRMitsubishi136 ac(kGpioUnused); - ac.setRaw(result->state); - return ac.toString(); - } -#endif // DECODE_MITSUBISHI136 -#if DECODE_MITSUBISHIHEAVY - case decode_type_t::MITSUBISHI_HEAVY_88: { - IRMitsubishiHeavy88Ac ac(kGpioUnused); - ac.setRaw(result->state); - return ac.toString(); - } - case decode_type_t::MITSUBISHI_HEAVY_152: { - IRMitsubishiHeavy152Ac ac(kGpioUnused); - ac.setRaw(result->state); - return ac.toString(); - } -#endif // DECODE_MITSUBISHIHEAVY -#if DECODE_NEOCLIMA - case decode_type_t::NEOCLIMA: { - IRNeoclimaAc ac(kGpioUnused); - ac.setRaw(result->state); - return ac.toString(); - } -#endif // DECODE_NEOCLIMA -#if DECODE_TOSHIBA_AC - case decode_type_t::TOSHIBA_AC: { - IRToshibaAC ac(kGpioUnused); - ac.setRaw(result->state, result->bits / 8); - return ac.toString(); - } -#endif // DECODE_TOSHIBA_AC -#if DECODE_TROTEC - case decode_type_t::TROTEC: { - IRTrotecESP ac(kGpioUnused); - ac.setRaw(result->state); - return ac.toString(); - } -#endif // DECODE_TROTEC -#if DECODE_TROTEC_3550 - case decode_type_t::TROTEC_3550: { - IRTrotec3550 ac(kGpioUnused); - ac.setRaw(result->state); - return ac.toString(); - } -#endif // DECODE_TROTEC_3550 -#if DECODE_TRUMA - case decode_type_t::TRUMA: { - IRTrumaAc ac(kGpioUnused); - ac.setRaw(result->value); // Truma uses value instead of state. - return ac.toString(); - } -#endif // DECODE_TRUMA -#if DECODE_GOODWEATHER - case decode_type_t::GOODWEATHER: { - IRGoodweatherAc ac(kGpioUnused); - ac.setRaw(result->value); // Goodweather uses value instead of state. - return ac.toString(); - } -#endif // DECODE_GOODWEATHER -#if DECODE_GREE - case decode_type_t::GREE: { - IRGreeAC ac(kGpioUnused); - ac.setRaw(result->state); - return ac.toString(); - } -#endif // DECODE_GREE -#if DECODE_MIDEA - case decode_type_t::MIDEA: { - IRMideaAC ac(kGpioUnused); - ac.setRaw(result->value); // Midea uses value instead of state. - return ac.toString(); - } -#endif // DECODE_MIDEA -#if DECODE_HAIER_AC - case decode_type_t::HAIER_AC: { - IRHaierAC ac(kGpioUnused); - ac.setRaw(result->state); - return ac.toString(); - } -#endif // DECODE_HAIER_AC -#if DECODE_HAIER_AC176 - case decode_type_t::HAIER_AC176: { - IRHaierAC176 ac(kGpioUnused); - ac.setRaw(result->state); - return ac.toString(); - } -#endif // DECODE_HAIER_AC176 -#if DECODE_HAIER_AC_YRW02 - case decode_type_t::HAIER_AC_YRW02: { - IRHaierACYRW02 ac(kGpioUnused); - ac.setRaw(result->state); - return ac.toString(); - } -#endif // DECODE_HAIER_AC_YRW02 -#if DECODE_SAMSUNG_AC - case decode_type_t::SAMSUNG_AC: { - IRSamsungAc ac(kGpioUnused); - ac.setRaw(result->state, result->bits / 8); - return ac.toString(); - } -#endif // DECODE_SAMSUNG_AC -#if DECODE_SANYO_AC - case decode_type_t::SANYO_AC: { - IRSanyoAc ac(kGpioUnused); - ac.setRaw(result->state); - return ac.toString(); - } -#endif // DECODE_SANYO_AC -#if DECODE_SANYO_AC88 - case decode_type_t::SANYO_AC88: { - IRSanyoAc88 ac(kGpioUnused); - ac.setRaw(result->state); - return ac.toString(); - } -#endif // DECODE_SANYO_AC88 -#if DECODE_SHARP_AC - case decode_type_t::SHARP_AC: { - IRSharpAc ac(kGpioUnused); - ac.setRaw(result->state); - return ac.toString(); - } -#endif // DECODE_SHARP_AC -#if DECODE_COOLIX - case decode_type_t::COOLIX: { - IRCoolixAC ac(kGpioUnused); - ac.on(); - ac.setRaw(result->value); // Coolix uses value instead of state. - return ac.toString(); - } -#endif // DECODE_COOLIX -#if DECODE_CORONA_AC - case decode_type_t::CORONA_AC: { - IRCoronaAc ac(kGpioUnused); - ac.setRaw(result->state, result->bits / 8); - return ac.toString(); - } -#endif // DECODE_CORONA_AC -#if DECODE_PANASONIC_AC - case decode_type_t::PANASONIC_AC: { - if (result->bits > kPanasonicAcShortBits) { - IRPanasonicAc ac(kGpioUnused); - ac.setRaw(result->state); - return ac.toString(); - } - return ""; - } -#endif // DECODE_PANASONIC_AC -#if DECODE_PANASONIC_AC32 - case decode_type_t::PANASONIC_AC32: { - if (result->bits >= kPanasonicAc32Bits) { - IRPanasonicAc32 ac(kGpioUnused); - ac.setRaw(result->value); // Uses value instead of state. - return ac.toString(); - } - return ""; - } -#endif // DECODE_PANASONIC_AC -#if DECODE_HITACHI_AC - case decode_type_t::HITACHI_AC: { - IRHitachiAc ac(kGpioUnused); - ac.setRaw(result->state); - return ac.toString(); - } -#endif // DECODE_HITACHI_AC -#if DECODE_HITACHI_AC1 - case decode_type_t::HITACHI_AC1: { - IRHitachiAc1 ac(kGpioUnused); - ac.setRaw(result->state); - return ac.toString(); - } -#endif // DECODE_HITACHI_AC1 -#if DECODE_HITACHI_AC344 - case decode_type_t::HITACHI_AC344: { - IRHitachiAc344 ac(kGpioUnused); - ac.setRaw(result->state); - return ac.toString(); - } -#endif // DECODE_HITACHI_AC344 -#if DECODE_HITACHI_AC424 - case decode_type_t::HITACHI_AC424: { - IRHitachiAc424 ac(kGpioUnused); - ac.setRaw(result->state); - return ac.toString(); - } -#endif // DECODE_HITACHI_AC424 -#if DECODE_WHIRLPOOL_AC - case decode_type_t::WHIRLPOOL_AC: { - IRWhirlpoolAc ac(kGpioUnused); - ac.setRaw(result->state); - return ac.toString(); - } -#endif // DECODE_WHIRLPOOL_AC -#if DECODE_VESTEL_AC - case decode_type_t::VESTEL_AC: { - IRVestelAc ac(kGpioUnused); - ac.setRaw(result->value); // Like Coolix, use value instead of state. - return ac.toString(); - } -#endif // DECODE_VESTEL_AC -#if DECODE_TECHNIBEL_AC - case decode_type_t::TECHNIBEL_AC: { - IRTechnibelAc ac(kGpioUnused); - ac.setRaw(result->value); // TechnibelAc uses value instead of state. - return ac.toString(); - } -#endif // DECODE_TECHNIBEL_AC -#if DECODE_VOLTAS - case decode_type_t::VOLTAS: { - IRVoltas ac(kGpioUnused); - ac.setRaw(result->state); - return ac.toString(); - } -#endif // DECODE_VOLTAS -#if DECODE_TECO - case decode_type_t::TECO: { - IRTecoAc ac(kGpioUnused); - ac.setRaw(result->value); // Like Coolix, use value instead of state. - return ac.toString(); - } -#endif // DECODE_TECO -#if (DECODE_TCL112AC || DECODE_TEKNOPOINT) - case decode_type_t::TCL112AC: - case decode_type_t::TEKNOPOINT: { - IRTcl112Ac ac(kGpioUnused); - ac.setRaw(result->state); - return ac.toString(); - } -#endif // (DECODE_TCL112AC || DECODE_TEKNOPOINT) -#if DECODE_LG - case decode_type_t::LG: - case decode_type_t::LG2: { - IRLgAc ac(kGpioUnused); - ac.setRaw(result->value, result->decode_type); // Use value, not state. - return ac.isValidLgAc() ? ac.toString() : ""; - } -#endif // DECODE_LG -#if DECODE_TRANSCOLD - case decode_type_t::TRANSCOLD: { - IRTranscoldAc ac(kGpioUnused); - ac.on(); - ac.setRaw(result->value); // TRANSCOLD uses value instead of state. - return ac.toString(); - } -#endif // DECODE_TRANSCOLD -#if DECODE_RHOSS - case decode_type_t::RHOSS: { - IRRhossAc ac(kGpioUnused); - ac.setRaw(result->state); - return ac.toString(); - } -#endif // DECODE_RHOSS - default: - return ""; - } - } - - /// Convert a valid IR A/C remote message that we understand enough into a - /// Common A/C state. - /// @param[in] decode A PTR to a successful raw IR decode object. - /// @param[in] result A PTR to a state structure to store the result in. - /// @param[in] prev A PTR to a state structure which has the prev. state. - /// @return A boolean indicating success or failure. - bool decodeToState(const decode_results *decode, stdAc::state_t *result, - const stdAc::state_t *prev -/// @cond IGNORE -// *prev flagged as "unused" due to potential compiler warning when some -// protocols that use it are disabled. It really is used. - __attribute__((unused)) -/// @endcond - ) { - if (decode == NULL || result == NULL) return false; // Safety check. - switch (decode->decode_type) { -#if DECODE_AIRWELL - case decode_type_t::AIRWELL: { - IRAirwellAc ac(kGpioUnused); - ac.setRaw(decode->value); // Uses value instead of state. - *result = ac.toCommon(prev); - break; - } -#endif // DECODE_AIRWELL -#if DECODE_AMCOR - case decode_type_t::AMCOR: { - IRAmcorAc ac(kGpioUnused); - ac.setRaw(decode->state); - *result = ac.toCommon(); - break; - } -#endif // DECODE_AMCOR -#if DECODE_ARGO - case decode_type_t::ARGO: { - IRArgoAC ac(kGpioUnused); - ac.setRaw(decode->state); - *result = ac.toCommon(); - break; - } -#endif // DECODE_ARGO -#if DECODE_COOLIX - case decode_type_t::COOLIX: { - IRCoolixAC ac(kGpioUnused); - ac.setRaw(decode->value); // Uses value instead of state. - *result = ac.toCommon(prev); - break; - } -#endif // DECODE_COOLIX -#if DECODE_CORONA_AC - case decode_type_t::CORONA_AC: { - IRCoronaAc ac(kGpioUnused); - ac.setRaw(decode->state, decode->bits / 8); - *result = ac.toCommon(); - break; - } -#endif // DECODE_CARRIER_AC64 -#if DECODE_CARRIER_AC64 - case decode_type_t::CARRIER_AC64: { - IRCarrierAc64 ac(kGpioUnused); - ac.setRaw(decode->value); // Uses value instead of state. - *result = ac.toCommon(); - break; - } -#endif // DECODE_CARRIER_AC64 -#if DECODE_DAIKIN - case decode_type_t::DAIKIN: { - IRDaikinESP ac(kGpioUnused); - ac.setRaw(decode->state); - *result = ac.toCommon(); - break; - } -#endif // DECODE_DAIKIN -#if DECODE_DAIKIN128 - case decode_type_t::DAIKIN128: { - IRDaikin128 ac(kGpioUnused); - ac.setRaw(decode->state); - *result = ac.toCommon(); - break; - } -#endif // DECODE_DAIKIN128 -#if DECODE_DAIKIN152 - case decode_type_t::DAIKIN152: { - IRDaikin152 ac(kGpioUnused); - ac.setRaw(decode->state); - *result = ac.toCommon(); - break; - } -#endif // DECODE_DAIKIN152 -#if DECODE_DAIKIN160 - case decode_type_t::DAIKIN160: { - IRDaikin160 ac(kGpioUnused); - ac.setRaw(decode->state); - *result = ac.toCommon(); - break; - } -#endif // DECODE_DAIKIN160 -#if DECODE_DAIKIN176 - case decode_type_t::DAIKIN176: { - IRDaikin176 ac(kGpioUnused); - ac.setRaw(decode->state); - *result = ac.toCommon(); - break; - } -#endif // DECODE_DAIKIN160 -#if DECODE_DAIKIN2 - case decode_type_t::DAIKIN2: { - IRDaikin2 ac(kGpioUnused); - ac.setRaw(decode->state); - *result = ac.toCommon(); - break; - } -#endif // DECODE_DAIKIN2 -#if DECODE_DAIKIN216 - case decode_type_t::DAIKIN216: { - IRDaikin216 ac(kGpioUnused); - ac.setRaw(decode->state); - *result = ac.toCommon(); - break; - } -#endif // DECODE_DAIKIN216 -#if DECODE_DAIKIN64 - case decode_type_t::DAIKIN64: { - IRDaikin64 ac(kGpioUnused); - ac.setRaw(decode->value); // Uses value instead of state. - *result = ac.toCommon(prev); - break; - } -#endif // DECODE_DAIKIN64 -#if DECODE_DELONGHI_AC - case decode_type_t::DELONGHI_AC: { - IRDelonghiAc ac(kGpioUnused); - ac.setRaw(decode->value); // Uses value instead of state. - *result = ac.toCommon(); - break; - } -#endif // DECODE_DELONGHI_AC -#if DECODE_ECOCLIM - case decode_type_t::ECOCLIM: { - if (decode->bits == kEcoclimBits) { - IREcoclimAc ac(kGpioUnused); - ac.setRaw(decode->value); // Uses value instead of state. - *result = ac.toCommon(); - } else { - return false; - } - break; - } -#endif // DECODE_ECOCLIM -#if DECODE_ELECTRA_AC - case decode_type_t::ELECTRA_AC: { - IRElectraAc ac(kGpioUnused); - ac.setRaw(decode->state); - *result = ac.toCommon(); - break; - } -#endif // DECODE_ELECTRA_AC -#if DECODE_FUJITSU_AC - case decode_type_t::FUJITSU_AC: { - IRFujitsuAC ac(kGpioUnused); - ac.setRaw(decode->state, decode->bits / 8); - *result = ac.toCommon(); - break; - } -#endif // DECODE_FUJITSU_AC -#if DECODE_GOODWEATHER - case decode_type_t::GOODWEATHER: { - IRGoodweatherAc ac(kGpioUnused); - ac.setRaw(decode->value); // Uses value instead of state. - *result = ac.toCommon(); - break; - } -#endif // DECODE_GOODWEATHER -#if DECODE_GREE - case decode_type_t::GREE: { - IRGreeAC ac(kGpioUnused); - ac.setRaw(decode->state); - *result = ac.toCommon(); - break; - } -#endif // DECODE_GREE -#if DECODE_HAIER_AC - case decode_type_t::HAIER_AC: { - IRHaierAC ac(kGpioUnused); - ac.setRaw(decode->state); - *result = ac.toCommon(); - break; - } -#endif // DECODE_HAIER_AC -#if DECODE_HAIER_AC176 - case decode_type_t::HAIER_AC176: { - IRHaierAC176 ac(kGpioUnused); - ac.setRaw(decode->state); - *result = ac.toCommon(); - break; - } -#endif // DECODE_HAIER_AC176 -#if DECODE_HAIER_AC_YRW02 - case decode_type_t::HAIER_AC_YRW02: { - IRHaierACYRW02 ac(kGpioUnused); - ac.setRaw(decode->state); - *result = ac.toCommon(); - break; - } -#endif // DECODE_HAIER_AC_YRW02 -#if (DECODE_HITACHI_AC || DECODE_HITACHI_AC2) - case decode_type_t::HITACHI_AC: { - IRHitachiAc ac(kGpioUnused); - ac.setRaw(decode->state); - *result = ac.toCommon(); - break; - } -#endif // (DECODE_HITACHI_AC || DECODE_HITACHI_AC2) -#if DECODE_HITACHI_AC1 - case decode_type_t::HITACHI_AC1: { - IRHitachiAc1 ac(kGpioUnused); - ac.setRaw(decode->state); - *result = ac.toCommon(); - break; - } -#endif // DECODE_HITACHI_AC1 -#if DECODE_HITACHI_AC344 - case decode_type_t::HITACHI_AC344: { - IRHitachiAc344 ac(kGpioUnused); - ac.setRaw(decode->state); - *result = ac.toCommon(); - break; - } -#endif // DECODE_HITACHI_AC344 -#if DECODE_HITACHI_AC424 - case decode_type_t::HITACHI_AC424: { - IRHitachiAc424 ac(kGpioUnused); - ac.setRaw(decode->state); - *result = ac.toCommon(); - break; - } -#endif // DECODE_HITACHI_AC424 -#if DECODE_KELON - case decode_type_t::KELON: { - IRKelonAc ac(kGpioUnused); - ac.setRaw(decode->value); - *result = ac.toCommon(); - break; - } -#endif // DECODE_KELON -#if DECODE_KELVINATOR - case decode_type_t::KELVINATOR: { - IRKelvinatorAC ac(kGpioUnused); - ac.setRaw(decode->state); - *result = ac.toCommon(); - break; - } -#endif // DECODE_KELVINATOR -#if DECODE_LG - case decode_type_t::LG: - case decode_type_t::LG2: { - IRLgAc ac(kGpioUnused); - ac.setRaw(decode->value, decode->decode_type); // Use value, not state. - if (!ac.isValidLgAc()) return false; - *result = ac.toCommon(prev); - break; - } -#endif // DECODE_LG -#if DECODE_MIDEA - case decode_type_t::MIDEA: { - IRMideaAC ac(kGpioUnused); - ac.setRaw(decode->value); // Uses value instead of state. - *result = ac.toCommon(prev); - break; - } -#endif // DECODE_MIDEA -#if DECODE_MITSUBISHI_AC - case decode_type_t::MITSUBISHI_AC: { - IRMitsubishiAC ac(kGpioUnused); - ac.setRaw(decode->state); - *result = ac.toCommon(); - break; - } -#endif // DECODE_MITSUBISHI_AC -#if DECODE_MITSUBISHI112 - case decode_type_t::MITSUBISHI112: { - IRMitsubishi112 ac(kGpioUnused); - ac.setRaw(decode->state); - *result = ac.toCommon(); - break; - } -#endif // DECODE_MITSUBISHI112 -#if DECODE_MITSUBISHI136 - case decode_type_t::MITSUBISHI136: { - IRMitsubishi136 ac(kGpioUnused); - ac.setRaw(decode->state); - *result = ac.toCommon(); - break; - } -#endif // DECODE_MITSUBISHI136 -#if DECODE_MITSUBISHIHEAVY - case decode_type_t::MITSUBISHI_HEAVY_88: { - IRMitsubishiHeavy88Ac ac(kGpioUnused); - ac.setRaw(decode->state); - *result = ac.toCommon(); - break; - } - case decode_type_t::MITSUBISHI_HEAVY_152: { - IRMitsubishiHeavy152Ac ac(kGpioUnused); - ac.setRaw(decode->state); - *result = ac.toCommon(); - break; - } -#endif // DECODE_MITSUBISHIHEAVY -#if DECODE_NEOCLIMA - case decode_type_t::NEOCLIMA: { - IRNeoclimaAc ac(kGpioUnused); - ac.setRaw(decode->state); - *result = ac.toCommon(); - break; - } -#endif // DECODE_NEOCLIMA -#if DECODE_PANASONIC_AC - case decode_type_t::PANASONIC_AC: { - IRPanasonicAc ac(kGpioUnused); - ac.setRaw(decode->state); - *result = ac.toCommon(); - break; - } -#endif // DECODE_PANASONIC_AC -#if DECODE_PANASONIC_AC32 - case decode_type_t::PANASONIC_AC32: { - IRPanasonicAc32 ac(kGpioUnused); - if (decode->bits >= kPanasonicAc32Bits) { - ac.setRaw(decode->value); // Uses value instead of state. - *result = ac.toCommon(prev); - } else { - return false; - } - break; - } -#endif // DECODE_PANASONIC_AC32 -#if DECODE_RHOSS - case decode_type_t::RHOSS: { - IRRhossAc ac(kGpioUnused); - ac.setRaw(decode->state); - *result = ac.toCommon(); - break; - } -#endif // DECODE_RHOSS -#if DECODE_SAMSUNG_AC - case decode_type_t::SAMSUNG_AC: { - IRSamsungAc ac(kGpioUnused); - ac.setRaw(decode->state, decode->bits / 8); - *result = ac.toCommon(); - break; - } -#endif // DECODE_SAMSUNG_AC -#if DECODE_SANYO_AC - case decode_type_t::SANYO_AC: { - IRSanyoAc ac(kGpioUnused); - ac.setRaw(decode->state); - *result = ac.toCommon(); - break; - } -#endif // DECODE_SANYO_AC -#if DECODE_SANYO_AC88 - case decode_type_t::SANYO_AC88: { - IRSanyoAc88 ac(kGpioUnused); - ac.setRaw(decode->state); - *result = ac.toCommon(); - break; - } -#endif // DECODE_SANYO_AC88 -#if DECODE_SHARP_AC - case decode_type_t::SHARP_AC: { - IRSharpAc ac(kGpioUnused); - ac.setRaw(decode->state); - *result = ac.toCommon(prev); - break; - } -#endif // DECODE_SHARP_AC -#if (DECODE_TCL112AC || DECODE_TEKNOPOINT) - case decode_type_t::TCL112AC: - case decode_type_t::TEKNOPOINT: { - IRTcl112Ac ac(kGpioUnused); - ac.setRaw(decode->state); - *result = ac.toCommon(prev); - // Teknopoint uses the TCL protocol, but with a different model number. - // Just keep the original protocol type ... for now. - result->protocol = decode->decode_type; - break; - } -#endif // (DECODE_TCL112AC || DECODE_TEKNOPOINT) -#if DECODE_TECHNIBEL_AC - case decode_type_t::TECHNIBEL_AC: { - IRTechnibelAc ac(kGpioUnused); - ac.setRaw(decode->value); // Uses value instead of state. - *result = ac.toCommon(); - break; - } -#endif // DECODE_TECHNIBEL_AC -#if DECODE_TECO - case decode_type_t::TECO: { - IRTecoAc ac(kGpioUnused); - ac.setRaw(decode->value); // Uses value instead of state. - *result = ac.toCommon(); - break; - } -#endif // DECODE_TECO -#if DECODE_TOSHIBA_AC - case decode_type_t::TOSHIBA_AC: { - IRToshibaAC ac(kGpioUnused); - ac.setRaw(decode->state, decode->bits / 8); - *result = ac.toCommon(prev); - break; - } -#endif // DECODE_TOSHIBA_AC -#if DECODE_TROTEC - case decode_type_t::TROTEC: { - IRTrotecESP ac(kGpioUnused); - ac.setRaw(decode->state); - *result = ac.toCommon(); - break; - } -#endif // DECODE_TROTEC -#if DECODE_TROTEC_3550 - case decode_type_t::TROTEC_3550: { - IRTrotec3550 ac(kGpioUnused); - ac.setRaw(decode->state); - *result = ac.toCommon(); - break; - } -#endif // DECODE_TROTEC_3550 -#if DECODE_TRUMA - case decode_type_t::TRUMA: { - IRTrumaAc ac(kGpioUnused); - ac.setRaw(decode->value); // Uses value instead of state. - *result = ac.toCommon(); - break; - } -#endif // DECODE_TRUMA -#if DECODE_VESTEL_AC - case decode_type_t::VESTEL_AC: { - IRVestelAc ac(kGpioUnused); - ac.setRaw(decode->value); // Uses value instead of state. - *result = ac.toCommon(); - break; - } -#endif // DECODE_VESTEL_AC -#if DECODE_VOLTAS - case decode_type_t::VOLTAS: { - IRVoltas ac(kGpioUnused); - ac.setRaw(decode->state); - *result = ac.toCommon(prev); - break; - } -#endif // DECODE_VOLTAS -#if DECODE_WHIRLPOOL_AC - case decode_type_t::WHIRLPOOL_AC: { - IRWhirlpoolAc ac(kGpioUnused); - ac.setRaw(decode->state); - *result = ac.toCommon(prev); - break; - } -#endif // DECODE_WHIRLPOOL_AC -#if DECODE_TRANSCOLD - case decode_type_t::TRANSCOLD: { - IRTranscoldAc ac(kGpioUnused); - ac.setRaw(decode->value); // TRANSCOLD Uses value instead of state. - *result = ac.toCommon(prev); - break; - } -#endif // DECODE_TRANSCOLD - default: - return false; - } - return true; - } -} // namespace IRAcUtils diff --git a/lib/IRremoteESP8266/src/IRac.h b/lib/IRremoteESP8266/src/IRac.h index 7ef3adf4ad..8dcf491dc3 100644 --- a/lib/IRremoteESP8266/src/IRac.h +++ b/lib/IRremoteESP8266/src/IRac.h @@ -6,44 +6,44 @@ #ifndef UNIT_TEST #include #endif -#include "IRremoteESP8266.h" -#include "ir_Airwell.h" -#include "ir_Amcor.h" -#include "ir_Argo.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_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 "../src/IRremoteESP8266.h" +#include "../src/ir_Airwell.h" +#include "../src/ir_Amcor.h" +#include "../src/ir_Argo.h" +#include "../src/ir_Carrier.h" +#include "../src/ir_Coolix.h" +#include "../src/ir_Corona.h" +#include "../src/ir_Daikin.h" +#include "../src/ir_Delonghi.h" +#include "../src/ir_Fujitsu.h" +#include "../src/ir_Ecoclim.h" +#include "../src/ir_Electra.h" +#include "../src/ir_Goodweather.h" +#include "../src/ir_Gree.h" +#include "../src/ir_Haier.h" +#include "../src/ir_Hitachi.h" +#include "../src/ir_Kelon.h" +#include "../src/ir_Kelvinator.h" +#include "../src/ir_LG.h" +#include "../src/ir_Midea.h" +#include "../src/ir_Mitsubishi.h" +#include "../src/ir_MitsubishiHeavy.h" +#include "../src/ir_Neoclima.h" +#include "../src/ir_Panasonic.h" +#include "../src/ir_Rhoss.h" +#include "../src/ir_Samsung.h" +#include "../src/ir_Sanyo.h" +#include "../src/ir_Sharp.h" +#include "../src/ir_Tcl.h" +#include "../src/ir_Technibel.h" +#include "../src/ir_Teco.h" +#include "../src/ir_Toshiba.h" +#include "../src/ir_Transcold.h" +#include "../src/ir_Trotec.h" +#include "../src/ir_Truma.h" +#include "../src/ir_Vestel.h" +#include "../src/ir_Voltas.h" +#include "../src/ir_Whirlpool.h" // Constants const int8_t kGpioUnused = -1; ///< A placeholder for not using an actual GPIO. diff --git a/lib/IRremoteESP8266/src/IRrecv.cpp b/lib/IRremoteESP8266/src/IRrecv.cpp deleted file mode 100644 index 5dc585bc75..0000000000 --- a/lib/IRremoteESP8266/src/IRrecv.cpp +++ /dev/null @@ -1,1937 +0,0 @@ -// Copyright 2009 Ken Shirriff -// Copyright 2015 Mark Szabo -// Copyright 2015 Sebastien Warin -// Copyright 2017, 2019 David Conran - -#include "IRrecv.h" -#include -#ifndef UNIT_TEST -#if defined(ESP8266) -extern "C" { -#include -#include -} -#endif // ESP8266 -#include -#endif -#include -#ifdef UNIT_TEST -#include -#endif // UNIT_TEST -#include "IRremoteESP8266.h" -#include "IRutils.h" - -#ifdef UNIT_TEST -#undef ICACHE_RAM_ATTR -#define ICACHE_RAM_ATTR -#endif - -#ifndef USE_IRAM_ATTR -#if defined(ESP8266) -#if defined(IRAM_ATTR) -#define USE_IRAM_ATTR IRAM_ATTR -#else // IRAM_ATTR -#define USE_IRAM_ATTR ICACHE_RAM_ATTR -#endif // IRAM_ATTR -#endif // ESP8266 -#if defined(ESP32) -#define USE_IRAM_ATTR IRAM_ATTR -#endif // ESP32 -#endif // USE_IRAM_ATTR - -#define ONCE 0 - -// Updated by David Conran (https://github.com/crankyoldgit) for receiving IR -// code on ESP32 -// Updated by Sebastien Warin (http://sebastien.warin.fr) for receiving IR code -// on ESP8266 -// Updated by markszabo (https://github.com/crankyoldgit/IRremoteESP8266) for -// sending IR code on ESP8266 - -// Globals -#ifndef UNIT_TEST -#if defined(ESP8266) -namespace _IRrecv { -static ETSTimer timer; -} // namespace _IRrecv -#endif // ESP8266 -#if defined(ESP32) -// Required structs/types from: -// https://github.com/espressif/arduino-esp32/blob/6b0114366baf986c155e8173ab7c22bc0c5fcedc/cores/esp32/esp32-hal-timer.c#L28-L58 -// These are needed to be able to directly manipulate the timer registers from -// inside an ISR. This is very very ugly. -// Ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/1350 -// Note: This will need to be updated if it ever changes. -// -// Start of Horrible Hack! -typedef struct { - union { - struct { - uint32_t reserved0: 10; - uint32_t alarm_en: 1; - /*When set alarm is enabled*/ - uint32_t level_int_en: 1; - /*When set level type interrupt will be generated during alarm*/ - uint32_t edge_int_en: 1; - /*When set edge type interrupt will be generated during alarm*/ - uint32_t divider: 16; - /*Timer clock (T0/1_clk) pre-scale value.*/ - uint32_t autoreload: 1; - /*When set timer 0/1 auto-reload at alarming is enabled*/ - uint32_t increase: 1; - /*When set timer 0/1 time-base counter increment. - When cleared timer 0 time-base counter decrement.*/ - uint32_t enable: 1; - /*When set timer 0/1 time-base counter is enabled*/ - }; - uint32_t val; - } config; - uint32_t cnt_low; - /*Register to store timer 0/1 time-base counter current value lower 32 - bits.*/ - uint32_t cnt_high; - /*Register to store timer 0 time-base counter current value higher 32 - bits.*/ - uint32_t update; - /*Write any value will trigger a timer 0 time-base counter value update - (timer 0 current value will be stored in registers above)*/ - uint32_t alarm_low; - /*Timer 0 time-base counter value lower 32 bits that will trigger the - alarm*/ - uint32_t alarm_high; - /*Timer 0 time-base counter value higher 32 bits that will trigger the - alarm*/ - uint32_t load_low; - /*Lower 32 bits of the value that will load into timer 0 time-base counter*/ - uint32_t load_high; - /*higher 32 bits of the value that will load into timer 0 time-base - counter*/ - uint32_t reload; - /*Write any value will trigger timer 0 time-base counter reload*/ -} hw_timer_reg_t; - -typedef struct hw_timer_s { - hw_timer_reg_t * dev; - uint8_t num; - uint8_t group; - uint8_t timer; - portMUX_TYPE lock; -} hw_timer_t; -// End of Horrible Hack. - -namespace _IRrecv { -static hw_timer_t * timer = NULL; -} // namespace _IRrecv -#endif // ESP32 -using _IRrecv::timer; -#endif // UNIT_TEST - -namespace _IRrecv { // Namespace extension -#if defined(ESP32) -portMUX_TYPE mux = portMUX_INITIALIZER_UNLOCKED; -#endif // ESP32 -volatile irparams_t params; -irparams_t *params_save; // A copy of the interrupt state while decoding. -} // namespace _IRrecv - -#if defined(ESP32) -using _IRrecv::mux; -#endif // ESP32 -using _IRrecv::params; -using _IRrecv::params_save; - -#ifndef UNIT_TEST -#if defined(ESP8266) -/// Interrupt handler for when the timer runs out. -/// It signals to the library that capturing of IR data has stopped. -/// @param[in] arg Unused. (ESP8266 Only) -static void USE_IRAM_ATTR read_timeout(void *arg __attribute__((unused))) { - os_intr_lock(); -#endif // ESP8266 -/// @cond IGNORE -#if defined(ESP32) -/// Interrupt handler for when the timer runs out. -/// It signals to the library that capturing of IR data has stopped. -/// @note ESP32 version -static void USE_IRAM_ATTR read_timeout(void) { -/// @endcond - portENTER_CRITICAL(&mux); -#endif // ESP32 - if (params.rawlen) params.rcvstate = kStopState; -#if defined(ESP8266) - os_intr_unlock(); -#endif // ESP8266 -#if defined(ESP32) - portEXIT_CRITICAL(&mux); -#endif // ESP32 -} - -/// Interrupt handler for changes on the GPIO pin handling incoming IR messages. -static void USE_IRAM_ATTR gpio_intr() { - uint32_t now = micros(); - static uint32_t start = 0; - -#if defined(ESP8266) - uint32_t gpio_status = GPIO_REG_READ(GPIO_STATUS_ADDRESS); - os_timer_disarm(&timer); - GPIO_REG_WRITE(GPIO_STATUS_W1TC_ADDRESS, gpio_status); -#endif // ESP8266 - - // Grab a local copy of rawlen to reduce instructions used in IRAM. - // This is an ugly premature optimisation code-wise, but we do everything we - // can to save IRAM. - // It seems referencing the value via the structure uses more instructions. - // Less instructions means faster and less IRAM used. - // N.B. It saves about 13 bytes of IRAM. - uint16_t rawlen = params.rawlen; - - if (rawlen >= params.bufsize) { - params.overflow = true; - params.rcvstate = kStopState; - } - - if (params.rcvstate == kStopState) return; - - if (params.rcvstate == kIdleState) { - params.rcvstate = kMarkState; - params.rawbuf[rawlen] = 1; - } else { - if (now < start) - params.rawbuf[rawlen] = (UINT32_MAX - start + now) / kRawTick; - else - params.rawbuf[rawlen] = (now - start) / kRawTick; - } - params.rawlen++; - - start = now; - -#if defined(ESP8266) - os_timer_arm(&timer, params.timeout, ONCE); -#endif // ESP8266 -#if defined(ESP32) - // Reset the timeout. - // - // The following three lines of code are the equiv of: - // `timerWrite(timer, 0);` - // We can't call that routine safely from inside an ISR as that procedure - // is not stored in IRAM. Hence, we do it manually so that it's covered by - // USE_IRAM_ATTR in this ISR. - // @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1350 - // @see https://github.com/espressif/arduino-esp32/blob/6b0114366baf986c155e8173ab7c22bc0c5fcedc/cores/esp32/esp32-hal-timer.c#L106-L110 - timer->dev->load_high = (uint32_t) 0; - timer->dev->load_low = (uint32_t) 0; - timer->dev->reload = 1; - // The next line is the same, but instead replaces: - // `timerAlarmEnable(timer);` - // @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1350 - // @see https://github.com/espressif/arduino-esp32/blob/6b0114366baf986c155e8173ab7c22bc0c5fcedc/cores/esp32/esp32-hal-timer.c#L176-L178 - timer->dev->config.alarm_en = 1; -#endif // ESP32 -} -#endif // UNIT_TEST - -// Start of IRrecv class ------------------- - -/// Class constructor -/// Args: -/// @param[in] recvpin The GPIO pin the IR receiver module's data pin is -/// connected to. -/// @param[in] bufsize Nr. of entries to have in the capture buffer. -/// (Default: kRawBuf) -/// @param[in] timeout Nr. of milli-Seconds of no signal before we stop -/// capturing data. (Default: kTimeoutMs) -/// @param[in] save_buffer Use a second (save) buffer to decode from. -/// (Default: false) -/// @param[in] timer_num Nr. of the ESP32 timer to use (0 to 3) (ESP32 Only) -#if defined(ESP32) -IRrecv::IRrecv(const uint16_t recvpin, const uint16_t bufsize, - const uint8_t timeout, const bool save_buffer, - const uint8_t timer_num) { - // There are only 4 timers. 0 to 3. - _timer_num = std::min(timer_num, (uint8_t)3); -#else // ESP32 -/// @cond IGNORE -/// Class constructor -/// Args: -/// @param[in] recvpin The GPIO pin the IR receiver module's data pin is -/// connected to. -/// @param[in] bufsize Nr. of entries to have in the capture buffer. -/// (Default: kRawBuf) -/// @param[in] timeout Nr. of milli-Seconds of no signal before we stop -/// capturing data. (Default: kTimeoutMs) -/// @param[in] save_buffer Use a second (save) buffer to decode from. -/// (Default: false) -IRrecv::IRrecv(const uint16_t recvpin, const uint16_t bufsize, - const uint8_t timeout, const bool save_buffer) { -/// @endcond -#endif // ESP32 - params.recvpin = recvpin; - params.bufsize = bufsize; - // Ensure we are going to be able to store all possible values in the - // capture buffer. - params.timeout = std::min(timeout, (uint8_t)kMaxTimeoutMs); - params.rawbuf = new uint16_t[bufsize]; - if (params.rawbuf == NULL) { - DPRINTLN( - "Could not allocate memory for the primary IR buffer.\n" - "Try a smaller size for CAPTURE_BUFFER_SIZE.\nRebooting!"); -#ifndef UNIT_TEST - ESP.restart(); // Mem alloc failure. Reboot. -#endif - } - // If we have been asked to use a save buffer (for decoding), then create one. - if (save_buffer) { - params_save = new irparams_t; - params_save->rawbuf = new uint16_t[bufsize]; - // Check we allocated the memory successfully. - if (params_save->rawbuf == NULL) { - DPRINTLN( - "Could not allocate memory for the second IR buffer.\n" - "Try a smaller size for CAPTURE_BUFFER_SIZE.\nRebooting!"); -#ifndef UNIT_TEST - ESP.restart(); // Mem alloc failure. Reboot. -#endif - } - } else { - params_save = NULL; - } -#if DECODE_HASH - _unknown_threshold = kUnknownThreshold; -#endif // DECODE_HASH - _tolerance = kTolerance; -} - -/// Class destructor -/// Cleans up after the object is no longer needed. -/// e.g. Frees up all memory used by the various buffers, and disables any -/// timers or interrupts used. -IRrecv::~IRrecv(void) { - disableIRIn(); -#if defined(ESP32) - if (timer != NULL) timerEnd(timer); // Cleanup the ESP32 timeout timer. -#endif // ESP32 - delete[] params.rawbuf; - if (params_save != NULL) { - delete[] params_save->rawbuf; - delete params_save; - } -} - -/// Set up and (re)start the IR capture mechanism. -/// @param[in] pullup A flag indicating should the GPIO use the internal pullup -/// resistor. (Default: `false`. i.e. No.) -void IRrecv::enableIRIn(const bool pullup) { - // ESP32's seem to require explicitly setting the GPIO to INPUT etc. - // This wasn't required on the ESP8266s, but it shouldn't hurt to make sure. - if (pullup) { -#ifndef UNIT_TEST - pinMode(params.recvpin, INPUT_PULLUP); - } else { - pinMode(params.recvpin, INPUT); -#endif // UNIT_TEST - } -#if defined(ESP32) - // Initialise the ESP32 timer. - // 80MHz / 80 = 1 uSec granularity. - timer = timerBegin(_timer_num, 80, true); - // Set the timer so it only fires once, and set it's trigger in uSeconds. - timerAlarmWrite(timer, MS_TO_USEC(params.timeout), ONCE); - // Note: Interrupt needs to be attached before it can be enabled or disabled. - timerAttachInterrupt(timer, &read_timeout, true); -#endif // ESP32 - - // Initialise state machine variables - resume(); - -#ifndef UNIT_TEST -#if defined(ESP8266) - // Initialise ESP8266 timer. - os_timer_disarm(&timer); - os_timer_setfn(&timer, reinterpret_cast(read_timeout), - NULL); -#endif // ESP8266 - // Attach Interrupt - attachInterrupt(params.recvpin, gpio_intr, CHANGE); -#endif // UNIT_TEST -} - -/// Stop collection of any received IR data. -/// Disable any timers and interrupts. -void IRrecv::disableIRIn(void) { -#ifndef UNIT_TEST -#if defined(ESP8266) - os_timer_disarm(&timer); -#endif // ESP8266 -#if defined(ESP32) - timerAlarmDisable(timer); - timerEnd(timer); -#endif // ESP32 - detachInterrupt(params.recvpin); -#endif // UNIT_TEST -} - -/// Resume collection of received IR data. -/// @note This is required if `decode()` is successful and `save_buffer` was -/// not set when the class was instanciated. -/// @see IRrecv class constructor -void IRrecv::resume(void) { - params.rcvstate = kIdleState; - params.rawlen = 0; - params.overflow = false; -#if defined(ESP32) - timerAlarmDisable(timer); -#endif // ESP32 -} - -/// Make a copy of the interrupt state & buffer data. -/// Needed because irparams is marked as volatile, thus memcpy() isn't allowed. -/// Only call this when you know the interrupt handlers won't modify anything. -/// i.e. In kStopState. -/// @param[in] src Pointer to an irparams_t structure to copy from. -/// @param[out] dst Pointer to an irparams_t structure to copy to. -void IRrecv::copyIrParams(volatile irparams_t *src, irparams_t *dst) { - // Typecast src and dst addresses to (char *) - char *csrc = (char *)src; // NOLINT(readability/casting) - char *cdst = (char *)dst; // NOLINT(readability/casting) - - // Save the pointer to the destination's rawbuf so we don't lose it as - // the for-loop/copy after this will overwrite it with src's rawbuf pointer. - // This isn't immediately obvious due to typecasting/different variable names. - uint16_t *dst_rawbuf_ptr; - dst_rawbuf_ptr = dst->rawbuf; - - // Copy contents of src[] to dst[] - for (uint16_t i = 0; i < sizeof(irparams_t); i++) cdst[i] = csrc[i]; - - // Restore the buffer pointer - dst->rawbuf = dst_rawbuf_ptr; - - // Copy the rawbuf - for (uint16_t i = 0; i < dst->bufsize; i++) dst->rawbuf[i] = src->rawbuf[i]; -} - -/// Obtain the maximum number of entries possible in the capture buffer. -/// i.e. It's size. -/// @return The size of the buffer that is in use by the object. -uint16_t IRrecv::getBufSize(void) { return params.bufsize; } - -#if DECODE_HASH -/// Set the minimum length we will consider for reporting UNKNOWN message types. -/// @param[in] length Min nr. of mark/space pulses required to be considered. -void IRrecv::setUnknownThreshold(const uint16_t length) { - _unknown_threshold = length; -} -#endif // DECODE_HASH - - -/// Set the base tolerance percentage for matching incoming IR messages. -/// @param[in] percent An integer percentage. (0-100) -void IRrecv::setTolerance(const uint8_t percent) { - _tolerance = std::min(percent, (uint8_t)100); -} - -/// Get the base tolerance percentage for matching incoming IR messages. -/// @return A integer percentage. -uint8_t IRrecv::getTolerance(void) { return _tolerance; } - -#if ENABLE_NOISE_FILTER_OPTION -/// Remove or merge pulses in the capture buffer that are too short. -/// @param[in,out] results Ptr to the decode_results we are going to filter. -/// @param[in] floor Only allow values in the buffer large than this. -/// (in microSeconds) -void IRrecv::crudeNoiseFilter(decode_results *results, const uint16_t floor) { - if (floor == 0) return; // Nothing to do. - const uint16_t kTickFloor = floor / kRawTick; - const uint16_t kBufSize = getBufSize(); - uint16_t offset = kStartOffset; - while (offset < results->rawlen && offset + 2 < kBufSize) { - uint16_t curr = results->rawbuf[offset]; - uint16_t next = results->rawbuf[offset + 1]; - uint16_t addition = curr + next; - if (curr < kTickFloor) { // Is it too short? - // Shuffle the buffer down. i.e. Remove the mark & space pair. - // Note: `memcpy()` can't be used as rawbuf is `volatile`. - for (uint16_t i = offset + 2; i <= results->rawlen && i < kBufSize; i++) - results->rawbuf[i - 2] = results->rawbuf[i]; - if (offset > 1) { // There is a previous pair we can add to. - // Merge this pair into into the previous space. - results->rawbuf[offset - 1] += addition; - } - results->rawlen -= 2; // Adjust the length. - } else { - offset++; // Move along. - } - } -} -#endif // ENABLE_NOISE_FILTER_OPTION - -/// Decodes the received IR message. -/// If the interrupt state is saved, we will immediately resume waiting -/// for the next IR message to avoid missing messages. -/// @note There is a trade-off here. Saving the state means less time lost until -/// we can receiving the next message vs. using more RAM. Choose appropriately. -/// @param[out] results A PTR to where the decoded IR message will be stored. -/// @param[out] save A PTR to an irparams_t instance in which to save -/// the interrupt's memory/state. NULL means don't save it. -/// @param[in] max_skip Maximum Nr. of pulses at the begining of a capture we -/// can skip when attempting to find a protocol we can successfully decode. -/// This parameter can dramatically improve detection of protocols -/// when there is light IR interference just before an incoming IR -/// message, however, it comes at a steep performace price. -/// (Default is 0. No skipping.) -/// @warning Increasing the `max_skip` value will dramatically (linearly) -/// increase the cpu time & usage to decode protocols. -/// e.g. 0 -> 1 will be a 2x increase in cpu usage/time. -/// 0 -> 2 will be a 3x increase etc. -/// If you are going to do this, consider disabling protocol decoding for -/// protocols you are not expecting. -/// @param[in] noise_floor Pulses below this size (in usecs) will be removed or -/// merged prior to any decoding. This is to try to remove noise/poor -/// readings & slightly increase the chances of a successful decode but at the -/// cost of data fidelity & integrity. -/// (Defaults to 0 usecs. i.e. Don't filter; which is safe!) -/// @warning DANGER: **Here Be Dragons!** -/// If you set the `noise_floor` value too high, it **WILL** break decoding -/// of some protocols. You have been warned! -/// **Any** non-zero value has the potential to **cook** the captured raw data -/// i.e. The raw data is going to lie to you. -/// It may obscure hardware, circuit, & environment issues thus making it -/// impossible to support you accurately or confidently. -/// Values of <= 50 usecs will probably be safe. -/// 51 - 100 usecs **might** be okay. -/// 100 - 150 usecs is "Danger, Will Robinson!". -/// 150 - 200 usecs expect broken protocols. -/// At 200+ usecs, you **have** protocols you can't decode!! -/// @return A boolean indicating if an IR message is ready or not. -bool IRrecv::decode(decode_results *results, irparams_t *save, - uint8_t max_skip, uint16_t noise_floor) { - // Proceed only if an IR message been received. -#ifndef UNIT_TEST - if (params.rcvstate != kStopState) return false; -#endif - - // Clear the entry we are currently pointing to when we got the timeout. - // i.e. Stopped collecting IR data. - // It's junk as we never wrote an entry to it and can only confuse decoding. - // This is done here rather than logically the best place in read_timeout() - // as it saves a few bytes of ICACHE_RAM as that routine is bound to an - // interrupt. decode() is not stored in ICACHE_RAM. - // Another better option would be to zero the entire irparams.rawbuf[] on - // resume() but that is a much more expensive operation compare to this. - // However, don't do this if rawbuf is already full as we stomp over the heap. - // See: https://github.com/crankyoldgit/IRremoteESP8266/issues/1516 - if (!params.overflow) params.rawbuf[params.rawlen] = 0; - - bool resumed = false; // Flag indicating if we have resumed. - - // If we were requested to use a save buffer previously, do so. - if (save == NULL) save = params_save; - - if (save == NULL) { - // We haven't been asked to copy it so use the existing memory. -#ifndef UNIT_TEST - results->rawbuf = params.rawbuf; - results->rawlen = params.rawlen; - results->overflow = params.overflow; -#endif - } else { - copyIrParams(¶ms, save); // Duplicate the interrupt's memory. - resume(); // It's now safe to rearm. The IR message won't be overridden. - resumed = true; - // Point the results at the saved copy. - results->rawbuf = save->rawbuf; - results->rawlen = save->rawlen; - results->overflow = save->overflow; - } - - // Reset any previously partially processed results. - results->decode_type = UNKNOWN; - results->bits = 0; - results->value = 0; - results->address = 0; - results->command = 0; - results->repeat = false; - -#if ENABLE_NOISE_FILTER_OPTION - crudeNoiseFilter(results, noise_floor); -#endif // ENABLE_NOISE_FILTER_OPTION - // Keep looking for protocols until we've run out of entries to skip or we - // find a valid protocol message. - for (uint16_t offset = kStartOffset; - offset <= (max_skip * 2) + kStartOffset; - offset += 2) { -#if DECODE_AIWA_RC_T501 - DPRINTLN("Attempting Aiwa RC T501 decode"); - // Try decodeAiwaRCT501() before decodeSanyoLC7461() & decodeNEC() - // because the protocols are similar. This protocol is more specific than - // those ones, so should go before them. - if (decodeAiwaRCT501(results, offset)) return true; -#endif -#if DECODE_SANYO - DPRINTLN("Attempting Sanyo LC7461 decode"); - // Try decodeSanyoLC7461() before decodeNEC() because the protocols are - // similar in timings & structure, but the Sanyo one is much longer than the - // NEC protocol (42 vs 32 bits) so this one should be tried first to try to - // reduce false detection as a NEC packet. - if (decodeSanyoLC7461(results, offset)) return true; -#endif -#if DECODE_CARRIER_AC - DPRINTLN("Attempting Carrier AC decode"); - // Try decodeCarrierAC() before decodeNEC() because the protocols are - // similar in timings & structure, but the Carrier one is much longer than - // the NEC protocol (3x32 bits vs 1x32 bits) so this one should be tried - // first to try to reduce false detection as a NEC packet. - if (decodeCarrierAC(results, offset)) return true; -#endif -#if DECODE_PIONEER - DPRINTLN("Attempting Pioneer decode"); - // Try decodePioneer() before decodeNEC() because the protocols are - // similar in timings & structure, but the Pioneer one is much longer than - // the NEC protocol (2x32 bits vs 1x32 bits) so this one should be tried - // first to try to reduce false detection as a NEC packet. - if (decodePioneer(results, offset)) return true; -#endif -#if DECODE_EPSON - DPRINTLN("Attempting Epson decode"); - // Try decodeEpson() before decodeNEC() because the protocols are - // similar in timings & structure, but the Epson one is much longer than the - // NEC protocol (3x32 identical bits vs 1x32 bits) so this one should be tried - // first to try to reduce false detection as a NEC packet. - if (decodeEpson(results, offset)) return true; -#endif -#if DECODE_NEC - DPRINTLN("Attempting NEC decode"); - if (decodeNEC(results, offset)) return true; -#endif -#if DECODE_MILESTAG2 - DPRINTLN("Attempting MilesTag2 decode"); - // Try decodeMilestag2() before decodeSony() because the protocols are - // similar in timings & structure, but the Miles one differs in nbits - // so this one should be tried first to try to reduce false detection - if (decodeMilestag2(results, offset, kMilesTag2MsgBits) || - decodeMilestag2(results, offset, kMilesTag2ShotBits)) return true; -#endif -#if DECODE_SONY - DPRINTLN("Attempting Sony decode"); - if (decodeSony(results, offset)) return true; -#endif -#if DECODE_MITSUBISHI - DPRINTLN("Attempting Mitsubishi decode"); - if (decodeMitsubishi(results, offset)) return true; -#endif -#if DECODE_MITSUBISHI_AC - DPRINTLN("Attempting Mitsubishi AC decode"); - if (decodeMitsubishiAC(results, offset)) return true; -#endif -#if DECODE_MITSUBISHI2 - DPRINTLN("Attempting Mitsubishi2 decode"); - if (decodeMitsubishi2(results, offset)) return true; -#endif -#if DECODE_RC5 - DPRINTLN("Attempting RC5 decode"); - if (decodeRC5(results, offset)) return true; -#endif -#if DECODE_RC6 - DPRINTLN("Attempting RC6 decode"); - if (decodeRC6(results, offset)) return true; -#endif -#if DECODE_RCMM - DPRINTLN("Attempting RC-MM decode"); - if (decodeRCMM(results, offset)) return true; -#endif -#if DECODE_FUJITSU_AC - // Fujitsu A/C needs to precede Panasonic and Denon as it has a short - // message which looks exactly the same as a Panasonic/Denon message. - DPRINTLN("Attempting Fujitsu A/C decode"); - if (decodeFujitsuAC(results, offset)) return true; -#endif -#if DECODE_DENON - // Denon needs to precede Panasonic as it is a special case of Panasonic. - DPRINTLN("Attempting Denon decode"); - if (decodeDenon(results, offset, kDenon48Bits) || - decodeDenon(results, offset, kDenonBits) || - decodeDenon(results, offset, kDenonLegacyBits)) - return true; -#endif -#if DECODE_PANASONIC - DPRINTLN("Attempting Panasonic decode"); - if (decodePanasonic(results, offset)) return true; -#endif -#if DECODE_LG - DPRINTLN("Attempting LG (28-bit) decode"); - if (decodeLG(results, offset, kLgBits, true)) return true; - DPRINTLN("Attempting LG (32-bit) decode"); - // LG32 should be tried before Samsung - if (decodeLG(results, offset, kLg32Bits, true)) return true; -#endif -#if DECODE_GICABLE - // Note: Needs to happen before JVC decode, because it looks similar except - // with a required NEC-like repeat code. - DPRINTLN("Attempting GICable decode"); - if (decodeGICable(results, offset)) return true; -#endif -#if DECODE_JVC - DPRINTLN("Attempting JVC decode"); - if (decodeJVC(results, offset)) return true; -#endif -#if DECODE_SAMSUNG - DPRINTLN("Attempting SAMSUNG decode"); - if (decodeSAMSUNG(results, offset)) return true; -#endif -#if DECODE_SAMSUNG36 - DPRINTLN("Attempting Samsung36 decode"); - if (decodeSamsung36(results, offset)) return true; -#endif -#if DECODE_WHYNTER - DPRINTLN("Attempting Whynter decode"); - if (decodeWhynter(results, offset)) return true; -#endif -#if DECODE_DISH - DPRINTLN("Attempting DISH decode"); - if (decodeDISH(results, offset)) return true; -#endif -#if DECODE_SHARP - DPRINTLN("Attempting Sharp decode"); - if (decodeSharp(results, offset)) return true; -#endif -#if DECODE_COOLIX - DPRINTLN("Attempting Coolix decode"); - if (decodeCOOLIX(results, offset)) return true; -#endif -#if DECODE_NIKAI - DPRINTLN("Attempting Nikai decode"); - if (decodeNikai(results, offset)) return true; -#endif -#if DECODE_KELVINATOR - // Kelvinator based-devices use a similar code to Gree ones, to avoid false - // matches this needs to happen before decodeGree(). - DPRINTLN("Attempting Kelvinator decode"); - if (decodeKelvinator(results, offset)) return true; -#endif -#if DECODE_DAIKIN - DPRINTLN("Attempting Daikin decode"); - if (decodeDaikin(results, offset)) return true; -#endif -#if DECODE_DAIKIN2 - DPRINTLN("Attempting Daikin2 decode"); - if (decodeDaikin2(results, offset)) return true; -#endif -#if DECODE_DAIKIN216 - DPRINTLN("Attempting Daikin216 decode"); - if (decodeDaikin216(results, offset)) return true; -#endif -#if DECODE_TOSHIBA_AC - DPRINTLN("Attempting Toshiba AC 72bit decode"); - if (decodeToshibaAC(results, offset)) return true; - DPRINTLN("Attempting Toshiba AC 80bit decode"); - if (decodeToshibaAC(results, offset, kToshibaACBitsLong)) return true; - DPRINTLN("Attempting Toshiba AC 56bit decode"); - if (decodeToshibaAC(results, offset, kToshibaACBitsShort)) return true; -#endif -#if DECODE_MIDEA - DPRINTLN("Attempting Midea decode"); - if (decodeMidea(results, offset)) return true; -#endif -#if DECODE_MAGIQUEST - DPRINTLN("Attempting Magiquest decode"); - if (decodeMagiQuest(results, offset)) return true; -#endif - /* NOTE: Disabled due to poor quality. -#if DECODE_SANYO - // The Sanyo S866500B decoder is very poor quality & depricated. - // *IF* you are going to enable it, do it near last to avoid false positive - // matches. - DPRINTLN("Attempting Sanyo SA8650B decode"); - if (decodeSanyo(results, offset)) - return true; -#endif - */ -#if DECODE_NEC - // Some devices send NEC-like codes that don't follow the true NEC spec. - // This should detect those. e.g. Apple TV remote etc. - // This needs to be done after all other codes that use strict and some - // other protocols that are NEC-like as well, as turning off strict may - // cause this to match other valid protocols. - DPRINTLN("Attempting NEC (non-strict) decode"); - if (decodeNEC(results, offset, kNECBits, false)) { - results->decode_type = NEC_LIKE; - return true; - } -#endif -#if DECODE_LASERTAG - DPRINTLN("Attempting Lasertag decode"); - if (decodeLasertag(results, offset)) return true; -#endif -#if DECODE_GREE - // Gree based-devices use a similar code to Kelvinator ones, to avoid false - // matches this needs to happen after decodeKelvinator(). - DPRINTLN("Attempting Gree decode"); - if (decodeGree(results, offset)) return true; -#endif -#if DECODE_HAIER_AC - DPRINTLN("Attempting Haier AC decode"); - if (decodeHaierAC(results, offset)) return true; -#endif -#if DECODE_HAIER_AC_YRW02 - DPRINTLN("Attempting Haier AC YR-W02 decode"); - if (decodeHaierACYRW02(results, offset)) return true; -#endif -#if DECODE_HAIER_AC176 - DPRINTLN("Attempting Haier AC 176 bit decode"); - if (decodeHaierAC176(results, offset)) return true; -#endif // DECODE_HAIER_AC176 -#if DECODE_HITACHI_AC424 - // HitachiAc424 should be checked before HitachiAC, HitachiAC2, - // & HitachiAC184 - DPRINTLN("Attempting Hitachi AC 424 decode"); - if (decodeHitachiAc424(results, offset, kHitachiAc424Bits)) return true; -#endif // DECODE_HITACHI_AC424 -#if DECODE_MITSUBISHI136 - // Needs to happen before HitachiAc3 decode. - DPRINTLN("Attempting Mitsubishi136 decode"); - if (decodeMitsubishi136(results, offset)) return true; -#endif // DECODE_MITSUBISHI136 -#if DECODE_HITACHI_AC3 - // HitachiAc3 should be checked before HitachiAC & HitachiAC2 - // Attempt normal before the short version. - DPRINTLN("Attempting Hitachi AC3 decode"); - // Order these in decreasing bit size, as it is more optimal. - if (decodeHitachiAc3(results, offset, kHitachiAc3Bits) || - decodeHitachiAc3(results, offset, kHitachiAc3Bits - 4 * 8) || - decodeHitachiAc3(results, offset, kHitachiAc3Bits - 6 * 8) || - decodeHitachiAc3(results, offset, kHitachiAc3MinBits + 2 * 8) || - decodeHitachiAc3(results, offset, kHitachiAc3MinBits)) - return true; -#endif // DECODE_HITACHI_AC3 -#if DECODE_HITACHI_AC344 - // HitachiAC344 should be checked before HitachiAC - DPRINTLN("Attempting Hitachi AC344 decode"); - if (decodeHitachiAC(results, offset, kHitachiAc344Bits, true, false)) - return true; -#endif // DECODE_HITACHI_AC344 -#if DECODE_HITACHI_AC2 - // HitachiAC2 should be checked before HitachiAC - DPRINTLN("Attempting Hitachi AC2 decode"); - if (decodeHitachiAC(results, offset, kHitachiAc2Bits)) return true; -#endif // DECODE_HITACHI_AC2 -#if DECODE_HITACHI_AC - DPRINTLN("Attempting Hitachi AC decode"); - if (decodeHitachiAC(results, offset, kHitachiAcBits)) return true; -#endif -#if DECODE_HITACHI_AC1 - DPRINTLN("Attempting Hitachi AC1 decode"); - if (decodeHitachiAC(results, offset, kHitachiAc1Bits)) return true; -#endif -#if DECODE_WHIRLPOOL_AC - DPRINTLN("Attempting Whirlpool AC decode"); - if (decodeWhirlpoolAC(results, offset)) return true; -#endif -#if DECODE_SAMSUNG_AC - DPRINTLN("Attempting Samsung AC (extended) decode"); - // Check the extended size first, as it should fail fast due to longer - // length. - if (decodeSamsungAC(results, offset, kSamsungAcExtendedBits, false)) - return true; - // Now check for the more common length. - DPRINTLN("Attempting Samsung AC decode"); - if (decodeSamsungAC(results, offset, kSamsungAcBits)) return true; -#endif -#if DECODE_ELECTRA_AC - DPRINTLN("Attempting Electra AC decode"); - if (decodeElectraAC(results, offset)) return true; -#endif -#if DECODE_PANASONIC_AC - DPRINTLN("Attempting Panasonic AC decode"); - if (decodePanasonicAC(results, offset)) return true; - DPRINTLN("Attempting Panasonic AC short decode"); - if (decodePanasonicAC(results, offset, kPanasonicAcShortBits)) return true; -#endif -#if DECODE_LUTRON - DPRINTLN("Attempting Lutron decode"); - if (decodeLutron(results, offset)) return true; -#endif -#if DECODE_MWM - DPRINTLN("Attempting MWM decode"); - if (decodeMWM(results, offset)) return true; -#endif -#if DECODE_VESTEL_AC - DPRINTLN("Attempting Vestel AC decode"); - if (decodeVestelAc(results, offset)) return true; -#endif -#if DECODE_MITSUBISHI112 || DECODE_TCL112AC - // Mitsubish112 and Tcl112 share the same decoder. - DPRINTLN("Attempting Mitsubishi112/TCL112AC decode"); - if (decodeMitsubishi112(results, offset)) return true; -#endif // DECODE_MITSUBISHI112 || DECODE_TCL112AC -#if DECODE_TECO - DPRINTLN("Attempting Teco decode"); - if (decodeTeco(results, offset)) return true; -#endif -#if DECODE_LEGOPF - DPRINTLN("Attempting LEGOPF decode"); - if (decodeLegoPf(results, offset)) return true; -#endif -#if DECODE_MITSUBISHIHEAVY - DPRINTLN("Attempting MITSUBISHIHEAVY (152 bit) decode"); - if (decodeMitsubishiHeavy(results, offset, kMitsubishiHeavy152Bits)) - return true; - DPRINTLN("Attempting MITSUBISHIHEAVY (88 bit) decode"); - if (decodeMitsubishiHeavy(results, offset, kMitsubishiHeavy88Bits)) - return true; -#endif -#if DECODE_ARGO - DPRINTLN("Attempting Argo decode"); - if (decodeArgo(results, offset)) return true; -#endif // DECODE_ARGO -#if DECODE_SHARP_AC - DPRINTLN("Attempting SHARP_AC decode"); - if (decodeSharpAc(results, offset)) return true; -#endif -#if DECODE_GOODWEATHER - DPRINTLN("Attempting GOODWEATHER decode"); - if (decodeGoodweather(results, offset)) return true; -#endif // DECODE_GOODWEATHER -#if DECODE_INAX - DPRINTLN("Attempting Inax decode"); - if (decodeInax(results, offset)) return true; -#endif // DECODE_INAX -#if DECODE_TROTEC - DPRINTLN("Attempting Trotec decode"); - if (decodeTrotec(results, offset)) return true; -#endif // DECODE_TROTEC -#if DECODE_TROTEC_3550 - DPRINTLN("Attempting Trotec 3550 decode"); - if (decodeTrotec3550(results, offset)) return true; -#endif // DECODE_TROTEC_3550 -#if DECODE_DAIKIN160 - DPRINTLN("Attempting Daikin160 decode"); - if (decodeDaikin160(results, offset)) return true; -#endif // DECODE_DAIKIN160 -#if DECODE_NEOCLIMA - DPRINTLN("Attempting Neoclima decode"); - if (decodeNeoclima(results, offset)) return true; -#endif // DECODE_NEOCLIMA -#if DECODE_DAIKIN176 - DPRINTLN("Attempting Daikin176 decode"); - if (decodeDaikin176(results, offset)) return true; -#endif // DECODE_DAIKIN176 -#if DECODE_DAIKIN128 - DPRINTLN("Attempting Daikin128 decode"); - if (decodeDaikin128(results, offset)) return true; -#endif // DECODE_DAIKIN128 -#if DECODE_AMCOR - DPRINTLN("Attempting Amcor decode"); - if (decodeAmcor(results, offset)) return true; -#endif // DECODE_AMCOR -#if DECODE_DAIKIN152 - DPRINTLN("Attempting Daikin152 decode"); - if (decodeDaikin152(results, offset)) return true; -#endif // DECODE_DAIKIN152 -#if DECODE_SYMPHONY - DPRINTLN("Attempting Symphony decode"); - if (decodeSymphony(results, offset)) return true; -#endif // DECODE_SYMPHONY -#if DECODE_DAIKIN64 - DPRINTLN("Attempting Daikin64 decode"); - if (decodeDaikin64(results, offset)) return true; -#endif // DECODE_DAIKIN64 -#if DECODE_AIRWELL - DPRINTLN("Attempting Airwell decode"); - if (decodeAirwell(results, offset)) return true; -#endif // DECODE_AIRWELL -#if DECODE_DELONGHI_AC - DPRINTLN("Attempting Delonghi AC decode"); - if (decodeDelonghiAc(results, offset)) return true; -#endif // DECODE_DELONGHI_AC -#if DECODE_DOSHISHA - DPRINTLN("Attempting Doshisha decode"); - if (decodeDoshisha(results, offset)) return true; -#endif // DECODE_DOSHISHA -#if DECODE_TRUMA - // Needs to happen before decodeMultibrackets() as they can appear similar. - DPRINTLN("Attempting Truma decode"); - if (decodeTruma(results, offset)) return true; -#endif // DECODE_TRUMA -#if DECODE_MULTIBRACKETS - DPRINTLN("Attempting Multibrackets decode"); - if (decodeMultibrackets(results, offset)) return true; -#endif // DECODE_MULTIBRACKETS -#if DECODE_CARRIER_AC40 - DPRINTLN("Attempting Carrier 40bit decode"); - if (decodeCarrierAC40(results, offset)) return true; -#endif // DECODE_CARRIER_AC40 -#if DECODE_CARRIER_AC64 - DPRINTLN("Attempting Carrier 64bit decode"); - if (decodeCarrierAC64(results, offset)) return true; -#endif // DECODE_CARRIER_AC64 -#if DECODE_TECHNIBEL_AC - DPRINTLN("Attempting Technibel AC decode"); - if (decodeTechnibelAc(results, offset)) return true; -#endif // DECODE_TECHNIBEL_AC -#if DECODE_CORONA_AC - DPRINTLN("Attempting CoronaAc decode"); - if (decodeCoronaAc(results, offset)) return true; -#endif // DECODE_CORONA_AC -#if DECODE_MIDEA24 - DPRINTLN("Attempting Midea-Nec decode"); - if (decodeMidea24(results, offset)) return true; -#endif // DECODE_MIDEA24 -#if DECODE_ZEPEAL - DPRINTLN("Attempting Zepeal decode"); - if (decodeZepeal(results, offset)) return true; -#endif // DECODE_ZEPEAL -#if DECODE_SANYO_AC - DPRINTLN("Attempting Sanyo AC decode"); - if (decodeSanyoAc(results, offset)) return true; -#endif // DECODE_SANYO_AC -#if DECODE_VOLTAS - DPRINTLN("Attempting Voltas decode"); - if (decodeVoltas(results)) return true; -#endif // DECODE_VOLTAS -#if DECODE_METZ - DPRINTLN("Attempting Metz decode"); - if (decodeMetz(results, offset)) return true; -#endif // DECODE_METZ -#if DECODE_TRANSCOLD - DPRINTLN("Attempting Transcold decode"); - if (decodeTranscold(results, offset)) return true; -#endif // DECODE_TRANSCOLD -#if DECODE_MIRAGE - DPRINTLN("Attempting Mirage decode"); - if (decodeMirage(results, offset)) return true; -#endif // DECODE_MIRAGE -#if DECODE_ELITESCREENS - DPRINTLN("Attempting EliteScreens decode"); - if (decodeElitescreens(results, offset)) return true; -#endif // DECODE_ELITESCREENS -#if DECODE_PANASONIC_AC32 - DPRINTLN("Attempting Panasonic AC (32bit) long decode"); - if (decodePanasonicAC32(results, offset, kPanasonicAc32Bits)) return true; - DPRINTLN("Attempting Panasonic AC (32bit) short decode"); - if (decodePanasonicAC32(results, offset, kPanasonicAc32Bits / 2)) - return true; -#endif // DECODE_PANASONIC_AC32 -#if DECODE_ECOCLIM - DPRINTLN("Attempting Ecoclim decode"); - if (decodeEcoclim(results, offset, kEcoclimBits) || - decodeEcoclim(results, offset, kEcoclimShortBits)) return true; -#endif // DECODE_ECOCLIM -#if DECODE_XMP - DPRINTLN("Attempting XMP decode"); - if (decodeXmp(results, offset, kXmpBits)) return true; -#endif // DECODE_XMP -#if DECODE_TEKNOPOINT - DPRINTLN("Attempting Teknopoint decode"); - if (decodeTeknopoint(results, offset)) return true; -#endif // DECODE_TEKNOPOINT -#if DECODE_KELON - DPRINTLN("Attempting Kelon decode"); - if (decodeKelon(results, offset)) return true; -#endif // DECODE_KELON -#if DECODE_SANYO_AC88 - DPRINTLN("Attempting SanyoAc88 decode"); - if (decodeSanyoAc88(results, offset)) return true; -#endif // DECODE_SANYO_AC88 -#if DECODE_BOSE - DPRINTLN("Attempting Bose decode"); - if (decodeBose(results, offset)) return true; -#endif // DECODE_BOSE -#if DECODE_ARRIS - DPRINTLN("Attempting Arris decode"); - if (decodeArris(results, offset)) return true; -#endif // DECODE_ARRIS -#if DECODE_RHOSS - DPRINTLN("Attempting Rhoss decode"); - if (decodeRhoss(results, offset)) return true; -#endif // DECODE_RHOSS - // Typically new protocols are added above this line. - } -#if DECODE_HASH - // decodeHash returns a hash on any input. - // Thus, it needs to be last in the list. - // If you add any decodes, add them before this. - if (decodeHash(results)) { - return true; - } -#endif // DECODE_HASH - // Throw away and start over - if (!resumed) // Check if we have already resumed. - resume(); - return false; -} - -/// Convert the tolerance percentage into something valid. -/// @param[in] percentage An integer percentage. -uint8_t IRrecv::_validTolerance(const uint8_t percentage) { - return (percentage > 100) ? _tolerance : percentage; -} - -/// Calculate the lower bound of the nr. of ticks. -/// @param[in] usecs Nr. of uSeconds. -/// @param[in] tolerance Percent as an integer. e.g. 10 is 10% -/// @param[in] delta A non-scaling amount to reduce usecs by. -/// @return Nr. of ticks. -uint32_t IRrecv::ticksLow(const uint32_t usecs, const uint8_t tolerance, - const uint16_t delta) { - // max() used to ensure the result can't drop below 0 before the cast. - return ((uint32_t)std::max( - (int32_t)(usecs * (1.0 - _validTolerance(tolerance) / 100.0) - delta), - 0)); -} - -/// Calculate the upper bound of the nr. of ticks. -/// @param[in] usecs Nr. of uSeconds. -/// @param[in] tolerance Percent as an integer. e.g. 10 is 10% -/// @param[in] delta A non-scaling amount to increase usecs by. -/// @return Nr. of ticks. -uint32_t IRrecv::ticksHigh(const uint32_t usecs, const uint8_t tolerance, - const uint16_t delta) { - return ((uint32_t)(usecs * (1.0 + _validTolerance(tolerance) / 100.0)) + 1 + - delta); -} - -/// Check if we match a pulse(measured) with the desired within -/// +/-tolerance percent and/or +/- a fixed delta range. -/// @param[in] measured The recorded period of the signal pulse. -/// @param[in] desired The expected period (in usecs) we are matching against. -/// @param[in] tolerance A percentage expressed as an integer. e.g. 10 is 10%. -/// @param[in] delta A non-scaling (+/-) error margin (in useconds). -/// @return A Boolean. true if it matches, false if it doesn't. -bool IRrecv::match(uint32_t measured, uint32_t desired, uint8_t tolerance, - uint16_t delta) { - measured *= kRawTick; // Convert to uSecs. - DPRINT("Matching: "); - DPRINT(ticksLow(desired, tolerance, delta)); - DPRINT(" <= "); - DPRINT(measured); - DPRINT(" <= "); - DPRINTLN(ticksHigh(desired, tolerance, delta)); -#ifdef UNIT_TEST - // Sanity checks that we don't have values that cause integer over/underflow. - // Only performed during testing so there is no performance hit in normal - // operation. - assert(ticksLow(desired, tolerance, delta) <= desired); - // Check if we overflowed. (UINT32_MAX >> 3 is approx 9 minutes!) - assert(ticksHigh(desired, tolerance, delta) < UINT32_MAX >> 3); - // Check if our high mark is below where we started. This could happen. - // If there is a legit case, then this should be removed. - assert(ticksHigh(desired, tolerance, delta) >= desired); -#endif // UNIT_TEST - return (measured >= ticksLow(desired, tolerance, delta) && - measured <= ticksHigh(desired, tolerance, delta)); -} - -/// Check if we match a pulse(measured) of at least desired within -/// tolerance percent and/or a fixed delta margin. -/// @param[in] measured The recorded period of the signal pulse. -/// @param[in] desired The expected period (in usecs) we are matching against. -/// @param[in] tolerance A percentage expressed as an integer. e.g. 10 is 10%. -/// @param[in] delta A non-scaling amount to reduce usecs by. -/// @return A Boolean. true if it matches, false if it doesn't. -bool IRrecv::matchAtLeast(uint32_t measured, uint32_t desired, - uint8_t tolerance, uint16_t delta) { - measured *= kRawTick; // Convert to uSecs. - DPRINT("Matching ATLEAST "); - DPRINT(measured); - DPRINT(" vs "); - DPRINT(desired); - DPRINT(". Matching: "); - DPRINT(measured); - DPRINT(" >= "); - DPRINT(ticksLow(std::min(desired, MS_TO_USEC(params.timeout)), tolerance, - delta)); - DPRINT(" [min("); - DPRINT(ticksLow(desired, tolerance, delta)); - DPRINT(", "); - DPRINT(ticksLow(MS_TO_USEC(params.timeout), tolerance, delta)); - DPRINTLN(")]"); -#ifdef UNIT_TEST - // Sanity checks that we don't have values that cause integer over/underflow. - // Only performed during testing so there is no performance hit in normal - // operation. - assert(ticksLow(desired, tolerance, delta) <= desired); - // Check if we overflowed. (UINT32_MAX >> 3 is approx 9 minutes!) - assert(ticksHigh(desired, tolerance, delta) < UINT32_MAX >> 3); - // Check if our high mark is below where we started. This could happen. - // If there is a legit case, then this should be removed. - assert(ticksHigh(desired, tolerance, delta) >= desired); -#endif // UNIT_TEST - // We really should never get a value of 0, except as the last value - // in the buffer. If that is the case, then assume infinity and return true. - if (measured == 0) return true; - return measured >= ticksLow(std::min(desired, MS_TO_USEC(params.timeout)), - tolerance, delta); -} - -/// Check if we match a mark signal(measured) with the desired within -/// +/-tolerance percent, after an expected is excess is added. -/// @param[in] measured The recorded period of the signal pulse. -/// @param[in] desired The expected period (in usecs) we are matching against. -/// @param[in] tolerance A percentage expressed as an integer. e.g. 10 is 10%. -/// @param[in] excess A non-scaling amount to reduce usecs by. -/// @return A Boolean. true if it matches, false if it doesn't. -bool IRrecv::matchMark(uint32_t measured, uint32_t desired, uint8_t tolerance, - int16_t excess) { - DPRINT("Matching MARK "); - DPRINT(measured * kRawTick); - DPRINT(" vs "); - DPRINT(desired); - DPRINT(" + "); - DPRINT(excess); - DPRINT(". "); - return match(measured, desired + excess, tolerance); -} - -/// Check if we match a mark signal(measured) with the desired within a -/// range (in uSeconds) either side of the desired, after an expected is excess -/// is added. -/// @param[in] measured The recorded period of the signal pulse. -/// @param[in] desired The expected period (in usecs) we are matching against. -/// @param[in] range The range limit from desired to accept in uSeconds. -/// @param[in] excess A non-scaling amount to reduce usecs by. -/// @return A Boolean. true if it matches, false if it doesn't. -bool IRrecv::matchMarkRange(const uint32_t measured, const uint32_t desired, - const uint16_t range, const int16_t excess) { - DPRINT("Matching MARK "); - DPRINT(measured * kRawTick); - DPRINT(" vs "); - DPRINT(desired); - DPRINT(" + "); - DPRINT(excess); - DPRINT(". "); - return match(measured, desired + excess, 0, range); -} - -/// Check if we match a space signal(measured) with the desired within -/// +/-tolerance percent, after an expected is excess is removed. -/// @param[in] measured The recorded period of the signal pulse. -/// @param[in] desired The expected period (in usecs) we are matching against. -/// @param[in] tolerance A percentage expressed as an integer. e.g. 10 is 10%. -/// @param[in] excess A non-scaling amount to reduce usecs by. -/// @return A Boolean. true if it matches, false if it doesn't. -bool IRrecv::matchSpace(uint32_t measured, uint32_t desired, uint8_t tolerance, - int16_t excess) { - DPRINT("Matching SPACE "); - DPRINT(measured * kRawTick); - DPRINT(" vs "); - DPRINT(desired); - DPRINT(" - "); - DPRINT(excess); - DPRINT(". "); - return match(measured, desired - excess, tolerance); -} - -/// Check if we match a space signal(measured) with the desired within a -/// range (in uSeconds) either side of the desired, after an expected is excess -/// is removed. -/// @param[in] measured The recorded period of the signal pulse. -/// @param[in] desired The expected period (in usecs) we are matching against. -/// @param[in] range The range limit from desired to accept in uSeconds. -/// @param[in] excess A non-scaling amount to reduce usecs by. -/// @return A Boolean. true if it matches, false if it doesn't. -bool IRrecv::matchSpaceRange(const uint32_t measured, const uint32_t desired, - const uint16_t range, const int16_t excess) { - DPRINT("Matching SPACE "); - DPRINT(measured * kRawTick); - DPRINT(" vs "); - DPRINT(desired); - DPRINT(" - "); - DPRINT(excess); - DPRINT(". "); - return match(measured, desired - excess, 0, range); -} - -#if DECODE_HASH -/// Compare two tick values. -/// @param[in] oldval Nr. of ticks. -/// @param[in] newval Nr. of ticks. -/// @return 0 if newval is shorter, 1 if it is equal, & 2 if it is longer. -/// @note Use a tolerance of 20% -uint16_t IRrecv::compare(const uint16_t oldval, const uint16_t newval) { - if (newval < oldval * 0.8) - return 0; - else if (oldval < newval * 0.8) - return 2; - else - return 1; -} - -/// Decode any arbitrary IR message into a 32-bit code value. -/// Instead of decoding using a standard encoding scheme -/// (e.g. Sony, NEC, RC5), the code is hashed to a 32-bit value. -/// -/// The algorithm: look at the sequence of MARK signals, and see if each one -/// is shorter (0), the same length (1), or longer (2) than the previous. -/// Do the same with the SPACE signals. Hash the resulting sequence of 0's, -/// 1's, and 2's to a 32-bit value. This will give a unique value for each -/// different code (probably), for most code systems. -/// @see http://arcfn.com/2010/01/using-arbitrary-remotes-with-arduino.html -/// @note This isn't a "real" decoding, just an arbitrary value. -/// Hopefully this code is unique for each button. -bool IRrecv::decodeHash(decode_results *results) { - // Require at least some samples to prevent triggering on noise - if (results->rawlen < _unknown_threshold) return false; - int32_t hash = kFnvBasis32; - // 'rawlen - 2' to avoid the look ahead from going out of bounds. - // Should probably be -3 to avoid comparing the trailing space entry, - // however it is left this way for compatibility with previously captured - // values. - for (uint16_t i = 1; i < results->rawlen - 2; i++) { - uint16_t value = compare(results->rawbuf[i], results->rawbuf[i + 2]); - // Add value into the hash - hash = (hash * kFnvPrime32) ^ value; - } - results->value = hash & 0xFFFFFFFF; - results->bits = results->rawlen / 2; - results->address = 0; - results->command = 0; - results->decode_type = UNKNOWN; - return true; -} -#endif // DECODE_HASH - -/// Match & decode the typical data section of an IR message. -/// The data value is stored in the least significant bits reguardless of the -/// bit ordering requested. -/// @param[in] data_ptr A pointer to where we are at in the capture buffer. -/// @param[in] nbits Nr. of data bits we expect. -/// @param[in] onemark Nr. of uSeconds in an expected mark signal for a '1' bit. -/// @param[in] onespace Nr. of uSecs in an expected space signal for a '1' bit. -/// @param[in] zeromark Nr. of uSecs in an expected mark signal for a '0' bit. -/// @param[in] zerospace Nr. of uSecs in an expected space signal for a '0' bit. -/// @param[in] tolerance Percentage error margin to allow. (Default: kUseDefTol) -/// @param[in] excess Nr. of uSeconds. (Def: kMarkExcess) -/// @param[in] MSBfirst Bit order to save the data in. (Def: true) -/// true is Most Significant Bit First Order, false is Least Significant First -/// @param[in] expectlastspace Do we expect a space at the end of the message? -/// @return A match_result_t structure containing the success (or not), the -/// data value, and how many buffer entries were used. -match_result_t IRrecv::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, const int16_t excess, const bool MSBfirst, - const bool expectlastspace) { - match_result_t result; - result.success = false; // Fail by default. - result.data = 0; - if (expectlastspace) { // We are expecting data with a final space. - for (result.used = 0; result.used < nbits * 2; - result.used += 2, data_ptr += 2) { - // Is the bit a '1'? - if (matchMark(*data_ptr, onemark, tolerance, excess) && - matchSpace(*(data_ptr + 1), onespace, tolerance, excess)) { - result.data = (result.data << 1) | 1; - } else if (matchMark(*data_ptr, zeromark, tolerance, excess) && - matchSpace(*(data_ptr + 1), zerospace, tolerance, excess)) { - result.data <<= 1; // The bit is a '0'. - } else { - if (!MSBfirst) result.data = reverseBits(result.data, result.used / 2); - return result; // It's neither, so fail. - } - } - result.success = true; - } else { // We are expecting data without a final space. - // Match all but the last bit, as it may not match easily. - result = matchData(data_ptr, nbits ? nbits - 1 : 0, onemark, onespace, - zeromark, zerospace, tolerance, excess, true, true); - if (result.success) { - // Is the bit a '1'? - if (matchMark(*(data_ptr + result.used), onemark, tolerance, excess)) - result.data = (result.data << 1) | 1; - else if (matchMark(*(data_ptr + result.used), zeromark, tolerance, - excess)) - result.data <<= 1; // The bit is a '0'. - else - result.success = false; - if (result.success) result.used++; - } - } - if (!MSBfirst) result.data = reverseBits(result.data, nbits); - return result; -} - -/// Match & decode the typical data section of an IR message. -/// The bytes are stored at result_ptr. The first byte in the result equates to -/// the first byte encountered, and so on. -/// @param[in] data_ptr A pointer to where we are at in the capture buffer. -/// @param[out] result_ptr A ptr to where to start storing the bytes we decoded. -/// @param[in] remaining The size of the capture buffer remaining. -/// @param[in] nbytes Nr. of data bytes we expect. -/// @param[in] onemark Nr. of uSeconds in an expected mark signal for a '1' bit. -/// @param[in] onespace Nr. of uSecs in an expected space signal for a '1' bit. -/// @param[in] zeromark Nr. of uSecs in an expected mark signal for a '0' bit. -/// @param[in] zerospace Nr. of uSecs in an expected space signal for a '0' bit. -/// @param[in] tolerance Percentage error margin to allow. (Default: kUseDefTol) -/// @param[in] excess Nr. of uSeconds. (Def: kMarkExcess) -/// @param[in] MSBfirst Bit order to save the data in. (Def: true) -/// true is Most Significant Bit First Order, false is Least Significant First -/// @param[in] expectlastspace Do we expect a space at the end of the message? -/// @return If successful, how many buffer entries were used. Otherwise 0. -uint16_t IRrecv::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, const int16_t excess, - const bool MSBfirst, const bool expectlastspace) { - // Check if there is enough capture buffer to possibly have the desired bytes. - if (remaining + expectlastspace < (nbytes * 8 * 2) + 1) - return 0; // Nope, so abort. - uint16_t offset = 0; - for (uint16_t byte_pos = 0; byte_pos < nbytes; byte_pos++) { - bool lastspace = (byte_pos + 1 == nbytes) ? expectlastspace : true; - match_result_t result = matchData(data_ptr + offset, 8, onemark, onespace, - zeromark, zerospace, tolerance, excess, - MSBfirst, lastspace); - if (result.success == false) return 0; // Fail - result_ptr[byte_pos] = (uint8_t)result.data; - offset += result.used; - } - return offset; -} - -/// Match & decode a generic/typical IR message. -/// The data is stored in result_bits_ptr or result_bytes_ptr depending on flag -/// `use_bits`. -/// @note Values of 0 for hdrmark, hdrspace, footermark, or footerspace mean -/// skip that requirement. -/// -/// @param[in] data_ptr A pointer to where we are at in the capture buffer. -/// @param[out] result_bits_ptr A pointer to where to start storing the bits we -/// decoded. -/// @param[out] result_bytes_ptr A pointer to where to start storing the bytes -/// we decoded. -/// @param[in] use_bits A flag indicating if we are to decode bits or bytes. -/// @param[in] remaining The size of the capture buffer remaining. -/// @param[in] nbits Nr. of data bits we expect. -/// @param[in] hdrmark Nr. of uSeconds for the expected header mark signal. -/// @param[in] hdrspace Nr. of uSeconds for the expected header space signal. -/// @param[in] onemark Nr. of uSeconds in an expected mark signal for a '1' bit. -/// @param[in] onespace Nr. of uSecs in an expected space signal for a '1' bit. -/// @param[in] zeromark Nr. of uSecs in an expected mark signal for a '0' bit. -/// @param[in] zerospace Nr. of uSecs in an expected space signal for a '0' bit. -/// @param[in] footermark Nr. of uSeconds for the expected footer mark signal. -/// @param[in] footerspace Nr. of uSeconds for the expected footer space/gap -/// signal. -/// @param[in] atleast Is the match on the footerspace a matchAtLeast or -/// matchSpace? -/// @param[in] tolerance Percentage error margin to allow. (Default: kUseDefTol) -/// @param[in] excess Nr. of uSeconds. (Def: kMarkExcess) -/// @param[in] MSBfirst Bit order to save the data in. (Def: true) -/// true is Most Significant Bit First Order, false is Least Significant First -/// @return If successful, how many buffer entries were used. Otherwise 0. -uint16_t IRrecv::_matchGeneric(volatile uint16_t *data_ptr, - uint64_t *result_bits_ptr, - uint8_t *result_bytes_ptr, - const bool use_bits, - 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, - const uint8_t tolerance, - const int16_t excess, - const bool MSBfirst) { - // If we are expecting byte sizes, check it's a factor of 8 or fail. - if (!use_bits && nbits % 8 != 0) return 0; - // Calculate if we expect a trailing space in the data section. - const bool kexpectspace = footermark || (onespace != zerospace); - // Calculate how much remaining buffer is required. - uint16_t min_remaining = nbits * 2 - (kexpectspace ? 0 : 1); - - if (hdrmark) min_remaining++; - if (hdrspace) min_remaining++; - if (footermark) min_remaining++; - // Don't need to extend for footerspace because it could be the end of message - - // Check if there is enough capture buffer to possibly have the message. - if (remaining < min_remaining) return 0; // Nope, so abort. - uint16_t offset = 0; - - // Header - if (hdrmark && !matchMark(*(data_ptr + offset++), hdrmark, tolerance, excess)) - return 0; - if (hdrspace && !matchSpace(*(data_ptr + offset++), hdrspace, tolerance, - excess)) - return 0; - - // Data - if (use_bits) { // Bits. - match_result_t result = IRrecv::matchData(data_ptr + offset, nbits, - onemark, onespace, - zeromark, zerospace, tolerance, - excess, MSBfirst, kexpectspace); - if (!result.success) return 0; - *result_bits_ptr = result.data; - offset += result.used; - } else { // bytes - uint16_t data_used = IRrecv::matchBytes(data_ptr + offset, result_bytes_ptr, - remaining - offset, nbits / 8, - onemark, onespace, - zeromark, zerospace, tolerance, - excess, MSBfirst, kexpectspace); - if (!data_used) return 0; - offset += data_used; - } - // Footer - if (footermark && !matchMark(*(data_ptr + offset++), footermark, tolerance, - excess)) - return 0; - // If we have something still to match & haven't reached the end of the buffer - if (footerspace && offset < remaining) { - if (atleast) { - if (!matchAtLeast(*(data_ptr + offset), footerspace, tolerance, excess)) - return 0; - } else { - if (!matchSpace(*(data_ptr + offset), footerspace, tolerance, excess)) - return 0; - } - offset++; - } - return offset; -} - -/// Match & decode a generic/typical <= 64bit IR message. -/// The data is stored at result_ptr. -/// @note Values of 0 for hdrmark, hdrspace, footermark, or footerspace mean -/// skip that requirement. -/// -/// @param[in] data_ptr: A pointer to where we are at in the capture buffer. -/// @param[out] result_ptr A ptr to where to start storing the bits we decoded. -/// @param[in] remaining The size of the capture buffer remaining. -/// @param[in] nbits Nr. of data bits we expect. -/// @param[in] hdrmark Nr. of uSeconds for the expected header mark signal. -/// @param[in] hdrspace Nr. of uSeconds for the expected header space signal. -/// @param[in] onemark Nr. of uSeconds in an expected mark signal for a '1' bit. -/// @param[in] onespace Nr. of uSecs in an expected space signal for a '1' bit. -/// @param[in] zeromark Nr. of uSecs in an expected mark signal for a '0' bit. -/// @param[in] zerospace Nr. of uSecs in an expected space signal for a '0' bit. -/// @param[in] footermark Nr. of uSeconds for the expected footer mark signal. -/// @param[in] footerspace Nr. of uSeconds for the expected footer space/gap -/// signal. -/// @param[in] atleast Is the match on the footerspace a matchAtLeast or -/// matchSpace? -/// @param[in] tolerance Percentage error margin to allow. (Default: kUseDefTol) -/// @param[in] excess Nr. of uSeconds. (Def: kMarkExcess) -/// @param[in] MSBfirst Bit order to save the data in. (Def: true) -/// true is Most Significant Bit First Order, false is Least Significant First -/// @return If successful, how many buffer entries were used. Otherwise 0. -uint16_t IRrecv::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, - const uint8_t tolerance, - const int16_t excess, - const bool MSBfirst) { - return _matchGeneric(data_ptr, result_ptr, NULL, true, remaining, nbits, - hdrmark, hdrspace, onemark, onespace, - zeromark, zerospace, footermark, footerspace, atleast, - tolerance, excess, MSBfirst); -} - -/// Match & decode a generic/typical > 64bit IR message. -/// The bytes are stored at result_ptr. The first byte in the result equates to -/// the first byte encountered, and so on. -/// @note Values of 0 for hdrmark, hdrspace, footermark, or footerspace mean -/// skip that requirement. -/// @param[in] data_ptr: A pointer to where we are at in the capture buffer. -/// @param[out] result_ptr A ptr to where to start storing the bytes we decoded. -/// @param[in] remaining The size of the capture buffer remaining. -/// @param[in] nbits Nr. of data bits we expect. -/// @param[in] hdrmark Nr. of uSeconds for the expected header mark signal. -/// @param[in] hdrspace Nr. of uSeconds for the expected header space signal. -/// @param[in] onemark Nr. of uSeconds in an expected mark signal for a '1' bit. -/// @param[in] onespace Nr. of uSecs in an expected space signal for a '1' bit. -/// @param[in] zeromark Nr. of uSecs in an expected mark signal for a '0' bit. -/// @param[in] zerospace Nr. of uSecs in an expected space signal for a '0' bit. -/// @param[in] footermark Nr. of uSeconds for the expected footer mark signal. -/// @param[in] footerspace Nr. of uSeconds for the expected footer space/gap -/// signal. -/// @param[in] atleast Is the match on the footerspace a matchAtLeast or -/// matchSpace? -/// @param[in] tolerance Percentage error margin to allow. (Default: kUseDefTol) -/// @param[in] excess Nr. of uSeconds. (Def: kMarkExcess) -/// @param[in] MSBfirst Bit order to save the data in. (Def: true) -/// true is Most Significant Bit First Order, false is Least Significant First -/// @return If successful, how many buffer entries were used. Otherwise 0. -uint16_t IRrecv::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, - const uint8_t tolerance, - const int16_t excess, - const bool MSBfirst) { - return _matchGeneric(data_ptr, NULL, result_ptr, false, remaining, nbits, - hdrmark, hdrspace, onemark, onespace, - zeromark, zerospace, footermark, footerspace, atleast, - tolerance, excess, MSBfirst); -} - -/// Match & decode a generic/typical constant bit time <= 64bit IR message. -/// The data is stored at result_ptr. -/// @note Values of 0 for hdrmark, hdrspace, footermark, or footerspace mean -/// skip that requirement. -/// @param[in] data_ptr A pointer to where we are at in the capture buffer. -/// @note `data_ptr` is assumed to be pointing to a "Mark", not a "Space". -/// @param[out] result_ptr A ptr to where to start storing the bits we decoded. -/// @param[in] remaining The size of the capture buffer remaining. -/// @param[in] nbits Nr. of data bits we expect. -/// @param[in] hdrmark Nr. of uSeconds for the expected header mark signal. -/// @param[in] hdrspace Nr. of uSeconds for the expected header space signal. -/// @param[in] one Nr. of uSeconds in an expected mark signal for a '1' bit. -/// @param[in] zero Nr. of uSeconds in an expected mark signal for a '0' bit. -/// @param[in] footermark Nr. of uSeconds for the expected footer mark signal. -/// @param[in] footerspace Nr. of uSeconds for the expected footer space/gap -/// signal. -/// @param[in] atleast Is the match on the footerspace a matchAtLeast or -/// matchSpace? -/// @param[in] tolerance Percentage error margin to allow. (Default: kUseDefTol) -/// @param[in] excess Nr. of uSeconds. (Def: kMarkExcess) -/// @param[in] MSBfirst Bit order to save the data in. (Def: true) -/// true is Most Significant Bit First Order, false is Least Significant First -/// @return If successful, how many buffer entries were used. Otherwise 0. -/// @note Parameters one + zero add up to the total time for a bit. -/// e.g. mark(one) + space(zero) is a `1`, mark(zero) + space(one) is a `0`. -uint16_t IRrecv::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, - const uint8_t tolerance, - const int16_t excess, - const bool MSBfirst) { - uint16_t offset = 0; - uint64_t result = 0; - // If we expect a footermark, then this can be processed like normal. - if (footermark) - return _matchGeneric(data_ptr, result_ptr, NULL, true, remaining, nbits, - hdrmark, hdrspace, one, zero, zero, one, - footermark, footerspace, atleast, - tolerance, excess, MSBfirst); - // Overwise handle like normal, except for the last bit. and no footer. - uint16_t bits = (nbits > 0) ? nbits - 1 : 0; // Make sure we don't underflow. - offset = _matchGeneric(data_ptr, &result, NULL, true, remaining, bits, - hdrmark, hdrspace, one, zero, zero, one, 0, 0, false, - tolerance, excess, true); - if (!offset) return 0; // Didn't match. - // Now for the last bit. - if (remaining <= offset) return 0; // Not enough buffer. - result <<= 1; - bool last_bit = 0; - // Is the mark a '1' or a `0`? - if (matchMark(*(data_ptr + offset), one, tolerance, excess)) { // 1 - last_bit = 1; - result |= 1; - } else if (matchMark(*(data_ptr + offset), zero, tolerance, excess)) { // 0 - last_bit = 0; - } else { - return 0; // It's neither, so fail. - } - offset++; - uint32_t expected_space = (last_bit ? zero : one) + footerspace; - // If we are not at the end of the buffer, check for at least the expected - // space value. - if (remaining > offset) { - if (atleast) { - if (!matchAtLeast(*(data_ptr + offset), expected_space, tolerance, - excess)) - return false; - } else { - if (!matchSpace(*(data_ptr + offset), expected_space, tolerance)) - return false; - } - offset++; - } - if (!MSBfirst) result = reverseBits(result, nbits); - *result_ptr = result; - return offset; -} - -/// Match & decode a Manchester Code <= 64bit IR message. -/// The data is stored at result_ptr. -/// @note Values of 0 for hdrmark, hdrspace, footermark, or footerspace mean -/// skip that requirement. -/// @param[in] data_ptr A pointer to where we are at in the capture buffer. -/// @note `data_ptr` is assumed to be pointing to a "Mark", not a "Space". -/// @param[out] result_ptr A ptr to where to start storing the bits we decoded. -/// @param[in] remaining The size of the capture buffer remaining. -/// @param[in] nbits Nr. of data bits we expect. -/// @param[in] hdrmark Nr. of uSeconds for the expected header mark signal. -/// @param[in] hdrspace Nr. of uSeconds for the expected header space signal. -/// @param[in] half_period Nr. of uSeconds for half the clock's period. -/// i.e. 1/2 wavelength -/// @param[in] footermark Nr. of uSeconds for the expected footer mark signal. -/// @param[in] footerspace Nr. of uSeconds for the expected footer space/gap -/// signal. -/// @param[in] atleast Is the match on the footerspace a matchAtLeast or -/// matchSpace? -/// @param[in] tolerance Percentage error margin to allow. (Default: kUseDefTol) -/// @param[in] excess Nr. of uSeconds. (Def: kMarkExcess) -/// @param[in] MSBfirst Bit order to save the data in. (Def: true) -/// true is Most Significant Bit First Order, false is Least Significant First -/// @param[in] GEThomas Use G.E. Thomas (true) or IEEE 802.3 (false) convention? -/// @return If successful, how many buffer entries were used. Otherwise 0. -/// @see https://en.wikipedia.org/wiki/Manchester_code -/// @see http://ww1.microchip.com/downloads/en/AppNotes/Atmel-9164-Manchester-Coding-Basics_Application-Note.pdf -uint16_t IRrecv::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 half_period, - const uint16_t footermark, - const uint32_t footerspace, - const bool atleast, - const uint8_t tolerance, - const int16_t excess, - const bool MSBfirst, - const bool GEThomas) { - uint16_t offset = 0; - uint16_t bank = 0; - uint16_t entry = 0; - - // Calculate how much remaining buffer is required. - // Shortest case is nbits. Longest case is 2 * nbits. - uint16_t min_remaining = nbits; - - if (hdrmark) min_remaining++; - if (hdrspace) min_remaining++; - if (footermark) min_remaining++; - // Don't need to extend for footerspace because it could be the end of message - - // Check if there is enough capture buffer to possibly have the message. - if (remaining < min_remaining) return 0; // Nope, so abort. - - // Header - if (hdrmark) { - entry = *(data_ptr + offset++); - if (!hdrspace) { // If we have no Header Space ... - // Do we have a data 'mark' half period merged with the header mark? - if (matchMark(entry, hdrmark + half_period, - tolerance, excess)) { - // Looks like we do. - bank = entry * kRawTick - hdrmark; - } else if (!matchMark(entry, hdrmark, tolerance, excess)) { - return 0; // It's not a normal header mark, so fail. - } - } else if (!matchMark(entry, hdrmark, tolerance, excess)) { - return 0; // It's not a normal header mark, so fail. - } - } - if (hdrspace) { - entry = *(data_ptr + offset++); - // Check to see if the header space has merged with a data space half period - if (matchSpace(entry, hdrspace + half_period, tolerance, excess)) { - // Looks like we do. - bank = entry * kRawTick - hdrspace; - } else if (!matchSpace(entry, hdrspace, tolerance, excess)) { - return 0; // It's not a normal header space, so fail. - } - } - - if (!match(bank / kRawTick, half_period, tolerance, excess)) bank = 0; - // Data - uint16_t used = matchManchesterData(data_ptr + offset, result_ptr, - remaining - offset, nbits, half_period, - bank, tolerance, excess, MSBfirst, - GEThomas); - if (!used) return 0; // Data did match. - offset += used; - // Footer - if (footermark && - !(matchMark(*(data_ptr + offset), footermark + half_period, - tolerance, excess) || - matchMark(*(data_ptr + offset), footermark, tolerance, excess))) - return 0; - offset++; - // If we have something still to match & haven't reached the end of the buffer - if (footerspace && offset < remaining) { - if (atleast) { - if (!matchAtLeast(*(data_ptr + offset), footerspace, tolerance, excess)) - return 0; - } else { - if (!matchSpace(*(data_ptr + offset), footerspace, tolerance, excess) && - !matchSpace(*(data_ptr + offset), footerspace + half_period, - tolerance, excess)) - return 0; - } - offset++; - } - return offset; -} - -/// Match & decode a Manchester Code data (<= 64bits. -/// @param[in] data_ptr A pointer to where we are at in the capture buffer. -/// @note `data_ptr` is assumed to be pointing to a "Mark", not a "Space". -/// @param[out] result_ptr A ptr to where to start storing the bits we decoded. -/// @param[in] remaining The size of the capture buffer remaining. -/// @param[in] nbits Nr. of data bits we expect. -/// @param[in] half_period Nr. of uSeconds for half the clock's period. -/// i.e. 1/2 wavelength -/// @param[in] tolerance Percentage error margin to allow. (Default: kUseDefTol) -/// @param[in] starting_balance Amount of uSeconds to assume exists prior to -/// the current value pointed too. -/// @param[in] excess Nr. of uSeconds. (Def: kMarkExcess) -/// @param[in] MSBfirst Bit order to save the data in. (Def: true) -/// true is Most Significant Bit First Order, false is Least Significant First -/// @param[in] GEThomas Use G.E. Thomas (true) or IEEE 802.3 (false) convention? -/// @return If successful, how many buffer entries were used. Otherwise 0. -/// @see https://en.wikipedia.org/wiki/Manchester_code -/// @see http://ww1.microchip.com/downloads/en/AppNotes/Atmel-9164-Manchester-Coding-Basics_Application-Note.pdf -/// @todo Clean up and optimise this. It is just "get it working code" atm. -uint16_t IRrecv::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, - const uint8_t tolerance, - const int16_t excess, - const bool MSBfirst, - const bool GEThomas) { - DPRINTLN("DEBUG: Entered matchManchesterData"); - uint16_t offset = 0; - uint64_t data = 0; - uint16_t nr_half_periods = 0; - const uint16_t expected_half_periods = nbits * 2; - // Flip the bit if we have a starting balance. ie. Carry over from the header. - bool currentBit = starting_balance ? !GEThomas : GEThomas; - const uint16_t raw_half_period = half_period / kRawTick; - - // Calculate how much remaining buffer is required. - // Shortest case is nbits. Longest case is 2 * nbits. - uint16_t min_remaining = nbits; - - // Check if there is enough capture buffer to possibly have the message. - if (remaining < min_remaining) { - DPRINTLN("DEBUG: Ran out of capture buffer!"); - return 0; // Nope, so abort. - } - - // Convert to ticks. Optimisation: Saves on math/extra instructions later. - uint16_t bank = starting_balance / kRawTick; - - // Data - // Loop through the buffer till we run out of buffer, or nr of half periods. - // Possible patterns are: - // short + short = 1 bit (Add the value of the previous bit again) - // short + long + short = 2 bits (Add the previous bit again, then flip & add) - // short + long + long + short = 3 bits (add prev, flip & add, flip & add) - // We can't start with a long. - // - // The general approach is thus: - // Check we have a short interval, next or in the bank. - // If the next timing value is long, act according and reset the bank to - // a short balance. - // or - // If it is short, act accordingly and declare the bank empty. - // Repeat. - while ((offset < remaining || bank) && - nr_half_periods < expected_half_periods) { - // Get the next entry if we haven't anything existing to process. - DPRINT("DEBUG: Offset = "); - DPRINTLN(offset); - if (!bank) bank = *(data_ptr + offset++); - DPRINT("DEBUG: Bank = "); - DPRINTLN(bank * kRawTick); - // Check if we don't have a short interval. - DPRINTLN("DEBUG: Checking for short interval"); - if (!match(bank, half_period, tolerance, excess)) { - DPRINTLN("DEBUG: It is. Exiting"); - return 0; // Not valid. - } - // We've succeeded in matching half a period, so count it. - nr_half_periods++; - DPRINT("DEBUG: Half Periods = "); - DPRINTLN(nr_half_periods); - // We've now used up our bank, so refill it with the next item, unless we - // are at the end of the capture buffer. - // If we are assume a single half period of "space". - if (offset < remaining) { - DPRINT("DEBUG: Offset = "); - DPRINTLN(offset); - bank = *(data_ptr + offset++); - } else if (offset == remaining) { - bank = raw_half_period; - } else { - return 0; // We are out of buffer, so abort! - } - DPRINT("DEBUG: Bank = "); - DPRINTLN(bank * kRawTick); - - // Shift the data along and add our new bit. - DPRINT("DEBUG: Adding bit: "); - DPRINTLN((currentBit ? "1" : "0")); - data <<= 1; - data |= currentBit; - - // Check if we have a long interval. - if (match(bank, half_period * 2, tolerance, excess)) { - // It is, so flip the bit we need to append, and remove a half_period of - // time from the bank. - DPRINTLN("DEBUG: long interval detected"); - currentBit = !currentBit; - bank -= raw_half_period; - } else if (match(bank, half_period, tolerance, excess)) { - // It is a short interval, so eat up all the time and move on. - DPRINTLN("DEBUG: short interval detected"); - bank = 0; - } else if (nr_half_periods == expected_half_periods - 1 && - matchAtLeast(bank, half_period, tolerance, excess)) { - // We are at the end of the data & it is a short interval, so eat up all - // the time and move on. - bank = 0; - // Reduce the offset as we are at the end of the data doing a - // matchAtLeast() because we could be processing part of a footer. - offset--; - } else { - // The length isn't what we expected (neither long or short), so bail. - return 0; - } - nr_half_periods++; - } - - // Clean up and process the data. - if (!MSBfirst) data = reverseBits(data, nbits); - // Trim the data to size. - *result_ptr = GETBITS64(data, 0, nbits); - return offset; -} - -#if UNIT_TEST -/// Unit test helper to get access to the params structure. -volatile irparams_t *IRrecv::_getParamsPtr(void) { - return ¶ms; -} -#endif // UNIT_TEST -// End of IRrecv class ------------------- diff --git a/lib/IRremoteESP8266/src/IRrecv.h b/lib/IRremoteESP8266/src/IRrecv.h index 13aadb6574..a5c2eb30de 100644 --- a/lib/IRremoteESP8266/src/IRrecv.h +++ b/lib/IRremoteESP8266/src/IRrecv.h @@ -12,7 +12,7 @@ #include #define __STDC_LIMIT_MACROS #include -#include "IRremoteESP8266.h" +#include "../src/IRremoteESP8266.h" // Constants const uint16_t kHeader = 2; // Usual nr. of header entries. diff --git a/lib/IRremoteESP8266/src/IRsend.cpp b/lib/IRremoteESP8266/src/IRsend.cpp deleted file mode 100644 index 62556b7e96..0000000000 --- a/lib/IRremoteESP8266/src/IRsend.cpp +++ /dev/null @@ -1,1317 +0,0 @@ -// Copyright 2009 Ken Shirriff -// Copyright 2015 Mark Szabo -// Copyright 2017,2019 David Conran - -#include "IRsend.h" -#ifndef UNIT_TEST -#include -#else -#define __STDC_LIMIT_MACROS -#include -#endif -#include -#ifdef UNIT_TEST -#include -#endif -#include "IRtimer.h" - -/// Constructor for an IRsend object. -/// @param[in] IRsendPin Which GPIO pin to use when sending an IR command. -/// @param[in] inverted Optional flag to invert the output. (default = false) -/// e.g. LED is illuminated when GPIO is LOW rather than HIGH. -/// @warning Setting `inverted` to something other than the default could -/// easily destroy your IR LED if you are overdriving it. -/// Unless you *REALLY* know what you are doing, don't change this. -/// @param[in] use_modulation Do we do frequency modulation during transmission? -/// i.e. If not, assume a 100% duty cycle. Ignore attempts to change the -/// duty cycle etc. -IRsend::IRsend(uint16_t IRsendPin, bool inverted, bool use_modulation) - : IRpin(IRsendPin), periodOffset(kPeriodOffset) { - if (inverted) { - outputOn = LOW; - outputOff = HIGH; - } else { - outputOn = HIGH; - outputOff = LOW; - } - modulation = use_modulation; - if (modulation) - _dutycycle = kDutyDefault; - else - _dutycycle = kDutyMax; -} - -/// Enable the pin for output. -void IRsend::begin() { -#ifndef UNIT_TEST - pinMode(IRpin, OUTPUT); -#endif - ledOff(); // Ensure the LED is in a known safe state when we start. -} - -/// Turn off the IR LED. -void IRsend::ledOff() { -#ifndef UNIT_TEST - digitalWrite(IRpin, outputOff); -#endif -} - -/// Turn on the IR LED. -void IRsend::ledOn() { -#ifndef UNIT_TEST - digitalWrite(IRpin, outputOn); -#endif -} - -/// Calculate the period for a given frequency. -/// @param[in] hz Frequency in Hz. -/// @param[in] use_offset Should we use the calculated offset or not? -/// @return nr. of uSeconds. -/// @note (T = 1/f) -uint32_t IRsend::calcUSecPeriod(uint32_t hz, bool use_offset) { - if (hz == 0) hz = 1; // Avoid Zero hz. Divide by Zero is nasty. - uint32_t period = - (1000000UL + hz / 2) / hz; // The equiv of round(1000000/hz). - // Apply the offset and ensure we don't result in a <= 0 value. - if (use_offset) - return std::max((uint32_t)1, period + periodOffset); - else - return std::max((uint32_t)1, period); -} - -/// Set the output frequency modulation and duty cycle. -/// @param[in] freq The freq we want to modulate at. -/// Assumes < 1000 means kHz else Hz. -/// @param[in] duty Percentage duty cycle of the LED. -/// e.g. 25 = 25% = 1/4 on, 3/4 off. -/// If you are not sure, try 50 percent. -/// This is ignored if modulation is disabled at object instantiation. -/// @note Integer timing functions & math mean we can't do fractions of -/// microseconds timing. Thus minor changes to the freq & duty values may have -/// limited effect. You've been warned. -void IRsend::enableIROut(uint32_t freq, uint8_t duty) { - // Set the duty cycle to use if we want freq. modulation. - if (modulation) { - _dutycycle = std::min(duty, kDutyMax); - } else { - _dutycycle = kDutyMax; - } - if (freq < 1000) // Were we given kHz? Supports the old call usage. - freq *= 1000; -#ifdef UNIT_TEST - _freq_unittest = freq; -#endif // UNIT_TEST - uint32_t period = calcUSecPeriod(freq); - // Nr. of uSeconds the LED will be on per pulse. - onTimePeriod = (period * _dutycycle) / kDutyMax; - // Nr. of uSeconds the LED will be off per pulse. - offTimePeriod = period - onTimePeriod; -} - -#if ALLOW_DELAY_CALLS -/// An ESP8266 RTOS watch-dog timer friendly version of delayMicroseconds(). -/// @param[in] usec Nr. of uSeconds to delay for. -void IRsend::_delayMicroseconds(uint32_t usec) { - // delayMicroseconds() is only accurate to 16383us. - // Ref: https://www.arduino.cc/en/Reference/delayMicroseconds - if (usec <= kMaxAccurateUsecDelay) { -#ifndef UNIT_TEST - delayMicroseconds(usec); -#endif - } else { -#ifndef UNIT_TEST - // Invoke a delay(), where possible, to avoid triggering the WDT. - delay(usec / 1000UL); // Delay for as many whole milliseconds as we can. - // Delay the remaining sub-millisecond. - delayMicroseconds(static_cast(usec % 1000UL)); -#endif - } -} -#else // ALLOW_DELAY_CALLS -/// A version of delayMicroseconds() that handles large values and does NOT use -/// the watch-dog friendly delay() calls where appropriate. -/// @note Use this only if you know what you are doing as it may cause the WDT -/// to reset the ESP8266. -void IRsend::_delayMicroseconds(uint32_t usec) { - for (; usec > kMaxAccurateUsecDelay; usec -= kMaxAccurateUsecDelay) -#ifndef UNIT_TEST - delayMicroseconds(kMaxAccurateUsecDelay); - delayMicroseconds(static_cast(usec)); -#endif // UNIT_TEST -} -#endif // ALLOW_DELAY_CALLS - -/// Modulate the IR LED for the given period (usec) and at the duty cycle set. -/// @param[in] usec The period of time to modulate the IR LED for, in -/// microseconds. -/// @return Nr. of pulses actually sent. -/// @note -/// The ESP8266 has no good way to do hardware PWM, so we have to do it all -/// in software. There is a horrible kludge/brilliant hack to use the second -/// serial TX line to do fairly accurate hardware PWM, but it is only -/// available on a single specific GPIO and only available on some modules. -/// e.g. It's not available on the ESP-01 module. -/// Hence, for greater compatibility & choice, we don't use that method. -/// Ref: -/// https://www.analysir.com/blog/2017/01/29/updated-esp8266-nodemcu-backdoor-upwm-hack-for-ir-signals/ -uint16_t IRsend::mark(uint16_t usec) { - // Handle the simple case of no required frequency modulation. - if (!modulation || _dutycycle >= 100) { - ledOn(); - _delayMicroseconds(usec); - ledOff(); - return 1; - } - - // Not simple, so do it assuming frequency modulation. - uint16_t counter = 0; - IRtimer usecTimer = IRtimer(); - // Cache the time taken so far. This saves us calling time, and we can be - // assured that we can't have odd math problems. i.e. unsigned under/overflow. - uint32_t elapsed = usecTimer.elapsed(); - - while (elapsed < usec) { // Loop until we've met/exceeded our required time. - ledOn(); - // Calculate how long we should pulse on for. - // e.g. Are we to close to the end of our requested mark time (usec)? - _delayMicroseconds(std::min((uint32_t)onTimePeriod, usec - elapsed)); - ledOff(); - counter++; - if (elapsed + onTimePeriod >= usec) - return counter; // LED is now off & we've passed our allotted time. - // Wait for the lesser of the rest of the duty cycle, or the time remaining. - _delayMicroseconds( - std::min(usec - elapsed - onTimePeriod, (uint32_t)offTimePeriod)); - elapsed = usecTimer.elapsed(); // Update & recache the actual elapsed time. - } - return counter; -} - -/// Turn the pin (LED) off for a given time. -/// Sends an IR space for the specified number of microseconds. -/// A space is no output, so the PWM output is disabled. -/// @param[in] time Time in microseconds (us). -void IRsend::space(uint32_t time) { - ledOff(); - if (time == 0) return; - _delayMicroseconds(time); -} - -/// Calculate & set any offsets to account for execution times during sending. -/// -/// @param[in] hz The frequency to calibrate at >= 1000Hz. Default is 38000Hz. -/// @return The calculated period offset (in uSeconds) which is now in use. -/// e.g. -5. -/// @note This will generate an 65535us mark() IR LED signal. -/// This only needs to be called once, if at all. -int8_t IRsend::calibrate(uint16_t hz) { - if (hz < 1000) // Were we given kHz? Supports the old call usage. - hz *= 1000; - periodOffset = 0; // Turn off any existing offset while we calibrate. - enableIROut(hz); - IRtimer usecTimer = IRtimer(); // Start a timer *just* before we do the call. - uint16_t pulses = mark(UINT16_MAX); // Generate a PWM of 65,535 us. (Max.) - uint32_t timeTaken = usecTimer.elapsed(); // Record the time it took. - // While it shouldn't be necessary, assume at least 1 pulse, to avoid a - // divide by 0 situation. - pulses = std::max(pulses, (uint16_t)1U); - uint32_t calcPeriod = calcUSecPeriod(hz); // e.g. @38kHz it should be 26us. - // Assuming 38kHz for the example calculations: - // In a 65535us pulse, we should have 2520.5769 pulses @ 26us periods. - // e.g. 65535.0us / 26us = 2520.5769 - // This should have caused approx 2520 loops through the main loop in mark(). - // The average over that many interations should give us a reasonable - // approximation at what offset we need to use to account for instruction - // execution times. - // - // Calculate the actual period from the actual time & the actual pulses - // generated. - double_t actualPeriod = (double_t)timeTaken / (double_t)pulses; - // Store the difference between the actual time per period vs. calculated. - periodOffset = (int8_t)((double_t)calcPeriod - actualPeriod); - return periodOffset; -} - -/// Generic method for sending data that is common to most protocols. -/// Will send leading or trailing 0's if the nbits is larger than the number -/// of bits in data. -/// @param[in] onemark Nr. of usecs for the led to be pulsed for a '1' bit. -/// @param[in] onespace Nr. of usecs for the led to be fully off for a '1' bit. -/// @param[in] zeromark Nr. of usecs for the led to be pulsed for a '0' bit. -/// @param[in] zerospace Nr. of usecs for the led to be fully off for a '0' bit. -/// @param[in] data The data to be transmitted. -/// @param[in] nbits Nr. of bits of data to be sent. -/// @param[in] MSBfirst Flag for bit transmission order. -/// Defaults to MSB->LSB order. -void IRsend::sendData(uint16_t onemark, uint32_t onespace, uint16_t zeromark, - uint32_t zerospace, uint64_t data, uint16_t nbits, - bool MSBfirst) { - if (nbits == 0) // If we are asked to send nothing, just return. - return; - if (MSBfirst) { // Send the MSB first. - // Send 0's until we get down to a bit size we can actually manage. - while (nbits > sizeof(data) * 8) { - mark(zeromark); - space(zerospace); - nbits--; - } - // Send the supplied data. - for (uint64_t mask = 1ULL << (nbits - 1); mask; mask >>= 1) - if (data & mask) { // Send a 1 - mark(onemark); - space(onespace); - } else { // Send a 0 - mark(zeromark); - space(zerospace); - } - } else { // Send the Least Significant Bit (LSB) first / MSB last. - for (uint16_t bit = 0; bit < nbits; bit++, data >>= 1) - if (data & 1) { // Send a 1 - mark(onemark); - space(onespace); - } else { // Send a 0 - mark(zeromark); - space(zerospace); - } - } -} - -/// Generic method for sending simple protocol messages. -/// Will send leading or trailing 0's if the nbits is larger than the number -/// of bits in data. -/// @param[in] headermark Nr. of usecs for the led to be pulsed for the header -/// mark. A value of 0 means no header mark. -/// @param[in] headerspace Nr. of usecs for the led to be off after the header -/// mark. A value of 0 means no header space. -/// @param[in] onemark Nr. of usecs for the led to be pulsed for a '1' bit. -/// @param[in] onespace Nr. of usecs for the led to be fully off for a '1' bit. -/// @param[in] zeromark Nr. of usecs for the led to be pulsed for a '0' bit. -/// @param[in] zerospace Nr. of usecs for the led to be fully off for a '0' bit. -/// @param[in] footermark Nr. of usecs for the led to be pulsed for the footer -/// mark. A value of 0 means no footer mark. -/// @param[in] gap Nr. of usecs for the led to be off after the footer mark. -/// This is effectively the gap between messages. -/// A value of 0 means no gap space. -/// @param[in] data The data to be transmitted. -/// @param[in] nbits Nr. of bits of data to be sent. -/// @param[in] frequency The frequency we want to modulate at. (Hz/kHz) -/// @param[in] MSBfirst Flag for bit transmission order. -/// Defaults to MSB->LSB order. -/// @param[in] repeat Nr. of extra times the message will be sent. -/// e.g. 0 = 1 message sent, 1 = 1 initial + 1 repeat = 2 messages -/// @param[in] dutycycle Percentage duty cycle of the LED. -/// e.g. 25 = 25% = 1/4 on, 3/4 off. -/// If you are not sure, try 50 percent. -/// @note Assumes a frequency < 1000 means kHz otherwise it is in Hz. -/// Most common value is 38000 or 38, for 38kHz. -void IRsend::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) { - sendGeneric(headermark, headerspace, onemark, onespace, zeromark, zerospace, - footermark, gap, 0U, data, nbits, frequency, MSBfirst, repeat, - dutycycle); -} - -/// Generic method for sending simple protocol messages. -/// Will send leading or trailing 0's if the nbits is larger than the number -/// of bits in data. -/// @param[in] headermark Nr. of usecs for the led to be pulsed for the header -/// mark. A value of 0 means no header mark. -/// @param[in] headerspace Nr. of usecs for the led to be off after the header -/// mark. A value of 0 means no header space. -/// @param[in] onemark Nr. of usecs for the led to be pulsed for a '1' bit. -/// @param[in] onespace Nr. of usecs for the led to be fully off for a '1' bit. -/// @param[in] zeromark Nr. of usecs for the led to be pulsed for a '0' bit. -/// @param[in] zerospace Nr. of usecs for the led to be fully off for a '0' bit. -/// @param[in] footermark Nr. of usecs for the led to be pulsed for the footer -/// mark. A value of 0 means no footer mark. -/// @param[in] gap Nr. of usecs for the led to be off after the footer mark. -/// This is effectively the gap between messages. -/// A value of 0 means no gap space. -/// @param[in] mesgtime Min. nr. of usecs a single message needs to be. -/// This is effectively the min. total length of a single message. -/// @param[in] data The data to be transmitted. -/// @param[in] nbits Nr. of bits of data to be sent. -/// @param[in] frequency The frequency we want to modulate at. (Hz/kHz) -/// @param[in] MSBfirst Flag for bit transmission order. -/// Defaults to MSB->LSB order. -/// @param[in] repeat Nr. of extra times the message will be sent. -/// e.g. 0 = 1 message sent, 1 = 1 initial + 1 repeat = 2 messages -/// @param[in] dutycycle Percentage duty cycle of the LED. -/// e.g. 25 = 25% = 1/4 on, 3/4 off. -/// If you are not sure, try 50 percent. -/// @note Assumes a frequency < 1000 means kHz otherwise it is in Hz. -/// Most common value is 38000 or 38, for 38kHz. -void IRsend::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) { - // Setup - enableIROut(frequency, dutycycle); - IRtimer usecs = IRtimer(); - - // We always send a message, even for repeat=0, hence '<= repeat'. - for (uint16_t r = 0; r <= repeat; r++) { - usecs.reset(); - - // Header - if (headermark) mark(headermark); - if (headerspace) space(headerspace); - - // Data - sendData(onemark, onespace, zeromark, zerospace, data, nbits, MSBfirst); - - // Footer - if (footermark) mark(footermark); - uint32_t elapsed = usecs.elapsed(); - // Avoid potential unsigned integer underflow. e.g. when mesgtime is 0. - if (elapsed >= mesgtime) - space(gap); - else - space(std::max(gap, mesgtime - elapsed)); - } -} - -/// Generic method for sending simple protocol messages. -/// @param[in] headermark Nr. of usecs for the led to be pulsed for the header -/// mark. A value of 0 means no header mark. -/// @param[in] headerspace Nr. of usecs for the led to be off after the header -/// mark. A value of 0 means no header space. -/// @param[in] onemark Nr. of usecs for the led to be pulsed for a '1' bit. -/// @param[in] onespace Nr. of usecs for the led to be fully off for a '1' bit. -/// @param[in] zeromark Nr. of usecs for the led to be pulsed for a '0' bit. -/// @param[in] zerospace Nr. of usecs for the led to be fully off for a '0' bit. -/// @param[in] footermark Nr. of usecs for the led to be pulsed for the footer -/// mark. A value of 0 means no footer mark. -/// @param[in] gap Nr. of usecs for the led to be off after the footer mark. -/// This is effectively the gap between messages. -/// A value of 0 means no gap space. -/// @param[in] dataptr Pointer to the data to be transmitted. -/// @param[in] nbytes Nr. of bytes of data to be sent. -/// @param[in] frequency The frequency we want to modulate at. (Hz/kHz) -/// @param[in] MSBfirst Flag for bit transmission order. -/// Defaults to MSB->LSB order. -/// @param[in] repeat Nr. of extra times the message will be sent. -/// e.g. 0 = 1 message sent, 1 = 1 initial + 1 repeat = 2 messages -/// @param[in] dutycycle Percentage duty cycle of the LED. -/// e.g. 25 = 25% = 1/4 on, 3/4 off. -/// If you are not sure, try 50 percent. -/// @note Assumes a frequency < 1000 means kHz otherwise it is in Hz. -/// Most common value is 38000 or 38, for 38kHz. -void IRsend::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) { - // Setup - enableIROut(frequency, dutycycle); - // We always send a message, even for repeat=0, hence '<= repeat'. - for (uint16_t r = 0; r <= repeat; r++) { - // Header - if (headermark) mark(headermark); - if (headerspace) space(headerspace); - - // Data - for (uint16_t i = 0; i < nbytes; i++) - sendData(onemark, onespace, zeromark, zerospace, *(dataptr + i), 8, - MSBfirst); - - // Footer - if (footermark) mark(footermark); - space(gap); - } -} - -/// Generic method for sending Manchester code data. -/// Will send leading or trailing 0's if the nbits is larger than the number -/// of bits in data. -/// @param[in] half_period Nr. of uSeconds for half the clock's period. -/// (1/2 wavelength) -/// @param[in] data The data to be transmitted. -/// @param[in] nbits Nr. of bits of data to be sent. -/// @param[in] MSBfirst Flag for bit transmission order. -/// Defaults to MSB->LSB order. -/// @param[in] GEThomas Use G.E. Thomas (true/default) or IEEE 802.3 (false). -void IRsend::sendManchesterData(const uint16_t half_period, - const uint64_t data, - const uint16_t nbits, const bool MSBfirst, - const bool GEThomas) { - if (nbits == 0) return; // Nothing to send. - uint16_t bits = nbits; - uint64_t copy = (GEThomas) ? data : ~data; - - if (MSBfirst) { // Send the MSB first. - // Send 0's until we get down to a bit size we can actually manage. - if (bits > (sizeof(data) * 8)) { - sendManchesterData(half_period, 0ULL, bits - sizeof(data) * 8, MSBfirst, - GEThomas); - bits = sizeof(data) * 8; - } - // Send the supplied data. - for (uint64_t mask = 1ULL << (bits - 1); mask; mask >>= 1) - if (copy & mask) { - mark(half_period); - space(half_period); - } else { - space(half_period); - mark(half_period); - } - } else { // Send the Least Significant Bit (LSB) first / MSB last. - for (bits = 0; bits < nbits; bits++, copy >>= 1) - if (copy & 1) { - mark(half_period); - space(half_period); - } else { - space(half_period); - mark(half_period); - } - } -} - -/// Generic method for sending Manchester code messages. -/// Will send leading or trailing 0's if the nbits is larger than the number -/// @param[in] headermark Nr. of usecs for the led to be pulsed for the header -/// mark. A value of 0 means no header mark. -/// @param[in] headerspace Nr. of usecs for the led to be off after the header -/// mark. A value of 0 means no header space. -/// @param[in] half_period Nr. of uSeconds for half the clock's period. -/// (1/2 wavelength) -/// @param[in] footermark Nr. of usecs for the led to be pulsed for the footer -/// mark. A value of 0 means no footer mark. -/// @param[in] gap Min. nr. of usecs for the led to be off after the footer -/// mark. This is effectively the absolute minimum gap between messages. -/// @param[in] data The data to be transmitted. -/// @param[in] nbits Nr. of bits of data to be sent. -/// @param[in] frequency The frequency we want to modulate at. (Hz/kHz) -/// @param[in] MSBfirst Flag for bit transmission order. -/// Defaults to MSB->LSB order. -/// @param[in] repeat Nr. of extra times the message will be sent. -/// e.g. 0 = 1 message sent, 1 = 1 initial + 1 repeat = 2 messages -/// @param[in] dutycycle Percentage duty cycle of the LED. -/// e.g. 25 = 25% = 1/4 on, 3/4 off. -/// If you are not sure, try 50 percent. -/// @param[in] GEThomas Use G.E. Thomas (true/default) or IEEE 802.3 (false). -/// @note Assumes a frequency < 1000 means kHz otherwise it is in Hz. -/// Most common value is 38000 or 38, for 38kHz. -void IRsend::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, const bool MSBfirst, - const uint16_t repeat, const uint8_t dutycycle, - const bool GEThomas) { - // Setup - enableIROut(frequency, dutycycle); - - // We always send a message, even for repeat=0, hence '<= repeat'. - for (uint16_t r = 0; r <= repeat; r++) { - // Header - if (headermark) mark(headermark); - if (headerspace) space(headerspace); - // Data - sendManchesterData(half_period, data, nbits, MSBfirst, GEThomas); - // Footer - if (footermark) mark(footermark); - if (gap) space(gap); - } -} - -#if SEND_RAW -/// Send a raw IRremote message. -/// -/// @param[in] buf An array of uint16_t's that has microseconds elements. -/// @param[in] len Nr. of elements in the buf[] array. -/// @param[in] hz Frequency to send the message at. (kHz < 1000; Hz >= 1000) -/// @note Even elements are Mark times (On), Odd elements are Space times (Off). -/// Ref: -/// examples/IRrecvDumpV2/IRrecvDumpV2.ino (or later) -void IRsend::sendRaw(const uint16_t buf[], const uint16_t len, - const uint16_t hz) { - // Set IR carrier frequency - enableIROut(hz); - for (uint16_t i = 0; i < len; i++) { - if (i & 1) { // Odd bit. - space(buf[i]); - } else { // Even bit. - mark(buf[i]); - } - } - ledOff(); // We potentially have ended with a mark(), so turn of the LED. -} -#endif // SEND_RAW - -/// Get the minimum number of repeats for a given protocol. -/// @param[in] protocol Protocol number/type of the message you want to send. -/// @return The number of repeats required. -uint16_t IRsend::minRepeats(const decode_type_t protocol) { - switch (protocol) { - // Single repeats - case AIWA_RC_T501: - case AMCOR: - case COOLIX: - case ELITESCREENS: - case GICABLE: - case INAX: - case MIDEA24: - case MITSUBISHI: - case MITSUBISHI2: - case MITSUBISHI_AC: - case MULTIBRACKETS: - case SHERWOOD: - case TOSHIBA_AC: - return kSingleRepeat; - // Special - case AIRWELL: - return kAirwellMinRepeats; - case CARRIER_AC40: - return kCarrierAc40MinRepeat; - case DISH: - return kDishMinRepeat; - case EPSON: - return kEpsonMinRepeat; - case SANYO_AC88: - return kSanyoAc88MinRepeat; - case SONY: - return kSonyMinRepeat; - case SONY_38K: - return kSonyMinRepeat + 1; - case SYMPHONY: - return kSymphonyDefaultRepeat; - case ZEPEAL: - return kZepealMinRepeat; - default: - return kNoRepeat; - } -} - -/// Get the default number of bits for a given protocol. -/// @param[in] protocol Protocol number/type you want the default bit size for. -/// @return The number of bits. -uint16_t IRsend::defaultBits(const decode_type_t protocol) { - switch (protocol) { - case MULTIBRACKETS: - return 8; - case RC5: - case SYMPHONY: - return 12; - case LASERTAG: - case RC5X: - return 13; - case AIWA_RC_T501: - case DENON: - case SHARP: - return 15; - case BOSE: - case DISH: - case GICABLE: - case JVC: - case LEGOPF: - case MITSUBISHI: - case MITSUBISHI2: - case ZEPEAL: - return 16; - case METZ: - return 19; - case RC6: - case SONY: - case SONY_38K: - return 20; - case COOLIX: - case INAX: - case MIDEA24: - case NIKAI: - case RCMM: - case TRANSCOLD: - return 24; - case LG: - case LG2: - return 28; - case ARRIS: - case CARRIER_AC: - case ELITESCREENS: - case EPSON: - case NEC: - case NEC_LIKE: - case PANASONIC_AC32: - case SAMSUNG: - case SHERWOOD: - case WHYNTER: - return 32; - case AIRWELL: - return 34; - case LUTRON: - case TECO: - return 35; - case SAMSUNG36: - return 36; - case CARRIER_AC40: - return kCarrierAc40Bits; // 40 - case DOSHISHA: - return kDoshishaBits; // 40 - case SANYO_LC7461: - return kSanyoLC7461Bits; // 42 - case GOODWEATHER: - case KELON: - case MIDEA: - case PANASONIC: - return 48; - case ECOCLIM: - case MAGIQUEST: - case VESTEL_AC: - case TECHNIBEL_AC: - case TRUMA: - return 56; - case AMCOR: - case CARRIER_AC64: - case DELONGHI_AC: - case PIONEER: - return 64; - case ARGO: - return kArgoBits; - case CORONA_AC: - return kCoronaAcBits; - case DAIKIN: - return kDaikinBits; - case DAIKIN128: - return kDaikin128Bits; - case DAIKIN152: - return kDaikin152Bits; - case DAIKIN160: - return kDaikin160Bits; - case DAIKIN176: - return kDaikin176Bits; - case DAIKIN2: - return kDaikin2Bits; - case DAIKIN216: - return kDaikin216Bits; - case DAIKIN64: - return kDaikin64Bits; - case ELECTRA_AC: - return kElectraAcBits; - case GREE: - return kGreeBits; - case HAIER_AC: - return kHaierACBits; - case HAIER_AC_YRW02: - return kHaierACYRW02Bits; - case HAIER_AC176: - return kHaierAC176Bits; - case HITACHI_AC: - return kHitachiAcBits; - case HITACHI_AC1: - return kHitachiAc1Bits; - case HITACHI_AC2: - return kHitachiAc2Bits; - case HITACHI_AC3: - return kHitachiAc3Bits; - case HITACHI_AC344: - return kHitachiAc344Bits; - case HITACHI_AC424: - return kHitachiAc424Bits; - case KELVINATOR: - return kKelvinatorBits; - case MILESTAG2: - return kMilesTag2ShotBits; - case MIRAGE: - return kMirageBits; - case MITSUBISHI_AC: - return kMitsubishiACBits; - case MITSUBISHI136: - return kMitsubishi136Bits; - case MITSUBISHI112: - return kMitsubishi112Bits; - case MITSUBISHI_HEAVY_152: - return kMitsubishiHeavy152Bits; - case MITSUBISHI_HEAVY_88: - return kMitsubishiHeavy88Bits; - case NEOCLIMA: - return kNeoclimaBits; - case PANASONIC_AC: - return kPanasonicAcBits; - case RHOSS: - return kRhossBits; - case SAMSUNG_AC: - return kSamsungAcBits; - case SANYO_AC: - return kSanyoAcBits; - case SANYO_AC88: - return kSanyoAc88Bits; - case SHARP_AC: - return kSharpAcBits; - case TCL112AC: - return kTcl112AcBits; - case TEKNOPOINT: - return kTeknopointBits; - case TOSHIBA_AC: - return kToshibaACBits; - case TROTEC: - case TROTEC_3550: - return kTrotecBits; - case VOLTAS: - return kVoltasBits; - case WHIRLPOOL_AC: - return kWhirlpoolAcBits; - case XMP: - return kXmpBits; - // No default amount of bits. - case FUJITSU_AC: - case MWM: - default: - return 0; - } -} - -/// Send a simple (up to 64 bits) IR message of a given type. -/// An unknown/unsupported type will send nothing. -/// @param[in] type Protocol number/type of the message you want to send. -/// @param[in] data The data you want to send (up to 64 bits). -/// @param[in] nbits How many bits long the message is to be. -/// @param[in] repeat How many repeats to do? -/// @return True if it is a type we can attempt to send, false if not. -bool IRsend::send(const decode_type_t type, const uint64_t data, - const uint16_t nbits, const uint16_t repeat) { - uint16_t min_repeat __attribute__((unused)) = - std::max(IRsend::minRepeats(type), repeat); - switch (type) { -#if SEND_AIRWELL - case AIRWELL: - sendAirwell(data, nbits, min_repeat); - break; -#endif -#if SEND_AIWA_RC_T501 - case AIWA_RC_T501: - sendAiwaRCT501(data, nbits, min_repeat); - break; -#endif // SEND_AIWA_RC_T501 -#if SEND_ARRIS - case ARRIS: - sendArris(data, nbits, min_repeat); - break; -#endif // SEND_ARRIS -#if SEND_BOSE - case BOSE: - sendBose(data, nbits, min_repeat); - break; -#endif // SEND_BOSE -#if SEND_CARRIER_AC - case CARRIER_AC: - sendCarrierAC(data, nbits, min_repeat); - break; -#endif -#if SEND_CARRIER_AC40 - case CARRIER_AC40: - sendCarrierAC40(data, nbits, min_repeat); - break; -#endif // SEND_CARRIER_AC40 -#if SEND_CARRIER_AC64 - case CARRIER_AC64: - sendCarrierAC64(data, nbits, min_repeat); - break; -#endif // SEND_CARRIER_AC64 -#if SEND_COOLIX - case COOLIX: - sendCOOLIX(data, nbits, min_repeat); - break; -#endif -#if SEND_DAIKIN64 - case DAIKIN64: - sendDaikin64(data, nbits, min_repeat); - break; -#endif -#if SEND_DELONGHI_AC - case DELONGHI_AC: - sendDelonghiAc(data, nbits, min_repeat); - break; -#endif -#if SEND_DENON - case DENON: - sendDenon(data, nbits, min_repeat); - break; -#endif -#if SEND_DISH - case DISH: - sendDISH(data, nbits, min_repeat); - break; -#endif -#if SEND_DOSHISHA - case DOSHISHA: - sendDoshisha(data, nbits, min_repeat); - break; -#endif -#if SEND_ECOCLIM - case ECOCLIM: - sendEcoclim(data, nbits, min_repeat); - break; -#endif // SEND_ECOCLIM -#if SEND_ELITESCREENS - case ELITESCREENS: - sendElitescreens(data, nbits, min_repeat); - break; -#endif // SEND_ELITESCREENS -#if SEND_EPSON - case EPSON: - sendEpson(data, nbits, min_repeat); - break; -#endif -#if SEND_GICABLE - case GICABLE: - sendGICable(data, nbits, min_repeat); - break; -#endif -#if SEND_GOODWEATHER - case GOODWEATHER: - sendGoodweather(data, nbits, min_repeat); - break; -#endif -#if SEND_GREE - case GREE: - sendGree(data, nbits, min_repeat); - break; -#endif -#if SEND_INAX - case INAX: - sendInax(data, nbits, min_repeat); - break; -#endif // SEND_INAX -#if SEND_JVC - case JVC: - sendJVC(data, nbits, min_repeat); - break; -#endif -#if SEND_KELON - case KELON: - sendKelon(data, nbits, min_repeat); - break; -#endif // SEND_KELON -#if SEND_LASERTAG - case LASERTAG: - sendLasertag(data, nbits, min_repeat); - break; -#endif -#if SEND_LEGOPF - case LEGOPF: - sendLegoPf(data, nbits, min_repeat); - break; -#endif -#if SEND_LG - case LG: - sendLG(data, nbits, min_repeat); - break; - case LG2: - sendLG2(data, nbits, min_repeat); - break; -#endif -#if SEND_LUTRON - case LUTRON: - sendLutron(data, nbits, min_repeat); - break; -#endif -#if SEND_MAGIQUEST - case MAGIQUEST: - sendMagiQuest(data, nbits, min_repeat); - break; -#endif // SEND_MAGIQUEST -#if SEND_METZ - case METZ: - sendMetz(data, nbits, min_repeat); - break; -#endif // SEND_METZ -#if SEND_MIDEA - case MIDEA: - sendMidea(data, nbits, min_repeat); - break; -#endif // SEND_MIDEA -#if SEND_MIDEA24 - case MIDEA24: - sendMidea24(data, nbits, min_repeat); - break; -#endif // SEND_MIDEA24 -#if SEND_MILESTAG2 - case MILESTAG2: - sendMilestag2(data, nbits, min_repeat); - break; -#endif // SEND_MILESTAG2 -#if SEND_MITSUBISHI - case MITSUBISHI: - sendMitsubishi(data, nbits, min_repeat); - break; -#endif -#if SEND_MITSUBISHI2 - case MITSUBISHI2: - sendMitsubishi2(data, nbits, min_repeat); - break; -#endif -#if SEND_MULTIBRACKETS - case MULTIBRACKETS: - sendMultibrackets(data, nbits, min_repeat); - break; -#endif -#if SEND_NIKAI - case NIKAI: - sendNikai(data, nbits, min_repeat); - break; -#endif -#if SEND_NEC - case NEC: - case NEC_LIKE: - sendNEC(data, nbits, min_repeat); - break; -#endif -#if SEND_PANASONIC - case PANASONIC: - sendPanasonic64(data, nbits, min_repeat); - break; -#endif // SEND_PANASONIC -#if SEND_PANASONIC_AC32 - case PANASONIC_AC32: - sendPanasonicAC32(data, nbits, min_repeat); - break; -#endif // SEND_PANASONIC_AC32 -#if SEND_PIONEER - case PIONEER: - sendPioneer(data, nbits, min_repeat); - break; -#endif -#if SEND_RC5 - case RC5: - case RC5X: - sendRC5(data, nbits, min_repeat); - break; -#endif -#if SEND_RC6 - case RC6: - sendRC6(data, nbits, min_repeat); - break; -#endif -#if SEND_RCMM - case RCMM: - sendRCMM(data, nbits, min_repeat); - break; -#endif -#if SEND_SAMSUNG - case SAMSUNG: - sendSAMSUNG(data, nbits, min_repeat); - break; -#endif -#if SEND_SAMSUNG36 - case SAMSUNG36: - sendSamsung36(data, nbits, min_repeat); - break; -#endif -#if SEND_SANYO - case SANYO_LC7461: - sendSanyoLC7461(data, nbits, min_repeat); - break; -#endif -#if SEND_SHARP - case SHARP: - sendSharpRaw(data, nbits, min_repeat); - break; -#endif -#if SEND_SHERWOOD - case SHERWOOD: - sendSherwood(data, nbits, min_repeat); - break; -#endif -#if SEND_SONY - case SONY: - sendSony(data, nbits, min_repeat); - break; - case SONY_38K: - sendSony38(data, nbits, min_repeat); - break; -#endif -#if SEND_SYMPHONY - case SYMPHONY: - sendSymphony(data, nbits, min_repeat); - break; -#endif -#if SEND_TECHNIBEL_AC - case TECHNIBEL_AC: - sendTechnibelAc(data, nbits, min_repeat); - break; -#endif -#if SEND_TECO - case TECO: - sendTeco(data, nbits, min_repeat); - break; -#endif // SEND_TECO -#if SEND_TRANSCOLD - case TRANSCOLD: - sendTranscold(data, nbits, min_repeat); - break; -#endif // SEND_TRANSCOLD -#if SEND_TRUMA - case TRUMA: - sendTruma(data, nbits, min_repeat); - break; -#endif // SEND_TRUMA -#if SEND_VESTEL_AC - case VESTEL_AC: - sendVestelAc(data, nbits, min_repeat); - break; -#endif -#if SEND_WHYNTER - case WHYNTER: - sendWhynter(data, nbits, min_repeat); - break; -#endif -#if SEND_XMP - case XMP: - sendXmp(data, nbits, min_repeat); - break; -#endif -#if SEND_ZEPEAL - case ZEPEAL: - sendZepeal(data, nbits, min_repeat); - break; -#endif // SEND_ZEPEAL - default: - return false; - } - return true; -} - -/// Send a complex (>= 64 bits) IR message of a given type. -/// An unknown/unsupported type will send nothing. -/// @param[in] type Protocol number/type of the message you want to send. -/// @param[in] state A pointer to the array of bytes that make up the state[]. -/// @param[in] nbytes How many bytes are in the state. -/// @return True if it is a type we can attempt to send, false if not. -bool IRsend::send(const decode_type_t type, const uint8_t *state, - const uint16_t nbytes) { - switch (type) { -#if SEND_VOLTAS - case VOLTAS: - sendVoltas(state, nbytes); - break; -#endif // SEND_VOLTAS -#if SEND_AMCOR - case AMCOR: - sendAmcor(state, nbytes); - break; -#endif -#if SEND_ARGO - case ARGO: - sendArgo(state, nbytes); - break; -#endif // SEND_ARGO -#if SEND_CORONA_AC - case CORONA_AC: - sendCoronaAc(state, nbytes); - break; -#endif // SEND_ARGO -#if SEND_DAIKIN - case DAIKIN: - sendDaikin(state, nbytes); - break; -#endif // SEND_DAIKIN -#if SEND_DAIKIN128 - case DAIKIN128: - sendDaikin128(state, nbytes); - break; -#endif // SEND_DAIKIN128 -#if SEND_DAIKIN152 - case DAIKIN152: - sendDaikin152(state, nbytes); - break; -#endif // SEND_DAIKIN152 -#if SEND_DAIKIN160 - case DAIKIN160: - sendDaikin160(state, nbytes); - break; -#endif // SEND_DAIKIN160 -#if SEND_DAIKIN176 - case DAIKIN176: - sendDaikin176(state, nbytes); - break; -#endif // SEND_DAIKIN176 -#if SEND_DAIKIN2 - case DAIKIN2: - sendDaikin2(state, nbytes); - break; -#endif // SEND_DAIKIN2 -#if SEND_DAIKIN216 - case DAIKIN216: - sendDaikin216(state, nbytes); - break; -#endif // SEND_DAIKIN216 -#if SEND_ELECTRA_AC - case ELECTRA_AC: - sendElectraAC(state, nbytes); - break; -#endif // SEND_ELECTRA_AC -#if SEND_FUJITSU_AC - case FUJITSU_AC: - sendFujitsuAC(state, nbytes); - break; -#endif // SEND_FUJITSU_AC -#if SEND_GREE - case GREE: - sendGree(state, nbytes); - break; -#endif // SEND_GREE -#if SEND_HAIER_AC - case HAIER_AC: - sendHaierAC(state, nbytes); - break; -#endif // SEND_HAIER_AC -#if SEND_HAIER_AC_YRW02 - case HAIER_AC_YRW02: - sendHaierACYRW02(state, nbytes); - break; -#endif // SEND_HAIER_AC_YRW02 -#if SEND_HAIER_AC176 - case HAIER_AC176: - sendHaierAC176(state, nbytes); - break; -#endif // SEND_HAIER_AC176 -#if SEND_HITACHI_AC - case HITACHI_AC: - sendHitachiAC(state, nbytes); - break; -#endif // SEND_HITACHI_AC -#if SEND_HITACHI_AC1 - case HITACHI_AC1: - sendHitachiAC1(state, nbytes); - break; -#endif // SEND_HITACHI_AC1 -#if SEND_HITACHI_AC2 - case HITACHI_AC2: - sendHitachiAC2(state, nbytes); - break; -#endif // SEND_HITACHI_AC2 -#if SEND_HITACHI_AC3 - case HITACHI_AC3: - sendHitachiAc3(state, nbytes); - break; -#endif // SEND_HITACHI_AC3 -#if SEND_HITACHI_AC344 - case HITACHI_AC344: - sendHitachiAc344(state, nbytes); - break; -#endif // SEND_HITACHI_AC344 -#if SEND_HITACHI_AC424 - case HITACHI_AC424: - sendHitachiAc424(state, nbytes); - break; -#endif // SEND_HITACHI_AC424 -#if SEND_KELVINATOR - case KELVINATOR: - sendKelvinator(state, nbytes); - break; -#endif // SEND_KELVINATOR -#if SEND_MIRAGE - case MIRAGE: - sendMirage(state, nbytes); - break; -#endif // SEND_MIRAGE -#if SEND_MITSUBISHI_AC - case MITSUBISHI_AC: - sendMitsubishiAC(state, nbytes); - break; -#endif // SEND_MITSUBISHI_AC -#if SEND_MITSUBISHI136 - case MITSUBISHI136: - sendMitsubishi136(state, nbytes); - break; -#endif // SEND_MITSUBISHI136 -#if SEND_MITSUBISHI112 - case MITSUBISHI112: - sendMitsubishi112(state, nbytes); - break; -#endif // SEND_MITSUBISHI112 -#if SEND_MITSUBISHIHEAVY - case MITSUBISHI_HEAVY_88: - sendMitsubishiHeavy88(state, nbytes); - break; - case MITSUBISHI_HEAVY_152: - sendMitsubishiHeavy152(state, nbytes); - break; -#endif // SEND_MITSUBISHIHEAVY -#if SEND_MWM - case MWM: - sendMWM(state, nbytes); - break; -#endif // SEND_MWM -#if SEND_NEOCLIMA - case NEOCLIMA: - sendNeoclima(state, nbytes); - break; -#endif // SEND_NEOCLIMA -#if SEND_PANASONIC_AC - case PANASONIC_AC: - sendPanasonicAC(state, nbytes); - break; -#endif // SEND_PANASONIC_AC -#if SEND_RHOSS - case RHOSS: - sendRhoss(state, nbytes); - break; -#endif // SEND_RHOSS -#if SEND_SAMSUNG_AC - case SAMSUNG_AC: - sendSamsungAC(state, nbytes); - break; -#endif // SEND_SAMSUNG_AC -#if SEND_SANYO_AC - case SANYO_AC: - sendSanyoAc(state, nbytes); - break; -#endif // SEND_SANYO_AC -#if SEND_SANYO_AC88 - case SANYO_AC88: - sendSanyoAc88(state, nbytes); - break; -#endif // SEND_SANYO_AC88 -#if SEND_SHARP_AC - case SHARP_AC: - sendSharpAc(state, nbytes); - break; -#endif // SEND_SHARP_AC -#if SEND_TCL112AC - case TCL112AC: - sendTcl112Ac(state, nbytes); - break; -#endif // SEND_TCL112AC -#if SEND_TEKNOPOINT - case TEKNOPOINT: - sendTeknopoint(state, nbytes); - break; -#endif // SEND_TEKNOPOINT -#if SEND_TOSHIBA_AC - case TOSHIBA_AC: - sendToshibaAC(state, nbytes); - break; -#endif // SEND_TOSHIBA_AC -#if SEND_TROTEC - case TROTEC: - sendTrotec(state, nbytes); - break; -#endif // SEND_TROTEC -#if SEND_TROTEC_3550 - case TROTEC_3550: - sendTrotec3550(state, nbytes); - break; -#endif // SEND_TROTEC_3550 -#if SEND_WHIRLPOOL_AC - case WHIRLPOOL_AC: - sendWhirlpoolAC(state, nbytes); - break; -#endif // SEND_WHIRLPOOL_AC - default: - return false; - } - return true; -} diff --git a/lib/IRremoteESP8266/src/IRsend.h b/lib/IRremoteESP8266/src/IRsend.h index c09ec3d4cf..a4ceba12cf 100644 --- a/lib/IRremoteESP8266/src/IRsend.h +++ b/lib/IRremoteESP8266/src/IRsend.h @@ -6,7 +6,7 @@ #define __STDC_LIMIT_MACROS #include -#include "IRremoteESP8266.h" +#include "../src/IRremoteESP8266.h" // Originally from https://github.com/shirriff/Arduino-IRremote/ // Updated by markszabo (https://github.com/crankyoldgit/IRremoteESP8266) for diff --git a/lib/IRremoteESP8266/src/IRtext.cpp b/lib/IRremoteESP8266/src/IRtext.cpp deleted file mode 100644 index 7866b47f9a..0000000000 --- a/lib/IRremoteESP8266/src/IRtext.cpp +++ /dev/null @@ -1,383 +0,0 @@ -// 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 -#endif // UNIT_TEST -#include "IRremoteESP8266.h" -#include "i18n.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(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(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(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(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(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" (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(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" - -// 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(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(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" - -// Protocol Names -// Needs to be in decode_type_t order. -IRTEXT_CONST_BLOB_DECL(kAllProtocolNamesStr) { - D_STR_UNUSED "\x0" - D_STR_RC5 "\x0" - D_STR_RC6 "\x0" - D_STR_NEC "\x0" - D_STR_SONY "\x0" - D_STR_PANASONIC "\x0" - D_STR_JVC "\x0" - D_STR_SAMSUNG "\x0" - D_STR_WHYNTER "\x0" - D_STR_AIWA_RC_T501 "\x0" - D_STR_LG "\x0" - D_STR_SANYO "\x0" - D_STR_MITSUBISHI "\x0" - D_STR_DISH "\x0" - D_STR_SHARP "\x0" - D_STR_COOLIX "\x0" - D_STR_DAIKIN "\x0" - D_STR_DENON "\x0" - D_STR_KELVINATOR "\x0" - D_STR_SHERWOOD "\x0" - D_STR_MITSUBISHI_AC "\x0" - D_STR_RCMM "\x0" - D_STR_SANYO_LC7461 "\x0" - D_STR_RC5X "\x0" - D_STR_GREE "\x0" - D_STR_PRONTO "\x0" - D_STR_NEC_LIKE "\x0" - D_STR_ARGO "\x0" - D_STR_TROTEC "\x0" - D_STR_NIKAI "\x0" - D_STR_RAW "\x0" - D_STR_GLOBALCACHE "\x0" - D_STR_TOSHIBA_AC "\x0" - D_STR_FUJITSU_AC "\x0" - D_STR_MIDEA "\x0" - D_STR_MAGIQUEST "\x0" - D_STR_LASERTAG "\x0" - D_STR_CARRIER_AC "\x0" - D_STR_HAIER_AC "\x0" - D_STR_MITSUBISHI2 "\x0" - D_STR_HITACHI_AC "\x0" - D_STR_HITACHI_AC1 "\x0" - D_STR_HITACHI_AC2 "\x0" - D_STR_GICABLE "\x0" - D_STR_HAIER_AC_YRW02 "\x0" - D_STR_WHIRLPOOL_AC "\x0" - D_STR_SAMSUNG_AC "\x0" - D_STR_LUTRON "\x0" - D_STR_ELECTRA_AC "\x0" - D_STR_PANASONIC_AC "\x0" - D_STR_PIONEER "\x0" - D_STR_LG2 "\x0" - D_STR_MWM "\x0" - D_STR_DAIKIN2 "\x0" - D_STR_VESTEL_AC "\x0" - D_STR_TECO "\x0" - D_STR_SAMSUNG36 "\x0" - D_STR_TCL112AC "\x0" - D_STR_LEGOPF "\x0" - D_STR_MITSUBISHI_HEAVY_88 "\x0" - D_STR_MITSUBISHI_HEAVY_152 "\x0" - D_STR_DAIKIN216 "\x0" - D_STR_SHARP_AC "\x0" - D_STR_GOODWEATHER "\x0" - D_STR_INAX "\x0" - D_STR_DAIKIN160 "\x0" - D_STR_NEOCLIMA "\x0" - D_STR_DAIKIN176 "\x0" - D_STR_DAIKIN128 "\x0" - D_STR_AMCOR "\x0" - D_STR_DAIKIN152 "\x0" - D_STR_MITSUBISHI136 "\x0" - D_STR_MITSUBISHI112 "\x0" - D_STR_HITACHI_AC424 "\x0" - D_STR_SONY_38K "\x0" - D_STR_EPSON "\x0" - D_STR_SYMPHONY "\x0" - D_STR_HITACHI_AC3 "\x0" - D_STR_DAIKIN64 "\x0" - D_STR_AIRWELL "\x0" - D_STR_DELONGHI_AC "\x0" - D_STR_DOSHISHA "\x0" - D_STR_MULTIBRACKETS "\x0" - D_STR_CARRIER_AC40 "\x0" - D_STR_CARRIER_AC64 "\x0" - D_STR_HITACHI_AC344 "\x0" - D_STR_CORONA_AC "\x0" - D_STR_MIDEA24 "\x0" - D_STR_ZEPEAL "\x0" - D_STR_SANYO_AC "\x0" - D_STR_VOLTAS "\x0" - D_STR_METZ "\x0" - D_STR_TRANSCOLD "\x0" - D_STR_TECHNIBEL_AC "\x0" - D_STR_MIRAGE "\x0" - D_STR_ELITESCREENS "\x0" - D_STR_PANASONIC_AC32 "\x0" - D_STR_MILESTAG2 "\x0" - D_STR_ECOCLIM "\x0" - D_STR_XMP "\x0" - D_STR_TRUMA "\x0" - D_STR_HAIER_AC176 "\x0" - D_STR_TEKNOPOINT "\x0" - D_STR_KELON "\x0" - D_STR_TROTEC_3550 "\x0" - D_STR_SANYO_AC88 "\x0" - D_STR_BOSE "\x0" - D_STR_ARRIS "\x0" - D_STR_RHOSS "\x0" - ///< New protocol strings should be added just above this line. - "\x0" ///< This string requires double null termination. -}; - -IRTEXT_CONST_BLOB_PTR(kAllProtocolNamesStr); diff --git a/lib/IRremoteESP8266/src/IRtimer.cpp b/lib/IRremoteESP8266/src/IRtimer.cpp deleted file mode 100644 index e1f098b280..0000000000 --- a/lib/IRremoteESP8266/src/IRtimer.cpp +++ /dev/null @@ -1,78 +0,0 @@ -// Copyright 2017 David Conran - -#include "IRtimer.h" -#ifndef UNIT_TEST -#include -#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 diff --git a/lib/IRremoteESP8266/src/IRutils.cpp b/lib/IRremoteESP8266/src/IRutils.cpp deleted file mode 100644 index bebb610776..0000000000 --- a/lib/IRremoteESP8266/src/IRutils.cpp +++ /dev/null @@ -1,1266 +0,0 @@ -// Copyright 2017-2021 David Conran - -#include "IRutils.h" -#ifndef UNIT_TEST -#include -#endif - -#define __STDC_LIMIT_MACROS -#include -#include -#include -#ifndef ARDUINO -#include -#endif -#include "IRrecv.h" -#include "IRremoteESP8266.h" -#include "IRsend.h" -#include "IRtext.h" - -// On the ESP8266 platform we need to use a set of ..._P functions -// to handle the strings stored in the flash address space. -#ifndef STRCASECMP -#if defined(ESP8266) -#define STRCASECMP(LHS, RHS) \ - strcasecmp_P(LHS, reinterpret_cast(RHS)) -#else // ESP8266 -#define STRCASECMP strcasecmp -#endif // ESP8266 -#endif // STRCASECMP -#ifndef STRLEN -#if defined(ESP8266) -#define STRLEN(PTR) strlen_P(PTR) -#else // ESP8266 -#define STRLEN(PTR) strlen(PTR) -#endif // ESP8266 -#endif // STRLEN -#ifndef FPSTR -#define FPSTR(X) X -#endif // FPSTR - -/// Reverse the order of the requested least significant nr. of bits. -/// @param[in] input Bit pattern/integer to reverse. -/// @param[in] nbits Nr. of bits to reverse. (LSB -> MSB) -/// @return The reversed bit pattern. -uint64_t reverseBits(uint64_t input, uint16_t nbits) { - if (nbits <= 1) return input; // Reversing <= 1 bits makes no change at all. - // Cap the nr. of bits to rotate to the max nr. of bits in the input. - nbits = std::min(nbits, (uint16_t)(sizeof(input) * 8)); - uint64_t output = 0; - for (uint16_t i = 0; i < nbits; i++) { - output <<= 1; - output |= (input & 1); - input >>= 1; - } - // Merge any remaining unreversed bits back to the top of the reversed bits. - return (input << nbits) | output; -} - -/// Convert a uint64_t (unsigned long long) to a string. -/// Arduino String/toInt/Serial.print() can't handle printing 64 bit values. -/// @param[in] input The value to print -/// @param[in] base The output base. -/// @returns A String representation of the integer. -/// @note Based on Arduino's Print::printNumber() -String uint64ToString(uint64_t input, uint8_t base) { - String result = ""; - // prevent issues if called with base <= 1 - if (base < 2) base = 10; - // Check we have a base that we can actually print. - // i.e. [0-9A-Z] == 36 - if (base > 36) base = 10; - - // Reserve some string space to reduce fragmentation. - // 16 bytes should store a uint64 in hex text which is the likely worst case. - // 64 bytes would be the worst case (base 2). - result.reserve(16); - - do { - char c = input % base; - input /= base; - - if (c < 10) - c += '0'; - else - c += 'A' - 10; - result = c + result; - } while (input); - return result; -} - -/// Convert a int64_t (signed long long) to a string. -/// Arduino String/toInt/Serial.print() can't handle printing 64 bit values. -/// @param[in] input The value to print -/// @param[in] base The output base. -/// @returns A String representation of the integer. -String int64ToString(int64_t input, uint8_t base) { - if (input < 0) { - return String(kDashStr) + uint64ToString(-input, base); - } - return uint64ToString(input, base); -} - -#ifdef ARDUINO -/// Print a uint64_t/unsigned long long to the Serial port -/// Serial.print() can't handle printing long longs. (uint64_t) -/// @param[in] input The value to print -/// @param[in] base The output base. -void serialPrintUint64(uint64_t input, uint8_t base) { - Serial.print(uint64ToString(input, base)); -} -#endif - -/// Convert a C-style string to a decode_type_t. -/// @param[in] str A C-style string containing a protocol name or number. -/// @return A decode_type_t enum. (decode_type_t::UNKNOWN if no match.) -decode_type_t strToDecodeType(const char * const str) { - auto *ptr = reinterpret_cast(kAllProtocolNamesStr); - uint16_t length = STRLEN(ptr); - for (uint16_t i = 0; length; i++) { - if (!STRCASECMP(str, ptr)) return (decode_type_t)i; - ptr += length + 1; - length = STRLEN(ptr); - } - // Handle integer values of the type by converting to a string and back again. - decode_type_t result = strToDecodeType( - typeToString((decode_type_t)atoi(str)).c_str()); - if (result > 0) - return result; - - return decode_type_t::UNKNOWN; -} - -/// Convert a protocol type (enum etc) to a human readable string. -/// @param[in] protocol Nr. (enum) of the protocol. -/// @param[in] isRepeat A flag indicating if it is a repeat message. -/// @return A String containing the protocol name. kUnknownStr if no match. -String typeToString(const decode_type_t protocol, const bool isRepeat) { - String result = ""; - result.reserve(30); // Size of longest protocol name + " (Repeat)" - if (protocol > kLastDecodeType || protocol == decode_type_t::UNKNOWN) { - result = kUnknownStr; - } else { - auto *ptr = reinterpret_cast(kAllProtocolNamesStr); - if (protocol > kLastDecodeType || protocol == decode_type_t::UNKNOWN) { - result = kUnknownStr; - } else { - for (uint16_t i = 0; i <= protocol && STRLEN(ptr); i++) { - if (i == protocol) { - result = FPSTR(ptr); - break; - } - ptr += STRLEN(ptr) + 1; - } - } - } - if (isRepeat) { - result += kSpaceLBraceStr; - result += kRepeatStr; - result += ')'; - } - return result; -} - -/// Does the given protocol use a complex state as part of the decode? -/// @param[in] protocol The decode_type_t protocol we are enquiring about. -/// @return True if the protocol uses a state array. False if just an integer. -bool hasACState(const decode_type_t protocol) { - switch (protocol) { - // This is kept sorted by name - case AMCOR: - case ARGO: - case CORONA_AC: - case DAIKIN: - case DAIKIN128: - case DAIKIN152: - case DAIKIN160: - case DAIKIN176: - case DAIKIN2: - case DAIKIN216: - case ELECTRA_AC: - case FUJITSU_AC: - case GREE: - case HAIER_AC: - case HAIER_AC_YRW02: - case HAIER_AC176: - case HITACHI_AC: - case HITACHI_AC1: - case HITACHI_AC2: - case HITACHI_AC3: - case HITACHI_AC344: - case HITACHI_AC424: - case KELVINATOR: - case MIRAGE: - case MITSUBISHI136: - case MITSUBISHI112: - case MITSUBISHI_AC: - case MITSUBISHI_HEAVY_88: - case MITSUBISHI_HEAVY_152: - case MWM: - case NEOCLIMA: - case PANASONIC_AC: - case RHOSS: - case SAMSUNG_AC: - case SANYO_AC: - case SANYO_AC88: - case SHARP_AC: - case TCL112AC: - case TEKNOPOINT: - case TOSHIBA_AC: - case TROTEC: - case TROTEC_3550: - case VOLTAS: - case WHIRLPOOL_AC: - return true; - default: - return false; - } -} - -/// Return the corrected length of a 'raw' format array structure -/// after over-large values are converted into multiple entries. -/// @param[in] results A ptr to a decode_results structure. -/// @return The corrected length. -uint16_t getCorrectedRawLength(const decode_results * const results) { - uint16_t extended_length = results->rawlen - 1; - for (uint16_t i = 0; i < results->rawlen - 1; i++) { - uint32_t usecs = results->rawbuf[i] * kRawTick; - // Add two extra entries for multiple larger than UINT16_MAX it is. - extended_length += (usecs / (UINT16_MAX + 1)) * 2; - } - return extended_length; -} - -/// Return a String containing the key values of a decode_results structure -/// in a C/C++ code style format. -/// @param[in] results A ptr to a decode_results structure. -/// @return A String containing the code-ified result. -String resultToSourceCode(const decode_results * const results) { - String output = ""; - const uint16_t length = getCorrectedRawLength(results); - const bool hasState = hasACState(results->decode_type); - // Reserve some space for the string to reduce heap fragmentation. - // "uint16_t rawData[9999] = {}; // LONGEST_PROTOCOL\n" = ~55 chars. - // "NNNN, " = ~7 chars on average per raw entry - // Protocols with a `state`: - // "uint8_t state[NN] = {};\n" = ~25 chars - // "0xNN, " = 6 chars per byte. - // Protocols without a `state`: - // " DEADBEEFDEADBEEF\n" - // "uint32_t address = 0xDEADBEEF;\n" - // "uint32_t command = 0xDEADBEEF;\n" - // "uint64_t data = 0xDEADBEEFDEADBEEF;" = ~116 chars max. - output.reserve(55 + (length * 7) + hasState ? 25 + (results->bits / 8) * 6 - : 116); - // Start declaration - output += F("uint16_t "); // variable type - output += F("rawData["); // array name - output += uint64ToString(length, 10); - // array size - output += F("] = {"); // Start declaration - - // Dump data - for (uint16_t i = 1; i < results->rawlen; i++) { - uint32_t usecs; - for (usecs = results->rawbuf[i] * kRawTick; usecs > UINT16_MAX; - usecs -= UINT16_MAX) { - output += uint64ToString(UINT16_MAX); - if (i % 2) - output += F(", 0, "); - else - output += F(", 0, "); - } - output += uint64ToString(usecs, 10); - if (i < results->rawlen - 1) - output += kCommaSpaceStr; // ',' not needed on the last one - if (i % 2 == 0) output += ' '; // Extra if it was even. - } - - // End declaration - output += F("};"); - - // Comment - output += F(" // "); - output += typeToString(results->decode_type, results->repeat); - // Only display the value if the decode type doesn't have an A/C state. - if (!hasState) - output += ' ' + uint64ToString(results->value, 16); - output += F("\n"); - - // Now dump "known" codes - if (results->decode_type != UNKNOWN) { - if (hasState) { -#if DECODE_AC - uint16_t nbytes = results->bits / 8; - output += F("uint8_t state["); - output += uint64ToString(nbytes); - output += F("] = {"); - for (uint16_t i = 0; i < nbytes; i++) { - output += F("0x"); - if (results->state[i] < 0x10) output += '0'; - output += uint64ToString(results->state[i], 16); - if (i < nbytes - 1) output += kCommaSpaceStr; - } - output += F("};\n"); -#endif // DECODE_AC - } else { - // Simple protocols - // Some protocols have an address &/or command. - // NOTE: It will ignore the atypical case when a message has been - // decoded but the address & the command are both 0. - if (results->address > 0 || results->command > 0) { - output += F("uint32_t address = 0x"); - output += uint64ToString(results->address, 16); - output += F(";\n"); - output += F("uint32_t command = 0x"); - output += uint64ToString(results->command, 16); - output += F(";\n"); - } - // Most protocols have data - output += F("uint64_t data = 0x"); - output += uint64ToString(results->value, 16); - output += F(";\n"); - } - } - return output; -} - -/// Dump out the decode_results structure. -/// @param[in] results A ptr to a decode_results structure. -/// @return A String containing the legacy information format. -/// @deprecated This is only for those that want this legacy format. -String resultToTimingInfo(const decode_results * const results) { - String output = ""; - String value = ""; - // Reserve some space for the string to reduce heap fragmentation. - // "Raw Timing[NNNN]:\n\n" = 19 chars - // " +123456, " / "-123456, " = ~12 chars on avg per raw entry. - output.reserve(19 + 12 * results->rawlen); // Should be less than this. - value.reserve(6); // Max value should be 2^17 = 131072 - output += F("Raw Timing["); - output += uint64ToString(results->rawlen - 1, 10); - output += F("]:\n"); - - for (uint16_t i = 1; i < results->rawlen; i++) { - if (i % 2 == 0) - output += kDashStr; // even - else - output += F(" +"); // odd - value = uint64ToString(results->rawbuf[i] * kRawTick); - // Space pad the value till it is at least 6 chars long. - while (value.length() < 6) value = ' ' + value; - output += value; - if (i < results->rawlen - 1) - output += kCommaSpaceStr; // ',' not needed for last one - if (!(i % 8)) output += '\n'; // Newline every 8 entries. - } - output += '\n'; - return output; -} - -/// Convert the decode_results structure's value/state to simple hexadecimal. -/// @param[in] result A ptr to a decode_results structure. -/// @return A String containing the output. -String resultToHexidecimal(const decode_results * const result) { - String output = F("0x"); - // Reserve some space for the string to reduce heap fragmentation. - output.reserve(2 * kStateSizeMax + 2); // Should cover worst cases. - if (hasACState(result->decode_type)) { -#if DECODE_AC - for (uint16_t i = 0; result->bits > i * 8; i++) { - if (result->state[i] < 0x10) output += '0'; // Zero pad - output += uint64ToString(result->state[i], 16); - } -#endif // DECODE_AC - } else { - output += uint64ToString(result->value, 16); - } - return output; -} - -/// Dump out the decode_results structure into a human readable format. -/// @param[in] results A ptr to a decode_results structure. -/// @return A String containing the output. -String resultToHumanReadableBasic(const decode_results * const results) { - String output = ""; - // Reserve some space for the string to reduce heap fragmentation. - // "Protocol : LONGEST_PROTOCOL_NAME (Repeat)\n" - // "Code : 0x (NNNN Bits)\n" = 70 chars - output.reserve(2 * kStateSizeMax + 70); // Should cover most cases. - // Show Encoding standard - output += kProtocolStr; - output += F(" : "); - output += typeToString(results->decode_type, results->repeat); - output += '\n'; - - // Show Code & length - output += kCodeStr; - output += F(" : "); - output += resultToHexidecimal(results); - output += kSpaceLBraceStr; - output += uint64ToString(results->bits); - output += ' '; - output += kBitsStr; - output += F(")\n"); - return output; -} - -/// Convert a decode_results into an array suitable for `sendRaw()`. -/// @param[in] decode A ptr to a decode_results structure that contains a mesg. -/// @return A PTR to a dynamically allocated uint16_t sendRaw compatible array. -/// @note The returned array needs to be delete[]'ed/free()'ed (deallocated) -/// after use by caller. -uint16_t* resultToRawArray(const decode_results * const decode) { - uint16_t *result = new uint16_t[getCorrectedRawLength(decode)]; - if (result != NULL) { // The memory was allocated successfully. - // Convert the decode data. - uint16_t pos = 0; - for (uint16_t i = 1; i < decode->rawlen; i++) { - uint32_t usecs = decode->rawbuf[i] * kRawTick; - while (usecs > UINT16_MAX) { // Keep truncating till it fits. - result[pos++] = UINT16_MAX; - result[pos++] = 0; // A 0 in a sendRaw() array basically means skip. - usecs -= UINT16_MAX; - } - result[pos++] = usecs; - } - } - return result; -} - -/// Sum all the bytes of an array and return the least significant 8-bits of -/// the result. -/// @param[in] start A ptr to the start of the byte array to calculate over. -/// @param[in] length How many bytes to use in the calculation. -/// @param[in] init Starting value of the calculation to use. (Default is 0) -/// @return The 8-bit calculated result of all the bytes and init value. -uint8_t sumBytes(const uint8_t * const start, const uint16_t length, - const uint8_t init) { - uint8_t checksum = init; - const uint8_t *ptr; - for (ptr = start; ptr - start < length; ptr++) checksum += *ptr; - return checksum; -} - -/// Calculate a rolling XOR of all the bytes of an array. -/// @param[in] start A ptr to the start of the byte array to calculate over. -/// @param[in] length How many bytes to use in the calculation. -/// @param[in] init Starting value of the calculation to use. (Default is 0) -/// @return The 8-bit calculated result of all the bytes and init value. -uint8_t xorBytes(const uint8_t * const start, const uint16_t length, - const uint8_t init) { - uint8_t checksum = init; - const uint8_t *ptr; - for (ptr = start; ptr - start < length; ptr++) checksum ^= *ptr; - return checksum; -} - -/// Count the number of bits of a certain type in an array. -/// @param[in] start A ptr to the start of the byte array to calculate over. -/// @param[in] length How many bytes to use in the calculation. -/// @param[in] ones Count the binary nr of `1` bits. False is count the `0`s. -/// @param[in] init Starting value of the calculation to use. (Default is 0) -/// @return The nr. of bits found of the given type found in the array. -uint16_t countBits(const uint8_t * const start, const uint16_t length, - const bool ones, const uint16_t init) { - uint16_t count = init; - for (uint16_t offset = 0; offset < length; offset++) - for (uint8_t currentbyte = *(start + offset); - currentbyte; - currentbyte >>= 1) - if (currentbyte & 1) count++; - if (ones || length == 0) - return count; - else - return (length * 8) - count; -} - -/// Count the number of bits of a certain type in an Integer. -/// @param[in] data The value you want bits counted for. Starting from the LSB. -/// @param[in] length How many bits to use in the calculation? Starts at the LSB -/// @param[in] ones Count the binary nr of `1` bits. False is count the `0`s. -/// @param[in] init Starting value of the calculation to use. (Default is 0) -/// @return The nr. of bits found of the given type found in the Integer. -uint16_t countBits(const uint64_t data, const uint8_t length, const bool ones, - const uint16_t init) { - uint16_t count = init; - uint8_t bitsSoFar = length; - for (uint64_t remainder = data; remainder && bitsSoFar; - remainder >>= 1, bitsSoFar--) - if (remainder & 1) count++; - if (ones || length == 0) - return count; - else - return length - count; -} - -/// Invert/Flip the bits in an Integer. -/// @param[in] data The Integer that will be inverted. -/// @param[in] nbits How many bits are to be inverted. Starting from the LSB. -/// @return An Integer with the appropriate bits inverted/flipped. -uint64_t invertBits(const uint64_t data, const uint16_t nbits) { - // No change if we are asked to invert no bits. - if (nbits == 0) return data; - uint64_t result = ~data; - // If we are asked to invert all the bits or more than we have, it's simple. - if (nbits >= sizeof(data) * 8) return result; - // Mask off any unwanted bits and return the result. - return (result & ((1ULL << nbits) - 1)); -} - -/// Convert degrees Celsius to degrees Fahrenheit. -float celsiusToFahrenheit(const float deg) { return (deg * 9.0) / 5.0 + 32.0; } - -/// Convert degrees Fahrenheit to degrees Celsius. -float fahrenheitToCelsius(const float deg) { return (deg - 32.0) * 5.0 / 9.0; } - -namespace irutils { - /// Create a String with a colon separated "label: value" pair suitable for - /// Humans. - /// @param[in] value The value to come after the label. - /// @param[in] label The label to precede the value. - /// @param[in] precomma Should the output string start with ", " or not? - /// @return The resulting String. - String addLabeledString(const String value, const String label, - const bool precomma) { - String result = ""; - // ", " + ": " = 4 chars - result.reserve(4 + value.length() + label.length()); - if (precomma) result += kCommaSpaceStr; - result += label; - result += kColonSpaceStr; - return result + value; - } - - /// Create a String with a colon separated flag suitable for Humans. - /// e.g. "Power: On" - /// @param[in] value The value to come after the label. - /// @param[in] label The label to precede the value. - /// @param[in] precomma Should the output string start with ", " or not? - /// @return The resulting String. - String addBoolToString(const bool value, const String label, - const bool precomma) { - return addLabeledString(value ? kOnStr : kOffStr, label, precomma); - } - - /// Create a String with a colon separated toggle flag suitable for Humans. - /// e.g. "Light: Toggle", "Light: -" - /// @param[in] toggle The value of the toggle to come after the label. - /// @param[in] label The label to precede the value. - /// @param[in] precomma Should the output string start with ", " or not? - /// @return The resulting String. - String addToggleToString(const bool toggle, const String label, - const bool precomma) { - return addLabeledString(toggle ? kToggleStr : kDashStr, label, precomma); - } - - /// Create a String with a colon separated labeled Integer suitable for - /// Humans. - /// e.g. "Foo: 23" - /// @param[in] value The value to come after the label. - /// @param[in] label The label to precede the value. - /// @param[in] precomma Should the output string start with ", " or not? - /// @return The resulting String. - String addIntToString(const uint16_t value, const String label, - const bool precomma) { - return addLabeledString(uint64ToString(value), label, precomma); - } - - /// Create a String with a colon separated labeled Integer suitable for - /// Humans. - /// e.g. "Foo: 23" - /// @param[in] value The value to come after the label. - /// @param[in] label The label to precede the value. - /// @param[in] precomma Should the output string start with ", " or not? - /// @return The resulting String. - String addSignedIntToString(const int16_t value, const String label, - const bool precomma) { - return addLabeledString(int64ToString(value), label, precomma); - } - - - /// Generate the model string for a given Protocol/Model pair. - /// @param[in] protocol The IR protocol. - /// @param[in] model The model number for that protocol. - /// @return The resulting String. - String modelToStr(const decode_type_t protocol, const int16_t model) { - switch (protocol) { - case decode_type_t::FUJITSU_AC: - switch (model) { - case fujitsu_ac_remote_model_t::ARRAH2E: return kArrah2eStr; - case fujitsu_ac_remote_model_t::ARDB1: return kArdb1Str; - case fujitsu_ac_remote_model_t::ARREB1E: return kArreb1eStr; - case fujitsu_ac_remote_model_t::ARJW2: return kArjw2Str; - case fujitsu_ac_remote_model_t::ARRY4: return kArry4Str; - case fujitsu_ac_remote_model_t::ARREW4E: return kArrew4eStr; - default: return kUnknownStr; - } - break; - case decode_type_t::GREE: - switch (model) { - case gree_ac_remote_model_t::YAW1F: return kYaw1fStr; - case gree_ac_remote_model_t::YBOFB: return kYbofbStr; - default: return kUnknownStr; - } - break; - case decode_type_t::HITACHI_AC1: - switch (model) { - case hitachi_ac1_remote_model_t::R_LT0541_HTA_A: - return kRlt0541htaaStr; - case hitachi_ac1_remote_model_t::R_LT0541_HTA_B: - return kRlt0541htabStr; - default: - return kUnknownStr; - } - break; - case decode_type_t::LG: - case decode_type_t::LG2: - switch (model) { - case lg_ac_remote_model_t::GE6711AR2853M: return kGe6711ar2853mStr; - case lg_ac_remote_model_t::AKB75215403: return kAkb75215403Str; - case lg_ac_remote_model_t::AKB74955603: return kAkb74955603Str; - case lg_ac_remote_model_t::AKB73757604: return kAkb73757604Str; - default: return kUnknownStr; - } - break; - case decode_type_t::PANASONIC_AC: - switch (model) { - case panasonic_ac_remote_model_t::kPanasonicLke: return kLkeStr; - case panasonic_ac_remote_model_t::kPanasonicNke: return kNkeStr; - case panasonic_ac_remote_model_t::kPanasonicDke: return kDkeStr; - case panasonic_ac_remote_model_t::kPanasonicJke: return kJkeStr; - case panasonic_ac_remote_model_t::kPanasonicCkp: return kCkpStr; - case panasonic_ac_remote_model_t::kPanasonicRkr: return kRkrStr; - default: return kUnknownStr; - } - break; - case decode_type_t::SHARP_AC: - switch (model) { - case sharp_ac_remote_model_t::A907: return kA907Str; - case sharp_ac_remote_model_t::A705: return kA705Str; - case sharp_ac_remote_model_t::A903: return kA903Str; - default: return kUnknownStr; - } - break; - case decode_type_t::TCL112AC: - switch (model) { - case tcl_ac_remote_model_t::TAC09CHSD: return kTac09chsdStr; - case tcl_ac_remote_model_t::GZ055BE1: return kGz055be1Str; - default: return kUnknownStr; - } - break; - case decode_type_t::VOLTAS: - switch (model) { - case voltas_ac_remote_model_t::kVoltas122LZF: return k122lzfStr; - default: return kUnknownStr; - } - break; - case decode_type_t::WHIRLPOOL_AC: - switch (model) { - case whirlpool_ac_remote_model_t::DG11J13A: return kDg11j13aStr; - case whirlpool_ac_remote_model_t::DG11J191: return kDg11j191Str; - default: return kUnknownStr; - } - break; - default: return kUnknownStr; - } - } - - /// Create a String of human output for a given protocol model number. - /// e.g. "Model: JKE" - /// @param[in] protocol The IR protocol. - /// @param[in] model The model number for that protocol. - /// @param[in] precomma Should the output string start with ", " or not? - /// @return The resulting String. - String addModelToString(const decode_type_t protocol, const int16_t model, - const bool precomma) { - String result = ""; - // ", Model: NNN (BlahBlahEtc)" = ~40 chars for longest model name. - result.reserve(40); - result += addIntToString(model, kModelStr, precomma); - result += kSpaceLBraceStr; - result += modelToStr(protocol, model); - return result + ')'; - } - - /// Create a String of human output for a given temperature. - /// e.g. "Temp: 25C" - /// @param[in] degrees The temperature in degrees. - /// @param[in] celsius Is the temp Celsius or Fahrenheit. - /// true is C, false is F - /// @param[in] precomma Should the output string start with ", " or not? - /// @return The resulting String. - String addTempToString(const uint16_t degrees, const bool celsius, - const bool precomma) { - String result = addIntToString(degrees, kTempStr, precomma); - result += celsius ? 'C' : 'F'; - return result; - } - - /// Create a String of human output for a given temperature. - /// e.g. "Temp: 25.5C" - /// @param[in] degrees The temperature in degrees. - /// @param[in] celsius Is the temp Celsius or Fahrenheit. - /// true is C, false is F - /// @param[in] precomma Should the output string start with ", " or not? - /// @return The resulting String. - String addTempFloatToString(const float degrees, const bool celsius, - const bool precomma) { - String result = ""; - result.reserve(14); // Assuming ", Temp: XXX.5F" is the largest. - result += addIntToString(degrees, kTempStr, precomma); - // Is it a half degree? - if (((uint16_t)(2 * degrees)) & 1) result += F(".5"); - result += celsius ? 'C' : 'F'; - return result; - } - - /// Create a String of human output for the given operating mode. - /// e.g. "Mode: 1 (Cool)" - /// @param[in] mode The operating mode to display. - /// @param[in] automatic The numeric value for Auto mode. - /// @param[in] cool The numeric value for Cool mode. - /// @param[in] heat The numeric value for Heat mode. - /// @param[in] dry The numeric value for Dry mode. - /// @param[in] fan The numeric value for Fan mode. - /// @return The resulting String. - 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 result = ""; - result.reserve(22); // ", Mode: NNN (UNKNOWN)" - result += addIntToString(mode, kModeStr); - result += kSpaceLBraceStr; - if (mode == automatic) result += kAutoStr; - else if (mode == cool) result += kCoolStr; - else if (mode == heat) result += kHeatStr; - else if (mode == dry) result += kDryStr; - else if (mode == fan) result += kFanStr; - else - result += kUnknownStr; - return result + ')'; - } - - /// Create a String of the 3-letter day of the week from a numerical day of - /// the week. e.g. "Day: 1 (Mon)" - /// @param[in] day_of_week A numerical version of the sequential day of the - /// week. e.g. Saturday = 7 etc. - /// @param[in] offset Days to offset by. - /// e.g. For different day starting the week. - /// @param[in] precomma Should the output string start with ", " or not? - /// @return The resulting String. - String addDayToString(const uint8_t day_of_week, const int8_t offset, - const bool precomma) { - String result = ""; - result.reserve(19); // ", Day: N (UNKNOWN)" - result += addIntToString(day_of_week, kDayStr, precomma); - result += kSpaceLBraceStr; - if ((uint8_t)(day_of_week + offset) < 7) -#if UNIT_TEST - result += String(kThreeLetterDayOfWeekStr).substr( - (day_of_week + offset) * 3, 3); -#else // UNIT_TEST - result += String(kThreeLetterDayOfWeekStr).substring( - (day_of_week + offset) * 3, (day_of_week + offset) * 3 + 3); -#endif // UNIT_TEST - else - result += kUnknownStr; - return result + ')'; - } - - /// Create a String of human output for the given fan speed. - /// e.g. "Fan: 0 (Auto)" - /// @param[in] speed The numeric speed of the fan to display. - /// @param[in] high The numeric value for High speed. - /// @param[in] low The numeric value for Low speed. - /// @param[in] automatic The numeric value for Auto speed. - /// @param[in] quiet The numeric value for Quiet speed. - /// @param[in] medium The numeric value for Medium speed. - /// @param[in] maximum The numeric value for Highest speed. (if > high) - /// @return The resulting String. - 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) { - String result = ""; - result.reserve(21); // ", Fan: NNN (UNKNOWN)" - result += addIntToString(speed, kFanStr); - result += kSpaceLBraceStr; - if (speed == high) result += kHighStr; - else if (speed == low) result += kLowStr; - else if (speed == automatic) result += kAutoStr; - else if (speed == quiet) result += kQuietStr; - else if (speed == medium) result += kMediumStr; - else if (speed == maximum) result += kMaximumStr; - else - result += kUnknownStr; - return result + ')'; - } - - /// Create a String of human output for the given horizontal swing setting. - /// e.g. "Swing(H): 0 (Auto)" - /// @param[in] position The numeric position of the swing to display. - /// @param[in] automatic The numeric value for Auto position. - /// @param[in] maxleft The numeric value for most left position. - /// @param[in] left The numeric value for Left position. - /// @param[in] middle The numeric value for Middle position. - /// @param[in] right The numeric value for Right position. - /// @param[in] maxright The numeric value for most right position. - /// @param[in] off The numeric value for Off position. - /// @param[in] leftright The numeric value for "left right" position. - /// @param[in] rightleft The numeric value for "right left" position. - /// @param[in] threed The numeric value for 3D setting. - /// @param[in] wide The numeric value for Wide position. - /// @return The resulting String. - 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 result = ""; - result.reserve(30); // ", Swing(H): NNN (Left Right)" - result += addIntToString(position, kSwingHStr); - result += kSpaceLBraceStr; - if (position == automatic) { - result += kAutoStr; - } else if (position == left) { - result += kLeftStr; - } else if (position == middle) { - result += kMiddleStr; - } else if (position == right) { - result += kRightStr; - } else if (position == maxleft) { - result += kMaxLeftStr; - } else if (position == maxright) { - result += kMaxRightStr; - } else if (position == off) { - result += kOffStr; - } else if (position == leftright) { - result += kLeftStr; - result += ' '; - result += kRightStr; - } else if (position == rightleft) { - result += kRightStr; - result += ' '; - result += kLeftStr; - } else if (position == threed) { - result += k3DStr; - } else if (position == wide) { - result += kWideStr; - } else { - result += kUnknownStr; - } - return result + ')'; - } - - /// Create a String of human output for the given vertical swing setting. - /// e.g. "Swing(V): 0 (Auto)" - /// @param[in] position The numeric position of the swing to display. - /// @param[in] automatic The numeric value for Auto position. - /// @param[in] highest The numeric value for Highest position. - /// @param[in] high The numeric value for High position. - /// @param[in] uppermiddle The numeric value for Upper Middle position. - /// @param[in] middle The numeric value for Middle position. - /// @param[in] lowermiddle The numeric value for Lower Middle position. - /// @param[in] low The numeric value for Low position. - /// @param[in] lowest The numeric value for Low position. - /// @param[in] off The numeric value for Off position. - /// @param[in] swing The numeric value for Swing setting. - /// @param[in] breeze The numeric value for Breeze setting. - /// @param[in] circulate The numeric value for Circulate setting. - /// @return The resulting String. - 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 result = ""; - result.reserve(31); // ", Swing(V): NNN (Upper Middle)" - result += addIntToString(position, kSwingVStr); - result += kSpaceLBraceStr; - if (position == automatic) { - result += kAutoStr; - } else if (position == highest) { - result += kHighestStr; - } else if (position == high) { - result += kHighStr; - } else if (position == middle) { - result += kMiddleStr; - } else if (position == low) { - result += kLowStr; - } else if (position == lowest) { - result += kLowestStr; - } else if (position == off) { - result += kOffStr; - } else if (position == uppermiddle) { - result += kUpperStr; - result += ' '; - result += kMiddleStr; - } else if (position == lowermiddle) { - result += kLowerStr; - result += ' '; - result += kMiddleStr; - } else if (position == swing) { - result += kSwingStr; - } else if (position == breeze) { - result += kBreezeStr; - } else if (position == circulate) { - result += kCirculateStr; - } else { - result += kUnknownStr; - } - return result + ')'; - } - - /// Escape any special HTML (unsafe) characters in a string. e.g. anti-XSS. - /// @param[in] unescaped A String containing text to make HTML safe. - /// @return A string that is HTML safe. - String htmlEscape(const String unescaped) { - String result = ""; - uint16_t ulen = unescaped.length(); - result.reserve(ulen); // The result will be at least the size of input. - for (size_t i = 0; i < ulen; i++) { - char c = unescaped[i]; - switch (c) { - // ';!-"<>=&#{}() are all unsafe. - case '\'': result += F("'"); break; - case ';': result += F(";"); break; - case '!': result += F("!"); break; - case '-': result += F("‐"); break; - case '\"': result += F("""); break; - case '<': result += F("<"); break; - case '>': result += F(">"); break; - case '=': result += F("&#equals;"); break; - case '&': result += F("&"); break; - case '#': result += F("#"); break; - case '{': result += F("{"); break; - case '}': result += F("}"); break; - case '(': result += F("("); break; - case ')': result += F(")"); break; - default: result += c; - } - } - return result; - } - - /// Convert a nr. of milliSeconds into a Human-readable string. - /// e.g. "1 Day 6 Hours 34 Minutes 17 Seconds" - /// @param[in] msecs Nr. of milliSeconds (ms). - /// @return A human readable string. - String msToString(uint32_t const msecs) { - uint32_t totalseconds = msecs / 1000; - if (totalseconds == 0) return kNowStr; - - // Note: uint32_t can only hold up to 45 days, so uint8_t is safe. - uint8_t days = totalseconds / (60 * 60 * 24); - uint8_t hours = (totalseconds / (60 * 60)) % 24; - uint8_t minutes = (totalseconds / 60) % 60; - uint8_t seconds = totalseconds % 60; - - String result = ""; - result.reserve(42); // "99 Days, 23 Hours, 59 Minutes, 59 Seconds" - if (days) - result += uint64ToString(days) + ' ' + String((days > 1) ? kDaysStr - : kDayStr); - if (hours) { - if (result.length()) result += ' '; - result += uint64ToString(hours) + ' ' + String((hours > 1) ? kHoursStr - : kHourStr); - } - if (minutes) { - if (result.length()) result += ' '; - result += uint64ToString(minutes) + ' ' + String( - (minutes > 1) ? kMinutesStr : kMinuteStr); - } - if (seconds) { - if (result.length()) result += ' '; - result += uint64ToString(seconds) + ' ' + String( - (seconds > 1) ? kSecondsStr : kSecondStr); - } - return result; - } - - /// Convert a nr. of minutes into a 24h clock format Human-readable string. - /// e.g. "23:59" - /// @param[in] mins Nr. of Minutes. - /// @return A human readable string. - String minsToString(const uint16_t mins) { - String result = ""; - result.reserve(5); // 23:59 is the typical worst case. - if (mins / 60 < 10) result += '0'; // Zero pad the hours - result += uint64ToString(mins / 60) + kTimeSep; - if (mins % 60 < 10) result += '0'; // Zero pad the minutes. - result += uint64ToString(mins % 60); - return result; - } - - /// Sum all the nibbles together in a series of bytes. - /// @param[in] start A ptr to the start of the byte array to calculate over. - /// @param[in] length How many bytes to use in the calculation. - /// @param[in] init Starting value of the calculation to use. (Default is 0) - /// @return The 8-bit calculated result of all the bytes and init value. - uint8_t sumNibbles(const uint8_t * const start, const uint16_t length, - const uint8_t init) { - uint8_t sum = init; - const uint8_t *ptr; - for (ptr = start; ptr - start < length; ptr++) - sum += (*ptr >> 4) + (*ptr & 0xF); - return sum; - } - - /// Sum all the nibbles together in an integer. - /// @param[in] data The integer to be summed. - /// @param[in] count The number of nibbles to sum. Starts from LSB. Max of 16. - /// @param[in] init Starting value of the calculation to use. (Default is 0) - /// @param[in] nibbleonly true, the result is 4 bits. false, it's 8 bits. - /// @return The 4/8-bit calculated result of all the nibbles and init value. - uint8_t sumNibbles(const uint64_t data, const uint8_t count, - const uint8_t init, const bool nibbleonly) { - uint8_t sum = init; - uint64_t copy = data; - const uint8_t nrofnibbles = (count < 16) ? count : (64 / 4); - for (uint8_t i = 0; i < nrofnibbles; i++, copy >>= 4) sum += copy & 0xF; - return nibbleonly ? sum & 0xF : sum; - } - - /// Convert a byte of Binary Coded Decimal(BCD) into an Integer. - /// @param[in] bcd The BCD value. - /// @return A normal Integer value. - uint8_t bcdToUint8(const uint8_t bcd) { - if (bcd > 0x99) return 255; // Too big. - return (bcd >> 4) * 10 + (bcd & 0xF); - } - - /// Convert an Integer into a byte of Binary Coded Decimal(BCD). - /// @param[in] integer The number to convert. - /// @return An 8-bit BCD value. - uint8_t uint8ToBcd(const uint8_t integer) { - if (integer > 99) return 255; // Too big. - return ((integer / 10) << 4) + (integer % 10); - } - - /// Return the value of `position`th bit of an Integer. - /// @param[in] data Value to be examined. - /// @param[in] position Nr. of the Nth bit to be examined. `0` is the LSB. - /// @param[in] size Nr. of bits in data. - /// @return The bit's value. - bool getBit(const uint64_t data, const uint8_t position, const uint8_t size) { - if (position >= size) return false; // Outside of range. - return data & (1ULL << position); - } - - /// Return the value of `position`th bit of an Integer. - /// @param[in] data Value to be examined. - /// @param[in] position Nr. of the Nth bit to be examined. `0` is the LSB. - /// @return The bit's value. - bool getBit(const uint8_t data, const uint8_t position) { - if (position >= 8) return false; // Outside of range. - return data & (1 << position); - } - - /// Return the value of an Integer with the `position`th bit changed. - /// @param[in] data Value to be changed. - /// @param[in] position Nr. of the bit to be changed. `0` is the LSB. - /// @param[in] on Value to set the position'th bit to. - /// @param[in] size Nr. of bits in data. - /// @return A suitably modified integer. - uint64_t setBit(const uint64_t data, const uint8_t position, const bool on, - const uint8_t size) { - if (position >= size) return data; // Outside of range. - uint64_t mask = 1ULL << position; - if (on) - return data | mask; - else - return data & ~mask; - } - - /// Return the value of an Integer with the `position`th bit changed. - /// @param[in] data Value to be changed. - /// @param[in] position Nr. of the bit to be changed. `0` is the LSB. - /// @param[in] on Value to set the position'th bit to. - /// @return A suitably modified integer. - uint8_t setBit(const uint8_t data, const uint8_t position, const bool on) { - if (position >= 8) return data; // Outside of range. - uint8_t mask = 1 << position; - if (on) - return data | mask; - else - return data & ~mask; - } - - /// Alter the value of an Integer with the `position`th bit changed. - /// @param[in,out] data A pointer to the 8-bit integer to be changed. - /// @param[in] position Nr. of the bit to be changed. `0` is the LSB. - /// @param[in] on Value to set the position'th bit to. - void setBit(uint8_t * const data, const uint8_t position, const bool on) { - uint8_t mask = 1 << position; - if (on) - *data |= mask; - else - *data &= ~mask; - } - - /// Alter the value of an Integer with the `position`th bit changed. - /// @param[in,out] data A pointer to the 32-bit integer to be changed. - /// @param[in] position Nr. of the bit to be changed. `0` is the LSB. - /// @param[in] on Value to set the position'th bit to. - void setBit(uint32_t * const data, const uint8_t position, const bool on) { - uint32_t mask = (uint32_t)1 << position; - if (on) - *data |= mask; - else - *data &= ~mask; - } - - /// Alter the value of an Integer with the `position`th bit changed. - /// @param[in,out] data A pointer to the 64-bit integer to be changed. - /// @param[in] position Nr. of the bit to be changed. `0` is the LSB. - /// @param[in] on Value to set the position'th bit to. - void setBit(uint64_t * const data, const uint8_t position, const bool on) { - uint64_t mask = (uint64_t)1 << position; - if (on) - *data |= mask; - else - *data &= ~mask; - } - - /// Alter an uint8_t value by overwriting an arbitrary given number of bits. - /// @param[in,out] dst A pointer to the value to be changed. - /// @param[in] offset Nr. of bits from the Least Significant Bit to be ignored - /// @param[in] nbits Nr of bits of data to be placed into the destination. - /// @param[in] data The value to be placed. - void setBits(uint8_t * const dst, const uint8_t offset, const uint8_t nbits, - const uint8_t data) { - if (offset >= 8 || !nbits) return; // Short circuit as it won't change. - // Calculate the mask for the supplied value. - uint8_t mask = UINT8_MAX >> (8 - ((nbits > 8) ? 8 : nbits)); - // Calculate the mask & clear the space for the data. - // Clear the destination bits. - *dst &= ~(uint8_t)(mask << offset); - // Merge in the data. - *dst |= ((data & mask) << offset); - } - - /// Alter an uint32_t value by overwriting an arbitrary given number of bits. - /// @param[in,out] dst A pointer to the value to be changed. - /// @param[in] offset Nr. of bits from the Least Significant Bit to be ignored - /// @param[in] nbits Nr of bits of data to be placed into the destination. - /// @param[in] data The value to be placed. - void setBits(uint32_t * const dst, const uint8_t offset, const uint8_t nbits, - const uint32_t data) { - if (offset >= 32 || !nbits) return; // Short circuit as it won't change. - // Calculate the mask for the supplied value. - uint32_t mask = UINT32_MAX >> (32 - ((nbits > 32) ? 32 : nbits)); - // Calculate the mask & clear the space for the data. - // Clear the destination bits. - *dst &= ~(mask << offset); - // Merge in the data. - *dst |= ((data & mask) << offset); - } - - /// Alter an uint64_t value by overwriting an arbitrary given number of bits. - /// @param[in,out] dst A pointer to the value to be changed. - /// @param[in] offset Nr. of bits from the Least Significant Bit to be ignored - /// @param[in] nbits Nr of bits of data to be placed into the destination. - /// @param[in] data The value to be placed. - void setBits(uint64_t * const dst, const uint8_t offset, const uint8_t nbits, - const uint64_t data) { - if (offset >= 64 || !nbits) return; // Short circuit as it won't change. - // Calculate the mask for the supplied value. - uint64_t mask = UINT64_MAX >> (64 - ((nbits > 64) ? 64 : nbits)); - // Calculate the mask & clear the space for the data. - // Clear the destination bits. - *dst &= ~(mask << offset); - // Merge in the data. - *dst |= ((data & mask) << offset); - } - - /// Create byte pairs where the second byte of the pair is a bit - /// inverted/flipped copy of the first/previous byte of the pair. - /// @param[in,out] ptr A pointer to the start of array to modify. - /// @param[in] length The byte size of the array. - /// @note A length of `<= 1` will do nothing. - /// @return A ptr to the modified array. - uint8_t * invertBytePairs(uint8_t *ptr, const uint16_t length) { - for (uint16_t i = 1; i < length; i += 2) { - // Code done this way to avoid a compiler warning bug. - uint8_t inv = ~*(ptr + i - 1); - *(ptr + i) = inv; - } - return ptr; - } - - /// Check an array to see if every second byte of a pair is a bit - /// inverted/flipped copy of the first/previous byte of the pair. - /// @param[in] ptr A pointer to the start of array to check. - /// @param[in] length The byte size of the array. - /// @note A length of `<= 1` will always return true. - /// @return true, if every second byte is inverted. Otherwise false. - bool checkInvertedBytePairs(const uint8_t * const ptr, - const uint16_t length) { - for (uint16_t i = 1; i < length; i += 2) { - // Code done this way to avoid a compiler warning bug. - uint8_t inv = ~*(ptr + i - 1); - if (*(ptr + i) != inv) return false; - } - return true; - } - - /// Perform a low level bit manipulation sanity check for the given cpu - /// architecture and the compiler operation. Calls to this should return - /// 0 if everything is as expected, anything else means the library won't work - /// as expected. - /// @return A bit mask value of potential issues. - /// 0: (e.g. 0b00000000) Everything appears okay. - /// 0th bit set: (0b1) Unexpected bit field/packing encountered. - /// Try a different compiler. - /// 1st bit set: (0b10) Unexpected Endianness. Try a different compiler flag - /// or use a CPU different architecture. - /// e.g. A result of 3 (0b11) would mean both a bit field and an Endianness - /// issue has been found. - uint8_t lowLevelSanityCheck(void) { - const uint64_t kExpectedBitFieldResult = 0x8000012340000039ULL; - volatile uint32_t EndianTest = 0x12345678; - const uint8_t kBitFieldError = 0b01; - const uint8_t kEndiannessError = 0b10; - uint8_t result = 0; - union bitpackdata { - struct { - uint64_t lowestbit:1; // 0th bit - uint64_t next7bits:7; // 1-7th bits - uint64_t _unused_1:20; // 8-27th bits - // Cross the 32 bit boundary. - uint64_t crossbits:16; // 28-43rd bits - uint64_t _usused_2:18; // 44-61st bits - uint64_t highest2bits:2; // 62-63rd bits - }; - uint64_t all; - }; - - bitpackdata data; - data.lowestbit = true; - data.next7bits = 0b0011100; // 0x1C - data._unused_1 = 0; - data.crossbits = 0x1234; - data._usused_2 = 0; - data.highest2bits = 0b10; // 2 - - if (data.all != kExpectedBitFieldResult) result |= kBitFieldError; - // Check that we are using Little Endian for integers -#if defined(BYTE_ORDER) && defined(LITTLE_ENDIAN) - if (BYTE_ORDER != LITTLE_ENDIAN) result |= kEndiannessError; -#endif -#if defined(__IEEE_BIG_ENDIAN) || defined(__IEEE_BYTES_BIG_ENDIAN) - result |= kEndiannessError; -#endif - // Brute force check for little endian. - if (*((uint8_t*)(&EndianTest)) != 0x78) // NOLINT(readability/casting) - result |= kEndiannessError; - return result; - } -} // namespace irutils diff --git a/lib/IRremoteESP8266/src/IRutils.h b/lib/IRremoteESP8266/src/IRutils.h index 61fe8b2699..7bd6af56c1 100644 --- a/lib/IRremoteESP8266/src/IRutils.h +++ b/lib/IRremoteESP8266/src/IRutils.h @@ -11,8 +11,8 @@ #ifndef ARDUINO #include #endif -#include "IRremoteESP8266.h" -#include "IRrecv.h" +#include "../src/IRremoteESP8266.h" +#include "../src/IRrecv.h" const uint8_t kNibbleSize = 4; const uint8_t kLowNibble = 0; diff --git a/lib/IRremoteESP8266/src/i18n.h b/lib/IRremoteESP8266/src/i18n.h index 27ac4d714b..260153b37c 100644 --- a/lib/IRremoteESP8266/src/i18n.h +++ b/lib/IRremoteESP8266/src/i18n.h @@ -3,7 +3,7 @@ #ifndef I18N_H_ #define I18N_H_ -#include "IRremoteESP8266.h" +#include "../src/IRremoteESP8266.h" // Load the appropriate locale header file. #ifndef _IR_LOCALE_ diff --git a/lib/IRremoteESP8266/src/ir_Airwell.cpp b/lib/IRremoteESP8266/src/ir_Airwell.cpp deleted file mode 100644 index 26053442ee..0000000000 --- a/lib/IRremoteESP8266/src/ir_Airwell.cpp +++ /dev/null @@ -1,286 +0,0 @@ -// Copyright 2020 David Conran -#include "ir_Airwell.h" -#include -#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; -} diff --git a/lib/IRremoteESP8266/src/ir_Airwell.h b/lib/IRremoteESP8266/src/ir_Airwell.h index 814719f1f1..68e5100dbe 100644 --- a/lib/IRremoteESP8266/src/ir_Airwell.h +++ b/lib/IRremoteESP8266/src/ir_Airwell.h @@ -17,10 +17,10 @@ #ifndef UNIT_TEST #include #endif -#include "IRremoteESP8266.h" -#include "IRsend.h" +#include "../src/IRremoteESP8266.h" +#include "../src/IRsend.h" #ifdef UNIT_TEST -#include "IRsend_test.h" +#include "../src/IRsend_test.h" #endif /// Native representation of a Airwell A/C message. diff --git a/lib/IRremoteESP8266/src/ir_Aiwa.cpp b/lib/IRremoteESP8266/src/ir_Aiwa.cpp deleted file mode 100644 index 266fe67d68..0000000000 --- a/lib/IRremoteESP8266/src/ir_Aiwa.cpp +++ /dev/null @@ -1,105 +0,0 @@ -// 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 diff --git a/lib/IRremoteESP8266/src/ir_Amcor.cpp b/lib/IRremoteESP8266/src/ir_Amcor.cpp deleted file mode 100644 index c2aea5cdd3..0000000000 --- a/lib/IRremoteESP8266/src/ir_Amcor.cpp +++ /dev/null @@ -1,354 +0,0 @@ -// 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 -#include -#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; -} diff --git a/lib/IRremoteESP8266/src/ir_Amcor.h b/lib/IRremoteESP8266/src/ir_Amcor.h index 62ea50d9d8..21c4ff2604 100644 --- a/lib/IRremoteESP8266/src/ir_Amcor.h +++ b/lib/IRremoteESP8266/src/ir_Amcor.h @@ -17,10 +17,10 @@ #ifndef UNIT_TEST #include #endif -#include "IRremoteESP8266.h" -#include "IRsend.h" +#include "../src/IRremoteESP8266.h" +#include "../src/IRsend.h" #ifdef UNIT_TEST -#include "IRsend_test.h" +#include "../src/IRsend_test.h" #endif diff --git a/lib/IRremoteESP8266/src/ir_Argo.cpp b/lib/IRremoteESP8266/src/ir_Argo.cpp deleted file mode 100644 index e1df28efbb..0000000000 --- a/lib/IRremoteESP8266/src/ir_Argo.cpp +++ /dev/null @@ -1,470 +0,0 @@ -// Copyright 2017 Schmolders -// Copyright 2019 crankyoldgit -/// @file -/// @brief Argo A/C protocol. -/// Controls an Argo Ulisse 13 DCI A/C - -#include "ir_Argo.h" -#include -#include -#ifndef UNIT_TEST -#include -#endif // UNIT_TEST -#include "IRremoteESP8266.h" -#include "IRtext.h" -#include "IRutils.h" - -// Constants -// using SPACE modulation. MARK is always const 400u -const uint16_t kArgoHdrMark = 6400; -const uint16_t kArgoHdrSpace = 3300; -const uint16_t kArgoBitMark = 400; -const uint16_t kArgoOneSpace = 2200; -const uint16_t kArgoZeroSpace = 900; -const uint32_t kArgoGap = kDefaultMessageGap; // Made up value. Complete guess. - -using irutils::addBoolToString; -using irutils::addIntToString; -using irutils::addLabeledString; -using irutils::addModeToString; -using irutils::addTempToString; - -#if SEND_ARGO -/// Send a Argo A/C formatted message. -/// Status: BETA / Probably works. -/// @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::sendArgo(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 < kArgoStateLength) return; - // TODO(kaschmo): validate - sendGeneric(kArgoHdrMark, kArgoHdrSpace, kArgoBitMark, kArgoOneSpace, - kArgoBitMark, kArgoZeroSpace, 0, 0, // No Footer. - data, nbytes, 38, false, repeat, kDutyDefault); -} -#endif // SEND_ARGO - -/// 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? -IRArgoAC::IRArgoAC(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 IRArgoAC::begin(void) { _irsend.begin(); } - -#if SEND_ARGO -/// Send the current internal state as an IR message. -/// @param[in] repeat Nr. of times the message will be repeated. -void IRArgoAC::send(const uint16_t repeat) { - _irsend.sendArgo(getRaw(), kArgoStateLength, repeat); -} -#endif // SEND_ARGO - -/// 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. -uint8_t IRArgoAC::calcChecksum(const uint8_t state[], const uint16_t length) { - // Corresponds to byte 11 being constant 0b01 - // Only add up bytes to 9. byte 10 is 0b01 constant anyway. - // Assume that argo array is MSB first (left) - return sumBytes(state, length - 2, 2); -} - -/// 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 IRArgoAC::validChecksum(const uint8_t state[], const uint16_t length) { - return ((state[length - 2] >> 2) + (state[length - 1] << 6)) == - IRArgoAC::calcChecksum(state, length); -} - -/// Update the checksum for the internal state. -void IRArgoAC::checksum(void) { - uint8_t sum = IRArgoAC::calcChecksum(_.raw, kArgoStateLength); - // Append sum to end of array - // Set const part of checksum bit 10 - _.raw[10] = 0b00000010; - _.Sum = sum; -} - -/// Reset the internals of the object to a known good state. -void IRArgoAC::stateReset(void) { - for (uint8_t i = 0; i < kArgoStateLength; i++) _.raw[i] = 0x0; - - // Argo Message. Store MSB left. - // Default message: - _.raw[0] = 0b10101100; // LSB first (as sent) 0b00110101; //const preamble - _.raw[1] = 0b11110101; // LSB first: 0b10101111; //const preamble - // Keep payload 2-9 at zero - _.raw[10] = 0b00000010; // Const 01 - _.Sum = 0; - - off(); - setTemp(20); - setRoomTemp(25); - setMode(kArgoAuto); - setFan(kArgoFanAuto); -} - -/// 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* IRArgoAC::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 IRArgoAC::setRaw(const uint8_t state[]) { - std::memcpy(_.raw, state, kArgoStateLength); -} - -/// Set the internal state to have the power on. -void IRArgoAC::on(void) { setPower(true); } - -/// Set the internal state to have the power off. -void IRArgoAC::off(void) { setPower(false); } - -/// Set the internal state to have the desired power. -/// @param[in] on The desired power state. -void IRArgoAC::setPower(const bool on) { - _.Power = on; -} - -/// Get the power setting from the internal state. -/// @return A boolean indicating the power setting. -bool IRArgoAC::getPower(void) const { return _.Power; } - -/// Control the current Max setting. (i.e. Turbo) -/// @param[in] on The desired setting. -void IRArgoAC::setMax(const bool on) { - _.Max = on; -} - -/// Is the Max (i.e. Turbo) setting on? -/// @return The current value. -bool IRArgoAC::getMax(void) const { return _.Max; } - -/// Set the temperature. -/// @param[in] degrees The temperature in degrees celsius. -/// @note Sending 0 equals +4 -void IRArgoAC::setTemp(const uint8_t degrees) { - uint8_t temp = std::max(kArgoMinTemp, degrees); - // delta 4 degrees. "If I want 12 degrees, I need to send 8" - temp = std::min(kArgoMaxTemp, temp) - kArgoTempDelta; - // mask out bits - // argo[13] & 0x00000100; // mask out ON/OFF Bit - _.Temp = temp; -} - -/// Get the current temperature setting. -/// @return The current setting for temp. in degrees celsius. -uint8_t IRArgoAC::getTemp(void) const { - return _.Temp + kArgoTempDelta; -} - -/// Set the speed of the fan. -/// @param[in] fan The desired setting. -void IRArgoAC::setFan(const uint8_t fan) { - _.Fan = std::min(fan, kArgoFan3); -} - -/// Get the current fan speed setting. -/// @return The current fan speed. -uint8_t IRArgoAC::getFan(void) const { - return _.Fan; -} - -/// Set the flap position. i.e. Swing. -/// @warning Not yet working! -/// @param[in] flap The desired setting. -void IRArgoAC::setFlap(const uint8_t flap) { - flap_mode = flap; - // TODO(kaschmo): set correct bits for flap mode -} - -/// Get the flap position. i.e. Swing. -/// @warning Not yet working! -/// @return The current flap setting. -uint8_t IRArgoAC::getFlap(void) const { return flap_mode; } - -/// Get the current operation mode setting. -/// @return The current operation mode. -uint8_t IRArgoAC::getMode(void) const { - return _.Mode; -} - -/// Set the desired operation mode. -/// @param[in] mode The desired operation mode. -void IRArgoAC::setMode(const uint8_t mode) { - switch (mode) { - case kArgoCool: - case kArgoDry: - case kArgoAuto: - case kArgoOff: - case kArgoHeat: - case kArgoHeatAuto: - _.Mode = mode; - return; - default: - _.Mode = kArgoAuto; - } -} - -/// Turn on/off the Night mode. i.e. Sleep. -/// @param[in] on The desired setting. -void IRArgoAC::setNight(const bool on) { - _.Night = on; -} - -/// Get the status of Night mode. i.e. Sleep. -/// @return true if on, false if off. -bool IRArgoAC::getNight(void) const { return _.Night; } - -/// Turn on/off the iFeel mode. -/// @param[in] on The desired setting. -void IRArgoAC::setiFeel(const bool on) { - _.iFeel = on; -} - -/// Get the status of iFeel mode. -/// @return true if on, false if off. -bool IRArgoAC::getiFeel(void) const { return _.iFeel; } - -/// Set the time for the A/C -/// @warning Not yet working! -void IRArgoAC::setTime(void) { - // TODO(kaschmo): use function call from checksum to set time first -} - -/// Set the value for the current room temperature. -/// @param[in] degrees The temperature in degrees celsius. -void IRArgoAC::setRoomTemp(const uint8_t degrees) { - uint8_t temp = std::min(degrees, kArgoMaxRoomTemp); - temp = std::max(temp, kArgoTempDelta) - kArgoTempDelta; - _.RoomTemp = temp; -} - -/// Get the currently stored value for the room temperature setting. -/// @return The current setting for the room temp. in degrees celsius. -uint8_t IRArgoAC::getRoomTemp(void) const { - return _.RoomTemp + kArgoTempDelta; -} - -/// 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 IRArgoAC::convertMode(const stdAc::opmode_t mode) { - switch (mode) { - case stdAc::opmode_t::kCool: - return kArgoCool; - case stdAc::opmode_t::kHeat: - return kArgoHeat; - case stdAc::opmode_t::kDry: - return kArgoDry; - case stdAc::opmode_t::kOff: - return kArgoOff; - // No fan mode. - default: - return kArgoAuto; - } -} - -/// 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 IRArgoAC::convertFan(const stdAc::fanspeed_t speed) { - switch (speed) { - case stdAc::fanspeed_t::kMin: - case stdAc::fanspeed_t::kLow: - return kArgoFan1; - case stdAc::fanspeed_t::kMedium: - return kArgoFan2; - case stdAc::fanspeed_t::kHigh: - case stdAc::fanspeed_t::kMax: - return kArgoFan3; - default: - return kArgoFanAuto; - } -} - -/// 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 IRArgoAC::convertSwingV(const stdAc::swingv_t position) { - switch (position) { - case stdAc::swingv_t::kHighest: - return kArgoFlapFull; - case stdAc::swingv_t::kHigh: - return kArgoFlap5; - case stdAc::swingv_t::kMiddle: - return kArgoFlap4; - case stdAc::swingv_t::kLow: - return kArgoFlap3; - case stdAc::swingv_t::kLowest: - return kArgoFlap1; - default: - return kArgoFlapAuto; - } -} - -/// 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 IRArgoAC::toCommonMode(const uint8_t mode) { - switch (mode) { - case kArgoCool: return stdAc::opmode_t::kCool; - case kArgoHeat: return stdAc::opmode_t::kHeat; - case kArgoDry: return stdAc::opmode_t::kDry; - // No fan mode. - 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 IRArgoAC::toCommonFanSpeed(const uint8_t speed) { - switch (speed) { - case kArgoFan3: return stdAc::fanspeed_t::kMax; - case kArgoFan2: return stdAc::fanspeed_t::kMedium; - case kArgoFan1: 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 IRArgoAC::toCommon(void) const { - stdAc::state_t result; - result.protocol = decode_type_t::ARGO; - result.power = _.Power; - result.mode = toCommonMode(_.Mode); - result.celsius = true; - result.degrees = getTemp(); - result.fanspeed = toCommonFanSpeed(_.Fan); - result.turbo = _.Max; - result.sleep = _.Night ? 0 : -1; - // Not supported. - result.model = -1; // Not supported. - 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 IRArgoAC::toString(void) const { - String result = ""; - result.reserve(100); // Reserve some heap for the string to reduce fragging. - result += addBoolToString(_.Power, kPowerStr, false); - result += addIntToString(_.Mode, kModeStr); - result += kSpaceLBraceStr; - switch (_.Mode) { - case kArgoAuto: - result += kAutoStr; - break; - case kArgoCool: - result += kCoolStr; - break; - case kArgoHeat: - result += kHeatStr; - break; - case kArgoDry: - result += kDryStr; - break; - case kArgoHeatAuto: - result += kHeatStr; - result += ' '; - result += kAutoStr; - break; - case kArgoOff: - result += kOffStr; - break; - default: - result += kUnknownStr; - } - result += ')'; - result += addIntToString(_.Fan, kFanStr); - result += kSpaceLBraceStr; - switch (_.Fan) { - case kArgoFanAuto: - result += kAutoStr; - break; - case kArgoFan3: - result += kMaxStr; - break; - case kArgoFan1: - result += kMinStr; - break; - case kArgoFan2: - result += kMedStr; - break; - default: - result += kUnknownStr; - } - result += ')'; - result += addTempToString(getTemp()); - result += kCommaSpaceStr; - result += kRoomStr; - result += ' '; - result += addTempToString(getRoomTemp(), true, false); - result += addBoolToString(_.Max, kMaxStr); - result += addBoolToString(_.iFeel, kIFeelStr); - result += addBoolToString(_.Night, kNightStr); - return result; -} - -#if DECODE_ARGO -/// Decode the supplied Argo 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 This decoder is based soley off sendArgo(). We have no actual captures -/// to test this against. If you have one of these units, please let us know. -bool IRrecv::decodeArgo(decode_results *results, uint16_t offset, - const uint16_t nbits, - const bool strict) { - if (strict && nbits != kArgoBits) return false; - - // Match Header + Data - if (!matchGeneric(results->rawbuf + offset, results->state, - results->rawlen - offset, nbits, - kArgoHdrMark, kArgoHdrSpace, - kArgoBitMark, kArgoOneSpace, - kArgoBitMark, kArgoZeroSpace, - 0, 0, // Footer (None, allegedly. This seems very wrong.) - true, _tolerance, 0, false)) return false; - - // Compliance - // Verify we got a valid checksum. - if (strict && !IRArgoAC::validChecksum(results->state)) return false; - // Success - results->decode_type = decode_type_t::ARGO; - 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_ARGO diff --git a/lib/IRremoteESP8266/src/ir_Argo.h b/lib/IRremoteESP8266/src/ir_Argo.h index 6ceb58e421..bc1844789a 100644 --- a/lib/IRremoteESP8266/src/ir_Argo.h +++ b/lib/IRremoteESP8266/src/ir_Argo.h @@ -11,10 +11,10 @@ #ifndef UNIT_TEST #include #endif -#include "IRremoteESP8266.h" -#include "IRsend.h" +#include "../src/IRremoteESP8266.h" +#include "../src/IRsend.h" #ifdef UNIT_TEST -#include "IRsend_test.h" +#include "../src/IRsend_test.h" #endif diff --git a/lib/IRremoteESP8266/src/ir_Arris.cpp b/lib/IRremoteESP8266/src/ir_Arris.cpp deleted file mode 100644 index 5b39808c8f..0000000000 --- a/lib/IRremoteESP8266/src/ir_Arris.cpp +++ /dev/null @@ -1,123 +0,0 @@ -// 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(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 diff --git a/lib/IRremoteESP8266/src/ir_Bose.cpp b/lib/IRremoteESP8266/src/ir_Bose.cpp deleted file mode 100644 index a57d125b3c..0000000000 --- a/lib/IRremoteESP8266/src/ir_Bose.cpp +++ /dev/null @@ -1,69 +0,0 @@ -// 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 diff --git a/lib/IRremoteESP8266/src/ir_Carrier.cpp b/lib/IRremoteESP8266/src/ir_Carrier.cpp deleted file mode 100644 index 92fca4bd2c..0000000000 --- a/lib/IRremoteESP8266/src/ir_Carrier.cpp +++ /dev/null @@ -1,535 +0,0 @@ -// Copyright 2018, 2020 David Conran -/// @file -/// @brief Carrier protocols. -/// @see CarrierAc https://github.com/crankyoldgit/IRremoteESP8266/issues/385 -/// @see CarrierAc64 https://github.com/crankyoldgit/IRremoteESP8266/issues/1127 - -#include "ir_Carrier.h" -#include -#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. - - -#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(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; -} diff --git a/lib/IRremoteESP8266/src/ir_Carrier.h b/lib/IRremoteESP8266/src/ir_Carrier.h index aa9ea8447c..734e702b6d 100644 --- a/lib/IRremoteESP8266/src/ir_Carrier.h +++ b/lib/IRremoteESP8266/src/ir_Carrier.h @@ -20,10 +20,10 @@ #ifndef UNIT_TEST #include #endif -#include "IRremoteESP8266.h" -#include "IRsend.h" +#include "../src/IRremoteESP8266.h" +#include "../src/IRsend.h" #ifdef UNIT_TEST -#include "IRsend_test.h" +#include "../src/IRsend_test.h" #endif /// Native representation of a Carrier A/C message. diff --git a/lib/IRremoteESP8266/src/ir_Coolix.cpp b/lib/IRremoteESP8266/src/ir_Coolix.cpp deleted file mode 100644 index 2f16a6d1f0..0000000000 --- a/lib/IRremoteESP8266/src/ir_Coolix.cpp +++ /dev/null @@ -1,697 +0,0 @@ -// 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 -#ifndef ARDUINO -#include -#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; -const uint16_t kCoolixOneSpaceTicks = 6; -const uint16_t kCoolixOneSpace = kCoolixOneSpaceTicks * kCoolixTick; -const uint16_t kCoolixZeroSpaceTicks = 2; -const uint16_t kCoolixZeroSpace = kCoolixZeroSpaceTicks * kCoolixTick; -const uint16_t kCoolixHdrMarkTicks = 17; -const uint16_t kCoolixHdrMark = kCoolixHdrMarkTicks * kCoolixTick; -const uint16_t kCoolixHdrSpaceTicks = 16; -const uint16_t kCoolixHdrSpace = kCoolixHdrSpaceTicks * kCoolixTick; -const uint16_t kCoolixMinGapTicks = kCoolixHdrMarkTicks + kCoolixZeroSpaceTicks; -const uint16_t kCoolixMinGap = kCoolixMinGapTicks * kCoolixTick; - -using irutils::addBoolToString; -using irutils::addIntToString; -using irutils::addLabeledString; -using irutils::addModeToString; -using irutils::addTempToString; - -#if SEND_COOLIX -/// Send a Coolix 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.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) - ? kOffStr : String(uint64ToString(getSensorTemp()) + 'C'), kSensorTempStr); - return result; -} - -#if DECODE_COOLIX -/// Decode the supplied Coolix 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; - // Calculate how long the common tick time is based on the header mark. - uint32_t m_tick = results->rawbuf[offset++] * kRawTick / kCoolixHdrMarkTicks; - if (!matchSpace(results->rawbuf[offset], kCoolixHdrSpace)) return false; - // Calculate how long the common tick time is based on the header space. - uint32_t s_tick = results->rawbuf[offset++] * kRawTick / kCoolixHdrSpaceTicks; - - // Data - // Twice as many bits as there are normal plus inverted bits. - for (uint16_t i = 0; i < nbits * 2; i++, offset++) { - bool flip = (i / 8) % 2; - if (!matchMark(results->rawbuf[offset++], kCoolixBitMarkTicks * m_tick)) - return false; - if (matchSpace(results->rawbuf[offset], kCoolixOneSpaceTicks * s_tick)) { - if (flip) - inverted = (inverted << 1) | 1; - else - data = (data << 1) | 1; - } else if (matchSpace(results->rawbuf[offset], - kCoolixZeroSpaceTicks * s_tick)) { - if (flip) - inverted <<= 1; - else - data <<= 1; - } else { - return false; - } - } - - // Footer - if (!matchMark(results->rawbuf[offset++], kCoolixBitMarkTicks * m_tick)) - return false; - if (offset < results->rawlen && - !matchAtLeast(results->rawbuf[offset], kCoolixMinGapTicks * s_tick)) - 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 diff --git a/lib/IRremoteESP8266/src/ir_Coolix.h b/lib/IRremoteESP8266/src/ir_Coolix.h index 42a2528d75..b0318141af 100644 --- a/lib/IRremoteESP8266/src/ir_Coolix.h +++ b/lib/IRremoteESP8266/src/ir_Coolix.h @@ -30,10 +30,10 @@ #ifndef UNIT_TEST #include #endif -#include "IRremoteESP8266.h" -#include "IRsend.h" +#include "../src/IRremoteESP8266.h" +#include "../src/IRsend.h" #ifdef UNIT_TEST -#include "IRsend_test.h" +#include "../src/IRsend_test.h" #endif // Constants diff --git a/lib/IRremoteESP8266/src/ir_Corona.cpp b/lib/IRremoteESP8266/src/ir_Corona.cpp deleted file mode 100644 index ef40c241a0..0000000000 --- a/lib/IRremoteESP8266/src/ir_Corona.cpp +++ /dev/null @@ -1,575 +0,0 @@ -// Copyright 2020 Christian Nilsson -// -/// @file -/// @brief Corona A/C protocol -/// @note Unsupported: -/// - Auto/Max button press (special format) - -#include "ir_Corona.h" -#include -#include -#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(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(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; -} diff --git a/lib/IRremoteESP8266/src/ir_Corona.h b/lib/IRremoteESP8266/src/ir_Corona.h index cbe3e99e47..43d211dfcd 100644 --- a/lib/IRremoteESP8266/src/ir_Corona.h +++ b/lib/IRremoteESP8266/src/ir_Corona.h @@ -20,10 +20,10 @@ #ifndef UNIT_TEST #include #endif -#include "IRremoteESP8266.h" -#include "IRsend.h" +#include "../src/IRremoteESP8266.h" +#include "../src/IRsend.h" #ifdef UNIT_TEST -#include "IRsend_test.h" +#include "../src/IRsend_test.h" #endif /// Native representation of a section of a Corona A/C message. diff --git a/lib/IRremoteESP8266/src/ir_Daikin.cpp b/lib/IRremoteESP8266/src/ir_Daikin.cpp deleted file mode 100644 index ea11daf781..0000000000 --- a/lib/IRremoteESP8266/src/ir_Daikin.cpp +++ /dev/null @@ -1,3735 +0,0 @@ -// Copyright 2016 sillyfrog -// Copyright 2017 sillyfrog, crankyoldgit -// Copyright 2018-2021 crankyoldgit -// Copyright 2019 pasna (IRDaikin160 class / Daikin176 class) - -/// @file -/// @brief Support for Daikin A/C protocols. -/// @see Daikin http://harizanov.com/2012/02/control-daikin-air-conditioner-over-the-internet/ -/// @see Daikin https://github.com/mharizanov/Daikin-AC-remote-control-over-the-Internet/tree/master/IRremote -/// @see Daikin http://rdlab.cdmt.vn/project-2013/daikin-ir-protocol -/// @see Daikin https://github.com/blafois/Daikin-IR-Reverse -/// @see Daikin128 https://github.com/crankyoldgit/IRremoteESP8266/issues/827 -/// @see Daikin152 https://github.com/crankyoldgit/IRremoteESP8266/issues/873 -/// @see Daikin152 https://github.com/ToniA/arduino-heatpumpir/blob/master/DaikinHeatpumpARC480A14IR.cpp -/// @see Daikin152 https://github.com/ToniA/arduino-heatpumpir/blob/master/DaikinHeatpumpARC480A14IR.h -/// @see Daikin160 https://github.com/crankyoldgit/IRremoteESP8266/issues/731 -/// @see Daikin2 https://docs.google.com/spreadsheets/d/1f8EGfIbBUo2B-CzUFdrgKQprWakoYNKM80IKZN4KXQE/edit#gid=236366525&range=B25:D32 -/// @see Daikin2 https://github.com/crankyoldgit/IRremoteESP8266/issues/582 -/// @see Daikin2 https://github.com/crankyoldgit/IRremoteESP8266/issues/1535 -/// @see Daikin2 https://www.daikin.co.nz/sites/default/files/daikin-split-system-US7-FTXZ25-50NV1B.pdf -/// @see Daikin216 https://github.com/crankyoldgit/IRremoteESP8266/issues/689 -/// @see Daikin216 https://github.com/danny-source/Arduino_DY_IRDaikin -/// @see Daikin64 https://github.com/crankyoldgit/IRremoteESP8266/issues/1064 - -#include "ir_Daikin.h" -#include -#include -#ifndef ARDUINO -#include -#endif -#include "IRrecv.h" -#include "IRremoteESP8266.h" -#include "IRsend.h" -#ifdef UNIT_TEST -#include "IRsend_test.h" -#endif -#include "IRtext.h" -#include "IRutils.h" - -using irutils::addBoolToString; -using irutils::addDayToString; -using irutils::addIntToString; -using irutils::addLabeledString; -using irutils::addModeToString; -using irutils::addSwingHToString; -using irutils::addSwingVToString; -using irutils::addTempToString; -using irutils::addFanToString; -using irutils::bcdToUint8; -using irutils::minsToString; -using irutils::setBit; -using irutils::setBits; -using irutils::sumNibbles; -using irutils::uint8ToBcd; - -#if SEND_DAIKIN -/// Send a Daikin 280-bit A/C formatted message. -/// Status: STABLE -/// @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. -/// @see https://github.com/mharizanov/Daikin-AC-remote-control-over-the-Internet/tree/master/IRremote -/// @see https://github.com/blafois/Daikin-IR-Reverse -void IRsend::sendDaikin(const unsigned char data[], const uint16_t nbytes, - const uint16_t repeat) { - if (nbytes < kDaikinStateLengthShort) - return; // Not enough bytes to send a proper message. - - for (uint16_t r = 0; r <= repeat; r++) { - uint16_t offset = 0; - // Send the header, 0b00000 - sendGeneric(0, 0, // No header for the header - kDaikinBitMark, kDaikinOneSpace, kDaikinBitMark, - kDaikinZeroSpace, kDaikinBitMark, kDaikinZeroSpace + kDaikinGap, - (uint64_t)0b00000, kDaikinHeaderLength, 38, false, 0, 50); - // Data #1 - if (nbytes < kDaikinStateLength) { // Are we using the legacy size? - // Do this as a constant to save RAM and keep in flash memory - sendGeneric(kDaikinHdrMark, kDaikinHdrSpace, kDaikinBitMark, - kDaikinOneSpace, kDaikinBitMark, kDaikinZeroSpace, - kDaikinBitMark, kDaikinZeroSpace + kDaikinGap, - kDaikinFirstHeader64, 64, 38, false, 0, 50); - } else { // We are using the newer/more correct size. - sendGeneric(kDaikinHdrMark, kDaikinHdrSpace, kDaikinBitMark, - kDaikinOneSpace, kDaikinBitMark, kDaikinZeroSpace, - kDaikinBitMark, kDaikinZeroSpace + kDaikinGap, - data, kDaikinSection1Length, 38, false, 0, 50); - offset += kDaikinSection1Length; - } - // Data #2 - sendGeneric(kDaikinHdrMark, kDaikinHdrSpace, kDaikinBitMark, - kDaikinOneSpace, kDaikinBitMark, kDaikinZeroSpace, - kDaikinBitMark, kDaikinZeroSpace + kDaikinGap, - data + offset, kDaikinSection2Length, 38, false, 0, 50); - offset += kDaikinSection2Length; - // Data #3 - sendGeneric(kDaikinHdrMark, kDaikinHdrSpace, kDaikinBitMark, - kDaikinOneSpace, kDaikinBitMark, kDaikinZeroSpace, - kDaikinBitMark, kDaikinZeroSpace + kDaikinGap, - data + offset, nbytes - offset, 38, false, 0, 50); - } -} -#endif // SEND_DAIKIN - -/// 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? -IRDaikinESP::IRDaikinESP(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 IRDaikinESP::begin(void) { _irsend.begin(); } - -#if SEND_DAIKIN -/// Send the current internal state as an IR message. -/// @param[in] repeat Nr. of times the message will be repeated. -void IRDaikinESP::send(const uint16_t repeat) { - _irsend.sendDaikin(getRaw(), kDaikinStateLength, repeat); -} -#endif // SEND_DAIKIN - -/// 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 IRDaikinESP::validChecksum(uint8_t state[], const uint16_t length) { - // Data #1 - if (length < kDaikinSection1Length || - state[kDaikinByteChecksum1] != sumBytes(state, kDaikinSection1Length - 1)) - return false; - // Data #2 - if (length < kDaikinSection1Length + kDaikinSection2Length || - state[kDaikinByteChecksum2] != sumBytes(state + kDaikinSection1Length, - kDaikinSection2Length - 1)) - return false; - // Data #3 - if (length < kDaikinSection1Length + kDaikinSection2Length + 2 || - state[length - 1] != sumBytes(state + kDaikinSection1Length + - kDaikinSection2Length, - length - (kDaikinSection1Length + - kDaikinSection2Length) - 1)) - return false; - return true; -} - -/// Calculate and set the checksum values for the internal state. -void IRDaikinESP::checksum(void) { - _.Sum1 = sumBytes(_.raw, kDaikinSection1Length - 1); - _.Sum2 = sumBytes(_.raw + kDaikinSection1Length, kDaikinSection2Length - 1); - _.Sum3 = sumBytes(_.raw + kDaikinSection1Length + kDaikinSection2Length, - kDaikinSection3Length - 1); -} - -/// Reset the internal state to a fixed known good state. -void IRDaikinESP::stateReset(void) { - for (uint8_t i = 0; i < kDaikinStateLength; i++) _.raw[i] = 0x0; - - _.raw[0] = 0x11; - _.raw[1] = 0xDA; - _.raw[2] = 0x27; - _.raw[4] = 0xC5; - // _.raw[7] is a checksum byte, it will be set by checksum(). - _.raw[8] = 0x11; - _.raw[9] = 0xDA; - _.raw[10] = 0x27; - _.raw[12] = 0x42; - // _.raw[15] is a checksum byte, it will be set by checksum(). - _.raw[16] = 0x11; - _.raw[17] = 0xDA; - _.raw[18] = 0x27; - _.raw[21] = 0x49; - _.raw[22] = 0x1E; - _.raw[24] = 0xB0; - _.raw[27] = 0x06; - _.raw[28] = 0x60; - _.raw[31] = 0xC0; - // _.raw[34] is a checksum byte, it will be set by checksum(). - checksum(); -} - -/// 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 *IRDaikinESP::getRaw(void) { - checksum(); // Ensure correct settings before sending. - 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 Length of the code in bytes. -void IRDaikinESP::setRaw(const uint8_t new_code[], const uint16_t length) { - uint8_t offset = 0; - if (length == kDaikinStateLengthShort) { // Handle the "short" length case. - offset = kDaikinStateLength - kDaikinStateLengthShort; - stateReset(); - } - for (uint8_t i = 0; i < length && i < kDaikinStateLength; i++) - _.raw[i + offset] = new_code[i]; -} - -/// Change the power setting to On. -void IRDaikinESP::on(void) { setPower(true); } - -/// Change the power setting to Off. -void IRDaikinESP::off(void) { setPower(false); } - -/// Change the power setting. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRDaikinESP::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 IRDaikinESP::getPower(void) const { - return _.Power; -} - -/// Set the temperature. -/// @param[in] temp The temperature in degrees celsius. -void IRDaikinESP::setTemp(const uint8_t temp) { - uint8_t degrees = std::max(temp, kDaikinMinTemp); - degrees = std::min(degrees, kDaikinMaxTemp); - _.Temp = degrees; -} - -/// Get the current temperature setting. -/// @return The current setting for temp. in degrees celsius. -uint8_t IRDaikinESP::getTemp(void) const { return _.Temp; } - -/// Set the speed of the fan. -/// @param[in] fan The desired setting. -/// @note 1-5 or kDaikinFanAuto or kDaikinFanQuiet -void IRDaikinESP::setFan(const uint8_t fan) { - // Set the fan speed bits, leave low 4 bits alone - uint8_t fanset; - if (fan == kDaikinFanQuiet || fan == kDaikinFanAuto) - fanset = fan; - else if (fan < kDaikinFanMin || fan > kDaikinFanMax) - fanset = kDaikinFanAuto; - else - fanset = 2 + fan; - _.Fan = fanset; -} - -/// Get the current fan speed setting. -/// @return The current fan speed. -uint8_t IRDaikinESP::getFan(void) const { - uint8_t fan = _.Fan; - if (fan != kDaikinFanQuiet && fan != kDaikinFanAuto) fan -= 2; - return fan; -} - -/// Get the operating mode setting of the A/C. -/// @return The current operating mode setting. -uint8_t IRDaikinESP::getMode(void) const { - return _.Mode; -} - -/// Set the operating mode of the A/C. -/// @param[in] mode The desired operating mode. -void IRDaikinESP::setMode(const uint8_t mode) { - switch (mode) { - case kDaikinAuto: - case kDaikinCool: - case kDaikinHeat: - case kDaikinFan: - case kDaikinDry: - _.Mode = mode; - break; - default: - _.Mode = kDaikinAuto; - } -} - -/// Set the Vertical Swing mode of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRDaikinESP::setSwingVertical(const bool on) { - _.SwingV = (on ? kDaikinSwingOn : kDaikinSwingOff); -} - -/// Get the Vertical Swing mode of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRDaikinESP::getSwingVertical(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 IRDaikinESP::setSwingHorizontal(const bool on) { - _.SwingH = (on ? kDaikinSwingOn : kDaikinSwingOff); -} - -/// Get the Horizontal Swing mode of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRDaikinESP::getSwingHorizontal(void) const { - return _.SwingH; -} - -/// Set the Quiet mode of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRDaikinESP::setQuiet(const bool on) { - _.Quiet = on; - // Powerful & Quiet mode being on are mutually exclusive. - if (on) setPowerful(false); -} - -/// Get the Quiet mode status of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRDaikinESP::getQuiet(void) const { - return _.Quiet; -} - -/// Set the Powerful (Turbo) mode of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRDaikinESP::setPowerful(const bool on) { - _.Powerful = on; - if (on) { - // Powerful, Quiet, & Econo mode being on are mutually exclusive. - setQuiet(false); - setEcono(false); - } -} - -/// Get the Powerful (Turbo) mode of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRDaikinESP::getPowerful(void) const { - return _.Powerful; -} - -/// Set the Sensor mode of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRDaikinESP::setSensor(const bool on) { - _.Sensor = on; -} - -/// Get the Sensor mode of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRDaikinESP::getSensor(void) const { - return _.Sensor; -} - -/// Set the Economy mode of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRDaikinESP::setEcono(const bool on) { - _.Econo = on; - // Powerful & Econo mode being on are mutually exclusive. - if (on) setPowerful(false); -} - -/// Get the Economical mode of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRDaikinESP::getEcono(void) const { - return _.Econo; -} - -/// Set the Mould mode of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRDaikinESP::setMold(const bool on) { - _.Mold = on; -} - -/// Get the Mould mode status of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRDaikinESP::getMold(void) const { - return _.Mold; -} - -/// Set the Comfort mode of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRDaikinESP::setComfort(const bool on) { - _.Comfort = on; -} - -/// Get the Comfort mode of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRDaikinESP::getComfort(void) const { - return _.Comfort; -} - -/// Set the enable status & time of the On Timer. -/// @param[in] starttime The number of minutes past midnight. -void IRDaikinESP::enableOnTimer(const uint16_t starttime) { - _.OnTimer = true; - _.OnTime = starttime; -} - -/// Clear and disable the On timer. -void IRDaikinESP::disableOnTimer(void) { - _.OnTimer = false; - _.OnTime = kDaikinUnusedTime; -} - -/// Get the On Timer time to be sent to the A/C unit. -/// @return The number of minutes past midnight. -uint16_t IRDaikinESP::getOnTime(void) const { - return _.OnTime; -} - -/// Get the enable status of the On Timer. -/// @return true, the setting is on. false, the setting is off. -bool IRDaikinESP::getOnTimerEnabled(void) const { - return _.OnTimer; -} - -/// Set the enable status & time of the Off Timer. -/// @param[in] endtime The number of minutes past midnight. -void IRDaikinESP::enableOffTimer(const uint16_t endtime) { - _.OffTimer = true; - _.OffTime = endtime; -} - -/// Clear and disable the Off timer. -void IRDaikinESP::disableOffTimer(void) { - _.OffTimer = false; - _.OffTime = kDaikinUnusedTime; -} - -/// Get the Off Timer time to be sent to the A/C unit. -/// @return The number of minutes past midnight. -uint16_t IRDaikinESP::getOffTime(void) const { - return _.OffTime; -} - -/// Get the enable status of the Off Timer. -/// @return true, the setting is on. false, the setting is off. -bool IRDaikinESP::getOffTimerEnabled(void) const { - return _.OffTimer; -} - -/// Set the clock on the A/C unit. -/// @param[in] mins_since_midnight Nr. of minutes past midnight. -void IRDaikinESP::setCurrentTime(const uint16_t mins_since_midnight) { - uint16_t mins = mins_since_midnight; - if (mins > 24 * 60) mins = 0; // If > 23:59, set to 00:00 - _.CurrentTime = mins; -} - -/// Get the clock time to be sent to the A/C unit. -/// @return The number of minutes past midnight. -uint16_t IRDaikinESP::getCurrentTime(void) const { - return _.CurrentTime; -} - -/// Set the current day of the week to be sent to the A/C unit. -/// @param[in] day_of_week The numerical representation of the day of the week. -/// @note 1 is SUN, 2 is MON, ..., 7 is SAT -void IRDaikinESP::setCurrentDay(const uint8_t day_of_week) { - _.CurrentDay = day_of_week; -} - -/// Get the current day of the week to be sent to the A/C unit. -/// @return The numerical representation of the day of the week. -/// @note 1 is SUN, 2 is MON, ..., 7 is SAT -uint8_t IRDaikinESP::getCurrentDay(void) const { - return _.CurrentDay; -} - -/// Set the enable status of the Weekly Timer. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRDaikinESP::setWeeklyTimerEnable(const bool on) { - // Bit is cleared for `on`. - _.WeeklyTimer = !on; -} - -/// Get the enable status of the Weekly Timer. -/// @return true, the setting is on. false, the setting is off. -bool IRDaikinESP::getWeeklyTimerEnable(void) const { - return !_.WeeklyTimer; -} - -/// 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 IRDaikinESP::convertMode(const stdAc::opmode_t mode) { - switch (mode) { - case stdAc::opmode_t::kCool: return kDaikinCool; - case stdAc::opmode_t::kHeat: return kDaikinHeat; - case stdAc::opmode_t::kDry: return kDaikinDry; - case stdAc::opmode_t::kFan: return kDaikinFan; - default: return kDaikinAuto; - } -} - -/// 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 IRDaikinESP::convertFan(const stdAc::fanspeed_t speed) { - switch (speed) { - case stdAc::fanspeed_t::kMin: return kDaikinFanQuiet; - case stdAc::fanspeed_t::kLow: return kDaikinFanMin; - case stdAc::fanspeed_t::kMedium: return kDaikinFanMed; - case stdAc::fanspeed_t::kHigh: return kDaikinFanMax - 1; - case stdAc::fanspeed_t::kMax: return kDaikinFanMax; - default: return kDaikinFanAuto; - } -} - -/// 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 IRDaikinESP::toCommonMode(const uint8_t mode) { - switch (mode) { - case kDaikinCool: return stdAc::opmode_t::kCool; - case kDaikinHeat: return stdAc::opmode_t::kHeat; - case kDaikinDry: return stdAc::opmode_t::kDry; - case kDaikinFan: 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 IRDaikinESP::toCommonFanSpeed(const uint8_t speed) { - switch (speed) { - case kDaikinFanMax: return stdAc::fanspeed_t::kMax; - case kDaikinFanMax - 1: return stdAc::fanspeed_t::kHigh; - case kDaikinFanMed: - case kDaikinFanMin + 1: return stdAc::fanspeed_t::kMedium; - case kDaikinFanMin: return stdAc::fanspeed_t::kLow; - case kDaikinFanQuiet: 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 IRDaikinESP::toCommon(void) const { - stdAc::state_t result; - result.protocol = decode_type_t::DAIKIN; - result.model = -1; // No models used. - result.power = _.Power; - result.mode = toCommonMode(_.Mode); - result.celsius = true; - result.degrees = _.Temp; - result.fanspeed = toCommonFanSpeed(getFan()); - 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 = _.Powerful; - result.clean = _.Mold; - result.econo = _.Econo; - // Not supported. - result.filter = false; - result.light = 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 IRDaikinESP::toString(void) const { - String result = ""; - result.reserve(230); // Reserve some heap for the string to reduce fragging. - result += addBoolToString(_.Power, kPowerStr, false); - result += addModeToString(_.Mode, kDaikinAuto, kDaikinCool, kDaikinHeat, - kDaikinDry, kDaikinFan); - result += addTempToString(_.Temp); - result += addFanToString(getFan(), kDaikinFanMax, kDaikinFanMin, - kDaikinFanAuto, kDaikinFanQuiet, kDaikinFanMed); - result += addBoolToString(_.Powerful, kPowerfulStr); - result += addBoolToString(_.Quiet, kQuietStr); - result += addBoolToString(getSensor(), kSensorStr); - result += addBoolToString(_.Mold, kMouldStr); - result += addBoolToString(_.Comfort, kComfortStr); - result += addBoolToString(_.SwingH, kSwingHStr); - result += addBoolToString(_.SwingV, kSwingVStr); - result += addLabeledString(minsToString(_.CurrentTime), kClockStr); - result += addDayToString(_.CurrentDay, -1); - result += addLabeledString(_.OnTimer - ? minsToString(_.OnTime) : kOffStr, - kOnTimerStr); - result += addLabeledString(_.OffTimer - ? minsToString(_.OffTime) : kOffStr, - kOffTimerStr); - result += addBoolToString(getWeeklyTimerEnable(), kWeeklyTimerStr); - return result; -} - -#if DECODE_DAIKIN -/// Decode the supplied Daikin 280-bit message. (DAIKIN) -/// 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. -/// @see https://github.com/mharizanov/Daikin-AC-remote-control-over-the-Internet/tree/master/IRremote -bool IRrecv::decodeDaikin(decode_results *results, uint16_t offset, - const uint16_t nbits, const bool strict) { - // Is there enough data to match successfully? - if (results->rawlen < (2 * (nbits + kDaikinHeaderLength) + - kDaikinSections * (kHeader + kFooter) + kFooter - 1) + - offset) - return false; - - // Compliance - if (strict && nbits != kDaikinBits) return false; - - match_result_t data_result; - - // Header #1 - Doesn't count as data. - data_result = matchData(&(results->rawbuf[offset]), kDaikinHeaderLength, - kDaikinBitMark, kDaikinOneSpace, - kDaikinBitMark, kDaikinZeroSpace, - kDaikinTolerance, kDaikinMarkExcess, false); - offset += data_result.used; - if (data_result.success == false) return false; // Fail - if (data_result.data) return false; // The header bits should be zero. - // Footer - if (!matchMark(results->rawbuf[offset++], kDaikinBitMark, - kDaikinTolerance, kDaikinMarkExcess)) return false; - if (!matchSpace(results->rawbuf[offset++], kDaikinZeroSpace + kDaikinGap, - kDaikinTolerance, kDaikinMarkExcess)) return false; - // Sections - const uint8_t ksectionSize[kDaikinSections] = { - kDaikinSection1Length, kDaikinSection2Length, kDaikinSection3Length}; - uint16_t pos = 0; - for (uint8_t section = 0; section < kDaikinSections; section++) { - uint16_t used; - // Section Header + Section Data (7 bytes) + Section Footer - used = matchGeneric(results->rawbuf + offset, results->state + pos, - results->rawlen - offset, ksectionSize[section] * 8, - kDaikinHdrMark, kDaikinHdrSpace, - kDaikinBitMark, kDaikinOneSpace, - kDaikinBitMark, kDaikinZeroSpace, - kDaikinBitMark, kDaikinZeroSpace + kDaikinGap, - section >= kDaikinSections - 1, - kDaikinTolerance, kDaikinMarkExcess, false); - if (used == 0) return false; - offset += used; - pos += ksectionSize[section]; - } - // Compliance - if (strict) { - // Re-check we got the correct size/length due to the way we read the data. - if (pos * 8 != kDaikinBits) return false; - // Validate the checksum. - if (!IRDaikinESP::validChecksum(results->state)) return false; - } - - // Success - results->decode_type = DAIKIN; - 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_DAIKIN - -#if SEND_DAIKIN2 -/// Send a Daikin2 (312-bit) A/C formatted message. -/// Status: STABLE / Expected to work. -/// @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. -/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/582 -void IRsend::sendDaikin2(const unsigned char data[], const uint16_t nbytes, - const uint16_t repeat) { - if (nbytes < kDaikin2Section1Length) - return; // Not enough bytes to send a partial message. - - for (uint16_t r = 0; r <= repeat; r++) { - // Leader - sendGeneric(kDaikin2LeaderMark, kDaikin2LeaderSpace, - 0, 0, 0, 0, 0, 0, (uint64_t) 0, // No data payload. - 0, kDaikin2Freq, false, 0, 50); - // Section #1 - sendGeneric(kDaikin2HdrMark, kDaikin2HdrSpace, kDaikin2BitMark, - kDaikin2OneSpace, kDaikin2BitMark, kDaikin2ZeroSpace, - kDaikin2BitMark, kDaikin2Gap, data, kDaikin2Section1Length, - kDaikin2Freq, false, 0, 50); - // Section #2 - sendGeneric(kDaikin2HdrMark, kDaikin2HdrSpace, kDaikin2BitMark, - kDaikin2OneSpace, kDaikin2BitMark, kDaikin2ZeroSpace, - kDaikin2BitMark, kDaikin2Gap, data + kDaikin2Section1Length, - nbytes - kDaikin2Section1Length, - kDaikin2Freq, false, 0, 50); - } -} -#endif // SEND_DAIKIN2 - -/// 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? -IRDaikin2::IRDaikin2(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 IRDaikin2::begin(void) { _irsend.begin(); } - -#if SEND_DAIKIN2 -/// Send the current internal state as an IR message. -/// @param[in] repeat Nr. of times the message will be repeated. -void IRDaikin2::send(const uint16_t repeat) { - _irsend.sendDaikin2(getRaw(), kDaikin2StateLength, repeat); -} -#endif // SEND_DAIKIN2 - -/// 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 IRDaikin2::validChecksum(uint8_t state[], const uint16_t length) { - // Validate the checksum of section #1. - if (length <= kDaikin2Section1Length - 1 || - state[kDaikin2Section1Length - 1] != sumBytes(state, - kDaikin2Section1Length - 1)) - return false; - // Validate the checksum of section #2 (a.k.a. the rest) - if (length <= kDaikin2Section1Length + 1 || - state[length - 1] != sumBytes(state + kDaikin2Section1Length, - length - kDaikin2Section1Length - 1)) - return false; - return true; -} - -/// Calculate and set the checksum values for the internal state. -void IRDaikin2::checksum(void) { - _.Sum1 = sumBytes(_.raw, kDaikin2Section1Length - 1); - _.Sum2 = sumBytes(_.raw + kDaikin2Section1Length, kDaikin2Section2Length - 1); -} - -/// Reset the internal state to a fixed known good state. -void IRDaikin2::stateReset(void) { - for (uint8_t i = 0; i < kDaikin2StateLength; i++) _.raw[i] = 0x0; - - _.raw[0] = 0x11; - _.raw[1] = 0xDA; - _.raw[2] = 0x27; - _.raw[4] = 0x01; - _.raw[6] = 0xC0; - _.raw[7] = 0x70; - _.raw[8] = 0x08; - _.raw[9] = 0x0C; - _.raw[10] = 0x80; - _.raw[11] = 0x04; - _.raw[12] = 0xB0; - _.raw[13] = 0x16; - _.raw[14] = 0x24; - _.raw[17] = 0xBE; - _.raw[18] = 0xD0; - // _.raw[19] is a checksum byte, it will be set by checksum(). - _.raw[20] = 0x11; - _.raw[21] = 0xDA; - _.raw[22] = 0x27; - _.raw[25] = 0x08; - _.raw[28] = 0xA0; - _.raw[35] = 0xC1; - _.raw[36] = 0x80; - _.raw[37] = 0x60; - // _.raw[38] is a checksum byte, it will be set by checksum(). - disableOnTimer(); - disableOffTimer(); - disableSleepTimer(); - checksum(); -} - -/// 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 *IRDaikin2::getRaw(void) { - checksum(); // Ensure correct settings before sending. - return _.raw; -} - -/// Set the internal state from a valid code for this protocol. -/// @param[in] new_code A valid code for this protocol. -void IRDaikin2::setRaw(const uint8_t new_code[]) { - std::memcpy(_.raw, new_code, kDaikin2StateLength); -} - -/// Change the power setting to On. -void IRDaikin2::on(void) { setPower(true); } - -/// Change the power setting to Off. -void IRDaikin2::off(void) { setPower(false); } - -/// Change the power setting. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRDaikin2::setPower(const bool on) { - _.Power = on; - _.Power2 = !on; -} - -/// Get the value of the current power setting. -/// @return true, the setting is on. false, the setting is off. -bool IRDaikin2::getPower(void) const { return _.Power && !_.Power2; } - -/// Get the operating mode setting of the A/C. -/// @return The current operating mode setting. -uint8_t IRDaikin2::getMode(void) const { return _.Mode; } - -/// Set the operating mode of the A/C. -/// @param[in] desired_mode The desired operating mode. -void IRDaikin2::setMode(const uint8_t desired_mode) { - uint8_t mode = desired_mode; - switch (mode) { - case kDaikinCool: - case kDaikinHeat: - case kDaikinFan: - case kDaikinDry: break; - default: mode = kDaikinAuto; - } - _.Mode = mode; - // Redo the temp setting as Cool mode has a different min temp. - if (mode == kDaikinCool) setTemp(getTemp()); - setHumidity(getHumidity()); // Make sure the humidity is okay for this mode. -} - -/// Set the temperature. -/// @param[in] desired The temperature in degrees celsius. -void IRDaikin2::setTemp(const uint8_t desired) { - // The A/C has a different min temp if in cool mode. - uint8_t temp = std::max( - (_.Mode == kDaikinCool) ? kDaikin2MinCoolTemp : kDaikinMinTemp, - desired); - _.Temp = std::min(kDaikinMaxTemp, temp); - // If the humidity setting is in use, the temp is a fixed value. - if (_.HumidOn) _.Temp = kDaikinMaxTemp; -} - -/// Get the current temperature setting. -/// @return The current setting for temp. in degrees celsius. -uint8_t IRDaikin2::getTemp(void) const { return _.Temp; } - -/// Set the speed of the fan. -/// @param[in] fan The desired setting. -/// @note 1-5 or kDaikinFanAuto or kDaikinFanQuiet -void IRDaikin2::setFan(const uint8_t fan) { - uint8_t fanset; - if (fan == kDaikinFanQuiet || fan == kDaikinFanAuto) - fanset = fan; - else if (fan < kDaikinFanMin || fan > kDaikinFanMax) - fanset = kDaikinFanAuto; - else - fanset = 2 + fan; - _.Fan = fanset; -} - -/// Get the current fan speed setting. -/// @return The current fan speed. -uint8_t IRDaikin2::getFan(void) const { - const uint8_t fan = _.Fan; - switch (fan) { - case kDaikinFanAuto: - case kDaikinFanQuiet: return fan; - default: return fan - 2; - } -} - -/// Set the Vertical Swing mode of the A/C. -/// @param[in] position The position/mode to set the swing to. -void IRDaikin2::setSwingVertical(const uint8_t position) { - switch (position) { - case kDaikin2SwingVHighest: - case kDaikin2SwingVHigh: - case kDaikin2SwingVUpperMiddle: - case kDaikin2SwingVLowerMiddle: - case kDaikin2SwingVLow: - case kDaikin2SwingVLowest: - case kDaikin2SwingVOff: - case kDaikin2SwingVBreeze: - case kDaikin2SwingVCirculate: - case kDaikin2SwingVAuto: - _.SwingV = position; - } -} - -/// Get the Vertical Swing mode of the A/C. -/// @return The native position/mode setting. -uint8_t IRDaikin2::getSwingVertical(void) const { return _.SwingV; } - -/// 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 IRDaikin2::convertSwingV(const stdAc::swingv_t position) { - switch (position) { - case stdAc::swingv_t::kHighest: - case stdAc::swingv_t::kHigh: - case stdAc::swingv_t::kMiddle: - case stdAc::swingv_t::kLow: - case stdAc::swingv_t::kLowest: - return (uint8_t)position + kDaikin2SwingVHighest; - case stdAc::swingv_t::kOff: - return kDaikin2SwingVOff; - default: - return kDaikin2SwingVAuto; - } -} - -/// Convert a native vertical swing postion to it's common equivalent. -/// @param[in] setting A native position to convert. -/// @return The common vertical swing position. -stdAc::swingv_t IRDaikin2::toCommonSwingV(const uint8_t setting) { - switch (setting) { - case kDaikin2SwingVHighest: return stdAc::swingv_t::kHighest; - case kDaikin2SwingVHigh: return stdAc::swingv_t::kHigh; - case kDaikin2SwingVUpperMiddle: - case kDaikin2SwingVLowerMiddle: return stdAc::swingv_t::kMiddle; - case kDaikin2SwingVLow: return stdAc::swingv_t::kLow; - case kDaikin2SwingVLowest: return stdAc::swingv_t::kLowest; - case kDaikin2SwingVOff: return stdAc::swingv_t::kOff; - default: return stdAc::swingv_t::kAuto; - } -} - -/// Set the Horizontal Swing mode of the A/C. -/// @param[in] position The position/mode to set the swing to. -void IRDaikin2::setSwingHorizontal(const uint8_t position) { - _.SwingH = position; -} - -/// Get the Horizontal Swing mode of the A/C. -/// @return The native position/mode setting. -uint8_t IRDaikin2::getSwingHorizontal(void) const { return _.SwingH; } - -/// Set the clock on the A/C unit. -/// @param[in] numMins Nr. of minutes past midnight. -void IRDaikin2::setCurrentTime(const uint16_t numMins) { - uint16_t mins = numMins; - if (numMins > 24 * 60) mins = 0; // If > 23:59, set to 00:00 - _.CurrentTime = mins; -} - -/// Get the clock time to be sent to the A/C unit. -/// @return The number of minutes past midnight. -uint16_t IRDaikin2::getCurrentTime(void) const { return _.CurrentTime; } - -/// Set the enable status & time of the On Timer. -/// @param[in] starttime The number of minutes past midnight. -/// @note Timer location is shared with sleep timer. -void IRDaikin2::enableOnTimer(const uint16_t starttime) { - clearSleepTimerFlag(); - _.OnTimer = true; - _.OnTime = starttime; -} - -/// Clear the On Timer flag. -void IRDaikin2::clearOnTimerFlag(void) { _.OnTimer = false; } - -/// Disable the On timer. -void IRDaikin2::disableOnTimer(void) { - _.OnTime = kDaikinUnusedTime; - clearOnTimerFlag(); - clearSleepTimerFlag(); -} - -/// Get the On Timer time to be sent to the A/C unit. -/// @return The number of minutes past midnight. -uint16_t IRDaikin2::getOnTime(void) const { return _.OnTime; } - -/// Get the enable status of the On Timer. -/// @return true, the setting is on. false, the setting is off. -bool IRDaikin2::getOnTimerEnabled(void) const { return _.OnTimer; } - -/// Set the enable status & time of the Off Timer. -/// @param[in] endtime The number of minutes past midnight. -void IRDaikin2::enableOffTimer(const uint16_t endtime) { - // Set the Off Timer flag. - _.OffTimer = true; - _.OffTime = endtime; -} - -/// Disable the Off timer. -void IRDaikin2::disableOffTimer(void) { - _.OffTime = kDaikinUnusedTime; - // Clear the Off Timer flag. - _.OffTimer = false; -} - -/// Get the Off Timer time to be sent to the A/C unit. -/// @return The number of minutes past midnight. -uint16_t IRDaikin2::getOffTime(void) const { return _.OffTime; } - -/// Get the enable status of the Off Timer. -/// @return true, the setting is on. false, the setting is off. -bool IRDaikin2::getOffTimerEnabled(void) const { return _.OffTimer; } - -/// Get the Beep status of the A/C. -/// @return true, the setting is on. false, the setting is off. -uint8_t IRDaikin2::getBeep(void) const { return _.Beep; } - -/// Set the Beep mode of the A/C. -/// @param[in] beep true, the setting is on. false, the setting is off. -void IRDaikin2::setBeep(const uint8_t beep) { _.Beep = beep; } - -/// Get the Light status of the A/C. -/// @return true, the setting is on. false, the setting is off. -uint8_t IRDaikin2::getLight(void) const { return _.Light; } - -/// Set the Light (LED) mode of the A/C. -/// @param[in] light true, the setting is on. false, the setting is off. -void IRDaikin2::setLight(const uint8_t light) { _.Light = light; } - -/// Set the Mould (filter) mode of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRDaikin2::setMold(const bool on) { _.Mold = on; } - -/// Get the Mould (filter) mode status of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRDaikin2::getMold(void) const { return _.Mold; } - -/// Set the Auto clean mode of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRDaikin2::setClean(const bool on) { _.Clean = on; } - -/// Get the Auto Clean mode status of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRDaikin2::getClean(void) const { return _.Clean; } - -/// Set the Fresh Air mode of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRDaikin2::setFreshAir(const bool on) { _.FreshAir = on; } - -/// Get the Fresh Air mode status of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRDaikin2::getFreshAir(void) const { return _.FreshAir; } - -/// Set the (High) Fresh Air mode of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRDaikin2::setFreshAirHigh(const bool on) { _.FreshAirHigh = on; } - -/// Get the (High) Fresh Air mode status of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRDaikin2::getFreshAirHigh(void) const { return _.FreshAirHigh; } - -/// Set the Automatic Eye (Sensor) mode of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRDaikin2::setEyeAuto(bool on) { _.EyeAuto = on; } - -/// Get the Automaitc Eye (Sensor) mode status of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRDaikin2::getEyeAuto(void) const { return _.EyeAuto; } - -/// Set the Eye (Sensor) mode of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRDaikin2::setEye(bool on) { _.Eye = on; } - -/// Get the Eye (Sensor) mode status of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRDaikin2::getEye(void) const { return _.Eye; } - -/// Set the Economy mode of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRDaikin2::setEcono(bool on) { _.Econo = on; } - -/// Get the Economical mode of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRDaikin2::getEcono(void) const { return _.Econo; } - -/// Set the enable status & time of the Sleep Timer. -/// @param[in] sleeptime The number of minutes past midnight. -/// @note The Timer location is shared with On Timer. -void IRDaikin2::enableSleepTimer(const uint16_t sleeptime) { - enableOnTimer(sleeptime); - clearOnTimerFlag(); - _.SleepTimer = true; -} - -/// Clear the sleep timer flag. -void IRDaikin2::clearSleepTimerFlag(void) { _.SleepTimer = false; } - -/// Disable the sleep timer. -void IRDaikin2::disableSleepTimer(void) { disableOnTimer(); } - -/// Get the Sleep Timer time to be sent to the A/C unit. -/// @return The number of minutes past midnight. -uint16_t IRDaikin2::getSleepTime(void) const { return getOnTime(); } - -/// Get the Sleep timer enabled status of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRDaikin2::getSleepTimerEnabled(void) const { return _.SleepTimer; } - -/// Set the Quiet mode of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRDaikin2::setQuiet(const bool on) { - _.Quiet = on; - // Powerful & Quiet mode being on are mutually exclusive. - if (on) setPowerful(false); -} - -/// Get the Quiet mode status of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRDaikin2::getQuiet(void) const { return _.Quiet; } - -/// Set the Powerful (Turbo) mode of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRDaikin2::setPowerful(const bool on) { - _.Powerful = on; - // Powerful & Quiet mode being on are mutually exclusive. - if (on) setQuiet(false); -} - -/// Get the Powerful (Turbo) mode of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRDaikin2::getPowerful(void) const { return _.Powerful; } - -/// Set the Purify (Filter) mode of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRDaikin2::setPurify(const bool on) { _.Purify = on; } - -/// Get the Purify (Filter) mode status of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRDaikin2::getPurify(void) const { return _.Purify; } - -/// Get the Humidity percentage setting of the A/C. -/// @return The setting percentage. 255 is Automatic. 0 is Off. -uint8_t IRDaikin2::getHumidity(void) const { return _.Humidity; } - -/// Set the Humidity percentage setting of the A/C. -/// @param[in] percent Percentage humidty. 255 is Auto. 0 is Off. -/// @note Only available in Dry & Heat modes, otherwise it is Off. -void IRDaikin2::setHumidity(const uint8_t percent) { - _.Humidity = kDaikin2HumidityOff; // Default to off. - switch (getMode()) { - case kDaikinHeat: - switch (percent) { - case kDaikin2HumidityOff: - case kDaikin2HumidityHeatLow: - case kDaikin2HumidityHeatMedium: - case kDaikin2HumidityHeatHigh: - case kDaikin2HumidityAuto: - _.Humidity = percent; - } - break; - case kDaikinDry: - switch (percent) { - case kDaikin2HumidityOff: - case kDaikin2HumidityDryLow: - case kDaikin2HumidityDryMedium: - case kDaikin2HumidityDryHigh: - case kDaikin2HumidityAuto: - _.Humidity = percent; - } - break; - } - _.HumidOn = (_.Humidity != kDaikin2HumidityOff); // Enabled? - setTemp(getTemp()); // Adjust the temperature if we need to. -} - -/// 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 IRDaikin2::convertMode(const stdAc::opmode_t mode) { - return IRDaikinESP::convertMode(mode); -} - -/// 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 IRDaikin2::convertFan(const stdAc::fanspeed_t speed) { - return IRDaikinESP::convertFan(speed); -} - -/// Convert a stdAc::swingh_t enum into it's native setting. -/// @param[in] position The enum to be converted. -/// @return The native equivalent of the enum. -uint8_t IRDaikin2::convertSwingH(const stdAc::swingh_t position) { - switch (position) { - case stdAc::swingh_t::kAuto: return kDaikin2SwingHSwing; - case stdAc::swingh_t::kLeftMax: return kDaikin2SwingHLeftMax; - case stdAc::swingh_t::kLeft: return kDaikin2SwingHLeft; - case stdAc::swingh_t::kMiddle: return kDaikin2SwingHMiddle; - case stdAc::swingh_t::kRight: return kDaikin2SwingHRight; - case stdAc::swingh_t::kRightMax: return kDaikin2SwingHRightMax; - case stdAc::swingh_t::kWide: return kDaikin2SwingHWide; - default: return kDaikin2SwingHAuto; - } -} - -/// Convert a native horizontal swing postion to it's common equivalent. -/// @param[in] setting A native position to convert. -/// @return The common horizontal swing position. -stdAc::swingh_t IRDaikin2::toCommonSwingH(const uint8_t setting) { - switch (setting) { - case kDaikin2SwingHSwing: return stdAc::swingh_t::kAuto; - case kDaikin2SwingHLeftMax: return stdAc::swingh_t::kLeftMax; - case kDaikin2SwingHLeft: return stdAc::swingh_t::kLeft; - case kDaikin2SwingHMiddle: return stdAc::swingh_t::kMiddle; - case kDaikin2SwingHRight: return stdAc::swingh_t::kRight; - case kDaikin2SwingHRightMax: return stdAc::swingh_t::kRightMax; - case kDaikin2SwingHWide: return stdAc::swingh_t::kWide; - 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 IRDaikin2::toCommon(void) const { - stdAc::state_t result; - result.protocol = decode_type_t::DAIKIN2; - result.model = -1; // No models used. - result.power = getPower(); - result.mode = IRDaikinESP::toCommonMode(_.Mode); - result.celsius = true; - result.degrees = _.Temp; - result.fanspeed = IRDaikinESP::toCommonFanSpeed(getFan()); - result.swingv = toCommonSwingV(_.SwingV); - result.swingh = toCommonSwingH(_.SwingH); - result.quiet = _.Quiet; - result.light = _.Light != 3; // 3 is Off, everything else is On. - result.turbo = _.Powerful; - result.clean = _.Mold; - result.econo = _.Econo; - result.filter = _.Purify; - result.beep = _.Beep != 3; // 3 is Off, everything else is On. - result.sleep = _.SleepTimer ? getSleepTime() : -1; - // Not supported. - result.clock = -1; - return result; -} - -/// Convert the current internal state into a human readable string. -/// @return A human readable string. -String IRDaikin2::toString(void) const { - String result = ""; - result.reserve(330); // Reserve some heap for the string to reduce fragging. - result += addBoolToString(getPower(), kPowerStr, false); - result += addModeToString(_.Mode, kDaikinAuto, kDaikinCool, kDaikinHeat, - kDaikinDry, kDaikinFan); - result += addTempToString(_.Temp); - result += addFanToString(getFan(), kDaikinFanMax, kDaikinFanMin, - kDaikinFanAuto, kDaikinFanQuiet, kDaikinFanMed); - result += addSwingVToString(_.SwingV, kDaikin2SwingVAuto, - kDaikin2SwingVHighest, kDaikin2SwingVHigh, - kDaikin2SwingVUpperMiddle, - kDaikin2SwingVAuto, // Middle is unused. - kDaikin2SwingVLowerMiddle, - kDaikin2SwingVLow, kDaikin2SwingVLowest, - kDaikin2SwingVOff, // Off is unused - kDaikin2SwingVSwing, kDaikin2SwingVBreeze, - kDaikin2SwingVCirculate); - result += addSwingHToString(_.SwingH, kDaikin2SwingHAuto, - kDaikin2SwingHLeftMax, - kDaikin2SwingHLeft, - kDaikin2SwingHMiddle, - kDaikin2SwingHRight, - kDaikin2SwingHRightMax, - kDaikin2SwingHOff, - kDaikin2SwingHAuto, // Unused - kDaikin2SwingHAuto, // Unused - kDaikin2SwingHAuto, // Unused - kDaikin2SwingHWide); - result += addLabeledString(minsToString(_.CurrentTime), kClockStr); - result += addLabeledString( - _.OnTimer ? minsToString(_.OnTime) : kOffStr, kOnTimerStr); - result += addLabeledString( - _.OffTimer ? minsToString(_.OffTime) : kOffStr, - kOffTimerStr); - result += addLabeledString( - _.SleepTimer ? minsToString(getSleepTime()) : kOffStr, - kSleepTimerStr); - result += addIntToString(_.Beep, kBeepStr); - result += kSpaceLBraceStr; - switch (_.Beep) { - case kDaikinBeepLoud: - result += kLoudStr; - break; - case kDaikinBeepQuiet: - result += kQuietStr; - break; - case kDaikinBeepOff: - result += kOffStr; - break; - default: - result += kUnknownStr; - } - result += ')'; - result += addIntToString(_.Light, kLightStr); - result += kSpaceLBraceStr; - switch (_.Light) { - case kDaikinLightBright: - result += kHighStr; - break; - case kDaikinLightDim: - result += kLowStr; - break; - case kDaikinLightOff: - result += kOffStr; - break; - default: - result += kUnknownStr; - } - result += ')'; - result += addBoolToString(_.Mold, kMouldStr); - result += addBoolToString(_.Clean, kCleanStr); - result += addLabeledString( - _.FreshAir ? (_.FreshAirHigh ? kHighStr : kOnStr) : kOffStr, - kFreshStr); - result += addBoolToString(_.Eye, kEyeStr); - result += addBoolToString(_.EyeAuto, kEyeAutoStr); - result += addBoolToString(_.Quiet, kQuietStr); - result += addBoolToString(_.Powerful, kPowerfulStr); - result += addBoolToString(_.Purify, kPurifyStr); - result += addBoolToString(_.Econo, kEconoStr); - result += addIntToString(_.Humidity, kHumidStr); - switch (_.Humidity) { - case kDaikin2HumidityOff: - case kDaikin2HumidityAuto: - result += kSpaceLBraceStr; - result += _.Humidity ? kAutoStr : kOffStr; - result += ')'; - break; - default: - result += '%'; - } - return result; -} - -#if DECODE_DAIKIN2 -/// Decode the supplied Daikin 312-bit message. (DAIKIN2) -/// Status: STABLE / Works as expected. -/// @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::decodeDaikin2(decode_results *results, uint16_t offset, - const uint16_t nbits, const bool strict) { - if (results->rawlen < 2 * (nbits + kHeader + kFooter) + kHeader - 1 + offset) - return false; - - // Compliance - if (strict && nbits != kDaikin2Bits) return false; - - const uint8_t ksectionSize[kDaikin2Sections] = {kDaikin2Section1Length, - kDaikin2Section2Length}; - - // Leader - if (!matchMark(results->rawbuf[offset++], kDaikin2LeaderMark, - _tolerance + kDaikin2Tolerance)) return false; - if (!matchSpace(results->rawbuf[offset++], kDaikin2LeaderSpace, - _tolerance + kDaikin2Tolerance)) return false; - - // Sections - uint16_t pos = 0; - for (uint8_t section = 0; section < kDaikin2Sections; section++) { - uint16_t used; - // Section Header + Section Data + Section Footer - used = matchGeneric(results->rawbuf + offset, results->state + pos, - results->rawlen - offset, ksectionSize[section] * 8, - kDaikin2HdrMark, kDaikin2HdrSpace, - kDaikin2BitMark, kDaikin2OneSpace, - kDaikin2BitMark, kDaikin2ZeroSpace, - kDaikin2BitMark, kDaikin2Gap, - section >= kDaikin2Sections - 1, - _tolerance + kDaikin2Tolerance, kDaikinMarkExcess, - false); - if (used == 0) return false; - offset += used; - pos += ksectionSize[section]; - } - // Compliance - if (strict) { - // Re-check we got the correct size/length due to the way we read the data. - if (pos * 8 != kDaikin2Bits) return false; - // Validate the checksum. - if (!IRDaikin2::validChecksum(results->state)) return false; - } - - // Success - results->decode_type = DAIKIN2; - 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_DAIKIN2 - -#if SEND_DAIKIN216 -/// Send a Daikin216 (216-bit) A/C formatted message. -/// Status: Alpha / Untested on a real device. -/// @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. -/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/689 -/// @see https://github.com/danny-source/Arduino_DY_IRDaikin -void IRsend::sendDaikin216(const unsigned char data[], const uint16_t nbytes, - const uint16_t repeat) { - if (nbytes < kDaikin216Section1Length) - return; // Not enough bytes to send a partial message. - - for (uint16_t r = 0; r <= repeat; r++) { - // Section #1 - sendGeneric(kDaikin216HdrMark, kDaikin216HdrSpace, kDaikin216BitMark, - kDaikin216OneSpace, kDaikin216BitMark, kDaikin216ZeroSpace, - kDaikin216BitMark, kDaikin216Gap, data, - kDaikin216Section1Length, - kDaikin216Freq, false, 0, kDutyDefault); - // Section #2 - sendGeneric(kDaikin216HdrMark, kDaikin216HdrSpace, kDaikin216BitMark, - kDaikin216OneSpace, kDaikin216BitMark, kDaikin216ZeroSpace, - kDaikin216BitMark, kDaikin216Gap, - data + kDaikin216Section1Length, - nbytes - kDaikin216Section1Length, - kDaikin216Freq, false, 0, kDutyDefault); - } -} -#endif // SEND_DAIKIN216 - -/// 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? -IRDaikin216::IRDaikin216(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 IRDaikin216::begin(void) { _irsend.begin(); } - -#if SEND_DAIKIN216 -/// Send the current internal state as an IR message. -/// @param[in] repeat Nr. of times the message will be repeated. -void IRDaikin216::send(const uint16_t repeat) { - _irsend.sendDaikin216(getRaw(), kDaikin216StateLength, repeat); -} -#endif // SEND_DAIKIN216 - -/// 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 IRDaikin216::validChecksum(uint8_t state[], const uint16_t length) { - // Validate the checksum of section #1. - if (length <= kDaikin216Section1Length - 1 || - state[kDaikin216Section1Length - 1] != sumBytes( - state, kDaikin216Section1Length - 1)) - return false; - // Validate the checksum of section #2 (a.k.a. the rest) - if (length <= kDaikin216Section1Length + 1 || - state[length - 1] != sumBytes(state + kDaikin216Section1Length, - length - kDaikin216Section1Length - 1)) - return false; - return true; -} - -/// Calculate and set the checksum values for the internal state. -void IRDaikin216::checksum(void) { - _.Sum1 = sumBytes(_.raw, kDaikin216Section1Length - 1); - _.Sum2 = sumBytes(_.raw + kDaikin216Section1Length, - kDaikin216Section2Length - 1); -} - -/// Reset the internal state to a fixed known good state. -void IRDaikin216::stateReset(void) { - for (uint8_t i = 0; i < kDaikin216StateLength; i++) _.raw[i] = 0x00; - _.raw[0] = 0x11; - _.raw[1] = 0xDA; - _.raw[2] = 0x27; - _.raw[3] = 0xF0; - // _.raw[7] is a checksum byte, it will be set by checksum(). - _.raw[8] = 0x11; - _.raw[9] = 0xDA; - _.raw[10] = 0x27; - _.raw[23] = 0xC0; - // _.raw[26] is a checksum byte, it will be set by checksum(). -} - -/// 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 *IRDaikin216::getRaw(void) { - checksum(); // Ensure correct settings before sending. - return _.raw; -} - -/// Set the internal state from a valid code for this protocol. -/// @param[in] new_code A valid code for this protocol. -void IRDaikin216::setRaw(const uint8_t new_code[]) { - std::memcpy(_.raw, new_code, kDaikin216StateLength); -} - -/// Change the power setting to On. -void IRDaikin216::on(void) { setPower(true); } - -/// Change the power setting to Off. -void IRDaikin216::off(void) { setPower(false); } - -/// Change the power setting. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRDaikin216::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 IRDaikin216::getPower(void) const { return _.Power; } - -/// Get the operating mode setting of the A/C. -/// @return The current operating mode setting. -uint8_t IRDaikin216::getMode(void) const { return _.Mode; } - -/// Set the operating mode of the A/C. -/// @param[in] mode The desired operating mode. -void IRDaikin216::setMode(const uint8_t mode) { - switch (mode) { - case kDaikinAuto: - case kDaikinCool: - case kDaikinHeat: - case kDaikinFan: - case kDaikinDry: - _.Mode = mode; - break; - default: - _.Mode = kDaikinAuto; - } -} - -/// 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 IRDaikin216::convertMode(const stdAc::opmode_t mode) { - return IRDaikinESP::convertMode(mode); -} - -/// Set the temperature. -/// @param[in] temp The temperature in degrees celsius. -void IRDaikin216::setTemp(const uint8_t temp) { - uint8_t degrees = std::max(temp, kDaikinMinTemp); - degrees = std::min(degrees, kDaikinMaxTemp); - _.Temp = degrees; -} - -/// Get the current temperature setting. -/// @return The current setting for temp. in degrees celsius. -uint8_t IRDaikin216::getTemp(void) const { return _.Temp; } - -/// Set the speed of the fan. -/// @param[in] fan The desired setting. -/// @note 1-5 or kDaikinFanAuto or kDaikinFanQuiet -void IRDaikin216::setFan(const uint8_t fan) { - // Set the fan speed bits, leave low 4 bits alone - uint8_t fanset; - if (fan == kDaikinFanQuiet || fan == kDaikinFanAuto) - fanset = fan; - else if (fan < kDaikinFanMin || fan > kDaikinFanMax) - fanset = kDaikinFanAuto; - else - fanset = 2 + fan; - _.Fan = fanset; -} - -/// Get the current fan speed setting. -/// @return The current fan speed. -uint8_t IRDaikin216::getFan(void) const { - uint8_t fan = _.Fan; - if (fan != kDaikinFanQuiet && fan != kDaikinFanAuto) fan -= 2; - 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 IRDaikin216::convertFan(const stdAc::fanspeed_t speed) { - return IRDaikinESP::convertFan(speed); -} - -/// Set the Vertical Swing mode of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRDaikin216::setSwingVertical(const bool on) { - _.SwingV = (on ? kDaikin216SwingOn : kDaikin216SwingOff); -} - -/// Get the Vertical Swing mode of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRDaikin216::getSwingVertical(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 IRDaikin216::setSwingHorizontal(const bool on) { - _.SwingH = (on ? kDaikin216SwingOn : kDaikin216SwingOff); -} - -/// Get the Horizontal Swing mode of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRDaikin216::getSwingHorizontal(void) const { return _.SwingH; } - -/// Set the Quiet mode of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -/// @note This is a horrible hack till someone works out the quiet mode bit. -void IRDaikin216::setQuiet(const bool on) { - if (on) { - setFan(kDaikinFanQuiet); - // Powerful & Quiet mode being on are mutually exclusive. - setPowerful(false); - } else if (getFan() == kDaikinFanQuiet) { - setFan(kDaikinFanAuto); - } -} - -/// Get the Quiet mode status of the A/C. -/// @return true, the setting is on. false, the setting is off. -/// @note This is a horrible hack till someone works out the quiet mode bit. -bool IRDaikin216::getQuiet(void) const { return getFan() == kDaikinFanQuiet; } - -/// Set the Powerful (Turbo) mode of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRDaikin216::setPowerful(const bool on) { - _.Powerful = on; - // Powerful & Quiet mode being on are mutually exclusive. - if (on) setQuiet(false); -} - -/// Get the Powerful (Turbo) mode of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRDaikin216::getPowerful(void) const { return _.Powerful; } - -/// Convert the current internal state into its stdAc::state_t equivalent. -/// @return The stdAc equivalent of the native settings. -stdAc::state_t IRDaikin216::toCommon(void) const { - stdAc::state_t result; - result.protocol = decode_type_t::DAIKIN216; - result.model = -1; // No models used. - result.power = _.Power; - result.mode = IRDaikinESP::toCommonMode(_.Mode); - result.celsius = true; - result.degrees = _.Temp; - result.fanspeed = IRDaikinESP::toCommonFanSpeed(getFan()); - result.swingv = _.SwingV ? stdAc::swingv_t::kAuto : - stdAc::swingv_t::kOff; - result.swingh = _.SwingH ? stdAc::swingh_t::kAuto : - stdAc::swingh_t::kOff; - result.quiet = getQuiet(); - result.turbo = _.Powerful; - // Not supported. - result.light = false; - result.clean = 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 IRDaikin216::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, kDaikinAuto, kDaikinCool, kDaikinHeat, - kDaikinDry, kDaikinFan); - result += addTempToString(_.Temp); - result += addFanToString(getFan(), kDaikinFanMax, kDaikinFanMin, - kDaikinFanAuto, kDaikinFanQuiet, kDaikinFanMed); - result += addBoolToString(_.SwingH, kSwingHStr); - result += addBoolToString(_.SwingV, kSwingVStr); - result += addBoolToString(getQuiet(), kQuietStr); - result += addBoolToString(_.Powerful, kPowerfulStr); - return result; -} - -#if DECODE_DAIKIN216 -/// Decode the supplied Daikin 216-bit message. (DAIKIN216) -/// Status: STABLE / Should 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/689 -/// @see https://github.com/danny-source/Arduino_DY_IRDaikin -bool IRrecv::decodeDaikin216(decode_results *results, uint16_t offset, - const uint16_t nbits, const bool strict) { - if (results->rawlen < 2 * (nbits + kHeader + kFooter) - 1 + offset) - return false; - - // Compliance - if (strict && nbits != kDaikin216Bits) return false; - - const uint8_t ksectionSize[kDaikin216Sections] = {kDaikin216Section1Length, - kDaikin216Section2Length}; - // Sections - uint16_t pos = 0; - for (uint8_t section = 0; section < kDaikin216Sections; section++) { - uint16_t used; - // Section Header + Section Data + Section Footer - used = matchGeneric(results->rawbuf + offset, results->state + pos, - results->rawlen - offset, ksectionSize[section] * 8, - kDaikin216HdrMark, kDaikin216HdrSpace, - kDaikin216BitMark, kDaikin216OneSpace, - kDaikin216BitMark, kDaikin216ZeroSpace, - kDaikin216BitMark, kDaikin216Gap, - section >= kDaikin216Sections - 1, - kDaikinTolerance, kDaikinMarkExcess, false); - if (used == 0) return false; - offset += used; - pos += ksectionSize[section]; - } - // Compliance - if (strict) { - if (pos * 8 != kDaikin216Bits) return false; - // Validate the checksum. - if (!IRDaikin216::validChecksum(results->state)) return false; - } - - // Success - results->decode_type = decode_type_t::DAIKIN216; - 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_DAIKIN216 - -#if SEND_DAIKIN160 -/// Send a Daikin160 (160-bit) A/C formatted message. -/// Status: STABLE / Confirmed 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. -/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/731 -void IRsend::sendDaikin160(const unsigned char data[], const uint16_t nbytes, - const uint16_t repeat) { - if (nbytes < kDaikin160Section1Length) - return; // Not enough bytes to send a partial message. - - for (uint16_t r = 0; r <= repeat; r++) { - // Section #1 - sendGeneric(kDaikin160HdrMark, kDaikin160HdrSpace, kDaikin160BitMark, - kDaikin160OneSpace, kDaikin160BitMark, kDaikin160ZeroSpace, - kDaikin160BitMark, kDaikin160Gap, data, - kDaikin160Section1Length, - kDaikin160Freq, false, 0, kDutyDefault); - // Section #2 - sendGeneric(kDaikin160HdrMark, kDaikin160HdrSpace, kDaikin160BitMark, - kDaikin160OneSpace, kDaikin160BitMark, kDaikin160ZeroSpace, - kDaikin160BitMark, kDaikin160Gap, - data + kDaikin160Section1Length, - nbytes - kDaikin160Section1Length, - kDaikin160Freq, false, 0, kDutyDefault); - } -} -#endif // SEND_DAIKIN160 - -/// 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? -IRDaikin160::IRDaikin160(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 IRDaikin160::begin(void) { _irsend.begin(); } - -/// 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 IRDaikin160::validChecksum(uint8_t state[], const uint16_t length) { - // Validate the checksum of section #1. - if (length <= kDaikin160Section1Length - 1 || - state[kDaikin160Section1Length - 1] != sumBytes( - state, kDaikin160Section1Length - 1)) - return false; - // Validate the checksum of section #2 (a.k.a. the rest) - if (length <= kDaikin160Section1Length + 1 || - state[length - 1] != sumBytes(state + kDaikin160Section1Length, - length - kDaikin160Section1Length - 1)) - return false; - return true; -} - -/// Calculate and set the checksum values for the internal state. -void IRDaikin160::checksum(void) { - _.Sum1 = sumBytes(_.raw, kDaikin160Section1Length - 1); - _.Sum2 = sumBytes(_.raw + kDaikin160Section1Length, - kDaikin160Section2Length - 1); -} - -/// Reset the internal state to a fixed known good state. -void IRDaikin160::stateReset(void) { - for (uint8_t i = 0; i < kDaikin160StateLength; i++) _.raw[i] = 0x00; - _.raw[0] = 0x11; - _.raw[1] = 0xDA; - _.raw[2] = 0x27; - _.raw[3] = 0xF0; - _.raw[4] = 0x0D; - // _.raw[6] is a checksum byte, it will be set by checksum(). - _.raw[7] = 0x11; - _.raw[8] = 0xDA; - _.raw[9] = 0x27; - _.raw[11] = 0xD3; - _.raw[12] = 0x30; - _.raw[13] = 0x11; - _.raw[16] = 0x1E; - _.raw[17] = 0x0A; - _.raw[18] = 0x08; - // _.raw[19] is a checksum byte, it will be set by checksum(). -} - -/// 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 *IRDaikin160::getRaw(void) { - checksum(); // Ensure correct settings before sending. - return _.raw; -} - -/// Set the internal state from a valid code for this protocol. -/// @param[in] new_code A valid code for this protocol. -void IRDaikin160::setRaw(const uint8_t new_code[]) { - std::memcpy(_.raw, new_code, kDaikin160StateLength); -} - -#if SEND_DAIKIN160 -/// Send the current internal state as an IR message. -/// @param[in] repeat Nr. of times the message will be repeated. -void IRDaikin160::send(const uint16_t repeat) { - _irsend.sendDaikin160(getRaw(), kDaikin160StateLength, repeat); -} -#endif // SEND_DAIKIN160 - -/// Change the power setting to On. -void IRDaikin160::on(void) { setPower(true); } - -/// Change the power setting to Off. -void IRDaikin160::off(void) { setPower(false); } - -/// Change the power setting. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRDaikin160::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 IRDaikin160::getPower(void) const { return _.Power; } - -/// Get the operating mode setting of the A/C. -/// @return The current operating mode setting. -uint8_t IRDaikin160::getMode(void) const { return _.Mode; } - -/// Set the operating mode of the A/C. -/// @param[in] mode The desired operating mode. -void IRDaikin160::setMode(const uint8_t mode) { - switch (mode) { - case kDaikinAuto: - case kDaikinCool: - case kDaikinHeat: - case kDaikinFan: - case kDaikinDry: - _.Mode = mode; - break; - default: _.Mode = kDaikinAuto; - } -} - -/// 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 IRDaikin160::convertMode(const stdAc::opmode_t mode) { - return IRDaikinESP::convertMode(mode); -} - -/// Set the temperature. -/// @param[in] temp The temperature in degrees celsius. -void IRDaikin160::setTemp(const uint8_t temp) { - uint8_t degrees = std::max(temp, kDaikinMinTemp); - degrees = std::min(degrees, kDaikinMaxTemp) - 10; - _.Temp = degrees; -} - -/// Get the current temperature setting. -/// @return The current setting for temp. in degrees celsius. -uint8_t IRDaikin160::getTemp(void) const { return _.Temp + 10; } - -/// Set the speed of the fan. -/// @param[in] fan The desired setting. -/// @note 1-5 or kDaikinFanAuto or kDaikinFanQuiet -void IRDaikin160::setFan(const uint8_t fan) { - uint8_t fanset; - if (fan == kDaikinFanQuiet || fan == kDaikinFanAuto) - fanset = fan; - else if (fan < kDaikinFanMin || fan > kDaikinFanMax) - fanset = kDaikinFanAuto; - else - fanset = 2 + fan; - _.Fan = fanset; -} - -/// Get the current fan speed setting. -/// @return The current fan speed. -uint8_t IRDaikin160::getFan(void) const { - uint8_t fan = _.Fan; - if (fan != kDaikinFanQuiet && fan != kDaikinFanAuto) fan -= 2; - 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 IRDaikin160::convertFan(const stdAc::fanspeed_t speed) { - switch (speed) { - case stdAc::fanspeed_t::kMin: return kDaikinFanMin; - case stdAc::fanspeed_t::kLow: return kDaikinFanMin + 1; - case stdAc::fanspeed_t::kMedium: return kDaikinFanMin + 2; - case stdAc::fanspeed_t::kHigh: return kDaikinFanMax - 1; - case stdAc::fanspeed_t::kMax: return kDaikinFanMax; - default: - return kDaikinFanAuto; - } -} - -/// Set the Vertical Swing mode of the A/C. -/// @param[in] position The position/mode to set the swing to. -void IRDaikin160::setSwingVertical(const uint8_t position) { - switch (position) { - case kDaikin160SwingVLowest: - case kDaikin160SwingVLow: - case kDaikin160SwingVMiddle: - case kDaikin160SwingVHigh: - case kDaikin160SwingVHighest: - case kDaikin160SwingVAuto: - _.SwingV = position; - break; - default: _.SwingV = kDaikin160SwingVAuto; - } -} - -/// Get the Vertical Swing mode of the A/C. -/// @return The native position/mode setting. -uint8_t IRDaikin160::getSwingVertical(void) const { return _.SwingV; } - -/// 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 IRDaikin160::convertSwingV(const stdAc::swingv_t position) { - switch (position) { - case stdAc::swingv_t::kHighest: - case stdAc::swingv_t::kHigh: - case stdAc::swingv_t::kMiddle: - case stdAc::swingv_t::kLow: - case stdAc::swingv_t::kLowest: - return kDaikin160SwingVHighest + 1 - (uint8_t)position; - default: - return kDaikin160SwingVAuto; - } -} - -/// Convert a native vertical swing postion to it's common equivalent. -/// @param[in] setting A native position to convert. -/// @return The common vertical swing position. -stdAc::swingv_t IRDaikin160::toCommonSwingV(const uint8_t setting) { - switch (setting) { - case kDaikin160SwingVHighest: return stdAc::swingv_t::kHighest; - case kDaikin160SwingVHigh: return stdAc::swingv_t::kHigh; - case kDaikin160SwingVMiddle: return stdAc::swingv_t::kMiddle; - case kDaikin160SwingVLow: return stdAc::swingv_t::kLow; - case kDaikin160SwingVLowest: return stdAc::swingv_t::kLowest; - default: - return stdAc::swingv_t::kAuto; - } -} - -/// Convert the current internal state into its stdAc::state_t equivalent. -/// @return The stdAc equivalent of the native settings. -stdAc::state_t IRDaikin160::toCommon(void) const { - stdAc::state_t result; - result.protocol = decode_type_t::DAIKIN160; - result.model = -1; // No models used. - result.power = _.Power; - result.mode = IRDaikinESP::toCommonMode(_.Mode); - result.celsius = true; - result.degrees = getTemp(); - result.fanspeed = IRDaikinESP::toCommonFanSpeed(getFan()); - result.swingv = toCommonSwingV(_.SwingV); - // Not supported. - result.swingh = stdAc::swingh_t::kOff; - result.quiet = false; - result.turbo = false; - result.light = false; - result.clean = 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 IRDaikin160::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, kDaikinAuto, kDaikinCool, kDaikinHeat, - kDaikinDry, kDaikinFan); - result += addTempToString(getTemp()); - result += addFanToString(getFan(), kDaikinFanMax, kDaikinFanMin, - kDaikinFanAuto, kDaikinFanQuiet, kDaikinFanMed); - result += addIntToString(_.SwingV, kSwingVStr); - result += kSpaceLBraceStr; - switch (_.SwingV) { - case kDaikin160SwingVHighest: result += kHighestStr; break; - case kDaikin160SwingVHigh: result += kHighStr; break; - case kDaikin160SwingVMiddle: result += kMiddleStr; break; - case kDaikin160SwingVLow: result += kLowStr; break; - case kDaikin160SwingVLowest: result += kLowestStr; break; - case kDaikin160SwingVAuto: result += kAutoStr; break; - default: result += kUnknownStr; - } - result += ')'; - return result; -} - -#if DECODE_DAIKIN160 -/// Decode the supplied Daikin 160-bit message. (DAIKIN160) -/// 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/731 -bool IRrecv::decodeDaikin160(decode_results *results, uint16_t offset, - const uint16_t nbits, const bool strict) { - if (results->rawlen < 2 * (nbits + kHeader + kFooter) - 1 + offset) - return false; - - // Compliance - if (strict && nbits != kDaikin160Bits) return false; - - const uint8_t ksectionSize[kDaikin160Sections] = {kDaikin160Section1Length, - kDaikin160Section2Length}; - - // Sections - uint16_t pos = 0; - for (uint8_t section = 0; section < kDaikin160Sections; section++) { - uint16_t used; - // Section Header + Section Data (7 bytes) + Section Footer - used = matchGeneric(results->rawbuf + offset, results->state + pos, - results->rawlen - offset, ksectionSize[section] * 8, - kDaikin160HdrMark, kDaikin160HdrSpace, - kDaikin160BitMark, kDaikin160OneSpace, - kDaikin160BitMark, kDaikin160ZeroSpace, - kDaikin160BitMark, kDaikin160Gap, - section >= kDaikin160Sections - 1, - kDaikinTolerance, kDaikinMarkExcess, false); - if (used == 0) return false; - offset += used; - pos += ksectionSize[section]; - } - // Compliance - if (strict) { - // Validate the checksum. - if (!IRDaikin160::validChecksum(results->state)) return false; - } - - // Success - results->decode_type = decode_type_t::DAIKIN160; - 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_DAIKIN160 - -#if SEND_DAIKIN176 -/// Send a Daikin176 (176-bit) A/C formatted message. -/// Status: STABLE / Working on a real device. -/// @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::sendDaikin176(const unsigned char data[], const uint16_t nbytes, - const uint16_t repeat) { - if (nbytes < kDaikin176Section1Length) - return; // Not enough bytes to send a partial message. - - for (uint16_t r = 0; r <= repeat; r++) { - // Section #1 - sendGeneric(kDaikin176HdrMark, kDaikin176HdrSpace, kDaikin176BitMark, - kDaikin176OneSpace, kDaikin176BitMark, kDaikin176ZeroSpace, - kDaikin176BitMark, kDaikin176Gap, data, - kDaikin176Section1Length, - kDaikin176Freq, false, 0, kDutyDefault); - // Section #2 - sendGeneric(kDaikin176HdrMark, kDaikin176HdrSpace, kDaikin176BitMark, - kDaikin176OneSpace, kDaikin176BitMark, kDaikin176ZeroSpace, - kDaikin176BitMark, kDaikin176Gap, - data + kDaikin176Section1Length, - nbytes - kDaikin176Section1Length, - kDaikin176Freq, false, 0, kDutyDefault); - } -} -#endif // SEND_DAIKIN176 - -/// 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? -IRDaikin176::IRDaikin176(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 IRDaikin176::begin(void) { _irsend.begin(); } - -/// 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 IRDaikin176::validChecksum(uint8_t state[], const uint16_t length) { - // Validate the checksum of section #1. - if (length <= kDaikin176Section1Length - 1 || - state[kDaikin176Section1Length - 1] != sumBytes( - state, kDaikin176Section1Length - 1)) - return false; - // Validate the checksum of section #2 (a.k.a. the rest) - if (length <= kDaikin176Section1Length + 1 || - state[length - 1] != sumBytes(state + kDaikin176Section1Length, - length - kDaikin176Section1Length - 1)) - return false; - return true; -} - -/// Calculate and set the checksum values for the internal state. -void IRDaikin176::checksum(void) { - _.Sum1 = sumBytes(_.raw, kDaikin176Section1Length - 1); - _.Sum2 = sumBytes(_.raw + kDaikin176Section1Length, - kDaikin176Section2Length - 1); -} - -/// Reset the internal state to a fixed known good state. -void IRDaikin176::stateReset(void) { - for (uint8_t i = 0; i < kDaikin176StateLength; i++) _.raw[i] = 0x00; - _.raw[0] = 0x11; - _.raw[1] = 0xDA; - _.raw[2] = 0x17; - _.raw[3] = 0x18; - _.raw[4] = 0x04; - // _.raw[6] is a checksum byte, it will be set by checksum(). - _.raw[7] = 0x11; - _.raw[8] = 0xDA; - _.raw[9] = 0x17; - _.raw[10] = 0x18; - _.raw[12] = 0x73; - _.raw[14] = 0x20; - _.raw[18] = 0x16; // Fan speed and swing - _.raw[20] = 0x20; - // _.raw[21] is a checksum byte, it will be set by checksum(). - _saved_temp = getTemp(); -} - -/// 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 *IRDaikin176::getRaw(void) { - checksum(); // Ensure correct settings before sending. - return _.raw; -} - -/// Set the internal state from a valid code for this protocol. -/// @param[in] new_code A valid code for this protocol. -void IRDaikin176::setRaw(const uint8_t new_code[]) { - std::memcpy(_.raw, new_code, kDaikin176StateLength); - _saved_temp = getTemp(); -} - -#if SEND_DAIKIN176 -/// Send the current internal state as an IR message. -/// @param[in] repeat Nr. of times the message will be repeated. -void IRDaikin176::send(const uint16_t repeat) { - _irsend.sendDaikin176(getRaw(), kDaikin176StateLength, repeat); -} -#endif // SEND_DAIKIN176 - -/// Change the power setting to On. -void IRDaikin176::on(void) { setPower(true); } - -/// Change the power setting to Off.. -void IRDaikin176::off(void) { setPower(false); } - -/// Change the power setting. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRDaikin176::setPower(const bool on) { - _.ModeButton = 0; - _.Power = on; -} - -/// Get the value of the current power setting. -/// @return true, the setting is on. false, the setting is off. -bool IRDaikin176::getPower(void) const { return _.Power; } - -/// Get the operating mode setting of the A/C. -/// @return The current operating mode setting. -uint8_t IRDaikin176::getMode(void) const { return _.Mode; } - -/// Set the operating mode of the A/C. -/// @param[in] mode The desired operating mode. -void IRDaikin176::setMode(const uint8_t mode) { - uint8_t altmode = 0; - // Set the mode bits. - _.Mode = mode; - // Daikin172 has some alternate/additional mode bits that need to be changed - // in line with the operating mode. The following few lines match up these - // bits with the corresponding operating bits. - switch (mode) { - case kDaikin176Dry: altmode = 2; break; - case kDaikin176Fan: altmode = 6; break; - case kDaikin176Auto: - case kDaikin176Cool: - case kDaikin176Heat: altmode = 7; break; - default: _.Mode = kDaikin176Cool; altmode = 7; break; - } - // Set the additional mode bits. - _.AltMode = altmode; - setTemp(_saved_temp); - // Needs to happen after setTemp() as it will clear it. - _.ModeButton = kDaikin176ModeButton; -} - -/// 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 IRDaikin176::convertMode(const stdAc::opmode_t mode) { - switch (mode) { - case stdAc::opmode_t::kDry: return kDaikin176Dry; - case stdAc::opmode_t::kHeat: return kDaikin176Heat; - case stdAc::opmode_t::kFan: return kDaikin176Fan; - case stdAc::opmode_t::kAuto: return kDaikin176Auto; - default: return kDaikin176Cool; - } -} - -/// 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 IRDaikin176::toCommonMode(const uint8_t mode) { - switch (mode) { - case kDaikin176Dry: return stdAc::opmode_t::kDry; - case kDaikin176Heat: return stdAc::opmode_t::kHeat; - case kDaikin176Fan: return stdAc::opmode_t::kFan; - case kDaikin176Auto: return stdAc::opmode_t::kAuto; - default: return stdAc::opmode_t::kCool; - } -} - -/// Set the temperature. -/// @param[in] temp The temperature in degrees celsius. -void IRDaikin176::setTemp(const uint8_t temp) { - uint8_t degrees = std::min(kDaikinMaxTemp, std::max(temp, kDaikinMinTemp)); - _saved_temp = degrees; - switch (_.Mode) { - case kDaikin176Dry: - case kDaikin176Fan: - degrees = kDaikin176DryFanTemp; break; - } - _.Temp = degrees - 9; - _.ModeButton = 0; -} - -/// Get the current temperature setting. -/// @return The current setting for temp. in degrees celsius. -uint8_t IRDaikin176::getTemp(void) const { return _.Temp + 9; } - -/// Set the speed of the fan. -/// @param[in] fan The desired setting. -/// @note 1 for Min or 3 for Max -void IRDaikin176::setFan(const uint8_t fan) { - switch (fan) { - case kDaikinFanMin: - case kDaikin176FanMax: - _.Fan = fan; - break; - default: - _.Fan = kDaikin176FanMax; - break; - } - _.ModeButton = 0; -} - -/// Get the current fan speed setting. -/// @return The current fan speed. -uint8_t IRDaikin176::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 IRDaikin176::convertFan(const stdAc::fanspeed_t speed) { - switch (speed) { - case stdAc::fanspeed_t::kMin: - case stdAc::fanspeed_t::kLow: return kDaikinFanMin; - default: return kDaikin176FanMax; - } -} - -/// Set the Horizontal Swing mode of the A/C. -/// @param[in] position The position/mode to set the swing to. -void IRDaikin176::setSwingHorizontal(const uint8_t position) { - switch (position) { - case kDaikin176SwingHOff: - case kDaikin176SwingHAuto: - _.SwingH = position; - break; - default: _.SwingH = kDaikin176SwingHAuto; - } -} - -/// Get the Horizontal Swing mode of the A/C. -/// @return The native position/mode setting. -uint8_t IRDaikin176::getSwingHorizontal(void) const { return _.SwingH; } - -/// Get the Unit Id of the A/C. -/// @return The Unit Id the A/C is to use. -uint8_t IRDaikin176::getId(void) const { return _.Id1; } - -/// Set the Unit Id of the A/C. -/// @param[in] num The Unit Id the A/C is to use. -/// @note 0 for Unit A; 1 for Unit B -void IRDaikin176::setId(const uint8_t num) { _.Id1 = _.Id2 = num; } - -/// Convert a stdAc::swingh_t enum into it's native setting. -/// @param[in] position The enum to be converted. -/// @return The native equivalent of the enum. -uint8_t IRDaikin176::convertSwingH(const stdAc::swingh_t position) { - switch (position) { - case stdAc::swingh_t::kOff: return kDaikin176SwingHOff; - case stdAc::swingh_t::kAuto: return kDaikin176SwingHAuto; - default: return kDaikin176SwingHAuto; - } -} - -/// Convert a native horizontal swing postion to it's common equivalent. -/// @param[in] setting A native position to convert. -/// @return The common horizontal swing position. -stdAc::swingh_t IRDaikin176::toCommonSwingH(const uint8_t setting) { - switch (setting) { - case kDaikin176SwingHOff: return stdAc::swingh_t::kOff; - case kDaikin176SwingHAuto: return stdAc::swingh_t::kAuto; - default: - return stdAc::swingh_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 IRDaikin176::toCommonFanSpeed(const uint8_t speed) { - return (speed == kDaikinFanMin) ? stdAc::fanspeed_t::kMin - : stdAc::fanspeed_t::kMax; -} - -/// Convert the current internal state into its stdAc::state_t equivalent. -/// @return The stdAc equivalent of the native settings. -stdAc::state_t IRDaikin176::toCommon(void) const { - stdAc::state_t result; - result.protocol = decode_type_t::DAIKIN176; - result.model = -1; // No models used. - result.power = _.Power; - result.mode = IRDaikin176::toCommonMode(_.Mode); - result.celsius = true; - result.degrees = getTemp(); - result.fanspeed = toCommonFanSpeed(_.Fan); - result.swingh = toCommonSwingH(_.SwingH); - - // Not supported. - result.swingv = stdAc::swingv_t::kOff; - result.quiet = false; - result.turbo = false; - result.light = false; - result.clean = 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 IRDaikin176::toString(void) const { - String result = ""; - result.reserve(90); // Reserve some heap for the string to reduce fragging. - result += addBoolToString(_.Power, kPowerStr, false); - result += addModeToString(_.Mode, kDaikin176Auto, kDaikin176Cool, - kDaikin176Heat, kDaikin176Dry, kDaikin176Fan); - result += addTempToString(getTemp()); - result += addFanToString(_.Fan, kDaikin176FanMax, kDaikinFanMin, - kDaikinFanMin, kDaikinFanMin, kDaikinFanMin); - result += addSwingHToString(_.SwingH, kDaikin176SwingHAuto, - kDaikin176SwingHAuto, // maxleft Unused - kDaikin176SwingHAuto, // left Unused - kDaikin176SwingHAuto, // middle Unused - kDaikin176SwingHAuto, // right Unused - kDaikin176SwingHAuto, // maxright Unused - kDaikin176SwingHOff, - // Below are unused. - kDaikin176SwingHAuto, - kDaikin176SwingHAuto, - kDaikin176SwingHAuto, - kDaikin176SwingHAuto); - result += addIntToString(_.Id1, kIdStr); - return result; -} - -#if DECODE_DAIKIN176 -/// Decode the supplied Daikin 176-bit message. (DAIKIN176) -/// 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::decodeDaikin176(decode_results *results, uint16_t offset, - const uint16_t nbits, - const bool strict) { - if (results->rawlen < 2 * (nbits + kHeader + kFooter) - 1 + offset) - return false; - - // Compliance - if (strict && nbits != kDaikin176Bits) return false; - - const uint8_t ksectionSize[kDaikin176Sections] = {kDaikin176Section1Length, - kDaikin176Section2Length}; - - // Sections - uint16_t pos = 0; - for (uint8_t section = 0; section < kDaikin176Sections; section++) { - uint16_t used; - // Section Header + Section Data (7 bytes) + Section Footer - used = matchGeneric(results->rawbuf + offset, results->state + pos, - results->rawlen - offset, ksectionSize[section] * 8, - kDaikin176HdrMark, kDaikin176HdrSpace, - kDaikin176BitMark, kDaikin176OneSpace, - kDaikin176BitMark, kDaikin176ZeroSpace, - kDaikin176BitMark, kDaikin176Gap, - section >= kDaikin176Sections - 1, - kDaikinTolerance, kDaikinMarkExcess, false); - if (used == 0) return false; - offset += used; - pos += ksectionSize[section]; - } - // Compliance - if (strict) { - // Validate the checksum. - if (!IRDaikin176::validChecksum(results->state)) return false; - } - - // Success - results->decode_type = decode_type_t::DAIKIN176; - 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_DAIKIN176 - -#if SEND_DAIKIN128 -/// Send a Daikin128 (128-bit) A/C formatted 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. -/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/827 -void IRsend::sendDaikin128(const unsigned char data[], const uint16_t nbytes, - const uint16_t repeat) { - if (nbytes < kDaikin128SectionLength) - return; // Not enough bytes to send a partial message. - - for (uint16_t r = 0; r <= repeat; r++) { - enableIROut(kDaikin128Freq); - // Leader - for (uint8_t i = 0; i < 2; i++) { - mark(kDaikin128LeaderMark); - space(kDaikin128LeaderSpace); - } - // Section #1 (Header + Data) - sendGeneric(kDaikin128HdrMark, kDaikin128HdrSpace, kDaikin128BitMark, - kDaikin128OneSpace, kDaikin128BitMark, kDaikin128ZeroSpace, - kDaikin128BitMark, kDaikin128Gap, data, - kDaikin128SectionLength, - kDaikin128Freq, false, 0, kDutyDefault); - // Section #2 (Data + Footer) - sendGeneric(0, 0, kDaikin128BitMark, - kDaikin128OneSpace, kDaikin128BitMark, kDaikin128ZeroSpace, - kDaikin128FooterMark, kDaikin128Gap, - data + kDaikin128SectionLength, - nbytes - kDaikin128SectionLength, - kDaikin128Freq, false, 0, kDutyDefault); - } -} -#endif // SEND_DAIKIN128 - -/// 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? -IRDaikin128::IRDaikin128(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 IRDaikin128::begin(void) { _irsend.begin(); } - -uint8_t IRDaikin128::calcFirstChecksum(const uint8_t state[]) { - return sumNibbles(state, kDaikin128SectionLength - 1, - state[kDaikin128SectionLength - 1] & 0x0F) & 0x0F; -} - -uint8_t IRDaikin128::calcSecondChecksum(const uint8_t state[]) { - return sumNibbles(state + kDaikin128SectionLength, - kDaikin128SectionLength - 1); -} - -/// 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 IRDaikin128::validChecksum(uint8_t state[]) { - // Validate the checksum of section #1. - if (state[kDaikin128SectionLength - 1] >> 4 != calcFirstChecksum(state)) - return false; - // Validate the checksum of section #2 - if (state[kDaikin128StateLength - 1] != calcSecondChecksum(state)) - return false; - return true; -} - -/// Calculate and set the checksum values for the internal state. -void IRDaikin128::checksum(void) { - _.Sum1 = calcFirstChecksum(_.raw); - _.Sum2 = calcSecondChecksum(_.raw); -} - -/// Reset the internal state to a fixed known good state. -void IRDaikin128::stateReset(void) { - for (uint8_t i = 0; i < kDaikin128StateLength; i++) _.raw[i] = 0x00; - _.raw[0] = 0x16; - _.raw[7] = 0x04; // Most significant nibble is a checksum. - _.raw[8] = 0xA1; - // _.raw[15] is a checksum byte, it will be set by checksum(). -} - -/// 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 *IRDaikin128::getRaw(void) { - checksum(); // Ensure correct settings before sending. - return _.raw; -} - -/// Set the internal state from a valid code for this protocol. -/// @param[in] new_code A valid code for this protocol. -void IRDaikin128::setRaw(const uint8_t new_code[]) { - std::memcpy(_.raw, new_code, kDaikin128StateLength); -} - -#if SEND_DAIKIN128 -/// Send the current internal state as an IR message. -/// @param[in] repeat Nr. of times the message will be repeated. -void IRDaikin128::send(const uint16_t repeat) { - _irsend.sendDaikin128(getRaw(), kDaikin128StateLength, repeat); -} -#endif // SEND_DAIKIN128 - -/// Set the Power toggle setting of the A/C. -/// @param[in] toggle true, the setting is on. false, the setting is off. -void IRDaikin128::setPowerToggle(const bool toggle) { _.Power = toggle; } - -/// Get the Power toggle setting of the A/C. -/// @return The current operating mode setting. -bool IRDaikin128::getPowerToggle(void) const { return _.Power; } - -/// Get the operating mode setting of the A/C. -/// @return The current operating mode setting. -uint8_t IRDaikin128::getMode(void) const { return _.Mode; } - -/// Set the operating mode of the A/C. -/// @param[in] mode The desired operating mode. -void IRDaikin128::setMode(const uint8_t mode) { - switch (mode) { - case kDaikin128Auto: - case kDaikin128Cool: - case kDaikin128Heat: - case kDaikin128Fan: - case kDaikin128Dry: - _.Mode = mode; - break; - default: - _.Mode = kDaikin128Auto; - break; - } - // Force a reset of mode dependant things. - setFan(getFan()); // Covers Quiet & Powerful too. - 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 IRDaikin128::convertMode(const stdAc::opmode_t mode) { - switch (mode) { - case stdAc::opmode_t::kCool: return kDaikin128Cool; - case stdAc::opmode_t::kHeat: return kDaikin128Heat; - case stdAc::opmode_t::kDry: return kDaikinDry; - case stdAc::opmode_t::kFan: return kDaikin128Fan; - default: return kDaikin128Auto; - } -} - -/// 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 IRDaikin128::toCommonMode(const uint8_t mode) { - switch (mode) { - case kDaikin128Cool: return stdAc::opmode_t::kCool; - case kDaikin128Heat: return stdAc::opmode_t::kHeat; - case kDaikin128Dry: return stdAc::opmode_t::kDry; - case kDaikin128Fan: return stdAc::opmode_t::kFan; - default: return stdAc::opmode_t::kAuto; - } -} - -/// Set the temperature. -/// @param[in] temp The temperature in degrees celsius. -void IRDaikin128::setTemp(const uint8_t temp) { - _.Temp = uint8ToBcd(std::min(kDaikin128MaxTemp, - std::max(temp, kDaikin128MinTemp))); -} - -/// Get the current temperature setting. -/// @return The current setting for temp. in degrees celsius. -uint8_t IRDaikin128::getTemp(void) const { return bcdToUint8(_.Temp); } - -/// Get the current fan speed setting. -/// @return The current fan speed. -uint8_t IRDaikin128::getFan(void) const { return _.Fan; } - -/// Set the speed of the fan. -/// @param[in] speed The desired setting. -void IRDaikin128::setFan(const uint8_t speed) { - uint8_t new_speed = speed; - uint8_t mode = _.Mode; - switch (speed) { - case kDaikin128FanQuiet: - case kDaikin128FanPowerful: - if (mode == kDaikin128Auto) new_speed = kDaikin128FanAuto; - // FALL-THRU - case kDaikin128FanAuto: - case kDaikin128FanHigh: - case kDaikin128FanMed: - case kDaikin128FanLow: - _.Fan = new_speed; - break; - default: - _.Fan = kDaikin128FanAuto; - return; - } -} - -/// 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 IRDaikin128::convertFan(const stdAc::fanspeed_t speed) { - switch (speed) { - case stdAc::fanspeed_t::kMin: return kDaikinFanQuiet; - case stdAc::fanspeed_t::kLow: return kDaikin128FanLow; - case stdAc::fanspeed_t::kMedium: return kDaikin128FanMed; - case stdAc::fanspeed_t::kHigh: return kDaikin128FanHigh; - case stdAc::fanspeed_t::kMax: return kDaikin128FanPowerful; - default: return kDaikin128FanAuto; - } -} - -/// 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 IRDaikin128::toCommonFanSpeed(const uint8_t speed) { - switch (speed) { - case kDaikin128FanPowerful: return stdAc::fanspeed_t::kMax; - case kDaikin128FanHigh: return stdAc::fanspeed_t::kHigh; - case kDaikin128FanMed: return stdAc::fanspeed_t::kMedium; - case kDaikin128FanLow: return stdAc::fanspeed_t::kLow; - case kDaikinFanQuiet: 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 IRDaikin128::setSwingVertical(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 IRDaikin128::getSwingVertical(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 IRDaikin128::setSleep(const bool on) { _.Sleep = on; } - -/// Get the Sleep mode of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRDaikin128::getSleep(void) const { return _.Sleep; } - -/// Set the Economy mode of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRDaikin128::setEcono(const bool on) { - uint8_t mode = _.Mode; - _.Econo = (on && (mode == kDaikin128Cool || mode == kDaikin128Heat)); -} - -/// Get the Economical mode of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRDaikin128::getEcono(void) const { return _.Econo; } - -/// Set the Quiet mode of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRDaikin128::setQuiet(const bool on) { - uint8_t mode = _.Mode; - if (on && (mode == kDaikin128Cool || mode == kDaikin128Heat)) - setFan(kDaikin128FanQuiet); - else if (_.Fan == kDaikin128FanQuiet) - setFan(kDaikin128FanAuto); -} - -/// Get the Quiet mode status of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRDaikin128::getQuiet(void) const { return _.Fan == kDaikin128FanQuiet; } - -/// Set the Powerful (Turbo) mode of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRDaikin128::setPowerful(const bool on) { - uint8_t mode = _.Mode; - if (on && (mode == kDaikin128Cool || mode == kDaikin128Heat)) - setFan(kDaikin128FanPowerful); - else if (_.Fan == kDaikin128FanPowerful) - setFan(kDaikin128FanAuto); -} - -/// Get the Powerful (Turbo) mode of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRDaikin128::getPowerful(void) const { - return _.Fan == kDaikin128FanPowerful; -} - -/// Set the clock on the A/C unit. -/// @param[in] mins_since_midnight Nr. of minutes past midnight. -void IRDaikin128::setClock(const uint16_t mins_since_midnight) { - uint16_t mins = mins_since_midnight; - if (mins_since_midnight >= 24 * 60) mins = 0; // Bounds check. - // Hours. - _.ClockHours = uint8ToBcd(mins / 60); - // Minutes. - _.ClockMins = uint8ToBcd(mins % 60); -} - -/// Get the clock time to be sent to the A/C unit. -/// @return The number of minutes past midnight. -uint16_t IRDaikin128::getClock(void) const { - return bcdToUint8(_.ClockHours) * 60 + bcdToUint8(_.ClockMins); -} - -/// Set the enable status of the On Timer. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRDaikin128::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 IRDaikin128::getOnTimerEnabled(void) const { return _.OnTimer; } - -#define SETTIME(x, n) do { \ - uint16_t mins = n;\ - if (n >= 24 * 60) mins = 0;\ - _.x##HalfHour = (mins % 60) >= 30;\ - _.x##Hours = uint8ToBcd(mins / 60);\ -} while (0) - -#define GETTIME(x) bcdToUint8(_.x##Hours) * 60 + (_.x##HalfHour ? 30 : 0) - -/// Set the On Timer time for the A/C unit. -/// @param[in] mins_since_midnight Nr. of minutes past midnight. -void IRDaikin128::setOnTimer(const uint16_t mins_since_midnight) { - SETTIME(On, mins_since_midnight); -} - -/// Get the On Timer time to be sent to the A/C unit. -/// @return The number of minutes past midnight. -uint16_t IRDaikin128::getOnTimer(void) const { return GETTIME(On); } - -/// Set the enable status of the Off Timer. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRDaikin128::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 IRDaikin128::getOffTimerEnabled(void) const { return _.OffTimer; } - -/// Set the Off Timer time for the A/C unit. -/// @param[in] mins_since_midnight Nr. of minutes past midnight. -void IRDaikin128::setOffTimer(const uint16_t mins_since_midnight) { - SETTIME(Off, mins_since_midnight); -} - -/// Get the Off Timer time to be sent to the A/C unit. -/// @return The number of minutes past midnight. -uint16_t IRDaikin128::getOffTimer(void) const { return GETTIME(Off); } - -/// Set the Light toggle setting of the A/C. -/// @param[in] unit Device to show the LED (Light) Display info about. -/// @note 0 is off. -void IRDaikin128::setLightToggle(const uint8_t unit) { - _.Ceiling = 0; - _.Wall = 0; - switch (unit) { - case kDaikin128BitCeiling: - _.Ceiling = 1; - break; - case kDaikin128BitWall: - _.Wall = 1; - break; - } -} - -/// Get the Light toggle setting of the A/C. -/// @return The current operating mode setting. -uint8_t IRDaikin128::getLightToggle(void) const { - uint8_t code = 0; - if (_.Ceiling) { - code = kDaikin128BitCeiling; - } else if (_.Wall) { - code = kDaikin128BitWall; - } - - return code; -} - -/// Convert the current internal state into a human readable string. -/// @return A human readable string. -String IRDaikin128::toString(void) const { - String result = ""; - result.reserve(240); // Reserve some heap for the string to reduce fragging. - result += addBoolToString(_.Power, kPowerToggleStr, false); - result += addModeToString(_.Mode, kDaikin128Auto, kDaikin128Cool, - kDaikin128Heat, kDaikin128Dry, kDaikin128Fan); - result += addTempToString(getTemp()); - result += addFanToString(_.Fan, kDaikin128FanHigh, kDaikin128FanLow, - kDaikin128FanAuto, kDaikin128FanQuiet, - kDaikin128FanMed); - result += addBoolToString(getPowerful(), kPowerfulStr); - result += addBoolToString(getQuiet(), kQuietStr); - result += addBoolToString(_.SwingV, kSwingVStr); - result += addBoolToString(_.Sleep, kSleepStr); - result += addBoolToString(_.Econo, kEconoStr); - result += addLabeledString(minsToString(getClock()), kClockStr); - result += addBoolToString(_.OnTimer, kOnTimerStr); - result += addLabeledString(minsToString(getOnTimer()), kOnTimerStr); - result += addBoolToString(_.OffTimer, kOffTimerStr); - result += addLabeledString(minsToString(getOffTimer()), kOffTimerStr); - result += addIntToString(getLightToggle(), kLightToggleStr); - result += kSpaceLBraceStr; - switch (getLightToggle()) { - case kDaikin128BitCeiling: result += kCeilingStr; break; - case kDaikin128BitWall: result += kWallStr; break; - case 0: result += kOffStr; break; - default: result += kUnknownStr; - } - result += ')'; - return result; -} - -/// Convert the current internal state into its stdAc::state_t equivalent. -/// @param[in] prev Ptr to a previous state. -/// @return The stdAc equivalent of the native settings. -stdAc::state_t IRDaikin128::toCommon(const stdAc::state_t *prev) const { - stdAc::state_t result; - if (prev != NULL) result = *prev; - result.protocol = decode_type_t::DAIKIN128; - 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.quiet = getQuiet(); - result.turbo = getPowerful(); - result.econo = _.Econo; - result.light ^= (getLightToggle() != 0); - result.sleep = _.Sleep ? 0 : -1; - result.clock = getClock(); - // Not supported. - result.swingh = stdAc::swingh_t::kOff; - result.clean = false; - result.filter = false; - result.beep = false; - return result; -} - -#if DECODE_DAIKIN128 -/// Decode the supplied Daikin 128-bit message. (DAIKIN128) -/// 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. -/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/827 -bool IRrecv::decodeDaikin128(decode_results *results, uint16_t offset, - const uint16_t nbits, const bool strict) { - if (results->rawlen < 2 * (nbits + kHeader) + kFooter - 1 + offset) - return false; - if (nbits / 8 <= kDaikin128SectionLength) return false; - - // Compliance - if (strict && nbits != kDaikin128Bits) return false; - - // Leader - for (uint8_t i = 0; i < 2; i++) { - if (!matchMark(results->rawbuf[offset++], kDaikin128LeaderMark, - kDaikinTolerance, kDaikinMarkExcess)) return false; - if (!matchSpace(results->rawbuf[offset++], kDaikin128LeaderSpace, - kDaikinTolerance, kDaikinMarkExcess)) return false; - } - const uint16_t ksectionSize[kDaikin128Sections] = { - kDaikin128SectionLength, (uint16_t)(nbits / 8 - kDaikin128SectionLength)}; - // Data Sections - uint16_t pos = 0; - for (uint8_t section = 0; section < kDaikin128Sections; section++) { - uint16_t used; - // Section Header (first section only) + Section Data (8 bytes) + - // Section Footer (Not for first section) - used = matchGeneric(results->rawbuf + offset, results->state + pos, - results->rawlen - offset, ksectionSize[section] * 8, - section == 0 ? kDaikin128HdrMark : 0, - section == 0 ? kDaikin128HdrSpace : 0, - kDaikin128BitMark, kDaikin128OneSpace, - kDaikin128BitMark, kDaikin128ZeroSpace, - section > 0 ? kDaikin128FooterMark : kDaikin128BitMark, - kDaikin128Gap, - section > 0, - kDaikinTolerance, kDaikinMarkExcess, false); - if (used == 0) return false; - offset += used; - pos += ksectionSize[section]; - } - // Compliance - if (strict) { - if (!IRDaikin128::validChecksum(results->state)) return false; - } - - // Success - results->decode_type = decode_type_t::DAIKIN128; - 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_DAIKIN128 - -#if SEND_DAIKIN152 -/// Send a Daikin152 (152-bit) A/C formatted 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. -/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/873 -void IRsend::sendDaikin152(const unsigned char data[], const uint16_t nbytes, - const uint16_t repeat) { - for (uint16_t r = 0; r <= repeat; r++) { - // Leader - sendGeneric(0, 0, kDaikin152BitMark, kDaikin152OneSpace, - kDaikin152BitMark, kDaikin152ZeroSpace, - kDaikin152BitMark, kDaikin152Gap, - (uint64_t)0, kDaikin152LeaderBits, - kDaikin152Freq, false, 0, kDutyDefault); - // Header + Data + Footer - sendGeneric(kDaikin152HdrMark, kDaikin152HdrSpace, kDaikin152BitMark, - kDaikin152OneSpace, kDaikin152BitMark, kDaikin152ZeroSpace, - kDaikin152BitMark, kDaikin152Gap, data, - nbytes, kDaikin152Freq, false, 0, kDutyDefault); - } -} -#endif // SEND_DAIKIN152 - -#if DECODE_DAIKIN152 -/// Decode the supplied Daikin 152-bit message. (DAIKIN152) -/// 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. -/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/873 -bool IRrecv::decodeDaikin152(decode_results *results, uint16_t offset, - const uint16_t nbits, const bool strict) { - if (results->rawlen < 2 * (5 + nbits + kFooter) + kHeader - 1 + offset) - return false; - if (nbits / 8 < kDaikin152StateLength) return false; - - // Compliance - if (strict && nbits != kDaikin152Bits) return false; - - uint16_t used; - - // Leader - uint64_t leader = 0; - used = matchGeneric(results->rawbuf + offset, &leader, - results->rawlen - offset, kDaikin152LeaderBits, - 0, 0, // No Header - kDaikin152BitMark, kDaikin152OneSpace, - kDaikin152BitMark, kDaikin152ZeroSpace, - kDaikin152BitMark, kDaikin152Gap, // Footer gap - false, _tolerance, kMarkExcess, false); - if (used == 0 || leader != 0) return false; - offset += used; - - // Header + Data + Footer - used = matchGeneric(results->rawbuf + offset, results->state, - results->rawlen - offset, nbits, - kDaikin152HdrMark, kDaikin152HdrSpace, - kDaikin152BitMark, kDaikin152OneSpace, - kDaikin152BitMark, kDaikin152ZeroSpace, - kDaikin152BitMark, kDaikin152Gap, - true, _tolerance, kMarkExcess, false); - if (used == 0) return false; - - // Compliance - if (strict) { - if (!IRDaikin152::validChecksum(results->state)) return false; - } - - // Success - results->decode_type = decode_type_t::DAIKIN152; - 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_DAIKIN152 - -/// 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? -IRDaikin152::IRDaikin152(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 IRDaikin152::begin(void) { _irsend.begin(); } - -#if SEND_DAIKIN152 -/// Send the current internal state as an IR message. -/// @param[in] repeat Nr. of times the message will be repeated. -void IRDaikin152::send(const uint16_t repeat) { - _irsend.sendDaikin152(getRaw(), kDaikin152StateLength, repeat); -} -#endif // SEND_DAIKIN152 - -/// 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 IRDaikin152::validChecksum(uint8_t state[], const uint16_t length) { - // Validate the checksum of the given state. - if (length <= 1 || state[length - 1] != sumBytes(state, length - 1)) - return false; - else - return true; -} - -/// Calculate and set the checksum values for the internal state. -void IRDaikin152::checksum(void) { - _.Sum = sumBytes(_.raw, kDaikin152StateLength - 1); -} - -/// Reset the internal state to a fixed known good state. -void IRDaikin152::stateReset(void) { - for (uint8_t i = 3; i < kDaikin152StateLength; i++) _.raw[i] = 0x00; - _.raw[0] = 0x11; - _.raw[1] = 0xDA; - _.raw[2] = 0x27; - _.raw[15] = 0xC5; - // _.raw[19] is a checksum byte, it will be set by checksum(). -} - -/// 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 *IRDaikin152::getRaw(void) { - checksum(); // Ensure correct settings before sending. - return _.raw; -} - -/// Set the internal state from a valid code for this protocol. -/// @param[in] new_code A valid code for this protocol. -void IRDaikin152::setRaw(const uint8_t new_code[]) { - std::memcpy(_.raw, new_code, kDaikin152StateLength); -} - -/// Change the power setting to On. -void IRDaikin152::on(void) { setPower(true); } - -/// Change the power setting to Off. -void IRDaikin152::off(void) { setPower(false); } - -/// Change the power setting. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRDaikin152::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 IRDaikin152::getPower(void) const { return _.Power; } - -/// Get the operating mode setting of the A/C. -/// @return The current operating mode setting. -uint8_t IRDaikin152::getMode(void) const { return _.Mode; } - -/// Set the operating mode of the A/C. -/// @param[in] mode The desired operating mode. -void IRDaikin152::setMode(const uint8_t mode) { - switch (mode) { - case kDaikinFan: - setTemp(kDaikin152FanTemp); // Handle special temp for fan mode. - break; - case kDaikinDry: - setTemp(kDaikin152DryTemp); // Handle special temp for dry mode. - break; - case kDaikinAuto: - case kDaikinCool: - case kDaikinHeat: - break; - default: - _.Mode = kDaikinAuto; - return; - } - _.Mode = 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 IRDaikin152::convertMode(const stdAc::opmode_t mode) { - return IRDaikinESP::convertMode(mode); -} - -/// Set the temperature. -/// @param[in] temp The temperature in degrees celsius. -void IRDaikin152::setTemp(const uint8_t temp) { - uint8_t degrees = std::max( - temp, (_.Mode == kDaikinHeat) ? kDaikinMinTemp : kDaikin2MinCoolTemp); - degrees = std::min(degrees, kDaikinMaxTemp); - if (temp == kDaikin152FanTemp) degrees = temp; // Handle fan only temp. - _.Temp = degrees; -} - -/// Get the current temperature setting. -/// @return The current setting for temp. in degrees celsius. -uint8_t IRDaikin152::getTemp(void) const { return _.Temp; } - -/// Set the speed of the fan. -/// @param[in] fan The desired setting. -/// @note 1-5 or kDaikinFanAuto or kDaikinFanQuiet -void IRDaikin152::setFan(const uint8_t fan) { - // Set the fan speed bits, leave low 4 bits alone - uint8_t fanset; - if (fan == kDaikinFanQuiet || fan == kDaikinFanAuto) - fanset = fan; - else if (fan < kDaikinFanMin || fan > kDaikinFanMax) - fanset = kDaikinFanAuto; - else - fanset = 2 + fan; - _.Fan = fanset; -} - -/// Get the current fan speed setting. -/// @return The current fan speed. -uint8_t IRDaikin152::getFan(void) const { - const uint8_t fan = _.Fan; - switch (fan) { - case kDaikinFanAuto: - case kDaikinFanQuiet: return fan; - default: return fan - 2; - } -} - -/// 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 IRDaikin152::convertFan(const stdAc::fanspeed_t speed) { - return IRDaikinESP::convertFan(speed); -} - -/// Set the Vertical Swing mode of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRDaikin152::setSwingV(const bool on) { - _.SwingV = (on ? kDaikinSwingOn : kDaikinSwingOff); -} - -/// Get the Vertical Swing mode of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRDaikin152::getSwingV(void) const { return _.SwingV; } - -/// Set the Quiet mode of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRDaikin152::setQuiet(const bool on) { - _.Quiet = on; - // Powerful & Quiet mode being on are mutually exclusive. - if (on) setPowerful(false); -} - -/// Get the Quiet mode status of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRDaikin152::getQuiet(void) const { return _.Quiet; } - -/// Set the Powerful (Turbo) mode of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRDaikin152::setPowerful(const bool on) { - _.Powerful = on; - if (on) { - // Powerful, Quiet, Comfort & Econo mode being on are mutually exclusive. - setQuiet(false); - setComfort(false); - setEcono(false); - } -} - -/// Get the Powerful (Turbo) mode of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRDaikin152::getPowerful(void) const { return _.Powerful; } - -/// Set the Economy mode of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRDaikin152::setEcono(const bool on) { - _.Econo = on; - // Powerful & Econo mode being on are mutually exclusive. - if (on) setPowerful(false); -} - -/// Get the Economical mode of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRDaikin152::getEcono(void) const { return _.Econo; } - -/// Set the Sensor mode of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRDaikin152::setSensor(const bool on) { _.Sensor = on; } - -/// Get the Sensor mode of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRDaikin152::getSensor(void) const { return _.Sensor; } - -/// Set the Comfort mode of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRDaikin152::setComfort(const bool on) { - _.Comfort = on; - if (on) { - // Comfort mode is incompatible with Powerful mode. - setPowerful(false); - // It also sets the fan to auto and turns off swingv. - setFan(kDaikinFanAuto); - setSwingV(false); - } -} - -/// Get the Comfort mode of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRDaikin152::getComfort(void) const { return _.Comfort; } - -/// Convert the current internal state into its stdAc::state_t equivalent. -/// @return The stdAc equivalent of the native settings. -stdAc::state_t IRDaikin152::toCommon(void) const { - stdAc::state_t result; - result.protocol = decode_type_t::DAIKIN152; - result.model = -1; // No models used. - result.power = _.Power; - result.mode = IRDaikinESP::toCommonMode(_.Mode); - result.celsius = true; - result.degrees = _.Temp; - result.fanspeed = IRDaikinESP::toCommonFanSpeed(getFan()); - result.swingv = _.SwingV ? stdAc::swingv_t::kAuto : stdAc::swingv_t::kOff; - result.quiet = _.Quiet; - result.turbo = _.Powerful; - result.econo = _.Econo; - // Not supported. - result.swingh = stdAc::swingh_t::kOff; - result.clean = false; - result.filter = false; - result.light = 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 IRDaikin152::toString(void) const { - String result = ""; - result.reserve(180); // Reserve some heap for the string to reduce fragging. - result += addBoolToString(_.Power, kPowerStr, false); - result += addModeToString(_.Mode, kDaikinAuto, kDaikinCool, kDaikinHeat, - kDaikinDry, kDaikinFan); - result += addTempToString(_.Temp); - result += addFanToString(getFan(), kDaikinFanMax, kDaikinFanMin, - kDaikinFanAuto, kDaikinFanQuiet, kDaikinFanMed); - result += addBoolToString(_.SwingV, kSwingVStr); - result += addBoolToString(_.Powerful, kPowerfulStr); - result += addBoolToString(_.Quiet, kQuietStr); - result += addBoolToString(_.Econo, kEconoStr); - result += addBoolToString(_.Sensor, kSensorStr); - result += addBoolToString(_.Comfort, kComfortStr); - return result; -} - -#if SEND_DAIKIN64 -/// Send a Daikin64 (64-bit) A/C 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. -/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1064 -void IRsend::sendDaikin64(const uint64_t data, const uint16_t nbits, - const uint16_t repeat) { - enableIROut(kDaikin64Freq); - for (uint16_t r = 0; r <= repeat; r++) { - for (uint8_t i = 0; i < 2; i++) { - // Leader - mark(kDaikin64LdrMark); - space(kDaikin64LdrSpace); - } - // Header + Data + Footer #1 - sendGeneric(kDaikin64HdrMark, kDaikin64HdrSpace, - kDaikin64BitMark, kDaikin64OneSpace, - kDaikin64BitMark, kDaikin64ZeroSpace, - kDaikin64BitMark, kDaikin64Gap, - data, nbits, kDaikin64Freq, false, 0, 50); - // Footer #2 - mark(kDaikin64HdrMark); - space(kDefaultMessageGap); // A guess of the gap between messages. - } -} -#endif // SEND_DAIKIN64 - -#if DECODE_DAIKIN64 -/// Decode the supplied Daikin 64-bit message. (DAIKIN64) -/// 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/1064 -bool IRrecv::decodeDaikin64(decode_results *results, uint16_t offset, - const uint16_t nbits, const bool strict) { - if (results->rawlen < 2 * nbits + kDaikin64Overhead - offset) - return false; // Too short a message to match. - // Compliance - if (strict && nbits != kDaikin64Bits) - return false; - - // Leader - for (uint8_t i = 0; i < 2; i++) { - if (!matchMark(results->rawbuf[offset++], kDaikin64LdrMark)) - return false; - if (!matchSpace(results->rawbuf[offset++], kDaikin64LdrSpace)) - return false; - } - // Header + Data + Footer #1 - uint16_t used = matchGeneric(results->rawbuf + offset, &results->value, - results->rawlen - offset, nbits, - kDaikin64HdrMark, kDaikin64HdrSpace, - kDaikin64BitMark, kDaikin64OneSpace, - kDaikin64BitMark, kDaikin64ZeroSpace, - kDaikin64BitMark, kDaikin64Gap, - false, _tolerance + kDaikin64ToleranceDelta, - kMarkExcess, false); - if (used == 0) return false; - offset += used; - // Footer #2 - if (!matchMark(results->rawbuf[offset++], kDaikin64HdrMark)) - return false; - - // Compliance - if (strict && !IRDaikin64::validChecksum(results->value)) return false; - // Success - results->decode_type = decode_type_t::DAIKIN64; - results->bits = nbits; - results->command = 0; - results->address = 0; - return true; -} -#endif // DAIKIN64 - -/// 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? -IRDaikin64::IRDaikin64(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 IRDaikin64::begin(void) { _irsend.begin(); } - -#if SEND_DAIKIN64 -/// Send the current internal state as an IR message. -/// @param[in] repeat Nr. of times the message will be repeated. -void IRDaikin64::send(const uint16_t repeat) { - _irsend.sendDaikin64(getRaw(), kDaikin64Bits, repeat); -} -#endif // SEND_DAIKIN64 - -/// 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 IRDaikin64::calcChecksum(const uint64_t state) { - uint64_t data = GETBITS64(state, 0, kDaikin64ChecksumOffset); - 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 state to verify the checksum of. -/// @return true, if the state has a valid checksum. Otherwise, false. -bool IRDaikin64::validChecksum(const uint64_t state) { - // Validate the checksum of the given state. - return (GETBITS64(state, kDaikin64ChecksumOffset, - kDaikin64ChecksumSize) == calcChecksum(state)); -} - -/// Calculate and set the checksum values for the internal state. -void IRDaikin64::checksum(void) { _.Sum = calcChecksum(_.raw); } - -/// Reset the internal state to a fixed known good state. -void IRDaikin64::stateReset(void) { _.raw = kDaikin64KnownGoodState; } - -/// 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 IRDaikin64::getRaw(void) { - checksum(); // Ensure correct settings before sending. - return _.raw; -} - -/// Set the internal state from a valid code for this protocol. -/// @param[in] new_state A valid code for this protocol. -void IRDaikin64::setRaw(const uint64_t new_state) { _.raw = new_state; } - -/// Set the Power toggle setting of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRDaikin64::setPowerToggle(const bool on) { _.Power = on; } - -/// Get the Power toggle setting of the A/C. -/// @return The current operating mode setting. -bool IRDaikin64::getPowerToggle(void) const { return _.Power; } - -/// Set the temperature. -/// @param[in] temp The temperature in degrees celsius. -void IRDaikin64::setTemp(const uint8_t temp) { - uint8_t degrees = std::max(temp, kDaikin64MinTemp); - degrees = std::min(degrees, kDaikin64MaxTemp); - _.Temp = uint8ToBcd(degrees); -} - -/// Get the current temperature setting. -/// @return The current setting for temp. in degrees celsius. -uint8_t IRDaikin64::getTemp(void) const { return bcdToUint8(_.Temp); } - -/// Get the operating mode setting of the A/C. -/// @return The current operating mode setting. -uint8_t IRDaikin64::getMode(void) const { return _.Mode; } - -/// Set the operating mode of the A/C. -/// @param[in] mode The desired operating mode. -void IRDaikin64::setMode(const uint8_t mode) { - switch (mode) { - case kDaikin64Fan: - case kDaikin64Dry: - case kDaikin64Cool: - case kDaikin64Heat: - _.Mode = mode; - break; - default: - _.Mode = kDaikin64Cool; - } -} - -/// 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 IRDaikin64::convertMode(const stdAc::opmode_t mode) { - switch (mode) { - case stdAc::opmode_t::kDry: return kDaikin64Dry; - case stdAc::opmode_t::kFan: return kDaikin64Fan; - case stdAc::opmode_t::kHeat: return kDaikin64Heat; - default: return kDaikin64Cool; - } -} - -/// 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 IRDaikin64::toCommonMode(const uint8_t mode) { - switch (mode) { - case kDaikin64Cool: return stdAc::opmode_t::kCool; - case kDaikin64Heat: return stdAc::opmode_t::kHeat; - case kDaikin64Dry: return stdAc::opmode_t::kDry; - case kDaikin64Fan: return stdAc::opmode_t::kFan; - default: return stdAc::opmode_t::kAuto; - } -} - -/// Get the current fan speed setting. -/// @return The current fan speed. -uint8_t IRDaikin64::getFan(void) const { return _.Fan; } - -/// Set the speed of the fan. -/// @param[in] speed The desired setting. -void IRDaikin64::setFan(const uint8_t speed) { - switch (speed) { - case kDaikin64FanQuiet: - case kDaikin64FanTurbo: - case kDaikin64FanAuto: - case kDaikin64FanHigh: - case kDaikin64FanMed: - case kDaikin64FanLow: - _.Fan = speed; - break; - default: - _.Fan = kDaikin64FanAuto; - } -} - -/// 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 IRDaikin64::convertFan(const stdAc::fanspeed_t speed) { - switch (speed) { - case stdAc::fanspeed_t::kMin: return kDaikin64FanQuiet; - case stdAc::fanspeed_t::kLow: return kDaikin64FanLow; - case stdAc::fanspeed_t::kMedium: return kDaikin64FanMed; - case stdAc::fanspeed_t::kHigh: return kDaikin64FanHigh; - case stdAc::fanspeed_t::kMax: return kDaikin64FanTurbo; - default: return kDaikin64FanAuto; - } -} - -/// 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 IRDaikin64::toCommonFanSpeed(const uint8_t speed) { - switch (speed) { - case kDaikin64FanTurbo: return stdAc::fanspeed_t::kMax; - case kDaikin64FanHigh: return stdAc::fanspeed_t::kHigh; - case kDaikin64FanMed: return stdAc::fanspeed_t::kMedium; - case kDaikin64FanLow: return stdAc::fanspeed_t::kLow; - case kDaikinFanQuiet: return stdAc::fanspeed_t::kMin; - default: return stdAc::fanspeed_t::kAuto; - } -} - -/// Get the Turbo (Powerful) mode status of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRDaikin64::getTurbo(void) const { return _.Fan == kDaikin64FanTurbo; } - -/// Set the Turbo (Powerful) mode of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRDaikin64::setTurbo(const bool on) { - if (on) { - setFan(kDaikin64FanTurbo); - } else if (_.Fan == kDaikin64FanTurbo) { - setFan(kDaikin64FanAuto); - } -} - -/// Get the Quiet mode status of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRDaikin64::getQuiet(void) const { return _.Fan == kDaikin64FanQuiet; } - -/// Set the Quiet mode of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRDaikin64::setQuiet(const bool on) { - if (on) { - setFan(kDaikin64FanQuiet); - } else if (_.Fan == kDaikin64FanQuiet) { - setFan(kDaikin64FanAuto); - } -} - -/// Set the Vertical Swing mode of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRDaikin64::setSwingVertical(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 IRDaikin64::getSwingVertical(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 IRDaikin64::setSleep(const bool on) { _.Sleep = on; } - -/// Get the Sleep mode of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRDaikin64::getSleep(void) const { return _.Sleep; } - -/// Set the clock on the A/C unit. -/// @param[in] mins_since_midnight Nr. of minutes past midnight. -void IRDaikin64::setClock(const uint16_t mins_since_midnight) { - uint16_t mins = mins_since_midnight; - if (mins_since_midnight >= 24 * 60) mins = 0; // Bounds check. - _.ClockMins = uint8ToBcd(mins % 60); - _.ClockHours = uint8ToBcd(mins / 60); -} - -/// Get the clock time to be sent to the A/C unit. -/// @return The number of minutes past midnight. -uint16_t IRDaikin64::getClock(void) const { - return bcdToUint8(_.ClockHours) * 60 + bcdToUint8(_.ClockMins); -} - -/// Set the enable status of the On Timer. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRDaikin64::setOnTimeEnabled(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 IRDaikin64::getOnTimeEnabled(void) const { return _.OnTimer; } - -/// Get the On Timer time to be sent to the A/C unit. -/// @return The number of minutes past midnight. -uint16_t IRDaikin64::getOnTime(void) const { return GETTIME(On); } - -/// Set the On Timer time for the A/C unit. -/// @param[in] mins_since_midnight Nr. of minutes past midnight. -void IRDaikin64::setOnTime(const uint16_t mins_since_midnight) { - SETTIME(On, mins_since_midnight); -} - -/// Set the enable status of the Off Timer. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRDaikin64::setOffTimeEnabled(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 IRDaikin64::getOffTimeEnabled(void) const { return _.OffTimer; } - -/// Get the Off Timer time to be sent to the A/C unit. -/// @return The number of minutes past midnight. -uint16_t IRDaikin64::getOffTime(void) const { return GETTIME(Off); } - -/// Set the Off Timer time for the A/C unit. -/// @param[in] mins_since_midnight Nr. of minutes past midnight. -void IRDaikin64::setOffTime(const uint16_t mins_since_midnight) { - SETTIME(Off, mins_since_midnight); -} - -/// Convert the current internal state into a human readable string. -/// @return A human readable string. -String IRDaikin64::toString(void) const { - String result = ""; - result.reserve(120); // Reserve some heap for the string to reduce fragging. - result += addBoolToString(_.Power, kPowerToggleStr, false); - result += addModeToString(_.Mode, 0xFF, kDaikin64Cool, - kDaikin64Heat, kDaikin64Dry, kDaikin64Fan); - result += addTempToString(getTemp()); - if (!getTurbo()) { - result += addFanToString(_.Fan, kDaikin64FanHigh, kDaikin64FanLow, - kDaikin64FanAuto, kDaikin64FanQuiet, - kDaikin64FanMed); - } else { - result += addIntToString(_.Fan, kFanStr); - result += kSpaceLBraceStr; - result += kTurboStr; - result += ')'; - } - result += addBoolToString(getTurbo(), kTurboStr); - result += addBoolToString(getQuiet(), kQuietStr); - result += addBoolToString(_.SwingV, kSwingVStr); - result += addBoolToString(_.Sleep, kSleepStr); - result += addLabeledString(minsToString(getClock()), kClockStr); - result += addLabeledString(_.OnTimer - ? minsToString(getOnTime()) : kOffStr, - kOnTimerStr); - result += addLabeledString(_.OffTimer - ? minsToString(getOffTime()) : kOffStr, - kOffTimerStr); - return result; -} - -/// Convert the current internal state into its stdAc::state_t equivalent. -/// @param[in] prev Ptr to a previous state. -/// @return The stdAc equivalent of the native settings. -stdAc::state_t IRDaikin64::toCommon(const stdAc::state_t *prev) const { - stdAc::state_t result; - if (prev != NULL) result = *prev; - result.protocol = decode_type_t::DAIKIN64; - 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.turbo = getTurbo(); - result.quiet = getQuiet(); - result.sleep = _.Sleep ? 0 : -1; - result.clock = getClock(); - // Not supported. - result.swingh = stdAc::swingh_t::kOff; - result.clean = false; - result.filter = false; - result.beep = false; - result.econo = false; - result.light = false; - return result; -} diff --git a/lib/IRremoteESP8266/src/ir_Daikin.h b/lib/IRremoteESP8266/src/ir_Daikin.h index 0a12c5b69e..6487d20d7c 100644 --- a/lib/IRremoteESP8266/src/ir_Daikin.h +++ b/lib/IRremoteESP8266/src/ir_Daikin.h @@ -54,11 +54,11 @@ #ifndef UNIT_TEST #include #endif -#include "IRrecv.h" -#include "IRremoteESP8266.h" -#include "IRsend.h" +#include "../src/IRrecv.h" +#include "../src/IRremoteESP8266.h" +#include "../src/IRsend.h" #ifdef UNIT_TEST -#include "IRsend_test.h" +#include "../src/IRsend_test.h" #endif /// Native representation of a Daikin A/C message. diff --git a/lib/IRremoteESP8266/src/ir_Delonghi.cpp b/lib/IRremoteESP8266/src/ir_Delonghi.cpp deleted file mode 100644 index 21a787799b..0000000000 --- a/lib/IRremoteESP8266/src/ir_Delonghi.cpp +++ /dev/null @@ -1,470 +0,0 @@ -// 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 - -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; -} diff --git a/lib/IRremoteESP8266/src/ir_Delonghi.h b/lib/IRremoteESP8266/src/ir_Delonghi.h index ac2394cb16..7954e4ade1 100644 --- a/lib/IRremoteESP8266/src/ir_Delonghi.h +++ b/lib/IRremoteESP8266/src/ir_Delonghi.h @@ -16,10 +16,10 @@ #ifndef UNIT_TEST #include #endif -#include "IRremoteESP8266.h" -#include "IRsend.h" +#include "../src/IRremoteESP8266.h" +#include "../src/IRsend.h" #ifdef UNIT_TEST -#include "IRsend_test.h" +#include "../src/IRsend_test.h" #endif /// Native representation of a Delonghi A/C message. diff --git a/lib/IRremoteESP8266/src/ir_Denon.cpp b/lib/IRremoteESP8266/src/ir_Denon.cpp deleted file mode 100644 index 700fd31cbc..0000000000 --- a/lib/IRremoteESP8266/src/ir_Denon.cpp +++ /dev/null @@ -1,122 +0,0 @@ -// 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 -#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 diff --git a/lib/IRremoteESP8266/src/ir_Dish.cpp b/lib/IRremoteESP8266/src/ir_Dish.cpp deleted file mode 100644 index 94f5450b80..0000000000 --- a/lib/IRremoteESP8266/src/ir_Dish.cpp +++ /dev/null @@ -1,103 +0,0 @@ -// 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 diff --git a/lib/IRremoteESP8266/src/ir_Doshisha.cpp b/lib/IRremoteESP8266/src/ir_Doshisha.cpp deleted file mode 100644 index 0a5fadedb8..0000000000 --- a/lib/IRremoteESP8266/src/ir_Doshisha.cpp +++ /dev/null @@ -1,124 +0,0 @@ -// 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 diff --git a/lib/IRremoteESP8266/src/ir_Ecoclim.cpp b/lib/IRremoteESP8266/src/ir_Ecoclim.cpp deleted file mode 100644 index b16f8beb2d..0000000000 --- a/lib/IRremoteESP8266/src/ir_Ecoclim.cpp +++ /dev/null @@ -1,423 +0,0 @@ -// Copyright 2021 David Conran - -/// @file -/// @brief EcoClim A/C protocol. -/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1397 - -#include "ir_Ecoclim.h" -#include -#include -#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.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; -} diff --git a/lib/IRremoteESP8266/src/ir_Ecoclim.h b/lib/IRremoteESP8266/src/ir_Ecoclim.h index ea243f67cc..63a91c7109 100644 --- a/lib/IRremoteESP8266/src/ir_Ecoclim.h +++ b/lib/IRremoteESP8266/src/ir_Ecoclim.h @@ -16,10 +16,10 @@ #ifndef UNIT_TEST #include #endif -#include "IRremoteESP8266.h" -#include "IRsend.h" +#include "../src/IRremoteESP8266.h" +#include "../src/IRsend.h" #ifdef UNIT_TEST -#include "IRsend_test.h" +#include "../src/IRsend_test.h" #endif // Constants diff --git a/lib/IRremoteESP8266/src/ir_Electra.cpp b/lib/IRremoteESP8266/src/ir_Electra.cpp deleted file mode 100644 index 7945cb0adc..0000000000 --- a/lib/IRremoteESP8266/src/ir_Electra.cpp +++ /dev/null @@ -1,402 +0,0 @@ -// Copyright 2018, 2019 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 -#include -#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; -} - -/// 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.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; - // 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(130); // Reserve some heap for the string to reduce fragging. - 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); - 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 diff --git a/lib/IRremoteESP8266/src/ir_Electra.h b/lib/IRremoteESP8266/src/ir_Electra.h index 886a92c56c..843c7a5a7c 100644 --- a/lib/IRremoteESP8266/src/ir_Electra.h +++ b/lib/IRremoteESP8266/src/ir_Electra.h @@ -18,10 +18,10 @@ #ifndef UNIT_TEST #include #endif -#include "IRremoteESP8266.h" -#include "IRsend.h" +#include "../src/IRremoteESP8266.h" +#include "../src/IRsend.h" #ifdef UNIT_TEST -#include "IRsend_test.h" +#include "../src/IRsend_test.h" #endif /// Native representation of a Electra A/C message. diff --git a/lib/IRremoteESP8266/src/ir_EliteScreens.cpp b/lib/IRremoteESP8266/src/ir_EliteScreens.cpp deleted file mode 100644 index bfbdcc1e70..0000000000 --- a/lib/IRremoteESP8266/src/ir_EliteScreens.cpp +++ /dev/null @@ -1,89 +0,0 @@ -// 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 diff --git a/lib/IRremoteESP8266/src/ir_Epson.cpp b/lib/IRremoteESP8266/src/ir_Epson.cpp deleted file mode 100644 index a92beef4ff..0000000000 --- a/lib/IRremoteESP8266/src/ir_Epson.cpp +++ /dev/null @@ -1,111 +0,0 @@ -// 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 -#include -#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 diff --git a/lib/IRremoteESP8266/src/ir_Fujitsu.cpp b/lib/IRremoteESP8266/src/ir_Fujitsu.cpp deleted file mode 100644 index da4b41dcf6..0000000000 --- a/lib/IRremoteESP8266/src/ir_Fujitsu.cpp +++ /dev/null @@ -1,1043 +0,0 @@ -// Copyright 2017 Jonny Graham -// Copyright 2017-2021 David Conran -// Copyright 2021 siriuslzx - -/// @file -/// @brief Support for Fujitsu A/C protocols. -/// Fujitsu A/C support added by Jonny Graham & David Conran -/// @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 - -#include "ir_Fujitsu.h" -#include -#ifndef ARDUINO -#include -#endif -#include "IRsend.h" -#include "IRtext.h" -#include "IRutils.h" - -// Ref: -// These values are based on averages of measurements -const uint16_t kFujitsuAcHdrMark = 3324; -const uint16_t kFujitsuAcHdrSpace = 1574; -const uint16_t kFujitsuAcBitMark = 448; -const uint16_t kFujitsuAcOneSpace = 1182; -const uint16_t kFujitsuAcZeroSpace = 390; -const uint16_t kFujitsuAcMinGap = 8100; -const uint8_t kFujitsuAcExtraTolerance = 5; // Extra tolerance percentage. - -using irutils::addBoolToString; -using irutils::addIntToString; -using irutils::addLabeledString; -using irutils::addModeToString; -using irutils::addModelToString; -using irutils::addFanToString; -using irutils::addTempFloatToString; -using irutils::minsToString; - -#if SEND_FUJITSU_AC -/// Send a Fujitsu A/C formatted message. -/// Status: STABLE / Known Good. -/// @param[in] data The message to be sent. -/// @param[in] nbytes The number of bytes of message to be sent. -/// Typically one of: -/// kFujitsuAcStateLength, -/// kFujitsuAcStateLength - 1, -/// kFujitsuAcStateLengthShort, -/// kFujitsuAcStateLengthShort - 1 -/// @param[in] repeat The number of times the command is to be repeated. -void IRsend::sendFujitsuAC(const unsigned char data[], const uint16_t nbytes, - const uint16_t repeat) { - sendGeneric(kFujitsuAcHdrMark, kFujitsuAcHdrSpace, kFujitsuAcBitMark, - kFujitsuAcOneSpace, kFujitsuAcBitMark, kFujitsuAcZeroSpace, - kFujitsuAcBitMark, kFujitsuAcMinGap, data, nbytes, 38, false, - repeat, 50); -} -#endif // SEND_FUJITSU_AC - -// Code to emulate Fujitsu A/C IR remote control unit. - -/// Class Constructor -/// @param[in] pin GPIO to be used when sending. -/// @param[in] model The enum for the model of A/C to be emulated. -/// @param[in] inverted Is the output signal to be inverted? -/// @param[in] use_modulation Is frequency modulation to be used? -IRFujitsuAC::IRFujitsuAC(const uint16_t pin, - const fujitsu_ac_remote_model_t model, - const bool inverted, const bool use_modulation) - : _irsend(pin, inverted, use_modulation) { - setModel(model); - stateReset(); -} - -/// Set the currently emulated model of the A/C. -/// @param[in] model An enum representing the model to support/emulate. -void IRFujitsuAC::setModel(const fujitsu_ac_remote_model_t model) { - _model = model; - switch (model) { - case fujitsu_ac_remote_model_t::ARDB1: - case fujitsu_ac_remote_model_t::ARJW2: - _state_length = kFujitsuAcStateLength - 1; - _state_length_short = kFujitsuAcStateLengthShort - 1; - break; - case fujitsu_ac_remote_model_t::ARRY4: - case fujitsu_ac_remote_model_t::ARRAH2E: - case fujitsu_ac_remote_model_t::ARREB1E: - default: - _state_length = kFujitsuAcStateLength; - _state_length_short = kFujitsuAcStateLengthShort; - } -} - -/// Get the currently emulated/detected model of the A/C. -/// @return The enum representing the model of A/C. -fujitsu_ac_remote_model_t IRFujitsuAC::getModel(void) const { return _model; } - -/// Reset the state of the remote to a known good state/sequence. -void IRFujitsuAC::stateReset(void) { - for (size_t i = 0; i < kFujitsuAcStateLength; i++) { - _.longcode[i] = 0; - } - setTemp(24); - _.Fan = kFujitsuAcFanHigh; - _.Mode = kFujitsuAcModeCool; - _.Swing = kFujitsuAcSwingBoth; - _cmd = kFujitsuAcCmdTurnOn; - _.Filter = false; - _.Clean = false; - _.TimerType = kFujitsuAcStopTimers; - _.OnTimer = 0; - _.OffTimer = 0; - _.longcode[0] = 0x14; - _.longcode[1] = 0x63; - _.longcode[3] = 0x10; - _.longcode[4] = 0x10; -} - -/// Set up hardware to be able to send a message. -void IRFujitsuAC::begin(void) { _irsend.begin(); } - -#if SEND_FUJITSU_AC -/// Send the current internal state as an IR message. -/// @param[in] repeat Nr. of times the message will be repeated. -void IRFujitsuAC::send(const uint16_t repeat) { - _irsend.sendFujitsuAC(getRaw(), getStateLength(), repeat); -} -#endif // SEND_FUJITSU_AC - -/// Update the length (size) of the state code for the current configuration. -/// @return true, if use long codes; false, use short codes. -bool IRFujitsuAC::updateUseLongOrShort(void) { - bool fullCmd = false; - switch (_cmd) { - case kFujitsuAcCmdTurnOff: // 0x02 - case kFujitsuAcCmdEcono: // 0x09 - case kFujitsuAcCmdPowerful: // 0x39 - case kFujitsuAcCmdStepVert: // 0x6C - case kFujitsuAcCmdToggleSwingVert: // 0x6D - case kFujitsuAcCmdStepHoriz: // 0x79 - case kFujitsuAcCmdToggleSwingHoriz: // 0x7A - _.Cmd = _cmd; - break; - default: - switch (_model) { - case fujitsu_ac_remote_model_t::ARRY4: - case fujitsu_ac_remote_model_t::ARRAH2E: - case fujitsu_ac_remote_model_t::ARREB1E: - case fujitsu_ac_remote_model_t::ARREW4E: - _.Cmd = 0xFE; - break; - case fujitsu_ac_remote_model_t::ARDB1: - case fujitsu_ac_remote_model_t::ARJW2: - _.Cmd = 0xFC; - break; - } - fullCmd = true; - break; - } - return fullCmd; -} - -/// Calculate and set the checksum values for the internal state. -void IRFujitsuAC::checkSum(void) { - if (updateUseLongOrShort()) { // Is it a long code? - // Nr. of bytes in the message after this byte. - _.RestLength = _state_length - 7; - _.Protocol = (_model == fujitsu_ac_remote_model_t::ARREW4E) ? 0x31 : 0x30; - _.Power = (_cmd == kFujitsuAcCmdTurnOn) || get10CHeat(); - - // These values depend on model - if (_model != fujitsu_ac_remote_model_t::ARREB1E && - _model != fujitsu_ac_remote_model_t::ARREW4E) { - _.OutsideQuiet = 0; - if (_model != fujitsu_ac_remote_model_t::ARRAH2E) { - _.TimerType = kFujitsuAcStopTimers; - } - } - if (_model != fujitsu_ac_remote_model_t::ARRY4) { - if (_model != fujitsu_ac_remote_model_t::ARREW4E) _.Clean = false; - _.Filter = false; - } - // Set the On/Off/Sleep timer Nr of mins. - _.OffTimer = getOffSleepTimer(); - _.OnTimer = getOnTimer(); - // Enable bit for the Off/Sleep timer - _.OffTimerEnable = _.OffTimer > 0; - // Enable bit for the On timer - _.OnTimerEnable = _.OnTimer > 0; - - uint8_t checksum = 0; - uint8_t checksum_complement = 0; - switch (_model) { - case fujitsu_ac_remote_model_t::ARDB1: - case fujitsu_ac_remote_model_t::ARJW2: - _.Swing = kFujitsuAcSwingOff; - checksum = sumBytes(_.longcode, _state_length - 1); - checksum_complement = 0x9B; - break; - case fujitsu_ac_remote_model_t::ARREB1E: - case fujitsu_ac_remote_model_t::ARRAH2E: - case fujitsu_ac_remote_model_t::ARRY4: - _.unknown = 1; - // FALL THRU - default: - checksum = sumBytes(_.longcode + _state_length_short, - _state_length - _state_length_short - 1); - } - // and negate the checksum and store it in the last byte. - _.longcode[_state_length - 1] = checksum_complement - checksum; - } else { // short codes - for (size_t i = 0; i < _state_length_short; i++) { - _.shortcode[i] = _.longcode[i]; - } - switch (_model) { - case fujitsu_ac_remote_model_t::ARRY4: - case fujitsu_ac_remote_model_t::ARRAH2E: - case fujitsu_ac_remote_model_t::ARREB1E: - case fujitsu_ac_remote_model_t::ARREW4E: - // The last byte is the inverse of penultimate byte - _.shortcode[_state_length_short - 1] = - ~_.shortcode[_state_length_short - 2]; - break; - default: - {}; // We don't need to do anything for the others. - } - } -} - -/// Get the length (size) of the state code for the current configuration. -/// @return The length of the state array required for this config. -uint8_t IRFujitsuAC::getStateLength(void) { - return updateUseLongOrShort() ? _state_length : _state_length_short; -} - -/// 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* IRFujitsuAC::getRaw(void) { - checkSum(); - if (_.Cmd == 0xFE || _.Cmd == 0xFC) - return _.longcode; - return _.shortcode; -} - -/// Build the internal state/config from the current (raw) A/C message. -/// @param[in] length Size of the current/used (raw) A/C message array. -void IRFujitsuAC::buildFromState(const uint16_t length) { - switch (length) { - case kFujitsuAcStateLength - 1: - case kFujitsuAcStateLengthShort - 1: - setModel(fujitsu_ac_remote_model_t::ARDB1); - // ARJW2 has horizontal swing. - if (_.Swing > kFujitsuAcSwingVert) - setModel(fujitsu_ac_remote_model_t::ARJW2); - break; - default: - switch (_.Cmd) { - case kFujitsuAcCmdEcono: - case kFujitsuAcCmdPowerful: - setModel(fujitsu_ac_remote_model_t::ARREB1E); - break; - default: - setModel(fujitsu_ac_remote_model_t::ARRAH2E); - } - } - switch (_.RestLength) { - case 8: - if (_model != fujitsu_ac_remote_model_t::ARJW2) - setModel(fujitsu_ac_remote_model_t::ARDB1); - break; - case 9: - if (_model != fujitsu_ac_remote_model_t::ARREB1E) - setModel(fujitsu_ac_remote_model_t::ARRAH2E); - break; - } - if (_.Power) - setCmd(kFujitsuAcCmdTurnOn); - else - setCmd(kFujitsuAcCmdStayOn); - // Currently the only way we know how to tell ARRAH2E & ARRY4 apart is if - // either the raw Filter or Clean setting is on. - if (_model == fujitsu_ac_remote_model_t::ARRAH2E && (_.Filter || _.Clean)) - setModel(fujitsu_ac_remote_model_t::ARRY4); - if (_state_length == kFujitsuAcStateLength && _.OutsideQuiet) - setModel(fujitsu_ac_remote_model_t::ARREB1E); - switch (_.Cmd) { - case kFujitsuAcCmdTurnOff: - case kFujitsuAcCmdStepHoriz: - case kFujitsuAcCmdToggleSwingHoriz: - case kFujitsuAcCmdStepVert: - case kFujitsuAcCmdToggleSwingVert: - case kFujitsuAcCmdEcono: - case kFujitsuAcCmdPowerful: - setCmd(_.Cmd); - break; - } - if (_.Protocol == 0x31) setModel(fujitsu_ac_remote_model_t::ARREW4E); -} - -/// Set the internal state from a valid code for this protocol. -/// @param[in] newState A valid code for this protocol. -/// @param[in] length Size of the newState array. -/// @return true, if successful; Otherwise false. (i.e. size check) -bool IRFujitsuAC::setRaw(const uint8_t newState[], const uint16_t length) { - if (length > kFujitsuAcStateLength) return false; - for (uint16_t i = 0; i < kFujitsuAcStateLength; i++) { - if (i < length) - _.longcode[i] = newState[i]; - else - _.longcode[i] = 0; - } - buildFromState(length); - return true; -} - -/// Request the A/C to step the Horizontal Swing. -void IRFujitsuAC::stepHoriz(void) { setCmd(kFujitsuAcCmdStepHoriz); } - -/// Request the A/C to toggle the Horizontal Swing mode. -/// @param[in] update Do we need to update the general swing config? -void IRFujitsuAC::toggleSwingHoriz(const bool update) { - // Toggle the current setting. - if (update) setSwing(getSwing() ^ kFujitsuAcSwingHoriz); - // and set the appropriate special command. - setCmd(kFujitsuAcCmdToggleSwingHoriz); -} - -/// Request the A/C to step the Vertical Swing. -void IRFujitsuAC::stepVert(void) { setCmd(kFujitsuAcCmdStepVert); } - -/// Request the A/C to toggle the Vertical Swing mode. -/// @param[in] update Do we need to update the general swing config? -void IRFujitsuAC::toggleSwingVert(const bool update) { - // Toggle the current setting. - if (update) setSwing(getSwing() ^ kFujitsuAcSwingVert); - // and set the appropriate special command. - setCmd(kFujitsuAcCmdToggleSwingVert); -} - -/// Set the requested (special) command part for the A/C message. -/// @param[in] cmd The special command code. -void IRFujitsuAC::setCmd(const uint8_t cmd) { - switch (cmd) { - case kFujitsuAcCmdTurnOff: - case kFujitsuAcCmdTurnOn: - case kFujitsuAcCmdStayOn: - case kFujitsuAcCmdStepVert: - case kFujitsuAcCmdToggleSwingVert: - _cmd = cmd; - break; - case kFujitsuAcCmdStepHoriz: - case kFujitsuAcCmdToggleSwingHoriz: - switch (_model) { - // Only these remotes have horizontal. - case fujitsu_ac_remote_model_t::ARRAH2E: - case fujitsu_ac_remote_model_t::ARJW2: - _cmd = cmd; - break; - default: - _cmd = kFujitsuAcCmdStayOn; - } - break; - case kFujitsuAcCmdEcono: - case kFujitsuAcCmdPowerful: - switch (_model) { - // Only these remotes have these commands. - case ARREB1E: - case ARREW4E: - _cmd = cmd; - break; - default: - _cmd = kFujitsuAcCmdStayOn; - } - break; - default: - _cmd = kFujitsuAcCmdStayOn; - } -} - -/// Set the requested (special) command part for the A/C message. -/// @return The special command code. -uint8_t IRFujitsuAC::getCmd(void) const { - return _cmd; -} - -/// Change the power setting. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRFujitsuAC::setPower(const bool on) { - setCmd(on ? kFujitsuAcCmdTurnOn : kFujitsuAcCmdTurnOff); -} - -/// Set the requested power state of the A/C to off. -void IRFujitsuAC::off(void) { setPower(false); } - -/// Set the requested power state of the A/C to on. -void IRFujitsuAC::on(void) { setPower(true); } - -/// Get the value of the current power setting. -/// @return true, the setting is on. false, the setting is off. -bool IRFujitsuAC::getPower(void) const { return _cmd != kFujitsuAcCmdTurnOff; } - -/// Set the Outside Quiet mode of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRFujitsuAC::setOutsideQuiet(const bool on) { - _.OutsideQuiet = on; - setCmd(kFujitsuAcCmdStayOn); // No special command involved. -} - -/// Get the Outside Quiet mode status of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRFujitsuAC::getOutsideQuiet(void) const { - switch (_model) { - // Only ARREB1E & ARREW4E seems to have this mode. - case fujitsu_ac_remote_model_t::ARREB1E: - case fujitsu_ac_remote_model_t::ARREW4E: - return _.OutsideQuiet; - default: return false; - } -} - -/// Set the temperature. -/// @param[in] temp The temperature in degrees. -/// @param[in] useCelsius Use Celsius or Fahrenheit? -void IRFujitsuAC::setTemp(const float temp, const bool useCelsius) { - float mintemp; - float maxtemp; - uint8_t offset; - bool _useCelsius; - float _temp; - - switch (_model) { - // These models have native Fahrenheit & Celsius upport. - case fujitsu_ac_remote_model_t::ARREW4E: - _useCelsius = useCelsius; - _temp = temp; - break; - // Make sure everything else uses Celsius. - default: - _useCelsius = true; - _temp = useCelsius ? temp : fahrenheitToCelsius(temp); - } - setCelsius(_useCelsius); - if (_useCelsius) { - mintemp = kFujitsuAcMinTemp; - maxtemp = kFujitsuAcMaxTemp; - offset = kFujitsuAcTempOffsetC; - } else { - mintemp = kFujitsuAcMinTempF; - maxtemp = kFujitsuAcMaxTempF; - offset = kFujitsuAcTempOffsetF; - } - _temp = std::max(mintemp, _temp); - _temp = std::min(maxtemp, _temp); - if (_useCelsius) { - if (_model == fujitsu_ac_remote_model_t::ARREW4E) - _.Temp = (_temp - (offset / 2)) * 2; - else - _.Temp = (_temp - offset) * 4; - } else { - _.Temp = _temp - offset; - } - setCmd(kFujitsuAcCmdStayOn); // No special command involved. -} - -/// Get the current temperature setting. -/// @return The current setting for temp. in degrees of the currently set units. -float IRFujitsuAC::getTemp(void) const { - if (_model == fujitsu_ac_remote_model_t::ARREW4E) { - if (_.Fahrenheit) // Currently only ARREW4E supports native Fahrenheit. - return _.Temp + kFujitsuAcTempOffsetF; - else - return (_.Temp / 2.0) + (kFujitsuAcMinTemp / 2); - } else { - return _.Temp / 4 + kFujitsuAcMinTemp; - } -} - -/// Set the speed of the fan. -/// @param[in] fanSpeed The desired setting. -void IRFujitsuAC::setFanSpeed(const uint8_t fanSpeed) { - if (fanSpeed > kFujitsuAcFanQuiet) - _.Fan = kFujitsuAcFanHigh; // Set the fan to maximum if out of range. - else - _.Fan = fanSpeed; - setCmd(kFujitsuAcCmdStayOn); // No special command involved. -} - -/// Get the current fan speed setting. -/// @return The current fan speed. -uint8_t IRFujitsuAC::getFanSpeed(void) const { return _.Fan; } - -/// Set the operating mode of the A/C. -/// @param[in] mode The desired operating mode. -void IRFujitsuAC::setMode(const uint8_t mode) { - if (mode > kFujitsuAcModeHeat) - _.Mode = kFujitsuAcModeHeat; // Set the mode to maximum if out of range. - else - _.Mode = mode; - setCmd(kFujitsuAcCmdStayOn); // No special command involved. -} - -/// Get the operating mode setting of the A/C. -/// @return The current operating mode setting. -uint8_t IRFujitsuAC::getMode(void) const { return _.Mode; } - -/// Set the requested swing operation mode of the A/C unit. -/// @param[in] swingMode The swingMode code for the A/C. -/// Vertical, Horizon, or Both. See constants for details. -/// @note Not all models support all possible swing modes. -void IRFujitsuAC::setSwing(const uint8_t swingMode) { - _.Swing = swingMode; - switch (_model) { - // No Horizontal support. - case fujitsu_ac_remote_model_t::ARDB1: - case fujitsu_ac_remote_model_t::ARREB1E: - case fujitsu_ac_remote_model_t::ARRY4: - // Set the mode to max if out of range - if (swingMode > kFujitsuAcSwingVert) _.Swing = kFujitsuAcSwingVert; - break; - // Has Horizontal support. - case fujitsu_ac_remote_model_t::ARRAH2E: - case fujitsu_ac_remote_model_t::ARJW2: - default: - // Set the mode to max if out of range - if (swingMode > kFujitsuAcSwingBoth) _.Swing = kFujitsuAcSwingBoth; - } - setCmd(kFujitsuAcCmdStayOn); // No special command involved. -} - -/// Get the requested swing operation mode of the A/C unit. -/// @return The contents of the swing state/mode. -uint8_t IRFujitsuAC::getSwing(void) const { - return _.Swing; -} - -/// Set the Clean mode of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRFujitsuAC::setClean(const bool on) { - _.Clean = on; - setCmd(kFujitsuAcCmdStayOn); // No special command involved. -} - -/// Get the Clean mode status of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRFujitsuAC::getClean(void) const { - switch (_model) { - case fujitsu_ac_remote_model_t::ARRY4: return _.Clean; - default: return false; - } -} - -/// Set the Filter mode status of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRFujitsuAC::setFilter(const bool on) { - _.Filter = on; - setCmd(kFujitsuAcCmdStayOn); // No special command involved. -} - -/// Get the Filter mode status of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRFujitsuAC::getFilter(void) const { - switch (_model) { - case fujitsu_ac_remote_model_t::ARRY4: return _.Filter; - default: return false; - } -} - -/// Set the 10C heat status of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRFujitsuAC::set10CHeat(const bool on) { - switch (_model) { - // Only selected models support this. - case fujitsu_ac_remote_model_t::ARREW4E: - setClean(on); // 10C Heat uses the same bit as Clean - if (on) { - _.Mode = kFujitsuAcModeFan; - _.Power = true; - _.Fan = kFujitsuAcFanAuto; - _.Swing = kFujitsuAcSwingOff; - } - default: - break; - } -} - -/// Get the 10C heat status of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRFujitsuAC::get10CHeat(void) const { - switch (_model) { - case fujitsu_ac_remote_model_t::ARREW4E: - return (_.Clean && _.Power && _.Mode == kFujitsuAcModeFan && - _.Fan == kFujitsuAcFanAuto && _.Swing == kFujitsuAcSwingOff); - default: return false; - } -} - -/// Get the Timer type of the A/C message. -/// @return The current timer type in numeric form. -uint8_t IRFujitsuAC::getTimerType(void) const { - switch (_model) { - // These models seem to have timer support. - case fujitsu_ac_remote_model_t::ARRAH2E: - case fujitsu_ac_remote_model_t::ARREB1E: - return _.TimerType; - default: return kFujitsuAcStopTimers; - } -} - -/// Set the Timer type of the A/C message. -/// @param[in] timertype The kind of timer to use for the message. -void IRFujitsuAC::setTimerType(const uint8_t timertype) { - switch (timertype) { - case kFujitsuAcSleepTimer: - case kFujitsuAcOnTimer: - case kFujitsuAcOffTimer: - case kFujitsuAcStopTimers: - _.TimerType = timertype; - break; - default: _.TimerType = kFujitsuAcStopTimers; - } -} - -/// Get the On Timer setting of the A/C. -/// @return nr of minutes left on the timer. 0 means disabled/not supported. -uint16_t IRFujitsuAC::getOnTimer(void) const { - if (getTimerType() == kFujitsuAcOnTimer) - return _.OnTimer; - return 0; -} - -/// Set the On Timer setting of the A/C. -/// @param[in] nr_mins Nr. of minutes to set the timer to. 0 means disabled. -void IRFujitsuAC::setOnTimer(const uint16_t nr_mins) { - _.OnTimer = std::min(kFujitsuAcTimerMax, nr_mins); // Bounds check. - if (_.OnTimer) { - _.TimerType = kFujitsuAcOnTimer; - } else if (getTimerType() == kFujitsuAcOnTimer) { - _.TimerType = kFujitsuAcStopTimers; - } -} - -/// Get the Off/Sleep Timer setting of the A/C. -/// @return nr of minutes left on the timer. 0 means disabled/not supported. -uint16_t IRFujitsuAC::getOffSleepTimer(void) const { - switch (getTimerType()) { - case kFujitsuAcOffTimer: - case kFujitsuAcSleepTimer: - return _.OffTimer; - default: - return 0; - } -} - -/// Set the Off/Sleep Timer time for the A/C. -/// @param[in] nr_mins Nr. of minutes to set the timer to. 0 means disabled. -inline void IRFujitsuAC::setOffSleepTimer(const uint16_t nr_mins) { - _.OffTimer = std::min(kFujitsuAcTimerMax, nr_mins); // Bounds check. -} - -/// Set the Off Timer time for the A/C. -/// @param[in] nr_mins Nr. of minutes to set the timer to. 0 means disabled. -void IRFujitsuAC::setOffTimer(const uint16_t nr_mins) { - setOffSleepTimer(nr_mins); - if (nr_mins) - _.TimerType = kFujitsuAcOffTimer; - else if (getTimerType() != kFujitsuAcOnTimer) - _.TimerType = kFujitsuAcStopTimers; -} - -/// Set the Sleep Timer time for the A/C. -/// @param[in] nr_mins Nr. of minutes to set the timer to. 0 means disabled. -void IRFujitsuAC::setSleepTimer(const uint16_t nr_mins) { - setOffSleepTimer(nr_mins); - if (nr_mins) - _.TimerType = kFujitsuAcSleepTimer; - else if (getTimerType() != kFujitsuAcOnTimer) - _.TimerType = kFujitsuAcStopTimers; -} - -/// 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 IRFujitsuAC::validChecksum(uint8_t state[], const uint16_t length) { - uint8_t sum = 0; - uint8_t sum_complement = 0; - uint8_t checksum = state[length - 1]; - switch (length) { - case kFujitsuAcStateLengthShort: // ARRAH2E, ARREB1E, & ARRY4 - return state[length - 1] == (uint8_t)~state[length - 2]; - case kFujitsuAcStateLength - 1: // ARDB1 & ARJW2 - sum = sumBytes(state, length - 1); - sum_complement = 0x9B; - break; - case kFujitsuAcStateLength: // ARRAH2E, ARRY4, & ARREB1E - sum = sumBytes(state + kFujitsuAcStateLengthShort, - length - 1 - kFujitsuAcStateLengthShort); - break; - default: // Includes ARDB1 & ARJW2 short. - return true; // Assume the checksum is valid for other lengths. - } - return checksum == (uint8_t)(sum_complement - sum); // Does it match? -} - -/// Set the device's remote ID number. -/// @param[in] num The ID for the remote. Valid number range is 0 to 3. -void IRFujitsuAC::setId(const uint8_t num) { _.Id = num; } - -/// Get the current device's remote ID number. -/// @return The current device's remote ID number. -uint8_t IRFujitsuAC::getId(void) const { return _.Id; } - -/// Set the Temperature units for the A/C. -/// @param[in] on true, use Celsius. false, use Fahrenheit. -void IRFujitsuAC::setCelsius(const bool on) { _.Fahrenheit = !on; } - -/// Get the Clean mode status of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRFujitsuAC::getCelsius(void) const { return !_.Fahrenheit; } - -/// 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 IRFujitsuAC::convertMode(const stdAc::opmode_t mode) { - switch (mode) { - case stdAc::opmode_t::kCool: return kFujitsuAcModeCool; - case stdAc::opmode_t::kHeat: return kFujitsuAcModeHeat; - case stdAc::opmode_t::kDry: return kFujitsuAcModeDry; - case stdAc::opmode_t::kFan: return kFujitsuAcModeFan; - default: return kFujitsuAcModeAuto; - } -} - -/// 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 IRFujitsuAC::convertFan(stdAc::fanspeed_t speed) { - switch (speed) { - case stdAc::fanspeed_t::kMin: return kFujitsuAcFanQuiet; - case stdAc::fanspeed_t::kLow: return kFujitsuAcFanLow; - case stdAc::fanspeed_t::kMedium: return kFujitsuAcFanMed; - case stdAc::fanspeed_t::kHigh: - case stdAc::fanspeed_t::kMax: return kFujitsuAcFanHigh; - default: return kFujitsuAcFanAuto; - } -} - -/// 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 IRFujitsuAC::toCommonMode(const uint8_t mode) { - switch (mode) { - case kFujitsuAcModeCool: return stdAc::opmode_t::kCool; - case kFujitsuAcModeHeat: return stdAc::opmode_t::kHeat; - case kFujitsuAcModeDry: return stdAc::opmode_t::kDry; - case kFujitsuAcModeFan: 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 IRFujitsuAC::toCommonFanSpeed(const uint8_t speed) { - switch (speed) { - case kFujitsuAcFanHigh: return stdAc::fanspeed_t::kMax; - case kFujitsuAcFanMed: return stdAc::fanspeed_t::kMedium; - case kFujitsuAcFanLow: return stdAc::fanspeed_t::kLow; - case kFujitsuAcFanQuiet: 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 IRFujitsuAC::toCommon(void) const { - stdAc::state_t result; - result.protocol = decode_type_t::FUJITSU_AC; - result.model = _model; - result.power = getPower(); - result.mode = toCommonMode(_.Mode); - result.celsius = getCelsius(); - result.degrees = getTemp(); - result.fanspeed = toCommonFanSpeed(_.Fan); - uint8_t swing = _.Swing; - switch (result.model) { - case fujitsu_ac_remote_model_t::ARREB1E: - case fujitsu_ac_remote_model_t::ARRAH2E: - case fujitsu_ac_remote_model_t::ARRY4: - result.clean = _.Clean; - result.filter = _.Filter; - result.swingv = (swing & kFujitsuAcSwingVert) ? stdAc::swingv_t::kAuto - : stdAc::swingv_t::kOff; - result.swingh = (swing & kFujitsuAcSwingHoriz) ? stdAc::swingh_t::kAuto - : stdAc::swingh_t::kOff; - break; - case fujitsu_ac_remote_model_t::ARDB1: - case fujitsu_ac_remote_model_t::ARJW2: - default: - result.swingv = stdAc::swingv_t::kOff; - result.swingh = stdAc::swingh_t::kOff; - } - - result.quiet = _.Fan == kFujitsuAcFanQuiet; - result.turbo = _cmd == kFujitsuAcCmdPowerful; - result.econo = _cmd == kFujitsuAcCmdEcono; - // Not supported. - result.light = false; - result.filter = 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 IRFujitsuAC::toString(void) const { - String result = ""; - result.reserve(100); // Reserve some heap for the string to reduce fragging. - fujitsu_ac_remote_model_t model = _model; - result += addModelToString(decode_type_t::FUJITSU_AC, model, false); - result += addIntToString(_.Id, kIdStr); - result += addBoolToString(getPower(), kPowerStr); - result += addModeToString(_.Mode, kFujitsuAcModeAuto, kFujitsuAcModeCool, - kFujitsuAcModeHeat, kFujitsuAcModeDry, - kFujitsuAcModeFan); - result += addTempFloatToString(getTemp(), getCelsius()); - result += addFanToString(_.Fan, kFujitsuAcFanHigh, kFujitsuAcFanLow, - kFujitsuAcFanAuto, kFujitsuAcFanQuiet, - kFujitsuAcFanMed); - switch (model) { - // These models have no internal swing, clean. or filter state. - case fujitsu_ac_remote_model_t::ARDB1: - case fujitsu_ac_remote_model_t::ARJW2: - break; - // These models have Clean & Filter, plus Swing (via fall thru) - case fujitsu_ac_remote_model_t::ARRAH2E: - case fujitsu_ac_remote_model_t::ARREB1E: - case fujitsu_ac_remote_model_t::ARRY4: - result += addBoolToString(getClean(), kCleanStr); - result += addBoolToString(getFilter(), kFilterStr); - // FALL THRU - default: // e.g. ARREW4E - if (model == fujitsu_ac_remote_model_t::ARREW4E) - result += addBoolToString(get10CHeat(), k10CHeatStr); - result += addIntToString(_.Swing, kSwingStr); - result += kSpaceLBraceStr; - switch (_.Swing) { - case kFujitsuAcSwingOff: - result += kOffStr; - break; - case kFujitsuAcSwingVert: - result += kSwingVStr; - break; - case kFujitsuAcSwingHoriz: - result += kSwingHStr; - break; - case kFujitsuAcSwingBoth: - result += kSwingVStr; - result += '+'; - result += kSwingHStr; - break; - default: - result += kUnknownStr; - } - result += ')'; - } - result += kCommaSpaceStr; - result += kCommandStr; - result += kColonSpaceStr; - switch (_cmd) { - case kFujitsuAcCmdStepHoriz: - result += kStepStr; - result += ' '; - result += kSwingHStr; - break; - case kFujitsuAcCmdStepVert: - result += kStepStr; - result += ' '; - result += kSwingVStr; - break; - case kFujitsuAcCmdToggleSwingHoriz: - result += kToggleStr; - result += ' '; - result += kSwingHStr; - break; - case kFujitsuAcCmdToggleSwingVert: - result += kToggleStr; - result += ' '; - result += kSwingVStr; - break; - case kFujitsuAcCmdEcono: - result += kEconoStr; - break; - case kFujitsuAcCmdPowerful: - result += kPowerfulStr; - break; - default: - result += kNAStr; - } - uint16_t mins = 0; - String type_str = kTimerStr; - switch (model) { - case fujitsu_ac_remote_model_t::ARREB1E: - case fujitsu_ac_remote_model_t::ARREW4E: - result += addBoolToString(getOutsideQuiet(), kOutsideQuietStr); - // FALL THRU - // These models seem to have timer support. - case fujitsu_ac_remote_model_t::ARRAH2E: - switch (getTimerType()) { - case kFujitsuAcOnTimer: - type_str = kOnTimerStr; - mins = getOnTimer(); - break; - case kFujitsuAcOffTimer: - type_str = kOffTimerStr; - mins = getOffSleepTimer(); - break; - case kFujitsuAcSleepTimer: - type_str = kSleepTimerStr; - mins = getOffSleepTimer(); - break; - } - result += addLabeledString(mins ? minsToString(mins) : kOffStr, type_str); - break; - default: - break; - } - return result; -} - -#if DECODE_FUJITSU_AC -/// Decode the supplied Fujitsu AC IR message if possible. -/// 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::decodeFujitsuAC(decode_results* results, uint16_t offset, - const uint16_t nbits, - const bool strict) { - uint16_t dataBitsSoFar = 0; - - // Have we got enough data to successfully decode? - if (results->rawlen < (2 * kFujitsuAcMinBits) + kHeader + kFooter - 1 + - offset) - return false; // Can't possibly be a valid message. - - // Compliance - if (strict) { - switch (nbits) { - case kFujitsuAcBits: - case kFujitsuAcBits - 8: - case kFujitsuAcMinBits: - case kFujitsuAcMinBits + 8: break; - default: return false; // Must be called with the correct nr. of bits. - } - } - - // Header / Some of the Data - uint16_t used = matchGeneric(results->rawbuf + offset, results->state, - results->rawlen - offset, kFujitsuAcMinBits - 8, - kFujitsuAcHdrMark, kFujitsuAcHdrSpace, // Header - kFujitsuAcBitMark, kFujitsuAcOneSpace, // Data - kFujitsuAcBitMark, kFujitsuAcZeroSpace, - 0, 0, // No Footer (yet) - false, _tolerance + kFujitsuAcExtraTolerance, 0, - false); // LSBF - if (!used) return false; - offset += used; - // Check we have the typical data header. - if (results->state[0] != 0x14 || results->state[1] != 0x63) return false; - dataBitsSoFar += kFujitsuAcMinBits - 8; - - // Keep reading bytes until we either run out of message or state to fill. - match_result_t data_result; - for (uint16_t i = 5; - offset <= results->rawlen - 16 && i < kFujitsuAcStateLength; - i++, dataBitsSoFar += 8, offset += data_result.used) { - data_result = matchData( - &(results->rawbuf[offset]), 8, kFujitsuAcBitMark, kFujitsuAcOneSpace, - kFujitsuAcBitMark, kFujitsuAcZeroSpace, - _tolerance + kFujitsuAcExtraTolerance, 0, false); - if (data_result.success == false) break; // Fail - results->state[i] = data_result.data; - } - - // Footer - if (offset > results->rawlen || - !matchMark(results->rawbuf[offset++], kFujitsuAcBitMark)) - return false; - // The space is optional if we are out of capture. - if (offset < results->rawlen && - !matchAtLeast(results->rawbuf[offset], kFujitsuAcMinGap)) - return false; - - // Compliance - if (strict) { - if (dataBitsSoFar != nbits) return false; - } - - results->decode_type = FUJITSU_AC; - results->bits = dataBitsSoFar; - - // Compliance - switch (dataBitsSoFar) { - case kFujitsuAcMinBits: - // Check if this values indicate that this should have been a long state - // message. - if (results->state[5] == 0xFC) return false; - return true; // Success - case kFujitsuAcMinBits + 8: - // Check if this values indicate that this should have been a long state - // message. - if (results->state[5] == 0xFE) return false; - // The last byte needs to be the inverse of the penultimate byte. - if (results->state[5] != (uint8_t)~results->state[6]) return false; - return true; // Success - case kFujitsuAcBits - 8: - // Long messages of this size require this byte be correct. - if (results->state[5] != 0xFC) return false; - break; - case kFujitsuAcBits: - // Long messages of this size require this byte be correct. - if (results->state[5] != 0xFE) return false; - break; - default: - return false; // Unexpected size. - } - if (!IRFujitsuAC::validChecksum(results->state, dataBitsSoFar / 8)) - return false; - - // Success - return true; // All good. -} -#endif // DECODE_FUJITSU_AC diff --git a/lib/IRremoteESP8266/src/ir_Fujitsu.h b/lib/IRremoteESP8266/src/ir_Fujitsu.h index 994b6f4a39..94618a84de 100644 --- a/lib/IRremoteESP8266/src/ir_Fujitsu.h +++ b/lib/IRremoteESP8266/src/ir_Fujitsu.h @@ -42,11 +42,11 @@ #ifdef ARDUINO #include #endif -#include "IRrecv.h" -#include "IRremoteESP8266.h" -#include "IRsend.h" +#include "../src/IRrecv.h" +#include "../src/IRremoteESP8266.h" +#include "../src/IRsend.h" #ifdef UNIT_TEST -#include "IRsend_test.h" +#include "../src/IRsend_test.h" #endif /// Native representation of a Fujitsu A/C message. diff --git a/lib/IRremoteESP8266/src/ir_GICable.cpp b/lib/IRremoteESP8266/src/ir_GICable.cpp deleted file mode 100644 index 7b29d71db4..0000000000 --- a/lib/IRremoteESP8266/src/ir_GICable.cpp +++ /dev/null @@ -1,95 +0,0 @@ -// 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 -#include -#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 diff --git a/lib/IRremoteESP8266/src/ir_GlobalCache.cpp b/lib/IRremoteESP8266/src/ir_GlobalCache.cpp deleted file mode 100644 index e8ebac4af8..0000000000 --- a/lib/IRremoteESP8266/src/ir_GlobalCache.cpp +++ /dev/null @@ -1,63 +0,0 @@ -// 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 -#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 diff --git a/lib/IRremoteESP8266/src/ir_Goodweather.cpp b/lib/IRremoteESP8266/src/ir_Goodweather.cpp deleted file mode 100644 index 1d6918e7ff..0000000000 --- a/lib/IRremoteESP8266/src/ir_Goodweather.cpp +++ /dev/null @@ -1,499 +0,0 @@ -// 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 -#ifndef ARDUINO -#include -#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 diff --git a/lib/IRremoteESP8266/src/ir_Goodweather.h b/lib/IRremoteESP8266/src/ir_Goodweather.h index 7e547edd83..e1f7f5ed5d 100644 --- a/lib/IRremoteESP8266/src/ir_Goodweather.h +++ b/lib/IRremoteESP8266/src/ir_Goodweather.h @@ -16,10 +16,10 @@ #ifndef UNIT_TEST #include #endif -#include "IRremoteESP8266.h" -#include "IRsend.h" +#include "../src/IRremoteESP8266.h" +#include "../src/IRsend.h" #ifdef UNIT_TEST -#include "IRsend_test.h" +#include "../src/IRsend_test.h" #endif /// Native representation of a Goodweather A/C message. diff --git a/lib/IRremoteESP8266/src/ir_Gree.cpp b/lib/IRremoteESP8266/src/ir_Gree.cpp deleted file mode 100644 index 1d1371b2d0..0000000000 --- a/lib/IRremoteESP8266/src/ir_Gree.cpp +++ /dev/null @@ -1,715 +0,0 @@ -// 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 -#include -#ifndef ARDUINO -#include -#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::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; - } -} - -/// 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: _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(kGreeMinTempC), safecelsius); - safecelsius = std::min(static_cast(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(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 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 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; - } - } - _.Swing = 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 _.Swing; -} - -/// 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 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 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(); - result.fanspeed = toCommonFanSpeed(_.Fan); - if (_.SwingAuto) - result.swingv = stdAc::swingv_t::kAuto; - else - result.swingv = toCommonSwingV(_.Swing); - result.turbo = _.Turbo; - result.light = _.Light; - result.clean = _.Xfan; - result.sleep = _.Sleep ? 0 : -1; - // Not supported. - result.swingh = stdAc::swingh_t::kOff; - result.quiet = false; - result.econo = 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); - 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(_.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(_.Swing, kSwingVStr); - result += kSpaceLBraceStr; - switch (_.Swing) { - case kGreeSwingLastPos: - result += kLastStr; - break; - case kGreeSwingAuto: - result += kAutoStr; - break; - default: result += kUnknownStr; - } - result += ')'; - 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 diff --git a/lib/IRremoteESP8266/src/ir_Gree.h b/lib/IRremoteESP8266/src/ir_Gree.h index d93b42d8d9..d1d3e00be9 100644 --- a/lib/IRremoteESP8266/src/ir_Gree.h +++ b/lib/IRremoteESP8266/src/ir_Gree.h @@ -32,10 +32,10 @@ #ifndef UNIT_TEST #include #endif -#include "IRremoteESP8266.h" -#include "IRsend.h" +#include "../src/IRremoteESP8266.h" +#include "../src/IRsend.h" #ifdef UNIT_TEST -#include "IRsend_test.h" +#include "../src/IRsend_test.h" #endif /// Native representation of a Gree A/C message. diff --git a/lib/IRremoteESP8266/src/ir_Haier.cpp b/lib/IRremoteESP8266/src/ir_Haier.cpp deleted file mode 100644 index 41e40541aa..0000000000 --- a/lib/IRremoteESP8266/src/ir_Haier.cpp +++ /dev/null @@ -1,1241 +0,0 @@ -// 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/1480 - -#include "ir_Haier.h" -#include -#include -#ifndef UNIT_TEST -#include -#endif -#include "IRremoteESP8266.h" -#include "IRtext.h" -#include "IRutils.h" - -// Constants -const uint16_t kHaierAcHdr = 3000; -const uint16_t kHaierAcHdrGap = 4300; -const uint16_t kHaierAcBitMark = 520; -const uint16_t kHaierAcOneSpace = 1650; -const uint16_t kHaierAcZeroSpace = 650; -const uint32_t kHaierAcMinGap = 150000; // Completely made up value. - -using irutils::addBoolToString; -using irutils::addIntToString; -using irutils::addLabeledString; -using irutils::addModeToString; -using irutils::addFanToString; -using irutils::addTempToString; -using irutils::minsToString; - -#define GETTIME(x) _.x##Hours * 60 + _.x##Mins -#define SETTIME(x, n) do { \ - uint16_t mins = n;\ - if (n > kHaierAcMaxTime) mins = kHaierAcMaxTime;\ - _.x##Hours = mins / 60;\ - _.x##Mins = mins % 60;\ -} while (0) - -#if (SEND_HAIER_AC || SEND_HAIER_AC_YRW02 || SEND_HAIER_AC176) -/// Send a Haier A/C formatted message. (HSU07-HEA03 remote) -/// 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::sendHaierAC(const unsigned char data[], const uint16_t nbytes, - const uint16_t repeat) { - if (nbytes < kHaierACStateLength) return; - - for (uint16_t r = 0; r <= repeat; r++) { - enableIROut(38000); - mark(kHaierAcHdr); - space(kHaierAcHdr); - sendGeneric(kHaierAcHdr, kHaierAcHdrGap, kHaierAcBitMark, kHaierAcOneSpace, - kHaierAcBitMark, kHaierAcZeroSpace, kHaierAcBitMark, - kHaierAcMinGap, data, nbytes, 38, true, - 0, // Repeats handled elsewhere - 50); - } -} -#endif // (SEND_HAIER_AC || SEND_HAIER_AC_YRW02 || SEND_HAIER_AC176) - -#if SEND_HAIER_AC_YRW02 -/// Send a Haier YR-W02 remote A/C formatted 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::sendHaierACYRW02(const unsigned char data[], const uint16_t nbytes, - const uint16_t repeat) { - if (nbytes >= kHaierACYRW02StateLength) sendHaierAC(data, nbytes, repeat); -} -#endif // SEND_HAIER_AC_YRW02 - -#if SEND_HAIER_AC176 -/// Send a Haier 176 bit remote A/C formatted 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::sendHaierAC176(const unsigned char data[], const uint16_t nbytes, - const uint16_t repeat) { - if (nbytes >= kHaierAC176StateLength) sendHaierAC(data, nbytes, repeat); -} -#endif // SEND_HAIER_AC176 - -/// 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? -IRHaierAC::IRHaierAC(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 IRHaierAC::begin(void) { _irsend.begin(); } - -#if SEND_HAIER_AC -/// Send the current internal state as an IR message. -/// @param[in] repeat Nr. of times the message will be repeated. -void IRHaierAC::send(const uint16_t repeat) { - _irsend.sendHaierAC(getRaw(), kHaierACStateLength, repeat); -} -#endif // SEND_HAIER_AC - -/// Calculate and set the checksum values for the internal state. -void IRHaierAC::checksum(void) { - _.Sum = sumBytes(_.remote_state, kHaierACStateLength - 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 of the state array. -/// @return true, if the state has a valid checksum. Otherwise, false. -bool IRHaierAC::validChecksum(uint8_t state[], const uint16_t length) { - if (length < 2) return false; // 1 byte of data can't have a checksum. - return (state[length - 1] == sumBytes(state, length - 1)); -} - -/// Reset the internal state to a fixed known good state. -void IRHaierAC::stateReset(void) { - std::memset(_.remote_state, 0, sizeof _.remote_state); - _.Prefix = kHaierAcPrefix; - _.unknown = 1; // const value - _.OffHours = 12; // default initial state - _.Temp = kHaierAcDefTemp - kHaierAcMinTemp; - _.Fan = 3; // kHaierAcFanLow; - _.Command = kHaierAcCmdOn; -} - -/// 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* IRHaierAC::getRaw(void) { - checksum(); - 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 IRHaierAC::setRaw(const uint8_t new_code[]) { - std::memcpy(_.remote_state, new_code, kHaierACStateLength); -} - -/// Set the Command/Button setting of the A/C. -/// @param[in] command The value of the command/button that was pressed. -void IRHaierAC::setCommand(const uint8_t command) { - switch (command) { - case kHaierAcCmdOff: - case kHaierAcCmdOn: - case kHaierAcCmdMode: - case kHaierAcCmdFan: - case kHaierAcCmdTempUp: - case kHaierAcCmdTempDown: - case kHaierAcCmdSleep: - case kHaierAcCmdTimerSet: - case kHaierAcCmdTimerCancel: - case kHaierAcCmdHealth: - case kHaierAcCmdSwing: - _.Command = command; - } -} - -/// Get the Command/Button setting of the A/C. -/// @return The value of the command/button that was pressed. -uint8_t IRHaierAC::getCommand(void) const { - return _.Command; -} - -/// Set the speed of the fan. -/// @param[in] speed The desired setting. -void IRHaierAC::setFan(const uint8_t speed) { - uint8_t new_speed = kHaierAcFanAuto; - switch (speed) { - case kHaierAcFanLow: new_speed = 3; break; - case kHaierAcFanMed: new_speed = 2; break; - case kHaierAcFanHigh: new_speed = 1; break; - // Default to auto for anything else. - default: new_speed = kHaierAcFanAuto; - } - - if (speed != getFan()) _.Command = kHaierAcCmdFan; - _.Fan = new_speed; -} - -/// Get the current fan speed setting. -/// @return The current fan speed. -uint8_t IRHaierAC::getFan(void) const { - switch (_.Fan) { - case 1: return kHaierAcFanHigh; - case 2: return kHaierAcFanMed; - case 3: return kHaierAcFanLow; - default: return kHaierAcFanAuto; - } -} - -/// Set the operating mode of the A/C. -/// @param[in] mode The desired operating mode. -void IRHaierAC::setMode(const uint8_t mode) { - uint8_t new_mode = mode; - _.Command = kHaierAcCmdMode; - // If out of range, default to auto mode. - if (mode > kHaierAcFan) new_mode = kHaierAcAuto; - _.Mode = new_mode; -} - -/// Get the operating mode setting of the A/C. -/// @return The current operating mode setting. -uint8_t IRHaierAC::getMode(void) const { - return _.Mode; -} - -/// Set the temperature. -/// @param[in] degrees The temperature in degrees celsius. -void IRHaierAC::setTemp(const uint8_t degrees) { - uint8_t temp = degrees; - if (temp < kHaierAcMinTemp) - temp = kHaierAcMinTemp; - else if (temp > kHaierAcMaxTemp) - temp = kHaierAcMaxTemp; - - uint8_t old_temp = getTemp(); - if (old_temp == temp) return; - if (old_temp > temp) - _.Command = kHaierAcCmdTempDown; - else - _.Command = kHaierAcCmdTempUp; - _.Temp = temp - kHaierAcMinTemp; -} - -/// Get the current temperature setting. -/// @return The current setting for temp. in degrees celsius. -uint8_t IRHaierAC::getTemp(void) const { - return _.Temp + kHaierAcMinTemp; -} - -/// Set the Health (filter) setting of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRHaierAC::setHealth(const bool on) { - _.Command = kHaierAcCmdHealth; - _.Health = on; -} - -/// Get the Health (filter) setting of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRHaierAC::getHealth(void) const { - return _.Health; -} - -/// Set the Sleep setting of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRHaierAC::setSleep(const bool on) { - _.Command = kHaierAcCmdSleep; - _.Sleep = on; -} - -/// Get the Sleep setting of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRHaierAC::getSleep(void) const { - return _.Sleep; -} - -/// Get the On Timer value/setting of the A/C. -/// @return Nr of minutes the timer is set to. -1 is Off/not set etc. -int16_t IRHaierAC::getOnTimer(void) const { - // Check if the timer is turned on. - if (_.OnTimer) - return GETTIME(On); - else - return -1; -} - -/// Get the Off Timer value/setting of the A/C. -/// @return Nr of minutes the timer is set to. -1 is Off/not set etc. -int16_t IRHaierAC::getOffTimer(void) const { - // Check if the timer is turned on. - if (_.OffTimer) - return GETTIME(Off); - else - return -1; -} - -/// Get the clock value of the A/C. -/// @return The clock time, in Nr of minutes past midnight. -uint16_t IRHaierAC::getCurrTime(void) const { return GETTIME(Curr); } - -/// Set & enable the On Timer. -/// @param[in] nr_mins The time expressed in total number of minutes. -void IRHaierAC::setOnTimer(const uint16_t nr_mins) { - _.Command = kHaierAcCmdTimerSet; - _.OnTimer = 1; - - SETTIME(On, nr_mins); -} - -/// Set & enable the Off Timer. -/// @param[in] nr_mins The time expressed in total number of minutes. -void IRHaierAC::setOffTimer(const uint16_t nr_mins) { - _.Command = kHaierAcCmdTimerSet; - _.OffTimer = 1; - - SETTIME(Off, nr_mins); -} - -/// Cancel/disable the On & Off timers. -void IRHaierAC::cancelTimers(void) { - _.Command = kHaierAcCmdTimerCancel; - _.OffTimer = 0; - _.OnTimer = 0; -} - -/// Set the clock value for the A/C. -/// @param[in] nr_mins The clock time, in Nr of minutes past midnight. -void IRHaierAC::setCurrTime(const uint16_t nr_mins) { - SETTIME(Curr, nr_mins); -} - -/// Get the Vertical Swing position setting of the A/C. -/// @return The native swing mode. -uint8_t IRHaierAC::getSwing(void) const { - return _.Swing; -} - -/// Set the Vertical Swing mode of the A/C. -/// @param[in] state The mode to set the vanes to. -void IRHaierAC::setSwing(const uint8_t state) { - if (state == _.Swing) return; // Nothing to do. - switch (state) { - case kHaierAcSwingOff: - case kHaierAcSwingUp: - case kHaierAcSwingDown: - case kHaierAcSwingChg: - _.Command = kHaierAcCmdSwing; - _.Swing = state; - 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 IRHaierAC::convertMode(const stdAc::opmode_t mode) { - switch (mode) { - case stdAc::opmode_t::kCool: return kHaierAcCool; - case stdAc::opmode_t::kHeat: return kHaierAcHeat; - case stdAc::opmode_t::kDry: return kHaierAcDry; - case stdAc::opmode_t::kFan: return kHaierAcFan; - default: return kHaierAcAuto; - } -} - -/// 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 IRHaierAC::convertFan(const stdAc::fanspeed_t speed) { - switch (speed) { - case stdAc::fanspeed_t::kMin: - case stdAc::fanspeed_t::kLow: return kHaierAcFanLow; - case stdAc::fanspeed_t::kMedium: return kHaierAcFanMed; - case stdAc::fanspeed_t::kHigh: - case stdAc::fanspeed_t::kMax: return kHaierAcFanHigh; - default: return kHaierAcFanAuto; - } -} - -/// 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 IRHaierAC::convertSwingV(const stdAc::swingv_t position) { - switch (position) { - case stdAc::swingv_t::kHighest: - case stdAc::swingv_t::kHigh: - case stdAc::swingv_t::kMiddle: return kHaierAcSwingUp; - case stdAc::swingv_t::kLow: - case stdAc::swingv_t::kLowest: return kHaierAcSwingDown; - case stdAc::swingv_t::kOff: return kHaierAcSwingOff; - default: return kHaierAcSwingChg; - } -} - -/// 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 IRHaierAC::toCommonMode(const uint8_t mode) { - switch (mode) { - case kHaierAcCool: return stdAc::opmode_t::kCool; - case kHaierAcHeat: return stdAc::opmode_t::kHeat; - case kHaierAcDry: return stdAc::opmode_t::kDry; - case kHaierAcFan: 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 IRHaierAC::toCommonFanSpeed(const uint8_t speed) { - switch (speed) { - case kHaierAcFanHigh: return stdAc::fanspeed_t::kMax; - case kHaierAcFanMed: return stdAc::fanspeed_t::kMedium; - case kHaierAcFanLow: return stdAc::fanspeed_t::kMin; - default: return stdAc::fanspeed_t::kAuto; - } -} - -/// Convert a stdAc::swingv_t enum into it's native setting. -/// @param[in] pos The enum to be converted. -/// @return The native equivalent of the enum. -stdAc::swingv_t IRHaierAC::toCommonSwingV(const uint8_t pos) { - switch (pos) { - case kHaierAcSwingUp: return stdAc::swingv_t::kHighest; - case kHaierAcSwingDown: return stdAc::swingv_t::kLowest; - case kHaierAcSwingOff: return stdAc::swingv_t::kOff; - default: return stdAc::swingv_t::kAuto; - } -} - -/// Convert the current internal state into its stdAc::state_t equivalent. -/// @return The stdAc equivalent of the native settings. -stdAc::state_t IRHaierAC::toCommon(void) const { - stdAc::state_t result; - result.protocol = decode_type_t::HAIER_AC; - result.model = -1; // No models used. - result.power = true; - if (_.Command == kHaierAcCmdOff) result.power = false; - result.mode = toCommonMode(_.Mode); - result.celsius = true; - result.degrees = getTemp(); - result.fanspeed = toCommonFanSpeed(getFan()); - result.swingv = toCommonSwingV(_.Swing); - result.filter = _.Health; - result.sleep = _.Sleep ? 0 : -1; - // Not supported. - result.swingh = stdAc::swingh_t::kOff; - result.quiet = false; - result.turbo = false; - result.econo = false; - result.light = false; - result.clean = false; - result.beep = true; - result.clock = -1; - return result; -} - -/// Convert the current internal state into a human readable string. -/// @return A human readable string. -String IRHaierAC::toString(void) const { - String result = ""; - result.reserve(150); // Reserve some heap for the string to reduce fragging. - uint8_t cmd = _.Command; - result += addIntToString(cmd, kCommandStr, false); - result += kSpaceLBraceStr; - switch (cmd) { - case kHaierAcCmdOff: - result += kOffStr; - break; - case kHaierAcCmdOn: - result += kOnStr; - break; - case kHaierAcCmdMode: - result += kModeStr; - break; - case kHaierAcCmdFan: - result += kFanStr; - break; - case kHaierAcCmdTempUp: - result += kTempUpStr; - break; - case kHaierAcCmdTempDown: - result += kTempDownStr; - break; - case kHaierAcCmdSleep: - result += kSleepStr; - break; - case kHaierAcCmdTimerSet: - result += kTimerStr; - result += ' '; - result += kSetStr; - break; - case kHaierAcCmdTimerCancel: - result += kTimerStr; - result += ' '; - result += kCancelStr; - break; - case kHaierAcCmdHealth: - result += kHealthStr; - break; - case kHaierAcCmdSwing: - result += kSwingStr; - break; - default: - result += kUnknownStr; - } - result += ')'; - result += addModeToString(_.Mode, kHaierAcAuto, kHaierAcCool, kHaierAcHeat, - kHaierAcDry, kHaierAcFan); - result += addTempToString(getTemp()); - result += addFanToString(getFan(), kHaierAcFanHigh, kHaierAcFanLow, - kHaierAcFanAuto, kHaierAcFanAuto, kHaierAcFanMed); - result += addIntToString(_.Swing, kSwingStr); - result += kSpaceLBraceStr; - switch (_.Swing) { - case kHaierAcSwingOff: - result += kOffStr; - break; - case kHaierAcSwingUp: - result += kUpStr; - break; - case kHaierAcSwingDown: - result += kDownStr; - break; - case kHaierAcSwingChg: - result += kChangeStr; - break; - default: - result += kUnknownStr; - } - result += ')'; - result += addBoolToString(_.Sleep, kSleepStr); - result += addBoolToString(_.Health, kHealthStr); - result += addLabeledString(minsToString(getCurrTime()), kClockStr); - result += addLabeledString( - getOnTimer() >= 0 ? minsToString(getOnTimer()) : kOffStr, kOnTimerStr); - result += addLabeledString( - getOffTimer() >= 0 ? minsToString(getOffTimer()) : kOffStr, - kOffTimerStr); - return result; -} -// End of IRHaierAC 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? -IRHaierAC176::IRHaierAC176(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 IRHaierAC176::begin(void) { _irsend.begin(); } - -#if SEND_HAIER_AC176 -/// Send the current internal state as an IR message. -/// @param[in] repeat Nr. of times the message will be repeated. -void IRHaierAC176::send(const uint16_t repeat) { - _irsend.sendHaierAC176(getRaw(), kHaierAC176StateLength, repeat); -} -#endif // SEND_HAIER_AC176 - -/// Calculate and set the checksum values for the internal state. -void IRHaierAC176::checksum(void) { - _.Sum = sumBytes(_.raw, kHaierACYRW02StateLength - 1); - _.Sum2 = sumBytes(_.raw + kHaierACYRW02StateLength, - kHaierAC176StateLength - kHaierACYRW02StateLength - 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 of the state array. -/// @return true, if the state has a valid checksum. Otherwise, false. -bool IRHaierAC176::validChecksum(const uint8_t state[], const uint16_t length) { - if (length < 2) return false; // 1 byte of data can't have a checksum. - if (length < kHaierAC176StateLength) { // Is it too short? - // Then it is just a checksum of the whole thing. - return (state[length - 1] == sumBytes(state, length - 1)); - } else { // It is long enough for two checksums. - return (state[kHaierACYRW02StateLength - 1] == - sumBytes(state, kHaierACYRW02StateLength - 1)) && - (state[length - 1] == - sumBytes(state + kHaierACYRW02StateLength, - length - kHaierACYRW02StateLength - 1)); - } -} - -/// Reset the internal state to a fixed known good state. -void IRHaierAC176::stateReset(void) { - std::memset(_.raw, 0, sizeof _.raw); - _.Prefix = kHaierAcYrw02Prefix; - _.Prefix2 = kHaierAc176Prefix; - _.Temp = kHaierAcDefTemp - kHaierAcMinTemp; - _.Health = true; - setFan(kHaierAcYrw02FanAuto); - _.Power = true; - _.Button = kHaierAcYrw02ButtonPower; -} - -/// 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* IRHaierAC176::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. -void IRHaierAC176::setRaw(const uint8_t new_code[]) { - memcpy(_.raw, new_code, kHaierAC176StateLength); -} - -/// Set the Button/Command setting of the A/C. -/// @param[in] button The value of the button/command that was pressed. -void IRHaierAC176::setButton(uint8_t button) { - switch (button) { - case kHaierAcYrw02ButtonTempUp: - case kHaierAcYrw02ButtonTempDown: - case kHaierAcYrw02ButtonSwing: - case kHaierAcYrw02ButtonFan: - case kHaierAcYrw02ButtonPower: - case kHaierAcYrw02ButtonMode: - case kHaierAcYrw02ButtonHealth: - case kHaierAcYrw02ButtonTurbo: - case kHaierAcYrw02ButtonSleep: - _.Button = button; - } -} - -/// Get the Button/Command setting of the A/C. -/// @return The value of the button/command that was pressed. -uint8_t IRHaierAC176::getButton(void) const { - return _.Button; -} - -/// Set the operating mode of the A/C. -/// @param[in] mode The desired operating mode. -void IRHaierAC176::setMode(uint8_t mode) { - switch (mode) { - case kHaierAcYrw02Auto: - case kHaierAcYrw02Dry: - case kHaierAcYrw02Fan: - // Turbo & Quiet is only available in Cool/Heat mode. - _.Turbo = false; - _.Quiet = false; - // FALL-THRU - case kHaierAcYrw02Cool: - case kHaierAcYrw02Heat: - _.Button = kHaierAcYrw02ButtonMode; - _.Mode = mode; - break; - default: - setMode(kHaierAcYrw02Auto); // Unexpected, default to auto mode. - } -} - -/// Get the operating mode setting of the A/C. -/// @return The current operating mode setting. -uint8_t IRHaierAC176::getMode(void) const { return _.Mode; } - -/// Set the temperature. -/// @param[in] celsius The temperature in degrees celsius. -void IRHaierAC176::setTemp(const uint8_t celsius) { - uint8_t temp = celsius; - if (temp < kHaierAcMinTemp) - temp = kHaierAcMinTemp; - else if (temp > kHaierAcMaxTemp) - temp = kHaierAcMaxTemp; - - uint8_t old_temp = getTemp(); - if (old_temp == temp) return; - if (old_temp > temp) - _.Button = kHaierAcYrw02ButtonTempDown; - else - _.Button = kHaierAcYrw02ButtonTempUp; - _.Temp = temp - kHaierAcMinTemp; -} - -/// Get the current temperature setting. -/// @return The current setting for temp. in degrees celsius. -uint8_t IRHaierAC176::getTemp(void) const { return _.Temp + kHaierAcMinTemp; } - -/// Set the Health (filter) setting of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRHaierAC176::setHealth(const bool on) { - _.Button = kHaierAcYrw02ButtonHealth; - _.Health = on; -} - -/// Get the Health (filter) setting of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRHaierAC176::getHealth(void) const { return _.Health; } - -/// Get the value of the current power setting. -/// @return true, the setting is on. false, the setting is off. -bool IRHaierAC176::getPower(void) const { return _.Power; } - -/// Change the power setting. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRHaierAC176::setPower(const bool on) { - _.Button = kHaierAcYrw02ButtonPower; - _.Power = on; -} - -/// Change the power setting to On. -void IRHaierAC176::on(void) { setPower(true); } - -/// Change the power setting to Off. -void IRHaierAC176::off(void) { setPower(false); } - -/// Get the Sleep setting of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRHaierAC176::getSleep(void) const { return _.Sleep; } - -/// Set the Sleep setting of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRHaierAC176::setSleep(const bool on) { - _.Button = kHaierAcYrw02ButtonSleep; - _.Sleep = on; -} - -/// Get the Turbo setting of the A/C. -/// @return The current turbo setting. -bool IRHaierAC176::getTurbo(void) const { return _.Turbo; } - -/// Set the Turbo setting of the A/C. -/// @param[in] on The desired turbo setting. -/// @note Turbo & Quiet can't be on at the same time, and only in Heat/Cool mode -void IRHaierAC176::setTurbo(const bool on) { - switch (getMode()) { - case kHaierAcYrw02Cool: - case kHaierAcYrw02Heat: - _.Turbo = on; - _.Button = kHaierAcYrw02ButtonTurbo; - if (on) _.Quiet = false; - } -} - -/// Get the Quiet setting of the A/C. -/// @return The current Quiet setting. -bool IRHaierAC176::getQuiet(void) const { return _.Quiet; } - -/// Set the Quiet setting of the A/C. -/// @param[in] on The desired Quiet setting. -/// @note Turbo & Quiet can't be on at the same time, and only in Heat/Cool mode -void IRHaierAC176::setQuiet(const bool on) { - switch (getMode()) { - case kHaierAcYrw02Cool: - case kHaierAcYrw02Heat: - _.Quiet = on; - _.Button = kHaierAcYrw02ButtonTurbo; - if (on) _.Turbo = false; - } -} - -/// Get the current fan speed setting. -/// @return The current fan speed. -uint8_t IRHaierAC176::getFan(void) const { return _.Fan; } - -/// Set the speed of the fan. -/// @param[in] speed The desired setting. -void IRHaierAC176::setFan(uint8_t speed) { - switch (speed) { - case kHaierAcYrw02FanLow: - case kHaierAcYrw02FanMed: - case kHaierAcYrw02FanHigh: - case kHaierAcYrw02FanAuto: - _.Fan = speed; - _.Fan2 = (speed == kHaierAcYrw02FanAuto) ? 0 : speed; - _.Button = kHaierAcYrw02ButtonFan; - } -} - -/// Get the Vertical Swing position setting of the A/C. -/// @return The native position/mode. -uint8_t IRHaierAC176::getSwing(void) const { return _.Swing; } - -/// Set the Vertical Swing mode of the A/C. -/// @param[in] pos The position/mode to set the vanes to. -void IRHaierAC176::setSwing(uint8_t pos) { - uint8_t newpos = pos; - switch (pos) { - case kHaierAcYrw02SwingOff: - case kHaierAcYrw02SwingAuto: - case kHaierAcYrw02SwingTop: - case kHaierAcYrw02SwingMiddle: - case kHaierAcYrw02SwingBottom: - case kHaierAcYrw02SwingDown: _.Button = kHaierAcYrw02ButtonSwing; break; - default: return; // Unexpected value so don't do anything. - } - // Heat mode has no MIDDLE setting, use BOTTOM instead. - if (pos == kHaierAcYrw02SwingMiddle && _.Mode == kHaierAcYrw02Heat) - newpos = kHaierAcYrw02SwingBottom; - // BOTTOM is only allowed if we are in Heat mode, otherwise MIDDLE. - if (pos == kHaierAcYrw02SwingBottom && _.Mode != kHaierAcYrw02Heat) - newpos = kHaierAcYrw02SwingMiddle; - _.Swing = newpos; -} - - -/// Set the Timer operating mode. -/// @param[in] mode The timer mode to use. -void IRHaierAC176::setTimerMode(const uint8_t mode) { - _.TimerMode = (mode > kHaierAcYrw02OffThenOnTimer) ? kHaierAcYrw02NoTimers - : mode; - switch (_.TimerMode) { - case kHaierAcYrw02NoTimers: - setOnTimer(0); // Disable the On timer. - setOffTimer(0); // Disable the Off timer. - break; - case kHaierAcYrw02OffTimer: - setOnTimer(0); // Disable the On timer. - break; - case kHaierAcYrw02OnTimer: - setOffTimer(0); // Disable the Off timer. - break; - } -} - -/// Get the Timer operating mode. -/// @return The mode of the timer is currently configured to. -uint8_t IRHaierAC176::getTimerMode(void) const { return _.TimerMode; } - -/// Set the number of minutes of the On Timer setting. -/// @param[in] mins Nr. of Minutes for the Timer. `0` means disable the timer. -void IRHaierAC176::setOnTimer(const uint16_t mins) { - const uint16_t nr_mins = std::min((uint16_t)(23 * 60 + 59), mins); - _.OnTimerHrs = nr_mins / 60; - _.OnTimerMins = nr_mins % 60; - - const bool enabled = (nr_mins > 0); - uint8_t mode = getTimerMode(); - switch (mode) { - case kHaierAcYrw02OffTimer: - mode = enabled ? kHaierAcYrw02OffThenOnTimer : mode; - break; - case kHaierAcYrw02OnThenOffTimer: - case kHaierAcYrw02OffThenOnTimer: - mode = enabled ? kHaierAcYrw02OffThenOnTimer : kHaierAcYrw02OffTimer; - break; - default: - // Enable/Disable the On timer for the simple case. - mode = enabled << 1; - } - _.TimerMode = mode; -} - -/// Get the number of minutes of the On Timer setting. -/// @return Nr of minutes. -uint16_t IRHaierAC176::getOnTimer(void) const { - return _.OnTimerHrs * 60 + _.OnTimerMins; -} - -/// Set the number of minutes of the Off Timer setting. -/// @param[in] mins Nr. of Minutes for the Timer. `0` means disable the timer. -void IRHaierAC176::setOffTimer(const uint16_t mins) { - const uint16_t nr_mins = std::min((uint16_t)(23 * 60 + 59), mins); - _.OffTimerHrs = nr_mins / 60; - _.OffTimerMins = nr_mins % 60; - - const bool enabled = (nr_mins > 0); - uint8_t mode = getTimerMode(); - switch (mode) { - case kHaierAcYrw02OnTimer: - mode = enabled ? kHaierAcYrw02OnThenOffTimer : mode; - break; - case kHaierAcYrw02OnThenOffTimer: - case kHaierAcYrw02OffThenOnTimer: - mode = enabled ? kHaierAcYrw02OnThenOffTimer : kHaierAcYrw02OnTimer; - break; - default: - // Enable/Disable the Off timer for the simple case. - mode = enabled; - } - _.TimerMode = mode; -} - -/// Get the number of minutes of the Off Timer setting. -/// @return Nr of minutes. -uint16_t IRHaierAC176::getOffTimer(void) const { - return _.OffTimerHrs * 60 + _.OffTimerMins; -} - -/// 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 IRHaierAC176::convertMode(const stdAc::opmode_t mode) { - switch (mode) { - case stdAc::opmode_t::kCool: return kHaierAcYrw02Cool; - case stdAc::opmode_t::kHeat: return kHaierAcYrw02Heat; - case stdAc::opmode_t::kDry: return kHaierAcYrw02Dry; - case stdAc::opmode_t::kFan: return kHaierAcYrw02Fan; - default: return kHaierAcYrw02Auto; - } -} - -/// 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 IRHaierAC176::convertFan(const stdAc::fanspeed_t speed) { - switch (speed) { - case stdAc::fanspeed_t::kMin: - case stdAc::fanspeed_t::kLow: return kHaierAcYrw02FanLow; - case stdAc::fanspeed_t::kMedium: return kHaierAcYrw02FanMed; - case stdAc::fanspeed_t::kHigh: - case stdAc::fanspeed_t::kMax: return kHaierAcYrw02FanHigh; - default: return kHaierAcYrw02FanAuto; - } -} - -/// 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 IRHaierAC176::convertSwingV(const stdAc::swingv_t position) { - switch (position) { - case stdAc::swingv_t::kHighest: - case stdAc::swingv_t::kHigh: return kHaierAcYrw02SwingTop; - case stdAc::swingv_t::kMiddle: return kHaierAcYrw02SwingMiddle; - case stdAc::swingv_t::kLow: return kHaierAcYrw02SwingDown; - case stdAc::swingv_t::kLowest: return kHaierAcYrw02SwingBottom; - case stdAc::swingv_t::kOff: return kHaierAcYrw02SwingOff; - default: return kHaierAcYrw02SwingAuto; - } -} - -/// 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 IRHaierAC176::toCommonMode(const uint8_t mode) { - switch (mode) { - case kHaierAcYrw02Cool: return stdAc::opmode_t::kCool; - case kHaierAcYrw02Heat: return stdAc::opmode_t::kHeat; - case kHaierAcYrw02Dry: return stdAc::opmode_t::kDry; - case kHaierAcYrw02Fan: 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 IRHaierAC176::toCommonFanSpeed(const uint8_t speed) { - switch (speed) { - case kHaierAcYrw02FanHigh: return stdAc::fanspeed_t::kMax; - case kHaierAcYrw02FanMed: return stdAc::fanspeed_t::kMedium; - case kHaierAcYrw02FanLow: return stdAc::fanspeed_t::kMin; - default: return stdAc::fanspeed_t::kAuto; - } -} - -/// Convert a stdAc::swingv_t enum into it's native setting. -/// @param[in] pos The enum to be converted. -/// @return The native equivalent of the enum. -stdAc::swingv_t IRHaierAC176::toCommonSwingV(const uint8_t pos) { - switch (pos) { - case kHaierAcYrw02SwingTop: return stdAc::swingv_t::kHighest; - case kHaierAcYrw02SwingMiddle: return stdAc::swingv_t::kMiddle; - case kHaierAcYrw02SwingDown: return stdAc::swingv_t::kLow; - case kHaierAcYrw02SwingBottom: return stdAc::swingv_t::kLowest; - case kHaierAcYrw02SwingOff: return stdAc::swingv_t::kOff; - default: return stdAc::swingv_t::kAuto; - } -} - -/// Convert the current internal state into its stdAc::state_t equivalent. -/// @return The stdAc equivalent of the native settings. -stdAc::state_t IRHaierAC176::toCommon(void) const { - stdAc::state_t result; - result.protocol = decode_type_t::HAIER_AC_YRW02; - 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 = toCommonSwingV(_.Swing); - result.filter = _.Health; - result.sleep = _.Sleep ? 0 : -1; - result.turbo = _.Turbo; - result.quiet = _.Quiet; - // Not supported. - result.swingh = stdAc::swingh_t::kOff; - result.econo = false; - result.light = false; - result.clean = false; - result.beep = true; - result.clock = -1; - return result; -} - -/// Convert the current internal state into a human readable string. -/// @return A human readable string. -String IRHaierAC176::toString(void) const { - String result = ""; - result.reserve(130); // Reserve some heap for the string to reduce fragging. - result += addBoolToString(_.Power, kPowerStr, false); - uint8_t cmd = _.Button; - result += addIntToString(cmd, kButtonStr); - result += kSpaceLBraceStr; - switch (cmd) { - case kHaierAcYrw02ButtonPower: - result += kPowerStr; - break; - case kHaierAcYrw02ButtonMode: - result += kModeStr; - break; - case kHaierAcYrw02ButtonFan: - result += kFanStr; - break; - case kHaierAcYrw02ButtonTempUp: - result += kTempUpStr; - break; - case kHaierAcYrw02ButtonTempDown: - result += kTempDownStr; - break; - case kHaierAcYrw02ButtonSleep: - result += kSleepStr; - break; - case kHaierAcYrw02ButtonHealth: - result += kHealthStr; - break; - case kHaierAcYrw02ButtonSwing: - result += kSwingStr; - break; - case kHaierAcYrw02ButtonTurbo: - result += kTurboStr; - break; - default: - result += kUnknownStr; - } - result += ')'; - result += addModeToString(_.Mode, kHaierAcYrw02Auto, kHaierAcYrw02Cool, - kHaierAcYrw02Heat, kHaierAcYrw02Dry, - kHaierAcYrw02Fan); - result += addTempToString(getTemp()); - result += addFanToString(_.Fan, kHaierAcYrw02FanHigh, kHaierAcYrw02FanLow, - kHaierAcYrw02FanAuto, kHaierAcYrw02FanAuto, - kHaierAcYrw02FanMed); - result += addBoolToString(_.Turbo, kTurboStr); - result += addBoolToString(_.Quiet, kQuietStr); - result += addIntToString(_.Swing, kSwingStr); - result += kSpaceLBraceStr; - switch (_.Swing) { - case kHaierAcYrw02SwingOff: - result += kOffStr; - break; - case kHaierAcYrw02SwingAuto: - result += kAutoStr; - break; - case kHaierAcYrw02SwingBottom: - result += kLowestStr; - break; - case kHaierAcYrw02SwingDown: - result += kLowStr; - break; - case kHaierAcYrw02SwingTop: - result += kHighestStr; - break; - case kHaierAcYrw02SwingMiddle: - result += kMiddleStr; - break; - default: - result += kUnknownStr; - } - result += ')'; - result += addBoolToString(_.Sleep, kSleepStr); - result += addBoolToString(_.Health, kHealthStr); - const uint8_t tmode = getTimerMode(); - result += addIntToString(tmode, kTimerModeStr); - result += kSpaceLBraceStr; - switch (tmode) { - case kHaierAcYrw02NoTimers: - result += kNAStr; - break; - case kHaierAcYrw02OnTimer: - result += kOnStr; - break; - case kHaierAcYrw02OffTimer: - result += kOffStr; - break; - case kHaierAcYrw02OnThenOffTimer: - result += kOnStr; - result += '-'; - result += kOffStr; - break; - case kHaierAcYrw02OffThenOnTimer: - result += kOffStr; - result += '-'; - result += kOnStr; - break; - default: - result += kUnknownStr; - } - result += ')'; - result += addLabeledString((tmode != kHaierAcYrw02NoTimers && - tmode != kHaierAcYrw02OffTimer) ? - minsToString(getOnTimer()) : kOffStr, kOnTimerStr); - result += addLabeledString((tmode != kHaierAcYrw02NoTimers && - tmode != kHaierAcYrw02OnTimer) ? - minsToString(getOffTimer()) : kOffStr, kOffTimerStr); - return result; -} -// End of IRHaierAC176 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? -IRHaierACYRW02::IRHaierACYRW02(const uint16_t pin, const bool inverted, - const bool use_modulation) - : IRHaierAC176(pin, inverted, use_modulation) { stateReset(); } - -#if SEND_HAIER_AC_YRW02 -/// Send the current internal state as an IR message. -/// @param[in] repeat Nr. of times the message will be repeated. -void IRHaierACYRW02::send(const uint16_t repeat) { - _irsend.sendHaierACYRW02(getRaw(), kHaierACYRW02StateLength, repeat); -} -#endif // SEND_HAIER_AC_YRW02 - -/// Set the internal state from a valid code for this protocol. -/// @param[in] new_code A valid code for this protocol. -void IRHaierACYRW02::setRaw(const uint8_t new_code[]) { - memcpy(_.raw, new_code, kHaierACYRW02StateLength); -} - -/// 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 IRHaierACYRW02::validChecksum(const uint8_t state[], - const uint16_t length) { - return IRHaierAC176::validChecksum(state, length); -} -// End of IRHaierACYRW02 class. - -#if (DECODE_HAIER_AC || DECODE_HAIER_AC_YRW02) -/// Decode the supplied Haier HSU07-HEA03 remote 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::decodeHaierAC(decode_results* results, uint16_t offset, - const uint16_t nbits, const bool strict) { - if (strict) { - if (nbits != kHaierACBits) - return false; // Not strictly a HAIER_AC message. - } - - if (results->rawlen <= (2 * nbits + kHeader) + kFooter - 1 + offset) - return false; // Can't possibly be a valid HAIER_AC message. - - // Pre-Header - if (!matchMark(results->rawbuf[offset++], kHaierAcHdr)) return false; - if (!matchSpace(results->rawbuf[offset++], kHaierAcHdr)) return false; - - // Match Header + Data + Footer - if (!matchGeneric(results->rawbuf + offset, results->state, - results->rawlen - offset, nbits, - kHaierAcHdr, kHaierAcHdrGap, - kHaierAcBitMark, kHaierAcOneSpace, - kHaierAcBitMark, kHaierAcZeroSpace, - kHaierAcBitMark, kHaierAcMinGap, true, - _tolerance, kMarkExcess)) return false; - - // Compliance - if (strict) { - if (results->state[0] != kHaierAcPrefix) return false; - if (!IRHaierAC::validChecksum(results->state, nbits / 8)) return false; - } - - // Success - results->decode_type = HAIER_AC; - results->bits = nbits; - return true; -} -#endif // (DECODE_HAIER_AC || DECODE_HAIER_AC_YRW02) - -#if DECODE_HAIER_AC_YRW02 -/// Decode the supplied Haier YR-W02 remote A/C 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. -bool IRrecv::decodeHaierACYRW02(decode_results* results, uint16_t offset, - const uint16_t nbits, const bool strict) { - if (strict) { - if (nbits != kHaierACYRW02Bits) - return false; // Not strictly a HAIER_AC_YRW02 message. - } - - // The protocol is almost exactly the same as HAIER_AC - if (!decodeHaierAC(results, offset, nbits, false)) return false; - - // Compliance - if (strict) { - if (results->state[0] != kHaierAcYrw02Prefix) return false; - if (!IRHaierACYRW02::validChecksum(results->state, nbits / 8)) return false; - } - - // Success - // It looks correct, but we haven't check the checksum etc. - results->decode_type = HAIER_AC_YRW02; - return true; -} -#endif // DECODE_HAIER_AC_YRW02 - -#if DECODE_HAIER_AC176 -/// Decode the supplied Haier 176 bit remote A/C 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::decodeHaierAC176(decode_results* results, uint16_t offset, - const uint16_t nbits, const bool strict) { - if (strict) { - if (nbits != kHaierAC176Bits) - return false; // Not strictly a HAIER_AC176 message. - } - - // The protocol is almost exactly the same as HAIER_AC - if (!decodeHaierAC(results, offset, nbits, false)) return false; - - // Compliance - if (strict) { - if (results->state[0] != kHaierAcYrw02Prefix) return false; - if (!IRHaierAC176::validChecksum(results->state, nbits / 8)) return false; - } - - // Success - // It looks correct, but we haven't check the checksum etc. - results->decode_type = HAIER_AC176; - return true; -} -#endif // DECODE_HAIER_AC176 diff --git a/lib/IRremoteESP8266/src/ir_Haier.h b/lib/IRremoteESP8266/src/ir_Haier.h index 6cc31b8906..98ba74c3a7 100644 --- a/lib/IRremoteESP8266/src/ir_Haier.h +++ b/lib/IRremoteESP8266/src/ir_Haier.h @@ -22,10 +22,10 @@ #ifndef UNIT_TEST #include #endif -#include "IRremoteESP8266.h" -#include "IRsend.h" +#include "../src/IRremoteESP8266.h" +#include "../src/IRsend.h" #ifdef UNIT_TEST -#include "IRsend_test.h" +#include "../src/IRsend_test.h" #endif /// Native representation of a Haier HSU07-HEA03 A/C message. diff --git a/lib/IRremoteESP8266/src/ir_Hitachi.cpp b/lib/IRremoteESP8266/src/ir_Hitachi.cpp deleted file mode 100644 index 49781997e1..0000000000 --- a/lib/IRremoteESP8266/src/ir_Hitachi.cpp +++ /dev/null @@ -1,1580 +0,0 @@ -// Copyright 2018-2019 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 - -#include "ir_Hitachi.h" -#include -#include -#ifndef ARDUINO -#include -#endif -#include "IRrecv.h" -#include "IRremoteESP8266.h" -#include "IRsend.h" -#include "IRtext.h" -#include "IRutils.h" - -// Constants -const uint16_t kHitachiAcHdrMark = 3300; -const uint16_t kHitachiAcHdrSpace = 1700; -const uint16_t kHitachiAc1HdrMark = 3400; -const uint16_t kHitachiAc1HdrSpace = 3400; -const uint16_t kHitachiAcBitMark = 400; -const uint16_t kHitachiAcOneSpace = 1250; -const uint16_t kHitachiAcZeroSpace = 500; -const uint32_t kHitachiAcMinGap = kDefaultMessageGap; // Just a guess. -// Support for HitachiAc424 protocol -const uint16_t kHitachiAc424LdrMark = 29784; // Leader -const uint16_t kHitachiAc424LdrSpace = 49290; // Leader -const uint16_t kHitachiAc424HdrMark = 3416; // Header -const uint16_t kHitachiAc424HdrSpace = 1604; // Header -const uint16_t kHitachiAc424BitMark = 463; -const uint16_t kHitachiAc424OneSpace = 1208; -const uint16_t kHitachiAc424ZeroSpace = 372; - -// Support for HitachiAc3 protocol -const uint16_t kHitachiAc3HdrMark = 3400; // Header -const uint16_t kHitachiAc3HdrSpace = 1660; // Header -const uint16_t kHitachiAc3BitMark = 460; -const uint16_t kHitachiAc3OneSpace = 1250; -const uint16_t kHitachiAc3ZeroSpace = 410; - -using irutils::addBoolToString; -using irutils::addIntToString; -using irutils::addLabeledString; -using irutils::addModeToString; -using irutils::addModelToString; -using irutils::addFanToString; -using irutils::addTempToString; -using irutils::checkInvertedBytePairs; -using irutils::invertBytePairs; -using irutils::minsToString; - -#if (SEND_HITACHI_AC || SEND_HITACHI_AC2 || SEND_HITACHI_AC344) -/// Send a Hitachi 28-byte/224-bit A/C formatted message. (HITACHI_AC) -/// 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. -/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/417 -void IRsend::sendHitachiAC(const unsigned char data[], const uint16_t nbytes, - const uint16_t repeat) { - if (nbytes < kHitachiAcStateLength) - return; // Not enough bytes to send a proper message. - - const bool MSBfirst = (nbytes == kHitachiAc344StateLength) ? false : true; - sendGeneric(kHitachiAcHdrMark, kHitachiAcHdrSpace, kHitachiAcBitMark, - kHitachiAcOneSpace, kHitachiAcBitMark, kHitachiAcZeroSpace, - kHitachiAcBitMark, kHitachiAcMinGap, data, nbytes, 38, MSBfirst, - repeat, 50); -} -#endif // (SEND_HITACHI_AC || SEND_HITACHI_AC2 || SEND_HITACHI_AC344) - -#if SEND_HITACHI_AC1 -/// Send a Hitachi 13 byte/224-bit A/C formatted message. (HITACHI_AC1) -/// Status: STABLE / Confirmed 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. -/// @note Basically the same as sendHitatchiAC() except different size & header. -/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/453 -void IRsend::sendHitachiAC1(const unsigned char data[], const uint16_t nbytes, - const uint16_t repeat) { - if (nbytes < kHitachiAc1StateLength) - return; // Not enough bytes to send a proper message. - sendGeneric(kHitachiAc1HdrMark, kHitachiAc1HdrSpace, kHitachiAcBitMark, - kHitachiAcOneSpace, kHitachiAcBitMark, kHitachiAcZeroSpace, - kHitachiAcBitMark, kHitachiAcMinGap, data, nbytes, kHitachiAcFreq, - true, repeat, kDutyDefault); -} -#endif // SEND_HITACHI_AC1 - -#if SEND_HITACHI_AC2 -/// Send a Hitachi 53 byte/424-bit A/C formatted message. (HITACHI_AC2) -/// Basically the same as sendHitatchiAC() except different size. -/// Status: STABLE / Expected to work. -/// @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::sendHitachiAC2(const unsigned char data[], const uint16_t nbytes, - const uint16_t repeat) { - if (nbytes < kHitachiAc2StateLength) - return; // Not enough bytes to send a proper message. - sendHitachiAC(data, nbytes, repeat); -} -#endif // SEND_HITACHI_AC2 - -#if SEND_HITACHI_AC344 -/// Send a Hitachi A/C 43-byte/344-bit message. (HITACHI_AC344) -/// Basically the same as sendHitatchiAC() except different size. -/// Status: Beta / Probably works. -/// @param[in] data An array of bytes containing the IR command. -/// @param[in] nbytes Nr. of bytes of data in the array. -/// @param[in] repeat Nr. of times the message is to be repeated. (Default = 0). -/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1134 -void IRsend::sendHitachiAc344(const unsigned char data[], const uint16_t nbytes, - const uint16_t repeat) { - if (nbytes < kHitachiAc344StateLength) - return; // Not enough bytes to send a proper message. - sendHitachiAC(data, nbytes, repeat); -} -#endif // SEND_HITACHI_AC344 - -/// 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? -IRHitachiAc::IRHitachiAc(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 IRHitachiAc::stateReset(void) { - _.raw[0] = 0x80; - _.raw[1] = 0x08; - _.raw[2] = 0x0C; - _.raw[3] = 0x02; - _.raw[4] = 0xFD; - _.raw[5] = 0x80; - _.raw[6] = 0x7F; - _.raw[7] = 0x88; - _.raw[8] = 0x48; - _.raw[9] = 0x10; - for (uint8_t i = 10; i < kHitachiAcStateLength; i++) _.raw[i] = 0x00; - _.raw[14] = 0x60; - _.raw[15] = 0x60; - _.raw[24] = 0x80; - setTemp(23); -} - -/// Set up hardware to be able to send a message. -void IRHitachiAc::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 size/length of the state. -/// @return The calculated checksum value. -uint8_t IRHitachiAc::calcChecksum(const uint8_t state[], - const uint16_t length) { - uint8_t sum = 62; - for (uint16_t i = 0; i < length - 1; i++) sum -= reverseBits(state[i], 8); - return reverseBits(sum, 8); -} - -/// Calculate and set the checksum values for the internal state. -/// @param[in] length The size/length of the state. -void IRHitachiAc::checksum(const uint16_t length) { - _.Sum = calcChecksum(_.raw, 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 IRHitachiAc::validChecksum(const uint8_t state[], const uint16_t length) { - if (length < 2) return true; // Assume true for lengths that are too short. - return (state[length - 1] == calcChecksum(state, length)); -} - -/// 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 *IRHitachiAc::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 new_code array. -void IRHitachiAc::setRaw(const uint8_t new_code[], const uint16_t length) { - std::memcpy(_.raw, new_code, std::min(length, kHitachiAcStateLength)); -} - -#if SEND_HITACHI_AC -/// Send the current internal state as an IR message. -/// @param[in] repeat Nr. of times the message will be repeated. -void IRHitachiAc::send(const uint16_t repeat) { - _irsend.sendHitachiAC(getRaw(), kHitachiAcStateLength, repeat); -} -#endif // SEND_HITACHI_AC - -/// Get the value of the current power setting. -/// @return true, the setting is on. false, the setting is off. -bool IRHitachiAc::getPower(void) const { - return _.Power; -} - -/// Change the power setting. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRHitachiAc::setPower(const bool on) { - _.Power = on; -} - -/// Change the power setting to On. -void IRHitachiAc::on(void) { setPower(true); } - -/// Change the power setting to Off. -void IRHitachiAc::off(void) { setPower(false); } - -/// Get the operating mode setting of the A/C. -/// @return The current operating mode setting. -uint8_t IRHitachiAc::getMode(void) const { return reverseBits(_.Mode, 8); } - -/// Set the operating mode of the A/C. -/// @param[in] mode The desired operating mode. -void IRHitachiAc::setMode(const uint8_t mode) { - uint8_t newmode = mode; - switch (mode) { - // Fan mode sets a special temp. - case kHitachiAcFan: setTemp(64); break; - case kHitachiAcAuto: - case kHitachiAcHeat: - case kHitachiAcCool: - case kHitachiAcDry: break; - default: newmode = kHitachiAcAuto; - } - _.Mode = reverseBits(newmode, 8); - if (mode != kHitachiAcFan) setTemp(_previoustemp); - setFan(getFan()); // Reset the fan speed after the mode change. -} - -/// Get the current temperature setting. -/// @return The current setting for temp. in degrees celsius. -uint8_t IRHitachiAc::getTemp(void) const { - return reverseBits(_.Temp, 8) >> 1; -} - -/// Set the temperature. -/// @param[in] celsius The temperature in degrees celsius. -void IRHitachiAc::setTemp(const uint8_t celsius) { - uint8_t temp; - if (celsius != 64) _previoustemp = celsius; - switch (celsius) { - case 64: - temp = celsius; - break; - default: - temp = std::min(celsius, kHitachiAcMaxTemp); - temp = std::max(temp, kHitachiAcMinTemp); - } - _.Temp = reverseBits(temp << 1, 8); - if (temp == kHitachiAcMinTemp) - _.raw[9] = 0x90; - else - _.raw[9] = 0x10; -} - -/// Get the current fan speed setting. -/// @return The current fan speed. -uint8_t IRHitachiAc::getFan(void) const { return reverseBits(_.Fan, 8); } - -/// Set the speed of the fan. -/// @param[in] speed The desired setting. -void IRHitachiAc::setFan(const uint8_t speed) { - uint8_t fanmin = kHitachiAcFanAuto; - uint8_t fanmax = kHitachiAcFanHigh; - switch (getMode()) { - case kHitachiAcDry: // Only 2 x low speeds in Dry mode. - fanmin = kHitachiAcFanLow; - fanmax = kHitachiAcFanLow + 1; - break; - case kHitachiAcFan: - fanmin = kHitachiAcFanLow; // No Auto in Fan mode. - break; - } - uint8_t newspeed = std::max(speed, fanmin); - newspeed = std::min(newspeed, fanmax); - _.Fan = reverseBits(newspeed, 8); -} - -/// Get the Vertical Swing setting of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRHitachiAc::getSwingVertical(void) const { - return _.SwingV; -} - -/// Set the Vertical Swing setting of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRHitachiAc::setSwingVertical(const bool on) { - _.SwingV = on; -} - -/// Get the Horizontal Swing setting of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRHitachiAc::getSwingHorizontal(void) const { - return _.SwingH; -} - -/// Set the Horizontal Swing setting of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRHitachiAc::setSwingHorizontal(const bool on) { - _.SwingH = on; -} - -/// 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 IRHitachiAc::convertMode(const stdAc::opmode_t mode) { - switch (mode) { - case stdAc::opmode_t::kCool: return kHitachiAcCool; - case stdAc::opmode_t::kHeat: return kHitachiAcHeat; - case stdAc::opmode_t::kDry: return kHitachiAcDry; - case stdAc::opmode_t::kFan: return kHitachiAcFan; - default: return kHitachiAcAuto; - } -} - -/// 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 IRHitachiAc::convertFan(const stdAc::fanspeed_t speed) { - switch (speed) { - case stdAc::fanspeed_t::kMin: - case stdAc::fanspeed_t::kLow: return kHitachiAcFanLow; - case stdAc::fanspeed_t::kMedium: return kHitachiAcFanLow + 1; - case stdAc::fanspeed_t::kHigh: return kHitachiAcFanHigh - 1; - case stdAc::fanspeed_t::kMax: return kHitachiAcFanHigh; - default: return kHitachiAcFanAuto; - } -} - -/// 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 IRHitachiAc::toCommonMode(const uint8_t mode) { - switch (mode) { - case kHitachiAcCool: return stdAc::opmode_t::kCool; - case kHitachiAcHeat: return stdAc::opmode_t::kHeat; - case kHitachiAcDry: return stdAc::opmode_t::kDry; - case kHitachiAcFan: 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 IRHitachiAc::toCommonFanSpeed(const uint8_t speed) { - switch (speed) { - case kHitachiAcFanHigh: return stdAc::fanspeed_t::kMax; - case kHitachiAcFanHigh - 1: return stdAc::fanspeed_t::kHigh; - case kHitachiAcFanLow + 1: return stdAc::fanspeed_t::kMedium; - case kHitachiAcFanLow: return stdAc::fanspeed_t::kLow; - 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 IRHitachiAc::toCommon(void) const { - stdAc::state_t result; - result.protocol = decode_type_t::HITACHI_AC; - result.model = -1; // No models used. - result.power = _.Power; - result.mode = toCommonMode(getMode()); - result.celsius = true; - result.degrees = getTemp(); - result.fanspeed = toCommonFanSpeed(getFan()); - result.swingv = (_.SwingV ? stdAc::swingv_t::kAuto : stdAc::swingv_t::kOff); - result.swingh = (_.SwingH ? stdAc::swingh_t::kAuto : stdAc::swingh_t::kOff); - // Not supported. - result.quiet = false; - result.turbo = false; - result.clean = false; - result.econo = false; - result.filter = false; - result.light = 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 IRHitachiAc::toString(void) const { - String result = ""; - result.reserve(110); // Reserve some heap for the string to reduce fragging. - result += addBoolToString(_.Power, kPowerStr, false); - result += addModeToString(getMode(), kHitachiAcAuto, kHitachiAcCool, - kHitachiAcHeat, kHitachiAcDry, kHitachiAcFan); - result += addTempToString(getTemp()); - result += addFanToString(getFan(), kHitachiAcFanHigh, kHitachiAcFanLow, - kHitachiAcFanAuto, kHitachiAcFanAuto, - kHitachiAcFanMed); - result += addBoolToString(_.SwingV, kSwingVStr); - result += addBoolToString(_.SwingH, kSwingHStr); - return result; -} - -/// 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? -IRHitachiAc1::IRHitachiAc1(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 IRHitachiAc1::stateReset(void) { - for (uint8_t i = 0; i < kHitachiAc1StateLength; i++) _.raw[i] = 0x00; - // Copy in a known good state. - _.raw[0] = 0xB2; - _.raw[1] = 0xAE; - _.raw[2] = 0x4D; - _.raw[3] = 0x91; - _.raw[4] = 0xF0; - _.raw[5] = 0xE1; - _.raw[6] = 0xA4; - _.raw[11] = 0x61; - _.raw[12] = 0x24; -} - -/// Set up hardware to be able to send a message. -void IRHitachiAc1::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 size/length of the state. -/// @return The calculated checksum value. -uint8_t IRHitachiAc1::calcChecksum(const uint8_t state[], - const uint16_t length) { - uint8_t sum = 0; - for (uint16_t i = kHitachiAc1ChecksumStartByte; i < length - 1; i++) { - sum += reverseBits(GETBITS8(state[i], kLowNibble, kNibbleSize), - kNibbleSize); - sum += reverseBits(GETBITS8(state[i], kHighNibble, kNibbleSize), - kNibbleSize); - } - return reverseBits(sum, 8); -} - -/// Calculate and set the checksum values for the internal state. -/// @param[in] length The size/length of the state. -void IRHitachiAc1::checksum(const uint16_t length) { - _.Sum = calcChecksum(_.raw, 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 IRHitachiAc1::validChecksum(const uint8_t state[], const uint16_t length) { - if (length < 2) return true; // Assume true for lengths that are too short. - return (state[length - 1] == calcChecksum(state, length)); -} - -/// 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 *IRHitachiAc1::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 new_code array. -void IRHitachiAc1::setRaw(const uint8_t new_code[], const uint16_t length) { - std::memcpy(_.raw, new_code, std::min(length, kHitachiAc1StateLength)); -} - -#if SEND_HITACHI_AC -/// Send the current internal state as an IR message. -/// @param[in] repeat Nr. of times the message will be repeated. -void IRHitachiAc1::send(const uint16_t repeat) { - _irsend.sendHitachiAC1(getRaw(), kHitachiAc1StateLength, repeat); - // Clear the toggle bits as we have actioned them by sending them. - setPowerToggle(false); - setSwingToggle(false); -} -#endif // SEND_HITACHI_AC - -/// Get/Detect the model of the A/C. -/// @return The enum of the compatible model. -hitachi_ac1_remote_model_t IRHitachiAc1::getModel(void) const { - switch (_.Model) { - case kHitachiAc1Model_B: return hitachi_ac1_remote_model_t::R_LT0541_HTA_B; - default: return hitachi_ac1_remote_model_t::R_LT0541_HTA_A; - } -} - -/// Set the model of the A/C to emulate. -/// @param[in] model The enum of the appropriate model. -void IRHitachiAc1::setModel(const hitachi_ac1_remote_model_t model) { - uint8_t value = 0; - switch (model) { - case hitachi_ac1_remote_model_t::R_LT0541_HTA_B: - value = kHitachiAc1Model_B; - break; - default: - value = kHitachiAc1Model_A; // i.e. 'A' mode. - } - _.Model = value; -} - -/// Get the value of the current power setting. -/// @return true, the setting is on. false, the setting is off. -bool IRHitachiAc1::getPower(void) const { - return _.Power; -} - -/// Change the power setting. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRHitachiAc1::setPower(const bool on) { - // If the power changes, set the power toggle bit. - if (on != _.Power) setPowerToggle(true); - _.Power = on; -} - -/// Get the value of the current power toggle setting. -/// @return true, the setting is on. false, the setting is off. -bool IRHitachiAc1::getPowerToggle(void) const { - return _.PowerToggle; -} - -/// Change the power toggle setting. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRHitachiAc1::setPowerToggle(const bool on) { - _.PowerToggle = on; -} - -/// Change the power setting to On. -void IRHitachiAc1::on(void) { setPower(true); } - -/// Change the power setting to Off. -void IRHitachiAc1::off(void) { setPower(false); } - -/// Get the operating mode setting of the A/C. -/// @return The current operating mode setting. -uint8_t IRHitachiAc1::getMode(void) const { - return _.Mode; -} - -/// Set the operating mode of the A/C. -/// @param[in] mode The desired operating mode. -void IRHitachiAc1::setMode(const uint8_t mode) { - switch (mode) { - case kHitachiAc1Auto: - setTemp(kHitachiAc1TempAuto); - // FALL THRU - case kHitachiAc1Fan: - case kHitachiAc1Heat: - case kHitachiAc1Cool: - case kHitachiAc1Dry: - _.Mode = mode; - break; - default: - setTemp(kHitachiAc1TempAuto); - _.Mode = kHitachiAc1Auto; - break; - } - setSleep(_.Sleep); // Correct the sleep mode if required. - setFan(_.Fan); // Correct the fan speed if required. -} - -/// Get the current temperature setting. -/// @return The current setting for temp. in degrees celsius. -uint8_t IRHitachiAc1::getTemp(void) const { - return reverseBits(_.Temp, kHitachiAc1TempSize) + kHitachiAc1TempDelta; -} - -/// Set the temperature. -/// @param[in] celsius The temperature in degrees celsius. -void IRHitachiAc1::setTemp(const uint8_t celsius) { - if (_.Mode == kHitachiAc1Auto) return; // Can't change temp in Auto mode. - uint8_t temp = std::min(celsius, kHitachiAcMaxTemp); - temp = std::max(temp, kHitachiAcMinTemp); - temp -= kHitachiAc1TempDelta; - temp = reverseBits(temp, kHitachiAc1TempSize); - _.Temp = temp; -} - -/// Get the current fan speed setting. -/// @return The current fan speed. -uint8_t IRHitachiAc1::getFan(void) const { - return _.Fan; -} - -/// Set the speed of the fan. -/// @param[in] speed The desired setting. -/// @param[in] force Deprecated -void IRHitachiAc1::setFan(const uint8_t speed, const bool /*force*/) { - // restrictions - switch (_.Mode) { - case kHitachiAc1Dry: - _.Fan = kHitachiAc1FanLow; // Dry is locked to Low speed. - return; - case kHitachiAc1Auto: - _.Fan = kHitachiAc1FanAuto; // Auto is locked to Auto speed. - return; - case kHitachiAc1Heat: - case kHitachiAc1Fan: // Auto speed not allowed in these modes. - if (speed == kHitachiAc1FanAuto || _.Fan == kHitachiAc1FanAuto) - _.Fan = kHitachiAc1FanLow; - return; - } - - switch (speed) { - case kHitachiAc1FanAuto: - case kHitachiAc1FanHigh: - case kHitachiAc1FanMed: - case kHitachiAc1FanLow: - _.Fan = speed; - break; - default: _.Fan = kHitachiAc1FanAuto; - } -} - -/// Get the Swing Toggle setting of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRHitachiAc1::getSwingToggle(void) const { - return _.SwingToggle; -} - -/// Set the Swing toggle setting of the A/C. -/// @param[in] toggle true, the setting is on. false, the setting is off. -void IRHitachiAc1::setSwingToggle(const bool toggle) { - _.SwingToggle = toggle; -} - -/// Get the Vertical Swing setting of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRHitachiAc1::getSwingV(void) const { - return _.SwingV; -} - -/// Set the Vertical Swing setting of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRHitachiAc1::setSwingV(const bool on) { - _.SwingV = on; -} - -/// Get the Horizontal Swing setting of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRHitachiAc1::getSwingH(void) const { - return _.SwingH; -} - -/// Set the Horizontal Swing setting of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRHitachiAc1::setSwingH(const bool on) { - _.SwingH = on; -} - -/// Get the Sleep setting of the A/C. -/// @return The currently configured sleep mode. -/// @note Sleep modes only available in Auto & Cool modes, otherwise it's off. -uint8_t IRHitachiAc1::getSleep(void) const { - return _.Sleep; -} - -/// Set the Sleep setting of the A/C. -/// @param[in] mode The mode of sleep to set the A/C to. -/// @note Sleep modes only available in Auto & Cool modes, otherwise it's off. -void IRHitachiAc1::setSleep(const uint8_t mode) { - switch (_.Mode) { - case kHitachiAc1Auto: - case kHitachiAc1Cool: - _.Sleep = std::min(mode, kHitachiAc1Sleep4); - break; - default: - _.Sleep = kHitachiAc1SleepOff; - } -} - -/// Set the On Timer time. -/// @param[in] mins The time expressed in total number of minutes. -void IRHitachiAc1::setOnTimer(const uint16_t mins) { - const uint16_t mins_lsb = reverseBits(mins, kHitachiAc1TimerSize); - _.OnTimerLow = GETBITS16(mins_lsb, 8, 8); - _.OnTimerHigh = GETBITS16(mins_lsb, 0, 8); -} - -/// Get the On Timer vtime of the A/C. -/// @return Nr of minutes the timer is set to. -uint16_t IRHitachiAc1::getOnTimer(void) const { - return reverseBits( - (_.OnTimerLow << 8) | _.OnTimerHigh, kHitachiAc1TimerSize); -} - -/// Set the Off Timer time. -/// @param[in] mins The time expressed in total number of minutes. -void IRHitachiAc1::setOffTimer(const uint16_t mins) { - const uint16_t mins_lsb = reverseBits(mins, kHitachiAc1TimerSize); - _.OffTimerLow = GETBITS16(mins_lsb, 8, 8); - _.OffTimerHigh = GETBITS16(mins_lsb, 0, 8); -} - -/// Get the Off Timer vtime of the A/C. -/// @return Nr of minutes the timer is set to. -uint16_t IRHitachiAc1::getOffTimer(void) const { - return reverseBits( - (_.OffTimerLow << 8) | _.OffTimerHigh, kHitachiAc1TimerSize); -} - -/// 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 IRHitachiAc1::convertMode(const stdAc::opmode_t mode) { - switch (mode) { - case stdAc::opmode_t::kCool: return kHitachiAc1Cool; - case stdAc::opmode_t::kHeat: return kHitachiAc1Heat; - case stdAc::opmode_t::kDry: return kHitachiAc1Dry; - case stdAc::opmode_t::kFan: return kHitachiAc1Fan; - default: return kHitachiAc1Auto; - } -} - -/// 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 IRHitachiAc1::convertFan(const stdAc::fanspeed_t speed) { - switch (speed) { - case stdAc::fanspeed_t::kMin: - case stdAc::fanspeed_t::kLow: return kHitachiAc1FanLow; - case stdAc::fanspeed_t::kMedium: return kHitachiAc1FanMed; - case stdAc::fanspeed_t::kHigh: - case stdAc::fanspeed_t::kMax: return kHitachiAc1FanHigh; - default: return kHitachiAc1FanAuto; - } -} - -/// 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 IRHitachiAc1::toCommonMode(const uint8_t mode) { - switch (mode) { - case kHitachiAc1Cool: return stdAc::opmode_t::kCool; - case kHitachiAc1Heat: return stdAc::opmode_t::kHeat; - case kHitachiAc1Dry: return stdAc::opmode_t::kDry; - case kHitachiAc1Fan: 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 IRHitachiAc1::toCommonFanSpeed(const uint8_t speed) { - switch (speed) { - case kHitachiAc1FanHigh: return stdAc::fanspeed_t::kMax; - case kHitachiAc1FanMed: return stdAc::fanspeed_t::kMedium; - case kHitachiAc1FanLow: return stdAc::fanspeed_t::kLow; - 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 IRHitachiAc1::toCommon(void) const { - stdAc::state_t result; - result.protocol = decode_type_t::HITACHI_AC1; - result.model = getModel(); - 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.sleep = _.Sleep ? 0 : -1; - // Not supported. - result.quiet = false; - result.turbo = false; - result.clean = false; - result.econo = false; - result.filter = false; - result.light = 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 IRHitachiAc1::toString(void) const { - String result = ""; - result.reserve(170); // Reserve some heap for the string to reduce fragging. - result += addModelToString(decode_type_t::HITACHI_AC1, getModel(), false); - result += addBoolToString(_.Power, kPowerStr); - result += addBoolToString(_.PowerToggle, kPowerToggleStr); - result += addModeToString(_.Mode, kHitachiAc1Auto, kHitachiAc1Cool, - kHitachiAc1Heat, kHitachiAc1Dry, kHitachiAc1Fan); - result += addTempToString(getTemp()); - result += addFanToString(_.Fan, kHitachiAc1FanHigh, kHitachiAc1FanLow, - kHitachiAc1FanAuto, kHitachiAc1FanAuto, - kHitachiAc1FanMed); - result += addBoolToString(_.SwingToggle, kSwingVToggleStr); - result += addBoolToString(_.SwingV, kSwingVStr); - result += addBoolToString(_.SwingH, kSwingHStr); - result += addLabeledString(_.Sleep ? uint64ToString(_.Sleep) : kOffStr, - kSleepStr); - result += addLabeledString(getOnTimer() ? minsToString(getOnTimer()) - : kOffStr, - kOnTimerStr); - result += addLabeledString(getOffTimer() ? minsToString(getOffTimer()) - : kOffStr, - kOffTimerStr); - return result; -} - -#if (DECODE_HITACHI_AC || DECODE_HITACHI_AC1 || DECODE_HITACHI_AC2 || \ - DECODE_HITACHI_AC344) -/// Decode the supplied Hitachi A/C message. -/// Status: STABLE / Expected 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. -/// Typically kHitachiAcBits, kHitachiAc1Bits, kHitachiAc2Bits, -/// kHitachiAc344Bits -/// @param[in] strict Flag indicating if we should perform strict matching. -/// @param[in] MSBfirst Is the data per byte stored in MSB First (true) or -/// LSB First order(false)? -/// @return True if it can decode it, false if it can't. -/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/417 -/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/453 -/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1134 -bool IRrecv::decodeHitachiAC(decode_results *results, uint16_t offset, - const uint16_t nbits, const bool strict, - const bool MSBfirst) { - const uint8_t k_tolerance = _tolerance + 5; - - if (strict) { - switch (nbits) { - case kHitachiAcBits: - case kHitachiAc1Bits: - case kHitachiAc2Bits: - case kHitachiAc344Bits: - break; // Okay to continue. - default: - return false; // Not strictly a Hitachi message. - } - } - uint16_t hmark; - uint32_t hspace; - if (nbits == kHitachiAc1Bits) { - hmark = kHitachiAc1HdrMark; - hspace = kHitachiAc1HdrSpace; - } else { - hmark = kHitachiAcHdrMark; - hspace = kHitachiAcHdrSpace; - } - // Match Header + Data + Footer - if (!matchGeneric(results->rawbuf + offset, results->state, - results->rawlen - offset, nbits, - hmark, hspace, - kHitachiAcBitMark, kHitachiAcOneSpace, - kHitachiAcBitMark, kHitachiAcZeroSpace, - kHitachiAcBitMark, kHitachiAcMinGap, true, - k_tolerance, kMarkExcess, MSBfirst)) return false; - - // Compliance - if (strict) { - if (nbits / 8 == kHitachiAcStateLength && - !IRHitachiAc::validChecksum(results->state, kHitachiAcStateLength)) - return false; - if (nbits / 8 == kHitachiAc1StateLength && - !IRHitachiAc1::validChecksum(results->state, kHitachiAc1StateLength)) - return false; - if (nbits / 8 == kHitachiAc344StateLength && - !IRHitachiAc3::hasInvertedStates(results->state, - kHitachiAc344StateLength)) - return false; - } - - // Success - switch (nbits) { - case kHitachiAc1Bits: - results->decode_type = decode_type_t::HITACHI_AC1; - break; - case kHitachiAc2Bits: - results->decode_type = decode_type_t::HITACHI_AC2; - break; - case kHitachiAc344Bits: - results->decode_type = decode_type_t::HITACHI_AC344; - break; - case kHitachiAcBits: - default: - results->decode_type = decode_type_t::HITACHI_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_HITACHI_AC || DECODE_HITACHI_AC1 || DECODE_HITACHI_AC2 || - // DECODE_HITACHI_AC344) - -#if SEND_HITACHI_AC424 -/// Send a Hitachi 53-byte/424-bit A/C formatted message. (HITACHI_AC424) -/// 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. -/// @note This protocol is almost exactly the same as HitachiAC2 except this -/// variant has a leader section as well, and subtle timing differences. -/// It is also in LSBF order (per byte), rather than MSBF order. -void IRsend::sendHitachiAc424(const uint8_t data[], const uint16_t nbytes, - const uint16_t repeat) { - enableIROut(kHitachiAcFreq); - for (uint16_t r = 0; r <= repeat; r++) { - // Leader - mark(kHitachiAc424LdrMark); - space(kHitachiAc424LdrSpace); - // Header + Data + Footer - sendGeneric(kHitachiAc424HdrMark, kHitachiAc424HdrSpace, - kHitachiAc424BitMark, kHitachiAc424OneSpace, - kHitachiAc424BitMark, kHitachiAc424ZeroSpace, - kHitachiAc424BitMark, kHitachiAcMinGap, - data, nbytes, // Bytes - kHitachiAcFreq, false, kNoRepeat, kDutyDefault); - } -} -#endif // SEND_HITACHI_AC424 - -#if DECODE_HITACHI_AC424 -/// Decode the supplied Hitachi 53-byte/424-bit A/C message. -/// Status: STABLE / Reported as 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 This protocol is almost exactly the same as HitachiAC2 except this -/// variant has a leader section as well, and subtle timing differences. -/// It is also in LSBF order (per byte), rather than MSBF order. -/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/973 -/// @see (Japanese Manual) https://kadenfan.hitachi.co.jp/support/raj/item/docs/ras_aj22h_a_tori.pdf -bool IRrecv::decodeHitachiAc424(decode_results *results, uint16_t offset, - const uint16_t nbits, - const bool strict) { - if (results->rawlen < 2 * nbits + kHeader + kHeader + kFooter - 1 + offset) - return false; // Too short a message to match. - if (strict && nbits != kHitachiAc424Bits) - return false; - - uint16_t used; - - // Leader - if (!matchMark(results->rawbuf[offset++], kHitachiAc424LdrMark)) - return false; - if (!matchSpace(results->rawbuf[offset++], kHitachiAc424LdrSpace)) - return false; - - // Header + Data + Footer - used = matchGeneric(results->rawbuf + offset, results->state, - results->rawlen - offset, nbits, - kHitachiAc424HdrMark, kHitachiAc424HdrSpace, - kHitachiAc424BitMark, kHitachiAc424OneSpace, - kHitachiAc424BitMark, kHitachiAc424ZeroSpace, - kHitachiAc424BitMark, kHitachiAcMinGap, true, - kUseDefTol, 0, false); - if (used == 0) return false; // We failed to find any data. - - // Success - results->decode_type = decode_type_t::HITACHI_AC424; - results->bits = nbits; - return true; -} -#endif // DECODE_HITACHI_AC424 - -/// 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? -IRHitachiAc424::IRHitachiAc424(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 Reset to auto fan, cooling, 23° Celsius -void IRHitachiAc424::stateReset(void) { - for (uint8_t i = 0; i < kHitachiAc424StateLength; i++) - _.raw[i] = 0x00; - - _.raw[0] = 0x01; - _.raw[1] = 0x10; - _.raw[3] = 0x40; - _.raw[5] = 0xFF; - _.raw[7] = 0xCC; - _.raw[33] = 0x80; - _.raw[35] = 0x03; - _.raw[37] = 0x01; - _.raw[39] = 0x88; - _.raw[45] = 0xFF; - _.raw[47] = 0xFF; - _.raw[49] = 0xFF; - _.raw[51] = 0xFF; - - setTemp(23); - setPower(true); - setMode(kHitachiAc424Cool); - setFan(kHitachiAc424FanAuto); -} - -/// Update the internal consistency check for the protocol. -void IRHitachiAc424::setInvertedStates(void) { - invertBytePairs(_.raw + 3, kHitachiAc424StateLength - 3); -} - -/// Set up hardware to be able to send a message. -void IRHitachiAc424::begin(void) { _irsend.begin(); } - -/// 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 *IRHitachiAc424::getRaw(void) { - setInvertedStates(); - 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 new_code array. -void IRHitachiAc424::setRaw(const uint8_t new_code[], const uint16_t length) { - std::memcpy(_.raw, new_code, std::min(length, kHitachiAc424StateLength)); -} - -#if SEND_HITACHI_AC424 -/// Send the current internal state as an IR message. -/// @param[in] repeat Nr. of times the message will be repeated. -void IRHitachiAc424::send(const uint16_t repeat) { - _irsend.sendHitachiAc424(getRaw(), kHitachiAc424StateLength, repeat); -} -#endif // SEND_HITACHI_AC424 - -/// Get the value of the current power setting. -/// @return true, the setting is on. false, the setting is off. -bool IRHitachiAc424::getPower(void) const { - return _.Power == kHitachiAc424PowerOn; -} - -/// Change the power setting. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRHitachiAc424::setPower(const bool on) { - setButton(kHitachiAc424ButtonPowerMode); - _.Power = (on ? kHitachiAc424PowerOn : kHitachiAc424PowerOff); -} - -/// Change the power setting to On. -void IRHitachiAc424::on(void) { setPower(true); } - -/// Change the power setting to Off. -void IRHitachiAc424::off(void) { setPower(false); } - -/// Get the operating mode setting of the A/C. -/// @return The current operating mode setting. -uint8_t IRHitachiAc424::getMode(void) const { - return _.Mode; -} - -/// Set the operating mode of the A/C. -/// @param[in] mode The desired operating mode. -void IRHitachiAc424::setMode(const uint8_t mode) { - uint8_t newMode = mode; - switch (mode) { - // Fan mode sets a special temp. - case kHitachiAc424Fan: setTemp(kHitachiAc424FanTemp, false); break; - case kHitachiAc424Heat: - case kHitachiAc424Cool: - case kHitachiAc424Dry: break; - default: newMode = kHitachiAc424Cool; - } - _.Mode = newMode; - if (newMode != kHitachiAc424Fan) setTemp(_previoustemp); - setFan(_.Fan); // Reset the fan speed after the mode change. - setButton(kHitachiAc424ButtonPowerMode); -} - -/// Get the current temperature setting. -/// @return The current setting for temp. in degrees celsius. -uint8_t IRHitachiAc424::getTemp(void) const { - return _.Temp; -} - -/// Set the temperature. -/// @param[in] celsius The temperature in degrees celsius. -/// @param[in] setPrevious true, remember this if we change mode. false, don't. -void IRHitachiAc424::setTemp(const uint8_t celsius, bool setPrevious) { - uint8_t temp; - temp = std::min(celsius, kHitachiAc424MaxTemp); - temp = std::max(temp, kHitachiAc424MinTemp); - _.Temp = temp; - if (_previoustemp > temp) - setButton(kHitachiAc424ButtonTempDown); - else if (_previoustemp < temp) - setButton(kHitachiAc424ButtonTempUp); - if (setPrevious) _previoustemp = temp; -} - -/// Get the current fan speed setting. -/// @return The current fan speed. -uint8_t IRHitachiAc424::getFan(void) const { - return _.Fan; -} - -/// Set the speed of the fan. -/// @param[in] speed The desired setting. -void IRHitachiAc424::setFan(const uint8_t speed) { - uint8_t newSpeed = std::max(speed, kHitachiAc424FanMin); - uint8_t fanMax = kHitachiAc424FanMax; - - // Only 2 x low speeds in Dry mode or Auto - if (_.Mode == kHitachiAc424Dry && speed == kHitachiAc424FanAuto) { - fanMax = kHitachiAc424FanAuto; - } else if (_.Mode == kHitachiAc424Dry) { - fanMax = kHitachiAc424FanMaxDry; - } else if (_.Mode == kHitachiAc424Fan && speed == kHitachiAc424FanAuto) { - // Fan Mode does not have auto. Set to safe low - newSpeed = kHitachiAc424FanMin; - } - - newSpeed = std::min(newSpeed, fanMax); - // Handle the setting the button value if we are going to change the value. - if (newSpeed != _.Fan) setButton(kHitachiAc424ButtonFan); - // Set the values - _.Fan = newSpeed; - _.raw[9] = 0x92; - _.raw[29] = 0x00; - - // When fan is at min/max, additional bytes seem to be set - if (newSpeed == kHitachiAc424FanMin) _.raw[9] = 0x98; - if (newSpeed == kHitachiAc424FanMax) { - _.raw[9] = 0xA9; - _.raw[29] = 0x30; - } -} - -/// Get the Button/Command setting of the A/C. -/// @return The value of the button/command that was pressed. -uint8_t IRHitachiAc424::getButton(void) const { - return _.Button; -} - -/// Set the Button/Command pressed setting of the A/C. -/// @param[in] button The value of the button/command that was pressed. -void IRHitachiAc424::setButton(const uint8_t button) { - _.Button = button; -} - -/// Set the Vertical Swing toggle setting of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -/// @note The remote does not keep state of the vertical swing. -/// A byte is sent indicating the swing button is pressed on the remote -void IRHitachiAc424::setSwingVToggle(const bool on) { - uint8_t button = _.Button; // Get the current button value. - if (on) - button = kHitachiAc424ButtonSwingV; // Set the button to SwingV. - else if (button == kHitachiAc424ButtonSwingV) // Asked to unset it - // It was set previous, so use Power as a default - button = kHitachiAc424ButtonPowerMode; - setButton(button); -} - -/// Get the Vertical Swing toggle setting of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRHitachiAc424::getSwingVToggle(void) const { - return _.Button == kHitachiAc424ButtonSwingV; -} - -/// 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 IRHitachiAc424::convertMode(const stdAc::opmode_t mode) { - switch (mode) { - case stdAc::opmode_t::kCool: return kHitachiAc424Cool; - case stdAc::opmode_t::kHeat: return kHitachiAc424Heat; - case stdAc::opmode_t::kDry: return kHitachiAc424Dry; - case stdAc::opmode_t::kFan: return kHitachiAc424Fan; - default: return kHitachiAc424Cool; - } -} - -/// 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 IRHitachiAc424::convertFan(const stdAc::fanspeed_t speed) { - switch (speed) { - case stdAc::fanspeed_t::kMin: return kHitachiAc424FanMin; - case stdAc::fanspeed_t::kLow: return kHitachiAc424FanLow; - case stdAc::fanspeed_t::kMedium: return kHitachiAc424FanMedium; - case stdAc::fanspeed_t::kHigh: return kHitachiAc424FanHigh; - case stdAc::fanspeed_t::kMax: return kHitachiAc424FanMax; - default: return kHitachiAc424FanAuto; - } -} - -/// 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 IRHitachiAc424::toCommonMode(const uint8_t mode) { - switch (mode) { - case kHitachiAc424Cool: return stdAc::opmode_t::kCool; - case kHitachiAc424Heat: return stdAc::opmode_t::kHeat; - case kHitachiAc424Dry: return stdAc::opmode_t::kDry; - case kHitachiAc424Fan: 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. -/// @return The stdAc equivalent of the native setting. -stdAc::fanspeed_t IRHitachiAc424::toCommonFanSpeed(const uint8_t speed) { - switch (speed) { - case kHitachiAc424FanMax: return stdAc::fanspeed_t::kMax; - case kHitachiAc424FanHigh: return stdAc::fanspeed_t::kHigh; - case kHitachiAc424FanMedium: return stdAc::fanspeed_t::kMedium; - case kHitachiAc424FanLow: return stdAc::fanspeed_t::kLow; - case kHitachiAc424FanMin: 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 IRHitachiAc424::toCommon(void) const { - stdAc::state_t result; - result.protocol = decode_type_t::HITACHI_AC424; - result.model = -1; // No models used. - result.power = getPower(); - result.mode = toCommonMode(_.Mode); - result.celsius = true; - result.degrees = _.Temp; - result.fanspeed = toCommonFanSpeed(_.Fan); - result.swingv = getSwingVToggle() ? stdAc::swingv_t::kAuto - : stdAc::swingv_t::kOff; - // Not supported. - result.swingh = stdAc::swingh_t::kOff; - result.quiet = false; - result.turbo = false; - result.clean = false; - result.econo = false; - result.filter = false; - result.light = false; - result.beep = false; - result.sleep = -1; - result.clock = -1; - return result; -} - -/// Convert the internal state into a human readable string for the settings -/// that are common to protocols of this nature. -/// @return A string containing the common settings in human-readable form. -String IRHitachiAc424::_toString(void) const { - String result = ""; - result.reserve(100); // Reserve some heap for the string to reduce fragging. - result += addBoolToString(getPower(), kPowerStr, false); - result += addModeToString(_.Mode, 0, kHitachiAc424Cool, - kHitachiAc424Heat, kHitachiAc424Dry, - kHitachiAc424Fan); - result += addTempToString(_.Temp); - result += addIntToString(_.Fan, kFanStr); - result += kSpaceLBraceStr; - switch (_.Fan) { - case kHitachiAc424FanAuto: result += kAutoStr; break; - case kHitachiAc424FanMax: result += kMaxStr; break; - case kHitachiAc424FanHigh: result += kHighStr; break; - case kHitachiAc424FanMedium: result += kMedStr; break; - case kHitachiAc424FanLow: result += kLowStr; break; - case kHitachiAc424FanMin: result += kMinStr; break; - default: result += kUnknownStr; - } - result += ')'; - result += addIntToString(_.Button, kButtonStr); - result += kSpaceLBraceStr; - switch (_.Button) { - case kHitachiAc424ButtonPowerMode: - result += kPowerStr; - result += '/'; - result += kModeStr; - break; - case kHitachiAc424ButtonFan: result += kFanStr; break; - case kHitachiAc424ButtonSwingV: result += kSwingVStr; break; - case kHitachiAc344ButtonSwingH: result += kSwingHStr; break; - case kHitachiAc424ButtonTempDown: result += kTempDownStr; break; - case kHitachiAc424ButtonTempUp: result += kTempUpStr; break; - default: result += kUnknownStr; - } - result += ')'; - return result; -} - -/// Convert the internal state into a human readable string. -/// @return A string containing the settings in human-readable form. -String IRHitachiAc424::toString(void) const { - return _toString() + addBoolToString(getSwingVToggle(), kSwingVToggleStr); -} - - -#if SEND_HITACHI_AC3 -/// Send a Hitachi(3) A/C formatted message. (HITACHI_AC3) -/// Status: STABLE / Working fine. -/// @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 almost exactly the same as HitachiAC424 except this -/// variant has subtle timing differences. There are five(5) typical sizes: -/// kHitachiAc3MinStateLength (Cancel Timer), -/// kHitachiAc3MinStateLength + 2 (Change Temp), -/// kHitachiAc3StateLength - 6 (Change Mode), -/// kHitachiAc3StateLength - 4 (Normal), & -/// kHitachiAc3StateLength (Set Timer) -void IRsend::sendHitachiAc3(const uint8_t data[], const uint16_t nbytes, - const uint16_t repeat) { - // Header + Data + Footer - sendGeneric(kHitachiAc3HdrMark, kHitachiAc3HdrSpace, - kHitachiAc3BitMark, kHitachiAc3OneSpace, - kHitachiAc3BitMark, kHitachiAc3ZeroSpace, - kHitachiAc3BitMark, kHitachiAcMinGap, - data, nbytes, // Bytes - kHitachiAcFreq, false, repeat, kDutyDefault); -} -#endif // SEND_HITACHI_AC3 - - -/// 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? -IRHitachiAc3::IRHitachiAc3(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 Reset to auto fan, cooling, 23° Celsius -void IRHitachiAc3::stateReset(void) { - for (uint8_t i = 0; i < kHitachiAc3StateLength; i++) - remote_state[i] = 0x00; - remote_state[0] = 0x01; - remote_state[1] = 0x10; - remote_state[3] = 0x40; - remote_state[5] = 0xFF; - remote_state[7] = 0xE8; - remote_state[9] = 0x89; - remote_state[11] = 0x0B; - remote_state[13] = 0x3F; - remote_state[15] = 0x15; - remote_state[21] = 0x4B; - remote_state[23] = 0x18; - setInvertedStates(); -} - -/// Invert every second byte of the internal state, after the fixed header. -/// @param[in] length The size of the state array. -/// @note This is this protocols integrity check. -void IRHitachiAc3::setInvertedStates(const uint16_t length) { - if (length > 3) invertBytePairs(remote_state + 3, length - 3); -} - -/// Check if every second byte of the state, after the fixed header -/// is inverted to the previous byte. -/// @param[in] state The state array to be checked. -/// @param[in] length The size of the state array. -/// @note This is this protocols integrity check. -bool IRHitachiAc3::hasInvertedStates(const uint8_t state[], - const uint16_t length) { - return (length <= 3 || checkInvertedBytePairs(state + 3, length - 3)); -} - -/// Set up hardware to be able to send a message. -void IRHitachiAc3::begin(void) { _irsend.begin(); } - -/// 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 *IRHitachiAc3::getRaw(void) { - setInvertedStates(); - return remote_state; -} - -/// 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 new_code array. -void IRHitachiAc3::setRaw(const uint8_t new_code[], const uint16_t length) { - memcpy(remote_state, new_code, std::min(length, kHitachiAc3StateLength)); -} - -#if DECODE_HITACHI_AC3 -/// Decode the supplied Hitachi 15to27-byte/120to216-bit A/C message. -/// Status: STABLE / Works fine. -/// @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 almost exactly the same as HitachiAC424 except this -/// variant has subtle timing differences and multiple lengths. -/// There are five(5) typical lengths: -/// kHitachiAc3MinStateLength (Cancel Timer), -/// kHitachiAc3MinStateLength + 2 (Change Temp), -/// kHitachiAc3StateLength - 6 (Change Mode), -/// kHitachiAc3StateLength - 4 (Normal), & -/// kHitachiAc3StateLength (Set Timer) -/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1060 -bool IRrecv::decodeHitachiAc3(decode_results *results, uint16_t offset, - const uint16_t nbits, - const bool strict) { - if (results->rawlen < 2 * nbits + kHeader + kFooter - 1 + offset) - return false; // Too short a message to match. - if (strict) { - // Check the requested bit length. - switch (nbits) { - case kHitachiAc3MinBits: // Cancel Timer (Min Size) - case kHitachiAc3MinBits + 2 * 8: // Change Temp - case kHitachiAc3Bits - 6 * 8: // Change Mode - case kHitachiAc3Bits - 4 * 8: // Normal - case kHitachiAc3Bits: // Set Temp (Max Size) - break; - default: return false; - } - } - - // Header + Data + Footer - if (!matchGeneric(results->rawbuf + offset, results->state, - results->rawlen - offset, nbits, - kHitachiAc3HdrMark, kHitachiAc3HdrSpace, - kHitachiAc3BitMark, kHitachiAc3OneSpace, - kHitachiAc3BitMark, kHitachiAc3ZeroSpace, - kHitachiAc3BitMark, kHitachiAcMinGap, true, - kUseDefTol, 0, false)) - return false; // We failed to find any data. - - // Compliance - if (strict && !IRHitachiAc3::hasInvertedStates(results->state, nbits / 8)) - return false; - // Success - results->decode_type = decode_type_t::HITACHI_AC3; - results->bits = nbits; - return true; -} -#endif // DECODE_HITACHI_AC3 - -/// Class constructor for handling detailed Hitachi_AC344 43 byte 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? -IRHitachiAc344::IRHitachiAc344(const uint16_t pin, const bool inverted, - const bool use_modulation) - : IRHitachiAc424(pin, inverted, use_modulation) { stateReset(); } - -/// Reset the internal state to auto fan, cooling, 23° Celsius -void IRHitachiAc344::stateReset(void) { - IRHitachiAc424::stateReset(); - _.raw[37] = 0x00; - _.raw[39] = 0x00; -} - -#if SEND_HITACHI_AC344 -/// Create and send the IR message to the A/C. -/// @param[in] repeat Nr. of times to repeat the message. -void IRHitachiAc344::send(const uint16_t repeat) { - _irsend.sendHitachiAc344(getRaw(), kHitachiAc344StateLength, repeat); -} -#endif // SEND_HITACHI_AC344 - -/// 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 (in bytes) of the code for this protocol. -void IRHitachiAc344::setRaw(const uint8_t new_code[], const uint16_t length) { - memcpy(_.raw, new_code, std::min(length, kHitachiAc344StateLength)); -} - -/// Control the vertical swing setting. -/// @param[in] on True, turns on the feature. False, turns off the feature. -void IRHitachiAc344::setSwingV(const bool on) { - setSwingVToggle(on); // Set the button value. - _.SwingV = on; -} - -/// Get the current vertical swing setting. -/// @return True, if the setting is on. False, it is off. -bool IRHitachiAc344::getSwingV(void) const { - return _.SwingV; -} - -/// Control the horizontal swing setting. -/// @param[in] position The position to set the horizontal swing to. -void IRHitachiAc344::setSwingH(const uint8_t position) { - if (position > kHitachiAc344SwingHLeftMax) - _.SwingH = kHitachiAc344SwingHMiddle; - else - _.SwingH = position; - setButton(kHitachiAc344ButtonSwingH); -} - -/// Get the current horizontal swing setting. -/// @return The current position horizontal swing is set to. -uint8_t IRHitachiAc344::getSwingH(void) const { - return _.SwingH; -} - -/// Convert a standard A/C horizontal swing into its native setting. -/// @param[in] position A stdAc::swingh_t position to convert. -/// @return The equivilent native horizontal swing position. -uint8_t IRHitachiAc344::convertSwingH(const stdAc::swingh_t position) { - switch (position) { - case stdAc::swingh_t::kAuto: return kHitachiAc344SwingHAuto; - case stdAc::swingh_t::kLeftMax: return kHitachiAc344SwingHLeftMax; - case stdAc::swingh_t::kLeft: return kHitachiAc344SwingHLeft; - case stdAc::swingh_t::kRight: return kHitachiAc344SwingHRight; - case stdAc::swingh_t::kRightMax: return kHitachiAc344SwingHRightMax; - default: return kHitachiAc344SwingHMiddle; - } -} - -/// Convert a native horizontal swing postion to it's common equivalent. -/// @param[in] pos A native position to convert. -/// @return The common horizontal swing position. -stdAc::swingh_t IRHitachiAc344::toCommonSwingH(const uint8_t pos) { - switch (pos) { - case kHitachiAc344SwingHLeftMax: return stdAc::swingh_t::kLeftMax; - case kHitachiAc344SwingHLeft: return stdAc::swingh_t::kLeft; - case kHitachiAc344SwingHRight: return stdAc::swingh_t::kRight; - case kHitachiAc344SwingHRightMax: return stdAc::swingh_t::kRightMax; - case kHitachiAc344SwingHAuto: return stdAc::swingh_t::kAuto; - 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 IRHitachiAc344::toCommon(void) const { - stdAc::state_t result = IRHitachiAc424::toCommon(); - result.protocol = decode_type_t::HITACHI_AC344; - result.swingv = _.SwingV ? stdAc::swingv_t::kAuto : stdAc::swingv_t::kOff; - result.swingh = toCommonSwingH(_.SwingH); - return result; -} - -/// Convert the internal state into a human readable string. -/// @return A string containing the settings in human-readable form. -String IRHitachiAc344::toString(void) const { - String result; - result.reserve(120); // Reserve some heap for the string to reduce fragging. - result += _toString(); - result += addBoolToString(_.SwingV, kSwingVStr); - result += addIntToString(_.SwingH, kSwingHStr); - result += kSpaceLBraceStr; - switch (_.SwingH) { - case kHitachiAc344SwingHLeftMax: result += kLeftMaxStr; break; - case kHitachiAc344SwingHLeft: result += kLeftStr; break; - case kHitachiAc344SwingHMiddle: result += kMiddleStr; break; - case kHitachiAc344SwingHRight: result += kRightStr; break; - case kHitachiAc344SwingHRightMax: result += kRightMaxStr; break; - case kHitachiAc344SwingHAuto: result += kAutoStr; break; - default: result += kUnknownStr; - } - result += ')'; - return result; -} diff --git a/lib/IRremoteESP8266/src/ir_Hitachi.h b/lib/IRremoteESP8266/src/ir_Hitachi.h index 32167596f2..88dae3f9aa 100644 --- a/lib/IRremoteESP8266/src/ir_Hitachi.h +++ b/lib/IRremoteESP8266/src/ir_Hitachi.h @@ -28,10 +28,10 @@ #ifndef UNIT_TEST #include #endif -#include "IRremoteESP8266.h" -#include "IRsend.h" +#include "../src/IRremoteESP8266.h" +#include "../src/IRsend.h" #ifdef UNIT_TEST -#include "IRsend_test.h" +#include "../src/IRsend_test.h" #endif /// Native representation of a Hitachi 224-bit A/C message. diff --git a/lib/IRremoteESP8266/src/ir_Inax.cpp b/lib/IRremoteESP8266/src/ir_Inax.cpp deleted file mode 100644 index bb68ff30d7..0000000000 --- a/lib/IRremoteESP8266/src/ir_Inax.cpp +++ /dev/null @@ -1,73 +0,0 @@ -// 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 -#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 diff --git a/lib/IRremoteESP8266/src/ir_JVC.cpp b/lib/IRremoteESP8266/src/ir_JVC.cpp deleted file mode 100644 index 9afd6a97f1..0000000000 --- a/lib/IRremoteESP8266/src/ir_JVC.cpp +++ /dev/null @@ -1,131 +0,0 @@ -// 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 -#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 diff --git a/lib/IRremoteESP8266/src/ir_Kelon.cpp b/lib/IRremoteESP8266/src/ir_Kelon.cpp deleted file mode 100644 index 39b61744eb..0000000000 --- a/lib/IRremoteESP8266/src/ir_Kelon.cpp +++ /dev/null @@ -1,502 +0,0 @@ -// Copyright 2021 Davide Depau - -/// @file -/// @brief Support for Kelan AC protocol. -/// 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 - -#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; - -#if SEND_KELON - -/// Send a Kelon 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, 50); -} - -#endif // SEND_KELON - -#if DECODE_KELON -/// Decode the supplied Kelon 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->state, - results->rawlen - offset, nbits, - kKelonHdrMark, kKelonHdrSpace, - kKelonBitMark, kKelonOneSpace, - kKelonBitMark, kKelonZeroSpace, - kKelonBitMark, 0, false, - _tolerance, 0, false)) { - return false; - } - - results->decode_type = decode_type_t::KELON; - 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(fan) - 4) * -1) % 4; -} - -/// Get the current fan speed setting. -/// @return The current fan speed. -uint8_t IRKelonAc::getFan() const { - return ((static_cast(_.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(_.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(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; - } -} - -/// 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. - result.mode = toCommonMode(getMode()); - result.celsius = true; - result.degrees = getTemp(); - result.fanspeed = toCommonFanSpeed(getFan()); - result.turbo = getSupercool(); - result.sleep = getSleep() ? 0 : -1; - // Not supported. - // N/A, AC only supports toggling it - result.power = (prev == nullptr || prev->power) ^ _.PowerToggle; - // N/A, 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.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; -} diff --git a/lib/IRremoteESP8266/src/ir_Kelon.h b/lib/IRremoteESP8266/src/ir_Kelon.h index 498650623f..663b25cb9f 100644 --- a/lib/IRremoteESP8266/src/ir_Kelon.h +++ b/lib/IRremoteESP8266/src/ir_Kelon.h @@ -17,12 +17,12 @@ #define IR_KELON_H_ #ifdef UNIT_TEST -#include "IRsend_test.h" +#include "../src/IRsend_test.h" #endif -#include "IRremoteESP8266.h" -#include "IRsend.h" -#include "IRutils.h" +#include "../src/IRremoteESP8266.h" +#include "../src/IRsend.h" +#include "../src/IRutils.h" union KelonProtocol { uint64_t raw; diff --git a/lib/IRremoteESP8266/src/ir_Kelvinator.cpp b/lib/IRremoteESP8266/src/ir_Kelvinator.cpp deleted file mode 100644 index 01d2e544c7..0000000000 --- a/lib/IRremoteESP8266/src/ir_Kelvinator.cpp +++ /dev/null @@ -1,525 +0,0 @@ -// 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 -#include -#ifndef ARDUINO -#include -#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; - -#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; - } -} - -/// Control the current vertical swing setting. -/// @param[in] on The desired setting. -void IRKelvinatorAC::setSwingVertical(const bool on) { - _.SwingV = on; - _.VentSwing = (on || _.SwingH); -} - -/// Is the vertical swing setting on? -/// @return The current value. -bool IRKelvinatorAC::getSwingVertical(void) const { - return _.SwingV; -} - -/// Control the current horizontal swing setting. -/// @param[in] on The desired setting. -void IRKelvinatorAC::setSwingHorizontal(const bool on) { - _.SwingH = on; - _.VentSwing = (on || _.SwingV); -} - -/// 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 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 += addBoolToString(_.SwingV, kSwingVStr); - 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 diff --git a/lib/IRremoteESP8266/src/ir_Kelvinator.h b/lib/IRremoteESP8266/src/ir_Kelvinator.h index 74371d8efb..8da8df345f 100644 --- a/lib/IRremoteESP8266/src/ir_Kelvinator.h +++ b/lib/IRremoteESP8266/src/ir_Kelvinator.h @@ -25,10 +25,10 @@ #ifndef UNIT_TEST #include #endif -#include "IRremoteESP8266.h" -#include "IRsend.h" +#include "../src/IRremoteESP8266.h" +#include "../src/IRsend.h" #ifdef UNIT_TEST -#include "IRsend_test.h" +#include "../src/IRsend_test.h" #endif /// Native representation of a Kelvinator A/C message. diff --git a/lib/IRremoteESP8266/src/ir_LG.cpp b/lib/IRremoteESP8266/src/ir_LG.cpp deleted file mode 100644 index d817fbdb74..0000000000 --- a/lib/IRremoteESP8266/src/ir_LG.cpp +++ /dev/null @@ -1,838 +0,0 @@ -// 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 -#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::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::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: - _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() || 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(); -} - -/// 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: - 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 Swing message. -/// @return true, if it is. Otherwise, false. -bool IRLgAc::isSwing(void) const { - return (_.raw >> 12) == kLgAcSwingSignature; -} - -/// 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); -} - -/// 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 || - 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 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; - result.model = getModel(); - result.power = getPower(); - result.mode = toCommonMode(_.Mode); - result.celsius = true; - result.degrees = getTemp(); - result.fanspeed = toCommonFanSpeed(_.Fan); - result.light = isLightToggle() ? !result.light : _light; - 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()) { - 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); -} diff --git a/lib/IRremoteESP8266/src/ir_LG.h b/lib/IRremoteESP8266/src/ir_LG.h index 6010282cae..095b5aad2b 100644 --- a/lib/IRremoteESP8266/src/ir_LG.h +++ b/lib/IRremoteESP8266/src/ir_LG.h @@ -26,11 +26,11 @@ #ifndef UNIT_TEST #include #endif -#include "IRremoteESP8266.h" -#include "IRsend.h" -#include "IRutils.h" +#include "../src/IRremoteESP8266.h" +#include "../src/IRsend.h" +#include "../src/IRutils.h" #ifdef UNIT_TEST -#include "IRsend_test.h" +#include "../src/IRsend_test.h" #endif /// Native representation of a LG A/C message. diff --git a/lib/IRremoteESP8266/src/ir_Lasertag.cpp b/lib/IRremoteESP8266/src/ir_Lasertag.cpp deleted file mode 100644 index 3e40de0efd..0000000000 --- a/lib/IRremoteESP8266/src/ir_Lasertag.cpp +++ /dev/null @@ -1,116 +0,0 @@ -// 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 -#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 diff --git a/lib/IRremoteESP8266/src/ir_Lego.cpp b/lib/IRremoteESP8266/src/ir_Lego.cpp deleted file mode 100644 index 3b7144768b..0000000000 --- a/lib/IRremoteESP8266/src/ir_Lego.cpp +++ /dev/null @@ -1,106 +0,0 @@ -// 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 -#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 diff --git a/lib/IRremoteESP8266/src/ir_Lutron.cpp b/lib/IRremoteESP8266/src/ir_Lutron.cpp deleted file mode 100644 index 5d04247843..0000000000 --- a/lib/IRremoteESP8266/src/ir_Lutron.cpp +++ /dev/null @@ -1,143 +0,0 @@ -// 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 -#include -#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 diff --git a/lib/IRremoteESP8266/src/ir_MWM.cpp b/lib/IRremoteESP8266/src/ir_MWM.cpp deleted file mode 100644 index 8aca4a4fd0..0000000000 --- a/lib/IRremoteESP8266/src/ir_MWM.cpp +++ /dev/null @@ -1,197 +0,0 @@ -// 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 -#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 diff --git a/lib/IRremoteESP8266/src/ir_Magiquest.cpp b/lib/IRremoteESP8266/src/ir_Magiquest.cpp deleted file mode 100644 index 3f79a18f64..0000000000 --- a/lib/IRremoteESP8266/src/ir_Magiquest.cpp +++ /dev/null @@ -1,154 +0,0 @@ -// 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 -#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 diff --git a/lib/IRremoteESP8266/src/ir_Magiquest.h b/lib/IRremoteESP8266/src/ir_Magiquest.h index 3999043752..37f928b3f8 100644 --- a/lib/IRremoteESP8266/src/ir_Magiquest.h +++ b/lib/IRremoteESP8266/src/ir_Magiquest.h @@ -15,8 +15,8 @@ #define __STDC_LIMIT_MACROS #include -#include "IRremoteESP8266.h" -#include "IRsend.h" +#include "../src/IRremoteESP8266.h" +#include "../src/IRsend.h" /// MagiQuest packet is both Wand ID and magnitude of swish and flick union magiquest { diff --git a/lib/IRremoteESP8266/src/ir_Metz.cpp b/lib/IRremoteESP8266/src/ir_Metz.cpp deleted file mode 100644 index 0dcc7dafa6..0000000000 --- a/lib/IRremoteESP8266/src/ir_Metz.cpp +++ /dev/null @@ -1,101 +0,0 @@ -// 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 diff --git a/lib/IRremoteESP8266/src/ir_Midea.cpp b/lib/IRremoteESP8266/src/ir_Midea.cpp deleted file mode 100644 index 8b3b645d80..0000000000 --- a/lib/IRremoteESP8266/src/ir_Midea.cpp +++ /dev/null @@ -1,796 +0,0 @@ -// 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 -#ifndef ARDUINO -#include -#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::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; - _SwingVToggle = false; - _EconoToggle = false; - _TurboToggle = false; - _LightToggle = 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; -} -#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; -} - -/// 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.clean = 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.fanspeed = toCommonFanSpeed(_.Fan); - result.sleep = _.Sleep ? 0 : -1; - result.econo = getEconoToggle(); - 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 += addBoolToString(getSwingVToggle(), kSwingVToggleStr); -#if KAYSUN_AC - result += addBoolToString(getSwingVStep(), kStepStr); -#endif // KAYSUN_AC - result += addBoolToString(getEconoToggle(), kEconoToggleStr); - result += addBoolToString(getTurboToggle(), kTurboToggleStr); - result += addBoolToString(getLightToggle(), kLightToggleStr); - 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 diff --git a/lib/IRremoteESP8266/src/ir_Midea.h b/lib/IRremoteESP8266/src/ir_Midea.h index eaeaa04d8d..95a47b0b44 100644 --- a/lib/IRremoteESP8266/src/ir_Midea.h +++ b/lib/IRremoteESP8266/src/ir_Midea.h @@ -33,10 +33,10 @@ #ifdef ARDUINO #include #endif -#include "IRremoteESP8266.h" -#include "IRsend.h" +#include "../src/IRremoteESP8266.h" +#include "../src/IRsend.h" #ifdef UNIT_TEST -#include "IRsend_test.h" +#include "../src/IRsend_test.h" #endif /// @note diff --git a/lib/IRremoteESP8266/src/ir_MilesTag2.cpp b/lib/IRremoteESP8266/src/ir_MilesTag2.cpp deleted file mode 100644 index 013b2fbe28..0000000000 --- a/lib/IRremoteESP8266/src/ir_MilesTag2.cpp +++ /dev/null @@ -1,113 +0,0 @@ -// 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 -#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 diff --git a/lib/IRremoteESP8266/src/ir_Mirage.cpp b/lib/IRremoteESP8266/src/ir_Mirage.cpp deleted file mode 100644 index 8a573a8dc2..0000000000 --- a/lib/IRremoteESP8266/src/ir_Mirage.cpp +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright 2020 David Conran (crankyoldgit) -/// @file -/// @brief Support for Mirage protocol -/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1289 - -// Supports: -// Brand: Mirage, Model: VLU series A/C - -#include "IRrecv.h" -#include "IRsend.h" -#include "IRutils.h" - - -// 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) - - -#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; - - // 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; -} -#endif // DECODE_MIRAGE diff --git a/lib/IRremoteESP8266/src/ir_Mitsubishi.cpp b/lib/IRremoteESP8266/src/ir_Mitsubishi.cpp deleted file mode 100644 index 160a882bf4..0000000000 --- a/lib/IRremoteESP8266/src/ir_Mitsubishi.cpp +++ /dev/null @@ -1,1623 +0,0 @@ -// Copyright 2009 Ken Shirriff -// Copyright 2017-2021 David Conran -// Copyright 2019 Mark Kuchel -// Copyright 2018 Denes Varga - -/// @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 - -#include "ir_Mitsubishi.h" -#include -#include -#ifndef ARDUINO -#include -#endif -#include "IRrecv.h" -#include "IRsend.h" -#include "IRtext.h" -#include "IRutils.h" -#include "ir_Tcl.h" - -// Constants -// Mitsubishi TV -// period time is 1/33000Hz = 30.303 uSeconds (T) -const uint16_t kMitsubishiTick = 30; -const uint16_t kMitsubishiBitMarkTicks = 10; -const uint16_t kMitsubishiBitMark = kMitsubishiBitMarkTicks * kMitsubishiTick; -const uint16_t kMitsubishiOneSpaceTicks = 70; -const uint16_t kMitsubishiOneSpace = kMitsubishiOneSpaceTicks * kMitsubishiTick; -const uint16_t kMitsubishiZeroSpaceTicks = 30; -const uint16_t kMitsubishiZeroSpace = - kMitsubishiZeroSpaceTicks * kMitsubishiTick; -const uint16_t kMitsubishiMinCommandLengthTicks = 1786; -const uint16_t kMitsubishiMinCommandLength = - kMitsubishiMinCommandLengthTicks * kMitsubishiTick; -const uint16_t kMitsubishiMinGapTicks = 936; -const uint16_t kMitsubishiMinGap = kMitsubishiMinGapTicks * kMitsubishiTick; - -// Mitsubishi Projector (HC3000) -const uint16_t kMitsubishi2HdrMark = 8400; -const uint16_t kMitsubishi2HdrSpace = kMitsubishi2HdrMark / 2; -const uint16_t kMitsubishi2BitMark = 560; -const uint16_t kMitsubishi2ZeroSpace = 520; -const uint16_t kMitsubishi2OneSpace = kMitsubishi2ZeroSpace * 3; -const uint16_t kMitsubishi2MinGap = 28500; - -// Mitsubishi A/C -const uint16_t kMitsubishiAcHdrMark = 3400; -const uint16_t kMitsubishiAcHdrSpace = 1750; -const uint16_t kMitsubishiAcBitMark = 450; -const uint16_t kMitsubishiAcOneSpace = 1300; -const uint16_t kMitsubishiAcZeroSpace = 420; -const uint16_t kMitsubishiAcRptMark = 440; -const uint16_t kMitsubishiAcRptSpace = 17100; -const uint8_t kMitsubishiAcExtraTolerance = 5; - -// Mitsubishi 136 bit A/C -const uint16_t kMitsubishi136HdrMark = 3324; -const uint16_t kMitsubishi136HdrSpace = 1474; -const uint16_t kMitsubishi136BitMark = 467; -const uint16_t kMitsubishi136OneSpace = 1137; -const uint16_t kMitsubishi136ZeroSpace = 351; -const uint32_t kMitsubishi136Gap = kDefaultMessageGap; - -// Mitsubishi 112 bit A/C -const uint16_t kMitsubishi112HdrMark = 3450; -const uint16_t kMitsubishi112HdrSpace = 1696; -const uint16_t kMitsubishi112BitMark = 450; -const uint16_t kMitsubishi112OneSpace = 1250; -const uint16_t kMitsubishi112ZeroSpace = 385; -const uint32_t kMitsubishi112Gap = kDefaultMessageGap; -// Total tolerance percentage to use for matching the header mark. -const uint8_t kMitsubishi112HdrMarkTolerance = 5; - - -using irutils::addBoolToString; -using irutils::addFanToString; -using irutils::addIntToString; -using irutils::addLabeledString; -using irutils::addModeToString; -using irutils::addSwingHToString; -using irutils::addSwingVToString; -using irutils::addTempToString; -using irutils::addTempFloatToString; -using irutils::minsToString; - -#if SEND_MITSUBISHI -/// Send the supplied Mitsubishi 16-bit 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 appears to have no header. -/// @see https://github.com/marcosamarinho/IRremoteESP8266/blob/master/ir_Mitsubishi.cpp -/// @see GlobalCache's Control Tower's Mitsubishi TV data. -void IRsend::sendMitsubishi(uint64_t data, uint16_t nbits, uint16_t repeat) { - sendGeneric(0, 0, // No Header - kMitsubishiBitMark, kMitsubishiOneSpace, kMitsubishiBitMark, - kMitsubishiZeroSpace, kMitsubishiBitMark, kMitsubishiMinGap, - kMitsubishiMinCommandLength, data, nbits, 33, true, repeat, 50); -} -#endif // SEND_MITSUBISHI - -#if DECODE_MITSUBISHI -/// Decode the supplied Mitsubishi 16-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. -/// @note This protocol appears to have no header. -/// @see GlobalCache's Control Tower's Mitsubishi TV data. -bool IRrecv::decodeMitsubishi(decode_results *results, uint16_t offset, - const uint16_t nbits, const bool strict) { - if (strict && nbits != kMitsubishiBits) - return false; // Request is out of spec. - - uint64_t data = 0; - - // Match Data + Footer - if (!matchGeneric(results->rawbuf + offset, &data, - results->rawlen - offset, nbits, - 0, 0, // No header - kMitsubishiBitMark, kMitsubishiOneSpace, - kMitsubishiBitMark, kMitsubishiZeroSpace, - kMitsubishiBitMark, kMitsubishiMinGap, - true, 30)) return false; - // Success - results->decode_type = MITSUBISHI; - results->bits = nbits; - results->value = data; - results->address = 0; - results->command = 0; - return true; -} -#endif // DECODE_MITSUBISHI - -#if SEND_MITSUBISHI2 -/// Send a supplied second variant Mitsubishi 16-bit message. -/// Status: BETA / Probably works. -/// @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 Based on a Mitsubishi HC3000 projector's remote. -/// This protocol appears to have a mandatory in-protocol repeat. -/// That is in *addition* to the entire message needing to be sent twice -/// for the device to accept the command. That is separate from the repeat. -/// i.e. Allegedly, the real remote requires the "Off" button pressed twice. -/// You will need to add a suitable gap yourself. -/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/441 -void IRsend::sendMitsubishi2(uint64_t data, uint16_t nbits, uint16_t repeat) { - for (uint16_t i = 0; i <= repeat; i++) { - // First half of the data. - sendGeneric(kMitsubishi2HdrMark, kMitsubishi2HdrSpace, kMitsubishi2BitMark, - kMitsubishi2OneSpace, kMitsubishi2BitMark, - kMitsubishi2ZeroSpace, kMitsubishi2BitMark, - kMitsubishi2HdrSpace, data >> (nbits / 2), nbits / 2, 33, true, - 0, 50); - // Second half of the data. - sendGeneric(0, 0, // No header for the second data block - kMitsubishi2BitMark, kMitsubishi2OneSpace, kMitsubishi2BitMark, - kMitsubishi2ZeroSpace, kMitsubishi2BitMark, kMitsubishi2MinGap, - data & ((1 << (nbits / 2)) - 1), nbits / 2, 33, true, 0, 50); - } -} -#endif // SEND_MITSUBISHI2 - -#if DECODE_MITSUBISHI2 -/// Decode the supplied second variation of a Mitsubishi 16-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. -/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/441 -bool IRrecv::decodeMitsubishi2(decode_results *results, uint16_t offset, - const uint16_t nbits, const bool strict) { - if (results->rawlen <= 2 * nbits + kHeader + (kFooter * 2) - 1 + offset) - return false; // Shorter than shortest possibly expected. - if (strict && nbits != kMitsubishiBits) - return false; // Request is out of spec. - - results->value = 0; - - // Header - if (!matchMark(results->rawbuf[offset++], kMitsubishi2HdrMark)) return false; - if (!matchSpace(results->rawbuf[offset++], kMitsubishi2HdrSpace)) - return false; - for (uint8_t i = 0; i < 2; i++) { - // Match Data + Footer - uint16_t used; - uint64_t data = 0; - used = matchGeneric(results->rawbuf + offset, &data, - results->rawlen - offset, nbits / 2, - 0, 0, // No header - kMitsubishi2BitMark, kMitsubishi2OneSpace, - kMitsubishi2BitMark, kMitsubishi2ZeroSpace, - kMitsubishi2BitMark, kMitsubishi2HdrSpace, - i % 2); - if (!used) return false; - offset += used; - results->value <<= (nbits / 2); - results->value |= data; - } - - // Success - results->decode_type = MITSUBISHI2; - results->bits = nbits; - results->address = GETBITS64(results->value, nbits / 2, nbits / 2); - results->command = GETBITS64(results->value, 0, nbits / 2); - return true; -} -#endif // DECODE_MITSUBISHI2 - -#if SEND_MITSUBISHI_AC -/// Send a Mitsubishi 144-bit A/C formatted message. (MITSUBISHI_AC) -/// 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::sendMitsubishiAC(const unsigned char data[], const uint16_t nbytes, - const uint16_t repeat) { - if (nbytes < kMitsubishiACStateLength) - return; // Not enough bytes to send a proper message. - - sendGeneric(kMitsubishiAcHdrMark, kMitsubishiAcHdrSpace, kMitsubishiAcBitMark, - kMitsubishiAcOneSpace, kMitsubishiAcBitMark, - kMitsubishiAcZeroSpace, kMitsubishiAcRptMark, - kMitsubishiAcRptSpace, data, nbytes, 38, false, repeat, 50); -} -#endif // SEND_MITSUBISHI_AC - -#if DECODE_MITSUBISHI_AC -/// Decode the supplied Mitsubish 144-bit A/C message. -/// Status: BETA / Probably works -/// @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. -/// @see https://www.analysir.com/blog/2015/01/06/reverse-engineering-mitsubishi-ac-infrared-protocol/ -bool IRrecv::decodeMitsubishiAC(decode_results *results, uint16_t offset, - const uint16_t nbits, - const bool strict) { - // Compliance - if (strict && nbits != kMitsubishiACBits) return false; // Out of spec. - // Do we need to look for a repeat? - const uint16_t expected_repeats = strict ? kMitsubishiACMinRepeat : kNoRepeat; - // Enough data? - if (results->rawlen <= (nbits * 2 + kHeader + kFooter) * - (expected_repeats + 1) + offset - 1) return false; - uint16_t save[kStateSizeMax]; - // Handle repeats if we need too. - for (uint16_t r = 0; r <= expected_repeats; r++) { - // Header + Data + Footer - uint16_t used = matchGeneric(results->rawbuf + offset, results->state, - results->rawlen - offset, nbits, - kMitsubishiAcHdrMark, kMitsubishiAcHdrSpace, - kMitsubishiAcBitMark, kMitsubishiAcOneSpace, - kMitsubishiAcBitMark, kMitsubishiAcZeroSpace, - kMitsubishiAcRptMark, kMitsubishiAcRptSpace, - r < expected_repeats, // At least? - _tolerance + kMitsubishiAcExtraTolerance, - 0, false); - if (!used) return false; // No match. - offset += used; - if (r) { // Is this a repeat? - // Repeats are expected to be exactly the same. - if (std::memcmp(save, results->state, nbits / 8) != 0) return false; - } else { // It is the first message. - // Compliance - if (strict) { - // Data signature check. - static const uint8_t signature[5] = {0x23, 0xCB, 0x26, 0x01, 0x00}; - if (std::memcmp(results->state, signature, 5) != 0) return false; - // Checksum verification. - if (!IRMitsubishiAC::validChecksum(results->state)) return false; - } - // Save a copy of the state to compare with. - std::memcpy(save, results->state, nbits / 8); - } - } - - // Success. - results->decode_type = MITSUBISHI_AC; - results->bits = nbits; - return true; -} -#endif // DECODE_MITSUBISHI_AC - -// Code to emulate Mitsubishi A/C IR remote control unit. -// Inspired and derived from the work done at: -// https://github.com/r45635/HVAC-IR-Control - -/// 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? -/// @warning Consider this very alpha code. Seems to work, but not validated. -IRMitsubishiAC::IRMitsubishiAC(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 IRMitsubishiAC::stateReset(void) { - // The state of the IR remote in IR code form. - // Known good state obtained from: - // https://github.com/r45635/HVAC-IR-Control/blob/master/HVAC_ESP8266/HVAC_ESP8266.ino#L108 - static const uint8_t kReset[kMitsubishiACStateLength] = { - 0x23, 0xCB, 0x26, 0x01, 0x00, 0x20, 0x08, 0x06, 0x30, 0x45, 0x67}; - setRaw(kReset); -} - -/// Set up hardware to be able to send a message. -void IRMitsubishiAC::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 IRMitsubishiAC::send(const uint16_t repeat) { - _irsend.sendMitsubishiAC(getRaw(), kMitsubishiACStateLength, repeat); -} -#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 *IRMitsubishiAC::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 IRMitsubishiAC::setRaw(const uint8_t *data) { - std::memcpy(_.raw, data, kMitsubishiACStateLength); -} - -/// Calculate and set the checksum values for the internal state. -void IRMitsubishiAC::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 IRMitsubishiAC::validChecksum(const uint8_t *data) { - return calculateChecksum(data) == data[kMitsubishiACStateLength - 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 IRMitsubishiAC::calculateChecksum(const uint8_t *data) { - return sumBytes(data, kMitsubishiACStateLength - 1); -} - -/// Set the requested power state of the A/C to on. -void IRMitsubishiAC::on(void) { setPower(true); } - -/// Set the requested power state of the A/C to off. -void IRMitsubishiAC::off(void) { setPower(false); } - -/// Change the power setting. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRMitsubishiAC::setPower(bool on) { - _.Power = on; -} - -/// Get the value of the current power setting. -/// @return true, the setting is on. false, the setting is off. -bool IRMitsubishiAC::getPower(void) const { - return _.Power; -} - -/// Set the temperature. -/// @param[in] degrees The temperature in degrees celsius. -/// @note The temperature resolution is 0.5 of a degree. -void IRMitsubishiAC::setTemp(const float degrees) { - // Make sure we have desired temp in the correct range. - float celsius = std::max(degrees, kMitsubishiAcMinTemp); - celsius = std::min(celsius, kMitsubishiAcMaxTemp); - // Convert to integer nr. of half degrees. - uint8_t nrHalfDegrees = celsius * 2; - // Do we have a half degree celsius? - _.HalfDegree = nrHalfDegrees & 1; - _.Temp = static_cast(nrHalfDegrees / 2 - kMitsubishiAcMinTemp); -} - -/// Get the current temperature setting. -/// @return The current setting for temp. in degrees celsius. -/// @note The temperature resolution is 0.5 of a degree. -float IRMitsubishiAC::getTemp(void) const { - return _.Temp + kMitsubishiAcMinTemp + (_.HalfDegree ? 0.5 : 0); -} - -/// Set the speed of the fan. -/// @param[in] speed The desired setting. 0 is auto, 1-5 is speed, 6 is silent. -void IRMitsubishiAC::setFan(const uint8_t speed) { - uint8_t fan = speed; - // Bounds check - if (fan > kMitsubishiAcFanSilent) - fan = kMitsubishiAcFanMax; // Set the fan to maximum if out of range. - // Auto has a special bit. - _.FanAuto = (fan == kMitsubishiAcFanAuto); - if (fan >= kMitsubishiAcFanMax) - fan--; // There is no spoon^H^H^Heed 5 (max), pretend it doesn't exist. - _.Fan = fan; -} - -/// Get the current fan speed setting. -/// @return The current fan speed/mode. -uint8_t IRMitsubishiAC::getFan(void) const { - uint8_t fan = _.Fan; - if (fan == kMitsubishiAcFanMax) return kMitsubishiAcFanSilent; - return fan; -} - -/// Get the operating mode setting of the A/C. -/// @return The current operating mode setting. -uint8_t IRMitsubishiAC::getMode(void) const { - return _.Mode; -} - -/// Set the operating mode of the A/C. -/// @param[in] mode The desired operating mode. -void IRMitsubishiAC::setMode(const uint8_t mode) { - // If we get an unexpected mode, default to AUTO. - switch (mode) { - case kMitsubishiAcAuto: _.raw[8] = 0b00110000; break; - case kMitsubishiAcCool: _.raw[8] = 0b00110110; break; - case kMitsubishiAcDry: _.raw[8] = 0b00110010; break; - case kMitsubishiAcHeat: _.raw[8] = 0b00110000; break; - case kMitsubishiAcFan: _.raw[8] = 0b00110111; break; - default: - _.raw[8] = 0b00110000; - _.Mode = kMitsubishiAcAuto; - return; - } - _.Mode = mode; -} - -/// Set the requested vane (Vertical Swing) operation mode of the a/c unit. -/// @note On some models, this represents the Right vertical vane. -/// @param[in] position The position/mode to set the vane to. -void IRMitsubishiAC::setVane(const uint8_t position) { - uint8_t pos = std::min(position, kMitsubishiAcVaneAutoMove); // bounds check - _.VaneBit = 1; - _.Vane = pos; -} - -/// Set the requested wide-vane (Horizontal Swing) operation mode of the a/c. -/// @param[in] position The position/mode to set the wide vane to. -void IRMitsubishiAC::setWideVane(const uint8_t position) { - _.WideVane = std::min(position, kMitsubishiAcWideVaneAuto); -} - -/// Get the Vane (Vertical Swing) mode of the A/C. -/// @note On some models, this represents the Right vertical vane. -/// @return The native position/mode setting. -uint8_t IRMitsubishiAC::getVane(void) const { - return _.Vane; -} - -/// Get the Wide Vane (Horizontal Swing) mode of the A/C. -/// @return The native position/mode setting. -uint8_t IRMitsubishiAC::getWideVane(void) const { - return _.WideVane; -} - -/// Set the requested Left Vane (Vertical Swing) operation mode of the a/c unit. -/// @param[in] position The position/mode to set the vane to. -void IRMitsubishiAC::setVaneLeft(const uint8_t position) { - _.VaneLeft = std::min(position, kMitsubishiAcVaneAutoMove); // bounds check -} - -/// Get the Left Vane (Vertical Swing) mode of the A/C. -/// @return The native position/mode setting. -uint8_t IRMitsubishiAC::getVaneLeft(void) const { return _.VaneLeft; } - -/// Get the clock time of the A/C unit. -/// @return Nr. of 10 minute increments past midnight. -/// @note 1 = 1/6 hour (10 minutes). e.g. 4pm = 48. -uint8_t IRMitsubishiAC::getClock(void) const { return _.Clock; } - -/// Set the clock time on the A/C unit. -/// @param[in] clock Nr. of 10 minute increments past midnight. -/// @note 1 = 1/6 hour (10 minutes). e.g. 6am = 36. -void IRMitsubishiAC::setClock(const uint8_t clock) { - _.Clock = clock; -} - -/// Get the desired start time of the A/C unit. -/// @return Nr. of 10 minute increments past midnight. -/// @note 1 = 1/6 hour (10 minutes). e.g. 4pm = 48. -uint8_t IRMitsubishiAC::getStartClock(void) const { return _.StartClock; } - -/// Set the desired start time of the A/C unit. -/// @param[in] clock Nr. of 10 minute increments past midnight. -/// @note 1 = 1/6 hour (10 minutes). e.g. 8pm = 120. -void IRMitsubishiAC::setStartClock(const uint8_t clock) { - _.StartClock = clock; -} - -/// Get the desired stop time of the A/C unit. -/// @return Nr. of 10 minute increments past midnight. -/// @note 1 = 1/6 hour (10 minutes). e.g. 10pm = 132. -uint8_t IRMitsubishiAC::getStopClock(void) const { return _.StopClock; } - -/// Set the desired stop time of the A/C unit. -/// @param[in] clock Nr. of 10 minute increments past midnight. -/// @note 1 = 1/6 hour (10 minutes). e.g. 10pm = 132. -void IRMitsubishiAC::setStopClock(const uint8_t clock) { - _.StopClock = clock; -} - -/// Get the timers active setting of the A/C. -/// @return The current timers enabled. -/// @note Possible values: kMitsubishiAcNoTimer, -/// kMitsubishiAcStartTimer, kMitsubishiAcStopTimer, -/// kMitsubishiAcStartStopTimer -uint8_t IRMitsubishiAC::getTimer(void) const { - return _.Timer; -} - -/// Set the timers active setting of the A/C. -/// @param[in] timer The timer code indicating which ones are active. -/// @note Possible values: kMitsubishiAcNoTimer, -/// kMitsubishiAcStartTimer, kMitsubishiAcStopTimer, -/// kMitsubishiAcStartStopTimer -void IRMitsubishiAC::setTimer(const uint8_t timer) { - _.Timer = timer; -} - -/// 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 IRMitsubishiAC::convertMode(const stdAc::opmode_t mode) { - switch (mode) { - case stdAc::opmode_t::kCool: return kMitsubishiAcCool; - case stdAc::opmode_t::kHeat: return kMitsubishiAcHeat; - case stdAc::opmode_t::kDry: return kMitsubishiAcDry; - case stdAc::opmode_t::kFan: return kMitsubishiAcFan; - default: return kMitsubishiAcAuto; - } -} - -/// 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 IRMitsubishiAC::convertFan(const stdAc::fanspeed_t speed) { - switch (speed) { - case stdAc::fanspeed_t::kMin: return kMitsubishiAcFanSilent; - case stdAc::fanspeed_t::kLow: return kMitsubishiAcFanRealMax - 3; - case stdAc::fanspeed_t::kMedium: return kMitsubishiAcFanRealMax - 2; - case stdAc::fanspeed_t::kHigh: return kMitsubishiAcFanRealMax - 1; - case stdAc::fanspeed_t::kMax: return kMitsubishiAcFanRealMax; - default: return kMitsubishiAcFanAuto; - } -} - - -/// 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. -/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1399 -/// @see https://github.com/crankyoldgit/IRremoteESP8266/pull/1401 -uint8_t IRMitsubishiAC::convertSwingV(const stdAc::swingv_t position) { - switch (position) { - case stdAc::swingv_t::kHighest: return kMitsubishiAcVaneHighest; - case stdAc::swingv_t::kHigh: return kMitsubishiAcVaneHigh; - case stdAc::swingv_t::kMiddle: return kMitsubishiAcVaneMiddle; - case stdAc::swingv_t::kLow: return kMitsubishiAcVaneLow; - case stdAc::swingv_t::kLowest: return kMitsubishiAcVaneLowest; - // These model Mitsubishi A/C have two automatic settings. - // 1. A typical up & down oscillation. (Native Swing) - // 2. The A/C determines where the best placement for the vanes, outside of - // user control. (Native Auto) - // Native "Swing" is what we consider "Auto" in stdAc. (Case 1) - case stdAc::swingv_t::kAuto: return kMitsubishiAcVaneSwing; - // Native "Auto" doesn't have a good match for this in stdAc. (Case 2) - // So we repurpose stdAc's "Off" (and anything else) to be Native Auto. - default: return kMitsubishiAcVaneAuto; - } -} - -/// Convert a stdAc::swingh_t enum into it's native setting. -/// @param[in] position The enum to be converted. -/// @return The native equivalent of the enum. -uint8_t IRMitsubishiAC::convertSwingH(const stdAc::swingh_t position) { - switch (position) { - case stdAc::swingh_t::kLeftMax: return kMitsubishiAcWideVaneLeftMax; - case stdAc::swingh_t::kLeft: return kMitsubishiAcWideVaneLeft; - case stdAc::swingh_t::kMiddle: return kMitsubishiAcWideVaneMiddle; - case stdAc::swingh_t::kRight: return kMitsubishiAcWideVaneRight; - case stdAc::swingh_t::kRightMax: return kMitsubishiAcWideVaneRightMax; - case stdAc::swingh_t::kWide: return kMitsubishiAcWideVaneWide; - case stdAc::swingh_t::kAuto: return kMitsubishiAcWideVaneAuto; - default: return kMitsubishiAcWideVaneMiddle; - } -} - -/// 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 IRMitsubishiAC::toCommonMode(const uint8_t mode) { - switch (mode) { - case kMitsubishiAcCool: return stdAc::opmode_t::kCool; - case kMitsubishiAcHeat: return stdAc::opmode_t::kHeat; - case kMitsubishiAcDry: return stdAc::opmode_t::kDry; - case kMitsubishiAcFan: 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 IRMitsubishiAC::toCommonFanSpeed(const uint8_t speed) { - switch (speed) { - case kMitsubishiAcFanRealMax: return stdAc::fanspeed_t::kMax; - case kMitsubishiAcFanRealMax - 1: return stdAc::fanspeed_t::kHigh; - case kMitsubishiAcFanRealMax - 2: return stdAc::fanspeed_t::kMedium; - case kMitsubishiAcFanRealMax - 3: return stdAc::fanspeed_t::kLow; - case kMitsubishiAcFanSilent: return stdAc::fanspeed_t::kMin; - default: return stdAc::fanspeed_t::kAuto; - } -} - -/// 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. -/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1399 -/// @see https://github.com/crankyoldgit/IRremoteESP8266/pull/1401 -stdAc::swingv_t IRMitsubishiAC::toCommonSwingV(const uint8_t pos) { - switch (pos) { - case kMitsubishiAcVaneHighest: return stdAc::swingv_t::kHighest; - case kMitsubishiAcVaneHigh: return stdAc::swingv_t::kHigh; - case kMitsubishiAcVaneMiddle: return stdAc::swingv_t::kMiddle; - case kMitsubishiAcVaneLow: return stdAc::swingv_t::kLow; - case kMitsubishiAcVaneLowest: return stdAc::swingv_t::kLowest; - // These model Mitsubishi A/C have two automatic settings. - // 1. A typical up & down oscillation. (Native Swing) - // 2. The A/C determines where the best placement for the vanes, outside of - // user control. (Native Auto) - // Native "Auto" doesn't have a good match for this in stdAc. (Case 2) - // So we repurpose stdAc's "Off" to be Native Auto. - case kMitsubishiAcVaneAuto: return stdAc::swingv_t::kOff; - // Native "Swing" is what we consider "Auto" in stdAc. (Case 1) - default: return stdAc::swingv_t::kAuto; - } -} - -/// Convert a native horizontal swing postion to it's common equivalent. -/// @param[in] pos A native position to convert. -/// @return The common horizontal swing position. -stdAc::swingh_t IRMitsubishiAC::toCommonSwingH(const uint8_t pos) { - switch (pos) { - case kMitsubishiAcWideVaneLeftMax: return stdAc::swingh_t::kLeftMax; - case kMitsubishiAcWideVaneLeft: return stdAc::swingh_t::kLeft; - case kMitsubishiAcWideVaneMiddle: return stdAc::swingh_t::kMiddle; - case kMitsubishiAcWideVaneRight: return stdAc::swingh_t::kRight; - case kMitsubishiAcWideVaneRightMax: return stdAc::swingh_t::kRightMax; - case kMitsubishiAcWideVaneWide: return stdAc::swingh_t::kWide; - default: return stdAc::swingh_t::kAuto; - } -} - -/// Convert the current internal state into its stdAc::state_t equivalent. -/// @return The stdAc equivalent of the native settings. -stdAc::state_t IRMitsubishiAC::toCommon(void) const { - stdAc::state_t result; - result.protocol = decode_type_t::MITSUBISHI_AC; - result.model = -1; // No models used. - result.power = _.Power; - result.mode = toCommonMode(_.Mode); - result.celsius = true; - result.degrees = getTemp(); - result.fanspeed = toCommonFanSpeed(getFan()); - result.swingv = toCommonSwingV(_.Vane); - result.swingh = toCommonSwingH(_.WideVane); - result.quiet = getFan() == kMitsubishiAcFanSilent; - // Not supported. - result.turbo = false; - result.clean = false; - result.econo = false; - result.filter = false; - result.light = false; - result.beep = false; - result.sleep = -1; - result.clock = -1; - return result; -} - -/// Change the Weekly Timer Enabled setting. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRMitsubishiAC::setWeeklyTimerEnabled(const bool on) { - _.WeeklyTimer = on; -} - -/// Get the value of the WeeklyTimer Enabled setting. -/// @return true, the setting is on. false, the setting is off. -bool IRMitsubishiAC::getWeeklyTimerEnabled(void) const { return _.WeeklyTimer; } - -/// Convert the internal state into a human readable string. -/// @return A string containing the settings in human-readable form. -String IRMitsubishiAC::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, kMitsubishiAcAuto, kMitsubishiAcCool, - kMitsubishiAcHeat, kMitsubishiAcDry, - kMitsubishiAcFan); - result += addTempFloatToString(getTemp()); - result += addFanToString(getFan(), kMitsubishiAcFanRealMax, - kMitsubishiAcFanRealMax - 3, - kMitsubishiAcFanAuto, kMitsubishiAcFanQuiet, - kMitsubishiAcFanRealMax - 2); - result += addSwingVToString(_.Vane, kMitsubishiAcVaneAuto, - kMitsubishiAcVaneHighest, kMitsubishiAcVaneHigh, - kMitsubishiAcVaneAuto, // Upper Middle unused. - kMitsubishiAcVaneMiddle, - kMitsubishiAcVaneAuto, // Lower Middle unused. - kMitsubishiAcVaneLow, kMitsubishiAcVaneLowest, - kMitsubishiAcVaneAuto, kMitsubishiAcVaneSwing, - // Below are unused. - kMitsubishiAcVaneAuto, kMitsubishiAcVaneAuto); - result += addSwingHToString(_.WideVane, kMitsubishiAcWideVaneAuto, - kMitsubishiAcWideVaneLeftMax, - kMitsubishiAcWideVaneLeft, - kMitsubishiAcWideVaneMiddle, - kMitsubishiAcWideVaneRight, - kMitsubishiAcWideVaneRightMax, - kMitsubishiAcWideVaneAuto, // Unused - kMitsubishiAcWideVaneAuto, // Unused - kMitsubishiAcWideVaneAuto, // Unused - kMitsubishiAcWideVaneAuto, // Unused - kMitsubishiAcWideVaneWide); - result += addLabeledString(minsToString(_.Clock * 10), kClockStr); - result += addLabeledString(minsToString(_.StartClock * 10), kOnTimerStr); - result += addLabeledString(minsToString(_.StopClock * 10), kOffTimerStr); - result += kCommaSpaceStr; - result += kTimerStr; - result += kColonSpaceStr; - switch (_.Timer) { - case kMitsubishiAcNoTimer: - result += '-'; - break; - case kMitsubishiAcStartTimer: - result += kStartStr; - break; - case kMitsubishiAcStopTimer: - result += kStopStr; - break; - case kMitsubishiAcStartStopTimer: - result += kStartStr; - result += '+'; - result += kStopStr; - break; - default: - result += F("? ("); - result += _.Timer; - result += ')'; - } - result += addBoolToString(_.WeeklyTimer, kWeeklyTimerStr); - return result; -} - -#if SEND_MITSUBISHI136 -/// Send a Mitsubishi 136-bit A/C message. (MITSUBISHI136) -/// Status: BETA / Probably working. Needs to be tested against a real device. -/// @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. -/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/888 -void IRsend::sendMitsubishi136(const unsigned char data[], - const uint16_t nbytes, - const uint16_t repeat) { - if (nbytes < kMitsubishi136StateLength) - return; // Not enough bytes to send a proper message. - - sendGeneric(kMitsubishi136HdrMark, kMitsubishi136HdrSpace, - kMitsubishi136BitMark, kMitsubishi136OneSpace, - kMitsubishi136BitMark, kMitsubishi136ZeroSpace, - kMitsubishi136BitMark, kMitsubishi136Gap, - data, nbytes, 38, false, repeat, 50); -} -#endif // SEND_MITSUBISHI136 - -#if DECODE_MITSUBISHI136 -/// Decode the supplied Mitsubishi 136-bit A/C message. (MITSUBISHI136) -/// Status: STABLE / Reported as 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. -/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/888 -bool IRrecv::decodeMitsubishi136(decode_results *results, uint16_t offset, - const uint16_t nbits, - const bool strict) { - if (nbits % 8 != 0) return false; // Not a multiple of an 8 bit byte. - if (strict) { // Do checks to see if it matches the spec. - if (nbits != kMitsubishi136Bits) return false; - } - uint16_t used = matchGeneric(results->rawbuf + offset, results->state, - results->rawlen - offset, nbits, - kMitsubishi136HdrMark, kMitsubishi136HdrSpace, - kMitsubishi136BitMark, kMitsubishi136OneSpace, - kMitsubishi136BitMark, kMitsubishi136ZeroSpace, - kMitsubishi136BitMark, kMitsubishi136Gap, - true, _tolerance, 0, false); - if (!used) return false; - if (strict) { - // Header validation: Codes start with 0x23CB26 - if (results->state[0] != 0x23 || results->state[1] != 0xCB || - results->state[2] != 0x26) return false; - if (!IRMitsubishi136::validChecksum(results->state, nbits / 8)) - return false; - } - results->decode_type = MITSUBISHI136; - results->bits = nbits; - return true; -} -#endif // DECODE_MITSUBISHI136 - -// Code to emulate Mitsubishi 136bit 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? -IRMitsubishi136::IRMitsubishi136(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 IRMitsubishi136::stateReset(void) { - // The state of the IR remote in IR code form. - // Known good state obtained from: - // https://docs.google.com/spreadsheets/d/1f8EGfIbBUo2B-CzUFdrgKQprWakoYNKM80IKZN4KXQE/edit#gid=312397579&range=A10 - static const uint8_t kReset[kMitsubishi136StateLength] = { - 0x23, 0xCB, 0x26, 0x21, 0x00, 0x40, 0xC2, 0xC7, 0x04}; - std::memcpy(_.raw, kReset, kMitsubishi136StateLength); -} - -/// Calculate the checksum for the current internal state of the remote. -void IRMitsubishi136::checksum(void) { - for (uint8_t i = 0; i < 6; i++) - _.raw[kMitsubishi136PowerByte + 6 + i] = - ~_.raw[kMitsubishi136PowerByte + i]; -} - -/// Verify the checksum is valid for a given state. -/// @param[in] data The array to verify the checksum of. -/// @param[in] len The length of the data array. -/// @return true, if the state has a valid checksum. Otherwise, false. -bool IRMitsubishi136::validChecksum(const uint8_t *data, const uint16_t len) { - if (len < kMitsubishi136StateLength) return false; - const uint16_t half = (len - kMitsubishi136PowerByte) / 2; - for (uint8_t i = 0; i < half; i++) { - // This variable is needed to avoid the warning: (known compiler issue) - // warning: comparison of promoted ~unsigned with unsigned [-Wsign-compare] - const uint8_t inverted = ~data[kMitsubishi136PowerByte + half + i]; - if (data[kMitsubishi136PowerByte + i] != inverted) return false; - } - return true; -} - -/// Set up hardware to be able to send a message. -void IRMitsubishi136::begin(void) { _irsend.begin(); } - -#if SEND_MITSUBISHI136 -/// Send the current internal state as an IR message. -/// @param[in] repeat Nr. of times the message will be repeated. -void IRMitsubishi136::send(const uint16_t repeat) { - _irsend.sendMitsubishi136(getRaw(), kMitsubishi136StateLength, repeat); -} -#endif // SEND_MITSUBISHI136 - -/// 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 *IRMitsubishi136::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 IRMitsubishi136::setRaw(const uint8_t *data) { - std::memcpy(_.raw, data, kMitsubishi136StateLength); -} - -/// Set the requested power state of the A/C to on. -void IRMitsubishi136::on(void) { setPower(true); } - -/// Set the requested power state of the A/C to off. -void IRMitsubishi136::off(void) { setPower(false); } - -/// Change the power setting. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRMitsubishi136::setPower(bool on) { - _.Power = on; -} - -/// Get the value of the current power setting. -/// @return true, the setting is on. false, the setting is off. -bool IRMitsubishi136::getPower(void) const { - return _.Power; -} - -/// Set the temperature. -/// @param[in] degrees The temperature in degrees celsius. -void IRMitsubishi136::setTemp(const uint8_t degrees) { - uint8_t temp = std::max((uint8_t)kMitsubishi136MinTemp, degrees); - temp = std::min((uint8_t)kMitsubishi136MaxTemp, temp); - _.Temp = temp - kMitsubishiAcMinTemp; -} - -/// Get the current temperature setting. -/// @return The current setting for temp. in degrees celsius. -uint8_t IRMitsubishi136::getTemp(void) const { - return _.Temp + kMitsubishiAcMinTemp; -} - -/// Set the speed of the fan. -/// @param[in] speed The desired setting. -void IRMitsubishi136::setFan(const uint8_t speed) { - _.Fan = std::min(speed, kMitsubishi136FanMax); -} - -/// Get the current fan speed setting. -/// @return The current fan speed/mode. -uint8_t IRMitsubishi136::getFan(void) const { - return _.Fan; -} - -/// Get the operating mode setting of the A/C. -/// @return The current operating mode setting. -uint8_t IRMitsubishi136::getMode(void) const { - return _.Mode; -} - -/// Set the operating mode of the A/C. -/// @param[in] mode The desired operating mode. -void IRMitsubishi136::setMode(const uint8_t mode) { - // If we get an unexpected mode, default to AUTO. - switch (mode) { - case kMitsubishi136Fan: - case kMitsubishi136Cool: - case kMitsubishi136Heat: - case kMitsubishi136Auto: - case kMitsubishi136Dry: - _.Mode = mode; - break; - default: - _.Mode = kMitsubishi136Auto; - } -} - -/// Set the Vertical Swing mode of the A/C. -/// @param[in] position The position/mode to set the swing to. -void IRMitsubishi136::setSwingV(const uint8_t position) { - // If we get an unexpected mode, default to auto. - switch (position) { - case kMitsubishi136SwingVLowest: - case kMitsubishi136SwingVLow: - case kMitsubishi136SwingVHigh: - case kMitsubishi136SwingVHighest: - case kMitsubishi136SwingVAuto: - _.SwingV = position; - break; - default: - _.SwingV = kMitsubishi136SwingVAuto; - } -} - -/// Get the Vertical Swing mode of the A/C. -/// @return The native position/mode setting. -uint8_t IRMitsubishi136::getSwingV(void) const { - return _.SwingV; -} - -/// Set the Quiet mode of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRMitsubishi136::setQuiet(bool on) { - if (on) setFan(kMitsubishi136FanQuiet); - else if (getQuiet()) setFan(kMitsubishi136FanLow); -} - - -/// Get the Quiet mode of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRMitsubishi136::getQuiet(void) const { - return _.Fan == kMitsubishi136FanQuiet; -} - -/// 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 IRMitsubishi136::convertMode(const stdAc::opmode_t mode) { - switch (mode) { - case stdAc::opmode_t::kCool: return kMitsubishi136Cool; - case stdAc::opmode_t::kHeat: return kMitsubishi136Heat; - case stdAc::opmode_t::kDry: return kMitsubishi136Dry; - case stdAc::opmode_t::kFan: return kMitsubishi136Fan; - default: return kMitsubishi136Auto; - } -} - -/// 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 IRMitsubishi136::convertFan(const stdAc::fanspeed_t speed) { - switch (speed) { - case stdAc::fanspeed_t::kMin: return kMitsubishi136FanMin; - case stdAc::fanspeed_t::kLow: return kMitsubishi136FanLow; - case stdAc::fanspeed_t::kHigh: - case stdAc::fanspeed_t::kMax: return kMitsubishi136FanMax; - default: return kMitsubishi136FanMed; - } -} - -/// 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 IRMitsubishi136::convertSwingV(const stdAc::swingv_t position) { - switch (position) { - case stdAc::swingv_t::kHighest: return kMitsubishi136SwingVHighest; - case stdAc::swingv_t::kHigh: - case stdAc::swingv_t::kMiddle: return kMitsubishi136SwingVHigh; - case stdAc::swingv_t::kLow: return kMitsubishi136SwingVLow; - case stdAc::swingv_t::kLowest: return kMitsubishi136SwingVLowest; - default: return kMitsubishi136SwingVAuto; - } -} - -/// 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 IRMitsubishi136::toCommonMode(const uint8_t mode) { - switch (mode) { - case kMitsubishi136Cool: return stdAc::opmode_t::kCool; - case kMitsubishi136Heat: return stdAc::opmode_t::kHeat; - case kMitsubishi136Dry: return stdAc::opmode_t::kDry; - case kMitsubishi136Fan: 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 IRMitsubishi136::toCommonFanSpeed(const uint8_t speed) { - switch (speed) { - case kMitsubishi136FanMax: return stdAc::fanspeed_t::kMax; - case kMitsubishi136FanMed: return stdAc::fanspeed_t::kMedium; - case kMitsubishi136FanLow: return stdAc::fanspeed_t::kLow; - case kMitsubishi136FanMin: return stdAc::fanspeed_t::kMin; - default: return stdAc::fanspeed_t::kMedium; - } -} - -/// 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 IRMitsubishi136::toCommonSwingV(const uint8_t pos) { - switch (pos) { - case kMitsubishi136SwingVHighest: return stdAc::swingv_t::kHighest; - case kMitsubishi136SwingVHigh: return stdAc::swingv_t::kHigh; - case kMitsubishi136SwingVLow: return stdAc::swingv_t::kLow; - case kMitsubishi136SwingVLowest: return stdAc::swingv_t::kLowest; - default: return stdAc::swingv_t::kAuto; - } -} - -/// Convert the current internal state into its stdAc::state_t equivalent. -/// @return The stdAc equivalent of the native settings. -stdAc::state_t IRMitsubishi136::toCommon(void) const { - stdAc::state_t result; - result.protocol = decode_type_t::MITSUBISHI136; - 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 = toCommonSwingV(_.SwingV); - result.quiet = getQuiet(); - // Not supported. - result.swingh = stdAc::swingh_t::kOff; - result.turbo = false; - result.clean = false; - result.econo = false; - result.filter = false; - result.light = false; - result.beep = false; - result.sleep = -1; - result.clock = -1; - return result; -} - -/// Convert the internal state into a human readable string. -/// @return A string containing the settings in human-readable form. -String IRMitsubishi136::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, kMitsubishi136Auto, kMitsubishi136Cool, - kMitsubishi136Heat, kMitsubishi136Dry, - kMitsubishi136Fan); - result += addTempToString(getTemp()); - result += addFanToString(_.Fan, kMitsubishi136FanMax, - kMitsubishi136FanLow, kMitsubishi136FanMax, - kMitsubishi136FanQuiet, kMitsubishi136FanMed); - result += addSwingVToString(_.SwingV, kMitsubishi136SwingVAuto, - kMitsubishi136SwingVHighest, - kMitsubishi136SwingVHigh, - kMitsubishi136SwingVAuto, // Unused - kMitsubishi136SwingVAuto, // Unused - kMitsubishi136SwingVAuto, // Unused - kMitsubishi136SwingVLow, - kMitsubishi136SwingVLow, - // Below are unused. - kMitsubishi136SwingVAuto, - kMitsubishi136SwingVAuto, - kMitsubishi136SwingVAuto, - kMitsubishi136SwingVAuto); - result += addBoolToString(getQuiet(), kQuietStr); - return result; -} - - -#if SEND_MITSUBISHI112 -/// Send a Mitsubishi 112-bit A/C formatted message. (MITSUBISHI112) -/// 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. -/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/947 -void IRsend::sendMitsubishi112(const unsigned char data[], - const uint16_t nbytes, - const uint16_t repeat) { - if (nbytes < kMitsubishi112StateLength) - return; // Not enough bytes to send a proper message. - - sendGeneric(kMitsubishi112HdrMark, kMitsubishi112HdrSpace, - kMitsubishi112BitMark, kMitsubishi112OneSpace, - kMitsubishi112BitMark, kMitsubishi112ZeroSpace, - kMitsubishi112BitMark, kMitsubishi112Gap, - data, nbytes, 38, false, repeat, 50); -} -#endif // SEND_MITSUBISHI112 - -#if (DECODE_MITSUBISHI112 || DECODE_TCL112AC) -/// Decode the supplied Mitsubishi/TCL 112-bit A/C message. -/// (MITSUBISHI112, TCL112AC) -/// Status: STABLE / Reported as 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. -/// @note Note Mitsubishi112 & Tcl112Ac are basically the same protocol. -/// The only significant difference I can see is Mitsubishi112 has a -/// slightly longer header mark. We will use that to determine which -/// variant it should be. The other differences require full decoding and -/// only only with certain settings. -/// There are some other timing differences too, but the tolerances will -/// overlap. -/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/619 -/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/947 -bool IRrecv::decodeMitsubishi112(decode_results *results, uint16_t offset, - const uint16_t nbits, const bool strict) { - if (results->rawlen < (2 * nbits) + kHeader + kFooter - 1 + offset) - return false; - if (nbits % 8 != 0) return false; // Not a multiple of an 8 bit byte. - if (strict) { // Do checks to see if it matches the spec. - if (nbits != kMitsubishi112Bits && nbits != kTcl112AcBits) return false; - } - decode_type_t typeguess = decode_type_t::UNKNOWN; - uint16_t hdrspace; - uint16_t bitmark; - uint16_t onespace; - uint16_t zerospace; - uint32_t gap; - uint8_t tolerance = _tolerance; - - // Header -#if DECODE_MITSUBISHI112 - if (matchMark(results->rawbuf[offset], kMitsubishi112HdrMark, - kMitsubishi112HdrMarkTolerance, 0)) { - typeguess = decode_type_t::MITSUBISHI112; - hdrspace = kMitsubishi112HdrSpace; - bitmark = kMitsubishi112BitMark; - onespace = kMitsubishi112OneSpace; - zerospace = kMitsubishi112ZeroSpace; - gap = kMitsubishi112Gap; - } -#endif // DECODE_MITSUBISHI112 -#if (DECODE_TCL112AC || DECODE_TEKNOPOINT) - if (typeguess == decode_type_t::UNKNOWN && // We didn't match Mitsubishi112 - matchMark(results->rawbuf[offset], kTcl112AcHdrMark, - kTcl112AcHdrMarkTolerance, 0)) { - typeguess = decode_type_t::TCL112AC; - hdrspace = kTcl112AcHdrSpace; - bitmark = kTcl112AcBitMark; - onespace = kTcl112AcOneSpace; - zerospace = kTcl112AcZeroSpace; - gap = kTcl112AcGap; - tolerance += kTcl112AcTolerance; - } -#endif // (DECODE_TCL112AC || DECODE_TEKNOPOINT) - if (typeguess == decode_type_t::UNKNOWN) return false; // No header matched. - offset++; - - uint16_t used = matchGeneric(results->rawbuf + offset, results->state, - results->rawlen - offset, nbits, - 0, // Skip the header as we matched it earlier. - hdrspace, bitmark, onespace, bitmark, zerospace, - bitmark, gap, - true, tolerance, 0, false); - if (!used) return false; - if (strict) { - // Header validation: Codes start with 0x23CB26 - if (results->state[0] != 0x23 || results->state[1] != 0xCB || - results->state[2] != 0x26) return false; - // TCL112 and MITSUBISHI112 share the exact same checksum. - if (!IRTcl112Ac::validChecksum(results->state, nbits / 8)) return false; - } - // Success - results->decode_type = typeguess; - 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_MITSUBISHI112 || DECODE_TCL112AC - -// Code to emulate Mitsubishi 112bit 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? -IRMitsubishi112::IRMitsubishi112(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 IRMitsubishi112::stateReset(void) { - const uint8_t kReset[kMitsubishi112StateLength] = { - 0x23, 0xCB, 0x26, 0x01, 0x00, 0x24, 0x03, 0x0B, 0x10, - 0x00, 0x00, 0x00, 0x30}; - setRaw(kReset); -} - -/// Calculate the checksum for the current internal state of the remote. -void IRMitsubishi112::checksum(void) { - _.Sum = IRTcl112Ac::calcChecksum(_.raw, kMitsubishi112StateLength); -} - -/// Set up hardware to be able to send a message. -void IRMitsubishi112::begin(void) { _irsend.begin(); } - -#if SEND_MITSUBISHI112 -/// Send the current internal state as an IR message. -/// @param[in] repeat Nr. of times the message will be repeated. -void IRMitsubishi112::send(const uint16_t repeat) { - _irsend.sendMitsubishi112(getRaw(), kMitsubishi112StateLength, repeat); -} -#endif // SEND_MITSUBISHI112 - -/// 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 *IRMitsubishi112::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 IRMitsubishi112::setRaw(const uint8_t *data) { - std::memcpy(_.raw, data, kMitsubishi112StateLength); -} - -/// Set the requested power state of the A/C to off. -void IRMitsubishi112::on(void) { setPower(true); } - -/// Set the requested power state of the A/C to off. -void IRMitsubishi112::off(void) { setPower(false); } - -/// Change the power setting. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRMitsubishi112::setPower(bool on) { - _.Power = on; -} - -/// Get the value of the current power setting. -/// @return true, the setting is on. false, the setting is off. -bool IRMitsubishi112::getPower(void) const { - return _.Power; -} - -/// Set the temperature. -/// @param[in] degrees The temperature in degrees celsius. -void IRMitsubishi112::setTemp(const uint8_t degrees) { - uint8_t temp = std::max((uint8_t)kMitsubishi112MinTemp, degrees); - temp = std::min((uint8_t)kMitsubishi112MaxTemp, temp); - _.Temp = kMitsubishiAcMaxTemp - temp; -} - -/// Get the current temperature setting. -/// @return The current setting for temp. in degrees celsius. -uint8_t IRMitsubishi112::getTemp(void) const { - return kMitsubishiAcMaxTemp - _.Temp; -} - -/// Set the speed of the fan. -/// @param[in] speed The desired setting. -void IRMitsubishi112::setFan(const uint8_t speed) { - switch (speed) { - case kMitsubishi112FanMin: - case kMitsubishi112FanLow: - case kMitsubishi112FanMed: - case kMitsubishi112FanMax: - _.Fan = speed; - break; - default: - _.Fan = kMitsubishi112FanMax; - } -} - -/// Get the current fan speed setting. -/// @return The current fan speed/mode. -uint8_t IRMitsubishi112::getFan(void) const { - return _.Fan; -} - -/// Get the operating mode setting of the A/C. -/// @return The current operating mode setting. -uint8_t IRMitsubishi112::getMode(void) const { - return _.Mode; -} - -/// Set the operating mode of the A/C. -/// @param[in] mode The desired operating mode. -void IRMitsubishi112::setMode(const uint8_t mode) { - // If we get an unexpected mode, default to AUTO. - switch (mode) { - // Note: No Fan Only mode. - case kMitsubishi112Cool: - case kMitsubishi112Heat: - case kMitsubishi112Auto: - case kMitsubishi112Dry: - _.Mode = mode; - break; - default: - _.Mode = kMitsubishi112Auto; - } -} - -/// Set the Vertical Swing mode of the A/C. -/// @param[in] position The position/mode to set the swing to. -void IRMitsubishi112::setSwingV(const uint8_t position) { - // If we get an unexpected mode, default to auto. - switch (position) { - case kMitsubishi112SwingVLowest: - case kMitsubishi112SwingVLow: - case kMitsubishi112SwingVMiddle: - case kMitsubishi112SwingVHigh: - case kMitsubishi112SwingVHighest: - case kMitsubishi112SwingVAuto: - _.SwingV = position; - break; - default: - _.SwingV = kMitsubishi112SwingVAuto; - } -} - -/// Get the Vertical Swing mode of the A/C. -/// @return The native position/mode setting. -uint8_t IRMitsubishi112::getSwingV(void) const { - return _.SwingV; -} - -/// Set the Horizontal Swing mode of the A/C. -/// @param[in] position The position/mode to set the swing to. -void IRMitsubishi112::setSwingH(const uint8_t position) { - // If we get an unexpected mode, default to auto. - switch (position) { - case kMitsubishi112SwingHLeftMax: - case kMitsubishi112SwingHLeft: - case kMitsubishi112SwingHMiddle: - case kMitsubishi112SwingHRight: - case kMitsubishi112SwingHRightMax: - case kMitsubishi112SwingHWide: - case kMitsubishi112SwingHAuto: - _.SwingH = position; - break; - default: - _.SwingH = kMitsubishi112SwingHAuto; - } -} - - -/// Get the Horizontal Swing mode of the A/C. -/// @return The native position/mode setting. -uint8_t IRMitsubishi112::getSwingH(void) const { - return _.SwingH; -} - -/// Set the Quiet mode of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -/// @note There is no true quiet setting on this A/C. -void IRMitsubishi112::setQuiet(bool on) { - if (on) - setFan(kMitsubishi112FanQuiet); - else if (getQuiet()) setFan(kMitsubishi112FanLow); -} - - -/// Get the Quiet mode of the A/C. -/// @return true, the setting is on. false, the setting is off. -/// @note There is no true quiet setting on this A/C. -bool IRMitsubishi112::getQuiet(void) const { - return _.Fan == kMitsubishi112FanQuiet; -} - -/// 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 IRMitsubishi112::convertMode(const stdAc::opmode_t mode) { - switch (mode) { - case stdAc::opmode_t::kCool: return kMitsubishi112Cool; - case stdAc::opmode_t::kHeat: return kMitsubishi112Heat; - case stdAc::opmode_t::kDry: return kMitsubishi112Dry; - // Note: No Fan Only mode. - default: return kMitsubishi112Auto; - } -} - -/// 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 IRMitsubishi112::convertFan(const stdAc::fanspeed_t speed) { - switch (speed) { - case stdAc::fanspeed_t::kMin: return kMitsubishi112FanMin; - case stdAc::fanspeed_t::kLow: return kMitsubishi112FanLow; - case stdAc::fanspeed_t::kMedium: return kMitsubishi112FanMed; - case stdAc::fanspeed_t::kHigh: - case stdAc::fanspeed_t::kMax: return kMitsubishi112FanMax; - default: return kMitsubishi112FanMed; - } -} - -/// 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 IRMitsubishi112::convertSwingV(const stdAc::swingv_t position) { - switch (position) { - case stdAc::swingv_t::kHighest: return kMitsubishi112SwingVHighest; - case stdAc::swingv_t::kHigh: return kMitsubishi112SwingVHigh; - case stdAc::swingv_t::kMiddle: return kMitsubishi112SwingVMiddle; - case stdAc::swingv_t::kLow: return kMitsubishi112SwingVLow; - case stdAc::swingv_t::kLowest: return kMitsubishi112SwingVLowest; - default: return kMitsubishi112SwingVAuto; - } -} - -/// Convert a stdAc::swingh_t enum into it's native setting. -/// @param[in] position The enum to be converted. -/// @return The native equivalent of the enum. -uint8_t IRMitsubishi112::convertSwingH(const stdAc::swingh_t position) { - switch (position) { - case stdAc::swingh_t::kLeftMax: return kMitsubishi112SwingHLeftMax; - case stdAc::swingh_t::kLeft: return kMitsubishi112SwingHLeft; - case stdAc::swingh_t::kMiddle: return kMitsubishi112SwingHMiddle; - case stdAc::swingh_t::kRight: return kMitsubishi112SwingHRight; - case stdAc::swingh_t::kRightMax: return kMitsubishi112SwingHRightMax; - case stdAc::swingh_t::kWide: return kMitsubishi112SwingHWide; - case stdAc::swingh_t::kAuto: return kMitsubishi112SwingHAuto; - default: return kMitsubishi112SwingHAuto; - } -} - -/// 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 IRMitsubishi112::toCommonMode(const uint8_t mode) { - switch (mode) { - case kMitsubishi112Cool: return stdAc::opmode_t::kCool; - case kMitsubishi112Heat: return stdAc::opmode_t::kHeat; - case kMitsubishi112Dry: return stdAc::opmode_t::kDry; - 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 IRMitsubishi112::toCommonFanSpeed(const uint8_t speed) { - switch (speed) { - case kMitsubishi112FanMax: return stdAc::fanspeed_t::kMax; - case kMitsubishi112FanMed: return stdAc::fanspeed_t::kMedium; - case kMitsubishi112FanLow: return stdAc::fanspeed_t::kLow; - case kMitsubishi112FanMin: return stdAc::fanspeed_t::kMin; - default: return stdAc::fanspeed_t::kMedium; - } -} - -/// 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 IRMitsubishi112::toCommonSwingV(const uint8_t pos) { - switch (pos) { - case kMitsubishi112SwingVHighest: return stdAc::swingv_t::kHighest; - case kMitsubishi112SwingVHigh: return stdAc::swingv_t::kHigh; - case kMitsubishi112SwingVMiddle: return stdAc::swingv_t::kMiddle; - case kMitsubishi112SwingVLow: return stdAc::swingv_t::kLow; - case kMitsubishi112SwingVLowest: return stdAc::swingv_t::kLowest; - default: return stdAc::swingv_t::kAuto; - } -} - -/// Convert a native horizontal swing postion to it's common equivalent. -/// @param[in] pos A native position to convert. -/// @return The common horizontal swing position. -stdAc::swingh_t IRMitsubishi112::toCommonSwingH(const uint8_t pos) { - switch (pos) { - case kMitsubishi112SwingHLeftMax: return stdAc::swingh_t::kLeftMax; - case kMitsubishi112SwingHLeft: return stdAc::swingh_t::kLeft; - case kMitsubishi112SwingHMiddle: return stdAc::swingh_t::kMiddle; - case kMitsubishi112SwingHRight: return stdAc::swingh_t::kRight; - case kMitsubishi112SwingHRightMax: return stdAc::swingh_t::kRightMax; - case kMitsubishi112SwingHWide: return stdAc::swingh_t::kWide; - default: return stdAc::swingh_t::kAuto; - } -} - -/// Convert the current internal state into its stdAc::state_t equivalent. -/// @return The stdAc equivalent of the native settings. -stdAc::state_t IRMitsubishi112::toCommon(void) const { - stdAc::state_t result; - result.protocol = decode_type_t::MITSUBISHI112; - 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 = toCommonSwingV(_.SwingV); - result.swingh = toCommonSwingH(_.SwingH);; - result.quiet = getQuiet(); - // Not supported. - result.econo = false; // Need to figure this part from stdAc - result.clock = -1; - result.sleep = -1; - result.turbo = false; - result.clean = false; - result.filter = false; - result.light = 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 IRMitsubishi112::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, kMitsubishi112Auto, kMitsubishi112Cool, - kMitsubishi112Heat, kMitsubishi112Dry, - kMitsubishi112Auto); - result += addTempToString(getTemp()); - result += addFanToString(_.Fan, kMitsubishi112FanMax, - kMitsubishi112FanLow, kMitsubishi112FanMax, - kMitsubishi112FanQuiet, kMitsubishi112FanMed); - result += addSwingVToString(_.SwingV, kMitsubishi112SwingVAuto, - kMitsubishi112SwingVHighest, - kMitsubishi112SwingVHigh, - kMitsubishi112SwingVAuto, // Upper Middle unused. - kMitsubishi112SwingVMiddle, - kMitsubishi112SwingVAuto, // Lower Middle unused. - kMitsubishi112SwingVLow, - kMitsubishi112SwingVLowest, - // Below are unused. - kMitsubishi112SwingVAuto, - kMitsubishi112SwingVAuto, - kMitsubishi112SwingVAuto, - kMitsubishi112SwingVAuto); - result += addSwingHToString(_.SwingH, kMitsubishi112SwingHAuto, - kMitsubishi112SwingHLeftMax, - kMitsubishi112SwingHLeft, - kMitsubishi112SwingHMiddle, - kMitsubishi112SwingHRight, - kMitsubishi112SwingHRightMax, - kMitsubishi112SwingHAuto, // Unused - kMitsubishi112SwingHAuto, // Unused - kMitsubishi112SwingHAuto, // Unused - kMitsubishi112SwingHAuto, // Unused - kMitsubishi112SwingHWide); - result += addBoolToString(getQuiet(), kQuietStr); - return result; -} diff --git a/lib/IRremoteESP8266/src/ir_Mitsubishi.h b/lib/IRremoteESP8266/src/ir_Mitsubishi.h index 1f3a421840..bc2c4152a1 100644 --- a/lib/IRremoteESP8266/src/ir_Mitsubishi.h +++ b/lib/IRremoteESP8266/src/ir_Mitsubishi.h @@ -43,10 +43,10 @@ #ifndef UNIT_TEST #include #endif -#include "IRremoteESP8266.h" -#include "IRsend.h" +#include "../src/IRremoteESP8266.h" +#include "../src/IRsend.h" #ifdef UNIT_TEST -#include "IRsend_test.h" +#include "../src/IRsend_test.h" #endif /// Native representation of a Mitsubishi 144-bit A/C message. diff --git a/lib/IRremoteESP8266/src/ir_MitsubishiHeavy.cpp b/lib/IRremoteESP8266/src/ir_MitsubishiHeavy.cpp deleted file mode 100644 index b4a6ffc352..0000000000 --- a/lib/IRremoteESP8266/src/ir_MitsubishiHeavy.cpp +++ /dev/null @@ -1,1050 +0,0 @@ -// 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 - -#include "ir_MitsubishiHeavy.h" -#include -#include -#include "IRremoteESP8266.h" -#include "IRtext.h" -#include "IRutils.h" -#ifndef ARDUINO -#include -#endif - -// Constants -const uint16_t kMitsubishiHeavyHdrMark = 3140; -const uint16_t kMitsubishiHeavyHdrSpace = 1630; -const uint16_t kMitsubishiHeavyBitMark = 370; -const uint16_t kMitsubishiHeavyOneSpace = 420; -const uint16_t kMitsubishiHeavyZeroSpace = 1220; -const uint32_t kMitsubishiHeavyGap = kDefaultMessageGap; // Just a guess. - -using irutils::addBoolToString; -using irutils::addIntToString; -using irutils::addLabeledString; -using irutils::addModeToString; -using irutils::addSwingHToString; -using irutils::addSwingVToString; -using irutils::addTempToString; -using irutils::checkInvertedBytePairs; -using irutils::invertBytePairs; - -#if SEND_MITSUBISHIHEAVY -/// Send a MitsubishiHeavy 88-bit A/C message. -/// Status: BETA / Appears to be working. Needs testing against a real device. -/// @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::sendMitsubishiHeavy88(const unsigned char data[], - const uint16_t nbytes, - const uint16_t repeat) { - if (nbytes < kMitsubishiHeavy88StateLength) - return; // Not enough bytes to send a proper message. - sendGeneric(kMitsubishiHeavyHdrMark, kMitsubishiHeavyHdrSpace, - kMitsubishiHeavyBitMark, kMitsubishiHeavyOneSpace, - kMitsubishiHeavyBitMark, kMitsubishiHeavyZeroSpace, - kMitsubishiHeavyBitMark, kMitsubishiHeavyGap, - data, nbytes, 38000, false, repeat, kDutyDefault); -} - -/// Send a MitsubishiHeavy 152-bit A/C message. -/// Status: BETA / Appears to be working. Needs testing against a real device. -/// @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::sendMitsubishiHeavy152(const unsigned char data[], - const uint16_t nbytes, - const uint16_t repeat) { - if (nbytes < kMitsubishiHeavy152StateLength) - return; // Not enough bytes to send a proper message. - sendMitsubishiHeavy88(data, nbytes, repeat); -} -#endif // SEND_MITSUBISHIHEAVY - -// Class for decoding and constructing MitsubishiHeavy152 AC messages. - -/// 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? -IRMitsubishiHeavy152Ac::IRMitsubishiHeavy152Ac(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 IRMitsubishiHeavy152Ac::begin(void) { _irsend.begin(); } - -#if SEND_MITSUBISHIHEAVY -/// Send the current internal state as an IR message. -/// @param[in] repeat Nr. of times the message will be repeated. -void IRMitsubishiHeavy152Ac::send(const uint16_t repeat) { - _irsend.sendMitsubishiHeavy152(getRaw(), kMitsubishiHeavy152StateLength, - repeat); -} -#endif // SEND_MITSUBISHIHEAVY - -/// Reset the state of the remote to a known good state/sequence. -void IRMitsubishiHeavy152Ac::stateReset(void) { - std::memcpy(_.raw, kMitsubishiHeavyZmsSig, kMitsubishiHeavySigLength); - for (uint8_t i = kMitsubishiHeavySigLength; - i < kMitsubishiHeavy152StateLength - 3; i += 2) _.raw[i] = 0; - _.raw[17] = 0x80; -} - -/// 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 *IRMitsubishiHeavy152Ac::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 IRMitsubishiHeavy152Ac::setRaw(const uint8_t *data) { - std::memcpy(_.raw, data, kMitsubishiHeavy152StateLength); -} - -/// Set the requested power state of the A/C to on. -void IRMitsubishiHeavy152Ac::on(void) { setPower(true); } - -/// Set the requested power state of the A/C to off. -void IRMitsubishiHeavy152Ac::off(void) { setPower(false); } - -/// Change the power setting. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRMitsubishiHeavy152Ac::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 IRMitsubishiHeavy152Ac::getPower(void) const { - return _.Power; -} - -/// Set the temperature. -/// @param[in] temp The temperature in degrees celsius. -void IRMitsubishiHeavy152Ac::setTemp(const uint8_t temp) { - uint8_t newtemp = temp; - newtemp = std::min(newtemp, kMitsubishiHeavyMaxTemp); - newtemp = std::max(newtemp, kMitsubishiHeavyMinTemp); - _.Temp = newtemp - kMitsubishiHeavyMinTemp; -} - -/// Get the current temperature setting. -/// @return The current setting for temp. in degrees celsius. -uint8_t IRMitsubishiHeavy152Ac::getTemp(void) const { - return _.Temp + kMitsubishiHeavyMinTemp; -} - -/// Set the speed of the fan. -/// @param[in] speed The desired setting. -void IRMitsubishiHeavy152Ac::setFan(const uint8_t speed) { - uint8_t newspeed = speed; - switch (speed) { - case kMitsubishiHeavy152FanLow: - case kMitsubishiHeavy152FanMed: - case kMitsubishiHeavy152FanHigh: - case kMitsubishiHeavy152FanMax: - case kMitsubishiHeavy152FanEcono: - case kMitsubishiHeavy152FanTurbo: break; - default: newspeed = kMitsubishiHeavy152FanAuto; - } - _.Fan = newspeed; -} - -/// Get the current fan speed setting. -/// @return The current fan speed/mode. -uint8_t IRMitsubishiHeavy152Ac::getFan(void) const { - return _.Fan; -} - -/// Set the operating mode of the A/C. -/// @param[in] mode The desired operating mode. -void IRMitsubishiHeavy152Ac::setMode(const uint8_t mode) { - uint8_t newmode = mode; - switch (mode) { - case kMitsubishiHeavyCool: - case kMitsubishiHeavyDry: - case kMitsubishiHeavyFan: - case kMitsubishiHeavyHeat: - break; - default: - newmode = kMitsubishiHeavyAuto; - } - _.Mode = newmode; -} - -/// Get the operating mode setting of the A/C. -/// @return The current operating mode setting. -uint8_t IRMitsubishiHeavy152Ac::getMode(void) const { - return _.Mode; -} - -/// Set the Vertical Swing mode of the A/C. -/// @param[in] pos The position/mode to set the swing to. -void IRMitsubishiHeavy152Ac::setSwingVertical(const uint8_t pos) { - _.SwingV = std::min(pos, kMitsubishiHeavy152SwingVOff); -} - -/// Get the Vertical Swing mode of the A/C. -/// @return The native position/mode setting. -uint8_t IRMitsubishiHeavy152Ac::getSwingVertical(void) const { - return _.SwingV; -} - -/// Set the Horizontal Swing mode of the A/C. -/// @param[in] pos The position/mode to set the swing to. -void IRMitsubishiHeavy152Ac::setSwingHorizontal(const uint8_t pos) { - _.SwingH = std::min(pos, kMitsubishiHeavy152SwingHOff); -} - -/// Get the Horizontal Swing mode of the A/C. -/// @return The native position/mode setting. -uint8_t IRMitsubishiHeavy152Ac::getSwingHorizontal(void) const { - return _.SwingH; -} - -/// Set the Night (Sleep) mode of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRMitsubishiHeavy152Ac::setNight(const bool on) { - _.Night = on; -} - -/// Get the Night (Sleep) mode of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRMitsubishiHeavy152Ac::getNight(void) const { - return _.Night; -} - -/// Set the 3D mode of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRMitsubishiHeavy152Ac::set3D(const bool on) { - if (on) - { _.Three = 1; _.D = 1; } - else - { _.Three = 0; _.D = 0; } -} - -/// Get the 3D mode of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRMitsubishiHeavy152Ac::get3D(void) const { - return _.Three && _.D; -} - -/// Set the Silent (Quiet) mode of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRMitsubishiHeavy152Ac::setSilent(const bool on) { - _.Silent = on; -} - -/// Get the Silent (Quiet) mode of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRMitsubishiHeavy152Ac::getSilent(void) const { - return _.Silent; -} - -/// Set the Filter mode of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRMitsubishiHeavy152Ac::setFilter(const bool on) { - _.Filter = on; -} - -/// Get the Filter mode of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRMitsubishiHeavy152Ac::getFilter(void) const { - return _.Filter; -} - -/// Set the Clean mode of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRMitsubishiHeavy152Ac::setClean(const bool on) { - _.Filter = on; - _.Clean = on; -} - -/// Get the Clean mode of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRMitsubishiHeavy152Ac::getClean(void) const { - return _.Clean && _.Filter; -} - -/// Set the Turbo mode of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRMitsubishiHeavy152Ac::setTurbo(const bool on) { - if (on) - setFan(kMitsubishiHeavy152FanTurbo); - else if (getTurbo()) setFan(kMitsubishiHeavy152FanAuto); -} - -/// Get the Turbo mode of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRMitsubishiHeavy152Ac::getTurbo(void) const { - return _.Fan == kMitsubishiHeavy152FanTurbo; -} - -/// Set the Economical mode of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRMitsubishiHeavy152Ac::setEcono(const bool on) { - if (on) - setFan(kMitsubishiHeavy152FanEcono); - else if (getEcono()) setFan(kMitsubishiHeavy152FanAuto); -} - -/// Get the Economical mode of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRMitsubishiHeavy152Ac::getEcono(void) const { - return _.Fan == kMitsubishiHeavy152FanEcono; -} - -/// Verify the given state has a ZM-S signature. -/// @param[in] state A ptr to a state to be checked. -/// @return true, the check passed. Otherwise, false. -bool IRMitsubishiHeavy152Ac::checkZmsSig(const uint8_t *state) { - for (uint8_t i = 0; i < kMitsubishiHeavySigLength; i++) - if (state[i] != kMitsubishiHeavyZmsSig[i]) return false; - return true; -} - -/// Calculate the checksum for the current internal state of the remote. -/// Note: Technically it has no checksum, but does have inverted byte pairs. -void IRMitsubishiHeavy152Ac::checksum(void) { - const uint8_t kOffset = kMitsubishiHeavySigLength - 2; - invertBytePairs(_.raw + kOffset, kMitsubishiHeavy152StateLength - kOffset); -} - -/// 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 state array. -/// @return true, if the state has a valid checksum. Otherwise, false. -/// Note: Technically it has no checksum, but does have inverted byte pairs. -bool IRMitsubishiHeavy152Ac::validChecksum(const uint8_t *state, - const uint16_t length) { - // Assume anything too short is fine. - if (length < kMitsubishiHeavySigLength) return true; - const uint8_t kOffset = kMitsubishiHeavySigLength - 2; - return checkInvertedBytePairs(state + kOffset, length - kOffset); -} - -/// 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 IRMitsubishiHeavy152Ac::convertMode(const stdAc::opmode_t mode) { - switch (mode) { - case stdAc::opmode_t::kCool: return kMitsubishiHeavyCool; - case stdAc::opmode_t::kHeat: return kMitsubishiHeavyHeat; - case stdAc::opmode_t::kDry: return kMitsubishiHeavyDry; - case stdAc::opmode_t::kFan: return kMitsubishiHeavyFan; - default: return kMitsubishiHeavyAuto; - } -} - -/// 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 IRMitsubishiHeavy152Ac::convertFan(const stdAc::fanspeed_t speed) { - switch (speed) { - // Assumes Econo is slower than Low. - case stdAc::fanspeed_t::kMin: return kMitsubishiHeavy152FanEcono; - case stdAc::fanspeed_t::kLow: return kMitsubishiHeavy152FanLow; - case stdAc::fanspeed_t::kMedium: return kMitsubishiHeavy152FanMed; - case stdAc::fanspeed_t::kHigh: return kMitsubishiHeavy152FanHigh; - case stdAc::fanspeed_t::kMax: return kMitsubishiHeavy152FanMax; - default: return kMitsubishiHeavy152FanAuto; - } -} - -/// 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 IRMitsubishiHeavy152Ac::convertSwingV(const stdAc::swingv_t position) { - switch (position) { - case stdAc::swingv_t::kAuto: return kMitsubishiHeavy152SwingVAuto; - case stdAc::swingv_t::kHighest: return kMitsubishiHeavy152SwingVHighest; - case stdAc::swingv_t::kHigh: return kMitsubishiHeavy152SwingVHigh; - case stdAc::swingv_t::kMiddle: return kMitsubishiHeavy152SwingVMiddle; - case stdAc::swingv_t::kLow: return kMitsubishiHeavy152SwingVLow; - case stdAc::swingv_t::kLowest: return kMitsubishiHeavy152SwingVLowest; - default: return kMitsubishiHeavy152SwingVOff; - } -} - -/// Convert a stdAc::swingh_t enum into it's native setting. -/// @param[in] position The enum to be converted. -/// @return The native equivalent of the enum. -uint8_t IRMitsubishiHeavy152Ac::convertSwingH(const stdAc::swingh_t position) { - switch (position) { - case stdAc::swingh_t::kAuto: return kMitsubishiHeavy152SwingHAuto; - case stdAc::swingh_t::kLeftMax: return kMitsubishiHeavy152SwingHLeftMax; - case stdAc::swingh_t::kLeft: return kMitsubishiHeavy152SwingHLeft; - case stdAc::swingh_t::kMiddle: return kMitsubishiHeavy152SwingHMiddle; - case stdAc::swingh_t::kRight: return kMitsubishiHeavy152SwingHRight; - case stdAc::swingh_t::kRightMax: return kMitsubishiHeavy152SwingHRightMax; - default: return kMitsubishiHeavy152SwingHOff; - } -} - -/// 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 IRMitsubishiHeavy152Ac::toCommonMode(const uint8_t mode) { - switch (mode) { - case kMitsubishiHeavyCool: return stdAc::opmode_t::kCool; - case kMitsubishiHeavyHeat: return stdAc::opmode_t::kHeat; - case kMitsubishiHeavyDry: return stdAc::opmode_t::kDry; - case kMitsubishiHeavyFan: return stdAc::opmode_t::kFan; - default: return stdAc::opmode_t::kAuto; - } -} - -/// Convert a native fan speed into its stdAc equivalent. -/// @param[in] spd The native setting to be converted. -/// @return The stdAc equivalent of the native setting. -stdAc::fanspeed_t IRMitsubishiHeavy152Ac::toCommonFanSpeed(const uint8_t spd) { - switch (spd) { - case kMitsubishiHeavy152FanMax: return stdAc::fanspeed_t::kMax; - case kMitsubishiHeavy152FanHigh: return stdAc::fanspeed_t::kHigh; - case kMitsubishiHeavy152FanMed: return stdAc::fanspeed_t::kMedium; - case kMitsubishiHeavy152FanLow: return stdAc::fanspeed_t::kLow; - case kMitsubishiHeavy152FanEcono: return stdAc::fanspeed_t::kMin; - default: return stdAc::fanspeed_t::kAuto; - } -} - -/// Convert a native horizontal swing postion to it's common equivalent. -/// @param[in] pos A native position to convert. -/// @return The common horizontal swing position. -stdAc::swingh_t IRMitsubishiHeavy152Ac::toCommonSwingH(const uint8_t pos) { - switch (pos) { - case kMitsubishiHeavy152SwingHLeftMax: return stdAc::swingh_t::kLeftMax; - case kMitsubishiHeavy152SwingHLeft: return stdAc::swingh_t::kLeft; - case kMitsubishiHeavy152SwingHMiddle: return stdAc::swingh_t::kMiddle; - case kMitsubishiHeavy152SwingHRight: return stdAc::swingh_t::kRight; - case kMitsubishiHeavy152SwingHRightMax: return stdAc::swingh_t::kRightMax; - case kMitsubishiHeavy152SwingHOff: return stdAc::swingh_t::kOff; - default: return stdAc::swingh_t::kAuto; - } -} - -/// 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 IRMitsubishiHeavy152Ac::toCommonSwingV(const uint8_t pos) { - switch (pos) { - case kMitsubishiHeavy152SwingVHighest: return stdAc::swingv_t::kHighest; - case kMitsubishiHeavy152SwingVHigh: return stdAc::swingv_t::kHigh; - case kMitsubishiHeavy152SwingVMiddle: return stdAc::swingv_t::kMiddle; - case kMitsubishiHeavy152SwingVLow: return stdAc::swingv_t::kLow; - case kMitsubishiHeavy152SwingVLowest: return stdAc::swingv_t::kLowest; - case kMitsubishiHeavy152SwingVOff: return stdAc::swingv_t::kOff; - default: return stdAc::swingv_t::kAuto; - } -} - -/// Convert the current internal state into its stdAc::state_t equivalent. -/// @return The stdAc equivalent of the native settings. -stdAc::state_t IRMitsubishiHeavy152Ac::toCommon(void) const { - stdAc::state_t result; - result.protocol = decode_type_t::MITSUBISHI_HEAVY_152; - 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 = toCommonSwingV(_.SwingV); - result.swingh = toCommonSwingH(_.SwingH); - result.turbo = getTurbo(); - result.econo = getEcono(); - result.clean = getClean(); - result.quiet = _.Silent; - result.filter = _.Filter; - result.sleep = _.Night ? 0 : -1; - // Not supported. - result.light = false; - result.beep = false; - result.clock = -1; - return result; -} - -/// Convert the internal state into a human readable string. -/// @return A string containing the settings in human-readable form. -String IRMitsubishiHeavy152Ac::toString(void) const { - String result = ""; - result.reserve(180); // Reserve some heap for the string to reduce fragging. - result += addBoolToString(_.Power, kPowerStr, false); - result += addModeToString(_.Mode, kMitsubishiHeavyAuto, - kMitsubishiHeavyCool, kMitsubishiHeavyHeat, - kMitsubishiHeavyDry, kMitsubishiHeavyFan); - result += addTempToString(getTemp()); - result += addIntToString(_.Fan, kFanStr); - result += kSpaceLBraceStr; - switch (_.Fan) { - case kMitsubishiHeavy152FanAuto: - result += kAutoStr; - break; - case kMitsubishiHeavy152FanHigh: - result += kHighStr; - break; - case kMitsubishiHeavy152FanLow: - result += kLowStr; - break; - case kMitsubishiHeavy152FanMed: - result += kMedStr; - break; - case kMitsubishiHeavy152FanMax: - result += kMaxStr; - break; - case kMitsubishiHeavy152FanEcono: - result += kEconoStr; - break; - case kMitsubishiHeavy152FanTurbo: - result += kTurboStr; - break; - default: - result += kUnknownStr; - } - result += ')'; - result += addSwingVToString(_.SwingV, kMitsubishiHeavy152SwingVAuto, - kMitsubishiHeavy152SwingVHighest, - kMitsubishiHeavy152SwingVHigh, - kMitsubishiHeavy152SwingVAuto, // UpperMid unused - kMitsubishiHeavy152SwingVMiddle, - kMitsubishiHeavy152SwingVAuto, // LowerMid unused - kMitsubishiHeavy152SwingVLow, - kMitsubishiHeavy152SwingVLowest, - kMitsubishiHeavy152SwingVOff, - // Below are unused. - kMitsubishiHeavy152SwingVAuto, - kMitsubishiHeavy152SwingVAuto, - kMitsubishiHeavy152SwingVAuto); - result += addSwingHToString(_.SwingH, kMitsubishiHeavy152SwingHAuto, - kMitsubishiHeavy152SwingHLeftMax, - kMitsubishiHeavy152SwingHLeft, - kMitsubishiHeavy152SwingHMiddle, - kMitsubishiHeavy152SwingHRight, - kMitsubishiHeavy152SwingHRightMax, - kMitsubishiHeavy152SwingHOff, - kMitsubishiHeavy152SwingHLeftRight, - kMitsubishiHeavy152SwingHRightLeft, - // Below are unused. - kMitsubishiHeavy152SwingHAuto, - kMitsubishiHeavy152SwingHAuto); - result += addBoolToString(_.Silent, kSilentStr); - result += addBoolToString(getTurbo(), kTurboStr); - result += addBoolToString(getEcono(), kEconoStr); - result += addBoolToString(_.Night, kNightStr); - result += addBoolToString(_.Filter, kFilterStr); - result += addBoolToString(get3D(), k3DStr); - result += addBoolToString(getClean(), kCleanStr); - return result; -} - - -// Class for decoding and constructing MitsubishiHeavy88 AC messages. - -/// 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? -IRMitsubishiHeavy88Ac::IRMitsubishiHeavy88Ac(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 IRMitsubishiHeavy88Ac::begin(void) { _irsend.begin(); } - -#if SEND_MITSUBISHIHEAVY -/// Send the current internal state as an IR message. -/// @param[in] repeat Nr. of times the message will be repeated. -void IRMitsubishiHeavy88Ac::send(const uint16_t repeat) { - _irsend.sendMitsubishiHeavy88(getRaw(), kMitsubishiHeavy88StateLength, - repeat); -} -#endif // SEND_MITSUBISHIHEAVY - -/// Reset the state of the remote to a known good state/sequence. -void IRMitsubishiHeavy88Ac::stateReset(void) { - std::memcpy(_.raw, kMitsubishiHeavyZjsSig, kMitsubishiHeavySigLength); - for (uint8_t i = kMitsubishiHeavySigLength; i < kMitsubishiHeavy88StateLength; - i++) _.raw[i] = 0; -} - -/// 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 *IRMitsubishiHeavy88Ac::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 IRMitsubishiHeavy88Ac::setRaw(const uint8_t *data) { - std::memcpy(_.raw, data, kMitsubishiHeavy88StateLength); -} - -/// Set the requested power state of the A/C to on. -void IRMitsubishiHeavy88Ac::on(void) { setPower(true); } - -/// Set the requested power state of the A/C to off. -void IRMitsubishiHeavy88Ac::off(void) { setPower(false); } - -/// Change the power setting. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRMitsubishiHeavy88Ac::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 IRMitsubishiHeavy88Ac::getPower(void) const { - return _.Power; -} - -/// Set the temperature. -/// @param[in] temp The temperature in degrees celsius. -void IRMitsubishiHeavy88Ac::setTemp(const uint8_t temp) { - uint8_t newtemp = temp; - newtemp = std::min(newtemp, kMitsubishiHeavyMaxTemp); - newtemp = std::max(newtemp, kMitsubishiHeavyMinTemp); - _.Temp = newtemp - kMitsubishiHeavyMinTemp; -} - -/// Get the current temperature setting. -/// @return The current setting for temp. in degrees celsius. -uint8_t IRMitsubishiHeavy88Ac::getTemp(void) const { - return _.Temp + kMitsubishiHeavyMinTemp; -} - -/// Set the speed of the fan. -/// @param[in] speed The desired setting. -void IRMitsubishiHeavy88Ac::setFan(const uint8_t speed) { - uint8_t newspeed = speed; - switch (speed) { - case kMitsubishiHeavy88FanLow: - case kMitsubishiHeavy88FanMed: - case kMitsubishiHeavy88FanHigh: - case kMitsubishiHeavy88FanTurbo: - case kMitsubishiHeavy88FanEcono: break; - default: newspeed = kMitsubishiHeavy88FanAuto; - } - _.Fan = newspeed; -} - -/// Get the current fan speed setting. -/// @return The current fan speed/mode. -uint8_t IRMitsubishiHeavy88Ac::getFan(void) const { - return _.Fan; -} - -/// Set the operating mode of the A/C. -/// @param[in] mode The desired operating mode. -void IRMitsubishiHeavy88Ac::setMode(const uint8_t mode) { - uint8_t newmode = mode; - switch (mode) { - case kMitsubishiHeavyCool: - case kMitsubishiHeavyDry: - case kMitsubishiHeavyFan: - case kMitsubishiHeavyHeat: - break; - default: - newmode = kMitsubishiHeavyAuto; - } - _.Mode = newmode; -} - -/// Get the operating mode setting of the A/C. -/// @return The current operating mode setting. -uint8_t IRMitsubishiHeavy88Ac::getMode(void) const { - return _.Mode; -} - -/// Set the Vertical Swing mode of the A/C. -/// @param[in] pos The position/mode to set the swing to. -void IRMitsubishiHeavy88Ac::setSwingVertical(const uint8_t pos) { - uint8_t newpos; - switch (pos) { - case kMitsubishiHeavy88SwingVAuto: - case kMitsubishiHeavy88SwingVHighest: - case kMitsubishiHeavy88SwingVHigh: - case kMitsubishiHeavy88SwingVMiddle: - case kMitsubishiHeavy88SwingVLow: - case kMitsubishiHeavy88SwingVLowest: newpos = pos; break; - default: newpos = kMitsubishiHeavy88SwingVOff; - } - _.SwingV5 = newpos; - _.SwingV7 = (newpos >> kMitsubishiHeavy88SwingVByte5Size); -} - -/// Get the Vertical Swing mode of the A/C. -/// @return The native position/mode setting. -uint8_t IRMitsubishiHeavy88Ac::getSwingVertical(void) const { - return _.SwingV5 | (_.SwingV7 << kMitsubishiHeavy88SwingVByte5Size); -} - -/// Set the Horizontal Swing mode of the A/C. -/// @param[in] pos The position/mode to set the swing to. -void IRMitsubishiHeavy88Ac::setSwingHorizontal(const uint8_t pos) { - uint8_t newpos; - switch (pos) { - case kMitsubishiHeavy88SwingHAuto: - case kMitsubishiHeavy88SwingHLeftMax: - case kMitsubishiHeavy88SwingHLeft: - case kMitsubishiHeavy88SwingHMiddle: - case kMitsubishiHeavy88SwingHRight: - case kMitsubishiHeavy88SwingHRightMax: - case kMitsubishiHeavy88SwingHLeftRight: - case kMitsubishiHeavy88SwingHRightLeft: - case kMitsubishiHeavy88SwingH3D: newpos = pos; break; - default: newpos = kMitsubishiHeavy88SwingHOff; - } - _.SwingH1 = newpos; - _.SwingH2 = (newpos >> kMitsubishiHeavy88SwingHSize); -} - -/// Get the Horizontal Swing mode of the A/C. -/// @return The native position/mode setting. -uint8_t IRMitsubishiHeavy88Ac::getSwingHorizontal(void) const { - return _.SwingH1 | (_.SwingH2 << kMitsubishiHeavy88SwingHSize); -} - -/// Set the Turbo mode of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRMitsubishiHeavy88Ac::setTurbo(const bool on) { - if (on) - setFan(kMitsubishiHeavy88FanTurbo); - else if (getTurbo()) setFan(kMitsubishiHeavy88FanAuto); -} - -/// Get the Turbo mode of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRMitsubishiHeavy88Ac::getTurbo(void) const { - return _.Fan == kMitsubishiHeavy88FanTurbo; -} - -/// Set the Economical mode of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRMitsubishiHeavy88Ac::setEcono(const bool on) { - if (on) - setFan(kMitsubishiHeavy88FanEcono); - else if (getEcono()) setFan(kMitsubishiHeavy88FanAuto); -} - -/// Get the Economical mode of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRMitsubishiHeavy88Ac::getEcono(void) const { - return _.Fan == kMitsubishiHeavy88FanEcono; -} - -/// Set the 3D mode of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRMitsubishiHeavy88Ac::set3D(const bool on) { - if (on) - setSwingHorizontal(kMitsubishiHeavy88SwingH3D); - else if (get3D()) - setSwingHorizontal(kMitsubishiHeavy88SwingHOff); -} - -/// Get the 3D mode of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRMitsubishiHeavy88Ac::get3D(void) const { - return getSwingHorizontal() == kMitsubishiHeavy88SwingH3D; -} - -/// Set the Clean mode of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRMitsubishiHeavy88Ac::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 IRMitsubishiHeavy88Ac::getClean(void) const { - return _.Clean; -} - -/// Verify the given state has a ZJ-S signature. -/// @param[in] state A ptr to a state to be checked. -/// @return true, the check passed. Otherwise, false. -bool IRMitsubishiHeavy88Ac::checkZjsSig(const uint8_t *state) { - for (uint8_t i = 0; i < kMitsubishiHeavySigLength; i++) - if (state[i] != kMitsubishiHeavyZjsSig[i]) return false; - return true; -} - -/// Calculate the checksum for the current internal state of the remote. -/// Note: Technically it has no checksum, but does have inverted byte pairs. -void IRMitsubishiHeavy88Ac::checksum(void) { - const uint8_t kOffset = kMitsubishiHeavySigLength - 2; - invertBytePairs(_.raw + kOffset, kMitsubishiHeavy88StateLength - kOffset); -} - -/// 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 state array. -/// @return true, if the state has a valid checksum. Otherwise, false. -/// Note: Technically it has no checksum, but does have inverted byte pairs. -bool IRMitsubishiHeavy88Ac::validChecksum(const uint8_t *state, - const uint16_t length) { - return IRMitsubishiHeavy152Ac::validChecksum(state, length); -} - -/// 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 IRMitsubishiHeavy88Ac::convertMode(const stdAc::opmode_t mode) { - return IRMitsubishiHeavy152Ac::convertMode(mode); -} - -/// 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 IRMitsubishiHeavy88Ac::convertFan(const stdAc::fanspeed_t speed) { - switch (speed) { - // Assumes Econo is slower than Low. - case stdAc::fanspeed_t::kMin: return kMitsubishiHeavy88FanEcono; - case stdAc::fanspeed_t::kLow: return kMitsubishiHeavy88FanLow; - case stdAc::fanspeed_t::kMedium: return kMitsubishiHeavy88FanMed; - case stdAc::fanspeed_t::kHigh: return kMitsubishiHeavy88FanHigh; - case stdAc::fanspeed_t::kMax: return kMitsubishiHeavy88FanTurbo; - default: return kMitsubishiHeavy88FanAuto; - } -} - -/// 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 IRMitsubishiHeavy88Ac::convertSwingV(const stdAc::swingv_t position) { - switch (position) { - case stdAc::swingv_t::kAuto: return kMitsubishiHeavy88SwingVAuto; - case stdAc::swingv_t::kHighest: return kMitsubishiHeavy88SwingVHighest; - case stdAc::swingv_t::kHigh: return kMitsubishiHeavy88SwingVHigh; - case stdAc::swingv_t::kMiddle: return kMitsubishiHeavy88SwingVMiddle; - case stdAc::swingv_t::kLow: return kMitsubishiHeavy88SwingVLow; - case stdAc::swingv_t::kLowest: return kMitsubishiHeavy88SwingVLowest; - default: return kMitsubishiHeavy88SwingVOff; - } -} - -/// Convert a stdAc::swingh_t enum into it's native setting. -/// @param[in] position The enum to be converted. -/// @return The native equivalent of the enum. -uint8_t IRMitsubishiHeavy88Ac::convertSwingH(const stdAc::swingh_t position) { - switch (position) { - case stdAc::swingh_t::kAuto: return kMitsubishiHeavy88SwingHAuto; - case stdAc::swingh_t::kLeftMax: return kMitsubishiHeavy88SwingHLeftMax; - case stdAc::swingh_t::kLeft: return kMitsubishiHeavy88SwingHLeft; - case stdAc::swingh_t::kMiddle: return kMitsubishiHeavy88SwingHMiddle; - case stdAc::swingh_t::kRight: return kMitsubishiHeavy88SwingHRight; - case stdAc::swingh_t::kRightMax: return kMitsubishiHeavy88SwingHRightMax; - default: return kMitsubishiHeavy88SwingHOff; - } -} - -/// 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 IRMitsubishiHeavy88Ac::toCommonFanSpeed(const uint8_t speed) { - switch (speed) { - case kMitsubishiHeavy88FanTurbo: return stdAc::fanspeed_t::kMax; - case kMitsubishiHeavy88FanHigh: return stdAc::fanspeed_t::kHigh; - case kMitsubishiHeavy88FanMed: return stdAc::fanspeed_t::kMedium; - case kMitsubishiHeavy88FanLow: return stdAc::fanspeed_t::kLow; - case kMitsubishiHeavy88FanEcono: return stdAc::fanspeed_t::kMin; - default: return stdAc::fanspeed_t::kAuto; - } -} - -/// Convert a native horizontal swing postion to it's common equivalent. -/// @param[in] pos A native position to convert. -/// @return The common horizontal swing position. -stdAc::swingh_t IRMitsubishiHeavy88Ac::toCommonSwingH(const uint8_t pos) { - switch (pos) { - case kMitsubishiHeavy88SwingHLeftMax: return stdAc::swingh_t::kLeftMax; - case kMitsubishiHeavy88SwingHLeft: return stdAc::swingh_t::kLeft; - case kMitsubishiHeavy88SwingHMiddle: return stdAc::swingh_t::kMiddle; - case kMitsubishiHeavy88SwingHRight: return stdAc::swingh_t::kRight; - case kMitsubishiHeavy88SwingHRightMax: return stdAc::swingh_t::kRightMax; - case kMitsubishiHeavy88SwingHOff: return stdAc::swingh_t::kOff; - default: return stdAc::swingh_t::kAuto; - } -} - -/// 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 IRMitsubishiHeavy88Ac::toCommonSwingV(const uint8_t pos) { - switch (pos) { - case kMitsubishiHeavy88SwingVHighest: return stdAc::swingv_t::kHighest; - case kMitsubishiHeavy88SwingVHigh: return stdAc::swingv_t::kHigh; - case kMitsubishiHeavy88SwingVMiddle: return stdAc::swingv_t::kMiddle; - case kMitsubishiHeavy88SwingVLow: return stdAc::swingv_t::kLow; - case kMitsubishiHeavy88SwingVLowest: return stdAc::swingv_t::kLowest; - case kMitsubishiHeavy88SwingVOff: return stdAc::swingv_t::kOff; - default: return stdAc::swingv_t::kAuto; - } -} - -/// Convert the current internal state into its stdAc::state_t equivalent. -/// @return The stdAc equivalent of the native settings. -stdAc::state_t IRMitsubishiHeavy88Ac::toCommon(void) const { - stdAc::state_t result; - result.protocol = decode_type_t::MITSUBISHI_HEAVY_88; - result.model = -1; // No models used. - result.power = _.Power; - result.mode = IRMitsubishiHeavy152Ac::toCommonMode(_.Mode); - result.celsius = true; - result.degrees = getTemp(); - result.fanspeed = toCommonFanSpeed(_.Fan); - result.swingv = toCommonSwingV(getSwingVertical()); - result.swingh = toCommonSwingH(getSwingHorizontal()); - result.turbo = getTurbo(); - result.econo = getEcono(); - result.clean = _.Clean; - // Not supported. - result.quiet = false; - result.filter = false; - result.light = false; - result.beep = false; - result.sleep = -1; - result.clock = -1; - return result; -} - -/// Convert the internal state into a human readable string. -/// @return A string containing the settings in human-readable form. -String IRMitsubishiHeavy88Ac::toString(void) const { - String result = ""; - result.reserve(140); // Reserve some heap for the string to reduce fragging. - result += addBoolToString(_.Power, kPowerStr, false); - result += addModeToString(_.Mode, kMitsubishiHeavyAuto, - kMitsubishiHeavyCool, kMitsubishiHeavyHeat, - kMitsubishiHeavyDry, kMitsubishiHeavyFan); - result += addTempToString(getTemp()); - result += addIntToString(_.Fan, kFanStr); - result += kSpaceLBraceStr; - switch (_.Fan) { - case kMitsubishiHeavy88FanAuto: - result += kAutoStr; - break; - case kMitsubishiHeavy88FanHigh: - result += kHighStr; - break; - case kMitsubishiHeavy88FanLow: - result += kLowStr; - break; - case kMitsubishiHeavy88FanMed: - result += kMedStr; - break; - case kMitsubishiHeavy88FanEcono: - result += kEconoStr; - break; - case kMitsubishiHeavy88FanTurbo: - result += kTurboStr; - break; - default: - result += kUnknownStr; - } - result += ')'; - result += addSwingVToString(getSwingVertical(), kMitsubishiHeavy88SwingVAuto, - kMitsubishiHeavy88SwingVHighest, - kMitsubishiHeavy88SwingVHigh, - kMitsubishiHeavy88SwingVAuto, // UpperMid unused - kMitsubishiHeavy88SwingVMiddle, - kMitsubishiHeavy88SwingVAuto, // LowerMid unused - kMitsubishiHeavy88SwingVLow, - kMitsubishiHeavy88SwingVLowest, - kMitsubishiHeavy88SwingVOff, - // Below are unused. - kMitsubishiHeavy88SwingVAuto, - kMitsubishiHeavy88SwingVAuto, - kMitsubishiHeavy88SwingVAuto); - result += addSwingHToString(getSwingHorizontal(), - kMitsubishiHeavy88SwingHAuto, - kMitsubishiHeavy88SwingHLeftMax, - kMitsubishiHeavy88SwingHLeft, - kMitsubishiHeavy88SwingHMiddle, - kMitsubishiHeavy88SwingHRight, - kMitsubishiHeavy88SwingHRightMax, - kMitsubishiHeavy88SwingHOff, - kMitsubishiHeavy88SwingHLeftRight, - kMitsubishiHeavy88SwingHRightLeft, - kMitsubishiHeavy88SwingH3D, - // Below are unused. - kMitsubishiHeavy88SwingHAuto); - result += addBoolToString(getTurbo(), kTurboStr); - result += addBoolToString(getEcono(), kEconoStr); - result += addBoolToString(get3D(), k3DStr); - result += addBoolToString(_.Clean, kCleanStr); - return result; -} - -#if DECODE_MITSUBISHIHEAVY -/// Decode the supplied Mitsubishi Heavy Industries A/C message. -/// Status: BETA / Appears to be working. 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 kMitsubishiHeavy88Bits or kMitsubishiHeavy152Bits (def). -/// @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::decodeMitsubishiHeavy(decode_results* results, uint16_t offset, - const uint16_t nbits, const bool strict) { - if (strict) { - switch (nbits) { - case kMitsubishiHeavy88Bits: - case kMitsubishiHeavy152Bits: - break; - default: - return false; // Not what is expected - } - } - - uint16_t used; - used = matchGeneric(results->rawbuf + offset, results->state, - results->rawlen - offset, nbits, - kMitsubishiHeavyHdrMark, kMitsubishiHeavyHdrSpace, - kMitsubishiHeavyBitMark, kMitsubishiHeavyOneSpace, - kMitsubishiHeavyBitMark, kMitsubishiHeavyZeroSpace, - kMitsubishiHeavyBitMark, kMitsubishiHeavyGap, true, - _tolerance, 0, false); - if (used == 0) return false; - offset += used; - // Compliance - switch (nbits) { - case kMitsubishiHeavy88Bits: - if (strict && !(IRMitsubishiHeavy88Ac::checkZjsSig(results->state) && - IRMitsubishiHeavy88Ac::validChecksum(results->state))) - return false; - results->decode_type = MITSUBISHI_HEAVY_88; - break; - case kMitsubishiHeavy152Bits: - if (strict && !(IRMitsubishiHeavy152Ac::checkZmsSig(results->state) && - IRMitsubishiHeavy152Ac::validChecksum(results->state))) - return false; - results->decode_type = MITSUBISHI_HEAVY_152; - break; - default: - return false; - } - - // Success - 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_MITSUBISHIHEAVY diff --git a/lib/IRremoteESP8266/src/ir_MitsubishiHeavy.h b/lib/IRremoteESP8266/src/ir_MitsubishiHeavy.h index b1059f12fa..71b0138476 100644 --- a/lib/IRremoteESP8266/src/ir_MitsubishiHeavy.h +++ b/lib/IRremoteESP8266/src/ir_MitsubishiHeavy.h @@ -23,10 +23,10 @@ #ifndef UNIT_TEST #include #endif -#include "IRremoteESP8266.h" -#include "IRsend.h" +#include "../src/IRremoteESP8266.h" +#include "../src/IRsend.h" #ifdef UNIT_TEST -#include "IRsend_test.h" +#include "../src/IRsend_test.h" #endif /// Native representation of a Mitsubishi Heavy 152-bit A/C message. diff --git a/lib/IRremoteESP8266/src/ir_Multibrackets.cpp b/lib/IRremoteESP8266/src/ir_Multibrackets.cpp deleted file mode 100644 index 2367b49bc9..0000000000 --- a/lib/IRremoteESP8266/src/ir_Multibrackets.cpp +++ /dev/null @@ -1,115 +0,0 @@ -// 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 diff --git a/lib/IRremoteESP8266/src/ir_NEC.cpp b/lib/IRremoteESP8266/src/ir_NEC.cpp deleted file mode 100644 index 9d4af76405..0000000000 --- a/lib/IRremoteESP8266/src/ir_NEC.cpp +++ /dev/null @@ -1,140 +0,0 @@ -// 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 -#include -#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) diff --git a/lib/IRremoteESP8266/src/ir_NEC.h b/lib/IRremoteESP8266/src/ir_NEC.h index 95da064b78..d7603304e1 100644 --- a/lib/IRremoteESP8266/src/ir_NEC.h +++ b/lib/IRremoteESP8266/src/ir_NEC.h @@ -19,7 +19,7 @@ #define IR_NEC_H_ #include -#include "IRremoteESP8266.h" +#include "../src/IRremoteESP8266.h" // Constants const uint16_t kNecTick = 560; diff --git a/lib/IRremoteESP8266/src/ir_Neoclima.cpp b/lib/IRremoteESP8266/src/ir_Neoclima.cpp deleted file mode 100644 index ff26e6c643..0000000000 --- a/lib/IRremoteESP8266/src/ir_Neoclima.cpp +++ /dev/null @@ -1,608 +0,0 @@ -// 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 -#include -#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 diff --git a/lib/IRremoteESP8266/src/ir_Neoclima.h b/lib/IRremoteESP8266/src/ir_Neoclima.h index 90eaa029a9..16d838b385 100644 --- a/lib/IRremoteESP8266/src/ir_Neoclima.h +++ b/lib/IRremoteESP8266/src/ir_Neoclima.h @@ -20,10 +20,10 @@ #ifndef UNIT_TEST #include #endif -#include "IRremoteESP8266.h" -#include "IRsend.h" +#include "../src/IRremoteESP8266.h" +#include "../src/IRsend.h" #ifdef UNIT_TEST -#include "IRsend_test.h" +#include "../src/IRsend_test.h" #endif /// Native representation of a Neoclima A/C message. diff --git a/lib/IRremoteESP8266/src/ir_Nikai.cpp b/lib/IRremoteESP8266/src/ir_Nikai.cpp deleted file mode 100644 index 01c789d70e..0000000000 --- a/lib/IRremoteESP8266/src/ir_Nikai.cpp +++ /dev/null @@ -1,74 +0,0 @@ -// 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 -#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 diff --git a/lib/IRremoteESP8266/src/ir_Panasonic.cpp b/lib/IRremoteESP8266/src/ir_Panasonic.cpp deleted file mode 100644 index e2add03f1f..0000000000 --- a/lib/IRremoteESP8266/src/ir_Panasonic.cpp +++ /dev/null @@ -1,1341 +0,0 @@ -// Copyright 2015 Kristian Lauszus -// Copyright 2017, 2018 David Conran - -/// @file -/// @brief Support for Panasonic protocols. -/// Panasonic protocol originally added by Kristian Lauszus -/// (Thanks to zenwheel and other people at the original blog post) -/// @see Panasonic https://github.com/z3t0/Arduino-IRremote -/// @see http://www.remotecentral.com/cgi-bin/mboard/rc-pronto/thread.cgi?2615 -/// @see Panasonic A/C support heavily influenced by https://github.com/ToniA/ESPEasy/blob/HeatpumpIR/lib/HeatpumpIR/PanasonicHeatpumpIR.cpp -/// Panasonic A/C Clock & Timer support: -/// Reverse Engineering by MikkelTb -/// Code by crankyoldgit - -#include "ir_Panasonic.h" -#include -#include -#ifndef ARDUINO -#include -#endif -#include "IRrecv.h" -#include "IRsend.h" -#include "IRtext.h" -#include "IRutils.h" - -// Constants -/// @see http://www.remotecentral.com/cgi-bin/mboard/rc-pronto/thread.cgi?26152 -const uint16_t kPanasonicHdrMark = 3456; ///< uSeconds. -const uint16_t kPanasonicHdrSpace = 1728; ///< uSeconds. -const uint16_t kPanasonicBitMark = 432; ///< uSeconds. -const uint16_t kPanasonicOneSpace = 1296; ///< uSeconds. -const uint16_t kPanasonicZeroSpace = 432; ///< uSeconds. -const uint32_t kPanasonicMinCommandLength = 163296; ///< uSeconds. -const uint16_t kPanasonicEndGap = 5000; ///< uSeconds. See #245 -const uint32_t kPanasonicMinGap = 74736; ///< uSeconds. - -const uint16_t kPanasonicAcSectionGap = 10000; ///< uSeconds. -const uint16_t kPanasonicAcSection1Length = 8; -const uint32_t kPanasonicAcMessageGap = kDefaultMessageGap; // Just a guess. - -const uint16_t kPanasonicAc32HdrMark = 3543; ///< uSeconds. -const uint16_t kPanasonicAc32BitMark = 920; ///< uSeconds. -const uint16_t kPanasonicAc32HdrSpace = 3450; ///< uSeconds. -const uint16_t kPanasonicAc32OneSpace = 2575; ///< uSeconds. -const uint16_t kPanasonicAc32ZeroSpace = 828; ///< uSeconds. -const uint16_t kPanasonicAc32SectionGap = 13946; ///< uSeconds. -const uint8_t kPanasonicAc32Sections = 2; -const uint8_t kPanasonicAc32BlocksPerSection = 2; - -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::minsToString; -using irutils::setBit; -using irutils::setBits; - -// Used by Denon as well. -#if (SEND_PANASONIC || SEND_DENON) -/// Send a Panasonic 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 This protocol is a modified version of Kaseikyo. -/// @note Use this method if you want to send the results of `decodePanasonic`. -void IRsend::sendPanasonic64(const uint64_t data, const uint16_t nbits, - const uint16_t repeat) { - sendGeneric(kPanasonicHdrMark, kPanasonicHdrSpace, kPanasonicBitMark, - kPanasonicOneSpace, kPanasonicBitMark, kPanasonicZeroSpace, - kPanasonicBitMark, kPanasonicMinGap, kPanasonicMinCommandLength, - data, nbits, kPanasonicFreq, true, repeat, 50); -} - -/// Send a Panasonic formatted message. -/// Status: STABLE, but DEPRECATED -/// @deprecated This is only for legacy use only, please use `sendPanasonic64()` -/// instead. -/// @param[in] address The 16-bit manufacturer code. -/// @param[in] data The 32-bit data portion of 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 a modified version of Kaseikyo. -void IRsend::sendPanasonic(const uint16_t address, const uint32_t data, - const uint16_t nbits, const uint16_t repeat) { - sendPanasonic64(((uint64_t)address << 32) | (uint64_t)data, nbits, repeat); -} - -/// Calculate the raw Panasonic data based on device, subdevice, & function. -/// Status: STABLE / Should be working. -/// @param[in] manufacturer A 16-bit manufacturer code. e.g. 0x4004 is Panasonic -/// @param[in] device An 8-bit code. -/// @param[in] subdevice An 8-bit code. -/// @param[in] function An 8-bit code. -/// @return A value suitable for use with `sendPanasonic64()`. -/// @note Panasonic 48-bit protocol is a modified version of Kaseikyo. -/// @see http://www.remotecentral.com/cgi-bin/mboard/rc-pronto/thread.cgi?2615 -uint64_t IRsend::encodePanasonic(const uint16_t manufacturer, - const uint8_t device, - const uint8_t subdevice, - const uint8_t function) { - uint8_t checksum = device ^ subdevice ^ function; - return (((uint64_t)manufacturer << 32) | ((uint64_t)device << 24) | - ((uint64_t)subdevice << 16) | ((uint64_t)function << 8) | checksum); -} -#endif // (SEND_PANASONIC || SEND_DENON) - -// Used by Denon as well. -#if (DECODE_PANASONIC || DECODE_DENON) -/// Decode the supplied Panasonic message. -/// Status: STABLE / Should 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] manufacturer A 16-bit manufacturer code. e.g. 0x4004 is Panasonic -/// @param[in] strict Flag indicating if we should perform strict matching. -/// @return True if it can decode it, false if it can't. -/// @warning Results to be used with `sendPanasonic64()`, not `sendPanasonic()`. -/// @note Panasonic 48-bit protocol is a modified version of Kaseikyo. -/// @see http://www.remotecentral.com/cgi-bin/mboard/rc-pronto/thread.cgi?2615 -/// @see http://www.hifi-remote.com/wiki/index.php?title=Panasonic -bool IRrecv::decodePanasonic(decode_results *results, uint16_t offset, - const uint16_t nbits, const bool strict, - const uint32_t manufacturer) { - if (strict && nbits != kPanasonicBits) - return false; // Request is out of spec. - - uint64_t data = 0; - - // Match Header + Data + Footer - if (!matchGeneric(results->rawbuf + offset, &data, - results->rawlen - offset, nbits, - kPanasonicHdrMark, kPanasonicHdrSpace, - kPanasonicBitMark, kPanasonicOneSpace, - kPanasonicBitMark, kPanasonicZeroSpace, - kPanasonicBitMark, kPanasonicEndGap, true)) return false; - // Compliance - uint32_t address = data >> 32; - uint32_t command = data; - if (strict) { - if (address != manufacturer) // Verify the Manufacturer code. - return false; - // Verify the checksum. - uint8_t checksumOrig = data; - uint8_t checksumCalc = (data >> 24) ^ (data >> 16) ^ (data >> 8); - if (checksumOrig != checksumCalc) return false; - } - - // Success - results->value = data; - results->address = address; - results->command = command; - results->decode_type = decode_type_t::PANASONIC; - results->bits = nbits; - return true; -} -#endif // (DECODE_PANASONIC || DECODE_DENON) - -#if SEND_PANASONIC_AC -/// Send a Panasonic A/C message. -/// Status: STABLE / Work with real device(s). -/// @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::sendPanasonicAC(const uint8_t data[], const uint16_t nbytes, - const uint16_t repeat) { - if (nbytes < kPanasonicAcSection1Length) return; - for (uint16_t r = 0; r <= repeat; r++) { - // First section. (8 bytes) - sendGeneric(kPanasonicHdrMark, kPanasonicHdrSpace, kPanasonicBitMark, - kPanasonicOneSpace, kPanasonicBitMark, kPanasonicZeroSpace, - kPanasonicBitMark, kPanasonicAcSectionGap, data, - kPanasonicAcSection1Length, kPanasonicFreq, false, 0, 50); - // First section. (The rest of the data bytes) - sendGeneric(kPanasonicHdrMark, kPanasonicHdrSpace, kPanasonicBitMark, - kPanasonicOneSpace, kPanasonicBitMark, kPanasonicZeroSpace, - kPanasonicBitMark, kPanasonicAcMessageGap, - data + kPanasonicAcSection1Length, - nbytes - kPanasonicAcSection1Length, kPanasonicFreq, false, 0, - 50); - } -} -#endif // SEND_PANASONIC_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? -IRPanasonicAc::IRPanasonicAc(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 IRPanasonicAc::stateReset(void) { - memcpy(remote_state, kPanasonicKnownGoodState, kPanasonicAcStateLength); - _temp = 25; // An initial saved desired temp. Completely made up. - _swingh = kPanasonicAcSwingHMiddle; // A similar made up value for H Swing. -} - -/// Set up hardware to be able to send a message. -void IRPanasonicAc::begin(void) { _irsend.begin(); } - -/// 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 IRPanasonicAc::validChecksum(const uint8_t *state, const uint16_t length) { - if (length < 2) return false; // 1 byte of data can't have a checksum. - return (state[length - 1] == - sumBytes(state, length - 1, kPanasonicAcChecksumInit)); -} - -/// Calculate the checksum for a given state. -/// @param[in] state The value to calc the checksum of. -/// @param[in] length The size/length of the state. -/// @return The calculated checksum value. -uint8_t IRPanasonicAc::calcChecksum(const uint8_t *state, - const uint16_t length) { - return sumBytes(state, length - 1, kPanasonicAcChecksumInit); -} - -/// Calculate and set the checksum values for the internal state. -/// @param[in] length The size/length of the state. -void IRPanasonicAc::fixChecksum(const uint16_t length) { - remote_state[length - 1] = calcChecksum(remote_state, length); -} - -#if SEND_PANASONIC_AC -/// Send the current internal state as an IR message. -/// @param[in] repeat Nr. of times the message will be repeated. -void IRPanasonicAc::send(const uint16_t repeat) { - _irsend.sendPanasonicAC(getRaw(), kPanasonicAcStateLength, repeat); -} -#endif // SEND_PANASONIC_AC - -/// Set the model of the A/C to emulate. -/// @param[in] model The enum of the appropriate model. -void IRPanasonicAc::setModel(const panasonic_ac_remote_model_t model) { - switch (model) { - case panasonic_ac_remote_model_t::kPanasonicDke: - case panasonic_ac_remote_model_t::kPanasonicJke: - case panasonic_ac_remote_model_t::kPanasonicLke: - case panasonic_ac_remote_model_t::kPanasonicNke: - case panasonic_ac_remote_model_t::kPanasonicCkp: - case panasonic_ac_remote_model_t::kPanasonicRkr: break; - // Only proceed if we know what to do. - default: return; - } - // clear & set the various bits and bytes. - remote_state[13] &= 0xF0; - remote_state[17] = 0x00; - remote_state[21] &= 0b11101111; - remote_state[23] = 0x81; - remote_state[25] = 0x00; - - switch (model) { - case kPanasonicLke: - remote_state[13] |= 0x02; - remote_state[17] = 0x06; - break; - case kPanasonicDke: - remote_state[23] = 0x01; - remote_state[25] = 0x06; - // Has to be done last as setSwingHorizontal has model check built-in - setSwingHorizontal(_swingh); - break; - case kPanasonicNke: - remote_state[17] = 0x06; - break; - case kPanasonicJke: - break; - case kPanasonicCkp: - remote_state[21] |= 0x10; - remote_state[23] = 0x01; - break; - case kPanasonicRkr: - remote_state[13] |= 0x08; - remote_state[23] = 0x89; - default: - break; - } - // Reset the Ion filter. - setIon(getIon()); -} - -/// Get/Detect the model of the A/C. -/// @return The enum of the compatible model. -panasonic_ac_remote_model_t IRPanasonicAc::getModel(void) { - if (remote_state[23] == 0x89) return kPanasonicRkr; - if (remote_state[17] == 0x00) { - if ((remote_state[21] & 0x10) && (remote_state[23] & 0x01)) - return panasonic_ac_remote_model_t::kPanasonicCkp; - if (remote_state[23] & 0x80) - return panasonic_ac_remote_model_t::kPanasonicJke; - } - if (remote_state[17] == 0x06 && (remote_state[13] & 0x0F) == 0x02) - return panasonic_ac_remote_model_t::kPanasonicLke; - if (remote_state[23] == 0x01) - return panasonic_ac_remote_model_t::kPanasonicDke; - if (remote_state[17] == 0x06) - return panasonic_ac_remote_model_t::kPanasonicNke; - return panasonic_ac_remote_model_t::kPanasonicUnknown; // Default -} - -/// 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 *IRPanasonicAc::getRaw(void) { - fixChecksum(); - return remote_state; -} - -/// Set the internal state from a valid code for this protocol. -/// @param[in] state A valid code for this protocol. -void IRPanasonicAc::setRaw(const uint8_t state[]) { - memcpy(remote_state, state, kPanasonicAcStateLength); -} - -/// Control the power state of the A/C unit. -/// @param[in] on true, the setting is on. false, the setting is off. -/// @warning For CKP models, the remote has no memory of the power state the A/C -/// unit should be in. For those models setting this on/true will toggle the -/// power state of the Panasonic A/C unit with the next message. -/// e.g. If the A/C unit is already on, setPower(true) will turn it off. -/// If the A/C unit is already off, setPower(true) will turn it on. -/// `setPower(false)` will leave the A/C power state as it was. -/// For all other models, setPower(true) should set the internal state to -/// turn it on, and setPower(false) should turn it off. -void IRPanasonicAc::setPower(const bool on) { - setBit(&remote_state[13], kPanasonicAcPowerOffset, on); -} - -/// Get the A/C power state of the remote. -/// @return true, the setting is on. false, the setting is off. -/// @warning Except for CKP models, where it returns if the power state will be -/// toggled on the A/C unit when the next message is sent. -bool IRPanasonicAc::getPower(void) { - return GETBIT8(remote_state[13], kPanasonicAcPowerOffset); -} - -/// Change the power setting to On. -void IRPanasonicAc::on(void) { setPower(true); } - -/// Change the power setting to Off. -void IRPanasonicAc::off(void) { setPower(false); } - -/// Get the operating mode setting of the A/C. -/// @return The current operating mode setting. -uint8_t IRPanasonicAc::getMode(void) { - return GETBITS8(remote_state[13], kHighNibble, kModeBitsSize); -} - -/// Set the operating mode of the A/C. -/// @param[in] desired The desired operating mode. -void IRPanasonicAc::setMode(const uint8_t desired) { - uint8_t mode = kPanasonicAcAuto; // Default to Auto mode. - switch (desired) { - case kPanasonicAcFan: - // Allegedly Fan mode has a temperature of 27. - setTemp(kPanasonicAcFanModeTemp, false); - mode = desired; - break; - case kPanasonicAcAuto: - case kPanasonicAcCool: - case kPanasonicAcHeat: - case kPanasonicAcDry: - mode = desired; - // Set the temp to the saved temp, just incase our previous mode was Fan. - setTemp(_temp); - break; - } - remote_state[13] &= 0x0F; // Clear the previous mode bits. - setBits(&remote_state[13], kHighNibble, kModeBitsSize, mode); -} - -/// Get the current temperature setting. -/// @return The current setting for temp. in degrees celsius. -uint8_t IRPanasonicAc::getTemp(void) { - return GETBITS8(remote_state[14], kPanasonicAcTempOffset, - kPanasonicAcTempSize); -} - -/// Set the temperature. -/// @param[in] celsius The temperature in degrees celsius. -/// @param[in] remember: A flag for the class to remember the temperature. -/// @note Automatically safely limits the temp to the operating range supported. -void IRPanasonicAc::setTemp(const uint8_t celsius, const bool remember) { - uint8_t temperature; - temperature = std::max(celsius, kPanasonicAcMinTemp); - temperature = std::min(temperature, kPanasonicAcMaxTemp); - if (remember) _temp = temperature; - setBits(&remote_state[14], kPanasonicAcTempOffset, kPanasonicAcTempSize, - temperature); -} - -/// Get the current vertical swing setting. -/// @return The current position it is set to. -uint8_t IRPanasonicAc::getSwingVertical(void) { - return GETBITS8(remote_state[16], kLowNibble, kNibbleSize); -} - -/// Control the vertical swing setting. -/// @param[in] desired_elevation The position to set the vertical swing to. -void IRPanasonicAc::setSwingVertical(const uint8_t desired_elevation) { - uint8_t elevation = desired_elevation; - if (elevation != kPanasonicAcSwingVAuto) { - elevation = std::max(elevation, kPanasonicAcSwingVHighest); - elevation = std::min(elevation, kPanasonicAcSwingVLowest); - } - setBits(&remote_state[16], kLowNibble, kNibbleSize, elevation); -} - -/// Get the current horizontal swing setting. -/// @return The current position it is set to. -uint8_t IRPanasonicAc::getSwingHorizontal(void) { - return GETBITS8(remote_state[17], kLowNibble, kNibbleSize); -} - -/// Control the horizontal swing setting. -/// @param[in] desired_direction The position to set the horizontal swing to. -void IRPanasonicAc::setSwingHorizontal(const uint8_t desired_direction) { - switch (desired_direction) { - case kPanasonicAcSwingHAuto: - case kPanasonicAcSwingHMiddle: - case kPanasonicAcSwingHFullLeft: - case kPanasonicAcSwingHLeft: - case kPanasonicAcSwingHRight: - case kPanasonicAcSwingHFullRight: break; - // Ignore anything that isn't valid. - default: return; - } - _swingh = desired_direction; // Store the direction for later. - uint8_t direction = desired_direction; - switch (getModel()) { - case kPanasonicDke: - case kPanasonicRkr: - break; - case kPanasonicNke: - case kPanasonicLke: - direction = kPanasonicAcSwingHMiddle; - break; - default: // Ignore everything else. - return; - } - setBits(&remote_state[17], kLowNibble, kNibbleSize, direction); -} - -/// Set the speed of the fan. -/// @param[in] speed The desired setting. -void IRPanasonicAc::setFan(const uint8_t speed) { - switch (speed) { - case kPanasonicAcFanMin: - case kPanasonicAcFanLow: - case kPanasonicAcFanMed: - case kPanasonicAcFanHigh: - case kPanasonicAcFanMax: - case kPanasonicAcFanAuto: - setBits(&remote_state[16], kHighNibble, kNibbleSize, - speed + kPanasonicAcFanDelta); - break; - default: setFan(kPanasonicAcFanAuto); - } -} - -/// Get the current fan speed setting. -/// @return The current fan speed. -uint8_t IRPanasonicAc::getFan(void) { - return GETBITS8(remote_state[16], kHighNibble, kNibbleSize) - - kPanasonicAcFanDelta; -} - -/// Get the Quiet setting of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRPanasonicAc::getQuiet(void) { - switch (getModel()) { - case kPanasonicRkr: - case kPanasonicCkp: - return GETBIT8(remote_state[21], kPanasonicAcQuietCkpOffset); - default: - return GETBIT8(remote_state[21], kPanasonicAcQuietOffset); - } -} - -/// Set the Quiet setting of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRPanasonicAc::setQuiet(const bool on) { - uint8_t offset; - switch (getModel()) { - case kPanasonicRkr: - case kPanasonicCkp: offset = kPanasonicAcQuietCkpOffset; break; - default: offset = kPanasonicAcQuietOffset; - } - if (on) setPowerful(false); // Powerful is mutually exclusive. - setBit(&remote_state[21], offset, on); -} - -/// Get the Powerful (Turbo) setting of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRPanasonicAc::getPowerful(void) { - switch (getModel()) { - case kPanasonicRkr: - case kPanasonicCkp: - return GETBIT8(remote_state[21], kPanasonicAcPowerfulCkpOffset); - default: - return GETBIT8(remote_state[21], kPanasonicAcPowerfulOffset); - } -} - -/// Set the Powerful (Turbo) setting of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRPanasonicAc::setPowerful(const bool on) { - uint8_t offset; - switch (getModel()) { - case kPanasonicRkr: - case kPanasonicCkp: offset = kPanasonicAcPowerfulCkpOffset; break; - default: offset = kPanasonicAcPowerfulOffset; - } - - if (on) setQuiet(false); // Quiet is mutually exclusive. - setBit(&remote_state[21], offset, on); -} - -/// Convert standard (military/24hr) time to nr. of minutes since midnight. -/// @param[in] hours The hours component of the time. -/// @param[in] mins The minutes component of the time. -/// @return The nr of minutes since midnight. -uint16_t IRPanasonicAc::encodeTime(const uint8_t hours, const uint8_t mins) { - return std::min(hours, (uint8_t)23) * 60 + std::min(mins, (uint8_t)59); -} - -/// Get the time from a given pointer location. -/// @param[in] ptr A pointer to a time location in a state. -/// @return The time expressed as nr. of minutes past midnight. -/// @note Internal use only. -uint16_t IRPanasonicAc::_getTime(const uint8_t ptr[]) { - uint16_t result = (GETBITS8( - ptr[1], kLowNibble, kPanasonicAcTimeOverflowSize) << - (kPanasonicAcTimeSize - kPanasonicAcTimeOverflowSize)) + ptr[0]; - if (result == kPanasonicAcTimeSpecial) return 0; - return result; -} - -/// Get the current clock time value. -/// @return The time expressed as nr. of minutes past midnight. -uint16_t IRPanasonicAc::getClock(void) { return _getTime(&remote_state[24]); } - -/// Set the time at a given pointer location. -/// @param[in, out] ptr A pointer to a time location in a state. -/// @param[in] mins_since_midnight The time as nr. of minutes past midnight. -/// @param[in] round_down Do we round to the nearest 10 minute mark? -/// @note Internal use only. -void IRPanasonicAc::_setTime(uint8_t * const ptr, - const uint16_t mins_since_midnight, - const bool round_down) { - uint16_t corrected = std::min(mins_since_midnight, kPanasonicAcTimeMax); - if (round_down) corrected -= corrected % 10; - if (mins_since_midnight == kPanasonicAcTimeSpecial) - corrected = kPanasonicAcTimeSpecial; - ptr[0] = corrected; - setBits(&ptr[1], kLowNibble, kPanasonicAcTimeOverflowSize, - corrected >> (kPanasonicAcTimeSize - kPanasonicAcTimeOverflowSize)); -} - -/// Set the current clock time value. -/// @param[in] mins_since_midnight The time as nr. of minutes past midnight. -void IRPanasonicAc::setClock(const uint16_t mins_since_midnight) { - _setTime(&remote_state[24], mins_since_midnight, false); -} - -/// Get the On Timer time value. -/// @return The time expressed as nr. of minutes past midnight. -uint16_t IRPanasonicAc::getOnTimer(void) { return _getTime(&remote_state[18]); } - -/// Set/Enable the On Timer. -/// @param[in] mins_since_midnight The time as nr. of minutes past midnight. -/// @param[in] enable Do we enable the timer or not? -void IRPanasonicAc::setOnTimer(const uint16_t mins_since_midnight, - const bool enable) { - // Set the timer flag. - setBit(&remote_state[13], kPanasonicAcOnTimerOffset, enable); - // Store the time. - _setTime(&remote_state[18], mins_since_midnight, true); -} - -/// Cancel the On Timer. -void IRPanasonicAc::cancelOnTimer(void) { setOnTimer(0, false); } - -/// Check if the On Timer is Enabled. -/// @return true, the setting is on. false, the setting is off. -bool IRPanasonicAc::isOnTimerEnabled(void) { - return GETBIT8(remote_state[13], kPanasonicAcOnTimerOffset); -} - -/// Get the Off Timer time value. -/// @return The time expressed as nr. of minutes past midnight. -uint16_t IRPanasonicAc::getOffTimer(void) { - uint16_t result = (GETBITS8(remote_state[20], 0, 7) << kNibbleSize) | - GETBITS8(remote_state[19], kHighNibble, kNibbleSize); - if (result == kPanasonicAcTimeSpecial) return 0; - return result; -} - -/// Set/Enable the Off Timer. -/// @param[in] mins_since_midnight The time as nr. of minutes past midnight. -/// @param[in] enable Do we enable the timer or not? -void IRPanasonicAc::setOffTimer(const uint16_t mins_since_midnight, - const bool enable) { - // Ensure its on a 10 minute boundary and no overflow. - uint16_t corrected = std::min(mins_since_midnight, kPanasonicAcTimeMax); - corrected -= corrected % 10; - if (mins_since_midnight == kPanasonicAcTimeSpecial) - corrected = kPanasonicAcTimeSpecial; - // Set the timer flag. - setBit(&remote_state[13], kPanasonicAcOffTimerOffset, enable); - // Store the time. - setBits(&remote_state[19], kHighNibble, kNibbleSize, corrected); - setBits(&remote_state[20], 0, 7, corrected >> kNibbleSize); -} - -/// Cancel the Off Timer. -void IRPanasonicAc::cancelOffTimer(void) { setOffTimer(0, false); } - -/// Check if the Off Timer is Enabled. -/// @return true, the setting is on. false, the setting is off. -bool IRPanasonicAc::isOffTimerEnabled(void) { - return GETBIT8(remote_state[13], kPanasonicAcOffTimerOffset); -} - -/// Get the Ion (filter) setting of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRPanasonicAc::getIon(void) { - switch (getModel()) { - case kPanasonicDke: - return GETBIT8(remote_state[kPanasonicAcIonFilterByte], - kPanasonicAcIonFilterOffset); - default: - return false; - } -} - -/// Set the Ion (filter) setting of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRPanasonicAc::setIon(const bool on) { - if (getModel() == kPanasonicDke) - setBit(&remote_state[kPanasonicAcIonFilterByte], - kPanasonicAcIonFilterOffset, on); -} - -/// 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 IRPanasonicAc::convertMode(const stdAc::opmode_t mode) { - switch (mode) { - case stdAc::opmode_t::kCool: return kPanasonicAcCool; - case stdAc::opmode_t::kHeat: return kPanasonicAcHeat; - case stdAc::opmode_t::kDry: return kPanasonicAcDry; - case stdAc::opmode_t::kFan: return kPanasonicAcFan; - default: return kPanasonicAcAuto; - } -} - -/// 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 IRPanasonicAc::convertFan(const stdAc::fanspeed_t speed) { - switch (speed) { - case stdAc::fanspeed_t::kMin: return kPanasonicAcFanMin; - case stdAc::fanspeed_t::kLow: return kPanasonicAcFanLow; - case stdAc::fanspeed_t::kMedium: return kPanasonicAcFanMed; - case stdAc::fanspeed_t::kHigh: return kPanasonicAcFanHigh; - case stdAc::fanspeed_t::kMax: return kPanasonicAcFanMax; - default: return kPanasonicAcFanAuto; - } -} - -/// Convert a standard A/C vertical swing into its native setting. -/// @param[in] position A stdAc::swingv_t position to convert. -/// @return The equivalent native horizontal swing position. -uint8_t IRPanasonicAc::convertSwingV(const stdAc::swingv_t position) { - switch (position) { - case stdAc::swingv_t::kHighest: - case stdAc::swingv_t::kHigh: - case stdAc::swingv_t::kMiddle: - case stdAc::swingv_t::kLow: - case stdAc::swingv_t::kLowest: return (uint8_t)position; - default: return kPanasonicAcSwingVAuto; - } -} - -/// Convert a standard A/C horizontal swing into its native setting. -/// @param[in] position A stdAc::swingh_t position to convert. -/// @return The equivalent native horizontal swing position. -uint8_t IRPanasonicAc::convertSwingH(const stdAc::swingh_t position) { - switch (position) { - case stdAc::swingh_t::kLeftMax: return kPanasonicAcSwingHFullLeft; - case stdAc::swingh_t::kLeft: return kPanasonicAcSwingHLeft; - case stdAc::swingh_t::kMiddle: return kPanasonicAcSwingHMiddle; - case stdAc::swingh_t::kRight: return kPanasonicAcSwingHRight; - case stdAc::swingh_t::kRightMax: return kPanasonicAcSwingHFullRight; - default: return kPanasonicAcSwingHAuto; - } -} - -/// 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 IRPanasonicAc::toCommonMode(const uint8_t mode) { - switch (mode) { - case kPanasonicAcCool: return stdAc::opmode_t::kCool; - case kPanasonicAcHeat: return stdAc::opmode_t::kHeat; - case kPanasonicAcDry: return stdAc::opmode_t::kDry; - case kPanasonicAcFan: return stdAc::opmode_t::kFan; - default: return stdAc::opmode_t::kAuto; - } -} - -/// Convert a native fan speed into its stdAc equivalent. -/// @param[in] spd The native setting to be converted. -/// @return The stdAc equivalent of the native setting. -stdAc::fanspeed_t IRPanasonicAc::toCommonFanSpeed(const uint8_t spd) { - switch (spd) { - case kPanasonicAcFanMax: return stdAc::fanspeed_t::kMax; - case kPanasonicAcFanHigh: return stdAc::fanspeed_t::kHigh; - case kPanasonicAcFanMed: return stdAc::fanspeed_t::kMedium; - case kPanasonicAcFanLow: return stdAc::fanspeed_t::kLow; - case kPanasonicAcFanMin: return stdAc::fanspeed_t::kMin; - default: return stdAc::fanspeed_t::kAuto; - } -} - -/// Convert a native horizontal swing postion to it's common equivalent. -/// @param[in] pos A native position to convert. -/// @return The common horizontal swing position. -stdAc::swingh_t IRPanasonicAc::toCommonSwingH(const uint8_t pos) { - switch (pos) { - case kPanasonicAcSwingHFullLeft: return stdAc::swingh_t::kLeftMax; - case kPanasonicAcSwingHLeft: return stdAc::swingh_t::kLeft; - case kPanasonicAcSwingHMiddle: return stdAc::swingh_t::kMiddle; - case kPanasonicAcSwingHRight: return stdAc::swingh_t::kRight; - case kPanasonicAcSwingHFullRight: return stdAc::swingh_t::kRightMax; - default: return stdAc::swingh_t::kAuto; - } -} - -/// 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 IRPanasonicAc::toCommonSwingV(const uint8_t pos) { - if (pos >= kPanasonicAcSwingVHighest && pos <= kPanasonicAcSwingVLowest) - return (stdAc::swingv_t)pos; - else - return stdAc::swingv_t::kAuto; -} - -/// Convert the current internal state into its stdAc::state_t equivalent. -/// @return The stdAc equivalent of the native settings. -stdAc::state_t IRPanasonicAc::toCommon(void) { - stdAc::state_t result; - result.protocol = decode_type_t::PANASONIC_AC; - result.model = getModel(); - result.power = getPower(); - result.mode = toCommonMode(getMode()); - result.celsius = true; - result.degrees = getTemp(); - result.fanspeed = toCommonFanSpeed(getFan()); - result.swingv = toCommonSwingV(getSwingVertical()); - result.swingh = toCommonSwingH(getSwingHorizontal()); - result.quiet = getQuiet(); - result.turbo = getPowerful(); - result.filter = getIon(); - // Not supported. - result.econo = false; - result.clean = false; - result.light = false; - result.beep = false; - result.sleep = -1; - result.clock = -1; - return result; -} - -/// Convert the internal state into a human readable string. -/// @return A string containing the settings in human-readable form. -String IRPanasonicAc::toString(void) { - String result = ""; - result.reserve(180); // Reserve some heap for the string to reduce fragging. - result += addModelToString(decode_type_t::PANASONIC_AC, getModel(), false); - result += addBoolToString(getPower(), kPowerStr); - result += addModeToString(getMode(), kPanasonicAcAuto, kPanasonicAcCool, - kPanasonicAcHeat, kPanasonicAcDry, kPanasonicAcFan); - result += addTempToString(getTemp()); - result += addFanToString(getFan(), kPanasonicAcFanHigh, kPanasonicAcFanLow, - kPanasonicAcFanAuto, kPanasonicAcFanMin, - kPanasonicAcFanMed, kPanasonicAcFanMax); - result += addSwingVToString(getSwingVertical(), kPanasonicAcSwingVAuto, - kPanasonicAcSwingVHighest, - kPanasonicAcSwingVHigh, - kPanasonicAcSwingVAuto, // Upper Middle is unused - kPanasonicAcSwingVMiddle, - kPanasonicAcSwingVAuto, // Lower Middle is unused - kPanasonicAcSwingVLow, - kPanasonicAcSwingVLowest, - // Below are unused. - kPanasonicAcSwingVAuto, - kPanasonicAcSwingVAuto, - kPanasonicAcSwingVAuto, - kPanasonicAcSwingVAuto); - switch (getModel()) { - case kPanasonicJke: - case kPanasonicCkp: - break; // No Horizontal Swing support. - default: - result += addSwingHToString(getSwingHorizontal(), kPanasonicAcSwingHAuto, - kPanasonicAcSwingHFullLeft, - kPanasonicAcSwingHLeft, - kPanasonicAcSwingHMiddle, - kPanasonicAcSwingHRight, - kPanasonicAcSwingHFullRight, - // Below are unused. - kPanasonicAcSwingHAuto, - kPanasonicAcSwingHAuto, - kPanasonicAcSwingHAuto, - kPanasonicAcSwingHAuto, - kPanasonicAcSwingHAuto); - } - result += addBoolToString(getQuiet(), kQuietStr); - result += addBoolToString(getPowerful(), kPowerfulStr); - if (getModel() == kPanasonicDke) - result += addBoolToString(getIon(), kIonStr); - result += addLabeledString(minsToString(getClock()), kClockStr); - result += addLabeledString( - isOnTimerEnabled() ? minsToString(getOnTimer()) : kOffStr, - kOnTimerStr); - result += addLabeledString( - isOffTimerEnabled() ? minsToString(getOffTimer()) : kOffStr, - kOffTimerStr); - return result; -} - -#if DECODE_PANASONIC_AC -/// Decode the supplied Panasonic AC message. -/// Status: STABLE / Works with real device(s). -/// @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::decodePanasonicAC(decode_results *results, uint16_t offset, - const uint16_t nbits, const bool strict) { - uint8_t min_nr_of_messages = 1; - if (strict) { - if (nbits != kPanasonicAcBits && nbits != kPanasonicAcShortBits) - return false; // Not strictly a PANASONIC_AC message. - } - - if (results->rawlen <= - min_nr_of_messages * (2 * nbits + kHeader + kFooter) - 1 + offset) - return false; // Can't possibly be a valid PANASONIC_AC message. - - // Match Header + Data #1 + Footer - uint16_t used; - used = matchGeneric(results->rawbuf + offset, results->state, - results->rawlen - offset, kPanasonicAcSection1Length * 8, - kPanasonicHdrMark, kPanasonicHdrSpace, - kPanasonicBitMark, kPanasonicOneSpace, - kPanasonicBitMark, kPanasonicZeroSpace, - kPanasonicBitMark, kPanasonicAcSectionGap, false, - kPanasonicAcTolerance, kPanasonicAcExcess, false); - if (!used) return false; - offset += used; - - // Match Header + Data #2 + Footer - if (!matchGeneric(results->rawbuf + offset, - results->state + kPanasonicAcSection1Length, - results->rawlen - offset, - nbits - kPanasonicAcSection1Length * 8, - kPanasonicHdrMark, kPanasonicHdrSpace, - kPanasonicBitMark, kPanasonicOneSpace, - kPanasonicBitMark, kPanasonicZeroSpace, - kPanasonicBitMark, kPanasonicAcMessageGap, true, - kPanasonicAcTolerance, kPanasonicAcExcess, false)) - return false; - // Compliance - if (strict) { - // Check the signatures of the section blocks. They start with 0x02& 0x20. - if (results->state[0] != 0x02 || results->state[1] != 0x20 || - results->state[8] != 0x02 || results->state[9] != 0x20) - return false; - if (!IRPanasonicAc::validChecksum(results->state, nbits / 8)) return false; - } - - // Success - results->decode_type = decode_type_t::PANASONIC_AC; - results->bits = nbits; - return true; -} -#endif // DECODE_PANASONIC_AC - -#if SEND_PANASONIC_AC32 -/// Send a Panasonic AC 32/16bit formatted message. -/// Status: STABLE / Confirmed working. -/// @param[in] data containing the IR command. -/// @param[in] nbits Nr. of bits to send. Usually kPanasonicAc32Bits -/// @param[in] repeat Nr. of times the message is to be repeated. -/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1307 -void IRsend::sendPanasonicAC32(const uint64_t data, const uint16_t nbits, - const uint16_t repeat) { - uint16_t section_bits; - uint16_t sections; - uint16_t blocks; - // Calculate the section, block, and bit sizes based on the requested bit size - if (nbits > kPanasonicAc32Bits / 2) { // A long message - section_bits = nbits / kPanasonicAc32Sections; - sections = kPanasonicAc32Sections; - blocks = kPanasonicAc32BlocksPerSection; - } else { // A short message - section_bits = nbits; - sections = kPanasonicAc32Sections - 1; - blocks = kPanasonicAc32BlocksPerSection + 1; - } - for (uint16_t r = 0; r <= repeat; r++) { - for (uint8_t section = 0; section < sections; section++) { - uint64_t section_data; - section_data = GETBITS64(data, section_bits * (sections - section - 1), - section_bits); - - // Duplicate bytes in the data. - uint64_t expanded_data = 0; - for (uint8_t i = 0; i < sizeof(expanded_data); i++) { - const uint8_t first_byte = section_data >> 56; - for (uint8_t i = 0; i < 2; i++) - expanded_data = (expanded_data << 8) | first_byte; - section_data <<= 8; - } - // Two data blocks per section (i.e. 1 + a repeat) - sendGeneric(kPanasonicAc32HdrMark, kPanasonicAc32HdrSpace, // Header - kPanasonicAc32BitMark, kPanasonicAc32OneSpace, // Data - kPanasonicAc32BitMark, kPanasonicAc32ZeroSpace, - 0, 0, // No Footer - expanded_data, section_bits * 2, kPanasonicFreq, false, - blocks - 1, // Repeat - 50); - // Section Footer - sendGeneric(kPanasonicAc32HdrMark, kPanasonicAc32HdrSpace, // Header - 0, 0, 0, 0, // No Data - kPanasonicAc32BitMark, kPanasonicAc32SectionGap, // Footer - data, 0, // No data (bits) - kPanasonicFreq, true, 0, 50); - } - } -} -#endif // SEND_PANASONIC_AC32 - -#if DECODE_PANASONIC_AC32 -/// Decode the supplied Panasonic AC 32/16bit 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. -/// Typically: kPanasonicAc32Bits or kPanasonicAc32Bits/2 -/// @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/1307 -/// @note Protocol has two known configurations: -/// (long) -/// Two sections of identical 32 bit data block pairs. ie. (32+32)+(32+32)=128 -/// or -/// (short) -/// A single section of 3 x identical 32 bit data blocks i.e. (32+32+32)=96 -/// Each data block also has a pair of 8 bits repeated identical bits. -/// e.g. (8+8)+(8+8)=32 -/// -/// So each long version really only has 32 unique bits, and the short version -/// really only has 16 unique bits. -bool IRrecv::decodePanasonicAC32(decode_results *results, uint16_t offset, - const uint16_t nbits, const bool strict) { - if (strict && (nbits != kPanasonicAc32Bits && - nbits != kPanasonicAc32Bits / 2)) - return false; // Not strictly a valid bit size. - - // Determine if this is a long or a short message we are looking for. - const bool is_long = (nbits > kPanasonicAc32Bits / 2); - const uint16_t min_length = is_long ? - kPanasonicAc32Sections * kPanasonicAc32BlocksPerSection * - ((2 * nbits) + kHeader + kFooter) - 1 + offset : - (kPanasonicAc32BlocksPerSection + 1) * ((4 * nbits) + kHeader) + - kFooter - 1 + offset; - - if (results->rawlen < min_length) - return false; // Can't possibly be a valid message. - - // Calculate the parameters for the decode based on it's length. - uint16_t sections; - uint16_t blocks_per_section; - if (is_long) { - sections = kPanasonicAc32Sections; - blocks_per_section = kPanasonicAc32BlocksPerSection; - } else { - sections = kPanasonicAc32Sections - 1; - blocks_per_section = kPanasonicAc32BlocksPerSection + 1; - } - const uint16_t bits_per_block = nbits / sections; - - uint64_t data = 0; - uint64_t section_data = 0; - uint32_t prev_section_data; - - // Match all the expected data blocks. - for (uint16_t block = 0; - block < sections * blocks_per_section; - block++) { - prev_section_data = section_data; - uint16_t used = matchGeneric(results->rawbuf + offset, §ion_data, - results->rawlen - offset, bits_per_block * 2, - kPanasonicAc32HdrMark, kPanasonicAc32HdrSpace, - kPanasonicAc32BitMark, kPanasonicAc32OneSpace, - kPanasonicAc32BitMark, kPanasonicAc32ZeroSpace, - 0, 0, // No Footer - false, kUseDefTol, kMarkExcess, false); - if (!used) return false; - offset += used; - // Is it the first block of the section? - if (block % blocks_per_section == 0) { - // The protocol repeats each byte twice, so to shrink the code we - // remove the duplicate bytes in the collected data. We only need to do - // this for the first block in a section. - uint64_t shrunk_data = 0; - uint64_t data_copy = section_data; - for (uint8_t i = 0; i < sizeof(data_copy); i += 2) { - const uint8_t first_byte = GETBITS64(data_copy, - (sizeof(data_copy) - 1) * 8, 8); - shrunk_data = (shrunk_data << 8) | first_byte; - // Compliance - if (strict) { - // Every second byte must be a duplicate of the previous. - const uint8_t next_byte = GETBITS64(data_copy, - (sizeof(data_copy) - 2) * 8, 8); - if (first_byte != next_byte) return false; - } - data_copy <<= 16; - } - // Keep the data from the first of the block in the section. - data = (data << bits_per_block) | shrunk_data; - } else { // Not the first block in a section. - // Compliance - if (strict) - // Compare the data from the blocks in pairs. - if (section_data != prev_section_data) return false; - // Look for the section footer at the end of the blocks. - if ((block + 1) % blocks_per_section == 0) { - uint64_t junk; - used = matchGeneric(results->rawbuf + offset, &junk, - results->rawlen - offset, 0, - // Header - kPanasonicAc32HdrMark, kPanasonicAc32HdrSpace, - // No Data - 0, 0, - 0, 0, - // Footer - kPanasonicAc32BitMark, kPanasonicAc32SectionGap, - true); - if (!used) return false; - offset += used; - } - } - } - - // Success - results->value = data; - results->decode_type = decode_type_t::PANASONIC_AC32; - results->bits = nbits; - results->address = 0; - results->command = 0; - return true; -} -#endif // DECODE_PANASONIC_AC32 - -/// 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? -IRPanasonicAc32::IRPanasonicAc32(const uint16_t pin, const bool inverted, - const bool use_modulation) - : _irsend(pin, inverted, use_modulation) { stateReset(); } - -#if SEND_PANASONIC_AC32 -/// Send the current internal state as IR messages. -/// @param[in] repeat Nr. of times the message will be repeated. -void IRPanasonicAc32::send(const uint16_t repeat) { - _irsend.sendPanasonicAC32(getRaw(), kPanasonicAc32Bits, repeat); -} -#endif // SEND_PANASONIC_AC32 - -/// Set up hardware to be able to send a message. -void IRPanasonicAc32::begin(void) { _irsend.begin(); } - -/// 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 IRPanasonicAc32::getRaw(void) const { return _.raw; } - -/// Set the internal state from a valid code for this protocol. -/// @param[in] state A valid code for this protocol. -void IRPanasonicAc32::setRaw(const uint32_t state) { _.raw = state; } - -/// Reset the state of the remote to a known good state/sequence. -void IRPanasonicAc32::stateReset(void) { setRaw(kPanasonicAc32KnownGood); } - -/// Set the Power Toggle setting of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRPanasonicAc32::setPowerToggle(const bool on) { _.PowerToggle = !on; } - -/// Get the Power Toggle setting of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRPanasonicAc32::getPowerToggle(void) const { return !_.PowerToggle; } - -/// Set the desired temperature. -/// @param[in] degrees The temperature in degrees celsius. -void IRPanasonicAc32::setTemp(const uint8_t degrees) { - uint8_t temp = std::max((uint8_t)kPanasonicAcMinTemp, degrees); - temp = std::min((uint8_t)kPanasonicAcMaxTemp, temp); - _.Temp = temp - (kPanasonicAcMinTemp - 1); -} - -/// Get the current desired temperature setting. -/// @return The current setting for temp. in degrees celsius. -uint8_t IRPanasonicAc32::getTemp(void) const { - return _.Temp + (kPanasonicAcMinTemp - 1); -} - -/// Get the operating mode setting of the A/C. -/// @return The current operating mode setting. -uint8_t IRPanasonicAc32::getMode(void) const { return _.Mode; } - -/// Set the operating mode of the A/C. -/// @param[in] mode The desired operating mode. -/// @note If we get an unexpected mode, default to AUTO. -void IRPanasonicAc32::setMode(const uint8_t mode) { - switch (mode) { - case kPanasonicAc32Auto: - case kPanasonicAc32Cool: - case kPanasonicAc32Dry: - case kPanasonicAc32Heat: - case kPanasonicAc32Fan: - _.Mode = mode; - break; - default: _.Mode = kPanasonicAc32Auto; - } -} - -/// 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 IRPanasonicAc32::convertMode(const stdAc::opmode_t mode) { - switch (mode) { - case stdAc::opmode_t::kCool: return kPanasonicAc32Cool; - case stdAc::opmode_t::kHeat: return kPanasonicAc32Heat; - case stdAc::opmode_t::kDry: return kPanasonicAc32Dry; - case stdAc::opmode_t::kFan: return kPanasonicAc32Fan; - default: return kPanasonicAc32Auto; - } -} - -/// 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 IRPanasonicAc32::toCommonMode(const uint8_t mode) { - switch (mode) { - case kPanasonicAc32Cool: return stdAc::opmode_t::kCool; - case kPanasonicAc32Heat: return stdAc::opmode_t::kHeat; - case kPanasonicAc32Dry: return stdAc::opmode_t::kDry; - case kPanasonicAc32Fan: return stdAc::opmode_t::kFan; - default: return stdAc::opmode_t::kAuto; - } -} - -/// Set the speed of the fan. -/// @param[in] speed The desired setting. -void IRPanasonicAc32::setFan(const uint8_t speed) { - switch (speed) { - case kPanasonicAc32FanMin: - case kPanasonicAc32FanLow: - case kPanasonicAc32FanMed: - case kPanasonicAc32FanHigh: - case kPanasonicAc32FanMax: - case kPanasonicAc32FanAuto: - _.Fan = speed; - break; - default: _.Fan = kPanasonicAc32FanAuto; - } -} - -/// Get the current fan speed setting. -/// @return The current fan speed. -uint8_t IRPanasonicAc32::getFan(void) const { return _.Fan; } - -/// Convert a native fan speed into its stdAc equivalent. -/// @param[in] spd The native setting to be converted. -/// @return The stdAc equivalent of the native setting. -stdAc::fanspeed_t IRPanasonicAc32::toCommonFanSpeed(const uint8_t spd) { - switch (spd) { - case kPanasonicAc32FanMax: return stdAc::fanspeed_t::kMax; - case kPanasonicAc32FanHigh: return stdAc::fanspeed_t::kHigh; - case kPanasonicAc32FanMed: return stdAc::fanspeed_t::kMedium; - case kPanasonicAc32FanLow: return stdAc::fanspeed_t::kLow; - case kPanasonicAc32FanMin: return stdAc::fanspeed_t::kMin; - default: return stdAc::fanspeed_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 IRPanasonicAc32::convertFan(const stdAc::fanspeed_t speed) { - switch (speed) { - case stdAc::fanspeed_t::kMin: return kPanasonicAc32FanMin; - case stdAc::fanspeed_t::kLow: return kPanasonicAc32FanLow; - case stdAc::fanspeed_t::kMedium: return kPanasonicAc32FanMed; - case stdAc::fanspeed_t::kHigh: return kPanasonicAc32FanHigh; - case stdAc::fanspeed_t::kMax: return kPanasonicAc32FanMax; - default: return kPanasonicAc32FanAuto; - } -} - -/// Get the current horizontal swing setting. -/// @return The current position it is set to. -bool IRPanasonicAc32::getSwingHorizontal(void) const { return _.SwingH; } - -/// Control the horizontal swing setting. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRPanasonicAc32::setSwingHorizontal(const bool on) { _.SwingH = on; } - -/// Get the current vertical swing setting. -/// @return The current position it is set to. -uint8_t IRPanasonicAc32::getSwingVertical(void) const { return _.SwingV; } - -/// Control the vertical swing setting. -/// @param[in] pos The position to set the vertical swing to. -void IRPanasonicAc32::setSwingVertical(const uint8_t pos) { - uint8_t elevation = pos; - if (elevation != kPanasonicAc32SwingVAuto) { - elevation = std::max(elevation, kPanasonicAcSwingVHighest); - elevation = std::min(elevation, kPanasonicAcSwingVLowest); - } - _.SwingV = elevation; -} - -/// 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 IRPanasonicAc32::toCommonSwingV(const uint8_t pos) { - return IRPanasonicAc::toCommonSwingV(pos); -} - -/// Convert a standard A/C vertical swing into its native setting. -/// @param[in] position A stdAc::swingv_t position to convert. -/// @return The equivalent native horizontal swing position. -uint8_t IRPanasonicAc32::convertSwingV(const stdAc::swingv_t position) { - switch (position) { - case stdAc::swingv_t::kHighest: - case stdAc::swingv_t::kHigh: - case stdAc::swingv_t::kMiddle: - case stdAc::swingv_t::kLow: - case stdAc::swingv_t::kLowest: return (uint8_t)position; - default: return kPanasonicAc32SwingVAuto; - } -} - -/// Convert the current internal state into a human readable string. -/// @return A human readable string. -String IRPanasonicAc32::toString(void) const { - String result = ""; - result.reserve(110); - result += addBoolToString(getPowerToggle(), kPowerToggleStr, false); - result += addModeToString(_.Mode, kPanasonicAc32Auto, kPanasonicAc32Cool, - kPanasonicAc32Heat, kPanasonicAc32Dry, - kPanasonicAc32Fan); - result += addTempToString(getTemp()); - result += addFanToString(_.Fan, kPanasonicAc32FanHigh, kPanasonicAc32FanLow, - kPanasonicAc32FanAuto, kPanasonicAc32FanMin, - kPanasonicAc32FanMed, kPanasonicAc32FanMax); - result += addBoolToString(_.SwingH, kSwingHStr); - result += addSwingVToString(getSwingVertical(), - kPanasonicAc32SwingVAuto, - kPanasonicAcSwingVHighest, - kPanasonicAcSwingVHigh, - kPanasonicAc32SwingVAuto, // Upper Middle unused - kPanasonicAcSwingVMiddle, - kPanasonicAc32SwingVAuto, // Lower Middle unused - kPanasonicAcSwingVLow, - kPanasonicAcSwingVLowest, - // Below are unused. - kPanasonicAc32SwingVAuto, - kPanasonicAc32SwingVAuto, - kPanasonicAc32SwingVAuto, - kPanasonicAc32SwingVAuto); - return result; -} - -/// 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 IRPanasonicAc32::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::PANASONIC_AC32; - result.model = -1; - if (getPowerToggle()) result.power = !result.power; - result.mode = toCommonMode(getMode()); - result.celsius = true; - result.degrees = getTemp(); - result.fanspeed = toCommonFanSpeed(getFan()); - result.swingv = toCommonSwingV(getSwingVertical()); - result.swingh = getSwingHorizontal() ? stdAc::swingh_t::kAuto - : stdAc::swingh_t::kOff; - // Not supported. - result.quiet = false; - result.turbo = false; - result.filter = false; - result.econo = false; - result.clean = false; - result.light = false; - result.beep = false; - result.sleep = -1; - result.clock = -1; - return result; -} diff --git a/lib/IRremoteESP8266/src/ir_Panasonic.h b/lib/IRremoteESP8266/src/ir_Panasonic.h index 5668e4a57a..9460a1b0ff 100644 --- a/lib/IRremoteESP8266/src/ir_Panasonic.h +++ b/lib/IRremoteESP8266/src/ir_Panasonic.h @@ -36,10 +36,10 @@ #ifdef ARDUINO #include #endif -#include "IRremoteESP8266.h" -#include "IRsend.h" +#include "../src/IRremoteESP8266.h" +#include "../src/IRsend.h" #ifdef UNIT_TEST -#include "IRsend_test.h" +#include "../src/IRsend_test.h" #endif // Constants diff --git a/lib/IRremoteESP8266/src/ir_Pioneer.cpp b/lib/IRremoteESP8266/src/ir_Pioneer.cpp deleted file mode 100644 index 90f58c6ed9..0000000000 --- a/lib/IRremoteESP8266/src/ir_Pioneer.cpp +++ /dev/null @@ -1,138 +0,0 @@ -// 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 - -// Supports: -// Brand: Pioneer, Model: AV Receivers -// Brand: Pioneer, Model: VSX-324 AV Receiver -// Brand: Pioneer, Model: AXD7690 Remote - -#define __STDC_LIMIT_MACROS -#include -#include -#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. -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 diff --git a/lib/IRremoteESP8266/src/ir_Pronto.cpp b/lib/IRremoteESP8266/src/ir_Pronto.cpp deleted file mode 100644 index 2d4ffa7592..0000000000 --- a/lib/IRremoteESP8266/src/ir_Pronto.cpp +++ /dev/null @@ -1,107 +0,0 @@ -// 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 -#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 diff --git a/lib/IRremoteESP8266/src/ir_RC5_RC6.cpp b/lib/IRremoteESP8266/src/ir_RC5_RC6.cpp deleted file mode 100644 index 1e0c75b3f1..0000000000 --- a/lib/IRremoteESP8266/src/ir_RC5_RC6.cpp +++ /dev/null @@ -1,454 +0,0 @@ -// Copyright 2009 Ken Shirriff -// Copyright 2017 David Conran - -/// @file -/// @brief RC-5 & RC-6 support -/// RC-5 & RC-6 support added from https://github.com/z3t0/Arduino-IRremote -/// RC-5X support added by David Conran -/// @see https://en.wikipedia.org/wiki/RC-5 -/// @see http://www.sbprojects.net/knowledge/ir/rc5.php -/// @see https://en.wikipedia.org/wiki/Manchester_code -/// @see https://en.wikipedia.org/wiki/RC-6 -/// @see https://www.sbprojects.net/knowledge/ir/rc6.php -/// @see http://www.pcbheaven.com/userpages/The_Philips_RC6_Protocol/ -/// @see http://www.righto.com/2010/12/64-bit-rc6-codes-arduino-and-xbox.html - -// Supports: -// Brand: Philips, Model: Standard RC-5 (RC5) -// Brand: Philips, Model: RC-5X (RC5X) -// Brand: Philips, Model: Standard RC-6 (RC6) - -#include -#include "IRrecv.h" -#include "IRsend.h" -#include "IRtimer.h" -#include "IRutils.h" - -// Constants -// RC-5/RC-5X -const uint16_t kRc5T1 = 889; -const uint32_t kRc5MinCommandLength = 113778; -const uint32_t kRc5MinGap = kRc5MinCommandLength - kRC5RawBits * (2 * kRc5T1); -const uint16_t kRc5ToggleMask = 0x800; // The 12th bit. -const uint16_t kRc5SamplesMin = 11; - -// RC-6 -const uint16_t kRc6Tick = 444; -const uint16_t kRc6HdrMarkTicks = 6; -const uint16_t kRc6HdrMark = kRc6HdrMarkTicks * kRc6Tick; -const uint16_t kRc6HdrSpaceTicks = 2; -const uint16_t kRc6HdrSpace = kRc6HdrSpaceTicks * kRc6Tick; -const uint16_t kRc6RptLengthTicks = 187; -const uint32_t kRc6RptLength = kRc6RptLengthTicks * kRc6Tick; -const uint32_t kRc6ToggleMask = 0x10000UL; // The 17th bit. -const uint16_t kRc6_36ToggleMask = 0x8000; // The 16th bit. - -// Common (getRClevel()) -const int16_t kMark = 0; -const int16_t kSpace = 1; - -#if SEND_RC5 -/// Send a Philips RC-5/RC-5X packet. -/// Status: RC-5 (stable), RC-5X (alpha) -/// @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 Caller needs to take care of flipping the toggle bit. -/// That bit differentiates between key press & key release. -/// For RC-5 it is the MSB of the data. -/// For RC-5X it is the 2nd MSB of the data. -/// @todo Testing of the RC-5X components. -void IRsend::sendRC5(const uint64_t data, uint16_t nbits, - const uint16_t repeat) { - if (nbits > sizeof(data) * 8) return; // We can't send something that big. - bool skipSpace = true; - bool field_bit = true; - // Set 36kHz IR carrier frequency & a 1/4 (25%) duty cycle. - enableIROut(36, 25); - - if (nbits >= kRC5XBits) { // Is this a RC-5X message? - // field bit is the inverted MSB of RC-5X data. - field_bit = ((data >> (nbits - 1)) ^ 1) & 1; - nbits--; - } - - IRtimer usecTimer = IRtimer(); - for (uint16_t i = 0; i <= repeat; i++) { - usecTimer.reset(); - - // Header - // First start bit (0x1). space, then mark. - if (skipSpace) - skipSpace = false; // First time through, we assume the leading space(). - else - space(kRc5T1); - mark(kRc5T1); - // Field/Second start bit. - if (field_bit) { // Send a 1. Normal for RC-5. - space(kRc5T1); - mark(kRc5T1); - } else { // Send a 0. Special case for RC-5X. Means 7th command bit is 1. - mark(kRc5T1); - space(kRc5T1); - } - - // Data - for (uint64_t mask = 1ULL << (nbits - 1); mask; mask >>= 1) - if (data & mask) { // 1 - space(kRc5T1); // 1 is space, then mark. - mark(kRc5T1); - } else { // 0 - mark(kRc5T1); // 0 is mark, then space. - space(kRc5T1); - } - // Footer - space(std::max(kRc5MinGap, kRc5MinCommandLength - usecTimer.elapsed())); - } -} - -/// Encode a Philips RC-5 data message. -/// Status: Beta / Should be working. -/// @param[in] address The 5-bit address value for the message. -/// @param[in] command The 6-bit command value for the message. -/// @param[in] key_released Indicate if the remote key has been released. -/// @return A message suitable for use in sendRC5(). -uint16_t IRsend::encodeRC5(const uint8_t address, const uint8_t command, - const bool key_released) { - return (key_released << (kRC5Bits - 1)) | ((address & 0x1f) << 6) | - (command & 0x3F); -} - -/// Encode a Philips RC-5X data message. -/// Status: Beta / Should be working. -/// @param[in] address The 5-bit address value for the message. -/// @param[in] command The 7-bit command value for the message. -/// @param[in] key_released Indicate if the remote key has been released. -/// @return A message suitable for use in sendRC5(). -uint16_t IRsend::encodeRC5X(const uint8_t address, const uint8_t command, - const bool key_released) { - // The 2nd start/field bit (MSB of the return value) is the value of the 7th - // command bit. - bool s2 = (command >> 6) & 1; - return ((uint16_t)s2 << (kRC5XBits - 1)) | - encodeRC5(address, command, key_released); -} - -/// Flip the toggle bit of a Philips RC-5/RC-5X data message. -/// Used to indicate a change of remote button's state. -/// Status: STABLE. -/// @param[in] data The existing RC-5/RC-5X message. -/// @return A data message suitable for use in sendRC5() with the toggle bit -/// flipped. -uint64_t IRsend::toggleRC5(const uint64_t data) { - return data ^ kRc5ToggleMask; -} -#endif // SEND_RC5 - -#if SEND_RC6 -/// Flip the toggle bit of a Philips RC-6 data message. -/// Used to indicate a change of remote button's state. -/// Status: STABLE / Should work fine. -/// @param[in] data The existing RC-6 message. -/// @param [in] nbits Nr. of bits in the RC-6 protocol. -/// @return A data message suitable for use in sendRC6() with the toggle bit -/// flipped. -/// @note For RC-6 (20-bits), it is the 17th least significant bit. -/// @note For RC-6 (36-bits/Xbox-360), it is the 16th least significant bit. -uint64_t IRsend::toggleRC6(const uint64_t data, const uint16_t nbits) { - if (nbits == kRC6_36Bits) return data ^ kRc6_36ToggleMask; - return data ^ kRc6ToggleMask; -} - -/// Encode a Philips RC-6 data message. -/// Status: Beta / Should be working. -/// @param[in] address The address (aka. control) value for the message. -/// Includes the field/mode/toggle bits. -/// @param[in] command The 8-bit command value for the message. -/// (aka. information) -/// @param[in] mode Which protocol to use. -/// Defined by nr. of bits in the protocol. -/// @return A data message suitable for use in `sendRC6()`. -uint64_t IRsend::encodeRC6(const uint32_t address, const uint8_t command, - const uint16_t mode) { - switch (mode) { - case kRC6Mode0Bits: - return ((address & 0xFFF) << 8) | (command & 0xFF); - case kRC6_36Bits: - return ((uint64_t)(address & 0xFFFFFFF) << 8) | (command & 0xFF); - default: - return 0; - } -} - -/// Send a Philips RC-6 packet. -/// Status: Stable. -/// @note Caller needs to take care of flipping the toggle bit (The 4th Most -/// Significant Bit). That bit differentiates between key press & key release. -/// @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::sendRC6(const uint64_t data, const uint16_t nbits, - const uint16_t repeat) { - // Check we can send the number of bits requested. - if (nbits > sizeof(data) * 8) return; - // Set 36kHz IR carrier frequency & a 1/3 (33%) duty cycle. - enableIROut(36, 33); - for (uint16_t r = 0; r <= repeat; r++) { - // Header - mark(kRc6HdrMark); - space(kRc6HdrSpace); - // Start bit. - mark(kRc6Tick); // mark, then space == 0x1. - space(kRc6Tick); - // Data - uint16_t bitTime; - for (uint64_t i = 1, mask = 1ULL << (nbits - 1); mask; i++, mask >>= 1) { - if (i == 4) // The fourth bit we send is a "double width trailer bit". - bitTime = 2 * kRc6Tick; // double-wide trailer bit - else - bitTime = kRc6Tick; // Normal bit - if (data & mask) { // 1 - mark(bitTime); - space(bitTime); - } else { // 0 - space(bitTime); - mark(bitTime); - } - } - // Footer - space(kRc6RptLength); - } -} -#endif // SEND_RC6 - -#if (DECODE_RC5 || DECODE_RC6 || DECODE_LASERTAG) -/// Gets one undecoded level at a time from the raw buffer. -/// The RC5/6 decoding is easier if the data is broken into time intervals. -/// E.g. if the buffer has MARK for 2 time intervals and SPACE for 1, -/// successive calls to getRClevel will return MARK, MARK, SPACE. -/// offset and used are updated to keep track of the current position. -/// @param[in,out] results Ptr to the data to decode and where to store the -/// decode result. -/// @param[in,out] offset Ptr to the currect offset to the rawbuf. -/// @param[in,out] used Ptr to the current used counter. -/// @param[in] bitTime Time interval of single bit in microseconds. -/// @param[in] tolerance Percent tolerance to be used in matching. -/// @param[in] excess Extra useconds to add to Marks & removed from Spaces. -/// @param[in] delta A non-scaling (+/-) error margin (in useconds). -/// @param[in] maxwidth Maximum number of successive levels to find in a single -/// level (default is 3) -/// @return MARK, SPACE, or -1 for error. -/// (The measured time interval is not a multiple of t1.) -/// @see https://en.wikipedia.org/wiki/Manchester_code -int16_t IRrecv::getRClevel(decode_results *results, uint16_t *offset, - uint16_t *used, const uint16_t bitTime, - const uint8_t tolerance, const int16_t excess, - const uint16_t delta, const uint8_t maxwidth) { - DPRINT("DEBUG: getRClevel: offset = "); - DPRINTLN(uint64ToString(*offset)); - DPRINT("DEBUG: getRClevel: rawlen = "); - DPRINTLN(uint64ToString(results->rawlen)); - if (*offset >= results->rawlen) { - DPRINTLN("DEBUG: getRClevel: SPACE, past end of rawbuf"); - return kSpace; // After end of recorded buffer, assume SPACE. - } - uint16_t width = results->rawbuf[*offset]; - // If the value of offset is odd, it's a MARK. Even, it's a SPACE. - uint16_t val = ((*offset) % 2) ? kMark : kSpace; - // Check to see if we have hit an inter-message gap (> 20ms). - if (val == kSpace && - (width > 20000 - delta || width > maxwidth * bitTime + delta)) { - DPRINTLN("DEBUG: getRClevel: SPACE, hit end of mesg gap."); - return kSpace; - } - int16_t correction = (val == kMark) ? excess : -excess; - - // Calculate the look-ahead for our current position in the buffer. - uint16_t avail; - // Note: We want to match in greedy order as the other way leads to - // mismatches due to overlaps induced by the correction and tolerance - // values. - for (avail = maxwidth; avail > 0; avail--) { - if (match(width, avail * bitTime + correction, tolerance, delta)) { - break; - } - } - if (!avail) { - DPRINTLN("DEBUG: getRClevel: Unexpected width. Exiting."); - return -1; // The width is not what we expected. - } - - (*used)++; // Count another one of the avail slots as used. - if (*used >= avail) { // Are we out of look-ahead/avail slots? - // Yes, so reset the used counter, and move the offset ahead. - *used = 0; - (*offset)++; - } - if (val == kMark) { - DPRINTLN("DEBUG: getRClevel: MARK"); - } else { - DPRINTLN("DEBUG: getRClevel: SPACE"); - } - - return val; -} -#endif // (DECODE_RC5 || DECODE_RC6 || DECODE_LASERTAG) - -#if DECODE_RC5 -/// Decode the supplied RC-5/RC5X message. -/// Status: RC-5 (stable), RC-5X (alpha) -/// @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 The 'toggle' bit is included as the 6th (MSB) address bit, the MSB of -/// data, & in the count of bits decoded. -/// @todo Serious testing of the RC-5X and strict aspects needs to be done. -bool IRrecv::decodeRC5(decode_results *results, uint16_t offset, - const uint16_t nbits, const bool strict) { - if (results->rawlen <= kRc5SamplesMin + kHeader - 1 + offset) return false; - - // Compliance - if (strict && nbits != kRC5Bits && nbits != kRC5XBits) - return false; // It's neither RC-5 or RC-5X. - - uint16_t used = 0; - bool is_rc5x = false; - uint64_t data = 0; - - // Header - // Get start bit #1. - if (getRClevel(results, &offset, &used, kRc5T1) != kMark) return false; - // Get field/start bit #2 (inverted bit-7 of the command if RC-5X protocol) - uint16_t actual_bits = 1; - int16_t levelA = getRClevel(results, &offset, &used, kRc5T1); - int16_t levelB = getRClevel(results, &offset, &used, kRc5T1); - if (levelA == kSpace && levelB == kMark) { // Matched a 1. - is_rc5x = false; - } else if (levelA == kMark && levelB == kSpace) { // Matched a 0. - if (nbits <= kRC5Bits) return false; // Field bit must be '1' for RC5. - is_rc5x = true; - data = 1; - } else { - return false; // Not what we expected. - } - - // Data - for (; offset < results->rawlen; actual_bits++) { - int16_t levelA = getRClevel(results, &offset, &used, kRc5T1); - int16_t levelB = getRClevel(results, &offset, &used, kRc5T1); - 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 != kRC5Bits && actual_bits != kRC5XBits) - return false; - - // Success - results->value = data; - results->address = (data >> 6) & 0x1F; - results->command = data & 0x3F; - results->repeat = false; - if (is_rc5x) { - results->decode_type = RC5X; - results->command |= ((uint32_t)is_rc5x) << 6; - } else { - results->decode_type = RC5; - actual_bits--; // RC5 doesn't count the field bit as data. - } - results->bits = actual_bits; - return true; -} -#endif // DECODE_RC5 - -#if DECODE_RC6 -/// Decode the supplied RC6 message. -/// Status: Stable. -/// @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. -/// @todo Testing of the strict compliance aspects. -bool IRrecv::decodeRC6(decode_results *results, uint16_t offset, - const uint16_t nbits, const bool strict) { - if (results->rawlen <= kHeader + 2 + 4 + offset) - // Up to the double-wide T bit. - return false; // Smaller than absolute smallest possible RC6 message. - - if (strict) { // Compliance - // Unlike typical protocols, the ability to have mark+space, and space+mark - // as data bits means it is possible to only have nbits of entries for the - // data portion, rather than the typically required 2 * nbits. - // Also due to potential melding with the start bit, we can only count - // the start bit as 1, instead of a more typical 2 value. The header still - // remains as normal. - if (results->rawlen <= nbits + kHeader + 1 + offset) - return false; // Don't have enough entries/samples to be valid. - switch (nbits) { - case kRC6Mode0Bits: - case kRC6_36Bits: - break; - default: - return false; // Asking for the wrong number of bits. - } - } - - // Header - if (!matchMark(results->rawbuf[offset], kRc6HdrMark)) return false; - // Calculate how long the common tick time is based on the header mark. - uint32_t tick = results->rawbuf[offset++] * kRawTick / kRc6HdrMarkTicks; - if (!matchSpace(results->rawbuf[offset++], kRc6HdrSpaceTicks * tick)) - return false; - - uint16_t used = 0; - - // Get the start bit. e.g. 1. - if (getRClevel(results, &offset, &used, tick) != kMark) return false; - if (getRClevel(results, &offset, &used, tick) != kSpace) return false; - - uint16_t actual_bits; - uint64_t data = 0; - - // Data (Warning: Here be dragons^Wpointers!!) - for (actual_bits = 0; offset < results->rawlen; actual_bits++) { - int16_t levelA, levelB; // Next two levels - levelA = getRClevel(results, &offset, &used, tick); - // T bit is double wide; make sure second half matches - if (actual_bits == 3 && levelA != getRClevel(results, &offset, &used, tick)) - return false; - levelB = getRClevel(results, &offset, &used, tick); - // T bit is double wide; make sure second half matches - if (actual_bits == 3 && levelB != getRClevel(results, &offset, &used, tick)) - return false; - if (levelA == kMark && levelB == kSpace) // reversed compared to RC5 - data = (data << 1) | 1; // 1 - else if (levelA == kSpace && levelB == kMark) - data <<= 1; // 0 - else - break; - } - - // More compliance - if (strict && actual_bits != nbits) - return false; // Actual nr. of bits didn't match expected. - - // Success - results->decode_type = RC6; - results->bits = actual_bits; - results->value = data; - results->address = data >> 8; - results->command = data & 0xFF; - return true; -} -#endif // DECODE_RC6 diff --git a/lib/IRremoteESP8266/src/ir_RCMM.cpp b/lib/IRremoteESP8266/src/ir_RCMM.cpp deleted file mode 100644 index 97a3c79b5b..0000000000 --- a/lib/IRremoteESP8266/src/ir_RCMM.cpp +++ /dev/null @@ -1,164 +0,0 @@ -// Copyright 2017 David Conran - -/// @file -/// @brief Support for the Phillips RC-MM protocol. -/// @see http://www.sbprojects.net/knowledge/ir/rcmm.php - -// Supports: -// Brand: Microsoft, Model: XBOX 360 - -#include -#include "IRrecv.h" -#include "IRsend.h" -#include "IRtimer.h" -#include "IRutils.h" - -// Constants -const uint16_t kRcmmTick = 28; // Technically it would be 27.777* -const uint16_t kRcmmHdrMarkTicks = 15; -const uint16_t kRcmmHdrMark = 416; -const uint16_t kRcmmHdrSpaceTicks = 10; -const uint16_t kRcmmHdrSpace = 277; -const uint16_t kRcmmBitMarkTicks = 6; -const uint16_t kRcmmBitMark = 166; -const uint16_t kRcmmBitSpace0Ticks = 10; -const uint16_t kRcmmBitSpace0 = 277; -const uint16_t kRcmmBitSpace1Ticks = 16; -const uint16_t kRcmmBitSpace1 = 444; -const uint16_t kRcmmBitSpace2Ticks = 22; -const uint16_t kRcmmBitSpace2 = 611; -const uint16_t kRcmmBitSpace3Ticks = 28; -const uint16_t kRcmmBitSpace3 = 777; -const uint16_t kRcmmRptLengthTicks = 992; -const uint32_t kRcmmRptLength = 27778; -const uint16_t kRcmmMinGapTicks = 120; -const uint32_t kRcmmMinGap = 3360; -// Use a tolerance of +/-10% when matching some data spaces. -const uint8_t kRcmmTolerance = 10; -const uint16_t kRcmmExcess = 50; - -#if SEND_RCMM -/// Send a Philips RC-MM packet. -/// 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. -void IRsend::sendRCMM(uint64_t data, uint16_t nbits, uint16_t repeat) { - // Set 36kHz IR carrier frequency & a 1/3 (33%) duty cycle. - enableIROut(36, 33); - IRtimer usecs = IRtimer(); - - for (uint16_t r = 0; r <= repeat; r++) { - usecs.reset(); - // Header - mark(kRcmmHdrMark); - space(kRcmmHdrSpace); - // Data - uint64_t mask = 0b11ULL << (nbits - 2); - // RC-MM sends data 2 bits at a time. - for (int32_t i = nbits; i > 0; i -= 2) { - mark(kRcmmBitMark); - // Grab the next Most Significant Bits to send. - switch ((data & mask) >> (i - 2)) { - case 0b00: - space(kRcmmBitSpace0); - break; - case 0b01: - space(kRcmmBitSpace1); - break; - case 0b10: - space(kRcmmBitSpace2); - break; - case 0b11: - space(kRcmmBitSpace3); - break; - } - mask >>= 2; - } - // Footer - mark(kRcmmBitMark); - // Protocol requires us to wait at least kRcmmRptLength usecs from the - // start or kRcmmMinGap usecs. - space(std::max(kRcmmRptLength - usecs.elapsed(), kRcmmMinGap)); - } -} -#endif // SEND_RCMM - -#if DECODE_RCMM -/// Decode a Philips RC-MM packet (between 12 & 32 bits) if possible. -/// Status: STABLE / Should 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::decodeRCMM(decode_results *results, uint16_t offset, - const uint16_t nbits, const bool strict) { - uint64_t data = 0; - - if (results->rawlen <= 4 + offset - 1) - return false; // Not enough entries to ever be RCMM. - - // Calc the maximum size in bits, the message can be, or that we can accept. - int16_t maxBitSize = - std::min((uint16_t)results->rawlen - 5, (uint16_t)sizeof(data) * 8); - // Compliance - if (strict) { - // Technically the spec says bit sizes should be 12 xor 24. however - // 32 bits has been seen from a device. We are going to assume - // 12 <= bits <= 32 is the 'required' bit length for the spec. - if (maxBitSize < 12 || maxBitSize > 32) return false; - if (maxBitSize < nbits) - return false; // Short cut, we can never reach the expected nr. of bits. - } - // Header decode - if (!matchMark(results->rawbuf[offset], kRcmmHdrMark)) return false; - // Calculate how long the common tick time is based on the header mark. - uint32_t m_tick = results->rawbuf[offset++] * kRawTick / kRcmmHdrMarkTicks; - if (!matchSpace(results->rawbuf[offset], kRcmmHdrSpace)) return false; - // Calculate how long the common tick time is based on the header space. - uint32_t s_tick = results->rawbuf[offset++] * kRawTick / kRcmmHdrSpaceTicks; - - // Data decode - // RC-MM has two bits of data per mark/space pair. - uint16_t actualBits; - for (actualBits = 0; actualBits < maxBitSize; actualBits += 2, offset++) { - if (!match(results->rawbuf[offset++], kRcmmBitMarkTicks * m_tick)) - return false; - - data <<= 2; - // Use non-default tolerance & excess for matching some of the spaces as the - // defaults are too generous and causes mis-matches in some cases. - if (match(results->rawbuf[offset], kRcmmBitSpace0Ticks * s_tick)) - data += 0; - else if (match(results->rawbuf[offset], kRcmmBitSpace1Ticks * s_tick)) - data += 1; - else if (match(results->rawbuf[offset], kRcmmBitSpace2Ticks * s_tick, - kRcmmTolerance)) - data += 2; - else if (match(results->rawbuf[offset], kRcmmBitSpace3Ticks * s_tick, - kRcmmTolerance)) - data += 3; - else - return false; - } - // Footer decode - if (!match(results->rawbuf[offset++], kRcmmBitMarkTicks * m_tick)) - return false; - if (offset < results->rawlen && - !matchAtLeast(results->rawbuf[offset], kRcmmMinGapTicks * s_tick)) - return false; - - // Compliance - if (strict && actualBits != nbits) return false; - - // Success - results->value = data; - results->decode_type = RCMM; - results->bits = actualBits; - results->address = 0; - results->command = 0; - return true; -} -#endif // DECODE_RCMM diff --git a/lib/IRremoteESP8266/src/ir_Rhoss.cpp b/lib/IRremoteESP8266/src/ir_Rhoss.cpp deleted file mode 100644 index f906f49be4..0000000000 --- a/lib/IRremoteESP8266/src/ir_Rhoss.cpp +++ /dev/null @@ -1,364 +0,0 @@ -// Copyright 2021 Tom Rosenback - -/// @file -/// @brief Support for Rhoss protocols. - -#include "ir_Rhoss.h" -#include -#include -#include "IRrecv.h" -#include "IRsend.h" -#include "IRtext.h" -#include "IRutils.h" - -const uint16_t kRhossHdrMark = 3042; -const uint16_t kRhossHdrSpace = 4248; -const uint16_t kRhossBitMark = 648; -const uint16_t kRhossOneSpace = 1545; -const uint16_t kRhossZeroSpace = 457; -const uint32_t kRhossGap = kDefaultMessageGap; -const uint16_t kRhossFreq = 38; - -using irutils::addBoolToString; -using irutils::addModeToString; -using irutils::addFanToString; -using irutils::addTempToString; - -#if SEND_RHOSS -/// Send a Rhoss 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::sendRhoss(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 < kRhossStateLength) return; - - // We always send a message, even for repeat=0, hence '<= repeat'. - for (uint16_t r = 0; r <= repeat; r++) { - sendGeneric(kRhossHdrMark, kRhossHdrSpace, kRhossBitMark, - kRhossOneSpace, kRhossBitMark, kRhossZeroSpace, - kRhossBitMark, kRhossZeroSpace, - data, nbytes, kRhossFreq, false, 0, kDutyDefault); - mark(kRhossBitMark); - // Gap - space(kRhossGap); - } -} -#endif // SEND_RHOSS - -#if DECODE_RHOSS -/// Decode the supplied Rhoss 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::decodeRhoss(decode_results *results, uint16_t offset, - const uint16_t nbits, const bool strict) { - if (strict && nbits != kRhossBits) return false; - - if (results->rawlen <= 2 * nbits + kHeader + kFooter - 1 + offset) { - return false; // Can't possibly be a valid Rhoss message. - } - - uint16_t used; - // Header + Data Block (96 bits) + Footer - used = matchGeneric(results->rawbuf + offset, results->state, - results->rawlen - offset, kRhossBits, - kRhossHdrMark, kRhossHdrSpace, - kRhossBitMark, kRhossOneSpace, - kRhossBitMark, kRhossZeroSpace, - kRhossBitMark, kRhossZeroSpace, - false, kUseDefTol, kMarkExcess, false); - - if (!used) return false; - offset += used; - - // Footer (Part 2) - if (!matchMark(results->rawbuf[offset++], kRhossBitMark)) { - return false; - } - - if (offset < results->rawlen && - !matchAtLeast(results->rawbuf[offset], kRhossGap)) { - return false; - } - - if (strict && !IRRhossAc::validChecksum(results->state)) return false; - - // Success - results->decode_type = decode_type_t::RHOSS; - 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_RHOSS - -/// 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? -IRRhossAc::IRRhossAc(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 IRRhossAc::begin(void) { _irsend.begin(); } - -#if SEND_RHOSS -/// Send the current internal state as an IR message. -/// @param[in] repeat Nr. of times the message will be repeated. -void IRRhossAc::send(const uint16_t repeat) { - _irsend.sendRhoss(getRaw(), kRhossStateLength, repeat); -} -#endif // SEND_RHOSS - -/// 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 IRRhossAc::calcChecksum(const uint8_t state[], const uint16_t length) { - 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 size of the state. -/// @return A boolean indicating if it's checksum is valid. -bool IRRhossAc::validChecksum(const uint8_t state[], const uint16_t length) { - return (state[length - 1] == IRRhossAc::calcChecksum(state, length)); -} - -/// Update the checksum value for the internal state. -void IRRhossAc::checksum(void) { - _.Sum = IRRhossAc::calcChecksum(_.raw, kRhossStateLength); - _.raw[kRhossStateLength - 1] = _.Sum; -} - -/// Reset the internals of the object to a known good state. -void IRRhossAc::stateReset(void) { - for (uint8_t i = 1; i < kRhossStateLength; i++) _.raw[i] = 0x0; - _.raw[0] = 0xAA; - _.raw[2] = 0x60; - _.raw[6] = 0x54; - _.Power = kRhossDefaultPower; - _.Fan = kRhossDefaultFan; - _.Mode = kRhossDefaultMode; - _.Swing = kRhossDefaultSwing; - _.Temp = kRhossDefaultTemp - kRhossTempMin; -} - -/// 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* IRRhossAc::getRaw(void) { - checksum(); // Ensure correct bit array before returning - return _.raw; -} - -/// Set the raw state of the object. -/// @param[state] state The raw state from the native IR message. -void IRRhossAc::setRaw(const uint8_t state[]) { - std::memcpy(_.raw, state, kRhossStateLength); -} - -/// Set the internal state to have the power on. -void IRRhossAc::on(void) { setPower(true); } - -/// Set the internal state to have the power off. -void IRRhossAc::off(void) { setPower(false); } - -/// Set the internal state to have the desired power. -/// @param[in] on The desired power state. -void IRRhossAc::setPower(const bool on) { - _.Power = (on ? kRhossPowerOn : kRhossPowerOff); -} - -/// Get the power setting from the internal state. -/// @return A boolean indicating the power setting. -bool IRRhossAc::getPower(void) const { - return _.Power == kRhossPowerOn; -} - -/// Set the temperature. -/// @param[in] degrees The temperature in degrees celsius. -void IRRhossAc::setTemp(const uint8_t degrees) { - uint8_t temp = std::max(kRhossTempMin, degrees); - _.Temp = std::min(kRhossTempMax, temp) - kRhossTempMin; -} - -/// Get the current temperature setting. -/// @return Get current setting for temp. in degrees celsius. -uint8_t IRRhossAc::getTemp(void) const { - return _.Temp + kRhossTempMin; -} - -/// Set the speed of the fan. -/// @param[in] speed The desired setting. -void IRRhossAc::setFan(const uint8_t speed) { - switch (speed) { - case kRhossFanAuto: - case kRhossFanMin: - case kRhossFanMed: - case kRhossFanMax: - _.Fan = speed; - break; - default: - _.Fan = kRhossFanAuto; - } -} - -/// Get the current fan speed setting. -/// @return The current fan speed. -uint8_t IRRhossAc::getFan(void) const { - return _.Fan; -} - -/// Set the Vertical Swing mode of the A/C. -/// @param[in] state true, the Swing is on. false, the Swing is off. -void IRRhossAc::setSwing(const bool state) { - _.Swing = state; -} - -/// Get the Vertical Swing speed of the A/C. -/// @return The native swing speed setting. -uint8_t IRRhossAc::getSwing(void) const { - return _.Swing; -} - -/// Get the current operation mode setting. -/// @return The current operation mode. -uint8_t IRRhossAc::getMode(void) const { - return _.Mode; -} - -/// Set the desired operation mode. -/// @param[in] mode The desired operation mode. -void IRRhossAc::setMode(const uint8_t mode) { - switch (mode) { - case kRhossModeFan: - case kRhossModeCool: - case kRhossModeDry: - case kRhossModeHeat: - case kRhossModeAuto: - _.Mode = mode; - return; - default: - _.Mode = kRhossDefaultMode; - 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 IRRhossAc::convertMode(const stdAc::opmode_t mode) { - switch (mode) { - case stdAc::opmode_t::kCool: - return kRhossModeCool; - case stdAc::opmode_t::kHeat: - return kRhossModeHeat; - case stdAc::opmode_t::kDry: - return kRhossModeDry; - case stdAc::opmode_t::kFan: - return kRhossModeFan; - case stdAc::opmode_t::kAuto: - return kRhossModeAuto; - default: - return kRhossDefaultMode; - } -} - -/// 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 IRRhossAc::convertFan(const stdAc::fanspeed_t speed) { - switch (speed) { - case stdAc::fanspeed_t::kMin: - case stdAc::fanspeed_t::kLow: - return kRhossFanMin; - case stdAc::fanspeed_t::kMedium: - return kRhossFanMed; - case stdAc::fanspeed_t::kHigh: - case stdAc::fanspeed_t::kMax: - return kRhossFanMax; - default: - return kRhossDefaultFan; - } -} - -/// 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 IRRhossAc::toCommonMode(const uint8_t mode) { - switch (mode) { - case kRhossModeCool: return stdAc::opmode_t::kCool; - case kRhossModeHeat: return stdAc::opmode_t::kHeat; - case kRhossModeDry: return stdAc::opmode_t::kDry; - case kRhossModeFan: return stdAc::opmode_t::kFan; - case kRhossModeAuto: return stdAc::opmode_t::kAuto; - 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 IRRhossAc::toCommonFanSpeed(const uint8_t speed) { - switch (speed) { - case kRhossFanMax: return stdAc::fanspeed_t::kMax; - case kRhossFanMed: return stdAc::fanspeed_t::kMedium; - case kRhossFanMin: return stdAc::fanspeed_t::kMin; - case kRhossFanAuto: - 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 IRRhossAc::toCommon(void) const { - stdAc::state_t result; - result.protocol = decode_type_t::RHOSS; - result.power = getPower(); - result.mode = toCommonMode(_.Mode); - result.celsius = true; - result.degrees = _.Temp; - result.fanspeed = toCommonFanSpeed(_.Fan); - result.swingv = _.Swing ? stdAc::swingv_t::kAuto : stdAc::swingv_t::kOff; - // Not supported. - result.model = -1; - result.turbo = false; - 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 IRRhossAc::toString(void) const { - String result = ""; - result.reserve(70); // Reserve some heap for the string to reduce fragging. - result += addBoolToString(getPower(), kPowerStr, false); - result += addModeToString(getMode(), kRhossModeAuto, kRhossModeCool, - kRhossModeHeat, kRhossModeDry, kRhossModeFan); - result += addTempToString(getTemp()); - result += addFanToString(getFan(), kRhossFanMax, kRhossFanMin, - kRhossFanAuto, kRhossFanAuto, - kRhossFanMed); - result += addBoolToString(getSwing(), kSwingVStr); - return result; -} diff --git a/lib/IRremoteESP8266/src/ir_Rhoss.h b/lib/IRremoteESP8266/src/ir_Rhoss.h index 8f66ce7384..e3a70aa431 100644 --- a/lib/IRremoteESP8266/src/ir_Rhoss.h +++ b/lib/IRremoteESP8266/src/ir_Rhoss.h @@ -13,10 +13,10 @@ #ifndef UNIT_TEST #include #endif -#include "IRremoteESP8266.h" -#include "IRsend.h" +#include "../src/IRremoteESP8266.h" +#include "../src/IRsend.h" #ifdef UNIT_TEST -#include "IRsend_test.h" +#include "../src/IRsend_test.h" #endif diff --git a/lib/IRremoteESP8266/src/ir_Samsung.cpp b/lib/IRremoteESP8266/src/ir_Samsung.cpp deleted file mode 100644 index 9e1ad46690..0000000000 --- a/lib/IRremoteESP8266/src/ir_Samsung.cpp +++ /dev/null @@ -1,832 +0,0 @@ -// Copyright 2009 Ken Shirriff -// Copyright 2017, 2018, 2019 David Conran -/// @file -/// @brief Support for Samsung protocols. -/// Samsung originally added from https://github.com/shirriff/Arduino-IRremote/ -/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/505 -/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/621 -/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1062 -/// @see http://elektrolab.wz.cz/katalog/samsung_protocol.pdf -/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1538 (Checksum) - -#include "ir_Samsung.h" -#include -#include -#ifndef ARDUINO -#include -#endif -#include "IRrecv.h" -#include "IRsend.h" -#include "IRtext.h" -#include "IRutils.h" - -// Constants -const uint16_t kSamsungTick = 560; -const uint16_t kSamsungHdrMarkTicks = 8; -const uint16_t kSamsungHdrMark = kSamsungHdrMarkTicks * kSamsungTick; -const uint16_t kSamsungHdrSpaceTicks = 8; -const uint16_t kSamsungHdrSpace = kSamsungHdrSpaceTicks * kSamsungTick; -const uint16_t kSamsungBitMarkTicks = 1; -const uint16_t kSamsungBitMark = kSamsungBitMarkTicks * kSamsungTick; -const uint16_t kSamsungOneSpaceTicks = 3; -const uint16_t kSamsungOneSpace = kSamsungOneSpaceTicks * kSamsungTick; -const uint16_t kSamsungZeroSpaceTicks = 1; -const uint16_t kSamsungZeroSpace = kSamsungZeroSpaceTicks * kSamsungTick; -const uint16_t kSamsungRptSpaceTicks = 4; -const uint16_t kSamsungRptSpace = kSamsungRptSpaceTicks * kSamsungTick; -const uint16_t kSamsungMinMessageLengthTicks = 193; -const uint32_t kSamsungMinMessageLength = - kSamsungMinMessageLengthTicks * kSamsungTick; -const uint16_t kSamsungMinGapTicks = - kSamsungMinMessageLengthTicks - - (kSamsungHdrMarkTicks + kSamsungHdrSpaceTicks + - kSamsungBits * (kSamsungBitMarkTicks + kSamsungOneSpaceTicks) + - kSamsungBitMarkTicks); -const uint32_t kSamsungMinGap = kSamsungMinGapTicks * kSamsungTick; - -const uint16_t kSamsungAcHdrMark = 690; -const uint16_t kSamsungAcHdrSpace = 17844; -const uint8_t kSamsungAcSections = 2; -const uint16_t kSamsungAcSectionMark = 3086; -const uint16_t kSamsungAcSectionSpace = 8864; -const uint16_t kSamsungAcSectionGap = 2886; -const uint16_t kSamsungAcBitMark = 586; -const uint16_t kSamsungAcOneSpace = 1432; -const uint16_t kSamsungAcZeroSpace = 436; - -// Data from https://github.com/crankyoldgit/IRremoteESP8266/issues/1220 -// Values calculated based on the average of ten messages. -const uint16_t kSamsung36HdrMark = 4515; /// < uSeconds -const uint16_t kSamsung36HdrSpace = 4438; /// < uSeconds -const uint16_t kSamsung36BitMark = 512; /// < uSeconds -const uint16_t kSamsung36OneSpace = 1468; /// < uSeconds -const uint16_t kSamsung36ZeroSpace = 490; /// < uSeconds - -using irutils::addBoolToString; -using irutils::addFanToString; -using irutils::addIntToString; -using irutils::addLabeledString; -using irutils::addModeToString; -using irutils::addTempToString; - -#if SEND_SAMSUNG -/// Send a 32-bit Samsung 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. -/// @see http://elektrolab.wz.cz/katalog/samsung_protocol.pdf -/// @note Samsung has a separate message to indicate a repeat, like NEC does. -/// @todo Confirm that is actually how Samsung sends a repeat. -/// The refdoc doesn't indicate it is true. -void IRsend::sendSAMSUNG(const uint64_t data, const uint16_t nbits, - const uint16_t repeat) { - sendGeneric(kSamsungHdrMark, kSamsungHdrSpace, kSamsungBitMark, - kSamsungOneSpace, kSamsungBitMark, kSamsungZeroSpace, - kSamsungBitMark, kSamsungMinGap, kSamsungMinMessageLength, data, - nbits, 38, true, repeat, 33); -} - -/// Construct a raw Samsung message from the supplied customer(address) & -/// command. -/// Status: STABLE / Should be working. -/// @param[in] customer The customer code. (aka. Address) -/// @param[in] command The command code. -/// @return A raw 32-bit Samsung message suitable for `sendSAMSUNG()`. -uint32_t IRsend::encodeSAMSUNG(const uint8_t customer, const uint8_t command) { - uint8_t revcustomer = reverseBits(customer, sizeof(customer) * 8); - uint8_t revcommand = reverseBits(command, sizeof(command) * 8); - return ((revcommand ^ 0xFF) | (revcommand << 8) | (revcustomer << 16) | - (revcustomer << 24)); -} -#endif - -#if DECODE_SAMSUNG -/// Decode the supplied Samsung 32-bit message. -/// Status: STABLE -/// @note Samsung messages whilst 32 bits in size, only contain 16 bits of -/// distinct data. e.g. In transmition order: -/// customer_byte + customer_byte(same) + address_byte + invert(address_byte) -/// @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 LG 32bit protocol appears near identical to the Samsung protocol. -/// They differ on their compliance criteria and how they repeat. -/// @see http://elektrolab.wz.cz/katalog/samsung_protocol.pdf -bool IRrecv::decodeSAMSUNG(decode_results *results, uint16_t offset, - const uint16_t nbits, const bool strict) { - if (strict && nbits != kSamsungBits) - return false; // We expect Samsung to be 32 bits of message. - - uint64_t data = 0; - - // Match Header + Data + Footer - if (!matchGeneric(results->rawbuf + offset, &data, - results->rawlen - offset, nbits, - kSamsungHdrMark, kSamsungHdrSpace, - kSamsungBitMark, kSamsungOneSpace, - kSamsungBitMark, kSamsungZeroSpace, - kSamsungBitMark, kSamsungMinGap, true)) return false; - // Compliance - // According to the spec, the customer (address) code is the first 8 - // transmitted bits. It's then repeated. Check for that. - uint8_t address = data >> 24; - if (strict && address != ((data >> 16) & 0xFF)) return false; - // Spec says the command code is the 3rd block of transmitted 8-bits, - // followed by the inverted command code. - uint8_t command = (data & 0xFF00) >> 8; - if (strict && command != ((data & 0xFF) ^ 0xFF)) return false; - - // Success - results->bits = nbits; - results->value = data; - results->decode_type = SAMSUNG; - // command & address need to be reversed as they are transmitted LSB first, - results->command = reverseBits(command, sizeof(command) * 8); - results->address = reverseBits(address, sizeof(address) * 8); - return true; -} -#endif - -#if SEND_SAMSUNG36 -/// Send a Samsung 36-bit 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. -/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/621 -void IRsend::sendSamsung36(const uint64_t data, const uint16_t nbits, - const uint16_t repeat) { - if (nbits < 16) return; // To small to send. - for (uint16_t r = 0; r <= repeat; r++) { - // Block #1 (16 bits) - sendGeneric(kSamsung36HdrMark, kSamsung36HdrSpace, - kSamsung36BitMark, kSamsung36OneSpace, - kSamsung36BitMark, kSamsung36ZeroSpace, - kSamsung36BitMark, kSamsung36HdrSpace, - data >> (nbits - 16), 16, 38, true, 0, kDutyDefault); - // Block #2 (The rest, typically 20 bits) - sendGeneric(0, 0, // No header - kSamsung36BitMark, kSamsung36OneSpace, - kSamsung36BitMark, kSamsung36ZeroSpace, - kSamsung36BitMark, kSamsungMinGap, // Gap is just a guess. - // Mask off the rest of the bits. - data & ((1ULL << (nbits - 16)) - 1), - nbits - 16, 38, true, 0, kDutyDefault); - } -} -#endif // SEND_SAMSUNG36 - -#if DECODE_SAMSUNG36 -/// Decode the supplied Samsung36 message. -/// Status: STABLE / Expected 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. -/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/621 -bool IRrecv::decodeSamsung36(decode_results *results, uint16_t offset, - const uint16_t nbits, const bool strict) { - if (results->rawlen < 2 * nbits + kHeader + kFooter * 2 - 1 + offset) - return false; // Can't possibly be a valid Samsung message. - // We need to be looking for > 16 bits to make sense. - if (nbits <= 16) return false; - if (strict && nbits != kSamsung36Bits) - return false; // We expect nbits to be 36 bits of message. - - uint64_t data = 0; - - // Match Header + Data + Footer - uint16_t used; - used = matchGeneric(results->rawbuf + offset, &data, - results->rawlen - offset, 16, - kSamsung36HdrMark, kSamsung36HdrSpace, - kSamsung36BitMark, kSamsung36OneSpace, - kSamsung36BitMark, kSamsung36ZeroSpace, - kSamsung36BitMark, kSamsung36HdrSpace, false); - if (!used) return false; - offset += used; - // Data (Block #2) - uint64_t data2 = 0; - if (!matchGeneric(results->rawbuf + offset, &data2, - results->rawlen - offset, nbits - 16, - 0, 0, - kSamsung36BitMark, kSamsung36OneSpace, - kSamsung36BitMark, kSamsung36ZeroSpace, - kSamsung36BitMark, kSamsungMinGap, true)) return false; - data <<= (nbits - 16); - data += data2; - - // Success - results->bits = nbits; - results->value = data; - results->decode_type = SAMSUNG36; - results->command = data & ((1ULL << (nbits - 16)) - 1); - results->address = data >> (nbits - 16); - return true; -} -#endif // DECODE_SAMSUNG36 - -#if SEND_SAMSUNG_AC -/// Send a Samsung A/C message. -/// Status: Stable / Known working. -/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/505 -/// @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::sendSamsungAC(const uint8_t data[], const uint16_t nbytes, - const uint16_t repeat) { - if (nbytes < kSamsungAcStateLength && nbytes % kSamsungAcSectionLength) - return; // Not an appropriate number of bytes to send a proper message. - - enableIROut(38); - for (uint16_t r = 0; r <= repeat; r++) { - // Header - mark(kSamsungAcHdrMark); - space(kSamsungAcHdrSpace); - // Send in 7 byte sections. - for (uint16_t offset = 0; offset < nbytes; - offset += kSamsungAcSectionLength) { - sendGeneric(kSamsungAcSectionMark, kSamsungAcSectionSpace, - kSamsungAcBitMark, kSamsungAcOneSpace, kSamsungAcBitMark, - kSamsungAcZeroSpace, kSamsungAcBitMark, kSamsungAcSectionGap, - data + offset, kSamsungAcSectionLength, // 7 bytes == 56 bits - 38000, false, 0, 50); // Send in LSBF order - } - // Complete made up guess at inter-message gap. - space(kDefaultMessageGap - kSamsungAcSectionGap); - } -} -#endif // SEND_SAMSUNG_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? -IRSamsungAc::IRSamsungAc(const uint16_t pin, const bool inverted, - const bool use_modulation) - : _irsend(pin, inverted, use_modulation) { - stateReset(); -} - -/// Reset the internal state of the emulation. -/// @param[in] forcepower A flag indicating if force sending a special power -/// message with the first `send()` call. -/// @param[in] initialPower Set the initial power state. True, on. False, off. -void IRSamsungAc::stateReset(const bool forcepower, const bool initialPower) { - static const uint8_t kReset[kSamsungAcExtendedStateLength] = { - 0x02, 0x92, 0x0F, 0x00, 0x00, 0x00, 0xF0, - 0x01, 0x02, 0xAE, 0x71, 0x00, 0x15, 0xF0}; - std::memcpy(_.raw, kReset, kSamsungAcExtendedStateLength); - _forcepower = forcepower; - _lastsentpowerstate = initialPower; - setPower(initialPower); -} - -/// Set up hardware to be able to send a message. -void IRSamsungAc::begin(void) { _irsend.begin(); } - -/// Get the existing checksum for a given state section. -/// @param[in] section The array to extract the checksum from. -/// @return The existing checksum value. -/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1538#issuecomment-894645947 -uint8_t IRSamsungAc::getSectionChecksum(const uint8_t *section) { - return ((GETBITS8(*(section + 2), kLowNibble, kNibbleSize) << kNibbleSize) + - GETBITS8(*(section + 1), kHighNibble, kNibbleSize)); -} - -/// Calculate the checksum for a given state section. -/// @param[in] section The array to calc the checksum of. -/// @return The calculated checksum value. -/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1538#issuecomment-894645947 -uint8_t IRSamsungAc::calcSectionChecksum(const uint8_t *section) { - uint8_t sum = 0; - - sum += countBits(*section, 8); // Include the entire first byte - // The lower half of the second byte. - sum += countBits(GETBITS8(*(section + 1), kLowNibble, kNibbleSize), 8); - // The upper half of the third byte. - sum += countBits(GETBITS8(*(section + 2), kHighNibble, kNibbleSize), 8); - // The next 4 bytes. - sum += countBits(section + 3, 4); - // Bitwise invert the result. - return sum ^ UINT8_MAX; -} - -/// 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 IRSamsungAc::validChecksum(const uint8_t state[], const uint16_t length) { - bool result = true; - const uint16_t maxlength = - (length > kSamsungAcExtendedStateLength) ? kSamsungAcExtendedStateLength - : length; - for (uint16_t offset = 0; - offset + kSamsungAcSectionLength <= maxlength; - offset += kSamsungAcSectionLength) - result &= (getSectionChecksum(state + offset) == - calcSectionChecksum(state + offset)); - return result; -} - -/// Update the checksum for the internal state. -void IRSamsungAc::checksum(void) { - uint8_t sectionsum = calcSectionChecksum(_.raw); - _.Sum1Upper = GETBITS8(sectionsum, kHighNibble, kNibbleSize); - _.Sum1Lower = GETBITS8(sectionsum, kLowNibble, kNibbleSize); - sectionsum = calcSectionChecksum(_.raw + kSamsungAcSectionLength); - _.Sum2Upper = GETBITS8(sectionsum, kHighNibble, kNibbleSize); - _.Sum2Lower = GETBITS8(sectionsum, kLowNibble, kNibbleSize); - sectionsum = calcSectionChecksum(_.raw + kSamsungAcSectionLength * 2); - _.Sum3Upper = GETBITS8(sectionsum, kHighNibble, kNibbleSize); - _.Sum3Lower = GETBITS8(sectionsum, kLowNibble, kNibbleSize); -} - -#if SEND_SAMSUNG_AC -/// Send the current internal state as an IR message. -/// @param[in] repeat Nr. of times the message will be repeated. -/// @param[in] calcchecksum Do we update the checksum before sending? -/// @note Use for most function/mode/settings changes to the unit. -/// i.e. When the device is already running. -void IRSamsungAc::send(const uint16_t repeat, const bool calcchecksum) { - // Do we need to send a the special power on/off message? i.e. An Extended Msg - if (getPower() != _lastsentpowerstate || _forcepower) { // We do. - sendExtended(repeat, calcchecksum); - _forcepower = false; // It has now been sent, so clear the flag if set. - } else { // No, it's just a normal message. - if (calcchecksum) checksum(); - _irsend.sendSamsungAC(_.raw, kSamsungAcStateLength, repeat); - } -} - -/// Send the extended current internal state as an IR message. -/// @param[in] repeat Nr. of times the message will be repeated. -/// @param[in] calcchecksum Do we update the checksum before sending? -/// @note Use this for when you need to power on/off the device. -/// Samsung A/C requires an extended length message when you want to -/// change the power operating mode of the A/C unit. -void IRSamsungAc::sendExtended(const uint16_t repeat, const bool calcchecksum) { - static const uint8_t extended_middle_section[kSamsungAcSectionLength] = { - 0x01, 0xD2, 0x0F, 0x00, 0x00, 0x00, 0x00}; - if (calcchecksum) checksum(); - // Copy/convert the internal state to an extended state by - // copying the second section to the third section, and inserting the extended - // middle (second) section. - std::memcpy(_.raw + 2 * kSamsungAcSectionLength, - _.raw + kSamsungAcSectionLength, - kSamsungAcSectionLength); - std::memcpy(_.raw + kSamsungAcSectionLength, extended_middle_section, - kSamsungAcSectionLength); - // Send it. - _irsend.sendSamsungAC(_.raw, kSamsungAcExtendedStateLength, repeat); - // Now revert it by copying the third section over the second section. - std::memcpy(_.raw + kSamsungAcSectionLength, - _.raw + 2* kSamsungAcSectionLength, - kSamsungAcSectionLength); - _lastsentpowerstate = getPower(); // Remember the last power state sent. -} - -/// Send the special extended "On" message as the library can't seem to -/// reproduce this message automatically. -/// @param[in] repeat Nr. of times the message will be repeated. -/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/604#issuecomment-475020036 -void IRSamsungAc::sendOn(const uint16_t repeat) { - const uint8_t extended_state[kSamsungAcExtendedStateLength] = { - 0x02, 0x92, 0x0F, 0x00, 0x00, 0x00, 0xF0, - 0x01, 0xD2, 0x0F, 0x00, 0x00, 0x00, 0x00, - 0x01, 0xE2, 0xFE, 0x71, 0x80, 0x11, 0xF0}; - _irsend.sendSamsungAC(extended_state, kSamsungAcExtendedStateLength, repeat); - _lastsentpowerstate = true; // On -} - -/// Send the special extended "Off" message as the library can't seem to -/// reproduce this message automatically. -/// @param[in] repeat Nr. of times the message will be repeated. -/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/604#issuecomment-475020036 -void IRSamsungAc::sendOff(const uint16_t repeat) { - const uint8_t extended_state[kSamsungAcExtendedStateLength] = { - 0x02, 0xB2, 0x0F, 0x00, 0x00, 0x00, 0xC0, - 0x01, 0xD2, 0x0F, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x02, 0xFF, 0x71, 0x80, 0x11, 0xC0}; - _irsend.sendSamsungAC(extended_state, kSamsungAcExtendedStateLength, repeat); - _lastsentpowerstate = false; // Off -} -#endif // SEND_SAMSUNG_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 *IRSamsungAc::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 IRSamsungAc::setRaw(const uint8_t new_code[], const uint16_t length) { - std::memcpy(_.raw, new_code, std::min(length, - kSamsungAcExtendedStateLength)); - // Shrink the extended state into a normal state. - if (length > kSamsungAcStateLength) { - for (uint8_t i = kSamsungAcStateLength; i < length; i++) - _.raw[i - kSamsungAcSectionLength] = _.raw[i]; - } -} - -/// Set the requested power state of the A/C to on. -void IRSamsungAc::on(void) { setPower(true); } - -/// Set the requested power state of the A/C to off. -void IRSamsungAc::off(void) { setPower(false); } - -/// Change the power setting. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRSamsungAc::setPower(const bool on) { - _.Power1 = !on; // Cleared when on. - _.Power6 = (on ? 0b11 : 0b00); -} - -/// Get the value of the current power setting. -/// @return true, the setting is on. false, the setting is off. -bool IRSamsungAc::getPower(void) const { - return (_.Power6 == 0b11) && !_.Power1; -} - -/// Set the temperature. -/// @param[in] temp The temperature in degrees celsius. -void IRSamsungAc::setTemp(const uint8_t temp) { - uint8_t newtemp = std::max(kSamsungAcMinTemp, temp); - newtemp = std::min(kSamsungAcMaxTemp, newtemp); - _.Temp = newtemp - kSamsungAcMinTemp; -} - -/// Get the current temperature setting. -/// @return The current setting for temp. in degrees celsius. -uint8_t IRSamsungAc::getTemp(void) const { - return _.Temp + kSamsungAcMinTemp; -} - -/// Set the operating mode of the A/C. -/// @param[in] mode The desired operating mode. -void IRSamsungAc::setMode(const uint8_t mode) { - // If we get an unexpected mode, default to AUTO. - uint8_t newmode = mode; - if (newmode > kSamsungAcHeat) newmode = kSamsungAcAuto; - _.Mode = newmode; - - // Auto mode has a special fan setting valid only in auto mode. - if (newmode == kSamsungAcAuto) { - _.Fan = kSamsungAcFanAuto2; - } else { - // Non-Auto can't have this fan setting - if (_.Fan == kSamsungAcFanAuto2) - _.Fan = kSamsungAcFanAuto; // Default to something safe. - } -} - -/// Get the operating mode setting of the A/C. -/// @return The current operating mode setting. -uint8_t IRSamsungAc::getMode(void) const { - return _.Mode; -} - -/// Set the speed of the fan. -/// @param[in] speed The desired setting. -void IRSamsungAc::setFan(const uint8_t speed) { - switch (speed) { - case kSamsungAcFanAuto: - case kSamsungAcFanLow: - case kSamsungAcFanMed: - case kSamsungAcFanHigh: - case kSamsungAcFanTurbo: - if (_.Mode == kSamsungAcAuto) return; // Not valid in Auto mode. - break; - case kSamsungAcFanAuto2: // Special fan setting for when in Auto mode. - if (_.Mode != kSamsungAcAuto) return; - break; - default: - return; - } - _.Fan = speed; -} - -/// Get the current fan speed setting. -/// @return The current fan speed/mode. -uint8_t IRSamsungAc::getFan(void) const { - return _.Fan; -} - -/// Get the vertical swing setting of the A/C. -/// @return true, the setting is on. false, the setting is off. -/// @todo (Hollako) Explain why sometimes the LSB of remote_state[9] is a 1. -/// e.g. 0xAE or 0XAF for swing move. -bool IRSamsungAc::getSwing(void) const { - return _.Swing == kSamsungAcSwingMove; -} - -/// Set the vertical swing setting of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -/// @todo (Hollako) Explain why sometimes the LSB of remote_state[9] is a 1. -/// e.g. 0xAE or 0XAF for swing move. -void IRSamsungAc::setSwing(const bool on) { - _.Swing = (on ? kSamsungAcSwingMove : kSamsungAcSwingStop); -} - -/// Get the Beep setting of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRSamsungAc::getBeep(void) const { - return _.Beep; -} - -/// Set the Beep setting of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRSamsungAc::setBeep(const bool on) { - _.Beep = on; -} - -/// Get the Clean setting of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRSamsungAc::getClean(void) const { - return _.Clean10 && _.Clean11; -} - -/// Set the Clean setting of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRSamsungAc::setClean(const bool on) { - _.Clean10 = on; - _.Clean11 = on; -} - -/// Get the Quiet setting of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRSamsungAc::getQuiet(void) const { - return !_.Quiet1 && _.Quiet5; -} - -/// Set the Quiet setting of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRSamsungAc::setQuiet(const bool on) { - _.Quiet1 = !on; // Cleared when on. - _.Quiet5 = on; - if (on) { - // Quiet mode seems to set fan speed to auto. - setFan(kSamsungAcFanAuto); - setPowerful(false); // Quiet 'on' is mutually exclusive to Powerful. - } -} - -/// Get the Powerful (Turbo) setting of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRSamsungAc::getPowerful(void) const { - return !(_.Powerful8 & kSamsungAcPowerfulMask8) && - (_.Powerful10 == kSamsungAcPowerful10On) && - (_.Fan == kSamsungAcFanTurbo); -} - -/// Set the Powerful (Turbo) setting of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRSamsungAc::setPowerful(const bool on) { - uint8_t off_value = getBreeze() ? kSamsungAcBreezeOn : 0b000; - _.Powerful10 = (on ? kSamsungAcPowerful10On : off_value); - if (on) { - _.Powerful8 &= ~kSamsungAcPowerfulMask8; // Bit needs to be cleared. - // Powerful mode sets fan speed to Turbo. - setFan(kSamsungAcFanTurbo); - setQuiet(false); // Powerful 'on' is mutually exclusive to Quiet. - } else { - _.Powerful8 |= kSamsungAcPowerfulMask8; // Bit needs to be set. - // Turning off Powerful mode sets fan speed to Auto if we were in Turbo mode - if (_.Fan == kSamsungAcFanTurbo) setFan(kSamsungAcFanAuto); - } -} - -/// Are the vanes closed over the fan outlet, to stop direct wind? Aka. WindFree -/// @return true, the setting is on. false, the setting is off. -/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1062 -bool IRSamsungAc::getBreeze(void) const { - return (_.Breeze == kSamsungAcBreezeOn) && - (_.Fan == kSamsungAcFanAuto && !getSwing()); -} - -/// Closes the vanes over the fan outlet, to stop direct wind. Aka. WindFree -/// @param[in] on true, the setting is on. false, the setting is off. -/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1062 -void IRSamsungAc::setBreeze(const bool on) { - uint8_t off_value = getPowerful() ? kSamsungAcPowerful10On : 0b000; - _.Breeze = (on ? kSamsungAcBreezeOn : off_value); - if (on) { - setFan(kSamsungAcFanAuto); - setSwing(false); - } -} - -/// Get the Display (Light/LED) setting of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRSamsungAc::getDisplay(void) const { - return _.Display; -} - -/// Set the Display (Light/LED) setting of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRSamsungAc::setDisplay(const bool on) { - _.Display = on; -} - -/// Get the Ion (Filter) setting of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRSamsungAc::getIon(void) const { - return _.Ion; -} - -/// Set the Ion (Filter) setting of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRSamsungAc::setIon(const bool on) { - _.Ion = on; -} - -/// 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 IRSamsungAc::convertMode(const stdAc::opmode_t mode) { - switch (mode) { - case stdAc::opmode_t::kCool: return kSamsungAcCool; - case stdAc::opmode_t::kHeat: return kSamsungAcHeat; - case stdAc::opmode_t::kDry: return kSamsungAcDry; - case stdAc::opmode_t::kFan: return kSamsungAcFan; - default: return kSamsungAcAuto; - } -} - -/// 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 IRSamsungAc::convertFan(const stdAc::fanspeed_t speed) { - switch (speed) { - case stdAc::fanspeed_t::kMin: - case stdAc::fanspeed_t::kLow: return kSamsungAcFanLow; - case stdAc::fanspeed_t::kMedium: return kSamsungAcFanMed; - case stdAc::fanspeed_t::kHigh: return kSamsungAcFanHigh; - case stdAc::fanspeed_t::kMax: return kSamsungAcFanTurbo; - default: return kSamsungAcFanAuto; - } -} - -/// 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 IRSamsungAc::toCommonMode(const uint8_t mode) { - switch (mode) { - case kSamsungAcCool: return stdAc::opmode_t::kCool; - case kSamsungAcHeat: return stdAc::opmode_t::kHeat; - case kSamsungAcDry: return stdAc::opmode_t::kDry; - case kSamsungAcFan: return stdAc::opmode_t::kFan; - default: return stdAc::opmode_t::kAuto; - } -} - -/// Convert a native fan speed into its stdAc equivalent. -/// @param[in] spd The native setting to be converted. -/// @return The stdAc equivalent of the native setting. -stdAc::fanspeed_t IRSamsungAc::toCommonFanSpeed(const uint8_t spd) { - switch (spd) { - case kSamsungAcFanTurbo: return stdAc::fanspeed_t::kMax; - case kSamsungAcFanHigh: return stdAc::fanspeed_t::kHigh; - case kSamsungAcFanMed: return stdAc::fanspeed_t::kMedium; - case kSamsungAcFanLow: 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 IRSamsungAc::toCommon(void) const { - stdAc::state_t result; - result.protocol = decode_type_t::SAMSUNG_AC; - result.model = -1; // Not supported. - result.power = getPower(); - result.mode = toCommonMode(_.Mode); - result.celsius = true; - result.degrees = getTemp(); - result.fanspeed = toCommonFanSpeed(_.Fan); - result.swingv = getSwing() ? stdAc::swingv_t::kAuto : - stdAc::swingv_t::kOff; - result.quiet = getQuiet(); - result.turbo = getPowerful(); - result.clean = getClean(); - result.beep = _.Beep; - result.light = _.Display; - result.filter = _.Ion; - // Not supported. - result.swingh = stdAc::swingh_t::kOff; - result.econo = 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 IRSamsungAc::toString(void) const { - String result = ""; - result.reserve(115); // Reserve some heap for the string to reduce fragging. - result += addBoolToString(getPower(), kPowerStr, false); - result += addModeToString(_.Mode, kSamsungAcAuto, kSamsungAcCool, - kSamsungAcHeat, kSamsungAcDry, - kSamsungAcFan); - result += addTempToString(getTemp()); - result += addIntToString(_.Fan, kFanStr); - result += kSpaceLBraceStr; - switch (_.Fan) { - case kSamsungAcFanAuto: - case kSamsungAcFanAuto2: - result += kAutoStr; - break; - case kSamsungAcFanLow: - result += kLowStr; - break; - case kSamsungAcFanMed: - result += kMedStr; - break; - case kSamsungAcFanHigh: - result += kHighStr; - break; - case kSamsungAcFanTurbo: - result += kTurboStr; - break; - default: - result += kUnknownStr; - break; - } - result += ')'; - result += addBoolToString(getSwing(), kSwingStr); - result += addBoolToString(_.Beep, kBeepStr); - result += addBoolToString(getClean(), kCleanStr); - result += addBoolToString(getQuiet(), kQuietStr); - result += addBoolToString(getPowerful(), kPowerfulStr); - result += addBoolToString(getBreeze(), kBreezeStr); - result += addBoolToString(_.Display, kLightStr); - result += addBoolToString(_.Ion, kIonStr); - return result; -} - -#if DECODE_SAMSUNG_AC -/// Decode the supplied Samsung A/C message. -/// Status: Stable / Known 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. -/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/505 -bool IRrecv::decodeSamsungAC(decode_results *results, uint16_t offset, - const uint16_t nbits, const bool strict) { - if (results->rawlen < 2 * nbits + kHeader * 3 + kFooter * 2 - 1 + offset) - return false; // Can't possibly be a valid Samsung A/C message. - if (nbits != kSamsungAcBits && nbits != kSamsungAcExtendedBits) return false; - - // Message Header - if (!matchMark(results->rawbuf[offset++], kSamsungAcBitMark)) return false; - if (!matchSpace(results->rawbuf[offset++], kSamsungAcHdrSpace)) return false; - // Section(s) - for (uint16_t pos = 0; pos <= (nbits / 8) - kSamsungAcSectionLength; - pos += kSamsungAcSectionLength) { - uint16_t used; - // Section Header + Section Data (7 bytes) + Section Footer - used = matchGeneric(results->rawbuf + offset, results->state + pos, - results->rawlen - offset, kSamsungAcSectionLength * 8, - kSamsungAcSectionMark, kSamsungAcSectionSpace, - kSamsungAcBitMark, kSamsungAcOneSpace, - kSamsungAcBitMark, kSamsungAcZeroSpace, - kSamsungAcBitMark, kSamsungAcSectionGap, - pos + kSamsungAcSectionLength >= nbits / 8, - _tolerance, 0, false); - if (used == 0) return false; - offset += used; - } - // Compliance - // Is the signature correct? - DPRINTLN("DEBUG: Checking signature."); - if (results->state[0] != 0x02 || results->state[2] != 0x0F) return false; - if (strict) { - // Is the checksum valid? - if (!IRSamsungAc::validChecksum(results->state, nbits / 8)) { - DPRINTLN("DEBUG: Checksum failed!"); - return false; - } - } - // Success - results->decode_type = SAMSUNG_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_SAMSUNG_AC diff --git a/lib/IRremoteESP8266/src/ir_Samsung.h b/lib/IRremoteESP8266/src/ir_Samsung.h index bf9215edcc..0e69a545f2 100644 --- a/lib/IRremoteESP8266/src/ir_Samsung.h +++ b/lib/IRremoteESP8266/src/ir_Samsung.h @@ -32,10 +32,10 @@ #ifndef UNIT_TEST #include #endif -#include "IRremoteESP8266.h" -#include "IRsend.h" +#include "../src/IRremoteESP8266.h" +#include "../src/IRsend.h" #ifdef UNIT_TEST -#include "IRsend_test.h" +#include "../src/IRsend_test.h" #endif /// Native representation of a Samsung A/C message. diff --git a/lib/IRremoteESP8266/src/ir_Sanyo.cpp b/lib/IRremoteESP8266/src/ir_Sanyo.cpp deleted file mode 100644 index 7dbed5a582..0000000000 --- a/lib/IRremoteESP8266/src/ir_Sanyo.cpp +++ /dev/null @@ -1,978 +0,0 @@ -// Copyright 2009 Ken Shirriff -// Copyright 2016 marcosamarinho -// Copyright 2017-2021 David Conran - -/// @file -/// @brief Support for Sanyo protocols. -/// Sanyo LC7461 support originally by marcosamarinho -/// Sanyo SA 8650B originally added from -/// https://github.com/shirriff/Arduino-IRremote/ -/// @see https://github.com/z3t0/Arduino-IRremote/blob/master/ir_Sanyo.cpp -/// @see http://pdf.datasheetcatalog.com/datasheet/sanyo/LC7461.pdf -/// @see https://github.com/marcosamarinho/IRremoteESP8266/blob/master/ir_Sanyo.cpp -/// @see http://slydiman.narod.ru/scr/kb/sanyo.htm -/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1211 -/// @see https://docs.google.com/spreadsheets/d/1dYfLsnYvpjV-SgO8pdinpfuBIpSzm8Q1R5SabrLeskw/edit?usp=sharing -/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1503 - -#include "ir_Sanyo.h" -#include -#include -#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::addSwingVToString; -using irutils::addTempToString; -using irutils::minsToString; -using irutils::sumNibbles; - -// Constants -// Sanyo SA 8650B -const uint16_t kSanyoSa8650bHdrMark = 3500; // seen range 3500 -const uint16_t kSanyoSa8650bHdrSpace = 950; // seen 950 -const uint16_t kSanyoSa8650bOneMark = 2400; // seen 2400 -const uint16_t kSanyoSa8650bZeroMark = 700; // seen 700 -// usually see 713 - not using ticks as get number wrapround -const uint16_t kSanyoSa8650bDoubleSpaceUsecs = 800; -const uint16_t kSanyoSa8650bRptLength = 45000; - -// Sanyo LC7461 -const uint16_t kSanyoLc7461AddressMask = (1 << kSanyoLC7461AddressBits) - 1; -const uint16_t kSanyoLc7461CommandMask = (1 << kSanyoLC7461CommandBits) - 1; -const uint16_t kSanyoLc7461HdrMark = 9000; -const uint16_t kSanyoLc7461HdrSpace = 4500; -const uint16_t kSanyoLc7461BitMark = 560; // 1T -const uint16_t kSanyoLc7461OneSpace = 1690; // 3T -const uint16_t kSanyoLc7461ZeroSpace = 560; // 1T -const uint32_t kSanyoLc7461MinCommandLength = 108000; - -const uint16_t kSanyoLc7461MinGap = - kSanyoLc7461MinCommandLength - - (kSanyoLc7461HdrMark + kSanyoLc7461HdrSpace + - kSanyoLC7461Bits * (kSanyoLc7461BitMark + - (kSanyoLc7461OneSpace + kSanyoLc7461ZeroSpace) / 2) + - kSanyoLc7461BitMark); - -const uint16_t kSanyoAcHdrMark = 8500; ///< uSeconds -const uint16_t kSanyoAcHdrSpace = 4200; ///< uSeconds -const uint16_t kSanyoAcBitMark = 500; ///< uSeconds -const uint16_t kSanyoAcOneSpace = 1600; ///< uSeconds -const uint16_t kSanyoAcZeroSpace = 550; ///< uSeconds -const uint32_t kSanyoAcGap = kDefaultMessageGap; ///< uSeconds (Guess only) -const uint16_t kSanyoAcFreq = 38000; ///< Hz. (Guess only) - -const uint16_t kSanyoAc88HdrMark = 5400; ///< uSeconds -const uint16_t kSanyoAc88HdrSpace = 2000; ///< uSeconds -const uint16_t kSanyoAc88BitMark = 500; ///< uSeconds -const uint16_t kSanyoAc88OneSpace = 1500; ///< uSeconds -const uint16_t kSanyoAc88ZeroSpace = 750; ///< uSeconds -const uint32_t kSanyoAc88Gap = 3675; ///< uSeconds -const uint16_t kSanyoAc88Freq = 38000; ///< Hz. (Guess only) -const uint8_t kSanyoAc88ExtraTolerance = 5; /// (%) Extra tolerance to use. - -#if SEND_SANYO -/// Construct a Sanyo LC7461 message. -/// @param[in] address The 13 bit value of the address(Custom) portion of the -/// protocol. -/// @param[in] command The 8 bit value of the command(Key) portion of the -/// protocol. -/// @return An uint64_t with the encoded raw 42 bit Sanyo LC7461 data value. -/// @note This protocol uses the NEC protocol timings. However, data is -/// formatted as : address(13 bits), !address, command(8 bits), !command. -/// According with LIRC, this protocol is used on Sanyo, Aiwa and Chinon -uint64_t IRsend::encodeSanyoLC7461(uint16_t address, uint8_t command) { - // Mask our input values to ensure the correct bit sizes. - address &= kSanyoLc7461AddressMask; - command &= kSanyoLc7461CommandMask; - - uint64_t data = address; - address ^= kSanyoLc7461AddressMask; // Invert the 13 LSBs. - // Append the now inverted address. - data = (data << kSanyoLC7461AddressBits) | address; - // Append the command. - data = (data << kSanyoLC7461CommandBits) | command; - command ^= kSanyoLc7461CommandMask; // Invert the command. - // Append the now inverted command. - data = (data << kSanyoLC7461CommandBits) | command; - - return data; -} - -/// Send a Sanyo LC7461 message. -/// Status: BETA / Probably works. -/// @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 Based on \@marcosamarinho's work. -/// This protocol uses the NEC protocol timings. However, data is -/// formatted as : address(13 bits), !address, command (8 bits), !command. -/// According with LIRC, this protocol is used on Sanyo, Aiwa and Chinon -/// Information for this protocol is available at the Sanyo LC7461 datasheet. -/// Repeats are performed similar to the NEC method of sending a special -/// repeat message, rather than duplicating the entire message. -/// @see https://github.com/marcosamarinho/IRremoteESP8266/blob/master/ir_Sanyo.cpp -/// @see http://pdf.datasheetcatalog.com/datasheet/sanyo/LC7461.pdf -void IRsend::sendSanyoLC7461(const uint64_t data, const uint16_t nbits, - const uint16_t repeat) { - // This protocol appears to be another 42-bit variant of the NEC protocol. - sendNEC(data, nbits, repeat); -} -#endif // SEND_SANYO - -#if DECODE_SANYO -/// Decode the supplied SANYO LC7461 message. -/// Status: BETA / Probably works. -/// @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 Based on \@marcosamarinho's work. -/// This protocol uses the NEC protocol. However, data is -/// formatted as : address(13 bits), !address, command (8 bits), !command. -/// According with LIRC, this protocol is used on Sanyo, Aiwa and Chinon -/// Information for this protocol is available at the Sanyo LC7461 datasheet. -/// @see http://slydiman.narod.ru/scr/kb/sanyo.htm -/// @see https://github.com/marcosamarinho/IRremoteESP8266/blob/master/ir_Sanyo.cpp -/// @see http://pdf.datasheetcatalog.com/datasheet/sanyo/LC7461.pdf -bool IRrecv::decodeSanyoLC7461(decode_results *results, uint16_t offset, - const uint16_t nbits, const bool strict) { - if (strict && nbits != kSanyoLC7461Bits) - return false; // Not strictly in spec. - // This protocol is basically a 42-bit variant of the NEC protocol. - if (!decodeNEC(results, offset, nbits, false)) - return false; // Didn't match a NEC format (without strict) - - // Bits 30 to 42+. - uint16_t address = - results->value >> (kSanyoLC7461Bits - kSanyoLC7461AddressBits); - // Bits 9 to 16. - uint8_t command = - (results->value >> kSanyoLC7461CommandBits) & kSanyoLc7461CommandMask; - // Compliance - if (strict) { - if (results->bits != nbits) return false; - // Bits 17 to 29. - uint16_t inverted_address = - (results->value >> (kSanyoLC7461CommandBits * 2)) & - kSanyoLc7461AddressMask; - // Bits 1-8. - uint8_t inverted_command = results->value & kSanyoLc7461CommandMask; - if ((address ^ kSanyoLc7461AddressMask) != inverted_address) - return false; // Address integrity check failed. - if ((command ^ kSanyoLc7461CommandMask) != inverted_command) - return false; // Command integrity check failed. - } - - // Success - results->decode_type = SANYO_LC7461; - results->address = address; - results->command = command; - return true; -} - -/* NOTE: Disabled due to poor quality. -/// Decode the supplied Sanyo SA 8650B message. -/// Status: Depricated. -/// @depricated Disabled due to poor quality. -/// @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. -/// @warning This decoder looks like rubbish. Only keeping it for compatibility -/// with the Arduino IRremote library. Seriously, don't trust it. -/// If someone has a device that this is supposed to be for, please log an -/// Issue on github with a rawData dump please. We should probably remove it. -/// We think this is a Sanyo decoder - serial = SA 8650B -/// @see https://github.com/z3t0/Arduino-IRremote/blob/master/ir_Sanyo.cpp -bool IRrecv::decodeSanyo(decode_results *results, uint16_t nbits, bool strict) { - if (results->rawlen < 2 * nbits + kHeader - 1) - return false; // Shorter than shortest possible. - if (strict && nbits != kSanyoSA8650BBits) - return false; // Doesn't match the spec. - - uint16_t offset = 0; - - // TODO(crankyoldgit): This repeat code looks like garbage, it should never - // match or if it does, it won't be reliable. We should probably just - // remove it. - if (results->rawbuf[offset++] < kSanyoSa8650bDoubleSpaceUsecs) { - results->bits = 0; - results->value = kRepeat; - results->decode_type = SANYO; - results->address = 0; - results->command = 0; - results->repeat = true; - return true; - } - - // Header - if (!matchMark(results->rawbuf[offset++], kSanyoSa8650bHdrMark)) - return false; - // NOTE: These next two lines look very wrong. Treat as suspect. - if (!matchMark(results->rawbuf[offset++], kSanyoSa8650bHdrMark)) - return false; - // Data - uint64_t data = 0; - while (offset + 1 < results->rawlen) { - if (!matchSpace(results->rawbuf[offset], kSanyoSa8650bHdrSpace)) - break; - offset++; - if (matchMark(results->rawbuf[offset], kSanyoSa8650bOneMark)) - data = (data << 1) | 1; // 1 - else if (matchMark(results->rawbuf[offset], kSanyoSa8650bZeroMark)) - data <<= 1; // 0 - else - return false; - offset++; - } - - if (strict && kSanyoSA8650BBits > (offset - 1U) / 2U) - return false; - - // Success - results->bits = (offset - 1) / 2; - results->decode_type = SANYO; - results->value = data; - results->address = 0; - results->command = 0; - return true; -} -*/ -#endif // DECODE_SANYO - - -#if SEND_SANYO_AC -/// Send a SanyoAc 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. -/// @param[in] repeat Nr. of times the message is to be repeated. -/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1211 -void IRsend::sendSanyoAc(const uint8_t data[], const uint16_t nbytes, - const uint16_t repeat) { - // Header + Data + Footer - sendGeneric(kSanyoAcHdrMark, kSanyoAcHdrSpace, - kSanyoAcBitMark, kSanyoAcOneSpace, - kSanyoAcBitMark, kSanyoAcZeroSpace, - kSanyoAcBitMark, kSanyoAcGap, - data, nbytes, kSanyoAcFreq, false, repeat, kDutyDefault); -} -#endif // SEND_SANYO_AC - -#if DECODE_SANYO_AC -/// Decode the supplied SanyoAc 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. -/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1211 -bool IRrecv::decodeSanyoAc(decode_results *results, uint16_t offset, - const uint16_t nbits, const bool strict) { - if (strict && nbits != kSanyoAcBits) - return false; - - // Header + Data + Footer - if (!matchGeneric(results->rawbuf + offset, results->state, - results->rawlen - offset, nbits, - kSanyoAcHdrMark, kSanyoAcHdrSpace, - kSanyoAcBitMark, kSanyoAcOneSpace, - kSanyoAcBitMark, kSanyoAcZeroSpace, - kSanyoAcBitMark, kSanyoAcGap, - true, kUseDefTol, kMarkExcess, false)) return false; - // Compliance - if (strict) - if (!IRSanyoAc::validChecksum(results->state, nbits / 8)) return false; - - // Success - results->decode_type = decode_type_t::SANYO_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_SANYO_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? -IRSanyoAc::IRSanyoAc(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 state/sequence. -void IRSanyoAc::stateReset(void) { - static const uint8_t kReset[kSanyoAcStateLength] = { - 0x6A, 0x6D, 0x51, 0x00, 0x10, 0x45, 0x00, 0x00, 0x33}; - std::memcpy(_.raw, kReset, kSanyoAcStateLength); -} - -/// Set up hardware to be able to send a message. -void IRSanyoAc::begin(void) { _irsend.begin(); } - -#if SEND_SANYO_AC -/// Send the current internal state as IR messages. -/// @param[in] repeat Nr. of times the message will be repeated. -void IRSanyoAc::send(const uint16_t repeat) { - _irsend.sendSanyoAc(getRaw(), kSanyoAcStateLength, repeat); -} -#endif // SEND_SANYO_AC - -/// Get a PTR to the internal state/code for this protocol with all integrity -/// checks passing. -/// @return PTR to a code for this protocol based on the current internal state. -uint8_t* IRSanyoAc::getRaw(void) { - checksum(); - return _.raw; -} - -/// Set the internal state from a valid code for this protocol. -/// @param[in] newState A valid code for this protocol. -void IRSanyoAc::setRaw(const uint8_t newState[]) { - std::memcpy(_.raw, newState, kSanyoAcStateLength); -} - -/// 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 IRSanyoAc::calcChecksum(const uint8_t state[], - const uint16_t length) { - return length ? sumNibbles(state, length - 1) : 0; -} - -/// 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 IRSanyoAc::validChecksum(const uint8_t state[], const uint16_t length) { - return length && state[length - 1] == IRSanyoAc::calcChecksum(state, length); -} - -/// Calculate & set the checksum for the current internal state of the remote. -void IRSanyoAc::checksum(void) { - // Stored the checksum value in the last byte. - _.Sum = calcChecksum(_.raw); -} - - -/// Set the requested power state of the A/C to on. -void IRSanyoAc::on(void) { setPower(true); } - -/// Set the requested power state of the A/C to off. -void IRSanyoAc::off(void) { setPower(false); } - -/// Change the power setting. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRSanyoAc::setPower(const bool on) { - _.Power = (on ? kSanyoAcPowerOn : kSanyoAcPowerOff); -} - -/// Get the value of the current power setting. -/// @return true, the setting is on. false, the setting is off. -bool IRSanyoAc::getPower(void) const { - return _.Power == kSanyoAcPowerOn; -} - -/// Get the operating mode setting of the A/C. -/// @return The current operating mode setting. -uint8_t IRSanyoAc::getMode(void) const { - return _.Mode; -} - -/// Set the operating mode of the A/C. -/// @param[in] mode The desired operating mode. -/// @note If we get an unexpected mode, default to AUTO. -void IRSanyoAc::setMode(const uint8_t mode) { - switch (mode) { - case kSanyoAcAuto: - case kSanyoAcCool: - case kSanyoAcDry: - case kSanyoAcHeat: - _.Mode = mode; - break; - default: _.Mode = kSanyoAcAuto; - } -} - -/// 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 IRSanyoAc::convertMode(const stdAc::opmode_t mode) { - switch (mode) { - case stdAc::opmode_t::kCool: return kSanyoAcCool; - case stdAc::opmode_t::kHeat: return kSanyoAcHeat; - case stdAc::opmode_t::kDry: return kSanyoAcDry; - default: return kSanyoAcAuto; - } -} - -/// 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 IRSanyoAc::toCommonMode(const uint8_t mode) { - switch (mode) { - case kSanyoAcCool: return stdAc::opmode_t::kCool; - case kSanyoAcHeat: return stdAc::opmode_t::kHeat; - case kSanyoAcDry: return stdAc::opmode_t::kDry; - default: return stdAc::opmode_t::kAuto; - } -} - -/// Set the desired temperature. -/// @param[in] degrees The temperature in degrees celsius. -void IRSanyoAc::setTemp(const uint8_t degrees) { - uint8_t temp = std::max((uint8_t)kSanyoAcTempMin, degrees); - temp = std::min((uint8_t)kSanyoAcTempMax, temp); - _.Temp = temp - kSanyoAcTempDelta; -} - -/// Get the current desired temperature setting. -/// @return The current setting for temp. in degrees celsius. -uint8_t IRSanyoAc::getTemp(void) const { - return _.Temp + kSanyoAcTempDelta; -} - -/// Set the sensor temperature. -/// @param[in] degrees The temperature in degrees celsius. -void IRSanyoAc::setSensorTemp(const uint8_t degrees) { - uint8_t temp = std::max((uint8_t)kSanyoAcTempMin, degrees); - temp = std::min((uint8_t)kSanyoAcTempMax, temp); - _.SensorTemp = temp - kSanyoAcTempDelta; -} - -/// Get the current sensor temperature setting. -/// @return The current setting for temp. in degrees celsius. -uint8_t IRSanyoAc::getSensorTemp(void) const { - return _.SensorTemp + kSanyoAcTempDelta; -} - -/// Set the speed of the fan. -/// @param[in] speed The desired setting. -void IRSanyoAc::setFan(const uint8_t speed) { - _.Fan = speed; -} - -/// Get the current fan speed setting. -/// @return The current fan speed/mode. -uint8_t IRSanyoAc::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 IRSanyoAc::convertFan(const stdAc::fanspeed_t speed) { - switch (speed) { - case stdAc::fanspeed_t::kMin: - case stdAc::fanspeed_t::kLow: return kSanyoAcFanLow; - case stdAc::fanspeed_t::kMedium: return kSanyoAcFanMedium; - case stdAc::fanspeed_t::kHigh: - case stdAc::fanspeed_t::kMax: return kSanyoAcFanHigh; - default: return kSanyoAcFanAuto; - } -} - -/// Convert a native fan speed into its stdAc equivalent. -/// @param[in] spd The native setting to be converted. -/// @return The stdAc equivalent of the native setting. -stdAc::fanspeed_t IRSanyoAc::toCommonFanSpeed(const uint8_t spd) { - switch (spd) { - case kSanyoAcFanHigh: return stdAc::fanspeed_t::kHigh; - case kSanyoAcFanMedium: return stdAc::fanspeed_t::kMedium; - case kSanyoAcFanLow: return stdAc::fanspeed_t::kLow; - default: return stdAc::fanspeed_t::kAuto; - } -} - -/// Get the vertical swing setting of the A/C. -/// @return The current swing mode setting. -uint8_t IRSanyoAc::getSwingV(void) const { - return _.SwingV; -} - -/// Set the vertical swing setting of the A/C. -/// @param[in] setting The value of the desired setting. -void IRSanyoAc::setSwingV(const uint8_t setting) { - if (setting == kSanyoAcSwingVAuto || - (setting >= kSanyoAcSwingVLowest && setting <= kSanyoAcSwingVHighest)) - _.SwingV = setting; - else - _.SwingV = kSanyoAcSwingVAuto; -} - -/// 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 IRSanyoAc::convertSwingV(const stdAc::swingv_t position) { - switch (position) { - case stdAc::swingv_t::kHighest: return kSanyoAcSwingVHighest; - case stdAc::swingv_t::kHigh: return kSanyoAcSwingVHigh; - case stdAc::swingv_t::kMiddle: return kSanyoAcSwingVUpperMiddle; - case stdAc::swingv_t::kLow: return kSanyoAcSwingVLow; - case stdAc::swingv_t::kLowest: return kSanyoAcSwingVLowest; - default: return kSanyoAcSwingVAuto; - } -} - -/// Convert a native vertical swing postion to it's common equivalent. -/// @param[in] setting A native position to convert. -/// @return The common vertical swing position. -stdAc::swingv_t IRSanyoAc::toCommonSwingV(const uint8_t setting) { - switch (setting) { - case kSanyoAcSwingVHighest: return stdAc::swingv_t::kHighest; - case kSanyoAcSwingVHigh: return stdAc::swingv_t::kHigh; - case kSanyoAcSwingVUpperMiddle: - case kSanyoAcSwingVLowerMiddle: return stdAc::swingv_t::kMiddle; - case kSanyoAcSwingVLow: return stdAc::swingv_t::kLow; - case kSanyoAcSwingVLowest: return stdAc::swingv_t::kLowest; - default: return stdAc::swingv_t::kAuto; - } -} - -/// Set the Sleep (Night Setback) setting of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRSanyoAc::setSleep(const bool on) { - _.Sleep = on; -} - -/// Get the Sleep (Night Setback) setting of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRSanyoAc::getSleep(void) const { - return _.Sleep; -} - -/// Set the Sensor Location setting of the A/C. -/// i.e. Where the ambient temperature is measured. -/// @param[in] location true is Unit/Wall, false is Remote/Room. -void IRSanyoAc::setSensor(const bool location) { - _.Sensor = location; -} - -/// Get the Sensor Location setting of the A/C. -/// i.e. Where the ambient temperature is measured. -/// @return true is Unit/Wall, false is Remote/Room. -bool IRSanyoAc::getSensor(void) const { - return _.Sensor; -} - -/// Set the Beep setting of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRSanyoAc::setBeep(const bool on) { - _.Beep = on; -} - -/// Get the Beep setting of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRSanyoAc::getBeep(void) const { - return _.Beep; -} - -/// Get the nr of minutes the Off Timer is set to. -/// @return The timer time expressed as the number of minutes. -/// A value of 0 means the Off Timer is off/disabled. -/// @note The internal precission has a resolution of 1 hour. -uint16_t IRSanyoAc::getOffTimer(void) const { - if (_.OffTimer) - return _.OffHour * 60; - else - return 0; -} - -/// Set the nr of minutes for the Off Timer. -/// @param[in] mins The timer time expressed as nr. of minutes. -/// A value of 0 means the Off Timer is off/disabled. -/// @note The internal precission has a resolution of 1 hour. -void IRSanyoAc::setOffTimer(const uint16_t mins) { - const uint8_t hours = std::min((uint8_t)(mins / 60), kSanyoAcHourMax); - _.OffTimer = (hours > 0); - _.OffHour = hours; -} - -/// Convert the current internal state into its stdAc::state_t equivalent. -/// @return The stdAc equivalent of the native settings. -stdAc::state_t IRSanyoAc::toCommon(void) const { - stdAc::state_t result; - result.protocol = decode_type_t::SANYO_AC; - result.model = -1; // Not supported. - result.power = getPower(); - result.mode = toCommonMode(_.Mode); - result.celsius = true; - result.degrees = getTemp(); - result.fanspeed = toCommonFanSpeed(_.Fan); - result.sleep = _.Sleep ? 0 : -1; - result.swingv = toCommonSwingV(_.SwingV); - result.beep = _.Beep; - // Not supported. - result.swingh = stdAc::swingh_t::kOff; - result.turbo = false; - result.econo = false; - result.light = false; - result.filter = false; - result.quiet = false; - result.clean = false; - result.clock = -1; - return result; -} - -/// Convert the current internal state into a human readable string. -/// @return A human readable string. -String IRSanyoAc::toString(void) const { - String result = ""; - result.reserve(140); - result += addBoolToString(getPower(), kPowerStr, false); - result += addModeToString(_.Mode, kSanyoAcAuto, kSanyoAcCool, - kSanyoAcHeat, kSanyoAcDry, kSanyoAcAuto); - result += addTempToString(getTemp()); - result += addFanToString(_.Fan, kSanyoAcFanHigh, kSanyoAcFanLow, - kSanyoAcFanAuto, kSanyoAcFanAuto, - kSanyoAcFanMedium); - result += addSwingVToString(_.SwingV, kSanyoAcSwingVAuto, - kSanyoAcSwingVHighest, kSanyoAcSwingVHigh, - kSanyoAcSwingVUpperMiddle, - kSanyoAcSwingVAuto, // Middle is unused - kSanyoAcSwingVLowerMiddle, - kSanyoAcSwingVLow, kSanyoAcSwingVLowest, - // Below are unused. - kSanyoAcSwingVAuto, - kSanyoAcSwingVAuto, - kSanyoAcSwingVAuto, - kSanyoAcSwingVAuto); - result += addBoolToString(_.Sleep, kSleepStr); - result += addBoolToString(_.Beep, kBeepStr); - result += addLabeledString(_.Sensor ? kRoomStr : kWallStr, kSensorStr); - result += kCommaSpaceStr; - result += kSensorStr; - result += ' '; - result += addTempToString(getSensorTemp(), true, false); - const uint16_t offtime = getOffTimer(); - result += addLabeledString(offtime ? minsToString(offtime) : kOffStr, - kOffTimerStr); - return result; -} - -#if SEND_SANYO_AC88 -/// Send a SanyoAc88 formatted message. -/// Status: ALPHA / Completely untested. -/// @param[in] data An array of bytes containing the IR command. -/// @warning data's bit order may change. It is not yet confirmed. -/// @param[in] nbytes Nr. of bytes of data in the array. -/// @param[in] repeat Nr. of times the message is to be repeated. -/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1503 -void IRsend::sendSanyoAc88(const uint8_t data[], const uint16_t nbytes, - const uint16_t repeat) { - // (Header + Data + Footer) per repeat - sendGeneric(kSanyoAc88HdrMark, kSanyoAc88HdrSpace, - kSanyoAc88BitMark, kSanyoAc88OneSpace, - kSanyoAc88BitMark, kSanyoAc88ZeroSpace, - kSanyoAc88BitMark, kSanyoAc88Gap, - data, nbytes, kSanyoAc88Freq, false, repeat, kDutyDefault); - space(kDefaultMessageGap); // Make a guess at a post message gap. -} -#endif // SEND_SANYO_AC88 - -#if DECODE_SANYO_AC88 -/// Decode the supplied SanyoAc message. -/// Status: ALPHA / Untested. -/// @param[in,out] results Ptr to the data to decode & where to store the decode -/// @warning data's bit order may change. It is not yet confirmed. -/// @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/1503 -bool IRrecv::decodeSanyoAc88(decode_results *results, uint16_t offset, - const uint16_t nbits, const bool strict) { - if (strict && nbits != kSanyoAc88Bits) - return false; - - uint16_t used = 0; - // Compliance - const uint16_t expected_repeats = strict ? kSanyoAc88MinRepeat : 0; - - // Handle the expected nr of repeats. - for (uint16_t r = 0; r <= expected_repeats; r++) { - // Header + Data + Footer - used = matchGeneric(results->rawbuf + offset, results->state, - results->rawlen - offset, nbits, - kSanyoAc88HdrMark, kSanyoAc88HdrSpace, - kSanyoAc88BitMark, kSanyoAc88OneSpace, - kSanyoAc88BitMark, kSanyoAc88ZeroSpace, - kSanyoAc88BitMark, - // Expect an inter-message gap, or just the end of msg? - (r < expected_repeats) ? kSanyoAc88Gap - : kDefaultMessageGap, - r == expected_repeats, - _tolerance + kSanyoAc88ExtraTolerance, - kMarkExcess, false); - if (!used) return false; // No match! - offset += used; - } - - // Success - results->decode_type = decode_type_t::SANYO_AC88; - 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_SANYO_AC88 - -/// 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? -IRSanyoAc88::IRSanyoAc88(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. -/// @see https://docs.google.com/spreadsheets/d/1dYfLsnYvpjV-SgO8pdinpfuBIpSzm8Q1R5SabrLeskw/edit?ts=5f0190a5#gid=1050142776&range=A2:B2 -void IRSanyoAc88::stateReset(void) { - static const uint8_t kReset[kSanyoAc88StateLength] = { - 0xAA, 0x55, 0xA0, 0x16, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x10}; - std::memcpy(_.raw, kReset, kSanyoAc88StateLength); -} - -/// Set up hardware to be able to send a message. -void IRSanyoAc88::begin(void) { _irsend.begin(); } - -#if SEND_SANYO_AC -/// Send the current internal state as IR messages. -/// @param[in] repeat Nr. of times the message will be repeated. -void IRSanyoAc88::send(const uint16_t repeat) { - _irsend.sendSanyoAc88(getRaw(), kSanyoAc88StateLength, repeat); -} -#endif // SEND_SANYO_AC - -/// Get a PTR to the internal state/code for this protocol with all integrity -/// checks passing. -/// @return PTR to a code for this protocol based on the current internal state. -uint8_t* IRSanyoAc88::getRaw(void) { - return _.raw; -} - -/// Set the internal state from a valid code for this protocol. -/// @param[in] newState A valid code for this protocol. -void IRSanyoAc88::setRaw(const uint8_t newState[]) { - std::memcpy(_.raw, newState, kSanyoAc88StateLength); -} - -/// Set the requested power state of the A/C to on. -void IRSanyoAc88::on(void) { setPower(true); } - -/// Set the requested power state of the A/C to off. -void IRSanyoAc88::off(void) { setPower(false); } - -/// Change the power setting. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRSanyoAc88::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 IRSanyoAc88::getPower(void) const { return _.Power; } - -/// Get the operating mode setting of the A/C. -/// @return The current operating mode setting. -uint8_t IRSanyoAc88::getMode(void) const { return _.Mode; } - -/// Set the operating mode of the A/C. -/// @param[in] mode The desired operating mode. -/// @note If we get an unexpected mode, default to AUTO. -void IRSanyoAc88::setMode(const uint8_t mode) { - switch (mode) { - case kSanyoAc88Auto: - case kSanyoAc88FeelCool: - case kSanyoAc88Cool: - case kSanyoAc88FeelHeat: - case kSanyoAc88Heat: - case kSanyoAc88Fan: - _.Mode = mode; - break; - default: _.Mode = kSanyoAc88Auto; - } -} - -/// 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 IRSanyoAc88::convertMode(const stdAc::opmode_t mode) { - switch (mode) { - case stdAc::opmode_t::kCool: return kSanyoAc88Cool; - case stdAc::opmode_t::kHeat: return kSanyoAc88Heat; - case stdAc::opmode_t::kFan: return kSanyoAc88Fan; - default: return kSanyoAc88Auto; - } -} - -/// 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 IRSanyoAc88::toCommonMode(const uint8_t mode) { - switch (mode) { - case kSanyoAc88FeelCool: - case kSanyoAc88Cool: - return stdAc::opmode_t::kCool; - case kSanyoAc88FeelHeat: - case kSanyoAc88Heat: - return stdAc::opmode_t::kHeat; - case kSanyoAc88Fan: - return stdAc::opmode_t::kFan; - default: - return stdAc::opmode_t::kAuto; - } -} - -/// Set the desired temperature. -/// @param[in] degrees The temperature in degrees celsius. -void IRSanyoAc88::setTemp(const uint8_t degrees) { - uint8_t temp = std::max((uint8_t)kSanyoAc88TempMin, degrees); - _.Temp = std::min((uint8_t)kSanyoAc88TempMax, temp); -} - -/// Get the current desired temperature setting. -/// @return The current setting for temp. in degrees celsius. -uint8_t IRSanyoAc88::getTemp(void) const { return _.Temp; } - -/// Set the speed of the fan. -/// @param[in] speed The desired setting. -void IRSanyoAc88::setFan(const uint8_t speed) { _.Fan = speed; } - -/// Get the current fan speed setting. -/// @return The current fan speed/mode. -uint8_t IRSanyoAc88::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 IRSanyoAc88::convertFan(const stdAc::fanspeed_t speed) { - switch (speed) { - case stdAc::fanspeed_t::kMin: - case stdAc::fanspeed_t::kLow: return kSanyoAc88FanLow; - case stdAc::fanspeed_t::kMedium: return kSanyoAc88FanMedium; - case stdAc::fanspeed_t::kHigh: - case stdAc::fanspeed_t::kMax: return kSanyoAc88FanHigh; - default: return kSanyoAc88FanAuto; - } -} - -/// Get the current clock time. -/// @return The time as the nr. of minutes past midnight. -uint16_t IRSanyoAc88::getClock(void) const { - return _.ClockHrs * 60 + _.ClockMins; -} - -/// Set the current clock time. -/// @param[in] mins_since_midnight The time as nr. of minutes past midnight. -void IRSanyoAc88::setClock(const uint16_t mins_since_midnight) { - uint16_t mins = std::min(mins_since_midnight, (uint16_t)(23 * 60 + 59)); - _.ClockMins = mins % 60; - _.ClockHrs = mins / 60; - _.ClockSecs = 0; -} - -/// Convert a native fan speed into its stdAc equivalent. -/// @param[in] spd The native setting to be converted. -/// @return The stdAc equivalent of the native setting. -stdAc::fanspeed_t IRSanyoAc88::toCommonFanSpeed(const uint8_t spd) { - switch (spd) { - case kSanyoAc88FanHigh: return stdAc::fanspeed_t::kHigh; - case kSanyoAc88FanMedium: return stdAc::fanspeed_t::kMedium; - case kSanyoAc88FanLow: return stdAc::fanspeed_t::kLow; - default: return stdAc::fanspeed_t::kAuto; - } -} - -/// Change the SwingV setting. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRSanyoAc88::setSwingV(const bool on) { _.SwingV = on; } - -/// Get the value of the current SwingV setting. -/// @return true, the setting is on. false, the setting is off. -bool IRSanyoAc88::getSwingV(void) const { return _.SwingV; } - -/// Change the Turbo setting. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRSanyoAc88::setTurbo(const bool on) { _.Turbo = on; } - -/// Get the value of the current Turbo setting. -/// @return true, the setting is on. false, the setting is off. -bool IRSanyoAc88::getTurbo(void) const { return _.Turbo; } - -/// Change the Filter setting. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRSanyoAc88::setFilter(const bool on) { _.Filter = on; } - -/// Get the value of the current Filter setting. -/// @return true, the setting is on. false, the setting is off. -bool IRSanyoAc88::getFilter(void) const { return _.Filter; } - -/// Change the Sleep setting. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRSanyoAc88::setSleep(const bool on) { _.Sleep = on; } - -/// Get the value of the current Sleep setting. -/// @return true, the setting is on. false, the setting is off. -bool IRSanyoAc88::getSleep(void) const { return _.Sleep; } - -/// Convert the current internal state into its stdAc::state_t equivalent. -/// @return The stdAc equivalent of the native settings. -stdAc::state_t IRSanyoAc88::toCommon(void) const { - stdAc::state_t result; - result.protocol = decode_type_t::SANYO_AC88; - result.model = -1; // Not supported. - result.power = getPower(); - 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.filter = _.Filter; - result.turbo = _.Turbo; - result.sleep = _.Sleep ? 0 : -1; - result.clock = getClock(); - // Not supported. - result.swingh = stdAc::swingh_t::kOff; - result.econo = false; - result.light = false; - result.quiet = false; - result.beep = false; - result.clean = false; - return result; -} - -/// Convert the current internal state into a human readable string. -/// @return A human readable string. -String IRSanyoAc88::toString(void) const { - String result = ""; - result.reserve(115); - result += addBoolToString(getPower(), kPowerStr, false); - result += addModeToString(_.Mode, kSanyoAc88Auto, kSanyoAc88Cool, - kSanyoAc88Heat, kSanyoAc88Auto, kSanyoAc88Fan); - result += addTempToString(getTemp()); - result += addFanToString(_.Fan, kSanyoAc88FanHigh, kSanyoAc88FanLow, - kSanyoAc88FanAuto, kSanyoAc88FanAuto, - kSanyoAc88FanMedium); - result += addBoolToString(_.SwingV, kSwingVStr); - result += addBoolToString(_.Turbo, kTurboStr); - result += addBoolToString(_.Sleep, kSleepStr); - result += addLabeledString(minsToString(getClock()), kClockStr); - return result; -} diff --git a/lib/IRremoteESP8266/src/ir_Sanyo.h b/lib/IRremoteESP8266/src/ir_Sanyo.h index 66376328f6..206a7314ff 100644 --- a/lib/IRremoteESP8266/src/ir_Sanyo.h +++ b/lib/IRremoteESP8266/src/ir_Sanyo.h @@ -30,10 +30,10 @@ #ifdef ARDUINO #include #endif -#include "IRremoteESP8266.h" -#include "IRsend.h" +#include "../src/IRremoteESP8266.h" +#include "../src/IRsend.h" #ifdef UNIT_TEST -#include "IRsend_test.h" +#include "../src/IRsend_test.h" #endif /// Native representation of a Sanyo A/C message. diff --git a/lib/IRremoteESP8266/src/ir_Sharp.cpp b/lib/IRremoteESP8266/src/ir_Sharp.cpp deleted file mode 100644 index 38f0ac32ce..0000000000 --- a/lib/IRremoteESP8266/src/ir_Sharp.cpp +++ /dev/null @@ -1,976 +0,0 @@ -// Copyright 2009 Ken Shirriff -// Copyright 2017, 2019 David Conran - -/// @file -/// @brief Support for Sharp protocols. -/// @see http://www.sbprojects.net/knowledge/ir/sharp.htm -/// @see http://lirc.sourceforge.net/remotes/sharp/GA538WJSA -/// @see http://www.mwftr.com/ucF08/LEC14%20PIC%20IR.pdf -/// @see http://www.hifi-remote.com/johnsfine/DecodeIR.html#Sharp -/// @see GlobalCache's IR Control Tower data. -/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/638 -/// @see https://github.com/ToniA/arduino-heatpumpir/blob/master/SharpHeatpumpIR.cpp - -#include "ir_Sharp.h" -#include -#include -#ifndef ARDUINO -#include -#endif -#include "IRrecv.h" -#include "IRsend.h" -#include "IRtext.h" -#include "IRutils.h" - -// Constants -// period time = 1/38000Hz = 26.316 microseconds. -const uint16_t kSharpTick = 26; -const uint16_t kSharpBitMarkTicks = 10; -const uint16_t kSharpBitMark = kSharpBitMarkTicks * kSharpTick; -const uint16_t kSharpOneSpaceTicks = 70; -const uint16_t kSharpOneSpace = kSharpOneSpaceTicks * kSharpTick; -const uint16_t kSharpZeroSpaceTicks = 30; -const uint16_t kSharpZeroSpace = kSharpZeroSpaceTicks * kSharpTick; -const uint16_t kSharpGapTicks = 1677; -const uint16_t kSharpGap = kSharpGapTicks * kSharpTick; -// Address(5) + Command(8) + Expansion(1) + Check(1) -const uint64_t kSharpToggleMask = - ((uint64_t)1 << (kSharpBits - kSharpAddressBits)) - 1; -const uint64_t kSharpAddressMask = ((uint64_t)1 << kSharpAddressBits) - 1; -const uint64_t kSharpCommandMask = ((uint64_t)1 << kSharpCommandBits) - 1; - -using irutils::addBoolToString; -using irutils::addFanToString; -using irutils::addIntToString; -using irutils::addLabeledString; -using irutils::addModeToString; -using irutils::addModelToString; -using irutils::addSwingVToString; -using irutils::addTempToString; -using irutils::addToggleToString; -using irutils::minsToString; - -// Also used by Denon protocol -#if (SEND_SHARP || SEND_DENON) -/// Send a (raw) Sharp message -/// @note Status: STABLE / Working fine. -/// @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 his procedure handles the inversion of bits required per protocol. -/// The protocol spec says to send the LSB first, but legacy code & usage -/// has us sending the MSB first. Grrrr. Normal invocation of encodeSharp() -/// handles this for you, assuming you are using the correct/standard values. -/// e.g. sendSharpRaw(encodeSharp(address, command)); -void IRsend::sendSharpRaw(const uint64_t data, const uint16_t nbits, - const uint16_t repeat) { - uint64_t tempdata = data; - for (uint16_t i = 0; i <= repeat; i++) { - // Protocol demands that the data be sent twice; once normally, - // then with all but the address bits inverted. - // Note: Previously this used to be performed 3 times (normal, inverted, - // normal), however all data points to that being incorrect. - for (uint8_t n = 0; n < 2; n++) { - sendGeneric(0, 0, // No Header - kSharpBitMark, kSharpOneSpace, kSharpBitMark, kSharpZeroSpace, - kSharpBitMark, kSharpGap, tempdata, nbits, 38, true, - 0, // Repeats are handled already. - 33); - // Invert the data per protocol. This is always called twice, so it's - // returned to original upon exiting the inner loop. - tempdata ^= kSharpToggleMask; - } - } -} - -/// Encode a (raw) Sharp message from it's components. -/// Status: STABLE / Works okay. -/// @param[in] address The value of the address to be sent. -/// @param[in] command The value of the address to be sent. (8 bits) -/// @param[in] expansion The value of the expansion bit to use. -/// (0 or 1, typically 1) -/// @param[in] check The value of the check bit to use. (0 or 1, typically 0) -/// @param[in] MSBfirst Flag indicating MSB first or LSB first order. -/// @return A uint32_t containing the raw Sharp message for `sendSharpRaw()`. -/// @note Assumes the standard Sharp bit sizes. -/// Historically sendSharp() sends address & command in -/// MSB first order. This is actually incorrect. It should be sent in LSB -/// order. The behaviour of sendSharp() hasn't been changed to maintain -/// backward compatibility. -uint32_t IRsend::encodeSharp(const uint16_t address, const uint16_t command, - const uint16_t expansion, const uint16_t check, - const bool MSBfirst) { - // Mask any unexpected bits. - uint16_t tempaddress = GETBITS16(address, 0, kSharpAddressBits); - uint16_t tempcommand = GETBITS16(command, 0, kSharpCommandBits); - uint16_t tempexpansion = GETBITS16(expansion, 0, 1); - uint16_t tempcheck = GETBITS16(check, 0, 1); - - if (!MSBfirst) { // Correct bit order if needed. - tempaddress = reverseBits(tempaddress, kSharpAddressBits); - tempcommand = reverseBits(tempcommand, kSharpCommandBits); - } - // Concatenate all the bits. - return (tempaddress << (kSharpCommandBits + 2)) | (tempcommand << 2) | - (tempexpansion << 1) | tempcheck; -} - -/// Send a Sharp message -/// Status: DEPRECATED / Previously working fine. -/// @deprecated Only use this if you are using legacy from the original -/// Arduino-IRremote library. 99% of the time, you will want to use -/// `sendSharpRaw()` instead -/// @param[in] address Address value to be sent. -/// @param[in] command Command value 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 procedure has a non-standard invocation style compared to similar -/// sendProtocol() routines. This is due to legacy, compatibility, & historic -/// reasons. Normally the calling syntax version is like sendSharpRaw(). -/// This procedure transmits the address & command in MSB first order, which is -/// incorrect. This behaviour is left as-is to maintain backward -/// compatibility with legacy code. -/// In short, you should use sendSharpRaw(), encodeSharp(), and the correct -/// values of address & command instead of using this, & the wrong values. -void IRsend::sendSharp(const uint16_t address, uint16_t const command, - const uint16_t nbits, const uint16_t repeat) { - sendSharpRaw(encodeSharp(address, command, 1, 0, true), nbits, repeat); -} -#endif // (SEND_SHARP || SEND_DENON) - -// Used by decodeDenon too. -#if (DECODE_SHARP || DECODE_DENON) -/// Decode the supplied Sharp message. -/// Status: STABLE / Working fine. -/// @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. -/// @param[in] expansion Should we expect the expansion bit to be set. -/// Default is true. -/// @return True if it can decode it, false if it can't. -/// @note This procedure returns a value suitable for use in `sendSharpRaw()`. -/// @todo Need to ensure capture of the inverted message as it can -/// be missed due to the interrupt timeout used to detect an end of message. -/// Several compliance checks are disabled until that is resolved. -bool IRrecv::decodeSharp(decode_results *results, uint16_t offset, - const uint16_t nbits, const bool strict, - const bool expansion) { - if (results->rawlen <= 2 * nbits + kFooter - 1 + offset) - return false; // Not enough entries to be a Sharp message. - // Compliance - if (strict) { - if (nbits != kSharpBits) return false; // Request is out of spec. - // DISABLED - See TODO -#ifdef UNIT_TEST - // An in spec message has the data sent normally, then inverted. So we - // expect twice as many entries than to just get the results. - if (results->rawlen <= (2 * (2 * nbits + kFooter)) - 1 + offset) - return false; -#endif - } - - uint64_t data = 0; - - // Match Data + Footer - uint16_t used; - used = matchGeneric(results->rawbuf + offset, &data, - results->rawlen - offset, nbits, - 0, 0, // No Header - kSharpBitMark, kSharpOneSpace, - kSharpBitMark, kSharpZeroSpace, - kSharpBitMark, kSharpGap, true, 35); - if (!used) return false; - offset += used; - // Compliance - if (strict) { - // Check the state of the expansion bit is what we expect. - if ((data & 0b10) >> 1 != expansion) return false; - // The check bit should be cleared in a normal message. - if (data & 0b1) return false; - // DISABLED - See TODO -#ifdef UNIT_TEST - // Grab the second copy of the data (i.e. inverted) - uint64_t second_data = 0; - // Match Data + Footer - if (!matchGeneric(results->rawbuf + offset, &second_data, - results->rawlen - offset, nbits, - 0, 0, - kSharpBitMark, kSharpOneSpace, - kSharpBitMark, kSharpZeroSpace, - kSharpBitMark, kSharpGap, true, 35)) return false; - // Check that second_data has been inverted correctly. - if (data != (second_data ^ kSharpToggleMask)) return false; -#endif // UNIT_TEST - } - - // Success - results->decode_type = SHARP; - results->bits = nbits; - results->value = data; - // Address & command are actually transmitted in LSB first order. - results->address = reverseBits(data, nbits) & kSharpAddressMask; - results->command = - reverseBits((data >> 2) & kSharpCommandMask, kSharpCommandBits); - return true; -} -#endif // (DECODE_SHARP || DECODE_DENON) - -#if SEND_SHARP_AC -/// Send a Sharp A/C message. -/// Status: Alpha / Untested. -/// @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. -/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/638 -/// @see https://github.com/ToniA/arduino-heatpumpir/blob/master/SharpHeatpumpIR.cpp -void IRsend::sendSharpAc(const unsigned char data[], const uint16_t nbytes, - const uint16_t repeat) { - if (nbytes < kSharpAcStateLength) - return; // Not enough bytes to send a proper message. - - sendGeneric(kSharpAcHdrMark, kSharpAcHdrSpace, - kSharpAcBitMark, kSharpAcOneSpace, - kSharpAcBitMark, kSharpAcZeroSpace, - kSharpAcBitMark, kSharpAcGap, - data, nbytes, 38000, false, repeat, 50); -} -#endif // SEND_SHARP_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? -IRSharpAc::IRSharpAc(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 IRSharpAc::begin(void) { _irsend.begin(); } - -#if SEND_SHARP_AC -/// Send the current internal state as an IR message. -/// @param[in] repeat Nr. of times the message will be repeated. -void IRSharpAc::send(const uint16_t repeat) { - _irsend.sendSharpAc(getRaw(), kSharpAcStateLength, repeat); -} -#endif // SEND_SHARP_AC - -/// 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 4-bit checksum value. -uint8_t IRSharpAc::calcChecksum(uint8_t state[], const uint16_t length) { - uint8_t xorsum = xorBytes(state, length - 1); - xorsum ^= GETBITS8(state[length - 1], kLowNibble, kNibbleSize); - xorsum ^= GETBITS8(xorsum, kHighNibble, kNibbleSize); - return GETBITS8(xorsum, kLowNibble, kNibbleSize); -} - -/// 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 IRSharpAc::validChecksum(uint8_t state[], const uint16_t length) { - return GETBITS8(state[length - 1], kHighNibble, kNibbleSize) == - IRSharpAc::calcChecksum(state, length); -} - -/// Calculate and set the checksum values for the internal state. -void IRSharpAc::checksum(void) { - _.Sum = calcChecksum(_.raw); -} - -/// Reset the state of the remote to a known good state/sequence. -void IRSharpAc::stateReset(void) { - static const uint8_t reset[kSharpAcStateLength] = { - 0xAA, 0x5A, 0xCF, 0x10, 0x00, 0x01, 0x00, 0x00, 0x08, 0x80, 0x00, 0xE0, - 0x01}; - std::memcpy(_.raw, reset, kSharpAcStateLength); - _temp = getTemp(); - _mode = _.Mode; - _fan = _.Fan; - _model = getModel(true); -} - -/// 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 *IRSharpAc::getRaw(void) { - checksum(); // Ensure correct settings before sending. - 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 IRSharpAc::setRaw(const uint8_t new_code[], const uint16_t length) { - std::memcpy(_.raw, new_code, std::min(length, kSharpAcStateLength)); - _model = getModel(true); -} - -/// Set the model of the A/C to emulate. -/// @param[in] model The enum of the appropriate model. -void IRSharpAc::setModel(const sharp_ac_remote_model_t model) { - switch (model) { - case sharp_ac_remote_model_t::A705: - case sharp_ac_remote_model_t::A903: - _model = model; - _.Model = true; - break; - default: - _model = sharp_ac_remote_model_t::A907; - _.Model = false; - } - _.Model2 = (_model != sharp_ac_remote_model_t::A907); - // Redo the operating mode as some models don't support all modes. - setMode(_.Mode); -} - -/// Get/Detect the model of the A/C. -/// @param[in] raw Try to determine the model from the raw code only. -/// @return The enum of the compatible model. -sharp_ac_remote_model_t IRSharpAc::getModel(const bool raw) const { - if (raw) { - if (_.Model2) { - if (_.Model) - return sharp_ac_remote_model_t::A705; - else - return sharp_ac_remote_model_t::A903; - } else { - return sharp_ac_remote_model_t::A907; - } - } - return _model; -} - -/// Set the value of the Power Special setting without any checks. -/// @param[in] value The value to set Power Special to. -inline void IRSharpAc::setPowerSpecial(const uint8_t value) { - _.PowerSpecial = value; -} - -/// Get the value of the Power Special setting. -/// @return The setting's value. -uint8_t IRSharpAc::getPowerSpecial(void) const { - return _.PowerSpecial; -} - -/// Clear the "special"/non-normal bits in the power section. -/// e.g. for normal/common command modes. -void IRSharpAc::clearPowerSpecial(void) { - setPowerSpecial(_.PowerSpecial & kSharpAcPowerOn); -} - -/// Is one of the special power states in use? -/// @return true, it is. false, it isn't. -bool IRSharpAc::isPowerSpecial(void) const { - switch (_.PowerSpecial) { - case kSharpAcPowerSetSpecialOff: - case kSharpAcPowerSetSpecialOn: - case kSharpAcPowerTimerSetting: return true; - default: return false; - } -} - -/// Set the requested power state of the A/C to on. -void IRSharpAc::on(void) { setPower(true); } - -/// Set the requested power state of the A/C to off. -void IRSharpAc::off(void) { setPower(false); } - -/// Change the power setting, including the previous power state. -/// @param[in] on true, the setting is on. false, the setting is off. -/// @param[in] prev_on true, the setting is on. false, the setting is off. -void IRSharpAc::setPower(const bool on, const bool prev_on) { - setPowerSpecial(on ? (prev_on ? kSharpAcPowerOn : kSharpAcPowerOnFromOff) - : kSharpAcPowerOff); - // Power operations are incompatible with clean mode. - if (_.Clean) setClean(false); - _.Special = kSharpAcSpecialPower; -} - -/// Get the value of the current power setting. -/// @return true, the setting is on. false, the setting is off. -bool IRSharpAc::getPower(void) const { - switch (_.PowerSpecial) { - case kSharpAcPowerUnknown: - case kSharpAcPowerOff: return false; - default: return true; // Everything else is "probably" on. - } -} - -/// Set the value of the Special (button/command?) setting. -/// @param[in] mode The value to set Special to. -void IRSharpAc::setSpecial(const uint8_t mode) { - switch (mode) { - case kSharpAcSpecialPower: - case kSharpAcSpecialTurbo: - case kSharpAcSpecialTempEcono: - case kSharpAcSpecialFan: - case kSharpAcSpecialSwing: - case kSharpAcSpecialTimer: - case kSharpAcSpecialTimerHalfHour: - _.Special = mode; - break; - default: - _.Special = kSharpAcSpecialPower; - } -} - -/// Get the value of the Special (button/command?) setting. -/// @return The setting's value. -uint8_t IRSharpAc::getSpecial(void) const { return _.Special; } - -/// Set the temperature. -/// @param[in] temp The temperature in degrees celsius. -/// @param[in] save Do we save this setting as a user set one? -void IRSharpAc::setTemp(const uint8_t temp, const bool save) { - switch (_.Mode) { - // Auto & Dry don't allow temp changes and have a special temp. - case kSharpAcAuto: - case kSharpAcDry: - _.raw[kSharpAcByteTemp] = 0; - return; - default: - switch (getModel()) { - case sharp_ac_remote_model_t::A705: - _.raw[kSharpAcByteTemp] = 0xD0; - break; - default: - _.raw[kSharpAcByteTemp] = 0xC0; - } - } - uint8_t degrees = std::max(temp, kSharpAcMinTemp); - degrees = std::min(degrees, kSharpAcMaxTemp); - if (save) _temp = degrees; - _.Temp = degrees - kSharpAcMinTemp; - _.Special = kSharpAcSpecialTempEcono; - clearPowerSpecial(); -} - -/// Get the current temperature setting. -/// @return The current setting for temp. in degrees celsius. -uint8_t IRSharpAc::getTemp(void) const { - return _.Temp + kSharpAcMinTemp; -} - -/// Get the operating mode setting of the A/C. -/// @return The current operating mode setting. -uint8_t IRSharpAc::getMode(void) const { - return _.Mode; -} - -/// Set the operating mode of the A/C. -/// @param[in] mode The desired operating mode. -/// @param[in] save Do we save this setting as a user set one? -void IRSharpAc::setMode(const uint8_t mode, const bool save) { - uint8_t realMode = mode; - if (mode == kSharpAcHeat) { - switch (getModel()) { - case sharp_ac_remote_model_t::A705: - case sharp_ac_remote_model_t::A903: - // These models have no heat mode, use Fan mode instead. - realMode = kSharpAcFan; - break; - default: - break; - } - } - - switch (realMode) { - case kSharpAcAuto: // Also kSharpAcFan - case kSharpAcDry: - // When Dry or Auto, Fan always 2(Auto) - setFan(kSharpAcFanAuto, false); - // FALLTHRU - case kSharpAcCool: - case kSharpAcHeat: - _.Mode = realMode; - break; - default: - setFan(kSharpAcFanAuto, false); - _.Mode = kSharpAcAuto; - } - // Dry/Auto have no temp setting. This step will enforce it. - setTemp(_temp, false); - // Save the mode in case we need to revert to it. eg. Clean - if (save) _mode = _.Mode; - - _.Special = kSharpAcSpecialPower; - clearPowerSpecial(); -} - -/// Set the speed of the fan. -/// @param[in] speed The desired setting. -/// @param[in] save Do we save this setting as a user set one? -void IRSharpAc::setFan(const uint8_t speed, const bool save) { - switch (speed) { - case kSharpAcFanAuto: - case kSharpAcFanMin: - case kSharpAcFanMed: - case kSharpAcFanHigh: - case kSharpAcFanMax: - _.Fan = speed; - if (save) _fan = speed; - break; - default: - _.Fan = kSharpAcFanAuto; - _fan = kSharpAcFanAuto; - } - _.Special = kSharpAcSpecialFan; - clearPowerSpecial(); -} - -/// Get the current fan speed setting. -/// @return The current fan speed/mode. -uint8_t IRSharpAc::getFan(void) const { - return _.Fan; -} - -/// Get the Turbo setting of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRSharpAc::getTurbo(void) const { - return (_.PowerSpecial == kSharpAcPowerSetSpecialOn) && - (_.Special == kSharpAcSpecialTurbo); -} - -/// Set the Turbo setting of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -/// @note If you use this method, you will need to send it before making -/// other changes to the settings, as they may overwrite some of the bits -/// used by this setting. -void IRSharpAc::setTurbo(const bool on) { - if (on) setFan(kSharpAcFanMax); - setPowerSpecial(on ? kSharpAcPowerSetSpecialOn : kSharpAcPowerSetSpecialOff); - _.Special = kSharpAcSpecialTurbo; -} - -/// Get the Vertical Swing setting of the A/C. -/// @return The position of the Vertical Swing setting. -uint8_t IRSharpAc::getSwingV(void) const { return _.Swing; } - -/// Set the Vertical Swing setting of the A/C. -/// @note Some positions may not work on all models. -/// @param[in] position The desired position/setting. -/// @note `setSwingV(kSharpAcSwingVLowest)` will only allow the Lowest setting -/// in Heat mode, it will default to `kSharpAcSwingVLow` otherwise. -/// If you want to set this value in other modes e.g. Cool, you must -/// use `setSwingV`s optional `force` parameter. -/// @param[in] force Do we override the safety checks and just do it? -void IRSharpAc::setSwingV(const uint8_t position, const bool force) { - switch (position) { - case kSharpAcSwingVCoanda: - // Only allowed in Heat mode. - if (!force && getMode() != kSharpAcHeat) { - setSwingV(kSharpAcSwingVLow); // Use the next lowest setting. - return; - } - // FALLTHRU - case kSharpAcSwingVHigh: - case kSharpAcSwingVMid: - case kSharpAcSwingVLow: - case kSharpAcSwingVToggle: - case kSharpAcSwingVOff: - case kSharpAcSwingVLast: // Technically valid, but we don't use it. - // All expected non-positions set the special bits. - _.Special = kSharpAcSpecialSwing; - // FALLTHRU - case kSharpAcSwingVIgnore: - _.Swing = position; - } -} - -/// Convert a standard A/C vertical swing into its native setting. -/// @param[in] position A stdAc::swingv_t position to convert. -/// @return The equivalent native horizontal swing position. -uint8_t IRSharpAc::convertSwingV(const stdAc::swingv_t position) { - switch (position) { - case stdAc::swingv_t::kHighest: - case stdAc::swingv_t::kHigh: return kSharpAcSwingVHigh; - case stdAc::swingv_t::kMiddle: return kSharpAcSwingVMid; - case stdAc::swingv_t::kLow: return kSharpAcSwingVLow; - case stdAc::swingv_t::kLowest: return kSharpAcSwingVCoanda; - case stdAc::swingv_t::kAuto: return kSharpAcSwingVToggle; - case stdAc::swingv_t::kOff: return kSharpAcSwingVOff; - default: return kSharpAcSwingVIgnore; - } -} - -/// Get the (vertical) Swing Toggle setting of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRSharpAc::getSwingToggle(void) const { - return getSwingV() == kSharpAcSwingVToggle; -} - -/// Set the (vertical) Swing Toggle setting of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRSharpAc::setSwingToggle(const bool on) { - setSwingV(on ? kSharpAcSwingVToggle : kSharpAcSwingVIgnore); - if (on) _.Special = kSharpAcSpecialSwing; -} - -/// Get the Ion (Filter) setting of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRSharpAc::getIon(void) const { return _.Ion; } - -/// Set the Ion (Filter) setting of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRSharpAc::setIon(const bool on) { - _.Ion = on; - clearPowerSpecial(); - if (on) _.Special = kSharpAcSpecialSwing; -} - -/// Get the Economical mode toggle setting of the A/C. -/// @return true, the setting is on. false, the setting is off. -/// @note Shares the same location as the Light setting on A705. -bool IRSharpAc::_getEconoToggle(void) const { - return (_.PowerSpecial == kSharpAcPowerSetSpecialOn) && - (_.Special == kSharpAcSpecialTempEcono); -} - -/// Set the Economical mode toggle setting of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -/// @warning Probably incompatible with `setTurbo()` -/// @note Shares the same location as the Light setting on A705. -void IRSharpAc::_setEconoToggle(const bool on) { - if (on) _.Special = kSharpAcSpecialTempEcono; - setPowerSpecial(on ? kSharpAcPowerSetSpecialOn : kSharpAcPowerSetSpecialOff); -} - -/// Set the Economical mode toggle setting of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -/// @warning Probably incompatible with `setTurbo()` -/// @note Available on the A907 models. -void IRSharpAc::setEconoToggle(const bool on) { - if (_model == sharp_ac_remote_model_t::A907) _setEconoToggle(on); -} - -/// Get the Economical mode toggle setting of the A/C. -/// @return true, the setting is on. false, the setting is off. -/// @note Available on the A907 models. -bool IRSharpAc::getEconoToggle(void) const { - return _model == sharp_ac_remote_model_t::A907 && _getEconoToggle(); -} - -/// Set the Light mode toggle setting of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -/// @warning Probably incompatible with `setTurbo()` -/// @note Not available on the A907 model. -void IRSharpAc::setLightToggle(const bool on) { - if (_model != sharp_ac_remote_model_t::A907) _setEconoToggle(on); -} - -/// Get the Light toggle setting of the A/C. -/// @return true, the setting is on. false, the setting is off. -/// @note Not available on the A907 model. -bool IRSharpAc::getLightToggle(void) const { - return _model != sharp_ac_remote_model_t::A907 && _getEconoToggle(); -} - -/// Get how long the timer is set for, in minutes. -/// @return The time in nr of minutes. -uint16_t IRSharpAc::getTimerTime(void) const { - return _.TimerHours * kSharpAcTimerIncrement * 2 + - ((_.Special == kSharpAcSpecialTimerHalfHour) ? kSharpAcTimerIncrement - : 0); -} - -/// Is the Timer enabled? -/// @return true, the setting is on. false, the setting is off. -bool IRSharpAc::getTimerEnabled(void) const { return _.TimerEnabled; } - -/// Get the current timer type. -/// @return true, It's an "On" timer. false, It's an "Off" timer. -bool IRSharpAc::getTimerType(void) const { return _.TimerType; } - -/// Set or cancel the timer function. -/// @param[in] enable Is the timer to be enabled (true) or canceled(false)? -/// @param[in] timer_type An On (true) or an Off (false). Ignored if canceled. -/// @param[in] mins Nr. of minutes the timer is to be set to. -/// @note Rounds down to 30 min increments. (max: 720 mins (12h), 0 is Off) -void IRSharpAc::setTimer(bool enable, bool timer_type, uint16_t mins) { - uint8_t half_hours = std::min(mins / kSharpAcTimerIncrement, - kSharpAcTimerHoursMax * 2); - if (half_hours == 0) enable = false; - if (!enable) { - half_hours = 0; - timer_type = kSharpAcOffTimerType; - } - _.TimerEnabled = enable; - _.TimerType = timer_type; - _.TimerHours = half_hours / 2; - // Handle non-round hours. - _.Special = (half_hours % 2) ? kSharpAcSpecialTimerHalfHour - : kSharpAcSpecialTimer; - setPowerSpecial(kSharpAcPowerTimerSetting); -} - -/// Get the Clean setting of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRSharpAc::getClean(void) const { - return _.Clean; -} - -/// Set the Economical mode toggle setting of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -/// @note Officially A/C unit needs to be "Off" before clean mode can be entered -void IRSharpAc::setClean(const bool on) { - // Clean mode appears to be just default dry mode, with an extra bit set. - if (on) { - setMode(kSharpAcDry, false); - setPower(true, false); - } else { - // Restore the previous operation mode & fan speed. - setMode(_mode, false); - setFan(_fan, false); - } - _.Clean = on; - clearPowerSpecial(); -} - -/// 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 IRSharpAc::convertMode(const stdAc::opmode_t mode) { - switch (mode) { - case stdAc::opmode_t::kCool: return kSharpAcCool; - case stdAc::opmode_t::kHeat: return kSharpAcHeat; - case stdAc::opmode_t::kDry: return kSharpAcDry; - // No Fan mode. - default: return kSharpAcAuto; - } -} - -/// Convert a stdAc::fanspeed_t enum into it's native speed. -/// @param[in] speed The enum to be converted. -/// @param[in] model The enum of the appropriate model. -/// @return The native equivalent of the enum. -uint8_t IRSharpAc::convertFan(const stdAc::fanspeed_t speed, - const sharp_ac_remote_model_t model) { - switch (model) { - case sharp_ac_remote_model_t::A705: - case sharp_ac_remote_model_t::A903: - switch (speed) { - case stdAc::fanspeed_t::kLow: return kSharpAcFanA705Low; - case stdAc::fanspeed_t::kMedium: return kSharpAcFanA705Med; - default: {}; // Fall thru to the next/default clause if not the above - // special cases. - } - // FALL THRU - default: - switch (speed) { - case stdAc::fanspeed_t::kMin: - case stdAc::fanspeed_t::kLow: return kSharpAcFanMin; - case stdAc::fanspeed_t::kMedium: return kSharpAcFanMed; - case stdAc::fanspeed_t::kHigh: return kSharpAcFanHigh; - case stdAc::fanspeed_t::kMax: return kSharpAcFanMax; - default: return kSharpAcFanAuto; - } - } -} - -/// 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 IRSharpAc::toCommonMode(const uint8_t mode) const { - switch (mode) { - case kSharpAcCool: return stdAc::opmode_t::kCool; - case kSharpAcHeat: return stdAc::opmode_t::kHeat; - case kSharpAcDry: return stdAc::opmode_t::kDry; - case kSharpAcAuto: // Also kSharpAcFan - switch (getModel()) { - case sharp_ac_remote_model_t::A705: return stdAc::opmode_t::kFan; - default: return stdAc::opmode_t::kAuto; - } - break; - 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 IRSharpAc::toCommonFanSpeed(const uint8_t speed) const { - switch (getModel()) { - case sharp_ac_remote_model_t::A705: - case sharp_ac_remote_model_t::A903: - switch (speed) { - case kSharpAcFanA705Low: return stdAc::fanspeed_t::kLow; - case kSharpAcFanA705Med: return stdAc::fanspeed_t::kMedium; - } - // FALL-THRU - default: - switch (speed) { - case kSharpAcFanMax: return stdAc::fanspeed_t::kMax; - case kSharpAcFanHigh: return stdAc::fanspeed_t::kHigh; - case kSharpAcFanMed: return stdAc::fanspeed_t::kMedium; - case kSharpAcFanMin: return stdAc::fanspeed_t::kMin; - default: return stdAc::fanspeed_t::kAuto; - } - } -} - -/// Convert a native vertical swing postion to it's common equivalent. -/// @param[in] pos A native position to convert. -/// @param[in] mode What operating mode are we in? -/// @return The common vertical swing position. -stdAc::swingv_t IRSharpAc::toCommonSwingV(const uint8_t pos, - const stdAc::opmode_t mode) const { - switch (pos) { - case kSharpAcSwingVHigh: return stdAc::swingv_t::kHighest; - case kSharpAcSwingVMid: return stdAc::swingv_t::kMiddle; - case kSharpAcSwingVLow: return stdAc::swingv_t::kLow; - case kSharpAcSwingVCoanda: // Coanda has mode dependent positionss - switch (mode) { - case stdAc::opmode_t::kCool: return stdAc::swingv_t::kHighest; - case stdAc::opmode_t::kHeat: return stdAc::swingv_t::kLowest; - default: return stdAc::swingv_t::kOff; - } - case kSharpAcSwingVToggle: return stdAc::swingv_t::kAuto; - default: return stdAc::swingv_t::kOff; - } -} - -/// 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 IRSharpAc::toCommon(const stdAc::state_t *prev) const { - stdAc::state_t result; - // Start with the previous state if given it. - if (prev != NULL) result = *prev; - result.protocol = decode_type_t::SHARP_AC; - result.model = getModel(); - result.power = getPower(); - result.mode = toCommonMode(_.Mode); - result.celsius = true; - result.degrees = getTemp(); - result.fanspeed = toCommonFanSpeed(_.Fan); - result.turbo = getTurbo(); - if (getSwingV() != kSharpAcSwingVIgnore) - result.swingv = toCommonSwingV(getSwingV(), result.mode); - result.filter = _.Ion; - result.econo = getEconoToggle(); - result.light = getLightToggle(); - result.clean = _.Clean; - // Not supported. - result.swingh = stdAc::swingh_t::kOff; - result.quiet = 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 IRSharpAc::toString(void) const { - String result = ""; - const sharp_ac_remote_model_t model = getModel(); - result.reserve(170); // Reserve some heap for the string to reduce fragging. - result += addModelToString(decode_type_t::SHARP_AC, getModel(), false); - - result += addLabeledString(isPowerSpecial() ? String("-") - : String(getPower() ? kOnStr - : kOffStr), - kPowerStr); - const uint8_t mode = _.Mode; - result += addModeToString( - mode, - // Make the value invalid if the model doesn't support an Auto mode. - (model == sharp_ac_remote_model_t::A907) ? kSharpAcAuto : 255, - kSharpAcCool, kSharpAcHeat, kSharpAcDry, kSharpAcFan); - result += addTempToString(getTemp()); - switch (model) { - case sharp_ac_remote_model_t::A705: - case sharp_ac_remote_model_t::A903: - result += addFanToString(_.Fan, kSharpAcFanMax, kSharpAcFanA705Low, - kSharpAcFanAuto, kSharpAcFanAuto, - kSharpAcFanA705Med); - break; - default: - result += addFanToString(_.Fan, kSharpAcFanMax, kSharpAcFanMin, - kSharpAcFanAuto, kSharpAcFanAuto, - kSharpAcFanMed); - } - if (getSwingV() == kSharpAcSwingVIgnore) { - result += addIntToString(kSharpAcSwingVIgnore, kSwingVStr); - result += kSpaceLBraceStr; - result += kNAStr; - result += ')'; - } else { - result += addSwingVToString( - getSwingV(), 0xFF, - // Coanda means Highest when in Cool mode. - (mode == kSharpAcCool) ? kSharpAcSwingVCoanda : kSharpAcSwingVToggle, - kSharpAcSwingVHigh, - 0xFF, // Upper Middle is unused - kSharpAcSwingVMid, - 0xFF, // Lower Middle is unused - kSharpAcSwingVLow, - kSharpAcSwingVCoanda, - kSharpAcSwingVOff, - // Below are unused. - kSharpAcSwingVToggle, - 0xFF, - 0xFF); - } - result += addBoolToString(getTurbo(), kTurboStr); - result += addBoolToString(_.Ion, kIonStr); - switch (model) { - case sharp_ac_remote_model_t::A705: - case sharp_ac_remote_model_t::A903: - result += addToggleToString(getLightToggle(), kLightStr); - break; - default: - result += addToggleToString(getEconoToggle(), kEconoStr); - } - result += addBoolToString(_.Clean, kCleanStr); - if (_.TimerEnabled) - result += addLabeledString(minsToString(getTimerTime()), - _.TimerType ? kOnTimerStr : kOffTimerStr); - return result; -} - -#if DECODE_SHARP_AC -/// Decode the supplied Sharp A/C 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/638 -/// @see https://github.com/ToniA/arduino-heatpumpir/blob/master/SharpHeatpumpIR.cpp -bool IRrecv::decodeSharpAc(decode_results *results, uint16_t offset, - const uint16_t nbits, const bool strict) { - // Compliance - if (strict && nbits != kSharpAcBits) return false; - - // Match Header + Data + Footer - uint16_t used; - used = matchGeneric(results->rawbuf + offset, results->state, - results->rawlen - offset, nbits, - kSharpAcHdrMark, kSharpAcHdrSpace, - kSharpAcBitMark, kSharpAcOneSpace, - kSharpAcBitMark, kSharpAcZeroSpace, - kSharpAcBitMark, kSharpAcGap, true, - _tolerance, kMarkExcess, false); - if (used == 0) return false; - offset += used; - // Compliance - if (strict) { - if (!IRSharpAc::validChecksum(results->state)) return false; - } - - // Success - results->decode_type = SHARP_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_SHARP_AC diff --git a/lib/IRremoteESP8266/src/ir_Sharp.h b/lib/IRremoteESP8266/src/ir_Sharp.h index b3be534e7b..507f63e437 100644 --- a/lib/IRremoteESP8266/src/ir_Sharp.h +++ b/lib/IRremoteESP8266/src/ir_Sharp.h @@ -32,13 +32,13 @@ #ifndef UNIT_TEST #include #endif -#include "IRrecv.h" -#include "IRremoteESP8266.h" -#include "IRsend.h" +#include "../src/IRrecv.h" +#include "../src/IRremoteESP8266.h" +#include "../src/IRsend.h" #ifdef UNIT_TEST -#include "IRsend_test.h" +#include "../src/IRsend_test.h" #endif -#include "IRutils.h" +#include "../src/IRutils.h" /// Native representation of a Sharp A/C message. union SharpProtocol{ diff --git a/lib/IRremoteESP8266/src/ir_Sherwood.cpp b/lib/IRremoteESP8266/src/ir_Sherwood.cpp deleted file mode 100644 index 76ffc35ff0..0000000000 --- a/lib/IRremoteESP8266/src/ir_Sherwood.cpp +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright 2017 David Conran - -/// @file -/// @brief Support for Sherwood protocols. - -// Supports: -// Brand: Sherwood, Model: RC-138 remote -// Brand: Sherwood, Model: RD6505(B) Receiver - -#include -#include "IRsend.h" - -#if SEND_SHERWOOD -/// Send an IR command to a Sherwood device. -/// 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 Sherwood remote codes appear to be NEC codes with a mandatory repeat -/// code. i.e. repeat should be >= kSherwoodMinRepeat (1). -void IRsend::sendSherwood(uint64_t data, uint16_t nbits, uint16_t repeat) { - sendNEC(data, nbits, std::max((uint16_t)kSherwoodMinRepeat, repeat)); -} -#endif // SEND_SHERWOOD diff --git a/lib/IRremoteESP8266/src/ir_Sony.cpp b/lib/IRremoteESP8266/src/ir_Sony.cpp deleted file mode 100644 index 0dbbec3c81..0000000000 --- a/lib/IRremoteESP8266/src/ir_Sony.cpp +++ /dev/null @@ -1,191 +0,0 @@ -// Copyright 2009 Ken Shirriff -// Copyright 2016 marcosamarinho -// Copyright 2017,2020 David Conran - -/// @file -/// @brief Support for Sony SIRC(Serial Infra-Red Control) protocols. -/// Sony originally added from https://github.com/shirriff/Arduino-IRremote/ -/// Updates from marcosamarinho -/// @see http://www.sbprojects.net/knowledge/ir/sirc.php -/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1018 - -// Supports: -// Brand: Sony, Model: HT-CT380 Soundbar (Uses 38kHz & 3 repeats) - -#include -#include "IRrecv.h" -#include "IRsend.h" -#include "IRutils.h" - - -// Constants -const uint16_t kSonyTick = 200; -const uint16_t kSonyHdrMarkTicks = 12; -const uint16_t kSonyHdrMark = kSonyHdrMarkTicks * kSonyTick; -const uint16_t kSonySpaceTicks = 3; -const uint16_t kSonySpace = kSonySpaceTicks * kSonyTick; -const uint16_t kSonyOneMarkTicks = 6; -const uint16_t kSonyOneMark = kSonyOneMarkTicks * kSonyTick; -const uint16_t kSonyZeroMarkTicks = 3; -const uint16_t kSonyZeroMark = kSonyZeroMarkTicks * kSonyTick; -const uint16_t kSonyRptLengthTicks = 225; -const uint16_t kSonyRptLength = kSonyRptLengthTicks * kSonyTick; -const uint16_t kSonyMinGapTicks = 50; -const uint16_t kSonyMinGap = kSonyMinGapTicks * kSonyTick; -const uint16_t kSonyStdFreq = 40000; // kHz -const uint16_t kSonyAltFreq = 38000; // kHz - -#if SEND_SONY -/// Send a standard Sony/SIRC(Serial Infra-Red Control) message. (40kHz) -/// 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 sendSony() should typically be called with repeat=2 as Sony devices -/// expect the message to be sent at least 3 times. -void IRsend::sendSony(const uint64_t data, const uint16_t nbits, - const uint16_t repeat) { - _sendSony(data, nbits, repeat, kSonyStdFreq); -} - -/// Send an alternative 38kHz Sony/SIRC(Serial Infra-Red Control) 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 `sendSony38()` should typically be called with repeat=3 as these Sony -/// devices expect the message to be sent at least 4 times. -/// @warning Messages send via this method will be detected by this library as -/// just `SONY`, not `SONY_38K` as the library has no way to determine the -/// modulation frequency used. Hence, there is no `decodeSony38()`. -/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1018 -void IRsend::sendSony38(const uint64_t data, const uint16_t nbits, - const uint16_t repeat) { - _sendSony(data, nbits, repeat, kSonyAltFreq); -} - -/// Internal procedure to generate a Sony/SIRC(Serial Infra-Red Control) 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. -/// @param[in] freq Frequency of the modulation to transmit at. (Hz or kHz) -void IRsend::_sendSony(const uint64_t data, const uint16_t nbits, - const uint16_t repeat, const uint16_t freq) { - sendGeneric(kSonyHdrMark, kSonySpace, kSonyOneMark, kSonySpace, kSonyZeroMark, - kSonySpace, - 0, // No Footer mark. - kSonyMinGap, kSonyRptLength, data, nbits, freq, true, repeat, 33); -} - -/// Convert Sony/SIRC command, address, & extended bits into sendSony format. -/// Status: STABLE / Should be working. -/// @param[in] nbits Sony protocol bit size. -/// @param[in] command Sony command bits. -/// @param[in] address Sony address bits. -/// @param[in] extended Sony extended bits. -/// @return A `sendSony()` etc compatible data message. -uint32_t IRsend::encodeSony(const uint16_t nbits, const uint16_t command, - const uint16_t address, const uint16_t extended) { - uint32_t result = 0; - switch (nbits) { - case 12: // 5 address bits. - result = address & 0x1F; - break; - case 15: // 8 address bits. - result = address & 0xFF; - break; - case 20: // 5 address bits, 8 extended bits. - result = address & 0x1F; - result |= (extended & 0xFF) << 5; - break; - default: - return 0; // This is not an expected Sony bit size/protocol. - } - result = (result << 7) | (command & 0x7F); // All sizes have 7 command bits. - return reverseBits(result, nbits); // sendSony uses reverse ordered bits. -} -#endif // SEND_SONY - -#if DECODE_SONY -/// Decode the supplied Sony/SIRC message. -/// Status: STABLE / Should be working. strict mode is ALPHA / Untested. -/// @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 SONY protocol, SIRC (Serial Infra-Red Control) can be 12, 15, or 20 -/// bits long. -bool IRrecv::decodeSony(decode_results *results, uint16_t offset, - const uint16_t nbits, const bool strict) { - if (results->rawlen <= 2 * nbits + kHeader - 1 + offset) - return false; // Message is smaller than we expected. - - // Compliance - if (strict) { - switch (nbits) { // Check we've been called with a correct bit size. - case 12: - case 15: - case 20: - break; - default: - return false; // The request doesn't strictly match the protocol defn. - } - } - - uint64_t data = 0; - uint16_t actualBits; - - // Header - if (!matchMark(results->rawbuf[offset], kSonyHdrMark)) return false; - // Calculate how long the common tick time is based on the header mark. - uint32_t tick = results->rawbuf[offset++] * kRawTick / kSonyHdrMarkTicks; - - // Data - for (actualBits = 0; offset < results->rawlen - 1; actualBits++, offset++) { - // The gap after a Sony packet for a repeat should be kSonyMinGap according - // to the spec. - if (matchAtLeast(results->rawbuf[offset], kSonyMinGapTicks * tick)) - break; // Found a repeat space. - if (!matchSpace(results->rawbuf[offset++], kSonySpaceTicks * tick)) - return false; - if (matchMark(results->rawbuf[offset], kSonyOneMarkTicks * tick)) - data = (data << 1) | 1; - else if (matchMark(results->rawbuf[offset], kSonyZeroMarkTicks * tick)) - data <<= 1; - else - return false; - } - // No Footer for Sony. - - // Compliance - if (strict && actualBits != nbits) - return false; // We got the wrong number of bits. - - // Success - results->bits = actualBits; - results->value = data; - // We can't detect SONY_38K messages so always assume it is just `SONY` 40kHz. - results->decode_type = SONY; - // Message comes in LSB first. Convert ot MSB first. - data = reverseBits(data, actualBits); - // Decode the address & command from raw decode value. - switch (actualBits) { - case 12: // 7 command bits, 5 address bits. - case 15: // 7 command bits, 8 address bits. - results->command = data & 0x7F; // Bits 0-6 - results->address = data >> 7; // Bits 7-14 - break; - case 20: // 7 command bits, 5 address bits, 8 extended (command) bits. - results->command = (data & 0x7F) + ((data >> 12) << 7); // Bits 0-6,12-19 - results->address = (data >> 7) & 0x1F; // Bits 7-11 - break; - default: // Shouldn't happen, but just in case. - results->address = 0; - results->command = 0; - } - return true; -} -#endif // DECODE_SONY diff --git a/lib/IRremoteESP8266/src/ir_Symphony.cpp b/lib/IRremoteESP8266/src/ir_Symphony.cpp deleted file mode 100644 index b629ef72d3..0000000000 --- a/lib/IRremoteESP8266/src/ir_Symphony.cpp +++ /dev/null @@ -1,95 +0,0 @@ -// Copyright 2020 David Conran - -/// @file -/// @brief Support for Symphony protocols. -/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1057 -/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1105 -/// @see https://www.alldatasheet.com/datasheet-pdf/pdf/124369/ANALOGICTECH/SM5021B.html - -// Supports: -// Brand: Symphony, Model: Air Cooler 3Di -// Brand: SamHop, Model: SM3015 Fan Remote Control -// Brand: SamHop, Model: SM5021 Encoder chip -// Brand: SamHop, Model: SM5032 Decoder chip -// Brand: Blyss, Model: Owen-SW-5 3 Fan -// Brand: Blyss, Model: WP-YK8 090218 remote -// Brand: Westinghouse, Model: Ceiling fan -// Brand: Westinghouse, Model: 78095 Remote -// Brand: Satellite Electronic, Model: ID6 Remote -// Brand: Satellite Electronic, Model: JY199I Fan driver -// Brand: Satellite Electronic, Model: JY199I-L Fan driver -// Brand: SilverCrest, Model: SSVS 85 A1 Fan - -// Known Codes: -// SilverCrest SSVS 85 A1 Fan: -// 0x581 - On/Off -// 0x582 - Speed -// 0x584 - Mist -// 0x588 - Timer -// 0x590 - OSC - -#include -#include "IRrecv.h" -#include "IRsend.h" -#include "IRtimer.h" -#include "IRutils.h" - -// Constants -const uint16_t kSymphonyZeroMark = 400; -const uint16_t kSymphonyZeroSpace = 1250; -const uint16_t kSymphonyOneMark = kSymphonyZeroSpace; -const uint16_t kSymphonyOneSpace = kSymphonyZeroMark; -const uint32_t kSymphonyFooterGap = 4 * (kSymphonyZeroMark + - kSymphonyZeroSpace); - -#if SEND_SYMPHONY -/// Send a Symphony packet. -/// 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. -void IRsend::sendSymphony(uint64_t data, uint16_t nbits, uint16_t repeat) { - sendGeneric(0, 0, - kSymphonyOneMark, kSymphonyOneSpace, - kSymphonyZeroMark, kSymphonyZeroSpace, - 0, kSymphonyFooterGap, - data, nbits, 38000, true, repeat, kDutyDefault); -} -#endif // SEND_SYMPHONY - -#if DECODE_SYMPHONY -/// Decode the supplied Symphony packet/message. -/// Status: STABLE / Should 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::decodeSymphony(decode_results *results, uint16_t offset, - const uint16_t nbits, const bool strict) { - uint64_t data = 0; - - if (results->rawlen < 2 * nbits + offset - 1) - return false; // Not enough entries to ever be SYMPHONY. - // Compliance - if (strict && nbits != kSymphonyBits) return false; - - if (!matchGenericConstBitTime(results->rawbuf + offset, &data, - results->rawlen - offset, - nbits, - 0, 0, // No Header - kSymphonyOneMark, kSymphonyZeroMark, - 0, kSymphonyFooterGap, true, - _tolerance, 0)) - return false; - - // Success - results->value = data; - results->decode_type = decode_type_t::SYMPHONY; - results->bits = nbits; - results->address = 0; - results->command = 0; - return true; -} -#endif // DECODE_SYMPHONY diff --git a/lib/IRremoteESP8266/src/ir_Tcl.cpp b/lib/IRremoteESP8266/src/ir_Tcl.cpp deleted file mode 100644 index a9e8784a39..0000000000 --- a/lib/IRremoteESP8266/src/ir_Tcl.cpp +++ /dev/null @@ -1,529 +0,0 @@ -// Copyright 2019, 2021 David Conran - -/// @file -/// @brief Support for TCL protocols. - -#include "ir_Tcl.h" -#include -#include -#ifndef ARDUINO -#include -#endif -#include "IRremoteESP8266.h" -#include "IRtext.h" -#include "IRutils.h" - -// Constants - -const uint8_t kTcl112AcTimerResolution = 20; // Minutes -const uint16_t kTcl112AcTimerMax = 720; // Minutes (12 hrs) - -using irutils::addBoolToString; -using irutils::addFanToString; -using irutils::addIntToString; -using irutils::addLabeledString; -using irutils::addModeToString; -using irutils::addModelToString; -using irutils::addSwingVToString; -using irutils::addTempFloatToString; -using irutils::minsToString; - -#if SEND_TCL112AC -/// Send a TCL 112-bit A/C message. -/// 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::sendTcl112Ac(const unsigned char data[], const uint16_t nbytes, - const uint16_t repeat) { - sendGeneric(kTcl112AcHdrMark, kTcl112AcHdrSpace, - kTcl112AcBitMark, kTcl112AcOneSpace, - kTcl112AcBitMark, kTcl112AcZeroSpace, - kTcl112AcBitMark, kTcl112AcGap, - data, nbytes, 38000, false, repeat, 50); -} -#endif // SEND_TCL112AC - -/// 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? -IRTcl112Ac::IRTcl112Ac(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 IRTcl112Ac::begin(void) { _irsend.begin(); } - -#if SEND_TCL112AC -/// Send the current internal state as an IR message. -/// @param[in] repeat Nr. of times the message will be repeated. -void IRTcl112Ac::send(const uint16_t repeat) { - uint8_t save[kTcl112AcStateLength]; - // Do we need to send the special "quiet" message? - if (_quiet != _quiet_prev) { - // Backup the current state. - std::memcpy(save, _.raw, kTcl112AcStateLength); - const uint8_t quiet_off[kTcl112AcStateLength] = { - 0x23, 0xCB, 0x26, 0x02, 0x00, 0x40, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x65}; - // Use a known good quiet/mute off/type 2 state for the time being. - // Ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/1528#issuecomment-876989044 - setRaw(quiet_off); - setQuiet(_quiet); - // Send it. - _irsend.sendTcl112Ac(getRaw(), kTcl112AcStateLength, repeat); - // Now it's been sent, update the quiet previous state. - _quiet_prev = _quiet; - // Restore the old state. - setRaw(save); - // Make sure it looks like a normal TCL mesg if needed. - if (_.MsgType == kTcl112AcNormal) _.isTcl = true; - } - // Send the normal (type 1) state. - _irsend.sendTcl112Ac(getRaw(), kTcl112AcStateLength, repeat); -} -#endif // SEND_TCL112AC - -/// 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 IRTcl112Ac::calcChecksum(uint8_t state[], const uint16_t length) { - if (length) { - if (length > 4 && state[3] == 0x02) { // Special nessage? - return sumBytes(state, length - 1, 0xF); // Checksum needs an offset. - } else { - return sumBytes(state, length - 1); - } - } else { - return 0; - } -} - -/// Calculate & set the checksum for the current internal state of the remote. -/// @param[in] length The length/size of the internal array to checksum. -void IRTcl112Ac::checksum(const uint16_t length) { - // Stored the checksum value in the last byte. - if (length > 1) - _.Sum = calcChecksum(_.raw, 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/size of the array. -/// @return true, if the state has a valid checksum. Otherwise, false. -bool IRTcl112Ac::validChecksum(uint8_t state[], const uint16_t length) { - return (length > 1 && state[length - 1] == calcChecksum(state, length)); -} - -/// Check the supplied state looks like a TCL112AC message. -/// @param[in] state The array to verify the checksum of. -/// @note Assumes the state is the correct size. -/// @return true, if the state looks like a TCL112AC message. Otherwise, false. -/// @warning This is just a guess. -bool IRTcl112Ac::isTcl(const uint8_t state[]) { - Tcl112Protocol mesg; - std::memcpy(mesg.raw, state, kTcl112AcStateLength); - return (mesg.MsgType != kTcl112AcNormal) || mesg.isTcl; -} - -/// Reset the internal state of the emulation. (On, Cool, 24C) -void IRTcl112Ac::stateReset(void) { - // A known good state. (On, Cool, 24C) - static const uint8_t reset[kTcl112AcStateLength] = { - 0x23, 0xCB, 0x26, 0x01, 0x00, 0x24, 0x03, 0x07, 0x40, 0x00, 0x00, 0x00, - 0x00, 0x03}; - std::memcpy(_.raw, reset, kTcl112AcStateLength); - _quiet = false; - _quiet_prev = false; - _quiet_explictly_set = false; -} - -/// Get/Detect the model of the A/C. -/// @return The enum of the compatible model. -tcl_ac_remote_model_t IRTcl112Ac::getModel(void) const { - return isTcl(_.raw) ? tcl_ac_remote_model_t::TAC09CHSD - : tcl_ac_remote_model_t::GZ055BE1; -} - -/// Set the model of the A/C to emulate. -/// @param[in] model The enum of the appropriate model. -void IRTcl112Ac::setModel(const tcl_ac_remote_model_t model) { - _.isTcl = (model != tcl_ac_remote_model_t::GZ055BE1); -} - -/// 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* IRTcl112Ac::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 IRTcl112Ac::setRaw(const uint8_t new_code[], const uint16_t length) { - std::memcpy(_.raw, new_code, std::min(length, kTcl112AcStateLength)); -} - -/// Set the requested power state of the A/C to on. -void IRTcl112Ac::on(void) { setPower(true); } - -/// Set the requested power state of the A/C to off. -void IRTcl112Ac::off(void) { setPower(false); } - -/// Change the power setting. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRTcl112Ac::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 IRTcl112Ac::getPower(void) const { return _.Power; } - -/// Get the operating mode setting of the A/C. -/// @return The current operating mode setting. -uint8_t IRTcl112Ac::getMode(void) const { return _.Mode; } - -/// Set the operating mode of the A/C. -/// @param[in] mode The desired operating mode. -/// @note Fan/Ventilation mode sets the fan speed to high. -/// Unknown values default to Auto. -void IRTcl112Ac::setMode(const uint8_t mode) { - // If we get an unexpected mode, default to AUTO. - switch (mode) { - case kTcl112AcFan: - setFan(kTcl112AcFanHigh); - // FALLTHRU - case kTcl112AcAuto: - case kTcl112AcCool: - case kTcl112AcHeat: - case kTcl112AcDry: - _.Mode = mode; - break; - default: - _.Mode = kTcl112AcAuto; - } -} - -/// Set the temperature. -/// @param[in] celsius The temperature in degrees celsius. -/// @note The temperature resolution is 0.5 of a degree. -void IRTcl112Ac::setTemp(const float celsius) { - // Make sure we have desired temp in the correct range. - float safecelsius = std::max(celsius, kTcl112AcTempMin); - safecelsius = std::min(safecelsius, kTcl112AcTempMax); - // Convert to integer nr. of half degrees. - uint8_t nrHalfDegrees = safecelsius * 2; - // Do we have a half degree celsius? - _.HalfDegree = nrHalfDegrees & 1; - _.Temp = static_cast(kTcl112AcTempMax - nrHalfDegrees / 2); -} - -/// Get the current temperature setting. -/// @return The current setting for temp. in degrees celsius. -/// @note The temperature resolution is 0.5 of a degree. -float IRTcl112Ac::getTemp(void) const { - float result = kTcl112AcTempMax - _.Temp; - if (_.HalfDegree) result += 0.5; - return result; -} - -/// Set the speed of the fan. -/// @param[in] speed The desired setting. -/// @note Unknown speeds will default to Auto. -void IRTcl112Ac::setFan(const uint8_t speed) { - switch (speed) { - case kTcl112AcFanAuto: - case kTcl112AcFanMin: - case kTcl112AcFanLow: - case kTcl112AcFanMed: - case kTcl112AcFanHigh: - _.Fan = speed; - break; - default: - _.Fan = kTcl112AcFanAuto; - } -} - -/// Get the current fan speed setting. -/// @return The current fan speed/mode. -uint8_t IRTcl112Ac::getFan(void) const { return _.Fan; } - -/// Set the economy setting of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRTcl112Ac::setEcono(const bool on) { _.Econo = on; } - -/// Get the economy setting of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRTcl112Ac::getEcono(void) const { return _.Econo; } - -/// Set the Health (Filter) setting of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRTcl112Ac::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 IRTcl112Ac::getHealth(void) const { return _.Health; } - -/// Set the Light (LED/Display) setting of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRTcl112Ac::setLight(const bool on) { _.Light = !on; } // Cleared when on. - -/// Get the Light (LED/Display) setting of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRTcl112Ac::getLight(void) const { return !_.Light; } - -/// Set the horizontal swing setting of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRTcl112Ac::setSwingHorizontal(const bool on) { _.SwingH = on; } - -/// Get the horizontal swing setting of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRTcl112Ac::getSwingHorizontal(void) const { return _.SwingH; } - -/// Set the vertical swing setting of the A/C. -/// @param[in] setting The value of the desired setting. -void IRTcl112Ac::setSwingVertical(const uint8_t setting) { - switch (setting) { - case kTcl112AcSwingVOff: - case kTcl112AcSwingVHighest: - case kTcl112AcSwingVHigh: - case kTcl112AcSwingVMiddle: - case kTcl112AcSwingVLow: - case kTcl112AcSwingVLowest: - case kTcl112AcSwingVOn: - _.SwingV = setting; - } -} - -/// Get the vertical swing setting of the A/C. -/// @return The current setting. -uint8_t IRTcl112Ac::getSwingVertical(void) const { return _.SwingV; } - -/// Set the Turbo setting of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRTcl112Ac::setTurbo(const bool on) { - _.Turbo = on; - if (on) { - _.Fan = kTcl112AcFanHigh; - _.SwingV = kTcl112AcSwingVOn; - } -} - -/// Get the Turbo setting of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRTcl112Ac::getTurbo(void) const { return _.Turbo; } - -/// Set the Quiet setting of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRTcl112Ac::setQuiet(const bool on) { - _quiet_explictly_set = true; - _quiet = on; - if (_.MsgType == kTcl112AcSpecial) _.Quiet = on; -} - -/// Get the Quiet setting of the A/C. -/// @param[in] def The default value to use if we are not sure. -/// @return true, the setting is on. false, the setting is off. -bool IRTcl112Ac::getQuiet(const bool def) const { - if (_.MsgType == kTcl112AcSpecial) - return _.Quiet; - else - return _quiet_explictly_set ? _quiet : def; -} - -/// Get how long the On Timer is set for, in minutes. -/// @return The time in nr of minutes. -uint16_t IRTcl112Ac::getOnTimer(void) const { - return _.OnTimer * kTcl112AcTimerResolution; -} - -/// Set or cancel the On Timer function. -/// @param[in] mins Nr. of minutes the timer is to be set to. -/// @note Rounds down to 20 min increments. (max: 720 mins (12h), 0 is Off) -void IRTcl112Ac::setOnTimer(const uint16_t mins) { - _.OnTimer = std::min(mins, kTcl112AcTimerMax) / kTcl112AcTimerResolution; - _.OnTimerEnabled = _.OnTimer > 0; - _.TimerIndicator = _.OnTimerEnabled || _.OffTimerEnabled; -} - -/// Get how long the Off Timer is set for, in minutes. -/// @return The time in nr of minutes. -uint16_t IRTcl112Ac::getOffTimer(void) const { - return _.OffTimer * kTcl112AcTimerResolution; -} - -/// Set or cancel the Off Timer function. -/// @param[in] mins Nr. of minutes the timer is to be set to. -/// @note Rounds down to 20 min increments. (max: 720 mins (12h), 0 is Off) -void IRTcl112Ac::setOffTimer(const uint16_t mins) { - _.OffTimer = std::min(mins, kTcl112AcTimerMax) / kTcl112AcTimerResolution; - _.OffTimerEnabled = _.OffTimer > 0; - _.TimerIndicator = _.OnTimerEnabled || _.OffTimerEnabled; -} - -/// 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 IRTcl112Ac::convertMode(const stdAc::opmode_t mode) { - switch (mode) { - case stdAc::opmode_t::kCool: return kTcl112AcCool; - case stdAc::opmode_t::kHeat: return kTcl112AcHeat; - case stdAc::opmode_t::kDry: return kTcl112AcDry; - case stdAc::opmode_t::kFan: return kTcl112AcFan; - default: return kTcl112AcAuto; - } -} - -/// 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 IRTcl112Ac::convertFan(const stdAc::fanspeed_t speed) { - switch (speed) { - case stdAc::fanspeed_t::kMin: return kTcl112AcFanMin; - case stdAc::fanspeed_t::kLow: return kTcl112AcFanLow; - case stdAc::fanspeed_t::kMedium: return kTcl112AcFanMed; - case stdAc::fanspeed_t::kHigh: - case stdAc::fanspeed_t::kMax: return kTcl112AcFanHigh; - default: return kTcl112AcFanAuto; - } -} - -/// 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 IRTcl112Ac::toCommonMode(const uint8_t mode) { - switch (mode) { - case kTcl112AcCool: return stdAc::opmode_t::kCool; - case kTcl112AcHeat: return stdAc::opmode_t::kHeat; - case kTcl112AcDry: return stdAc::opmode_t::kDry; - case kTcl112AcFan: return stdAc::opmode_t::kFan; - default: return stdAc::opmode_t::kAuto; - } -} - -/// 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 IRTcl112Ac::convertSwingV(const stdAc::swingv_t position) { - switch (position) { - case stdAc::swingv_t::kOff: return kTcl112AcSwingVOff; - case stdAc::swingv_t::kHighest: return kTcl112AcSwingVHighest; - case stdAc::swingv_t::kHigh: return kTcl112AcSwingVHigh; - case stdAc::swingv_t::kMiddle: return kTcl112AcSwingVMiddle; - case stdAc::swingv_t::kLow: return kTcl112AcSwingVLow; - case stdAc::swingv_t::kLowest: return kTcl112AcSwingVLowest; - default: return kTcl112AcSwingVOn; - } -} - -/// Convert a native fan speed into its stdAc equivalent. -/// @param[in] spd The native setting to be converted. -/// @return The stdAc equivalent of the native setting. -stdAc::fanspeed_t IRTcl112Ac::toCommonFanSpeed(const uint8_t spd) { - switch (spd) { - case kTcl112AcFanHigh: return stdAc::fanspeed_t::kMax; - case kTcl112AcFanMed: return stdAc::fanspeed_t::kMedium; - case kTcl112AcFanLow: return stdAc::fanspeed_t::kLow; - case kTcl112AcFanMin: return stdAc::fanspeed_t::kMin; - default: return stdAc::fanspeed_t::kAuto; - } -} - -/// Convert a native vertical swing postion to it's common equivalent. -/// @param[in] setting A native position to convert. -/// @return The common vertical swing position. -stdAc::swingv_t IRTcl112Ac::toCommonSwingV(const uint8_t setting) { - switch (setting) { - case kTcl112AcSwingVOff: return stdAc::swingv_t::kOff; - default: return stdAc::swingv_t::kAuto; - } -} -/// 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 IRTcl112Ac::toCommon(const stdAc::state_t *prev) const { - stdAc::state_t result; - // Start with the previous state if given it. - if (prev != NULL) result = *prev; - result.protocol = decode_type_t::TCL112AC; - result.model = getModel(); - result.quiet = getQuiet(result.quiet); - // The rest only get updated if it is a "normal" message. - if (_.MsgType == kTcl112AcNormal) { - result.power = _.Power; - result.mode = toCommonMode(_.Mode); - result.celsius = true; - result.degrees = getTemp(); - result.fanspeed = toCommonFanSpeed(_.Fan); - result.swingv = toCommonSwingV(_.SwingV); - result.swingh = _.SwingH ? stdAc::swingh_t::kAuto : stdAc::swingh_t::kOff; - result.turbo = _.Turbo; - result.filter = _.Health; - result.econo = _.Econo; - result.light = getLight(); - } - // Not supported. - 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 IRTcl112Ac::toString(void) const { - String result = ""; - result.reserve(220); // Reserve some heap for the string to reduce fragging. - tcl_ac_remote_model_t model = getModel(); - result += addModelToString(decode_type_t::TCL112AC, model, false); - result += addIntToString(_.MsgType, D_STR_TYPE); - switch (_.MsgType) { - case kTcl112AcNormal: - result += addBoolToString(_.Power, kPowerStr); - result += addModeToString(_.Mode, kTcl112AcAuto, kTcl112AcCool, - kTcl112AcHeat, kTcl112AcDry, kTcl112AcFan); - result += addTempFloatToString(getTemp()); - result += addFanToString(_.Fan, kTcl112AcFanHigh, kTcl112AcFanLow, - kTcl112AcFanAuto, kTcl112AcFanMin, - kTcl112AcFanMed); - result += addSwingVToString(_.SwingV, kTcl112AcSwingVOff, - kTcl112AcSwingVHighest, - kTcl112AcSwingVHigh, - 0xFF, // unused - kTcl112AcSwingVMiddle, - 0xFF, // unused - kTcl112AcSwingVLow, - kTcl112AcSwingVLowest, - kTcl112AcSwingVOff, - kTcl112AcSwingVOn, // Swing - 0xFF, 0xFF); // Both Unused - if (model != tcl_ac_remote_model_t::GZ055BE1) { - result += addBoolToString(_.SwingH, kSwingHStr); - result += addBoolToString(_.Econo, kEconoStr); - result += addBoolToString(_.Health, kHealthStr); - result += addBoolToString(_.Turbo, kTurboStr); - result += addBoolToString(getLight(), kLightStr); - } - result += addLabeledString( - _.OnTimerEnabled ? minsToString(getOnTimer()) : kOffStr, - kOnTimerStr); - result += addLabeledString( - _.OffTimerEnabled ? minsToString(getOffTimer()) : kOffStr, - kOffTimerStr); - break; - case kTcl112AcSpecial: - result += addBoolToString(_.Quiet, kQuietStr); - break; - } - return result; -} - -#if DECODE_TCL112AC -/// @file -/// @note There is no `decodedecodeTcl112Ac()`. -/// It's the same as `decodeMitsubishi112()`. A shared routine is used. -/// You can find it in: ir_Mitsubishi.cpp -#endif // DECODE_TCL112AC diff --git a/lib/IRremoteESP8266/src/ir_Tcl.h b/lib/IRremoteESP8266/src/ir_Tcl.h index e1696c42ad..0f075b43b1 100644 --- a/lib/IRremoteESP8266/src/ir_Tcl.h +++ b/lib/IRremoteESP8266/src/ir_Tcl.h @@ -15,11 +15,11 @@ #ifndef UNIT_TEST #include #endif -#include "IRremoteESP8266.h" -#include "IRsend.h" -#include "IRrecv.h" +#include "../src/IRremoteESP8266.h" +#include "../src/IRsend.h" +#include "../src/IRrecv.h" #ifdef UNIT_TEST -#include "IRsend_test.h" +#include "../src/IRsend_test.h" #endif /// Native representation of a TCL 112 A/C message. diff --git a/lib/IRremoteESP8266/src/ir_Technibel.cpp b/lib/IRremoteESP8266/src/ir_Technibel.cpp deleted file mode 100644 index d58cc7e055..0000000000 --- a/lib/IRremoteESP8266/src/ir_Technibel.cpp +++ /dev/null @@ -1,408 +0,0 @@ -// Copyright 2020 Quentin Briollant - -/// @file -/// @brief Support for Technibel protocol. - -#include "ir_Technibel.h" -#include "IRrecv.h" -#include "IRsend.h" -#include "IRtext.h" -#include "IRutils.h" -#include - -using irutils::addBoolToString; -using irutils::addModeToString; -using irutils::addFanToString; -using irutils::addLabeledString; -using irutils::addTempToString; -using irutils::minsToString; - -const uint16_t kTechnibelAcHdrMark = 8836; -const uint16_t kTechnibelAcHdrSpace = 4380; -const uint16_t kTechnibelAcBitMark = 523; -const uint16_t kTechnibelAcOneSpace = 1696; -const uint16_t kTechnibelAcZeroSpace = 564; -const uint32_t kTechnibelAcGap = kDefaultMessageGap; -const uint16_t kTechnibelAcFreq = 38000; - - -#if SEND_TECHNIBEL_AC -/// Send an Technibel AC formatted message. -/// Status: STABLE / Reported as working on a real device. -/// @param[in] data containing the IR command. -/// @param[in] nbits Nr. of bits to send. usually kTechnibelAcBits -/// @param[in] repeat Nr. of times the message is to be repeated. -void IRsend::sendTechnibelAc(const uint64_t data, const uint16_t nbits, - const uint16_t repeat) { - sendGeneric(kTechnibelAcHdrMark, kTechnibelAcHdrSpace, - kTechnibelAcBitMark, kTechnibelAcOneSpace, - kTechnibelAcBitMark, kTechnibelAcZeroSpace, - kTechnibelAcBitMark, kTechnibelAcGap, - data, nbits, kTechnibelAcFreq, true, // LSB First. - repeat, kDutyDefault); -} -#endif // SEND_TECHNIBEL_AC - -#if DECODE_TECHNIBEL_AC -/// Status: STABLE / Reported as working on a real device -/// @param[in,out] results Ptr to 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 (kTechnibelAcBits). -/// @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::decodeTechnibelAc(decode_results *results, uint16_t offset, - const uint16_t nbits, const bool strict) { - // Compliance - if (strict && nbits != kTechnibelAcBits) { - return false; - } - - uint64_t data = 0; - - // Header + Data + Footer - if (!matchGeneric(results->rawbuf + offset, &data, - results->rawlen - offset, nbits, - kTechnibelAcHdrMark, kTechnibelAcHdrSpace, - kTechnibelAcBitMark, kTechnibelAcOneSpace, - kTechnibelAcBitMark, kTechnibelAcZeroSpace, - kTechnibelAcBitMark, kTechnibelAcGap, true, - _tolerance, kMarkExcess, true)) return false; - - // Compliance - if (strict && !IRTechnibelAc::validChecksum(data)) return false; - - // Success - results->decode_type = decode_type_t::TECHNIBEL_AC; - results->bits = nbits; - results->value = data; - results->command = 0; - results->address = 0; - return true; -} -#endif // DECODE_TECHNIBEL_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? -IRTechnibelAc::IRTechnibelAc(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 IRTechnibelAc::begin(void) { _irsend.begin(); } - -#if SEND_TECHNIBEL_AC -/// Send the current internal state as an IR message. -/// @param[in] repeat Nr. of times the message will be repeated. -void IRTechnibelAc::send(const uint16_t repeat) { - _irsend.sendTechnibelAc(getRaw(), kTechnibelAcBits, repeat); -} -#endif // SEND_TECHNIBEL_AC - -/// Compute the checksum of the supplied state. -/// @param[in] state A valid code for this protocol. -/// @return The calculated checksum of the supplied state. -uint8_t IRTechnibelAc::calcChecksum(const uint64_t state) { - uint8_t sum = 0; - // Add up all the 8 bit data chunks. - for (uint8_t offset = kTechnibelAcTimerHoursOffset; - offset < kTechnibelAcHeaderOffset; offset += 8) - sum += GETBITS64(state, offset, 8); - return ~sum + 1; -} - -/// Confirm the checksum of the supplied state is valid. -/// @param[in] state A valid code for this protocol. -/// @return `true` if the checksum is correct, otherwise `false`. -bool IRTechnibelAc::validChecksum(const uint64_t state) { - TechnibelProtocol p{.raw = state}; - return calcChecksum(state) == p.Sum; -} - -/// Set the checksum of the internal state. -void IRTechnibelAc::checksum(void) { - _.Sum = calcChecksum(_.raw); -} - -/// Reset the internal state of the emulation. -/// @note Mode:Cool, Power:Off, fan:Low, temp:20, swing:Off, sleep:Off -void IRTechnibelAc::stateReset(void) { - _.raw = kTechnibelAcResetState; - _saved_temp = 20; // DegC (Random reasonable default value) - _saved_temp_units = 0; // Celsius -} - -/// Get a copy of the internal state/code for this protocol. -/// @return A code for this protocol based on the current internal state. -uint64_t IRTechnibelAc::getRaw(void) { - checksum(); - return _.raw; -} - -/// Set the internal state from a valid code for this protocol. -/// @param[in] state A valid code for this protocol. -void IRTechnibelAc::setRaw(const uint64_t state) { - _.raw = state; -} - -/// Set the requested power state of the A/C to on. -void IRTechnibelAc::on(void) { setPower(true); } - -/// Set the requested power state of the A/C to off. -void IRTechnibelAc::off(void) { setPower(false); } - -/// Change the power setting. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRTechnibelAc::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 IRTechnibelAc::getPower(void) const { - return _.Power; -} - -/// Set the temperature unit setting. -/// @param[in] fahrenheit true, the unit is °F. false, the unit is °C. -void IRTechnibelAc::setTempUnit(const bool fahrenheit) { - _saved_temp_units = fahrenheit; - _.UseFah = fahrenheit; -} - -/// Get the temperature unit setting. -/// @return true, the unit is °F. false, the unit is °C. -bool IRTechnibelAc::getTempUnit(void) const { - return _.UseFah; -} - -/// Set the temperature. -/// @param[in] degrees The temperature in degrees. -/// @param[in] fahrenheit The temperature unit: true=°F, false(default)=°C. -void IRTechnibelAc::setTemp(const uint8_t degrees, const bool fahrenheit) { - setTempUnit(fahrenheit); - uint8_t temp_min = fahrenheit ? kTechnibelAcTempMinF : kTechnibelAcTempMinC; - uint8_t temp_max = fahrenheit ? kTechnibelAcTempMaxF : kTechnibelAcTempMaxC; - _saved_temp = std::min(temp_max, std::max(temp_min, degrees)); - _.Temp = _saved_temp; -} - -/// Get the current temperature setting. -/// @return The current setting for temp. in degrees. -uint8_t IRTechnibelAc::getTemp(void) const { - return _.Temp; -} - -/// Set the speed of the fan. -/// @param[in] speed The desired setting. -void IRTechnibelAc::setFan(const uint8_t speed) { - // Mode fan speed rules. - if (_.Mode == kTechnibelAcDry && speed != kTechnibelAcFanLow) { - _.Fan = kTechnibelAcFanLow; - return; - } - switch (speed) { - case kTechnibelAcFanHigh: - case kTechnibelAcFanMedium: - case kTechnibelAcFanLow: - _.Fan = speed; - break; - default: - _.Fan = kTechnibelAcFanLow; - } -} - -/// Get the current fan speed setting. -/// @return The current fan speed/mode. -uint8_t IRTechnibelAc::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 IRTechnibelAc::convertFan(const stdAc::fanspeed_t speed) { - switch (speed) { - case stdAc::fanspeed_t::kMin: - case stdAc::fanspeed_t::kLow: return kTechnibelAcFanLow; - case stdAc::fanspeed_t::kMedium: return kTechnibelAcFanMedium; - case stdAc::fanspeed_t::kHigh: - case stdAc::fanspeed_t::kMax: return kTechnibelAcFanHigh; - default: return kTechnibelAcFanLow; - } -} - -/// 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 IRTechnibelAc::toCommonFanSpeed(const uint8_t speed) { - switch (speed) { - case kTechnibelAcFanHigh: return stdAc::fanspeed_t::kHigh; - case kTechnibelAcFanMedium: return stdAc::fanspeed_t::kMedium; - default: return stdAc::fanspeed_t::kLow; - } -} - -/// Get the operating mode setting of the A/C. -/// @return The current operating mode setting. -uint8_t IRTechnibelAc::getMode(void) const { - return _.Mode; -} - -/// Set the operating mode of the A/C. -/// @param[in] mode The desired operating mode. -void IRTechnibelAc::setMode(const uint8_t mode) { - _.Mode = mode; - switch (mode) { - case kTechnibelAcHeat: - case kTechnibelAcFan: - case kTechnibelAcDry: - case kTechnibelAcCool: - break; - default: - _.Mode = kTechnibelAcCool; - } - setFan(_.Fan); // Re-force any fan speed constraints. - // Restore previous temp settings for cool mode. - setTemp(_saved_temp, _saved_temp_units); -} - -/// 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 IRTechnibelAc::convertMode(const stdAc::opmode_t mode) { - switch (mode) { - case stdAc::opmode_t::kHeat: return kTechnibelAcHeat; - case stdAc::opmode_t::kDry: return kTechnibelAcDry; - case stdAc::opmode_t::kFan: return kTechnibelAcFan; - default: return kTechnibelAcCool; - } -} - -/// 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 IRTechnibelAc::toCommonMode(const uint8_t mode) { - switch (mode) { - case kTechnibelAcHeat: return stdAc::opmode_t::kHeat; - case kTechnibelAcDry: return stdAc::opmode_t::kDry; - case kTechnibelAcFan: return stdAc::opmode_t::kFan; - default: return stdAc::opmode_t::kCool; - } -} - -/// Set the (vertical) swing setting of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRTechnibelAc::setSwing(const bool on) { - _.Swing = on; -} - -/// Get the (vertical) swing setting of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRTechnibelAc::getSwing(void) const { - return _.Swing; -} - -/// Convert a stdAc::swingv_t enum into it's native swing. -/// @param[in] swing The enum to be converted. -/// @return true, the swing is on. false, the swing is off. -bool IRTechnibelAc::convertSwing(const stdAc::swingv_t swing) { - return swing != stdAc::swingv_t::kOff; -} - -/// Convert a native swing into its stdAc equivalent. -/// @param[in] swing true, the swing is on. false, the swing is off. -/// @return The stdAc equivalent of the native setting. -stdAc::swingv_t IRTechnibelAc::toCommonSwing(const bool swing) { - return swing ? stdAc::swingv_t::kAuto : stdAc::swingv_t::kOff; -} - -/// Set the Sleep setting of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRTechnibelAc::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 IRTechnibelAc::getSleep(void) const { - return _.Sleep; -} - -/// Set the enable timer setting. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRTechnibelAc::setTimerEnabled(const bool on) { - _.TimerEnable = on; -} - -/// Is the timer function enabled? -/// @return true, the setting is on. false, the setting is off. -bool IRTechnibelAc::getTimerEnabled(void) const { - return _.TimerEnable; -} - -/// Set the timer for when the A/C unit will switch off. -/// @param[in] nr_of_mins Number of minutes before power off. -/// `0` will clear the timer. Max is 24 hrs (1440 mins). -/// @note Time is stored internally in hours. -void IRTechnibelAc::setTimer(const uint16_t nr_of_mins) { - const uint8_t hours = nr_of_mins / 60; - _.TimerHours = std::min(kTechnibelAcTimerMax, hours); - // Enable or not? - setTimerEnabled(hours); -} - -/// Get the timer time for when the A/C unit will switch power state. -/// @return The number of minutes left on the timer. `0` means off. -uint16_t IRTechnibelAc::getTimer(void) const { - return _.TimerEnable ? _.TimerHours * 60 : 0; -} - -/// Convert the current internal state into its stdAc::state_t equivalent. -/// @return The stdAc equivalent of the native settings. -stdAc::state_t IRTechnibelAc::toCommon(void) const { - stdAc::state_t result; - result.protocol = decode_type_t::TECHNIBEL_AC; - result.power = _.Power; - result.mode = toCommonMode(_.Mode); - result.celsius = !_.UseFah; - result.degrees = _.Temp; - result.fanspeed = toCommonFanSpeed(_.Fan); - result.sleep = _.Sleep ? 0 : -1; - result.swingv = toCommonSwing(_.Swing); - // Not supported. - result.model = -1; - result.turbo = false; - 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 IRTechnibelAc::toString(void) const { - String result = ""; - result.reserve(100); // Reserve some heap for the string to reduce fragging. - result += addBoolToString(_.Power, kPowerStr, false); - result += addModeToString(_.Mode, 255, // No Auto, so use impossible value - kTechnibelAcCool, kTechnibelAcHeat, kTechnibelAcDry, - kTechnibelAcFan); - result += addFanToString(_.Fan, kTechnibelAcFanHigh, kTechnibelAcFanLow, - kTechnibelAcFanLow, kTechnibelAcFanLow, - kTechnibelAcFanMedium); - result += addTempToString(_.Temp, !_.UseFah); - result += addBoolToString(_.Sleep, kSleepStr); - result += addBoolToString(_.Swing, kSwingVStr); - result += addLabeledString(_.TimerEnable ? minsToString(getTimer()) - : kOffStr, - kTimerStr); - return result; -} diff --git a/lib/IRremoteESP8266/src/ir_Technibel.h b/lib/IRremoteESP8266/src/ir_Technibel.h index fb4a08717c..decbee4e87 100644 --- a/lib/IRremoteESP8266/src/ir_Technibel.h +++ b/lib/IRremoteESP8266/src/ir_Technibel.h @@ -11,10 +11,10 @@ #ifndef UNIT_TEST #include #endif -#include "IRremoteESP8266.h" -#include "IRsend.h" +#include "../src/IRremoteESP8266.h" +#include "../src/IRsend.h" #ifdef UNIT_TEST -#include "IRsend_test.h" +#include "../src/IRsend_test.h" #endif // Supports: diff --git a/lib/IRremoteESP8266/src/ir_Teco.cpp b/lib/IRremoteESP8266/src/ir_Teco.cpp deleted file mode 100644 index 031691eddd..0000000000 --- a/lib/IRremoteESP8266/src/ir_Teco.cpp +++ /dev/null @@ -1,375 +0,0 @@ -// Copyright 2019 Fabien Valthier - -/// @file -/// @brief Support for Teco protocols. - -#include "ir_Teco.h" -#include -#include "IRremoteESP8266.h" -#include "IRtext.h" -#include "IRutils.h" -#ifndef ARDUINO -#include -#endif - -// Constants -// using SPACE modulation. -const uint16_t kTecoHdrMark = 9000; -const uint16_t kTecoHdrSpace = 4440; -const uint16_t kTecoBitMark = 620; -const uint16_t kTecoOneSpace = 1650; -const uint16_t kTecoZeroSpace = 580; -const uint32_t kTecoGap = kDefaultMessageGap; // Made-up value. Just a guess. - -using irutils::addBoolToString; -using irutils::addFanToString; -using irutils::addIntToString; -using irutils::addLabeledString; -using irutils::addModeToString; -using irutils::addTempToString; - -#if SEND_TECO -/// Send a Teco A/C 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::sendTeco(const uint64_t data, const uint16_t nbits, - const uint16_t repeat) { - sendGeneric(kTecoHdrMark, kTecoHdrSpace, kTecoBitMark, kTecoOneSpace, - kTecoBitMark, kTecoZeroSpace, kTecoBitMark, kTecoGap, - data, nbits, 38000, false, repeat, kDutyDefault); -} -#endif // SEND_TECO - -/// 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? -IRTecoAc::IRTecoAc(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 IRTecoAc::begin(void) { _irsend.begin(); } - -#if SEND_TECO -/// Send the current internal state as an IR message. -/// @param[in] repeat Nr. of times the message will be repeated. -void IRTecoAc::send(const uint16_t repeat) { - _irsend.sendTeco(_.raw, kTecoBits, repeat); -} -#endif // SEND_TECO - -/// Reset the internal state of the emulation. -/// @note Mode:auto, Power:Off, fan:auto, temp:16, swing:off, sleep:off -void IRTecoAc::stateReset(void) { - _.raw = kTecoReset; -} - -/// Get a copy of the internal state/code for this protocol. -/// @return A code for this protocol based on the current internal state. -uint64_t IRTecoAc::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 IRTecoAc::setRaw(const uint64_t new_code) { _.raw = new_code; } - -/// Set the requested power state of the A/C to on. -void IRTecoAc::on(void) { setPower(true); } - -/// Set the requested power state of the A/C to off. -void IRTecoAc::off(void) { setPower(false); } - -/// Change the power setting. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRTecoAc::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 IRTecoAc::getPower(void) const { - return _.Power; -} - -/// Set the temperature. -/// @param[in] temp The temperature in degrees celsius. -void IRTecoAc::setTemp(const uint8_t temp) { - uint8_t newtemp = temp; - newtemp = std::min(newtemp, kTecoMaxTemp); - newtemp = std::max(newtemp, kTecoMinTemp); - _.Temp = newtemp - kTecoMinTemp; -} - -/// Get the current temperature setting. -/// @return The current setting for temp. in degrees celsius. -uint8_t IRTecoAc::getTemp(void) const { - return _.Temp + kTecoMinTemp; -} - -/// Set the speed of the fan. -/// @param[in] speed The desired setting. -void IRTecoAc::setFan(const uint8_t speed) { - uint8_t newspeed = speed; - switch (speed) { - case kTecoFanAuto: - case kTecoFanHigh: - case kTecoFanMed: - case kTecoFanLow: break; - default: newspeed = kTecoFanAuto; - } - _.Fan = newspeed; -} - -/// Get the current fan speed setting. -/// @return The current fan speed/mode. -uint8_t IRTecoAc::getFan(void) const { - return _.Fan; -} - -/// Set the operating mode of the A/C. -/// @param[in] mode The desired operating mode. -void IRTecoAc::setMode(const uint8_t mode) { - uint8_t newmode = mode; - switch (mode) { - case kTecoAuto: - case kTecoCool: - case kTecoDry: - case kTecoFan: - case kTecoHeat: break; - default: newmode = kTecoAuto; - } - _.Mode = newmode; -} - -/// Get the operating mode setting of the A/C. -/// @return The current operating mode setting. -uint8_t IRTecoAc::getMode(void) const { - return _.Mode; -} - -/// Set the (vertical) swing setting of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRTecoAc::setSwing(const bool on) { - _.Swing = on; -} - -/// Get the (vertical) swing setting of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRTecoAc::getSwing(void) const { - return _.Swing; -} - -/// Set the Sleep setting of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRTecoAc::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 IRTecoAc::getSleep(void) const { - return _.Sleep; -} - -/// Set the Light (LED/Display) setting of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRTecoAc::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 IRTecoAc::getLight(void) const { - return _.Light; -} - -/// Set the Humid setting of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRTecoAc::setHumid(const bool on) { - _.Humid = on; -} - -/// Get the Humid setting of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRTecoAc::getHumid(void) const { - return _.Humid; -} - -/// Set the Save setting of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRTecoAc::setSave(const bool on) { - _.Save = on; -} - -/// Get the Save setting of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRTecoAc::getSave(void) const { - return _.Save; -} - -/// Is the timer function enabled? -/// @return true, the setting is on. false, the setting is off. -inline bool IRTecoAc::getTimerEnabled(void) const { - return _.TimerOn; -} - -/// Get the timer time for when the A/C unit will switch power state. -/// @return The number of minutes left on the timer. `0` means off. -uint16_t IRTecoAc::getTimer(void) const { - uint16_t mins = 0; - if (getTimerEnabled()) { - mins = (_.TensHours * 10 + _.UnitHours) * 60; - if (_.HalfHour) mins += 30; - } - return mins; -} - -/// Set the timer for when the A/C unit will switch power state. -/// @param[in] nr_mins Number of minutes before power state change. -/// `0` will clear the timer. Max is 24 hrs. -/// @note Time is stored internally in increments of 30 mins. -void IRTecoAc::setTimer(const uint16_t nr_mins) { - uint16_t mins = std::min(nr_mins, (uint16_t)(24 * 60)); // Limit to 24 hrs. - uint8_t hours = mins / 60; - _.TimerOn = mins > 0; // Set the timer flag. - _.HalfHour = (mins % 60) >= 30; - _.UnitHours = hours % 10; - _.TensHours = hours / 10; -} - -/// 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 IRTecoAc::convertMode(const stdAc::opmode_t mode) { - switch (mode) { - case stdAc::opmode_t::kCool: return kTecoCool; - case stdAc::opmode_t::kHeat: return kTecoHeat; - case stdAc::opmode_t::kDry: return kTecoDry; - case stdAc::opmode_t::kFan: return kTecoFan; - default: return kTecoAuto; - } -} - -/// 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 IRTecoAc::convertFan(const stdAc::fanspeed_t speed) { - switch (speed) { - case stdAc::fanspeed_t::kMin: - case stdAc::fanspeed_t::kLow: return kTecoFanLow; - case stdAc::fanspeed_t::kMedium: return kTecoFanMed; - case stdAc::fanspeed_t::kHigh: - case stdAc::fanspeed_t::kMax: return kTecoFanHigh; - default: return kTecoFanAuto; - } -} - -/// 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 IRTecoAc::toCommonMode(const uint8_t mode) { - switch (mode) { - case kTecoCool: return stdAc::opmode_t::kCool; - case kTecoHeat: return stdAc::opmode_t::kHeat; - case kTecoDry: return stdAc::opmode_t::kDry; - case kTecoFan: 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 IRTecoAc::toCommonFanSpeed(const uint8_t speed) { - switch (speed) { - case kTecoFanHigh: return stdAc::fanspeed_t::kMax; - case kTecoFanMed: return stdAc::fanspeed_t::kMedium; - case kTecoFanLow: 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 IRTecoAc::toCommon(void) const { - stdAc::state_t result; - result.protocol = decode_type_t::TECO; - result.model = -1; // Not supported. - result.power = _.Power; - result.mode = toCommonMode(_.Mode); - result.celsius = true; - result.degrees = getTemp(); - result.fanspeed = toCommonFanSpeed(_.Fan); - result.swingv = _.Swing ? stdAc::swingv_t::kAuto : stdAc::swingv_t::kOff; - result.sleep = _.Sleep ? 0 : -1; - result.light = _.Light; - // Not supported. - result.swingh = stdAc::swingh_t::kOff; - result.turbo = 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 IRTecoAc::toString(void) const { - String result = ""; - result.reserve(100); // Reserve some heap for the string to reduce fragging. - result += addBoolToString(_.Power, kPowerStr, false); - result += addModeToString(_.Mode, kTecoAuto, kTecoCool, kTecoHeat, - kTecoDry, kTecoFan); - result += addTempToString(getTemp()); - result += addFanToString(_.Fan, kTecoFanHigh, kTecoFanLow, - kTecoFanAuto, kTecoFanAuto, kTecoFanMed); - result += addBoolToString(_.Sleep, kSleepStr); - result += addBoolToString(_.Swing, kSwingStr); - result += addBoolToString(_.Light, kLightStr); - result += addBoolToString(_.Humid, kHumidStr); - result += addBoolToString(_.Save, kSaveStr); - if (getTimerEnabled()) - result += addLabeledString(irutils::minsToString(getTimer()), - kTimerStr); - else - result += addBoolToString(false, kTimerStr); - return result; -} - -#if DECODE_TECO -/// Decode the supplied Teco message. -/// Status: STABLE / Tested. -/// @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::decodeTeco(decode_results* results, uint16_t offset, - const uint16_t nbits, const bool strict) { - if (strict && nbits != kTecoBits) return false; // Not what is expected - - uint64_t data = 0; - // Match Header + Data + Footer - if (!matchGeneric(results->rawbuf + offset, &data, - results->rawlen - offset, nbits, - kTecoHdrMark, kTecoHdrSpace, - kTecoBitMark, kTecoOneSpace, - kTecoBitMark, kTecoZeroSpace, - kTecoBitMark, kTecoGap, true, - _tolerance, kMarkExcess, false)) return false; - - // Success - results->decode_type = TECO; - results->bits = nbits; - results->value = data; - results->address = 0; - results->command = 0; - return true; -} -#endif // DECODE_TECO diff --git a/lib/IRremoteESP8266/src/ir_Teco.h b/lib/IRremoteESP8266/src/ir_Teco.h index c2ea1c561e..0ce034a9ea 100644 --- a/lib/IRremoteESP8266/src/ir_Teco.h +++ b/lib/IRremoteESP8266/src/ir_Teco.h @@ -13,10 +13,10 @@ #ifndef UNIT_TEST #include #endif -#include "IRremoteESP8266.h" -#include "IRsend.h" +#include "../src/IRremoteESP8266.h" +#include "../src/IRsend.h" #ifdef UNIT_TEST -#include "IRsend_test.h" +#include "../src/IRsend_test.h" #endif /// Native representation of a Teco A/C message. diff --git a/lib/IRremoteESP8266/src/ir_Teknopoint.cpp b/lib/IRremoteESP8266/src/ir_Teknopoint.cpp deleted file mode 100644 index 0feee6d704..0000000000 --- a/lib/IRremoteESP8266/src/ir_Teknopoint.cpp +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright 2021 David Conran (crankyoldgit) -/// @file -/// @brief Support for the Teknopoint protocol -/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1486 - -// Supports: -// Brand: Teknopoint, Model: Allegro SSA-09H A/C -// Brand: Teknopoint, Model: GZ-055B-E1 remote - -#include "IRrecv.h" -#include "IRsend.h" -#include "IRutils.h" - -// Protocol timings -const uint16_t kTeknopointHdrMark = 3600; -const uint16_t kTeknopointBitMark = 477; -const uint16_t kTeknopointHdrSpace = 1600; -const uint16_t kTeknopointOneSpace = 1200; -const uint16_t kTeknopointZeroSpace = 530; -const uint16_t kTeknopointFreq = 38000; // Hz. (Guess Only) -const uint8_t kTeknopointExtraTol = 10; // Extra tolerance percentage. - -#if SEND_TEKNOPOINT -/// Send a Teknopoint formatted message. -/// Status: BETA / Probably works. -/// @param[in] data An array of bytes containing the IR command. -/// @param[in] nbytes Nr. of bytes of data in the array. -/// @param[in] repeat Nr. of times the message is to be repeated. -void IRsend::sendTeknopoint(const uint8_t data[], const uint16_t nbytes, - const uint16_t repeat) { - sendGeneric(kTeknopointHdrMark, kTeknopointHdrSpace, - kTeknopointBitMark, kTeknopointOneSpace, - kTeknopointBitMark, kTeknopointZeroSpace, - kTeknopointBitMark, kDefaultMessageGap, - data, nbytes, // Bytes - kTeknopointFreq, false, repeat, kDutyDefault); -} -#endif // SEND_TEKNOPOINT - -#if DECODE_TEKNOPOINT -/// Decode the supplied Teknopoint message. -/// Status: Alpha / 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::decodeTeknopoint(decode_results *results, uint16_t offset, - const uint16_t nbits, const bool strict) { - if (results->rawlen < 2 * nbits + kHeader + kFooter - 1 - offset) - return false; // Too short a message to match. - if (strict && nbits != kTeknopointBits) - return false; - - if (!matchGeneric(results->rawbuf + offset, results->state, - results->rawlen - offset, nbits, - kTeknopointHdrMark, kTeknopointHdrSpace, - kTeknopointBitMark, kTeknopointOneSpace, - kTeknopointBitMark, kTeknopointZeroSpace, - kTeknopointBitMark, kDefaultMessageGap, - true, _tolerance + kTeknopointExtraTol, - kMarkExcess, false)) return false; - // Compliance - if (strict) { - // Is the checksum valid? - if (sumBytes(results->state, kTeknopointStateLength - 1) != - results->state[kTeknopointStateLength - 1]) return false; - } - // Success - results->decode_type = decode_type_t::TEKNOPOINT; - results->bits = nbits; - return true; -} -#endif // DECODE_TEKNOPOINT diff --git a/lib/IRremoteESP8266/src/ir_Toshiba.cpp b/lib/IRremoteESP8266/src/ir_Toshiba.cpp deleted file mode 100644 index 0e3ac8ba27..0000000000 --- a/lib/IRremoteESP8266/src/ir_Toshiba.cpp +++ /dev/null @@ -1,529 +0,0 @@ -// Copyright 2017-2021 David Conran - -/// @file -/// @brief Support for Toshiba protocols. -/// @see https://github.com/r45635/HVAC-IR-Control -/// @see https://github.com/r45635/HVAC-IR-Control/blob/master/HVAC_ESP8266/HVAC_ESP8266T.ino#L77 -/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1205 -/// @see https://www.toshiba-carrier.co.jp/global/about/index.htm -/// @see http://www.toshiba-carrier.co.th/AboutUs/Pages/CompanyProfile.aspx - -#include "ir_Toshiba.h" -#include -#include -#ifndef ARDUINO -#include -#endif -#include "IRrecv.h" -#include "IRsend.h" -#include "IRtext.h" -#include "IRutils.h" - -// Constants - -// Toshiba A/C -const uint16_t kToshibaAcHdrMark = 4400; -const uint16_t kToshibaAcHdrSpace = 4300; -const uint16_t kToshibaAcBitMark = 580; -const uint16_t kToshibaAcOneSpace = 1600; -const uint16_t kToshibaAcZeroSpace = 490; -// Some models have a different inter-message gap. -// See: https://github.com/crankyoldgit/IRremoteESP8266/issues/1420 -const uint16_t kToshibaAcMinGap = 4600; // WH-UB03NJ remote -const uint16_t kToshibaAcUsualGap = 7400; // Others - -using irutils::addBoolToString; -using irutils::addFanToString; -using irutils::addIntToString; -using irutils::addLabeledString; -using irutils::addModeToString; -using irutils::addTempToString; -using irutils::checkInvertedBytePairs; -using irutils::invertBytePairs; - -#if SEND_TOSHIBA_AC -/// Send a Toshiba A/C 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::sendToshibaAC(const uint8_t data[], const uint16_t nbytes, - const uint16_t repeat) { - sendGeneric(kToshibaAcHdrMark, kToshibaAcHdrSpace, kToshibaAcBitMark, - kToshibaAcOneSpace, kToshibaAcBitMark, kToshibaAcZeroSpace, - kToshibaAcBitMark, kToshibaAcUsualGap, data, nbytes, 38, true, - repeat, 50); -} -#endif // SEND_TOSHIBA_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? -IRToshibaAC::IRToshibaAC(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. -/// @see https://github.com/r45635/HVAC-IR-Control/blob/master/HVAC_ESP8266/HVAC_ESP8266T.ino#L103 -void IRToshibaAC::stateReset(void) { - static const uint8_t kReset[kToshibaACStateLength] = { - 0xF2, 0x0D, 0x03, 0xFC, 0x01}; - memcpy(_.raw, kReset, kToshibaACStateLength); - setTemp(22); // Remote defaults to 22C after factory reset. So do the same. - setSwing(kToshibaAcSwingOff); - _prev_mode = getMode(); -} - -/// Set up hardware to be able to send a message. -void IRToshibaAC::begin(void) { _irsend.begin(); } - -#if SEND_TOSHIBA_AC -/// Send the current internal state as IR messages. -/// @param[in] repeat Nr. of times the message will be repeated. -void IRToshibaAC::send(const uint16_t repeat) { - _backupState(); - _irsend.sendToshibaAC(getRaw(), getStateLength(), repeat); - if (_send_swing && (getStateLength() != kToshibaACStateLengthShort)) { - setStateLength(kToshibaACStateLengthShort); - // Swing settings expect the min temp to be set. - // Ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/1205#issuecomment-653922374 - setTemp(kToshibaAcMinTemp); - setSwing(_swing_mode); - _irsend.sendToshibaAC(getRaw(), getStateLength(), repeat); - _restoreState(); - } - _send_swing = false; -} -#endif // SEND_TOSHIBA_AC - -/// Get the length of the supplied Toshiba state per it's protocol structure. -/// @param[in] state The array to get the built-in length from. -/// @param[in] size The physical size of the state array. -/// @return Nr. of bytes in use for the provided state message. -uint16_t IRToshibaAC::getInternalStateLength(const uint8_t state[], - const uint16_t size) { - if (size < kToshibaAcLengthByte) return 0; - return std::min((uint16_t)(state[kToshibaAcLengthByte] + kToshibaAcMinLength), - kToshibaACStateLengthLong); -} - -/// Get the length of the current internal state per the protocol structure. -/// @return Nr. of bytes in use for the current internal state message. -uint16_t IRToshibaAC::getStateLength(void) const { - return getInternalStateLength(_.raw, kToshibaACStateLengthLong); -} - -/// Set the internal length of the current internal state per the protocol. -/// @param[in] size Nr. of bytes in use for the current internal state message. -void IRToshibaAC::setStateLength(const uint16_t size) { - if (size < kToshibaAcMinLength) return; - _.Length = size - kToshibaAcMinLength; -} - -/// Make a copy of the internal code-form A/C state. -void IRToshibaAC::_backupState(void) { - memcpy(backup, _.raw, kToshibaACStateLengthLong); -} - -/// Recover the internal code-form A/C state from the backup. -void IRToshibaAC::_restoreState(void) { - memcpy(_.raw, backup, kToshibaACStateLengthLong); -} - -/// Get a PTR to the internal state/code for this protocol with all integrity -/// checks passing. -/// @return PTR to a code for this protocol based on the current internal state. -uint8_t* IRToshibaAC::getRaw(void) { - checksum(getStateLength()); - return _.raw; -} - -/// Set the internal state from a valid code for this protocol. -/// @param[in] newState A valid code for this protocol. -/// @param[in] length The length/size of the array. -void IRToshibaAC::setRaw(const uint8_t newState[], const uint16_t length) { - memcpy(_.raw, newState, length); - _prev_mode = getMode(); - _send_swing = true; -} - -/// 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 IRToshibaAC::calcChecksum(const uint8_t state[], - const uint16_t length) { - return length ? xorBytes(state, length - 1) : 0; -} - -/// 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 IRToshibaAC::validChecksum(const uint8_t state[], const uint16_t length) { - return length >= kToshibaAcMinLength && - state[length - 1] == IRToshibaAC::calcChecksum(state, length) && - checkInvertedBytePairs(state, kToshibaAcInvertedLength) && - IRToshibaAC::getInternalStateLength(state, length) == length; -} - -/// Calculate & set the checksum for the current internal state of the remote. -/// @param[in] length The length/size of the internal array to checksum. -void IRToshibaAC::checksum(const uint16_t length) { - // Stored the checksum value in the last byte. - if (length >= kToshibaAcMinLength) { - // Set/clear the short msg bit. - _.ShortMsg = (getStateLength() == kToshibaACStateLengthShort); - // Set/clear the long msg bit. - _.LongMsg = (getStateLength() == kToshibaACStateLengthLong); - invertBytePairs(_.raw, kToshibaAcInvertedLength); - // Always do the Xor checksum LAST! - _.raw[length - 1] = calcChecksum(_.raw, length); - } -} - -/// Set the requested power state of the A/C to on. -void IRToshibaAC::on(void) { setPower(true); } - -/// Set the requested power state of the A/C to off. -void IRToshibaAC::off(void) { setPower(false); } - -/// Change the power setting. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRToshibaAC::setPower(const bool on) { - if (on) { // On - // If not already on, pick the last non-off mode used - if (!getPower()) setMode(_prev_mode); - } else { // Off - setMode(kToshibaAcOff); - } -} - -/// Get the value of the current power setting. -/// @return true, the setting is on. false, the setting is off. -bool IRToshibaAC::getPower(void) const { - return getMode(true) != kToshibaAcOff; -} - -/// Set the temperature. -/// @param[in] degrees The temperature in degrees celsius. -void IRToshibaAC::setTemp(const uint8_t degrees) { - uint8_t temp = std::max(kToshibaAcMinTemp, degrees); - temp = std::min(kToshibaAcMaxTemp, temp); - _.Temp = temp - kToshibaAcMinTemp; -} - -/// Get the current temperature setting. -/// @return The current setting for temp. in degrees celsius. -uint8_t IRToshibaAC::getTemp(void) const { - return _.Temp + kToshibaAcMinTemp; -} - -/// Set the speed of the fan. -/// @param[in] speed The desired setting (0 is Auto, 1-5 is the speed, 5 is Max) -void IRToshibaAC::setFan(const uint8_t speed) { - uint8_t fan = speed; - // Bounds check - if (fan > kToshibaAcFanMax) - fan = kToshibaAcFanMax; // Set the fan to maximum if out of range. - if (fan > kToshibaAcFanAuto) fan++; - _.Fan = fan; -} - -/// Get the current fan speed setting. -/// @return The current fan speed/mode. -uint8_t IRToshibaAC::getFan(void) const { - uint8_t fan = _.Fan; - if (fan == kToshibaAcFanAuto) return kToshibaAcFanAuto; - return --fan; -} - -/// Get the swing setting of the A/C. -/// @param[in] raw Calculate the answer from just the state data. -/// @return The current swing mode setting. -uint8_t IRToshibaAC::getSwing(const bool raw) const { - return raw ? _.Swing : _swing_mode; -} - -/// Set the swing setting of the A/C. -/// @param[in] setting The value of the desired setting. -void IRToshibaAC::setSwing(const uint8_t setting) { - switch (setting) { - case kToshibaAcSwingStep: - case kToshibaAcSwingOn: - case kToshibaAcSwingOff: - case kToshibaAcSwingToggle: - _send_swing = true; - _swing_mode = setting; - if (getStateLength() == kToshibaACStateLengthShort) - _.Swing = setting; - } -} - -/// Get the operating mode setting of the A/C. -/// @param[in] raw Get the value without any intelligent processing. -/// @return The current operating mode setting. -uint8_t IRToshibaAC::getMode(const bool raw) const { - const uint8_t mode = _.Mode; - if (raw) return mode; - switch (mode) { - case kToshibaAcOff: return _prev_mode; - default: return mode; - } -} - -/// Set the operating mode of the A/C. -/// @param[in] mode The desired operating mode. -/// @note If we get an unexpected mode, default to AUTO. -/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1205#issuecomment-654446771 -void IRToshibaAC::setMode(const uint8_t mode) { - if (mode != _prev_mode) - // Changing mode or power turns Econo & Turbo to off on a real remote. - // Setting the internal message length to "normal" will do that. - setStateLength(kToshibaACStateLength); - switch (mode) { - case kToshibaAcAuto: - case kToshibaAcCool: - case kToshibaAcDry: - case kToshibaAcHeat: - case kToshibaAcFan: - _prev_mode = mode; - // FALL-THRU - case kToshibaAcOff: - _.Mode = mode; - break; - default: - _prev_mode = kToshibaAcAuto; - _.Mode = kToshibaAcAuto; - } -} - -/// Get the Turbo (Powerful) setting of the A/C. -/// @return true, if the current setting is on. Otherwise, false. -bool IRToshibaAC::getTurbo(void) const { - if (getStateLength() == kToshibaACStateLengthLong) - return _.EcoTurbo == kToshibaAcTurboOn; - return false; -} - -/// Set the Turbo (Powerful) setting of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -/// Note: Turbo mode is mutually exclusive with Economy mode. -void IRToshibaAC::setTurbo(const bool on) { - if (on) { - _.EcoTurbo = kToshibaAcTurboOn; - setStateLength(kToshibaACStateLengthLong); - } else { - if (!getEcono()) setStateLength(kToshibaACStateLength); - } -} - -/// Get the Economy mode setting of the A/C. -/// @return true, if the current setting is on. Otherwise, false. -bool IRToshibaAC::getEcono(void) const { - if (getStateLength() == kToshibaACStateLengthLong) - return _.EcoTurbo == kToshibaAcEconoOn; - return false; -} - -/// Set the Economy mode setting of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -/// Note: Economy mode is mutually exclusive with Turbo mode. -void IRToshibaAC::setEcono(const bool on) { - if (on) { - _.EcoTurbo = kToshibaAcEconoOn; - setStateLength(kToshibaACStateLengthLong); - } else { - if (!getTurbo()) setStateLength(kToshibaACStateLength); - } -} - -/// 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 IRToshibaAC::convertMode(const stdAc::opmode_t mode) { - switch (mode) { - case stdAc::opmode_t::kCool: return kToshibaAcCool; - case stdAc::opmode_t::kHeat: return kToshibaAcHeat; - case stdAc::opmode_t::kDry: return kToshibaAcDry; - case stdAc::opmode_t::kFan: return kToshibaAcFan; - case stdAc::opmode_t::kOff: return kToshibaAcOff; - default: return kToshibaAcAuto; - } -} - -/// 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 IRToshibaAC::convertFan(const stdAc::fanspeed_t speed) { - switch (speed) { - case stdAc::fanspeed_t::kMin: return kToshibaAcFanMax - 4; - case stdAc::fanspeed_t::kLow: return kToshibaAcFanMax - 3; - case stdAc::fanspeed_t::kMedium: return kToshibaAcFanMax - 2; - case stdAc::fanspeed_t::kHigh: return kToshibaAcFanMax - 1; - case stdAc::fanspeed_t::kMax: return kToshibaAcFanMax; - default: return kToshibaAcFanAuto; - } -} - -/// 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 IRToshibaAC::toCommonMode(const uint8_t mode) { - switch (mode) { - case kToshibaAcCool: return stdAc::opmode_t::kCool; - case kToshibaAcHeat: return stdAc::opmode_t::kHeat; - case kToshibaAcDry: return stdAc::opmode_t::kDry; - case kToshibaAcFan: return stdAc::opmode_t::kFan; - case kToshibaAcOff: return stdAc::opmode_t::kOff; - default: return stdAc::opmode_t::kAuto; - } -} - -/// Convert a native fan speed into its stdAc equivalent. -/// @param[in] spd The native setting to be converted. -/// @return The stdAc equivalent of the native setting. -stdAc::fanspeed_t IRToshibaAC::toCommonFanSpeed(const uint8_t spd) { - switch (spd) { - case kToshibaAcFanMax: return stdAc::fanspeed_t::kMax; - case kToshibaAcFanMax - 1: return stdAc::fanspeed_t::kHigh; - case kToshibaAcFanMax - 2: return stdAc::fanspeed_t::kMedium; - case kToshibaAcFanMax - 3: return stdAc::fanspeed_t::kLow; - case kToshibaAcFanMax - 4: 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 IRToshibaAC::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.protocol = decode_type_t::TOSHIBA_AC; - result.model = -1; // Not supported. - // Do we have enough current state info to override any previous state? - // i.e. Was the class just setRaw()'ed with a short "swing" message. - // This should enables us to also ignore the Swing msg's special 17C setting. - if (getStateLength() != kToshibaACStateLengthShort) { - result.power = getPower(); - result.mode = toCommonMode(getMode()); - result.celsius = true; - result.degrees = getTemp(); - result.fanspeed = toCommonFanSpeed(getFan()); - result.turbo = getTurbo(); - result.econo = getEcono(); - } - switch (getSwing()) { - case kToshibaAcSwingOn: - result.swingv = stdAc::swingv_t::kAuto; - break; - case kToshibaAcSwingToggle: - if (prev->swingv != stdAc::swingv_t::kOff) - result.swingv = stdAc::swingv_t::kOff; - else - result.swingv = stdAc::swingv_t::kAuto; - break; - default: result.swingv = stdAc::swingv_t::kOff; - } - // Not supported. - result.light = false; - result.filter = false; - result.swingh = stdAc::swingh_t::kOff; - 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 IRToshibaAC::toString(void) const { - String result = ""; - result.reserve(80); - result += addTempToString(getTemp(), true, false); - switch (getStateLength()) { - case kToshibaACStateLengthShort: - result += addIntToString(getSwing(true), kSwingVStr); - result += kSpaceLBraceStr; - switch (getSwing(true)) { - case kToshibaAcSwingOff: result += kOffStr; break; - case kToshibaAcSwingOn: result += kOnStr; break; - case kToshibaAcSwingStep: result += kStepStr; break; - case kToshibaAcSwingToggle: result += kToggleStr; break; - default: result += kUnknownStr; - } - result += ')'; - break; - case kToshibaACStateLengthLong: - case kToshibaACStateLength: - default: - result += addBoolToString(getPower(), kPowerStr); - if (getPower()) - result += addModeToString(getMode(), kToshibaAcAuto, kToshibaAcCool, - kToshibaAcHeat, kToshibaAcDry, kToshibaAcFan); - result += addFanToString(getFan(), kToshibaAcFanMax, kToshibaAcFanMin, - kToshibaAcFanAuto, kToshibaAcFanAuto, - kToshibaAcFanMed); - result += addBoolToString(getTurbo(), kTurboStr); - result += addBoolToString(getEcono(), kEconoStr); - } - return result; -} - -#if DECODE_TOSHIBA_AC -/// Decode the supplied Toshiba A/C 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::decodeToshibaAC(decode_results* results, uint16_t offset, - const uint16_t nbits, const bool strict) { - // Compliance - if (strict) { - switch (nbits) { // Must be called with the correct nr. of bits. - case kToshibaACBits: - case kToshibaACBitsShort: - case kToshibaACBitsLong: - break; - default: - return false; - } - } - - // Match Header + Data + Footer - if (!matchGeneric(results->rawbuf + offset, results->state, - results->rawlen - offset, nbits, - kToshibaAcHdrMark, kToshibaAcHdrSpace, - kToshibaAcBitMark, kToshibaAcOneSpace, - kToshibaAcBitMark, kToshibaAcZeroSpace, - kToshibaAcBitMark, kToshibaAcMinGap, true, - _tolerance, kMarkExcess)) return false; - // Compliance - if (strict) { - // Check that the checksum of the message is correct. - if (!IRToshibaAC::validChecksum(results->state, nbits / 8)) return false; - } - - // Success - results->decode_type = TOSHIBA_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_TOSHIBA_AC diff --git a/lib/IRremoteESP8266/src/ir_Toshiba.h b/lib/IRremoteESP8266/src/ir_Toshiba.h index 3ebf5e6938..d756dffdce 100644 --- a/lib/IRremoteESP8266/src/ir_Toshiba.h +++ b/lib/IRremoteESP8266/src/ir_Toshiba.h @@ -31,10 +31,10 @@ #ifdef ARDUINO #include #endif -#include "IRremoteESP8266.h" -#include "IRsend.h" +#include "../src/IRremoteESP8266.h" +#include "../src/IRsend.h" #ifdef UNIT_TEST -#include "IRsend_test.h" +#include "../src/IRsend_test.h" #endif /// Native representation of a Toshiba A/C message. diff --git a/lib/IRremoteESP8266/src/ir_Transcold.cpp b/lib/IRremoteESP8266/src/ir_Transcold.cpp deleted file mode 100644 index 14cc7f8bd2..0000000000 --- a/lib/IRremoteESP8266/src/ir_Transcold.cpp +++ /dev/null @@ -1,500 +0,0 @@ -// Copyright 2020 Chandrashekar Shetty (iamDshetty) -// Copyright 2020 crankyoldgit -// Copyright 2021 siriuslzx - -/// @file -/// @brief Support for Transcold A/C protocols. -/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1256 - -#include "ir_Transcold.h" -#include -#ifndef ARDUINO -#include -#endif -#include "IRrecv.h" -#include "IRsend.h" -#include "IRtext.h" -#include "IRutils.h" - -// Constants - -const uint16_t kTranscoldHdrMark = 5944; ///< uSeconds. -const uint16_t kTranscoldBitMark = 555; ///< uSeconds. -const uint16_t kTranscoldHdrSpace = 7563; ///< uSeconds. -const uint16_t kTranscoldOneSpace = 3556; ///< uSeconds. -const uint16_t kTranscoldZeroSpace = 1526; ///< uSeconds. - -using irutils::addBoolToString; -using irutils::addIntToString; -using irutils::addLabeledString; -using irutils::addModeToString; -using irutils::addTempToString; -using irutils::addToggleToString; - -#if SEND_TRANSCOLD -/// Send a Transcold 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. -void IRsend::sendTranscold(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(kTranscoldHdrMark); - space(kTranscoldHdrSpace); - // 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; - uint8_t segment = GETBITS64(data, nbits - i, 8); - // Normal + Inverted - uint16_t both = (segment << 8) | (~segment & 0xFF); - sendData(kTranscoldBitMark, kTranscoldOneSpace, kTranscoldBitMark, - kTranscoldZeroSpace, both, 16, true); - } - // Footer - mark(kTranscoldBitMark); - space(kTranscoldHdrSpace); - mark(kTranscoldBitMark); - space(kDefaultMessageGap); - } -} -#endif // SEND_TRANSCOLD - -/// 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? -IRTranscoldAc::IRTranscoldAc(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 IRTranscoldAc::stateReset(void) { - setRaw(kTranscoldKnownGoodState); - special_state = kTranscoldOff; - swingFlag = false; - swingHFlag = false; - swingVFlag = false; -} - -/// Set up hardware to be able to send a message. -void IRTranscoldAc::begin(void) { _irsend.begin(); } - -#if SEND_TRANSCOLD -/// Send the current internal state as an IR message. -/// @param[in] repeat Nr. of times the message will be repeated. -void IRTranscoldAc::send(uint16_t repeat) { - _irsend.sendTranscold(getRaw(), kTranscoldBits, repeat); - if (isSpecialState()) { - // make sure to remove special state from special_state - // after command has being transmitted. - special_state = kTranscoldKnownGoodState; - } -} -#endif // SEND_TRANSCOLD - -/// 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 IRTranscoldAc::getRaw(void) const { - if (isSpecialState()) { - return special_state; - } - return _.raw; - } - -/// Set the internal state from a valid code for this protocol. -/// @param[in] new_code A valid code for this protocol. -void IRTranscoldAc::setRaw(const uint32_t new_code) { - if (handleSpecialState(new_code)) { - special_state = new_code; - _.raw = kTranscoldKnownGoodState; - } else { - // must be a command changing Temp|Mode|Fan - // it is safe to just copy to remote var - _.raw = new_code; - special_state = kTranscoldKnownGoodState; - // it isn`t special so might affect Temp|mode|Fan - if (new_code == kTranscoldCmdFan) { - setMode(kTranscoldFan); - } - } -} - -/// Is the current state is a special state? -/// @return true, if it is. false if it isn't. -bool IRTranscoldAc::isSpecialState(void) const { - switch (special_state) { - case kTranscoldOff: - case kTranscoldSwing: 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 -/// @return true, if it is a special state. false if it isn't. -bool IRTranscoldAc::handleSpecialState(const uint32_t data) { - switch (data) { - case kTranscoldOff: - break; - case kTranscoldSwing: - swingFlag = !swingFlag; - break; - default: - return false; - } - return true; -} - -/// Set the temperature. -/// @param[in] desired The temperature in degrees celsius. -void IRTranscoldAc::setTemp(const uint8_t desired) { - // Range check. - uint8_t temp = std::min(desired, kTranscoldTempMax); - temp = std::max(temp, kTranscoldTempMin) - kTranscoldTempMin + 1; - _.Temp = reverseBits(invertBits(temp, kTranscoldTempSize), - kTranscoldTempSize); -} - -/// Get the current temperature setting. -/// @return The current setting for temp. in degrees celsius. -uint8_t IRTranscoldAc::getTemp(void) const { - return reverseBits(invertBits(_.Temp, kTranscoldTempSize), - kTranscoldTempSize) + kTranscoldTempMin - 1; -} - -/// Get the value of the current power setting. -/// @return true, the setting is on. false, the setting is off. -bool IRTranscoldAc::getPower(void) const { - // There is only an off state. Everything else is "on". - return special_state != kTranscoldOff; -} - -/// Change the power setting. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRTranscoldAc::setPower(const bool on) { - if (!on) { - special_state = kTranscoldOff; - } else { - special_state = kTranscoldKnownGoodState; - } -} - -/// Change the power setting to On. -void IRTranscoldAc::on(void) { setPower(true); } - -/// Change the power setting to Off. -void IRTranscoldAc::off(void) { setPower(false); } - -/// Get the Swing setting of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRTranscoldAc::getSwing(void) const { return swingFlag; } - -/// Toggle the Swing mode of the A/C. -void IRTranscoldAc::setSwing(void) { - // Assumes that repeated sending "swing" toggles the action on the device. - // if not, the variable "swingFlag" can be removed. - special_state = kTranscoldSwing; - swingFlag = !swingFlag; -} - -/// Set the operating mode of the A/C. -/// @param[in] mode The desired operating mode. -void IRTranscoldAc::setMode(const uint8_t mode) { - uint32_t actualmode = mode; - switch (actualmode) { - case kTranscoldAuto: - case kTranscoldDry: - _.Fan = kTranscoldFanAuto0; - break; - case kTranscoldCool: - case kTranscoldHeat: - case kTranscoldFan: - _.Fan = kTranscoldFanAuto; - break; - default: // Anything else, go with Auto mode. - actualmode = kTranscoldAuto; - _.Fan = kTranscoldFanAuto0; - } - setTemp(getTemp()); - // Fan mode is a special case of Dry. - if (actualmode == kTranscoldFan) { - actualmode = kTranscoldDry; - _.Temp = kTranscoldFanTempCode; - } - _.Mode = actualmode; -} - -/// Get the operating mode setting of the A/C. -/// @return The current operating mode setting. -uint8_t IRTranscoldAc::getMode(void) const { - uint8_t mode = _.Mode; - if (mode == kTranscoldDry) - if (_.Temp == kTranscoldFanTempCode) return kTranscoldFan; - return mode; -} - -/// Get the current fan speed setting. -/// @return The current fan speed. -uint8_t IRTranscoldAc::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 IRTranscoldAc::setFan(const uint8_t speed, const bool modecheck) { - uint8_t newspeed = speed; - if (modecheck) { - switch (getMode()) { - case kTranscoldAuto: - case kTranscoldDry: // Dry & Auto mode can't have speed Auto. - if (speed == kTranscoldFanAuto) - newspeed = kTranscoldFanAuto0; - break; - default: // Only Dry & Auto mode can have speed Auto0. - if (speed == kTranscoldFanAuto0) - newspeed = kTranscoldFanAuto; - } - } - switch (speed) { - case kTranscoldFanAuto: - case kTranscoldFanAuto0: - case kTranscoldFanMin: - case kTranscoldFanMed: - case kTranscoldFanMax: - case kTranscoldFanZoneFollow: - case kTranscoldFanFixed: - break; - default: // Unknown speed requested. - newspeed = kTranscoldFanAuto; - break; - } - _.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 IRTranscoldAc::convertMode(const stdAc::opmode_t mode) { - switch (mode) { - case stdAc::opmode_t::kCool: return kTranscoldCool; - case stdAc::opmode_t::kHeat: return kTranscoldHeat; - case stdAc::opmode_t::kDry: return kTranscoldDry; - case stdAc::opmode_t::kFan: return kTranscoldFan; - default: return kTranscoldAuto; - } -} - -/// 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 IRTranscoldAc::convertFan(const stdAc::fanspeed_t speed) { - switch (speed) { - case stdAc::fanspeed_t::kMin: - case stdAc::fanspeed_t::kLow: return kTranscoldFanMin; - case stdAc::fanspeed_t::kMedium: return kTranscoldFanMed; - case stdAc::fanspeed_t::kHigh: - case stdAc::fanspeed_t::kMax: return kTranscoldFanMax; - default: return kTranscoldFanAuto; - } -} - -/// 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 IRTranscoldAc::toCommonMode(const uint8_t mode) { - switch (mode) { - case kTranscoldCool: return stdAc::opmode_t::kCool; - case kTranscoldHeat: return stdAc::opmode_t::kHeat; - case kTranscoldDry: return stdAc::opmode_t::kDry; - case kTranscoldFan: 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 IRTranscoldAc::toCommonFanSpeed(const uint8_t speed) { - switch (speed) { - case kTranscoldFanMax: return stdAc::fanspeed_t::kMax; - case kTranscoldFanMed: return stdAc::fanspeed_t::kMedium; - case kTranscoldFanMin: 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 IRTranscoldAc::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; - } - // Not supported. - result.model = -1; // No models used. - result.swingh = stdAc::swingh_t::kOff; - result.turbo = false; - result.clean = false; - result.light = false; - result.quiet = false; - result.econo = false; - result.filter = false; - result.beep = false; - result.clock = -1; - result.sleep = -1; - - // Supported. - result.protocol = decode_type_t::TRANSCOLD; - 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; - } - // Back to "normal" stateful messages. - result.mode = toCommonMode(getMode()); - result.degrees = getTemp(); - result.fanspeed = toCommonFanSpeed(_.Fan); - return result; -} - -/// Convert the internal state into a human readable string. -/// @return The current internal state expressed as a human readable String. -String IRTranscoldAc::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. - // Special modes. - if (getSwing()) return result + addToggleToString(true, kSwingStr); - result += addModeToString(getMode(), kTranscoldAuto, kTranscoldCool, - kTranscoldHeat, kTranscoldDry, kTranscoldFan); - result += addIntToString(_.Fan, kFanStr); - result += kSpaceLBraceStr; - switch (_.Fan) { - case kTranscoldFanAuto: - result += kAutoStr; - break; - case kTranscoldFanAuto0: - result += kAutoStr; - result += '0'; - break; - case kTranscoldFanMax: - result += kMaxStr; - break; - case kTranscoldFanMin: - result += kMinStr; - break; - case kTranscoldFanMed: - result += kMedStr; - break; - case kTranscoldFanZoneFollow: - result += kZoneFollowStr; - break; - case kTranscoldFanFixed: - result += kFixedStr; - break; - default: - result += kUnknownStr; - } - result += ')'; - // Fan mode doesn't have a temperature. - if (getMode() != kTranscoldFan) result += addTempToString(getTemp()); - return result; -} - -#if DECODE_TRANSCOLD -/// Decode the supplied Transcold 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::decodeTranscold(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; - if (strict && nbits != kTranscoldBits) return false; - if (nbits % 8 != 0) return false; - - uint64_t data = 0; - uint64_t inverted = 0; - - if (nbits > sizeof(data) * 8) - return false; // We can't possibly capture a Transcold packet that big. - - // Header - if (!matchMark(results->rawbuf[offset++], kTranscoldHdrMark)) return false; - if (!matchSpace(results->rawbuf[offset++], kTranscoldHdrSpace)) return false; - - // Data - // Twice as many bits as there are normal plus inverted bits. - for (uint16_t i = 0; i < nbits * 2; i++, offset++) { - bool flip = (i / 8) % 2; - if (!matchMark(results->rawbuf[offset++], kTranscoldBitMark)) - return false; - if (matchSpace(results->rawbuf[offset], kTranscoldOneSpace)) { - if (flip) - inverted = (inverted << 1) | 1; - else - data = (data << 1) | 1; - } else if (matchSpace(results->rawbuf[offset], kTranscoldZeroSpace)) { - if (flip) - inverted <<= 1; - else - data <<= 1; - } else { - return false; - } - } - - // Footer - if (!matchMark(results->rawbuf[offset++], kTranscoldBitMark)) return false; - if (!matchSpace(results->rawbuf[offset++], kTranscoldHdrSpace)) return false; - if (!matchMark(results->rawbuf[offset++], kTranscoldBitMark)) return false; - if (offset < results->rawlen && - !matchAtLeast(results->rawbuf[offset], kDefaultMessageGap)) - return false; - - // Compliance - if (strict && inverted != invertBits(data, nbits)) return false; - - // Success - results->decode_type = decode_type_t::TRANSCOLD; - results->bits = nbits; - results->value = data; - results->address = 0; - results->command = 0; - return true; -} -#endif // DECODE_TRANSCOLD diff --git a/lib/IRremoteESP8266/src/ir_Transcold.h b/lib/IRremoteESP8266/src/ir_Transcold.h index 8fd924fbaf..bedb9611d5 100644 --- a/lib/IRremoteESP8266/src/ir_Transcold.h +++ b/lib/IRremoteESP8266/src/ir_Transcold.h @@ -63,10 +63,10 @@ temp 16 Auto cool close (right) 11101111000100000110011110011000010101001010101 #ifndef UNIT_TEST #include #endif -#include "IRremoteESP8266.h" -#include "IRsend.h" +#include "../src/IRremoteESP8266.h" +#include "../src/IRsend.h" #ifdef UNIT_TEST -#include "IRsend_test.h" +#include "../src/IRsend_test.h" #endif /// Native representation of a Transcold A/C message. diff --git a/lib/IRremoteESP8266/src/ir_Trotec.cpp b/lib/IRremoteESP8266/src/ir_Trotec.cpp deleted file mode 100644 index 7cd6fff5e6..0000000000 --- a/lib/IRremoteESP8266/src/ir_Trotec.cpp +++ /dev/null @@ -1,642 +0,0 @@ -// Copyright 2017 stufisher -// Copyright 2019 crankyoldgit - -/// @file -/// @brief Support for Trotec protocols. -/// @see https://github.com/crankyoldgit/IRremoteESP8266/pull/279 -/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1176 - -#include "ir_Trotec.h" -#include -#include -#ifndef UNIT_TEST -#include -#endif -#include "IRremoteESP8266.h" -#include "IRtext.h" -#include "IRutils.h" - -// Constants -const uint16_t kTrotecHdrMark = 5952; -const uint16_t kTrotecHdrSpace = 7364; -const uint16_t kTrotecBitMark = 592; -const uint16_t kTrotecOneSpace = 1560; -const uint16_t kTrotecZeroSpace = 592; -const uint16_t kTrotecGap = 6184; -const uint16_t kTrotecGapEnd = 1500; // made up value - -const uint16_t kTrotec3550HdrMark = 12000; -const uint16_t kTrotec3550HdrSpace = 5130; -const uint16_t kTrotec3550BitMark = 550; -const uint16_t kTrotec3550OneSpace = 1950; -const uint16_t kTrotec3550ZeroSpace = 500; - -const uint16_t kTrotec3550TimerMax = 8 * 60; ///< 8 hours in Minutes. - -using irutils::addBoolToString; -using irutils::addFanToString; -using irutils::addIntToString; -using irutils::addLabeledString; -using irutils::addModeToString; -using irutils::addTempToString; -using irutils::minsToString; - -#if SEND_TROTEC -/// Send a Trotec message. -/// 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::sendTrotec(const unsigned char data[], const uint16_t nbytes, - const uint16_t repeat) { - if (nbytes < kTrotecStateLength) return; - - enableIROut(36); - for (uint16_t r = 0; r <= repeat; r++) { - sendGeneric(kTrotecHdrMark, kTrotecHdrSpace, kTrotecBitMark, - kTrotecOneSpace, kTrotecBitMark, kTrotecZeroSpace, - kTrotecBitMark, kTrotecGap, data, nbytes, 36, false, - 0, // Repeats handled elsewhere - 50); - // More footer - mark(kTrotecBitMark); - space(kTrotecGapEnd); - } -} -#endif // SEND_TROTEC - -/// 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? -IRTrotecESP::IRTrotecESP(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 IRTrotecESP::begin(void) { _irsend.begin(); } - -#if SEND_TROTEC -/// Send the current internal state as an IR message. -/// @param[in] repeat Nr. of times the message will be repeated. -void IRTrotecESP::send(const uint16_t repeat) { - _irsend.sendTrotec(getRaw(), kTrotecStateLength, repeat); -} -#endif // SEND_TROTEC - -/// 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 IRTrotecESP::calcChecksum(const uint8_t state[], - const uint16_t length) { - return sumBytes(state + 2, length - 3); -} - -/// 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 IRTrotecESP::validChecksum(const uint8_t state[], const uint16_t length) { - return state[length - 1] == calcChecksum(state, length); -} - -/// Calculate & set the checksum for the current internal state of the remote. -void IRTrotecESP::checksum(void) { - _.Sum = sumBytes(_.raw + 2, kTrotecStateLength - 3); -} - -/// Reset the state of the remote to a known good state/sequence. -void IRTrotecESP::stateReset(void) { - for (uint8_t i = 2; i < kTrotecStateLength; i++) _.raw[i] = 0x0; - - _.Intro1 = kTrotecIntro1; - _.Intro2 = kTrotecIntro2; - - _.Power = false; - setTemp(kTrotecDefTemp); - _.Fan = kTrotecFanMed; - _.Mode = kTrotecAuto; -} - -/// 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* IRTrotecESP::getRaw(void) { - checksum(); - return _.raw; -} - -/// Set the internal state from a valid code for this protocol. -/// @param[in] state A valid code for this protocol. -void IRTrotecESP::setRaw(const uint8_t state[]) { - memcpy(_.raw, state, kTrotecStateLength); -} - -/// Set the requested power state of the A/C to on. -void IRTrotecESP::on(void) { setPower(true); } - -/// Set the requested power state of the A/C to off. -void IRTrotecESP::off(void) { setPower(false); } - -/// Change the power setting. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRTrotecESP::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 IRTrotecESP::getPower(void) const { - return _.Power; -} - -/// Set the speed of the fan. -/// @param[in] fan The desired setting. -void IRTrotecESP::setSpeed(const uint8_t fan) { - uint8_t speed = std::min(fan, kTrotecFanHigh); - _.Fan = speed; -} - -/// Get the current fan speed setting. -/// @return The current fan speed/mode. -uint8_t IRTrotecESP::getSpeed(void) const { - return _.Fan; -} - -/// Set the operating mode of the A/C. -/// @param[in] mode The desired operating mode. -void IRTrotecESP::setMode(const uint8_t mode) { - _.Mode = (mode > kTrotecFan) ? kTrotecAuto : mode; -} - -/// Get the operating mode setting of the A/C. -/// @return The current operating mode setting. -uint8_t IRTrotecESP::getMode(void) const { - return _.Mode; -} - -/// Set the temperature. -/// @param[in] celsius The temperature in degrees celsius. -void IRTrotecESP::setTemp(const uint8_t celsius) { - uint8_t temp = std::max(celsius, kTrotecMinTemp); - temp = std::min(temp, kTrotecMaxTemp); - _.Temp = temp - kTrotecMinTemp; -} - -/// Get the current temperature setting. -/// @return The current setting for temp. in degrees celsius. -uint8_t IRTrotecESP::getTemp(void) const { - return _.Temp + kTrotecMinTemp; -} - -/// Set the Sleep setting of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRTrotecESP::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 IRTrotecESP::getSleep(void) const { - return _.Sleep; -} - -/// Set the timer time in nr. of Hours. -/// @param[in] timer Nr. of Hours. Max is `kTrotecMaxTimer` -void IRTrotecESP::setTimer(const uint8_t timer) { - _.Timer = timer; - _.Hours = (timer > kTrotecMaxTimer) ? kTrotecMaxTimer : timer; -} - -/// Get the timer time in nr. of Hours. -/// @return Nr. of Hours. -uint8_t IRTrotecESP::getTimer(void) const { return _.Hours; } - -/// 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 IRTrotecESP::convertMode(const stdAc::opmode_t mode) { - switch (mode) { - case stdAc::opmode_t::kCool: return kTrotecCool; - case stdAc::opmode_t::kDry: return kTrotecDry; - case stdAc::opmode_t::kFan: return kTrotecFan; - // Note: No Heat mode. - default: return kTrotecAuto; - } -} - -/// 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 IRTrotecESP::convertFan(const stdAc::fanspeed_t speed) { - switch (speed) { - case stdAc::fanspeed_t::kMin: - case stdAc::fanspeed_t::kLow: return kTrotecFanLow; - case stdAc::fanspeed_t::kMedium: return kTrotecFanMed; - case stdAc::fanspeed_t::kHigh: - case stdAc::fanspeed_t::kMax: return kTrotecFanHigh; - default: return kTrotecFanMed; - } -} - -/// 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 IRTrotecESP::toCommonMode(const uint8_t mode) { - switch (mode) { - case kTrotecCool: return stdAc::opmode_t::kCool; - case kTrotecDry: return stdAc::opmode_t::kDry; - case kTrotecFan: return stdAc::opmode_t::kFan; - default: return stdAc::opmode_t::kAuto; - } -} - -/// Convert a native fan speed into its stdAc equivalent. -/// @param[in] spd The native setting to be converted. -/// @return The stdAc equivalent of the native setting. -stdAc::fanspeed_t IRTrotecESP::toCommonFanSpeed(const uint8_t spd) { - switch (spd) { - case kTrotecFanHigh: return stdAc::fanspeed_t::kMax; - case kTrotecFanMed: return stdAc::fanspeed_t::kMedium; - case kTrotecFanLow: 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 IRTrotecESP::toCommon(void) const { - stdAc::state_t result; - result.protocol = decode_type_t::TROTEC; - result.power = _.Power; - result.mode = toCommonMode(_.Mode); - result.celsius = true; - result.degrees = getTemp(); - result.fanspeed = toCommonFanSpeed(_.Fan); - result.sleep = _.Sleep ? 0 : -1; - // Not supported. - result.model = -1; // Not supported. - result.swingv = stdAc::swingv_t::kOff; - result.swingh = stdAc::swingh_t::kOff; - result.turbo = false; - 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 IRTrotecESP::toString(void) const { - String result = ""; - result.reserve(100); // Reserve some heap for the string to reduce fragging. - result += addBoolToString(_.Power, kPowerStr, false); - result += addModeToString(_.Mode, kTrotecAuto, kTrotecCool, kTrotecAuto, - kTrotecDry, kTrotecFan); - result += addTempToString(getTemp()); - result += addFanToString(_.Fan, kTrotecFanHigh, kTrotecFanLow, - kTrotecFanHigh, kTrotecFanHigh, kTrotecFanMed); - result += addBoolToString(_.Sleep, kSleepStr); - return result; -} - -#if DECODE_TROTEC -/// Decode the supplied Trotec message. -/// Status: STABLE / Works. Untested on real devices. -/// @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::decodeTrotec(decode_results *results, uint16_t offset, - const uint16_t nbits, const bool strict) { - if (results->rawlen <= 2 * nbits + kHeader + 2 * kFooter - 1 + offset) - return false; // Can't possibly be a valid Trotec A/C message. - if (strict && nbits != kTrotecBits) return false; - - uint16_t used; - // Header + Data + Footer #1 - used = matchGeneric(results->rawbuf + offset, results->state, - results->rawlen - offset, nbits, - kTrotecHdrMark, kTrotecHdrSpace, - kTrotecBitMark, kTrotecOneSpace, - kTrotecBitMark, kTrotecZeroSpace, - kTrotecBitMark, kTrotecGap, true, - _tolerance, 0, false); - if (used == 0) return false; - offset += used; - - // Footer #2 - if (!matchMark(results->rawbuf[offset++], kTrotecBitMark)) return false; - if (offset <= results->rawlen && - !matchAtLeast(results->rawbuf[offset++], kTrotecGapEnd)) return false; - // Compliance - // Verify we got a valid checksum. - if (strict && !IRTrotecESP::validChecksum(results->state)) return false; - // Success - results->decode_type = TROTEC; - 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_TROTEC - -#if SEND_TROTEC_3550 -/// Send a Trotec 3550 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::sendTrotec3550(const unsigned char data[], const uint16_t nbytes, - const uint16_t repeat) { - sendGeneric(kTrotec3550HdrMark, kTrotec3550HdrSpace, - kTrotec3550BitMark, kTrotec3550OneSpace, - kTrotec3550BitMark, kTrotec3550ZeroSpace, - kTrotec3550BitMark, kDefaultMessageGap, - data, nbytes, 38, true, repeat, kDutyDefault); -} -#endif // SEND_TROTEC_3550 - -#if DECODE_TROTEC_3550 -/// Decode the supplied Trotec 3550 message. -/// Status: STABLE / Known 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::decodeTrotec3550(decode_results *results, uint16_t offset, - const uint16_t nbits, const bool strict) { - if (strict && nbits != kTrotecBits) return false; - - // Header + Data + Footer - if (!matchGeneric(results->rawbuf + offset, results->state, - results->rawlen - offset, nbits, - kTrotec3550HdrMark, kTrotec3550HdrSpace, - kTrotec3550BitMark, kTrotec3550OneSpace, - kTrotec3550BitMark, kTrotec3550ZeroSpace, - kTrotec3550BitMark, kDefaultMessageGap)) return false; - // Compliance - if (strict && !IRTrotec3550::validChecksum(results->state, nbits / 8)) - return false; - // Success - results->decode_type = TROTEC_3550; - 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_TROTEC_3550 - -/// 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? -IRTrotec3550::IRTrotec3550(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 IRTrotec3550::begin(void) { _irsend.begin(); } - -#if SEND_TROTEC_3550 -/// Send the current internal state as an IR message. -/// @param[in] repeat Nr. of times the message will be repeated. -void IRTrotec3550::send(const uint16_t repeat) { - _irsend.sendTrotec3550(getRaw(), kTrotecStateLength, repeat); -} -#endif // SEND_TROTEC_3550 - -/// 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 IRTrotec3550::calcChecksum(const uint8_t state[], - const uint16_t length) { - return length ? sumBytes(state, length - 1) : 0; -} - -/// 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 IRTrotec3550::validChecksum(const uint8_t state[], const uint16_t length) { - return state[length - 1] == calcChecksum(state, length); -} - -/// Calculate & set the checksum for the current internal state of the remote. -void IRTrotec3550::checksum(void) { _.Sum = calcChecksum(_.raw); } - -/// Reset the state of the remote to a known good state/sequence. -void IRTrotec3550::stateReset(void) { - static const uint8_t kReset[kTrotecStateLength] = { - 0x55, 0x60, 0x00, 0x0D, 0x00, 0x00, 0x10, 0x88, 0x5A}; - std::memcpy(_.raw, kReset, kTrotecStateLength); -} - -/// 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* IRTrotec3550::getRaw(void) { - checksum(); - return _.raw; -} - -/// Set the internal state from a valid code for this protocol. -/// @param[in] state A valid code for this protocol. -void IRTrotec3550::setRaw(const uint8_t state[]) { - memcpy(_.raw, state, kTrotecStateLength); -} - -/// Set the requested power state of the A/C to on. -void IRTrotec3550::on(void) { setPower(true); } - -/// Set the requested power state of the A/C to off. -void IRTrotec3550::off(void) { setPower(false); } - -/// Change the power setting. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRTrotec3550::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 IRTrotec3550::getPower(void) const { return _.Power; } - -/// Set the speed of the fan. -/// @param[in] fan The desired setting. -void IRTrotec3550::setFan(const uint8_t fan) { - uint8_t speed = std::min(fan, kTrotecFanHigh); - _.Fan = speed; -} - -/// Get the current fan speed setting. -/// @return The current fan speed/mode. -uint8_t IRTrotec3550::getFan(void) const { return _.Fan; } - -/// Set the operating mode of the A/C. -/// @param[in] mode The desired operating mode. -void IRTrotec3550::setMode(const uint8_t mode) { - _.Mode = (mode > kTrotecFan) ? kTrotecAuto : mode; -} - -/// Get the operating mode setting of the A/C. -/// @return The current operating mode setting. -uint8_t IRTrotec3550::getMode(void) const { return _.Mode; } - -/// Set the temperature. -/// @param[in] degrees The temperature in degrees. -/// @param[in] celsius Use celsius units. True, Celsius; False Fahrenheit. -void IRTrotec3550::setTemp(const uint8_t degrees, const bool celsius) { - setTempUnit(celsius); - uint8_t minTemp = kTrotec3550MinTempC; - uint8_t maxTemp = kTrotec3550MaxTempC; - if (!celsius) { // Fahrenheit? - minTemp = kTrotec3550MinTempF; - maxTemp = kTrotec3550MaxTempF; - } - uint8_t temp = std::max(degrees, minTemp); - temp = std::min(temp, maxTemp); - if (celsius) { - _.TempC = temp - minTemp; - _.TempF = celsiusToFahrenheit(temp) - kTrotec3550MinTempF; - } else { - _.TempF = temp - minTemp; - _.TempC = fahrenheitToCelsius(temp) - kTrotec3550MinTempC; - } -} - -/// Get the current temperature setting. -/// @return The current setting for temp. in degrees. -uint8_t IRTrotec3550::getTemp(void) const { - return getTempUnit() ? _.TempC + kTrotec3550MinTempC - : _.TempF + kTrotec3550MinTempF; -} - -/// Set the temperature unit that the A/C will use.. -/// @param[in] celsius Use celsius units. True, Celsius; False Fahrenheit. -void IRTrotec3550::setTempUnit(const bool celsius) { _.Celsius = celsius; } - -/// Get the current temperature unit setting. -/// @return True, Celsius; False Fahrenheit. -bool IRTrotec3550::getTempUnit(void) const { return _.Celsius; } - -/// Change the Vertical Swing setting. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRTrotec3550::setSwingV(const bool on) { _.SwingV = on; } - -/// Get the value of the current Vertical Swing setting. -/// @return true, the setting is on. false, the setting is off. -bool IRTrotec3550::getSwingV(void) const { return _.SwingV; } - -/// Get the number of minutes of the Timer setting. -/// @return Nr of minutes. -uint16_t IRTrotec3550::getTimer(void) const { return _.TimerHrs * 60; } - -/// Set the number of minutes of the Timer setting. -/// @param[in] mins Nr. of Minutes for the Timer. `0` means disable the timer. -void IRTrotec3550::setTimer(const uint16_t mins) { - _.TimerSet = mins > 0; - _.TimerHrs = (std::min(mins, kTrotec3550TimerMax) / 60); -} - -/// 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 IRTrotec3550::convertMode(const stdAc::opmode_t mode) { - switch (mode) { - case stdAc::opmode_t::kCool: return kTrotecCool; - case stdAc::opmode_t::kDry: return kTrotecDry; - case stdAc::opmode_t::kFan: return kTrotecFan; - // Note: No Heat mode. - default: return kTrotecAuto; - } -} - -/// 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 IRTrotec3550::convertFan(const stdAc::fanspeed_t speed) { - switch (speed) { - case stdAc::fanspeed_t::kMin: - case stdAc::fanspeed_t::kLow: return kTrotecFanLow; - case stdAc::fanspeed_t::kMedium: return kTrotecFanMed; - case stdAc::fanspeed_t::kHigh: - case stdAc::fanspeed_t::kMax: return kTrotecFanHigh; - default: return kTrotecFanMed; - } -} - -/// 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 IRTrotec3550::toCommonMode(const uint8_t mode) { - switch (mode) { - case kTrotecCool: return stdAc::opmode_t::kCool; - case kTrotecDry: return stdAc::opmode_t::kDry; - case kTrotecFan: return stdAc::opmode_t::kFan; - default: return stdAc::opmode_t::kAuto; - } -} - -/// Convert a native fan speed into its stdAc equivalent. -/// @param[in] spd The native setting to be converted. -/// @return The stdAc equivalent of the native setting. -stdAc::fanspeed_t IRTrotec3550::toCommonFanSpeed(const uint8_t spd) { - switch (spd) { - case kTrotecFanHigh: return stdAc::fanspeed_t::kMax; - case kTrotecFanMed: return stdAc::fanspeed_t::kMedium; - case kTrotecFanLow: 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 IRTrotec3550::toCommon(void) const { - stdAc::state_t result; - result.protocol = decode_type_t::TROTEC_3550; - result.power = _.Power; - result.mode = toCommonMode(_.Mode); - result.celsius = getTempUnit(); - result.degrees = getTemp(); - result.fanspeed = toCommonFanSpeed(_.Fan); - result.swingv = _.SwingV ? stdAc::swingv_t::kAuto : stdAc::swingv_t::kOff; - // Not supported. - result.model = -1; - result.swingh = stdAc::swingh_t::kOff; - result.turbo = false; - 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 IRTrotec3550::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, kTrotecAuto, kTrotecCool, kTrotecAuto, - kTrotecDry, kTrotecFan); - result += addTempToString(getTemp(), _.Celsius); - result += addFanToString(_.Fan, kTrotecFanHigh, kTrotecFanLow, - kTrotecFanHigh, kTrotecFanHigh, kTrotecFanMed); - result += addBoolToString(_.SwingV, kSwingVStr); - result += addLabeledString(_.TimerSet ? minsToString(getTimer()) : kOffStr, - kTimerStr); - return result; -} diff --git a/lib/IRremoteESP8266/src/ir_Trotec.h b/lib/IRremoteESP8266/src/ir_Trotec.h index 3381ec3f20..b8586dd77d 100644 --- a/lib/IRremoteESP8266/src/ir_Trotec.h +++ b/lib/IRremoteESP8266/src/ir_Trotec.h @@ -18,10 +18,10 @@ #ifndef UNIT_TEST #include #endif -#include "IRremoteESP8266.h" -#include "IRsend.h" +#include "../src/IRremoteESP8266.h" +#include "../src/IRsend.h" #ifdef UNIT_TEST -#include "IRsend_test.h" +#include "../src/IRsend_test.h" #endif /// Native representation of a Trotec A/C message. diff --git a/lib/IRremoteESP8266/src/ir_Truma.cpp b/lib/IRremoteESP8266/src/ir_Truma.cpp deleted file mode 100644 index 853c640e16..0000000000 --- a/lib/IRremoteESP8266/src/ir_Truma.cpp +++ /dev/null @@ -1,340 +0,0 @@ -// Copyright 2021 David Conran (crankyoldgit) - -/// @file -/// @brief Support for Truma protocol. -/// This protocol uses mark length bit encoding. -/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1440 -/// @see https://docs.google.com/spreadsheets/d/1k-RHu0vSIB6IweiTZSa3Rxy3Z_qPUtqwcqot8uXVO6I/edit?usp=sharing - - -#include "ir_Truma.h" -#include -#include "IRrecv.h" -#include "IRsend.h" -#include "IRtext.h" -#include "IRutils.h" - -using irutils::addBoolToString; -using irutils::addFanToString; -using irutils::addModeToString; -using irutils::addTempToString; - -// Constants - -const uint16_t kTrumaLdrMark = 20200; -const uint16_t kTrumaLdrSpace = 1000; -const uint16_t kTrumaHdrMark = 1800; -const uint16_t kTrumaSpace = 630; -const uint16_t kTrumaOneMark = 600; -const uint16_t kTrumaZeroMark = 1200; -const uint16_t kTrumaFooterMark = kTrumaOneMark; -const uint32_t kTrumaGap = kDefaultMessageGap; // Just a guess. - - -#if SEND_TRUMA -/// Send a Truma formatted message. -/// Status: STABLE / Confirmed 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::sendTruma(const uint64_t data, const uint16_t nbits, - const uint16_t repeat) { - for (uint16_t r = 0; r <= repeat; r++) { - enableIROut(38000); - mark(kTrumaLdrMark); - space(kTrumaLdrSpace); - sendGeneric(kTrumaHdrMark, kTrumaSpace, // Header - kTrumaOneMark, kTrumaSpace, // Data - kTrumaZeroMark, kTrumaSpace, - kTrumaFooterMark, kTrumaGap, // Footer - data, nbits, 38, false, 0, kDutyDefault); - } -} -#endif // SEND_TRUMA - -#if DECODE_TRUMA -/// Decode the supplied Truma message. -/// Status: STABLE / Confirmed working with 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. Typically kTrumaBits. -/// @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::decodeTruma(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 message. - if (strict && nbits != kTrumaBits) - return false; // Not strictly a message. - - // Leader. - if (!matchMark(results->rawbuf[offset++], kTrumaLdrMark)) return false; - if (!matchSpace(results->rawbuf[offset++], kTrumaLdrSpace)) return false; - - uint64_t data = 0; - uint16_t used; - used = matchGeneric(results->rawbuf + offset, &data, - results->rawlen - offset, nbits, - kTrumaHdrMark, kTrumaSpace, - kTrumaOneMark, kTrumaSpace, - kTrumaZeroMark, kTrumaSpace, - kTrumaFooterMark, kTrumaGap, - true, kUseDefTol, kMarkExcess, false); - if (!used) return false; - - // Compliance - if (strict && !IRTrumaAc::validChecksum(data)) return false; // Checksum. - - // Success - results->value = data; - results->decode_type = decode_type_t::TRUMA; - results->bits = nbits; - results->address = 0; - results->command = 0; - return true; -} -#endif // DECODE_TRUMA - - -/// 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? -IRTrumaAc::IRTrumaAc(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 IRTrumaAc::begin(void) { _irsend.begin(); } - -#if SEND_TRUMA -/// Send the current internal state as an IR message. -/// @param[in] repeat Nr. of times the message will be repeated. -void IRTrumaAc::send(const uint16_t repeat) { - _irsend.sendTruma(getRaw(), kTrumaBits, repeat); -} -#endif // SEND_TRUMA - -/// Calculate the checksum for a given state. -/// @param[in] state The value to calc the checksum of. -/// @return The calculated checksum value. -uint8_t IRTrumaAc::calcChecksum(const uint64_t state) { - uint8_t sum = kTrumaChecksumInit; - uint64_t to_checksum = state; - for (uint16_t i = 8; i < kTrumaBits; i += 8) { - sum += (to_checksum & 0xFF); - to_checksum >>= 8; - } - return sum; -} - -/// 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 IRTrumaAc::validChecksum(const uint64_t state) { - TrumaProtocol state_copy; - state_copy.raw = state; - return state_copy.Sum == calcChecksum(state); -} - -/// Calculate & set the checksum for the current internal state of the remote. -void IRTrumaAc::checksum(void) { _.Sum = calcChecksum(_.raw); } - -/// Reset the state of the remote to a known good state/sequence. -void IRTrumaAc::stateReset(void) { setRaw(kTrumaDefaultState); } - -/// 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 IRTrumaAc::getRaw(void) { - checksum(); - return _.raw; -} - -/// Set the internal state from a valid code for this protocol. -/// @param[in] state A valid code for this protocol. -void IRTrumaAc::setRaw(const uint64_t state) { - _.raw = state; - _lastfan = _.Fan; - _lastmode = _.Mode; -} - -/// Set the requested power state of the A/C to on. -void IRTrumaAc::on(void) { setPower(true); } - -/// Set the requested power state of the A/C to off. -void IRTrumaAc::off(void) { setPower(false); } - -/// Change the power setting. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRTrumaAc::setPower(const bool on) { - _.PowerOff = !on; - _.Mode = on ? _lastmode : kTrumaFan; // Off temporarily sets mode to Fan. -} - -/// Get the value of the current power setting. -/// @return true, the setting is on. false, the setting is off. -bool IRTrumaAc::getPower(void) const { return !_.PowerOff; } - -/// Set the speed of the fan. -/// @param[in] speed The desired setting. -void IRTrumaAc::setFan(const uint8_t speed) { - switch (speed) { - case kTrumaFanHigh: - case kTrumaFanMed: - case kTrumaFanLow: - _lastfan = speed; // Never allow _lastfan to be Quiet. - _.Fan = speed; - break; - case kTrumaFanQuiet: - if (_.Mode == kTrumaCool) _.Fan = kTrumaFanQuiet; // Only in Cool mode. - break; - default: - setFan(kTrumaFanHigh); - } -} - -/// Get the current fan speed setting. -/// @return The current fan speed/mode. -uint8_t IRTrumaAc::getFan(void) const { return _.Fan; } - -/// Set the operating mode of the A/C. -/// @param[in] mode The desired operating mode. -void IRTrumaAc::setMode(const uint8_t mode) { - switch (mode) { - case kTrumaAuto: - case kTrumaFan: - if (getQuiet()) setFan(kTrumaFanHigh); // Can only have quiet in Cool. - // FALL THRU - case kTrumaCool: - _.Mode = _.PowerOff ? kTrumaFan : mode; // When Off, only set Fan mode. - _lastmode = mode; - break; - default: - setMode(kTrumaAuto); - } -} - -/// Get the operating mode setting of the A/C. -/// @return The current operating mode setting. -uint8_t IRTrumaAc::getMode(void) const { return _.Mode; } - -/// Set the temperature. -/// @param[in] celsius The temperature in degrees celsius. -void IRTrumaAc::setTemp(const uint8_t celsius) { - uint8_t temp = std::max(celsius, kTrumaMinTemp); - temp = std::min(temp, kTrumaMaxTemp); - _.Temp = temp - kTrumaTempOffset; -} - -/// Get the current temperature setting. -/// @return The current setting for temp. in degrees celsius. -uint8_t IRTrumaAc::getTemp(void) const { return _.Temp + kTrumaTempOffset; } - -/// Change the Quiet setting. -/// @param[in] on true, the setting is on. false, the setting is off. -/// @note Quiet is only available in Cool mode. -void IRTrumaAc::setQuiet(const bool on) { - if (on && _.Mode == kTrumaCool) - setFan(kTrumaFanQuiet); - else - setFan(_lastfan); -} - -/// Get the value of the current quiet setting. -/// @return true, the setting is on. false, the setting is off. -bool IRTrumaAc::getQuiet(void) const { return _.Fan == kTrumaFanQuiet; } - -/// 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 IRTrumaAc::convertMode(const stdAc::opmode_t mode) { - switch (mode) { - case stdAc::opmode_t::kCool: return kTrumaCool; - case stdAc::opmode_t::kFan: return kTrumaFan; - default: return kTrumaAuto; - } -} - -/// 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 IRTrumaAc::convertFan(const stdAc::fanspeed_t speed) { - switch (speed) { - case stdAc::fanspeed_t::kMin: return kTrumaFanQuiet; - case stdAc::fanspeed_t::kLow: return kTrumaFanLow; - case stdAc::fanspeed_t::kMedium: return kTrumaFanMed; - default: return kTrumaFanHigh; - } -} - -/// 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 IRTrumaAc::toCommonMode(const uint8_t mode) { - switch (mode) { - case kTrumaCool: return stdAc::opmode_t::kCool; - case kTrumaFan: return stdAc::opmode_t::kFan; - default: return stdAc::opmode_t::kAuto; - } -} - -/// Convert a native fan speed into its stdAc equivalent. -/// @param[in] spd The native setting to be converted. -/// @return The stdAc equivalent of the native setting. -stdAc::fanspeed_t IRTrumaAc::toCommonFanSpeed(const uint8_t spd) { - switch (spd) { - case kTrumaFanMed: return stdAc::fanspeed_t::kMedium; - case kTrumaFanLow: return stdAc::fanspeed_t::kLow; - case kTrumaFanQuiet: return stdAc::fanspeed_t::kMin; - default: return stdAc::fanspeed_t::kHigh; - } -} - -/// Convert the current internal state into its stdAc::state_t equivalent. -/// @return The stdAc equivalent of the native settings. -stdAc::state_t IRTrumaAc::toCommon(void) const { - stdAc::state_t result; - - result.protocol = decode_type_t::TRUMA; - result.model = -1; // Not supported. - // Do we have enough current state info to override any previous state? - // i.e. Was the class just setRaw()'ed with a short "swing" message. - // This should enables us to also ignore the Swing msg's special 17C setting. - result.power = getPower(); - result.mode = toCommonMode(getMode()); - result.celsius = true; - result.degrees = getTemp(); - result.fanspeed = toCommonFanSpeed(getFan()); - result.quiet = getQuiet(); - // Not supported. - result.turbo = false; - result.econo = false; - result.light = false; - result.filter = false; - result.swingv = stdAc::swingv_t::kOff; - result.swingh = stdAc::swingh_t::kOff; - 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 IRTrumaAc::toString(void) const { - String result = ""; - result.reserve(80); - result += addBoolToString(getPower(), kPowerStr, false); - if (getPower()) // Only show the Operating Mode if the unit is on. - result += addModeToString(_.Mode, kTrumaAuto, kTrumaCool, - kTrumaAuto, kTrumaAuto, kTrumaFan); - - result += addTempToString(getTemp()); - result += addFanToString(_.Fan, kTrumaFanHigh, kTrumaFanLow, kTrumaFanHigh, - kTrumaFanQuiet, kTrumaFanMed); - result += addBoolToString(getQuiet(), kQuietStr); - return result; -} diff --git a/lib/IRremoteESP8266/src/ir_Truma.h b/lib/IRremoteESP8266/src/ir_Truma.h index 9946a03229..58beeeba44 100644 --- a/lib/IRremoteESP8266/src/ir_Truma.h +++ b/lib/IRremoteESP8266/src/ir_Truma.h @@ -15,10 +15,10 @@ #ifndef UNIT_TEST #include #endif -#include "IRremoteESP8266.h" -#include "IRsend.h" +#include "../src/IRremoteESP8266.h" +#include "../src/IRsend.h" #ifdef UNIT_TEST -#include "IRsend_test.h" +#include "../src/IRsend_test.h" #endif /// Native representation of a Truma A/C message. diff --git a/lib/IRremoteESP8266/src/ir_Vestel.cpp b/lib/IRremoteESP8266/src/ir_Vestel.cpp deleted file mode 100644 index 803363a169..0000000000 --- a/lib/IRremoteESP8266/src/ir_Vestel.cpp +++ /dev/null @@ -1,572 +0,0 @@ -// Copyright 2018 Erdem U. Altinyurt -// Copyright 2019 David Conran - -/// @file -/// @brief Support for Vestel protocols. -/// Vestel added by Erdem U. Altinyurt - -#include "ir_Vestel.h" -#include -#ifndef UNIT_TEST -#include -#endif -#include "IRrecv.h" -#include "IRremoteESP8266.h" -#include "IRsend.h" -#include "IRtext.h" -#include "IRutils.h" -#include "ir_Haier.h" - -// Ref: -// None. Totally reverse engineered. - -using irutils::addBoolToString; -using irutils::addIntToString; -using irutils::addLabeledString; -using irutils::addModeToString; -using irutils::addTempToString; -using irutils::minsToString; - -#if SEND_VESTEL_AC -/// Send a Vestel 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::sendVestelAc(const uint64_t data, const uint16_t nbits, - const uint16_t repeat) { - if (nbits % 8 != 0) return; // nbits is required to be a multiple of 8. - - sendGeneric(kVestelAcHdrMark, kVestelAcHdrSpace, // Header - kVestelAcBitMark, kVestelAcOneSpace, // Data - kVestelAcBitMark, kVestelAcZeroSpace, // Data - kVestelAcBitMark, 100000, // Footer + repeat gap - data, nbits, 38, false, repeat, 50); -} -#endif // SEND_VESTEL_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? -IRVestelAc::IRVestelAc(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. -/// @note Power On, Mode Auto, Fan Auto, Temp = 25C/77F -void IRVestelAc::stateReset(void) { - _.cmdState = kVestelAcStateDefault; - _.timeState = kVestelAcTimeStateDefault; -} - -/// Set up hardware to be able to send a message. -void IRVestelAc::begin(void) { _irsend.begin(); } - -#if SEND_VESTEL_AC -/// Send the current internal state as an IR message. -/// @param[in] repeat Nr. of times the message will be repeated. -void IRVestelAc::send(const uint16_t repeat) { - _irsend.sendVestelAc(getRaw(), kVestelAcBits, repeat); -} -#endif // SEND_VESTEL_AC - -/// Get a copy of the internal state/code for this protocol. -/// @return A code for this protocol based on the current internal state. -uint64_t IRVestelAc::getRaw(void) { - checksum(); - if (!_.UseCmd) return _.timeState; - return _.cmdState; -} - -/// Set the internal state from a valid code for this protocol. -/// @param[in] newState A valid code for this protocol. -void IRVestelAc::setRaw(const uint8_t* newState) { - uint64_t upState = 0; - for (int i = 0; i < 7; i++) - upState |= static_cast(newState[i]) << (i * 8); - setRaw(upState); -} - -/// Set the internal state from a valid code for this protocol. -/// @param[in] newState A valid code for this protocol. -void IRVestelAc::setRaw(const uint64_t newState) { - _.cmdState = newState; - _.timeState = newState; - if (isTimeCommand()) { - _.cmdState = kVestelAcStateDefault; - _.UseCmd = false; - } else { - _.timeState = kVestelAcTimeStateDefault; - } -} - -/// Set the requested power state of the A/C to on. -void IRVestelAc::on(void) { setPower(true); } - -/// Set the requested power state of the A/C to off. -void IRVestelAc::off(void) { setPower(false); } - -/// Change the power setting. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRVestelAc::setPower(const bool on) { - _.Power = (on ? 0b11 : 0b00); - _.UseCmd = true; -} - -/// Get the value of the current power setting. -/// @return true, the setting is on. false, the setting is off. -bool IRVestelAc::getPower(void) const { - return _.Power; -} - -/// Set the temperature. -/// @param[in] temp The temperature in degrees celsius. -void IRVestelAc::setTemp(const uint8_t temp) { - uint8_t new_temp = std::max(kVestelAcMinTempC, temp); - new_temp = std::min(kVestelAcMaxTemp, new_temp); - _.Temp = new_temp - kVestelAcMinTempH; - _.UseCmd = true; -} - -/// Get the current temperature setting. -/// @return The current setting for temp. in degrees celsius. -uint8_t IRVestelAc::getTemp(void) const { - return _.Temp + kVestelAcMinTempH; -} - -/// Set the speed of the fan. -/// @param[in] fan The desired setting. -void IRVestelAc::setFan(const uint8_t fan) { - switch (fan) { - case kVestelAcFanLow: - case kVestelAcFanMed: - case kVestelAcFanHigh: - case kVestelAcFanAutoCool: - case kVestelAcFanAutoHot: - case kVestelAcFanAuto: - _.Fan = fan; - break; - default: - _.Fan = kVestelAcFanAuto; - } - _.UseCmd = true; -} - -/// Get the current fan speed setting. -/// @return The current fan speed/mode. -uint8_t IRVestelAc::getFan(void) const { - return _.Fan; -} - -/// Get the operating mode setting of the A/C. -/// @return The current operating mode setting. -uint8_t IRVestelAc::getMode(void) const { - return _.Mode; -} - -/// Set the operating mode of the A/C. -/// @param[in] mode The desired operating mode. -/// @note If we get an unexpected mode, default to AUTO. -void IRVestelAc::setMode(const uint8_t mode) { - switch (mode) { - case kVestelAcAuto: - case kVestelAcCool: - case kVestelAcHeat: - case kVestelAcDry: - case kVestelAcFan: - _.Mode = mode; - break; - default: - _.Mode = kVestelAcAuto; - } - _.UseCmd = true; -} - -/// Set Auto mode/level of the A/C. -/// @param[in] autoLevel The auto mode/level setting. -void IRVestelAc::setAuto(const int8_t autoLevel) { - if (autoLevel < -2 || autoLevel > 2) return; - _.Mode = kVestelAcAuto; - _.Fan = (autoLevel < 0 ? kVestelAcFanAutoCool : kVestelAcFanAutoHot); - if (autoLevel == 2) - setTemp(30); - else if (autoLevel == 1) - setTemp(31); - else if (autoLevel == 0) - setTemp(25); - else if (autoLevel == -1) - setTemp(16); - else if (autoLevel == -2) - setTemp(17); -} - -/// Set the timer to be active on the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRVestelAc::setTimerActive(const bool on) { - _.Timer = on; - _.UseCmd = false; -} - -/// Get if the Timer is active on the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRVestelAc::isTimerActive(void) const { - return _.Timer; -} - -/// Set Timer option of A/C. -/// @param[in] minutes Nr of minutes the timer is to be set for. -/// @note Valid arguments are 0, 0.5, 1, 2, 3 and 5 hours (in minutes). -/// 0 disables the timer. -void IRVestelAc::setTimer(const uint16_t minutes) { - // Clear both On & Off timers. - _.OnHours = 0; - _.OnTenMins = 0; - // Set the "Off" time with the nr of minutes before we turn off. - _.OffHours = minutes / 60; - _.OffTenMins = (minutes % 60) / 10; - setOffTimerActive(false); - // Yes. On Timer instead of Off timer active. - setOnTimerActive(minutes != 0); - setTimerActive(minutes != 0); -} - -/// Get the Timer time of A/C. -/// @return The number of minutes of time on the timer. -uint16_t IRVestelAc::getTimer(void) const { return getOffTimer(); } - -/// Set the A/C's internal clock. -/// @param[in] minutes The time expressed in nr. of minutes past midnight. -void IRVestelAc::setTime(const uint16_t minutes) { - _.Hours = minutes / 60; - _.Minutes = minutes % 60; - _.UseCmd = false; -} - -/// Get the A/C's internal clock's time. -/// @return The time expressed in nr. of minutes past midnight. -uint16_t IRVestelAc::getTime(void) const { - return _.Hours * 60 + _.Minutes; -} - -/// Set the On timer to be active on the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRVestelAc::setOnTimerActive(const bool on) { - _.OnTimer = on; - _.UseCmd = false; -} - -/// Get if the On Timer is active on the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRVestelAc::isOnTimerActive(void) const { - return _.OnTimer; -} - -/// Set the On timer time on the A/C. -/// @param[in] minutes Time in nr. of minutes. -void IRVestelAc::setOnTimer(const uint16_t minutes) { - setOnTimerActive(minutes); - _.OnHours = minutes / 60; - _.OnTenMins = (minutes % 60) / 10; - setTimerActive(false); -} - -/// Get the A/C's On Timer time. -/// @return The time expressed in nr. of minutes. -uint16_t IRVestelAc::getOnTimer(void) const { - return _.OnHours * 60 + _.OnTenMins * 10; -} - -/// Set the Off timer to be active on the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRVestelAc::setOffTimerActive(const bool on) { - _.OffTimer = on; - _.UseCmd = false; -} - -/// Get if the Off Timer is active on the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRVestelAc::isOffTimerActive(void) const { - return _.OffTimer; -} - -/// Set the Off timer time on the A/C. -/// @param[in] minutes Time in nr. of minutes. -void IRVestelAc::setOffTimer(const uint16_t minutes) { - setOffTimerActive(minutes); - _.OffHours = minutes / 60; - _.OffTenMins = (minutes % 60) / 10; - setTimerActive(false); -} - -/// Get the A/C's Off Timer time. -/// @return The time expressed in nr. of minutes. -uint16_t IRVestelAc::getOffTimer(void) const { - return _.OffHours * 60 + _.OffTenMins * 10; -} - -/// Set the Sleep setting of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRVestelAc::setSleep(const bool on) { - _.TurboSleep = (on ? kVestelAcSleep : kVestelAcNormal); - _.UseCmd = true; -} - -/// Get the Sleep setting of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRVestelAc::getSleep(void) const { - return _.TurboSleep == kVestelAcSleep; -} - -/// Set the Turbo setting of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRVestelAc::setTurbo(const bool on) { - _.TurboSleep = (on ? kVestelAcTurbo : kVestelAcNormal); - _.UseCmd = true; -} - -/// Get the Turbo setting of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRVestelAc::getTurbo(void) const { - return _.TurboSleep == kVestelAcTurbo; -} - -/// Set the Ion (Filter) setting of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRVestelAc::setIon(const bool on) { - _.Ion = on; - _.UseCmd = true; -} - -/// Get the Ion (Filter) setting of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRVestelAc::getIon(void) const { - return _.Ion; -} - -/// Set the Swing Roaming setting of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRVestelAc::setSwing(const bool on) { - _.Swing = (on ? kVestelAcSwing : 0xF); - _.UseCmd = true; -} - -/// Get the Swing Roaming setting of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRVestelAc::getSwing(void) const { - return _.Swing == kVestelAcSwing; -} - -/// Calculate the checksum for a given state. -/// @param[in] state The state to calc the checksum of. -/// @return The calculated checksum value. -uint8_t IRVestelAc::calcChecksum(const uint64_t state) { - // Just counts the set bits +1 on stream and take inverse after mask - return 0xFF - countBits(GETBITS64(state, 20, 44), 44, true, 2); -} - -/// 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 IRVestelAc::validChecksum(const uint64_t state) { - VestelProtocol vp; - vp.cmdState = state; - return vp.CmdSum == IRVestelAc::calcChecksum(state); -} - -/// Calculate & set the checksum for the current internal state of the remote. -void IRVestelAc::checksum(void) { - // Stored the checksum value in the last byte. - _.CmdSum = calcChecksum(_.cmdState); - _.TimeSum = calcChecksum(_.timeState); -} - -/// Is the current state a time command? -/// @return true, if the state is a time message. Otherwise, false. -bool IRVestelAc::isTimeCommand(void) const { - return !_.UseCmd; -} - -/// 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 IRVestelAc::convertMode(const stdAc::opmode_t mode) { - switch (mode) { - case stdAc::opmode_t::kCool: return kVestelAcCool; - case stdAc::opmode_t::kHeat: return kVestelAcHeat; - case stdAc::opmode_t::kDry: return kVestelAcDry; - case stdAc::opmode_t::kFan: return kVestelAcFan; - default: return kVestelAcAuto; - } -} - -/// 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 IRVestelAc::convertFan(const stdAc::fanspeed_t speed) { - switch (speed) { - case stdAc::fanspeed_t::kMin: - case stdAc::fanspeed_t::kLow: return kVestelAcFanLow; - case stdAc::fanspeed_t::kMedium: return kVestelAcFanMed; - case stdAc::fanspeed_t::kHigh: - case stdAc::fanspeed_t::kMax: return kVestelAcFanHigh; - default: return kVestelAcFanAuto; - } -} - -/// 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 IRVestelAc::toCommonMode(const uint8_t mode) { - switch (mode) { - case kVestelAcCool: return stdAc::opmode_t::kCool; - case kVestelAcHeat: return stdAc::opmode_t::kHeat; - case kVestelAcDry: return stdAc::opmode_t::kDry; - case kVestelAcFan: return stdAc::opmode_t::kFan; - default: return stdAc::opmode_t::kAuto; - } -} - -/// Convert a native fan speed into its stdAc equivalent. -/// @param[in] spd The native setting to be converted. -/// @return The stdAc equivalent of the native setting. -stdAc::fanspeed_t IRVestelAc::toCommonFanSpeed(const uint8_t spd) { - switch (spd) { - case kVestelAcFanHigh: return stdAc::fanspeed_t::kMax; - case kVestelAcFanMed: return stdAc::fanspeed_t::kMedium; - case kVestelAcFanLow: 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 IRVestelAc::toCommon(void) const { - stdAc::state_t result; - result.protocol = decode_type_t::VESTEL_AC; - result.model = -1; // Not supported. - result.power = _.Power; - result.mode = toCommonMode(_.Mode); - result.celsius = true; - result.degrees = getTemp(); - result.fanspeed = toCommonFanSpeed(_.Fan); - result.swingv = (getSwing() ? stdAc::swingv_t::kAuto - : stdAc::swingv_t::kOff); - result.turbo = getTurbo(); - result.filter = _.Ion; - result.sleep = (getSleep() ? 0 : -1); - // Not supported. - result.swingh = stdAc::swingh_t::kOff; - result.light = 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 IRVestelAc::toString(void) const { - String result = ""; - result.reserve(100); // Reserve some heap for the string to reduce fragging. - if (isTimeCommand()) { - result += addLabeledString(minsToString(getTime()), kClockStr, false); - result += addLabeledString( - (_.Timer ? minsToString(getTimer()) : kOffStr), - kTimerStr); - result += addLabeledString( - (_.OnTimer && !_.Timer) ? minsToString(getOnTimer()) : kOffStr, - kOnTimerStr); - result += addLabeledString( - (_.OffTimer ? minsToString(getOffTimer()) : kOffStr), - kOffTimerStr); - return result; - } - // Not a time command, it's a normal command. - result += addBoolToString(_.Power, kPowerStr, false); - result += addModeToString(_.Mode, kVestelAcAuto, kVestelAcCool, - kVestelAcHeat, kVestelAcDry, kVestelAcFan); - result += addTempToString(getTemp()); - result += addIntToString(_.Fan, kFanStr); - result += kSpaceLBraceStr; - switch (_.Fan) { - case kVestelAcFanAuto: - result += kAutoStr; - break; - case kVestelAcFanLow: - result += kLowStr; - break; - case kVestelAcFanMed: - result += kMedStr; - break; - case kVestelAcFanHigh: - result += kHighStr; - break; - case kVestelAcFanAutoCool: - result += kAutoStr; - result += ' '; - result += kCoolStr; - break; - case kVestelAcFanAutoHot: - result += kAutoStr; - result += ' '; - result += kHeatStr; - break; - default: - result += kUnknownStr; - } - result += ')'; - result += addBoolToString(getSleep(), kSleepStr); - result += addBoolToString(getTurbo(), kTurboStr); - result += addBoolToString(_.Ion, kIonStr); - result += addBoolToString(getSwing(), kSwingStr); - return result; -} - -#if DECODE_VESTEL_AC -/// Decode the supplied Vestel 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. -/// @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::decodeVestelAc(decode_results* results, uint16_t offset, - const uint16_t nbits, const bool strict) { - if (nbits % 8 != 0) // nbits has to be a multiple of nr. of bits in a byte. - return false; - - if (strict) - if (nbits != kVestelAcBits) - return false; // Not strictly a Vestel AC message. - - uint64_t data = 0; - - if (nbits > sizeof(data) * 8) - return false; // We can't possibly capture a Vestel packet that big. - - // Match Header + Data + Footer - if (!matchGeneric(results->rawbuf + offset, &data, - results->rawlen - offset, nbits, - kVestelAcHdrMark, kVestelAcHdrSpace, - kVestelAcBitMark, kVestelAcOneSpace, - kVestelAcBitMark, kVestelAcZeroSpace, - kVestelAcBitMark, 0, false, - kVestelAcTolerance, kMarkExcess, false)) return false; - // Compliance - if (strict) - if (!IRVestelAc::validChecksum(data)) return false; - - // Success - results->decode_type = VESTEL_AC; - results->bits = nbits; - results->value = data; - results->address = 0; - results->command = 0; - - return true; -} -#endif // DECODE_VESTEL_AC diff --git a/lib/IRremoteESP8266/src/ir_Vestel.h b/lib/IRremoteESP8266/src/ir_Vestel.h index 53ebdc7cda..0b3bd2c4b6 100644 --- a/lib/IRremoteESP8266/src/ir_Vestel.h +++ b/lib/IRremoteESP8266/src/ir_Vestel.h @@ -16,10 +16,10 @@ #ifdef ARDUINO #include #endif -#include "IRremoteESP8266.h" -#include "IRsend.h" +#include "../src/IRremoteESP8266.h" +#include "../src/IRsend.h" #ifdef UNIT_TEST -#include "IRsend_test.h" +#include "../src/IRsend_test.h" #endif /// Native representation of a Vestel A/C message. diff --git a/lib/IRremoteESP8266/src/ir_Voltas.cpp b/lib/IRremoteESP8266/src/ir_Voltas.cpp deleted file mode 100644 index 847446f385..0000000000 --- a/lib/IRremoteESP8266/src/ir_Voltas.cpp +++ /dev/null @@ -1,516 +0,0 @@ -// Copyright 2020 David Conran (crankyoldgit) -// Copyright 2020 manj9501 -/// @file -/// @brief Support for Voltas A/C protocol -/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1238 - -#include "ir_Voltas.h" -#include -#include -#include "IRrecv.h" -#include "IRsend.h" -#include "IRtext.h" -#include "IRutils.h" - -using irutils::addBoolToString; -using irutils::addModelToString; -using irutils::addModeToString; -using irutils::addFanToString; -using irutils::addLabeledString; -using irutils::addTempToString; -using irutils::minsToString; - -// Constants -const uint16_t kVoltasBitMark = 1026; ///< uSeconds. -const uint16_t kVoltasOneSpace = 2553; ///< uSeconds. -const uint16_t kVoltasZeroSpace = 554; ///< uSeconds. -const uint16_t kVoltasFreq = 38000; ///< Hz. - -#if SEND_VOLTAS -/// Send a Voltas formatted message. -/// Status: STABLE / Working on real device. -/// @param[in] data An array of bytes containing the IR command. -/// It is assumed to be in MSB order for this code. -/// e.g. -/// @code -/// uint8_t data[kVoltasStateLength] = {0x33, 0x28, 0x88, 0x1A, 0x3B, 0x3B, -/// 0x3B, 0x11, 0x00, 0x40}; -/// @endcode -/// @param[in] nbytes Nr. of bytes of data in the array. (>=kVoltasStateLength) -/// @param[in] repeat Nr. of times the message is to be repeated. -void IRsend::sendVoltas(const uint8_t data[], const uint16_t nbytes, - const uint16_t repeat) { - sendGeneric(0, 0, - kVoltasBitMark, kVoltasOneSpace, - kVoltasBitMark, kVoltasZeroSpace, - kVoltasBitMark, kDefaultMessageGap, - data, nbytes, - kVoltasFreq, true, repeat, kDutyDefault); -} -#endif // SEND_VOLTAS - -#if DECODE_VOLTAS -/// Decode the supplied Voltas message. -/// Status: STABLE / Working on real device. -/// @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::decodeVoltas(decode_results *results, uint16_t offset, - const uint16_t nbits, const bool strict) { - if (strict && nbits != kVoltasBits) return false; - - // Data + Footer - if (!matchGeneric(results->rawbuf + offset, results->state, - results->rawlen - offset, nbits, - 0, 0, // No header - kVoltasBitMark, kVoltasOneSpace, - kVoltasBitMark, kVoltasZeroSpace, - kVoltasBitMark, kDefaultMessageGap, true)) return false; - - // Compliance - if (strict && !IRVoltas::validChecksum(results->state, nbits / 8)) - return false; - // Success - results->decode_type = decode_type_t::VOLTAS; - results->bits = nbits; - return true; -} -#endif // DECODE_VOLTAS - -/// 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? -IRVoltas::IRVoltas(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 IRVoltas::stateReset() { - // This resets to a known-good state. - // ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/1238#issuecomment-674699746 - const uint8_t kReset[kVoltasStateLength] = { - 0x33, 0x28, 0x00, 0x17, 0x3B, 0x3B, 0x3B, 0x11, 0x00, 0xCB}; - setRaw(kReset); -} - -/// Set up hardware to be able to send a message. -void IRVoltas::begin() { _irsend.begin(); } - -#if SEND_VOLTAS -/// Send the current internal state as an IR message. -/// @param[in] repeat Nr. of times the message will be repeated. -void IRVoltas::send(const uint16_t repeat) { - _irsend.sendVoltas(getRaw(), kVoltasStateLength, repeat); -} -#endif // SEND_VOLTAS - -/// Get the model information currently known. -/// @param[in] raw Work out the model info from the current raw state. -/// @return The known model number. -voltas_ac_remote_model_t IRVoltas::getModel(const bool raw) const { - if (raw) { - switch (_.SwingHChange) { - case kVoltasSwingHNoChange: - return voltas_ac_remote_model_t::kVoltas122LZF; - default: - return voltas_ac_remote_model_t::kVoltasUnknown; - } - } else { - return _model; - } -} - -/// Set the current model for the remote. -/// @param[in] model The model number. -void IRVoltas::setModel(const voltas_ac_remote_model_t model) { - switch (model) { - case voltas_ac_remote_model_t::kVoltas122LZF: - _model = model; - setSwingHChange(false); - break; - default: _model = voltas_ac_remote_model_t::kVoltasUnknown; - } -} - -/// 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* IRVoltas::getRaw(void) { - checksum(); // Ensure correct settings before sending. - return _.raw; -} - -/// Set the internal state from a valid code for this protocol. -/// @param[in] new_code A valid code for this protocol. -void IRVoltas::setRaw(const uint8_t new_code[]) { - std::memcpy(_.raw, new_code, kVoltasStateLength); - setModel(getModel(true)); -} - -/// Calculate and set the checksum values for the internal state. -void IRVoltas::checksum(void) { - _.Checksum = calcChecksum(_.raw); -} - -/// 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 IRVoltas::validChecksum(const uint8_t state[], const uint16_t length) { - if (length) return state[length - 1] == calcChecksum(state, length); - return true; -} - -/// Calculate the checksum is valid for a given state. -/// @param[in] state The array to calculate the checksum of. -/// @param[in] length The length of the state array. -/// @return The valid checksum value for the state. -uint8_t IRVoltas::calcChecksum(const uint8_t state[], const uint16_t length) { - uint8_t result = 0; - if (length) - result = sumBytes(state, length - 1); - return ~result; -} - -/// Change the power setting to On. -void IRVoltas::on() { setPower(true); } - -/// Change the power setting to Off. -void IRVoltas::off() { setPower(false); } - -/// Change the power setting. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRVoltas::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 IRVoltas::getPower(void) const { return _.Power; } - -/// Set the operating mode of the A/C. -/// @param[in] mode The desired operating mode. -/// @note If we get an unexpected mode, default to AUTO. -void IRVoltas::setMode(const uint8_t mode) { - _.Mode = mode; - switch (mode) { - case kVoltasFan: - setFan(getFan()); // Force the fan speed to a correct one fo the mode. - break; - case kVoltasDry: - setFan(kVoltasFanLow); - setTemp(kVoltasDryTemp); - break; - case kVoltasHeat: - case kVoltasCool: - break; - default: - setMode(kVoltasCool); - return; - } - // Reset some settings if needed. - setEcono(getEcono()); - setTurbo(getTurbo()); - setSleep(getSleep()); -} - -/// Get the operating mode setting of the A/C. -/// @return The current operating mode setting. -uint8_t IRVoltas::getMode(void) { 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 IRVoltas::convertMode(const stdAc::opmode_t mode) { - switch (mode) { - case stdAc::opmode_t::kHeat: return kVoltasHeat; - case stdAc::opmode_t::kDry: return kVoltasDry; - case stdAc::opmode_t::kFan: return kVoltasFan; - default: return kVoltasCool; - } -} - -/// 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 IRVoltas::toCommonMode(const uint8_t mode) { - switch (mode) { - case kVoltasHeat: return stdAc::opmode_t::kHeat; - case kVoltasDry: return stdAc::opmode_t::kDry; - case kVoltasFan: return stdAc::opmode_t::kFan; - default: return stdAc::opmode_t::kCool; - } -} - -/// Set the temperature. -/// @param[in] temp The temperature in degrees celsius. -void IRVoltas::setTemp(const uint8_t temp) { - uint8_t new_temp = std::max(kVoltasMinTemp, temp); - new_temp = std::min(kVoltasMaxTemp, new_temp); - _.Temp = new_temp - kVoltasMinTemp; -} - -/// Get the current temperature setting. -/// @return The current setting for temp. in degrees celsius. -uint8_t IRVoltas::getTemp(void) { return _.Temp + kVoltasMinTemp; } - -/// Set the speed of the fan. -/// @param[in] fan The desired setting. -void IRVoltas::setFan(const uint8_t fan) { - switch (fan) { - case kVoltasFanAuto: - if (_.Mode == kVoltasFan) { // Auto speed is not available in fan mode. - setFan(kVoltasFanHigh); - return; - } - // FALL-THRU - case kVoltasFanLow: - case kVoltasFanMed: - case kVoltasFanHigh: - _.FanSpeed = fan; - break; - default: - setFan(kVoltasFanAuto); - } -} - -/// Get the current fan speed setting. -/// @return The current fan speed/mode. -uint8_t IRVoltas::getFan(void) { return _.FanSpeed; } - -/// 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 IRVoltas::convertFan(const stdAc::fanspeed_t speed) { - switch (speed) { - case stdAc::fanspeed_t::kMin: - case stdAc::fanspeed_t::kLow: return kVoltasFanLow; - case stdAc::fanspeed_t::kMedium: return kVoltasFanMed; - case stdAc::fanspeed_t::kHigh: - case stdAc::fanspeed_t::kMax: return kVoltasFanHigh; - default: return kVoltasFanAuto; - } -} - -/// Convert a native fan speed into its stdAc equivalent. -/// @param[in] spd The native setting to be converted. -/// @return The stdAc equivalent of the native setting. -stdAc::fanspeed_t IRVoltas::toCommonFanSpeed(const uint8_t spd) { - switch (spd) { - case kVoltasFanHigh: return stdAc::fanspeed_t::kMax; - case kVoltasFanMed: return stdAc::fanspeed_t::kMedium; - case kVoltasFanLow: 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 IRVoltas::setSwingV(const bool on) { _.SwingV = on ? 0b111 : 0b000; } - -/// Get the Vertical Swing setting of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRVoltas::getSwingV(void) const { return _.SwingV == 0b111; } - -/// Set the Horizontal Swing setting of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRVoltas::setSwingH(const bool on) { - switch (_model) { - case voltas_ac_remote_model_t::kVoltas122LZF: - break; // unsupported on these models. - default: - _.SwingH = on; - setSwingHChange(true); - } -} - -/// Get the Horizontal Swing setting of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRVoltas::getSwingH(void) const { - switch (_model) { - case voltas_ac_remote_model_t::kVoltas122LZF: - return false; // unsupported on these models. - default: - return _.SwingH; - } -} - -/// Set the bits for changing the Horizontal Swing setting of the A/C. -/// @param[in] on true, the change bits are set. -/// false, the "no change" bits are set. -void IRVoltas::setSwingHChange(const bool on) { - _.SwingHChange = on ? kVoltasSwingHChange : kVoltasSwingHNoChange; - if (!on) _.SwingH = true; // "No Change" also sets SwingH to 1. -} - -/// Are the Horizontal Swing change bits set in the message? -/// @return true, the correct bits are set. false, the correct bits are not set. -bool IRVoltas::getSwingHChange(void) const { - return _.SwingHChange == kVoltasSwingHChange; -} - -/// Change the Wifi setting. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRVoltas::setWifi(const bool on) { _.Wifi = on; } - -/// Get the value of the current Wifi setting. -/// @return true, the setting is on. false, the setting is off. -bool IRVoltas::getWifi(void) const { return _.Wifi; } - -/// Change the Turbo setting. -/// @param[in] on true, the setting is on. false, the setting is off. -/// @note The Turbo setting is only available in Cool mode. -void IRVoltas::setTurbo(const bool on) { - if (on && _.Mode == kVoltasCool) - _.Turbo = true; - else - _.Turbo = false; -} - -/// Get the value of the current Turbo setting. -/// @return true, the setting is on. false, the setting is off. -bool IRVoltas::getTurbo(void) const { return _.Turbo; } - -/// Change the Economy setting. -/// @param[in] on true, the setting is on. false, the setting is off. -/// @note The Economy setting is only available in Cool mode. -void IRVoltas::setEcono(const bool on) { - if (on && _.Mode == kVoltasCool) - _.Econo = true; - else - _.Econo = false; -} - -/// Get the value of the current Econo setting. -/// @return true, the setting is on. false, the setting is off. -bool IRVoltas::getEcono(void) const { return _.Econo; } - -/// Change the Light setting. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRVoltas::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 IRVoltas::getLight(void) const { return _.Light; } - -/// Change the Sleep setting. -/// @param[in] on true, the setting is on. false, the setting is off. -/// @note The Sleep setting is only available in Cool mode. -void IRVoltas::setSleep(const bool on) { - if (on && _.Mode == kVoltasCool) - _.Sleep = true; - else - _.Sleep = false; -} - -/// Get the value of the current Sleep setting. -/// @return true, the setting is on. false, the setting is off. -bool IRVoltas::getSleep(void) const { return _.Sleep; } - -/// Get the value of the On Timer time. -/// @return Number of minutes before the timer activates. -uint16_t IRVoltas::getOnTime(void) const { - return std::min((unsigned)(12 * _.OnTimer12Hr + _.OnTimerHrs - 1), 23U) * 60 + - _.OnTimerMins; -} - -/// Set the value of the On Timer time. -/// @param[in] nr_of_mins Number of minutes before the timer activates. -/// 0 disables the timer. Max is 23 hrs & 59 mins (1439 mins) -void IRVoltas::setOnTime(const uint16_t nr_of_mins) { - // Cap the total number of mins. - uint16_t mins = std::min(nr_of_mins, (uint16_t)(23 * 60 + 59)); - uint16_t hrs = (mins / 60) + 1; - _.OnTimerMins = mins % 60; - _.OnTimer12Hr = hrs / 12; - _.OnTimerHrs = hrs % 12; - _.OnTimerEnable = (mins > 0); // Is the timer is to be enabled? -} - -/// Get the value of the On Timer time. -/// @return Number of minutes before the timer activates. -uint16_t IRVoltas::getOffTime(void) const { - return std::min((unsigned)(12 * _.OffTimer12Hr + _.OffTimerHrs - 1), 23U) * - 60 + _.OffTimerMins; -} - -/// Set the value of the Off Timer time. -/// @param[in] nr_of_mins Number of minutes before the timer activates. -/// 0 disables the timer. Max is 23 hrs & 59 mins (1439 mins) -void IRVoltas::setOffTime(const uint16_t nr_of_mins) { - // Cap the total number of mins. - uint16_t mins = std::min(nr_of_mins, (uint16_t)(23 * 60 + 59)); - uint16_t hrs = (mins / 60) + 1; - _.OffTimerMins = mins % 60; - _.OffTimer12Hr = hrs / 12; - _.OffTimerHrs = hrs % 12; - _.OffTimerEnable = (mins > 0); // Is the timer is to be enabled? -} - -/// Convert the current internal state into its stdAc::state_t equivalent. -/// @param[in] prev Ptr to the previous state if available. -/// @return The stdAc equivalent of the native settings. -stdAc::state_t IRVoltas::toCommon(const stdAc::state_t *prev) { - 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. - result.swingh = stdAc::swingh_t::kOff; - } - result.model = getModel(); - result.protocol = decode_type_t::VOLTAS; - result.power = _.Power; - result.mode = toCommonMode(_.Mode); - result.celsius = true; - result.degrees = getTemp(); - result.fanspeed = toCommonFanSpeed(_.FanSpeed); - result.swingv = getSwingV() ? stdAc::swingv_t::kAuto : stdAc::swingv_t::kOff; - if (getSwingHChange()) - result.swingh = _.SwingH ? stdAc::swingh_t::kAuto : stdAc::swingh_t::kOff; - result.turbo = _.Turbo; - result.econo = _.Econo; - result.light = _.Light; - result.sleep = _.Sleep ? 0 : -1; - // Not supported. - result.quiet = 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 IRVoltas::toString() { - String result = ""; - result.reserve(200); // Reserve some heap for the string to reduce fragging. - result += addModelToString(decode_type_t::VOLTAS, getModel(), false); - result += addBoolToString(_.Power, kPowerStr); - result += addModeToString(_.Mode, 255, kVoltasCool, kVoltasHeat, - kVoltasDry, kVoltasFan); - result += addTempToString(getTemp()); - result += addFanToString(_.FanSpeed, kVoltasFanHigh, kVoltasFanLow, - kVoltasFanAuto, kVoltasFanAuto, kVoltasFanMed); - result += addBoolToString(getSwingV(), kSwingVStr); - if (getSwingHChange()) - result += addBoolToString(_.SwingH, kSwingHStr); - else - result += addLabeledString(kNAStr, kSwingHStr); - result += addBoolToString(_.Turbo, kTurboStr); - result += addBoolToString(_.Econo, kEconoStr); - result += addBoolToString(_.Wifi, kWifiStr); - result += addBoolToString(_.Light, kLightStr); - result += addBoolToString(_.Sleep, kSleepStr); - result += addLabeledString(_.OnTimerEnable ? minsToString(getOnTime()) - : kOffStr, kOnTimerStr); - result += addLabeledString(_.OffTimerEnable ? minsToString(getOffTime()) - : kOffStr, kOffTimerStr); - return result; -} diff --git a/lib/IRremoteESP8266/src/ir_Voltas.h b/lib/IRremoteESP8266/src/ir_Voltas.h index cf79d3458f..64ab1b5127 100644 --- a/lib/IRremoteESP8266/src/ir_Voltas.h +++ b/lib/IRremoteESP8266/src/ir_Voltas.h @@ -18,10 +18,10 @@ #ifndef UNIT_TEST #include #endif -#include "IRremoteESP8266.h" -#include "IRsend.h" +#include "../src/IRremoteESP8266.h" +#include "../src/IRsend.h" #ifdef UNIT_TEST -#include "IRsend_test.h" +#include "../src/IRsend_test.h" #endif /// Native representation of a Voltas A/C message. diff --git a/lib/IRremoteESP8266/src/ir_Whirlpool.cpp b/lib/IRremoteESP8266/src/ir_Whirlpool.cpp deleted file mode 100644 index 0bba57bfec..0000000000 --- a/lib/IRremoteESP8266/src/ir_Whirlpool.cpp +++ /dev/null @@ -1,657 +0,0 @@ -// Copyright 2018 David Conran - -/// @file -/// @brief Support for Whirlpool protocols. -/// Decoding help from: \@redmusicxd, \@josh929800, \@raducostea -/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/509 -/// @note Smart, iFeel, AroundU, PowerSave, & Silent modes are unsupported. -/// Advanced 6thSense, Dehumidify, & Sleep modes are not supported. -/// @note Dim == !Light, Jet == Super == Turbo - -#include "ir_Whirlpool.h" -#include -#include -#ifndef ARDUINO -#include -#endif -#include "IRrecv.h" -#include "IRremoteESP8266.h" -#include "IRsend.h" -#include "IRtext.h" -#include "IRutils.h" - -// Constants -const uint16_t kWhirlpoolAcHdrMark = 8950; -const uint16_t kWhirlpoolAcHdrSpace = 4484; -const uint16_t kWhirlpoolAcBitMark = 597; -const uint16_t kWhirlpoolAcOneSpace = 1649; -const uint16_t kWhirlpoolAcZeroSpace = 533; -const uint16_t kWhirlpoolAcGap = 7920; -const uint32_t kWhirlpoolAcMinGap = kDefaultMessageGap; // Just a guess. -const uint8_t kWhirlpoolAcSections = 3; - -using irutils::addBoolToString; -using irutils::addFanToString; -using irutils::addIntToString; -using irutils::addLabeledString; -using irutils::addModeToString; -using irutils::addModelToString; -using irutils::addTempToString; -using irutils::minsToString; - -#define GETTIME(x) (_.x##Hours * 60 + _.x##Mins) -#define SETTIME(x, n) do { \ - uint16_t mins = n;\ - _.x##Hours = (mins / 60) % 24;\ - _.x##Mins = mins % 60;\ -} while (0) - -#if SEND_WHIRLPOOL_AC -/// Send a Whirlpool A/C message. -/// Status: BETA / Probably works. -/// @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::sendWhirlpoolAC(const unsigned char data[], const uint16_t nbytes, - const uint16_t repeat) { - if (nbytes < kWhirlpoolAcStateLength) - return; // Not enough bytes to send a proper message. - for (uint16_t r = 0; r <= repeat; r++) { - // Section 1 - sendGeneric(kWhirlpoolAcHdrMark, kWhirlpoolAcHdrSpace, kWhirlpoolAcBitMark, - kWhirlpoolAcOneSpace, kWhirlpoolAcBitMark, - kWhirlpoolAcZeroSpace, kWhirlpoolAcBitMark, kWhirlpoolAcGap, - data, 6, // 6 bytes == 48 bits - 38000, // Complete guess of the modulation frequency. - false, 0, 50); - // Section 2 - sendGeneric(0, 0, kWhirlpoolAcBitMark, kWhirlpoolAcOneSpace, - kWhirlpoolAcBitMark, kWhirlpoolAcZeroSpace, kWhirlpoolAcBitMark, - kWhirlpoolAcGap, data + 6, 8, // 8 bytes == 64 bits - 38000, // Complete guess of the modulation frequency. - false, 0, 50); - // Section 3 - sendGeneric(0, 0, kWhirlpoolAcBitMark, kWhirlpoolAcOneSpace, - kWhirlpoolAcBitMark, kWhirlpoolAcZeroSpace, kWhirlpoolAcBitMark, - kWhirlpoolAcMinGap, data + 14, 7, // 7 bytes == 56 bits - 38000, // Complete guess of the modulation frequency. - false, 0, 50); - } -} -#endif // SEND_WHIRLPOOL_AC - -// Class for emulating a Whirlpool A/C remote. - -/// 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? -IRWhirlpoolAc::IRWhirlpoolAc(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 IRWhirlpoolAc::stateReset(void) { - for (uint8_t i = 2; i < kWhirlpoolAcStateLength; i++) _.raw[i] = 0x0; - _.raw[0] = 0x83; - _.raw[1] = 0x06; - _.raw[6] = 0x80; - _setTemp(kWhirlpoolAcAutoTemp); // Default to a sane value. -} - -/// Set up hardware to be able to send a message. -void IRWhirlpoolAc::begin(void) { _irsend.begin(); } - -/// 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 IRWhirlpoolAc::validChecksum(const uint8_t state[], - const uint16_t length) { - if (length > kWhirlpoolAcChecksumByte1 && - state[kWhirlpoolAcChecksumByte1] != - xorBytes(state + 2, kWhirlpoolAcChecksumByte1 - 1 - 2)) { - DPRINTLN("DEBUG: First Whirlpool AC checksum failed."); - return false; - } - if (length > kWhirlpoolAcChecksumByte2 && - state[kWhirlpoolAcChecksumByte2] != - xorBytes(state + kWhirlpoolAcChecksumByte1 + 1, - kWhirlpoolAcChecksumByte2 - kWhirlpoolAcChecksumByte1 - 1)) { - DPRINTLN("DEBUG: Second Whirlpool AC checksum failed."); - return false; - } - // State is too short to have a checksum or everything checked out. - return true; -} - -/// Calculate & set the checksum for the current internal state of the remote. -/// @param[in] length The length/size of the internal state array. -void IRWhirlpoolAc::checksum(uint16_t length) { - if (length >= kWhirlpoolAcChecksumByte1) - _.Sum1 = xorBytes(_.raw + 2, kWhirlpoolAcChecksumByte1 - 1 - 2); - if (length >= kWhirlpoolAcChecksumByte2) - _.Sum2 = xorBytes(_.raw + kWhirlpoolAcChecksumByte1 + 1, - kWhirlpoolAcChecksumByte2 - kWhirlpoolAcChecksumByte1 - 1); -} - -#if SEND_WHIRLPOOL_AC -/// Send the current internal state as an IR message. -/// @param[in] repeat Nr. of times the message will be repeated. -/// @param[in] calcchecksum Do we need to calculate the checksum?. -void IRWhirlpoolAc::send(const uint16_t repeat, const bool calcchecksum) { - _irsend.sendWhirlpoolAC(getRaw(calcchecksum), kWhirlpoolAcStateLength, - repeat); -} -#endif // SEND_WHIRLPOOL_AC - -/// Get a copy of the internal state/code for this protocol. -/// @param[in] calcchecksum Do we need to calculate the checksum?. -/// @return A code for this protocol based on the current internal state. -uint8_t *IRWhirlpoolAc::getRaw(const bool calcchecksum) { - if (calcchecksum) 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 IRWhirlpoolAc::setRaw(const uint8_t new_code[], const uint16_t length) { - std::memcpy(_.raw, new_code, std::min(length, kWhirlpoolAcStateLength)); -} - -/// Get/Detect the model of the A/C. -/// @return The enum of the compatible model. -whirlpool_ac_remote_model_t IRWhirlpoolAc::getModel(void) const { - if (_.J191) - return DG11J191; - else - return DG11J13A; -} - -/// Set the model of the A/C to emulate. -/// @param[in] model The enum of the appropriate model. -void IRWhirlpoolAc::setModel(const whirlpool_ac_remote_model_t model) { - switch (model) { - case DG11J191: - _.J191 = true; - break; - case DG11J13A: - // FALL THRU - default: - _.J191 = false; - } - _setTemp(_desiredtemp); // Different models have different temp values. -} - -/// Calculate the temp. offset in deg C for the current model. -/// @return The temperature offset. -int8_t IRWhirlpoolAc::getTempOffset(void) const { - switch (getModel()) { - case whirlpool_ac_remote_model_t::DG11J191: return -2; - default: return 0; - } -} - -/// Set the temperature. -/// @param[in] temp The temperature in degrees celsius. -/// @param[in] remember Do we save this temperature? -/// @note Internal use only. -void IRWhirlpoolAc::_setTemp(const uint8_t temp, const bool remember) { - if (remember) _desiredtemp = temp; - int8_t offset = getTempOffset(); // Cache the min temp for the model. - uint8_t newtemp = std::max((uint8_t)(kWhirlpoolAcMinTemp + offset), temp); - newtemp = std::min((uint8_t)(kWhirlpoolAcMaxTemp + offset), newtemp); - _.Temp = newtemp - (kWhirlpoolAcMinTemp + offset); -} - -/// Set the temperature. -/// @param[in] temp The temperature in degrees celsius. -void IRWhirlpoolAc::setTemp(const uint8_t temp) { - _setTemp(temp); - setSuper(false); // Changing temp cancels Super/Jet mode. - _.Cmd = kWhirlpoolAcCommandTemp; -} - -/// Get the current temperature setting. -/// @return The current setting for temp. in degrees celsius. -uint8_t IRWhirlpoolAc::getTemp(void) const { - return _.Temp + kWhirlpoolAcMinTemp + getTempOffset(); -} - -/// Set the operating mode of the A/C. -/// @param[in] mode The desired operating mode. -/// @note Internal use only. -void IRWhirlpoolAc::_setMode(const uint8_t mode) { - switch (mode) { - case kWhirlpoolAcAuto: - setFan(kWhirlpoolAcFanAuto); - _setTemp(kWhirlpoolAcAutoTemp, false); - setSleep(false); // Cancel sleep mode when in auto/6thsense mode. - // FALL THRU - case kWhirlpoolAcHeat: - case kWhirlpoolAcCool: - case kWhirlpoolAcDry: - case kWhirlpoolAcFan: - _.Mode = mode; - _.Cmd = kWhirlpoolAcCommandMode; - break; - default: - return; - } - if (mode == kWhirlpoolAcAuto) _.Cmd = kWhirlpoolAcCommand6thSense; -} - -/// Set the operating mode of the A/C. -/// @param[in] mode The desired operating mode. -void IRWhirlpoolAc::setMode(const uint8_t mode) { - setSuper(false); // Changing mode cancels Super/Jet mode. - _setMode(mode); -} - -/// Get the operating mode setting of the A/C. -/// @return The current operating mode setting. -uint8_t IRWhirlpoolAc::getMode(void) const { - return _.Mode; -} - -/// Set the speed of the fan. -/// @param[in] speed The desired setting. -void IRWhirlpoolAc::setFan(const uint8_t speed) { - switch (speed) { - case kWhirlpoolAcFanAuto: - case kWhirlpoolAcFanLow: - case kWhirlpoolAcFanMedium: - case kWhirlpoolAcFanHigh: - _.Fan = speed; - setSuper(false); // Changing fan speed cancels Super/Jet mode. - _.Cmd = kWhirlpoolAcCommandFanSpeed; - break; - } -} - -/// Get the current fan speed setting. -/// @return The current fan speed/mode. -uint8_t IRWhirlpoolAc::getFan(void) const { - return _.Fan; -} - -/// Set the (vertical) swing setting of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRWhirlpoolAc::setSwing(const bool on) { - _.Swing1 = on; - _.Swing2 = on; - _.Cmd = kWhirlpoolAcCommandSwing; -} - -/// Get the (vertical) swing setting of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRWhirlpoolAc::getSwing(void) const { - return _.Swing1 && _.Swing2; -} - -/// Set the Light (Display/LED) setting of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRWhirlpoolAc::setLight(const bool on) { - // Cleared when on. - _.LightOff = !on; -} - -/// Get the Light (Display/LED) setting of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRWhirlpoolAc::getLight(void) const { - return !_.LightOff; -} - -/// Set the clock time in nr. of minutes past midnight. -/// @param[in] minspastmidnight The time expressed as minutes past midnight. -void IRWhirlpoolAc::setClock(const uint16_t minspastmidnight) { - SETTIME(Clock, minspastmidnight); -} - -/// Get the clock time in nr. of minutes past midnight. -/// @return The time expressed as the Nr. of minutes past midnight. -uint16_t IRWhirlpoolAc::getClock(void) const { - return GETTIME(Clock); -} - -/// Set the Off Timer time. -/// @param[in] minspastmidnight The time expressed as minutes past midnight. -void IRWhirlpoolAc::setOffTimer(const uint16_t minspastmidnight) { - SETTIME(Off, minspastmidnight); -} - -/// Get the Off Timer time.. -/// @return The time expressed as the Nr. of minutes past midnight. -uint16_t IRWhirlpoolAc::getOffTimer(void) const { - return GETTIME(Off); -} - -/// Is the Off timer enabled? -/// @return true, the Timer is enabled. false, the Timer is disabled. -bool IRWhirlpoolAc::isOffTimerEnabled(void) const { - return _.OffTimerEnabled; -} - -/// Enable the Off Timer. -/// @param[in] on true, the timer is enabled. false, the timer is disabled. -void IRWhirlpoolAc::enableOffTimer(const bool on) { - _.OffTimerEnabled = on; - _.Cmd = kWhirlpoolAcCommandOffTimer; -} - -/// Set the On Timer time. -/// @param[in] minspastmidnight The time expressed as minutes past midnight. -void IRWhirlpoolAc::setOnTimer(const uint16_t minspastmidnight) { - SETTIME(On, minspastmidnight); -} - -/// Get the On Timer time.. -/// @return The time expressed as the Nr. of minutes past midnight. -uint16_t IRWhirlpoolAc::getOnTimer(void) const { - return GETTIME(On); -} - -/// Is the On timer enabled? -/// @return true, the Timer is enabled. false, the Timer is disabled. -bool IRWhirlpoolAc::isOnTimerEnabled(void) const { - return _.OnTimerEnabled; -} - -/// Enable the On Timer. -/// @param[in] on true, the timer is enabled. false, the timer is disabled. -void IRWhirlpoolAc::enableOnTimer(const bool on) { - _.OnTimerEnabled = on; - _.Cmd = kWhirlpoolAcCommandOnTimer; -} - -/// Change the power toggle setting. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRWhirlpoolAc::setPowerToggle(const bool on) { - _.Power = on; - setSuper(false); // Changing power cancels Super/Jet mode. - _.Cmd = kWhirlpoolAcCommandPower; -} - -/// Get the value of the current power toggle setting. -/// @return true, the setting is on. false, the setting is off. -bool IRWhirlpoolAc::getPowerToggle(void) const { - return _.Power; -} - -/// Get the Command (Button) setting of the A/C. -/// @return The current Command (Button) of the A/C. -uint8_t IRWhirlpoolAc::getCommand(void) const { - return _.Cmd; -} - -/// Set the Sleep setting of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRWhirlpoolAc::setSleep(const bool on) { - _.Sleep = on; - if (on) setFan(kWhirlpoolAcFanLow); - _.Cmd = kWhirlpoolAcCommandSleep; -} - -/// Get the Sleep setting of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRWhirlpoolAc::getSleep(void) const { - return _.Sleep; -} - -/// Set the Super (Turbo/Jet) setting of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRWhirlpoolAc::setSuper(const bool on) { - if (on) { - setFan(kWhirlpoolAcFanHigh); - switch (_.Mode) { - case kWhirlpoolAcHeat: - setTemp(kWhirlpoolAcMaxTemp + getTempOffset()); - break; - case kWhirlpoolAcCool: - default: - setTemp(kWhirlpoolAcMinTemp + getTempOffset()); - setMode(kWhirlpoolAcCool); - break; - } - _.Super1 = 1; - _.Super2 = 1; - } else { - _.Super1 = 0; - _.Super2 = 0; - } - _.Cmd = kWhirlpoolAcCommandSuper; -} - -/// Get the Super (Turbo/Jet) setting of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRWhirlpoolAc:: getSuper(void) const { - return _.Super1 && _.Super2; -} - -/// Set the Command (Button) setting of the A/C. -/// @param[in] code The current Command (Button) of the A/C. -void IRWhirlpoolAc::setCommand(const uint8_t code) { - _.Cmd = code; -} - -/// 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 IRWhirlpoolAc::convertMode(const stdAc::opmode_t mode) { - switch (mode) { - case stdAc::opmode_t::kAuto: return kWhirlpoolAcAuto; - case stdAc::opmode_t::kHeat: return kWhirlpoolAcHeat; - case stdAc::opmode_t::kDry: return kWhirlpoolAcDry; - case stdAc::opmode_t::kFan: return kWhirlpoolAcFan; - // Default to Cool as some Whirlpool models don't have an Auto mode. - // Ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/1283 - default: return kWhirlpoolAcCool; - } -} - -/// 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 IRWhirlpoolAc::convertFan(const stdAc::fanspeed_t speed) { - switch (speed) { - case stdAc::fanspeed_t::kMin: - case stdAc::fanspeed_t::kLow: return kWhirlpoolAcFanLow; - case stdAc::fanspeed_t::kMedium: return kWhirlpoolAcFanMedium; - case stdAc::fanspeed_t::kHigh: - case stdAc::fanspeed_t::kMax: return kWhirlpoolAcFanHigh; - default: return kWhirlpoolAcFanAuto; - } -} - -/// 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 IRWhirlpoolAc::toCommonMode(const uint8_t mode) { - switch (mode) { - case kWhirlpoolAcCool: return stdAc::opmode_t::kCool; - case kWhirlpoolAcHeat: return stdAc::opmode_t::kHeat; - case kWhirlpoolAcDry: return stdAc::opmode_t::kDry; - case kWhirlpoolAcFan: 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 IRWhirlpoolAc::toCommonFanSpeed(const uint8_t speed) { - switch (speed) { - case kWhirlpoolAcFanHigh: return stdAc::fanspeed_t::kMax; - case kWhirlpoolAcFanMedium: return stdAc::fanspeed_t::kMedium; - case kWhirlpoolAcFanLow: 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 Ptr to the previous state if required. -/// @return The stdAc equivalent of the native settings. -stdAc::state_t IRWhirlpoolAc::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::WHIRLPOOL_AC; - result.model = getModel(); - if (_.Power) result.power = !result.power; - result.mode = toCommonMode(_.Mode); - result.celsius = true; - result.degrees = getTemp(); - result.fanspeed = toCommonFanSpeed(_.Fan); - result.swingv = getSwing() ? stdAc::swingv_t::kAuto : stdAc::swingv_t::kOff; - result.turbo = getSuper(); - result.light = getLight(); - result.sleep = _.Sleep ? 0 : -1; - // Not supported. - result.swingh = stdAc::swingh_t::kOff; - result.quiet = false; - result.filter = false; - result.econo = 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 IRWhirlpoolAc::toString(void) const { - String result = ""; - result.reserve(200); // Reserve some heap for the string to reduce fragging. - result += addModelToString(decode_type_t::WHIRLPOOL_AC, getModel(), false); - result += addBoolToString(_.Power, kPowerToggleStr); - result += addModeToString(_.Mode, kWhirlpoolAcAuto, kWhirlpoolAcCool, - kWhirlpoolAcHeat, kWhirlpoolAcDry, kWhirlpoolAcFan); - result += addTempToString(getTemp()); - result += addFanToString(_.Fan, kWhirlpoolAcFanHigh, kWhirlpoolAcFanLow, - kWhirlpoolAcFanAuto, kWhirlpoolAcFanAuto, - kWhirlpoolAcFanMedium); - result += addBoolToString(getSwing(), kSwingStr); - result += addBoolToString(getLight(), kLightStr); - result += addLabeledString(minsToString(getClock()), kClockStr); - result += addLabeledString( - _.OnTimerEnabled ? minsToString(getOnTimer()) : kOffStr, kOnTimerStr); - result += addLabeledString( - _.OffTimerEnabled ? minsToString(getOffTimer()) : kOffStr, kOffTimerStr); - result += addBoolToString(_.Sleep, kSleepStr); - result += addBoolToString(getSuper(), kSuperStr); - result += addIntToString(_.Cmd, kCommandStr); - result += kSpaceLBraceStr; - switch (_.Cmd) { - case kWhirlpoolAcCommandLight: - result += kLightStr; - break; - case kWhirlpoolAcCommandPower: - result += kPowerStr; - break; - case kWhirlpoolAcCommandTemp: - result += kTempStr; - break; - case kWhirlpoolAcCommandSleep: - result += kSleepStr; - break; - case kWhirlpoolAcCommandSuper: - result += kSuperStr; - break; - case kWhirlpoolAcCommandOnTimer: - result += kOnTimerStr; - break; - case kWhirlpoolAcCommandMode: - result += kModeStr; - break; - case kWhirlpoolAcCommandSwing: - result += kSwingStr; - break; - case kWhirlpoolAcCommandIFeel: - result += kIFeelStr; - break; - case kWhirlpoolAcCommandFanSpeed: - result += kFanStr; - break; - case kWhirlpoolAcCommand6thSense: - result += k6thSenseStr; - break; - case kWhirlpoolAcCommandOffTimer: - result += kOffTimerStr; - break; - default: - result += kUnknownStr; - break; - } - result += ')'; - return result; -} - -#if DECODE_WHIRLPOOL_AC - -/// Decode the supplied Whirlpool A/C message. -/// Status: STABLE / Working as intended. -/// @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::decodeWhirlpoolAC(decode_results *results, uint16_t offset, - const uint16_t nbits, const bool strict) { - if (results->rawlen < 2 * nbits + 4 + kHeader + kFooter - 1 + offset) - return false; // Can't possibly be a valid Whirlpool A/C message. - if (strict) { - if (nbits != kWhirlpoolAcBits) return false; - } - - const uint8_t sectionSize[kWhirlpoolAcSections] = {6, 8, 7}; - - // Header - if (!matchMark(results->rawbuf[offset++], kWhirlpoolAcHdrMark)) return false; - if (!matchSpace(results->rawbuf[offset++], kWhirlpoolAcHdrSpace)) - return false; - - // Data Sections - uint16_t pos = 0; - for (uint8_t section = 0; section < kWhirlpoolAcSections; - section++) { - uint16_t used; - // Section Data - used = matchGeneric(results->rawbuf + offset, results->state + pos, - results->rawlen - offset, sectionSize[section] * 8, - 0, 0, - kWhirlpoolAcBitMark, kWhirlpoolAcOneSpace, - kWhirlpoolAcBitMark, kWhirlpoolAcZeroSpace, - kWhirlpoolAcBitMark, kWhirlpoolAcGap, - section >= kWhirlpoolAcSections - 1, - _tolerance, kMarkExcess, false); - if (used == 0) return false; - offset += used; - pos += sectionSize[section]; - } - - // Compliance - if (strict) { - // Re-check we got the correct size/length due to the way we read the data. - if (pos * 8 != nbits) return false; - if (!IRWhirlpoolAc::validChecksum(results->state, nbits / 8)) - return false; - } - - // Success - results->decode_type = WHIRLPOOL_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 // WHIRLPOOL_AC diff --git a/lib/IRremoteESP8266/src/ir_Whirlpool.h b/lib/IRremoteESP8266/src/ir_Whirlpool.h index ac73be926b..87e7105068 100644 --- a/lib/IRremoteESP8266/src/ir_Whirlpool.h +++ b/lib/IRremoteESP8266/src/ir_Whirlpool.h @@ -26,10 +26,10 @@ #ifndef UNIT_TEST #include #endif -#include "IRremoteESP8266.h" -#include "IRsend.h" +#include "../src/IRremoteESP8266.h" +#include "../src/IRsend.h" #ifdef UNIT_TEST -#include "IRsend_test.h" +#include "../src/IRsend_test.h" #endif /// Native representation of a Whirlpool A/C message. diff --git a/lib/IRremoteESP8266/src/ir_Whynter.cpp b/lib/IRremoteESP8266/src/ir_Whynter.cpp deleted file mode 100644 index 7b184eb0be..0000000000 --- a/lib/IRremoteESP8266/src/ir_Whynter.cpp +++ /dev/null @@ -1,103 +0,0 @@ -// Copyright 2009 Ken Shirriff -// Copyright 2017 David Conran - -/// @file -/// @brief Support for Whynter protocols. -/// Whynter A/C ARC-110WD added by Francesco Meschia -/// Whynter originally added from https://github.com/shirriff/Arduino-IRremote/ - -// Supports: -// Brand: Whynter, Model: ARC-110WD A/C - -#include -#include "IRrecv.h" -#include "IRsend.h" -#include "IRutils.h" - -// Constants -const uint16_t kWhynterTick = 50; -const uint16_t kWhynterHdrMarkTicks = 57; -const uint16_t kWhynterHdrMark = kWhynterHdrMarkTicks * kWhynterTick; -const uint16_t kWhynterHdrSpaceTicks = 57; -const uint16_t kWhynterHdrSpace = kWhynterHdrSpaceTicks * kWhynterTick; -const uint16_t kWhynterBitMarkTicks = 15; -const uint16_t kWhynterBitMark = kWhynterBitMarkTicks * kWhynterTick; -const uint16_t kWhynterOneSpaceTicks = 43; -const uint16_t kWhynterOneSpace = kWhynterOneSpaceTicks * kWhynterTick; -const uint16_t kWhynterZeroSpaceTicks = 15; -const uint16_t kWhynterZeroSpace = kWhynterZeroSpaceTicks * kWhynterTick; -const uint16_t kWhynterMinCommandLengthTicks = 2160; // Totally made up value. -const uint32_t kWhynterMinCommandLength = - kWhynterMinCommandLengthTicks * kWhynterTick; -const uint16_t kWhynterMinGapTicks = - kWhynterMinCommandLengthTicks - - (2 * (kWhynterBitMarkTicks + kWhynterZeroSpaceTicks) + - kWhynterBits * (kWhynterBitMarkTicks + kWhynterOneSpaceTicks)); -const uint16_t kWhynterMinGap = kWhynterMinGapTicks * kWhynterTick; - -#if SEND_WHYNTER -/// Send a Whynter message. -/// Status: STABLE -/// @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_Whynter.cpp -void IRsend::sendWhynter(const uint64_t data, const uint16_t nbits, - const uint16_t repeat) { - // Set IR carrier frequency - enableIROut(38); - - for (uint16_t i = 0; i <= repeat; i++) { - // (Pre-)Header - mark(kWhynterBitMark); - space(kWhynterZeroSpace); - sendGeneric( - kWhynterHdrMark, kWhynterHdrSpace, kWhynterBitMark, kWhynterOneSpace, - kWhynterBitMark, kWhynterZeroSpace, kWhynterBitMark, kWhynterMinGap, - kWhynterMinCommandLength - (kWhynterBitMark + kWhynterZeroSpace), data, - nbits, 38, true, 0, // Repeats are already handled. - 50); - } -} -#endif // SEND_WHYNTER - -#if DECODE_WHYNTER -/// Decode the supplied Whynter message. -/// Status: STABLE / Working. Strict mode is ALPHA. -/// @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/z3t0/Arduino-IRremote/blob/master/ir_Whynter.cpp -bool IRrecv::decodeWhynter(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; // We don't have enough entries to possibly match. - - // Compliance - if (strict && nbits != kWhynterBits) - return false; // Incorrect nr. of bits per spec. - - uint64_t data = 0; - // Pre-Header - // Sequence begins with a bit mark and a zero space. - if (!matchMark(results->rawbuf[offset++], kWhynterBitMark)) return false; - if (!matchSpace(results->rawbuf[offset++], kWhynterZeroSpace)) return false; - // Match Main Header + Data + Footer - if (!matchGeneric(results->rawbuf + offset, &data, - results->rawlen - offset, nbits, - kWhynterHdrMark, kWhynterHdrSpace, - kWhynterBitMark, kWhynterOneSpace, - kWhynterBitMark, kWhynterZeroSpace, - kWhynterBitMark, kWhynterMinGap, true)) return false; - // Success - results->decode_type = WHYNTER; - results->bits = nbits; - results->value = data; - results->address = 0; - results->command = 0; - return true; -} -#endif // DECODE_WHYNTER diff --git a/lib/IRremoteESP8266/src/ir_Xmp.cpp b/lib/IRremoteESP8266/src/ir_Xmp.cpp deleted file mode 100644 index 29d7f6c1bb..0000000000 --- a/lib/IRremoteESP8266/src/ir_Xmp.cpp +++ /dev/null @@ -1,226 +0,0 @@ -// Copyright 2021 David Conran - -/// @file -/// @brief Support for XMP protocols. -/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1414 -/// @see http://www.hifi-remote.com/wiki/index.php/XMP - -// Supports: -// Brand: Xfinity, Model: XR2 remote -// Brand: Xfinity, Model: XR11 remote - - -#include -#include "IRrecv.h" -#include "IRsend.h" -#include "IRutils.h" - -// Constants -const uint16_t kXmpMark = 210; ///< uSeconds. -const uint16_t kXmpBaseSpace = 760; ///< uSeconds -const uint16_t kXmpSpaceStep = 135; ///< uSeconds -const uint16_t kXmpFooterSpace = 13000; ///< uSeconds. -const uint32_t kXmpMessageGap = 80400; ///< uSeconds. -const uint8_t kXmpWordSize = kNibbleSize; ///< nr. of Bits in a word. -const uint8_t kXmpMaxWordValue = (1 << kXmpWordSize) - 1; // Max word value. -const uint8_t kXmpSections = 2; ///< Nr. of Data sections -const uint8_t kXmpRepeatCode = 0b1000; -const uint8_t kXmpRepeatCodeAlt = 0b1001; - -using irutils::setBits; - -namespace IRXmpUtils { - /// Get the current checksum value from an XMP data section. - /// @param[in] data The value of the data section. - /// @param[in] nbits The number of data bits in the section. - /// @return The value of the stored checksum. - /// @warning Returns 0 if we can't obtain a valid checksum. - uint8_t getSectionChecksum(const uint32_t data, const uint16_t nbits) { - // The checksum is the 2nd most significant nibble of a section. - return (nbits < 2 * kNibbleSize) ? 0 : GETBITS32(data, - nbits - (2 * kNibbleSize), - kNibbleSize); - } - - /// Calculate the correct checksum value for an XMP data section. - /// @param[in] data The value of the data section. - /// @param[in] nbits The number of data bits in the section. - /// @return The value of the correct checksum. - uint8_t calcSectionChecksum(const uint32_t data, const uint16_t nbits) { - return (0xF & ~(irutils::sumNibbles(data, nbits / kNibbleSize, 0xF, false) - - getSectionChecksum(data, nbits))); - } - - /// Recalculate a XMP message code ensuring it has the checksums valid. - /// @param[in] data The value of the XMP message code. - /// @param[in] nbits The number of data bits in the entire message code. - /// @return The corrected XMP message with valid checksum sections. - uint64_t updateChecksums(const uint64_t data, const uint16_t nbits) { - const uint16_t sectionbits = nbits / kXmpSections; - uint64_t result = data; - for (uint16_t sectionOffset = 0; sectionOffset < nbits; - sectionOffset += sectionbits) { - const uint16_t checksumOffset = sectionOffset + sectionbits - - (2 * kNibbleSize); - setBits(&result, checksumOffset, kNibbleSize, - calcSectionChecksum(GETBITS64(data, sectionOffset, sectionbits), - sectionbits)); - } - return result; - } - - /// Calculate the bit offset the repeat nibble in an XMP code. - /// @param[in] nbits The number of data bits in the entire message code. - /// @return The offset to the start of the XMP repeat nibble. - uint16_t calcRepeatOffset(const uint16_t nbits) { - return (nbits < 3 * kNibbleSize) ? 0 - : (nbits / kXmpSections) - - (3 * kNibbleSize); - } - - /// Test if an XMP message code is a repeat or not. - /// @param[in] data The value of the XMP message code. - /// @param[in] nbits The number of data bits in the entire message code. - /// @return true, if it looks like a repeat, false if not. - bool isRepeat(const uint64_t data, const uint16_t nbits) { - switch (GETBITS64(data, calcRepeatOffset(nbits), kNibbleSize)) { - case kXmpRepeatCode: - case kXmpRepeatCodeAlt: - return true; - default: - return false; - } - } - - /// Adjust an XMP message code to make it a valid repeat or non-repeat code. - /// @param[in] data The value of the XMP message code. - /// @param[in] nbits The number of data bits in the entire message code. - /// @param[in] repeat_code The value of the XMP repeat nibble to use. - /// A value of `8` is the normal value for a repeat. `9` has also been seen. - /// A value of `0` will convert the code to a non-repeat code. - /// @return The valud of the modified XMP code. - uint64_t adjustRepeat(const uint64_t data, const uint16_t nbits, - const uint8_t repeat_code) { - uint64_t result = data; - setBits(&result, calcRepeatOffset(nbits), kNibbleSize, repeat_code); - return updateChecksums(result, nbits); - } -} // namespace IRXmpUtils - -using IRXmpUtils::calcSectionChecksum; -using IRXmpUtils::getSectionChecksum; -using IRXmpUtils::isRepeat; -using IRXmpUtils::adjustRepeat; - - -#if SEND_XMP -/// Send a XMP packet. -/// Status: Beta / Untested 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::sendXmp(const uint64_t data, const uint16_t nbits, - const uint16_t repeat) { - enableIROut(38000); - if (nbits < 2 * kXmpWordSize) return; // Too small to send, abort! - uint64_t send_data = data; - for (uint16_t r = 0; r <= repeat; r++) { - uint16_t bits_so_far = kXmpWordSize; - for (uint64_t mask = ((uint64_t)kXmpMaxWordValue) << (nbits - kXmpWordSize); - mask; - mask >>= kXmpWordSize) { - uint8_t word = (send_data & mask) >> (nbits - bits_so_far); - mark(kXmpMark); - space(kXmpBaseSpace + word * kXmpSpaceStep); - bits_so_far += kXmpWordSize; - // Are we at a data section boundary? - if ((bits_so_far - kXmpWordSize) % (nbits / kXmpSections) == 0) { // Yes. - mark(kXmpMark); - space(kXmpFooterSpace); - } - } - space(kXmpMessageGap - kXmpFooterSpace); - - // Modify the value if needed, to make it into a valid repeat code. - if (!isRepeat(send_data, nbits)) - send_data = adjustRepeat(send_data, nbits, kXmpRepeatCode); - } -} -#endif // SEND_XMP - -#if DECODE_XMP -/// Decode the supplied XMP packet/message. -/// Status: BETA / Probably works. -/// @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::decodeXmp(decode_results *results, uint16_t offset, - const uint16_t nbits, const bool strict) { - uint64_t data = 0; - - if (results->rawlen < 2 * (nbits / kXmpWordSize) + (kXmpSections * kFooter) + - offset - 1) - return false; // Not enough entries to ever be XMP. - - // Compliance - if (strict && nbits != kXmpBits) return false; - - // Data - // Sections - for (uint8_t section = 1; section <= kXmpSections; section++) { - for (uint16_t bits_so_far = 0; bits_so_far < nbits / kXmpSections; - bits_so_far += kXmpWordSize) { - if (!matchMarkRange(results->rawbuf[offset++], kXmpMark)) return 0; - uint8_t value = 0; - bool found = false; - for (; value <= kXmpMaxWordValue; value++) { - if (matchSpaceRange(results->rawbuf[offset], - kXmpBaseSpace + value * kXmpSpaceStep, - kXmpSpaceStep / 2, 0)) { - found = true; - break; - } - } - if (!found) return 0; // Failure. - data <<= kXmpWordSize; - data += value; - offset++; - } - // Section Footer - if (!matchMarkRange(results->rawbuf[offset++], kXmpMark)) return 0; - if (section < kXmpSections) { - if (!matchSpace(results->rawbuf[offset++], kXmpFooterSpace)) return 0; - } else { // Last section - if (offset < results->rawlen && - !matchAtLeast(results->rawbuf[offset++], kXmpFooterSpace)) return 0; - } - } - - // Compliance - if (strict) { - // Validate checksums. - uint64_t checksum_data = data; - const uint16_t section_size = nbits / kXmpSections; - // Each section has a checksum. - for (uint16_t section = 0; section < kXmpSections; section++) { - if (getSectionChecksum(checksum_data, section_size) != - calcSectionChecksum(checksum_data, section_size)) - return 0; - checksum_data >>= section_size; - } - } - - // Success - results->value = data; - results->decode_type = decode_type_t::XMP; - results->bits = nbits; - results->address = 0; - results->command = 0; - // See if it is a repeat message. - results->repeat = isRepeat(data, nbits); - return true; -} -#endif // DECODE_XMP diff --git a/lib/IRremoteESP8266/src/ir_Zepeal.cpp b/lib/IRremoteESP8266/src/ir_Zepeal.cpp deleted file mode 100644 index 96017226c3..0000000000 --- a/lib/IRremoteESP8266/src/ir_Zepeal.cpp +++ /dev/null @@ -1,94 +0,0 @@ -// Copyright 2020 Christian Nilsson (nikize) - -/// @file -/// @brief Support for Zepeal protocol. -/// This protocol uses fixed length bit encoding. -/// Most official information about Zepeal seems to be from Denkyosha -/// @see https://www.denkyosha.co.jp/ - -// Supports: -// Brand: Zepeal, Model: DRT-A3311(BG) floor fan -// Brand: Zepeal, Model: DRT-A3311(BG) 5 button remote - -#include "IRrecv.h" -#include "IRsend.h" -#include "IRutils.h" - -// Constants - -const uint16_t kZepealHdrMark = 2330; -const uint16_t kZepealHdrSpace = 3380; -const uint16_t kZepealOneMark = 1300; -const uint16_t kZepealZeroMark = 420; -const uint16_t kZepealOneSpace = kZepealZeroMark; -const uint16_t kZepealZeroSpace = kZepealOneMark; -const uint16_t kZepealFooterMark = 420; -const uint16_t kZepealGap = 6750; - -const uint8_t kZepealTolerance = 40; - -// Signature limits possible false possitvies, -// but might need change (removal) if more devices are detected -const uint8_t kZepealSignature = 0x6C; - -// Known Zepeal DRT-A3311(BG) Buttons - documentation rather than actual usage -const uint16_t kZepealCommandSpeed = 0x6C82; -const uint16_t kZepealCommandOffOn = 0x6C81; -const uint16_t kZepealCommandRhythm = 0x6C84; -const uint16_t kZepealCommandOffTimer = 0x6C88; -const uint16_t kZepealCommandOnTimer = 0x6CC3; - -#if SEND_ZEPEAL -/// Send a Zepeal formatted message. -/// Status: STABLE / Works on 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::sendZepeal(const uint64_t data, const uint16_t nbits, - const uint16_t repeat) { - sendGeneric(kZepealHdrMark, kZepealHdrSpace, - kZepealOneMark, kZepealOneSpace, - kZepealZeroMark, kZepealZeroSpace, - kZepealFooterMark, kZepealGap, - data, nbits, 38, true, repeat, kDutyDefault); -} -#endif // SEND_ZEPEAL - -#if DECODE_ZEPEAL -/// Decode the supplied Zepeal 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. Typically kZepealBits. -/// @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::decodeZepeal(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 != kZepealBits) - return false; // Not strictly a message. - - uint64_t data = 0; - uint16_t used; - used = matchGeneric(results->rawbuf + offset, &data, - results->rawlen - offset, nbits, - kZepealHdrMark, kZepealHdrSpace, - kZepealOneMark, kZepealOneSpace, - kZepealZeroMark, kZepealZeroSpace, - kZepealFooterMark, kZepealGap, true, - kZepealTolerance); - if (!used) return false; - if (strict && (data >> 8) != kZepealSignature) return false; - - // Success - results->value = data; - results->decode_type = decode_type_t::ZEPEAL; - results->bits = nbits; - results->address = 0; - results->command = 0; - return true; -} -#endif // DECODE_ZEPEAL diff --git a/lib/IRremoteESP8266/test/IRac_test.cpp b/lib/IRremoteESP8266/test/IRac_test.cpp index b2eff6efa7..6a2ddd692a 100644 --- a/lib/IRremoteESP8266/test/IRac_test.cpp +++ b/lib/IRremoteESP8266/test/IRac_test.cpp @@ -1,44 +1,44 @@ // Copyright 2019-2021 David Conran #include -#include "ir_Airwell.h" -#include "ir_Amcor.h" -#include "ir_Argo.h" -#include "ir_Carrier.h" -#include "ir_Coolix.h" -#include "ir_Corona.h" -#include "ir_Daikin.h" -#include "ir_Delonghi.h" -#include "ir_Ecoclim.h" -#include "ir_Electra.h" -#include "ir_Fujitsu.h" -#include "ir_Goodweather.h" -#include "ir_Gree.h" -#include "ir_Haier.h" -#include "ir_Hitachi.h" -#include "ir_Kelvinator.h" -#include "ir_LG.h" -#include "ir_Midea.h" -#include "ir_Mitsubishi.h" -#include "ir_MitsubishiHeavy.h" -#include "ir_Neoclima.h" -#include "ir_Panasonic.h" -#include "ir_Samsung.h" -#include "ir_Sharp.h" -#include "ir_Tcl.h" -#include "ir_Teco.h" -#include "ir_Toshiba.h" -#include "ir_Trotec.h" -#include "ir_Truma.h" -#include "ir_Vestel.h" -#include "ir_Voltas.h" -#include "ir_Whirlpool.h" -#include "IRac.h" -#include "IRrecv.h" -#include "IRrecv_test.h" -#include "IRremoteESP8266.h" -#include "IRsend.h" -#include "IRsend_test.h" +#include "../src/ir_Airwell.h" +#include "../src/ir_Amcor.h" +#include "../src/ir_Argo.h" +#include "../src/ir_Carrier.h" +#include "../src/ir_Coolix.h" +#include "../src/ir_Corona.h" +#include "../src/ir_Daikin.h" +#include "../src/ir_Delonghi.h" +#include "../src/ir_Ecoclim.h" +#include "../src/ir_Electra.h" +#include "../src/ir_Fujitsu.h" +#include "../src/ir_Goodweather.h" +#include "../src/ir_Gree.h" +#include "../src/ir_Haier.h" +#include "../src/ir_Hitachi.h" +#include "../src/ir_Kelvinator.h" +#include "../src/ir_LG.h" +#include "../src/ir_Midea.h" +#include "../src/ir_Mitsubishi.h" +#include "../src/ir_MitsubishiHeavy.h" +#include "../src/ir_Neoclima.h" +#include "../src/ir_Panasonic.h" +#include "../src/ir_Samsung.h" +#include "../src/ir_Sharp.h" +#include "../src/ir_Tcl.h" +#include "../src/ir_Teco.h" +#include "../src/ir_Toshiba.h" +#include "../src/ir_Trotec.h" +#include "../src/ir_Truma.h" +#include "../src/ir_Vestel.h" +#include "../src/ir_Voltas.h" +#include "../src/ir_Whirlpool.h" +#include "../src/IRac.h" +#include "../src/IRrecv.h" +#include "../src/IRrecv_test.h" +#include "../src/IRremoteESP8266.h" +#include "../src/IRsend.h" +#include "../src/IRsend_test.h" #include "gtest/gtest.h" // Tests for IRac class. diff --git a/lib/IRremoteESP8266/test/IRrecv_test.cpp b/lib/IRremoteESP8266/test/IRrecv_test.cpp index 2d32847d38..aada6aa82b 100644 --- a/lib/IRremoteESP8266/test/IRrecv_test.cpp +++ b/lib/IRremoteESP8266/test/IRrecv_test.cpp @@ -1,10 +1,10 @@ // Copyright 2017 David Conran -#include "IRrecv_test.h" -#include "IRrecv.h" -#include "IRremoteESP8266.h" -#include "IRsend.h" -#include "IRsend_test.h" +#include "../src/IRrecv_test.h" +#include "../src/IRrecv.h" +#include "../src/IRremoteESP8266.h" +#include "../src/IRsend.h" +#include "../src/IRsend_test.h" #include "gtest/gtest.h" // Tests for the IRrecv object. diff --git a/lib/IRremoteESP8266/test/IRrecv_test.h b/lib/IRremoteESP8266/test/IRrecv_test.h index bb366c1ee0..e4e0e70954 100644 --- a/lib/IRremoteESP8266/test/IRrecv_test.h +++ b/lib/IRremoteESP8266/test/IRrecv_test.h @@ -6,7 +6,7 @@ #include #include #include -#include "IRutils.h" +#include "../src/IRutils.h" #define EXPECT_STATE_EQ(a, b, c) \ for (uint8_t i = 0; i < c / 8; ++i) { \ diff --git a/lib/IRremoteESP8266/test/IRsend_test.cpp b/lib/IRremoteESP8266/test/IRsend_test.cpp index 51fae74996..ffd230d2b1 100644 --- a/lib/IRremoteESP8266/test/IRsend_test.cpp +++ b/lib/IRremoteESP8266/test/IRsend_test.cpp @@ -1,9 +1,9 @@ // Copyright 2017,2019 David Conran -#include "IRsend_test.h" -#include "IRrecv_test.h" -#include "IRsend.h" -#include "IRutils.h" +#include "../src/IRsend_test.h" +#include "../src/IRrecv_test.h" +#include "../src/IRsend.h" +#include "../src/IRutils.h" #include "gtest/gtest.h" // Tests sendData(). diff --git a/lib/IRremoteESP8266/test/IRsend_test.h b/lib/IRremoteESP8266/test/IRsend_test.h index f434094332..ec900a44e9 100644 --- a/lib/IRremoteESP8266/test/IRsend_test.h +++ b/lib/IRremoteESP8266/test/IRsend_test.h @@ -8,9 +8,9 @@ #include #include #include -#include "IRrecv.h" -#include "IRsend.h" -#include "IRtimer.h" +#include "../src/IRrecv.h" +#include "../src/IRsend.h" +#include "../src/IRtimer.h" #define OUTPUT_BUF 10000U #define RAW_BUF 10000U diff --git a/lib/IRremoteESP8266/test/IRutils_test.cpp b/lib/IRremoteESP8266/test/IRutils_test.cpp index 73ba569991..8f398f7650 100644 --- a/lib/IRremoteESP8266/test/IRutils_test.cpp +++ b/lib/IRremoteESP8266/test/IRutils_test.cpp @@ -1,11 +1,11 @@ // Copyright 2017-2019 David Conran -#include "IRutils.h" +#include "../src/IRutils.h" #include -#include "IRrecv.h" -#include "IRrecv_test.h" -#include "IRsend.h" -#include "IRsend_test.h" +#include "../src/IRrecv.h" +#include "../src/IRrecv_test.h" +#include "../src/IRsend.h" +#include "../src/IRsend_test.h" #include "gtest/gtest.h" // Tests reverseBits(). diff --git a/lib/IRremoteESP8266/test/ir_Airwell_test.cpp b/lib/IRremoteESP8266/test/ir_Airwell_test.cpp index e5f28d4df4..c9d14f36dd 100644 --- a/lib/IRremoteESP8266/test/ir_Airwell_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Airwell_test.cpp @@ -1,11 +1,11 @@ // Copyright 2020 David Conran -#include "ir_Airwell.h" -#include "IRac.h" -#include "IRrecv.h" -#include "IRrecv_test.h" -#include "IRsend.h" -#include "IRsend_test.h" +#include "../src/ir_Airwell.h" +#include "../src/IRac.h" +#include "../src/IRrecv.h" +#include "../src/IRrecv_test.h" +#include "../src/IRsend.h" +#include "../src/IRsend_test.h" #include "gtest/gtest.h" // Tests for decodeAirwell(). diff --git a/lib/IRremoteESP8266/test/ir_Aiwa_test.cpp b/lib/IRremoteESP8266/test/ir_Aiwa_test.cpp index f87d5c5e28..78e3e33b03 100644 --- a/lib/IRremoteESP8266/test/ir_Aiwa_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Aiwa_test.cpp @@ -1,7 +1,7 @@ // Copyright 2017 David Conran -#include "IRsend.h" -#include "IRsend_test.h" +#include "../src/IRsend.h" +#include "../src/IRsend_test.h" #include "gtest/gtest.h" // Tests for sendAiwaRCT501(). diff --git a/lib/IRremoteESP8266/test/ir_Amcor_test.cpp b/lib/IRremoteESP8266/test/ir_Amcor_test.cpp index 787769516d..e8ba1eb3bc 100644 --- a/lib/IRremoteESP8266/test/ir_Amcor_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Amcor_test.cpp @@ -1,12 +1,12 @@ // Copyright 2019 David Conran -#include "IRac.h" -#include "ir_Amcor.h" -#include "IRrecv.h" -#include "IRrecv_test.h" -#include "IRsend.h" -#include "IRsend_test.h" -#include "IRutils.h" +#include "../src/IRac.h" +#include "../src/ir_Amcor.h" +#include "../src/IRrecv.h" +#include "../src/IRrecv_test.h" +#include "../src/IRsend.h" +#include "../src/IRsend_test.h" +#include "../src/IRutils.h" #include "gtest/gtest.h" TEST(TestUtils, Housekeeping) { diff --git a/lib/IRremoteESP8266/test/ir_Argo_test.cpp b/lib/IRremoteESP8266/test/ir_Argo_test.cpp index 3ab890adca..ea8e16ab7d 100644 --- a/lib/IRremoteESP8266/test/ir_Argo_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Argo_test.cpp @@ -1,10 +1,10 @@ // Copyright 2019 David Conran -#include "ir_Argo.h" -#include "IRac.h" -#include "IRrecv.h" -#include "IRrecv_test.h" -#include "IRsend.h" -#include "IRsend_test.h" +#include "../src/ir_Argo.h" +#include "../src/IRac.h" +#include "../src/IRrecv.h" +#include "../src/IRrecv_test.h" +#include "../src/IRsend.h" +#include "../src/IRsend_test.h" #include "gtest/gtest.h" diff --git a/lib/IRremoteESP8266/test/ir_Arris_test.cpp b/lib/IRremoteESP8266/test/ir_Arris_test.cpp index 2a219b6b22..ab30f3046c 100644 --- a/lib/IRremoteESP8266/test/ir_Arris_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Arris_test.cpp @@ -1,10 +1,10 @@ // Copyright 2021 David Conran -#include "IRac.h" -#include "IRrecv.h" -#include "IRrecv_test.h" -#include "IRsend.h" -#include "IRsend_test.h" +#include "../src/IRac.h" +#include "../src/IRrecv.h" +#include "../src/IRrecv_test.h" +#include "../src/IRsend.h" +#include "../src/IRsend_test.h" #include "gtest/gtest.h" // Tests for decodeArris(). diff --git a/lib/IRremoteESP8266/test/ir_Bose_test.cpp b/lib/IRremoteESP8266/test/ir_Bose_test.cpp index bf3e18c910..80b3204fe8 100644 --- a/lib/IRremoteESP8266/test/ir_Bose_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Bose_test.cpp @@ -1,11 +1,11 @@ // Copyright 2021 parsnip42 // Copyright 2021 David Conran -#include "IRac.h" -#include "IRrecv.h" -#include "IRrecv_test.h" -#include "IRsend.h" -#include "IRsend_test.h" +#include "../src/IRac.h" +#include "../src/IRrecv.h" +#include "../src/IRrecv_test.h" +#include "../src/IRsend.h" +#include "../src/IRsend_test.h" #include "gtest/gtest.h" diff --git a/lib/IRremoteESP8266/test/ir_Carrier_test.cpp b/lib/IRremoteESP8266/test/ir_Carrier_test.cpp index caf7e46d84..10cc435aab 100644 --- a/lib/IRremoteESP8266/test/ir_Carrier_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Carrier_test.cpp @@ -1,10 +1,10 @@ // Copyright 2018, 2020 David Conran -#include "ir_Carrier.h" -#include "IRac.h" -#include "IRrecv.h" -#include "IRsend.h" -#include "IRsend_test.h" +#include "../src/ir_Carrier.h" +#include "../src/IRac.h" +#include "../src/IRrecv.h" +#include "../src/IRsend.h" +#include "../src/IRsend_test.h" #include "gtest/gtest.h" // Tests for sendCarrierAC() diff --git a/lib/IRremoteESP8266/test/ir_Coolix_test.cpp b/lib/IRremoteESP8266/test/ir_Coolix_test.cpp index 46c5548820..ada4caf450 100644 --- a/lib/IRremoteESP8266/test/ir_Coolix_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Coolix_test.cpp @@ -1,9 +1,9 @@ // Copyright 2017-2018 David Conran -#include "ir_Coolix.h" -#include "IRac.h" -#include "IRsend.h" -#include "IRsend_test.h" +#include "../src/ir_Coolix.h" +#include "../src/IRac.h" +#include "../src/IRsend.h" +#include "../src/IRsend_test.h" #include "gtest/gtest.h" // Tests for sendCOOLIX(). diff --git a/lib/IRremoteESP8266/test/ir_Corona_test.cpp b/lib/IRremoteESP8266/test/ir_Corona_test.cpp index 9c16e1f963..72c7758ea0 100644 --- a/lib/IRremoteESP8266/test/ir_Corona_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Corona_test.cpp @@ -1,11 +1,11 @@ // Copyright 2020 Christian Nilsson -#include "ir_Corona.h" -#include "IRac.h" -#include "IRrecv.h" -#include "IRrecv_test.h" -#include "IRsend.h" -#include "IRsend_test.h" +#include "../src/ir_Corona.h" +#include "../src/IRac.h" +#include "../src/IRrecv.h" +#include "../src/IRrecv_test.h" +#include "../src/IRsend.h" +#include "../src/IRsend_test.h" #include "gtest/gtest.h" // Tests for decodeCoronaAc(). diff --git a/lib/IRremoteESP8266/test/ir_Daikin_test.cpp b/lib/IRremoteESP8266/test/ir_Daikin_test.cpp index 783bd4e66d..eadb508ccd 100644 --- a/lib/IRremoteESP8266/test/ir_Daikin_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Daikin_test.cpp @@ -1,10 +1,10 @@ // Copyright 2017-2019 David Conran -#include "ir_Daikin.h" -#include "IRac.h" -#include "IRrecv.h" -#include "IRrecv_test.h" -#include "IRsend.h" -#include "IRsend_test.h" +#include "../src/ir_Daikin.h" +#include "../src/IRac.h" +#include "../src/IRrecv.h" +#include "../src/IRrecv_test.h" +#include "../src/IRsend.h" +#include "../src/IRsend_test.h" #include "gtest/gtest.h" // Tests for sendDaikin(). diff --git a/lib/IRremoteESP8266/test/ir_Delonghi_test.cpp b/lib/IRremoteESP8266/test/ir_Delonghi_test.cpp index 9f069e40ec..fd5a888248 100644 --- a/lib/IRremoteESP8266/test/ir_Delonghi_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Delonghi_test.cpp @@ -1,12 +1,12 @@ // Copyright 2020 David Conran -#include "IRac.h" -#include "ir_Delonghi.h" -#include "IRrecv.h" -#include "IRrecv_test.h" -#include "IRsend.h" -#include "IRsend_test.h" -#include "IRutils.h" +#include "../src/IRac.h" +#include "../src/ir_Delonghi.h" +#include "../src/IRrecv.h" +#include "../src/IRrecv_test.h" +#include "../src/IRsend.h" +#include "../src/IRsend_test.h" +#include "../src/IRutils.h" #include "gtest/gtest.h" TEST(TestUtils, Housekeeping) { diff --git a/lib/IRremoteESP8266/test/ir_Denon_test.cpp b/lib/IRremoteESP8266/test/ir_Denon_test.cpp index 64be43f97e..0f2a4fded8 100644 --- a/lib/IRremoteESP8266/test/ir_Denon_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Denon_test.cpp @@ -1,7 +1,7 @@ // Copyright 2017 David Conran -#include "IRsend.h" -#include "IRsend_test.h" +#include "../src/IRsend.h" +#include "../src/IRsend_test.h" #include "gtest/gtest.h" // Tests for sendDenon(). diff --git a/lib/IRremoteESP8266/test/ir_Dish_test.cpp b/lib/IRremoteESP8266/test/ir_Dish_test.cpp index d745648111..4c6cb5c895 100644 --- a/lib/IRremoteESP8266/test/ir_Dish_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Dish_test.cpp @@ -1,7 +1,7 @@ // Copyright 2017 David Conran -#include "IRsend.h" -#include "IRsend_test.h" +#include "../src/IRsend.h" +#include "../src/IRsend_test.h" #include "gtest/gtest.h" // Tests for sendDISH(). diff --git a/lib/IRremoteESP8266/test/ir_Doshisha_test.cpp b/lib/IRremoteESP8266/test/ir_Doshisha_test.cpp index 8c69bfbb3b..3ee6ee11e6 100644 --- a/lib/IRremoteESP8266/test/ir_Doshisha_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Doshisha_test.cpp @@ -1,10 +1,10 @@ // Copyright 2020 Christian Nilsson -#include "IRac.h" -#include "IRrecv.h" -#include "IRrecv_test.h" -#include "IRsend.h" -#include "IRsend_test.h" +#include "../src/IRac.h" +#include "../src/IRrecv.h" +#include "../src/IRrecv_test.h" +#include "../src/IRsend.h" +#include "../src/IRsend_test.h" #include "gtest/gtest.h" // Tests for decodeDoshisha(). diff --git a/lib/IRremoteESP8266/test/ir_Ecoclim_test.cpp b/lib/IRremoteESP8266/test/ir_Ecoclim_test.cpp index fe031e4e27..2cc2a662bb 100644 --- a/lib/IRremoteESP8266/test/ir_Ecoclim_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Ecoclim_test.cpp @@ -1,13 +1,13 @@ // Copyright 2021 David Conran -#include "ir_Ecoclim.h" +#include "../src/ir_Ecoclim.h" #include -#include "IRac.h" -#include "IRrecv.h" -#include "IRrecv_test.h" -#include "IRsend.h" -#include "IRsend_test.h" -#include "IRutils.h" +#include "../src/IRac.h" +#include "../src/IRrecv.h" +#include "../src/IRrecv_test.h" +#include "../src/IRsend.h" +#include "../src/IRsend_test.h" +#include "../src/IRutils.h" #include "gtest/gtest.h" TEST(TestUtils, Housekeeping) { diff --git a/lib/IRremoteESP8266/test/ir_Electra_test.cpp b/lib/IRremoteESP8266/test/ir_Electra_test.cpp index 1fddf537ba..d75c75e27c 100644 --- a/lib/IRremoteESP8266/test/ir_Electra_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Electra_test.cpp @@ -1,12 +1,12 @@ // Copyright 2018, 2019 David Conran -#include "ir_Electra.h" +#include "../src/ir_Electra.h" #include -#include "IRac.h" -#include "IRrecv.h" -#include "IRrecv_test.h" -#include "IRsend.h" -#include "IRsend_test.h" +#include "../src/IRac.h" +#include "../src/IRrecv.h" +#include "../src/IRrecv_test.h" +#include "../src/IRsend.h" +#include "../src/IRsend_test.h" #include "gtest/gtest.h" // Tests for sendElectraAC(). diff --git a/lib/IRremoteESP8266/test/ir_EliteScreens_test.cpp b/lib/IRremoteESP8266/test/ir_EliteScreens_test.cpp index 0af0f1f552..7bcda48c25 100644 --- a/lib/IRremoteESP8266/test/ir_EliteScreens_test.cpp +++ b/lib/IRremoteESP8266/test/ir_EliteScreens_test.cpp @@ -1,8 +1,8 @@ // Copyright 2017 David Conran -#include "IRac.h" -#include "IRsend.h" -#include "IRsend_test.h" +#include "../src/IRac.h" +#include "../src/IRsend.h" +#include "../src/IRsend_test.h" #include "gtest/gtest.h" // Housekeeping tests diff --git a/lib/IRremoteESP8266/test/ir_Epson_test.cpp b/lib/IRremoteESP8266/test/ir_Epson_test.cpp index d1df3f9acc..86c0cc185b 100644 --- a/lib/IRremoteESP8266/test/ir_Epson_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Epson_test.cpp @@ -1,10 +1,10 @@ // Copyright 2020 David Conran -#include "IRac.h" -#include "IRrecv.h" -#include "IRrecv_test.h" -#include "IRsend.h" -#include "IRsend_test.h" +#include "../src/IRac.h" +#include "../src/IRrecv.h" +#include "../src/IRrecv_test.h" +#include "../src/IRsend.h" +#include "../src/IRsend_test.h" #include "gtest/gtest.h" // Tests for sendEpson(). diff --git a/lib/IRremoteESP8266/test/ir_Fujitsu_test.cpp b/lib/IRremoteESP8266/test/ir_Fujitsu_test.cpp index caf4e53f1f..c05a2e5a4c 100644 --- a/lib/IRremoteESP8266/test/ir_Fujitsu_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Fujitsu_test.cpp @@ -1,9 +1,9 @@ // Copyright 2017 Jonny Graham, David Conran -#include "IRac.h" -#include "IRrecv_test.h" -#include "IRsend.h" -#include "IRsend_test.h" -#include "ir_Fujitsu.h" +#include "../src/IRac.h" +#include "../src/IRrecv_test.h" +#include "../src/IRsend.h" +#include "../src/IRsend_test.h" +#include "../src/ir_Fujitsu.h" #include "gtest/gtest.h" // Tests for Fujitsu A/C methods. diff --git a/lib/IRremoteESP8266/test/ir_GICable_test.cpp b/lib/IRremoteESP8266/test/ir_GICable_test.cpp index 234a748b5c..e58f14bccc 100644 --- a/lib/IRremoteESP8266/test/ir_GICable_test.cpp +++ b/lib/IRremoteESP8266/test/ir_GICable_test.cpp @@ -1,7 +1,7 @@ // Copyright 2018 David Conran -#include "IRsend.h" -#include "IRsend_test.h" +#include "../src/IRsend.h" +#include "../src/IRsend_test.h" #include "gtest/gtest.h" // Tests for sendGICable(). diff --git a/lib/IRremoteESP8266/test/ir_GlobalCache_test.cpp b/lib/IRremoteESP8266/test/ir_GlobalCache_test.cpp index 00742aedac..c4d492ec0e 100644 --- a/lib/IRremoteESP8266/test/ir_GlobalCache_test.cpp +++ b/lib/IRremoteESP8266/test/ir_GlobalCache_test.cpp @@ -1,7 +1,7 @@ // Copyright 2017 David Conran -#include "IRsend.h" -#include "IRsend_test.h" +#include "../src/IRsend.h" +#include "../src/IRsend_test.h" #include "gtest/gtest.h" // Tests for sendGlobalCache(). diff --git a/lib/IRremoteESP8266/test/ir_Goodweather_test.cpp b/lib/IRremoteESP8266/test/ir_Goodweather_test.cpp index be07b9d093..9dbb729f09 100644 --- a/lib/IRremoteESP8266/test/ir_Goodweather_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Goodweather_test.cpp @@ -1,11 +1,11 @@ // Copyright 2019 David Conran -#include "ir_Goodweather.h" -#include "IRac.h" -#include "IRrecv.h" -#include "IRrecv_test.h" -#include "IRsend.h" -#include "IRsend_test.h" +#include "../src/ir_Goodweather.h" +#include "../src/IRac.h" +#include "../src/IRrecv.h" +#include "../src/IRrecv_test.h" +#include "../src/IRsend.h" +#include "../src/IRsend_test.h" #include "gtest/gtest.h" TEST(TestIRUtils, Goodweather) { diff --git a/lib/IRremoteESP8266/test/ir_Gree_test.cpp b/lib/IRremoteESP8266/test/ir_Gree_test.cpp index 597fd3c09d..de9bb9aa52 100644 --- a/lib/IRremoteESP8266/test/ir_Gree_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Gree_test.cpp @@ -1,12 +1,12 @@ // Copyright 2017 David Conran -#include "ir_Gree.h" -#include "IRac.h" -#include "IRrecv.h" -#include "IRrecv_test.h" -#include "IRremoteESP8266.h" -#include "IRsend.h" -#include "IRsend_test.h" +#include "../src/ir_Gree.h" +#include "../src/IRac.h" +#include "../src/IRrecv.h" +#include "../src/IRrecv_test.h" +#include "../src/IRremoteESP8266.h" +#include "../src/IRsend.h" +#include "../src/IRsend_test.h" #include "gtest/gtest.h" // Tests for sendGree(). diff --git a/lib/IRremoteESP8266/test/ir_Haier_test.cpp b/lib/IRremoteESP8266/test/ir_Haier_test.cpp index 0c63b089f7..0e4fba2d03 100644 --- a/lib/IRremoteESP8266/test/ir_Haier_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Haier_test.cpp @@ -1,11 +1,11 @@ // Copyright 2018 David Conran -#include "ir_Haier.h" -#include "IRac.h" -#include "IRrecv.h" -#include "IRrecv_test.h" -#include "IRsend.h" -#include "IRsend_test.h" +#include "../src/ir_Haier.h" +#include "../src/IRac.h" +#include "../src/IRrecv.h" +#include "../src/IRrecv_test.h" +#include "../src/IRsend.h" +#include "../src/IRsend_test.h" #include "gtest/gtest.h" // Tests for sendHaierAC() diff --git a/lib/IRremoteESP8266/test/ir_Hitachi_test.cpp b/lib/IRremoteESP8266/test/ir_Hitachi_test.cpp index 2bbbd29e91..bff5f350c1 100644 --- a/lib/IRremoteESP8266/test/ir_Hitachi_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Hitachi_test.cpp @@ -1,12 +1,12 @@ // Copyright 2018 David Conran -#include "ir_Hitachi.h" -#include "IRac.h" -#include "IRrecv.h" -#include "IRrecv_test.h" -#include "IRremoteESP8266.h" -#include "IRsend.h" -#include "IRsend_test.h" +#include "../src/ir_Hitachi.h" +#include "../src/IRac.h" +#include "../src/IRrecv.h" +#include "../src/IRrecv_test.h" +#include "../src/IRremoteESP8266.h" +#include "../src/IRsend.h" +#include "../src/IRsend_test.h" #include "gtest/gtest.h" // Tests for sendHitachiAC(). diff --git a/lib/IRremoteESP8266/test/ir_Inax_test.cpp b/lib/IRremoteESP8266/test/ir_Inax_test.cpp index b182cce32e..e9523954d0 100644 --- a/lib/IRremoteESP8266/test/ir_Inax_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Inax_test.cpp @@ -1,8 +1,8 @@ // Copyright 2019 crankyoldgit (David Conran) -#include "IRsend.h" -#include "IRsend_test.h" -#include "IRutils.h" +#include "../src/IRsend.h" +#include "../src/IRsend_test.h" +#include "../src/IRutils.h" #include "gtest/gtest.h" diff --git a/lib/IRremoteESP8266/test/ir_JVC_test.cpp b/lib/IRremoteESP8266/test/ir_JVC_test.cpp index 51b16b82ce..3ab51d2101 100644 --- a/lib/IRremoteESP8266/test/ir_JVC_test.cpp +++ b/lib/IRremoteESP8266/test/ir_JVC_test.cpp @@ -1,7 +1,7 @@ // Copyright 2017 David Conran -#include "IRsend.h" -#include "IRsend_test.h" +#include "../src/IRsend.h" +#include "../src/IRsend_test.h" #include "gtest/gtest.h" // Tests for sendJVC(). diff --git a/lib/IRremoteESP8266/test/ir_Kelon_test.cpp b/lib/IRremoteESP8266/test/ir_Kelon_test.cpp index 7212d49ee5..5e02c26235 100644 --- a/lib/IRremoteESP8266/test/ir_Kelon_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Kelon_test.cpp @@ -1,10 +1,10 @@ // Copyright 2021 Davide Depau -#include "IRac.h" -#include "IRrecv.h" -#include "IRrecv_test.h" -#include "IRsend.h" -#include "IRsend_test.h" +#include "../src/IRac.h" +#include "../src/IRrecv.h" +#include "../src/IRrecv_test.h" +#include "../src/IRsend.h" +#include "../src/IRsend_test.h" #include "gtest/gtest.h" // Tests for sendKelon(). diff --git a/lib/IRremoteESP8266/test/ir_Kelvinator_test.cpp b/lib/IRremoteESP8266/test/ir_Kelvinator_test.cpp index 73ad6581d8..a7b4552573 100644 --- a/lib/IRremoteESP8266/test/ir_Kelvinator_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Kelvinator_test.cpp @@ -1,12 +1,12 @@ // Copyright 2017 David Conran -#include "ir_Kelvinator.h" -#include "IRac.h" -#include "IRrecv.h" -#include "IRrecv_test.h" -#include "IRremoteESP8266.h" -#include "IRsend.h" -#include "IRsend_test.h" +#include "../src/ir_Kelvinator.h" +#include "../src/IRac.h" +#include "../src/IRrecv.h" +#include "../src/IRrecv_test.h" +#include "../src/IRremoteESP8266.h" +#include "../src/IRsend.h" +#include "../src/IRsend_test.h" #include "gtest/gtest.h" // Tests for sendKelvinator(). diff --git a/lib/IRremoteESP8266/test/ir_LG_test.cpp b/lib/IRremoteESP8266/test/ir_LG_test.cpp index 808f7b83af..54c4bcfafb 100644 --- a/lib/IRremoteESP8266/test/ir_LG_test.cpp +++ b/lib/IRremoteESP8266/test/ir_LG_test.cpp @@ -1,9 +1,9 @@ // Copyright 2017, 2019 David Conran -#include "ir_LG.h" -#include "IRac.h" -#include "IRsend.h" -#include "IRsend_test.h" +#include "../src/ir_LG.h" +#include "../src/IRac.h" +#include "../src/IRsend.h" +#include "../src/IRsend_test.h" #include "gtest/gtest.h" diff --git a/lib/IRremoteESP8266/test/ir_Lasertag_test.cpp b/lib/IRremoteESP8266/test/ir_Lasertag_test.cpp index bad724f76d..d3d96f299c 100644 --- a/lib/IRremoteESP8266/test/ir_Lasertag_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Lasertag_test.cpp @@ -1,7 +1,7 @@ // Copyright 2017 David Conran -#include "IRsend.h" -#include "IRsend_test.h" +#include "../src/IRsend.h" +#include "../src/IRsend_test.h" #include "gtest/gtest.h" // LL AAA SSSSS EEEEEEE RRRRRR TTTTTTT AAA GGGG diff --git a/lib/IRremoteESP8266/test/ir_Lego_test.cpp b/lib/IRremoteESP8266/test/ir_Lego_test.cpp index 4e859b1708..c2aacb1c9b 100644 --- a/lib/IRremoteESP8266/test/ir_Lego_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Lego_test.cpp @@ -1,9 +1,9 @@ // Copyright 2019 David Conran -#include "IRrecv.h" -#include "IRrecv_test.h" -#include "IRsend.h" -#include "IRsend_test.h" +#include "../src/IRrecv.h" +#include "../src/IRrecv_test.h" +#include "../src/IRsend.h" +#include "../src/IRsend_test.h" #include "gtest/gtest.h" // General housekeeping diff --git a/lib/IRremoteESP8266/test/ir_Lutron_test.cpp b/lib/IRremoteESP8266/test/ir_Lutron_test.cpp index 81cf0df9ce..b0a4e346e5 100644 --- a/lib/IRremoteESP8266/test/ir_Lutron_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Lutron_test.cpp @@ -1,7 +1,7 @@ // Copyright 2018 David Conran -#include "IRsend.h" -#include "IRsend_test.h" +#include "../src/IRsend.h" +#include "../src/IRsend_test.h" #include "gtest/gtest.h" // Tests for sendLutron(). diff --git a/lib/IRremoteESP8266/test/ir_MWM_test.cpp b/lib/IRremoteESP8266/test/ir_MWM_test.cpp index 2ca69ac833..7b70a46b4c 100644 --- a/lib/IRremoteESP8266/test/ir_MWM_test.cpp +++ b/lib/IRremoteESP8266/test/ir_MWM_test.cpp @@ -1,10 +1,10 @@ // Copyright 2017 David Conran // Copyright 2018 Brett T. Warden -#include "IRrecv.h" -#include "IRrecv_test.h" -#include "IRsend.h" -#include "IRsend_test.h" +#include "../src/IRrecv.h" +#include "../src/IRrecv_test.h" +#include "../src/IRsend.h" +#include "../src/IRsend_test.h" #include "gtest/gtest.h" // MM MM WW WW MM MM diff --git a/lib/IRremoteESP8266/test/ir_Magiquest_test.cpp b/lib/IRremoteESP8266/test/ir_Magiquest_test.cpp index 1e5faf5c08..c60a893de0 100644 --- a/lib/IRremoteESP8266/test/ir_Magiquest_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Magiquest_test.cpp @@ -1,9 +1,9 @@ // Copyright 2017 David Conran -#include "ir_Magiquest.h" -#include "IRrecv.h" -#include "IRsend.h" -#include "IRsend_test.h" +#include "../src/ir_Magiquest.h" +#include "../src/IRrecv.h" +#include "../src/IRsend.h" +#include "../src/IRsend_test.h" #include "gtest/gtest.h" // Tests for encodeMagiQuest() diff --git a/lib/IRremoteESP8266/test/ir_Metz_test.cpp b/lib/IRremoteESP8266/test/ir_Metz_test.cpp index 67d2170231..8644e048dd 100644 --- a/lib/IRremoteESP8266/test/ir_Metz_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Metz_test.cpp @@ -1,10 +1,10 @@ // Copyright 2020 crankyoldgit -#include "IRac.h" -#include "IRrecv.h" -#include "IRrecv_test.h" -#include "IRsend.h" -#include "IRsend_test.h" +#include "../src/IRac.h" +#include "../src/IRrecv.h" +#include "../src/IRrecv_test.h" +#include "../src/IRsend.h" +#include "../src/IRsend_test.h" #include "gtest/gtest.h" // Tests for decodeMetz(). diff --git a/lib/IRremoteESP8266/test/ir_Midea_test.cpp b/lib/IRremoteESP8266/test/ir_Midea_test.cpp index 713fcd0e41..2e147fd0df 100644 --- a/lib/IRremoteESP8266/test/ir_Midea_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Midea_test.cpp @@ -1,9 +1,9 @@ // Copyright 2017 David Conran -#include "ir_Midea.h" -#include "IRac.h" -#include "IRsend.h" -#include "IRsend_test.h" +#include "../src/ir_Midea.h" +#include "../src/IRac.h" +#include "../src/IRsend.h" +#include "../src/IRsend_test.h" #include "gtest/gtest.h" // Tests for sendMidea(). diff --git a/lib/IRremoteESP8266/test/ir_Milestag2_test.cpp b/lib/IRremoteESP8266/test/ir_Milestag2_test.cpp index 542852da85..37b74ad896 100644 --- a/lib/IRremoteESP8266/test/ir_Milestag2_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Milestag2_test.cpp @@ -1,11 +1,11 @@ // Copyright 2021 David Conran -#include "IRac.h" -#include "IRrecv.h" -#include "IRrecv_test.h" -#include "IRsend.h" -#include "IRsend_test.h" -#include "IRutils.h" +#include "../src/IRac.h" +#include "../src/IRrecv.h" +#include "../src/IRrecv_test.h" +#include "../src/IRsend.h" +#include "../src/IRsend_test.h" +#include "../src/IRutils.h" #include "gtest/gtest.h" TEST(TestUtils, Housekeeping) { diff --git a/lib/IRremoteESP8266/test/ir_Mirage_test.cpp b/lib/IRremoteESP8266/test/ir_Mirage_test.cpp index 2530afc5cb..30348cbf1e 100644 --- a/lib/IRremoteESP8266/test/ir_Mirage_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Mirage_test.cpp @@ -1,10 +1,10 @@ // Copyright 2020 David Conran -#include "IRac.h" -#include "IRrecv.h" -#include "IRrecv_test.h" -#include "IRsend.h" -#include "IRsend_test.h" +#include "../src/IRac.h" +#include "../src/IRrecv.h" +#include "../src/IRrecv_test.h" +#include "../src/IRsend.h" +#include "../src/IRsend_test.h" #include "gtest/gtest.h" TEST(TestUtils, Housekeeping) { diff --git a/lib/IRremoteESP8266/test/ir_MitsubishiHeavy_test.cpp b/lib/IRremoteESP8266/test/ir_MitsubishiHeavy_test.cpp index d182fd941b..f5e4a29d3a 100644 --- a/lib/IRremoteESP8266/test/ir_MitsubishiHeavy_test.cpp +++ b/lib/IRremoteESP8266/test/ir_MitsubishiHeavy_test.cpp @@ -1,12 +1,12 @@ // Copyright 2019 David Conran -#include "ir_MitsubishiHeavy.h" -#include "IRac.h" -#include "IRrecv.h" -#include "IRrecv_test.h" -#include "IRremoteESP8266.h" -#include "IRsend.h" -#include "IRsend_test.h" +#include "../src/ir_MitsubishiHeavy.h" +#include "../src/IRac.h" +#include "../src/IRrecv.h" +#include "../src/IRrecv_test.h" +#include "../src/IRremoteESP8266.h" +#include "../src/IRsend.h" +#include "../src/IRsend_test.h" #include "gtest/gtest.h" // General housekeeping diff --git a/lib/IRremoteESP8266/test/ir_Mitsubishi_test.cpp b/lib/IRremoteESP8266/test/ir_Mitsubishi_test.cpp index 28cc04d8e7..d208d94a2b 100644 --- a/lib/IRremoteESP8266/test/ir_Mitsubishi_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Mitsubishi_test.cpp @@ -2,11 +2,11 @@ // Copyright 2019 kuchel77 // Copyright 2018 denxhun -#include "ir_Mitsubishi.h" -#include "IRac.h" -#include "IRrecv_test.h" -#include "IRsend.h" -#include "IRsend_test.h" +#include "../src/ir_Mitsubishi.h" +#include "../src/IRac.h" +#include "../src/IRrecv_test.h" +#include "../src/IRsend.h" +#include "../src/IRsend_test.h" #include "gtest/gtest.h" // Tests for sendMitsubishi(). diff --git a/lib/IRremoteESP8266/test/ir_Multibrackets_test.cpp b/lib/IRremoteESP8266/test/ir_Multibrackets_test.cpp index 7b58b3333a..683072685d 100644 --- a/lib/IRremoteESP8266/test/ir_Multibrackets_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Multibrackets_test.cpp @@ -1,10 +1,10 @@ // Copyright 2020 David Conran -#include "IRac.h" -#include "IRrecv.h" -#include "IRrecv_test.h" -#include "IRsend.h" -#include "IRsend_test.h" +#include "../src/IRac.h" +#include "../src/IRrecv.h" +#include "../src/IRrecv_test.h" +#include "../src/IRsend.h" +#include "../src/IRsend_test.h" #include "gtest/gtest.h" // Tests for decodeMultibrackets(). diff --git a/lib/IRremoteESP8266/test/ir_NEC_test.cpp b/lib/IRremoteESP8266/test/ir_NEC_test.cpp index f7598c9811..b8c2be1b49 100644 --- a/lib/IRremoteESP8266/test/ir_NEC_test.cpp +++ b/lib/IRremoteESP8266/test/ir_NEC_test.cpp @@ -1,7 +1,7 @@ // Copyright 2017 David Conran -#include "IRsend.h" -#include "IRsend_test.h" +#include "../src/IRsend.h" +#include "../src/IRsend_test.h" #include "gtest/gtest.h" // Tests for sendNEC(). diff --git a/lib/IRremoteESP8266/test/ir_Neoclima_test.cpp b/lib/IRremoteESP8266/test/ir_Neoclima_test.cpp index 68fc2ded53..4c2ed04652 100644 --- a/lib/IRremoteESP8266/test/ir_Neoclima_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Neoclima_test.cpp @@ -1,12 +1,12 @@ // Copyright 2019 David Conran (crankyoldgit) -#include "ir_Neoclima.h" +#include "../src/ir_Neoclima.h" #include -#include "IRac.h" -#include "IRsend.h" -#include "IRsend_test.h" -#include "IRrecv.h" -#include "IRrecv_test.h" +#include "../src/IRac.h" +#include "../src/IRsend.h" +#include "../src/IRsend_test.h" +#include "../src/IRrecv.h" +#include "../src/IRrecv_test.h" #include "gtest/gtest.h" TEST(TestUtils, Housekeeping) { diff --git a/lib/IRremoteESP8266/test/ir_Nikai_test.cpp b/lib/IRremoteESP8266/test/ir_Nikai_test.cpp index 98d6d806da..7a89b601cd 100644 --- a/lib/IRremoteESP8266/test/ir_Nikai_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Nikai_test.cpp @@ -1,7 +1,7 @@ // Copyright 2017 David Conran -#include "IRsend.h" -#include "IRsend_test.h" +#include "../src/IRsend.h" +#include "../src/IRsend_test.h" #include "gtest/gtest.h" // Tests for sendNikai(). diff --git a/lib/IRremoteESP8266/test/ir_Panasonic_test.cpp b/lib/IRremoteESP8266/test/ir_Panasonic_test.cpp index 3e52cef8b2..5c2a09dd26 100644 --- a/lib/IRremoteESP8266/test/ir_Panasonic_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Panasonic_test.cpp @@ -1,13 +1,13 @@ // Copyright 2017, 2018 David Conran -#include "ir_Panasonic.h" -#include "IRac.h" -#include "IRrecv.h" -#include "IRrecv_test.h" -#include "IRsend.h" -#include "IRsend_test.h" -#include "IRtext.h" -#include "IRutils.h" +#include "../src/ir_Panasonic.h" +#include "../src/IRac.h" +#include "../src/IRrecv.h" +#include "../src/IRrecv_test.h" +#include "../src/IRsend.h" +#include "../src/IRsend_test.h" +#include "../src/IRtext.h" +#include "../src/IRutils.h" #include "gtest/gtest.h" // Tests for encodePanasonic(). diff --git a/lib/IRremoteESP8266/test/ir_Pioneer_test.cpp b/lib/IRremoteESP8266/test/ir_Pioneer_test.cpp index fce2503e4a..7ac7d15561 100644 --- a/lib/IRremoteESP8266/test/ir_Pioneer_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Pioneer_test.cpp @@ -1,8 +1,8 @@ // Copyright 2018 David Conran -#include "IRsend.h" -#include "IRsend_test.h" -#include "IRutils.h" +#include "../src/IRsend.h" +#include "../src/IRsend_test.h" +#include "../src/IRutils.h" #include "gtest/gtest.h" // Tests for sendPioneer(). diff --git a/lib/IRremoteESP8266/test/ir_Pronto_test.cpp b/lib/IRremoteESP8266/test/ir_Pronto_test.cpp index 8331192bca..f441120f1b 100644 --- a/lib/IRremoteESP8266/test/ir_Pronto_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Pronto_test.cpp @@ -1,6 +1,6 @@ // Copyright 2017 David Conran -#include "IRsend.h" -#include "IRsend_test.h" +#include "../src/IRsend.h" +#include "../src/IRsend_test.h" #include "gtest/gtest.h" // Tests for sendPronto(). diff --git a/lib/IRremoteESP8266/test/ir_RC5_RC6_test.cpp b/lib/IRremoteESP8266/test/ir_RC5_RC6_test.cpp index 0b46bad7f7..8073ea5ab1 100644 --- a/lib/IRremoteESP8266/test/ir_RC5_RC6_test.cpp +++ b/lib/IRremoteESP8266/test/ir_RC5_RC6_test.cpp @@ -1,7 +1,7 @@ // Copyright 2017 David Conran -#include "IRsend.h" -#include "IRsend_test.h" +#include "../src/IRsend.h" +#include "../src/IRsend_test.h" #include "gtest/gtest.h" // RRRRRR CCCCC 555555 RRRRRR CCCCC 555555 XX XX diff --git a/lib/IRremoteESP8266/test/ir_RCMM_test.cpp b/lib/IRremoteESP8266/test/ir_RCMM_test.cpp index ef37d87a6a..b635c6173f 100644 --- a/lib/IRremoteESP8266/test/ir_RCMM_test.cpp +++ b/lib/IRremoteESP8266/test/ir_RCMM_test.cpp @@ -1,7 +1,7 @@ // Copyright 2017 David Conran -#include "IRsend.h" -#include "IRsend_test.h" +#include "../src/IRsend.h" +#include "../src/IRsend_test.h" #include "gtest/gtest.h" // Tests for sendRCMM(). diff --git a/lib/IRremoteESP8266/test/ir_Rhoss_test.cpp b/lib/IRremoteESP8266/test/ir_Rhoss_test.cpp index 855b9a48c8..18428bb40d 100644 --- a/lib/IRremoteESP8266/test/ir_Rhoss_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Rhoss_test.cpp @@ -1,12 +1,12 @@ // Copyright 2021 Tom Rosenback -#include "IRac.h" -#include "ir_Rhoss.h" -#include "IRrecv.h" -#include "IRrecv_test.h" -#include "IRsend.h" -#include "IRsend_test.h" -#include "IRutils.h" +#include "../src/IRac.h" +#include "../src/ir_Rhoss.h" +#include "../src/IRrecv.h" +#include "../src/IRrecv_test.h" +#include "../src/IRsend.h" +#include "../src/IRsend_test.h" +#include "../src/IRutils.h" #include "gtest/gtest.h" TEST(TestUtils, Housekeeping) { diff --git a/lib/IRremoteESP8266/test/ir_Samsung_test.cpp b/lib/IRremoteESP8266/test/ir_Samsung_test.cpp index a4e0503cdf..3e62ac3a76 100644 --- a/lib/IRremoteESP8266/test/ir_Samsung_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Samsung_test.cpp @@ -1,12 +1,12 @@ // Copyright 2017-2020 David Conran #include -#include "ir_Samsung.h" -#include "IRac.h" -#include "IRrecv.h" -#include "IRrecv_test.h" -#include "IRsend.h" -#include "IRsend_test.h" +#include "../src/ir_Samsung.h" +#include "../src/IRac.h" +#include "../src/IRrecv.h" +#include "../src/IRrecv_test.h" +#include "../src/IRsend.h" +#include "../src/IRsend_test.h" #include "gtest/gtest.h" diff --git a/lib/IRremoteESP8266/test/ir_Sanyo_test.cpp b/lib/IRremoteESP8266/test/ir_Sanyo_test.cpp index 6378e7679d..bb44b3a013 100644 --- a/lib/IRremoteESP8266/test/ir_Sanyo_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Sanyo_test.cpp @@ -1,11 +1,11 @@ // Copyright 2017-2021 David Conran -#include "ir_Sanyo.h" -#include "IRac.h" -#include "IRrecv.h" -#include "IRrecv_test.h" -#include "IRsend.h" -#include "IRsend_test.h" +#include "../src/ir_Sanyo.h" +#include "../src/IRac.h" +#include "../src/IRrecv.h" +#include "../src/IRrecv_test.h" +#include "../src/IRsend.h" +#include "../src/IRsend_test.h" #include "gtest/gtest.h" // Tests for encodeSanyoLC7461(). diff --git a/lib/IRremoteESP8266/test/ir_Sharp_test.cpp b/lib/IRremoteESP8266/test/ir_Sharp_test.cpp index a2acafb9cc..6635e2dfd5 100644 --- a/lib/IRremoteESP8266/test/ir_Sharp_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Sharp_test.cpp @@ -1,11 +1,11 @@ // Copyright 2017 David Conran -#include "ir_Sharp.h" -#include "IRac.h" -#include "IRrecv.h" -#include "IRrecv_test.h" -#include "IRsend.h" -#include "IRsend_test.h" +#include "../src/ir_Sharp.h" +#include "../src/IRac.h" +#include "../src/IRrecv.h" +#include "../src/IRrecv_test.h" +#include "../src/IRsend.h" +#include "../src/IRsend_test.h" #include "gtest/gtest.h" // Tests for encodeSharp(). diff --git a/lib/IRremoteESP8266/test/ir_Sherwood_test.cpp b/lib/IRremoteESP8266/test/ir_Sherwood_test.cpp index f1f41d9c8c..2ac8b4af95 100644 --- a/lib/IRremoteESP8266/test/ir_Sherwood_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Sherwood_test.cpp @@ -1,7 +1,7 @@ // Copyright 2017 David Conran -#include "IRsend.h" -#include "IRsend_test.h" +#include "../src/IRsend.h" +#include "../src/IRsend_test.h" #include "gtest/gtest.h" // Tests for sendSherwood(). diff --git a/lib/IRremoteESP8266/test/ir_Sony_test.cpp b/lib/IRremoteESP8266/test/ir_Sony_test.cpp index c69d40e67a..dd0692f45c 100644 --- a/lib/IRremoteESP8266/test/ir_Sony_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Sony_test.cpp @@ -1,7 +1,7 @@ // Copyright 2017 David Conran -#include "IRsend.h" -#include "IRsend_test.h" +#include "../src/IRsend.h" +#include "../src/IRsend_test.h" #include "gtest/gtest.h" // Tests for sendSony(). diff --git a/lib/IRremoteESP8266/test/ir_Symphony_test.cpp b/lib/IRremoteESP8266/test/ir_Symphony_test.cpp index e3f818136e..7960db61eb 100644 --- a/lib/IRremoteESP8266/test/ir_Symphony_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Symphony_test.cpp @@ -1,10 +1,10 @@ // Copyright 2020 David Conran -#include "IRac.h" -#include "IRrecv.h" -#include "IRrecv_test.h" -#include "IRsend.h" -#include "IRsend_test.h" +#include "../src/IRac.h" +#include "../src/IRrecv.h" +#include "../src/IRrecv_test.h" +#include "../src/IRsend.h" +#include "../src/IRsend_test.h" #include "gtest/gtest.h" // Tests for sendSymphony(). diff --git a/lib/IRremoteESP8266/test/ir_Tcl_test.cpp b/lib/IRremoteESP8266/test/ir_Tcl_test.cpp index b7ab5301f9..37e75f42f6 100644 --- a/lib/IRremoteESP8266/test/ir_Tcl_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Tcl_test.cpp @@ -1,11 +1,11 @@ // Copyright 2019 David Conran -#include "ir_Tcl.h" -#include "IRac.h" -#include "IRrecv.h" -#include "IRrecv_test.h" -#include "IRsend.h" -#include "IRsend_test.h" +#include "../src/ir_Tcl.h" +#include "../src/IRac.h" +#include "../src/IRrecv.h" +#include "../src/IRrecv_test.h" +#include "../src/IRsend.h" +#include "../src/IRsend_test.h" #include "gtest/gtest.h" // General housekeeping diff --git a/lib/IRremoteESP8266/test/ir_Technibel_test.cpp b/lib/IRremoteESP8266/test/ir_Technibel_test.cpp index 808da1e951..420662e8f7 100644 --- a/lib/IRremoteESP8266/test/ir_Technibel_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Technibel_test.cpp @@ -1,12 +1,12 @@ // Copyright 2020 Quentin BRIOLLANT -#include "IRac.h" -#include "ir_Technibel.h" -#include "IRrecv.h" -#include "IRrecv_test.h" -#include "IRsend.h" -#include "IRsend_test.h" -#include "IRutils.h" +#include "../src/IRac.h" +#include "../src/ir_Technibel.h" +#include "../src/IRrecv.h" +#include "../src/IRrecv_test.h" +#include "../src/IRsend.h" +#include "../src/IRsend_test.h" +#include "../src/IRutils.h" #include "gtest/gtest.h" TEST(TestUtils, Housekeeping) { diff --git a/lib/IRremoteESP8266/test/ir_Teco_test.cpp b/lib/IRremoteESP8266/test/ir_Teco_test.cpp index f4196c3aef..2913c3dc6a 100644 --- a/lib/IRremoteESP8266/test/ir_Teco_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Teco_test.cpp @@ -1,11 +1,11 @@ // Copyright 2019 David Conran -#include "ir_Teco.h" -#include "IRac.h" -#include "IRrecv.h" -#include "IRrecv_test.h" -#include "IRsend.h" -#include "IRsend_test.h" +#include "../src/ir_Teco.h" +#include "../src/IRac.h" +#include "../src/IRrecv.h" +#include "../src/IRrecv_test.h" +#include "../src/IRsend.h" +#include "../src/IRsend_test.h" #include "gtest/gtest.h" // General housekeeping diff --git a/lib/IRremoteESP8266/test/ir_Teknopoint_test.cpp b/lib/IRremoteESP8266/test/ir_Teknopoint_test.cpp index 74f29d1468..59a7ee23d6 100644 --- a/lib/IRremoteESP8266/test/ir_Teknopoint_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Teknopoint_test.cpp @@ -1,10 +1,10 @@ // Copyright 2021 David Conran (crankyoldgit) -#include "IRac.h" -#include "IRrecv.h" -#include "IRrecv_test.h" -#include "IRsend.h" -#include "IRsend_test.h" +#include "../src/IRac.h" +#include "../src/IRrecv.h" +#include "../src/IRrecv_test.h" +#include "../src/IRsend.h" +#include "../src/IRsend_test.h" #include "gtest/gtest.h" // Tests for decodeTeknopoint(). diff --git a/lib/IRremoteESP8266/test/ir_Toshiba_test.cpp b/lib/IRremoteESP8266/test/ir_Toshiba_test.cpp index 1785b38956..a58ff02253 100644 --- a/lib/IRremoteESP8266/test/ir_Toshiba_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Toshiba_test.cpp @@ -1,10 +1,10 @@ // Copyright 2017 David Conran -#include "ir_Toshiba.h" -#include "IRac.h" -#include "IRrecv.h" -#include "IRrecv_test.h" -#include "IRsend.h" -#include "IRsend_test.h" +#include "../src/ir_Toshiba.h" +#include "../src/IRac.h" +#include "../src/IRrecv.h" +#include "../src/IRrecv_test.h" +#include "../src/IRsend.h" +#include "../src/IRsend_test.h" #include "gtest/gtest.h" // Tests for Toshiba A/C methods. diff --git a/lib/IRremoteESP8266/test/ir_Transcold_test.cpp b/lib/IRremoteESP8266/test/ir_Transcold_test.cpp index 8a98a28969..93ac4ad03e 100644 --- a/lib/IRremoteESP8266/test/ir_Transcold_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Transcold_test.cpp @@ -1,10 +1,10 @@ // Copyright 2020 crankyoldgit -#include "IRac.h" -#include "IRrecv.h" -#include "IRrecv_test.h" -#include "IRsend.h" -#include "IRsend_test.h" +#include "../src/IRac.h" +#include "../src/IRrecv.h" +#include "../src/IRrecv_test.h" +#include "../src/IRsend.h" +#include "../src/IRsend_test.h" #include "gtest/gtest.h" // Tests for decodeTranscold(). diff --git a/lib/IRremoteESP8266/test/ir_Trotec_test.cpp b/lib/IRremoteESP8266/test/ir_Trotec_test.cpp index b6658f3f52..8e4c069db4 100644 --- a/lib/IRremoteESP8266/test/ir_Trotec_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Trotec_test.cpp @@ -1,10 +1,10 @@ // Copyright 2019 David Conran -#include "ir_Trotec.h" -#include "IRac.h" -#include "IRrecv.h" -#include "IRrecv_test.h" -#include "IRsend.h" -#include "IRsend_test.h" +#include "../src/ir_Trotec.h" +#include "../src/IRac.h" +#include "../src/IRrecv.h" +#include "../src/IRrecv_test.h" +#include "../src/IRsend.h" +#include "../src/IRsend_test.h" #include "gtest/gtest.h" diff --git a/lib/IRremoteESP8266/test/ir_Truma_test.cpp b/lib/IRremoteESP8266/test/ir_Truma_test.cpp index 0d26fb6dbe..aab6fbf8b4 100644 --- a/lib/IRremoteESP8266/test/ir_Truma_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Truma_test.cpp @@ -1,10 +1,10 @@ // Copyright 2021 David Conran (crankyoldgit) -#include "IRac.h" -#include "IRrecv.h" -#include "IRrecv_test.h" -#include "IRsend.h" -#include "IRsend_test.h" +#include "../src/IRac.h" +#include "../src/IRrecv.h" +#include "../src/IRrecv_test.h" +#include "../src/IRsend.h" +#include "../src/IRsend_test.h" #include "gtest/gtest.h" // Tests for decodeTruma(). diff --git a/lib/IRremoteESP8266/test/ir_Vestel_test.cpp b/lib/IRremoteESP8266/test/ir_Vestel_test.cpp index d3f1febf8a..cb8d97554c 100644 --- a/lib/IRremoteESP8266/test/ir_Vestel_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Vestel_test.cpp @@ -1,11 +1,11 @@ // Copyright 2019 David Conran -#include "ir_Vestel.h" -#include "IRac.h" -#include "IRrecv.h" -#include "IRrecv_test.h" -#include "IRsend.h" -#include "IRsend_test.h" +#include "../src/ir_Vestel.h" +#include "../src/IRac.h" +#include "../src/IRrecv.h" +#include "../src/IRrecv_test.h" +#include "../src/IRsend.h" +#include "../src/IRsend_test.h" #include "gtest/gtest.h" // Tests for sendVestelAc() diff --git a/lib/IRremoteESP8266/test/ir_Voltas_test.cpp b/lib/IRremoteESP8266/test/ir_Voltas_test.cpp index 38fa9223b2..2a8de3eb1f 100644 --- a/lib/IRremoteESP8266/test/ir_Voltas_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Voltas_test.cpp @@ -1,11 +1,11 @@ // Copyright 2020 crankyoldgit -#include "ir_Voltas.h" -#include "IRac.h" -#include "IRrecv.h" -#include "IRrecv_test.h" -#include "IRsend.h" -#include "IRsend_test.h" +#include "../src/ir_Voltas.h" +#include "../src/IRac.h" +#include "../src/IRrecv.h" +#include "../src/IRrecv_test.h" +#include "../src/IRsend.h" +#include "../src/IRsend_test.h" #include "gtest/gtest.h" // Tests for decodeVoltas(). diff --git a/lib/IRremoteESP8266/test/ir_Whirlpool_test.cpp b/lib/IRremoteESP8266/test/ir_Whirlpool_test.cpp index 4bc295d4f3..c156e81e7f 100644 --- a/lib/IRremoteESP8266/test/ir_Whirlpool_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Whirlpool_test.cpp @@ -1,11 +1,11 @@ // Copyright 2018 David Conran -#include "ir_Whirlpool.h" -#include "IRac.h" -#include "IRrecv.h" -#include "IRrecv_test.h" -#include "IRsend.h" -#include "IRsend_test.h" +#include "../src/ir_Whirlpool.h" +#include "../src/IRac.h" +#include "../src/IRrecv.h" +#include "../src/IRrecv_test.h" +#include "../src/IRsend.h" +#include "../src/IRsend_test.h" #include "gtest/gtest.h" diff --git a/lib/IRremoteESP8266/test/ir_Whynter_test.cpp b/lib/IRremoteESP8266/test/ir_Whynter_test.cpp index eaccf0ea10..e9f532d4b2 100644 --- a/lib/IRremoteESP8266/test/ir_Whynter_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Whynter_test.cpp @@ -1,7 +1,7 @@ // Copyright 2017 David Conran -#include "IRsend.h" -#include "IRsend_test.h" +#include "../src/IRsend.h" +#include "../src/IRsend_test.h" #include "gtest/gtest.h" // Tests for sendWhynter(). diff --git a/lib/IRremoteESP8266/test/ir_Xmp_test.cpp b/lib/IRremoteESP8266/test/ir_Xmp_test.cpp index 796f2ef903..4c998139b8 100644 --- a/lib/IRremoteESP8266/test/ir_Xmp_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Xmp_test.cpp @@ -1,10 +1,10 @@ // Copyright 2021 David Conran -#include "IRac.h" -#include "IRrecv.h" -#include "IRrecv_test.h" -#include "IRsend.h" -#include "IRsend_test.h" +#include "../src/IRac.h" +#include "../src/IRrecv.h" +#include "../src/IRrecv_test.h" +#include "../src/IRsend.h" +#include "../src/IRsend_test.h" #include "gtest/gtest.h" // Tests for sendXmp(). diff --git a/lib/IRremoteESP8266/test/ir_Zepeal_test.cpp b/lib/IRremoteESP8266/test/ir_Zepeal_test.cpp index 12240d2f7c..986a6986c4 100644 --- a/lib/IRremoteESP8266/test/ir_Zepeal_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Zepeal_test.cpp @@ -1,10 +1,10 @@ // Copyright 2020 Christian Nilsson -#include "IRac.h" -#include "IRrecv.h" -#include "IRrecv_test.h" -#include "IRsend.h" -#include "IRsend_test.h" +#include "../src/IRac.h" +#include "../src/IRrecv.h" +#include "../src/IRrecv_test.h" +#include "../src/IRsend.h" +#include "../src/IRsend_test.h" #include "gtest/gtest.h" // Tests for decodeZepeal(). diff --git a/lib/IRremoteESP8266/tools/auto_analyse_raw_data.py b/lib/IRremoteESP8266/tools/auto_analyse_raw_data.py index e061d3c424..5c797aff4d 100644 --- a/lib/IRremoteESP8266/tools/auto_analyse_raw_data.py +++ b/lib/IRremoteESP8266/tools/auto_analyse_raw_data.py @@ -647,9 +647,9 @@ def generate_code(defines, code, bits_str, name="", output=sys.stdout): f"/// @brief Support for {def_name} protocol\n\n" "// Supports:\n" f"// Brand: {def_name}, Model: TODO add device and remote\n\n" - '#include "IRrecv.h"\n' - '#include "IRsend.h"\n' - '#include "IRutils.h"\n\n' + '#include "../src/IRrecv.h"\n' + '#include "../src/IRsend.h"\n' + '#include "../src/IRutils.h"\n\n' "// WARNING: This probably isn't directly usable." " It's a guide only.\n\n" "// See https://github.com/crankyoldgit/IRremoteESP8266/wiki/" diff --git a/lib/IRremoteESP8266/tools/auto_analyse_raw_data_test.py b/lib/IRremoteESP8266/tools/auto_analyse_raw_data_test.py index 1b080c5a8b..6682dcf766 100644 --- a/lib/IRremoteESP8266/tools/auto_analyse_raw_data_test.py +++ b/lib/IRremoteESP8266/tools/auto_analyse_raw_data_test.py @@ -286,9 +286,9 @@ def test_parse_and_report(self): '// Supports:\n' '// Brand: FOO, Model: TODO add device and remote\n' '\n' - '#include "IRrecv.h"\n' - '#include "IRsend.h"\n' - '#include "IRutils.h"\n' + '#include "../src/IRrecv.h"\n' + '#include "../src/IRsend.h"\n' + '#include "../src/IRutils.h"\n' '\n' "// WARNING: This probably isn't directly usable. It's a guide only.\n" '\n' @@ -567,9 +567,9 @@ def test_leader_marks(self): '// Supports:\n' '// Brand: Hitachi, Model: TODO add device and remote\n' '\n' - '#include "IRrecv.h"\n' - '#include "IRsend.h"\n' - '#include "IRutils.h"\n' + '#include "../src/IRrecv.h"\n' + '#include "../src/IRsend.h"\n' + '#include "../src/IRutils.h"\n' '\n' "// WARNING: This probably isn't directly usable. It's a guide only.\n" '\n' @@ -881,9 +881,9 @@ def test_unusual_gaps(self): '// Supports:\n' '// Brand: FOO, Model: TODO add device and remote\n' '\n' - '#include "IRrecv.h"\n' - '#include "IRsend.h"\n' - '#include "IRutils.h"\n' + '#include "../src/IRrecv.h"\n' + '#include "../src/IRsend.h"\n' + '#include "../src/IRutils.h"\n' '\n' "// WARNING: This probably isn't directly usable. It's a guide only.\n" '\n' @@ -1317,9 +1317,9 @@ def test_no_headers(self): '// Supports:\n' '// Brand: TBD, Model: TODO add device and remote\n' '\n' - '#include "IRrecv.h"\n' - '#include "IRsend.h"\n' - '#include "IRutils.h"\n' + '#include "../src/IRrecv.h"\n' + '#include "../src/IRsend.h"\n' + '#include "../src/IRutils.h"\n' '\n' "// WARNING: This probably isn't directly usable. It's a guide only.\n" '\n' diff --git a/lib/IRremoteESP8266/tools/gc_decode.cpp b/lib/IRremoteESP8266/tools/gc_decode.cpp index f1c374bbef..38ab03ba45 100644 --- a/lib/IRremoteESP8266/tools/gc_decode.cpp +++ b/lib/IRremoteESP8266/tools/gc_decode.cpp @@ -7,10 +7,10 @@ #include #include #include -#include "IRac.h" -#include "IRsend.h" -#include "IRsend_test.h" -#include "IRutils.h" +#include "../src/IRac.h" +#include "../src/IRsend.h" +#include "../src/IRsend_test.h" +#include "../src/IRutils.h" const uint16_t kMaxGcCodeLength = 10000; diff --git a/lib/IRremoteESP8266/tools/mode2_decode.cpp b/lib/IRremoteESP8266/tools/mode2_decode.cpp index 63dfa62210..8ba52aa000 100644 --- a/lib/IRremoteESP8266/tools/mode2_decode.cpp +++ b/lib/IRremoteESP8266/tools/mode2_decode.cpp @@ -24,9 +24,9 @@ space 500000 #include #include #include -#include "IRsend.h" -#include "IRsend_test.h" -#include "IRutils.h" +#include "../src/IRsend.h" +#include "../src/IRsend_test.h" +#include "../src/IRutils.h" const uint16_t kMaxGcCodeLength = 10000; diff --git a/platformio.ini b/platformio.ini index 8090915ae0..31baf3cea1 100644 --- a/platformio.ini +++ b/platformio.ini @@ -89,7 +89,7 @@ monitor_speed = 115200 targets = extra_scripts = pre:tools/pio/pre_default_check.py ${extra_scripts_esp8266.extra_scripts} -src_filter = +<*> -<.git/> -<.svn/> - - - - -<*/Commands/> -<*/ControllerQueue/> -<*/DataTypes/> -<*/Globals/> -<*/Helpers/> -<*/PluginStructs/> -<*/WebServer/> +src_filter = +<*> -<.git/> -<.svn/> - - - - -<*/Commands/> -<*/ControllerQueue/> -<*/DataTypes/> -<*/Globals/> -<*/Helpers/> -<*/PluginStructs/> -<*/WebServer/> -<*/IRremoteESP8266/src/> diff --git a/platformio_esp32_envs.ini b/platformio_esp32_envs.ini index 614e7cd531..d4a1337a13 100644 --- a/platformio_esp32_envs.ini +++ b/platformio_esp32_envs.ini @@ -11,7 +11,7 @@ lib_ignore = ESP8266WiFi, ESP8266Ping, ESP8266WebServer, ESP8266H [esp32_common] extends = common, core_esp32_stage lib_deps = td-er/ESPeasySerial @ 2.0.7, adafruit/Adafruit ILI9341 @ ^1.5.6, Adafruit GFX Library, LOLIN_EPD, Adafruit BusIO, VL53L0X @ 1.3.0, SparkFun VL53L1X 4m Laser Distance Sensor @ 1.2.9, td-er/SparkFun MAX1704x Fuel Gauge Arduino Library @ ^1.0.1, ArduinoOTA, ESP32HTTPUpdateServer -lib_ignore = ${esp32_always.lib_ignore}, ESP32_ping, IRremoteESP8266, HeatpumpIR +lib_ignore = ${esp32_always.lib_ignore}, ESP32_ping, IRremoteESP8266, HeatpumpIR, ESP32 BLE Arduino board_build.f_flash = 80000000L board_build.flash_mode = dout board_upload.maximum_size = 1900544 From 3dff37f9a744507ed9440858d85d2be60f15f121 Mon Sep 17 00:00:00 2001 From: TD-er Date: Sat, 13 Nov 2021 22:07:56 +0100 Subject: [PATCH 005/147] [IR build] Fix build issue on IR plugins --- src/src/PluginStructs/P016_data_struct.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/src/PluginStructs/P016_data_struct.h b/src/src/PluginStructs/P016_data_struct.h index d833a8d7aa..80a21b32a2 100644 --- a/src/src/PluginStructs/P016_data_struct.h +++ b/src/src/PluginStructs/P016_data_struct.h @@ -55,9 +55,6 @@ typedef struct { } tCommandLines; # endif // ifdef P16_SETTINGS_V1 -extern String uint64ToString(uint64_t input, - uint8_t base); - struct P016_data_struct : public PluginTaskData_base { public: From 895389413003692bdc3bcc3369c16a0cbccf3855 Mon Sep 17 00:00:00 2001 From: TD-er Date: Sat, 13 Nov 2021 22:09:15 +0100 Subject: [PATCH 006/147] [WiFi ESP32] Disable WiFi TX power settings for ESP32 (IDF4.4) Changing WiFi TX power may have negative effect on WiFi stability in IDF 4.4 --- src/src/Helpers/StringProvider.cpp | 4 ++++ src/src/Helpers/StringProvider.h | 4 +++- src/src/WebServer/AdvancedConfigPage.cpp | 4 ++++ src/src/WebServer/JSON.cpp | 2 ++ src/src/WebServer/SysInfoPage.cpp | 2 ++ 5 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/src/Helpers/StringProvider.cpp b/src/src/Helpers/StringProvider.cpp index 699fd15098..78dc0891a9 100644 --- a/src/src/Helpers/StringProvider.cpp +++ b/src/src/Helpers/StringProvider.cpp @@ -50,10 +50,12 @@ const __FlashStringHelper * getLabel(LabelType::Enum label) { case LabelType::LOAD_PCT: return F("Load"); case LabelType::LOOP_COUNT: return F("Load LC"); case LabelType::CPU_ECO_MODE: return F("CPU Eco Mode"); +#ifdef ESP8266 // TD-er: Disable setting TX power on ESP32 as it seems to cause issues on IDF4.4 case LabelType::WIFI_TX_MAX_PWR: return F("Max WiFi TX Power"); case LabelType::WIFI_CUR_TX_PWR: return F("Current WiFi TX Power"); case LabelType::WIFI_SENS_MARGIN: return F("WiFi Sensitivity Margin"); case LabelType::WIFI_SEND_AT_MAX_TX_PWR:return F("Send With Max TX Power"); +#endif case LabelType::WIFI_NR_EXTRA_SCANS: return F("Extra WiFi scan loops"); case LabelType::WIFI_USE_LAST_CONN_FROM_RTC: return F("Use Last Connected AP from RTC"); @@ -226,10 +228,12 @@ String getValue(LabelType::Enum label) { case LabelType::LOAD_PCT: return String(getCPUload()); case LabelType::LOOP_COUNT: return String(getLoopCountPerSec()); case LabelType::CPU_ECO_MODE: return jsonBool(Settings.EcoPowerMode()); +#ifdef ESP8266 // TD-er: Disable setting TX power on ESP32 as it seems to cause issues on IDF4.4 case LabelType::WIFI_TX_MAX_PWR: return String(Settings.getWiFi_TX_power(), 2); case LabelType::WIFI_CUR_TX_PWR: return String(WiFiEventData.wifi_TX_pwr, 2); case LabelType::WIFI_SENS_MARGIN: return String(Settings.WiFi_sensitivity_margin); case LabelType::WIFI_SEND_AT_MAX_TX_PWR:return jsonBool(Settings.UseMaxTXpowerForSending()); +#endif case LabelType::WIFI_NR_EXTRA_SCANS: return String(Settings.NumberExtraWiFiScans); case LabelType::WIFI_USE_LAST_CONN_FROM_RTC: return jsonBool(Settings.UseLastWiFiFromRTC()); diff --git a/src/src/Helpers/StringProvider.h b/src/src/Helpers/StringProvider.h index 3abbd1c147..af1496f007 100644 --- a/src/src/Helpers/StringProvider.h +++ b/src/src/Helpers/StringProvider.h @@ -23,11 +23,13 @@ struct LabelType { LOAD_PCT, // 15.10 LOOP_COUNT, // 400 CPU_ECO_MODE, // true +#ifdef ESP8266 // TD-er: Disable setting TX power on ESP32 as it seems to cause issues on IDF4.4 WIFI_TX_MAX_PWR, // Unit: 0.25 dBm, 0 = use default (do not set) WIFI_CUR_TX_PWR, // Unit dBm of current WiFi TX power. WIFI_SENS_MARGIN, // Margin in dB on top of sensitivity WIFI_SEND_AT_MAX_TX_PWR, - WIFI_NR_EXTRA_SCANS, +#endif + WIFI_NR_EXTRA_SCANS, WIFI_USE_LAST_CONN_FROM_RTC, FREE_MEM, // 9876 diff --git a/src/src/WebServer/AdvancedConfigPage.cpp b/src/src/WebServer/AdvancedConfigPage.cpp index 9a89a0b9cc..62a18d99be 100644 --- a/src/src/WebServer/AdvancedConfigPage.cpp +++ b/src/src/WebServer/AdvancedConfigPage.cpp @@ -95,9 +95,11 @@ void handle_advanced() { #ifdef SUPPORT_ARP Settings.gratuitousARP(isFormItemChecked(LabelType::PERIODICAL_GRAT_ARP)); #endif // ifdef SUPPORT_ARP +#ifdef ESP8266 // TD-er: Disable setting TX power on ESP32 as it seems to cause issues on IDF4.4 Settings.setWiFi_TX_power(getFormItemFloat(LabelType::WIFI_TX_MAX_PWR)); Settings.WiFi_sensitivity_margin = getFormItemInt(LabelType::WIFI_SENS_MARGIN); Settings.UseMaxTXpowerForSending(isFormItemChecked(LabelType::WIFI_SEND_AT_MAX_TX_PWR)); +#endif Settings.NumberExtraWiFiScans = getFormItemInt(LabelType::WIFI_NR_EXTRA_SCANS); Settings.UseLastWiFiFromRTC(isFormItemChecked(LabelType::WIFI_USE_LAST_CONN_FROM_RTC)); Settings.JSONBoolWithoutQuotes(isFormItemChecked(LabelType::JSON_BOOL_QUOTES)); @@ -250,6 +252,7 @@ void handle_advanced() { #endif // ifdef SUPPORT_ARP addFormCheckBox(LabelType::CPU_ECO_MODE, Settings.EcoPowerMode()); addFormNote(F("Node may miss receiving packets with Eco mode enabled")); +#ifdef ESP8266 // TD-er: Disable setting TX power on ESP32 as it seems to cause issues on IDF4.4 { float maxTXpwr; float threshold = GetRSSIthreshold(maxTXpwr); @@ -269,6 +272,7 @@ void handle_advanced() { addFormNote(note); } addFormCheckBox(LabelType::WIFI_SEND_AT_MAX_TX_PWR, Settings.UseMaxTXpowerForSending()); +#endif { addFormNumericBox(LabelType::WIFI_NR_EXTRA_SCANS, Settings.NumberExtraWiFiScans, 0, 5); String note = F("Number of extra times to scan all channels to have higher chance of finding the desired AP"); diff --git a/src/src/WebServer/JSON.cpp b/src/src/WebServer/JSON.cpp index b572bdb148..a4d684aa5e 100644 --- a/src/src/WebServer/JSON.cpp +++ b/src/src/WebServer/JSON.cpp @@ -239,10 +239,12 @@ void handle_json() LabelType::PERIODICAL_GRAT_ARP, #endif // ifdef SUPPORT_ARP LabelType::CONNECTION_FAIL_THRESH, +#ifdef ESP8266 // TD-er: Disable setting TX power on ESP32 as it seems to cause issues on IDF4.4 LabelType::WIFI_TX_MAX_PWR, LabelType::WIFI_CUR_TX_PWR, LabelType::WIFI_SENS_MARGIN, LabelType::WIFI_SEND_AT_MAX_TX_PWR, +#endif LabelType::WIFI_NR_EXTRA_SCANS, LabelType::WIFI_USE_LAST_CONN_FROM_RTC, LabelType::WIFI_RSSI, diff --git a/src/src/WebServer/SysInfoPage.cpp b/src/src/WebServer/SysInfoPage.cpp index cb12214040..655da2276a 100644 --- a/src/src/WebServer/SysInfoPage.cpp +++ b/src/src/WebServer/SysInfoPage.cpp @@ -486,10 +486,12 @@ void handle_sysinfo_WiFiSettings() { addRowLabelValue(LabelType::PERIODICAL_GRAT_ARP); # endif // ifdef SUPPORT_ARP addRowLabelValue(LabelType::CONNECTION_FAIL_THRESH); +#ifdef ESP8266 // TD-er: Disable setting TX power on ESP32 as it seems to cause issues on IDF4.4 addRowLabelValue(LabelType::WIFI_TX_MAX_PWR); addRowLabelValue(LabelType::WIFI_CUR_TX_PWR); addRowLabelValue(LabelType::WIFI_SENS_MARGIN); addRowLabelValue(LabelType::WIFI_SEND_AT_MAX_TX_PWR); +#endif addRowLabelValue(LabelType::WIFI_NR_EXTRA_SCANS); addRowLabelValue(LabelType::WIFI_USE_LAST_CONN_FROM_RTC); } From 3bbf39d3ea8c3cd936af87554f81bc513c795052 Mon Sep 17 00:00:00 2001 From: TD-er Date: Fri, 3 Dec 2021 23:48:25 +0100 Subject: [PATCH 007/147] [ESP32 IDF4.4] Update to the latest Arduino ESP32 2.0.1.1 --- platformio_core_defs.ini | 10 ++++++++++ platformio_esp32_envs.ini | 8 ++++---- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/platformio_core_defs.ini b/platformio_core_defs.ini index 6ef11b9012..c5256dd166 100644 --- a/platformio_core_defs.ini +++ b/platformio_core_defs.ini @@ -157,6 +157,11 @@ platform = espressif32 @ 3.3.2 platform_packages = framework-arduinoespressif32 build_flags = -DESP32_STAGE +[core_esp32_3_4_0] +platform = https://github.com/tasmota/platform-espressif32/releases/download/v3.4.0/Tasmota-platform-espressif32.zip +platform_packages = framework-arduinoespressif32 @ https://github.com/tasmota/arduino-esp32/releases/download/2.0.1.1/framework-arduinoespressif32-release_IDF4.4.tar.gz +build_flags = -DESP32_STAGE + [core_esp32_3_3_2_esp32s2] platform = https://github.com/platformio/platform-espressif32.git#feature/arduino-idf-master @@ -164,6 +169,11 @@ platform_packages = framework-arduinoespressif32 @ https://github.com/ta platformio/tool-esptoolpy @ https://github.com/tasmota/esptool/releases/download/v3.2/esptool-v3.2.zip build_flags = -DESP32_STAGE +[core_esp32_3_4_0_esp32s2] +platform = https://github.com/tasmota/platform-espressif32/releases/download/v3.4.0/Tasmota-platform-espressif32.zip +platform_packages = framework-arduinoespressif32 @ https://github.com/tasmota/arduino-esp32/releases/download/2.0.1.1/framework-arduinoespressif32-release_IDF4.4.tar.gz +build_flags = -DESP32_STAGE + [core_esp32_3_0_0] platform = espressif32@3.0.0 diff --git a/platformio_esp32_envs.ini b/platformio_esp32_envs.ini index 29726bf5e0..0a8b58b44a 100644 --- a/platformio_esp32_envs.ini +++ b/platformio_esp32_envs.ini @@ -9,7 +9,7 @@ lib_ignore = ESP8266WiFi, ESP8266Ping, ESP8266WebServer, ESP8266H [esp32_common] -extends = common, core_esp32_stage +extends = common, core_esp32_3_4_0 lib_deps = td-er/ESPeasySerial @ 2.0.8, adafruit/Adafruit ILI9341 @ ^1.5.6, Adafruit GFX Library, LOLIN_EPD, Adafruit BusIO, VL53L0X @ 1.3.0, SparkFun VL53L1X 4m Laser Distance Sensor @ 1.2.9, td-er/SparkFun MAX1704x Fuel Gauge Arduino Library @ ^1.0.1, ArduinoOTA, ESP32HTTPUpdateServer lib_ignore = ${esp32_always.lib_ignore}, ESP32_ping, IRremoteESP8266, HeatpumpIR, ESP32 BLE Arduino board_build.f_flash = 80000000L @@ -19,7 +19,7 @@ board_build.partitions = esp32_partition_app1810k_spiffs316k.csv extra_scripts = post:tools/pio/post_esp32.py ${extra_scripts_default.extra_scripts} build_unflags = -Wall -build_flags = ${core_esp32_3_3_0.build_flags} +build_flags = ${core_esp32_3_4_0.build_flags} ${mqtt_flags.build_flags} -DCONFIG_FREERTOS_ASSERT_DISABLE -DCONFIG_LWIP_ESP_GRATUITOUS_ARP @@ -59,8 +59,8 @@ build_flags = ${esp32_common.build_flags} board = esp32-s2-saola-1 extra_scripts = ${esp32_common.extra_scripts} pre:tools/pio/pre_custom_esp32.py -platform = ${core_esp32_3_3_2_esp32s2.platform} -platform_packages = ${core_esp32_3_3_2_esp32s2.platform_packages} +platform = ${core_esp32_3_4_0_esp32s2.platform} +platform_packages = ${core_esp32_3_4_0_esp32s2.platform_packages} [env:custom_IR_ESP32_4M316k] From d4bd54119ebe281d9577ed251669c9bcea4edeb8 Mon Sep 17 00:00:00 2001 From: TD-er Date: Fri, 3 Dec 2021 23:50:50 +0100 Subject: [PATCH 008/147] [Webserver] Change chunked buffer size to 1360 bytes --- src/src/DataStructs/Web_StreamingBuffer.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/src/DataStructs/Web_StreamingBuffer.cpp b/src/src/DataStructs/Web_StreamingBuffer.cpp index 4898333e2d..9b5386ee80 100644 --- a/src/src/DataStructs/Web_StreamingBuffer.cpp +++ b/src/src/DataStructs/Web_StreamingBuffer.cpp @@ -11,8 +11,11 @@ #include "../Helpers/ESPEasy_time_calc.h" - -#define CHUNKED_BUFFER_SIZE 400 +#ifdef ESP8266 +#define CHUNKED_BUFFER_SIZE 512 +#else +#define CHUNKED_BUFFER_SIZE 1360 +#endif Web_StreamingBuffer::Web_StreamingBuffer(void) : lowMemorySkip(false), initialRam(0), beforeTXRam(0), duringTXRam(0), finalRam(0), maxCoreUsage(0), From 9b24b2828ec0da48e047bfbb519bb25d9d68c5c1 Mon Sep 17 00:00:00 2001 From: TD-er Date: Fri, 3 Dec 2021 23:58:18 +0100 Subject: [PATCH 009/147] [Web UI] Small tweaks improving speed serving pages --- src/src/Helpers/ESPEasy_Storage.cpp | 68 +++++++++++++++-------------- src/src/WebServer/SysInfoPage.cpp | 17 +++----- src/src/WebServer/WebServer.cpp | 26 +++++++---- src/src/WebServer/WebServer.h | 12 ++--- 4 files changed, 66 insertions(+), 57 deletions(-) diff --git a/src/src/Helpers/ESPEasy_Storage.cpp b/src/src/Helpers/ESPEasy_Storage.cpp index 5172d926a2..55e93650a1 100644 --- a/src/src/Helpers/ESPEasy_Storage.cpp +++ b/src/src/Helpers/ESPEasy_Storage.cpp @@ -1366,56 +1366,60 @@ size_t SpiffsUsedBytes() { } size_t SpiffsTotalBytes() { - size_t result = 1; // Do not output 0, this may be used in divisions. - - #ifdef ESP32 - result = ESPEASY_FS.totalBytes(); - #endif // ifdef ESP32 - #ifdef ESP8266 - fs::FSInfo fs_info; - ESPEASY_FS.info(fs_info); - result = fs_info.totalBytes; - #endif // ifdef ESP8266 + static size_t result = 1; // Do not output 0, this may be used in divisions. + if (result == 1) { + #ifdef ESP32 + result = ESPEASY_FS.totalBytes(); + #endif // ifdef ESP32 + #ifdef ESP8266 + fs::FSInfo fs_info; + ESPEASY_FS.info(fs_info); + result = fs_info.totalBytes; + #endif // ifdef ESP8266 + } return result; } size_t SpiffsBlocksize() { - size_t result = 8192; // Some default viable for most 1 MB file systems - - #ifdef ESP32 - result = 8192; // Just assume 8k, since we cannot query it - #endif // ifdef ESP32 - #ifdef ESP8266 - fs::FSInfo fs_info; - ESPEASY_FS.info(fs_info); - result = fs_info.blockSize; - #endif // ifdef ESP8266 + static size_t result = 1; + if (result == 1) { + #ifdef ESP32 + result = 8192; // Just assume 8k, since we cannot query it + #endif // ifdef ESP32 + #ifdef ESP8266 + fs::FSInfo fs_info; + ESPEASY_FS.info(fs_info); + result = fs_info.blockSize; + #endif // ifdef ESP8266 + } return result; } size_t SpiffsPagesize() { - size_t result = 256; // Most common - - #ifdef ESP32 - result = 256; // Just assume 256, since we cannot query it - #endif // ifdef ESP32 - #ifdef ESP8266 - fs::FSInfo fs_info; - ESPEASY_FS.info(fs_info); - result = fs_info.pageSize; - #endif // ifdef ESP8266 + static size_t result = 1; + if (result == 1) { + #ifdef ESP32 + result = 256; // Just assume 256, since we cannot query it + #endif // ifdef ESP32 + #ifdef ESP8266 + fs::FSInfo fs_info; + ESPEASY_FS.info(fs_info); + result = fs_info.pageSize; + #endif // ifdef ESP8266 + } return result; } size_t SpiffsFreeSpace() { int freeSpace = SpiffsTotalBytes() - SpiffsUsedBytes(); + const size_t blocksize = SpiffsBlocksize(); - if (freeSpace < static_cast(2 * SpiffsBlocksize())) { + if (freeSpace < static_cast(2 * blocksize)) { // Not enough free space left to store anything // There needs to be minimum of 2 free blocks. return 0; } - return freeSpace - 2 * SpiffsBlocksize(); + return freeSpace - 2 * blocksize; } bool SpiffsFull() { diff --git a/src/src/WebServer/SysInfoPage.cpp b/src/src/WebServer/SysInfoPage.cpp index 655da2276a..adb06744a6 100644 --- a/src/src/WebServer/SysInfoPage.cpp +++ b/src/src/WebServer/SysInfoPage.cpp @@ -614,8 +614,8 @@ void handle_sysinfo_Storage() { uint32_t flashDevice = (flashChipId & 0xFF00) | ((flashChipId >> 16) & 0xFF); addHtml(formatToHex(flashDevice)); } - uint32_t realSize = getFlashRealSizeInBytes(); - uint32_t ideSize = ESP.getFlashChipSize(); + const uint32_t realSize = getFlashRealSizeInBytes(); + const uint32_t ideSize = ESP.getFlashChipSize(); addRowLabel(LabelType::FLASH_CHIP_REAL_SIZE); addHtmlInt(realSize / 1024); @@ -634,17 +634,14 @@ void handle_sysinfo_Storage() { FlashMode_t ideMode = ESP.getFlashChipMode(); addRowLabel(LabelType::FLASH_IDE_MODE); { - String html; - switch (ideMode) { - case FM_QIO: html += F("QIO"); break; - case FM_QOUT: html += F("QOUT"); break; - case FM_DIO: html += F("DIO"); break; - case FM_DOUT: html += F("DOUT"); break; + case FM_QIO: addHtml(F("QIO")); break; + case FM_QOUT: addHtml(F("QOUT")); break; + case FM_DIO: addHtml(F("DIO")); break; + case FM_DOUT: addHtml(F("DOUT")); break; default: - html += getUnknownString(); break; + addHtml(getUnknownString()); break; } - addHtml(html); } # endif // if defined(ESP8266) diff --git a/src/src/WebServer/WebServer.cpp b/src/src/WebServer/WebServer.cpp index e37debab8c..7ebad1046e 100644 --- a/src/src/WebServer/WebServer.cpp +++ b/src/src/WebServer/WebServer.cpp @@ -1039,11 +1039,21 @@ String getControllerSymbol(uint8_t index) return ret; } */ -void addSVG_param(const String& key, float value) { + +void addSVG_param(const __FlashStringHelper * key, int value) { + addHtml(' '); + addHtml(key); + addHtml('='); + addHtml('\"'); + addHtmlInt(value); + addHtml('\"'); +} + +void addSVG_param(const __FlashStringHelper * key, float value) { addSVG_param(key, String(value, 2)); } -void addSVG_param(const String& key, const String& value) { +void addSVG_param(const __FlashStringHelper * key, const String& value) { addHtml(' '); addHtml(key); addHtml('='); @@ -1076,8 +1086,8 @@ void createSvgRect(const String& classname, addSVG_param(F("stroke"), formatToHex(strokeColor, F("#"))); addSVG_param(F("stroke-width"), strokeWidth); } - addSVG_param("x", xoffset); - addSVG_param("y", yoffset); + addSVG_param(F("x"), xoffset); + addSVG_param(F("y"), yoffset); addSVG_param(F("width"), width); addSVG_param(F("height"), height); addSVG_param(F("rx"), rx); @@ -1121,10 +1131,6 @@ void createSvgTextElement(const String& text, float textXoffset, float textYoffs #define SVG_BAR_HEIGHT 16 #define SVG_BAR_WIDTH 400 -void write_SVG_image_header(int width, int height) { - write_SVG_image_header(width, height, false); -} - void write_SVG_image_header(int width, int height, bool useViewbox) { addHtml(F(" Date: Sat, 4 Dec 2021 00:01:00 +0100 Subject: [PATCH 010/147] [Cleanup] Use .clear() on strings instead of assigning empty string --- src/_C015.ino | 2 +- src/_P016_IR.ino | 8 ++++---- src/_P050_TCS34725.ino | 4 ++-- src/_P073_7DGT.ino | 2 +- src/src/ESPEasyCore/ESPEasyRules.cpp | 4 ++-- src/src/PluginStructs/P104_data_struct.cpp | 6 +++--- 6 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/_C015.ino b/src/_C015.ino index 4379528472..6821f547d8 100644 --- a/src/_C015.ino +++ b/src/_C015.ino @@ -194,7 +194,7 @@ bool CPlugin_015(CPlugin::Function function, struct EventStruct *event, String& if (!isvalid) { // send empty string to Blynk in case of error - formattedValue = EMPTY_STRING; + formattedValue.clear(); } String valueName = ExtraTaskSettings.TaskDeviceValueNames[x]; diff --git a/src/_P016_IR.ino b/src/_P016_IR.ino index f2229bc8d9..70a11c632e 100644 --- a/src/_P016_IR.ino +++ b/src/_P016_IR.ino @@ -378,7 +378,7 @@ boolean Plugin_016(uint8_t function, struct EventStruct *event, String& string) html_TD(); addCheckBox(getPluginCustomArgName(rowCnt + 1), bitRead(P016_data->CommandLines[varNr].CodeFlags, P16_FLAGS_REPEAT)); html_TD(); - strCode = EMPTY_STRING; + strCode.clear(); if (P016_data->CommandLines[varNr].Code > 0) { strCode = uint64ToString(P016_data->CommandLines[varNr].Code, 16); // convert code to hex for display @@ -393,7 +393,7 @@ boolean Plugin_016(uint8_t function, struct EventStruct *event, String& string) html_TD(); addCheckBox(getPluginCustomArgName(rowCnt + 4), bitRead(P016_data->CommandLines[varNr].AlternativeCodeFlags, P16_FLAGS_REPEAT)); html_TD(); - strCode = EMPTY_STRING; + strCode.clear(); if (P016_data->CommandLines[varNr].AlternativeCode > 0) { strCode = uint64ToString(P016_data->CommandLines[varNr].AlternativeCode, 16); // convert code to hex for display @@ -470,7 +470,7 @@ boolean Plugin_016(uint8_t function, struct EventStruct *event, String& string) for (uint8_t varNr = 0; varNr < P16_Nlines; varNr++) { P016_data->CommandLines.push_back(tCommandLinesV2()); - strError = EMPTY_STRING; + strError.clear(); // Normal Code & flags P016_data->CommandLines[varNr].CodeDecodeType = static_cast(getFormItemInt(getPluginCustomArgName(rowCnt + 0))); @@ -754,7 +754,7 @@ boolean Plugin_016(uint8_t function, struct EventStruct *event, String& string) if (state.clock >= 0) { doc[F("clock")] = state.clock; // Nr. of mins past midnight to set the clock to. (< 0 means off.) } - output = EMPTY_STRING; + output.clear(); serializeJson(doc, output); event->String2 = output; diff --git a/src/_P050_TCS34725.ino b/src/_P050_TCS34725.ino index 73219d028b..ba4a3cd794 100644 --- a/src/_P050_TCS34725.ino +++ b/src/_P050_TCS34725.ino @@ -430,7 +430,7 @@ boolean Plugin_050(uint8_t function, struct EventStruct *event, String& string) RuleEvent += String(static_cast(b) / t * sRGBFactor, 4); break; default: - RuleEvent = EMPTY_STRING; + RuleEvent.clear(); break; } if (!RuleEvent.isEmpty()) { @@ -466,7 +466,7 @@ boolean Plugin_050(uint8_t function, struct EventStruct *event, String& string) RuleEvent += c; break; default: - RuleEvent = EMPTY_STRING; + RuleEvent.clear(); break; } if (!RuleEvent.isEmpty()) { diff --git a/src/_P073_7DGT.ino b/src/_P073_7DGT.ino index 14ed3d6de4..91946539ae 100644 --- a/src/_P073_7DGT.ino +++ b/src/_P073_7DGT.ino @@ -401,7 +401,7 @@ struct P073_data_struct : public PluginTaskData_base { } void setTextToScroll(const String& text) { - _textToScroll = EMPTY_STRING; + _textToScroll.clear(); if (text.length() > 0) { int bufToFill = getBufferLength(displayModel); diff --git a/src/src/ESPEasyCore/ESPEasyRules.cpp b/src/src/ESPEasyCore/ESPEasyRules.cpp index 7d0c1d81cd..2750262741 100644 --- a/src/src/ESPEasyCore/ESPEasyRules.cpp +++ b/src/src/ESPEasyCore/ESPEasyRules.cpp @@ -252,7 +252,7 @@ String rulesProcessingFile(const String& fileName, const String& event) { } // Prepare for new line - line = EMPTY_STRING; + line.clear(); line.reserve(longestLineSize); firstNonSpaceRead = false; commentFound = false; @@ -743,7 +743,7 @@ void parseCompleteNonCommentLine(String& line, const String& event, String eventTrigger; - action = EMPTY_STRING; + action.clear(); if (!codeBlock) // do not check "on" rules if a block of actions is to be // processed diff --git a/src/src/PluginStructs/P104_data_struct.cpp b/src/src/PluginStructs/P104_data_struct.cpp index effa3775b5..0ea77e8444 100644 --- a/src/src/PluginStructs/P104_data_struct.cpp +++ b/src/src/PluginStructs/P104_data_struct.cpp @@ -334,7 +334,7 @@ void P104_data_struct::loadSettings() { zones.push_back(P104_zone_struct(zoneIndex + 1)); if (zones[zoneIndex].text == F("\"\"")) { // Special case - zones[zoneIndex].text = EMPTY_STRING; + zones[zoneIndex].text.clear(); } zoneIndex++; @@ -1565,7 +1565,7 @@ String P104_data_struct::enquoteString(const String& input) { * saveSettings gather the zones data from the UI and store in customsettings **************************************/ bool P104_data_struct::saveSettings() { - error = EMPTY_STRING; // Clear + error.clear(); // Clear String zbuffer; # ifdef P104_DEBUG_DEV @@ -1680,7 +1680,7 @@ bool P104_data_struct::saveSettings() { if (zbuffer.reserve(P104_SETTINGS_BUFFER_V2 + 2)) { for (auto it = zones.begin(); it != zones.end() && error.length() == 0; ++it) { - zbuffer = EMPTY_STRING; + zbuffer.clear(); // WARNING: Order of values should match the numeric order of P104_OFFSET_* values zbuffer += it->size; // 2 From 6f08138064177f3c8352d61fb32b98a14cff7e96 Mon Sep 17 00:00:00 2001 From: TD-er Date: Sat, 4 Dec 2021 00:02:30 +0100 Subject: [PATCH 011/147] [Webserver] Fix serving CSS I made an error in previous commit for this PR --- src/src/Static/WebStaticData.cpp | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/src/src/Static/WebStaticData.cpp b/src/src/Static/WebStaticData.cpp index 7c77168b47..3236c71a68 100644 --- a/src/src/Static/WebStaticData.cpp +++ b/src/src/Static/WebStaticData.cpp @@ -14,27 +14,28 @@ String generate_external_URL(const String& fname) { void serve_CSS() { - String url = F("esp.css"); - if (!fileExists(url)) + const String cssFile = F("esp.css"); + if (fileExists(cssFile)) { - #ifndef WEBSERVER_CSS - url = generate_external_URL(F("espeasy_default.css")); - #else addHtml(F("")); return; - #endif } - + #ifndef WEBSERVER_CSS addHtml(F("'); + #else + addHtml(F("")); + #endif } void serve_favicon() { From 090de793d8142ad49877d2595c2407d60b971213 Mon Sep 17 00:00:00 2001 From: TD-er Date: Sat, 4 Dec 2021 00:04:21 +0100 Subject: [PATCH 012/147] [Web] Allow to stream from file system (e.g. CSS inline) This may prevent additional calls to load the CSS from the file system in a separate HTTP GET call and also not loading the file into memory when streaming. --- src/src/Static/WebStaticData.cpp | 14 ++- src/src/WebServer/404.cpp | 4 +- src/src/WebServer/CustomPage.cpp | 10 -- src/src/WebServer/LoadFromFS.cpp | 154 ++++++++++++++++++------------- src/src/WebServer/LoadFromFS.h | 6 +- src/src/WebServer/RootPage.cpp | 11 +-- 6 files changed, 110 insertions(+), 89 deletions(-) diff --git a/src/src/Static/WebStaticData.cpp b/src/src/Static/WebStaticData.cpp index 3236c71a68..37c39ac6f7 100644 --- a/src/src/Static/WebStaticData.cpp +++ b/src/src/Static/WebStaticData.cpp @@ -3,6 +3,7 @@ #include "../Globals/Cache.h" #include "../Helpers/ESPEasy_Storage.h" #include "../WebServer/HTML_wrappers.h" +#include "../WebServer/LoadFromFS.h" String generate_external_URL(const String& fname) { String url; @@ -113,10 +114,15 @@ void serve_JS(JSfiles_e JSfile) { html_add_script_end(); return; #endif + addHtml(F("'); + html_add_script_end(); + return; } - addHtml(F("'); + // Now stream the file directly from the file system. + html_add_script(false); + streamFromFS(url); html_add_script_end(); } \ No newline at end of file diff --git a/src/src/WebServer/404.cpp b/src/src/WebServer/404.cpp index 1edbfc006a..35883426ad 100644 --- a/src/src/WebServer/404.cpp +++ b/src/src/WebServer/404.cpp @@ -32,9 +32,7 @@ void handleNotFound() { if (handle_rules_edit(web_server.uri())) { return; } #endif - if (loadFromFS(true, web_server.uri())) { return; } - - if (loadFromFS(false, web_server.uri())) { return; } + if (loadFromFS(web_server.uri())) { return; } String message = F("URI: "); message += web_server.uri(); message += F("\nMethod: "); diff --git a/src/src/WebServer/CustomPage.cpp b/src/src/WebServer/CustomPage.cpp index 8135cb3f58..98ed4d4381 100644 --- a/src/src/WebServer/CustomPage.cpp +++ b/src/src/WebServer/CustomPage.cpp @@ -31,19 +31,9 @@ boolean handle_custom(String path) { if (!clientIPallowed()) { return false; } -#ifdef ESP8266 - // For ESP32 remove the leading slash - path = path.substring(1); -#endif - // create a dynamic custom page, parsing task values into [#] placeholders and parsing %xx% system variables fs::File dataFile = tryOpenFile(path.c_str(), "r"); -#ifdef ESP8266 const bool dashboardPage = path.startsWith(F("dashboard")); -#endif -#ifdef ESP32 - const bool dashboardPage = path.startsWith(F("/dashboard")); -#endif if (!dataFile && !dashboardPage) { return false; // unknown file that does not exist... diff --git a/src/src/WebServer/LoadFromFS.cpp b/src/src/WebServer/LoadFromFS.cpp index 6f9992a7c6..0a7c322496 100644 --- a/src/src/WebServer/LoadFromFS.cpp +++ b/src/src/WebServer/LoadFromFS.cpp @@ -1,46 +1,66 @@ #include "../WebServer/LoadFromFS.h" -#include "../WebServer/WebServer.h" -#include "../WebServer/CustomPage.h" #include "../Globals/RamTracker.h" + #include "../Helpers/ESPEasy_Storage.h" #include "../Helpers/Network.h" +#include "../WebServer/CustomPage.h" +#include "../WebServer/HTML_wrappers.h" +#include "../WebServer/WebServer.h" + #ifdef FEATURE_SD -#include -#endif +# include +#endif // ifdef FEATURE_SD -bool match_ext(const String& path, const __FlashStringHelper * ext) { - return (path.endsWith(ext) || path.endsWith(String(ext) + F(".gz"))); +bool match_ext(const String& path, const __FlashStringHelper *ext) { + return path.endsWith(ext) || path.endsWith(String(ext) + F(".gz")); } +bool gzipEncoded(const String& path) { + return path.endsWith(F(".gz")); +} -// ******************************************************************************** -// Web Interface server web file from FS -// ******************************************************************************** -bool loadFromFS(boolean spiffs, String path) { - // path is a deepcopy, since it will be changed here. - #ifndef BUILD_NO_RAM_TRACKER - checkRAM(F("loadFromFS")); - #endif - - statusLED(true); - - String dataType = F("text/plain"); - bool mustCheckCredentials = false; - +String fileFromUrl(String path) { const int questionmarkPos = path.indexOf('?'); + if (questionmarkPos >= 0) { path = path.substring(0, questionmarkPos); } + // First prepend slash if (!path.startsWith(F("/"))) { path = String(F("/")) + path; } - if (path.endsWith(F("/"))) { path += F("index.htm"); } + #ifdef ESP8266 + // Remove leading slash to generate filename from it. + if (path.startsWith(F("/"))) { + path = path.substring(1); + } + #endif + + return path; +} + +// ******************************************************************************** +// Web Interface server web file from FS +// ******************************************************************************** +bool loadFromFS(String path) { + // path is a deepcopy, since it will be changed here. + #ifndef BUILD_NO_RAM_TRACKER + checkRAM(F("loadFromFS")); + #endif // ifndef BUILD_NO_RAM_TRACKER + + statusLED(true); + + String dataType = F("text/plain"); + bool mustCheckCredentials = false; + + path = fileFromUrl(path); + if (path.endsWith(F(".src"))) { path = path.substring(0, path.lastIndexOf(".")); } else if (match_ext(path, F(".htm")) || match_ext(path, F(".html"))) { dataType = F("text/html"); } else if (match_ext(path, F(".css"))) { dataType = F("text/css"); } @@ -52,15 +72,15 @@ bool loadFromFS(boolean spiffs, String path) { else if (path.endsWith(F(".svg"))) { dataType = F("image/svg+xml"); } else if (path.endsWith(F(".json"))) { dataType = F("application/json"); } else if (path.endsWith(F(".txt")) || - path.endsWith(F(".dat"))) { + path.endsWith(F(".dat"))) { mustCheckCredentials = true; - dataType = F("application/octet-stream"); + dataType = F("application/octet-stream"); } #ifdef WEBSERVER_CUSTOM else if (path.endsWith(F(".esp"))) { - return handle_custom(path); + return handle_custom(path); } -#endif +#endif // ifdef WEBSERVER_CUSTOM else { mustCheckCredentials = true; } @@ -78,53 +98,63 @@ bool loadFromFS(boolean spiffs, String path) { } #endif // ifndef BUILD_NO_DEBUG -#if !defined(ESP32) - path = path.substring(1); -#endif // if !defined(ESP32) + fs::File f; - if (spiffs) - { - if (!fileExists(path)) { - return false; - } - fs::File dataFile = tryOpenFile(path.c_str(), "r"); - - if (!dataFile) { - return false; - } + // Search flash file system first, then SD if present + f = tryOpenFile(path.c_str(), "r"); + #ifdef FEATURE_SD + if (!f) { + f = SD.open(path.c_str(), "r"); + } + #endif // ifdef FEATURE_SD - // prevent reloading stuff on every click - web_server.sendHeader(F("Cache-Control"), F("max-age=3600, public")); - web_server.sendHeader(F("Vary"), "*"); - web_server.sendHeader(F("ETag"), F("\"2.0.0\"")); + if (!f) { + return false; + } - if (path.endsWith(F(".dat"))) { - web_server.sendHeader(F("Content-Disposition"), F("attachment;")); - } + // prevent reloading stuff on every click + web_server.sendHeader(F("Cache-Control"), F("max-age=3600, public")); + web_server.sendHeader(F("Vary"), "*"); + web_server.sendHeader(F("ETag"), F("\"2.0.0\"")); - web_server.streamFile(dataFile, dataType); - dataFile.close(); + if (path.endsWith(F(".dat"))) { + web_server.sendHeader(F("Content-Disposition"), F("attachment;")); } - else - { -#ifdef FEATURE_SD - File dataFile = SD.open(path.c_str()); + if (gzipEncoded(path)) { + web_server.sendHeader(F("Content-Encoding"), F("gzip")); + } + + web_server.streamFile(f, dataType); + f.close(); + + statusLED(true); + return true; +} + +bool streamFromFS(String path) { + // path is a deepcopy, since it will be changed here. + path = fileFromUrl(path); + statusLED(true); - if (!dataFile) { - return false; - } + fs::File f; - if (path.endsWith(F(".DAT"))) { - web_server.sendHeader(F("Content-Disposition"), F("attachment;")); - } - web_server.streamFile(dataFile, dataType); - dataFile.close(); -#else // ifdef FEATURE_SD + // Search flash file system first, then SD if present + f = tryOpenFile(path.c_str(), "r"); + #ifdef FEATURE_SD + if (!f) { + f = SD.open(path.c_str(), "r"); + } + #endif // ifdef FEATURE_SD - // File from SD requested, but no SD support. + if (!f) { return false; -#endif // ifdef FEATURE_SD + } + + while (f.available()) { + addHtml((char)f.read()); } statusLED(true); + + f.close(); return true; } diff --git a/src/src/WebServer/LoadFromFS.h b/src/src/WebServer/LoadFromFS.h index de51704aa5..352ad980ad 100644 --- a/src/src/WebServer/LoadFromFS.h +++ b/src/src/WebServer/LoadFromFS.h @@ -4,6 +4,10 @@ #include "../WebServer/common.h" -bool loadFromFS(boolean spiffs, String path); +bool loadFromFS(String path); + + +// Send the content of a file directly to the webserver, like addHtml() +bool streamFromFS(String path); #endif \ No newline at end of file diff --git a/src/src/WebServer/RootPage.cpp b/src/src/WebServer/RootPage.cpp index f939d0fbaa..dac0c3d11c 100644 --- a/src/src/WebServer/RootPage.cpp +++ b/src/src/WebServer/RootPage.cpp @@ -81,15 +81,8 @@ void handle_root() { navMenuIndex = 0; // if index.htm exists on FS serve that one (first check if gziped version exists) - if (loadFromFS(true, F("/index.htm.gz"))) { return; } - #ifdef FEATURE_SD - if (loadFromFS(false, F("/index.htm.gz"))) { return; } - #endif - - if (loadFromFS(true, F("/index.htm"))) { return; } - #ifdef FEATURE_SD - if (loadFromFS(false, F("/index.htm"))) { return; } - #endif + if (loadFromFS(F("/index.htm.gz"))) { return; } + if (loadFromFS(F("/index.htm"))) { return; } TXBuffer.startStream(); From 4cc53effef32200a100df01f4a7f2d43a812b34d Mon Sep 17 00:00:00 2001 From: TD-er Date: Sat, 4 Dec 2021 00:48:01 +0100 Subject: [PATCH 013/147] [ESP32-S2] Add PlatformIO envs for ESP32-s2 --- platformio_esp32_envs.ini | 75 +++++++++++++++++++++++++++++++++------ 1 file changed, 65 insertions(+), 10 deletions(-) diff --git a/platformio_esp32_envs.ini b/platformio_esp32_envs.ini index 0a8b58b44a..d39582745b 100644 --- a/platformio_esp32_envs.ini +++ b/platformio_esp32_envs.ini @@ -27,6 +27,18 @@ build_flags = ${core_esp32_3_4_0.build_flags} -DCORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_NONE monitor_filters = esp32_exception_decoder + +[esp32s2_common] +extends = esp32_common +build_flags = ${esp32_common.build_flags} + -DESP32S2 + -DBOARD_HAS_PSRAM + -DESP32_ENABLE_PSRAM +board = esp32-s2-saola-1 +platform = ${core_esp32_3_4_0_esp32s2.platform} +platform_packages = ${core_esp32_3_4_0_esp32s2.platform_packages} + + [esp32_LittleFS] extends = esp32_common platform_packages = ${esp32_common.platform_packages} @@ -49,18 +61,11 @@ extra_scripts = ${esp32_common.extra_scripts} ; ESP32-S2 [env:custom_ESP32s2_4M316k] -extends = esp32_common -build_flags = ${esp32_common.build_flags} +extends = esp32s2_common +build_flags = ${esp32s2_common.build_flags} -DPLUGIN_BUILD_CUSTOM - -DESP32S2 - -DBOARD_HAS_PSRAM - -DESP32_ENABLE_PSRAM - -mfix-esp32-psram-cache-issue -board = esp32-s2-saola-1 -extra_scripts = ${esp32_common.extra_scripts} +extra_scripts = ${esp32s2_common.extra_scripts} pre:tools/pio/pre_custom_esp32.py -platform = ${core_esp32_3_4_0_esp32s2.platform} -platform_packages = ${core_esp32_3_4_0_esp32s2.platform_packages} [env:custom_IR_ESP32_4M316k] @@ -327,6 +332,56 @@ platform = ${env:test_D_ESP32-wrover-kit_4M316k.platform} build_flags = ${env:test_D_ESP32-wrover-kit_4M316k.build_flags} -DHAS_ETHERNET -DTESTING_USE_RTTTL +;;; ESP32-s2 *********************************************************** + +[env:normal_ESP32s2_4M316k] +extends = esp32s2_common +lib_deps = ${esp32s2_common.lib_deps}, ServoESP32 +build_flags = ${esp32s2_common.build_flags} + -DFEATURE_ARDUINO_OTA + +[env:test_A_ESP32s2_4M316k] +extends = esp32s2_common +build_flags = ${esp32s2_common.build_flags} + -DFEATURE_ARDUINO_OTA + -DPLUGIN_SET_TEST_ESP32 + -DTESTING_USE_RTTTL + +[env:test_B_ESP32s2_4M316k] +extends = esp32s2_common +build_flags = ${esp32s2_common.build_flags} + -DFEATURE_ARDUINO_OTA + -DPLUGIN_SET_TEST_B_ESP32 + -DTESTING_USE_RTTTL + +[env:test_C_ESP32s2_4M316k] +extends = esp32s2_common +build_flags = ${esp32s2_common.build_flags} + -DFEATURE_ARDUINO_OTA + -DPLUGIN_SET_TEST_C_ESP32 + -DTESTING_USE_RTTTL + +[env:test_D_ESP32s2_4M316k] +extends = esp32s2_common +build_flags = ${esp32s2_common.build_flags} + -DFEATURE_ARDUINO_OTA + -DPLUGIN_SET_TEST_D_ESP32 + -DTESTING_USE_RTTTL + + +[env:energy_ESP32s2_4M316k] +extends = esp32s2_common +lib_deps = ${esp32s2_common.lib_deps}, ServoESP32 +build_flags = ${esp32s2_common.build_flags} + -DFEATURE_ARDUINO_OTA + -D PLUGIN_ENERGY_COLLECTION + +[env:display_ESP32s2_4M316k] +extends = esp32s2_common +lib_deps = ${esp32s2_common.lib_deps}, ServoESP32 +build_flags = ${esp32s2_common.build_flags} + -DFEATURE_ARDUINO_OTA + -D PLUGIN_DISPLAY_COLLECTION ; A Lolin D32 PRO with 16MB Flash, allowing 4MB sketch size, and file storage using the default (SPIFFS) filesystem [max_ESP32_16M] From 65969533388964088314201dcb2e3eba9bf11da6 Mon Sep 17 00:00:00 2001 From: TD-er Date: Sat, 4 Dec 2021 02:05:08 +0100 Subject: [PATCH 014/147] [IR] Cleanup check for redefine IR plugin USES --- src/src/CustomBuild/define_plugin_sets.h | 25 ++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/src/src/CustomBuild/define_plugin_sets.h b/src/src/CustomBuild/define_plugin_sets.h index 4130947376..8a912c0cee 100644 --- a/src/src/CustomBuild/define_plugin_sets.h +++ b/src/src/CustomBuild/define_plugin_sets.h @@ -447,9 +447,13 @@ To create/register a plugin, you have to : #if !defined(PLUGIN_DESCR) && !defined(PLUGIN_BUILD_MAX_ESP32) #define PLUGIN_DESCR "IR" #endif - #define USES_P016 // IR + #ifndef USES_P016 + #define USES_P016 // IR + #endif #define P016_SEND_IR_TO_CONTROLLER false //IF true then the JSON replay solution is transmited back to the condroller. - #define USES_P035 // IRTX + #ifndef USES_P035 + #define USES_P035 // IRTX + #endif #define P016_P035_USE_RAW_RAW2 //Use the RAW and RAW2 encodings, disabling it saves 3.7Kb #endif @@ -457,9 +461,13 @@ To create/register a plugin, you have to : #if !defined(PLUGIN_DESCR) && !defined(PLUGIN_BUILD_MAX_ESP32) #define PLUGIN_DESCR "IR Extended" #endif // PLUGIN_DESCR - #define USES_P016 // IR + #ifndef USES_P016 + #define USES_P016 // IR + #endif #define P016_SEND_IR_TO_CONTROLLER false //IF true then the JSON replay solution is transmited back to the condroller. - #define USES_P035 // IRTX + #ifndef USES_P035 + #define USES_P035 // IRTX + #endif // The following define is needed for extended decoding of A/C Messages and or using standardised common arguments for controlling all deeply supported A/C units #define P016_P035_Extended_AC #define P016_P035_USE_RAW_RAW2 //Use the RAW and RAW2 encodings, disabling it saves 3.7Kb @@ -474,7 +482,9 @@ To create/register a plugin, you have to : #if !defined(PLUGIN_DESCR) && !defined(PLUGIN_BUILD_MAX_ESP32) #define PLUGIN_DESCR "IR Extended, no IR RX" #endif // PLUGIN_DESCR - #define USES_P035 // IRTX + #ifndef USES_P035 + #define USES_P035 // IRTX + #endif // The following define is needed for extended decoding of A/C Messages and or using standardised common arguments for controlling all deeply supported A/C units #define P016_P035_Extended_AC #define P016_P035_USE_RAW_RAW2 //Use the RAW and RAW2 encodings, disabling it saves 3.7Kb @@ -601,7 +611,10 @@ To create/register a plugin, you have to : #ifdef PLUGIN_SET_MAGICHOME_IR #define PLUGIN_SET_ONLY_LEDSTRIP - #define USES_P016 // IR + #ifndef USES_P016 + #define USES_P016 // IR + #endif + #endif From 0bcafacf715b3bb63ed5f4775b657a7714adc4f6 Mon Sep 17 00:00:00 2001 From: TD-er Date: Sat, 4 Dec 2021 02:08:18 +0100 Subject: [PATCH 015/147] Revert "[Windows Build] Convert IRremoteESP8266 to single cpp file" This reverts commit ea3418c75bf833b4f2d99bb8b644fac58a02db04. --- .../examples/IRMQTTServer/IRMQTTServer.ino | 2 +- lib/IRremoteESP8266/src/IRac.cpp | 4319 +++++++++++++++++ lib/IRremoteESP8266/src/IRac.h | 76 +- lib/IRremoteESP8266/src/IRrecv.cpp | 1937 ++++++++ lib/IRremoteESP8266/src/IRrecv.h | 2 +- lib/IRremoteESP8266/src/IRsend.cpp | 1317 +++++ lib/IRremoteESP8266/src/IRsend.h | 2 +- lib/IRremoteESP8266/src/IRtext.cpp | 383 ++ lib/IRremoteESP8266/src/IRtimer.cpp | 78 + lib/IRremoteESP8266/src/IRutils.cpp | 1266 +++++ lib/IRremoteESP8266/src/IRutils.h | 4 +- lib/IRremoteESP8266/src/i18n.h | 2 +- lib/IRremoteESP8266/src/ir_Airwell.cpp | 286 ++ lib/IRremoteESP8266/src/ir_Airwell.h | 6 +- lib/IRremoteESP8266/src/ir_Aiwa.cpp | 105 + lib/IRremoteESP8266/src/ir_Amcor.cpp | 354 ++ lib/IRremoteESP8266/src/ir_Amcor.h | 6 +- lib/IRremoteESP8266/src/ir_Argo.cpp | 470 ++ lib/IRremoteESP8266/src/ir_Argo.h | 6 +- lib/IRremoteESP8266/src/ir_Arris.cpp | 123 + lib/IRremoteESP8266/src/ir_Bose.cpp | 69 + lib/IRremoteESP8266/src/ir_Carrier.cpp | 535 ++ lib/IRremoteESP8266/src/ir_Carrier.h | 6 +- lib/IRremoteESP8266/src/ir_Coolix.cpp | 697 +++ lib/IRremoteESP8266/src/ir_Coolix.h | 6 +- lib/IRremoteESP8266/src/ir_Corona.cpp | 575 +++ lib/IRremoteESP8266/src/ir_Corona.h | 6 +- lib/IRremoteESP8266/src/ir_Daikin.cpp | 3735 ++++++++++++++ lib/IRremoteESP8266/src/ir_Daikin.h | 8 +- lib/IRremoteESP8266/src/ir_Delonghi.cpp | 470 ++ lib/IRremoteESP8266/src/ir_Delonghi.h | 6 +- lib/IRremoteESP8266/src/ir_Denon.cpp | 122 + lib/IRremoteESP8266/src/ir_Dish.cpp | 103 + lib/IRremoteESP8266/src/ir_Doshisha.cpp | 124 + lib/IRremoteESP8266/src/ir_Ecoclim.cpp | 423 ++ lib/IRremoteESP8266/src/ir_Ecoclim.h | 6 +- lib/IRremoteESP8266/src/ir_Electra.cpp | 402 ++ lib/IRremoteESP8266/src/ir_Electra.h | 6 +- lib/IRremoteESP8266/src/ir_EliteScreens.cpp | 89 + lib/IRremoteESP8266/src/ir_Epson.cpp | 111 + lib/IRremoteESP8266/src/ir_Fujitsu.cpp | 1043 ++++ lib/IRremoteESP8266/src/ir_Fujitsu.h | 8 +- lib/IRremoteESP8266/src/ir_GICable.cpp | 95 + lib/IRremoteESP8266/src/ir_GlobalCache.cpp | 63 + lib/IRremoteESP8266/src/ir_Goodweather.cpp | 499 ++ lib/IRremoteESP8266/src/ir_Goodweather.h | 6 +- lib/IRremoteESP8266/src/ir_Gree.cpp | 715 +++ lib/IRremoteESP8266/src/ir_Gree.h | 6 +- lib/IRremoteESP8266/src/ir_Haier.cpp | 1241 +++++ lib/IRremoteESP8266/src/ir_Haier.h | 6 +- lib/IRremoteESP8266/src/ir_Hitachi.cpp | 1580 ++++++ lib/IRremoteESP8266/src/ir_Hitachi.h | 6 +- lib/IRremoteESP8266/src/ir_Inax.cpp | 73 + lib/IRremoteESP8266/src/ir_JVC.cpp | 131 + lib/IRremoteESP8266/src/ir_Kelon.cpp | 502 ++ lib/IRremoteESP8266/src/ir_Kelon.h | 8 +- lib/IRremoteESP8266/src/ir_Kelvinator.cpp | 525 ++ lib/IRremoteESP8266/src/ir_Kelvinator.h | 6 +- lib/IRremoteESP8266/src/ir_LG.cpp | 838 ++++ lib/IRremoteESP8266/src/ir_LG.h | 8 +- lib/IRremoteESP8266/src/ir_Lasertag.cpp | 116 + lib/IRremoteESP8266/src/ir_Lego.cpp | 106 + lib/IRremoteESP8266/src/ir_Lutron.cpp | 143 + lib/IRremoteESP8266/src/ir_MWM.cpp | 197 + lib/IRremoteESP8266/src/ir_Magiquest.cpp | 154 + lib/IRremoteESP8266/src/ir_Magiquest.h | 4 +- lib/IRremoteESP8266/src/ir_Metz.cpp | 101 + lib/IRremoteESP8266/src/ir_Midea.cpp | 796 +++ lib/IRremoteESP8266/src/ir_Midea.h | 6 +- lib/IRremoteESP8266/src/ir_MilesTag2.cpp | 113 + lib/IRremoteESP8266/src/ir_Mirage.cpp | 70 + lib/IRremoteESP8266/src/ir_Mitsubishi.cpp | 1623 +++++++ lib/IRremoteESP8266/src/ir_Mitsubishi.h | 6 +- .../src/ir_MitsubishiHeavy.cpp | 1050 ++++ lib/IRremoteESP8266/src/ir_MitsubishiHeavy.h | 6 +- lib/IRremoteESP8266/src/ir_Multibrackets.cpp | 115 + lib/IRremoteESP8266/src/ir_NEC.cpp | 140 + lib/IRremoteESP8266/src/ir_NEC.h | 2 +- lib/IRremoteESP8266/src/ir_Neoclima.cpp | 608 +++ lib/IRremoteESP8266/src/ir_Neoclima.h | 6 +- lib/IRremoteESP8266/src/ir_Nikai.cpp | 74 + lib/IRremoteESP8266/src/ir_Panasonic.cpp | 1341 +++++ lib/IRremoteESP8266/src/ir_Panasonic.h | 6 +- lib/IRremoteESP8266/src/ir_Pioneer.cpp | 138 + lib/IRremoteESP8266/src/ir_Pronto.cpp | 107 + lib/IRremoteESP8266/src/ir_RC5_RC6.cpp | 454 ++ lib/IRremoteESP8266/src/ir_RCMM.cpp | 164 + lib/IRremoteESP8266/src/ir_Rhoss.cpp | 364 ++ lib/IRremoteESP8266/src/ir_Rhoss.h | 6 +- lib/IRremoteESP8266/src/ir_Samsung.cpp | 832 ++++ lib/IRremoteESP8266/src/ir_Samsung.h | 6 +- lib/IRremoteESP8266/src/ir_Sanyo.cpp | 978 ++++ lib/IRremoteESP8266/src/ir_Sanyo.h | 6 +- lib/IRremoteESP8266/src/ir_Sharp.cpp | 976 ++++ lib/IRremoteESP8266/src/ir_Sharp.h | 10 +- lib/IRremoteESP8266/src/ir_Sherwood.cpp | 24 + lib/IRremoteESP8266/src/ir_Sony.cpp | 191 + lib/IRremoteESP8266/src/ir_Symphony.cpp | 95 + lib/IRremoteESP8266/src/ir_Tcl.cpp | 529 ++ lib/IRremoteESP8266/src/ir_Tcl.h | 8 +- lib/IRremoteESP8266/src/ir_Technibel.cpp | 408 ++ lib/IRremoteESP8266/src/ir_Technibel.h | 6 +- lib/IRremoteESP8266/src/ir_Teco.cpp | 375 ++ lib/IRremoteESP8266/src/ir_Teco.h | 6 +- lib/IRremoteESP8266/src/ir_Teknopoint.cpp | 75 + lib/IRremoteESP8266/src/ir_Toshiba.cpp | 529 ++ lib/IRremoteESP8266/src/ir_Toshiba.h | 6 +- lib/IRremoteESP8266/src/ir_Transcold.cpp | 500 ++ lib/IRremoteESP8266/src/ir_Transcold.h | 6 +- lib/IRremoteESP8266/src/ir_Trotec.cpp | 642 +++ lib/IRremoteESP8266/src/ir_Trotec.h | 6 +- lib/IRremoteESP8266/src/ir_Truma.cpp | 340 ++ lib/IRremoteESP8266/src/ir_Truma.h | 6 +- lib/IRremoteESP8266/src/ir_Vestel.cpp | 572 +++ lib/IRremoteESP8266/src/ir_Vestel.h | 6 +- lib/IRremoteESP8266/src/ir_Voltas.cpp | 516 ++ lib/IRremoteESP8266/src/ir_Voltas.h | 6 +- lib/IRremoteESP8266/src/ir_Whirlpool.cpp | 657 +++ lib/IRremoteESP8266/src/ir_Whirlpool.h | 6 +- lib/IRremoteESP8266/src/ir_Whynter.cpp | 103 + lib/IRremoteESP8266/src/ir_Xmp.cpp | 226 + lib/IRremoteESP8266/src/ir_Zepeal.cpp | 94 + lib/IRremoteESP8266/test/IRac_test.cpp | 76 +- lib/IRremoteESP8266/test/IRrecv_test.cpp | 10 +- lib/IRremoteESP8266/test/IRrecv_test.h | 2 +- lib/IRremoteESP8266/test/IRsend_test.cpp | 8 +- lib/IRremoteESP8266/test/IRsend_test.h | 6 +- lib/IRremoteESP8266/test/IRutils_test.cpp | 10 +- lib/IRremoteESP8266/test/ir_Airwell_test.cpp | 12 +- lib/IRremoteESP8266/test/ir_Aiwa_test.cpp | 4 +- lib/IRremoteESP8266/test/ir_Amcor_test.cpp | 14 +- lib/IRremoteESP8266/test/ir_Argo_test.cpp | 12 +- lib/IRremoteESP8266/test/ir_Arris_test.cpp | 10 +- lib/IRremoteESP8266/test/ir_Bose_test.cpp | 10 +- lib/IRremoteESP8266/test/ir_Carrier_test.cpp | 10 +- lib/IRremoteESP8266/test/ir_Coolix_test.cpp | 8 +- lib/IRremoteESP8266/test/ir_Corona_test.cpp | 12 +- lib/IRremoteESP8266/test/ir_Daikin_test.cpp | 12 +- lib/IRremoteESP8266/test/ir_Delonghi_test.cpp | 14 +- lib/IRremoteESP8266/test/ir_Denon_test.cpp | 4 +- lib/IRremoteESP8266/test/ir_Dish_test.cpp | 4 +- lib/IRremoteESP8266/test/ir_Doshisha_test.cpp | 10 +- lib/IRremoteESP8266/test/ir_Ecoclim_test.cpp | 14 +- lib/IRremoteESP8266/test/ir_Electra_test.cpp | 12 +- .../test/ir_EliteScreens_test.cpp | 6 +- lib/IRremoteESP8266/test/ir_Epson_test.cpp | 10 +- lib/IRremoteESP8266/test/ir_Fujitsu_test.cpp | 10 +- lib/IRremoteESP8266/test/ir_GICable_test.cpp | 4 +- .../test/ir_GlobalCache_test.cpp | 4 +- .../test/ir_Goodweather_test.cpp | 12 +- lib/IRremoteESP8266/test/ir_Gree_test.cpp | 14 +- lib/IRremoteESP8266/test/ir_Haier_test.cpp | 12 +- lib/IRremoteESP8266/test/ir_Hitachi_test.cpp | 14 +- lib/IRremoteESP8266/test/ir_Inax_test.cpp | 6 +- lib/IRremoteESP8266/test/ir_JVC_test.cpp | 4 +- lib/IRremoteESP8266/test/ir_Kelon_test.cpp | 10 +- .../test/ir_Kelvinator_test.cpp | 14 +- lib/IRremoteESP8266/test/ir_LG_test.cpp | 8 +- lib/IRremoteESP8266/test/ir_Lasertag_test.cpp | 4 +- lib/IRremoteESP8266/test/ir_Lego_test.cpp | 8 +- lib/IRremoteESP8266/test/ir_Lutron_test.cpp | 4 +- lib/IRremoteESP8266/test/ir_MWM_test.cpp | 8 +- .../test/ir_Magiquest_test.cpp | 8 +- lib/IRremoteESP8266/test/ir_Metz_test.cpp | 10 +- lib/IRremoteESP8266/test/ir_Midea_test.cpp | 8 +- .../test/ir_Milestag2_test.cpp | 12 +- lib/IRremoteESP8266/test/ir_Mirage_test.cpp | 10 +- .../test/ir_MitsubishiHeavy_test.cpp | 14 +- .../test/ir_Mitsubishi_test.cpp | 10 +- .../test/ir_Multibrackets_test.cpp | 10 +- lib/IRremoteESP8266/test/ir_NEC_test.cpp | 4 +- lib/IRremoteESP8266/test/ir_Neoclima_test.cpp | 12 +- lib/IRremoteESP8266/test/ir_Nikai_test.cpp | 4 +- .../test/ir_Panasonic_test.cpp | 16 +- lib/IRremoteESP8266/test/ir_Pioneer_test.cpp | 6 +- lib/IRremoteESP8266/test/ir_Pronto_test.cpp | 4 +- lib/IRremoteESP8266/test/ir_RC5_RC6_test.cpp | 4 +- lib/IRremoteESP8266/test/ir_RCMM_test.cpp | 4 +- lib/IRremoteESP8266/test/ir_Rhoss_test.cpp | 14 +- lib/IRremoteESP8266/test/ir_Samsung_test.cpp | 12 +- lib/IRremoteESP8266/test/ir_Sanyo_test.cpp | 12 +- lib/IRremoteESP8266/test/ir_Sharp_test.cpp | 12 +- lib/IRremoteESP8266/test/ir_Sherwood_test.cpp | 4 +- lib/IRremoteESP8266/test/ir_Sony_test.cpp | 4 +- lib/IRremoteESP8266/test/ir_Symphony_test.cpp | 10 +- lib/IRremoteESP8266/test/ir_Tcl_test.cpp | 12 +- .../test/ir_Technibel_test.cpp | 14 +- lib/IRremoteESP8266/test/ir_Teco_test.cpp | 12 +- .../test/ir_Teknopoint_test.cpp | 10 +- lib/IRremoteESP8266/test/ir_Toshiba_test.cpp | 12 +- .../test/ir_Transcold_test.cpp | 10 +- lib/IRremoteESP8266/test/ir_Trotec_test.cpp | 12 +- lib/IRremoteESP8266/test/ir_Truma_test.cpp | 10 +- lib/IRremoteESP8266/test/ir_Vestel_test.cpp | 12 +- lib/IRremoteESP8266/test/ir_Voltas_test.cpp | 12 +- .../test/ir_Whirlpool_test.cpp | 12 +- lib/IRremoteESP8266/test/ir_Whynter_test.cpp | 4 +- lib/IRremoteESP8266/test/ir_Xmp_test.cpp | 10 +- lib/IRremoteESP8266/test/ir_Zepeal_test.cpp | 10 +- .../tools/auto_analyse_raw_data.py | 6 +- .../tools/auto_analyse_raw_data_test.py | 24 +- lib/IRremoteESP8266/tools/gc_decode.cpp | 8 +- lib/IRremoteESP8266/tools/mode2_decode.cpp | 6 +- platformio.ini | 2 +- 204 files changed, 42613 insertions(+), 579 deletions(-) create mode 100644 lib/IRremoteESP8266/src/IRac.cpp create mode 100644 lib/IRremoteESP8266/src/IRrecv.cpp create mode 100644 lib/IRremoteESP8266/src/IRsend.cpp create mode 100644 lib/IRremoteESP8266/src/IRtext.cpp create mode 100644 lib/IRremoteESP8266/src/IRtimer.cpp create mode 100644 lib/IRremoteESP8266/src/IRutils.cpp create mode 100644 lib/IRremoteESP8266/src/ir_Airwell.cpp create mode 100644 lib/IRremoteESP8266/src/ir_Aiwa.cpp create mode 100644 lib/IRremoteESP8266/src/ir_Amcor.cpp create mode 100644 lib/IRremoteESP8266/src/ir_Argo.cpp create mode 100644 lib/IRremoteESP8266/src/ir_Arris.cpp create mode 100644 lib/IRremoteESP8266/src/ir_Bose.cpp create mode 100644 lib/IRremoteESP8266/src/ir_Carrier.cpp create mode 100644 lib/IRremoteESP8266/src/ir_Coolix.cpp create mode 100644 lib/IRremoteESP8266/src/ir_Corona.cpp create mode 100644 lib/IRremoteESP8266/src/ir_Daikin.cpp create mode 100644 lib/IRremoteESP8266/src/ir_Delonghi.cpp create mode 100644 lib/IRremoteESP8266/src/ir_Denon.cpp create mode 100644 lib/IRremoteESP8266/src/ir_Dish.cpp create mode 100644 lib/IRremoteESP8266/src/ir_Doshisha.cpp create mode 100644 lib/IRremoteESP8266/src/ir_Ecoclim.cpp create mode 100644 lib/IRremoteESP8266/src/ir_Electra.cpp create mode 100644 lib/IRremoteESP8266/src/ir_EliteScreens.cpp create mode 100644 lib/IRremoteESP8266/src/ir_Epson.cpp create mode 100644 lib/IRremoteESP8266/src/ir_Fujitsu.cpp create mode 100644 lib/IRremoteESP8266/src/ir_GICable.cpp create mode 100644 lib/IRremoteESP8266/src/ir_GlobalCache.cpp create mode 100644 lib/IRremoteESP8266/src/ir_Goodweather.cpp create mode 100644 lib/IRremoteESP8266/src/ir_Gree.cpp create mode 100644 lib/IRremoteESP8266/src/ir_Haier.cpp create mode 100644 lib/IRremoteESP8266/src/ir_Hitachi.cpp create mode 100644 lib/IRremoteESP8266/src/ir_Inax.cpp create mode 100644 lib/IRremoteESP8266/src/ir_JVC.cpp create mode 100644 lib/IRremoteESP8266/src/ir_Kelon.cpp create mode 100644 lib/IRremoteESP8266/src/ir_Kelvinator.cpp create mode 100644 lib/IRremoteESP8266/src/ir_LG.cpp create mode 100644 lib/IRremoteESP8266/src/ir_Lasertag.cpp create mode 100644 lib/IRremoteESP8266/src/ir_Lego.cpp create mode 100644 lib/IRremoteESP8266/src/ir_Lutron.cpp create mode 100644 lib/IRremoteESP8266/src/ir_MWM.cpp create mode 100644 lib/IRremoteESP8266/src/ir_Magiquest.cpp create mode 100644 lib/IRremoteESP8266/src/ir_Metz.cpp create mode 100644 lib/IRremoteESP8266/src/ir_Midea.cpp create mode 100644 lib/IRremoteESP8266/src/ir_MilesTag2.cpp create mode 100644 lib/IRremoteESP8266/src/ir_Mirage.cpp create mode 100644 lib/IRremoteESP8266/src/ir_Mitsubishi.cpp create mode 100644 lib/IRremoteESP8266/src/ir_MitsubishiHeavy.cpp create mode 100644 lib/IRremoteESP8266/src/ir_Multibrackets.cpp create mode 100644 lib/IRremoteESP8266/src/ir_NEC.cpp create mode 100644 lib/IRremoteESP8266/src/ir_Neoclima.cpp create mode 100644 lib/IRremoteESP8266/src/ir_Nikai.cpp create mode 100644 lib/IRremoteESP8266/src/ir_Panasonic.cpp create mode 100644 lib/IRremoteESP8266/src/ir_Pioneer.cpp create mode 100644 lib/IRremoteESP8266/src/ir_Pronto.cpp create mode 100644 lib/IRremoteESP8266/src/ir_RC5_RC6.cpp create mode 100644 lib/IRremoteESP8266/src/ir_RCMM.cpp create mode 100644 lib/IRremoteESP8266/src/ir_Rhoss.cpp create mode 100644 lib/IRremoteESP8266/src/ir_Samsung.cpp create mode 100644 lib/IRremoteESP8266/src/ir_Sanyo.cpp create mode 100644 lib/IRremoteESP8266/src/ir_Sharp.cpp create mode 100644 lib/IRremoteESP8266/src/ir_Sherwood.cpp create mode 100644 lib/IRremoteESP8266/src/ir_Sony.cpp create mode 100644 lib/IRremoteESP8266/src/ir_Symphony.cpp create mode 100644 lib/IRremoteESP8266/src/ir_Tcl.cpp create mode 100644 lib/IRremoteESP8266/src/ir_Technibel.cpp create mode 100644 lib/IRremoteESP8266/src/ir_Teco.cpp create mode 100644 lib/IRremoteESP8266/src/ir_Teknopoint.cpp create mode 100644 lib/IRremoteESP8266/src/ir_Toshiba.cpp create mode 100644 lib/IRremoteESP8266/src/ir_Transcold.cpp create mode 100644 lib/IRremoteESP8266/src/ir_Trotec.cpp create mode 100644 lib/IRremoteESP8266/src/ir_Truma.cpp create mode 100644 lib/IRremoteESP8266/src/ir_Vestel.cpp create mode 100644 lib/IRremoteESP8266/src/ir_Voltas.cpp create mode 100644 lib/IRremoteESP8266/src/ir_Whirlpool.cpp create mode 100644 lib/IRremoteESP8266/src/ir_Whynter.cpp create mode 100644 lib/IRremoteESP8266/src/ir_Xmp.cpp create mode 100644 lib/IRremoteESP8266/src/ir_Zepeal.cpp diff --git a/lib/IRremoteESP8266/examples/IRMQTTServer/IRMQTTServer.ino b/lib/IRremoteESP8266/examples/IRMQTTServer/IRMQTTServer.ino index 5a93dfa7a2..180c4214c3 100644 --- a/lib/IRremoteESP8266/examples/IRMQTTServer/IRMQTTServer.ino +++ b/lib/IRremoteESP8266/examples/IRMQTTServer/IRMQTTServer.ino @@ -329,7 +329,7 @@ * */ -#include "../src/IRMQTTServer.h" +#include "IRMQTTServer.h" #include #include diff --git a/lib/IRremoteESP8266/src/IRac.cpp b/lib/IRremoteESP8266/src/IRac.cpp new file mode 100644 index 0000000000..e3e0874bae --- /dev/null +++ b/lib/IRremoteESP8266/src/IRac.cpp @@ -0,0 +1,4319 @@ +// Copyright 2019 David Conran + +// Provide a universal/standard interface for sending A/C nessages. +// It does not provide complete and maximum granular control but tries +// to offer most common functionality across all supported devices. + +#include "IRac.h" +#ifndef UNIT_TEST +#include +#endif +#include +#ifndef ARDUINO +#include +#endif +#include "IRsend.h" +#include "IRremoteESP8266.h" +#include "IRtext.h" +#include "IRutils.h" +#include "ir_Airwell.h" +#include "ir_Amcor.h" +#include "ir_Argo.h" +#include "ir_Carrier.h" +#include "ir_Coolix.h" +#include "ir_Corona.h" +#include "ir_Daikin.h" +#include "ir_Ecoclim.h" +#include "ir_Electra.h" +#include "ir_Fujitsu.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_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" + +// On the ESP8266 platform we need to use a special version of string handling +// functions to handle the strings stored in the flash address space. +#ifndef STRCASECMP +#if defined(ESP8266) +#define STRCASECMP(LHS, RHS) \ + strcasecmp_P(LHS, reinterpret_cast(RHS)) +#else // ESP8266 +#define STRCASECMP(LHS, RHS) strcasecmp(LHS, RHS) +#endif // ESP8266 +#endif // STRCASECMP + +/// Class constructor +/// @param[in] pin Gpio pin to use when transmitting IR messages. +/// @param[in] inverted true, gpio output defaults to high. false, to low. +/// @param[in] use_modulation true means use frequency modulation. false, don't. +IRac::IRac(const uint16_t pin, const bool inverted, const bool use_modulation) { + _pin = pin; + _inverted = inverted; + _modulation = use_modulation; + initState(&next); + this->markAsSent(); +} + +/// Initialise the given state with the supplied settings. +/// @param[out] state A Ptr to where the settings will be stored. +/// @param[in] vendor The vendor/protocol type. +/// @param[in] model The A/C model if applicable. +/// @param[in] power The power setting. +/// @param[in] mode The operation mode setting. +/// @param[in] degrees The temperature setting in degrees. +/// @param[in] celsius Temperature units. True is Celsius, False is Fahrenheit. +/// @param[in] fan The speed setting for the fan. +/// @param[in] swingv The vertical swing setting. +/// @param[in] swingh The horizontal swing setting. +/// @param[in] quiet Run the device in quiet/silent mode. +/// @param[in] turbo Run the device in turbo/powerful mode. +/// @param[in] econo Run the device in economical mode. +/// @param[in] light Turn on the LED/Display mode. +/// @param[in] filter Turn on the (ion/pollen/etc) filter mode. +/// @param[in] clean Turn on the self-cleaning mode. e.g. Mould, dry filters etc +/// @param[in] beep Enable/Disable beeps when receiving IR messages. +/// @param[in] sleep Nr. of minutes for sleep mode. +/// -1 is Off, >= 0 is on. Some devices it is the nr. of mins to run for. +/// Others it may be the time to enter/exit sleep mode. +/// i.e. Time in Nr. of mins since midnight. +/// @param[in] clock The time in Nr. of mins since midnight. < 0 is ignore. +void IRac::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) { + state->protocol = vendor; + state->model = model; + state->power = power; + state->mode = mode; + state->degrees = degrees; + state->celsius = celsius; + state->fanspeed = fan; + state->swingv = swingv; + state->swingh = swingh; + state->quiet = quiet; + state->turbo = turbo; + state->econo = econo; + state->light = light; + state->filter = filter; + state->clean = clean; + state->beep = beep; + state->sleep = sleep; + state->clock = clock; +} + +/// Initialise the given state with the supplied settings. +/// @param[out] state A Ptr to where the settings will be stored. +/// @note Sets all the parameters to reasonable base/automatic defaults. +void IRac::initState(stdAc::state_t *state) { + initState(state, decode_type_t::UNKNOWN, -1, false, stdAc::opmode_t::kOff, + 25, true, // 25 degrees Celsius + stdAc::fanspeed_t::kAuto, stdAc::swingv_t::kOff, + stdAc::swingh_t::kOff, false, false, false, false, false, false, + false, -1, -1); +} + +/// Get the current internal A/C climate state. +/// @return A Ptr to a state containing the current (to be sent) settings. +stdAc::state_t IRac::getState(void) { return next; } + +/// Get the previous internal A/C climate state that should have already been +/// sent to the device. i.e. What the A/C unit should already be set to. +/// @return A Ptr to a state containing the previously sent settings. +stdAc::state_t IRac::getStatePrev(void) { return _prev; } + +/// Is the given protocol supported by the IRac class? +/// @param[in] protocol The vendor/protocol type. +/// @return true if the protocol is supported by this class, otherwise false. +bool IRac::isProtocolSupported(const decode_type_t protocol) { + switch (protocol) { +#if SEND_AIRWELL + case decode_type_t::AIRWELL: +#endif +#if SEND_AMCOR + case decode_type_t::AMCOR: +#endif +#if SEND_ARGO + case decode_type_t::ARGO: +#endif +#if SEND_CARRIER_AC64 + case decode_type_t::CARRIER_AC64: +#endif // SEND_CARRIER_AC64 +#if SEND_COOLIX + case decode_type_t::COOLIX: +#endif +#if SEND_CORONA_AC + case decode_type_t::CORONA_AC: +#endif +#if SEND_DAIKIN + case decode_type_t::DAIKIN: +#endif +#if SEND_DAIKIN128 + case decode_type_t::DAIKIN128: +#endif +#if SEND_DAIKIN152 + case decode_type_t::DAIKIN152: +#endif +#if SEND_DAIKIN160 + case decode_type_t::DAIKIN160: +#endif +#if SEND_DAIKIN176 + case decode_type_t::DAIKIN176: +#endif +#if SEND_DAIKIN2 + case decode_type_t::DAIKIN2: +#endif +#if SEND_DAIKIN216 + case decode_type_t::DAIKIN216: +#endif +#if SEND_DAIKIN64 + case decode_type_t::DAIKIN64: +#endif +#if SEND_DELONGHI_AC + case decode_type_t::DELONGHI_AC: +#endif +#if SEND_ECOCLIM + case decode_type_t::ECOCLIM: +#endif +#if SEND_ELECTRA_AC + case decode_type_t::ELECTRA_AC: +#endif +#if SEND_FUJITSU_AC + case decode_type_t::FUJITSU_AC: +#endif +#if SEND_GOODWEATHER + case decode_type_t::GOODWEATHER: +#endif +#if SEND_GREE + case decode_type_t::GREE: +#endif +#if SEND_HAIER_AC + case decode_type_t::HAIER_AC: +#endif +#if SEND_HAIER_AC176 + case decode_type_t::HAIER_AC176: +#endif // SEND_HAIER_AC176 +#if SEND_HAIER_AC_YRW02 + case decode_type_t::HAIER_AC_YRW02: +#endif +#if SEND_HITACHI_AC + case decode_type_t::HITACHI_AC: +#endif +#if SEND_HITACHI_AC1 + case decode_type_t::HITACHI_AC1: +#endif +#if SEND_HITACHI_AC344 + case decode_type_t::HITACHI_AC344: +#endif +#if SEND_HITACHI_AC424 + case decode_type_t::HITACHI_AC424: +#endif +#if SEND_KELON + case decode_type_t::KELON: +#endif +#if SEND_KELVINATOR + case decode_type_t::KELVINATOR: +#endif +#if SEND_LG + case decode_type_t::LG: + case decode_type_t::LG2: +#endif +#if SEND_MIDEA + case decode_type_t::MIDEA: +#endif +#if SEND_MITSUBISHI_AC + case decode_type_t::MITSUBISHI_AC: +#endif +#if SEND_MITSUBISHI112 + case decode_type_t::MITSUBISHI112: +#endif +#if SEND_MITSUBISHI136 + case decode_type_t::MITSUBISHI136: +#endif +#if SEND_MITSUBISHIHEAVY + case decode_type_t::MITSUBISHI_HEAVY_88: + case decode_type_t::MITSUBISHI_HEAVY_152: +#endif +#if SEND_NEOCLIMA + case decode_type_t::NEOCLIMA: +#endif +#if SEND_PANASONIC_AC + case decode_type_t::PANASONIC_AC: +#endif +#if SEND_PANASONIC_AC32 + case decode_type_t::PANASONIC_AC32: +#endif +#if SEND_RHOSS + case decode_type_t::RHOSS: +#endif +#if SEND_SAMSUNG_AC + case decode_type_t::SAMSUNG_AC: +#endif +#if SEND_SANYO_AC + case decode_type_t::SANYO_AC: +#endif +#if SEND_SANYO_AC88 + case decode_type_t::SANYO_AC88: +#endif +#if SEND_SHARP_AC + case decode_type_t::SHARP_AC: +#endif +#if SEND_TCL112AC + case decode_type_t::TCL112AC: +#endif +#if SEND_TECHNIBEL_AC + case decode_type_t::TECHNIBEL_AC: +#endif +#if SEND_TECO + case decode_type_t::TECO: +#endif +#if SEND_TEKNOPOINT + case decode_type_t::TEKNOPOINT: +#endif // SEND_TEKNOPOINT +#if SEND_TOSHIBA_AC + case decode_type_t::TOSHIBA_AC: +#endif +#if SEND_TRANSCOLD + case decode_type_t::TRANSCOLD: +#endif +#if SEND_TROTEC + case decode_type_t::TROTEC: +#endif +#if SEND_TROTEC_3550 + case decode_type_t::TROTEC_3550: +#endif // SEND_TROTEC_3550 +#if SEND_TRUMA + case decode_type_t::TRUMA: +#endif // SEND_TRUMA +#if SEND_VESTEL_AC + case decode_type_t::VESTEL_AC: +#endif +#if SEND_VOLTAS + case decode_type_t::VOLTAS: +#endif + case decode_type_t::WHIRLPOOL_AC: + return true; + default: + return false; + } +} + +#if SEND_AIRWELL +/// Send an Airwell A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRAirwellAc object to use. +/// @param[in] on The power setting. +/// @param[in] mode The operation mode setting. +/// @param[in] degrees The temperature setting in degrees. +/// @param[in] fan The speed setting for the fan. +void IRac::airwell(IRAirwellAc *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan) { + ac->begin(); + ac->setPowerToggle(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + // No Swing setting available. + // No Quiet setting available. + // No Light setting available. + // No Filter setting available. + // No Turbo setting available. + // No Economy setting available. + // No Clean setting available. + // No Beep setting available. + // No Sleep setting available. + ac->send(); +} +#endif // SEND_AIRWELL + +#if SEND_AMCOR +/// Send an Amcor A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRAmcorAc object to use. +/// @param[in] on The power setting. +/// @param[in] mode The operation mode setting. +/// @param[in] degrees The temperature setting in degrees. +/// @param[in] fan The speed setting for the fan. +void IRac::amcor(IRAmcorAc *ac, + const bool on, const stdAc::opmode_t mode, const float degrees, + const stdAc::fanspeed_t fan) { + ac->begin(); + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + // No Swing setting available. + // No Quiet setting available. + // No Light setting available. + // No Filter setting available. + // No Turbo setting available. + // No Economy setting available. + // No Clean setting available. + // No Beep setting available. + // No Sleep setting available. + ac->send(); +} +#endif // SEND_AMCOR + +#if SEND_ARGO +/// Send an Argo A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRArgoAC object to use. +/// @param[in] on The power setting. +/// @param[in] mode The operation mode setting. +/// @param[in] degrees The temperature setting in degrees. +/// @param[in] fan The speed setting for the fan. +/// @param[in] swingv The vertical swing setting. +/// @param[in] turbo Run the device in turbo/powerful mode. +/// @param[in] sleep Nr. of minutes for sleep mode. +/// @note -1 is Off, >= 0 is on. +void IRac::argo(IRArgoAC *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 int16_t sleep) { + ac->begin(); + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setFlap(ac->convertSwingV(swingv)); + // No Quiet setting available. + // No Light setting available. + // No Filter setting available. + ac->setMax(turbo); + // No Economy setting available. + // No Clean setting available. + // No Beep setting available. + ac->setNight(sleep >= 0); // Convert to a boolean. + ac->send(); +} +#endif // SEND_ARGO + +#if SEND_CARRIER_AC64 +/// Send a Carrier 64-bit A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRCarrierAc64 object to use. +/// @param[in] on The power setting. +/// @param[in] mode The operation mode setting. +/// @param[in] degrees The temperature setting in degrees. +/// @param[in] fan The speed setting for the fan. +/// @param[in] swingv The vertical swing setting. +/// @param[in] sleep Nr. of minutes for sleep mode. +/// @note -1 is Off, >= 0 is on. +void IRac::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) { + ac->begin(); + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setSwingV((int8_t)swingv >= 0); + // No Quiet setting available. + // No Light setting available. + // No Filter setting available. + // No Turbo setting available. + // No Economy setting available. + // No Clean setting available. + // No Beep setting available. + ac->setSleep(sleep >= 0); // Convert to a boolean. + ac->send(); +} +#endif // SEND_CARRIER_AC64 + +#if SEND_COOLIX +/// Send a Coolix A/C message with the supplied settings. +/// @note May result in multiple messages being sent. +/// @param[in, out] ac A Ptr to an IRCoolixAC object to use. +/// @param[in] on The power setting. +/// @param[in] mode The operation mode setting. +/// @param[in] degrees The temperature setting in degrees. +/// @param[in] fan The speed setting for the fan. +/// @param[in] swingv The vertical swing setting. +/// @param[in] swingh The horizontal swing setting. +/// @param[in] turbo Run the device in turbo/powerful mode. +/// @param[in] light Turn on the LED/Display mode. +/// @param[in] clean Turn on the self-cleaning mode. e.g. Mould, dry filters etc +/// @param[in] sleep Nr. of minutes for sleep mode. +/// @note -1 is Off, >= 0 is on. +void IRac::coolix(IRCoolixAC *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 light, const bool clean, + const int16_t sleep) { + ac->begin(); + ac->setPower(on); + if (!on) { + // after turn off AC no more commands should + // be accepted + ac->send(); + return; + } + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + // No Filter setting available. + // No Beep setting available. + // No Clock setting available. + // No Econo setting available. + // No Quiet setting available. + ac->send(); // Send the state, which will also power on the unit. + // The following are all options/settings that create their own special + // messages. Often they only make sense to be sent after the unit is turned + // on. For instance, assuming a person wants to have the a/c on and in turbo + // mode. If we send the turbo message, it is ignored if the unit is off. + // Hence we send the special mode/setting messages after a normal message + // which will turn on the device. + if (swingv != stdAc::swingv_t::kOff || swingh != stdAc::swingh_t::kOff) { + // Swing has a special command that needs to be sent independently. + ac->setSwing(); + ac->send(); + } + if (turbo) { + // Turbo has a special command that needs to be sent independently. + ac->setTurbo(); + ac->send(); + } + if (sleep >= 0) { + // Sleep has a special command that needs to be sent independently. + ac->setSleep(); + ac->send(); + } + if (light) { + // Light has a special command that needs to be sent independently. + ac->setLed(); + ac->send(); + } + if (clean) { + // Clean has a special command that needs to be sent independently. + ac->setClean(); + ac->send(); + } +} +#endif // SEND_COOLIX + +#if SEND_CORONA_AC +/// Send a Corona A/C message with the supplied settings. +/// @note May result in multiple messages being sent. +/// @param[in, out] ac A Ptr to an IRCoronaAc object to use. +/// @param[in] on The power setting. +/// @param[in] mode The operation mode setting. +/// @param[in] degrees The temperature setting in degrees. +/// @param[in] fan The speed setting for the fan. +/// @param[in] swingv The vertical swing setting. +/// @param[in] econo Run the device in economical mode. +void IRac::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) { + ac->begin(); + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setSwingVToggle(swingv != stdAc::swingv_t::kOff); + // No Quiet setting available. + // No Light setting available. + // No Filter setting available. + // No Turbo setting available. + ac->setEcono(econo); + // No Clean setting available. + // No Beep setting available. + // No Sleep setting available. + ac->send(); +} +#endif // SEND_CARRIER_AC64 + +#if SEND_DAIKIN +/// Send a Daikin A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRDaikinESP object to use. +/// @param[in] on The power setting. +/// @param[in] mode The operation mode setting. +/// @param[in] degrees The temperature setting in degrees. +/// @param[in] fan The speed setting for the fan. +/// @param[in] swingv The vertical swing setting. +/// @param[in] swingh The horizontal swing setting. +/// @param[in] quiet Run the device in quiet/silent mode. +/// @param[in] turbo Run the device in turbo/powerful mode. +/// @param[in] econo Run the device in economical mode. +/// @param[in] clean Turn on the self-cleaning mode. e.g. Mould, dry filters etc +void IRac::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) { + ac->begin(); + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setSwingVertical((int8_t)swingv >= 0); + ac->setSwingHorizontal((int8_t)swingh >= 0); + ac->setQuiet(quiet); + // No Light setting available. + // No Filter setting available. + ac->setPowerful(turbo); + ac->setEcono(econo); + ac->setMold(clean); + // No Beep setting available. + // No Sleep setting available. + // No Clock setting available. + ac->send(); +} +#endif // SEND_DAIKIN + +#if SEND_DAIKIN128 +/// Send a Daikin 128-bit A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRDaikin128 object to use. +/// @param[in] on The power setting. +/// @param[in] mode The operation mode setting. +/// @param[in] degrees The temperature setting in degrees. +/// @param[in] fan The speed setting for the fan. +/// @param[in] swingv The vertical swing setting. +/// @param[in] quiet Run the device in quiet/silent mode. +/// @param[in] turbo Run the device in turbo/powerful mode. +/// @param[in] light Turn on the LED/Display mode. +/// @param[in] econo Run the device in economical mode. +/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on. +/// @param[in] clock The time in Nr. of mins since midnight. < 0 is ignore. +void IRac::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, const int16_t clock) { + ac->begin(); + ac->setPowerToggle(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setSwingVertical((int8_t)swingv >= 0); + // No Horizontal Swing setting avaliable. + ac->setQuiet(quiet); + ac->setLightToggle(light ? kDaikin128BitWall : 0); + // No Filter setting available. + ac->setPowerful(turbo); + ac->setEcono(econo); + // No Clean setting available. + // No Beep setting available. + ac->setSleep(sleep > 0); + if (clock >= 0) ac->setClock(clock); + ac->send(); +} +#endif // SEND_DAIKIN128 + +#if SEND_DAIKIN152 +/// Send a Daikin 152-bit A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRDaikin152 object to use. +/// @param[in] on The power setting. +/// @param[in] mode The operation mode setting. +/// @param[in] degrees The temperature setting in degrees. +/// @param[in] fan The speed setting for the fan. +/// @param[in] swingv The vertical swing setting. +/// @param[in] quiet Run the device in quiet/silent mode. +/// @param[in] turbo Run the device in turbo/powerful mode. +/// @param[in] econo Run the device in economical mode. +void IRac::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) { + ac->begin(); + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setSwingV((int8_t)swingv >= 0); + // No Horizontal Swing setting avaliable. + ac->setQuiet(quiet); + // No Light setting available. + // No Filter setting available. + ac->setPowerful(turbo); + ac->setEcono(econo); + // No Clean setting available. + // No Beep setting available. + // No Sleep setting available. + // No Clock setting available. + ac->send(); +} +#endif // SEND_DAIKIN152 + +#if SEND_DAIKIN160 +/// Send a Daikin 160-bit A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRDaikin160 object to use. +/// @param[in] on The power setting. +/// @param[in] mode The operation mode setting. +/// @param[in] degrees The temperature setting in degrees. +/// @param[in] fan The speed setting for the fan. +/// @param[in] swingv The vertical swing setting. +void IRac::daikin160(IRDaikin160 *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv) { + ac->begin(); + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setSwingVertical(ac->convertSwingV(swingv)); + ac->send(); +} +#endif // SEND_DAIKIN160 + +#if SEND_DAIKIN176 +/// Send a Daikin 176-bit A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRDaikin176 object to use. +/// @param[in] on The power setting. +/// @param[in] mode The operation mode setting. +/// @param[in] degrees The temperature setting in degrees. +/// @param[in] fan The speed setting for the fan. +/// @param[in] swingh The horizontal swing setting. +void IRac::daikin176(IRDaikin176 *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingh_t swingh) { + ac->begin(); + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setSwingHorizontal(ac->convertSwingH(swingh)); + ac->send(); +} +#endif // SEND_DAIKIN176 + +#if SEND_DAIKIN2 +/// Send a Daikin2 A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRDaikin2 object to use. +/// @param[in] on The power setting. +/// @param[in] mode The operation mode setting. +/// @param[in] degrees The temperature setting in degrees. +/// @param[in] fan The speed setting for the fan. +/// @param[in] swingv The vertical swing setting. +/// @param[in] swingh The horizontal swing setting. +/// @param[in] quiet Run the device in quiet/silent mode. +/// @param[in] turbo Run the device in turbo/powerful mode. +/// @param[in] light Turn on the LED/Display mode. +/// @param[in] econo Run the device in economical mode. +/// @param[in] filter Turn on the (ion/pollen/etc) filter mode. +/// @param[in] clean Turn on the self-cleaning mode. e.g. Mould, dry filters etc +/// @param[in] beep Enable/Disable beeps when receiving IR messages. +/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on. +/// @param[in] clock The time in Nr. of mins since midnight. < 0 is ignore. +void IRac::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, const int16_t clock) { + ac->begin(); + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setSwingVertical(ac->convertSwingV(swingv)); + ac->setSwingHorizontal(ac->convertSwingH(swingh)); + ac->setQuiet(quiet); + ac->setLight(light ? 1 : 3); // On/High is 1, Off is 3. + ac->setPowerful(turbo); + ac->setEcono(econo); + ac->setPurify(filter); + ac->setMold(clean); + ac->setClean(true); // Hardwire auto clean to be on per request (@sheppy99) + ac->setBeep(beep ? 2 : 3); // On/Loud is 2, Off is 3. + if (sleep > 0) ac->enableSleepTimer(sleep); + if (clock >= 0) ac->setCurrentTime(clock); + ac->send(); +} +#endif // SEND_DAIKIN2 + +#if SEND_DAIKIN216 +/// Send a Daikin 216-bit A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRDaikin216 object to use. +/// @param[in] on The power setting. +/// @param[in] mode The operation mode setting. +/// @param[in] degrees The temperature setting in degrees. +/// @param[in] fan The speed setting for the fan. +/// @param[in] swingv The vertical swing setting. +/// @param[in] swingh The horizontal swing setting. +/// @param[in] quiet Run the device in quiet/silent mode. +/// @param[in] turbo Run the device in turbo/powerful mode. +void IRac::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) { + ac->begin(); + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setSwingVertical((int8_t)swingv >= 0); + ac->setSwingHorizontal((int8_t)swingh >= 0); + ac->setQuiet(quiet); + ac->setPowerful(turbo); + ac->send(); +} +#endif // SEND_DAIKIN216 + +#if SEND_DAIKIN64 +/// Send a Daikin 64-bit A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRDaikin64 object to use. +/// @param[in] on The power setting. +/// @param[in] mode The operation mode setting. +/// @param[in] degrees The temperature setting in degrees. +/// @param[in] fan The speed setting for the fan. +/// @param[in] swingv The vertical swing setting. +/// @param[in] quiet Run the device in quiet/silent mode. +/// @param[in] turbo Run the device in turbo/powerful mode. +/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on. +/// @param[in] clock The time in Nr. of mins since midnight. < 0 is ignore. +void IRac::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, const int16_t clock) { + ac->begin(); + ac->setPowerToggle(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setSwingVertical((int8_t)swingv >= 0); + ac->setTurbo(turbo); + ac->setQuiet(quiet); + ac->setSleep(sleep >= 0); + if (clock >= 0) ac->setClock(clock); + ac->send(); +} +#endif // SEND_DAIKIN64 + +#if SEND_DELONGHI_AC +/// Send a Delonghi A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRDelonghiAc object to use. +/// @param[in] on The power setting. +/// @param[in] mode The operation mode setting. +/// @param[in] celsius Temperature units. True is Celsius, False is Fahrenheit. +/// @param[in] degrees The temperature setting in degrees. +/// @param[in] fan The speed setting for the fan. +/// @param[in] turbo Run the device in turbo/powerful mode. +/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on. +void IRac::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) { + ac->begin(); + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees, !celsius); + ac->setFan(ac->convertFan(fan)); + ac->setBoost(turbo); + ac->setSleep(sleep >= 0); + ac->send(); +} +#endif // SEND_DELONGHI_AC + +#if SEND_ECOCLIM +/// Send an EcoClim A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IREcoclimAc object to use. +/// @param[in] on The power setting. +/// @param[in] mode The operation mode setting. +/// @param[in] degrees The temperature setting in degrees. +/// @param[in] fan The speed setting for the fan. +/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on. +/// @param[in] clock The time in Nr. of mins since midnight. < 0 is ignore. +void IRac::ecoclim(IREcoclimAc *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const int16_t sleep, const int16_t clock) { + ac->begin(); + ac->setPower(on); + uint8_t new_mode; + if (sleep >= 0) // EcoClim has a descrete Sleep operation mode, not a setting + new_mode = kEcoclimSleep; // Override the requested operating mode. + else + new_mode = ac->convertMode(mode); // Not Sleep, so use the supplied mode. + ac->setMode(new_mode); + ac->setTemp(degrees); + ac->setSensorTemp(degrees); //< Set to the desired temp until we cab disable. + ac->setFan(ac->convertFan(fan)); + // No SwingV setting available + // No SwingH setting available + // No Quiet setting available. + // No Turbo setting available. + // No Light setting available. + // No Econo setting available. + // No Filter setting available. + // No Clean setting available + // No Beep setting available. + // No Sleep setting available. + if (clock >= 0) ac->setClock(clock); + ac->send(); +} +#endif // SEND_ECOCLIM + +#if SEND_ELECTRA_AC +/// Send an Electra A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRElectraAc object to use. +/// @param[in] on The power setting. +/// @param[in] mode The operation mode setting. +/// @param[in] degrees The temperature setting in degrees. +/// @param[in] fan The speed setting for the fan. +/// @param[in] swingv The vertical swing setting. +/// @param[in] swingh The horizontal swing setting. +/// @param[in] turbo Run the device in turbo/powerful mode. +/// @param[in] lighttoggle Should we toggle the LED/Display? +/// @param[in] clean Turn on the self-cleaning mode. e.g. Mould, dry filters etc +void IRac::electra(IRElectraAc *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 lighttoggle, const bool clean) { + ac->begin(); + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setSwingV(swingv != stdAc::swingv_t::kOff); + ac->setSwingH(swingh != stdAc::swingh_t::kOff); + // No Quiet setting available. + ac->setTurbo(turbo); + ac->setLightToggle(lighttoggle); + // No Econo setting available. + // No Filter setting available. + ac->setClean(clean); + // No Beep setting available. + // No Sleep setting available. + // No Clock setting available. + ac->send(); +} +#endif // SEND_ELECTRA_AC + +#if SEND_FUJITSU_AC +/// Send a Fujitsu A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRFujitsuAC object to use. +/// @param[in] model The A/C model to use. +/// @param[in] on The power setting. +/// @param[in] mode The operation mode setting. +/// @param[in] celsius Temperature units. True is Celsius, False is Fahrenheit. +/// @param[in] degrees The temperature setting in degrees. +/// @param[in] fan The speed setting for the fan. +/// @param[in] swingv The vertical swing setting. +/// @param[in] swingh The horizontal swing setting. +/// @param[in] quiet Run the device in quiet/silent mode. +/// @param[in] turbo Run the device in turbo/powerful mode. +/// @param[in] econo Run the device in economical mode. +/// @param[in] filter Turn on the (ion/pollen/etc) filter mode. +/// @param[in] clean Turn on the self-cleaning mode. e.g. Mould, dry filters etc +/// @param[in] sleep Nr. of minutes for sleep mode. <= 0 is Off, > 0 is on. +void IRac::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) { + ac->begin(); + ac->setModel(model); + if (on) { + // Do all special messages (except "Off") first, + // These need to be sent separately. + switch (ac->getModel()) { + // Some functions are only available on some models. + case fujitsu_ac_remote_model_t::ARREB1E: + if (turbo) { + ac->setCmd(kFujitsuAcCmdPowerful); + // Powerful is a separate command. + ac->send(); + } + if (econo) { + ac->setCmd(kFujitsuAcCmdEcono); + // Econo is a separate command. + ac->send(); + } + break; + default: + {}; + } + // Normal operation. + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees, celsius); + ac->setFanSpeed(ac->convertFan(fan)); + uint8_t swing = kFujitsuAcSwingOff; + if (swingv > stdAc::swingv_t::kOff) swing |= kFujitsuAcSwingVert; + if (swingh > stdAc::swingh_t::kOff) swing |= kFujitsuAcSwingHoriz; + ac->setSwing(swing); + if (quiet) ac->setFanSpeed(kFujitsuAcFanQuiet); + // No Light setting available. + ac->setFilter(filter); + ac->setClean(clean); + // No Beep setting available. + ac->setSleepTimer(sleep > 0 ? sleep : 0); + // No Sleep setting available. + // No Clock setting available. + ac->on(); // Ref: Issue #860 + } else { + // Off is special case/message. We don't need to send other messages. + ac->off(); + } + ac->send(); +} +#endif // SEND_FUJITSU_AC + +#if SEND_GOODWEATHER +/// Send a Goodweather A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRGoodweatherAc object to use. +/// @param[in] on The power setting. +/// @param[in] mode The operation mode setting. +/// @param[in] degrees The temperature setting in degrees. +/// @param[in] fan The speed setting for the fan. +/// @param[in] swingv The vertical swing setting. +/// @param[in] turbo Run the device in turbo/powerful mode. +/// @param[in] light Turn on the LED/Display mode. +/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on. +void IRac::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) { + ac->begin(); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setSwing(swingv == stdAc::swingv_t::kOff ? kGoodweatherSwingOff + : kGoodweatherSwingSlow); + ac->setTurbo(turbo); + ac->setLight(light); + // No Clean setting available. + ac->setSleep(sleep >= 0); // Sleep on this A/C is either on or off. + // No Horizontal Swing setting available. + // No Econo setting available. + // No Filter setting available. + // No Beep setting available. + // No Quiet setting available. + // No Clock setting available. + ac->setPower(on); + ac->send(); +} +#endif // SEND_GOODWEATHER + +#if SEND_GREE +/// Send a Gree A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRGreeAC object to use. +/// @param[in] model The A/C model to use. +/// @param[in] on The power setting. +/// @param[in] mode The operation mode setting. +/// @param[in] celsius Temperature units. True is Celsius, False is Fahrenheit. +/// @param[in] degrees The temperature setting in degrees. +/// @param[in] fan The speed setting for the fan. +/// @param[in] swingv The vertical swing setting. +/// @param[in] turbo Run the device in turbo/powerful mode. +/// @param[in] light Turn on the LED/Display mode. +/// @param[in] clean Turn on the self-cleaning mode. e.g. Mould, dry filters etc +/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on. +void IRac::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 bool turbo, + const bool light, const bool clean, const int16_t sleep) { + ac->begin(); + ac->setModel(model); + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees, !celsius); + ac->setFan(ac->convertFan(fan)); + ac->setSwingVertical(swingv == stdAc::swingv_t::kAuto, // Set auto flag. + ac->convertSwingV(swingv)); + ac->setLight(light); + ac->setTurbo(turbo); + ac->setXFan(clean); + ac->setSleep(sleep >= 0); // Sleep on this A/C is either on or off. + // No Horizontal Swing setting available. + // No Econo setting available. + // No Filter setting available. + // No Beep setting available. + // No Quiet setting available. + // No Clock setting available. + ac->send(); +} +#endif // SEND_GREE + +#if SEND_HAIER_AC +/// Send a Haier A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRGreeAC object to use. +/// @param[in] on The power setting. +/// @param[in] mode The operation mode setting. +/// @param[in] degrees The temperature setting in degrees. +/// @param[in] fan The speed setting for the fan. +/// @param[in] swingv The vertical swing setting. +/// @param[in] filter Turn on the (ion/pollen/etc) filter mode. +/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on. +/// @param[in] clock The time in Nr. of mins since midnight. < 0 is ignore. +void IRac::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, const int16_t clock) { + ac->begin(); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setSwing(ac->convertSwingV(swingv)); + // No Horizontal Swing setting available. + // No Quiet setting available. + // No Turbo setting available. + // No Light setting available. + ac->setHealth(filter); + // No Clean setting available. + // No Beep setting available. + ac->setSleep(sleep >= 0); // Sleep on this A/C is either on or off. + if (clock >= 0) ac->setCurrTime(clock); + if (on) + ac->setCommand(kHaierAcCmdOn); + else + ac->setCommand(kHaierAcCmdOff); + ac->send(); +} +#endif // SEND_HAIER_AC + +#if SEND_HAIER_AC176 +/// Send a Haier 176 bit A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRHaierAC176 object to use. +/// @param[in] on The power setting. +/// @param[in] mode The operation mode setting. +/// @param[in] degrees The temperature setting in degrees. +/// @param[in] fan The speed setting for the fan. +/// @param[in] swingv The vertical swing setting. +/// @param[in] turbo Run the device in turbo/powerful mode. +/// @param[in] quiet Run the device in quiet mode. +/// @param[in] filter Turn on the (ion/pollen/etc) filter mode. +/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on. +void IRac::haier176(IRHaierAC176 *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 quiet, const bool filter, const int16_t sleep) { + ac->begin(); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setSwing(ac->convertSwingV(swingv)); + // No Horizontal Swing setting available. + ac->setQuiet(quiet); + ac->setTurbo(turbo); + // No Light setting available. + ac->setHealth(filter); + // No Clean setting available. + // No Beep setting available. + ac->setSleep(sleep >= 0); // Sleep on this A/C is either on or off. + ac->setPower(on); + ac->send(); +} +#endif // SEND_HAIER_AC176 + +#if SEND_HAIER_AC_YRW02 +/// Send a Haier YRWO2 A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRHaierACYRW02 object to use. +/// @param[in] on The power setting. +/// @param[in] mode The operation mode setting. +/// @param[in] degrees The temperature setting in degrees. +/// @param[in] fan The speed setting for the fan. +/// @param[in] swingv The vertical swing setting. +/// @param[in] turbo Run the device in turbo/powerful mode. +/// @param[in] quiet Run the device in quiet mode. +/// @param[in] filter Turn on the (ion/pollen/etc) filter mode. +/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on. +void IRac::haierYrwo2(IRHaierACYRW02 *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 quiet, const bool filter, + const int16_t sleep) { + ac->begin(); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setSwing(ac->convertSwingV(swingv)); + // No Horizontal Swing setting available. + ac->setQuiet(quiet); + ac->setTurbo(turbo); + // No Light setting available. + ac->setHealth(filter); + // No Clean setting available. + // No Beep setting available. + ac->setSleep(sleep >= 0); // Sleep on this A/C is either on or off. + ac->setPower(on); + ac->send(); +} +#endif // SEND_HAIER_AC_YRW02 + +#if SEND_HITACHI_AC +/// Send a Hitachi A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRHitachiAc object to use. +/// @param[in] on The power setting. +/// @param[in] mode The operation mode setting. +/// @param[in] degrees The temperature setting in degrees. +/// @param[in] fan The speed setting for the fan. +/// @param[in] swingv The vertical swing setting. +/// @param[in] swingh The horizontal swing setting. +void IRac::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) { + ac->begin(); + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setSwingVertical(swingv != stdAc::swingv_t::kOff); + ac->setSwingHorizontal(swingh != stdAc::swingh_t::kOff); + // No Quiet setting available. + // No Turbo setting available. + // No Light setting available. + // No Filter setting available. + // No Clean setting available. + // No Beep setting available. + // No Sleep setting available. + // No Clock setting available. + ac->send(); +} +#endif // SEND_HITACHI_AC + +#if SEND_HITACHI_AC1 +/// Send a Hitachi1 A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRHitachiAc1 object to use. +/// @param[in] model The A/C model to use. +/// @param[in] on The power setting. +/// @param[in] power_toggle The power toggle setting. +/// @param[in] mode The operation mode setting. +/// @param[in] degrees The temperature setting in degrees. +/// @param[in] fan The speed setting for the fan. +/// @param[in] swingv The vertical swing setting. +/// @param[in] swingh The horizontal swing setting. +/// @param[in] swing_toggle The swing_toggle setting. +/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on. +/// @note The sleep mode used is the "Sleep 2" setting. +void IRac::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) { + ac->begin(); + ac->setModel(model); + ac->setPower(on); + ac->setPowerToggle(power_toggle); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setSwingV(swingv != stdAc::swingv_t::kOff); + ac->setSwingH(swingh != stdAc::swingh_t::kOff); + ac->setSwingToggle(swing_toggle); + ac->setSleep((sleep >= 0) ? kHitachiAc1Sleep2 : kHitachiAc1SleepOff); + // No Sleep setting available. + // No Swing(H) setting available. + // No Quiet setting available. + // No Turbo setting available. + // No Light setting available. + // No Filter setting available. + // No Clean setting available. + // No Beep setting available. + // No Clock setting available. + ac->send(); +} +#endif // SEND_HITACHI_AC1 + +#if SEND_HITACHI_AC344 +/// Send a Hitachi 344-bit A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRHitachiAc344 object to use. +/// @param[in] on The power setting. +/// @param[in] mode The operation mode setting. +/// @param[in] degrees The temperature setting in degrees. +/// @param[in] fan The speed setting for the fan. +/// @param[in] swingv The vertical swing setting. +/// @param[in] swingh The horizontal swing setting. +void IRac::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) { + ac->begin(); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setSwingH(ac->convertSwingH(swingh)); + ac->setPower(on); + // No Quiet setting available. + // No Turbo setting available. + // No Light setting available. + // No Filter setting available. + // No Clean setting available. + // No Beep setting available. + // No Sleep setting available. + // No Clock setting available. + + // SwingVToggle is special. Needs to be last method called. + ac->setSwingVToggle(swingv != stdAc::swingv_t::kOff); + ac->send(); +} +#endif // SEND_HITACHI_AC344 + +#if SEND_HITACHI_AC424 +/// Send a Hitachi 424-bit A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRHitachiAc424 object to use. +/// @param[in] on The power setting. +/// @param[in] mode The operation mode setting. +/// @param[in] degrees The temperature setting in degrees. +/// @param[in] fan The speed setting for the fan. +/// @param[in] swingv The vertical swing setting. +void IRac::hitachi424(IRHitachiAc424 *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv) { + ac->begin(); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setPower(on); + // SwingVToggle is special. Needs to be last method called. + ac->setSwingVToggle(swingv != stdAc::swingv_t::kOff); + // No Swing(H) setting available. + // No Quiet setting available. + // No Turbo setting available. + // No Light setting available. + // No Filter setting available. + // No Clean setting available. + // No Beep setting available. + // No Sleep setting available. + // No Clock setting available. + ac->send(); +} +#endif // SEND_HITACHI_AC424 + +#if SEND_KELON +/// Send a Kelon A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRKelonAc object to use. +/// @param[in] togglePower Whether to toggle the unit's power +/// @param[in] mode The operation mode setting. +/// @param[in] dryGrade The dehumidification intensity grade +/// @param[in] degrees The temperature setting in degrees. +/// @param[in] fan The speed setting for the fan. +/// @param[in] toggleSwing Whether to toggle the swing setting +/// @param[in] superCool Run the device in Super cooling mode. +/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on +void IRac::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) { + ac->begin(); + ac->setMode(IRKelonAc::convertMode(mode)); + ac->setFan(IRKelonAc::convertFan(fan)); + ac->setTemp(static_cast(degrees)); + ac->setSleep(sleep >= 0); + ac->setSupercool(superCool); + ac->setDryGrade(dryGrade); + + ac->setTogglePower(togglePower); + ac->setToggleSwingVertical(toggleSwing); + + ac->send(); +} +#endif // SEND_KELON + +#if SEND_KELVINATOR +/// Send a Kelvinator A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRKelvinatorAC object to use. +/// @param[in] on The power setting. +/// @param[in] mode The operation mode setting. +/// @param[in] degrees The temperature setting in degrees. +/// @param[in] fan The speed setting for the fan. +/// @param[in] swingv The vertical swing setting. +/// @param[in] swingh The horizontal swing setting. +/// @param[in] quiet Run the device in quiet/silent mode. +/// @param[in] turbo Run the device in turbo/powerful mode. +/// @param[in] light Turn on the LED/Display mode. +/// @param[in] filter Turn on the (ion/pollen/etc) filter mode. +/// @param[in] clean Turn on the self-cleaning mode. e.g. XFan, dry filters etc +void IRac::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) { + ac->begin(); + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan((uint8_t)fan); // No conversion needed. + ac->setSwingVertical((int8_t)swingv >= 0); + ac->setSwingHorizontal((int8_t)swingh >= 0); + ac->setQuiet(quiet); + ac->setTurbo(turbo); + ac->setLight(light); + ac->setIonFilter(filter); + ac->setXFan(clean); + // No Beep setting available. + // No Sleep setting available. + // No Clock setting available. + ac->send(); +} +#endif // SEND_KELVINATOR + +#if SEND_LG +/// Send a LG A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRLgAc object to use. +/// @param[in] model The A/C model to use. +/// @param[in] on The power setting. +/// @param[in] mode The operation mode setting. +/// @param[in] degrees The temperature setting in degrees. +/// @param[in] fan The speed setting for the fan. +/// @param[in] swingv The vertical swing setting. +/// @param[in] swingv_prev The previous vertical swing setting. +/// @param[in] swingh The horizontal swing setting. +/// @param[in] light Turn on the LED/Display mode. +void IRac::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) { + ac->begin(); + ac->setModel(model); + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setSwingV(ac->convertSwingV(swingv_prev)); + ac->updateSwingPrev(); + ac->setSwingV(ac->convertSwingV(swingv)); + const uint8_t pos = ac->convertVaneSwingV(swingv); + for (uint8_t vane = 0; vane < kLgAcSwingVMaxVanes; vane++) + ac->setVaneSwingV(vane, pos); + ac->setSwingH(swingh != stdAc::swingh_t::kOff); + // No Quiet setting available. + // No Turbo setting available. + ac->setLight(light); + // No Filter setting available. + // No Clean setting available. + // No Beep setting available. + // No Sleep setting available. + // No Clock setting available. + ac->send(); +} +#endif // SEND_LG + +#if SEND_MIDEA +/// Send a Midea A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRMideaAC object to use. +/// @param[in] on The power setting. +/// @param[in] mode The operation mode setting. +/// @param[in] celsius Temperature units. True is Celsius, False is Fahrenheit. +/// @param[in] degrees The temperature setting in degrees. +/// @param[in] fan The speed setting for the fan. +/// @param[in] swingv The vertical swing setting. +/// @param[in] turbo Toggle the device's turbo/powerful mode. +/// @param[in] econo Toggle the device's economical mode. +/// @param[in] light Toggle the LED/Display mode. +/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on. +/// @note On Danby A/C units, swingv controls the Ion Filter instead. +void IRac::midea(IRMideaAC *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 econo, const bool light, const int16_t sleep) { + ac->begin(); + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setUseCelsius(celsius); + ac->setTemp(degrees, celsius); + ac->setFan(ac->convertFan(fan)); + ac->setSwingVToggle(swingv != stdAc::swingv_t::kOff); + // No Horizontal swing setting available. + // No Quiet setting available. + ac->setTurboToggle(turbo); + ac->setEconoToggle(econo); + ac->setLightToggle(light); + // No Filter setting available. + // No Clean setting available. + // No Beep setting available. + ac->setSleep(sleep >= 0); // Sleep on this A/C is either on or off. + // No Clock setting available. + ac->send(); +} +#endif // SEND_MIDEA + +#if SEND_MITSUBISHI_AC +/// Send a Mitsubishi A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRMitsubishiAC object to use. +/// @param[in] on The power setting. +/// @param[in] mode The operation mode setting. +/// @param[in] degrees The temperature setting in degrees. +/// @param[in] fan The speed setting for the fan. +/// @param[in] swingv The vertical swing setting. +/// @param[in] swingh The horizontal swing setting. +/// @param[in] quiet Run the device in quiet/silent mode. +/// @param[in] clock The time in Nr. of mins since midnight. < 0 is ignore. +/// @note Clock can only be set in 10 minute increments. i.e. % 10. +void IRac::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) { + ac->begin(); + // Uncomment next line if you *really* need the weekly timer enabled via IRac. + // ac->setWeeklyTimerEnabled(true); // Weekly Timer is disabled by default. + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setVane(ac->convertSwingV(swingv)); + ac->setWideVane(ac->convertSwingH(swingh)); + if (quiet) ac->setFan(kMitsubishiAcFanSilent); + // No Turbo setting available. + // No Light setting available. + // No Filter setting available. + // No Clean setting available. + // No Beep setting available. + // No Sleep setting available. + if (clock >= 0) ac->setClock(clock / 10); // Clock is in 10 min increments. + ac->send(); +} +#endif // SEND_MITSUBISHI_AC + +#if SEND_MITSUBISHI112 +/// Send a Mitsubishi 112-bit A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRMitsubishi112 object to use. +/// @param[in] on The power setting. +/// @param[in] mode The operation mode setting. +/// @param[in] degrees The temperature setting in degrees. +/// @param[in] fan The speed setting for the fan. +/// @param[in] swingv The vertical swing setting. +/// @param[in] swingh The horizontal swing setting. +/// @param[in] quiet Run the device in quiet/silent mode. +void IRac::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) { + ac->begin(); + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setSwingV(ac->convertSwingV(swingv)); + ac->setSwingH(ac->convertSwingH(swingh)); + ac->setQuiet(quiet); + // FIXME - Econo + // ac->setEcono(econo); + // No Turbo setting available. + // No Light setting available. + // No Filter setting available. + // No Clean setting available. + // No Beep setting available. + // No Sleep setting available. + // No Clock setting available. + ac->send(); +} +#endif // SEND_MITSUBISHI112 + +#if SEND_MITSUBISHI136 +/// Send a Mitsubishi 136-bit A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRMitsubishi136 object to use. +/// @param[in] on The power setting. +/// @param[in] mode The operation mode setting. +/// @param[in] degrees The temperature setting in degrees. +/// @param[in] fan The speed setting for the fan. +/// @param[in] swingv The vertical swing setting. +/// @param[in] quiet Run the device in quiet/silent mode. +void IRac::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) { + ac->begin(); + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setSwingV(ac->convertSwingV(swingv)); + // No Horizontal Swing setting available. + ac->setQuiet(quiet); + // No Turbo setting available. + // No Light setting available. + // No Filter setting available. + // No Clean setting available. + // No Beep setting available. + // No Sleep setting available. + // No Clock setting available. + ac->send(); +} +#endif // SEND_MITSUBISHI136 + +#if SEND_MITSUBISHIHEAVY +/// Send a Mitsubishi Heavy 88-bit A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRMitsubishiHeavy88Ac object to use. +/// @param[in] on The power setting. +/// @param[in] mode The operation mode setting. +/// @param[in] degrees The temperature setting in degrees. +/// @param[in] fan The speed setting for the fan. +/// @param[in] swingv The vertical swing setting. +/// @param[in] swingh The horizontal swing setting. +/// @param[in] turbo Run the device in turbo/powerful mode. +/// @param[in] econo Run the device in economical mode. +/// @param[in] clean Turn on the self-cleaning mode. e.g. Mould, dry filters etc +void IRac::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) { + ac->begin(); + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setSwingVertical(ac->convertSwingV(swingv)); + ac->setSwingHorizontal(ac->convertSwingH(swingh)); + // No Quiet setting available. + ac->setTurbo(turbo); + // No Light setting available. + ac->setEcono(econo); + // No Filter setting available. + ac->setClean(clean); + // No Beep setting available. + // No Sleep setting available. + // No Clock setting available. + ac->send(); +} + +/// Send a Mitsubishi Heavy 152-bit A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRMitsubishiHeavy152Ac object to use. +/// @param[in] on The power setting. +/// @param[in] mode The operation mode setting. +/// @param[in] degrees The temperature setting in degrees. +/// @param[in] fan The speed setting for the fan. +/// @param[in] swingv The vertical swing setting. +/// @param[in] swingh The horizontal swing setting. +/// @param[in] quiet Run the device in quiet/silent mode. +/// @param[in] turbo Run the device in turbo/powerful mode. +/// @param[in] econo Run the device in economical mode. +/// @param[in] filter Turn on the (ion/pollen/etc) filter mode. +/// @param[in] clean Turn on the self-cleaning mode. e.g. Mould, dry filters etc +/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on. +void IRac::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) { + ac->begin(); + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setSwingVertical(ac->convertSwingV(swingv)); + ac->setSwingHorizontal(ac->convertSwingH(swingh)); + ac->setSilent(quiet); + ac->setTurbo(turbo); + // No Light setting available. + ac->setEcono(econo); + ac->setClean(clean); + ac->setFilter(filter); + // No Beep setting available. + ac->setNight(sleep >= 0); // Sleep is either on/off, so convert to boolean. + // No Clock setting available. + ac->send(); +} +#endif // SEND_MITSUBISHIHEAVY + +#if SEND_NEOCLIMA +/// Send a Neoclima A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRNeoclimaAc object to use. +/// @param[in] on The power setting. +/// @param[in] mode The operation mode setting. +/// @param[in] celsius Temperature units. True is Celsius, False is Fahrenheit. +/// @param[in] degrees The temperature setting in degrees. +/// @param[in] fan The speed setting for the fan. +/// @param[in] swingv The vertical swing setting. +/// @param[in] swingh The horizontal swing setting. +/// @param[in] turbo Run the device in turbo/powerful mode. +/// @param[in] econo Run the device in economical mode. +/// @param[in] light Turn on the LED/Display mode. +/// @param[in] filter Turn on the (ion/pollen/etc) filter mode. +/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on. +void IRac::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) { + ac->begin(); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees, celsius); + ac->setFan(ac->convertFan(fan)); + ac->setSwingV(swingv != stdAc::swingv_t::kOff); + ac->setSwingH(swingh != stdAc::swingh_t::kOff); + // No Quiet setting available. + ac->setTurbo(turbo); + ac->setLight(light); + ac->setEcono(econo); + ac->setIon(filter); + // No Clean setting available. + // No Beep setting available. + ac->setSleep(sleep >= 0); // Sleep is either on/off, so convert to boolean. + // No Clock setting available. + ac->setPower(on); + ac->send(); +} +#endif // SEND_NEOCLIMA + +#if SEND_PANASONIC_AC +/// Send a Panasonic A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRPanasonicAc object to use. +/// @param[in] model The A/C model to use. +/// @param[in] on The power setting. +/// @param[in] mode The operation mode setting. +/// @param[in] degrees The temperature setting in degrees. +/// @param[in] fan The speed setting for the fan. +/// @param[in] swingv The vertical swing setting. +/// @param[in] swingh The horizontal swing setting. +/// @param[in] quiet Run the device in quiet/silent mode. +/// @param[in] turbo Run the device in turbo/powerful mode. +/// @param[in] filter Turn on the (ion/pollen/etc) filter mode. +/// @param[in] clock The time in Nr. of mins since midnight. < 0 is ignore. +void IRac::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) { + ac->begin(); + ac->setModel(model); + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setSwingVertical(ac->convertSwingV(swingv)); + ac->setSwingHorizontal(ac->convertSwingH(swingh)); + ac->setQuiet(quiet); + ac->setPowerful(turbo); + ac->setIon(filter); + // No Light setting available. + // No Econo setting available. + // No Clean setting available. + // No Beep setting available. + // No Sleep setting available. + if (clock >= 0) ac->setClock(clock); + ac->send(); +} +#endif // SEND_PANASONIC_AC + +#if SEND_PANASONIC_AC32 +/// Send a Panasonic A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRPanasonicAc32 object to use. +/// @param[in] on The power setting. +/// @param[in] mode The operation mode setting. +/// @param[in] degrees The temperature setting in degrees. +/// @param[in] fan The speed setting for the fan. +/// @param[in] swingv The vertical swing setting. +/// @param[in] swingh The horizontal swing setting. +void IRac::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) { + ac->begin(); + ac->setPowerToggle(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setSwingVertical(ac->convertSwingV(swingv)); + ac->setSwingHorizontal(swingh != stdAc::swingh_t::kOff); + // No Quiet setting available. + // No Turbo setting available. + // No Filter setting available. + // No Light setting available. + // No Econo setting available. + // No Clean setting available. + // No Beep setting available. + // No Sleep setting available. + // No Clock setting available. + ac->send(); +} +#endif // SEND_PANASONIC_AC32 + +#if SEND_SAMSUNG_AC +/// Send a Samsung A/C message with the supplied settings. +/// @note Multiple IR messages may be generated & sent. +/// @param[in, out] ac A Ptr to an IRSamsungAc object to use. +/// @param[in] on The power setting. +/// @param[in] mode The operation mode setting. +/// @param[in] degrees The temperature setting in degrees. +/// @param[in] fan The speed setting for the fan. +/// @param[in] swingv The vertical swing setting. +/// @param[in] quiet Run the device in quiet/silent mode. +/// @param[in] turbo Run the device in turbo/powerful mode. +/// @param[in] light Turn on the LED/Display mode. +/// @param[in] filter Turn on the (ion/pollen/etc) filter mode. +/// @param[in] clean Turn on the self-cleaning mode. e.g. Mould, dry filters etc +/// @param[in] beep Enable/Disable beeps when receiving IR messages. +/// @param[in] prevpower The power setting from the previous A/C state. +/// @param[in] forcepower Do we force send the special power message? +void IRac::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 bool quiet, const bool turbo, const bool light, + const bool filter, const bool clean, + const bool beep, const bool prevpower, + const bool forcepower) { + ac->begin(); + ac->stateReset(forcepower, prevpower); + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setSwing(swingv != stdAc::swingv_t::kOff); + // No Horizontal swing setting available. + ac->setQuiet(quiet); + ac->setPowerful(turbo); + ac->setDisplay(light); + // No Econo setting available. + ac->setIon(filter); + ac->setClean(clean); + ac->setBeep(beep); + // No Sleep setting available. + // No Clock setting available. + // Do setMode() again as it can affect fan speed. + ac->setMode(ac->convertMode(mode)); + ac->send(); +} +#endif // SEND_SAMSUNG_AC + +#if SEND_SANYO_AC +/// Send a Sanyo A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRSanyoAc object to use. +/// @param[in] on The power setting. +/// @param[in] mode The operation mode setting. +/// @param[in] degrees The temperature setting in degrees. +/// @param[in] fan The speed setting for the fan. +/// @param[in] swingv The vertical swing setting. +/// @param[in] beep Enable/Disable beeps when receiving IR messages. +/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on. +void IRac::sanyo(IRSanyoAc *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const bool beep, + const int16_t sleep) { + ac->begin(); + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setSwingV(ac->convertSwingV(swingv)); + // No Horizontal swing setting available. + // No Quiet setting available. + // No Turbo setting available. + // No Econo setting available. + // No Light setting available. + // No Filter setting available. + // No Clean setting available. + ac->setBeep(beep); + ac->setSleep(sleep >= 0); // Sleep is either on/off, so convert to boolean. + // No Clock setting available. + + // Extra + ac->setSensor(true); // Set the A/C to use the temp sensor in the Unit/Wall. + ac->setSensorTemp(degrees); // Set the sensor temp to the desired temp. + ac->send(); +} +#endif // SEND_SANYO_AC + +#if SEND_SANYO_AC88 +/// Send a Sanyo 88-bit A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRSanyoAc88 object to use. +/// @param[in] on The power setting. +/// @param[in] mode The operation mode setting. +/// @param[in] degrees The temperature setting in degrees. +/// @param[in] fan The speed setting for the fan. +/// @param[in] swingv The vertical swing setting. +/// @param[in] turbo Run the device in turbo/powerful mode. +/// @param[in] filter Turn on the (ion/pollen/etc) filter mode. +/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on. +/// @param[in] clock The time in Nr. of mins since midnight. < 0 is ignore. +void IRac::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, + const int16_t clock) { + ac->begin(); + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setSwingV(swingv != stdAc::swingv_t::kOff); + // No Horizontal swing setting available. + // No Quiet setting available. + ac->setTurbo(turbo); + // No Econo setting available. + // No Light setting available. + ac->setFilter(filter); + // No Clean setting available. + // No Beep setting available. + ac->setSleep(sleep >= 0); // Sleep is either on/off, so convert to boolean. + if (clock >= 0) ac->setClock(clock); + ac->send(); +} +#endif // SEND_SANYO_AC88 + +#if SEND_SHARP_AC +/// Send a Sharp A/C message with the supplied settings. +/// @note Multiple IR messages may be generated & sent. +/// @param[in, out] ac A Ptr to an IRSharpAc object to use. +/// @param[in] model The A/C model to use. +/// @param[in] on The power setting. +/// @param[in] prev_power The power setting from the previous A/C state. +/// @param[in] mode The operation mode setting. +/// @param[in] degrees The temperature setting in degrees. +/// @param[in] fan The speed setting for the fan. +/// @param[in] swingv The vertical swing setting. +/// @param[in] swingv_prev The previous vertical swing setting. +/// @param[in] turbo Run the device in turbo/powerful mode. +/// @param[in] light Turn on the LED/Display mode. +/// @param[in] filter Turn on the (ion/pollen/etc) filter mode. +/// @param[in] clean Turn on the self-cleaning mode. e.g. Mould, dry filters etc +void IRac::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) { + ac->begin(); + ac->setModel(model); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan, model)); + if (swingv != swingv_prev) ac->setSwingV(ac->convertSwingV(swingv)); + // Econo deliberately not used as it cycles through 3 modes uncontrollably. + // ac->setEconoToggle(econo); + ac->setIon(filter); + // No Horizontal swing setting available. + // No Quiet setting available. + ac->setLightToggle(light); + // No Beep setting available. + // No Sleep setting available. + // No Clock setting available. + // Do setMode() again as it can affect fan speed and temp. + ac->setMode(ac->convertMode(mode)); + // Clean after mode, as it can affect the mode, temp & fan speed. + if (clean) { + // A/C needs to be off before we can enter clean mode. + ac->setPower(false, prev_power); + ac->send(); + } + ac->setClean(clean); + ac->setPower(on, prev_power); + if (turbo) { + ac->send(); // Send the current state. + // Set up turbo mode as it needs to be sent after everything else. + ac->setTurbo(true); + } + ac->send(); +} +#endif // SEND_SHARP_AC + +#if SEND_TCL112AC +/// Send a TCL 112-bit A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRTcl112Ac object to use. +/// @param[in] model The A/C model to use. +/// @param[in] on The power setting. +/// @param[in] mode The operation mode setting. +/// @param[in] degrees The temperature setting in degrees. +/// @param[in] fan The speed setting for the fan. +/// @param[in] swingv The vertical swing setting. +/// @param[in] swingh The horizontal swing setting. +/// @param[in] quiet Run the device in quiet/silent mode. +/// @param[in] turbo Run the device in turbo/powerful mode. +/// @param[in] light Turn on the LED/Display mode. +/// @param[in] econo Run the device in economical mode. +/// @param[in] filter Turn on the (ion/pollen/etc) filter mode. +void IRac::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) { + ac->begin(); + ac->setModel(model); + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setSwingVertical(ac->convertSwingV(swingv)); + ac->setSwingHorizontal(swingh != stdAc::swingh_t::kOff); + ac->setQuiet(quiet); + ac->setTurbo(turbo); + ac->setLight(light); + ac->setEcono(econo); + ac->setHealth(filter); + // No Clean setting available. + // No Beep setting available. + // No Sleep setting available. + // No Clock setting available. + ac->send(); +} +#endif // SEND_TCL112AC + +#if SEND_TECHNIBEL_AC +/// Send a Technibel A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRTechnibelAc object to use. +/// @param[in] on The power setting. +/// @param[in] mode The operation mode setting. +/// @param[in] celsius Temperature units. True is Celsius, False is Fahrenheit. +/// @param[in] degrees The temperature setting in degrees. +/// @param[in] fan The speed setting for the fan. +/// @param[in] swingv The vertical swing setting. +/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on. +void IRac::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) { + ac->begin(); + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees, !celsius); + ac->setFan(ac->convertFan(fan)); + ac->setSwing(swingv != stdAc::swingv_t::kOff); + // No Horizontal swing setting available. + // No Quiet setting available. + // No Turbo setting available. + // No Light setting available. + // No Filter setting available. + // No Clean setting available. + // No Beep setting available. + ac->setSleep(sleep >= 0); // Sleep is either on/off, so convert to boolean. + // No Clock setting available. + ac->send(); +} +#endif // SEND_TECHNIBEL_AC + +#if SEND_TECO +/// Send a Teco A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRTecoAc object to use. +/// @param[in] on The power setting. +/// @param[in] mode The operation mode setting. +/// @param[in] degrees The temperature setting in degrees. +/// @param[in] fan The speed setting for the fan. +/// @param[in] swingv The vertical swing setting. +/// @param[in] light Turn on the LED/Display mode. +/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on. +void IRac::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) { + ac->begin(); + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setSwing(swingv != stdAc::swingv_t::kOff); + // No Horizontal swing setting available. + // No Quiet setting available. + // No Turbo setting available. + ac->setLight(light); + // No Filter setting available. + // No Clean setting available. + // No Beep setting available. + ac->setSleep(sleep >= 0); // Sleep is either on/off, so convert to boolean. + // No Clock setting available. + ac->send(); +} +#endif // SEND_TECO + +#if SEND_TOSHIBA_AC +/// Send a Toshiba A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRToshibaAC object to use. +/// @param[in] on The power setting. +/// @param[in] mode The operation mode setting. +/// @param[in] degrees The temperature setting in degrees. +/// @param[in] fan The speed setting for the fan. +/// @param[in] swingv The vertical swing setting. +/// @param[in] turbo Run the device in turbo/powerful mode. +/// @param[in] econo Run the device in economical mode. +void IRac::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) { + ac->begin(); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + // The API has no "step" option, so off is off, anything else is on. + ac->setSwing((swingv == stdAc::swingv_t::kOff) ? kToshibaAcSwingOff + : kToshibaAcSwingOn); + // No Horizontal swing setting available. + // No Quiet setting available. + ac->setTurbo(turbo); + ac->setEcono(econo); + // No Light setting available. + // No Filter setting available. + // No Clean setting available. + // No Beep setting available. + // No Sleep setting available. + // No Clock setting available. + // Do this last because Toshiba A/C has an odd quirk with how power off works. + ac->setPower(on); + ac->send(); +} +#endif // SEND_TOSHIBA_AC + +#if SEND_TROTEC +/// Send a Trotec A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRTrotecESP object to use. +/// @param[in] on The power setting. +/// @param[in] mode The operation mode setting. +/// @param[in] degrees The temperature setting in degrees. +/// @param[in] fan The speed setting for the fan. +/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on. +void IRac::trotec(IRTrotecESP *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const int16_t sleep) { + ac->begin(); + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setSpeed(ac->convertFan(fan)); + // No Vertical swing setting available. + // No Horizontal swing setting available. + // No Quiet setting available. + // No Turbo setting available. + // No Light setting available. + // No Filter setting available. + // No Clean setting available. + // No Beep setting available. + ac->setSleep(sleep >= 0); // Sleep is either on/off, so convert to boolean. + // No Clock setting available. + ac->send(); +} +#endif // SEND_TROTEC + +#if SEND_TROTEC_3550 +/// Send a Trotec 3550 A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRTrotecESP object to use. +/// @param[in] on The power setting. +/// @param[in] mode The operation mode setting. +/// @param[in] celsius Temperature units. True is Celsius, False is Fahrenheit. +/// @param[in] degrees The temperature setting in degrees. +/// @param[in] fan The speed setting for the fan. +/// @param[in] swingv The vertical swing setting. +void IRac::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) { + ac->begin(); + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees, celsius); + ac->setFan(ac->convertFan(fan)); + ac->setSwingV(swingv != stdAc::swingv_t::kOff); + // No Horizontal swing setting available. + // No Quiet setting available. + // No Turbo setting available. + // No Light setting available. + // No Filter setting available. + // No Clean setting available. + // No Beep setting available. + // No Sleep setting available. + // No Clock setting available. + ac->send(); +} +#endif // SEND_TROTEC_3550 + +#if SEND_TRUMA +/// Send a Truma A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRTrumaAc object to use. +/// @param[in] on The power setting. +/// @param[in] mode The operation mode setting. +/// @param[in] degrees The temperature setting in degrees. +/// @param[in] fan The speed setting for the fan. +/// @param[in] quiet Run the device quietly if we can. +void IRac::truma(IRTrumaAc *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const bool quiet) { + ac->begin(); + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setQuiet(quiet); // Only available in Cool mode. + // No Vertical swing setting available. + // No Horizontal swing setting available. + // No Turbo setting available. + // No Light setting available. + // No Filter setting available. + // No Clean setting available. + // No Beep setting available. + // No Sleep setting available. + // No Clock setting available. + ac->send(); +} +#endif // SEND_TRUMA + +#if SEND_VESTEL_AC +/// Send a Vestel A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRVestelAc object to use. +/// @param[in] on The power setting. +/// @param[in] mode The operation mode setting. +/// @param[in] degrees The temperature setting in degrees. +/// @param[in] fan The speed setting for the fan. +/// @param[in] swingv The vertical swing setting. +/// @param[in] turbo Run the device in turbo/powerful mode. +/// @param[in] filter Turn on the (ion/pollen/etc) filter mode. +/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on. +/// @param[in] clock The time in Nr. of mins since midnight. < 0 is ignore. +/// @param[in] sendNormal Do we send a Normal settings message at all? +/// i.e In addition to the clock/time/timer message +void IRac::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, + const int16_t clock, const bool sendNormal) { + ac->begin(); + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setSwing(swingv != stdAc::swingv_t::kOff); + // No Horizontal swing setting available. + // No Quiet setting available. + ac->setTurbo(turbo); + // No Light setting available. + ac->setIon(filter); + // No Clean setting available. + // No Beep setting available. + ac->setSleep(sleep >= 0); // Sleep is either on/off, so convert to boolean. + if (sendNormal) ac->send(); // Send the normal message. + if (clock >= 0) { + ac->setTime(clock); + ac->send(); // Setting the clock requires a different "timer" message. + } +} +#endif // SEND_VESTEL_AC + +#if SEND_VOLTAS +/// Send a Voltas A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRVoltas object to use. +/// @param[in] model The A/C model to use. +/// @param[in] on The power setting. +/// @param[in] mode The operation mode setting. +/// @param[in] degrees The temperature setting in degrees. +/// @param[in] fan The speed setting for the fan. +/// @param[in] swingv The vertical swing setting. +/// @param[in] swingh The horizontal swing setting. +/// @param[in] turbo Run the device in turbo/powerful mode. +/// @param[in] econo Run the device in economical mode. +/// @param[in] light Turn on the LED/Display mode. +/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on. +void IRac::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) { + ac->begin(); + ac->setModel(model); + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setSwingV(swingv != stdAc::swingv_t::kOff); + ac->setSwingH(swingh != stdAc::swingh_t::kOff); + // No Quiet setting available. + ac->setTurbo(turbo); + ac->setEcono(econo); + ac->setLight(light); + // No Filter setting available. + // No Clean setting available. + // No Beep setting available. + ac->setSleep(sleep >= 0); // Sleep is either on/off, so convert to boolean. + // No Clock setting available. + ac->send(); +} +#endif // SEND_VOLTAS + +#if SEND_WHIRLPOOL_AC +/// Send a Whirlpool A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRWhirlpoolAc object to use. +/// @param[in] model The A/C model to use. +/// @param[in] on The power setting. +/// @param[in] mode The operation mode setting. +/// @param[in] degrees The temperature setting in degrees. +/// @param[in] fan The speed setting for the fan. +/// @param[in] swingv The vertical swing setting. +/// @param[in] turbo Run the device in turbo/powerful mode. +/// @param[in] light Turn on the LED/Display mode. +/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on. +/// @param[in] clock The time in Nr. of mins since midnight. < 0 is ignore. +void IRac::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, const int16_t clock) { + ac->begin(); + ac->setModel(model); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setSwing(swingv != stdAc::swingv_t::kOff); + // No Horizontal swing setting available. + // No Quiet setting available. + ac->setSuper(turbo); + ac->setLight(light); + // No Filter setting available + // No Clean setting available. + // No Beep setting available. + ac->setSleep(sleep >= 0); // Sleep is either on/off, so convert to boolean. + if (clock >= 0) ac->setClock(clock); + ac->setPowerToggle(on); + ac->send(); +} +#endif // SEND_WHIRLPOOL_AC + +#if SEND_TRANSCOLD +/// Send a Transcold A/C message with the supplied settings. +/// @note May result in multiple messages being sent. +/// @param[in, out] ac A Ptr to an IRTranscoldAc object to use. +/// @param[in] on The power setting. +/// @param[in] mode The operation mode setting. +/// @param[in] degrees The temperature setting in degrees. +/// @param[in] fan The speed setting for the fan. +/// @param[in] swingv The vertical swing setting. +/// @param[in] swingh The horizontal swing setting. +/// @note -1 is Off, >= 0 is on. +void IRac::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) { + ac->begin(); + ac->setPower(on); + if (!on) { + // after turn off AC no more commands should + // be accepted + ac->send(); + return; + } + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + // No Filter setting available. + // No Beep setting available. + // No Clock setting available. + // No Econo setting available. + // No Quiet setting available. + if (swingv != stdAc::swingv_t::kOff || swingh != stdAc::swingh_t::kOff) { + // Swing has a special command that needs to be sent independently. + ac->setSwing(); + ac->send(); + } + + ac->send(); +} +#endif // SEND_TRANSCOLD + +#if SEND_RHOSS +/// Send an Rhoss A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRRhossAc object to use. +/// @param[in] on The power setting. +/// @param[in] mode The operation mode setting. +/// @param[in] degrees The temperature setting in degrees. +/// @param[in] fan The speed setting for the fan. +/// @param[in] swing The swing setting. +void IRac::rhoss(IRRhossAc *ac, + const bool on, const stdAc::opmode_t mode, const float degrees, + const stdAc::fanspeed_t fan, const stdAc::swingv_t swing) { + ac->begin(); + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setSwing(swing != stdAc::swingv_t::kOff); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + // No Quiet setting available. + // No Light setting available. + // No Filter setting available. + // No Turbo setting available. + // No Economy setting available. + // No Clean setting available. + // No Beep setting available. + // No Sleep setting available. + ac->send(); +} +#endif // SEND_RHOSS + +/// Create a new state base on the provided state that has been suitably fixed. +/// @note This is for use with Home Assistant, which requires mode to be off if +/// the power is off. +/// @param[in] state The state_t structure describing the desired a/c state. +/// @return A stdAc::state_t with the needed settings. +stdAc::state_t IRac::cleanState(const stdAc::state_t state) { + stdAc::state_t result = state; + // A hack for Home Assistant, it appears to need/want an Off opmode. + // So enforce the power is off if the mode is also off. + if (state.mode == stdAc::opmode_t::kOff) result.power = false; + return result; +} + +/// Create a new state base on desired & previous states but handle +/// any state changes for options that need to be toggled. +/// @param[in] desired The state_t structure describing the desired a/c state. +/// @param[in] prev A Ptr to the previous state_t structure. +/// @return A stdAc::state_t with the needed settings. +stdAc::state_t IRac::handleToggles(const stdAc::state_t desired, + const stdAc::state_t *prev) { + stdAc::state_t result = desired; + // If we've been given a previous state AND the it's the same A/C basically. + if (prev != NULL && desired.protocol == prev->protocol && + desired.model == prev->model) { + // Check if we have to handle toggle settings for specific A/C protocols. + switch (desired.protocol) { + case decode_type_t::COOLIX: + case decode_type_t::TRANSCOLD: + if ((desired.swingv == stdAc::swingv_t::kOff) ^ + (prev->swingv == stdAc::swingv_t::kOff)) // It changed, so toggle. + result.swingv = stdAc::swingv_t::kAuto; + else + result.swingv = stdAc::swingv_t::kOff; // No change, so no toggle. + result.turbo = desired.turbo ^ prev->turbo; + result.light = desired.light ^ prev->light; + result.clean = desired.clean ^ prev->clean; + result.sleep = ((desired.sleep >= 0) ^ (prev->sleep >= 0)) ? 0 : -1; + break; + case decode_type_t::DAIKIN128: + result.power = desired.power ^ prev->power; + result.light = desired.light ^ prev->light; + break; + case decode_type_t::ELECTRA_AC: + result.light = desired.light ^ prev->light; + break; + case decode_type_t::FUJITSU_AC: + result.turbo = desired.turbo ^ prev->turbo; + result.econo = desired.econo ^ prev->econo; + break; + case decode_type_t::MIDEA: + result.turbo = desired.turbo ^ prev->turbo; + result.econo = desired.econo ^ prev->econo; + result.light = desired.light ^ prev->light; + // FALL THRU + case decode_type_t::CORONA_AC: + case decode_type_t::HITACHI_AC344: + case decode_type_t::HITACHI_AC424: + if ((desired.swingv == stdAc::swingv_t::kOff) ^ + (prev->swingv == stdAc::swingv_t::kOff)) // It changed, so toggle. + result.swingv = stdAc::swingv_t::kAuto; + else + result.swingv = stdAc::swingv_t::kOff; // No change, so no toggle. + break; + case decode_type_t::SHARP_AC: + result.light = desired.light ^ prev->light; + if ((desired.swingv == stdAc::swingv_t::kOff) ^ + (prev->swingv == stdAc::swingv_t::kOff)) // It changed, so toggle. + result.swingv = stdAc::swingv_t::kAuto; + else + result.swingv = stdAc::swingv_t::kOff; // No change, so no toggle. + break; + case decode_type_t::KELON: + if ((desired.swingv == stdAc::swingv_t::kOff) ^ + (prev->swingv == stdAc::swingv_t::kOff)) // It changed, so toggle. + result.swingv = stdAc::swingv_t::kAuto; + else + result.swingv = stdAc::swingv_t::kOff; // No change, so no toggle. + // FALL-THRU + case decode_type_t::AIRWELL: + case decode_type_t::DAIKIN64: + case decode_type_t::PANASONIC_AC32: + case decode_type_t::WHIRLPOOL_AC: + result.power = desired.power ^ prev->power; + break; + case decode_type_t::PANASONIC_AC: + // CKP models use a power mode toggle. + if (desired.model == panasonic_ac_remote_model_t::kPanasonicCkp) + result.power = desired.power ^ prev->power; + break; + default: + {}; + } + } + return result; +} + +/// Send A/C message for a given device using common A/C settings. +/// @param[in] vendor The vendor/protocol type. +/// @param[in] model The A/C model if applicable. +/// @param[in] power The power setting. +/// @param[in] mode The operation mode setting. +/// @note Changing mode from "Off" to something else does NOT turn on a device. +/// You need to use `power` for that. +/// @param[in] degrees The temperature setting in degrees. +/// @param[in] celsius Temperature units. True is Celsius, False is Fahrenheit. +/// @param[in] fan The speed setting for the fan. +/// @note The following are all "if supported" by the underlying A/C classes. +/// @param[in] swingv The vertical swing setting. +/// @param[in] swingh The horizontal swing setting. +/// @param[in] quiet Run the device in quiet/silent mode. +/// @param[in] turbo Run the device in turbo/powerful mode. +/// @param[in] econo Run the device in economical mode. +/// @param[in] light Turn on the LED/Display mode. +/// @param[in] filter Turn on the (ion/pollen/etc) filter mode. +/// @param[in] clean Turn on the self-cleaning mode. e.g. Mould, dry filters etc +/// @param[in] beep Enable/Disable beeps when receiving IR messages. +/// @param[in] sleep Nr. of minutes for sleep mode. +/// -1 is Off, >= 0 is on. Some devices it is the nr. of mins to run for. +/// Others it may be the time to enter/exit sleep mode. +/// i.e. Time in Nr. of mins since midnight. +/// @param[in] clock The time in Nr. of mins since midnight. < 0 is ignore. +/// @return True, if accepted/converted/attempted etc. False, if unsupported. +bool IRac::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, const int16_t clock) { + stdAc::state_t to_send; + initState(&to_send, vendor, model, power, mode, degrees, celsius, fan, swingv, + swingh, quiet, turbo, econo, light, filter, clean, beep, sleep, + clock); + return this->sendAc(to_send, &to_send); +} + +/// Send A/C message for a given device using state_t structures. +/// @param[in] desired The state_t structure describing the desired new ac state +/// @param[in] prev A Ptr to the state_t structure containing the previous state +/// @note Changing mode from "Off" to something else does NOT turn on a device. +/// You need to use `power` for that. +/// @return True, if accepted/converted/attempted etc. False, if unsupported. +bool IRac::sendAc(const stdAc::state_t desired, const stdAc::state_t *prev) { + // Convert the temp from Fahrenheit to Celsius if we are not in Celsius mode. + float degC __attribute__((unused)) = + desired.celsius ? desired.degrees : fahrenheitToCelsius(desired.degrees); + // special `state_t` that is required to be sent based on that. + stdAc::state_t send = this->handleToggles(this->cleanState(desired), prev); + // Some protocols expect a previous state for power. + // Construct a pointer-safe previous power state incase prev is NULL/NULLPTR. +#if (SEND_HITACHI_AC1 || SEND_SAMSUNG_AC || SEND_SHARP_AC) + const bool prev_power = (prev != NULL) ? prev->power : !send.power; +#endif // (SEND_HITACHI_AC1 || SEND_SAMSUNG_AC || SEND_SHARP_AC) +#if (SEND_LG || SEND_SHARP_AC) + const stdAc::swingv_t prev_swingv = (prev != NULL) ? prev->swingv + : stdAc::swingv_t::kOff; +#endif // (SEND_LG || SEND_SHARP_AC) + // Per vendor settings & setup. + switch (send.protocol) { +#if SEND_AIRWELL + case AIRWELL: + { + IRAirwellAc ac(_pin, _inverted, _modulation); + airwell(&ac, send.power, send.mode, degC, send.fanspeed); + break; + } +#endif // SEND_AIRWELL +#if SEND_AMCOR + case AMCOR: + { + IRAmcorAc ac(_pin, _inverted, _modulation); + amcor(&ac, send.power, send.mode, degC, send.fanspeed); + break; + } +#endif // SEND_AMCOR +#if SEND_ARGO + case ARGO: + { + IRArgoAC ac(_pin, _inverted, _modulation); + argo(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv, + send.turbo, send.sleep); + break; + } +#endif // SEND_ARGO +#if SEND_CARRIER_AC64 + case CARRIER_AC64: + { + IRCarrierAc64 ac(_pin, _inverted, _modulation); + carrier64(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv, + send.sleep); + break; + } +#endif // SEND_CARRIER_AC64 +#if SEND_COOLIX + case COOLIX: + { + IRCoolixAC ac(_pin, _inverted, _modulation); + coolix(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv, + send.swingh, send.turbo, send.light, send.clean, send.sleep); + break; + } +#endif // SEND_COOLIX +#if SEND_CORONA_AC + case CORONA_AC: + { + IRCoronaAc ac(_pin, _inverted, _modulation); + corona(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv, + send.econo); + break; + } +#endif // SEND_CORONA_AC +#if SEND_DAIKIN + case DAIKIN: + { + IRDaikinESP ac(_pin, _inverted, _modulation); + daikin(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv, + send.swingh, send.quiet, send.turbo, send.econo, send.clean); + break; + } +#endif // SEND_DAIKIN +#if SEND_DAIKIN128 + case DAIKIN128: + { + IRDaikin128 ac(_pin, _inverted, _modulation); + daikin128(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv, + send.quiet, send.turbo, send.light, send.econo, send.sleep, + send.clock); + break; + } +#endif // SEND_DAIKIN2 +#if SEND_DAIKIN152 + case DAIKIN152: + { + IRDaikin152 ac(_pin, _inverted, _modulation); + daikin152(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv, + send.quiet, send.turbo, send.econo); + break; + } +#endif // SEND_DAIKIN152 +#if SEND_DAIKIN160 + case DAIKIN160: + { + IRDaikin160 ac(_pin, _inverted, _modulation); + daikin160(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv); + break; + } +#endif // SEND_DAIKIN160 +#if SEND_DAIKIN176 + case DAIKIN176: + { + IRDaikin176 ac(_pin, _inverted, _modulation); + daikin176(&ac, send.power, send.mode, degC, send.fanspeed, send.swingh); + break; + } +#endif // SEND_DAIKIN176 +#if SEND_DAIKIN2 + case DAIKIN2: + { + IRDaikin2 ac(_pin, _inverted, _modulation); + daikin2(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv, + send.swingh, send.quiet, send.turbo, send.light, send.econo, + send.filter, send.clean, send.beep, send.sleep, send.clock); + break; + } +#endif // SEND_DAIKIN2 +#if SEND_DAIKIN216 + case DAIKIN216: + { + IRDaikin216 ac(_pin, _inverted, _modulation); + daikin216(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv, + send.swingh, send.quiet, send.turbo); + break; + } +#endif // SEND_DAIKIN216 +#if SEND_DAIKIN64 + case DAIKIN64: + { + IRDaikin64 ac(_pin, _inverted, _modulation); + daikin64(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv, + send.quiet, send.turbo, send.sleep, send.clock); + break; + } +#endif // SEND_DAIKIN64 +#if SEND_DELONGHI_AC + case DELONGHI_AC: + { + IRDelonghiAc ac(_pin, _inverted, _modulation); + delonghiac(&ac, send.power, send.mode, send.celsius, degC, send.fanspeed, + send.turbo, send.sleep); + break; + } +#endif // SEND_DELONGHI_AC +#if SEND_ECOCLIM + case ECOCLIM: + { + IREcoclimAc ac(_pin, _inverted, _modulation); + ecoclim(&ac, send.power, send.mode, degC, send.fanspeed, send.clock); + break; + } +#endif // SEND_ECOCLIM +#if SEND_ELECTRA_AC + case ELECTRA_AC: + { + IRElectraAc ac(_pin, _inverted, _modulation); + electra(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv, + send.swingh, send.turbo, send.light, send.clean); + break; + } +#endif // SEND_ELECTRA_AC +#if SEND_FUJITSU_AC + case FUJITSU_AC: + { + IRFujitsuAC ac(_pin, (fujitsu_ac_remote_model_t)send.model, _inverted, + _modulation); + fujitsu(&ac, (fujitsu_ac_remote_model_t)send.model, send.power, send.mode, + send.celsius, send.degrees, send.fanspeed, + send.swingv, send.swingh, send.quiet, + send.turbo, send.econo, send.filter, send.clean); + break; + } +#endif // SEND_FUJITSU_AC +#if SEND_GOODWEATHER + case GOODWEATHER: + { + IRGoodweatherAc ac(_pin, _inverted, _modulation); + goodweather(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv, + send.turbo, send.light, send.sleep); + break; + } +#endif // SEND_GOODWEATHER +#if SEND_GREE + case GREE: + { + IRGreeAC ac(_pin, (gree_ac_remote_model_t)send.model, _inverted, + _modulation); + gree(&ac, (gree_ac_remote_model_t)send.model, send.power, send.mode, + send.celsius, send.degrees, send.fanspeed, send.swingv, send.turbo, + send.light, send.clean, send.sleep); + break; + } +#endif // SEND_GREE +#if SEND_HAIER_AC + case HAIER_AC: + { + IRHaierAC ac(_pin, _inverted, _modulation); + haier(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv, + send.filter, send.sleep, send.clock); + break; + } +#endif // SEND_HAIER_AC +#if SEND_HAIER_AC176 + case HAIER_AC176: + { + IRHaierAC176 ac(_pin, _inverted, _modulation); + haier176(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv, + send.turbo, send.filter, send.sleep); + break; + } +#endif // SEND_HAIER_AC176 +#if SEND_HAIER_AC_YRW02 + case HAIER_AC_YRW02: + { + IRHaierACYRW02 ac(_pin, _inverted, _modulation); + haierYrwo2(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv, + send.turbo, send.filter, send.sleep); + break; + } +#endif // SEND_HAIER_AC_YRW02 +#if SEND_HITACHI_AC + case HITACHI_AC: + { + IRHitachiAc ac(_pin, _inverted, _modulation); + hitachi(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv, + send.swingh); + break; + } +#endif // SEND_HITACHI_AC +#if SEND_HITACHI_AC1 + case HITACHI_AC1: + { + IRHitachiAc1 ac(_pin, _inverted, _modulation); + bool power_toggle = false; + bool swing_toggle = false; + if (prev != NULL) { + power_toggle = (send.power != prev->power); + swing_toggle = (send.swingv != prev->swingv) || + (send.swingh != prev->swingh); + } + hitachi1(&ac, (hitachi_ac1_remote_model_t)send.model, send.power, + power_toggle, send.mode, degC, send.fanspeed, send.swingv, + send.swingh, swing_toggle, send.sleep); + break; + } +#endif // SEND_HITACHI_AC1 +#if SEND_HITACHI_AC344 + case HITACHI_AC344: + { + IRHitachiAc344 ac(_pin, _inverted, _modulation); + hitachi344(&ac, send.power, send.mode, degC, send.fanspeed, + send.swingv, send.swingh); + break; + } +#endif // SEND_HITACHI_AC344 +#if SEND_HITACHI_AC424 + case HITACHI_AC424: + { + IRHitachiAc424 ac(_pin, _inverted, _modulation); + hitachi424(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv); + break; + } +#endif // SEND_HITACHI_AC424 +#if SEND_KELON + case KELON: { + IRKelonAc ac(_pin, _inverted, _modulation); + kelon(&ac, send.power, send.mode, 0, send.degrees, send.fanspeed, + send.swingv != stdAc::swingv_t::kOff, send.turbo, send.sleep); + break; + } +#endif +#if SEND_KELVINATOR + case KELVINATOR: + { + IRKelvinatorAC ac(_pin, _inverted, _modulation); + kelvinator(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv, + send.swingh, send.quiet, send.turbo, send.light, send.filter, + send.clean); + break; + } +#endif // SEND_KELVINATOR +#if SEND_LG + case LG: + case LG2: + { + IRLgAc ac(_pin, _inverted, _modulation); + lg(&ac, (lg_ac_remote_model_t)send.model, send.power, send.mode, + send.degrees, send.fanspeed, send.swingv, prev_swingv, send.swingh, + send.light); + break; + } +#endif // SEND_LG +#if SEND_MIDEA + case MIDEA: + { + IRMideaAC ac(_pin, _inverted, _modulation); + midea(&ac, send.power, send.mode, send.celsius, send.degrees, + send.fanspeed, send.swingv, send.turbo, send.econo, send.light, + send.sleep); + break; + } +#endif // SEND_MIDEA +#if SEND_MITSUBISHI_AC + case MITSUBISHI_AC: + { + IRMitsubishiAC ac(_pin, _inverted, _modulation); + mitsubishi(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv, + send.swingh, send.quiet, send.clock); + break; + } +#endif // SEND_MITSUBISHI_AC +#if SEND_MITSUBISHI112 + case MITSUBISHI112: + { + IRMitsubishi112 ac(_pin, _inverted, _modulation); + mitsubishi112(&ac, send.power, send.mode, degC, send.fanspeed, + send.swingv, send.swingh, send.quiet); + break; + } +#endif // SEND_MITSUBISHI112 +#if SEND_MITSUBISHI136 + case MITSUBISHI136: + { + IRMitsubishi136 ac(_pin, _inverted, _modulation); + mitsubishi136(&ac, send.power, send.mode, degC, send.fanspeed, + send.swingv, send.quiet); + break; + } +#endif // SEND_MITSUBISHI136 +#if SEND_MITSUBISHIHEAVY + case MITSUBISHI_HEAVY_88: + { + IRMitsubishiHeavy88Ac ac(_pin, _inverted, _modulation); + mitsubishiHeavy88(&ac, send.power, send.mode, degC, send.fanspeed, + send.swingv, send.swingh, send.turbo, send.econo, + send.clean); + break; + } + case MITSUBISHI_HEAVY_152: + { + IRMitsubishiHeavy152Ac ac(_pin, _inverted, _modulation); + mitsubishiHeavy152(&ac, send.power, send.mode, degC, send.fanspeed, + send.swingv, send.swingh, send.quiet, send.turbo, + send.econo, send.filter, send.clean, send.sleep); + break; + } +#endif // SEND_MITSUBISHIHEAVY +#if SEND_NEOCLIMA + case NEOCLIMA: + { + IRNeoclimaAc ac(_pin, _inverted, _modulation); + neoclima(&ac, send.power, send.mode, send.celsius, send.degrees, + send.fanspeed, send.swingv, send.swingh, send.turbo, + send.econo, send.light, send.filter, send.sleep); + break; + } +#endif // SEND_NEOCLIMA +#if SEND_PANASONIC_AC + case PANASONIC_AC: + { + IRPanasonicAc ac(_pin, _inverted, _modulation); + panasonic(&ac, (panasonic_ac_remote_model_t)send.model, send.power, + send.mode, degC, send.fanspeed, send.swingv, send.swingh, + send.quiet, send.turbo, send.clock); + break; + } +#endif // SEND_PANASONIC_AC +#if SEND_PANASONIC_AC32 + case PANASONIC_AC32: + { + IRPanasonicAc32 ac(_pin, _inverted, _modulation); + panasonic32(&ac, send.power, send.mode, degC, send.fanspeed, + send.swingv, send.swingh); + break; + } +#endif // SEND_PANASONIC_AC32 +#if SEND_RHOSS + case RHOSS: + { + IRRhossAc ac(_pin, _inverted, _modulation); + rhoss(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv); + break; + } +#endif // SEND_RHOSS +#if SEND_SAMSUNG_AC + case SAMSUNG_AC: + { + IRSamsungAc ac(_pin, _inverted, _modulation); + samsung(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv, + send.quiet, send.turbo, send.light, send.filter, send.clean, + send.beep, prev_power); + break; + } +#endif // SEND_SAMSUNG_AC +#if SEND_SANYO_AC + case SANYO_AC: + { + IRSanyoAc ac(_pin, _inverted, _modulation); + sanyo(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv, + send.beep, send.sleep); + break; + } +#endif // SEND_SANYO_AC +#if SEND_SANYO_AC88 + case SANYO_AC88: + { + IRSanyoAc88 ac(_pin, _inverted, _modulation); + sanyo88(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv, + send.turbo, send.filter, send.sleep, send.clock); + break; + } +#endif // SEND_SANYO_AC88 +#if SEND_SHARP_AC + case SHARP_AC: + { + IRSharpAc ac(_pin, _inverted, _modulation); + sharp(&ac, (sharp_ac_remote_model_t)send.model, send.power, prev_power, + send.mode, degC, send.fanspeed, send.swingv, prev_swingv, + send.turbo, send.light, send.filter, send.clean); + break; + } +#endif // SEND_SHARP_AC +#if (SEND_TCL112AC || SEND_TEKNOPOINT) + case TCL112AC: + case TEKNOPOINT: + { + IRTcl112Ac ac(_pin, _inverted, _modulation); + tcl_ac_remote_model_t model = (tcl_ac_remote_model_t)send.model; + if (send.protocol == decode_type_t::TEKNOPOINT) + model = tcl_ac_remote_model_t::GZ055BE1; + tcl112(&ac, model, send.power, send.mode, + degC, send.fanspeed, send.swingv, send.swingh, send.quiet, + send.turbo, send.light, send.econo, send.filter); + break; + } +#endif // (SEND_TCL112AC || SEND_TEKNOPOINT) +#if SEND_TECHNIBEL_AC + case TECHNIBEL_AC: + { + IRTechnibelAc ac(_pin, _inverted, _modulation); + technibel(&ac, send.power, send.mode, send.celsius, send.degrees, + send.fanspeed, send.swingv, send.sleep); + break; + } +#endif // SEND_TECHNIBEL_AC +#if SEND_TECO + case TECO: + { + IRTecoAc ac(_pin, _inverted, _modulation); + teco(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv, + send.light, send.sleep); + break; + } +#endif // SEND_TECO +#if SEND_TOSHIBA_AC + case TOSHIBA_AC: + { + IRToshibaAC ac(_pin, _inverted, _modulation); + toshiba(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv, + send.turbo, send.econo); + break; + } +#endif // SEND_TOSHIBA_AC +#if SEND_TROTEC + case TROTEC: + { + IRTrotecESP ac(_pin, _inverted, _modulation); + trotec(&ac, send.power, send.mode, degC, send.fanspeed, send.sleep); + break; + } +#endif // SEND_TROTEC +#if SEND_TROTEC_3550 + case TROTEC_3550: + { + IRTrotec3550 ac(_pin, _inverted, _modulation); + trotec3550(&ac, send.power, send.mode, send.celsius, send.degrees, + send.fanspeed, send.swingv); + break; + } +#endif // SEND_TROTEC_3550 +#if SEND_TRUMA + case TRUMA: + { + IRTrumaAc ac(_pin, _inverted, _modulation); + truma(&ac, send.power, send.mode, degC, send.fanspeed, send.quiet); + break; + } +#endif // SEND_TRUMA +#if SEND_VESTEL_AC + case VESTEL_AC: + { + IRVestelAc ac(_pin, _inverted, _modulation); + vestel(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv, + send.turbo, send.filter, send.sleep, send.clock); + break; + } +#endif // SEND_VESTEL_AC +#if SEND_VOLTAS + case VOLTAS: + { + IRVoltas ac(_pin, _inverted, _modulation); + voltas(&ac, (voltas_ac_remote_model_t)send.model, send.power, send.mode, + degC, send.fanspeed, send.swingv, send.swingh, send.turbo, + send.econo, send.light, send.sleep); + break; + } +#endif // SEND_VOLTAS +#if SEND_WHIRLPOOL_AC + case WHIRLPOOL_AC: + { + IRWhirlpoolAc ac(_pin, _inverted, _modulation); + whirlpool(&ac, (whirlpool_ac_remote_model_t)send.model, send.power, + send.mode, degC, send.fanspeed, send.swingv, send.turbo, + send.light, send.sleep, send.clock); + break; + } +#endif // SEND_WHIRLPOOL_AC +#if SEND_TRANSCOLD + case TRANSCOLD: + { + IRTranscoldAc ac(_pin, _inverted, _modulation); + transcold(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv, + send.swingh); + break; + } +#endif // SEND_TRANSCOLD_AC + default: + return false; // Fail, didn't match anything. + } + return true; // Success. +} // NOLINT(readability/fn_size) + +/// Update the previous state to the current one. +void IRac::markAsSent(void) { + _prev = next; +} + +/// Send an A/C message based soley on our internal state. +/// @return True, if accepted/converted/attempted. False, if unsupported. +bool IRac::sendAc(void) { + bool success = this->sendAc(next, &_prev); + if (success) this->markAsSent(); + return success; +} + +/// Compare two AirCon states. +/// @note The comparison excludes the clock. +/// @param a A state_t to be compared. +/// @param b A state_t to be compared. +/// @return True if they differ, False if they don't. +bool IRac::cmpStates(const stdAc::state_t a, const stdAc::state_t b) { + return a.protocol != b.protocol || a.model != b.model || a.power != b.power || + a.mode != b.mode || a.degrees != b.degrees || a.celsius != b.celsius || + a.fanspeed != b.fanspeed || a.swingv != b.swingv || + a.swingh != b.swingh || a.quiet != b.quiet || a.turbo != b.turbo || + a.econo != b.econo || a.light != b.light || a.filter != b.filter || + a.clean != b.clean || a.beep != b.beep || a.sleep != b.sleep; +} + +/// Check if the internal state has changed from what was previously sent. +/// @note The comparison excludes the clock. +/// @return True if it has changed, False if not. +bool IRac::hasStateChanged(void) { return cmpStates(next, _prev); } + +/// Convert the supplied str into the appropriate enum. +/// @param[in] str A Ptr to a C-style string to be converted. +/// @param[in] def The enum to return if no conversion was possible. +/// @return The equivalent enum. +stdAc::opmode_t IRac::strToOpmode(const char *str, + const stdAc::opmode_t def) { + if (!STRCASECMP(str, kAutoStr) || + !STRCASECMP(str, kAutomaticStr)) + return stdAc::opmode_t::kAuto; + else if (!STRCASECMP(str, kOffStr) || + !STRCASECMP(str, kStopStr)) + return stdAc::opmode_t::kOff; + else if (!STRCASECMP(str, kCoolStr) || + !STRCASECMP(str, kCoolingStr)) + return stdAc::opmode_t::kCool; + else if (!STRCASECMP(str, kHeatStr) || + !STRCASECMP(str, kHeatingStr)) + return stdAc::opmode_t::kHeat; + else if (!STRCASECMP(str, kDryStr) || + !STRCASECMP(str, kDryingStr) || + !STRCASECMP(str, kDehumidifyStr)) + return stdAc::opmode_t::kDry; + else if (!STRCASECMP(str, kFanStr) || + // 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 + !STRCASECMP(str, kFanOnlyStr) || + !STRCASECMP(str, kFan_OnlyStr) || + !STRCASECMP(str, kFanOnlyWithSpaceStr) || + !STRCASECMP(str, kFanOnlyNoSpaceStr)) + return stdAc::opmode_t::kFan; + else + return def; +} + +/// Convert the supplied str into the appropriate enum. +/// @param[in] str A Ptr to a C-style string to be converted. +/// @param[in] def The enum to return if no conversion was possible. +/// @return The equivalent enum. +stdAc::fanspeed_t IRac::strToFanspeed(const char *str, + const stdAc::fanspeed_t def) { + if (!STRCASECMP(str, kAutoStr) || + !STRCASECMP(str, kAutomaticStr)) + return stdAc::fanspeed_t::kAuto; + else if (!STRCASECMP(str, kMinStr) || + !STRCASECMP(str, kMinimumStr) || + !STRCASECMP(str, kLowestStr)) + return stdAc::fanspeed_t::kMin; + else if (!STRCASECMP(str, kLowStr) || + !STRCASECMP(str, kLoStr)) + return stdAc::fanspeed_t::kLow; + else if (!STRCASECMP(str, kMedStr) || + !STRCASECMP(str, kMediumStr) || + !STRCASECMP(str, kMidStr)) + return stdAc::fanspeed_t::kMedium; + else if (!STRCASECMP(str, kHighStr) || + !STRCASECMP(str, kHiStr)) + return stdAc::fanspeed_t::kHigh; + else if (!STRCASECMP(str, kMaxStr) || + !STRCASECMP(str, kMaximumStr) || + !STRCASECMP(str, kHighestStr)) + return stdAc::fanspeed_t::kMax; + else + return def; +} + +/// Convert the supplied str into the appropriate enum. +/// @param[in] str A Ptr to a C-style string to be converted. +/// @param[in] def The enum to return if no conversion was possible. +/// @return The equivalent enum. +stdAc::swingv_t IRac::strToSwingV(const char *str, + const stdAc::swingv_t def) { + if (!STRCASECMP(str, kAutoStr) || + !STRCASECMP(str, kAutomaticStr) || + !STRCASECMP(str, kOnStr) || + !STRCASECMP(str, kSwingStr)) + return stdAc::swingv_t::kAuto; + else if (!STRCASECMP(str, kOffStr) || + !STRCASECMP(str, kStopStr)) + return stdAc::swingv_t::kOff; + else if (!STRCASECMP(str, kMinStr) || + !STRCASECMP(str, kMinimumStr) || + !STRCASECMP(str, kLowestStr) || + !STRCASECMP(str, kBottomStr) || + !STRCASECMP(str, kDownStr)) + return stdAc::swingv_t::kLowest; + else if (!STRCASECMP(str, kLowStr)) + return stdAc::swingv_t::kLow; + else if (!STRCASECMP(str, kMidStr) || + !STRCASECMP(str, kMiddleStr) || + !STRCASECMP(str, kMedStr) || + !STRCASECMP(str, kMediumStr) || + !STRCASECMP(str, kCentreStr)) + return stdAc::swingv_t::kMiddle; + else if (!STRCASECMP(str, kHighStr) || + !STRCASECMP(str, kHiStr)) + return stdAc::swingv_t::kHigh; + else if (!STRCASECMP(str, kHighestStr) || + !STRCASECMP(str, kMaxStr) || + !STRCASECMP(str, kMaximumStr) || + !STRCASECMP(str, kTopStr) || + !STRCASECMP(str, kUpStr)) + return stdAc::swingv_t::kHighest; + else + return def; +} + +/// Convert the supplied str into the appropriate enum. +/// @param[in] str A Ptr to a C-style string to be converted. +/// @param[in] def The enum to return if no conversion was possible. +/// @return The equivalent enum. +stdAc::swingh_t IRac::strToSwingH(const char *str, + const stdAc::swingh_t def) { + if (!STRCASECMP(str, kAutoStr) || + !STRCASECMP(str, kAutomaticStr) || + !STRCASECMP(str, kOnStr) || !STRCASECMP(str, kSwingStr)) + return stdAc::swingh_t::kAuto; + else if (!STRCASECMP(str, kOffStr) || + !STRCASECMP(str, kStopStr)) + return stdAc::swingh_t::kOff; + else if (!STRCASECMP(str, kLeftMaxNoSpaceStr) || // "LeftMax" + !STRCASECMP(str, kLeftMaxStr) || // "Left Max" + !STRCASECMP(str, kMaxLeftNoSpaceStr) || // "MaxLeft" + !STRCASECMP(str, kMaxLeftStr)) // "Max Left" + return stdAc::swingh_t::kLeftMax; + else if (!STRCASECMP(str, kLeftStr)) + return stdAc::swingh_t::kLeft; + else if (!STRCASECMP(str, kMidStr) || + !STRCASECMP(str, kMiddleStr) || + !STRCASECMP(str, kMedStr) || + !STRCASECMP(str, kMediumStr) || + !STRCASECMP(str, kCentreStr)) + return stdAc::swingh_t::kMiddle; + else if (!STRCASECMP(str, kRightStr)) + return stdAc::swingh_t::kRight; + else if (!STRCASECMP(str, kRightMaxNoSpaceStr) || // "RightMax" + !STRCASECMP(str, kRightMaxStr) || // "Right Max" + !STRCASECMP(str, kMaxRightNoSpaceStr) || // "MaxRight" + !STRCASECMP(str, kMaxRightStr)) // "Max Right" + return stdAc::swingh_t::kRightMax; + else if (!STRCASECMP(str, kWideStr)) + return stdAc::swingh_t::kWide; + else + return def; +} + +/// Convert the supplied str into the appropriate enum. +/// @note Assumes str is the model code or an integer >= 1. +/// @param[in] str A Ptr to a C-style string to be converted. +/// @param[in] def The enum to return if no conversion was possible. +/// @return The equivalent enum. +int16_t IRac::strToModel(const char *str, const int16_t def) { + // Gree + if (!STRCASECMP(str, kYaw1fStr)) { + return gree_ac_remote_model_t::YAW1F; + } else if (!STRCASECMP(str, kYbofbStr)) { + return gree_ac_remote_model_t::YBOFB; + // HitachiAc1 models + } else if (!STRCASECMP(str, kRlt0541htaaStr)) { + return hitachi_ac1_remote_model_t::R_LT0541_HTA_A; + } else if (!STRCASECMP(str, kRlt0541htabStr)) { + return hitachi_ac1_remote_model_t::R_LT0541_HTA_B; + // Fujitsu A/C models + } else if (!STRCASECMP(str, kArrah2eStr)) { + return fujitsu_ac_remote_model_t::ARRAH2E; + } else if (!STRCASECMP(str, kArdb1Str)) { + return fujitsu_ac_remote_model_t::ARDB1; + } else if (!STRCASECMP(str, kArreb1eStr)) { + return fujitsu_ac_remote_model_t::ARREB1E; + } else if (!STRCASECMP(str, kArjw2Str)) { + return fujitsu_ac_remote_model_t::ARJW2; + } else if (!STRCASECMP(str, kArry4Str)) { + return fujitsu_ac_remote_model_t::ARRY4; + } else if (!STRCASECMP(str, kArrew4eStr)) { + return fujitsu_ac_remote_model_t::ARREW4E; + // LG A/C models + } else if (!STRCASECMP(str, kGe6711ar2853mStr)) { + return lg_ac_remote_model_t::GE6711AR2853M; + } else if (!STRCASECMP(str, kAkb75215403Str)) { + return lg_ac_remote_model_t::AKB75215403; + } else if (!STRCASECMP(str, kAkb74955603Str)) { + return lg_ac_remote_model_t::AKB74955603; + } else if (!STRCASECMP(str, kAkb73757604Str)) { + return lg_ac_remote_model_t::AKB73757604; + // Panasonic A/C families + } else if (!STRCASECMP(str, kLkeStr) || + !STRCASECMP(str, kPanasonicLkeStr)) { + return panasonic_ac_remote_model_t::kPanasonicLke; + } else if (!STRCASECMP(str, kNkeStr) || + !STRCASECMP(str, kPanasonicNkeStr)) { + return panasonic_ac_remote_model_t::kPanasonicNke; + } else if (!STRCASECMP(str, kDkeStr) || + !STRCASECMP(str, kPanasonicDkeStr) || + !STRCASECMP(str, kPkrStr) || + !STRCASECMP(str, kPanasonicPkrStr)) { + return panasonic_ac_remote_model_t::kPanasonicDke; + } else if (!STRCASECMP(str, kJkeStr) || + !STRCASECMP(str, kPanasonicJkeStr)) { + return panasonic_ac_remote_model_t::kPanasonicJke; + } else if (!STRCASECMP(str, kCkpStr) || + !STRCASECMP(str, kPanasonicCkpStr)) { + return panasonic_ac_remote_model_t::kPanasonicCkp; + } else if (!STRCASECMP(str, kRkrStr) || + !STRCASECMP(str, kPanasonicRkrStr)) { + return panasonic_ac_remote_model_t::kPanasonicRkr; + // Sharp A/C Models + } else if (!STRCASECMP(str, kA907Str)) { + return sharp_ac_remote_model_t::A907; + } else if (!STRCASECMP(str, kA705Str)) { + return sharp_ac_remote_model_t::A705; + } else if (!STRCASECMP(str, kA903Str)) { + return sharp_ac_remote_model_t::A903; + // TCL A/C Models + } else if (!STRCASECMP(str, kTac09chsdStr)) { + return tcl_ac_remote_model_t::TAC09CHSD; + } else if (!STRCASECMP(str, kGz055be1Str)) { + return tcl_ac_remote_model_t::GZ055BE1; + // Voltas A/C models + } else if (!STRCASECMP(str, k122lzfStr)) { + return voltas_ac_remote_model_t::kVoltas122LZF; + // Whirlpool A/C models + } else if (!STRCASECMP(str, kDg11j13aStr) || + !STRCASECMP(str, kDg11j104Str)) { + return whirlpool_ac_remote_model_t::DG11J13A; + } else if (!STRCASECMP(str, kDg11j191Str)) { + return whirlpool_ac_remote_model_t::DG11J191; + } else { + int16_t number = atoi(str); + if (number > 0) + return number; + else + return def; + } +} + +/// Convert the supplied str into the appropriate boolean value. +/// @param[in] str A Ptr to a C-style string to be converted. +/// @param[in] def The boolean value to return if no conversion was possible. +/// @return The equivalent boolean value. +bool IRac::strToBool(const char *str, const bool def) { + if (!STRCASECMP(str, kOnStr) || + !STRCASECMP(str, k1Str) || + !STRCASECMP(str, kYesStr) || + !STRCASECMP(str, kTrueStr)) + return true; + else if (!STRCASECMP(str, kOffStr) || + !STRCASECMP(str, k0Str) || + !STRCASECMP(str, kNoStr) || + !STRCASECMP(str, kFalseStr)) + return false; + else + return def; +} + +/// Convert the supplied boolean into the appropriate String. +/// @param[in] value The boolean value to be converted. +/// @return The equivalent String for the locale. +String IRac::boolToString(const bool value) { + return value ? kOnStr : kOffStr; +} + +/// Convert the supplied operation mode into the appropriate String. +/// @param[in] mode The enum to be converted. +/// @param[in] ha A flag to indicate we want GoogleHome/HomeAssistant output. +/// @return The equivalent String for the locale. +String IRac::opmodeToString(const stdAc::opmode_t mode, const bool ha) { + switch (mode) { + case stdAc::opmode_t::kOff: return kOffStr; + case stdAc::opmode_t::kAuto: return kAutoStr; + case stdAc::opmode_t::kCool: return kCoolStr; + case stdAc::opmode_t::kHeat: return kHeatStr; + case stdAc::opmode_t::kDry: return kDryStr; + case stdAc::opmode_t::kFan: return ha ? kFanOnlyStr : kFanStr; + default: return kUnknownStr; + } +} + +/// Convert the supplied fan speed enum into the appropriate String. +/// @param[in] speed The enum to be converted. +/// @return The equivalent String for the locale. +String IRac::fanspeedToString(const stdAc::fanspeed_t speed) { + switch (speed) { + case stdAc::fanspeed_t::kAuto: return kAutoStr; + case stdAc::fanspeed_t::kMax: return kMaxStr; + case stdAc::fanspeed_t::kHigh: return kHighStr; + case stdAc::fanspeed_t::kMedium: return kMediumStr; + case stdAc::fanspeed_t::kLow: return kLowStr; + case stdAc::fanspeed_t::kMin: return kMinStr; + default: return kUnknownStr; + } +} + +/// Convert the supplied enum into the appropriate String. +/// @param[in] swingv The enum to be converted. +/// @return The equivalent String for the locale. +String IRac::swingvToString(const stdAc::swingv_t swingv) { + switch (swingv) { + case stdAc::swingv_t::kOff: return kOffStr; + case stdAc::swingv_t::kAuto: return kAutoStr; + case stdAc::swingv_t::kHighest: return kHighestStr; + case stdAc::swingv_t::kHigh: return kHighStr; + case stdAc::swingv_t::kMiddle: return kMiddleStr; + case stdAc::swingv_t::kLow: return kLowStr; + case stdAc::swingv_t::kLowest: return kLowestStr; + default: return kUnknownStr; + } +} + +/// Convert the supplied enum into the appropriate String. +/// @param[in] swingh The enum to be converted. +/// @return The equivalent String for the locale. +String IRac::swinghToString(const stdAc::swingh_t swingh) { + switch (swingh) { + case stdAc::swingh_t::kOff: return kOffStr; + case stdAc::swingh_t::kAuto: return kAutoStr; + case stdAc::swingh_t::kLeftMax: return kLeftMaxStr; + case stdAc::swingh_t::kLeft: return kLeftStr; + case stdAc::swingh_t::kMiddle: return kMiddleStr; + case stdAc::swingh_t::kRight: return kRightStr; + case stdAc::swingh_t::kRightMax: return kRightMaxStr; + case stdAc::swingh_t::kWide: return kWideStr; + default: return kUnknownStr; + } +} + +namespace IRAcUtils { + /// Display the human readable state of an A/C message if we can. + /// @param[in] result A Ptr to the captured `decode_results` that contains an + /// A/C mesg. + /// @return A string with the human description of the A/C message. + /// An empty string if we can't. + String resultAcToString(const decode_results * const result) { + switch (result->decode_type) { +#if DECODE_AIRWELL + case decode_type_t::AIRWELL: { + IRAirwellAc ac(kGpioUnused); + ac.setRaw(result->value); // AIRWELL uses value instead of state. + return ac.toString(); + } +#endif // DECODE_AIRWELL +#if DECODE_AMCOR + case decode_type_t::AMCOR: { + IRAmcorAc ac(kGpioUnused); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_AMCOR +#if DECODE_ARGO + case decode_type_t::ARGO: { + IRArgoAC ac(kGpioUnused); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_ARGO +#if DECODE_CARRIER_AC64 + case decode_type_t::CARRIER_AC64: { + IRCarrierAc64 ac(kGpioUnused); + ac.setRaw(result->value); // CARRIER_AC64 uses value instead of state. + return ac.toString(); + } +#endif // DECODE_CARRIER_AC64 +#if DECODE_DAIKIN + case decode_type_t::DAIKIN: { + IRDaikinESP ac(kGpioUnused); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_DAIKIN +#if DECODE_DAIKIN128 + case decode_type_t::DAIKIN128: { + IRDaikin128 ac(kGpioUnused); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_DAIKIN128 +#if DECODE_DAIKIN152 + case decode_type_t::DAIKIN152: { + IRDaikin152 ac(kGpioUnused); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_DAIKIN152 +#if DECODE_DAIKIN160 + case decode_type_t::DAIKIN160: { + IRDaikin160 ac(kGpioUnused); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_DAIKIN160 +#if DECODE_DAIKIN176 + case decode_type_t::DAIKIN176: { + IRDaikin176 ac(kGpioUnused); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_DAIKIN160 +#if DECODE_DAIKIN2 + case decode_type_t::DAIKIN2: { + IRDaikin2 ac(kGpioUnused); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_DAIKIN2 +#if DECODE_DAIKIN216 + case decode_type_t::DAIKIN216: { + IRDaikin216 ac(kGpioUnused); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_DAIKIN216 +#if DECODE_DAIKIN64 + case decode_type_t::DAIKIN64: { + IRDaikin64 ac(kGpioUnused); + ac.setRaw(result->value); // Daikin64 uses value instead of state. + return ac.toString(); + } +#endif // DECODE_DAIKIN64 +#if DECODE_DELONGHI_AC + case decode_type_t::DELONGHI_AC: { + IRDelonghiAc ac(kGpioUnused); + ac.setRaw(result->value); // DelonghiAc uses value instead of state. + return ac.toString(); + } +#endif // DECODE_DELONGHI_AC +#if DECODE_ECOCLIM + case decode_type_t::ECOCLIM: { + if (result->bits == kEcoclimBits) { + IREcoclimAc ac(kGpioUnused); + ac.setRaw(result->value); // EcoClim uses value instead of state. + return ac.toString(); + } + return ""; + } +#endif // DECODE_ECOCLIM +#if DECODE_ELECTRA_AC + case decode_type_t::ELECTRA_AC: { + IRElectraAc ac(kGpioUnused); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_ELECTRA_AC +#if DECODE_FUJITSU_AC + case decode_type_t::FUJITSU_AC: { + IRFujitsuAC ac(kGpioUnused); + ac.setRaw(result->state, result->bits / 8); + return ac.toString(); + } +#endif // DECODE_FUJITSU_AC +#if DECODE_KELON + case decode_type_t::KELON: { + IRKelonAc ac(kGpioUnused); + ac.setRaw(result->value); + return ac.toString(); + } +#endif // DECODE_KELON +#if DECODE_KELVINATOR + case decode_type_t::KELVINATOR: { + IRKelvinatorAC ac(kGpioUnused); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_KELVINATOR +#if DECODE_MITSUBISHI_AC + case decode_type_t::MITSUBISHI_AC: { + IRMitsubishiAC ac(kGpioUnused); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_MITSUBISHI_AC +#if DECODE_MITSUBISHI112 + case decode_type_t::MITSUBISHI112: { + IRMitsubishi112 ac(kGpioUnused); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_MITSUBISHI112 +#if DECODE_MITSUBISHI136 + case decode_type_t::MITSUBISHI136: { + IRMitsubishi136 ac(kGpioUnused); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_MITSUBISHI136 +#if DECODE_MITSUBISHIHEAVY + case decode_type_t::MITSUBISHI_HEAVY_88: { + IRMitsubishiHeavy88Ac ac(kGpioUnused); + ac.setRaw(result->state); + return ac.toString(); + } + case decode_type_t::MITSUBISHI_HEAVY_152: { + IRMitsubishiHeavy152Ac ac(kGpioUnused); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_MITSUBISHIHEAVY +#if DECODE_NEOCLIMA + case decode_type_t::NEOCLIMA: { + IRNeoclimaAc ac(kGpioUnused); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_NEOCLIMA +#if DECODE_TOSHIBA_AC + case decode_type_t::TOSHIBA_AC: { + IRToshibaAC ac(kGpioUnused); + ac.setRaw(result->state, result->bits / 8); + return ac.toString(); + } +#endif // DECODE_TOSHIBA_AC +#if DECODE_TROTEC + case decode_type_t::TROTEC: { + IRTrotecESP ac(kGpioUnused); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_TROTEC +#if DECODE_TROTEC_3550 + case decode_type_t::TROTEC_3550: { + IRTrotec3550 ac(kGpioUnused); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_TROTEC_3550 +#if DECODE_TRUMA + case decode_type_t::TRUMA: { + IRTrumaAc ac(kGpioUnused); + ac.setRaw(result->value); // Truma uses value instead of state. + return ac.toString(); + } +#endif // DECODE_TRUMA +#if DECODE_GOODWEATHER + case decode_type_t::GOODWEATHER: { + IRGoodweatherAc ac(kGpioUnused); + ac.setRaw(result->value); // Goodweather uses value instead of state. + return ac.toString(); + } +#endif // DECODE_GOODWEATHER +#if DECODE_GREE + case decode_type_t::GREE: { + IRGreeAC ac(kGpioUnused); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_GREE +#if DECODE_MIDEA + case decode_type_t::MIDEA: { + IRMideaAC ac(kGpioUnused); + ac.setRaw(result->value); // Midea uses value instead of state. + return ac.toString(); + } +#endif // DECODE_MIDEA +#if DECODE_HAIER_AC + case decode_type_t::HAIER_AC: { + IRHaierAC ac(kGpioUnused); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_HAIER_AC +#if DECODE_HAIER_AC176 + case decode_type_t::HAIER_AC176: { + IRHaierAC176 ac(kGpioUnused); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_HAIER_AC176 +#if DECODE_HAIER_AC_YRW02 + case decode_type_t::HAIER_AC_YRW02: { + IRHaierACYRW02 ac(kGpioUnused); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_HAIER_AC_YRW02 +#if DECODE_SAMSUNG_AC + case decode_type_t::SAMSUNG_AC: { + IRSamsungAc ac(kGpioUnused); + ac.setRaw(result->state, result->bits / 8); + return ac.toString(); + } +#endif // DECODE_SAMSUNG_AC +#if DECODE_SANYO_AC + case decode_type_t::SANYO_AC: { + IRSanyoAc ac(kGpioUnused); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_SANYO_AC +#if DECODE_SANYO_AC88 + case decode_type_t::SANYO_AC88: { + IRSanyoAc88 ac(kGpioUnused); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_SANYO_AC88 +#if DECODE_SHARP_AC + case decode_type_t::SHARP_AC: { + IRSharpAc ac(kGpioUnused); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_SHARP_AC +#if DECODE_COOLIX + case decode_type_t::COOLIX: { + IRCoolixAC ac(kGpioUnused); + ac.on(); + ac.setRaw(result->value); // Coolix uses value instead of state. + return ac.toString(); + } +#endif // DECODE_COOLIX +#if DECODE_CORONA_AC + case decode_type_t::CORONA_AC: { + IRCoronaAc ac(kGpioUnused); + ac.setRaw(result->state, result->bits / 8); + return ac.toString(); + } +#endif // DECODE_CORONA_AC +#if DECODE_PANASONIC_AC + case decode_type_t::PANASONIC_AC: { + if (result->bits > kPanasonicAcShortBits) { + IRPanasonicAc ac(kGpioUnused); + ac.setRaw(result->state); + return ac.toString(); + } + return ""; + } +#endif // DECODE_PANASONIC_AC +#if DECODE_PANASONIC_AC32 + case decode_type_t::PANASONIC_AC32: { + if (result->bits >= kPanasonicAc32Bits) { + IRPanasonicAc32 ac(kGpioUnused); + ac.setRaw(result->value); // Uses value instead of state. + return ac.toString(); + } + return ""; + } +#endif // DECODE_PANASONIC_AC +#if DECODE_HITACHI_AC + case decode_type_t::HITACHI_AC: { + IRHitachiAc ac(kGpioUnused); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_HITACHI_AC +#if DECODE_HITACHI_AC1 + case decode_type_t::HITACHI_AC1: { + IRHitachiAc1 ac(kGpioUnused); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_HITACHI_AC1 +#if DECODE_HITACHI_AC344 + case decode_type_t::HITACHI_AC344: { + IRHitachiAc344 ac(kGpioUnused); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_HITACHI_AC344 +#if DECODE_HITACHI_AC424 + case decode_type_t::HITACHI_AC424: { + IRHitachiAc424 ac(kGpioUnused); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_HITACHI_AC424 +#if DECODE_WHIRLPOOL_AC + case decode_type_t::WHIRLPOOL_AC: { + IRWhirlpoolAc ac(kGpioUnused); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_WHIRLPOOL_AC +#if DECODE_VESTEL_AC + case decode_type_t::VESTEL_AC: { + IRVestelAc ac(kGpioUnused); + ac.setRaw(result->value); // Like Coolix, use value instead of state. + return ac.toString(); + } +#endif // DECODE_VESTEL_AC +#if DECODE_TECHNIBEL_AC + case decode_type_t::TECHNIBEL_AC: { + IRTechnibelAc ac(kGpioUnused); + ac.setRaw(result->value); // TechnibelAc uses value instead of state. + return ac.toString(); + } +#endif // DECODE_TECHNIBEL_AC +#if DECODE_VOLTAS + case decode_type_t::VOLTAS: { + IRVoltas ac(kGpioUnused); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_VOLTAS +#if DECODE_TECO + case decode_type_t::TECO: { + IRTecoAc ac(kGpioUnused); + ac.setRaw(result->value); // Like Coolix, use value instead of state. + return ac.toString(); + } +#endif // DECODE_TECO +#if (DECODE_TCL112AC || DECODE_TEKNOPOINT) + case decode_type_t::TCL112AC: + case decode_type_t::TEKNOPOINT: { + IRTcl112Ac ac(kGpioUnused); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // (DECODE_TCL112AC || DECODE_TEKNOPOINT) +#if DECODE_LG + case decode_type_t::LG: + case decode_type_t::LG2: { + IRLgAc ac(kGpioUnused); + ac.setRaw(result->value, result->decode_type); // Use value, not state. + return ac.isValidLgAc() ? ac.toString() : ""; + } +#endif // DECODE_LG +#if DECODE_TRANSCOLD + case decode_type_t::TRANSCOLD: { + IRTranscoldAc ac(kGpioUnused); + ac.on(); + ac.setRaw(result->value); // TRANSCOLD uses value instead of state. + return ac.toString(); + } +#endif // DECODE_TRANSCOLD +#if DECODE_RHOSS + case decode_type_t::RHOSS: { + IRRhossAc ac(kGpioUnused); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_RHOSS + default: + return ""; + } + } + + /// Convert a valid IR A/C remote message that we understand enough into a + /// Common A/C state. + /// @param[in] decode A PTR to a successful raw IR decode object. + /// @param[in] result A PTR to a state structure to store the result in. + /// @param[in] prev A PTR to a state structure which has the prev. state. + /// @return A boolean indicating success or failure. + bool decodeToState(const decode_results *decode, stdAc::state_t *result, + const stdAc::state_t *prev +/// @cond IGNORE +// *prev flagged as "unused" due to potential compiler warning when some +// protocols that use it are disabled. It really is used. + __attribute__((unused)) +/// @endcond + ) { + if (decode == NULL || result == NULL) return false; // Safety check. + switch (decode->decode_type) { +#if DECODE_AIRWELL + case decode_type_t::AIRWELL: { + IRAirwellAc ac(kGpioUnused); + ac.setRaw(decode->value); // Uses value instead of state. + *result = ac.toCommon(prev); + break; + } +#endif // DECODE_AIRWELL +#if DECODE_AMCOR + case decode_type_t::AMCOR: { + IRAmcorAc ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } +#endif // DECODE_AMCOR +#if DECODE_ARGO + case decode_type_t::ARGO: { + IRArgoAC ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } +#endif // DECODE_ARGO +#if DECODE_COOLIX + case decode_type_t::COOLIX: { + IRCoolixAC ac(kGpioUnused); + ac.setRaw(decode->value); // Uses value instead of state. + *result = ac.toCommon(prev); + break; + } +#endif // DECODE_COOLIX +#if DECODE_CORONA_AC + case decode_type_t::CORONA_AC: { + IRCoronaAc ac(kGpioUnused); + ac.setRaw(decode->state, decode->bits / 8); + *result = ac.toCommon(); + break; + } +#endif // DECODE_CARRIER_AC64 +#if DECODE_CARRIER_AC64 + case decode_type_t::CARRIER_AC64: { + IRCarrierAc64 ac(kGpioUnused); + ac.setRaw(decode->value); // Uses value instead of state. + *result = ac.toCommon(); + break; + } +#endif // DECODE_CARRIER_AC64 +#if DECODE_DAIKIN + case decode_type_t::DAIKIN: { + IRDaikinESP ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } +#endif // DECODE_DAIKIN +#if DECODE_DAIKIN128 + case decode_type_t::DAIKIN128: { + IRDaikin128 ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } +#endif // DECODE_DAIKIN128 +#if DECODE_DAIKIN152 + case decode_type_t::DAIKIN152: { + IRDaikin152 ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } +#endif // DECODE_DAIKIN152 +#if DECODE_DAIKIN160 + case decode_type_t::DAIKIN160: { + IRDaikin160 ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } +#endif // DECODE_DAIKIN160 +#if DECODE_DAIKIN176 + case decode_type_t::DAIKIN176: { + IRDaikin176 ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } +#endif // DECODE_DAIKIN160 +#if DECODE_DAIKIN2 + case decode_type_t::DAIKIN2: { + IRDaikin2 ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } +#endif // DECODE_DAIKIN2 +#if DECODE_DAIKIN216 + case decode_type_t::DAIKIN216: { + IRDaikin216 ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } +#endif // DECODE_DAIKIN216 +#if DECODE_DAIKIN64 + case decode_type_t::DAIKIN64: { + IRDaikin64 ac(kGpioUnused); + ac.setRaw(decode->value); // Uses value instead of state. + *result = ac.toCommon(prev); + break; + } +#endif // DECODE_DAIKIN64 +#if DECODE_DELONGHI_AC + case decode_type_t::DELONGHI_AC: { + IRDelonghiAc ac(kGpioUnused); + ac.setRaw(decode->value); // Uses value instead of state. + *result = ac.toCommon(); + break; + } +#endif // DECODE_DELONGHI_AC +#if DECODE_ECOCLIM + case decode_type_t::ECOCLIM: { + if (decode->bits == kEcoclimBits) { + IREcoclimAc ac(kGpioUnused); + ac.setRaw(decode->value); // Uses value instead of state. + *result = ac.toCommon(); + } else { + return false; + } + break; + } +#endif // DECODE_ECOCLIM +#if DECODE_ELECTRA_AC + case decode_type_t::ELECTRA_AC: { + IRElectraAc ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } +#endif // DECODE_ELECTRA_AC +#if DECODE_FUJITSU_AC + case decode_type_t::FUJITSU_AC: { + IRFujitsuAC ac(kGpioUnused); + ac.setRaw(decode->state, decode->bits / 8); + *result = ac.toCommon(); + break; + } +#endif // DECODE_FUJITSU_AC +#if DECODE_GOODWEATHER + case decode_type_t::GOODWEATHER: { + IRGoodweatherAc ac(kGpioUnused); + ac.setRaw(decode->value); // Uses value instead of state. + *result = ac.toCommon(); + break; + } +#endif // DECODE_GOODWEATHER +#if DECODE_GREE + case decode_type_t::GREE: { + IRGreeAC ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } +#endif // DECODE_GREE +#if DECODE_HAIER_AC + case decode_type_t::HAIER_AC: { + IRHaierAC ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } +#endif // DECODE_HAIER_AC +#if DECODE_HAIER_AC176 + case decode_type_t::HAIER_AC176: { + IRHaierAC176 ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } +#endif // DECODE_HAIER_AC176 +#if DECODE_HAIER_AC_YRW02 + case decode_type_t::HAIER_AC_YRW02: { + IRHaierACYRW02 ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } +#endif // DECODE_HAIER_AC_YRW02 +#if (DECODE_HITACHI_AC || DECODE_HITACHI_AC2) + case decode_type_t::HITACHI_AC: { + IRHitachiAc ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } +#endif // (DECODE_HITACHI_AC || DECODE_HITACHI_AC2) +#if DECODE_HITACHI_AC1 + case decode_type_t::HITACHI_AC1: { + IRHitachiAc1 ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } +#endif // DECODE_HITACHI_AC1 +#if DECODE_HITACHI_AC344 + case decode_type_t::HITACHI_AC344: { + IRHitachiAc344 ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } +#endif // DECODE_HITACHI_AC344 +#if DECODE_HITACHI_AC424 + case decode_type_t::HITACHI_AC424: { + IRHitachiAc424 ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } +#endif // DECODE_HITACHI_AC424 +#if DECODE_KELON + case decode_type_t::KELON: { + IRKelonAc ac(kGpioUnused); + ac.setRaw(decode->value); + *result = ac.toCommon(); + break; + } +#endif // DECODE_KELON +#if DECODE_KELVINATOR + case decode_type_t::KELVINATOR: { + IRKelvinatorAC ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } +#endif // DECODE_KELVINATOR +#if DECODE_LG + case decode_type_t::LG: + case decode_type_t::LG2: { + IRLgAc ac(kGpioUnused); + ac.setRaw(decode->value, decode->decode_type); // Use value, not state. + if (!ac.isValidLgAc()) return false; + *result = ac.toCommon(prev); + break; + } +#endif // DECODE_LG +#if DECODE_MIDEA + case decode_type_t::MIDEA: { + IRMideaAC ac(kGpioUnused); + ac.setRaw(decode->value); // Uses value instead of state. + *result = ac.toCommon(prev); + break; + } +#endif // DECODE_MIDEA +#if DECODE_MITSUBISHI_AC + case decode_type_t::MITSUBISHI_AC: { + IRMitsubishiAC ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } +#endif // DECODE_MITSUBISHI_AC +#if DECODE_MITSUBISHI112 + case decode_type_t::MITSUBISHI112: { + IRMitsubishi112 ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } +#endif // DECODE_MITSUBISHI112 +#if DECODE_MITSUBISHI136 + case decode_type_t::MITSUBISHI136: { + IRMitsubishi136 ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } +#endif // DECODE_MITSUBISHI136 +#if DECODE_MITSUBISHIHEAVY + case decode_type_t::MITSUBISHI_HEAVY_88: { + IRMitsubishiHeavy88Ac ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } + case decode_type_t::MITSUBISHI_HEAVY_152: { + IRMitsubishiHeavy152Ac ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } +#endif // DECODE_MITSUBISHIHEAVY +#if DECODE_NEOCLIMA + case decode_type_t::NEOCLIMA: { + IRNeoclimaAc ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } +#endif // DECODE_NEOCLIMA +#if DECODE_PANASONIC_AC + case decode_type_t::PANASONIC_AC: { + IRPanasonicAc ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } +#endif // DECODE_PANASONIC_AC +#if DECODE_PANASONIC_AC32 + case decode_type_t::PANASONIC_AC32: { + IRPanasonicAc32 ac(kGpioUnused); + if (decode->bits >= kPanasonicAc32Bits) { + ac.setRaw(decode->value); // Uses value instead of state. + *result = ac.toCommon(prev); + } else { + return false; + } + break; + } +#endif // DECODE_PANASONIC_AC32 +#if DECODE_RHOSS + case decode_type_t::RHOSS: { + IRRhossAc ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } +#endif // DECODE_RHOSS +#if DECODE_SAMSUNG_AC + case decode_type_t::SAMSUNG_AC: { + IRSamsungAc ac(kGpioUnused); + ac.setRaw(decode->state, decode->bits / 8); + *result = ac.toCommon(); + break; + } +#endif // DECODE_SAMSUNG_AC +#if DECODE_SANYO_AC + case decode_type_t::SANYO_AC: { + IRSanyoAc ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } +#endif // DECODE_SANYO_AC +#if DECODE_SANYO_AC88 + case decode_type_t::SANYO_AC88: { + IRSanyoAc88 ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } +#endif // DECODE_SANYO_AC88 +#if DECODE_SHARP_AC + case decode_type_t::SHARP_AC: { + IRSharpAc ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(prev); + break; + } +#endif // DECODE_SHARP_AC +#if (DECODE_TCL112AC || DECODE_TEKNOPOINT) + case decode_type_t::TCL112AC: + case decode_type_t::TEKNOPOINT: { + IRTcl112Ac ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(prev); + // Teknopoint uses the TCL protocol, but with a different model number. + // Just keep the original protocol type ... for now. + result->protocol = decode->decode_type; + break; + } +#endif // (DECODE_TCL112AC || DECODE_TEKNOPOINT) +#if DECODE_TECHNIBEL_AC + case decode_type_t::TECHNIBEL_AC: { + IRTechnibelAc ac(kGpioUnused); + ac.setRaw(decode->value); // Uses value instead of state. + *result = ac.toCommon(); + break; + } +#endif // DECODE_TECHNIBEL_AC +#if DECODE_TECO + case decode_type_t::TECO: { + IRTecoAc ac(kGpioUnused); + ac.setRaw(decode->value); // Uses value instead of state. + *result = ac.toCommon(); + break; + } +#endif // DECODE_TECO +#if DECODE_TOSHIBA_AC + case decode_type_t::TOSHIBA_AC: { + IRToshibaAC ac(kGpioUnused); + ac.setRaw(decode->state, decode->bits / 8); + *result = ac.toCommon(prev); + break; + } +#endif // DECODE_TOSHIBA_AC +#if DECODE_TROTEC + case decode_type_t::TROTEC: { + IRTrotecESP ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } +#endif // DECODE_TROTEC +#if DECODE_TROTEC_3550 + case decode_type_t::TROTEC_3550: { + IRTrotec3550 ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } +#endif // DECODE_TROTEC_3550 +#if DECODE_TRUMA + case decode_type_t::TRUMA: { + IRTrumaAc ac(kGpioUnused); + ac.setRaw(decode->value); // Uses value instead of state. + *result = ac.toCommon(); + break; + } +#endif // DECODE_TRUMA +#if DECODE_VESTEL_AC + case decode_type_t::VESTEL_AC: { + IRVestelAc ac(kGpioUnused); + ac.setRaw(decode->value); // Uses value instead of state. + *result = ac.toCommon(); + break; + } +#endif // DECODE_VESTEL_AC +#if DECODE_VOLTAS + case decode_type_t::VOLTAS: { + IRVoltas ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(prev); + break; + } +#endif // DECODE_VOLTAS +#if DECODE_WHIRLPOOL_AC + case decode_type_t::WHIRLPOOL_AC: { + IRWhirlpoolAc ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(prev); + break; + } +#endif // DECODE_WHIRLPOOL_AC +#if DECODE_TRANSCOLD + case decode_type_t::TRANSCOLD: { + IRTranscoldAc ac(kGpioUnused); + ac.setRaw(decode->value); // TRANSCOLD Uses value instead of state. + *result = ac.toCommon(prev); + break; + } +#endif // DECODE_TRANSCOLD + default: + return false; + } + return true; + } +} // namespace IRAcUtils diff --git a/lib/IRremoteESP8266/src/IRac.h b/lib/IRremoteESP8266/src/IRac.h index 8dcf491dc3..7ef3adf4ad 100644 --- a/lib/IRremoteESP8266/src/IRac.h +++ b/lib/IRremoteESP8266/src/IRac.h @@ -6,44 +6,44 @@ #ifndef UNIT_TEST #include #endif -#include "../src/IRremoteESP8266.h" -#include "../src/ir_Airwell.h" -#include "../src/ir_Amcor.h" -#include "../src/ir_Argo.h" -#include "../src/ir_Carrier.h" -#include "../src/ir_Coolix.h" -#include "../src/ir_Corona.h" -#include "../src/ir_Daikin.h" -#include "../src/ir_Delonghi.h" -#include "../src/ir_Fujitsu.h" -#include "../src/ir_Ecoclim.h" -#include "../src/ir_Electra.h" -#include "../src/ir_Goodweather.h" -#include "../src/ir_Gree.h" -#include "../src/ir_Haier.h" -#include "../src/ir_Hitachi.h" -#include "../src/ir_Kelon.h" -#include "../src/ir_Kelvinator.h" -#include "../src/ir_LG.h" -#include "../src/ir_Midea.h" -#include "../src/ir_Mitsubishi.h" -#include "../src/ir_MitsubishiHeavy.h" -#include "../src/ir_Neoclima.h" -#include "../src/ir_Panasonic.h" -#include "../src/ir_Rhoss.h" -#include "../src/ir_Samsung.h" -#include "../src/ir_Sanyo.h" -#include "../src/ir_Sharp.h" -#include "../src/ir_Tcl.h" -#include "../src/ir_Technibel.h" -#include "../src/ir_Teco.h" -#include "../src/ir_Toshiba.h" -#include "../src/ir_Transcold.h" -#include "../src/ir_Trotec.h" -#include "../src/ir_Truma.h" -#include "../src/ir_Vestel.h" -#include "../src/ir_Voltas.h" -#include "../src/ir_Whirlpool.h" +#include "IRremoteESP8266.h" +#include "ir_Airwell.h" +#include "ir_Amcor.h" +#include "ir_Argo.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_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" // Constants const int8_t kGpioUnused = -1; ///< A placeholder for not using an actual GPIO. diff --git a/lib/IRremoteESP8266/src/IRrecv.cpp b/lib/IRremoteESP8266/src/IRrecv.cpp new file mode 100644 index 0000000000..5dc585bc75 --- /dev/null +++ b/lib/IRremoteESP8266/src/IRrecv.cpp @@ -0,0 +1,1937 @@ +// Copyright 2009 Ken Shirriff +// Copyright 2015 Mark Szabo +// Copyright 2015 Sebastien Warin +// Copyright 2017, 2019 David Conran + +#include "IRrecv.h" +#include +#ifndef UNIT_TEST +#if defined(ESP8266) +extern "C" { +#include +#include +} +#endif // ESP8266 +#include +#endif +#include +#ifdef UNIT_TEST +#include +#endif // UNIT_TEST +#include "IRremoteESP8266.h" +#include "IRutils.h" + +#ifdef UNIT_TEST +#undef ICACHE_RAM_ATTR +#define ICACHE_RAM_ATTR +#endif + +#ifndef USE_IRAM_ATTR +#if defined(ESP8266) +#if defined(IRAM_ATTR) +#define USE_IRAM_ATTR IRAM_ATTR +#else // IRAM_ATTR +#define USE_IRAM_ATTR ICACHE_RAM_ATTR +#endif // IRAM_ATTR +#endif // ESP8266 +#if defined(ESP32) +#define USE_IRAM_ATTR IRAM_ATTR +#endif // ESP32 +#endif // USE_IRAM_ATTR + +#define ONCE 0 + +// Updated by David Conran (https://github.com/crankyoldgit) for receiving IR +// code on ESP32 +// Updated by Sebastien Warin (http://sebastien.warin.fr) for receiving IR code +// on ESP8266 +// Updated by markszabo (https://github.com/crankyoldgit/IRremoteESP8266) for +// sending IR code on ESP8266 + +// Globals +#ifndef UNIT_TEST +#if defined(ESP8266) +namespace _IRrecv { +static ETSTimer timer; +} // namespace _IRrecv +#endif // ESP8266 +#if defined(ESP32) +// Required structs/types from: +// https://github.com/espressif/arduino-esp32/blob/6b0114366baf986c155e8173ab7c22bc0c5fcedc/cores/esp32/esp32-hal-timer.c#L28-L58 +// These are needed to be able to directly manipulate the timer registers from +// inside an ISR. This is very very ugly. +// Ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/1350 +// Note: This will need to be updated if it ever changes. +// +// Start of Horrible Hack! +typedef struct { + union { + struct { + uint32_t reserved0: 10; + uint32_t alarm_en: 1; + /*When set alarm is enabled*/ + uint32_t level_int_en: 1; + /*When set level type interrupt will be generated during alarm*/ + uint32_t edge_int_en: 1; + /*When set edge type interrupt will be generated during alarm*/ + uint32_t divider: 16; + /*Timer clock (T0/1_clk) pre-scale value.*/ + uint32_t autoreload: 1; + /*When set timer 0/1 auto-reload at alarming is enabled*/ + uint32_t increase: 1; + /*When set timer 0/1 time-base counter increment. + When cleared timer 0 time-base counter decrement.*/ + uint32_t enable: 1; + /*When set timer 0/1 time-base counter is enabled*/ + }; + uint32_t val; + } config; + uint32_t cnt_low; + /*Register to store timer 0/1 time-base counter current value lower 32 + bits.*/ + uint32_t cnt_high; + /*Register to store timer 0 time-base counter current value higher 32 + bits.*/ + uint32_t update; + /*Write any value will trigger a timer 0 time-base counter value update + (timer 0 current value will be stored in registers above)*/ + uint32_t alarm_low; + /*Timer 0 time-base counter value lower 32 bits that will trigger the + alarm*/ + uint32_t alarm_high; + /*Timer 0 time-base counter value higher 32 bits that will trigger the + alarm*/ + uint32_t load_low; + /*Lower 32 bits of the value that will load into timer 0 time-base counter*/ + uint32_t load_high; + /*higher 32 bits of the value that will load into timer 0 time-base + counter*/ + uint32_t reload; + /*Write any value will trigger timer 0 time-base counter reload*/ +} hw_timer_reg_t; + +typedef struct hw_timer_s { + hw_timer_reg_t * dev; + uint8_t num; + uint8_t group; + uint8_t timer; + portMUX_TYPE lock; +} hw_timer_t; +// End of Horrible Hack. + +namespace _IRrecv { +static hw_timer_t * timer = NULL; +} // namespace _IRrecv +#endif // ESP32 +using _IRrecv::timer; +#endif // UNIT_TEST + +namespace _IRrecv { // Namespace extension +#if defined(ESP32) +portMUX_TYPE mux = portMUX_INITIALIZER_UNLOCKED; +#endif // ESP32 +volatile irparams_t params; +irparams_t *params_save; // A copy of the interrupt state while decoding. +} // namespace _IRrecv + +#if defined(ESP32) +using _IRrecv::mux; +#endif // ESP32 +using _IRrecv::params; +using _IRrecv::params_save; + +#ifndef UNIT_TEST +#if defined(ESP8266) +/// Interrupt handler for when the timer runs out. +/// It signals to the library that capturing of IR data has stopped. +/// @param[in] arg Unused. (ESP8266 Only) +static void USE_IRAM_ATTR read_timeout(void *arg __attribute__((unused))) { + os_intr_lock(); +#endif // ESP8266 +/// @cond IGNORE +#if defined(ESP32) +/// Interrupt handler for when the timer runs out. +/// It signals to the library that capturing of IR data has stopped. +/// @note ESP32 version +static void USE_IRAM_ATTR read_timeout(void) { +/// @endcond + portENTER_CRITICAL(&mux); +#endif // ESP32 + if (params.rawlen) params.rcvstate = kStopState; +#if defined(ESP8266) + os_intr_unlock(); +#endif // ESP8266 +#if defined(ESP32) + portEXIT_CRITICAL(&mux); +#endif // ESP32 +} + +/// Interrupt handler for changes on the GPIO pin handling incoming IR messages. +static void USE_IRAM_ATTR gpio_intr() { + uint32_t now = micros(); + static uint32_t start = 0; + +#if defined(ESP8266) + uint32_t gpio_status = GPIO_REG_READ(GPIO_STATUS_ADDRESS); + os_timer_disarm(&timer); + GPIO_REG_WRITE(GPIO_STATUS_W1TC_ADDRESS, gpio_status); +#endif // ESP8266 + + // Grab a local copy of rawlen to reduce instructions used in IRAM. + // This is an ugly premature optimisation code-wise, but we do everything we + // can to save IRAM. + // It seems referencing the value via the structure uses more instructions. + // Less instructions means faster and less IRAM used. + // N.B. It saves about 13 bytes of IRAM. + uint16_t rawlen = params.rawlen; + + if (rawlen >= params.bufsize) { + params.overflow = true; + params.rcvstate = kStopState; + } + + if (params.rcvstate == kStopState) return; + + if (params.rcvstate == kIdleState) { + params.rcvstate = kMarkState; + params.rawbuf[rawlen] = 1; + } else { + if (now < start) + params.rawbuf[rawlen] = (UINT32_MAX - start + now) / kRawTick; + else + params.rawbuf[rawlen] = (now - start) / kRawTick; + } + params.rawlen++; + + start = now; + +#if defined(ESP8266) + os_timer_arm(&timer, params.timeout, ONCE); +#endif // ESP8266 +#if defined(ESP32) + // Reset the timeout. + // + // The following three lines of code are the equiv of: + // `timerWrite(timer, 0);` + // We can't call that routine safely from inside an ISR as that procedure + // is not stored in IRAM. Hence, we do it manually so that it's covered by + // USE_IRAM_ATTR in this ISR. + // @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1350 + // @see https://github.com/espressif/arduino-esp32/blob/6b0114366baf986c155e8173ab7c22bc0c5fcedc/cores/esp32/esp32-hal-timer.c#L106-L110 + timer->dev->load_high = (uint32_t) 0; + timer->dev->load_low = (uint32_t) 0; + timer->dev->reload = 1; + // The next line is the same, but instead replaces: + // `timerAlarmEnable(timer);` + // @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1350 + // @see https://github.com/espressif/arduino-esp32/blob/6b0114366baf986c155e8173ab7c22bc0c5fcedc/cores/esp32/esp32-hal-timer.c#L176-L178 + timer->dev->config.alarm_en = 1; +#endif // ESP32 +} +#endif // UNIT_TEST + +// Start of IRrecv class ------------------- + +/// Class constructor +/// Args: +/// @param[in] recvpin The GPIO pin the IR receiver module's data pin is +/// connected to. +/// @param[in] bufsize Nr. of entries to have in the capture buffer. +/// (Default: kRawBuf) +/// @param[in] timeout Nr. of milli-Seconds of no signal before we stop +/// capturing data. (Default: kTimeoutMs) +/// @param[in] save_buffer Use a second (save) buffer to decode from. +/// (Default: false) +/// @param[in] timer_num Nr. of the ESP32 timer to use (0 to 3) (ESP32 Only) +#if defined(ESP32) +IRrecv::IRrecv(const uint16_t recvpin, const uint16_t bufsize, + const uint8_t timeout, const bool save_buffer, + const uint8_t timer_num) { + // There are only 4 timers. 0 to 3. + _timer_num = std::min(timer_num, (uint8_t)3); +#else // ESP32 +/// @cond IGNORE +/// Class constructor +/// Args: +/// @param[in] recvpin The GPIO pin the IR receiver module's data pin is +/// connected to. +/// @param[in] bufsize Nr. of entries to have in the capture buffer. +/// (Default: kRawBuf) +/// @param[in] timeout Nr. of milli-Seconds of no signal before we stop +/// capturing data. (Default: kTimeoutMs) +/// @param[in] save_buffer Use a second (save) buffer to decode from. +/// (Default: false) +IRrecv::IRrecv(const uint16_t recvpin, const uint16_t bufsize, + const uint8_t timeout, const bool save_buffer) { +/// @endcond +#endif // ESP32 + params.recvpin = recvpin; + params.bufsize = bufsize; + // Ensure we are going to be able to store all possible values in the + // capture buffer. + params.timeout = std::min(timeout, (uint8_t)kMaxTimeoutMs); + params.rawbuf = new uint16_t[bufsize]; + if (params.rawbuf == NULL) { + DPRINTLN( + "Could not allocate memory for the primary IR buffer.\n" + "Try a smaller size for CAPTURE_BUFFER_SIZE.\nRebooting!"); +#ifndef UNIT_TEST + ESP.restart(); // Mem alloc failure. Reboot. +#endif + } + // If we have been asked to use a save buffer (for decoding), then create one. + if (save_buffer) { + params_save = new irparams_t; + params_save->rawbuf = new uint16_t[bufsize]; + // Check we allocated the memory successfully. + if (params_save->rawbuf == NULL) { + DPRINTLN( + "Could not allocate memory for the second IR buffer.\n" + "Try a smaller size for CAPTURE_BUFFER_SIZE.\nRebooting!"); +#ifndef UNIT_TEST + ESP.restart(); // Mem alloc failure. Reboot. +#endif + } + } else { + params_save = NULL; + } +#if DECODE_HASH + _unknown_threshold = kUnknownThreshold; +#endif // DECODE_HASH + _tolerance = kTolerance; +} + +/// Class destructor +/// Cleans up after the object is no longer needed. +/// e.g. Frees up all memory used by the various buffers, and disables any +/// timers or interrupts used. +IRrecv::~IRrecv(void) { + disableIRIn(); +#if defined(ESP32) + if (timer != NULL) timerEnd(timer); // Cleanup the ESP32 timeout timer. +#endif // ESP32 + delete[] params.rawbuf; + if (params_save != NULL) { + delete[] params_save->rawbuf; + delete params_save; + } +} + +/// Set up and (re)start the IR capture mechanism. +/// @param[in] pullup A flag indicating should the GPIO use the internal pullup +/// resistor. (Default: `false`. i.e. No.) +void IRrecv::enableIRIn(const bool pullup) { + // ESP32's seem to require explicitly setting the GPIO to INPUT etc. + // This wasn't required on the ESP8266s, but it shouldn't hurt to make sure. + if (pullup) { +#ifndef UNIT_TEST + pinMode(params.recvpin, INPUT_PULLUP); + } else { + pinMode(params.recvpin, INPUT); +#endif // UNIT_TEST + } +#if defined(ESP32) + // Initialise the ESP32 timer. + // 80MHz / 80 = 1 uSec granularity. + timer = timerBegin(_timer_num, 80, true); + // Set the timer so it only fires once, and set it's trigger in uSeconds. + timerAlarmWrite(timer, MS_TO_USEC(params.timeout), ONCE); + // Note: Interrupt needs to be attached before it can be enabled or disabled. + timerAttachInterrupt(timer, &read_timeout, true); +#endif // ESP32 + + // Initialise state machine variables + resume(); + +#ifndef UNIT_TEST +#if defined(ESP8266) + // Initialise ESP8266 timer. + os_timer_disarm(&timer); + os_timer_setfn(&timer, reinterpret_cast(read_timeout), + NULL); +#endif // ESP8266 + // Attach Interrupt + attachInterrupt(params.recvpin, gpio_intr, CHANGE); +#endif // UNIT_TEST +} + +/// Stop collection of any received IR data. +/// Disable any timers and interrupts. +void IRrecv::disableIRIn(void) { +#ifndef UNIT_TEST +#if defined(ESP8266) + os_timer_disarm(&timer); +#endif // ESP8266 +#if defined(ESP32) + timerAlarmDisable(timer); + timerEnd(timer); +#endif // ESP32 + detachInterrupt(params.recvpin); +#endif // UNIT_TEST +} + +/// Resume collection of received IR data. +/// @note This is required if `decode()` is successful and `save_buffer` was +/// not set when the class was instanciated. +/// @see IRrecv class constructor +void IRrecv::resume(void) { + params.rcvstate = kIdleState; + params.rawlen = 0; + params.overflow = false; +#if defined(ESP32) + timerAlarmDisable(timer); +#endif // ESP32 +} + +/// Make a copy of the interrupt state & buffer data. +/// Needed because irparams is marked as volatile, thus memcpy() isn't allowed. +/// Only call this when you know the interrupt handlers won't modify anything. +/// i.e. In kStopState. +/// @param[in] src Pointer to an irparams_t structure to copy from. +/// @param[out] dst Pointer to an irparams_t structure to copy to. +void IRrecv::copyIrParams(volatile irparams_t *src, irparams_t *dst) { + // Typecast src and dst addresses to (char *) + char *csrc = (char *)src; // NOLINT(readability/casting) + char *cdst = (char *)dst; // NOLINT(readability/casting) + + // Save the pointer to the destination's rawbuf so we don't lose it as + // the for-loop/copy after this will overwrite it with src's rawbuf pointer. + // This isn't immediately obvious due to typecasting/different variable names. + uint16_t *dst_rawbuf_ptr; + dst_rawbuf_ptr = dst->rawbuf; + + // Copy contents of src[] to dst[] + for (uint16_t i = 0; i < sizeof(irparams_t); i++) cdst[i] = csrc[i]; + + // Restore the buffer pointer + dst->rawbuf = dst_rawbuf_ptr; + + // Copy the rawbuf + for (uint16_t i = 0; i < dst->bufsize; i++) dst->rawbuf[i] = src->rawbuf[i]; +} + +/// Obtain the maximum number of entries possible in the capture buffer. +/// i.e. It's size. +/// @return The size of the buffer that is in use by the object. +uint16_t IRrecv::getBufSize(void) { return params.bufsize; } + +#if DECODE_HASH +/// Set the minimum length we will consider for reporting UNKNOWN message types. +/// @param[in] length Min nr. of mark/space pulses required to be considered. +void IRrecv::setUnknownThreshold(const uint16_t length) { + _unknown_threshold = length; +} +#endif // DECODE_HASH + + +/// Set the base tolerance percentage for matching incoming IR messages. +/// @param[in] percent An integer percentage. (0-100) +void IRrecv::setTolerance(const uint8_t percent) { + _tolerance = std::min(percent, (uint8_t)100); +} + +/// Get the base tolerance percentage for matching incoming IR messages. +/// @return A integer percentage. +uint8_t IRrecv::getTolerance(void) { return _tolerance; } + +#if ENABLE_NOISE_FILTER_OPTION +/// Remove or merge pulses in the capture buffer that are too short. +/// @param[in,out] results Ptr to the decode_results we are going to filter. +/// @param[in] floor Only allow values in the buffer large than this. +/// (in microSeconds) +void IRrecv::crudeNoiseFilter(decode_results *results, const uint16_t floor) { + if (floor == 0) return; // Nothing to do. + const uint16_t kTickFloor = floor / kRawTick; + const uint16_t kBufSize = getBufSize(); + uint16_t offset = kStartOffset; + while (offset < results->rawlen && offset + 2 < kBufSize) { + uint16_t curr = results->rawbuf[offset]; + uint16_t next = results->rawbuf[offset + 1]; + uint16_t addition = curr + next; + if (curr < kTickFloor) { // Is it too short? + // Shuffle the buffer down. i.e. Remove the mark & space pair. + // Note: `memcpy()` can't be used as rawbuf is `volatile`. + for (uint16_t i = offset + 2; i <= results->rawlen && i < kBufSize; i++) + results->rawbuf[i - 2] = results->rawbuf[i]; + if (offset > 1) { // There is a previous pair we can add to. + // Merge this pair into into the previous space. + results->rawbuf[offset - 1] += addition; + } + results->rawlen -= 2; // Adjust the length. + } else { + offset++; // Move along. + } + } +} +#endif // ENABLE_NOISE_FILTER_OPTION + +/// Decodes the received IR message. +/// If the interrupt state is saved, we will immediately resume waiting +/// for the next IR message to avoid missing messages. +/// @note There is a trade-off here. Saving the state means less time lost until +/// we can receiving the next message vs. using more RAM. Choose appropriately. +/// @param[out] results A PTR to where the decoded IR message will be stored. +/// @param[out] save A PTR to an irparams_t instance in which to save +/// the interrupt's memory/state. NULL means don't save it. +/// @param[in] max_skip Maximum Nr. of pulses at the begining of a capture we +/// can skip when attempting to find a protocol we can successfully decode. +/// This parameter can dramatically improve detection of protocols +/// when there is light IR interference just before an incoming IR +/// message, however, it comes at a steep performace price. +/// (Default is 0. No skipping.) +/// @warning Increasing the `max_skip` value will dramatically (linearly) +/// increase the cpu time & usage to decode protocols. +/// e.g. 0 -> 1 will be a 2x increase in cpu usage/time. +/// 0 -> 2 will be a 3x increase etc. +/// If you are going to do this, consider disabling protocol decoding for +/// protocols you are not expecting. +/// @param[in] noise_floor Pulses below this size (in usecs) will be removed or +/// merged prior to any decoding. This is to try to remove noise/poor +/// readings & slightly increase the chances of a successful decode but at the +/// cost of data fidelity & integrity. +/// (Defaults to 0 usecs. i.e. Don't filter; which is safe!) +/// @warning DANGER: **Here Be Dragons!** +/// If you set the `noise_floor` value too high, it **WILL** break decoding +/// of some protocols. You have been warned! +/// **Any** non-zero value has the potential to **cook** the captured raw data +/// i.e. The raw data is going to lie to you. +/// It may obscure hardware, circuit, & environment issues thus making it +/// impossible to support you accurately or confidently. +/// Values of <= 50 usecs will probably be safe. +/// 51 - 100 usecs **might** be okay. +/// 100 - 150 usecs is "Danger, Will Robinson!". +/// 150 - 200 usecs expect broken protocols. +/// At 200+ usecs, you **have** protocols you can't decode!! +/// @return A boolean indicating if an IR message is ready or not. +bool IRrecv::decode(decode_results *results, irparams_t *save, + uint8_t max_skip, uint16_t noise_floor) { + // Proceed only if an IR message been received. +#ifndef UNIT_TEST + if (params.rcvstate != kStopState) return false; +#endif + + // Clear the entry we are currently pointing to when we got the timeout. + // i.e. Stopped collecting IR data. + // It's junk as we never wrote an entry to it and can only confuse decoding. + // This is done here rather than logically the best place in read_timeout() + // as it saves a few bytes of ICACHE_RAM as that routine is bound to an + // interrupt. decode() is not stored in ICACHE_RAM. + // Another better option would be to zero the entire irparams.rawbuf[] on + // resume() but that is a much more expensive operation compare to this. + // However, don't do this if rawbuf is already full as we stomp over the heap. + // See: https://github.com/crankyoldgit/IRremoteESP8266/issues/1516 + if (!params.overflow) params.rawbuf[params.rawlen] = 0; + + bool resumed = false; // Flag indicating if we have resumed. + + // If we were requested to use a save buffer previously, do so. + if (save == NULL) save = params_save; + + if (save == NULL) { + // We haven't been asked to copy it so use the existing memory. +#ifndef UNIT_TEST + results->rawbuf = params.rawbuf; + results->rawlen = params.rawlen; + results->overflow = params.overflow; +#endif + } else { + copyIrParams(¶ms, save); // Duplicate the interrupt's memory. + resume(); // It's now safe to rearm. The IR message won't be overridden. + resumed = true; + // Point the results at the saved copy. + results->rawbuf = save->rawbuf; + results->rawlen = save->rawlen; + results->overflow = save->overflow; + } + + // Reset any previously partially processed results. + results->decode_type = UNKNOWN; + results->bits = 0; + results->value = 0; + results->address = 0; + results->command = 0; + results->repeat = false; + +#if ENABLE_NOISE_FILTER_OPTION + crudeNoiseFilter(results, noise_floor); +#endif // ENABLE_NOISE_FILTER_OPTION + // Keep looking for protocols until we've run out of entries to skip or we + // find a valid protocol message. + for (uint16_t offset = kStartOffset; + offset <= (max_skip * 2) + kStartOffset; + offset += 2) { +#if DECODE_AIWA_RC_T501 + DPRINTLN("Attempting Aiwa RC T501 decode"); + // Try decodeAiwaRCT501() before decodeSanyoLC7461() & decodeNEC() + // because the protocols are similar. This protocol is more specific than + // those ones, so should go before them. + if (decodeAiwaRCT501(results, offset)) return true; +#endif +#if DECODE_SANYO + DPRINTLN("Attempting Sanyo LC7461 decode"); + // Try decodeSanyoLC7461() before decodeNEC() because the protocols are + // similar in timings & structure, but the Sanyo one is much longer than the + // NEC protocol (42 vs 32 bits) so this one should be tried first to try to + // reduce false detection as a NEC packet. + if (decodeSanyoLC7461(results, offset)) return true; +#endif +#if DECODE_CARRIER_AC + DPRINTLN("Attempting Carrier AC decode"); + // Try decodeCarrierAC() before decodeNEC() because the protocols are + // similar in timings & structure, but the Carrier one is much longer than + // the NEC protocol (3x32 bits vs 1x32 bits) so this one should be tried + // first to try to reduce false detection as a NEC packet. + if (decodeCarrierAC(results, offset)) return true; +#endif +#if DECODE_PIONEER + DPRINTLN("Attempting Pioneer decode"); + // Try decodePioneer() before decodeNEC() because the protocols are + // similar in timings & structure, but the Pioneer one is much longer than + // the NEC protocol (2x32 bits vs 1x32 bits) so this one should be tried + // first to try to reduce false detection as a NEC packet. + if (decodePioneer(results, offset)) return true; +#endif +#if DECODE_EPSON + DPRINTLN("Attempting Epson decode"); + // Try decodeEpson() before decodeNEC() because the protocols are + // similar in timings & structure, but the Epson one is much longer than the + // NEC protocol (3x32 identical bits vs 1x32 bits) so this one should be tried + // first to try to reduce false detection as a NEC packet. + if (decodeEpson(results, offset)) return true; +#endif +#if DECODE_NEC + DPRINTLN("Attempting NEC decode"); + if (decodeNEC(results, offset)) return true; +#endif +#if DECODE_MILESTAG2 + DPRINTLN("Attempting MilesTag2 decode"); + // Try decodeMilestag2() before decodeSony() because the protocols are + // similar in timings & structure, but the Miles one differs in nbits + // so this one should be tried first to try to reduce false detection + if (decodeMilestag2(results, offset, kMilesTag2MsgBits) || + decodeMilestag2(results, offset, kMilesTag2ShotBits)) return true; +#endif +#if DECODE_SONY + DPRINTLN("Attempting Sony decode"); + if (decodeSony(results, offset)) return true; +#endif +#if DECODE_MITSUBISHI + DPRINTLN("Attempting Mitsubishi decode"); + if (decodeMitsubishi(results, offset)) return true; +#endif +#if DECODE_MITSUBISHI_AC + DPRINTLN("Attempting Mitsubishi AC decode"); + if (decodeMitsubishiAC(results, offset)) return true; +#endif +#if DECODE_MITSUBISHI2 + DPRINTLN("Attempting Mitsubishi2 decode"); + if (decodeMitsubishi2(results, offset)) return true; +#endif +#if DECODE_RC5 + DPRINTLN("Attempting RC5 decode"); + if (decodeRC5(results, offset)) return true; +#endif +#if DECODE_RC6 + DPRINTLN("Attempting RC6 decode"); + if (decodeRC6(results, offset)) return true; +#endif +#if DECODE_RCMM + DPRINTLN("Attempting RC-MM decode"); + if (decodeRCMM(results, offset)) return true; +#endif +#if DECODE_FUJITSU_AC + // Fujitsu A/C needs to precede Panasonic and Denon as it has a short + // message which looks exactly the same as a Panasonic/Denon message. + DPRINTLN("Attempting Fujitsu A/C decode"); + if (decodeFujitsuAC(results, offset)) return true; +#endif +#if DECODE_DENON + // Denon needs to precede Panasonic as it is a special case of Panasonic. + DPRINTLN("Attempting Denon decode"); + if (decodeDenon(results, offset, kDenon48Bits) || + decodeDenon(results, offset, kDenonBits) || + decodeDenon(results, offset, kDenonLegacyBits)) + return true; +#endif +#if DECODE_PANASONIC + DPRINTLN("Attempting Panasonic decode"); + if (decodePanasonic(results, offset)) return true; +#endif +#if DECODE_LG + DPRINTLN("Attempting LG (28-bit) decode"); + if (decodeLG(results, offset, kLgBits, true)) return true; + DPRINTLN("Attempting LG (32-bit) decode"); + // LG32 should be tried before Samsung + if (decodeLG(results, offset, kLg32Bits, true)) return true; +#endif +#if DECODE_GICABLE + // Note: Needs to happen before JVC decode, because it looks similar except + // with a required NEC-like repeat code. + DPRINTLN("Attempting GICable decode"); + if (decodeGICable(results, offset)) return true; +#endif +#if DECODE_JVC + DPRINTLN("Attempting JVC decode"); + if (decodeJVC(results, offset)) return true; +#endif +#if DECODE_SAMSUNG + DPRINTLN("Attempting SAMSUNG decode"); + if (decodeSAMSUNG(results, offset)) return true; +#endif +#if DECODE_SAMSUNG36 + DPRINTLN("Attempting Samsung36 decode"); + if (decodeSamsung36(results, offset)) return true; +#endif +#if DECODE_WHYNTER + DPRINTLN("Attempting Whynter decode"); + if (decodeWhynter(results, offset)) return true; +#endif +#if DECODE_DISH + DPRINTLN("Attempting DISH decode"); + if (decodeDISH(results, offset)) return true; +#endif +#if DECODE_SHARP + DPRINTLN("Attempting Sharp decode"); + if (decodeSharp(results, offset)) return true; +#endif +#if DECODE_COOLIX + DPRINTLN("Attempting Coolix decode"); + if (decodeCOOLIX(results, offset)) return true; +#endif +#if DECODE_NIKAI + DPRINTLN("Attempting Nikai decode"); + if (decodeNikai(results, offset)) return true; +#endif +#if DECODE_KELVINATOR + // Kelvinator based-devices use a similar code to Gree ones, to avoid false + // matches this needs to happen before decodeGree(). + DPRINTLN("Attempting Kelvinator decode"); + if (decodeKelvinator(results, offset)) return true; +#endif +#if DECODE_DAIKIN + DPRINTLN("Attempting Daikin decode"); + if (decodeDaikin(results, offset)) return true; +#endif +#if DECODE_DAIKIN2 + DPRINTLN("Attempting Daikin2 decode"); + if (decodeDaikin2(results, offset)) return true; +#endif +#if DECODE_DAIKIN216 + DPRINTLN("Attempting Daikin216 decode"); + if (decodeDaikin216(results, offset)) return true; +#endif +#if DECODE_TOSHIBA_AC + DPRINTLN("Attempting Toshiba AC 72bit decode"); + if (decodeToshibaAC(results, offset)) return true; + DPRINTLN("Attempting Toshiba AC 80bit decode"); + if (decodeToshibaAC(results, offset, kToshibaACBitsLong)) return true; + DPRINTLN("Attempting Toshiba AC 56bit decode"); + if (decodeToshibaAC(results, offset, kToshibaACBitsShort)) return true; +#endif +#if DECODE_MIDEA + DPRINTLN("Attempting Midea decode"); + if (decodeMidea(results, offset)) return true; +#endif +#if DECODE_MAGIQUEST + DPRINTLN("Attempting Magiquest decode"); + if (decodeMagiQuest(results, offset)) return true; +#endif + /* NOTE: Disabled due to poor quality. +#if DECODE_SANYO + // The Sanyo S866500B decoder is very poor quality & depricated. + // *IF* you are going to enable it, do it near last to avoid false positive + // matches. + DPRINTLN("Attempting Sanyo SA8650B decode"); + if (decodeSanyo(results, offset)) + return true; +#endif + */ +#if DECODE_NEC + // Some devices send NEC-like codes that don't follow the true NEC spec. + // This should detect those. e.g. Apple TV remote etc. + // This needs to be done after all other codes that use strict and some + // other protocols that are NEC-like as well, as turning off strict may + // cause this to match other valid protocols. + DPRINTLN("Attempting NEC (non-strict) decode"); + if (decodeNEC(results, offset, kNECBits, false)) { + results->decode_type = NEC_LIKE; + return true; + } +#endif +#if DECODE_LASERTAG + DPRINTLN("Attempting Lasertag decode"); + if (decodeLasertag(results, offset)) return true; +#endif +#if DECODE_GREE + // Gree based-devices use a similar code to Kelvinator ones, to avoid false + // matches this needs to happen after decodeKelvinator(). + DPRINTLN("Attempting Gree decode"); + if (decodeGree(results, offset)) return true; +#endif +#if DECODE_HAIER_AC + DPRINTLN("Attempting Haier AC decode"); + if (decodeHaierAC(results, offset)) return true; +#endif +#if DECODE_HAIER_AC_YRW02 + DPRINTLN("Attempting Haier AC YR-W02 decode"); + if (decodeHaierACYRW02(results, offset)) return true; +#endif +#if DECODE_HAIER_AC176 + DPRINTLN("Attempting Haier AC 176 bit decode"); + if (decodeHaierAC176(results, offset)) return true; +#endif // DECODE_HAIER_AC176 +#if DECODE_HITACHI_AC424 + // HitachiAc424 should be checked before HitachiAC, HitachiAC2, + // & HitachiAC184 + DPRINTLN("Attempting Hitachi AC 424 decode"); + if (decodeHitachiAc424(results, offset, kHitachiAc424Bits)) return true; +#endif // DECODE_HITACHI_AC424 +#if DECODE_MITSUBISHI136 + // Needs to happen before HitachiAc3 decode. + DPRINTLN("Attempting Mitsubishi136 decode"); + if (decodeMitsubishi136(results, offset)) return true; +#endif // DECODE_MITSUBISHI136 +#if DECODE_HITACHI_AC3 + // HitachiAc3 should be checked before HitachiAC & HitachiAC2 + // Attempt normal before the short version. + DPRINTLN("Attempting Hitachi AC3 decode"); + // Order these in decreasing bit size, as it is more optimal. + if (decodeHitachiAc3(results, offset, kHitachiAc3Bits) || + decodeHitachiAc3(results, offset, kHitachiAc3Bits - 4 * 8) || + decodeHitachiAc3(results, offset, kHitachiAc3Bits - 6 * 8) || + decodeHitachiAc3(results, offset, kHitachiAc3MinBits + 2 * 8) || + decodeHitachiAc3(results, offset, kHitachiAc3MinBits)) + return true; +#endif // DECODE_HITACHI_AC3 +#if DECODE_HITACHI_AC344 + // HitachiAC344 should be checked before HitachiAC + DPRINTLN("Attempting Hitachi AC344 decode"); + if (decodeHitachiAC(results, offset, kHitachiAc344Bits, true, false)) + return true; +#endif // DECODE_HITACHI_AC344 +#if DECODE_HITACHI_AC2 + // HitachiAC2 should be checked before HitachiAC + DPRINTLN("Attempting Hitachi AC2 decode"); + if (decodeHitachiAC(results, offset, kHitachiAc2Bits)) return true; +#endif // DECODE_HITACHI_AC2 +#if DECODE_HITACHI_AC + DPRINTLN("Attempting Hitachi AC decode"); + if (decodeHitachiAC(results, offset, kHitachiAcBits)) return true; +#endif +#if DECODE_HITACHI_AC1 + DPRINTLN("Attempting Hitachi AC1 decode"); + if (decodeHitachiAC(results, offset, kHitachiAc1Bits)) return true; +#endif +#if DECODE_WHIRLPOOL_AC + DPRINTLN("Attempting Whirlpool AC decode"); + if (decodeWhirlpoolAC(results, offset)) return true; +#endif +#if DECODE_SAMSUNG_AC + DPRINTLN("Attempting Samsung AC (extended) decode"); + // Check the extended size first, as it should fail fast due to longer + // length. + if (decodeSamsungAC(results, offset, kSamsungAcExtendedBits, false)) + return true; + // Now check for the more common length. + DPRINTLN("Attempting Samsung AC decode"); + if (decodeSamsungAC(results, offset, kSamsungAcBits)) return true; +#endif +#if DECODE_ELECTRA_AC + DPRINTLN("Attempting Electra AC decode"); + if (decodeElectraAC(results, offset)) return true; +#endif +#if DECODE_PANASONIC_AC + DPRINTLN("Attempting Panasonic AC decode"); + if (decodePanasonicAC(results, offset)) return true; + DPRINTLN("Attempting Panasonic AC short decode"); + if (decodePanasonicAC(results, offset, kPanasonicAcShortBits)) return true; +#endif +#if DECODE_LUTRON + DPRINTLN("Attempting Lutron decode"); + if (decodeLutron(results, offset)) return true; +#endif +#if DECODE_MWM + DPRINTLN("Attempting MWM decode"); + if (decodeMWM(results, offset)) return true; +#endif +#if DECODE_VESTEL_AC + DPRINTLN("Attempting Vestel AC decode"); + if (decodeVestelAc(results, offset)) return true; +#endif +#if DECODE_MITSUBISHI112 || DECODE_TCL112AC + // Mitsubish112 and Tcl112 share the same decoder. + DPRINTLN("Attempting Mitsubishi112/TCL112AC decode"); + if (decodeMitsubishi112(results, offset)) return true; +#endif // DECODE_MITSUBISHI112 || DECODE_TCL112AC +#if DECODE_TECO + DPRINTLN("Attempting Teco decode"); + if (decodeTeco(results, offset)) return true; +#endif +#if DECODE_LEGOPF + DPRINTLN("Attempting LEGOPF decode"); + if (decodeLegoPf(results, offset)) return true; +#endif +#if DECODE_MITSUBISHIHEAVY + DPRINTLN("Attempting MITSUBISHIHEAVY (152 bit) decode"); + if (decodeMitsubishiHeavy(results, offset, kMitsubishiHeavy152Bits)) + return true; + DPRINTLN("Attempting MITSUBISHIHEAVY (88 bit) decode"); + if (decodeMitsubishiHeavy(results, offset, kMitsubishiHeavy88Bits)) + return true; +#endif +#if DECODE_ARGO + DPRINTLN("Attempting Argo decode"); + if (decodeArgo(results, offset)) return true; +#endif // DECODE_ARGO +#if DECODE_SHARP_AC + DPRINTLN("Attempting SHARP_AC decode"); + if (decodeSharpAc(results, offset)) return true; +#endif +#if DECODE_GOODWEATHER + DPRINTLN("Attempting GOODWEATHER decode"); + if (decodeGoodweather(results, offset)) return true; +#endif // DECODE_GOODWEATHER +#if DECODE_INAX + DPRINTLN("Attempting Inax decode"); + if (decodeInax(results, offset)) return true; +#endif // DECODE_INAX +#if DECODE_TROTEC + DPRINTLN("Attempting Trotec decode"); + if (decodeTrotec(results, offset)) return true; +#endif // DECODE_TROTEC +#if DECODE_TROTEC_3550 + DPRINTLN("Attempting Trotec 3550 decode"); + if (decodeTrotec3550(results, offset)) return true; +#endif // DECODE_TROTEC_3550 +#if DECODE_DAIKIN160 + DPRINTLN("Attempting Daikin160 decode"); + if (decodeDaikin160(results, offset)) return true; +#endif // DECODE_DAIKIN160 +#if DECODE_NEOCLIMA + DPRINTLN("Attempting Neoclima decode"); + if (decodeNeoclima(results, offset)) return true; +#endif // DECODE_NEOCLIMA +#if DECODE_DAIKIN176 + DPRINTLN("Attempting Daikin176 decode"); + if (decodeDaikin176(results, offset)) return true; +#endif // DECODE_DAIKIN176 +#if DECODE_DAIKIN128 + DPRINTLN("Attempting Daikin128 decode"); + if (decodeDaikin128(results, offset)) return true; +#endif // DECODE_DAIKIN128 +#if DECODE_AMCOR + DPRINTLN("Attempting Amcor decode"); + if (decodeAmcor(results, offset)) return true; +#endif // DECODE_AMCOR +#if DECODE_DAIKIN152 + DPRINTLN("Attempting Daikin152 decode"); + if (decodeDaikin152(results, offset)) return true; +#endif // DECODE_DAIKIN152 +#if DECODE_SYMPHONY + DPRINTLN("Attempting Symphony decode"); + if (decodeSymphony(results, offset)) return true; +#endif // DECODE_SYMPHONY +#if DECODE_DAIKIN64 + DPRINTLN("Attempting Daikin64 decode"); + if (decodeDaikin64(results, offset)) return true; +#endif // DECODE_DAIKIN64 +#if DECODE_AIRWELL + DPRINTLN("Attempting Airwell decode"); + if (decodeAirwell(results, offset)) return true; +#endif // DECODE_AIRWELL +#if DECODE_DELONGHI_AC + DPRINTLN("Attempting Delonghi AC decode"); + if (decodeDelonghiAc(results, offset)) return true; +#endif // DECODE_DELONGHI_AC +#if DECODE_DOSHISHA + DPRINTLN("Attempting Doshisha decode"); + if (decodeDoshisha(results, offset)) return true; +#endif // DECODE_DOSHISHA +#if DECODE_TRUMA + // Needs to happen before decodeMultibrackets() as they can appear similar. + DPRINTLN("Attempting Truma decode"); + if (decodeTruma(results, offset)) return true; +#endif // DECODE_TRUMA +#if DECODE_MULTIBRACKETS + DPRINTLN("Attempting Multibrackets decode"); + if (decodeMultibrackets(results, offset)) return true; +#endif // DECODE_MULTIBRACKETS +#if DECODE_CARRIER_AC40 + DPRINTLN("Attempting Carrier 40bit decode"); + if (decodeCarrierAC40(results, offset)) return true; +#endif // DECODE_CARRIER_AC40 +#if DECODE_CARRIER_AC64 + DPRINTLN("Attempting Carrier 64bit decode"); + if (decodeCarrierAC64(results, offset)) return true; +#endif // DECODE_CARRIER_AC64 +#if DECODE_TECHNIBEL_AC + DPRINTLN("Attempting Technibel AC decode"); + if (decodeTechnibelAc(results, offset)) return true; +#endif // DECODE_TECHNIBEL_AC +#if DECODE_CORONA_AC + DPRINTLN("Attempting CoronaAc decode"); + if (decodeCoronaAc(results, offset)) return true; +#endif // DECODE_CORONA_AC +#if DECODE_MIDEA24 + DPRINTLN("Attempting Midea-Nec decode"); + if (decodeMidea24(results, offset)) return true; +#endif // DECODE_MIDEA24 +#if DECODE_ZEPEAL + DPRINTLN("Attempting Zepeal decode"); + if (decodeZepeal(results, offset)) return true; +#endif // DECODE_ZEPEAL +#if DECODE_SANYO_AC + DPRINTLN("Attempting Sanyo AC decode"); + if (decodeSanyoAc(results, offset)) return true; +#endif // DECODE_SANYO_AC +#if DECODE_VOLTAS + DPRINTLN("Attempting Voltas decode"); + if (decodeVoltas(results)) return true; +#endif // DECODE_VOLTAS +#if DECODE_METZ + DPRINTLN("Attempting Metz decode"); + if (decodeMetz(results, offset)) return true; +#endif // DECODE_METZ +#if DECODE_TRANSCOLD + DPRINTLN("Attempting Transcold decode"); + if (decodeTranscold(results, offset)) return true; +#endif // DECODE_TRANSCOLD +#if DECODE_MIRAGE + DPRINTLN("Attempting Mirage decode"); + if (decodeMirage(results, offset)) return true; +#endif // DECODE_MIRAGE +#if DECODE_ELITESCREENS + DPRINTLN("Attempting EliteScreens decode"); + if (decodeElitescreens(results, offset)) return true; +#endif // DECODE_ELITESCREENS +#if DECODE_PANASONIC_AC32 + DPRINTLN("Attempting Panasonic AC (32bit) long decode"); + if (decodePanasonicAC32(results, offset, kPanasonicAc32Bits)) return true; + DPRINTLN("Attempting Panasonic AC (32bit) short decode"); + if (decodePanasonicAC32(results, offset, kPanasonicAc32Bits / 2)) + return true; +#endif // DECODE_PANASONIC_AC32 +#if DECODE_ECOCLIM + DPRINTLN("Attempting Ecoclim decode"); + if (decodeEcoclim(results, offset, kEcoclimBits) || + decodeEcoclim(results, offset, kEcoclimShortBits)) return true; +#endif // DECODE_ECOCLIM +#if DECODE_XMP + DPRINTLN("Attempting XMP decode"); + if (decodeXmp(results, offset, kXmpBits)) return true; +#endif // DECODE_XMP +#if DECODE_TEKNOPOINT + DPRINTLN("Attempting Teknopoint decode"); + if (decodeTeknopoint(results, offset)) return true; +#endif // DECODE_TEKNOPOINT +#if DECODE_KELON + DPRINTLN("Attempting Kelon decode"); + if (decodeKelon(results, offset)) return true; +#endif // DECODE_KELON +#if DECODE_SANYO_AC88 + DPRINTLN("Attempting SanyoAc88 decode"); + if (decodeSanyoAc88(results, offset)) return true; +#endif // DECODE_SANYO_AC88 +#if DECODE_BOSE + DPRINTLN("Attempting Bose decode"); + if (decodeBose(results, offset)) return true; +#endif // DECODE_BOSE +#if DECODE_ARRIS + DPRINTLN("Attempting Arris decode"); + if (decodeArris(results, offset)) return true; +#endif // DECODE_ARRIS +#if DECODE_RHOSS + DPRINTLN("Attempting Rhoss decode"); + if (decodeRhoss(results, offset)) return true; +#endif // DECODE_RHOSS + // Typically new protocols are added above this line. + } +#if DECODE_HASH + // decodeHash returns a hash on any input. + // Thus, it needs to be last in the list. + // If you add any decodes, add them before this. + if (decodeHash(results)) { + return true; + } +#endif // DECODE_HASH + // Throw away and start over + if (!resumed) // Check if we have already resumed. + resume(); + return false; +} + +/// Convert the tolerance percentage into something valid. +/// @param[in] percentage An integer percentage. +uint8_t IRrecv::_validTolerance(const uint8_t percentage) { + return (percentage > 100) ? _tolerance : percentage; +} + +/// Calculate the lower bound of the nr. of ticks. +/// @param[in] usecs Nr. of uSeconds. +/// @param[in] tolerance Percent as an integer. e.g. 10 is 10% +/// @param[in] delta A non-scaling amount to reduce usecs by. +/// @return Nr. of ticks. +uint32_t IRrecv::ticksLow(const uint32_t usecs, const uint8_t tolerance, + const uint16_t delta) { + // max() used to ensure the result can't drop below 0 before the cast. + return ((uint32_t)std::max( + (int32_t)(usecs * (1.0 - _validTolerance(tolerance) / 100.0) - delta), + 0)); +} + +/// Calculate the upper bound of the nr. of ticks. +/// @param[in] usecs Nr. of uSeconds. +/// @param[in] tolerance Percent as an integer. e.g. 10 is 10% +/// @param[in] delta A non-scaling amount to increase usecs by. +/// @return Nr. of ticks. +uint32_t IRrecv::ticksHigh(const uint32_t usecs, const uint8_t tolerance, + const uint16_t delta) { + return ((uint32_t)(usecs * (1.0 + _validTolerance(tolerance) / 100.0)) + 1 + + delta); +} + +/// Check if we match a pulse(measured) with the desired within +/// +/-tolerance percent and/or +/- a fixed delta range. +/// @param[in] measured The recorded period of the signal pulse. +/// @param[in] desired The expected period (in usecs) we are matching against. +/// @param[in] tolerance A percentage expressed as an integer. e.g. 10 is 10%. +/// @param[in] delta A non-scaling (+/-) error margin (in useconds). +/// @return A Boolean. true if it matches, false if it doesn't. +bool IRrecv::match(uint32_t measured, uint32_t desired, uint8_t tolerance, + uint16_t delta) { + measured *= kRawTick; // Convert to uSecs. + DPRINT("Matching: "); + DPRINT(ticksLow(desired, tolerance, delta)); + DPRINT(" <= "); + DPRINT(measured); + DPRINT(" <= "); + DPRINTLN(ticksHigh(desired, tolerance, delta)); +#ifdef UNIT_TEST + // Sanity checks that we don't have values that cause integer over/underflow. + // Only performed during testing so there is no performance hit in normal + // operation. + assert(ticksLow(desired, tolerance, delta) <= desired); + // Check if we overflowed. (UINT32_MAX >> 3 is approx 9 minutes!) + assert(ticksHigh(desired, tolerance, delta) < UINT32_MAX >> 3); + // Check if our high mark is below where we started. This could happen. + // If there is a legit case, then this should be removed. + assert(ticksHigh(desired, tolerance, delta) >= desired); +#endif // UNIT_TEST + return (measured >= ticksLow(desired, tolerance, delta) && + measured <= ticksHigh(desired, tolerance, delta)); +} + +/// Check if we match a pulse(measured) of at least desired within +/// tolerance percent and/or a fixed delta margin. +/// @param[in] measured The recorded period of the signal pulse. +/// @param[in] desired The expected period (in usecs) we are matching against. +/// @param[in] tolerance A percentage expressed as an integer. e.g. 10 is 10%. +/// @param[in] delta A non-scaling amount to reduce usecs by. +/// @return A Boolean. true if it matches, false if it doesn't. +bool IRrecv::matchAtLeast(uint32_t measured, uint32_t desired, + uint8_t tolerance, uint16_t delta) { + measured *= kRawTick; // Convert to uSecs. + DPRINT("Matching ATLEAST "); + DPRINT(measured); + DPRINT(" vs "); + DPRINT(desired); + DPRINT(". Matching: "); + DPRINT(measured); + DPRINT(" >= "); + DPRINT(ticksLow(std::min(desired, MS_TO_USEC(params.timeout)), tolerance, + delta)); + DPRINT(" [min("); + DPRINT(ticksLow(desired, tolerance, delta)); + DPRINT(", "); + DPRINT(ticksLow(MS_TO_USEC(params.timeout), tolerance, delta)); + DPRINTLN(")]"); +#ifdef UNIT_TEST + // Sanity checks that we don't have values that cause integer over/underflow. + // Only performed during testing so there is no performance hit in normal + // operation. + assert(ticksLow(desired, tolerance, delta) <= desired); + // Check if we overflowed. (UINT32_MAX >> 3 is approx 9 minutes!) + assert(ticksHigh(desired, tolerance, delta) < UINT32_MAX >> 3); + // Check if our high mark is below where we started. This could happen. + // If there is a legit case, then this should be removed. + assert(ticksHigh(desired, tolerance, delta) >= desired); +#endif // UNIT_TEST + // We really should never get a value of 0, except as the last value + // in the buffer. If that is the case, then assume infinity and return true. + if (measured == 0) return true; + return measured >= ticksLow(std::min(desired, MS_TO_USEC(params.timeout)), + tolerance, delta); +} + +/// Check if we match a mark signal(measured) with the desired within +/// +/-tolerance percent, after an expected is excess is added. +/// @param[in] measured The recorded period of the signal pulse. +/// @param[in] desired The expected period (in usecs) we are matching against. +/// @param[in] tolerance A percentage expressed as an integer. e.g. 10 is 10%. +/// @param[in] excess A non-scaling amount to reduce usecs by. +/// @return A Boolean. true if it matches, false if it doesn't. +bool IRrecv::matchMark(uint32_t measured, uint32_t desired, uint8_t tolerance, + int16_t excess) { + DPRINT("Matching MARK "); + DPRINT(measured * kRawTick); + DPRINT(" vs "); + DPRINT(desired); + DPRINT(" + "); + DPRINT(excess); + DPRINT(". "); + return match(measured, desired + excess, tolerance); +} + +/// Check if we match a mark signal(measured) with the desired within a +/// range (in uSeconds) either side of the desired, after an expected is excess +/// is added. +/// @param[in] measured The recorded period of the signal pulse. +/// @param[in] desired The expected period (in usecs) we are matching against. +/// @param[in] range The range limit from desired to accept in uSeconds. +/// @param[in] excess A non-scaling amount to reduce usecs by. +/// @return A Boolean. true if it matches, false if it doesn't. +bool IRrecv::matchMarkRange(const uint32_t measured, const uint32_t desired, + const uint16_t range, const int16_t excess) { + DPRINT("Matching MARK "); + DPRINT(measured * kRawTick); + DPRINT(" vs "); + DPRINT(desired); + DPRINT(" + "); + DPRINT(excess); + DPRINT(". "); + return match(measured, desired + excess, 0, range); +} + +/// Check if we match a space signal(measured) with the desired within +/// +/-tolerance percent, after an expected is excess is removed. +/// @param[in] measured The recorded period of the signal pulse. +/// @param[in] desired The expected period (in usecs) we are matching against. +/// @param[in] tolerance A percentage expressed as an integer. e.g. 10 is 10%. +/// @param[in] excess A non-scaling amount to reduce usecs by. +/// @return A Boolean. true if it matches, false if it doesn't. +bool IRrecv::matchSpace(uint32_t measured, uint32_t desired, uint8_t tolerance, + int16_t excess) { + DPRINT("Matching SPACE "); + DPRINT(measured * kRawTick); + DPRINT(" vs "); + DPRINT(desired); + DPRINT(" - "); + DPRINT(excess); + DPRINT(". "); + return match(measured, desired - excess, tolerance); +} + +/// Check if we match a space signal(measured) with the desired within a +/// range (in uSeconds) either side of the desired, after an expected is excess +/// is removed. +/// @param[in] measured The recorded period of the signal pulse. +/// @param[in] desired The expected period (in usecs) we are matching against. +/// @param[in] range The range limit from desired to accept in uSeconds. +/// @param[in] excess A non-scaling amount to reduce usecs by. +/// @return A Boolean. true if it matches, false if it doesn't. +bool IRrecv::matchSpaceRange(const uint32_t measured, const uint32_t desired, + const uint16_t range, const int16_t excess) { + DPRINT("Matching SPACE "); + DPRINT(measured * kRawTick); + DPRINT(" vs "); + DPRINT(desired); + DPRINT(" - "); + DPRINT(excess); + DPRINT(". "); + return match(measured, desired - excess, 0, range); +} + +#if DECODE_HASH +/// Compare two tick values. +/// @param[in] oldval Nr. of ticks. +/// @param[in] newval Nr. of ticks. +/// @return 0 if newval is shorter, 1 if it is equal, & 2 if it is longer. +/// @note Use a tolerance of 20% +uint16_t IRrecv::compare(const uint16_t oldval, const uint16_t newval) { + if (newval < oldval * 0.8) + return 0; + else if (oldval < newval * 0.8) + return 2; + else + return 1; +} + +/// Decode any arbitrary IR message into a 32-bit code value. +/// Instead of decoding using a standard encoding scheme +/// (e.g. Sony, NEC, RC5), the code is hashed to a 32-bit value. +/// +/// The algorithm: look at the sequence of MARK signals, and see if each one +/// is shorter (0), the same length (1), or longer (2) than the previous. +/// Do the same with the SPACE signals. Hash the resulting sequence of 0's, +/// 1's, and 2's to a 32-bit value. This will give a unique value for each +/// different code (probably), for most code systems. +/// @see http://arcfn.com/2010/01/using-arbitrary-remotes-with-arduino.html +/// @note This isn't a "real" decoding, just an arbitrary value. +/// Hopefully this code is unique for each button. +bool IRrecv::decodeHash(decode_results *results) { + // Require at least some samples to prevent triggering on noise + if (results->rawlen < _unknown_threshold) return false; + int32_t hash = kFnvBasis32; + // 'rawlen - 2' to avoid the look ahead from going out of bounds. + // Should probably be -3 to avoid comparing the trailing space entry, + // however it is left this way for compatibility with previously captured + // values. + for (uint16_t i = 1; i < results->rawlen - 2; i++) { + uint16_t value = compare(results->rawbuf[i], results->rawbuf[i + 2]); + // Add value into the hash + hash = (hash * kFnvPrime32) ^ value; + } + results->value = hash & 0xFFFFFFFF; + results->bits = results->rawlen / 2; + results->address = 0; + results->command = 0; + results->decode_type = UNKNOWN; + return true; +} +#endif // DECODE_HASH + +/// Match & decode the typical data section of an IR message. +/// The data value is stored in the least significant bits reguardless of the +/// bit ordering requested. +/// @param[in] data_ptr A pointer to where we are at in the capture buffer. +/// @param[in] nbits Nr. of data bits we expect. +/// @param[in] onemark Nr. of uSeconds in an expected mark signal for a '1' bit. +/// @param[in] onespace Nr. of uSecs in an expected space signal for a '1' bit. +/// @param[in] zeromark Nr. of uSecs in an expected mark signal for a '0' bit. +/// @param[in] zerospace Nr. of uSecs in an expected space signal for a '0' bit. +/// @param[in] tolerance Percentage error margin to allow. (Default: kUseDefTol) +/// @param[in] excess Nr. of uSeconds. (Def: kMarkExcess) +/// @param[in] MSBfirst Bit order to save the data in. (Def: true) +/// true is Most Significant Bit First Order, false is Least Significant First +/// @param[in] expectlastspace Do we expect a space at the end of the message? +/// @return A match_result_t structure containing the success (or not), the +/// data value, and how many buffer entries were used. +match_result_t IRrecv::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, const int16_t excess, const bool MSBfirst, + const bool expectlastspace) { + match_result_t result; + result.success = false; // Fail by default. + result.data = 0; + if (expectlastspace) { // We are expecting data with a final space. + for (result.used = 0; result.used < nbits * 2; + result.used += 2, data_ptr += 2) { + // Is the bit a '1'? + if (matchMark(*data_ptr, onemark, tolerance, excess) && + matchSpace(*(data_ptr + 1), onespace, tolerance, excess)) { + result.data = (result.data << 1) | 1; + } else if (matchMark(*data_ptr, zeromark, tolerance, excess) && + matchSpace(*(data_ptr + 1), zerospace, tolerance, excess)) { + result.data <<= 1; // The bit is a '0'. + } else { + if (!MSBfirst) result.data = reverseBits(result.data, result.used / 2); + return result; // It's neither, so fail. + } + } + result.success = true; + } else { // We are expecting data without a final space. + // Match all but the last bit, as it may not match easily. + result = matchData(data_ptr, nbits ? nbits - 1 : 0, onemark, onespace, + zeromark, zerospace, tolerance, excess, true, true); + if (result.success) { + // Is the bit a '1'? + if (matchMark(*(data_ptr + result.used), onemark, tolerance, excess)) + result.data = (result.data << 1) | 1; + else if (matchMark(*(data_ptr + result.used), zeromark, tolerance, + excess)) + result.data <<= 1; // The bit is a '0'. + else + result.success = false; + if (result.success) result.used++; + } + } + if (!MSBfirst) result.data = reverseBits(result.data, nbits); + return result; +} + +/// Match & decode the typical data section of an IR message. +/// The bytes are stored at result_ptr. The first byte in the result equates to +/// the first byte encountered, and so on. +/// @param[in] data_ptr A pointer to where we are at in the capture buffer. +/// @param[out] result_ptr A ptr to where to start storing the bytes we decoded. +/// @param[in] remaining The size of the capture buffer remaining. +/// @param[in] nbytes Nr. of data bytes we expect. +/// @param[in] onemark Nr. of uSeconds in an expected mark signal for a '1' bit. +/// @param[in] onespace Nr. of uSecs in an expected space signal for a '1' bit. +/// @param[in] zeromark Nr. of uSecs in an expected mark signal for a '0' bit. +/// @param[in] zerospace Nr. of uSecs in an expected space signal for a '0' bit. +/// @param[in] tolerance Percentage error margin to allow. (Default: kUseDefTol) +/// @param[in] excess Nr. of uSeconds. (Def: kMarkExcess) +/// @param[in] MSBfirst Bit order to save the data in. (Def: true) +/// true is Most Significant Bit First Order, false is Least Significant First +/// @param[in] expectlastspace Do we expect a space at the end of the message? +/// @return If successful, how many buffer entries were used. Otherwise 0. +uint16_t IRrecv::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, const int16_t excess, + const bool MSBfirst, const bool expectlastspace) { + // Check if there is enough capture buffer to possibly have the desired bytes. + if (remaining + expectlastspace < (nbytes * 8 * 2) + 1) + return 0; // Nope, so abort. + uint16_t offset = 0; + for (uint16_t byte_pos = 0; byte_pos < nbytes; byte_pos++) { + bool lastspace = (byte_pos + 1 == nbytes) ? expectlastspace : true; + match_result_t result = matchData(data_ptr + offset, 8, onemark, onespace, + zeromark, zerospace, tolerance, excess, + MSBfirst, lastspace); + if (result.success == false) return 0; // Fail + result_ptr[byte_pos] = (uint8_t)result.data; + offset += result.used; + } + return offset; +} + +/// Match & decode a generic/typical IR message. +/// The data is stored in result_bits_ptr or result_bytes_ptr depending on flag +/// `use_bits`. +/// @note Values of 0 for hdrmark, hdrspace, footermark, or footerspace mean +/// skip that requirement. +/// +/// @param[in] data_ptr A pointer to where we are at in the capture buffer. +/// @param[out] result_bits_ptr A pointer to where to start storing the bits we +/// decoded. +/// @param[out] result_bytes_ptr A pointer to where to start storing the bytes +/// we decoded. +/// @param[in] use_bits A flag indicating if we are to decode bits or bytes. +/// @param[in] remaining The size of the capture buffer remaining. +/// @param[in] nbits Nr. of data bits we expect. +/// @param[in] hdrmark Nr. of uSeconds for the expected header mark signal. +/// @param[in] hdrspace Nr. of uSeconds for the expected header space signal. +/// @param[in] onemark Nr. of uSeconds in an expected mark signal for a '1' bit. +/// @param[in] onespace Nr. of uSecs in an expected space signal for a '1' bit. +/// @param[in] zeromark Nr. of uSecs in an expected mark signal for a '0' bit. +/// @param[in] zerospace Nr. of uSecs in an expected space signal for a '0' bit. +/// @param[in] footermark Nr. of uSeconds for the expected footer mark signal. +/// @param[in] footerspace Nr. of uSeconds for the expected footer space/gap +/// signal. +/// @param[in] atleast Is the match on the footerspace a matchAtLeast or +/// matchSpace? +/// @param[in] tolerance Percentage error margin to allow. (Default: kUseDefTol) +/// @param[in] excess Nr. of uSeconds. (Def: kMarkExcess) +/// @param[in] MSBfirst Bit order to save the data in. (Def: true) +/// true is Most Significant Bit First Order, false is Least Significant First +/// @return If successful, how many buffer entries were used. Otherwise 0. +uint16_t IRrecv::_matchGeneric(volatile uint16_t *data_ptr, + uint64_t *result_bits_ptr, + uint8_t *result_bytes_ptr, + const bool use_bits, + 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, + const uint8_t tolerance, + const int16_t excess, + const bool MSBfirst) { + // If we are expecting byte sizes, check it's a factor of 8 or fail. + if (!use_bits && nbits % 8 != 0) return 0; + // Calculate if we expect a trailing space in the data section. + const bool kexpectspace = footermark || (onespace != zerospace); + // Calculate how much remaining buffer is required. + uint16_t min_remaining = nbits * 2 - (kexpectspace ? 0 : 1); + + if (hdrmark) min_remaining++; + if (hdrspace) min_remaining++; + if (footermark) min_remaining++; + // Don't need to extend for footerspace because it could be the end of message + + // Check if there is enough capture buffer to possibly have the message. + if (remaining < min_remaining) return 0; // Nope, so abort. + uint16_t offset = 0; + + // Header + if (hdrmark && !matchMark(*(data_ptr + offset++), hdrmark, tolerance, excess)) + return 0; + if (hdrspace && !matchSpace(*(data_ptr + offset++), hdrspace, tolerance, + excess)) + return 0; + + // Data + if (use_bits) { // Bits. + match_result_t result = IRrecv::matchData(data_ptr + offset, nbits, + onemark, onespace, + zeromark, zerospace, tolerance, + excess, MSBfirst, kexpectspace); + if (!result.success) return 0; + *result_bits_ptr = result.data; + offset += result.used; + } else { // bytes + uint16_t data_used = IRrecv::matchBytes(data_ptr + offset, result_bytes_ptr, + remaining - offset, nbits / 8, + onemark, onespace, + zeromark, zerospace, tolerance, + excess, MSBfirst, kexpectspace); + if (!data_used) return 0; + offset += data_used; + } + // Footer + if (footermark && !matchMark(*(data_ptr + offset++), footermark, tolerance, + excess)) + return 0; + // If we have something still to match & haven't reached the end of the buffer + if (footerspace && offset < remaining) { + if (atleast) { + if (!matchAtLeast(*(data_ptr + offset), footerspace, tolerance, excess)) + return 0; + } else { + if (!matchSpace(*(data_ptr + offset), footerspace, tolerance, excess)) + return 0; + } + offset++; + } + return offset; +} + +/// Match & decode a generic/typical <= 64bit IR message. +/// The data is stored at result_ptr. +/// @note Values of 0 for hdrmark, hdrspace, footermark, or footerspace mean +/// skip that requirement. +/// +/// @param[in] data_ptr: A pointer to where we are at in the capture buffer. +/// @param[out] result_ptr A ptr to where to start storing the bits we decoded. +/// @param[in] remaining The size of the capture buffer remaining. +/// @param[in] nbits Nr. of data bits we expect. +/// @param[in] hdrmark Nr. of uSeconds for the expected header mark signal. +/// @param[in] hdrspace Nr. of uSeconds for the expected header space signal. +/// @param[in] onemark Nr. of uSeconds in an expected mark signal for a '1' bit. +/// @param[in] onespace Nr. of uSecs in an expected space signal for a '1' bit. +/// @param[in] zeromark Nr. of uSecs in an expected mark signal for a '0' bit. +/// @param[in] zerospace Nr. of uSecs in an expected space signal for a '0' bit. +/// @param[in] footermark Nr. of uSeconds for the expected footer mark signal. +/// @param[in] footerspace Nr. of uSeconds for the expected footer space/gap +/// signal. +/// @param[in] atleast Is the match on the footerspace a matchAtLeast or +/// matchSpace? +/// @param[in] tolerance Percentage error margin to allow. (Default: kUseDefTol) +/// @param[in] excess Nr. of uSeconds. (Def: kMarkExcess) +/// @param[in] MSBfirst Bit order to save the data in. (Def: true) +/// true is Most Significant Bit First Order, false is Least Significant First +/// @return If successful, how many buffer entries were used. Otherwise 0. +uint16_t IRrecv::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, + const uint8_t tolerance, + const int16_t excess, + const bool MSBfirst) { + return _matchGeneric(data_ptr, result_ptr, NULL, true, remaining, nbits, + hdrmark, hdrspace, onemark, onespace, + zeromark, zerospace, footermark, footerspace, atleast, + tolerance, excess, MSBfirst); +} + +/// Match & decode a generic/typical > 64bit IR message. +/// The bytes are stored at result_ptr. The first byte in the result equates to +/// the first byte encountered, and so on. +/// @note Values of 0 for hdrmark, hdrspace, footermark, or footerspace mean +/// skip that requirement. +/// @param[in] data_ptr: A pointer to where we are at in the capture buffer. +/// @param[out] result_ptr A ptr to where to start storing the bytes we decoded. +/// @param[in] remaining The size of the capture buffer remaining. +/// @param[in] nbits Nr. of data bits we expect. +/// @param[in] hdrmark Nr. of uSeconds for the expected header mark signal. +/// @param[in] hdrspace Nr. of uSeconds for the expected header space signal. +/// @param[in] onemark Nr. of uSeconds in an expected mark signal for a '1' bit. +/// @param[in] onespace Nr. of uSecs in an expected space signal for a '1' bit. +/// @param[in] zeromark Nr. of uSecs in an expected mark signal for a '0' bit. +/// @param[in] zerospace Nr. of uSecs in an expected space signal for a '0' bit. +/// @param[in] footermark Nr. of uSeconds for the expected footer mark signal. +/// @param[in] footerspace Nr. of uSeconds for the expected footer space/gap +/// signal. +/// @param[in] atleast Is the match on the footerspace a matchAtLeast or +/// matchSpace? +/// @param[in] tolerance Percentage error margin to allow. (Default: kUseDefTol) +/// @param[in] excess Nr. of uSeconds. (Def: kMarkExcess) +/// @param[in] MSBfirst Bit order to save the data in. (Def: true) +/// true is Most Significant Bit First Order, false is Least Significant First +/// @return If successful, how many buffer entries were used. Otherwise 0. +uint16_t IRrecv::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, + const uint8_t tolerance, + const int16_t excess, + const bool MSBfirst) { + return _matchGeneric(data_ptr, NULL, result_ptr, false, remaining, nbits, + hdrmark, hdrspace, onemark, onespace, + zeromark, zerospace, footermark, footerspace, atleast, + tolerance, excess, MSBfirst); +} + +/// Match & decode a generic/typical constant bit time <= 64bit IR message. +/// The data is stored at result_ptr. +/// @note Values of 0 for hdrmark, hdrspace, footermark, or footerspace mean +/// skip that requirement. +/// @param[in] data_ptr A pointer to where we are at in the capture buffer. +/// @note `data_ptr` is assumed to be pointing to a "Mark", not a "Space". +/// @param[out] result_ptr A ptr to where to start storing the bits we decoded. +/// @param[in] remaining The size of the capture buffer remaining. +/// @param[in] nbits Nr. of data bits we expect. +/// @param[in] hdrmark Nr. of uSeconds for the expected header mark signal. +/// @param[in] hdrspace Nr. of uSeconds for the expected header space signal. +/// @param[in] one Nr. of uSeconds in an expected mark signal for a '1' bit. +/// @param[in] zero Nr. of uSeconds in an expected mark signal for a '0' bit. +/// @param[in] footermark Nr. of uSeconds for the expected footer mark signal. +/// @param[in] footerspace Nr. of uSeconds for the expected footer space/gap +/// signal. +/// @param[in] atleast Is the match on the footerspace a matchAtLeast or +/// matchSpace? +/// @param[in] tolerance Percentage error margin to allow. (Default: kUseDefTol) +/// @param[in] excess Nr. of uSeconds. (Def: kMarkExcess) +/// @param[in] MSBfirst Bit order to save the data in. (Def: true) +/// true is Most Significant Bit First Order, false is Least Significant First +/// @return If successful, how many buffer entries were used. Otherwise 0. +/// @note Parameters one + zero add up to the total time for a bit. +/// e.g. mark(one) + space(zero) is a `1`, mark(zero) + space(one) is a `0`. +uint16_t IRrecv::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, + const uint8_t tolerance, + const int16_t excess, + const bool MSBfirst) { + uint16_t offset = 0; + uint64_t result = 0; + // If we expect a footermark, then this can be processed like normal. + if (footermark) + return _matchGeneric(data_ptr, result_ptr, NULL, true, remaining, nbits, + hdrmark, hdrspace, one, zero, zero, one, + footermark, footerspace, atleast, + tolerance, excess, MSBfirst); + // Overwise handle like normal, except for the last bit. and no footer. + uint16_t bits = (nbits > 0) ? nbits - 1 : 0; // Make sure we don't underflow. + offset = _matchGeneric(data_ptr, &result, NULL, true, remaining, bits, + hdrmark, hdrspace, one, zero, zero, one, 0, 0, false, + tolerance, excess, true); + if (!offset) return 0; // Didn't match. + // Now for the last bit. + if (remaining <= offset) return 0; // Not enough buffer. + result <<= 1; + bool last_bit = 0; + // Is the mark a '1' or a `0`? + if (matchMark(*(data_ptr + offset), one, tolerance, excess)) { // 1 + last_bit = 1; + result |= 1; + } else if (matchMark(*(data_ptr + offset), zero, tolerance, excess)) { // 0 + last_bit = 0; + } else { + return 0; // It's neither, so fail. + } + offset++; + uint32_t expected_space = (last_bit ? zero : one) + footerspace; + // If we are not at the end of the buffer, check for at least the expected + // space value. + if (remaining > offset) { + if (atleast) { + if (!matchAtLeast(*(data_ptr + offset), expected_space, tolerance, + excess)) + return false; + } else { + if (!matchSpace(*(data_ptr + offset), expected_space, tolerance)) + return false; + } + offset++; + } + if (!MSBfirst) result = reverseBits(result, nbits); + *result_ptr = result; + return offset; +} + +/// Match & decode a Manchester Code <= 64bit IR message. +/// The data is stored at result_ptr. +/// @note Values of 0 for hdrmark, hdrspace, footermark, or footerspace mean +/// skip that requirement. +/// @param[in] data_ptr A pointer to where we are at in the capture buffer. +/// @note `data_ptr` is assumed to be pointing to a "Mark", not a "Space". +/// @param[out] result_ptr A ptr to where to start storing the bits we decoded. +/// @param[in] remaining The size of the capture buffer remaining. +/// @param[in] nbits Nr. of data bits we expect. +/// @param[in] hdrmark Nr. of uSeconds for the expected header mark signal. +/// @param[in] hdrspace Nr. of uSeconds for the expected header space signal. +/// @param[in] half_period Nr. of uSeconds for half the clock's period. +/// i.e. 1/2 wavelength +/// @param[in] footermark Nr. of uSeconds for the expected footer mark signal. +/// @param[in] footerspace Nr. of uSeconds for the expected footer space/gap +/// signal. +/// @param[in] atleast Is the match on the footerspace a matchAtLeast or +/// matchSpace? +/// @param[in] tolerance Percentage error margin to allow. (Default: kUseDefTol) +/// @param[in] excess Nr. of uSeconds. (Def: kMarkExcess) +/// @param[in] MSBfirst Bit order to save the data in. (Def: true) +/// true is Most Significant Bit First Order, false is Least Significant First +/// @param[in] GEThomas Use G.E. Thomas (true) or IEEE 802.3 (false) convention? +/// @return If successful, how many buffer entries were used. Otherwise 0. +/// @see https://en.wikipedia.org/wiki/Manchester_code +/// @see http://ww1.microchip.com/downloads/en/AppNotes/Atmel-9164-Manchester-Coding-Basics_Application-Note.pdf +uint16_t IRrecv::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 half_period, + const uint16_t footermark, + const uint32_t footerspace, + const bool atleast, + const uint8_t tolerance, + const int16_t excess, + const bool MSBfirst, + const bool GEThomas) { + uint16_t offset = 0; + uint16_t bank = 0; + uint16_t entry = 0; + + // Calculate how much remaining buffer is required. + // Shortest case is nbits. Longest case is 2 * nbits. + uint16_t min_remaining = nbits; + + if (hdrmark) min_remaining++; + if (hdrspace) min_remaining++; + if (footermark) min_remaining++; + // Don't need to extend for footerspace because it could be the end of message + + // Check if there is enough capture buffer to possibly have the message. + if (remaining < min_remaining) return 0; // Nope, so abort. + + // Header + if (hdrmark) { + entry = *(data_ptr + offset++); + if (!hdrspace) { // If we have no Header Space ... + // Do we have a data 'mark' half period merged with the header mark? + if (matchMark(entry, hdrmark + half_period, + tolerance, excess)) { + // Looks like we do. + bank = entry * kRawTick - hdrmark; + } else if (!matchMark(entry, hdrmark, tolerance, excess)) { + return 0; // It's not a normal header mark, so fail. + } + } else if (!matchMark(entry, hdrmark, tolerance, excess)) { + return 0; // It's not a normal header mark, so fail. + } + } + if (hdrspace) { + entry = *(data_ptr + offset++); + // Check to see if the header space has merged with a data space half period + if (matchSpace(entry, hdrspace + half_period, tolerance, excess)) { + // Looks like we do. + bank = entry * kRawTick - hdrspace; + } else if (!matchSpace(entry, hdrspace, tolerance, excess)) { + return 0; // It's not a normal header space, so fail. + } + } + + if (!match(bank / kRawTick, half_period, tolerance, excess)) bank = 0; + // Data + uint16_t used = matchManchesterData(data_ptr + offset, result_ptr, + remaining - offset, nbits, half_period, + bank, tolerance, excess, MSBfirst, + GEThomas); + if (!used) return 0; // Data did match. + offset += used; + // Footer + if (footermark && + !(matchMark(*(data_ptr + offset), footermark + half_period, + tolerance, excess) || + matchMark(*(data_ptr + offset), footermark, tolerance, excess))) + return 0; + offset++; + // If we have something still to match & haven't reached the end of the buffer + if (footerspace && offset < remaining) { + if (atleast) { + if (!matchAtLeast(*(data_ptr + offset), footerspace, tolerance, excess)) + return 0; + } else { + if (!matchSpace(*(data_ptr + offset), footerspace, tolerance, excess) && + !matchSpace(*(data_ptr + offset), footerspace + half_period, + tolerance, excess)) + return 0; + } + offset++; + } + return offset; +} + +/// Match & decode a Manchester Code data (<= 64bits. +/// @param[in] data_ptr A pointer to where we are at in the capture buffer. +/// @note `data_ptr` is assumed to be pointing to a "Mark", not a "Space". +/// @param[out] result_ptr A ptr to where to start storing the bits we decoded. +/// @param[in] remaining The size of the capture buffer remaining. +/// @param[in] nbits Nr. of data bits we expect. +/// @param[in] half_period Nr. of uSeconds for half the clock's period. +/// i.e. 1/2 wavelength +/// @param[in] tolerance Percentage error margin to allow. (Default: kUseDefTol) +/// @param[in] starting_balance Amount of uSeconds to assume exists prior to +/// the current value pointed too. +/// @param[in] excess Nr. of uSeconds. (Def: kMarkExcess) +/// @param[in] MSBfirst Bit order to save the data in. (Def: true) +/// true is Most Significant Bit First Order, false is Least Significant First +/// @param[in] GEThomas Use G.E. Thomas (true) or IEEE 802.3 (false) convention? +/// @return If successful, how many buffer entries were used. Otherwise 0. +/// @see https://en.wikipedia.org/wiki/Manchester_code +/// @see http://ww1.microchip.com/downloads/en/AppNotes/Atmel-9164-Manchester-Coding-Basics_Application-Note.pdf +/// @todo Clean up and optimise this. It is just "get it working code" atm. +uint16_t IRrecv::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, + const uint8_t tolerance, + const int16_t excess, + const bool MSBfirst, + const bool GEThomas) { + DPRINTLN("DEBUG: Entered matchManchesterData"); + uint16_t offset = 0; + uint64_t data = 0; + uint16_t nr_half_periods = 0; + const uint16_t expected_half_periods = nbits * 2; + // Flip the bit if we have a starting balance. ie. Carry over from the header. + bool currentBit = starting_balance ? !GEThomas : GEThomas; + const uint16_t raw_half_period = half_period / kRawTick; + + // Calculate how much remaining buffer is required. + // Shortest case is nbits. Longest case is 2 * nbits. + uint16_t min_remaining = nbits; + + // Check if there is enough capture buffer to possibly have the message. + if (remaining < min_remaining) { + DPRINTLN("DEBUG: Ran out of capture buffer!"); + return 0; // Nope, so abort. + } + + // Convert to ticks. Optimisation: Saves on math/extra instructions later. + uint16_t bank = starting_balance / kRawTick; + + // Data + // Loop through the buffer till we run out of buffer, or nr of half periods. + // Possible patterns are: + // short + short = 1 bit (Add the value of the previous bit again) + // short + long + short = 2 bits (Add the previous bit again, then flip & add) + // short + long + long + short = 3 bits (add prev, flip & add, flip & add) + // We can't start with a long. + // + // The general approach is thus: + // Check we have a short interval, next or in the bank. + // If the next timing value is long, act according and reset the bank to + // a short balance. + // or + // If it is short, act accordingly and declare the bank empty. + // Repeat. + while ((offset < remaining || bank) && + nr_half_periods < expected_half_periods) { + // Get the next entry if we haven't anything existing to process. + DPRINT("DEBUG: Offset = "); + DPRINTLN(offset); + if (!bank) bank = *(data_ptr + offset++); + DPRINT("DEBUG: Bank = "); + DPRINTLN(bank * kRawTick); + // Check if we don't have a short interval. + DPRINTLN("DEBUG: Checking for short interval"); + if (!match(bank, half_period, tolerance, excess)) { + DPRINTLN("DEBUG: It is. Exiting"); + return 0; // Not valid. + } + // We've succeeded in matching half a period, so count it. + nr_half_periods++; + DPRINT("DEBUG: Half Periods = "); + DPRINTLN(nr_half_periods); + // We've now used up our bank, so refill it with the next item, unless we + // are at the end of the capture buffer. + // If we are assume a single half period of "space". + if (offset < remaining) { + DPRINT("DEBUG: Offset = "); + DPRINTLN(offset); + bank = *(data_ptr + offset++); + } else if (offset == remaining) { + bank = raw_half_period; + } else { + return 0; // We are out of buffer, so abort! + } + DPRINT("DEBUG: Bank = "); + DPRINTLN(bank * kRawTick); + + // Shift the data along and add our new bit. + DPRINT("DEBUG: Adding bit: "); + DPRINTLN((currentBit ? "1" : "0")); + data <<= 1; + data |= currentBit; + + // Check if we have a long interval. + if (match(bank, half_period * 2, tolerance, excess)) { + // It is, so flip the bit we need to append, and remove a half_period of + // time from the bank. + DPRINTLN("DEBUG: long interval detected"); + currentBit = !currentBit; + bank -= raw_half_period; + } else if (match(bank, half_period, tolerance, excess)) { + // It is a short interval, so eat up all the time and move on. + DPRINTLN("DEBUG: short interval detected"); + bank = 0; + } else if (nr_half_periods == expected_half_periods - 1 && + matchAtLeast(bank, half_period, tolerance, excess)) { + // We are at the end of the data & it is a short interval, so eat up all + // the time and move on. + bank = 0; + // Reduce the offset as we are at the end of the data doing a + // matchAtLeast() because we could be processing part of a footer. + offset--; + } else { + // The length isn't what we expected (neither long or short), so bail. + return 0; + } + nr_half_periods++; + } + + // Clean up and process the data. + if (!MSBfirst) data = reverseBits(data, nbits); + // Trim the data to size. + *result_ptr = GETBITS64(data, 0, nbits); + return offset; +} + +#if UNIT_TEST +/// Unit test helper to get access to the params structure. +volatile irparams_t *IRrecv::_getParamsPtr(void) { + return ¶ms; +} +#endif // UNIT_TEST +// End of IRrecv class ------------------- diff --git a/lib/IRremoteESP8266/src/IRrecv.h b/lib/IRremoteESP8266/src/IRrecv.h index a5c2eb30de..13aadb6574 100644 --- a/lib/IRremoteESP8266/src/IRrecv.h +++ b/lib/IRremoteESP8266/src/IRrecv.h @@ -12,7 +12,7 @@ #include #define __STDC_LIMIT_MACROS #include -#include "../src/IRremoteESP8266.h" +#include "IRremoteESP8266.h" // Constants const uint16_t kHeader = 2; // Usual nr. of header entries. diff --git a/lib/IRremoteESP8266/src/IRsend.cpp b/lib/IRremoteESP8266/src/IRsend.cpp new file mode 100644 index 0000000000..62556b7e96 --- /dev/null +++ b/lib/IRremoteESP8266/src/IRsend.cpp @@ -0,0 +1,1317 @@ +// Copyright 2009 Ken Shirriff +// Copyright 2015 Mark Szabo +// Copyright 2017,2019 David Conran + +#include "IRsend.h" +#ifndef UNIT_TEST +#include +#else +#define __STDC_LIMIT_MACROS +#include +#endif +#include +#ifdef UNIT_TEST +#include +#endif +#include "IRtimer.h" + +/// Constructor for an IRsend object. +/// @param[in] IRsendPin Which GPIO pin to use when sending an IR command. +/// @param[in] inverted Optional flag to invert the output. (default = false) +/// e.g. LED is illuminated when GPIO is LOW rather than HIGH. +/// @warning Setting `inverted` to something other than the default could +/// easily destroy your IR LED if you are overdriving it. +/// Unless you *REALLY* know what you are doing, don't change this. +/// @param[in] use_modulation Do we do frequency modulation during transmission? +/// i.e. If not, assume a 100% duty cycle. Ignore attempts to change the +/// duty cycle etc. +IRsend::IRsend(uint16_t IRsendPin, bool inverted, bool use_modulation) + : IRpin(IRsendPin), periodOffset(kPeriodOffset) { + if (inverted) { + outputOn = LOW; + outputOff = HIGH; + } else { + outputOn = HIGH; + outputOff = LOW; + } + modulation = use_modulation; + if (modulation) + _dutycycle = kDutyDefault; + else + _dutycycle = kDutyMax; +} + +/// Enable the pin for output. +void IRsend::begin() { +#ifndef UNIT_TEST + pinMode(IRpin, OUTPUT); +#endif + ledOff(); // Ensure the LED is in a known safe state when we start. +} + +/// Turn off the IR LED. +void IRsend::ledOff() { +#ifndef UNIT_TEST + digitalWrite(IRpin, outputOff); +#endif +} + +/// Turn on the IR LED. +void IRsend::ledOn() { +#ifndef UNIT_TEST + digitalWrite(IRpin, outputOn); +#endif +} + +/// Calculate the period for a given frequency. +/// @param[in] hz Frequency in Hz. +/// @param[in] use_offset Should we use the calculated offset or not? +/// @return nr. of uSeconds. +/// @note (T = 1/f) +uint32_t IRsend::calcUSecPeriod(uint32_t hz, bool use_offset) { + if (hz == 0) hz = 1; // Avoid Zero hz. Divide by Zero is nasty. + uint32_t period = + (1000000UL + hz / 2) / hz; // The equiv of round(1000000/hz). + // Apply the offset and ensure we don't result in a <= 0 value. + if (use_offset) + return std::max((uint32_t)1, period + periodOffset); + else + return std::max((uint32_t)1, period); +} + +/// Set the output frequency modulation and duty cycle. +/// @param[in] freq The freq we want to modulate at. +/// Assumes < 1000 means kHz else Hz. +/// @param[in] duty Percentage duty cycle of the LED. +/// e.g. 25 = 25% = 1/4 on, 3/4 off. +/// If you are not sure, try 50 percent. +/// This is ignored if modulation is disabled at object instantiation. +/// @note Integer timing functions & math mean we can't do fractions of +/// microseconds timing. Thus minor changes to the freq & duty values may have +/// limited effect. You've been warned. +void IRsend::enableIROut(uint32_t freq, uint8_t duty) { + // Set the duty cycle to use if we want freq. modulation. + if (modulation) { + _dutycycle = std::min(duty, kDutyMax); + } else { + _dutycycle = kDutyMax; + } + if (freq < 1000) // Were we given kHz? Supports the old call usage. + freq *= 1000; +#ifdef UNIT_TEST + _freq_unittest = freq; +#endif // UNIT_TEST + uint32_t period = calcUSecPeriod(freq); + // Nr. of uSeconds the LED will be on per pulse. + onTimePeriod = (period * _dutycycle) / kDutyMax; + // Nr. of uSeconds the LED will be off per pulse. + offTimePeriod = period - onTimePeriod; +} + +#if ALLOW_DELAY_CALLS +/// An ESP8266 RTOS watch-dog timer friendly version of delayMicroseconds(). +/// @param[in] usec Nr. of uSeconds to delay for. +void IRsend::_delayMicroseconds(uint32_t usec) { + // delayMicroseconds() is only accurate to 16383us. + // Ref: https://www.arduino.cc/en/Reference/delayMicroseconds + if (usec <= kMaxAccurateUsecDelay) { +#ifndef UNIT_TEST + delayMicroseconds(usec); +#endif + } else { +#ifndef UNIT_TEST + // Invoke a delay(), where possible, to avoid triggering the WDT. + delay(usec / 1000UL); // Delay for as many whole milliseconds as we can. + // Delay the remaining sub-millisecond. + delayMicroseconds(static_cast(usec % 1000UL)); +#endif + } +} +#else // ALLOW_DELAY_CALLS +/// A version of delayMicroseconds() that handles large values and does NOT use +/// the watch-dog friendly delay() calls where appropriate. +/// @note Use this only if you know what you are doing as it may cause the WDT +/// to reset the ESP8266. +void IRsend::_delayMicroseconds(uint32_t usec) { + for (; usec > kMaxAccurateUsecDelay; usec -= kMaxAccurateUsecDelay) +#ifndef UNIT_TEST + delayMicroseconds(kMaxAccurateUsecDelay); + delayMicroseconds(static_cast(usec)); +#endif // UNIT_TEST +} +#endif // ALLOW_DELAY_CALLS + +/// Modulate the IR LED for the given period (usec) and at the duty cycle set. +/// @param[in] usec The period of time to modulate the IR LED for, in +/// microseconds. +/// @return Nr. of pulses actually sent. +/// @note +/// The ESP8266 has no good way to do hardware PWM, so we have to do it all +/// in software. There is a horrible kludge/brilliant hack to use the second +/// serial TX line to do fairly accurate hardware PWM, but it is only +/// available on a single specific GPIO and only available on some modules. +/// e.g. It's not available on the ESP-01 module. +/// Hence, for greater compatibility & choice, we don't use that method. +/// Ref: +/// https://www.analysir.com/blog/2017/01/29/updated-esp8266-nodemcu-backdoor-upwm-hack-for-ir-signals/ +uint16_t IRsend::mark(uint16_t usec) { + // Handle the simple case of no required frequency modulation. + if (!modulation || _dutycycle >= 100) { + ledOn(); + _delayMicroseconds(usec); + ledOff(); + return 1; + } + + // Not simple, so do it assuming frequency modulation. + uint16_t counter = 0; + IRtimer usecTimer = IRtimer(); + // Cache the time taken so far. This saves us calling time, and we can be + // assured that we can't have odd math problems. i.e. unsigned under/overflow. + uint32_t elapsed = usecTimer.elapsed(); + + while (elapsed < usec) { // Loop until we've met/exceeded our required time. + ledOn(); + // Calculate how long we should pulse on for. + // e.g. Are we to close to the end of our requested mark time (usec)? + _delayMicroseconds(std::min((uint32_t)onTimePeriod, usec - elapsed)); + ledOff(); + counter++; + if (elapsed + onTimePeriod >= usec) + return counter; // LED is now off & we've passed our allotted time. + // Wait for the lesser of the rest of the duty cycle, or the time remaining. + _delayMicroseconds( + std::min(usec - elapsed - onTimePeriod, (uint32_t)offTimePeriod)); + elapsed = usecTimer.elapsed(); // Update & recache the actual elapsed time. + } + return counter; +} + +/// Turn the pin (LED) off for a given time. +/// Sends an IR space for the specified number of microseconds. +/// A space is no output, so the PWM output is disabled. +/// @param[in] time Time in microseconds (us). +void IRsend::space(uint32_t time) { + ledOff(); + if (time == 0) return; + _delayMicroseconds(time); +} + +/// Calculate & set any offsets to account for execution times during sending. +/// +/// @param[in] hz The frequency to calibrate at >= 1000Hz. Default is 38000Hz. +/// @return The calculated period offset (in uSeconds) which is now in use. +/// e.g. -5. +/// @note This will generate an 65535us mark() IR LED signal. +/// This only needs to be called once, if at all. +int8_t IRsend::calibrate(uint16_t hz) { + if (hz < 1000) // Were we given kHz? Supports the old call usage. + hz *= 1000; + periodOffset = 0; // Turn off any existing offset while we calibrate. + enableIROut(hz); + IRtimer usecTimer = IRtimer(); // Start a timer *just* before we do the call. + uint16_t pulses = mark(UINT16_MAX); // Generate a PWM of 65,535 us. (Max.) + uint32_t timeTaken = usecTimer.elapsed(); // Record the time it took. + // While it shouldn't be necessary, assume at least 1 pulse, to avoid a + // divide by 0 situation. + pulses = std::max(pulses, (uint16_t)1U); + uint32_t calcPeriod = calcUSecPeriod(hz); // e.g. @38kHz it should be 26us. + // Assuming 38kHz for the example calculations: + // In a 65535us pulse, we should have 2520.5769 pulses @ 26us periods. + // e.g. 65535.0us / 26us = 2520.5769 + // This should have caused approx 2520 loops through the main loop in mark(). + // The average over that many interations should give us a reasonable + // approximation at what offset we need to use to account for instruction + // execution times. + // + // Calculate the actual period from the actual time & the actual pulses + // generated. + double_t actualPeriod = (double_t)timeTaken / (double_t)pulses; + // Store the difference between the actual time per period vs. calculated. + periodOffset = (int8_t)((double_t)calcPeriod - actualPeriod); + return periodOffset; +} + +/// Generic method for sending data that is common to most protocols. +/// Will send leading or trailing 0's if the nbits is larger than the number +/// of bits in data. +/// @param[in] onemark Nr. of usecs for the led to be pulsed for a '1' bit. +/// @param[in] onespace Nr. of usecs for the led to be fully off for a '1' bit. +/// @param[in] zeromark Nr. of usecs for the led to be pulsed for a '0' bit. +/// @param[in] zerospace Nr. of usecs for the led to be fully off for a '0' bit. +/// @param[in] data The data to be transmitted. +/// @param[in] nbits Nr. of bits of data to be sent. +/// @param[in] MSBfirst Flag for bit transmission order. +/// Defaults to MSB->LSB order. +void IRsend::sendData(uint16_t onemark, uint32_t onespace, uint16_t zeromark, + uint32_t zerospace, uint64_t data, uint16_t nbits, + bool MSBfirst) { + if (nbits == 0) // If we are asked to send nothing, just return. + return; + if (MSBfirst) { // Send the MSB first. + // Send 0's until we get down to a bit size we can actually manage. + while (nbits > sizeof(data) * 8) { + mark(zeromark); + space(zerospace); + nbits--; + } + // Send the supplied data. + for (uint64_t mask = 1ULL << (nbits - 1); mask; mask >>= 1) + if (data & mask) { // Send a 1 + mark(onemark); + space(onespace); + } else { // Send a 0 + mark(zeromark); + space(zerospace); + } + } else { // Send the Least Significant Bit (LSB) first / MSB last. + for (uint16_t bit = 0; bit < nbits; bit++, data >>= 1) + if (data & 1) { // Send a 1 + mark(onemark); + space(onespace); + } else { // Send a 0 + mark(zeromark); + space(zerospace); + } + } +} + +/// Generic method for sending simple protocol messages. +/// Will send leading or trailing 0's if the nbits is larger than the number +/// of bits in data. +/// @param[in] headermark Nr. of usecs for the led to be pulsed for the header +/// mark. A value of 0 means no header mark. +/// @param[in] headerspace Nr. of usecs for the led to be off after the header +/// mark. A value of 0 means no header space. +/// @param[in] onemark Nr. of usecs for the led to be pulsed for a '1' bit. +/// @param[in] onespace Nr. of usecs for the led to be fully off for a '1' bit. +/// @param[in] zeromark Nr. of usecs for the led to be pulsed for a '0' bit. +/// @param[in] zerospace Nr. of usecs for the led to be fully off for a '0' bit. +/// @param[in] footermark Nr. of usecs for the led to be pulsed for the footer +/// mark. A value of 0 means no footer mark. +/// @param[in] gap Nr. of usecs for the led to be off after the footer mark. +/// This is effectively the gap between messages. +/// A value of 0 means no gap space. +/// @param[in] data The data to be transmitted. +/// @param[in] nbits Nr. of bits of data to be sent. +/// @param[in] frequency The frequency we want to modulate at. (Hz/kHz) +/// @param[in] MSBfirst Flag for bit transmission order. +/// Defaults to MSB->LSB order. +/// @param[in] repeat Nr. of extra times the message will be sent. +/// e.g. 0 = 1 message sent, 1 = 1 initial + 1 repeat = 2 messages +/// @param[in] dutycycle Percentage duty cycle of the LED. +/// e.g. 25 = 25% = 1/4 on, 3/4 off. +/// If you are not sure, try 50 percent. +/// @note Assumes a frequency < 1000 means kHz otherwise it is in Hz. +/// Most common value is 38000 or 38, for 38kHz. +void IRsend::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) { + sendGeneric(headermark, headerspace, onemark, onespace, zeromark, zerospace, + footermark, gap, 0U, data, nbits, frequency, MSBfirst, repeat, + dutycycle); +} + +/// Generic method for sending simple protocol messages. +/// Will send leading or trailing 0's if the nbits is larger than the number +/// of bits in data. +/// @param[in] headermark Nr. of usecs for the led to be pulsed for the header +/// mark. A value of 0 means no header mark. +/// @param[in] headerspace Nr. of usecs for the led to be off after the header +/// mark. A value of 0 means no header space. +/// @param[in] onemark Nr. of usecs for the led to be pulsed for a '1' bit. +/// @param[in] onespace Nr. of usecs for the led to be fully off for a '1' bit. +/// @param[in] zeromark Nr. of usecs for the led to be pulsed for a '0' bit. +/// @param[in] zerospace Nr. of usecs for the led to be fully off for a '0' bit. +/// @param[in] footermark Nr. of usecs for the led to be pulsed for the footer +/// mark. A value of 0 means no footer mark. +/// @param[in] gap Nr. of usecs for the led to be off after the footer mark. +/// This is effectively the gap between messages. +/// A value of 0 means no gap space. +/// @param[in] mesgtime Min. nr. of usecs a single message needs to be. +/// This is effectively the min. total length of a single message. +/// @param[in] data The data to be transmitted. +/// @param[in] nbits Nr. of bits of data to be sent. +/// @param[in] frequency The frequency we want to modulate at. (Hz/kHz) +/// @param[in] MSBfirst Flag for bit transmission order. +/// Defaults to MSB->LSB order. +/// @param[in] repeat Nr. of extra times the message will be sent. +/// e.g. 0 = 1 message sent, 1 = 1 initial + 1 repeat = 2 messages +/// @param[in] dutycycle Percentage duty cycle of the LED. +/// e.g. 25 = 25% = 1/4 on, 3/4 off. +/// If you are not sure, try 50 percent. +/// @note Assumes a frequency < 1000 means kHz otherwise it is in Hz. +/// Most common value is 38000 or 38, for 38kHz. +void IRsend::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) { + // Setup + enableIROut(frequency, dutycycle); + IRtimer usecs = IRtimer(); + + // We always send a message, even for repeat=0, hence '<= repeat'. + for (uint16_t r = 0; r <= repeat; r++) { + usecs.reset(); + + // Header + if (headermark) mark(headermark); + if (headerspace) space(headerspace); + + // Data + sendData(onemark, onespace, zeromark, zerospace, data, nbits, MSBfirst); + + // Footer + if (footermark) mark(footermark); + uint32_t elapsed = usecs.elapsed(); + // Avoid potential unsigned integer underflow. e.g. when mesgtime is 0. + if (elapsed >= mesgtime) + space(gap); + else + space(std::max(gap, mesgtime - elapsed)); + } +} + +/// Generic method for sending simple protocol messages. +/// @param[in] headermark Nr. of usecs for the led to be pulsed for the header +/// mark. A value of 0 means no header mark. +/// @param[in] headerspace Nr. of usecs for the led to be off after the header +/// mark. A value of 0 means no header space. +/// @param[in] onemark Nr. of usecs for the led to be pulsed for a '1' bit. +/// @param[in] onespace Nr. of usecs for the led to be fully off for a '1' bit. +/// @param[in] zeromark Nr. of usecs for the led to be pulsed for a '0' bit. +/// @param[in] zerospace Nr. of usecs for the led to be fully off for a '0' bit. +/// @param[in] footermark Nr. of usecs for the led to be pulsed for the footer +/// mark. A value of 0 means no footer mark. +/// @param[in] gap Nr. of usecs for the led to be off after the footer mark. +/// This is effectively the gap between messages. +/// A value of 0 means no gap space. +/// @param[in] dataptr Pointer to the data to be transmitted. +/// @param[in] nbytes Nr. of bytes of data to be sent. +/// @param[in] frequency The frequency we want to modulate at. (Hz/kHz) +/// @param[in] MSBfirst Flag for bit transmission order. +/// Defaults to MSB->LSB order. +/// @param[in] repeat Nr. of extra times the message will be sent. +/// e.g. 0 = 1 message sent, 1 = 1 initial + 1 repeat = 2 messages +/// @param[in] dutycycle Percentage duty cycle of the LED. +/// e.g. 25 = 25% = 1/4 on, 3/4 off. +/// If you are not sure, try 50 percent. +/// @note Assumes a frequency < 1000 means kHz otherwise it is in Hz. +/// Most common value is 38000 or 38, for 38kHz. +void IRsend::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) { + // Setup + enableIROut(frequency, dutycycle); + // We always send a message, even for repeat=0, hence '<= repeat'. + for (uint16_t r = 0; r <= repeat; r++) { + // Header + if (headermark) mark(headermark); + if (headerspace) space(headerspace); + + // Data + for (uint16_t i = 0; i < nbytes; i++) + sendData(onemark, onespace, zeromark, zerospace, *(dataptr + i), 8, + MSBfirst); + + // Footer + if (footermark) mark(footermark); + space(gap); + } +} + +/// Generic method for sending Manchester code data. +/// Will send leading or trailing 0's if the nbits is larger than the number +/// of bits in data. +/// @param[in] half_period Nr. of uSeconds for half the clock's period. +/// (1/2 wavelength) +/// @param[in] data The data to be transmitted. +/// @param[in] nbits Nr. of bits of data to be sent. +/// @param[in] MSBfirst Flag for bit transmission order. +/// Defaults to MSB->LSB order. +/// @param[in] GEThomas Use G.E. Thomas (true/default) or IEEE 802.3 (false). +void IRsend::sendManchesterData(const uint16_t half_period, + const uint64_t data, + const uint16_t nbits, const bool MSBfirst, + const bool GEThomas) { + if (nbits == 0) return; // Nothing to send. + uint16_t bits = nbits; + uint64_t copy = (GEThomas) ? data : ~data; + + if (MSBfirst) { // Send the MSB first. + // Send 0's until we get down to a bit size we can actually manage. + if (bits > (sizeof(data) * 8)) { + sendManchesterData(half_period, 0ULL, bits - sizeof(data) * 8, MSBfirst, + GEThomas); + bits = sizeof(data) * 8; + } + // Send the supplied data. + for (uint64_t mask = 1ULL << (bits - 1); mask; mask >>= 1) + if (copy & mask) { + mark(half_period); + space(half_period); + } else { + space(half_period); + mark(half_period); + } + } else { // Send the Least Significant Bit (LSB) first / MSB last. + for (bits = 0; bits < nbits; bits++, copy >>= 1) + if (copy & 1) { + mark(half_period); + space(half_period); + } else { + space(half_period); + mark(half_period); + } + } +} + +/// Generic method for sending Manchester code messages. +/// Will send leading or trailing 0's if the nbits is larger than the number +/// @param[in] headermark Nr. of usecs for the led to be pulsed for the header +/// mark. A value of 0 means no header mark. +/// @param[in] headerspace Nr. of usecs for the led to be off after the header +/// mark. A value of 0 means no header space. +/// @param[in] half_period Nr. of uSeconds for half the clock's period. +/// (1/2 wavelength) +/// @param[in] footermark Nr. of usecs for the led to be pulsed for the footer +/// mark. A value of 0 means no footer mark. +/// @param[in] gap Min. nr. of usecs for the led to be off after the footer +/// mark. This is effectively the absolute minimum gap between messages. +/// @param[in] data The data to be transmitted. +/// @param[in] nbits Nr. of bits of data to be sent. +/// @param[in] frequency The frequency we want to modulate at. (Hz/kHz) +/// @param[in] MSBfirst Flag for bit transmission order. +/// Defaults to MSB->LSB order. +/// @param[in] repeat Nr. of extra times the message will be sent. +/// e.g. 0 = 1 message sent, 1 = 1 initial + 1 repeat = 2 messages +/// @param[in] dutycycle Percentage duty cycle of the LED. +/// e.g. 25 = 25% = 1/4 on, 3/4 off. +/// If you are not sure, try 50 percent. +/// @param[in] GEThomas Use G.E. Thomas (true/default) or IEEE 802.3 (false). +/// @note Assumes a frequency < 1000 means kHz otherwise it is in Hz. +/// Most common value is 38000 or 38, for 38kHz. +void IRsend::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, const bool MSBfirst, + const uint16_t repeat, const uint8_t dutycycle, + const bool GEThomas) { + // Setup + enableIROut(frequency, dutycycle); + + // We always send a message, even for repeat=0, hence '<= repeat'. + for (uint16_t r = 0; r <= repeat; r++) { + // Header + if (headermark) mark(headermark); + if (headerspace) space(headerspace); + // Data + sendManchesterData(half_period, data, nbits, MSBfirst, GEThomas); + // Footer + if (footermark) mark(footermark); + if (gap) space(gap); + } +} + +#if SEND_RAW +/// Send a raw IRremote message. +/// +/// @param[in] buf An array of uint16_t's that has microseconds elements. +/// @param[in] len Nr. of elements in the buf[] array. +/// @param[in] hz Frequency to send the message at. (kHz < 1000; Hz >= 1000) +/// @note Even elements are Mark times (On), Odd elements are Space times (Off). +/// Ref: +/// examples/IRrecvDumpV2/IRrecvDumpV2.ino (or later) +void IRsend::sendRaw(const uint16_t buf[], const uint16_t len, + const uint16_t hz) { + // Set IR carrier frequency + enableIROut(hz); + for (uint16_t i = 0; i < len; i++) { + if (i & 1) { // Odd bit. + space(buf[i]); + } else { // Even bit. + mark(buf[i]); + } + } + ledOff(); // We potentially have ended with a mark(), so turn of the LED. +} +#endif // SEND_RAW + +/// Get the minimum number of repeats for a given protocol. +/// @param[in] protocol Protocol number/type of the message you want to send. +/// @return The number of repeats required. +uint16_t IRsend::minRepeats(const decode_type_t protocol) { + switch (protocol) { + // Single repeats + case AIWA_RC_T501: + case AMCOR: + case COOLIX: + case ELITESCREENS: + case GICABLE: + case INAX: + case MIDEA24: + case MITSUBISHI: + case MITSUBISHI2: + case MITSUBISHI_AC: + case MULTIBRACKETS: + case SHERWOOD: + case TOSHIBA_AC: + return kSingleRepeat; + // Special + case AIRWELL: + return kAirwellMinRepeats; + case CARRIER_AC40: + return kCarrierAc40MinRepeat; + case DISH: + return kDishMinRepeat; + case EPSON: + return kEpsonMinRepeat; + case SANYO_AC88: + return kSanyoAc88MinRepeat; + case SONY: + return kSonyMinRepeat; + case SONY_38K: + return kSonyMinRepeat + 1; + case SYMPHONY: + return kSymphonyDefaultRepeat; + case ZEPEAL: + return kZepealMinRepeat; + default: + return kNoRepeat; + } +} + +/// Get the default number of bits for a given protocol. +/// @param[in] protocol Protocol number/type you want the default bit size for. +/// @return The number of bits. +uint16_t IRsend::defaultBits(const decode_type_t protocol) { + switch (protocol) { + case MULTIBRACKETS: + return 8; + case RC5: + case SYMPHONY: + return 12; + case LASERTAG: + case RC5X: + return 13; + case AIWA_RC_T501: + case DENON: + case SHARP: + return 15; + case BOSE: + case DISH: + case GICABLE: + case JVC: + case LEGOPF: + case MITSUBISHI: + case MITSUBISHI2: + case ZEPEAL: + return 16; + case METZ: + return 19; + case RC6: + case SONY: + case SONY_38K: + return 20; + case COOLIX: + case INAX: + case MIDEA24: + case NIKAI: + case RCMM: + case TRANSCOLD: + return 24; + case LG: + case LG2: + return 28; + case ARRIS: + case CARRIER_AC: + case ELITESCREENS: + case EPSON: + case NEC: + case NEC_LIKE: + case PANASONIC_AC32: + case SAMSUNG: + case SHERWOOD: + case WHYNTER: + return 32; + case AIRWELL: + return 34; + case LUTRON: + case TECO: + return 35; + case SAMSUNG36: + return 36; + case CARRIER_AC40: + return kCarrierAc40Bits; // 40 + case DOSHISHA: + return kDoshishaBits; // 40 + case SANYO_LC7461: + return kSanyoLC7461Bits; // 42 + case GOODWEATHER: + case KELON: + case MIDEA: + case PANASONIC: + return 48; + case ECOCLIM: + case MAGIQUEST: + case VESTEL_AC: + case TECHNIBEL_AC: + case TRUMA: + return 56; + case AMCOR: + case CARRIER_AC64: + case DELONGHI_AC: + case PIONEER: + return 64; + case ARGO: + return kArgoBits; + case CORONA_AC: + return kCoronaAcBits; + case DAIKIN: + return kDaikinBits; + case DAIKIN128: + return kDaikin128Bits; + case DAIKIN152: + return kDaikin152Bits; + case DAIKIN160: + return kDaikin160Bits; + case DAIKIN176: + return kDaikin176Bits; + case DAIKIN2: + return kDaikin2Bits; + case DAIKIN216: + return kDaikin216Bits; + case DAIKIN64: + return kDaikin64Bits; + case ELECTRA_AC: + return kElectraAcBits; + case GREE: + return kGreeBits; + case HAIER_AC: + return kHaierACBits; + case HAIER_AC_YRW02: + return kHaierACYRW02Bits; + case HAIER_AC176: + return kHaierAC176Bits; + case HITACHI_AC: + return kHitachiAcBits; + case HITACHI_AC1: + return kHitachiAc1Bits; + case HITACHI_AC2: + return kHitachiAc2Bits; + case HITACHI_AC3: + return kHitachiAc3Bits; + case HITACHI_AC344: + return kHitachiAc344Bits; + case HITACHI_AC424: + return kHitachiAc424Bits; + case KELVINATOR: + return kKelvinatorBits; + case MILESTAG2: + return kMilesTag2ShotBits; + case MIRAGE: + return kMirageBits; + case MITSUBISHI_AC: + return kMitsubishiACBits; + case MITSUBISHI136: + return kMitsubishi136Bits; + case MITSUBISHI112: + return kMitsubishi112Bits; + case MITSUBISHI_HEAVY_152: + return kMitsubishiHeavy152Bits; + case MITSUBISHI_HEAVY_88: + return kMitsubishiHeavy88Bits; + case NEOCLIMA: + return kNeoclimaBits; + case PANASONIC_AC: + return kPanasonicAcBits; + case RHOSS: + return kRhossBits; + case SAMSUNG_AC: + return kSamsungAcBits; + case SANYO_AC: + return kSanyoAcBits; + case SANYO_AC88: + return kSanyoAc88Bits; + case SHARP_AC: + return kSharpAcBits; + case TCL112AC: + return kTcl112AcBits; + case TEKNOPOINT: + return kTeknopointBits; + case TOSHIBA_AC: + return kToshibaACBits; + case TROTEC: + case TROTEC_3550: + return kTrotecBits; + case VOLTAS: + return kVoltasBits; + case WHIRLPOOL_AC: + return kWhirlpoolAcBits; + case XMP: + return kXmpBits; + // No default amount of bits. + case FUJITSU_AC: + case MWM: + default: + return 0; + } +} + +/// Send a simple (up to 64 bits) IR message of a given type. +/// An unknown/unsupported type will send nothing. +/// @param[in] type Protocol number/type of the message you want to send. +/// @param[in] data The data you want to send (up to 64 bits). +/// @param[in] nbits How many bits long the message is to be. +/// @param[in] repeat How many repeats to do? +/// @return True if it is a type we can attempt to send, false if not. +bool IRsend::send(const decode_type_t type, const uint64_t data, + const uint16_t nbits, const uint16_t repeat) { + uint16_t min_repeat __attribute__((unused)) = + std::max(IRsend::minRepeats(type), repeat); + switch (type) { +#if SEND_AIRWELL + case AIRWELL: + sendAirwell(data, nbits, min_repeat); + break; +#endif +#if SEND_AIWA_RC_T501 + case AIWA_RC_T501: + sendAiwaRCT501(data, nbits, min_repeat); + break; +#endif // SEND_AIWA_RC_T501 +#if SEND_ARRIS + case ARRIS: + sendArris(data, nbits, min_repeat); + break; +#endif // SEND_ARRIS +#if SEND_BOSE + case BOSE: + sendBose(data, nbits, min_repeat); + break; +#endif // SEND_BOSE +#if SEND_CARRIER_AC + case CARRIER_AC: + sendCarrierAC(data, nbits, min_repeat); + break; +#endif +#if SEND_CARRIER_AC40 + case CARRIER_AC40: + sendCarrierAC40(data, nbits, min_repeat); + break; +#endif // SEND_CARRIER_AC40 +#if SEND_CARRIER_AC64 + case CARRIER_AC64: + sendCarrierAC64(data, nbits, min_repeat); + break; +#endif // SEND_CARRIER_AC64 +#if SEND_COOLIX + case COOLIX: + sendCOOLIX(data, nbits, min_repeat); + break; +#endif +#if SEND_DAIKIN64 + case DAIKIN64: + sendDaikin64(data, nbits, min_repeat); + break; +#endif +#if SEND_DELONGHI_AC + case DELONGHI_AC: + sendDelonghiAc(data, nbits, min_repeat); + break; +#endif +#if SEND_DENON + case DENON: + sendDenon(data, nbits, min_repeat); + break; +#endif +#if SEND_DISH + case DISH: + sendDISH(data, nbits, min_repeat); + break; +#endif +#if SEND_DOSHISHA + case DOSHISHA: + sendDoshisha(data, nbits, min_repeat); + break; +#endif +#if SEND_ECOCLIM + case ECOCLIM: + sendEcoclim(data, nbits, min_repeat); + break; +#endif // SEND_ECOCLIM +#if SEND_ELITESCREENS + case ELITESCREENS: + sendElitescreens(data, nbits, min_repeat); + break; +#endif // SEND_ELITESCREENS +#if SEND_EPSON + case EPSON: + sendEpson(data, nbits, min_repeat); + break; +#endif +#if SEND_GICABLE + case GICABLE: + sendGICable(data, nbits, min_repeat); + break; +#endif +#if SEND_GOODWEATHER + case GOODWEATHER: + sendGoodweather(data, nbits, min_repeat); + break; +#endif +#if SEND_GREE + case GREE: + sendGree(data, nbits, min_repeat); + break; +#endif +#if SEND_INAX + case INAX: + sendInax(data, nbits, min_repeat); + break; +#endif // SEND_INAX +#if SEND_JVC + case JVC: + sendJVC(data, nbits, min_repeat); + break; +#endif +#if SEND_KELON + case KELON: + sendKelon(data, nbits, min_repeat); + break; +#endif // SEND_KELON +#if SEND_LASERTAG + case LASERTAG: + sendLasertag(data, nbits, min_repeat); + break; +#endif +#if SEND_LEGOPF + case LEGOPF: + sendLegoPf(data, nbits, min_repeat); + break; +#endif +#if SEND_LG + case LG: + sendLG(data, nbits, min_repeat); + break; + case LG2: + sendLG2(data, nbits, min_repeat); + break; +#endif +#if SEND_LUTRON + case LUTRON: + sendLutron(data, nbits, min_repeat); + break; +#endif +#if SEND_MAGIQUEST + case MAGIQUEST: + sendMagiQuest(data, nbits, min_repeat); + break; +#endif // SEND_MAGIQUEST +#if SEND_METZ + case METZ: + sendMetz(data, nbits, min_repeat); + break; +#endif // SEND_METZ +#if SEND_MIDEA + case MIDEA: + sendMidea(data, nbits, min_repeat); + break; +#endif // SEND_MIDEA +#if SEND_MIDEA24 + case MIDEA24: + sendMidea24(data, nbits, min_repeat); + break; +#endif // SEND_MIDEA24 +#if SEND_MILESTAG2 + case MILESTAG2: + sendMilestag2(data, nbits, min_repeat); + break; +#endif // SEND_MILESTAG2 +#if SEND_MITSUBISHI + case MITSUBISHI: + sendMitsubishi(data, nbits, min_repeat); + break; +#endif +#if SEND_MITSUBISHI2 + case MITSUBISHI2: + sendMitsubishi2(data, nbits, min_repeat); + break; +#endif +#if SEND_MULTIBRACKETS + case MULTIBRACKETS: + sendMultibrackets(data, nbits, min_repeat); + break; +#endif +#if SEND_NIKAI + case NIKAI: + sendNikai(data, nbits, min_repeat); + break; +#endif +#if SEND_NEC + case NEC: + case NEC_LIKE: + sendNEC(data, nbits, min_repeat); + break; +#endif +#if SEND_PANASONIC + case PANASONIC: + sendPanasonic64(data, nbits, min_repeat); + break; +#endif // SEND_PANASONIC +#if SEND_PANASONIC_AC32 + case PANASONIC_AC32: + sendPanasonicAC32(data, nbits, min_repeat); + break; +#endif // SEND_PANASONIC_AC32 +#if SEND_PIONEER + case PIONEER: + sendPioneer(data, nbits, min_repeat); + break; +#endif +#if SEND_RC5 + case RC5: + case RC5X: + sendRC5(data, nbits, min_repeat); + break; +#endif +#if SEND_RC6 + case RC6: + sendRC6(data, nbits, min_repeat); + break; +#endif +#if SEND_RCMM + case RCMM: + sendRCMM(data, nbits, min_repeat); + break; +#endif +#if SEND_SAMSUNG + case SAMSUNG: + sendSAMSUNG(data, nbits, min_repeat); + break; +#endif +#if SEND_SAMSUNG36 + case SAMSUNG36: + sendSamsung36(data, nbits, min_repeat); + break; +#endif +#if SEND_SANYO + case SANYO_LC7461: + sendSanyoLC7461(data, nbits, min_repeat); + break; +#endif +#if SEND_SHARP + case SHARP: + sendSharpRaw(data, nbits, min_repeat); + break; +#endif +#if SEND_SHERWOOD + case SHERWOOD: + sendSherwood(data, nbits, min_repeat); + break; +#endif +#if SEND_SONY + case SONY: + sendSony(data, nbits, min_repeat); + break; + case SONY_38K: + sendSony38(data, nbits, min_repeat); + break; +#endif +#if SEND_SYMPHONY + case SYMPHONY: + sendSymphony(data, nbits, min_repeat); + break; +#endif +#if SEND_TECHNIBEL_AC + case TECHNIBEL_AC: + sendTechnibelAc(data, nbits, min_repeat); + break; +#endif +#if SEND_TECO + case TECO: + sendTeco(data, nbits, min_repeat); + break; +#endif // SEND_TECO +#if SEND_TRANSCOLD + case TRANSCOLD: + sendTranscold(data, nbits, min_repeat); + break; +#endif // SEND_TRANSCOLD +#if SEND_TRUMA + case TRUMA: + sendTruma(data, nbits, min_repeat); + break; +#endif // SEND_TRUMA +#if SEND_VESTEL_AC + case VESTEL_AC: + sendVestelAc(data, nbits, min_repeat); + break; +#endif +#if SEND_WHYNTER + case WHYNTER: + sendWhynter(data, nbits, min_repeat); + break; +#endif +#if SEND_XMP + case XMP: + sendXmp(data, nbits, min_repeat); + break; +#endif +#if SEND_ZEPEAL + case ZEPEAL: + sendZepeal(data, nbits, min_repeat); + break; +#endif // SEND_ZEPEAL + default: + return false; + } + return true; +} + +/// Send a complex (>= 64 bits) IR message of a given type. +/// An unknown/unsupported type will send nothing. +/// @param[in] type Protocol number/type of the message you want to send. +/// @param[in] state A pointer to the array of bytes that make up the state[]. +/// @param[in] nbytes How many bytes are in the state. +/// @return True if it is a type we can attempt to send, false if not. +bool IRsend::send(const decode_type_t type, const uint8_t *state, + const uint16_t nbytes) { + switch (type) { +#if SEND_VOLTAS + case VOLTAS: + sendVoltas(state, nbytes); + break; +#endif // SEND_VOLTAS +#if SEND_AMCOR + case AMCOR: + sendAmcor(state, nbytes); + break; +#endif +#if SEND_ARGO + case ARGO: + sendArgo(state, nbytes); + break; +#endif // SEND_ARGO +#if SEND_CORONA_AC + case CORONA_AC: + sendCoronaAc(state, nbytes); + break; +#endif // SEND_ARGO +#if SEND_DAIKIN + case DAIKIN: + sendDaikin(state, nbytes); + break; +#endif // SEND_DAIKIN +#if SEND_DAIKIN128 + case DAIKIN128: + sendDaikin128(state, nbytes); + break; +#endif // SEND_DAIKIN128 +#if SEND_DAIKIN152 + case DAIKIN152: + sendDaikin152(state, nbytes); + break; +#endif // SEND_DAIKIN152 +#if SEND_DAIKIN160 + case DAIKIN160: + sendDaikin160(state, nbytes); + break; +#endif // SEND_DAIKIN160 +#if SEND_DAIKIN176 + case DAIKIN176: + sendDaikin176(state, nbytes); + break; +#endif // SEND_DAIKIN176 +#if SEND_DAIKIN2 + case DAIKIN2: + sendDaikin2(state, nbytes); + break; +#endif // SEND_DAIKIN2 +#if SEND_DAIKIN216 + case DAIKIN216: + sendDaikin216(state, nbytes); + break; +#endif // SEND_DAIKIN216 +#if SEND_ELECTRA_AC + case ELECTRA_AC: + sendElectraAC(state, nbytes); + break; +#endif // SEND_ELECTRA_AC +#if SEND_FUJITSU_AC + case FUJITSU_AC: + sendFujitsuAC(state, nbytes); + break; +#endif // SEND_FUJITSU_AC +#if SEND_GREE + case GREE: + sendGree(state, nbytes); + break; +#endif // SEND_GREE +#if SEND_HAIER_AC + case HAIER_AC: + sendHaierAC(state, nbytes); + break; +#endif // SEND_HAIER_AC +#if SEND_HAIER_AC_YRW02 + case HAIER_AC_YRW02: + sendHaierACYRW02(state, nbytes); + break; +#endif // SEND_HAIER_AC_YRW02 +#if SEND_HAIER_AC176 + case HAIER_AC176: + sendHaierAC176(state, nbytes); + break; +#endif // SEND_HAIER_AC176 +#if SEND_HITACHI_AC + case HITACHI_AC: + sendHitachiAC(state, nbytes); + break; +#endif // SEND_HITACHI_AC +#if SEND_HITACHI_AC1 + case HITACHI_AC1: + sendHitachiAC1(state, nbytes); + break; +#endif // SEND_HITACHI_AC1 +#if SEND_HITACHI_AC2 + case HITACHI_AC2: + sendHitachiAC2(state, nbytes); + break; +#endif // SEND_HITACHI_AC2 +#if SEND_HITACHI_AC3 + case HITACHI_AC3: + sendHitachiAc3(state, nbytes); + break; +#endif // SEND_HITACHI_AC3 +#if SEND_HITACHI_AC344 + case HITACHI_AC344: + sendHitachiAc344(state, nbytes); + break; +#endif // SEND_HITACHI_AC344 +#if SEND_HITACHI_AC424 + case HITACHI_AC424: + sendHitachiAc424(state, nbytes); + break; +#endif // SEND_HITACHI_AC424 +#if SEND_KELVINATOR + case KELVINATOR: + sendKelvinator(state, nbytes); + break; +#endif // SEND_KELVINATOR +#if SEND_MIRAGE + case MIRAGE: + sendMirage(state, nbytes); + break; +#endif // SEND_MIRAGE +#if SEND_MITSUBISHI_AC + case MITSUBISHI_AC: + sendMitsubishiAC(state, nbytes); + break; +#endif // SEND_MITSUBISHI_AC +#if SEND_MITSUBISHI136 + case MITSUBISHI136: + sendMitsubishi136(state, nbytes); + break; +#endif // SEND_MITSUBISHI136 +#if SEND_MITSUBISHI112 + case MITSUBISHI112: + sendMitsubishi112(state, nbytes); + break; +#endif // SEND_MITSUBISHI112 +#if SEND_MITSUBISHIHEAVY + case MITSUBISHI_HEAVY_88: + sendMitsubishiHeavy88(state, nbytes); + break; + case MITSUBISHI_HEAVY_152: + sendMitsubishiHeavy152(state, nbytes); + break; +#endif // SEND_MITSUBISHIHEAVY +#if SEND_MWM + case MWM: + sendMWM(state, nbytes); + break; +#endif // SEND_MWM +#if SEND_NEOCLIMA + case NEOCLIMA: + sendNeoclima(state, nbytes); + break; +#endif // SEND_NEOCLIMA +#if SEND_PANASONIC_AC + case PANASONIC_AC: + sendPanasonicAC(state, nbytes); + break; +#endif // SEND_PANASONIC_AC +#if SEND_RHOSS + case RHOSS: + sendRhoss(state, nbytes); + break; +#endif // SEND_RHOSS +#if SEND_SAMSUNG_AC + case SAMSUNG_AC: + sendSamsungAC(state, nbytes); + break; +#endif // SEND_SAMSUNG_AC +#if SEND_SANYO_AC + case SANYO_AC: + sendSanyoAc(state, nbytes); + break; +#endif // SEND_SANYO_AC +#if SEND_SANYO_AC88 + case SANYO_AC88: + sendSanyoAc88(state, nbytes); + break; +#endif // SEND_SANYO_AC88 +#if SEND_SHARP_AC + case SHARP_AC: + sendSharpAc(state, nbytes); + break; +#endif // SEND_SHARP_AC +#if SEND_TCL112AC + case TCL112AC: + sendTcl112Ac(state, nbytes); + break; +#endif // SEND_TCL112AC +#if SEND_TEKNOPOINT + case TEKNOPOINT: + sendTeknopoint(state, nbytes); + break; +#endif // SEND_TEKNOPOINT +#if SEND_TOSHIBA_AC + case TOSHIBA_AC: + sendToshibaAC(state, nbytes); + break; +#endif // SEND_TOSHIBA_AC +#if SEND_TROTEC + case TROTEC: + sendTrotec(state, nbytes); + break; +#endif // SEND_TROTEC +#if SEND_TROTEC_3550 + case TROTEC_3550: + sendTrotec3550(state, nbytes); + break; +#endif // SEND_TROTEC_3550 +#if SEND_WHIRLPOOL_AC + case WHIRLPOOL_AC: + sendWhirlpoolAC(state, nbytes); + break; +#endif // SEND_WHIRLPOOL_AC + default: + return false; + } + return true; +} diff --git a/lib/IRremoteESP8266/src/IRsend.h b/lib/IRremoteESP8266/src/IRsend.h index a4ceba12cf..c09ec3d4cf 100644 --- a/lib/IRremoteESP8266/src/IRsend.h +++ b/lib/IRremoteESP8266/src/IRsend.h @@ -6,7 +6,7 @@ #define __STDC_LIMIT_MACROS #include -#include "../src/IRremoteESP8266.h" +#include "IRremoteESP8266.h" // Originally from https://github.com/shirriff/Arduino-IRremote/ // Updated by markszabo (https://github.com/crankyoldgit/IRremoteESP8266) for diff --git a/lib/IRremoteESP8266/src/IRtext.cpp b/lib/IRremoteESP8266/src/IRtext.cpp new file mode 100644 index 0000000000..7866b47f9a --- /dev/null +++ b/lib/IRremoteESP8266/src/IRtext.cpp @@ -0,0 +1,383 @@ +// 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 +#endif // UNIT_TEST +#include "IRremoteESP8266.h" +#include "i18n.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(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(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(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(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(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" (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(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" + +// 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(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(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" + +// Protocol Names +// Needs to be in decode_type_t order. +IRTEXT_CONST_BLOB_DECL(kAllProtocolNamesStr) { + D_STR_UNUSED "\x0" + D_STR_RC5 "\x0" + D_STR_RC6 "\x0" + D_STR_NEC "\x0" + D_STR_SONY "\x0" + D_STR_PANASONIC "\x0" + D_STR_JVC "\x0" + D_STR_SAMSUNG "\x0" + D_STR_WHYNTER "\x0" + D_STR_AIWA_RC_T501 "\x0" + D_STR_LG "\x0" + D_STR_SANYO "\x0" + D_STR_MITSUBISHI "\x0" + D_STR_DISH "\x0" + D_STR_SHARP "\x0" + D_STR_COOLIX "\x0" + D_STR_DAIKIN "\x0" + D_STR_DENON "\x0" + D_STR_KELVINATOR "\x0" + D_STR_SHERWOOD "\x0" + D_STR_MITSUBISHI_AC "\x0" + D_STR_RCMM "\x0" + D_STR_SANYO_LC7461 "\x0" + D_STR_RC5X "\x0" + D_STR_GREE "\x0" + D_STR_PRONTO "\x0" + D_STR_NEC_LIKE "\x0" + D_STR_ARGO "\x0" + D_STR_TROTEC "\x0" + D_STR_NIKAI "\x0" + D_STR_RAW "\x0" + D_STR_GLOBALCACHE "\x0" + D_STR_TOSHIBA_AC "\x0" + D_STR_FUJITSU_AC "\x0" + D_STR_MIDEA "\x0" + D_STR_MAGIQUEST "\x0" + D_STR_LASERTAG "\x0" + D_STR_CARRIER_AC "\x0" + D_STR_HAIER_AC "\x0" + D_STR_MITSUBISHI2 "\x0" + D_STR_HITACHI_AC "\x0" + D_STR_HITACHI_AC1 "\x0" + D_STR_HITACHI_AC2 "\x0" + D_STR_GICABLE "\x0" + D_STR_HAIER_AC_YRW02 "\x0" + D_STR_WHIRLPOOL_AC "\x0" + D_STR_SAMSUNG_AC "\x0" + D_STR_LUTRON "\x0" + D_STR_ELECTRA_AC "\x0" + D_STR_PANASONIC_AC "\x0" + D_STR_PIONEER "\x0" + D_STR_LG2 "\x0" + D_STR_MWM "\x0" + D_STR_DAIKIN2 "\x0" + D_STR_VESTEL_AC "\x0" + D_STR_TECO "\x0" + D_STR_SAMSUNG36 "\x0" + D_STR_TCL112AC "\x0" + D_STR_LEGOPF "\x0" + D_STR_MITSUBISHI_HEAVY_88 "\x0" + D_STR_MITSUBISHI_HEAVY_152 "\x0" + D_STR_DAIKIN216 "\x0" + D_STR_SHARP_AC "\x0" + D_STR_GOODWEATHER "\x0" + D_STR_INAX "\x0" + D_STR_DAIKIN160 "\x0" + D_STR_NEOCLIMA "\x0" + D_STR_DAIKIN176 "\x0" + D_STR_DAIKIN128 "\x0" + D_STR_AMCOR "\x0" + D_STR_DAIKIN152 "\x0" + D_STR_MITSUBISHI136 "\x0" + D_STR_MITSUBISHI112 "\x0" + D_STR_HITACHI_AC424 "\x0" + D_STR_SONY_38K "\x0" + D_STR_EPSON "\x0" + D_STR_SYMPHONY "\x0" + D_STR_HITACHI_AC3 "\x0" + D_STR_DAIKIN64 "\x0" + D_STR_AIRWELL "\x0" + D_STR_DELONGHI_AC "\x0" + D_STR_DOSHISHA "\x0" + D_STR_MULTIBRACKETS "\x0" + D_STR_CARRIER_AC40 "\x0" + D_STR_CARRIER_AC64 "\x0" + D_STR_HITACHI_AC344 "\x0" + D_STR_CORONA_AC "\x0" + D_STR_MIDEA24 "\x0" + D_STR_ZEPEAL "\x0" + D_STR_SANYO_AC "\x0" + D_STR_VOLTAS "\x0" + D_STR_METZ "\x0" + D_STR_TRANSCOLD "\x0" + D_STR_TECHNIBEL_AC "\x0" + D_STR_MIRAGE "\x0" + D_STR_ELITESCREENS "\x0" + D_STR_PANASONIC_AC32 "\x0" + D_STR_MILESTAG2 "\x0" + D_STR_ECOCLIM "\x0" + D_STR_XMP "\x0" + D_STR_TRUMA "\x0" + D_STR_HAIER_AC176 "\x0" + D_STR_TEKNOPOINT "\x0" + D_STR_KELON "\x0" + D_STR_TROTEC_3550 "\x0" + D_STR_SANYO_AC88 "\x0" + D_STR_BOSE "\x0" + D_STR_ARRIS "\x0" + D_STR_RHOSS "\x0" + ///< New protocol strings should be added just above this line. + "\x0" ///< This string requires double null termination. +}; + +IRTEXT_CONST_BLOB_PTR(kAllProtocolNamesStr); diff --git a/lib/IRremoteESP8266/src/IRtimer.cpp b/lib/IRremoteESP8266/src/IRtimer.cpp new file mode 100644 index 0000000000..e1f098b280 --- /dev/null +++ b/lib/IRremoteESP8266/src/IRtimer.cpp @@ -0,0 +1,78 @@ +// Copyright 2017 David Conran + +#include "IRtimer.h" +#ifndef UNIT_TEST +#include +#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 diff --git a/lib/IRremoteESP8266/src/IRutils.cpp b/lib/IRremoteESP8266/src/IRutils.cpp new file mode 100644 index 0000000000..bebb610776 --- /dev/null +++ b/lib/IRremoteESP8266/src/IRutils.cpp @@ -0,0 +1,1266 @@ +// Copyright 2017-2021 David Conran + +#include "IRutils.h" +#ifndef UNIT_TEST +#include +#endif + +#define __STDC_LIMIT_MACROS +#include +#include +#include +#ifndef ARDUINO +#include +#endif +#include "IRrecv.h" +#include "IRremoteESP8266.h" +#include "IRsend.h" +#include "IRtext.h" + +// On the ESP8266 platform we need to use a set of ..._P functions +// to handle the strings stored in the flash address space. +#ifndef STRCASECMP +#if defined(ESP8266) +#define STRCASECMP(LHS, RHS) \ + strcasecmp_P(LHS, reinterpret_cast(RHS)) +#else // ESP8266 +#define STRCASECMP strcasecmp +#endif // ESP8266 +#endif // STRCASECMP +#ifndef STRLEN +#if defined(ESP8266) +#define STRLEN(PTR) strlen_P(PTR) +#else // ESP8266 +#define STRLEN(PTR) strlen(PTR) +#endif // ESP8266 +#endif // STRLEN +#ifndef FPSTR +#define FPSTR(X) X +#endif // FPSTR + +/// Reverse the order of the requested least significant nr. of bits. +/// @param[in] input Bit pattern/integer to reverse. +/// @param[in] nbits Nr. of bits to reverse. (LSB -> MSB) +/// @return The reversed bit pattern. +uint64_t reverseBits(uint64_t input, uint16_t nbits) { + if (nbits <= 1) return input; // Reversing <= 1 bits makes no change at all. + // Cap the nr. of bits to rotate to the max nr. of bits in the input. + nbits = std::min(nbits, (uint16_t)(sizeof(input) * 8)); + uint64_t output = 0; + for (uint16_t i = 0; i < nbits; i++) { + output <<= 1; + output |= (input & 1); + input >>= 1; + } + // Merge any remaining unreversed bits back to the top of the reversed bits. + return (input << nbits) | output; +} + +/// Convert a uint64_t (unsigned long long) to a string. +/// Arduino String/toInt/Serial.print() can't handle printing 64 bit values. +/// @param[in] input The value to print +/// @param[in] base The output base. +/// @returns A String representation of the integer. +/// @note Based on Arduino's Print::printNumber() +String uint64ToString(uint64_t input, uint8_t base) { + String result = ""; + // prevent issues if called with base <= 1 + if (base < 2) base = 10; + // Check we have a base that we can actually print. + // i.e. [0-9A-Z] == 36 + if (base > 36) base = 10; + + // Reserve some string space to reduce fragmentation. + // 16 bytes should store a uint64 in hex text which is the likely worst case. + // 64 bytes would be the worst case (base 2). + result.reserve(16); + + do { + char c = input % base; + input /= base; + + if (c < 10) + c += '0'; + else + c += 'A' - 10; + result = c + result; + } while (input); + return result; +} + +/// Convert a int64_t (signed long long) to a string. +/// Arduino String/toInt/Serial.print() can't handle printing 64 bit values. +/// @param[in] input The value to print +/// @param[in] base The output base. +/// @returns A String representation of the integer. +String int64ToString(int64_t input, uint8_t base) { + if (input < 0) { + return String(kDashStr) + uint64ToString(-input, base); + } + return uint64ToString(input, base); +} + +#ifdef ARDUINO +/// Print a uint64_t/unsigned long long to the Serial port +/// Serial.print() can't handle printing long longs. (uint64_t) +/// @param[in] input The value to print +/// @param[in] base The output base. +void serialPrintUint64(uint64_t input, uint8_t base) { + Serial.print(uint64ToString(input, base)); +} +#endif + +/// Convert a C-style string to a decode_type_t. +/// @param[in] str A C-style string containing a protocol name or number. +/// @return A decode_type_t enum. (decode_type_t::UNKNOWN if no match.) +decode_type_t strToDecodeType(const char * const str) { + auto *ptr = reinterpret_cast(kAllProtocolNamesStr); + uint16_t length = STRLEN(ptr); + for (uint16_t i = 0; length; i++) { + if (!STRCASECMP(str, ptr)) return (decode_type_t)i; + ptr += length + 1; + length = STRLEN(ptr); + } + // Handle integer values of the type by converting to a string and back again. + decode_type_t result = strToDecodeType( + typeToString((decode_type_t)atoi(str)).c_str()); + if (result > 0) + return result; + + return decode_type_t::UNKNOWN; +} + +/// Convert a protocol type (enum etc) to a human readable string. +/// @param[in] protocol Nr. (enum) of the protocol. +/// @param[in] isRepeat A flag indicating if it is a repeat message. +/// @return A String containing the protocol name. kUnknownStr if no match. +String typeToString(const decode_type_t protocol, const bool isRepeat) { + String result = ""; + result.reserve(30); // Size of longest protocol name + " (Repeat)" + if (protocol > kLastDecodeType || protocol == decode_type_t::UNKNOWN) { + result = kUnknownStr; + } else { + auto *ptr = reinterpret_cast(kAllProtocolNamesStr); + if (protocol > kLastDecodeType || protocol == decode_type_t::UNKNOWN) { + result = kUnknownStr; + } else { + for (uint16_t i = 0; i <= protocol && STRLEN(ptr); i++) { + if (i == protocol) { + result = FPSTR(ptr); + break; + } + ptr += STRLEN(ptr) + 1; + } + } + } + if (isRepeat) { + result += kSpaceLBraceStr; + result += kRepeatStr; + result += ')'; + } + return result; +} + +/// Does the given protocol use a complex state as part of the decode? +/// @param[in] protocol The decode_type_t protocol we are enquiring about. +/// @return True if the protocol uses a state array. False if just an integer. +bool hasACState(const decode_type_t protocol) { + switch (protocol) { + // This is kept sorted by name + case AMCOR: + case ARGO: + case CORONA_AC: + case DAIKIN: + case DAIKIN128: + case DAIKIN152: + case DAIKIN160: + case DAIKIN176: + case DAIKIN2: + case DAIKIN216: + case ELECTRA_AC: + case FUJITSU_AC: + case GREE: + case HAIER_AC: + case HAIER_AC_YRW02: + case HAIER_AC176: + case HITACHI_AC: + case HITACHI_AC1: + case HITACHI_AC2: + case HITACHI_AC3: + case HITACHI_AC344: + case HITACHI_AC424: + case KELVINATOR: + case MIRAGE: + case MITSUBISHI136: + case MITSUBISHI112: + case MITSUBISHI_AC: + case MITSUBISHI_HEAVY_88: + case MITSUBISHI_HEAVY_152: + case MWM: + case NEOCLIMA: + case PANASONIC_AC: + case RHOSS: + case SAMSUNG_AC: + case SANYO_AC: + case SANYO_AC88: + case SHARP_AC: + case TCL112AC: + case TEKNOPOINT: + case TOSHIBA_AC: + case TROTEC: + case TROTEC_3550: + case VOLTAS: + case WHIRLPOOL_AC: + return true; + default: + return false; + } +} + +/// Return the corrected length of a 'raw' format array structure +/// after over-large values are converted into multiple entries. +/// @param[in] results A ptr to a decode_results structure. +/// @return The corrected length. +uint16_t getCorrectedRawLength(const decode_results * const results) { + uint16_t extended_length = results->rawlen - 1; + for (uint16_t i = 0; i < results->rawlen - 1; i++) { + uint32_t usecs = results->rawbuf[i] * kRawTick; + // Add two extra entries for multiple larger than UINT16_MAX it is. + extended_length += (usecs / (UINT16_MAX + 1)) * 2; + } + return extended_length; +} + +/// Return a String containing the key values of a decode_results structure +/// in a C/C++ code style format. +/// @param[in] results A ptr to a decode_results structure. +/// @return A String containing the code-ified result. +String resultToSourceCode(const decode_results * const results) { + String output = ""; + const uint16_t length = getCorrectedRawLength(results); + const bool hasState = hasACState(results->decode_type); + // Reserve some space for the string to reduce heap fragmentation. + // "uint16_t rawData[9999] = {}; // LONGEST_PROTOCOL\n" = ~55 chars. + // "NNNN, " = ~7 chars on average per raw entry + // Protocols with a `state`: + // "uint8_t state[NN] = {};\n" = ~25 chars + // "0xNN, " = 6 chars per byte. + // Protocols without a `state`: + // " DEADBEEFDEADBEEF\n" + // "uint32_t address = 0xDEADBEEF;\n" + // "uint32_t command = 0xDEADBEEF;\n" + // "uint64_t data = 0xDEADBEEFDEADBEEF;" = ~116 chars max. + output.reserve(55 + (length * 7) + hasState ? 25 + (results->bits / 8) * 6 + : 116); + // Start declaration + output += F("uint16_t "); // variable type + output += F("rawData["); // array name + output += uint64ToString(length, 10); + // array size + output += F("] = {"); // Start declaration + + // Dump data + for (uint16_t i = 1; i < results->rawlen; i++) { + uint32_t usecs; + for (usecs = results->rawbuf[i] * kRawTick; usecs > UINT16_MAX; + usecs -= UINT16_MAX) { + output += uint64ToString(UINT16_MAX); + if (i % 2) + output += F(", 0, "); + else + output += F(", 0, "); + } + output += uint64ToString(usecs, 10); + if (i < results->rawlen - 1) + output += kCommaSpaceStr; // ',' not needed on the last one + if (i % 2 == 0) output += ' '; // Extra if it was even. + } + + // End declaration + output += F("};"); + + // Comment + output += F(" // "); + output += typeToString(results->decode_type, results->repeat); + // Only display the value if the decode type doesn't have an A/C state. + if (!hasState) + output += ' ' + uint64ToString(results->value, 16); + output += F("\n"); + + // Now dump "known" codes + if (results->decode_type != UNKNOWN) { + if (hasState) { +#if DECODE_AC + uint16_t nbytes = results->bits / 8; + output += F("uint8_t state["); + output += uint64ToString(nbytes); + output += F("] = {"); + for (uint16_t i = 0; i < nbytes; i++) { + output += F("0x"); + if (results->state[i] < 0x10) output += '0'; + output += uint64ToString(results->state[i], 16); + if (i < nbytes - 1) output += kCommaSpaceStr; + } + output += F("};\n"); +#endif // DECODE_AC + } else { + // Simple protocols + // Some protocols have an address &/or command. + // NOTE: It will ignore the atypical case when a message has been + // decoded but the address & the command are both 0. + if (results->address > 0 || results->command > 0) { + output += F("uint32_t address = 0x"); + output += uint64ToString(results->address, 16); + output += F(";\n"); + output += F("uint32_t command = 0x"); + output += uint64ToString(results->command, 16); + output += F(";\n"); + } + // Most protocols have data + output += F("uint64_t data = 0x"); + output += uint64ToString(results->value, 16); + output += F(";\n"); + } + } + return output; +} + +/// Dump out the decode_results structure. +/// @param[in] results A ptr to a decode_results structure. +/// @return A String containing the legacy information format. +/// @deprecated This is only for those that want this legacy format. +String resultToTimingInfo(const decode_results * const results) { + String output = ""; + String value = ""; + // Reserve some space for the string to reduce heap fragmentation. + // "Raw Timing[NNNN]:\n\n" = 19 chars + // " +123456, " / "-123456, " = ~12 chars on avg per raw entry. + output.reserve(19 + 12 * results->rawlen); // Should be less than this. + value.reserve(6); // Max value should be 2^17 = 131072 + output += F("Raw Timing["); + output += uint64ToString(results->rawlen - 1, 10); + output += F("]:\n"); + + for (uint16_t i = 1; i < results->rawlen; i++) { + if (i % 2 == 0) + output += kDashStr; // even + else + output += F(" +"); // odd + value = uint64ToString(results->rawbuf[i] * kRawTick); + // Space pad the value till it is at least 6 chars long. + while (value.length() < 6) value = ' ' + value; + output += value; + if (i < results->rawlen - 1) + output += kCommaSpaceStr; // ',' not needed for last one + if (!(i % 8)) output += '\n'; // Newline every 8 entries. + } + output += '\n'; + return output; +} + +/// Convert the decode_results structure's value/state to simple hexadecimal. +/// @param[in] result A ptr to a decode_results structure. +/// @return A String containing the output. +String resultToHexidecimal(const decode_results * const result) { + String output = F("0x"); + // Reserve some space for the string to reduce heap fragmentation. + output.reserve(2 * kStateSizeMax + 2); // Should cover worst cases. + if (hasACState(result->decode_type)) { +#if DECODE_AC + for (uint16_t i = 0; result->bits > i * 8; i++) { + if (result->state[i] < 0x10) output += '0'; // Zero pad + output += uint64ToString(result->state[i], 16); + } +#endif // DECODE_AC + } else { + output += uint64ToString(result->value, 16); + } + return output; +} + +/// Dump out the decode_results structure into a human readable format. +/// @param[in] results A ptr to a decode_results structure. +/// @return A String containing the output. +String resultToHumanReadableBasic(const decode_results * const results) { + String output = ""; + // Reserve some space for the string to reduce heap fragmentation. + // "Protocol : LONGEST_PROTOCOL_NAME (Repeat)\n" + // "Code : 0x (NNNN Bits)\n" = 70 chars + output.reserve(2 * kStateSizeMax + 70); // Should cover most cases. + // Show Encoding standard + output += kProtocolStr; + output += F(" : "); + output += typeToString(results->decode_type, results->repeat); + output += '\n'; + + // Show Code & length + output += kCodeStr; + output += F(" : "); + output += resultToHexidecimal(results); + output += kSpaceLBraceStr; + output += uint64ToString(results->bits); + output += ' '; + output += kBitsStr; + output += F(")\n"); + return output; +} + +/// Convert a decode_results into an array suitable for `sendRaw()`. +/// @param[in] decode A ptr to a decode_results structure that contains a mesg. +/// @return A PTR to a dynamically allocated uint16_t sendRaw compatible array. +/// @note The returned array needs to be delete[]'ed/free()'ed (deallocated) +/// after use by caller. +uint16_t* resultToRawArray(const decode_results * const decode) { + uint16_t *result = new uint16_t[getCorrectedRawLength(decode)]; + if (result != NULL) { // The memory was allocated successfully. + // Convert the decode data. + uint16_t pos = 0; + for (uint16_t i = 1; i < decode->rawlen; i++) { + uint32_t usecs = decode->rawbuf[i] * kRawTick; + while (usecs > UINT16_MAX) { // Keep truncating till it fits. + result[pos++] = UINT16_MAX; + result[pos++] = 0; // A 0 in a sendRaw() array basically means skip. + usecs -= UINT16_MAX; + } + result[pos++] = usecs; + } + } + return result; +} + +/// Sum all the bytes of an array and return the least significant 8-bits of +/// the result. +/// @param[in] start A ptr to the start of the byte array to calculate over. +/// @param[in] length How many bytes to use in the calculation. +/// @param[in] init Starting value of the calculation to use. (Default is 0) +/// @return The 8-bit calculated result of all the bytes and init value. +uint8_t sumBytes(const uint8_t * const start, const uint16_t length, + const uint8_t init) { + uint8_t checksum = init; + const uint8_t *ptr; + for (ptr = start; ptr - start < length; ptr++) checksum += *ptr; + return checksum; +} + +/// Calculate a rolling XOR of all the bytes of an array. +/// @param[in] start A ptr to the start of the byte array to calculate over. +/// @param[in] length How many bytes to use in the calculation. +/// @param[in] init Starting value of the calculation to use. (Default is 0) +/// @return The 8-bit calculated result of all the bytes and init value. +uint8_t xorBytes(const uint8_t * const start, const uint16_t length, + const uint8_t init) { + uint8_t checksum = init; + const uint8_t *ptr; + for (ptr = start; ptr - start < length; ptr++) checksum ^= *ptr; + return checksum; +} + +/// Count the number of bits of a certain type in an array. +/// @param[in] start A ptr to the start of the byte array to calculate over. +/// @param[in] length How many bytes to use in the calculation. +/// @param[in] ones Count the binary nr of `1` bits. False is count the `0`s. +/// @param[in] init Starting value of the calculation to use. (Default is 0) +/// @return The nr. of bits found of the given type found in the array. +uint16_t countBits(const uint8_t * const start, const uint16_t length, + const bool ones, const uint16_t init) { + uint16_t count = init; + for (uint16_t offset = 0; offset < length; offset++) + for (uint8_t currentbyte = *(start + offset); + currentbyte; + currentbyte >>= 1) + if (currentbyte & 1) count++; + if (ones || length == 0) + return count; + else + return (length * 8) - count; +} + +/// Count the number of bits of a certain type in an Integer. +/// @param[in] data The value you want bits counted for. Starting from the LSB. +/// @param[in] length How many bits to use in the calculation? Starts at the LSB +/// @param[in] ones Count the binary nr of `1` bits. False is count the `0`s. +/// @param[in] init Starting value of the calculation to use. (Default is 0) +/// @return The nr. of bits found of the given type found in the Integer. +uint16_t countBits(const uint64_t data, const uint8_t length, const bool ones, + const uint16_t init) { + uint16_t count = init; + uint8_t bitsSoFar = length; + for (uint64_t remainder = data; remainder && bitsSoFar; + remainder >>= 1, bitsSoFar--) + if (remainder & 1) count++; + if (ones || length == 0) + return count; + else + return length - count; +} + +/// Invert/Flip the bits in an Integer. +/// @param[in] data The Integer that will be inverted. +/// @param[in] nbits How many bits are to be inverted. Starting from the LSB. +/// @return An Integer with the appropriate bits inverted/flipped. +uint64_t invertBits(const uint64_t data, const uint16_t nbits) { + // No change if we are asked to invert no bits. + if (nbits == 0) return data; + uint64_t result = ~data; + // If we are asked to invert all the bits or more than we have, it's simple. + if (nbits >= sizeof(data) * 8) return result; + // Mask off any unwanted bits and return the result. + return (result & ((1ULL << nbits) - 1)); +} + +/// Convert degrees Celsius to degrees Fahrenheit. +float celsiusToFahrenheit(const float deg) { return (deg * 9.0) / 5.0 + 32.0; } + +/// Convert degrees Fahrenheit to degrees Celsius. +float fahrenheitToCelsius(const float deg) { return (deg - 32.0) * 5.0 / 9.0; } + +namespace irutils { + /// Create a String with a colon separated "label: value" pair suitable for + /// Humans. + /// @param[in] value The value to come after the label. + /// @param[in] label The label to precede the value. + /// @param[in] precomma Should the output string start with ", " or not? + /// @return The resulting String. + String addLabeledString(const String value, const String label, + const bool precomma) { + String result = ""; + // ", " + ": " = 4 chars + result.reserve(4 + value.length() + label.length()); + if (precomma) result += kCommaSpaceStr; + result += label; + result += kColonSpaceStr; + return result + value; + } + + /// Create a String with a colon separated flag suitable for Humans. + /// e.g. "Power: On" + /// @param[in] value The value to come after the label. + /// @param[in] label The label to precede the value. + /// @param[in] precomma Should the output string start with ", " or not? + /// @return The resulting String. + String addBoolToString(const bool value, const String label, + const bool precomma) { + return addLabeledString(value ? kOnStr : kOffStr, label, precomma); + } + + /// Create a String with a colon separated toggle flag suitable for Humans. + /// e.g. "Light: Toggle", "Light: -" + /// @param[in] toggle The value of the toggle to come after the label. + /// @param[in] label The label to precede the value. + /// @param[in] precomma Should the output string start with ", " or not? + /// @return The resulting String. + String addToggleToString(const bool toggle, const String label, + const bool precomma) { + return addLabeledString(toggle ? kToggleStr : kDashStr, label, precomma); + } + + /// Create a String with a colon separated labeled Integer suitable for + /// Humans. + /// e.g. "Foo: 23" + /// @param[in] value The value to come after the label. + /// @param[in] label The label to precede the value. + /// @param[in] precomma Should the output string start with ", " or not? + /// @return The resulting String. + String addIntToString(const uint16_t value, const String label, + const bool precomma) { + return addLabeledString(uint64ToString(value), label, precomma); + } + + /// Create a String with a colon separated labeled Integer suitable for + /// Humans. + /// e.g. "Foo: 23" + /// @param[in] value The value to come after the label. + /// @param[in] label The label to precede the value. + /// @param[in] precomma Should the output string start with ", " or not? + /// @return The resulting String. + String addSignedIntToString(const int16_t value, const String label, + const bool precomma) { + return addLabeledString(int64ToString(value), label, precomma); + } + + + /// Generate the model string for a given Protocol/Model pair. + /// @param[in] protocol The IR protocol. + /// @param[in] model The model number for that protocol. + /// @return The resulting String. + String modelToStr(const decode_type_t protocol, const int16_t model) { + switch (protocol) { + case decode_type_t::FUJITSU_AC: + switch (model) { + case fujitsu_ac_remote_model_t::ARRAH2E: return kArrah2eStr; + case fujitsu_ac_remote_model_t::ARDB1: return kArdb1Str; + case fujitsu_ac_remote_model_t::ARREB1E: return kArreb1eStr; + case fujitsu_ac_remote_model_t::ARJW2: return kArjw2Str; + case fujitsu_ac_remote_model_t::ARRY4: return kArry4Str; + case fujitsu_ac_remote_model_t::ARREW4E: return kArrew4eStr; + default: return kUnknownStr; + } + break; + case decode_type_t::GREE: + switch (model) { + case gree_ac_remote_model_t::YAW1F: return kYaw1fStr; + case gree_ac_remote_model_t::YBOFB: return kYbofbStr; + default: return kUnknownStr; + } + break; + case decode_type_t::HITACHI_AC1: + switch (model) { + case hitachi_ac1_remote_model_t::R_LT0541_HTA_A: + return kRlt0541htaaStr; + case hitachi_ac1_remote_model_t::R_LT0541_HTA_B: + return kRlt0541htabStr; + default: + return kUnknownStr; + } + break; + case decode_type_t::LG: + case decode_type_t::LG2: + switch (model) { + case lg_ac_remote_model_t::GE6711AR2853M: return kGe6711ar2853mStr; + case lg_ac_remote_model_t::AKB75215403: return kAkb75215403Str; + case lg_ac_remote_model_t::AKB74955603: return kAkb74955603Str; + case lg_ac_remote_model_t::AKB73757604: return kAkb73757604Str; + default: return kUnknownStr; + } + break; + case decode_type_t::PANASONIC_AC: + switch (model) { + case panasonic_ac_remote_model_t::kPanasonicLke: return kLkeStr; + case panasonic_ac_remote_model_t::kPanasonicNke: return kNkeStr; + case panasonic_ac_remote_model_t::kPanasonicDke: return kDkeStr; + case panasonic_ac_remote_model_t::kPanasonicJke: return kJkeStr; + case panasonic_ac_remote_model_t::kPanasonicCkp: return kCkpStr; + case panasonic_ac_remote_model_t::kPanasonicRkr: return kRkrStr; + default: return kUnknownStr; + } + break; + case decode_type_t::SHARP_AC: + switch (model) { + case sharp_ac_remote_model_t::A907: return kA907Str; + case sharp_ac_remote_model_t::A705: return kA705Str; + case sharp_ac_remote_model_t::A903: return kA903Str; + default: return kUnknownStr; + } + break; + case decode_type_t::TCL112AC: + switch (model) { + case tcl_ac_remote_model_t::TAC09CHSD: return kTac09chsdStr; + case tcl_ac_remote_model_t::GZ055BE1: return kGz055be1Str; + default: return kUnknownStr; + } + break; + case decode_type_t::VOLTAS: + switch (model) { + case voltas_ac_remote_model_t::kVoltas122LZF: return k122lzfStr; + default: return kUnknownStr; + } + break; + case decode_type_t::WHIRLPOOL_AC: + switch (model) { + case whirlpool_ac_remote_model_t::DG11J13A: return kDg11j13aStr; + case whirlpool_ac_remote_model_t::DG11J191: return kDg11j191Str; + default: return kUnknownStr; + } + break; + default: return kUnknownStr; + } + } + + /// Create a String of human output for a given protocol model number. + /// e.g. "Model: JKE" + /// @param[in] protocol The IR protocol. + /// @param[in] model The model number for that protocol. + /// @param[in] precomma Should the output string start with ", " or not? + /// @return The resulting String. + String addModelToString(const decode_type_t protocol, const int16_t model, + const bool precomma) { + String result = ""; + // ", Model: NNN (BlahBlahEtc)" = ~40 chars for longest model name. + result.reserve(40); + result += addIntToString(model, kModelStr, precomma); + result += kSpaceLBraceStr; + result += modelToStr(protocol, model); + return result + ')'; + } + + /// Create a String of human output for a given temperature. + /// e.g. "Temp: 25C" + /// @param[in] degrees The temperature in degrees. + /// @param[in] celsius Is the temp Celsius or Fahrenheit. + /// true is C, false is F + /// @param[in] precomma Should the output string start with ", " or not? + /// @return The resulting String. + String addTempToString(const uint16_t degrees, const bool celsius, + const bool precomma) { + String result = addIntToString(degrees, kTempStr, precomma); + result += celsius ? 'C' : 'F'; + return result; + } + + /// Create a String of human output for a given temperature. + /// e.g. "Temp: 25.5C" + /// @param[in] degrees The temperature in degrees. + /// @param[in] celsius Is the temp Celsius or Fahrenheit. + /// true is C, false is F + /// @param[in] precomma Should the output string start with ", " or not? + /// @return The resulting String. + String addTempFloatToString(const float degrees, const bool celsius, + const bool precomma) { + String result = ""; + result.reserve(14); // Assuming ", Temp: XXX.5F" is the largest. + result += addIntToString(degrees, kTempStr, precomma); + // Is it a half degree? + if (((uint16_t)(2 * degrees)) & 1) result += F(".5"); + result += celsius ? 'C' : 'F'; + return result; + } + + /// Create a String of human output for the given operating mode. + /// e.g. "Mode: 1 (Cool)" + /// @param[in] mode The operating mode to display. + /// @param[in] automatic The numeric value for Auto mode. + /// @param[in] cool The numeric value for Cool mode. + /// @param[in] heat The numeric value for Heat mode. + /// @param[in] dry The numeric value for Dry mode. + /// @param[in] fan The numeric value for Fan mode. + /// @return The resulting String. + 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 result = ""; + result.reserve(22); // ", Mode: NNN (UNKNOWN)" + result += addIntToString(mode, kModeStr); + result += kSpaceLBraceStr; + if (mode == automatic) result += kAutoStr; + else if (mode == cool) result += kCoolStr; + else if (mode == heat) result += kHeatStr; + else if (mode == dry) result += kDryStr; + else if (mode == fan) result += kFanStr; + else + result += kUnknownStr; + return result + ')'; + } + + /// Create a String of the 3-letter day of the week from a numerical day of + /// the week. e.g. "Day: 1 (Mon)" + /// @param[in] day_of_week A numerical version of the sequential day of the + /// week. e.g. Saturday = 7 etc. + /// @param[in] offset Days to offset by. + /// e.g. For different day starting the week. + /// @param[in] precomma Should the output string start with ", " or not? + /// @return The resulting String. + String addDayToString(const uint8_t day_of_week, const int8_t offset, + const bool precomma) { + String result = ""; + result.reserve(19); // ", Day: N (UNKNOWN)" + result += addIntToString(day_of_week, kDayStr, precomma); + result += kSpaceLBraceStr; + if ((uint8_t)(day_of_week + offset) < 7) +#if UNIT_TEST + result += String(kThreeLetterDayOfWeekStr).substr( + (day_of_week + offset) * 3, 3); +#else // UNIT_TEST + result += String(kThreeLetterDayOfWeekStr).substring( + (day_of_week + offset) * 3, (day_of_week + offset) * 3 + 3); +#endif // UNIT_TEST + else + result += kUnknownStr; + return result + ')'; + } + + /// Create a String of human output for the given fan speed. + /// e.g. "Fan: 0 (Auto)" + /// @param[in] speed The numeric speed of the fan to display. + /// @param[in] high The numeric value for High speed. + /// @param[in] low The numeric value for Low speed. + /// @param[in] automatic The numeric value for Auto speed. + /// @param[in] quiet The numeric value for Quiet speed. + /// @param[in] medium The numeric value for Medium speed. + /// @param[in] maximum The numeric value for Highest speed. (if > high) + /// @return The resulting String. + 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) { + String result = ""; + result.reserve(21); // ", Fan: NNN (UNKNOWN)" + result += addIntToString(speed, kFanStr); + result += kSpaceLBraceStr; + if (speed == high) result += kHighStr; + else if (speed == low) result += kLowStr; + else if (speed == automatic) result += kAutoStr; + else if (speed == quiet) result += kQuietStr; + else if (speed == medium) result += kMediumStr; + else if (speed == maximum) result += kMaximumStr; + else + result += kUnknownStr; + return result + ')'; + } + + /// Create a String of human output for the given horizontal swing setting. + /// e.g. "Swing(H): 0 (Auto)" + /// @param[in] position The numeric position of the swing to display. + /// @param[in] automatic The numeric value for Auto position. + /// @param[in] maxleft The numeric value for most left position. + /// @param[in] left The numeric value for Left position. + /// @param[in] middle The numeric value for Middle position. + /// @param[in] right The numeric value for Right position. + /// @param[in] maxright The numeric value for most right position. + /// @param[in] off The numeric value for Off position. + /// @param[in] leftright The numeric value for "left right" position. + /// @param[in] rightleft The numeric value for "right left" position. + /// @param[in] threed The numeric value for 3D setting. + /// @param[in] wide The numeric value for Wide position. + /// @return The resulting String. + 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 result = ""; + result.reserve(30); // ", Swing(H): NNN (Left Right)" + result += addIntToString(position, kSwingHStr); + result += kSpaceLBraceStr; + if (position == automatic) { + result += kAutoStr; + } else if (position == left) { + result += kLeftStr; + } else if (position == middle) { + result += kMiddleStr; + } else if (position == right) { + result += kRightStr; + } else if (position == maxleft) { + result += kMaxLeftStr; + } else if (position == maxright) { + result += kMaxRightStr; + } else if (position == off) { + result += kOffStr; + } else if (position == leftright) { + result += kLeftStr; + result += ' '; + result += kRightStr; + } else if (position == rightleft) { + result += kRightStr; + result += ' '; + result += kLeftStr; + } else if (position == threed) { + result += k3DStr; + } else if (position == wide) { + result += kWideStr; + } else { + result += kUnknownStr; + } + return result + ')'; + } + + /// Create a String of human output for the given vertical swing setting. + /// e.g. "Swing(V): 0 (Auto)" + /// @param[in] position The numeric position of the swing to display. + /// @param[in] automatic The numeric value for Auto position. + /// @param[in] highest The numeric value for Highest position. + /// @param[in] high The numeric value for High position. + /// @param[in] uppermiddle The numeric value for Upper Middle position. + /// @param[in] middle The numeric value for Middle position. + /// @param[in] lowermiddle The numeric value for Lower Middle position. + /// @param[in] low The numeric value for Low position. + /// @param[in] lowest The numeric value for Low position. + /// @param[in] off The numeric value for Off position. + /// @param[in] swing The numeric value for Swing setting. + /// @param[in] breeze The numeric value for Breeze setting. + /// @param[in] circulate The numeric value for Circulate setting. + /// @return The resulting String. + 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 result = ""; + result.reserve(31); // ", Swing(V): NNN (Upper Middle)" + result += addIntToString(position, kSwingVStr); + result += kSpaceLBraceStr; + if (position == automatic) { + result += kAutoStr; + } else if (position == highest) { + result += kHighestStr; + } else if (position == high) { + result += kHighStr; + } else if (position == middle) { + result += kMiddleStr; + } else if (position == low) { + result += kLowStr; + } else if (position == lowest) { + result += kLowestStr; + } else if (position == off) { + result += kOffStr; + } else if (position == uppermiddle) { + result += kUpperStr; + result += ' '; + result += kMiddleStr; + } else if (position == lowermiddle) { + result += kLowerStr; + result += ' '; + result += kMiddleStr; + } else if (position == swing) { + result += kSwingStr; + } else if (position == breeze) { + result += kBreezeStr; + } else if (position == circulate) { + result += kCirculateStr; + } else { + result += kUnknownStr; + } + return result + ')'; + } + + /// Escape any special HTML (unsafe) characters in a string. e.g. anti-XSS. + /// @param[in] unescaped A String containing text to make HTML safe. + /// @return A string that is HTML safe. + String htmlEscape(const String unescaped) { + String result = ""; + uint16_t ulen = unescaped.length(); + result.reserve(ulen); // The result will be at least the size of input. + for (size_t i = 0; i < ulen; i++) { + char c = unescaped[i]; + switch (c) { + // ';!-"<>=&#{}() are all unsafe. + case '\'': result += F("'"); break; + case ';': result += F(";"); break; + case '!': result += F("!"); break; + case '-': result += F("‐"); break; + case '\"': result += F("""); break; + case '<': result += F("<"); break; + case '>': result += F(">"); break; + case '=': result += F("&#equals;"); break; + case '&': result += F("&"); break; + case '#': result += F("#"); break; + case '{': result += F("{"); break; + case '}': result += F("}"); break; + case '(': result += F("("); break; + case ')': result += F(")"); break; + default: result += c; + } + } + return result; + } + + /// Convert a nr. of milliSeconds into a Human-readable string. + /// e.g. "1 Day 6 Hours 34 Minutes 17 Seconds" + /// @param[in] msecs Nr. of milliSeconds (ms). + /// @return A human readable string. + String msToString(uint32_t const msecs) { + uint32_t totalseconds = msecs / 1000; + if (totalseconds == 0) return kNowStr; + + // Note: uint32_t can only hold up to 45 days, so uint8_t is safe. + uint8_t days = totalseconds / (60 * 60 * 24); + uint8_t hours = (totalseconds / (60 * 60)) % 24; + uint8_t minutes = (totalseconds / 60) % 60; + uint8_t seconds = totalseconds % 60; + + String result = ""; + result.reserve(42); // "99 Days, 23 Hours, 59 Minutes, 59 Seconds" + if (days) + result += uint64ToString(days) + ' ' + String((days > 1) ? kDaysStr + : kDayStr); + if (hours) { + if (result.length()) result += ' '; + result += uint64ToString(hours) + ' ' + String((hours > 1) ? kHoursStr + : kHourStr); + } + if (minutes) { + if (result.length()) result += ' '; + result += uint64ToString(minutes) + ' ' + String( + (minutes > 1) ? kMinutesStr : kMinuteStr); + } + if (seconds) { + if (result.length()) result += ' '; + result += uint64ToString(seconds) + ' ' + String( + (seconds > 1) ? kSecondsStr : kSecondStr); + } + return result; + } + + /// Convert a nr. of minutes into a 24h clock format Human-readable string. + /// e.g. "23:59" + /// @param[in] mins Nr. of Minutes. + /// @return A human readable string. + String minsToString(const uint16_t mins) { + String result = ""; + result.reserve(5); // 23:59 is the typical worst case. + if (mins / 60 < 10) result += '0'; // Zero pad the hours + result += uint64ToString(mins / 60) + kTimeSep; + if (mins % 60 < 10) result += '0'; // Zero pad the minutes. + result += uint64ToString(mins % 60); + return result; + } + + /// Sum all the nibbles together in a series of bytes. + /// @param[in] start A ptr to the start of the byte array to calculate over. + /// @param[in] length How many bytes to use in the calculation. + /// @param[in] init Starting value of the calculation to use. (Default is 0) + /// @return The 8-bit calculated result of all the bytes and init value. + uint8_t sumNibbles(const uint8_t * const start, const uint16_t length, + const uint8_t init) { + uint8_t sum = init; + const uint8_t *ptr; + for (ptr = start; ptr - start < length; ptr++) + sum += (*ptr >> 4) + (*ptr & 0xF); + return sum; + } + + /// Sum all the nibbles together in an integer. + /// @param[in] data The integer to be summed. + /// @param[in] count The number of nibbles to sum. Starts from LSB. Max of 16. + /// @param[in] init Starting value of the calculation to use. (Default is 0) + /// @param[in] nibbleonly true, the result is 4 bits. false, it's 8 bits. + /// @return The 4/8-bit calculated result of all the nibbles and init value. + uint8_t sumNibbles(const uint64_t data, const uint8_t count, + const uint8_t init, const bool nibbleonly) { + uint8_t sum = init; + uint64_t copy = data; + const uint8_t nrofnibbles = (count < 16) ? count : (64 / 4); + for (uint8_t i = 0; i < nrofnibbles; i++, copy >>= 4) sum += copy & 0xF; + return nibbleonly ? sum & 0xF : sum; + } + + /// Convert a byte of Binary Coded Decimal(BCD) into an Integer. + /// @param[in] bcd The BCD value. + /// @return A normal Integer value. + uint8_t bcdToUint8(const uint8_t bcd) { + if (bcd > 0x99) return 255; // Too big. + return (bcd >> 4) * 10 + (bcd & 0xF); + } + + /// Convert an Integer into a byte of Binary Coded Decimal(BCD). + /// @param[in] integer The number to convert. + /// @return An 8-bit BCD value. + uint8_t uint8ToBcd(const uint8_t integer) { + if (integer > 99) return 255; // Too big. + return ((integer / 10) << 4) + (integer % 10); + } + + /// Return the value of `position`th bit of an Integer. + /// @param[in] data Value to be examined. + /// @param[in] position Nr. of the Nth bit to be examined. `0` is the LSB. + /// @param[in] size Nr. of bits in data. + /// @return The bit's value. + bool getBit(const uint64_t data, const uint8_t position, const uint8_t size) { + if (position >= size) return false; // Outside of range. + return data & (1ULL << position); + } + + /// Return the value of `position`th bit of an Integer. + /// @param[in] data Value to be examined. + /// @param[in] position Nr. of the Nth bit to be examined. `0` is the LSB. + /// @return The bit's value. + bool getBit(const uint8_t data, const uint8_t position) { + if (position >= 8) return false; // Outside of range. + return data & (1 << position); + } + + /// Return the value of an Integer with the `position`th bit changed. + /// @param[in] data Value to be changed. + /// @param[in] position Nr. of the bit to be changed. `0` is the LSB. + /// @param[in] on Value to set the position'th bit to. + /// @param[in] size Nr. of bits in data. + /// @return A suitably modified integer. + uint64_t setBit(const uint64_t data, const uint8_t position, const bool on, + const uint8_t size) { + if (position >= size) return data; // Outside of range. + uint64_t mask = 1ULL << position; + if (on) + return data | mask; + else + return data & ~mask; + } + + /// Return the value of an Integer with the `position`th bit changed. + /// @param[in] data Value to be changed. + /// @param[in] position Nr. of the bit to be changed. `0` is the LSB. + /// @param[in] on Value to set the position'th bit to. + /// @return A suitably modified integer. + uint8_t setBit(const uint8_t data, const uint8_t position, const bool on) { + if (position >= 8) return data; // Outside of range. + uint8_t mask = 1 << position; + if (on) + return data | mask; + else + return data & ~mask; + } + + /// Alter the value of an Integer with the `position`th bit changed. + /// @param[in,out] data A pointer to the 8-bit integer to be changed. + /// @param[in] position Nr. of the bit to be changed. `0` is the LSB. + /// @param[in] on Value to set the position'th bit to. + void setBit(uint8_t * const data, const uint8_t position, const bool on) { + uint8_t mask = 1 << position; + if (on) + *data |= mask; + else + *data &= ~mask; + } + + /// Alter the value of an Integer with the `position`th bit changed. + /// @param[in,out] data A pointer to the 32-bit integer to be changed. + /// @param[in] position Nr. of the bit to be changed. `0` is the LSB. + /// @param[in] on Value to set the position'th bit to. + void setBit(uint32_t * const data, const uint8_t position, const bool on) { + uint32_t mask = (uint32_t)1 << position; + if (on) + *data |= mask; + else + *data &= ~mask; + } + + /// Alter the value of an Integer with the `position`th bit changed. + /// @param[in,out] data A pointer to the 64-bit integer to be changed. + /// @param[in] position Nr. of the bit to be changed. `0` is the LSB. + /// @param[in] on Value to set the position'th bit to. + void setBit(uint64_t * const data, const uint8_t position, const bool on) { + uint64_t mask = (uint64_t)1 << position; + if (on) + *data |= mask; + else + *data &= ~mask; + } + + /// Alter an uint8_t value by overwriting an arbitrary given number of bits. + /// @param[in,out] dst A pointer to the value to be changed. + /// @param[in] offset Nr. of bits from the Least Significant Bit to be ignored + /// @param[in] nbits Nr of bits of data to be placed into the destination. + /// @param[in] data The value to be placed. + void setBits(uint8_t * const dst, const uint8_t offset, const uint8_t nbits, + const uint8_t data) { + if (offset >= 8 || !nbits) return; // Short circuit as it won't change. + // Calculate the mask for the supplied value. + uint8_t mask = UINT8_MAX >> (8 - ((nbits > 8) ? 8 : nbits)); + // Calculate the mask & clear the space for the data. + // Clear the destination bits. + *dst &= ~(uint8_t)(mask << offset); + // Merge in the data. + *dst |= ((data & mask) << offset); + } + + /// Alter an uint32_t value by overwriting an arbitrary given number of bits. + /// @param[in,out] dst A pointer to the value to be changed. + /// @param[in] offset Nr. of bits from the Least Significant Bit to be ignored + /// @param[in] nbits Nr of bits of data to be placed into the destination. + /// @param[in] data The value to be placed. + void setBits(uint32_t * const dst, const uint8_t offset, const uint8_t nbits, + const uint32_t data) { + if (offset >= 32 || !nbits) return; // Short circuit as it won't change. + // Calculate the mask for the supplied value. + uint32_t mask = UINT32_MAX >> (32 - ((nbits > 32) ? 32 : nbits)); + // Calculate the mask & clear the space for the data. + // Clear the destination bits. + *dst &= ~(mask << offset); + // Merge in the data. + *dst |= ((data & mask) << offset); + } + + /// Alter an uint64_t value by overwriting an arbitrary given number of bits. + /// @param[in,out] dst A pointer to the value to be changed. + /// @param[in] offset Nr. of bits from the Least Significant Bit to be ignored + /// @param[in] nbits Nr of bits of data to be placed into the destination. + /// @param[in] data The value to be placed. + void setBits(uint64_t * const dst, const uint8_t offset, const uint8_t nbits, + const uint64_t data) { + if (offset >= 64 || !nbits) return; // Short circuit as it won't change. + // Calculate the mask for the supplied value. + uint64_t mask = UINT64_MAX >> (64 - ((nbits > 64) ? 64 : nbits)); + // Calculate the mask & clear the space for the data. + // Clear the destination bits. + *dst &= ~(mask << offset); + // Merge in the data. + *dst |= ((data & mask) << offset); + } + + /// Create byte pairs where the second byte of the pair is a bit + /// inverted/flipped copy of the first/previous byte of the pair. + /// @param[in,out] ptr A pointer to the start of array to modify. + /// @param[in] length The byte size of the array. + /// @note A length of `<= 1` will do nothing. + /// @return A ptr to the modified array. + uint8_t * invertBytePairs(uint8_t *ptr, const uint16_t length) { + for (uint16_t i = 1; i < length; i += 2) { + // Code done this way to avoid a compiler warning bug. + uint8_t inv = ~*(ptr + i - 1); + *(ptr + i) = inv; + } + return ptr; + } + + /// Check an array to see if every second byte of a pair is a bit + /// inverted/flipped copy of the first/previous byte of the pair. + /// @param[in] ptr A pointer to the start of array to check. + /// @param[in] length The byte size of the array. + /// @note A length of `<= 1` will always return true. + /// @return true, if every second byte is inverted. Otherwise false. + bool checkInvertedBytePairs(const uint8_t * const ptr, + const uint16_t length) { + for (uint16_t i = 1; i < length; i += 2) { + // Code done this way to avoid a compiler warning bug. + uint8_t inv = ~*(ptr + i - 1); + if (*(ptr + i) != inv) return false; + } + return true; + } + + /// Perform a low level bit manipulation sanity check for the given cpu + /// architecture and the compiler operation. Calls to this should return + /// 0 if everything is as expected, anything else means the library won't work + /// as expected. + /// @return A bit mask value of potential issues. + /// 0: (e.g. 0b00000000) Everything appears okay. + /// 0th bit set: (0b1) Unexpected bit field/packing encountered. + /// Try a different compiler. + /// 1st bit set: (0b10) Unexpected Endianness. Try a different compiler flag + /// or use a CPU different architecture. + /// e.g. A result of 3 (0b11) would mean both a bit field and an Endianness + /// issue has been found. + uint8_t lowLevelSanityCheck(void) { + const uint64_t kExpectedBitFieldResult = 0x8000012340000039ULL; + volatile uint32_t EndianTest = 0x12345678; + const uint8_t kBitFieldError = 0b01; + const uint8_t kEndiannessError = 0b10; + uint8_t result = 0; + union bitpackdata { + struct { + uint64_t lowestbit:1; // 0th bit + uint64_t next7bits:7; // 1-7th bits + uint64_t _unused_1:20; // 8-27th bits + // Cross the 32 bit boundary. + uint64_t crossbits:16; // 28-43rd bits + uint64_t _usused_2:18; // 44-61st bits + uint64_t highest2bits:2; // 62-63rd bits + }; + uint64_t all; + }; + + bitpackdata data; + data.lowestbit = true; + data.next7bits = 0b0011100; // 0x1C + data._unused_1 = 0; + data.crossbits = 0x1234; + data._usused_2 = 0; + data.highest2bits = 0b10; // 2 + + if (data.all != kExpectedBitFieldResult) result |= kBitFieldError; + // Check that we are using Little Endian for integers +#if defined(BYTE_ORDER) && defined(LITTLE_ENDIAN) + if (BYTE_ORDER != LITTLE_ENDIAN) result |= kEndiannessError; +#endif +#if defined(__IEEE_BIG_ENDIAN) || defined(__IEEE_BYTES_BIG_ENDIAN) + result |= kEndiannessError; +#endif + // Brute force check for little endian. + if (*((uint8_t*)(&EndianTest)) != 0x78) // NOLINT(readability/casting) + result |= kEndiannessError; + return result; + } +} // namespace irutils diff --git a/lib/IRremoteESP8266/src/IRutils.h b/lib/IRremoteESP8266/src/IRutils.h index 7bd6af56c1..61fe8b2699 100644 --- a/lib/IRremoteESP8266/src/IRutils.h +++ b/lib/IRremoteESP8266/src/IRutils.h @@ -11,8 +11,8 @@ #ifndef ARDUINO #include #endif -#include "../src/IRremoteESP8266.h" -#include "../src/IRrecv.h" +#include "IRremoteESP8266.h" +#include "IRrecv.h" const uint8_t kNibbleSize = 4; const uint8_t kLowNibble = 0; diff --git a/lib/IRremoteESP8266/src/i18n.h b/lib/IRremoteESP8266/src/i18n.h index 260153b37c..27ac4d714b 100644 --- a/lib/IRremoteESP8266/src/i18n.h +++ b/lib/IRremoteESP8266/src/i18n.h @@ -3,7 +3,7 @@ #ifndef I18N_H_ #define I18N_H_ -#include "../src/IRremoteESP8266.h" +#include "IRremoteESP8266.h" // Load the appropriate locale header file. #ifndef _IR_LOCALE_ diff --git a/lib/IRremoteESP8266/src/ir_Airwell.cpp b/lib/IRremoteESP8266/src/ir_Airwell.cpp new file mode 100644 index 0000000000..26053442ee --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_Airwell.cpp @@ -0,0 +1,286 @@ +// Copyright 2020 David Conran +#include "ir_Airwell.h" +#include +#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; +} diff --git a/lib/IRremoteESP8266/src/ir_Airwell.h b/lib/IRremoteESP8266/src/ir_Airwell.h index 68e5100dbe..814719f1f1 100644 --- a/lib/IRremoteESP8266/src/ir_Airwell.h +++ b/lib/IRremoteESP8266/src/ir_Airwell.h @@ -17,10 +17,10 @@ #ifndef UNIT_TEST #include #endif -#include "../src/IRremoteESP8266.h" -#include "../src/IRsend.h" +#include "IRremoteESP8266.h" +#include "IRsend.h" #ifdef UNIT_TEST -#include "../src/IRsend_test.h" +#include "IRsend_test.h" #endif /// Native representation of a Airwell A/C message. diff --git a/lib/IRremoteESP8266/src/ir_Aiwa.cpp b/lib/IRremoteESP8266/src/ir_Aiwa.cpp new file mode 100644 index 0000000000..266fe67d68 --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_Aiwa.cpp @@ -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 diff --git a/lib/IRremoteESP8266/src/ir_Amcor.cpp b/lib/IRremoteESP8266/src/ir_Amcor.cpp new file mode 100644 index 0000000000..c2aea5cdd3 --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_Amcor.cpp @@ -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 +#include +#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; +} diff --git a/lib/IRremoteESP8266/src/ir_Amcor.h b/lib/IRremoteESP8266/src/ir_Amcor.h index 21c4ff2604..62ea50d9d8 100644 --- a/lib/IRremoteESP8266/src/ir_Amcor.h +++ b/lib/IRremoteESP8266/src/ir_Amcor.h @@ -17,10 +17,10 @@ #ifndef UNIT_TEST #include #endif -#include "../src/IRremoteESP8266.h" -#include "../src/IRsend.h" +#include "IRremoteESP8266.h" +#include "IRsend.h" #ifdef UNIT_TEST -#include "../src/IRsend_test.h" +#include "IRsend_test.h" #endif diff --git a/lib/IRremoteESP8266/src/ir_Argo.cpp b/lib/IRremoteESP8266/src/ir_Argo.cpp new file mode 100644 index 0000000000..e1df28efbb --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_Argo.cpp @@ -0,0 +1,470 @@ +// Copyright 2017 Schmolders +// Copyright 2019 crankyoldgit +/// @file +/// @brief Argo A/C protocol. +/// Controls an Argo Ulisse 13 DCI A/C + +#include "ir_Argo.h" +#include +#include +#ifndef UNIT_TEST +#include +#endif // UNIT_TEST +#include "IRremoteESP8266.h" +#include "IRtext.h" +#include "IRutils.h" + +// Constants +// using SPACE modulation. MARK is always const 400u +const uint16_t kArgoHdrMark = 6400; +const uint16_t kArgoHdrSpace = 3300; +const uint16_t kArgoBitMark = 400; +const uint16_t kArgoOneSpace = 2200; +const uint16_t kArgoZeroSpace = 900; +const uint32_t kArgoGap = kDefaultMessageGap; // Made up value. Complete guess. + +using irutils::addBoolToString; +using irutils::addIntToString; +using irutils::addLabeledString; +using irutils::addModeToString; +using irutils::addTempToString; + +#if SEND_ARGO +/// Send a Argo A/C formatted message. +/// Status: BETA / Probably works. +/// @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::sendArgo(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 < kArgoStateLength) return; + // TODO(kaschmo): validate + sendGeneric(kArgoHdrMark, kArgoHdrSpace, kArgoBitMark, kArgoOneSpace, + kArgoBitMark, kArgoZeroSpace, 0, 0, // No Footer. + data, nbytes, 38, false, repeat, kDutyDefault); +} +#endif // SEND_ARGO + +/// 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? +IRArgoAC::IRArgoAC(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 IRArgoAC::begin(void) { _irsend.begin(); } + +#if SEND_ARGO +/// Send the current internal state as an IR message. +/// @param[in] repeat Nr. of times the message will be repeated. +void IRArgoAC::send(const uint16_t repeat) { + _irsend.sendArgo(getRaw(), kArgoStateLength, repeat); +} +#endif // SEND_ARGO + +/// 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. +uint8_t IRArgoAC::calcChecksum(const uint8_t state[], const uint16_t length) { + // Corresponds to byte 11 being constant 0b01 + // Only add up bytes to 9. byte 10 is 0b01 constant anyway. + // Assume that argo array is MSB first (left) + return sumBytes(state, length - 2, 2); +} + +/// 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 IRArgoAC::validChecksum(const uint8_t state[], const uint16_t length) { + return ((state[length - 2] >> 2) + (state[length - 1] << 6)) == + IRArgoAC::calcChecksum(state, length); +} + +/// Update the checksum for the internal state. +void IRArgoAC::checksum(void) { + uint8_t sum = IRArgoAC::calcChecksum(_.raw, kArgoStateLength); + // Append sum to end of array + // Set const part of checksum bit 10 + _.raw[10] = 0b00000010; + _.Sum = sum; +} + +/// Reset the internals of the object to a known good state. +void IRArgoAC::stateReset(void) { + for (uint8_t i = 0; i < kArgoStateLength; i++) _.raw[i] = 0x0; + + // Argo Message. Store MSB left. + // Default message: + _.raw[0] = 0b10101100; // LSB first (as sent) 0b00110101; //const preamble + _.raw[1] = 0b11110101; // LSB first: 0b10101111; //const preamble + // Keep payload 2-9 at zero + _.raw[10] = 0b00000010; // Const 01 + _.Sum = 0; + + off(); + setTemp(20); + setRoomTemp(25); + setMode(kArgoAuto); + setFan(kArgoFanAuto); +} + +/// 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* IRArgoAC::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 IRArgoAC::setRaw(const uint8_t state[]) { + std::memcpy(_.raw, state, kArgoStateLength); +} + +/// Set the internal state to have the power on. +void IRArgoAC::on(void) { setPower(true); } + +/// Set the internal state to have the power off. +void IRArgoAC::off(void) { setPower(false); } + +/// Set the internal state to have the desired power. +/// @param[in] on The desired power state. +void IRArgoAC::setPower(const bool on) { + _.Power = on; +} + +/// Get the power setting from the internal state. +/// @return A boolean indicating the power setting. +bool IRArgoAC::getPower(void) const { return _.Power; } + +/// Control the current Max setting. (i.e. Turbo) +/// @param[in] on The desired setting. +void IRArgoAC::setMax(const bool on) { + _.Max = on; +} + +/// Is the Max (i.e. Turbo) setting on? +/// @return The current value. +bool IRArgoAC::getMax(void) const { return _.Max; } + +/// Set the temperature. +/// @param[in] degrees The temperature in degrees celsius. +/// @note Sending 0 equals +4 +void IRArgoAC::setTemp(const uint8_t degrees) { + uint8_t temp = std::max(kArgoMinTemp, degrees); + // delta 4 degrees. "If I want 12 degrees, I need to send 8" + temp = std::min(kArgoMaxTemp, temp) - kArgoTempDelta; + // mask out bits + // argo[13] & 0x00000100; // mask out ON/OFF Bit + _.Temp = temp; +} + +/// Get the current temperature setting. +/// @return The current setting for temp. in degrees celsius. +uint8_t IRArgoAC::getTemp(void) const { + return _.Temp + kArgoTempDelta; +} + +/// Set the speed of the fan. +/// @param[in] fan The desired setting. +void IRArgoAC::setFan(const uint8_t fan) { + _.Fan = std::min(fan, kArgoFan3); +} + +/// Get the current fan speed setting. +/// @return The current fan speed. +uint8_t IRArgoAC::getFan(void) const { + return _.Fan; +} + +/// Set the flap position. i.e. Swing. +/// @warning Not yet working! +/// @param[in] flap The desired setting. +void IRArgoAC::setFlap(const uint8_t flap) { + flap_mode = flap; + // TODO(kaschmo): set correct bits for flap mode +} + +/// Get the flap position. i.e. Swing. +/// @warning Not yet working! +/// @return The current flap setting. +uint8_t IRArgoAC::getFlap(void) const { return flap_mode; } + +/// Get the current operation mode setting. +/// @return The current operation mode. +uint8_t IRArgoAC::getMode(void) const { + return _.Mode; +} + +/// Set the desired operation mode. +/// @param[in] mode The desired operation mode. +void IRArgoAC::setMode(const uint8_t mode) { + switch (mode) { + case kArgoCool: + case kArgoDry: + case kArgoAuto: + case kArgoOff: + case kArgoHeat: + case kArgoHeatAuto: + _.Mode = mode; + return; + default: + _.Mode = kArgoAuto; + } +} + +/// Turn on/off the Night mode. i.e. Sleep. +/// @param[in] on The desired setting. +void IRArgoAC::setNight(const bool on) { + _.Night = on; +} + +/// Get the status of Night mode. i.e. Sleep. +/// @return true if on, false if off. +bool IRArgoAC::getNight(void) const { return _.Night; } + +/// Turn on/off the iFeel mode. +/// @param[in] on The desired setting. +void IRArgoAC::setiFeel(const bool on) { + _.iFeel = on; +} + +/// Get the status of iFeel mode. +/// @return true if on, false if off. +bool IRArgoAC::getiFeel(void) const { return _.iFeel; } + +/// Set the time for the A/C +/// @warning Not yet working! +void IRArgoAC::setTime(void) { + // TODO(kaschmo): use function call from checksum to set time first +} + +/// Set the value for the current room temperature. +/// @param[in] degrees The temperature in degrees celsius. +void IRArgoAC::setRoomTemp(const uint8_t degrees) { + uint8_t temp = std::min(degrees, kArgoMaxRoomTemp); + temp = std::max(temp, kArgoTempDelta) - kArgoTempDelta; + _.RoomTemp = temp; +} + +/// Get the currently stored value for the room temperature setting. +/// @return The current setting for the room temp. in degrees celsius. +uint8_t IRArgoAC::getRoomTemp(void) const { + return _.RoomTemp + kArgoTempDelta; +} + +/// 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 IRArgoAC::convertMode(const stdAc::opmode_t mode) { + switch (mode) { + case stdAc::opmode_t::kCool: + return kArgoCool; + case stdAc::opmode_t::kHeat: + return kArgoHeat; + case stdAc::opmode_t::kDry: + return kArgoDry; + case stdAc::opmode_t::kOff: + return kArgoOff; + // No fan mode. + default: + return kArgoAuto; + } +} + +/// 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 IRArgoAC::convertFan(const stdAc::fanspeed_t speed) { + switch (speed) { + case stdAc::fanspeed_t::kMin: + case stdAc::fanspeed_t::kLow: + return kArgoFan1; + case stdAc::fanspeed_t::kMedium: + return kArgoFan2; + case stdAc::fanspeed_t::kHigh: + case stdAc::fanspeed_t::kMax: + return kArgoFan3; + default: + return kArgoFanAuto; + } +} + +/// 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 IRArgoAC::convertSwingV(const stdAc::swingv_t position) { + switch (position) { + case stdAc::swingv_t::kHighest: + return kArgoFlapFull; + case stdAc::swingv_t::kHigh: + return kArgoFlap5; + case stdAc::swingv_t::kMiddle: + return kArgoFlap4; + case stdAc::swingv_t::kLow: + return kArgoFlap3; + case stdAc::swingv_t::kLowest: + return kArgoFlap1; + default: + return kArgoFlapAuto; + } +} + +/// 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 IRArgoAC::toCommonMode(const uint8_t mode) { + switch (mode) { + case kArgoCool: return stdAc::opmode_t::kCool; + case kArgoHeat: return stdAc::opmode_t::kHeat; + case kArgoDry: return stdAc::opmode_t::kDry; + // No fan mode. + 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 IRArgoAC::toCommonFanSpeed(const uint8_t speed) { + switch (speed) { + case kArgoFan3: return stdAc::fanspeed_t::kMax; + case kArgoFan2: return stdAc::fanspeed_t::kMedium; + case kArgoFan1: 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 IRArgoAC::toCommon(void) const { + stdAc::state_t result; + result.protocol = decode_type_t::ARGO; + result.power = _.Power; + result.mode = toCommonMode(_.Mode); + result.celsius = true; + result.degrees = getTemp(); + result.fanspeed = toCommonFanSpeed(_.Fan); + result.turbo = _.Max; + result.sleep = _.Night ? 0 : -1; + // Not supported. + result.model = -1; // Not supported. + 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 IRArgoAC::toString(void) const { + String result = ""; + result.reserve(100); // Reserve some heap for the string to reduce fragging. + result += addBoolToString(_.Power, kPowerStr, false); + result += addIntToString(_.Mode, kModeStr); + result += kSpaceLBraceStr; + switch (_.Mode) { + case kArgoAuto: + result += kAutoStr; + break; + case kArgoCool: + result += kCoolStr; + break; + case kArgoHeat: + result += kHeatStr; + break; + case kArgoDry: + result += kDryStr; + break; + case kArgoHeatAuto: + result += kHeatStr; + result += ' '; + result += kAutoStr; + break; + case kArgoOff: + result += kOffStr; + break; + default: + result += kUnknownStr; + } + result += ')'; + result += addIntToString(_.Fan, kFanStr); + result += kSpaceLBraceStr; + switch (_.Fan) { + case kArgoFanAuto: + result += kAutoStr; + break; + case kArgoFan3: + result += kMaxStr; + break; + case kArgoFan1: + result += kMinStr; + break; + case kArgoFan2: + result += kMedStr; + break; + default: + result += kUnknownStr; + } + result += ')'; + result += addTempToString(getTemp()); + result += kCommaSpaceStr; + result += kRoomStr; + result += ' '; + result += addTempToString(getRoomTemp(), true, false); + result += addBoolToString(_.Max, kMaxStr); + result += addBoolToString(_.iFeel, kIFeelStr); + result += addBoolToString(_.Night, kNightStr); + return result; +} + +#if DECODE_ARGO +/// Decode the supplied Argo 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 This decoder is based soley off sendArgo(). We have no actual captures +/// to test this against. If you have one of these units, please let us know. +bool IRrecv::decodeArgo(decode_results *results, uint16_t offset, + const uint16_t nbits, + const bool strict) { + if (strict && nbits != kArgoBits) return false; + + // Match Header + Data + if (!matchGeneric(results->rawbuf + offset, results->state, + results->rawlen - offset, nbits, + kArgoHdrMark, kArgoHdrSpace, + kArgoBitMark, kArgoOneSpace, + kArgoBitMark, kArgoZeroSpace, + 0, 0, // Footer (None, allegedly. This seems very wrong.) + true, _tolerance, 0, false)) return false; + + // Compliance + // Verify we got a valid checksum. + if (strict && !IRArgoAC::validChecksum(results->state)) return false; + // Success + results->decode_type = decode_type_t::ARGO; + 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_ARGO diff --git a/lib/IRremoteESP8266/src/ir_Argo.h b/lib/IRremoteESP8266/src/ir_Argo.h index bc1844789a..6ceb58e421 100644 --- a/lib/IRremoteESP8266/src/ir_Argo.h +++ b/lib/IRremoteESP8266/src/ir_Argo.h @@ -11,10 +11,10 @@ #ifndef UNIT_TEST #include #endif -#include "../src/IRremoteESP8266.h" -#include "../src/IRsend.h" +#include "IRremoteESP8266.h" +#include "IRsend.h" #ifdef UNIT_TEST -#include "../src/IRsend_test.h" +#include "IRsend_test.h" #endif diff --git a/lib/IRremoteESP8266/src/ir_Arris.cpp b/lib/IRremoteESP8266/src/ir_Arris.cpp new file mode 100644 index 0000000000..5b39808c8f --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_Arris.cpp @@ -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(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 diff --git a/lib/IRremoteESP8266/src/ir_Bose.cpp b/lib/IRremoteESP8266/src/ir_Bose.cpp new file mode 100644 index 0000000000..a57d125b3c --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_Bose.cpp @@ -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 diff --git a/lib/IRremoteESP8266/src/ir_Carrier.cpp b/lib/IRremoteESP8266/src/ir_Carrier.cpp new file mode 100644 index 0000000000..92fca4bd2c --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_Carrier.cpp @@ -0,0 +1,535 @@ +// Copyright 2018, 2020 David Conran +/// @file +/// @brief Carrier protocols. +/// @see CarrierAc https://github.com/crankyoldgit/IRremoteESP8266/issues/385 +/// @see CarrierAc64 https://github.com/crankyoldgit/IRremoteESP8266/issues/1127 + +#include "ir_Carrier.h" +#include +#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. + + +#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(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; +} diff --git a/lib/IRremoteESP8266/src/ir_Carrier.h b/lib/IRremoteESP8266/src/ir_Carrier.h index 734e702b6d..aa9ea8447c 100644 --- a/lib/IRremoteESP8266/src/ir_Carrier.h +++ b/lib/IRremoteESP8266/src/ir_Carrier.h @@ -20,10 +20,10 @@ #ifndef UNIT_TEST #include #endif -#include "../src/IRremoteESP8266.h" -#include "../src/IRsend.h" +#include "IRremoteESP8266.h" +#include "IRsend.h" #ifdef UNIT_TEST -#include "../src/IRsend_test.h" +#include "IRsend_test.h" #endif /// Native representation of a Carrier A/C message. diff --git a/lib/IRremoteESP8266/src/ir_Coolix.cpp b/lib/IRremoteESP8266/src/ir_Coolix.cpp new file mode 100644 index 0000000000..2f16a6d1f0 --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_Coolix.cpp @@ -0,0 +1,697 @@ +// 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 +#ifndef ARDUINO +#include +#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; +const uint16_t kCoolixOneSpaceTicks = 6; +const uint16_t kCoolixOneSpace = kCoolixOneSpaceTicks * kCoolixTick; +const uint16_t kCoolixZeroSpaceTicks = 2; +const uint16_t kCoolixZeroSpace = kCoolixZeroSpaceTicks * kCoolixTick; +const uint16_t kCoolixHdrMarkTicks = 17; +const uint16_t kCoolixHdrMark = kCoolixHdrMarkTicks * kCoolixTick; +const uint16_t kCoolixHdrSpaceTicks = 16; +const uint16_t kCoolixHdrSpace = kCoolixHdrSpaceTicks * kCoolixTick; +const uint16_t kCoolixMinGapTicks = kCoolixHdrMarkTicks + kCoolixZeroSpaceTicks; +const uint16_t kCoolixMinGap = kCoolixMinGapTicks * kCoolixTick; + +using irutils::addBoolToString; +using irutils::addIntToString; +using irutils::addLabeledString; +using irutils::addModeToString; +using irutils::addTempToString; + +#if SEND_COOLIX +/// Send a Coolix 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.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) + ? kOffStr : String(uint64ToString(getSensorTemp()) + 'C'), kSensorTempStr); + return result; +} + +#if DECODE_COOLIX +/// Decode the supplied Coolix 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; + // Calculate how long the common tick time is based on the header mark. + uint32_t m_tick = results->rawbuf[offset++] * kRawTick / kCoolixHdrMarkTicks; + if (!matchSpace(results->rawbuf[offset], kCoolixHdrSpace)) return false; + // Calculate how long the common tick time is based on the header space. + uint32_t s_tick = results->rawbuf[offset++] * kRawTick / kCoolixHdrSpaceTicks; + + // Data + // Twice as many bits as there are normal plus inverted bits. + for (uint16_t i = 0; i < nbits * 2; i++, offset++) { + bool flip = (i / 8) % 2; + if (!matchMark(results->rawbuf[offset++], kCoolixBitMarkTicks * m_tick)) + return false; + if (matchSpace(results->rawbuf[offset], kCoolixOneSpaceTicks * s_tick)) { + if (flip) + inverted = (inverted << 1) | 1; + else + data = (data << 1) | 1; + } else if (matchSpace(results->rawbuf[offset], + kCoolixZeroSpaceTicks * s_tick)) { + if (flip) + inverted <<= 1; + else + data <<= 1; + } else { + return false; + } + } + + // Footer + if (!matchMark(results->rawbuf[offset++], kCoolixBitMarkTicks * m_tick)) + return false; + if (offset < results->rawlen && + !matchAtLeast(results->rawbuf[offset], kCoolixMinGapTicks * s_tick)) + 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 diff --git a/lib/IRremoteESP8266/src/ir_Coolix.h b/lib/IRremoteESP8266/src/ir_Coolix.h index b0318141af..42a2528d75 100644 --- a/lib/IRremoteESP8266/src/ir_Coolix.h +++ b/lib/IRremoteESP8266/src/ir_Coolix.h @@ -30,10 +30,10 @@ #ifndef UNIT_TEST #include #endif -#include "../src/IRremoteESP8266.h" -#include "../src/IRsend.h" +#include "IRremoteESP8266.h" +#include "IRsend.h" #ifdef UNIT_TEST -#include "../src/IRsend_test.h" +#include "IRsend_test.h" #endif // Constants diff --git a/lib/IRremoteESP8266/src/ir_Corona.cpp b/lib/IRremoteESP8266/src/ir_Corona.cpp new file mode 100644 index 0000000000..ef40c241a0 --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_Corona.cpp @@ -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 +#include +#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(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(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; +} diff --git a/lib/IRremoteESP8266/src/ir_Corona.h b/lib/IRremoteESP8266/src/ir_Corona.h index 43d211dfcd..cbe3e99e47 100644 --- a/lib/IRremoteESP8266/src/ir_Corona.h +++ b/lib/IRremoteESP8266/src/ir_Corona.h @@ -20,10 +20,10 @@ #ifndef UNIT_TEST #include #endif -#include "../src/IRremoteESP8266.h" -#include "../src/IRsend.h" +#include "IRremoteESP8266.h" +#include "IRsend.h" #ifdef UNIT_TEST -#include "../src/IRsend_test.h" +#include "IRsend_test.h" #endif /// Native representation of a section of a Corona A/C message. diff --git a/lib/IRremoteESP8266/src/ir_Daikin.cpp b/lib/IRremoteESP8266/src/ir_Daikin.cpp new file mode 100644 index 0000000000..ea11daf781 --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_Daikin.cpp @@ -0,0 +1,3735 @@ +// Copyright 2016 sillyfrog +// Copyright 2017 sillyfrog, crankyoldgit +// Copyright 2018-2021 crankyoldgit +// Copyright 2019 pasna (IRDaikin160 class / Daikin176 class) + +/// @file +/// @brief Support for Daikin A/C protocols. +/// @see Daikin http://harizanov.com/2012/02/control-daikin-air-conditioner-over-the-internet/ +/// @see Daikin https://github.com/mharizanov/Daikin-AC-remote-control-over-the-Internet/tree/master/IRremote +/// @see Daikin http://rdlab.cdmt.vn/project-2013/daikin-ir-protocol +/// @see Daikin https://github.com/blafois/Daikin-IR-Reverse +/// @see Daikin128 https://github.com/crankyoldgit/IRremoteESP8266/issues/827 +/// @see Daikin152 https://github.com/crankyoldgit/IRremoteESP8266/issues/873 +/// @see Daikin152 https://github.com/ToniA/arduino-heatpumpir/blob/master/DaikinHeatpumpARC480A14IR.cpp +/// @see Daikin152 https://github.com/ToniA/arduino-heatpumpir/blob/master/DaikinHeatpumpARC480A14IR.h +/// @see Daikin160 https://github.com/crankyoldgit/IRremoteESP8266/issues/731 +/// @see Daikin2 https://docs.google.com/spreadsheets/d/1f8EGfIbBUo2B-CzUFdrgKQprWakoYNKM80IKZN4KXQE/edit#gid=236366525&range=B25:D32 +/// @see Daikin2 https://github.com/crankyoldgit/IRremoteESP8266/issues/582 +/// @see Daikin2 https://github.com/crankyoldgit/IRremoteESP8266/issues/1535 +/// @see Daikin2 https://www.daikin.co.nz/sites/default/files/daikin-split-system-US7-FTXZ25-50NV1B.pdf +/// @see Daikin216 https://github.com/crankyoldgit/IRremoteESP8266/issues/689 +/// @see Daikin216 https://github.com/danny-source/Arduino_DY_IRDaikin +/// @see Daikin64 https://github.com/crankyoldgit/IRremoteESP8266/issues/1064 + +#include "ir_Daikin.h" +#include +#include +#ifndef ARDUINO +#include +#endif +#include "IRrecv.h" +#include "IRremoteESP8266.h" +#include "IRsend.h" +#ifdef UNIT_TEST +#include "IRsend_test.h" +#endif +#include "IRtext.h" +#include "IRutils.h" + +using irutils::addBoolToString; +using irutils::addDayToString; +using irutils::addIntToString; +using irutils::addLabeledString; +using irutils::addModeToString; +using irutils::addSwingHToString; +using irutils::addSwingVToString; +using irutils::addTempToString; +using irutils::addFanToString; +using irutils::bcdToUint8; +using irutils::minsToString; +using irutils::setBit; +using irutils::setBits; +using irutils::sumNibbles; +using irutils::uint8ToBcd; + +#if SEND_DAIKIN +/// Send a Daikin 280-bit A/C formatted message. +/// Status: STABLE +/// @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. +/// @see https://github.com/mharizanov/Daikin-AC-remote-control-over-the-Internet/tree/master/IRremote +/// @see https://github.com/blafois/Daikin-IR-Reverse +void IRsend::sendDaikin(const unsigned char data[], const uint16_t nbytes, + const uint16_t repeat) { + if (nbytes < kDaikinStateLengthShort) + return; // Not enough bytes to send a proper message. + + for (uint16_t r = 0; r <= repeat; r++) { + uint16_t offset = 0; + // Send the header, 0b00000 + sendGeneric(0, 0, // No header for the header + kDaikinBitMark, kDaikinOneSpace, kDaikinBitMark, + kDaikinZeroSpace, kDaikinBitMark, kDaikinZeroSpace + kDaikinGap, + (uint64_t)0b00000, kDaikinHeaderLength, 38, false, 0, 50); + // Data #1 + if (nbytes < kDaikinStateLength) { // Are we using the legacy size? + // Do this as a constant to save RAM and keep in flash memory + sendGeneric(kDaikinHdrMark, kDaikinHdrSpace, kDaikinBitMark, + kDaikinOneSpace, kDaikinBitMark, kDaikinZeroSpace, + kDaikinBitMark, kDaikinZeroSpace + kDaikinGap, + kDaikinFirstHeader64, 64, 38, false, 0, 50); + } else { // We are using the newer/more correct size. + sendGeneric(kDaikinHdrMark, kDaikinHdrSpace, kDaikinBitMark, + kDaikinOneSpace, kDaikinBitMark, kDaikinZeroSpace, + kDaikinBitMark, kDaikinZeroSpace + kDaikinGap, + data, kDaikinSection1Length, 38, false, 0, 50); + offset += kDaikinSection1Length; + } + // Data #2 + sendGeneric(kDaikinHdrMark, kDaikinHdrSpace, kDaikinBitMark, + kDaikinOneSpace, kDaikinBitMark, kDaikinZeroSpace, + kDaikinBitMark, kDaikinZeroSpace + kDaikinGap, + data + offset, kDaikinSection2Length, 38, false, 0, 50); + offset += kDaikinSection2Length; + // Data #3 + sendGeneric(kDaikinHdrMark, kDaikinHdrSpace, kDaikinBitMark, + kDaikinOneSpace, kDaikinBitMark, kDaikinZeroSpace, + kDaikinBitMark, kDaikinZeroSpace + kDaikinGap, + data + offset, nbytes - offset, 38, false, 0, 50); + } +} +#endif // SEND_DAIKIN + +/// 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? +IRDaikinESP::IRDaikinESP(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 IRDaikinESP::begin(void) { _irsend.begin(); } + +#if SEND_DAIKIN +/// Send the current internal state as an IR message. +/// @param[in] repeat Nr. of times the message will be repeated. +void IRDaikinESP::send(const uint16_t repeat) { + _irsend.sendDaikin(getRaw(), kDaikinStateLength, repeat); +} +#endif // SEND_DAIKIN + +/// 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 IRDaikinESP::validChecksum(uint8_t state[], const uint16_t length) { + // Data #1 + if (length < kDaikinSection1Length || + state[kDaikinByteChecksum1] != sumBytes(state, kDaikinSection1Length - 1)) + return false; + // Data #2 + if (length < kDaikinSection1Length + kDaikinSection2Length || + state[kDaikinByteChecksum2] != sumBytes(state + kDaikinSection1Length, + kDaikinSection2Length - 1)) + return false; + // Data #3 + if (length < kDaikinSection1Length + kDaikinSection2Length + 2 || + state[length - 1] != sumBytes(state + kDaikinSection1Length + + kDaikinSection2Length, + length - (kDaikinSection1Length + + kDaikinSection2Length) - 1)) + return false; + return true; +} + +/// Calculate and set the checksum values for the internal state. +void IRDaikinESP::checksum(void) { + _.Sum1 = sumBytes(_.raw, kDaikinSection1Length - 1); + _.Sum2 = sumBytes(_.raw + kDaikinSection1Length, kDaikinSection2Length - 1); + _.Sum3 = sumBytes(_.raw + kDaikinSection1Length + kDaikinSection2Length, + kDaikinSection3Length - 1); +} + +/// Reset the internal state to a fixed known good state. +void IRDaikinESP::stateReset(void) { + for (uint8_t i = 0; i < kDaikinStateLength; i++) _.raw[i] = 0x0; + + _.raw[0] = 0x11; + _.raw[1] = 0xDA; + _.raw[2] = 0x27; + _.raw[4] = 0xC5; + // _.raw[7] is a checksum byte, it will be set by checksum(). + _.raw[8] = 0x11; + _.raw[9] = 0xDA; + _.raw[10] = 0x27; + _.raw[12] = 0x42; + // _.raw[15] is a checksum byte, it will be set by checksum(). + _.raw[16] = 0x11; + _.raw[17] = 0xDA; + _.raw[18] = 0x27; + _.raw[21] = 0x49; + _.raw[22] = 0x1E; + _.raw[24] = 0xB0; + _.raw[27] = 0x06; + _.raw[28] = 0x60; + _.raw[31] = 0xC0; + // _.raw[34] is a checksum byte, it will be set by checksum(). + checksum(); +} + +/// 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 *IRDaikinESP::getRaw(void) { + checksum(); // Ensure correct settings before sending. + 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 Length of the code in bytes. +void IRDaikinESP::setRaw(const uint8_t new_code[], const uint16_t length) { + uint8_t offset = 0; + if (length == kDaikinStateLengthShort) { // Handle the "short" length case. + offset = kDaikinStateLength - kDaikinStateLengthShort; + stateReset(); + } + for (uint8_t i = 0; i < length && i < kDaikinStateLength; i++) + _.raw[i + offset] = new_code[i]; +} + +/// Change the power setting to On. +void IRDaikinESP::on(void) { setPower(true); } + +/// Change the power setting to Off. +void IRDaikinESP::off(void) { setPower(false); } + +/// Change the power setting. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRDaikinESP::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 IRDaikinESP::getPower(void) const { + return _.Power; +} + +/// Set the temperature. +/// @param[in] temp The temperature in degrees celsius. +void IRDaikinESP::setTemp(const uint8_t temp) { + uint8_t degrees = std::max(temp, kDaikinMinTemp); + degrees = std::min(degrees, kDaikinMaxTemp); + _.Temp = degrees; +} + +/// Get the current temperature setting. +/// @return The current setting for temp. in degrees celsius. +uint8_t IRDaikinESP::getTemp(void) const { return _.Temp; } + +/// Set the speed of the fan. +/// @param[in] fan The desired setting. +/// @note 1-5 or kDaikinFanAuto or kDaikinFanQuiet +void IRDaikinESP::setFan(const uint8_t fan) { + // Set the fan speed bits, leave low 4 bits alone + uint8_t fanset; + if (fan == kDaikinFanQuiet || fan == kDaikinFanAuto) + fanset = fan; + else if (fan < kDaikinFanMin || fan > kDaikinFanMax) + fanset = kDaikinFanAuto; + else + fanset = 2 + fan; + _.Fan = fanset; +} + +/// Get the current fan speed setting. +/// @return The current fan speed. +uint8_t IRDaikinESP::getFan(void) const { + uint8_t fan = _.Fan; + if (fan != kDaikinFanQuiet && fan != kDaikinFanAuto) fan -= 2; + return fan; +} + +/// Get the operating mode setting of the A/C. +/// @return The current operating mode setting. +uint8_t IRDaikinESP::getMode(void) const { + return _.Mode; +} + +/// Set the operating mode of the A/C. +/// @param[in] mode The desired operating mode. +void IRDaikinESP::setMode(const uint8_t mode) { + switch (mode) { + case kDaikinAuto: + case kDaikinCool: + case kDaikinHeat: + case kDaikinFan: + case kDaikinDry: + _.Mode = mode; + break; + default: + _.Mode = kDaikinAuto; + } +} + +/// Set the Vertical Swing mode of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRDaikinESP::setSwingVertical(const bool on) { + _.SwingV = (on ? kDaikinSwingOn : kDaikinSwingOff); +} + +/// Get the Vertical Swing mode of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRDaikinESP::getSwingVertical(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 IRDaikinESP::setSwingHorizontal(const bool on) { + _.SwingH = (on ? kDaikinSwingOn : kDaikinSwingOff); +} + +/// Get the Horizontal Swing mode of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRDaikinESP::getSwingHorizontal(void) const { + return _.SwingH; +} + +/// Set the Quiet mode of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRDaikinESP::setQuiet(const bool on) { + _.Quiet = on; + // Powerful & Quiet mode being on are mutually exclusive. + if (on) setPowerful(false); +} + +/// Get the Quiet mode status of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRDaikinESP::getQuiet(void) const { + return _.Quiet; +} + +/// Set the Powerful (Turbo) mode of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRDaikinESP::setPowerful(const bool on) { + _.Powerful = on; + if (on) { + // Powerful, Quiet, & Econo mode being on are mutually exclusive. + setQuiet(false); + setEcono(false); + } +} + +/// Get the Powerful (Turbo) mode of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRDaikinESP::getPowerful(void) const { + return _.Powerful; +} + +/// Set the Sensor mode of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRDaikinESP::setSensor(const bool on) { + _.Sensor = on; +} + +/// Get the Sensor mode of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRDaikinESP::getSensor(void) const { + return _.Sensor; +} + +/// Set the Economy mode of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRDaikinESP::setEcono(const bool on) { + _.Econo = on; + // Powerful & Econo mode being on are mutually exclusive. + if (on) setPowerful(false); +} + +/// Get the Economical mode of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRDaikinESP::getEcono(void) const { + return _.Econo; +} + +/// Set the Mould mode of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRDaikinESP::setMold(const bool on) { + _.Mold = on; +} + +/// Get the Mould mode status of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRDaikinESP::getMold(void) const { + return _.Mold; +} + +/// Set the Comfort mode of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRDaikinESP::setComfort(const bool on) { + _.Comfort = on; +} + +/// Get the Comfort mode of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRDaikinESP::getComfort(void) const { + return _.Comfort; +} + +/// Set the enable status & time of the On Timer. +/// @param[in] starttime The number of minutes past midnight. +void IRDaikinESP::enableOnTimer(const uint16_t starttime) { + _.OnTimer = true; + _.OnTime = starttime; +} + +/// Clear and disable the On timer. +void IRDaikinESP::disableOnTimer(void) { + _.OnTimer = false; + _.OnTime = kDaikinUnusedTime; +} + +/// Get the On Timer time to be sent to the A/C unit. +/// @return The number of minutes past midnight. +uint16_t IRDaikinESP::getOnTime(void) const { + return _.OnTime; +} + +/// Get the enable status of the On Timer. +/// @return true, the setting is on. false, the setting is off. +bool IRDaikinESP::getOnTimerEnabled(void) const { + return _.OnTimer; +} + +/// Set the enable status & time of the Off Timer. +/// @param[in] endtime The number of minutes past midnight. +void IRDaikinESP::enableOffTimer(const uint16_t endtime) { + _.OffTimer = true; + _.OffTime = endtime; +} + +/// Clear and disable the Off timer. +void IRDaikinESP::disableOffTimer(void) { + _.OffTimer = false; + _.OffTime = kDaikinUnusedTime; +} + +/// Get the Off Timer time to be sent to the A/C unit. +/// @return The number of minutes past midnight. +uint16_t IRDaikinESP::getOffTime(void) const { + return _.OffTime; +} + +/// Get the enable status of the Off Timer. +/// @return true, the setting is on. false, the setting is off. +bool IRDaikinESP::getOffTimerEnabled(void) const { + return _.OffTimer; +} + +/// Set the clock on the A/C unit. +/// @param[in] mins_since_midnight Nr. of minutes past midnight. +void IRDaikinESP::setCurrentTime(const uint16_t mins_since_midnight) { + uint16_t mins = mins_since_midnight; + if (mins > 24 * 60) mins = 0; // If > 23:59, set to 00:00 + _.CurrentTime = mins; +} + +/// Get the clock time to be sent to the A/C unit. +/// @return The number of minutes past midnight. +uint16_t IRDaikinESP::getCurrentTime(void) const { + return _.CurrentTime; +} + +/// Set the current day of the week to be sent to the A/C unit. +/// @param[in] day_of_week The numerical representation of the day of the week. +/// @note 1 is SUN, 2 is MON, ..., 7 is SAT +void IRDaikinESP::setCurrentDay(const uint8_t day_of_week) { + _.CurrentDay = day_of_week; +} + +/// Get the current day of the week to be sent to the A/C unit. +/// @return The numerical representation of the day of the week. +/// @note 1 is SUN, 2 is MON, ..., 7 is SAT +uint8_t IRDaikinESP::getCurrentDay(void) const { + return _.CurrentDay; +} + +/// Set the enable status of the Weekly Timer. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRDaikinESP::setWeeklyTimerEnable(const bool on) { + // Bit is cleared for `on`. + _.WeeklyTimer = !on; +} + +/// Get the enable status of the Weekly Timer. +/// @return true, the setting is on. false, the setting is off. +bool IRDaikinESP::getWeeklyTimerEnable(void) const { + return !_.WeeklyTimer; +} + +/// 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 IRDaikinESP::convertMode(const stdAc::opmode_t mode) { + switch (mode) { + case stdAc::opmode_t::kCool: return kDaikinCool; + case stdAc::opmode_t::kHeat: return kDaikinHeat; + case stdAc::opmode_t::kDry: return kDaikinDry; + case stdAc::opmode_t::kFan: return kDaikinFan; + default: return kDaikinAuto; + } +} + +/// 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 IRDaikinESP::convertFan(const stdAc::fanspeed_t speed) { + switch (speed) { + case stdAc::fanspeed_t::kMin: return kDaikinFanQuiet; + case stdAc::fanspeed_t::kLow: return kDaikinFanMin; + case stdAc::fanspeed_t::kMedium: return kDaikinFanMed; + case stdAc::fanspeed_t::kHigh: return kDaikinFanMax - 1; + case stdAc::fanspeed_t::kMax: return kDaikinFanMax; + default: return kDaikinFanAuto; + } +} + +/// 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 IRDaikinESP::toCommonMode(const uint8_t mode) { + switch (mode) { + case kDaikinCool: return stdAc::opmode_t::kCool; + case kDaikinHeat: return stdAc::opmode_t::kHeat; + case kDaikinDry: return stdAc::opmode_t::kDry; + case kDaikinFan: 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 IRDaikinESP::toCommonFanSpeed(const uint8_t speed) { + switch (speed) { + case kDaikinFanMax: return stdAc::fanspeed_t::kMax; + case kDaikinFanMax - 1: return stdAc::fanspeed_t::kHigh; + case kDaikinFanMed: + case kDaikinFanMin + 1: return stdAc::fanspeed_t::kMedium; + case kDaikinFanMin: return stdAc::fanspeed_t::kLow; + case kDaikinFanQuiet: 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 IRDaikinESP::toCommon(void) const { + stdAc::state_t result; + result.protocol = decode_type_t::DAIKIN; + result.model = -1; // No models used. + result.power = _.Power; + result.mode = toCommonMode(_.Mode); + result.celsius = true; + result.degrees = _.Temp; + result.fanspeed = toCommonFanSpeed(getFan()); + 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 = _.Powerful; + result.clean = _.Mold; + result.econo = _.Econo; + // Not supported. + result.filter = false; + result.light = 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 IRDaikinESP::toString(void) const { + String result = ""; + result.reserve(230); // Reserve some heap for the string to reduce fragging. + result += addBoolToString(_.Power, kPowerStr, false); + result += addModeToString(_.Mode, kDaikinAuto, kDaikinCool, kDaikinHeat, + kDaikinDry, kDaikinFan); + result += addTempToString(_.Temp); + result += addFanToString(getFan(), kDaikinFanMax, kDaikinFanMin, + kDaikinFanAuto, kDaikinFanQuiet, kDaikinFanMed); + result += addBoolToString(_.Powerful, kPowerfulStr); + result += addBoolToString(_.Quiet, kQuietStr); + result += addBoolToString(getSensor(), kSensorStr); + result += addBoolToString(_.Mold, kMouldStr); + result += addBoolToString(_.Comfort, kComfortStr); + result += addBoolToString(_.SwingH, kSwingHStr); + result += addBoolToString(_.SwingV, kSwingVStr); + result += addLabeledString(minsToString(_.CurrentTime), kClockStr); + result += addDayToString(_.CurrentDay, -1); + result += addLabeledString(_.OnTimer + ? minsToString(_.OnTime) : kOffStr, + kOnTimerStr); + result += addLabeledString(_.OffTimer + ? minsToString(_.OffTime) : kOffStr, + kOffTimerStr); + result += addBoolToString(getWeeklyTimerEnable(), kWeeklyTimerStr); + return result; +} + +#if DECODE_DAIKIN +/// Decode the supplied Daikin 280-bit message. (DAIKIN) +/// 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. +/// @see https://github.com/mharizanov/Daikin-AC-remote-control-over-the-Internet/tree/master/IRremote +bool IRrecv::decodeDaikin(decode_results *results, uint16_t offset, + const uint16_t nbits, const bool strict) { + // Is there enough data to match successfully? + if (results->rawlen < (2 * (nbits + kDaikinHeaderLength) + + kDaikinSections * (kHeader + kFooter) + kFooter - 1) + + offset) + return false; + + // Compliance + if (strict && nbits != kDaikinBits) return false; + + match_result_t data_result; + + // Header #1 - Doesn't count as data. + data_result = matchData(&(results->rawbuf[offset]), kDaikinHeaderLength, + kDaikinBitMark, kDaikinOneSpace, + kDaikinBitMark, kDaikinZeroSpace, + kDaikinTolerance, kDaikinMarkExcess, false); + offset += data_result.used; + if (data_result.success == false) return false; // Fail + if (data_result.data) return false; // The header bits should be zero. + // Footer + if (!matchMark(results->rawbuf[offset++], kDaikinBitMark, + kDaikinTolerance, kDaikinMarkExcess)) return false; + if (!matchSpace(results->rawbuf[offset++], kDaikinZeroSpace + kDaikinGap, + kDaikinTolerance, kDaikinMarkExcess)) return false; + // Sections + const uint8_t ksectionSize[kDaikinSections] = { + kDaikinSection1Length, kDaikinSection2Length, kDaikinSection3Length}; + uint16_t pos = 0; + for (uint8_t section = 0; section < kDaikinSections; section++) { + uint16_t used; + // Section Header + Section Data (7 bytes) + Section Footer + used = matchGeneric(results->rawbuf + offset, results->state + pos, + results->rawlen - offset, ksectionSize[section] * 8, + kDaikinHdrMark, kDaikinHdrSpace, + kDaikinBitMark, kDaikinOneSpace, + kDaikinBitMark, kDaikinZeroSpace, + kDaikinBitMark, kDaikinZeroSpace + kDaikinGap, + section >= kDaikinSections - 1, + kDaikinTolerance, kDaikinMarkExcess, false); + if (used == 0) return false; + offset += used; + pos += ksectionSize[section]; + } + // Compliance + if (strict) { + // Re-check we got the correct size/length due to the way we read the data. + if (pos * 8 != kDaikinBits) return false; + // Validate the checksum. + if (!IRDaikinESP::validChecksum(results->state)) return false; + } + + // Success + results->decode_type = DAIKIN; + 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_DAIKIN + +#if SEND_DAIKIN2 +/// Send a Daikin2 (312-bit) A/C formatted message. +/// Status: STABLE / Expected to work. +/// @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. +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/582 +void IRsend::sendDaikin2(const unsigned char data[], const uint16_t nbytes, + const uint16_t repeat) { + if (nbytes < kDaikin2Section1Length) + return; // Not enough bytes to send a partial message. + + for (uint16_t r = 0; r <= repeat; r++) { + // Leader + sendGeneric(kDaikin2LeaderMark, kDaikin2LeaderSpace, + 0, 0, 0, 0, 0, 0, (uint64_t) 0, // No data payload. + 0, kDaikin2Freq, false, 0, 50); + // Section #1 + sendGeneric(kDaikin2HdrMark, kDaikin2HdrSpace, kDaikin2BitMark, + kDaikin2OneSpace, kDaikin2BitMark, kDaikin2ZeroSpace, + kDaikin2BitMark, kDaikin2Gap, data, kDaikin2Section1Length, + kDaikin2Freq, false, 0, 50); + // Section #2 + sendGeneric(kDaikin2HdrMark, kDaikin2HdrSpace, kDaikin2BitMark, + kDaikin2OneSpace, kDaikin2BitMark, kDaikin2ZeroSpace, + kDaikin2BitMark, kDaikin2Gap, data + kDaikin2Section1Length, + nbytes - kDaikin2Section1Length, + kDaikin2Freq, false, 0, 50); + } +} +#endif // SEND_DAIKIN2 + +/// 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? +IRDaikin2::IRDaikin2(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 IRDaikin2::begin(void) { _irsend.begin(); } + +#if SEND_DAIKIN2 +/// Send the current internal state as an IR message. +/// @param[in] repeat Nr. of times the message will be repeated. +void IRDaikin2::send(const uint16_t repeat) { + _irsend.sendDaikin2(getRaw(), kDaikin2StateLength, repeat); +} +#endif // SEND_DAIKIN2 + +/// 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 IRDaikin2::validChecksum(uint8_t state[], const uint16_t length) { + // Validate the checksum of section #1. + if (length <= kDaikin2Section1Length - 1 || + state[kDaikin2Section1Length - 1] != sumBytes(state, + kDaikin2Section1Length - 1)) + return false; + // Validate the checksum of section #2 (a.k.a. the rest) + if (length <= kDaikin2Section1Length + 1 || + state[length - 1] != sumBytes(state + kDaikin2Section1Length, + length - kDaikin2Section1Length - 1)) + return false; + return true; +} + +/// Calculate and set the checksum values for the internal state. +void IRDaikin2::checksum(void) { + _.Sum1 = sumBytes(_.raw, kDaikin2Section1Length - 1); + _.Sum2 = sumBytes(_.raw + kDaikin2Section1Length, kDaikin2Section2Length - 1); +} + +/// Reset the internal state to a fixed known good state. +void IRDaikin2::stateReset(void) { + for (uint8_t i = 0; i < kDaikin2StateLength; i++) _.raw[i] = 0x0; + + _.raw[0] = 0x11; + _.raw[1] = 0xDA; + _.raw[2] = 0x27; + _.raw[4] = 0x01; + _.raw[6] = 0xC0; + _.raw[7] = 0x70; + _.raw[8] = 0x08; + _.raw[9] = 0x0C; + _.raw[10] = 0x80; + _.raw[11] = 0x04; + _.raw[12] = 0xB0; + _.raw[13] = 0x16; + _.raw[14] = 0x24; + _.raw[17] = 0xBE; + _.raw[18] = 0xD0; + // _.raw[19] is a checksum byte, it will be set by checksum(). + _.raw[20] = 0x11; + _.raw[21] = 0xDA; + _.raw[22] = 0x27; + _.raw[25] = 0x08; + _.raw[28] = 0xA0; + _.raw[35] = 0xC1; + _.raw[36] = 0x80; + _.raw[37] = 0x60; + // _.raw[38] is a checksum byte, it will be set by checksum(). + disableOnTimer(); + disableOffTimer(); + disableSleepTimer(); + checksum(); +} + +/// 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 *IRDaikin2::getRaw(void) { + checksum(); // Ensure correct settings before sending. + return _.raw; +} + +/// Set the internal state from a valid code for this protocol. +/// @param[in] new_code A valid code for this protocol. +void IRDaikin2::setRaw(const uint8_t new_code[]) { + std::memcpy(_.raw, new_code, kDaikin2StateLength); +} + +/// Change the power setting to On. +void IRDaikin2::on(void) { setPower(true); } + +/// Change the power setting to Off. +void IRDaikin2::off(void) { setPower(false); } + +/// Change the power setting. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRDaikin2::setPower(const bool on) { + _.Power = on; + _.Power2 = !on; +} + +/// Get the value of the current power setting. +/// @return true, the setting is on. false, the setting is off. +bool IRDaikin2::getPower(void) const { return _.Power && !_.Power2; } + +/// Get the operating mode setting of the A/C. +/// @return The current operating mode setting. +uint8_t IRDaikin2::getMode(void) const { return _.Mode; } + +/// Set the operating mode of the A/C. +/// @param[in] desired_mode The desired operating mode. +void IRDaikin2::setMode(const uint8_t desired_mode) { + uint8_t mode = desired_mode; + switch (mode) { + case kDaikinCool: + case kDaikinHeat: + case kDaikinFan: + case kDaikinDry: break; + default: mode = kDaikinAuto; + } + _.Mode = mode; + // Redo the temp setting as Cool mode has a different min temp. + if (mode == kDaikinCool) setTemp(getTemp()); + setHumidity(getHumidity()); // Make sure the humidity is okay for this mode. +} + +/// Set the temperature. +/// @param[in] desired The temperature in degrees celsius. +void IRDaikin2::setTemp(const uint8_t desired) { + // The A/C has a different min temp if in cool mode. + uint8_t temp = std::max( + (_.Mode == kDaikinCool) ? kDaikin2MinCoolTemp : kDaikinMinTemp, + desired); + _.Temp = std::min(kDaikinMaxTemp, temp); + // If the humidity setting is in use, the temp is a fixed value. + if (_.HumidOn) _.Temp = kDaikinMaxTemp; +} + +/// Get the current temperature setting. +/// @return The current setting for temp. in degrees celsius. +uint8_t IRDaikin2::getTemp(void) const { return _.Temp; } + +/// Set the speed of the fan. +/// @param[in] fan The desired setting. +/// @note 1-5 or kDaikinFanAuto or kDaikinFanQuiet +void IRDaikin2::setFan(const uint8_t fan) { + uint8_t fanset; + if (fan == kDaikinFanQuiet || fan == kDaikinFanAuto) + fanset = fan; + else if (fan < kDaikinFanMin || fan > kDaikinFanMax) + fanset = kDaikinFanAuto; + else + fanset = 2 + fan; + _.Fan = fanset; +} + +/// Get the current fan speed setting. +/// @return The current fan speed. +uint8_t IRDaikin2::getFan(void) const { + const uint8_t fan = _.Fan; + switch (fan) { + case kDaikinFanAuto: + case kDaikinFanQuiet: return fan; + default: return fan - 2; + } +} + +/// Set the Vertical Swing mode of the A/C. +/// @param[in] position The position/mode to set the swing to. +void IRDaikin2::setSwingVertical(const uint8_t position) { + switch (position) { + case kDaikin2SwingVHighest: + case kDaikin2SwingVHigh: + case kDaikin2SwingVUpperMiddle: + case kDaikin2SwingVLowerMiddle: + case kDaikin2SwingVLow: + case kDaikin2SwingVLowest: + case kDaikin2SwingVOff: + case kDaikin2SwingVBreeze: + case kDaikin2SwingVCirculate: + case kDaikin2SwingVAuto: + _.SwingV = position; + } +} + +/// Get the Vertical Swing mode of the A/C. +/// @return The native position/mode setting. +uint8_t IRDaikin2::getSwingVertical(void) const { return _.SwingV; } + +/// 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 IRDaikin2::convertSwingV(const stdAc::swingv_t position) { + switch (position) { + case stdAc::swingv_t::kHighest: + case stdAc::swingv_t::kHigh: + case stdAc::swingv_t::kMiddle: + case stdAc::swingv_t::kLow: + case stdAc::swingv_t::kLowest: + return (uint8_t)position + kDaikin2SwingVHighest; + case stdAc::swingv_t::kOff: + return kDaikin2SwingVOff; + default: + return kDaikin2SwingVAuto; + } +} + +/// Convert a native vertical swing postion to it's common equivalent. +/// @param[in] setting A native position to convert. +/// @return The common vertical swing position. +stdAc::swingv_t IRDaikin2::toCommonSwingV(const uint8_t setting) { + switch (setting) { + case kDaikin2SwingVHighest: return stdAc::swingv_t::kHighest; + case kDaikin2SwingVHigh: return stdAc::swingv_t::kHigh; + case kDaikin2SwingVUpperMiddle: + case kDaikin2SwingVLowerMiddle: return stdAc::swingv_t::kMiddle; + case kDaikin2SwingVLow: return stdAc::swingv_t::kLow; + case kDaikin2SwingVLowest: return stdAc::swingv_t::kLowest; + case kDaikin2SwingVOff: return stdAc::swingv_t::kOff; + default: return stdAc::swingv_t::kAuto; + } +} + +/// Set the Horizontal Swing mode of the A/C. +/// @param[in] position The position/mode to set the swing to. +void IRDaikin2::setSwingHorizontal(const uint8_t position) { + _.SwingH = position; +} + +/// Get the Horizontal Swing mode of the A/C. +/// @return The native position/mode setting. +uint8_t IRDaikin2::getSwingHorizontal(void) const { return _.SwingH; } + +/// Set the clock on the A/C unit. +/// @param[in] numMins Nr. of minutes past midnight. +void IRDaikin2::setCurrentTime(const uint16_t numMins) { + uint16_t mins = numMins; + if (numMins > 24 * 60) mins = 0; // If > 23:59, set to 00:00 + _.CurrentTime = mins; +} + +/// Get the clock time to be sent to the A/C unit. +/// @return The number of minutes past midnight. +uint16_t IRDaikin2::getCurrentTime(void) const { return _.CurrentTime; } + +/// Set the enable status & time of the On Timer. +/// @param[in] starttime The number of minutes past midnight. +/// @note Timer location is shared with sleep timer. +void IRDaikin2::enableOnTimer(const uint16_t starttime) { + clearSleepTimerFlag(); + _.OnTimer = true; + _.OnTime = starttime; +} + +/// Clear the On Timer flag. +void IRDaikin2::clearOnTimerFlag(void) { _.OnTimer = false; } + +/// Disable the On timer. +void IRDaikin2::disableOnTimer(void) { + _.OnTime = kDaikinUnusedTime; + clearOnTimerFlag(); + clearSleepTimerFlag(); +} + +/// Get the On Timer time to be sent to the A/C unit. +/// @return The number of minutes past midnight. +uint16_t IRDaikin2::getOnTime(void) const { return _.OnTime; } + +/// Get the enable status of the On Timer. +/// @return true, the setting is on. false, the setting is off. +bool IRDaikin2::getOnTimerEnabled(void) const { return _.OnTimer; } + +/// Set the enable status & time of the Off Timer. +/// @param[in] endtime The number of minutes past midnight. +void IRDaikin2::enableOffTimer(const uint16_t endtime) { + // Set the Off Timer flag. + _.OffTimer = true; + _.OffTime = endtime; +} + +/// Disable the Off timer. +void IRDaikin2::disableOffTimer(void) { + _.OffTime = kDaikinUnusedTime; + // Clear the Off Timer flag. + _.OffTimer = false; +} + +/// Get the Off Timer time to be sent to the A/C unit. +/// @return The number of minutes past midnight. +uint16_t IRDaikin2::getOffTime(void) const { return _.OffTime; } + +/// Get the enable status of the Off Timer. +/// @return true, the setting is on. false, the setting is off. +bool IRDaikin2::getOffTimerEnabled(void) const { return _.OffTimer; } + +/// Get the Beep status of the A/C. +/// @return true, the setting is on. false, the setting is off. +uint8_t IRDaikin2::getBeep(void) const { return _.Beep; } + +/// Set the Beep mode of the A/C. +/// @param[in] beep true, the setting is on. false, the setting is off. +void IRDaikin2::setBeep(const uint8_t beep) { _.Beep = beep; } + +/// Get the Light status of the A/C. +/// @return true, the setting is on. false, the setting is off. +uint8_t IRDaikin2::getLight(void) const { return _.Light; } + +/// Set the Light (LED) mode of the A/C. +/// @param[in] light true, the setting is on. false, the setting is off. +void IRDaikin2::setLight(const uint8_t light) { _.Light = light; } + +/// Set the Mould (filter) mode of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRDaikin2::setMold(const bool on) { _.Mold = on; } + +/// Get the Mould (filter) mode status of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRDaikin2::getMold(void) const { return _.Mold; } + +/// Set the Auto clean mode of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRDaikin2::setClean(const bool on) { _.Clean = on; } + +/// Get the Auto Clean mode status of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRDaikin2::getClean(void) const { return _.Clean; } + +/// Set the Fresh Air mode of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRDaikin2::setFreshAir(const bool on) { _.FreshAir = on; } + +/// Get the Fresh Air mode status of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRDaikin2::getFreshAir(void) const { return _.FreshAir; } + +/// Set the (High) Fresh Air mode of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRDaikin2::setFreshAirHigh(const bool on) { _.FreshAirHigh = on; } + +/// Get the (High) Fresh Air mode status of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRDaikin2::getFreshAirHigh(void) const { return _.FreshAirHigh; } + +/// Set the Automatic Eye (Sensor) mode of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRDaikin2::setEyeAuto(bool on) { _.EyeAuto = on; } + +/// Get the Automaitc Eye (Sensor) mode status of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRDaikin2::getEyeAuto(void) const { return _.EyeAuto; } + +/// Set the Eye (Sensor) mode of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRDaikin2::setEye(bool on) { _.Eye = on; } + +/// Get the Eye (Sensor) mode status of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRDaikin2::getEye(void) const { return _.Eye; } + +/// Set the Economy mode of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRDaikin2::setEcono(bool on) { _.Econo = on; } + +/// Get the Economical mode of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRDaikin2::getEcono(void) const { return _.Econo; } + +/// Set the enable status & time of the Sleep Timer. +/// @param[in] sleeptime The number of minutes past midnight. +/// @note The Timer location is shared with On Timer. +void IRDaikin2::enableSleepTimer(const uint16_t sleeptime) { + enableOnTimer(sleeptime); + clearOnTimerFlag(); + _.SleepTimer = true; +} + +/// Clear the sleep timer flag. +void IRDaikin2::clearSleepTimerFlag(void) { _.SleepTimer = false; } + +/// Disable the sleep timer. +void IRDaikin2::disableSleepTimer(void) { disableOnTimer(); } + +/// Get the Sleep Timer time to be sent to the A/C unit. +/// @return The number of minutes past midnight. +uint16_t IRDaikin2::getSleepTime(void) const { return getOnTime(); } + +/// Get the Sleep timer enabled status of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRDaikin2::getSleepTimerEnabled(void) const { return _.SleepTimer; } + +/// Set the Quiet mode of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRDaikin2::setQuiet(const bool on) { + _.Quiet = on; + // Powerful & Quiet mode being on are mutually exclusive. + if (on) setPowerful(false); +} + +/// Get the Quiet mode status of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRDaikin2::getQuiet(void) const { return _.Quiet; } + +/// Set the Powerful (Turbo) mode of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRDaikin2::setPowerful(const bool on) { + _.Powerful = on; + // Powerful & Quiet mode being on are mutually exclusive. + if (on) setQuiet(false); +} + +/// Get the Powerful (Turbo) mode of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRDaikin2::getPowerful(void) const { return _.Powerful; } + +/// Set the Purify (Filter) mode of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRDaikin2::setPurify(const bool on) { _.Purify = on; } + +/// Get the Purify (Filter) mode status of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRDaikin2::getPurify(void) const { return _.Purify; } + +/// Get the Humidity percentage setting of the A/C. +/// @return The setting percentage. 255 is Automatic. 0 is Off. +uint8_t IRDaikin2::getHumidity(void) const { return _.Humidity; } + +/// Set the Humidity percentage setting of the A/C. +/// @param[in] percent Percentage humidty. 255 is Auto. 0 is Off. +/// @note Only available in Dry & Heat modes, otherwise it is Off. +void IRDaikin2::setHumidity(const uint8_t percent) { + _.Humidity = kDaikin2HumidityOff; // Default to off. + switch (getMode()) { + case kDaikinHeat: + switch (percent) { + case kDaikin2HumidityOff: + case kDaikin2HumidityHeatLow: + case kDaikin2HumidityHeatMedium: + case kDaikin2HumidityHeatHigh: + case kDaikin2HumidityAuto: + _.Humidity = percent; + } + break; + case kDaikinDry: + switch (percent) { + case kDaikin2HumidityOff: + case kDaikin2HumidityDryLow: + case kDaikin2HumidityDryMedium: + case kDaikin2HumidityDryHigh: + case kDaikin2HumidityAuto: + _.Humidity = percent; + } + break; + } + _.HumidOn = (_.Humidity != kDaikin2HumidityOff); // Enabled? + setTemp(getTemp()); // Adjust the temperature if we need to. +} + +/// 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 IRDaikin2::convertMode(const stdAc::opmode_t mode) { + return IRDaikinESP::convertMode(mode); +} + +/// 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 IRDaikin2::convertFan(const stdAc::fanspeed_t speed) { + return IRDaikinESP::convertFan(speed); +} + +/// Convert a stdAc::swingh_t enum into it's native setting. +/// @param[in] position The enum to be converted. +/// @return The native equivalent of the enum. +uint8_t IRDaikin2::convertSwingH(const stdAc::swingh_t position) { + switch (position) { + case stdAc::swingh_t::kAuto: return kDaikin2SwingHSwing; + case stdAc::swingh_t::kLeftMax: return kDaikin2SwingHLeftMax; + case stdAc::swingh_t::kLeft: return kDaikin2SwingHLeft; + case stdAc::swingh_t::kMiddle: return kDaikin2SwingHMiddle; + case stdAc::swingh_t::kRight: return kDaikin2SwingHRight; + case stdAc::swingh_t::kRightMax: return kDaikin2SwingHRightMax; + case stdAc::swingh_t::kWide: return kDaikin2SwingHWide; + default: return kDaikin2SwingHAuto; + } +} + +/// Convert a native horizontal swing postion to it's common equivalent. +/// @param[in] setting A native position to convert. +/// @return The common horizontal swing position. +stdAc::swingh_t IRDaikin2::toCommonSwingH(const uint8_t setting) { + switch (setting) { + case kDaikin2SwingHSwing: return stdAc::swingh_t::kAuto; + case kDaikin2SwingHLeftMax: return stdAc::swingh_t::kLeftMax; + case kDaikin2SwingHLeft: return stdAc::swingh_t::kLeft; + case kDaikin2SwingHMiddle: return stdAc::swingh_t::kMiddle; + case kDaikin2SwingHRight: return stdAc::swingh_t::kRight; + case kDaikin2SwingHRightMax: return stdAc::swingh_t::kRightMax; + case kDaikin2SwingHWide: return stdAc::swingh_t::kWide; + 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 IRDaikin2::toCommon(void) const { + stdAc::state_t result; + result.protocol = decode_type_t::DAIKIN2; + result.model = -1; // No models used. + result.power = getPower(); + result.mode = IRDaikinESP::toCommonMode(_.Mode); + result.celsius = true; + result.degrees = _.Temp; + result.fanspeed = IRDaikinESP::toCommonFanSpeed(getFan()); + result.swingv = toCommonSwingV(_.SwingV); + result.swingh = toCommonSwingH(_.SwingH); + result.quiet = _.Quiet; + result.light = _.Light != 3; // 3 is Off, everything else is On. + result.turbo = _.Powerful; + result.clean = _.Mold; + result.econo = _.Econo; + result.filter = _.Purify; + result.beep = _.Beep != 3; // 3 is Off, everything else is On. + result.sleep = _.SleepTimer ? getSleepTime() : -1; + // Not supported. + result.clock = -1; + return result; +} + +/// Convert the current internal state into a human readable string. +/// @return A human readable string. +String IRDaikin2::toString(void) const { + String result = ""; + result.reserve(330); // Reserve some heap for the string to reduce fragging. + result += addBoolToString(getPower(), kPowerStr, false); + result += addModeToString(_.Mode, kDaikinAuto, kDaikinCool, kDaikinHeat, + kDaikinDry, kDaikinFan); + result += addTempToString(_.Temp); + result += addFanToString(getFan(), kDaikinFanMax, kDaikinFanMin, + kDaikinFanAuto, kDaikinFanQuiet, kDaikinFanMed); + result += addSwingVToString(_.SwingV, kDaikin2SwingVAuto, + kDaikin2SwingVHighest, kDaikin2SwingVHigh, + kDaikin2SwingVUpperMiddle, + kDaikin2SwingVAuto, // Middle is unused. + kDaikin2SwingVLowerMiddle, + kDaikin2SwingVLow, kDaikin2SwingVLowest, + kDaikin2SwingVOff, // Off is unused + kDaikin2SwingVSwing, kDaikin2SwingVBreeze, + kDaikin2SwingVCirculate); + result += addSwingHToString(_.SwingH, kDaikin2SwingHAuto, + kDaikin2SwingHLeftMax, + kDaikin2SwingHLeft, + kDaikin2SwingHMiddle, + kDaikin2SwingHRight, + kDaikin2SwingHRightMax, + kDaikin2SwingHOff, + kDaikin2SwingHAuto, // Unused + kDaikin2SwingHAuto, // Unused + kDaikin2SwingHAuto, // Unused + kDaikin2SwingHWide); + result += addLabeledString(minsToString(_.CurrentTime), kClockStr); + result += addLabeledString( + _.OnTimer ? minsToString(_.OnTime) : kOffStr, kOnTimerStr); + result += addLabeledString( + _.OffTimer ? minsToString(_.OffTime) : kOffStr, + kOffTimerStr); + result += addLabeledString( + _.SleepTimer ? minsToString(getSleepTime()) : kOffStr, + kSleepTimerStr); + result += addIntToString(_.Beep, kBeepStr); + result += kSpaceLBraceStr; + switch (_.Beep) { + case kDaikinBeepLoud: + result += kLoudStr; + break; + case kDaikinBeepQuiet: + result += kQuietStr; + break; + case kDaikinBeepOff: + result += kOffStr; + break; + default: + result += kUnknownStr; + } + result += ')'; + result += addIntToString(_.Light, kLightStr); + result += kSpaceLBraceStr; + switch (_.Light) { + case kDaikinLightBright: + result += kHighStr; + break; + case kDaikinLightDim: + result += kLowStr; + break; + case kDaikinLightOff: + result += kOffStr; + break; + default: + result += kUnknownStr; + } + result += ')'; + result += addBoolToString(_.Mold, kMouldStr); + result += addBoolToString(_.Clean, kCleanStr); + result += addLabeledString( + _.FreshAir ? (_.FreshAirHigh ? kHighStr : kOnStr) : kOffStr, + kFreshStr); + result += addBoolToString(_.Eye, kEyeStr); + result += addBoolToString(_.EyeAuto, kEyeAutoStr); + result += addBoolToString(_.Quiet, kQuietStr); + result += addBoolToString(_.Powerful, kPowerfulStr); + result += addBoolToString(_.Purify, kPurifyStr); + result += addBoolToString(_.Econo, kEconoStr); + result += addIntToString(_.Humidity, kHumidStr); + switch (_.Humidity) { + case kDaikin2HumidityOff: + case kDaikin2HumidityAuto: + result += kSpaceLBraceStr; + result += _.Humidity ? kAutoStr : kOffStr; + result += ')'; + break; + default: + result += '%'; + } + return result; +} + +#if DECODE_DAIKIN2 +/// Decode the supplied Daikin 312-bit message. (DAIKIN2) +/// Status: STABLE / Works as expected. +/// @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::decodeDaikin2(decode_results *results, uint16_t offset, + const uint16_t nbits, const bool strict) { + if (results->rawlen < 2 * (nbits + kHeader + kFooter) + kHeader - 1 + offset) + return false; + + // Compliance + if (strict && nbits != kDaikin2Bits) return false; + + const uint8_t ksectionSize[kDaikin2Sections] = {kDaikin2Section1Length, + kDaikin2Section2Length}; + + // Leader + if (!matchMark(results->rawbuf[offset++], kDaikin2LeaderMark, + _tolerance + kDaikin2Tolerance)) return false; + if (!matchSpace(results->rawbuf[offset++], kDaikin2LeaderSpace, + _tolerance + kDaikin2Tolerance)) return false; + + // Sections + uint16_t pos = 0; + for (uint8_t section = 0; section < kDaikin2Sections; section++) { + uint16_t used; + // Section Header + Section Data + Section Footer + used = matchGeneric(results->rawbuf + offset, results->state + pos, + results->rawlen - offset, ksectionSize[section] * 8, + kDaikin2HdrMark, kDaikin2HdrSpace, + kDaikin2BitMark, kDaikin2OneSpace, + kDaikin2BitMark, kDaikin2ZeroSpace, + kDaikin2BitMark, kDaikin2Gap, + section >= kDaikin2Sections - 1, + _tolerance + kDaikin2Tolerance, kDaikinMarkExcess, + false); + if (used == 0) return false; + offset += used; + pos += ksectionSize[section]; + } + // Compliance + if (strict) { + // Re-check we got the correct size/length due to the way we read the data. + if (pos * 8 != kDaikin2Bits) return false; + // Validate the checksum. + if (!IRDaikin2::validChecksum(results->state)) return false; + } + + // Success + results->decode_type = DAIKIN2; + 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_DAIKIN2 + +#if SEND_DAIKIN216 +/// Send a Daikin216 (216-bit) A/C formatted message. +/// Status: Alpha / Untested on a real device. +/// @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. +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/689 +/// @see https://github.com/danny-source/Arduino_DY_IRDaikin +void IRsend::sendDaikin216(const unsigned char data[], const uint16_t nbytes, + const uint16_t repeat) { + if (nbytes < kDaikin216Section1Length) + return; // Not enough bytes to send a partial message. + + for (uint16_t r = 0; r <= repeat; r++) { + // Section #1 + sendGeneric(kDaikin216HdrMark, kDaikin216HdrSpace, kDaikin216BitMark, + kDaikin216OneSpace, kDaikin216BitMark, kDaikin216ZeroSpace, + kDaikin216BitMark, kDaikin216Gap, data, + kDaikin216Section1Length, + kDaikin216Freq, false, 0, kDutyDefault); + // Section #2 + sendGeneric(kDaikin216HdrMark, kDaikin216HdrSpace, kDaikin216BitMark, + kDaikin216OneSpace, kDaikin216BitMark, kDaikin216ZeroSpace, + kDaikin216BitMark, kDaikin216Gap, + data + kDaikin216Section1Length, + nbytes - kDaikin216Section1Length, + kDaikin216Freq, false, 0, kDutyDefault); + } +} +#endif // SEND_DAIKIN216 + +/// 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? +IRDaikin216::IRDaikin216(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 IRDaikin216::begin(void) { _irsend.begin(); } + +#if SEND_DAIKIN216 +/// Send the current internal state as an IR message. +/// @param[in] repeat Nr. of times the message will be repeated. +void IRDaikin216::send(const uint16_t repeat) { + _irsend.sendDaikin216(getRaw(), kDaikin216StateLength, repeat); +} +#endif // SEND_DAIKIN216 + +/// 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 IRDaikin216::validChecksum(uint8_t state[], const uint16_t length) { + // Validate the checksum of section #1. + if (length <= kDaikin216Section1Length - 1 || + state[kDaikin216Section1Length - 1] != sumBytes( + state, kDaikin216Section1Length - 1)) + return false; + // Validate the checksum of section #2 (a.k.a. the rest) + if (length <= kDaikin216Section1Length + 1 || + state[length - 1] != sumBytes(state + kDaikin216Section1Length, + length - kDaikin216Section1Length - 1)) + return false; + return true; +} + +/// Calculate and set the checksum values for the internal state. +void IRDaikin216::checksum(void) { + _.Sum1 = sumBytes(_.raw, kDaikin216Section1Length - 1); + _.Sum2 = sumBytes(_.raw + kDaikin216Section1Length, + kDaikin216Section2Length - 1); +} + +/// Reset the internal state to a fixed known good state. +void IRDaikin216::stateReset(void) { + for (uint8_t i = 0; i < kDaikin216StateLength; i++) _.raw[i] = 0x00; + _.raw[0] = 0x11; + _.raw[1] = 0xDA; + _.raw[2] = 0x27; + _.raw[3] = 0xF0; + // _.raw[7] is a checksum byte, it will be set by checksum(). + _.raw[8] = 0x11; + _.raw[9] = 0xDA; + _.raw[10] = 0x27; + _.raw[23] = 0xC0; + // _.raw[26] is a checksum byte, it will be set by checksum(). +} + +/// 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 *IRDaikin216::getRaw(void) { + checksum(); // Ensure correct settings before sending. + return _.raw; +} + +/// Set the internal state from a valid code for this protocol. +/// @param[in] new_code A valid code for this protocol. +void IRDaikin216::setRaw(const uint8_t new_code[]) { + std::memcpy(_.raw, new_code, kDaikin216StateLength); +} + +/// Change the power setting to On. +void IRDaikin216::on(void) { setPower(true); } + +/// Change the power setting to Off. +void IRDaikin216::off(void) { setPower(false); } + +/// Change the power setting. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRDaikin216::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 IRDaikin216::getPower(void) const { return _.Power; } + +/// Get the operating mode setting of the A/C. +/// @return The current operating mode setting. +uint8_t IRDaikin216::getMode(void) const { return _.Mode; } + +/// Set the operating mode of the A/C. +/// @param[in] mode The desired operating mode. +void IRDaikin216::setMode(const uint8_t mode) { + switch (mode) { + case kDaikinAuto: + case kDaikinCool: + case kDaikinHeat: + case kDaikinFan: + case kDaikinDry: + _.Mode = mode; + break; + default: + _.Mode = kDaikinAuto; + } +} + +/// 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 IRDaikin216::convertMode(const stdAc::opmode_t mode) { + return IRDaikinESP::convertMode(mode); +} + +/// Set the temperature. +/// @param[in] temp The temperature in degrees celsius. +void IRDaikin216::setTemp(const uint8_t temp) { + uint8_t degrees = std::max(temp, kDaikinMinTemp); + degrees = std::min(degrees, kDaikinMaxTemp); + _.Temp = degrees; +} + +/// Get the current temperature setting. +/// @return The current setting for temp. in degrees celsius. +uint8_t IRDaikin216::getTemp(void) const { return _.Temp; } + +/// Set the speed of the fan. +/// @param[in] fan The desired setting. +/// @note 1-5 or kDaikinFanAuto or kDaikinFanQuiet +void IRDaikin216::setFan(const uint8_t fan) { + // Set the fan speed bits, leave low 4 bits alone + uint8_t fanset; + if (fan == kDaikinFanQuiet || fan == kDaikinFanAuto) + fanset = fan; + else if (fan < kDaikinFanMin || fan > kDaikinFanMax) + fanset = kDaikinFanAuto; + else + fanset = 2 + fan; + _.Fan = fanset; +} + +/// Get the current fan speed setting. +/// @return The current fan speed. +uint8_t IRDaikin216::getFan(void) const { + uint8_t fan = _.Fan; + if (fan != kDaikinFanQuiet && fan != kDaikinFanAuto) fan -= 2; + 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 IRDaikin216::convertFan(const stdAc::fanspeed_t speed) { + return IRDaikinESP::convertFan(speed); +} + +/// Set the Vertical Swing mode of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRDaikin216::setSwingVertical(const bool on) { + _.SwingV = (on ? kDaikin216SwingOn : kDaikin216SwingOff); +} + +/// Get the Vertical Swing mode of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRDaikin216::getSwingVertical(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 IRDaikin216::setSwingHorizontal(const bool on) { + _.SwingH = (on ? kDaikin216SwingOn : kDaikin216SwingOff); +} + +/// Get the Horizontal Swing mode of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRDaikin216::getSwingHorizontal(void) const { return _.SwingH; } + +/// Set the Quiet mode of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +/// @note This is a horrible hack till someone works out the quiet mode bit. +void IRDaikin216::setQuiet(const bool on) { + if (on) { + setFan(kDaikinFanQuiet); + // Powerful & Quiet mode being on are mutually exclusive. + setPowerful(false); + } else if (getFan() == kDaikinFanQuiet) { + setFan(kDaikinFanAuto); + } +} + +/// Get the Quiet mode status of the A/C. +/// @return true, the setting is on. false, the setting is off. +/// @note This is a horrible hack till someone works out the quiet mode bit. +bool IRDaikin216::getQuiet(void) const { return getFan() == kDaikinFanQuiet; } + +/// Set the Powerful (Turbo) mode of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRDaikin216::setPowerful(const bool on) { + _.Powerful = on; + // Powerful & Quiet mode being on are mutually exclusive. + if (on) setQuiet(false); +} + +/// Get the Powerful (Turbo) mode of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRDaikin216::getPowerful(void) const { return _.Powerful; } + +/// Convert the current internal state into its stdAc::state_t equivalent. +/// @return The stdAc equivalent of the native settings. +stdAc::state_t IRDaikin216::toCommon(void) const { + stdAc::state_t result; + result.protocol = decode_type_t::DAIKIN216; + result.model = -1; // No models used. + result.power = _.Power; + result.mode = IRDaikinESP::toCommonMode(_.Mode); + result.celsius = true; + result.degrees = _.Temp; + result.fanspeed = IRDaikinESP::toCommonFanSpeed(getFan()); + result.swingv = _.SwingV ? stdAc::swingv_t::kAuto : + stdAc::swingv_t::kOff; + result.swingh = _.SwingH ? stdAc::swingh_t::kAuto : + stdAc::swingh_t::kOff; + result.quiet = getQuiet(); + result.turbo = _.Powerful; + // Not supported. + result.light = false; + result.clean = 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 IRDaikin216::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, kDaikinAuto, kDaikinCool, kDaikinHeat, + kDaikinDry, kDaikinFan); + result += addTempToString(_.Temp); + result += addFanToString(getFan(), kDaikinFanMax, kDaikinFanMin, + kDaikinFanAuto, kDaikinFanQuiet, kDaikinFanMed); + result += addBoolToString(_.SwingH, kSwingHStr); + result += addBoolToString(_.SwingV, kSwingVStr); + result += addBoolToString(getQuiet(), kQuietStr); + result += addBoolToString(_.Powerful, kPowerfulStr); + return result; +} + +#if DECODE_DAIKIN216 +/// Decode the supplied Daikin 216-bit message. (DAIKIN216) +/// Status: STABLE / Should 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/689 +/// @see https://github.com/danny-source/Arduino_DY_IRDaikin +bool IRrecv::decodeDaikin216(decode_results *results, uint16_t offset, + const uint16_t nbits, const bool strict) { + if (results->rawlen < 2 * (nbits + kHeader + kFooter) - 1 + offset) + return false; + + // Compliance + if (strict && nbits != kDaikin216Bits) return false; + + const uint8_t ksectionSize[kDaikin216Sections] = {kDaikin216Section1Length, + kDaikin216Section2Length}; + // Sections + uint16_t pos = 0; + for (uint8_t section = 0; section < kDaikin216Sections; section++) { + uint16_t used; + // Section Header + Section Data + Section Footer + used = matchGeneric(results->rawbuf + offset, results->state + pos, + results->rawlen - offset, ksectionSize[section] * 8, + kDaikin216HdrMark, kDaikin216HdrSpace, + kDaikin216BitMark, kDaikin216OneSpace, + kDaikin216BitMark, kDaikin216ZeroSpace, + kDaikin216BitMark, kDaikin216Gap, + section >= kDaikin216Sections - 1, + kDaikinTolerance, kDaikinMarkExcess, false); + if (used == 0) return false; + offset += used; + pos += ksectionSize[section]; + } + // Compliance + if (strict) { + if (pos * 8 != kDaikin216Bits) return false; + // Validate the checksum. + if (!IRDaikin216::validChecksum(results->state)) return false; + } + + // Success + results->decode_type = decode_type_t::DAIKIN216; + 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_DAIKIN216 + +#if SEND_DAIKIN160 +/// Send a Daikin160 (160-bit) A/C formatted message. +/// Status: STABLE / Confirmed 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. +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/731 +void IRsend::sendDaikin160(const unsigned char data[], const uint16_t nbytes, + const uint16_t repeat) { + if (nbytes < kDaikin160Section1Length) + return; // Not enough bytes to send a partial message. + + for (uint16_t r = 0; r <= repeat; r++) { + // Section #1 + sendGeneric(kDaikin160HdrMark, kDaikin160HdrSpace, kDaikin160BitMark, + kDaikin160OneSpace, kDaikin160BitMark, kDaikin160ZeroSpace, + kDaikin160BitMark, kDaikin160Gap, data, + kDaikin160Section1Length, + kDaikin160Freq, false, 0, kDutyDefault); + // Section #2 + sendGeneric(kDaikin160HdrMark, kDaikin160HdrSpace, kDaikin160BitMark, + kDaikin160OneSpace, kDaikin160BitMark, kDaikin160ZeroSpace, + kDaikin160BitMark, kDaikin160Gap, + data + kDaikin160Section1Length, + nbytes - kDaikin160Section1Length, + kDaikin160Freq, false, 0, kDutyDefault); + } +} +#endif // SEND_DAIKIN160 + +/// 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? +IRDaikin160::IRDaikin160(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 IRDaikin160::begin(void) { _irsend.begin(); } + +/// 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 IRDaikin160::validChecksum(uint8_t state[], const uint16_t length) { + // Validate the checksum of section #1. + if (length <= kDaikin160Section1Length - 1 || + state[kDaikin160Section1Length - 1] != sumBytes( + state, kDaikin160Section1Length - 1)) + return false; + // Validate the checksum of section #2 (a.k.a. the rest) + if (length <= kDaikin160Section1Length + 1 || + state[length - 1] != sumBytes(state + kDaikin160Section1Length, + length - kDaikin160Section1Length - 1)) + return false; + return true; +} + +/// Calculate and set the checksum values for the internal state. +void IRDaikin160::checksum(void) { + _.Sum1 = sumBytes(_.raw, kDaikin160Section1Length - 1); + _.Sum2 = sumBytes(_.raw + kDaikin160Section1Length, + kDaikin160Section2Length - 1); +} + +/// Reset the internal state to a fixed known good state. +void IRDaikin160::stateReset(void) { + for (uint8_t i = 0; i < kDaikin160StateLength; i++) _.raw[i] = 0x00; + _.raw[0] = 0x11; + _.raw[1] = 0xDA; + _.raw[2] = 0x27; + _.raw[3] = 0xF0; + _.raw[4] = 0x0D; + // _.raw[6] is a checksum byte, it will be set by checksum(). + _.raw[7] = 0x11; + _.raw[8] = 0xDA; + _.raw[9] = 0x27; + _.raw[11] = 0xD3; + _.raw[12] = 0x30; + _.raw[13] = 0x11; + _.raw[16] = 0x1E; + _.raw[17] = 0x0A; + _.raw[18] = 0x08; + // _.raw[19] is a checksum byte, it will be set by checksum(). +} + +/// 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 *IRDaikin160::getRaw(void) { + checksum(); // Ensure correct settings before sending. + return _.raw; +} + +/// Set the internal state from a valid code for this protocol. +/// @param[in] new_code A valid code for this protocol. +void IRDaikin160::setRaw(const uint8_t new_code[]) { + std::memcpy(_.raw, new_code, kDaikin160StateLength); +} + +#if SEND_DAIKIN160 +/// Send the current internal state as an IR message. +/// @param[in] repeat Nr. of times the message will be repeated. +void IRDaikin160::send(const uint16_t repeat) { + _irsend.sendDaikin160(getRaw(), kDaikin160StateLength, repeat); +} +#endif // SEND_DAIKIN160 + +/// Change the power setting to On. +void IRDaikin160::on(void) { setPower(true); } + +/// Change the power setting to Off. +void IRDaikin160::off(void) { setPower(false); } + +/// Change the power setting. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRDaikin160::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 IRDaikin160::getPower(void) const { return _.Power; } + +/// Get the operating mode setting of the A/C. +/// @return The current operating mode setting. +uint8_t IRDaikin160::getMode(void) const { return _.Mode; } + +/// Set the operating mode of the A/C. +/// @param[in] mode The desired operating mode. +void IRDaikin160::setMode(const uint8_t mode) { + switch (mode) { + case kDaikinAuto: + case kDaikinCool: + case kDaikinHeat: + case kDaikinFan: + case kDaikinDry: + _.Mode = mode; + break; + default: _.Mode = kDaikinAuto; + } +} + +/// 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 IRDaikin160::convertMode(const stdAc::opmode_t mode) { + return IRDaikinESP::convertMode(mode); +} + +/// Set the temperature. +/// @param[in] temp The temperature in degrees celsius. +void IRDaikin160::setTemp(const uint8_t temp) { + uint8_t degrees = std::max(temp, kDaikinMinTemp); + degrees = std::min(degrees, kDaikinMaxTemp) - 10; + _.Temp = degrees; +} + +/// Get the current temperature setting. +/// @return The current setting for temp. in degrees celsius. +uint8_t IRDaikin160::getTemp(void) const { return _.Temp + 10; } + +/// Set the speed of the fan. +/// @param[in] fan The desired setting. +/// @note 1-5 or kDaikinFanAuto or kDaikinFanQuiet +void IRDaikin160::setFan(const uint8_t fan) { + uint8_t fanset; + if (fan == kDaikinFanQuiet || fan == kDaikinFanAuto) + fanset = fan; + else if (fan < kDaikinFanMin || fan > kDaikinFanMax) + fanset = kDaikinFanAuto; + else + fanset = 2 + fan; + _.Fan = fanset; +} + +/// Get the current fan speed setting. +/// @return The current fan speed. +uint8_t IRDaikin160::getFan(void) const { + uint8_t fan = _.Fan; + if (fan != kDaikinFanQuiet && fan != kDaikinFanAuto) fan -= 2; + 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 IRDaikin160::convertFan(const stdAc::fanspeed_t speed) { + switch (speed) { + case stdAc::fanspeed_t::kMin: return kDaikinFanMin; + case stdAc::fanspeed_t::kLow: return kDaikinFanMin + 1; + case stdAc::fanspeed_t::kMedium: return kDaikinFanMin + 2; + case stdAc::fanspeed_t::kHigh: return kDaikinFanMax - 1; + case stdAc::fanspeed_t::kMax: return kDaikinFanMax; + default: + return kDaikinFanAuto; + } +} + +/// Set the Vertical Swing mode of the A/C. +/// @param[in] position The position/mode to set the swing to. +void IRDaikin160::setSwingVertical(const uint8_t position) { + switch (position) { + case kDaikin160SwingVLowest: + case kDaikin160SwingVLow: + case kDaikin160SwingVMiddle: + case kDaikin160SwingVHigh: + case kDaikin160SwingVHighest: + case kDaikin160SwingVAuto: + _.SwingV = position; + break; + default: _.SwingV = kDaikin160SwingVAuto; + } +} + +/// Get the Vertical Swing mode of the A/C. +/// @return The native position/mode setting. +uint8_t IRDaikin160::getSwingVertical(void) const { return _.SwingV; } + +/// 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 IRDaikin160::convertSwingV(const stdAc::swingv_t position) { + switch (position) { + case stdAc::swingv_t::kHighest: + case stdAc::swingv_t::kHigh: + case stdAc::swingv_t::kMiddle: + case stdAc::swingv_t::kLow: + case stdAc::swingv_t::kLowest: + return kDaikin160SwingVHighest + 1 - (uint8_t)position; + default: + return kDaikin160SwingVAuto; + } +} + +/// Convert a native vertical swing postion to it's common equivalent. +/// @param[in] setting A native position to convert. +/// @return The common vertical swing position. +stdAc::swingv_t IRDaikin160::toCommonSwingV(const uint8_t setting) { + switch (setting) { + case kDaikin160SwingVHighest: return stdAc::swingv_t::kHighest; + case kDaikin160SwingVHigh: return stdAc::swingv_t::kHigh; + case kDaikin160SwingVMiddle: return stdAc::swingv_t::kMiddle; + case kDaikin160SwingVLow: return stdAc::swingv_t::kLow; + case kDaikin160SwingVLowest: return stdAc::swingv_t::kLowest; + default: + return stdAc::swingv_t::kAuto; + } +} + +/// Convert the current internal state into its stdAc::state_t equivalent. +/// @return The stdAc equivalent of the native settings. +stdAc::state_t IRDaikin160::toCommon(void) const { + stdAc::state_t result; + result.protocol = decode_type_t::DAIKIN160; + result.model = -1; // No models used. + result.power = _.Power; + result.mode = IRDaikinESP::toCommonMode(_.Mode); + result.celsius = true; + result.degrees = getTemp(); + result.fanspeed = IRDaikinESP::toCommonFanSpeed(getFan()); + result.swingv = toCommonSwingV(_.SwingV); + // Not supported. + result.swingh = stdAc::swingh_t::kOff; + result.quiet = false; + result.turbo = false; + result.light = false; + result.clean = 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 IRDaikin160::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, kDaikinAuto, kDaikinCool, kDaikinHeat, + kDaikinDry, kDaikinFan); + result += addTempToString(getTemp()); + result += addFanToString(getFan(), kDaikinFanMax, kDaikinFanMin, + kDaikinFanAuto, kDaikinFanQuiet, kDaikinFanMed); + result += addIntToString(_.SwingV, kSwingVStr); + result += kSpaceLBraceStr; + switch (_.SwingV) { + case kDaikin160SwingVHighest: result += kHighestStr; break; + case kDaikin160SwingVHigh: result += kHighStr; break; + case kDaikin160SwingVMiddle: result += kMiddleStr; break; + case kDaikin160SwingVLow: result += kLowStr; break; + case kDaikin160SwingVLowest: result += kLowestStr; break; + case kDaikin160SwingVAuto: result += kAutoStr; break; + default: result += kUnknownStr; + } + result += ')'; + return result; +} + +#if DECODE_DAIKIN160 +/// Decode the supplied Daikin 160-bit message. (DAIKIN160) +/// 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/731 +bool IRrecv::decodeDaikin160(decode_results *results, uint16_t offset, + const uint16_t nbits, const bool strict) { + if (results->rawlen < 2 * (nbits + kHeader + kFooter) - 1 + offset) + return false; + + // Compliance + if (strict && nbits != kDaikin160Bits) return false; + + const uint8_t ksectionSize[kDaikin160Sections] = {kDaikin160Section1Length, + kDaikin160Section2Length}; + + // Sections + uint16_t pos = 0; + for (uint8_t section = 0; section < kDaikin160Sections; section++) { + uint16_t used; + // Section Header + Section Data (7 bytes) + Section Footer + used = matchGeneric(results->rawbuf + offset, results->state + pos, + results->rawlen - offset, ksectionSize[section] * 8, + kDaikin160HdrMark, kDaikin160HdrSpace, + kDaikin160BitMark, kDaikin160OneSpace, + kDaikin160BitMark, kDaikin160ZeroSpace, + kDaikin160BitMark, kDaikin160Gap, + section >= kDaikin160Sections - 1, + kDaikinTolerance, kDaikinMarkExcess, false); + if (used == 0) return false; + offset += used; + pos += ksectionSize[section]; + } + // Compliance + if (strict) { + // Validate the checksum. + if (!IRDaikin160::validChecksum(results->state)) return false; + } + + // Success + results->decode_type = decode_type_t::DAIKIN160; + 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_DAIKIN160 + +#if SEND_DAIKIN176 +/// Send a Daikin176 (176-bit) A/C formatted message. +/// Status: STABLE / Working on a real device. +/// @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::sendDaikin176(const unsigned char data[], const uint16_t nbytes, + const uint16_t repeat) { + if (nbytes < kDaikin176Section1Length) + return; // Not enough bytes to send a partial message. + + for (uint16_t r = 0; r <= repeat; r++) { + // Section #1 + sendGeneric(kDaikin176HdrMark, kDaikin176HdrSpace, kDaikin176BitMark, + kDaikin176OneSpace, kDaikin176BitMark, kDaikin176ZeroSpace, + kDaikin176BitMark, kDaikin176Gap, data, + kDaikin176Section1Length, + kDaikin176Freq, false, 0, kDutyDefault); + // Section #2 + sendGeneric(kDaikin176HdrMark, kDaikin176HdrSpace, kDaikin176BitMark, + kDaikin176OneSpace, kDaikin176BitMark, kDaikin176ZeroSpace, + kDaikin176BitMark, kDaikin176Gap, + data + kDaikin176Section1Length, + nbytes - kDaikin176Section1Length, + kDaikin176Freq, false, 0, kDutyDefault); + } +} +#endif // SEND_DAIKIN176 + +/// 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? +IRDaikin176::IRDaikin176(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 IRDaikin176::begin(void) { _irsend.begin(); } + +/// 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 IRDaikin176::validChecksum(uint8_t state[], const uint16_t length) { + // Validate the checksum of section #1. + if (length <= kDaikin176Section1Length - 1 || + state[kDaikin176Section1Length - 1] != sumBytes( + state, kDaikin176Section1Length - 1)) + return false; + // Validate the checksum of section #2 (a.k.a. the rest) + if (length <= kDaikin176Section1Length + 1 || + state[length - 1] != sumBytes(state + kDaikin176Section1Length, + length - kDaikin176Section1Length - 1)) + return false; + return true; +} + +/// Calculate and set the checksum values for the internal state. +void IRDaikin176::checksum(void) { + _.Sum1 = sumBytes(_.raw, kDaikin176Section1Length - 1); + _.Sum2 = sumBytes(_.raw + kDaikin176Section1Length, + kDaikin176Section2Length - 1); +} + +/// Reset the internal state to a fixed known good state. +void IRDaikin176::stateReset(void) { + for (uint8_t i = 0; i < kDaikin176StateLength; i++) _.raw[i] = 0x00; + _.raw[0] = 0x11; + _.raw[1] = 0xDA; + _.raw[2] = 0x17; + _.raw[3] = 0x18; + _.raw[4] = 0x04; + // _.raw[6] is a checksum byte, it will be set by checksum(). + _.raw[7] = 0x11; + _.raw[8] = 0xDA; + _.raw[9] = 0x17; + _.raw[10] = 0x18; + _.raw[12] = 0x73; + _.raw[14] = 0x20; + _.raw[18] = 0x16; // Fan speed and swing + _.raw[20] = 0x20; + // _.raw[21] is a checksum byte, it will be set by checksum(). + _saved_temp = getTemp(); +} + +/// 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 *IRDaikin176::getRaw(void) { + checksum(); // Ensure correct settings before sending. + return _.raw; +} + +/// Set the internal state from a valid code for this protocol. +/// @param[in] new_code A valid code for this protocol. +void IRDaikin176::setRaw(const uint8_t new_code[]) { + std::memcpy(_.raw, new_code, kDaikin176StateLength); + _saved_temp = getTemp(); +} + +#if SEND_DAIKIN176 +/// Send the current internal state as an IR message. +/// @param[in] repeat Nr. of times the message will be repeated. +void IRDaikin176::send(const uint16_t repeat) { + _irsend.sendDaikin176(getRaw(), kDaikin176StateLength, repeat); +} +#endif // SEND_DAIKIN176 + +/// Change the power setting to On. +void IRDaikin176::on(void) { setPower(true); } + +/// Change the power setting to Off.. +void IRDaikin176::off(void) { setPower(false); } + +/// Change the power setting. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRDaikin176::setPower(const bool on) { + _.ModeButton = 0; + _.Power = on; +} + +/// Get the value of the current power setting. +/// @return true, the setting is on. false, the setting is off. +bool IRDaikin176::getPower(void) const { return _.Power; } + +/// Get the operating mode setting of the A/C. +/// @return The current operating mode setting. +uint8_t IRDaikin176::getMode(void) const { return _.Mode; } + +/// Set the operating mode of the A/C. +/// @param[in] mode The desired operating mode. +void IRDaikin176::setMode(const uint8_t mode) { + uint8_t altmode = 0; + // Set the mode bits. + _.Mode = mode; + // Daikin172 has some alternate/additional mode bits that need to be changed + // in line with the operating mode. The following few lines match up these + // bits with the corresponding operating bits. + switch (mode) { + case kDaikin176Dry: altmode = 2; break; + case kDaikin176Fan: altmode = 6; break; + case kDaikin176Auto: + case kDaikin176Cool: + case kDaikin176Heat: altmode = 7; break; + default: _.Mode = kDaikin176Cool; altmode = 7; break; + } + // Set the additional mode bits. + _.AltMode = altmode; + setTemp(_saved_temp); + // Needs to happen after setTemp() as it will clear it. + _.ModeButton = kDaikin176ModeButton; +} + +/// 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 IRDaikin176::convertMode(const stdAc::opmode_t mode) { + switch (mode) { + case stdAc::opmode_t::kDry: return kDaikin176Dry; + case stdAc::opmode_t::kHeat: return kDaikin176Heat; + case stdAc::opmode_t::kFan: return kDaikin176Fan; + case stdAc::opmode_t::kAuto: return kDaikin176Auto; + default: return kDaikin176Cool; + } +} + +/// 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 IRDaikin176::toCommonMode(const uint8_t mode) { + switch (mode) { + case kDaikin176Dry: return stdAc::opmode_t::kDry; + case kDaikin176Heat: return stdAc::opmode_t::kHeat; + case kDaikin176Fan: return stdAc::opmode_t::kFan; + case kDaikin176Auto: return stdAc::opmode_t::kAuto; + default: return stdAc::opmode_t::kCool; + } +} + +/// Set the temperature. +/// @param[in] temp The temperature in degrees celsius. +void IRDaikin176::setTemp(const uint8_t temp) { + uint8_t degrees = std::min(kDaikinMaxTemp, std::max(temp, kDaikinMinTemp)); + _saved_temp = degrees; + switch (_.Mode) { + case kDaikin176Dry: + case kDaikin176Fan: + degrees = kDaikin176DryFanTemp; break; + } + _.Temp = degrees - 9; + _.ModeButton = 0; +} + +/// Get the current temperature setting. +/// @return The current setting for temp. in degrees celsius. +uint8_t IRDaikin176::getTemp(void) const { return _.Temp + 9; } + +/// Set the speed of the fan. +/// @param[in] fan The desired setting. +/// @note 1 for Min or 3 for Max +void IRDaikin176::setFan(const uint8_t fan) { + switch (fan) { + case kDaikinFanMin: + case kDaikin176FanMax: + _.Fan = fan; + break; + default: + _.Fan = kDaikin176FanMax; + break; + } + _.ModeButton = 0; +} + +/// Get the current fan speed setting. +/// @return The current fan speed. +uint8_t IRDaikin176::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 IRDaikin176::convertFan(const stdAc::fanspeed_t speed) { + switch (speed) { + case stdAc::fanspeed_t::kMin: + case stdAc::fanspeed_t::kLow: return kDaikinFanMin; + default: return kDaikin176FanMax; + } +} + +/// Set the Horizontal Swing mode of the A/C. +/// @param[in] position The position/mode to set the swing to. +void IRDaikin176::setSwingHorizontal(const uint8_t position) { + switch (position) { + case kDaikin176SwingHOff: + case kDaikin176SwingHAuto: + _.SwingH = position; + break; + default: _.SwingH = kDaikin176SwingHAuto; + } +} + +/// Get the Horizontal Swing mode of the A/C. +/// @return The native position/mode setting. +uint8_t IRDaikin176::getSwingHorizontal(void) const { return _.SwingH; } + +/// Get the Unit Id of the A/C. +/// @return The Unit Id the A/C is to use. +uint8_t IRDaikin176::getId(void) const { return _.Id1; } + +/// Set the Unit Id of the A/C. +/// @param[in] num The Unit Id the A/C is to use. +/// @note 0 for Unit A; 1 for Unit B +void IRDaikin176::setId(const uint8_t num) { _.Id1 = _.Id2 = num; } + +/// Convert a stdAc::swingh_t enum into it's native setting. +/// @param[in] position The enum to be converted. +/// @return The native equivalent of the enum. +uint8_t IRDaikin176::convertSwingH(const stdAc::swingh_t position) { + switch (position) { + case stdAc::swingh_t::kOff: return kDaikin176SwingHOff; + case stdAc::swingh_t::kAuto: return kDaikin176SwingHAuto; + default: return kDaikin176SwingHAuto; + } +} + +/// Convert a native horizontal swing postion to it's common equivalent. +/// @param[in] setting A native position to convert. +/// @return The common horizontal swing position. +stdAc::swingh_t IRDaikin176::toCommonSwingH(const uint8_t setting) { + switch (setting) { + case kDaikin176SwingHOff: return stdAc::swingh_t::kOff; + case kDaikin176SwingHAuto: return stdAc::swingh_t::kAuto; + default: + return stdAc::swingh_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 IRDaikin176::toCommonFanSpeed(const uint8_t speed) { + return (speed == kDaikinFanMin) ? stdAc::fanspeed_t::kMin + : stdAc::fanspeed_t::kMax; +} + +/// Convert the current internal state into its stdAc::state_t equivalent. +/// @return The stdAc equivalent of the native settings. +stdAc::state_t IRDaikin176::toCommon(void) const { + stdAc::state_t result; + result.protocol = decode_type_t::DAIKIN176; + result.model = -1; // No models used. + result.power = _.Power; + result.mode = IRDaikin176::toCommonMode(_.Mode); + result.celsius = true; + result.degrees = getTemp(); + result.fanspeed = toCommonFanSpeed(_.Fan); + result.swingh = toCommonSwingH(_.SwingH); + + // Not supported. + result.swingv = stdAc::swingv_t::kOff; + result.quiet = false; + result.turbo = false; + result.light = false; + result.clean = 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 IRDaikin176::toString(void) const { + String result = ""; + result.reserve(90); // Reserve some heap for the string to reduce fragging. + result += addBoolToString(_.Power, kPowerStr, false); + result += addModeToString(_.Mode, kDaikin176Auto, kDaikin176Cool, + kDaikin176Heat, kDaikin176Dry, kDaikin176Fan); + result += addTempToString(getTemp()); + result += addFanToString(_.Fan, kDaikin176FanMax, kDaikinFanMin, + kDaikinFanMin, kDaikinFanMin, kDaikinFanMin); + result += addSwingHToString(_.SwingH, kDaikin176SwingHAuto, + kDaikin176SwingHAuto, // maxleft Unused + kDaikin176SwingHAuto, // left Unused + kDaikin176SwingHAuto, // middle Unused + kDaikin176SwingHAuto, // right Unused + kDaikin176SwingHAuto, // maxright Unused + kDaikin176SwingHOff, + // Below are unused. + kDaikin176SwingHAuto, + kDaikin176SwingHAuto, + kDaikin176SwingHAuto, + kDaikin176SwingHAuto); + result += addIntToString(_.Id1, kIdStr); + return result; +} + +#if DECODE_DAIKIN176 +/// Decode the supplied Daikin 176-bit message. (DAIKIN176) +/// 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::decodeDaikin176(decode_results *results, uint16_t offset, + const uint16_t nbits, + const bool strict) { + if (results->rawlen < 2 * (nbits + kHeader + kFooter) - 1 + offset) + return false; + + // Compliance + if (strict && nbits != kDaikin176Bits) return false; + + const uint8_t ksectionSize[kDaikin176Sections] = {kDaikin176Section1Length, + kDaikin176Section2Length}; + + // Sections + uint16_t pos = 0; + for (uint8_t section = 0; section < kDaikin176Sections; section++) { + uint16_t used; + // Section Header + Section Data (7 bytes) + Section Footer + used = matchGeneric(results->rawbuf + offset, results->state + pos, + results->rawlen - offset, ksectionSize[section] * 8, + kDaikin176HdrMark, kDaikin176HdrSpace, + kDaikin176BitMark, kDaikin176OneSpace, + kDaikin176BitMark, kDaikin176ZeroSpace, + kDaikin176BitMark, kDaikin176Gap, + section >= kDaikin176Sections - 1, + kDaikinTolerance, kDaikinMarkExcess, false); + if (used == 0) return false; + offset += used; + pos += ksectionSize[section]; + } + // Compliance + if (strict) { + // Validate the checksum. + if (!IRDaikin176::validChecksum(results->state)) return false; + } + + // Success + results->decode_type = decode_type_t::DAIKIN176; + 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_DAIKIN176 + +#if SEND_DAIKIN128 +/// Send a Daikin128 (128-bit) A/C formatted 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. +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/827 +void IRsend::sendDaikin128(const unsigned char data[], const uint16_t nbytes, + const uint16_t repeat) { + if (nbytes < kDaikin128SectionLength) + return; // Not enough bytes to send a partial message. + + for (uint16_t r = 0; r <= repeat; r++) { + enableIROut(kDaikin128Freq); + // Leader + for (uint8_t i = 0; i < 2; i++) { + mark(kDaikin128LeaderMark); + space(kDaikin128LeaderSpace); + } + // Section #1 (Header + Data) + sendGeneric(kDaikin128HdrMark, kDaikin128HdrSpace, kDaikin128BitMark, + kDaikin128OneSpace, kDaikin128BitMark, kDaikin128ZeroSpace, + kDaikin128BitMark, kDaikin128Gap, data, + kDaikin128SectionLength, + kDaikin128Freq, false, 0, kDutyDefault); + // Section #2 (Data + Footer) + sendGeneric(0, 0, kDaikin128BitMark, + kDaikin128OneSpace, kDaikin128BitMark, kDaikin128ZeroSpace, + kDaikin128FooterMark, kDaikin128Gap, + data + kDaikin128SectionLength, + nbytes - kDaikin128SectionLength, + kDaikin128Freq, false, 0, kDutyDefault); + } +} +#endif // SEND_DAIKIN128 + +/// 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? +IRDaikin128::IRDaikin128(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 IRDaikin128::begin(void) { _irsend.begin(); } + +uint8_t IRDaikin128::calcFirstChecksum(const uint8_t state[]) { + return sumNibbles(state, kDaikin128SectionLength - 1, + state[kDaikin128SectionLength - 1] & 0x0F) & 0x0F; +} + +uint8_t IRDaikin128::calcSecondChecksum(const uint8_t state[]) { + return sumNibbles(state + kDaikin128SectionLength, + kDaikin128SectionLength - 1); +} + +/// 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 IRDaikin128::validChecksum(uint8_t state[]) { + // Validate the checksum of section #1. + if (state[kDaikin128SectionLength - 1] >> 4 != calcFirstChecksum(state)) + return false; + // Validate the checksum of section #2 + if (state[kDaikin128StateLength - 1] != calcSecondChecksum(state)) + return false; + return true; +} + +/// Calculate and set the checksum values for the internal state. +void IRDaikin128::checksum(void) { + _.Sum1 = calcFirstChecksum(_.raw); + _.Sum2 = calcSecondChecksum(_.raw); +} + +/// Reset the internal state to a fixed known good state. +void IRDaikin128::stateReset(void) { + for (uint8_t i = 0; i < kDaikin128StateLength; i++) _.raw[i] = 0x00; + _.raw[0] = 0x16; + _.raw[7] = 0x04; // Most significant nibble is a checksum. + _.raw[8] = 0xA1; + // _.raw[15] is a checksum byte, it will be set by checksum(). +} + +/// 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 *IRDaikin128::getRaw(void) { + checksum(); // Ensure correct settings before sending. + return _.raw; +} + +/// Set the internal state from a valid code for this protocol. +/// @param[in] new_code A valid code for this protocol. +void IRDaikin128::setRaw(const uint8_t new_code[]) { + std::memcpy(_.raw, new_code, kDaikin128StateLength); +} + +#if SEND_DAIKIN128 +/// Send the current internal state as an IR message. +/// @param[in] repeat Nr. of times the message will be repeated. +void IRDaikin128::send(const uint16_t repeat) { + _irsend.sendDaikin128(getRaw(), kDaikin128StateLength, repeat); +} +#endif // SEND_DAIKIN128 + +/// Set the Power toggle setting of the A/C. +/// @param[in] toggle true, the setting is on. false, the setting is off. +void IRDaikin128::setPowerToggle(const bool toggle) { _.Power = toggle; } + +/// Get the Power toggle setting of the A/C. +/// @return The current operating mode setting. +bool IRDaikin128::getPowerToggle(void) const { return _.Power; } + +/// Get the operating mode setting of the A/C. +/// @return The current operating mode setting. +uint8_t IRDaikin128::getMode(void) const { return _.Mode; } + +/// Set the operating mode of the A/C. +/// @param[in] mode The desired operating mode. +void IRDaikin128::setMode(const uint8_t mode) { + switch (mode) { + case kDaikin128Auto: + case kDaikin128Cool: + case kDaikin128Heat: + case kDaikin128Fan: + case kDaikin128Dry: + _.Mode = mode; + break; + default: + _.Mode = kDaikin128Auto; + break; + } + // Force a reset of mode dependant things. + setFan(getFan()); // Covers Quiet & Powerful too. + 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 IRDaikin128::convertMode(const stdAc::opmode_t mode) { + switch (mode) { + case stdAc::opmode_t::kCool: return kDaikin128Cool; + case stdAc::opmode_t::kHeat: return kDaikin128Heat; + case stdAc::opmode_t::kDry: return kDaikinDry; + case stdAc::opmode_t::kFan: return kDaikin128Fan; + default: return kDaikin128Auto; + } +} + +/// 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 IRDaikin128::toCommonMode(const uint8_t mode) { + switch (mode) { + case kDaikin128Cool: return stdAc::opmode_t::kCool; + case kDaikin128Heat: return stdAc::opmode_t::kHeat; + case kDaikin128Dry: return stdAc::opmode_t::kDry; + case kDaikin128Fan: return stdAc::opmode_t::kFan; + default: return stdAc::opmode_t::kAuto; + } +} + +/// Set the temperature. +/// @param[in] temp The temperature in degrees celsius. +void IRDaikin128::setTemp(const uint8_t temp) { + _.Temp = uint8ToBcd(std::min(kDaikin128MaxTemp, + std::max(temp, kDaikin128MinTemp))); +} + +/// Get the current temperature setting. +/// @return The current setting for temp. in degrees celsius. +uint8_t IRDaikin128::getTemp(void) const { return bcdToUint8(_.Temp); } + +/// Get the current fan speed setting. +/// @return The current fan speed. +uint8_t IRDaikin128::getFan(void) const { return _.Fan; } + +/// Set the speed of the fan. +/// @param[in] speed The desired setting. +void IRDaikin128::setFan(const uint8_t speed) { + uint8_t new_speed = speed; + uint8_t mode = _.Mode; + switch (speed) { + case kDaikin128FanQuiet: + case kDaikin128FanPowerful: + if (mode == kDaikin128Auto) new_speed = kDaikin128FanAuto; + // FALL-THRU + case kDaikin128FanAuto: + case kDaikin128FanHigh: + case kDaikin128FanMed: + case kDaikin128FanLow: + _.Fan = new_speed; + break; + default: + _.Fan = kDaikin128FanAuto; + return; + } +} + +/// 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 IRDaikin128::convertFan(const stdAc::fanspeed_t speed) { + switch (speed) { + case stdAc::fanspeed_t::kMin: return kDaikinFanQuiet; + case stdAc::fanspeed_t::kLow: return kDaikin128FanLow; + case stdAc::fanspeed_t::kMedium: return kDaikin128FanMed; + case stdAc::fanspeed_t::kHigh: return kDaikin128FanHigh; + case stdAc::fanspeed_t::kMax: return kDaikin128FanPowerful; + default: return kDaikin128FanAuto; + } +} + +/// 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 IRDaikin128::toCommonFanSpeed(const uint8_t speed) { + switch (speed) { + case kDaikin128FanPowerful: return stdAc::fanspeed_t::kMax; + case kDaikin128FanHigh: return stdAc::fanspeed_t::kHigh; + case kDaikin128FanMed: return stdAc::fanspeed_t::kMedium; + case kDaikin128FanLow: return stdAc::fanspeed_t::kLow; + case kDaikinFanQuiet: 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 IRDaikin128::setSwingVertical(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 IRDaikin128::getSwingVertical(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 IRDaikin128::setSleep(const bool on) { _.Sleep = on; } + +/// Get the Sleep mode of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRDaikin128::getSleep(void) const { return _.Sleep; } + +/// Set the Economy mode of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRDaikin128::setEcono(const bool on) { + uint8_t mode = _.Mode; + _.Econo = (on && (mode == kDaikin128Cool || mode == kDaikin128Heat)); +} + +/// Get the Economical mode of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRDaikin128::getEcono(void) const { return _.Econo; } + +/// Set the Quiet mode of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRDaikin128::setQuiet(const bool on) { + uint8_t mode = _.Mode; + if (on && (mode == kDaikin128Cool || mode == kDaikin128Heat)) + setFan(kDaikin128FanQuiet); + else if (_.Fan == kDaikin128FanQuiet) + setFan(kDaikin128FanAuto); +} + +/// Get the Quiet mode status of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRDaikin128::getQuiet(void) const { return _.Fan == kDaikin128FanQuiet; } + +/// Set the Powerful (Turbo) mode of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRDaikin128::setPowerful(const bool on) { + uint8_t mode = _.Mode; + if (on && (mode == kDaikin128Cool || mode == kDaikin128Heat)) + setFan(kDaikin128FanPowerful); + else if (_.Fan == kDaikin128FanPowerful) + setFan(kDaikin128FanAuto); +} + +/// Get the Powerful (Turbo) mode of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRDaikin128::getPowerful(void) const { + return _.Fan == kDaikin128FanPowerful; +} + +/// Set the clock on the A/C unit. +/// @param[in] mins_since_midnight Nr. of minutes past midnight. +void IRDaikin128::setClock(const uint16_t mins_since_midnight) { + uint16_t mins = mins_since_midnight; + if (mins_since_midnight >= 24 * 60) mins = 0; // Bounds check. + // Hours. + _.ClockHours = uint8ToBcd(mins / 60); + // Minutes. + _.ClockMins = uint8ToBcd(mins % 60); +} + +/// Get the clock time to be sent to the A/C unit. +/// @return The number of minutes past midnight. +uint16_t IRDaikin128::getClock(void) const { + return bcdToUint8(_.ClockHours) * 60 + bcdToUint8(_.ClockMins); +} + +/// Set the enable status of the On Timer. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRDaikin128::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 IRDaikin128::getOnTimerEnabled(void) const { return _.OnTimer; } + +#define SETTIME(x, n) do { \ + uint16_t mins = n;\ + if (n >= 24 * 60) mins = 0;\ + _.x##HalfHour = (mins % 60) >= 30;\ + _.x##Hours = uint8ToBcd(mins / 60);\ +} while (0) + +#define GETTIME(x) bcdToUint8(_.x##Hours) * 60 + (_.x##HalfHour ? 30 : 0) + +/// Set the On Timer time for the A/C unit. +/// @param[in] mins_since_midnight Nr. of minutes past midnight. +void IRDaikin128::setOnTimer(const uint16_t mins_since_midnight) { + SETTIME(On, mins_since_midnight); +} + +/// Get the On Timer time to be sent to the A/C unit. +/// @return The number of minutes past midnight. +uint16_t IRDaikin128::getOnTimer(void) const { return GETTIME(On); } + +/// Set the enable status of the Off Timer. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRDaikin128::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 IRDaikin128::getOffTimerEnabled(void) const { return _.OffTimer; } + +/// Set the Off Timer time for the A/C unit. +/// @param[in] mins_since_midnight Nr. of minutes past midnight. +void IRDaikin128::setOffTimer(const uint16_t mins_since_midnight) { + SETTIME(Off, mins_since_midnight); +} + +/// Get the Off Timer time to be sent to the A/C unit. +/// @return The number of minutes past midnight. +uint16_t IRDaikin128::getOffTimer(void) const { return GETTIME(Off); } + +/// Set the Light toggle setting of the A/C. +/// @param[in] unit Device to show the LED (Light) Display info about. +/// @note 0 is off. +void IRDaikin128::setLightToggle(const uint8_t unit) { + _.Ceiling = 0; + _.Wall = 0; + switch (unit) { + case kDaikin128BitCeiling: + _.Ceiling = 1; + break; + case kDaikin128BitWall: + _.Wall = 1; + break; + } +} + +/// Get the Light toggle setting of the A/C. +/// @return The current operating mode setting. +uint8_t IRDaikin128::getLightToggle(void) const { + uint8_t code = 0; + if (_.Ceiling) { + code = kDaikin128BitCeiling; + } else if (_.Wall) { + code = kDaikin128BitWall; + } + + return code; +} + +/// Convert the current internal state into a human readable string. +/// @return A human readable string. +String IRDaikin128::toString(void) const { + String result = ""; + result.reserve(240); // Reserve some heap for the string to reduce fragging. + result += addBoolToString(_.Power, kPowerToggleStr, false); + result += addModeToString(_.Mode, kDaikin128Auto, kDaikin128Cool, + kDaikin128Heat, kDaikin128Dry, kDaikin128Fan); + result += addTempToString(getTemp()); + result += addFanToString(_.Fan, kDaikin128FanHigh, kDaikin128FanLow, + kDaikin128FanAuto, kDaikin128FanQuiet, + kDaikin128FanMed); + result += addBoolToString(getPowerful(), kPowerfulStr); + result += addBoolToString(getQuiet(), kQuietStr); + result += addBoolToString(_.SwingV, kSwingVStr); + result += addBoolToString(_.Sleep, kSleepStr); + result += addBoolToString(_.Econo, kEconoStr); + result += addLabeledString(minsToString(getClock()), kClockStr); + result += addBoolToString(_.OnTimer, kOnTimerStr); + result += addLabeledString(minsToString(getOnTimer()), kOnTimerStr); + result += addBoolToString(_.OffTimer, kOffTimerStr); + result += addLabeledString(minsToString(getOffTimer()), kOffTimerStr); + result += addIntToString(getLightToggle(), kLightToggleStr); + result += kSpaceLBraceStr; + switch (getLightToggle()) { + case kDaikin128BitCeiling: result += kCeilingStr; break; + case kDaikin128BitWall: result += kWallStr; break; + case 0: result += kOffStr; break; + default: result += kUnknownStr; + } + result += ')'; + return result; +} + +/// Convert the current internal state into its stdAc::state_t equivalent. +/// @param[in] prev Ptr to a previous state. +/// @return The stdAc equivalent of the native settings. +stdAc::state_t IRDaikin128::toCommon(const stdAc::state_t *prev) const { + stdAc::state_t result; + if (prev != NULL) result = *prev; + result.protocol = decode_type_t::DAIKIN128; + 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.quiet = getQuiet(); + result.turbo = getPowerful(); + result.econo = _.Econo; + result.light ^= (getLightToggle() != 0); + result.sleep = _.Sleep ? 0 : -1; + result.clock = getClock(); + // Not supported. + result.swingh = stdAc::swingh_t::kOff; + result.clean = false; + result.filter = false; + result.beep = false; + return result; +} + +#if DECODE_DAIKIN128 +/// Decode the supplied Daikin 128-bit message. (DAIKIN128) +/// 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. +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/827 +bool IRrecv::decodeDaikin128(decode_results *results, uint16_t offset, + const uint16_t nbits, const bool strict) { + if (results->rawlen < 2 * (nbits + kHeader) + kFooter - 1 + offset) + return false; + if (nbits / 8 <= kDaikin128SectionLength) return false; + + // Compliance + if (strict && nbits != kDaikin128Bits) return false; + + // Leader + for (uint8_t i = 0; i < 2; i++) { + if (!matchMark(results->rawbuf[offset++], kDaikin128LeaderMark, + kDaikinTolerance, kDaikinMarkExcess)) return false; + if (!matchSpace(results->rawbuf[offset++], kDaikin128LeaderSpace, + kDaikinTolerance, kDaikinMarkExcess)) return false; + } + const uint16_t ksectionSize[kDaikin128Sections] = { + kDaikin128SectionLength, (uint16_t)(nbits / 8 - kDaikin128SectionLength)}; + // Data Sections + uint16_t pos = 0; + for (uint8_t section = 0; section < kDaikin128Sections; section++) { + uint16_t used; + // Section Header (first section only) + Section Data (8 bytes) + + // Section Footer (Not for first section) + used = matchGeneric(results->rawbuf + offset, results->state + pos, + results->rawlen - offset, ksectionSize[section] * 8, + section == 0 ? kDaikin128HdrMark : 0, + section == 0 ? kDaikin128HdrSpace : 0, + kDaikin128BitMark, kDaikin128OneSpace, + kDaikin128BitMark, kDaikin128ZeroSpace, + section > 0 ? kDaikin128FooterMark : kDaikin128BitMark, + kDaikin128Gap, + section > 0, + kDaikinTolerance, kDaikinMarkExcess, false); + if (used == 0) return false; + offset += used; + pos += ksectionSize[section]; + } + // Compliance + if (strict) { + if (!IRDaikin128::validChecksum(results->state)) return false; + } + + // Success + results->decode_type = decode_type_t::DAIKIN128; + 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_DAIKIN128 + +#if SEND_DAIKIN152 +/// Send a Daikin152 (152-bit) A/C formatted 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. +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/873 +void IRsend::sendDaikin152(const unsigned char data[], const uint16_t nbytes, + const uint16_t repeat) { + for (uint16_t r = 0; r <= repeat; r++) { + // Leader + sendGeneric(0, 0, kDaikin152BitMark, kDaikin152OneSpace, + kDaikin152BitMark, kDaikin152ZeroSpace, + kDaikin152BitMark, kDaikin152Gap, + (uint64_t)0, kDaikin152LeaderBits, + kDaikin152Freq, false, 0, kDutyDefault); + // Header + Data + Footer + sendGeneric(kDaikin152HdrMark, kDaikin152HdrSpace, kDaikin152BitMark, + kDaikin152OneSpace, kDaikin152BitMark, kDaikin152ZeroSpace, + kDaikin152BitMark, kDaikin152Gap, data, + nbytes, kDaikin152Freq, false, 0, kDutyDefault); + } +} +#endif // SEND_DAIKIN152 + +#if DECODE_DAIKIN152 +/// Decode the supplied Daikin 152-bit message. (DAIKIN152) +/// 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. +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/873 +bool IRrecv::decodeDaikin152(decode_results *results, uint16_t offset, + const uint16_t nbits, const bool strict) { + if (results->rawlen < 2 * (5 + nbits + kFooter) + kHeader - 1 + offset) + return false; + if (nbits / 8 < kDaikin152StateLength) return false; + + // Compliance + if (strict && nbits != kDaikin152Bits) return false; + + uint16_t used; + + // Leader + uint64_t leader = 0; + used = matchGeneric(results->rawbuf + offset, &leader, + results->rawlen - offset, kDaikin152LeaderBits, + 0, 0, // No Header + kDaikin152BitMark, kDaikin152OneSpace, + kDaikin152BitMark, kDaikin152ZeroSpace, + kDaikin152BitMark, kDaikin152Gap, // Footer gap + false, _tolerance, kMarkExcess, false); + if (used == 0 || leader != 0) return false; + offset += used; + + // Header + Data + Footer + used = matchGeneric(results->rawbuf + offset, results->state, + results->rawlen - offset, nbits, + kDaikin152HdrMark, kDaikin152HdrSpace, + kDaikin152BitMark, kDaikin152OneSpace, + kDaikin152BitMark, kDaikin152ZeroSpace, + kDaikin152BitMark, kDaikin152Gap, + true, _tolerance, kMarkExcess, false); + if (used == 0) return false; + + // Compliance + if (strict) { + if (!IRDaikin152::validChecksum(results->state)) return false; + } + + // Success + results->decode_type = decode_type_t::DAIKIN152; + 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_DAIKIN152 + +/// 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? +IRDaikin152::IRDaikin152(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 IRDaikin152::begin(void) { _irsend.begin(); } + +#if SEND_DAIKIN152 +/// Send the current internal state as an IR message. +/// @param[in] repeat Nr. of times the message will be repeated. +void IRDaikin152::send(const uint16_t repeat) { + _irsend.sendDaikin152(getRaw(), kDaikin152StateLength, repeat); +} +#endif // SEND_DAIKIN152 + +/// 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 IRDaikin152::validChecksum(uint8_t state[], const uint16_t length) { + // Validate the checksum of the given state. + if (length <= 1 || state[length - 1] != sumBytes(state, length - 1)) + return false; + else + return true; +} + +/// Calculate and set the checksum values for the internal state. +void IRDaikin152::checksum(void) { + _.Sum = sumBytes(_.raw, kDaikin152StateLength - 1); +} + +/// Reset the internal state to a fixed known good state. +void IRDaikin152::stateReset(void) { + for (uint8_t i = 3; i < kDaikin152StateLength; i++) _.raw[i] = 0x00; + _.raw[0] = 0x11; + _.raw[1] = 0xDA; + _.raw[2] = 0x27; + _.raw[15] = 0xC5; + // _.raw[19] is a checksum byte, it will be set by checksum(). +} + +/// 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 *IRDaikin152::getRaw(void) { + checksum(); // Ensure correct settings before sending. + return _.raw; +} + +/// Set the internal state from a valid code for this protocol. +/// @param[in] new_code A valid code for this protocol. +void IRDaikin152::setRaw(const uint8_t new_code[]) { + std::memcpy(_.raw, new_code, kDaikin152StateLength); +} + +/// Change the power setting to On. +void IRDaikin152::on(void) { setPower(true); } + +/// Change the power setting to Off. +void IRDaikin152::off(void) { setPower(false); } + +/// Change the power setting. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRDaikin152::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 IRDaikin152::getPower(void) const { return _.Power; } + +/// Get the operating mode setting of the A/C. +/// @return The current operating mode setting. +uint8_t IRDaikin152::getMode(void) const { return _.Mode; } + +/// Set the operating mode of the A/C. +/// @param[in] mode The desired operating mode. +void IRDaikin152::setMode(const uint8_t mode) { + switch (mode) { + case kDaikinFan: + setTemp(kDaikin152FanTemp); // Handle special temp for fan mode. + break; + case kDaikinDry: + setTemp(kDaikin152DryTemp); // Handle special temp for dry mode. + break; + case kDaikinAuto: + case kDaikinCool: + case kDaikinHeat: + break; + default: + _.Mode = kDaikinAuto; + return; + } + _.Mode = 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 IRDaikin152::convertMode(const stdAc::opmode_t mode) { + return IRDaikinESP::convertMode(mode); +} + +/// Set the temperature. +/// @param[in] temp The temperature in degrees celsius. +void IRDaikin152::setTemp(const uint8_t temp) { + uint8_t degrees = std::max( + temp, (_.Mode == kDaikinHeat) ? kDaikinMinTemp : kDaikin2MinCoolTemp); + degrees = std::min(degrees, kDaikinMaxTemp); + if (temp == kDaikin152FanTemp) degrees = temp; // Handle fan only temp. + _.Temp = degrees; +} + +/// Get the current temperature setting. +/// @return The current setting for temp. in degrees celsius. +uint8_t IRDaikin152::getTemp(void) const { return _.Temp; } + +/// Set the speed of the fan. +/// @param[in] fan The desired setting. +/// @note 1-5 or kDaikinFanAuto or kDaikinFanQuiet +void IRDaikin152::setFan(const uint8_t fan) { + // Set the fan speed bits, leave low 4 bits alone + uint8_t fanset; + if (fan == kDaikinFanQuiet || fan == kDaikinFanAuto) + fanset = fan; + else if (fan < kDaikinFanMin || fan > kDaikinFanMax) + fanset = kDaikinFanAuto; + else + fanset = 2 + fan; + _.Fan = fanset; +} + +/// Get the current fan speed setting. +/// @return The current fan speed. +uint8_t IRDaikin152::getFan(void) const { + const uint8_t fan = _.Fan; + switch (fan) { + case kDaikinFanAuto: + case kDaikinFanQuiet: return fan; + default: return fan - 2; + } +} + +/// 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 IRDaikin152::convertFan(const stdAc::fanspeed_t speed) { + return IRDaikinESP::convertFan(speed); +} + +/// Set the Vertical Swing mode of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRDaikin152::setSwingV(const bool on) { + _.SwingV = (on ? kDaikinSwingOn : kDaikinSwingOff); +} + +/// Get the Vertical Swing mode of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRDaikin152::getSwingV(void) const { return _.SwingV; } + +/// Set the Quiet mode of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRDaikin152::setQuiet(const bool on) { + _.Quiet = on; + // Powerful & Quiet mode being on are mutually exclusive. + if (on) setPowerful(false); +} + +/// Get the Quiet mode status of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRDaikin152::getQuiet(void) const { return _.Quiet; } + +/// Set the Powerful (Turbo) mode of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRDaikin152::setPowerful(const bool on) { + _.Powerful = on; + if (on) { + // Powerful, Quiet, Comfort & Econo mode being on are mutually exclusive. + setQuiet(false); + setComfort(false); + setEcono(false); + } +} + +/// Get the Powerful (Turbo) mode of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRDaikin152::getPowerful(void) const { return _.Powerful; } + +/// Set the Economy mode of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRDaikin152::setEcono(const bool on) { + _.Econo = on; + // Powerful & Econo mode being on are mutually exclusive. + if (on) setPowerful(false); +} + +/// Get the Economical mode of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRDaikin152::getEcono(void) const { return _.Econo; } + +/// Set the Sensor mode of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRDaikin152::setSensor(const bool on) { _.Sensor = on; } + +/// Get the Sensor mode of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRDaikin152::getSensor(void) const { return _.Sensor; } + +/// Set the Comfort mode of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRDaikin152::setComfort(const bool on) { + _.Comfort = on; + if (on) { + // Comfort mode is incompatible with Powerful mode. + setPowerful(false); + // It also sets the fan to auto and turns off swingv. + setFan(kDaikinFanAuto); + setSwingV(false); + } +} + +/// Get the Comfort mode of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRDaikin152::getComfort(void) const { return _.Comfort; } + +/// Convert the current internal state into its stdAc::state_t equivalent. +/// @return The stdAc equivalent of the native settings. +stdAc::state_t IRDaikin152::toCommon(void) const { + stdAc::state_t result; + result.protocol = decode_type_t::DAIKIN152; + result.model = -1; // No models used. + result.power = _.Power; + result.mode = IRDaikinESP::toCommonMode(_.Mode); + result.celsius = true; + result.degrees = _.Temp; + result.fanspeed = IRDaikinESP::toCommonFanSpeed(getFan()); + result.swingv = _.SwingV ? stdAc::swingv_t::kAuto : stdAc::swingv_t::kOff; + result.quiet = _.Quiet; + result.turbo = _.Powerful; + result.econo = _.Econo; + // Not supported. + result.swingh = stdAc::swingh_t::kOff; + result.clean = false; + result.filter = false; + result.light = 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 IRDaikin152::toString(void) const { + String result = ""; + result.reserve(180); // Reserve some heap for the string to reduce fragging. + result += addBoolToString(_.Power, kPowerStr, false); + result += addModeToString(_.Mode, kDaikinAuto, kDaikinCool, kDaikinHeat, + kDaikinDry, kDaikinFan); + result += addTempToString(_.Temp); + result += addFanToString(getFan(), kDaikinFanMax, kDaikinFanMin, + kDaikinFanAuto, kDaikinFanQuiet, kDaikinFanMed); + result += addBoolToString(_.SwingV, kSwingVStr); + result += addBoolToString(_.Powerful, kPowerfulStr); + result += addBoolToString(_.Quiet, kQuietStr); + result += addBoolToString(_.Econo, kEconoStr); + result += addBoolToString(_.Sensor, kSensorStr); + result += addBoolToString(_.Comfort, kComfortStr); + return result; +} + +#if SEND_DAIKIN64 +/// Send a Daikin64 (64-bit) A/C 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. +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1064 +void IRsend::sendDaikin64(const uint64_t data, const uint16_t nbits, + const uint16_t repeat) { + enableIROut(kDaikin64Freq); + for (uint16_t r = 0; r <= repeat; r++) { + for (uint8_t i = 0; i < 2; i++) { + // Leader + mark(kDaikin64LdrMark); + space(kDaikin64LdrSpace); + } + // Header + Data + Footer #1 + sendGeneric(kDaikin64HdrMark, kDaikin64HdrSpace, + kDaikin64BitMark, kDaikin64OneSpace, + kDaikin64BitMark, kDaikin64ZeroSpace, + kDaikin64BitMark, kDaikin64Gap, + data, nbits, kDaikin64Freq, false, 0, 50); + // Footer #2 + mark(kDaikin64HdrMark); + space(kDefaultMessageGap); // A guess of the gap between messages. + } +} +#endif // SEND_DAIKIN64 + +#if DECODE_DAIKIN64 +/// Decode the supplied Daikin 64-bit message. (DAIKIN64) +/// 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/1064 +bool IRrecv::decodeDaikin64(decode_results *results, uint16_t offset, + const uint16_t nbits, const bool strict) { + if (results->rawlen < 2 * nbits + kDaikin64Overhead - offset) + return false; // Too short a message to match. + // Compliance + if (strict && nbits != kDaikin64Bits) + return false; + + // Leader + for (uint8_t i = 0; i < 2; i++) { + if (!matchMark(results->rawbuf[offset++], kDaikin64LdrMark)) + return false; + if (!matchSpace(results->rawbuf[offset++], kDaikin64LdrSpace)) + return false; + } + // Header + Data + Footer #1 + uint16_t used = matchGeneric(results->rawbuf + offset, &results->value, + results->rawlen - offset, nbits, + kDaikin64HdrMark, kDaikin64HdrSpace, + kDaikin64BitMark, kDaikin64OneSpace, + kDaikin64BitMark, kDaikin64ZeroSpace, + kDaikin64BitMark, kDaikin64Gap, + false, _tolerance + kDaikin64ToleranceDelta, + kMarkExcess, false); + if (used == 0) return false; + offset += used; + // Footer #2 + if (!matchMark(results->rawbuf[offset++], kDaikin64HdrMark)) + return false; + + // Compliance + if (strict && !IRDaikin64::validChecksum(results->value)) return false; + // Success + results->decode_type = decode_type_t::DAIKIN64; + results->bits = nbits; + results->command = 0; + results->address = 0; + return true; +} +#endif // DAIKIN64 + +/// 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? +IRDaikin64::IRDaikin64(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 IRDaikin64::begin(void) { _irsend.begin(); } + +#if SEND_DAIKIN64 +/// Send the current internal state as an IR message. +/// @param[in] repeat Nr. of times the message will be repeated. +void IRDaikin64::send(const uint16_t repeat) { + _irsend.sendDaikin64(getRaw(), kDaikin64Bits, repeat); +} +#endif // SEND_DAIKIN64 + +/// 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 IRDaikin64::calcChecksum(const uint64_t state) { + uint64_t data = GETBITS64(state, 0, kDaikin64ChecksumOffset); + 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 state to verify the checksum of. +/// @return true, if the state has a valid checksum. Otherwise, false. +bool IRDaikin64::validChecksum(const uint64_t state) { + // Validate the checksum of the given state. + return (GETBITS64(state, kDaikin64ChecksumOffset, + kDaikin64ChecksumSize) == calcChecksum(state)); +} + +/// Calculate and set the checksum values for the internal state. +void IRDaikin64::checksum(void) { _.Sum = calcChecksum(_.raw); } + +/// Reset the internal state to a fixed known good state. +void IRDaikin64::stateReset(void) { _.raw = kDaikin64KnownGoodState; } + +/// 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 IRDaikin64::getRaw(void) { + checksum(); // Ensure correct settings before sending. + return _.raw; +} + +/// Set the internal state from a valid code for this protocol. +/// @param[in] new_state A valid code for this protocol. +void IRDaikin64::setRaw(const uint64_t new_state) { _.raw = new_state; } + +/// Set the Power toggle setting of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRDaikin64::setPowerToggle(const bool on) { _.Power = on; } + +/// Get the Power toggle setting of the A/C. +/// @return The current operating mode setting. +bool IRDaikin64::getPowerToggle(void) const { return _.Power; } + +/// Set the temperature. +/// @param[in] temp The temperature in degrees celsius. +void IRDaikin64::setTemp(const uint8_t temp) { + uint8_t degrees = std::max(temp, kDaikin64MinTemp); + degrees = std::min(degrees, kDaikin64MaxTemp); + _.Temp = uint8ToBcd(degrees); +} + +/// Get the current temperature setting. +/// @return The current setting for temp. in degrees celsius. +uint8_t IRDaikin64::getTemp(void) const { return bcdToUint8(_.Temp); } + +/// Get the operating mode setting of the A/C. +/// @return The current operating mode setting. +uint8_t IRDaikin64::getMode(void) const { return _.Mode; } + +/// Set the operating mode of the A/C. +/// @param[in] mode The desired operating mode. +void IRDaikin64::setMode(const uint8_t mode) { + switch (mode) { + case kDaikin64Fan: + case kDaikin64Dry: + case kDaikin64Cool: + case kDaikin64Heat: + _.Mode = mode; + break; + default: + _.Mode = kDaikin64Cool; + } +} + +/// 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 IRDaikin64::convertMode(const stdAc::opmode_t mode) { + switch (mode) { + case stdAc::opmode_t::kDry: return kDaikin64Dry; + case stdAc::opmode_t::kFan: return kDaikin64Fan; + case stdAc::opmode_t::kHeat: return kDaikin64Heat; + default: return kDaikin64Cool; + } +} + +/// 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 IRDaikin64::toCommonMode(const uint8_t mode) { + switch (mode) { + case kDaikin64Cool: return stdAc::opmode_t::kCool; + case kDaikin64Heat: return stdAc::opmode_t::kHeat; + case kDaikin64Dry: return stdAc::opmode_t::kDry; + case kDaikin64Fan: return stdAc::opmode_t::kFan; + default: return stdAc::opmode_t::kAuto; + } +} + +/// Get the current fan speed setting. +/// @return The current fan speed. +uint8_t IRDaikin64::getFan(void) const { return _.Fan; } + +/// Set the speed of the fan. +/// @param[in] speed The desired setting. +void IRDaikin64::setFan(const uint8_t speed) { + switch (speed) { + case kDaikin64FanQuiet: + case kDaikin64FanTurbo: + case kDaikin64FanAuto: + case kDaikin64FanHigh: + case kDaikin64FanMed: + case kDaikin64FanLow: + _.Fan = speed; + break; + default: + _.Fan = kDaikin64FanAuto; + } +} + +/// 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 IRDaikin64::convertFan(const stdAc::fanspeed_t speed) { + switch (speed) { + case stdAc::fanspeed_t::kMin: return kDaikin64FanQuiet; + case stdAc::fanspeed_t::kLow: return kDaikin64FanLow; + case stdAc::fanspeed_t::kMedium: return kDaikin64FanMed; + case stdAc::fanspeed_t::kHigh: return kDaikin64FanHigh; + case stdAc::fanspeed_t::kMax: return kDaikin64FanTurbo; + default: return kDaikin64FanAuto; + } +} + +/// 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 IRDaikin64::toCommonFanSpeed(const uint8_t speed) { + switch (speed) { + case kDaikin64FanTurbo: return stdAc::fanspeed_t::kMax; + case kDaikin64FanHigh: return stdAc::fanspeed_t::kHigh; + case kDaikin64FanMed: return stdAc::fanspeed_t::kMedium; + case kDaikin64FanLow: return stdAc::fanspeed_t::kLow; + case kDaikinFanQuiet: return stdAc::fanspeed_t::kMin; + default: return stdAc::fanspeed_t::kAuto; + } +} + +/// Get the Turbo (Powerful) mode status of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRDaikin64::getTurbo(void) const { return _.Fan == kDaikin64FanTurbo; } + +/// Set the Turbo (Powerful) mode of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRDaikin64::setTurbo(const bool on) { + if (on) { + setFan(kDaikin64FanTurbo); + } else if (_.Fan == kDaikin64FanTurbo) { + setFan(kDaikin64FanAuto); + } +} + +/// Get the Quiet mode status of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRDaikin64::getQuiet(void) const { return _.Fan == kDaikin64FanQuiet; } + +/// Set the Quiet mode of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRDaikin64::setQuiet(const bool on) { + if (on) { + setFan(kDaikin64FanQuiet); + } else if (_.Fan == kDaikin64FanQuiet) { + setFan(kDaikin64FanAuto); + } +} + +/// Set the Vertical Swing mode of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRDaikin64::setSwingVertical(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 IRDaikin64::getSwingVertical(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 IRDaikin64::setSleep(const bool on) { _.Sleep = on; } + +/// Get the Sleep mode of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRDaikin64::getSleep(void) const { return _.Sleep; } + +/// Set the clock on the A/C unit. +/// @param[in] mins_since_midnight Nr. of minutes past midnight. +void IRDaikin64::setClock(const uint16_t mins_since_midnight) { + uint16_t mins = mins_since_midnight; + if (mins_since_midnight >= 24 * 60) mins = 0; // Bounds check. + _.ClockMins = uint8ToBcd(mins % 60); + _.ClockHours = uint8ToBcd(mins / 60); +} + +/// Get the clock time to be sent to the A/C unit. +/// @return The number of minutes past midnight. +uint16_t IRDaikin64::getClock(void) const { + return bcdToUint8(_.ClockHours) * 60 + bcdToUint8(_.ClockMins); +} + +/// Set the enable status of the On Timer. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRDaikin64::setOnTimeEnabled(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 IRDaikin64::getOnTimeEnabled(void) const { return _.OnTimer; } + +/// Get the On Timer time to be sent to the A/C unit. +/// @return The number of minutes past midnight. +uint16_t IRDaikin64::getOnTime(void) const { return GETTIME(On); } + +/// Set the On Timer time for the A/C unit. +/// @param[in] mins_since_midnight Nr. of minutes past midnight. +void IRDaikin64::setOnTime(const uint16_t mins_since_midnight) { + SETTIME(On, mins_since_midnight); +} + +/// Set the enable status of the Off Timer. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRDaikin64::setOffTimeEnabled(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 IRDaikin64::getOffTimeEnabled(void) const { return _.OffTimer; } + +/// Get the Off Timer time to be sent to the A/C unit. +/// @return The number of minutes past midnight. +uint16_t IRDaikin64::getOffTime(void) const { return GETTIME(Off); } + +/// Set the Off Timer time for the A/C unit. +/// @param[in] mins_since_midnight Nr. of minutes past midnight. +void IRDaikin64::setOffTime(const uint16_t mins_since_midnight) { + SETTIME(Off, mins_since_midnight); +} + +/// Convert the current internal state into a human readable string. +/// @return A human readable string. +String IRDaikin64::toString(void) const { + String result = ""; + result.reserve(120); // Reserve some heap for the string to reduce fragging. + result += addBoolToString(_.Power, kPowerToggleStr, false); + result += addModeToString(_.Mode, 0xFF, kDaikin64Cool, + kDaikin64Heat, kDaikin64Dry, kDaikin64Fan); + result += addTempToString(getTemp()); + if (!getTurbo()) { + result += addFanToString(_.Fan, kDaikin64FanHigh, kDaikin64FanLow, + kDaikin64FanAuto, kDaikin64FanQuiet, + kDaikin64FanMed); + } else { + result += addIntToString(_.Fan, kFanStr); + result += kSpaceLBraceStr; + result += kTurboStr; + result += ')'; + } + result += addBoolToString(getTurbo(), kTurboStr); + result += addBoolToString(getQuiet(), kQuietStr); + result += addBoolToString(_.SwingV, kSwingVStr); + result += addBoolToString(_.Sleep, kSleepStr); + result += addLabeledString(minsToString(getClock()), kClockStr); + result += addLabeledString(_.OnTimer + ? minsToString(getOnTime()) : kOffStr, + kOnTimerStr); + result += addLabeledString(_.OffTimer + ? minsToString(getOffTime()) : kOffStr, + kOffTimerStr); + return result; +} + +/// Convert the current internal state into its stdAc::state_t equivalent. +/// @param[in] prev Ptr to a previous state. +/// @return The stdAc equivalent of the native settings. +stdAc::state_t IRDaikin64::toCommon(const stdAc::state_t *prev) const { + stdAc::state_t result; + if (prev != NULL) result = *prev; + result.protocol = decode_type_t::DAIKIN64; + 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.turbo = getTurbo(); + result.quiet = getQuiet(); + result.sleep = _.Sleep ? 0 : -1; + result.clock = getClock(); + // Not supported. + result.swingh = stdAc::swingh_t::kOff; + result.clean = false; + result.filter = false; + result.beep = false; + result.econo = false; + result.light = false; + return result; +} diff --git a/lib/IRremoteESP8266/src/ir_Daikin.h b/lib/IRremoteESP8266/src/ir_Daikin.h index 6487d20d7c..0a12c5b69e 100644 --- a/lib/IRremoteESP8266/src/ir_Daikin.h +++ b/lib/IRremoteESP8266/src/ir_Daikin.h @@ -54,11 +54,11 @@ #ifndef UNIT_TEST #include #endif -#include "../src/IRrecv.h" -#include "../src/IRremoteESP8266.h" -#include "../src/IRsend.h" +#include "IRrecv.h" +#include "IRremoteESP8266.h" +#include "IRsend.h" #ifdef UNIT_TEST -#include "../src/IRsend_test.h" +#include "IRsend_test.h" #endif /// Native representation of a Daikin A/C message. diff --git a/lib/IRremoteESP8266/src/ir_Delonghi.cpp b/lib/IRremoteESP8266/src/ir_Delonghi.cpp new file mode 100644 index 0000000000..21a787799b --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_Delonghi.cpp @@ -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 + +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; +} diff --git a/lib/IRremoteESP8266/src/ir_Delonghi.h b/lib/IRremoteESP8266/src/ir_Delonghi.h index 7954e4ade1..ac2394cb16 100644 --- a/lib/IRremoteESP8266/src/ir_Delonghi.h +++ b/lib/IRremoteESP8266/src/ir_Delonghi.h @@ -16,10 +16,10 @@ #ifndef UNIT_TEST #include #endif -#include "../src/IRremoteESP8266.h" -#include "../src/IRsend.h" +#include "IRremoteESP8266.h" +#include "IRsend.h" #ifdef UNIT_TEST -#include "../src/IRsend_test.h" +#include "IRsend_test.h" #endif /// Native representation of a Delonghi A/C message. diff --git a/lib/IRremoteESP8266/src/ir_Denon.cpp b/lib/IRremoteESP8266/src/ir_Denon.cpp new file mode 100644 index 0000000000..700fd31cbc --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_Denon.cpp @@ -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 +#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 diff --git a/lib/IRremoteESP8266/src/ir_Dish.cpp b/lib/IRremoteESP8266/src/ir_Dish.cpp new file mode 100644 index 0000000000..94f5450b80 --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_Dish.cpp @@ -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 diff --git a/lib/IRremoteESP8266/src/ir_Doshisha.cpp b/lib/IRremoteESP8266/src/ir_Doshisha.cpp new file mode 100644 index 0000000000..0a5fadedb8 --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_Doshisha.cpp @@ -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 diff --git a/lib/IRremoteESP8266/src/ir_Ecoclim.cpp b/lib/IRremoteESP8266/src/ir_Ecoclim.cpp new file mode 100644 index 0000000000..b16f8beb2d --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_Ecoclim.cpp @@ -0,0 +1,423 @@ +// Copyright 2021 David Conran + +/// @file +/// @brief EcoClim A/C protocol. +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1397 + +#include "ir_Ecoclim.h" +#include +#include +#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.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; +} diff --git a/lib/IRremoteESP8266/src/ir_Ecoclim.h b/lib/IRremoteESP8266/src/ir_Ecoclim.h index 63a91c7109..ea243f67cc 100644 --- a/lib/IRremoteESP8266/src/ir_Ecoclim.h +++ b/lib/IRremoteESP8266/src/ir_Ecoclim.h @@ -16,10 +16,10 @@ #ifndef UNIT_TEST #include #endif -#include "../src/IRremoteESP8266.h" -#include "../src/IRsend.h" +#include "IRremoteESP8266.h" +#include "IRsend.h" #ifdef UNIT_TEST -#include "../src/IRsend_test.h" +#include "IRsend_test.h" #endif // Constants diff --git a/lib/IRremoteESP8266/src/ir_Electra.cpp b/lib/IRremoteESP8266/src/ir_Electra.cpp new file mode 100644 index 0000000000..7945cb0adc --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_Electra.cpp @@ -0,0 +1,402 @@ +// Copyright 2018, 2019 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 +#include +#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; +} + +/// 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.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; + // 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(130); // Reserve some heap for the string to reduce fragging. + 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); + 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 diff --git a/lib/IRremoteESP8266/src/ir_Electra.h b/lib/IRremoteESP8266/src/ir_Electra.h index 843c7a5a7c..886a92c56c 100644 --- a/lib/IRremoteESP8266/src/ir_Electra.h +++ b/lib/IRremoteESP8266/src/ir_Electra.h @@ -18,10 +18,10 @@ #ifndef UNIT_TEST #include #endif -#include "../src/IRremoteESP8266.h" -#include "../src/IRsend.h" +#include "IRremoteESP8266.h" +#include "IRsend.h" #ifdef UNIT_TEST -#include "../src/IRsend_test.h" +#include "IRsend_test.h" #endif /// Native representation of a Electra A/C message. diff --git a/lib/IRremoteESP8266/src/ir_EliteScreens.cpp b/lib/IRremoteESP8266/src/ir_EliteScreens.cpp new file mode 100644 index 0000000000..bfbdcc1e70 --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_EliteScreens.cpp @@ -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 diff --git a/lib/IRremoteESP8266/src/ir_Epson.cpp b/lib/IRremoteESP8266/src/ir_Epson.cpp new file mode 100644 index 0000000000..a92beef4ff --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_Epson.cpp @@ -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 +#include +#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 diff --git a/lib/IRremoteESP8266/src/ir_Fujitsu.cpp b/lib/IRremoteESP8266/src/ir_Fujitsu.cpp new file mode 100644 index 0000000000..da4b41dcf6 --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_Fujitsu.cpp @@ -0,0 +1,1043 @@ +// Copyright 2017 Jonny Graham +// Copyright 2017-2021 David Conran +// Copyright 2021 siriuslzx + +/// @file +/// @brief Support for Fujitsu A/C protocols. +/// Fujitsu A/C support added by Jonny Graham & David Conran +/// @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 + +#include "ir_Fujitsu.h" +#include +#ifndef ARDUINO +#include +#endif +#include "IRsend.h" +#include "IRtext.h" +#include "IRutils.h" + +// Ref: +// These values are based on averages of measurements +const uint16_t kFujitsuAcHdrMark = 3324; +const uint16_t kFujitsuAcHdrSpace = 1574; +const uint16_t kFujitsuAcBitMark = 448; +const uint16_t kFujitsuAcOneSpace = 1182; +const uint16_t kFujitsuAcZeroSpace = 390; +const uint16_t kFujitsuAcMinGap = 8100; +const uint8_t kFujitsuAcExtraTolerance = 5; // Extra tolerance percentage. + +using irutils::addBoolToString; +using irutils::addIntToString; +using irutils::addLabeledString; +using irutils::addModeToString; +using irutils::addModelToString; +using irutils::addFanToString; +using irutils::addTempFloatToString; +using irutils::minsToString; + +#if SEND_FUJITSU_AC +/// Send a Fujitsu A/C formatted message. +/// Status: STABLE / Known Good. +/// @param[in] data The message to be sent. +/// @param[in] nbytes The number of bytes of message to be sent. +/// Typically one of: +/// kFujitsuAcStateLength, +/// kFujitsuAcStateLength - 1, +/// kFujitsuAcStateLengthShort, +/// kFujitsuAcStateLengthShort - 1 +/// @param[in] repeat The number of times the command is to be repeated. +void IRsend::sendFujitsuAC(const unsigned char data[], const uint16_t nbytes, + const uint16_t repeat) { + sendGeneric(kFujitsuAcHdrMark, kFujitsuAcHdrSpace, kFujitsuAcBitMark, + kFujitsuAcOneSpace, kFujitsuAcBitMark, kFujitsuAcZeroSpace, + kFujitsuAcBitMark, kFujitsuAcMinGap, data, nbytes, 38, false, + repeat, 50); +} +#endif // SEND_FUJITSU_AC + +// Code to emulate Fujitsu A/C IR remote control unit. + +/// Class Constructor +/// @param[in] pin GPIO to be used when sending. +/// @param[in] model The enum for the model of A/C to be emulated. +/// @param[in] inverted Is the output signal to be inverted? +/// @param[in] use_modulation Is frequency modulation to be used? +IRFujitsuAC::IRFujitsuAC(const uint16_t pin, + const fujitsu_ac_remote_model_t model, + const bool inverted, const bool use_modulation) + : _irsend(pin, inverted, use_modulation) { + setModel(model); + stateReset(); +} + +/// Set the currently emulated model of the A/C. +/// @param[in] model An enum representing the model to support/emulate. +void IRFujitsuAC::setModel(const fujitsu_ac_remote_model_t model) { + _model = model; + switch (model) { + case fujitsu_ac_remote_model_t::ARDB1: + case fujitsu_ac_remote_model_t::ARJW2: + _state_length = kFujitsuAcStateLength - 1; + _state_length_short = kFujitsuAcStateLengthShort - 1; + break; + case fujitsu_ac_remote_model_t::ARRY4: + case fujitsu_ac_remote_model_t::ARRAH2E: + case fujitsu_ac_remote_model_t::ARREB1E: + default: + _state_length = kFujitsuAcStateLength; + _state_length_short = kFujitsuAcStateLengthShort; + } +} + +/// Get the currently emulated/detected model of the A/C. +/// @return The enum representing the model of A/C. +fujitsu_ac_remote_model_t IRFujitsuAC::getModel(void) const { return _model; } + +/// Reset the state of the remote to a known good state/sequence. +void IRFujitsuAC::stateReset(void) { + for (size_t i = 0; i < kFujitsuAcStateLength; i++) { + _.longcode[i] = 0; + } + setTemp(24); + _.Fan = kFujitsuAcFanHigh; + _.Mode = kFujitsuAcModeCool; + _.Swing = kFujitsuAcSwingBoth; + _cmd = kFujitsuAcCmdTurnOn; + _.Filter = false; + _.Clean = false; + _.TimerType = kFujitsuAcStopTimers; + _.OnTimer = 0; + _.OffTimer = 0; + _.longcode[0] = 0x14; + _.longcode[1] = 0x63; + _.longcode[3] = 0x10; + _.longcode[4] = 0x10; +} + +/// Set up hardware to be able to send a message. +void IRFujitsuAC::begin(void) { _irsend.begin(); } + +#if SEND_FUJITSU_AC +/// Send the current internal state as an IR message. +/// @param[in] repeat Nr. of times the message will be repeated. +void IRFujitsuAC::send(const uint16_t repeat) { + _irsend.sendFujitsuAC(getRaw(), getStateLength(), repeat); +} +#endif // SEND_FUJITSU_AC + +/// Update the length (size) of the state code for the current configuration. +/// @return true, if use long codes; false, use short codes. +bool IRFujitsuAC::updateUseLongOrShort(void) { + bool fullCmd = false; + switch (_cmd) { + case kFujitsuAcCmdTurnOff: // 0x02 + case kFujitsuAcCmdEcono: // 0x09 + case kFujitsuAcCmdPowerful: // 0x39 + case kFujitsuAcCmdStepVert: // 0x6C + case kFujitsuAcCmdToggleSwingVert: // 0x6D + case kFujitsuAcCmdStepHoriz: // 0x79 + case kFujitsuAcCmdToggleSwingHoriz: // 0x7A + _.Cmd = _cmd; + break; + default: + switch (_model) { + case fujitsu_ac_remote_model_t::ARRY4: + case fujitsu_ac_remote_model_t::ARRAH2E: + case fujitsu_ac_remote_model_t::ARREB1E: + case fujitsu_ac_remote_model_t::ARREW4E: + _.Cmd = 0xFE; + break; + case fujitsu_ac_remote_model_t::ARDB1: + case fujitsu_ac_remote_model_t::ARJW2: + _.Cmd = 0xFC; + break; + } + fullCmd = true; + break; + } + return fullCmd; +} + +/// Calculate and set the checksum values for the internal state. +void IRFujitsuAC::checkSum(void) { + if (updateUseLongOrShort()) { // Is it a long code? + // Nr. of bytes in the message after this byte. + _.RestLength = _state_length - 7; + _.Protocol = (_model == fujitsu_ac_remote_model_t::ARREW4E) ? 0x31 : 0x30; + _.Power = (_cmd == kFujitsuAcCmdTurnOn) || get10CHeat(); + + // These values depend on model + if (_model != fujitsu_ac_remote_model_t::ARREB1E && + _model != fujitsu_ac_remote_model_t::ARREW4E) { + _.OutsideQuiet = 0; + if (_model != fujitsu_ac_remote_model_t::ARRAH2E) { + _.TimerType = kFujitsuAcStopTimers; + } + } + if (_model != fujitsu_ac_remote_model_t::ARRY4) { + if (_model != fujitsu_ac_remote_model_t::ARREW4E) _.Clean = false; + _.Filter = false; + } + // Set the On/Off/Sleep timer Nr of mins. + _.OffTimer = getOffSleepTimer(); + _.OnTimer = getOnTimer(); + // Enable bit for the Off/Sleep timer + _.OffTimerEnable = _.OffTimer > 0; + // Enable bit for the On timer + _.OnTimerEnable = _.OnTimer > 0; + + uint8_t checksum = 0; + uint8_t checksum_complement = 0; + switch (_model) { + case fujitsu_ac_remote_model_t::ARDB1: + case fujitsu_ac_remote_model_t::ARJW2: + _.Swing = kFujitsuAcSwingOff; + checksum = sumBytes(_.longcode, _state_length - 1); + checksum_complement = 0x9B; + break; + case fujitsu_ac_remote_model_t::ARREB1E: + case fujitsu_ac_remote_model_t::ARRAH2E: + case fujitsu_ac_remote_model_t::ARRY4: + _.unknown = 1; + // FALL THRU + default: + checksum = sumBytes(_.longcode + _state_length_short, + _state_length - _state_length_short - 1); + } + // and negate the checksum and store it in the last byte. + _.longcode[_state_length - 1] = checksum_complement - checksum; + } else { // short codes + for (size_t i = 0; i < _state_length_short; i++) { + _.shortcode[i] = _.longcode[i]; + } + switch (_model) { + case fujitsu_ac_remote_model_t::ARRY4: + case fujitsu_ac_remote_model_t::ARRAH2E: + case fujitsu_ac_remote_model_t::ARREB1E: + case fujitsu_ac_remote_model_t::ARREW4E: + // The last byte is the inverse of penultimate byte + _.shortcode[_state_length_short - 1] = + ~_.shortcode[_state_length_short - 2]; + break; + default: + {}; // We don't need to do anything for the others. + } + } +} + +/// Get the length (size) of the state code for the current configuration. +/// @return The length of the state array required for this config. +uint8_t IRFujitsuAC::getStateLength(void) { + return updateUseLongOrShort() ? _state_length : _state_length_short; +} + +/// 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* IRFujitsuAC::getRaw(void) { + checkSum(); + if (_.Cmd == 0xFE || _.Cmd == 0xFC) + return _.longcode; + return _.shortcode; +} + +/// Build the internal state/config from the current (raw) A/C message. +/// @param[in] length Size of the current/used (raw) A/C message array. +void IRFujitsuAC::buildFromState(const uint16_t length) { + switch (length) { + case kFujitsuAcStateLength - 1: + case kFujitsuAcStateLengthShort - 1: + setModel(fujitsu_ac_remote_model_t::ARDB1); + // ARJW2 has horizontal swing. + if (_.Swing > kFujitsuAcSwingVert) + setModel(fujitsu_ac_remote_model_t::ARJW2); + break; + default: + switch (_.Cmd) { + case kFujitsuAcCmdEcono: + case kFujitsuAcCmdPowerful: + setModel(fujitsu_ac_remote_model_t::ARREB1E); + break; + default: + setModel(fujitsu_ac_remote_model_t::ARRAH2E); + } + } + switch (_.RestLength) { + case 8: + if (_model != fujitsu_ac_remote_model_t::ARJW2) + setModel(fujitsu_ac_remote_model_t::ARDB1); + break; + case 9: + if (_model != fujitsu_ac_remote_model_t::ARREB1E) + setModel(fujitsu_ac_remote_model_t::ARRAH2E); + break; + } + if (_.Power) + setCmd(kFujitsuAcCmdTurnOn); + else + setCmd(kFujitsuAcCmdStayOn); + // Currently the only way we know how to tell ARRAH2E & ARRY4 apart is if + // either the raw Filter or Clean setting is on. + if (_model == fujitsu_ac_remote_model_t::ARRAH2E && (_.Filter || _.Clean)) + setModel(fujitsu_ac_remote_model_t::ARRY4); + if (_state_length == kFujitsuAcStateLength && _.OutsideQuiet) + setModel(fujitsu_ac_remote_model_t::ARREB1E); + switch (_.Cmd) { + case kFujitsuAcCmdTurnOff: + case kFujitsuAcCmdStepHoriz: + case kFujitsuAcCmdToggleSwingHoriz: + case kFujitsuAcCmdStepVert: + case kFujitsuAcCmdToggleSwingVert: + case kFujitsuAcCmdEcono: + case kFujitsuAcCmdPowerful: + setCmd(_.Cmd); + break; + } + if (_.Protocol == 0x31) setModel(fujitsu_ac_remote_model_t::ARREW4E); +} + +/// Set the internal state from a valid code for this protocol. +/// @param[in] newState A valid code for this protocol. +/// @param[in] length Size of the newState array. +/// @return true, if successful; Otherwise false. (i.e. size check) +bool IRFujitsuAC::setRaw(const uint8_t newState[], const uint16_t length) { + if (length > kFujitsuAcStateLength) return false; + for (uint16_t i = 0; i < kFujitsuAcStateLength; i++) { + if (i < length) + _.longcode[i] = newState[i]; + else + _.longcode[i] = 0; + } + buildFromState(length); + return true; +} + +/// Request the A/C to step the Horizontal Swing. +void IRFujitsuAC::stepHoriz(void) { setCmd(kFujitsuAcCmdStepHoriz); } + +/// Request the A/C to toggle the Horizontal Swing mode. +/// @param[in] update Do we need to update the general swing config? +void IRFujitsuAC::toggleSwingHoriz(const bool update) { + // Toggle the current setting. + if (update) setSwing(getSwing() ^ kFujitsuAcSwingHoriz); + // and set the appropriate special command. + setCmd(kFujitsuAcCmdToggleSwingHoriz); +} + +/// Request the A/C to step the Vertical Swing. +void IRFujitsuAC::stepVert(void) { setCmd(kFujitsuAcCmdStepVert); } + +/// Request the A/C to toggle the Vertical Swing mode. +/// @param[in] update Do we need to update the general swing config? +void IRFujitsuAC::toggleSwingVert(const bool update) { + // Toggle the current setting. + if (update) setSwing(getSwing() ^ kFujitsuAcSwingVert); + // and set the appropriate special command. + setCmd(kFujitsuAcCmdToggleSwingVert); +} + +/// Set the requested (special) command part for the A/C message. +/// @param[in] cmd The special command code. +void IRFujitsuAC::setCmd(const uint8_t cmd) { + switch (cmd) { + case kFujitsuAcCmdTurnOff: + case kFujitsuAcCmdTurnOn: + case kFujitsuAcCmdStayOn: + case kFujitsuAcCmdStepVert: + case kFujitsuAcCmdToggleSwingVert: + _cmd = cmd; + break; + case kFujitsuAcCmdStepHoriz: + case kFujitsuAcCmdToggleSwingHoriz: + switch (_model) { + // Only these remotes have horizontal. + case fujitsu_ac_remote_model_t::ARRAH2E: + case fujitsu_ac_remote_model_t::ARJW2: + _cmd = cmd; + break; + default: + _cmd = kFujitsuAcCmdStayOn; + } + break; + case kFujitsuAcCmdEcono: + case kFujitsuAcCmdPowerful: + switch (_model) { + // Only these remotes have these commands. + case ARREB1E: + case ARREW4E: + _cmd = cmd; + break; + default: + _cmd = kFujitsuAcCmdStayOn; + } + break; + default: + _cmd = kFujitsuAcCmdStayOn; + } +} + +/// Set the requested (special) command part for the A/C message. +/// @return The special command code. +uint8_t IRFujitsuAC::getCmd(void) const { + return _cmd; +} + +/// Change the power setting. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRFujitsuAC::setPower(const bool on) { + setCmd(on ? kFujitsuAcCmdTurnOn : kFujitsuAcCmdTurnOff); +} + +/// Set the requested power state of the A/C to off. +void IRFujitsuAC::off(void) { setPower(false); } + +/// Set the requested power state of the A/C to on. +void IRFujitsuAC::on(void) { setPower(true); } + +/// Get the value of the current power setting. +/// @return true, the setting is on. false, the setting is off. +bool IRFujitsuAC::getPower(void) const { return _cmd != kFujitsuAcCmdTurnOff; } + +/// Set the Outside Quiet mode of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRFujitsuAC::setOutsideQuiet(const bool on) { + _.OutsideQuiet = on; + setCmd(kFujitsuAcCmdStayOn); // No special command involved. +} + +/// Get the Outside Quiet mode status of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRFujitsuAC::getOutsideQuiet(void) const { + switch (_model) { + // Only ARREB1E & ARREW4E seems to have this mode. + case fujitsu_ac_remote_model_t::ARREB1E: + case fujitsu_ac_remote_model_t::ARREW4E: + return _.OutsideQuiet; + default: return false; + } +} + +/// Set the temperature. +/// @param[in] temp The temperature in degrees. +/// @param[in] useCelsius Use Celsius or Fahrenheit? +void IRFujitsuAC::setTemp(const float temp, const bool useCelsius) { + float mintemp; + float maxtemp; + uint8_t offset; + bool _useCelsius; + float _temp; + + switch (_model) { + // These models have native Fahrenheit & Celsius upport. + case fujitsu_ac_remote_model_t::ARREW4E: + _useCelsius = useCelsius; + _temp = temp; + break; + // Make sure everything else uses Celsius. + default: + _useCelsius = true; + _temp = useCelsius ? temp : fahrenheitToCelsius(temp); + } + setCelsius(_useCelsius); + if (_useCelsius) { + mintemp = kFujitsuAcMinTemp; + maxtemp = kFujitsuAcMaxTemp; + offset = kFujitsuAcTempOffsetC; + } else { + mintemp = kFujitsuAcMinTempF; + maxtemp = kFujitsuAcMaxTempF; + offset = kFujitsuAcTempOffsetF; + } + _temp = std::max(mintemp, _temp); + _temp = std::min(maxtemp, _temp); + if (_useCelsius) { + if (_model == fujitsu_ac_remote_model_t::ARREW4E) + _.Temp = (_temp - (offset / 2)) * 2; + else + _.Temp = (_temp - offset) * 4; + } else { + _.Temp = _temp - offset; + } + setCmd(kFujitsuAcCmdStayOn); // No special command involved. +} + +/// Get the current temperature setting. +/// @return The current setting for temp. in degrees of the currently set units. +float IRFujitsuAC::getTemp(void) const { + if (_model == fujitsu_ac_remote_model_t::ARREW4E) { + if (_.Fahrenheit) // Currently only ARREW4E supports native Fahrenheit. + return _.Temp + kFujitsuAcTempOffsetF; + else + return (_.Temp / 2.0) + (kFujitsuAcMinTemp / 2); + } else { + return _.Temp / 4 + kFujitsuAcMinTemp; + } +} + +/// Set the speed of the fan. +/// @param[in] fanSpeed The desired setting. +void IRFujitsuAC::setFanSpeed(const uint8_t fanSpeed) { + if (fanSpeed > kFujitsuAcFanQuiet) + _.Fan = kFujitsuAcFanHigh; // Set the fan to maximum if out of range. + else + _.Fan = fanSpeed; + setCmd(kFujitsuAcCmdStayOn); // No special command involved. +} + +/// Get the current fan speed setting. +/// @return The current fan speed. +uint8_t IRFujitsuAC::getFanSpeed(void) const { return _.Fan; } + +/// Set the operating mode of the A/C. +/// @param[in] mode The desired operating mode. +void IRFujitsuAC::setMode(const uint8_t mode) { + if (mode > kFujitsuAcModeHeat) + _.Mode = kFujitsuAcModeHeat; // Set the mode to maximum if out of range. + else + _.Mode = mode; + setCmd(kFujitsuAcCmdStayOn); // No special command involved. +} + +/// Get the operating mode setting of the A/C. +/// @return The current operating mode setting. +uint8_t IRFujitsuAC::getMode(void) const { return _.Mode; } + +/// Set the requested swing operation mode of the A/C unit. +/// @param[in] swingMode The swingMode code for the A/C. +/// Vertical, Horizon, or Both. See constants for details. +/// @note Not all models support all possible swing modes. +void IRFujitsuAC::setSwing(const uint8_t swingMode) { + _.Swing = swingMode; + switch (_model) { + // No Horizontal support. + case fujitsu_ac_remote_model_t::ARDB1: + case fujitsu_ac_remote_model_t::ARREB1E: + case fujitsu_ac_remote_model_t::ARRY4: + // Set the mode to max if out of range + if (swingMode > kFujitsuAcSwingVert) _.Swing = kFujitsuAcSwingVert; + break; + // Has Horizontal support. + case fujitsu_ac_remote_model_t::ARRAH2E: + case fujitsu_ac_remote_model_t::ARJW2: + default: + // Set the mode to max if out of range + if (swingMode > kFujitsuAcSwingBoth) _.Swing = kFujitsuAcSwingBoth; + } + setCmd(kFujitsuAcCmdStayOn); // No special command involved. +} + +/// Get the requested swing operation mode of the A/C unit. +/// @return The contents of the swing state/mode. +uint8_t IRFujitsuAC::getSwing(void) const { + return _.Swing; +} + +/// Set the Clean mode of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRFujitsuAC::setClean(const bool on) { + _.Clean = on; + setCmd(kFujitsuAcCmdStayOn); // No special command involved. +} + +/// Get the Clean mode status of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRFujitsuAC::getClean(void) const { + switch (_model) { + case fujitsu_ac_remote_model_t::ARRY4: return _.Clean; + default: return false; + } +} + +/// Set the Filter mode status of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRFujitsuAC::setFilter(const bool on) { + _.Filter = on; + setCmd(kFujitsuAcCmdStayOn); // No special command involved. +} + +/// Get the Filter mode status of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRFujitsuAC::getFilter(void) const { + switch (_model) { + case fujitsu_ac_remote_model_t::ARRY4: return _.Filter; + default: return false; + } +} + +/// Set the 10C heat status of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRFujitsuAC::set10CHeat(const bool on) { + switch (_model) { + // Only selected models support this. + case fujitsu_ac_remote_model_t::ARREW4E: + setClean(on); // 10C Heat uses the same bit as Clean + if (on) { + _.Mode = kFujitsuAcModeFan; + _.Power = true; + _.Fan = kFujitsuAcFanAuto; + _.Swing = kFujitsuAcSwingOff; + } + default: + break; + } +} + +/// Get the 10C heat status of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRFujitsuAC::get10CHeat(void) const { + switch (_model) { + case fujitsu_ac_remote_model_t::ARREW4E: + return (_.Clean && _.Power && _.Mode == kFujitsuAcModeFan && + _.Fan == kFujitsuAcFanAuto && _.Swing == kFujitsuAcSwingOff); + default: return false; + } +} + +/// Get the Timer type of the A/C message. +/// @return The current timer type in numeric form. +uint8_t IRFujitsuAC::getTimerType(void) const { + switch (_model) { + // These models seem to have timer support. + case fujitsu_ac_remote_model_t::ARRAH2E: + case fujitsu_ac_remote_model_t::ARREB1E: + return _.TimerType; + default: return kFujitsuAcStopTimers; + } +} + +/// Set the Timer type of the A/C message. +/// @param[in] timertype The kind of timer to use for the message. +void IRFujitsuAC::setTimerType(const uint8_t timertype) { + switch (timertype) { + case kFujitsuAcSleepTimer: + case kFujitsuAcOnTimer: + case kFujitsuAcOffTimer: + case kFujitsuAcStopTimers: + _.TimerType = timertype; + break; + default: _.TimerType = kFujitsuAcStopTimers; + } +} + +/// Get the On Timer setting of the A/C. +/// @return nr of minutes left on the timer. 0 means disabled/not supported. +uint16_t IRFujitsuAC::getOnTimer(void) const { + if (getTimerType() == kFujitsuAcOnTimer) + return _.OnTimer; + return 0; +} + +/// Set the On Timer setting of the A/C. +/// @param[in] nr_mins Nr. of minutes to set the timer to. 0 means disabled. +void IRFujitsuAC::setOnTimer(const uint16_t nr_mins) { + _.OnTimer = std::min(kFujitsuAcTimerMax, nr_mins); // Bounds check. + if (_.OnTimer) { + _.TimerType = kFujitsuAcOnTimer; + } else if (getTimerType() == kFujitsuAcOnTimer) { + _.TimerType = kFujitsuAcStopTimers; + } +} + +/// Get the Off/Sleep Timer setting of the A/C. +/// @return nr of minutes left on the timer. 0 means disabled/not supported. +uint16_t IRFujitsuAC::getOffSleepTimer(void) const { + switch (getTimerType()) { + case kFujitsuAcOffTimer: + case kFujitsuAcSleepTimer: + return _.OffTimer; + default: + return 0; + } +} + +/// Set the Off/Sleep Timer time for the A/C. +/// @param[in] nr_mins Nr. of minutes to set the timer to. 0 means disabled. +inline void IRFujitsuAC::setOffSleepTimer(const uint16_t nr_mins) { + _.OffTimer = std::min(kFujitsuAcTimerMax, nr_mins); // Bounds check. +} + +/// Set the Off Timer time for the A/C. +/// @param[in] nr_mins Nr. of minutes to set the timer to. 0 means disabled. +void IRFujitsuAC::setOffTimer(const uint16_t nr_mins) { + setOffSleepTimer(nr_mins); + if (nr_mins) + _.TimerType = kFujitsuAcOffTimer; + else if (getTimerType() != kFujitsuAcOnTimer) + _.TimerType = kFujitsuAcStopTimers; +} + +/// Set the Sleep Timer time for the A/C. +/// @param[in] nr_mins Nr. of minutes to set the timer to. 0 means disabled. +void IRFujitsuAC::setSleepTimer(const uint16_t nr_mins) { + setOffSleepTimer(nr_mins); + if (nr_mins) + _.TimerType = kFujitsuAcSleepTimer; + else if (getTimerType() != kFujitsuAcOnTimer) + _.TimerType = kFujitsuAcStopTimers; +} + +/// 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 IRFujitsuAC::validChecksum(uint8_t state[], const uint16_t length) { + uint8_t sum = 0; + uint8_t sum_complement = 0; + uint8_t checksum = state[length - 1]; + switch (length) { + case kFujitsuAcStateLengthShort: // ARRAH2E, ARREB1E, & ARRY4 + return state[length - 1] == (uint8_t)~state[length - 2]; + case kFujitsuAcStateLength - 1: // ARDB1 & ARJW2 + sum = sumBytes(state, length - 1); + sum_complement = 0x9B; + break; + case kFujitsuAcStateLength: // ARRAH2E, ARRY4, & ARREB1E + sum = sumBytes(state + kFujitsuAcStateLengthShort, + length - 1 - kFujitsuAcStateLengthShort); + break; + default: // Includes ARDB1 & ARJW2 short. + return true; // Assume the checksum is valid for other lengths. + } + return checksum == (uint8_t)(sum_complement - sum); // Does it match? +} + +/// Set the device's remote ID number. +/// @param[in] num The ID for the remote. Valid number range is 0 to 3. +void IRFujitsuAC::setId(const uint8_t num) { _.Id = num; } + +/// Get the current device's remote ID number. +/// @return The current device's remote ID number. +uint8_t IRFujitsuAC::getId(void) const { return _.Id; } + +/// Set the Temperature units for the A/C. +/// @param[in] on true, use Celsius. false, use Fahrenheit. +void IRFujitsuAC::setCelsius(const bool on) { _.Fahrenheit = !on; } + +/// Get the Clean mode status of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRFujitsuAC::getCelsius(void) const { return !_.Fahrenheit; } + +/// 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 IRFujitsuAC::convertMode(const stdAc::opmode_t mode) { + switch (mode) { + case stdAc::opmode_t::kCool: return kFujitsuAcModeCool; + case stdAc::opmode_t::kHeat: return kFujitsuAcModeHeat; + case stdAc::opmode_t::kDry: return kFujitsuAcModeDry; + case stdAc::opmode_t::kFan: return kFujitsuAcModeFan; + default: return kFujitsuAcModeAuto; + } +} + +/// 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 IRFujitsuAC::convertFan(stdAc::fanspeed_t speed) { + switch (speed) { + case stdAc::fanspeed_t::kMin: return kFujitsuAcFanQuiet; + case stdAc::fanspeed_t::kLow: return kFujitsuAcFanLow; + case stdAc::fanspeed_t::kMedium: return kFujitsuAcFanMed; + case stdAc::fanspeed_t::kHigh: + case stdAc::fanspeed_t::kMax: return kFujitsuAcFanHigh; + default: return kFujitsuAcFanAuto; + } +} + +/// 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 IRFujitsuAC::toCommonMode(const uint8_t mode) { + switch (mode) { + case kFujitsuAcModeCool: return stdAc::opmode_t::kCool; + case kFujitsuAcModeHeat: return stdAc::opmode_t::kHeat; + case kFujitsuAcModeDry: return stdAc::opmode_t::kDry; + case kFujitsuAcModeFan: 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 IRFujitsuAC::toCommonFanSpeed(const uint8_t speed) { + switch (speed) { + case kFujitsuAcFanHigh: return stdAc::fanspeed_t::kMax; + case kFujitsuAcFanMed: return stdAc::fanspeed_t::kMedium; + case kFujitsuAcFanLow: return stdAc::fanspeed_t::kLow; + case kFujitsuAcFanQuiet: 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 IRFujitsuAC::toCommon(void) const { + stdAc::state_t result; + result.protocol = decode_type_t::FUJITSU_AC; + result.model = _model; + result.power = getPower(); + result.mode = toCommonMode(_.Mode); + result.celsius = getCelsius(); + result.degrees = getTemp(); + result.fanspeed = toCommonFanSpeed(_.Fan); + uint8_t swing = _.Swing; + switch (result.model) { + case fujitsu_ac_remote_model_t::ARREB1E: + case fujitsu_ac_remote_model_t::ARRAH2E: + case fujitsu_ac_remote_model_t::ARRY4: + result.clean = _.Clean; + result.filter = _.Filter; + result.swingv = (swing & kFujitsuAcSwingVert) ? stdAc::swingv_t::kAuto + : stdAc::swingv_t::kOff; + result.swingh = (swing & kFujitsuAcSwingHoriz) ? stdAc::swingh_t::kAuto + : stdAc::swingh_t::kOff; + break; + case fujitsu_ac_remote_model_t::ARDB1: + case fujitsu_ac_remote_model_t::ARJW2: + default: + result.swingv = stdAc::swingv_t::kOff; + result.swingh = stdAc::swingh_t::kOff; + } + + result.quiet = _.Fan == kFujitsuAcFanQuiet; + result.turbo = _cmd == kFujitsuAcCmdPowerful; + result.econo = _cmd == kFujitsuAcCmdEcono; + // Not supported. + result.light = false; + result.filter = 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 IRFujitsuAC::toString(void) const { + String result = ""; + result.reserve(100); // Reserve some heap for the string to reduce fragging. + fujitsu_ac_remote_model_t model = _model; + result += addModelToString(decode_type_t::FUJITSU_AC, model, false); + result += addIntToString(_.Id, kIdStr); + result += addBoolToString(getPower(), kPowerStr); + result += addModeToString(_.Mode, kFujitsuAcModeAuto, kFujitsuAcModeCool, + kFujitsuAcModeHeat, kFujitsuAcModeDry, + kFujitsuAcModeFan); + result += addTempFloatToString(getTemp(), getCelsius()); + result += addFanToString(_.Fan, kFujitsuAcFanHigh, kFujitsuAcFanLow, + kFujitsuAcFanAuto, kFujitsuAcFanQuiet, + kFujitsuAcFanMed); + switch (model) { + // These models have no internal swing, clean. or filter state. + case fujitsu_ac_remote_model_t::ARDB1: + case fujitsu_ac_remote_model_t::ARJW2: + break; + // These models have Clean & Filter, plus Swing (via fall thru) + case fujitsu_ac_remote_model_t::ARRAH2E: + case fujitsu_ac_remote_model_t::ARREB1E: + case fujitsu_ac_remote_model_t::ARRY4: + result += addBoolToString(getClean(), kCleanStr); + result += addBoolToString(getFilter(), kFilterStr); + // FALL THRU + default: // e.g. ARREW4E + if (model == fujitsu_ac_remote_model_t::ARREW4E) + result += addBoolToString(get10CHeat(), k10CHeatStr); + result += addIntToString(_.Swing, kSwingStr); + result += kSpaceLBraceStr; + switch (_.Swing) { + case kFujitsuAcSwingOff: + result += kOffStr; + break; + case kFujitsuAcSwingVert: + result += kSwingVStr; + break; + case kFujitsuAcSwingHoriz: + result += kSwingHStr; + break; + case kFujitsuAcSwingBoth: + result += kSwingVStr; + result += '+'; + result += kSwingHStr; + break; + default: + result += kUnknownStr; + } + result += ')'; + } + result += kCommaSpaceStr; + result += kCommandStr; + result += kColonSpaceStr; + switch (_cmd) { + case kFujitsuAcCmdStepHoriz: + result += kStepStr; + result += ' '; + result += kSwingHStr; + break; + case kFujitsuAcCmdStepVert: + result += kStepStr; + result += ' '; + result += kSwingVStr; + break; + case kFujitsuAcCmdToggleSwingHoriz: + result += kToggleStr; + result += ' '; + result += kSwingHStr; + break; + case kFujitsuAcCmdToggleSwingVert: + result += kToggleStr; + result += ' '; + result += kSwingVStr; + break; + case kFujitsuAcCmdEcono: + result += kEconoStr; + break; + case kFujitsuAcCmdPowerful: + result += kPowerfulStr; + break; + default: + result += kNAStr; + } + uint16_t mins = 0; + String type_str = kTimerStr; + switch (model) { + case fujitsu_ac_remote_model_t::ARREB1E: + case fujitsu_ac_remote_model_t::ARREW4E: + result += addBoolToString(getOutsideQuiet(), kOutsideQuietStr); + // FALL THRU + // These models seem to have timer support. + case fujitsu_ac_remote_model_t::ARRAH2E: + switch (getTimerType()) { + case kFujitsuAcOnTimer: + type_str = kOnTimerStr; + mins = getOnTimer(); + break; + case kFujitsuAcOffTimer: + type_str = kOffTimerStr; + mins = getOffSleepTimer(); + break; + case kFujitsuAcSleepTimer: + type_str = kSleepTimerStr; + mins = getOffSleepTimer(); + break; + } + result += addLabeledString(mins ? minsToString(mins) : kOffStr, type_str); + break; + default: + break; + } + return result; +} + +#if DECODE_FUJITSU_AC +/// Decode the supplied Fujitsu AC IR message if possible. +/// 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::decodeFujitsuAC(decode_results* results, uint16_t offset, + const uint16_t nbits, + const bool strict) { + uint16_t dataBitsSoFar = 0; + + // Have we got enough data to successfully decode? + if (results->rawlen < (2 * kFujitsuAcMinBits) + kHeader + kFooter - 1 + + offset) + return false; // Can't possibly be a valid message. + + // Compliance + if (strict) { + switch (nbits) { + case kFujitsuAcBits: + case kFujitsuAcBits - 8: + case kFujitsuAcMinBits: + case kFujitsuAcMinBits + 8: break; + default: return false; // Must be called with the correct nr. of bits. + } + } + + // Header / Some of the Data + uint16_t used = matchGeneric(results->rawbuf + offset, results->state, + results->rawlen - offset, kFujitsuAcMinBits - 8, + kFujitsuAcHdrMark, kFujitsuAcHdrSpace, // Header + kFujitsuAcBitMark, kFujitsuAcOneSpace, // Data + kFujitsuAcBitMark, kFujitsuAcZeroSpace, + 0, 0, // No Footer (yet) + false, _tolerance + kFujitsuAcExtraTolerance, 0, + false); // LSBF + if (!used) return false; + offset += used; + // Check we have the typical data header. + if (results->state[0] != 0x14 || results->state[1] != 0x63) return false; + dataBitsSoFar += kFujitsuAcMinBits - 8; + + // Keep reading bytes until we either run out of message or state to fill. + match_result_t data_result; + for (uint16_t i = 5; + offset <= results->rawlen - 16 && i < kFujitsuAcStateLength; + i++, dataBitsSoFar += 8, offset += data_result.used) { + data_result = matchData( + &(results->rawbuf[offset]), 8, kFujitsuAcBitMark, kFujitsuAcOneSpace, + kFujitsuAcBitMark, kFujitsuAcZeroSpace, + _tolerance + kFujitsuAcExtraTolerance, 0, false); + if (data_result.success == false) break; // Fail + results->state[i] = data_result.data; + } + + // Footer + if (offset > results->rawlen || + !matchMark(results->rawbuf[offset++], kFujitsuAcBitMark)) + return false; + // The space is optional if we are out of capture. + if (offset < results->rawlen && + !matchAtLeast(results->rawbuf[offset], kFujitsuAcMinGap)) + return false; + + // Compliance + if (strict) { + if (dataBitsSoFar != nbits) return false; + } + + results->decode_type = FUJITSU_AC; + results->bits = dataBitsSoFar; + + // Compliance + switch (dataBitsSoFar) { + case kFujitsuAcMinBits: + // Check if this values indicate that this should have been a long state + // message. + if (results->state[5] == 0xFC) return false; + return true; // Success + case kFujitsuAcMinBits + 8: + // Check if this values indicate that this should have been a long state + // message. + if (results->state[5] == 0xFE) return false; + // The last byte needs to be the inverse of the penultimate byte. + if (results->state[5] != (uint8_t)~results->state[6]) return false; + return true; // Success + case kFujitsuAcBits - 8: + // Long messages of this size require this byte be correct. + if (results->state[5] != 0xFC) return false; + break; + case kFujitsuAcBits: + // Long messages of this size require this byte be correct. + if (results->state[5] != 0xFE) return false; + break; + default: + return false; // Unexpected size. + } + if (!IRFujitsuAC::validChecksum(results->state, dataBitsSoFar / 8)) + return false; + + // Success + return true; // All good. +} +#endif // DECODE_FUJITSU_AC diff --git a/lib/IRremoteESP8266/src/ir_Fujitsu.h b/lib/IRremoteESP8266/src/ir_Fujitsu.h index 94618a84de..994b6f4a39 100644 --- a/lib/IRremoteESP8266/src/ir_Fujitsu.h +++ b/lib/IRremoteESP8266/src/ir_Fujitsu.h @@ -42,11 +42,11 @@ #ifdef ARDUINO #include #endif -#include "../src/IRrecv.h" -#include "../src/IRremoteESP8266.h" -#include "../src/IRsend.h" +#include "IRrecv.h" +#include "IRremoteESP8266.h" +#include "IRsend.h" #ifdef UNIT_TEST -#include "../src/IRsend_test.h" +#include "IRsend_test.h" #endif /// Native representation of a Fujitsu A/C message. diff --git a/lib/IRremoteESP8266/src/ir_GICable.cpp b/lib/IRremoteESP8266/src/ir_GICable.cpp new file mode 100644 index 0000000000..7b29d71db4 --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_GICable.cpp @@ -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 +#include +#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 diff --git a/lib/IRremoteESP8266/src/ir_GlobalCache.cpp b/lib/IRremoteESP8266/src/ir_GlobalCache.cpp new file mode 100644 index 0000000000..e8ebac4af8 --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_GlobalCache.cpp @@ -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 +#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 diff --git a/lib/IRremoteESP8266/src/ir_Goodweather.cpp b/lib/IRremoteESP8266/src/ir_Goodweather.cpp new file mode 100644 index 0000000000..1d6918e7ff --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_Goodweather.cpp @@ -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 +#ifndef ARDUINO +#include +#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 diff --git a/lib/IRremoteESP8266/src/ir_Goodweather.h b/lib/IRremoteESP8266/src/ir_Goodweather.h index e1f7f5ed5d..7e547edd83 100644 --- a/lib/IRremoteESP8266/src/ir_Goodweather.h +++ b/lib/IRremoteESP8266/src/ir_Goodweather.h @@ -16,10 +16,10 @@ #ifndef UNIT_TEST #include #endif -#include "../src/IRremoteESP8266.h" -#include "../src/IRsend.h" +#include "IRremoteESP8266.h" +#include "IRsend.h" #ifdef UNIT_TEST -#include "../src/IRsend_test.h" +#include "IRsend_test.h" #endif /// Native representation of a Goodweather A/C message. diff --git a/lib/IRremoteESP8266/src/ir_Gree.cpp b/lib/IRremoteESP8266/src/ir_Gree.cpp new file mode 100644 index 0000000000..1d1371b2d0 --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_Gree.cpp @@ -0,0 +1,715 @@ +// 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 +#include +#ifndef ARDUINO +#include +#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::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; + } +} + +/// 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: _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(kGreeMinTempC), safecelsius); + safecelsius = std::min(static_cast(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(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 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 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; + } + } + _.Swing = 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 _.Swing; +} + +/// 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 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 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(); + result.fanspeed = toCommonFanSpeed(_.Fan); + if (_.SwingAuto) + result.swingv = stdAc::swingv_t::kAuto; + else + result.swingv = toCommonSwingV(_.Swing); + result.turbo = _.Turbo; + result.light = _.Light; + result.clean = _.Xfan; + result.sleep = _.Sleep ? 0 : -1; + // Not supported. + result.swingh = stdAc::swingh_t::kOff; + result.quiet = false; + result.econo = 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); + 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(_.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(_.Swing, kSwingVStr); + result += kSpaceLBraceStr; + switch (_.Swing) { + case kGreeSwingLastPos: + result += kLastStr; + break; + case kGreeSwingAuto: + result += kAutoStr; + break; + default: result += kUnknownStr; + } + result += ')'; + 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 diff --git a/lib/IRremoteESP8266/src/ir_Gree.h b/lib/IRremoteESP8266/src/ir_Gree.h index d1d3e00be9..d93b42d8d9 100644 --- a/lib/IRremoteESP8266/src/ir_Gree.h +++ b/lib/IRremoteESP8266/src/ir_Gree.h @@ -32,10 +32,10 @@ #ifndef UNIT_TEST #include #endif -#include "../src/IRremoteESP8266.h" -#include "../src/IRsend.h" +#include "IRremoteESP8266.h" +#include "IRsend.h" #ifdef UNIT_TEST -#include "../src/IRsend_test.h" +#include "IRsend_test.h" #endif /// Native representation of a Gree A/C message. diff --git a/lib/IRremoteESP8266/src/ir_Haier.cpp b/lib/IRremoteESP8266/src/ir_Haier.cpp new file mode 100644 index 0000000000..41e40541aa --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_Haier.cpp @@ -0,0 +1,1241 @@ +// 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/1480 + +#include "ir_Haier.h" +#include +#include +#ifndef UNIT_TEST +#include +#endif +#include "IRremoteESP8266.h" +#include "IRtext.h" +#include "IRutils.h" + +// Constants +const uint16_t kHaierAcHdr = 3000; +const uint16_t kHaierAcHdrGap = 4300; +const uint16_t kHaierAcBitMark = 520; +const uint16_t kHaierAcOneSpace = 1650; +const uint16_t kHaierAcZeroSpace = 650; +const uint32_t kHaierAcMinGap = 150000; // Completely made up value. + +using irutils::addBoolToString; +using irutils::addIntToString; +using irutils::addLabeledString; +using irutils::addModeToString; +using irutils::addFanToString; +using irutils::addTempToString; +using irutils::minsToString; + +#define GETTIME(x) _.x##Hours * 60 + _.x##Mins +#define SETTIME(x, n) do { \ + uint16_t mins = n;\ + if (n > kHaierAcMaxTime) mins = kHaierAcMaxTime;\ + _.x##Hours = mins / 60;\ + _.x##Mins = mins % 60;\ +} while (0) + +#if (SEND_HAIER_AC || SEND_HAIER_AC_YRW02 || SEND_HAIER_AC176) +/// Send a Haier A/C formatted message. (HSU07-HEA03 remote) +/// 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::sendHaierAC(const unsigned char data[], const uint16_t nbytes, + const uint16_t repeat) { + if (nbytes < kHaierACStateLength) return; + + for (uint16_t r = 0; r <= repeat; r++) { + enableIROut(38000); + mark(kHaierAcHdr); + space(kHaierAcHdr); + sendGeneric(kHaierAcHdr, kHaierAcHdrGap, kHaierAcBitMark, kHaierAcOneSpace, + kHaierAcBitMark, kHaierAcZeroSpace, kHaierAcBitMark, + kHaierAcMinGap, data, nbytes, 38, true, + 0, // Repeats handled elsewhere + 50); + } +} +#endif // (SEND_HAIER_AC || SEND_HAIER_AC_YRW02 || SEND_HAIER_AC176) + +#if SEND_HAIER_AC_YRW02 +/// Send a Haier YR-W02 remote A/C formatted 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::sendHaierACYRW02(const unsigned char data[], const uint16_t nbytes, + const uint16_t repeat) { + if (nbytes >= kHaierACYRW02StateLength) sendHaierAC(data, nbytes, repeat); +} +#endif // SEND_HAIER_AC_YRW02 + +#if SEND_HAIER_AC176 +/// Send a Haier 176 bit remote A/C formatted 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::sendHaierAC176(const unsigned char data[], const uint16_t nbytes, + const uint16_t repeat) { + if (nbytes >= kHaierAC176StateLength) sendHaierAC(data, nbytes, repeat); +} +#endif // SEND_HAIER_AC176 + +/// 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? +IRHaierAC::IRHaierAC(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 IRHaierAC::begin(void) { _irsend.begin(); } + +#if SEND_HAIER_AC +/// Send the current internal state as an IR message. +/// @param[in] repeat Nr. of times the message will be repeated. +void IRHaierAC::send(const uint16_t repeat) { + _irsend.sendHaierAC(getRaw(), kHaierACStateLength, repeat); +} +#endif // SEND_HAIER_AC + +/// Calculate and set the checksum values for the internal state. +void IRHaierAC::checksum(void) { + _.Sum = sumBytes(_.remote_state, kHaierACStateLength - 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 of the state array. +/// @return true, if the state has a valid checksum. Otherwise, false. +bool IRHaierAC::validChecksum(uint8_t state[], const uint16_t length) { + if (length < 2) return false; // 1 byte of data can't have a checksum. + return (state[length - 1] == sumBytes(state, length - 1)); +} + +/// Reset the internal state to a fixed known good state. +void IRHaierAC::stateReset(void) { + std::memset(_.remote_state, 0, sizeof _.remote_state); + _.Prefix = kHaierAcPrefix; + _.unknown = 1; // const value + _.OffHours = 12; // default initial state + _.Temp = kHaierAcDefTemp - kHaierAcMinTemp; + _.Fan = 3; // kHaierAcFanLow; + _.Command = kHaierAcCmdOn; +} + +/// 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* IRHaierAC::getRaw(void) { + checksum(); + 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 IRHaierAC::setRaw(const uint8_t new_code[]) { + std::memcpy(_.remote_state, new_code, kHaierACStateLength); +} + +/// Set the Command/Button setting of the A/C. +/// @param[in] command The value of the command/button that was pressed. +void IRHaierAC::setCommand(const uint8_t command) { + switch (command) { + case kHaierAcCmdOff: + case kHaierAcCmdOn: + case kHaierAcCmdMode: + case kHaierAcCmdFan: + case kHaierAcCmdTempUp: + case kHaierAcCmdTempDown: + case kHaierAcCmdSleep: + case kHaierAcCmdTimerSet: + case kHaierAcCmdTimerCancel: + case kHaierAcCmdHealth: + case kHaierAcCmdSwing: + _.Command = command; + } +} + +/// Get the Command/Button setting of the A/C. +/// @return The value of the command/button that was pressed. +uint8_t IRHaierAC::getCommand(void) const { + return _.Command; +} + +/// Set the speed of the fan. +/// @param[in] speed The desired setting. +void IRHaierAC::setFan(const uint8_t speed) { + uint8_t new_speed = kHaierAcFanAuto; + switch (speed) { + case kHaierAcFanLow: new_speed = 3; break; + case kHaierAcFanMed: new_speed = 2; break; + case kHaierAcFanHigh: new_speed = 1; break; + // Default to auto for anything else. + default: new_speed = kHaierAcFanAuto; + } + + if (speed != getFan()) _.Command = kHaierAcCmdFan; + _.Fan = new_speed; +} + +/// Get the current fan speed setting. +/// @return The current fan speed. +uint8_t IRHaierAC::getFan(void) const { + switch (_.Fan) { + case 1: return kHaierAcFanHigh; + case 2: return kHaierAcFanMed; + case 3: return kHaierAcFanLow; + default: return kHaierAcFanAuto; + } +} + +/// Set the operating mode of the A/C. +/// @param[in] mode The desired operating mode. +void IRHaierAC::setMode(const uint8_t mode) { + uint8_t new_mode = mode; + _.Command = kHaierAcCmdMode; + // If out of range, default to auto mode. + if (mode > kHaierAcFan) new_mode = kHaierAcAuto; + _.Mode = new_mode; +} + +/// Get the operating mode setting of the A/C. +/// @return The current operating mode setting. +uint8_t IRHaierAC::getMode(void) const { + return _.Mode; +} + +/// Set the temperature. +/// @param[in] degrees The temperature in degrees celsius. +void IRHaierAC::setTemp(const uint8_t degrees) { + uint8_t temp = degrees; + if (temp < kHaierAcMinTemp) + temp = kHaierAcMinTemp; + else if (temp > kHaierAcMaxTemp) + temp = kHaierAcMaxTemp; + + uint8_t old_temp = getTemp(); + if (old_temp == temp) return; + if (old_temp > temp) + _.Command = kHaierAcCmdTempDown; + else + _.Command = kHaierAcCmdTempUp; + _.Temp = temp - kHaierAcMinTemp; +} + +/// Get the current temperature setting. +/// @return The current setting for temp. in degrees celsius. +uint8_t IRHaierAC::getTemp(void) const { + return _.Temp + kHaierAcMinTemp; +} + +/// Set the Health (filter) setting of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRHaierAC::setHealth(const bool on) { + _.Command = kHaierAcCmdHealth; + _.Health = on; +} + +/// Get the Health (filter) setting of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRHaierAC::getHealth(void) const { + return _.Health; +} + +/// Set the Sleep setting of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRHaierAC::setSleep(const bool on) { + _.Command = kHaierAcCmdSleep; + _.Sleep = on; +} + +/// Get the Sleep setting of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRHaierAC::getSleep(void) const { + return _.Sleep; +} + +/// Get the On Timer value/setting of the A/C. +/// @return Nr of minutes the timer is set to. -1 is Off/not set etc. +int16_t IRHaierAC::getOnTimer(void) const { + // Check if the timer is turned on. + if (_.OnTimer) + return GETTIME(On); + else + return -1; +} + +/// Get the Off Timer value/setting of the A/C. +/// @return Nr of minutes the timer is set to. -1 is Off/not set etc. +int16_t IRHaierAC::getOffTimer(void) const { + // Check if the timer is turned on. + if (_.OffTimer) + return GETTIME(Off); + else + return -1; +} + +/// Get the clock value of the A/C. +/// @return The clock time, in Nr of minutes past midnight. +uint16_t IRHaierAC::getCurrTime(void) const { return GETTIME(Curr); } + +/// Set & enable the On Timer. +/// @param[in] nr_mins The time expressed in total number of minutes. +void IRHaierAC::setOnTimer(const uint16_t nr_mins) { + _.Command = kHaierAcCmdTimerSet; + _.OnTimer = 1; + + SETTIME(On, nr_mins); +} + +/// Set & enable the Off Timer. +/// @param[in] nr_mins The time expressed in total number of minutes. +void IRHaierAC::setOffTimer(const uint16_t nr_mins) { + _.Command = kHaierAcCmdTimerSet; + _.OffTimer = 1; + + SETTIME(Off, nr_mins); +} + +/// Cancel/disable the On & Off timers. +void IRHaierAC::cancelTimers(void) { + _.Command = kHaierAcCmdTimerCancel; + _.OffTimer = 0; + _.OnTimer = 0; +} + +/// Set the clock value for the A/C. +/// @param[in] nr_mins The clock time, in Nr of minutes past midnight. +void IRHaierAC::setCurrTime(const uint16_t nr_mins) { + SETTIME(Curr, nr_mins); +} + +/// Get the Vertical Swing position setting of the A/C. +/// @return The native swing mode. +uint8_t IRHaierAC::getSwing(void) const { + return _.Swing; +} + +/// Set the Vertical Swing mode of the A/C. +/// @param[in] state The mode to set the vanes to. +void IRHaierAC::setSwing(const uint8_t state) { + if (state == _.Swing) return; // Nothing to do. + switch (state) { + case kHaierAcSwingOff: + case kHaierAcSwingUp: + case kHaierAcSwingDown: + case kHaierAcSwingChg: + _.Command = kHaierAcCmdSwing; + _.Swing = state; + 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 IRHaierAC::convertMode(const stdAc::opmode_t mode) { + switch (mode) { + case stdAc::opmode_t::kCool: return kHaierAcCool; + case stdAc::opmode_t::kHeat: return kHaierAcHeat; + case stdAc::opmode_t::kDry: return kHaierAcDry; + case stdAc::opmode_t::kFan: return kHaierAcFan; + default: return kHaierAcAuto; + } +} + +/// 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 IRHaierAC::convertFan(const stdAc::fanspeed_t speed) { + switch (speed) { + case stdAc::fanspeed_t::kMin: + case stdAc::fanspeed_t::kLow: return kHaierAcFanLow; + case stdAc::fanspeed_t::kMedium: return kHaierAcFanMed; + case stdAc::fanspeed_t::kHigh: + case stdAc::fanspeed_t::kMax: return kHaierAcFanHigh; + default: return kHaierAcFanAuto; + } +} + +/// 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 IRHaierAC::convertSwingV(const stdAc::swingv_t position) { + switch (position) { + case stdAc::swingv_t::kHighest: + case stdAc::swingv_t::kHigh: + case stdAc::swingv_t::kMiddle: return kHaierAcSwingUp; + case stdAc::swingv_t::kLow: + case stdAc::swingv_t::kLowest: return kHaierAcSwingDown; + case stdAc::swingv_t::kOff: return kHaierAcSwingOff; + default: return kHaierAcSwingChg; + } +} + +/// 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 IRHaierAC::toCommonMode(const uint8_t mode) { + switch (mode) { + case kHaierAcCool: return stdAc::opmode_t::kCool; + case kHaierAcHeat: return stdAc::opmode_t::kHeat; + case kHaierAcDry: return stdAc::opmode_t::kDry; + case kHaierAcFan: 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 IRHaierAC::toCommonFanSpeed(const uint8_t speed) { + switch (speed) { + case kHaierAcFanHigh: return stdAc::fanspeed_t::kMax; + case kHaierAcFanMed: return stdAc::fanspeed_t::kMedium; + case kHaierAcFanLow: return stdAc::fanspeed_t::kMin; + default: return stdAc::fanspeed_t::kAuto; + } +} + +/// Convert a stdAc::swingv_t enum into it's native setting. +/// @param[in] pos The enum to be converted. +/// @return The native equivalent of the enum. +stdAc::swingv_t IRHaierAC::toCommonSwingV(const uint8_t pos) { + switch (pos) { + case kHaierAcSwingUp: return stdAc::swingv_t::kHighest; + case kHaierAcSwingDown: return stdAc::swingv_t::kLowest; + case kHaierAcSwingOff: return stdAc::swingv_t::kOff; + default: return stdAc::swingv_t::kAuto; + } +} + +/// Convert the current internal state into its stdAc::state_t equivalent. +/// @return The stdAc equivalent of the native settings. +stdAc::state_t IRHaierAC::toCommon(void) const { + stdAc::state_t result; + result.protocol = decode_type_t::HAIER_AC; + result.model = -1; // No models used. + result.power = true; + if (_.Command == kHaierAcCmdOff) result.power = false; + result.mode = toCommonMode(_.Mode); + result.celsius = true; + result.degrees = getTemp(); + result.fanspeed = toCommonFanSpeed(getFan()); + result.swingv = toCommonSwingV(_.Swing); + result.filter = _.Health; + result.sleep = _.Sleep ? 0 : -1; + // Not supported. + result.swingh = stdAc::swingh_t::kOff; + result.quiet = false; + result.turbo = false; + result.econo = false; + result.light = false; + result.clean = false; + result.beep = true; + result.clock = -1; + return result; +} + +/// Convert the current internal state into a human readable string. +/// @return A human readable string. +String IRHaierAC::toString(void) const { + String result = ""; + result.reserve(150); // Reserve some heap for the string to reduce fragging. + uint8_t cmd = _.Command; + result += addIntToString(cmd, kCommandStr, false); + result += kSpaceLBraceStr; + switch (cmd) { + case kHaierAcCmdOff: + result += kOffStr; + break; + case kHaierAcCmdOn: + result += kOnStr; + break; + case kHaierAcCmdMode: + result += kModeStr; + break; + case kHaierAcCmdFan: + result += kFanStr; + break; + case kHaierAcCmdTempUp: + result += kTempUpStr; + break; + case kHaierAcCmdTempDown: + result += kTempDownStr; + break; + case kHaierAcCmdSleep: + result += kSleepStr; + break; + case kHaierAcCmdTimerSet: + result += kTimerStr; + result += ' '; + result += kSetStr; + break; + case kHaierAcCmdTimerCancel: + result += kTimerStr; + result += ' '; + result += kCancelStr; + break; + case kHaierAcCmdHealth: + result += kHealthStr; + break; + case kHaierAcCmdSwing: + result += kSwingStr; + break; + default: + result += kUnknownStr; + } + result += ')'; + result += addModeToString(_.Mode, kHaierAcAuto, kHaierAcCool, kHaierAcHeat, + kHaierAcDry, kHaierAcFan); + result += addTempToString(getTemp()); + result += addFanToString(getFan(), kHaierAcFanHigh, kHaierAcFanLow, + kHaierAcFanAuto, kHaierAcFanAuto, kHaierAcFanMed); + result += addIntToString(_.Swing, kSwingStr); + result += kSpaceLBraceStr; + switch (_.Swing) { + case kHaierAcSwingOff: + result += kOffStr; + break; + case kHaierAcSwingUp: + result += kUpStr; + break; + case kHaierAcSwingDown: + result += kDownStr; + break; + case kHaierAcSwingChg: + result += kChangeStr; + break; + default: + result += kUnknownStr; + } + result += ')'; + result += addBoolToString(_.Sleep, kSleepStr); + result += addBoolToString(_.Health, kHealthStr); + result += addLabeledString(minsToString(getCurrTime()), kClockStr); + result += addLabeledString( + getOnTimer() >= 0 ? minsToString(getOnTimer()) : kOffStr, kOnTimerStr); + result += addLabeledString( + getOffTimer() >= 0 ? minsToString(getOffTimer()) : kOffStr, + kOffTimerStr); + return result; +} +// End of IRHaierAC 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? +IRHaierAC176::IRHaierAC176(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 IRHaierAC176::begin(void) { _irsend.begin(); } + +#if SEND_HAIER_AC176 +/// Send the current internal state as an IR message. +/// @param[in] repeat Nr. of times the message will be repeated. +void IRHaierAC176::send(const uint16_t repeat) { + _irsend.sendHaierAC176(getRaw(), kHaierAC176StateLength, repeat); +} +#endif // SEND_HAIER_AC176 + +/// Calculate and set the checksum values for the internal state. +void IRHaierAC176::checksum(void) { + _.Sum = sumBytes(_.raw, kHaierACYRW02StateLength - 1); + _.Sum2 = sumBytes(_.raw + kHaierACYRW02StateLength, + kHaierAC176StateLength - kHaierACYRW02StateLength - 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 of the state array. +/// @return true, if the state has a valid checksum. Otherwise, false. +bool IRHaierAC176::validChecksum(const uint8_t state[], const uint16_t length) { + if (length < 2) return false; // 1 byte of data can't have a checksum. + if (length < kHaierAC176StateLength) { // Is it too short? + // Then it is just a checksum of the whole thing. + return (state[length - 1] == sumBytes(state, length - 1)); + } else { // It is long enough for two checksums. + return (state[kHaierACYRW02StateLength - 1] == + sumBytes(state, kHaierACYRW02StateLength - 1)) && + (state[length - 1] == + sumBytes(state + kHaierACYRW02StateLength, + length - kHaierACYRW02StateLength - 1)); + } +} + +/// Reset the internal state to a fixed known good state. +void IRHaierAC176::stateReset(void) { + std::memset(_.raw, 0, sizeof _.raw); + _.Prefix = kHaierAcYrw02Prefix; + _.Prefix2 = kHaierAc176Prefix; + _.Temp = kHaierAcDefTemp - kHaierAcMinTemp; + _.Health = true; + setFan(kHaierAcYrw02FanAuto); + _.Power = true; + _.Button = kHaierAcYrw02ButtonPower; +} + +/// 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* IRHaierAC176::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. +void IRHaierAC176::setRaw(const uint8_t new_code[]) { + memcpy(_.raw, new_code, kHaierAC176StateLength); +} + +/// Set the Button/Command setting of the A/C. +/// @param[in] button The value of the button/command that was pressed. +void IRHaierAC176::setButton(uint8_t button) { + switch (button) { + case kHaierAcYrw02ButtonTempUp: + case kHaierAcYrw02ButtonTempDown: + case kHaierAcYrw02ButtonSwing: + case kHaierAcYrw02ButtonFan: + case kHaierAcYrw02ButtonPower: + case kHaierAcYrw02ButtonMode: + case kHaierAcYrw02ButtonHealth: + case kHaierAcYrw02ButtonTurbo: + case kHaierAcYrw02ButtonSleep: + _.Button = button; + } +} + +/// Get the Button/Command setting of the A/C. +/// @return The value of the button/command that was pressed. +uint8_t IRHaierAC176::getButton(void) const { + return _.Button; +} + +/// Set the operating mode of the A/C. +/// @param[in] mode The desired operating mode. +void IRHaierAC176::setMode(uint8_t mode) { + switch (mode) { + case kHaierAcYrw02Auto: + case kHaierAcYrw02Dry: + case kHaierAcYrw02Fan: + // Turbo & Quiet is only available in Cool/Heat mode. + _.Turbo = false; + _.Quiet = false; + // FALL-THRU + case kHaierAcYrw02Cool: + case kHaierAcYrw02Heat: + _.Button = kHaierAcYrw02ButtonMode; + _.Mode = mode; + break; + default: + setMode(kHaierAcYrw02Auto); // Unexpected, default to auto mode. + } +} + +/// Get the operating mode setting of the A/C. +/// @return The current operating mode setting. +uint8_t IRHaierAC176::getMode(void) const { return _.Mode; } + +/// Set the temperature. +/// @param[in] celsius The temperature in degrees celsius. +void IRHaierAC176::setTemp(const uint8_t celsius) { + uint8_t temp = celsius; + if (temp < kHaierAcMinTemp) + temp = kHaierAcMinTemp; + else if (temp > kHaierAcMaxTemp) + temp = kHaierAcMaxTemp; + + uint8_t old_temp = getTemp(); + if (old_temp == temp) return; + if (old_temp > temp) + _.Button = kHaierAcYrw02ButtonTempDown; + else + _.Button = kHaierAcYrw02ButtonTempUp; + _.Temp = temp - kHaierAcMinTemp; +} + +/// Get the current temperature setting. +/// @return The current setting for temp. in degrees celsius. +uint8_t IRHaierAC176::getTemp(void) const { return _.Temp + kHaierAcMinTemp; } + +/// Set the Health (filter) setting of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRHaierAC176::setHealth(const bool on) { + _.Button = kHaierAcYrw02ButtonHealth; + _.Health = on; +} + +/// Get the Health (filter) setting of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRHaierAC176::getHealth(void) const { return _.Health; } + +/// Get the value of the current power setting. +/// @return true, the setting is on. false, the setting is off. +bool IRHaierAC176::getPower(void) const { return _.Power; } + +/// Change the power setting. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRHaierAC176::setPower(const bool on) { + _.Button = kHaierAcYrw02ButtonPower; + _.Power = on; +} + +/// Change the power setting to On. +void IRHaierAC176::on(void) { setPower(true); } + +/// Change the power setting to Off. +void IRHaierAC176::off(void) { setPower(false); } + +/// Get the Sleep setting of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRHaierAC176::getSleep(void) const { return _.Sleep; } + +/// Set the Sleep setting of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRHaierAC176::setSleep(const bool on) { + _.Button = kHaierAcYrw02ButtonSleep; + _.Sleep = on; +} + +/// Get the Turbo setting of the A/C. +/// @return The current turbo setting. +bool IRHaierAC176::getTurbo(void) const { return _.Turbo; } + +/// Set the Turbo setting of the A/C. +/// @param[in] on The desired turbo setting. +/// @note Turbo & Quiet can't be on at the same time, and only in Heat/Cool mode +void IRHaierAC176::setTurbo(const bool on) { + switch (getMode()) { + case kHaierAcYrw02Cool: + case kHaierAcYrw02Heat: + _.Turbo = on; + _.Button = kHaierAcYrw02ButtonTurbo; + if (on) _.Quiet = false; + } +} + +/// Get the Quiet setting of the A/C. +/// @return The current Quiet setting. +bool IRHaierAC176::getQuiet(void) const { return _.Quiet; } + +/// Set the Quiet setting of the A/C. +/// @param[in] on The desired Quiet setting. +/// @note Turbo & Quiet can't be on at the same time, and only in Heat/Cool mode +void IRHaierAC176::setQuiet(const bool on) { + switch (getMode()) { + case kHaierAcYrw02Cool: + case kHaierAcYrw02Heat: + _.Quiet = on; + _.Button = kHaierAcYrw02ButtonTurbo; + if (on) _.Turbo = false; + } +} + +/// Get the current fan speed setting. +/// @return The current fan speed. +uint8_t IRHaierAC176::getFan(void) const { return _.Fan; } + +/// Set the speed of the fan. +/// @param[in] speed The desired setting. +void IRHaierAC176::setFan(uint8_t speed) { + switch (speed) { + case kHaierAcYrw02FanLow: + case kHaierAcYrw02FanMed: + case kHaierAcYrw02FanHigh: + case kHaierAcYrw02FanAuto: + _.Fan = speed; + _.Fan2 = (speed == kHaierAcYrw02FanAuto) ? 0 : speed; + _.Button = kHaierAcYrw02ButtonFan; + } +} + +/// Get the Vertical Swing position setting of the A/C. +/// @return The native position/mode. +uint8_t IRHaierAC176::getSwing(void) const { return _.Swing; } + +/// Set the Vertical Swing mode of the A/C. +/// @param[in] pos The position/mode to set the vanes to. +void IRHaierAC176::setSwing(uint8_t pos) { + uint8_t newpos = pos; + switch (pos) { + case kHaierAcYrw02SwingOff: + case kHaierAcYrw02SwingAuto: + case kHaierAcYrw02SwingTop: + case kHaierAcYrw02SwingMiddle: + case kHaierAcYrw02SwingBottom: + case kHaierAcYrw02SwingDown: _.Button = kHaierAcYrw02ButtonSwing; break; + default: return; // Unexpected value so don't do anything. + } + // Heat mode has no MIDDLE setting, use BOTTOM instead. + if (pos == kHaierAcYrw02SwingMiddle && _.Mode == kHaierAcYrw02Heat) + newpos = kHaierAcYrw02SwingBottom; + // BOTTOM is only allowed if we are in Heat mode, otherwise MIDDLE. + if (pos == kHaierAcYrw02SwingBottom && _.Mode != kHaierAcYrw02Heat) + newpos = kHaierAcYrw02SwingMiddle; + _.Swing = newpos; +} + + +/// Set the Timer operating mode. +/// @param[in] mode The timer mode to use. +void IRHaierAC176::setTimerMode(const uint8_t mode) { + _.TimerMode = (mode > kHaierAcYrw02OffThenOnTimer) ? kHaierAcYrw02NoTimers + : mode; + switch (_.TimerMode) { + case kHaierAcYrw02NoTimers: + setOnTimer(0); // Disable the On timer. + setOffTimer(0); // Disable the Off timer. + break; + case kHaierAcYrw02OffTimer: + setOnTimer(0); // Disable the On timer. + break; + case kHaierAcYrw02OnTimer: + setOffTimer(0); // Disable the Off timer. + break; + } +} + +/// Get the Timer operating mode. +/// @return The mode of the timer is currently configured to. +uint8_t IRHaierAC176::getTimerMode(void) const { return _.TimerMode; } + +/// Set the number of minutes of the On Timer setting. +/// @param[in] mins Nr. of Minutes for the Timer. `0` means disable the timer. +void IRHaierAC176::setOnTimer(const uint16_t mins) { + const uint16_t nr_mins = std::min((uint16_t)(23 * 60 + 59), mins); + _.OnTimerHrs = nr_mins / 60; + _.OnTimerMins = nr_mins % 60; + + const bool enabled = (nr_mins > 0); + uint8_t mode = getTimerMode(); + switch (mode) { + case kHaierAcYrw02OffTimer: + mode = enabled ? kHaierAcYrw02OffThenOnTimer : mode; + break; + case kHaierAcYrw02OnThenOffTimer: + case kHaierAcYrw02OffThenOnTimer: + mode = enabled ? kHaierAcYrw02OffThenOnTimer : kHaierAcYrw02OffTimer; + break; + default: + // Enable/Disable the On timer for the simple case. + mode = enabled << 1; + } + _.TimerMode = mode; +} + +/// Get the number of minutes of the On Timer setting. +/// @return Nr of minutes. +uint16_t IRHaierAC176::getOnTimer(void) const { + return _.OnTimerHrs * 60 + _.OnTimerMins; +} + +/// Set the number of minutes of the Off Timer setting. +/// @param[in] mins Nr. of Minutes for the Timer. `0` means disable the timer. +void IRHaierAC176::setOffTimer(const uint16_t mins) { + const uint16_t nr_mins = std::min((uint16_t)(23 * 60 + 59), mins); + _.OffTimerHrs = nr_mins / 60; + _.OffTimerMins = nr_mins % 60; + + const bool enabled = (nr_mins > 0); + uint8_t mode = getTimerMode(); + switch (mode) { + case kHaierAcYrw02OnTimer: + mode = enabled ? kHaierAcYrw02OnThenOffTimer : mode; + break; + case kHaierAcYrw02OnThenOffTimer: + case kHaierAcYrw02OffThenOnTimer: + mode = enabled ? kHaierAcYrw02OnThenOffTimer : kHaierAcYrw02OnTimer; + break; + default: + // Enable/Disable the Off timer for the simple case. + mode = enabled; + } + _.TimerMode = mode; +} + +/// Get the number of minutes of the Off Timer setting. +/// @return Nr of minutes. +uint16_t IRHaierAC176::getOffTimer(void) const { + return _.OffTimerHrs * 60 + _.OffTimerMins; +} + +/// 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 IRHaierAC176::convertMode(const stdAc::opmode_t mode) { + switch (mode) { + case stdAc::opmode_t::kCool: return kHaierAcYrw02Cool; + case stdAc::opmode_t::kHeat: return kHaierAcYrw02Heat; + case stdAc::opmode_t::kDry: return kHaierAcYrw02Dry; + case stdAc::opmode_t::kFan: return kHaierAcYrw02Fan; + default: return kHaierAcYrw02Auto; + } +} + +/// 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 IRHaierAC176::convertFan(const stdAc::fanspeed_t speed) { + switch (speed) { + case stdAc::fanspeed_t::kMin: + case stdAc::fanspeed_t::kLow: return kHaierAcYrw02FanLow; + case stdAc::fanspeed_t::kMedium: return kHaierAcYrw02FanMed; + case stdAc::fanspeed_t::kHigh: + case stdAc::fanspeed_t::kMax: return kHaierAcYrw02FanHigh; + default: return kHaierAcYrw02FanAuto; + } +} + +/// 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 IRHaierAC176::convertSwingV(const stdAc::swingv_t position) { + switch (position) { + case stdAc::swingv_t::kHighest: + case stdAc::swingv_t::kHigh: return kHaierAcYrw02SwingTop; + case stdAc::swingv_t::kMiddle: return kHaierAcYrw02SwingMiddle; + case stdAc::swingv_t::kLow: return kHaierAcYrw02SwingDown; + case stdAc::swingv_t::kLowest: return kHaierAcYrw02SwingBottom; + case stdAc::swingv_t::kOff: return kHaierAcYrw02SwingOff; + default: return kHaierAcYrw02SwingAuto; + } +} + +/// 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 IRHaierAC176::toCommonMode(const uint8_t mode) { + switch (mode) { + case kHaierAcYrw02Cool: return stdAc::opmode_t::kCool; + case kHaierAcYrw02Heat: return stdAc::opmode_t::kHeat; + case kHaierAcYrw02Dry: return stdAc::opmode_t::kDry; + case kHaierAcYrw02Fan: 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 IRHaierAC176::toCommonFanSpeed(const uint8_t speed) { + switch (speed) { + case kHaierAcYrw02FanHigh: return stdAc::fanspeed_t::kMax; + case kHaierAcYrw02FanMed: return stdAc::fanspeed_t::kMedium; + case kHaierAcYrw02FanLow: return stdAc::fanspeed_t::kMin; + default: return stdAc::fanspeed_t::kAuto; + } +} + +/// Convert a stdAc::swingv_t enum into it's native setting. +/// @param[in] pos The enum to be converted. +/// @return The native equivalent of the enum. +stdAc::swingv_t IRHaierAC176::toCommonSwingV(const uint8_t pos) { + switch (pos) { + case kHaierAcYrw02SwingTop: return stdAc::swingv_t::kHighest; + case kHaierAcYrw02SwingMiddle: return stdAc::swingv_t::kMiddle; + case kHaierAcYrw02SwingDown: return stdAc::swingv_t::kLow; + case kHaierAcYrw02SwingBottom: return stdAc::swingv_t::kLowest; + case kHaierAcYrw02SwingOff: return stdAc::swingv_t::kOff; + default: return stdAc::swingv_t::kAuto; + } +} + +/// Convert the current internal state into its stdAc::state_t equivalent. +/// @return The stdAc equivalent of the native settings. +stdAc::state_t IRHaierAC176::toCommon(void) const { + stdAc::state_t result; + result.protocol = decode_type_t::HAIER_AC_YRW02; + 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 = toCommonSwingV(_.Swing); + result.filter = _.Health; + result.sleep = _.Sleep ? 0 : -1; + result.turbo = _.Turbo; + result.quiet = _.Quiet; + // Not supported. + result.swingh = stdAc::swingh_t::kOff; + result.econo = false; + result.light = false; + result.clean = false; + result.beep = true; + result.clock = -1; + return result; +} + +/// Convert the current internal state into a human readable string. +/// @return A human readable string. +String IRHaierAC176::toString(void) const { + String result = ""; + result.reserve(130); // Reserve some heap for the string to reduce fragging. + result += addBoolToString(_.Power, kPowerStr, false); + uint8_t cmd = _.Button; + result += addIntToString(cmd, kButtonStr); + result += kSpaceLBraceStr; + switch (cmd) { + case kHaierAcYrw02ButtonPower: + result += kPowerStr; + break; + case kHaierAcYrw02ButtonMode: + result += kModeStr; + break; + case kHaierAcYrw02ButtonFan: + result += kFanStr; + break; + case kHaierAcYrw02ButtonTempUp: + result += kTempUpStr; + break; + case kHaierAcYrw02ButtonTempDown: + result += kTempDownStr; + break; + case kHaierAcYrw02ButtonSleep: + result += kSleepStr; + break; + case kHaierAcYrw02ButtonHealth: + result += kHealthStr; + break; + case kHaierAcYrw02ButtonSwing: + result += kSwingStr; + break; + case kHaierAcYrw02ButtonTurbo: + result += kTurboStr; + break; + default: + result += kUnknownStr; + } + result += ')'; + result += addModeToString(_.Mode, kHaierAcYrw02Auto, kHaierAcYrw02Cool, + kHaierAcYrw02Heat, kHaierAcYrw02Dry, + kHaierAcYrw02Fan); + result += addTempToString(getTemp()); + result += addFanToString(_.Fan, kHaierAcYrw02FanHigh, kHaierAcYrw02FanLow, + kHaierAcYrw02FanAuto, kHaierAcYrw02FanAuto, + kHaierAcYrw02FanMed); + result += addBoolToString(_.Turbo, kTurboStr); + result += addBoolToString(_.Quiet, kQuietStr); + result += addIntToString(_.Swing, kSwingStr); + result += kSpaceLBraceStr; + switch (_.Swing) { + case kHaierAcYrw02SwingOff: + result += kOffStr; + break; + case kHaierAcYrw02SwingAuto: + result += kAutoStr; + break; + case kHaierAcYrw02SwingBottom: + result += kLowestStr; + break; + case kHaierAcYrw02SwingDown: + result += kLowStr; + break; + case kHaierAcYrw02SwingTop: + result += kHighestStr; + break; + case kHaierAcYrw02SwingMiddle: + result += kMiddleStr; + break; + default: + result += kUnknownStr; + } + result += ')'; + result += addBoolToString(_.Sleep, kSleepStr); + result += addBoolToString(_.Health, kHealthStr); + const uint8_t tmode = getTimerMode(); + result += addIntToString(tmode, kTimerModeStr); + result += kSpaceLBraceStr; + switch (tmode) { + case kHaierAcYrw02NoTimers: + result += kNAStr; + break; + case kHaierAcYrw02OnTimer: + result += kOnStr; + break; + case kHaierAcYrw02OffTimer: + result += kOffStr; + break; + case kHaierAcYrw02OnThenOffTimer: + result += kOnStr; + result += '-'; + result += kOffStr; + break; + case kHaierAcYrw02OffThenOnTimer: + result += kOffStr; + result += '-'; + result += kOnStr; + break; + default: + result += kUnknownStr; + } + result += ')'; + result += addLabeledString((tmode != kHaierAcYrw02NoTimers && + tmode != kHaierAcYrw02OffTimer) ? + minsToString(getOnTimer()) : kOffStr, kOnTimerStr); + result += addLabeledString((tmode != kHaierAcYrw02NoTimers && + tmode != kHaierAcYrw02OnTimer) ? + minsToString(getOffTimer()) : kOffStr, kOffTimerStr); + return result; +} +// End of IRHaierAC176 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? +IRHaierACYRW02::IRHaierACYRW02(const uint16_t pin, const bool inverted, + const bool use_modulation) + : IRHaierAC176(pin, inverted, use_modulation) { stateReset(); } + +#if SEND_HAIER_AC_YRW02 +/// Send the current internal state as an IR message. +/// @param[in] repeat Nr. of times the message will be repeated. +void IRHaierACYRW02::send(const uint16_t repeat) { + _irsend.sendHaierACYRW02(getRaw(), kHaierACYRW02StateLength, repeat); +} +#endif // SEND_HAIER_AC_YRW02 + +/// Set the internal state from a valid code for this protocol. +/// @param[in] new_code A valid code for this protocol. +void IRHaierACYRW02::setRaw(const uint8_t new_code[]) { + memcpy(_.raw, new_code, kHaierACYRW02StateLength); +} + +/// 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 IRHaierACYRW02::validChecksum(const uint8_t state[], + const uint16_t length) { + return IRHaierAC176::validChecksum(state, length); +} +// End of IRHaierACYRW02 class. + +#if (DECODE_HAIER_AC || DECODE_HAIER_AC_YRW02) +/// Decode the supplied Haier HSU07-HEA03 remote 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::decodeHaierAC(decode_results* results, uint16_t offset, + const uint16_t nbits, const bool strict) { + if (strict) { + if (nbits != kHaierACBits) + return false; // Not strictly a HAIER_AC message. + } + + if (results->rawlen <= (2 * nbits + kHeader) + kFooter - 1 + offset) + return false; // Can't possibly be a valid HAIER_AC message. + + // Pre-Header + if (!matchMark(results->rawbuf[offset++], kHaierAcHdr)) return false; + if (!matchSpace(results->rawbuf[offset++], kHaierAcHdr)) return false; + + // Match Header + Data + Footer + if (!matchGeneric(results->rawbuf + offset, results->state, + results->rawlen - offset, nbits, + kHaierAcHdr, kHaierAcHdrGap, + kHaierAcBitMark, kHaierAcOneSpace, + kHaierAcBitMark, kHaierAcZeroSpace, + kHaierAcBitMark, kHaierAcMinGap, true, + _tolerance, kMarkExcess)) return false; + + // Compliance + if (strict) { + if (results->state[0] != kHaierAcPrefix) return false; + if (!IRHaierAC::validChecksum(results->state, nbits / 8)) return false; + } + + // Success + results->decode_type = HAIER_AC; + results->bits = nbits; + return true; +} +#endif // (DECODE_HAIER_AC || DECODE_HAIER_AC_YRW02) + +#if DECODE_HAIER_AC_YRW02 +/// Decode the supplied Haier YR-W02 remote A/C 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. +bool IRrecv::decodeHaierACYRW02(decode_results* results, uint16_t offset, + const uint16_t nbits, const bool strict) { + if (strict) { + if (nbits != kHaierACYRW02Bits) + return false; // Not strictly a HAIER_AC_YRW02 message. + } + + // The protocol is almost exactly the same as HAIER_AC + if (!decodeHaierAC(results, offset, nbits, false)) return false; + + // Compliance + if (strict) { + if (results->state[0] != kHaierAcYrw02Prefix) return false; + if (!IRHaierACYRW02::validChecksum(results->state, nbits / 8)) return false; + } + + // Success + // It looks correct, but we haven't check the checksum etc. + results->decode_type = HAIER_AC_YRW02; + return true; +} +#endif // DECODE_HAIER_AC_YRW02 + +#if DECODE_HAIER_AC176 +/// Decode the supplied Haier 176 bit remote A/C 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::decodeHaierAC176(decode_results* results, uint16_t offset, + const uint16_t nbits, const bool strict) { + if (strict) { + if (nbits != kHaierAC176Bits) + return false; // Not strictly a HAIER_AC176 message. + } + + // The protocol is almost exactly the same as HAIER_AC + if (!decodeHaierAC(results, offset, nbits, false)) return false; + + // Compliance + if (strict) { + if (results->state[0] != kHaierAcYrw02Prefix) return false; + if (!IRHaierAC176::validChecksum(results->state, nbits / 8)) return false; + } + + // Success + // It looks correct, but we haven't check the checksum etc. + results->decode_type = HAIER_AC176; + return true; +} +#endif // DECODE_HAIER_AC176 diff --git a/lib/IRremoteESP8266/src/ir_Haier.h b/lib/IRremoteESP8266/src/ir_Haier.h index 98ba74c3a7..6cc31b8906 100644 --- a/lib/IRremoteESP8266/src/ir_Haier.h +++ b/lib/IRremoteESP8266/src/ir_Haier.h @@ -22,10 +22,10 @@ #ifndef UNIT_TEST #include #endif -#include "../src/IRremoteESP8266.h" -#include "../src/IRsend.h" +#include "IRremoteESP8266.h" +#include "IRsend.h" #ifdef UNIT_TEST -#include "../src/IRsend_test.h" +#include "IRsend_test.h" #endif /// Native representation of a Haier HSU07-HEA03 A/C message. diff --git a/lib/IRremoteESP8266/src/ir_Hitachi.cpp b/lib/IRremoteESP8266/src/ir_Hitachi.cpp new file mode 100644 index 0000000000..49781997e1 --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_Hitachi.cpp @@ -0,0 +1,1580 @@ +// Copyright 2018-2019 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 + +#include "ir_Hitachi.h" +#include +#include +#ifndef ARDUINO +#include +#endif +#include "IRrecv.h" +#include "IRremoteESP8266.h" +#include "IRsend.h" +#include "IRtext.h" +#include "IRutils.h" + +// Constants +const uint16_t kHitachiAcHdrMark = 3300; +const uint16_t kHitachiAcHdrSpace = 1700; +const uint16_t kHitachiAc1HdrMark = 3400; +const uint16_t kHitachiAc1HdrSpace = 3400; +const uint16_t kHitachiAcBitMark = 400; +const uint16_t kHitachiAcOneSpace = 1250; +const uint16_t kHitachiAcZeroSpace = 500; +const uint32_t kHitachiAcMinGap = kDefaultMessageGap; // Just a guess. +// Support for HitachiAc424 protocol +const uint16_t kHitachiAc424LdrMark = 29784; // Leader +const uint16_t kHitachiAc424LdrSpace = 49290; // Leader +const uint16_t kHitachiAc424HdrMark = 3416; // Header +const uint16_t kHitachiAc424HdrSpace = 1604; // Header +const uint16_t kHitachiAc424BitMark = 463; +const uint16_t kHitachiAc424OneSpace = 1208; +const uint16_t kHitachiAc424ZeroSpace = 372; + +// Support for HitachiAc3 protocol +const uint16_t kHitachiAc3HdrMark = 3400; // Header +const uint16_t kHitachiAc3HdrSpace = 1660; // Header +const uint16_t kHitachiAc3BitMark = 460; +const uint16_t kHitachiAc3OneSpace = 1250; +const uint16_t kHitachiAc3ZeroSpace = 410; + +using irutils::addBoolToString; +using irutils::addIntToString; +using irutils::addLabeledString; +using irutils::addModeToString; +using irutils::addModelToString; +using irutils::addFanToString; +using irutils::addTempToString; +using irutils::checkInvertedBytePairs; +using irutils::invertBytePairs; +using irutils::minsToString; + +#if (SEND_HITACHI_AC || SEND_HITACHI_AC2 || SEND_HITACHI_AC344) +/// Send a Hitachi 28-byte/224-bit A/C formatted message. (HITACHI_AC) +/// 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. +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/417 +void IRsend::sendHitachiAC(const unsigned char data[], const uint16_t nbytes, + const uint16_t repeat) { + if (nbytes < kHitachiAcStateLength) + return; // Not enough bytes to send a proper message. + + const bool MSBfirst = (nbytes == kHitachiAc344StateLength) ? false : true; + sendGeneric(kHitachiAcHdrMark, kHitachiAcHdrSpace, kHitachiAcBitMark, + kHitachiAcOneSpace, kHitachiAcBitMark, kHitachiAcZeroSpace, + kHitachiAcBitMark, kHitachiAcMinGap, data, nbytes, 38, MSBfirst, + repeat, 50); +} +#endif // (SEND_HITACHI_AC || SEND_HITACHI_AC2 || SEND_HITACHI_AC344) + +#if SEND_HITACHI_AC1 +/// Send a Hitachi 13 byte/224-bit A/C formatted message. (HITACHI_AC1) +/// Status: STABLE / Confirmed 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. +/// @note Basically the same as sendHitatchiAC() except different size & header. +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/453 +void IRsend::sendHitachiAC1(const unsigned char data[], const uint16_t nbytes, + const uint16_t repeat) { + if (nbytes < kHitachiAc1StateLength) + return; // Not enough bytes to send a proper message. + sendGeneric(kHitachiAc1HdrMark, kHitachiAc1HdrSpace, kHitachiAcBitMark, + kHitachiAcOneSpace, kHitachiAcBitMark, kHitachiAcZeroSpace, + kHitachiAcBitMark, kHitachiAcMinGap, data, nbytes, kHitachiAcFreq, + true, repeat, kDutyDefault); +} +#endif // SEND_HITACHI_AC1 + +#if SEND_HITACHI_AC2 +/// Send a Hitachi 53 byte/424-bit A/C formatted message. (HITACHI_AC2) +/// Basically the same as sendHitatchiAC() except different size. +/// Status: STABLE / Expected to work. +/// @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::sendHitachiAC2(const unsigned char data[], const uint16_t nbytes, + const uint16_t repeat) { + if (nbytes < kHitachiAc2StateLength) + return; // Not enough bytes to send a proper message. + sendHitachiAC(data, nbytes, repeat); +} +#endif // SEND_HITACHI_AC2 + +#if SEND_HITACHI_AC344 +/// Send a Hitachi A/C 43-byte/344-bit message. (HITACHI_AC344) +/// Basically the same as sendHitatchiAC() except different size. +/// Status: Beta / Probably works. +/// @param[in] data An array of bytes containing the IR command. +/// @param[in] nbytes Nr. of bytes of data in the array. +/// @param[in] repeat Nr. of times the message is to be repeated. (Default = 0). +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1134 +void IRsend::sendHitachiAc344(const unsigned char data[], const uint16_t nbytes, + const uint16_t repeat) { + if (nbytes < kHitachiAc344StateLength) + return; // Not enough bytes to send a proper message. + sendHitachiAC(data, nbytes, repeat); +} +#endif // SEND_HITACHI_AC344 + +/// 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? +IRHitachiAc::IRHitachiAc(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 IRHitachiAc::stateReset(void) { + _.raw[0] = 0x80; + _.raw[1] = 0x08; + _.raw[2] = 0x0C; + _.raw[3] = 0x02; + _.raw[4] = 0xFD; + _.raw[5] = 0x80; + _.raw[6] = 0x7F; + _.raw[7] = 0x88; + _.raw[8] = 0x48; + _.raw[9] = 0x10; + for (uint8_t i = 10; i < kHitachiAcStateLength; i++) _.raw[i] = 0x00; + _.raw[14] = 0x60; + _.raw[15] = 0x60; + _.raw[24] = 0x80; + setTemp(23); +} + +/// Set up hardware to be able to send a message. +void IRHitachiAc::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 size/length of the state. +/// @return The calculated checksum value. +uint8_t IRHitachiAc::calcChecksum(const uint8_t state[], + const uint16_t length) { + uint8_t sum = 62; + for (uint16_t i = 0; i < length - 1; i++) sum -= reverseBits(state[i], 8); + return reverseBits(sum, 8); +} + +/// Calculate and set the checksum values for the internal state. +/// @param[in] length The size/length of the state. +void IRHitachiAc::checksum(const uint16_t length) { + _.Sum = calcChecksum(_.raw, 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 IRHitachiAc::validChecksum(const uint8_t state[], const uint16_t length) { + if (length < 2) return true; // Assume true for lengths that are too short. + return (state[length - 1] == calcChecksum(state, length)); +} + +/// 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 *IRHitachiAc::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 new_code array. +void IRHitachiAc::setRaw(const uint8_t new_code[], const uint16_t length) { + std::memcpy(_.raw, new_code, std::min(length, kHitachiAcStateLength)); +} + +#if SEND_HITACHI_AC +/// Send the current internal state as an IR message. +/// @param[in] repeat Nr. of times the message will be repeated. +void IRHitachiAc::send(const uint16_t repeat) { + _irsend.sendHitachiAC(getRaw(), kHitachiAcStateLength, repeat); +} +#endif // SEND_HITACHI_AC + +/// Get the value of the current power setting. +/// @return true, the setting is on. false, the setting is off. +bool IRHitachiAc::getPower(void) const { + return _.Power; +} + +/// Change the power setting. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRHitachiAc::setPower(const bool on) { + _.Power = on; +} + +/// Change the power setting to On. +void IRHitachiAc::on(void) { setPower(true); } + +/// Change the power setting to Off. +void IRHitachiAc::off(void) { setPower(false); } + +/// Get the operating mode setting of the A/C. +/// @return The current operating mode setting. +uint8_t IRHitachiAc::getMode(void) const { return reverseBits(_.Mode, 8); } + +/// Set the operating mode of the A/C. +/// @param[in] mode The desired operating mode. +void IRHitachiAc::setMode(const uint8_t mode) { + uint8_t newmode = mode; + switch (mode) { + // Fan mode sets a special temp. + case kHitachiAcFan: setTemp(64); break; + case kHitachiAcAuto: + case kHitachiAcHeat: + case kHitachiAcCool: + case kHitachiAcDry: break; + default: newmode = kHitachiAcAuto; + } + _.Mode = reverseBits(newmode, 8); + if (mode != kHitachiAcFan) setTemp(_previoustemp); + setFan(getFan()); // Reset the fan speed after the mode change. +} + +/// Get the current temperature setting. +/// @return The current setting for temp. in degrees celsius. +uint8_t IRHitachiAc::getTemp(void) const { + return reverseBits(_.Temp, 8) >> 1; +} + +/// Set the temperature. +/// @param[in] celsius The temperature in degrees celsius. +void IRHitachiAc::setTemp(const uint8_t celsius) { + uint8_t temp; + if (celsius != 64) _previoustemp = celsius; + switch (celsius) { + case 64: + temp = celsius; + break; + default: + temp = std::min(celsius, kHitachiAcMaxTemp); + temp = std::max(temp, kHitachiAcMinTemp); + } + _.Temp = reverseBits(temp << 1, 8); + if (temp == kHitachiAcMinTemp) + _.raw[9] = 0x90; + else + _.raw[9] = 0x10; +} + +/// Get the current fan speed setting. +/// @return The current fan speed. +uint8_t IRHitachiAc::getFan(void) const { return reverseBits(_.Fan, 8); } + +/// Set the speed of the fan. +/// @param[in] speed The desired setting. +void IRHitachiAc::setFan(const uint8_t speed) { + uint8_t fanmin = kHitachiAcFanAuto; + uint8_t fanmax = kHitachiAcFanHigh; + switch (getMode()) { + case kHitachiAcDry: // Only 2 x low speeds in Dry mode. + fanmin = kHitachiAcFanLow; + fanmax = kHitachiAcFanLow + 1; + break; + case kHitachiAcFan: + fanmin = kHitachiAcFanLow; // No Auto in Fan mode. + break; + } + uint8_t newspeed = std::max(speed, fanmin); + newspeed = std::min(newspeed, fanmax); + _.Fan = reverseBits(newspeed, 8); +} + +/// Get the Vertical Swing setting of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRHitachiAc::getSwingVertical(void) const { + return _.SwingV; +} + +/// Set the Vertical Swing setting of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRHitachiAc::setSwingVertical(const bool on) { + _.SwingV = on; +} + +/// Get the Horizontal Swing setting of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRHitachiAc::getSwingHorizontal(void) const { + return _.SwingH; +} + +/// Set the Horizontal Swing setting of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRHitachiAc::setSwingHorizontal(const bool on) { + _.SwingH = on; +} + +/// 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 IRHitachiAc::convertMode(const stdAc::opmode_t mode) { + switch (mode) { + case stdAc::opmode_t::kCool: return kHitachiAcCool; + case stdAc::opmode_t::kHeat: return kHitachiAcHeat; + case stdAc::opmode_t::kDry: return kHitachiAcDry; + case stdAc::opmode_t::kFan: return kHitachiAcFan; + default: return kHitachiAcAuto; + } +} + +/// 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 IRHitachiAc::convertFan(const stdAc::fanspeed_t speed) { + switch (speed) { + case stdAc::fanspeed_t::kMin: + case stdAc::fanspeed_t::kLow: return kHitachiAcFanLow; + case stdAc::fanspeed_t::kMedium: return kHitachiAcFanLow + 1; + case stdAc::fanspeed_t::kHigh: return kHitachiAcFanHigh - 1; + case stdAc::fanspeed_t::kMax: return kHitachiAcFanHigh; + default: return kHitachiAcFanAuto; + } +} + +/// 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 IRHitachiAc::toCommonMode(const uint8_t mode) { + switch (mode) { + case kHitachiAcCool: return stdAc::opmode_t::kCool; + case kHitachiAcHeat: return stdAc::opmode_t::kHeat; + case kHitachiAcDry: return stdAc::opmode_t::kDry; + case kHitachiAcFan: 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 IRHitachiAc::toCommonFanSpeed(const uint8_t speed) { + switch (speed) { + case kHitachiAcFanHigh: return stdAc::fanspeed_t::kMax; + case kHitachiAcFanHigh - 1: return stdAc::fanspeed_t::kHigh; + case kHitachiAcFanLow + 1: return stdAc::fanspeed_t::kMedium; + case kHitachiAcFanLow: return stdAc::fanspeed_t::kLow; + 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 IRHitachiAc::toCommon(void) const { + stdAc::state_t result; + result.protocol = decode_type_t::HITACHI_AC; + result.model = -1; // No models used. + result.power = _.Power; + result.mode = toCommonMode(getMode()); + result.celsius = true; + result.degrees = getTemp(); + result.fanspeed = toCommonFanSpeed(getFan()); + result.swingv = (_.SwingV ? stdAc::swingv_t::kAuto : stdAc::swingv_t::kOff); + result.swingh = (_.SwingH ? stdAc::swingh_t::kAuto : stdAc::swingh_t::kOff); + // Not supported. + result.quiet = false; + result.turbo = false; + result.clean = false; + result.econo = false; + result.filter = false; + result.light = 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 IRHitachiAc::toString(void) const { + String result = ""; + result.reserve(110); // Reserve some heap for the string to reduce fragging. + result += addBoolToString(_.Power, kPowerStr, false); + result += addModeToString(getMode(), kHitachiAcAuto, kHitachiAcCool, + kHitachiAcHeat, kHitachiAcDry, kHitachiAcFan); + result += addTempToString(getTemp()); + result += addFanToString(getFan(), kHitachiAcFanHigh, kHitachiAcFanLow, + kHitachiAcFanAuto, kHitachiAcFanAuto, + kHitachiAcFanMed); + result += addBoolToString(_.SwingV, kSwingVStr); + result += addBoolToString(_.SwingH, kSwingHStr); + return result; +} + +/// 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? +IRHitachiAc1::IRHitachiAc1(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 IRHitachiAc1::stateReset(void) { + for (uint8_t i = 0; i < kHitachiAc1StateLength; i++) _.raw[i] = 0x00; + // Copy in a known good state. + _.raw[0] = 0xB2; + _.raw[1] = 0xAE; + _.raw[2] = 0x4D; + _.raw[3] = 0x91; + _.raw[4] = 0xF0; + _.raw[5] = 0xE1; + _.raw[6] = 0xA4; + _.raw[11] = 0x61; + _.raw[12] = 0x24; +} + +/// Set up hardware to be able to send a message. +void IRHitachiAc1::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 size/length of the state. +/// @return The calculated checksum value. +uint8_t IRHitachiAc1::calcChecksum(const uint8_t state[], + const uint16_t length) { + uint8_t sum = 0; + for (uint16_t i = kHitachiAc1ChecksumStartByte; i < length - 1; i++) { + sum += reverseBits(GETBITS8(state[i], kLowNibble, kNibbleSize), + kNibbleSize); + sum += reverseBits(GETBITS8(state[i], kHighNibble, kNibbleSize), + kNibbleSize); + } + return reverseBits(sum, 8); +} + +/// Calculate and set the checksum values for the internal state. +/// @param[in] length The size/length of the state. +void IRHitachiAc1::checksum(const uint16_t length) { + _.Sum = calcChecksum(_.raw, 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 IRHitachiAc1::validChecksum(const uint8_t state[], const uint16_t length) { + if (length < 2) return true; // Assume true for lengths that are too short. + return (state[length - 1] == calcChecksum(state, length)); +} + +/// 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 *IRHitachiAc1::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 new_code array. +void IRHitachiAc1::setRaw(const uint8_t new_code[], const uint16_t length) { + std::memcpy(_.raw, new_code, std::min(length, kHitachiAc1StateLength)); +} + +#if SEND_HITACHI_AC +/// Send the current internal state as an IR message. +/// @param[in] repeat Nr. of times the message will be repeated. +void IRHitachiAc1::send(const uint16_t repeat) { + _irsend.sendHitachiAC1(getRaw(), kHitachiAc1StateLength, repeat); + // Clear the toggle bits as we have actioned them by sending them. + setPowerToggle(false); + setSwingToggle(false); +} +#endif // SEND_HITACHI_AC + +/// Get/Detect the model of the A/C. +/// @return The enum of the compatible model. +hitachi_ac1_remote_model_t IRHitachiAc1::getModel(void) const { + switch (_.Model) { + case kHitachiAc1Model_B: return hitachi_ac1_remote_model_t::R_LT0541_HTA_B; + default: return hitachi_ac1_remote_model_t::R_LT0541_HTA_A; + } +} + +/// Set the model of the A/C to emulate. +/// @param[in] model The enum of the appropriate model. +void IRHitachiAc1::setModel(const hitachi_ac1_remote_model_t model) { + uint8_t value = 0; + switch (model) { + case hitachi_ac1_remote_model_t::R_LT0541_HTA_B: + value = kHitachiAc1Model_B; + break; + default: + value = kHitachiAc1Model_A; // i.e. 'A' mode. + } + _.Model = value; +} + +/// Get the value of the current power setting. +/// @return true, the setting is on. false, the setting is off. +bool IRHitachiAc1::getPower(void) const { + return _.Power; +} + +/// Change the power setting. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRHitachiAc1::setPower(const bool on) { + // If the power changes, set the power toggle bit. + if (on != _.Power) setPowerToggle(true); + _.Power = on; +} + +/// Get the value of the current power toggle setting. +/// @return true, the setting is on. false, the setting is off. +bool IRHitachiAc1::getPowerToggle(void) const { + return _.PowerToggle; +} + +/// Change the power toggle setting. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRHitachiAc1::setPowerToggle(const bool on) { + _.PowerToggle = on; +} + +/// Change the power setting to On. +void IRHitachiAc1::on(void) { setPower(true); } + +/// Change the power setting to Off. +void IRHitachiAc1::off(void) { setPower(false); } + +/// Get the operating mode setting of the A/C. +/// @return The current operating mode setting. +uint8_t IRHitachiAc1::getMode(void) const { + return _.Mode; +} + +/// Set the operating mode of the A/C. +/// @param[in] mode The desired operating mode. +void IRHitachiAc1::setMode(const uint8_t mode) { + switch (mode) { + case kHitachiAc1Auto: + setTemp(kHitachiAc1TempAuto); + // FALL THRU + case kHitachiAc1Fan: + case kHitachiAc1Heat: + case kHitachiAc1Cool: + case kHitachiAc1Dry: + _.Mode = mode; + break; + default: + setTemp(kHitachiAc1TempAuto); + _.Mode = kHitachiAc1Auto; + break; + } + setSleep(_.Sleep); // Correct the sleep mode if required. + setFan(_.Fan); // Correct the fan speed if required. +} + +/// Get the current temperature setting. +/// @return The current setting for temp. in degrees celsius. +uint8_t IRHitachiAc1::getTemp(void) const { + return reverseBits(_.Temp, kHitachiAc1TempSize) + kHitachiAc1TempDelta; +} + +/// Set the temperature. +/// @param[in] celsius The temperature in degrees celsius. +void IRHitachiAc1::setTemp(const uint8_t celsius) { + if (_.Mode == kHitachiAc1Auto) return; // Can't change temp in Auto mode. + uint8_t temp = std::min(celsius, kHitachiAcMaxTemp); + temp = std::max(temp, kHitachiAcMinTemp); + temp -= kHitachiAc1TempDelta; + temp = reverseBits(temp, kHitachiAc1TempSize); + _.Temp = temp; +} + +/// Get the current fan speed setting. +/// @return The current fan speed. +uint8_t IRHitachiAc1::getFan(void) const { + return _.Fan; +} + +/// Set the speed of the fan. +/// @param[in] speed The desired setting. +/// @param[in] force Deprecated +void IRHitachiAc1::setFan(const uint8_t speed, const bool /*force*/) { + // restrictions + switch (_.Mode) { + case kHitachiAc1Dry: + _.Fan = kHitachiAc1FanLow; // Dry is locked to Low speed. + return; + case kHitachiAc1Auto: + _.Fan = kHitachiAc1FanAuto; // Auto is locked to Auto speed. + return; + case kHitachiAc1Heat: + case kHitachiAc1Fan: // Auto speed not allowed in these modes. + if (speed == kHitachiAc1FanAuto || _.Fan == kHitachiAc1FanAuto) + _.Fan = kHitachiAc1FanLow; + return; + } + + switch (speed) { + case kHitachiAc1FanAuto: + case kHitachiAc1FanHigh: + case kHitachiAc1FanMed: + case kHitachiAc1FanLow: + _.Fan = speed; + break; + default: _.Fan = kHitachiAc1FanAuto; + } +} + +/// Get the Swing Toggle setting of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRHitachiAc1::getSwingToggle(void) const { + return _.SwingToggle; +} + +/// Set the Swing toggle setting of the A/C. +/// @param[in] toggle true, the setting is on. false, the setting is off. +void IRHitachiAc1::setSwingToggle(const bool toggle) { + _.SwingToggle = toggle; +} + +/// Get the Vertical Swing setting of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRHitachiAc1::getSwingV(void) const { + return _.SwingV; +} + +/// Set the Vertical Swing setting of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRHitachiAc1::setSwingV(const bool on) { + _.SwingV = on; +} + +/// Get the Horizontal Swing setting of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRHitachiAc1::getSwingH(void) const { + return _.SwingH; +} + +/// Set the Horizontal Swing setting of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRHitachiAc1::setSwingH(const bool on) { + _.SwingH = on; +} + +/// Get the Sleep setting of the A/C. +/// @return The currently configured sleep mode. +/// @note Sleep modes only available in Auto & Cool modes, otherwise it's off. +uint8_t IRHitachiAc1::getSleep(void) const { + return _.Sleep; +} + +/// Set the Sleep setting of the A/C. +/// @param[in] mode The mode of sleep to set the A/C to. +/// @note Sleep modes only available in Auto & Cool modes, otherwise it's off. +void IRHitachiAc1::setSleep(const uint8_t mode) { + switch (_.Mode) { + case kHitachiAc1Auto: + case kHitachiAc1Cool: + _.Sleep = std::min(mode, kHitachiAc1Sleep4); + break; + default: + _.Sleep = kHitachiAc1SleepOff; + } +} + +/// Set the On Timer time. +/// @param[in] mins The time expressed in total number of minutes. +void IRHitachiAc1::setOnTimer(const uint16_t mins) { + const uint16_t mins_lsb = reverseBits(mins, kHitachiAc1TimerSize); + _.OnTimerLow = GETBITS16(mins_lsb, 8, 8); + _.OnTimerHigh = GETBITS16(mins_lsb, 0, 8); +} + +/// Get the On Timer vtime of the A/C. +/// @return Nr of minutes the timer is set to. +uint16_t IRHitachiAc1::getOnTimer(void) const { + return reverseBits( + (_.OnTimerLow << 8) | _.OnTimerHigh, kHitachiAc1TimerSize); +} + +/// Set the Off Timer time. +/// @param[in] mins The time expressed in total number of minutes. +void IRHitachiAc1::setOffTimer(const uint16_t mins) { + const uint16_t mins_lsb = reverseBits(mins, kHitachiAc1TimerSize); + _.OffTimerLow = GETBITS16(mins_lsb, 8, 8); + _.OffTimerHigh = GETBITS16(mins_lsb, 0, 8); +} + +/// Get the Off Timer vtime of the A/C. +/// @return Nr of minutes the timer is set to. +uint16_t IRHitachiAc1::getOffTimer(void) const { + return reverseBits( + (_.OffTimerLow << 8) | _.OffTimerHigh, kHitachiAc1TimerSize); +} + +/// 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 IRHitachiAc1::convertMode(const stdAc::opmode_t mode) { + switch (mode) { + case stdAc::opmode_t::kCool: return kHitachiAc1Cool; + case stdAc::opmode_t::kHeat: return kHitachiAc1Heat; + case stdAc::opmode_t::kDry: return kHitachiAc1Dry; + case stdAc::opmode_t::kFan: return kHitachiAc1Fan; + default: return kHitachiAc1Auto; + } +} + +/// 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 IRHitachiAc1::convertFan(const stdAc::fanspeed_t speed) { + switch (speed) { + case stdAc::fanspeed_t::kMin: + case stdAc::fanspeed_t::kLow: return kHitachiAc1FanLow; + case stdAc::fanspeed_t::kMedium: return kHitachiAc1FanMed; + case stdAc::fanspeed_t::kHigh: + case stdAc::fanspeed_t::kMax: return kHitachiAc1FanHigh; + default: return kHitachiAc1FanAuto; + } +} + +/// 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 IRHitachiAc1::toCommonMode(const uint8_t mode) { + switch (mode) { + case kHitachiAc1Cool: return stdAc::opmode_t::kCool; + case kHitachiAc1Heat: return stdAc::opmode_t::kHeat; + case kHitachiAc1Dry: return stdAc::opmode_t::kDry; + case kHitachiAc1Fan: 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 IRHitachiAc1::toCommonFanSpeed(const uint8_t speed) { + switch (speed) { + case kHitachiAc1FanHigh: return stdAc::fanspeed_t::kMax; + case kHitachiAc1FanMed: return stdAc::fanspeed_t::kMedium; + case kHitachiAc1FanLow: return stdAc::fanspeed_t::kLow; + 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 IRHitachiAc1::toCommon(void) const { + stdAc::state_t result; + result.protocol = decode_type_t::HITACHI_AC1; + result.model = getModel(); + 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.sleep = _.Sleep ? 0 : -1; + // Not supported. + result.quiet = false; + result.turbo = false; + result.clean = false; + result.econo = false; + result.filter = false; + result.light = 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 IRHitachiAc1::toString(void) const { + String result = ""; + result.reserve(170); // Reserve some heap for the string to reduce fragging. + result += addModelToString(decode_type_t::HITACHI_AC1, getModel(), false); + result += addBoolToString(_.Power, kPowerStr); + result += addBoolToString(_.PowerToggle, kPowerToggleStr); + result += addModeToString(_.Mode, kHitachiAc1Auto, kHitachiAc1Cool, + kHitachiAc1Heat, kHitachiAc1Dry, kHitachiAc1Fan); + result += addTempToString(getTemp()); + result += addFanToString(_.Fan, kHitachiAc1FanHigh, kHitachiAc1FanLow, + kHitachiAc1FanAuto, kHitachiAc1FanAuto, + kHitachiAc1FanMed); + result += addBoolToString(_.SwingToggle, kSwingVToggleStr); + result += addBoolToString(_.SwingV, kSwingVStr); + result += addBoolToString(_.SwingH, kSwingHStr); + result += addLabeledString(_.Sleep ? uint64ToString(_.Sleep) : kOffStr, + kSleepStr); + result += addLabeledString(getOnTimer() ? minsToString(getOnTimer()) + : kOffStr, + kOnTimerStr); + result += addLabeledString(getOffTimer() ? minsToString(getOffTimer()) + : kOffStr, + kOffTimerStr); + return result; +} + +#if (DECODE_HITACHI_AC || DECODE_HITACHI_AC1 || DECODE_HITACHI_AC2 || \ + DECODE_HITACHI_AC344) +/// Decode the supplied Hitachi A/C message. +/// Status: STABLE / Expected 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. +/// Typically kHitachiAcBits, kHitachiAc1Bits, kHitachiAc2Bits, +/// kHitachiAc344Bits +/// @param[in] strict Flag indicating if we should perform strict matching. +/// @param[in] MSBfirst Is the data per byte stored in MSB First (true) or +/// LSB First order(false)? +/// @return True if it can decode it, false if it can't. +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/417 +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/453 +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1134 +bool IRrecv::decodeHitachiAC(decode_results *results, uint16_t offset, + const uint16_t nbits, const bool strict, + const bool MSBfirst) { + const uint8_t k_tolerance = _tolerance + 5; + + if (strict) { + switch (nbits) { + case kHitachiAcBits: + case kHitachiAc1Bits: + case kHitachiAc2Bits: + case kHitachiAc344Bits: + break; // Okay to continue. + default: + return false; // Not strictly a Hitachi message. + } + } + uint16_t hmark; + uint32_t hspace; + if (nbits == kHitachiAc1Bits) { + hmark = kHitachiAc1HdrMark; + hspace = kHitachiAc1HdrSpace; + } else { + hmark = kHitachiAcHdrMark; + hspace = kHitachiAcHdrSpace; + } + // Match Header + Data + Footer + if (!matchGeneric(results->rawbuf + offset, results->state, + results->rawlen - offset, nbits, + hmark, hspace, + kHitachiAcBitMark, kHitachiAcOneSpace, + kHitachiAcBitMark, kHitachiAcZeroSpace, + kHitachiAcBitMark, kHitachiAcMinGap, true, + k_tolerance, kMarkExcess, MSBfirst)) return false; + + // Compliance + if (strict) { + if (nbits / 8 == kHitachiAcStateLength && + !IRHitachiAc::validChecksum(results->state, kHitachiAcStateLength)) + return false; + if (nbits / 8 == kHitachiAc1StateLength && + !IRHitachiAc1::validChecksum(results->state, kHitachiAc1StateLength)) + return false; + if (nbits / 8 == kHitachiAc344StateLength && + !IRHitachiAc3::hasInvertedStates(results->state, + kHitachiAc344StateLength)) + return false; + } + + // Success + switch (nbits) { + case kHitachiAc1Bits: + results->decode_type = decode_type_t::HITACHI_AC1; + break; + case kHitachiAc2Bits: + results->decode_type = decode_type_t::HITACHI_AC2; + break; + case kHitachiAc344Bits: + results->decode_type = decode_type_t::HITACHI_AC344; + break; + case kHitachiAcBits: + default: + results->decode_type = decode_type_t::HITACHI_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_HITACHI_AC || DECODE_HITACHI_AC1 || DECODE_HITACHI_AC2 || + // DECODE_HITACHI_AC344) + +#if SEND_HITACHI_AC424 +/// Send a Hitachi 53-byte/424-bit A/C formatted message. (HITACHI_AC424) +/// 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. +/// @note This protocol is almost exactly the same as HitachiAC2 except this +/// variant has a leader section as well, and subtle timing differences. +/// It is also in LSBF order (per byte), rather than MSBF order. +void IRsend::sendHitachiAc424(const uint8_t data[], const uint16_t nbytes, + const uint16_t repeat) { + enableIROut(kHitachiAcFreq); + for (uint16_t r = 0; r <= repeat; r++) { + // Leader + mark(kHitachiAc424LdrMark); + space(kHitachiAc424LdrSpace); + // Header + Data + Footer + sendGeneric(kHitachiAc424HdrMark, kHitachiAc424HdrSpace, + kHitachiAc424BitMark, kHitachiAc424OneSpace, + kHitachiAc424BitMark, kHitachiAc424ZeroSpace, + kHitachiAc424BitMark, kHitachiAcMinGap, + data, nbytes, // Bytes + kHitachiAcFreq, false, kNoRepeat, kDutyDefault); + } +} +#endif // SEND_HITACHI_AC424 + +#if DECODE_HITACHI_AC424 +/// Decode the supplied Hitachi 53-byte/424-bit A/C message. +/// Status: STABLE / Reported as 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 This protocol is almost exactly the same as HitachiAC2 except this +/// variant has a leader section as well, and subtle timing differences. +/// It is also in LSBF order (per byte), rather than MSBF order. +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/973 +/// @see (Japanese Manual) https://kadenfan.hitachi.co.jp/support/raj/item/docs/ras_aj22h_a_tori.pdf +bool IRrecv::decodeHitachiAc424(decode_results *results, uint16_t offset, + const uint16_t nbits, + const bool strict) { + if (results->rawlen < 2 * nbits + kHeader + kHeader + kFooter - 1 + offset) + return false; // Too short a message to match. + if (strict && nbits != kHitachiAc424Bits) + return false; + + uint16_t used; + + // Leader + if (!matchMark(results->rawbuf[offset++], kHitachiAc424LdrMark)) + return false; + if (!matchSpace(results->rawbuf[offset++], kHitachiAc424LdrSpace)) + return false; + + // Header + Data + Footer + used = matchGeneric(results->rawbuf + offset, results->state, + results->rawlen - offset, nbits, + kHitachiAc424HdrMark, kHitachiAc424HdrSpace, + kHitachiAc424BitMark, kHitachiAc424OneSpace, + kHitachiAc424BitMark, kHitachiAc424ZeroSpace, + kHitachiAc424BitMark, kHitachiAcMinGap, true, + kUseDefTol, 0, false); + if (used == 0) return false; // We failed to find any data. + + // Success + results->decode_type = decode_type_t::HITACHI_AC424; + results->bits = nbits; + return true; +} +#endif // DECODE_HITACHI_AC424 + +/// 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? +IRHitachiAc424::IRHitachiAc424(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 Reset to auto fan, cooling, 23° Celsius +void IRHitachiAc424::stateReset(void) { + for (uint8_t i = 0; i < kHitachiAc424StateLength; i++) + _.raw[i] = 0x00; + + _.raw[0] = 0x01; + _.raw[1] = 0x10; + _.raw[3] = 0x40; + _.raw[5] = 0xFF; + _.raw[7] = 0xCC; + _.raw[33] = 0x80; + _.raw[35] = 0x03; + _.raw[37] = 0x01; + _.raw[39] = 0x88; + _.raw[45] = 0xFF; + _.raw[47] = 0xFF; + _.raw[49] = 0xFF; + _.raw[51] = 0xFF; + + setTemp(23); + setPower(true); + setMode(kHitachiAc424Cool); + setFan(kHitachiAc424FanAuto); +} + +/// Update the internal consistency check for the protocol. +void IRHitachiAc424::setInvertedStates(void) { + invertBytePairs(_.raw + 3, kHitachiAc424StateLength - 3); +} + +/// Set up hardware to be able to send a message. +void IRHitachiAc424::begin(void) { _irsend.begin(); } + +/// 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 *IRHitachiAc424::getRaw(void) { + setInvertedStates(); + 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 new_code array. +void IRHitachiAc424::setRaw(const uint8_t new_code[], const uint16_t length) { + std::memcpy(_.raw, new_code, std::min(length, kHitachiAc424StateLength)); +} + +#if SEND_HITACHI_AC424 +/// Send the current internal state as an IR message. +/// @param[in] repeat Nr. of times the message will be repeated. +void IRHitachiAc424::send(const uint16_t repeat) { + _irsend.sendHitachiAc424(getRaw(), kHitachiAc424StateLength, repeat); +} +#endif // SEND_HITACHI_AC424 + +/// Get the value of the current power setting. +/// @return true, the setting is on. false, the setting is off. +bool IRHitachiAc424::getPower(void) const { + return _.Power == kHitachiAc424PowerOn; +} + +/// Change the power setting. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRHitachiAc424::setPower(const bool on) { + setButton(kHitachiAc424ButtonPowerMode); + _.Power = (on ? kHitachiAc424PowerOn : kHitachiAc424PowerOff); +} + +/// Change the power setting to On. +void IRHitachiAc424::on(void) { setPower(true); } + +/// Change the power setting to Off. +void IRHitachiAc424::off(void) { setPower(false); } + +/// Get the operating mode setting of the A/C. +/// @return The current operating mode setting. +uint8_t IRHitachiAc424::getMode(void) const { + return _.Mode; +} + +/// Set the operating mode of the A/C. +/// @param[in] mode The desired operating mode. +void IRHitachiAc424::setMode(const uint8_t mode) { + uint8_t newMode = mode; + switch (mode) { + // Fan mode sets a special temp. + case kHitachiAc424Fan: setTemp(kHitachiAc424FanTemp, false); break; + case kHitachiAc424Heat: + case kHitachiAc424Cool: + case kHitachiAc424Dry: break; + default: newMode = kHitachiAc424Cool; + } + _.Mode = newMode; + if (newMode != kHitachiAc424Fan) setTemp(_previoustemp); + setFan(_.Fan); // Reset the fan speed after the mode change. + setButton(kHitachiAc424ButtonPowerMode); +} + +/// Get the current temperature setting. +/// @return The current setting for temp. in degrees celsius. +uint8_t IRHitachiAc424::getTemp(void) const { + return _.Temp; +} + +/// Set the temperature. +/// @param[in] celsius The temperature in degrees celsius. +/// @param[in] setPrevious true, remember this if we change mode. false, don't. +void IRHitachiAc424::setTemp(const uint8_t celsius, bool setPrevious) { + uint8_t temp; + temp = std::min(celsius, kHitachiAc424MaxTemp); + temp = std::max(temp, kHitachiAc424MinTemp); + _.Temp = temp; + if (_previoustemp > temp) + setButton(kHitachiAc424ButtonTempDown); + else if (_previoustemp < temp) + setButton(kHitachiAc424ButtonTempUp); + if (setPrevious) _previoustemp = temp; +} + +/// Get the current fan speed setting. +/// @return The current fan speed. +uint8_t IRHitachiAc424::getFan(void) const { + return _.Fan; +} + +/// Set the speed of the fan. +/// @param[in] speed The desired setting. +void IRHitachiAc424::setFan(const uint8_t speed) { + uint8_t newSpeed = std::max(speed, kHitachiAc424FanMin); + uint8_t fanMax = kHitachiAc424FanMax; + + // Only 2 x low speeds in Dry mode or Auto + if (_.Mode == kHitachiAc424Dry && speed == kHitachiAc424FanAuto) { + fanMax = kHitachiAc424FanAuto; + } else if (_.Mode == kHitachiAc424Dry) { + fanMax = kHitachiAc424FanMaxDry; + } else if (_.Mode == kHitachiAc424Fan && speed == kHitachiAc424FanAuto) { + // Fan Mode does not have auto. Set to safe low + newSpeed = kHitachiAc424FanMin; + } + + newSpeed = std::min(newSpeed, fanMax); + // Handle the setting the button value if we are going to change the value. + if (newSpeed != _.Fan) setButton(kHitachiAc424ButtonFan); + // Set the values + _.Fan = newSpeed; + _.raw[9] = 0x92; + _.raw[29] = 0x00; + + // When fan is at min/max, additional bytes seem to be set + if (newSpeed == kHitachiAc424FanMin) _.raw[9] = 0x98; + if (newSpeed == kHitachiAc424FanMax) { + _.raw[9] = 0xA9; + _.raw[29] = 0x30; + } +} + +/// Get the Button/Command setting of the A/C. +/// @return The value of the button/command that was pressed. +uint8_t IRHitachiAc424::getButton(void) const { + return _.Button; +} + +/// Set the Button/Command pressed setting of the A/C. +/// @param[in] button The value of the button/command that was pressed. +void IRHitachiAc424::setButton(const uint8_t button) { + _.Button = button; +} + +/// Set the Vertical Swing toggle setting of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +/// @note The remote does not keep state of the vertical swing. +/// A byte is sent indicating the swing button is pressed on the remote +void IRHitachiAc424::setSwingVToggle(const bool on) { + uint8_t button = _.Button; // Get the current button value. + if (on) + button = kHitachiAc424ButtonSwingV; // Set the button to SwingV. + else if (button == kHitachiAc424ButtonSwingV) // Asked to unset it + // It was set previous, so use Power as a default + button = kHitachiAc424ButtonPowerMode; + setButton(button); +} + +/// Get the Vertical Swing toggle setting of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRHitachiAc424::getSwingVToggle(void) const { + return _.Button == kHitachiAc424ButtonSwingV; +} + +/// 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 IRHitachiAc424::convertMode(const stdAc::opmode_t mode) { + switch (mode) { + case stdAc::opmode_t::kCool: return kHitachiAc424Cool; + case stdAc::opmode_t::kHeat: return kHitachiAc424Heat; + case stdAc::opmode_t::kDry: return kHitachiAc424Dry; + case stdAc::opmode_t::kFan: return kHitachiAc424Fan; + default: return kHitachiAc424Cool; + } +} + +/// 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 IRHitachiAc424::convertFan(const stdAc::fanspeed_t speed) { + switch (speed) { + case stdAc::fanspeed_t::kMin: return kHitachiAc424FanMin; + case stdAc::fanspeed_t::kLow: return kHitachiAc424FanLow; + case stdAc::fanspeed_t::kMedium: return kHitachiAc424FanMedium; + case stdAc::fanspeed_t::kHigh: return kHitachiAc424FanHigh; + case stdAc::fanspeed_t::kMax: return kHitachiAc424FanMax; + default: return kHitachiAc424FanAuto; + } +} + +/// 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 IRHitachiAc424::toCommonMode(const uint8_t mode) { + switch (mode) { + case kHitachiAc424Cool: return stdAc::opmode_t::kCool; + case kHitachiAc424Heat: return stdAc::opmode_t::kHeat; + case kHitachiAc424Dry: return stdAc::opmode_t::kDry; + case kHitachiAc424Fan: 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. +/// @return The stdAc equivalent of the native setting. +stdAc::fanspeed_t IRHitachiAc424::toCommonFanSpeed(const uint8_t speed) { + switch (speed) { + case kHitachiAc424FanMax: return stdAc::fanspeed_t::kMax; + case kHitachiAc424FanHigh: return stdAc::fanspeed_t::kHigh; + case kHitachiAc424FanMedium: return stdAc::fanspeed_t::kMedium; + case kHitachiAc424FanLow: return stdAc::fanspeed_t::kLow; + case kHitachiAc424FanMin: 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 IRHitachiAc424::toCommon(void) const { + stdAc::state_t result; + result.protocol = decode_type_t::HITACHI_AC424; + result.model = -1; // No models used. + result.power = getPower(); + result.mode = toCommonMode(_.Mode); + result.celsius = true; + result.degrees = _.Temp; + result.fanspeed = toCommonFanSpeed(_.Fan); + result.swingv = getSwingVToggle() ? stdAc::swingv_t::kAuto + : stdAc::swingv_t::kOff; + // Not supported. + result.swingh = stdAc::swingh_t::kOff; + result.quiet = false; + result.turbo = false; + result.clean = false; + result.econo = false; + result.filter = false; + result.light = false; + result.beep = false; + result.sleep = -1; + result.clock = -1; + return result; +} + +/// Convert the internal state into a human readable string for the settings +/// that are common to protocols of this nature. +/// @return A string containing the common settings in human-readable form. +String IRHitachiAc424::_toString(void) const { + String result = ""; + result.reserve(100); // Reserve some heap for the string to reduce fragging. + result += addBoolToString(getPower(), kPowerStr, false); + result += addModeToString(_.Mode, 0, kHitachiAc424Cool, + kHitachiAc424Heat, kHitachiAc424Dry, + kHitachiAc424Fan); + result += addTempToString(_.Temp); + result += addIntToString(_.Fan, kFanStr); + result += kSpaceLBraceStr; + switch (_.Fan) { + case kHitachiAc424FanAuto: result += kAutoStr; break; + case kHitachiAc424FanMax: result += kMaxStr; break; + case kHitachiAc424FanHigh: result += kHighStr; break; + case kHitachiAc424FanMedium: result += kMedStr; break; + case kHitachiAc424FanLow: result += kLowStr; break; + case kHitachiAc424FanMin: result += kMinStr; break; + default: result += kUnknownStr; + } + result += ')'; + result += addIntToString(_.Button, kButtonStr); + result += kSpaceLBraceStr; + switch (_.Button) { + case kHitachiAc424ButtonPowerMode: + result += kPowerStr; + result += '/'; + result += kModeStr; + break; + case kHitachiAc424ButtonFan: result += kFanStr; break; + case kHitachiAc424ButtonSwingV: result += kSwingVStr; break; + case kHitachiAc344ButtonSwingH: result += kSwingHStr; break; + case kHitachiAc424ButtonTempDown: result += kTempDownStr; break; + case kHitachiAc424ButtonTempUp: result += kTempUpStr; break; + default: result += kUnknownStr; + } + result += ')'; + return result; +} + +/// Convert the internal state into a human readable string. +/// @return A string containing the settings in human-readable form. +String IRHitachiAc424::toString(void) const { + return _toString() + addBoolToString(getSwingVToggle(), kSwingVToggleStr); +} + + +#if SEND_HITACHI_AC3 +/// Send a Hitachi(3) A/C formatted message. (HITACHI_AC3) +/// Status: STABLE / Working fine. +/// @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 almost exactly the same as HitachiAC424 except this +/// variant has subtle timing differences. There are five(5) typical sizes: +/// kHitachiAc3MinStateLength (Cancel Timer), +/// kHitachiAc3MinStateLength + 2 (Change Temp), +/// kHitachiAc3StateLength - 6 (Change Mode), +/// kHitachiAc3StateLength - 4 (Normal), & +/// kHitachiAc3StateLength (Set Timer) +void IRsend::sendHitachiAc3(const uint8_t data[], const uint16_t nbytes, + const uint16_t repeat) { + // Header + Data + Footer + sendGeneric(kHitachiAc3HdrMark, kHitachiAc3HdrSpace, + kHitachiAc3BitMark, kHitachiAc3OneSpace, + kHitachiAc3BitMark, kHitachiAc3ZeroSpace, + kHitachiAc3BitMark, kHitachiAcMinGap, + data, nbytes, // Bytes + kHitachiAcFreq, false, repeat, kDutyDefault); +} +#endif // SEND_HITACHI_AC3 + + +/// 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? +IRHitachiAc3::IRHitachiAc3(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 Reset to auto fan, cooling, 23° Celsius +void IRHitachiAc3::stateReset(void) { + for (uint8_t i = 0; i < kHitachiAc3StateLength; i++) + remote_state[i] = 0x00; + remote_state[0] = 0x01; + remote_state[1] = 0x10; + remote_state[3] = 0x40; + remote_state[5] = 0xFF; + remote_state[7] = 0xE8; + remote_state[9] = 0x89; + remote_state[11] = 0x0B; + remote_state[13] = 0x3F; + remote_state[15] = 0x15; + remote_state[21] = 0x4B; + remote_state[23] = 0x18; + setInvertedStates(); +} + +/// Invert every second byte of the internal state, after the fixed header. +/// @param[in] length The size of the state array. +/// @note This is this protocols integrity check. +void IRHitachiAc3::setInvertedStates(const uint16_t length) { + if (length > 3) invertBytePairs(remote_state + 3, length - 3); +} + +/// Check if every second byte of the state, after the fixed header +/// is inverted to the previous byte. +/// @param[in] state The state array to be checked. +/// @param[in] length The size of the state array. +/// @note This is this protocols integrity check. +bool IRHitachiAc3::hasInvertedStates(const uint8_t state[], + const uint16_t length) { + return (length <= 3 || checkInvertedBytePairs(state + 3, length - 3)); +} + +/// Set up hardware to be able to send a message. +void IRHitachiAc3::begin(void) { _irsend.begin(); } + +/// 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 *IRHitachiAc3::getRaw(void) { + setInvertedStates(); + return remote_state; +} + +/// 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 new_code array. +void IRHitachiAc3::setRaw(const uint8_t new_code[], const uint16_t length) { + memcpy(remote_state, new_code, std::min(length, kHitachiAc3StateLength)); +} + +#if DECODE_HITACHI_AC3 +/// Decode the supplied Hitachi 15to27-byte/120to216-bit A/C message. +/// Status: STABLE / Works fine. +/// @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 almost exactly the same as HitachiAC424 except this +/// variant has subtle timing differences and multiple lengths. +/// There are five(5) typical lengths: +/// kHitachiAc3MinStateLength (Cancel Timer), +/// kHitachiAc3MinStateLength + 2 (Change Temp), +/// kHitachiAc3StateLength - 6 (Change Mode), +/// kHitachiAc3StateLength - 4 (Normal), & +/// kHitachiAc3StateLength (Set Timer) +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1060 +bool IRrecv::decodeHitachiAc3(decode_results *results, uint16_t offset, + const uint16_t nbits, + const bool strict) { + if (results->rawlen < 2 * nbits + kHeader + kFooter - 1 + offset) + return false; // Too short a message to match. + if (strict) { + // Check the requested bit length. + switch (nbits) { + case kHitachiAc3MinBits: // Cancel Timer (Min Size) + case kHitachiAc3MinBits + 2 * 8: // Change Temp + case kHitachiAc3Bits - 6 * 8: // Change Mode + case kHitachiAc3Bits - 4 * 8: // Normal + case kHitachiAc3Bits: // Set Temp (Max Size) + break; + default: return false; + } + } + + // Header + Data + Footer + if (!matchGeneric(results->rawbuf + offset, results->state, + results->rawlen - offset, nbits, + kHitachiAc3HdrMark, kHitachiAc3HdrSpace, + kHitachiAc3BitMark, kHitachiAc3OneSpace, + kHitachiAc3BitMark, kHitachiAc3ZeroSpace, + kHitachiAc3BitMark, kHitachiAcMinGap, true, + kUseDefTol, 0, false)) + return false; // We failed to find any data. + + // Compliance + if (strict && !IRHitachiAc3::hasInvertedStates(results->state, nbits / 8)) + return false; + // Success + results->decode_type = decode_type_t::HITACHI_AC3; + results->bits = nbits; + return true; +} +#endif // DECODE_HITACHI_AC3 + +/// Class constructor for handling detailed Hitachi_AC344 43 byte 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? +IRHitachiAc344::IRHitachiAc344(const uint16_t pin, const bool inverted, + const bool use_modulation) + : IRHitachiAc424(pin, inverted, use_modulation) { stateReset(); } + +/// Reset the internal state to auto fan, cooling, 23° Celsius +void IRHitachiAc344::stateReset(void) { + IRHitachiAc424::stateReset(); + _.raw[37] = 0x00; + _.raw[39] = 0x00; +} + +#if SEND_HITACHI_AC344 +/// Create and send the IR message to the A/C. +/// @param[in] repeat Nr. of times to repeat the message. +void IRHitachiAc344::send(const uint16_t repeat) { + _irsend.sendHitachiAc344(getRaw(), kHitachiAc344StateLength, repeat); +} +#endif // SEND_HITACHI_AC344 + +/// 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 (in bytes) of the code for this protocol. +void IRHitachiAc344::setRaw(const uint8_t new_code[], const uint16_t length) { + memcpy(_.raw, new_code, std::min(length, kHitachiAc344StateLength)); +} + +/// Control the vertical swing setting. +/// @param[in] on True, turns on the feature. False, turns off the feature. +void IRHitachiAc344::setSwingV(const bool on) { + setSwingVToggle(on); // Set the button value. + _.SwingV = on; +} + +/// Get the current vertical swing setting. +/// @return True, if the setting is on. False, it is off. +bool IRHitachiAc344::getSwingV(void) const { + return _.SwingV; +} + +/// Control the horizontal swing setting. +/// @param[in] position The position to set the horizontal swing to. +void IRHitachiAc344::setSwingH(const uint8_t position) { + if (position > kHitachiAc344SwingHLeftMax) + _.SwingH = kHitachiAc344SwingHMiddle; + else + _.SwingH = position; + setButton(kHitachiAc344ButtonSwingH); +} + +/// Get the current horizontal swing setting. +/// @return The current position horizontal swing is set to. +uint8_t IRHitachiAc344::getSwingH(void) const { + return _.SwingH; +} + +/// Convert a standard A/C horizontal swing into its native setting. +/// @param[in] position A stdAc::swingh_t position to convert. +/// @return The equivilent native horizontal swing position. +uint8_t IRHitachiAc344::convertSwingH(const stdAc::swingh_t position) { + switch (position) { + case stdAc::swingh_t::kAuto: return kHitachiAc344SwingHAuto; + case stdAc::swingh_t::kLeftMax: return kHitachiAc344SwingHLeftMax; + case stdAc::swingh_t::kLeft: return kHitachiAc344SwingHLeft; + case stdAc::swingh_t::kRight: return kHitachiAc344SwingHRight; + case stdAc::swingh_t::kRightMax: return kHitachiAc344SwingHRightMax; + default: return kHitachiAc344SwingHMiddle; + } +} + +/// Convert a native horizontal swing postion to it's common equivalent. +/// @param[in] pos A native position to convert. +/// @return The common horizontal swing position. +stdAc::swingh_t IRHitachiAc344::toCommonSwingH(const uint8_t pos) { + switch (pos) { + case kHitachiAc344SwingHLeftMax: return stdAc::swingh_t::kLeftMax; + case kHitachiAc344SwingHLeft: return stdAc::swingh_t::kLeft; + case kHitachiAc344SwingHRight: return stdAc::swingh_t::kRight; + case kHitachiAc344SwingHRightMax: return stdAc::swingh_t::kRightMax; + case kHitachiAc344SwingHAuto: return stdAc::swingh_t::kAuto; + 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 IRHitachiAc344::toCommon(void) const { + stdAc::state_t result = IRHitachiAc424::toCommon(); + result.protocol = decode_type_t::HITACHI_AC344; + result.swingv = _.SwingV ? stdAc::swingv_t::kAuto : stdAc::swingv_t::kOff; + result.swingh = toCommonSwingH(_.SwingH); + return result; +} + +/// Convert the internal state into a human readable string. +/// @return A string containing the settings in human-readable form. +String IRHitachiAc344::toString(void) const { + String result; + result.reserve(120); // Reserve some heap for the string to reduce fragging. + result += _toString(); + result += addBoolToString(_.SwingV, kSwingVStr); + result += addIntToString(_.SwingH, kSwingHStr); + result += kSpaceLBraceStr; + switch (_.SwingH) { + case kHitachiAc344SwingHLeftMax: result += kLeftMaxStr; break; + case kHitachiAc344SwingHLeft: result += kLeftStr; break; + case kHitachiAc344SwingHMiddle: result += kMiddleStr; break; + case kHitachiAc344SwingHRight: result += kRightStr; break; + case kHitachiAc344SwingHRightMax: result += kRightMaxStr; break; + case kHitachiAc344SwingHAuto: result += kAutoStr; break; + default: result += kUnknownStr; + } + result += ')'; + return result; +} diff --git a/lib/IRremoteESP8266/src/ir_Hitachi.h b/lib/IRremoteESP8266/src/ir_Hitachi.h index 88dae3f9aa..32167596f2 100644 --- a/lib/IRremoteESP8266/src/ir_Hitachi.h +++ b/lib/IRremoteESP8266/src/ir_Hitachi.h @@ -28,10 +28,10 @@ #ifndef UNIT_TEST #include #endif -#include "../src/IRremoteESP8266.h" -#include "../src/IRsend.h" +#include "IRremoteESP8266.h" +#include "IRsend.h" #ifdef UNIT_TEST -#include "../src/IRsend_test.h" +#include "IRsend_test.h" #endif /// Native representation of a Hitachi 224-bit A/C message. diff --git a/lib/IRremoteESP8266/src/ir_Inax.cpp b/lib/IRremoteESP8266/src/ir_Inax.cpp new file mode 100644 index 0000000000..bb68ff30d7 --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_Inax.cpp @@ -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 +#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 diff --git a/lib/IRremoteESP8266/src/ir_JVC.cpp b/lib/IRremoteESP8266/src/ir_JVC.cpp new file mode 100644 index 0000000000..9afd6a97f1 --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_JVC.cpp @@ -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 +#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 diff --git a/lib/IRremoteESP8266/src/ir_Kelon.cpp b/lib/IRremoteESP8266/src/ir_Kelon.cpp new file mode 100644 index 0000000000..39b61744eb --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_Kelon.cpp @@ -0,0 +1,502 @@ +// Copyright 2021 Davide Depau + +/// @file +/// @brief Support for Kelan AC protocol. +/// 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 + +#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; + +#if SEND_KELON + +/// Send a Kelon 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, 50); +} + +#endif // SEND_KELON + +#if DECODE_KELON +/// Decode the supplied Kelon 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->state, + results->rawlen - offset, nbits, + kKelonHdrMark, kKelonHdrSpace, + kKelonBitMark, kKelonOneSpace, + kKelonBitMark, kKelonZeroSpace, + kKelonBitMark, 0, false, + _tolerance, 0, false)) { + return false; + } + + results->decode_type = decode_type_t::KELON; + 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(fan) - 4) * -1) % 4; +} + +/// Get the current fan speed setting. +/// @return The current fan speed. +uint8_t IRKelonAc::getFan() const { + return ((static_cast(_.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(_.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(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; + } +} + +/// 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. + result.mode = toCommonMode(getMode()); + result.celsius = true; + result.degrees = getTemp(); + result.fanspeed = toCommonFanSpeed(getFan()); + result.turbo = getSupercool(); + result.sleep = getSleep() ? 0 : -1; + // Not supported. + // N/A, AC only supports toggling it + result.power = (prev == nullptr || prev->power) ^ _.PowerToggle; + // N/A, 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.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; +} diff --git a/lib/IRremoteESP8266/src/ir_Kelon.h b/lib/IRremoteESP8266/src/ir_Kelon.h index 663b25cb9f..498650623f 100644 --- a/lib/IRremoteESP8266/src/ir_Kelon.h +++ b/lib/IRremoteESP8266/src/ir_Kelon.h @@ -17,12 +17,12 @@ #define IR_KELON_H_ #ifdef UNIT_TEST -#include "../src/IRsend_test.h" +#include "IRsend_test.h" #endif -#include "../src/IRremoteESP8266.h" -#include "../src/IRsend.h" -#include "../src/IRutils.h" +#include "IRremoteESP8266.h" +#include "IRsend.h" +#include "IRutils.h" union KelonProtocol { uint64_t raw; diff --git a/lib/IRremoteESP8266/src/ir_Kelvinator.cpp b/lib/IRremoteESP8266/src/ir_Kelvinator.cpp new file mode 100644 index 0000000000..01d2e544c7 --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_Kelvinator.cpp @@ -0,0 +1,525 @@ +// 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 +#include +#ifndef ARDUINO +#include +#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; + +#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; + } +} + +/// Control the current vertical swing setting. +/// @param[in] on The desired setting. +void IRKelvinatorAC::setSwingVertical(const bool on) { + _.SwingV = on; + _.VentSwing = (on || _.SwingH); +} + +/// Is the vertical swing setting on? +/// @return The current value. +bool IRKelvinatorAC::getSwingVertical(void) const { + return _.SwingV; +} + +/// Control the current horizontal swing setting. +/// @param[in] on The desired setting. +void IRKelvinatorAC::setSwingHorizontal(const bool on) { + _.SwingH = on; + _.VentSwing = (on || _.SwingV); +} + +/// 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 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 += addBoolToString(_.SwingV, kSwingVStr); + 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 diff --git a/lib/IRremoteESP8266/src/ir_Kelvinator.h b/lib/IRremoteESP8266/src/ir_Kelvinator.h index 8da8df345f..74371d8efb 100644 --- a/lib/IRremoteESP8266/src/ir_Kelvinator.h +++ b/lib/IRremoteESP8266/src/ir_Kelvinator.h @@ -25,10 +25,10 @@ #ifndef UNIT_TEST #include #endif -#include "../src/IRremoteESP8266.h" -#include "../src/IRsend.h" +#include "IRremoteESP8266.h" +#include "IRsend.h" #ifdef UNIT_TEST -#include "../src/IRsend_test.h" +#include "IRsend_test.h" #endif /// Native representation of a Kelvinator A/C message. diff --git a/lib/IRremoteESP8266/src/ir_LG.cpp b/lib/IRremoteESP8266/src/ir_LG.cpp new file mode 100644 index 0000000000..d817fbdb74 --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_LG.cpp @@ -0,0 +1,838 @@ +// 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 +#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::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::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: + _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() || 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(); +} + +/// 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: + 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 Swing message. +/// @return true, if it is. Otherwise, false. +bool IRLgAc::isSwing(void) const { + return (_.raw >> 12) == kLgAcSwingSignature; +} + +/// 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); +} + +/// 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 || + 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 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; + result.model = getModel(); + result.power = getPower(); + result.mode = toCommonMode(_.Mode); + result.celsius = true; + result.degrees = getTemp(); + result.fanspeed = toCommonFanSpeed(_.Fan); + result.light = isLightToggle() ? !result.light : _light; + 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()) { + 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); +} diff --git a/lib/IRremoteESP8266/src/ir_LG.h b/lib/IRremoteESP8266/src/ir_LG.h index 095b5aad2b..6010282cae 100644 --- a/lib/IRremoteESP8266/src/ir_LG.h +++ b/lib/IRremoteESP8266/src/ir_LG.h @@ -26,11 +26,11 @@ #ifndef UNIT_TEST #include #endif -#include "../src/IRremoteESP8266.h" -#include "../src/IRsend.h" -#include "../src/IRutils.h" +#include "IRremoteESP8266.h" +#include "IRsend.h" +#include "IRutils.h" #ifdef UNIT_TEST -#include "../src/IRsend_test.h" +#include "IRsend_test.h" #endif /// Native representation of a LG A/C message. diff --git a/lib/IRremoteESP8266/src/ir_Lasertag.cpp b/lib/IRremoteESP8266/src/ir_Lasertag.cpp new file mode 100644 index 0000000000..3e40de0efd --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_Lasertag.cpp @@ -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 +#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 diff --git a/lib/IRremoteESP8266/src/ir_Lego.cpp b/lib/IRremoteESP8266/src/ir_Lego.cpp new file mode 100644 index 0000000000..3b7144768b --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_Lego.cpp @@ -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 +#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 diff --git a/lib/IRremoteESP8266/src/ir_Lutron.cpp b/lib/IRremoteESP8266/src/ir_Lutron.cpp new file mode 100644 index 0000000000..5d04247843 --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_Lutron.cpp @@ -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 +#include +#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 diff --git a/lib/IRremoteESP8266/src/ir_MWM.cpp b/lib/IRremoteESP8266/src/ir_MWM.cpp new file mode 100644 index 0000000000..8aca4a4fd0 --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_MWM.cpp @@ -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 +#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 diff --git a/lib/IRremoteESP8266/src/ir_Magiquest.cpp b/lib/IRremoteESP8266/src/ir_Magiquest.cpp new file mode 100644 index 0000000000..3f79a18f64 --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_Magiquest.cpp @@ -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 +#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 diff --git a/lib/IRremoteESP8266/src/ir_Magiquest.h b/lib/IRremoteESP8266/src/ir_Magiquest.h index 37f928b3f8..3999043752 100644 --- a/lib/IRremoteESP8266/src/ir_Magiquest.h +++ b/lib/IRremoteESP8266/src/ir_Magiquest.h @@ -15,8 +15,8 @@ #define __STDC_LIMIT_MACROS #include -#include "../src/IRremoteESP8266.h" -#include "../src/IRsend.h" +#include "IRremoteESP8266.h" +#include "IRsend.h" /// MagiQuest packet is both Wand ID and magnitude of swish and flick union magiquest { diff --git a/lib/IRremoteESP8266/src/ir_Metz.cpp b/lib/IRremoteESP8266/src/ir_Metz.cpp new file mode 100644 index 0000000000..0dcc7dafa6 --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_Metz.cpp @@ -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 diff --git a/lib/IRremoteESP8266/src/ir_Midea.cpp b/lib/IRremoteESP8266/src/ir_Midea.cpp new file mode 100644 index 0000000000..8b3b645d80 --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_Midea.cpp @@ -0,0 +1,796 @@ +// 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 +#ifndef ARDUINO +#include +#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::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; + _SwingVToggle = false; + _EconoToggle = false; + _TurboToggle = false; + _LightToggle = 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; +} +#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; +} + +/// 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.clean = 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.fanspeed = toCommonFanSpeed(_.Fan); + result.sleep = _.Sleep ? 0 : -1; + result.econo = getEconoToggle(); + 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 += addBoolToString(getSwingVToggle(), kSwingVToggleStr); +#if KAYSUN_AC + result += addBoolToString(getSwingVStep(), kStepStr); +#endif // KAYSUN_AC + result += addBoolToString(getEconoToggle(), kEconoToggleStr); + result += addBoolToString(getTurboToggle(), kTurboToggleStr); + result += addBoolToString(getLightToggle(), kLightToggleStr); + 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 diff --git a/lib/IRremoteESP8266/src/ir_Midea.h b/lib/IRremoteESP8266/src/ir_Midea.h index 95a47b0b44..eaeaa04d8d 100644 --- a/lib/IRremoteESP8266/src/ir_Midea.h +++ b/lib/IRremoteESP8266/src/ir_Midea.h @@ -33,10 +33,10 @@ #ifdef ARDUINO #include #endif -#include "../src/IRremoteESP8266.h" -#include "../src/IRsend.h" +#include "IRremoteESP8266.h" +#include "IRsend.h" #ifdef UNIT_TEST -#include "../src/IRsend_test.h" +#include "IRsend_test.h" #endif /// @note diff --git a/lib/IRremoteESP8266/src/ir_MilesTag2.cpp b/lib/IRremoteESP8266/src/ir_MilesTag2.cpp new file mode 100644 index 0000000000..013b2fbe28 --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_MilesTag2.cpp @@ -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 +#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 diff --git a/lib/IRremoteESP8266/src/ir_Mirage.cpp b/lib/IRremoteESP8266/src/ir_Mirage.cpp new file mode 100644 index 0000000000..8a573a8dc2 --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_Mirage.cpp @@ -0,0 +1,70 @@ +// Copyright 2020 David Conran (crankyoldgit) +/// @file +/// @brief Support for Mirage protocol +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1289 + +// Supports: +// Brand: Mirage, Model: VLU series A/C + +#include "IRrecv.h" +#include "IRsend.h" +#include "IRutils.h" + + +// 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) + + +#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; + + // 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; +} +#endif // DECODE_MIRAGE diff --git a/lib/IRremoteESP8266/src/ir_Mitsubishi.cpp b/lib/IRremoteESP8266/src/ir_Mitsubishi.cpp new file mode 100644 index 0000000000..160a882bf4 --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_Mitsubishi.cpp @@ -0,0 +1,1623 @@ +// Copyright 2009 Ken Shirriff +// Copyright 2017-2021 David Conran +// Copyright 2019 Mark Kuchel +// Copyright 2018 Denes Varga + +/// @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 + +#include "ir_Mitsubishi.h" +#include +#include +#ifndef ARDUINO +#include +#endif +#include "IRrecv.h" +#include "IRsend.h" +#include "IRtext.h" +#include "IRutils.h" +#include "ir_Tcl.h" + +// Constants +// Mitsubishi TV +// period time is 1/33000Hz = 30.303 uSeconds (T) +const uint16_t kMitsubishiTick = 30; +const uint16_t kMitsubishiBitMarkTicks = 10; +const uint16_t kMitsubishiBitMark = kMitsubishiBitMarkTicks * kMitsubishiTick; +const uint16_t kMitsubishiOneSpaceTicks = 70; +const uint16_t kMitsubishiOneSpace = kMitsubishiOneSpaceTicks * kMitsubishiTick; +const uint16_t kMitsubishiZeroSpaceTicks = 30; +const uint16_t kMitsubishiZeroSpace = + kMitsubishiZeroSpaceTicks * kMitsubishiTick; +const uint16_t kMitsubishiMinCommandLengthTicks = 1786; +const uint16_t kMitsubishiMinCommandLength = + kMitsubishiMinCommandLengthTicks * kMitsubishiTick; +const uint16_t kMitsubishiMinGapTicks = 936; +const uint16_t kMitsubishiMinGap = kMitsubishiMinGapTicks * kMitsubishiTick; + +// Mitsubishi Projector (HC3000) +const uint16_t kMitsubishi2HdrMark = 8400; +const uint16_t kMitsubishi2HdrSpace = kMitsubishi2HdrMark / 2; +const uint16_t kMitsubishi2BitMark = 560; +const uint16_t kMitsubishi2ZeroSpace = 520; +const uint16_t kMitsubishi2OneSpace = kMitsubishi2ZeroSpace * 3; +const uint16_t kMitsubishi2MinGap = 28500; + +// Mitsubishi A/C +const uint16_t kMitsubishiAcHdrMark = 3400; +const uint16_t kMitsubishiAcHdrSpace = 1750; +const uint16_t kMitsubishiAcBitMark = 450; +const uint16_t kMitsubishiAcOneSpace = 1300; +const uint16_t kMitsubishiAcZeroSpace = 420; +const uint16_t kMitsubishiAcRptMark = 440; +const uint16_t kMitsubishiAcRptSpace = 17100; +const uint8_t kMitsubishiAcExtraTolerance = 5; + +// Mitsubishi 136 bit A/C +const uint16_t kMitsubishi136HdrMark = 3324; +const uint16_t kMitsubishi136HdrSpace = 1474; +const uint16_t kMitsubishi136BitMark = 467; +const uint16_t kMitsubishi136OneSpace = 1137; +const uint16_t kMitsubishi136ZeroSpace = 351; +const uint32_t kMitsubishi136Gap = kDefaultMessageGap; + +// Mitsubishi 112 bit A/C +const uint16_t kMitsubishi112HdrMark = 3450; +const uint16_t kMitsubishi112HdrSpace = 1696; +const uint16_t kMitsubishi112BitMark = 450; +const uint16_t kMitsubishi112OneSpace = 1250; +const uint16_t kMitsubishi112ZeroSpace = 385; +const uint32_t kMitsubishi112Gap = kDefaultMessageGap; +// Total tolerance percentage to use for matching the header mark. +const uint8_t kMitsubishi112HdrMarkTolerance = 5; + + +using irutils::addBoolToString; +using irutils::addFanToString; +using irutils::addIntToString; +using irutils::addLabeledString; +using irutils::addModeToString; +using irutils::addSwingHToString; +using irutils::addSwingVToString; +using irutils::addTempToString; +using irutils::addTempFloatToString; +using irutils::minsToString; + +#if SEND_MITSUBISHI +/// Send the supplied Mitsubishi 16-bit 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 appears to have no header. +/// @see https://github.com/marcosamarinho/IRremoteESP8266/blob/master/ir_Mitsubishi.cpp +/// @see GlobalCache's Control Tower's Mitsubishi TV data. +void IRsend::sendMitsubishi(uint64_t data, uint16_t nbits, uint16_t repeat) { + sendGeneric(0, 0, // No Header + kMitsubishiBitMark, kMitsubishiOneSpace, kMitsubishiBitMark, + kMitsubishiZeroSpace, kMitsubishiBitMark, kMitsubishiMinGap, + kMitsubishiMinCommandLength, data, nbits, 33, true, repeat, 50); +} +#endif // SEND_MITSUBISHI + +#if DECODE_MITSUBISHI +/// Decode the supplied Mitsubishi 16-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. +/// @note This protocol appears to have no header. +/// @see GlobalCache's Control Tower's Mitsubishi TV data. +bool IRrecv::decodeMitsubishi(decode_results *results, uint16_t offset, + const uint16_t nbits, const bool strict) { + if (strict && nbits != kMitsubishiBits) + return false; // Request is out of spec. + + uint64_t data = 0; + + // Match Data + Footer + if (!matchGeneric(results->rawbuf + offset, &data, + results->rawlen - offset, nbits, + 0, 0, // No header + kMitsubishiBitMark, kMitsubishiOneSpace, + kMitsubishiBitMark, kMitsubishiZeroSpace, + kMitsubishiBitMark, kMitsubishiMinGap, + true, 30)) return false; + // Success + results->decode_type = MITSUBISHI; + results->bits = nbits; + results->value = data; + results->address = 0; + results->command = 0; + return true; +} +#endif // DECODE_MITSUBISHI + +#if SEND_MITSUBISHI2 +/// Send a supplied second variant Mitsubishi 16-bit message. +/// Status: BETA / Probably works. +/// @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 Based on a Mitsubishi HC3000 projector's remote. +/// This protocol appears to have a mandatory in-protocol repeat. +/// That is in *addition* to the entire message needing to be sent twice +/// for the device to accept the command. That is separate from the repeat. +/// i.e. Allegedly, the real remote requires the "Off" button pressed twice. +/// You will need to add a suitable gap yourself. +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/441 +void IRsend::sendMitsubishi2(uint64_t data, uint16_t nbits, uint16_t repeat) { + for (uint16_t i = 0; i <= repeat; i++) { + // First half of the data. + sendGeneric(kMitsubishi2HdrMark, kMitsubishi2HdrSpace, kMitsubishi2BitMark, + kMitsubishi2OneSpace, kMitsubishi2BitMark, + kMitsubishi2ZeroSpace, kMitsubishi2BitMark, + kMitsubishi2HdrSpace, data >> (nbits / 2), nbits / 2, 33, true, + 0, 50); + // Second half of the data. + sendGeneric(0, 0, // No header for the second data block + kMitsubishi2BitMark, kMitsubishi2OneSpace, kMitsubishi2BitMark, + kMitsubishi2ZeroSpace, kMitsubishi2BitMark, kMitsubishi2MinGap, + data & ((1 << (nbits / 2)) - 1), nbits / 2, 33, true, 0, 50); + } +} +#endif // SEND_MITSUBISHI2 + +#if DECODE_MITSUBISHI2 +/// Decode the supplied second variation of a Mitsubishi 16-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. +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/441 +bool IRrecv::decodeMitsubishi2(decode_results *results, uint16_t offset, + const uint16_t nbits, const bool strict) { + if (results->rawlen <= 2 * nbits + kHeader + (kFooter * 2) - 1 + offset) + return false; // Shorter than shortest possibly expected. + if (strict && nbits != kMitsubishiBits) + return false; // Request is out of spec. + + results->value = 0; + + // Header + if (!matchMark(results->rawbuf[offset++], kMitsubishi2HdrMark)) return false; + if (!matchSpace(results->rawbuf[offset++], kMitsubishi2HdrSpace)) + return false; + for (uint8_t i = 0; i < 2; i++) { + // Match Data + Footer + uint16_t used; + uint64_t data = 0; + used = matchGeneric(results->rawbuf + offset, &data, + results->rawlen - offset, nbits / 2, + 0, 0, // No header + kMitsubishi2BitMark, kMitsubishi2OneSpace, + kMitsubishi2BitMark, kMitsubishi2ZeroSpace, + kMitsubishi2BitMark, kMitsubishi2HdrSpace, + i % 2); + if (!used) return false; + offset += used; + results->value <<= (nbits / 2); + results->value |= data; + } + + // Success + results->decode_type = MITSUBISHI2; + results->bits = nbits; + results->address = GETBITS64(results->value, nbits / 2, nbits / 2); + results->command = GETBITS64(results->value, 0, nbits / 2); + return true; +} +#endif // DECODE_MITSUBISHI2 + +#if SEND_MITSUBISHI_AC +/// Send a Mitsubishi 144-bit A/C formatted message. (MITSUBISHI_AC) +/// 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::sendMitsubishiAC(const unsigned char data[], const uint16_t nbytes, + const uint16_t repeat) { + if (nbytes < kMitsubishiACStateLength) + return; // Not enough bytes to send a proper message. + + sendGeneric(kMitsubishiAcHdrMark, kMitsubishiAcHdrSpace, kMitsubishiAcBitMark, + kMitsubishiAcOneSpace, kMitsubishiAcBitMark, + kMitsubishiAcZeroSpace, kMitsubishiAcRptMark, + kMitsubishiAcRptSpace, data, nbytes, 38, false, repeat, 50); +} +#endif // SEND_MITSUBISHI_AC + +#if DECODE_MITSUBISHI_AC +/// Decode the supplied Mitsubish 144-bit A/C message. +/// Status: BETA / Probably works +/// @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. +/// @see https://www.analysir.com/blog/2015/01/06/reverse-engineering-mitsubishi-ac-infrared-protocol/ +bool IRrecv::decodeMitsubishiAC(decode_results *results, uint16_t offset, + const uint16_t nbits, + const bool strict) { + // Compliance + if (strict && nbits != kMitsubishiACBits) return false; // Out of spec. + // Do we need to look for a repeat? + const uint16_t expected_repeats = strict ? kMitsubishiACMinRepeat : kNoRepeat; + // Enough data? + if (results->rawlen <= (nbits * 2 + kHeader + kFooter) * + (expected_repeats + 1) + offset - 1) return false; + uint16_t save[kStateSizeMax]; + // Handle repeats if we need too. + for (uint16_t r = 0; r <= expected_repeats; r++) { + // Header + Data + Footer + uint16_t used = matchGeneric(results->rawbuf + offset, results->state, + results->rawlen - offset, nbits, + kMitsubishiAcHdrMark, kMitsubishiAcHdrSpace, + kMitsubishiAcBitMark, kMitsubishiAcOneSpace, + kMitsubishiAcBitMark, kMitsubishiAcZeroSpace, + kMitsubishiAcRptMark, kMitsubishiAcRptSpace, + r < expected_repeats, // At least? + _tolerance + kMitsubishiAcExtraTolerance, + 0, false); + if (!used) return false; // No match. + offset += used; + if (r) { // Is this a repeat? + // Repeats are expected to be exactly the same. + if (std::memcmp(save, results->state, nbits / 8) != 0) return false; + } else { // It is the first message. + // Compliance + if (strict) { + // Data signature check. + static const uint8_t signature[5] = {0x23, 0xCB, 0x26, 0x01, 0x00}; + if (std::memcmp(results->state, signature, 5) != 0) return false; + // Checksum verification. + if (!IRMitsubishiAC::validChecksum(results->state)) return false; + } + // Save a copy of the state to compare with. + std::memcpy(save, results->state, nbits / 8); + } + } + + // Success. + results->decode_type = MITSUBISHI_AC; + results->bits = nbits; + return true; +} +#endif // DECODE_MITSUBISHI_AC + +// Code to emulate Mitsubishi A/C IR remote control unit. +// Inspired and derived from the work done at: +// https://github.com/r45635/HVAC-IR-Control + +/// 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? +/// @warning Consider this very alpha code. Seems to work, but not validated. +IRMitsubishiAC::IRMitsubishiAC(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 IRMitsubishiAC::stateReset(void) { + // The state of the IR remote in IR code form. + // Known good state obtained from: + // https://github.com/r45635/HVAC-IR-Control/blob/master/HVAC_ESP8266/HVAC_ESP8266.ino#L108 + static const uint8_t kReset[kMitsubishiACStateLength] = { + 0x23, 0xCB, 0x26, 0x01, 0x00, 0x20, 0x08, 0x06, 0x30, 0x45, 0x67}; + setRaw(kReset); +} + +/// Set up hardware to be able to send a message. +void IRMitsubishiAC::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 IRMitsubishiAC::send(const uint16_t repeat) { + _irsend.sendMitsubishiAC(getRaw(), kMitsubishiACStateLength, repeat); +} +#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 *IRMitsubishiAC::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 IRMitsubishiAC::setRaw(const uint8_t *data) { + std::memcpy(_.raw, data, kMitsubishiACStateLength); +} + +/// Calculate and set the checksum values for the internal state. +void IRMitsubishiAC::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 IRMitsubishiAC::validChecksum(const uint8_t *data) { + return calculateChecksum(data) == data[kMitsubishiACStateLength - 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 IRMitsubishiAC::calculateChecksum(const uint8_t *data) { + return sumBytes(data, kMitsubishiACStateLength - 1); +} + +/// Set the requested power state of the A/C to on. +void IRMitsubishiAC::on(void) { setPower(true); } + +/// Set the requested power state of the A/C to off. +void IRMitsubishiAC::off(void) { setPower(false); } + +/// Change the power setting. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRMitsubishiAC::setPower(bool on) { + _.Power = on; +} + +/// Get the value of the current power setting. +/// @return true, the setting is on. false, the setting is off. +bool IRMitsubishiAC::getPower(void) const { + return _.Power; +} + +/// Set the temperature. +/// @param[in] degrees The temperature in degrees celsius. +/// @note The temperature resolution is 0.5 of a degree. +void IRMitsubishiAC::setTemp(const float degrees) { + // Make sure we have desired temp in the correct range. + float celsius = std::max(degrees, kMitsubishiAcMinTemp); + celsius = std::min(celsius, kMitsubishiAcMaxTemp); + // Convert to integer nr. of half degrees. + uint8_t nrHalfDegrees = celsius * 2; + // Do we have a half degree celsius? + _.HalfDegree = nrHalfDegrees & 1; + _.Temp = static_cast(nrHalfDegrees / 2 - kMitsubishiAcMinTemp); +} + +/// Get the current temperature setting. +/// @return The current setting for temp. in degrees celsius. +/// @note The temperature resolution is 0.5 of a degree. +float IRMitsubishiAC::getTemp(void) const { + return _.Temp + kMitsubishiAcMinTemp + (_.HalfDegree ? 0.5 : 0); +} + +/// Set the speed of the fan. +/// @param[in] speed The desired setting. 0 is auto, 1-5 is speed, 6 is silent. +void IRMitsubishiAC::setFan(const uint8_t speed) { + uint8_t fan = speed; + // Bounds check + if (fan > kMitsubishiAcFanSilent) + fan = kMitsubishiAcFanMax; // Set the fan to maximum if out of range. + // Auto has a special bit. + _.FanAuto = (fan == kMitsubishiAcFanAuto); + if (fan >= kMitsubishiAcFanMax) + fan--; // There is no spoon^H^H^Heed 5 (max), pretend it doesn't exist. + _.Fan = fan; +} + +/// Get the current fan speed setting. +/// @return The current fan speed/mode. +uint8_t IRMitsubishiAC::getFan(void) const { + uint8_t fan = _.Fan; + if (fan == kMitsubishiAcFanMax) return kMitsubishiAcFanSilent; + return fan; +} + +/// Get the operating mode setting of the A/C. +/// @return The current operating mode setting. +uint8_t IRMitsubishiAC::getMode(void) const { + return _.Mode; +} + +/// Set the operating mode of the A/C. +/// @param[in] mode The desired operating mode. +void IRMitsubishiAC::setMode(const uint8_t mode) { + // If we get an unexpected mode, default to AUTO. + switch (mode) { + case kMitsubishiAcAuto: _.raw[8] = 0b00110000; break; + case kMitsubishiAcCool: _.raw[8] = 0b00110110; break; + case kMitsubishiAcDry: _.raw[8] = 0b00110010; break; + case kMitsubishiAcHeat: _.raw[8] = 0b00110000; break; + case kMitsubishiAcFan: _.raw[8] = 0b00110111; break; + default: + _.raw[8] = 0b00110000; + _.Mode = kMitsubishiAcAuto; + return; + } + _.Mode = mode; +} + +/// Set the requested vane (Vertical Swing) operation mode of the a/c unit. +/// @note On some models, this represents the Right vertical vane. +/// @param[in] position The position/mode to set the vane to. +void IRMitsubishiAC::setVane(const uint8_t position) { + uint8_t pos = std::min(position, kMitsubishiAcVaneAutoMove); // bounds check + _.VaneBit = 1; + _.Vane = pos; +} + +/// Set the requested wide-vane (Horizontal Swing) operation mode of the a/c. +/// @param[in] position The position/mode to set the wide vane to. +void IRMitsubishiAC::setWideVane(const uint8_t position) { + _.WideVane = std::min(position, kMitsubishiAcWideVaneAuto); +} + +/// Get the Vane (Vertical Swing) mode of the A/C. +/// @note On some models, this represents the Right vertical vane. +/// @return The native position/mode setting. +uint8_t IRMitsubishiAC::getVane(void) const { + return _.Vane; +} + +/// Get the Wide Vane (Horizontal Swing) mode of the A/C. +/// @return The native position/mode setting. +uint8_t IRMitsubishiAC::getWideVane(void) const { + return _.WideVane; +} + +/// Set the requested Left Vane (Vertical Swing) operation mode of the a/c unit. +/// @param[in] position The position/mode to set the vane to. +void IRMitsubishiAC::setVaneLeft(const uint8_t position) { + _.VaneLeft = std::min(position, kMitsubishiAcVaneAutoMove); // bounds check +} + +/// Get the Left Vane (Vertical Swing) mode of the A/C. +/// @return The native position/mode setting. +uint8_t IRMitsubishiAC::getVaneLeft(void) const { return _.VaneLeft; } + +/// Get the clock time of the A/C unit. +/// @return Nr. of 10 minute increments past midnight. +/// @note 1 = 1/6 hour (10 minutes). e.g. 4pm = 48. +uint8_t IRMitsubishiAC::getClock(void) const { return _.Clock; } + +/// Set the clock time on the A/C unit. +/// @param[in] clock Nr. of 10 minute increments past midnight. +/// @note 1 = 1/6 hour (10 minutes). e.g. 6am = 36. +void IRMitsubishiAC::setClock(const uint8_t clock) { + _.Clock = clock; +} + +/// Get the desired start time of the A/C unit. +/// @return Nr. of 10 minute increments past midnight. +/// @note 1 = 1/6 hour (10 minutes). e.g. 4pm = 48. +uint8_t IRMitsubishiAC::getStartClock(void) const { return _.StartClock; } + +/// Set the desired start time of the A/C unit. +/// @param[in] clock Nr. of 10 minute increments past midnight. +/// @note 1 = 1/6 hour (10 minutes). e.g. 8pm = 120. +void IRMitsubishiAC::setStartClock(const uint8_t clock) { + _.StartClock = clock; +} + +/// Get the desired stop time of the A/C unit. +/// @return Nr. of 10 minute increments past midnight. +/// @note 1 = 1/6 hour (10 minutes). e.g. 10pm = 132. +uint8_t IRMitsubishiAC::getStopClock(void) const { return _.StopClock; } + +/// Set the desired stop time of the A/C unit. +/// @param[in] clock Nr. of 10 minute increments past midnight. +/// @note 1 = 1/6 hour (10 minutes). e.g. 10pm = 132. +void IRMitsubishiAC::setStopClock(const uint8_t clock) { + _.StopClock = clock; +} + +/// Get the timers active setting of the A/C. +/// @return The current timers enabled. +/// @note Possible values: kMitsubishiAcNoTimer, +/// kMitsubishiAcStartTimer, kMitsubishiAcStopTimer, +/// kMitsubishiAcStartStopTimer +uint8_t IRMitsubishiAC::getTimer(void) const { + return _.Timer; +} + +/// Set the timers active setting of the A/C. +/// @param[in] timer The timer code indicating which ones are active. +/// @note Possible values: kMitsubishiAcNoTimer, +/// kMitsubishiAcStartTimer, kMitsubishiAcStopTimer, +/// kMitsubishiAcStartStopTimer +void IRMitsubishiAC::setTimer(const uint8_t timer) { + _.Timer = timer; +} + +/// 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 IRMitsubishiAC::convertMode(const stdAc::opmode_t mode) { + switch (mode) { + case stdAc::opmode_t::kCool: return kMitsubishiAcCool; + case stdAc::opmode_t::kHeat: return kMitsubishiAcHeat; + case stdAc::opmode_t::kDry: return kMitsubishiAcDry; + case stdAc::opmode_t::kFan: return kMitsubishiAcFan; + default: return kMitsubishiAcAuto; + } +} + +/// 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 IRMitsubishiAC::convertFan(const stdAc::fanspeed_t speed) { + switch (speed) { + case stdAc::fanspeed_t::kMin: return kMitsubishiAcFanSilent; + case stdAc::fanspeed_t::kLow: return kMitsubishiAcFanRealMax - 3; + case stdAc::fanspeed_t::kMedium: return kMitsubishiAcFanRealMax - 2; + case stdAc::fanspeed_t::kHigh: return kMitsubishiAcFanRealMax - 1; + case stdAc::fanspeed_t::kMax: return kMitsubishiAcFanRealMax; + default: return kMitsubishiAcFanAuto; + } +} + + +/// 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. +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1399 +/// @see https://github.com/crankyoldgit/IRremoteESP8266/pull/1401 +uint8_t IRMitsubishiAC::convertSwingV(const stdAc::swingv_t position) { + switch (position) { + case stdAc::swingv_t::kHighest: return kMitsubishiAcVaneHighest; + case stdAc::swingv_t::kHigh: return kMitsubishiAcVaneHigh; + case stdAc::swingv_t::kMiddle: return kMitsubishiAcVaneMiddle; + case stdAc::swingv_t::kLow: return kMitsubishiAcVaneLow; + case stdAc::swingv_t::kLowest: return kMitsubishiAcVaneLowest; + // These model Mitsubishi A/C have two automatic settings. + // 1. A typical up & down oscillation. (Native Swing) + // 2. The A/C determines where the best placement for the vanes, outside of + // user control. (Native Auto) + // Native "Swing" is what we consider "Auto" in stdAc. (Case 1) + case stdAc::swingv_t::kAuto: return kMitsubishiAcVaneSwing; + // Native "Auto" doesn't have a good match for this in stdAc. (Case 2) + // So we repurpose stdAc's "Off" (and anything else) to be Native Auto. + default: return kMitsubishiAcVaneAuto; + } +} + +/// Convert a stdAc::swingh_t enum into it's native setting. +/// @param[in] position The enum to be converted. +/// @return The native equivalent of the enum. +uint8_t IRMitsubishiAC::convertSwingH(const stdAc::swingh_t position) { + switch (position) { + case stdAc::swingh_t::kLeftMax: return kMitsubishiAcWideVaneLeftMax; + case stdAc::swingh_t::kLeft: return kMitsubishiAcWideVaneLeft; + case stdAc::swingh_t::kMiddle: return kMitsubishiAcWideVaneMiddle; + case stdAc::swingh_t::kRight: return kMitsubishiAcWideVaneRight; + case stdAc::swingh_t::kRightMax: return kMitsubishiAcWideVaneRightMax; + case stdAc::swingh_t::kWide: return kMitsubishiAcWideVaneWide; + case stdAc::swingh_t::kAuto: return kMitsubishiAcWideVaneAuto; + default: return kMitsubishiAcWideVaneMiddle; + } +} + +/// 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 IRMitsubishiAC::toCommonMode(const uint8_t mode) { + switch (mode) { + case kMitsubishiAcCool: return stdAc::opmode_t::kCool; + case kMitsubishiAcHeat: return stdAc::opmode_t::kHeat; + case kMitsubishiAcDry: return stdAc::opmode_t::kDry; + case kMitsubishiAcFan: 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 IRMitsubishiAC::toCommonFanSpeed(const uint8_t speed) { + switch (speed) { + case kMitsubishiAcFanRealMax: return stdAc::fanspeed_t::kMax; + case kMitsubishiAcFanRealMax - 1: return stdAc::fanspeed_t::kHigh; + case kMitsubishiAcFanRealMax - 2: return stdAc::fanspeed_t::kMedium; + case kMitsubishiAcFanRealMax - 3: return stdAc::fanspeed_t::kLow; + case kMitsubishiAcFanSilent: return stdAc::fanspeed_t::kMin; + default: return stdAc::fanspeed_t::kAuto; + } +} + +/// 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. +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1399 +/// @see https://github.com/crankyoldgit/IRremoteESP8266/pull/1401 +stdAc::swingv_t IRMitsubishiAC::toCommonSwingV(const uint8_t pos) { + switch (pos) { + case kMitsubishiAcVaneHighest: return stdAc::swingv_t::kHighest; + case kMitsubishiAcVaneHigh: return stdAc::swingv_t::kHigh; + case kMitsubishiAcVaneMiddle: return stdAc::swingv_t::kMiddle; + case kMitsubishiAcVaneLow: return stdAc::swingv_t::kLow; + case kMitsubishiAcVaneLowest: return stdAc::swingv_t::kLowest; + // These model Mitsubishi A/C have two automatic settings. + // 1. A typical up & down oscillation. (Native Swing) + // 2. The A/C determines where the best placement for the vanes, outside of + // user control. (Native Auto) + // Native "Auto" doesn't have a good match for this in stdAc. (Case 2) + // So we repurpose stdAc's "Off" to be Native Auto. + case kMitsubishiAcVaneAuto: return stdAc::swingv_t::kOff; + // Native "Swing" is what we consider "Auto" in stdAc. (Case 1) + default: return stdAc::swingv_t::kAuto; + } +} + +/// Convert a native horizontal swing postion to it's common equivalent. +/// @param[in] pos A native position to convert. +/// @return The common horizontal swing position. +stdAc::swingh_t IRMitsubishiAC::toCommonSwingH(const uint8_t pos) { + switch (pos) { + case kMitsubishiAcWideVaneLeftMax: return stdAc::swingh_t::kLeftMax; + case kMitsubishiAcWideVaneLeft: return stdAc::swingh_t::kLeft; + case kMitsubishiAcWideVaneMiddle: return stdAc::swingh_t::kMiddle; + case kMitsubishiAcWideVaneRight: return stdAc::swingh_t::kRight; + case kMitsubishiAcWideVaneRightMax: return stdAc::swingh_t::kRightMax; + case kMitsubishiAcWideVaneWide: return stdAc::swingh_t::kWide; + default: return stdAc::swingh_t::kAuto; + } +} + +/// Convert the current internal state into its stdAc::state_t equivalent. +/// @return The stdAc equivalent of the native settings. +stdAc::state_t IRMitsubishiAC::toCommon(void) const { + stdAc::state_t result; + result.protocol = decode_type_t::MITSUBISHI_AC; + result.model = -1; // No models used. + result.power = _.Power; + result.mode = toCommonMode(_.Mode); + result.celsius = true; + result.degrees = getTemp(); + result.fanspeed = toCommonFanSpeed(getFan()); + result.swingv = toCommonSwingV(_.Vane); + result.swingh = toCommonSwingH(_.WideVane); + result.quiet = getFan() == kMitsubishiAcFanSilent; + // Not supported. + result.turbo = false; + result.clean = false; + result.econo = false; + result.filter = false; + result.light = false; + result.beep = false; + result.sleep = -1; + result.clock = -1; + return result; +} + +/// Change the Weekly Timer Enabled setting. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRMitsubishiAC::setWeeklyTimerEnabled(const bool on) { + _.WeeklyTimer = on; +} + +/// Get the value of the WeeklyTimer Enabled setting. +/// @return true, the setting is on. false, the setting is off. +bool IRMitsubishiAC::getWeeklyTimerEnabled(void) const { return _.WeeklyTimer; } + +/// Convert the internal state into a human readable string. +/// @return A string containing the settings in human-readable form. +String IRMitsubishiAC::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, kMitsubishiAcAuto, kMitsubishiAcCool, + kMitsubishiAcHeat, kMitsubishiAcDry, + kMitsubishiAcFan); + result += addTempFloatToString(getTemp()); + result += addFanToString(getFan(), kMitsubishiAcFanRealMax, + kMitsubishiAcFanRealMax - 3, + kMitsubishiAcFanAuto, kMitsubishiAcFanQuiet, + kMitsubishiAcFanRealMax - 2); + result += addSwingVToString(_.Vane, kMitsubishiAcVaneAuto, + kMitsubishiAcVaneHighest, kMitsubishiAcVaneHigh, + kMitsubishiAcVaneAuto, // Upper Middle unused. + kMitsubishiAcVaneMiddle, + kMitsubishiAcVaneAuto, // Lower Middle unused. + kMitsubishiAcVaneLow, kMitsubishiAcVaneLowest, + kMitsubishiAcVaneAuto, kMitsubishiAcVaneSwing, + // Below are unused. + kMitsubishiAcVaneAuto, kMitsubishiAcVaneAuto); + result += addSwingHToString(_.WideVane, kMitsubishiAcWideVaneAuto, + kMitsubishiAcWideVaneLeftMax, + kMitsubishiAcWideVaneLeft, + kMitsubishiAcWideVaneMiddle, + kMitsubishiAcWideVaneRight, + kMitsubishiAcWideVaneRightMax, + kMitsubishiAcWideVaneAuto, // Unused + kMitsubishiAcWideVaneAuto, // Unused + kMitsubishiAcWideVaneAuto, // Unused + kMitsubishiAcWideVaneAuto, // Unused + kMitsubishiAcWideVaneWide); + result += addLabeledString(minsToString(_.Clock * 10), kClockStr); + result += addLabeledString(minsToString(_.StartClock * 10), kOnTimerStr); + result += addLabeledString(minsToString(_.StopClock * 10), kOffTimerStr); + result += kCommaSpaceStr; + result += kTimerStr; + result += kColonSpaceStr; + switch (_.Timer) { + case kMitsubishiAcNoTimer: + result += '-'; + break; + case kMitsubishiAcStartTimer: + result += kStartStr; + break; + case kMitsubishiAcStopTimer: + result += kStopStr; + break; + case kMitsubishiAcStartStopTimer: + result += kStartStr; + result += '+'; + result += kStopStr; + break; + default: + result += F("? ("); + result += _.Timer; + result += ')'; + } + result += addBoolToString(_.WeeklyTimer, kWeeklyTimerStr); + return result; +} + +#if SEND_MITSUBISHI136 +/// Send a Mitsubishi 136-bit A/C message. (MITSUBISHI136) +/// Status: BETA / Probably working. Needs to be tested against a real device. +/// @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. +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/888 +void IRsend::sendMitsubishi136(const unsigned char data[], + const uint16_t nbytes, + const uint16_t repeat) { + if (nbytes < kMitsubishi136StateLength) + return; // Not enough bytes to send a proper message. + + sendGeneric(kMitsubishi136HdrMark, kMitsubishi136HdrSpace, + kMitsubishi136BitMark, kMitsubishi136OneSpace, + kMitsubishi136BitMark, kMitsubishi136ZeroSpace, + kMitsubishi136BitMark, kMitsubishi136Gap, + data, nbytes, 38, false, repeat, 50); +} +#endif // SEND_MITSUBISHI136 + +#if DECODE_MITSUBISHI136 +/// Decode the supplied Mitsubishi 136-bit A/C message. (MITSUBISHI136) +/// Status: STABLE / Reported as 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. +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/888 +bool IRrecv::decodeMitsubishi136(decode_results *results, uint16_t offset, + const uint16_t nbits, + const bool strict) { + if (nbits % 8 != 0) return false; // Not a multiple of an 8 bit byte. + if (strict) { // Do checks to see if it matches the spec. + if (nbits != kMitsubishi136Bits) return false; + } + uint16_t used = matchGeneric(results->rawbuf + offset, results->state, + results->rawlen - offset, nbits, + kMitsubishi136HdrMark, kMitsubishi136HdrSpace, + kMitsubishi136BitMark, kMitsubishi136OneSpace, + kMitsubishi136BitMark, kMitsubishi136ZeroSpace, + kMitsubishi136BitMark, kMitsubishi136Gap, + true, _tolerance, 0, false); + if (!used) return false; + if (strict) { + // Header validation: Codes start with 0x23CB26 + if (results->state[0] != 0x23 || results->state[1] != 0xCB || + results->state[2] != 0x26) return false; + if (!IRMitsubishi136::validChecksum(results->state, nbits / 8)) + return false; + } + results->decode_type = MITSUBISHI136; + results->bits = nbits; + return true; +} +#endif // DECODE_MITSUBISHI136 + +// Code to emulate Mitsubishi 136bit 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? +IRMitsubishi136::IRMitsubishi136(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 IRMitsubishi136::stateReset(void) { + // The state of the IR remote in IR code form. + // Known good state obtained from: + // https://docs.google.com/spreadsheets/d/1f8EGfIbBUo2B-CzUFdrgKQprWakoYNKM80IKZN4KXQE/edit#gid=312397579&range=A10 + static const uint8_t kReset[kMitsubishi136StateLength] = { + 0x23, 0xCB, 0x26, 0x21, 0x00, 0x40, 0xC2, 0xC7, 0x04}; + std::memcpy(_.raw, kReset, kMitsubishi136StateLength); +} + +/// Calculate the checksum for the current internal state of the remote. +void IRMitsubishi136::checksum(void) { + for (uint8_t i = 0; i < 6; i++) + _.raw[kMitsubishi136PowerByte + 6 + i] = + ~_.raw[kMitsubishi136PowerByte + i]; +} + +/// Verify the checksum is valid for a given state. +/// @param[in] data The array to verify the checksum of. +/// @param[in] len The length of the data array. +/// @return true, if the state has a valid checksum. Otherwise, false. +bool IRMitsubishi136::validChecksum(const uint8_t *data, const uint16_t len) { + if (len < kMitsubishi136StateLength) return false; + const uint16_t half = (len - kMitsubishi136PowerByte) / 2; + for (uint8_t i = 0; i < half; i++) { + // This variable is needed to avoid the warning: (known compiler issue) + // warning: comparison of promoted ~unsigned with unsigned [-Wsign-compare] + const uint8_t inverted = ~data[kMitsubishi136PowerByte + half + i]; + if (data[kMitsubishi136PowerByte + i] != inverted) return false; + } + return true; +} + +/// Set up hardware to be able to send a message. +void IRMitsubishi136::begin(void) { _irsend.begin(); } + +#if SEND_MITSUBISHI136 +/// Send the current internal state as an IR message. +/// @param[in] repeat Nr. of times the message will be repeated. +void IRMitsubishi136::send(const uint16_t repeat) { + _irsend.sendMitsubishi136(getRaw(), kMitsubishi136StateLength, repeat); +} +#endif // SEND_MITSUBISHI136 + +/// 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 *IRMitsubishi136::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 IRMitsubishi136::setRaw(const uint8_t *data) { + std::memcpy(_.raw, data, kMitsubishi136StateLength); +} + +/// Set the requested power state of the A/C to on. +void IRMitsubishi136::on(void) { setPower(true); } + +/// Set the requested power state of the A/C to off. +void IRMitsubishi136::off(void) { setPower(false); } + +/// Change the power setting. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRMitsubishi136::setPower(bool on) { + _.Power = on; +} + +/// Get the value of the current power setting. +/// @return true, the setting is on. false, the setting is off. +bool IRMitsubishi136::getPower(void) const { + return _.Power; +} + +/// Set the temperature. +/// @param[in] degrees The temperature in degrees celsius. +void IRMitsubishi136::setTemp(const uint8_t degrees) { + uint8_t temp = std::max((uint8_t)kMitsubishi136MinTemp, degrees); + temp = std::min((uint8_t)kMitsubishi136MaxTemp, temp); + _.Temp = temp - kMitsubishiAcMinTemp; +} + +/// Get the current temperature setting. +/// @return The current setting for temp. in degrees celsius. +uint8_t IRMitsubishi136::getTemp(void) const { + return _.Temp + kMitsubishiAcMinTemp; +} + +/// Set the speed of the fan. +/// @param[in] speed The desired setting. +void IRMitsubishi136::setFan(const uint8_t speed) { + _.Fan = std::min(speed, kMitsubishi136FanMax); +} + +/// Get the current fan speed setting. +/// @return The current fan speed/mode. +uint8_t IRMitsubishi136::getFan(void) const { + return _.Fan; +} + +/// Get the operating mode setting of the A/C. +/// @return The current operating mode setting. +uint8_t IRMitsubishi136::getMode(void) const { + return _.Mode; +} + +/// Set the operating mode of the A/C. +/// @param[in] mode The desired operating mode. +void IRMitsubishi136::setMode(const uint8_t mode) { + // If we get an unexpected mode, default to AUTO. + switch (mode) { + case kMitsubishi136Fan: + case kMitsubishi136Cool: + case kMitsubishi136Heat: + case kMitsubishi136Auto: + case kMitsubishi136Dry: + _.Mode = mode; + break; + default: + _.Mode = kMitsubishi136Auto; + } +} + +/// Set the Vertical Swing mode of the A/C. +/// @param[in] position The position/mode to set the swing to. +void IRMitsubishi136::setSwingV(const uint8_t position) { + // If we get an unexpected mode, default to auto. + switch (position) { + case kMitsubishi136SwingVLowest: + case kMitsubishi136SwingVLow: + case kMitsubishi136SwingVHigh: + case kMitsubishi136SwingVHighest: + case kMitsubishi136SwingVAuto: + _.SwingV = position; + break; + default: + _.SwingV = kMitsubishi136SwingVAuto; + } +} + +/// Get the Vertical Swing mode of the A/C. +/// @return The native position/mode setting. +uint8_t IRMitsubishi136::getSwingV(void) const { + return _.SwingV; +} + +/// Set the Quiet mode of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRMitsubishi136::setQuiet(bool on) { + if (on) setFan(kMitsubishi136FanQuiet); + else if (getQuiet()) setFan(kMitsubishi136FanLow); +} + + +/// Get the Quiet mode of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRMitsubishi136::getQuiet(void) const { + return _.Fan == kMitsubishi136FanQuiet; +} + +/// 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 IRMitsubishi136::convertMode(const stdAc::opmode_t mode) { + switch (mode) { + case stdAc::opmode_t::kCool: return kMitsubishi136Cool; + case stdAc::opmode_t::kHeat: return kMitsubishi136Heat; + case stdAc::opmode_t::kDry: return kMitsubishi136Dry; + case stdAc::opmode_t::kFan: return kMitsubishi136Fan; + default: return kMitsubishi136Auto; + } +} + +/// 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 IRMitsubishi136::convertFan(const stdAc::fanspeed_t speed) { + switch (speed) { + case stdAc::fanspeed_t::kMin: return kMitsubishi136FanMin; + case stdAc::fanspeed_t::kLow: return kMitsubishi136FanLow; + case stdAc::fanspeed_t::kHigh: + case stdAc::fanspeed_t::kMax: return kMitsubishi136FanMax; + default: return kMitsubishi136FanMed; + } +} + +/// 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 IRMitsubishi136::convertSwingV(const stdAc::swingv_t position) { + switch (position) { + case stdAc::swingv_t::kHighest: return kMitsubishi136SwingVHighest; + case stdAc::swingv_t::kHigh: + case stdAc::swingv_t::kMiddle: return kMitsubishi136SwingVHigh; + case stdAc::swingv_t::kLow: return kMitsubishi136SwingVLow; + case stdAc::swingv_t::kLowest: return kMitsubishi136SwingVLowest; + default: return kMitsubishi136SwingVAuto; + } +} + +/// 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 IRMitsubishi136::toCommonMode(const uint8_t mode) { + switch (mode) { + case kMitsubishi136Cool: return stdAc::opmode_t::kCool; + case kMitsubishi136Heat: return stdAc::opmode_t::kHeat; + case kMitsubishi136Dry: return stdAc::opmode_t::kDry; + case kMitsubishi136Fan: 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 IRMitsubishi136::toCommonFanSpeed(const uint8_t speed) { + switch (speed) { + case kMitsubishi136FanMax: return stdAc::fanspeed_t::kMax; + case kMitsubishi136FanMed: return stdAc::fanspeed_t::kMedium; + case kMitsubishi136FanLow: return stdAc::fanspeed_t::kLow; + case kMitsubishi136FanMin: return stdAc::fanspeed_t::kMin; + default: return stdAc::fanspeed_t::kMedium; + } +} + +/// 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 IRMitsubishi136::toCommonSwingV(const uint8_t pos) { + switch (pos) { + case kMitsubishi136SwingVHighest: return stdAc::swingv_t::kHighest; + case kMitsubishi136SwingVHigh: return stdAc::swingv_t::kHigh; + case kMitsubishi136SwingVLow: return stdAc::swingv_t::kLow; + case kMitsubishi136SwingVLowest: return stdAc::swingv_t::kLowest; + default: return stdAc::swingv_t::kAuto; + } +} + +/// Convert the current internal state into its stdAc::state_t equivalent. +/// @return The stdAc equivalent of the native settings. +stdAc::state_t IRMitsubishi136::toCommon(void) const { + stdAc::state_t result; + result.protocol = decode_type_t::MITSUBISHI136; + 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 = toCommonSwingV(_.SwingV); + result.quiet = getQuiet(); + // Not supported. + result.swingh = stdAc::swingh_t::kOff; + result.turbo = false; + result.clean = false; + result.econo = false; + result.filter = false; + result.light = false; + result.beep = false; + result.sleep = -1; + result.clock = -1; + return result; +} + +/// Convert the internal state into a human readable string. +/// @return A string containing the settings in human-readable form. +String IRMitsubishi136::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, kMitsubishi136Auto, kMitsubishi136Cool, + kMitsubishi136Heat, kMitsubishi136Dry, + kMitsubishi136Fan); + result += addTempToString(getTemp()); + result += addFanToString(_.Fan, kMitsubishi136FanMax, + kMitsubishi136FanLow, kMitsubishi136FanMax, + kMitsubishi136FanQuiet, kMitsubishi136FanMed); + result += addSwingVToString(_.SwingV, kMitsubishi136SwingVAuto, + kMitsubishi136SwingVHighest, + kMitsubishi136SwingVHigh, + kMitsubishi136SwingVAuto, // Unused + kMitsubishi136SwingVAuto, // Unused + kMitsubishi136SwingVAuto, // Unused + kMitsubishi136SwingVLow, + kMitsubishi136SwingVLow, + // Below are unused. + kMitsubishi136SwingVAuto, + kMitsubishi136SwingVAuto, + kMitsubishi136SwingVAuto, + kMitsubishi136SwingVAuto); + result += addBoolToString(getQuiet(), kQuietStr); + return result; +} + + +#if SEND_MITSUBISHI112 +/// Send a Mitsubishi 112-bit A/C formatted message. (MITSUBISHI112) +/// 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. +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/947 +void IRsend::sendMitsubishi112(const unsigned char data[], + const uint16_t nbytes, + const uint16_t repeat) { + if (nbytes < kMitsubishi112StateLength) + return; // Not enough bytes to send a proper message. + + sendGeneric(kMitsubishi112HdrMark, kMitsubishi112HdrSpace, + kMitsubishi112BitMark, kMitsubishi112OneSpace, + kMitsubishi112BitMark, kMitsubishi112ZeroSpace, + kMitsubishi112BitMark, kMitsubishi112Gap, + data, nbytes, 38, false, repeat, 50); +} +#endif // SEND_MITSUBISHI112 + +#if (DECODE_MITSUBISHI112 || DECODE_TCL112AC) +/// Decode the supplied Mitsubishi/TCL 112-bit A/C message. +/// (MITSUBISHI112, TCL112AC) +/// Status: STABLE / Reported as 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. +/// @note Note Mitsubishi112 & Tcl112Ac are basically the same protocol. +/// The only significant difference I can see is Mitsubishi112 has a +/// slightly longer header mark. We will use that to determine which +/// variant it should be. The other differences require full decoding and +/// only only with certain settings. +/// There are some other timing differences too, but the tolerances will +/// overlap. +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/619 +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/947 +bool IRrecv::decodeMitsubishi112(decode_results *results, uint16_t offset, + const uint16_t nbits, const bool strict) { + if (results->rawlen < (2 * nbits) + kHeader + kFooter - 1 + offset) + return false; + if (nbits % 8 != 0) return false; // Not a multiple of an 8 bit byte. + if (strict) { // Do checks to see if it matches the spec. + if (nbits != kMitsubishi112Bits && nbits != kTcl112AcBits) return false; + } + decode_type_t typeguess = decode_type_t::UNKNOWN; + uint16_t hdrspace; + uint16_t bitmark; + uint16_t onespace; + uint16_t zerospace; + uint32_t gap; + uint8_t tolerance = _tolerance; + + // Header +#if DECODE_MITSUBISHI112 + if (matchMark(results->rawbuf[offset], kMitsubishi112HdrMark, + kMitsubishi112HdrMarkTolerance, 0)) { + typeguess = decode_type_t::MITSUBISHI112; + hdrspace = kMitsubishi112HdrSpace; + bitmark = kMitsubishi112BitMark; + onespace = kMitsubishi112OneSpace; + zerospace = kMitsubishi112ZeroSpace; + gap = kMitsubishi112Gap; + } +#endif // DECODE_MITSUBISHI112 +#if (DECODE_TCL112AC || DECODE_TEKNOPOINT) + if (typeguess == decode_type_t::UNKNOWN && // We didn't match Mitsubishi112 + matchMark(results->rawbuf[offset], kTcl112AcHdrMark, + kTcl112AcHdrMarkTolerance, 0)) { + typeguess = decode_type_t::TCL112AC; + hdrspace = kTcl112AcHdrSpace; + bitmark = kTcl112AcBitMark; + onespace = kTcl112AcOneSpace; + zerospace = kTcl112AcZeroSpace; + gap = kTcl112AcGap; + tolerance += kTcl112AcTolerance; + } +#endif // (DECODE_TCL112AC || DECODE_TEKNOPOINT) + if (typeguess == decode_type_t::UNKNOWN) return false; // No header matched. + offset++; + + uint16_t used = matchGeneric(results->rawbuf + offset, results->state, + results->rawlen - offset, nbits, + 0, // Skip the header as we matched it earlier. + hdrspace, bitmark, onespace, bitmark, zerospace, + bitmark, gap, + true, tolerance, 0, false); + if (!used) return false; + if (strict) { + // Header validation: Codes start with 0x23CB26 + if (results->state[0] != 0x23 || results->state[1] != 0xCB || + results->state[2] != 0x26) return false; + // TCL112 and MITSUBISHI112 share the exact same checksum. + if (!IRTcl112Ac::validChecksum(results->state, nbits / 8)) return false; + } + // Success + results->decode_type = typeguess; + 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_MITSUBISHI112 || DECODE_TCL112AC + +// Code to emulate Mitsubishi 112bit 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? +IRMitsubishi112::IRMitsubishi112(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 IRMitsubishi112::stateReset(void) { + const uint8_t kReset[kMitsubishi112StateLength] = { + 0x23, 0xCB, 0x26, 0x01, 0x00, 0x24, 0x03, 0x0B, 0x10, + 0x00, 0x00, 0x00, 0x30}; + setRaw(kReset); +} + +/// Calculate the checksum for the current internal state of the remote. +void IRMitsubishi112::checksum(void) { + _.Sum = IRTcl112Ac::calcChecksum(_.raw, kMitsubishi112StateLength); +} + +/// Set up hardware to be able to send a message. +void IRMitsubishi112::begin(void) { _irsend.begin(); } + +#if SEND_MITSUBISHI112 +/// Send the current internal state as an IR message. +/// @param[in] repeat Nr. of times the message will be repeated. +void IRMitsubishi112::send(const uint16_t repeat) { + _irsend.sendMitsubishi112(getRaw(), kMitsubishi112StateLength, repeat); +} +#endif // SEND_MITSUBISHI112 + +/// 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 *IRMitsubishi112::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 IRMitsubishi112::setRaw(const uint8_t *data) { + std::memcpy(_.raw, data, kMitsubishi112StateLength); +} + +/// Set the requested power state of the A/C to off. +void IRMitsubishi112::on(void) { setPower(true); } + +/// Set the requested power state of the A/C to off. +void IRMitsubishi112::off(void) { setPower(false); } + +/// Change the power setting. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRMitsubishi112::setPower(bool on) { + _.Power = on; +} + +/// Get the value of the current power setting. +/// @return true, the setting is on. false, the setting is off. +bool IRMitsubishi112::getPower(void) const { + return _.Power; +} + +/// Set the temperature. +/// @param[in] degrees The temperature in degrees celsius. +void IRMitsubishi112::setTemp(const uint8_t degrees) { + uint8_t temp = std::max((uint8_t)kMitsubishi112MinTemp, degrees); + temp = std::min((uint8_t)kMitsubishi112MaxTemp, temp); + _.Temp = kMitsubishiAcMaxTemp - temp; +} + +/// Get the current temperature setting. +/// @return The current setting for temp. in degrees celsius. +uint8_t IRMitsubishi112::getTemp(void) const { + return kMitsubishiAcMaxTemp - _.Temp; +} + +/// Set the speed of the fan. +/// @param[in] speed The desired setting. +void IRMitsubishi112::setFan(const uint8_t speed) { + switch (speed) { + case kMitsubishi112FanMin: + case kMitsubishi112FanLow: + case kMitsubishi112FanMed: + case kMitsubishi112FanMax: + _.Fan = speed; + break; + default: + _.Fan = kMitsubishi112FanMax; + } +} + +/// Get the current fan speed setting. +/// @return The current fan speed/mode. +uint8_t IRMitsubishi112::getFan(void) const { + return _.Fan; +} + +/// Get the operating mode setting of the A/C. +/// @return The current operating mode setting. +uint8_t IRMitsubishi112::getMode(void) const { + return _.Mode; +} + +/// Set the operating mode of the A/C. +/// @param[in] mode The desired operating mode. +void IRMitsubishi112::setMode(const uint8_t mode) { + // If we get an unexpected mode, default to AUTO. + switch (mode) { + // Note: No Fan Only mode. + case kMitsubishi112Cool: + case kMitsubishi112Heat: + case kMitsubishi112Auto: + case kMitsubishi112Dry: + _.Mode = mode; + break; + default: + _.Mode = kMitsubishi112Auto; + } +} + +/// Set the Vertical Swing mode of the A/C. +/// @param[in] position The position/mode to set the swing to. +void IRMitsubishi112::setSwingV(const uint8_t position) { + // If we get an unexpected mode, default to auto. + switch (position) { + case kMitsubishi112SwingVLowest: + case kMitsubishi112SwingVLow: + case kMitsubishi112SwingVMiddle: + case kMitsubishi112SwingVHigh: + case kMitsubishi112SwingVHighest: + case kMitsubishi112SwingVAuto: + _.SwingV = position; + break; + default: + _.SwingV = kMitsubishi112SwingVAuto; + } +} + +/// Get the Vertical Swing mode of the A/C. +/// @return The native position/mode setting. +uint8_t IRMitsubishi112::getSwingV(void) const { + return _.SwingV; +} + +/// Set the Horizontal Swing mode of the A/C. +/// @param[in] position The position/mode to set the swing to. +void IRMitsubishi112::setSwingH(const uint8_t position) { + // If we get an unexpected mode, default to auto. + switch (position) { + case kMitsubishi112SwingHLeftMax: + case kMitsubishi112SwingHLeft: + case kMitsubishi112SwingHMiddle: + case kMitsubishi112SwingHRight: + case kMitsubishi112SwingHRightMax: + case kMitsubishi112SwingHWide: + case kMitsubishi112SwingHAuto: + _.SwingH = position; + break; + default: + _.SwingH = kMitsubishi112SwingHAuto; + } +} + + +/// Get the Horizontal Swing mode of the A/C. +/// @return The native position/mode setting. +uint8_t IRMitsubishi112::getSwingH(void) const { + return _.SwingH; +} + +/// Set the Quiet mode of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +/// @note There is no true quiet setting on this A/C. +void IRMitsubishi112::setQuiet(bool on) { + if (on) + setFan(kMitsubishi112FanQuiet); + else if (getQuiet()) setFan(kMitsubishi112FanLow); +} + + +/// Get the Quiet mode of the A/C. +/// @return true, the setting is on. false, the setting is off. +/// @note There is no true quiet setting on this A/C. +bool IRMitsubishi112::getQuiet(void) const { + return _.Fan == kMitsubishi112FanQuiet; +} + +/// 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 IRMitsubishi112::convertMode(const stdAc::opmode_t mode) { + switch (mode) { + case stdAc::opmode_t::kCool: return kMitsubishi112Cool; + case stdAc::opmode_t::kHeat: return kMitsubishi112Heat; + case stdAc::opmode_t::kDry: return kMitsubishi112Dry; + // Note: No Fan Only mode. + default: return kMitsubishi112Auto; + } +} + +/// 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 IRMitsubishi112::convertFan(const stdAc::fanspeed_t speed) { + switch (speed) { + case stdAc::fanspeed_t::kMin: return kMitsubishi112FanMin; + case stdAc::fanspeed_t::kLow: return kMitsubishi112FanLow; + case stdAc::fanspeed_t::kMedium: return kMitsubishi112FanMed; + case stdAc::fanspeed_t::kHigh: + case stdAc::fanspeed_t::kMax: return kMitsubishi112FanMax; + default: return kMitsubishi112FanMed; + } +} + +/// 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 IRMitsubishi112::convertSwingV(const stdAc::swingv_t position) { + switch (position) { + case stdAc::swingv_t::kHighest: return kMitsubishi112SwingVHighest; + case stdAc::swingv_t::kHigh: return kMitsubishi112SwingVHigh; + case stdAc::swingv_t::kMiddle: return kMitsubishi112SwingVMiddle; + case stdAc::swingv_t::kLow: return kMitsubishi112SwingVLow; + case stdAc::swingv_t::kLowest: return kMitsubishi112SwingVLowest; + default: return kMitsubishi112SwingVAuto; + } +} + +/// Convert a stdAc::swingh_t enum into it's native setting. +/// @param[in] position The enum to be converted. +/// @return The native equivalent of the enum. +uint8_t IRMitsubishi112::convertSwingH(const stdAc::swingh_t position) { + switch (position) { + case stdAc::swingh_t::kLeftMax: return kMitsubishi112SwingHLeftMax; + case stdAc::swingh_t::kLeft: return kMitsubishi112SwingHLeft; + case stdAc::swingh_t::kMiddle: return kMitsubishi112SwingHMiddle; + case stdAc::swingh_t::kRight: return kMitsubishi112SwingHRight; + case stdAc::swingh_t::kRightMax: return kMitsubishi112SwingHRightMax; + case stdAc::swingh_t::kWide: return kMitsubishi112SwingHWide; + case stdAc::swingh_t::kAuto: return kMitsubishi112SwingHAuto; + default: return kMitsubishi112SwingHAuto; + } +} + +/// 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 IRMitsubishi112::toCommonMode(const uint8_t mode) { + switch (mode) { + case kMitsubishi112Cool: return stdAc::opmode_t::kCool; + case kMitsubishi112Heat: return stdAc::opmode_t::kHeat; + case kMitsubishi112Dry: return stdAc::opmode_t::kDry; + 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 IRMitsubishi112::toCommonFanSpeed(const uint8_t speed) { + switch (speed) { + case kMitsubishi112FanMax: return stdAc::fanspeed_t::kMax; + case kMitsubishi112FanMed: return stdAc::fanspeed_t::kMedium; + case kMitsubishi112FanLow: return stdAc::fanspeed_t::kLow; + case kMitsubishi112FanMin: return stdAc::fanspeed_t::kMin; + default: return stdAc::fanspeed_t::kMedium; + } +} + +/// 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 IRMitsubishi112::toCommonSwingV(const uint8_t pos) { + switch (pos) { + case kMitsubishi112SwingVHighest: return stdAc::swingv_t::kHighest; + case kMitsubishi112SwingVHigh: return stdAc::swingv_t::kHigh; + case kMitsubishi112SwingVMiddle: return stdAc::swingv_t::kMiddle; + case kMitsubishi112SwingVLow: return stdAc::swingv_t::kLow; + case kMitsubishi112SwingVLowest: return stdAc::swingv_t::kLowest; + default: return stdAc::swingv_t::kAuto; + } +} + +/// Convert a native horizontal swing postion to it's common equivalent. +/// @param[in] pos A native position to convert. +/// @return The common horizontal swing position. +stdAc::swingh_t IRMitsubishi112::toCommonSwingH(const uint8_t pos) { + switch (pos) { + case kMitsubishi112SwingHLeftMax: return stdAc::swingh_t::kLeftMax; + case kMitsubishi112SwingHLeft: return stdAc::swingh_t::kLeft; + case kMitsubishi112SwingHMiddle: return stdAc::swingh_t::kMiddle; + case kMitsubishi112SwingHRight: return stdAc::swingh_t::kRight; + case kMitsubishi112SwingHRightMax: return stdAc::swingh_t::kRightMax; + case kMitsubishi112SwingHWide: return stdAc::swingh_t::kWide; + default: return stdAc::swingh_t::kAuto; + } +} + +/// Convert the current internal state into its stdAc::state_t equivalent. +/// @return The stdAc equivalent of the native settings. +stdAc::state_t IRMitsubishi112::toCommon(void) const { + stdAc::state_t result; + result.protocol = decode_type_t::MITSUBISHI112; + 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 = toCommonSwingV(_.SwingV); + result.swingh = toCommonSwingH(_.SwingH);; + result.quiet = getQuiet(); + // Not supported. + result.econo = false; // Need to figure this part from stdAc + result.clock = -1; + result.sleep = -1; + result.turbo = false; + result.clean = false; + result.filter = false; + result.light = 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 IRMitsubishi112::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, kMitsubishi112Auto, kMitsubishi112Cool, + kMitsubishi112Heat, kMitsubishi112Dry, + kMitsubishi112Auto); + result += addTempToString(getTemp()); + result += addFanToString(_.Fan, kMitsubishi112FanMax, + kMitsubishi112FanLow, kMitsubishi112FanMax, + kMitsubishi112FanQuiet, kMitsubishi112FanMed); + result += addSwingVToString(_.SwingV, kMitsubishi112SwingVAuto, + kMitsubishi112SwingVHighest, + kMitsubishi112SwingVHigh, + kMitsubishi112SwingVAuto, // Upper Middle unused. + kMitsubishi112SwingVMiddle, + kMitsubishi112SwingVAuto, // Lower Middle unused. + kMitsubishi112SwingVLow, + kMitsubishi112SwingVLowest, + // Below are unused. + kMitsubishi112SwingVAuto, + kMitsubishi112SwingVAuto, + kMitsubishi112SwingVAuto, + kMitsubishi112SwingVAuto); + result += addSwingHToString(_.SwingH, kMitsubishi112SwingHAuto, + kMitsubishi112SwingHLeftMax, + kMitsubishi112SwingHLeft, + kMitsubishi112SwingHMiddle, + kMitsubishi112SwingHRight, + kMitsubishi112SwingHRightMax, + kMitsubishi112SwingHAuto, // Unused + kMitsubishi112SwingHAuto, // Unused + kMitsubishi112SwingHAuto, // Unused + kMitsubishi112SwingHAuto, // Unused + kMitsubishi112SwingHWide); + result += addBoolToString(getQuiet(), kQuietStr); + return result; +} diff --git a/lib/IRremoteESP8266/src/ir_Mitsubishi.h b/lib/IRremoteESP8266/src/ir_Mitsubishi.h index bc2c4152a1..1f3a421840 100644 --- a/lib/IRremoteESP8266/src/ir_Mitsubishi.h +++ b/lib/IRremoteESP8266/src/ir_Mitsubishi.h @@ -43,10 +43,10 @@ #ifndef UNIT_TEST #include #endif -#include "../src/IRremoteESP8266.h" -#include "../src/IRsend.h" +#include "IRremoteESP8266.h" +#include "IRsend.h" #ifdef UNIT_TEST -#include "../src/IRsend_test.h" +#include "IRsend_test.h" #endif /// Native representation of a Mitsubishi 144-bit A/C message. diff --git a/lib/IRremoteESP8266/src/ir_MitsubishiHeavy.cpp b/lib/IRremoteESP8266/src/ir_MitsubishiHeavy.cpp new file mode 100644 index 0000000000..b4a6ffc352 --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_MitsubishiHeavy.cpp @@ -0,0 +1,1050 @@ +// 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 + +#include "ir_MitsubishiHeavy.h" +#include +#include +#include "IRremoteESP8266.h" +#include "IRtext.h" +#include "IRutils.h" +#ifndef ARDUINO +#include +#endif + +// Constants +const uint16_t kMitsubishiHeavyHdrMark = 3140; +const uint16_t kMitsubishiHeavyHdrSpace = 1630; +const uint16_t kMitsubishiHeavyBitMark = 370; +const uint16_t kMitsubishiHeavyOneSpace = 420; +const uint16_t kMitsubishiHeavyZeroSpace = 1220; +const uint32_t kMitsubishiHeavyGap = kDefaultMessageGap; // Just a guess. + +using irutils::addBoolToString; +using irutils::addIntToString; +using irutils::addLabeledString; +using irutils::addModeToString; +using irutils::addSwingHToString; +using irutils::addSwingVToString; +using irutils::addTempToString; +using irutils::checkInvertedBytePairs; +using irutils::invertBytePairs; + +#if SEND_MITSUBISHIHEAVY +/// Send a MitsubishiHeavy 88-bit A/C message. +/// Status: BETA / Appears to be working. Needs testing against a real device. +/// @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::sendMitsubishiHeavy88(const unsigned char data[], + const uint16_t nbytes, + const uint16_t repeat) { + if (nbytes < kMitsubishiHeavy88StateLength) + return; // Not enough bytes to send a proper message. + sendGeneric(kMitsubishiHeavyHdrMark, kMitsubishiHeavyHdrSpace, + kMitsubishiHeavyBitMark, kMitsubishiHeavyOneSpace, + kMitsubishiHeavyBitMark, kMitsubishiHeavyZeroSpace, + kMitsubishiHeavyBitMark, kMitsubishiHeavyGap, + data, nbytes, 38000, false, repeat, kDutyDefault); +} + +/// Send a MitsubishiHeavy 152-bit A/C message. +/// Status: BETA / Appears to be working. Needs testing against a real device. +/// @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::sendMitsubishiHeavy152(const unsigned char data[], + const uint16_t nbytes, + const uint16_t repeat) { + if (nbytes < kMitsubishiHeavy152StateLength) + return; // Not enough bytes to send a proper message. + sendMitsubishiHeavy88(data, nbytes, repeat); +} +#endif // SEND_MITSUBISHIHEAVY + +// Class for decoding and constructing MitsubishiHeavy152 AC messages. + +/// 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? +IRMitsubishiHeavy152Ac::IRMitsubishiHeavy152Ac(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 IRMitsubishiHeavy152Ac::begin(void) { _irsend.begin(); } + +#if SEND_MITSUBISHIHEAVY +/// Send the current internal state as an IR message. +/// @param[in] repeat Nr. of times the message will be repeated. +void IRMitsubishiHeavy152Ac::send(const uint16_t repeat) { + _irsend.sendMitsubishiHeavy152(getRaw(), kMitsubishiHeavy152StateLength, + repeat); +} +#endif // SEND_MITSUBISHIHEAVY + +/// Reset the state of the remote to a known good state/sequence. +void IRMitsubishiHeavy152Ac::stateReset(void) { + std::memcpy(_.raw, kMitsubishiHeavyZmsSig, kMitsubishiHeavySigLength); + for (uint8_t i = kMitsubishiHeavySigLength; + i < kMitsubishiHeavy152StateLength - 3; i += 2) _.raw[i] = 0; + _.raw[17] = 0x80; +} + +/// 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 *IRMitsubishiHeavy152Ac::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 IRMitsubishiHeavy152Ac::setRaw(const uint8_t *data) { + std::memcpy(_.raw, data, kMitsubishiHeavy152StateLength); +} + +/// Set the requested power state of the A/C to on. +void IRMitsubishiHeavy152Ac::on(void) { setPower(true); } + +/// Set the requested power state of the A/C to off. +void IRMitsubishiHeavy152Ac::off(void) { setPower(false); } + +/// Change the power setting. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRMitsubishiHeavy152Ac::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 IRMitsubishiHeavy152Ac::getPower(void) const { + return _.Power; +} + +/// Set the temperature. +/// @param[in] temp The temperature in degrees celsius. +void IRMitsubishiHeavy152Ac::setTemp(const uint8_t temp) { + uint8_t newtemp = temp; + newtemp = std::min(newtemp, kMitsubishiHeavyMaxTemp); + newtemp = std::max(newtemp, kMitsubishiHeavyMinTemp); + _.Temp = newtemp - kMitsubishiHeavyMinTemp; +} + +/// Get the current temperature setting. +/// @return The current setting for temp. in degrees celsius. +uint8_t IRMitsubishiHeavy152Ac::getTemp(void) const { + return _.Temp + kMitsubishiHeavyMinTemp; +} + +/// Set the speed of the fan. +/// @param[in] speed The desired setting. +void IRMitsubishiHeavy152Ac::setFan(const uint8_t speed) { + uint8_t newspeed = speed; + switch (speed) { + case kMitsubishiHeavy152FanLow: + case kMitsubishiHeavy152FanMed: + case kMitsubishiHeavy152FanHigh: + case kMitsubishiHeavy152FanMax: + case kMitsubishiHeavy152FanEcono: + case kMitsubishiHeavy152FanTurbo: break; + default: newspeed = kMitsubishiHeavy152FanAuto; + } + _.Fan = newspeed; +} + +/// Get the current fan speed setting. +/// @return The current fan speed/mode. +uint8_t IRMitsubishiHeavy152Ac::getFan(void) const { + return _.Fan; +} + +/// Set the operating mode of the A/C. +/// @param[in] mode The desired operating mode. +void IRMitsubishiHeavy152Ac::setMode(const uint8_t mode) { + uint8_t newmode = mode; + switch (mode) { + case kMitsubishiHeavyCool: + case kMitsubishiHeavyDry: + case kMitsubishiHeavyFan: + case kMitsubishiHeavyHeat: + break; + default: + newmode = kMitsubishiHeavyAuto; + } + _.Mode = newmode; +} + +/// Get the operating mode setting of the A/C. +/// @return The current operating mode setting. +uint8_t IRMitsubishiHeavy152Ac::getMode(void) const { + return _.Mode; +} + +/// Set the Vertical Swing mode of the A/C. +/// @param[in] pos The position/mode to set the swing to. +void IRMitsubishiHeavy152Ac::setSwingVertical(const uint8_t pos) { + _.SwingV = std::min(pos, kMitsubishiHeavy152SwingVOff); +} + +/// Get the Vertical Swing mode of the A/C. +/// @return The native position/mode setting. +uint8_t IRMitsubishiHeavy152Ac::getSwingVertical(void) const { + return _.SwingV; +} + +/// Set the Horizontal Swing mode of the A/C. +/// @param[in] pos The position/mode to set the swing to. +void IRMitsubishiHeavy152Ac::setSwingHorizontal(const uint8_t pos) { + _.SwingH = std::min(pos, kMitsubishiHeavy152SwingHOff); +} + +/// Get the Horizontal Swing mode of the A/C. +/// @return The native position/mode setting. +uint8_t IRMitsubishiHeavy152Ac::getSwingHorizontal(void) const { + return _.SwingH; +} + +/// Set the Night (Sleep) mode of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRMitsubishiHeavy152Ac::setNight(const bool on) { + _.Night = on; +} + +/// Get the Night (Sleep) mode of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRMitsubishiHeavy152Ac::getNight(void) const { + return _.Night; +} + +/// Set the 3D mode of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRMitsubishiHeavy152Ac::set3D(const bool on) { + if (on) + { _.Three = 1; _.D = 1; } + else + { _.Three = 0; _.D = 0; } +} + +/// Get the 3D mode of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRMitsubishiHeavy152Ac::get3D(void) const { + return _.Three && _.D; +} + +/// Set the Silent (Quiet) mode of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRMitsubishiHeavy152Ac::setSilent(const bool on) { + _.Silent = on; +} + +/// Get the Silent (Quiet) mode of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRMitsubishiHeavy152Ac::getSilent(void) const { + return _.Silent; +} + +/// Set the Filter mode of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRMitsubishiHeavy152Ac::setFilter(const bool on) { + _.Filter = on; +} + +/// Get the Filter mode of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRMitsubishiHeavy152Ac::getFilter(void) const { + return _.Filter; +} + +/// Set the Clean mode of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRMitsubishiHeavy152Ac::setClean(const bool on) { + _.Filter = on; + _.Clean = on; +} + +/// Get the Clean mode of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRMitsubishiHeavy152Ac::getClean(void) const { + return _.Clean && _.Filter; +} + +/// Set the Turbo mode of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRMitsubishiHeavy152Ac::setTurbo(const bool on) { + if (on) + setFan(kMitsubishiHeavy152FanTurbo); + else if (getTurbo()) setFan(kMitsubishiHeavy152FanAuto); +} + +/// Get the Turbo mode of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRMitsubishiHeavy152Ac::getTurbo(void) const { + return _.Fan == kMitsubishiHeavy152FanTurbo; +} + +/// Set the Economical mode of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRMitsubishiHeavy152Ac::setEcono(const bool on) { + if (on) + setFan(kMitsubishiHeavy152FanEcono); + else if (getEcono()) setFan(kMitsubishiHeavy152FanAuto); +} + +/// Get the Economical mode of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRMitsubishiHeavy152Ac::getEcono(void) const { + return _.Fan == kMitsubishiHeavy152FanEcono; +} + +/// Verify the given state has a ZM-S signature. +/// @param[in] state A ptr to a state to be checked. +/// @return true, the check passed. Otherwise, false. +bool IRMitsubishiHeavy152Ac::checkZmsSig(const uint8_t *state) { + for (uint8_t i = 0; i < kMitsubishiHeavySigLength; i++) + if (state[i] != kMitsubishiHeavyZmsSig[i]) return false; + return true; +} + +/// Calculate the checksum for the current internal state of the remote. +/// Note: Technically it has no checksum, but does have inverted byte pairs. +void IRMitsubishiHeavy152Ac::checksum(void) { + const uint8_t kOffset = kMitsubishiHeavySigLength - 2; + invertBytePairs(_.raw + kOffset, kMitsubishiHeavy152StateLength - kOffset); +} + +/// 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 state array. +/// @return true, if the state has a valid checksum. Otherwise, false. +/// Note: Technically it has no checksum, but does have inverted byte pairs. +bool IRMitsubishiHeavy152Ac::validChecksum(const uint8_t *state, + const uint16_t length) { + // Assume anything too short is fine. + if (length < kMitsubishiHeavySigLength) return true; + const uint8_t kOffset = kMitsubishiHeavySigLength - 2; + return checkInvertedBytePairs(state + kOffset, length - kOffset); +} + +/// 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 IRMitsubishiHeavy152Ac::convertMode(const stdAc::opmode_t mode) { + switch (mode) { + case stdAc::opmode_t::kCool: return kMitsubishiHeavyCool; + case stdAc::opmode_t::kHeat: return kMitsubishiHeavyHeat; + case stdAc::opmode_t::kDry: return kMitsubishiHeavyDry; + case stdAc::opmode_t::kFan: return kMitsubishiHeavyFan; + default: return kMitsubishiHeavyAuto; + } +} + +/// 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 IRMitsubishiHeavy152Ac::convertFan(const stdAc::fanspeed_t speed) { + switch (speed) { + // Assumes Econo is slower than Low. + case stdAc::fanspeed_t::kMin: return kMitsubishiHeavy152FanEcono; + case stdAc::fanspeed_t::kLow: return kMitsubishiHeavy152FanLow; + case stdAc::fanspeed_t::kMedium: return kMitsubishiHeavy152FanMed; + case stdAc::fanspeed_t::kHigh: return kMitsubishiHeavy152FanHigh; + case stdAc::fanspeed_t::kMax: return kMitsubishiHeavy152FanMax; + default: return kMitsubishiHeavy152FanAuto; + } +} + +/// 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 IRMitsubishiHeavy152Ac::convertSwingV(const stdAc::swingv_t position) { + switch (position) { + case stdAc::swingv_t::kAuto: return kMitsubishiHeavy152SwingVAuto; + case stdAc::swingv_t::kHighest: return kMitsubishiHeavy152SwingVHighest; + case stdAc::swingv_t::kHigh: return kMitsubishiHeavy152SwingVHigh; + case stdAc::swingv_t::kMiddle: return kMitsubishiHeavy152SwingVMiddle; + case stdAc::swingv_t::kLow: return kMitsubishiHeavy152SwingVLow; + case stdAc::swingv_t::kLowest: return kMitsubishiHeavy152SwingVLowest; + default: return kMitsubishiHeavy152SwingVOff; + } +} + +/// Convert a stdAc::swingh_t enum into it's native setting. +/// @param[in] position The enum to be converted. +/// @return The native equivalent of the enum. +uint8_t IRMitsubishiHeavy152Ac::convertSwingH(const stdAc::swingh_t position) { + switch (position) { + case stdAc::swingh_t::kAuto: return kMitsubishiHeavy152SwingHAuto; + case stdAc::swingh_t::kLeftMax: return kMitsubishiHeavy152SwingHLeftMax; + case stdAc::swingh_t::kLeft: return kMitsubishiHeavy152SwingHLeft; + case stdAc::swingh_t::kMiddle: return kMitsubishiHeavy152SwingHMiddle; + case stdAc::swingh_t::kRight: return kMitsubishiHeavy152SwingHRight; + case stdAc::swingh_t::kRightMax: return kMitsubishiHeavy152SwingHRightMax; + default: return kMitsubishiHeavy152SwingHOff; + } +} + +/// 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 IRMitsubishiHeavy152Ac::toCommonMode(const uint8_t mode) { + switch (mode) { + case kMitsubishiHeavyCool: return stdAc::opmode_t::kCool; + case kMitsubishiHeavyHeat: return stdAc::opmode_t::kHeat; + case kMitsubishiHeavyDry: return stdAc::opmode_t::kDry; + case kMitsubishiHeavyFan: return stdAc::opmode_t::kFan; + default: return stdAc::opmode_t::kAuto; + } +} + +/// Convert a native fan speed into its stdAc equivalent. +/// @param[in] spd The native setting to be converted. +/// @return The stdAc equivalent of the native setting. +stdAc::fanspeed_t IRMitsubishiHeavy152Ac::toCommonFanSpeed(const uint8_t spd) { + switch (spd) { + case kMitsubishiHeavy152FanMax: return stdAc::fanspeed_t::kMax; + case kMitsubishiHeavy152FanHigh: return stdAc::fanspeed_t::kHigh; + case kMitsubishiHeavy152FanMed: return stdAc::fanspeed_t::kMedium; + case kMitsubishiHeavy152FanLow: return stdAc::fanspeed_t::kLow; + case kMitsubishiHeavy152FanEcono: return stdAc::fanspeed_t::kMin; + default: return stdAc::fanspeed_t::kAuto; + } +} + +/// Convert a native horizontal swing postion to it's common equivalent. +/// @param[in] pos A native position to convert. +/// @return The common horizontal swing position. +stdAc::swingh_t IRMitsubishiHeavy152Ac::toCommonSwingH(const uint8_t pos) { + switch (pos) { + case kMitsubishiHeavy152SwingHLeftMax: return stdAc::swingh_t::kLeftMax; + case kMitsubishiHeavy152SwingHLeft: return stdAc::swingh_t::kLeft; + case kMitsubishiHeavy152SwingHMiddle: return stdAc::swingh_t::kMiddle; + case kMitsubishiHeavy152SwingHRight: return stdAc::swingh_t::kRight; + case kMitsubishiHeavy152SwingHRightMax: return stdAc::swingh_t::kRightMax; + case kMitsubishiHeavy152SwingHOff: return stdAc::swingh_t::kOff; + default: return stdAc::swingh_t::kAuto; + } +} + +/// 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 IRMitsubishiHeavy152Ac::toCommonSwingV(const uint8_t pos) { + switch (pos) { + case kMitsubishiHeavy152SwingVHighest: return stdAc::swingv_t::kHighest; + case kMitsubishiHeavy152SwingVHigh: return stdAc::swingv_t::kHigh; + case kMitsubishiHeavy152SwingVMiddle: return stdAc::swingv_t::kMiddle; + case kMitsubishiHeavy152SwingVLow: return stdAc::swingv_t::kLow; + case kMitsubishiHeavy152SwingVLowest: return stdAc::swingv_t::kLowest; + case kMitsubishiHeavy152SwingVOff: return stdAc::swingv_t::kOff; + default: return stdAc::swingv_t::kAuto; + } +} + +/// Convert the current internal state into its stdAc::state_t equivalent. +/// @return The stdAc equivalent of the native settings. +stdAc::state_t IRMitsubishiHeavy152Ac::toCommon(void) const { + stdAc::state_t result; + result.protocol = decode_type_t::MITSUBISHI_HEAVY_152; + 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 = toCommonSwingV(_.SwingV); + result.swingh = toCommonSwingH(_.SwingH); + result.turbo = getTurbo(); + result.econo = getEcono(); + result.clean = getClean(); + result.quiet = _.Silent; + result.filter = _.Filter; + result.sleep = _.Night ? 0 : -1; + // Not supported. + result.light = false; + result.beep = false; + result.clock = -1; + return result; +} + +/// Convert the internal state into a human readable string. +/// @return A string containing the settings in human-readable form. +String IRMitsubishiHeavy152Ac::toString(void) const { + String result = ""; + result.reserve(180); // Reserve some heap for the string to reduce fragging. + result += addBoolToString(_.Power, kPowerStr, false); + result += addModeToString(_.Mode, kMitsubishiHeavyAuto, + kMitsubishiHeavyCool, kMitsubishiHeavyHeat, + kMitsubishiHeavyDry, kMitsubishiHeavyFan); + result += addTempToString(getTemp()); + result += addIntToString(_.Fan, kFanStr); + result += kSpaceLBraceStr; + switch (_.Fan) { + case kMitsubishiHeavy152FanAuto: + result += kAutoStr; + break; + case kMitsubishiHeavy152FanHigh: + result += kHighStr; + break; + case kMitsubishiHeavy152FanLow: + result += kLowStr; + break; + case kMitsubishiHeavy152FanMed: + result += kMedStr; + break; + case kMitsubishiHeavy152FanMax: + result += kMaxStr; + break; + case kMitsubishiHeavy152FanEcono: + result += kEconoStr; + break; + case kMitsubishiHeavy152FanTurbo: + result += kTurboStr; + break; + default: + result += kUnknownStr; + } + result += ')'; + result += addSwingVToString(_.SwingV, kMitsubishiHeavy152SwingVAuto, + kMitsubishiHeavy152SwingVHighest, + kMitsubishiHeavy152SwingVHigh, + kMitsubishiHeavy152SwingVAuto, // UpperMid unused + kMitsubishiHeavy152SwingVMiddle, + kMitsubishiHeavy152SwingVAuto, // LowerMid unused + kMitsubishiHeavy152SwingVLow, + kMitsubishiHeavy152SwingVLowest, + kMitsubishiHeavy152SwingVOff, + // Below are unused. + kMitsubishiHeavy152SwingVAuto, + kMitsubishiHeavy152SwingVAuto, + kMitsubishiHeavy152SwingVAuto); + result += addSwingHToString(_.SwingH, kMitsubishiHeavy152SwingHAuto, + kMitsubishiHeavy152SwingHLeftMax, + kMitsubishiHeavy152SwingHLeft, + kMitsubishiHeavy152SwingHMiddle, + kMitsubishiHeavy152SwingHRight, + kMitsubishiHeavy152SwingHRightMax, + kMitsubishiHeavy152SwingHOff, + kMitsubishiHeavy152SwingHLeftRight, + kMitsubishiHeavy152SwingHRightLeft, + // Below are unused. + kMitsubishiHeavy152SwingHAuto, + kMitsubishiHeavy152SwingHAuto); + result += addBoolToString(_.Silent, kSilentStr); + result += addBoolToString(getTurbo(), kTurboStr); + result += addBoolToString(getEcono(), kEconoStr); + result += addBoolToString(_.Night, kNightStr); + result += addBoolToString(_.Filter, kFilterStr); + result += addBoolToString(get3D(), k3DStr); + result += addBoolToString(getClean(), kCleanStr); + return result; +} + + +// Class for decoding and constructing MitsubishiHeavy88 AC messages. + +/// 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? +IRMitsubishiHeavy88Ac::IRMitsubishiHeavy88Ac(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 IRMitsubishiHeavy88Ac::begin(void) { _irsend.begin(); } + +#if SEND_MITSUBISHIHEAVY +/// Send the current internal state as an IR message. +/// @param[in] repeat Nr. of times the message will be repeated. +void IRMitsubishiHeavy88Ac::send(const uint16_t repeat) { + _irsend.sendMitsubishiHeavy88(getRaw(), kMitsubishiHeavy88StateLength, + repeat); +} +#endif // SEND_MITSUBISHIHEAVY + +/// Reset the state of the remote to a known good state/sequence. +void IRMitsubishiHeavy88Ac::stateReset(void) { + std::memcpy(_.raw, kMitsubishiHeavyZjsSig, kMitsubishiHeavySigLength); + for (uint8_t i = kMitsubishiHeavySigLength; i < kMitsubishiHeavy88StateLength; + i++) _.raw[i] = 0; +} + +/// 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 *IRMitsubishiHeavy88Ac::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 IRMitsubishiHeavy88Ac::setRaw(const uint8_t *data) { + std::memcpy(_.raw, data, kMitsubishiHeavy88StateLength); +} + +/// Set the requested power state of the A/C to on. +void IRMitsubishiHeavy88Ac::on(void) { setPower(true); } + +/// Set the requested power state of the A/C to off. +void IRMitsubishiHeavy88Ac::off(void) { setPower(false); } + +/// Change the power setting. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRMitsubishiHeavy88Ac::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 IRMitsubishiHeavy88Ac::getPower(void) const { + return _.Power; +} + +/// Set the temperature. +/// @param[in] temp The temperature in degrees celsius. +void IRMitsubishiHeavy88Ac::setTemp(const uint8_t temp) { + uint8_t newtemp = temp; + newtemp = std::min(newtemp, kMitsubishiHeavyMaxTemp); + newtemp = std::max(newtemp, kMitsubishiHeavyMinTemp); + _.Temp = newtemp - kMitsubishiHeavyMinTemp; +} + +/// Get the current temperature setting. +/// @return The current setting for temp. in degrees celsius. +uint8_t IRMitsubishiHeavy88Ac::getTemp(void) const { + return _.Temp + kMitsubishiHeavyMinTemp; +} + +/// Set the speed of the fan. +/// @param[in] speed The desired setting. +void IRMitsubishiHeavy88Ac::setFan(const uint8_t speed) { + uint8_t newspeed = speed; + switch (speed) { + case kMitsubishiHeavy88FanLow: + case kMitsubishiHeavy88FanMed: + case kMitsubishiHeavy88FanHigh: + case kMitsubishiHeavy88FanTurbo: + case kMitsubishiHeavy88FanEcono: break; + default: newspeed = kMitsubishiHeavy88FanAuto; + } + _.Fan = newspeed; +} + +/// Get the current fan speed setting. +/// @return The current fan speed/mode. +uint8_t IRMitsubishiHeavy88Ac::getFan(void) const { + return _.Fan; +} + +/// Set the operating mode of the A/C. +/// @param[in] mode The desired operating mode. +void IRMitsubishiHeavy88Ac::setMode(const uint8_t mode) { + uint8_t newmode = mode; + switch (mode) { + case kMitsubishiHeavyCool: + case kMitsubishiHeavyDry: + case kMitsubishiHeavyFan: + case kMitsubishiHeavyHeat: + break; + default: + newmode = kMitsubishiHeavyAuto; + } + _.Mode = newmode; +} + +/// Get the operating mode setting of the A/C. +/// @return The current operating mode setting. +uint8_t IRMitsubishiHeavy88Ac::getMode(void) const { + return _.Mode; +} + +/// Set the Vertical Swing mode of the A/C. +/// @param[in] pos The position/mode to set the swing to. +void IRMitsubishiHeavy88Ac::setSwingVertical(const uint8_t pos) { + uint8_t newpos; + switch (pos) { + case kMitsubishiHeavy88SwingVAuto: + case kMitsubishiHeavy88SwingVHighest: + case kMitsubishiHeavy88SwingVHigh: + case kMitsubishiHeavy88SwingVMiddle: + case kMitsubishiHeavy88SwingVLow: + case kMitsubishiHeavy88SwingVLowest: newpos = pos; break; + default: newpos = kMitsubishiHeavy88SwingVOff; + } + _.SwingV5 = newpos; + _.SwingV7 = (newpos >> kMitsubishiHeavy88SwingVByte5Size); +} + +/// Get the Vertical Swing mode of the A/C. +/// @return The native position/mode setting. +uint8_t IRMitsubishiHeavy88Ac::getSwingVertical(void) const { + return _.SwingV5 | (_.SwingV7 << kMitsubishiHeavy88SwingVByte5Size); +} + +/// Set the Horizontal Swing mode of the A/C. +/// @param[in] pos The position/mode to set the swing to. +void IRMitsubishiHeavy88Ac::setSwingHorizontal(const uint8_t pos) { + uint8_t newpos; + switch (pos) { + case kMitsubishiHeavy88SwingHAuto: + case kMitsubishiHeavy88SwingHLeftMax: + case kMitsubishiHeavy88SwingHLeft: + case kMitsubishiHeavy88SwingHMiddle: + case kMitsubishiHeavy88SwingHRight: + case kMitsubishiHeavy88SwingHRightMax: + case kMitsubishiHeavy88SwingHLeftRight: + case kMitsubishiHeavy88SwingHRightLeft: + case kMitsubishiHeavy88SwingH3D: newpos = pos; break; + default: newpos = kMitsubishiHeavy88SwingHOff; + } + _.SwingH1 = newpos; + _.SwingH2 = (newpos >> kMitsubishiHeavy88SwingHSize); +} + +/// Get the Horizontal Swing mode of the A/C. +/// @return The native position/mode setting. +uint8_t IRMitsubishiHeavy88Ac::getSwingHorizontal(void) const { + return _.SwingH1 | (_.SwingH2 << kMitsubishiHeavy88SwingHSize); +} + +/// Set the Turbo mode of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRMitsubishiHeavy88Ac::setTurbo(const bool on) { + if (on) + setFan(kMitsubishiHeavy88FanTurbo); + else if (getTurbo()) setFan(kMitsubishiHeavy88FanAuto); +} + +/// Get the Turbo mode of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRMitsubishiHeavy88Ac::getTurbo(void) const { + return _.Fan == kMitsubishiHeavy88FanTurbo; +} + +/// Set the Economical mode of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRMitsubishiHeavy88Ac::setEcono(const bool on) { + if (on) + setFan(kMitsubishiHeavy88FanEcono); + else if (getEcono()) setFan(kMitsubishiHeavy88FanAuto); +} + +/// Get the Economical mode of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRMitsubishiHeavy88Ac::getEcono(void) const { + return _.Fan == kMitsubishiHeavy88FanEcono; +} + +/// Set the 3D mode of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRMitsubishiHeavy88Ac::set3D(const bool on) { + if (on) + setSwingHorizontal(kMitsubishiHeavy88SwingH3D); + else if (get3D()) + setSwingHorizontal(kMitsubishiHeavy88SwingHOff); +} + +/// Get the 3D mode of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRMitsubishiHeavy88Ac::get3D(void) const { + return getSwingHorizontal() == kMitsubishiHeavy88SwingH3D; +} + +/// Set the Clean mode of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRMitsubishiHeavy88Ac::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 IRMitsubishiHeavy88Ac::getClean(void) const { + return _.Clean; +} + +/// Verify the given state has a ZJ-S signature. +/// @param[in] state A ptr to a state to be checked. +/// @return true, the check passed. Otherwise, false. +bool IRMitsubishiHeavy88Ac::checkZjsSig(const uint8_t *state) { + for (uint8_t i = 0; i < kMitsubishiHeavySigLength; i++) + if (state[i] != kMitsubishiHeavyZjsSig[i]) return false; + return true; +} + +/// Calculate the checksum for the current internal state of the remote. +/// Note: Technically it has no checksum, but does have inverted byte pairs. +void IRMitsubishiHeavy88Ac::checksum(void) { + const uint8_t kOffset = kMitsubishiHeavySigLength - 2; + invertBytePairs(_.raw + kOffset, kMitsubishiHeavy88StateLength - kOffset); +} + +/// 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 state array. +/// @return true, if the state has a valid checksum. Otherwise, false. +/// Note: Technically it has no checksum, but does have inverted byte pairs. +bool IRMitsubishiHeavy88Ac::validChecksum(const uint8_t *state, + const uint16_t length) { + return IRMitsubishiHeavy152Ac::validChecksum(state, length); +} + +/// 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 IRMitsubishiHeavy88Ac::convertMode(const stdAc::opmode_t mode) { + return IRMitsubishiHeavy152Ac::convertMode(mode); +} + +/// 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 IRMitsubishiHeavy88Ac::convertFan(const stdAc::fanspeed_t speed) { + switch (speed) { + // Assumes Econo is slower than Low. + case stdAc::fanspeed_t::kMin: return kMitsubishiHeavy88FanEcono; + case stdAc::fanspeed_t::kLow: return kMitsubishiHeavy88FanLow; + case stdAc::fanspeed_t::kMedium: return kMitsubishiHeavy88FanMed; + case stdAc::fanspeed_t::kHigh: return kMitsubishiHeavy88FanHigh; + case stdAc::fanspeed_t::kMax: return kMitsubishiHeavy88FanTurbo; + default: return kMitsubishiHeavy88FanAuto; + } +} + +/// 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 IRMitsubishiHeavy88Ac::convertSwingV(const stdAc::swingv_t position) { + switch (position) { + case stdAc::swingv_t::kAuto: return kMitsubishiHeavy88SwingVAuto; + case stdAc::swingv_t::kHighest: return kMitsubishiHeavy88SwingVHighest; + case stdAc::swingv_t::kHigh: return kMitsubishiHeavy88SwingVHigh; + case stdAc::swingv_t::kMiddle: return kMitsubishiHeavy88SwingVMiddle; + case stdAc::swingv_t::kLow: return kMitsubishiHeavy88SwingVLow; + case stdAc::swingv_t::kLowest: return kMitsubishiHeavy88SwingVLowest; + default: return kMitsubishiHeavy88SwingVOff; + } +} + +/// Convert a stdAc::swingh_t enum into it's native setting. +/// @param[in] position The enum to be converted. +/// @return The native equivalent of the enum. +uint8_t IRMitsubishiHeavy88Ac::convertSwingH(const stdAc::swingh_t position) { + switch (position) { + case stdAc::swingh_t::kAuto: return kMitsubishiHeavy88SwingHAuto; + case stdAc::swingh_t::kLeftMax: return kMitsubishiHeavy88SwingHLeftMax; + case stdAc::swingh_t::kLeft: return kMitsubishiHeavy88SwingHLeft; + case stdAc::swingh_t::kMiddle: return kMitsubishiHeavy88SwingHMiddle; + case stdAc::swingh_t::kRight: return kMitsubishiHeavy88SwingHRight; + case stdAc::swingh_t::kRightMax: return kMitsubishiHeavy88SwingHRightMax; + default: return kMitsubishiHeavy88SwingHOff; + } +} + +/// 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 IRMitsubishiHeavy88Ac::toCommonFanSpeed(const uint8_t speed) { + switch (speed) { + case kMitsubishiHeavy88FanTurbo: return stdAc::fanspeed_t::kMax; + case kMitsubishiHeavy88FanHigh: return stdAc::fanspeed_t::kHigh; + case kMitsubishiHeavy88FanMed: return stdAc::fanspeed_t::kMedium; + case kMitsubishiHeavy88FanLow: return stdAc::fanspeed_t::kLow; + case kMitsubishiHeavy88FanEcono: return stdAc::fanspeed_t::kMin; + default: return stdAc::fanspeed_t::kAuto; + } +} + +/// Convert a native horizontal swing postion to it's common equivalent. +/// @param[in] pos A native position to convert. +/// @return The common horizontal swing position. +stdAc::swingh_t IRMitsubishiHeavy88Ac::toCommonSwingH(const uint8_t pos) { + switch (pos) { + case kMitsubishiHeavy88SwingHLeftMax: return stdAc::swingh_t::kLeftMax; + case kMitsubishiHeavy88SwingHLeft: return stdAc::swingh_t::kLeft; + case kMitsubishiHeavy88SwingHMiddle: return stdAc::swingh_t::kMiddle; + case kMitsubishiHeavy88SwingHRight: return stdAc::swingh_t::kRight; + case kMitsubishiHeavy88SwingHRightMax: return stdAc::swingh_t::kRightMax; + case kMitsubishiHeavy88SwingHOff: return stdAc::swingh_t::kOff; + default: return stdAc::swingh_t::kAuto; + } +} + +/// 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 IRMitsubishiHeavy88Ac::toCommonSwingV(const uint8_t pos) { + switch (pos) { + case kMitsubishiHeavy88SwingVHighest: return stdAc::swingv_t::kHighest; + case kMitsubishiHeavy88SwingVHigh: return stdAc::swingv_t::kHigh; + case kMitsubishiHeavy88SwingVMiddle: return stdAc::swingv_t::kMiddle; + case kMitsubishiHeavy88SwingVLow: return stdAc::swingv_t::kLow; + case kMitsubishiHeavy88SwingVLowest: return stdAc::swingv_t::kLowest; + case kMitsubishiHeavy88SwingVOff: return stdAc::swingv_t::kOff; + default: return stdAc::swingv_t::kAuto; + } +} + +/// Convert the current internal state into its stdAc::state_t equivalent. +/// @return The stdAc equivalent of the native settings. +stdAc::state_t IRMitsubishiHeavy88Ac::toCommon(void) const { + stdAc::state_t result; + result.protocol = decode_type_t::MITSUBISHI_HEAVY_88; + result.model = -1; // No models used. + result.power = _.Power; + result.mode = IRMitsubishiHeavy152Ac::toCommonMode(_.Mode); + result.celsius = true; + result.degrees = getTemp(); + result.fanspeed = toCommonFanSpeed(_.Fan); + result.swingv = toCommonSwingV(getSwingVertical()); + result.swingh = toCommonSwingH(getSwingHorizontal()); + result.turbo = getTurbo(); + result.econo = getEcono(); + result.clean = _.Clean; + // Not supported. + result.quiet = false; + result.filter = false; + result.light = false; + result.beep = false; + result.sleep = -1; + result.clock = -1; + return result; +} + +/// Convert the internal state into a human readable string. +/// @return A string containing the settings in human-readable form. +String IRMitsubishiHeavy88Ac::toString(void) const { + String result = ""; + result.reserve(140); // Reserve some heap for the string to reduce fragging. + result += addBoolToString(_.Power, kPowerStr, false); + result += addModeToString(_.Mode, kMitsubishiHeavyAuto, + kMitsubishiHeavyCool, kMitsubishiHeavyHeat, + kMitsubishiHeavyDry, kMitsubishiHeavyFan); + result += addTempToString(getTemp()); + result += addIntToString(_.Fan, kFanStr); + result += kSpaceLBraceStr; + switch (_.Fan) { + case kMitsubishiHeavy88FanAuto: + result += kAutoStr; + break; + case kMitsubishiHeavy88FanHigh: + result += kHighStr; + break; + case kMitsubishiHeavy88FanLow: + result += kLowStr; + break; + case kMitsubishiHeavy88FanMed: + result += kMedStr; + break; + case kMitsubishiHeavy88FanEcono: + result += kEconoStr; + break; + case kMitsubishiHeavy88FanTurbo: + result += kTurboStr; + break; + default: + result += kUnknownStr; + } + result += ')'; + result += addSwingVToString(getSwingVertical(), kMitsubishiHeavy88SwingVAuto, + kMitsubishiHeavy88SwingVHighest, + kMitsubishiHeavy88SwingVHigh, + kMitsubishiHeavy88SwingVAuto, // UpperMid unused + kMitsubishiHeavy88SwingVMiddle, + kMitsubishiHeavy88SwingVAuto, // LowerMid unused + kMitsubishiHeavy88SwingVLow, + kMitsubishiHeavy88SwingVLowest, + kMitsubishiHeavy88SwingVOff, + // Below are unused. + kMitsubishiHeavy88SwingVAuto, + kMitsubishiHeavy88SwingVAuto, + kMitsubishiHeavy88SwingVAuto); + result += addSwingHToString(getSwingHorizontal(), + kMitsubishiHeavy88SwingHAuto, + kMitsubishiHeavy88SwingHLeftMax, + kMitsubishiHeavy88SwingHLeft, + kMitsubishiHeavy88SwingHMiddle, + kMitsubishiHeavy88SwingHRight, + kMitsubishiHeavy88SwingHRightMax, + kMitsubishiHeavy88SwingHOff, + kMitsubishiHeavy88SwingHLeftRight, + kMitsubishiHeavy88SwingHRightLeft, + kMitsubishiHeavy88SwingH3D, + // Below are unused. + kMitsubishiHeavy88SwingHAuto); + result += addBoolToString(getTurbo(), kTurboStr); + result += addBoolToString(getEcono(), kEconoStr); + result += addBoolToString(get3D(), k3DStr); + result += addBoolToString(_.Clean, kCleanStr); + return result; +} + +#if DECODE_MITSUBISHIHEAVY +/// Decode the supplied Mitsubishi Heavy Industries A/C message. +/// Status: BETA / Appears to be working. 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 kMitsubishiHeavy88Bits or kMitsubishiHeavy152Bits (def). +/// @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::decodeMitsubishiHeavy(decode_results* results, uint16_t offset, + const uint16_t nbits, const bool strict) { + if (strict) { + switch (nbits) { + case kMitsubishiHeavy88Bits: + case kMitsubishiHeavy152Bits: + break; + default: + return false; // Not what is expected + } + } + + uint16_t used; + used = matchGeneric(results->rawbuf + offset, results->state, + results->rawlen - offset, nbits, + kMitsubishiHeavyHdrMark, kMitsubishiHeavyHdrSpace, + kMitsubishiHeavyBitMark, kMitsubishiHeavyOneSpace, + kMitsubishiHeavyBitMark, kMitsubishiHeavyZeroSpace, + kMitsubishiHeavyBitMark, kMitsubishiHeavyGap, true, + _tolerance, 0, false); + if (used == 0) return false; + offset += used; + // Compliance + switch (nbits) { + case kMitsubishiHeavy88Bits: + if (strict && !(IRMitsubishiHeavy88Ac::checkZjsSig(results->state) && + IRMitsubishiHeavy88Ac::validChecksum(results->state))) + return false; + results->decode_type = MITSUBISHI_HEAVY_88; + break; + case kMitsubishiHeavy152Bits: + if (strict && !(IRMitsubishiHeavy152Ac::checkZmsSig(results->state) && + IRMitsubishiHeavy152Ac::validChecksum(results->state))) + return false; + results->decode_type = MITSUBISHI_HEAVY_152; + break; + default: + return false; + } + + // Success + 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_MITSUBISHIHEAVY diff --git a/lib/IRremoteESP8266/src/ir_MitsubishiHeavy.h b/lib/IRremoteESP8266/src/ir_MitsubishiHeavy.h index 71b0138476..b1059f12fa 100644 --- a/lib/IRremoteESP8266/src/ir_MitsubishiHeavy.h +++ b/lib/IRremoteESP8266/src/ir_MitsubishiHeavy.h @@ -23,10 +23,10 @@ #ifndef UNIT_TEST #include #endif -#include "../src/IRremoteESP8266.h" -#include "../src/IRsend.h" +#include "IRremoteESP8266.h" +#include "IRsend.h" #ifdef UNIT_TEST -#include "../src/IRsend_test.h" +#include "IRsend_test.h" #endif /// Native representation of a Mitsubishi Heavy 152-bit A/C message. diff --git a/lib/IRremoteESP8266/src/ir_Multibrackets.cpp b/lib/IRremoteESP8266/src/ir_Multibrackets.cpp new file mode 100644 index 0000000000..2367b49bc9 --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_Multibrackets.cpp @@ -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 diff --git a/lib/IRremoteESP8266/src/ir_NEC.cpp b/lib/IRremoteESP8266/src/ir_NEC.cpp new file mode 100644 index 0000000000..9d4af76405 --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_NEC.cpp @@ -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 +#include +#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) diff --git a/lib/IRremoteESP8266/src/ir_NEC.h b/lib/IRremoteESP8266/src/ir_NEC.h index d7603304e1..95da064b78 100644 --- a/lib/IRremoteESP8266/src/ir_NEC.h +++ b/lib/IRremoteESP8266/src/ir_NEC.h @@ -19,7 +19,7 @@ #define IR_NEC_H_ #include -#include "../src/IRremoteESP8266.h" +#include "IRremoteESP8266.h" // Constants const uint16_t kNecTick = 560; diff --git a/lib/IRremoteESP8266/src/ir_Neoclima.cpp b/lib/IRremoteESP8266/src/ir_Neoclima.cpp new file mode 100644 index 0000000000..ff26e6c643 --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_Neoclima.cpp @@ -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 +#include +#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 diff --git a/lib/IRremoteESP8266/src/ir_Neoclima.h b/lib/IRremoteESP8266/src/ir_Neoclima.h index 16d838b385..90eaa029a9 100644 --- a/lib/IRremoteESP8266/src/ir_Neoclima.h +++ b/lib/IRremoteESP8266/src/ir_Neoclima.h @@ -20,10 +20,10 @@ #ifndef UNIT_TEST #include #endif -#include "../src/IRremoteESP8266.h" -#include "../src/IRsend.h" +#include "IRremoteESP8266.h" +#include "IRsend.h" #ifdef UNIT_TEST -#include "../src/IRsend_test.h" +#include "IRsend_test.h" #endif /// Native representation of a Neoclima A/C message. diff --git a/lib/IRremoteESP8266/src/ir_Nikai.cpp b/lib/IRremoteESP8266/src/ir_Nikai.cpp new file mode 100644 index 0000000000..01c789d70e --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_Nikai.cpp @@ -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 +#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 diff --git a/lib/IRremoteESP8266/src/ir_Panasonic.cpp b/lib/IRremoteESP8266/src/ir_Panasonic.cpp new file mode 100644 index 0000000000..e2add03f1f --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_Panasonic.cpp @@ -0,0 +1,1341 @@ +// Copyright 2015 Kristian Lauszus +// Copyright 2017, 2018 David Conran + +/// @file +/// @brief Support for Panasonic protocols. +/// Panasonic protocol originally added by Kristian Lauszus +/// (Thanks to zenwheel and other people at the original blog post) +/// @see Panasonic https://github.com/z3t0/Arduino-IRremote +/// @see http://www.remotecentral.com/cgi-bin/mboard/rc-pronto/thread.cgi?2615 +/// @see Panasonic A/C support heavily influenced by https://github.com/ToniA/ESPEasy/blob/HeatpumpIR/lib/HeatpumpIR/PanasonicHeatpumpIR.cpp +/// Panasonic A/C Clock & Timer support: +/// Reverse Engineering by MikkelTb +/// Code by crankyoldgit + +#include "ir_Panasonic.h" +#include +#include +#ifndef ARDUINO +#include +#endif +#include "IRrecv.h" +#include "IRsend.h" +#include "IRtext.h" +#include "IRutils.h" + +// Constants +/// @see http://www.remotecentral.com/cgi-bin/mboard/rc-pronto/thread.cgi?26152 +const uint16_t kPanasonicHdrMark = 3456; ///< uSeconds. +const uint16_t kPanasonicHdrSpace = 1728; ///< uSeconds. +const uint16_t kPanasonicBitMark = 432; ///< uSeconds. +const uint16_t kPanasonicOneSpace = 1296; ///< uSeconds. +const uint16_t kPanasonicZeroSpace = 432; ///< uSeconds. +const uint32_t kPanasonicMinCommandLength = 163296; ///< uSeconds. +const uint16_t kPanasonicEndGap = 5000; ///< uSeconds. See #245 +const uint32_t kPanasonicMinGap = 74736; ///< uSeconds. + +const uint16_t kPanasonicAcSectionGap = 10000; ///< uSeconds. +const uint16_t kPanasonicAcSection1Length = 8; +const uint32_t kPanasonicAcMessageGap = kDefaultMessageGap; // Just a guess. + +const uint16_t kPanasonicAc32HdrMark = 3543; ///< uSeconds. +const uint16_t kPanasonicAc32BitMark = 920; ///< uSeconds. +const uint16_t kPanasonicAc32HdrSpace = 3450; ///< uSeconds. +const uint16_t kPanasonicAc32OneSpace = 2575; ///< uSeconds. +const uint16_t kPanasonicAc32ZeroSpace = 828; ///< uSeconds. +const uint16_t kPanasonicAc32SectionGap = 13946; ///< uSeconds. +const uint8_t kPanasonicAc32Sections = 2; +const uint8_t kPanasonicAc32BlocksPerSection = 2; + +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::minsToString; +using irutils::setBit; +using irutils::setBits; + +// Used by Denon as well. +#if (SEND_PANASONIC || SEND_DENON) +/// Send a Panasonic 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 This protocol is a modified version of Kaseikyo. +/// @note Use this method if you want to send the results of `decodePanasonic`. +void IRsend::sendPanasonic64(const uint64_t data, const uint16_t nbits, + const uint16_t repeat) { + sendGeneric(kPanasonicHdrMark, kPanasonicHdrSpace, kPanasonicBitMark, + kPanasonicOneSpace, kPanasonicBitMark, kPanasonicZeroSpace, + kPanasonicBitMark, kPanasonicMinGap, kPanasonicMinCommandLength, + data, nbits, kPanasonicFreq, true, repeat, 50); +} + +/// Send a Panasonic formatted message. +/// Status: STABLE, but DEPRECATED +/// @deprecated This is only for legacy use only, please use `sendPanasonic64()` +/// instead. +/// @param[in] address The 16-bit manufacturer code. +/// @param[in] data The 32-bit data portion of 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 a modified version of Kaseikyo. +void IRsend::sendPanasonic(const uint16_t address, const uint32_t data, + const uint16_t nbits, const uint16_t repeat) { + sendPanasonic64(((uint64_t)address << 32) | (uint64_t)data, nbits, repeat); +} + +/// Calculate the raw Panasonic data based on device, subdevice, & function. +/// Status: STABLE / Should be working. +/// @param[in] manufacturer A 16-bit manufacturer code. e.g. 0x4004 is Panasonic +/// @param[in] device An 8-bit code. +/// @param[in] subdevice An 8-bit code. +/// @param[in] function An 8-bit code. +/// @return A value suitable for use with `sendPanasonic64()`. +/// @note Panasonic 48-bit protocol is a modified version of Kaseikyo. +/// @see http://www.remotecentral.com/cgi-bin/mboard/rc-pronto/thread.cgi?2615 +uint64_t IRsend::encodePanasonic(const uint16_t manufacturer, + const uint8_t device, + const uint8_t subdevice, + const uint8_t function) { + uint8_t checksum = device ^ subdevice ^ function; + return (((uint64_t)manufacturer << 32) | ((uint64_t)device << 24) | + ((uint64_t)subdevice << 16) | ((uint64_t)function << 8) | checksum); +} +#endif // (SEND_PANASONIC || SEND_DENON) + +// Used by Denon as well. +#if (DECODE_PANASONIC || DECODE_DENON) +/// Decode the supplied Panasonic message. +/// Status: STABLE / Should 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] manufacturer A 16-bit manufacturer code. e.g. 0x4004 is Panasonic +/// @param[in] strict Flag indicating if we should perform strict matching. +/// @return True if it can decode it, false if it can't. +/// @warning Results to be used with `sendPanasonic64()`, not `sendPanasonic()`. +/// @note Panasonic 48-bit protocol is a modified version of Kaseikyo. +/// @see http://www.remotecentral.com/cgi-bin/mboard/rc-pronto/thread.cgi?2615 +/// @see http://www.hifi-remote.com/wiki/index.php?title=Panasonic +bool IRrecv::decodePanasonic(decode_results *results, uint16_t offset, + const uint16_t nbits, const bool strict, + const uint32_t manufacturer) { + if (strict && nbits != kPanasonicBits) + return false; // Request is out of spec. + + uint64_t data = 0; + + // Match Header + Data + Footer + if (!matchGeneric(results->rawbuf + offset, &data, + results->rawlen - offset, nbits, + kPanasonicHdrMark, kPanasonicHdrSpace, + kPanasonicBitMark, kPanasonicOneSpace, + kPanasonicBitMark, kPanasonicZeroSpace, + kPanasonicBitMark, kPanasonicEndGap, true)) return false; + // Compliance + uint32_t address = data >> 32; + uint32_t command = data; + if (strict) { + if (address != manufacturer) // Verify the Manufacturer code. + return false; + // Verify the checksum. + uint8_t checksumOrig = data; + uint8_t checksumCalc = (data >> 24) ^ (data >> 16) ^ (data >> 8); + if (checksumOrig != checksumCalc) return false; + } + + // Success + results->value = data; + results->address = address; + results->command = command; + results->decode_type = decode_type_t::PANASONIC; + results->bits = nbits; + return true; +} +#endif // (DECODE_PANASONIC || DECODE_DENON) + +#if SEND_PANASONIC_AC +/// Send a Panasonic A/C message. +/// Status: STABLE / Work with real device(s). +/// @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::sendPanasonicAC(const uint8_t data[], const uint16_t nbytes, + const uint16_t repeat) { + if (nbytes < kPanasonicAcSection1Length) return; + for (uint16_t r = 0; r <= repeat; r++) { + // First section. (8 bytes) + sendGeneric(kPanasonicHdrMark, kPanasonicHdrSpace, kPanasonicBitMark, + kPanasonicOneSpace, kPanasonicBitMark, kPanasonicZeroSpace, + kPanasonicBitMark, kPanasonicAcSectionGap, data, + kPanasonicAcSection1Length, kPanasonicFreq, false, 0, 50); + // First section. (The rest of the data bytes) + sendGeneric(kPanasonicHdrMark, kPanasonicHdrSpace, kPanasonicBitMark, + kPanasonicOneSpace, kPanasonicBitMark, kPanasonicZeroSpace, + kPanasonicBitMark, kPanasonicAcMessageGap, + data + kPanasonicAcSection1Length, + nbytes - kPanasonicAcSection1Length, kPanasonicFreq, false, 0, + 50); + } +} +#endif // SEND_PANASONIC_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? +IRPanasonicAc::IRPanasonicAc(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 IRPanasonicAc::stateReset(void) { + memcpy(remote_state, kPanasonicKnownGoodState, kPanasonicAcStateLength); + _temp = 25; // An initial saved desired temp. Completely made up. + _swingh = kPanasonicAcSwingHMiddle; // A similar made up value for H Swing. +} + +/// Set up hardware to be able to send a message. +void IRPanasonicAc::begin(void) { _irsend.begin(); } + +/// 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 IRPanasonicAc::validChecksum(const uint8_t *state, const uint16_t length) { + if (length < 2) return false; // 1 byte of data can't have a checksum. + return (state[length - 1] == + sumBytes(state, length - 1, kPanasonicAcChecksumInit)); +} + +/// Calculate the checksum for a given state. +/// @param[in] state The value to calc the checksum of. +/// @param[in] length The size/length of the state. +/// @return The calculated checksum value. +uint8_t IRPanasonicAc::calcChecksum(const uint8_t *state, + const uint16_t length) { + return sumBytes(state, length - 1, kPanasonicAcChecksumInit); +} + +/// Calculate and set the checksum values for the internal state. +/// @param[in] length The size/length of the state. +void IRPanasonicAc::fixChecksum(const uint16_t length) { + remote_state[length - 1] = calcChecksum(remote_state, length); +} + +#if SEND_PANASONIC_AC +/// Send the current internal state as an IR message. +/// @param[in] repeat Nr. of times the message will be repeated. +void IRPanasonicAc::send(const uint16_t repeat) { + _irsend.sendPanasonicAC(getRaw(), kPanasonicAcStateLength, repeat); +} +#endif // SEND_PANASONIC_AC + +/// Set the model of the A/C to emulate. +/// @param[in] model The enum of the appropriate model. +void IRPanasonicAc::setModel(const panasonic_ac_remote_model_t model) { + switch (model) { + case panasonic_ac_remote_model_t::kPanasonicDke: + case panasonic_ac_remote_model_t::kPanasonicJke: + case panasonic_ac_remote_model_t::kPanasonicLke: + case panasonic_ac_remote_model_t::kPanasonicNke: + case panasonic_ac_remote_model_t::kPanasonicCkp: + case panasonic_ac_remote_model_t::kPanasonicRkr: break; + // Only proceed if we know what to do. + default: return; + } + // clear & set the various bits and bytes. + remote_state[13] &= 0xF0; + remote_state[17] = 0x00; + remote_state[21] &= 0b11101111; + remote_state[23] = 0x81; + remote_state[25] = 0x00; + + switch (model) { + case kPanasonicLke: + remote_state[13] |= 0x02; + remote_state[17] = 0x06; + break; + case kPanasonicDke: + remote_state[23] = 0x01; + remote_state[25] = 0x06; + // Has to be done last as setSwingHorizontal has model check built-in + setSwingHorizontal(_swingh); + break; + case kPanasonicNke: + remote_state[17] = 0x06; + break; + case kPanasonicJke: + break; + case kPanasonicCkp: + remote_state[21] |= 0x10; + remote_state[23] = 0x01; + break; + case kPanasonicRkr: + remote_state[13] |= 0x08; + remote_state[23] = 0x89; + default: + break; + } + // Reset the Ion filter. + setIon(getIon()); +} + +/// Get/Detect the model of the A/C. +/// @return The enum of the compatible model. +panasonic_ac_remote_model_t IRPanasonicAc::getModel(void) { + if (remote_state[23] == 0x89) return kPanasonicRkr; + if (remote_state[17] == 0x00) { + if ((remote_state[21] & 0x10) && (remote_state[23] & 0x01)) + return panasonic_ac_remote_model_t::kPanasonicCkp; + if (remote_state[23] & 0x80) + return panasonic_ac_remote_model_t::kPanasonicJke; + } + if (remote_state[17] == 0x06 && (remote_state[13] & 0x0F) == 0x02) + return panasonic_ac_remote_model_t::kPanasonicLke; + if (remote_state[23] == 0x01) + return panasonic_ac_remote_model_t::kPanasonicDke; + if (remote_state[17] == 0x06) + return panasonic_ac_remote_model_t::kPanasonicNke; + return panasonic_ac_remote_model_t::kPanasonicUnknown; // Default +} + +/// 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 *IRPanasonicAc::getRaw(void) { + fixChecksum(); + return remote_state; +} + +/// Set the internal state from a valid code for this protocol. +/// @param[in] state A valid code for this protocol. +void IRPanasonicAc::setRaw(const uint8_t state[]) { + memcpy(remote_state, state, kPanasonicAcStateLength); +} + +/// Control the power state of the A/C unit. +/// @param[in] on true, the setting is on. false, the setting is off. +/// @warning For CKP models, the remote has no memory of the power state the A/C +/// unit should be in. For those models setting this on/true will toggle the +/// power state of the Panasonic A/C unit with the next message. +/// e.g. If the A/C unit is already on, setPower(true) will turn it off. +/// If the A/C unit is already off, setPower(true) will turn it on. +/// `setPower(false)` will leave the A/C power state as it was. +/// For all other models, setPower(true) should set the internal state to +/// turn it on, and setPower(false) should turn it off. +void IRPanasonicAc::setPower(const bool on) { + setBit(&remote_state[13], kPanasonicAcPowerOffset, on); +} + +/// Get the A/C power state of the remote. +/// @return true, the setting is on. false, the setting is off. +/// @warning Except for CKP models, where it returns if the power state will be +/// toggled on the A/C unit when the next message is sent. +bool IRPanasonicAc::getPower(void) { + return GETBIT8(remote_state[13], kPanasonicAcPowerOffset); +} + +/// Change the power setting to On. +void IRPanasonicAc::on(void) { setPower(true); } + +/// Change the power setting to Off. +void IRPanasonicAc::off(void) { setPower(false); } + +/// Get the operating mode setting of the A/C. +/// @return The current operating mode setting. +uint8_t IRPanasonicAc::getMode(void) { + return GETBITS8(remote_state[13], kHighNibble, kModeBitsSize); +} + +/// Set the operating mode of the A/C. +/// @param[in] desired The desired operating mode. +void IRPanasonicAc::setMode(const uint8_t desired) { + uint8_t mode = kPanasonicAcAuto; // Default to Auto mode. + switch (desired) { + case kPanasonicAcFan: + // Allegedly Fan mode has a temperature of 27. + setTemp(kPanasonicAcFanModeTemp, false); + mode = desired; + break; + case kPanasonicAcAuto: + case kPanasonicAcCool: + case kPanasonicAcHeat: + case kPanasonicAcDry: + mode = desired; + // Set the temp to the saved temp, just incase our previous mode was Fan. + setTemp(_temp); + break; + } + remote_state[13] &= 0x0F; // Clear the previous mode bits. + setBits(&remote_state[13], kHighNibble, kModeBitsSize, mode); +} + +/// Get the current temperature setting. +/// @return The current setting for temp. in degrees celsius. +uint8_t IRPanasonicAc::getTemp(void) { + return GETBITS8(remote_state[14], kPanasonicAcTempOffset, + kPanasonicAcTempSize); +} + +/// Set the temperature. +/// @param[in] celsius The temperature in degrees celsius. +/// @param[in] remember: A flag for the class to remember the temperature. +/// @note Automatically safely limits the temp to the operating range supported. +void IRPanasonicAc::setTemp(const uint8_t celsius, const bool remember) { + uint8_t temperature; + temperature = std::max(celsius, kPanasonicAcMinTemp); + temperature = std::min(temperature, kPanasonicAcMaxTemp); + if (remember) _temp = temperature; + setBits(&remote_state[14], kPanasonicAcTempOffset, kPanasonicAcTempSize, + temperature); +} + +/// Get the current vertical swing setting. +/// @return The current position it is set to. +uint8_t IRPanasonicAc::getSwingVertical(void) { + return GETBITS8(remote_state[16], kLowNibble, kNibbleSize); +} + +/// Control the vertical swing setting. +/// @param[in] desired_elevation The position to set the vertical swing to. +void IRPanasonicAc::setSwingVertical(const uint8_t desired_elevation) { + uint8_t elevation = desired_elevation; + if (elevation != kPanasonicAcSwingVAuto) { + elevation = std::max(elevation, kPanasonicAcSwingVHighest); + elevation = std::min(elevation, kPanasonicAcSwingVLowest); + } + setBits(&remote_state[16], kLowNibble, kNibbleSize, elevation); +} + +/// Get the current horizontal swing setting. +/// @return The current position it is set to. +uint8_t IRPanasonicAc::getSwingHorizontal(void) { + return GETBITS8(remote_state[17], kLowNibble, kNibbleSize); +} + +/// Control the horizontal swing setting. +/// @param[in] desired_direction The position to set the horizontal swing to. +void IRPanasonicAc::setSwingHorizontal(const uint8_t desired_direction) { + switch (desired_direction) { + case kPanasonicAcSwingHAuto: + case kPanasonicAcSwingHMiddle: + case kPanasonicAcSwingHFullLeft: + case kPanasonicAcSwingHLeft: + case kPanasonicAcSwingHRight: + case kPanasonicAcSwingHFullRight: break; + // Ignore anything that isn't valid. + default: return; + } + _swingh = desired_direction; // Store the direction for later. + uint8_t direction = desired_direction; + switch (getModel()) { + case kPanasonicDke: + case kPanasonicRkr: + break; + case kPanasonicNke: + case kPanasonicLke: + direction = kPanasonicAcSwingHMiddle; + break; + default: // Ignore everything else. + return; + } + setBits(&remote_state[17], kLowNibble, kNibbleSize, direction); +} + +/// Set the speed of the fan. +/// @param[in] speed The desired setting. +void IRPanasonicAc::setFan(const uint8_t speed) { + switch (speed) { + case kPanasonicAcFanMin: + case kPanasonicAcFanLow: + case kPanasonicAcFanMed: + case kPanasonicAcFanHigh: + case kPanasonicAcFanMax: + case kPanasonicAcFanAuto: + setBits(&remote_state[16], kHighNibble, kNibbleSize, + speed + kPanasonicAcFanDelta); + break; + default: setFan(kPanasonicAcFanAuto); + } +} + +/// Get the current fan speed setting. +/// @return The current fan speed. +uint8_t IRPanasonicAc::getFan(void) { + return GETBITS8(remote_state[16], kHighNibble, kNibbleSize) - + kPanasonicAcFanDelta; +} + +/// Get the Quiet setting of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRPanasonicAc::getQuiet(void) { + switch (getModel()) { + case kPanasonicRkr: + case kPanasonicCkp: + return GETBIT8(remote_state[21], kPanasonicAcQuietCkpOffset); + default: + return GETBIT8(remote_state[21], kPanasonicAcQuietOffset); + } +} + +/// Set the Quiet setting of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRPanasonicAc::setQuiet(const bool on) { + uint8_t offset; + switch (getModel()) { + case kPanasonicRkr: + case kPanasonicCkp: offset = kPanasonicAcQuietCkpOffset; break; + default: offset = kPanasonicAcQuietOffset; + } + if (on) setPowerful(false); // Powerful is mutually exclusive. + setBit(&remote_state[21], offset, on); +} + +/// Get the Powerful (Turbo) setting of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRPanasonicAc::getPowerful(void) { + switch (getModel()) { + case kPanasonicRkr: + case kPanasonicCkp: + return GETBIT8(remote_state[21], kPanasonicAcPowerfulCkpOffset); + default: + return GETBIT8(remote_state[21], kPanasonicAcPowerfulOffset); + } +} + +/// Set the Powerful (Turbo) setting of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRPanasonicAc::setPowerful(const bool on) { + uint8_t offset; + switch (getModel()) { + case kPanasonicRkr: + case kPanasonicCkp: offset = kPanasonicAcPowerfulCkpOffset; break; + default: offset = kPanasonicAcPowerfulOffset; + } + + if (on) setQuiet(false); // Quiet is mutually exclusive. + setBit(&remote_state[21], offset, on); +} + +/// Convert standard (military/24hr) time to nr. of minutes since midnight. +/// @param[in] hours The hours component of the time. +/// @param[in] mins The minutes component of the time. +/// @return The nr of minutes since midnight. +uint16_t IRPanasonicAc::encodeTime(const uint8_t hours, const uint8_t mins) { + return std::min(hours, (uint8_t)23) * 60 + std::min(mins, (uint8_t)59); +} + +/// Get the time from a given pointer location. +/// @param[in] ptr A pointer to a time location in a state. +/// @return The time expressed as nr. of minutes past midnight. +/// @note Internal use only. +uint16_t IRPanasonicAc::_getTime(const uint8_t ptr[]) { + uint16_t result = (GETBITS8( + ptr[1], kLowNibble, kPanasonicAcTimeOverflowSize) << + (kPanasonicAcTimeSize - kPanasonicAcTimeOverflowSize)) + ptr[0]; + if (result == kPanasonicAcTimeSpecial) return 0; + return result; +} + +/// Get the current clock time value. +/// @return The time expressed as nr. of minutes past midnight. +uint16_t IRPanasonicAc::getClock(void) { return _getTime(&remote_state[24]); } + +/// Set the time at a given pointer location. +/// @param[in, out] ptr A pointer to a time location in a state. +/// @param[in] mins_since_midnight The time as nr. of minutes past midnight. +/// @param[in] round_down Do we round to the nearest 10 minute mark? +/// @note Internal use only. +void IRPanasonicAc::_setTime(uint8_t * const ptr, + const uint16_t mins_since_midnight, + const bool round_down) { + uint16_t corrected = std::min(mins_since_midnight, kPanasonicAcTimeMax); + if (round_down) corrected -= corrected % 10; + if (mins_since_midnight == kPanasonicAcTimeSpecial) + corrected = kPanasonicAcTimeSpecial; + ptr[0] = corrected; + setBits(&ptr[1], kLowNibble, kPanasonicAcTimeOverflowSize, + corrected >> (kPanasonicAcTimeSize - kPanasonicAcTimeOverflowSize)); +} + +/// Set the current clock time value. +/// @param[in] mins_since_midnight The time as nr. of minutes past midnight. +void IRPanasonicAc::setClock(const uint16_t mins_since_midnight) { + _setTime(&remote_state[24], mins_since_midnight, false); +} + +/// Get the On Timer time value. +/// @return The time expressed as nr. of minutes past midnight. +uint16_t IRPanasonicAc::getOnTimer(void) { return _getTime(&remote_state[18]); } + +/// Set/Enable the On Timer. +/// @param[in] mins_since_midnight The time as nr. of minutes past midnight. +/// @param[in] enable Do we enable the timer or not? +void IRPanasonicAc::setOnTimer(const uint16_t mins_since_midnight, + const bool enable) { + // Set the timer flag. + setBit(&remote_state[13], kPanasonicAcOnTimerOffset, enable); + // Store the time. + _setTime(&remote_state[18], mins_since_midnight, true); +} + +/// Cancel the On Timer. +void IRPanasonicAc::cancelOnTimer(void) { setOnTimer(0, false); } + +/// Check if the On Timer is Enabled. +/// @return true, the setting is on. false, the setting is off. +bool IRPanasonicAc::isOnTimerEnabled(void) { + return GETBIT8(remote_state[13], kPanasonicAcOnTimerOffset); +} + +/// Get the Off Timer time value. +/// @return The time expressed as nr. of minutes past midnight. +uint16_t IRPanasonicAc::getOffTimer(void) { + uint16_t result = (GETBITS8(remote_state[20], 0, 7) << kNibbleSize) | + GETBITS8(remote_state[19], kHighNibble, kNibbleSize); + if (result == kPanasonicAcTimeSpecial) return 0; + return result; +} + +/// Set/Enable the Off Timer. +/// @param[in] mins_since_midnight The time as nr. of minutes past midnight. +/// @param[in] enable Do we enable the timer or not? +void IRPanasonicAc::setOffTimer(const uint16_t mins_since_midnight, + const bool enable) { + // Ensure its on a 10 minute boundary and no overflow. + uint16_t corrected = std::min(mins_since_midnight, kPanasonicAcTimeMax); + corrected -= corrected % 10; + if (mins_since_midnight == kPanasonicAcTimeSpecial) + corrected = kPanasonicAcTimeSpecial; + // Set the timer flag. + setBit(&remote_state[13], kPanasonicAcOffTimerOffset, enable); + // Store the time. + setBits(&remote_state[19], kHighNibble, kNibbleSize, corrected); + setBits(&remote_state[20], 0, 7, corrected >> kNibbleSize); +} + +/// Cancel the Off Timer. +void IRPanasonicAc::cancelOffTimer(void) { setOffTimer(0, false); } + +/// Check if the Off Timer is Enabled. +/// @return true, the setting is on. false, the setting is off. +bool IRPanasonicAc::isOffTimerEnabled(void) { + return GETBIT8(remote_state[13], kPanasonicAcOffTimerOffset); +} + +/// Get the Ion (filter) setting of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRPanasonicAc::getIon(void) { + switch (getModel()) { + case kPanasonicDke: + return GETBIT8(remote_state[kPanasonicAcIonFilterByte], + kPanasonicAcIonFilterOffset); + default: + return false; + } +} + +/// Set the Ion (filter) setting of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRPanasonicAc::setIon(const bool on) { + if (getModel() == kPanasonicDke) + setBit(&remote_state[kPanasonicAcIonFilterByte], + kPanasonicAcIonFilterOffset, on); +} + +/// 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 IRPanasonicAc::convertMode(const stdAc::opmode_t mode) { + switch (mode) { + case stdAc::opmode_t::kCool: return kPanasonicAcCool; + case stdAc::opmode_t::kHeat: return kPanasonicAcHeat; + case stdAc::opmode_t::kDry: return kPanasonicAcDry; + case stdAc::opmode_t::kFan: return kPanasonicAcFan; + default: return kPanasonicAcAuto; + } +} + +/// 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 IRPanasonicAc::convertFan(const stdAc::fanspeed_t speed) { + switch (speed) { + case stdAc::fanspeed_t::kMin: return kPanasonicAcFanMin; + case stdAc::fanspeed_t::kLow: return kPanasonicAcFanLow; + case stdAc::fanspeed_t::kMedium: return kPanasonicAcFanMed; + case stdAc::fanspeed_t::kHigh: return kPanasonicAcFanHigh; + case stdAc::fanspeed_t::kMax: return kPanasonicAcFanMax; + default: return kPanasonicAcFanAuto; + } +} + +/// Convert a standard A/C vertical swing into its native setting. +/// @param[in] position A stdAc::swingv_t position to convert. +/// @return The equivalent native horizontal swing position. +uint8_t IRPanasonicAc::convertSwingV(const stdAc::swingv_t position) { + switch (position) { + case stdAc::swingv_t::kHighest: + case stdAc::swingv_t::kHigh: + case stdAc::swingv_t::kMiddle: + case stdAc::swingv_t::kLow: + case stdAc::swingv_t::kLowest: return (uint8_t)position; + default: return kPanasonicAcSwingVAuto; + } +} + +/// Convert a standard A/C horizontal swing into its native setting. +/// @param[in] position A stdAc::swingh_t position to convert. +/// @return The equivalent native horizontal swing position. +uint8_t IRPanasonicAc::convertSwingH(const stdAc::swingh_t position) { + switch (position) { + case stdAc::swingh_t::kLeftMax: return kPanasonicAcSwingHFullLeft; + case stdAc::swingh_t::kLeft: return kPanasonicAcSwingHLeft; + case stdAc::swingh_t::kMiddle: return kPanasonicAcSwingHMiddle; + case stdAc::swingh_t::kRight: return kPanasonicAcSwingHRight; + case stdAc::swingh_t::kRightMax: return kPanasonicAcSwingHFullRight; + default: return kPanasonicAcSwingHAuto; + } +} + +/// 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 IRPanasonicAc::toCommonMode(const uint8_t mode) { + switch (mode) { + case kPanasonicAcCool: return stdAc::opmode_t::kCool; + case kPanasonicAcHeat: return stdAc::opmode_t::kHeat; + case kPanasonicAcDry: return stdAc::opmode_t::kDry; + case kPanasonicAcFan: return stdAc::opmode_t::kFan; + default: return stdAc::opmode_t::kAuto; + } +} + +/// Convert a native fan speed into its stdAc equivalent. +/// @param[in] spd The native setting to be converted. +/// @return The stdAc equivalent of the native setting. +stdAc::fanspeed_t IRPanasonicAc::toCommonFanSpeed(const uint8_t spd) { + switch (spd) { + case kPanasonicAcFanMax: return stdAc::fanspeed_t::kMax; + case kPanasonicAcFanHigh: return stdAc::fanspeed_t::kHigh; + case kPanasonicAcFanMed: return stdAc::fanspeed_t::kMedium; + case kPanasonicAcFanLow: return stdAc::fanspeed_t::kLow; + case kPanasonicAcFanMin: return stdAc::fanspeed_t::kMin; + default: return stdAc::fanspeed_t::kAuto; + } +} + +/// Convert a native horizontal swing postion to it's common equivalent. +/// @param[in] pos A native position to convert. +/// @return The common horizontal swing position. +stdAc::swingh_t IRPanasonicAc::toCommonSwingH(const uint8_t pos) { + switch (pos) { + case kPanasonicAcSwingHFullLeft: return stdAc::swingh_t::kLeftMax; + case kPanasonicAcSwingHLeft: return stdAc::swingh_t::kLeft; + case kPanasonicAcSwingHMiddle: return stdAc::swingh_t::kMiddle; + case kPanasonicAcSwingHRight: return stdAc::swingh_t::kRight; + case kPanasonicAcSwingHFullRight: return stdAc::swingh_t::kRightMax; + default: return stdAc::swingh_t::kAuto; + } +} + +/// 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 IRPanasonicAc::toCommonSwingV(const uint8_t pos) { + if (pos >= kPanasonicAcSwingVHighest && pos <= kPanasonicAcSwingVLowest) + return (stdAc::swingv_t)pos; + else + return stdAc::swingv_t::kAuto; +} + +/// Convert the current internal state into its stdAc::state_t equivalent. +/// @return The stdAc equivalent of the native settings. +stdAc::state_t IRPanasonicAc::toCommon(void) { + stdAc::state_t result; + result.protocol = decode_type_t::PANASONIC_AC; + result.model = getModel(); + result.power = getPower(); + result.mode = toCommonMode(getMode()); + result.celsius = true; + result.degrees = getTemp(); + result.fanspeed = toCommonFanSpeed(getFan()); + result.swingv = toCommonSwingV(getSwingVertical()); + result.swingh = toCommonSwingH(getSwingHorizontal()); + result.quiet = getQuiet(); + result.turbo = getPowerful(); + result.filter = getIon(); + // Not supported. + result.econo = false; + result.clean = false; + result.light = false; + result.beep = false; + result.sleep = -1; + result.clock = -1; + return result; +} + +/// Convert the internal state into a human readable string. +/// @return A string containing the settings in human-readable form. +String IRPanasonicAc::toString(void) { + String result = ""; + result.reserve(180); // Reserve some heap for the string to reduce fragging. + result += addModelToString(decode_type_t::PANASONIC_AC, getModel(), false); + result += addBoolToString(getPower(), kPowerStr); + result += addModeToString(getMode(), kPanasonicAcAuto, kPanasonicAcCool, + kPanasonicAcHeat, kPanasonicAcDry, kPanasonicAcFan); + result += addTempToString(getTemp()); + result += addFanToString(getFan(), kPanasonicAcFanHigh, kPanasonicAcFanLow, + kPanasonicAcFanAuto, kPanasonicAcFanMin, + kPanasonicAcFanMed, kPanasonicAcFanMax); + result += addSwingVToString(getSwingVertical(), kPanasonicAcSwingVAuto, + kPanasonicAcSwingVHighest, + kPanasonicAcSwingVHigh, + kPanasonicAcSwingVAuto, // Upper Middle is unused + kPanasonicAcSwingVMiddle, + kPanasonicAcSwingVAuto, // Lower Middle is unused + kPanasonicAcSwingVLow, + kPanasonicAcSwingVLowest, + // Below are unused. + kPanasonicAcSwingVAuto, + kPanasonicAcSwingVAuto, + kPanasonicAcSwingVAuto, + kPanasonicAcSwingVAuto); + switch (getModel()) { + case kPanasonicJke: + case kPanasonicCkp: + break; // No Horizontal Swing support. + default: + result += addSwingHToString(getSwingHorizontal(), kPanasonicAcSwingHAuto, + kPanasonicAcSwingHFullLeft, + kPanasonicAcSwingHLeft, + kPanasonicAcSwingHMiddle, + kPanasonicAcSwingHRight, + kPanasonicAcSwingHFullRight, + // Below are unused. + kPanasonicAcSwingHAuto, + kPanasonicAcSwingHAuto, + kPanasonicAcSwingHAuto, + kPanasonicAcSwingHAuto, + kPanasonicAcSwingHAuto); + } + result += addBoolToString(getQuiet(), kQuietStr); + result += addBoolToString(getPowerful(), kPowerfulStr); + if (getModel() == kPanasonicDke) + result += addBoolToString(getIon(), kIonStr); + result += addLabeledString(minsToString(getClock()), kClockStr); + result += addLabeledString( + isOnTimerEnabled() ? minsToString(getOnTimer()) : kOffStr, + kOnTimerStr); + result += addLabeledString( + isOffTimerEnabled() ? minsToString(getOffTimer()) : kOffStr, + kOffTimerStr); + return result; +} + +#if DECODE_PANASONIC_AC +/// Decode the supplied Panasonic AC message. +/// Status: STABLE / Works with real device(s). +/// @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::decodePanasonicAC(decode_results *results, uint16_t offset, + const uint16_t nbits, const bool strict) { + uint8_t min_nr_of_messages = 1; + if (strict) { + if (nbits != kPanasonicAcBits && nbits != kPanasonicAcShortBits) + return false; // Not strictly a PANASONIC_AC message. + } + + if (results->rawlen <= + min_nr_of_messages * (2 * nbits + kHeader + kFooter) - 1 + offset) + return false; // Can't possibly be a valid PANASONIC_AC message. + + // Match Header + Data #1 + Footer + uint16_t used; + used = matchGeneric(results->rawbuf + offset, results->state, + results->rawlen - offset, kPanasonicAcSection1Length * 8, + kPanasonicHdrMark, kPanasonicHdrSpace, + kPanasonicBitMark, kPanasonicOneSpace, + kPanasonicBitMark, kPanasonicZeroSpace, + kPanasonicBitMark, kPanasonicAcSectionGap, false, + kPanasonicAcTolerance, kPanasonicAcExcess, false); + if (!used) return false; + offset += used; + + // Match Header + Data #2 + Footer + if (!matchGeneric(results->rawbuf + offset, + results->state + kPanasonicAcSection1Length, + results->rawlen - offset, + nbits - kPanasonicAcSection1Length * 8, + kPanasonicHdrMark, kPanasonicHdrSpace, + kPanasonicBitMark, kPanasonicOneSpace, + kPanasonicBitMark, kPanasonicZeroSpace, + kPanasonicBitMark, kPanasonicAcMessageGap, true, + kPanasonicAcTolerance, kPanasonicAcExcess, false)) + return false; + // Compliance + if (strict) { + // Check the signatures of the section blocks. They start with 0x02& 0x20. + if (results->state[0] != 0x02 || results->state[1] != 0x20 || + results->state[8] != 0x02 || results->state[9] != 0x20) + return false; + if (!IRPanasonicAc::validChecksum(results->state, nbits / 8)) return false; + } + + // Success + results->decode_type = decode_type_t::PANASONIC_AC; + results->bits = nbits; + return true; +} +#endif // DECODE_PANASONIC_AC + +#if SEND_PANASONIC_AC32 +/// Send a Panasonic AC 32/16bit formatted message. +/// Status: STABLE / Confirmed working. +/// @param[in] data containing the IR command. +/// @param[in] nbits Nr. of bits to send. Usually kPanasonicAc32Bits +/// @param[in] repeat Nr. of times the message is to be repeated. +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1307 +void IRsend::sendPanasonicAC32(const uint64_t data, const uint16_t nbits, + const uint16_t repeat) { + uint16_t section_bits; + uint16_t sections; + uint16_t blocks; + // Calculate the section, block, and bit sizes based on the requested bit size + if (nbits > kPanasonicAc32Bits / 2) { // A long message + section_bits = nbits / kPanasonicAc32Sections; + sections = kPanasonicAc32Sections; + blocks = kPanasonicAc32BlocksPerSection; + } else { // A short message + section_bits = nbits; + sections = kPanasonicAc32Sections - 1; + blocks = kPanasonicAc32BlocksPerSection + 1; + } + for (uint16_t r = 0; r <= repeat; r++) { + for (uint8_t section = 0; section < sections; section++) { + uint64_t section_data; + section_data = GETBITS64(data, section_bits * (sections - section - 1), + section_bits); + + // Duplicate bytes in the data. + uint64_t expanded_data = 0; + for (uint8_t i = 0; i < sizeof(expanded_data); i++) { + const uint8_t first_byte = section_data >> 56; + for (uint8_t i = 0; i < 2; i++) + expanded_data = (expanded_data << 8) | first_byte; + section_data <<= 8; + } + // Two data blocks per section (i.e. 1 + a repeat) + sendGeneric(kPanasonicAc32HdrMark, kPanasonicAc32HdrSpace, // Header + kPanasonicAc32BitMark, kPanasonicAc32OneSpace, // Data + kPanasonicAc32BitMark, kPanasonicAc32ZeroSpace, + 0, 0, // No Footer + expanded_data, section_bits * 2, kPanasonicFreq, false, + blocks - 1, // Repeat + 50); + // Section Footer + sendGeneric(kPanasonicAc32HdrMark, kPanasonicAc32HdrSpace, // Header + 0, 0, 0, 0, // No Data + kPanasonicAc32BitMark, kPanasonicAc32SectionGap, // Footer + data, 0, // No data (bits) + kPanasonicFreq, true, 0, 50); + } + } +} +#endif // SEND_PANASONIC_AC32 + +#if DECODE_PANASONIC_AC32 +/// Decode the supplied Panasonic AC 32/16bit 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. +/// Typically: kPanasonicAc32Bits or kPanasonicAc32Bits/2 +/// @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/1307 +/// @note Protocol has two known configurations: +/// (long) +/// Two sections of identical 32 bit data block pairs. ie. (32+32)+(32+32)=128 +/// or +/// (short) +/// A single section of 3 x identical 32 bit data blocks i.e. (32+32+32)=96 +/// Each data block also has a pair of 8 bits repeated identical bits. +/// e.g. (8+8)+(8+8)=32 +/// +/// So each long version really only has 32 unique bits, and the short version +/// really only has 16 unique bits. +bool IRrecv::decodePanasonicAC32(decode_results *results, uint16_t offset, + const uint16_t nbits, const bool strict) { + if (strict && (nbits != kPanasonicAc32Bits && + nbits != kPanasonicAc32Bits / 2)) + return false; // Not strictly a valid bit size. + + // Determine if this is a long or a short message we are looking for. + const bool is_long = (nbits > kPanasonicAc32Bits / 2); + const uint16_t min_length = is_long ? + kPanasonicAc32Sections * kPanasonicAc32BlocksPerSection * + ((2 * nbits) + kHeader + kFooter) - 1 + offset : + (kPanasonicAc32BlocksPerSection + 1) * ((4 * nbits) + kHeader) + + kFooter - 1 + offset; + + if (results->rawlen < min_length) + return false; // Can't possibly be a valid message. + + // Calculate the parameters for the decode based on it's length. + uint16_t sections; + uint16_t blocks_per_section; + if (is_long) { + sections = kPanasonicAc32Sections; + blocks_per_section = kPanasonicAc32BlocksPerSection; + } else { + sections = kPanasonicAc32Sections - 1; + blocks_per_section = kPanasonicAc32BlocksPerSection + 1; + } + const uint16_t bits_per_block = nbits / sections; + + uint64_t data = 0; + uint64_t section_data = 0; + uint32_t prev_section_data; + + // Match all the expected data blocks. + for (uint16_t block = 0; + block < sections * blocks_per_section; + block++) { + prev_section_data = section_data; + uint16_t used = matchGeneric(results->rawbuf + offset, §ion_data, + results->rawlen - offset, bits_per_block * 2, + kPanasonicAc32HdrMark, kPanasonicAc32HdrSpace, + kPanasonicAc32BitMark, kPanasonicAc32OneSpace, + kPanasonicAc32BitMark, kPanasonicAc32ZeroSpace, + 0, 0, // No Footer + false, kUseDefTol, kMarkExcess, false); + if (!used) return false; + offset += used; + // Is it the first block of the section? + if (block % blocks_per_section == 0) { + // The protocol repeats each byte twice, so to shrink the code we + // remove the duplicate bytes in the collected data. We only need to do + // this for the first block in a section. + uint64_t shrunk_data = 0; + uint64_t data_copy = section_data; + for (uint8_t i = 0; i < sizeof(data_copy); i += 2) { + const uint8_t first_byte = GETBITS64(data_copy, + (sizeof(data_copy) - 1) * 8, 8); + shrunk_data = (shrunk_data << 8) | first_byte; + // Compliance + if (strict) { + // Every second byte must be a duplicate of the previous. + const uint8_t next_byte = GETBITS64(data_copy, + (sizeof(data_copy) - 2) * 8, 8); + if (first_byte != next_byte) return false; + } + data_copy <<= 16; + } + // Keep the data from the first of the block in the section. + data = (data << bits_per_block) | shrunk_data; + } else { // Not the first block in a section. + // Compliance + if (strict) + // Compare the data from the blocks in pairs. + if (section_data != prev_section_data) return false; + // Look for the section footer at the end of the blocks. + if ((block + 1) % blocks_per_section == 0) { + uint64_t junk; + used = matchGeneric(results->rawbuf + offset, &junk, + results->rawlen - offset, 0, + // Header + kPanasonicAc32HdrMark, kPanasonicAc32HdrSpace, + // No Data + 0, 0, + 0, 0, + // Footer + kPanasonicAc32BitMark, kPanasonicAc32SectionGap, + true); + if (!used) return false; + offset += used; + } + } + } + + // Success + results->value = data; + results->decode_type = decode_type_t::PANASONIC_AC32; + results->bits = nbits; + results->address = 0; + results->command = 0; + return true; +} +#endif // DECODE_PANASONIC_AC32 + +/// 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? +IRPanasonicAc32::IRPanasonicAc32(const uint16_t pin, const bool inverted, + const bool use_modulation) + : _irsend(pin, inverted, use_modulation) { stateReset(); } + +#if SEND_PANASONIC_AC32 +/// Send the current internal state as IR messages. +/// @param[in] repeat Nr. of times the message will be repeated. +void IRPanasonicAc32::send(const uint16_t repeat) { + _irsend.sendPanasonicAC32(getRaw(), kPanasonicAc32Bits, repeat); +} +#endif // SEND_PANASONIC_AC32 + +/// Set up hardware to be able to send a message. +void IRPanasonicAc32::begin(void) { _irsend.begin(); } + +/// 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 IRPanasonicAc32::getRaw(void) const { return _.raw; } + +/// Set the internal state from a valid code for this protocol. +/// @param[in] state A valid code for this protocol. +void IRPanasonicAc32::setRaw(const uint32_t state) { _.raw = state; } + +/// Reset the state of the remote to a known good state/sequence. +void IRPanasonicAc32::stateReset(void) { setRaw(kPanasonicAc32KnownGood); } + +/// Set the Power Toggle setting of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRPanasonicAc32::setPowerToggle(const bool on) { _.PowerToggle = !on; } + +/// Get the Power Toggle setting of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRPanasonicAc32::getPowerToggle(void) const { return !_.PowerToggle; } + +/// Set the desired temperature. +/// @param[in] degrees The temperature in degrees celsius. +void IRPanasonicAc32::setTemp(const uint8_t degrees) { + uint8_t temp = std::max((uint8_t)kPanasonicAcMinTemp, degrees); + temp = std::min((uint8_t)kPanasonicAcMaxTemp, temp); + _.Temp = temp - (kPanasonicAcMinTemp - 1); +} + +/// Get the current desired temperature setting. +/// @return The current setting for temp. in degrees celsius. +uint8_t IRPanasonicAc32::getTemp(void) const { + return _.Temp + (kPanasonicAcMinTemp - 1); +} + +/// Get the operating mode setting of the A/C. +/// @return The current operating mode setting. +uint8_t IRPanasonicAc32::getMode(void) const { return _.Mode; } + +/// Set the operating mode of the A/C. +/// @param[in] mode The desired operating mode. +/// @note If we get an unexpected mode, default to AUTO. +void IRPanasonicAc32::setMode(const uint8_t mode) { + switch (mode) { + case kPanasonicAc32Auto: + case kPanasonicAc32Cool: + case kPanasonicAc32Dry: + case kPanasonicAc32Heat: + case kPanasonicAc32Fan: + _.Mode = mode; + break; + default: _.Mode = kPanasonicAc32Auto; + } +} + +/// 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 IRPanasonicAc32::convertMode(const stdAc::opmode_t mode) { + switch (mode) { + case stdAc::opmode_t::kCool: return kPanasonicAc32Cool; + case stdAc::opmode_t::kHeat: return kPanasonicAc32Heat; + case stdAc::opmode_t::kDry: return kPanasonicAc32Dry; + case stdAc::opmode_t::kFan: return kPanasonicAc32Fan; + default: return kPanasonicAc32Auto; + } +} + +/// 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 IRPanasonicAc32::toCommonMode(const uint8_t mode) { + switch (mode) { + case kPanasonicAc32Cool: return stdAc::opmode_t::kCool; + case kPanasonicAc32Heat: return stdAc::opmode_t::kHeat; + case kPanasonicAc32Dry: return stdAc::opmode_t::kDry; + case kPanasonicAc32Fan: return stdAc::opmode_t::kFan; + default: return stdAc::opmode_t::kAuto; + } +} + +/// Set the speed of the fan. +/// @param[in] speed The desired setting. +void IRPanasonicAc32::setFan(const uint8_t speed) { + switch (speed) { + case kPanasonicAc32FanMin: + case kPanasonicAc32FanLow: + case kPanasonicAc32FanMed: + case kPanasonicAc32FanHigh: + case kPanasonicAc32FanMax: + case kPanasonicAc32FanAuto: + _.Fan = speed; + break; + default: _.Fan = kPanasonicAc32FanAuto; + } +} + +/// Get the current fan speed setting. +/// @return The current fan speed. +uint8_t IRPanasonicAc32::getFan(void) const { return _.Fan; } + +/// Convert a native fan speed into its stdAc equivalent. +/// @param[in] spd The native setting to be converted. +/// @return The stdAc equivalent of the native setting. +stdAc::fanspeed_t IRPanasonicAc32::toCommonFanSpeed(const uint8_t spd) { + switch (spd) { + case kPanasonicAc32FanMax: return stdAc::fanspeed_t::kMax; + case kPanasonicAc32FanHigh: return stdAc::fanspeed_t::kHigh; + case kPanasonicAc32FanMed: return stdAc::fanspeed_t::kMedium; + case kPanasonicAc32FanLow: return stdAc::fanspeed_t::kLow; + case kPanasonicAc32FanMin: return stdAc::fanspeed_t::kMin; + default: return stdAc::fanspeed_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 IRPanasonicAc32::convertFan(const stdAc::fanspeed_t speed) { + switch (speed) { + case stdAc::fanspeed_t::kMin: return kPanasonicAc32FanMin; + case stdAc::fanspeed_t::kLow: return kPanasonicAc32FanLow; + case stdAc::fanspeed_t::kMedium: return kPanasonicAc32FanMed; + case stdAc::fanspeed_t::kHigh: return kPanasonicAc32FanHigh; + case stdAc::fanspeed_t::kMax: return kPanasonicAc32FanMax; + default: return kPanasonicAc32FanAuto; + } +} + +/// Get the current horizontal swing setting. +/// @return The current position it is set to. +bool IRPanasonicAc32::getSwingHorizontal(void) const { return _.SwingH; } + +/// Control the horizontal swing setting. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRPanasonicAc32::setSwingHorizontal(const bool on) { _.SwingH = on; } + +/// Get the current vertical swing setting. +/// @return The current position it is set to. +uint8_t IRPanasonicAc32::getSwingVertical(void) const { return _.SwingV; } + +/// Control the vertical swing setting. +/// @param[in] pos The position to set the vertical swing to. +void IRPanasonicAc32::setSwingVertical(const uint8_t pos) { + uint8_t elevation = pos; + if (elevation != kPanasonicAc32SwingVAuto) { + elevation = std::max(elevation, kPanasonicAcSwingVHighest); + elevation = std::min(elevation, kPanasonicAcSwingVLowest); + } + _.SwingV = elevation; +} + +/// 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 IRPanasonicAc32::toCommonSwingV(const uint8_t pos) { + return IRPanasonicAc::toCommonSwingV(pos); +} + +/// Convert a standard A/C vertical swing into its native setting. +/// @param[in] position A stdAc::swingv_t position to convert. +/// @return The equivalent native horizontal swing position. +uint8_t IRPanasonicAc32::convertSwingV(const stdAc::swingv_t position) { + switch (position) { + case stdAc::swingv_t::kHighest: + case stdAc::swingv_t::kHigh: + case stdAc::swingv_t::kMiddle: + case stdAc::swingv_t::kLow: + case stdAc::swingv_t::kLowest: return (uint8_t)position; + default: return kPanasonicAc32SwingVAuto; + } +} + +/// Convert the current internal state into a human readable string. +/// @return A human readable string. +String IRPanasonicAc32::toString(void) const { + String result = ""; + result.reserve(110); + result += addBoolToString(getPowerToggle(), kPowerToggleStr, false); + result += addModeToString(_.Mode, kPanasonicAc32Auto, kPanasonicAc32Cool, + kPanasonicAc32Heat, kPanasonicAc32Dry, + kPanasonicAc32Fan); + result += addTempToString(getTemp()); + result += addFanToString(_.Fan, kPanasonicAc32FanHigh, kPanasonicAc32FanLow, + kPanasonicAc32FanAuto, kPanasonicAc32FanMin, + kPanasonicAc32FanMed, kPanasonicAc32FanMax); + result += addBoolToString(_.SwingH, kSwingHStr); + result += addSwingVToString(getSwingVertical(), + kPanasonicAc32SwingVAuto, + kPanasonicAcSwingVHighest, + kPanasonicAcSwingVHigh, + kPanasonicAc32SwingVAuto, // Upper Middle unused + kPanasonicAcSwingVMiddle, + kPanasonicAc32SwingVAuto, // Lower Middle unused + kPanasonicAcSwingVLow, + kPanasonicAcSwingVLowest, + // Below are unused. + kPanasonicAc32SwingVAuto, + kPanasonicAc32SwingVAuto, + kPanasonicAc32SwingVAuto, + kPanasonicAc32SwingVAuto); + return result; +} + +/// 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 IRPanasonicAc32::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::PANASONIC_AC32; + result.model = -1; + if (getPowerToggle()) result.power = !result.power; + result.mode = toCommonMode(getMode()); + result.celsius = true; + result.degrees = getTemp(); + result.fanspeed = toCommonFanSpeed(getFan()); + result.swingv = toCommonSwingV(getSwingVertical()); + result.swingh = getSwingHorizontal() ? stdAc::swingh_t::kAuto + : stdAc::swingh_t::kOff; + // Not supported. + result.quiet = false; + result.turbo = false; + result.filter = false; + result.econo = false; + result.clean = false; + result.light = false; + result.beep = false; + result.sleep = -1; + result.clock = -1; + return result; +} diff --git a/lib/IRremoteESP8266/src/ir_Panasonic.h b/lib/IRremoteESP8266/src/ir_Panasonic.h index 9460a1b0ff..5668e4a57a 100644 --- a/lib/IRremoteESP8266/src/ir_Panasonic.h +++ b/lib/IRremoteESP8266/src/ir_Panasonic.h @@ -36,10 +36,10 @@ #ifdef ARDUINO #include #endif -#include "../src/IRremoteESP8266.h" -#include "../src/IRsend.h" +#include "IRremoteESP8266.h" +#include "IRsend.h" #ifdef UNIT_TEST -#include "../src/IRsend_test.h" +#include "IRsend_test.h" #endif // Constants diff --git a/lib/IRremoteESP8266/src/ir_Pioneer.cpp b/lib/IRremoteESP8266/src/ir_Pioneer.cpp new file mode 100644 index 0000000000..90f58c6ed9 --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_Pioneer.cpp @@ -0,0 +1,138 @@ +// 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 + +// Supports: +// Brand: Pioneer, Model: AV Receivers +// Brand: Pioneer, Model: VSX-324 AV Receiver +// Brand: Pioneer, Model: AXD7690 Remote + +#define __STDC_LIMIT_MACROS +#include +#include +#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. +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 diff --git a/lib/IRremoteESP8266/src/ir_Pronto.cpp b/lib/IRremoteESP8266/src/ir_Pronto.cpp new file mode 100644 index 0000000000..2d4ffa7592 --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_Pronto.cpp @@ -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 +#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 diff --git a/lib/IRremoteESP8266/src/ir_RC5_RC6.cpp b/lib/IRremoteESP8266/src/ir_RC5_RC6.cpp new file mode 100644 index 0000000000..1e0c75b3f1 --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_RC5_RC6.cpp @@ -0,0 +1,454 @@ +// Copyright 2009 Ken Shirriff +// Copyright 2017 David Conran + +/// @file +/// @brief RC-5 & RC-6 support +/// RC-5 & RC-6 support added from https://github.com/z3t0/Arduino-IRremote +/// RC-5X support added by David Conran +/// @see https://en.wikipedia.org/wiki/RC-5 +/// @see http://www.sbprojects.net/knowledge/ir/rc5.php +/// @see https://en.wikipedia.org/wiki/Manchester_code +/// @see https://en.wikipedia.org/wiki/RC-6 +/// @see https://www.sbprojects.net/knowledge/ir/rc6.php +/// @see http://www.pcbheaven.com/userpages/The_Philips_RC6_Protocol/ +/// @see http://www.righto.com/2010/12/64-bit-rc6-codes-arduino-and-xbox.html + +// Supports: +// Brand: Philips, Model: Standard RC-5 (RC5) +// Brand: Philips, Model: RC-5X (RC5X) +// Brand: Philips, Model: Standard RC-6 (RC6) + +#include +#include "IRrecv.h" +#include "IRsend.h" +#include "IRtimer.h" +#include "IRutils.h" + +// Constants +// RC-5/RC-5X +const uint16_t kRc5T1 = 889; +const uint32_t kRc5MinCommandLength = 113778; +const uint32_t kRc5MinGap = kRc5MinCommandLength - kRC5RawBits * (2 * kRc5T1); +const uint16_t kRc5ToggleMask = 0x800; // The 12th bit. +const uint16_t kRc5SamplesMin = 11; + +// RC-6 +const uint16_t kRc6Tick = 444; +const uint16_t kRc6HdrMarkTicks = 6; +const uint16_t kRc6HdrMark = kRc6HdrMarkTicks * kRc6Tick; +const uint16_t kRc6HdrSpaceTicks = 2; +const uint16_t kRc6HdrSpace = kRc6HdrSpaceTicks * kRc6Tick; +const uint16_t kRc6RptLengthTicks = 187; +const uint32_t kRc6RptLength = kRc6RptLengthTicks * kRc6Tick; +const uint32_t kRc6ToggleMask = 0x10000UL; // The 17th bit. +const uint16_t kRc6_36ToggleMask = 0x8000; // The 16th bit. + +// Common (getRClevel()) +const int16_t kMark = 0; +const int16_t kSpace = 1; + +#if SEND_RC5 +/// Send a Philips RC-5/RC-5X packet. +/// Status: RC-5 (stable), RC-5X (alpha) +/// @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 Caller needs to take care of flipping the toggle bit. +/// That bit differentiates between key press & key release. +/// For RC-5 it is the MSB of the data. +/// For RC-5X it is the 2nd MSB of the data. +/// @todo Testing of the RC-5X components. +void IRsend::sendRC5(const uint64_t data, uint16_t nbits, + const uint16_t repeat) { + if (nbits > sizeof(data) * 8) return; // We can't send something that big. + bool skipSpace = true; + bool field_bit = true; + // Set 36kHz IR carrier frequency & a 1/4 (25%) duty cycle. + enableIROut(36, 25); + + if (nbits >= kRC5XBits) { // Is this a RC-5X message? + // field bit is the inverted MSB of RC-5X data. + field_bit = ((data >> (nbits - 1)) ^ 1) & 1; + nbits--; + } + + IRtimer usecTimer = IRtimer(); + for (uint16_t i = 0; i <= repeat; i++) { + usecTimer.reset(); + + // Header + // First start bit (0x1). space, then mark. + if (skipSpace) + skipSpace = false; // First time through, we assume the leading space(). + else + space(kRc5T1); + mark(kRc5T1); + // Field/Second start bit. + if (field_bit) { // Send a 1. Normal for RC-5. + space(kRc5T1); + mark(kRc5T1); + } else { // Send a 0. Special case for RC-5X. Means 7th command bit is 1. + mark(kRc5T1); + space(kRc5T1); + } + + // Data + for (uint64_t mask = 1ULL << (nbits - 1); mask; mask >>= 1) + if (data & mask) { // 1 + space(kRc5T1); // 1 is space, then mark. + mark(kRc5T1); + } else { // 0 + mark(kRc5T1); // 0 is mark, then space. + space(kRc5T1); + } + // Footer + space(std::max(kRc5MinGap, kRc5MinCommandLength - usecTimer.elapsed())); + } +} + +/// Encode a Philips RC-5 data message. +/// Status: Beta / Should be working. +/// @param[in] address The 5-bit address value for the message. +/// @param[in] command The 6-bit command value for the message. +/// @param[in] key_released Indicate if the remote key has been released. +/// @return A message suitable for use in sendRC5(). +uint16_t IRsend::encodeRC5(const uint8_t address, const uint8_t command, + const bool key_released) { + return (key_released << (kRC5Bits - 1)) | ((address & 0x1f) << 6) | + (command & 0x3F); +} + +/// Encode a Philips RC-5X data message. +/// Status: Beta / Should be working. +/// @param[in] address The 5-bit address value for the message. +/// @param[in] command The 7-bit command value for the message. +/// @param[in] key_released Indicate if the remote key has been released. +/// @return A message suitable for use in sendRC5(). +uint16_t IRsend::encodeRC5X(const uint8_t address, const uint8_t command, + const bool key_released) { + // The 2nd start/field bit (MSB of the return value) is the value of the 7th + // command bit. + bool s2 = (command >> 6) & 1; + return ((uint16_t)s2 << (kRC5XBits - 1)) | + encodeRC5(address, command, key_released); +} + +/// Flip the toggle bit of a Philips RC-5/RC-5X data message. +/// Used to indicate a change of remote button's state. +/// Status: STABLE. +/// @param[in] data The existing RC-5/RC-5X message. +/// @return A data message suitable for use in sendRC5() with the toggle bit +/// flipped. +uint64_t IRsend::toggleRC5(const uint64_t data) { + return data ^ kRc5ToggleMask; +} +#endif // SEND_RC5 + +#if SEND_RC6 +/// Flip the toggle bit of a Philips RC-6 data message. +/// Used to indicate a change of remote button's state. +/// Status: STABLE / Should work fine. +/// @param[in] data The existing RC-6 message. +/// @param [in] nbits Nr. of bits in the RC-6 protocol. +/// @return A data message suitable for use in sendRC6() with the toggle bit +/// flipped. +/// @note For RC-6 (20-bits), it is the 17th least significant bit. +/// @note For RC-6 (36-bits/Xbox-360), it is the 16th least significant bit. +uint64_t IRsend::toggleRC6(const uint64_t data, const uint16_t nbits) { + if (nbits == kRC6_36Bits) return data ^ kRc6_36ToggleMask; + return data ^ kRc6ToggleMask; +} + +/// Encode a Philips RC-6 data message. +/// Status: Beta / Should be working. +/// @param[in] address The address (aka. control) value for the message. +/// Includes the field/mode/toggle bits. +/// @param[in] command The 8-bit command value for the message. +/// (aka. information) +/// @param[in] mode Which protocol to use. +/// Defined by nr. of bits in the protocol. +/// @return A data message suitable for use in `sendRC6()`. +uint64_t IRsend::encodeRC6(const uint32_t address, const uint8_t command, + const uint16_t mode) { + switch (mode) { + case kRC6Mode0Bits: + return ((address & 0xFFF) << 8) | (command & 0xFF); + case kRC6_36Bits: + return ((uint64_t)(address & 0xFFFFFFF) << 8) | (command & 0xFF); + default: + return 0; + } +} + +/// Send a Philips RC-6 packet. +/// Status: Stable. +/// @note Caller needs to take care of flipping the toggle bit (The 4th Most +/// Significant Bit). That bit differentiates between key press & key release. +/// @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::sendRC6(const uint64_t data, const uint16_t nbits, + const uint16_t repeat) { + // Check we can send the number of bits requested. + if (nbits > sizeof(data) * 8) return; + // Set 36kHz IR carrier frequency & a 1/3 (33%) duty cycle. + enableIROut(36, 33); + for (uint16_t r = 0; r <= repeat; r++) { + // Header + mark(kRc6HdrMark); + space(kRc6HdrSpace); + // Start bit. + mark(kRc6Tick); // mark, then space == 0x1. + space(kRc6Tick); + // Data + uint16_t bitTime; + for (uint64_t i = 1, mask = 1ULL << (nbits - 1); mask; i++, mask >>= 1) { + if (i == 4) // The fourth bit we send is a "double width trailer bit". + bitTime = 2 * kRc6Tick; // double-wide trailer bit + else + bitTime = kRc6Tick; // Normal bit + if (data & mask) { // 1 + mark(bitTime); + space(bitTime); + } else { // 0 + space(bitTime); + mark(bitTime); + } + } + // Footer + space(kRc6RptLength); + } +} +#endif // SEND_RC6 + +#if (DECODE_RC5 || DECODE_RC6 || DECODE_LASERTAG) +/// Gets one undecoded level at a time from the raw buffer. +/// The RC5/6 decoding is easier if the data is broken into time intervals. +/// E.g. if the buffer has MARK for 2 time intervals and SPACE for 1, +/// successive calls to getRClevel will return MARK, MARK, SPACE. +/// offset and used are updated to keep track of the current position. +/// @param[in,out] results Ptr to the data to decode and where to store the +/// decode result. +/// @param[in,out] offset Ptr to the currect offset to the rawbuf. +/// @param[in,out] used Ptr to the current used counter. +/// @param[in] bitTime Time interval of single bit in microseconds. +/// @param[in] tolerance Percent tolerance to be used in matching. +/// @param[in] excess Extra useconds to add to Marks & removed from Spaces. +/// @param[in] delta A non-scaling (+/-) error margin (in useconds). +/// @param[in] maxwidth Maximum number of successive levels to find in a single +/// level (default is 3) +/// @return MARK, SPACE, or -1 for error. +/// (The measured time interval is not a multiple of t1.) +/// @see https://en.wikipedia.org/wiki/Manchester_code +int16_t IRrecv::getRClevel(decode_results *results, uint16_t *offset, + uint16_t *used, const uint16_t bitTime, + const uint8_t tolerance, const int16_t excess, + const uint16_t delta, const uint8_t maxwidth) { + DPRINT("DEBUG: getRClevel: offset = "); + DPRINTLN(uint64ToString(*offset)); + DPRINT("DEBUG: getRClevel: rawlen = "); + DPRINTLN(uint64ToString(results->rawlen)); + if (*offset >= results->rawlen) { + DPRINTLN("DEBUG: getRClevel: SPACE, past end of rawbuf"); + return kSpace; // After end of recorded buffer, assume SPACE. + } + uint16_t width = results->rawbuf[*offset]; + // If the value of offset is odd, it's a MARK. Even, it's a SPACE. + uint16_t val = ((*offset) % 2) ? kMark : kSpace; + // Check to see if we have hit an inter-message gap (> 20ms). + if (val == kSpace && + (width > 20000 - delta || width > maxwidth * bitTime + delta)) { + DPRINTLN("DEBUG: getRClevel: SPACE, hit end of mesg gap."); + return kSpace; + } + int16_t correction = (val == kMark) ? excess : -excess; + + // Calculate the look-ahead for our current position in the buffer. + uint16_t avail; + // Note: We want to match in greedy order as the other way leads to + // mismatches due to overlaps induced by the correction and tolerance + // values. + for (avail = maxwidth; avail > 0; avail--) { + if (match(width, avail * bitTime + correction, tolerance, delta)) { + break; + } + } + if (!avail) { + DPRINTLN("DEBUG: getRClevel: Unexpected width. Exiting."); + return -1; // The width is not what we expected. + } + + (*used)++; // Count another one of the avail slots as used. + if (*used >= avail) { // Are we out of look-ahead/avail slots? + // Yes, so reset the used counter, and move the offset ahead. + *used = 0; + (*offset)++; + } + if (val == kMark) { + DPRINTLN("DEBUG: getRClevel: MARK"); + } else { + DPRINTLN("DEBUG: getRClevel: SPACE"); + } + + return val; +} +#endif // (DECODE_RC5 || DECODE_RC6 || DECODE_LASERTAG) + +#if DECODE_RC5 +/// Decode the supplied RC-5/RC5X message. +/// Status: RC-5 (stable), RC-5X (alpha) +/// @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 The 'toggle' bit is included as the 6th (MSB) address bit, the MSB of +/// data, & in the count of bits decoded. +/// @todo Serious testing of the RC-5X and strict aspects needs to be done. +bool IRrecv::decodeRC5(decode_results *results, uint16_t offset, + const uint16_t nbits, const bool strict) { + if (results->rawlen <= kRc5SamplesMin + kHeader - 1 + offset) return false; + + // Compliance + if (strict && nbits != kRC5Bits && nbits != kRC5XBits) + return false; // It's neither RC-5 or RC-5X. + + uint16_t used = 0; + bool is_rc5x = false; + uint64_t data = 0; + + // Header + // Get start bit #1. + if (getRClevel(results, &offset, &used, kRc5T1) != kMark) return false; + // Get field/start bit #2 (inverted bit-7 of the command if RC-5X protocol) + uint16_t actual_bits = 1; + int16_t levelA = getRClevel(results, &offset, &used, kRc5T1); + int16_t levelB = getRClevel(results, &offset, &used, kRc5T1); + if (levelA == kSpace && levelB == kMark) { // Matched a 1. + is_rc5x = false; + } else if (levelA == kMark && levelB == kSpace) { // Matched a 0. + if (nbits <= kRC5Bits) return false; // Field bit must be '1' for RC5. + is_rc5x = true; + data = 1; + } else { + return false; // Not what we expected. + } + + // Data + for (; offset < results->rawlen; actual_bits++) { + int16_t levelA = getRClevel(results, &offset, &used, kRc5T1); + int16_t levelB = getRClevel(results, &offset, &used, kRc5T1); + 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 != kRC5Bits && actual_bits != kRC5XBits) + return false; + + // Success + results->value = data; + results->address = (data >> 6) & 0x1F; + results->command = data & 0x3F; + results->repeat = false; + if (is_rc5x) { + results->decode_type = RC5X; + results->command |= ((uint32_t)is_rc5x) << 6; + } else { + results->decode_type = RC5; + actual_bits--; // RC5 doesn't count the field bit as data. + } + results->bits = actual_bits; + return true; +} +#endif // DECODE_RC5 + +#if DECODE_RC6 +/// Decode the supplied RC6 message. +/// Status: Stable. +/// @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. +/// @todo Testing of the strict compliance aspects. +bool IRrecv::decodeRC6(decode_results *results, uint16_t offset, + const uint16_t nbits, const bool strict) { + if (results->rawlen <= kHeader + 2 + 4 + offset) + // Up to the double-wide T bit. + return false; // Smaller than absolute smallest possible RC6 message. + + if (strict) { // Compliance + // Unlike typical protocols, the ability to have mark+space, and space+mark + // as data bits means it is possible to only have nbits of entries for the + // data portion, rather than the typically required 2 * nbits. + // Also due to potential melding with the start bit, we can only count + // the start bit as 1, instead of a more typical 2 value. The header still + // remains as normal. + if (results->rawlen <= nbits + kHeader + 1 + offset) + return false; // Don't have enough entries/samples to be valid. + switch (nbits) { + case kRC6Mode0Bits: + case kRC6_36Bits: + break; + default: + return false; // Asking for the wrong number of bits. + } + } + + // Header + if (!matchMark(results->rawbuf[offset], kRc6HdrMark)) return false; + // Calculate how long the common tick time is based on the header mark. + uint32_t tick = results->rawbuf[offset++] * kRawTick / kRc6HdrMarkTicks; + if (!matchSpace(results->rawbuf[offset++], kRc6HdrSpaceTicks * tick)) + return false; + + uint16_t used = 0; + + // Get the start bit. e.g. 1. + if (getRClevel(results, &offset, &used, tick) != kMark) return false; + if (getRClevel(results, &offset, &used, tick) != kSpace) return false; + + uint16_t actual_bits; + uint64_t data = 0; + + // Data (Warning: Here be dragons^Wpointers!!) + for (actual_bits = 0; offset < results->rawlen; actual_bits++) { + int16_t levelA, levelB; // Next two levels + levelA = getRClevel(results, &offset, &used, tick); + // T bit is double wide; make sure second half matches + if (actual_bits == 3 && levelA != getRClevel(results, &offset, &used, tick)) + return false; + levelB = getRClevel(results, &offset, &used, tick); + // T bit is double wide; make sure second half matches + if (actual_bits == 3 && levelB != getRClevel(results, &offset, &used, tick)) + return false; + if (levelA == kMark && levelB == kSpace) // reversed compared to RC5 + data = (data << 1) | 1; // 1 + else if (levelA == kSpace && levelB == kMark) + data <<= 1; // 0 + else + break; + } + + // More compliance + if (strict && actual_bits != nbits) + return false; // Actual nr. of bits didn't match expected. + + // Success + results->decode_type = RC6; + results->bits = actual_bits; + results->value = data; + results->address = data >> 8; + results->command = data & 0xFF; + return true; +} +#endif // DECODE_RC6 diff --git a/lib/IRremoteESP8266/src/ir_RCMM.cpp b/lib/IRremoteESP8266/src/ir_RCMM.cpp new file mode 100644 index 0000000000..97a3c79b5b --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_RCMM.cpp @@ -0,0 +1,164 @@ +// Copyright 2017 David Conran + +/// @file +/// @brief Support for the Phillips RC-MM protocol. +/// @see http://www.sbprojects.net/knowledge/ir/rcmm.php + +// Supports: +// Brand: Microsoft, Model: XBOX 360 + +#include +#include "IRrecv.h" +#include "IRsend.h" +#include "IRtimer.h" +#include "IRutils.h" + +// Constants +const uint16_t kRcmmTick = 28; // Technically it would be 27.777* +const uint16_t kRcmmHdrMarkTicks = 15; +const uint16_t kRcmmHdrMark = 416; +const uint16_t kRcmmHdrSpaceTicks = 10; +const uint16_t kRcmmHdrSpace = 277; +const uint16_t kRcmmBitMarkTicks = 6; +const uint16_t kRcmmBitMark = 166; +const uint16_t kRcmmBitSpace0Ticks = 10; +const uint16_t kRcmmBitSpace0 = 277; +const uint16_t kRcmmBitSpace1Ticks = 16; +const uint16_t kRcmmBitSpace1 = 444; +const uint16_t kRcmmBitSpace2Ticks = 22; +const uint16_t kRcmmBitSpace2 = 611; +const uint16_t kRcmmBitSpace3Ticks = 28; +const uint16_t kRcmmBitSpace3 = 777; +const uint16_t kRcmmRptLengthTicks = 992; +const uint32_t kRcmmRptLength = 27778; +const uint16_t kRcmmMinGapTicks = 120; +const uint32_t kRcmmMinGap = 3360; +// Use a tolerance of +/-10% when matching some data spaces. +const uint8_t kRcmmTolerance = 10; +const uint16_t kRcmmExcess = 50; + +#if SEND_RCMM +/// Send a Philips RC-MM packet. +/// 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. +void IRsend::sendRCMM(uint64_t data, uint16_t nbits, uint16_t repeat) { + // Set 36kHz IR carrier frequency & a 1/3 (33%) duty cycle. + enableIROut(36, 33); + IRtimer usecs = IRtimer(); + + for (uint16_t r = 0; r <= repeat; r++) { + usecs.reset(); + // Header + mark(kRcmmHdrMark); + space(kRcmmHdrSpace); + // Data + uint64_t mask = 0b11ULL << (nbits - 2); + // RC-MM sends data 2 bits at a time. + for (int32_t i = nbits; i > 0; i -= 2) { + mark(kRcmmBitMark); + // Grab the next Most Significant Bits to send. + switch ((data & mask) >> (i - 2)) { + case 0b00: + space(kRcmmBitSpace0); + break; + case 0b01: + space(kRcmmBitSpace1); + break; + case 0b10: + space(kRcmmBitSpace2); + break; + case 0b11: + space(kRcmmBitSpace3); + break; + } + mask >>= 2; + } + // Footer + mark(kRcmmBitMark); + // Protocol requires us to wait at least kRcmmRptLength usecs from the + // start or kRcmmMinGap usecs. + space(std::max(kRcmmRptLength - usecs.elapsed(), kRcmmMinGap)); + } +} +#endif // SEND_RCMM + +#if DECODE_RCMM +/// Decode a Philips RC-MM packet (between 12 & 32 bits) if possible. +/// Status: STABLE / Should 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::decodeRCMM(decode_results *results, uint16_t offset, + const uint16_t nbits, const bool strict) { + uint64_t data = 0; + + if (results->rawlen <= 4 + offset - 1) + return false; // Not enough entries to ever be RCMM. + + // Calc the maximum size in bits, the message can be, or that we can accept. + int16_t maxBitSize = + std::min((uint16_t)results->rawlen - 5, (uint16_t)sizeof(data) * 8); + // Compliance + if (strict) { + // Technically the spec says bit sizes should be 12 xor 24. however + // 32 bits has been seen from a device. We are going to assume + // 12 <= bits <= 32 is the 'required' bit length for the spec. + if (maxBitSize < 12 || maxBitSize > 32) return false; + if (maxBitSize < nbits) + return false; // Short cut, we can never reach the expected nr. of bits. + } + // Header decode + if (!matchMark(results->rawbuf[offset], kRcmmHdrMark)) return false; + // Calculate how long the common tick time is based on the header mark. + uint32_t m_tick = results->rawbuf[offset++] * kRawTick / kRcmmHdrMarkTicks; + if (!matchSpace(results->rawbuf[offset], kRcmmHdrSpace)) return false; + // Calculate how long the common tick time is based on the header space. + uint32_t s_tick = results->rawbuf[offset++] * kRawTick / kRcmmHdrSpaceTicks; + + // Data decode + // RC-MM has two bits of data per mark/space pair. + uint16_t actualBits; + for (actualBits = 0; actualBits < maxBitSize; actualBits += 2, offset++) { + if (!match(results->rawbuf[offset++], kRcmmBitMarkTicks * m_tick)) + return false; + + data <<= 2; + // Use non-default tolerance & excess for matching some of the spaces as the + // defaults are too generous and causes mis-matches in some cases. + if (match(results->rawbuf[offset], kRcmmBitSpace0Ticks * s_tick)) + data += 0; + else if (match(results->rawbuf[offset], kRcmmBitSpace1Ticks * s_tick)) + data += 1; + else if (match(results->rawbuf[offset], kRcmmBitSpace2Ticks * s_tick, + kRcmmTolerance)) + data += 2; + else if (match(results->rawbuf[offset], kRcmmBitSpace3Ticks * s_tick, + kRcmmTolerance)) + data += 3; + else + return false; + } + // Footer decode + if (!match(results->rawbuf[offset++], kRcmmBitMarkTicks * m_tick)) + return false; + if (offset < results->rawlen && + !matchAtLeast(results->rawbuf[offset], kRcmmMinGapTicks * s_tick)) + return false; + + // Compliance + if (strict && actualBits != nbits) return false; + + // Success + results->value = data; + results->decode_type = RCMM; + results->bits = actualBits; + results->address = 0; + results->command = 0; + return true; +} +#endif // DECODE_RCMM diff --git a/lib/IRremoteESP8266/src/ir_Rhoss.cpp b/lib/IRremoteESP8266/src/ir_Rhoss.cpp new file mode 100644 index 0000000000..f906f49be4 --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_Rhoss.cpp @@ -0,0 +1,364 @@ +// Copyright 2021 Tom Rosenback + +/// @file +/// @brief Support for Rhoss protocols. + +#include "ir_Rhoss.h" +#include +#include +#include "IRrecv.h" +#include "IRsend.h" +#include "IRtext.h" +#include "IRutils.h" + +const uint16_t kRhossHdrMark = 3042; +const uint16_t kRhossHdrSpace = 4248; +const uint16_t kRhossBitMark = 648; +const uint16_t kRhossOneSpace = 1545; +const uint16_t kRhossZeroSpace = 457; +const uint32_t kRhossGap = kDefaultMessageGap; +const uint16_t kRhossFreq = 38; + +using irutils::addBoolToString; +using irutils::addModeToString; +using irutils::addFanToString; +using irutils::addTempToString; + +#if SEND_RHOSS +/// Send a Rhoss 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::sendRhoss(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 < kRhossStateLength) return; + + // We always send a message, even for repeat=0, hence '<= repeat'. + for (uint16_t r = 0; r <= repeat; r++) { + sendGeneric(kRhossHdrMark, kRhossHdrSpace, kRhossBitMark, + kRhossOneSpace, kRhossBitMark, kRhossZeroSpace, + kRhossBitMark, kRhossZeroSpace, + data, nbytes, kRhossFreq, false, 0, kDutyDefault); + mark(kRhossBitMark); + // Gap + space(kRhossGap); + } +} +#endif // SEND_RHOSS + +#if DECODE_RHOSS +/// Decode the supplied Rhoss 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::decodeRhoss(decode_results *results, uint16_t offset, + const uint16_t nbits, const bool strict) { + if (strict && nbits != kRhossBits) return false; + + if (results->rawlen <= 2 * nbits + kHeader + kFooter - 1 + offset) { + return false; // Can't possibly be a valid Rhoss message. + } + + uint16_t used; + // Header + Data Block (96 bits) + Footer + used = matchGeneric(results->rawbuf + offset, results->state, + results->rawlen - offset, kRhossBits, + kRhossHdrMark, kRhossHdrSpace, + kRhossBitMark, kRhossOneSpace, + kRhossBitMark, kRhossZeroSpace, + kRhossBitMark, kRhossZeroSpace, + false, kUseDefTol, kMarkExcess, false); + + if (!used) return false; + offset += used; + + // Footer (Part 2) + if (!matchMark(results->rawbuf[offset++], kRhossBitMark)) { + return false; + } + + if (offset < results->rawlen && + !matchAtLeast(results->rawbuf[offset], kRhossGap)) { + return false; + } + + if (strict && !IRRhossAc::validChecksum(results->state)) return false; + + // Success + results->decode_type = decode_type_t::RHOSS; + 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_RHOSS + +/// 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? +IRRhossAc::IRRhossAc(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 IRRhossAc::begin(void) { _irsend.begin(); } + +#if SEND_RHOSS +/// Send the current internal state as an IR message. +/// @param[in] repeat Nr. of times the message will be repeated. +void IRRhossAc::send(const uint16_t repeat) { + _irsend.sendRhoss(getRaw(), kRhossStateLength, repeat); +} +#endif // SEND_RHOSS + +/// 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 IRRhossAc::calcChecksum(const uint8_t state[], const uint16_t length) { + 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 size of the state. +/// @return A boolean indicating if it's checksum is valid. +bool IRRhossAc::validChecksum(const uint8_t state[], const uint16_t length) { + return (state[length - 1] == IRRhossAc::calcChecksum(state, length)); +} + +/// Update the checksum value for the internal state. +void IRRhossAc::checksum(void) { + _.Sum = IRRhossAc::calcChecksum(_.raw, kRhossStateLength); + _.raw[kRhossStateLength - 1] = _.Sum; +} + +/// Reset the internals of the object to a known good state. +void IRRhossAc::stateReset(void) { + for (uint8_t i = 1; i < kRhossStateLength; i++) _.raw[i] = 0x0; + _.raw[0] = 0xAA; + _.raw[2] = 0x60; + _.raw[6] = 0x54; + _.Power = kRhossDefaultPower; + _.Fan = kRhossDefaultFan; + _.Mode = kRhossDefaultMode; + _.Swing = kRhossDefaultSwing; + _.Temp = kRhossDefaultTemp - kRhossTempMin; +} + +/// 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* IRRhossAc::getRaw(void) { + checksum(); // Ensure correct bit array before returning + return _.raw; +} + +/// Set the raw state of the object. +/// @param[state] state The raw state from the native IR message. +void IRRhossAc::setRaw(const uint8_t state[]) { + std::memcpy(_.raw, state, kRhossStateLength); +} + +/// Set the internal state to have the power on. +void IRRhossAc::on(void) { setPower(true); } + +/// Set the internal state to have the power off. +void IRRhossAc::off(void) { setPower(false); } + +/// Set the internal state to have the desired power. +/// @param[in] on The desired power state. +void IRRhossAc::setPower(const bool on) { + _.Power = (on ? kRhossPowerOn : kRhossPowerOff); +} + +/// Get the power setting from the internal state. +/// @return A boolean indicating the power setting. +bool IRRhossAc::getPower(void) const { + return _.Power == kRhossPowerOn; +} + +/// Set the temperature. +/// @param[in] degrees The temperature in degrees celsius. +void IRRhossAc::setTemp(const uint8_t degrees) { + uint8_t temp = std::max(kRhossTempMin, degrees); + _.Temp = std::min(kRhossTempMax, temp) - kRhossTempMin; +} + +/// Get the current temperature setting. +/// @return Get current setting for temp. in degrees celsius. +uint8_t IRRhossAc::getTemp(void) const { + return _.Temp + kRhossTempMin; +} + +/// Set the speed of the fan. +/// @param[in] speed The desired setting. +void IRRhossAc::setFan(const uint8_t speed) { + switch (speed) { + case kRhossFanAuto: + case kRhossFanMin: + case kRhossFanMed: + case kRhossFanMax: + _.Fan = speed; + break; + default: + _.Fan = kRhossFanAuto; + } +} + +/// Get the current fan speed setting. +/// @return The current fan speed. +uint8_t IRRhossAc::getFan(void) const { + return _.Fan; +} + +/// Set the Vertical Swing mode of the A/C. +/// @param[in] state true, the Swing is on. false, the Swing is off. +void IRRhossAc::setSwing(const bool state) { + _.Swing = state; +} + +/// Get the Vertical Swing speed of the A/C. +/// @return The native swing speed setting. +uint8_t IRRhossAc::getSwing(void) const { + return _.Swing; +} + +/// Get the current operation mode setting. +/// @return The current operation mode. +uint8_t IRRhossAc::getMode(void) const { + return _.Mode; +} + +/// Set the desired operation mode. +/// @param[in] mode The desired operation mode. +void IRRhossAc::setMode(const uint8_t mode) { + switch (mode) { + case kRhossModeFan: + case kRhossModeCool: + case kRhossModeDry: + case kRhossModeHeat: + case kRhossModeAuto: + _.Mode = mode; + return; + default: + _.Mode = kRhossDefaultMode; + 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 IRRhossAc::convertMode(const stdAc::opmode_t mode) { + switch (mode) { + case stdAc::opmode_t::kCool: + return kRhossModeCool; + case stdAc::opmode_t::kHeat: + return kRhossModeHeat; + case stdAc::opmode_t::kDry: + return kRhossModeDry; + case stdAc::opmode_t::kFan: + return kRhossModeFan; + case stdAc::opmode_t::kAuto: + return kRhossModeAuto; + default: + return kRhossDefaultMode; + } +} + +/// 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 IRRhossAc::convertFan(const stdAc::fanspeed_t speed) { + switch (speed) { + case stdAc::fanspeed_t::kMin: + case stdAc::fanspeed_t::kLow: + return kRhossFanMin; + case stdAc::fanspeed_t::kMedium: + return kRhossFanMed; + case stdAc::fanspeed_t::kHigh: + case stdAc::fanspeed_t::kMax: + return kRhossFanMax; + default: + return kRhossDefaultFan; + } +} + +/// 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 IRRhossAc::toCommonMode(const uint8_t mode) { + switch (mode) { + case kRhossModeCool: return stdAc::opmode_t::kCool; + case kRhossModeHeat: return stdAc::opmode_t::kHeat; + case kRhossModeDry: return stdAc::opmode_t::kDry; + case kRhossModeFan: return stdAc::opmode_t::kFan; + case kRhossModeAuto: return stdAc::opmode_t::kAuto; + 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 IRRhossAc::toCommonFanSpeed(const uint8_t speed) { + switch (speed) { + case kRhossFanMax: return stdAc::fanspeed_t::kMax; + case kRhossFanMed: return stdAc::fanspeed_t::kMedium; + case kRhossFanMin: return stdAc::fanspeed_t::kMin; + case kRhossFanAuto: + 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 IRRhossAc::toCommon(void) const { + stdAc::state_t result; + result.protocol = decode_type_t::RHOSS; + result.power = getPower(); + result.mode = toCommonMode(_.Mode); + result.celsius = true; + result.degrees = _.Temp; + result.fanspeed = toCommonFanSpeed(_.Fan); + result.swingv = _.Swing ? stdAc::swingv_t::kAuto : stdAc::swingv_t::kOff; + // Not supported. + result.model = -1; + result.turbo = false; + 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 IRRhossAc::toString(void) const { + String result = ""; + result.reserve(70); // Reserve some heap for the string to reduce fragging. + result += addBoolToString(getPower(), kPowerStr, false); + result += addModeToString(getMode(), kRhossModeAuto, kRhossModeCool, + kRhossModeHeat, kRhossModeDry, kRhossModeFan); + result += addTempToString(getTemp()); + result += addFanToString(getFan(), kRhossFanMax, kRhossFanMin, + kRhossFanAuto, kRhossFanAuto, + kRhossFanMed); + result += addBoolToString(getSwing(), kSwingVStr); + return result; +} diff --git a/lib/IRremoteESP8266/src/ir_Rhoss.h b/lib/IRremoteESP8266/src/ir_Rhoss.h index e3a70aa431..8f66ce7384 100644 --- a/lib/IRremoteESP8266/src/ir_Rhoss.h +++ b/lib/IRremoteESP8266/src/ir_Rhoss.h @@ -13,10 +13,10 @@ #ifndef UNIT_TEST #include #endif -#include "../src/IRremoteESP8266.h" -#include "../src/IRsend.h" +#include "IRremoteESP8266.h" +#include "IRsend.h" #ifdef UNIT_TEST -#include "../src/IRsend_test.h" +#include "IRsend_test.h" #endif diff --git a/lib/IRremoteESP8266/src/ir_Samsung.cpp b/lib/IRremoteESP8266/src/ir_Samsung.cpp new file mode 100644 index 0000000000..9e1ad46690 --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_Samsung.cpp @@ -0,0 +1,832 @@ +// Copyright 2009 Ken Shirriff +// Copyright 2017, 2018, 2019 David Conran +/// @file +/// @brief Support for Samsung protocols. +/// Samsung originally added from https://github.com/shirriff/Arduino-IRremote/ +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/505 +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/621 +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1062 +/// @see http://elektrolab.wz.cz/katalog/samsung_protocol.pdf +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1538 (Checksum) + +#include "ir_Samsung.h" +#include +#include +#ifndef ARDUINO +#include +#endif +#include "IRrecv.h" +#include "IRsend.h" +#include "IRtext.h" +#include "IRutils.h" + +// Constants +const uint16_t kSamsungTick = 560; +const uint16_t kSamsungHdrMarkTicks = 8; +const uint16_t kSamsungHdrMark = kSamsungHdrMarkTicks * kSamsungTick; +const uint16_t kSamsungHdrSpaceTicks = 8; +const uint16_t kSamsungHdrSpace = kSamsungHdrSpaceTicks * kSamsungTick; +const uint16_t kSamsungBitMarkTicks = 1; +const uint16_t kSamsungBitMark = kSamsungBitMarkTicks * kSamsungTick; +const uint16_t kSamsungOneSpaceTicks = 3; +const uint16_t kSamsungOneSpace = kSamsungOneSpaceTicks * kSamsungTick; +const uint16_t kSamsungZeroSpaceTicks = 1; +const uint16_t kSamsungZeroSpace = kSamsungZeroSpaceTicks * kSamsungTick; +const uint16_t kSamsungRptSpaceTicks = 4; +const uint16_t kSamsungRptSpace = kSamsungRptSpaceTicks * kSamsungTick; +const uint16_t kSamsungMinMessageLengthTicks = 193; +const uint32_t kSamsungMinMessageLength = + kSamsungMinMessageLengthTicks * kSamsungTick; +const uint16_t kSamsungMinGapTicks = + kSamsungMinMessageLengthTicks - + (kSamsungHdrMarkTicks + kSamsungHdrSpaceTicks + + kSamsungBits * (kSamsungBitMarkTicks + kSamsungOneSpaceTicks) + + kSamsungBitMarkTicks); +const uint32_t kSamsungMinGap = kSamsungMinGapTicks * kSamsungTick; + +const uint16_t kSamsungAcHdrMark = 690; +const uint16_t kSamsungAcHdrSpace = 17844; +const uint8_t kSamsungAcSections = 2; +const uint16_t kSamsungAcSectionMark = 3086; +const uint16_t kSamsungAcSectionSpace = 8864; +const uint16_t kSamsungAcSectionGap = 2886; +const uint16_t kSamsungAcBitMark = 586; +const uint16_t kSamsungAcOneSpace = 1432; +const uint16_t kSamsungAcZeroSpace = 436; + +// Data from https://github.com/crankyoldgit/IRremoteESP8266/issues/1220 +// Values calculated based on the average of ten messages. +const uint16_t kSamsung36HdrMark = 4515; /// < uSeconds +const uint16_t kSamsung36HdrSpace = 4438; /// < uSeconds +const uint16_t kSamsung36BitMark = 512; /// < uSeconds +const uint16_t kSamsung36OneSpace = 1468; /// < uSeconds +const uint16_t kSamsung36ZeroSpace = 490; /// < uSeconds + +using irutils::addBoolToString; +using irutils::addFanToString; +using irutils::addIntToString; +using irutils::addLabeledString; +using irutils::addModeToString; +using irutils::addTempToString; + +#if SEND_SAMSUNG +/// Send a 32-bit Samsung 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. +/// @see http://elektrolab.wz.cz/katalog/samsung_protocol.pdf +/// @note Samsung has a separate message to indicate a repeat, like NEC does. +/// @todo Confirm that is actually how Samsung sends a repeat. +/// The refdoc doesn't indicate it is true. +void IRsend::sendSAMSUNG(const uint64_t data, const uint16_t nbits, + const uint16_t repeat) { + sendGeneric(kSamsungHdrMark, kSamsungHdrSpace, kSamsungBitMark, + kSamsungOneSpace, kSamsungBitMark, kSamsungZeroSpace, + kSamsungBitMark, kSamsungMinGap, kSamsungMinMessageLength, data, + nbits, 38, true, repeat, 33); +} + +/// Construct a raw Samsung message from the supplied customer(address) & +/// command. +/// Status: STABLE / Should be working. +/// @param[in] customer The customer code. (aka. Address) +/// @param[in] command The command code. +/// @return A raw 32-bit Samsung message suitable for `sendSAMSUNG()`. +uint32_t IRsend::encodeSAMSUNG(const uint8_t customer, const uint8_t command) { + uint8_t revcustomer = reverseBits(customer, sizeof(customer) * 8); + uint8_t revcommand = reverseBits(command, sizeof(command) * 8); + return ((revcommand ^ 0xFF) | (revcommand << 8) | (revcustomer << 16) | + (revcustomer << 24)); +} +#endif + +#if DECODE_SAMSUNG +/// Decode the supplied Samsung 32-bit message. +/// Status: STABLE +/// @note Samsung messages whilst 32 bits in size, only contain 16 bits of +/// distinct data. e.g. In transmition order: +/// customer_byte + customer_byte(same) + address_byte + invert(address_byte) +/// @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 LG 32bit protocol appears near identical to the Samsung protocol. +/// They differ on their compliance criteria and how they repeat. +/// @see http://elektrolab.wz.cz/katalog/samsung_protocol.pdf +bool IRrecv::decodeSAMSUNG(decode_results *results, uint16_t offset, + const uint16_t nbits, const bool strict) { + if (strict && nbits != kSamsungBits) + return false; // We expect Samsung to be 32 bits of message. + + uint64_t data = 0; + + // Match Header + Data + Footer + if (!matchGeneric(results->rawbuf + offset, &data, + results->rawlen - offset, nbits, + kSamsungHdrMark, kSamsungHdrSpace, + kSamsungBitMark, kSamsungOneSpace, + kSamsungBitMark, kSamsungZeroSpace, + kSamsungBitMark, kSamsungMinGap, true)) return false; + // Compliance + // According to the spec, the customer (address) code is the first 8 + // transmitted bits. It's then repeated. Check for that. + uint8_t address = data >> 24; + if (strict && address != ((data >> 16) & 0xFF)) return false; + // Spec says the command code is the 3rd block of transmitted 8-bits, + // followed by the inverted command code. + uint8_t command = (data & 0xFF00) >> 8; + if (strict && command != ((data & 0xFF) ^ 0xFF)) return false; + + // Success + results->bits = nbits; + results->value = data; + results->decode_type = SAMSUNG; + // command & address need to be reversed as they are transmitted LSB first, + results->command = reverseBits(command, sizeof(command) * 8); + results->address = reverseBits(address, sizeof(address) * 8); + return true; +} +#endif + +#if SEND_SAMSUNG36 +/// Send a Samsung 36-bit 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. +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/621 +void IRsend::sendSamsung36(const uint64_t data, const uint16_t nbits, + const uint16_t repeat) { + if (nbits < 16) return; // To small to send. + for (uint16_t r = 0; r <= repeat; r++) { + // Block #1 (16 bits) + sendGeneric(kSamsung36HdrMark, kSamsung36HdrSpace, + kSamsung36BitMark, kSamsung36OneSpace, + kSamsung36BitMark, kSamsung36ZeroSpace, + kSamsung36BitMark, kSamsung36HdrSpace, + data >> (nbits - 16), 16, 38, true, 0, kDutyDefault); + // Block #2 (The rest, typically 20 bits) + sendGeneric(0, 0, // No header + kSamsung36BitMark, kSamsung36OneSpace, + kSamsung36BitMark, kSamsung36ZeroSpace, + kSamsung36BitMark, kSamsungMinGap, // Gap is just a guess. + // Mask off the rest of the bits. + data & ((1ULL << (nbits - 16)) - 1), + nbits - 16, 38, true, 0, kDutyDefault); + } +} +#endif // SEND_SAMSUNG36 + +#if DECODE_SAMSUNG36 +/// Decode the supplied Samsung36 message. +/// Status: STABLE / Expected 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. +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/621 +bool IRrecv::decodeSamsung36(decode_results *results, uint16_t offset, + const uint16_t nbits, const bool strict) { + if (results->rawlen < 2 * nbits + kHeader + kFooter * 2 - 1 + offset) + return false; // Can't possibly be a valid Samsung message. + // We need to be looking for > 16 bits to make sense. + if (nbits <= 16) return false; + if (strict && nbits != kSamsung36Bits) + return false; // We expect nbits to be 36 bits of message. + + uint64_t data = 0; + + // Match Header + Data + Footer + uint16_t used; + used = matchGeneric(results->rawbuf + offset, &data, + results->rawlen - offset, 16, + kSamsung36HdrMark, kSamsung36HdrSpace, + kSamsung36BitMark, kSamsung36OneSpace, + kSamsung36BitMark, kSamsung36ZeroSpace, + kSamsung36BitMark, kSamsung36HdrSpace, false); + if (!used) return false; + offset += used; + // Data (Block #2) + uint64_t data2 = 0; + if (!matchGeneric(results->rawbuf + offset, &data2, + results->rawlen - offset, nbits - 16, + 0, 0, + kSamsung36BitMark, kSamsung36OneSpace, + kSamsung36BitMark, kSamsung36ZeroSpace, + kSamsung36BitMark, kSamsungMinGap, true)) return false; + data <<= (nbits - 16); + data += data2; + + // Success + results->bits = nbits; + results->value = data; + results->decode_type = SAMSUNG36; + results->command = data & ((1ULL << (nbits - 16)) - 1); + results->address = data >> (nbits - 16); + return true; +} +#endif // DECODE_SAMSUNG36 + +#if SEND_SAMSUNG_AC +/// Send a Samsung A/C message. +/// Status: Stable / Known working. +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/505 +/// @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::sendSamsungAC(const uint8_t data[], const uint16_t nbytes, + const uint16_t repeat) { + if (nbytes < kSamsungAcStateLength && nbytes % kSamsungAcSectionLength) + return; // Not an appropriate number of bytes to send a proper message. + + enableIROut(38); + for (uint16_t r = 0; r <= repeat; r++) { + // Header + mark(kSamsungAcHdrMark); + space(kSamsungAcHdrSpace); + // Send in 7 byte sections. + for (uint16_t offset = 0; offset < nbytes; + offset += kSamsungAcSectionLength) { + sendGeneric(kSamsungAcSectionMark, kSamsungAcSectionSpace, + kSamsungAcBitMark, kSamsungAcOneSpace, kSamsungAcBitMark, + kSamsungAcZeroSpace, kSamsungAcBitMark, kSamsungAcSectionGap, + data + offset, kSamsungAcSectionLength, // 7 bytes == 56 bits + 38000, false, 0, 50); // Send in LSBF order + } + // Complete made up guess at inter-message gap. + space(kDefaultMessageGap - kSamsungAcSectionGap); + } +} +#endif // SEND_SAMSUNG_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? +IRSamsungAc::IRSamsungAc(const uint16_t pin, const bool inverted, + const bool use_modulation) + : _irsend(pin, inverted, use_modulation) { + stateReset(); +} + +/// Reset the internal state of the emulation. +/// @param[in] forcepower A flag indicating if force sending a special power +/// message with the first `send()` call. +/// @param[in] initialPower Set the initial power state. True, on. False, off. +void IRSamsungAc::stateReset(const bool forcepower, const bool initialPower) { + static const uint8_t kReset[kSamsungAcExtendedStateLength] = { + 0x02, 0x92, 0x0F, 0x00, 0x00, 0x00, 0xF0, + 0x01, 0x02, 0xAE, 0x71, 0x00, 0x15, 0xF0}; + std::memcpy(_.raw, kReset, kSamsungAcExtendedStateLength); + _forcepower = forcepower; + _lastsentpowerstate = initialPower; + setPower(initialPower); +} + +/// Set up hardware to be able to send a message. +void IRSamsungAc::begin(void) { _irsend.begin(); } + +/// Get the existing checksum for a given state section. +/// @param[in] section The array to extract the checksum from. +/// @return The existing checksum value. +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1538#issuecomment-894645947 +uint8_t IRSamsungAc::getSectionChecksum(const uint8_t *section) { + return ((GETBITS8(*(section + 2), kLowNibble, kNibbleSize) << kNibbleSize) + + GETBITS8(*(section + 1), kHighNibble, kNibbleSize)); +} + +/// Calculate the checksum for a given state section. +/// @param[in] section The array to calc the checksum of. +/// @return The calculated checksum value. +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1538#issuecomment-894645947 +uint8_t IRSamsungAc::calcSectionChecksum(const uint8_t *section) { + uint8_t sum = 0; + + sum += countBits(*section, 8); // Include the entire first byte + // The lower half of the second byte. + sum += countBits(GETBITS8(*(section + 1), kLowNibble, kNibbleSize), 8); + // The upper half of the third byte. + sum += countBits(GETBITS8(*(section + 2), kHighNibble, kNibbleSize), 8); + // The next 4 bytes. + sum += countBits(section + 3, 4); + // Bitwise invert the result. + return sum ^ UINT8_MAX; +} + +/// 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 IRSamsungAc::validChecksum(const uint8_t state[], const uint16_t length) { + bool result = true; + const uint16_t maxlength = + (length > kSamsungAcExtendedStateLength) ? kSamsungAcExtendedStateLength + : length; + for (uint16_t offset = 0; + offset + kSamsungAcSectionLength <= maxlength; + offset += kSamsungAcSectionLength) + result &= (getSectionChecksum(state + offset) == + calcSectionChecksum(state + offset)); + return result; +} + +/// Update the checksum for the internal state. +void IRSamsungAc::checksum(void) { + uint8_t sectionsum = calcSectionChecksum(_.raw); + _.Sum1Upper = GETBITS8(sectionsum, kHighNibble, kNibbleSize); + _.Sum1Lower = GETBITS8(sectionsum, kLowNibble, kNibbleSize); + sectionsum = calcSectionChecksum(_.raw + kSamsungAcSectionLength); + _.Sum2Upper = GETBITS8(sectionsum, kHighNibble, kNibbleSize); + _.Sum2Lower = GETBITS8(sectionsum, kLowNibble, kNibbleSize); + sectionsum = calcSectionChecksum(_.raw + kSamsungAcSectionLength * 2); + _.Sum3Upper = GETBITS8(sectionsum, kHighNibble, kNibbleSize); + _.Sum3Lower = GETBITS8(sectionsum, kLowNibble, kNibbleSize); +} + +#if SEND_SAMSUNG_AC +/// Send the current internal state as an IR message. +/// @param[in] repeat Nr. of times the message will be repeated. +/// @param[in] calcchecksum Do we update the checksum before sending? +/// @note Use for most function/mode/settings changes to the unit. +/// i.e. When the device is already running. +void IRSamsungAc::send(const uint16_t repeat, const bool calcchecksum) { + // Do we need to send a the special power on/off message? i.e. An Extended Msg + if (getPower() != _lastsentpowerstate || _forcepower) { // We do. + sendExtended(repeat, calcchecksum); + _forcepower = false; // It has now been sent, so clear the flag if set. + } else { // No, it's just a normal message. + if (calcchecksum) checksum(); + _irsend.sendSamsungAC(_.raw, kSamsungAcStateLength, repeat); + } +} + +/// Send the extended current internal state as an IR message. +/// @param[in] repeat Nr. of times the message will be repeated. +/// @param[in] calcchecksum Do we update the checksum before sending? +/// @note Use this for when you need to power on/off the device. +/// Samsung A/C requires an extended length message when you want to +/// change the power operating mode of the A/C unit. +void IRSamsungAc::sendExtended(const uint16_t repeat, const bool calcchecksum) { + static const uint8_t extended_middle_section[kSamsungAcSectionLength] = { + 0x01, 0xD2, 0x0F, 0x00, 0x00, 0x00, 0x00}; + if (calcchecksum) checksum(); + // Copy/convert the internal state to an extended state by + // copying the second section to the third section, and inserting the extended + // middle (second) section. + std::memcpy(_.raw + 2 * kSamsungAcSectionLength, + _.raw + kSamsungAcSectionLength, + kSamsungAcSectionLength); + std::memcpy(_.raw + kSamsungAcSectionLength, extended_middle_section, + kSamsungAcSectionLength); + // Send it. + _irsend.sendSamsungAC(_.raw, kSamsungAcExtendedStateLength, repeat); + // Now revert it by copying the third section over the second section. + std::memcpy(_.raw + kSamsungAcSectionLength, + _.raw + 2* kSamsungAcSectionLength, + kSamsungAcSectionLength); + _lastsentpowerstate = getPower(); // Remember the last power state sent. +} + +/// Send the special extended "On" message as the library can't seem to +/// reproduce this message automatically. +/// @param[in] repeat Nr. of times the message will be repeated. +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/604#issuecomment-475020036 +void IRSamsungAc::sendOn(const uint16_t repeat) { + const uint8_t extended_state[kSamsungAcExtendedStateLength] = { + 0x02, 0x92, 0x0F, 0x00, 0x00, 0x00, 0xF0, + 0x01, 0xD2, 0x0F, 0x00, 0x00, 0x00, 0x00, + 0x01, 0xE2, 0xFE, 0x71, 0x80, 0x11, 0xF0}; + _irsend.sendSamsungAC(extended_state, kSamsungAcExtendedStateLength, repeat); + _lastsentpowerstate = true; // On +} + +/// Send the special extended "Off" message as the library can't seem to +/// reproduce this message automatically. +/// @param[in] repeat Nr. of times the message will be repeated. +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/604#issuecomment-475020036 +void IRSamsungAc::sendOff(const uint16_t repeat) { + const uint8_t extended_state[kSamsungAcExtendedStateLength] = { + 0x02, 0xB2, 0x0F, 0x00, 0x00, 0x00, 0xC0, + 0x01, 0xD2, 0x0F, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x02, 0xFF, 0x71, 0x80, 0x11, 0xC0}; + _irsend.sendSamsungAC(extended_state, kSamsungAcExtendedStateLength, repeat); + _lastsentpowerstate = false; // Off +} +#endif // SEND_SAMSUNG_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 *IRSamsungAc::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 IRSamsungAc::setRaw(const uint8_t new_code[], const uint16_t length) { + std::memcpy(_.raw, new_code, std::min(length, + kSamsungAcExtendedStateLength)); + // Shrink the extended state into a normal state. + if (length > kSamsungAcStateLength) { + for (uint8_t i = kSamsungAcStateLength; i < length; i++) + _.raw[i - kSamsungAcSectionLength] = _.raw[i]; + } +} + +/// Set the requested power state of the A/C to on. +void IRSamsungAc::on(void) { setPower(true); } + +/// Set the requested power state of the A/C to off. +void IRSamsungAc::off(void) { setPower(false); } + +/// Change the power setting. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRSamsungAc::setPower(const bool on) { + _.Power1 = !on; // Cleared when on. + _.Power6 = (on ? 0b11 : 0b00); +} + +/// Get the value of the current power setting. +/// @return true, the setting is on. false, the setting is off. +bool IRSamsungAc::getPower(void) const { + return (_.Power6 == 0b11) && !_.Power1; +} + +/// Set the temperature. +/// @param[in] temp The temperature in degrees celsius. +void IRSamsungAc::setTemp(const uint8_t temp) { + uint8_t newtemp = std::max(kSamsungAcMinTemp, temp); + newtemp = std::min(kSamsungAcMaxTemp, newtemp); + _.Temp = newtemp - kSamsungAcMinTemp; +} + +/// Get the current temperature setting. +/// @return The current setting for temp. in degrees celsius. +uint8_t IRSamsungAc::getTemp(void) const { + return _.Temp + kSamsungAcMinTemp; +} + +/// Set the operating mode of the A/C. +/// @param[in] mode The desired operating mode. +void IRSamsungAc::setMode(const uint8_t mode) { + // If we get an unexpected mode, default to AUTO. + uint8_t newmode = mode; + if (newmode > kSamsungAcHeat) newmode = kSamsungAcAuto; + _.Mode = newmode; + + // Auto mode has a special fan setting valid only in auto mode. + if (newmode == kSamsungAcAuto) { + _.Fan = kSamsungAcFanAuto2; + } else { + // Non-Auto can't have this fan setting + if (_.Fan == kSamsungAcFanAuto2) + _.Fan = kSamsungAcFanAuto; // Default to something safe. + } +} + +/// Get the operating mode setting of the A/C. +/// @return The current operating mode setting. +uint8_t IRSamsungAc::getMode(void) const { + return _.Mode; +} + +/// Set the speed of the fan. +/// @param[in] speed The desired setting. +void IRSamsungAc::setFan(const uint8_t speed) { + switch (speed) { + case kSamsungAcFanAuto: + case kSamsungAcFanLow: + case kSamsungAcFanMed: + case kSamsungAcFanHigh: + case kSamsungAcFanTurbo: + if (_.Mode == kSamsungAcAuto) return; // Not valid in Auto mode. + break; + case kSamsungAcFanAuto2: // Special fan setting for when in Auto mode. + if (_.Mode != kSamsungAcAuto) return; + break; + default: + return; + } + _.Fan = speed; +} + +/// Get the current fan speed setting. +/// @return The current fan speed/mode. +uint8_t IRSamsungAc::getFan(void) const { + return _.Fan; +} + +/// Get the vertical swing setting of the A/C. +/// @return true, the setting is on. false, the setting is off. +/// @todo (Hollako) Explain why sometimes the LSB of remote_state[9] is a 1. +/// e.g. 0xAE or 0XAF for swing move. +bool IRSamsungAc::getSwing(void) const { + return _.Swing == kSamsungAcSwingMove; +} + +/// Set the vertical swing setting of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +/// @todo (Hollako) Explain why sometimes the LSB of remote_state[9] is a 1. +/// e.g. 0xAE or 0XAF for swing move. +void IRSamsungAc::setSwing(const bool on) { + _.Swing = (on ? kSamsungAcSwingMove : kSamsungAcSwingStop); +} + +/// Get the Beep setting of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRSamsungAc::getBeep(void) const { + return _.Beep; +} + +/// Set the Beep setting of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRSamsungAc::setBeep(const bool on) { + _.Beep = on; +} + +/// Get the Clean setting of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRSamsungAc::getClean(void) const { + return _.Clean10 && _.Clean11; +} + +/// Set the Clean setting of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRSamsungAc::setClean(const bool on) { + _.Clean10 = on; + _.Clean11 = on; +} + +/// Get the Quiet setting of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRSamsungAc::getQuiet(void) const { + return !_.Quiet1 && _.Quiet5; +} + +/// Set the Quiet setting of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRSamsungAc::setQuiet(const bool on) { + _.Quiet1 = !on; // Cleared when on. + _.Quiet5 = on; + if (on) { + // Quiet mode seems to set fan speed to auto. + setFan(kSamsungAcFanAuto); + setPowerful(false); // Quiet 'on' is mutually exclusive to Powerful. + } +} + +/// Get the Powerful (Turbo) setting of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRSamsungAc::getPowerful(void) const { + return !(_.Powerful8 & kSamsungAcPowerfulMask8) && + (_.Powerful10 == kSamsungAcPowerful10On) && + (_.Fan == kSamsungAcFanTurbo); +} + +/// Set the Powerful (Turbo) setting of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRSamsungAc::setPowerful(const bool on) { + uint8_t off_value = getBreeze() ? kSamsungAcBreezeOn : 0b000; + _.Powerful10 = (on ? kSamsungAcPowerful10On : off_value); + if (on) { + _.Powerful8 &= ~kSamsungAcPowerfulMask8; // Bit needs to be cleared. + // Powerful mode sets fan speed to Turbo. + setFan(kSamsungAcFanTurbo); + setQuiet(false); // Powerful 'on' is mutually exclusive to Quiet. + } else { + _.Powerful8 |= kSamsungAcPowerfulMask8; // Bit needs to be set. + // Turning off Powerful mode sets fan speed to Auto if we were in Turbo mode + if (_.Fan == kSamsungAcFanTurbo) setFan(kSamsungAcFanAuto); + } +} + +/// Are the vanes closed over the fan outlet, to stop direct wind? Aka. WindFree +/// @return true, the setting is on. false, the setting is off. +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1062 +bool IRSamsungAc::getBreeze(void) const { + return (_.Breeze == kSamsungAcBreezeOn) && + (_.Fan == kSamsungAcFanAuto && !getSwing()); +} + +/// Closes the vanes over the fan outlet, to stop direct wind. Aka. WindFree +/// @param[in] on true, the setting is on. false, the setting is off. +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1062 +void IRSamsungAc::setBreeze(const bool on) { + uint8_t off_value = getPowerful() ? kSamsungAcPowerful10On : 0b000; + _.Breeze = (on ? kSamsungAcBreezeOn : off_value); + if (on) { + setFan(kSamsungAcFanAuto); + setSwing(false); + } +} + +/// Get the Display (Light/LED) setting of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRSamsungAc::getDisplay(void) const { + return _.Display; +} + +/// Set the Display (Light/LED) setting of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRSamsungAc::setDisplay(const bool on) { + _.Display = on; +} + +/// Get the Ion (Filter) setting of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRSamsungAc::getIon(void) const { + return _.Ion; +} + +/// Set the Ion (Filter) setting of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRSamsungAc::setIon(const bool on) { + _.Ion = on; +} + +/// 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 IRSamsungAc::convertMode(const stdAc::opmode_t mode) { + switch (mode) { + case stdAc::opmode_t::kCool: return kSamsungAcCool; + case stdAc::opmode_t::kHeat: return kSamsungAcHeat; + case stdAc::opmode_t::kDry: return kSamsungAcDry; + case stdAc::opmode_t::kFan: return kSamsungAcFan; + default: return kSamsungAcAuto; + } +} + +/// 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 IRSamsungAc::convertFan(const stdAc::fanspeed_t speed) { + switch (speed) { + case stdAc::fanspeed_t::kMin: + case stdAc::fanspeed_t::kLow: return kSamsungAcFanLow; + case stdAc::fanspeed_t::kMedium: return kSamsungAcFanMed; + case stdAc::fanspeed_t::kHigh: return kSamsungAcFanHigh; + case stdAc::fanspeed_t::kMax: return kSamsungAcFanTurbo; + default: return kSamsungAcFanAuto; + } +} + +/// 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 IRSamsungAc::toCommonMode(const uint8_t mode) { + switch (mode) { + case kSamsungAcCool: return stdAc::opmode_t::kCool; + case kSamsungAcHeat: return stdAc::opmode_t::kHeat; + case kSamsungAcDry: return stdAc::opmode_t::kDry; + case kSamsungAcFan: return stdAc::opmode_t::kFan; + default: return stdAc::opmode_t::kAuto; + } +} + +/// Convert a native fan speed into its stdAc equivalent. +/// @param[in] spd The native setting to be converted. +/// @return The stdAc equivalent of the native setting. +stdAc::fanspeed_t IRSamsungAc::toCommonFanSpeed(const uint8_t spd) { + switch (spd) { + case kSamsungAcFanTurbo: return stdAc::fanspeed_t::kMax; + case kSamsungAcFanHigh: return stdAc::fanspeed_t::kHigh; + case kSamsungAcFanMed: return stdAc::fanspeed_t::kMedium; + case kSamsungAcFanLow: 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 IRSamsungAc::toCommon(void) const { + stdAc::state_t result; + result.protocol = decode_type_t::SAMSUNG_AC; + result.model = -1; // Not supported. + result.power = getPower(); + result.mode = toCommonMode(_.Mode); + result.celsius = true; + result.degrees = getTemp(); + result.fanspeed = toCommonFanSpeed(_.Fan); + result.swingv = getSwing() ? stdAc::swingv_t::kAuto : + stdAc::swingv_t::kOff; + result.quiet = getQuiet(); + result.turbo = getPowerful(); + result.clean = getClean(); + result.beep = _.Beep; + result.light = _.Display; + result.filter = _.Ion; + // Not supported. + result.swingh = stdAc::swingh_t::kOff; + result.econo = 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 IRSamsungAc::toString(void) const { + String result = ""; + result.reserve(115); // Reserve some heap for the string to reduce fragging. + result += addBoolToString(getPower(), kPowerStr, false); + result += addModeToString(_.Mode, kSamsungAcAuto, kSamsungAcCool, + kSamsungAcHeat, kSamsungAcDry, + kSamsungAcFan); + result += addTempToString(getTemp()); + result += addIntToString(_.Fan, kFanStr); + result += kSpaceLBraceStr; + switch (_.Fan) { + case kSamsungAcFanAuto: + case kSamsungAcFanAuto2: + result += kAutoStr; + break; + case kSamsungAcFanLow: + result += kLowStr; + break; + case kSamsungAcFanMed: + result += kMedStr; + break; + case kSamsungAcFanHigh: + result += kHighStr; + break; + case kSamsungAcFanTurbo: + result += kTurboStr; + break; + default: + result += kUnknownStr; + break; + } + result += ')'; + result += addBoolToString(getSwing(), kSwingStr); + result += addBoolToString(_.Beep, kBeepStr); + result += addBoolToString(getClean(), kCleanStr); + result += addBoolToString(getQuiet(), kQuietStr); + result += addBoolToString(getPowerful(), kPowerfulStr); + result += addBoolToString(getBreeze(), kBreezeStr); + result += addBoolToString(_.Display, kLightStr); + result += addBoolToString(_.Ion, kIonStr); + return result; +} + +#if DECODE_SAMSUNG_AC +/// Decode the supplied Samsung A/C message. +/// Status: Stable / Known 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. +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/505 +bool IRrecv::decodeSamsungAC(decode_results *results, uint16_t offset, + const uint16_t nbits, const bool strict) { + if (results->rawlen < 2 * nbits + kHeader * 3 + kFooter * 2 - 1 + offset) + return false; // Can't possibly be a valid Samsung A/C message. + if (nbits != kSamsungAcBits && nbits != kSamsungAcExtendedBits) return false; + + // Message Header + if (!matchMark(results->rawbuf[offset++], kSamsungAcBitMark)) return false; + if (!matchSpace(results->rawbuf[offset++], kSamsungAcHdrSpace)) return false; + // Section(s) + for (uint16_t pos = 0; pos <= (nbits / 8) - kSamsungAcSectionLength; + pos += kSamsungAcSectionLength) { + uint16_t used; + // Section Header + Section Data (7 bytes) + Section Footer + used = matchGeneric(results->rawbuf + offset, results->state + pos, + results->rawlen - offset, kSamsungAcSectionLength * 8, + kSamsungAcSectionMark, kSamsungAcSectionSpace, + kSamsungAcBitMark, kSamsungAcOneSpace, + kSamsungAcBitMark, kSamsungAcZeroSpace, + kSamsungAcBitMark, kSamsungAcSectionGap, + pos + kSamsungAcSectionLength >= nbits / 8, + _tolerance, 0, false); + if (used == 0) return false; + offset += used; + } + // Compliance + // Is the signature correct? + DPRINTLN("DEBUG: Checking signature."); + if (results->state[0] != 0x02 || results->state[2] != 0x0F) return false; + if (strict) { + // Is the checksum valid? + if (!IRSamsungAc::validChecksum(results->state, nbits / 8)) { + DPRINTLN("DEBUG: Checksum failed!"); + return false; + } + } + // Success + results->decode_type = SAMSUNG_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_SAMSUNG_AC diff --git a/lib/IRremoteESP8266/src/ir_Samsung.h b/lib/IRremoteESP8266/src/ir_Samsung.h index 0e69a545f2..bf9215edcc 100644 --- a/lib/IRremoteESP8266/src/ir_Samsung.h +++ b/lib/IRremoteESP8266/src/ir_Samsung.h @@ -32,10 +32,10 @@ #ifndef UNIT_TEST #include #endif -#include "../src/IRremoteESP8266.h" -#include "../src/IRsend.h" +#include "IRremoteESP8266.h" +#include "IRsend.h" #ifdef UNIT_TEST -#include "../src/IRsend_test.h" +#include "IRsend_test.h" #endif /// Native representation of a Samsung A/C message. diff --git a/lib/IRremoteESP8266/src/ir_Sanyo.cpp b/lib/IRremoteESP8266/src/ir_Sanyo.cpp new file mode 100644 index 0000000000..7dbed5a582 --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_Sanyo.cpp @@ -0,0 +1,978 @@ +// Copyright 2009 Ken Shirriff +// Copyright 2016 marcosamarinho +// Copyright 2017-2021 David Conran + +/// @file +/// @brief Support for Sanyo protocols. +/// Sanyo LC7461 support originally by marcosamarinho +/// Sanyo SA 8650B originally added from +/// https://github.com/shirriff/Arduino-IRremote/ +/// @see https://github.com/z3t0/Arduino-IRremote/blob/master/ir_Sanyo.cpp +/// @see http://pdf.datasheetcatalog.com/datasheet/sanyo/LC7461.pdf +/// @see https://github.com/marcosamarinho/IRremoteESP8266/blob/master/ir_Sanyo.cpp +/// @see http://slydiman.narod.ru/scr/kb/sanyo.htm +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1211 +/// @see https://docs.google.com/spreadsheets/d/1dYfLsnYvpjV-SgO8pdinpfuBIpSzm8Q1R5SabrLeskw/edit?usp=sharing +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1503 + +#include "ir_Sanyo.h" +#include +#include +#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::addSwingVToString; +using irutils::addTempToString; +using irutils::minsToString; +using irutils::sumNibbles; + +// Constants +// Sanyo SA 8650B +const uint16_t kSanyoSa8650bHdrMark = 3500; // seen range 3500 +const uint16_t kSanyoSa8650bHdrSpace = 950; // seen 950 +const uint16_t kSanyoSa8650bOneMark = 2400; // seen 2400 +const uint16_t kSanyoSa8650bZeroMark = 700; // seen 700 +// usually see 713 - not using ticks as get number wrapround +const uint16_t kSanyoSa8650bDoubleSpaceUsecs = 800; +const uint16_t kSanyoSa8650bRptLength = 45000; + +// Sanyo LC7461 +const uint16_t kSanyoLc7461AddressMask = (1 << kSanyoLC7461AddressBits) - 1; +const uint16_t kSanyoLc7461CommandMask = (1 << kSanyoLC7461CommandBits) - 1; +const uint16_t kSanyoLc7461HdrMark = 9000; +const uint16_t kSanyoLc7461HdrSpace = 4500; +const uint16_t kSanyoLc7461BitMark = 560; // 1T +const uint16_t kSanyoLc7461OneSpace = 1690; // 3T +const uint16_t kSanyoLc7461ZeroSpace = 560; // 1T +const uint32_t kSanyoLc7461MinCommandLength = 108000; + +const uint16_t kSanyoLc7461MinGap = + kSanyoLc7461MinCommandLength - + (kSanyoLc7461HdrMark + kSanyoLc7461HdrSpace + + kSanyoLC7461Bits * (kSanyoLc7461BitMark + + (kSanyoLc7461OneSpace + kSanyoLc7461ZeroSpace) / 2) + + kSanyoLc7461BitMark); + +const uint16_t kSanyoAcHdrMark = 8500; ///< uSeconds +const uint16_t kSanyoAcHdrSpace = 4200; ///< uSeconds +const uint16_t kSanyoAcBitMark = 500; ///< uSeconds +const uint16_t kSanyoAcOneSpace = 1600; ///< uSeconds +const uint16_t kSanyoAcZeroSpace = 550; ///< uSeconds +const uint32_t kSanyoAcGap = kDefaultMessageGap; ///< uSeconds (Guess only) +const uint16_t kSanyoAcFreq = 38000; ///< Hz. (Guess only) + +const uint16_t kSanyoAc88HdrMark = 5400; ///< uSeconds +const uint16_t kSanyoAc88HdrSpace = 2000; ///< uSeconds +const uint16_t kSanyoAc88BitMark = 500; ///< uSeconds +const uint16_t kSanyoAc88OneSpace = 1500; ///< uSeconds +const uint16_t kSanyoAc88ZeroSpace = 750; ///< uSeconds +const uint32_t kSanyoAc88Gap = 3675; ///< uSeconds +const uint16_t kSanyoAc88Freq = 38000; ///< Hz. (Guess only) +const uint8_t kSanyoAc88ExtraTolerance = 5; /// (%) Extra tolerance to use. + +#if SEND_SANYO +/// Construct a Sanyo LC7461 message. +/// @param[in] address The 13 bit value of the address(Custom) portion of the +/// protocol. +/// @param[in] command The 8 bit value of the command(Key) portion of the +/// protocol. +/// @return An uint64_t with the encoded raw 42 bit Sanyo LC7461 data value. +/// @note This protocol uses the NEC protocol timings. However, data is +/// formatted as : address(13 bits), !address, command(8 bits), !command. +/// According with LIRC, this protocol is used on Sanyo, Aiwa and Chinon +uint64_t IRsend::encodeSanyoLC7461(uint16_t address, uint8_t command) { + // Mask our input values to ensure the correct bit sizes. + address &= kSanyoLc7461AddressMask; + command &= kSanyoLc7461CommandMask; + + uint64_t data = address; + address ^= kSanyoLc7461AddressMask; // Invert the 13 LSBs. + // Append the now inverted address. + data = (data << kSanyoLC7461AddressBits) | address; + // Append the command. + data = (data << kSanyoLC7461CommandBits) | command; + command ^= kSanyoLc7461CommandMask; // Invert the command. + // Append the now inverted command. + data = (data << kSanyoLC7461CommandBits) | command; + + return data; +} + +/// Send a Sanyo LC7461 message. +/// Status: BETA / Probably works. +/// @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 Based on \@marcosamarinho's work. +/// This protocol uses the NEC protocol timings. However, data is +/// formatted as : address(13 bits), !address, command (8 bits), !command. +/// According with LIRC, this protocol is used on Sanyo, Aiwa and Chinon +/// Information for this protocol is available at the Sanyo LC7461 datasheet. +/// Repeats are performed similar to the NEC method of sending a special +/// repeat message, rather than duplicating the entire message. +/// @see https://github.com/marcosamarinho/IRremoteESP8266/blob/master/ir_Sanyo.cpp +/// @see http://pdf.datasheetcatalog.com/datasheet/sanyo/LC7461.pdf +void IRsend::sendSanyoLC7461(const uint64_t data, const uint16_t nbits, + const uint16_t repeat) { + // This protocol appears to be another 42-bit variant of the NEC protocol. + sendNEC(data, nbits, repeat); +} +#endif // SEND_SANYO + +#if DECODE_SANYO +/// Decode the supplied SANYO LC7461 message. +/// Status: BETA / Probably works. +/// @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 Based on \@marcosamarinho's work. +/// This protocol uses the NEC protocol. However, data is +/// formatted as : address(13 bits), !address, command (8 bits), !command. +/// According with LIRC, this protocol is used on Sanyo, Aiwa and Chinon +/// Information for this protocol is available at the Sanyo LC7461 datasheet. +/// @see http://slydiman.narod.ru/scr/kb/sanyo.htm +/// @see https://github.com/marcosamarinho/IRremoteESP8266/blob/master/ir_Sanyo.cpp +/// @see http://pdf.datasheetcatalog.com/datasheet/sanyo/LC7461.pdf +bool IRrecv::decodeSanyoLC7461(decode_results *results, uint16_t offset, + const uint16_t nbits, const bool strict) { + if (strict && nbits != kSanyoLC7461Bits) + return false; // Not strictly in spec. + // This protocol is basically a 42-bit variant of the NEC protocol. + if (!decodeNEC(results, offset, nbits, false)) + return false; // Didn't match a NEC format (without strict) + + // Bits 30 to 42+. + uint16_t address = + results->value >> (kSanyoLC7461Bits - kSanyoLC7461AddressBits); + // Bits 9 to 16. + uint8_t command = + (results->value >> kSanyoLC7461CommandBits) & kSanyoLc7461CommandMask; + // Compliance + if (strict) { + if (results->bits != nbits) return false; + // Bits 17 to 29. + uint16_t inverted_address = + (results->value >> (kSanyoLC7461CommandBits * 2)) & + kSanyoLc7461AddressMask; + // Bits 1-8. + uint8_t inverted_command = results->value & kSanyoLc7461CommandMask; + if ((address ^ kSanyoLc7461AddressMask) != inverted_address) + return false; // Address integrity check failed. + if ((command ^ kSanyoLc7461CommandMask) != inverted_command) + return false; // Command integrity check failed. + } + + // Success + results->decode_type = SANYO_LC7461; + results->address = address; + results->command = command; + return true; +} + +/* NOTE: Disabled due to poor quality. +/// Decode the supplied Sanyo SA 8650B message. +/// Status: Depricated. +/// @depricated Disabled due to poor quality. +/// @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. +/// @warning This decoder looks like rubbish. Only keeping it for compatibility +/// with the Arduino IRremote library. Seriously, don't trust it. +/// If someone has a device that this is supposed to be for, please log an +/// Issue on github with a rawData dump please. We should probably remove it. +/// We think this is a Sanyo decoder - serial = SA 8650B +/// @see https://github.com/z3t0/Arduino-IRremote/blob/master/ir_Sanyo.cpp +bool IRrecv::decodeSanyo(decode_results *results, uint16_t nbits, bool strict) { + if (results->rawlen < 2 * nbits + kHeader - 1) + return false; // Shorter than shortest possible. + if (strict && nbits != kSanyoSA8650BBits) + return false; // Doesn't match the spec. + + uint16_t offset = 0; + + // TODO(crankyoldgit): This repeat code looks like garbage, it should never + // match or if it does, it won't be reliable. We should probably just + // remove it. + if (results->rawbuf[offset++] < kSanyoSa8650bDoubleSpaceUsecs) { + results->bits = 0; + results->value = kRepeat; + results->decode_type = SANYO; + results->address = 0; + results->command = 0; + results->repeat = true; + return true; + } + + // Header + if (!matchMark(results->rawbuf[offset++], kSanyoSa8650bHdrMark)) + return false; + // NOTE: These next two lines look very wrong. Treat as suspect. + if (!matchMark(results->rawbuf[offset++], kSanyoSa8650bHdrMark)) + return false; + // Data + uint64_t data = 0; + while (offset + 1 < results->rawlen) { + if (!matchSpace(results->rawbuf[offset], kSanyoSa8650bHdrSpace)) + break; + offset++; + if (matchMark(results->rawbuf[offset], kSanyoSa8650bOneMark)) + data = (data << 1) | 1; // 1 + else if (matchMark(results->rawbuf[offset], kSanyoSa8650bZeroMark)) + data <<= 1; // 0 + else + return false; + offset++; + } + + if (strict && kSanyoSA8650BBits > (offset - 1U) / 2U) + return false; + + // Success + results->bits = (offset - 1) / 2; + results->decode_type = SANYO; + results->value = data; + results->address = 0; + results->command = 0; + return true; +} +*/ +#endif // DECODE_SANYO + + +#if SEND_SANYO_AC +/// Send a SanyoAc 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. +/// @param[in] repeat Nr. of times the message is to be repeated. +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1211 +void IRsend::sendSanyoAc(const uint8_t data[], const uint16_t nbytes, + const uint16_t repeat) { + // Header + Data + Footer + sendGeneric(kSanyoAcHdrMark, kSanyoAcHdrSpace, + kSanyoAcBitMark, kSanyoAcOneSpace, + kSanyoAcBitMark, kSanyoAcZeroSpace, + kSanyoAcBitMark, kSanyoAcGap, + data, nbytes, kSanyoAcFreq, false, repeat, kDutyDefault); +} +#endif // SEND_SANYO_AC + +#if DECODE_SANYO_AC +/// Decode the supplied SanyoAc 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. +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1211 +bool IRrecv::decodeSanyoAc(decode_results *results, uint16_t offset, + const uint16_t nbits, const bool strict) { + if (strict && nbits != kSanyoAcBits) + return false; + + // Header + Data + Footer + if (!matchGeneric(results->rawbuf + offset, results->state, + results->rawlen - offset, nbits, + kSanyoAcHdrMark, kSanyoAcHdrSpace, + kSanyoAcBitMark, kSanyoAcOneSpace, + kSanyoAcBitMark, kSanyoAcZeroSpace, + kSanyoAcBitMark, kSanyoAcGap, + true, kUseDefTol, kMarkExcess, false)) return false; + // Compliance + if (strict) + if (!IRSanyoAc::validChecksum(results->state, nbits / 8)) return false; + + // Success + results->decode_type = decode_type_t::SANYO_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_SANYO_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? +IRSanyoAc::IRSanyoAc(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 state/sequence. +void IRSanyoAc::stateReset(void) { + static const uint8_t kReset[kSanyoAcStateLength] = { + 0x6A, 0x6D, 0x51, 0x00, 0x10, 0x45, 0x00, 0x00, 0x33}; + std::memcpy(_.raw, kReset, kSanyoAcStateLength); +} + +/// Set up hardware to be able to send a message. +void IRSanyoAc::begin(void) { _irsend.begin(); } + +#if SEND_SANYO_AC +/// Send the current internal state as IR messages. +/// @param[in] repeat Nr. of times the message will be repeated. +void IRSanyoAc::send(const uint16_t repeat) { + _irsend.sendSanyoAc(getRaw(), kSanyoAcStateLength, repeat); +} +#endif // SEND_SANYO_AC + +/// Get a PTR to the internal state/code for this protocol with all integrity +/// checks passing. +/// @return PTR to a code for this protocol based on the current internal state. +uint8_t* IRSanyoAc::getRaw(void) { + checksum(); + return _.raw; +} + +/// Set the internal state from a valid code for this protocol. +/// @param[in] newState A valid code for this protocol. +void IRSanyoAc::setRaw(const uint8_t newState[]) { + std::memcpy(_.raw, newState, kSanyoAcStateLength); +} + +/// 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 IRSanyoAc::calcChecksum(const uint8_t state[], + const uint16_t length) { + return length ? sumNibbles(state, length - 1) : 0; +} + +/// 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 IRSanyoAc::validChecksum(const uint8_t state[], const uint16_t length) { + return length && state[length - 1] == IRSanyoAc::calcChecksum(state, length); +} + +/// Calculate & set the checksum for the current internal state of the remote. +void IRSanyoAc::checksum(void) { + // Stored the checksum value in the last byte. + _.Sum = calcChecksum(_.raw); +} + + +/// Set the requested power state of the A/C to on. +void IRSanyoAc::on(void) { setPower(true); } + +/// Set the requested power state of the A/C to off. +void IRSanyoAc::off(void) { setPower(false); } + +/// Change the power setting. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRSanyoAc::setPower(const bool on) { + _.Power = (on ? kSanyoAcPowerOn : kSanyoAcPowerOff); +} + +/// Get the value of the current power setting. +/// @return true, the setting is on. false, the setting is off. +bool IRSanyoAc::getPower(void) const { + return _.Power == kSanyoAcPowerOn; +} + +/// Get the operating mode setting of the A/C. +/// @return The current operating mode setting. +uint8_t IRSanyoAc::getMode(void) const { + return _.Mode; +} + +/// Set the operating mode of the A/C. +/// @param[in] mode The desired operating mode. +/// @note If we get an unexpected mode, default to AUTO. +void IRSanyoAc::setMode(const uint8_t mode) { + switch (mode) { + case kSanyoAcAuto: + case kSanyoAcCool: + case kSanyoAcDry: + case kSanyoAcHeat: + _.Mode = mode; + break; + default: _.Mode = kSanyoAcAuto; + } +} + +/// 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 IRSanyoAc::convertMode(const stdAc::opmode_t mode) { + switch (mode) { + case stdAc::opmode_t::kCool: return kSanyoAcCool; + case stdAc::opmode_t::kHeat: return kSanyoAcHeat; + case stdAc::opmode_t::kDry: return kSanyoAcDry; + default: return kSanyoAcAuto; + } +} + +/// 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 IRSanyoAc::toCommonMode(const uint8_t mode) { + switch (mode) { + case kSanyoAcCool: return stdAc::opmode_t::kCool; + case kSanyoAcHeat: return stdAc::opmode_t::kHeat; + case kSanyoAcDry: return stdAc::opmode_t::kDry; + default: return stdAc::opmode_t::kAuto; + } +} + +/// Set the desired temperature. +/// @param[in] degrees The temperature in degrees celsius. +void IRSanyoAc::setTemp(const uint8_t degrees) { + uint8_t temp = std::max((uint8_t)kSanyoAcTempMin, degrees); + temp = std::min((uint8_t)kSanyoAcTempMax, temp); + _.Temp = temp - kSanyoAcTempDelta; +} + +/// Get the current desired temperature setting. +/// @return The current setting for temp. in degrees celsius. +uint8_t IRSanyoAc::getTemp(void) const { + return _.Temp + kSanyoAcTempDelta; +} + +/// Set the sensor temperature. +/// @param[in] degrees The temperature in degrees celsius. +void IRSanyoAc::setSensorTemp(const uint8_t degrees) { + uint8_t temp = std::max((uint8_t)kSanyoAcTempMin, degrees); + temp = std::min((uint8_t)kSanyoAcTempMax, temp); + _.SensorTemp = temp - kSanyoAcTempDelta; +} + +/// Get the current sensor temperature setting. +/// @return The current setting for temp. in degrees celsius. +uint8_t IRSanyoAc::getSensorTemp(void) const { + return _.SensorTemp + kSanyoAcTempDelta; +} + +/// Set the speed of the fan. +/// @param[in] speed The desired setting. +void IRSanyoAc::setFan(const uint8_t speed) { + _.Fan = speed; +} + +/// Get the current fan speed setting. +/// @return The current fan speed/mode. +uint8_t IRSanyoAc::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 IRSanyoAc::convertFan(const stdAc::fanspeed_t speed) { + switch (speed) { + case stdAc::fanspeed_t::kMin: + case stdAc::fanspeed_t::kLow: return kSanyoAcFanLow; + case stdAc::fanspeed_t::kMedium: return kSanyoAcFanMedium; + case stdAc::fanspeed_t::kHigh: + case stdAc::fanspeed_t::kMax: return kSanyoAcFanHigh; + default: return kSanyoAcFanAuto; + } +} + +/// Convert a native fan speed into its stdAc equivalent. +/// @param[in] spd The native setting to be converted. +/// @return The stdAc equivalent of the native setting. +stdAc::fanspeed_t IRSanyoAc::toCommonFanSpeed(const uint8_t spd) { + switch (spd) { + case kSanyoAcFanHigh: return stdAc::fanspeed_t::kHigh; + case kSanyoAcFanMedium: return stdAc::fanspeed_t::kMedium; + case kSanyoAcFanLow: return stdAc::fanspeed_t::kLow; + default: return stdAc::fanspeed_t::kAuto; + } +} + +/// Get the vertical swing setting of the A/C. +/// @return The current swing mode setting. +uint8_t IRSanyoAc::getSwingV(void) const { + return _.SwingV; +} + +/// Set the vertical swing setting of the A/C. +/// @param[in] setting The value of the desired setting. +void IRSanyoAc::setSwingV(const uint8_t setting) { + if (setting == kSanyoAcSwingVAuto || + (setting >= kSanyoAcSwingVLowest && setting <= kSanyoAcSwingVHighest)) + _.SwingV = setting; + else + _.SwingV = kSanyoAcSwingVAuto; +} + +/// 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 IRSanyoAc::convertSwingV(const stdAc::swingv_t position) { + switch (position) { + case stdAc::swingv_t::kHighest: return kSanyoAcSwingVHighest; + case stdAc::swingv_t::kHigh: return kSanyoAcSwingVHigh; + case stdAc::swingv_t::kMiddle: return kSanyoAcSwingVUpperMiddle; + case stdAc::swingv_t::kLow: return kSanyoAcSwingVLow; + case stdAc::swingv_t::kLowest: return kSanyoAcSwingVLowest; + default: return kSanyoAcSwingVAuto; + } +} + +/// Convert a native vertical swing postion to it's common equivalent. +/// @param[in] setting A native position to convert. +/// @return The common vertical swing position. +stdAc::swingv_t IRSanyoAc::toCommonSwingV(const uint8_t setting) { + switch (setting) { + case kSanyoAcSwingVHighest: return stdAc::swingv_t::kHighest; + case kSanyoAcSwingVHigh: return stdAc::swingv_t::kHigh; + case kSanyoAcSwingVUpperMiddle: + case kSanyoAcSwingVLowerMiddle: return stdAc::swingv_t::kMiddle; + case kSanyoAcSwingVLow: return stdAc::swingv_t::kLow; + case kSanyoAcSwingVLowest: return stdAc::swingv_t::kLowest; + default: return stdAc::swingv_t::kAuto; + } +} + +/// Set the Sleep (Night Setback) setting of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRSanyoAc::setSleep(const bool on) { + _.Sleep = on; +} + +/// Get the Sleep (Night Setback) setting of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRSanyoAc::getSleep(void) const { + return _.Sleep; +} + +/// Set the Sensor Location setting of the A/C. +/// i.e. Where the ambient temperature is measured. +/// @param[in] location true is Unit/Wall, false is Remote/Room. +void IRSanyoAc::setSensor(const bool location) { + _.Sensor = location; +} + +/// Get the Sensor Location setting of the A/C. +/// i.e. Where the ambient temperature is measured. +/// @return true is Unit/Wall, false is Remote/Room. +bool IRSanyoAc::getSensor(void) const { + return _.Sensor; +} + +/// Set the Beep setting of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRSanyoAc::setBeep(const bool on) { + _.Beep = on; +} + +/// Get the Beep setting of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRSanyoAc::getBeep(void) const { + return _.Beep; +} + +/// Get the nr of minutes the Off Timer is set to. +/// @return The timer time expressed as the number of minutes. +/// A value of 0 means the Off Timer is off/disabled. +/// @note The internal precission has a resolution of 1 hour. +uint16_t IRSanyoAc::getOffTimer(void) const { + if (_.OffTimer) + return _.OffHour * 60; + else + return 0; +} + +/// Set the nr of minutes for the Off Timer. +/// @param[in] mins The timer time expressed as nr. of minutes. +/// A value of 0 means the Off Timer is off/disabled. +/// @note The internal precission has a resolution of 1 hour. +void IRSanyoAc::setOffTimer(const uint16_t mins) { + const uint8_t hours = std::min((uint8_t)(mins / 60), kSanyoAcHourMax); + _.OffTimer = (hours > 0); + _.OffHour = hours; +} + +/// Convert the current internal state into its stdAc::state_t equivalent. +/// @return The stdAc equivalent of the native settings. +stdAc::state_t IRSanyoAc::toCommon(void) const { + stdAc::state_t result; + result.protocol = decode_type_t::SANYO_AC; + result.model = -1; // Not supported. + result.power = getPower(); + result.mode = toCommonMode(_.Mode); + result.celsius = true; + result.degrees = getTemp(); + result.fanspeed = toCommonFanSpeed(_.Fan); + result.sleep = _.Sleep ? 0 : -1; + result.swingv = toCommonSwingV(_.SwingV); + result.beep = _.Beep; + // Not supported. + result.swingh = stdAc::swingh_t::kOff; + result.turbo = false; + result.econo = false; + result.light = false; + result.filter = false; + result.quiet = false; + result.clean = false; + result.clock = -1; + return result; +} + +/// Convert the current internal state into a human readable string. +/// @return A human readable string. +String IRSanyoAc::toString(void) const { + String result = ""; + result.reserve(140); + result += addBoolToString(getPower(), kPowerStr, false); + result += addModeToString(_.Mode, kSanyoAcAuto, kSanyoAcCool, + kSanyoAcHeat, kSanyoAcDry, kSanyoAcAuto); + result += addTempToString(getTemp()); + result += addFanToString(_.Fan, kSanyoAcFanHigh, kSanyoAcFanLow, + kSanyoAcFanAuto, kSanyoAcFanAuto, + kSanyoAcFanMedium); + result += addSwingVToString(_.SwingV, kSanyoAcSwingVAuto, + kSanyoAcSwingVHighest, kSanyoAcSwingVHigh, + kSanyoAcSwingVUpperMiddle, + kSanyoAcSwingVAuto, // Middle is unused + kSanyoAcSwingVLowerMiddle, + kSanyoAcSwingVLow, kSanyoAcSwingVLowest, + // Below are unused. + kSanyoAcSwingVAuto, + kSanyoAcSwingVAuto, + kSanyoAcSwingVAuto, + kSanyoAcSwingVAuto); + result += addBoolToString(_.Sleep, kSleepStr); + result += addBoolToString(_.Beep, kBeepStr); + result += addLabeledString(_.Sensor ? kRoomStr : kWallStr, kSensorStr); + result += kCommaSpaceStr; + result += kSensorStr; + result += ' '; + result += addTempToString(getSensorTemp(), true, false); + const uint16_t offtime = getOffTimer(); + result += addLabeledString(offtime ? minsToString(offtime) : kOffStr, + kOffTimerStr); + return result; +} + +#if SEND_SANYO_AC88 +/// Send a SanyoAc88 formatted message. +/// Status: ALPHA / Completely untested. +/// @param[in] data An array of bytes containing the IR command. +/// @warning data's bit order may change. It is not yet confirmed. +/// @param[in] nbytes Nr. of bytes of data in the array. +/// @param[in] repeat Nr. of times the message is to be repeated. +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1503 +void IRsend::sendSanyoAc88(const uint8_t data[], const uint16_t nbytes, + const uint16_t repeat) { + // (Header + Data + Footer) per repeat + sendGeneric(kSanyoAc88HdrMark, kSanyoAc88HdrSpace, + kSanyoAc88BitMark, kSanyoAc88OneSpace, + kSanyoAc88BitMark, kSanyoAc88ZeroSpace, + kSanyoAc88BitMark, kSanyoAc88Gap, + data, nbytes, kSanyoAc88Freq, false, repeat, kDutyDefault); + space(kDefaultMessageGap); // Make a guess at a post message gap. +} +#endif // SEND_SANYO_AC88 + +#if DECODE_SANYO_AC88 +/// Decode the supplied SanyoAc message. +/// Status: ALPHA / Untested. +/// @param[in,out] results Ptr to the data to decode & where to store the decode +/// @warning data's bit order may change. It is not yet confirmed. +/// @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/1503 +bool IRrecv::decodeSanyoAc88(decode_results *results, uint16_t offset, + const uint16_t nbits, const bool strict) { + if (strict && nbits != kSanyoAc88Bits) + return false; + + uint16_t used = 0; + // Compliance + const uint16_t expected_repeats = strict ? kSanyoAc88MinRepeat : 0; + + // Handle the expected nr of repeats. + for (uint16_t r = 0; r <= expected_repeats; r++) { + // Header + Data + Footer + used = matchGeneric(results->rawbuf + offset, results->state, + results->rawlen - offset, nbits, + kSanyoAc88HdrMark, kSanyoAc88HdrSpace, + kSanyoAc88BitMark, kSanyoAc88OneSpace, + kSanyoAc88BitMark, kSanyoAc88ZeroSpace, + kSanyoAc88BitMark, + // Expect an inter-message gap, or just the end of msg? + (r < expected_repeats) ? kSanyoAc88Gap + : kDefaultMessageGap, + r == expected_repeats, + _tolerance + kSanyoAc88ExtraTolerance, + kMarkExcess, false); + if (!used) return false; // No match! + offset += used; + } + + // Success + results->decode_type = decode_type_t::SANYO_AC88; + 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_SANYO_AC88 + +/// 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? +IRSanyoAc88::IRSanyoAc88(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. +/// @see https://docs.google.com/spreadsheets/d/1dYfLsnYvpjV-SgO8pdinpfuBIpSzm8Q1R5SabrLeskw/edit?ts=5f0190a5#gid=1050142776&range=A2:B2 +void IRSanyoAc88::stateReset(void) { + static const uint8_t kReset[kSanyoAc88StateLength] = { + 0xAA, 0x55, 0xA0, 0x16, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x10}; + std::memcpy(_.raw, kReset, kSanyoAc88StateLength); +} + +/// Set up hardware to be able to send a message. +void IRSanyoAc88::begin(void) { _irsend.begin(); } + +#if SEND_SANYO_AC +/// Send the current internal state as IR messages. +/// @param[in] repeat Nr. of times the message will be repeated. +void IRSanyoAc88::send(const uint16_t repeat) { + _irsend.sendSanyoAc88(getRaw(), kSanyoAc88StateLength, repeat); +} +#endif // SEND_SANYO_AC + +/// Get a PTR to the internal state/code for this protocol with all integrity +/// checks passing. +/// @return PTR to a code for this protocol based on the current internal state. +uint8_t* IRSanyoAc88::getRaw(void) { + return _.raw; +} + +/// Set the internal state from a valid code for this protocol. +/// @param[in] newState A valid code for this protocol. +void IRSanyoAc88::setRaw(const uint8_t newState[]) { + std::memcpy(_.raw, newState, kSanyoAc88StateLength); +} + +/// Set the requested power state of the A/C to on. +void IRSanyoAc88::on(void) { setPower(true); } + +/// Set the requested power state of the A/C to off. +void IRSanyoAc88::off(void) { setPower(false); } + +/// Change the power setting. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRSanyoAc88::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 IRSanyoAc88::getPower(void) const { return _.Power; } + +/// Get the operating mode setting of the A/C. +/// @return The current operating mode setting. +uint8_t IRSanyoAc88::getMode(void) const { return _.Mode; } + +/// Set the operating mode of the A/C. +/// @param[in] mode The desired operating mode. +/// @note If we get an unexpected mode, default to AUTO. +void IRSanyoAc88::setMode(const uint8_t mode) { + switch (mode) { + case kSanyoAc88Auto: + case kSanyoAc88FeelCool: + case kSanyoAc88Cool: + case kSanyoAc88FeelHeat: + case kSanyoAc88Heat: + case kSanyoAc88Fan: + _.Mode = mode; + break; + default: _.Mode = kSanyoAc88Auto; + } +} + +/// 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 IRSanyoAc88::convertMode(const stdAc::opmode_t mode) { + switch (mode) { + case stdAc::opmode_t::kCool: return kSanyoAc88Cool; + case stdAc::opmode_t::kHeat: return kSanyoAc88Heat; + case stdAc::opmode_t::kFan: return kSanyoAc88Fan; + default: return kSanyoAc88Auto; + } +} + +/// 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 IRSanyoAc88::toCommonMode(const uint8_t mode) { + switch (mode) { + case kSanyoAc88FeelCool: + case kSanyoAc88Cool: + return stdAc::opmode_t::kCool; + case kSanyoAc88FeelHeat: + case kSanyoAc88Heat: + return stdAc::opmode_t::kHeat; + case kSanyoAc88Fan: + return stdAc::opmode_t::kFan; + default: + return stdAc::opmode_t::kAuto; + } +} + +/// Set the desired temperature. +/// @param[in] degrees The temperature in degrees celsius. +void IRSanyoAc88::setTemp(const uint8_t degrees) { + uint8_t temp = std::max((uint8_t)kSanyoAc88TempMin, degrees); + _.Temp = std::min((uint8_t)kSanyoAc88TempMax, temp); +} + +/// Get the current desired temperature setting. +/// @return The current setting for temp. in degrees celsius. +uint8_t IRSanyoAc88::getTemp(void) const { return _.Temp; } + +/// Set the speed of the fan. +/// @param[in] speed The desired setting. +void IRSanyoAc88::setFan(const uint8_t speed) { _.Fan = speed; } + +/// Get the current fan speed setting. +/// @return The current fan speed/mode. +uint8_t IRSanyoAc88::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 IRSanyoAc88::convertFan(const stdAc::fanspeed_t speed) { + switch (speed) { + case stdAc::fanspeed_t::kMin: + case stdAc::fanspeed_t::kLow: return kSanyoAc88FanLow; + case stdAc::fanspeed_t::kMedium: return kSanyoAc88FanMedium; + case stdAc::fanspeed_t::kHigh: + case stdAc::fanspeed_t::kMax: return kSanyoAc88FanHigh; + default: return kSanyoAc88FanAuto; + } +} + +/// Get the current clock time. +/// @return The time as the nr. of minutes past midnight. +uint16_t IRSanyoAc88::getClock(void) const { + return _.ClockHrs * 60 + _.ClockMins; +} + +/// Set the current clock time. +/// @param[in] mins_since_midnight The time as nr. of minutes past midnight. +void IRSanyoAc88::setClock(const uint16_t mins_since_midnight) { + uint16_t mins = std::min(mins_since_midnight, (uint16_t)(23 * 60 + 59)); + _.ClockMins = mins % 60; + _.ClockHrs = mins / 60; + _.ClockSecs = 0; +} + +/// Convert a native fan speed into its stdAc equivalent. +/// @param[in] spd The native setting to be converted. +/// @return The stdAc equivalent of the native setting. +stdAc::fanspeed_t IRSanyoAc88::toCommonFanSpeed(const uint8_t spd) { + switch (spd) { + case kSanyoAc88FanHigh: return stdAc::fanspeed_t::kHigh; + case kSanyoAc88FanMedium: return stdAc::fanspeed_t::kMedium; + case kSanyoAc88FanLow: return stdAc::fanspeed_t::kLow; + default: return stdAc::fanspeed_t::kAuto; + } +} + +/// Change the SwingV setting. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRSanyoAc88::setSwingV(const bool on) { _.SwingV = on; } + +/// Get the value of the current SwingV setting. +/// @return true, the setting is on. false, the setting is off. +bool IRSanyoAc88::getSwingV(void) const { return _.SwingV; } + +/// Change the Turbo setting. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRSanyoAc88::setTurbo(const bool on) { _.Turbo = on; } + +/// Get the value of the current Turbo setting. +/// @return true, the setting is on. false, the setting is off. +bool IRSanyoAc88::getTurbo(void) const { return _.Turbo; } + +/// Change the Filter setting. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRSanyoAc88::setFilter(const bool on) { _.Filter = on; } + +/// Get the value of the current Filter setting. +/// @return true, the setting is on. false, the setting is off. +bool IRSanyoAc88::getFilter(void) const { return _.Filter; } + +/// Change the Sleep setting. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRSanyoAc88::setSleep(const bool on) { _.Sleep = on; } + +/// Get the value of the current Sleep setting. +/// @return true, the setting is on. false, the setting is off. +bool IRSanyoAc88::getSleep(void) const { return _.Sleep; } + +/// Convert the current internal state into its stdAc::state_t equivalent. +/// @return The stdAc equivalent of the native settings. +stdAc::state_t IRSanyoAc88::toCommon(void) const { + stdAc::state_t result; + result.protocol = decode_type_t::SANYO_AC88; + result.model = -1; // Not supported. + result.power = getPower(); + 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.filter = _.Filter; + result.turbo = _.Turbo; + result.sleep = _.Sleep ? 0 : -1; + result.clock = getClock(); + // Not supported. + result.swingh = stdAc::swingh_t::kOff; + result.econo = false; + result.light = false; + result.quiet = false; + result.beep = false; + result.clean = false; + return result; +} + +/// Convert the current internal state into a human readable string. +/// @return A human readable string. +String IRSanyoAc88::toString(void) const { + String result = ""; + result.reserve(115); + result += addBoolToString(getPower(), kPowerStr, false); + result += addModeToString(_.Mode, kSanyoAc88Auto, kSanyoAc88Cool, + kSanyoAc88Heat, kSanyoAc88Auto, kSanyoAc88Fan); + result += addTempToString(getTemp()); + result += addFanToString(_.Fan, kSanyoAc88FanHigh, kSanyoAc88FanLow, + kSanyoAc88FanAuto, kSanyoAc88FanAuto, + kSanyoAc88FanMedium); + result += addBoolToString(_.SwingV, kSwingVStr); + result += addBoolToString(_.Turbo, kTurboStr); + result += addBoolToString(_.Sleep, kSleepStr); + result += addLabeledString(minsToString(getClock()), kClockStr); + return result; +} diff --git a/lib/IRremoteESP8266/src/ir_Sanyo.h b/lib/IRremoteESP8266/src/ir_Sanyo.h index 206a7314ff..66376328f6 100644 --- a/lib/IRremoteESP8266/src/ir_Sanyo.h +++ b/lib/IRremoteESP8266/src/ir_Sanyo.h @@ -30,10 +30,10 @@ #ifdef ARDUINO #include #endif -#include "../src/IRremoteESP8266.h" -#include "../src/IRsend.h" +#include "IRremoteESP8266.h" +#include "IRsend.h" #ifdef UNIT_TEST -#include "../src/IRsend_test.h" +#include "IRsend_test.h" #endif /// Native representation of a Sanyo A/C message. diff --git a/lib/IRremoteESP8266/src/ir_Sharp.cpp b/lib/IRremoteESP8266/src/ir_Sharp.cpp new file mode 100644 index 0000000000..38f0ac32ce --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_Sharp.cpp @@ -0,0 +1,976 @@ +// Copyright 2009 Ken Shirriff +// Copyright 2017, 2019 David Conran + +/// @file +/// @brief Support for Sharp protocols. +/// @see http://www.sbprojects.net/knowledge/ir/sharp.htm +/// @see http://lirc.sourceforge.net/remotes/sharp/GA538WJSA +/// @see http://www.mwftr.com/ucF08/LEC14%20PIC%20IR.pdf +/// @see http://www.hifi-remote.com/johnsfine/DecodeIR.html#Sharp +/// @see GlobalCache's IR Control Tower data. +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/638 +/// @see https://github.com/ToniA/arduino-heatpumpir/blob/master/SharpHeatpumpIR.cpp + +#include "ir_Sharp.h" +#include +#include +#ifndef ARDUINO +#include +#endif +#include "IRrecv.h" +#include "IRsend.h" +#include "IRtext.h" +#include "IRutils.h" + +// Constants +// period time = 1/38000Hz = 26.316 microseconds. +const uint16_t kSharpTick = 26; +const uint16_t kSharpBitMarkTicks = 10; +const uint16_t kSharpBitMark = kSharpBitMarkTicks * kSharpTick; +const uint16_t kSharpOneSpaceTicks = 70; +const uint16_t kSharpOneSpace = kSharpOneSpaceTicks * kSharpTick; +const uint16_t kSharpZeroSpaceTicks = 30; +const uint16_t kSharpZeroSpace = kSharpZeroSpaceTicks * kSharpTick; +const uint16_t kSharpGapTicks = 1677; +const uint16_t kSharpGap = kSharpGapTicks * kSharpTick; +// Address(5) + Command(8) + Expansion(1) + Check(1) +const uint64_t kSharpToggleMask = + ((uint64_t)1 << (kSharpBits - kSharpAddressBits)) - 1; +const uint64_t kSharpAddressMask = ((uint64_t)1 << kSharpAddressBits) - 1; +const uint64_t kSharpCommandMask = ((uint64_t)1 << kSharpCommandBits) - 1; + +using irutils::addBoolToString; +using irutils::addFanToString; +using irutils::addIntToString; +using irutils::addLabeledString; +using irutils::addModeToString; +using irutils::addModelToString; +using irutils::addSwingVToString; +using irutils::addTempToString; +using irutils::addToggleToString; +using irutils::minsToString; + +// Also used by Denon protocol +#if (SEND_SHARP || SEND_DENON) +/// Send a (raw) Sharp message +/// @note Status: STABLE / Working fine. +/// @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 his procedure handles the inversion of bits required per protocol. +/// The protocol spec says to send the LSB first, but legacy code & usage +/// has us sending the MSB first. Grrrr. Normal invocation of encodeSharp() +/// handles this for you, assuming you are using the correct/standard values. +/// e.g. sendSharpRaw(encodeSharp(address, command)); +void IRsend::sendSharpRaw(const uint64_t data, const uint16_t nbits, + const uint16_t repeat) { + uint64_t tempdata = data; + for (uint16_t i = 0; i <= repeat; i++) { + // Protocol demands that the data be sent twice; once normally, + // then with all but the address bits inverted. + // Note: Previously this used to be performed 3 times (normal, inverted, + // normal), however all data points to that being incorrect. + for (uint8_t n = 0; n < 2; n++) { + sendGeneric(0, 0, // No Header + kSharpBitMark, kSharpOneSpace, kSharpBitMark, kSharpZeroSpace, + kSharpBitMark, kSharpGap, tempdata, nbits, 38, true, + 0, // Repeats are handled already. + 33); + // Invert the data per protocol. This is always called twice, so it's + // returned to original upon exiting the inner loop. + tempdata ^= kSharpToggleMask; + } + } +} + +/// Encode a (raw) Sharp message from it's components. +/// Status: STABLE / Works okay. +/// @param[in] address The value of the address to be sent. +/// @param[in] command The value of the address to be sent. (8 bits) +/// @param[in] expansion The value of the expansion bit to use. +/// (0 or 1, typically 1) +/// @param[in] check The value of the check bit to use. (0 or 1, typically 0) +/// @param[in] MSBfirst Flag indicating MSB first or LSB first order. +/// @return A uint32_t containing the raw Sharp message for `sendSharpRaw()`. +/// @note Assumes the standard Sharp bit sizes. +/// Historically sendSharp() sends address & command in +/// MSB first order. This is actually incorrect. It should be sent in LSB +/// order. The behaviour of sendSharp() hasn't been changed to maintain +/// backward compatibility. +uint32_t IRsend::encodeSharp(const uint16_t address, const uint16_t command, + const uint16_t expansion, const uint16_t check, + const bool MSBfirst) { + // Mask any unexpected bits. + uint16_t tempaddress = GETBITS16(address, 0, kSharpAddressBits); + uint16_t tempcommand = GETBITS16(command, 0, kSharpCommandBits); + uint16_t tempexpansion = GETBITS16(expansion, 0, 1); + uint16_t tempcheck = GETBITS16(check, 0, 1); + + if (!MSBfirst) { // Correct bit order if needed. + tempaddress = reverseBits(tempaddress, kSharpAddressBits); + tempcommand = reverseBits(tempcommand, kSharpCommandBits); + } + // Concatenate all the bits. + return (tempaddress << (kSharpCommandBits + 2)) | (tempcommand << 2) | + (tempexpansion << 1) | tempcheck; +} + +/// Send a Sharp message +/// Status: DEPRECATED / Previously working fine. +/// @deprecated Only use this if you are using legacy from the original +/// Arduino-IRremote library. 99% of the time, you will want to use +/// `sendSharpRaw()` instead +/// @param[in] address Address value to be sent. +/// @param[in] command Command value 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 procedure has a non-standard invocation style compared to similar +/// sendProtocol() routines. This is due to legacy, compatibility, & historic +/// reasons. Normally the calling syntax version is like sendSharpRaw(). +/// This procedure transmits the address & command in MSB first order, which is +/// incorrect. This behaviour is left as-is to maintain backward +/// compatibility with legacy code. +/// In short, you should use sendSharpRaw(), encodeSharp(), and the correct +/// values of address & command instead of using this, & the wrong values. +void IRsend::sendSharp(const uint16_t address, uint16_t const command, + const uint16_t nbits, const uint16_t repeat) { + sendSharpRaw(encodeSharp(address, command, 1, 0, true), nbits, repeat); +} +#endif // (SEND_SHARP || SEND_DENON) + +// Used by decodeDenon too. +#if (DECODE_SHARP || DECODE_DENON) +/// Decode the supplied Sharp message. +/// Status: STABLE / Working fine. +/// @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. +/// @param[in] expansion Should we expect the expansion bit to be set. +/// Default is true. +/// @return True if it can decode it, false if it can't. +/// @note This procedure returns a value suitable for use in `sendSharpRaw()`. +/// @todo Need to ensure capture of the inverted message as it can +/// be missed due to the interrupt timeout used to detect an end of message. +/// Several compliance checks are disabled until that is resolved. +bool IRrecv::decodeSharp(decode_results *results, uint16_t offset, + const uint16_t nbits, const bool strict, + const bool expansion) { + if (results->rawlen <= 2 * nbits + kFooter - 1 + offset) + return false; // Not enough entries to be a Sharp message. + // Compliance + if (strict) { + if (nbits != kSharpBits) return false; // Request is out of spec. + // DISABLED - See TODO +#ifdef UNIT_TEST + // An in spec message has the data sent normally, then inverted. So we + // expect twice as many entries than to just get the results. + if (results->rawlen <= (2 * (2 * nbits + kFooter)) - 1 + offset) + return false; +#endif + } + + uint64_t data = 0; + + // Match Data + Footer + uint16_t used; + used = matchGeneric(results->rawbuf + offset, &data, + results->rawlen - offset, nbits, + 0, 0, // No Header + kSharpBitMark, kSharpOneSpace, + kSharpBitMark, kSharpZeroSpace, + kSharpBitMark, kSharpGap, true, 35); + if (!used) return false; + offset += used; + // Compliance + if (strict) { + // Check the state of the expansion bit is what we expect. + if ((data & 0b10) >> 1 != expansion) return false; + // The check bit should be cleared in a normal message. + if (data & 0b1) return false; + // DISABLED - See TODO +#ifdef UNIT_TEST + // Grab the second copy of the data (i.e. inverted) + uint64_t second_data = 0; + // Match Data + Footer + if (!matchGeneric(results->rawbuf + offset, &second_data, + results->rawlen - offset, nbits, + 0, 0, + kSharpBitMark, kSharpOneSpace, + kSharpBitMark, kSharpZeroSpace, + kSharpBitMark, kSharpGap, true, 35)) return false; + // Check that second_data has been inverted correctly. + if (data != (second_data ^ kSharpToggleMask)) return false; +#endif // UNIT_TEST + } + + // Success + results->decode_type = SHARP; + results->bits = nbits; + results->value = data; + // Address & command are actually transmitted in LSB first order. + results->address = reverseBits(data, nbits) & kSharpAddressMask; + results->command = + reverseBits((data >> 2) & kSharpCommandMask, kSharpCommandBits); + return true; +} +#endif // (DECODE_SHARP || DECODE_DENON) + +#if SEND_SHARP_AC +/// Send a Sharp A/C message. +/// Status: Alpha / Untested. +/// @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. +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/638 +/// @see https://github.com/ToniA/arduino-heatpumpir/blob/master/SharpHeatpumpIR.cpp +void IRsend::sendSharpAc(const unsigned char data[], const uint16_t nbytes, + const uint16_t repeat) { + if (nbytes < kSharpAcStateLength) + return; // Not enough bytes to send a proper message. + + sendGeneric(kSharpAcHdrMark, kSharpAcHdrSpace, + kSharpAcBitMark, kSharpAcOneSpace, + kSharpAcBitMark, kSharpAcZeroSpace, + kSharpAcBitMark, kSharpAcGap, + data, nbytes, 38000, false, repeat, 50); +} +#endif // SEND_SHARP_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? +IRSharpAc::IRSharpAc(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 IRSharpAc::begin(void) { _irsend.begin(); } + +#if SEND_SHARP_AC +/// Send the current internal state as an IR message. +/// @param[in] repeat Nr. of times the message will be repeated. +void IRSharpAc::send(const uint16_t repeat) { + _irsend.sendSharpAc(getRaw(), kSharpAcStateLength, repeat); +} +#endif // SEND_SHARP_AC + +/// 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 4-bit checksum value. +uint8_t IRSharpAc::calcChecksum(uint8_t state[], const uint16_t length) { + uint8_t xorsum = xorBytes(state, length - 1); + xorsum ^= GETBITS8(state[length - 1], kLowNibble, kNibbleSize); + xorsum ^= GETBITS8(xorsum, kHighNibble, kNibbleSize); + return GETBITS8(xorsum, kLowNibble, kNibbleSize); +} + +/// 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 IRSharpAc::validChecksum(uint8_t state[], const uint16_t length) { + return GETBITS8(state[length - 1], kHighNibble, kNibbleSize) == + IRSharpAc::calcChecksum(state, length); +} + +/// Calculate and set the checksum values for the internal state. +void IRSharpAc::checksum(void) { + _.Sum = calcChecksum(_.raw); +} + +/// Reset the state of the remote to a known good state/sequence. +void IRSharpAc::stateReset(void) { + static const uint8_t reset[kSharpAcStateLength] = { + 0xAA, 0x5A, 0xCF, 0x10, 0x00, 0x01, 0x00, 0x00, 0x08, 0x80, 0x00, 0xE0, + 0x01}; + std::memcpy(_.raw, reset, kSharpAcStateLength); + _temp = getTemp(); + _mode = _.Mode; + _fan = _.Fan; + _model = getModel(true); +} + +/// 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 *IRSharpAc::getRaw(void) { + checksum(); // Ensure correct settings before sending. + 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 IRSharpAc::setRaw(const uint8_t new_code[], const uint16_t length) { + std::memcpy(_.raw, new_code, std::min(length, kSharpAcStateLength)); + _model = getModel(true); +} + +/// Set the model of the A/C to emulate. +/// @param[in] model The enum of the appropriate model. +void IRSharpAc::setModel(const sharp_ac_remote_model_t model) { + switch (model) { + case sharp_ac_remote_model_t::A705: + case sharp_ac_remote_model_t::A903: + _model = model; + _.Model = true; + break; + default: + _model = sharp_ac_remote_model_t::A907; + _.Model = false; + } + _.Model2 = (_model != sharp_ac_remote_model_t::A907); + // Redo the operating mode as some models don't support all modes. + setMode(_.Mode); +} + +/// Get/Detect the model of the A/C. +/// @param[in] raw Try to determine the model from the raw code only. +/// @return The enum of the compatible model. +sharp_ac_remote_model_t IRSharpAc::getModel(const bool raw) const { + if (raw) { + if (_.Model2) { + if (_.Model) + return sharp_ac_remote_model_t::A705; + else + return sharp_ac_remote_model_t::A903; + } else { + return sharp_ac_remote_model_t::A907; + } + } + return _model; +} + +/// Set the value of the Power Special setting without any checks. +/// @param[in] value The value to set Power Special to. +inline void IRSharpAc::setPowerSpecial(const uint8_t value) { + _.PowerSpecial = value; +} + +/// Get the value of the Power Special setting. +/// @return The setting's value. +uint8_t IRSharpAc::getPowerSpecial(void) const { + return _.PowerSpecial; +} + +/// Clear the "special"/non-normal bits in the power section. +/// e.g. for normal/common command modes. +void IRSharpAc::clearPowerSpecial(void) { + setPowerSpecial(_.PowerSpecial & kSharpAcPowerOn); +} + +/// Is one of the special power states in use? +/// @return true, it is. false, it isn't. +bool IRSharpAc::isPowerSpecial(void) const { + switch (_.PowerSpecial) { + case kSharpAcPowerSetSpecialOff: + case kSharpAcPowerSetSpecialOn: + case kSharpAcPowerTimerSetting: return true; + default: return false; + } +} + +/// Set the requested power state of the A/C to on. +void IRSharpAc::on(void) { setPower(true); } + +/// Set the requested power state of the A/C to off. +void IRSharpAc::off(void) { setPower(false); } + +/// Change the power setting, including the previous power state. +/// @param[in] on true, the setting is on. false, the setting is off. +/// @param[in] prev_on true, the setting is on. false, the setting is off. +void IRSharpAc::setPower(const bool on, const bool prev_on) { + setPowerSpecial(on ? (prev_on ? kSharpAcPowerOn : kSharpAcPowerOnFromOff) + : kSharpAcPowerOff); + // Power operations are incompatible with clean mode. + if (_.Clean) setClean(false); + _.Special = kSharpAcSpecialPower; +} + +/// Get the value of the current power setting. +/// @return true, the setting is on. false, the setting is off. +bool IRSharpAc::getPower(void) const { + switch (_.PowerSpecial) { + case kSharpAcPowerUnknown: + case kSharpAcPowerOff: return false; + default: return true; // Everything else is "probably" on. + } +} + +/// Set the value of the Special (button/command?) setting. +/// @param[in] mode The value to set Special to. +void IRSharpAc::setSpecial(const uint8_t mode) { + switch (mode) { + case kSharpAcSpecialPower: + case kSharpAcSpecialTurbo: + case kSharpAcSpecialTempEcono: + case kSharpAcSpecialFan: + case kSharpAcSpecialSwing: + case kSharpAcSpecialTimer: + case kSharpAcSpecialTimerHalfHour: + _.Special = mode; + break; + default: + _.Special = kSharpAcSpecialPower; + } +} + +/// Get the value of the Special (button/command?) setting. +/// @return The setting's value. +uint8_t IRSharpAc::getSpecial(void) const { return _.Special; } + +/// Set the temperature. +/// @param[in] temp The temperature in degrees celsius. +/// @param[in] save Do we save this setting as a user set one? +void IRSharpAc::setTemp(const uint8_t temp, const bool save) { + switch (_.Mode) { + // Auto & Dry don't allow temp changes and have a special temp. + case kSharpAcAuto: + case kSharpAcDry: + _.raw[kSharpAcByteTemp] = 0; + return; + default: + switch (getModel()) { + case sharp_ac_remote_model_t::A705: + _.raw[kSharpAcByteTemp] = 0xD0; + break; + default: + _.raw[kSharpAcByteTemp] = 0xC0; + } + } + uint8_t degrees = std::max(temp, kSharpAcMinTemp); + degrees = std::min(degrees, kSharpAcMaxTemp); + if (save) _temp = degrees; + _.Temp = degrees - kSharpAcMinTemp; + _.Special = kSharpAcSpecialTempEcono; + clearPowerSpecial(); +} + +/// Get the current temperature setting. +/// @return The current setting for temp. in degrees celsius. +uint8_t IRSharpAc::getTemp(void) const { + return _.Temp + kSharpAcMinTemp; +} + +/// Get the operating mode setting of the A/C. +/// @return The current operating mode setting. +uint8_t IRSharpAc::getMode(void) const { + return _.Mode; +} + +/// Set the operating mode of the A/C. +/// @param[in] mode The desired operating mode. +/// @param[in] save Do we save this setting as a user set one? +void IRSharpAc::setMode(const uint8_t mode, const bool save) { + uint8_t realMode = mode; + if (mode == kSharpAcHeat) { + switch (getModel()) { + case sharp_ac_remote_model_t::A705: + case sharp_ac_remote_model_t::A903: + // These models have no heat mode, use Fan mode instead. + realMode = kSharpAcFan; + break; + default: + break; + } + } + + switch (realMode) { + case kSharpAcAuto: // Also kSharpAcFan + case kSharpAcDry: + // When Dry or Auto, Fan always 2(Auto) + setFan(kSharpAcFanAuto, false); + // FALLTHRU + case kSharpAcCool: + case kSharpAcHeat: + _.Mode = realMode; + break; + default: + setFan(kSharpAcFanAuto, false); + _.Mode = kSharpAcAuto; + } + // Dry/Auto have no temp setting. This step will enforce it. + setTemp(_temp, false); + // Save the mode in case we need to revert to it. eg. Clean + if (save) _mode = _.Mode; + + _.Special = kSharpAcSpecialPower; + clearPowerSpecial(); +} + +/// Set the speed of the fan. +/// @param[in] speed The desired setting. +/// @param[in] save Do we save this setting as a user set one? +void IRSharpAc::setFan(const uint8_t speed, const bool save) { + switch (speed) { + case kSharpAcFanAuto: + case kSharpAcFanMin: + case kSharpAcFanMed: + case kSharpAcFanHigh: + case kSharpAcFanMax: + _.Fan = speed; + if (save) _fan = speed; + break; + default: + _.Fan = kSharpAcFanAuto; + _fan = kSharpAcFanAuto; + } + _.Special = kSharpAcSpecialFan; + clearPowerSpecial(); +} + +/// Get the current fan speed setting. +/// @return The current fan speed/mode. +uint8_t IRSharpAc::getFan(void) const { + return _.Fan; +} + +/// Get the Turbo setting of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRSharpAc::getTurbo(void) const { + return (_.PowerSpecial == kSharpAcPowerSetSpecialOn) && + (_.Special == kSharpAcSpecialTurbo); +} + +/// Set the Turbo setting of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +/// @note If you use this method, you will need to send it before making +/// other changes to the settings, as they may overwrite some of the bits +/// used by this setting. +void IRSharpAc::setTurbo(const bool on) { + if (on) setFan(kSharpAcFanMax); + setPowerSpecial(on ? kSharpAcPowerSetSpecialOn : kSharpAcPowerSetSpecialOff); + _.Special = kSharpAcSpecialTurbo; +} + +/// Get the Vertical Swing setting of the A/C. +/// @return The position of the Vertical Swing setting. +uint8_t IRSharpAc::getSwingV(void) const { return _.Swing; } + +/// Set the Vertical Swing setting of the A/C. +/// @note Some positions may not work on all models. +/// @param[in] position The desired position/setting. +/// @note `setSwingV(kSharpAcSwingVLowest)` will only allow the Lowest setting +/// in Heat mode, it will default to `kSharpAcSwingVLow` otherwise. +/// If you want to set this value in other modes e.g. Cool, you must +/// use `setSwingV`s optional `force` parameter. +/// @param[in] force Do we override the safety checks and just do it? +void IRSharpAc::setSwingV(const uint8_t position, const bool force) { + switch (position) { + case kSharpAcSwingVCoanda: + // Only allowed in Heat mode. + if (!force && getMode() != kSharpAcHeat) { + setSwingV(kSharpAcSwingVLow); // Use the next lowest setting. + return; + } + // FALLTHRU + case kSharpAcSwingVHigh: + case kSharpAcSwingVMid: + case kSharpAcSwingVLow: + case kSharpAcSwingVToggle: + case kSharpAcSwingVOff: + case kSharpAcSwingVLast: // Technically valid, but we don't use it. + // All expected non-positions set the special bits. + _.Special = kSharpAcSpecialSwing; + // FALLTHRU + case kSharpAcSwingVIgnore: + _.Swing = position; + } +} + +/// Convert a standard A/C vertical swing into its native setting. +/// @param[in] position A stdAc::swingv_t position to convert. +/// @return The equivalent native horizontal swing position. +uint8_t IRSharpAc::convertSwingV(const stdAc::swingv_t position) { + switch (position) { + case stdAc::swingv_t::kHighest: + case stdAc::swingv_t::kHigh: return kSharpAcSwingVHigh; + case stdAc::swingv_t::kMiddle: return kSharpAcSwingVMid; + case stdAc::swingv_t::kLow: return kSharpAcSwingVLow; + case stdAc::swingv_t::kLowest: return kSharpAcSwingVCoanda; + case stdAc::swingv_t::kAuto: return kSharpAcSwingVToggle; + case stdAc::swingv_t::kOff: return kSharpAcSwingVOff; + default: return kSharpAcSwingVIgnore; + } +} + +/// Get the (vertical) Swing Toggle setting of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRSharpAc::getSwingToggle(void) const { + return getSwingV() == kSharpAcSwingVToggle; +} + +/// Set the (vertical) Swing Toggle setting of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRSharpAc::setSwingToggle(const bool on) { + setSwingV(on ? kSharpAcSwingVToggle : kSharpAcSwingVIgnore); + if (on) _.Special = kSharpAcSpecialSwing; +} + +/// Get the Ion (Filter) setting of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRSharpAc::getIon(void) const { return _.Ion; } + +/// Set the Ion (Filter) setting of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRSharpAc::setIon(const bool on) { + _.Ion = on; + clearPowerSpecial(); + if (on) _.Special = kSharpAcSpecialSwing; +} + +/// Get the Economical mode toggle setting of the A/C. +/// @return true, the setting is on. false, the setting is off. +/// @note Shares the same location as the Light setting on A705. +bool IRSharpAc::_getEconoToggle(void) const { + return (_.PowerSpecial == kSharpAcPowerSetSpecialOn) && + (_.Special == kSharpAcSpecialTempEcono); +} + +/// Set the Economical mode toggle setting of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +/// @warning Probably incompatible with `setTurbo()` +/// @note Shares the same location as the Light setting on A705. +void IRSharpAc::_setEconoToggle(const bool on) { + if (on) _.Special = kSharpAcSpecialTempEcono; + setPowerSpecial(on ? kSharpAcPowerSetSpecialOn : kSharpAcPowerSetSpecialOff); +} + +/// Set the Economical mode toggle setting of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +/// @warning Probably incompatible with `setTurbo()` +/// @note Available on the A907 models. +void IRSharpAc::setEconoToggle(const bool on) { + if (_model == sharp_ac_remote_model_t::A907) _setEconoToggle(on); +} + +/// Get the Economical mode toggle setting of the A/C. +/// @return true, the setting is on. false, the setting is off. +/// @note Available on the A907 models. +bool IRSharpAc::getEconoToggle(void) const { + return _model == sharp_ac_remote_model_t::A907 && _getEconoToggle(); +} + +/// Set the Light mode toggle setting of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +/// @warning Probably incompatible with `setTurbo()` +/// @note Not available on the A907 model. +void IRSharpAc::setLightToggle(const bool on) { + if (_model != sharp_ac_remote_model_t::A907) _setEconoToggle(on); +} + +/// Get the Light toggle setting of the A/C. +/// @return true, the setting is on. false, the setting is off. +/// @note Not available on the A907 model. +bool IRSharpAc::getLightToggle(void) const { + return _model != sharp_ac_remote_model_t::A907 && _getEconoToggle(); +} + +/// Get how long the timer is set for, in minutes. +/// @return The time in nr of minutes. +uint16_t IRSharpAc::getTimerTime(void) const { + return _.TimerHours * kSharpAcTimerIncrement * 2 + + ((_.Special == kSharpAcSpecialTimerHalfHour) ? kSharpAcTimerIncrement + : 0); +} + +/// Is the Timer enabled? +/// @return true, the setting is on. false, the setting is off. +bool IRSharpAc::getTimerEnabled(void) const { return _.TimerEnabled; } + +/// Get the current timer type. +/// @return true, It's an "On" timer. false, It's an "Off" timer. +bool IRSharpAc::getTimerType(void) const { return _.TimerType; } + +/// Set or cancel the timer function. +/// @param[in] enable Is the timer to be enabled (true) or canceled(false)? +/// @param[in] timer_type An On (true) or an Off (false). Ignored if canceled. +/// @param[in] mins Nr. of minutes the timer is to be set to. +/// @note Rounds down to 30 min increments. (max: 720 mins (12h), 0 is Off) +void IRSharpAc::setTimer(bool enable, bool timer_type, uint16_t mins) { + uint8_t half_hours = std::min(mins / kSharpAcTimerIncrement, + kSharpAcTimerHoursMax * 2); + if (half_hours == 0) enable = false; + if (!enable) { + half_hours = 0; + timer_type = kSharpAcOffTimerType; + } + _.TimerEnabled = enable; + _.TimerType = timer_type; + _.TimerHours = half_hours / 2; + // Handle non-round hours. + _.Special = (half_hours % 2) ? kSharpAcSpecialTimerHalfHour + : kSharpAcSpecialTimer; + setPowerSpecial(kSharpAcPowerTimerSetting); +} + +/// Get the Clean setting of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRSharpAc::getClean(void) const { + return _.Clean; +} + +/// Set the Economical mode toggle setting of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +/// @note Officially A/C unit needs to be "Off" before clean mode can be entered +void IRSharpAc::setClean(const bool on) { + // Clean mode appears to be just default dry mode, with an extra bit set. + if (on) { + setMode(kSharpAcDry, false); + setPower(true, false); + } else { + // Restore the previous operation mode & fan speed. + setMode(_mode, false); + setFan(_fan, false); + } + _.Clean = on; + clearPowerSpecial(); +} + +/// 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 IRSharpAc::convertMode(const stdAc::opmode_t mode) { + switch (mode) { + case stdAc::opmode_t::kCool: return kSharpAcCool; + case stdAc::opmode_t::kHeat: return kSharpAcHeat; + case stdAc::opmode_t::kDry: return kSharpAcDry; + // No Fan mode. + default: return kSharpAcAuto; + } +} + +/// Convert a stdAc::fanspeed_t enum into it's native speed. +/// @param[in] speed The enum to be converted. +/// @param[in] model The enum of the appropriate model. +/// @return The native equivalent of the enum. +uint8_t IRSharpAc::convertFan(const stdAc::fanspeed_t speed, + const sharp_ac_remote_model_t model) { + switch (model) { + case sharp_ac_remote_model_t::A705: + case sharp_ac_remote_model_t::A903: + switch (speed) { + case stdAc::fanspeed_t::kLow: return kSharpAcFanA705Low; + case stdAc::fanspeed_t::kMedium: return kSharpAcFanA705Med; + default: {}; // Fall thru to the next/default clause if not the above + // special cases. + } + // FALL THRU + default: + switch (speed) { + case stdAc::fanspeed_t::kMin: + case stdAc::fanspeed_t::kLow: return kSharpAcFanMin; + case stdAc::fanspeed_t::kMedium: return kSharpAcFanMed; + case stdAc::fanspeed_t::kHigh: return kSharpAcFanHigh; + case stdAc::fanspeed_t::kMax: return kSharpAcFanMax; + default: return kSharpAcFanAuto; + } + } +} + +/// 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 IRSharpAc::toCommonMode(const uint8_t mode) const { + switch (mode) { + case kSharpAcCool: return stdAc::opmode_t::kCool; + case kSharpAcHeat: return stdAc::opmode_t::kHeat; + case kSharpAcDry: return stdAc::opmode_t::kDry; + case kSharpAcAuto: // Also kSharpAcFan + switch (getModel()) { + case sharp_ac_remote_model_t::A705: return stdAc::opmode_t::kFan; + default: return stdAc::opmode_t::kAuto; + } + break; + 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 IRSharpAc::toCommonFanSpeed(const uint8_t speed) const { + switch (getModel()) { + case sharp_ac_remote_model_t::A705: + case sharp_ac_remote_model_t::A903: + switch (speed) { + case kSharpAcFanA705Low: return stdAc::fanspeed_t::kLow; + case kSharpAcFanA705Med: return stdAc::fanspeed_t::kMedium; + } + // FALL-THRU + default: + switch (speed) { + case kSharpAcFanMax: return stdAc::fanspeed_t::kMax; + case kSharpAcFanHigh: return stdAc::fanspeed_t::kHigh; + case kSharpAcFanMed: return stdAc::fanspeed_t::kMedium; + case kSharpAcFanMin: return stdAc::fanspeed_t::kMin; + default: return stdAc::fanspeed_t::kAuto; + } + } +} + +/// Convert a native vertical swing postion to it's common equivalent. +/// @param[in] pos A native position to convert. +/// @param[in] mode What operating mode are we in? +/// @return The common vertical swing position. +stdAc::swingv_t IRSharpAc::toCommonSwingV(const uint8_t pos, + const stdAc::opmode_t mode) const { + switch (pos) { + case kSharpAcSwingVHigh: return stdAc::swingv_t::kHighest; + case kSharpAcSwingVMid: return stdAc::swingv_t::kMiddle; + case kSharpAcSwingVLow: return stdAc::swingv_t::kLow; + case kSharpAcSwingVCoanda: // Coanda has mode dependent positionss + switch (mode) { + case stdAc::opmode_t::kCool: return stdAc::swingv_t::kHighest; + case stdAc::opmode_t::kHeat: return stdAc::swingv_t::kLowest; + default: return stdAc::swingv_t::kOff; + } + case kSharpAcSwingVToggle: return stdAc::swingv_t::kAuto; + default: return stdAc::swingv_t::kOff; + } +} + +/// 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 IRSharpAc::toCommon(const stdAc::state_t *prev) const { + stdAc::state_t result; + // Start with the previous state if given it. + if (prev != NULL) result = *prev; + result.protocol = decode_type_t::SHARP_AC; + result.model = getModel(); + result.power = getPower(); + result.mode = toCommonMode(_.Mode); + result.celsius = true; + result.degrees = getTemp(); + result.fanspeed = toCommonFanSpeed(_.Fan); + result.turbo = getTurbo(); + if (getSwingV() != kSharpAcSwingVIgnore) + result.swingv = toCommonSwingV(getSwingV(), result.mode); + result.filter = _.Ion; + result.econo = getEconoToggle(); + result.light = getLightToggle(); + result.clean = _.Clean; + // Not supported. + result.swingh = stdAc::swingh_t::kOff; + result.quiet = 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 IRSharpAc::toString(void) const { + String result = ""; + const sharp_ac_remote_model_t model = getModel(); + result.reserve(170); // Reserve some heap for the string to reduce fragging. + result += addModelToString(decode_type_t::SHARP_AC, getModel(), false); + + result += addLabeledString(isPowerSpecial() ? String("-") + : String(getPower() ? kOnStr + : kOffStr), + kPowerStr); + const uint8_t mode = _.Mode; + result += addModeToString( + mode, + // Make the value invalid if the model doesn't support an Auto mode. + (model == sharp_ac_remote_model_t::A907) ? kSharpAcAuto : 255, + kSharpAcCool, kSharpAcHeat, kSharpAcDry, kSharpAcFan); + result += addTempToString(getTemp()); + switch (model) { + case sharp_ac_remote_model_t::A705: + case sharp_ac_remote_model_t::A903: + result += addFanToString(_.Fan, kSharpAcFanMax, kSharpAcFanA705Low, + kSharpAcFanAuto, kSharpAcFanAuto, + kSharpAcFanA705Med); + break; + default: + result += addFanToString(_.Fan, kSharpAcFanMax, kSharpAcFanMin, + kSharpAcFanAuto, kSharpAcFanAuto, + kSharpAcFanMed); + } + if (getSwingV() == kSharpAcSwingVIgnore) { + result += addIntToString(kSharpAcSwingVIgnore, kSwingVStr); + result += kSpaceLBraceStr; + result += kNAStr; + result += ')'; + } else { + result += addSwingVToString( + getSwingV(), 0xFF, + // Coanda means Highest when in Cool mode. + (mode == kSharpAcCool) ? kSharpAcSwingVCoanda : kSharpAcSwingVToggle, + kSharpAcSwingVHigh, + 0xFF, // Upper Middle is unused + kSharpAcSwingVMid, + 0xFF, // Lower Middle is unused + kSharpAcSwingVLow, + kSharpAcSwingVCoanda, + kSharpAcSwingVOff, + // Below are unused. + kSharpAcSwingVToggle, + 0xFF, + 0xFF); + } + result += addBoolToString(getTurbo(), kTurboStr); + result += addBoolToString(_.Ion, kIonStr); + switch (model) { + case sharp_ac_remote_model_t::A705: + case sharp_ac_remote_model_t::A903: + result += addToggleToString(getLightToggle(), kLightStr); + break; + default: + result += addToggleToString(getEconoToggle(), kEconoStr); + } + result += addBoolToString(_.Clean, kCleanStr); + if (_.TimerEnabled) + result += addLabeledString(minsToString(getTimerTime()), + _.TimerType ? kOnTimerStr : kOffTimerStr); + return result; +} + +#if DECODE_SHARP_AC +/// Decode the supplied Sharp A/C 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/638 +/// @see https://github.com/ToniA/arduino-heatpumpir/blob/master/SharpHeatpumpIR.cpp +bool IRrecv::decodeSharpAc(decode_results *results, uint16_t offset, + const uint16_t nbits, const bool strict) { + // Compliance + if (strict && nbits != kSharpAcBits) return false; + + // Match Header + Data + Footer + uint16_t used; + used = matchGeneric(results->rawbuf + offset, results->state, + results->rawlen - offset, nbits, + kSharpAcHdrMark, kSharpAcHdrSpace, + kSharpAcBitMark, kSharpAcOneSpace, + kSharpAcBitMark, kSharpAcZeroSpace, + kSharpAcBitMark, kSharpAcGap, true, + _tolerance, kMarkExcess, false); + if (used == 0) return false; + offset += used; + // Compliance + if (strict) { + if (!IRSharpAc::validChecksum(results->state)) return false; + } + + // Success + results->decode_type = SHARP_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_SHARP_AC diff --git a/lib/IRremoteESP8266/src/ir_Sharp.h b/lib/IRremoteESP8266/src/ir_Sharp.h index 507f63e437..b3be534e7b 100644 --- a/lib/IRremoteESP8266/src/ir_Sharp.h +++ b/lib/IRremoteESP8266/src/ir_Sharp.h @@ -32,13 +32,13 @@ #ifndef UNIT_TEST #include #endif -#include "../src/IRrecv.h" -#include "../src/IRremoteESP8266.h" -#include "../src/IRsend.h" +#include "IRrecv.h" +#include "IRremoteESP8266.h" +#include "IRsend.h" #ifdef UNIT_TEST -#include "../src/IRsend_test.h" +#include "IRsend_test.h" #endif -#include "../src/IRutils.h" +#include "IRutils.h" /// Native representation of a Sharp A/C message. union SharpProtocol{ diff --git a/lib/IRremoteESP8266/src/ir_Sherwood.cpp b/lib/IRremoteESP8266/src/ir_Sherwood.cpp new file mode 100644 index 0000000000..76ffc35ff0 --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_Sherwood.cpp @@ -0,0 +1,24 @@ +// Copyright 2017 David Conran + +/// @file +/// @brief Support for Sherwood protocols. + +// Supports: +// Brand: Sherwood, Model: RC-138 remote +// Brand: Sherwood, Model: RD6505(B) Receiver + +#include +#include "IRsend.h" + +#if SEND_SHERWOOD +/// Send an IR command to a Sherwood device. +/// 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 Sherwood remote codes appear to be NEC codes with a mandatory repeat +/// code. i.e. repeat should be >= kSherwoodMinRepeat (1). +void IRsend::sendSherwood(uint64_t data, uint16_t nbits, uint16_t repeat) { + sendNEC(data, nbits, std::max((uint16_t)kSherwoodMinRepeat, repeat)); +} +#endif // SEND_SHERWOOD diff --git a/lib/IRremoteESP8266/src/ir_Sony.cpp b/lib/IRremoteESP8266/src/ir_Sony.cpp new file mode 100644 index 0000000000..0dbbec3c81 --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_Sony.cpp @@ -0,0 +1,191 @@ +// Copyright 2009 Ken Shirriff +// Copyright 2016 marcosamarinho +// Copyright 2017,2020 David Conran + +/// @file +/// @brief Support for Sony SIRC(Serial Infra-Red Control) protocols. +/// Sony originally added from https://github.com/shirriff/Arduino-IRremote/ +/// Updates from marcosamarinho +/// @see http://www.sbprojects.net/knowledge/ir/sirc.php +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1018 + +// Supports: +// Brand: Sony, Model: HT-CT380 Soundbar (Uses 38kHz & 3 repeats) + +#include +#include "IRrecv.h" +#include "IRsend.h" +#include "IRutils.h" + + +// Constants +const uint16_t kSonyTick = 200; +const uint16_t kSonyHdrMarkTicks = 12; +const uint16_t kSonyHdrMark = kSonyHdrMarkTicks * kSonyTick; +const uint16_t kSonySpaceTicks = 3; +const uint16_t kSonySpace = kSonySpaceTicks * kSonyTick; +const uint16_t kSonyOneMarkTicks = 6; +const uint16_t kSonyOneMark = kSonyOneMarkTicks * kSonyTick; +const uint16_t kSonyZeroMarkTicks = 3; +const uint16_t kSonyZeroMark = kSonyZeroMarkTicks * kSonyTick; +const uint16_t kSonyRptLengthTicks = 225; +const uint16_t kSonyRptLength = kSonyRptLengthTicks * kSonyTick; +const uint16_t kSonyMinGapTicks = 50; +const uint16_t kSonyMinGap = kSonyMinGapTicks * kSonyTick; +const uint16_t kSonyStdFreq = 40000; // kHz +const uint16_t kSonyAltFreq = 38000; // kHz + +#if SEND_SONY +/// Send a standard Sony/SIRC(Serial Infra-Red Control) message. (40kHz) +/// 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 sendSony() should typically be called with repeat=2 as Sony devices +/// expect the message to be sent at least 3 times. +void IRsend::sendSony(const uint64_t data, const uint16_t nbits, + const uint16_t repeat) { + _sendSony(data, nbits, repeat, kSonyStdFreq); +} + +/// Send an alternative 38kHz Sony/SIRC(Serial Infra-Red Control) 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 `sendSony38()` should typically be called with repeat=3 as these Sony +/// devices expect the message to be sent at least 4 times. +/// @warning Messages send via this method will be detected by this library as +/// just `SONY`, not `SONY_38K` as the library has no way to determine the +/// modulation frequency used. Hence, there is no `decodeSony38()`. +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1018 +void IRsend::sendSony38(const uint64_t data, const uint16_t nbits, + const uint16_t repeat) { + _sendSony(data, nbits, repeat, kSonyAltFreq); +} + +/// Internal procedure to generate a Sony/SIRC(Serial Infra-Red Control) 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. +/// @param[in] freq Frequency of the modulation to transmit at. (Hz or kHz) +void IRsend::_sendSony(const uint64_t data, const uint16_t nbits, + const uint16_t repeat, const uint16_t freq) { + sendGeneric(kSonyHdrMark, kSonySpace, kSonyOneMark, kSonySpace, kSonyZeroMark, + kSonySpace, + 0, // No Footer mark. + kSonyMinGap, kSonyRptLength, data, nbits, freq, true, repeat, 33); +} + +/// Convert Sony/SIRC command, address, & extended bits into sendSony format. +/// Status: STABLE / Should be working. +/// @param[in] nbits Sony protocol bit size. +/// @param[in] command Sony command bits. +/// @param[in] address Sony address bits. +/// @param[in] extended Sony extended bits. +/// @return A `sendSony()` etc compatible data message. +uint32_t IRsend::encodeSony(const uint16_t nbits, const uint16_t command, + const uint16_t address, const uint16_t extended) { + uint32_t result = 0; + switch (nbits) { + case 12: // 5 address bits. + result = address & 0x1F; + break; + case 15: // 8 address bits. + result = address & 0xFF; + break; + case 20: // 5 address bits, 8 extended bits. + result = address & 0x1F; + result |= (extended & 0xFF) << 5; + break; + default: + return 0; // This is not an expected Sony bit size/protocol. + } + result = (result << 7) | (command & 0x7F); // All sizes have 7 command bits. + return reverseBits(result, nbits); // sendSony uses reverse ordered bits. +} +#endif // SEND_SONY + +#if DECODE_SONY +/// Decode the supplied Sony/SIRC message. +/// Status: STABLE / Should be working. strict mode is ALPHA / Untested. +/// @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 SONY protocol, SIRC (Serial Infra-Red Control) can be 12, 15, or 20 +/// bits long. +bool IRrecv::decodeSony(decode_results *results, uint16_t offset, + const uint16_t nbits, const bool strict) { + if (results->rawlen <= 2 * nbits + kHeader - 1 + offset) + return false; // Message is smaller than we expected. + + // Compliance + if (strict) { + switch (nbits) { // Check we've been called with a correct bit size. + case 12: + case 15: + case 20: + break; + default: + return false; // The request doesn't strictly match the protocol defn. + } + } + + uint64_t data = 0; + uint16_t actualBits; + + // Header + if (!matchMark(results->rawbuf[offset], kSonyHdrMark)) return false; + // Calculate how long the common tick time is based on the header mark. + uint32_t tick = results->rawbuf[offset++] * kRawTick / kSonyHdrMarkTicks; + + // Data + for (actualBits = 0; offset < results->rawlen - 1; actualBits++, offset++) { + // The gap after a Sony packet for a repeat should be kSonyMinGap according + // to the spec. + if (matchAtLeast(results->rawbuf[offset], kSonyMinGapTicks * tick)) + break; // Found a repeat space. + if (!matchSpace(results->rawbuf[offset++], kSonySpaceTicks * tick)) + return false; + if (matchMark(results->rawbuf[offset], kSonyOneMarkTicks * tick)) + data = (data << 1) | 1; + else if (matchMark(results->rawbuf[offset], kSonyZeroMarkTicks * tick)) + data <<= 1; + else + return false; + } + // No Footer for Sony. + + // Compliance + if (strict && actualBits != nbits) + return false; // We got the wrong number of bits. + + // Success + results->bits = actualBits; + results->value = data; + // We can't detect SONY_38K messages so always assume it is just `SONY` 40kHz. + results->decode_type = SONY; + // Message comes in LSB first. Convert ot MSB first. + data = reverseBits(data, actualBits); + // Decode the address & command from raw decode value. + switch (actualBits) { + case 12: // 7 command bits, 5 address bits. + case 15: // 7 command bits, 8 address bits. + results->command = data & 0x7F; // Bits 0-6 + results->address = data >> 7; // Bits 7-14 + break; + case 20: // 7 command bits, 5 address bits, 8 extended (command) bits. + results->command = (data & 0x7F) + ((data >> 12) << 7); // Bits 0-6,12-19 + results->address = (data >> 7) & 0x1F; // Bits 7-11 + break; + default: // Shouldn't happen, but just in case. + results->address = 0; + results->command = 0; + } + return true; +} +#endif // DECODE_SONY diff --git a/lib/IRremoteESP8266/src/ir_Symphony.cpp b/lib/IRremoteESP8266/src/ir_Symphony.cpp new file mode 100644 index 0000000000..b629ef72d3 --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_Symphony.cpp @@ -0,0 +1,95 @@ +// Copyright 2020 David Conran + +/// @file +/// @brief Support for Symphony protocols. +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1057 +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1105 +/// @see https://www.alldatasheet.com/datasheet-pdf/pdf/124369/ANALOGICTECH/SM5021B.html + +// Supports: +// Brand: Symphony, Model: Air Cooler 3Di +// Brand: SamHop, Model: SM3015 Fan Remote Control +// Brand: SamHop, Model: SM5021 Encoder chip +// Brand: SamHop, Model: SM5032 Decoder chip +// Brand: Blyss, Model: Owen-SW-5 3 Fan +// Brand: Blyss, Model: WP-YK8 090218 remote +// Brand: Westinghouse, Model: Ceiling fan +// Brand: Westinghouse, Model: 78095 Remote +// Brand: Satellite Electronic, Model: ID6 Remote +// Brand: Satellite Electronic, Model: JY199I Fan driver +// Brand: Satellite Electronic, Model: JY199I-L Fan driver +// Brand: SilverCrest, Model: SSVS 85 A1 Fan + +// Known Codes: +// SilverCrest SSVS 85 A1 Fan: +// 0x581 - On/Off +// 0x582 - Speed +// 0x584 - Mist +// 0x588 - Timer +// 0x590 - OSC + +#include +#include "IRrecv.h" +#include "IRsend.h" +#include "IRtimer.h" +#include "IRutils.h" + +// Constants +const uint16_t kSymphonyZeroMark = 400; +const uint16_t kSymphonyZeroSpace = 1250; +const uint16_t kSymphonyOneMark = kSymphonyZeroSpace; +const uint16_t kSymphonyOneSpace = kSymphonyZeroMark; +const uint32_t kSymphonyFooterGap = 4 * (kSymphonyZeroMark + + kSymphonyZeroSpace); + +#if SEND_SYMPHONY +/// Send a Symphony packet. +/// 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. +void IRsend::sendSymphony(uint64_t data, uint16_t nbits, uint16_t repeat) { + sendGeneric(0, 0, + kSymphonyOneMark, kSymphonyOneSpace, + kSymphonyZeroMark, kSymphonyZeroSpace, + 0, kSymphonyFooterGap, + data, nbits, 38000, true, repeat, kDutyDefault); +} +#endif // SEND_SYMPHONY + +#if DECODE_SYMPHONY +/// Decode the supplied Symphony packet/message. +/// Status: STABLE / Should 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::decodeSymphony(decode_results *results, uint16_t offset, + const uint16_t nbits, const bool strict) { + uint64_t data = 0; + + if (results->rawlen < 2 * nbits + offset - 1) + return false; // Not enough entries to ever be SYMPHONY. + // Compliance + if (strict && nbits != kSymphonyBits) return false; + + if (!matchGenericConstBitTime(results->rawbuf + offset, &data, + results->rawlen - offset, + nbits, + 0, 0, // No Header + kSymphonyOneMark, kSymphonyZeroMark, + 0, kSymphonyFooterGap, true, + _tolerance, 0)) + return false; + + // Success + results->value = data; + results->decode_type = decode_type_t::SYMPHONY; + results->bits = nbits; + results->address = 0; + results->command = 0; + return true; +} +#endif // DECODE_SYMPHONY diff --git a/lib/IRremoteESP8266/src/ir_Tcl.cpp b/lib/IRremoteESP8266/src/ir_Tcl.cpp new file mode 100644 index 0000000000..a9e8784a39 --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_Tcl.cpp @@ -0,0 +1,529 @@ +// Copyright 2019, 2021 David Conran + +/// @file +/// @brief Support for TCL protocols. + +#include "ir_Tcl.h" +#include +#include +#ifndef ARDUINO +#include +#endif +#include "IRremoteESP8266.h" +#include "IRtext.h" +#include "IRutils.h" + +// Constants + +const uint8_t kTcl112AcTimerResolution = 20; // Minutes +const uint16_t kTcl112AcTimerMax = 720; // Minutes (12 hrs) + +using irutils::addBoolToString; +using irutils::addFanToString; +using irutils::addIntToString; +using irutils::addLabeledString; +using irutils::addModeToString; +using irutils::addModelToString; +using irutils::addSwingVToString; +using irutils::addTempFloatToString; +using irutils::minsToString; + +#if SEND_TCL112AC +/// Send a TCL 112-bit A/C message. +/// 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::sendTcl112Ac(const unsigned char data[], const uint16_t nbytes, + const uint16_t repeat) { + sendGeneric(kTcl112AcHdrMark, kTcl112AcHdrSpace, + kTcl112AcBitMark, kTcl112AcOneSpace, + kTcl112AcBitMark, kTcl112AcZeroSpace, + kTcl112AcBitMark, kTcl112AcGap, + data, nbytes, 38000, false, repeat, 50); +} +#endif // SEND_TCL112AC + +/// 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? +IRTcl112Ac::IRTcl112Ac(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 IRTcl112Ac::begin(void) { _irsend.begin(); } + +#if SEND_TCL112AC +/// Send the current internal state as an IR message. +/// @param[in] repeat Nr. of times the message will be repeated. +void IRTcl112Ac::send(const uint16_t repeat) { + uint8_t save[kTcl112AcStateLength]; + // Do we need to send the special "quiet" message? + if (_quiet != _quiet_prev) { + // Backup the current state. + std::memcpy(save, _.raw, kTcl112AcStateLength); + const uint8_t quiet_off[kTcl112AcStateLength] = { + 0x23, 0xCB, 0x26, 0x02, 0x00, 0x40, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x65}; + // Use a known good quiet/mute off/type 2 state for the time being. + // Ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/1528#issuecomment-876989044 + setRaw(quiet_off); + setQuiet(_quiet); + // Send it. + _irsend.sendTcl112Ac(getRaw(), kTcl112AcStateLength, repeat); + // Now it's been sent, update the quiet previous state. + _quiet_prev = _quiet; + // Restore the old state. + setRaw(save); + // Make sure it looks like a normal TCL mesg if needed. + if (_.MsgType == kTcl112AcNormal) _.isTcl = true; + } + // Send the normal (type 1) state. + _irsend.sendTcl112Ac(getRaw(), kTcl112AcStateLength, repeat); +} +#endif // SEND_TCL112AC + +/// 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 IRTcl112Ac::calcChecksum(uint8_t state[], const uint16_t length) { + if (length) { + if (length > 4 && state[3] == 0x02) { // Special nessage? + return sumBytes(state, length - 1, 0xF); // Checksum needs an offset. + } else { + return sumBytes(state, length - 1); + } + } else { + return 0; + } +} + +/// Calculate & set the checksum for the current internal state of the remote. +/// @param[in] length The length/size of the internal array to checksum. +void IRTcl112Ac::checksum(const uint16_t length) { + // Stored the checksum value in the last byte. + if (length > 1) + _.Sum = calcChecksum(_.raw, 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/size of the array. +/// @return true, if the state has a valid checksum. Otherwise, false. +bool IRTcl112Ac::validChecksum(uint8_t state[], const uint16_t length) { + return (length > 1 && state[length - 1] == calcChecksum(state, length)); +} + +/// Check the supplied state looks like a TCL112AC message. +/// @param[in] state The array to verify the checksum of. +/// @note Assumes the state is the correct size. +/// @return true, if the state looks like a TCL112AC message. Otherwise, false. +/// @warning This is just a guess. +bool IRTcl112Ac::isTcl(const uint8_t state[]) { + Tcl112Protocol mesg; + std::memcpy(mesg.raw, state, kTcl112AcStateLength); + return (mesg.MsgType != kTcl112AcNormal) || mesg.isTcl; +} + +/// Reset the internal state of the emulation. (On, Cool, 24C) +void IRTcl112Ac::stateReset(void) { + // A known good state. (On, Cool, 24C) + static const uint8_t reset[kTcl112AcStateLength] = { + 0x23, 0xCB, 0x26, 0x01, 0x00, 0x24, 0x03, 0x07, 0x40, 0x00, 0x00, 0x00, + 0x00, 0x03}; + std::memcpy(_.raw, reset, kTcl112AcStateLength); + _quiet = false; + _quiet_prev = false; + _quiet_explictly_set = false; +} + +/// Get/Detect the model of the A/C. +/// @return The enum of the compatible model. +tcl_ac_remote_model_t IRTcl112Ac::getModel(void) const { + return isTcl(_.raw) ? tcl_ac_remote_model_t::TAC09CHSD + : tcl_ac_remote_model_t::GZ055BE1; +} + +/// Set the model of the A/C to emulate. +/// @param[in] model The enum of the appropriate model. +void IRTcl112Ac::setModel(const tcl_ac_remote_model_t model) { + _.isTcl = (model != tcl_ac_remote_model_t::GZ055BE1); +} + +/// 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* IRTcl112Ac::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 IRTcl112Ac::setRaw(const uint8_t new_code[], const uint16_t length) { + std::memcpy(_.raw, new_code, std::min(length, kTcl112AcStateLength)); +} + +/// Set the requested power state of the A/C to on. +void IRTcl112Ac::on(void) { setPower(true); } + +/// Set the requested power state of the A/C to off. +void IRTcl112Ac::off(void) { setPower(false); } + +/// Change the power setting. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRTcl112Ac::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 IRTcl112Ac::getPower(void) const { return _.Power; } + +/// Get the operating mode setting of the A/C. +/// @return The current operating mode setting. +uint8_t IRTcl112Ac::getMode(void) const { return _.Mode; } + +/// Set the operating mode of the A/C. +/// @param[in] mode The desired operating mode. +/// @note Fan/Ventilation mode sets the fan speed to high. +/// Unknown values default to Auto. +void IRTcl112Ac::setMode(const uint8_t mode) { + // If we get an unexpected mode, default to AUTO. + switch (mode) { + case kTcl112AcFan: + setFan(kTcl112AcFanHigh); + // FALLTHRU + case kTcl112AcAuto: + case kTcl112AcCool: + case kTcl112AcHeat: + case kTcl112AcDry: + _.Mode = mode; + break; + default: + _.Mode = kTcl112AcAuto; + } +} + +/// Set the temperature. +/// @param[in] celsius The temperature in degrees celsius. +/// @note The temperature resolution is 0.5 of a degree. +void IRTcl112Ac::setTemp(const float celsius) { + // Make sure we have desired temp in the correct range. + float safecelsius = std::max(celsius, kTcl112AcTempMin); + safecelsius = std::min(safecelsius, kTcl112AcTempMax); + // Convert to integer nr. of half degrees. + uint8_t nrHalfDegrees = safecelsius * 2; + // Do we have a half degree celsius? + _.HalfDegree = nrHalfDegrees & 1; + _.Temp = static_cast(kTcl112AcTempMax - nrHalfDegrees / 2); +} + +/// Get the current temperature setting. +/// @return The current setting for temp. in degrees celsius. +/// @note The temperature resolution is 0.5 of a degree. +float IRTcl112Ac::getTemp(void) const { + float result = kTcl112AcTempMax - _.Temp; + if (_.HalfDegree) result += 0.5; + return result; +} + +/// Set the speed of the fan. +/// @param[in] speed The desired setting. +/// @note Unknown speeds will default to Auto. +void IRTcl112Ac::setFan(const uint8_t speed) { + switch (speed) { + case kTcl112AcFanAuto: + case kTcl112AcFanMin: + case kTcl112AcFanLow: + case kTcl112AcFanMed: + case kTcl112AcFanHigh: + _.Fan = speed; + break; + default: + _.Fan = kTcl112AcFanAuto; + } +} + +/// Get the current fan speed setting. +/// @return The current fan speed/mode. +uint8_t IRTcl112Ac::getFan(void) const { return _.Fan; } + +/// Set the economy setting of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRTcl112Ac::setEcono(const bool on) { _.Econo = on; } + +/// Get the economy setting of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRTcl112Ac::getEcono(void) const { return _.Econo; } + +/// Set the Health (Filter) setting of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRTcl112Ac::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 IRTcl112Ac::getHealth(void) const { return _.Health; } + +/// Set the Light (LED/Display) setting of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRTcl112Ac::setLight(const bool on) { _.Light = !on; } // Cleared when on. + +/// Get the Light (LED/Display) setting of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRTcl112Ac::getLight(void) const { return !_.Light; } + +/// Set the horizontal swing setting of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRTcl112Ac::setSwingHorizontal(const bool on) { _.SwingH = on; } + +/// Get the horizontal swing setting of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRTcl112Ac::getSwingHorizontal(void) const { return _.SwingH; } + +/// Set the vertical swing setting of the A/C. +/// @param[in] setting The value of the desired setting. +void IRTcl112Ac::setSwingVertical(const uint8_t setting) { + switch (setting) { + case kTcl112AcSwingVOff: + case kTcl112AcSwingVHighest: + case kTcl112AcSwingVHigh: + case kTcl112AcSwingVMiddle: + case kTcl112AcSwingVLow: + case kTcl112AcSwingVLowest: + case kTcl112AcSwingVOn: + _.SwingV = setting; + } +} + +/// Get the vertical swing setting of the A/C. +/// @return The current setting. +uint8_t IRTcl112Ac::getSwingVertical(void) const { return _.SwingV; } + +/// Set the Turbo setting of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRTcl112Ac::setTurbo(const bool on) { + _.Turbo = on; + if (on) { + _.Fan = kTcl112AcFanHigh; + _.SwingV = kTcl112AcSwingVOn; + } +} + +/// Get the Turbo setting of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRTcl112Ac::getTurbo(void) const { return _.Turbo; } + +/// Set the Quiet setting of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRTcl112Ac::setQuiet(const bool on) { + _quiet_explictly_set = true; + _quiet = on; + if (_.MsgType == kTcl112AcSpecial) _.Quiet = on; +} + +/// Get the Quiet setting of the A/C. +/// @param[in] def The default value to use if we are not sure. +/// @return true, the setting is on. false, the setting is off. +bool IRTcl112Ac::getQuiet(const bool def) const { + if (_.MsgType == kTcl112AcSpecial) + return _.Quiet; + else + return _quiet_explictly_set ? _quiet : def; +} + +/// Get how long the On Timer is set for, in minutes. +/// @return The time in nr of minutes. +uint16_t IRTcl112Ac::getOnTimer(void) const { + return _.OnTimer * kTcl112AcTimerResolution; +} + +/// Set or cancel the On Timer function. +/// @param[in] mins Nr. of minutes the timer is to be set to. +/// @note Rounds down to 20 min increments. (max: 720 mins (12h), 0 is Off) +void IRTcl112Ac::setOnTimer(const uint16_t mins) { + _.OnTimer = std::min(mins, kTcl112AcTimerMax) / kTcl112AcTimerResolution; + _.OnTimerEnabled = _.OnTimer > 0; + _.TimerIndicator = _.OnTimerEnabled || _.OffTimerEnabled; +} + +/// Get how long the Off Timer is set for, in minutes. +/// @return The time in nr of minutes. +uint16_t IRTcl112Ac::getOffTimer(void) const { + return _.OffTimer * kTcl112AcTimerResolution; +} + +/// Set or cancel the Off Timer function. +/// @param[in] mins Nr. of minutes the timer is to be set to. +/// @note Rounds down to 20 min increments. (max: 720 mins (12h), 0 is Off) +void IRTcl112Ac::setOffTimer(const uint16_t mins) { + _.OffTimer = std::min(mins, kTcl112AcTimerMax) / kTcl112AcTimerResolution; + _.OffTimerEnabled = _.OffTimer > 0; + _.TimerIndicator = _.OnTimerEnabled || _.OffTimerEnabled; +} + +/// 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 IRTcl112Ac::convertMode(const stdAc::opmode_t mode) { + switch (mode) { + case stdAc::opmode_t::kCool: return kTcl112AcCool; + case stdAc::opmode_t::kHeat: return kTcl112AcHeat; + case stdAc::opmode_t::kDry: return kTcl112AcDry; + case stdAc::opmode_t::kFan: return kTcl112AcFan; + default: return kTcl112AcAuto; + } +} + +/// 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 IRTcl112Ac::convertFan(const stdAc::fanspeed_t speed) { + switch (speed) { + case stdAc::fanspeed_t::kMin: return kTcl112AcFanMin; + case stdAc::fanspeed_t::kLow: return kTcl112AcFanLow; + case stdAc::fanspeed_t::kMedium: return kTcl112AcFanMed; + case stdAc::fanspeed_t::kHigh: + case stdAc::fanspeed_t::kMax: return kTcl112AcFanHigh; + default: return kTcl112AcFanAuto; + } +} + +/// 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 IRTcl112Ac::toCommonMode(const uint8_t mode) { + switch (mode) { + case kTcl112AcCool: return stdAc::opmode_t::kCool; + case kTcl112AcHeat: return stdAc::opmode_t::kHeat; + case kTcl112AcDry: return stdAc::opmode_t::kDry; + case kTcl112AcFan: return stdAc::opmode_t::kFan; + default: return stdAc::opmode_t::kAuto; + } +} + +/// 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 IRTcl112Ac::convertSwingV(const stdAc::swingv_t position) { + switch (position) { + case stdAc::swingv_t::kOff: return kTcl112AcSwingVOff; + case stdAc::swingv_t::kHighest: return kTcl112AcSwingVHighest; + case stdAc::swingv_t::kHigh: return kTcl112AcSwingVHigh; + case stdAc::swingv_t::kMiddle: return kTcl112AcSwingVMiddle; + case stdAc::swingv_t::kLow: return kTcl112AcSwingVLow; + case stdAc::swingv_t::kLowest: return kTcl112AcSwingVLowest; + default: return kTcl112AcSwingVOn; + } +} + +/// Convert a native fan speed into its stdAc equivalent. +/// @param[in] spd The native setting to be converted. +/// @return The stdAc equivalent of the native setting. +stdAc::fanspeed_t IRTcl112Ac::toCommonFanSpeed(const uint8_t spd) { + switch (spd) { + case kTcl112AcFanHigh: return stdAc::fanspeed_t::kMax; + case kTcl112AcFanMed: return stdAc::fanspeed_t::kMedium; + case kTcl112AcFanLow: return stdAc::fanspeed_t::kLow; + case kTcl112AcFanMin: return stdAc::fanspeed_t::kMin; + default: return stdAc::fanspeed_t::kAuto; + } +} + +/// Convert a native vertical swing postion to it's common equivalent. +/// @param[in] setting A native position to convert. +/// @return The common vertical swing position. +stdAc::swingv_t IRTcl112Ac::toCommonSwingV(const uint8_t setting) { + switch (setting) { + case kTcl112AcSwingVOff: return stdAc::swingv_t::kOff; + default: return stdAc::swingv_t::kAuto; + } +} +/// 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 IRTcl112Ac::toCommon(const stdAc::state_t *prev) const { + stdAc::state_t result; + // Start with the previous state if given it. + if (prev != NULL) result = *prev; + result.protocol = decode_type_t::TCL112AC; + result.model = getModel(); + result.quiet = getQuiet(result.quiet); + // The rest only get updated if it is a "normal" message. + if (_.MsgType == kTcl112AcNormal) { + result.power = _.Power; + result.mode = toCommonMode(_.Mode); + result.celsius = true; + result.degrees = getTemp(); + result.fanspeed = toCommonFanSpeed(_.Fan); + result.swingv = toCommonSwingV(_.SwingV); + result.swingh = _.SwingH ? stdAc::swingh_t::kAuto : stdAc::swingh_t::kOff; + result.turbo = _.Turbo; + result.filter = _.Health; + result.econo = _.Econo; + result.light = getLight(); + } + // Not supported. + 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 IRTcl112Ac::toString(void) const { + String result = ""; + result.reserve(220); // Reserve some heap for the string to reduce fragging. + tcl_ac_remote_model_t model = getModel(); + result += addModelToString(decode_type_t::TCL112AC, model, false); + result += addIntToString(_.MsgType, D_STR_TYPE); + switch (_.MsgType) { + case kTcl112AcNormal: + result += addBoolToString(_.Power, kPowerStr); + result += addModeToString(_.Mode, kTcl112AcAuto, kTcl112AcCool, + kTcl112AcHeat, kTcl112AcDry, kTcl112AcFan); + result += addTempFloatToString(getTemp()); + result += addFanToString(_.Fan, kTcl112AcFanHigh, kTcl112AcFanLow, + kTcl112AcFanAuto, kTcl112AcFanMin, + kTcl112AcFanMed); + result += addSwingVToString(_.SwingV, kTcl112AcSwingVOff, + kTcl112AcSwingVHighest, + kTcl112AcSwingVHigh, + 0xFF, // unused + kTcl112AcSwingVMiddle, + 0xFF, // unused + kTcl112AcSwingVLow, + kTcl112AcSwingVLowest, + kTcl112AcSwingVOff, + kTcl112AcSwingVOn, // Swing + 0xFF, 0xFF); // Both Unused + if (model != tcl_ac_remote_model_t::GZ055BE1) { + result += addBoolToString(_.SwingH, kSwingHStr); + result += addBoolToString(_.Econo, kEconoStr); + result += addBoolToString(_.Health, kHealthStr); + result += addBoolToString(_.Turbo, kTurboStr); + result += addBoolToString(getLight(), kLightStr); + } + result += addLabeledString( + _.OnTimerEnabled ? minsToString(getOnTimer()) : kOffStr, + kOnTimerStr); + result += addLabeledString( + _.OffTimerEnabled ? minsToString(getOffTimer()) : kOffStr, + kOffTimerStr); + break; + case kTcl112AcSpecial: + result += addBoolToString(_.Quiet, kQuietStr); + break; + } + return result; +} + +#if DECODE_TCL112AC +/// @file +/// @note There is no `decodedecodeTcl112Ac()`. +/// It's the same as `decodeMitsubishi112()`. A shared routine is used. +/// You can find it in: ir_Mitsubishi.cpp +#endif // DECODE_TCL112AC diff --git a/lib/IRremoteESP8266/src/ir_Tcl.h b/lib/IRremoteESP8266/src/ir_Tcl.h index 0f075b43b1..e1696c42ad 100644 --- a/lib/IRremoteESP8266/src/ir_Tcl.h +++ b/lib/IRremoteESP8266/src/ir_Tcl.h @@ -15,11 +15,11 @@ #ifndef UNIT_TEST #include #endif -#include "../src/IRremoteESP8266.h" -#include "../src/IRsend.h" -#include "../src/IRrecv.h" +#include "IRremoteESP8266.h" +#include "IRsend.h" +#include "IRrecv.h" #ifdef UNIT_TEST -#include "../src/IRsend_test.h" +#include "IRsend_test.h" #endif /// Native representation of a TCL 112 A/C message. diff --git a/lib/IRremoteESP8266/src/ir_Technibel.cpp b/lib/IRremoteESP8266/src/ir_Technibel.cpp new file mode 100644 index 0000000000..d58cc7e055 --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_Technibel.cpp @@ -0,0 +1,408 @@ +// Copyright 2020 Quentin Briollant + +/// @file +/// @brief Support for Technibel protocol. + +#include "ir_Technibel.h" +#include "IRrecv.h" +#include "IRsend.h" +#include "IRtext.h" +#include "IRutils.h" +#include + +using irutils::addBoolToString; +using irutils::addModeToString; +using irutils::addFanToString; +using irutils::addLabeledString; +using irutils::addTempToString; +using irutils::minsToString; + +const uint16_t kTechnibelAcHdrMark = 8836; +const uint16_t kTechnibelAcHdrSpace = 4380; +const uint16_t kTechnibelAcBitMark = 523; +const uint16_t kTechnibelAcOneSpace = 1696; +const uint16_t kTechnibelAcZeroSpace = 564; +const uint32_t kTechnibelAcGap = kDefaultMessageGap; +const uint16_t kTechnibelAcFreq = 38000; + + +#if SEND_TECHNIBEL_AC +/// Send an Technibel AC formatted message. +/// Status: STABLE / Reported as working on a real device. +/// @param[in] data containing the IR command. +/// @param[in] nbits Nr. of bits to send. usually kTechnibelAcBits +/// @param[in] repeat Nr. of times the message is to be repeated. +void IRsend::sendTechnibelAc(const uint64_t data, const uint16_t nbits, + const uint16_t repeat) { + sendGeneric(kTechnibelAcHdrMark, kTechnibelAcHdrSpace, + kTechnibelAcBitMark, kTechnibelAcOneSpace, + kTechnibelAcBitMark, kTechnibelAcZeroSpace, + kTechnibelAcBitMark, kTechnibelAcGap, + data, nbits, kTechnibelAcFreq, true, // LSB First. + repeat, kDutyDefault); +} +#endif // SEND_TECHNIBEL_AC + +#if DECODE_TECHNIBEL_AC +/// Status: STABLE / Reported as working on a real device +/// @param[in,out] results Ptr to 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 (kTechnibelAcBits). +/// @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::decodeTechnibelAc(decode_results *results, uint16_t offset, + const uint16_t nbits, const bool strict) { + // Compliance + if (strict && nbits != kTechnibelAcBits) { + return false; + } + + uint64_t data = 0; + + // Header + Data + Footer + if (!matchGeneric(results->rawbuf + offset, &data, + results->rawlen - offset, nbits, + kTechnibelAcHdrMark, kTechnibelAcHdrSpace, + kTechnibelAcBitMark, kTechnibelAcOneSpace, + kTechnibelAcBitMark, kTechnibelAcZeroSpace, + kTechnibelAcBitMark, kTechnibelAcGap, true, + _tolerance, kMarkExcess, true)) return false; + + // Compliance + if (strict && !IRTechnibelAc::validChecksum(data)) return false; + + // Success + results->decode_type = decode_type_t::TECHNIBEL_AC; + results->bits = nbits; + results->value = data; + results->command = 0; + results->address = 0; + return true; +} +#endif // DECODE_TECHNIBEL_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? +IRTechnibelAc::IRTechnibelAc(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 IRTechnibelAc::begin(void) { _irsend.begin(); } + +#if SEND_TECHNIBEL_AC +/// Send the current internal state as an IR message. +/// @param[in] repeat Nr. of times the message will be repeated. +void IRTechnibelAc::send(const uint16_t repeat) { + _irsend.sendTechnibelAc(getRaw(), kTechnibelAcBits, repeat); +} +#endif // SEND_TECHNIBEL_AC + +/// Compute the checksum of the supplied state. +/// @param[in] state A valid code for this protocol. +/// @return The calculated checksum of the supplied state. +uint8_t IRTechnibelAc::calcChecksum(const uint64_t state) { + uint8_t sum = 0; + // Add up all the 8 bit data chunks. + for (uint8_t offset = kTechnibelAcTimerHoursOffset; + offset < kTechnibelAcHeaderOffset; offset += 8) + sum += GETBITS64(state, offset, 8); + return ~sum + 1; +} + +/// Confirm the checksum of the supplied state is valid. +/// @param[in] state A valid code for this protocol. +/// @return `true` if the checksum is correct, otherwise `false`. +bool IRTechnibelAc::validChecksum(const uint64_t state) { + TechnibelProtocol p{.raw = state}; + return calcChecksum(state) == p.Sum; +} + +/// Set the checksum of the internal state. +void IRTechnibelAc::checksum(void) { + _.Sum = calcChecksum(_.raw); +} + +/// Reset the internal state of the emulation. +/// @note Mode:Cool, Power:Off, fan:Low, temp:20, swing:Off, sleep:Off +void IRTechnibelAc::stateReset(void) { + _.raw = kTechnibelAcResetState; + _saved_temp = 20; // DegC (Random reasonable default value) + _saved_temp_units = 0; // Celsius +} + +/// Get a copy of the internal state/code for this protocol. +/// @return A code for this protocol based on the current internal state. +uint64_t IRTechnibelAc::getRaw(void) { + checksum(); + return _.raw; +} + +/// Set the internal state from a valid code for this protocol. +/// @param[in] state A valid code for this protocol. +void IRTechnibelAc::setRaw(const uint64_t state) { + _.raw = state; +} + +/// Set the requested power state of the A/C to on. +void IRTechnibelAc::on(void) { setPower(true); } + +/// Set the requested power state of the A/C to off. +void IRTechnibelAc::off(void) { setPower(false); } + +/// Change the power setting. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRTechnibelAc::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 IRTechnibelAc::getPower(void) const { + return _.Power; +} + +/// Set the temperature unit setting. +/// @param[in] fahrenheit true, the unit is °F. false, the unit is °C. +void IRTechnibelAc::setTempUnit(const bool fahrenheit) { + _saved_temp_units = fahrenheit; + _.UseFah = fahrenheit; +} + +/// Get the temperature unit setting. +/// @return true, the unit is °F. false, the unit is °C. +bool IRTechnibelAc::getTempUnit(void) const { + return _.UseFah; +} + +/// Set the temperature. +/// @param[in] degrees The temperature in degrees. +/// @param[in] fahrenheit The temperature unit: true=°F, false(default)=°C. +void IRTechnibelAc::setTemp(const uint8_t degrees, const bool fahrenheit) { + setTempUnit(fahrenheit); + uint8_t temp_min = fahrenheit ? kTechnibelAcTempMinF : kTechnibelAcTempMinC; + uint8_t temp_max = fahrenheit ? kTechnibelAcTempMaxF : kTechnibelAcTempMaxC; + _saved_temp = std::min(temp_max, std::max(temp_min, degrees)); + _.Temp = _saved_temp; +} + +/// Get the current temperature setting. +/// @return The current setting for temp. in degrees. +uint8_t IRTechnibelAc::getTemp(void) const { + return _.Temp; +} + +/// Set the speed of the fan. +/// @param[in] speed The desired setting. +void IRTechnibelAc::setFan(const uint8_t speed) { + // Mode fan speed rules. + if (_.Mode == kTechnibelAcDry && speed != kTechnibelAcFanLow) { + _.Fan = kTechnibelAcFanLow; + return; + } + switch (speed) { + case kTechnibelAcFanHigh: + case kTechnibelAcFanMedium: + case kTechnibelAcFanLow: + _.Fan = speed; + break; + default: + _.Fan = kTechnibelAcFanLow; + } +} + +/// Get the current fan speed setting. +/// @return The current fan speed/mode. +uint8_t IRTechnibelAc::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 IRTechnibelAc::convertFan(const stdAc::fanspeed_t speed) { + switch (speed) { + case stdAc::fanspeed_t::kMin: + case stdAc::fanspeed_t::kLow: return kTechnibelAcFanLow; + case stdAc::fanspeed_t::kMedium: return kTechnibelAcFanMedium; + case stdAc::fanspeed_t::kHigh: + case stdAc::fanspeed_t::kMax: return kTechnibelAcFanHigh; + default: return kTechnibelAcFanLow; + } +} + +/// 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 IRTechnibelAc::toCommonFanSpeed(const uint8_t speed) { + switch (speed) { + case kTechnibelAcFanHigh: return stdAc::fanspeed_t::kHigh; + case kTechnibelAcFanMedium: return stdAc::fanspeed_t::kMedium; + default: return stdAc::fanspeed_t::kLow; + } +} + +/// Get the operating mode setting of the A/C. +/// @return The current operating mode setting. +uint8_t IRTechnibelAc::getMode(void) const { + return _.Mode; +} + +/// Set the operating mode of the A/C. +/// @param[in] mode The desired operating mode. +void IRTechnibelAc::setMode(const uint8_t mode) { + _.Mode = mode; + switch (mode) { + case kTechnibelAcHeat: + case kTechnibelAcFan: + case kTechnibelAcDry: + case kTechnibelAcCool: + break; + default: + _.Mode = kTechnibelAcCool; + } + setFan(_.Fan); // Re-force any fan speed constraints. + // Restore previous temp settings for cool mode. + setTemp(_saved_temp, _saved_temp_units); +} + +/// 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 IRTechnibelAc::convertMode(const stdAc::opmode_t mode) { + switch (mode) { + case stdAc::opmode_t::kHeat: return kTechnibelAcHeat; + case stdAc::opmode_t::kDry: return kTechnibelAcDry; + case stdAc::opmode_t::kFan: return kTechnibelAcFan; + default: return kTechnibelAcCool; + } +} + +/// 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 IRTechnibelAc::toCommonMode(const uint8_t mode) { + switch (mode) { + case kTechnibelAcHeat: return stdAc::opmode_t::kHeat; + case kTechnibelAcDry: return stdAc::opmode_t::kDry; + case kTechnibelAcFan: return stdAc::opmode_t::kFan; + default: return stdAc::opmode_t::kCool; + } +} + +/// Set the (vertical) swing setting of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRTechnibelAc::setSwing(const bool on) { + _.Swing = on; +} + +/// Get the (vertical) swing setting of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRTechnibelAc::getSwing(void) const { + return _.Swing; +} + +/// Convert a stdAc::swingv_t enum into it's native swing. +/// @param[in] swing The enum to be converted. +/// @return true, the swing is on. false, the swing is off. +bool IRTechnibelAc::convertSwing(const stdAc::swingv_t swing) { + return swing != stdAc::swingv_t::kOff; +} + +/// Convert a native swing into its stdAc equivalent. +/// @param[in] swing true, the swing is on. false, the swing is off. +/// @return The stdAc equivalent of the native setting. +stdAc::swingv_t IRTechnibelAc::toCommonSwing(const bool swing) { + return swing ? stdAc::swingv_t::kAuto : stdAc::swingv_t::kOff; +} + +/// Set the Sleep setting of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRTechnibelAc::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 IRTechnibelAc::getSleep(void) const { + return _.Sleep; +} + +/// Set the enable timer setting. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRTechnibelAc::setTimerEnabled(const bool on) { + _.TimerEnable = on; +} + +/// Is the timer function enabled? +/// @return true, the setting is on. false, the setting is off. +bool IRTechnibelAc::getTimerEnabled(void) const { + return _.TimerEnable; +} + +/// Set the timer for when the A/C unit will switch off. +/// @param[in] nr_of_mins Number of minutes before power off. +/// `0` will clear the timer. Max is 24 hrs (1440 mins). +/// @note Time is stored internally in hours. +void IRTechnibelAc::setTimer(const uint16_t nr_of_mins) { + const uint8_t hours = nr_of_mins / 60; + _.TimerHours = std::min(kTechnibelAcTimerMax, hours); + // Enable or not? + setTimerEnabled(hours); +} + +/// Get the timer time for when the A/C unit will switch power state. +/// @return The number of minutes left on the timer. `0` means off. +uint16_t IRTechnibelAc::getTimer(void) const { + return _.TimerEnable ? _.TimerHours * 60 : 0; +} + +/// Convert the current internal state into its stdAc::state_t equivalent. +/// @return The stdAc equivalent of the native settings. +stdAc::state_t IRTechnibelAc::toCommon(void) const { + stdAc::state_t result; + result.protocol = decode_type_t::TECHNIBEL_AC; + result.power = _.Power; + result.mode = toCommonMode(_.Mode); + result.celsius = !_.UseFah; + result.degrees = _.Temp; + result.fanspeed = toCommonFanSpeed(_.Fan); + result.sleep = _.Sleep ? 0 : -1; + result.swingv = toCommonSwing(_.Swing); + // Not supported. + result.model = -1; + result.turbo = false; + 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 IRTechnibelAc::toString(void) const { + String result = ""; + result.reserve(100); // Reserve some heap for the string to reduce fragging. + result += addBoolToString(_.Power, kPowerStr, false); + result += addModeToString(_.Mode, 255, // No Auto, so use impossible value + kTechnibelAcCool, kTechnibelAcHeat, kTechnibelAcDry, + kTechnibelAcFan); + result += addFanToString(_.Fan, kTechnibelAcFanHigh, kTechnibelAcFanLow, + kTechnibelAcFanLow, kTechnibelAcFanLow, + kTechnibelAcFanMedium); + result += addTempToString(_.Temp, !_.UseFah); + result += addBoolToString(_.Sleep, kSleepStr); + result += addBoolToString(_.Swing, kSwingVStr); + result += addLabeledString(_.TimerEnable ? minsToString(getTimer()) + : kOffStr, + kTimerStr); + return result; +} diff --git a/lib/IRremoteESP8266/src/ir_Technibel.h b/lib/IRremoteESP8266/src/ir_Technibel.h index decbee4e87..fb4a08717c 100644 --- a/lib/IRremoteESP8266/src/ir_Technibel.h +++ b/lib/IRremoteESP8266/src/ir_Technibel.h @@ -11,10 +11,10 @@ #ifndef UNIT_TEST #include #endif -#include "../src/IRremoteESP8266.h" -#include "../src/IRsend.h" +#include "IRremoteESP8266.h" +#include "IRsend.h" #ifdef UNIT_TEST -#include "../src/IRsend_test.h" +#include "IRsend_test.h" #endif // Supports: diff --git a/lib/IRremoteESP8266/src/ir_Teco.cpp b/lib/IRremoteESP8266/src/ir_Teco.cpp new file mode 100644 index 0000000000..031691eddd --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_Teco.cpp @@ -0,0 +1,375 @@ +// Copyright 2019 Fabien Valthier + +/// @file +/// @brief Support for Teco protocols. + +#include "ir_Teco.h" +#include +#include "IRremoteESP8266.h" +#include "IRtext.h" +#include "IRutils.h" +#ifndef ARDUINO +#include +#endif + +// Constants +// using SPACE modulation. +const uint16_t kTecoHdrMark = 9000; +const uint16_t kTecoHdrSpace = 4440; +const uint16_t kTecoBitMark = 620; +const uint16_t kTecoOneSpace = 1650; +const uint16_t kTecoZeroSpace = 580; +const uint32_t kTecoGap = kDefaultMessageGap; // Made-up value. Just a guess. + +using irutils::addBoolToString; +using irutils::addFanToString; +using irutils::addIntToString; +using irutils::addLabeledString; +using irutils::addModeToString; +using irutils::addTempToString; + +#if SEND_TECO +/// Send a Teco A/C 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::sendTeco(const uint64_t data, const uint16_t nbits, + const uint16_t repeat) { + sendGeneric(kTecoHdrMark, kTecoHdrSpace, kTecoBitMark, kTecoOneSpace, + kTecoBitMark, kTecoZeroSpace, kTecoBitMark, kTecoGap, + data, nbits, 38000, false, repeat, kDutyDefault); +} +#endif // SEND_TECO + +/// 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? +IRTecoAc::IRTecoAc(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 IRTecoAc::begin(void) { _irsend.begin(); } + +#if SEND_TECO +/// Send the current internal state as an IR message. +/// @param[in] repeat Nr. of times the message will be repeated. +void IRTecoAc::send(const uint16_t repeat) { + _irsend.sendTeco(_.raw, kTecoBits, repeat); +} +#endif // SEND_TECO + +/// Reset the internal state of the emulation. +/// @note Mode:auto, Power:Off, fan:auto, temp:16, swing:off, sleep:off +void IRTecoAc::stateReset(void) { + _.raw = kTecoReset; +} + +/// Get a copy of the internal state/code for this protocol. +/// @return A code for this protocol based on the current internal state. +uint64_t IRTecoAc::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 IRTecoAc::setRaw(const uint64_t new_code) { _.raw = new_code; } + +/// Set the requested power state of the A/C to on. +void IRTecoAc::on(void) { setPower(true); } + +/// Set the requested power state of the A/C to off. +void IRTecoAc::off(void) { setPower(false); } + +/// Change the power setting. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRTecoAc::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 IRTecoAc::getPower(void) const { + return _.Power; +} + +/// Set the temperature. +/// @param[in] temp The temperature in degrees celsius. +void IRTecoAc::setTemp(const uint8_t temp) { + uint8_t newtemp = temp; + newtemp = std::min(newtemp, kTecoMaxTemp); + newtemp = std::max(newtemp, kTecoMinTemp); + _.Temp = newtemp - kTecoMinTemp; +} + +/// Get the current temperature setting. +/// @return The current setting for temp. in degrees celsius. +uint8_t IRTecoAc::getTemp(void) const { + return _.Temp + kTecoMinTemp; +} + +/// Set the speed of the fan. +/// @param[in] speed The desired setting. +void IRTecoAc::setFan(const uint8_t speed) { + uint8_t newspeed = speed; + switch (speed) { + case kTecoFanAuto: + case kTecoFanHigh: + case kTecoFanMed: + case kTecoFanLow: break; + default: newspeed = kTecoFanAuto; + } + _.Fan = newspeed; +} + +/// Get the current fan speed setting. +/// @return The current fan speed/mode. +uint8_t IRTecoAc::getFan(void) const { + return _.Fan; +} + +/// Set the operating mode of the A/C. +/// @param[in] mode The desired operating mode. +void IRTecoAc::setMode(const uint8_t mode) { + uint8_t newmode = mode; + switch (mode) { + case kTecoAuto: + case kTecoCool: + case kTecoDry: + case kTecoFan: + case kTecoHeat: break; + default: newmode = kTecoAuto; + } + _.Mode = newmode; +} + +/// Get the operating mode setting of the A/C. +/// @return The current operating mode setting. +uint8_t IRTecoAc::getMode(void) const { + return _.Mode; +} + +/// Set the (vertical) swing setting of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRTecoAc::setSwing(const bool on) { + _.Swing = on; +} + +/// Get the (vertical) swing setting of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRTecoAc::getSwing(void) const { + return _.Swing; +} + +/// Set the Sleep setting of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRTecoAc::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 IRTecoAc::getSleep(void) const { + return _.Sleep; +} + +/// Set the Light (LED/Display) setting of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRTecoAc::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 IRTecoAc::getLight(void) const { + return _.Light; +} + +/// Set the Humid setting of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRTecoAc::setHumid(const bool on) { + _.Humid = on; +} + +/// Get the Humid setting of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRTecoAc::getHumid(void) const { + return _.Humid; +} + +/// Set the Save setting of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRTecoAc::setSave(const bool on) { + _.Save = on; +} + +/// Get the Save setting of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRTecoAc::getSave(void) const { + return _.Save; +} + +/// Is the timer function enabled? +/// @return true, the setting is on. false, the setting is off. +inline bool IRTecoAc::getTimerEnabled(void) const { + return _.TimerOn; +} + +/// Get the timer time for when the A/C unit will switch power state. +/// @return The number of minutes left on the timer. `0` means off. +uint16_t IRTecoAc::getTimer(void) const { + uint16_t mins = 0; + if (getTimerEnabled()) { + mins = (_.TensHours * 10 + _.UnitHours) * 60; + if (_.HalfHour) mins += 30; + } + return mins; +} + +/// Set the timer for when the A/C unit will switch power state. +/// @param[in] nr_mins Number of minutes before power state change. +/// `0` will clear the timer. Max is 24 hrs. +/// @note Time is stored internally in increments of 30 mins. +void IRTecoAc::setTimer(const uint16_t nr_mins) { + uint16_t mins = std::min(nr_mins, (uint16_t)(24 * 60)); // Limit to 24 hrs. + uint8_t hours = mins / 60; + _.TimerOn = mins > 0; // Set the timer flag. + _.HalfHour = (mins % 60) >= 30; + _.UnitHours = hours % 10; + _.TensHours = hours / 10; +} + +/// 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 IRTecoAc::convertMode(const stdAc::opmode_t mode) { + switch (mode) { + case stdAc::opmode_t::kCool: return kTecoCool; + case stdAc::opmode_t::kHeat: return kTecoHeat; + case stdAc::opmode_t::kDry: return kTecoDry; + case stdAc::opmode_t::kFan: return kTecoFan; + default: return kTecoAuto; + } +} + +/// 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 IRTecoAc::convertFan(const stdAc::fanspeed_t speed) { + switch (speed) { + case stdAc::fanspeed_t::kMin: + case stdAc::fanspeed_t::kLow: return kTecoFanLow; + case stdAc::fanspeed_t::kMedium: return kTecoFanMed; + case stdAc::fanspeed_t::kHigh: + case stdAc::fanspeed_t::kMax: return kTecoFanHigh; + default: return kTecoFanAuto; + } +} + +/// 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 IRTecoAc::toCommonMode(const uint8_t mode) { + switch (mode) { + case kTecoCool: return stdAc::opmode_t::kCool; + case kTecoHeat: return stdAc::opmode_t::kHeat; + case kTecoDry: return stdAc::opmode_t::kDry; + case kTecoFan: 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 IRTecoAc::toCommonFanSpeed(const uint8_t speed) { + switch (speed) { + case kTecoFanHigh: return stdAc::fanspeed_t::kMax; + case kTecoFanMed: return stdAc::fanspeed_t::kMedium; + case kTecoFanLow: 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 IRTecoAc::toCommon(void) const { + stdAc::state_t result; + result.protocol = decode_type_t::TECO; + result.model = -1; // Not supported. + result.power = _.Power; + result.mode = toCommonMode(_.Mode); + result.celsius = true; + result.degrees = getTemp(); + result.fanspeed = toCommonFanSpeed(_.Fan); + result.swingv = _.Swing ? stdAc::swingv_t::kAuto : stdAc::swingv_t::kOff; + result.sleep = _.Sleep ? 0 : -1; + result.light = _.Light; + // Not supported. + result.swingh = stdAc::swingh_t::kOff; + result.turbo = 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 IRTecoAc::toString(void) const { + String result = ""; + result.reserve(100); // Reserve some heap for the string to reduce fragging. + result += addBoolToString(_.Power, kPowerStr, false); + result += addModeToString(_.Mode, kTecoAuto, kTecoCool, kTecoHeat, + kTecoDry, kTecoFan); + result += addTempToString(getTemp()); + result += addFanToString(_.Fan, kTecoFanHigh, kTecoFanLow, + kTecoFanAuto, kTecoFanAuto, kTecoFanMed); + result += addBoolToString(_.Sleep, kSleepStr); + result += addBoolToString(_.Swing, kSwingStr); + result += addBoolToString(_.Light, kLightStr); + result += addBoolToString(_.Humid, kHumidStr); + result += addBoolToString(_.Save, kSaveStr); + if (getTimerEnabled()) + result += addLabeledString(irutils::minsToString(getTimer()), + kTimerStr); + else + result += addBoolToString(false, kTimerStr); + return result; +} + +#if DECODE_TECO +/// Decode the supplied Teco message. +/// Status: STABLE / Tested. +/// @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::decodeTeco(decode_results* results, uint16_t offset, + const uint16_t nbits, const bool strict) { + if (strict && nbits != kTecoBits) return false; // Not what is expected + + uint64_t data = 0; + // Match Header + Data + Footer + if (!matchGeneric(results->rawbuf + offset, &data, + results->rawlen - offset, nbits, + kTecoHdrMark, kTecoHdrSpace, + kTecoBitMark, kTecoOneSpace, + kTecoBitMark, kTecoZeroSpace, + kTecoBitMark, kTecoGap, true, + _tolerance, kMarkExcess, false)) return false; + + // Success + results->decode_type = TECO; + results->bits = nbits; + results->value = data; + results->address = 0; + results->command = 0; + return true; +} +#endif // DECODE_TECO diff --git a/lib/IRremoteESP8266/src/ir_Teco.h b/lib/IRremoteESP8266/src/ir_Teco.h index 0ce034a9ea..c2ea1c561e 100644 --- a/lib/IRremoteESP8266/src/ir_Teco.h +++ b/lib/IRremoteESP8266/src/ir_Teco.h @@ -13,10 +13,10 @@ #ifndef UNIT_TEST #include #endif -#include "../src/IRremoteESP8266.h" -#include "../src/IRsend.h" +#include "IRremoteESP8266.h" +#include "IRsend.h" #ifdef UNIT_TEST -#include "../src/IRsend_test.h" +#include "IRsend_test.h" #endif /// Native representation of a Teco A/C message. diff --git a/lib/IRremoteESP8266/src/ir_Teknopoint.cpp b/lib/IRremoteESP8266/src/ir_Teknopoint.cpp new file mode 100644 index 0000000000..0feee6d704 --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_Teknopoint.cpp @@ -0,0 +1,75 @@ +// Copyright 2021 David Conran (crankyoldgit) +/// @file +/// @brief Support for the Teknopoint protocol +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1486 + +// Supports: +// Brand: Teknopoint, Model: Allegro SSA-09H A/C +// Brand: Teknopoint, Model: GZ-055B-E1 remote + +#include "IRrecv.h" +#include "IRsend.h" +#include "IRutils.h" + +// Protocol timings +const uint16_t kTeknopointHdrMark = 3600; +const uint16_t kTeknopointBitMark = 477; +const uint16_t kTeknopointHdrSpace = 1600; +const uint16_t kTeknopointOneSpace = 1200; +const uint16_t kTeknopointZeroSpace = 530; +const uint16_t kTeknopointFreq = 38000; // Hz. (Guess Only) +const uint8_t kTeknopointExtraTol = 10; // Extra tolerance percentage. + +#if SEND_TEKNOPOINT +/// Send a Teknopoint formatted message. +/// Status: BETA / Probably works. +/// @param[in] data An array of bytes containing the IR command. +/// @param[in] nbytes Nr. of bytes of data in the array. +/// @param[in] repeat Nr. of times the message is to be repeated. +void IRsend::sendTeknopoint(const uint8_t data[], const uint16_t nbytes, + const uint16_t repeat) { + sendGeneric(kTeknopointHdrMark, kTeknopointHdrSpace, + kTeknopointBitMark, kTeknopointOneSpace, + kTeknopointBitMark, kTeknopointZeroSpace, + kTeknopointBitMark, kDefaultMessageGap, + data, nbytes, // Bytes + kTeknopointFreq, false, repeat, kDutyDefault); +} +#endif // SEND_TEKNOPOINT + +#if DECODE_TEKNOPOINT +/// Decode the supplied Teknopoint message. +/// Status: Alpha / 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::decodeTeknopoint(decode_results *results, uint16_t offset, + const uint16_t nbits, const bool strict) { + if (results->rawlen < 2 * nbits + kHeader + kFooter - 1 - offset) + return false; // Too short a message to match. + if (strict && nbits != kTeknopointBits) + return false; + + if (!matchGeneric(results->rawbuf + offset, results->state, + results->rawlen - offset, nbits, + kTeknopointHdrMark, kTeknopointHdrSpace, + kTeknopointBitMark, kTeknopointOneSpace, + kTeknopointBitMark, kTeknopointZeroSpace, + kTeknopointBitMark, kDefaultMessageGap, + true, _tolerance + kTeknopointExtraTol, + kMarkExcess, false)) return false; + // Compliance + if (strict) { + // Is the checksum valid? + if (sumBytes(results->state, kTeknopointStateLength - 1) != + results->state[kTeknopointStateLength - 1]) return false; + } + // Success + results->decode_type = decode_type_t::TEKNOPOINT; + results->bits = nbits; + return true; +} +#endif // DECODE_TEKNOPOINT diff --git a/lib/IRremoteESP8266/src/ir_Toshiba.cpp b/lib/IRremoteESP8266/src/ir_Toshiba.cpp new file mode 100644 index 0000000000..0e3ac8ba27 --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_Toshiba.cpp @@ -0,0 +1,529 @@ +// Copyright 2017-2021 David Conran + +/// @file +/// @brief Support for Toshiba protocols. +/// @see https://github.com/r45635/HVAC-IR-Control +/// @see https://github.com/r45635/HVAC-IR-Control/blob/master/HVAC_ESP8266/HVAC_ESP8266T.ino#L77 +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1205 +/// @see https://www.toshiba-carrier.co.jp/global/about/index.htm +/// @see http://www.toshiba-carrier.co.th/AboutUs/Pages/CompanyProfile.aspx + +#include "ir_Toshiba.h" +#include +#include +#ifndef ARDUINO +#include +#endif +#include "IRrecv.h" +#include "IRsend.h" +#include "IRtext.h" +#include "IRutils.h" + +// Constants + +// Toshiba A/C +const uint16_t kToshibaAcHdrMark = 4400; +const uint16_t kToshibaAcHdrSpace = 4300; +const uint16_t kToshibaAcBitMark = 580; +const uint16_t kToshibaAcOneSpace = 1600; +const uint16_t kToshibaAcZeroSpace = 490; +// Some models have a different inter-message gap. +// See: https://github.com/crankyoldgit/IRremoteESP8266/issues/1420 +const uint16_t kToshibaAcMinGap = 4600; // WH-UB03NJ remote +const uint16_t kToshibaAcUsualGap = 7400; // Others + +using irutils::addBoolToString; +using irutils::addFanToString; +using irutils::addIntToString; +using irutils::addLabeledString; +using irutils::addModeToString; +using irutils::addTempToString; +using irutils::checkInvertedBytePairs; +using irutils::invertBytePairs; + +#if SEND_TOSHIBA_AC +/// Send a Toshiba A/C 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::sendToshibaAC(const uint8_t data[], const uint16_t nbytes, + const uint16_t repeat) { + sendGeneric(kToshibaAcHdrMark, kToshibaAcHdrSpace, kToshibaAcBitMark, + kToshibaAcOneSpace, kToshibaAcBitMark, kToshibaAcZeroSpace, + kToshibaAcBitMark, kToshibaAcUsualGap, data, nbytes, 38, true, + repeat, 50); +} +#endif // SEND_TOSHIBA_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? +IRToshibaAC::IRToshibaAC(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. +/// @see https://github.com/r45635/HVAC-IR-Control/blob/master/HVAC_ESP8266/HVAC_ESP8266T.ino#L103 +void IRToshibaAC::stateReset(void) { + static const uint8_t kReset[kToshibaACStateLength] = { + 0xF2, 0x0D, 0x03, 0xFC, 0x01}; + memcpy(_.raw, kReset, kToshibaACStateLength); + setTemp(22); // Remote defaults to 22C after factory reset. So do the same. + setSwing(kToshibaAcSwingOff); + _prev_mode = getMode(); +} + +/// Set up hardware to be able to send a message. +void IRToshibaAC::begin(void) { _irsend.begin(); } + +#if SEND_TOSHIBA_AC +/// Send the current internal state as IR messages. +/// @param[in] repeat Nr. of times the message will be repeated. +void IRToshibaAC::send(const uint16_t repeat) { + _backupState(); + _irsend.sendToshibaAC(getRaw(), getStateLength(), repeat); + if (_send_swing && (getStateLength() != kToshibaACStateLengthShort)) { + setStateLength(kToshibaACStateLengthShort); + // Swing settings expect the min temp to be set. + // Ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/1205#issuecomment-653922374 + setTemp(kToshibaAcMinTemp); + setSwing(_swing_mode); + _irsend.sendToshibaAC(getRaw(), getStateLength(), repeat); + _restoreState(); + } + _send_swing = false; +} +#endif // SEND_TOSHIBA_AC + +/// Get the length of the supplied Toshiba state per it's protocol structure. +/// @param[in] state The array to get the built-in length from. +/// @param[in] size The physical size of the state array. +/// @return Nr. of bytes in use for the provided state message. +uint16_t IRToshibaAC::getInternalStateLength(const uint8_t state[], + const uint16_t size) { + if (size < kToshibaAcLengthByte) return 0; + return std::min((uint16_t)(state[kToshibaAcLengthByte] + kToshibaAcMinLength), + kToshibaACStateLengthLong); +} + +/// Get the length of the current internal state per the protocol structure. +/// @return Nr. of bytes in use for the current internal state message. +uint16_t IRToshibaAC::getStateLength(void) const { + return getInternalStateLength(_.raw, kToshibaACStateLengthLong); +} + +/// Set the internal length of the current internal state per the protocol. +/// @param[in] size Nr. of bytes in use for the current internal state message. +void IRToshibaAC::setStateLength(const uint16_t size) { + if (size < kToshibaAcMinLength) return; + _.Length = size - kToshibaAcMinLength; +} + +/// Make a copy of the internal code-form A/C state. +void IRToshibaAC::_backupState(void) { + memcpy(backup, _.raw, kToshibaACStateLengthLong); +} + +/// Recover the internal code-form A/C state from the backup. +void IRToshibaAC::_restoreState(void) { + memcpy(_.raw, backup, kToshibaACStateLengthLong); +} + +/// Get a PTR to the internal state/code for this protocol with all integrity +/// checks passing. +/// @return PTR to a code for this protocol based on the current internal state. +uint8_t* IRToshibaAC::getRaw(void) { + checksum(getStateLength()); + return _.raw; +} + +/// Set the internal state from a valid code for this protocol. +/// @param[in] newState A valid code for this protocol. +/// @param[in] length The length/size of the array. +void IRToshibaAC::setRaw(const uint8_t newState[], const uint16_t length) { + memcpy(_.raw, newState, length); + _prev_mode = getMode(); + _send_swing = true; +} + +/// 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 IRToshibaAC::calcChecksum(const uint8_t state[], + const uint16_t length) { + return length ? xorBytes(state, length - 1) : 0; +} + +/// 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 IRToshibaAC::validChecksum(const uint8_t state[], const uint16_t length) { + return length >= kToshibaAcMinLength && + state[length - 1] == IRToshibaAC::calcChecksum(state, length) && + checkInvertedBytePairs(state, kToshibaAcInvertedLength) && + IRToshibaAC::getInternalStateLength(state, length) == length; +} + +/// Calculate & set the checksum for the current internal state of the remote. +/// @param[in] length The length/size of the internal array to checksum. +void IRToshibaAC::checksum(const uint16_t length) { + // Stored the checksum value in the last byte. + if (length >= kToshibaAcMinLength) { + // Set/clear the short msg bit. + _.ShortMsg = (getStateLength() == kToshibaACStateLengthShort); + // Set/clear the long msg bit. + _.LongMsg = (getStateLength() == kToshibaACStateLengthLong); + invertBytePairs(_.raw, kToshibaAcInvertedLength); + // Always do the Xor checksum LAST! + _.raw[length - 1] = calcChecksum(_.raw, length); + } +} + +/// Set the requested power state of the A/C to on. +void IRToshibaAC::on(void) { setPower(true); } + +/// Set the requested power state of the A/C to off. +void IRToshibaAC::off(void) { setPower(false); } + +/// Change the power setting. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRToshibaAC::setPower(const bool on) { + if (on) { // On + // If not already on, pick the last non-off mode used + if (!getPower()) setMode(_prev_mode); + } else { // Off + setMode(kToshibaAcOff); + } +} + +/// Get the value of the current power setting. +/// @return true, the setting is on. false, the setting is off. +bool IRToshibaAC::getPower(void) const { + return getMode(true) != kToshibaAcOff; +} + +/// Set the temperature. +/// @param[in] degrees The temperature in degrees celsius. +void IRToshibaAC::setTemp(const uint8_t degrees) { + uint8_t temp = std::max(kToshibaAcMinTemp, degrees); + temp = std::min(kToshibaAcMaxTemp, temp); + _.Temp = temp - kToshibaAcMinTemp; +} + +/// Get the current temperature setting. +/// @return The current setting for temp. in degrees celsius. +uint8_t IRToshibaAC::getTemp(void) const { + return _.Temp + kToshibaAcMinTemp; +} + +/// Set the speed of the fan. +/// @param[in] speed The desired setting (0 is Auto, 1-5 is the speed, 5 is Max) +void IRToshibaAC::setFan(const uint8_t speed) { + uint8_t fan = speed; + // Bounds check + if (fan > kToshibaAcFanMax) + fan = kToshibaAcFanMax; // Set the fan to maximum if out of range. + if (fan > kToshibaAcFanAuto) fan++; + _.Fan = fan; +} + +/// Get the current fan speed setting. +/// @return The current fan speed/mode. +uint8_t IRToshibaAC::getFan(void) const { + uint8_t fan = _.Fan; + if (fan == kToshibaAcFanAuto) return kToshibaAcFanAuto; + return --fan; +} + +/// Get the swing setting of the A/C. +/// @param[in] raw Calculate the answer from just the state data. +/// @return The current swing mode setting. +uint8_t IRToshibaAC::getSwing(const bool raw) const { + return raw ? _.Swing : _swing_mode; +} + +/// Set the swing setting of the A/C. +/// @param[in] setting The value of the desired setting. +void IRToshibaAC::setSwing(const uint8_t setting) { + switch (setting) { + case kToshibaAcSwingStep: + case kToshibaAcSwingOn: + case kToshibaAcSwingOff: + case kToshibaAcSwingToggle: + _send_swing = true; + _swing_mode = setting; + if (getStateLength() == kToshibaACStateLengthShort) + _.Swing = setting; + } +} + +/// Get the operating mode setting of the A/C. +/// @param[in] raw Get the value without any intelligent processing. +/// @return The current operating mode setting. +uint8_t IRToshibaAC::getMode(const bool raw) const { + const uint8_t mode = _.Mode; + if (raw) return mode; + switch (mode) { + case kToshibaAcOff: return _prev_mode; + default: return mode; + } +} + +/// Set the operating mode of the A/C. +/// @param[in] mode The desired operating mode. +/// @note If we get an unexpected mode, default to AUTO. +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1205#issuecomment-654446771 +void IRToshibaAC::setMode(const uint8_t mode) { + if (mode != _prev_mode) + // Changing mode or power turns Econo & Turbo to off on a real remote. + // Setting the internal message length to "normal" will do that. + setStateLength(kToshibaACStateLength); + switch (mode) { + case kToshibaAcAuto: + case kToshibaAcCool: + case kToshibaAcDry: + case kToshibaAcHeat: + case kToshibaAcFan: + _prev_mode = mode; + // FALL-THRU + case kToshibaAcOff: + _.Mode = mode; + break; + default: + _prev_mode = kToshibaAcAuto; + _.Mode = kToshibaAcAuto; + } +} + +/// Get the Turbo (Powerful) setting of the A/C. +/// @return true, if the current setting is on. Otherwise, false. +bool IRToshibaAC::getTurbo(void) const { + if (getStateLength() == kToshibaACStateLengthLong) + return _.EcoTurbo == kToshibaAcTurboOn; + return false; +} + +/// Set the Turbo (Powerful) setting of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +/// Note: Turbo mode is mutually exclusive with Economy mode. +void IRToshibaAC::setTurbo(const bool on) { + if (on) { + _.EcoTurbo = kToshibaAcTurboOn; + setStateLength(kToshibaACStateLengthLong); + } else { + if (!getEcono()) setStateLength(kToshibaACStateLength); + } +} + +/// Get the Economy mode setting of the A/C. +/// @return true, if the current setting is on. Otherwise, false. +bool IRToshibaAC::getEcono(void) const { + if (getStateLength() == kToshibaACStateLengthLong) + return _.EcoTurbo == kToshibaAcEconoOn; + return false; +} + +/// Set the Economy mode setting of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +/// Note: Economy mode is mutually exclusive with Turbo mode. +void IRToshibaAC::setEcono(const bool on) { + if (on) { + _.EcoTurbo = kToshibaAcEconoOn; + setStateLength(kToshibaACStateLengthLong); + } else { + if (!getTurbo()) setStateLength(kToshibaACStateLength); + } +} + +/// 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 IRToshibaAC::convertMode(const stdAc::opmode_t mode) { + switch (mode) { + case stdAc::opmode_t::kCool: return kToshibaAcCool; + case stdAc::opmode_t::kHeat: return kToshibaAcHeat; + case stdAc::opmode_t::kDry: return kToshibaAcDry; + case stdAc::opmode_t::kFan: return kToshibaAcFan; + case stdAc::opmode_t::kOff: return kToshibaAcOff; + default: return kToshibaAcAuto; + } +} + +/// 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 IRToshibaAC::convertFan(const stdAc::fanspeed_t speed) { + switch (speed) { + case stdAc::fanspeed_t::kMin: return kToshibaAcFanMax - 4; + case stdAc::fanspeed_t::kLow: return kToshibaAcFanMax - 3; + case stdAc::fanspeed_t::kMedium: return kToshibaAcFanMax - 2; + case stdAc::fanspeed_t::kHigh: return kToshibaAcFanMax - 1; + case stdAc::fanspeed_t::kMax: return kToshibaAcFanMax; + default: return kToshibaAcFanAuto; + } +} + +/// 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 IRToshibaAC::toCommonMode(const uint8_t mode) { + switch (mode) { + case kToshibaAcCool: return stdAc::opmode_t::kCool; + case kToshibaAcHeat: return stdAc::opmode_t::kHeat; + case kToshibaAcDry: return stdAc::opmode_t::kDry; + case kToshibaAcFan: return stdAc::opmode_t::kFan; + case kToshibaAcOff: return stdAc::opmode_t::kOff; + default: return stdAc::opmode_t::kAuto; + } +} + +/// Convert a native fan speed into its stdAc equivalent. +/// @param[in] spd The native setting to be converted. +/// @return The stdAc equivalent of the native setting. +stdAc::fanspeed_t IRToshibaAC::toCommonFanSpeed(const uint8_t spd) { + switch (spd) { + case kToshibaAcFanMax: return stdAc::fanspeed_t::kMax; + case kToshibaAcFanMax - 1: return stdAc::fanspeed_t::kHigh; + case kToshibaAcFanMax - 2: return stdAc::fanspeed_t::kMedium; + case kToshibaAcFanMax - 3: return stdAc::fanspeed_t::kLow; + case kToshibaAcFanMax - 4: 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 IRToshibaAC::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.protocol = decode_type_t::TOSHIBA_AC; + result.model = -1; // Not supported. + // Do we have enough current state info to override any previous state? + // i.e. Was the class just setRaw()'ed with a short "swing" message. + // This should enables us to also ignore the Swing msg's special 17C setting. + if (getStateLength() != kToshibaACStateLengthShort) { + result.power = getPower(); + result.mode = toCommonMode(getMode()); + result.celsius = true; + result.degrees = getTemp(); + result.fanspeed = toCommonFanSpeed(getFan()); + result.turbo = getTurbo(); + result.econo = getEcono(); + } + switch (getSwing()) { + case kToshibaAcSwingOn: + result.swingv = stdAc::swingv_t::kAuto; + break; + case kToshibaAcSwingToggle: + if (prev->swingv != stdAc::swingv_t::kOff) + result.swingv = stdAc::swingv_t::kOff; + else + result.swingv = stdAc::swingv_t::kAuto; + break; + default: result.swingv = stdAc::swingv_t::kOff; + } + // Not supported. + result.light = false; + result.filter = false; + result.swingh = stdAc::swingh_t::kOff; + 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 IRToshibaAC::toString(void) const { + String result = ""; + result.reserve(80); + result += addTempToString(getTemp(), true, false); + switch (getStateLength()) { + case kToshibaACStateLengthShort: + result += addIntToString(getSwing(true), kSwingVStr); + result += kSpaceLBraceStr; + switch (getSwing(true)) { + case kToshibaAcSwingOff: result += kOffStr; break; + case kToshibaAcSwingOn: result += kOnStr; break; + case kToshibaAcSwingStep: result += kStepStr; break; + case kToshibaAcSwingToggle: result += kToggleStr; break; + default: result += kUnknownStr; + } + result += ')'; + break; + case kToshibaACStateLengthLong: + case kToshibaACStateLength: + default: + result += addBoolToString(getPower(), kPowerStr); + if (getPower()) + result += addModeToString(getMode(), kToshibaAcAuto, kToshibaAcCool, + kToshibaAcHeat, kToshibaAcDry, kToshibaAcFan); + result += addFanToString(getFan(), kToshibaAcFanMax, kToshibaAcFanMin, + kToshibaAcFanAuto, kToshibaAcFanAuto, + kToshibaAcFanMed); + result += addBoolToString(getTurbo(), kTurboStr); + result += addBoolToString(getEcono(), kEconoStr); + } + return result; +} + +#if DECODE_TOSHIBA_AC +/// Decode the supplied Toshiba A/C 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::decodeToshibaAC(decode_results* results, uint16_t offset, + const uint16_t nbits, const bool strict) { + // Compliance + if (strict) { + switch (nbits) { // Must be called with the correct nr. of bits. + case kToshibaACBits: + case kToshibaACBitsShort: + case kToshibaACBitsLong: + break; + default: + return false; + } + } + + // Match Header + Data + Footer + if (!matchGeneric(results->rawbuf + offset, results->state, + results->rawlen - offset, nbits, + kToshibaAcHdrMark, kToshibaAcHdrSpace, + kToshibaAcBitMark, kToshibaAcOneSpace, + kToshibaAcBitMark, kToshibaAcZeroSpace, + kToshibaAcBitMark, kToshibaAcMinGap, true, + _tolerance, kMarkExcess)) return false; + // Compliance + if (strict) { + // Check that the checksum of the message is correct. + if (!IRToshibaAC::validChecksum(results->state, nbits / 8)) return false; + } + + // Success + results->decode_type = TOSHIBA_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_TOSHIBA_AC diff --git a/lib/IRremoteESP8266/src/ir_Toshiba.h b/lib/IRremoteESP8266/src/ir_Toshiba.h index d756dffdce..3ebf5e6938 100644 --- a/lib/IRremoteESP8266/src/ir_Toshiba.h +++ b/lib/IRremoteESP8266/src/ir_Toshiba.h @@ -31,10 +31,10 @@ #ifdef ARDUINO #include #endif -#include "../src/IRremoteESP8266.h" -#include "../src/IRsend.h" +#include "IRremoteESP8266.h" +#include "IRsend.h" #ifdef UNIT_TEST -#include "../src/IRsend_test.h" +#include "IRsend_test.h" #endif /// Native representation of a Toshiba A/C message. diff --git a/lib/IRremoteESP8266/src/ir_Transcold.cpp b/lib/IRremoteESP8266/src/ir_Transcold.cpp new file mode 100644 index 0000000000..14cc7f8bd2 --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_Transcold.cpp @@ -0,0 +1,500 @@ +// Copyright 2020 Chandrashekar Shetty (iamDshetty) +// Copyright 2020 crankyoldgit +// Copyright 2021 siriuslzx + +/// @file +/// @brief Support for Transcold A/C protocols. +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1256 + +#include "ir_Transcold.h" +#include +#ifndef ARDUINO +#include +#endif +#include "IRrecv.h" +#include "IRsend.h" +#include "IRtext.h" +#include "IRutils.h" + +// Constants + +const uint16_t kTranscoldHdrMark = 5944; ///< uSeconds. +const uint16_t kTranscoldBitMark = 555; ///< uSeconds. +const uint16_t kTranscoldHdrSpace = 7563; ///< uSeconds. +const uint16_t kTranscoldOneSpace = 3556; ///< uSeconds. +const uint16_t kTranscoldZeroSpace = 1526; ///< uSeconds. + +using irutils::addBoolToString; +using irutils::addIntToString; +using irutils::addLabeledString; +using irutils::addModeToString; +using irutils::addTempToString; +using irutils::addToggleToString; + +#if SEND_TRANSCOLD +/// Send a Transcold 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. +void IRsend::sendTranscold(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(kTranscoldHdrMark); + space(kTranscoldHdrSpace); + // 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; + uint8_t segment = GETBITS64(data, nbits - i, 8); + // Normal + Inverted + uint16_t both = (segment << 8) | (~segment & 0xFF); + sendData(kTranscoldBitMark, kTranscoldOneSpace, kTranscoldBitMark, + kTranscoldZeroSpace, both, 16, true); + } + // Footer + mark(kTranscoldBitMark); + space(kTranscoldHdrSpace); + mark(kTranscoldBitMark); + space(kDefaultMessageGap); + } +} +#endif // SEND_TRANSCOLD + +/// 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? +IRTranscoldAc::IRTranscoldAc(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 IRTranscoldAc::stateReset(void) { + setRaw(kTranscoldKnownGoodState); + special_state = kTranscoldOff; + swingFlag = false; + swingHFlag = false; + swingVFlag = false; +} + +/// Set up hardware to be able to send a message. +void IRTranscoldAc::begin(void) { _irsend.begin(); } + +#if SEND_TRANSCOLD +/// Send the current internal state as an IR message. +/// @param[in] repeat Nr. of times the message will be repeated. +void IRTranscoldAc::send(uint16_t repeat) { + _irsend.sendTranscold(getRaw(), kTranscoldBits, repeat); + if (isSpecialState()) { + // make sure to remove special state from special_state + // after command has being transmitted. + special_state = kTranscoldKnownGoodState; + } +} +#endif // SEND_TRANSCOLD + +/// 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 IRTranscoldAc::getRaw(void) const { + if (isSpecialState()) { + return special_state; + } + return _.raw; + } + +/// Set the internal state from a valid code for this protocol. +/// @param[in] new_code A valid code for this protocol. +void IRTranscoldAc::setRaw(const uint32_t new_code) { + if (handleSpecialState(new_code)) { + special_state = new_code; + _.raw = kTranscoldKnownGoodState; + } else { + // must be a command changing Temp|Mode|Fan + // it is safe to just copy to remote var + _.raw = new_code; + special_state = kTranscoldKnownGoodState; + // it isn`t special so might affect Temp|mode|Fan + if (new_code == kTranscoldCmdFan) { + setMode(kTranscoldFan); + } + } +} + +/// Is the current state is a special state? +/// @return true, if it is. false if it isn't. +bool IRTranscoldAc::isSpecialState(void) const { + switch (special_state) { + case kTranscoldOff: + case kTranscoldSwing: 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 +/// @return true, if it is a special state. false if it isn't. +bool IRTranscoldAc::handleSpecialState(const uint32_t data) { + switch (data) { + case kTranscoldOff: + break; + case kTranscoldSwing: + swingFlag = !swingFlag; + break; + default: + return false; + } + return true; +} + +/// Set the temperature. +/// @param[in] desired The temperature in degrees celsius. +void IRTranscoldAc::setTemp(const uint8_t desired) { + // Range check. + uint8_t temp = std::min(desired, kTranscoldTempMax); + temp = std::max(temp, kTranscoldTempMin) - kTranscoldTempMin + 1; + _.Temp = reverseBits(invertBits(temp, kTranscoldTempSize), + kTranscoldTempSize); +} + +/// Get the current temperature setting. +/// @return The current setting for temp. in degrees celsius. +uint8_t IRTranscoldAc::getTemp(void) const { + return reverseBits(invertBits(_.Temp, kTranscoldTempSize), + kTranscoldTempSize) + kTranscoldTempMin - 1; +} + +/// Get the value of the current power setting. +/// @return true, the setting is on. false, the setting is off. +bool IRTranscoldAc::getPower(void) const { + // There is only an off state. Everything else is "on". + return special_state != kTranscoldOff; +} + +/// Change the power setting. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRTranscoldAc::setPower(const bool on) { + if (!on) { + special_state = kTranscoldOff; + } else { + special_state = kTranscoldKnownGoodState; + } +} + +/// Change the power setting to On. +void IRTranscoldAc::on(void) { setPower(true); } + +/// Change the power setting to Off. +void IRTranscoldAc::off(void) { setPower(false); } + +/// Get the Swing setting of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRTranscoldAc::getSwing(void) const { return swingFlag; } + +/// Toggle the Swing mode of the A/C. +void IRTranscoldAc::setSwing(void) { + // Assumes that repeated sending "swing" toggles the action on the device. + // if not, the variable "swingFlag" can be removed. + special_state = kTranscoldSwing; + swingFlag = !swingFlag; +} + +/// Set the operating mode of the A/C. +/// @param[in] mode The desired operating mode. +void IRTranscoldAc::setMode(const uint8_t mode) { + uint32_t actualmode = mode; + switch (actualmode) { + case kTranscoldAuto: + case kTranscoldDry: + _.Fan = kTranscoldFanAuto0; + break; + case kTranscoldCool: + case kTranscoldHeat: + case kTranscoldFan: + _.Fan = kTranscoldFanAuto; + break; + default: // Anything else, go with Auto mode. + actualmode = kTranscoldAuto; + _.Fan = kTranscoldFanAuto0; + } + setTemp(getTemp()); + // Fan mode is a special case of Dry. + if (actualmode == kTranscoldFan) { + actualmode = kTranscoldDry; + _.Temp = kTranscoldFanTempCode; + } + _.Mode = actualmode; +} + +/// Get the operating mode setting of the A/C. +/// @return The current operating mode setting. +uint8_t IRTranscoldAc::getMode(void) const { + uint8_t mode = _.Mode; + if (mode == kTranscoldDry) + if (_.Temp == kTranscoldFanTempCode) return kTranscoldFan; + return mode; +} + +/// Get the current fan speed setting. +/// @return The current fan speed. +uint8_t IRTranscoldAc::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 IRTranscoldAc::setFan(const uint8_t speed, const bool modecheck) { + uint8_t newspeed = speed; + if (modecheck) { + switch (getMode()) { + case kTranscoldAuto: + case kTranscoldDry: // Dry & Auto mode can't have speed Auto. + if (speed == kTranscoldFanAuto) + newspeed = kTranscoldFanAuto0; + break; + default: // Only Dry & Auto mode can have speed Auto0. + if (speed == kTranscoldFanAuto0) + newspeed = kTranscoldFanAuto; + } + } + switch (speed) { + case kTranscoldFanAuto: + case kTranscoldFanAuto0: + case kTranscoldFanMin: + case kTranscoldFanMed: + case kTranscoldFanMax: + case kTranscoldFanZoneFollow: + case kTranscoldFanFixed: + break; + default: // Unknown speed requested. + newspeed = kTranscoldFanAuto; + break; + } + _.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 IRTranscoldAc::convertMode(const stdAc::opmode_t mode) { + switch (mode) { + case stdAc::opmode_t::kCool: return kTranscoldCool; + case stdAc::opmode_t::kHeat: return kTranscoldHeat; + case stdAc::opmode_t::kDry: return kTranscoldDry; + case stdAc::opmode_t::kFan: return kTranscoldFan; + default: return kTranscoldAuto; + } +} + +/// 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 IRTranscoldAc::convertFan(const stdAc::fanspeed_t speed) { + switch (speed) { + case stdAc::fanspeed_t::kMin: + case stdAc::fanspeed_t::kLow: return kTranscoldFanMin; + case stdAc::fanspeed_t::kMedium: return kTranscoldFanMed; + case stdAc::fanspeed_t::kHigh: + case stdAc::fanspeed_t::kMax: return kTranscoldFanMax; + default: return kTranscoldFanAuto; + } +} + +/// 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 IRTranscoldAc::toCommonMode(const uint8_t mode) { + switch (mode) { + case kTranscoldCool: return stdAc::opmode_t::kCool; + case kTranscoldHeat: return stdAc::opmode_t::kHeat; + case kTranscoldDry: return stdAc::opmode_t::kDry; + case kTranscoldFan: 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 IRTranscoldAc::toCommonFanSpeed(const uint8_t speed) { + switch (speed) { + case kTranscoldFanMax: return stdAc::fanspeed_t::kMax; + case kTranscoldFanMed: return stdAc::fanspeed_t::kMedium; + case kTranscoldFanMin: 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 IRTranscoldAc::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; + } + // Not supported. + result.model = -1; // No models used. + result.swingh = stdAc::swingh_t::kOff; + result.turbo = false; + result.clean = false; + result.light = false; + result.quiet = false; + result.econo = false; + result.filter = false; + result.beep = false; + result.clock = -1; + result.sleep = -1; + + // Supported. + result.protocol = decode_type_t::TRANSCOLD; + 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; + } + // Back to "normal" stateful messages. + result.mode = toCommonMode(getMode()); + result.degrees = getTemp(); + result.fanspeed = toCommonFanSpeed(_.Fan); + return result; +} + +/// Convert the internal state into a human readable string. +/// @return The current internal state expressed as a human readable String. +String IRTranscoldAc::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. + // Special modes. + if (getSwing()) return result + addToggleToString(true, kSwingStr); + result += addModeToString(getMode(), kTranscoldAuto, kTranscoldCool, + kTranscoldHeat, kTranscoldDry, kTranscoldFan); + result += addIntToString(_.Fan, kFanStr); + result += kSpaceLBraceStr; + switch (_.Fan) { + case kTranscoldFanAuto: + result += kAutoStr; + break; + case kTranscoldFanAuto0: + result += kAutoStr; + result += '0'; + break; + case kTranscoldFanMax: + result += kMaxStr; + break; + case kTranscoldFanMin: + result += kMinStr; + break; + case kTranscoldFanMed: + result += kMedStr; + break; + case kTranscoldFanZoneFollow: + result += kZoneFollowStr; + break; + case kTranscoldFanFixed: + result += kFixedStr; + break; + default: + result += kUnknownStr; + } + result += ')'; + // Fan mode doesn't have a temperature. + if (getMode() != kTranscoldFan) result += addTempToString(getTemp()); + return result; +} + +#if DECODE_TRANSCOLD +/// Decode the supplied Transcold 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::decodeTranscold(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; + if (strict && nbits != kTranscoldBits) return false; + if (nbits % 8 != 0) return false; + + uint64_t data = 0; + uint64_t inverted = 0; + + if (nbits > sizeof(data) * 8) + return false; // We can't possibly capture a Transcold packet that big. + + // Header + if (!matchMark(results->rawbuf[offset++], kTranscoldHdrMark)) return false; + if (!matchSpace(results->rawbuf[offset++], kTranscoldHdrSpace)) return false; + + // Data + // Twice as many bits as there are normal plus inverted bits. + for (uint16_t i = 0; i < nbits * 2; i++, offset++) { + bool flip = (i / 8) % 2; + if (!matchMark(results->rawbuf[offset++], kTranscoldBitMark)) + return false; + if (matchSpace(results->rawbuf[offset], kTranscoldOneSpace)) { + if (flip) + inverted = (inverted << 1) | 1; + else + data = (data << 1) | 1; + } else if (matchSpace(results->rawbuf[offset], kTranscoldZeroSpace)) { + if (flip) + inverted <<= 1; + else + data <<= 1; + } else { + return false; + } + } + + // Footer + if (!matchMark(results->rawbuf[offset++], kTranscoldBitMark)) return false; + if (!matchSpace(results->rawbuf[offset++], kTranscoldHdrSpace)) return false; + if (!matchMark(results->rawbuf[offset++], kTranscoldBitMark)) return false; + if (offset < results->rawlen && + !matchAtLeast(results->rawbuf[offset], kDefaultMessageGap)) + return false; + + // Compliance + if (strict && inverted != invertBits(data, nbits)) return false; + + // Success + results->decode_type = decode_type_t::TRANSCOLD; + results->bits = nbits; + results->value = data; + results->address = 0; + results->command = 0; + return true; +} +#endif // DECODE_TRANSCOLD diff --git a/lib/IRremoteESP8266/src/ir_Transcold.h b/lib/IRremoteESP8266/src/ir_Transcold.h index bedb9611d5..8fd924fbaf 100644 --- a/lib/IRremoteESP8266/src/ir_Transcold.h +++ b/lib/IRremoteESP8266/src/ir_Transcold.h @@ -63,10 +63,10 @@ temp 16 Auto cool close (right) 11101111000100000110011110011000010101001010101 #ifndef UNIT_TEST #include #endif -#include "../src/IRremoteESP8266.h" -#include "../src/IRsend.h" +#include "IRremoteESP8266.h" +#include "IRsend.h" #ifdef UNIT_TEST -#include "../src/IRsend_test.h" +#include "IRsend_test.h" #endif /// Native representation of a Transcold A/C message. diff --git a/lib/IRremoteESP8266/src/ir_Trotec.cpp b/lib/IRremoteESP8266/src/ir_Trotec.cpp new file mode 100644 index 0000000000..7cd6fff5e6 --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_Trotec.cpp @@ -0,0 +1,642 @@ +// Copyright 2017 stufisher +// Copyright 2019 crankyoldgit + +/// @file +/// @brief Support for Trotec protocols. +/// @see https://github.com/crankyoldgit/IRremoteESP8266/pull/279 +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1176 + +#include "ir_Trotec.h" +#include +#include +#ifndef UNIT_TEST +#include +#endif +#include "IRremoteESP8266.h" +#include "IRtext.h" +#include "IRutils.h" + +// Constants +const uint16_t kTrotecHdrMark = 5952; +const uint16_t kTrotecHdrSpace = 7364; +const uint16_t kTrotecBitMark = 592; +const uint16_t kTrotecOneSpace = 1560; +const uint16_t kTrotecZeroSpace = 592; +const uint16_t kTrotecGap = 6184; +const uint16_t kTrotecGapEnd = 1500; // made up value + +const uint16_t kTrotec3550HdrMark = 12000; +const uint16_t kTrotec3550HdrSpace = 5130; +const uint16_t kTrotec3550BitMark = 550; +const uint16_t kTrotec3550OneSpace = 1950; +const uint16_t kTrotec3550ZeroSpace = 500; + +const uint16_t kTrotec3550TimerMax = 8 * 60; ///< 8 hours in Minutes. + +using irutils::addBoolToString; +using irutils::addFanToString; +using irutils::addIntToString; +using irutils::addLabeledString; +using irutils::addModeToString; +using irutils::addTempToString; +using irutils::minsToString; + +#if SEND_TROTEC +/// Send a Trotec message. +/// 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::sendTrotec(const unsigned char data[], const uint16_t nbytes, + const uint16_t repeat) { + if (nbytes < kTrotecStateLength) return; + + enableIROut(36); + for (uint16_t r = 0; r <= repeat; r++) { + sendGeneric(kTrotecHdrMark, kTrotecHdrSpace, kTrotecBitMark, + kTrotecOneSpace, kTrotecBitMark, kTrotecZeroSpace, + kTrotecBitMark, kTrotecGap, data, nbytes, 36, false, + 0, // Repeats handled elsewhere + 50); + // More footer + mark(kTrotecBitMark); + space(kTrotecGapEnd); + } +} +#endif // SEND_TROTEC + +/// 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? +IRTrotecESP::IRTrotecESP(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 IRTrotecESP::begin(void) { _irsend.begin(); } + +#if SEND_TROTEC +/// Send the current internal state as an IR message. +/// @param[in] repeat Nr. of times the message will be repeated. +void IRTrotecESP::send(const uint16_t repeat) { + _irsend.sendTrotec(getRaw(), kTrotecStateLength, repeat); +} +#endif // SEND_TROTEC + +/// 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 IRTrotecESP::calcChecksum(const uint8_t state[], + const uint16_t length) { + return sumBytes(state + 2, length - 3); +} + +/// 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 IRTrotecESP::validChecksum(const uint8_t state[], const uint16_t length) { + return state[length - 1] == calcChecksum(state, length); +} + +/// Calculate & set the checksum for the current internal state of the remote. +void IRTrotecESP::checksum(void) { + _.Sum = sumBytes(_.raw + 2, kTrotecStateLength - 3); +} + +/// Reset the state of the remote to a known good state/sequence. +void IRTrotecESP::stateReset(void) { + for (uint8_t i = 2; i < kTrotecStateLength; i++) _.raw[i] = 0x0; + + _.Intro1 = kTrotecIntro1; + _.Intro2 = kTrotecIntro2; + + _.Power = false; + setTemp(kTrotecDefTemp); + _.Fan = kTrotecFanMed; + _.Mode = kTrotecAuto; +} + +/// 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* IRTrotecESP::getRaw(void) { + checksum(); + return _.raw; +} + +/// Set the internal state from a valid code for this protocol. +/// @param[in] state A valid code for this protocol. +void IRTrotecESP::setRaw(const uint8_t state[]) { + memcpy(_.raw, state, kTrotecStateLength); +} + +/// Set the requested power state of the A/C to on. +void IRTrotecESP::on(void) { setPower(true); } + +/// Set the requested power state of the A/C to off. +void IRTrotecESP::off(void) { setPower(false); } + +/// Change the power setting. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRTrotecESP::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 IRTrotecESP::getPower(void) const { + return _.Power; +} + +/// Set the speed of the fan. +/// @param[in] fan The desired setting. +void IRTrotecESP::setSpeed(const uint8_t fan) { + uint8_t speed = std::min(fan, kTrotecFanHigh); + _.Fan = speed; +} + +/// Get the current fan speed setting. +/// @return The current fan speed/mode. +uint8_t IRTrotecESP::getSpeed(void) const { + return _.Fan; +} + +/// Set the operating mode of the A/C. +/// @param[in] mode The desired operating mode. +void IRTrotecESP::setMode(const uint8_t mode) { + _.Mode = (mode > kTrotecFan) ? kTrotecAuto : mode; +} + +/// Get the operating mode setting of the A/C. +/// @return The current operating mode setting. +uint8_t IRTrotecESP::getMode(void) const { + return _.Mode; +} + +/// Set the temperature. +/// @param[in] celsius The temperature in degrees celsius. +void IRTrotecESP::setTemp(const uint8_t celsius) { + uint8_t temp = std::max(celsius, kTrotecMinTemp); + temp = std::min(temp, kTrotecMaxTemp); + _.Temp = temp - kTrotecMinTemp; +} + +/// Get the current temperature setting. +/// @return The current setting for temp. in degrees celsius. +uint8_t IRTrotecESP::getTemp(void) const { + return _.Temp + kTrotecMinTemp; +} + +/// Set the Sleep setting of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRTrotecESP::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 IRTrotecESP::getSleep(void) const { + return _.Sleep; +} + +/// Set the timer time in nr. of Hours. +/// @param[in] timer Nr. of Hours. Max is `kTrotecMaxTimer` +void IRTrotecESP::setTimer(const uint8_t timer) { + _.Timer = timer; + _.Hours = (timer > kTrotecMaxTimer) ? kTrotecMaxTimer : timer; +} + +/// Get the timer time in nr. of Hours. +/// @return Nr. of Hours. +uint8_t IRTrotecESP::getTimer(void) const { return _.Hours; } + +/// 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 IRTrotecESP::convertMode(const stdAc::opmode_t mode) { + switch (mode) { + case stdAc::opmode_t::kCool: return kTrotecCool; + case stdAc::opmode_t::kDry: return kTrotecDry; + case stdAc::opmode_t::kFan: return kTrotecFan; + // Note: No Heat mode. + default: return kTrotecAuto; + } +} + +/// 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 IRTrotecESP::convertFan(const stdAc::fanspeed_t speed) { + switch (speed) { + case stdAc::fanspeed_t::kMin: + case stdAc::fanspeed_t::kLow: return kTrotecFanLow; + case stdAc::fanspeed_t::kMedium: return kTrotecFanMed; + case stdAc::fanspeed_t::kHigh: + case stdAc::fanspeed_t::kMax: return kTrotecFanHigh; + default: return kTrotecFanMed; + } +} + +/// 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 IRTrotecESP::toCommonMode(const uint8_t mode) { + switch (mode) { + case kTrotecCool: return stdAc::opmode_t::kCool; + case kTrotecDry: return stdAc::opmode_t::kDry; + case kTrotecFan: return stdAc::opmode_t::kFan; + default: return stdAc::opmode_t::kAuto; + } +} + +/// Convert a native fan speed into its stdAc equivalent. +/// @param[in] spd The native setting to be converted. +/// @return The stdAc equivalent of the native setting. +stdAc::fanspeed_t IRTrotecESP::toCommonFanSpeed(const uint8_t spd) { + switch (spd) { + case kTrotecFanHigh: return stdAc::fanspeed_t::kMax; + case kTrotecFanMed: return stdAc::fanspeed_t::kMedium; + case kTrotecFanLow: 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 IRTrotecESP::toCommon(void) const { + stdAc::state_t result; + result.protocol = decode_type_t::TROTEC; + result.power = _.Power; + result.mode = toCommonMode(_.Mode); + result.celsius = true; + result.degrees = getTemp(); + result.fanspeed = toCommonFanSpeed(_.Fan); + result.sleep = _.Sleep ? 0 : -1; + // Not supported. + result.model = -1; // Not supported. + result.swingv = stdAc::swingv_t::kOff; + result.swingh = stdAc::swingh_t::kOff; + result.turbo = false; + 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 IRTrotecESP::toString(void) const { + String result = ""; + result.reserve(100); // Reserve some heap for the string to reduce fragging. + result += addBoolToString(_.Power, kPowerStr, false); + result += addModeToString(_.Mode, kTrotecAuto, kTrotecCool, kTrotecAuto, + kTrotecDry, kTrotecFan); + result += addTempToString(getTemp()); + result += addFanToString(_.Fan, kTrotecFanHigh, kTrotecFanLow, + kTrotecFanHigh, kTrotecFanHigh, kTrotecFanMed); + result += addBoolToString(_.Sleep, kSleepStr); + return result; +} + +#if DECODE_TROTEC +/// Decode the supplied Trotec message. +/// Status: STABLE / Works. Untested on real devices. +/// @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::decodeTrotec(decode_results *results, uint16_t offset, + const uint16_t nbits, const bool strict) { + if (results->rawlen <= 2 * nbits + kHeader + 2 * kFooter - 1 + offset) + return false; // Can't possibly be a valid Trotec A/C message. + if (strict && nbits != kTrotecBits) return false; + + uint16_t used; + // Header + Data + Footer #1 + used = matchGeneric(results->rawbuf + offset, results->state, + results->rawlen - offset, nbits, + kTrotecHdrMark, kTrotecHdrSpace, + kTrotecBitMark, kTrotecOneSpace, + kTrotecBitMark, kTrotecZeroSpace, + kTrotecBitMark, kTrotecGap, true, + _tolerance, 0, false); + if (used == 0) return false; + offset += used; + + // Footer #2 + if (!matchMark(results->rawbuf[offset++], kTrotecBitMark)) return false; + if (offset <= results->rawlen && + !matchAtLeast(results->rawbuf[offset++], kTrotecGapEnd)) return false; + // Compliance + // Verify we got a valid checksum. + if (strict && !IRTrotecESP::validChecksum(results->state)) return false; + // Success + results->decode_type = TROTEC; + 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_TROTEC + +#if SEND_TROTEC_3550 +/// Send a Trotec 3550 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::sendTrotec3550(const unsigned char data[], const uint16_t nbytes, + const uint16_t repeat) { + sendGeneric(kTrotec3550HdrMark, kTrotec3550HdrSpace, + kTrotec3550BitMark, kTrotec3550OneSpace, + kTrotec3550BitMark, kTrotec3550ZeroSpace, + kTrotec3550BitMark, kDefaultMessageGap, + data, nbytes, 38, true, repeat, kDutyDefault); +} +#endif // SEND_TROTEC_3550 + +#if DECODE_TROTEC_3550 +/// Decode the supplied Trotec 3550 message. +/// Status: STABLE / Known 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::decodeTrotec3550(decode_results *results, uint16_t offset, + const uint16_t nbits, const bool strict) { + if (strict && nbits != kTrotecBits) return false; + + // Header + Data + Footer + if (!matchGeneric(results->rawbuf + offset, results->state, + results->rawlen - offset, nbits, + kTrotec3550HdrMark, kTrotec3550HdrSpace, + kTrotec3550BitMark, kTrotec3550OneSpace, + kTrotec3550BitMark, kTrotec3550ZeroSpace, + kTrotec3550BitMark, kDefaultMessageGap)) return false; + // Compliance + if (strict && !IRTrotec3550::validChecksum(results->state, nbits / 8)) + return false; + // Success + results->decode_type = TROTEC_3550; + 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_TROTEC_3550 + +/// 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? +IRTrotec3550::IRTrotec3550(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 IRTrotec3550::begin(void) { _irsend.begin(); } + +#if SEND_TROTEC_3550 +/// Send the current internal state as an IR message. +/// @param[in] repeat Nr. of times the message will be repeated. +void IRTrotec3550::send(const uint16_t repeat) { + _irsend.sendTrotec3550(getRaw(), kTrotecStateLength, repeat); +} +#endif // SEND_TROTEC_3550 + +/// 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 IRTrotec3550::calcChecksum(const uint8_t state[], + const uint16_t length) { + return length ? sumBytes(state, length - 1) : 0; +} + +/// 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 IRTrotec3550::validChecksum(const uint8_t state[], const uint16_t length) { + return state[length - 1] == calcChecksum(state, length); +} + +/// Calculate & set the checksum for the current internal state of the remote. +void IRTrotec3550::checksum(void) { _.Sum = calcChecksum(_.raw); } + +/// Reset the state of the remote to a known good state/sequence. +void IRTrotec3550::stateReset(void) { + static const uint8_t kReset[kTrotecStateLength] = { + 0x55, 0x60, 0x00, 0x0D, 0x00, 0x00, 0x10, 0x88, 0x5A}; + std::memcpy(_.raw, kReset, kTrotecStateLength); +} + +/// 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* IRTrotec3550::getRaw(void) { + checksum(); + return _.raw; +} + +/// Set the internal state from a valid code for this protocol. +/// @param[in] state A valid code for this protocol. +void IRTrotec3550::setRaw(const uint8_t state[]) { + memcpy(_.raw, state, kTrotecStateLength); +} + +/// Set the requested power state of the A/C to on. +void IRTrotec3550::on(void) { setPower(true); } + +/// Set the requested power state of the A/C to off. +void IRTrotec3550::off(void) { setPower(false); } + +/// Change the power setting. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRTrotec3550::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 IRTrotec3550::getPower(void) const { return _.Power; } + +/// Set the speed of the fan. +/// @param[in] fan The desired setting. +void IRTrotec3550::setFan(const uint8_t fan) { + uint8_t speed = std::min(fan, kTrotecFanHigh); + _.Fan = speed; +} + +/// Get the current fan speed setting. +/// @return The current fan speed/mode. +uint8_t IRTrotec3550::getFan(void) const { return _.Fan; } + +/// Set the operating mode of the A/C. +/// @param[in] mode The desired operating mode. +void IRTrotec3550::setMode(const uint8_t mode) { + _.Mode = (mode > kTrotecFan) ? kTrotecAuto : mode; +} + +/// Get the operating mode setting of the A/C. +/// @return The current operating mode setting. +uint8_t IRTrotec3550::getMode(void) const { return _.Mode; } + +/// Set the temperature. +/// @param[in] degrees The temperature in degrees. +/// @param[in] celsius Use celsius units. True, Celsius; False Fahrenheit. +void IRTrotec3550::setTemp(const uint8_t degrees, const bool celsius) { + setTempUnit(celsius); + uint8_t minTemp = kTrotec3550MinTempC; + uint8_t maxTemp = kTrotec3550MaxTempC; + if (!celsius) { // Fahrenheit? + minTemp = kTrotec3550MinTempF; + maxTemp = kTrotec3550MaxTempF; + } + uint8_t temp = std::max(degrees, minTemp); + temp = std::min(temp, maxTemp); + if (celsius) { + _.TempC = temp - minTemp; + _.TempF = celsiusToFahrenheit(temp) - kTrotec3550MinTempF; + } else { + _.TempF = temp - minTemp; + _.TempC = fahrenheitToCelsius(temp) - kTrotec3550MinTempC; + } +} + +/// Get the current temperature setting. +/// @return The current setting for temp. in degrees. +uint8_t IRTrotec3550::getTemp(void) const { + return getTempUnit() ? _.TempC + kTrotec3550MinTempC + : _.TempF + kTrotec3550MinTempF; +} + +/// Set the temperature unit that the A/C will use.. +/// @param[in] celsius Use celsius units. True, Celsius; False Fahrenheit. +void IRTrotec3550::setTempUnit(const bool celsius) { _.Celsius = celsius; } + +/// Get the current temperature unit setting. +/// @return True, Celsius; False Fahrenheit. +bool IRTrotec3550::getTempUnit(void) const { return _.Celsius; } + +/// Change the Vertical Swing setting. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRTrotec3550::setSwingV(const bool on) { _.SwingV = on; } + +/// Get the value of the current Vertical Swing setting. +/// @return true, the setting is on. false, the setting is off. +bool IRTrotec3550::getSwingV(void) const { return _.SwingV; } + +/// Get the number of minutes of the Timer setting. +/// @return Nr of minutes. +uint16_t IRTrotec3550::getTimer(void) const { return _.TimerHrs * 60; } + +/// Set the number of minutes of the Timer setting. +/// @param[in] mins Nr. of Minutes for the Timer. `0` means disable the timer. +void IRTrotec3550::setTimer(const uint16_t mins) { + _.TimerSet = mins > 0; + _.TimerHrs = (std::min(mins, kTrotec3550TimerMax) / 60); +} + +/// 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 IRTrotec3550::convertMode(const stdAc::opmode_t mode) { + switch (mode) { + case stdAc::opmode_t::kCool: return kTrotecCool; + case stdAc::opmode_t::kDry: return kTrotecDry; + case stdAc::opmode_t::kFan: return kTrotecFan; + // Note: No Heat mode. + default: return kTrotecAuto; + } +} + +/// 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 IRTrotec3550::convertFan(const stdAc::fanspeed_t speed) { + switch (speed) { + case stdAc::fanspeed_t::kMin: + case stdAc::fanspeed_t::kLow: return kTrotecFanLow; + case stdAc::fanspeed_t::kMedium: return kTrotecFanMed; + case stdAc::fanspeed_t::kHigh: + case stdAc::fanspeed_t::kMax: return kTrotecFanHigh; + default: return kTrotecFanMed; + } +} + +/// 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 IRTrotec3550::toCommonMode(const uint8_t mode) { + switch (mode) { + case kTrotecCool: return stdAc::opmode_t::kCool; + case kTrotecDry: return stdAc::opmode_t::kDry; + case kTrotecFan: return stdAc::opmode_t::kFan; + default: return stdAc::opmode_t::kAuto; + } +} + +/// Convert a native fan speed into its stdAc equivalent. +/// @param[in] spd The native setting to be converted. +/// @return The stdAc equivalent of the native setting. +stdAc::fanspeed_t IRTrotec3550::toCommonFanSpeed(const uint8_t spd) { + switch (spd) { + case kTrotecFanHigh: return stdAc::fanspeed_t::kMax; + case kTrotecFanMed: return stdAc::fanspeed_t::kMedium; + case kTrotecFanLow: 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 IRTrotec3550::toCommon(void) const { + stdAc::state_t result; + result.protocol = decode_type_t::TROTEC_3550; + result.power = _.Power; + result.mode = toCommonMode(_.Mode); + result.celsius = getTempUnit(); + result.degrees = getTemp(); + result.fanspeed = toCommonFanSpeed(_.Fan); + result.swingv = _.SwingV ? stdAc::swingv_t::kAuto : stdAc::swingv_t::kOff; + // Not supported. + result.model = -1; + result.swingh = stdAc::swingh_t::kOff; + result.turbo = false; + 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 IRTrotec3550::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, kTrotecAuto, kTrotecCool, kTrotecAuto, + kTrotecDry, kTrotecFan); + result += addTempToString(getTemp(), _.Celsius); + result += addFanToString(_.Fan, kTrotecFanHigh, kTrotecFanLow, + kTrotecFanHigh, kTrotecFanHigh, kTrotecFanMed); + result += addBoolToString(_.SwingV, kSwingVStr); + result += addLabeledString(_.TimerSet ? minsToString(getTimer()) : kOffStr, + kTimerStr); + return result; +} diff --git a/lib/IRremoteESP8266/src/ir_Trotec.h b/lib/IRremoteESP8266/src/ir_Trotec.h index b8586dd77d..3381ec3f20 100644 --- a/lib/IRremoteESP8266/src/ir_Trotec.h +++ b/lib/IRremoteESP8266/src/ir_Trotec.h @@ -18,10 +18,10 @@ #ifndef UNIT_TEST #include #endif -#include "../src/IRremoteESP8266.h" -#include "../src/IRsend.h" +#include "IRremoteESP8266.h" +#include "IRsend.h" #ifdef UNIT_TEST -#include "../src/IRsend_test.h" +#include "IRsend_test.h" #endif /// Native representation of a Trotec A/C message. diff --git a/lib/IRremoteESP8266/src/ir_Truma.cpp b/lib/IRremoteESP8266/src/ir_Truma.cpp new file mode 100644 index 0000000000..853c640e16 --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_Truma.cpp @@ -0,0 +1,340 @@ +// Copyright 2021 David Conran (crankyoldgit) + +/// @file +/// @brief Support for Truma protocol. +/// This protocol uses mark length bit encoding. +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1440 +/// @see https://docs.google.com/spreadsheets/d/1k-RHu0vSIB6IweiTZSa3Rxy3Z_qPUtqwcqot8uXVO6I/edit?usp=sharing + + +#include "ir_Truma.h" +#include +#include "IRrecv.h" +#include "IRsend.h" +#include "IRtext.h" +#include "IRutils.h" + +using irutils::addBoolToString; +using irutils::addFanToString; +using irutils::addModeToString; +using irutils::addTempToString; + +// Constants + +const uint16_t kTrumaLdrMark = 20200; +const uint16_t kTrumaLdrSpace = 1000; +const uint16_t kTrumaHdrMark = 1800; +const uint16_t kTrumaSpace = 630; +const uint16_t kTrumaOneMark = 600; +const uint16_t kTrumaZeroMark = 1200; +const uint16_t kTrumaFooterMark = kTrumaOneMark; +const uint32_t kTrumaGap = kDefaultMessageGap; // Just a guess. + + +#if SEND_TRUMA +/// Send a Truma formatted message. +/// Status: STABLE / Confirmed 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::sendTruma(const uint64_t data, const uint16_t nbits, + const uint16_t repeat) { + for (uint16_t r = 0; r <= repeat; r++) { + enableIROut(38000); + mark(kTrumaLdrMark); + space(kTrumaLdrSpace); + sendGeneric(kTrumaHdrMark, kTrumaSpace, // Header + kTrumaOneMark, kTrumaSpace, // Data + kTrumaZeroMark, kTrumaSpace, + kTrumaFooterMark, kTrumaGap, // Footer + data, nbits, 38, false, 0, kDutyDefault); + } +} +#endif // SEND_TRUMA + +#if DECODE_TRUMA +/// Decode the supplied Truma message. +/// Status: STABLE / Confirmed working with 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. Typically kTrumaBits. +/// @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::decodeTruma(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 message. + if (strict && nbits != kTrumaBits) + return false; // Not strictly a message. + + // Leader. + if (!matchMark(results->rawbuf[offset++], kTrumaLdrMark)) return false; + if (!matchSpace(results->rawbuf[offset++], kTrumaLdrSpace)) return false; + + uint64_t data = 0; + uint16_t used; + used = matchGeneric(results->rawbuf + offset, &data, + results->rawlen - offset, nbits, + kTrumaHdrMark, kTrumaSpace, + kTrumaOneMark, kTrumaSpace, + kTrumaZeroMark, kTrumaSpace, + kTrumaFooterMark, kTrumaGap, + true, kUseDefTol, kMarkExcess, false); + if (!used) return false; + + // Compliance + if (strict && !IRTrumaAc::validChecksum(data)) return false; // Checksum. + + // Success + results->value = data; + results->decode_type = decode_type_t::TRUMA; + results->bits = nbits; + results->address = 0; + results->command = 0; + return true; +} +#endif // DECODE_TRUMA + + +/// 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? +IRTrumaAc::IRTrumaAc(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 IRTrumaAc::begin(void) { _irsend.begin(); } + +#if SEND_TRUMA +/// Send the current internal state as an IR message. +/// @param[in] repeat Nr. of times the message will be repeated. +void IRTrumaAc::send(const uint16_t repeat) { + _irsend.sendTruma(getRaw(), kTrumaBits, repeat); +} +#endif // SEND_TRUMA + +/// Calculate the checksum for a given state. +/// @param[in] state The value to calc the checksum of. +/// @return The calculated checksum value. +uint8_t IRTrumaAc::calcChecksum(const uint64_t state) { + uint8_t sum = kTrumaChecksumInit; + uint64_t to_checksum = state; + for (uint16_t i = 8; i < kTrumaBits; i += 8) { + sum += (to_checksum & 0xFF); + to_checksum >>= 8; + } + return sum; +} + +/// 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 IRTrumaAc::validChecksum(const uint64_t state) { + TrumaProtocol state_copy; + state_copy.raw = state; + return state_copy.Sum == calcChecksum(state); +} + +/// Calculate & set the checksum for the current internal state of the remote. +void IRTrumaAc::checksum(void) { _.Sum = calcChecksum(_.raw); } + +/// Reset the state of the remote to a known good state/sequence. +void IRTrumaAc::stateReset(void) { setRaw(kTrumaDefaultState); } + +/// 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 IRTrumaAc::getRaw(void) { + checksum(); + return _.raw; +} + +/// Set the internal state from a valid code for this protocol. +/// @param[in] state A valid code for this protocol. +void IRTrumaAc::setRaw(const uint64_t state) { + _.raw = state; + _lastfan = _.Fan; + _lastmode = _.Mode; +} + +/// Set the requested power state of the A/C to on. +void IRTrumaAc::on(void) { setPower(true); } + +/// Set the requested power state of the A/C to off. +void IRTrumaAc::off(void) { setPower(false); } + +/// Change the power setting. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRTrumaAc::setPower(const bool on) { + _.PowerOff = !on; + _.Mode = on ? _lastmode : kTrumaFan; // Off temporarily sets mode to Fan. +} + +/// Get the value of the current power setting. +/// @return true, the setting is on. false, the setting is off. +bool IRTrumaAc::getPower(void) const { return !_.PowerOff; } + +/// Set the speed of the fan. +/// @param[in] speed The desired setting. +void IRTrumaAc::setFan(const uint8_t speed) { + switch (speed) { + case kTrumaFanHigh: + case kTrumaFanMed: + case kTrumaFanLow: + _lastfan = speed; // Never allow _lastfan to be Quiet. + _.Fan = speed; + break; + case kTrumaFanQuiet: + if (_.Mode == kTrumaCool) _.Fan = kTrumaFanQuiet; // Only in Cool mode. + break; + default: + setFan(kTrumaFanHigh); + } +} + +/// Get the current fan speed setting. +/// @return The current fan speed/mode. +uint8_t IRTrumaAc::getFan(void) const { return _.Fan; } + +/// Set the operating mode of the A/C. +/// @param[in] mode The desired operating mode. +void IRTrumaAc::setMode(const uint8_t mode) { + switch (mode) { + case kTrumaAuto: + case kTrumaFan: + if (getQuiet()) setFan(kTrumaFanHigh); // Can only have quiet in Cool. + // FALL THRU + case kTrumaCool: + _.Mode = _.PowerOff ? kTrumaFan : mode; // When Off, only set Fan mode. + _lastmode = mode; + break; + default: + setMode(kTrumaAuto); + } +} + +/// Get the operating mode setting of the A/C. +/// @return The current operating mode setting. +uint8_t IRTrumaAc::getMode(void) const { return _.Mode; } + +/// Set the temperature. +/// @param[in] celsius The temperature in degrees celsius. +void IRTrumaAc::setTemp(const uint8_t celsius) { + uint8_t temp = std::max(celsius, kTrumaMinTemp); + temp = std::min(temp, kTrumaMaxTemp); + _.Temp = temp - kTrumaTempOffset; +} + +/// Get the current temperature setting. +/// @return The current setting for temp. in degrees celsius. +uint8_t IRTrumaAc::getTemp(void) const { return _.Temp + kTrumaTempOffset; } + +/// Change the Quiet setting. +/// @param[in] on true, the setting is on. false, the setting is off. +/// @note Quiet is only available in Cool mode. +void IRTrumaAc::setQuiet(const bool on) { + if (on && _.Mode == kTrumaCool) + setFan(kTrumaFanQuiet); + else + setFan(_lastfan); +} + +/// Get the value of the current quiet setting. +/// @return true, the setting is on. false, the setting is off. +bool IRTrumaAc::getQuiet(void) const { return _.Fan == kTrumaFanQuiet; } + +/// 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 IRTrumaAc::convertMode(const stdAc::opmode_t mode) { + switch (mode) { + case stdAc::opmode_t::kCool: return kTrumaCool; + case stdAc::opmode_t::kFan: return kTrumaFan; + default: return kTrumaAuto; + } +} + +/// 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 IRTrumaAc::convertFan(const stdAc::fanspeed_t speed) { + switch (speed) { + case stdAc::fanspeed_t::kMin: return kTrumaFanQuiet; + case stdAc::fanspeed_t::kLow: return kTrumaFanLow; + case stdAc::fanspeed_t::kMedium: return kTrumaFanMed; + default: return kTrumaFanHigh; + } +} + +/// 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 IRTrumaAc::toCommonMode(const uint8_t mode) { + switch (mode) { + case kTrumaCool: return stdAc::opmode_t::kCool; + case kTrumaFan: return stdAc::opmode_t::kFan; + default: return stdAc::opmode_t::kAuto; + } +} + +/// Convert a native fan speed into its stdAc equivalent. +/// @param[in] spd The native setting to be converted. +/// @return The stdAc equivalent of the native setting. +stdAc::fanspeed_t IRTrumaAc::toCommonFanSpeed(const uint8_t spd) { + switch (spd) { + case kTrumaFanMed: return stdAc::fanspeed_t::kMedium; + case kTrumaFanLow: return stdAc::fanspeed_t::kLow; + case kTrumaFanQuiet: return stdAc::fanspeed_t::kMin; + default: return stdAc::fanspeed_t::kHigh; + } +} + +/// Convert the current internal state into its stdAc::state_t equivalent. +/// @return The stdAc equivalent of the native settings. +stdAc::state_t IRTrumaAc::toCommon(void) const { + stdAc::state_t result; + + result.protocol = decode_type_t::TRUMA; + result.model = -1; // Not supported. + // Do we have enough current state info to override any previous state? + // i.e. Was the class just setRaw()'ed with a short "swing" message. + // This should enables us to also ignore the Swing msg's special 17C setting. + result.power = getPower(); + result.mode = toCommonMode(getMode()); + result.celsius = true; + result.degrees = getTemp(); + result.fanspeed = toCommonFanSpeed(getFan()); + result.quiet = getQuiet(); + // Not supported. + result.turbo = false; + result.econo = false; + result.light = false; + result.filter = false; + result.swingv = stdAc::swingv_t::kOff; + result.swingh = stdAc::swingh_t::kOff; + 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 IRTrumaAc::toString(void) const { + String result = ""; + result.reserve(80); + result += addBoolToString(getPower(), kPowerStr, false); + if (getPower()) // Only show the Operating Mode if the unit is on. + result += addModeToString(_.Mode, kTrumaAuto, kTrumaCool, + kTrumaAuto, kTrumaAuto, kTrumaFan); + + result += addTempToString(getTemp()); + result += addFanToString(_.Fan, kTrumaFanHigh, kTrumaFanLow, kTrumaFanHigh, + kTrumaFanQuiet, kTrumaFanMed); + result += addBoolToString(getQuiet(), kQuietStr); + return result; +} diff --git a/lib/IRremoteESP8266/src/ir_Truma.h b/lib/IRremoteESP8266/src/ir_Truma.h index 58beeeba44..9946a03229 100644 --- a/lib/IRremoteESP8266/src/ir_Truma.h +++ b/lib/IRremoteESP8266/src/ir_Truma.h @@ -15,10 +15,10 @@ #ifndef UNIT_TEST #include #endif -#include "../src/IRremoteESP8266.h" -#include "../src/IRsend.h" +#include "IRremoteESP8266.h" +#include "IRsend.h" #ifdef UNIT_TEST -#include "../src/IRsend_test.h" +#include "IRsend_test.h" #endif /// Native representation of a Truma A/C message. diff --git a/lib/IRremoteESP8266/src/ir_Vestel.cpp b/lib/IRremoteESP8266/src/ir_Vestel.cpp new file mode 100644 index 0000000000..803363a169 --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_Vestel.cpp @@ -0,0 +1,572 @@ +// Copyright 2018 Erdem U. Altinyurt +// Copyright 2019 David Conran + +/// @file +/// @brief Support for Vestel protocols. +/// Vestel added by Erdem U. Altinyurt + +#include "ir_Vestel.h" +#include +#ifndef UNIT_TEST +#include +#endif +#include "IRrecv.h" +#include "IRremoteESP8266.h" +#include "IRsend.h" +#include "IRtext.h" +#include "IRutils.h" +#include "ir_Haier.h" + +// Ref: +// None. Totally reverse engineered. + +using irutils::addBoolToString; +using irutils::addIntToString; +using irutils::addLabeledString; +using irutils::addModeToString; +using irutils::addTempToString; +using irutils::minsToString; + +#if SEND_VESTEL_AC +/// Send a Vestel 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::sendVestelAc(const uint64_t data, const uint16_t nbits, + const uint16_t repeat) { + if (nbits % 8 != 0) return; // nbits is required to be a multiple of 8. + + sendGeneric(kVestelAcHdrMark, kVestelAcHdrSpace, // Header + kVestelAcBitMark, kVestelAcOneSpace, // Data + kVestelAcBitMark, kVestelAcZeroSpace, // Data + kVestelAcBitMark, 100000, // Footer + repeat gap + data, nbits, 38, false, repeat, 50); +} +#endif // SEND_VESTEL_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? +IRVestelAc::IRVestelAc(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. +/// @note Power On, Mode Auto, Fan Auto, Temp = 25C/77F +void IRVestelAc::stateReset(void) { + _.cmdState = kVestelAcStateDefault; + _.timeState = kVestelAcTimeStateDefault; +} + +/// Set up hardware to be able to send a message. +void IRVestelAc::begin(void) { _irsend.begin(); } + +#if SEND_VESTEL_AC +/// Send the current internal state as an IR message. +/// @param[in] repeat Nr. of times the message will be repeated. +void IRVestelAc::send(const uint16_t repeat) { + _irsend.sendVestelAc(getRaw(), kVestelAcBits, repeat); +} +#endif // SEND_VESTEL_AC + +/// Get a copy of the internal state/code for this protocol. +/// @return A code for this protocol based on the current internal state. +uint64_t IRVestelAc::getRaw(void) { + checksum(); + if (!_.UseCmd) return _.timeState; + return _.cmdState; +} + +/// Set the internal state from a valid code for this protocol. +/// @param[in] newState A valid code for this protocol. +void IRVestelAc::setRaw(const uint8_t* newState) { + uint64_t upState = 0; + for (int i = 0; i < 7; i++) + upState |= static_cast(newState[i]) << (i * 8); + setRaw(upState); +} + +/// Set the internal state from a valid code for this protocol. +/// @param[in] newState A valid code for this protocol. +void IRVestelAc::setRaw(const uint64_t newState) { + _.cmdState = newState; + _.timeState = newState; + if (isTimeCommand()) { + _.cmdState = kVestelAcStateDefault; + _.UseCmd = false; + } else { + _.timeState = kVestelAcTimeStateDefault; + } +} + +/// Set the requested power state of the A/C to on. +void IRVestelAc::on(void) { setPower(true); } + +/// Set the requested power state of the A/C to off. +void IRVestelAc::off(void) { setPower(false); } + +/// Change the power setting. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRVestelAc::setPower(const bool on) { + _.Power = (on ? 0b11 : 0b00); + _.UseCmd = true; +} + +/// Get the value of the current power setting. +/// @return true, the setting is on. false, the setting is off. +bool IRVestelAc::getPower(void) const { + return _.Power; +} + +/// Set the temperature. +/// @param[in] temp The temperature in degrees celsius. +void IRVestelAc::setTemp(const uint8_t temp) { + uint8_t new_temp = std::max(kVestelAcMinTempC, temp); + new_temp = std::min(kVestelAcMaxTemp, new_temp); + _.Temp = new_temp - kVestelAcMinTempH; + _.UseCmd = true; +} + +/// Get the current temperature setting. +/// @return The current setting for temp. in degrees celsius. +uint8_t IRVestelAc::getTemp(void) const { + return _.Temp + kVestelAcMinTempH; +} + +/// Set the speed of the fan. +/// @param[in] fan The desired setting. +void IRVestelAc::setFan(const uint8_t fan) { + switch (fan) { + case kVestelAcFanLow: + case kVestelAcFanMed: + case kVestelAcFanHigh: + case kVestelAcFanAutoCool: + case kVestelAcFanAutoHot: + case kVestelAcFanAuto: + _.Fan = fan; + break; + default: + _.Fan = kVestelAcFanAuto; + } + _.UseCmd = true; +} + +/// Get the current fan speed setting. +/// @return The current fan speed/mode. +uint8_t IRVestelAc::getFan(void) const { + return _.Fan; +} + +/// Get the operating mode setting of the A/C. +/// @return The current operating mode setting. +uint8_t IRVestelAc::getMode(void) const { + return _.Mode; +} + +/// Set the operating mode of the A/C. +/// @param[in] mode The desired operating mode. +/// @note If we get an unexpected mode, default to AUTO. +void IRVestelAc::setMode(const uint8_t mode) { + switch (mode) { + case kVestelAcAuto: + case kVestelAcCool: + case kVestelAcHeat: + case kVestelAcDry: + case kVestelAcFan: + _.Mode = mode; + break; + default: + _.Mode = kVestelAcAuto; + } + _.UseCmd = true; +} + +/// Set Auto mode/level of the A/C. +/// @param[in] autoLevel The auto mode/level setting. +void IRVestelAc::setAuto(const int8_t autoLevel) { + if (autoLevel < -2 || autoLevel > 2) return; + _.Mode = kVestelAcAuto; + _.Fan = (autoLevel < 0 ? kVestelAcFanAutoCool : kVestelAcFanAutoHot); + if (autoLevel == 2) + setTemp(30); + else if (autoLevel == 1) + setTemp(31); + else if (autoLevel == 0) + setTemp(25); + else if (autoLevel == -1) + setTemp(16); + else if (autoLevel == -2) + setTemp(17); +} + +/// Set the timer to be active on the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRVestelAc::setTimerActive(const bool on) { + _.Timer = on; + _.UseCmd = false; +} + +/// Get if the Timer is active on the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRVestelAc::isTimerActive(void) const { + return _.Timer; +} + +/// Set Timer option of A/C. +/// @param[in] minutes Nr of minutes the timer is to be set for. +/// @note Valid arguments are 0, 0.5, 1, 2, 3 and 5 hours (in minutes). +/// 0 disables the timer. +void IRVestelAc::setTimer(const uint16_t minutes) { + // Clear both On & Off timers. + _.OnHours = 0; + _.OnTenMins = 0; + // Set the "Off" time with the nr of minutes before we turn off. + _.OffHours = minutes / 60; + _.OffTenMins = (minutes % 60) / 10; + setOffTimerActive(false); + // Yes. On Timer instead of Off timer active. + setOnTimerActive(minutes != 0); + setTimerActive(minutes != 0); +} + +/// Get the Timer time of A/C. +/// @return The number of minutes of time on the timer. +uint16_t IRVestelAc::getTimer(void) const { return getOffTimer(); } + +/// Set the A/C's internal clock. +/// @param[in] minutes The time expressed in nr. of minutes past midnight. +void IRVestelAc::setTime(const uint16_t minutes) { + _.Hours = minutes / 60; + _.Minutes = minutes % 60; + _.UseCmd = false; +} + +/// Get the A/C's internal clock's time. +/// @return The time expressed in nr. of minutes past midnight. +uint16_t IRVestelAc::getTime(void) const { + return _.Hours * 60 + _.Minutes; +} + +/// Set the On timer to be active on the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRVestelAc::setOnTimerActive(const bool on) { + _.OnTimer = on; + _.UseCmd = false; +} + +/// Get if the On Timer is active on the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRVestelAc::isOnTimerActive(void) const { + return _.OnTimer; +} + +/// Set the On timer time on the A/C. +/// @param[in] minutes Time in nr. of minutes. +void IRVestelAc::setOnTimer(const uint16_t minutes) { + setOnTimerActive(minutes); + _.OnHours = minutes / 60; + _.OnTenMins = (minutes % 60) / 10; + setTimerActive(false); +} + +/// Get the A/C's On Timer time. +/// @return The time expressed in nr. of minutes. +uint16_t IRVestelAc::getOnTimer(void) const { + return _.OnHours * 60 + _.OnTenMins * 10; +} + +/// Set the Off timer to be active on the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRVestelAc::setOffTimerActive(const bool on) { + _.OffTimer = on; + _.UseCmd = false; +} + +/// Get if the Off Timer is active on the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRVestelAc::isOffTimerActive(void) const { + return _.OffTimer; +} + +/// Set the Off timer time on the A/C. +/// @param[in] minutes Time in nr. of minutes. +void IRVestelAc::setOffTimer(const uint16_t minutes) { + setOffTimerActive(minutes); + _.OffHours = minutes / 60; + _.OffTenMins = (minutes % 60) / 10; + setTimerActive(false); +} + +/// Get the A/C's Off Timer time. +/// @return The time expressed in nr. of minutes. +uint16_t IRVestelAc::getOffTimer(void) const { + return _.OffHours * 60 + _.OffTenMins * 10; +} + +/// Set the Sleep setting of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRVestelAc::setSleep(const bool on) { + _.TurboSleep = (on ? kVestelAcSleep : kVestelAcNormal); + _.UseCmd = true; +} + +/// Get the Sleep setting of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRVestelAc::getSleep(void) const { + return _.TurboSleep == kVestelAcSleep; +} + +/// Set the Turbo setting of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRVestelAc::setTurbo(const bool on) { + _.TurboSleep = (on ? kVestelAcTurbo : kVestelAcNormal); + _.UseCmd = true; +} + +/// Get the Turbo setting of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRVestelAc::getTurbo(void) const { + return _.TurboSleep == kVestelAcTurbo; +} + +/// Set the Ion (Filter) setting of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRVestelAc::setIon(const bool on) { + _.Ion = on; + _.UseCmd = true; +} + +/// Get the Ion (Filter) setting of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRVestelAc::getIon(void) const { + return _.Ion; +} + +/// Set the Swing Roaming setting of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRVestelAc::setSwing(const bool on) { + _.Swing = (on ? kVestelAcSwing : 0xF); + _.UseCmd = true; +} + +/// Get the Swing Roaming setting of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRVestelAc::getSwing(void) const { + return _.Swing == kVestelAcSwing; +} + +/// Calculate the checksum for a given state. +/// @param[in] state The state to calc the checksum of. +/// @return The calculated checksum value. +uint8_t IRVestelAc::calcChecksum(const uint64_t state) { + // Just counts the set bits +1 on stream and take inverse after mask + return 0xFF - countBits(GETBITS64(state, 20, 44), 44, true, 2); +} + +/// 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 IRVestelAc::validChecksum(const uint64_t state) { + VestelProtocol vp; + vp.cmdState = state; + return vp.CmdSum == IRVestelAc::calcChecksum(state); +} + +/// Calculate & set the checksum for the current internal state of the remote. +void IRVestelAc::checksum(void) { + // Stored the checksum value in the last byte. + _.CmdSum = calcChecksum(_.cmdState); + _.TimeSum = calcChecksum(_.timeState); +} + +/// Is the current state a time command? +/// @return true, if the state is a time message. Otherwise, false. +bool IRVestelAc::isTimeCommand(void) const { + return !_.UseCmd; +} + +/// 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 IRVestelAc::convertMode(const stdAc::opmode_t mode) { + switch (mode) { + case stdAc::opmode_t::kCool: return kVestelAcCool; + case stdAc::opmode_t::kHeat: return kVestelAcHeat; + case stdAc::opmode_t::kDry: return kVestelAcDry; + case stdAc::opmode_t::kFan: return kVestelAcFan; + default: return kVestelAcAuto; + } +} + +/// 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 IRVestelAc::convertFan(const stdAc::fanspeed_t speed) { + switch (speed) { + case stdAc::fanspeed_t::kMin: + case stdAc::fanspeed_t::kLow: return kVestelAcFanLow; + case stdAc::fanspeed_t::kMedium: return kVestelAcFanMed; + case stdAc::fanspeed_t::kHigh: + case stdAc::fanspeed_t::kMax: return kVestelAcFanHigh; + default: return kVestelAcFanAuto; + } +} + +/// 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 IRVestelAc::toCommonMode(const uint8_t mode) { + switch (mode) { + case kVestelAcCool: return stdAc::opmode_t::kCool; + case kVestelAcHeat: return stdAc::opmode_t::kHeat; + case kVestelAcDry: return stdAc::opmode_t::kDry; + case kVestelAcFan: return stdAc::opmode_t::kFan; + default: return stdAc::opmode_t::kAuto; + } +} + +/// Convert a native fan speed into its stdAc equivalent. +/// @param[in] spd The native setting to be converted. +/// @return The stdAc equivalent of the native setting. +stdAc::fanspeed_t IRVestelAc::toCommonFanSpeed(const uint8_t spd) { + switch (spd) { + case kVestelAcFanHigh: return stdAc::fanspeed_t::kMax; + case kVestelAcFanMed: return stdAc::fanspeed_t::kMedium; + case kVestelAcFanLow: 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 IRVestelAc::toCommon(void) const { + stdAc::state_t result; + result.protocol = decode_type_t::VESTEL_AC; + result.model = -1; // Not supported. + result.power = _.Power; + result.mode = toCommonMode(_.Mode); + result.celsius = true; + result.degrees = getTemp(); + result.fanspeed = toCommonFanSpeed(_.Fan); + result.swingv = (getSwing() ? stdAc::swingv_t::kAuto + : stdAc::swingv_t::kOff); + result.turbo = getTurbo(); + result.filter = _.Ion; + result.sleep = (getSleep() ? 0 : -1); + // Not supported. + result.swingh = stdAc::swingh_t::kOff; + result.light = 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 IRVestelAc::toString(void) const { + String result = ""; + result.reserve(100); // Reserve some heap for the string to reduce fragging. + if (isTimeCommand()) { + result += addLabeledString(minsToString(getTime()), kClockStr, false); + result += addLabeledString( + (_.Timer ? minsToString(getTimer()) : kOffStr), + kTimerStr); + result += addLabeledString( + (_.OnTimer && !_.Timer) ? minsToString(getOnTimer()) : kOffStr, + kOnTimerStr); + result += addLabeledString( + (_.OffTimer ? minsToString(getOffTimer()) : kOffStr), + kOffTimerStr); + return result; + } + // Not a time command, it's a normal command. + result += addBoolToString(_.Power, kPowerStr, false); + result += addModeToString(_.Mode, kVestelAcAuto, kVestelAcCool, + kVestelAcHeat, kVestelAcDry, kVestelAcFan); + result += addTempToString(getTemp()); + result += addIntToString(_.Fan, kFanStr); + result += kSpaceLBraceStr; + switch (_.Fan) { + case kVestelAcFanAuto: + result += kAutoStr; + break; + case kVestelAcFanLow: + result += kLowStr; + break; + case kVestelAcFanMed: + result += kMedStr; + break; + case kVestelAcFanHigh: + result += kHighStr; + break; + case kVestelAcFanAutoCool: + result += kAutoStr; + result += ' '; + result += kCoolStr; + break; + case kVestelAcFanAutoHot: + result += kAutoStr; + result += ' '; + result += kHeatStr; + break; + default: + result += kUnknownStr; + } + result += ')'; + result += addBoolToString(getSleep(), kSleepStr); + result += addBoolToString(getTurbo(), kTurboStr); + result += addBoolToString(_.Ion, kIonStr); + result += addBoolToString(getSwing(), kSwingStr); + return result; +} + +#if DECODE_VESTEL_AC +/// Decode the supplied Vestel 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. +/// @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::decodeVestelAc(decode_results* results, uint16_t offset, + const uint16_t nbits, const bool strict) { + if (nbits % 8 != 0) // nbits has to be a multiple of nr. of bits in a byte. + return false; + + if (strict) + if (nbits != kVestelAcBits) + return false; // Not strictly a Vestel AC message. + + uint64_t data = 0; + + if (nbits > sizeof(data) * 8) + return false; // We can't possibly capture a Vestel packet that big. + + // Match Header + Data + Footer + if (!matchGeneric(results->rawbuf + offset, &data, + results->rawlen - offset, nbits, + kVestelAcHdrMark, kVestelAcHdrSpace, + kVestelAcBitMark, kVestelAcOneSpace, + kVestelAcBitMark, kVestelAcZeroSpace, + kVestelAcBitMark, 0, false, + kVestelAcTolerance, kMarkExcess, false)) return false; + // Compliance + if (strict) + if (!IRVestelAc::validChecksum(data)) return false; + + // Success + results->decode_type = VESTEL_AC; + results->bits = nbits; + results->value = data; + results->address = 0; + results->command = 0; + + return true; +} +#endif // DECODE_VESTEL_AC diff --git a/lib/IRremoteESP8266/src/ir_Vestel.h b/lib/IRremoteESP8266/src/ir_Vestel.h index 0b3bd2c4b6..53ebdc7cda 100644 --- a/lib/IRremoteESP8266/src/ir_Vestel.h +++ b/lib/IRremoteESP8266/src/ir_Vestel.h @@ -16,10 +16,10 @@ #ifdef ARDUINO #include #endif -#include "../src/IRremoteESP8266.h" -#include "../src/IRsend.h" +#include "IRremoteESP8266.h" +#include "IRsend.h" #ifdef UNIT_TEST -#include "../src/IRsend_test.h" +#include "IRsend_test.h" #endif /// Native representation of a Vestel A/C message. diff --git a/lib/IRremoteESP8266/src/ir_Voltas.cpp b/lib/IRremoteESP8266/src/ir_Voltas.cpp new file mode 100644 index 0000000000..847446f385 --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_Voltas.cpp @@ -0,0 +1,516 @@ +// Copyright 2020 David Conran (crankyoldgit) +// Copyright 2020 manj9501 +/// @file +/// @brief Support for Voltas A/C protocol +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1238 + +#include "ir_Voltas.h" +#include +#include +#include "IRrecv.h" +#include "IRsend.h" +#include "IRtext.h" +#include "IRutils.h" + +using irutils::addBoolToString; +using irutils::addModelToString; +using irutils::addModeToString; +using irutils::addFanToString; +using irutils::addLabeledString; +using irutils::addTempToString; +using irutils::minsToString; + +// Constants +const uint16_t kVoltasBitMark = 1026; ///< uSeconds. +const uint16_t kVoltasOneSpace = 2553; ///< uSeconds. +const uint16_t kVoltasZeroSpace = 554; ///< uSeconds. +const uint16_t kVoltasFreq = 38000; ///< Hz. + +#if SEND_VOLTAS +/// Send a Voltas formatted message. +/// Status: STABLE / Working on real device. +/// @param[in] data An array of bytes containing the IR command. +/// It is assumed to be in MSB order for this code. +/// e.g. +/// @code +/// uint8_t data[kVoltasStateLength] = {0x33, 0x28, 0x88, 0x1A, 0x3B, 0x3B, +/// 0x3B, 0x11, 0x00, 0x40}; +/// @endcode +/// @param[in] nbytes Nr. of bytes of data in the array. (>=kVoltasStateLength) +/// @param[in] repeat Nr. of times the message is to be repeated. +void IRsend::sendVoltas(const uint8_t data[], const uint16_t nbytes, + const uint16_t repeat) { + sendGeneric(0, 0, + kVoltasBitMark, kVoltasOneSpace, + kVoltasBitMark, kVoltasZeroSpace, + kVoltasBitMark, kDefaultMessageGap, + data, nbytes, + kVoltasFreq, true, repeat, kDutyDefault); +} +#endif // SEND_VOLTAS + +#if DECODE_VOLTAS +/// Decode the supplied Voltas message. +/// Status: STABLE / Working on real device. +/// @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::decodeVoltas(decode_results *results, uint16_t offset, + const uint16_t nbits, const bool strict) { + if (strict && nbits != kVoltasBits) return false; + + // Data + Footer + if (!matchGeneric(results->rawbuf + offset, results->state, + results->rawlen - offset, nbits, + 0, 0, // No header + kVoltasBitMark, kVoltasOneSpace, + kVoltasBitMark, kVoltasZeroSpace, + kVoltasBitMark, kDefaultMessageGap, true)) return false; + + // Compliance + if (strict && !IRVoltas::validChecksum(results->state, nbits / 8)) + return false; + // Success + results->decode_type = decode_type_t::VOLTAS; + results->bits = nbits; + return true; +} +#endif // DECODE_VOLTAS + +/// 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? +IRVoltas::IRVoltas(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 IRVoltas::stateReset() { + // This resets to a known-good state. + // ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/1238#issuecomment-674699746 + const uint8_t kReset[kVoltasStateLength] = { + 0x33, 0x28, 0x00, 0x17, 0x3B, 0x3B, 0x3B, 0x11, 0x00, 0xCB}; + setRaw(kReset); +} + +/// Set up hardware to be able to send a message. +void IRVoltas::begin() { _irsend.begin(); } + +#if SEND_VOLTAS +/// Send the current internal state as an IR message. +/// @param[in] repeat Nr. of times the message will be repeated. +void IRVoltas::send(const uint16_t repeat) { + _irsend.sendVoltas(getRaw(), kVoltasStateLength, repeat); +} +#endif // SEND_VOLTAS + +/// Get the model information currently known. +/// @param[in] raw Work out the model info from the current raw state. +/// @return The known model number. +voltas_ac_remote_model_t IRVoltas::getModel(const bool raw) const { + if (raw) { + switch (_.SwingHChange) { + case kVoltasSwingHNoChange: + return voltas_ac_remote_model_t::kVoltas122LZF; + default: + return voltas_ac_remote_model_t::kVoltasUnknown; + } + } else { + return _model; + } +} + +/// Set the current model for the remote. +/// @param[in] model The model number. +void IRVoltas::setModel(const voltas_ac_remote_model_t model) { + switch (model) { + case voltas_ac_remote_model_t::kVoltas122LZF: + _model = model; + setSwingHChange(false); + break; + default: _model = voltas_ac_remote_model_t::kVoltasUnknown; + } +} + +/// 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* IRVoltas::getRaw(void) { + checksum(); // Ensure correct settings before sending. + return _.raw; +} + +/// Set the internal state from a valid code for this protocol. +/// @param[in] new_code A valid code for this protocol. +void IRVoltas::setRaw(const uint8_t new_code[]) { + std::memcpy(_.raw, new_code, kVoltasStateLength); + setModel(getModel(true)); +} + +/// Calculate and set the checksum values for the internal state. +void IRVoltas::checksum(void) { + _.Checksum = calcChecksum(_.raw); +} + +/// 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 IRVoltas::validChecksum(const uint8_t state[], const uint16_t length) { + if (length) return state[length - 1] == calcChecksum(state, length); + return true; +} + +/// Calculate the checksum is valid for a given state. +/// @param[in] state The array to calculate the checksum of. +/// @param[in] length The length of the state array. +/// @return The valid checksum value for the state. +uint8_t IRVoltas::calcChecksum(const uint8_t state[], const uint16_t length) { + uint8_t result = 0; + if (length) + result = sumBytes(state, length - 1); + return ~result; +} + +/// Change the power setting to On. +void IRVoltas::on() { setPower(true); } + +/// Change the power setting to Off. +void IRVoltas::off() { setPower(false); } + +/// Change the power setting. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRVoltas::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 IRVoltas::getPower(void) const { return _.Power; } + +/// Set the operating mode of the A/C. +/// @param[in] mode The desired operating mode. +/// @note If we get an unexpected mode, default to AUTO. +void IRVoltas::setMode(const uint8_t mode) { + _.Mode = mode; + switch (mode) { + case kVoltasFan: + setFan(getFan()); // Force the fan speed to a correct one fo the mode. + break; + case kVoltasDry: + setFan(kVoltasFanLow); + setTemp(kVoltasDryTemp); + break; + case kVoltasHeat: + case kVoltasCool: + break; + default: + setMode(kVoltasCool); + return; + } + // Reset some settings if needed. + setEcono(getEcono()); + setTurbo(getTurbo()); + setSleep(getSleep()); +} + +/// Get the operating mode setting of the A/C. +/// @return The current operating mode setting. +uint8_t IRVoltas::getMode(void) { 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 IRVoltas::convertMode(const stdAc::opmode_t mode) { + switch (mode) { + case stdAc::opmode_t::kHeat: return kVoltasHeat; + case stdAc::opmode_t::kDry: return kVoltasDry; + case stdAc::opmode_t::kFan: return kVoltasFan; + default: return kVoltasCool; + } +} + +/// 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 IRVoltas::toCommonMode(const uint8_t mode) { + switch (mode) { + case kVoltasHeat: return stdAc::opmode_t::kHeat; + case kVoltasDry: return stdAc::opmode_t::kDry; + case kVoltasFan: return stdAc::opmode_t::kFan; + default: return stdAc::opmode_t::kCool; + } +} + +/// Set the temperature. +/// @param[in] temp The temperature in degrees celsius. +void IRVoltas::setTemp(const uint8_t temp) { + uint8_t new_temp = std::max(kVoltasMinTemp, temp); + new_temp = std::min(kVoltasMaxTemp, new_temp); + _.Temp = new_temp - kVoltasMinTemp; +} + +/// Get the current temperature setting. +/// @return The current setting for temp. in degrees celsius. +uint8_t IRVoltas::getTemp(void) { return _.Temp + kVoltasMinTemp; } + +/// Set the speed of the fan. +/// @param[in] fan The desired setting. +void IRVoltas::setFan(const uint8_t fan) { + switch (fan) { + case kVoltasFanAuto: + if (_.Mode == kVoltasFan) { // Auto speed is not available in fan mode. + setFan(kVoltasFanHigh); + return; + } + // FALL-THRU + case kVoltasFanLow: + case kVoltasFanMed: + case kVoltasFanHigh: + _.FanSpeed = fan; + break; + default: + setFan(kVoltasFanAuto); + } +} + +/// Get the current fan speed setting. +/// @return The current fan speed/mode. +uint8_t IRVoltas::getFan(void) { return _.FanSpeed; } + +/// 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 IRVoltas::convertFan(const stdAc::fanspeed_t speed) { + switch (speed) { + case stdAc::fanspeed_t::kMin: + case stdAc::fanspeed_t::kLow: return kVoltasFanLow; + case stdAc::fanspeed_t::kMedium: return kVoltasFanMed; + case stdAc::fanspeed_t::kHigh: + case stdAc::fanspeed_t::kMax: return kVoltasFanHigh; + default: return kVoltasFanAuto; + } +} + +/// Convert a native fan speed into its stdAc equivalent. +/// @param[in] spd The native setting to be converted. +/// @return The stdAc equivalent of the native setting. +stdAc::fanspeed_t IRVoltas::toCommonFanSpeed(const uint8_t spd) { + switch (spd) { + case kVoltasFanHigh: return stdAc::fanspeed_t::kMax; + case kVoltasFanMed: return stdAc::fanspeed_t::kMedium; + case kVoltasFanLow: 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 IRVoltas::setSwingV(const bool on) { _.SwingV = on ? 0b111 : 0b000; } + +/// Get the Vertical Swing setting of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRVoltas::getSwingV(void) const { return _.SwingV == 0b111; } + +/// Set the Horizontal Swing setting of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRVoltas::setSwingH(const bool on) { + switch (_model) { + case voltas_ac_remote_model_t::kVoltas122LZF: + break; // unsupported on these models. + default: + _.SwingH = on; + setSwingHChange(true); + } +} + +/// Get the Horizontal Swing setting of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRVoltas::getSwingH(void) const { + switch (_model) { + case voltas_ac_remote_model_t::kVoltas122LZF: + return false; // unsupported on these models. + default: + return _.SwingH; + } +} + +/// Set the bits for changing the Horizontal Swing setting of the A/C. +/// @param[in] on true, the change bits are set. +/// false, the "no change" bits are set. +void IRVoltas::setSwingHChange(const bool on) { + _.SwingHChange = on ? kVoltasSwingHChange : kVoltasSwingHNoChange; + if (!on) _.SwingH = true; // "No Change" also sets SwingH to 1. +} + +/// Are the Horizontal Swing change bits set in the message? +/// @return true, the correct bits are set. false, the correct bits are not set. +bool IRVoltas::getSwingHChange(void) const { + return _.SwingHChange == kVoltasSwingHChange; +} + +/// Change the Wifi setting. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRVoltas::setWifi(const bool on) { _.Wifi = on; } + +/// Get the value of the current Wifi setting. +/// @return true, the setting is on. false, the setting is off. +bool IRVoltas::getWifi(void) const { return _.Wifi; } + +/// Change the Turbo setting. +/// @param[in] on true, the setting is on. false, the setting is off. +/// @note The Turbo setting is only available in Cool mode. +void IRVoltas::setTurbo(const bool on) { + if (on && _.Mode == kVoltasCool) + _.Turbo = true; + else + _.Turbo = false; +} + +/// Get the value of the current Turbo setting. +/// @return true, the setting is on. false, the setting is off. +bool IRVoltas::getTurbo(void) const { return _.Turbo; } + +/// Change the Economy setting. +/// @param[in] on true, the setting is on. false, the setting is off. +/// @note The Economy setting is only available in Cool mode. +void IRVoltas::setEcono(const bool on) { + if (on && _.Mode == kVoltasCool) + _.Econo = true; + else + _.Econo = false; +} + +/// Get the value of the current Econo setting. +/// @return true, the setting is on. false, the setting is off. +bool IRVoltas::getEcono(void) const { return _.Econo; } + +/// Change the Light setting. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRVoltas::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 IRVoltas::getLight(void) const { return _.Light; } + +/// Change the Sleep setting. +/// @param[in] on true, the setting is on. false, the setting is off. +/// @note The Sleep setting is only available in Cool mode. +void IRVoltas::setSleep(const bool on) { + if (on && _.Mode == kVoltasCool) + _.Sleep = true; + else + _.Sleep = false; +} + +/// Get the value of the current Sleep setting. +/// @return true, the setting is on. false, the setting is off. +bool IRVoltas::getSleep(void) const { return _.Sleep; } + +/// Get the value of the On Timer time. +/// @return Number of minutes before the timer activates. +uint16_t IRVoltas::getOnTime(void) const { + return std::min((unsigned)(12 * _.OnTimer12Hr + _.OnTimerHrs - 1), 23U) * 60 + + _.OnTimerMins; +} + +/// Set the value of the On Timer time. +/// @param[in] nr_of_mins Number of minutes before the timer activates. +/// 0 disables the timer. Max is 23 hrs & 59 mins (1439 mins) +void IRVoltas::setOnTime(const uint16_t nr_of_mins) { + // Cap the total number of mins. + uint16_t mins = std::min(nr_of_mins, (uint16_t)(23 * 60 + 59)); + uint16_t hrs = (mins / 60) + 1; + _.OnTimerMins = mins % 60; + _.OnTimer12Hr = hrs / 12; + _.OnTimerHrs = hrs % 12; + _.OnTimerEnable = (mins > 0); // Is the timer is to be enabled? +} + +/// Get the value of the On Timer time. +/// @return Number of minutes before the timer activates. +uint16_t IRVoltas::getOffTime(void) const { + return std::min((unsigned)(12 * _.OffTimer12Hr + _.OffTimerHrs - 1), 23U) * + 60 + _.OffTimerMins; +} + +/// Set the value of the Off Timer time. +/// @param[in] nr_of_mins Number of minutes before the timer activates. +/// 0 disables the timer. Max is 23 hrs & 59 mins (1439 mins) +void IRVoltas::setOffTime(const uint16_t nr_of_mins) { + // Cap the total number of mins. + uint16_t mins = std::min(nr_of_mins, (uint16_t)(23 * 60 + 59)); + uint16_t hrs = (mins / 60) + 1; + _.OffTimerMins = mins % 60; + _.OffTimer12Hr = hrs / 12; + _.OffTimerHrs = hrs % 12; + _.OffTimerEnable = (mins > 0); // Is the timer is to be enabled? +} + +/// Convert the current internal state into its stdAc::state_t equivalent. +/// @param[in] prev Ptr to the previous state if available. +/// @return The stdAc equivalent of the native settings. +stdAc::state_t IRVoltas::toCommon(const stdAc::state_t *prev) { + 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. + result.swingh = stdAc::swingh_t::kOff; + } + result.model = getModel(); + result.protocol = decode_type_t::VOLTAS; + result.power = _.Power; + result.mode = toCommonMode(_.Mode); + result.celsius = true; + result.degrees = getTemp(); + result.fanspeed = toCommonFanSpeed(_.FanSpeed); + result.swingv = getSwingV() ? stdAc::swingv_t::kAuto : stdAc::swingv_t::kOff; + if (getSwingHChange()) + result.swingh = _.SwingH ? stdAc::swingh_t::kAuto : stdAc::swingh_t::kOff; + result.turbo = _.Turbo; + result.econo = _.Econo; + result.light = _.Light; + result.sleep = _.Sleep ? 0 : -1; + // Not supported. + result.quiet = 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 IRVoltas::toString() { + String result = ""; + result.reserve(200); // Reserve some heap for the string to reduce fragging. + result += addModelToString(decode_type_t::VOLTAS, getModel(), false); + result += addBoolToString(_.Power, kPowerStr); + result += addModeToString(_.Mode, 255, kVoltasCool, kVoltasHeat, + kVoltasDry, kVoltasFan); + result += addTempToString(getTemp()); + result += addFanToString(_.FanSpeed, kVoltasFanHigh, kVoltasFanLow, + kVoltasFanAuto, kVoltasFanAuto, kVoltasFanMed); + result += addBoolToString(getSwingV(), kSwingVStr); + if (getSwingHChange()) + result += addBoolToString(_.SwingH, kSwingHStr); + else + result += addLabeledString(kNAStr, kSwingHStr); + result += addBoolToString(_.Turbo, kTurboStr); + result += addBoolToString(_.Econo, kEconoStr); + result += addBoolToString(_.Wifi, kWifiStr); + result += addBoolToString(_.Light, kLightStr); + result += addBoolToString(_.Sleep, kSleepStr); + result += addLabeledString(_.OnTimerEnable ? minsToString(getOnTime()) + : kOffStr, kOnTimerStr); + result += addLabeledString(_.OffTimerEnable ? minsToString(getOffTime()) + : kOffStr, kOffTimerStr); + return result; +} diff --git a/lib/IRremoteESP8266/src/ir_Voltas.h b/lib/IRremoteESP8266/src/ir_Voltas.h index 64ab1b5127..cf79d3458f 100644 --- a/lib/IRremoteESP8266/src/ir_Voltas.h +++ b/lib/IRremoteESP8266/src/ir_Voltas.h @@ -18,10 +18,10 @@ #ifndef UNIT_TEST #include #endif -#include "../src/IRremoteESP8266.h" -#include "../src/IRsend.h" +#include "IRremoteESP8266.h" +#include "IRsend.h" #ifdef UNIT_TEST -#include "../src/IRsend_test.h" +#include "IRsend_test.h" #endif /// Native representation of a Voltas A/C message. diff --git a/lib/IRremoteESP8266/src/ir_Whirlpool.cpp b/lib/IRremoteESP8266/src/ir_Whirlpool.cpp new file mode 100644 index 0000000000..0bba57bfec --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_Whirlpool.cpp @@ -0,0 +1,657 @@ +// Copyright 2018 David Conran + +/// @file +/// @brief Support for Whirlpool protocols. +/// Decoding help from: \@redmusicxd, \@josh929800, \@raducostea +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/509 +/// @note Smart, iFeel, AroundU, PowerSave, & Silent modes are unsupported. +/// Advanced 6thSense, Dehumidify, & Sleep modes are not supported. +/// @note Dim == !Light, Jet == Super == Turbo + +#include "ir_Whirlpool.h" +#include +#include +#ifndef ARDUINO +#include +#endif +#include "IRrecv.h" +#include "IRremoteESP8266.h" +#include "IRsend.h" +#include "IRtext.h" +#include "IRutils.h" + +// Constants +const uint16_t kWhirlpoolAcHdrMark = 8950; +const uint16_t kWhirlpoolAcHdrSpace = 4484; +const uint16_t kWhirlpoolAcBitMark = 597; +const uint16_t kWhirlpoolAcOneSpace = 1649; +const uint16_t kWhirlpoolAcZeroSpace = 533; +const uint16_t kWhirlpoolAcGap = 7920; +const uint32_t kWhirlpoolAcMinGap = kDefaultMessageGap; // Just a guess. +const uint8_t kWhirlpoolAcSections = 3; + +using irutils::addBoolToString; +using irutils::addFanToString; +using irutils::addIntToString; +using irutils::addLabeledString; +using irutils::addModeToString; +using irutils::addModelToString; +using irutils::addTempToString; +using irutils::minsToString; + +#define GETTIME(x) (_.x##Hours * 60 + _.x##Mins) +#define SETTIME(x, n) do { \ + uint16_t mins = n;\ + _.x##Hours = (mins / 60) % 24;\ + _.x##Mins = mins % 60;\ +} while (0) + +#if SEND_WHIRLPOOL_AC +/// Send a Whirlpool A/C message. +/// Status: BETA / Probably works. +/// @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::sendWhirlpoolAC(const unsigned char data[], const uint16_t nbytes, + const uint16_t repeat) { + if (nbytes < kWhirlpoolAcStateLength) + return; // Not enough bytes to send a proper message. + for (uint16_t r = 0; r <= repeat; r++) { + // Section 1 + sendGeneric(kWhirlpoolAcHdrMark, kWhirlpoolAcHdrSpace, kWhirlpoolAcBitMark, + kWhirlpoolAcOneSpace, kWhirlpoolAcBitMark, + kWhirlpoolAcZeroSpace, kWhirlpoolAcBitMark, kWhirlpoolAcGap, + data, 6, // 6 bytes == 48 bits + 38000, // Complete guess of the modulation frequency. + false, 0, 50); + // Section 2 + sendGeneric(0, 0, kWhirlpoolAcBitMark, kWhirlpoolAcOneSpace, + kWhirlpoolAcBitMark, kWhirlpoolAcZeroSpace, kWhirlpoolAcBitMark, + kWhirlpoolAcGap, data + 6, 8, // 8 bytes == 64 bits + 38000, // Complete guess of the modulation frequency. + false, 0, 50); + // Section 3 + sendGeneric(0, 0, kWhirlpoolAcBitMark, kWhirlpoolAcOneSpace, + kWhirlpoolAcBitMark, kWhirlpoolAcZeroSpace, kWhirlpoolAcBitMark, + kWhirlpoolAcMinGap, data + 14, 7, // 7 bytes == 56 bits + 38000, // Complete guess of the modulation frequency. + false, 0, 50); + } +} +#endif // SEND_WHIRLPOOL_AC + +// Class for emulating a Whirlpool A/C remote. + +/// 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? +IRWhirlpoolAc::IRWhirlpoolAc(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 IRWhirlpoolAc::stateReset(void) { + for (uint8_t i = 2; i < kWhirlpoolAcStateLength; i++) _.raw[i] = 0x0; + _.raw[0] = 0x83; + _.raw[1] = 0x06; + _.raw[6] = 0x80; + _setTemp(kWhirlpoolAcAutoTemp); // Default to a sane value. +} + +/// Set up hardware to be able to send a message. +void IRWhirlpoolAc::begin(void) { _irsend.begin(); } + +/// 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 IRWhirlpoolAc::validChecksum(const uint8_t state[], + const uint16_t length) { + if (length > kWhirlpoolAcChecksumByte1 && + state[kWhirlpoolAcChecksumByte1] != + xorBytes(state + 2, kWhirlpoolAcChecksumByte1 - 1 - 2)) { + DPRINTLN("DEBUG: First Whirlpool AC checksum failed."); + return false; + } + if (length > kWhirlpoolAcChecksumByte2 && + state[kWhirlpoolAcChecksumByte2] != + xorBytes(state + kWhirlpoolAcChecksumByte1 + 1, + kWhirlpoolAcChecksumByte2 - kWhirlpoolAcChecksumByte1 - 1)) { + DPRINTLN("DEBUG: Second Whirlpool AC checksum failed."); + return false; + } + // State is too short to have a checksum or everything checked out. + return true; +} + +/// Calculate & set the checksum for the current internal state of the remote. +/// @param[in] length The length/size of the internal state array. +void IRWhirlpoolAc::checksum(uint16_t length) { + if (length >= kWhirlpoolAcChecksumByte1) + _.Sum1 = xorBytes(_.raw + 2, kWhirlpoolAcChecksumByte1 - 1 - 2); + if (length >= kWhirlpoolAcChecksumByte2) + _.Sum2 = xorBytes(_.raw + kWhirlpoolAcChecksumByte1 + 1, + kWhirlpoolAcChecksumByte2 - kWhirlpoolAcChecksumByte1 - 1); +} + +#if SEND_WHIRLPOOL_AC +/// Send the current internal state as an IR message. +/// @param[in] repeat Nr. of times the message will be repeated. +/// @param[in] calcchecksum Do we need to calculate the checksum?. +void IRWhirlpoolAc::send(const uint16_t repeat, const bool calcchecksum) { + _irsend.sendWhirlpoolAC(getRaw(calcchecksum), kWhirlpoolAcStateLength, + repeat); +} +#endif // SEND_WHIRLPOOL_AC + +/// Get a copy of the internal state/code for this protocol. +/// @param[in] calcchecksum Do we need to calculate the checksum?. +/// @return A code for this protocol based on the current internal state. +uint8_t *IRWhirlpoolAc::getRaw(const bool calcchecksum) { + if (calcchecksum) 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 IRWhirlpoolAc::setRaw(const uint8_t new_code[], const uint16_t length) { + std::memcpy(_.raw, new_code, std::min(length, kWhirlpoolAcStateLength)); +} + +/// Get/Detect the model of the A/C. +/// @return The enum of the compatible model. +whirlpool_ac_remote_model_t IRWhirlpoolAc::getModel(void) const { + if (_.J191) + return DG11J191; + else + return DG11J13A; +} + +/// Set the model of the A/C to emulate. +/// @param[in] model The enum of the appropriate model. +void IRWhirlpoolAc::setModel(const whirlpool_ac_remote_model_t model) { + switch (model) { + case DG11J191: + _.J191 = true; + break; + case DG11J13A: + // FALL THRU + default: + _.J191 = false; + } + _setTemp(_desiredtemp); // Different models have different temp values. +} + +/// Calculate the temp. offset in deg C for the current model. +/// @return The temperature offset. +int8_t IRWhirlpoolAc::getTempOffset(void) const { + switch (getModel()) { + case whirlpool_ac_remote_model_t::DG11J191: return -2; + default: return 0; + } +} + +/// Set the temperature. +/// @param[in] temp The temperature in degrees celsius. +/// @param[in] remember Do we save this temperature? +/// @note Internal use only. +void IRWhirlpoolAc::_setTemp(const uint8_t temp, const bool remember) { + if (remember) _desiredtemp = temp; + int8_t offset = getTempOffset(); // Cache the min temp for the model. + uint8_t newtemp = std::max((uint8_t)(kWhirlpoolAcMinTemp + offset), temp); + newtemp = std::min((uint8_t)(kWhirlpoolAcMaxTemp + offset), newtemp); + _.Temp = newtemp - (kWhirlpoolAcMinTemp + offset); +} + +/// Set the temperature. +/// @param[in] temp The temperature in degrees celsius. +void IRWhirlpoolAc::setTemp(const uint8_t temp) { + _setTemp(temp); + setSuper(false); // Changing temp cancels Super/Jet mode. + _.Cmd = kWhirlpoolAcCommandTemp; +} + +/// Get the current temperature setting. +/// @return The current setting for temp. in degrees celsius. +uint8_t IRWhirlpoolAc::getTemp(void) const { + return _.Temp + kWhirlpoolAcMinTemp + getTempOffset(); +} + +/// Set the operating mode of the A/C. +/// @param[in] mode The desired operating mode. +/// @note Internal use only. +void IRWhirlpoolAc::_setMode(const uint8_t mode) { + switch (mode) { + case kWhirlpoolAcAuto: + setFan(kWhirlpoolAcFanAuto); + _setTemp(kWhirlpoolAcAutoTemp, false); + setSleep(false); // Cancel sleep mode when in auto/6thsense mode. + // FALL THRU + case kWhirlpoolAcHeat: + case kWhirlpoolAcCool: + case kWhirlpoolAcDry: + case kWhirlpoolAcFan: + _.Mode = mode; + _.Cmd = kWhirlpoolAcCommandMode; + break; + default: + return; + } + if (mode == kWhirlpoolAcAuto) _.Cmd = kWhirlpoolAcCommand6thSense; +} + +/// Set the operating mode of the A/C. +/// @param[in] mode The desired operating mode. +void IRWhirlpoolAc::setMode(const uint8_t mode) { + setSuper(false); // Changing mode cancels Super/Jet mode. + _setMode(mode); +} + +/// Get the operating mode setting of the A/C. +/// @return The current operating mode setting. +uint8_t IRWhirlpoolAc::getMode(void) const { + return _.Mode; +} + +/// Set the speed of the fan. +/// @param[in] speed The desired setting. +void IRWhirlpoolAc::setFan(const uint8_t speed) { + switch (speed) { + case kWhirlpoolAcFanAuto: + case kWhirlpoolAcFanLow: + case kWhirlpoolAcFanMedium: + case kWhirlpoolAcFanHigh: + _.Fan = speed; + setSuper(false); // Changing fan speed cancels Super/Jet mode. + _.Cmd = kWhirlpoolAcCommandFanSpeed; + break; + } +} + +/// Get the current fan speed setting. +/// @return The current fan speed/mode. +uint8_t IRWhirlpoolAc::getFan(void) const { + return _.Fan; +} + +/// Set the (vertical) swing setting of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRWhirlpoolAc::setSwing(const bool on) { + _.Swing1 = on; + _.Swing2 = on; + _.Cmd = kWhirlpoolAcCommandSwing; +} + +/// Get the (vertical) swing setting of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRWhirlpoolAc::getSwing(void) const { + return _.Swing1 && _.Swing2; +} + +/// Set the Light (Display/LED) setting of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRWhirlpoolAc::setLight(const bool on) { + // Cleared when on. + _.LightOff = !on; +} + +/// Get the Light (Display/LED) setting of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRWhirlpoolAc::getLight(void) const { + return !_.LightOff; +} + +/// Set the clock time in nr. of minutes past midnight. +/// @param[in] minspastmidnight The time expressed as minutes past midnight. +void IRWhirlpoolAc::setClock(const uint16_t minspastmidnight) { + SETTIME(Clock, minspastmidnight); +} + +/// Get the clock time in nr. of minutes past midnight. +/// @return The time expressed as the Nr. of minutes past midnight. +uint16_t IRWhirlpoolAc::getClock(void) const { + return GETTIME(Clock); +} + +/// Set the Off Timer time. +/// @param[in] minspastmidnight The time expressed as minutes past midnight. +void IRWhirlpoolAc::setOffTimer(const uint16_t minspastmidnight) { + SETTIME(Off, minspastmidnight); +} + +/// Get the Off Timer time.. +/// @return The time expressed as the Nr. of minutes past midnight. +uint16_t IRWhirlpoolAc::getOffTimer(void) const { + return GETTIME(Off); +} + +/// Is the Off timer enabled? +/// @return true, the Timer is enabled. false, the Timer is disabled. +bool IRWhirlpoolAc::isOffTimerEnabled(void) const { + return _.OffTimerEnabled; +} + +/// Enable the Off Timer. +/// @param[in] on true, the timer is enabled. false, the timer is disabled. +void IRWhirlpoolAc::enableOffTimer(const bool on) { + _.OffTimerEnabled = on; + _.Cmd = kWhirlpoolAcCommandOffTimer; +} + +/// Set the On Timer time. +/// @param[in] minspastmidnight The time expressed as minutes past midnight. +void IRWhirlpoolAc::setOnTimer(const uint16_t minspastmidnight) { + SETTIME(On, minspastmidnight); +} + +/// Get the On Timer time.. +/// @return The time expressed as the Nr. of minutes past midnight. +uint16_t IRWhirlpoolAc::getOnTimer(void) const { + return GETTIME(On); +} + +/// Is the On timer enabled? +/// @return true, the Timer is enabled. false, the Timer is disabled. +bool IRWhirlpoolAc::isOnTimerEnabled(void) const { + return _.OnTimerEnabled; +} + +/// Enable the On Timer. +/// @param[in] on true, the timer is enabled. false, the timer is disabled. +void IRWhirlpoolAc::enableOnTimer(const bool on) { + _.OnTimerEnabled = on; + _.Cmd = kWhirlpoolAcCommandOnTimer; +} + +/// Change the power toggle setting. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRWhirlpoolAc::setPowerToggle(const bool on) { + _.Power = on; + setSuper(false); // Changing power cancels Super/Jet mode. + _.Cmd = kWhirlpoolAcCommandPower; +} + +/// Get the value of the current power toggle setting. +/// @return true, the setting is on. false, the setting is off. +bool IRWhirlpoolAc::getPowerToggle(void) const { + return _.Power; +} + +/// Get the Command (Button) setting of the A/C. +/// @return The current Command (Button) of the A/C. +uint8_t IRWhirlpoolAc::getCommand(void) const { + return _.Cmd; +} + +/// Set the Sleep setting of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRWhirlpoolAc::setSleep(const bool on) { + _.Sleep = on; + if (on) setFan(kWhirlpoolAcFanLow); + _.Cmd = kWhirlpoolAcCommandSleep; +} + +/// Get the Sleep setting of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRWhirlpoolAc::getSleep(void) const { + return _.Sleep; +} + +/// Set the Super (Turbo/Jet) setting of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRWhirlpoolAc::setSuper(const bool on) { + if (on) { + setFan(kWhirlpoolAcFanHigh); + switch (_.Mode) { + case kWhirlpoolAcHeat: + setTemp(kWhirlpoolAcMaxTemp + getTempOffset()); + break; + case kWhirlpoolAcCool: + default: + setTemp(kWhirlpoolAcMinTemp + getTempOffset()); + setMode(kWhirlpoolAcCool); + break; + } + _.Super1 = 1; + _.Super2 = 1; + } else { + _.Super1 = 0; + _.Super2 = 0; + } + _.Cmd = kWhirlpoolAcCommandSuper; +} + +/// Get the Super (Turbo/Jet) setting of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRWhirlpoolAc:: getSuper(void) const { + return _.Super1 && _.Super2; +} + +/// Set the Command (Button) setting of the A/C. +/// @param[in] code The current Command (Button) of the A/C. +void IRWhirlpoolAc::setCommand(const uint8_t code) { + _.Cmd = code; +} + +/// 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 IRWhirlpoolAc::convertMode(const stdAc::opmode_t mode) { + switch (mode) { + case stdAc::opmode_t::kAuto: return kWhirlpoolAcAuto; + case stdAc::opmode_t::kHeat: return kWhirlpoolAcHeat; + case stdAc::opmode_t::kDry: return kWhirlpoolAcDry; + case stdAc::opmode_t::kFan: return kWhirlpoolAcFan; + // Default to Cool as some Whirlpool models don't have an Auto mode. + // Ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/1283 + default: return kWhirlpoolAcCool; + } +} + +/// 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 IRWhirlpoolAc::convertFan(const stdAc::fanspeed_t speed) { + switch (speed) { + case stdAc::fanspeed_t::kMin: + case stdAc::fanspeed_t::kLow: return kWhirlpoolAcFanLow; + case stdAc::fanspeed_t::kMedium: return kWhirlpoolAcFanMedium; + case stdAc::fanspeed_t::kHigh: + case stdAc::fanspeed_t::kMax: return kWhirlpoolAcFanHigh; + default: return kWhirlpoolAcFanAuto; + } +} + +/// 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 IRWhirlpoolAc::toCommonMode(const uint8_t mode) { + switch (mode) { + case kWhirlpoolAcCool: return stdAc::opmode_t::kCool; + case kWhirlpoolAcHeat: return stdAc::opmode_t::kHeat; + case kWhirlpoolAcDry: return stdAc::opmode_t::kDry; + case kWhirlpoolAcFan: 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 IRWhirlpoolAc::toCommonFanSpeed(const uint8_t speed) { + switch (speed) { + case kWhirlpoolAcFanHigh: return stdAc::fanspeed_t::kMax; + case kWhirlpoolAcFanMedium: return stdAc::fanspeed_t::kMedium; + case kWhirlpoolAcFanLow: 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 Ptr to the previous state if required. +/// @return The stdAc equivalent of the native settings. +stdAc::state_t IRWhirlpoolAc::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::WHIRLPOOL_AC; + result.model = getModel(); + if (_.Power) result.power = !result.power; + result.mode = toCommonMode(_.Mode); + result.celsius = true; + result.degrees = getTemp(); + result.fanspeed = toCommonFanSpeed(_.Fan); + result.swingv = getSwing() ? stdAc::swingv_t::kAuto : stdAc::swingv_t::kOff; + result.turbo = getSuper(); + result.light = getLight(); + result.sleep = _.Sleep ? 0 : -1; + // Not supported. + result.swingh = stdAc::swingh_t::kOff; + result.quiet = false; + result.filter = false; + result.econo = 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 IRWhirlpoolAc::toString(void) const { + String result = ""; + result.reserve(200); // Reserve some heap for the string to reduce fragging. + result += addModelToString(decode_type_t::WHIRLPOOL_AC, getModel(), false); + result += addBoolToString(_.Power, kPowerToggleStr); + result += addModeToString(_.Mode, kWhirlpoolAcAuto, kWhirlpoolAcCool, + kWhirlpoolAcHeat, kWhirlpoolAcDry, kWhirlpoolAcFan); + result += addTempToString(getTemp()); + result += addFanToString(_.Fan, kWhirlpoolAcFanHigh, kWhirlpoolAcFanLow, + kWhirlpoolAcFanAuto, kWhirlpoolAcFanAuto, + kWhirlpoolAcFanMedium); + result += addBoolToString(getSwing(), kSwingStr); + result += addBoolToString(getLight(), kLightStr); + result += addLabeledString(minsToString(getClock()), kClockStr); + result += addLabeledString( + _.OnTimerEnabled ? minsToString(getOnTimer()) : kOffStr, kOnTimerStr); + result += addLabeledString( + _.OffTimerEnabled ? minsToString(getOffTimer()) : kOffStr, kOffTimerStr); + result += addBoolToString(_.Sleep, kSleepStr); + result += addBoolToString(getSuper(), kSuperStr); + result += addIntToString(_.Cmd, kCommandStr); + result += kSpaceLBraceStr; + switch (_.Cmd) { + case kWhirlpoolAcCommandLight: + result += kLightStr; + break; + case kWhirlpoolAcCommandPower: + result += kPowerStr; + break; + case kWhirlpoolAcCommandTemp: + result += kTempStr; + break; + case kWhirlpoolAcCommandSleep: + result += kSleepStr; + break; + case kWhirlpoolAcCommandSuper: + result += kSuperStr; + break; + case kWhirlpoolAcCommandOnTimer: + result += kOnTimerStr; + break; + case kWhirlpoolAcCommandMode: + result += kModeStr; + break; + case kWhirlpoolAcCommandSwing: + result += kSwingStr; + break; + case kWhirlpoolAcCommandIFeel: + result += kIFeelStr; + break; + case kWhirlpoolAcCommandFanSpeed: + result += kFanStr; + break; + case kWhirlpoolAcCommand6thSense: + result += k6thSenseStr; + break; + case kWhirlpoolAcCommandOffTimer: + result += kOffTimerStr; + break; + default: + result += kUnknownStr; + break; + } + result += ')'; + return result; +} + +#if DECODE_WHIRLPOOL_AC + +/// Decode the supplied Whirlpool A/C message. +/// Status: STABLE / Working as intended. +/// @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::decodeWhirlpoolAC(decode_results *results, uint16_t offset, + const uint16_t nbits, const bool strict) { + if (results->rawlen < 2 * nbits + 4 + kHeader + kFooter - 1 + offset) + return false; // Can't possibly be a valid Whirlpool A/C message. + if (strict) { + if (nbits != kWhirlpoolAcBits) return false; + } + + const uint8_t sectionSize[kWhirlpoolAcSections] = {6, 8, 7}; + + // Header + if (!matchMark(results->rawbuf[offset++], kWhirlpoolAcHdrMark)) return false; + if (!matchSpace(results->rawbuf[offset++], kWhirlpoolAcHdrSpace)) + return false; + + // Data Sections + uint16_t pos = 0; + for (uint8_t section = 0; section < kWhirlpoolAcSections; + section++) { + uint16_t used; + // Section Data + used = matchGeneric(results->rawbuf + offset, results->state + pos, + results->rawlen - offset, sectionSize[section] * 8, + 0, 0, + kWhirlpoolAcBitMark, kWhirlpoolAcOneSpace, + kWhirlpoolAcBitMark, kWhirlpoolAcZeroSpace, + kWhirlpoolAcBitMark, kWhirlpoolAcGap, + section >= kWhirlpoolAcSections - 1, + _tolerance, kMarkExcess, false); + if (used == 0) return false; + offset += used; + pos += sectionSize[section]; + } + + // Compliance + if (strict) { + // Re-check we got the correct size/length due to the way we read the data. + if (pos * 8 != nbits) return false; + if (!IRWhirlpoolAc::validChecksum(results->state, nbits / 8)) + return false; + } + + // Success + results->decode_type = WHIRLPOOL_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 // WHIRLPOOL_AC diff --git a/lib/IRremoteESP8266/src/ir_Whirlpool.h b/lib/IRremoteESP8266/src/ir_Whirlpool.h index 87e7105068..ac73be926b 100644 --- a/lib/IRremoteESP8266/src/ir_Whirlpool.h +++ b/lib/IRremoteESP8266/src/ir_Whirlpool.h @@ -26,10 +26,10 @@ #ifndef UNIT_TEST #include #endif -#include "../src/IRremoteESP8266.h" -#include "../src/IRsend.h" +#include "IRremoteESP8266.h" +#include "IRsend.h" #ifdef UNIT_TEST -#include "../src/IRsend_test.h" +#include "IRsend_test.h" #endif /// Native representation of a Whirlpool A/C message. diff --git a/lib/IRremoteESP8266/src/ir_Whynter.cpp b/lib/IRremoteESP8266/src/ir_Whynter.cpp new file mode 100644 index 0000000000..7b184eb0be --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_Whynter.cpp @@ -0,0 +1,103 @@ +// Copyright 2009 Ken Shirriff +// Copyright 2017 David Conran + +/// @file +/// @brief Support for Whynter protocols. +/// Whynter A/C ARC-110WD added by Francesco Meschia +/// Whynter originally added from https://github.com/shirriff/Arduino-IRremote/ + +// Supports: +// Brand: Whynter, Model: ARC-110WD A/C + +#include +#include "IRrecv.h" +#include "IRsend.h" +#include "IRutils.h" + +// Constants +const uint16_t kWhynterTick = 50; +const uint16_t kWhynterHdrMarkTicks = 57; +const uint16_t kWhynterHdrMark = kWhynterHdrMarkTicks * kWhynterTick; +const uint16_t kWhynterHdrSpaceTicks = 57; +const uint16_t kWhynterHdrSpace = kWhynterHdrSpaceTicks * kWhynterTick; +const uint16_t kWhynterBitMarkTicks = 15; +const uint16_t kWhynterBitMark = kWhynterBitMarkTicks * kWhynterTick; +const uint16_t kWhynterOneSpaceTicks = 43; +const uint16_t kWhynterOneSpace = kWhynterOneSpaceTicks * kWhynterTick; +const uint16_t kWhynterZeroSpaceTicks = 15; +const uint16_t kWhynterZeroSpace = kWhynterZeroSpaceTicks * kWhynterTick; +const uint16_t kWhynterMinCommandLengthTicks = 2160; // Totally made up value. +const uint32_t kWhynterMinCommandLength = + kWhynterMinCommandLengthTicks * kWhynterTick; +const uint16_t kWhynterMinGapTicks = + kWhynterMinCommandLengthTicks - + (2 * (kWhynterBitMarkTicks + kWhynterZeroSpaceTicks) + + kWhynterBits * (kWhynterBitMarkTicks + kWhynterOneSpaceTicks)); +const uint16_t kWhynterMinGap = kWhynterMinGapTicks * kWhynterTick; + +#if SEND_WHYNTER +/// Send a Whynter message. +/// Status: STABLE +/// @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_Whynter.cpp +void IRsend::sendWhynter(const uint64_t data, const uint16_t nbits, + const uint16_t repeat) { + // Set IR carrier frequency + enableIROut(38); + + for (uint16_t i = 0; i <= repeat; i++) { + // (Pre-)Header + mark(kWhynterBitMark); + space(kWhynterZeroSpace); + sendGeneric( + kWhynterHdrMark, kWhynterHdrSpace, kWhynterBitMark, kWhynterOneSpace, + kWhynterBitMark, kWhynterZeroSpace, kWhynterBitMark, kWhynterMinGap, + kWhynterMinCommandLength - (kWhynterBitMark + kWhynterZeroSpace), data, + nbits, 38, true, 0, // Repeats are already handled. + 50); + } +} +#endif // SEND_WHYNTER + +#if DECODE_WHYNTER +/// Decode the supplied Whynter message. +/// Status: STABLE / Working. Strict mode is ALPHA. +/// @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/z3t0/Arduino-IRremote/blob/master/ir_Whynter.cpp +bool IRrecv::decodeWhynter(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; // We don't have enough entries to possibly match. + + // Compliance + if (strict && nbits != kWhynterBits) + return false; // Incorrect nr. of bits per spec. + + uint64_t data = 0; + // Pre-Header + // Sequence begins with a bit mark and a zero space. + if (!matchMark(results->rawbuf[offset++], kWhynterBitMark)) return false; + if (!matchSpace(results->rawbuf[offset++], kWhynterZeroSpace)) return false; + // Match Main Header + Data + Footer + if (!matchGeneric(results->rawbuf + offset, &data, + results->rawlen - offset, nbits, + kWhynterHdrMark, kWhynterHdrSpace, + kWhynterBitMark, kWhynterOneSpace, + kWhynterBitMark, kWhynterZeroSpace, + kWhynterBitMark, kWhynterMinGap, true)) return false; + // Success + results->decode_type = WHYNTER; + results->bits = nbits; + results->value = data; + results->address = 0; + results->command = 0; + return true; +} +#endif // DECODE_WHYNTER diff --git a/lib/IRremoteESP8266/src/ir_Xmp.cpp b/lib/IRremoteESP8266/src/ir_Xmp.cpp new file mode 100644 index 0000000000..29d7f6c1bb --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_Xmp.cpp @@ -0,0 +1,226 @@ +// Copyright 2021 David Conran + +/// @file +/// @brief Support for XMP protocols. +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1414 +/// @see http://www.hifi-remote.com/wiki/index.php/XMP + +// Supports: +// Brand: Xfinity, Model: XR2 remote +// Brand: Xfinity, Model: XR11 remote + + +#include +#include "IRrecv.h" +#include "IRsend.h" +#include "IRutils.h" + +// Constants +const uint16_t kXmpMark = 210; ///< uSeconds. +const uint16_t kXmpBaseSpace = 760; ///< uSeconds +const uint16_t kXmpSpaceStep = 135; ///< uSeconds +const uint16_t kXmpFooterSpace = 13000; ///< uSeconds. +const uint32_t kXmpMessageGap = 80400; ///< uSeconds. +const uint8_t kXmpWordSize = kNibbleSize; ///< nr. of Bits in a word. +const uint8_t kXmpMaxWordValue = (1 << kXmpWordSize) - 1; // Max word value. +const uint8_t kXmpSections = 2; ///< Nr. of Data sections +const uint8_t kXmpRepeatCode = 0b1000; +const uint8_t kXmpRepeatCodeAlt = 0b1001; + +using irutils::setBits; + +namespace IRXmpUtils { + /// Get the current checksum value from an XMP data section. + /// @param[in] data The value of the data section. + /// @param[in] nbits The number of data bits in the section. + /// @return The value of the stored checksum. + /// @warning Returns 0 if we can't obtain a valid checksum. + uint8_t getSectionChecksum(const uint32_t data, const uint16_t nbits) { + // The checksum is the 2nd most significant nibble of a section. + return (nbits < 2 * kNibbleSize) ? 0 : GETBITS32(data, + nbits - (2 * kNibbleSize), + kNibbleSize); + } + + /// Calculate the correct checksum value for an XMP data section. + /// @param[in] data The value of the data section. + /// @param[in] nbits The number of data bits in the section. + /// @return The value of the correct checksum. + uint8_t calcSectionChecksum(const uint32_t data, const uint16_t nbits) { + return (0xF & ~(irutils::sumNibbles(data, nbits / kNibbleSize, 0xF, false) - + getSectionChecksum(data, nbits))); + } + + /// Recalculate a XMP message code ensuring it has the checksums valid. + /// @param[in] data The value of the XMP message code. + /// @param[in] nbits The number of data bits in the entire message code. + /// @return The corrected XMP message with valid checksum sections. + uint64_t updateChecksums(const uint64_t data, const uint16_t nbits) { + const uint16_t sectionbits = nbits / kXmpSections; + uint64_t result = data; + for (uint16_t sectionOffset = 0; sectionOffset < nbits; + sectionOffset += sectionbits) { + const uint16_t checksumOffset = sectionOffset + sectionbits - + (2 * kNibbleSize); + setBits(&result, checksumOffset, kNibbleSize, + calcSectionChecksum(GETBITS64(data, sectionOffset, sectionbits), + sectionbits)); + } + return result; + } + + /// Calculate the bit offset the repeat nibble in an XMP code. + /// @param[in] nbits The number of data bits in the entire message code. + /// @return The offset to the start of the XMP repeat nibble. + uint16_t calcRepeatOffset(const uint16_t nbits) { + return (nbits < 3 * kNibbleSize) ? 0 + : (nbits / kXmpSections) - + (3 * kNibbleSize); + } + + /// Test if an XMP message code is a repeat or not. + /// @param[in] data The value of the XMP message code. + /// @param[in] nbits The number of data bits in the entire message code. + /// @return true, if it looks like a repeat, false if not. + bool isRepeat(const uint64_t data, const uint16_t nbits) { + switch (GETBITS64(data, calcRepeatOffset(nbits), kNibbleSize)) { + case kXmpRepeatCode: + case kXmpRepeatCodeAlt: + return true; + default: + return false; + } + } + + /// Adjust an XMP message code to make it a valid repeat or non-repeat code. + /// @param[in] data The value of the XMP message code. + /// @param[in] nbits The number of data bits in the entire message code. + /// @param[in] repeat_code The value of the XMP repeat nibble to use. + /// A value of `8` is the normal value for a repeat. `9` has also been seen. + /// A value of `0` will convert the code to a non-repeat code. + /// @return The valud of the modified XMP code. + uint64_t adjustRepeat(const uint64_t data, const uint16_t nbits, + const uint8_t repeat_code) { + uint64_t result = data; + setBits(&result, calcRepeatOffset(nbits), kNibbleSize, repeat_code); + return updateChecksums(result, nbits); + } +} // namespace IRXmpUtils + +using IRXmpUtils::calcSectionChecksum; +using IRXmpUtils::getSectionChecksum; +using IRXmpUtils::isRepeat; +using IRXmpUtils::adjustRepeat; + + +#if SEND_XMP +/// Send a XMP packet. +/// Status: Beta / Untested 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::sendXmp(const uint64_t data, const uint16_t nbits, + const uint16_t repeat) { + enableIROut(38000); + if (nbits < 2 * kXmpWordSize) return; // Too small to send, abort! + uint64_t send_data = data; + for (uint16_t r = 0; r <= repeat; r++) { + uint16_t bits_so_far = kXmpWordSize; + for (uint64_t mask = ((uint64_t)kXmpMaxWordValue) << (nbits - kXmpWordSize); + mask; + mask >>= kXmpWordSize) { + uint8_t word = (send_data & mask) >> (nbits - bits_so_far); + mark(kXmpMark); + space(kXmpBaseSpace + word * kXmpSpaceStep); + bits_so_far += kXmpWordSize; + // Are we at a data section boundary? + if ((bits_so_far - kXmpWordSize) % (nbits / kXmpSections) == 0) { // Yes. + mark(kXmpMark); + space(kXmpFooterSpace); + } + } + space(kXmpMessageGap - kXmpFooterSpace); + + // Modify the value if needed, to make it into a valid repeat code. + if (!isRepeat(send_data, nbits)) + send_data = adjustRepeat(send_data, nbits, kXmpRepeatCode); + } +} +#endif // SEND_XMP + +#if DECODE_XMP +/// Decode the supplied XMP packet/message. +/// Status: BETA / Probably works. +/// @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::decodeXmp(decode_results *results, uint16_t offset, + const uint16_t nbits, const bool strict) { + uint64_t data = 0; + + if (results->rawlen < 2 * (nbits / kXmpWordSize) + (kXmpSections * kFooter) + + offset - 1) + return false; // Not enough entries to ever be XMP. + + // Compliance + if (strict && nbits != kXmpBits) return false; + + // Data + // Sections + for (uint8_t section = 1; section <= kXmpSections; section++) { + for (uint16_t bits_so_far = 0; bits_so_far < nbits / kXmpSections; + bits_so_far += kXmpWordSize) { + if (!matchMarkRange(results->rawbuf[offset++], kXmpMark)) return 0; + uint8_t value = 0; + bool found = false; + for (; value <= kXmpMaxWordValue; value++) { + if (matchSpaceRange(results->rawbuf[offset], + kXmpBaseSpace + value * kXmpSpaceStep, + kXmpSpaceStep / 2, 0)) { + found = true; + break; + } + } + if (!found) return 0; // Failure. + data <<= kXmpWordSize; + data += value; + offset++; + } + // Section Footer + if (!matchMarkRange(results->rawbuf[offset++], kXmpMark)) return 0; + if (section < kXmpSections) { + if (!matchSpace(results->rawbuf[offset++], kXmpFooterSpace)) return 0; + } else { // Last section + if (offset < results->rawlen && + !matchAtLeast(results->rawbuf[offset++], kXmpFooterSpace)) return 0; + } + } + + // Compliance + if (strict) { + // Validate checksums. + uint64_t checksum_data = data; + const uint16_t section_size = nbits / kXmpSections; + // Each section has a checksum. + for (uint16_t section = 0; section < kXmpSections; section++) { + if (getSectionChecksum(checksum_data, section_size) != + calcSectionChecksum(checksum_data, section_size)) + return 0; + checksum_data >>= section_size; + } + } + + // Success + results->value = data; + results->decode_type = decode_type_t::XMP; + results->bits = nbits; + results->address = 0; + results->command = 0; + // See if it is a repeat message. + results->repeat = isRepeat(data, nbits); + return true; +} +#endif // DECODE_XMP diff --git a/lib/IRremoteESP8266/src/ir_Zepeal.cpp b/lib/IRremoteESP8266/src/ir_Zepeal.cpp new file mode 100644 index 0000000000..96017226c3 --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_Zepeal.cpp @@ -0,0 +1,94 @@ +// Copyright 2020 Christian Nilsson (nikize) + +/// @file +/// @brief Support for Zepeal protocol. +/// This protocol uses fixed length bit encoding. +/// Most official information about Zepeal seems to be from Denkyosha +/// @see https://www.denkyosha.co.jp/ + +// Supports: +// Brand: Zepeal, Model: DRT-A3311(BG) floor fan +// Brand: Zepeal, Model: DRT-A3311(BG) 5 button remote + +#include "IRrecv.h" +#include "IRsend.h" +#include "IRutils.h" + +// Constants + +const uint16_t kZepealHdrMark = 2330; +const uint16_t kZepealHdrSpace = 3380; +const uint16_t kZepealOneMark = 1300; +const uint16_t kZepealZeroMark = 420; +const uint16_t kZepealOneSpace = kZepealZeroMark; +const uint16_t kZepealZeroSpace = kZepealOneMark; +const uint16_t kZepealFooterMark = 420; +const uint16_t kZepealGap = 6750; + +const uint8_t kZepealTolerance = 40; + +// Signature limits possible false possitvies, +// but might need change (removal) if more devices are detected +const uint8_t kZepealSignature = 0x6C; + +// Known Zepeal DRT-A3311(BG) Buttons - documentation rather than actual usage +const uint16_t kZepealCommandSpeed = 0x6C82; +const uint16_t kZepealCommandOffOn = 0x6C81; +const uint16_t kZepealCommandRhythm = 0x6C84; +const uint16_t kZepealCommandOffTimer = 0x6C88; +const uint16_t kZepealCommandOnTimer = 0x6CC3; + +#if SEND_ZEPEAL +/// Send a Zepeal formatted message. +/// Status: STABLE / Works on 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::sendZepeal(const uint64_t data, const uint16_t nbits, + const uint16_t repeat) { + sendGeneric(kZepealHdrMark, kZepealHdrSpace, + kZepealOneMark, kZepealOneSpace, + kZepealZeroMark, kZepealZeroSpace, + kZepealFooterMark, kZepealGap, + data, nbits, 38, true, repeat, kDutyDefault); +} +#endif // SEND_ZEPEAL + +#if DECODE_ZEPEAL +/// Decode the supplied Zepeal 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. Typically kZepealBits. +/// @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::decodeZepeal(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 != kZepealBits) + return false; // Not strictly a message. + + uint64_t data = 0; + uint16_t used; + used = matchGeneric(results->rawbuf + offset, &data, + results->rawlen - offset, nbits, + kZepealHdrMark, kZepealHdrSpace, + kZepealOneMark, kZepealOneSpace, + kZepealZeroMark, kZepealZeroSpace, + kZepealFooterMark, kZepealGap, true, + kZepealTolerance); + if (!used) return false; + if (strict && (data >> 8) != kZepealSignature) return false; + + // Success + results->value = data; + results->decode_type = decode_type_t::ZEPEAL; + results->bits = nbits; + results->address = 0; + results->command = 0; + return true; +} +#endif // DECODE_ZEPEAL diff --git a/lib/IRremoteESP8266/test/IRac_test.cpp b/lib/IRremoteESP8266/test/IRac_test.cpp index 6a2ddd692a..b2eff6efa7 100644 --- a/lib/IRremoteESP8266/test/IRac_test.cpp +++ b/lib/IRremoteESP8266/test/IRac_test.cpp @@ -1,44 +1,44 @@ // Copyright 2019-2021 David Conran #include -#include "../src/ir_Airwell.h" -#include "../src/ir_Amcor.h" -#include "../src/ir_Argo.h" -#include "../src/ir_Carrier.h" -#include "../src/ir_Coolix.h" -#include "../src/ir_Corona.h" -#include "../src/ir_Daikin.h" -#include "../src/ir_Delonghi.h" -#include "../src/ir_Ecoclim.h" -#include "../src/ir_Electra.h" -#include "../src/ir_Fujitsu.h" -#include "../src/ir_Goodweather.h" -#include "../src/ir_Gree.h" -#include "../src/ir_Haier.h" -#include "../src/ir_Hitachi.h" -#include "../src/ir_Kelvinator.h" -#include "../src/ir_LG.h" -#include "../src/ir_Midea.h" -#include "../src/ir_Mitsubishi.h" -#include "../src/ir_MitsubishiHeavy.h" -#include "../src/ir_Neoclima.h" -#include "../src/ir_Panasonic.h" -#include "../src/ir_Samsung.h" -#include "../src/ir_Sharp.h" -#include "../src/ir_Tcl.h" -#include "../src/ir_Teco.h" -#include "../src/ir_Toshiba.h" -#include "../src/ir_Trotec.h" -#include "../src/ir_Truma.h" -#include "../src/ir_Vestel.h" -#include "../src/ir_Voltas.h" -#include "../src/ir_Whirlpool.h" -#include "../src/IRac.h" -#include "../src/IRrecv.h" -#include "../src/IRrecv_test.h" -#include "../src/IRremoteESP8266.h" -#include "../src/IRsend.h" -#include "../src/IRsend_test.h" +#include "ir_Airwell.h" +#include "ir_Amcor.h" +#include "ir_Argo.h" +#include "ir_Carrier.h" +#include "ir_Coolix.h" +#include "ir_Corona.h" +#include "ir_Daikin.h" +#include "ir_Delonghi.h" +#include "ir_Ecoclim.h" +#include "ir_Electra.h" +#include "ir_Fujitsu.h" +#include "ir_Goodweather.h" +#include "ir_Gree.h" +#include "ir_Haier.h" +#include "ir_Hitachi.h" +#include "ir_Kelvinator.h" +#include "ir_LG.h" +#include "ir_Midea.h" +#include "ir_Mitsubishi.h" +#include "ir_MitsubishiHeavy.h" +#include "ir_Neoclima.h" +#include "ir_Panasonic.h" +#include "ir_Samsung.h" +#include "ir_Sharp.h" +#include "ir_Tcl.h" +#include "ir_Teco.h" +#include "ir_Toshiba.h" +#include "ir_Trotec.h" +#include "ir_Truma.h" +#include "ir_Vestel.h" +#include "ir_Voltas.h" +#include "ir_Whirlpool.h" +#include "IRac.h" +#include "IRrecv.h" +#include "IRrecv_test.h" +#include "IRremoteESP8266.h" +#include "IRsend.h" +#include "IRsend_test.h" #include "gtest/gtest.h" // Tests for IRac class. diff --git a/lib/IRremoteESP8266/test/IRrecv_test.cpp b/lib/IRremoteESP8266/test/IRrecv_test.cpp index aada6aa82b..2d32847d38 100644 --- a/lib/IRremoteESP8266/test/IRrecv_test.cpp +++ b/lib/IRremoteESP8266/test/IRrecv_test.cpp @@ -1,10 +1,10 @@ // Copyright 2017 David Conran -#include "../src/IRrecv_test.h" -#include "../src/IRrecv.h" -#include "../src/IRremoteESP8266.h" -#include "../src/IRsend.h" -#include "../src/IRsend_test.h" +#include "IRrecv_test.h" +#include "IRrecv.h" +#include "IRremoteESP8266.h" +#include "IRsend.h" +#include "IRsend_test.h" #include "gtest/gtest.h" // Tests for the IRrecv object. diff --git a/lib/IRremoteESP8266/test/IRrecv_test.h b/lib/IRremoteESP8266/test/IRrecv_test.h index e4e0e70954..bb366c1ee0 100644 --- a/lib/IRremoteESP8266/test/IRrecv_test.h +++ b/lib/IRremoteESP8266/test/IRrecv_test.h @@ -6,7 +6,7 @@ #include #include #include -#include "../src/IRutils.h" +#include "IRutils.h" #define EXPECT_STATE_EQ(a, b, c) \ for (uint8_t i = 0; i < c / 8; ++i) { \ diff --git a/lib/IRremoteESP8266/test/IRsend_test.cpp b/lib/IRremoteESP8266/test/IRsend_test.cpp index ffd230d2b1..51fae74996 100644 --- a/lib/IRremoteESP8266/test/IRsend_test.cpp +++ b/lib/IRremoteESP8266/test/IRsend_test.cpp @@ -1,9 +1,9 @@ // Copyright 2017,2019 David Conran -#include "../src/IRsend_test.h" -#include "../src/IRrecv_test.h" -#include "../src/IRsend.h" -#include "../src/IRutils.h" +#include "IRsend_test.h" +#include "IRrecv_test.h" +#include "IRsend.h" +#include "IRutils.h" #include "gtest/gtest.h" // Tests sendData(). diff --git a/lib/IRremoteESP8266/test/IRsend_test.h b/lib/IRremoteESP8266/test/IRsend_test.h index ec900a44e9..f434094332 100644 --- a/lib/IRremoteESP8266/test/IRsend_test.h +++ b/lib/IRremoteESP8266/test/IRsend_test.h @@ -8,9 +8,9 @@ #include #include #include -#include "../src/IRrecv.h" -#include "../src/IRsend.h" -#include "../src/IRtimer.h" +#include "IRrecv.h" +#include "IRsend.h" +#include "IRtimer.h" #define OUTPUT_BUF 10000U #define RAW_BUF 10000U diff --git a/lib/IRremoteESP8266/test/IRutils_test.cpp b/lib/IRremoteESP8266/test/IRutils_test.cpp index 8f398f7650..73ba569991 100644 --- a/lib/IRremoteESP8266/test/IRutils_test.cpp +++ b/lib/IRremoteESP8266/test/IRutils_test.cpp @@ -1,11 +1,11 @@ // Copyright 2017-2019 David Conran -#include "../src/IRutils.h" +#include "IRutils.h" #include -#include "../src/IRrecv.h" -#include "../src/IRrecv_test.h" -#include "../src/IRsend.h" -#include "../src/IRsend_test.h" +#include "IRrecv.h" +#include "IRrecv_test.h" +#include "IRsend.h" +#include "IRsend_test.h" #include "gtest/gtest.h" // Tests reverseBits(). diff --git a/lib/IRremoteESP8266/test/ir_Airwell_test.cpp b/lib/IRremoteESP8266/test/ir_Airwell_test.cpp index c9d14f36dd..e5f28d4df4 100644 --- a/lib/IRremoteESP8266/test/ir_Airwell_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Airwell_test.cpp @@ -1,11 +1,11 @@ // Copyright 2020 David Conran -#include "../src/ir_Airwell.h" -#include "../src/IRac.h" -#include "../src/IRrecv.h" -#include "../src/IRrecv_test.h" -#include "../src/IRsend.h" -#include "../src/IRsend_test.h" +#include "ir_Airwell.h" +#include "IRac.h" +#include "IRrecv.h" +#include "IRrecv_test.h" +#include "IRsend.h" +#include "IRsend_test.h" #include "gtest/gtest.h" // Tests for decodeAirwell(). diff --git a/lib/IRremoteESP8266/test/ir_Aiwa_test.cpp b/lib/IRremoteESP8266/test/ir_Aiwa_test.cpp index 78e3e33b03..f87d5c5e28 100644 --- a/lib/IRremoteESP8266/test/ir_Aiwa_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Aiwa_test.cpp @@ -1,7 +1,7 @@ // Copyright 2017 David Conran -#include "../src/IRsend.h" -#include "../src/IRsend_test.h" +#include "IRsend.h" +#include "IRsend_test.h" #include "gtest/gtest.h" // Tests for sendAiwaRCT501(). diff --git a/lib/IRremoteESP8266/test/ir_Amcor_test.cpp b/lib/IRremoteESP8266/test/ir_Amcor_test.cpp index e8ba1eb3bc..787769516d 100644 --- a/lib/IRremoteESP8266/test/ir_Amcor_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Amcor_test.cpp @@ -1,12 +1,12 @@ // Copyright 2019 David Conran -#include "../src/IRac.h" -#include "../src/ir_Amcor.h" -#include "../src/IRrecv.h" -#include "../src/IRrecv_test.h" -#include "../src/IRsend.h" -#include "../src/IRsend_test.h" -#include "../src/IRutils.h" +#include "IRac.h" +#include "ir_Amcor.h" +#include "IRrecv.h" +#include "IRrecv_test.h" +#include "IRsend.h" +#include "IRsend_test.h" +#include "IRutils.h" #include "gtest/gtest.h" TEST(TestUtils, Housekeeping) { diff --git a/lib/IRremoteESP8266/test/ir_Argo_test.cpp b/lib/IRremoteESP8266/test/ir_Argo_test.cpp index ea8e16ab7d..3ab890adca 100644 --- a/lib/IRremoteESP8266/test/ir_Argo_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Argo_test.cpp @@ -1,10 +1,10 @@ // Copyright 2019 David Conran -#include "../src/ir_Argo.h" -#include "../src/IRac.h" -#include "../src/IRrecv.h" -#include "../src/IRrecv_test.h" -#include "../src/IRsend.h" -#include "../src/IRsend_test.h" +#include "ir_Argo.h" +#include "IRac.h" +#include "IRrecv.h" +#include "IRrecv_test.h" +#include "IRsend.h" +#include "IRsend_test.h" #include "gtest/gtest.h" diff --git a/lib/IRremoteESP8266/test/ir_Arris_test.cpp b/lib/IRremoteESP8266/test/ir_Arris_test.cpp index ab30f3046c..2a219b6b22 100644 --- a/lib/IRremoteESP8266/test/ir_Arris_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Arris_test.cpp @@ -1,10 +1,10 @@ // Copyright 2021 David Conran -#include "../src/IRac.h" -#include "../src/IRrecv.h" -#include "../src/IRrecv_test.h" -#include "../src/IRsend.h" -#include "../src/IRsend_test.h" +#include "IRac.h" +#include "IRrecv.h" +#include "IRrecv_test.h" +#include "IRsend.h" +#include "IRsend_test.h" #include "gtest/gtest.h" // Tests for decodeArris(). diff --git a/lib/IRremoteESP8266/test/ir_Bose_test.cpp b/lib/IRremoteESP8266/test/ir_Bose_test.cpp index 80b3204fe8..bf3e18c910 100644 --- a/lib/IRremoteESP8266/test/ir_Bose_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Bose_test.cpp @@ -1,11 +1,11 @@ // Copyright 2021 parsnip42 // Copyright 2021 David Conran -#include "../src/IRac.h" -#include "../src/IRrecv.h" -#include "../src/IRrecv_test.h" -#include "../src/IRsend.h" -#include "../src/IRsend_test.h" +#include "IRac.h" +#include "IRrecv.h" +#include "IRrecv_test.h" +#include "IRsend.h" +#include "IRsend_test.h" #include "gtest/gtest.h" diff --git a/lib/IRremoteESP8266/test/ir_Carrier_test.cpp b/lib/IRremoteESP8266/test/ir_Carrier_test.cpp index 10cc435aab..caf7e46d84 100644 --- a/lib/IRremoteESP8266/test/ir_Carrier_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Carrier_test.cpp @@ -1,10 +1,10 @@ // Copyright 2018, 2020 David Conran -#include "../src/ir_Carrier.h" -#include "../src/IRac.h" -#include "../src/IRrecv.h" -#include "../src/IRsend.h" -#include "../src/IRsend_test.h" +#include "ir_Carrier.h" +#include "IRac.h" +#include "IRrecv.h" +#include "IRsend.h" +#include "IRsend_test.h" #include "gtest/gtest.h" // Tests for sendCarrierAC() diff --git a/lib/IRremoteESP8266/test/ir_Coolix_test.cpp b/lib/IRremoteESP8266/test/ir_Coolix_test.cpp index ada4caf450..46c5548820 100644 --- a/lib/IRremoteESP8266/test/ir_Coolix_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Coolix_test.cpp @@ -1,9 +1,9 @@ // Copyright 2017-2018 David Conran -#include "../src/ir_Coolix.h" -#include "../src/IRac.h" -#include "../src/IRsend.h" -#include "../src/IRsend_test.h" +#include "ir_Coolix.h" +#include "IRac.h" +#include "IRsend.h" +#include "IRsend_test.h" #include "gtest/gtest.h" // Tests for sendCOOLIX(). diff --git a/lib/IRremoteESP8266/test/ir_Corona_test.cpp b/lib/IRremoteESP8266/test/ir_Corona_test.cpp index 72c7758ea0..9c16e1f963 100644 --- a/lib/IRremoteESP8266/test/ir_Corona_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Corona_test.cpp @@ -1,11 +1,11 @@ // Copyright 2020 Christian Nilsson -#include "../src/ir_Corona.h" -#include "../src/IRac.h" -#include "../src/IRrecv.h" -#include "../src/IRrecv_test.h" -#include "../src/IRsend.h" -#include "../src/IRsend_test.h" +#include "ir_Corona.h" +#include "IRac.h" +#include "IRrecv.h" +#include "IRrecv_test.h" +#include "IRsend.h" +#include "IRsend_test.h" #include "gtest/gtest.h" // Tests for decodeCoronaAc(). diff --git a/lib/IRremoteESP8266/test/ir_Daikin_test.cpp b/lib/IRremoteESP8266/test/ir_Daikin_test.cpp index eadb508ccd..783bd4e66d 100644 --- a/lib/IRremoteESP8266/test/ir_Daikin_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Daikin_test.cpp @@ -1,10 +1,10 @@ // Copyright 2017-2019 David Conran -#include "../src/ir_Daikin.h" -#include "../src/IRac.h" -#include "../src/IRrecv.h" -#include "../src/IRrecv_test.h" -#include "../src/IRsend.h" -#include "../src/IRsend_test.h" +#include "ir_Daikin.h" +#include "IRac.h" +#include "IRrecv.h" +#include "IRrecv_test.h" +#include "IRsend.h" +#include "IRsend_test.h" #include "gtest/gtest.h" // Tests for sendDaikin(). diff --git a/lib/IRremoteESP8266/test/ir_Delonghi_test.cpp b/lib/IRremoteESP8266/test/ir_Delonghi_test.cpp index fd5a888248..9f069e40ec 100644 --- a/lib/IRremoteESP8266/test/ir_Delonghi_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Delonghi_test.cpp @@ -1,12 +1,12 @@ // Copyright 2020 David Conran -#include "../src/IRac.h" -#include "../src/ir_Delonghi.h" -#include "../src/IRrecv.h" -#include "../src/IRrecv_test.h" -#include "../src/IRsend.h" -#include "../src/IRsend_test.h" -#include "../src/IRutils.h" +#include "IRac.h" +#include "ir_Delonghi.h" +#include "IRrecv.h" +#include "IRrecv_test.h" +#include "IRsend.h" +#include "IRsend_test.h" +#include "IRutils.h" #include "gtest/gtest.h" TEST(TestUtils, Housekeeping) { diff --git a/lib/IRremoteESP8266/test/ir_Denon_test.cpp b/lib/IRremoteESP8266/test/ir_Denon_test.cpp index 0f2a4fded8..64be43f97e 100644 --- a/lib/IRremoteESP8266/test/ir_Denon_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Denon_test.cpp @@ -1,7 +1,7 @@ // Copyright 2017 David Conran -#include "../src/IRsend.h" -#include "../src/IRsend_test.h" +#include "IRsend.h" +#include "IRsend_test.h" #include "gtest/gtest.h" // Tests for sendDenon(). diff --git a/lib/IRremoteESP8266/test/ir_Dish_test.cpp b/lib/IRremoteESP8266/test/ir_Dish_test.cpp index 4c6cb5c895..d745648111 100644 --- a/lib/IRremoteESP8266/test/ir_Dish_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Dish_test.cpp @@ -1,7 +1,7 @@ // Copyright 2017 David Conran -#include "../src/IRsend.h" -#include "../src/IRsend_test.h" +#include "IRsend.h" +#include "IRsend_test.h" #include "gtest/gtest.h" // Tests for sendDISH(). diff --git a/lib/IRremoteESP8266/test/ir_Doshisha_test.cpp b/lib/IRremoteESP8266/test/ir_Doshisha_test.cpp index 3ee6ee11e6..8c69bfbb3b 100644 --- a/lib/IRremoteESP8266/test/ir_Doshisha_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Doshisha_test.cpp @@ -1,10 +1,10 @@ // Copyright 2020 Christian Nilsson -#include "../src/IRac.h" -#include "../src/IRrecv.h" -#include "../src/IRrecv_test.h" -#include "../src/IRsend.h" -#include "../src/IRsend_test.h" +#include "IRac.h" +#include "IRrecv.h" +#include "IRrecv_test.h" +#include "IRsend.h" +#include "IRsend_test.h" #include "gtest/gtest.h" // Tests for decodeDoshisha(). diff --git a/lib/IRremoteESP8266/test/ir_Ecoclim_test.cpp b/lib/IRremoteESP8266/test/ir_Ecoclim_test.cpp index 2cc2a662bb..fe031e4e27 100644 --- a/lib/IRremoteESP8266/test/ir_Ecoclim_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Ecoclim_test.cpp @@ -1,13 +1,13 @@ // Copyright 2021 David Conran -#include "../src/ir_Ecoclim.h" +#include "ir_Ecoclim.h" #include -#include "../src/IRac.h" -#include "../src/IRrecv.h" -#include "../src/IRrecv_test.h" -#include "../src/IRsend.h" -#include "../src/IRsend_test.h" -#include "../src/IRutils.h" +#include "IRac.h" +#include "IRrecv.h" +#include "IRrecv_test.h" +#include "IRsend.h" +#include "IRsend_test.h" +#include "IRutils.h" #include "gtest/gtest.h" TEST(TestUtils, Housekeeping) { diff --git a/lib/IRremoteESP8266/test/ir_Electra_test.cpp b/lib/IRremoteESP8266/test/ir_Electra_test.cpp index d75c75e27c..1fddf537ba 100644 --- a/lib/IRremoteESP8266/test/ir_Electra_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Electra_test.cpp @@ -1,12 +1,12 @@ // Copyright 2018, 2019 David Conran -#include "../src/ir_Electra.h" +#include "ir_Electra.h" #include -#include "../src/IRac.h" -#include "../src/IRrecv.h" -#include "../src/IRrecv_test.h" -#include "../src/IRsend.h" -#include "../src/IRsend_test.h" +#include "IRac.h" +#include "IRrecv.h" +#include "IRrecv_test.h" +#include "IRsend.h" +#include "IRsend_test.h" #include "gtest/gtest.h" // Tests for sendElectraAC(). diff --git a/lib/IRremoteESP8266/test/ir_EliteScreens_test.cpp b/lib/IRremoteESP8266/test/ir_EliteScreens_test.cpp index 7bcda48c25..0af0f1f552 100644 --- a/lib/IRremoteESP8266/test/ir_EliteScreens_test.cpp +++ b/lib/IRremoteESP8266/test/ir_EliteScreens_test.cpp @@ -1,8 +1,8 @@ // Copyright 2017 David Conran -#include "../src/IRac.h" -#include "../src/IRsend.h" -#include "../src/IRsend_test.h" +#include "IRac.h" +#include "IRsend.h" +#include "IRsend_test.h" #include "gtest/gtest.h" // Housekeeping tests diff --git a/lib/IRremoteESP8266/test/ir_Epson_test.cpp b/lib/IRremoteESP8266/test/ir_Epson_test.cpp index 86c0cc185b..d1df3f9acc 100644 --- a/lib/IRremoteESP8266/test/ir_Epson_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Epson_test.cpp @@ -1,10 +1,10 @@ // Copyright 2020 David Conran -#include "../src/IRac.h" -#include "../src/IRrecv.h" -#include "../src/IRrecv_test.h" -#include "../src/IRsend.h" -#include "../src/IRsend_test.h" +#include "IRac.h" +#include "IRrecv.h" +#include "IRrecv_test.h" +#include "IRsend.h" +#include "IRsend_test.h" #include "gtest/gtest.h" // Tests for sendEpson(). diff --git a/lib/IRremoteESP8266/test/ir_Fujitsu_test.cpp b/lib/IRremoteESP8266/test/ir_Fujitsu_test.cpp index c05a2e5a4c..caf4e53f1f 100644 --- a/lib/IRremoteESP8266/test/ir_Fujitsu_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Fujitsu_test.cpp @@ -1,9 +1,9 @@ // Copyright 2017 Jonny Graham, David Conran -#include "../src/IRac.h" -#include "../src/IRrecv_test.h" -#include "../src/IRsend.h" -#include "../src/IRsend_test.h" -#include "../src/ir_Fujitsu.h" +#include "IRac.h" +#include "IRrecv_test.h" +#include "IRsend.h" +#include "IRsend_test.h" +#include "ir_Fujitsu.h" #include "gtest/gtest.h" // Tests for Fujitsu A/C methods. diff --git a/lib/IRremoteESP8266/test/ir_GICable_test.cpp b/lib/IRremoteESP8266/test/ir_GICable_test.cpp index e58f14bccc..234a748b5c 100644 --- a/lib/IRremoteESP8266/test/ir_GICable_test.cpp +++ b/lib/IRremoteESP8266/test/ir_GICable_test.cpp @@ -1,7 +1,7 @@ // Copyright 2018 David Conran -#include "../src/IRsend.h" -#include "../src/IRsend_test.h" +#include "IRsend.h" +#include "IRsend_test.h" #include "gtest/gtest.h" // Tests for sendGICable(). diff --git a/lib/IRremoteESP8266/test/ir_GlobalCache_test.cpp b/lib/IRremoteESP8266/test/ir_GlobalCache_test.cpp index c4d492ec0e..00742aedac 100644 --- a/lib/IRremoteESP8266/test/ir_GlobalCache_test.cpp +++ b/lib/IRremoteESP8266/test/ir_GlobalCache_test.cpp @@ -1,7 +1,7 @@ // Copyright 2017 David Conran -#include "../src/IRsend.h" -#include "../src/IRsend_test.h" +#include "IRsend.h" +#include "IRsend_test.h" #include "gtest/gtest.h" // Tests for sendGlobalCache(). diff --git a/lib/IRremoteESP8266/test/ir_Goodweather_test.cpp b/lib/IRremoteESP8266/test/ir_Goodweather_test.cpp index 9dbb729f09..be07b9d093 100644 --- a/lib/IRremoteESP8266/test/ir_Goodweather_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Goodweather_test.cpp @@ -1,11 +1,11 @@ // Copyright 2019 David Conran -#include "../src/ir_Goodweather.h" -#include "../src/IRac.h" -#include "../src/IRrecv.h" -#include "../src/IRrecv_test.h" -#include "../src/IRsend.h" -#include "../src/IRsend_test.h" +#include "ir_Goodweather.h" +#include "IRac.h" +#include "IRrecv.h" +#include "IRrecv_test.h" +#include "IRsend.h" +#include "IRsend_test.h" #include "gtest/gtest.h" TEST(TestIRUtils, Goodweather) { diff --git a/lib/IRremoteESP8266/test/ir_Gree_test.cpp b/lib/IRremoteESP8266/test/ir_Gree_test.cpp index de9bb9aa52..597fd3c09d 100644 --- a/lib/IRremoteESP8266/test/ir_Gree_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Gree_test.cpp @@ -1,12 +1,12 @@ // Copyright 2017 David Conran -#include "../src/ir_Gree.h" -#include "../src/IRac.h" -#include "../src/IRrecv.h" -#include "../src/IRrecv_test.h" -#include "../src/IRremoteESP8266.h" -#include "../src/IRsend.h" -#include "../src/IRsend_test.h" +#include "ir_Gree.h" +#include "IRac.h" +#include "IRrecv.h" +#include "IRrecv_test.h" +#include "IRremoteESP8266.h" +#include "IRsend.h" +#include "IRsend_test.h" #include "gtest/gtest.h" // Tests for sendGree(). diff --git a/lib/IRremoteESP8266/test/ir_Haier_test.cpp b/lib/IRremoteESP8266/test/ir_Haier_test.cpp index 0e4fba2d03..0c63b089f7 100644 --- a/lib/IRremoteESP8266/test/ir_Haier_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Haier_test.cpp @@ -1,11 +1,11 @@ // Copyright 2018 David Conran -#include "../src/ir_Haier.h" -#include "../src/IRac.h" -#include "../src/IRrecv.h" -#include "../src/IRrecv_test.h" -#include "../src/IRsend.h" -#include "../src/IRsend_test.h" +#include "ir_Haier.h" +#include "IRac.h" +#include "IRrecv.h" +#include "IRrecv_test.h" +#include "IRsend.h" +#include "IRsend_test.h" #include "gtest/gtest.h" // Tests for sendHaierAC() diff --git a/lib/IRremoteESP8266/test/ir_Hitachi_test.cpp b/lib/IRremoteESP8266/test/ir_Hitachi_test.cpp index bff5f350c1..2bbbd29e91 100644 --- a/lib/IRremoteESP8266/test/ir_Hitachi_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Hitachi_test.cpp @@ -1,12 +1,12 @@ // Copyright 2018 David Conran -#include "../src/ir_Hitachi.h" -#include "../src/IRac.h" -#include "../src/IRrecv.h" -#include "../src/IRrecv_test.h" -#include "../src/IRremoteESP8266.h" -#include "../src/IRsend.h" -#include "../src/IRsend_test.h" +#include "ir_Hitachi.h" +#include "IRac.h" +#include "IRrecv.h" +#include "IRrecv_test.h" +#include "IRremoteESP8266.h" +#include "IRsend.h" +#include "IRsend_test.h" #include "gtest/gtest.h" // Tests for sendHitachiAC(). diff --git a/lib/IRremoteESP8266/test/ir_Inax_test.cpp b/lib/IRremoteESP8266/test/ir_Inax_test.cpp index e9523954d0..b182cce32e 100644 --- a/lib/IRremoteESP8266/test/ir_Inax_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Inax_test.cpp @@ -1,8 +1,8 @@ // Copyright 2019 crankyoldgit (David Conran) -#include "../src/IRsend.h" -#include "../src/IRsend_test.h" -#include "../src/IRutils.h" +#include "IRsend.h" +#include "IRsend_test.h" +#include "IRutils.h" #include "gtest/gtest.h" diff --git a/lib/IRremoteESP8266/test/ir_JVC_test.cpp b/lib/IRremoteESP8266/test/ir_JVC_test.cpp index 3ab51d2101..51b16b82ce 100644 --- a/lib/IRremoteESP8266/test/ir_JVC_test.cpp +++ b/lib/IRremoteESP8266/test/ir_JVC_test.cpp @@ -1,7 +1,7 @@ // Copyright 2017 David Conran -#include "../src/IRsend.h" -#include "../src/IRsend_test.h" +#include "IRsend.h" +#include "IRsend_test.h" #include "gtest/gtest.h" // Tests for sendJVC(). diff --git a/lib/IRremoteESP8266/test/ir_Kelon_test.cpp b/lib/IRremoteESP8266/test/ir_Kelon_test.cpp index 5e02c26235..7212d49ee5 100644 --- a/lib/IRremoteESP8266/test/ir_Kelon_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Kelon_test.cpp @@ -1,10 +1,10 @@ // Copyright 2021 Davide Depau -#include "../src/IRac.h" -#include "../src/IRrecv.h" -#include "../src/IRrecv_test.h" -#include "../src/IRsend.h" -#include "../src/IRsend_test.h" +#include "IRac.h" +#include "IRrecv.h" +#include "IRrecv_test.h" +#include "IRsend.h" +#include "IRsend_test.h" #include "gtest/gtest.h" // Tests for sendKelon(). diff --git a/lib/IRremoteESP8266/test/ir_Kelvinator_test.cpp b/lib/IRremoteESP8266/test/ir_Kelvinator_test.cpp index a7b4552573..73ad6581d8 100644 --- a/lib/IRremoteESP8266/test/ir_Kelvinator_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Kelvinator_test.cpp @@ -1,12 +1,12 @@ // Copyright 2017 David Conran -#include "../src/ir_Kelvinator.h" -#include "../src/IRac.h" -#include "../src/IRrecv.h" -#include "../src/IRrecv_test.h" -#include "../src/IRremoteESP8266.h" -#include "../src/IRsend.h" -#include "../src/IRsend_test.h" +#include "ir_Kelvinator.h" +#include "IRac.h" +#include "IRrecv.h" +#include "IRrecv_test.h" +#include "IRremoteESP8266.h" +#include "IRsend.h" +#include "IRsend_test.h" #include "gtest/gtest.h" // Tests for sendKelvinator(). diff --git a/lib/IRremoteESP8266/test/ir_LG_test.cpp b/lib/IRremoteESP8266/test/ir_LG_test.cpp index 54c4bcfafb..808f7b83af 100644 --- a/lib/IRremoteESP8266/test/ir_LG_test.cpp +++ b/lib/IRremoteESP8266/test/ir_LG_test.cpp @@ -1,9 +1,9 @@ // Copyright 2017, 2019 David Conran -#include "../src/ir_LG.h" -#include "../src/IRac.h" -#include "../src/IRsend.h" -#include "../src/IRsend_test.h" +#include "ir_LG.h" +#include "IRac.h" +#include "IRsend.h" +#include "IRsend_test.h" #include "gtest/gtest.h" diff --git a/lib/IRremoteESP8266/test/ir_Lasertag_test.cpp b/lib/IRremoteESP8266/test/ir_Lasertag_test.cpp index d3d96f299c..bad724f76d 100644 --- a/lib/IRremoteESP8266/test/ir_Lasertag_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Lasertag_test.cpp @@ -1,7 +1,7 @@ // Copyright 2017 David Conran -#include "../src/IRsend.h" -#include "../src/IRsend_test.h" +#include "IRsend.h" +#include "IRsend_test.h" #include "gtest/gtest.h" // LL AAA SSSSS EEEEEEE RRRRRR TTTTTTT AAA GGGG diff --git a/lib/IRremoteESP8266/test/ir_Lego_test.cpp b/lib/IRremoteESP8266/test/ir_Lego_test.cpp index c2aacb1c9b..4e859b1708 100644 --- a/lib/IRremoteESP8266/test/ir_Lego_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Lego_test.cpp @@ -1,9 +1,9 @@ // Copyright 2019 David Conran -#include "../src/IRrecv.h" -#include "../src/IRrecv_test.h" -#include "../src/IRsend.h" -#include "../src/IRsend_test.h" +#include "IRrecv.h" +#include "IRrecv_test.h" +#include "IRsend.h" +#include "IRsend_test.h" #include "gtest/gtest.h" // General housekeeping diff --git a/lib/IRremoteESP8266/test/ir_Lutron_test.cpp b/lib/IRremoteESP8266/test/ir_Lutron_test.cpp index b0a4e346e5..81cf0df9ce 100644 --- a/lib/IRremoteESP8266/test/ir_Lutron_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Lutron_test.cpp @@ -1,7 +1,7 @@ // Copyright 2018 David Conran -#include "../src/IRsend.h" -#include "../src/IRsend_test.h" +#include "IRsend.h" +#include "IRsend_test.h" #include "gtest/gtest.h" // Tests for sendLutron(). diff --git a/lib/IRremoteESP8266/test/ir_MWM_test.cpp b/lib/IRremoteESP8266/test/ir_MWM_test.cpp index 7b70a46b4c..2ca69ac833 100644 --- a/lib/IRremoteESP8266/test/ir_MWM_test.cpp +++ b/lib/IRremoteESP8266/test/ir_MWM_test.cpp @@ -1,10 +1,10 @@ // Copyright 2017 David Conran // Copyright 2018 Brett T. Warden -#include "../src/IRrecv.h" -#include "../src/IRrecv_test.h" -#include "../src/IRsend.h" -#include "../src/IRsend_test.h" +#include "IRrecv.h" +#include "IRrecv_test.h" +#include "IRsend.h" +#include "IRsend_test.h" #include "gtest/gtest.h" // MM MM WW WW MM MM diff --git a/lib/IRremoteESP8266/test/ir_Magiquest_test.cpp b/lib/IRremoteESP8266/test/ir_Magiquest_test.cpp index c60a893de0..1e5faf5c08 100644 --- a/lib/IRremoteESP8266/test/ir_Magiquest_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Magiquest_test.cpp @@ -1,9 +1,9 @@ // Copyright 2017 David Conran -#include "../src/ir_Magiquest.h" -#include "../src/IRrecv.h" -#include "../src/IRsend.h" -#include "../src/IRsend_test.h" +#include "ir_Magiquest.h" +#include "IRrecv.h" +#include "IRsend.h" +#include "IRsend_test.h" #include "gtest/gtest.h" // Tests for encodeMagiQuest() diff --git a/lib/IRremoteESP8266/test/ir_Metz_test.cpp b/lib/IRremoteESP8266/test/ir_Metz_test.cpp index 8644e048dd..67d2170231 100644 --- a/lib/IRremoteESP8266/test/ir_Metz_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Metz_test.cpp @@ -1,10 +1,10 @@ // Copyright 2020 crankyoldgit -#include "../src/IRac.h" -#include "../src/IRrecv.h" -#include "../src/IRrecv_test.h" -#include "../src/IRsend.h" -#include "../src/IRsend_test.h" +#include "IRac.h" +#include "IRrecv.h" +#include "IRrecv_test.h" +#include "IRsend.h" +#include "IRsend_test.h" #include "gtest/gtest.h" // Tests for decodeMetz(). diff --git a/lib/IRremoteESP8266/test/ir_Midea_test.cpp b/lib/IRremoteESP8266/test/ir_Midea_test.cpp index 2e147fd0df..713fcd0e41 100644 --- a/lib/IRremoteESP8266/test/ir_Midea_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Midea_test.cpp @@ -1,9 +1,9 @@ // Copyright 2017 David Conran -#include "../src/ir_Midea.h" -#include "../src/IRac.h" -#include "../src/IRsend.h" -#include "../src/IRsend_test.h" +#include "ir_Midea.h" +#include "IRac.h" +#include "IRsend.h" +#include "IRsend_test.h" #include "gtest/gtest.h" // Tests for sendMidea(). diff --git a/lib/IRremoteESP8266/test/ir_Milestag2_test.cpp b/lib/IRremoteESP8266/test/ir_Milestag2_test.cpp index 37b74ad896..542852da85 100644 --- a/lib/IRremoteESP8266/test/ir_Milestag2_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Milestag2_test.cpp @@ -1,11 +1,11 @@ // Copyright 2021 David Conran -#include "../src/IRac.h" -#include "../src/IRrecv.h" -#include "../src/IRrecv_test.h" -#include "../src/IRsend.h" -#include "../src/IRsend_test.h" -#include "../src/IRutils.h" +#include "IRac.h" +#include "IRrecv.h" +#include "IRrecv_test.h" +#include "IRsend.h" +#include "IRsend_test.h" +#include "IRutils.h" #include "gtest/gtest.h" TEST(TestUtils, Housekeeping) { diff --git a/lib/IRremoteESP8266/test/ir_Mirage_test.cpp b/lib/IRremoteESP8266/test/ir_Mirage_test.cpp index 30348cbf1e..2530afc5cb 100644 --- a/lib/IRremoteESP8266/test/ir_Mirage_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Mirage_test.cpp @@ -1,10 +1,10 @@ // Copyright 2020 David Conran -#include "../src/IRac.h" -#include "../src/IRrecv.h" -#include "../src/IRrecv_test.h" -#include "../src/IRsend.h" -#include "../src/IRsend_test.h" +#include "IRac.h" +#include "IRrecv.h" +#include "IRrecv_test.h" +#include "IRsend.h" +#include "IRsend_test.h" #include "gtest/gtest.h" TEST(TestUtils, Housekeeping) { diff --git a/lib/IRremoteESP8266/test/ir_MitsubishiHeavy_test.cpp b/lib/IRremoteESP8266/test/ir_MitsubishiHeavy_test.cpp index f5e4a29d3a..d182fd941b 100644 --- a/lib/IRremoteESP8266/test/ir_MitsubishiHeavy_test.cpp +++ b/lib/IRremoteESP8266/test/ir_MitsubishiHeavy_test.cpp @@ -1,12 +1,12 @@ // Copyright 2019 David Conran -#include "../src/ir_MitsubishiHeavy.h" -#include "../src/IRac.h" -#include "../src/IRrecv.h" -#include "../src/IRrecv_test.h" -#include "../src/IRremoteESP8266.h" -#include "../src/IRsend.h" -#include "../src/IRsend_test.h" +#include "ir_MitsubishiHeavy.h" +#include "IRac.h" +#include "IRrecv.h" +#include "IRrecv_test.h" +#include "IRremoteESP8266.h" +#include "IRsend.h" +#include "IRsend_test.h" #include "gtest/gtest.h" // General housekeeping diff --git a/lib/IRremoteESP8266/test/ir_Mitsubishi_test.cpp b/lib/IRremoteESP8266/test/ir_Mitsubishi_test.cpp index d208d94a2b..28cc04d8e7 100644 --- a/lib/IRremoteESP8266/test/ir_Mitsubishi_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Mitsubishi_test.cpp @@ -2,11 +2,11 @@ // Copyright 2019 kuchel77 // Copyright 2018 denxhun -#include "../src/ir_Mitsubishi.h" -#include "../src/IRac.h" -#include "../src/IRrecv_test.h" -#include "../src/IRsend.h" -#include "../src/IRsend_test.h" +#include "ir_Mitsubishi.h" +#include "IRac.h" +#include "IRrecv_test.h" +#include "IRsend.h" +#include "IRsend_test.h" #include "gtest/gtest.h" // Tests for sendMitsubishi(). diff --git a/lib/IRremoteESP8266/test/ir_Multibrackets_test.cpp b/lib/IRremoteESP8266/test/ir_Multibrackets_test.cpp index 683072685d..7b58b3333a 100644 --- a/lib/IRremoteESP8266/test/ir_Multibrackets_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Multibrackets_test.cpp @@ -1,10 +1,10 @@ // Copyright 2020 David Conran -#include "../src/IRac.h" -#include "../src/IRrecv.h" -#include "../src/IRrecv_test.h" -#include "../src/IRsend.h" -#include "../src/IRsend_test.h" +#include "IRac.h" +#include "IRrecv.h" +#include "IRrecv_test.h" +#include "IRsend.h" +#include "IRsend_test.h" #include "gtest/gtest.h" // Tests for decodeMultibrackets(). diff --git a/lib/IRremoteESP8266/test/ir_NEC_test.cpp b/lib/IRremoteESP8266/test/ir_NEC_test.cpp index b8c2be1b49..f7598c9811 100644 --- a/lib/IRremoteESP8266/test/ir_NEC_test.cpp +++ b/lib/IRremoteESP8266/test/ir_NEC_test.cpp @@ -1,7 +1,7 @@ // Copyright 2017 David Conran -#include "../src/IRsend.h" -#include "../src/IRsend_test.h" +#include "IRsend.h" +#include "IRsend_test.h" #include "gtest/gtest.h" // Tests for sendNEC(). diff --git a/lib/IRremoteESP8266/test/ir_Neoclima_test.cpp b/lib/IRremoteESP8266/test/ir_Neoclima_test.cpp index 4c2ed04652..68fc2ded53 100644 --- a/lib/IRremoteESP8266/test/ir_Neoclima_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Neoclima_test.cpp @@ -1,12 +1,12 @@ // Copyright 2019 David Conran (crankyoldgit) -#include "../src/ir_Neoclima.h" +#include "ir_Neoclima.h" #include -#include "../src/IRac.h" -#include "../src/IRsend.h" -#include "../src/IRsend_test.h" -#include "../src/IRrecv.h" -#include "../src/IRrecv_test.h" +#include "IRac.h" +#include "IRsend.h" +#include "IRsend_test.h" +#include "IRrecv.h" +#include "IRrecv_test.h" #include "gtest/gtest.h" TEST(TestUtils, Housekeeping) { diff --git a/lib/IRremoteESP8266/test/ir_Nikai_test.cpp b/lib/IRremoteESP8266/test/ir_Nikai_test.cpp index 7a89b601cd..98d6d806da 100644 --- a/lib/IRremoteESP8266/test/ir_Nikai_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Nikai_test.cpp @@ -1,7 +1,7 @@ // Copyright 2017 David Conran -#include "../src/IRsend.h" -#include "../src/IRsend_test.h" +#include "IRsend.h" +#include "IRsend_test.h" #include "gtest/gtest.h" // Tests for sendNikai(). diff --git a/lib/IRremoteESP8266/test/ir_Panasonic_test.cpp b/lib/IRremoteESP8266/test/ir_Panasonic_test.cpp index 5c2a09dd26..3e52cef8b2 100644 --- a/lib/IRremoteESP8266/test/ir_Panasonic_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Panasonic_test.cpp @@ -1,13 +1,13 @@ // Copyright 2017, 2018 David Conran -#include "../src/ir_Panasonic.h" -#include "../src/IRac.h" -#include "../src/IRrecv.h" -#include "../src/IRrecv_test.h" -#include "../src/IRsend.h" -#include "../src/IRsend_test.h" -#include "../src/IRtext.h" -#include "../src/IRutils.h" +#include "ir_Panasonic.h" +#include "IRac.h" +#include "IRrecv.h" +#include "IRrecv_test.h" +#include "IRsend.h" +#include "IRsend_test.h" +#include "IRtext.h" +#include "IRutils.h" #include "gtest/gtest.h" // Tests for encodePanasonic(). diff --git a/lib/IRremoteESP8266/test/ir_Pioneer_test.cpp b/lib/IRremoteESP8266/test/ir_Pioneer_test.cpp index 7ac7d15561..fce2503e4a 100644 --- a/lib/IRremoteESP8266/test/ir_Pioneer_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Pioneer_test.cpp @@ -1,8 +1,8 @@ // Copyright 2018 David Conran -#include "../src/IRsend.h" -#include "../src/IRsend_test.h" -#include "../src/IRutils.h" +#include "IRsend.h" +#include "IRsend_test.h" +#include "IRutils.h" #include "gtest/gtest.h" // Tests for sendPioneer(). diff --git a/lib/IRremoteESP8266/test/ir_Pronto_test.cpp b/lib/IRremoteESP8266/test/ir_Pronto_test.cpp index f441120f1b..8331192bca 100644 --- a/lib/IRremoteESP8266/test/ir_Pronto_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Pronto_test.cpp @@ -1,6 +1,6 @@ // Copyright 2017 David Conran -#include "../src/IRsend.h" -#include "../src/IRsend_test.h" +#include "IRsend.h" +#include "IRsend_test.h" #include "gtest/gtest.h" // Tests for sendPronto(). diff --git a/lib/IRremoteESP8266/test/ir_RC5_RC6_test.cpp b/lib/IRremoteESP8266/test/ir_RC5_RC6_test.cpp index 8073ea5ab1..0b46bad7f7 100644 --- a/lib/IRremoteESP8266/test/ir_RC5_RC6_test.cpp +++ b/lib/IRremoteESP8266/test/ir_RC5_RC6_test.cpp @@ -1,7 +1,7 @@ // Copyright 2017 David Conran -#include "../src/IRsend.h" -#include "../src/IRsend_test.h" +#include "IRsend.h" +#include "IRsend_test.h" #include "gtest/gtest.h" // RRRRRR CCCCC 555555 RRRRRR CCCCC 555555 XX XX diff --git a/lib/IRremoteESP8266/test/ir_RCMM_test.cpp b/lib/IRremoteESP8266/test/ir_RCMM_test.cpp index b635c6173f..ef37d87a6a 100644 --- a/lib/IRremoteESP8266/test/ir_RCMM_test.cpp +++ b/lib/IRremoteESP8266/test/ir_RCMM_test.cpp @@ -1,7 +1,7 @@ // Copyright 2017 David Conran -#include "../src/IRsend.h" -#include "../src/IRsend_test.h" +#include "IRsend.h" +#include "IRsend_test.h" #include "gtest/gtest.h" // Tests for sendRCMM(). diff --git a/lib/IRremoteESP8266/test/ir_Rhoss_test.cpp b/lib/IRremoteESP8266/test/ir_Rhoss_test.cpp index 18428bb40d..855b9a48c8 100644 --- a/lib/IRremoteESP8266/test/ir_Rhoss_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Rhoss_test.cpp @@ -1,12 +1,12 @@ // Copyright 2021 Tom Rosenback -#include "../src/IRac.h" -#include "../src/ir_Rhoss.h" -#include "../src/IRrecv.h" -#include "../src/IRrecv_test.h" -#include "../src/IRsend.h" -#include "../src/IRsend_test.h" -#include "../src/IRutils.h" +#include "IRac.h" +#include "ir_Rhoss.h" +#include "IRrecv.h" +#include "IRrecv_test.h" +#include "IRsend.h" +#include "IRsend_test.h" +#include "IRutils.h" #include "gtest/gtest.h" TEST(TestUtils, Housekeeping) { diff --git a/lib/IRremoteESP8266/test/ir_Samsung_test.cpp b/lib/IRremoteESP8266/test/ir_Samsung_test.cpp index 3e62ac3a76..a4e0503cdf 100644 --- a/lib/IRremoteESP8266/test/ir_Samsung_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Samsung_test.cpp @@ -1,12 +1,12 @@ // Copyright 2017-2020 David Conran #include -#include "../src/ir_Samsung.h" -#include "../src/IRac.h" -#include "../src/IRrecv.h" -#include "../src/IRrecv_test.h" -#include "../src/IRsend.h" -#include "../src/IRsend_test.h" +#include "ir_Samsung.h" +#include "IRac.h" +#include "IRrecv.h" +#include "IRrecv_test.h" +#include "IRsend.h" +#include "IRsend_test.h" #include "gtest/gtest.h" diff --git a/lib/IRremoteESP8266/test/ir_Sanyo_test.cpp b/lib/IRremoteESP8266/test/ir_Sanyo_test.cpp index bb44b3a013..6378e7679d 100644 --- a/lib/IRremoteESP8266/test/ir_Sanyo_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Sanyo_test.cpp @@ -1,11 +1,11 @@ // Copyright 2017-2021 David Conran -#include "../src/ir_Sanyo.h" -#include "../src/IRac.h" -#include "../src/IRrecv.h" -#include "../src/IRrecv_test.h" -#include "../src/IRsend.h" -#include "../src/IRsend_test.h" +#include "ir_Sanyo.h" +#include "IRac.h" +#include "IRrecv.h" +#include "IRrecv_test.h" +#include "IRsend.h" +#include "IRsend_test.h" #include "gtest/gtest.h" // Tests for encodeSanyoLC7461(). diff --git a/lib/IRremoteESP8266/test/ir_Sharp_test.cpp b/lib/IRremoteESP8266/test/ir_Sharp_test.cpp index 6635e2dfd5..a2acafb9cc 100644 --- a/lib/IRremoteESP8266/test/ir_Sharp_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Sharp_test.cpp @@ -1,11 +1,11 @@ // Copyright 2017 David Conran -#include "../src/ir_Sharp.h" -#include "../src/IRac.h" -#include "../src/IRrecv.h" -#include "../src/IRrecv_test.h" -#include "../src/IRsend.h" -#include "../src/IRsend_test.h" +#include "ir_Sharp.h" +#include "IRac.h" +#include "IRrecv.h" +#include "IRrecv_test.h" +#include "IRsend.h" +#include "IRsend_test.h" #include "gtest/gtest.h" // Tests for encodeSharp(). diff --git a/lib/IRremoteESP8266/test/ir_Sherwood_test.cpp b/lib/IRremoteESP8266/test/ir_Sherwood_test.cpp index 2ac8b4af95..f1f41d9c8c 100644 --- a/lib/IRremoteESP8266/test/ir_Sherwood_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Sherwood_test.cpp @@ -1,7 +1,7 @@ // Copyright 2017 David Conran -#include "../src/IRsend.h" -#include "../src/IRsend_test.h" +#include "IRsend.h" +#include "IRsend_test.h" #include "gtest/gtest.h" // Tests for sendSherwood(). diff --git a/lib/IRremoteESP8266/test/ir_Sony_test.cpp b/lib/IRremoteESP8266/test/ir_Sony_test.cpp index dd0692f45c..c69d40e67a 100644 --- a/lib/IRremoteESP8266/test/ir_Sony_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Sony_test.cpp @@ -1,7 +1,7 @@ // Copyright 2017 David Conran -#include "../src/IRsend.h" -#include "../src/IRsend_test.h" +#include "IRsend.h" +#include "IRsend_test.h" #include "gtest/gtest.h" // Tests for sendSony(). diff --git a/lib/IRremoteESP8266/test/ir_Symphony_test.cpp b/lib/IRremoteESP8266/test/ir_Symphony_test.cpp index 7960db61eb..e3f818136e 100644 --- a/lib/IRremoteESP8266/test/ir_Symphony_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Symphony_test.cpp @@ -1,10 +1,10 @@ // Copyright 2020 David Conran -#include "../src/IRac.h" -#include "../src/IRrecv.h" -#include "../src/IRrecv_test.h" -#include "../src/IRsend.h" -#include "../src/IRsend_test.h" +#include "IRac.h" +#include "IRrecv.h" +#include "IRrecv_test.h" +#include "IRsend.h" +#include "IRsend_test.h" #include "gtest/gtest.h" // Tests for sendSymphony(). diff --git a/lib/IRremoteESP8266/test/ir_Tcl_test.cpp b/lib/IRremoteESP8266/test/ir_Tcl_test.cpp index 37e75f42f6..b7ab5301f9 100644 --- a/lib/IRremoteESP8266/test/ir_Tcl_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Tcl_test.cpp @@ -1,11 +1,11 @@ // Copyright 2019 David Conran -#include "../src/ir_Tcl.h" -#include "../src/IRac.h" -#include "../src/IRrecv.h" -#include "../src/IRrecv_test.h" -#include "../src/IRsend.h" -#include "../src/IRsend_test.h" +#include "ir_Tcl.h" +#include "IRac.h" +#include "IRrecv.h" +#include "IRrecv_test.h" +#include "IRsend.h" +#include "IRsend_test.h" #include "gtest/gtest.h" // General housekeeping diff --git a/lib/IRremoteESP8266/test/ir_Technibel_test.cpp b/lib/IRremoteESP8266/test/ir_Technibel_test.cpp index 420662e8f7..808da1e951 100644 --- a/lib/IRremoteESP8266/test/ir_Technibel_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Technibel_test.cpp @@ -1,12 +1,12 @@ // Copyright 2020 Quentin BRIOLLANT -#include "../src/IRac.h" -#include "../src/ir_Technibel.h" -#include "../src/IRrecv.h" -#include "../src/IRrecv_test.h" -#include "../src/IRsend.h" -#include "../src/IRsend_test.h" -#include "../src/IRutils.h" +#include "IRac.h" +#include "ir_Technibel.h" +#include "IRrecv.h" +#include "IRrecv_test.h" +#include "IRsend.h" +#include "IRsend_test.h" +#include "IRutils.h" #include "gtest/gtest.h" TEST(TestUtils, Housekeeping) { diff --git a/lib/IRremoteESP8266/test/ir_Teco_test.cpp b/lib/IRremoteESP8266/test/ir_Teco_test.cpp index 2913c3dc6a..f4196c3aef 100644 --- a/lib/IRremoteESP8266/test/ir_Teco_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Teco_test.cpp @@ -1,11 +1,11 @@ // Copyright 2019 David Conran -#include "../src/ir_Teco.h" -#include "../src/IRac.h" -#include "../src/IRrecv.h" -#include "../src/IRrecv_test.h" -#include "../src/IRsend.h" -#include "../src/IRsend_test.h" +#include "ir_Teco.h" +#include "IRac.h" +#include "IRrecv.h" +#include "IRrecv_test.h" +#include "IRsend.h" +#include "IRsend_test.h" #include "gtest/gtest.h" // General housekeeping diff --git a/lib/IRremoteESP8266/test/ir_Teknopoint_test.cpp b/lib/IRremoteESP8266/test/ir_Teknopoint_test.cpp index 59a7ee23d6..74f29d1468 100644 --- a/lib/IRremoteESP8266/test/ir_Teknopoint_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Teknopoint_test.cpp @@ -1,10 +1,10 @@ // Copyright 2021 David Conran (crankyoldgit) -#include "../src/IRac.h" -#include "../src/IRrecv.h" -#include "../src/IRrecv_test.h" -#include "../src/IRsend.h" -#include "../src/IRsend_test.h" +#include "IRac.h" +#include "IRrecv.h" +#include "IRrecv_test.h" +#include "IRsend.h" +#include "IRsend_test.h" #include "gtest/gtest.h" // Tests for decodeTeknopoint(). diff --git a/lib/IRremoteESP8266/test/ir_Toshiba_test.cpp b/lib/IRremoteESP8266/test/ir_Toshiba_test.cpp index a58ff02253..1785b38956 100644 --- a/lib/IRremoteESP8266/test/ir_Toshiba_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Toshiba_test.cpp @@ -1,10 +1,10 @@ // Copyright 2017 David Conran -#include "../src/ir_Toshiba.h" -#include "../src/IRac.h" -#include "../src/IRrecv.h" -#include "../src/IRrecv_test.h" -#include "../src/IRsend.h" -#include "../src/IRsend_test.h" +#include "ir_Toshiba.h" +#include "IRac.h" +#include "IRrecv.h" +#include "IRrecv_test.h" +#include "IRsend.h" +#include "IRsend_test.h" #include "gtest/gtest.h" // Tests for Toshiba A/C methods. diff --git a/lib/IRremoteESP8266/test/ir_Transcold_test.cpp b/lib/IRremoteESP8266/test/ir_Transcold_test.cpp index 93ac4ad03e..8a98a28969 100644 --- a/lib/IRremoteESP8266/test/ir_Transcold_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Transcold_test.cpp @@ -1,10 +1,10 @@ // Copyright 2020 crankyoldgit -#include "../src/IRac.h" -#include "../src/IRrecv.h" -#include "../src/IRrecv_test.h" -#include "../src/IRsend.h" -#include "../src/IRsend_test.h" +#include "IRac.h" +#include "IRrecv.h" +#include "IRrecv_test.h" +#include "IRsend.h" +#include "IRsend_test.h" #include "gtest/gtest.h" // Tests for decodeTranscold(). diff --git a/lib/IRremoteESP8266/test/ir_Trotec_test.cpp b/lib/IRremoteESP8266/test/ir_Trotec_test.cpp index 8e4c069db4..b6658f3f52 100644 --- a/lib/IRremoteESP8266/test/ir_Trotec_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Trotec_test.cpp @@ -1,10 +1,10 @@ // Copyright 2019 David Conran -#include "../src/ir_Trotec.h" -#include "../src/IRac.h" -#include "../src/IRrecv.h" -#include "../src/IRrecv_test.h" -#include "../src/IRsend.h" -#include "../src/IRsend_test.h" +#include "ir_Trotec.h" +#include "IRac.h" +#include "IRrecv.h" +#include "IRrecv_test.h" +#include "IRsend.h" +#include "IRsend_test.h" #include "gtest/gtest.h" diff --git a/lib/IRremoteESP8266/test/ir_Truma_test.cpp b/lib/IRremoteESP8266/test/ir_Truma_test.cpp index aab6fbf8b4..0d26fb6dbe 100644 --- a/lib/IRremoteESP8266/test/ir_Truma_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Truma_test.cpp @@ -1,10 +1,10 @@ // Copyright 2021 David Conran (crankyoldgit) -#include "../src/IRac.h" -#include "../src/IRrecv.h" -#include "../src/IRrecv_test.h" -#include "../src/IRsend.h" -#include "../src/IRsend_test.h" +#include "IRac.h" +#include "IRrecv.h" +#include "IRrecv_test.h" +#include "IRsend.h" +#include "IRsend_test.h" #include "gtest/gtest.h" // Tests for decodeTruma(). diff --git a/lib/IRremoteESP8266/test/ir_Vestel_test.cpp b/lib/IRremoteESP8266/test/ir_Vestel_test.cpp index cb8d97554c..d3f1febf8a 100644 --- a/lib/IRremoteESP8266/test/ir_Vestel_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Vestel_test.cpp @@ -1,11 +1,11 @@ // Copyright 2019 David Conran -#include "../src/ir_Vestel.h" -#include "../src/IRac.h" -#include "../src/IRrecv.h" -#include "../src/IRrecv_test.h" -#include "../src/IRsend.h" -#include "../src/IRsend_test.h" +#include "ir_Vestel.h" +#include "IRac.h" +#include "IRrecv.h" +#include "IRrecv_test.h" +#include "IRsend.h" +#include "IRsend_test.h" #include "gtest/gtest.h" // Tests for sendVestelAc() diff --git a/lib/IRremoteESP8266/test/ir_Voltas_test.cpp b/lib/IRremoteESP8266/test/ir_Voltas_test.cpp index 2a8de3eb1f..38fa9223b2 100644 --- a/lib/IRremoteESP8266/test/ir_Voltas_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Voltas_test.cpp @@ -1,11 +1,11 @@ // Copyright 2020 crankyoldgit -#include "../src/ir_Voltas.h" -#include "../src/IRac.h" -#include "../src/IRrecv.h" -#include "../src/IRrecv_test.h" -#include "../src/IRsend.h" -#include "../src/IRsend_test.h" +#include "ir_Voltas.h" +#include "IRac.h" +#include "IRrecv.h" +#include "IRrecv_test.h" +#include "IRsend.h" +#include "IRsend_test.h" #include "gtest/gtest.h" // Tests for decodeVoltas(). diff --git a/lib/IRremoteESP8266/test/ir_Whirlpool_test.cpp b/lib/IRremoteESP8266/test/ir_Whirlpool_test.cpp index c156e81e7f..4bc295d4f3 100644 --- a/lib/IRremoteESP8266/test/ir_Whirlpool_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Whirlpool_test.cpp @@ -1,11 +1,11 @@ // Copyright 2018 David Conran -#include "../src/ir_Whirlpool.h" -#include "../src/IRac.h" -#include "../src/IRrecv.h" -#include "../src/IRrecv_test.h" -#include "../src/IRsend.h" -#include "../src/IRsend_test.h" +#include "ir_Whirlpool.h" +#include "IRac.h" +#include "IRrecv.h" +#include "IRrecv_test.h" +#include "IRsend.h" +#include "IRsend_test.h" #include "gtest/gtest.h" diff --git a/lib/IRremoteESP8266/test/ir_Whynter_test.cpp b/lib/IRremoteESP8266/test/ir_Whynter_test.cpp index e9f532d4b2..eaccf0ea10 100644 --- a/lib/IRremoteESP8266/test/ir_Whynter_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Whynter_test.cpp @@ -1,7 +1,7 @@ // Copyright 2017 David Conran -#include "../src/IRsend.h" -#include "../src/IRsend_test.h" +#include "IRsend.h" +#include "IRsend_test.h" #include "gtest/gtest.h" // Tests for sendWhynter(). diff --git a/lib/IRremoteESP8266/test/ir_Xmp_test.cpp b/lib/IRremoteESP8266/test/ir_Xmp_test.cpp index 4c998139b8..796f2ef903 100644 --- a/lib/IRremoteESP8266/test/ir_Xmp_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Xmp_test.cpp @@ -1,10 +1,10 @@ // Copyright 2021 David Conran -#include "../src/IRac.h" -#include "../src/IRrecv.h" -#include "../src/IRrecv_test.h" -#include "../src/IRsend.h" -#include "../src/IRsend_test.h" +#include "IRac.h" +#include "IRrecv.h" +#include "IRrecv_test.h" +#include "IRsend.h" +#include "IRsend_test.h" #include "gtest/gtest.h" // Tests for sendXmp(). diff --git a/lib/IRremoteESP8266/test/ir_Zepeal_test.cpp b/lib/IRremoteESP8266/test/ir_Zepeal_test.cpp index 986a6986c4..12240d2f7c 100644 --- a/lib/IRremoteESP8266/test/ir_Zepeal_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Zepeal_test.cpp @@ -1,10 +1,10 @@ // Copyright 2020 Christian Nilsson -#include "../src/IRac.h" -#include "../src/IRrecv.h" -#include "../src/IRrecv_test.h" -#include "../src/IRsend.h" -#include "../src/IRsend_test.h" +#include "IRac.h" +#include "IRrecv.h" +#include "IRrecv_test.h" +#include "IRsend.h" +#include "IRsend_test.h" #include "gtest/gtest.h" // Tests for decodeZepeal(). diff --git a/lib/IRremoteESP8266/tools/auto_analyse_raw_data.py b/lib/IRremoteESP8266/tools/auto_analyse_raw_data.py index 5c797aff4d..e061d3c424 100644 --- a/lib/IRremoteESP8266/tools/auto_analyse_raw_data.py +++ b/lib/IRremoteESP8266/tools/auto_analyse_raw_data.py @@ -647,9 +647,9 @@ def generate_code(defines, code, bits_str, name="", output=sys.stdout): f"/// @brief Support for {def_name} protocol\n\n" "// Supports:\n" f"// Brand: {def_name}, Model: TODO add device and remote\n\n" - '#include "../src/IRrecv.h"\n' - '#include "../src/IRsend.h"\n' - '#include "../src/IRutils.h"\n\n' + '#include "IRrecv.h"\n' + '#include "IRsend.h"\n' + '#include "IRutils.h"\n\n' "// WARNING: This probably isn't directly usable." " It's a guide only.\n\n" "// See https://github.com/crankyoldgit/IRremoteESP8266/wiki/" diff --git a/lib/IRremoteESP8266/tools/auto_analyse_raw_data_test.py b/lib/IRremoteESP8266/tools/auto_analyse_raw_data_test.py index 6682dcf766..1b080c5a8b 100644 --- a/lib/IRremoteESP8266/tools/auto_analyse_raw_data_test.py +++ b/lib/IRremoteESP8266/tools/auto_analyse_raw_data_test.py @@ -286,9 +286,9 @@ def test_parse_and_report(self): '// Supports:\n' '// Brand: FOO, Model: TODO add device and remote\n' '\n' - '#include "../src/IRrecv.h"\n' - '#include "../src/IRsend.h"\n' - '#include "../src/IRutils.h"\n' + '#include "IRrecv.h"\n' + '#include "IRsend.h"\n' + '#include "IRutils.h"\n' '\n' "// WARNING: This probably isn't directly usable. It's a guide only.\n" '\n' @@ -567,9 +567,9 @@ def test_leader_marks(self): '// Supports:\n' '// Brand: Hitachi, Model: TODO add device and remote\n' '\n' - '#include "../src/IRrecv.h"\n' - '#include "../src/IRsend.h"\n' - '#include "../src/IRutils.h"\n' + '#include "IRrecv.h"\n' + '#include "IRsend.h"\n' + '#include "IRutils.h"\n' '\n' "// WARNING: This probably isn't directly usable. It's a guide only.\n" '\n' @@ -881,9 +881,9 @@ def test_unusual_gaps(self): '// Supports:\n' '// Brand: FOO, Model: TODO add device and remote\n' '\n' - '#include "../src/IRrecv.h"\n' - '#include "../src/IRsend.h"\n' - '#include "../src/IRutils.h"\n' + '#include "IRrecv.h"\n' + '#include "IRsend.h"\n' + '#include "IRutils.h"\n' '\n' "// WARNING: This probably isn't directly usable. It's a guide only.\n" '\n' @@ -1317,9 +1317,9 @@ def test_no_headers(self): '// Supports:\n' '// Brand: TBD, Model: TODO add device and remote\n' '\n' - '#include "../src/IRrecv.h"\n' - '#include "../src/IRsend.h"\n' - '#include "../src/IRutils.h"\n' + '#include "IRrecv.h"\n' + '#include "IRsend.h"\n' + '#include "IRutils.h"\n' '\n' "// WARNING: This probably isn't directly usable. It's a guide only.\n" '\n' diff --git a/lib/IRremoteESP8266/tools/gc_decode.cpp b/lib/IRremoteESP8266/tools/gc_decode.cpp index 38ab03ba45..f1c374bbef 100644 --- a/lib/IRremoteESP8266/tools/gc_decode.cpp +++ b/lib/IRremoteESP8266/tools/gc_decode.cpp @@ -7,10 +7,10 @@ #include #include #include -#include "../src/IRac.h" -#include "../src/IRsend.h" -#include "../src/IRsend_test.h" -#include "../src/IRutils.h" +#include "IRac.h" +#include "IRsend.h" +#include "IRsend_test.h" +#include "IRutils.h" const uint16_t kMaxGcCodeLength = 10000; diff --git a/lib/IRremoteESP8266/tools/mode2_decode.cpp b/lib/IRremoteESP8266/tools/mode2_decode.cpp index 8ba52aa000..63dfa62210 100644 --- a/lib/IRremoteESP8266/tools/mode2_decode.cpp +++ b/lib/IRremoteESP8266/tools/mode2_decode.cpp @@ -24,9 +24,9 @@ space 500000 #include #include #include -#include "../src/IRsend.h" -#include "../src/IRsend_test.h" -#include "../src/IRutils.h" +#include "IRsend.h" +#include "IRsend_test.h" +#include "IRutils.h" const uint16_t kMaxGcCodeLength = 10000; diff --git a/platformio.ini b/platformio.ini index 31baf3cea1..8090915ae0 100644 --- a/platformio.ini +++ b/platformio.ini @@ -89,7 +89,7 @@ monitor_speed = 115200 targets = extra_scripts = pre:tools/pio/pre_default_check.py ${extra_scripts_esp8266.extra_scripts} -src_filter = +<*> -<.git/> -<.svn/> - - - - -<*/Commands/> -<*/ControllerQueue/> -<*/DataTypes/> -<*/Globals/> -<*/Helpers/> -<*/PluginStructs/> -<*/WebServer/> -<*/IRremoteESP8266/src/> +src_filter = +<*> -<.git/> -<.svn/> - - - - -<*/Commands/> -<*/ControllerQueue/> -<*/DataTypes/> -<*/Globals/> -<*/Helpers/> -<*/PluginStructs/> -<*/WebServer/> From 9367922b000a22ec865dded20b81ed690cc4fa91 Mon Sep 17 00:00:00 2001 From: TD-er Date: Sun, 5 Dec 2021 22:22:21 +0100 Subject: [PATCH 016/147] [Build] Fix build of ESP32 max LittleFS build on Windows In order to be able to build in Windows, we need to concatenate all .cpp files in some folders to reduce the number of compiled object files. This is needed due to the limitation of Windows to have a command line longer than 32767 characters. The linker command was becoming longer than this maximum. However the DataStructs folder does have at least one templated .cpp file. Since you may need to include a .cpp file if it contains a templated class, the DataStruct folder could not be concatenated into a single .cpp file. Therefore the templated class(es) will be stored in a folder postfixed with _templ --- platformio.ini | 2 +- src/src/DataStructs_templ/README.txt | 13 +++++++++++++ .../SettingsStruct.cpp | 0 src/src/Globals/Settings.h | 4 +++- tools/pio/concat_cpp_files.py | 1 + tools/pio/remove_concat_cpp_files.py | 1 + 6 files changed, 19 insertions(+), 2 deletions(-) create mode 100644 src/src/DataStructs_templ/README.txt rename src/src/{DataStructs => DataStructs_templ}/SettingsStruct.cpp (100%) diff --git a/platformio.ini b/platformio.ini index 8090915ae0..03dc492f77 100644 --- a/platformio.ini +++ b/platformio.ini @@ -89,7 +89,7 @@ monitor_speed = 115200 targets = extra_scripts = pre:tools/pio/pre_default_check.py ${extra_scripts_esp8266.extra_scripts} -src_filter = +<*> -<.git/> -<.svn/> - - - - -<*/Commands/> -<*/ControllerQueue/> -<*/DataTypes/> -<*/Globals/> -<*/Helpers/> -<*/PluginStructs/> -<*/WebServer/> +src_filter = +<*> -<.git/> -<.svn/> - - - - -<*/Commands/> -<*/ControllerQueue/> -<*/DataStructs/> -<*/DataTypes/> -<*/Globals/> -<*/Helpers/> -<*/PluginStructs/> -<*/WebServer/> diff --git a/src/src/DataStructs_templ/README.txt b/src/src/DataStructs_templ/README.txt new file mode 100644 index 0000000000..2c8611fd45 --- /dev/null +++ b/src/src/DataStructs_templ/README.txt @@ -0,0 +1,13 @@ +In order to be able to build in Windows, we need to concatenate all .cpp files +in some folders to reduce the number of compiled object files. +This is needed due to the limitation of Windows to have a command line longer than 32767 characters. +The linker command was becoming longer than this maximum. + +However the DataStructs folder does have at least one templated .cpp file. +Since you may need to include a .cpp file if it contains a templated class, +the DataStruct folder could not be concatenated into a single .cpp file. + +Therefore the templated class(es) will be stored in a folder postfixed with +_templ + + diff --git a/src/src/DataStructs/SettingsStruct.cpp b/src/src/DataStructs_templ/SettingsStruct.cpp similarity index 100% rename from src/src/DataStructs/SettingsStruct.cpp rename to src/src/DataStructs_templ/SettingsStruct.cpp diff --git a/src/src/Globals/Settings.h b/src/src/Globals/Settings.h index 736141b8d4..9930b528e9 100644 --- a/src/src/Globals/Settings.h +++ b/src/src/Globals/Settings.h @@ -4,7 +4,9 @@ #include "../DataStructs/SettingsStruct.h" // include the source file since it is a template class. -#include "../DataStructs/SettingsStruct.cpp" +// Moved to a separate folder to allow concatenating all *.cpp files +// in the DataStructs folder to make Windows builds work again. +#include "../DataStructs_templ/SettingsStruct.cpp" extern SettingsStruct Settings; diff --git a/tools/pio/concat_cpp_files.py b/tools/pio/concat_cpp_files.py index ad4be8eb8b..2f2a32e3e1 100644 --- a/tools/pio/concat_cpp_files.py +++ b/tools/pio/concat_cpp_files.py @@ -52,6 +52,7 @@ def concat_cpp_files(path_to_concat): concat_cpp_files('./src/src/Commands') concat_cpp_files('./src/src/ControllerQueue') +concat_cpp_files('./src/src/DataStructs') concat_cpp_files('./src/src/DataTypes') concat_cpp_files('./src/src/Globals') concat_cpp_files('./src/src/Helpers') diff --git a/tools/pio/remove_concat_cpp_files.py b/tools/pio/remove_concat_cpp_files.py index 3dfbd6700e..3f2f293eb0 100644 --- a/tools/pio/remove_concat_cpp_files.py +++ b/tools/pio/remove_concat_cpp_files.py @@ -19,6 +19,7 @@ def clear_all_concat_cpp_files(source, target, env): print("\u001b[32m Remove temp concatenated files \u001b[0m") clear_concat_cpp_files('./src/src/Commands') clear_concat_cpp_files('./src/src/ControllerQueue') + clear_concat_cpp_files('./src/src/DataStructs') clear_concat_cpp_files('./src/src/DataTypes') clear_concat_cpp_files('./src/src/Globals') clear_concat_cpp_files('./src/src/Helpers') From 16dffb638fcbdcde3693b82909d227ef605ee3c6 Mon Sep 17 00:00:00 2001 From: TD-er Date: Mon, 6 Dec 2021 00:26:48 +0100 Subject: [PATCH 017/147] [PIO] Simplify ESP32 env definitions. --- platformio_esp32_envs.ini | 49 +++++++------------------------------ platformio_esp82xx_base.ini | 5 ---- 2 files changed, 9 insertions(+), 45 deletions(-) diff --git a/platformio_esp32_envs.ini b/platformio_esp32_envs.ini index d39582745b..7dec571cee 100644 --- a/platformio_esp32_envs.ini +++ b/platformio_esp32_envs.ini @@ -25,6 +25,7 @@ build_flags = ${core_esp32_3_4_0.build_flags} -DCONFIG_LWIP_ESP_GRATUITOUS_ARP -fno-strict-aliasing -DCORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_NONE +board = esp32dev monitor_filters = esp32_exception_decoder @@ -52,9 +53,7 @@ board_build.filesystem = littlefs ; Custom: 4096k version -------------------------- [env:custom_ESP32_4M316k] extends = esp32_common -platform = ${esp32_common.platform} build_flags = ${esp32_common.build_flags} -DPLUGIN_BUILD_CUSTOM -board = esp32dev extra_scripts = ${esp32_common.extra_scripts} pre:tools/pio/pre_custom_esp32.py @@ -70,21 +69,16 @@ extra_scripts = ${esp32s2_common.extra_scripts} [env:custom_IR_ESP32_4M316k] extends = esp32_common -platform = ${esp32_common.platform} build_flags = ${esp32_common.build_flags} -DPLUGIN_BUILD_CUSTOM -DPLUGIN_BUILD_IR -board = esp32dev lib_ignore = ${esp32_always.lib_ignore}, ESP32_ping, HeatpumpIR extra_scripts = ${esp32_common.extra_scripts} pre:tools/pio/pre_custom_esp32.py [env:normal_ESP32_4M316k] extends = esp32_common -platform = ${esp32_common.platform} lib_deps = ${esp32_common.lib_deps}, ServoESP32 build_flags = ${esp32_common.build_flags} -DFEATURE_ARDUINO_OTA -board = esp32dev -extra_scripts = ${esp32_common.extra_scripts} [env:test_A_ESP32_4M316k] extends = esp32_common @@ -93,43 +87,31 @@ build_flags = ${esp32_common.build_flags} -DFEATURE_ARDUINO_OTA -DPLUGIN_SET_TEST_ESP32 -DTESTING_USE_RTTTL -board = esp32dev -extra_scripts = ${esp32_common.extra_scripts} [env:test_B_ESP32_4M316k] extends = esp32_common -platform = ${esp32_common.platform} build_flags = ${esp32_common.build_flags} -DFEATURE_ARDUINO_OTA -DPLUGIN_SET_TEST_B_ESP32 -DTESTING_USE_RTTTL -board = esp32dev -extra_scripts = ${esp32_common.extra_scripts} [env:test_C_ESP32_4M316k] extends = esp32_common -platform = ${esp32_common.platform} build_flags = ${esp32_common.build_flags} -DFEATURE_ARDUINO_OTA -DPLUGIN_SET_TEST_C_ESP32 -DTESTING_USE_RTTTL -board = esp32dev -extra_scripts = ${esp32_common.extra_scripts} [env:test_D_ESP32_4M316k] extends = esp32_common -platform = ${esp32_common.platform} build_flags = ${esp32_common.build_flags} -DFEATURE_ARDUINO_OTA -DPLUGIN_SET_TEST_D_ESP32 -DTESTING_USE_RTTTL -board = esp32dev -extra_scripts = ${esp32_common.extra_scripts} [env:test_A_ESP32-wrover-kit_4M316k] extends = esp32_common -platform = ${esp32_common.platform} build_flags = ${esp32_common.build_flags} -DFEATURE_ARDUINO_OTA -DPLUGIN_SET_TEST_ESP32 @@ -138,11 +120,10 @@ board = esp-wrover-kit upload_protocol = ftdi debug_tool = ftdi debug_extra_cmds = break Misc.ino:3011 -extra_scripts = ${esp32_common.extra_scripts} + [env:test_B_ESP32-wrover-kit_4M316k] extends = esp32_common -platform = ${esp32_common.platform} build_flags = ${esp32_common.build_flags} -DFEATURE_ARDUINO_OTA -DPLUGIN_SET_TEST_B_ESP32 @@ -151,11 +132,10 @@ board = esp-wrover-kit upload_protocol = ftdi debug_tool = ftdi debug_extra_cmds = break Misc.ino:3011 -extra_scripts = ${esp32_common.extra_scripts} + [env:test_C_ESP32-wrover-kit_4M316k] extends = esp32_common -platform = ${esp32_common.platform} build_flags = ${esp32_common.build_flags} -DFEATURE_ARDUINO_OTA -DPLUGIN_SET_TEST_C_ESP32 @@ -164,11 +144,10 @@ board = esp-wrover-kit upload_protocol = ftdi debug_tool = ftdi debug_extra_cmds = break Misc.ino:3011 -extra_scripts = ${esp32_common.extra_scripts} + [env:test_D_ESP32-wrover-kit_4M316k] extends = esp32_common -platform = ${esp32_common.platform} build_flags = ${esp32_common.build_flags} -DFEATURE_ARDUINO_OTA -DPLUGIN_SET_TEST_D_ESP32 @@ -177,51 +156,47 @@ board = esp-wrover-kit upload_protocol = ftdi debug_tool = ftdi debug_extra_cmds = break Misc.ino:3011 -extra_scripts = ${esp32_common.extra_scripts} + [env:test_A_ESP32_4M316k_lolin_d32_pro] extends = esp32_common -platform = ${esp32_common.platform} build_flags = ${esp32_common.build_flags} ; -DFEATURE_ARDUINO_OTA -DPLUGIN_SET_TEST_ESP32 ; -DUSES_P096 -DTESTING_USE_RTTTL board = lolin_d32_pro -extra_scripts = ${esp32_common.extra_scripts} + [env:test_B_ESP32_4M316k_lolin_d32_pro] extends = esp32_common -platform = ${esp32_common.platform} build_flags = ${esp32_common.build_flags} ; -DFEATURE_ARDUINO_OTA -DPLUGIN_SET_TEST_B_ESP32 -DUSES_P096 -DTESTING_USE_RTTTL board = lolin_d32_pro -extra_scripts = ${esp32_common.extra_scripts} + [env:test_C_ESP32_4M316k_lolin_d32_pro] extends = esp32_common -platform = ${esp32_common.platform} build_flags = ${esp32_common.build_flags} ; -DFEATURE_ARDUINO_OTA -DPLUGIN_SET_TEST_C_ESP32 ; -DUSES_P096 -DTESTING_USE_RTTTL board = lolin_d32_pro -extra_scripts = ${esp32_common.extra_scripts} + [env:test_D_ESP32_4M316k_lolin_d32_pro] extends = esp32_common -platform = ${esp32_common.platform} build_flags = ${esp32_common.build_flags} ; -DFEATURE_ARDUINO_OTA -DPLUGIN_SET_TEST_D_ESP32 -DUSES_P096 -DTESTING_USE_RTTTL board = lolin_d32_pro -extra_scripts = ${esp32_common.extra_scripts} + [env:test_A_ESP32_IRExt_4M316k] extends = env:test_A_ESP32_4M316k @@ -253,23 +228,17 @@ build_flags = ${esp32_common.build_flags} [env:energy_ESP32_4M316k] extends = esp32_common -platform = ${esp32_common.platform} lib_deps = ${esp32_common.lib_deps}, ServoESP32 build_flags = ${esp32_common.build_flags} -DFEATURE_ARDUINO_OTA -D PLUGIN_ENERGY_COLLECTION -board = esp32dev -extra_scripts = ${esp32_common.extra_scripts} [env:display_ESP32_4M316k] extends = esp32_common -platform = ${esp32_common.platform} lib_deps = ${esp32_common.lib_deps}, ServoESP32 build_flags = ${esp32_common.build_flags} -DFEATURE_ARDUINO_OTA -D PLUGIN_DISPLAY_COLLECTION -board = esp32dev -extra_scripts = ${esp32_common.extra_scripts} ; Custom: 4096k version -------------------------- diff --git a/platformio_esp82xx_base.ini b/platformio_esp82xx_base.ini index 2cc0bbfdd8..011512793a 100644 --- a/platformio_esp82xx_base.ini +++ b/platformio_esp82xx_base.ini @@ -201,7 +201,6 @@ board = esp12e board_build.flash_mode = dout board_upload.maximum_size = 1044464 board_build.ldscript = eagle.flash.2m256.ld -build_flags = ${esp82xx_common.build_flags} [espWroom2M] extends = esp82xx_common @@ -209,7 +208,6 @@ board = esp12e board_build.flash_mode = dout board_upload.maximum_size = 1044464 board_build.ldscript = eagle.flash.2m.ld -build_flags = ${esp82xx_common.build_flags} [espWroom2M256] extends = esp82xx_common @@ -217,7 +215,6 @@ board_build.flash_mode = dout board_upload.maximum_size = 1044464 board = esp_wroom_02 board_build.ldscript = eagle.flash.2m256.ld -build_flags = ${esp82xx_common.build_flags} ;;; 4MB flash nodes ************************************************** @@ -230,7 +227,6 @@ board = esp12e board_build.flash_mode = dout board_upload.maximum_size = 1044464 board_build.ldscript = eagle.flash.4m1m.ld -build_flags = ${esp82xx_common.build_flags} [esp8266_4M2M] extends = esp82xx_common @@ -238,7 +234,6 @@ board = esp12e board_build.flash_mode = dout board_upload.maximum_size = 1044464 board_build.ldscript = eagle.flash.4m2m.ld -build_flags = ${esp82xx_common.build_flags} From 2eafc096e560d9ab3ecc90f4cef1c3b05ef77239 Mon Sep 17 00:00:00 2001 From: TD-er Date: Mon, 6 Dec 2021 00:28:12 +0100 Subject: [PATCH 018/147] [PIO] Simplify WROVER kit envs and remove WROVER kit ETH builds The WROVER kit doesn't have ethernet support. --- platformio_esp32_envs.ini | 63 ++++++++++----------------------------- 1 file changed, 15 insertions(+), 48 deletions(-) diff --git a/platformio_esp32_envs.ini b/platformio_esp32_envs.ini index 7dec571cee..766424a5cc 100644 --- a/platformio_esp32_envs.ini +++ b/platformio_esp32_envs.ini @@ -40,6 +40,13 @@ platform = ${core_esp32_3_4_0_esp32s2.platform} platform_packages = ${core_esp32_3_4_0_esp32s2.platform_packages} +[ESP32-wrover-kit] +extends = esp32_common +board = esp-wrover-kit +upload_protocol = ftdi +debug_tool = ftdi +;debug_extra_cmds = break Misc.ino:3011 + [esp32_LittleFS] extends = esp32_common platform_packages = ${esp32_common.platform_packages} @@ -111,51 +118,35 @@ build_flags = ${esp32_common.build_flags} [env:test_A_ESP32-wrover-kit_4M316k] -extends = esp32_common -build_flags = ${esp32_common.build_flags} +extends = ESP32-wrover-kit +build_flags = ${ESP32-wrover-kit.build_flags} -DFEATURE_ARDUINO_OTA -DPLUGIN_SET_TEST_ESP32 -DTESTING_USE_RTTTL -board = esp-wrover-kit -upload_protocol = ftdi -debug_tool = ftdi -debug_extra_cmds = break Misc.ino:3011 [env:test_B_ESP32-wrover-kit_4M316k] -extends = esp32_common -build_flags = ${esp32_common.build_flags} +extends = ESP32-wrover-kit +build_flags = ${ESP32-wrover-kit.build_flags} -DFEATURE_ARDUINO_OTA -DPLUGIN_SET_TEST_B_ESP32 -DTESTING_USE_RTTTL -board = esp-wrover-kit -upload_protocol = ftdi -debug_tool = ftdi -debug_extra_cmds = break Misc.ino:3011 [env:test_C_ESP32-wrover-kit_4M316k] -extends = esp32_common -build_flags = ${esp32_common.build_flags} +extends = ESP32-wrover-kit +build_flags = ${ESP32-wrover-kit.build_flags} -DFEATURE_ARDUINO_OTA -DPLUGIN_SET_TEST_C_ESP32 -DTESTING_USE_RTTTL -board = esp-wrover-kit -upload_protocol = ftdi -debug_tool = ftdi -debug_extra_cmds = break Misc.ino:3011 [env:test_D_ESP32-wrover-kit_4M316k] -extends = esp32_common -build_flags = ${esp32_common.build_flags} +extends = ESP32-wrover-kit +build_flags = ${ESP32-wrover-kit.build_flags} -DFEATURE_ARDUINO_OTA -DPLUGIN_SET_TEST_D_ESP32 -DTESTING_USE_RTTTL -board = esp-wrover-kit -upload_protocol = ftdi -debug_tool = ftdi -debug_extra_cmds = break Misc.ino:3011 [env:test_A_ESP32_4M316k_lolin_d32_pro] @@ -277,30 +268,6 @@ build_flags = ${env:test_D_ESP32_4M316k.build_flags} -DHAS_ETHERNE -DTESTING_USE_RTTTL -[env:test_A_ESP32-wrover-kit_4M316k_ETH] -extends = env:test_A_ESP32-wrover-kit_4M316k -platform = ${env:test_A_ESP32-wrover-kit_4M316k.platform} -build_flags = ${env:test_A_ESP32-wrover-kit_4M316k.build_flags} -DHAS_ETHERNET - -DTESTING_USE_RTTTL - -[env:test_B_ESP32-wrover-kit_4M316k_ETH] -extends = env:test_B_ESP32-wrover-kit_4M316k -platform = ${env:test_B_ESP32-wrover-kit_4M316k.platform} -build_flags = ${env:test_B_ESP32-wrover-kit_4M316k.build_flags} -DHAS_ETHERNET - -DTESTING_USE_RTTTL - -[env:test_C_ESP32-wrover-kit_4M316k_ETH] -extends = env:test_C_ESP32-wrover-kit_4M316k -platform = ${env:test_C_ESP32-wrover-kit_4M316k.platform} -build_flags = ${env:test_C_ESP32-wrover-kit_4M316k.build_flags} -DHAS_ETHERNET - -DTESTING_USE_RTTTL - -[env:test_D_ESP32-wrover-kit_4M316k_ETH] -extends = env:test_D_ESP32-wrover-kit_4M316k -platform = ${env:test_D_ESP32-wrover-kit_4M316k.platform} -build_flags = ${env:test_D_ESP32-wrover-kit_4M316k.build_flags} -DHAS_ETHERNET - -DTESTING_USE_RTTTL - ;;; ESP32-s2 *********************************************************** [env:normal_ESP32s2_4M316k] From 19acf9fa9fef25a12c938887a5eb7e9e71410d6f Mon Sep 17 00:00:00 2001 From: TD-er Date: Mon, 6 Dec 2021 00:31:17 +0100 Subject: [PATCH 019/147] [PIO] Add missing ESP32-S2 build envs Not sure if there is already an official 16M board present with the ESP32-S2, but if there is, there will be support for it :) --- platformio_esp32_envs.ini | 47 +++++++++++++++++++++++++++++++++++---- tools/pio/copy_files.py | 7 +++--- 2 files changed, 47 insertions(+), 7 deletions(-) diff --git a/platformio_esp32_envs.ini b/platformio_esp32_envs.ini index 766424a5cc..d6cba5ee03 100644 --- a/platformio_esp32_envs.ini +++ b/platformio_esp32_envs.ini @@ -47,6 +47,7 @@ upload_protocol = ftdi debug_tool = ftdi ;debug_extra_cmds = break Misc.ino:3011 + [esp32_LittleFS] extends = esp32_common platform_packages = ${esp32_common.platform_packages} @@ -54,6 +55,12 @@ platform_packages = ${esp32_common.platform_packages} ;lib_deps = ${esp32_common.lib_deps}, https://github.com/lorol/LITTLEFS.git board_build.filesystem = littlefs +[esp32s2_LittleFS] +extends = esp32s2_common +platform_packages = ${esp32s2_common.platform_packages} + platformio/tool-mklittlefs @ ~1.203.200522 +;lib_deps = ${esp32_common.lib_deps}, https://github.com/lorol/LITTLEFS.git +board_build.filesystem = littlefs @@ -64,6 +71,13 @@ build_flags = ${esp32_common.build_flags} -DPLUGIN_BUILD_CUSTOM extra_scripts = ${esp32_common.extra_scripts} pre:tools/pio/pre_custom_esp32.py +[env:custom_IR_ESP32_4M316k] +extends = esp32_common +build_flags = ${esp32_common.build_flags} -DPLUGIN_BUILD_CUSTOM -DPLUGIN_BUILD_IR +lib_ignore = ${esp32_always.lib_ignore}, ESP32_ping, HeatpumpIR +extra_scripts = ${esp32_common.extra_scripts} + pre:tools/pio/pre_custom_esp32.py + ; ESP32-S2 [env:custom_ESP32s2_4M316k] @@ -74,11 +88,11 @@ extra_scripts = ${esp32s2_common.extra_scripts} pre:tools/pio/pre_custom_esp32.py -[env:custom_IR_ESP32_4M316k] -extends = esp32_common -build_flags = ${esp32_common.build_flags} -DPLUGIN_BUILD_CUSTOM -DPLUGIN_BUILD_IR +[env:custom_IR_ESP32s2_4M316k] +extends = esp32s2_common +build_flags = ${esp32s2_common.build_flags} -DPLUGIN_BUILD_CUSTOM -DPLUGIN_BUILD_IR lib_ignore = ${esp32_always.lib_ignore}, ESP32_ping, HeatpumpIR -extra_scripts = ${esp32_common.extra_scripts} +extra_scripts = ${esp32s2_common.extra_scripts} pre:tools/pio/pre_custom_esp32.py [env:normal_ESP32_4M316k] @@ -349,6 +363,26 @@ build_flags = ${esp32_LittleFS.build_flags} ; -mfix-esp32-psram-cache-issue board = lolin_d32_pro +[max_ESP32s2_16M] +extends = esp32s2_common +lib_ignore = ${esp32_always.lib_ignore}, ESP32_ping +lib_deps = ${esp32s2_common.lib_deps}, VL53L0X +board_upload.maximum_size = 4194304 +build_flags = ${esp32s2_common.build_flags} + -DFEATURE_ARDUINO_OTA + -DPLUGIN_BUILD_MAX_ESP32 + -DPLUGIN_BUILD_IR_EXTENDED + +; A Lolin D32 PRO with 16MB Flash, allowing 4MB sketch size, and file storage using LittleFS filesystem +[max_ESP32s2_16M_LittleFS] +extends = esp32s2_LittleFS +lib_ignore = ${esp32_always.lib_ignore}, ESP32_ping +lib_deps = ${esp32s2_LittleFS.lib_deps}, VL53L0X +board_upload.maximum_size = 4194304 +build_flags = ${esp32s2_LittleFS.build_flags} + -DFEATURE_ARDUINO_OTA + -DPLUGIN_BUILD_MAX_ESP32 + -DPLUGIN_BUILD_IR_EXTENDED ; A Lolin D32 PRO with 16MB Flash, allowing 4MB sketch size, and 1MB assigned to file storage using SPIFFS filesystem [env:max_ESP32_16M1M] @@ -379,3 +413,8 @@ build_flags = ${env:max_ESP32_16M2M_LittleFS.build_flags} -DHAS_ET [env:max_ESP32_16M8M_LittleFS_ETH] extends = env:max_ESP32_16M8M_LittleFS build_flags = ${env:max_ESP32_16M8M_LittleFS.build_flags} -DHAS_ETHERNET + +[env:max_ESP32s2_16M8M_LittleFS] +extends = max_ESP32s2_16M_LittleFS +board_build.partitions = esp32_partition_app4096k_spiffs8124k.csv + diff --git a/tools/pio/copy_files.py b/tools/pio/copy_files.py index d0458fe412..10d1943618 100644 --- a/tools/pio/copy_files.py +++ b/tools/pio/copy_files.py @@ -24,9 +24,10 @@ def get_max_bin_size(env_name, file_suffix): if "factory" in file_suffix: # Factory bin files include a part which is not overwritten via OTA max_bin_size = max_bin_size + 65536 - if "_ESP32_16M8M" in env_name or "_ESP32_16M2M" in env_name or "_ESP32_16M1M" in env_name: - # ESP32 with 4096k of sketch space. - max_bin_size = 4194304 + if "_ESP32_" in env_name or "_ESP32s2_" in env_name: + if "_16M8M" in env_name or "_16M2M" in env_name or "_16M1M" in env_name: + # ESP32 with 4096k of sketch space. + max_bin_size = 4194304 if "debug_" in env_name: # Debug env, used for analysis, not to be run on a node. max_bin_size = 0 From e9a097527defe605604016942b5b462279f7074e Mon Sep 17 00:00:00 2001 From: TD-er Date: Mon, 27 Dec 2021 23:56:32 +0100 Subject: [PATCH 020/147] [IDF4.4] Use Tasmota's IDF 4.4 platform-tasmota-espressif32 As suggested by @Jason2866 here: https://github.com/letscontrolit/ESPEasy/issues/3863#issuecomment-1001231966 --- platformio_core_defs.ini | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/platformio_core_defs.ini b/platformio_core_defs.ini index c12e19d821..44eaeaf4d2 100644 --- a/platformio_core_defs.ini +++ b/platformio_core_defs.ini @@ -158,8 +158,10 @@ platform = espressif32 @ 3.3.2 platform_packages = framework-arduinoespressif32 build_flags = -DESP32_STAGE +; IDF 4.4 = platform-espressif32 3.4.x = Tasmota platform-espressif32 2.0.2 +; Just for those who lost track of the extremely confusing numbering schema. [core_esp32_3_4_0] -platform = https://github.com/tasmota/platform-espressif32/releases/download/v3.4.0/Tasmota-platform-espressif32.zip +platform = https://github.com/tasmota/platform-espressif32/releases/download/v2.0.2/platform-tasmota-espressif32-2.0.2.zip platform_packages = framework-arduinoespressif32 @ https://github.com/tasmota/arduino-esp32/releases/download/2.0.1.1/framework-arduinoespressif32-release_IDF4.4.tar.gz build_flags = -DESP32_STAGE @@ -171,7 +173,7 @@ platform_packages = framework-arduinoespressif32 @ https://github.com/ta build_flags = -DESP32_STAGE [core_esp32_3_4_0_esp32s2] -platform = https://github.com/tasmota/platform-espressif32/releases/download/v3.4.0/Tasmota-platform-espressif32.zip +platform = https://github.com/tasmota/platform-espressif32/releases/download/v2.0.2/platform-tasmota-espressif32-2.0.2.zip platform_packages = framework-arduinoespressif32 @ https://github.com/tasmota/arduino-esp32/releases/download/2.0.1.1/framework-arduinoespressif32-release_IDF4.4.tar.gz build_flags = -DESP32_STAGE From c30893b0de4e4046616a8c98b47d1a5f70c81b75 Mon Sep 17 00:00:00 2001 From: TD-er Date: Tue, 28 Dec 2021 13:26:03 +0100 Subject: [PATCH 021/147] [IDF4.4-v2.0.2] Now actually using the latest espressif/Arduino-esp32 --- platformio_core_defs.ini | 37 ++++--------------------------------- platformio_esp32_envs.ini | 16 ++++++++-------- 2 files changed, 12 insertions(+), 41 deletions(-) diff --git a/platformio_core_defs.ini b/platformio_core_defs.ini index 44eaeaf4d2..a1e64cca22 100644 --- a/platformio_core_defs.ini +++ b/platformio_core_defs.ini @@ -139,50 +139,21 @@ platform_packages = mcspr/toolchain-xtensa @ ~5.100300.211127 -; Updated ESP-IDF to the latest stable 4.0.1 -; See: https://github.com/platformio/platform-espressif32/releases -[core_esp32_1_12_2] -platform = espressif32@1.12.4 [core_esp32_2_1_0] platform = espressif32@2.1.0 build_flags = -DESP32_STAGE -[core_esp32_3_3_0] -platform = espressif32 @ 3.3.0 -platform_packages = framework-arduinoespressif32 -build_flags = -DESP32_STAGE - -[core_esp32_3_3_2] -platform = espressif32 @ 3.3.2 -platform_packages = framework-arduinoespressif32 -build_flags = -DESP32_STAGE -; IDF 4.4 = platform-espressif32 3.4.x = Tasmota platform-espressif32 2.0.2 +; Updated ESP-IDF to the latest stable 4.0.1 +; See: https://github.com/platformio/platform-espressif32/releases +; IDF 4.4 = platform-espressif32 3.4.x = espressif/arduino-esp32 tag 2.0.2 ; Just for those who lost track of the extremely confusing numbering schema. -[core_esp32_3_4_0] +[core_esp32_IDF4_4__2_0_2] platform = https://github.com/tasmota/platform-espressif32/releases/download/v2.0.2/platform-tasmota-espressif32-2.0.2.zip -platform_packages = framework-arduinoespressif32 @ https://github.com/tasmota/arduino-esp32/releases/download/2.0.1.1/framework-arduinoespressif32-release_IDF4.4.tar.gz build_flags = -DESP32_STAGE -[core_esp32_3_3_2_esp32s2] -platform = https://github.com/platformio/platform-espressif32.git#feature/arduino-idf-master -platform_packages = framework-arduinoespressif32 @ https://github.com/tasmota/arduino-esp32/releases/download/2.0.1/framework-arduinoespressif32-release_IDF4.4.tar.gz - platformio/tool-esptoolpy @ https://github.com/tasmota/esptool/releases/download/v3.2/esptool-v3.2.zip -build_flags = -DESP32_STAGE - -[core_esp32_3_4_0_esp32s2] -platform = https://github.com/tasmota/platform-espressif32/releases/download/v2.0.2/platform-tasmota-espressif32-2.0.2.zip -platform_packages = framework-arduinoespressif32 @ https://github.com/tasmota/arduino-esp32/releases/download/2.0.1.1/framework-arduinoespressif32-release_IDF4.4.tar.gz -build_flags = -DESP32_STAGE - - -[core_esp32_3_0_0] -platform = espressif32@3.0.0 -platform_packages = framework-arduinoespressif32 @ https://github.com/Jason2866/arduino-esp32/releases/download/1.0.5-rc6/esp32-1.0.5-rc6.zip - - [core_esp32_stage] platform = https://github.com/platformio/platform-espressif32.git#feature/arduino-idf-master platform_packages = framework-arduinoespressif32 @ https://github.com/tasmota/arduino-esp32/releases/download/2.0.1/framework-arduinoespressif32-release_IDF4.4.tar.gz diff --git a/platformio_esp32_envs.ini b/platformio_esp32_envs.ini index 7490029745..22953b6bc7 100644 --- a/platformio_esp32_envs.ini +++ b/platformio_esp32_envs.ini @@ -9,7 +9,7 @@ lib_ignore = ESP8266WiFi, ESP8266Ping, ESP8266WebServer, ESP8266H [esp32_common] -extends = common, core_esp32_3_4_0 +extends = common, core_esp32_IDF4_4__2_0_2 lib_deps = td-er/ESPeasySerial @ 2.0.8, adafruit/Adafruit ILI9341 @ ^1.5.6, Adafruit GFX Library, LOLIN_EPD, Adafruit BusIO, VL53L0X @ 1.3.0, SparkFun VL53L1X 4m Laser Distance Sensor @ 1.2.9, td-er/SparkFun MAX1704x Fuel Gauge Arduino Library @ ^1.0.1, ArduinoOTA, ESP32HTTPUpdateServer, FrogmoreScd30, Multi Channel Relay Arduino Library lib_ignore = ${esp32_always.lib_ignore}, ESP32_ping, IRremoteESP8266, HeatpumpIR, ESP32 BLE Arduino board_build.f_flash = 80000000L @@ -19,7 +19,7 @@ board_build.partitions = esp32_partition_app1810k_spiffs316k.csv extra_scripts = post:tools/pio/post_esp32.py ${extra_scripts_default.extra_scripts} build_unflags = -Wall -build_flags = ${core_esp32_3_4_0.build_flags} +build_flags = ${core_esp32_IDF4_4__2_0_2.build_flags} ${mqtt_flags.build_flags} -DCONFIG_FREERTOS_ASSERT_DISABLE -DCONFIG_LWIP_ESP_GRATUITOUS_ARP @@ -27,6 +27,7 @@ build_flags = ${core_esp32_3_4_0.build_flags} -DCORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_NONE board = esp32dev monitor_filters = esp32_exception_decoder +platform_packages = [esp32s2_common] @@ -36,8 +37,7 @@ build_flags = ${esp32_common.build_flags} -DBOARD_HAS_PSRAM -DESP32_ENABLE_PSRAM board = esp32-s2-saola-1 -platform = ${core_esp32_3_4_0_esp32s2.platform} -platform_packages = ${core_esp32_3_4_0_esp32s2.platform_packages} + [ESP32-wrover-kit] @@ -50,15 +50,15 @@ debug_tool = ftdi [esp32_LittleFS] extends = esp32_common -platform_packages = ${esp32_common.platform_packages} - platformio/tool-mklittlefs @ ~1.203.200522 +;platform_packages = ${esp32_common.platform_packages} +; platformio/tool-mklittlefs @ ~1.203.200522 ;lib_deps = ${esp32_common.lib_deps}, https://github.com/lorol/LITTLEFS.git board_build.filesystem = littlefs [esp32s2_LittleFS] extends = esp32s2_common -platform_packages = ${esp32s2_common.platform_packages} - platformio/tool-mklittlefs @ ~1.203.200522 +;platform_packages = ${esp32s2_common.platform_packages} +; platformio/tool-mklittlefs @ ~1.203.200522 ;lib_deps = ${esp32_common.lib_deps}, https://github.com/lorol/LITTLEFS.git board_build.filesystem = littlefs From 659685485eb71b06d25616dc1a17e66be8411a13 Mon Sep 17 00:00:00 2001 From: TD-er Date: Tue, 28 Dec 2021 17:22:55 +0100 Subject: [PATCH 022/147] [LittleFS] Make sure to always define USE_LITTLEFS on LittleFS envs --- platformio_esp32_envs.ini | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/platformio_esp32_envs.ini b/platformio_esp32_envs.ini index 22953b6bc7..847de0809e 100644 --- a/platformio_esp32_envs.ini +++ b/platformio_esp32_envs.ini @@ -53,6 +53,8 @@ extends = esp32_common ;platform_packages = ${esp32_common.platform_packages} ; platformio/tool-mklittlefs @ ~1.203.200522 ;lib_deps = ${esp32_common.lib_deps}, https://github.com/lorol/LITTLEFS.git +build_flags = ${esp32_common.build_flags} + -DUSE_LITTLEFS board_build.filesystem = littlefs [esp32s2_LittleFS] @@ -60,6 +62,8 @@ extends = esp32s2_common ;platform_packages = ${esp32s2_common.platform_packages} ; platformio/tool-mklittlefs @ ~1.203.200522 ;lib_deps = ${esp32_common.lib_deps}, https://github.com/lorol/LITTLEFS.git +build_flags = ${esp32s2_common.build_flags} + -DUSE_LITTLEFS board_build.filesystem = littlefs From 840f706fc711f0de1fd86e399e9f6c0e17b77002 Mon Sep 17 00:00:00 2001 From: TD-er Date: Sun, 2 Jan 2022 02:16:46 +0100 Subject: [PATCH 023/147] [PIO build ESP32] Attempt to fix boot loop on ESP32 with IDF4.4 16M flash ESP32 units keep rebooting when built with a partitioning schema for 16M flash units. This does not happen on older IDF versions, but does happen on IDF 4.4 --- .gitignore | 8 ++ boards/esp32_16M.json | 38 ++++++ .../configure/esp32/spi_download.conf | 125 ------------------ platformio_esp32_envs.ini | 11 +- src/src/Helpers/ESPEasy_time.cpp | 2 +- tools/pio/post_esp32.py | 9 +- 6 files changed, 63 insertions(+), 130 deletions(-) create mode 100644 boards/esp32_16M.json delete mode 100644 dist/Espressif_flash_download_tool_v3.8.5/configure/esp32/spi_download.conf diff --git a/.gitignore b/.gitignore index 981c8ae045..70cc10dd02 100644 --- a/.gitignore +++ b/.gitignore @@ -53,3 +53,11 @@ build_output/ **/__tmpfile.cpp src/_Custom.h + +dist/Espressif_flash_download_tool_v3.8.5/logs/ + +dist/Espressif_flash_download_tool_v3.8.5/dl_temp/bin_tmp/ + +dist/Espressif_flash_download_tool_v3.8.5/dl_temp/_temp_by_dltool/ + +dist/Espressif_flash_download_tool_v3.8.5/configure/esp32/spi_download.conf diff --git a/boards/esp32_16M.json b/boards/esp32_16M.json new file mode 100644 index 0000000000..bd61eb4e9b --- /dev/null +++ b/boards/esp32_16M.json @@ -0,0 +1,38 @@ +{ + "build": { + "arduino":{ + "ldscript": "esp32_out.ld" + }, + "core": "esp32", + "extra_flags": "-DARDUINO_ESP32_DEV -DBOARD_HAS_PSRAM -DARDUINO_USB_CDC_ON_BOOT=0 -DESP32_16M", + "f_cpu": "240000000L", + "f_flash": "40000000L", + "flash_mode": "dout", + "mcu": "esp32", + "variant": "esp32", + "partitions": "esp32_partition_app4096k_spiffs8124k.csv" + }, + "connectivity": [ + "wifi", + "bluetooth", + "ethernet", + "can" + ], + "debug": { + "openocd_target": "esp32.cfg" + }, + "frameworks": [ + "arduino", + "espidf" + ], + "name": "Espressif Generic ESP32 16M Flash, ESPEasy 4096 Code/OTA, 8M FS", + "upload": { + "flash_size": "16MB", + "maximum_ram_size": 327680, + "maximum_size": 16777216, + "require_upload_port": true, + "speed": 460800 + }, + "url": "https://en.wikipedia.org/wiki/ESP32", + "vendor": "Espressif" + } \ No newline at end of file diff --git a/dist/Espressif_flash_download_tool_v3.8.5/configure/esp32/spi_download.conf b/dist/Espressif_flash_download_tool_v3.8.5/configure/esp32/spi_download.conf deleted file mode 100644 index 16bc6a9fd1..0000000000 --- a/dist/Espressif_flash_download_tool_v3.8.5/configure/esp32/spi_download.conf +++ /dev/null @@ -1,125 +0,0 @@ -[EFUSE CHECK] -efuse_mode = 1 -efuse_err_halt = 1 - -[DOWNLOAD PATH] -file_sel0 = 0 -file_path0 = -file_offset0 = -file_sel1 = 0 -file_path1 = -file_offset1 = -file_sel2 = 0 -file_path2 = -file_offset2 = -file_sel3 = 0 -file_path3 = -file_offset3 = -file_sel4 = 0 -file_path4 = -file_offset4 = -file_sel5 = 0 -file_path5 = -file_offset5 = -file_sel6 = 0 -file_path6 = -file_offset6 = -file_sel7 = 0 -file_path7 = -file_offset7 = -file_sel8 = 0 -file_path8 = -file_offset8 = -file_sel9 = 0 -file_path9 = -file_offset9 = -file_sel10 = 0 -file_path10 = -file_offset10 = -file_sel11 = 0 -file_path11 = -file_offset11 = -file_sel12 = 0 -file_path12 = -file_offset12 = -file_sel13 = 0 -file_path13 = -file_offset13 = -file_sel14 = 0 -file_path14 = -file_offset14 = -file_sel15 = 0 -file_path15 = -file_offset15 = -file_sel16 = 0 -file_path16 = -file_offset16 = -file_sel17 = 0 -file_path17 = -file_offset17 = -file_sel18 = 0 -file_path18 = -file_offset18 = -default_path = C:\bin - -[LOCK] -lock_setting_password = 12345678 -lock_settings = 0 - -[FLASH_CRYSTAL] -spiautoset = 0 -spicfgdis = 0 -spispeed = 0 -spimode = 2 -flashsize = 2 -crystal = 0 - -[DOWNLOAD] -autostart1 = 0 -com_port1 = COM90 -baudrate1 = 4 -checkmac1 = 1 -erase_flash_en = True -new_status = 0 -num_bytes = 0 -non_volatile = False - -[FACTORY_MODE] -factory_mode_enable = False - -[LOCK_CHECK] -log_check_enable = False -log_check_baud = 115200 -log_check_str = -log_check_timeout = 3 - -[LOG_CHECK] -log_check_enable = False -log_check_baud = 115200 -log_check_str = 1.0.26 -log_check_timeout = 3 -log_check_port = COM6 -log_check_cmd_str = AT+GMR -log_check_enable_cmd = False -log_check_delaytime = 3 - -[ESPTOOL_PARAM] -after = hard_reset -baud = 115200 -before = default_reset -chip = auto -compress = 0 -flash_freq = keep -flash_mode = keep -flash_size = keep -no_compress = False -no_progress = False -no_stub = False -operation = write_flash -port = /dev/cu.SLAB_USBtoUART -spi_connection = 0 -verify = False - -[MAC SAVE] -mac_save_enable = False - diff --git a/platformio_esp32_envs.ini b/platformio_esp32_envs.ini index 847de0809e..9050860d1a 100644 --- a/platformio_esp32_envs.ini +++ b/platformio_esp32_envs.ini @@ -12,7 +12,6 @@ lib_ignore = ESP8266WiFi, ESP8266Ping, ESP8266WebServer, ESP8266H extends = common, core_esp32_IDF4_4__2_0_2 lib_deps = td-er/ESPeasySerial @ 2.0.8, adafruit/Adafruit ILI9341 @ ^1.5.6, Adafruit GFX Library, LOLIN_EPD, Adafruit BusIO, VL53L0X @ 1.3.0, SparkFun VL53L1X 4m Laser Distance Sensor @ 1.2.9, td-er/SparkFun MAX1704x Fuel Gauge Arduino Library @ ^1.0.1, ArduinoOTA, ESP32HTTPUpdateServer, FrogmoreScd30, Multi Channel Relay Arduino Library lib_ignore = ${esp32_always.lib_ignore}, ESP32_ping, IRremoteESP8266, HeatpumpIR, ESP32 BLE Arduino -board_build.f_flash = 80000000L board_build.flash_mode = dout board_upload.maximum_size = 1900544 board_build.partitions = esp32_partition_app1810k_spiffs316k.csv @@ -75,6 +74,12 @@ build_flags = ${esp32_common.build_flags} -DPLUGIN_BUILD_CUSTOM extra_scripts = ${esp32_common.extra_scripts} pre:tools/pio/pre_custom_esp32.py +[env:custom_ESP32_4M316k_LittleFS] +extends = esp32_LittleFS +build_flags = ${esp32_LittleFS.build_flags} -DPLUGIN_BUILD_CUSTOM +extra_scripts = ${esp32_LittleFS.extra_scripts} + pre:tools/pio/pre_custom_esp32.py + [env:custom_IR_ESP32_4M316k] extends = esp32_common build_flags = ${esp32_common.build_flags} -DPLUGIN_BUILD_CUSTOM -DPLUGIN_BUILD_IR @@ -350,8 +355,10 @@ build_flags = ${esp32_common.build_flags} ; TODO: To enable PS-RAM Support needs more build flags than these 2, for now define ESP32_ENABLE_PSRAM is used to en/disable detecting PS-Ram size on Info page ; -DBOARD_HAS_PSRAM // both flags already enabled for Lolin D32 Pro board by PlatformIO ; -mfix-esp32-psram-cache-issue +;board = esp32_16M board = lolin_d32_pro + ; A Lolin D32 PRO with 16MB Flash, allowing 4MB sketch size, and file storage using LittleFS filesystem [max_ESP32_16M_LittleFS] extends = esp32_LittleFS @@ -365,7 +372,7 @@ build_flags = ${esp32_LittleFS.build_flags} ; TODO: To enable PS-RAM Support needs more build flags than these 2, for now define ESP32_ENABLE_PSRAM is used to en/disable detecting PS-Ram size on Info page ; -DBOARD_HAS_PSRAM // both flags already enabled for Lolin D32 Pro board by PlatformIO ; -mfix-esp32-psram-cache-issue -board = lolin_d32_pro +board = esp32_16M [max_ESP32s2_16M] extends = esp32s2_common diff --git a/src/src/Helpers/ESPEasy_time.cpp b/src/src/Helpers/ESPEasy_time.cpp index 78970a1014..e6be983b90 100644 --- a/src/src/Helpers/ESPEasy_time.cpp +++ b/src/src/Helpers/ESPEasy_time.cpp @@ -658,7 +658,7 @@ float ESPEasy_time::diurnalArc(float dec, float lat) { float height = -50.0f / 60.0f * rad; float latRad = lat * rad; - return 12.0 * acos((sin(height) - sin(latRad) * sin(dec)) / (cos(latRad) * cos(dec))) / M_PI; + return 12.0f * acos((sin(height) - sin(latRad) * sin(dec)) / (cos(latRad) * cos(dec))) / M_PI; } float ESPEasy_time::equationOfTime(int doy) { diff --git a/tools/pio/post_esp32.py b/tools/pio/post_esp32.py index 611b7dc570..c0b5ce6dfc 100644 --- a/tools/pio/post_esp32.py +++ b/tools/pio/post_esp32.py @@ -7,15 +7,20 @@ def esp32_create_factory_bin(source, target, env): new_file_name = env.subst("$BUILD_DIR/${PROGNAME}-factory.bin") sections = env.subst(env.get('FLASH_EXTRA_IMAGES')) new_file = open(new_file_name,"wb") + print(" {} | {}".format("Offset", "File")) for section in sections: sect_adr,sect_file = section.split(" ",1) + print(" - {} | {}".format(sect_adr,sect_file)) source = open(sect_file,"rb") new_file.seek(int(sect_adr,0)-offset) new_file.write(source.read()); source.close() - firmware = open(env.subst("$BUILD_DIR/${PROGNAME}.bin"),"rb") - new_file.seek(0x10000-offset) + firmware_name = env.subst("$BUILD_DIR/${PROGNAME}.bin") + firmware_start = 0x10000-offset + firmware = open(firmware_name,"rb") + print(" - {} | {}".format(hex(firmware_start),firmware_name)) + new_file.seek(firmware_start) new_file.write(firmware.read()) new_file.close() firmware.close() From 9b110b5053cb7ec57145406dd9b4989d3115ef7b Mon Sep 17 00:00:00 2001 From: TD-er Date: Sun, 2 Jan 2022 02:17:36 +0100 Subject: [PATCH 024/147] [Cleanup] Misc optimisations to reduce build size --- src/_P109_ThermOLED.ino | 6 +++--- src/src/ESPEasyCore/ESPEasyRules.cpp | 7 +++---- src/src/WebServer/DevicesPage.cpp | 9 +++++---- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/_P109_ThermOLED.ino b/src/_P109_ThermOLED.ino index 5f303cd6e9..5ae698744e 100644 --- a/src/_P109_ThermOLED.ino +++ b/src/_P109_ThermOLED.ino @@ -744,7 +744,7 @@ void P109_display_current_temp() { if (atempstr.length() > 0) { float atemp = atempstr.toFloat(); - atemp = (round(atemp * 10)) / 10.0; + atemp = (round(atemp * 10)) / 10.0f; if (Plugin_109_prev_temp != atemp) { P109_display->setColor(BLACK); @@ -760,7 +760,7 @@ void P109_display_current_temp() { void P109_display_setpoint_temp(byte force) { if (UserVar[Plugin_109_varindex + 2] == 1) { - float stemp = (round(UserVar[Plugin_109_varindex] * 10)) / 10.0; + float stemp = (round(UserVar[Plugin_109_varindex] * 10)) / 10.0f; if ((Plugin_109_prev_setpoint != stemp) || (force == 1)) { P109_display->setColor(BLACK); @@ -876,7 +876,7 @@ void P109_display_page() { } void P109_setSetpoint(String sptemp) { - float stemp = (round(UserVar[Plugin_109_varindex] * 10)) / 10.0; + float stemp = (round(UserVar[Plugin_109_varindex] * 10)) / 10.0f; if ((sptemp.charAt(0) == '+') || (sptemp.charAt(0) == 'p')) { stemp = stemp + sptemp.substring(1).toFloat(); diff --git a/src/src/ESPEasyCore/ESPEasyRules.cpp b/src/src/ESPEasyCore/ESPEasyRules.cpp index d1d2eb1ab6..19bf1eda62 100644 --- a/src/src/ESPEasyCore/ESPEasyRules.cpp +++ b/src/src/ESPEasyCore/ESPEasyRules.cpp @@ -522,8 +522,7 @@ bool parse_bitwise_functions(const String& cmd_s_lower, const String& arg1, cons } bool parse_math_functions(const String& cmd_s_lower, const String& arg1, const String& arg2, const String& arg3, double& result) { - double farg1; - float farg2, farg3 = 0.0f; + double farg1, farg2, farg3 = 0.0f; if (!validDoubleFromString(arg1, farg1)) { return false; @@ -532,9 +531,9 @@ bool parse_math_functions(const String& cmd_s_lower, const String& arg1, const S if (cmd_s_lower.equals(F("constrain"))) { // Contrain a value X to be within range of A to B // Syntax like {constrain:x:a:b} to constrain x in range a...b - if (validFloatFromString(arg2, farg2) && validFloatFromString(arg3, farg3)) { + if (validDoubleFromString(arg2, farg2) && validDoubleFromString(arg3, farg3)) { if (farg2 > farg3) { - const float tmp = farg2; + const double tmp = farg2; farg2 = farg3; farg3 = tmp; } diff --git a/src/src/WebServer/DevicesPage.cpp b/src/src/WebServer/DevicesPage.cpp index 41a6fe057d..3bc39c7a78 100644 --- a/src/src/WebServer/DevicesPage.cpp +++ b/src/src/WebServer/DevicesPage.cpp @@ -1283,10 +1283,11 @@ void devicePage_show_task_values(taskIndex_t taskIndex, deviceIndex_t DeviceInde html_TR_TD(); addHtmlInt(varNr + 1); html_TD(); - String id = F("TDVN"); // ="taskdevicevaluename" - id += (varNr + 1); - addTextBox(id, ExtraTaskSettings.TaskDeviceValueNames[varNr], NAME_FORMULA_LENGTH_MAX); - + { + String id = F("TDVN"); // ="taskdevicevaluename" + id += (varNr + 1); + addTextBox(id, ExtraTaskSettings.TaskDeviceValueNames[varNr], NAME_FORMULA_LENGTH_MAX); + } if (Device[DeviceIndex].FormulaOption) { html_TD(); From 91a427a08c745b4d135d5bc6dbe345c53b7d6a4a Mon Sep 17 00:00:00 2001 From: TD-er Date: Sun, 2 Jan 2022 02:20:03 +0100 Subject: [PATCH 025/147] [LittleFS] Some cleanup to fix boot loops ESP32 IDF4.4 for 16M flash --- src/src/Helpers/ESPEasy_FactoryDefault.cpp | 4 +- src/src/Helpers/ESPEasy_Storage.cpp | 73 +++++++++++++++++----- src/src/Helpers/ESPEasy_Storage.h | 8 +++ src/src/WebServer/WebServer.cpp | 14 ----- src/src/WebServer/WebServer.h | 2 - 5 files changed, 67 insertions(+), 34 deletions(-) diff --git a/src/src/Helpers/ESPEasy_FactoryDefault.cpp b/src/src/Helpers/ESPEasy_FactoryDefault.cpp index 1545621dc0..b996002ced 100644 --- a/src/src/Helpers/ESPEasy_FactoryDefault.cpp +++ b/src/src/Helpers/ESPEasy_FactoryDefault.cpp @@ -63,9 +63,9 @@ void ResetFactory() saveToRTC(); // always format on factory reset, in case of corrupt FS - ESPEASY_FS.end(); +// ESPEASY_FS.end(); serialPrintln(F("RESET: formatting...")); - ESPEASY_FS.format(); + FS_format(); serialPrintln(F("RESET: formatting done...")); if (!ESPEASY_FS.begin()) diff --git a/src/src/Helpers/ESPEasy_Storage.cpp b/src/src/Helpers/ESPEasy_Storage.cpp index 55e93650a1..48b95e0c6e 100644 --- a/src/src/Helpers/ESPEasy_Storage.cpp +++ b/src/src/Helpers/ESPEasy_Storage.cpp @@ -326,19 +326,19 @@ void fileSystemCheck() checkRAM(F("fileSystemCheck")); #endif addLog(LOG_LEVEL_INFO, F("FS : Mounting...")); - +#if defined(ESP32) && defined(USE_LITTLEFS) + if (getPartionCount(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_SPIFFS) != 0 + && ESPEASY_FS.begin()) +#else if (ESPEASY_FS.begin()) +#endif { clearAllCaches(); - #if defined(ESP8266) - fs::FSInfo fs_info; - ESPEASY_FS.info(fs_info); - if (loglevelActiveFor(LOG_LEVEL_INFO)) { String log = F("FS : Mount successful, used "); - log = log + fs_info.usedBytes; - log = log + F(" bytes of "); - log = log + fs_info.totalBytes; + log += SpiffsUsedBytes(); + log += F(" bytes of "); + log += SpiffsTotalBytes(); addLog(LOG_LEVEL_INFO, log); } @@ -348,26 +348,61 @@ void fileSystemCheck() while (retries > 0 && GarbageCollection()) { --retries; } - #endif // if defined(ESP8266) fs::File f = tryOpenFile(SettingsType::getSettingsFileName(SettingsType::Enum::BasicSettings_Type).c_str(), "r"); - - if (!f) - { + if (f) { + f.close(); + } else { ResetFactory(); } - - if (f) { f.close(); } } else { - String log = F("FS : Mount failed"); + const __FlashStringHelper * log = F("FS : Mount failed"); serialPrintln(log); addLog(LOG_LEVEL_ERROR, log); ResetFactory(); } } +bool FS_format() { + #ifdef USE_LITTLEFS + #ifdef ESP32 + disableCore1WDT(); + const bool res = ESPEASY_FS.begin(true); + ESPEASY_FS.end(); + enableCore1WDT(); + return res; + #else + return ESPEASY_FS.format(); + #endif + #else + return ESPEASY_FS.format(); + #endif +} + +#ifdef ESP32 + +# include + +int getPartionCount(uint8_t pType, uint8_t pSubType) { + esp_partition_type_t partitionType = static_cast(pType); + esp_partition_subtype_t subtype = static_cast(pSubType); + esp_partition_iterator_t _mypartiterator = esp_partition_find(partitionType, subtype, NULL); + int nrPartitions = 0; + + if (_mypartiterator) { + do { + ++nrPartitions; + } while ((_mypartiterator = esp_partition_next(_mypartiterator)) != NULL); + } + esp_partition_iterator_release(_mypartiterator); + return nrPartitions; +} + + +#endif + /********************************************************************************************\ Garbage collection \*********************************************************************************************/ @@ -1553,7 +1588,13 @@ String getPartitionType(uint8_t pType, uint8_t pSubType) { case ESP_PARTITION_SUBTYPE_DATA_COREDUMP: return F("COREDUMP"); case ESP_PARTITION_SUBTYPE_DATA_ESPHTTPD: return F("ESPHTTPD"); case ESP_PARTITION_SUBTYPE_DATA_FAT: return F("FAT"); - case ESP_PARTITION_SUBTYPE_DATA_SPIFFS: return F("SPIFFS"); + case ESP_PARTITION_SUBTYPE_DATA_SPIFFS: + #ifdef USE_LITTLEFS + return F("LittleFS"); + #else + return F("SPIFFS"); + #endif + case 0x99: return F("EEPROM"); // Not defined in esp_partition_subtype_t default: break; } } diff --git a/src/src/Helpers/ESPEasy_Storage.h b/src/src/Helpers/ESPEasy_Storage.h index c9d88ceac6..18bffc11d4 100644 --- a/src/src/Helpers/ESPEasy_Storage.h +++ b/src/src/Helpers/ESPEasy_Storage.h @@ -45,6 +45,14 @@ String BuildFixes(); \*********************************************************************************************/ void fileSystemCheck(); +bool FS_format(); + +#ifdef ESP32 + +int getPartionCount(uint8_t pType, uint8_t pSubType = 0xFF); + +#endif + /********************************************************************************************\ Garbage collection \*********************************************************************************************/ diff --git a/src/src/WebServer/WebServer.cpp b/src/src/WebServer/WebServer.cpp index 3e115cd834..d0a0cd4244 100644 --- a/src/src/WebServer/WebServer.cpp +++ b/src/src/WebServer/WebServer.cpp @@ -1258,20 +1258,6 @@ void getStorageTableSVG(SettingsType::Enum settingsType) { # include -int getPartionCount(uint8_t pType) { - esp_partition_type_t partitionType = static_cast(pType); - esp_partition_iterator_t _mypartiterator = esp_partition_find(partitionType, ESP_PARTITION_SUBTYPE_ANY, NULL); - int nrPartitions = 0; - - if (_mypartiterator) { - do { - ++nrPartitions; - } while ((_mypartiterator = esp_partition_next(_mypartiterator)) != NULL); - } - esp_partition_iterator_release(_mypartiterator); - return nrPartitions; -} - void getPartitionTableSVG(uint8_t pType, unsigned int partitionColor) { int nrPartitions = getPartionCount(pType); diff --git a/src/src/WebServer/WebServer.h b/src/src/WebServer/WebServer.h index e3c6773509..c03bd8455b 100644 --- a/src/src/WebServer/WebServer.h +++ b/src/src/WebServer/WebServer.h @@ -202,8 +202,6 @@ void getStorageTableSVG(SettingsType::Enum settingsType); #ifdef ESP32 -int getPartionCount(uint8_t pType); - void getPartitionTableSVG(uint8_t pType, unsigned int partitionColor); From 76e410ab338160b7cce2c938ea780693beaaaf2b Mon Sep 17 00:00:00 2001 From: TD-er Date: Tue, 4 Jan 2022 00:30:16 +0100 Subject: [PATCH 026/147] [IDF4.4 + 16M] Make normal builds with IDF4.4 work on 16M flash --- boards/esp32-cam.json | 38 +++ boards/esp32-m5core2.json | 38 +++ boards/esp32-odroid.json | 38 +++ boards/esp32_16M.json | 72 ++--- boards/esp32_4M.json | 38 +++ boards/esp32_8M.json | 38 +++ boards/esp32_solo1_4M.json | 38 +++ boards/esp32c3.json | 35 ++ boards/esp32s2.json | 62 ++-- boards/esp8266_16M14M_board.json | 32 ++ boards/esp8266_1M128k.json | 32 ++ boards/esp8266_1M128k_OTA.json | 32 ++ boards/esp8266_2M1M.json | 32 ++ boards/esp8266_2M256.json | 32 ++ boards/esp8266_4M1M_board.json | 32 ++ boards/esp8266_4M2M_board.json | 32 ++ boards/esp8266_4M3M.json | 32 ++ boards/esp8266_zbbridge.json | 32 ++ boards/esp8285_1M128k.json | 32 ++ boards/esp8285_1M128k_OTA.json | 32 ++ esp32_partition_app1572k_spiffs983k.csv | 6 + esp32_partition_app1856k_spiffs320k.csv | 6 + esp32_partition_app2944k_spiffs10M.csv | 6 + esp32_partition_app2944k_spiffs2M.csv | 6 + platformio.ini | 3 +- platformio_esp32_envs.ini | 414 ++---------------------- platformio_esp82xx_base.ini | 42 +-- platformio_esp82xx_envs.ini | 185 ++++------- src/ESPEasy_common.h | 29 +- src/src/ESPEasyCore/ESPEasyRules.cpp | 7 +- src/src/Helpers/ESPEasy_Storage.cpp | 2 - src/src/Helpers/Hardware.cpp | 12 +- src/src/Helpers/Hardware.h | 2 + src/src/Helpers/OTA.cpp | 5 +- src/src/Helpers/StringProvider.cpp | 2 + src/src/Helpers/StringProvider.h | 1 + src/src/WebServer/CustomPage.cpp | 6 +- src/src/WebServer/FileList.cpp | 2 + src/src/WebServer/LoadFromFS.cpp | 6 +- src/src/WebServer/SysInfoPage.cpp | 6 +- 40 files changed, 856 insertions(+), 641 deletions(-) create mode 100644 boards/esp32-cam.json create mode 100644 boards/esp32-m5core2.json create mode 100644 boards/esp32-odroid.json create mode 100644 boards/esp32_4M.json create mode 100644 boards/esp32_8M.json create mode 100644 boards/esp32_solo1_4M.json create mode 100644 boards/esp32c3.json create mode 100644 boards/esp8266_16M14M_board.json create mode 100644 boards/esp8266_1M128k.json create mode 100644 boards/esp8266_1M128k_OTA.json create mode 100644 boards/esp8266_2M1M.json create mode 100644 boards/esp8266_2M256.json create mode 100644 boards/esp8266_4M1M_board.json create mode 100644 boards/esp8266_4M2M_board.json create mode 100644 boards/esp8266_4M3M.json create mode 100644 boards/esp8266_zbbridge.json create mode 100644 boards/esp8285_1M128k.json create mode 100644 boards/esp8285_1M128k_OTA.json create mode 100644 esp32_partition_app1572k_spiffs983k.csv create mode 100644 esp32_partition_app1856k_spiffs320k.csv create mode 100644 esp32_partition_app2944k_spiffs10M.csv create mode 100644 esp32_partition_app2944k_spiffs2M.csv diff --git a/boards/esp32-cam.json b/boards/esp32-cam.json new file mode 100644 index 0000000000..a0abdbbfef --- /dev/null +++ b/boards/esp32-cam.json @@ -0,0 +1,38 @@ +{ + "build": { + "arduino":{ + "ldscript": "esp32_out.ld" + }, + "core": "esp32", + "extra_flags": "-DARDUINO_ESP32_DEV -DBOARD_HAS_PSRAM -DHAS_PSRAM_FIX -mfix-esp32-psram-cache-issue -mfix-esp32-psram-cache-strategy=memw -DARDUINO_USB_CDC_ON_BOOT=0 -DESP32_4M", + "f_cpu": "240000000L", + "f_flash": "80000000L", + "flash_mode": "dout", + "mcu": "esp32", + "variant": "esp32", + "partitions": "esp32_partition_app1810k_spiffs316k.csv" + }, + "connectivity": [ + "wifi", + "bluetooth", + "ethernet", + "can" + ], + "debug": { + "openocd_target": "esp32.cfg" + }, + "frameworks": [ + "arduino", + "espidf" + ], + "name": "AI Thinker ESP32-CAM, 4M Flash 4MB PSRAM, ESPEasy 1810k Code/OTA, 316k FS", + "upload": { + "flash_size": "4MB", + "maximum_ram_size": 327680, + "maximum_size": 1900544, + "require_upload_port": true, + "speed": 460800 + }, + "url": "https://wiki.ai-thinker.com/esp32-cam", + "vendor": "AI Thinker" +} diff --git a/boards/esp32-m5core2.json b/boards/esp32-m5core2.json new file mode 100644 index 0000000000..03ccbb52de --- /dev/null +++ b/boards/esp32-m5core2.json @@ -0,0 +1,38 @@ +{ + "build": { + "arduino":{ + "ldscript": "esp32_out.ld" + }, + "core": "esp32", + "extra_flags": "-DARDUINO_M5STACK_Core2 -DBOARD_HAS_PSRAM -DARDUINO_USB_CDC_ON_BOOT=0 -DESP32_16M", + "f_cpu": "240000000L", + "f_flash": "80000000L", + "flash_mode": "dout", + "mcu": "esp32", + "variant": "m5stack_core2", + "partitions": "esp32_partition_app4096k_spiffs8124k.csv" + }, + "connectivity": [ + "wifi", + "bluetooth", + "ethernet", + "can" + ], + "debug": { + "openocd_target": "esp32.cfg" + }, + "frameworks": [ + "arduino", + "espidf" + ], + "name": "M5Stack Core2 16M Flash, 4MB PSRAM, ESPEasy 4M Code/OTA, 8M FS", + "upload": { + "flash_size": "16MB", + "maximum_ram_size": 327680, + "maximum_size": 16777216, + "require_upload_port": true, + "speed": 2000000 + }, + "url": "http://www.m5stack.com", + "vendor": "M5Stack" +} diff --git a/boards/esp32-odroid.json b/boards/esp32-odroid.json new file mode 100644 index 0000000000..91236379aa --- /dev/null +++ b/boards/esp32-odroid.json @@ -0,0 +1,38 @@ +{ + "build": { + "arduino":{ + "ldscript": "esp32_out.ld" + }, + "core": "esp32", + "extra_flags": "-DARDUINO_ODROID_ESP32 -DBOARD_HAS_PSRAM -DHAS_PSRAM_FIX -mfix-esp32-psram-cache-issue -mfix-esp32-psram-cache-strategy=memw -DARDUINO_USB_CDC_ON_BOOT=0 -DESP32_16M", + "f_cpu": "240000000L", + "f_flash": "80000000L", + "flash_mode": "dout", + "mcu": "esp32", + "variant": "odroid_esp32", + "partitions": "esp32_partition_app4096k_spiffs8124k.csv" + }, + "connectivity": [ + "wifi", + "bluetooth", + "ethernet", + "can" + ], + "debug": { + "openocd_target": "esp32.cfg" + }, + "frameworks": [ + "arduino", + "espidf" + ], + "name": "ESP32 ODROID-GO 16M Flash, 4MB PSRAM, ESPEasy 4M Code/OTA, 8M FS", + "upload": { + "flash_size": "16MB", + "maximum_ram_size": 327680, + "maximum_size": 16777216, + "require_upload_port": true, + "speed": 2000000 + }, + "url": "https://www.hardkernel.com/main/products/prdt_info.php?g_code=G152875062626", + "vendor": "Hardkernel" +} diff --git a/boards/esp32_16M.json b/boards/esp32_16M.json index bd61eb4e9b..745a513249 100644 --- a/boards/esp32_16M.json +++ b/boards/esp32_16M.json @@ -1,38 +1,38 @@ { - "build": { - "arduino":{ - "ldscript": "esp32_out.ld" - }, - "core": "esp32", - "extra_flags": "-DARDUINO_ESP32_DEV -DBOARD_HAS_PSRAM -DARDUINO_USB_CDC_ON_BOOT=0 -DESP32_16M", - "f_cpu": "240000000L", - "f_flash": "40000000L", - "flash_mode": "dout", - "mcu": "esp32", - "variant": "esp32", - "partitions": "esp32_partition_app4096k_spiffs8124k.csv" + "build": { + "arduino":{ + "ldscript": "esp32_out.ld" }, - "connectivity": [ - "wifi", - "bluetooth", - "ethernet", - "can" - ], - "debug": { - "openocd_target": "esp32.cfg" - }, - "frameworks": [ - "arduino", - "espidf" - ], - "name": "Espressif Generic ESP32 16M Flash, ESPEasy 4096 Code/OTA, 8M FS", - "upload": { - "flash_size": "16MB", - "maximum_ram_size": 327680, - "maximum_size": 16777216, - "require_upload_port": true, - "speed": 460800 - }, - "url": "https://en.wikipedia.org/wiki/ESP32", - "vendor": "Espressif" - } \ No newline at end of file + "core": "esp32", + "extra_flags": "-DARDUINO_ESP32_DEV -DBOARD_HAS_PSRAM -DARDUINO_USB_CDC_ON_BOOT=0 -DESP32_16M", + "f_cpu": "80000000L", + "f_flash": "40000000L", + "flash_mode": "dout", + "mcu": "esp32", + "variant": "esp32", + "partitions": "esp32_partition_app4096k_spiffs8124k.csv" + }, + "connectivity": [ + "wifi", + "bluetooth", + "ethernet", + "can" + ], + "debug": { + "openocd_target": "esp32.cfg" + }, + "frameworks": [ + "arduino", + "espidf" + ], + "name": "Espressif Generic ESP32 16M Flash, ESPEasy 4M Code/OTA, 8M FS", + "upload": { + "flash_size": "16MB", + "maximum_ram_size": 327680, + "maximum_size": 4194304, + "require_upload_port": true, + "speed": 460800 + }, + "url": "https://en.wikipedia.org/wiki/ESP32", + "vendor": "Espressif" +} diff --git a/boards/esp32_4M.json b/boards/esp32_4M.json new file mode 100644 index 0000000000..8448cd05a1 --- /dev/null +++ b/boards/esp32_4M.json @@ -0,0 +1,38 @@ +{ + "build": { + "arduino":{ + "ldscript": "esp32_out.ld" + }, + "core": "esp32", + "extra_flags": "-DARDUINO_ESP32_DEV -DBOARD_HAS_PSRAM -DARDUINO_USB_CDC_ON_BOOT=0 -DESP32_4M", + "f_cpu": "80000000L", + "f_flash": "40000000L", + "flash_mode": "dout", + "mcu": "esp32", + "variant": "esp32", + "partitions": "esp32_partition_app1810k_spiffs316k.csv" + }, + "connectivity": [ + "wifi", + "bluetooth", + "ethernet", + "can" + ], + "debug": { + "openocd_target": "esp32.cfg" + }, + "frameworks": [ + "arduino", + "espidf" + ], + "name": "Espressif Generic ESP32 4M Flash, ESPEasy 1810k Code/OTA, 316k FS", + "upload": { + "flash_size": "4MB", + "maximum_ram_size": 327680, + "maximum_size": 1900544, + "require_upload_port": true, + "speed": 460800 + }, + "url": "https://en.wikipedia.org/wiki/ESP32", + "vendor": "Espressif" +} diff --git a/boards/esp32_8M.json b/boards/esp32_8M.json new file mode 100644 index 0000000000..4f17b22946 --- /dev/null +++ b/boards/esp32_8M.json @@ -0,0 +1,38 @@ +{ + "build": { + "arduino":{ + "ldscript": "esp32_out.ld" + }, + "core": "esp32", + "extra_flags": "-DARDUINO_ESP32_DEV -DBOARD_HAS_PSRAM -DARDUINO_USB_CDC_ON_BOOT=0 -DESP32_8M", + "f_cpu": "80000000L", + "f_flash": "40000000L", + "flash_mode": "dout", + "mcu": "esp32", + "variant": "esp32", + "partitions": "esp32_partition_app2944k_spiffs2M.csv" + }, + "connectivity": [ + "wifi", + "bluetooth", + "ethernet", + "can" + ], + "debug": { + "openocd_target": "esp32.cfg" + }, + "frameworks": [ + "arduino", + "espidf" + ], + "name": "Espressif Generic ESP32 8M Flash, ESPEasy 2944k Code/OTA, 2112k FS", + "upload": { + "flash_size": "8MB", + "maximum_ram_size": 327680, + "maximum_size": 8388608, + "require_upload_port": true, + "speed": 460800 + }, + "url": "https://en.wikipedia.org/wiki/ESP32", + "vendor": "Espressif" +} diff --git a/boards/esp32_solo1_4M.json b/boards/esp32_solo1_4M.json new file mode 100644 index 0000000000..0137459fea --- /dev/null +++ b/boards/esp32_solo1_4M.json @@ -0,0 +1,38 @@ +{ + "build": { + "arduino":{ + "ldscript": "esp32_out.ld" + }, + "core": "esp32", + "extra_flags": "-DARDUINO_ESP32_DEV -DARDUINO_USB_CDC_ON_BOOT=0 -DESP32_4M -DCORE32SOLO1", + "f_cpu": "80000000L", + "f_flash": "40000000L", + "flash_mode": "dout", + "mcu": "esp32", + "variant": "esp32", + "partitions": "esp32_partition_app1810k_spiffs316k.csv" + }, + "connectivity": [ + "wifi", + "bluetooth", + "ethernet", + "can" + ], + "debug": { + "openocd_target": "esp32-solo-1.cfg" + }, + "frameworks": [ + "arduino", + "espidf" + ], + "name": "Espressif Generic ESP32 4M Flash, ESPEasy 1810k Code/OTA, 316k FS", + "upload": { + "flash_size": "4MB", + "maximum_ram_size": 327680, + "maximum_size": 1900544, + "require_upload_port": true, + "speed": 460800 + }, + "url": "https://en.wikipedia.org/wiki/ESP32", + "vendor": "Espressif" +} diff --git a/boards/esp32c3.json b/boards/esp32c3.json new file mode 100644 index 0000000000..ebac09c3e3 --- /dev/null +++ b/boards/esp32c3.json @@ -0,0 +1,35 @@ +{ + "build": { + "arduino":{ + "ldscript": "esp32c3_out.ld" + }, + "core": "esp32", + "extra_flags": "-DARDUINO_USB_CDC_ON_BOOT=0 -DESP32_4M -DESP32C3", + "f_cpu": "160000000L", + "f_flash": "80000000L", + "flash_mode": "dout", + "mcu": "esp32c3", + "variant": "esp32c3", + "partitions": "esp32_partition_app1810k_spiffs316k.csv" + }, + "connectivity": [ + "wifi" + ], + "debug": { + "openocd_target": "esp32c3.cfg" + }, + "frameworks": [ + "arduino", + "espidf" + ], + "name": "Espressif Generic ESP32-C3 4M Flash, ESPEasy 1810k Code/OTA, 316k FS", + "upload": { + "flash_size": "4MB", + "maximum_ram_size": 327680, + "maximum_size": 1900544, + "require_upload_port": true, + "speed": 460800 + }, + "url": "https://docs.espressif.com/projects/esp-idf/en/latest/esp32c3/hw-reference/esp32c3/user-guide-devkitm-1.html", + "vendor": "Espressif" +} diff --git a/boards/esp32s2.json b/boards/esp32s2.json index 7aff095fce..f80f87a567 100644 --- a/boards/esp32s2.json +++ b/boards/esp32s2.json @@ -1,31 +1,35 @@ { - "build": { - "arduino":{ - "ldscript": "esp32s2_out.ld" - }, - "core": "esp32", - "mcu": "esp32s2", - "extra_flags": "-Desp32S2_dev_module", - "f_cpu": "240000000L", - "f_flash": "80000000L", - "flash_mode": "qio", - "mcu": "esp32s2", - "variant": "esp32s2" + "build": { + "arduino":{ + "ldscript": "esp32s2_out.ld" }, - "connectivity": [ - "wifi" - ], - "frameworks": [ - "arduino" - ], - "name": "ESP32S2 Dev Module", - "upload": { - "flash_size": "4MB", - "maximum_ram_size": 327680, - "maximum_size": 4194304, - "require_upload_port": true, - "speed": 460800 - }, - "url": "https://espressif.com", - "vendor": "espressif" - } \ No newline at end of file + "core": "esp32", + "extra_flags": "-DBOARD_HAS_PSRAM -DESP32_4M -DESP32S2", + "f_cpu": "240000000L", + "f_flash": "80000000L", + "flash_mode": "dout", + "mcu": "esp32s2", + "variant": "esp32s2", + "partitions": "esp32_partition_app1810k_spiffs316k.csv" + }, + "connectivity": [ + "wifi" + ], + "debug": { + "openocd_target": "esp32s2.cfg" + }, + "frameworks": [ + "espidf", + "arduino" + ], + "name": "Espressif Generic ESP32-S2 4M Flash, ESPEasy 1810k Code/OTA, 316k FS", + "upload": { + "flash_size": "4MB", + "maximum_ram_size": 327680, + "maximum_size": 1900544, + "require_upload_port": true, + "speed": 460800 + }, + "url": "https://docs.espressif.com/projects/esp-idf/en/latest/esp32s2/hw-reference/esp32s2/user-guide-saola-1-v1.2.html", + "vendor": "Espressif" +} diff --git a/boards/esp8266_16M14M_board.json b/boards/esp8266_16M14M_board.json new file mode 100644 index 0000000000..a620a464b8 --- /dev/null +++ b/boards/esp8266_16M14M_board.json @@ -0,0 +1,32 @@ +{ + "build": { + "arduino": { + "ldscript": "eagle.flash.16m14m.ld" + }, + "core": "esp8266", + "extra_flags": "-DESP8266 -DARDUINO_ARCH_ESP8266 -DARDUINO_ESP8266_ESP01 -DESP8266_4M -DESP8266_4M3M", + "f_cpu": "80000000L", + "f_flash": "40000000L", + "flash_mode": "dout", + "mcu": "esp8266", + "variant": "generic" + }, + "connectivity": [ + "wifi" + ], + "frameworks": [ + "arduino", + "esp8266-rtos-sdk", + "esp8266-nonos-sdk" + ], + "name": "Espressif Generic ESP8266 ESPEasy 16M Flash 14M FS", + "upload": { + "maximum_ram_size": 81920, + "maximum_size": 1040316, + "require_upload_port": true, + "resetmethod": "ck", + "speed": 115200 + }, + "url": "http://www.esp8266.com/wiki/doku.php?id=esp8266-module-family", + "vendor": "Espressif" +} diff --git a/boards/esp8266_1M128k.json b/boards/esp8266_1M128k.json new file mode 100644 index 0000000000..67ac758c84 --- /dev/null +++ b/boards/esp8266_1M128k.json @@ -0,0 +1,32 @@ +{ + "build": { + "arduino": { + "ldscript": "eagle.flash.1m128.ld" + }, + "core": "esp8266", + "extra_flags": "-DESP8266 -DARDUINO_ARCH_ESP8266 -DARDUINO_ESP8266_ESP01 -DESP8266_1M", + "f_cpu": "80000000L", + "f_flash": "40000000L", + "flash_mode": "dout", + "mcu": "esp8266", + "variant": "generic" + }, + "connectivity": [ + "wifi" + ], + "frameworks": [ + "arduino", + "esp8266-rtos-sdk", + "esp8266-nonos-sdk" + ], + "name": "Espressif Generic ESP8266 ESPEasy 1M Flash 128k FS", + "upload": { + "maximum_ram_size": 81920, + "maximum_size": 892912, + "require_upload_port": true, + "resetmethod": "ck", + "speed": 115200 + }, + "url": "http://www.esp8266.com/wiki/doku.php?id=esp8266-module-family", + "vendor": "Espressif" +} diff --git a/boards/esp8266_1M128k_OTA.json b/boards/esp8266_1M128k_OTA.json new file mode 100644 index 0000000000..217d424733 --- /dev/null +++ b/boards/esp8266_1M128k_OTA.json @@ -0,0 +1,32 @@ +{ + "build": { + "arduino": { + "ldscript": "eagle.flash.1m128.ld" + }, + "core": "esp8266", + "extra_flags": "-DESP8266 -DARDUINO_ARCH_ESP8266 -DARDUINO_ESP8266_ESP01 -DESP8266_1M", + "f_cpu": "80000000L", + "f_flash": "40000000L", + "flash_mode": "dout", + "mcu": "esp8266", + "variant": "generic" + }, + "connectivity": [ + "wifi" + ], + "frameworks": [ + "arduino", + "esp8266-rtos-sdk", + "esp8266-nonos-sdk" + ], + "name": "Espressif Generic ESP8266 ESPEasy 1M Flash 128k FS", + "upload": { + "maximum_ram_size": 81920, + "maximum_size": 616448, + "require_upload_port": true, + "resetmethod": "ck", + "speed": 115200 + }, + "url": "http://www.esp8266.com/wiki/doku.php?id=esp8266-module-family", + "vendor": "Espressif" +} diff --git a/boards/esp8266_2M1M.json b/boards/esp8266_2M1M.json new file mode 100644 index 0000000000..942bab84f6 --- /dev/null +++ b/boards/esp8266_2M1M.json @@ -0,0 +1,32 @@ +{ + "build": { + "arduino": { + "ldscript": "eagle.flash.2m1m.ld" + }, + "core": "esp8266", + "extra_flags": "-DESP8266 -DARDUINO_ARCH_ESP8266 -DARDUINO_ESP8266_ESP01 -DESP8266_2M -DESP8266_2M1M", + "f_cpu": "80000000L", + "f_flash": "40000000L", + "flash_mode": "dout", + "mcu": "esp8266", + "variant": "generic" + }, + "connectivity": [ + "wifi" + ], + "frameworks": [ + "arduino", + "esp8266-rtos-sdk", + "esp8266-nonos-sdk" + ], + "name": "Espressif Generic ESP8266 ESPEasy 1M sketch 1M FS", + "upload": { + "maximum_ram_size": 81920, + "maximum_size": 1040316, + "require_upload_port": true, + "resetmethod": "ck", + "speed": 115200 + }, + "url": "http://www.esp8266.com/wiki/doku.php?id=esp8266-module-family", + "vendor": "Espressif" +} diff --git a/boards/esp8266_2M256.json b/boards/esp8266_2M256.json new file mode 100644 index 0000000000..88ddd31092 --- /dev/null +++ b/boards/esp8266_2M256.json @@ -0,0 +1,32 @@ +{ + "build": { + "arduino": { + "ldscript": "eagle.flash.2m256.ld" + }, + "core": "esp8266", + "extra_flags": "-DESP8266 -DARDUINO_ARCH_ESP8266 -DARDUINO_ESP8266_ESP01 -DESP8266_2M -DESP8266_2M256", + "f_cpu": "80000000L", + "f_flash": "40000000L", + "flash_mode": "dout", + "mcu": "esp8266", + "variant": "generic" + }, + "connectivity": [ + "wifi" + ], + "frameworks": [ + "arduino", + "esp8266-rtos-sdk", + "esp8266-nonos-sdk" + ], + "name": "Espressif Generic ESP8266 ESPEasy 2M Flash 256k FS", + "upload": { + "maximum_ram_size": 81920, + "maximum_size": 1040316, + "require_upload_port": true, + "resetmethod": "ck", + "speed": 115200 + }, + "url": "http://www.esp8266.com/wiki/doku.php?id=esp8266-module-family", + "vendor": "Espressif" +} diff --git a/boards/esp8266_4M1M_board.json b/boards/esp8266_4M1M_board.json new file mode 100644 index 0000000000..6e8d75fd76 --- /dev/null +++ b/boards/esp8266_4M1M_board.json @@ -0,0 +1,32 @@ +{ + "build": { + "arduino": { + "ldscript": "eagle.flash.4m1m.ld" + }, + "core": "esp8266", + "extra_flags": "-DESP8266 -DARDUINO_ARCH_ESP8266 -DARDUINO_ESP8266_ESP01 -DESP8266_4M -DESP8266_4M3M", + "f_cpu": "80000000L", + "f_flash": "40000000L", + "flash_mode": "dout", + "mcu": "esp8266", + "variant": "generic" + }, + "connectivity": [ + "wifi" + ], + "frameworks": [ + "arduino", + "esp8266-rtos-sdk", + "esp8266-nonos-sdk" + ], + "name": "Espressif Generic ESP8266 ESPEasy 4M Flash 1M FS", + "upload": { + "maximum_ram_size": 81920, + "maximum_size": 1040316, + "require_upload_port": true, + "resetmethod": "ck", + "speed": 115200 + }, + "url": "http://www.esp8266.com/wiki/doku.php?id=esp8266-module-family", + "vendor": "Espressif" +} diff --git a/boards/esp8266_4M2M_board.json b/boards/esp8266_4M2M_board.json new file mode 100644 index 0000000000..2025df330b --- /dev/null +++ b/boards/esp8266_4M2M_board.json @@ -0,0 +1,32 @@ +{ + "build": { + "arduino": { + "ldscript": "eagle.flash.4m2m.ld" + }, + "core": "esp8266", + "extra_flags": "-DESP8266 -DARDUINO_ARCH_ESP8266 -DARDUINO_ESP8266_ESP01 -DESP8266_4M -DESP8266_4M2M", + "f_cpu": "80000000L", + "f_flash": "40000000L", + "flash_mode": "dout", + "mcu": "esp8266", + "variant": "generic" + }, + "connectivity": [ + "wifi" + ], + "frameworks": [ + "arduino", + "esp8266-rtos-sdk", + "esp8266-nonos-sdk" + ], + "name": "Espressif Generic ESP8266 ESPEasy 4M Flash 2M FS", + "upload": { + "maximum_ram_size": 81920, + "maximum_size": 1040316, + "require_upload_port": true, + "resetmethod": "ck", + "speed": 115200 + }, + "url": "http://www.esp8266.com/wiki/doku.php?id=esp8266-module-family", + "vendor": "Espressif" +} diff --git a/boards/esp8266_4M3M.json b/boards/esp8266_4M3M.json new file mode 100644 index 0000000000..d036e0527b --- /dev/null +++ b/boards/esp8266_4M3M.json @@ -0,0 +1,32 @@ +{ + "build": { + "arduino": { + "ldscript": "eagle.flash.4m3m.ld" + }, + "core": "esp8266", + "extra_flags": "-DESP8266 -DARDUINO_ARCH_ESP8266 -DARDUINO_ESP8266_ESP01 -DESP8266_4M -DESP8266_4M3M", + "f_cpu": "80000000L", + "f_flash": "40000000L", + "flash_mode": "dout", + "mcu": "esp8266", + "variant": "generic" + }, + "connectivity": [ + "wifi" + ], + "frameworks": [ + "arduino", + "esp8266-rtos-sdk", + "esp8266-nonos-sdk" + ], + "name": "Espressif Generic ESP8266 ESPEasy 4M Flash 3M FS", + "upload": { + "maximum_ram_size": 81920, + "maximum_size": 1040316, + "require_upload_port": true, + "resetmethod": "ck", + "speed": 115200 + }, + "url": "http://www.esp8266.com/wiki/doku.php?id=esp8266-module-family", + "vendor": "Espressif" +} diff --git a/boards/esp8266_zbbridge.json b/boards/esp8266_zbbridge.json new file mode 100644 index 0000000000..e02039015f --- /dev/null +++ b/boards/esp8266_zbbridge.json @@ -0,0 +1,32 @@ +{ + "build": { + "arduino": { + "ldscript": "eagle.flash.2m256.ld" + }, + "core": "esp8266", + "extra_flags": "-DESP8266 -DARDUINO_ARCH_ESP8266 -DARDUINO_ESP8266_ESP01 -DESP8266_2M -DESP8266_2M256", + "f_cpu": "160000000L", + "f_flash": "40000000L", + "flash_mode": "dout", + "mcu": "esp8266", + "variant": "generic" + }, + "connectivity": [ + "wifi" + ], + "frameworks": [ + "arduino", + "esp8266-rtos-sdk", + "esp8266-nonos-sdk" + ], + "name": "Sonoff ZbBridge ESPEasy 2M Flash 256k FS", + "upload": { + "maximum_ram_size": 81920, + "maximum_size": 1040316, + "require_upload_port": true, + "resetmethod": "ck", + "speed": 115200 + }, + "url": "https://templates.blakadder.com/sonoff_ZBBridge.html", + "vendor": "Espressif" +} diff --git a/boards/esp8285_1M128k.json b/boards/esp8285_1M128k.json new file mode 100644 index 0000000000..476ad73f27 --- /dev/null +++ b/boards/esp8285_1M128k.json @@ -0,0 +1,32 @@ +{ + "build": { + "arduino": { + "ldscript": "eagle.flash.1m128.ld" + }, + "core": "esp8266", + "extra_flags": "-DESP8266 -DESP8285 -DARDUINO_ARCH_ESP8266 -DARDUINO_ESP8266_ESP01 -DESP8266_1M", + "f_cpu": "80000000L", + "f_flash": "40000000L", + "flash_mode": "dout", + "mcu": "esp8266", + "variant": "generic" + }, + "connectivity": [ + "wifi" + ], + "frameworks": [ + "arduino", + "esp8266-rtos-sdk", + "esp8266-nonos-sdk" + ], + "name": "Espressif Generic ESP8285 ESPEasy 1M Flash 128k FS", + "upload": { + "maximum_ram_size": 81920, + "maximum_size": 892912, + "require_upload_port": true, + "resetmethod": "ck", + "speed": 115200 + }, + "url": "http://www.esp8266.com/wiki/doku.php?id=esp8266-module-family", + "vendor": "Espressif" +} diff --git a/boards/esp8285_1M128k_OTA.json b/boards/esp8285_1M128k_OTA.json new file mode 100644 index 0000000000..cc1ed3f674 --- /dev/null +++ b/boards/esp8285_1M128k_OTA.json @@ -0,0 +1,32 @@ +{ + "build": { + "arduino": { + "ldscript": "eagle.flash.1m128.ld" + }, + "core": "esp8266", + "extra_flags": "-DESP8266 -DESP8285 -DARDUINO_ARCH_ESP8266 -DARDUINO_ESP8266_ESP01 -DESP8266_1M", + "f_cpu": "80000000L", + "f_flash": "40000000L", + "flash_mode": "dout", + "mcu": "esp8266", + "variant": "generic" + }, + "connectivity": [ + "wifi" + ], + "frameworks": [ + "arduino", + "esp8266-rtos-sdk", + "esp8266-nonos-sdk" + ], + "name": "Espressif Generic ESP8285 ESPEasy 1M Flash 128k FS", + "upload": { + "maximum_ram_size": 81920, + "maximum_size": 616448, + "require_upload_port": true, + "resetmethod": "ck", + "speed": 115200 + }, + "url": "http://www.esp8266.com/wiki/doku.php?id=esp8266-module-family", + "vendor": "Espressif" +} diff --git a/esp32_partition_app1572k_spiffs983k.csv b/esp32_partition_app1572k_spiffs983k.csv new file mode 100644 index 0000000000..3a317ea7bb --- /dev/null +++ b/esp32_partition_app1572k_spiffs983k.csv @@ -0,0 +1,6 @@ +# Name, Type, SubType, Offset, Size, Flags +nvs, data, nvs, 0x9000, 0x5000, +otadata, data, ota, 0xe000, 0x2000, +app0, app, ota_0, 0x10000, 0x180000, +app1, app, ota_1, 0x190000, 0x180000, +spiffs, data, spiffs, 0x310000,0x0F0000, diff --git a/esp32_partition_app1856k_spiffs320k.csv b/esp32_partition_app1856k_spiffs320k.csv new file mode 100644 index 0000000000..09659603f9 --- /dev/null +++ b/esp32_partition_app1856k_spiffs320k.csv @@ -0,0 +1,6 @@ +# Name, Type, SubType, Offset, Size, Flags +nvs, data, nvs, 0x9000, 0x5000, +otadata, data, ota, 0xe000, 0x2000, +app0, app, ota_0, 0x10000, 0x1D0000, +app1, app, ota_1, 0x1E0000, 0x1D0000, +spiffs, data, spiffs, 0x3B0000,0x50000, diff --git a/esp32_partition_app2944k_spiffs10M.csv b/esp32_partition_app2944k_spiffs10M.csv new file mode 100644 index 0000000000..f87b84a9c2 --- /dev/null +++ b/esp32_partition_app2944k_spiffs10M.csv @@ -0,0 +1,6 @@ +# Name, Type, SubType, Offset, Size, Flags +nvs, data, nvs, 0x9000, 0x5000, +otadata, data, ota, 0xe000, 0x2000, +app0, app, ota_0, 0x10000, 0x2F0000, +app1, app, ota_1, 0x300000, 0x2F0000, +spiffs, data, spiffs, 0x5F0000,0xA10000, diff --git a/esp32_partition_app2944k_spiffs2M.csv b/esp32_partition_app2944k_spiffs2M.csv new file mode 100644 index 0000000000..9c0897d514 --- /dev/null +++ b/esp32_partition_app2944k_spiffs2M.csv @@ -0,0 +1,6 @@ +# Name, Type, SubType, Offset, Size, Flags +nvs, data, nvs, 0x9000, 0x5000, +otadata, data, ota, 0xe000, 0x2000, +app0, app, ota_0, 0x10000, 0x2F0000, +app1, app, ota_1, 0x300000, 0x2F0000, +spiffs, data, spiffs, 0x5F0000,0x210000, diff --git a/platformio.ini b/platformio.ini index 03dc492f77..847b6c033c 100644 --- a/platformio.ini +++ b/platformio.ini @@ -20,7 +20,8 @@ extra_configs = platformio_esp32_envs.ini platformio_special_envs.ini -default_envs = custom_ESP8266_4M1M, custom_ESP32_4M316k +;default_envs = normal_ESP32_4M +default_envs = normal_ESP32_16M8M_LittleFS ; default_envs = custom_ESP8266_4M1M ;default_envs = normal_ESP8266_4M1M diff --git a/platformio_esp32_envs.ini b/platformio_esp32_envs.ini index 9050860d1a..8339cfec6f 100644 --- a/platformio_esp32_envs.ini +++ b/platformio_esp32_envs.ini @@ -12,420 +12,50 @@ lib_ignore = ESP8266WiFi, ESP8266Ping, ESP8266WebServer, ESP8266H extends = common, core_esp32_IDF4_4__2_0_2 lib_deps = td-er/ESPeasySerial @ 2.0.8, adafruit/Adafruit ILI9341 @ ^1.5.6, Adafruit GFX Library, LOLIN_EPD, Adafruit BusIO, VL53L0X @ 1.3.0, SparkFun VL53L1X 4m Laser Distance Sensor @ 1.2.9, td-er/SparkFun MAX1704x Fuel Gauge Arduino Library @ ^1.0.1, ArduinoOTA, ESP32HTTPUpdateServer, FrogmoreScd30, Multi Channel Relay Arduino Library lib_ignore = ${esp32_always.lib_ignore}, ESP32_ping, IRremoteESP8266, HeatpumpIR, ESP32 BLE Arduino -board_build.flash_mode = dout -board_upload.maximum_size = 1900544 -board_build.partitions = esp32_partition_app1810k_spiffs316k.csv extra_scripts = post:tools/pio/post_esp32.py ${extra_scripts_default.extra_scripts} build_unflags = -Wall -build_flags = ${core_esp32_IDF4_4__2_0_2.build_flags} - ${mqtt_flags.build_flags} +build_flags = ${core_esp32_IDF4_4__2_0_2.build_flags} + ${mqtt_flags.build_flags} -DCONFIG_FREERTOS_ASSERT_DISABLE -DCONFIG_LWIP_ESP_GRATUITOUS_ARP -fno-strict-aliasing -DCORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_NONE -board = esp32dev monitor_filters = esp32_exception_decoder -platform_packages = - -[esp32s2_common] -extends = esp32_common -build_flags = ${esp32_common.build_flags} - -DESP32S2 - -DBOARD_HAS_PSRAM - -DESP32_ENABLE_PSRAM -board = esp32-s2-saola-1 - - - -[ESP32-wrover-kit] -extends = esp32_common -board = esp-wrover-kit -upload_protocol = ftdi -debug_tool = ftdi -;debug_extra_cmds = break Misc.ino:3011 - - -[esp32_LittleFS] +[esp32_common_LittleFS] extends = esp32_common -;platform_packages = ${esp32_common.platform_packages} -; platformio/tool-mklittlefs @ ~1.203.200522 -;lib_deps = ${esp32_common.lib_deps}, https://github.com/lorol/LITTLEFS.git build_flags = ${esp32_common.build_flags} -DUSE_LITTLEFS +lib_deps = ${esp32_common.lib_deps}, LittleFS board_build.filesystem = littlefs -[esp32s2_LittleFS] -extends = esp32s2_common -;platform_packages = ${esp32s2_common.platform_packages} -; platformio/tool-mklittlefs @ ~1.203.200522 -;lib_deps = ${esp32_common.lib_deps}, https://github.com/lorol/LITTLEFS.git -build_flags = ${esp32s2_common.build_flags} - -DUSE_LITTLEFS -board_build.filesystem = littlefs - - - -; Custom: 4096k version -------------------------- -[env:custom_ESP32_4M316k] -extends = esp32_common -build_flags = ${esp32_common.build_flags} -DPLUGIN_BUILD_CUSTOM -extra_scripts = ${esp32_common.extra_scripts} - pre:tools/pio/pre_custom_esp32.py - -[env:custom_ESP32_4M316k_LittleFS] -extends = esp32_LittleFS -build_flags = ${esp32_LittleFS.build_flags} -DPLUGIN_BUILD_CUSTOM -extra_scripts = ${esp32_LittleFS.extra_scripts} - pre:tools/pio/pre_custom_esp32.py - -[env:custom_IR_ESP32_4M316k] -extends = esp32_common -build_flags = ${esp32_common.build_flags} -DPLUGIN_BUILD_CUSTOM -DPLUGIN_BUILD_IR -lib_ignore = ${esp32_always.lib_ignore}, ESP32_ping, HeatpumpIR -extra_scripts = ${esp32_common.extra_scripts} - pre:tools/pio/pre_custom_esp32.py - - -; ESP32-S2 -[env:custom_ESP32s2_4M316k] -extends = esp32s2_common -build_flags = ${esp32s2_common.build_flags} - -DPLUGIN_BUILD_CUSTOM -extra_scripts = ${esp32s2_common.extra_scripts} - pre:tools/pio/pre_custom_esp32.py - -[env:custom_IR_ESP32s2_4M316k] -extends = esp32s2_common -build_flags = ${esp32s2_common.build_flags} -DPLUGIN_BUILD_CUSTOM -DPLUGIN_BUILD_IR -lib_ignore = ${esp32_always.lib_ignore}, ESP32_ping, HeatpumpIR -extra_scripts = ${esp32s2_common.extra_scripts} - pre:tools/pio/pre_custom_esp32.py - -[env:normal_ESP32_4M316k] -extends = esp32_common -lib_deps = ${esp32_common.lib_deps}, ServoESP32 -build_flags = ${esp32_common.build_flags} - -DFEATURE_ARDUINO_OTA - -[env:test_A_ESP32_4M316k] -extends = esp32_common -platform = ${esp32_common.platform} -build_flags = ${esp32_common.build_flags} - -DFEATURE_ARDUINO_OTA - -DPLUGIN_SET_TEST_ESP32 - -DTESTING_USE_RTTTL - -[env:test_B_ESP32_4M316k] -extends = esp32_common -build_flags = ${esp32_common.build_flags} - -DFEATURE_ARDUINO_OTA - -DPLUGIN_SET_TEST_B_ESP32 - -DTESTING_USE_RTTTL - -[env:test_C_ESP32_4M316k] -extends = esp32_common -build_flags = ${esp32_common.build_flags} - -DFEATURE_ARDUINO_OTA - -DPLUGIN_SET_TEST_C_ESP32 - -DTESTING_USE_RTTTL - -[env:test_D_ESP32_4M316k] -extends = esp32_common -build_flags = ${esp32_common.build_flags} - -DFEATURE_ARDUINO_OTA - -DPLUGIN_SET_TEST_D_ESP32 - -DTESTING_USE_RTTTL - - -[env:test_A_ESP32-wrover-kit_4M316k] -extends = ESP32-wrover-kit -build_flags = ${ESP32-wrover-kit.build_flags} - -DFEATURE_ARDUINO_OTA - -DPLUGIN_SET_TEST_ESP32 - -DTESTING_USE_RTTTL - - -[env:test_B_ESP32-wrover-kit_4M316k] -extends = ESP32-wrover-kit -build_flags = ${ESP32-wrover-kit.build_flags} - -DFEATURE_ARDUINO_OTA - -DPLUGIN_SET_TEST_B_ESP32 - -DTESTING_USE_RTTTL - - -[env:test_C_ESP32-wrover-kit_4M316k] -extends = ESP32-wrover-kit -build_flags = ${ESP32-wrover-kit.build_flags} - -DFEATURE_ARDUINO_OTA - -DPLUGIN_SET_TEST_C_ESP32 - -DTESTING_USE_RTTTL - - -[env:test_D_ESP32-wrover-kit_4M316k] -extends = ESP32-wrover-kit -build_flags = ${ESP32-wrover-kit.build_flags} - -DFEATURE_ARDUINO_OTA - -DPLUGIN_SET_TEST_D_ESP32 - -DTESTING_USE_RTTTL - - -[env:test_A_ESP32_4M316k_lolin_d32_pro] -extends = esp32_common -build_flags = ${esp32_common.build_flags} -; -DFEATURE_ARDUINO_OTA - -DPLUGIN_SET_TEST_ESP32 -; -DUSES_P096 - -DTESTING_USE_RTTTL -board = lolin_d32_pro - - -[env:test_B_ESP32_4M316k_lolin_d32_pro] +[esp32s2_common_LittleFS] extends = esp32_common -build_flags = ${esp32_common.build_flags} -; -DFEATURE_ARDUINO_OTA - -DPLUGIN_SET_TEST_B_ESP32 - -DUSES_P096 - -DTESTING_USE_RTTTL -board = lolin_d32_pro - - -[env:test_C_ESP32_4M316k_lolin_d32_pro] -extends = esp32_common -build_flags = ${esp32_common.build_flags} -; -DFEATURE_ARDUINO_OTA - -DPLUGIN_SET_TEST_C_ESP32 -; -DUSES_P096 - -DTESTING_USE_RTTTL -board = lolin_d32_pro - - -[env:test_D_ESP32_4M316k_lolin_d32_pro] -extends = esp32_common -build_flags = ${esp32_common.build_flags} -; -DFEATURE_ARDUINO_OTA - -DPLUGIN_SET_TEST_D_ESP32 - -DUSES_P096 - -DTESTING_USE_RTTTL -board = lolin_d32_pro - - -[env:test_A_ESP32_IRExt_4M316k] -extends = env:test_A_ESP32_4M316k -lib_ignore = AS_BH1750, ${esp32_always.lib_ignore}, ESP32_ping -build_flags = ${esp32_common.build_flags} - -DPLUGIN_BUILD_NORMAL_IRext - -DTESTING_USE_RTTTL - -[env:test_B_ESP32_IRExt_4M316k] -extends = env:test_B_ESP32_4M316k -lib_ignore = AS_BH1750, ${esp32_always.lib_ignore}, ESP32_ping -build_flags = ${esp32_common.build_flags} - -DPLUGIN_BUILD_NORMAL_IRext - -DTESTING_USE_RTTTL - -[env:test_C_ESP32_IRExt_4M316k] -extends = env:test_C_ESP32_4M316k -lib_ignore = AS_BH1750, ${esp32_always.lib_ignore}, ESP32_ping -build_flags = ${esp32_common.build_flags} - -DPLUGIN_BUILD_NORMAL_IRext - -DTESTING_USE_RTTTL - -[env:test_D_ESP32_IRExt_4M316k] -extends = env:test_D_ESP32_4M316k -lib_ignore = AS_BH1750, ${esp32_always.lib_ignore}, ESP32_ping build_flags = ${esp32_common.build_flags} - -DPLUGIN_BUILD_NORMAL_IRext - -DTESTING_USE_RTTTL - -[env:energy_ESP32_4M316k] -extends = esp32_common -lib_deps = ${esp32_common.lib_deps}, ServoESP32 -build_flags = ${esp32_common.build_flags} - -DFEATURE_ARDUINO_OTA - -D PLUGIN_ENERGY_COLLECTION - -[env:display_ESP32_4M316k] -extends = esp32_common -lib_deps = ${esp32_common.lib_deps}, ServoESP32 -build_flags = ${esp32_common.build_flags} - -DFEATURE_ARDUINO_OTA - -D PLUGIN_DISPLAY_COLLECTION - - -; Custom: 4096k version -------------------------- -[env:custom_ESP32_4M316k_ETH] -extends = env:custom_ESP32_4M316k -platform = ${env:custom_ESP32_4M316k.platform} -build_flags = ${env:custom_ESP32_4M316k.build_flags} -DHAS_ETHERNET - -[env:normal_ESP32_4M316k_ETH] -extends = env:normal_ESP32_4M316k -platform = ${env:normal_ESP32_4M316k.platform} -build_flags = ${env:normal_ESP32_4M316k.build_flags} -DHAS_ETHERNET - -[env:test_A_ESP32_4M316k_ETH] -extends = env:test_A_ESP32_4M316k -platform = ${env:test_A_ESP32_4M316k.platform} -build_flags = ${env:test_A_ESP32_4M316k.build_flags} -DHAS_ETHERNET - -DTESTING_USE_RTTTL - -[env:test_B_ESP32_4M316k_ETH] -extends = env:test_B_ESP32_4M316k -platform = ${env:test_B_ESP32_4M316k.platform} -build_flags = ${env:test_B_ESP32_4M316k.build_flags} -DHAS_ETHERNET - -DTESTING_USE_RTTTL - -[env:test_C_ESP32_4M316k_ETH] -extends = env:test_C_ESP32_4M316k -platform = ${env:test_C_ESP32_4M316k.platform} -build_flags = ${env:test_C_ESP32_4M316k.build_flags} -DHAS_ETHERNET - -DTESTING_USE_RTTTL - -[env:test_D_ESP32_4M316k_ETH] -extends = env:test_D_ESP32_4M316k -platform = ${env:test_D_ESP32_4M316k.platform} -build_flags = ${env:test_D_ESP32_4M316k.build_flags} -DHAS_ETHERNET - -DTESTING_USE_RTTTL - - -;;; ESP32-s2 *********************************************************** - -[env:normal_ESP32s2_4M316k] -extends = esp32s2_common -lib_deps = ${esp32s2_common.lib_deps}, ServoESP32 -build_flags = ${esp32s2_common.build_flags} - -DFEATURE_ARDUINO_OTA - -[env:test_A_ESP32s2_4M316k] -extends = esp32s2_common -build_flags = ${esp32s2_common.build_flags} - -DFEATURE_ARDUINO_OTA - -DPLUGIN_SET_TEST_ESP32 - -DTESTING_USE_RTTTL - -[env:test_B_ESP32s2_4M316k] -extends = esp32s2_common -build_flags = ${esp32s2_common.build_flags} - -DFEATURE_ARDUINO_OTA - -DPLUGIN_SET_TEST_B_ESP32 - -DTESTING_USE_RTTTL - -[env:test_C_ESP32s2_4M316k] -extends = esp32s2_common -build_flags = ${esp32s2_common.build_flags} - -DFEATURE_ARDUINO_OTA - -DPLUGIN_SET_TEST_C_ESP32 - -DTESTING_USE_RTTTL - -[env:test_D_ESP32s2_4M316k] -extends = esp32s2_common -build_flags = ${esp32s2_common.build_flags} - -DFEATURE_ARDUINO_OTA - -DPLUGIN_SET_TEST_D_ESP32 - -DTESTING_USE_RTTTL - - -[env:energy_ESP32s2_4M316k] -extends = esp32s2_common -lib_deps = ${esp32s2_common.lib_deps}, ServoESP32 -build_flags = ${esp32s2_common.build_flags} - -DFEATURE_ARDUINO_OTA - -D PLUGIN_ENERGY_COLLECTION + -DESP32S2 + -DBOARD_HAS_PSRAM + -DESP32_ENABLE_PSRAM + -DUSE_LITTLEFS +lib_deps = ${esp32_common.lib_deps}, LittleFS +board_build.filesystem = littlefs -[env:display_ESP32s2_4M316k] -extends = esp32s2_common -lib_deps = ${esp32s2_common.lib_deps}, ServoESP32 -build_flags = ${esp32s2_common.build_flags} - -DFEATURE_ARDUINO_OTA - -D PLUGIN_DISPLAY_COLLECTION -; A Lolin D32 PRO with 16MB Flash, allowing 4MB sketch size, and file storage using the default (SPIFFS) filesystem -[max_ESP32_16M] -extends = esp32_common -lib_ignore = ${esp32_always.lib_ignore}, ESP32_ping -lib_deps = ${esp32_common.lib_deps}, VL53L0X -board_upload.maximum_size = 4194304 -build_flags = ${esp32_common.build_flags} - -DFEATURE_ARDUINO_OTA - -DPLUGIN_BUILD_MAX_ESP32 - -DPLUGIN_BUILD_IR_EXTENDED -; TODO: To enable PS-RAM Support needs more build flags than these 2, for now define ESP32_ENABLE_PSRAM is used to en/disable detecting PS-Ram size on Info page -; -DBOARD_HAS_PSRAM // both flags already enabled for Lolin D32 Pro board by PlatformIO -; -mfix-esp32-psram-cache-issue -;board = esp32_16M -board = lolin_d32_pro +[env:normal_ESP32_4M316k_LittleFS] +extends = esp32_common_LittleFS +board = esp32_4M +lib_deps = ${esp32_common_LittleFS.lib_deps}, ServoESP32 -; A Lolin D32 PRO with 16MB Flash, allowing 4MB sketch size, and file storage using LittleFS filesystem -[max_ESP32_16M_LittleFS] -extends = esp32_LittleFS -lib_ignore = ${esp32_always.lib_ignore}, ESP32_ping -lib_deps = ${esp32_LittleFS.lib_deps}, VL53L0X -board_upload.maximum_size = 4194304 -build_flags = ${esp32_LittleFS.build_flags} - -DFEATURE_ARDUINO_OTA - -DPLUGIN_BUILD_MAX_ESP32 - -DPLUGIN_BUILD_IR_EXTENDED -; TODO: To enable PS-RAM Support needs more build flags than these 2, for now define ESP32_ENABLE_PSRAM is used to en/disable detecting PS-Ram size on Info page -; -DBOARD_HAS_PSRAM // both flags already enabled for Lolin D32 Pro board by PlatformIO -; -mfix-esp32-psram-cache-issue +[env:normal_ESP32_16M8M_LittleFS] +extends = esp32_common_LittleFS board = esp32_16M +lib_deps = ${esp32_common_LittleFS.lib_deps}, ServoESP32 -[max_ESP32s2_16M] -extends = esp32s2_common -lib_ignore = ${esp32_always.lib_ignore}, ESP32_ping -lib_deps = ${esp32s2_common.lib_deps}, VL53L0X -board_upload.maximum_size = 4194304 -build_flags = ${esp32s2_common.build_flags} - -DFEATURE_ARDUINO_OTA - -DPLUGIN_BUILD_MAX_ESP32 - -DPLUGIN_BUILD_IR_EXTENDED - -; A Lolin D32 PRO with 16MB Flash, allowing 4MB sketch size, and file storage using LittleFS filesystem -[max_ESP32s2_16M_LittleFS] -extends = esp32s2_LittleFS -lib_ignore = ${esp32_always.lib_ignore}, ESP32_ping -lib_deps = ${esp32s2_LittleFS.lib_deps}, VL53L0X -board_upload.maximum_size = 4194304 -build_flags = ${esp32s2_LittleFS.build_flags} - -DFEATURE_ARDUINO_OTA - -DPLUGIN_BUILD_MAX_ESP32 - -DPLUGIN_BUILD_IR_EXTENDED - -; A Lolin D32 PRO with 16MB Flash, allowing 4MB sketch size, and 1MB assigned to file storage using SPIFFS filesystem -[env:max_ESP32_16M1M] -extends = max_ESP32_16M -board_build.partitions = esp32_partition_app4096k_spiffs1024k.csv - -; A Lolin D32 PRO with 16MB Flash, allowing 4MB sketch size, and 2MB assigned to file storage using LittleFS filesystem -[env:max_ESP32_16M2M_LittleFS] -extends = max_ESP32_16M_LittleFS -board_build.partitions = esp32_partition_app4096k_spiffs2048k.csv -; A Lolin D32 PRO with 16MB Flash, allowing 4MB sketch size, and the rest (nearly 8MB) assigned to file storage using LittleFS filesystem -[env:max_ESP32_16M8M_LittleFS] -extends = max_ESP32_16M_LittleFS -board_build.partitions = esp32_partition_app4096k_spiffs8124k.csv - -; If you have a board with Ethernet integrated and 16MB Flash, then this configuration could be enabled, it's based on the max_ESP32_16M1M definition -[env:max_ESP32_16M1M_ETH] -extends = env:max_ESP32_16M1M -build_flags = ${env:max_ESP32_16M1M.build_flags} -DHAS_ETHERNET - -; If you have a board with Ethernet integrated and 16MB Flash, then this configuration could be enabled, it's based on the max_ESP32_16M2M_LittleFS definition -[env:max_ESP32_16M2M_LittleFS_ETH] -extends = env:max_ESP32_16M2M_LittleFS -build_flags = ${env:max_ESP32_16M2M_LittleFS.build_flags} -DHAS_ETHERNET - -; If you have a board with Ethernet integrated and 16MB Flash, then this configuration could be enabled, it's based on the max_ESP32_16M8M_LittleFS definition -[env:max_ESP32_16M8M_LittleFS_ETH] -extends = env:max_ESP32_16M8M_LittleFS -build_flags = ${env:max_ESP32_16M8M_LittleFS.build_flags} -DHAS_ETHERNET - -[env:max_ESP32s2_16M8M_LittleFS] -extends = max_ESP32s2_16M_LittleFS -board_build.partitions = esp32_partition_app4096k_spiffs8124k.csv +; ESP32-S2 +[env:normal_ESP32s2_4M316k_LittleFS] +extends = esp32s2_common_LittleFS +board = esp32s2 diff --git a/platformio_esp82xx_base.ini b/platformio_esp82xx_base.ini index ba02e1c93f..d2cbe8ac8e 100644 --- a/platformio_esp82xx_base.ini +++ b/platformio_esp82xx_base.ini @@ -49,12 +49,10 @@ lib_ignore = ${no_ir.lib_ignore}, ${no_sd.lib_ignore}, ${no_littl [esp82xx_common] extends = common -board_build.f_cpu = 80000000L build_flags = ${debug_flags.build_flags} ${mqtt_flags.build_flags} -DHTTPCLIENT_1_1_COMPATIBLE=0 build_unflags = -DDEBUG_ESP_PORT lib_deps = td-er/ESPeasySerial @ 2.0.8, adafruit/Adafruit ILI9341 @ ^1.5.6, Adafruit GFX Library, LOLIN_EPD, Adafruit BusIO, bblanchon/ArduinoJson @ ^6.17.2, VL53L0X @ 1.3.0, SparkFun VL53L1X 4m Laser Distance Sensor @ 1.2.9, td-er/RABurton ESP8266 Mutex @ ^1.0.2, td-er/SparkFun MAX1704x Fuel Gauge Arduino Library @ ^1.0.1, ESP8266HTTPUpdateServer, FrogmoreScd30, Multi Channel Relay Arduino Library lib_ignore = ${esp82xx_defaults.lib_ignore}, IRremoteESP8266, HeatpumpIR, LittleFS(esp8266), ServoESP32, TinyWireM -board = esp12e monitor_filters = esp8266_exception_decoder @@ -152,9 +150,6 @@ build_flags = ${ir.build_flags} -DPLUGIN_BUILD_IR_EXTENDED_NO_RX ; ********************************************************************* [esp82xx_1M] extends = esp82xx_common -board_build.flash_mode = dout -board_upload.maximum_size = 786432 -board_build.ldscript = eagle.flash.1m128.ld build_flags = -DSIZE_1M -DBUILD_NO_DEBUG -Os @@ -163,11 +158,11 @@ build_flags = -DSIZE_1M [esp8266_1M] extends = esp82xx_1M -board = esp01_1m +board = esp8266_1M128k [esp8285_1M] extends = esp82xx_1M -board = esp8285 +board = esp8285_1M128k build_flags = ${esp8266_1M.build_flags} -DESP8285 @@ -176,18 +171,16 @@ build_flags = ${esp8266_1M.build_flags} -DESP8285 ; ********************************************************************* [esp82xx_1M_OTA] extends = esp82xx_1M -board_build.flash_mode = dout -board_upload.maximum_size = 616448 build_flags = ${esp82xx_1M.build_flags} -DPLUGIN_BUILD_MINIMAL_OTA -DDISABLE_SC16IS752_Serial [esp8266_1M_OTA] extends = esp82xx_1M_OTA -board = esp01_1m +board = esp8266_1M128k_OTA [esp8285_1M_OTA] extends = esp82xx_1M_OTA -board = esp8285 +board = esp8285_1M128k_OTA build_flags = ${esp82xx_1M_OTA.build_flags} -DESP8285 @@ -197,17 +190,7 @@ build_flags = ${esp82xx_1M_OTA.build_flags} -DESP8285 [esp8266_2M256] extends = esp82xx_common -board = esp12e -board_build.flash_mode = dout -board_upload.maximum_size = 1044464 -board_build.ldscript = eagle.flash.2m256.ld - -[espWroom2M] -extends = esp82xx_common -board = esp12e -board_build.flash_mode = dout -board_upload.maximum_size = 1044464 -board_build.ldscript = eagle.flash.2m.ld +board = esp8266_2M256 [espWroom2M256] extends = esp82xx_common @@ -223,17 +206,11 @@ board_build.ldscript = eagle.flash.2m256.ld [esp8266_4M1M] extends = esp82xx_common -board = esp12e -board_build.flash_mode = dout -board_upload.maximum_size = 1044464 -board_build.ldscript = eagle.flash.4m1m.ld +board = esp8266_4M1M_board [esp8266_4M2M] extends = esp82xx_common -board = esp12e -board_build.flash_mode = dout -board_upload.maximum_size = 1044464 -board_build.ldscript = eagle.flash.4m2m.ld +board = esp8266_4M2M_board @@ -247,10 +224,7 @@ board_build.ldscript = eagle.flash.4m2m.ld ; See https://github.com/esp8266/Arduino/issues/5932 [esp8266_16M] extends = esp82xx_common -board = esp12e -board_build.flash_mode = dout -board_upload.maximum_size = 1044464 -board_build.ldscript = eagle.flash.16m14m.ld +board = esp8266_16M14M_board build_flags = -DSPIFFS_MAX_OPEN_FILES=20 ${esp82xx_common.build_flags} diff --git a/platformio_esp82xx_envs.ini b/platformio_esp82xx_envs.ini index 5b86fd3979..d3cc0f0f1b 100644 --- a/platformio_esp82xx_envs.ini +++ b/platformio_esp82xx_envs.ini @@ -207,15 +207,6 @@ build_flags = ${regular_platform.build_flags} ${esp8285_1M.build_flags} -; NORMAL: 2048k WROOM02 version -------------------------- -[env:normal_WROOM02_2M] -extends = espWroom2M -platform = ${regular_platform.platform} -platform_packages = ${regular_platform.platform_packages} -build_flags = ${regular_platform.build_flags} - ${espWroom2M.build_flags} - - ; NORMAL: 2048k WROOM02 version 256k SPIFFS -------------------------- [env:normal_WROOM02_2M256] extends = espWroom2M256 @@ -400,112 +391,108 @@ build_flags = ${normal_ir_extended_no_rx.build_flags} ; Includes "normal" + "testing" plugins ; ; ********************************************************************* -[env:test_A_ESP8266_4M1M] +[testing_ESP8266_4M1M] extends = esp8266_4M1M platform = ${testing.platform} platform_packages = ${testing.platform_packages} build_flags = ${testing.build_flags} ${esp8266_4M1M.build_flags} - -DTESTING_USE_RTTTL -[env:test_B_ESP8266_4M1M] +[testing_alt_wifi_ESP8266_4M1M] extends = esp8266_4M1M -platform = ${testing.platform} -platform_packages = ${testing.platform_packages} -build_flags = ${testing.build_flags} +platform = ${testing_alt_wifi.platform} +platform_packages = ${testing_alt_wifi.platform_packages} +build_flags = ${testing_alt_wifi.build_flags} ${esp8266_4M1M.build_flags} - -DPLUGIN_BUILD_TESTING_B -[env:test_C_ESP8266_4M1M] +[testing_beta_ESP8266_4M1M] extends = esp8266_4M1M -platform = ${testing.platform} -platform_packages = ${testing.platform_packages} -build_flags = ${testing.build_flags} +platform = ${testing_beta.platform} +platform_packages = ${testing_beta.platform_packages} +build_flags = ${testing_beta.build_flags} ${esp8266_4M1M.build_flags} + +[testing_beta_ESP8266_16M_LittleFS] +extends = esp8266_16M +platform = ${testing_beta.platform} +platform_packages = ${testing_beta.platform_packages} +build_flags = ${testing_beta.build_flags} + ${esp8266_16M.build_flags} + -DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK22y +board_build.filesystem = littlefs +lib_ignore = ESP32_ping, ESP32WebServer, ServoESP32, ESP32HTTPUpdateServer, IRremoteESP8266, HeatpumpIR + + +[env:test_A_ESP8266_4M1M] +extends = testing_ESP8266_4M1M +build_flags = ${testing_ESP8266_4M1M.build_flags} + -DTESTING_USE_RTTTL + +[env:test_B_ESP8266_4M1M] +extends = testing_ESP8266_4M1M +build_flags = ${testing_ESP8266_4M1M.build_flags} + -DPLUGIN_BUILD_TESTING_B + +[env:test_C_ESP8266_4M1M] +extends = testing_ESP8266_4M1M +build_flags = ${testing_ESP8266_4M1M.build_flags} -DPLUGIN_BUILD_TESTING_C -DTESTING_USE_RTTTL [env:test_D_ESP8266_4M1M] -extends = esp8266_4M1M -platform = ${testing.platform} -platform_packages = ${testing.platform_packages} -build_flags = ${testing.build_flags} - ${esp8266_4M1M.build_flags} +extends = testing_ESP8266_4M1M +build_flags = ${testing_ESP8266_4M1M.build_flags} -DPLUGIN_BUILD_TESTING_D -DTESTING_USE_RTTTL ; TEST: 4096k version + FEATURE_ADC_VCC ---------- [env:test_A_ESP8266_4M1M_VCC] -extends = esp8266_4M1M -platform = ${testing.platform} -platform_packages = ${testing.platform_packages} -build_flags = ${testing.build_flags} - ${esp8266_4M1M.build_flags} +extends = testing_ESP8266_4M1M +build_flags = ${testing_ESP8266_4M1M.build_flags} -DFEATURE_ADC_VCC=true -DTESTING_USE_RTTTL [env:test_B_ESP8266_4M1M_VCC] -extends = esp8266_4M1M -platform = ${testing.platform} -platform_packages = ${testing.platform_packages} -build_flags = ${testing.build_flags} - ${esp8266_4M1M.build_flags} +extends = testing_ESP8266_4M1M +build_flags = ${testing_ESP8266_4M1M.build_flags} -DFEATURE_ADC_VCC=true -DPLUGIN_BUILD_TESTING_B [env:test_C_ESP8266_4M1M_VCC] -extends = esp8266_4M1M -platform = ${testing.platform} -platform_packages = ${testing.platform_packages} -build_flags = ${testing.build_flags} - ${esp8266_4M1M.build_flags} +extends = testing_ESP8266_4M1M +build_flags = ${testing_ESP8266_4M1M.build_flags} -DFEATURE_ADC_VCC=true -DPLUGIN_BUILD_TESTING_C -DTESTING_USE_RTTTL [env:test_D_ESP8266_4M1M_VCC] -extends = esp8266_4M1M -platform = ${testing.platform} -platform_packages = ${testing.platform_packages} -build_flags = ${testing.build_flags} - ${esp8266_4M1M.build_flags} +extends = testing_ESP8266_4M1M +build_flags = ${testing_ESP8266_4M1M.build_flags} -DFEATURE_ADC_VCC=true -DPLUGIN_BUILD_TESTING_D -DTESTING_USE_RTTTL [env:test_A_alt_wifi_ESP8266_4M1M_VCC] -extends = esp8266_4M1M -platform = ${testing_alt_wifi.platform} -platform_packages = ${testing_alt_wifi.platform_packages} -build_flags = ${testing_alt_wifi.build_flags} - ${esp8266_4M1M.build_flags} +extends = testing_alt_wifi_ESP8266_4M1M +build_flags = ${testing_alt_wifi_ESP8266_4M1M.build_flags} -DFEATURE_ADC_VCC=true [env:test_B_alt_wifi_ESP8266_4M1M_VCC] -extends = esp8266_4M1M -platform = ${testing_alt_wifi.platform} -platform_packages = ${testing_alt_wifi.platform_packages} -build_flags = ${testing_alt_wifi.build_flags} - ${esp8266_4M1M.build_flags} +extends = testing_alt_wifi_ESP8266_4M1M +build_flags = ${testing_alt_wifi_ESP8266_4M1M.build_flags} -DFEATURE_ADC_VCC=true -DPLUGIN_BUILD_TESTING_B [env:test_C_alt_wifi_ESP8266_4M1M_VCC] -extends = esp8266_4M1M -platform = ${testing_alt_wifi.platform} -platform_packages = ${testing_alt_wifi.platform_packages} -build_flags = ${testing_alt_wifi.build_flags} - ${esp8266_4M1M.build_flags} +extends = testing_alt_wifi_ESP8266_4M1M +build_flags = ${testing_alt_wifi_ESP8266_4M1M.build_flags} -DFEATURE_ADC_VCC=true -DPLUGIN_BUILD_TESTING_C [env:test_D_alt_wifi_ESP8266_4M1M_VCC] -extends = esp8266_4M1M -platform = ${testing_alt_wifi.platform} -platform_packages = ${testing_alt_wifi.platform_packages} -build_flags = ${testing_alt_wifi.build_flags} - ${esp8266_4M1M.build_flags} +extends = testing_alt_wifi_ESP8266_4M1M +build_flags = ${testing_alt_wifi_ESP8266_4M1M.build_flags} -DFEATURE_ADC_VCC=true -DPLUGIN_BUILD_TESTING_D @@ -526,39 +513,27 @@ build_flags = ${testing_alt_wifi.build_flags} [env:test_A_beta_ESP8266_4M1M] -extends = esp8266_4M1M -platform = ${testing_beta.platform} -platform_packages = ${testing_beta.platform_packages} -build_flags = ${testing_beta.build_flags} - ${esp8266_4M1M.build_flags} +extends = testing_beta_ESP8266_4M1M +build_flags = ${testing_beta_ESP8266_4M1M.build_flags} -DLIMIT_BUILD_SIZE -DTESTING_USE_RTTTL [env:test_B_beta_ESP8266_4M1M] -extends = esp8266_4M1M -platform = ${testing_beta.platform} -platform_packages = ${testing_beta.platform_packages} -build_flags = ${testing_beta.build_flags} - ${esp8266_4M1M.build_flags} +extends = testing_beta_ESP8266_4M1M +build_flags = ${testing_beta_ESP8266_4M1M.build_flags} -DLIMIT_BUILD_SIZE -DPLUGIN_BUILD_TESTING_B [env:test_C_beta_ESP8266_4M1M] -extends = esp8266_4M1M -platform = ${testing_beta.platform} -platform_packages = ${testing_beta.platform_packages} -build_flags = ${testing_beta.build_flags} - ${esp8266_4M1M.build_flags} +extends = testing_beta_ESP8266_4M1M +build_flags = ${testing_beta_ESP8266_4M1M.build_flags} -DLIMIT_BUILD_SIZE -DPLUGIN_BUILD_TESTING_C -DTESTING_USE_RTTTL [env:test_D_beta_ESP8266_4M1M] -extends = esp8266_4M1M -platform = ${testing_beta.platform} -platform_packages = ${testing_beta.platform_packages} -build_flags = ${testing_beta.build_flags} - ${esp8266_4M1M.build_flags} +extends = testing_beta_ESP8266_4M1M +build_flags = ${testing_beta_ESP8266_4M1M.build_flags} -DLIMIT_BUILD_SIZE -DPLUGIN_BUILD_TESTING_D -DTESTING_USE_RTTTL @@ -566,49 +541,25 @@ build_flags = ${testing_beta.build_flags} ; Test: 16M version -- LittleFS -------------- ; LittleFS is determined by using "LittleFS" in the pio env name [env:test_A_beta_ESP8266_16M_LittleFS] -extends = esp8266_16M -platform = ${testing_beta.platform} -platform_packages = ${testing_beta.platform_packages} -build_flags = ${testing_beta.build_flags} - ${esp8266_16M.build_flags} - -DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK22y -lib_ignore = ESP32_ping, ESP32WebServer, ServoESP32, ESP32HTTPUpdateServer, IRremoteESP8266, HeatpumpIR +extends = testing_beta_ESP8266_16M_LittleFS +build_flags = ${testing_beta_ESP8266_16M_LittleFS.build_flags} [env:test_B_beta_ESP8266_16M_LittleFS] -extends = esp8266_16M -platform = ${testing_beta.platform} -platform_packages = ${testing_beta.platform_packages} -build_flags = ${testing_beta.build_flags} - ${esp8266_16M.build_flags} +extends = testing_beta_ESP8266_16M_LittleFS +build_flags = ${testing_beta_ESP8266_16M_LittleFS.build_flags} -DPLUGIN_BUILD_TESTING_B - -DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK22y -lib_ignore = ESP32_ping, ESP32WebServer, ServoESP32, ESP32HTTPUpdateServer, IRremoteESP8266, HeatpumpIR [env:test_C_beta_ESP8266_16M_LittleFS] -extends = esp8266_16M -platform = ${testing_beta.platform} -platform_packages = ${testing_beta.platform_packages} -build_flags = ${testing_beta.build_flags} - ${esp8266_16M.build_flags} - -DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK22y +extends = testing_beta_ESP8266_16M_LittleFS +build_flags = ${testing_beta_ESP8266_16M_LittleFS.build_flags} -DPLUGIN_BUILD_TESTING_C -DTESTING_USE_RTTTL -lib_ignore = ESP32_ping, ESP32WebServer, ServoESP32, ESP32HTTPUpdateServer, IRremoteESP8266, HeatpumpIR [env:test_D_beta_ESP8266_16M_LittleFS] -extends = esp8266_16M -platform = ${testing_beta.platform} -platform_packages = ${testing_beta.platform_packages} -build_flags = ${testing_beta.build_flags} - ${esp8266_16M.build_flags} - -DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK22y +extends = testing_beta_ESP8266_16M_LittleFS +build_flags = ${testing_beta_ESP8266_16M_LittleFS.build_flags} -DPLUGIN_BUILD_TESTING_D -DTESTING_USE_RTTTL -lib_ignore = ESP32_ping, ESP32WebServer, ServoESP32, ESP32HTTPUpdateServer, IRremoteESP8266, HeatpumpIR - - - - diff --git a/src/ESPEasy_common.h b/src/ESPEasy_common.h index 216a007278..8f914b55c0 100644 --- a/src/ESPEasy_common.h +++ b/src/ESPEasy_common.h @@ -129,24 +129,8 @@ namespace std #endif #include // Needed to call ESP-IDF functions like esp_wifi_.... - #ifdef PLUGIN_BUILD_MAX_ESP32 - #define MAX_SKETCH_SIZE 4194304 // 0x400000 look at partitions in csv file - #else // PLUGIN_BUILD_MAX_ESP32 - #define MAX_SKETCH_SIZE 1900544 // 0x1d0000 look at partitions in csv file - #endif // PLUGIN_BUILD_MAX_ESP32 #endif -#include -#include -#include -#include -#ifdef FEATURE_SD -#include -#else -using namespace fs; -#endif -#include - #ifdef USE_LITTLEFS #ifdef ESP32 @@ -168,6 +152,19 @@ using namespace fs; #define ESPEASY_FS SPIFFS #endif + +#include +#include +#include +#include +#ifdef FEATURE_SD +#include +#else +using namespace fs; +#endif +#include + + // Include custom first, then build info. (one may want to set BUILD_GIT for example) #include "src/CustomBuild/ESPEasy_buildinfo.h" #include "src/CustomBuild/ESPEasyLimits.h" diff --git a/src/src/ESPEasyCore/ESPEasyRules.cpp b/src/src/ESPEasyCore/ESPEasyRules.cpp index 19bf1eda62..d1d2eb1ab6 100644 --- a/src/src/ESPEasyCore/ESPEasyRules.cpp +++ b/src/src/ESPEasyCore/ESPEasyRules.cpp @@ -522,7 +522,8 @@ bool parse_bitwise_functions(const String& cmd_s_lower, const String& arg1, cons } bool parse_math_functions(const String& cmd_s_lower, const String& arg1, const String& arg2, const String& arg3, double& result) { - double farg1, farg2, farg3 = 0.0f; + double farg1; + float farg2, farg3 = 0.0f; if (!validDoubleFromString(arg1, farg1)) { return false; @@ -531,9 +532,9 @@ bool parse_math_functions(const String& cmd_s_lower, const String& arg1, const S if (cmd_s_lower.equals(F("constrain"))) { // Contrain a value X to be within range of A to B // Syntax like {constrain:x:a:b} to constrain x in range a...b - if (validDoubleFromString(arg2, farg2) && validDoubleFromString(arg3, farg3)) { + if (validFloatFromString(arg2, farg2) && validFloatFromString(arg3, farg3)) { if (farg2 > farg3) { - const double tmp = farg2; + const float tmp = farg2; farg2 = farg3; farg3 = tmp; } diff --git a/src/src/Helpers/ESPEasy_Storage.cpp b/src/src/Helpers/ESPEasy_Storage.cpp index 48b95e0c6e..1baaf9d8f1 100644 --- a/src/src/Helpers/ESPEasy_Storage.cpp +++ b/src/src/Helpers/ESPEasy_Storage.cpp @@ -368,10 +368,8 @@ void fileSystemCheck() bool FS_format() { #ifdef USE_LITTLEFS #ifdef ESP32 - disableCore1WDT(); const bool res = ESPEASY_FS.begin(true); ESPEASY_FS.end(); - enableCore1WDT(); return res; #else return ESPEASY_FS.format(); diff --git a/src/src/Helpers/Hardware.cpp b/src/src/Helpers/Hardware.cpp index a10bc7dc46..7bb954ae5d 100644 --- a/src/src/Helpers/Hardware.cpp +++ b/src/src/Helpers/Hardware.cpp @@ -485,7 +485,12 @@ uint32_t getFlashChipId() { static uint32_t flashChipId = 0; if (flashChipId == 0) { #ifdef ESP32 - flashChipId = g_rom_flashchip.device_id; + uint32_t tmp = g_rom_flashchip.device_id; + for (int i = 0; i < 3; ++i) { + flashChipId = flashChipId << 8; + flashChipId |= (tmp & 0xFF); + tmp = tmp >> 8; + } // esp_flash_read_id(nullptr, &flashChipId); #elif defined(ESP8266) flashChipId = ESP.getFlashChipId(); @@ -499,7 +504,7 @@ uint32_t getFlashRealSizeInBytes() { static uint32_t res = 0; if (res == 0) { #if defined(ESP32) - res = ESP.getFlashChipSize(); + res = (1 << ((getFlashChipId() >> 16) & 0xFF)); #else // if defined(ESP32) res = ESP.getFlashChipRealSize(); // ESP.getFlashChipSize(); #endif // if defined(ESP32) @@ -507,6 +512,9 @@ uint32_t getFlashRealSizeInBytes() { return res; } +uint32_t getFlashChipSpeed() { + return ESP.getFlashChipSpeed(); +} bool puyaSupport() { bool supported = false; diff --git a/src/src/Helpers/Hardware.h b/src/src/Helpers/Hardware.h index 6cbfe96865..399bbae051 100644 --- a/src/src/Helpers/Hardware.h +++ b/src/src/Helpers/Hardware.h @@ -67,6 +67,8 @@ uint32_t getFlashChipId(); uint32_t getFlashRealSizeInBytes(); +uint32_t getFlashChipSpeed(); + bool puyaSupport(); uint8_t getFlashChipVendorId(); diff --git a/src/src/Helpers/OTA.cpp b/src/src/Helpers/OTA.cpp index a54c8a1dcf..955c17ef09 100644 --- a/src/src/Helpers/OTA.cpp +++ b/src/src/Helpers/OTA.cpp @@ -5,6 +5,7 @@ #include "../Globals/SecuritySettings.h" #include "../Globals/Services.h" #include "../Globals/Settings.h" +#include "../Helpers/Hardware.h" #include "../Helpers/Misc.h" bool OTA_possible(uint32_t& maxSketchSize, bool& use2step) { @@ -29,7 +30,9 @@ bool OTA_possible(uint32_t& maxSketchSize, bool& use2step) { if (maxSketchSize > MAX_SKETCH_SIZE) { maxSketchSize = MAX_SKETCH_SIZE; } return otaPossible; #elif defined(ESP32) - maxSketchSize = MAX_SKETCH_SIZE; + // ESP32 writes an OTA image to the "other" app partition. + // Thus what is reported as "free" sketch space is the size of the not used app partition. + maxSketchSize = getFreeSketchSpace(); use2step = false; return true; #else // if defined(ESP8266) diff --git a/src/src/Helpers/StringProvider.cpp b/src/src/Helpers/StringProvider.cpp index 78dc0891a9..ceb4ed8726 100644 --- a/src/src/Helpers/StringProvider.cpp +++ b/src/src/Helpers/StringProvider.cpp @@ -164,6 +164,7 @@ const __FlashStringHelper * getLabel(LabelType::Enum label) { case LabelType::FLASH_CHIP_ID: return F("Flash Chip ID"); case LabelType::FLASH_CHIP_REAL_SIZE: return F("Flash Chip Real Size"); + case LabelType::FLASH_CHIP_SPEED: return F("Flash Chip Speed"); case LabelType::FLASH_IDE_SIZE: return F("Flash IDE Size"); case LabelType::FLASH_IDE_SPEED: return F("Flash IDE Speed"); case LabelType::FLASH_IDE_MODE: return F("Flash IDE Mode"); @@ -353,6 +354,7 @@ String getValue(LabelType::Enum label) { case LabelType::FLASH_CHIP_ID: break; case LabelType::FLASH_CHIP_REAL_SIZE: break; + case LabelType::FLASH_CHIP_SPEED: return String(getFlashChipSpeed()); case LabelType::FLASH_IDE_SIZE: break; case LabelType::FLASH_IDE_SPEED: break; case LabelType::FLASH_IDE_MODE: break; diff --git a/src/src/Helpers/StringProvider.h b/src/src/Helpers/StringProvider.h index af1496f007..57f094256f 100644 --- a/src/src/Helpers/StringProvider.h +++ b/src/src/Helpers/StringProvider.h @@ -135,6 +135,7 @@ struct LabelType { FLASH_CHIP_ID, FLASH_CHIP_REAL_SIZE, + FLASH_CHIP_SPEED, FLASH_IDE_SIZE, FLASH_IDE_SPEED, FLASH_IDE_MODE, diff --git a/src/src/WebServer/CustomPage.cpp b/src/src/WebServer/CustomPage.cpp index b7114b2432..81d10c1f17 100644 --- a/src/src/WebServer/CustomPage.cpp +++ b/src/src/WebServer/CustomPage.cpp @@ -141,14 +141,14 @@ bool handle_custom(const String& path) { String line; line.reserve(128); while (available > 0) { - uint32_t chunksize = 64; + int32_t chunksize = 64; if (available < chunksize) { chunksize = available; } uint8_t buf[64] = {0}; - const size_t read = dataFile.read(buf, chunksize); + const int read = dataFile.read(buf, chunksize); if (read == chunksize) { - for (uint32_t i = 0; i < chunksize; ++i) { + for (int32_t i = 0; i < chunksize; ++i) { const char c = (char)buf[i]; line += c; if (c == '\n') { diff --git a/src/src/WebServer/FileList.cpp b/src/src/WebServer/FileList.cpp index 10467c0e48..a6d8e55ca8 100644 --- a/src/src/WebServer/FileList.cpp +++ b/src/src/WebServer/FileList.cpp @@ -11,6 +11,8 @@ #include "../Helpers/ESPEasy_Storage.h" #include "../Helpers/Numerical.h" +#include "../../ESPEasy_common.h" + #ifdef USES_C016 diff --git a/src/src/WebServer/LoadFromFS.cpp b/src/src/WebServer/LoadFromFS.cpp index 8ff857fb64..6b90dc29db 100644 --- a/src/src/WebServer/LoadFromFS.cpp +++ b/src/src/WebServer/LoadFromFS.cpp @@ -155,14 +155,14 @@ size_t streamFromFS(String path, bool htmlEscape) { int available = f.available(); String escaped; while (available > 0) { - uint32_t chunksize = 64; + int32_t chunksize = 64; if (available < chunksize) { chunksize = available; } uint8_t buf[64] = {0}; - const size_t read = f.read(buf, chunksize); + const int read = f.read(buf, chunksize); if (read == chunksize) { - for (uint32_t i = 0; i < chunksize; ++i) { + for (int32_t i = 0; i < chunksize; ++i) { const char c = (char)buf[i]; if (htmlEscape && htmlEscapeChar(c, escaped)) { addHtml(escaped); diff --git a/src/src/WebServer/SysInfoPage.cpp b/src/src/WebServer/SysInfoPage.cpp index dd7721354b..e19fa1687d 100644 --- a/src/src/WebServer/SysInfoPage.cpp +++ b/src/src/WebServer/SysInfoPage.cpp @@ -188,7 +188,6 @@ void handle_sysinfo_json() { json_number(F("ide_size"), String(ESP.getFlashChipSize() / 1024)); // Please check what is supported for the ESP32 - # if defined(ESP8266) json_number(F("flash_speed"), String(ESP.getFlashChipSpeed() / 1000000)); FlashMode_t ideMode = ESP.getFlashChipMode(); @@ -201,7 +200,6 @@ void handle_sysinfo_json() { default: json_prop(F("mode"), getUnknownString()); break; } - # endif // if defined(ESP8266) json_number(F("writes"), String(RTC.flashDayCounter)); json_number(F("flash_counter"), String(RTC.flashCounter)); @@ -625,6 +623,10 @@ void handle_sysinfo_Storage() { addHtmlInt(ideSize / 1024); addHtml(F(" kB")); + addRowLabel(LabelType::FLASH_CHIP_SPEED); + addHtmlInt(getFlashChipSpeed() / 1000000); + addHtml(F(" MHz")); + // Please check what is supported for the ESP32 # if defined(ESP8266) addRowLabel(LabelType::FLASH_IDE_SPEED); From ddbb6a7bb1328bfe7a31d225a738f40a3b8b8cf0 Mon Sep 17 00:00:00 2001 From: TD-er Date: Tue, 4 Jan 2022 00:31:32 +0100 Subject: [PATCH 027/147] [IDF4.4 + 16M] Restore missing ESP32 builds Somehow the max 16M8M builds do end up again in boot loops.... --- boards/esp32_16M1M.json | 38 +++ boards/{esp32_16M.json => esp32_16M8M.json} | 0 platformio_esp32_envs.ini | 306 +++++++++++++++++++- 3 files changed, 341 insertions(+), 3 deletions(-) create mode 100644 boards/esp32_16M1M.json rename boards/{esp32_16M.json => esp32_16M8M.json} (100%) diff --git a/boards/esp32_16M1M.json b/boards/esp32_16M1M.json new file mode 100644 index 0000000000..2d483c3ded --- /dev/null +++ b/boards/esp32_16M1M.json @@ -0,0 +1,38 @@ +{ + "build": { + "arduino":{ + "ldscript": "esp32_out.ld" + }, + "core": "esp32", + "extra_flags": "-DARDUINO_ESP32_DEV -DBOARD_HAS_PSRAM -DARDUINO_USB_CDC_ON_BOOT=0 -DESP32_16M", + "f_cpu": "80000000L", + "f_flash": "40000000L", + "flash_mode": "dout", + "mcu": "esp32", + "variant": "esp32", + "partitions": "esp32_partition_app4096k_spiffs1024k.csv" + }, + "connectivity": [ + "wifi", + "bluetooth", + "ethernet", + "can" + ], + "debug": { + "openocd_target": "esp32.cfg" + }, + "frameworks": [ + "arduino", + "espidf" + ], + "name": "Espressif Generic ESP32 16M Flash, ESPEasy 4M Code/OTA, 1M FS", + "upload": { + "flash_size": "16MB", + "maximum_ram_size": 327680, + "maximum_size": 4194304, + "require_upload_port": true, + "speed": 460800 + }, + "url": "https://en.wikipedia.org/wiki/ESP32", + "vendor": "Espressif" +} diff --git a/boards/esp32_16M.json b/boards/esp32_16M8M.json similarity index 100% rename from boards/esp32_16M.json rename to boards/esp32_16M8M.json diff --git a/platformio_esp32_envs.ini b/platformio_esp32_envs.ini index 8339cfec6f..fd4c4711d0 100644 --- a/platformio_esp32_envs.ini +++ b/platformio_esp32_envs.ini @@ -31,17 +31,89 @@ lib_deps = ${esp32_common.lib_deps}, LittleFS board_build.filesystem = littlefs -[esp32s2_common_LittleFS] +[esp32s2_common] extends = esp32_common build_flags = ${esp32_common.build_flags} -DESP32S2 -DBOARD_HAS_PSRAM -DESP32_ENABLE_PSRAM + -DFEATURE_ARDUINO_OTA + +[esp32s2_common_LittleFS] +extends = esp32s2_common +build_flags = ${esp32s2_common.build_flags} -DUSE_LITTLEFS -lib_deps = ${esp32_common.lib_deps}, LittleFS +lib_deps = ${esp32s2_common.lib_deps}, LittleFS board_build.filesystem = littlefs + + +; Custom: 4096k version -------------------------- +[env:custom_ESP32_4M316k] +extends = esp32_common +board = esp32_4M +build_flags = ${esp32_common.build_flags} + -DPLUGIN_BUILD_CUSTOM +extra_scripts = ${esp32_common.extra_scripts} + pre:tools/pio/pre_custom_esp32.py + +[env:custom_ESP32_4M316k_LittleFS] +extends = esp32_common_LittleFS +board = esp32_4M +build_flags = ${esp32_common_LittleFS.build_flags} + -DPLUGIN_BUILD_CUSTOM +extra_scripts = ${esp32_common_LittleFS.extra_scripts} + pre:tools/pio/pre_custom_esp32.py + +[env:custom_ESP32_16M8M_LittleFS] +extends = esp32_common_LittleFS +board = esp32_16M8M +build_flags = ${esp32_common_LittleFS.build_flags} + -DPLUGIN_BUILD_CUSTOM +extra_scripts = ${esp32_common_LittleFS.extra_scripts} + pre:tools/pio/pre_custom_esp32.py + +[env:custom_IR_ESP32_4M316k] +extends = esp32_common +board = esp32_4M +build_flags = ${esp32_common.build_flags} + -DPLUGIN_BUILD_CUSTOM + -DPLUGIN_BUILD_IR +lib_ignore = ${esp32_always.lib_ignore}, ESP32_ping, HeatpumpIR +extra_scripts = ${esp32_common.extra_scripts} + pre:tools/pio/pre_custom_esp32.py + + +; ESP32-S2 +[env:custom_ESP32s2_4M316k] +extends = esp32s2_common +board = esp32s2 +build_flags = ${esp32s2_common.build_flags} + -DPLUGIN_BUILD_CUSTOM +extra_scripts = ${esp32s2_common.extra_scripts} + pre:tools/pio/pre_custom_esp32.py + + +[env:custom_IR_ESP32s2_4M316k] +extends = esp32s2_common +board = esp32s2 +build_flags = ${esp32s2_common.build_flags} + -DPLUGIN_BUILD_CUSTOM + -DPLUGIN_BUILD_IR +lib_ignore = ${esp32_always.lib_ignore}, ESP32_ping, HeatpumpIR +extra_scripts = ${esp32s2_common.extra_scripts} + pre:tools/pio/pre_custom_esp32.py + + + +; Normal: 4096k version -------------------------- +[env:normal_ESP32_4M316k] +extends = esp32_common +board = esp32_4M +lib_deps = ${esp32_common.lib_deps}, ServoESP32 + + [env:normal_ESP32_4M316k_LittleFS] extends = esp32_common_LittleFS board = esp32_4M @@ -50,7 +122,7 @@ lib_deps = ${esp32_common_LittleFS.lib_deps}, ServoESP32 [env:normal_ESP32_16M8M_LittleFS] extends = esp32_common_LittleFS -board = esp32_16M +board = esp32_16M8M lib_deps = ${esp32_common_LittleFS.lib_deps}, ServoESP32 @@ -59,3 +131,231 @@ lib_deps = ${esp32_common_LittleFS.lib_deps}, ServoESP32 extends = esp32s2_common_LittleFS board = esp32s2 + + + +; Test A....E builds -------------------------- +[env:test_A_ESP32_4M316k] +extends = esp32_common +board = esp32_4M +build_flags = ${esp32_common.build_flags} + -DFEATURE_ARDUINO_OTA + -DPLUGIN_SET_TEST_ESP32 + -DTESTING_USE_RTTTL + +[env:test_B_ESP32_4M316k] +extends = esp32_common +board = esp32_4M +build_flags = ${esp32_common.build_flags} + -DFEATURE_ARDUINO_OTA + -DPLUGIN_SET_TEST_B_ESP32 + -DTESTING_USE_RTTTL + +[env:test_C_ESP32_4M316k] +extends = esp32_common +board = esp32_4M +build_flags = ${esp32_common.build_flags} + -DFEATURE_ARDUINO_OTA + -DPLUGIN_SET_TEST_C_ESP32 + -DTESTING_USE_RTTTL + +[env:test_D_ESP32_4M316k] +extends = esp32_common +board = esp32_4M +build_flags = ${esp32_common.build_flags} + -DFEATURE_ARDUINO_OTA + -DPLUGIN_SET_TEST_D_ESP32 + -DTESTING_USE_RTTTL + + +[env:test_A_ESP32_IRExt_4M316k] +extends = test_A_ESP32_4M316k +board = esp32_4M +lib_ignore = AS_BH1750, ${esp32_always.lib_ignore}, ESP32_ping +build_flags = ${esp32_common.build_flags} + -DPLUGIN_BUILD_NORMAL_IRext + -DTESTING_USE_RTTTL + +[env:test_B_ESP32_IRExt_4M316k] +extends = test_B_ESP32_4M316k +board = esp32_4M +lib_ignore = AS_BH1750, ${esp32_always.lib_ignore}, ESP32_ping +build_flags = ${esp32_common.build_flags} + -DPLUGIN_BUILD_NORMAL_IRext + -DTESTING_USE_RTTTL + +[env:test_C_ESP32_IRExt_4M316k] +extends = test_C_ESP32_4M316k +board = esp32_4M +lib_ignore = AS_BH1750, ${esp32_always.lib_ignore}, ESP32_ping +build_flags = ${esp32_common.build_flags} + -DPLUGIN_BUILD_NORMAL_IRext + -DTESTING_USE_RTTTL + +[env:test_D_ESP32_IRExt_4M316k] +extends = test_D_ESP32_4M316k +board = esp32_4M +lib_ignore = AS_BH1750, ${esp32_always.lib_ignore}, ESP32_ping +build_flags = ${esp32_common.build_flags} + -DPLUGIN_BUILD_NORMAL_IRext + -DTESTING_USE_RTTTL + +[env:energy_ESP32_4M316k] +extends = esp32_common +board = esp32_4M +lib_deps = ${esp32_common.lib_deps}, ServoESP32 +build_flags = ${esp32_common.build_flags} + -DFEATURE_ARDUINO_OTA + -D PLUGIN_ENERGY_COLLECTION + +[env:display_ESP32_4M316k] +extends = esp32_common +board = esp32_4M +lib_deps = ${esp32_common.lib_deps}, ServoESP32 +build_flags = ${esp32_common.build_flags} + -DFEATURE_ARDUINO_OTA + + + +; ETH builds -------------------------- +[env:custom_ESP32_4M316k_ETH] +extends = custom_ESP32_4M316k +board = esp32_4M +platform = ${custom_ESP32_4M316k.platform} +build_flags = ${custom_ESP32_4M316k.build_flags} -DHAS_ETHERNET + +[env:normal_ESP32_4M316k_ETH] +extends = normal_ESP32_4M316k +board = esp32_4M +platform = ${normal_ESP32_4M316k.platform} +build_flags = ${normal_ESP32_4M316k.build_flags} -DHAS_ETHERNET + +[env:test_A_ESP32_4M316k_ETH] +extends = test_A_ESP32_4M316k +board = esp32_4M +platform = ${test_A_ESP32_4M316k.platform} +build_flags = ${test_A_ESP32_4M316k.build_flags} -DHAS_ETHERNET + -DTESTING_USE_RTTTL + +[env:test_B_ESP32_4M316k_ETH] +extends = test_B_ESP32_4M316k +board = esp32_4M +platform = ${test_B_ESP32_4M316k.platform} +build_flags = ${test_B_ESP32_4M316k.build_flags} -DHAS_ETHERNET + -DTESTING_USE_RTTTL + +[env:test_C_ESP32_4M316k_ETH] +extends = test_C_ESP32_4M316k +board = esp32_4M +platform = ${test_C_ESP32_4M316k.platform} +build_flags = ${test_C_ESP32_4M316k.build_flags} -DHAS_ETHERNET + -DTESTING_USE_RTTTL + +[env:test_D_ESP32_4M316k_ETH] +extends = test_D_ESP32_4M316k +board = esp32_4M +platform = ${test_D_ESP32_4M316k.platform} +build_flags = ${test_D_ESP32_4M316k.build_flags} -DHAS_ETHERNET + -DTESTING_USE_RTTTL + + + +;;; ESP32-s2 *********************************************************** + +[env:normal_ESP32s2_4M316k] +extends = esp32s2_common +board = esp32s2 +lib_deps = ${esp32s2_common.lib_deps}, ServoESP32 + +[env:test_A_ESP32s2_4M316k] +extends = esp32s2_common +board = esp32s2 +build_flags = ${esp32s2_common.build_flags} + -DPLUGIN_SET_TEST_ESP32 + -DTESTING_USE_RTTTL + +[env:test_B_ESP32s2_4M316k] +extends = esp32s2_common +board = esp32s2 +build_flags = ${esp32s2_common.build_flags} + -DPLUGIN_SET_TEST_B_ESP32 + -DTESTING_USE_RTTTL + +[env:test_C_ESP32s2_4M316k] +extends = esp32s2_common +board = esp32s2 +build_flags = ${esp32s2_common.build_flags} + -DPLUGIN_SET_TEST_C_ESP32 + -DTESTING_USE_RTTTL + +[env:test_D_ESP32s2_4M316k] +extends = esp32s2_common +board = esp32s2 +build_flags = ${esp32s2_common.build_flags} + -DPLUGIN_SET_TEST_D_ESP32 + -DTESTING_USE_RTTTL + + +[env:energy_ESP32s2_4M316k] +extends = esp32s2_common +board = esp32s2 +lib_deps = ${esp32s2_common.lib_deps}, ServoESP32 +build_flags = ${esp32s2_common.build_flags} + -D PLUGIN_ENERGY_COLLECTION + +[env:display_ESP32s2_4M316k] +extends = esp32s2_common +board = esp32s2 +lib_deps = ${esp32s2_common.lib_deps}, ServoESP32 +build_flags = ${esp32s2_common.build_flags} + -D PLUGIN_DISPLAY_COLLECTION + + + + + +;;; ESP32 MAX builds 16M flash ------------------------------ + +; A Lolin D32 PRO with 16MB Flash, allowing 4MB sketch size, and file storage using the default (SPIFFS) filesystem +[env:max_ESP32_16M1M] +extends = esp32_common +board = esp32_16M1M +lib_ignore = ${esp32_always.lib_ignore}, ESP32_ping +lib_deps = ${esp32_common.lib_deps}, VL53L0X +build_flags = ${esp32_common.build_flags} + -DFEATURE_ARDUINO_OTA + -DPLUGIN_BUILD_MAX_ESP32 + -DPLUGIN_BUILD_IR_EXTENDED +; TODO: To enable PS-RAM Support needs more build flags than these 2, for now define ESP32_ENABLE_PSRAM is used to en/disable detecting PS-Ram size on Info page +; -DBOARD_HAS_PSRAM // both flags already enabled for Lolin D32 Pro board by PlatformIO +; -mfix-esp32-psram-cache-issue + + +; A Lolin D32 PRO with 16MB Flash, allowing 4MB sketch size, and file storage using LittleFS filesystem +[env:max_ESP32_16M8M_LittleFS] +extends = esp32_common_LittleFS +board = esp32_16M8M +lib_ignore = ${esp32_always.lib_ignore}, ESP32_ping +lib_deps = ${esp32_common_LittleFS.lib_deps}, VL53L0X +build_flags = ${esp32_common_LittleFS.build_flags} + -DFEATURE_ARDUINO_OTA + -DPLUGIN_BUILD_MAX_ESP32 + -DPLUGIN_BUILD_IR_EXTENDED +; TODO: To enable PS-RAM Support needs more build flags than these 2, for now define ESP32_ENABLE_PSRAM is used to en/disable detecting PS-Ram size on Info page +; -DBOARD_HAS_PSRAM // both flags already enabled for Lolin D32 Pro board by PlatformIO +; -mfix-esp32-psram-cache-issue + + +; If you have a board with Ethernet integrated and 16MB Flash, then this configuration could be enabled, it's based on the max_ESP32_16M8M_LittleFS definition +[env:max_ESP32_16M8M_LittleFS_ETH] +extends = max_ESP32_16M8M_LittleFS +build_flags = ${max_ESP32_16M8M_LittleFS.build_flags} -DHAS_ETHERNET + + + + + + + + + From 360a4c80bd8b57efe332585106aa317c143e5797 Mon Sep 17 00:00:00 2001 From: TD-er Date: Tue, 4 Jan 2022 01:20:01 +0100 Subject: [PATCH 028/147] [Cleanup] Make ESP32 PIO envs a bit more readable --- platformio_esp32_envs.ini | 105 ++++++++++++++++---------------------- 1 file changed, 45 insertions(+), 60 deletions(-) diff --git a/platformio_esp32_envs.ini b/platformio_esp32_envs.ini index fd4c4711d0..12fb4ccfd7 100644 --- a/platformio_esp32_envs.ini +++ b/platformio_esp32_envs.ini @@ -7,79 +7,82 @@ [esp32_always] lib_ignore = ESP8266WiFi, ESP8266Ping, ESP8266WebServer, ESP8266HTTPUpdateServer, ESP8266mDNS, ESPEasy_ESP8266Ping, RABurton ESP8266 Mutex, TinyWireM - -[esp32_common] +[esp32_base] extends = common, core_esp32_IDF4_4__2_0_2 lib_deps = td-er/ESPeasySerial @ 2.0.8, adafruit/Adafruit ILI9341 @ ^1.5.6, Adafruit GFX Library, LOLIN_EPD, Adafruit BusIO, VL53L0X @ 1.3.0, SparkFun VL53L1X 4m Laser Distance Sensor @ 1.2.9, td-er/SparkFun MAX1704x Fuel Gauge Arduino Library @ ^1.0.1, ArduinoOTA, ESP32HTTPUpdateServer, FrogmoreScd30, Multi Channel Relay Arduino Library -lib_ignore = ${esp32_always.lib_ignore}, ESP32_ping, IRremoteESP8266, HeatpumpIR, ESP32 BLE Arduino extra_scripts = post:tools/pio/post_esp32.py ${extra_scripts_default.extra_scripts} build_unflags = -Wall -build_flags = ${core_esp32_IDF4_4__2_0_2.build_flags} - ${mqtt_flags.build_flags} - -DCONFIG_FREERTOS_ASSERT_DISABLE - -DCONFIG_LWIP_ESP_GRATUITOUS_ARP - -fno-strict-aliasing - -DCORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_NONE +build_flags = ${core_esp32_IDF4_4__2_0_2.build_flags}, ${mqtt_flags.build_flags}, -DCONFIG_FREERTOS_ASSERT_DISABLE, -DCONFIG_LWIP_ESP_GRATUITOUS_ARP, -fno-strict-aliasing, -DCORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_NONE monitor_filters = esp32_exception_decoder + +[esp32_common] +extends = esp32_base +lib_ignore = ${esp32_always.lib_ignore}, ESP32_ping, IRremoteESP8266, HeatpumpIR, ESP32 BLE Arduino + + [esp32_common_LittleFS] extends = esp32_common -build_flags = ${esp32_common.build_flags} - -DUSE_LITTLEFS +build_flags = ${esp32_common.build_flags}, -DUSE_LITTLEFS lib_deps = ${esp32_common.lib_deps}, LittleFS board_build.filesystem = littlefs +[esp32_max_SPIFFS] +extends = esp32_base +lib_ignore = ${esp32_always.lib_ignore}, ESP32_ping +build_flags = ${esp32_base.build_flags}, -DFEATURE_ARDUINO_OTA, -DPLUGIN_BUILD_MAX_ESP32, -DPLUGIN_BUILD_IR_EXTENDED + + +[esp32_max_LittleFS] +extends = esp32_base +lib_ignore = ${esp32_always.lib_ignore}, ESP32_ping +build_flags = ${esp32_base.build_flags}, -DUSE_LITTLEFS, -DFEATURE_ARDUINO_OTA, -DPLUGIN_BUILD_MAX_ESP32, -DPLUGIN_BUILD_IR_EXTENDED +lib_deps = ${esp32_base.lib_deps}, LittleFS +board_build.filesystem = littlefs + [esp32s2_common] extends = esp32_common -build_flags = ${esp32_common.build_flags} - -DESP32S2 - -DBOARD_HAS_PSRAM - -DESP32_ENABLE_PSRAM - -DFEATURE_ARDUINO_OTA +build_flags = ${esp32_common.build_flags}, -DESP32S2, -DBOARD_HAS_PSRAM, -DESP32_ENABLE_PSRAM, -DFEATURE_ARDUINO_OTA [esp32s2_common_LittleFS] extends = esp32s2_common -build_flags = ${esp32s2_common.build_flags} - -DUSE_LITTLEFS +build_flags = ${esp32s2_common.build_flags}, -DUSE_LITTLEFS lib_deps = ${esp32s2_common.lib_deps}, LittleFS board_build.filesystem = littlefs +[esp32_custom_base] +extends = esp32_common +build_flags = ${esp32_common.build_flags}, -DPLUGIN_BUILD_CUSTOM +extra_scripts = ${esp32_common.extra_scripts} + pre:tools/pio/pre_custom_esp32.py + +[esp32_custom_base_LittleFS] +extends = esp32_common_LittleFS +build_flags = ${esp32_common.build_flags}, -DPLUGIN_BUILD_CUSTOM +extra_scripts = ${esp32_common.extra_scripts} + pre:tools/pio/pre_custom_esp32.py ; Custom: 4096k version -------------------------- [env:custom_ESP32_4M316k] -extends = esp32_common +extends = esp32_custom_base board = esp32_4M -build_flags = ${esp32_common.build_flags} - -DPLUGIN_BUILD_CUSTOM -extra_scripts = ${esp32_common.extra_scripts} - pre:tools/pio/pre_custom_esp32.py [env:custom_ESP32_4M316k_LittleFS] -extends = esp32_common_LittleFS +extends = esp32_custom_base_LittleFS board = esp32_4M -build_flags = ${esp32_common_LittleFS.build_flags} - -DPLUGIN_BUILD_CUSTOM -extra_scripts = ${esp32_common_LittleFS.extra_scripts} - pre:tools/pio/pre_custom_esp32.py [env:custom_ESP32_16M8M_LittleFS] -extends = esp32_common_LittleFS +extends = esp32_custom_base_LittleFS board = esp32_16M8M -build_flags = ${esp32_common_LittleFS.build_flags} - -DPLUGIN_BUILD_CUSTOM -extra_scripts = ${esp32_common_LittleFS.extra_scripts} - pre:tools/pio/pre_custom_esp32.py [env:custom_IR_ESP32_4M316k] -extends = esp32_common +extends = esp32_base board = esp32_4M -build_flags = ${esp32_common.build_flags} - -DPLUGIN_BUILD_CUSTOM - -DPLUGIN_BUILD_IR +build_flags = ${esp32_common.build_flags}, -DPLUGIN_BUILD_CUSTOM, -DPLUGIN_BUILD_IR lib_ignore = ${esp32_always.lib_ignore}, ESP32_ping, HeatpumpIR extra_scripts = ${esp32_common.extra_scripts} pre:tools/pio/pre_custom_esp32.py @@ -318,38 +321,20 @@ build_flags = ${esp32s2_common.build_flags} ; A Lolin D32 PRO with 16MB Flash, allowing 4MB sketch size, and file storage using the default (SPIFFS) filesystem [env:max_ESP32_16M1M] -extends = esp32_common +extends = esp32_max_SPIFFS board = esp32_16M1M -lib_ignore = ${esp32_always.lib_ignore}, ESP32_ping -lib_deps = ${esp32_common.lib_deps}, VL53L0X -build_flags = ${esp32_common.build_flags} - -DFEATURE_ARDUINO_OTA - -DPLUGIN_BUILD_MAX_ESP32 - -DPLUGIN_BUILD_IR_EXTENDED -; TODO: To enable PS-RAM Support needs more build flags than these 2, for now define ESP32_ENABLE_PSRAM is used to en/disable detecting PS-Ram size on Info page -; -DBOARD_HAS_PSRAM // both flags already enabled for Lolin D32 Pro board by PlatformIO -; -mfix-esp32-psram-cache-issue ; A Lolin D32 PRO with 16MB Flash, allowing 4MB sketch size, and file storage using LittleFS filesystem [env:max_ESP32_16M8M_LittleFS] -extends = esp32_common_LittleFS +extends = esp32_max_LittleFS board = esp32_16M8M -lib_ignore = ${esp32_always.lib_ignore}, ESP32_ping -lib_deps = ${esp32_common_LittleFS.lib_deps}, VL53L0X -build_flags = ${esp32_common_LittleFS.build_flags} - -DFEATURE_ARDUINO_OTA - -DPLUGIN_BUILD_MAX_ESP32 - -DPLUGIN_BUILD_IR_EXTENDED -; TODO: To enable PS-RAM Support needs more build flags than these 2, for now define ESP32_ENABLE_PSRAM is used to en/disable detecting PS-Ram size on Info page -; -DBOARD_HAS_PSRAM // both flags already enabled for Lolin D32 Pro board by PlatformIO -; -mfix-esp32-psram-cache-issue - ; If you have a board with Ethernet integrated and 16MB Flash, then this configuration could be enabled, it's based on the max_ESP32_16M8M_LittleFS definition [env:max_ESP32_16M8M_LittleFS_ETH] -extends = max_ESP32_16M8M_LittleFS -build_flags = ${max_ESP32_16M8M_LittleFS.build_flags} -DHAS_ETHERNET +extends = esp32_max_LittleFS +board = esp32_16M8M +build_flags = ${esp32_max_LittleFS.build_flags} -DHAS_ETHERNET From efcfd92a1e0feb38e9894192322d698604f07e1b Mon Sep 17 00:00:00 2001 From: TD-er Date: Tue, 4 Jan 2022 02:08:28 +0100 Subject: [PATCH 029/147] [Cleanup] Split ESP32S2 envs to separate ini file. --- platformio.ini | 13 +++-- platformio_esp32_envs.ini | 94 +------------------------------------ platformio_esp32s2_envs.ini | 83 ++++++++++++++++++++++++++++++++ platformio_esp82xx_base.ini | 4 +- 4 files changed, 92 insertions(+), 102 deletions(-) create mode 100644 platformio_esp32s2_envs.ini diff --git a/platformio.ini b/platformio.ini index 847b6c033c..507868fe22 100644 --- a/platformio.ini +++ b/platformio.ini @@ -18,6 +18,7 @@ extra_configs = platformio_esp82xx_base.ini platformio_esp82xx_envs.ini platformio_esp32_envs.ini + platformio_esp32s2_envs.ini platformio_special_envs.ini ;default_envs = normal_ESP32_4M @@ -68,11 +69,6 @@ build_flags = [mqtt_flags] build_flags = -DMQTT_MAX_PACKET_SIZE=1024 -[extra_scripts_esp8266] -extra_scripts = tools/pio/gzip-firmware.py - ${extra_scripts_default.extra_scripts} - - [extra_scripts_default] extra_scripts = pre:tools/pio/set-ci-defines.py pre:tools/pio/concat_cpp_files.py @@ -80,6 +76,11 @@ extra_scripts = pre:tools/pio/set-ci-defines.py tools/pio/copy_files.py post:tools/pio/remove_concat_cpp_files.py +[extra_scripts_esp8266] +extra_scripts = tools/pio/gzip-firmware.py + ${extra_scripts_default.extra_scripts} + + [common] lib_ldf_mode = deep+ lib_archive = false @@ -88,8 +89,6 @@ upload_speed = 115200 monitor_speed = 115200 ;targets = size, checkprogsize targets = -extra_scripts = pre:tools/pio/pre_default_check.py - ${extra_scripts_esp8266.extra_scripts} src_filter = +<*> -<.git/> -<.svn/> - - - - -<*/Commands/> -<*/ControllerQueue/> -<*/DataStructs/> -<*/DataTypes/> -<*/Globals/> -<*/Helpers/> -<*/PluginStructs/> -<*/WebServer/> diff --git a/platformio_esp32_envs.ini b/platformio_esp32_envs.ini index 12fb4ccfd7..f098f53fa0 100644 --- a/platformio_esp32_envs.ini +++ b/platformio_esp32_envs.ini @@ -42,17 +42,6 @@ lib_deps = ${esp32_base.lib_deps}, LittleFS board_build.filesystem = littlefs -[esp32s2_common] -extends = esp32_common -build_flags = ${esp32_common.build_flags}, -DESP32S2, -DBOARD_HAS_PSRAM, -DESP32_ENABLE_PSRAM, -DFEATURE_ARDUINO_OTA - -[esp32s2_common_LittleFS] -extends = esp32s2_common -build_flags = ${esp32s2_common.build_flags}, -DUSE_LITTLEFS -lib_deps = ${esp32s2_common.lib_deps}, LittleFS -board_build.filesystem = littlefs - - [esp32_custom_base] extends = esp32_common build_flags = ${esp32_common.build_flags}, -DPLUGIN_BUILD_CUSTOM @@ -88,27 +77,6 @@ extra_scripts = ${esp32_common.extra_scripts} pre:tools/pio/pre_custom_esp32.py -; ESP32-S2 -[env:custom_ESP32s2_4M316k] -extends = esp32s2_common -board = esp32s2 -build_flags = ${esp32s2_common.build_flags} - -DPLUGIN_BUILD_CUSTOM -extra_scripts = ${esp32s2_common.extra_scripts} - pre:tools/pio/pre_custom_esp32.py - - -[env:custom_IR_ESP32s2_4M316k] -extends = esp32s2_common -board = esp32s2 -build_flags = ${esp32s2_common.build_flags} - -DPLUGIN_BUILD_CUSTOM - -DPLUGIN_BUILD_IR -lib_ignore = ${esp32_always.lib_ignore}, ESP32_ping, HeatpumpIR -extra_scripts = ${esp32s2_common.extra_scripts} - pre:tools/pio/pre_custom_esp32.py - - ; Normal: 4096k version -------------------------- [env:normal_ESP32_4M316k] @@ -129,12 +97,6 @@ board = esp32_16M8M lib_deps = ${esp32_common_LittleFS.lib_deps}, ServoESP32 -; ESP32-S2 -[env:normal_ESP32s2_4M316k_LittleFS] -extends = esp32s2_common_LittleFS -board = esp32s2 - - ; Test A....E builds -------------------------- @@ -263,61 +225,7 @@ build_flags = ${test_D_ESP32_4M316k.build_flags} -DHAS_ETHERNET -;;; ESP32-s2 *********************************************************** - -[env:normal_ESP32s2_4M316k] -extends = esp32s2_common -board = esp32s2 -lib_deps = ${esp32s2_common.lib_deps}, ServoESP32 - -[env:test_A_ESP32s2_4M316k] -extends = esp32s2_common -board = esp32s2 -build_flags = ${esp32s2_common.build_flags} - -DPLUGIN_SET_TEST_ESP32 - -DTESTING_USE_RTTTL - -[env:test_B_ESP32s2_4M316k] -extends = esp32s2_common -board = esp32s2 -build_flags = ${esp32s2_common.build_flags} - -DPLUGIN_SET_TEST_B_ESP32 - -DTESTING_USE_RTTTL - -[env:test_C_ESP32s2_4M316k] -extends = esp32s2_common -board = esp32s2 -build_flags = ${esp32s2_common.build_flags} - -DPLUGIN_SET_TEST_C_ESP32 - -DTESTING_USE_RTTTL - -[env:test_D_ESP32s2_4M316k] -extends = esp32s2_common -board = esp32s2 -build_flags = ${esp32s2_common.build_flags} - -DPLUGIN_SET_TEST_D_ESP32 - -DTESTING_USE_RTTTL - - -[env:energy_ESP32s2_4M316k] -extends = esp32s2_common -board = esp32s2 -lib_deps = ${esp32s2_common.lib_deps}, ServoESP32 -build_flags = ${esp32s2_common.build_flags} - -D PLUGIN_ENERGY_COLLECTION - -[env:display_ESP32s2_4M316k] -extends = esp32s2_common -board = esp32s2 -lib_deps = ${esp32s2_common.lib_deps}, ServoESP32 -build_flags = ${esp32s2_common.build_flags} - -D PLUGIN_DISPLAY_COLLECTION - - - - - -;;; ESP32 MAX builds 16M flash ------------------------------ +; ESP32 MAX builds 16M flash ------------------------------ ; A Lolin D32 PRO with 16MB Flash, allowing 4MB sketch size, and file storage using the default (SPIFFS) filesystem [env:max_ESP32_16M1M] diff --git a/platformio_esp32s2_envs.ini b/platformio_esp32s2_envs.ini new file mode 100644 index 0000000000..ba3d442e3f --- /dev/null +++ b/platformio_esp32s2_envs.ini @@ -0,0 +1,83 @@ + + + + +[esp32s2_common] +extends = esp32_common +build_flags = ${esp32_common.build_flags}, -DESP32S2, -DBOARD_HAS_PSRAM, -DESP32_ENABLE_PSRAM, -DFEATURE_ARDUINO_OTA + +[esp32s2_common_LittleFS] +extends = esp32s2_common +build_flags = ${esp32s2_common.build_flags}, -DUSE_LITTLEFS +lib_deps = ${esp32s2_common.lib_deps}, LittleFS +board_build.filesystem = littlefs + + +[env:custom_ESP32s2_4M316k] +extends = esp32s2_common +board = esp32s2 +build_flags = ${esp32s2_common.build_flags} + -DPLUGIN_BUILD_CUSTOM +extra_scripts = ${esp32s2_common.extra_scripts} + pre:tools/pio/pre_custom_esp32.py + + +[env:custom_IR_ESP32s2_4M316k] +extends = esp32s2_common +board = esp32s2 +build_flags = ${esp32s2_common.build_flags} + -DPLUGIN_BUILD_CUSTOM + -DPLUGIN_BUILD_IR +lib_ignore = ${esp32_always.lib_ignore}, ESP32_ping, HeatpumpIR +extra_scripts = ${esp32s2_common.extra_scripts} + pre:tools/pio/pre_custom_esp32.py + + + +[env:normal_ESP32s2_4M316k] +extends = esp32s2_common +board = esp32s2 +lib_deps = ${esp32s2_common.lib_deps}, ServoESP32 + +[env:test_A_ESP32s2_4M316k] +extends = esp32s2_common +board = esp32s2 +build_flags = ${esp32s2_common.build_flags} + -DPLUGIN_SET_TEST_ESP32 + -DTESTING_USE_RTTTL + +[env:test_B_ESP32s2_4M316k] +extends = esp32s2_common +board = esp32s2 +build_flags = ${esp32s2_common.build_flags} + -DPLUGIN_SET_TEST_B_ESP32 + -DTESTING_USE_RTTTL + +[env:test_C_ESP32s2_4M316k] +extends = esp32s2_common +board = esp32s2 +build_flags = ${esp32s2_common.build_flags} + -DPLUGIN_SET_TEST_C_ESP32 + -DTESTING_USE_RTTTL + +[env:test_D_ESP32s2_4M316k] +extends = esp32s2_common +board = esp32s2 +build_flags = ${esp32s2_common.build_flags} + -DPLUGIN_SET_TEST_D_ESP32 + -DTESTING_USE_RTTTL + + +[env:energy_ESP32s2_4M316k] +extends = esp32s2_common +board = esp32s2 +lib_deps = ${esp32s2_common.lib_deps}, ServoESP32 +build_flags = ${esp32s2_common.build_flags} + -D PLUGIN_ENERGY_COLLECTION + +[env:display_ESP32s2_4M316k] +extends = esp32s2_common +board = esp32s2 +lib_deps = ${esp32s2_common.lib_deps}, ServoESP32 +build_flags = ${esp32s2_common.build_flags} + -D PLUGIN_DISPLAY_COLLECTION diff --git a/platformio_esp82xx_base.ini b/platformio_esp82xx_base.ini index d2cbe8ac8e..6e6fc99038 100644 --- a/platformio_esp82xx_base.ini +++ b/platformio_esp82xx_base.ini @@ -3,7 +3,6 @@ ; ********************************************************************* - [regular_platform] build_unflags = build_flags = ${core_2_7_4.build_flags} @@ -54,7 +53,8 @@ build_unflags = -DDEBUG_ESP_PORT lib_deps = td-er/ESPeasySerial @ 2.0.8, adafruit/Adafruit ILI9341 @ ^1.5.6, Adafruit GFX Library, LOLIN_EPD, Adafruit BusIO, bblanchon/ArduinoJson @ ^6.17.2, VL53L0X @ 1.3.0, SparkFun VL53L1X 4m Laser Distance Sensor @ 1.2.9, td-er/RABurton ESP8266 Mutex @ ^1.0.2, td-er/SparkFun MAX1704x Fuel Gauge Arduino Library @ ^1.0.1, ESP8266HTTPUpdateServer, FrogmoreScd30, Multi Channel Relay Arduino Library lib_ignore = ${esp82xx_defaults.lib_ignore}, IRremoteESP8266, HeatpumpIR, LittleFS(esp8266), ServoESP32, TinyWireM monitor_filters = esp8266_exception_decoder - +extra_scripts = pre:tools/pio/pre_default_check.py + ${extra_scripts_esp8266.extra_scripts} From 342688d6807a29d77a9f745f667074a3e84692c6 Mon Sep 17 00:00:00 2001 From: TD-er Date: Tue, 4 Jan 2022 11:19:35 +0100 Subject: [PATCH 030/147] [PIO build] Do not use commas in build_flags (Linux build fail) --- platformio_esp32_envs.ini | 55 +++++++++++++++++++++++++++---------- platformio_esp32s2_envs.ini | 9 ++++-- platformio_esp82xx_base.ini | 38 +++++++++++++++++-------- 3 files changed, 73 insertions(+), 29 deletions(-) diff --git a/platformio_esp32_envs.ini b/platformio_esp32_envs.ini index f098f53fa0..9e4926ddbc 100644 --- a/platformio_esp32_envs.ini +++ b/platformio_esp32_envs.ini @@ -13,7 +13,12 @@ lib_deps = td-er/ESPeasySerial @ 2.0.8, adafruit/Adafruit ILI93 extra_scripts = post:tools/pio/post_esp32.py ${extra_scripts_default.extra_scripts} build_unflags = -Wall -build_flags = ${core_esp32_IDF4_4__2_0_2.build_flags}, ${mqtt_flags.build_flags}, -DCONFIG_FREERTOS_ASSERT_DISABLE, -DCONFIG_LWIP_ESP_GRATUITOUS_ARP, -fno-strict-aliasing, -DCORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_NONE +build_flags = ${core_esp32_IDF4_4__2_0_2.build_flags} + ${mqtt_flags.build_flags} + -DCONFIG_FREERTOS_ASSERT_DISABLE + -DCONFIG_LWIP_ESP_GRATUITOUS_ARP + -fno-strict-aliasing + -DCORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_NONE monitor_filters = esp32_exception_decoder @@ -24,33 +29,43 @@ lib_ignore = ${esp32_always.lib_ignore}, ESP32_ping, IRremoteESP8 [esp32_common_LittleFS] extends = esp32_common -build_flags = ${esp32_common.build_flags}, -DUSE_LITTLEFS +build_flags = ${esp32_common.build_flags} + -DUSE_LITTLEFS lib_deps = ${esp32_common.lib_deps}, LittleFS board_build.filesystem = littlefs [esp32_max_SPIFFS] extends = esp32_base lib_ignore = ${esp32_always.lib_ignore}, ESP32_ping -build_flags = ${esp32_base.build_flags}, -DFEATURE_ARDUINO_OTA, -DPLUGIN_BUILD_MAX_ESP32, -DPLUGIN_BUILD_IR_EXTENDED +build_flags = ${esp32_base.build_flags} + -DFEATURE_ARDUINO_OTA + -DPLUGIN_BUILD_MAX_ESP32 + -DPLUGIN_BUILD_IR_EXTENDED [esp32_max_LittleFS] extends = esp32_base lib_ignore = ${esp32_always.lib_ignore}, ESP32_ping -build_flags = ${esp32_base.build_flags}, -DUSE_LITTLEFS, -DFEATURE_ARDUINO_OTA, -DPLUGIN_BUILD_MAX_ESP32, -DPLUGIN_BUILD_IR_EXTENDED +build_flags = ${esp32_base.build_flags} + -DUSE_LITTLEFS + -DFEATURE_ARDUINO_OTA + -DPLUGIN_BUILD_MAX_ESP32 + -DPLUGIN_BUILD_IR_EXTENDED lib_deps = ${esp32_base.lib_deps}, LittleFS board_build.filesystem = littlefs [esp32_custom_base] extends = esp32_common -build_flags = ${esp32_common.build_flags}, -DPLUGIN_BUILD_CUSTOM +build_flags = ${esp32_common.build_flags} + -DPLUGIN_BUILD_CUSTOM extra_scripts = ${esp32_common.extra_scripts} pre:tools/pio/pre_custom_esp32.py [esp32_custom_base_LittleFS] extends = esp32_common_LittleFS -build_flags = ${esp32_common.build_flags}, -DPLUGIN_BUILD_CUSTOM +build_flags = ${esp32_common.build_flags} + -DPLUGIN_BUILD_CUSTOM extra_scripts = ${esp32_common.extra_scripts} pre:tools/pio/pre_custom_esp32.py @@ -71,7 +86,9 @@ board = esp32_16M8M [env:custom_IR_ESP32_4M316k] extends = esp32_base board = esp32_4M -build_flags = ${esp32_common.build_flags}, -DPLUGIN_BUILD_CUSTOM, -DPLUGIN_BUILD_IR +build_flags = ${esp32_common.build_flags} + -DPLUGIN_BUILD_CUSTOM + -DPLUGIN_BUILD_IR lib_ignore = ${esp32_always.lib_ignore}, ESP32_ping, HeatpumpIR extra_scripts = ${esp32_common.extra_scripts} pre:tools/pio/pre_custom_esp32.py @@ -82,7 +99,8 @@ extra_scripts = ${esp32_common.extra_scripts} [env:normal_ESP32_4M316k] extends = esp32_common board = esp32_4M -lib_deps = ${esp32_common.lib_deps}, ServoESP32 +lib_deps = ${esp32_common.lib_deps} + ServoESP32 [env:normal_ESP32_4M316k_LittleFS] @@ -187,40 +205,46 @@ build_flags = ${esp32_common.build_flags} extends = custom_ESP32_4M316k board = esp32_4M platform = ${custom_ESP32_4M316k.platform} -build_flags = ${custom_ESP32_4M316k.build_flags} -DHAS_ETHERNET +build_flags = ${custom_ESP32_4M316k.build_flags} + -DHAS_ETHERNET [env:normal_ESP32_4M316k_ETH] extends = normal_ESP32_4M316k board = esp32_4M platform = ${normal_ESP32_4M316k.platform} -build_flags = ${normal_ESP32_4M316k.build_flags} -DHAS_ETHERNET +build_flags = ${normal_ESP32_4M316k.build_flags} + -DHAS_ETHERNET [env:test_A_ESP32_4M316k_ETH] extends = test_A_ESP32_4M316k board = esp32_4M platform = ${test_A_ESP32_4M316k.platform} -build_flags = ${test_A_ESP32_4M316k.build_flags} -DHAS_ETHERNET +build_flags = ${test_A_ESP32_4M316k.build_flags} + -DHAS_ETHERNET -DTESTING_USE_RTTTL [env:test_B_ESP32_4M316k_ETH] extends = test_B_ESP32_4M316k board = esp32_4M platform = ${test_B_ESP32_4M316k.platform} -build_flags = ${test_B_ESP32_4M316k.build_flags} -DHAS_ETHERNET +build_flags = ${test_B_ESP32_4M316k.build_flags} + -DHAS_ETHERNET -DTESTING_USE_RTTTL [env:test_C_ESP32_4M316k_ETH] extends = test_C_ESP32_4M316k board = esp32_4M platform = ${test_C_ESP32_4M316k.platform} -build_flags = ${test_C_ESP32_4M316k.build_flags} -DHAS_ETHERNET +build_flags = ${test_C_ESP32_4M316k.build_flags} + -DHAS_ETHERNET -DTESTING_USE_RTTTL [env:test_D_ESP32_4M316k_ETH] extends = test_D_ESP32_4M316k board = esp32_4M platform = ${test_D_ESP32_4M316k.platform} -build_flags = ${test_D_ESP32_4M316k.build_flags} -DHAS_ETHERNET +build_flags = ${test_D_ESP32_4M316k.build_flags} + -DHAS_ETHERNET -DTESTING_USE_RTTTL @@ -242,7 +266,8 @@ board = esp32_16M8M [env:max_ESP32_16M8M_LittleFS_ETH] extends = esp32_max_LittleFS board = esp32_16M8M -build_flags = ${esp32_max_LittleFS.build_flags} -DHAS_ETHERNET +build_flags = ${esp32_max_LittleFS.build_flags} + -DHAS_ETHERNET diff --git a/platformio_esp32s2_envs.ini b/platformio_esp32s2_envs.ini index ba3d442e3f..cb0095f282 100644 --- a/platformio_esp32s2_envs.ini +++ b/platformio_esp32s2_envs.ini @@ -4,11 +4,16 @@ [esp32s2_common] extends = esp32_common -build_flags = ${esp32_common.build_flags}, -DESP32S2, -DBOARD_HAS_PSRAM, -DESP32_ENABLE_PSRAM, -DFEATURE_ARDUINO_OTA +build_flags = ${esp32_common.build_flags} + -DESP32S2 + -DBOARD_HAS_PSRAM + -DESP32_ENABLE_PSRAM + -DFEATURE_ARDUINO_OTA [esp32s2_common_LittleFS] extends = esp32s2_common -build_flags = ${esp32s2_common.build_flags}, -DUSE_LITTLEFS +build_flags = ${esp32s2_common.build_flags} + -DUSE_LITTLEFS lib_deps = ${esp32s2_common.lib_deps}, LittleFS board_build.filesystem = littlefs diff --git a/platformio_esp82xx_base.ini b/platformio_esp82xx_base.ini index 6e6fc99038..3303885dad 100644 --- a/platformio_esp82xx_base.ini +++ b/platformio_esp82xx_base.ini @@ -48,7 +48,9 @@ lib_ignore = ${no_ir.lib_ignore}, ${no_sd.lib_ignore}, ${no_littl [esp82xx_common] extends = common -build_flags = ${debug_flags.build_flags} ${mqtt_flags.build_flags} -DHTTPCLIENT_1_1_COMPATIBLE=0 +build_flags = ${debug_flags.build_flags} + ${mqtt_flags.build_flags} + -DHTTPCLIENT_1_1_COMPATIBLE=0 build_unflags = -DDEBUG_ESP_PORT lib_deps = td-er/ESPeasySerial @ 2.0.8, adafruit/Adafruit ILI9341 @ ^1.5.6, Adafruit GFX Library, LOLIN_EPD, Adafruit BusIO, bblanchon/ArduinoJson @ ^6.17.2, VL53L0X @ 1.3.0, SparkFun VL53L1X 4m Laser Distance Sensor @ 1.2.9, td-er/RABurton ESP8266 Mutex @ ^1.0.2, td-er/SparkFun MAX1704x Fuel Gauge Arduino Library @ ^1.0.1, ESP8266HTTPUpdateServer, FrogmoreScd30, Multi Channel Relay Arduino Library lib_ignore = ${esp82xx_defaults.lib_ignore}, IRremoteESP8266, HeatpumpIR, LittleFS(esp8266), ServoESP32, TinyWireM @@ -90,19 +92,22 @@ lib_ignore = ${beta_platform.lib_ignore} SD(esp8266), SDFS [testing] platform = ${regular_platform.platform} platform_packages = ${regular_platform.platform_packages} -build_flags = ${regular_platform.build_flags} -DPLUGIN_BUILD_TESTING +build_flags = ${regular_platform.build_flags} + -DPLUGIN_BUILD_TESTING lib_ignore = ${regular_platform.lib_ignore} [testing_alt_wifi] platform = ${regular_platform_alt_wifi.platform} platform_packages = ${regular_platform_alt_wifi.platform_packages} -build_flags = ${regular_platform_alt_wifi.build_flags} -DPLUGIN_BUILD_TESTING +build_flags = ${regular_platform_alt_wifi.build_flags} + -DPLUGIN_BUILD_TESTING lib_ignore = ${regular_platform_alt_wifi.lib_ignore} [testing_beta] platform = ${beta_platform.platform} platform_packages = ${beta_platform.platform_packages} -build_flags = ${beta_platform.build_flags} -DPLUGIN_BUILD_TESTING +build_flags = ${beta_platform.build_flags} + -DPLUGIN_BUILD_TESTING lib_ignore = ${beta_platform.lib_ignore} @@ -125,23 +130,28 @@ lib_ignore = ESP32_ping, ESP32WebServer, ServoESP32, ESP32HTTPUpd [minimal_ir] extends = ir -build_flags = ${ir.build_flags} -DPLUGIN_BUILD_MINIMAL_IR +build_flags = ${ir.build_flags} + -DPLUGIN_BUILD_MINIMAL_IR [minimal_ir_extended] extends = ir -build_flags = ${ir.build_flags} -DPLUGIN_BUILD_MINIMAL_IRext +build_flags = ${ir.build_flags} + -DPLUGIN_BUILD_MINIMAL_IRext [normal_ir] extends = ir -build_flags = ${ir.build_flags} -DPLUGIN_BUILD_NORMAL_IR +build_flags = ${ir.build_flags} + -DPLUGIN_BUILD_NORMAL_IR [normal_ir_extended] extends = ir -build_flags = ${ir.build_flags} -DPLUGIN_BUILD_IR_EXTENDED +build_flags = ${ir.build_flags} + -DPLUGIN_BUILD_IR_EXTENDED [normal_ir_extended_no_rx] extends = ir -build_flags = ${ir.build_flags} -DPLUGIN_BUILD_IR_EXTENDED_NO_RX +build_flags = ${ir.build_flags} + -DPLUGIN_BUILD_IR_EXTENDED_NO_RX @@ -163,7 +173,8 @@ board = esp8266_1M128k [esp8285_1M] extends = esp82xx_1M board = esp8285_1M128k -build_flags = ${esp8266_1M.build_flags} -DESP8285 +build_flags = ${esp8266_1M.build_flags} + -DESP8285 ;;; Minimal *********************************************************** @@ -171,7 +182,9 @@ build_flags = ${esp8266_1M.build_flags} -DESP8285 ; ********************************************************************* [esp82xx_1M_OTA] extends = esp82xx_1M -build_flags = ${esp82xx_1M.build_flags} -DPLUGIN_BUILD_MINIMAL_OTA -DDISABLE_SC16IS752_Serial +build_flags = ${esp82xx_1M.build_flags} + -DPLUGIN_BUILD_MINIMAL_OTA + -DDISABLE_SC16IS752_Serial [esp8266_1M_OTA] @@ -181,7 +194,8 @@ board = esp8266_1M128k_OTA [esp8285_1M_OTA] extends = esp82xx_1M_OTA board = esp8285_1M128k_OTA -build_flags = ${esp82xx_1M_OTA.build_flags} -DESP8285 +build_flags = ${esp82xx_1M_OTA.build_flags} + -DESP8285 ;;; 2MB flash nodes ************************************************** From dca0c437549dc9a57a9e50e9f260ecbdadced4de Mon Sep 17 00:00:00 2001 From: TD-er Date: Tue, 4 Jan 2022 11:58:22 +0100 Subject: [PATCH 031/147] [PIO] fix incorrect esp32 IRExt envs --- platformio_esp32_envs.ini | 56 +++++++++++++++------------------------ 1 file changed, 21 insertions(+), 35 deletions(-) diff --git a/platformio_esp32_envs.ini b/platformio_esp32_envs.ini index 9e4926ddbc..c369b29280 100644 --- a/platformio_esp32_envs.ini +++ b/platformio_esp32_envs.ini @@ -54,6 +54,16 @@ build_flags = ${esp32_base.build_flags} lib_deps = ${esp32_base.lib_deps}, LittleFS board_build.filesystem = littlefs +[esp32_IRExt] +extends = esp32_base +lib_ignore = AS_BH1750, ${esp32_always.lib_ignore}, ESP32_ping +build_flags = ${esp32_base.build_flags} + -DFEATURE_ARDUINO_OTA + -DPLUGIN_BUILD_NORMAL_IRext + -DTESTING_USE_RTTTL +lib_deps = ${esp32_base.lib_deps}, LittleFS + + [esp32_custom_base] extends = esp32_common @@ -116,8 +126,6 @@ lib_deps = ${esp32_common_LittleFS.lib_deps}, ServoESP32 - -; Test A....E builds -------------------------- [env:test_A_ESP32_4M316k] extends = esp32_common board = esp32_4M @@ -152,36 +160,26 @@ build_flags = ${esp32_common.build_flags} [env:test_A_ESP32_IRExt_4M316k] -extends = test_A_ESP32_4M316k +extends = esp32_IRExt board = esp32_4M -lib_ignore = AS_BH1750, ${esp32_always.lib_ignore}, ESP32_ping -build_flags = ${esp32_common.build_flags} - -DPLUGIN_BUILD_NORMAL_IRext - -DTESTING_USE_RTTTL [env:test_B_ESP32_IRExt_4M316k] -extends = test_B_ESP32_4M316k +extends = esp32_IRExt board = esp32_4M -lib_ignore = AS_BH1750, ${esp32_always.lib_ignore}, ESP32_ping -build_flags = ${esp32_common.build_flags} - -DPLUGIN_BUILD_NORMAL_IRext - -DTESTING_USE_RTTTL +build_flags = ${esp32_IRExt.build_flags} + -DPLUGIN_SET_TEST_B_ESP32 [env:test_C_ESP32_IRExt_4M316k] -extends = test_C_ESP32_4M316k +extends = esp32_IRExt board = esp32_4M -lib_ignore = AS_BH1750, ${esp32_always.lib_ignore}, ESP32_ping -build_flags = ${esp32_common.build_flags} - -DPLUGIN_BUILD_NORMAL_IRext - -DTESTING_USE_RTTTL +build_flags = ${esp32_IRExt.build_flags} + -DPLUGIN_SET_TEST_C_ESP32 [env:test_D_ESP32_IRExt_4M316k] -extends = test_D_ESP32_4M316k +extends = esp32_IRExt board = esp32_4M -lib_ignore = AS_BH1750, ${esp32_always.lib_ignore}, ESP32_ping -build_flags = ${esp32_common.build_flags} - -DPLUGIN_BUILD_NORMAL_IRext - -DTESTING_USE_RTTTL +build_flags = ${esp32_IRExt.build_flags} + -DPLUGIN_SET_TEST_D_ESP32 [env:energy_ESP32_4M316k] extends = esp32_common @@ -189,7 +187,7 @@ board = esp32_4M lib_deps = ${esp32_common.lib_deps}, ServoESP32 build_flags = ${esp32_common.build_flags} -DFEATURE_ARDUINO_OTA - -D PLUGIN_ENERGY_COLLECTION + -DPLUGIN_ENERGY_COLLECTION [env:display_ESP32_4M316k] extends = esp32_common @@ -203,46 +201,34 @@ build_flags = ${esp32_common.build_flags} ; ETH builds -------------------------- [env:custom_ESP32_4M316k_ETH] extends = custom_ESP32_4M316k -board = esp32_4M -platform = ${custom_ESP32_4M316k.platform} build_flags = ${custom_ESP32_4M316k.build_flags} -DHAS_ETHERNET [env:normal_ESP32_4M316k_ETH] extends = normal_ESP32_4M316k -board = esp32_4M -platform = ${normal_ESP32_4M316k.platform} build_flags = ${normal_ESP32_4M316k.build_flags} -DHAS_ETHERNET [env:test_A_ESP32_4M316k_ETH] extends = test_A_ESP32_4M316k -board = esp32_4M -platform = ${test_A_ESP32_4M316k.platform} build_flags = ${test_A_ESP32_4M316k.build_flags} -DHAS_ETHERNET -DTESTING_USE_RTTTL [env:test_B_ESP32_4M316k_ETH] extends = test_B_ESP32_4M316k -board = esp32_4M -platform = ${test_B_ESP32_4M316k.platform} build_flags = ${test_B_ESP32_4M316k.build_flags} -DHAS_ETHERNET -DTESTING_USE_RTTTL [env:test_C_ESP32_4M316k_ETH] extends = test_C_ESP32_4M316k -board = esp32_4M -platform = ${test_C_ESP32_4M316k.platform} build_flags = ${test_C_ESP32_4M316k.build_flags} -DHAS_ETHERNET -DTESTING_USE_RTTTL [env:test_D_ESP32_4M316k_ETH] extends = test_D_ESP32_4M316k -board = esp32_4M -platform = ${test_D_ESP32_4M316k.platform} build_flags = ${test_D_ESP32_4M316k.build_flags} -DHAS_ETHERNET -DTESTING_USE_RTTTL From ac36ff2411ad37e121e41da95b1624db0943e4f8 Mon Sep 17 00:00:00 2001 From: TD-er Date: Tue, 4 Jan 2022 12:18:40 +0100 Subject: [PATCH 032/147] [PIO] add labels to envs which wil later adjusted in extends --- platformio_esp32_envs.ini | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/platformio_esp32_envs.ini b/platformio_esp32_envs.ini index c369b29280..81a448cfe2 100644 --- a/platformio_esp32_envs.ini +++ b/platformio_esp32_envs.ini @@ -25,6 +25,9 @@ monitor_filters = esp32_exception_decoder [esp32_common] extends = esp32_base lib_ignore = ${esp32_always.lib_ignore}, ESP32_ping, IRremoteESP8266, HeatpumpIR, ESP32 BLE Arduino +build_flags = ${esp32_base.build_flags} +extra_scripts = ${esp32_base.extra_scripts} +lib_deps = ${esp32_base.lib_deps} [esp32_common_LittleFS] @@ -80,7 +83,6 @@ extra_scripts = ${esp32_common.extra_scripts} pre:tools/pio/pre_custom_esp32.py -; Custom: 4096k version -------------------------- [env:custom_ESP32_4M316k] extends = esp32_custom_base board = esp32_4M @@ -104,8 +106,6 @@ extra_scripts = ${esp32_common.extra_scripts} pre:tools/pio/pre_custom_esp32.py - -; Normal: 4096k version -------------------------- [env:normal_ESP32_4M316k] extends = esp32_common board = esp32_4M @@ -197,8 +197,6 @@ build_flags = ${esp32_common.build_flags} -DFEATURE_ARDUINO_OTA - -; ETH builds -------------------------- [env:custom_ESP32_4M316k_ETH] extends = custom_ESP32_4M316k build_flags = ${custom_ESP32_4M316k.build_flags} From 6dfc163efdaeaf5f8b8dc748b0689eb0d2ad5569 Mon Sep 17 00:00:00 2001 From: TD-er Date: Tue, 4 Jan 2022 13:17:55 +0100 Subject: [PATCH 033/147] [PIO] Use shorter extends chain for ESP32 max builds --- platformio_esp32_envs.ini | 53 +++++++++++++++++---------------------- 1 file changed, 23 insertions(+), 30 deletions(-) diff --git a/platformio_esp32_envs.ini b/platformio_esp32_envs.ini index 81a448cfe2..7ecd618d22 100644 --- a/platformio_esp32_envs.ini +++ b/platformio_esp32_envs.ini @@ -34,28 +34,10 @@ lib_deps = ${esp32_base.lib_deps} extends = esp32_common build_flags = ${esp32_common.build_flags} -DUSE_LITTLEFS +extra_scripts = ${esp32_common.extra_scripts} lib_deps = ${esp32_common.lib_deps}, LittleFS board_build.filesystem = littlefs -[esp32_max_SPIFFS] -extends = esp32_base -lib_ignore = ${esp32_always.lib_ignore}, ESP32_ping -build_flags = ${esp32_base.build_flags} - -DFEATURE_ARDUINO_OTA - -DPLUGIN_BUILD_MAX_ESP32 - -DPLUGIN_BUILD_IR_EXTENDED - - -[esp32_max_LittleFS] -extends = esp32_base -lib_ignore = ${esp32_always.lib_ignore}, ESP32_ping -build_flags = ${esp32_base.build_flags} - -DUSE_LITTLEFS - -DFEATURE_ARDUINO_OTA - -DPLUGIN_BUILD_MAX_ESP32 - -DPLUGIN_BUILD_IR_EXTENDED -lib_deps = ${esp32_base.lib_deps}, LittleFS -board_build.filesystem = littlefs [esp32_IRExt] extends = esp32_base @@ -77,9 +59,9 @@ extra_scripts = ${esp32_common.extra_scripts} [esp32_custom_base_LittleFS] extends = esp32_common_LittleFS -build_flags = ${esp32_common.build_flags} +build_flags = ${esp32_common_LittleFS.build_flags} -DPLUGIN_BUILD_CUSTOM -extra_scripts = ${esp32_common.extra_scripts} +extra_scripts = ${esp32_common_LittleFS.extra_scripts} pre:tools/pio/pre_custom_esp32.py @@ -98,19 +80,18 @@ board = esp32_16M8M [env:custom_IR_ESP32_4M316k] extends = esp32_base board = esp32_4M -build_flags = ${esp32_common.build_flags} +build_flags = ${esp32_base.build_flags} -DPLUGIN_BUILD_CUSTOM -DPLUGIN_BUILD_IR lib_ignore = ${esp32_always.lib_ignore}, ESP32_ping, HeatpumpIR -extra_scripts = ${esp32_common.extra_scripts} +extra_scripts = ${esp32_base.extra_scripts} pre:tools/pio/pre_custom_esp32.py [env:normal_ESP32_4M316k] extends = esp32_common board = esp32_4M -lib_deps = ${esp32_common.lib_deps} - ServoESP32 +lib_deps = ${esp32_common.lib_deps}, ServoESP32 [env:normal_ESP32_4M316k_LittleFS] @@ -237,20 +218,32 @@ build_flags = ${test_D_ESP32_4M316k.build_flags} ; A Lolin D32 PRO with 16MB Flash, allowing 4MB sketch size, and file storage using the default (SPIFFS) filesystem [env:max_ESP32_16M1M] -extends = esp32_max_SPIFFS +extends = esp32_base board = esp32_16M1M +lib_ignore = ${esp32_always.lib_ignore}, ESP32_ping +build_flags = ${esp32_base.build_flags} + -DFEATURE_ARDUINO_OTA + -DPLUGIN_BUILD_MAX_ESP32 + -DPLUGIN_BUILD_IR_EXTENDED ; A Lolin D32 PRO with 16MB Flash, allowing 4MB sketch size, and file storage using LittleFS filesystem [env:max_ESP32_16M8M_LittleFS] -extends = esp32_max_LittleFS +extends = esp32_base board = esp32_16M8M +lib_ignore = ${esp32_always.lib_ignore}, ESP32_ping +build_flags = ${esp32_base.build_flags} + -DUSE_LITTLEFS + -DFEATURE_ARDUINO_OTA + -DPLUGIN_BUILD_MAX_ESP32 + -DPLUGIN_BUILD_IR_EXTENDED +lib_deps = ${esp32_base.lib_deps}, LittleFS +board_build.filesystem = littlefs ; If you have a board with Ethernet integrated and 16MB Flash, then this configuration could be enabled, it's based on the max_ESP32_16M8M_LittleFS definition [env:max_ESP32_16M8M_LittleFS_ETH] -extends = esp32_max_LittleFS -board = esp32_16M8M -build_flags = ${esp32_max_LittleFS.build_flags} +extends = max_ESP32_16M8M_LittleFS +build_flags = ${max_ESP32_16M8M_LittleFS.build_flags} -DHAS_ETHERNET From 136dcd0158407e7cc83f068c481d2c9db5e7cba5 Mon Sep 17 00:00:00 2001 From: TD-er Date: Tue, 4 Jan 2022 15:40:22 +0100 Subject: [PATCH 034/147] [PIO] Apparently when referring an env, the 'env:' prefix is needed Or at least in Linux, not in Windows.... --- platformio_esp32_envs.ini | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/platformio_esp32_envs.ini b/platformio_esp32_envs.ini index 7ecd618d22..caa5d37b5e 100644 --- a/platformio_esp32_envs.ini +++ b/platformio_esp32_envs.ini @@ -179,36 +179,36 @@ build_flags = ${esp32_common.build_flags} [env:custom_ESP32_4M316k_ETH] -extends = custom_ESP32_4M316k -build_flags = ${custom_ESP32_4M316k.build_flags} +extends = env:custom_ESP32_4M316k +build_flags = ${env:custom_ESP32_4M316k.build_flags} -DHAS_ETHERNET [env:normal_ESP32_4M316k_ETH] -extends = normal_ESP32_4M316k -build_flags = ${normal_ESP32_4M316k.build_flags} +extends = env:normal_ESP32_4M316k +build_flags = ${env:normal_ESP32_4M316k.build_flags} -DHAS_ETHERNET [env:test_A_ESP32_4M316k_ETH] -extends = test_A_ESP32_4M316k -build_flags = ${test_A_ESP32_4M316k.build_flags} +extends = env:test_A_ESP32_4M316k +build_flags = ${env:test_A_ESP32_4M316k.build_flags} -DHAS_ETHERNET -DTESTING_USE_RTTTL [env:test_B_ESP32_4M316k_ETH] -extends = test_B_ESP32_4M316k -build_flags = ${test_B_ESP32_4M316k.build_flags} +extends = env:test_B_ESP32_4M316k +build_flags = ${env:test_B_ESP32_4M316k.build_flags} -DHAS_ETHERNET -DTESTING_USE_RTTTL [env:test_C_ESP32_4M316k_ETH] -extends = test_C_ESP32_4M316k -build_flags = ${test_C_ESP32_4M316k.build_flags} +extends = env:test_C_ESP32_4M316k +build_flags = ${env:test_C_ESP32_4M316k.build_flags} -DHAS_ETHERNET -DTESTING_USE_RTTTL [env:test_D_ESP32_4M316k_ETH] -extends = test_D_ESP32_4M316k -build_flags = ${test_D_ESP32_4M316k.build_flags} +extends = env:test_D_ESP32_4M316k +build_flags = ${env:test_D_ESP32_4M316k.build_flags} -DHAS_ETHERNET -DTESTING_USE_RTTTL @@ -242,8 +242,8 @@ board_build.filesystem = littlefs ; If you have a board with Ethernet integrated and 16MB Flash, then this configuration could be enabled, it's based on the max_ESP32_16M8M_LittleFS definition [env:max_ESP32_16M8M_LittleFS_ETH] -extends = max_ESP32_16M8M_LittleFS -build_flags = ${max_ESP32_16M8M_LittleFS.build_flags} +extends = env:max_ESP32_16M8M_LittleFS +build_flags = ${env:max_ESP32_16M8M_LittleFS.build_flags} -DHAS_ETHERNET From 2371ea1f18990e65ff8b66cc9d498d06c14bab9f Mon Sep 17 00:00:00 2001 From: TD-er Date: Tue, 4 Jan 2022 15:58:39 +0100 Subject: [PATCH 035/147] [P096 eink] Fix failing build --- src/_P096_eInk.ino | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/_P096_eInk.ino b/src/_P096_eInk.ino index 54d9013a6f..1599c2e670 100644 --- a/src/_P096_eInk.ino +++ b/src/_P096_eInk.ino @@ -111,8 +111,8 @@ Examples: #define EPD_BUSY -1 // can set to -1 to not use a pin (will wait a fixed delay) #else //for D1 Mini with shield connection - #define EPD_CS D0 - #define EPD_DC D8 + #define EPD_CS 16 // D0 + #define EPD_DC 15 // D8 #define EPD_RST -1 // can set to -1 and share with microcontroller Reset! #define EPD_BUSY -1 // can set to -1 to not use a pin (will wait a fixed delay) #endif From 64c7ae8ada0b1003e71321d2f229256b151dce25 Mon Sep 17 00:00:00 2001 From: TD-er Date: Tue, 4 Jan 2022 19:18:13 +0100 Subject: [PATCH 036/147] [ESP32 LittleFS] Add old LittleFS library to lib_ignore For some reason it failed to build on Linux, but completes on Windows. Still the builds will not work when flashed (max builds) --- platformio_esp32_envs.ini | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/platformio_esp32_envs.ini b/platformio_esp32_envs.ini index caa5d37b5e..08b4f33b7f 100644 --- a/platformio_esp32_envs.ini +++ b/platformio_esp32_envs.ini @@ -5,7 +5,7 @@ ; ***************************************************************************************; [esp32_always] -lib_ignore = ESP8266WiFi, ESP8266Ping, ESP8266WebServer, ESP8266HTTPUpdateServer, ESP8266mDNS, ESPEasy_ESP8266Ping, RABurton ESP8266 Mutex, TinyWireM +lib_ignore = ESP8266WiFi, ESP8266Ping, ESP8266WebServer, ESP8266HTTPUpdateServer, ESP8266mDNS, ESPEasy_ESP8266Ping, RABurton ESP8266 Mutex, TinyWireM, LittleFS_esp32 [esp32_base] extends = common, core_esp32_IDF4_4__2_0_2 @@ -237,6 +237,7 @@ build_flags = ${esp32_base.build_flags} -DFEATURE_ARDUINO_OTA -DPLUGIN_BUILD_MAX_ESP32 -DPLUGIN_BUILD_IR_EXTENDED +extra_scripts = ${esp32_base.extra_scripts} lib_deps = ${esp32_base.lib_deps}, LittleFS board_build.filesystem = littlefs From 38cd32e0d7e1ad102bd94e1a7b0407f547844ad3 Mon Sep 17 00:00:00 2001 From: TD-er Date: Wed, 5 Jan 2022 01:39:34 +0100 Subject: [PATCH 037/147] [P118] Disable P118 ITHO for now as it crashes on IDF4.4 --- src/_P118_Itho.ino | 9 +++++---- src/src/CustomBuild/ESPEasyLimits.h | 20 ++++++++------------ 2 files changed, 13 insertions(+), 16 deletions(-) diff --git a/src/_P118_Itho.ino b/src/_P118_Itho.ino index f29d9bf24c..baf7ca2c2a 100644 --- a/src/_P118_Itho.ino +++ b/src/_P118_Itho.ino @@ -56,13 +56,15 @@ // See https://gathering.tweakers.net/forum/list_messages/1690945 for more information // code/idea was inspired by first release of code from 'Thinkpad' -#ifdef USES_P118 +#include "_Plugin_Helper.h" + +#ifdef USES_P118_disabled #include #include "IthoCC1101.h" #include "IthoPacket.h" -#include "_Plugin_Helper.h" +void PLUGIN_118_ITHOinterrupt() ICACHE_RAM_ATTR; //This extra settings struct is needed because the default settingsstruct doesn't support strings struct PLUGIN__ExtraSettingsStruct { char ID1[9]; @@ -400,8 +402,7 @@ boolean Plugin_118(byte function, struct EventStruct *event, String &string) return success; } -ICACHE_RAM_ATTR void PLUGIN_118_ITHOinterrupt() -{ +void PLUGIN_118_ITHOinterrupt() { PLUGIN_118_Int = true; //flag //PLUGIN_118_Int_time = millis(); //used to register time since interrupt, to make sure we don't read within 10 ms as the RX buffer needs some time to get ready. Update: Disabled as it appear not necessary } diff --git a/src/src/CustomBuild/ESPEasyLimits.h b/src/src/CustomBuild/ESPEasyLimits.h index fe6dcb2a5f..524da1c32f 100644 --- a/src/src/CustomBuild/ESPEasyLimits.h +++ b/src/src/CustomBuild/ESPEasyLimits.h @@ -72,19 +72,15 @@ // *********************************************************************** #ifndef DEVICES_MAX // TODO TD-er: This should be set automatically by counting the number of included plugins. - #if defined(PLUGIN_BUILD_TESTING) || defined(PLUGIN_BUILD_DEV) - # define DEVICES_MAX 95 - #else // if defined(PLUGIN_BUILD_TESTING) || defined(PLUGIN_BUILD_DEV) - # ifdef ESP32 - # ifdef PLUGIN_BUILD_MAX_ESP32 - # define DEVICES_MAX 125 - # else // ifdef PLUGIN_BUILD_MAX_ESP32 - # define DEVICES_MAX 100 - # endif // ifdef PLUGIN_BUILD_MAX_ESP32 - # else // ifdef ESP32 + # ifdef ESP32 + # define DEVICES_MAX 130 + #else + #if defined(PLUGIN_BUILD_TESTING) || defined(PLUGIN_BUILD_DEV) + # define DEVICES_MAX 95 + # else # define DEVICES_MAX 60 - # endif // ifdef ESP32 - #endif // if defined(PLUGIN_BUILD_TESTING) || defined(PLUGIN_BUILD_DEV) + # endif + #endif #endif #ifndef PLUGIN_MAX From e74ef187f53bd4a763c1fafdea58b97d6db4c0b8 Mon Sep 17 00:00:00 2001 From: TD-er Date: Wed, 5 Jan 2022 17:34:05 +0100 Subject: [PATCH 038/147] [ESP32 Factory] Fix boot loop on 16M flash nodes using factory image See discussion: https://github.com/letscontrolit/ESPEasy/pull/3845#issuecomment-1005656587 --- tools/pio/post_esp32.py | 76 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 73 insertions(+), 3 deletions(-) diff --git a/tools/pio/post_esp32.py b/tools/pio/post_esp32.py index c0b5ce6dfc..68a040e28e 100644 --- a/tools/pio/post_esp32.py +++ b/tools/pio/post_esp32.py @@ -1,23 +1,93 @@ Import("env") +import os + +def decode_flash_size_speed(byte_value): + speed = byte_value[0] & 0x0f + size = int((byte_value[0] & 0xf0) / 16) + + # As defined in esp_image_spi_freq_t + spi_speed = { + 0: "40", + 1: "26", + 2: "20", + 0xf: "80" + } + speed_mhz = spi_speed.get(speed, "??") + size_mb = str(1<\partitions.bin + # - 0xe000 | ~\.platformio\packages\framework-arduinoespressif32\tools\partitions\boot_app0.bin + # - 0x10000 | ~\ESPEasy\.pio\build\/.bin + new_file_name = env.subst("$BUILD_DIR/${PROGNAME}-factory.bin") sections = env.subst(env.get('FLASH_EXTRA_IMAGES')) new_file = open(new_file_name,"wb") + new_file.truncate() # Make sure no left over data is present from a previous build + + firmware_name = env.subst("$BUILD_DIR/${PROGNAME}.bin") + + # fill the file with 0xFF binary data to reflect 'erased and not yet written' data. + # Make sure to use exactly the size that normally would be erased + new_file.seek(0) + total_size = sketch_partition_start + os.path.getsize(firmware_name) + total_size = total_size - (total_size % flash_page_size) + flash_page_size + + new_file.write(bytes([0xff] * total_size)) + print(" {} | {}".format("Offset", "File")) for section in sections: sect_adr,sect_file = section.split(" ",1) print(" - {} | {}".format(sect_adr,sect_file)) source = open(sect_file,"rb") new_file.seek(int(sect_adr,0)-offset) - new_file.write(source.read()); + new_file.write(source.read()) + if "bootloader" in sect_file: + # Need to patch the bootloader to match the flash size + flash_size = env.BoardConfig().get("upload.flash_size") + + # See esp_image_header_t for struct definition + spi_speed_size_byte_offset = 3 + source.seek(spi_speed_size_byte_offset) + spi_speed_size_byte = source.read(1) + new_spi_speed_size_byte = spi_speed_size_byte[0] & 0x0f + + # As defined in esp_image_flash_size_t + spi_size = { + "1MB": 0x00, + "2MB": 0x10, + "4MB": 0x20, + "8MB": 0x30, + "16MB": 0x40 + } + new_spi_speed_size_byte |= spi_size.get(flash_size, spi_speed_size_byte[0] & 0xf0) + + if new_spi_speed_size_byte != spi_speed_size_byte: + new_byte = bytes([new_spi_speed_size_byte]) + print(" | Patch flash size in bootloader: {} -> {}".format(decode_flash_size_speed(spi_speed_size_byte), decode_flash_size_speed(new_byte))) + new_file.seek(int(sect_adr,0)-offset + spi_speed_size_byte_offset) + new_file.write(new_byte) + source.close() + - firmware_name = env.subst("$BUILD_DIR/${PROGNAME}.bin") - firmware_start = 0x10000-offset + firmware_start = sketch_partition_start-offset firmware = open(firmware_name,"rb") print(" - {} | {}".format(hex(firmware_start),firmware_name)) new_file.seek(firmware_start) From e0afe5b05b7c7f166e9b1c0cc4e1461217ca6b42 Mon Sep 17 00:00:00 2001 From: TD-er Date: Thu, 6 Jan 2022 16:56:36 +0100 Subject: [PATCH 039/147] [ESP32 factory] Use esptool.py to generate and patch combined binary --- tools/pio/post_esp32.py | 138 +++++++++++++++------------------------- 1 file changed, 52 insertions(+), 86 deletions(-) diff --git a/tools/pio/post_esp32.py b/tools/pio/post_esp32.py index 68a040e28e..5ca8d18e35 100644 --- a/tools/pio/post_esp32.py +++ b/tools/pio/post_esp32.py @@ -1,98 +1,64 @@ -Import("env") -import os - -def decode_flash_size_speed(byte_value): - speed = byte_value[0] & 0x0f - size = int((byte_value[0] & 0xf0) / 16) - - # As defined in esp_image_spi_freq_t - spi_speed = { - 0: "40", - 1: "26", - 2: "20", - 0xf: "80" - } - speed_mhz = spi_speed.get(speed, "??") - size_mb = str(1<4MB flash +# Thanks @jesserockz (esphome) for adapting to use esptool.py with merge_bin +# +# Typical layout of the generated file: +# Offset | File +# - 0x1000 | ~\.platformio\packages\framework-arduinoespressif32\tools\sdk\esp32\bin\bootloader_dout_40m.bin +# - 0x8000 | ~\ESPEasy\.pio\build\\partitions.bin +# - 0xe000 | ~\.platformio\packages\framework-arduinoespressif32\tools\partitions\boot_app0.bin +# - 0x10000 | ~\ESPEasy\.pio\build\/.bin + +import subprocess + +# pylint: disable=E0602 +Import("env") # noqa + +import esptool + + +def esp32_create_combined_bin(source, target, env): + print("Generating combined binary for serial flashing") # The offset from begin of the file where the app0 partition starts # This is defined in the partition .csv file - sketch_partition_start = 0x10000 + app_offset = 0x10000 - # Typical layout of the generated file: - # Offset | File - # - 0x1000 | ~\.platformio\packages\framework-arduinoespressif32\tools\sdk\esp32\bin\bootloader_dout_40m.bin - # - 0x8000 | ~\ESPEasy\.pio\build\\partitions.bin - # - 0xe000 | ~\.platformio\packages\framework-arduinoespressif32\tools\partitions\boot_app0.bin - # - 0x10000 | ~\ESPEasy\.pio\build\/.bin - new_file_name = env.subst("$BUILD_DIR/${PROGNAME}-factory.bin") - sections = env.subst(env.get('FLASH_EXTRA_IMAGES')) - new_file = open(new_file_name,"wb") - new_file.truncate() # Make sure no left over data is present from a previous build - + sections = env.subst(env.get("FLASH_EXTRA_IMAGES")) firmware_name = env.subst("$BUILD_DIR/${PROGNAME}.bin") - - # fill the file with 0xFF binary data to reflect 'erased and not yet written' data. - # Make sure to use exactly the size that normally would be erased - new_file.seek(0) - total_size = sketch_partition_start + os.path.getsize(firmware_name) - total_size = total_size - (total_size % flash_page_size) + flash_page_size - - new_file.write(bytes([0xff] * total_size)) - - print(" {} | {}".format("Offset", "File")) + chip = env.get("BOARD_MCU") + flash_size = env.BoardConfig().get("upload.flash_size") + cmd = [ + "--chip", + chip, + "merge_bin", + "-o", + new_file_name, + "--flash_size", + flash_size, + ] + + print(" Offset | File") for section in sections: - sect_adr,sect_file = section.split(" ",1) - print(" - {} | {}".format(sect_adr,sect_file)) - source = open(sect_file,"rb") - new_file.seek(int(sect_adr,0)-offset) - new_file.write(source.read()) - if "bootloader" in sect_file: - # Need to patch the bootloader to match the flash size - flash_size = env.BoardConfig().get("upload.flash_size") - - # See esp_image_header_t for struct definition - spi_speed_size_byte_offset = 3 - source.seek(spi_speed_size_byte_offset) - spi_speed_size_byte = source.read(1) - new_spi_speed_size_byte = spi_speed_size_byte[0] & 0x0f + sect_adr, sect_file = section.split(" ", 1) + print(f" - {sect_adr} | {sect_file}") + cmd += [sect_adr, sect_file] - # As defined in esp_image_flash_size_t - spi_size = { - "1MB": 0x00, - "2MB": 0x10, - "4MB": 0x20, - "8MB": 0x30, - "16MB": 0x40 - } - new_spi_speed_size_byte |= spi_size.get(flash_size, spi_speed_size_byte[0] & 0xf0) + print(f" - {hex(app_offset)} | {firmware_name}") + cmd += [hex(app_offset), firmware_name] - if new_spi_speed_size_byte != spi_speed_size_byte: - new_byte = bytes([new_spi_speed_size_byte]) - print(" | Patch flash size in bootloader: {} -> {}".format(decode_flash_size_speed(spi_speed_size_byte), decode_flash_size_speed(new_byte))) - new_file.seek(int(sect_adr,0)-offset + spi_speed_size_byte_offset) - new_file.write(new_byte) + print('Using esptool.py arguments: %s' % ' '.join(cmd)) - source.close() + esptool.main(cmd) - - firmware_start = sketch_partition_start-offset - firmware = open(firmware_name,"rb") - print(" - {} | {}".format(hex(firmware_start),firmware_name)) - new_file.seek(firmware_start) - new_file.write(firmware.read()) - new_file.close() - firmware.close() -env.AddPostAction("$BUILD_DIR/${PROGNAME}.bin", esp32_create_factory_bin) +# pylint: disable=E0602 +env.AddPostAction("$BUILD_DIR/${PROGNAME}.bin", esp32_create_combined_bin) # noqa \ No newline at end of file From 5167f487a5ae893bc0da41bcbc0b446aa3824f29 Mon Sep 17 00:00:00 2001 From: TD-er Date: Thu, 6 Jan 2022 19:23:13 +0100 Subject: [PATCH 040/147] [Build] Add missing esptool==3.2 in requirements.txt --- requirements.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/requirements.txt b/requirements.txt index d010247cf7..b712c1634b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -10,6 +10,7 @@ click==8.0.3 colorama==0.4.4 commonmark==0.9.1 docutils==0.17.1 +esptool==3.2 idna==3.3 imagesize==1.2.0 Jinja2==3.0.2 From 466a8e85d373d33fd7d0228ebcd9c97d1ab19916 Mon Sep 17 00:00:00 2001 From: TD-er Date: Thu, 6 Jan 2022 23:11:51 +0100 Subject: [PATCH 041/147] [ESP32 factory] Revert factory image script to not use esptool.py As it seems next to impossible to call the already installed esptool.py from the PlatformIO package dir. --- tools/pio/post_esp32.py | 108 ++++++++++++++++++++++---------- tools/pio/post_esp32_esptool.py | 70 +++++++++++++++++++++ 2 files changed, 146 insertions(+), 32 deletions(-) create mode 100644 tools/pio/post_esp32_esptool.py diff --git a/tools/pio/post_esp32.py b/tools/pio/post_esp32.py index 5ca8d18e35..334e3a2b27 100644 --- a/tools/pio/post_esp32.py +++ b/tools/pio/post_esp32.py @@ -7,7 +7,6 @@ # Maintainer: Gijs Noorlander (@TD-er) # # Special thanks to @Jason2866 for helping debug flashing to >4MB flash -# Thanks @jesserockz (esphome) for adapting to use esptool.py with merge_bin # # Typical layout of the generated file: # Offset | File @@ -16,49 +15,94 @@ # - 0xe000 | ~\.platformio\packages\framework-arduinoespressif32\tools\partitions\boot_app0.bin # - 0x10000 | ~\ESPEasy\.pio\build\/.bin -import subprocess +Import("env") +import os -# pylint: disable=E0602 -Import("env") # noqa +def decode_flash_size_speed(byte_value): + speed = byte_value[0] & 0x0f + size = int((byte_value[0] & 0xf0) / 16) -import esptool + # As defined in esp_image_spi_freq_t + spi_speed = { + 0: "40", + 1: "26", + 2: "20", + 0xf: "80" + } + speed_mhz = spi_speed.get(speed, "??") + size_mb = str(1< {}".format(decode_flash_size_speed(spi_speed_size_byte), decode_flash_size_speed(new_byte))) + new_file.seek(int(sect_adr,0)-offset + spi_speed_size_byte_offset) + new_file.write(new_byte) - esptool.main(cmd) + source.close() + + firmware_start = sketch_partition_start-offset + firmware = open(firmware_name,"rb") + print(" - {} | {}".format(hex(firmware_start),firmware_name)) + new_file.seek(firmware_start) + new_file.write(firmware.read()) + new_file.close() + firmware.close() -# pylint: disable=E0602 -env.AddPostAction("$BUILD_DIR/${PROGNAME}.bin", esp32_create_combined_bin) # noqa \ No newline at end of file +env.AddPostAction("$BUILD_DIR/${PROGNAME}.bin", esp32_create_factory_bin) \ No newline at end of file diff --git a/tools/pio/post_esp32_esptool.py b/tools/pio/post_esp32_esptool.py new file mode 100644 index 0000000000..3842047a57 --- /dev/null +++ b/tools/pio/post_esp32_esptool.py @@ -0,0 +1,70 @@ +# Disabled for now, as it is unclear how to use esptool.py from the installed PlatformIO +# It is working fine as long as you install esptool.py in your Python virtual env. +# But that needs an extra user action to complete. +# +# For now we use post_esp32.py + +# Part of ESPEasy build toolchain. +# +# Combines separate bin files with their respective offsets into a single file +# This single file must then be flashed to an ESP32 node with 0 offset. +# +# Original implementation: Bartłomiej Zimoń (@uzi18) +# Maintainer: Gijs Noorlander (@TD-er) +# +# Special thanks to @Jason2866 for helping debug flashing to >4MB flash +# Thanks @jesserockz (esphome) for adapting to use esptool.py with merge_bin +# +# Typical layout of the generated file: +# Offset | File +# - 0x1000 | ~\.platformio\packages\framework-arduinoespressif32\tools\sdk\esp32\bin\bootloader_dout_40m.bin +# - 0x8000 | ~\ESPEasy\.pio\build\\partitions.bin +# - 0xe000 | ~\.platformio\packages\framework-arduinoespressif32\tools\partitions\boot_app0.bin +# - 0x10000 | ~\ESPEasy\.pio\build\/.bin + +import subprocess + +# pylint: disable=E0602 +Import("env") # noqa + +import esptool + + +def esp32_create_combined_bin(source, target, env): + print("Generating combined binary for serial flashing") + + # The offset from begin of the file where the app0 partition starts + # This is defined in the partition .csv file + app_offset = 0x10000 + + new_file_name = env.subst("$BUILD_DIR/${PROGNAME}-factory.bin") + sections = env.subst(env.get("FLASH_EXTRA_IMAGES")) + firmware_name = env.subst("$BUILD_DIR/${PROGNAME}.bin") + chip = env.get("BOARD_MCU") + flash_size = env.BoardConfig().get("upload.flash_size") + cmd = [ + "--chip", + chip, + "merge_bin", + "-o", + new_file_name, + "--flash_size", + flash_size, + ] + + print(" Offset | File") + for section in sections: + sect_adr, sect_file = section.split(" ", 1) + print(f" - {sect_adr} | {sect_file}") + cmd += [sect_adr, sect_file] + + print(f" - {hex(app_offset)} | {firmware_name}") + cmd += [hex(app_offset), firmware_name] + + print('Using esptool.py arguments: %s' % ' '.join(cmd)) + + esptool.main(cmd) + + +# pylint: disable=E0602 +env.AddPostAction("$BUILD_DIR/${PROGNAME}.bin", esp32_create_combined_bin) # noqa \ No newline at end of file From 5782514a0d1eb27a902c5b55e3121d126f6a8ae4 Mon Sep 17 00:00:00 2001 From: TD-er Date: Thu, 6 Jan 2022 23:41:25 +0100 Subject: [PATCH 042/147] [ESP32 factory] Final fix using esptool.py by Jason2866 See: https://github.com/letscontrolit/ESPEasy/pull/3845#issuecomment-1006971300 --- tools/pio/post_esp32.py | 111 ++++++++++---------------------- tools/pio/post_esp32_esptool.py | 70 -------------------- 2 files changed, 35 insertions(+), 146 deletions(-) delete mode 100644 tools/pio/post_esp32_esptool.py diff --git a/tools/pio/post_esp32.py b/tools/pio/post_esp32.py index 334e3a2b27..9f6a5454d7 100644 --- a/tools/pio/post_esp32.py +++ b/tools/pio/post_esp32.py @@ -6,7 +6,8 @@ # Original implementation: Bartłomiej Zimoń (@uzi18) # Maintainer: Gijs Noorlander (@TD-er) # -# Special thanks to @Jason2866 for helping debug flashing to >4MB flash +# Special thanks to @Jason2866 (Tasmota) for helping debug flashing to >4MB flash +# Thanks @jesserockz (esphome) for adapting to use esptool.py with merge_bin # # Typical layout of the generated file: # Offset | File @@ -16,93 +17,51 @@ # - 0x10000 | ~\ESPEasy\.pio\build\/.bin Import("env") -import os -def decode_flash_size_speed(byte_value): - speed = byte_value[0] & 0x0f - size = int((byte_value[0] & 0xf0) / 16) +import subprocess +import sys - # As defined in esp_image_spi_freq_t - spi_speed = { - 0: "40", - 1: "26", - 2: "20", - 0xf: "80" - } - speed_mhz = spi_speed.get(speed, "??") - size_mb = str(1< {}".format(decode_flash_size_speed(spi_speed_size_byte), decode_flash_size_speed(new_byte))) - new_file.seek(int(sect_adr,0)-offset + spi_speed_size_byte_offset) - new_file.write(new_byte) + print('Using esptool.py arguments: %s' % ' '.join(cmd)) - source.close() + esptool.main(cmd) - - firmware_start = sketch_partition_start-offset - firmware = open(firmware_name,"rb") - print(" - {} | {}".format(hex(firmware_start),firmware_name)) - new_file.seek(firmware_start) - new_file.write(firmware.read()) - new_file.close() - firmware.close() -env.AddPostAction("$BUILD_DIR/${PROGNAME}.bin", esp32_create_factory_bin) \ No newline at end of file +env.AddPostAction("$BUILD_DIR/${PROGNAME}.bin", esp32_create_combined_bin) \ No newline at end of file diff --git a/tools/pio/post_esp32_esptool.py b/tools/pio/post_esp32_esptool.py deleted file mode 100644 index 3842047a57..0000000000 --- a/tools/pio/post_esp32_esptool.py +++ /dev/null @@ -1,70 +0,0 @@ -# Disabled for now, as it is unclear how to use esptool.py from the installed PlatformIO -# It is working fine as long as you install esptool.py in your Python virtual env. -# But that needs an extra user action to complete. -# -# For now we use post_esp32.py - -# Part of ESPEasy build toolchain. -# -# Combines separate bin files with their respective offsets into a single file -# This single file must then be flashed to an ESP32 node with 0 offset. -# -# Original implementation: Bartłomiej Zimoń (@uzi18) -# Maintainer: Gijs Noorlander (@TD-er) -# -# Special thanks to @Jason2866 for helping debug flashing to >4MB flash -# Thanks @jesserockz (esphome) for adapting to use esptool.py with merge_bin -# -# Typical layout of the generated file: -# Offset | File -# - 0x1000 | ~\.platformio\packages\framework-arduinoespressif32\tools\sdk\esp32\bin\bootloader_dout_40m.bin -# - 0x8000 | ~\ESPEasy\.pio\build\\partitions.bin -# - 0xe000 | ~\.platformio\packages\framework-arduinoespressif32\tools\partitions\boot_app0.bin -# - 0x10000 | ~\ESPEasy\.pio\build\/.bin - -import subprocess - -# pylint: disable=E0602 -Import("env") # noqa - -import esptool - - -def esp32_create_combined_bin(source, target, env): - print("Generating combined binary for serial flashing") - - # The offset from begin of the file where the app0 partition starts - # This is defined in the partition .csv file - app_offset = 0x10000 - - new_file_name = env.subst("$BUILD_DIR/${PROGNAME}-factory.bin") - sections = env.subst(env.get("FLASH_EXTRA_IMAGES")) - firmware_name = env.subst("$BUILD_DIR/${PROGNAME}.bin") - chip = env.get("BOARD_MCU") - flash_size = env.BoardConfig().get("upload.flash_size") - cmd = [ - "--chip", - chip, - "merge_bin", - "-o", - new_file_name, - "--flash_size", - flash_size, - ] - - print(" Offset | File") - for section in sections: - sect_adr, sect_file = section.split(" ", 1) - print(f" - {sect_adr} | {sect_file}") - cmd += [sect_adr, sect_file] - - print(f" - {hex(app_offset)} | {firmware_name}") - cmd += [hex(app_offset), firmware_name] - - print('Using esptool.py arguments: %s' % ' '.join(cmd)) - - esptool.main(cmd) - - -# pylint: disable=E0602 -env.AddPostAction("$BUILD_DIR/${PROGNAME}.bin", esp32_create_combined_bin) # noqa \ No newline at end of file From aab143c2e69a326413c8e071007a42b4ddf879a7 Mon Sep 17 00:00:00 2001 From: TD-er Date: Fri, 7 Jan 2022 00:03:13 +0100 Subject: [PATCH 043/147] [ESP32 factory] Change -factory.bin to .factory.bin --- dist/README.txt | 6 +++--- docs/source/Participate/ProjectStructure.rst | 6 +++--- tools/create_index.sh | 22 ++++++++++---------- tools/pio/copy_files.py | 8 +++---- tools/pio/post_esp32.py | 2 +- 5 files changed, 22 insertions(+), 22 deletions(-) diff --git a/dist/README.txt b/dist/README.txt index 9405875f08..5b6622405d 100644 --- a/dist/README.txt +++ b/dist/README.txt @@ -73,12 +73,12 @@ Ethernet support is included in similar builds as mentioned before, only ending Since ESP32 does have its flash partitioned in several blocks, we have 2 bin files of each ESP32 build: - test_ESP32_4M316k.bin -- test_ESP32_4M316k-factory.bin +- test_ESP32_4M316k.factory.bin -The binary with "-factory" in the name must be flashed on a new node, via the serial interface of the board. +The binary with ".factory" in the name must be flashed on a new node, via the serial interface of the board. This flash must be started at address 0. -The binary without "-factory" can be used for OTA updates. (OTA for ESP32 is added in May 2020) +The binary without ".factory" can be used for OTA updates. (OTA for ESP32 is added in May 2020) Please note that changing between those versions will destroy the settings! diff --git a/docs/source/Participate/ProjectStructure.rst b/docs/source/Participate/ProjectStructure.rst index 960aab774c..db4fcd4465 100644 --- a/docs/source/Participate/ProjectStructure.rst +++ b/docs/source/Participate/ProjectStructure.rst @@ -189,12 +189,12 @@ There are several builds for ESP32: Since ESP32 does have its flash partitioned in several blocks, we have 2 bin files of each ESP32 build, f.e.: * ``test_D_ESP32_4M316k.bin`` Use for OTA upgrades. -* ``test_D_ESP32_4M316k-factory.bin`` Use on clean nodes as initial inistall. +* ``test_D_ESP32_4M316k.factory.bin`` Use on clean nodes as initial inistall. -The binary with ``-factory`` in the name must be flashed on a new node, via the serial interface of the board. +The binary with ``.factory`` in the name must be flashed on a new node, via the serial interface of the board. This flash must be started at address 0. -The binary without ``-factory`` can be used for OTA updates. (OTA for ESP32 is added in May 2020) +The binary without ``.factory`` can be used for OTA updates. (OTA for ESP32 is added in May 2020) Blank Images diff --git a/tools/create_index.sh b/tools/create_index.sh index 8e430f29ae..82283d38b0 100644 --- a/tools/create_index.sh +++ b/tools/create_index.sh @@ -65,47 +65,47 @@ echo "

config command"), F("p021_save_always"), PCONFIG(2) == 0); // inverted // flag! diff --git a/src/_P025_ADS1115.ino b/src/_P025_ADS1115.ino index 06c6630a20..1092905d9b 100644 --- a/src/_P025_ADS1115.ino +++ b/src/_P025_ADS1115.ino @@ -112,11 +112,11 @@ boolean Plugin_025(uint8_t function, struct EventStruct *event, String& string) addFormNumericBox(F("Point 1"), F("p025_adc1"), PCONFIG_LONG(0), -32768, 32767); html_add_estimate_symbol(); - addTextBox(F("p025_out1"), String(PCONFIG_FLOAT(0), 3), 10); + addTextBox(F("p025_out1"), toString(PCONFIG_FLOAT(0), 3), 10); addFormNumericBox(F("Point 2"), F("p025_adc2"), PCONFIG_LONG(1), -32768, 32767); html_add_estimate_symbol(); - addTextBox(F("p025_out2"), String(PCONFIG_FLOAT(1), 3), 10); + addTextBox(F("p025_out2"), toString(PCONFIG_FLOAT(1), 3), 10); success = true; break; diff --git a/src/_P050_TCS34725.ino b/src/_P050_TCS34725.ino index ba4a3cd794..64e006f6e7 100644 --- a/src/_P050_TCS34725.ino +++ b/src/_P050_TCS34725.ino @@ -408,11 +408,11 @@ boolean Plugin_050(uint8_t function, struct EventStruct *event, String& string) nb = static_cast(b) / t * sRGBFactor; P050_data->applyTransformation(nr, ng, nb, &tr, &tg, &tb); } - RuleEvent += String(tr, 4); + RuleEvent += toString(tr, 4); RuleEvent += ','; - RuleEvent += String(tg, 4); + RuleEvent += toString(tg, 4); RuleEvent += ','; - RuleEvent += String(tb, 4); + RuleEvent += toString(tb, 4); break; case 2: sRGBFactor = 255.0f; @@ -423,11 +423,11 @@ boolean Plugin_050(uint8_t function, struct EventStruct *event, String& string) } else { RuleEvent += F("NormSRGB="); } - RuleEvent += String(static_cast(r) / t * sRGBFactor, 4); + RuleEvent += toString(static_cast(r) / t * sRGBFactor, 4); RuleEvent += ','; - RuleEvent += String(static_cast(g) / t * sRGBFactor, 4); + RuleEvent += toString(static_cast(g) / t * sRGBFactor, 4); RuleEvent += ','; - RuleEvent += String(static_cast(b) / t * sRGBFactor, 4); + RuleEvent += toString(static_cast(b) / t * sRGBFactor, 4); break; default: RuleEvent.clear(); diff --git a/src/_P060_MCP3221.ino b/src/_P060_MCP3221.ino index 9a3e4fc514..933696208a 100644 --- a/src/_P060_MCP3221.ino +++ b/src/_P060_MCP3221.ino @@ -73,11 +73,11 @@ boolean Plugin_060(uint8_t function, struct EventStruct *event, String& string) addFormNumericBox(F("Point 1"), F("p060_adc1"), PCONFIG_LONG(0), 0, 4095); html_add_estimate_symbol(); - addTextBox(F("p060_out1"), String(PCONFIG_FLOAT(0), 3), 10); + addTextBox(F("p060_out1"), toString(PCONFIG_FLOAT(0), 3), 10); addFormNumericBox(F("Point 2"), F("p060_adc2"), PCONFIG_LONG(1), 0, 4095); html_add_estimate_symbol(); - addTextBox(F("p060_out2"), String(PCONFIG_FLOAT(1), 3), 10); + addTextBox(F("p060_out2"), toString(PCONFIG_FLOAT(1), 3), 10); success = true; break; diff --git a/src/_P067_HX711_Load_Cell.ino b/src/_P067_HX711_Load_Cell.ino index e6f7548a25..67e2f1c703 100644 --- a/src/_P067_HX711_Load_Cell.ino +++ b/src/_P067_HX711_Load_Cell.ino @@ -197,7 +197,7 @@ boolean Plugin_067(uint8_t function, struct EventStruct *event, String& string) } int2float(PCONFIG(1), PCONFIG(2), &valFloat); - addFormTextBox(F("Offset"), F("p067_offset_chanA"), String(valFloat, 3), 25); + addFormTextBox(F("Offset"), F("p067_offset_chanA"), toString(valFloat, 3), 25); addHtml(F("     ↩ Tare: ")); addCheckBox(F("tareChanA"), 0); //always off @@ -212,7 +212,7 @@ boolean Plugin_067(uint8_t function, struct EventStruct *event, String& string) } int2float(PCONFIG(3), PCONFIG(4), &valFloat); - addFormTextBox(F("Offset"), F("p067_offset_chanB"), String(valFloat, 3), 25); + addFormTextBox(F("Offset"), F("p067_offset_chanB"), toString(valFloat, 3), 25); addHtml(F("     ↩ Tare: ")); addCheckBox(F("tareChanB"), 0); //always off @@ -222,11 +222,11 @@ boolean Plugin_067(uint8_t function, struct EventStruct *event, String& string) addFormNumericBox(F("Point 1"), F("p067_adc1_chanA"), PCONFIG_LONG(0)); html_add_estimate_symbol(); - addTextBox(F("p067_out1_chanA"), String(PCONFIG_FLOAT(0), 3), 10); + addTextBox(F("p067_out1_chanA"), toString(PCONFIG_FLOAT(0), 3), 10); addFormNumericBox(F("Point 2"), F("p067_adc2_chanA"), PCONFIG_LONG(1)); html_add_estimate_symbol(); - addTextBox(F("p067_out2_chanA"), String(PCONFIG_FLOAT(1), 3), 10); + addTextBox(F("p067_out2_chanA"), toString(PCONFIG_FLOAT(1), 3), 10); //------------ addFormSubHeader(F("Two Point Calibration Channel B")); @@ -235,11 +235,11 @@ boolean Plugin_067(uint8_t function, struct EventStruct *event, String& string) addFormNumericBox(F("Point 1"), F("p067_adc1_chanB"), PCONFIG_LONG(2)); html_add_estimate_symbol(); - addTextBox(F("p067_out1_chanB"), String(PCONFIG_FLOAT(2), 3), 10); + addTextBox(F("p067_out1_chanB"), toString(PCONFIG_FLOAT(2), 3), 10); addFormNumericBox(F("Point 2"), F("p067_adc2_chanB"), PCONFIG_LONG(3)); html_add_estimate_symbol(); - addTextBox(F("p067_out2_chanB"), String(PCONFIG_FLOAT(3), 3), 10); + addTextBox(F("p067_out2_chanB"), toString(PCONFIG_FLOAT(3), 3), 10); success = true; break; diff --git a/src/_P073_7DGT.ino b/src/_P073_7DGT.ino index 0c96448e0d..7d7028a7fa 100644 --- a/src/_P073_7DGT.ino +++ b/src/_P073_7DGT.ino @@ -1139,6 +1139,7 @@ bool p073_plugin_write_7dn(struct EventStruct *event, const String& text) { switch (P073_data->displayModel) { case P073_TM1637_4DGTCOLON: { if ((event->Par1 > -1000) && (event->Par1 < 10000)) { + // FIXME TD-er: Why call round on an int? P073_data->FillBufferWithNumber(String(round(event->Par1))); } else { diff --git a/src/_P074_TSL2591.ino b/src/_P074_TSL2591.ino index 8d9e1ae50c..d1248105c9 100644 --- a/src/_P074_TSL2591.ino +++ b/src/_P074_TSL2591.ino @@ -270,7 +270,7 @@ boolean Plugin_074(uint8_t function, struct EventStruct *event, String& string) if (loglevelActiveFor(LOG_LEVEL_INFO)) { String log = F("TSL2591: Lux: "); - log += String(lux); + log += toString(lux); log += F(" Full: "); log += String(full); log += F(" Visible: "); diff --git a/src/_P076_HLW8012.ino b/src/_P076_HLW8012.ino index 56ca2da550..b16c61995d 100644 --- a/src/_P076_HLW8012.ino +++ b/src/_P076_HLW8012.ino @@ -217,11 +217,11 @@ boolean Plugin_076(uint8_t function, struct EventStruct *event, String &string) if (Plugin076_LoadMultipliers(event->TaskIndex, current, voltage, power)) { addFormSubHeader(F("Calibration Values")); addFormTextBox(F("Current Multiplier"), F("p076_currmult"), - String(current, 2), 25); + doubleToString(current, 2), 25); addFormTextBox(F("Voltage Multiplier"), F("p076_voltmult"), - String(voltage, 2), 25); + doubleToString(voltage, 2), 25); addFormTextBox(F("Power Multiplier"), F("p076_powmult"), - String(power, 2), 25); + doubleToString(power, 2), 25); } success = true; diff --git a/src/_P082_GPS.ino b/src/_P082_GPS.ino index b83e61284e..b213ffd22f 100644 --- a/src/_P082_GPS.ino +++ b/src/_P082_GPS.ino @@ -677,7 +677,7 @@ void P082_html_show_stats(struct EventStruct *event) { P082_html_show_satStats(event, false, false); addRowLabel(F("HDOP")); - addHtml(String(P082_data->gps->hdop.value() / 100.0f)); + addHtml(toString(P082_data->gps->hdop.value() / 100.0f)); addRowLabel(F("UTC Time")); struct tm dateTime; diff --git a/src/_P090_CCS811.ino b/src/_P090_CCS811.ino index b64e9b62a0..41437e0f33 100644 --- a/src/_P090_CCS811.ino +++ b/src/_P090_CCS811.ino @@ -305,7 +305,7 @@ boolean Plugin_090(uint8_t function, struct EventStruct *event, String& string) if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { String log = F("CCS811 : Compensating for Temperature: "); - log += String(temperature) + temp + F(" & Humidity: ") + String(humidity) + F("%"); + log += toString(temperature) + temp + F(" & Humidity: ") + toString(humidity) + F("%"); addLog(LOG_LEVEL_DEBUG, log); } #endif // ifndef BUILD_NO_DEBUG diff --git a/src/_P096_eInk.ino b/src/_P096_eInk.ino index 1599c2e670..015323599b 100644 --- a/src/_P096_eInk.ino +++ b/src/_P096_eInk.ino @@ -370,7 +370,7 @@ boolean Plugin_096(uint8_t function, struct EventStruct *event, String& string) #ifndef BUILD_NO_DEBUG s.add(usecPassedSince(statisticsTimerStart)); - tmpString += "
Display timings = " + String(s.getAvg()); + tmpString += "
Display timings = " + toString(s.getAvg()); #endif P096_data->eInkScreen.clearBuffer(); P096_data->plugin_096_sequence_in_progress = false; diff --git a/src/_P103_Atlas_EZO_pH_ORP_EC_DO.ino b/src/_P103_Atlas_EZO_pH_ORP_EC_DO.ino index e55a2e085a..5de0b0edf0 100644 --- a/src/_P103_Atlas_EZO_pH_ORP_EC_DO.ino +++ b/src/_P103_Atlas_EZO_pH_ORP_EC_DO.ino @@ -190,7 +190,7 @@ boolean Plugin_103(uint8_t function, struct EventStruct *event, String &string) addUnit(F("V")); addRowLabel(F("Sensor Data")); - addHtml(String(UserVar[event->BaseVarIndex])); + addHtml(toString(UserVar[event->BaseVarIndex])); switch (board_type) { case PH: diff --git a/src/_P109_ThermOLED.ino b/src/_P109_ThermOLED.ino index 5ae698744e..e882e4f2ab 100644 --- a/src/_P109_ThermOLED.ino +++ b/src/_P109_ThermOLED.ino @@ -340,9 +340,9 @@ boolean Plugin_109(byte function, struct EventStruct *event, String& string) } logstr = F("Thermo : Starting status S:"); - logstr += String(UserVar[event->BaseVarIndex]); + logstr += toString(UserVar[event->BaseVarIndex]); logstr += F(", R:"); - logstr += String(UserVar[event->BaseVarIndex + 1]); + logstr += toString(UserVar[event->BaseVarIndex + 1]); addLog(LOG_LEVEL_INFO, logstr); Plugin_109_changed = 1; diff --git a/src/_P112_AS7265x.ino b/src/_P112_AS7265x.ino index 2394e00ad2..2d86f3455b 100644 --- a/src/_P112_AS7265x.ino +++ b/src/_P112_AS7265x.ino @@ -423,7 +423,7 @@ void queueEvent(taskIndex_t TaskIndex, int wavelength, float value) { RuleEvent += '#'; RuleEvent += wavelength; RuleEvent += '='; - RuleEvent += String(value, 2); + RuleEvent += toString(value, 2); eventQueue.add(RuleEvent); } diff --git a/src/_P117_SCD30.ino b/src/_P117_SCD30.ino index 8064b7d522..330bd99d89 100644 --- a/src/_P117_SCD30.ino +++ b/src/_P117_SCD30.ino @@ -91,7 +91,7 @@ boolean Plugin_117(uint8_t function, struct EventStruct *event, String& string) { addFormNumericBox(F("Altitude"), F("plugin_117_SCD30_alt"), PCONFIG(0), 0, 2000); addUnit(F("0..2000 m")); - addFormTextBox(F("Temp offset"), F("plugin_117_SCD30_tmp"), String(PCONFIG_FLOAT(0), 2), 5); + addFormTextBox(F("Temp offset"), F("plugin_117_SCD30_tmp"), toString(PCONFIG_FLOAT(0), 2), 5); addUnit(F("°C")); success = true; break; @@ -185,7 +185,7 @@ boolean Plugin_117(uint8_t function, struct EventStruct *event, String& string) { P117_data->getTemperatureOffset(&temp); log += F("Temp offset: "); - log += String(temp, 2); + log += toString(temp, 2); success = true; } diff --git a/src/src/Commands/Tasks.cpp b/src/src/Commands/Tasks.cpp index 8252c6df0c..0d0af49db5 100644 --- a/src/src/Commands/Tasks.cpp +++ b/src/src/Commands/Tasks.cpp @@ -101,7 +101,7 @@ const __FlashStringHelper * taskValueSet(struct EventStruct *event, const char * UserVar[uservarIndex] = result; } else { // TODO: Get Task description and var name - serialPrintln(String(UserVar[uservarIndex])); + serialPrintln(toString(UserVar[uservarIndex])); } success = true; return return_command_success(); diff --git a/src/src/DataStructs/Web_StreamingBuffer.cpp b/src/src/DataStructs/Web_StreamingBuffer.cpp index 9b5386ee80..5333a08cc6 100644 --- a/src/src/DataStructs/Web_StreamingBuffer.cpp +++ b/src/src/DataStructs/Web_StreamingBuffer.cpp @@ -9,6 +9,7 @@ #include "../Globals/Services.h" #include "../Helpers/ESPEasy_time_calc.h" +#include "../Helpers/Convert.h" #ifdef ESP8266 @@ -43,12 +44,16 @@ Web_StreamingBuffer& Web_StreamingBuffer::operator+=(char a) { return addString(String(a)); } -Web_StreamingBuffer& Web_StreamingBuffer::operator+=(long unsigned int a) { +Web_StreamingBuffer& Web_StreamingBuffer::operator+=(long unsigned int a) { return addString(String(a)); } -Web_StreamingBuffer& Web_StreamingBuffer::operator+=(float a) { - return addString(String(a)); +Web_StreamingBuffer& Web_StreamingBuffer::operator+=(const float& a) { + return addString(toString(a, 2)); +} + +Web_StreamingBuffer& Web_StreamingBuffer::operator+=(const double& a) { + return addString(doubleToString(a)); } Web_StreamingBuffer& Web_StreamingBuffer::operator+=(int a) { diff --git a/src/src/DataStructs/Web_StreamingBuffer.h b/src/src/DataStructs/Web_StreamingBuffer.h index 7757669a71..9af118ede0 100644 --- a/src/src/DataStructs/Web_StreamingBuffer.h +++ b/src/src/DataStructs/Web_StreamingBuffer.h @@ -39,7 +39,8 @@ class Web_StreamingBuffer { // Web_StreamingBuffer& operator=(const String& a); Web_StreamingBuffer& operator+=(char a); Web_StreamingBuffer& operator+=(long unsigned int a); - Web_StreamingBuffer& operator+=(float a); + Web_StreamingBuffer& operator+=(const float& a); + Web_StreamingBuffer& operator+=(const double& a); Web_StreamingBuffer& operator+=(int a); Web_StreamingBuffer& operator+=(uint32_t a); Web_StreamingBuffer& operator+=(const String& a); diff --git a/src/src/ESPEasyCore/ESPEasyWifi.cpp b/src/src/ESPEasyCore/ESPEasyWifi.cpp index eeb836080c..51fd0b9ce0 100644 --- a/src/src/ESPEasyCore/ESPEasyWifi.cpp +++ b/src/src/ESPEasyCore/ESPEasyWifi.cpp @@ -681,14 +681,14 @@ void SetWiFiTXpower(float dBm, float rssi) { if (TX_pwr_int != last_log) { last_log = TX_pwr_int; String log = F("WiFi : Set TX power to "); - log += String(dBm, 0); + log += toString(dBm, 0); log += F("dBm"); log += F(" sensitivity: "); - log += String(threshold, 0); + log += toString(threshold, 0); log += F("dBm"); if (rssi < 0) { log += F(" RSSI: "); - log += String(rssi, 0); + log += toString(rssi, 0); log += F("dBm"); } addLog(LOG_LEVEL_DEBUG, log); diff --git a/src/src/Helpers/Convert.cpp b/src/src/Helpers/Convert.cpp index a0c1f77737..0107f9627b 100644 --- a/src/src/Helpers/Convert.cpp +++ b/src/src/Helpers/Convert.cpp @@ -234,17 +234,48 @@ float ul2float(unsigned long ul) /*********************************************************************************************\ Workaround for removing trailing white space when String() converts a float with 0 decimals \*********************************************************************************************/ -String toString(const float& value, unsigned int decimals) +String toString(const float& value, unsigned int decimalPlaces) { - String sValue = String(value, decimals); + // This has been fixed in ESP32 code, not (yet) in ESP8266 code + // https://github.com/espressif/arduino-esp32/pull/6138/files + #ifdef ESP8266 + char *buf = (char*)malloc(decimalPlaces + 42); + if (nullptr == buf) { + return F("nan"); + } + String sValue(dtostrf(value, (decimalPlaces + 2), decimalPlaces, buf)); + free(buf); + #else + String sValue = String(value, decimalPlaces); + #endif sValue.trim(); return sValue; } -String doubleToString(const double& value, unsigned int decimals, bool trimTrailingZeros) { - String res(value, decimals); +String doubleToString(const double& value, unsigned int decimalPlaces, bool trimTrailingZeros) { + // This has been fixed in ESP32 code, not (yet) in ESP8266 code + // https://github.com/espressif/arduino-esp32/pull/6138/files + #ifdef ESP8266 + unsigned int expectedChars = decimalPlaces + 4; // 1 dot, 2 minus signs and terminating zero + if (value > 1e32 || value < -1e32) { + expectedChars += 308; // Just assume the worst + } else { + expectedChars += 33; + } + char *buf = (char*)malloc(expectedChars); + + if (nullptr == buf) { + return F("nan"); + } + String res(dtostrf(value, (decimalPlaces + 2), decimalPlaces, buf)); + free(buf); + + #else + String res(value, decimalPlaces); + #endif res.trim(); + if (trimTrailingZeros) { int dot_pos = res.lastIndexOf('.'); if (dot_pos != -1) { diff --git a/src/src/Helpers/Convert.h b/src/src/Helpers/Convert.h index a08ea18ada..ed9b41ff3f 100644 --- a/src/src/Helpers/Convert.h +++ b/src/src/Helpers/Convert.h @@ -65,9 +65,9 @@ float ul2float(unsigned long ul); /*********************************************************************************************\ Workaround for removing trailing white space when String() converts a float with 0 decimals \*********************************************************************************************/ -String toString(const float& value, unsigned int decimals); +String toString(const float& value, unsigned int decimalPlaces = 2); -String doubleToString(const double& value, unsigned int decimals = 2, bool trimTrailingZeros = false); +String doubleToString(const double& value, unsigned int decimalPlaces = 2, bool trimTrailingZeros = false); #endif // HELPERS_CONVERT_H \ No newline at end of file diff --git a/src/src/Helpers/ESPEasyStatistics.cpp b/src/src/Helpers/ESPEasyStatistics.cpp index c8e1070d0b..8aa0ddbb92 100644 --- a/src/src/Helpers/ESPEasyStatistics.cpp +++ b/src/src/Helpers/ESPEasyStatistics.cpp @@ -6,6 +6,7 @@ #include "../DataStructs/TimingStats.h" #include "../WebServer/WebServer.h" #include "../Globals/Protocol.h" +#include "../Helpers/Convert.h" /* void logStatistics(uint8_t loglevel, bool clearStats) { @@ -56,10 +57,10 @@ void stream_json_timing_stats(const TimingStats& stats, long timeSinceLastReset) float call_per_sec = static_cast(count) / static_cast(timeSinceLastReset) * 1000.0f; json_number(F("count"), String(count)); - json_number(F("call-per-sec"), String(call_per_sec)); + json_number(F("call-per-sec"), toString(call_per_sec, 2)); json_number(F("min"), String(minVal)); json_number(F("max"), String(maxVal)); - json_number(F("avg"), String(stats.getAvg())); + json_number(F("avg"), toString(stats.getAvg(), 2)); json_prop(F("unit"), F("usec")); } diff --git a/src/src/Helpers/ESPEasy_time.cpp b/src/src/Helpers/ESPEasy_time.cpp index e6be983b90..3f46a68cb3 100644 --- a/src/src/Helpers/ESPEasy_time.cpp +++ b/src/src/Helpers/ESPEasy_time.cpp @@ -195,14 +195,14 @@ unsigned long ESPEasy_time::now() { if (loglevelActiveFor(LOG_LEVEL_INFO)) { String log = F("Time set to "); - log += String(unixTime_d, 3); + log += doubleToString(unixTime_d, 3); if ((-86400 < time_offset) && (time_offset < 86400)) { // Only useful to show adjustment if it is less than a day. log += F(" Time adjusted by "); - log += String(time_offset * 1000.0); + log += doubleToString(time_offset * 1000.0); log += F(" msec. Wander: "); - log += String(timeWander, 3); + log += doubleToString(timeWander, 3); log += F(" msec/second"); log += F(" Source: "); log += toString(timeSource); @@ -444,7 +444,7 @@ bool ESPEasy_time::getNtpTime(double& unixTime_d) // We gained more than 1 second in accuracy fractpart += 1.0; } - log += String(fractpart, 3); + log += doubleToString(fractpart, 3); log += F(" seconds"); addLog(LOG_LEVEL_INFO, log); } diff --git a/src/src/Helpers/StringProvider.cpp b/src/src/Helpers/StringProvider.cpp index f7874e89e9..8fc6f4a804 100644 --- a/src/src/Helpers/StringProvider.cpp +++ b/src/src/Helpers/StringProvider.cpp @@ -22,6 +22,7 @@ #include "../Globals/Settings.h" #include "../Globals/WiFi_AP_Candidates.h" +#include "../Helpers/Convert.h" #include "../Helpers/Memory.h" #include "../Helpers/Misc.h" #include "../Helpers/Scheduler.h" @@ -226,12 +227,12 @@ String getValue(LabelType::Enum label) { case LabelType::TIME_SOURCE: return toString(node_time.timeSource); case LabelType::TIME_WANDER: return String(node_time.timeWander, 3); case LabelType::UPTIME: return String(getUptimeMinutes()); - case LabelType::LOAD_PCT: return String(getCPUload()); + case LabelType::LOAD_PCT: return toString(getCPUload(), 2); case LabelType::LOOP_COUNT: return String(getLoopCountPerSec()); case LabelType::CPU_ECO_MODE: return jsonBool(Settings.EcoPowerMode()); #ifdef ESP8266 // TD-er: Disable setting TX power on ESP32 as it seems to cause issues on IDF4.4 - case LabelType::WIFI_TX_MAX_PWR: return String(Settings.getWiFi_TX_power(), 2); - case LabelType::WIFI_CUR_TX_PWR: return String(WiFiEventData.wifi_TX_pwr, 2); + case LabelType::WIFI_TX_MAX_PWR: return toString(Settings.getWiFi_TX_power(), 2); + case LabelType::WIFI_CUR_TX_PWR: return toString(WiFiEventData.wifi_TX_pwr, 2); case LabelType::WIFI_SENS_MARGIN: return String(Settings.WiFi_sensitivity_margin); case LabelType::WIFI_SEND_AT_MAX_TX_PWR:return jsonBool(Settings.UseMaxTXpowerForSending()); #endif diff --git a/src/src/Helpers/SystemVariables.cpp b/src/src/Helpers/SystemVariables.cpp index ab3ad944af..03cfd4076b 100644 --- a/src/src/Helpers/SystemVariables.cpp +++ b/src/src/Helpers/SystemVariables.cpp @@ -23,6 +23,7 @@ #include "../Globals/Settings.h" #include "../Globals/Statistics.h" +#include "../Helpers/Convert.h" #include "../Helpers/Hardware.h" #include "../Helpers/Misc.h" #include "../Helpers/Numerical.h" @@ -150,7 +151,7 @@ void SystemVariables::parseSystemVariables(String& s, boolean useURLencode) case SYSHEAP: value = String(ESP.getFreeHeap()); break; case SYSHOUR: value = String(node_time.hour()); break; case SYSHOUR_0: value = timeReplacement_leadZero(node_time.hour()); break; - case SYSLOAD: value = String(getCPUload()); break; + case SYSLOAD: value = String(getCPUload(), 2); break; case SYSMIN: value = String(node_time.minute()); break; case SYSMIN_0: value = timeReplacement_leadZero(node_time.minute()); break; case SYSMONTH: value = String(node_time.month()); break; diff --git a/src/src/Helpers/_CPlugin_DomoticzHelper.cpp b/src/src/Helpers/_CPlugin_DomoticzHelper.cpp index 3883437a8d..63a06e7844 100644 --- a/src/src/Helpers/_CPlugin_DomoticzHelper.cpp +++ b/src/src/Helpers/_CPlugin_DomoticzHelper.cpp @@ -284,7 +284,7 @@ String serializeDomoticzJson(struct EventStruct *event) } else { json += ','; - json += to_json_object_value(F("Set%20Level"), String(UserVar[event->BaseVarIndex], 2)); + json += to_json_object_value(F("Set%20Level"), toString(UserVar[event->BaseVarIndex], 2)); } break; diff --git a/src/src/PluginStructs/P121_data_struct.cpp b/src/src/PluginStructs/P121_data_struct.cpp index 8e34f8621c..3cedf21126 100644 --- a/src/src/PluginStructs/P121_data_struct.cpp +++ b/src/src/PluginStructs/P121_data_struct.cpp @@ -29,11 +29,11 @@ bool P121_data_struct::begin(int taskid) log += F(" Unique ID: "); log += sensor.sensor_id; log += F(" Max Value: "); - log += String(sensor.max_value); + log += toString(sensor.max_value); log += F(" Min Value: "); - log += String(sensor.min_value); + log += toString(sensor.min_value); log += F(" Resolution: "); - log += String(sensor.resolution); + log += toString(sensor.resolution); addLog(LOG_LEVEL_DEBUG, log); } #endif diff --git a/src/src/WebServer/AdvancedConfigPage.cpp b/src/src/WebServer/AdvancedConfigPage.cpp index 62a18d99be..9ac35999d0 100644 --- a/src/src/WebServer/AdvancedConfigPage.cpp +++ b/src/src/WebServer/AdvancedConfigPage.cpp @@ -260,14 +260,14 @@ void handle_advanced() { addUnit(F("dBm")); String note; note = F("Current max: "); - note += String(maxTXpwr, 2); + note += toString(maxTXpwr, 2); note += F(" dBm"); addFormNote(note); addFormNumericBox(LabelType::WIFI_SENS_MARGIN, Settings.WiFi_sensitivity_margin, -20, 30); addUnit(F("dB")); // Relative, thus the unit is dB, not dBm note = F("Adjust TX power to target the AP with (threshold + margin) dBm signal strength. Current threshold: "); - note += String(threshold, 2); + note += toString(threshold, 2); note += F(" dBm"); addFormNote(note); } diff --git a/src/src/WebServer/Markup.cpp b/src/src/WebServer/Markup.cpp index a50abd342a..dd35fb5c79 100644 --- a/src/src/WebServer/Markup.cpp +++ b/src/src/WebServer/Markup.cpp @@ -7,6 +7,7 @@ #include "../Globals/Settings.h" +#include "../Helpers/Convert.h" #include "../Helpers/Hardware.h" #include "../Helpers/StringGenerator_GPIO.h" @@ -683,9 +684,9 @@ void addFloatNumberBox(const String& id, float value, float min, float max, unsi html += id; html += '\''; html += F(" min="); - html += String(min, nrDecimals); + html += toString(min, nrDecimals); html += F(" max="); - html += String(max, nrDecimals); + html += toString(max, nrDecimals); html += F(" step="); if (stepsize <= 0.0f) { @@ -696,11 +697,11 @@ void addFloatNumberBox(const String& id, float value, float min, float max, unsi } html += '1'; } else { - html += String(stepsize, nrDecimals); + html += toString(stepsize, nrDecimals); } html += F(" style='width:7em;' value="); - html += String(value, nrDecimals); + html += toString(value, nrDecimals); #ifdef ENABLE_TOOLTIPS diff --git a/src/src/WebServer/SysInfoPage.cpp b/src/src/WebServer/SysInfoPage.cpp index 2d9330227f..c59b1bb1e7 100644 --- a/src/src/WebServer/SysInfoPage.cpp +++ b/src/src/WebServer/SysInfoPage.cpp @@ -24,6 +24,7 @@ #include "../Globals/RTC.h" #include "../Globals/Settings.h" +#include "../Helpers/Convert.h" #include "../Helpers/ESPEasyStatistics.h" #include "../Helpers/ESPEasy_Storage.h" #include "../Helpers/Hardware.h" @@ -64,7 +65,7 @@ void handle_sysinfo_json() { json_number(F("unit"), String(Settings.Unit)); json_prop(F("time"), node_time.getDateTimeString('-', ':', ' ')); json_prop(F("uptime"), getExtendedValue(LabelType::UPTIME)); - json_number(F("cpu_load"), String(getCPUload())); + json_number(F("cpu_load"), toString(getCPUload())); json_number(F("loop_count"), String(getLoopCountPerSec())); json_close(); diff --git a/src/src/WebServer/TimingStats.cpp b/src/src/WebServer/TimingStats.cpp index 6ff6b29eec..fc890f92d7 100644 --- a/src/src/WebServer/TimingStats.cpp +++ b/src/src/WebServer/TimingStats.cpp @@ -46,7 +46,7 @@ void handle_timingstats() { addHtml(ESPEasy_time::getDateTimeString(startPeriod, '-', ':', ' ', false)); addRowLabelValue(LabelType::LOCAL_TIME); addRowLabel(F("Time span")); - addHtml(String(timespan)); + addHtml(toString(timespan)); addHtml(F(" sec")); addRowLabel(F("*")); addHtml(F("Duty cycle based on average < 1 msec is highly unreliable")); @@ -63,9 +63,9 @@ void format_using_threshhold(unsigned long value) { float value_msec = value / 1000.0f; if (value > TIMING_STATS_THRESHOLD) { - html_B(String(value_msec, 3)); + html_B(toString(value_msec, 3)); } else { - addHtml(String(value_msec, 3)); + addHtml(toString(value_msec, 3)); } } @@ -78,11 +78,11 @@ void stream_html_timing_stats(const TimingStats& stats, long timeSinceLastReset) html_TD(); const float call_per_sec = static_cast(c) / static_cast(timeSinceLastReset) * 1000.0f; const float avg = stats.getAvg(); - addHtml(String(call_per_sec, 2)); + addHtml(toString(call_per_sec, 2)); html_TD(); { const float duty = (call_per_sec * avg / 10000.0f); - String duty_str = String(duty, 2); + String duty_str = toString(duty, 2); if (avg < 1000) { // Unreliable as average is below 1 msec duty_str += '*'; diff --git a/src/src/WebServer/WebServer.cpp b/src/src/WebServer/WebServer.cpp index d0a0cd4244..b6dba41954 100644 --- a/src/src/WebServer/WebServer.cpp +++ b/src/src/WebServer/WebServer.cpp @@ -1024,7 +1024,7 @@ void addSVG_param(const __FlashStringHelper * key, int value) { } void addSVG_param(const __FlashStringHelper * key, float value) { - addSVG_param(key, String(value, 2)); + addSVG_param(key, toString(value, 2)); } void addSVG_param(const __FlashStringHelper * key, const String& value) { From ff69652ff79879f3b5b3240e6d53fa218eddb8f2 Mon Sep 17 00:00:00 2001 From: TD-er Date: Sun, 23 Jan 2022 22:34:00 +0100 Subject: [PATCH 066/147] [Cleanup] Fix define paths on .h files --- src/_N001_Email.ino | 7 ++++--- src/__NPlugin.ino | 2 +- src/src/Commands/Time.cpp | 2 ++ src/src/Commands/WiFi.cpp | 2 ++ src/src/ControllerQueue/C016_queue_element.h | 1 + src/src/CustomBuild/CompiletimeDefines.h | 6 +++--- src/src/CustomBuild/ESPEasyDefaults.h | 6 +++--- src/src/CustomBuild/ESPEasyLimits.h | 1 + src/src/CustomBuild/ESPEasy_buildinfo.h | 6 +++--- src/src/CustomBuild/StorageLayout.h | 6 +++--- src/src/CustomBuild/define_plugin_sets.h | 6 +++--- src/src/DataStructs/C013_p2p_dataStructs.cpp | 5 ----- src/src/DataStructs/C013_p2p_dataStructs.h | 5 ++--- src/src/DataStructs/ESPEasy_EventStruct.h | 11 +++++------ src/src/DataStructs/ESPEasy_packed_raw_data.h | 6 +++--- .../ExtendedControllerCredentialsStruct.h | 6 +++--- src/src/DataStructs/I2CTypes.h | 6 +++--- src/src/DataStructs/MAC_address.cpp | 15 +++++++++++++++ src/src/DataStructs/MAC_address.h | 4 ++++ src/src/DataStructs/Web_StreamingBuffer.h | 6 +++--- src/src/DataStructs_templ/SettingsStruct.cpp | 6 +++--- src/src/DataTypes/CPluginID.h | 6 +++--- src/src/DataTypes/ControllerIndex.h | 4 ++-- src/src/DataTypes/DeviceIndex.cpp | 2 +- src/src/DataTypes/DeviceIndex.h | 12 +++++------- src/src/DataTypes/DeviceModel.h | 6 +++--- src/src/DataTypes/ESPEasy_plugin_functions.h | 6 +++--- src/src/DataTypes/NPluginID.cpp | 3 +++ src/src/DataTypes/NPluginID.h | 12 ++++++++++++ src/src/DataTypes/NetworkMedium.h | 6 +++--- src/src/DataTypes/NotifierIndex.cpp | 5 +++++ src/src/DataTypes/NotifierIndex.h | 10 ++++++++++ src/src/DataTypes/PluginID.cpp | 2 +- src/src/DataTypes/PluginID.h | 10 +++++----- src/src/DataTypes/ProtocolIndex.h | 4 ++-- src/src/DataTypes/SPI_options.h | 6 +++--- src/src/DataTypes/SettingsType.h | 6 +++--- src/src/DataTypes/TaskIndex.cpp | 2 +- src/src/DataTypes/TaskIndex.h | 10 +++------- src/src/Globals/NPlugins.cpp | 4 ++-- src/src/Globals/NPlugins.h | 14 +++++++------- src/src/Globals/Plugins.h | 6 +++--- src/src/Helpers/Scheduler.cpp | 1 + src/src/Helpers/Scheduler.h | 1 + src/src/WebServer/NotificationPage.cpp | 1 - 45 files changed, 149 insertions(+), 105 deletions(-) create mode 100644 src/src/DataTypes/NPluginID.cpp create mode 100644 src/src/DataTypes/NPluginID.h create mode 100644 src/src/DataTypes/NotifierIndex.cpp create mode 100644 src/src/DataTypes/NotifierIndex.h diff --git a/src/_N001_Email.ino b/src/_N001_Email.ino index 5ad734bc9b..c75993e107 100644 --- a/src/_N001_Email.ino +++ b/src/_N001_Email.ino @@ -23,6 +23,10 @@ #include "src/Helpers/_CPlugin_Helper.h" // safeReadStringUntil +// Forward declaration +boolean NPlugin_001_send(const NotificationSettingsStruct& notificationsettings, const String& aSub, String& aMesg); + + // The message body is included in event->String1 boolean NPlugin_001(NPlugin::Function function, struct EventStruct *event, String& string) @@ -85,8 +89,6 @@ boolean NPlugin_001(NPlugin::Function function, struct EventStruct *event, Strin } -#ifdef USES_NOTIFIER - boolean NPlugin_001_send(const NotificationSettingsStruct& notificationsettings, const String& aSub, String& aMesg) { // String& aDomain , String aTo, String aFrom, String aSub, String aMesg, String aHost, int aPort) @@ -186,7 +188,6 @@ boolean NPlugin_001_send(const NotificationSettingsStruct& notificationsettings, return myStatus; } -#endif boolean NPlugin_001_Auth(WiFiClient& client, const String& user, const String& pass) { diff --git a/src/__NPlugin.ino b/src/__NPlugin.ino index b90dde7ba9..da0f22b640 100644 --- a/src/__NPlugin.ino +++ b/src/__NPlugin.ino @@ -1,8 +1,8 @@ #include "ESPEasy_common.h" -#ifdef USES_NOTIFIER #include "src/Globals/NPlugins.h" #include "src/DataStructs/ESPEasy_EventStruct.h" +#ifdef USES_NOTIFIER // ******************************************************************************** diff --git a/src/src/Commands/Time.cpp b/src/src/Commands/Time.cpp index 1b326278e2..7cfbbba0f9 100644 --- a/src/src/Commands/Time.cpp +++ b/src/src/Commands/Time.cpp @@ -6,6 +6,8 @@ #include "../Commands/Common.h" +#include "../DataStructs/ESPEasy_EventStruct.h" + #include "../ESPEasyCore/Serial.h" #include "../Globals/ESPEasy_time.h" diff --git a/src/src/Commands/WiFi.cpp b/src/src/Commands/WiFi.cpp index cff91030e0..07b83d15b3 100644 --- a/src/src/Commands/WiFi.cpp +++ b/src/src/Commands/WiFi.cpp @@ -4,6 +4,8 @@ #include "../Commands/Common.h" +#include "../DataStructs/ESPEasy_EventStruct.h" + #include "../ESPEasyCore/ESPEasyWifi.h" #include "../ESPEasyCore/Serial.h" diff --git a/src/src/ControllerQueue/C016_queue_element.h b/src/src/ControllerQueue/C016_queue_element.h index 72f2b66d34..e0dc6a635b 100644 --- a/src/src/ControllerQueue/C016_queue_element.h +++ b/src/src/ControllerQueue/C016_queue_element.h @@ -4,6 +4,7 @@ #include "../../ESPEasy_common.h" #include "../CustomBuild/ESPEasyLimits.h" #include "../DataStructs/DeviceStruct.h" +#include "../DataTypes/ControllerIndex.h" #include "../DataStructs/UnitMessageCount.h" #include "../Globals/Plugins.h" diff --git a/src/src/CustomBuild/CompiletimeDefines.h b/src/src/CustomBuild/CompiletimeDefines.h index 68021118e9..14a7ff057b 100644 --- a/src/src/CustomBuild/CompiletimeDefines.h +++ b/src/src/CustomBuild/CompiletimeDefines.h @@ -1,5 +1,5 @@ -#ifndef HELPERS_COMPILETIMEDEFINES_H -#define HELPERS_COMPILETIMEDEFINES_H +#ifndef CUSTOMBUILD_COMPILETIMEDEFINES_H +#define CUSTOMBUILD_COMPILETIMEDEFINES_H #include @@ -13,4 +13,4 @@ const __FlashStringHelper * get_build_platform(); const __FlashStringHelper * get_git_head(); -#endif // HELPERS_COMPILETIMEDEFINES_H +#endif // CUSTOMBUILD_COMPILETIMEDEFINES_H diff --git a/src/src/CustomBuild/ESPEasyDefaults.h b/src/src/CustomBuild/ESPEasyDefaults.h index 665a54e6bf..3e57caf12c 100644 --- a/src/src/CustomBuild/ESPEasyDefaults.h +++ b/src/src/CustomBuild/ESPEasyDefaults.h @@ -1,5 +1,5 @@ -#ifndef ESPEASY_DEFAULTS_H_ -#define ESPEASY_DEFAULTS_H_ +#ifndef CUSTOMBUILD_ESPEASY_DEFAULTS_H_ +#define CUSTOMBUILD_ESPEASY_DEFAULTS_H_ // Needed to make sure Custom.h is used. #include "../../ESPEasy_common.h" @@ -339,4 +339,4 @@ #define DEFAULT_I2C_CLOCK_LIMIT 0 // */ -#endif // ESPEASY_DEFAULTS_H_ +#endif // CUSTOMBUILD_ESPEASY_DEFAULTS_H_ diff --git a/src/src/CustomBuild/ESPEasyLimits.h b/src/src/CustomBuild/ESPEasyLimits.h index 29a5fa3ea1..f423c3d51a 100644 --- a/src/src/CustomBuild/ESPEasyLimits.h +++ b/src/src/CustomBuild/ESPEasyLimits.h @@ -76,6 +76,7 @@ #define NAME_FORMULA_LENGTH_MAX 40 #endif +#define USERVAR_MAX_INDEX (VARS_PER_TASK * TASKS_MAX) // *********************************************************************** // * The next limits affect memory usage diff --git a/src/src/CustomBuild/ESPEasy_buildinfo.h b/src/src/CustomBuild/ESPEasy_buildinfo.h index f6719bf6bb..b201d613f5 100644 --- a/src/src/CustomBuild/ESPEasy_buildinfo.h +++ b/src/src/CustomBuild/ESPEasy_buildinfo.h @@ -1,5 +1,5 @@ -#ifndef ESPEASY_BUILD_INFO_H -#define ESPEASY_BUILD_INFO_H +#ifndef CUSTOMBUILD_ESPEASY_BUILD_INFO_H +#define CUSTOMBUILD_ESPEASY_BUILD_INFO_H // ******************************************************************************** @@ -41,4 +41,4 @@ #endif // ifndef BUILD_GIT -#endif // ESPEASY_BUILD_INFO_H +#endif // CUSTOMBUILD_ESPEASY_BUILD_INFO_H diff --git a/src/src/CustomBuild/StorageLayout.h b/src/src/CustomBuild/StorageLayout.h index 37b891cfbb..3b3cebfd3f 100644 --- a/src/src/CustomBuild/StorageLayout.h +++ b/src/src/CustomBuild/StorageLayout.h @@ -1,5 +1,5 @@ -#ifndef DATASTRUCTS_STORAGE_LAYOUT_H -#define DATASTRUCTS_STORAGE_LAYOUT_H +#ifndef CUSTOMBUILD_STORAGE_LAYOUT_H +#define CUSTOMBUILD_STORAGE_LAYOUT_H #include "../../ESPEasy_common.h" @@ -172,4 +172,4 @@ #endif // if defined(ESP32) -#endif // DATASTRUCTS_STORAGE_LAYOUT_H +#endif // CUSTOMBUILD_STORAGE_LAYOUT_H diff --git a/src/src/CustomBuild/define_plugin_sets.h b/src/src/CustomBuild/define_plugin_sets.h index 33fae9d21e..b5e36cde97 100644 --- a/src/src/CustomBuild/define_plugin_sets.h +++ b/src/src/CustomBuild/define_plugin_sets.h @@ -1,5 +1,5 @@ -#ifndef DEFINE_PLUGIN_SETS_H -#define DEFINE_PLUGIN_SETS_H +#ifndef CUSTOMBUILD_DEFINE_PLUGIN_SETS_H +#define CUSTOMBUILD_DEFINE_PLUGIN_SETS_H #include "../../ESPEasy_common.h" @@ -1833,4 +1833,4 @@ To create/register a plugin, you have to : #endif #endif -#endif // DEFINE_PLUGIN_SETS_H \ No newline at end of file +#endif // CUSTOMBUILD_DEFINE_PLUGIN_SETS_H \ No newline at end of file diff --git a/src/src/DataStructs/C013_p2p_dataStructs.cpp b/src/src/DataStructs/C013_p2p_dataStructs.cpp index cea255abe3..fc31041d2f 100644 --- a/src/src/DataStructs/C013_p2p_dataStructs.cpp +++ b/src/src/DataStructs/C013_p2p_dataStructs.cpp @@ -1,9 +1,6 @@ #include "../DataStructs/C013_p2p_dataStructs.h" #include "../Globals/Plugins.h" -#include "../../ESPEasy_common.h" - -#ifdef USES_C013 C013_SensorInfoStruct::C013_SensorInfoStruct() { @@ -45,5 +42,3 @@ bool C013_SensorDataStruct::isValid() const } return true; } - -#endif \ No newline at end of file diff --git a/src/src/DataStructs/C013_p2p_dataStructs.h b/src/src/DataStructs/C013_p2p_dataStructs.h index e8b11fe363..c465d7195d 100644 --- a/src/src/DataStructs/C013_p2p_dataStructs.h +++ b/src/src/DataStructs/C013_p2p_dataStructs.h @@ -4,11 +4,11 @@ #include #include "../CustomBuild/ESPEasyLimits.h" -#include "../Globals/Plugins.h" +#include "../DataTypes/TaskIndex.h" +#include "../DataTypes/PluginID.h" // These structs are sent to other nodes, so make sure not to change order or offset in struct. -#ifdef USES_C013 struct C013_SensorInfoStruct { C013_SensorInfoStruct(); @@ -41,6 +41,5 @@ struct C013_SensorDataStruct float Values[VARS_PER_TASK]; }; -#endif #endif // DATASTRUCTS_C013_P2P_DATASTRUCTS_H diff --git a/src/src/DataStructs/ESPEasy_EventStruct.h b/src/src/DataStructs/ESPEasy_EventStruct.h index 21fbbcdfe6..ab2ebd0f5e 100644 --- a/src/src/DataStructs/ESPEasy_EventStruct.h +++ b/src/src/DataStructs/ESPEasy_EventStruct.h @@ -1,17 +1,16 @@ -#ifndef ESPEASY_EVENTSTRUCT_H -#define ESPEASY_EVENTSTRUCT_H +#ifndef DATASTRUCTS_ESPEASY_EVENTSTRUCT_H +#define DATASTRUCTS_ESPEASY_EVENTSTRUCT_H #include #include "../DataTypes/ControllerIndex.h" #include "../DataTypes/EventValueSource.h" #include "../DataTypes/TaskIndex.h" -//#include "../Globals/CPlugins.h" -#include "../Globals/NPlugins.h" -//#include "../Globals/Plugins.h" +#include "../DataTypes/NotifierIndex.h" #include "../DataStructs/DeviceStruct.h" + /*********************************************************************************************\ * EventStruct * This should not be copied, only moved. @@ -68,4 +67,4 @@ struct EventStruct uint8_t OriginTaskIndex = 0; }; -#endif // ESPEASY_EVENTSTRUCT_H +#endif // DATASTRUCTS_ESPEASY_EVENTSTRUCT_H diff --git a/src/src/DataStructs/ESPEasy_packed_raw_data.h b/src/src/DataStructs/ESPEasy_packed_raw_data.h index 2512c11e84..9cf4c97929 100644 --- a/src/src/DataStructs/ESPEasy_packed_raw_data.h +++ b/src/src/DataStructs/ESPEasy_packed_raw_data.h @@ -1,5 +1,5 @@ -#ifndef ESPEASY_PACKED_RAW_DATA_H -#define ESPEASY_PACKED_RAW_DATA_H +#ifndef DATASTRUCT_ESPEASY_PACKED_RAW_DATA_H +#define DATASTRUCT_ESPEASY_PACKED_RAW_DATA_H #include "../../ESPEasy_common.h" @@ -80,4 +80,4 @@ String LoRa_addInt(uint64_t value, PackedData_enum datatype); String LoRa_addFloat(float value, PackedData_enum datatype); -#endif // ESPEASY_PACKED_RAW_DATA_H +#endif // DATASTRUCT_ESPEASY_PACKED_RAW_DATA_H diff --git a/src/src/DataStructs/ExtendedControllerCredentialsStruct.h b/src/src/DataStructs/ExtendedControllerCredentialsStruct.h index 4df65733a3..e83e5c0fa5 100644 --- a/src/src/DataStructs/ExtendedControllerCredentialsStruct.h +++ b/src/src/DataStructs/ExtendedControllerCredentialsStruct.h @@ -1,5 +1,5 @@ -#ifndef DATASTRUCTS_EXTENDED_SECURITYSTRUCT_H -#define DATASTRUCTS_EXTENDED_SECURITYSTRUCT_H +#ifndef DATASTRUCTS_EXTENDED_CONTROLLERCREDENTIALSSTRUCT_H +#define DATASTRUCTS_EXTENDED_CONTROLLERCREDENTIALSSTRUCT_H #include "../../ESPEasy_common.h" #include "../CustomBuild/ESPEasyLimits.h" @@ -32,4 +32,4 @@ struct ExtendedControllerCredentialsStruct -#endif // DATASTRUCTS_EXTENDED_SECURITYSTRUCT_H \ No newline at end of file +#endif // DATASTRUCTS_EXTENDED_CONTROLLERCREDENTIALSSTRUCT_H diff --git a/src/src/DataStructs/I2CTypes.h b/src/src/DataStructs/I2CTypes.h index 1fa8c24ffc..ee9760cd74 100644 --- a/src/src/DataStructs/I2CTypes.h +++ b/src/src/DataStructs/I2CTypes.h @@ -1,5 +1,5 @@ -#ifndef I2C_TYPES_H -#define I2C_TYPES_H I2C_TYPES_H +#ifndef DATASTRUCTS_I2C_TYPES_H +#define DATASTRUCTS_I2C_TYPES_H #include #include @@ -70,4 +70,4 @@ const __FlashStringHelper * toString(I2C_bus_state state); -#endif // I2C_TYPES_H +#endif // DATASTRUCTS_I2C_TYPES_H diff --git a/src/src/DataStructs/MAC_address.cpp b/src/src/DataStructs/MAC_address.cpp index 2588adcef0..b3d9c15cf7 100644 --- a/src/src/DataStructs/MAC_address.cpp +++ b/src/src/DataStructs/MAC_address.cpp @@ -10,6 +10,21 @@ MAC_address::MAC_address(const uint8_t new_mac[6]) memcpy(mac, new_mac, 6); } +MAC_address::MAC_address(const MAC_address& other) +{ + for (int i = 0; i < 6; ++i) { + mac[i] = other.mac[i]; + } +} + +MAC_address& MAC_address::operator=(const MAC_address& other) +{ + for (int i = 0; i < 6; ++i) { + mac[i] = other.mac[i]; + } + return *this; +} + bool MAC_address::set(const char *string) { unsigned u[6]; diff --git a/src/src/DataStructs/MAC_address.h b/src/src/DataStructs/MAC_address.h index 98978a2af0..b2b34ae13e 100644 --- a/src/src/DataStructs/MAC_address.h +++ b/src/src/DataStructs/MAC_address.h @@ -11,6 +11,10 @@ class __attribute__((__packed__)) MAC_address { MAC_address(const uint8_t new_mac[6]); + MAC_address(const MAC_address& other); + + MAC_address& operator=(const MAC_address& other); + bool operator==(const MAC_address& other) const { return mac_addr_cmp(other.mac); } diff --git a/src/src/DataStructs/Web_StreamingBuffer.h b/src/src/DataStructs/Web_StreamingBuffer.h index 9af118ede0..3bb4ba60ad 100644 --- a/src/src/DataStructs/Web_StreamingBuffer.h +++ b/src/src/DataStructs/Web_StreamingBuffer.h @@ -1,5 +1,5 @@ -#ifndef DATASTRUCTS_TXBUFFER_STRUCT_H -#define DATASTRUCTS_TXBUFFER_STRUCT_H +#ifndef DATASTRUCTS_WEB_STREAMINGBUFFER_H +#define DATASTRUCTS_WEB_STREAMINGBUFFER_H #include #include "../../ESPEasy_common.h" @@ -87,4 +87,4 @@ class Web_StreamingBuffer { }; -#endif // DATASTRUCTS_TXBUFFER_STRUCT_H +#endif // DATASTRUCTS_WEB_STREAMINGBUFFER_H diff --git a/src/src/DataStructs_templ/SettingsStruct.cpp b/src/src/DataStructs_templ/SettingsStruct.cpp index 5457f7d1b7..fb4f1c5b5d 100644 --- a/src/src/DataStructs_templ/SettingsStruct.cpp +++ b/src/src/DataStructs_templ/SettingsStruct.cpp @@ -1,11 +1,11 @@ #include "../DataStructs/SettingsStruct.h" -#include "../Globals/Plugins.h" -#include "../Globals/CPlugins.h" +#include "../../ESPEasy_common.h" #include "../CustomBuild/ESPEasyLimits.h" #include "../DataStructs/DeviceStruct.h" #include "../DataTypes/SPI_options.h" -#include "../../ESPEasy_common.h" +#include "../Globals/Plugins.h" +#include "../Globals/CPlugins.h" #ifndef DATASTRUCTS_SETTINGSSTRUCT_CPP #define DATASTRUCTS_SETTINGSSTRUCT_CPP diff --git a/src/src/DataTypes/CPluginID.h b/src/src/DataTypes/CPluginID.h index 075a9a6d24..b9fc144df4 100644 --- a/src/src/DataTypes/CPluginID.h +++ b/src/src/DataTypes/CPluginID.h @@ -1,5 +1,5 @@ -#ifndef DATASTRUCTS_CPLUGINID_H -#define DATASTRUCTS_CPLUGINID_H +#ifndef DATATYPES_CPLUGINID_H +#define DATATYPES_CPLUGINID_H #include @@ -9,4 +9,4 @@ typedef uint8_t cpluginID_t; extern cpluginID_t INVALID_C_PLUGIN_ID; -#endif // ifndef DATASTRUCTS_CPLUGINID_H +#endif // ifndef DATATYPES_CPLUGINID_H diff --git a/src/src/DataTypes/ControllerIndex.h b/src/src/DataTypes/ControllerIndex.h index 75f7db59b7..dc219a6fa3 100644 --- a/src/src/DataTypes/ControllerIndex.h +++ b/src/src/DataTypes/ControllerIndex.h @@ -1,5 +1,5 @@ -#ifndef DATASTRUCTS_CONTROLLERINDEX_H -#define DATASTRUCTS_CONTROLLERINDEX_H +#ifndef DATATYPES_CONTROLLERINDEX_H +#define DATATYPES_CONTROLLERINDEX_H #include diff --git a/src/src/DataTypes/DeviceIndex.cpp b/src/src/DataTypes/DeviceIndex.cpp index 1141c88e8f..64869e5531 100644 --- a/src/src/DataTypes/DeviceIndex.cpp +++ b/src/src/DataTypes/DeviceIndex.cpp @@ -2,4 +2,4 @@ #include "../CustomBuild/ESPEasyLimits.h" -deviceIndex_t INVALID_DEVICE_INDEX = PLUGIN_MAX; \ No newline at end of file +deviceIndex_t INVALID_DEVICE_INDEX = PLUGIN_MAX; diff --git a/src/src/DataTypes/DeviceIndex.h b/src/src/DataTypes/DeviceIndex.h index a2bfa4d2e0..d3d693613b 100644 --- a/src/src/DataTypes/DeviceIndex.h +++ b/src/src/DataTypes/DeviceIndex.h @@ -1,12 +1,10 @@ -#ifndef DATASTRUCT_DEVICEINDEX_H -#define DATASTRUCT_DEVICEINDEX_H +#ifndef DATATYPES_DEVICEINDEX_H +#define DATATYPES_DEVICEINDEX_H #include -typedef uint8_t deviceIndex_t; +typedef uint8_t deviceIndex_t; +extern deviceIndex_t INVALID_DEVICE_INDEX; -extern deviceIndex_t INVALID_DEVICE_INDEX; - - -#endif \ No newline at end of file +#endif // ifndef DATATYPES_DEVICEINDEX_H diff --git a/src/src/DataTypes/DeviceModel.h b/src/src/DataTypes/DeviceModel.h index 4649359a45..a55fb8df57 100644 --- a/src/src/DataTypes/DeviceModel.h +++ b/src/src/DataTypes/DeviceModel.h @@ -1,5 +1,5 @@ -#ifndef DATASTRUCTS_DEVICEMODEL_H -#define DATASTRUCTS_DEVICEMODEL_H +#ifndef DATATYPES_DEVICEMODEL_H +#define DATATYPES_DEVICEMODEL_H /********************************************************************************************\ Pre defined settings for off-the-shelf hardware @@ -32,4 +32,4 @@ enum class DeviceModel : uint8_t { }; -#endif // DATASTRUCTS_DEVICEMODEL_H \ No newline at end of file +#endif // DATATYPES_DEVICEMODEL_H \ No newline at end of file diff --git a/src/src/DataTypes/ESPEasy_plugin_functions.h b/src/src/DataTypes/ESPEasy_plugin_functions.h index 911c49bb39..a6addb6bec 100644 --- a/src/src/DataTypes/ESPEasy_plugin_functions.h +++ b/src/src/DataTypes/ESPEasy_plugin_functions.h @@ -1,5 +1,5 @@ -#ifndef ESPEASY_PLUGIN_DEFS_H -#define ESPEASY_PLUGIN_DEFS_H +#ifndef DATATYPES_ESPEASY_PLUGIN_DEFS_H +#define DATATYPES_ESPEASY_PLUGIN_DEFS_H // ******************************************************************************** @@ -105,4 +105,4 @@ class NPlugin { }; -#endif // ESPEASY_PLUGIN_DEFS_H +#endif // DATATYPES_ESPEASY_PLUGIN_DEFS_H diff --git a/src/src/DataTypes/NPluginID.cpp b/src/src/DataTypes/NPluginID.cpp new file mode 100644 index 0000000000..f4ee3a195c --- /dev/null +++ b/src/src/DataTypes/NPluginID.cpp @@ -0,0 +1,3 @@ +#include "../DataTypes/NPluginID.h" + +npluginID_t INVALID_N_PLUGIN_ID = 0; diff --git a/src/src/DataTypes/NPluginID.h b/src/src/DataTypes/NPluginID.h new file mode 100644 index 0000000000..9dc6abd749 --- /dev/null +++ b/src/src/DataTypes/NPluginID.h @@ -0,0 +1,12 @@ +#ifndef DATATYPES_NPLUGINID_H +#define DATATYPES_NPLUGINID_H + +#include + + +typedef uint8_t npluginID_t; + +extern npluginID_t INVALID_N_PLUGIN_ID; + + +#endif // ifndef DATATYPES_NPLUGINID_H diff --git a/src/src/DataTypes/NetworkMedium.h b/src/src/DataTypes/NetworkMedium.h index 17aefce040..388a6c37bc 100644 --- a/src/src/DataTypes/NetworkMedium.h +++ b/src/src/DataTypes/NetworkMedium.h @@ -1,5 +1,5 @@ -#ifndef ESPEASY_WTH_WIFI_H_ -#define ESPEASY_WTH_WIFI_H_ +#ifndef DATATYPES_NETWORKMEDIUM_H +#define DATATYPES_NETWORKMEDIUM_H #include @@ -14,4 +14,4 @@ bool isValid(NetworkMedium_t medium); const __FlashStringHelper * toString(NetworkMedium_t medium); -#endif // ESPEASY_WTH_WIFI_H_ +#endif // DATATYPES_NETWORKMEDIUM_H diff --git a/src/src/DataTypes/NotifierIndex.cpp b/src/src/DataTypes/NotifierIndex.cpp new file mode 100644 index 0000000000..cb22d0f51c --- /dev/null +++ b/src/src/DataTypes/NotifierIndex.cpp @@ -0,0 +1,5 @@ +#include "../DataTypes/NotifierIndex.h" + +#include "../CustomBuild/ESPEasyLimits.h" + +notifierIndex_t INVALID_NOTIFIER_INDEX = NOTIFICATION_MAX; diff --git a/src/src/DataTypes/NotifierIndex.h b/src/src/DataTypes/NotifierIndex.h new file mode 100644 index 0000000000..c747ac453a --- /dev/null +++ b/src/src/DataTypes/NotifierIndex.h @@ -0,0 +1,10 @@ +#ifndef DATATYPES_NOTIFIERINDEX_H +#define DATATYPES_NOTIFIERINDEX_H + +#include + +typedef uint8_t notifierIndex_t; + +extern notifierIndex_t INVALID_NOTIFIER_INDEX; + +#endif // ifndef DATATYPES_NOTIFIERINDEX_H diff --git a/src/src/DataTypes/PluginID.cpp b/src/src/DataTypes/PluginID.cpp index 72302df71f..832943791f 100644 --- a/src/src/DataTypes/PluginID.cpp +++ b/src/src/DataTypes/PluginID.cpp @@ -1,3 +1,3 @@ #include "../DataTypes/PluginID.h" -pluginID_t INVALID_PLUGIN_ID = 0; \ No newline at end of file +pluginID_t INVALID_PLUGIN_ID = 0; diff --git a/src/src/DataTypes/PluginID.h b/src/src/DataTypes/PluginID.h index 48e4b6be42..7e66733c71 100644 --- a/src/src/DataTypes/PluginID.h +++ b/src/src/DataTypes/PluginID.h @@ -1,10 +1,10 @@ -#ifndef DATASTRUCT_PLUGINID_H -#define DATASTRUCT_PLUGINID_H +#ifndef DATATYPES_PLUGINID_H +#define DATATYPES_PLUGINID_H #include -typedef uint8_t pluginID_t; +typedef uint8_t pluginID_t; -extern pluginID_t INVALID_PLUGIN_ID; +extern pluginID_t INVALID_PLUGIN_ID; -#endif \ No newline at end of file +#endif // ifndef DATATYPES_PLUGINID_H diff --git a/src/src/DataTypes/ProtocolIndex.h b/src/src/DataTypes/ProtocolIndex.h index 9167f3546f..accbd429b1 100644 --- a/src/src/DataTypes/ProtocolIndex.h +++ b/src/src/DataTypes/ProtocolIndex.h @@ -1,5 +1,5 @@ -#ifndef DATASTRUCTS_PROTOCOLINDEX_H -#define DATASTRUCTS_PROTOCOLINDEX_H +#ifndef DATATYPES_PROTOCOLINDEX_H +#define DATATYPES_PROTOCOLINDEX_H #include diff --git a/src/src/DataTypes/SPI_options.h b/src/src/DataTypes/SPI_options.h index a7e014c038..71b767e291 100644 --- a/src/src/DataTypes/SPI_options.h +++ b/src/src/DataTypes/SPI_options.h @@ -1,5 +1,5 @@ -#ifndef DATASTRUCTS_SPI_OPTIONS_H -#define DATASTRUCTS_SPI_OPTIONS_H +#ifndef DATATYPES_SPI_OPTIONS_H +#define DATATYPES_SPI_OPTIONS_H #include @@ -15,4 +15,4 @@ const __FlashStringHelper* getSPI_optionToString(SPI_Options_e option); const __FlashStringHelper* getSPI_optionToShortString(SPI_Options_e option); #endif // ifdef ESP32 -#endif // ifndef DATASTRUCTS_SPI_OPTIONS_H +#endif // ifndef DATATYPES_SPI_OPTIONS_H diff --git a/src/src/DataTypes/SettingsType.h b/src/src/DataTypes/SettingsType.h index 4e36589826..046f593a9e 100644 --- a/src/src/DataTypes/SettingsType.h +++ b/src/src/DataTypes/SettingsType.h @@ -1,5 +1,5 @@ -#ifndef DATASTRUCTS_SETTINGSTYPE_H -#define DATASTRUCTS_SETTINGSTYPE_H +#ifndef DATATYPES_SETTINGSTYPE_H +#define DATATYPES_SETTINGSTYPE_H #include "../../ESPEasy_common.h" @@ -53,4 +53,4 @@ class SettingsType { }; -#endif // DATASTRUCTS_SETTINGSTYPE_H +#endif // DATATYPES_SETTINGSTYPE_H diff --git a/src/src/DataTypes/TaskIndex.cpp b/src/src/DataTypes/TaskIndex.cpp index 7f68f61258..f2ebea9167 100644 --- a/src/src/DataTypes/TaskIndex.cpp +++ b/src/src/DataTypes/TaskIndex.cpp @@ -1,6 +1,6 @@ #include "../DataTypes/TaskIndex.h" - +#include "../../ESPEasy_common.h" taskIndex_t INVALID_TASK_INDEX = TASKS_MAX; userVarIndex_t INVALID_USERVAR_INDEX = USERVAR_MAX_INDEX; diff --git a/src/src/DataTypes/TaskIndex.h b/src/src/DataTypes/TaskIndex.h index 610e94c6b6..cefc8f73cf 100644 --- a/src/src/DataTypes/TaskIndex.h +++ b/src/src/DataTypes/TaskIndex.h @@ -1,12 +1,8 @@ -#ifndef DATASTRUCT_TASKINDEX_H -#define DATASTRUCT_TASKINDEX_H +#ifndef DATATYPES_TASKINDEX_H +#define DATATYPES_TASKINDEX_H #include -#include "../CustomBuild/ESPEasyLimits.h" - -#define USERVAR_MAX_INDEX (VARS_PER_TASK * TASKS_MAX) - typedef uint8_t taskIndex_t; typedef uint16_t userVarIndex_t; typedef uint16_t taskVarIndex_t; @@ -16,4 +12,4 @@ extern userVarIndex_t INVALID_USERVAR_INDEX; extern taskVarIndex_t INVALID_TASKVAR_INDEX; -#endif // ifndef DATASTRUCT_TASKINDEX_H +#endif // ifndef DATATYPES_TASKINDEX_H diff --git a/src/src/Globals/NPlugins.cpp b/src/src/Globals/NPlugins.cpp index 05ca1abea4..940a50d2c9 100644 --- a/src/src/Globals/NPlugins.cpp +++ b/src/src/Globals/NPlugins.cpp @@ -1,12 +1,11 @@ #include "../Globals/NPlugins.h" + #include "../DataStructs/NotificationStruct.h" #include "../Globals/Settings.h" nprotocolIndex_t INVALID_NPROTOCOL_INDEX = NPLUGIN_MAX; -notifierIndex_t INVALID_NOTIFIER_INDEX = NOTIFICATION_MAX; -npluginID_t INVALID_N_PLUGIN_ID = 0; boolean (*NPlugin_ptr[NPLUGIN_MAX])(NPlugin::Function, @@ -66,3 +65,4 @@ nprotocolIndex_t getNProtocolIndex_from_NotifierIndex(notifierIndex_t index) { } return INVALID_NPROTOCOL_INDEX; } + diff --git a/src/src/Globals/NPlugins.h b/src/src/Globals/NPlugins.h index 38f4e659e0..eb34a771f1 100644 --- a/src/src/Globals/NPlugins.h +++ b/src/src/Globals/NPlugins.h @@ -1,21 +1,22 @@ #ifndef GLOBALS_NPLUGIN_H #define GLOBALS_NPLUGIN_H -#include -#include +#include "../../ESPEasy_common.h" + #include "../CustomBuild/ESPEasyLimits.h" #include "../DataStructs/NotificationStruct.h" #include "../DataStructs/NotificationSettingsStruct.h" #include "../DataTypes/ESPEasy_plugin_functions.h" +#include "../DataTypes/NotifierIndex.h" +#include "../DataTypes/NPluginID.h" +#include +#include +#include typedef uint8_t nprotocolIndex_t; -typedef uint8_t notifierIndex_t; -typedef uint8_t npluginID_t; extern nprotocolIndex_t INVALID_NPROTOCOL_INDEX; -extern notifierIndex_t INVALID_NOTIFIER_INDEX; -extern npluginID_t INVALID_N_PLUGIN_ID; extern boolean (*NPlugin_ptr[NPLUGIN_MAX])(NPlugin::Function, @@ -39,5 +40,4 @@ String getNPluginNameFromNotifierIndex(notifierIndex_t NotifierIndex); nprotocolIndex_t getNProtocolIndex(npluginID_t Number); nprotocolIndex_t getNProtocolIndex_from_NotifierIndex(notifierIndex_t index); - #endif // GLOBALS_NPLUGIN_H diff --git a/src/src/Globals/Plugins.h b/src/src/Globals/Plugins.h index 4bbd0dccba..05abeca1ff 100644 --- a/src/src/Globals/Plugins.h +++ b/src/src/Globals/Plugins.h @@ -1,16 +1,16 @@ #ifndef GLOBALS_PLUGIN_H #define GLOBALS_PLUGIN_H +#include "../../ESPEasy_common.h" + #include #include #include "../CustomBuild/ESPEasyLimits.h" -#include "../DataStructs/ESPEasy_EventStruct.h" #include "../DataTypes/PluginID.h" #include "../DataTypes/DeviceIndex.h" #include "../DataTypes/TaskIndex.h" -#include "../../ESPEasy_common.h" /********************************************************************************************\ @@ -41,7 +41,7 @@ - USERVAR_MAX_INDEX = (TASKS_MAX * VARS_PER_TASK) \*********************************************************************************************/ - +struct EventStruct; extern int deviceCount; diff --git a/src/src/Helpers/Scheduler.cpp b/src/src/Helpers/Scheduler.cpp index a0c4ae1242..d018bf138b 100644 --- a/src/src/Helpers/Scheduler.cpp +++ b/src/src/Helpers/Scheduler.cpp @@ -11,6 +11,7 @@ #include "../ESPEasyCore/ESPEasyRules.h" #include "../Globals/GlobalMapPortStatus.h" #include "../Globals/RTC.h" +#include "../Globals/NPlugins.h" #include "../Helpers/DeepSleep.h" #include "../Helpers/ESPEasyRTC.h" #include "../Helpers/Networking.h" diff --git a/src/src/Helpers/Scheduler.h b/src/src/Helpers/Scheduler.h index 8b19a9dfcc..82ab1daa33 100644 --- a/src/src/Helpers/Scheduler.h +++ b/src/src/Helpers/Scheduler.h @@ -6,6 +6,7 @@ #include "../DataStructs/EventStructCommandWrapper.h" #include "../DataStructs/SystemTimerStruct.h" #include "../DataTypes/ProtocolIndex.h" +#include "../DataTypes/ESPEasy_plugin_functions.h" #include "../Helpers/msecTimerHandlerStruct.h" #include diff --git a/src/src/WebServer/NotificationPage.cpp b/src/src/WebServer/NotificationPage.cpp index 99763791d6..0426d1b76f 100644 --- a/src/src/WebServer/NotificationPage.cpp +++ b/src/src/WebServer/NotificationPage.cpp @@ -12,7 +12,6 @@ #include "../Helpers/ESPEasy_Storage.h" #include "../Globals/ESPEasy_Scheduler.h" -#include "../Globals/NPlugins.h" #include "../Globals/Settings.h" From ae306e1978687fd35b935215f8ba09772f67a809 Mon Sep 17 00:00:00 2001 From: TD-er Date: Sun, 23 Jan 2022 22:46:48 +0100 Subject: [PATCH 067/147] [Cleanup] Fix missing paths on #include statements + update I2Cdev lib --- lib/I2Cdevlib/I2Cdev.cpp | 432 ++++++++++-------- lib/I2Cdevlib/I2Cdev.h | 85 ++-- lib/I2Cdevlib/library.json | 10 +- platformio_esp32_envs.ini | 2 +- platformio_esp82xx_base.ini | 2 +- src/ESPEasy_common.h | 14 +- src/_N001_Email.ino | 3 - src/src/Commands/Diagnostic.cpp | 8 - src/src/Commands/Networks.cpp | 2 +- src/src/CustomBuild/define_plugin_sets.h | 2 +- .../DataStructs/EventStructCommandWrapper.h | 2 +- .../DataStructs/RTC_cache_handler_struct.h | 2 +- src/src/ESPEasyCore/ESPEasyWiFiEvent.cpp | 4 +- src/src/Globals/I2Cdev.cpp | 2 +- src/src/Helpers/DeepSleep.cpp | 2 +- src/src/Helpers/FS_Helper.h | 2 +- src/src/Helpers/Memory.cpp | 2 +- src/src/Helpers/Networking.cpp | 2 +- src/src/Helpers/StringProvider.cpp | 2 +- src/src/Helpers/_CPlugin_Helper.cpp | 2 +- src/src/WebServer/ConfigPage.cpp | 2 + 21 files changed, 318 insertions(+), 266 deletions(-) diff --git a/lib/I2Cdevlib/I2Cdev.cpp b/lib/I2Cdevlib/I2Cdev.cpp index 21d4e18875..892274ba00 100644 --- a/lib/I2Cdevlib/I2Cdev.cpp +++ b/lib/I2Cdevlib/I2Cdev.cpp @@ -3,6 +3,9 @@ // 2013-06-05 by Jeff Rowberg // // Changelog: +// 2021-09-28 - allow custom Wire object as transaction function argument +// 2020-01-20 - hardija : complete support for Teensy 3.x +// 2015-10-30 - simondlevy : support i2c_t3 for Teensy3.1 // 2013-05-06 - add Francesco Ferrara's Fastwire v0.24 implementation with small modifications // 2013-05-05 - fix issue with writing bit values to words (Sasquatch/Farzanegan) // 2012-06-09 - fix major issue with reading > 32 bytes at a time with Arduino Wire @@ -44,11 +47,8 @@ THE SOFTWARE. */ #include "I2Cdev.h" -#if defined(ESP32) - #define BUFFER_LENGTH 32 -#endif -#if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE +#if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE || I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_SBWIRE #ifdef I2CDEV_IMPLEMENTATION_WARNINGS #if ARDUINO < 100 @@ -103,9 +103,9 @@ I2Cdev::I2Cdev() { * @param timeout Optional read timeout in milliseconds (0 to disable, leave off to use default class value in I2Cdev::readTimeout) * @return Status of read operation (true = success) */ -int8_t I2Cdev::readBit(uint8_t devAddr, uint8_t regAddr, uint8_t bitNum, uint8_t *data, uint16_t timeout) { +int8_t I2Cdev::readBit(uint8_t devAddr, uint8_t regAddr, uint8_t bitNum, uint8_t *data, uint16_t timeout, void *wireObj) { uint8_t b; - uint8_t count = readByte(devAddr, regAddr, &b, timeout); + uint8_t count = readByte(devAddr, regAddr, &b, timeout, wireObj); *data = b & (1 << bitNum); return count; } @@ -118,9 +118,9 @@ int8_t I2Cdev::readBit(uint8_t devAddr, uint8_t regAddr, uint8_t bitNum, uint8_t * @param timeout Optional read timeout in milliseconds (0 to disable, leave off to use default class value in I2Cdev::readTimeout) * @return Status of read operation (true = success) */ -int8_t I2Cdev::readBitW(uint8_t devAddr, uint8_t regAddr, uint8_t bitNum, uint16_t *data, uint16_t timeout) { +int8_t I2Cdev::readBitW(uint8_t devAddr, uint8_t regAddr, uint8_t bitNum, uint16_t *data, uint16_t timeout, void *wireObj) { uint16_t b; - uint8_t count = readWord(devAddr, regAddr, &b, timeout); + uint8_t count = readWord(devAddr, regAddr, &b, timeout, wireObj); *data = b & (1 << bitNum); return count; } @@ -134,14 +134,14 @@ int8_t I2Cdev::readBitW(uint8_t devAddr, uint8_t regAddr, uint8_t bitNum, uint16 * @param timeout Optional read timeout in milliseconds (0 to disable, leave off to use default class value in I2Cdev::readTimeout) * @return Status of read operation (true = success) */ -int8_t I2Cdev::readBits(uint8_t devAddr, uint8_t regAddr, uint8_t bitStart, uint8_t length, uint8_t *data, uint16_t timeout) { +int8_t I2Cdev::readBits(uint8_t devAddr, uint8_t regAddr, uint8_t bitStart, uint8_t length, uint8_t *data, uint16_t timeout, void *wireObj) { // 01101001 read byte // 76543210 bit numbers // xxx args: bitStart=4, length=3 // 010 masked // -> 010 shifted uint8_t count, b; - if ((count = readByte(devAddr, regAddr, &b, timeout)) != 0) { + if ((count = readByte(devAddr, regAddr, &b, timeout, wireObj)) != 0) { uint8_t mask = ((1 << length) - 1) << (bitStart - length + 1); b &= mask; b >>= (bitStart - length + 1); @@ -159,7 +159,7 @@ int8_t I2Cdev::readBits(uint8_t devAddr, uint8_t regAddr, uint8_t bitStart, uint * @param timeout Optional read timeout in milliseconds (0 to disable, leave off to use default class value in I2Cdev::readTimeout) * @return Status of read operation (1 = success, 0 = failure, -1 = timeout) */ -int8_t I2Cdev::readBitsW(uint8_t devAddr, uint8_t regAddr, uint8_t bitStart, uint8_t length, uint16_t *data, uint16_t timeout) { +int8_t I2Cdev::readBitsW(uint8_t devAddr, uint8_t regAddr, uint8_t bitStart, uint8_t length, uint16_t *data, uint16_t timeout, void *wireObj) { // 1101011001101001 read byte // fedcba9876543210 bit numbers // xxx args: bitStart=12, length=3 @@ -167,7 +167,7 @@ int8_t I2Cdev::readBitsW(uint8_t devAddr, uint8_t regAddr, uint8_t bitStart, uin // -> 010 shifted uint8_t count; uint16_t w; - if ((count = readWord(devAddr, regAddr, &w, timeout)) != 0) { + if ((count = readWord(devAddr, regAddr, &w, timeout, wireObj)) != 0) { uint16_t mask = ((1 << length) - 1) << (bitStart - length + 1); w &= mask; w >>= (bitStart - length + 1); @@ -183,8 +183,8 @@ int8_t I2Cdev::readBitsW(uint8_t devAddr, uint8_t regAddr, uint8_t bitStart, uin * @param timeout Optional read timeout in milliseconds (0 to disable, leave off to use default class value in I2Cdev::readTimeout) * @return Status of read operation (true = success) */ -int8_t I2Cdev::readByte(uint8_t devAddr, uint8_t regAddr, uint8_t *data, uint16_t timeout) { - return readBytes(devAddr, regAddr, 1, data, timeout); +int8_t I2Cdev::readByte(uint8_t devAddr, uint8_t regAddr, uint8_t *data, uint16_t timeout, void *wireObj) { + return readBytes(devAddr, regAddr, 1, data, timeout, wireObj); } /** Read single word from a 16-bit device register. @@ -194,8 +194,8 @@ int8_t I2Cdev::readByte(uint8_t devAddr, uint8_t regAddr, uint8_t *data, uint16_ * @param timeout Optional read timeout in milliseconds (0 to disable, leave off to use default class value in I2Cdev::readTimeout) * @return Status of read operation (true = success) */ -int8_t I2Cdev::readWord(uint8_t devAddr, uint8_t regAddr, uint16_t *data, uint16_t timeout) { - return readWords(devAddr, regAddr, 1, data, timeout); +int8_t I2Cdev::readWord(uint8_t devAddr, uint8_t regAddr, uint16_t *data, uint16_t timeout, void *wireObj) { + return readWords(devAddr, regAddr, 1, data, timeout, wireObj); } /** Read multiple bytes from an 8-bit device register. @@ -206,7 +206,7 @@ int8_t I2Cdev::readWord(uint8_t devAddr, uint8_t regAddr, uint16_t *data, uint16 * @param timeout Optional read timeout in milliseconds (0 to disable, leave off to use default class value in I2Cdev::readTimeout) * @return Number of bytes read (-1 indicates failure) */ -int8_t I2Cdev::readBytes(uint8_t devAddr, uint8_t regAddr, uint8_t length, uint8_t *data, uint16_t timeout) { +int8_t I2Cdev::readBytes(uint8_t devAddr, uint8_t regAddr, uint8_t length, uint8_t *data, uint16_t timeout, void *wireObj) { #ifdef I2CDEV_SERIAL_DEBUG Serial.print("I2C (0x"); Serial.print(devAddr, HEX); @@ -220,71 +220,73 @@ int8_t I2Cdev::readBytes(uint8_t devAddr, uint8_t regAddr, uint8_t length, uint8 int8_t count = 0; uint32_t t1 = millis(); - #if (I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE) + #if (I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE || I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_SBWIRE || I2CDEV_IMPLEMENTATION == I2CDEV_TEENSY_3X_WIRE) + TwoWire *useWire = &Wire; + if (wireObj) useWire = (TwoWire *)wireObj; #if (ARDUINO < 100) // Arduino v00xx (before v1.0), Wire library // I2C/TWI subsystem uses internal buffer that breaks with large data requests - // so if user requests more than BUFFER_LENGTH bytes, we have to do it in + // so if user requests more than I2CDEVLIB_WIRE_BUFFER_LENGTH bytes, we have to do it in // smaller chunks instead of all at once - for (uint8_t k = 0; k < length; k += _min(length, BUFFER_LENGTH)) { - Wire.beginTransmission(devAddr); - Wire.send(regAddr); - Wire.endTransmission(); - Wire.beginTransmission(devAddr); - Wire.requestFrom(devAddr, (uint8_t)_min(length - k, BUFFER_LENGTH)); - - for (; Wire.available() && (timeout == 0 || millis() - t1 < timeout); count++) { - data[count] = Wire.receive(); + for (int k = 0; k < length; k += min((int)length, I2CDEVLIB_WIRE_BUFFER_LENGTH)) { + useWire->beginTransmission(devAddr); + useWire->send(regAddr); + useWire->endTransmission(); + useWire->beginTransmission(devAddr); + useWire->requestFrom((uint8_t)devAddr, (uint8_t)min((int)length - k, I2CDEVLIB_WIRE_BUFFER_LENGTH)); + + for (; useWire->available() && (timeout == 0 || millis() - t1 < timeout); count++) { + data[count] = useWire->receive(); #ifdef I2CDEV_SERIAL_DEBUG Serial.print(data[count], HEX); if (count + 1 < length) Serial.print(" "); #endif } - Wire.endTransmission(); + useWire->endTransmission(); } #elif (ARDUINO == 100) // Arduino v1.0.0, Wire library // Adds standardized write() and read() stream methods instead of send() and receive() // I2C/TWI subsystem uses internal buffer that breaks with large data requests - // so if user requests more than BUFFER_LENGTH bytes, we have to do it in + // so if user requests more than I2CDEVLIB_WIRE_BUFFER_LENGTH bytes, we have to do it in // smaller chunks instead of all at once - for (uint8_t k = 0; k < length; k += _min(length, BUFFER_LENGTH)) { - Wire.beginTransmission(devAddr); - Wire.write(regAddr); - Wire.endTransmission(); - Wire.beginTransmission(devAddr); - Wire.requestFrom(devAddr, (uint8_t)_min(length - k, BUFFER_LENGTH)); - - for (; Wire.available() && (timeout == 0 || millis() - t1 < timeout); count++) { - data[count] = Wire.read(); + for (int k = 0; k < length; k += min((int)length, I2CDEVLIB_WIRE_BUFFER_LENGTH)) { + useWire->beginTransmission(devAddr); + useWire->write(regAddr); + useWire->endTransmission(); + useWire->beginTransmission(devAddr); + useWire->requestFrom((uint8_t)devAddr, (uint8_t)min((int)length - k, I2CDEVLIB_WIRE_BUFFER_LENGTH)); + + for (; useWire->available() && (timeout == 0 || millis() - t1 < timeout); count++) { + data[count] = useWire->read(); #ifdef I2CDEV_SERIAL_DEBUG Serial.print(data[count], HEX); if (count + 1 < length) Serial.print(" "); #endif } - - Wire.endTransmission(); + + useWire->endTransmission(); } #elif (ARDUINO > 100) // Arduino v1.0.1+, Wire library // Adds official support for repeated start condition, yay! // I2C/TWI subsystem uses internal buffer that breaks with large data requests - // so if user requests more than BUFFER_LENGTH bytes, we have to do it in + // so if user requests more than I2CDEVLIB_WIRE_BUFFER_LENGTH bytes, we have to do it in // smaller chunks instead of all at once - for (uint8_t k = 0; k < length; k += _min(length, BUFFER_LENGTH)) { - Wire.beginTransmission(devAddr); - Wire.write(regAddr); - Wire.endTransmission(); - Wire.beginTransmission(devAddr); - Wire.requestFrom(devAddr, (uint8_t)_min(length - k, BUFFER_LENGTH)); - - for (; Wire.available() && (timeout == 0 || millis() - t1 < timeout); count++) { - data[count] = Wire.read(); + for (int k = 0; k < length; k += min((int)length, I2CDEVLIB_WIRE_BUFFER_LENGTH)) { + useWire->beginTransmission(devAddr); + useWire->write(regAddr); + useWire->endTransmission(); + useWire->beginTransmission(devAddr); + useWire->requestFrom((uint8_t)devAddr, (uint8_t)min((int)length - k, I2CDEVLIB_WIRE_BUFFER_LENGTH)); + + for (; useWire->available() && (timeout == 0 || millis() - t1 < timeout); count++) { + data[count] = useWire->read(); #ifdef I2CDEV_SERIAL_DEBUG Serial.print(data[count], HEX); if (count + 1 < length) Serial.print(" "); @@ -326,7 +328,7 @@ int8_t I2Cdev::readBytes(uint8_t devAddr, uint8_t regAddr, uint8_t length, uint8 * @param timeout Optional read timeout in milliseconds (0 to disable, leave off to use default class value in I2Cdev::readTimeout) * @return Number of words read (-1 indicates failure) */ -int8_t I2Cdev::readWords(uint8_t devAddr, uint8_t regAddr, uint8_t length, uint16_t *data, uint16_t timeout) { +int8_t I2Cdev::readWords(uint8_t devAddr, uint8_t regAddr, uint8_t length, uint16_t *data, uint16_t timeout, void *wireObj) { #ifdef I2CDEV_SERIAL_DEBUG Serial.print("I2C (0x"); Serial.print(devAddr, HEX); @@ -340,29 +342,31 @@ int8_t I2Cdev::readWords(uint8_t devAddr, uint8_t regAddr, uint8_t length, uint1 int8_t count = 0; uint32_t t1 = millis(); - #if (I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE) +#if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE || I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_SBWIRE || I2CDEV_IMPLEMENTATION == I2CDEV_TEENSY_3X_WIRE + TwoWire *useWire = &Wire; + if (wireObj) useWire = (TwoWire *)wireObj; #if (ARDUINO < 100) // Arduino v00xx (before v1.0), Wire library // I2C/TWI subsystem uses internal buffer that breaks with large data requests - // so if user requests more than BUFFER_LENGTH bytes, we have to do it in + // so if user requests more than I2CDEVLIB_WIRE_BUFFER_LENGTH bytes, we have to do it in // smaller chunks instead of all at once - for (uint8_t k = 0; k < length * 2; k += _min(length * 2, BUFFER_LENGTH)) { - Wire.beginTransmission(devAddr); - Wire.send(regAddr); - Wire.endTransmission(); - Wire.beginTransmission(devAddr); - Wire.requestFrom(devAddr, (uint8_t)(length * 2)); // length=words, this wants bytes - + for (uint8_t k = 0; k < length * 2; k += min(length * 2, I2CDEVLIB_WIRE_BUFFER_LENGTH)) { + useWire->beginTransmission(devAddr); + useWire->send(regAddr); + useWire->endTransmission(); + useWire->beginTransmission(devAddr); + useWire->requestFrom(devAddr, (uint8_t)(length * 2)); // length=words, this wants bytes + bool msb = true; // starts with MSB, then LSB - for (; Wire.available() && count < length && (timeout == 0 || millis() - t1 < timeout);) { + for (; useWire->available() && count < length && (timeout == 0 || millis() - t1 < timeout);) { if (msb) { // first byte is bits 15-8 (MSb=15) - data[count] = Wire.receive() << 8; + data[count] = useWire->receive() << 8; } else { // second byte is bits 7-0 (LSb=0) - data[count] |= Wire.receive(); + data[count] |= useWire->receive(); #ifdef I2CDEV_SERIAL_DEBUG Serial.print(data[count], HEX); if (count + 1 < length) Serial.print(" "); @@ -372,30 +376,30 @@ int8_t I2Cdev::readWords(uint8_t devAddr, uint8_t regAddr, uint8_t length, uint1 msb = !msb; } - Wire.endTransmission(); + useWire->endTransmission(); } #elif (ARDUINO == 100) // Arduino v1.0.0, Wire library // Adds standardized write() and read() stream methods instead of send() and receive() - + // I2C/TWI subsystem uses internal buffer that breaks with large data requests - // so if user requests more than BUFFER_LENGTH bytes, we have to do it in + // so if user requests more than I2CDEVLIB_WIRE_BUFFER_LENGTH bytes, we have to do it in // smaller chunks instead of all at once - for (uint8_t k = 0; k < length * 2; k += _min(length * 2, BUFFER_LENGTH)) { - Wire.beginTransmission(devAddr); - Wire.write(regAddr); - Wire.endTransmission(); - Wire.beginTransmission(devAddr); - Wire.requestFrom(devAddr, (uint8_t)(length * 2)); // length=words, this wants bytes - + for (uint8_t k = 0; k < length * 2; k += min(length * 2, I2CDEVLIB_WIRE_BUFFER_LENGTH)) { + useWire->beginTransmission(devAddr); + useWire->write(regAddr); + useWire->endTransmission(); + useWire->beginTransmission(devAddr); + useWire->requestFrom(devAddr, (uint8_t)(length * 2)); // length=words, this wants bytes + bool msb = true; // starts with MSB, then LSB - for (; Wire.available() && count < length && (timeout == 0 || millis() - t1 < timeout);) { + for (; useWire->available() && count < length && (timeout == 0 || millis() - t1 < timeout);) { if (msb) { // first byte is bits 15-8 (MSb=15) - data[count] = Wire.read() << 8; + data[count] = useWire->read() << 8; } else { // second byte is bits 7-0 (LSb=0) - data[count] |= Wire.read(); + data[count] |= useWire->read(); #ifdef I2CDEV_SERIAL_DEBUG Serial.print(data[count], HEX); if (count + 1 < length) Serial.print(" "); @@ -404,31 +408,31 @@ int8_t I2Cdev::readWords(uint8_t devAddr, uint8_t regAddr, uint8_t length, uint1 } msb = !msb; } - - Wire.endTransmission(); + + useWire->endTransmission(); } #elif (ARDUINO > 100) // Arduino v1.0.1+, Wire library // Adds official support for repeated start condition, yay! // I2C/TWI subsystem uses internal buffer that breaks with large data requests - // so if user requests more than BUFFER_LENGTH bytes, we have to do it in + // so if user requests more than I2CDEVLIB_WIRE_BUFFER_LENGTH bytes, we have to do it in // smaller chunks instead of all at once - for (uint8_t k = 0; k < length * 2; k += _min(length * 2, BUFFER_LENGTH)) { - Wire.beginTransmission(devAddr); - Wire.write(regAddr); - Wire.endTransmission(); - Wire.beginTransmission(devAddr); - Wire.requestFrom(devAddr, (uint8_t)(length * 2)); // length=words, this wants bytes - + for (uint8_t k = 0; k < length * 2; k += min(length * 2, I2CDEVLIB_WIRE_BUFFER_LENGTH)) { + useWire->beginTransmission(devAddr); + useWire->write(regAddr); + useWire->endTransmission(); + useWire->beginTransmission(devAddr); + useWire->requestFrom(devAddr, (uint8_t)(length * 2)); // length=words, this wants bytes + bool msb = true; // starts with MSB, then LSB - for (; Wire.available() && count < length && (timeout == 0 || millis() - t1 < timeout);) { + for (; useWire->available() && count < length && (timeout == 0 || millis() - t1 < timeout);) { if (msb) { // first byte is bits 15-8 (MSb=15) - data[count] = Wire.read() << 8; + data[count] = useWire->read() << 8; } else { // second byte is bits 7-0 (LSb=0) - data[count] |= Wire.read(); + data[count] |= useWire->read(); #ifdef I2CDEV_SERIAL_DEBUG Serial.print(data[count], HEX); if (count + 1 < length) Serial.print(" "); @@ -437,8 +441,8 @@ int8_t I2Cdev::readWords(uint8_t devAddr, uint8_t regAddr, uint8_t length, uint1 } msb = !msb; } - - Wire.endTransmission(); + + useWire->endTransmission(); } #endif @@ -446,8 +450,8 @@ int8_t I2Cdev::readWords(uint8_t devAddr, uint8_t regAddr, uint8_t length, uint1 // Fastwire library // no loop required for fastwire - uint16_t intermediate[(uint8_t)length]; - uint8_t status = Fastwire::readBuf(devAddr << 1, regAddr, (uint8_t *)intermediate, (uint8_t)(length * 2)); + uint8_t intermediate[(uint8_t)length*2]; + uint8_t status = Fastwire::readBuf(devAddr << 1, regAddr, intermediate, (uint8_t)(length * 2)); if (status == 0) { count = length; // success for (uint8_t i = 0; i < length; i++) { @@ -466,7 +470,7 @@ int8_t I2Cdev::readWords(uint8_t devAddr, uint8_t regAddr, uint8_t length, uint1 Serial.print(count, DEC); Serial.println(" read)."); #endif - + return count; } @@ -477,11 +481,11 @@ int8_t I2Cdev::readWords(uint8_t devAddr, uint8_t regAddr, uint8_t length, uint1 * @param value New bit value to write * @return Status of operation (true = success) */ -bool I2Cdev::writeBit(uint8_t devAddr, uint8_t regAddr, uint8_t bitNum, uint8_t data) { +bool I2Cdev::writeBit(uint8_t devAddr, uint8_t regAddr, uint8_t bitNum, uint8_t data, void *wireObj) { uint8_t b; - readByte(devAddr, regAddr, &b); + readByte(devAddr, regAddr, &b, I2Cdev::readTimeout, wireObj); b = (data != 0) ? (b | (1 << bitNum)) : (b & ~(1 << bitNum)); - return writeByte(devAddr, regAddr, b); + return writeByte(devAddr, regAddr, b, wireObj); } /** write a single bit in a 16-bit device register. @@ -491,11 +495,11 @@ bool I2Cdev::writeBit(uint8_t devAddr, uint8_t regAddr, uint8_t bitNum, uint8_t * @param value New bit value to write * @return Status of operation (true = success) */ -bool I2Cdev::writeBitW(uint8_t devAddr, uint8_t regAddr, uint8_t bitNum, uint16_t data) { +bool I2Cdev::writeBitW(uint8_t devAddr, uint8_t regAddr, uint8_t bitNum, uint16_t data, void *wireObj) { uint16_t w; - readWord(devAddr, regAddr, &w); + readWord(devAddr, regAddr, &w, I2Cdev::readTimeout, wireObj); w = (data != 0) ? (w | (1 << bitNum)) : (w & ~(1 << bitNum)); - return writeWord(devAddr, regAddr, w); + return writeWord(devAddr, regAddr, w, wireObj); } /** Write multiple bits in an 8-bit device register. @@ -506,7 +510,7 @@ bool I2Cdev::writeBitW(uint8_t devAddr, uint8_t regAddr, uint8_t bitNum, uint16_ * @param data Right-aligned value to write * @return Status of operation (true = success) */ -bool I2Cdev::writeBits(uint8_t devAddr, uint8_t regAddr, uint8_t bitStart, uint8_t length, uint8_t data) { +bool I2Cdev::writeBits(uint8_t devAddr, uint8_t regAddr, uint8_t bitStart, uint8_t length, uint8_t data, void *wireObj) { // 010 value to write // 76543210 bit numbers // xxx args: bitStart=4, length=3 @@ -515,13 +519,13 @@ bool I2Cdev::writeBits(uint8_t devAddr, uint8_t regAddr, uint8_t bitStart, uint8 // 10100011 original & ~mask // 10101011 masked | value uint8_t b; - if (readByte(devAddr, regAddr, &b) != 0) { + if (readByte(devAddr, regAddr, &b, I2Cdev::readTimeout, wireObj) != 0) { uint8_t mask = ((1 << length) - 1) << (bitStart - length + 1); data <<= (bitStart - length + 1); // shift data into correct position data &= mask; // zero all non-important bits in data b &= ~(mask); // zero all important bits in existing byte b |= data; // combine data with existing byte - return writeByte(devAddr, regAddr, b); + return writeByte(devAddr, regAddr, b, wireObj); } else { return false; } @@ -535,7 +539,7 @@ bool I2Cdev::writeBits(uint8_t devAddr, uint8_t regAddr, uint8_t bitStart, uint8 * @param data Right-aligned value to write * @return Status of operation (true = success) */ -bool I2Cdev::writeBitsW(uint8_t devAddr, uint8_t regAddr, uint8_t bitStart, uint8_t length, uint16_t data) { +bool I2Cdev::writeBitsW(uint8_t devAddr, uint8_t regAddr, uint8_t bitStart, uint8_t length, uint16_t data, void *wireObj) { // 010 value to write // fedcba9876543210 bit numbers // xxx args: bitStart=12, length=3 @@ -544,13 +548,13 @@ bool I2Cdev::writeBitsW(uint8_t devAddr, uint8_t regAddr, uint8_t bitStart, uint // 1010001110010110 original & ~mask // 1010101110010110 masked | value uint16_t w; - if (readWord(devAddr, regAddr, &w) != 0) { + if (readWord(devAddr, regAddr, &w, I2Cdev::readTimeout, wireObj) != 0) { uint16_t mask = ((1 << length) - 1) << (bitStart - length + 1); data <<= (bitStart - length + 1); // shift data into correct position data &= mask; // zero all non-important bits in data w &= ~(mask); // zero all important bits in existing word w |= data; // combine data with existing word - return writeWord(devAddr, regAddr, w); + return writeWord(devAddr, regAddr, w, wireObj); } else { return false; } @@ -562,8 +566,8 @@ bool I2Cdev::writeBitsW(uint8_t devAddr, uint8_t regAddr, uint8_t bitStart, uint * @param data New byte value to write * @return Status of operation (true = success) */ -bool I2Cdev::writeByte(uint8_t devAddr, uint8_t regAddr, uint8_t data) { - return writeBytes(devAddr, regAddr, 1, &data); +bool I2Cdev::writeByte(uint8_t devAddr, uint8_t regAddr, uint8_t data, void *wireObj) { + return writeBytes(devAddr, regAddr, 1, &data, wireObj); } /** Write single word to a 16-bit device register. @@ -572,8 +576,8 @@ bool I2Cdev::writeByte(uint8_t devAddr, uint8_t regAddr, uint8_t data) { * @param data New word value to write * @return Status of operation (true = success) */ -bool I2Cdev::writeWord(uint8_t devAddr, uint8_t regAddr, uint16_t data) { - return writeWords(devAddr, regAddr, 1, &data); +bool I2Cdev::writeWord(uint8_t devAddr, uint8_t regAddr, uint16_t data, void *wireObj) { + return writeWords(devAddr, regAddr, 1, &data, wireObj); } /** Write multiple bytes to an 8-bit device register. @@ -583,7 +587,7 @@ bool I2Cdev::writeWord(uint8_t devAddr, uint8_t regAddr, uint16_t data) { * @param data Buffer to copy new data from * @return Status of operation (true = success) */ -bool I2Cdev::writeBytes(uint8_t devAddr, uint8_t regAddr, uint8_t length, uint8_t* data) { +bool I2Cdev::writeBytes(uint8_t devAddr, uint8_t regAddr, uint8_t length, uint8_t* data, void *wireObj) { #ifdef I2CDEV_SERIAL_DEBUG Serial.print("I2C (0x"); Serial.print(devAddr, HEX); @@ -594,12 +598,20 @@ bool I2Cdev::writeBytes(uint8_t devAddr, uint8_t regAddr, uint8_t length, uint8_ Serial.print("..."); #endif uint8_t status = 0; + +#if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE || I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_SBWIRE || I2CDEV_IMPLEMENTATION == I2CDEV_TEENSY_3X_WIRE + TwoWire *useWire = &Wire; + if (wireObj) useWire = (TwoWire *)wireObj; +#endif + #if ((I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE && ARDUINO < 100) || I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_NBWIRE) - Wire.beginTransmission(devAddr); - Wire.send((uint8_t) regAddr); // send address - #elif (I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE && ARDUINO >= 100) - Wire.beginTransmission(devAddr); - Wire.write((uint8_t) regAddr); // send address + useWire->beginTransmission(devAddr); + useWire->send((uint8_t) regAddr); // send address + #elif ((I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE && ARDUINO >= 100) \ + || (I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_SBWIRE && ARDUINO >= 100) \ + || I2CDEV_IMPLEMENTATION == I2CDEV_TEENSY_3X_WIRE) + useWire->beginTransmission(devAddr); + useWire->write((uint8_t) regAddr); // send address #elif (I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_FASTWIRE) Fastwire::beginTransmission(devAddr); Fastwire::write(regAddr); @@ -610,17 +622,21 @@ bool I2Cdev::writeBytes(uint8_t devAddr, uint8_t regAddr, uint8_t length, uint8_ if (i + 1 < length) Serial.print(" "); #endif #if ((I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE && ARDUINO < 100) || I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_NBWIRE) - Wire.send((uint8_t) data[i]); - #elif (I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE && ARDUINO >= 100) - Wire.write((uint8_t) data[i]); + useWire->send((uint8_t) data[i]); + #elif ((I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE && ARDUINO >= 100) \ + || (I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_SBWIRE && ARDUINO >= 100) \ + || I2CDEV_IMPLEMENTATION == I2CDEV_TEENSY_3X_WIRE) + useWire->write((uint8_t) data[i]); #elif (I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_FASTWIRE) Fastwire::write((uint8_t) data[i]); #endif } #if ((I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE && ARDUINO < 100) || I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_NBWIRE) - Wire.endTransmission(); - #elif (I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE && ARDUINO >= 100) - status = Wire.endTransmission(); + useWire->endTransmission(); + #elif ((I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE && ARDUINO >= 100) \ + || (I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_SBWIRE && ARDUINO >= 100) \ + || I2CDEV_IMPLEMENTATION == I2CDEV_TEENSY_3X_WIRE) + status = useWire->endTransmission(); #elif (I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_FASTWIRE) Fastwire::stop(); //status = Fastwire::endTransmission(); @@ -638,7 +654,7 @@ bool I2Cdev::writeBytes(uint8_t devAddr, uint8_t regAddr, uint8_t length, uint8_ * @param data Buffer to copy new data from * @return Status of operation (true = success) */ -bool I2Cdev::writeWords(uint8_t devAddr, uint8_t regAddr, uint8_t length, uint16_t* data) { +bool I2Cdev::writeWords(uint8_t devAddr, uint8_t regAddr, uint8_t length, uint16_t* data, void *wireObj) { #ifdef I2CDEV_SERIAL_DEBUG Serial.print("I2C (0x"); Serial.print(devAddr, HEX); @@ -649,37 +665,49 @@ bool I2Cdev::writeWords(uint8_t devAddr, uint8_t regAddr, uint8_t length, uint16 Serial.print("..."); #endif uint8_t status = 0; + +#if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE || I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_SBWIRE || I2CDEV_IMPLEMENTATION == I2CDEV_TEENSY_3X_WIRE + TwoWire *useWire = &Wire; + if (wireObj) useWire = (TwoWire *)wireObj; +#endif + #if ((I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE && ARDUINO < 100) || I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_NBWIRE) - Wire.beginTransmission(devAddr); - Wire.send(regAddr); // send address - #elif (I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE && ARDUINO >= 100) - Wire.beginTransmission(devAddr); - Wire.write(regAddr); // send address + useWire->beginTransmission(devAddr); + useWire->send(regAddr); // send address + #elif ((I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE && ARDUINO >= 100) \ + || (I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_SBWIRE && ARDUINO >= 100) \ + || I2CDEV_IMPLEMENTATION == I2CDEV_TEENSY_3X_WIRE) + useWire->beginTransmission(devAddr); + useWire->write(regAddr); // send address #elif (I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_FASTWIRE) Fastwire::beginTransmission(devAddr); Fastwire::write(regAddr); #endif - for (uint8_t i = 0; i < length * 2; i++) { + for (uint8_t i = 0; i < length; i++) { #ifdef I2CDEV_SERIAL_DEBUG Serial.print(data[i], HEX); if (i + 1 < length) Serial.print(" "); #endif #if ((I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE && ARDUINO < 100) || I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_NBWIRE) - Wire.send((uint8_t)(data[i] >> 8)); // send MSB - Wire.send((uint8_t)data[i++]); // send LSB - #elif (I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE && ARDUINO >= 100) - Wire.write((uint8_t)(data[i] >> 8)); // send MSB - Wire.write((uint8_t)data[i++]); // send LSB + useWire->send((uint8_t)(data[i] >> 8)); // send MSB + useWire->send((uint8_t)data[i]); // send LSB + #elif ((I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE && ARDUINO >= 100) \ + || (I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_SBWIRE && ARDUINO >= 100) \ + || I2CDEV_IMPLEMENTATION == I2CDEV_TEENSY_3X_WIRE) + useWire->write((uint8_t)(data[i] >> 8)); // send MSB + useWire->write((uint8_t)data[i]); // send LSB #elif (I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_FASTWIRE) Fastwire::write((uint8_t)(data[i] >> 8)); // send MSB - status = Fastwire::write((uint8_t)data[i++]); // send LSB + status = Fastwire::write((uint8_t)data[i]); // send LSB if (status != 0) break; #endif } #if ((I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE && ARDUINO < 100) || I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_NBWIRE) - Wire.endTransmission(); - #elif (I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE && ARDUINO >= 100) - status = Wire.endTransmission(); + useWire->endTransmission(); + #elif ((I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE && ARDUINO >= 100) \ + || (I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_SBWIRE && ARDUINO >= 100) \ + || I2CDEV_IMPLEMENTATION == I2CDEV_TEENSY_3X_WIRE) + status = useWire->endTransmission(); #elif (I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_FASTWIRE) Fastwire::stop(); //status = Fastwire::endTransmission(); @@ -739,15 +767,15 @@ uint16_t I2Cdev::readTimeout = I2CDEV_DEFAULT_READ_TIMEOUT; #endif TWSR = 0; // no prescaler => prescaler = 1 - TWBR = ((16000L / khz) - 16) / 2; // change the I2C clock rate + TWBR = F_CPU / 2000 / khz - 8; // change the I2C clock rate TWCR = 1 << TWEN; // enable twi module, no interrupt } // added by Jeff Rowberg 2013-05-07: // Arduino Wire-style "beginTransmission" function // (takes 7-bit device address like the Wire method, NOT 8-bit: 0x68, not 0xD0/0xD1) - uint8_t Fastwire::beginTransmission(uint8_t device) { - uint8_t twst, retry; + byte Fastwire::beginTransmission(byte device) { + byte twst, retry; retry = 2; do { TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWSTO) | (1 << TWSTA); @@ -766,8 +794,8 @@ uint16_t I2Cdev::readTimeout = I2CDEV_DEFAULT_READ_TIMEOUT; return 0; } - uint8_t Fastwire::writeBuf(uint8_t device, uint8_t address, uint8_t *data, uint8_t num) { - uint8_t twst, retry; + byte Fastwire::writeBuf(byte device, byte address, byte *data, byte num) { + byte twst, retry; retry = 2; do { @@ -793,7 +821,7 @@ uint16_t I2Cdev::readTimeout = I2CDEV_DEFAULT_READ_TIMEOUT; twst = TWSR & 0xF8; if (twst != TW_MT_DATA_ACK) return 6; - for (uint8_t i = 0; i < num; i++) { + for (byte i = 0; i < num; i++) { //Serial.print(data[i], HEX); //Serial.print(" "); TWDR = data[i]; // send data to the previously addressed device @@ -807,8 +835,8 @@ uint16_t I2Cdev::readTimeout = I2CDEV_DEFAULT_READ_TIMEOUT; return 0; } - uint8_t Fastwire::write(uint8_t value) { - uint8_t twst; + byte Fastwire::write(byte value) { + byte twst; //Serial.println(value, HEX); TWDR = value; // send data TWCR = (1 << TWINT) | (1 << TWEN); @@ -818,8 +846,8 @@ uint16_t I2Cdev::readTimeout = I2CDEV_DEFAULT_READ_TIMEOUT; return 0; } - uint8_t Fastwire::readBuf(uint8_t device, uint8_t address, uint8_t *data, uint8_t num) { - uint8_t twst, retry; + byte Fastwire::readBuf(byte device, byte address, byte *data, byte num) { + byte twst, retry; retry = 2; do { @@ -885,7 +913,7 @@ uint16_t I2Cdev::readTimeout = I2CDEV_DEFAULT_READ_TIMEOUT; TWCR = 0; } - uint8_t Fastwire::stop() { + byte Fastwire::stop() { TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWSTO); if (!waitInt()) return 1; return 0; @@ -899,50 +927,50 @@ uint16_t I2Cdev::readTimeout = I2CDEV_DEFAULT_READ_TIMEOUT; /* call this version 1.0 - + Offhand, the only funky part that I can think of is in nbrequestFrom, where the buffer length and index are set *before* the data is actually read. The problem is that these are variables local to the TwoWire object, and by the time we actually have read the - data, and know what the length actually is, we have no simple access to the object's + data, and know what the length actually is, we have no simple access to the object's variables. The actual bytes read *is* given to the callback function, though. - + The ISR code for a slave receiver is commented out. I don't have that setup, and can't verify it at this time. Save it for 2.0! - - The handling of the read and write processes here is much like in the demo sketch code: + + The handling of the read and write processes here is much like in the demo sketch code: the process is broken down into sequential functions, where each registers the next as a callback, essentially. - - For example, for the Read process, twi_read00 just returns if TWI is not yet in a + + For example, for the Read process, twi_read00 just returns if TWI is not yet in a ready state. When there's another interrupt, and the interface *is* ready, then it sets up the read, starts it, and registers twi_read01 as the function to call after the *next* interrupt. twi_read01, then, just returns if the interface is still in a "reading" state. When the reading is done, it copies the information to the buffer, - cleans up, and calls the user-requested callback function with the actual number of + cleans up, and calls the user-requested callback function with the actual number of bytes read. - + The writing is similar. - + Questions, comments and problems can go to Gene@Telobot.com. - + Thumbs Up! Gene Knight - + */ - + uint8_t TwoWire::rxBuffer[NBWIRE_BUFFER_LENGTH]; uint8_t TwoWire::rxBufferIndex = 0; uint8_t TwoWire::rxBufferLength = 0; - + uint8_t TwoWire::txAddress = 0; uint8_t TwoWire::txBuffer[NBWIRE_BUFFER_LENGTH]; uint8_t TwoWire::txBufferIndex = 0; uint8_t TwoWire::txBufferLength = 0; - + //uint8_t TwoWire::transmitting = 0; void (*TwoWire::user_onRequest)(void); void (*TwoWire::user_onReceive)(int); - + static volatile uint8_t twi_transmitting; static volatile uint8_t twi_state; static uint8_t twi_slarw; @@ -957,7 +985,7 @@ uint16_t I2Cdev::readTimeout = I2CDEV_DEFAULT_READ_TIMEOUT; static volatile uint8_t twi_Done; void (*twi_cbendTransmissionDone)(int); void (*twi_cbreadFromDone)(int); - + void twi_init() { // initialize state twi_state = TWI_READY; @@ -979,7 +1007,7 @@ uint16_t I2Cdev::readTimeout = I2CDEV_DEFAULT_READ_TIMEOUT; TWBR = ((CPU_FREQ / TWI_FREQ) - 16) / 2; // bitrate register // enable twi module, acks, and twi interrupt - TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWEA); + TWCR = (1 << TWEN) | (1 << TWIE) | (1 << TWEA); /* TWEN - TWI Enable Bit TWIE - TWI Interrupt Enable @@ -988,7 +1016,7 @@ uint16_t I2Cdev::readTimeout = I2CDEV_DEFAULT_READ_TIMEOUT; TWSTA - TWI Start Condition */ } - + typedef struct { uint8_t address; uint8_t* data; @@ -1000,7 +1028,7 @@ uint16_t I2Cdev::readTimeout = I2CDEV_DEFAULT_READ_TIMEOUT; twi_Write_Vars *ptwv = 0; static void (*fNextInterruptFunction)(void) = 0; - void twi_Finish(uint8_t bRetVal) { + void twi_Finish(byte bRetVal) { if (ptwv) { free(ptwv); ptwv = 0; @@ -1009,13 +1037,13 @@ uint16_t I2Cdev::readTimeout = I2CDEV_DEFAULT_READ_TIMEOUT; twi_Return_Value = bRetVal; fNextInterruptFunction = 0; } - + uint8_t twii_WaitForDone(uint16_t timeout) { uint32_t endMillis = millis() + timeout; while (!twi_Done && (timeout == 0 || millis() < endMillis)) continue; return twi_Return_Value; } - + void twii_SetState(uint8_t ucState) { twi_state = ucState; } @@ -1048,7 +1076,7 @@ uint16_t I2Cdev::readTimeout = I2CDEV_DEFAULT_READ_TIMEOUT; } void twii_SetStart() { - TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWEA) | _BV(TWINT) | _BV(TWSTA); + TWCR = (1 << TWEN) | (1 << TWIE) | (1 << TWEA) | (1 << TWINT) | (1 << TWSTA); } void twi_write01() { @@ -1065,8 +1093,8 @@ uint16_t I2Cdev::readTimeout = I2CDEV_DEFAULT_READ_TIMEOUT; if (twi_cbendTransmissionDone) return twi_cbendTransmissionDone(twi_Return_Value); return; } - - + + void twi_write00() { if (TWI_READY != twi_state) return; // blocking test if (TWI_BUFFER_LENGTH < ptwv -> length) { @@ -1083,7 +1111,7 @@ uint16_t I2Cdev::readTimeout = I2CDEV_DEFAULT_READ_TIMEOUT; fNextInterruptFunction = twi_write01; // next routine return twi_write01(); } - + void twi_writeTo(uint8_t address, uint8_t* data, uint8_t length, uint8_t wait) { uint8_t i; ptwv = (twi_Write_Vars *)malloc(sizeof(twi_Write_Vars)); @@ -1103,7 +1131,7 @@ uint16_t I2Cdev::readTimeout = I2CDEV_DEFAULT_READ_TIMEOUT; if (twi_cbreadFromDone) return twi_cbreadFromDone(twi_Return_Value); return; } - + void twi_read00() { if (TWI_READY != twi_state) return; // blocking test if (TWI_BUFFER_LENGTH < ptwv -> length) twi_Finish(0); // error return @@ -1131,34 +1159,34 @@ uint16_t I2Cdev::readTimeout = I2CDEV_DEFAULT_READ_TIMEOUT; void twi_reply(uint8_t ack) { // transmit master read ready signal, with or without ack if (ack){ - TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWINT) | _BV(TWEA); + TWCR = (1 << TWEN) | (1 << TWIE) | (1 << TWINT) | (1 << TWEA); } else { - TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWINT); + TWCR = (1 << TWEN) | (1 << TWIE) | (1 << TWINT); } } - + void twi_stop(void) { // send stop condition - TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWEA) | _BV(TWINT) | _BV(TWSTO); - + TWCR = (1 << TWEN) | (1 << TWIE) | (1 << TWEA) | (1 << TWINT) | (1 << TWSTO); + // wait for stop condition to be exectued on bus // TWINT is not set after a stop condition! - while (TWCR & _BV(TWSTO)) { + while (TWCR & (1 << TWSTO)) { continue; } - + // update twi state twi_state = TWI_READY; } void twi_releaseBus(void) { // release bus - TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWEA) | _BV(TWINT); - + TWCR = (1 << TWEN) | (1 << TWIE) | (1 << TWEA) | (1 << TWINT); + // update twi state twi_state = TWI_READY; } - + SIGNAL(TWI_vect) { switch (TW_STATUS) { // All Master @@ -1168,7 +1196,7 @@ uint16_t I2Cdev::readTimeout = I2CDEV_DEFAULT_READ_TIMEOUT; TWDR = twi_slarw; twi_reply(1); break; - + // Master Transmitter case TW_MT_SLA_ACK: // slave receiver acked address case TW_MT_DATA_ACK: // slave receiver acked data @@ -1196,7 +1224,7 @@ uint16_t I2Cdev::readTimeout = I2CDEV_DEFAULT_READ_TIMEOUT; twi_error = TW_MT_ARB_LOST; twi_releaseBus(); break; - + // Master Receiver case TW_MR_DATA_ACK: // data received, ack sent // put byte into buffer @@ -1294,7 +1322,7 @@ uint16_t I2Cdev::readTimeout = I2CDEV_DEFAULT_READ_TIMEOUT; twi_txBufferLength = 1; twi_txBuffer[0] = 0x00; } - + // transmit first byte from buffer, fall through case TW_ST_DATA_ACK: // byte sent, ack returned @@ -1332,26 +1360,26 @@ uint16_t I2Cdev::readTimeout = I2CDEV_DEFAULT_READ_TIMEOUT; } TwoWire::TwoWire() { } - + void TwoWire::begin(void) { rxBufferIndex = 0; rxBufferLength = 0; - + txBufferIndex = 0; txBufferLength = 0; twi_init(); } - + void TwoWire::beginTransmission(uint8_t address) { //beginTransmission((uint8_t)address); // indicate that we are transmitting twi_transmitting = 1; - + // set address of targeted slave txAddress = address; - + // reset tx buffer iterator vars txBufferIndex = 0; txBufferLength = 0; @@ -1378,7 +1406,7 @@ uint16_t I2Cdev::readTimeout = I2CDEV_DEFAULT_READ_TIMEOUT; twi_writeTo(txAddress, txBuffer, txBufferLength, 1); return; } - + void TwoWire::send(uint8_t data) { if (twi_transmitting) { // in master transmitter mode @@ -1399,21 +1427,21 @@ uint16_t I2Cdev::readTimeout = I2CDEV_DEFAULT_READ_TIMEOUT; //twi_transmit(&data, 1); } } - + uint8_t TwoWire::receive(void) { // default to returning null char // for people using with char strings uint8_t value = 0; - + // get each successive byte on each call if (rxBufferIndex < rxBufferLength) { value = rxBuffer[rxBufferIndex]; ++rxBufferIndex; } - + return value; } - + uint8_t TwoWire::requestFrom(uint8_t address, int quantity, uint16_t timeout) { // clamp to buffer length if (quantity > NBWIRE_BUFFER_LENGTH) { @@ -1428,10 +1456,10 @@ uint16_t I2Cdev::readTimeout = I2CDEV_DEFAULT_READ_TIMEOUT; // set rx buffer iterator vars rxBufferIndex = 0; rxBufferLength = read; - + return read; } - + void TwoWire::nbrequestFrom(uint8_t address, int quantity, void (*function)(int)) { // clamp to buffer length if (quantity > NBWIRE_BUFFER_LENGTH) { diff --git a/lib/I2Cdevlib/I2Cdev.h b/lib/I2Cdevlib/I2Cdev.h index 007281de68..5b59c56ff2 100644 --- a/lib/I2Cdevlib/I2Cdev.h +++ b/lib/I2Cdevlib/I2Cdev.h @@ -3,6 +3,8 @@ // 2013-06-05 by Jeff Rowberg // // Changelog: +// 2021-09-28 - allow custom Wire object as transaction function argument +// 2020-01-20 - hardija : complete support for Teensy 3.x // 2015-10-30 - simondlevy : support i2c_t3 for Teensy3.1 // 2013-05-06 - add Francesco Ferrara's Fastwire v0.24 implementation with small modifications // 2013-05-05 - fix issue with writing bit values to words (Sasquatch/Farzanegan) @@ -47,11 +49,18 @@ THE SOFTWARE. #ifndef _I2CDEV_H_ #define _I2CDEV_H_ +// ----------------------------------------------------------------------------- +// Enable deprecated pgmspace typedefs in avr-libc +// ----------------------------------------------------------------------------- +#define __PROG_TYPES_COMPAT__ + // ----------------------------------------------------------------------------- // I2C interface implementation setting // ----------------------------------------------------------------------------- #ifndef I2CDEV_IMPLEMENTATION #define I2CDEV_IMPLEMENTATION I2CDEV_ARDUINO_WIRE +//#define I2CDEV_IMPLEMENTATION I2CDEV_TEENSY_3X_WIRE +//#define I2CDEV_IMPLEMENTATION I2CDEV_BUILTIN_SBWIRE //#define I2CDEV_IMPLEMENTATION I2CDEV_BUILTIN_FASTWIRE #endif // I2CDEV_IMPLEMENTATION @@ -67,6 +76,8 @@ THE SOFTWARE. // ^^^ NBWire implementation is still buggy w/some interrupts! #define I2CDEV_BUILTIN_FASTWIRE 3 // FastWire object from Francesco Ferrara's project #define I2CDEV_I2CMASTER_LIBRARY 4 // I2C object from DSSCircuits I2C-Master Library at https://github.com/DSSCircuits/I2C-Master-Library +#define I2CDEV_BUILTIN_SBWIRE 5 // I2C object from Shuning (Steve) Bian's SBWire Library at https://github.com/freespace/SBWire +#define I2CDEV_TEENSY_3X_WIRE 6 // Teensy 3.x support using i2c_t3 library // ----------------------------------------------------------------------------- // Arduino-style "Serial.print" debug constant (uncomment to enable) @@ -82,16 +93,38 @@ THE SOFTWARE. #if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE #include #endif + #if I2CDEV_IMPLEMENTATION == I2CDEV_TEENSY_3X_WIRE + #include + #endif #if I2CDEV_IMPLEMENTATION == I2CDEV_I2CMASTER_LIBRARY #include #endif + #if I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_SBWIRE + #include "SBWire.h" + #endif #endif #ifdef SPARK - #include + #include "application.h" #define ARDUINO 101 + #define BUFFER_LENGTH 32 #endif +#ifndef I2CDEVLIB_WIRE_BUFFER_LENGTH + #if defined(I2C_BUFFER_LENGTH) + // Arduino ESP32 core Wire uses this + #define I2CDEVLIB_WIRE_BUFFER_LENGTH I2C_BUFFER_LENGTH + #elif defined(BUFFER_LENGTH) + // Arduino AVR core Wire and many others use this + #define I2CDEVLIB_WIRE_BUFFER_LENGTH BUFFER_LENGTH + #elif defined(SERIAL_BUFFER_SIZE) + // Arduino SAMD core Wire uses this + #define I2CDEVLIB_WIRE_BUFFER_LENGTH SERIAL_BUFFER_SIZE + #else + // should be a safe fallback, though possibly inefficient + #define I2CDEVLIB_WIRE_BUFFER_LENGTH 32 + #endif +#endif // 1000ms default read timeout (modify with "I2Cdev::readTimeout = [ms];") #define I2CDEV_DEFAULT_READ_TIMEOUT 1000 @@ -100,23 +133,23 @@ class I2Cdev { public: I2Cdev(); - static int8_t readBit(uint8_t devAddr, uint8_t regAddr, uint8_t bitNum, uint8_t *data, uint16_t timeout=I2Cdev::readTimeout); - static int8_t readBitW(uint8_t devAddr, uint8_t regAddr, uint8_t bitNum, uint16_t *data, uint16_t timeout=I2Cdev::readTimeout); - static int8_t readBits(uint8_t devAddr, uint8_t regAddr, uint8_t bitStart, uint8_t length, uint8_t *data, uint16_t timeout=I2Cdev::readTimeout); - static int8_t readBitsW(uint8_t devAddr, uint8_t regAddr, uint8_t bitStart, uint8_t length, uint16_t *data, uint16_t timeout=I2Cdev::readTimeout); - static int8_t readByte(uint8_t devAddr, uint8_t regAddr, uint8_t *data, uint16_t timeout=I2Cdev::readTimeout); - static int8_t readWord(uint8_t devAddr, uint8_t regAddr, uint16_t *data, uint16_t timeout=I2Cdev::readTimeout); - static int8_t readBytes(uint8_t devAddr, uint8_t regAddr, uint8_t length, uint8_t *data, uint16_t timeout=I2Cdev::readTimeout); - static int8_t readWords(uint8_t devAddr, uint8_t regAddr, uint8_t length, uint16_t *data, uint16_t timeout=I2Cdev::readTimeout); - - static bool writeBit(uint8_t devAddr, uint8_t regAddr, uint8_t bitNum, uint8_t data); - static bool writeBitW(uint8_t devAddr, uint8_t regAddr, uint8_t bitNum, uint16_t data); - static bool writeBits(uint8_t devAddr, uint8_t regAddr, uint8_t bitStart, uint8_t length, uint8_t data); - static bool writeBitsW(uint8_t devAddr, uint8_t regAddr, uint8_t bitStart, uint8_t length, uint16_t data); - static bool writeByte(uint8_t devAddr, uint8_t regAddr, uint8_t data); - static bool writeWord(uint8_t devAddr, uint8_t regAddr, uint16_t data); - static bool writeBytes(uint8_t devAddr, uint8_t regAddr, uint8_t length, uint8_t *data); - static bool writeWords(uint8_t devAddr, uint8_t regAddr, uint8_t length, uint16_t *data); + static int8_t readBit(uint8_t devAddr, uint8_t regAddr, uint8_t bitNum, uint8_t *data, uint16_t timeout=I2Cdev::readTimeout, void *wireObj=0); + static int8_t readBitW(uint8_t devAddr, uint8_t regAddr, uint8_t bitNum, uint16_t *data, uint16_t timeout=I2Cdev::readTimeout, void *wireObj=0); + static int8_t readBits(uint8_t devAddr, uint8_t regAddr, uint8_t bitStart, uint8_t length, uint8_t *data, uint16_t timeout=I2Cdev::readTimeout, void *wireObj=0); + static int8_t readBitsW(uint8_t devAddr, uint8_t regAddr, uint8_t bitStart, uint8_t length, uint16_t *data, uint16_t timeout=I2Cdev::readTimeout, void *wireObj=0); + static int8_t readByte(uint8_t devAddr, uint8_t regAddr, uint8_t *data, uint16_t timeout=I2Cdev::readTimeout, void *wireObj=0); + static int8_t readWord(uint8_t devAddr, uint8_t regAddr, uint16_t *data, uint16_t timeout=I2Cdev::readTimeout, void *wireObj=0); + static int8_t readBytes(uint8_t devAddr, uint8_t regAddr, uint8_t length, uint8_t *data, uint16_t timeout=I2Cdev::readTimeout, void *wireObj=0); + static int8_t readWords(uint8_t devAddr, uint8_t regAddr, uint8_t length, uint16_t *data, uint16_t timeout=I2Cdev::readTimeout, void *wireObj=0); + + static bool writeBit(uint8_t devAddr, uint8_t regAddr, uint8_t bitNum, uint8_t data, void *wireObj=0); + static bool writeBitW(uint8_t devAddr, uint8_t regAddr, uint8_t bitNum, uint16_t data, void *wireObj=0); + static bool writeBits(uint8_t devAddr, uint8_t regAddr, uint8_t bitStart, uint8_t length, uint8_t data, void *wireObj=0); + static bool writeBitsW(uint8_t devAddr, uint8_t regAddr, uint8_t bitStart, uint8_t length, uint16_t data, void *wireObj=0); + static bool writeByte(uint8_t devAddr, uint8_t regAddr, uint8_t data, void *wireObj=0); + static bool writeWord(uint8_t devAddr, uint8_t regAddr, uint16_t data, void *wireObj=0); + static bool writeBytes(uint8_t devAddr, uint8_t regAddr, uint8_t length, uint8_t *data, void *wireObj=0); + static bool writeWords(uint8_t devAddr, uint8_t regAddr, uint8_t length, uint16_t *data, void *wireObj=0); static uint16_t readTimeout; }; @@ -156,12 +189,12 @@ class I2Cdev { public: static void setup(int khz, boolean pullup); - static uint8_t beginTransmission(uint8_t device); - static uint8_t write(uint8_t value); - static uint8_t writeBuf(uint8_t device, uint8_t address, uint8_t *data, uint8_t num); - static uint8_t readBuf(uint8_t device, uint8_t address, uint8_t *data, uint8_t num); + static byte beginTransmission(byte device); + static byte write(byte value); + static byte writeBuf(byte device, byte address, byte *data, byte num); + static byte readBuf(byte device, byte address, byte *data, byte num); static void reset(); - static uint8_t stop(); + static byte stop(); }; #endif @@ -229,7 +262,7 @@ class I2Cdev { /* TWI Status is in TWSR, in the top 5 bits: TWS7 - TWS3 */ - #define TW_STATUS_MASK (_BV(TWS7)|_BV(TWS6)|_BV(TWS5)|_BV(TWS4)|_BV(TWS3)) + #define TW_STATUS_MASK ((1 << TWS7)|(1 << TWS6)|(1 << TWS5)|(1 << TWS4)|(1 << TWS3)) #define TW_STATUS (TWSR & TW_STATUS_MASK) #define TW_START 0x08 #define TW_REP_START 0x10 @@ -264,11 +297,11 @@ class I2Cdev { //#define _SFR_BYTE(sfr) _MMIO_BYTE(_SFR_ADDR(sfr)) #ifndef sbi // set bit - #define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit)) + #define sbi(sfr, bit) (_SFR_BYTE(sfr) |= (1 << bit)) #endif // sbi #ifndef cbi // clear bit - #define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit)) + #define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~(1 << bit)) #endif // cbi extern TwoWire Wire; diff --git a/lib/I2Cdevlib/library.json b/lib/I2Cdevlib/library.json index e974a9ab31..5c5eab8fbb 100644 --- a/lib/I2Cdevlib/library.json +++ b/lib/I2Cdevlib/library.json @@ -9,10 +9,10 @@ "url": "https://github.com/jrowberg/i2cdevlib.git" }, "frameworks": "arduino", - "platforms": "atmelavr", - "dependencies": [ - { - "name": "Wire" - } + "platforms": "*", + "dependencies": [ + { + "name": "Wire" + } ] } diff --git a/platformio_esp32_envs.ini b/platformio_esp32_envs.ini index 3fb205386d..05e60c3dbb 100644 --- a/platformio_esp32_envs.ini +++ b/platformio_esp32_envs.ini @@ -9,7 +9,7 @@ lib_ignore = ESP8266WiFi, ESP8266Ping, ESP8266WebServer, ESP8266H [esp32_base] extends = common, core_esp32_IDF4_4__2_0_2_1 -lib_deps = td-er/ESPeasySerial @ 2.0.8, adafruit/Adafruit ILI9341 @ ^1.5.6, Adafruit GFX Library, LOLIN_EPD, adafruit/Adafruit BusIO @ ^1.10.0, VL53L0X @ 1.3.0, SparkFun VL53L1X 4m Laser Distance Sensor @ 1.2.9, td-er/SparkFun MAX1704x Fuel Gauge Arduino Library @ ^1.0.1, ArduinoOTA, ESP32HTTPUpdateServer, FrogmoreScd30, Multi Channel Relay Arduino Library, SparkFun ADXL345 Arduino Library, ITG3205 +lib_deps = td-er/ESPeasySerial @ 2.0.8, I2Cdevlib-Core, adafruit/Adafruit ILI9341 @ ^1.5.6, Adafruit GFX Library, LOLIN_EPD, adafruit/Adafruit BusIO @ ^1.10.0, VL53L0X @ 1.3.0, SparkFun VL53L1X 4m Laser Distance Sensor @ 1.2.9, td-er/SparkFun MAX1704x Fuel Gauge Arduino Library @ ^1.0.1, ArduinoOTA, ESP32HTTPUpdateServer, FrogmoreScd30, Multi Channel Relay Arduino Library, SparkFun ADXL345 Arduino Library, ITG3205 extra_scripts = post:tools/pio/post_esp32.py ${extra_scripts_default.extra_scripts} build_unflags = -Wall diff --git a/platformio_esp82xx_base.ini b/platformio_esp82xx_base.ini index b12eeaebb2..37a00e72fd 100644 --- a/platformio_esp82xx_base.ini +++ b/platformio_esp82xx_base.ini @@ -52,7 +52,7 @@ build_flags = ${debug_flags.build_flags} ${mqtt_flags.build_flags} -DHTTPCLIENT_1_1_COMPATIBLE=0 build_unflags = -DDEBUG_ESP_PORT -lib_deps = td-er/ESPeasySerial @ 2.0.8, adafruit/Adafruit ILI9341 @ ^1.5.6, Adafruit GFX Library, LOLIN_EPD, adafruit/Adafruit BusIO @ ^1.10.0, bblanchon/ArduinoJson @ ^6.17.2, VL53L0X @ 1.3.0, SparkFun VL53L1X 4m Laser Distance Sensor @ 1.2.9, td-er/RABurton ESP8266 Mutex @ ^1.0.2, td-er/SparkFun MAX1704x Fuel Gauge Arduino Library @ ^1.0.1, ESP8266HTTPUpdateServer, FrogmoreScd30, Multi Channel Relay Arduino Library, SparkFun ADXL345 Arduino Library, ITG3205 +lib_deps = td-er/ESPeasySerial @ 2.0.8, I2Cdevlib-Core, adafruit/Adafruit ILI9341 @ ^1.5.6, Adafruit GFX Library, LOLIN_EPD, adafruit/Adafruit BusIO @ ^1.10.0, bblanchon/ArduinoJson @ ^6.17.2, VL53L0X @ 1.3.0, SparkFun VL53L1X 4m Laser Distance Sensor @ 1.2.9, td-er/RABurton ESP8266 Mutex @ ^1.0.2, td-er/SparkFun MAX1704x Fuel Gauge Arduino Library @ ^1.0.1, ESP8266HTTPUpdateServer, FrogmoreScd30, Multi Channel Relay Arduino Library, SparkFun ADXL345 Arduino Library, ITG3205 lib_ignore = ${esp82xx_defaults.lib_ignore}, IRremoteESP8266, HeatpumpIR, LittleFS(esp8266), ServoESP32, TinyWireM monitor_filters = esp8266_exception_decoder extra_scripts = pre:tools/pio/pre_default_check.py diff --git a/src/ESPEasy_common.h b/src/ESPEasy_common.h index 8f914b55c0..ae797157d4 100644 --- a/src/ESPEasy_common.h +++ b/src/ESPEasy_common.h @@ -64,7 +64,7 @@ namespace std #define FS_NO_GLOBALS #if defined(ESP8266) - #include "core_version.h" + #include #define NODE_TYPE_ID NODE_TYPE_ID_ESP_EASYM_STD #define FILE_CONFIG "config.dat" #define FILE_SECURITY "security.dat" @@ -84,13 +84,13 @@ namespace std #ifndef LWIP_OPEN_SRC #define LWIP_OPEN_SRC #endif - #include "lwip/opt.h" - #include "lwip/udp.h" - #include "lwip/igmp.h" - #include "include/UdpContext.h" - #include "limits.h" + #include + #include + #include + #include + #include extern "C" { - #include "user_interface.h" + #include } #define SMALLEST_OTA_IMAGE 276848 // smallest known 2-step OTA image diff --git a/src/_N001_Email.ino b/src/_N001_Email.ino index c75993e107..58c2e336ff 100644 --- a/src/_N001_Email.ino +++ b/src/_N001_Email.ino @@ -22,13 +22,10 @@ #include "src/Helpers/StringParser.h" #include "src/Helpers/_CPlugin_Helper.h" // safeReadStringUntil - // Forward declaration boolean NPlugin_001_send(const NotificationSettingsStruct& notificationsettings, const String& aSub, String& aMesg); - // The message body is included in event->String1 - boolean NPlugin_001(NPlugin::Function function, struct EventStruct *event, String& string) { boolean success = false; diff --git a/src/src/Commands/Diagnostic.cpp b/src/src/Commands/Diagnostic.cpp index f20afbee93..dff3c072c2 100644 --- a/src/src/Commands/Diagnostic.cpp +++ b/src/src/Commands/Diagnostic.cpp @@ -1,13 +1,5 @@ #include "../Commands/Diagnostic.h" -/* - #include "Common.h" - #include "../../ESPEasy_common.h" - - #include "../DataStructs/ESPEasy_EventStruct.h" - */ - - #include "../Commands/Common.h" diff --git a/src/src/Commands/Networks.cpp b/src/src/Commands/Networks.cpp index 1357ff06cf..24775fd9d9 100644 --- a/src/src/Commands/Networks.cpp +++ b/src/src/Commands/Networks.cpp @@ -7,7 +7,7 @@ #ifdef HAS_ETHERNET -#include "ETH.h" +#include #endif String Command_AccessInfo_Ls(struct EventStruct *event, const char* Line) diff --git a/src/src/CustomBuild/define_plugin_sets.h b/src/src/CustomBuild/define_plugin_sets.h index b5e36cde97..7b6d6ffc1c 100644 --- a/src/src/CustomBuild/define_plugin_sets.h +++ b/src/src/CustomBuild/define_plugin_sets.h @@ -1428,7 +1428,7 @@ To create/register a plugin, you have to : #define USES_P096 // eInk (Needs lib_deps = Adafruit GFX Library, LOLIN_EPD ) #endif #ifndef USES_P098 - #define USES_P098 // ESPEasy-NOW Receiver +// #define USES_P098 // ESPEasy-NOW Receiver #endif #ifndef USES_P099 #define USES_P099 // XPT2046 Touchscreen diff --git a/src/src/DataStructs/EventStructCommandWrapper.h b/src/src/DataStructs/EventStructCommandWrapper.h index ef3cd5c31e..86ae05fc6e 100644 --- a/src/src/DataStructs/EventStructCommandWrapper.h +++ b/src/src/DataStructs/EventStructCommandWrapper.h @@ -2,7 +2,7 @@ #define DATASTRUCTS_EVENTSTRUCTCOMMANDWRAPPER_H #include -#include "ESPEasy_EventStruct.h" +#include "../DataStructs/ESPEasy_EventStruct.h" struct EventStructCommandWrapper { EventStructCommandWrapper() : id(0) {} diff --git a/src/src/DataStructs/RTC_cache_handler_struct.h b/src/src/DataStructs/RTC_cache_handler_struct.h index 9717e6522e..9984011c6a 100644 --- a/src/src/DataStructs/RTC_cache_handler_struct.h +++ b/src/src/DataStructs/RTC_cache_handler_struct.h @@ -2,7 +2,7 @@ #define DATASTRUCTS_RTC_CACHE_HANDLER_STRUCT_H -#include "RTCCacheStruct.h" +#include "../DataStructs/RTCCacheStruct.h" #include "../../ESPEasy_common.h" diff --git a/src/src/ESPEasyCore/ESPEasyWiFiEvent.cpp b/src/src/ESPEasyCore/ESPEasyWiFiEvent.cpp index e76ac3f55c..03a08b418b 100644 --- a/src/src/ESPEasyCore/ESPEasyWiFiEvent.cpp +++ b/src/src/ESPEasyCore/ESPEasyWiFiEvent.cpp @@ -1,7 +1,7 @@ -#include "ESPEasyWiFiEvent.h" +#include "../ESPEasyCore/ESPEasyWiFiEvent.h" #ifdef HAS_ETHERNET -#include "ETH.h" +#include #endif #include "../DataStructs/RTCStruct.h" diff --git a/src/src/Globals/I2Cdev.cpp b/src/src/Globals/I2Cdev.cpp index 5c2f05c386..f4062267a7 100644 --- a/src/src/Globals/I2Cdev.cpp +++ b/src/src/Globals/I2Cdev.cpp @@ -1,4 +1,4 @@ -#include "I2Cdev.h" +#include "../Globals/I2Cdev.h" I2Cdev i2cdev; diff --git a/src/src/Helpers/DeepSleep.cpp b/src/src/Helpers/DeepSleep.cpp index 2a90f13931..8ac9284dec 100644 --- a/src/src/Helpers/DeepSleep.cpp +++ b/src/src/Helpers/DeepSleep.cpp @@ -141,7 +141,7 @@ void deepSleepStart(int dsdelay) if (Settings.UseAlternativeDeepSleep()) { // See: https://github.com/esp8266/Arduino/issues/6318#issuecomment-711389479 - #include "c_types.h" + #include // system_phy_set_powerup_option: // 1 = RF initialization only calibrate VDD33 and Tx power which will take about 18 ms // 2 = RF initialization only calibrate VDD33 which will take about 2 ms diff --git a/src/src/Helpers/FS_Helper.h b/src/src/Helpers/FS_Helper.h index 16528f8683..8ced8f9f42 100644 --- a/src/src/Helpers/FS_Helper.h +++ b/src/src/Helpers/FS_Helper.h @@ -11,7 +11,7 @@ #define FS_NO_GLOBALS #if defined(ESP8266) extern "C" { - #include "spi_flash.h" + #include } #ifdef CORE_POST_2_6_0 extern "C" uint32_t _FS_start; diff --git a/src/src/Helpers/Memory.cpp b/src/src/Helpers/Memory.cpp index 67822f49d4..7b865815ef 100644 --- a/src/src/Helpers/Memory.cpp +++ b/src/src/Helpers/Memory.cpp @@ -3,7 +3,7 @@ #ifdef ESP8266 extern "C" { -#include "user_interface.h" +#include } #endif diff --git a/src/src/Helpers/Networking.cpp b/src/src/Helpers/Networking.cpp index 854a0d07d9..4e1360e388 100644 --- a/src/src/Helpers/Networking.cpp +++ b/src/src/Helpers/Networking.cpp @@ -60,7 +60,7 @@ void etharp_gratuitous_r(struct netif *netif) { # include # endif // ifdef ESP8266 # ifdef ESP32 -# include "HTTPClient.h" +# include # endif // ifdef ESP32 #endif // USE_SETTINGS_ARCHIVE diff --git a/src/src/Helpers/StringProvider.cpp b/src/src/Helpers/StringProvider.cpp index 8fc6f4a804..5b0b897313 100644 --- a/src/src/Helpers/StringProvider.cpp +++ b/src/src/Helpers/StringProvider.cpp @@ -1,7 +1,7 @@ #include "../Helpers/StringProvider.h" #ifdef HAS_ETHERNET -# include "ETH.h" +# include #endif // ifdef HAS_ETHERNET #include "../../ESPEasy-Globals.h" diff --git a/src/src/Helpers/_CPlugin_Helper.cpp b/src/src/Helpers/_CPlugin_Helper.cpp index f0f973ac6b..23f9861a7b 100644 --- a/src/src/Helpers/_CPlugin_Helper.cpp +++ b/src/src/Helpers/_CPlugin_Helper.cpp @@ -36,7 +36,7 @@ # include #endif // ifdef ESP8266 #ifdef ESP32 -# include "HTTPClient.h" +# include #endif // ifdef ESP32 bool safeReadStringUntil(Stream & input, diff --git a/src/src/WebServer/ConfigPage.cpp b/src/src/WebServer/ConfigPage.cpp index 0dd2d4f3e6..59bf65c8bc 100644 --- a/src/src/WebServer/ConfigPage.cpp +++ b/src/src/WebServer/ConfigPage.cpp @@ -23,6 +23,8 @@ +#include "../DataStructs/MAC_address.h" + // ******************************************************************************** // Web Interface config page // ******************************************************************************** From f44ac12b48ea12ed2d55816956dbab027bae0852 Mon Sep 17 00:00:00 2001 From: TD-er Date: Tue, 25 Jan 2022 17:09:40 +0100 Subject: [PATCH 068/147] [I2Cdev lib] Change `byte` to `uint8_t` --- lib/I2Cdevlib/I2Cdev.cpp | 22 +++++++++++----------- lib/I2Cdevlib/I2Cdev.h | 10 +++++----- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/lib/I2Cdevlib/I2Cdev.cpp b/lib/I2Cdevlib/I2Cdev.cpp index 892274ba00..cf9f0ea004 100644 --- a/lib/I2Cdevlib/I2Cdev.cpp +++ b/lib/I2Cdevlib/I2Cdev.cpp @@ -774,8 +774,8 @@ uint16_t I2Cdev::readTimeout = I2CDEV_DEFAULT_READ_TIMEOUT; // added by Jeff Rowberg 2013-05-07: // Arduino Wire-style "beginTransmission" function // (takes 7-bit device address like the Wire method, NOT 8-bit: 0x68, not 0xD0/0xD1) - byte Fastwire::beginTransmission(byte device) { - byte twst, retry; + uint8_t Fastwire::beginTransmission(uint8_t device) { + uint8_t twst, retry; retry = 2; do { TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWSTO) | (1 << TWSTA); @@ -794,8 +794,8 @@ uint16_t I2Cdev::readTimeout = I2CDEV_DEFAULT_READ_TIMEOUT; return 0; } - byte Fastwire::writeBuf(byte device, byte address, byte *data, byte num) { - byte twst, retry; + uint8_t Fastwire::writeBuf(uint8_t device, uint8_t address, uint8_t *data, uint8_t num) { + uint8_t twst, retry; retry = 2; do { @@ -821,7 +821,7 @@ uint16_t I2Cdev::readTimeout = I2CDEV_DEFAULT_READ_TIMEOUT; twst = TWSR & 0xF8; if (twst != TW_MT_DATA_ACK) return 6; - for (byte i = 0; i < num; i++) { + for (uint8_t i = 0; i < num; i++) { //Serial.print(data[i], HEX); //Serial.print(" "); TWDR = data[i]; // send data to the previously addressed device @@ -835,8 +835,8 @@ uint16_t I2Cdev::readTimeout = I2CDEV_DEFAULT_READ_TIMEOUT; return 0; } - byte Fastwire::write(byte value) { - byte twst; + uint8_t Fastwire::write(uint8_t value) { + uint8_t twst; //Serial.println(value, HEX); TWDR = value; // send data TWCR = (1 << TWINT) | (1 << TWEN); @@ -846,8 +846,8 @@ uint16_t I2Cdev::readTimeout = I2CDEV_DEFAULT_READ_TIMEOUT; return 0; } - byte Fastwire::readBuf(byte device, byte address, byte *data, byte num) { - byte twst, retry; + uint8_t Fastwire::readBuf(uint8_t device, uint8_t address, uint8_t *data, uint8_t num) { + uint8_t twst, retry; retry = 2; do { @@ -913,7 +913,7 @@ uint16_t I2Cdev::readTimeout = I2CDEV_DEFAULT_READ_TIMEOUT; TWCR = 0; } - byte Fastwire::stop() { + uint8_t Fastwire::stop() { TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWSTO); if (!waitInt()) return 1; return 0; @@ -1028,7 +1028,7 @@ uint16_t I2Cdev::readTimeout = I2CDEV_DEFAULT_READ_TIMEOUT; twi_Write_Vars *ptwv = 0; static void (*fNextInterruptFunction)(void) = 0; - void twi_Finish(byte bRetVal) { + void twi_Finish(uint8_t bRetVal) { if (ptwv) { free(ptwv); ptwv = 0; diff --git a/lib/I2Cdevlib/I2Cdev.h b/lib/I2Cdevlib/I2Cdev.h index 5b59c56ff2..64fd942bd2 100644 --- a/lib/I2Cdevlib/I2Cdev.h +++ b/lib/I2Cdevlib/I2Cdev.h @@ -189,12 +189,12 @@ class I2Cdev { public: static void setup(int khz, boolean pullup); - static byte beginTransmission(byte device); - static byte write(byte value); - static byte writeBuf(byte device, byte address, byte *data, byte num); - static byte readBuf(byte device, byte address, byte *data, byte num); + static uint8_t beginTransmission(uint8_t device); + static uint8_t write(uint8_t value); + static uint8_t writeBuf(uint8_t device, uint8_t address, uint8_t *data, uint8_t num); + static uint8_t readBuf(uint8_t device, uint8_t address, uint8_t *data, uint8_t num); static void reset(); - static byte stop(); + static uint8_t stop(); }; #endif From 6fecebae60ed5ce951ac5a21f98a4906e57f2aaa Mon Sep 17 00:00:00 2001 From: TD-er Date: Sat, 13 Nov 2021 00:50:24 +0100 Subject: [PATCH 069/147] [ESP32] Update to IDF 4.4 Arduino-ESP32 2.0.1 Including lots of WiFi fixes to be able to get a stable WiFi connection on the new ESP32 SDK. --- platformio_core_defs.ini | 8 ++- platformio_esp32_envs.ini | 2 +- src/src/Commands/WiFi.cpp | 2 +- src/src/DataStructs/SettingsStruct.cpp | 4 ++ src/src/ESPEasyCore/ESPEasyNetwork.cpp | 2 + src/src/ESPEasyCore/ESPEasyWifi.cpp | 72 ++++++++++++------- src/src/ESPEasyCore/ESPEasyWifi.h | 4 +- .../ESPEasyCore/ESPEasyWifi_ProcessEvent.cpp | 3 +- src/src/WebServer/AdvancedConfigPage.cpp | 2 +- 9 files changed, 66 insertions(+), 33 deletions(-) diff --git a/platformio_core_defs.ini b/platformio_core_defs.ini index 1a51b13f78..c3de139641 100644 --- a/platformio_core_defs.ini +++ b/platformio_core_defs.ini @@ -175,7 +175,8 @@ build_flags = -DESP32_STAGE [core_esp32_3_3_2_esp32s2] platform = https://github.com/platformio/platform-espressif32.git#feature/arduino-idf-master -platform_packages = framework-arduinoespressif32 @ https://github.com/Jason2866/arduino-esp32/releases/download/v.2.0-post/framework-arduinoespressif32_i2c.zip +platform_packages = framework-arduinoespressif32 @ https://github.com/tasmota/arduino-esp32/releases/download/2.0.1/framework-arduinoespressif32-release_IDF4.4.tar.gz + platformio/tool-esptoolpy @ https://github.com/tasmota/esptool/releases/download/v3.2/esptool-v3.2.zip build_flags = -DESP32_STAGE @@ -185,6 +186,7 @@ platform_packages = framework-arduinoespressif32 @ https://github.com/Ja [core_esp32_stage] -platform = https://github.com/platformio/platform-espressif32.git#feature/arduino-upstream -platform_packages = framework-arduinoespressif32 @ https://github.com/Jason2866/arduino-esp32/releases/download/v.2.0-post/framework-arduinoespressif32_i2c.zip +platform = https://github.com/platformio/platform-espressif32.git#feature/arduino-idf-master +platform_packages = framework-arduinoespressif32 @ https://github.com/tasmota/arduino-esp32/releases/download/2.0.1/framework-arduinoespressif32-release_IDF4.4.tar.gz + platformio/tool-esptoolpy @ https://github.com/tasmota/esptool/releases/download/v3.2/esptool-v3.2.zip build_flags = -DESP32_STAGE diff --git a/platformio_esp32_envs.ini b/platformio_esp32_envs.ini index bafecb7be3..ef89e5a080 100644 --- a/platformio_esp32_envs.ini +++ b/platformio_esp32_envs.ini @@ -9,7 +9,7 @@ lib_ignore = ESP8266WiFi, ESP8266Ping, ESP8266WebServer, ESP8266H [esp32_common] -extends = common, core_esp32_3_3_0 +extends = common, core_esp32_stage lib_deps = td-er/ESPeasySerial @ 2.0.8, I2Cdevlib-Core, adafruit/Adafruit ILI9341 @ ^1.5.6, Adafruit GFX Library, LOLIN_EPD, adafruit/Adafruit BusIO @ ^1.10.0, VL53L0X @ 1.3.0, SparkFun VL53L1X 4m Laser Distance Sensor @ 1.2.9, td-er/SparkFun MAX1704x Fuel Gauge Arduino Library @ ^1.0.1, ArduinoOTA, ESP32HTTPUpdateServer, FrogmoreScd30, Multi Channel Relay Arduino Library, SparkFun ADXL345 Arduino Library, ITG3205 lib_ignore = ${esp32_always.lib_ignore}, ESP32_ping, IRremoteESP8266, HeatpumpIR board_build.f_flash = 80000000L diff --git a/src/src/Commands/WiFi.cpp b/src/src/Commands/WiFi.cpp index ed13682238..07b83d15b3 100644 --- a/src/src/Commands/WiFi.cpp +++ b/src/src/Commands/WiFi.cpp @@ -57,7 +57,7 @@ String Command_Wifi_Key2(struct EventStruct *event, const char *Line) const __FlashStringHelper * Command_Wifi_Scan(struct EventStruct *event, const char *Line) { - WifiScan(); + WiFiScan_log_to_serial(); return return_command_success(); } diff --git a/src/src/DataStructs/SettingsStruct.cpp b/src/src/DataStructs/SettingsStruct.cpp index e2a0068fbb..b1cd6a7c0c 100644 --- a/src/src/DataStructs/SettingsStruct.cpp +++ b/src/src/DataStructs/SettingsStruct.cpp @@ -83,7 +83,11 @@ void SettingsStruct_tmpl::EcoPowerMode(bool value) { template bool SettingsStruct_tmpl::WifiNoneSleep() const { + #ifdef ESP32 + return true; + #else return bitRead(VariousBits1, 7); + #endif } template diff --git a/src/src/ESPEasyCore/ESPEasyNetwork.cpp b/src/src/ESPEasyCore/ESPEasyNetwork.cpp index 537993a9f0..a89921f2b6 100644 --- a/src/src/ESPEasyCore/ESPEasyNetwork.cpp +++ b/src/src/ESPEasyCore/ESPEasyNetwork.cpp @@ -222,10 +222,12 @@ String WifiSTAmacAddress() { void CheckRunningServices() { set_mDNS(); + #ifdef ESP8266 if (active_network_medium == NetworkMedium_t::WIFI) { SetWiFiTXpower(); } + #endif } #ifdef HAS_ETHERNET diff --git a/src/src/ESPEasyCore/ESPEasyWifi.cpp b/src/src/ESPEasyCore/ESPEasyWifi.cpp index dd9ffca4c2..502932dbe9 100644 --- a/src/src/ESPEasyCore/ESPEasyWifi.cpp +++ b/src/src/ESPEasyCore/ESPEasyWifi.cpp @@ -121,14 +121,11 @@ bool ESPEasyWiFi_t::connectSTA() { } WiFiEventData.warnedNoValidWiFiSettings = false; setSTA(true); - char hostname[40]; - safe_strncpy(hostname, NetworkCreateRFCCompliantHostname().c_str(), sizeof(hostname)); #if defined(ESP8266) - wifi_station_set_hostname(hostname); + wifi_station_set_hostname(NetworkCreateRFCCompliantHostname().c_str()); #endif // if defined(ESP8266) #if defined(ESP32) - WiFi.setHostname(hostname); WiFi.config(INADDR_NONE, INADDR_NONE, INADDR_NONE); #endif // if defined(ESP32) setConnectionSpeed(); @@ -277,7 +274,9 @@ bool WiFiConnected() { STOP_TIMER(WIFI_ISCONNECTED_STATS); recursiveCall = false; // Only return true after some time since it got connected. + #ifdef ESP8266 SetWiFiTXpower(); + #endif return WiFiEventData.wifi_considered_stable || WiFiEventData.lastConnectMoment.timeoutReached(100); } @@ -285,6 +284,7 @@ bool WiFiConnected() { // Timer reached, so enable AP mode. if (!WifiIsAP(WiFi.getMode())) { if (!Settings.DoNotStartAP()) { + WifiScan(false); setAP(true); } } @@ -397,10 +397,12 @@ void AttemptWiFiConnect() { RTC.clearLastWiFi(); float tx_pwr = 0; // Will be set higher based on RSSI when needed. // FIXME TD-er: Must check WiFiEventData.wifi_connect_attempt to increase TX power + #ifdef ESP8266 if (Settings.UseMaxTXpowerForSending()) { tx_pwr = Settings.getWiFi_TX_power(); } SetWiFiTXpower(tx_pwr, candidate.rssi); + #endif // Start connect attempt now, so no longer needed to attempt new connection. WiFiEventData.wifiConnectAttemptNeeded = false; if (candidate.allowQuickConnect() && !candidate.isHidden) { @@ -414,11 +416,14 @@ void AttemptWiFiConnect() { } else { if (!wifiAPmodeActivelyUsed() || WiFiEventData.wifiSetupConnect) { if (!prepareWiFi()) { - return; + //return; + } + + if (WiFiScanAllowed()) { + // Maybe not scan async to give the ESP some slack in power consumption? + const bool async = false; + WifiScan(async); } - // Maybe not scan async to give the ESP some slack in power consumption? - const bool async = false; - WifiScan(async); } } @@ -440,6 +445,7 @@ bool prepareWiFi() { // No need to wait longer to start AP mode. if (!Settings.DoNotStartAP()) { + WifiScan(false); setAP(true); } return false; @@ -447,14 +453,11 @@ bool prepareWiFi() { WiFiEventData.warnedNoValidWiFiSettings = false; setSTA(true); - char hostname[40]; - safe_strncpy(hostname, NetworkCreateRFCCompliantHostname().c_str(), sizeof(hostname)); #if defined(ESP8266) - wifi_station_set_hostname(hostname); + wifi_station_set_hostname(NetworkCreateRFCCompliantHostname().c_str()); #endif // if defined(ESP8266) #if defined(ESP32) - WiFi.setHostname(hostname); WiFi.config(INADDR_NONE, INADDR_NONE, INADDR_NONE); #endif // if defined(ESP32) setConnectionSpeed(); @@ -568,6 +571,7 @@ void initWiFi() // ******************************************************************************** // Configure WiFi TX power // ******************************************************************************** +#ifdef ESP8266 void SetWiFiTXpower() { SetWiFiTXpower(0.0f); // Just some minimal value, will be adjusted in SetWiFiTXpower } @@ -693,6 +697,7 @@ void SetWiFiTXpower(float dBm, float rssi) { } #endif } +#endif float GetRSSIthreshold(float& maxTXpwr) { maxTXpwr = Settings.getWiFi_TX_power(); @@ -773,6 +778,9 @@ void WifiDisconnect() #endif // if defined(ESP32) WiFiEventData.setWiFiDisconnected(); WiFiEventData.markDisconnect(WIFI_DISCONNECT_REASON_ASSOC_LEAVE); + if (!Settings.UseLastWiFiFromRTC()) { + RTC.clearLastWiFi(); + } delay(1); } @@ -842,6 +850,14 @@ void WifiScan(bool async, uint8_t channel) { if (!WiFiScanAllowed()) { return; } +#ifdef ESP32 + // TD-er: Don't run async scan on ESP32. + // Since IDF 4.4 it seems like the active channel may be messed up when running async scan + // Perform a disconnect after scanning. + // See: https://github.com/letscontrolit/ESPEasy/pull/3579#issuecomment-967021347 + async = false; +#endif + START_TIMER; WiFiEventData.lastScanMoment.setNow(); if (loglevelActiveFor(LOG_LEVEL_INFO)) { @@ -866,7 +882,7 @@ void WifiScan(bool async, uint8_t channel) { FeedSW_watchdog(); } --nrScans; - #ifdef ESP8266 +#ifdef ESP8266 /* { static bool FIRST_SCAN = true; @@ -891,24 +907,30 @@ void WifiScan(bool async, uint8_t channel) { } */ WiFi.scanNetworks(async, show_hidden, channel); - #endif - #ifdef ESP32 +#endif +#ifdef ESP32 const bool passive = false; const uint32_t max_ms_per_chan = 300; WiFi.scanNetworks(async, show_hidden, passive, max_ms_per_chan /*, channel */); - #endif +#endif if (!async) { FeedSW_watchdog(); processScanDone(); } } STOP_TIMER(async ? WIFI_SCAN_ASYNC : WIFI_SCAN_SYNC); + +#ifdef ESP32 + RTC.clearLastWiFi(); + WifiDisconnect(); +#endif + } // ******************************************************************************** // Scan all Wifi Access Points // ******************************************************************************** -void WifiScan() +void WiFiScan_log_to_serial() { // Direct Serial is allowed here, since this function will only be called from serial input. serialPrintln(F("WIFI : SSID Scan start")); @@ -977,7 +999,6 @@ void setAP(bool enable) { case WIFI_OFF: if (enable) { - WifiScan(false); setWifiMode(WIFI_AP); } break; @@ -1073,15 +1094,17 @@ void setWifiMode(WiFiMode_t wifimode) { if (cur_mode == WIFI_OFF) { WiFiEventData.markWiFiTurnOn(); - + } + if (wifimode != WIFI_OFF) { #if defined(ESP32) - esp_wifi_set_ps(WIFI_PS_NONE); + // Needs to be set before calling WiFi.mode() on ESP32 + WiFi.hostname(NetworkCreateRFCCompliantHostname()); #endif - #ifdef ESP8266 + #ifdef ESP8266 // See: https://github.com/esp8266/Arduino/issues/6172#issuecomment-500457407 WiFi.forceSleepWake(); // Make sure WiFi is really active. - #endif // ifdef ESP8266 + #endif delay(100); } @@ -1106,7 +1129,7 @@ void setWifiMode(WiFiMode_t wifimode) { WiFiEventData.markWiFiTurnOn(); delay(100); #if defined(ESP32) - esp_wifi_set_ps(WIFI_PS_MAX_MODEM); +// esp_wifi_set_ps(WIFI_PS_MAX_MODEM); #endif #ifdef ESP8266 WiFi.forceSleepBegin(); @@ -1146,8 +1169,9 @@ void setWifiMode(WiFiMode_t wifimode) { #endif } } - +#ifdef ESP8266 SetWiFiTXpower(); +#endif if (WifiIsSTA(wifimode)) { if (WiFi.getAutoConnect()) { WiFi.setAutoConnect(false); diff --git a/src/src/ESPEasyCore/ESPEasyWifi.h b/src/src/ESPEasyCore/ESPEasyWifi.h index e10bb144ab..3fb202445c 100644 --- a/src/src/ESPEasyCore/ESPEasyWifi.h +++ b/src/src/ESPEasyCore/ESPEasyWifi.h @@ -109,15 +109,17 @@ bool prepareWiFi(); bool checkAndResetWiFi(); void resetWiFi(); void initWiFi(); +#ifdef ESP8266 void SetWiFiTXpower(); void SetWiFiTXpower(float dBm); // 0-20.5 void SetWiFiTXpower(float dBm, float rssi); +#endif float GetRSSIthreshold(float& maxTXpwr); WiFiConnectionProtocol getConnectionProtocol(); void WifiDisconnect(); bool WiFiScanAllowed(); void WifiScan(bool async, uint8_t channel = 0); -void WifiScan(); +void WiFiScan_log_to_serial(); void setSTA(bool enable); void setAP(bool enable); const __FlashStringHelper * getWifiModeString(WiFiMode_t wifimode); diff --git a/src/src/ESPEasyCore/ESPEasyWifi_ProcessEvent.cpp b/src/src/ESPEasyCore/ESPEasyWifi_ProcessEvent.cpp index a3f7dabb6f..060ecb57b6 100644 --- a/src/src/ESPEasyCore/ESPEasyWifi_ProcessEvent.cpp +++ b/src/src/ESPEasyCore/ESPEasyWifi_ProcessEvent.cpp @@ -252,6 +252,7 @@ void processDisconnect() { WifiDisconnect(); // Needed or else node may not reconnect reliably. if (mustRestartWiFi) { + WifiScan(false); delay(100); setWifiMode(WIFI_OFF); initWiFi(); @@ -259,8 +260,6 @@ void processDisconnect() { if (WiFiEventData.unprocessedWifiEvents()) { handle_unprocessedNetworkEvents(); } - - WifiScan(false); } logConnectionStatus(); WiFiEventData.processedDisconnect = true; diff --git a/src/src/WebServer/AdvancedConfigPage.cpp b/src/src/WebServer/AdvancedConfigPage.cpp index 260ea09ab8..3939c7b344 100644 --- a/src/src/WebServer/AdvancedConfigPage.cpp +++ b/src/src/WebServer/AdvancedConfigPage.cpp @@ -252,8 +252,8 @@ void handle_advanced() { addFormCheckBox(LabelType::RESTART_WIFI_LOST_CONN, Settings.WiFiRestart_connection_lost()); #ifdef ESP8266 addFormCheckBox(LabelType::FORCE_WIFI_NOSLEEP, Settings.WifiNoneSleep()); -#endif // ifdef ESP8266 addFormNote(F("Change WiFi sleep settings requires reboot to activate")); +#endif #ifdef SUPPORT_ARP addFormCheckBox(LabelType::PERIODICAL_GRAT_ARP, Settings.gratuitousARP()); #endif // ifdef SUPPORT_ARP From 0a68716d96850fa343110d175d96fac331318a80 Mon Sep 17 00:00:00 2001 From: TD-er Date: Sat, 13 Nov 2021 01:47:57 +0100 Subject: [PATCH 070/147] [ESP32] Allow building LittleFS builds on ESP32 Still cannot build it on Windows. --- platformio.ini | 2 +- platformio_esp32_envs.ini | 25 +++++++++++++++++++++--- src/ESPEasy_common.h | 9 +++++++-- src/src/DataTypes/CPluginID.cpp | 2 +- src/src/DataTypes/DeviceIndex.cpp | 2 +- src/src/DataTypes/ESPEasyTimeSource.cpp | 2 +- src/src/DataTypes/EthernetParameters.cpp | 2 +- src/src/DataTypes/NetworkMedium.cpp | 2 +- src/src/DataTypes/PluginID.cpp | 2 +- src/src/DataTypes/ProtocolIndex.cpp | 2 +- src/src/DataTypes/SettingsType.cpp | 2 +- src/src/DataTypes/TaskIndex.cpp | 2 +- tools/pio/concat_cpp_files.py | 1 + tools/pio/remove_concat_cpp_files.py | 1 + 14 files changed, 41 insertions(+), 15 deletions(-) diff --git a/platformio.ini b/platformio.ini index 4fe062eaef..8090915ae0 100644 --- a/platformio.ini +++ b/platformio.ini @@ -89,7 +89,7 @@ monitor_speed = 115200 targets = extra_scripts = pre:tools/pio/pre_default_check.py ${extra_scripts_esp8266.extra_scripts} -src_filter = +<*> -<.git/> -<.svn/> - - - - -<*/Commands/> -<*/ControllerQueue/> -<*/Globals/> -<*/Helpers/> -<*/PluginStructs/> -<*/WebServer/> +src_filter = +<*> -<.git/> -<.svn/> - - - - -<*/Commands/> -<*/ControllerQueue/> -<*/DataTypes/> -<*/Globals/> -<*/Helpers/> -<*/PluginStructs/> -<*/WebServer/> diff --git a/platformio_esp32_envs.ini b/platformio_esp32_envs.ini index ef89e5a080..17f2bc4353 100644 --- a/platformio_esp32_envs.ini +++ b/platformio_esp32_envs.ini @@ -27,6 +27,15 @@ build_flags = ${core_esp32_3_3_0.build_flags} -DCORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_NONE monitor_filters = esp32_exception_decoder +[esp32_LittleFS] +extends = esp32_common +platform_packages = ${esp32_common.platform_packages} + platformio/tool-mklittlefs @ ~1.203.200522 +;lib_deps = ${esp32_common.lib_deps}, https://github.com/lorol/LITTLEFS.git +board_build.filesystem = littlefs + + + ; Custom: 4096k version -------------------------- [env:custom_ESP32_4M316k] @@ -389,9 +398,19 @@ board = lolin_d32_pro ; A Lolin D32 PRO with 16MB Flash, allowing 4MB sketch size, and file storage using LittleFS filesystem [max_ESP32_16M_LittleFS] -extends = max_ESP32_16M -lib_deps = ${max_ESP32_16M.lib_deps}, https://github.com/lorol/LITTLEFS.git -board_build.filesystem = littlefs +extends = esp32_LittleFS +lib_ignore = ${esp32_always.lib_ignore}, ESP32_ping +lib_deps = ${esp32_LittleFS.lib_deps}, VL53L0X +board_upload.maximum_size = 4194304 +build_flags = ${esp32_LittleFS.build_flags} + -DFEATURE_ARDUINO_OTA + -DPLUGIN_BUILD_MAX_ESP32 + -DPLUGIN_BUILD_IR_EXTENDED +; TODO: To enable PS-RAM Support needs more build flags than these 2, for now define ESP32_ENABLE_PSRAM is used to en/disable detecting PS-Ram size on Info page +; -DBOARD_HAS_PSRAM // both flags already enabled for Lolin D32 Pro board by PlatformIO +; -mfix-esp32-psram-cache-issue +board = lolin_d32_pro + ; A Lolin D32 PRO with 16MB Flash, allowing 4MB sketch size, and 1MB assigned to file storage using SPIFFS filesystem [env:max_ESP32_16M1M] diff --git a/src/ESPEasy_common.h b/src/ESPEasy_common.h index 4aaa84f5f6..f6705e67c3 100644 --- a/src/ESPEasy_common.h +++ b/src/ESPEasy_common.h @@ -161,8 +161,13 @@ using namespace fs; #ifdef USE_LITTLEFS #ifdef ESP32 - #include - #define ESPEASY_FS LITTLEFS + #if ESP_IDF_VERSION_MAJOR >= 4 + #include + #define ESPEASY_FS LittleFS + #else + #include + #define ESPEASY_FS LITTLEFS + #endif #else #include #define ESPEASY_FS LittleFS diff --git a/src/src/DataTypes/CPluginID.cpp b/src/src/DataTypes/CPluginID.cpp index 9c55bc4887..bf5470fd2f 100644 --- a/src/src/DataTypes/CPluginID.cpp +++ b/src/src/DataTypes/CPluginID.cpp @@ -1,3 +1,3 @@ -#include "CPluginID.h" +#include "../DataTypes/CPluginID.h" cpluginID_t INVALID_C_PLUGIN_ID = 0; diff --git a/src/src/DataTypes/DeviceIndex.cpp b/src/src/DataTypes/DeviceIndex.cpp index 66888af940..64869e5531 100644 --- a/src/src/DataTypes/DeviceIndex.cpp +++ b/src/src/DataTypes/DeviceIndex.cpp @@ -1,4 +1,4 @@ -#include "DeviceIndex.h" +#include "../DataTypes/DeviceIndex.h" #include "../CustomBuild/ESPEasyLimits.h" diff --git a/src/src/DataTypes/ESPEasyTimeSource.cpp b/src/src/DataTypes/ESPEasyTimeSource.cpp index 1949453736..7ff7bd698b 100644 --- a/src/src/DataTypes/ESPEasyTimeSource.cpp +++ b/src/src/DataTypes/ESPEasyTimeSource.cpp @@ -1,4 +1,4 @@ -#include "ESPEasyTimeSource.h" +#include "../DataTypes/ESPEasyTimeSource.h" #include diff --git a/src/src/DataTypes/EthernetParameters.cpp b/src/src/DataTypes/EthernetParameters.cpp index b4cac0de46..cb25de9dda 100644 --- a/src/src/DataTypes/EthernetParameters.cpp +++ b/src/src/DataTypes/EthernetParameters.cpp @@ -1,4 +1,4 @@ -#include "EthernetParameters.h" +#include "../DataTypes/EthernetParameters.h" bool isValid(EthClockMode_t clockMode) { switch (clockMode) { diff --git a/src/src/DataTypes/NetworkMedium.cpp b/src/src/DataTypes/NetworkMedium.cpp index f8f7fc6af3..61fe0dee9a 100644 --- a/src/src/DataTypes/NetworkMedium.cpp +++ b/src/src/DataTypes/NetworkMedium.cpp @@ -1,4 +1,4 @@ -#include "NetworkMedium.h" +#include "../DataTypes/NetworkMedium.h" bool isValid(NetworkMedium_t medium) { switch (medium) { diff --git a/src/src/DataTypes/PluginID.cpp b/src/src/DataTypes/PluginID.cpp index a6ff107ba2..832943791f 100644 --- a/src/src/DataTypes/PluginID.cpp +++ b/src/src/DataTypes/PluginID.cpp @@ -1,3 +1,3 @@ -#include "PluginID.h" +#include "../DataTypes/PluginID.h" pluginID_t INVALID_PLUGIN_ID = 0; diff --git a/src/src/DataTypes/ProtocolIndex.cpp b/src/src/DataTypes/ProtocolIndex.cpp index ceff2f5bf4..45ec8c3728 100644 --- a/src/src/DataTypes/ProtocolIndex.cpp +++ b/src/src/DataTypes/ProtocolIndex.cpp @@ -1,4 +1,4 @@ -#include "ProtocolIndex.h" +#include "../DataTypes/ProtocolIndex.h" #include "../CustomBuild/ESPEasyLimits.h" diff --git a/src/src/DataTypes/SettingsType.cpp b/src/src/DataTypes/SettingsType.cpp index 583629ad5a..ebb5ed83ac 100644 --- a/src/src/DataTypes/SettingsType.cpp +++ b/src/src/DataTypes/SettingsType.cpp @@ -1,4 +1,4 @@ -#include "SettingsType.h" +#include "../DataTypes/SettingsType.h" #include "../CustomBuild/StorageLayout.h" #include "../DataStructs/NotificationSettingsStruct.h" diff --git a/src/src/DataTypes/TaskIndex.cpp b/src/src/DataTypes/TaskIndex.cpp index d64b790a8a..f2ebea9167 100644 --- a/src/src/DataTypes/TaskIndex.cpp +++ b/src/src/DataTypes/TaskIndex.cpp @@ -1,4 +1,4 @@ -#include "TaskIndex.h" +#include "../DataTypes/TaskIndex.h" #include "../../ESPEasy_common.h" diff --git a/tools/pio/concat_cpp_files.py b/tools/pio/concat_cpp_files.py index b33e06a1a2..ad4be8eb8b 100644 --- a/tools/pio/concat_cpp_files.py +++ b/tools/pio/concat_cpp_files.py @@ -52,6 +52,7 @@ def concat_cpp_files(path_to_concat): concat_cpp_files('./src/src/Commands') concat_cpp_files('./src/src/ControllerQueue') +concat_cpp_files('./src/src/DataTypes') concat_cpp_files('./src/src/Globals') concat_cpp_files('./src/src/Helpers') concat_cpp_files('./src/src/PluginStructs') diff --git a/tools/pio/remove_concat_cpp_files.py b/tools/pio/remove_concat_cpp_files.py index 32cc86dcd3..3dfbd6700e 100644 --- a/tools/pio/remove_concat_cpp_files.py +++ b/tools/pio/remove_concat_cpp_files.py @@ -19,6 +19,7 @@ def clear_all_concat_cpp_files(source, target, env): print("\u001b[32m Remove temp concatenated files \u001b[0m") clear_concat_cpp_files('./src/src/Commands') clear_concat_cpp_files('./src/src/ControllerQueue') + clear_concat_cpp_files('./src/src/DataTypes') clear_concat_cpp_files('./src/src/Globals') clear_concat_cpp_files('./src/src/Helpers') clear_concat_cpp_files('./src/src/PluginStructs') From d34460fcbbd6038410f33ad01167266446a0a353 Mon Sep 17 00:00:00 2001 From: TD-er Date: Sat, 13 Nov 2021 01:58:35 +0100 Subject: [PATCH 071/147] [Build] Fix case sensitive include --- src/src/Helpers/FS_Helper.h | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/src/Helpers/FS_Helper.h b/src/src/Helpers/FS_Helper.h index e1d9b335c2..8ced8f9f42 100644 --- a/src/src/Helpers/FS_Helper.h +++ b/src/src/Helpers/FS_Helper.h @@ -26,12 +26,4 @@ #endif #endif -#if defined(ESP32) - #ifdef USE_LITTLEFS - #include "LITTLEFS.h" - #else - #include "SPIFFS.h" - #endif -#endif - #endif \ No newline at end of file From def7e6752d8e218fb998d0fa5b8de32910265238 Mon Sep 17 00:00:00 2001 From: TD-er Date: Sat, 13 Nov 2021 02:50:35 +0100 Subject: [PATCH 072/147] [Windows Build] Convert IRremoteESP8266 to single cpp file --- lib/IRremoteESP8266/src/IRrecv.h | 2 +- lib/IRremoteESP8266/src/IRsend.h | 2 +- lib/IRremoteESP8266/src/IRtimer.cpp | 78 - lib/IRremoteESP8266/src/IRutils.h | 4 +- lib/IRremoteESP8266/src/ir_Airwell.cpp | 286 -- lib/IRremoteESP8266/src/ir_Airwell.h | 6 +- lib/IRremoteESP8266/src/ir_Aiwa.cpp | 105 - lib/IRremoteESP8266/src/ir_Amcor.cpp | 354 -- lib/IRremoteESP8266/src/ir_Amcor.h | 6 +- lib/IRremoteESP8266/src/ir_Argo.cpp | 470 --- lib/IRremoteESP8266/src/ir_Argo.h | 6 +- lib/IRremoteESP8266/src/ir_Arris.cpp | 123 - lib/IRremoteESP8266/src/ir_Bose.cpp | 69 - lib/IRremoteESP8266/src/ir_Carrier.cpp | 535 --- lib/IRremoteESP8266/src/ir_Carrier.h | 6 +- lib/IRremoteESP8266/src/ir_Coolix.h | 6 +- lib/IRremoteESP8266/src/ir_Corona.cpp | 575 --- lib/IRremoteESP8266/src/ir_Corona.h | 6 +- lib/IRremoteESP8266/src/ir_Daikin.cpp | 3735 ----------------- lib/IRremoteESP8266/src/ir_Daikin.h | 8 +- lib/IRremoteESP8266/src/ir_Delonghi.cpp | 470 --- lib/IRremoteESP8266/src/ir_Delonghi.h | 6 +- lib/IRremoteESP8266/src/ir_Denon.cpp | 122 - lib/IRremoteESP8266/src/ir_Dish.cpp | 103 - lib/IRremoteESP8266/src/ir_Doshisha.cpp | 124 - lib/IRremoteESP8266/src/ir_Ecoclim.cpp | 423 -- lib/IRremoteESP8266/src/ir_Ecoclim.h | 6 +- lib/IRremoteESP8266/src/ir_Electra.h | 6 +- lib/IRremoteESP8266/src/ir_EliteScreens.cpp | 89 - lib/IRremoteESP8266/src/ir_Epson.cpp | 111 - lib/IRremoteESP8266/src/ir_Fujitsu.cpp | 1043 ----- lib/IRremoteESP8266/src/ir_Fujitsu.h | 8 +- lib/IRremoteESP8266/src/ir_GICable.cpp | 95 - lib/IRremoteESP8266/src/ir_GlobalCache.cpp | 63 - lib/IRremoteESP8266/src/ir_Goodweather.cpp | 499 --- lib/IRremoteESP8266/src/ir_Goodweather.h | 6 +- lib/IRremoteESP8266/src/ir_Gree.h | 6 +- lib/IRremoteESP8266/src/ir_Haier.h | 6 +- lib/IRremoteESP8266/src/ir_Hitachi.cpp | 1580 ------- lib/IRremoteESP8266/src/ir_Hitachi.h | 6 +- lib/IRremoteESP8266/src/ir_Inax.cpp | 73 - lib/IRremoteESP8266/src/ir_JVC.cpp | 131 - lib/IRremoteESP8266/src/ir_Kelon.cpp | 502 --- lib/IRremoteESP8266/src/ir_Kelon.h | 8 +- lib/IRremoteESP8266/src/ir_Kelvinator.cpp | 525 --- lib/IRremoteESP8266/src/ir_Kelvinator.h | 6 +- lib/IRremoteESP8266/src/ir_LG.cpp | 838 ---- lib/IRremoteESP8266/src/ir_LG.h | 8 +- lib/IRremoteESP8266/src/ir_Lasertag.cpp | 116 - lib/IRremoteESP8266/src/ir_Lego.cpp | 106 - lib/IRremoteESP8266/src/ir_Lutron.cpp | 143 - lib/IRremoteESP8266/src/ir_MWM.cpp | 197 - lib/IRremoteESP8266/src/ir_Magiquest.cpp | 154 - lib/IRremoteESP8266/src/ir_Magiquest.h | 4 +- lib/IRremoteESP8266/src/ir_Metz.cpp | 101 - lib/IRremoteESP8266/src/ir_Midea.cpp | 796 ---- lib/IRremoteESP8266/src/ir_Midea.h | 6 +- lib/IRremoteESP8266/src/ir_MilesTag2.cpp | 113 - lib/IRremoteESP8266/src/ir_Mitsubishi.h | 6 +- .../src/ir_MitsubishiHeavy.cpp | 1050 ----- lib/IRremoteESP8266/src/ir_MitsubishiHeavy.h | 6 +- lib/IRremoteESP8266/src/ir_Multibrackets.cpp | 115 - lib/IRremoteESP8266/src/ir_NEC.cpp | 140 - lib/IRremoteESP8266/src/ir_NEC.h | 2 +- lib/IRremoteESP8266/src/ir_Neoclima.cpp | 608 --- lib/IRremoteESP8266/src/ir_Neoclima.h | 6 +- lib/IRremoteESP8266/src/ir_Nikai.cpp | 74 - lib/IRremoteESP8266/src/ir_Panasonic.cpp | 1341 ------ lib/IRremoteESP8266/src/ir_Panasonic.h | 6 +- lib/IRremoteESP8266/src/ir_Pioneer.cpp | 138 - lib/IRremoteESP8266/src/ir_Pronto.cpp | 107 - lib/IRremoteESP8266/src/ir_RC5_RC6.cpp | 454 -- lib/IRremoteESP8266/src/ir_RCMM.cpp | 164 - lib/IRremoteESP8266/src/ir_Rhoss.cpp | 364 -- lib/IRremoteESP8266/src/ir_Rhoss.h | 6 +- lib/IRremoteESP8266/src/ir_Samsung.h | 6 +- lib/IRremoteESP8266/src/ir_Sanyo.cpp | 978 ----- lib/IRremoteESP8266/src/ir_Sanyo.h | 6 +- lib/IRremoteESP8266/src/ir_Sharp.cpp | 976 ----- lib/IRremoteESP8266/src/ir_Sharp.h | 10 +- lib/IRremoteESP8266/src/ir_Sherwood.cpp | 24 - lib/IRremoteESP8266/src/ir_Symphony.cpp | 95 - lib/IRremoteESP8266/src/ir_Tcl.cpp | 529 --- lib/IRremoteESP8266/src/ir_Tcl.h | 8 +- lib/IRremoteESP8266/src/ir_Technibel.cpp | 408 -- lib/IRremoteESP8266/src/ir_Technibel.h | 6 +- lib/IRremoteESP8266/src/ir_Teco.cpp | 375 -- lib/IRremoteESP8266/src/ir_Teco.h | 6 +- lib/IRremoteESP8266/src/ir_Teknopoint.cpp | 75 - lib/IRremoteESP8266/src/ir_Toshiba.h | 6 +- lib/IRremoteESP8266/src/ir_Transcold.cpp | 500 --- lib/IRremoteESP8266/src/ir_Transcold.h | 6 +- lib/IRremoteESP8266/src/ir_Trotec.cpp | 642 --- lib/IRremoteESP8266/src/ir_Trotec.h | 6 +- lib/IRremoteESP8266/src/ir_Truma.cpp | 340 -- lib/IRremoteESP8266/src/ir_Truma.h | 6 +- lib/IRremoteESP8266/src/ir_Vestel.cpp | 572 --- lib/IRremoteESP8266/src/ir_Vestel.h | 6 +- lib/IRremoteESP8266/src/ir_Voltas.cpp | 516 --- lib/IRremoteESP8266/src/ir_Voltas.h | 6 +- lib/IRremoteESP8266/src/ir_Whirlpool.cpp | 657 --- lib/IRremoteESP8266/src/ir_Whirlpool.h | 6 +- lib/IRremoteESP8266/src/ir_Whynter.cpp | 103 - lib/IRremoteESP8266/src/ir_Xmp.cpp | 226 - lib/IRremoteESP8266/src/ir_Zepeal.cpp | 94 - lib/IRremoteESP8266/test/IRrecv_test.cpp | 10 +- lib/IRremoteESP8266/test/IRrecv_test.h | 2 +- lib/IRremoteESP8266/test/IRsend_test.cpp | 8 +- lib/IRremoteESP8266/test/IRsend_test.h | 6 +- lib/IRremoteESP8266/test/IRutils_test.cpp | 10 +- lib/IRremoteESP8266/test/ir_Airwell_test.cpp | 12 +- lib/IRremoteESP8266/test/ir_Aiwa_test.cpp | 4 +- lib/IRremoteESP8266/test/ir_Amcor_test.cpp | 14 +- lib/IRremoteESP8266/test/ir_Argo_test.cpp | 12 +- lib/IRremoteESP8266/test/ir_Arris_test.cpp | 10 +- lib/IRremoteESP8266/test/ir_Bose_test.cpp | 10 +- lib/IRremoteESP8266/test/ir_Carrier_test.cpp | 10 +- lib/IRremoteESP8266/test/ir_Coolix_test.cpp | 8 +- lib/IRremoteESP8266/test/ir_Corona_test.cpp | 12 +- lib/IRremoteESP8266/test/ir_Daikin_test.cpp | 12 +- lib/IRremoteESP8266/test/ir_Delonghi_test.cpp | 14 +- lib/IRremoteESP8266/test/ir_Denon_test.cpp | 4 +- lib/IRremoteESP8266/test/ir_Dish_test.cpp | 4 +- lib/IRremoteESP8266/test/ir_Doshisha_test.cpp | 10 +- lib/IRremoteESP8266/test/ir_Ecoclim_test.cpp | 14 +- lib/IRremoteESP8266/test/ir_Electra_test.cpp | 12 +- .../test/ir_EliteScreens_test.cpp | 6 +- lib/IRremoteESP8266/test/ir_Epson_test.cpp | 10 +- lib/IRremoteESP8266/test/ir_Fujitsu_test.cpp | 10 +- lib/IRremoteESP8266/test/ir_GICable_test.cpp | 4 +- .../test/ir_GlobalCache_test.cpp | 4 +- .../test/ir_Goodweather_test.cpp | 12 +- lib/IRremoteESP8266/test/ir_Gree_test.cpp | 14 +- lib/IRremoteESP8266/test/ir_Haier_test.cpp | 12 +- lib/IRremoteESP8266/test/ir_Hitachi_test.cpp | 14 +- lib/IRremoteESP8266/test/ir_Inax_test.cpp | 6 +- lib/IRremoteESP8266/test/ir_JVC_test.cpp | 4 +- lib/IRremoteESP8266/test/ir_Kelon_test.cpp | 10 +- .../test/ir_Kelvinator_test.cpp | 14 +- lib/IRremoteESP8266/test/ir_LG_test.cpp | 8 +- lib/IRremoteESP8266/test/ir_Lasertag_test.cpp | 4 +- lib/IRremoteESP8266/test/ir_Lego_test.cpp | 8 +- lib/IRremoteESP8266/test/ir_Lutron_test.cpp | 4 +- lib/IRremoteESP8266/test/ir_MWM_test.cpp | 8 +- .../test/ir_Magiquest_test.cpp | 8 +- lib/IRremoteESP8266/test/ir_Metz_test.cpp | 10 +- lib/IRremoteESP8266/test/ir_Midea_test.cpp | 8 +- .../test/ir_Milestag2_test.cpp | 12 +- .../test/ir_MitsubishiHeavy_test.cpp | 14 +- .../test/ir_Mitsubishi_test.cpp | 10 +- .../test/ir_Multibrackets_test.cpp | 10 +- lib/IRremoteESP8266/test/ir_NEC_test.cpp | 4 +- lib/IRremoteESP8266/test/ir_Neoclima_test.cpp | 12 +- lib/IRremoteESP8266/test/ir_Nikai_test.cpp | 4 +- .../test/ir_Panasonic_test.cpp | 16 +- lib/IRremoteESP8266/test/ir_Pioneer_test.cpp | 6 +- lib/IRremoteESP8266/test/ir_Pronto_test.cpp | 4 +- lib/IRremoteESP8266/test/ir_RC5_RC6_test.cpp | 4 +- lib/IRremoteESP8266/test/ir_RCMM_test.cpp | 4 +- lib/IRremoteESP8266/test/ir_Rhoss_test.cpp | 14 +- lib/IRremoteESP8266/test/ir_Samsung_test.cpp | 12 +- lib/IRremoteESP8266/test/ir_Sanyo_test.cpp | 12 +- lib/IRremoteESP8266/test/ir_Sharp_test.cpp | 12 +- lib/IRremoteESP8266/test/ir_Sherwood_test.cpp | 4 +- lib/IRremoteESP8266/test/ir_Sony_test.cpp | 4 +- lib/IRremoteESP8266/test/ir_Symphony_test.cpp | 10 +- lib/IRremoteESP8266/test/ir_Tcl_test.cpp | 12 +- .../test/ir_Technibel_test.cpp | 14 +- lib/IRremoteESP8266/test/ir_Teco_test.cpp | 12 +- .../test/ir_Teknopoint_test.cpp | 10 +- lib/IRremoteESP8266/test/ir_Toshiba_test.cpp | 12 +- .../test/ir_Transcold_test.cpp | 10 +- lib/IRremoteESP8266/test/ir_Trotec_test.cpp | 12 +- lib/IRremoteESP8266/test/ir_Truma_test.cpp | 10 +- lib/IRremoteESP8266/test/ir_Vestel_test.cpp | 12 +- lib/IRremoteESP8266/test/ir_Voltas_test.cpp | 12 +- .../test/ir_Whirlpool_test.cpp | 12 +- lib/IRremoteESP8266/test/ir_Whynter_test.cpp | 4 +- lib/IRremoteESP8266/test/ir_Xmp_test.cpp | 10 +- lib/IRremoteESP8266/test/ir_Zepeal_test.cpp | 10 +- .../tools/auto_analyse_raw_data.py | 6 +- .../tools/auto_analyse_raw_data_test.py | 24 +- lib/IRremoteESP8266/tools/gc_decode.cpp | 8 +- lib/IRremoteESP8266/tools/mode2_decode.cpp | 6 +- platformio.ini | 2 +- 185 files changed, 496 insertions(+), 27008 deletions(-) delete mode 100644 lib/IRremoteESP8266/src/IRtimer.cpp delete mode 100644 lib/IRremoteESP8266/src/ir_Airwell.cpp delete mode 100644 lib/IRremoteESP8266/src/ir_Aiwa.cpp delete mode 100644 lib/IRremoteESP8266/src/ir_Amcor.cpp delete mode 100644 lib/IRremoteESP8266/src/ir_Argo.cpp delete mode 100644 lib/IRremoteESP8266/src/ir_Arris.cpp delete mode 100644 lib/IRremoteESP8266/src/ir_Bose.cpp delete mode 100644 lib/IRremoteESP8266/src/ir_Carrier.cpp delete mode 100644 lib/IRremoteESP8266/src/ir_Corona.cpp delete mode 100644 lib/IRremoteESP8266/src/ir_Daikin.cpp delete mode 100644 lib/IRremoteESP8266/src/ir_Delonghi.cpp delete mode 100644 lib/IRremoteESP8266/src/ir_Denon.cpp delete mode 100644 lib/IRremoteESP8266/src/ir_Dish.cpp delete mode 100644 lib/IRremoteESP8266/src/ir_Doshisha.cpp delete mode 100644 lib/IRremoteESP8266/src/ir_Ecoclim.cpp delete mode 100644 lib/IRremoteESP8266/src/ir_EliteScreens.cpp delete mode 100644 lib/IRremoteESP8266/src/ir_Epson.cpp delete mode 100644 lib/IRremoteESP8266/src/ir_Fujitsu.cpp delete mode 100644 lib/IRremoteESP8266/src/ir_GICable.cpp delete mode 100644 lib/IRremoteESP8266/src/ir_GlobalCache.cpp delete mode 100644 lib/IRremoteESP8266/src/ir_Goodweather.cpp delete mode 100644 lib/IRremoteESP8266/src/ir_Hitachi.cpp delete mode 100644 lib/IRremoteESP8266/src/ir_Inax.cpp delete mode 100644 lib/IRremoteESP8266/src/ir_JVC.cpp delete mode 100644 lib/IRremoteESP8266/src/ir_Kelon.cpp delete mode 100644 lib/IRremoteESP8266/src/ir_Kelvinator.cpp delete mode 100644 lib/IRremoteESP8266/src/ir_LG.cpp delete mode 100644 lib/IRremoteESP8266/src/ir_Lasertag.cpp delete mode 100644 lib/IRremoteESP8266/src/ir_Lego.cpp delete mode 100644 lib/IRremoteESP8266/src/ir_Lutron.cpp delete mode 100644 lib/IRremoteESP8266/src/ir_MWM.cpp delete mode 100644 lib/IRremoteESP8266/src/ir_Magiquest.cpp delete mode 100644 lib/IRremoteESP8266/src/ir_Metz.cpp delete mode 100644 lib/IRremoteESP8266/src/ir_Midea.cpp delete mode 100644 lib/IRremoteESP8266/src/ir_MilesTag2.cpp delete mode 100644 lib/IRremoteESP8266/src/ir_MitsubishiHeavy.cpp delete mode 100644 lib/IRremoteESP8266/src/ir_Multibrackets.cpp delete mode 100644 lib/IRremoteESP8266/src/ir_NEC.cpp delete mode 100644 lib/IRremoteESP8266/src/ir_Neoclima.cpp delete mode 100644 lib/IRremoteESP8266/src/ir_Nikai.cpp delete mode 100644 lib/IRremoteESP8266/src/ir_Panasonic.cpp delete mode 100644 lib/IRremoteESP8266/src/ir_Pioneer.cpp delete mode 100644 lib/IRremoteESP8266/src/ir_Pronto.cpp delete mode 100644 lib/IRremoteESP8266/src/ir_RC5_RC6.cpp delete mode 100644 lib/IRremoteESP8266/src/ir_RCMM.cpp delete mode 100644 lib/IRremoteESP8266/src/ir_Rhoss.cpp delete mode 100644 lib/IRremoteESP8266/src/ir_Sanyo.cpp delete mode 100644 lib/IRremoteESP8266/src/ir_Sharp.cpp delete mode 100644 lib/IRremoteESP8266/src/ir_Sherwood.cpp delete mode 100644 lib/IRremoteESP8266/src/ir_Symphony.cpp delete mode 100644 lib/IRremoteESP8266/src/ir_Tcl.cpp delete mode 100644 lib/IRremoteESP8266/src/ir_Technibel.cpp delete mode 100644 lib/IRremoteESP8266/src/ir_Teco.cpp delete mode 100644 lib/IRremoteESP8266/src/ir_Teknopoint.cpp delete mode 100644 lib/IRremoteESP8266/src/ir_Transcold.cpp delete mode 100644 lib/IRremoteESP8266/src/ir_Trotec.cpp delete mode 100644 lib/IRremoteESP8266/src/ir_Truma.cpp delete mode 100644 lib/IRremoteESP8266/src/ir_Vestel.cpp delete mode 100644 lib/IRremoteESP8266/src/ir_Voltas.cpp delete mode 100644 lib/IRremoteESP8266/src/ir_Whirlpool.cpp delete mode 100644 lib/IRremoteESP8266/src/ir_Whynter.cpp delete mode 100644 lib/IRremoteESP8266/src/ir_Xmp.cpp delete mode 100644 lib/IRremoteESP8266/src/ir_Zepeal.cpp diff --git a/lib/IRremoteESP8266/src/IRrecv.h b/lib/IRremoteESP8266/src/IRrecv.h index 75070172d4..3ae55e9d19 100644 --- a/lib/IRremoteESP8266/src/IRrecv.h +++ b/lib/IRremoteESP8266/src/IRrecv.h @@ -12,7 +12,7 @@ #include #define __STDC_LIMIT_MACROS #include -#include "IRremoteESP8266.h" +#include "../src/IRremoteESP8266.h" // Constants const uint16_t kHeader = 2; // Usual nr. of header entries. diff --git a/lib/IRremoteESP8266/src/IRsend.h b/lib/IRremoteESP8266/src/IRsend.h index 3696eb40ff..fc2722cab6 100644 --- a/lib/IRremoteESP8266/src/IRsend.h +++ b/lib/IRremoteESP8266/src/IRsend.h @@ -6,7 +6,7 @@ #define __STDC_LIMIT_MACROS #include -#include "IRremoteESP8266.h" +#include "../src/IRremoteESP8266.h" // Originally from https://github.com/shirriff/Arduino-IRremote/ // Updated by markszabo (https://github.com/crankyoldgit/IRremoteESP8266) for diff --git a/lib/IRremoteESP8266/src/IRtimer.cpp b/lib/IRremoteESP8266/src/IRtimer.cpp deleted file mode 100644 index e1f098b280..0000000000 --- a/lib/IRremoteESP8266/src/IRtimer.cpp +++ /dev/null @@ -1,78 +0,0 @@ -// Copyright 2017 David Conran - -#include "IRtimer.h" -#ifndef UNIT_TEST -#include -#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 diff --git a/lib/IRremoteESP8266/src/IRutils.h b/lib/IRremoteESP8266/src/IRutils.h index a5dcde0432..51a44e657a 100644 --- a/lib/IRremoteESP8266/src/IRutils.h +++ b/lib/IRremoteESP8266/src/IRutils.h @@ -11,8 +11,8 @@ #ifndef ARDUINO #include #endif -#include "IRremoteESP8266.h" -#include "IRrecv.h" +#include "../src/IRremoteESP8266.h" +#include "../src/IRrecv.h" const uint8_t kNibbleSize = 4; const uint8_t kLowNibble = 0; diff --git a/lib/IRremoteESP8266/src/ir_Airwell.cpp b/lib/IRremoteESP8266/src/ir_Airwell.cpp deleted file mode 100644 index 26053442ee..0000000000 --- a/lib/IRremoteESP8266/src/ir_Airwell.cpp +++ /dev/null @@ -1,286 +0,0 @@ -// Copyright 2020 David Conran -#include "ir_Airwell.h" -#include -#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; -} diff --git a/lib/IRremoteESP8266/src/ir_Airwell.h b/lib/IRremoteESP8266/src/ir_Airwell.h index 814719f1f1..68e5100dbe 100644 --- a/lib/IRremoteESP8266/src/ir_Airwell.h +++ b/lib/IRremoteESP8266/src/ir_Airwell.h @@ -17,10 +17,10 @@ #ifndef UNIT_TEST #include #endif -#include "IRremoteESP8266.h" -#include "IRsend.h" +#include "../src/IRremoteESP8266.h" +#include "../src/IRsend.h" #ifdef UNIT_TEST -#include "IRsend_test.h" +#include "../src/IRsend_test.h" #endif /// Native representation of a Airwell A/C message. diff --git a/lib/IRremoteESP8266/src/ir_Aiwa.cpp b/lib/IRremoteESP8266/src/ir_Aiwa.cpp deleted file mode 100644 index 266fe67d68..0000000000 --- a/lib/IRremoteESP8266/src/ir_Aiwa.cpp +++ /dev/null @@ -1,105 +0,0 @@ -// 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 diff --git a/lib/IRremoteESP8266/src/ir_Amcor.cpp b/lib/IRremoteESP8266/src/ir_Amcor.cpp deleted file mode 100644 index c2aea5cdd3..0000000000 --- a/lib/IRremoteESP8266/src/ir_Amcor.cpp +++ /dev/null @@ -1,354 +0,0 @@ -// 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 -#include -#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; -} diff --git a/lib/IRremoteESP8266/src/ir_Amcor.h b/lib/IRremoteESP8266/src/ir_Amcor.h index 62ea50d9d8..21c4ff2604 100644 --- a/lib/IRremoteESP8266/src/ir_Amcor.h +++ b/lib/IRremoteESP8266/src/ir_Amcor.h @@ -17,10 +17,10 @@ #ifndef UNIT_TEST #include #endif -#include "IRremoteESP8266.h" -#include "IRsend.h" +#include "../src/IRremoteESP8266.h" +#include "../src/IRsend.h" #ifdef UNIT_TEST -#include "IRsend_test.h" +#include "../src/IRsend_test.h" #endif diff --git a/lib/IRremoteESP8266/src/ir_Argo.cpp b/lib/IRremoteESP8266/src/ir_Argo.cpp deleted file mode 100644 index e1df28efbb..0000000000 --- a/lib/IRremoteESP8266/src/ir_Argo.cpp +++ /dev/null @@ -1,470 +0,0 @@ -// Copyright 2017 Schmolders -// Copyright 2019 crankyoldgit -/// @file -/// @brief Argo A/C protocol. -/// Controls an Argo Ulisse 13 DCI A/C - -#include "ir_Argo.h" -#include -#include -#ifndef UNIT_TEST -#include -#endif // UNIT_TEST -#include "IRremoteESP8266.h" -#include "IRtext.h" -#include "IRutils.h" - -// Constants -// using SPACE modulation. MARK is always const 400u -const uint16_t kArgoHdrMark = 6400; -const uint16_t kArgoHdrSpace = 3300; -const uint16_t kArgoBitMark = 400; -const uint16_t kArgoOneSpace = 2200; -const uint16_t kArgoZeroSpace = 900; -const uint32_t kArgoGap = kDefaultMessageGap; // Made up value. Complete guess. - -using irutils::addBoolToString; -using irutils::addIntToString; -using irutils::addLabeledString; -using irutils::addModeToString; -using irutils::addTempToString; - -#if SEND_ARGO -/// Send a Argo A/C formatted message. -/// Status: BETA / Probably works. -/// @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::sendArgo(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 < kArgoStateLength) return; - // TODO(kaschmo): validate - sendGeneric(kArgoHdrMark, kArgoHdrSpace, kArgoBitMark, kArgoOneSpace, - kArgoBitMark, kArgoZeroSpace, 0, 0, // No Footer. - data, nbytes, 38, false, repeat, kDutyDefault); -} -#endif // SEND_ARGO - -/// 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? -IRArgoAC::IRArgoAC(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 IRArgoAC::begin(void) { _irsend.begin(); } - -#if SEND_ARGO -/// Send the current internal state as an IR message. -/// @param[in] repeat Nr. of times the message will be repeated. -void IRArgoAC::send(const uint16_t repeat) { - _irsend.sendArgo(getRaw(), kArgoStateLength, repeat); -} -#endif // SEND_ARGO - -/// 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. -uint8_t IRArgoAC::calcChecksum(const uint8_t state[], const uint16_t length) { - // Corresponds to byte 11 being constant 0b01 - // Only add up bytes to 9. byte 10 is 0b01 constant anyway. - // Assume that argo array is MSB first (left) - return sumBytes(state, length - 2, 2); -} - -/// 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 IRArgoAC::validChecksum(const uint8_t state[], const uint16_t length) { - return ((state[length - 2] >> 2) + (state[length - 1] << 6)) == - IRArgoAC::calcChecksum(state, length); -} - -/// Update the checksum for the internal state. -void IRArgoAC::checksum(void) { - uint8_t sum = IRArgoAC::calcChecksum(_.raw, kArgoStateLength); - // Append sum to end of array - // Set const part of checksum bit 10 - _.raw[10] = 0b00000010; - _.Sum = sum; -} - -/// Reset the internals of the object to a known good state. -void IRArgoAC::stateReset(void) { - for (uint8_t i = 0; i < kArgoStateLength; i++) _.raw[i] = 0x0; - - // Argo Message. Store MSB left. - // Default message: - _.raw[0] = 0b10101100; // LSB first (as sent) 0b00110101; //const preamble - _.raw[1] = 0b11110101; // LSB first: 0b10101111; //const preamble - // Keep payload 2-9 at zero - _.raw[10] = 0b00000010; // Const 01 - _.Sum = 0; - - off(); - setTemp(20); - setRoomTemp(25); - setMode(kArgoAuto); - setFan(kArgoFanAuto); -} - -/// 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* IRArgoAC::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 IRArgoAC::setRaw(const uint8_t state[]) { - std::memcpy(_.raw, state, kArgoStateLength); -} - -/// Set the internal state to have the power on. -void IRArgoAC::on(void) { setPower(true); } - -/// Set the internal state to have the power off. -void IRArgoAC::off(void) { setPower(false); } - -/// Set the internal state to have the desired power. -/// @param[in] on The desired power state. -void IRArgoAC::setPower(const bool on) { - _.Power = on; -} - -/// Get the power setting from the internal state. -/// @return A boolean indicating the power setting. -bool IRArgoAC::getPower(void) const { return _.Power; } - -/// Control the current Max setting. (i.e. Turbo) -/// @param[in] on The desired setting. -void IRArgoAC::setMax(const bool on) { - _.Max = on; -} - -/// Is the Max (i.e. Turbo) setting on? -/// @return The current value. -bool IRArgoAC::getMax(void) const { return _.Max; } - -/// Set the temperature. -/// @param[in] degrees The temperature in degrees celsius. -/// @note Sending 0 equals +4 -void IRArgoAC::setTemp(const uint8_t degrees) { - uint8_t temp = std::max(kArgoMinTemp, degrees); - // delta 4 degrees. "If I want 12 degrees, I need to send 8" - temp = std::min(kArgoMaxTemp, temp) - kArgoTempDelta; - // mask out bits - // argo[13] & 0x00000100; // mask out ON/OFF Bit - _.Temp = temp; -} - -/// Get the current temperature setting. -/// @return The current setting for temp. in degrees celsius. -uint8_t IRArgoAC::getTemp(void) const { - return _.Temp + kArgoTempDelta; -} - -/// Set the speed of the fan. -/// @param[in] fan The desired setting. -void IRArgoAC::setFan(const uint8_t fan) { - _.Fan = std::min(fan, kArgoFan3); -} - -/// Get the current fan speed setting. -/// @return The current fan speed. -uint8_t IRArgoAC::getFan(void) const { - return _.Fan; -} - -/// Set the flap position. i.e. Swing. -/// @warning Not yet working! -/// @param[in] flap The desired setting. -void IRArgoAC::setFlap(const uint8_t flap) { - flap_mode = flap; - // TODO(kaschmo): set correct bits for flap mode -} - -/// Get the flap position. i.e. Swing. -/// @warning Not yet working! -/// @return The current flap setting. -uint8_t IRArgoAC::getFlap(void) const { return flap_mode; } - -/// Get the current operation mode setting. -/// @return The current operation mode. -uint8_t IRArgoAC::getMode(void) const { - return _.Mode; -} - -/// Set the desired operation mode. -/// @param[in] mode The desired operation mode. -void IRArgoAC::setMode(const uint8_t mode) { - switch (mode) { - case kArgoCool: - case kArgoDry: - case kArgoAuto: - case kArgoOff: - case kArgoHeat: - case kArgoHeatAuto: - _.Mode = mode; - return; - default: - _.Mode = kArgoAuto; - } -} - -/// Turn on/off the Night mode. i.e. Sleep. -/// @param[in] on The desired setting. -void IRArgoAC::setNight(const bool on) { - _.Night = on; -} - -/// Get the status of Night mode. i.e. Sleep. -/// @return true if on, false if off. -bool IRArgoAC::getNight(void) const { return _.Night; } - -/// Turn on/off the iFeel mode. -/// @param[in] on The desired setting. -void IRArgoAC::setiFeel(const bool on) { - _.iFeel = on; -} - -/// Get the status of iFeel mode. -/// @return true if on, false if off. -bool IRArgoAC::getiFeel(void) const { return _.iFeel; } - -/// Set the time for the A/C -/// @warning Not yet working! -void IRArgoAC::setTime(void) { - // TODO(kaschmo): use function call from checksum to set time first -} - -/// Set the value for the current room temperature. -/// @param[in] degrees The temperature in degrees celsius. -void IRArgoAC::setRoomTemp(const uint8_t degrees) { - uint8_t temp = std::min(degrees, kArgoMaxRoomTemp); - temp = std::max(temp, kArgoTempDelta) - kArgoTempDelta; - _.RoomTemp = temp; -} - -/// Get the currently stored value for the room temperature setting. -/// @return The current setting for the room temp. in degrees celsius. -uint8_t IRArgoAC::getRoomTemp(void) const { - return _.RoomTemp + kArgoTempDelta; -} - -/// 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 IRArgoAC::convertMode(const stdAc::opmode_t mode) { - switch (mode) { - case stdAc::opmode_t::kCool: - return kArgoCool; - case stdAc::opmode_t::kHeat: - return kArgoHeat; - case stdAc::opmode_t::kDry: - return kArgoDry; - case stdAc::opmode_t::kOff: - return kArgoOff; - // No fan mode. - default: - return kArgoAuto; - } -} - -/// 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 IRArgoAC::convertFan(const stdAc::fanspeed_t speed) { - switch (speed) { - case stdAc::fanspeed_t::kMin: - case stdAc::fanspeed_t::kLow: - return kArgoFan1; - case stdAc::fanspeed_t::kMedium: - return kArgoFan2; - case stdAc::fanspeed_t::kHigh: - case stdAc::fanspeed_t::kMax: - return kArgoFan3; - default: - return kArgoFanAuto; - } -} - -/// 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 IRArgoAC::convertSwingV(const stdAc::swingv_t position) { - switch (position) { - case stdAc::swingv_t::kHighest: - return kArgoFlapFull; - case stdAc::swingv_t::kHigh: - return kArgoFlap5; - case stdAc::swingv_t::kMiddle: - return kArgoFlap4; - case stdAc::swingv_t::kLow: - return kArgoFlap3; - case stdAc::swingv_t::kLowest: - return kArgoFlap1; - default: - return kArgoFlapAuto; - } -} - -/// 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 IRArgoAC::toCommonMode(const uint8_t mode) { - switch (mode) { - case kArgoCool: return stdAc::opmode_t::kCool; - case kArgoHeat: return stdAc::opmode_t::kHeat; - case kArgoDry: return stdAc::opmode_t::kDry; - // No fan mode. - 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 IRArgoAC::toCommonFanSpeed(const uint8_t speed) { - switch (speed) { - case kArgoFan3: return stdAc::fanspeed_t::kMax; - case kArgoFan2: return stdAc::fanspeed_t::kMedium; - case kArgoFan1: 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 IRArgoAC::toCommon(void) const { - stdAc::state_t result; - result.protocol = decode_type_t::ARGO; - result.power = _.Power; - result.mode = toCommonMode(_.Mode); - result.celsius = true; - result.degrees = getTemp(); - result.fanspeed = toCommonFanSpeed(_.Fan); - result.turbo = _.Max; - result.sleep = _.Night ? 0 : -1; - // Not supported. - result.model = -1; // Not supported. - 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 IRArgoAC::toString(void) const { - String result = ""; - result.reserve(100); // Reserve some heap for the string to reduce fragging. - result += addBoolToString(_.Power, kPowerStr, false); - result += addIntToString(_.Mode, kModeStr); - result += kSpaceLBraceStr; - switch (_.Mode) { - case kArgoAuto: - result += kAutoStr; - break; - case kArgoCool: - result += kCoolStr; - break; - case kArgoHeat: - result += kHeatStr; - break; - case kArgoDry: - result += kDryStr; - break; - case kArgoHeatAuto: - result += kHeatStr; - result += ' '; - result += kAutoStr; - break; - case kArgoOff: - result += kOffStr; - break; - default: - result += kUnknownStr; - } - result += ')'; - result += addIntToString(_.Fan, kFanStr); - result += kSpaceLBraceStr; - switch (_.Fan) { - case kArgoFanAuto: - result += kAutoStr; - break; - case kArgoFan3: - result += kMaxStr; - break; - case kArgoFan1: - result += kMinStr; - break; - case kArgoFan2: - result += kMedStr; - break; - default: - result += kUnknownStr; - } - result += ')'; - result += addTempToString(getTemp()); - result += kCommaSpaceStr; - result += kRoomStr; - result += ' '; - result += addTempToString(getRoomTemp(), true, false); - result += addBoolToString(_.Max, kMaxStr); - result += addBoolToString(_.iFeel, kIFeelStr); - result += addBoolToString(_.Night, kNightStr); - return result; -} - -#if DECODE_ARGO -/// Decode the supplied Argo 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 This decoder is based soley off sendArgo(). We have no actual captures -/// to test this against. If you have one of these units, please let us know. -bool IRrecv::decodeArgo(decode_results *results, uint16_t offset, - const uint16_t nbits, - const bool strict) { - if (strict && nbits != kArgoBits) return false; - - // Match Header + Data - if (!matchGeneric(results->rawbuf + offset, results->state, - results->rawlen - offset, nbits, - kArgoHdrMark, kArgoHdrSpace, - kArgoBitMark, kArgoOneSpace, - kArgoBitMark, kArgoZeroSpace, - 0, 0, // Footer (None, allegedly. This seems very wrong.) - true, _tolerance, 0, false)) return false; - - // Compliance - // Verify we got a valid checksum. - if (strict && !IRArgoAC::validChecksum(results->state)) return false; - // Success - results->decode_type = decode_type_t::ARGO; - 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_ARGO diff --git a/lib/IRremoteESP8266/src/ir_Argo.h b/lib/IRremoteESP8266/src/ir_Argo.h index 6ceb58e421..bc1844789a 100644 --- a/lib/IRremoteESP8266/src/ir_Argo.h +++ b/lib/IRremoteESP8266/src/ir_Argo.h @@ -11,10 +11,10 @@ #ifndef UNIT_TEST #include #endif -#include "IRremoteESP8266.h" -#include "IRsend.h" +#include "../src/IRremoteESP8266.h" +#include "../src/IRsend.h" #ifdef UNIT_TEST -#include "IRsend_test.h" +#include "../src/IRsend_test.h" #endif diff --git a/lib/IRremoteESP8266/src/ir_Arris.cpp b/lib/IRremoteESP8266/src/ir_Arris.cpp deleted file mode 100644 index 5b39808c8f..0000000000 --- a/lib/IRremoteESP8266/src/ir_Arris.cpp +++ /dev/null @@ -1,123 +0,0 @@ -// 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(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 diff --git a/lib/IRremoteESP8266/src/ir_Bose.cpp b/lib/IRremoteESP8266/src/ir_Bose.cpp deleted file mode 100644 index a57d125b3c..0000000000 --- a/lib/IRremoteESP8266/src/ir_Bose.cpp +++ /dev/null @@ -1,69 +0,0 @@ -// 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 diff --git a/lib/IRremoteESP8266/src/ir_Carrier.cpp b/lib/IRremoteESP8266/src/ir_Carrier.cpp deleted file mode 100644 index 92fca4bd2c..0000000000 --- a/lib/IRremoteESP8266/src/ir_Carrier.cpp +++ /dev/null @@ -1,535 +0,0 @@ -// Copyright 2018, 2020 David Conran -/// @file -/// @brief Carrier protocols. -/// @see CarrierAc https://github.com/crankyoldgit/IRremoteESP8266/issues/385 -/// @see CarrierAc64 https://github.com/crankyoldgit/IRremoteESP8266/issues/1127 - -#include "ir_Carrier.h" -#include -#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. - - -#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(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; -} diff --git a/lib/IRremoteESP8266/src/ir_Carrier.h b/lib/IRremoteESP8266/src/ir_Carrier.h index aa9ea8447c..734e702b6d 100644 --- a/lib/IRremoteESP8266/src/ir_Carrier.h +++ b/lib/IRremoteESP8266/src/ir_Carrier.h @@ -20,10 +20,10 @@ #ifndef UNIT_TEST #include #endif -#include "IRremoteESP8266.h" -#include "IRsend.h" +#include "../src/IRremoteESP8266.h" +#include "../src/IRsend.h" #ifdef UNIT_TEST -#include "IRsend_test.h" +#include "../src/IRsend_test.h" #endif /// Native representation of a Carrier A/C message. diff --git a/lib/IRremoteESP8266/src/ir_Coolix.h b/lib/IRremoteESP8266/src/ir_Coolix.h index 2155b54b05..da2c514cc1 100644 --- a/lib/IRremoteESP8266/src/ir_Coolix.h +++ b/lib/IRremoteESP8266/src/ir_Coolix.h @@ -34,10 +34,10 @@ #ifndef UNIT_TEST #include #endif -#include "IRremoteESP8266.h" -#include "IRsend.h" +#include "../src/IRremoteESP8266.h" +#include "../src/IRsend.h" #ifdef UNIT_TEST -#include "IRsend_test.h" +#include "../src/IRsend_test.h" #endif // Constants diff --git a/lib/IRremoteESP8266/src/ir_Corona.cpp b/lib/IRremoteESP8266/src/ir_Corona.cpp deleted file mode 100644 index ef40c241a0..0000000000 --- a/lib/IRremoteESP8266/src/ir_Corona.cpp +++ /dev/null @@ -1,575 +0,0 @@ -// Copyright 2020 Christian Nilsson -// -/// @file -/// @brief Corona A/C protocol -/// @note Unsupported: -/// - Auto/Max button press (special format) - -#include "ir_Corona.h" -#include -#include -#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(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(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; -} diff --git a/lib/IRremoteESP8266/src/ir_Corona.h b/lib/IRremoteESP8266/src/ir_Corona.h index cbe3e99e47..43d211dfcd 100644 --- a/lib/IRremoteESP8266/src/ir_Corona.h +++ b/lib/IRremoteESP8266/src/ir_Corona.h @@ -20,10 +20,10 @@ #ifndef UNIT_TEST #include #endif -#include "IRremoteESP8266.h" -#include "IRsend.h" +#include "../src/IRremoteESP8266.h" +#include "../src/IRsend.h" #ifdef UNIT_TEST -#include "IRsend_test.h" +#include "../src/IRsend_test.h" #endif /// Native representation of a section of a Corona A/C message. diff --git a/lib/IRremoteESP8266/src/ir_Daikin.cpp b/lib/IRremoteESP8266/src/ir_Daikin.cpp deleted file mode 100644 index ea11daf781..0000000000 --- a/lib/IRremoteESP8266/src/ir_Daikin.cpp +++ /dev/null @@ -1,3735 +0,0 @@ -// Copyright 2016 sillyfrog -// Copyright 2017 sillyfrog, crankyoldgit -// Copyright 2018-2021 crankyoldgit -// Copyright 2019 pasna (IRDaikin160 class / Daikin176 class) - -/// @file -/// @brief Support for Daikin A/C protocols. -/// @see Daikin http://harizanov.com/2012/02/control-daikin-air-conditioner-over-the-internet/ -/// @see Daikin https://github.com/mharizanov/Daikin-AC-remote-control-over-the-Internet/tree/master/IRremote -/// @see Daikin http://rdlab.cdmt.vn/project-2013/daikin-ir-protocol -/// @see Daikin https://github.com/blafois/Daikin-IR-Reverse -/// @see Daikin128 https://github.com/crankyoldgit/IRremoteESP8266/issues/827 -/// @see Daikin152 https://github.com/crankyoldgit/IRremoteESP8266/issues/873 -/// @see Daikin152 https://github.com/ToniA/arduino-heatpumpir/blob/master/DaikinHeatpumpARC480A14IR.cpp -/// @see Daikin152 https://github.com/ToniA/arduino-heatpumpir/blob/master/DaikinHeatpumpARC480A14IR.h -/// @see Daikin160 https://github.com/crankyoldgit/IRremoteESP8266/issues/731 -/// @see Daikin2 https://docs.google.com/spreadsheets/d/1f8EGfIbBUo2B-CzUFdrgKQprWakoYNKM80IKZN4KXQE/edit#gid=236366525&range=B25:D32 -/// @see Daikin2 https://github.com/crankyoldgit/IRremoteESP8266/issues/582 -/// @see Daikin2 https://github.com/crankyoldgit/IRremoteESP8266/issues/1535 -/// @see Daikin2 https://www.daikin.co.nz/sites/default/files/daikin-split-system-US7-FTXZ25-50NV1B.pdf -/// @see Daikin216 https://github.com/crankyoldgit/IRremoteESP8266/issues/689 -/// @see Daikin216 https://github.com/danny-source/Arduino_DY_IRDaikin -/// @see Daikin64 https://github.com/crankyoldgit/IRremoteESP8266/issues/1064 - -#include "ir_Daikin.h" -#include -#include -#ifndef ARDUINO -#include -#endif -#include "IRrecv.h" -#include "IRremoteESP8266.h" -#include "IRsend.h" -#ifdef UNIT_TEST -#include "IRsend_test.h" -#endif -#include "IRtext.h" -#include "IRutils.h" - -using irutils::addBoolToString; -using irutils::addDayToString; -using irutils::addIntToString; -using irutils::addLabeledString; -using irutils::addModeToString; -using irutils::addSwingHToString; -using irutils::addSwingVToString; -using irutils::addTempToString; -using irutils::addFanToString; -using irutils::bcdToUint8; -using irutils::minsToString; -using irutils::setBit; -using irutils::setBits; -using irutils::sumNibbles; -using irutils::uint8ToBcd; - -#if SEND_DAIKIN -/// Send a Daikin 280-bit A/C formatted message. -/// Status: STABLE -/// @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. -/// @see https://github.com/mharizanov/Daikin-AC-remote-control-over-the-Internet/tree/master/IRremote -/// @see https://github.com/blafois/Daikin-IR-Reverse -void IRsend::sendDaikin(const unsigned char data[], const uint16_t nbytes, - const uint16_t repeat) { - if (nbytes < kDaikinStateLengthShort) - return; // Not enough bytes to send a proper message. - - for (uint16_t r = 0; r <= repeat; r++) { - uint16_t offset = 0; - // Send the header, 0b00000 - sendGeneric(0, 0, // No header for the header - kDaikinBitMark, kDaikinOneSpace, kDaikinBitMark, - kDaikinZeroSpace, kDaikinBitMark, kDaikinZeroSpace + kDaikinGap, - (uint64_t)0b00000, kDaikinHeaderLength, 38, false, 0, 50); - // Data #1 - if (nbytes < kDaikinStateLength) { // Are we using the legacy size? - // Do this as a constant to save RAM and keep in flash memory - sendGeneric(kDaikinHdrMark, kDaikinHdrSpace, kDaikinBitMark, - kDaikinOneSpace, kDaikinBitMark, kDaikinZeroSpace, - kDaikinBitMark, kDaikinZeroSpace + kDaikinGap, - kDaikinFirstHeader64, 64, 38, false, 0, 50); - } else { // We are using the newer/more correct size. - sendGeneric(kDaikinHdrMark, kDaikinHdrSpace, kDaikinBitMark, - kDaikinOneSpace, kDaikinBitMark, kDaikinZeroSpace, - kDaikinBitMark, kDaikinZeroSpace + kDaikinGap, - data, kDaikinSection1Length, 38, false, 0, 50); - offset += kDaikinSection1Length; - } - // Data #2 - sendGeneric(kDaikinHdrMark, kDaikinHdrSpace, kDaikinBitMark, - kDaikinOneSpace, kDaikinBitMark, kDaikinZeroSpace, - kDaikinBitMark, kDaikinZeroSpace + kDaikinGap, - data + offset, kDaikinSection2Length, 38, false, 0, 50); - offset += kDaikinSection2Length; - // Data #3 - sendGeneric(kDaikinHdrMark, kDaikinHdrSpace, kDaikinBitMark, - kDaikinOneSpace, kDaikinBitMark, kDaikinZeroSpace, - kDaikinBitMark, kDaikinZeroSpace + kDaikinGap, - data + offset, nbytes - offset, 38, false, 0, 50); - } -} -#endif // SEND_DAIKIN - -/// 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? -IRDaikinESP::IRDaikinESP(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 IRDaikinESP::begin(void) { _irsend.begin(); } - -#if SEND_DAIKIN -/// Send the current internal state as an IR message. -/// @param[in] repeat Nr. of times the message will be repeated. -void IRDaikinESP::send(const uint16_t repeat) { - _irsend.sendDaikin(getRaw(), kDaikinStateLength, repeat); -} -#endif // SEND_DAIKIN - -/// 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 IRDaikinESP::validChecksum(uint8_t state[], const uint16_t length) { - // Data #1 - if (length < kDaikinSection1Length || - state[kDaikinByteChecksum1] != sumBytes(state, kDaikinSection1Length - 1)) - return false; - // Data #2 - if (length < kDaikinSection1Length + kDaikinSection2Length || - state[kDaikinByteChecksum2] != sumBytes(state + kDaikinSection1Length, - kDaikinSection2Length - 1)) - return false; - // Data #3 - if (length < kDaikinSection1Length + kDaikinSection2Length + 2 || - state[length - 1] != sumBytes(state + kDaikinSection1Length + - kDaikinSection2Length, - length - (kDaikinSection1Length + - kDaikinSection2Length) - 1)) - return false; - return true; -} - -/// Calculate and set the checksum values for the internal state. -void IRDaikinESP::checksum(void) { - _.Sum1 = sumBytes(_.raw, kDaikinSection1Length - 1); - _.Sum2 = sumBytes(_.raw + kDaikinSection1Length, kDaikinSection2Length - 1); - _.Sum3 = sumBytes(_.raw + kDaikinSection1Length + kDaikinSection2Length, - kDaikinSection3Length - 1); -} - -/// Reset the internal state to a fixed known good state. -void IRDaikinESP::stateReset(void) { - for (uint8_t i = 0; i < kDaikinStateLength; i++) _.raw[i] = 0x0; - - _.raw[0] = 0x11; - _.raw[1] = 0xDA; - _.raw[2] = 0x27; - _.raw[4] = 0xC5; - // _.raw[7] is a checksum byte, it will be set by checksum(). - _.raw[8] = 0x11; - _.raw[9] = 0xDA; - _.raw[10] = 0x27; - _.raw[12] = 0x42; - // _.raw[15] is a checksum byte, it will be set by checksum(). - _.raw[16] = 0x11; - _.raw[17] = 0xDA; - _.raw[18] = 0x27; - _.raw[21] = 0x49; - _.raw[22] = 0x1E; - _.raw[24] = 0xB0; - _.raw[27] = 0x06; - _.raw[28] = 0x60; - _.raw[31] = 0xC0; - // _.raw[34] is a checksum byte, it will be set by checksum(). - checksum(); -} - -/// 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 *IRDaikinESP::getRaw(void) { - checksum(); // Ensure correct settings before sending. - 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 Length of the code in bytes. -void IRDaikinESP::setRaw(const uint8_t new_code[], const uint16_t length) { - uint8_t offset = 0; - if (length == kDaikinStateLengthShort) { // Handle the "short" length case. - offset = kDaikinStateLength - kDaikinStateLengthShort; - stateReset(); - } - for (uint8_t i = 0; i < length && i < kDaikinStateLength; i++) - _.raw[i + offset] = new_code[i]; -} - -/// Change the power setting to On. -void IRDaikinESP::on(void) { setPower(true); } - -/// Change the power setting to Off. -void IRDaikinESP::off(void) { setPower(false); } - -/// Change the power setting. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRDaikinESP::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 IRDaikinESP::getPower(void) const { - return _.Power; -} - -/// Set the temperature. -/// @param[in] temp The temperature in degrees celsius. -void IRDaikinESP::setTemp(const uint8_t temp) { - uint8_t degrees = std::max(temp, kDaikinMinTemp); - degrees = std::min(degrees, kDaikinMaxTemp); - _.Temp = degrees; -} - -/// Get the current temperature setting. -/// @return The current setting for temp. in degrees celsius. -uint8_t IRDaikinESP::getTemp(void) const { return _.Temp; } - -/// Set the speed of the fan. -/// @param[in] fan The desired setting. -/// @note 1-5 or kDaikinFanAuto or kDaikinFanQuiet -void IRDaikinESP::setFan(const uint8_t fan) { - // Set the fan speed bits, leave low 4 bits alone - uint8_t fanset; - if (fan == kDaikinFanQuiet || fan == kDaikinFanAuto) - fanset = fan; - else if (fan < kDaikinFanMin || fan > kDaikinFanMax) - fanset = kDaikinFanAuto; - else - fanset = 2 + fan; - _.Fan = fanset; -} - -/// Get the current fan speed setting. -/// @return The current fan speed. -uint8_t IRDaikinESP::getFan(void) const { - uint8_t fan = _.Fan; - if (fan != kDaikinFanQuiet && fan != kDaikinFanAuto) fan -= 2; - return fan; -} - -/// Get the operating mode setting of the A/C. -/// @return The current operating mode setting. -uint8_t IRDaikinESP::getMode(void) const { - return _.Mode; -} - -/// Set the operating mode of the A/C. -/// @param[in] mode The desired operating mode. -void IRDaikinESP::setMode(const uint8_t mode) { - switch (mode) { - case kDaikinAuto: - case kDaikinCool: - case kDaikinHeat: - case kDaikinFan: - case kDaikinDry: - _.Mode = mode; - break; - default: - _.Mode = kDaikinAuto; - } -} - -/// Set the Vertical Swing mode of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRDaikinESP::setSwingVertical(const bool on) { - _.SwingV = (on ? kDaikinSwingOn : kDaikinSwingOff); -} - -/// Get the Vertical Swing mode of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRDaikinESP::getSwingVertical(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 IRDaikinESP::setSwingHorizontal(const bool on) { - _.SwingH = (on ? kDaikinSwingOn : kDaikinSwingOff); -} - -/// Get the Horizontal Swing mode of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRDaikinESP::getSwingHorizontal(void) const { - return _.SwingH; -} - -/// Set the Quiet mode of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRDaikinESP::setQuiet(const bool on) { - _.Quiet = on; - // Powerful & Quiet mode being on are mutually exclusive. - if (on) setPowerful(false); -} - -/// Get the Quiet mode status of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRDaikinESP::getQuiet(void) const { - return _.Quiet; -} - -/// Set the Powerful (Turbo) mode of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRDaikinESP::setPowerful(const bool on) { - _.Powerful = on; - if (on) { - // Powerful, Quiet, & Econo mode being on are mutually exclusive. - setQuiet(false); - setEcono(false); - } -} - -/// Get the Powerful (Turbo) mode of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRDaikinESP::getPowerful(void) const { - return _.Powerful; -} - -/// Set the Sensor mode of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRDaikinESP::setSensor(const bool on) { - _.Sensor = on; -} - -/// Get the Sensor mode of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRDaikinESP::getSensor(void) const { - return _.Sensor; -} - -/// Set the Economy mode of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRDaikinESP::setEcono(const bool on) { - _.Econo = on; - // Powerful & Econo mode being on are mutually exclusive. - if (on) setPowerful(false); -} - -/// Get the Economical mode of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRDaikinESP::getEcono(void) const { - return _.Econo; -} - -/// Set the Mould mode of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRDaikinESP::setMold(const bool on) { - _.Mold = on; -} - -/// Get the Mould mode status of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRDaikinESP::getMold(void) const { - return _.Mold; -} - -/// Set the Comfort mode of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRDaikinESP::setComfort(const bool on) { - _.Comfort = on; -} - -/// Get the Comfort mode of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRDaikinESP::getComfort(void) const { - return _.Comfort; -} - -/// Set the enable status & time of the On Timer. -/// @param[in] starttime The number of minutes past midnight. -void IRDaikinESP::enableOnTimer(const uint16_t starttime) { - _.OnTimer = true; - _.OnTime = starttime; -} - -/// Clear and disable the On timer. -void IRDaikinESP::disableOnTimer(void) { - _.OnTimer = false; - _.OnTime = kDaikinUnusedTime; -} - -/// Get the On Timer time to be sent to the A/C unit. -/// @return The number of minutes past midnight. -uint16_t IRDaikinESP::getOnTime(void) const { - return _.OnTime; -} - -/// Get the enable status of the On Timer. -/// @return true, the setting is on. false, the setting is off. -bool IRDaikinESP::getOnTimerEnabled(void) const { - return _.OnTimer; -} - -/// Set the enable status & time of the Off Timer. -/// @param[in] endtime The number of minutes past midnight. -void IRDaikinESP::enableOffTimer(const uint16_t endtime) { - _.OffTimer = true; - _.OffTime = endtime; -} - -/// Clear and disable the Off timer. -void IRDaikinESP::disableOffTimer(void) { - _.OffTimer = false; - _.OffTime = kDaikinUnusedTime; -} - -/// Get the Off Timer time to be sent to the A/C unit. -/// @return The number of minutes past midnight. -uint16_t IRDaikinESP::getOffTime(void) const { - return _.OffTime; -} - -/// Get the enable status of the Off Timer. -/// @return true, the setting is on. false, the setting is off. -bool IRDaikinESP::getOffTimerEnabled(void) const { - return _.OffTimer; -} - -/// Set the clock on the A/C unit. -/// @param[in] mins_since_midnight Nr. of minutes past midnight. -void IRDaikinESP::setCurrentTime(const uint16_t mins_since_midnight) { - uint16_t mins = mins_since_midnight; - if (mins > 24 * 60) mins = 0; // If > 23:59, set to 00:00 - _.CurrentTime = mins; -} - -/// Get the clock time to be sent to the A/C unit. -/// @return The number of minutes past midnight. -uint16_t IRDaikinESP::getCurrentTime(void) const { - return _.CurrentTime; -} - -/// Set the current day of the week to be sent to the A/C unit. -/// @param[in] day_of_week The numerical representation of the day of the week. -/// @note 1 is SUN, 2 is MON, ..., 7 is SAT -void IRDaikinESP::setCurrentDay(const uint8_t day_of_week) { - _.CurrentDay = day_of_week; -} - -/// Get the current day of the week to be sent to the A/C unit. -/// @return The numerical representation of the day of the week. -/// @note 1 is SUN, 2 is MON, ..., 7 is SAT -uint8_t IRDaikinESP::getCurrentDay(void) const { - return _.CurrentDay; -} - -/// Set the enable status of the Weekly Timer. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRDaikinESP::setWeeklyTimerEnable(const bool on) { - // Bit is cleared for `on`. - _.WeeklyTimer = !on; -} - -/// Get the enable status of the Weekly Timer. -/// @return true, the setting is on. false, the setting is off. -bool IRDaikinESP::getWeeklyTimerEnable(void) const { - return !_.WeeklyTimer; -} - -/// 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 IRDaikinESP::convertMode(const stdAc::opmode_t mode) { - switch (mode) { - case stdAc::opmode_t::kCool: return kDaikinCool; - case stdAc::opmode_t::kHeat: return kDaikinHeat; - case stdAc::opmode_t::kDry: return kDaikinDry; - case stdAc::opmode_t::kFan: return kDaikinFan; - default: return kDaikinAuto; - } -} - -/// 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 IRDaikinESP::convertFan(const stdAc::fanspeed_t speed) { - switch (speed) { - case stdAc::fanspeed_t::kMin: return kDaikinFanQuiet; - case stdAc::fanspeed_t::kLow: return kDaikinFanMin; - case stdAc::fanspeed_t::kMedium: return kDaikinFanMed; - case stdAc::fanspeed_t::kHigh: return kDaikinFanMax - 1; - case stdAc::fanspeed_t::kMax: return kDaikinFanMax; - default: return kDaikinFanAuto; - } -} - -/// 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 IRDaikinESP::toCommonMode(const uint8_t mode) { - switch (mode) { - case kDaikinCool: return stdAc::opmode_t::kCool; - case kDaikinHeat: return stdAc::opmode_t::kHeat; - case kDaikinDry: return stdAc::opmode_t::kDry; - case kDaikinFan: 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 IRDaikinESP::toCommonFanSpeed(const uint8_t speed) { - switch (speed) { - case kDaikinFanMax: return stdAc::fanspeed_t::kMax; - case kDaikinFanMax - 1: return stdAc::fanspeed_t::kHigh; - case kDaikinFanMed: - case kDaikinFanMin + 1: return stdAc::fanspeed_t::kMedium; - case kDaikinFanMin: return stdAc::fanspeed_t::kLow; - case kDaikinFanQuiet: 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 IRDaikinESP::toCommon(void) const { - stdAc::state_t result; - result.protocol = decode_type_t::DAIKIN; - result.model = -1; // No models used. - result.power = _.Power; - result.mode = toCommonMode(_.Mode); - result.celsius = true; - result.degrees = _.Temp; - result.fanspeed = toCommonFanSpeed(getFan()); - 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 = _.Powerful; - result.clean = _.Mold; - result.econo = _.Econo; - // Not supported. - result.filter = false; - result.light = 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 IRDaikinESP::toString(void) const { - String result = ""; - result.reserve(230); // Reserve some heap for the string to reduce fragging. - result += addBoolToString(_.Power, kPowerStr, false); - result += addModeToString(_.Mode, kDaikinAuto, kDaikinCool, kDaikinHeat, - kDaikinDry, kDaikinFan); - result += addTempToString(_.Temp); - result += addFanToString(getFan(), kDaikinFanMax, kDaikinFanMin, - kDaikinFanAuto, kDaikinFanQuiet, kDaikinFanMed); - result += addBoolToString(_.Powerful, kPowerfulStr); - result += addBoolToString(_.Quiet, kQuietStr); - result += addBoolToString(getSensor(), kSensorStr); - result += addBoolToString(_.Mold, kMouldStr); - result += addBoolToString(_.Comfort, kComfortStr); - result += addBoolToString(_.SwingH, kSwingHStr); - result += addBoolToString(_.SwingV, kSwingVStr); - result += addLabeledString(minsToString(_.CurrentTime), kClockStr); - result += addDayToString(_.CurrentDay, -1); - result += addLabeledString(_.OnTimer - ? minsToString(_.OnTime) : kOffStr, - kOnTimerStr); - result += addLabeledString(_.OffTimer - ? minsToString(_.OffTime) : kOffStr, - kOffTimerStr); - result += addBoolToString(getWeeklyTimerEnable(), kWeeklyTimerStr); - return result; -} - -#if DECODE_DAIKIN -/// Decode the supplied Daikin 280-bit message. (DAIKIN) -/// 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. -/// @see https://github.com/mharizanov/Daikin-AC-remote-control-over-the-Internet/tree/master/IRremote -bool IRrecv::decodeDaikin(decode_results *results, uint16_t offset, - const uint16_t nbits, const bool strict) { - // Is there enough data to match successfully? - if (results->rawlen < (2 * (nbits + kDaikinHeaderLength) + - kDaikinSections * (kHeader + kFooter) + kFooter - 1) + - offset) - return false; - - // Compliance - if (strict && nbits != kDaikinBits) return false; - - match_result_t data_result; - - // Header #1 - Doesn't count as data. - data_result = matchData(&(results->rawbuf[offset]), kDaikinHeaderLength, - kDaikinBitMark, kDaikinOneSpace, - kDaikinBitMark, kDaikinZeroSpace, - kDaikinTolerance, kDaikinMarkExcess, false); - offset += data_result.used; - if (data_result.success == false) return false; // Fail - if (data_result.data) return false; // The header bits should be zero. - // Footer - if (!matchMark(results->rawbuf[offset++], kDaikinBitMark, - kDaikinTolerance, kDaikinMarkExcess)) return false; - if (!matchSpace(results->rawbuf[offset++], kDaikinZeroSpace + kDaikinGap, - kDaikinTolerance, kDaikinMarkExcess)) return false; - // Sections - const uint8_t ksectionSize[kDaikinSections] = { - kDaikinSection1Length, kDaikinSection2Length, kDaikinSection3Length}; - uint16_t pos = 0; - for (uint8_t section = 0; section < kDaikinSections; section++) { - uint16_t used; - // Section Header + Section Data (7 bytes) + Section Footer - used = matchGeneric(results->rawbuf + offset, results->state + pos, - results->rawlen - offset, ksectionSize[section] * 8, - kDaikinHdrMark, kDaikinHdrSpace, - kDaikinBitMark, kDaikinOneSpace, - kDaikinBitMark, kDaikinZeroSpace, - kDaikinBitMark, kDaikinZeroSpace + kDaikinGap, - section >= kDaikinSections - 1, - kDaikinTolerance, kDaikinMarkExcess, false); - if (used == 0) return false; - offset += used; - pos += ksectionSize[section]; - } - // Compliance - if (strict) { - // Re-check we got the correct size/length due to the way we read the data. - if (pos * 8 != kDaikinBits) return false; - // Validate the checksum. - if (!IRDaikinESP::validChecksum(results->state)) return false; - } - - // Success - results->decode_type = DAIKIN; - 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_DAIKIN - -#if SEND_DAIKIN2 -/// Send a Daikin2 (312-bit) A/C formatted message. -/// Status: STABLE / Expected to work. -/// @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. -/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/582 -void IRsend::sendDaikin2(const unsigned char data[], const uint16_t nbytes, - const uint16_t repeat) { - if (nbytes < kDaikin2Section1Length) - return; // Not enough bytes to send a partial message. - - for (uint16_t r = 0; r <= repeat; r++) { - // Leader - sendGeneric(kDaikin2LeaderMark, kDaikin2LeaderSpace, - 0, 0, 0, 0, 0, 0, (uint64_t) 0, // No data payload. - 0, kDaikin2Freq, false, 0, 50); - // Section #1 - sendGeneric(kDaikin2HdrMark, kDaikin2HdrSpace, kDaikin2BitMark, - kDaikin2OneSpace, kDaikin2BitMark, kDaikin2ZeroSpace, - kDaikin2BitMark, kDaikin2Gap, data, kDaikin2Section1Length, - kDaikin2Freq, false, 0, 50); - // Section #2 - sendGeneric(kDaikin2HdrMark, kDaikin2HdrSpace, kDaikin2BitMark, - kDaikin2OneSpace, kDaikin2BitMark, kDaikin2ZeroSpace, - kDaikin2BitMark, kDaikin2Gap, data + kDaikin2Section1Length, - nbytes - kDaikin2Section1Length, - kDaikin2Freq, false, 0, 50); - } -} -#endif // SEND_DAIKIN2 - -/// 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? -IRDaikin2::IRDaikin2(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 IRDaikin2::begin(void) { _irsend.begin(); } - -#if SEND_DAIKIN2 -/// Send the current internal state as an IR message. -/// @param[in] repeat Nr. of times the message will be repeated. -void IRDaikin2::send(const uint16_t repeat) { - _irsend.sendDaikin2(getRaw(), kDaikin2StateLength, repeat); -} -#endif // SEND_DAIKIN2 - -/// 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 IRDaikin2::validChecksum(uint8_t state[], const uint16_t length) { - // Validate the checksum of section #1. - if (length <= kDaikin2Section1Length - 1 || - state[kDaikin2Section1Length - 1] != sumBytes(state, - kDaikin2Section1Length - 1)) - return false; - // Validate the checksum of section #2 (a.k.a. the rest) - if (length <= kDaikin2Section1Length + 1 || - state[length - 1] != sumBytes(state + kDaikin2Section1Length, - length - kDaikin2Section1Length - 1)) - return false; - return true; -} - -/// Calculate and set the checksum values for the internal state. -void IRDaikin2::checksum(void) { - _.Sum1 = sumBytes(_.raw, kDaikin2Section1Length - 1); - _.Sum2 = sumBytes(_.raw + kDaikin2Section1Length, kDaikin2Section2Length - 1); -} - -/// Reset the internal state to a fixed known good state. -void IRDaikin2::stateReset(void) { - for (uint8_t i = 0; i < kDaikin2StateLength; i++) _.raw[i] = 0x0; - - _.raw[0] = 0x11; - _.raw[1] = 0xDA; - _.raw[2] = 0x27; - _.raw[4] = 0x01; - _.raw[6] = 0xC0; - _.raw[7] = 0x70; - _.raw[8] = 0x08; - _.raw[9] = 0x0C; - _.raw[10] = 0x80; - _.raw[11] = 0x04; - _.raw[12] = 0xB0; - _.raw[13] = 0x16; - _.raw[14] = 0x24; - _.raw[17] = 0xBE; - _.raw[18] = 0xD0; - // _.raw[19] is a checksum byte, it will be set by checksum(). - _.raw[20] = 0x11; - _.raw[21] = 0xDA; - _.raw[22] = 0x27; - _.raw[25] = 0x08; - _.raw[28] = 0xA0; - _.raw[35] = 0xC1; - _.raw[36] = 0x80; - _.raw[37] = 0x60; - // _.raw[38] is a checksum byte, it will be set by checksum(). - disableOnTimer(); - disableOffTimer(); - disableSleepTimer(); - checksum(); -} - -/// 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 *IRDaikin2::getRaw(void) { - checksum(); // Ensure correct settings before sending. - return _.raw; -} - -/// Set the internal state from a valid code for this protocol. -/// @param[in] new_code A valid code for this protocol. -void IRDaikin2::setRaw(const uint8_t new_code[]) { - std::memcpy(_.raw, new_code, kDaikin2StateLength); -} - -/// Change the power setting to On. -void IRDaikin2::on(void) { setPower(true); } - -/// Change the power setting to Off. -void IRDaikin2::off(void) { setPower(false); } - -/// Change the power setting. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRDaikin2::setPower(const bool on) { - _.Power = on; - _.Power2 = !on; -} - -/// Get the value of the current power setting. -/// @return true, the setting is on. false, the setting is off. -bool IRDaikin2::getPower(void) const { return _.Power && !_.Power2; } - -/// Get the operating mode setting of the A/C. -/// @return The current operating mode setting. -uint8_t IRDaikin2::getMode(void) const { return _.Mode; } - -/// Set the operating mode of the A/C. -/// @param[in] desired_mode The desired operating mode. -void IRDaikin2::setMode(const uint8_t desired_mode) { - uint8_t mode = desired_mode; - switch (mode) { - case kDaikinCool: - case kDaikinHeat: - case kDaikinFan: - case kDaikinDry: break; - default: mode = kDaikinAuto; - } - _.Mode = mode; - // Redo the temp setting as Cool mode has a different min temp. - if (mode == kDaikinCool) setTemp(getTemp()); - setHumidity(getHumidity()); // Make sure the humidity is okay for this mode. -} - -/// Set the temperature. -/// @param[in] desired The temperature in degrees celsius. -void IRDaikin2::setTemp(const uint8_t desired) { - // The A/C has a different min temp if in cool mode. - uint8_t temp = std::max( - (_.Mode == kDaikinCool) ? kDaikin2MinCoolTemp : kDaikinMinTemp, - desired); - _.Temp = std::min(kDaikinMaxTemp, temp); - // If the humidity setting is in use, the temp is a fixed value. - if (_.HumidOn) _.Temp = kDaikinMaxTemp; -} - -/// Get the current temperature setting. -/// @return The current setting for temp. in degrees celsius. -uint8_t IRDaikin2::getTemp(void) const { return _.Temp; } - -/// Set the speed of the fan. -/// @param[in] fan The desired setting. -/// @note 1-5 or kDaikinFanAuto or kDaikinFanQuiet -void IRDaikin2::setFan(const uint8_t fan) { - uint8_t fanset; - if (fan == kDaikinFanQuiet || fan == kDaikinFanAuto) - fanset = fan; - else if (fan < kDaikinFanMin || fan > kDaikinFanMax) - fanset = kDaikinFanAuto; - else - fanset = 2 + fan; - _.Fan = fanset; -} - -/// Get the current fan speed setting. -/// @return The current fan speed. -uint8_t IRDaikin2::getFan(void) const { - const uint8_t fan = _.Fan; - switch (fan) { - case kDaikinFanAuto: - case kDaikinFanQuiet: return fan; - default: return fan - 2; - } -} - -/// Set the Vertical Swing mode of the A/C. -/// @param[in] position The position/mode to set the swing to. -void IRDaikin2::setSwingVertical(const uint8_t position) { - switch (position) { - case kDaikin2SwingVHighest: - case kDaikin2SwingVHigh: - case kDaikin2SwingVUpperMiddle: - case kDaikin2SwingVLowerMiddle: - case kDaikin2SwingVLow: - case kDaikin2SwingVLowest: - case kDaikin2SwingVOff: - case kDaikin2SwingVBreeze: - case kDaikin2SwingVCirculate: - case kDaikin2SwingVAuto: - _.SwingV = position; - } -} - -/// Get the Vertical Swing mode of the A/C. -/// @return The native position/mode setting. -uint8_t IRDaikin2::getSwingVertical(void) const { return _.SwingV; } - -/// 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 IRDaikin2::convertSwingV(const stdAc::swingv_t position) { - switch (position) { - case stdAc::swingv_t::kHighest: - case stdAc::swingv_t::kHigh: - case stdAc::swingv_t::kMiddle: - case stdAc::swingv_t::kLow: - case stdAc::swingv_t::kLowest: - return (uint8_t)position + kDaikin2SwingVHighest; - case stdAc::swingv_t::kOff: - return kDaikin2SwingVOff; - default: - return kDaikin2SwingVAuto; - } -} - -/// Convert a native vertical swing postion to it's common equivalent. -/// @param[in] setting A native position to convert. -/// @return The common vertical swing position. -stdAc::swingv_t IRDaikin2::toCommonSwingV(const uint8_t setting) { - switch (setting) { - case kDaikin2SwingVHighest: return stdAc::swingv_t::kHighest; - case kDaikin2SwingVHigh: return stdAc::swingv_t::kHigh; - case kDaikin2SwingVUpperMiddle: - case kDaikin2SwingVLowerMiddle: return stdAc::swingv_t::kMiddle; - case kDaikin2SwingVLow: return stdAc::swingv_t::kLow; - case kDaikin2SwingVLowest: return stdAc::swingv_t::kLowest; - case kDaikin2SwingVOff: return stdAc::swingv_t::kOff; - default: return stdAc::swingv_t::kAuto; - } -} - -/// Set the Horizontal Swing mode of the A/C. -/// @param[in] position The position/mode to set the swing to. -void IRDaikin2::setSwingHorizontal(const uint8_t position) { - _.SwingH = position; -} - -/// Get the Horizontal Swing mode of the A/C. -/// @return The native position/mode setting. -uint8_t IRDaikin2::getSwingHorizontal(void) const { return _.SwingH; } - -/// Set the clock on the A/C unit. -/// @param[in] numMins Nr. of minutes past midnight. -void IRDaikin2::setCurrentTime(const uint16_t numMins) { - uint16_t mins = numMins; - if (numMins > 24 * 60) mins = 0; // If > 23:59, set to 00:00 - _.CurrentTime = mins; -} - -/// Get the clock time to be sent to the A/C unit. -/// @return The number of minutes past midnight. -uint16_t IRDaikin2::getCurrentTime(void) const { return _.CurrentTime; } - -/// Set the enable status & time of the On Timer. -/// @param[in] starttime The number of minutes past midnight. -/// @note Timer location is shared with sleep timer. -void IRDaikin2::enableOnTimer(const uint16_t starttime) { - clearSleepTimerFlag(); - _.OnTimer = true; - _.OnTime = starttime; -} - -/// Clear the On Timer flag. -void IRDaikin2::clearOnTimerFlag(void) { _.OnTimer = false; } - -/// Disable the On timer. -void IRDaikin2::disableOnTimer(void) { - _.OnTime = kDaikinUnusedTime; - clearOnTimerFlag(); - clearSleepTimerFlag(); -} - -/// Get the On Timer time to be sent to the A/C unit. -/// @return The number of minutes past midnight. -uint16_t IRDaikin2::getOnTime(void) const { return _.OnTime; } - -/// Get the enable status of the On Timer. -/// @return true, the setting is on. false, the setting is off. -bool IRDaikin2::getOnTimerEnabled(void) const { return _.OnTimer; } - -/// Set the enable status & time of the Off Timer. -/// @param[in] endtime The number of minutes past midnight. -void IRDaikin2::enableOffTimer(const uint16_t endtime) { - // Set the Off Timer flag. - _.OffTimer = true; - _.OffTime = endtime; -} - -/// Disable the Off timer. -void IRDaikin2::disableOffTimer(void) { - _.OffTime = kDaikinUnusedTime; - // Clear the Off Timer flag. - _.OffTimer = false; -} - -/// Get the Off Timer time to be sent to the A/C unit. -/// @return The number of minutes past midnight. -uint16_t IRDaikin2::getOffTime(void) const { return _.OffTime; } - -/// Get the enable status of the Off Timer. -/// @return true, the setting is on. false, the setting is off. -bool IRDaikin2::getOffTimerEnabled(void) const { return _.OffTimer; } - -/// Get the Beep status of the A/C. -/// @return true, the setting is on. false, the setting is off. -uint8_t IRDaikin2::getBeep(void) const { return _.Beep; } - -/// Set the Beep mode of the A/C. -/// @param[in] beep true, the setting is on. false, the setting is off. -void IRDaikin2::setBeep(const uint8_t beep) { _.Beep = beep; } - -/// Get the Light status of the A/C. -/// @return true, the setting is on. false, the setting is off. -uint8_t IRDaikin2::getLight(void) const { return _.Light; } - -/// Set the Light (LED) mode of the A/C. -/// @param[in] light true, the setting is on. false, the setting is off. -void IRDaikin2::setLight(const uint8_t light) { _.Light = light; } - -/// Set the Mould (filter) mode of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRDaikin2::setMold(const bool on) { _.Mold = on; } - -/// Get the Mould (filter) mode status of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRDaikin2::getMold(void) const { return _.Mold; } - -/// Set the Auto clean mode of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRDaikin2::setClean(const bool on) { _.Clean = on; } - -/// Get the Auto Clean mode status of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRDaikin2::getClean(void) const { return _.Clean; } - -/// Set the Fresh Air mode of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRDaikin2::setFreshAir(const bool on) { _.FreshAir = on; } - -/// Get the Fresh Air mode status of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRDaikin2::getFreshAir(void) const { return _.FreshAir; } - -/// Set the (High) Fresh Air mode of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRDaikin2::setFreshAirHigh(const bool on) { _.FreshAirHigh = on; } - -/// Get the (High) Fresh Air mode status of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRDaikin2::getFreshAirHigh(void) const { return _.FreshAirHigh; } - -/// Set the Automatic Eye (Sensor) mode of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRDaikin2::setEyeAuto(bool on) { _.EyeAuto = on; } - -/// Get the Automaitc Eye (Sensor) mode status of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRDaikin2::getEyeAuto(void) const { return _.EyeAuto; } - -/// Set the Eye (Sensor) mode of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRDaikin2::setEye(bool on) { _.Eye = on; } - -/// Get the Eye (Sensor) mode status of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRDaikin2::getEye(void) const { return _.Eye; } - -/// Set the Economy mode of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRDaikin2::setEcono(bool on) { _.Econo = on; } - -/// Get the Economical mode of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRDaikin2::getEcono(void) const { return _.Econo; } - -/// Set the enable status & time of the Sleep Timer. -/// @param[in] sleeptime The number of minutes past midnight. -/// @note The Timer location is shared with On Timer. -void IRDaikin2::enableSleepTimer(const uint16_t sleeptime) { - enableOnTimer(sleeptime); - clearOnTimerFlag(); - _.SleepTimer = true; -} - -/// Clear the sleep timer flag. -void IRDaikin2::clearSleepTimerFlag(void) { _.SleepTimer = false; } - -/// Disable the sleep timer. -void IRDaikin2::disableSleepTimer(void) { disableOnTimer(); } - -/// Get the Sleep Timer time to be sent to the A/C unit. -/// @return The number of minutes past midnight. -uint16_t IRDaikin2::getSleepTime(void) const { return getOnTime(); } - -/// Get the Sleep timer enabled status of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRDaikin2::getSleepTimerEnabled(void) const { return _.SleepTimer; } - -/// Set the Quiet mode of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRDaikin2::setQuiet(const bool on) { - _.Quiet = on; - // Powerful & Quiet mode being on are mutually exclusive. - if (on) setPowerful(false); -} - -/// Get the Quiet mode status of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRDaikin2::getQuiet(void) const { return _.Quiet; } - -/// Set the Powerful (Turbo) mode of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRDaikin2::setPowerful(const bool on) { - _.Powerful = on; - // Powerful & Quiet mode being on are mutually exclusive. - if (on) setQuiet(false); -} - -/// Get the Powerful (Turbo) mode of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRDaikin2::getPowerful(void) const { return _.Powerful; } - -/// Set the Purify (Filter) mode of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRDaikin2::setPurify(const bool on) { _.Purify = on; } - -/// Get the Purify (Filter) mode status of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRDaikin2::getPurify(void) const { return _.Purify; } - -/// Get the Humidity percentage setting of the A/C. -/// @return The setting percentage. 255 is Automatic. 0 is Off. -uint8_t IRDaikin2::getHumidity(void) const { return _.Humidity; } - -/// Set the Humidity percentage setting of the A/C. -/// @param[in] percent Percentage humidty. 255 is Auto. 0 is Off. -/// @note Only available in Dry & Heat modes, otherwise it is Off. -void IRDaikin2::setHumidity(const uint8_t percent) { - _.Humidity = kDaikin2HumidityOff; // Default to off. - switch (getMode()) { - case kDaikinHeat: - switch (percent) { - case kDaikin2HumidityOff: - case kDaikin2HumidityHeatLow: - case kDaikin2HumidityHeatMedium: - case kDaikin2HumidityHeatHigh: - case kDaikin2HumidityAuto: - _.Humidity = percent; - } - break; - case kDaikinDry: - switch (percent) { - case kDaikin2HumidityOff: - case kDaikin2HumidityDryLow: - case kDaikin2HumidityDryMedium: - case kDaikin2HumidityDryHigh: - case kDaikin2HumidityAuto: - _.Humidity = percent; - } - break; - } - _.HumidOn = (_.Humidity != kDaikin2HumidityOff); // Enabled? - setTemp(getTemp()); // Adjust the temperature if we need to. -} - -/// 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 IRDaikin2::convertMode(const stdAc::opmode_t mode) { - return IRDaikinESP::convertMode(mode); -} - -/// 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 IRDaikin2::convertFan(const stdAc::fanspeed_t speed) { - return IRDaikinESP::convertFan(speed); -} - -/// Convert a stdAc::swingh_t enum into it's native setting. -/// @param[in] position The enum to be converted. -/// @return The native equivalent of the enum. -uint8_t IRDaikin2::convertSwingH(const stdAc::swingh_t position) { - switch (position) { - case stdAc::swingh_t::kAuto: return kDaikin2SwingHSwing; - case stdAc::swingh_t::kLeftMax: return kDaikin2SwingHLeftMax; - case stdAc::swingh_t::kLeft: return kDaikin2SwingHLeft; - case stdAc::swingh_t::kMiddle: return kDaikin2SwingHMiddle; - case stdAc::swingh_t::kRight: return kDaikin2SwingHRight; - case stdAc::swingh_t::kRightMax: return kDaikin2SwingHRightMax; - case stdAc::swingh_t::kWide: return kDaikin2SwingHWide; - default: return kDaikin2SwingHAuto; - } -} - -/// Convert a native horizontal swing postion to it's common equivalent. -/// @param[in] setting A native position to convert. -/// @return The common horizontal swing position. -stdAc::swingh_t IRDaikin2::toCommonSwingH(const uint8_t setting) { - switch (setting) { - case kDaikin2SwingHSwing: return stdAc::swingh_t::kAuto; - case kDaikin2SwingHLeftMax: return stdAc::swingh_t::kLeftMax; - case kDaikin2SwingHLeft: return stdAc::swingh_t::kLeft; - case kDaikin2SwingHMiddle: return stdAc::swingh_t::kMiddle; - case kDaikin2SwingHRight: return stdAc::swingh_t::kRight; - case kDaikin2SwingHRightMax: return stdAc::swingh_t::kRightMax; - case kDaikin2SwingHWide: return stdAc::swingh_t::kWide; - 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 IRDaikin2::toCommon(void) const { - stdAc::state_t result; - result.protocol = decode_type_t::DAIKIN2; - result.model = -1; // No models used. - result.power = getPower(); - result.mode = IRDaikinESP::toCommonMode(_.Mode); - result.celsius = true; - result.degrees = _.Temp; - result.fanspeed = IRDaikinESP::toCommonFanSpeed(getFan()); - result.swingv = toCommonSwingV(_.SwingV); - result.swingh = toCommonSwingH(_.SwingH); - result.quiet = _.Quiet; - result.light = _.Light != 3; // 3 is Off, everything else is On. - result.turbo = _.Powerful; - result.clean = _.Mold; - result.econo = _.Econo; - result.filter = _.Purify; - result.beep = _.Beep != 3; // 3 is Off, everything else is On. - result.sleep = _.SleepTimer ? getSleepTime() : -1; - // Not supported. - result.clock = -1; - return result; -} - -/// Convert the current internal state into a human readable string. -/// @return A human readable string. -String IRDaikin2::toString(void) const { - String result = ""; - result.reserve(330); // Reserve some heap for the string to reduce fragging. - result += addBoolToString(getPower(), kPowerStr, false); - result += addModeToString(_.Mode, kDaikinAuto, kDaikinCool, kDaikinHeat, - kDaikinDry, kDaikinFan); - result += addTempToString(_.Temp); - result += addFanToString(getFan(), kDaikinFanMax, kDaikinFanMin, - kDaikinFanAuto, kDaikinFanQuiet, kDaikinFanMed); - result += addSwingVToString(_.SwingV, kDaikin2SwingVAuto, - kDaikin2SwingVHighest, kDaikin2SwingVHigh, - kDaikin2SwingVUpperMiddle, - kDaikin2SwingVAuto, // Middle is unused. - kDaikin2SwingVLowerMiddle, - kDaikin2SwingVLow, kDaikin2SwingVLowest, - kDaikin2SwingVOff, // Off is unused - kDaikin2SwingVSwing, kDaikin2SwingVBreeze, - kDaikin2SwingVCirculate); - result += addSwingHToString(_.SwingH, kDaikin2SwingHAuto, - kDaikin2SwingHLeftMax, - kDaikin2SwingHLeft, - kDaikin2SwingHMiddle, - kDaikin2SwingHRight, - kDaikin2SwingHRightMax, - kDaikin2SwingHOff, - kDaikin2SwingHAuto, // Unused - kDaikin2SwingHAuto, // Unused - kDaikin2SwingHAuto, // Unused - kDaikin2SwingHWide); - result += addLabeledString(minsToString(_.CurrentTime), kClockStr); - result += addLabeledString( - _.OnTimer ? minsToString(_.OnTime) : kOffStr, kOnTimerStr); - result += addLabeledString( - _.OffTimer ? minsToString(_.OffTime) : kOffStr, - kOffTimerStr); - result += addLabeledString( - _.SleepTimer ? minsToString(getSleepTime()) : kOffStr, - kSleepTimerStr); - result += addIntToString(_.Beep, kBeepStr); - result += kSpaceLBraceStr; - switch (_.Beep) { - case kDaikinBeepLoud: - result += kLoudStr; - break; - case kDaikinBeepQuiet: - result += kQuietStr; - break; - case kDaikinBeepOff: - result += kOffStr; - break; - default: - result += kUnknownStr; - } - result += ')'; - result += addIntToString(_.Light, kLightStr); - result += kSpaceLBraceStr; - switch (_.Light) { - case kDaikinLightBright: - result += kHighStr; - break; - case kDaikinLightDim: - result += kLowStr; - break; - case kDaikinLightOff: - result += kOffStr; - break; - default: - result += kUnknownStr; - } - result += ')'; - result += addBoolToString(_.Mold, kMouldStr); - result += addBoolToString(_.Clean, kCleanStr); - result += addLabeledString( - _.FreshAir ? (_.FreshAirHigh ? kHighStr : kOnStr) : kOffStr, - kFreshStr); - result += addBoolToString(_.Eye, kEyeStr); - result += addBoolToString(_.EyeAuto, kEyeAutoStr); - result += addBoolToString(_.Quiet, kQuietStr); - result += addBoolToString(_.Powerful, kPowerfulStr); - result += addBoolToString(_.Purify, kPurifyStr); - result += addBoolToString(_.Econo, kEconoStr); - result += addIntToString(_.Humidity, kHumidStr); - switch (_.Humidity) { - case kDaikin2HumidityOff: - case kDaikin2HumidityAuto: - result += kSpaceLBraceStr; - result += _.Humidity ? kAutoStr : kOffStr; - result += ')'; - break; - default: - result += '%'; - } - return result; -} - -#if DECODE_DAIKIN2 -/// Decode the supplied Daikin 312-bit message. (DAIKIN2) -/// Status: STABLE / Works as expected. -/// @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::decodeDaikin2(decode_results *results, uint16_t offset, - const uint16_t nbits, const bool strict) { - if (results->rawlen < 2 * (nbits + kHeader + kFooter) + kHeader - 1 + offset) - return false; - - // Compliance - if (strict && nbits != kDaikin2Bits) return false; - - const uint8_t ksectionSize[kDaikin2Sections] = {kDaikin2Section1Length, - kDaikin2Section2Length}; - - // Leader - if (!matchMark(results->rawbuf[offset++], kDaikin2LeaderMark, - _tolerance + kDaikin2Tolerance)) return false; - if (!matchSpace(results->rawbuf[offset++], kDaikin2LeaderSpace, - _tolerance + kDaikin2Tolerance)) return false; - - // Sections - uint16_t pos = 0; - for (uint8_t section = 0; section < kDaikin2Sections; section++) { - uint16_t used; - // Section Header + Section Data + Section Footer - used = matchGeneric(results->rawbuf + offset, results->state + pos, - results->rawlen - offset, ksectionSize[section] * 8, - kDaikin2HdrMark, kDaikin2HdrSpace, - kDaikin2BitMark, kDaikin2OneSpace, - kDaikin2BitMark, kDaikin2ZeroSpace, - kDaikin2BitMark, kDaikin2Gap, - section >= kDaikin2Sections - 1, - _tolerance + kDaikin2Tolerance, kDaikinMarkExcess, - false); - if (used == 0) return false; - offset += used; - pos += ksectionSize[section]; - } - // Compliance - if (strict) { - // Re-check we got the correct size/length due to the way we read the data. - if (pos * 8 != kDaikin2Bits) return false; - // Validate the checksum. - if (!IRDaikin2::validChecksum(results->state)) return false; - } - - // Success - results->decode_type = DAIKIN2; - 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_DAIKIN2 - -#if SEND_DAIKIN216 -/// Send a Daikin216 (216-bit) A/C formatted message. -/// Status: Alpha / Untested on a real device. -/// @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. -/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/689 -/// @see https://github.com/danny-source/Arduino_DY_IRDaikin -void IRsend::sendDaikin216(const unsigned char data[], const uint16_t nbytes, - const uint16_t repeat) { - if (nbytes < kDaikin216Section1Length) - return; // Not enough bytes to send a partial message. - - for (uint16_t r = 0; r <= repeat; r++) { - // Section #1 - sendGeneric(kDaikin216HdrMark, kDaikin216HdrSpace, kDaikin216BitMark, - kDaikin216OneSpace, kDaikin216BitMark, kDaikin216ZeroSpace, - kDaikin216BitMark, kDaikin216Gap, data, - kDaikin216Section1Length, - kDaikin216Freq, false, 0, kDutyDefault); - // Section #2 - sendGeneric(kDaikin216HdrMark, kDaikin216HdrSpace, kDaikin216BitMark, - kDaikin216OneSpace, kDaikin216BitMark, kDaikin216ZeroSpace, - kDaikin216BitMark, kDaikin216Gap, - data + kDaikin216Section1Length, - nbytes - kDaikin216Section1Length, - kDaikin216Freq, false, 0, kDutyDefault); - } -} -#endif // SEND_DAIKIN216 - -/// 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? -IRDaikin216::IRDaikin216(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 IRDaikin216::begin(void) { _irsend.begin(); } - -#if SEND_DAIKIN216 -/// Send the current internal state as an IR message. -/// @param[in] repeat Nr. of times the message will be repeated. -void IRDaikin216::send(const uint16_t repeat) { - _irsend.sendDaikin216(getRaw(), kDaikin216StateLength, repeat); -} -#endif // SEND_DAIKIN216 - -/// 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 IRDaikin216::validChecksum(uint8_t state[], const uint16_t length) { - // Validate the checksum of section #1. - if (length <= kDaikin216Section1Length - 1 || - state[kDaikin216Section1Length - 1] != sumBytes( - state, kDaikin216Section1Length - 1)) - return false; - // Validate the checksum of section #2 (a.k.a. the rest) - if (length <= kDaikin216Section1Length + 1 || - state[length - 1] != sumBytes(state + kDaikin216Section1Length, - length - kDaikin216Section1Length - 1)) - return false; - return true; -} - -/// Calculate and set the checksum values for the internal state. -void IRDaikin216::checksum(void) { - _.Sum1 = sumBytes(_.raw, kDaikin216Section1Length - 1); - _.Sum2 = sumBytes(_.raw + kDaikin216Section1Length, - kDaikin216Section2Length - 1); -} - -/// Reset the internal state to a fixed known good state. -void IRDaikin216::stateReset(void) { - for (uint8_t i = 0; i < kDaikin216StateLength; i++) _.raw[i] = 0x00; - _.raw[0] = 0x11; - _.raw[1] = 0xDA; - _.raw[2] = 0x27; - _.raw[3] = 0xF0; - // _.raw[7] is a checksum byte, it will be set by checksum(). - _.raw[8] = 0x11; - _.raw[9] = 0xDA; - _.raw[10] = 0x27; - _.raw[23] = 0xC0; - // _.raw[26] is a checksum byte, it will be set by checksum(). -} - -/// 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 *IRDaikin216::getRaw(void) { - checksum(); // Ensure correct settings before sending. - return _.raw; -} - -/// Set the internal state from a valid code for this protocol. -/// @param[in] new_code A valid code for this protocol. -void IRDaikin216::setRaw(const uint8_t new_code[]) { - std::memcpy(_.raw, new_code, kDaikin216StateLength); -} - -/// Change the power setting to On. -void IRDaikin216::on(void) { setPower(true); } - -/// Change the power setting to Off. -void IRDaikin216::off(void) { setPower(false); } - -/// Change the power setting. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRDaikin216::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 IRDaikin216::getPower(void) const { return _.Power; } - -/// Get the operating mode setting of the A/C. -/// @return The current operating mode setting. -uint8_t IRDaikin216::getMode(void) const { return _.Mode; } - -/// Set the operating mode of the A/C. -/// @param[in] mode The desired operating mode. -void IRDaikin216::setMode(const uint8_t mode) { - switch (mode) { - case kDaikinAuto: - case kDaikinCool: - case kDaikinHeat: - case kDaikinFan: - case kDaikinDry: - _.Mode = mode; - break; - default: - _.Mode = kDaikinAuto; - } -} - -/// 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 IRDaikin216::convertMode(const stdAc::opmode_t mode) { - return IRDaikinESP::convertMode(mode); -} - -/// Set the temperature. -/// @param[in] temp The temperature in degrees celsius. -void IRDaikin216::setTemp(const uint8_t temp) { - uint8_t degrees = std::max(temp, kDaikinMinTemp); - degrees = std::min(degrees, kDaikinMaxTemp); - _.Temp = degrees; -} - -/// Get the current temperature setting. -/// @return The current setting for temp. in degrees celsius. -uint8_t IRDaikin216::getTemp(void) const { return _.Temp; } - -/// Set the speed of the fan. -/// @param[in] fan The desired setting. -/// @note 1-5 or kDaikinFanAuto or kDaikinFanQuiet -void IRDaikin216::setFan(const uint8_t fan) { - // Set the fan speed bits, leave low 4 bits alone - uint8_t fanset; - if (fan == kDaikinFanQuiet || fan == kDaikinFanAuto) - fanset = fan; - else if (fan < kDaikinFanMin || fan > kDaikinFanMax) - fanset = kDaikinFanAuto; - else - fanset = 2 + fan; - _.Fan = fanset; -} - -/// Get the current fan speed setting. -/// @return The current fan speed. -uint8_t IRDaikin216::getFan(void) const { - uint8_t fan = _.Fan; - if (fan != kDaikinFanQuiet && fan != kDaikinFanAuto) fan -= 2; - 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 IRDaikin216::convertFan(const stdAc::fanspeed_t speed) { - return IRDaikinESP::convertFan(speed); -} - -/// Set the Vertical Swing mode of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRDaikin216::setSwingVertical(const bool on) { - _.SwingV = (on ? kDaikin216SwingOn : kDaikin216SwingOff); -} - -/// Get the Vertical Swing mode of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRDaikin216::getSwingVertical(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 IRDaikin216::setSwingHorizontal(const bool on) { - _.SwingH = (on ? kDaikin216SwingOn : kDaikin216SwingOff); -} - -/// Get the Horizontal Swing mode of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRDaikin216::getSwingHorizontal(void) const { return _.SwingH; } - -/// Set the Quiet mode of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -/// @note This is a horrible hack till someone works out the quiet mode bit. -void IRDaikin216::setQuiet(const bool on) { - if (on) { - setFan(kDaikinFanQuiet); - // Powerful & Quiet mode being on are mutually exclusive. - setPowerful(false); - } else if (getFan() == kDaikinFanQuiet) { - setFan(kDaikinFanAuto); - } -} - -/// Get the Quiet mode status of the A/C. -/// @return true, the setting is on. false, the setting is off. -/// @note This is a horrible hack till someone works out the quiet mode bit. -bool IRDaikin216::getQuiet(void) const { return getFan() == kDaikinFanQuiet; } - -/// Set the Powerful (Turbo) mode of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRDaikin216::setPowerful(const bool on) { - _.Powerful = on; - // Powerful & Quiet mode being on are mutually exclusive. - if (on) setQuiet(false); -} - -/// Get the Powerful (Turbo) mode of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRDaikin216::getPowerful(void) const { return _.Powerful; } - -/// Convert the current internal state into its stdAc::state_t equivalent. -/// @return The stdAc equivalent of the native settings. -stdAc::state_t IRDaikin216::toCommon(void) const { - stdAc::state_t result; - result.protocol = decode_type_t::DAIKIN216; - result.model = -1; // No models used. - result.power = _.Power; - result.mode = IRDaikinESP::toCommonMode(_.Mode); - result.celsius = true; - result.degrees = _.Temp; - result.fanspeed = IRDaikinESP::toCommonFanSpeed(getFan()); - result.swingv = _.SwingV ? stdAc::swingv_t::kAuto : - stdAc::swingv_t::kOff; - result.swingh = _.SwingH ? stdAc::swingh_t::kAuto : - stdAc::swingh_t::kOff; - result.quiet = getQuiet(); - result.turbo = _.Powerful; - // Not supported. - result.light = false; - result.clean = 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 IRDaikin216::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, kDaikinAuto, kDaikinCool, kDaikinHeat, - kDaikinDry, kDaikinFan); - result += addTempToString(_.Temp); - result += addFanToString(getFan(), kDaikinFanMax, kDaikinFanMin, - kDaikinFanAuto, kDaikinFanQuiet, kDaikinFanMed); - result += addBoolToString(_.SwingH, kSwingHStr); - result += addBoolToString(_.SwingV, kSwingVStr); - result += addBoolToString(getQuiet(), kQuietStr); - result += addBoolToString(_.Powerful, kPowerfulStr); - return result; -} - -#if DECODE_DAIKIN216 -/// Decode the supplied Daikin 216-bit message. (DAIKIN216) -/// Status: STABLE / Should 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/689 -/// @see https://github.com/danny-source/Arduino_DY_IRDaikin -bool IRrecv::decodeDaikin216(decode_results *results, uint16_t offset, - const uint16_t nbits, const bool strict) { - if (results->rawlen < 2 * (nbits + kHeader + kFooter) - 1 + offset) - return false; - - // Compliance - if (strict && nbits != kDaikin216Bits) return false; - - const uint8_t ksectionSize[kDaikin216Sections] = {kDaikin216Section1Length, - kDaikin216Section2Length}; - // Sections - uint16_t pos = 0; - for (uint8_t section = 0; section < kDaikin216Sections; section++) { - uint16_t used; - // Section Header + Section Data + Section Footer - used = matchGeneric(results->rawbuf + offset, results->state + pos, - results->rawlen - offset, ksectionSize[section] * 8, - kDaikin216HdrMark, kDaikin216HdrSpace, - kDaikin216BitMark, kDaikin216OneSpace, - kDaikin216BitMark, kDaikin216ZeroSpace, - kDaikin216BitMark, kDaikin216Gap, - section >= kDaikin216Sections - 1, - kDaikinTolerance, kDaikinMarkExcess, false); - if (used == 0) return false; - offset += used; - pos += ksectionSize[section]; - } - // Compliance - if (strict) { - if (pos * 8 != kDaikin216Bits) return false; - // Validate the checksum. - if (!IRDaikin216::validChecksum(results->state)) return false; - } - - // Success - results->decode_type = decode_type_t::DAIKIN216; - 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_DAIKIN216 - -#if SEND_DAIKIN160 -/// Send a Daikin160 (160-bit) A/C formatted message. -/// Status: STABLE / Confirmed 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. -/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/731 -void IRsend::sendDaikin160(const unsigned char data[], const uint16_t nbytes, - const uint16_t repeat) { - if (nbytes < kDaikin160Section1Length) - return; // Not enough bytes to send a partial message. - - for (uint16_t r = 0; r <= repeat; r++) { - // Section #1 - sendGeneric(kDaikin160HdrMark, kDaikin160HdrSpace, kDaikin160BitMark, - kDaikin160OneSpace, kDaikin160BitMark, kDaikin160ZeroSpace, - kDaikin160BitMark, kDaikin160Gap, data, - kDaikin160Section1Length, - kDaikin160Freq, false, 0, kDutyDefault); - // Section #2 - sendGeneric(kDaikin160HdrMark, kDaikin160HdrSpace, kDaikin160BitMark, - kDaikin160OneSpace, kDaikin160BitMark, kDaikin160ZeroSpace, - kDaikin160BitMark, kDaikin160Gap, - data + kDaikin160Section1Length, - nbytes - kDaikin160Section1Length, - kDaikin160Freq, false, 0, kDutyDefault); - } -} -#endif // SEND_DAIKIN160 - -/// 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? -IRDaikin160::IRDaikin160(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 IRDaikin160::begin(void) { _irsend.begin(); } - -/// 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 IRDaikin160::validChecksum(uint8_t state[], const uint16_t length) { - // Validate the checksum of section #1. - if (length <= kDaikin160Section1Length - 1 || - state[kDaikin160Section1Length - 1] != sumBytes( - state, kDaikin160Section1Length - 1)) - return false; - // Validate the checksum of section #2 (a.k.a. the rest) - if (length <= kDaikin160Section1Length + 1 || - state[length - 1] != sumBytes(state + kDaikin160Section1Length, - length - kDaikin160Section1Length - 1)) - return false; - return true; -} - -/// Calculate and set the checksum values for the internal state. -void IRDaikin160::checksum(void) { - _.Sum1 = sumBytes(_.raw, kDaikin160Section1Length - 1); - _.Sum2 = sumBytes(_.raw + kDaikin160Section1Length, - kDaikin160Section2Length - 1); -} - -/// Reset the internal state to a fixed known good state. -void IRDaikin160::stateReset(void) { - for (uint8_t i = 0; i < kDaikin160StateLength; i++) _.raw[i] = 0x00; - _.raw[0] = 0x11; - _.raw[1] = 0xDA; - _.raw[2] = 0x27; - _.raw[3] = 0xF0; - _.raw[4] = 0x0D; - // _.raw[6] is a checksum byte, it will be set by checksum(). - _.raw[7] = 0x11; - _.raw[8] = 0xDA; - _.raw[9] = 0x27; - _.raw[11] = 0xD3; - _.raw[12] = 0x30; - _.raw[13] = 0x11; - _.raw[16] = 0x1E; - _.raw[17] = 0x0A; - _.raw[18] = 0x08; - // _.raw[19] is a checksum byte, it will be set by checksum(). -} - -/// 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 *IRDaikin160::getRaw(void) { - checksum(); // Ensure correct settings before sending. - return _.raw; -} - -/// Set the internal state from a valid code for this protocol. -/// @param[in] new_code A valid code for this protocol. -void IRDaikin160::setRaw(const uint8_t new_code[]) { - std::memcpy(_.raw, new_code, kDaikin160StateLength); -} - -#if SEND_DAIKIN160 -/// Send the current internal state as an IR message. -/// @param[in] repeat Nr. of times the message will be repeated. -void IRDaikin160::send(const uint16_t repeat) { - _irsend.sendDaikin160(getRaw(), kDaikin160StateLength, repeat); -} -#endif // SEND_DAIKIN160 - -/// Change the power setting to On. -void IRDaikin160::on(void) { setPower(true); } - -/// Change the power setting to Off. -void IRDaikin160::off(void) { setPower(false); } - -/// Change the power setting. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRDaikin160::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 IRDaikin160::getPower(void) const { return _.Power; } - -/// Get the operating mode setting of the A/C. -/// @return The current operating mode setting. -uint8_t IRDaikin160::getMode(void) const { return _.Mode; } - -/// Set the operating mode of the A/C. -/// @param[in] mode The desired operating mode. -void IRDaikin160::setMode(const uint8_t mode) { - switch (mode) { - case kDaikinAuto: - case kDaikinCool: - case kDaikinHeat: - case kDaikinFan: - case kDaikinDry: - _.Mode = mode; - break; - default: _.Mode = kDaikinAuto; - } -} - -/// 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 IRDaikin160::convertMode(const stdAc::opmode_t mode) { - return IRDaikinESP::convertMode(mode); -} - -/// Set the temperature. -/// @param[in] temp The temperature in degrees celsius. -void IRDaikin160::setTemp(const uint8_t temp) { - uint8_t degrees = std::max(temp, kDaikinMinTemp); - degrees = std::min(degrees, kDaikinMaxTemp) - 10; - _.Temp = degrees; -} - -/// Get the current temperature setting. -/// @return The current setting for temp. in degrees celsius. -uint8_t IRDaikin160::getTemp(void) const { return _.Temp + 10; } - -/// Set the speed of the fan. -/// @param[in] fan The desired setting. -/// @note 1-5 or kDaikinFanAuto or kDaikinFanQuiet -void IRDaikin160::setFan(const uint8_t fan) { - uint8_t fanset; - if (fan == kDaikinFanQuiet || fan == kDaikinFanAuto) - fanset = fan; - else if (fan < kDaikinFanMin || fan > kDaikinFanMax) - fanset = kDaikinFanAuto; - else - fanset = 2 + fan; - _.Fan = fanset; -} - -/// Get the current fan speed setting. -/// @return The current fan speed. -uint8_t IRDaikin160::getFan(void) const { - uint8_t fan = _.Fan; - if (fan != kDaikinFanQuiet && fan != kDaikinFanAuto) fan -= 2; - 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 IRDaikin160::convertFan(const stdAc::fanspeed_t speed) { - switch (speed) { - case stdAc::fanspeed_t::kMin: return kDaikinFanMin; - case stdAc::fanspeed_t::kLow: return kDaikinFanMin + 1; - case stdAc::fanspeed_t::kMedium: return kDaikinFanMin + 2; - case stdAc::fanspeed_t::kHigh: return kDaikinFanMax - 1; - case stdAc::fanspeed_t::kMax: return kDaikinFanMax; - default: - return kDaikinFanAuto; - } -} - -/// Set the Vertical Swing mode of the A/C. -/// @param[in] position The position/mode to set the swing to. -void IRDaikin160::setSwingVertical(const uint8_t position) { - switch (position) { - case kDaikin160SwingVLowest: - case kDaikin160SwingVLow: - case kDaikin160SwingVMiddle: - case kDaikin160SwingVHigh: - case kDaikin160SwingVHighest: - case kDaikin160SwingVAuto: - _.SwingV = position; - break; - default: _.SwingV = kDaikin160SwingVAuto; - } -} - -/// Get the Vertical Swing mode of the A/C. -/// @return The native position/mode setting. -uint8_t IRDaikin160::getSwingVertical(void) const { return _.SwingV; } - -/// 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 IRDaikin160::convertSwingV(const stdAc::swingv_t position) { - switch (position) { - case stdAc::swingv_t::kHighest: - case stdAc::swingv_t::kHigh: - case stdAc::swingv_t::kMiddle: - case stdAc::swingv_t::kLow: - case stdAc::swingv_t::kLowest: - return kDaikin160SwingVHighest + 1 - (uint8_t)position; - default: - return kDaikin160SwingVAuto; - } -} - -/// Convert a native vertical swing postion to it's common equivalent. -/// @param[in] setting A native position to convert. -/// @return The common vertical swing position. -stdAc::swingv_t IRDaikin160::toCommonSwingV(const uint8_t setting) { - switch (setting) { - case kDaikin160SwingVHighest: return stdAc::swingv_t::kHighest; - case kDaikin160SwingVHigh: return stdAc::swingv_t::kHigh; - case kDaikin160SwingVMiddle: return stdAc::swingv_t::kMiddle; - case kDaikin160SwingVLow: return stdAc::swingv_t::kLow; - case kDaikin160SwingVLowest: return stdAc::swingv_t::kLowest; - default: - return stdAc::swingv_t::kAuto; - } -} - -/// Convert the current internal state into its stdAc::state_t equivalent. -/// @return The stdAc equivalent of the native settings. -stdAc::state_t IRDaikin160::toCommon(void) const { - stdAc::state_t result; - result.protocol = decode_type_t::DAIKIN160; - result.model = -1; // No models used. - result.power = _.Power; - result.mode = IRDaikinESP::toCommonMode(_.Mode); - result.celsius = true; - result.degrees = getTemp(); - result.fanspeed = IRDaikinESP::toCommonFanSpeed(getFan()); - result.swingv = toCommonSwingV(_.SwingV); - // Not supported. - result.swingh = stdAc::swingh_t::kOff; - result.quiet = false; - result.turbo = false; - result.light = false; - result.clean = 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 IRDaikin160::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, kDaikinAuto, kDaikinCool, kDaikinHeat, - kDaikinDry, kDaikinFan); - result += addTempToString(getTemp()); - result += addFanToString(getFan(), kDaikinFanMax, kDaikinFanMin, - kDaikinFanAuto, kDaikinFanQuiet, kDaikinFanMed); - result += addIntToString(_.SwingV, kSwingVStr); - result += kSpaceLBraceStr; - switch (_.SwingV) { - case kDaikin160SwingVHighest: result += kHighestStr; break; - case kDaikin160SwingVHigh: result += kHighStr; break; - case kDaikin160SwingVMiddle: result += kMiddleStr; break; - case kDaikin160SwingVLow: result += kLowStr; break; - case kDaikin160SwingVLowest: result += kLowestStr; break; - case kDaikin160SwingVAuto: result += kAutoStr; break; - default: result += kUnknownStr; - } - result += ')'; - return result; -} - -#if DECODE_DAIKIN160 -/// Decode the supplied Daikin 160-bit message. (DAIKIN160) -/// 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/731 -bool IRrecv::decodeDaikin160(decode_results *results, uint16_t offset, - const uint16_t nbits, const bool strict) { - if (results->rawlen < 2 * (nbits + kHeader + kFooter) - 1 + offset) - return false; - - // Compliance - if (strict && nbits != kDaikin160Bits) return false; - - const uint8_t ksectionSize[kDaikin160Sections] = {kDaikin160Section1Length, - kDaikin160Section2Length}; - - // Sections - uint16_t pos = 0; - for (uint8_t section = 0; section < kDaikin160Sections; section++) { - uint16_t used; - // Section Header + Section Data (7 bytes) + Section Footer - used = matchGeneric(results->rawbuf + offset, results->state + pos, - results->rawlen - offset, ksectionSize[section] * 8, - kDaikin160HdrMark, kDaikin160HdrSpace, - kDaikin160BitMark, kDaikin160OneSpace, - kDaikin160BitMark, kDaikin160ZeroSpace, - kDaikin160BitMark, kDaikin160Gap, - section >= kDaikin160Sections - 1, - kDaikinTolerance, kDaikinMarkExcess, false); - if (used == 0) return false; - offset += used; - pos += ksectionSize[section]; - } - // Compliance - if (strict) { - // Validate the checksum. - if (!IRDaikin160::validChecksum(results->state)) return false; - } - - // Success - results->decode_type = decode_type_t::DAIKIN160; - 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_DAIKIN160 - -#if SEND_DAIKIN176 -/// Send a Daikin176 (176-bit) A/C formatted message. -/// Status: STABLE / Working on a real device. -/// @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::sendDaikin176(const unsigned char data[], const uint16_t nbytes, - const uint16_t repeat) { - if (nbytes < kDaikin176Section1Length) - return; // Not enough bytes to send a partial message. - - for (uint16_t r = 0; r <= repeat; r++) { - // Section #1 - sendGeneric(kDaikin176HdrMark, kDaikin176HdrSpace, kDaikin176BitMark, - kDaikin176OneSpace, kDaikin176BitMark, kDaikin176ZeroSpace, - kDaikin176BitMark, kDaikin176Gap, data, - kDaikin176Section1Length, - kDaikin176Freq, false, 0, kDutyDefault); - // Section #2 - sendGeneric(kDaikin176HdrMark, kDaikin176HdrSpace, kDaikin176BitMark, - kDaikin176OneSpace, kDaikin176BitMark, kDaikin176ZeroSpace, - kDaikin176BitMark, kDaikin176Gap, - data + kDaikin176Section1Length, - nbytes - kDaikin176Section1Length, - kDaikin176Freq, false, 0, kDutyDefault); - } -} -#endif // SEND_DAIKIN176 - -/// 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? -IRDaikin176::IRDaikin176(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 IRDaikin176::begin(void) { _irsend.begin(); } - -/// 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 IRDaikin176::validChecksum(uint8_t state[], const uint16_t length) { - // Validate the checksum of section #1. - if (length <= kDaikin176Section1Length - 1 || - state[kDaikin176Section1Length - 1] != sumBytes( - state, kDaikin176Section1Length - 1)) - return false; - // Validate the checksum of section #2 (a.k.a. the rest) - if (length <= kDaikin176Section1Length + 1 || - state[length - 1] != sumBytes(state + kDaikin176Section1Length, - length - kDaikin176Section1Length - 1)) - return false; - return true; -} - -/// Calculate and set the checksum values for the internal state. -void IRDaikin176::checksum(void) { - _.Sum1 = sumBytes(_.raw, kDaikin176Section1Length - 1); - _.Sum2 = sumBytes(_.raw + kDaikin176Section1Length, - kDaikin176Section2Length - 1); -} - -/// Reset the internal state to a fixed known good state. -void IRDaikin176::stateReset(void) { - for (uint8_t i = 0; i < kDaikin176StateLength; i++) _.raw[i] = 0x00; - _.raw[0] = 0x11; - _.raw[1] = 0xDA; - _.raw[2] = 0x17; - _.raw[3] = 0x18; - _.raw[4] = 0x04; - // _.raw[6] is a checksum byte, it will be set by checksum(). - _.raw[7] = 0x11; - _.raw[8] = 0xDA; - _.raw[9] = 0x17; - _.raw[10] = 0x18; - _.raw[12] = 0x73; - _.raw[14] = 0x20; - _.raw[18] = 0x16; // Fan speed and swing - _.raw[20] = 0x20; - // _.raw[21] is a checksum byte, it will be set by checksum(). - _saved_temp = getTemp(); -} - -/// 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 *IRDaikin176::getRaw(void) { - checksum(); // Ensure correct settings before sending. - return _.raw; -} - -/// Set the internal state from a valid code for this protocol. -/// @param[in] new_code A valid code for this protocol. -void IRDaikin176::setRaw(const uint8_t new_code[]) { - std::memcpy(_.raw, new_code, kDaikin176StateLength); - _saved_temp = getTemp(); -} - -#if SEND_DAIKIN176 -/// Send the current internal state as an IR message. -/// @param[in] repeat Nr. of times the message will be repeated. -void IRDaikin176::send(const uint16_t repeat) { - _irsend.sendDaikin176(getRaw(), kDaikin176StateLength, repeat); -} -#endif // SEND_DAIKIN176 - -/// Change the power setting to On. -void IRDaikin176::on(void) { setPower(true); } - -/// Change the power setting to Off.. -void IRDaikin176::off(void) { setPower(false); } - -/// Change the power setting. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRDaikin176::setPower(const bool on) { - _.ModeButton = 0; - _.Power = on; -} - -/// Get the value of the current power setting. -/// @return true, the setting is on. false, the setting is off. -bool IRDaikin176::getPower(void) const { return _.Power; } - -/// Get the operating mode setting of the A/C. -/// @return The current operating mode setting. -uint8_t IRDaikin176::getMode(void) const { return _.Mode; } - -/// Set the operating mode of the A/C. -/// @param[in] mode The desired operating mode. -void IRDaikin176::setMode(const uint8_t mode) { - uint8_t altmode = 0; - // Set the mode bits. - _.Mode = mode; - // Daikin172 has some alternate/additional mode bits that need to be changed - // in line with the operating mode. The following few lines match up these - // bits with the corresponding operating bits. - switch (mode) { - case kDaikin176Dry: altmode = 2; break; - case kDaikin176Fan: altmode = 6; break; - case kDaikin176Auto: - case kDaikin176Cool: - case kDaikin176Heat: altmode = 7; break; - default: _.Mode = kDaikin176Cool; altmode = 7; break; - } - // Set the additional mode bits. - _.AltMode = altmode; - setTemp(_saved_temp); - // Needs to happen after setTemp() as it will clear it. - _.ModeButton = kDaikin176ModeButton; -} - -/// 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 IRDaikin176::convertMode(const stdAc::opmode_t mode) { - switch (mode) { - case stdAc::opmode_t::kDry: return kDaikin176Dry; - case stdAc::opmode_t::kHeat: return kDaikin176Heat; - case stdAc::opmode_t::kFan: return kDaikin176Fan; - case stdAc::opmode_t::kAuto: return kDaikin176Auto; - default: return kDaikin176Cool; - } -} - -/// 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 IRDaikin176::toCommonMode(const uint8_t mode) { - switch (mode) { - case kDaikin176Dry: return stdAc::opmode_t::kDry; - case kDaikin176Heat: return stdAc::opmode_t::kHeat; - case kDaikin176Fan: return stdAc::opmode_t::kFan; - case kDaikin176Auto: return stdAc::opmode_t::kAuto; - default: return stdAc::opmode_t::kCool; - } -} - -/// Set the temperature. -/// @param[in] temp The temperature in degrees celsius. -void IRDaikin176::setTemp(const uint8_t temp) { - uint8_t degrees = std::min(kDaikinMaxTemp, std::max(temp, kDaikinMinTemp)); - _saved_temp = degrees; - switch (_.Mode) { - case kDaikin176Dry: - case kDaikin176Fan: - degrees = kDaikin176DryFanTemp; break; - } - _.Temp = degrees - 9; - _.ModeButton = 0; -} - -/// Get the current temperature setting. -/// @return The current setting for temp. in degrees celsius. -uint8_t IRDaikin176::getTemp(void) const { return _.Temp + 9; } - -/// Set the speed of the fan. -/// @param[in] fan The desired setting. -/// @note 1 for Min or 3 for Max -void IRDaikin176::setFan(const uint8_t fan) { - switch (fan) { - case kDaikinFanMin: - case kDaikin176FanMax: - _.Fan = fan; - break; - default: - _.Fan = kDaikin176FanMax; - break; - } - _.ModeButton = 0; -} - -/// Get the current fan speed setting. -/// @return The current fan speed. -uint8_t IRDaikin176::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 IRDaikin176::convertFan(const stdAc::fanspeed_t speed) { - switch (speed) { - case stdAc::fanspeed_t::kMin: - case stdAc::fanspeed_t::kLow: return kDaikinFanMin; - default: return kDaikin176FanMax; - } -} - -/// Set the Horizontal Swing mode of the A/C. -/// @param[in] position The position/mode to set the swing to. -void IRDaikin176::setSwingHorizontal(const uint8_t position) { - switch (position) { - case kDaikin176SwingHOff: - case kDaikin176SwingHAuto: - _.SwingH = position; - break; - default: _.SwingH = kDaikin176SwingHAuto; - } -} - -/// Get the Horizontal Swing mode of the A/C. -/// @return The native position/mode setting. -uint8_t IRDaikin176::getSwingHorizontal(void) const { return _.SwingH; } - -/// Get the Unit Id of the A/C. -/// @return The Unit Id the A/C is to use. -uint8_t IRDaikin176::getId(void) const { return _.Id1; } - -/// Set the Unit Id of the A/C. -/// @param[in] num The Unit Id the A/C is to use. -/// @note 0 for Unit A; 1 for Unit B -void IRDaikin176::setId(const uint8_t num) { _.Id1 = _.Id2 = num; } - -/// Convert a stdAc::swingh_t enum into it's native setting. -/// @param[in] position The enum to be converted. -/// @return The native equivalent of the enum. -uint8_t IRDaikin176::convertSwingH(const stdAc::swingh_t position) { - switch (position) { - case stdAc::swingh_t::kOff: return kDaikin176SwingHOff; - case stdAc::swingh_t::kAuto: return kDaikin176SwingHAuto; - default: return kDaikin176SwingHAuto; - } -} - -/// Convert a native horizontal swing postion to it's common equivalent. -/// @param[in] setting A native position to convert. -/// @return The common horizontal swing position. -stdAc::swingh_t IRDaikin176::toCommonSwingH(const uint8_t setting) { - switch (setting) { - case kDaikin176SwingHOff: return stdAc::swingh_t::kOff; - case kDaikin176SwingHAuto: return stdAc::swingh_t::kAuto; - default: - return stdAc::swingh_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 IRDaikin176::toCommonFanSpeed(const uint8_t speed) { - return (speed == kDaikinFanMin) ? stdAc::fanspeed_t::kMin - : stdAc::fanspeed_t::kMax; -} - -/// Convert the current internal state into its stdAc::state_t equivalent. -/// @return The stdAc equivalent of the native settings. -stdAc::state_t IRDaikin176::toCommon(void) const { - stdAc::state_t result; - result.protocol = decode_type_t::DAIKIN176; - result.model = -1; // No models used. - result.power = _.Power; - result.mode = IRDaikin176::toCommonMode(_.Mode); - result.celsius = true; - result.degrees = getTemp(); - result.fanspeed = toCommonFanSpeed(_.Fan); - result.swingh = toCommonSwingH(_.SwingH); - - // Not supported. - result.swingv = stdAc::swingv_t::kOff; - result.quiet = false; - result.turbo = false; - result.light = false; - result.clean = 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 IRDaikin176::toString(void) const { - String result = ""; - result.reserve(90); // Reserve some heap for the string to reduce fragging. - result += addBoolToString(_.Power, kPowerStr, false); - result += addModeToString(_.Mode, kDaikin176Auto, kDaikin176Cool, - kDaikin176Heat, kDaikin176Dry, kDaikin176Fan); - result += addTempToString(getTemp()); - result += addFanToString(_.Fan, kDaikin176FanMax, kDaikinFanMin, - kDaikinFanMin, kDaikinFanMin, kDaikinFanMin); - result += addSwingHToString(_.SwingH, kDaikin176SwingHAuto, - kDaikin176SwingHAuto, // maxleft Unused - kDaikin176SwingHAuto, // left Unused - kDaikin176SwingHAuto, // middle Unused - kDaikin176SwingHAuto, // right Unused - kDaikin176SwingHAuto, // maxright Unused - kDaikin176SwingHOff, - // Below are unused. - kDaikin176SwingHAuto, - kDaikin176SwingHAuto, - kDaikin176SwingHAuto, - kDaikin176SwingHAuto); - result += addIntToString(_.Id1, kIdStr); - return result; -} - -#if DECODE_DAIKIN176 -/// Decode the supplied Daikin 176-bit message. (DAIKIN176) -/// 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::decodeDaikin176(decode_results *results, uint16_t offset, - const uint16_t nbits, - const bool strict) { - if (results->rawlen < 2 * (nbits + kHeader + kFooter) - 1 + offset) - return false; - - // Compliance - if (strict && nbits != kDaikin176Bits) return false; - - const uint8_t ksectionSize[kDaikin176Sections] = {kDaikin176Section1Length, - kDaikin176Section2Length}; - - // Sections - uint16_t pos = 0; - for (uint8_t section = 0; section < kDaikin176Sections; section++) { - uint16_t used; - // Section Header + Section Data (7 bytes) + Section Footer - used = matchGeneric(results->rawbuf + offset, results->state + pos, - results->rawlen - offset, ksectionSize[section] * 8, - kDaikin176HdrMark, kDaikin176HdrSpace, - kDaikin176BitMark, kDaikin176OneSpace, - kDaikin176BitMark, kDaikin176ZeroSpace, - kDaikin176BitMark, kDaikin176Gap, - section >= kDaikin176Sections - 1, - kDaikinTolerance, kDaikinMarkExcess, false); - if (used == 0) return false; - offset += used; - pos += ksectionSize[section]; - } - // Compliance - if (strict) { - // Validate the checksum. - if (!IRDaikin176::validChecksum(results->state)) return false; - } - - // Success - results->decode_type = decode_type_t::DAIKIN176; - 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_DAIKIN176 - -#if SEND_DAIKIN128 -/// Send a Daikin128 (128-bit) A/C formatted 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. -/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/827 -void IRsend::sendDaikin128(const unsigned char data[], const uint16_t nbytes, - const uint16_t repeat) { - if (nbytes < kDaikin128SectionLength) - return; // Not enough bytes to send a partial message. - - for (uint16_t r = 0; r <= repeat; r++) { - enableIROut(kDaikin128Freq); - // Leader - for (uint8_t i = 0; i < 2; i++) { - mark(kDaikin128LeaderMark); - space(kDaikin128LeaderSpace); - } - // Section #1 (Header + Data) - sendGeneric(kDaikin128HdrMark, kDaikin128HdrSpace, kDaikin128BitMark, - kDaikin128OneSpace, kDaikin128BitMark, kDaikin128ZeroSpace, - kDaikin128BitMark, kDaikin128Gap, data, - kDaikin128SectionLength, - kDaikin128Freq, false, 0, kDutyDefault); - // Section #2 (Data + Footer) - sendGeneric(0, 0, kDaikin128BitMark, - kDaikin128OneSpace, kDaikin128BitMark, kDaikin128ZeroSpace, - kDaikin128FooterMark, kDaikin128Gap, - data + kDaikin128SectionLength, - nbytes - kDaikin128SectionLength, - kDaikin128Freq, false, 0, kDutyDefault); - } -} -#endif // SEND_DAIKIN128 - -/// 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? -IRDaikin128::IRDaikin128(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 IRDaikin128::begin(void) { _irsend.begin(); } - -uint8_t IRDaikin128::calcFirstChecksum(const uint8_t state[]) { - return sumNibbles(state, kDaikin128SectionLength - 1, - state[kDaikin128SectionLength - 1] & 0x0F) & 0x0F; -} - -uint8_t IRDaikin128::calcSecondChecksum(const uint8_t state[]) { - return sumNibbles(state + kDaikin128SectionLength, - kDaikin128SectionLength - 1); -} - -/// 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 IRDaikin128::validChecksum(uint8_t state[]) { - // Validate the checksum of section #1. - if (state[kDaikin128SectionLength - 1] >> 4 != calcFirstChecksum(state)) - return false; - // Validate the checksum of section #2 - if (state[kDaikin128StateLength - 1] != calcSecondChecksum(state)) - return false; - return true; -} - -/// Calculate and set the checksum values for the internal state. -void IRDaikin128::checksum(void) { - _.Sum1 = calcFirstChecksum(_.raw); - _.Sum2 = calcSecondChecksum(_.raw); -} - -/// Reset the internal state to a fixed known good state. -void IRDaikin128::stateReset(void) { - for (uint8_t i = 0; i < kDaikin128StateLength; i++) _.raw[i] = 0x00; - _.raw[0] = 0x16; - _.raw[7] = 0x04; // Most significant nibble is a checksum. - _.raw[8] = 0xA1; - // _.raw[15] is a checksum byte, it will be set by checksum(). -} - -/// 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 *IRDaikin128::getRaw(void) { - checksum(); // Ensure correct settings before sending. - return _.raw; -} - -/// Set the internal state from a valid code for this protocol. -/// @param[in] new_code A valid code for this protocol. -void IRDaikin128::setRaw(const uint8_t new_code[]) { - std::memcpy(_.raw, new_code, kDaikin128StateLength); -} - -#if SEND_DAIKIN128 -/// Send the current internal state as an IR message. -/// @param[in] repeat Nr. of times the message will be repeated. -void IRDaikin128::send(const uint16_t repeat) { - _irsend.sendDaikin128(getRaw(), kDaikin128StateLength, repeat); -} -#endif // SEND_DAIKIN128 - -/// Set the Power toggle setting of the A/C. -/// @param[in] toggle true, the setting is on. false, the setting is off. -void IRDaikin128::setPowerToggle(const bool toggle) { _.Power = toggle; } - -/// Get the Power toggle setting of the A/C. -/// @return The current operating mode setting. -bool IRDaikin128::getPowerToggle(void) const { return _.Power; } - -/// Get the operating mode setting of the A/C. -/// @return The current operating mode setting. -uint8_t IRDaikin128::getMode(void) const { return _.Mode; } - -/// Set the operating mode of the A/C. -/// @param[in] mode The desired operating mode. -void IRDaikin128::setMode(const uint8_t mode) { - switch (mode) { - case kDaikin128Auto: - case kDaikin128Cool: - case kDaikin128Heat: - case kDaikin128Fan: - case kDaikin128Dry: - _.Mode = mode; - break; - default: - _.Mode = kDaikin128Auto; - break; - } - // Force a reset of mode dependant things. - setFan(getFan()); // Covers Quiet & Powerful too. - 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 IRDaikin128::convertMode(const stdAc::opmode_t mode) { - switch (mode) { - case stdAc::opmode_t::kCool: return kDaikin128Cool; - case stdAc::opmode_t::kHeat: return kDaikin128Heat; - case stdAc::opmode_t::kDry: return kDaikinDry; - case stdAc::opmode_t::kFan: return kDaikin128Fan; - default: return kDaikin128Auto; - } -} - -/// 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 IRDaikin128::toCommonMode(const uint8_t mode) { - switch (mode) { - case kDaikin128Cool: return stdAc::opmode_t::kCool; - case kDaikin128Heat: return stdAc::opmode_t::kHeat; - case kDaikin128Dry: return stdAc::opmode_t::kDry; - case kDaikin128Fan: return stdAc::opmode_t::kFan; - default: return stdAc::opmode_t::kAuto; - } -} - -/// Set the temperature. -/// @param[in] temp The temperature in degrees celsius. -void IRDaikin128::setTemp(const uint8_t temp) { - _.Temp = uint8ToBcd(std::min(kDaikin128MaxTemp, - std::max(temp, kDaikin128MinTemp))); -} - -/// Get the current temperature setting. -/// @return The current setting for temp. in degrees celsius. -uint8_t IRDaikin128::getTemp(void) const { return bcdToUint8(_.Temp); } - -/// Get the current fan speed setting. -/// @return The current fan speed. -uint8_t IRDaikin128::getFan(void) const { return _.Fan; } - -/// Set the speed of the fan. -/// @param[in] speed The desired setting. -void IRDaikin128::setFan(const uint8_t speed) { - uint8_t new_speed = speed; - uint8_t mode = _.Mode; - switch (speed) { - case kDaikin128FanQuiet: - case kDaikin128FanPowerful: - if (mode == kDaikin128Auto) new_speed = kDaikin128FanAuto; - // FALL-THRU - case kDaikin128FanAuto: - case kDaikin128FanHigh: - case kDaikin128FanMed: - case kDaikin128FanLow: - _.Fan = new_speed; - break; - default: - _.Fan = kDaikin128FanAuto; - return; - } -} - -/// 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 IRDaikin128::convertFan(const stdAc::fanspeed_t speed) { - switch (speed) { - case stdAc::fanspeed_t::kMin: return kDaikinFanQuiet; - case stdAc::fanspeed_t::kLow: return kDaikin128FanLow; - case stdAc::fanspeed_t::kMedium: return kDaikin128FanMed; - case stdAc::fanspeed_t::kHigh: return kDaikin128FanHigh; - case stdAc::fanspeed_t::kMax: return kDaikin128FanPowerful; - default: return kDaikin128FanAuto; - } -} - -/// 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 IRDaikin128::toCommonFanSpeed(const uint8_t speed) { - switch (speed) { - case kDaikin128FanPowerful: return stdAc::fanspeed_t::kMax; - case kDaikin128FanHigh: return stdAc::fanspeed_t::kHigh; - case kDaikin128FanMed: return stdAc::fanspeed_t::kMedium; - case kDaikin128FanLow: return stdAc::fanspeed_t::kLow; - case kDaikinFanQuiet: 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 IRDaikin128::setSwingVertical(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 IRDaikin128::getSwingVertical(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 IRDaikin128::setSleep(const bool on) { _.Sleep = on; } - -/// Get the Sleep mode of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRDaikin128::getSleep(void) const { return _.Sleep; } - -/// Set the Economy mode of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRDaikin128::setEcono(const bool on) { - uint8_t mode = _.Mode; - _.Econo = (on && (mode == kDaikin128Cool || mode == kDaikin128Heat)); -} - -/// Get the Economical mode of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRDaikin128::getEcono(void) const { return _.Econo; } - -/// Set the Quiet mode of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRDaikin128::setQuiet(const bool on) { - uint8_t mode = _.Mode; - if (on && (mode == kDaikin128Cool || mode == kDaikin128Heat)) - setFan(kDaikin128FanQuiet); - else if (_.Fan == kDaikin128FanQuiet) - setFan(kDaikin128FanAuto); -} - -/// Get the Quiet mode status of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRDaikin128::getQuiet(void) const { return _.Fan == kDaikin128FanQuiet; } - -/// Set the Powerful (Turbo) mode of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRDaikin128::setPowerful(const bool on) { - uint8_t mode = _.Mode; - if (on && (mode == kDaikin128Cool || mode == kDaikin128Heat)) - setFan(kDaikin128FanPowerful); - else if (_.Fan == kDaikin128FanPowerful) - setFan(kDaikin128FanAuto); -} - -/// Get the Powerful (Turbo) mode of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRDaikin128::getPowerful(void) const { - return _.Fan == kDaikin128FanPowerful; -} - -/// Set the clock on the A/C unit. -/// @param[in] mins_since_midnight Nr. of minutes past midnight. -void IRDaikin128::setClock(const uint16_t mins_since_midnight) { - uint16_t mins = mins_since_midnight; - if (mins_since_midnight >= 24 * 60) mins = 0; // Bounds check. - // Hours. - _.ClockHours = uint8ToBcd(mins / 60); - // Minutes. - _.ClockMins = uint8ToBcd(mins % 60); -} - -/// Get the clock time to be sent to the A/C unit. -/// @return The number of minutes past midnight. -uint16_t IRDaikin128::getClock(void) const { - return bcdToUint8(_.ClockHours) * 60 + bcdToUint8(_.ClockMins); -} - -/// Set the enable status of the On Timer. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRDaikin128::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 IRDaikin128::getOnTimerEnabled(void) const { return _.OnTimer; } - -#define SETTIME(x, n) do { \ - uint16_t mins = n;\ - if (n >= 24 * 60) mins = 0;\ - _.x##HalfHour = (mins % 60) >= 30;\ - _.x##Hours = uint8ToBcd(mins / 60);\ -} while (0) - -#define GETTIME(x) bcdToUint8(_.x##Hours) * 60 + (_.x##HalfHour ? 30 : 0) - -/// Set the On Timer time for the A/C unit. -/// @param[in] mins_since_midnight Nr. of minutes past midnight. -void IRDaikin128::setOnTimer(const uint16_t mins_since_midnight) { - SETTIME(On, mins_since_midnight); -} - -/// Get the On Timer time to be sent to the A/C unit. -/// @return The number of minutes past midnight. -uint16_t IRDaikin128::getOnTimer(void) const { return GETTIME(On); } - -/// Set the enable status of the Off Timer. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRDaikin128::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 IRDaikin128::getOffTimerEnabled(void) const { return _.OffTimer; } - -/// Set the Off Timer time for the A/C unit. -/// @param[in] mins_since_midnight Nr. of minutes past midnight. -void IRDaikin128::setOffTimer(const uint16_t mins_since_midnight) { - SETTIME(Off, mins_since_midnight); -} - -/// Get the Off Timer time to be sent to the A/C unit. -/// @return The number of minutes past midnight. -uint16_t IRDaikin128::getOffTimer(void) const { return GETTIME(Off); } - -/// Set the Light toggle setting of the A/C. -/// @param[in] unit Device to show the LED (Light) Display info about. -/// @note 0 is off. -void IRDaikin128::setLightToggle(const uint8_t unit) { - _.Ceiling = 0; - _.Wall = 0; - switch (unit) { - case kDaikin128BitCeiling: - _.Ceiling = 1; - break; - case kDaikin128BitWall: - _.Wall = 1; - break; - } -} - -/// Get the Light toggle setting of the A/C. -/// @return The current operating mode setting. -uint8_t IRDaikin128::getLightToggle(void) const { - uint8_t code = 0; - if (_.Ceiling) { - code = kDaikin128BitCeiling; - } else if (_.Wall) { - code = kDaikin128BitWall; - } - - return code; -} - -/// Convert the current internal state into a human readable string. -/// @return A human readable string. -String IRDaikin128::toString(void) const { - String result = ""; - result.reserve(240); // Reserve some heap for the string to reduce fragging. - result += addBoolToString(_.Power, kPowerToggleStr, false); - result += addModeToString(_.Mode, kDaikin128Auto, kDaikin128Cool, - kDaikin128Heat, kDaikin128Dry, kDaikin128Fan); - result += addTempToString(getTemp()); - result += addFanToString(_.Fan, kDaikin128FanHigh, kDaikin128FanLow, - kDaikin128FanAuto, kDaikin128FanQuiet, - kDaikin128FanMed); - result += addBoolToString(getPowerful(), kPowerfulStr); - result += addBoolToString(getQuiet(), kQuietStr); - result += addBoolToString(_.SwingV, kSwingVStr); - result += addBoolToString(_.Sleep, kSleepStr); - result += addBoolToString(_.Econo, kEconoStr); - result += addLabeledString(minsToString(getClock()), kClockStr); - result += addBoolToString(_.OnTimer, kOnTimerStr); - result += addLabeledString(minsToString(getOnTimer()), kOnTimerStr); - result += addBoolToString(_.OffTimer, kOffTimerStr); - result += addLabeledString(minsToString(getOffTimer()), kOffTimerStr); - result += addIntToString(getLightToggle(), kLightToggleStr); - result += kSpaceLBraceStr; - switch (getLightToggle()) { - case kDaikin128BitCeiling: result += kCeilingStr; break; - case kDaikin128BitWall: result += kWallStr; break; - case 0: result += kOffStr; break; - default: result += kUnknownStr; - } - result += ')'; - return result; -} - -/// Convert the current internal state into its stdAc::state_t equivalent. -/// @param[in] prev Ptr to a previous state. -/// @return The stdAc equivalent of the native settings. -stdAc::state_t IRDaikin128::toCommon(const stdAc::state_t *prev) const { - stdAc::state_t result; - if (prev != NULL) result = *prev; - result.protocol = decode_type_t::DAIKIN128; - 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.quiet = getQuiet(); - result.turbo = getPowerful(); - result.econo = _.Econo; - result.light ^= (getLightToggle() != 0); - result.sleep = _.Sleep ? 0 : -1; - result.clock = getClock(); - // Not supported. - result.swingh = stdAc::swingh_t::kOff; - result.clean = false; - result.filter = false; - result.beep = false; - return result; -} - -#if DECODE_DAIKIN128 -/// Decode the supplied Daikin 128-bit message. (DAIKIN128) -/// 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. -/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/827 -bool IRrecv::decodeDaikin128(decode_results *results, uint16_t offset, - const uint16_t nbits, const bool strict) { - if (results->rawlen < 2 * (nbits + kHeader) + kFooter - 1 + offset) - return false; - if (nbits / 8 <= kDaikin128SectionLength) return false; - - // Compliance - if (strict && nbits != kDaikin128Bits) return false; - - // Leader - for (uint8_t i = 0; i < 2; i++) { - if (!matchMark(results->rawbuf[offset++], kDaikin128LeaderMark, - kDaikinTolerance, kDaikinMarkExcess)) return false; - if (!matchSpace(results->rawbuf[offset++], kDaikin128LeaderSpace, - kDaikinTolerance, kDaikinMarkExcess)) return false; - } - const uint16_t ksectionSize[kDaikin128Sections] = { - kDaikin128SectionLength, (uint16_t)(nbits / 8 - kDaikin128SectionLength)}; - // Data Sections - uint16_t pos = 0; - for (uint8_t section = 0; section < kDaikin128Sections; section++) { - uint16_t used; - // Section Header (first section only) + Section Data (8 bytes) + - // Section Footer (Not for first section) - used = matchGeneric(results->rawbuf + offset, results->state + pos, - results->rawlen - offset, ksectionSize[section] * 8, - section == 0 ? kDaikin128HdrMark : 0, - section == 0 ? kDaikin128HdrSpace : 0, - kDaikin128BitMark, kDaikin128OneSpace, - kDaikin128BitMark, kDaikin128ZeroSpace, - section > 0 ? kDaikin128FooterMark : kDaikin128BitMark, - kDaikin128Gap, - section > 0, - kDaikinTolerance, kDaikinMarkExcess, false); - if (used == 0) return false; - offset += used; - pos += ksectionSize[section]; - } - // Compliance - if (strict) { - if (!IRDaikin128::validChecksum(results->state)) return false; - } - - // Success - results->decode_type = decode_type_t::DAIKIN128; - 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_DAIKIN128 - -#if SEND_DAIKIN152 -/// Send a Daikin152 (152-bit) A/C formatted 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. -/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/873 -void IRsend::sendDaikin152(const unsigned char data[], const uint16_t nbytes, - const uint16_t repeat) { - for (uint16_t r = 0; r <= repeat; r++) { - // Leader - sendGeneric(0, 0, kDaikin152BitMark, kDaikin152OneSpace, - kDaikin152BitMark, kDaikin152ZeroSpace, - kDaikin152BitMark, kDaikin152Gap, - (uint64_t)0, kDaikin152LeaderBits, - kDaikin152Freq, false, 0, kDutyDefault); - // Header + Data + Footer - sendGeneric(kDaikin152HdrMark, kDaikin152HdrSpace, kDaikin152BitMark, - kDaikin152OneSpace, kDaikin152BitMark, kDaikin152ZeroSpace, - kDaikin152BitMark, kDaikin152Gap, data, - nbytes, kDaikin152Freq, false, 0, kDutyDefault); - } -} -#endif // SEND_DAIKIN152 - -#if DECODE_DAIKIN152 -/// Decode the supplied Daikin 152-bit message. (DAIKIN152) -/// 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. -/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/873 -bool IRrecv::decodeDaikin152(decode_results *results, uint16_t offset, - const uint16_t nbits, const bool strict) { - if (results->rawlen < 2 * (5 + nbits + kFooter) + kHeader - 1 + offset) - return false; - if (nbits / 8 < kDaikin152StateLength) return false; - - // Compliance - if (strict && nbits != kDaikin152Bits) return false; - - uint16_t used; - - // Leader - uint64_t leader = 0; - used = matchGeneric(results->rawbuf + offset, &leader, - results->rawlen - offset, kDaikin152LeaderBits, - 0, 0, // No Header - kDaikin152BitMark, kDaikin152OneSpace, - kDaikin152BitMark, kDaikin152ZeroSpace, - kDaikin152BitMark, kDaikin152Gap, // Footer gap - false, _tolerance, kMarkExcess, false); - if (used == 0 || leader != 0) return false; - offset += used; - - // Header + Data + Footer - used = matchGeneric(results->rawbuf + offset, results->state, - results->rawlen - offset, nbits, - kDaikin152HdrMark, kDaikin152HdrSpace, - kDaikin152BitMark, kDaikin152OneSpace, - kDaikin152BitMark, kDaikin152ZeroSpace, - kDaikin152BitMark, kDaikin152Gap, - true, _tolerance, kMarkExcess, false); - if (used == 0) return false; - - // Compliance - if (strict) { - if (!IRDaikin152::validChecksum(results->state)) return false; - } - - // Success - results->decode_type = decode_type_t::DAIKIN152; - 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_DAIKIN152 - -/// 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? -IRDaikin152::IRDaikin152(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 IRDaikin152::begin(void) { _irsend.begin(); } - -#if SEND_DAIKIN152 -/// Send the current internal state as an IR message. -/// @param[in] repeat Nr. of times the message will be repeated. -void IRDaikin152::send(const uint16_t repeat) { - _irsend.sendDaikin152(getRaw(), kDaikin152StateLength, repeat); -} -#endif // SEND_DAIKIN152 - -/// 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 IRDaikin152::validChecksum(uint8_t state[], const uint16_t length) { - // Validate the checksum of the given state. - if (length <= 1 || state[length - 1] != sumBytes(state, length - 1)) - return false; - else - return true; -} - -/// Calculate and set the checksum values for the internal state. -void IRDaikin152::checksum(void) { - _.Sum = sumBytes(_.raw, kDaikin152StateLength - 1); -} - -/// Reset the internal state to a fixed known good state. -void IRDaikin152::stateReset(void) { - for (uint8_t i = 3; i < kDaikin152StateLength; i++) _.raw[i] = 0x00; - _.raw[0] = 0x11; - _.raw[1] = 0xDA; - _.raw[2] = 0x27; - _.raw[15] = 0xC5; - // _.raw[19] is a checksum byte, it will be set by checksum(). -} - -/// 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 *IRDaikin152::getRaw(void) { - checksum(); // Ensure correct settings before sending. - return _.raw; -} - -/// Set the internal state from a valid code for this protocol. -/// @param[in] new_code A valid code for this protocol. -void IRDaikin152::setRaw(const uint8_t new_code[]) { - std::memcpy(_.raw, new_code, kDaikin152StateLength); -} - -/// Change the power setting to On. -void IRDaikin152::on(void) { setPower(true); } - -/// Change the power setting to Off. -void IRDaikin152::off(void) { setPower(false); } - -/// Change the power setting. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRDaikin152::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 IRDaikin152::getPower(void) const { return _.Power; } - -/// Get the operating mode setting of the A/C. -/// @return The current operating mode setting. -uint8_t IRDaikin152::getMode(void) const { return _.Mode; } - -/// Set the operating mode of the A/C. -/// @param[in] mode The desired operating mode. -void IRDaikin152::setMode(const uint8_t mode) { - switch (mode) { - case kDaikinFan: - setTemp(kDaikin152FanTemp); // Handle special temp for fan mode. - break; - case kDaikinDry: - setTemp(kDaikin152DryTemp); // Handle special temp for dry mode. - break; - case kDaikinAuto: - case kDaikinCool: - case kDaikinHeat: - break; - default: - _.Mode = kDaikinAuto; - return; - } - _.Mode = 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 IRDaikin152::convertMode(const stdAc::opmode_t mode) { - return IRDaikinESP::convertMode(mode); -} - -/// Set the temperature. -/// @param[in] temp The temperature in degrees celsius. -void IRDaikin152::setTemp(const uint8_t temp) { - uint8_t degrees = std::max( - temp, (_.Mode == kDaikinHeat) ? kDaikinMinTemp : kDaikin2MinCoolTemp); - degrees = std::min(degrees, kDaikinMaxTemp); - if (temp == kDaikin152FanTemp) degrees = temp; // Handle fan only temp. - _.Temp = degrees; -} - -/// Get the current temperature setting. -/// @return The current setting for temp. in degrees celsius. -uint8_t IRDaikin152::getTemp(void) const { return _.Temp; } - -/// Set the speed of the fan. -/// @param[in] fan The desired setting. -/// @note 1-5 or kDaikinFanAuto or kDaikinFanQuiet -void IRDaikin152::setFan(const uint8_t fan) { - // Set the fan speed bits, leave low 4 bits alone - uint8_t fanset; - if (fan == kDaikinFanQuiet || fan == kDaikinFanAuto) - fanset = fan; - else if (fan < kDaikinFanMin || fan > kDaikinFanMax) - fanset = kDaikinFanAuto; - else - fanset = 2 + fan; - _.Fan = fanset; -} - -/// Get the current fan speed setting. -/// @return The current fan speed. -uint8_t IRDaikin152::getFan(void) const { - const uint8_t fan = _.Fan; - switch (fan) { - case kDaikinFanAuto: - case kDaikinFanQuiet: return fan; - default: return fan - 2; - } -} - -/// 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 IRDaikin152::convertFan(const stdAc::fanspeed_t speed) { - return IRDaikinESP::convertFan(speed); -} - -/// Set the Vertical Swing mode of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRDaikin152::setSwingV(const bool on) { - _.SwingV = (on ? kDaikinSwingOn : kDaikinSwingOff); -} - -/// Get the Vertical Swing mode of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRDaikin152::getSwingV(void) const { return _.SwingV; } - -/// Set the Quiet mode of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRDaikin152::setQuiet(const bool on) { - _.Quiet = on; - // Powerful & Quiet mode being on are mutually exclusive. - if (on) setPowerful(false); -} - -/// Get the Quiet mode status of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRDaikin152::getQuiet(void) const { return _.Quiet; } - -/// Set the Powerful (Turbo) mode of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRDaikin152::setPowerful(const bool on) { - _.Powerful = on; - if (on) { - // Powerful, Quiet, Comfort & Econo mode being on are mutually exclusive. - setQuiet(false); - setComfort(false); - setEcono(false); - } -} - -/// Get the Powerful (Turbo) mode of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRDaikin152::getPowerful(void) const { return _.Powerful; } - -/// Set the Economy mode of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRDaikin152::setEcono(const bool on) { - _.Econo = on; - // Powerful & Econo mode being on are mutually exclusive. - if (on) setPowerful(false); -} - -/// Get the Economical mode of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRDaikin152::getEcono(void) const { return _.Econo; } - -/// Set the Sensor mode of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRDaikin152::setSensor(const bool on) { _.Sensor = on; } - -/// Get the Sensor mode of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRDaikin152::getSensor(void) const { return _.Sensor; } - -/// Set the Comfort mode of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRDaikin152::setComfort(const bool on) { - _.Comfort = on; - if (on) { - // Comfort mode is incompatible with Powerful mode. - setPowerful(false); - // It also sets the fan to auto and turns off swingv. - setFan(kDaikinFanAuto); - setSwingV(false); - } -} - -/// Get the Comfort mode of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRDaikin152::getComfort(void) const { return _.Comfort; } - -/// Convert the current internal state into its stdAc::state_t equivalent. -/// @return The stdAc equivalent of the native settings. -stdAc::state_t IRDaikin152::toCommon(void) const { - stdAc::state_t result; - result.protocol = decode_type_t::DAIKIN152; - result.model = -1; // No models used. - result.power = _.Power; - result.mode = IRDaikinESP::toCommonMode(_.Mode); - result.celsius = true; - result.degrees = _.Temp; - result.fanspeed = IRDaikinESP::toCommonFanSpeed(getFan()); - result.swingv = _.SwingV ? stdAc::swingv_t::kAuto : stdAc::swingv_t::kOff; - result.quiet = _.Quiet; - result.turbo = _.Powerful; - result.econo = _.Econo; - // Not supported. - result.swingh = stdAc::swingh_t::kOff; - result.clean = false; - result.filter = false; - result.light = 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 IRDaikin152::toString(void) const { - String result = ""; - result.reserve(180); // Reserve some heap for the string to reduce fragging. - result += addBoolToString(_.Power, kPowerStr, false); - result += addModeToString(_.Mode, kDaikinAuto, kDaikinCool, kDaikinHeat, - kDaikinDry, kDaikinFan); - result += addTempToString(_.Temp); - result += addFanToString(getFan(), kDaikinFanMax, kDaikinFanMin, - kDaikinFanAuto, kDaikinFanQuiet, kDaikinFanMed); - result += addBoolToString(_.SwingV, kSwingVStr); - result += addBoolToString(_.Powerful, kPowerfulStr); - result += addBoolToString(_.Quiet, kQuietStr); - result += addBoolToString(_.Econo, kEconoStr); - result += addBoolToString(_.Sensor, kSensorStr); - result += addBoolToString(_.Comfort, kComfortStr); - return result; -} - -#if SEND_DAIKIN64 -/// Send a Daikin64 (64-bit) A/C 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. -/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1064 -void IRsend::sendDaikin64(const uint64_t data, const uint16_t nbits, - const uint16_t repeat) { - enableIROut(kDaikin64Freq); - for (uint16_t r = 0; r <= repeat; r++) { - for (uint8_t i = 0; i < 2; i++) { - // Leader - mark(kDaikin64LdrMark); - space(kDaikin64LdrSpace); - } - // Header + Data + Footer #1 - sendGeneric(kDaikin64HdrMark, kDaikin64HdrSpace, - kDaikin64BitMark, kDaikin64OneSpace, - kDaikin64BitMark, kDaikin64ZeroSpace, - kDaikin64BitMark, kDaikin64Gap, - data, nbits, kDaikin64Freq, false, 0, 50); - // Footer #2 - mark(kDaikin64HdrMark); - space(kDefaultMessageGap); // A guess of the gap between messages. - } -} -#endif // SEND_DAIKIN64 - -#if DECODE_DAIKIN64 -/// Decode the supplied Daikin 64-bit message. (DAIKIN64) -/// 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/1064 -bool IRrecv::decodeDaikin64(decode_results *results, uint16_t offset, - const uint16_t nbits, const bool strict) { - if (results->rawlen < 2 * nbits + kDaikin64Overhead - offset) - return false; // Too short a message to match. - // Compliance - if (strict && nbits != kDaikin64Bits) - return false; - - // Leader - for (uint8_t i = 0; i < 2; i++) { - if (!matchMark(results->rawbuf[offset++], kDaikin64LdrMark)) - return false; - if (!matchSpace(results->rawbuf[offset++], kDaikin64LdrSpace)) - return false; - } - // Header + Data + Footer #1 - uint16_t used = matchGeneric(results->rawbuf + offset, &results->value, - results->rawlen - offset, nbits, - kDaikin64HdrMark, kDaikin64HdrSpace, - kDaikin64BitMark, kDaikin64OneSpace, - kDaikin64BitMark, kDaikin64ZeroSpace, - kDaikin64BitMark, kDaikin64Gap, - false, _tolerance + kDaikin64ToleranceDelta, - kMarkExcess, false); - if (used == 0) return false; - offset += used; - // Footer #2 - if (!matchMark(results->rawbuf[offset++], kDaikin64HdrMark)) - return false; - - // Compliance - if (strict && !IRDaikin64::validChecksum(results->value)) return false; - // Success - results->decode_type = decode_type_t::DAIKIN64; - results->bits = nbits; - results->command = 0; - results->address = 0; - return true; -} -#endif // DAIKIN64 - -/// 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? -IRDaikin64::IRDaikin64(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 IRDaikin64::begin(void) { _irsend.begin(); } - -#if SEND_DAIKIN64 -/// Send the current internal state as an IR message. -/// @param[in] repeat Nr. of times the message will be repeated. -void IRDaikin64::send(const uint16_t repeat) { - _irsend.sendDaikin64(getRaw(), kDaikin64Bits, repeat); -} -#endif // SEND_DAIKIN64 - -/// 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 IRDaikin64::calcChecksum(const uint64_t state) { - uint64_t data = GETBITS64(state, 0, kDaikin64ChecksumOffset); - 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 state to verify the checksum of. -/// @return true, if the state has a valid checksum. Otherwise, false. -bool IRDaikin64::validChecksum(const uint64_t state) { - // Validate the checksum of the given state. - return (GETBITS64(state, kDaikin64ChecksumOffset, - kDaikin64ChecksumSize) == calcChecksum(state)); -} - -/// Calculate and set the checksum values for the internal state. -void IRDaikin64::checksum(void) { _.Sum = calcChecksum(_.raw); } - -/// Reset the internal state to a fixed known good state. -void IRDaikin64::stateReset(void) { _.raw = kDaikin64KnownGoodState; } - -/// 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 IRDaikin64::getRaw(void) { - checksum(); // Ensure correct settings before sending. - return _.raw; -} - -/// Set the internal state from a valid code for this protocol. -/// @param[in] new_state A valid code for this protocol. -void IRDaikin64::setRaw(const uint64_t new_state) { _.raw = new_state; } - -/// Set the Power toggle setting of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRDaikin64::setPowerToggle(const bool on) { _.Power = on; } - -/// Get the Power toggle setting of the A/C. -/// @return The current operating mode setting. -bool IRDaikin64::getPowerToggle(void) const { return _.Power; } - -/// Set the temperature. -/// @param[in] temp The temperature in degrees celsius. -void IRDaikin64::setTemp(const uint8_t temp) { - uint8_t degrees = std::max(temp, kDaikin64MinTemp); - degrees = std::min(degrees, kDaikin64MaxTemp); - _.Temp = uint8ToBcd(degrees); -} - -/// Get the current temperature setting. -/// @return The current setting for temp. in degrees celsius. -uint8_t IRDaikin64::getTemp(void) const { return bcdToUint8(_.Temp); } - -/// Get the operating mode setting of the A/C. -/// @return The current operating mode setting. -uint8_t IRDaikin64::getMode(void) const { return _.Mode; } - -/// Set the operating mode of the A/C. -/// @param[in] mode The desired operating mode. -void IRDaikin64::setMode(const uint8_t mode) { - switch (mode) { - case kDaikin64Fan: - case kDaikin64Dry: - case kDaikin64Cool: - case kDaikin64Heat: - _.Mode = mode; - break; - default: - _.Mode = kDaikin64Cool; - } -} - -/// 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 IRDaikin64::convertMode(const stdAc::opmode_t mode) { - switch (mode) { - case stdAc::opmode_t::kDry: return kDaikin64Dry; - case stdAc::opmode_t::kFan: return kDaikin64Fan; - case stdAc::opmode_t::kHeat: return kDaikin64Heat; - default: return kDaikin64Cool; - } -} - -/// 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 IRDaikin64::toCommonMode(const uint8_t mode) { - switch (mode) { - case kDaikin64Cool: return stdAc::opmode_t::kCool; - case kDaikin64Heat: return stdAc::opmode_t::kHeat; - case kDaikin64Dry: return stdAc::opmode_t::kDry; - case kDaikin64Fan: return stdAc::opmode_t::kFan; - default: return stdAc::opmode_t::kAuto; - } -} - -/// Get the current fan speed setting. -/// @return The current fan speed. -uint8_t IRDaikin64::getFan(void) const { return _.Fan; } - -/// Set the speed of the fan. -/// @param[in] speed The desired setting. -void IRDaikin64::setFan(const uint8_t speed) { - switch (speed) { - case kDaikin64FanQuiet: - case kDaikin64FanTurbo: - case kDaikin64FanAuto: - case kDaikin64FanHigh: - case kDaikin64FanMed: - case kDaikin64FanLow: - _.Fan = speed; - break; - default: - _.Fan = kDaikin64FanAuto; - } -} - -/// 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 IRDaikin64::convertFan(const stdAc::fanspeed_t speed) { - switch (speed) { - case stdAc::fanspeed_t::kMin: return kDaikin64FanQuiet; - case stdAc::fanspeed_t::kLow: return kDaikin64FanLow; - case stdAc::fanspeed_t::kMedium: return kDaikin64FanMed; - case stdAc::fanspeed_t::kHigh: return kDaikin64FanHigh; - case stdAc::fanspeed_t::kMax: return kDaikin64FanTurbo; - default: return kDaikin64FanAuto; - } -} - -/// 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 IRDaikin64::toCommonFanSpeed(const uint8_t speed) { - switch (speed) { - case kDaikin64FanTurbo: return stdAc::fanspeed_t::kMax; - case kDaikin64FanHigh: return stdAc::fanspeed_t::kHigh; - case kDaikin64FanMed: return stdAc::fanspeed_t::kMedium; - case kDaikin64FanLow: return stdAc::fanspeed_t::kLow; - case kDaikinFanQuiet: return stdAc::fanspeed_t::kMin; - default: return stdAc::fanspeed_t::kAuto; - } -} - -/// Get the Turbo (Powerful) mode status of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRDaikin64::getTurbo(void) const { return _.Fan == kDaikin64FanTurbo; } - -/// Set the Turbo (Powerful) mode of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRDaikin64::setTurbo(const bool on) { - if (on) { - setFan(kDaikin64FanTurbo); - } else if (_.Fan == kDaikin64FanTurbo) { - setFan(kDaikin64FanAuto); - } -} - -/// Get the Quiet mode status of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRDaikin64::getQuiet(void) const { return _.Fan == kDaikin64FanQuiet; } - -/// Set the Quiet mode of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRDaikin64::setQuiet(const bool on) { - if (on) { - setFan(kDaikin64FanQuiet); - } else if (_.Fan == kDaikin64FanQuiet) { - setFan(kDaikin64FanAuto); - } -} - -/// Set the Vertical Swing mode of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRDaikin64::setSwingVertical(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 IRDaikin64::getSwingVertical(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 IRDaikin64::setSleep(const bool on) { _.Sleep = on; } - -/// Get the Sleep mode of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRDaikin64::getSleep(void) const { return _.Sleep; } - -/// Set the clock on the A/C unit. -/// @param[in] mins_since_midnight Nr. of minutes past midnight. -void IRDaikin64::setClock(const uint16_t mins_since_midnight) { - uint16_t mins = mins_since_midnight; - if (mins_since_midnight >= 24 * 60) mins = 0; // Bounds check. - _.ClockMins = uint8ToBcd(mins % 60); - _.ClockHours = uint8ToBcd(mins / 60); -} - -/// Get the clock time to be sent to the A/C unit. -/// @return The number of minutes past midnight. -uint16_t IRDaikin64::getClock(void) const { - return bcdToUint8(_.ClockHours) * 60 + bcdToUint8(_.ClockMins); -} - -/// Set the enable status of the On Timer. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRDaikin64::setOnTimeEnabled(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 IRDaikin64::getOnTimeEnabled(void) const { return _.OnTimer; } - -/// Get the On Timer time to be sent to the A/C unit. -/// @return The number of minutes past midnight. -uint16_t IRDaikin64::getOnTime(void) const { return GETTIME(On); } - -/// Set the On Timer time for the A/C unit. -/// @param[in] mins_since_midnight Nr. of minutes past midnight. -void IRDaikin64::setOnTime(const uint16_t mins_since_midnight) { - SETTIME(On, mins_since_midnight); -} - -/// Set the enable status of the Off Timer. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRDaikin64::setOffTimeEnabled(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 IRDaikin64::getOffTimeEnabled(void) const { return _.OffTimer; } - -/// Get the Off Timer time to be sent to the A/C unit. -/// @return The number of minutes past midnight. -uint16_t IRDaikin64::getOffTime(void) const { return GETTIME(Off); } - -/// Set the Off Timer time for the A/C unit. -/// @param[in] mins_since_midnight Nr. of minutes past midnight. -void IRDaikin64::setOffTime(const uint16_t mins_since_midnight) { - SETTIME(Off, mins_since_midnight); -} - -/// Convert the current internal state into a human readable string. -/// @return A human readable string. -String IRDaikin64::toString(void) const { - String result = ""; - result.reserve(120); // Reserve some heap for the string to reduce fragging. - result += addBoolToString(_.Power, kPowerToggleStr, false); - result += addModeToString(_.Mode, 0xFF, kDaikin64Cool, - kDaikin64Heat, kDaikin64Dry, kDaikin64Fan); - result += addTempToString(getTemp()); - if (!getTurbo()) { - result += addFanToString(_.Fan, kDaikin64FanHigh, kDaikin64FanLow, - kDaikin64FanAuto, kDaikin64FanQuiet, - kDaikin64FanMed); - } else { - result += addIntToString(_.Fan, kFanStr); - result += kSpaceLBraceStr; - result += kTurboStr; - result += ')'; - } - result += addBoolToString(getTurbo(), kTurboStr); - result += addBoolToString(getQuiet(), kQuietStr); - result += addBoolToString(_.SwingV, kSwingVStr); - result += addBoolToString(_.Sleep, kSleepStr); - result += addLabeledString(minsToString(getClock()), kClockStr); - result += addLabeledString(_.OnTimer - ? minsToString(getOnTime()) : kOffStr, - kOnTimerStr); - result += addLabeledString(_.OffTimer - ? minsToString(getOffTime()) : kOffStr, - kOffTimerStr); - return result; -} - -/// Convert the current internal state into its stdAc::state_t equivalent. -/// @param[in] prev Ptr to a previous state. -/// @return The stdAc equivalent of the native settings. -stdAc::state_t IRDaikin64::toCommon(const stdAc::state_t *prev) const { - stdAc::state_t result; - if (prev != NULL) result = *prev; - result.protocol = decode_type_t::DAIKIN64; - 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.turbo = getTurbo(); - result.quiet = getQuiet(); - result.sleep = _.Sleep ? 0 : -1; - result.clock = getClock(); - // Not supported. - result.swingh = stdAc::swingh_t::kOff; - result.clean = false; - result.filter = false; - result.beep = false; - result.econo = false; - result.light = false; - return result; -} diff --git a/lib/IRremoteESP8266/src/ir_Daikin.h b/lib/IRremoteESP8266/src/ir_Daikin.h index 0a12c5b69e..6487d20d7c 100644 --- a/lib/IRremoteESP8266/src/ir_Daikin.h +++ b/lib/IRremoteESP8266/src/ir_Daikin.h @@ -54,11 +54,11 @@ #ifndef UNIT_TEST #include #endif -#include "IRrecv.h" -#include "IRremoteESP8266.h" -#include "IRsend.h" +#include "../src/IRrecv.h" +#include "../src/IRremoteESP8266.h" +#include "../src/IRsend.h" #ifdef UNIT_TEST -#include "IRsend_test.h" +#include "../src/IRsend_test.h" #endif /// Native representation of a Daikin A/C message. diff --git a/lib/IRremoteESP8266/src/ir_Delonghi.cpp b/lib/IRremoteESP8266/src/ir_Delonghi.cpp deleted file mode 100644 index 21a787799b..0000000000 --- a/lib/IRremoteESP8266/src/ir_Delonghi.cpp +++ /dev/null @@ -1,470 +0,0 @@ -// 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 - -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; -} diff --git a/lib/IRremoteESP8266/src/ir_Delonghi.h b/lib/IRremoteESP8266/src/ir_Delonghi.h index ac2394cb16..7954e4ade1 100644 --- a/lib/IRremoteESP8266/src/ir_Delonghi.h +++ b/lib/IRremoteESP8266/src/ir_Delonghi.h @@ -16,10 +16,10 @@ #ifndef UNIT_TEST #include #endif -#include "IRremoteESP8266.h" -#include "IRsend.h" +#include "../src/IRremoteESP8266.h" +#include "../src/IRsend.h" #ifdef UNIT_TEST -#include "IRsend_test.h" +#include "../src/IRsend_test.h" #endif /// Native representation of a Delonghi A/C message. diff --git a/lib/IRremoteESP8266/src/ir_Denon.cpp b/lib/IRremoteESP8266/src/ir_Denon.cpp deleted file mode 100644 index 700fd31cbc..0000000000 --- a/lib/IRremoteESP8266/src/ir_Denon.cpp +++ /dev/null @@ -1,122 +0,0 @@ -// 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 -#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 diff --git a/lib/IRremoteESP8266/src/ir_Dish.cpp b/lib/IRremoteESP8266/src/ir_Dish.cpp deleted file mode 100644 index 94f5450b80..0000000000 --- a/lib/IRremoteESP8266/src/ir_Dish.cpp +++ /dev/null @@ -1,103 +0,0 @@ -// 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 diff --git a/lib/IRremoteESP8266/src/ir_Doshisha.cpp b/lib/IRremoteESP8266/src/ir_Doshisha.cpp deleted file mode 100644 index 0a5fadedb8..0000000000 --- a/lib/IRremoteESP8266/src/ir_Doshisha.cpp +++ /dev/null @@ -1,124 +0,0 @@ -// 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 diff --git a/lib/IRremoteESP8266/src/ir_Ecoclim.cpp b/lib/IRremoteESP8266/src/ir_Ecoclim.cpp deleted file mode 100644 index b16f8beb2d..0000000000 --- a/lib/IRremoteESP8266/src/ir_Ecoclim.cpp +++ /dev/null @@ -1,423 +0,0 @@ -// Copyright 2021 David Conran - -/// @file -/// @brief EcoClim A/C protocol. -/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1397 - -#include "ir_Ecoclim.h" -#include -#include -#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.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; -} diff --git a/lib/IRremoteESP8266/src/ir_Ecoclim.h b/lib/IRremoteESP8266/src/ir_Ecoclim.h index ea243f67cc..63a91c7109 100644 --- a/lib/IRremoteESP8266/src/ir_Ecoclim.h +++ b/lib/IRremoteESP8266/src/ir_Ecoclim.h @@ -16,10 +16,10 @@ #ifndef UNIT_TEST #include #endif -#include "IRremoteESP8266.h" -#include "IRsend.h" +#include "../src/IRremoteESP8266.h" +#include "../src/IRsend.h" #ifdef UNIT_TEST -#include "IRsend_test.h" +#include "../src/IRsend_test.h" #endif // Constants diff --git a/lib/IRremoteESP8266/src/ir_Electra.h b/lib/IRremoteESP8266/src/ir_Electra.h index 8fd4ee182a..e21d09930c 100644 --- a/lib/IRremoteESP8266/src/ir_Electra.h +++ b/lib/IRremoteESP8266/src/ir_Electra.h @@ -22,10 +22,10 @@ #ifndef UNIT_TEST #include #endif -#include "IRremoteESP8266.h" -#include "IRsend.h" +#include "../src/IRremoteESP8266.h" +#include "../src/IRsend.h" #ifdef UNIT_TEST -#include "IRsend_test.h" +#include "../src/IRsend_test.h" #endif /// Native representation of a Electra A/C message. diff --git a/lib/IRremoteESP8266/src/ir_EliteScreens.cpp b/lib/IRremoteESP8266/src/ir_EliteScreens.cpp deleted file mode 100644 index bfbdcc1e70..0000000000 --- a/lib/IRremoteESP8266/src/ir_EliteScreens.cpp +++ /dev/null @@ -1,89 +0,0 @@ -// 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 diff --git a/lib/IRremoteESP8266/src/ir_Epson.cpp b/lib/IRremoteESP8266/src/ir_Epson.cpp deleted file mode 100644 index a92beef4ff..0000000000 --- a/lib/IRremoteESP8266/src/ir_Epson.cpp +++ /dev/null @@ -1,111 +0,0 @@ -// 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 -#include -#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 diff --git a/lib/IRremoteESP8266/src/ir_Fujitsu.cpp b/lib/IRremoteESP8266/src/ir_Fujitsu.cpp deleted file mode 100644 index da4b41dcf6..0000000000 --- a/lib/IRremoteESP8266/src/ir_Fujitsu.cpp +++ /dev/null @@ -1,1043 +0,0 @@ -// Copyright 2017 Jonny Graham -// Copyright 2017-2021 David Conran -// Copyright 2021 siriuslzx - -/// @file -/// @brief Support for Fujitsu A/C protocols. -/// Fujitsu A/C support added by Jonny Graham & David Conran -/// @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 - -#include "ir_Fujitsu.h" -#include -#ifndef ARDUINO -#include -#endif -#include "IRsend.h" -#include "IRtext.h" -#include "IRutils.h" - -// Ref: -// These values are based on averages of measurements -const uint16_t kFujitsuAcHdrMark = 3324; -const uint16_t kFujitsuAcHdrSpace = 1574; -const uint16_t kFujitsuAcBitMark = 448; -const uint16_t kFujitsuAcOneSpace = 1182; -const uint16_t kFujitsuAcZeroSpace = 390; -const uint16_t kFujitsuAcMinGap = 8100; -const uint8_t kFujitsuAcExtraTolerance = 5; // Extra tolerance percentage. - -using irutils::addBoolToString; -using irutils::addIntToString; -using irutils::addLabeledString; -using irutils::addModeToString; -using irutils::addModelToString; -using irutils::addFanToString; -using irutils::addTempFloatToString; -using irutils::minsToString; - -#if SEND_FUJITSU_AC -/// Send a Fujitsu A/C formatted message. -/// Status: STABLE / Known Good. -/// @param[in] data The message to be sent. -/// @param[in] nbytes The number of bytes of message to be sent. -/// Typically one of: -/// kFujitsuAcStateLength, -/// kFujitsuAcStateLength - 1, -/// kFujitsuAcStateLengthShort, -/// kFujitsuAcStateLengthShort - 1 -/// @param[in] repeat The number of times the command is to be repeated. -void IRsend::sendFujitsuAC(const unsigned char data[], const uint16_t nbytes, - const uint16_t repeat) { - sendGeneric(kFujitsuAcHdrMark, kFujitsuAcHdrSpace, kFujitsuAcBitMark, - kFujitsuAcOneSpace, kFujitsuAcBitMark, kFujitsuAcZeroSpace, - kFujitsuAcBitMark, kFujitsuAcMinGap, data, nbytes, 38, false, - repeat, 50); -} -#endif // SEND_FUJITSU_AC - -// Code to emulate Fujitsu A/C IR remote control unit. - -/// Class Constructor -/// @param[in] pin GPIO to be used when sending. -/// @param[in] model The enum for the model of A/C to be emulated. -/// @param[in] inverted Is the output signal to be inverted? -/// @param[in] use_modulation Is frequency modulation to be used? -IRFujitsuAC::IRFujitsuAC(const uint16_t pin, - const fujitsu_ac_remote_model_t model, - const bool inverted, const bool use_modulation) - : _irsend(pin, inverted, use_modulation) { - setModel(model); - stateReset(); -} - -/// Set the currently emulated model of the A/C. -/// @param[in] model An enum representing the model to support/emulate. -void IRFujitsuAC::setModel(const fujitsu_ac_remote_model_t model) { - _model = model; - switch (model) { - case fujitsu_ac_remote_model_t::ARDB1: - case fujitsu_ac_remote_model_t::ARJW2: - _state_length = kFujitsuAcStateLength - 1; - _state_length_short = kFujitsuAcStateLengthShort - 1; - break; - case fujitsu_ac_remote_model_t::ARRY4: - case fujitsu_ac_remote_model_t::ARRAH2E: - case fujitsu_ac_remote_model_t::ARREB1E: - default: - _state_length = kFujitsuAcStateLength; - _state_length_short = kFujitsuAcStateLengthShort; - } -} - -/// Get the currently emulated/detected model of the A/C. -/// @return The enum representing the model of A/C. -fujitsu_ac_remote_model_t IRFujitsuAC::getModel(void) const { return _model; } - -/// Reset the state of the remote to a known good state/sequence. -void IRFujitsuAC::stateReset(void) { - for (size_t i = 0; i < kFujitsuAcStateLength; i++) { - _.longcode[i] = 0; - } - setTemp(24); - _.Fan = kFujitsuAcFanHigh; - _.Mode = kFujitsuAcModeCool; - _.Swing = kFujitsuAcSwingBoth; - _cmd = kFujitsuAcCmdTurnOn; - _.Filter = false; - _.Clean = false; - _.TimerType = kFujitsuAcStopTimers; - _.OnTimer = 0; - _.OffTimer = 0; - _.longcode[0] = 0x14; - _.longcode[1] = 0x63; - _.longcode[3] = 0x10; - _.longcode[4] = 0x10; -} - -/// Set up hardware to be able to send a message. -void IRFujitsuAC::begin(void) { _irsend.begin(); } - -#if SEND_FUJITSU_AC -/// Send the current internal state as an IR message. -/// @param[in] repeat Nr. of times the message will be repeated. -void IRFujitsuAC::send(const uint16_t repeat) { - _irsend.sendFujitsuAC(getRaw(), getStateLength(), repeat); -} -#endif // SEND_FUJITSU_AC - -/// Update the length (size) of the state code for the current configuration. -/// @return true, if use long codes; false, use short codes. -bool IRFujitsuAC::updateUseLongOrShort(void) { - bool fullCmd = false; - switch (_cmd) { - case kFujitsuAcCmdTurnOff: // 0x02 - case kFujitsuAcCmdEcono: // 0x09 - case kFujitsuAcCmdPowerful: // 0x39 - case kFujitsuAcCmdStepVert: // 0x6C - case kFujitsuAcCmdToggleSwingVert: // 0x6D - case kFujitsuAcCmdStepHoriz: // 0x79 - case kFujitsuAcCmdToggleSwingHoriz: // 0x7A - _.Cmd = _cmd; - break; - default: - switch (_model) { - case fujitsu_ac_remote_model_t::ARRY4: - case fujitsu_ac_remote_model_t::ARRAH2E: - case fujitsu_ac_remote_model_t::ARREB1E: - case fujitsu_ac_remote_model_t::ARREW4E: - _.Cmd = 0xFE; - break; - case fujitsu_ac_remote_model_t::ARDB1: - case fujitsu_ac_remote_model_t::ARJW2: - _.Cmd = 0xFC; - break; - } - fullCmd = true; - break; - } - return fullCmd; -} - -/// Calculate and set the checksum values for the internal state. -void IRFujitsuAC::checkSum(void) { - if (updateUseLongOrShort()) { // Is it a long code? - // Nr. of bytes in the message after this byte. - _.RestLength = _state_length - 7; - _.Protocol = (_model == fujitsu_ac_remote_model_t::ARREW4E) ? 0x31 : 0x30; - _.Power = (_cmd == kFujitsuAcCmdTurnOn) || get10CHeat(); - - // These values depend on model - if (_model != fujitsu_ac_remote_model_t::ARREB1E && - _model != fujitsu_ac_remote_model_t::ARREW4E) { - _.OutsideQuiet = 0; - if (_model != fujitsu_ac_remote_model_t::ARRAH2E) { - _.TimerType = kFujitsuAcStopTimers; - } - } - if (_model != fujitsu_ac_remote_model_t::ARRY4) { - if (_model != fujitsu_ac_remote_model_t::ARREW4E) _.Clean = false; - _.Filter = false; - } - // Set the On/Off/Sleep timer Nr of mins. - _.OffTimer = getOffSleepTimer(); - _.OnTimer = getOnTimer(); - // Enable bit for the Off/Sleep timer - _.OffTimerEnable = _.OffTimer > 0; - // Enable bit for the On timer - _.OnTimerEnable = _.OnTimer > 0; - - uint8_t checksum = 0; - uint8_t checksum_complement = 0; - switch (_model) { - case fujitsu_ac_remote_model_t::ARDB1: - case fujitsu_ac_remote_model_t::ARJW2: - _.Swing = kFujitsuAcSwingOff; - checksum = sumBytes(_.longcode, _state_length - 1); - checksum_complement = 0x9B; - break; - case fujitsu_ac_remote_model_t::ARREB1E: - case fujitsu_ac_remote_model_t::ARRAH2E: - case fujitsu_ac_remote_model_t::ARRY4: - _.unknown = 1; - // FALL THRU - default: - checksum = sumBytes(_.longcode + _state_length_short, - _state_length - _state_length_short - 1); - } - // and negate the checksum and store it in the last byte. - _.longcode[_state_length - 1] = checksum_complement - checksum; - } else { // short codes - for (size_t i = 0; i < _state_length_short; i++) { - _.shortcode[i] = _.longcode[i]; - } - switch (_model) { - case fujitsu_ac_remote_model_t::ARRY4: - case fujitsu_ac_remote_model_t::ARRAH2E: - case fujitsu_ac_remote_model_t::ARREB1E: - case fujitsu_ac_remote_model_t::ARREW4E: - // The last byte is the inverse of penultimate byte - _.shortcode[_state_length_short - 1] = - ~_.shortcode[_state_length_short - 2]; - break; - default: - {}; // We don't need to do anything for the others. - } - } -} - -/// Get the length (size) of the state code for the current configuration. -/// @return The length of the state array required for this config. -uint8_t IRFujitsuAC::getStateLength(void) { - return updateUseLongOrShort() ? _state_length : _state_length_short; -} - -/// 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* IRFujitsuAC::getRaw(void) { - checkSum(); - if (_.Cmd == 0xFE || _.Cmd == 0xFC) - return _.longcode; - return _.shortcode; -} - -/// Build the internal state/config from the current (raw) A/C message. -/// @param[in] length Size of the current/used (raw) A/C message array. -void IRFujitsuAC::buildFromState(const uint16_t length) { - switch (length) { - case kFujitsuAcStateLength - 1: - case kFujitsuAcStateLengthShort - 1: - setModel(fujitsu_ac_remote_model_t::ARDB1); - // ARJW2 has horizontal swing. - if (_.Swing > kFujitsuAcSwingVert) - setModel(fujitsu_ac_remote_model_t::ARJW2); - break; - default: - switch (_.Cmd) { - case kFujitsuAcCmdEcono: - case kFujitsuAcCmdPowerful: - setModel(fujitsu_ac_remote_model_t::ARREB1E); - break; - default: - setModel(fujitsu_ac_remote_model_t::ARRAH2E); - } - } - switch (_.RestLength) { - case 8: - if (_model != fujitsu_ac_remote_model_t::ARJW2) - setModel(fujitsu_ac_remote_model_t::ARDB1); - break; - case 9: - if (_model != fujitsu_ac_remote_model_t::ARREB1E) - setModel(fujitsu_ac_remote_model_t::ARRAH2E); - break; - } - if (_.Power) - setCmd(kFujitsuAcCmdTurnOn); - else - setCmd(kFujitsuAcCmdStayOn); - // Currently the only way we know how to tell ARRAH2E & ARRY4 apart is if - // either the raw Filter or Clean setting is on. - if (_model == fujitsu_ac_remote_model_t::ARRAH2E && (_.Filter || _.Clean)) - setModel(fujitsu_ac_remote_model_t::ARRY4); - if (_state_length == kFujitsuAcStateLength && _.OutsideQuiet) - setModel(fujitsu_ac_remote_model_t::ARREB1E); - switch (_.Cmd) { - case kFujitsuAcCmdTurnOff: - case kFujitsuAcCmdStepHoriz: - case kFujitsuAcCmdToggleSwingHoriz: - case kFujitsuAcCmdStepVert: - case kFujitsuAcCmdToggleSwingVert: - case kFujitsuAcCmdEcono: - case kFujitsuAcCmdPowerful: - setCmd(_.Cmd); - break; - } - if (_.Protocol == 0x31) setModel(fujitsu_ac_remote_model_t::ARREW4E); -} - -/// Set the internal state from a valid code for this protocol. -/// @param[in] newState A valid code for this protocol. -/// @param[in] length Size of the newState array. -/// @return true, if successful; Otherwise false. (i.e. size check) -bool IRFujitsuAC::setRaw(const uint8_t newState[], const uint16_t length) { - if (length > kFujitsuAcStateLength) return false; - for (uint16_t i = 0; i < kFujitsuAcStateLength; i++) { - if (i < length) - _.longcode[i] = newState[i]; - else - _.longcode[i] = 0; - } - buildFromState(length); - return true; -} - -/// Request the A/C to step the Horizontal Swing. -void IRFujitsuAC::stepHoriz(void) { setCmd(kFujitsuAcCmdStepHoriz); } - -/// Request the A/C to toggle the Horizontal Swing mode. -/// @param[in] update Do we need to update the general swing config? -void IRFujitsuAC::toggleSwingHoriz(const bool update) { - // Toggle the current setting. - if (update) setSwing(getSwing() ^ kFujitsuAcSwingHoriz); - // and set the appropriate special command. - setCmd(kFujitsuAcCmdToggleSwingHoriz); -} - -/// Request the A/C to step the Vertical Swing. -void IRFujitsuAC::stepVert(void) { setCmd(kFujitsuAcCmdStepVert); } - -/// Request the A/C to toggle the Vertical Swing mode. -/// @param[in] update Do we need to update the general swing config? -void IRFujitsuAC::toggleSwingVert(const bool update) { - // Toggle the current setting. - if (update) setSwing(getSwing() ^ kFujitsuAcSwingVert); - // and set the appropriate special command. - setCmd(kFujitsuAcCmdToggleSwingVert); -} - -/// Set the requested (special) command part for the A/C message. -/// @param[in] cmd The special command code. -void IRFujitsuAC::setCmd(const uint8_t cmd) { - switch (cmd) { - case kFujitsuAcCmdTurnOff: - case kFujitsuAcCmdTurnOn: - case kFujitsuAcCmdStayOn: - case kFujitsuAcCmdStepVert: - case kFujitsuAcCmdToggleSwingVert: - _cmd = cmd; - break; - case kFujitsuAcCmdStepHoriz: - case kFujitsuAcCmdToggleSwingHoriz: - switch (_model) { - // Only these remotes have horizontal. - case fujitsu_ac_remote_model_t::ARRAH2E: - case fujitsu_ac_remote_model_t::ARJW2: - _cmd = cmd; - break; - default: - _cmd = kFujitsuAcCmdStayOn; - } - break; - case kFujitsuAcCmdEcono: - case kFujitsuAcCmdPowerful: - switch (_model) { - // Only these remotes have these commands. - case ARREB1E: - case ARREW4E: - _cmd = cmd; - break; - default: - _cmd = kFujitsuAcCmdStayOn; - } - break; - default: - _cmd = kFujitsuAcCmdStayOn; - } -} - -/// Set the requested (special) command part for the A/C message. -/// @return The special command code. -uint8_t IRFujitsuAC::getCmd(void) const { - return _cmd; -} - -/// Change the power setting. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRFujitsuAC::setPower(const bool on) { - setCmd(on ? kFujitsuAcCmdTurnOn : kFujitsuAcCmdTurnOff); -} - -/// Set the requested power state of the A/C to off. -void IRFujitsuAC::off(void) { setPower(false); } - -/// Set the requested power state of the A/C to on. -void IRFujitsuAC::on(void) { setPower(true); } - -/// Get the value of the current power setting. -/// @return true, the setting is on. false, the setting is off. -bool IRFujitsuAC::getPower(void) const { return _cmd != kFujitsuAcCmdTurnOff; } - -/// Set the Outside Quiet mode of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRFujitsuAC::setOutsideQuiet(const bool on) { - _.OutsideQuiet = on; - setCmd(kFujitsuAcCmdStayOn); // No special command involved. -} - -/// Get the Outside Quiet mode status of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRFujitsuAC::getOutsideQuiet(void) const { - switch (_model) { - // Only ARREB1E & ARREW4E seems to have this mode. - case fujitsu_ac_remote_model_t::ARREB1E: - case fujitsu_ac_remote_model_t::ARREW4E: - return _.OutsideQuiet; - default: return false; - } -} - -/// Set the temperature. -/// @param[in] temp The temperature in degrees. -/// @param[in] useCelsius Use Celsius or Fahrenheit? -void IRFujitsuAC::setTemp(const float temp, const bool useCelsius) { - float mintemp; - float maxtemp; - uint8_t offset; - bool _useCelsius; - float _temp; - - switch (_model) { - // These models have native Fahrenheit & Celsius upport. - case fujitsu_ac_remote_model_t::ARREW4E: - _useCelsius = useCelsius; - _temp = temp; - break; - // Make sure everything else uses Celsius. - default: - _useCelsius = true; - _temp = useCelsius ? temp : fahrenheitToCelsius(temp); - } - setCelsius(_useCelsius); - if (_useCelsius) { - mintemp = kFujitsuAcMinTemp; - maxtemp = kFujitsuAcMaxTemp; - offset = kFujitsuAcTempOffsetC; - } else { - mintemp = kFujitsuAcMinTempF; - maxtemp = kFujitsuAcMaxTempF; - offset = kFujitsuAcTempOffsetF; - } - _temp = std::max(mintemp, _temp); - _temp = std::min(maxtemp, _temp); - if (_useCelsius) { - if (_model == fujitsu_ac_remote_model_t::ARREW4E) - _.Temp = (_temp - (offset / 2)) * 2; - else - _.Temp = (_temp - offset) * 4; - } else { - _.Temp = _temp - offset; - } - setCmd(kFujitsuAcCmdStayOn); // No special command involved. -} - -/// Get the current temperature setting. -/// @return The current setting for temp. in degrees of the currently set units. -float IRFujitsuAC::getTemp(void) const { - if (_model == fujitsu_ac_remote_model_t::ARREW4E) { - if (_.Fahrenheit) // Currently only ARREW4E supports native Fahrenheit. - return _.Temp + kFujitsuAcTempOffsetF; - else - return (_.Temp / 2.0) + (kFujitsuAcMinTemp / 2); - } else { - return _.Temp / 4 + kFujitsuAcMinTemp; - } -} - -/// Set the speed of the fan. -/// @param[in] fanSpeed The desired setting. -void IRFujitsuAC::setFanSpeed(const uint8_t fanSpeed) { - if (fanSpeed > kFujitsuAcFanQuiet) - _.Fan = kFujitsuAcFanHigh; // Set the fan to maximum if out of range. - else - _.Fan = fanSpeed; - setCmd(kFujitsuAcCmdStayOn); // No special command involved. -} - -/// Get the current fan speed setting. -/// @return The current fan speed. -uint8_t IRFujitsuAC::getFanSpeed(void) const { return _.Fan; } - -/// Set the operating mode of the A/C. -/// @param[in] mode The desired operating mode. -void IRFujitsuAC::setMode(const uint8_t mode) { - if (mode > kFujitsuAcModeHeat) - _.Mode = kFujitsuAcModeHeat; // Set the mode to maximum if out of range. - else - _.Mode = mode; - setCmd(kFujitsuAcCmdStayOn); // No special command involved. -} - -/// Get the operating mode setting of the A/C. -/// @return The current operating mode setting. -uint8_t IRFujitsuAC::getMode(void) const { return _.Mode; } - -/// Set the requested swing operation mode of the A/C unit. -/// @param[in] swingMode The swingMode code for the A/C. -/// Vertical, Horizon, or Both. See constants for details. -/// @note Not all models support all possible swing modes. -void IRFujitsuAC::setSwing(const uint8_t swingMode) { - _.Swing = swingMode; - switch (_model) { - // No Horizontal support. - case fujitsu_ac_remote_model_t::ARDB1: - case fujitsu_ac_remote_model_t::ARREB1E: - case fujitsu_ac_remote_model_t::ARRY4: - // Set the mode to max if out of range - if (swingMode > kFujitsuAcSwingVert) _.Swing = kFujitsuAcSwingVert; - break; - // Has Horizontal support. - case fujitsu_ac_remote_model_t::ARRAH2E: - case fujitsu_ac_remote_model_t::ARJW2: - default: - // Set the mode to max if out of range - if (swingMode > kFujitsuAcSwingBoth) _.Swing = kFujitsuAcSwingBoth; - } - setCmd(kFujitsuAcCmdStayOn); // No special command involved. -} - -/// Get the requested swing operation mode of the A/C unit. -/// @return The contents of the swing state/mode. -uint8_t IRFujitsuAC::getSwing(void) const { - return _.Swing; -} - -/// Set the Clean mode of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRFujitsuAC::setClean(const bool on) { - _.Clean = on; - setCmd(kFujitsuAcCmdStayOn); // No special command involved. -} - -/// Get the Clean mode status of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRFujitsuAC::getClean(void) const { - switch (_model) { - case fujitsu_ac_remote_model_t::ARRY4: return _.Clean; - default: return false; - } -} - -/// Set the Filter mode status of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRFujitsuAC::setFilter(const bool on) { - _.Filter = on; - setCmd(kFujitsuAcCmdStayOn); // No special command involved. -} - -/// Get the Filter mode status of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRFujitsuAC::getFilter(void) const { - switch (_model) { - case fujitsu_ac_remote_model_t::ARRY4: return _.Filter; - default: return false; - } -} - -/// Set the 10C heat status of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRFujitsuAC::set10CHeat(const bool on) { - switch (_model) { - // Only selected models support this. - case fujitsu_ac_remote_model_t::ARREW4E: - setClean(on); // 10C Heat uses the same bit as Clean - if (on) { - _.Mode = kFujitsuAcModeFan; - _.Power = true; - _.Fan = kFujitsuAcFanAuto; - _.Swing = kFujitsuAcSwingOff; - } - default: - break; - } -} - -/// Get the 10C heat status of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRFujitsuAC::get10CHeat(void) const { - switch (_model) { - case fujitsu_ac_remote_model_t::ARREW4E: - return (_.Clean && _.Power && _.Mode == kFujitsuAcModeFan && - _.Fan == kFujitsuAcFanAuto && _.Swing == kFujitsuAcSwingOff); - default: return false; - } -} - -/// Get the Timer type of the A/C message. -/// @return The current timer type in numeric form. -uint8_t IRFujitsuAC::getTimerType(void) const { - switch (_model) { - // These models seem to have timer support. - case fujitsu_ac_remote_model_t::ARRAH2E: - case fujitsu_ac_remote_model_t::ARREB1E: - return _.TimerType; - default: return kFujitsuAcStopTimers; - } -} - -/// Set the Timer type of the A/C message. -/// @param[in] timertype The kind of timer to use for the message. -void IRFujitsuAC::setTimerType(const uint8_t timertype) { - switch (timertype) { - case kFujitsuAcSleepTimer: - case kFujitsuAcOnTimer: - case kFujitsuAcOffTimer: - case kFujitsuAcStopTimers: - _.TimerType = timertype; - break; - default: _.TimerType = kFujitsuAcStopTimers; - } -} - -/// Get the On Timer setting of the A/C. -/// @return nr of minutes left on the timer. 0 means disabled/not supported. -uint16_t IRFujitsuAC::getOnTimer(void) const { - if (getTimerType() == kFujitsuAcOnTimer) - return _.OnTimer; - return 0; -} - -/// Set the On Timer setting of the A/C. -/// @param[in] nr_mins Nr. of minutes to set the timer to. 0 means disabled. -void IRFujitsuAC::setOnTimer(const uint16_t nr_mins) { - _.OnTimer = std::min(kFujitsuAcTimerMax, nr_mins); // Bounds check. - if (_.OnTimer) { - _.TimerType = kFujitsuAcOnTimer; - } else if (getTimerType() == kFujitsuAcOnTimer) { - _.TimerType = kFujitsuAcStopTimers; - } -} - -/// Get the Off/Sleep Timer setting of the A/C. -/// @return nr of minutes left on the timer. 0 means disabled/not supported. -uint16_t IRFujitsuAC::getOffSleepTimer(void) const { - switch (getTimerType()) { - case kFujitsuAcOffTimer: - case kFujitsuAcSleepTimer: - return _.OffTimer; - default: - return 0; - } -} - -/// Set the Off/Sleep Timer time for the A/C. -/// @param[in] nr_mins Nr. of minutes to set the timer to. 0 means disabled. -inline void IRFujitsuAC::setOffSleepTimer(const uint16_t nr_mins) { - _.OffTimer = std::min(kFujitsuAcTimerMax, nr_mins); // Bounds check. -} - -/// Set the Off Timer time for the A/C. -/// @param[in] nr_mins Nr. of minutes to set the timer to. 0 means disabled. -void IRFujitsuAC::setOffTimer(const uint16_t nr_mins) { - setOffSleepTimer(nr_mins); - if (nr_mins) - _.TimerType = kFujitsuAcOffTimer; - else if (getTimerType() != kFujitsuAcOnTimer) - _.TimerType = kFujitsuAcStopTimers; -} - -/// Set the Sleep Timer time for the A/C. -/// @param[in] nr_mins Nr. of minutes to set the timer to. 0 means disabled. -void IRFujitsuAC::setSleepTimer(const uint16_t nr_mins) { - setOffSleepTimer(nr_mins); - if (nr_mins) - _.TimerType = kFujitsuAcSleepTimer; - else if (getTimerType() != kFujitsuAcOnTimer) - _.TimerType = kFujitsuAcStopTimers; -} - -/// 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 IRFujitsuAC::validChecksum(uint8_t state[], const uint16_t length) { - uint8_t sum = 0; - uint8_t sum_complement = 0; - uint8_t checksum = state[length - 1]; - switch (length) { - case kFujitsuAcStateLengthShort: // ARRAH2E, ARREB1E, & ARRY4 - return state[length - 1] == (uint8_t)~state[length - 2]; - case kFujitsuAcStateLength - 1: // ARDB1 & ARJW2 - sum = sumBytes(state, length - 1); - sum_complement = 0x9B; - break; - case kFujitsuAcStateLength: // ARRAH2E, ARRY4, & ARREB1E - sum = sumBytes(state + kFujitsuAcStateLengthShort, - length - 1 - kFujitsuAcStateLengthShort); - break; - default: // Includes ARDB1 & ARJW2 short. - return true; // Assume the checksum is valid for other lengths. - } - return checksum == (uint8_t)(sum_complement - sum); // Does it match? -} - -/// Set the device's remote ID number. -/// @param[in] num The ID for the remote. Valid number range is 0 to 3. -void IRFujitsuAC::setId(const uint8_t num) { _.Id = num; } - -/// Get the current device's remote ID number. -/// @return The current device's remote ID number. -uint8_t IRFujitsuAC::getId(void) const { return _.Id; } - -/// Set the Temperature units for the A/C. -/// @param[in] on true, use Celsius. false, use Fahrenheit. -void IRFujitsuAC::setCelsius(const bool on) { _.Fahrenheit = !on; } - -/// Get the Clean mode status of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRFujitsuAC::getCelsius(void) const { return !_.Fahrenheit; } - -/// 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 IRFujitsuAC::convertMode(const stdAc::opmode_t mode) { - switch (mode) { - case stdAc::opmode_t::kCool: return kFujitsuAcModeCool; - case stdAc::opmode_t::kHeat: return kFujitsuAcModeHeat; - case stdAc::opmode_t::kDry: return kFujitsuAcModeDry; - case stdAc::opmode_t::kFan: return kFujitsuAcModeFan; - default: return kFujitsuAcModeAuto; - } -} - -/// 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 IRFujitsuAC::convertFan(stdAc::fanspeed_t speed) { - switch (speed) { - case stdAc::fanspeed_t::kMin: return kFujitsuAcFanQuiet; - case stdAc::fanspeed_t::kLow: return kFujitsuAcFanLow; - case stdAc::fanspeed_t::kMedium: return kFujitsuAcFanMed; - case stdAc::fanspeed_t::kHigh: - case stdAc::fanspeed_t::kMax: return kFujitsuAcFanHigh; - default: return kFujitsuAcFanAuto; - } -} - -/// 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 IRFujitsuAC::toCommonMode(const uint8_t mode) { - switch (mode) { - case kFujitsuAcModeCool: return stdAc::opmode_t::kCool; - case kFujitsuAcModeHeat: return stdAc::opmode_t::kHeat; - case kFujitsuAcModeDry: return stdAc::opmode_t::kDry; - case kFujitsuAcModeFan: 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 IRFujitsuAC::toCommonFanSpeed(const uint8_t speed) { - switch (speed) { - case kFujitsuAcFanHigh: return stdAc::fanspeed_t::kMax; - case kFujitsuAcFanMed: return stdAc::fanspeed_t::kMedium; - case kFujitsuAcFanLow: return stdAc::fanspeed_t::kLow; - case kFujitsuAcFanQuiet: 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 IRFujitsuAC::toCommon(void) const { - stdAc::state_t result; - result.protocol = decode_type_t::FUJITSU_AC; - result.model = _model; - result.power = getPower(); - result.mode = toCommonMode(_.Mode); - result.celsius = getCelsius(); - result.degrees = getTemp(); - result.fanspeed = toCommonFanSpeed(_.Fan); - uint8_t swing = _.Swing; - switch (result.model) { - case fujitsu_ac_remote_model_t::ARREB1E: - case fujitsu_ac_remote_model_t::ARRAH2E: - case fujitsu_ac_remote_model_t::ARRY4: - result.clean = _.Clean; - result.filter = _.Filter; - result.swingv = (swing & kFujitsuAcSwingVert) ? stdAc::swingv_t::kAuto - : stdAc::swingv_t::kOff; - result.swingh = (swing & kFujitsuAcSwingHoriz) ? stdAc::swingh_t::kAuto - : stdAc::swingh_t::kOff; - break; - case fujitsu_ac_remote_model_t::ARDB1: - case fujitsu_ac_remote_model_t::ARJW2: - default: - result.swingv = stdAc::swingv_t::kOff; - result.swingh = stdAc::swingh_t::kOff; - } - - result.quiet = _.Fan == kFujitsuAcFanQuiet; - result.turbo = _cmd == kFujitsuAcCmdPowerful; - result.econo = _cmd == kFujitsuAcCmdEcono; - // Not supported. - result.light = false; - result.filter = 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 IRFujitsuAC::toString(void) const { - String result = ""; - result.reserve(100); // Reserve some heap for the string to reduce fragging. - fujitsu_ac_remote_model_t model = _model; - result += addModelToString(decode_type_t::FUJITSU_AC, model, false); - result += addIntToString(_.Id, kIdStr); - result += addBoolToString(getPower(), kPowerStr); - result += addModeToString(_.Mode, kFujitsuAcModeAuto, kFujitsuAcModeCool, - kFujitsuAcModeHeat, kFujitsuAcModeDry, - kFujitsuAcModeFan); - result += addTempFloatToString(getTemp(), getCelsius()); - result += addFanToString(_.Fan, kFujitsuAcFanHigh, kFujitsuAcFanLow, - kFujitsuAcFanAuto, kFujitsuAcFanQuiet, - kFujitsuAcFanMed); - switch (model) { - // These models have no internal swing, clean. or filter state. - case fujitsu_ac_remote_model_t::ARDB1: - case fujitsu_ac_remote_model_t::ARJW2: - break; - // These models have Clean & Filter, plus Swing (via fall thru) - case fujitsu_ac_remote_model_t::ARRAH2E: - case fujitsu_ac_remote_model_t::ARREB1E: - case fujitsu_ac_remote_model_t::ARRY4: - result += addBoolToString(getClean(), kCleanStr); - result += addBoolToString(getFilter(), kFilterStr); - // FALL THRU - default: // e.g. ARREW4E - if (model == fujitsu_ac_remote_model_t::ARREW4E) - result += addBoolToString(get10CHeat(), k10CHeatStr); - result += addIntToString(_.Swing, kSwingStr); - result += kSpaceLBraceStr; - switch (_.Swing) { - case kFujitsuAcSwingOff: - result += kOffStr; - break; - case kFujitsuAcSwingVert: - result += kSwingVStr; - break; - case kFujitsuAcSwingHoriz: - result += kSwingHStr; - break; - case kFujitsuAcSwingBoth: - result += kSwingVStr; - result += '+'; - result += kSwingHStr; - break; - default: - result += kUnknownStr; - } - result += ')'; - } - result += kCommaSpaceStr; - result += kCommandStr; - result += kColonSpaceStr; - switch (_cmd) { - case kFujitsuAcCmdStepHoriz: - result += kStepStr; - result += ' '; - result += kSwingHStr; - break; - case kFujitsuAcCmdStepVert: - result += kStepStr; - result += ' '; - result += kSwingVStr; - break; - case kFujitsuAcCmdToggleSwingHoriz: - result += kToggleStr; - result += ' '; - result += kSwingHStr; - break; - case kFujitsuAcCmdToggleSwingVert: - result += kToggleStr; - result += ' '; - result += kSwingVStr; - break; - case kFujitsuAcCmdEcono: - result += kEconoStr; - break; - case kFujitsuAcCmdPowerful: - result += kPowerfulStr; - break; - default: - result += kNAStr; - } - uint16_t mins = 0; - String type_str = kTimerStr; - switch (model) { - case fujitsu_ac_remote_model_t::ARREB1E: - case fujitsu_ac_remote_model_t::ARREW4E: - result += addBoolToString(getOutsideQuiet(), kOutsideQuietStr); - // FALL THRU - // These models seem to have timer support. - case fujitsu_ac_remote_model_t::ARRAH2E: - switch (getTimerType()) { - case kFujitsuAcOnTimer: - type_str = kOnTimerStr; - mins = getOnTimer(); - break; - case kFujitsuAcOffTimer: - type_str = kOffTimerStr; - mins = getOffSleepTimer(); - break; - case kFujitsuAcSleepTimer: - type_str = kSleepTimerStr; - mins = getOffSleepTimer(); - break; - } - result += addLabeledString(mins ? minsToString(mins) : kOffStr, type_str); - break; - default: - break; - } - return result; -} - -#if DECODE_FUJITSU_AC -/// Decode the supplied Fujitsu AC IR message if possible. -/// 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::decodeFujitsuAC(decode_results* results, uint16_t offset, - const uint16_t nbits, - const bool strict) { - uint16_t dataBitsSoFar = 0; - - // Have we got enough data to successfully decode? - if (results->rawlen < (2 * kFujitsuAcMinBits) + kHeader + kFooter - 1 + - offset) - return false; // Can't possibly be a valid message. - - // Compliance - if (strict) { - switch (nbits) { - case kFujitsuAcBits: - case kFujitsuAcBits - 8: - case kFujitsuAcMinBits: - case kFujitsuAcMinBits + 8: break; - default: return false; // Must be called with the correct nr. of bits. - } - } - - // Header / Some of the Data - uint16_t used = matchGeneric(results->rawbuf + offset, results->state, - results->rawlen - offset, kFujitsuAcMinBits - 8, - kFujitsuAcHdrMark, kFujitsuAcHdrSpace, // Header - kFujitsuAcBitMark, kFujitsuAcOneSpace, // Data - kFujitsuAcBitMark, kFujitsuAcZeroSpace, - 0, 0, // No Footer (yet) - false, _tolerance + kFujitsuAcExtraTolerance, 0, - false); // LSBF - if (!used) return false; - offset += used; - // Check we have the typical data header. - if (results->state[0] != 0x14 || results->state[1] != 0x63) return false; - dataBitsSoFar += kFujitsuAcMinBits - 8; - - // Keep reading bytes until we either run out of message or state to fill. - match_result_t data_result; - for (uint16_t i = 5; - offset <= results->rawlen - 16 && i < kFujitsuAcStateLength; - i++, dataBitsSoFar += 8, offset += data_result.used) { - data_result = matchData( - &(results->rawbuf[offset]), 8, kFujitsuAcBitMark, kFujitsuAcOneSpace, - kFujitsuAcBitMark, kFujitsuAcZeroSpace, - _tolerance + kFujitsuAcExtraTolerance, 0, false); - if (data_result.success == false) break; // Fail - results->state[i] = data_result.data; - } - - // Footer - if (offset > results->rawlen || - !matchMark(results->rawbuf[offset++], kFujitsuAcBitMark)) - return false; - // The space is optional if we are out of capture. - if (offset < results->rawlen && - !matchAtLeast(results->rawbuf[offset], kFujitsuAcMinGap)) - return false; - - // Compliance - if (strict) { - if (dataBitsSoFar != nbits) return false; - } - - results->decode_type = FUJITSU_AC; - results->bits = dataBitsSoFar; - - // Compliance - switch (dataBitsSoFar) { - case kFujitsuAcMinBits: - // Check if this values indicate that this should have been a long state - // message. - if (results->state[5] == 0xFC) return false; - return true; // Success - case kFujitsuAcMinBits + 8: - // Check if this values indicate that this should have been a long state - // message. - if (results->state[5] == 0xFE) return false; - // The last byte needs to be the inverse of the penultimate byte. - if (results->state[5] != (uint8_t)~results->state[6]) return false; - return true; // Success - case kFujitsuAcBits - 8: - // Long messages of this size require this byte be correct. - if (results->state[5] != 0xFC) return false; - break; - case kFujitsuAcBits: - // Long messages of this size require this byte be correct. - if (results->state[5] != 0xFE) return false; - break; - default: - return false; // Unexpected size. - } - if (!IRFujitsuAC::validChecksum(results->state, dataBitsSoFar / 8)) - return false; - - // Success - return true; // All good. -} -#endif // DECODE_FUJITSU_AC diff --git a/lib/IRremoteESP8266/src/ir_Fujitsu.h b/lib/IRremoteESP8266/src/ir_Fujitsu.h index 70c0a4cf03..1414cab711 100644 --- a/lib/IRremoteESP8266/src/ir_Fujitsu.h +++ b/lib/IRremoteESP8266/src/ir_Fujitsu.h @@ -46,11 +46,11 @@ #ifdef ARDUINO #include #endif -#include "IRrecv.h" -#include "IRremoteESP8266.h" -#include "IRsend.h" +#include "../src/IRrecv.h" +#include "../src/IRremoteESP8266.h" +#include "../src/IRsend.h" #ifdef UNIT_TEST -#include "IRsend_test.h" +#include "../src/IRsend_test.h" #endif /// Native representation of a Fujitsu A/C message. diff --git a/lib/IRremoteESP8266/src/ir_GICable.cpp b/lib/IRremoteESP8266/src/ir_GICable.cpp deleted file mode 100644 index 7b29d71db4..0000000000 --- a/lib/IRremoteESP8266/src/ir_GICable.cpp +++ /dev/null @@ -1,95 +0,0 @@ -// 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 -#include -#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 diff --git a/lib/IRremoteESP8266/src/ir_GlobalCache.cpp b/lib/IRremoteESP8266/src/ir_GlobalCache.cpp deleted file mode 100644 index e8ebac4af8..0000000000 --- a/lib/IRremoteESP8266/src/ir_GlobalCache.cpp +++ /dev/null @@ -1,63 +0,0 @@ -// 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 -#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 diff --git a/lib/IRremoteESP8266/src/ir_Goodweather.cpp b/lib/IRremoteESP8266/src/ir_Goodweather.cpp deleted file mode 100644 index 1d6918e7ff..0000000000 --- a/lib/IRremoteESP8266/src/ir_Goodweather.cpp +++ /dev/null @@ -1,499 +0,0 @@ -// 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 -#ifndef ARDUINO -#include -#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 diff --git a/lib/IRremoteESP8266/src/ir_Goodweather.h b/lib/IRremoteESP8266/src/ir_Goodweather.h index 7e547edd83..e1f7f5ed5d 100644 --- a/lib/IRremoteESP8266/src/ir_Goodweather.h +++ b/lib/IRremoteESP8266/src/ir_Goodweather.h @@ -16,10 +16,10 @@ #ifndef UNIT_TEST #include #endif -#include "IRremoteESP8266.h" -#include "IRsend.h" +#include "../src/IRremoteESP8266.h" +#include "../src/IRsend.h" #ifdef UNIT_TEST -#include "IRsend_test.h" +#include "../src/IRsend_test.h" #endif /// Native representation of a Goodweather A/C message. diff --git a/lib/IRremoteESP8266/src/ir_Gree.h b/lib/IRremoteESP8266/src/ir_Gree.h index be5ac31ce5..cd0ef8a848 100644 --- a/lib/IRremoteESP8266/src/ir_Gree.h +++ b/lib/IRremoteESP8266/src/ir_Gree.h @@ -32,10 +32,10 @@ #ifndef UNIT_TEST #include #endif -#include "IRremoteESP8266.h" -#include "IRsend.h" +#include "../src/IRremoteESP8266.h" +#include "../src/IRsend.h" #ifdef UNIT_TEST -#include "IRsend_test.h" +#include "../src/IRsend_test.h" #endif /// Native representation of a Gree A/C message. diff --git a/lib/IRremoteESP8266/src/ir_Haier.h b/lib/IRremoteESP8266/src/ir_Haier.h index 4e5c8e64c2..516a0ca0ee 100644 --- a/lib/IRremoteESP8266/src/ir_Haier.h +++ b/lib/IRremoteESP8266/src/ir_Haier.h @@ -24,10 +24,10 @@ #ifndef UNIT_TEST #include #endif -#include "IRremoteESP8266.h" -#include "IRsend.h" +#include "../src/IRremoteESP8266.h" +#include "../src/IRsend.h" #ifdef UNIT_TEST -#include "IRsend_test.h" +#include "../src/IRsend_test.h" #endif /// Native representation of a Haier HSU07-HEA03 A/C message. diff --git a/lib/IRremoteESP8266/src/ir_Hitachi.cpp b/lib/IRremoteESP8266/src/ir_Hitachi.cpp deleted file mode 100644 index 49781997e1..0000000000 --- a/lib/IRremoteESP8266/src/ir_Hitachi.cpp +++ /dev/null @@ -1,1580 +0,0 @@ -// Copyright 2018-2019 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 - -#include "ir_Hitachi.h" -#include -#include -#ifndef ARDUINO -#include -#endif -#include "IRrecv.h" -#include "IRremoteESP8266.h" -#include "IRsend.h" -#include "IRtext.h" -#include "IRutils.h" - -// Constants -const uint16_t kHitachiAcHdrMark = 3300; -const uint16_t kHitachiAcHdrSpace = 1700; -const uint16_t kHitachiAc1HdrMark = 3400; -const uint16_t kHitachiAc1HdrSpace = 3400; -const uint16_t kHitachiAcBitMark = 400; -const uint16_t kHitachiAcOneSpace = 1250; -const uint16_t kHitachiAcZeroSpace = 500; -const uint32_t kHitachiAcMinGap = kDefaultMessageGap; // Just a guess. -// Support for HitachiAc424 protocol -const uint16_t kHitachiAc424LdrMark = 29784; // Leader -const uint16_t kHitachiAc424LdrSpace = 49290; // Leader -const uint16_t kHitachiAc424HdrMark = 3416; // Header -const uint16_t kHitachiAc424HdrSpace = 1604; // Header -const uint16_t kHitachiAc424BitMark = 463; -const uint16_t kHitachiAc424OneSpace = 1208; -const uint16_t kHitachiAc424ZeroSpace = 372; - -// Support for HitachiAc3 protocol -const uint16_t kHitachiAc3HdrMark = 3400; // Header -const uint16_t kHitachiAc3HdrSpace = 1660; // Header -const uint16_t kHitachiAc3BitMark = 460; -const uint16_t kHitachiAc3OneSpace = 1250; -const uint16_t kHitachiAc3ZeroSpace = 410; - -using irutils::addBoolToString; -using irutils::addIntToString; -using irutils::addLabeledString; -using irutils::addModeToString; -using irutils::addModelToString; -using irutils::addFanToString; -using irutils::addTempToString; -using irutils::checkInvertedBytePairs; -using irutils::invertBytePairs; -using irutils::minsToString; - -#if (SEND_HITACHI_AC || SEND_HITACHI_AC2 || SEND_HITACHI_AC344) -/// Send a Hitachi 28-byte/224-bit A/C formatted message. (HITACHI_AC) -/// 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. -/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/417 -void IRsend::sendHitachiAC(const unsigned char data[], const uint16_t nbytes, - const uint16_t repeat) { - if (nbytes < kHitachiAcStateLength) - return; // Not enough bytes to send a proper message. - - const bool MSBfirst = (nbytes == kHitachiAc344StateLength) ? false : true; - sendGeneric(kHitachiAcHdrMark, kHitachiAcHdrSpace, kHitachiAcBitMark, - kHitachiAcOneSpace, kHitachiAcBitMark, kHitachiAcZeroSpace, - kHitachiAcBitMark, kHitachiAcMinGap, data, nbytes, 38, MSBfirst, - repeat, 50); -} -#endif // (SEND_HITACHI_AC || SEND_HITACHI_AC2 || SEND_HITACHI_AC344) - -#if SEND_HITACHI_AC1 -/// Send a Hitachi 13 byte/224-bit A/C formatted message. (HITACHI_AC1) -/// Status: STABLE / Confirmed 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. -/// @note Basically the same as sendHitatchiAC() except different size & header. -/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/453 -void IRsend::sendHitachiAC1(const unsigned char data[], const uint16_t nbytes, - const uint16_t repeat) { - if (nbytes < kHitachiAc1StateLength) - return; // Not enough bytes to send a proper message. - sendGeneric(kHitachiAc1HdrMark, kHitachiAc1HdrSpace, kHitachiAcBitMark, - kHitachiAcOneSpace, kHitachiAcBitMark, kHitachiAcZeroSpace, - kHitachiAcBitMark, kHitachiAcMinGap, data, nbytes, kHitachiAcFreq, - true, repeat, kDutyDefault); -} -#endif // SEND_HITACHI_AC1 - -#if SEND_HITACHI_AC2 -/// Send a Hitachi 53 byte/424-bit A/C formatted message. (HITACHI_AC2) -/// Basically the same as sendHitatchiAC() except different size. -/// Status: STABLE / Expected to work. -/// @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::sendHitachiAC2(const unsigned char data[], const uint16_t nbytes, - const uint16_t repeat) { - if (nbytes < kHitachiAc2StateLength) - return; // Not enough bytes to send a proper message. - sendHitachiAC(data, nbytes, repeat); -} -#endif // SEND_HITACHI_AC2 - -#if SEND_HITACHI_AC344 -/// Send a Hitachi A/C 43-byte/344-bit message. (HITACHI_AC344) -/// Basically the same as sendHitatchiAC() except different size. -/// Status: Beta / Probably works. -/// @param[in] data An array of bytes containing the IR command. -/// @param[in] nbytes Nr. of bytes of data in the array. -/// @param[in] repeat Nr. of times the message is to be repeated. (Default = 0). -/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1134 -void IRsend::sendHitachiAc344(const unsigned char data[], const uint16_t nbytes, - const uint16_t repeat) { - if (nbytes < kHitachiAc344StateLength) - return; // Not enough bytes to send a proper message. - sendHitachiAC(data, nbytes, repeat); -} -#endif // SEND_HITACHI_AC344 - -/// 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? -IRHitachiAc::IRHitachiAc(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 IRHitachiAc::stateReset(void) { - _.raw[0] = 0x80; - _.raw[1] = 0x08; - _.raw[2] = 0x0C; - _.raw[3] = 0x02; - _.raw[4] = 0xFD; - _.raw[5] = 0x80; - _.raw[6] = 0x7F; - _.raw[7] = 0x88; - _.raw[8] = 0x48; - _.raw[9] = 0x10; - for (uint8_t i = 10; i < kHitachiAcStateLength; i++) _.raw[i] = 0x00; - _.raw[14] = 0x60; - _.raw[15] = 0x60; - _.raw[24] = 0x80; - setTemp(23); -} - -/// Set up hardware to be able to send a message. -void IRHitachiAc::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 size/length of the state. -/// @return The calculated checksum value. -uint8_t IRHitachiAc::calcChecksum(const uint8_t state[], - const uint16_t length) { - uint8_t sum = 62; - for (uint16_t i = 0; i < length - 1; i++) sum -= reverseBits(state[i], 8); - return reverseBits(sum, 8); -} - -/// Calculate and set the checksum values for the internal state. -/// @param[in] length The size/length of the state. -void IRHitachiAc::checksum(const uint16_t length) { - _.Sum = calcChecksum(_.raw, 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 IRHitachiAc::validChecksum(const uint8_t state[], const uint16_t length) { - if (length < 2) return true; // Assume true for lengths that are too short. - return (state[length - 1] == calcChecksum(state, length)); -} - -/// 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 *IRHitachiAc::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 new_code array. -void IRHitachiAc::setRaw(const uint8_t new_code[], const uint16_t length) { - std::memcpy(_.raw, new_code, std::min(length, kHitachiAcStateLength)); -} - -#if SEND_HITACHI_AC -/// Send the current internal state as an IR message. -/// @param[in] repeat Nr. of times the message will be repeated. -void IRHitachiAc::send(const uint16_t repeat) { - _irsend.sendHitachiAC(getRaw(), kHitachiAcStateLength, repeat); -} -#endif // SEND_HITACHI_AC - -/// Get the value of the current power setting. -/// @return true, the setting is on. false, the setting is off. -bool IRHitachiAc::getPower(void) const { - return _.Power; -} - -/// Change the power setting. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRHitachiAc::setPower(const bool on) { - _.Power = on; -} - -/// Change the power setting to On. -void IRHitachiAc::on(void) { setPower(true); } - -/// Change the power setting to Off. -void IRHitachiAc::off(void) { setPower(false); } - -/// Get the operating mode setting of the A/C. -/// @return The current operating mode setting. -uint8_t IRHitachiAc::getMode(void) const { return reverseBits(_.Mode, 8); } - -/// Set the operating mode of the A/C. -/// @param[in] mode The desired operating mode. -void IRHitachiAc::setMode(const uint8_t mode) { - uint8_t newmode = mode; - switch (mode) { - // Fan mode sets a special temp. - case kHitachiAcFan: setTemp(64); break; - case kHitachiAcAuto: - case kHitachiAcHeat: - case kHitachiAcCool: - case kHitachiAcDry: break; - default: newmode = kHitachiAcAuto; - } - _.Mode = reverseBits(newmode, 8); - if (mode != kHitachiAcFan) setTemp(_previoustemp); - setFan(getFan()); // Reset the fan speed after the mode change. -} - -/// Get the current temperature setting. -/// @return The current setting for temp. in degrees celsius. -uint8_t IRHitachiAc::getTemp(void) const { - return reverseBits(_.Temp, 8) >> 1; -} - -/// Set the temperature. -/// @param[in] celsius The temperature in degrees celsius. -void IRHitachiAc::setTemp(const uint8_t celsius) { - uint8_t temp; - if (celsius != 64) _previoustemp = celsius; - switch (celsius) { - case 64: - temp = celsius; - break; - default: - temp = std::min(celsius, kHitachiAcMaxTemp); - temp = std::max(temp, kHitachiAcMinTemp); - } - _.Temp = reverseBits(temp << 1, 8); - if (temp == kHitachiAcMinTemp) - _.raw[9] = 0x90; - else - _.raw[9] = 0x10; -} - -/// Get the current fan speed setting. -/// @return The current fan speed. -uint8_t IRHitachiAc::getFan(void) const { return reverseBits(_.Fan, 8); } - -/// Set the speed of the fan. -/// @param[in] speed The desired setting. -void IRHitachiAc::setFan(const uint8_t speed) { - uint8_t fanmin = kHitachiAcFanAuto; - uint8_t fanmax = kHitachiAcFanHigh; - switch (getMode()) { - case kHitachiAcDry: // Only 2 x low speeds in Dry mode. - fanmin = kHitachiAcFanLow; - fanmax = kHitachiAcFanLow + 1; - break; - case kHitachiAcFan: - fanmin = kHitachiAcFanLow; // No Auto in Fan mode. - break; - } - uint8_t newspeed = std::max(speed, fanmin); - newspeed = std::min(newspeed, fanmax); - _.Fan = reverseBits(newspeed, 8); -} - -/// Get the Vertical Swing setting of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRHitachiAc::getSwingVertical(void) const { - return _.SwingV; -} - -/// Set the Vertical Swing setting of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRHitachiAc::setSwingVertical(const bool on) { - _.SwingV = on; -} - -/// Get the Horizontal Swing setting of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRHitachiAc::getSwingHorizontal(void) const { - return _.SwingH; -} - -/// Set the Horizontal Swing setting of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRHitachiAc::setSwingHorizontal(const bool on) { - _.SwingH = on; -} - -/// 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 IRHitachiAc::convertMode(const stdAc::opmode_t mode) { - switch (mode) { - case stdAc::opmode_t::kCool: return kHitachiAcCool; - case stdAc::opmode_t::kHeat: return kHitachiAcHeat; - case stdAc::opmode_t::kDry: return kHitachiAcDry; - case stdAc::opmode_t::kFan: return kHitachiAcFan; - default: return kHitachiAcAuto; - } -} - -/// 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 IRHitachiAc::convertFan(const stdAc::fanspeed_t speed) { - switch (speed) { - case stdAc::fanspeed_t::kMin: - case stdAc::fanspeed_t::kLow: return kHitachiAcFanLow; - case stdAc::fanspeed_t::kMedium: return kHitachiAcFanLow + 1; - case stdAc::fanspeed_t::kHigh: return kHitachiAcFanHigh - 1; - case stdAc::fanspeed_t::kMax: return kHitachiAcFanHigh; - default: return kHitachiAcFanAuto; - } -} - -/// 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 IRHitachiAc::toCommonMode(const uint8_t mode) { - switch (mode) { - case kHitachiAcCool: return stdAc::opmode_t::kCool; - case kHitachiAcHeat: return stdAc::opmode_t::kHeat; - case kHitachiAcDry: return stdAc::opmode_t::kDry; - case kHitachiAcFan: 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 IRHitachiAc::toCommonFanSpeed(const uint8_t speed) { - switch (speed) { - case kHitachiAcFanHigh: return stdAc::fanspeed_t::kMax; - case kHitachiAcFanHigh - 1: return stdAc::fanspeed_t::kHigh; - case kHitachiAcFanLow + 1: return stdAc::fanspeed_t::kMedium; - case kHitachiAcFanLow: return stdAc::fanspeed_t::kLow; - 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 IRHitachiAc::toCommon(void) const { - stdAc::state_t result; - result.protocol = decode_type_t::HITACHI_AC; - result.model = -1; // No models used. - result.power = _.Power; - result.mode = toCommonMode(getMode()); - result.celsius = true; - result.degrees = getTemp(); - result.fanspeed = toCommonFanSpeed(getFan()); - result.swingv = (_.SwingV ? stdAc::swingv_t::kAuto : stdAc::swingv_t::kOff); - result.swingh = (_.SwingH ? stdAc::swingh_t::kAuto : stdAc::swingh_t::kOff); - // Not supported. - result.quiet = false; - result.turbo = false; - result.clean = false; - result.econo = false; - result.filter = false; - result.light = 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 IRHitachiAc::toString(void) const { - String result = ""; - result.reserve(110); // Reserve some heap for the string to reduce fragging. - result += addBoolToString(_.Power, kPowerStr, false); - result += addModeToString(getMode(), kHitachiAcAuto, kHitachiAcCool, - kHitachiAcHeat, kHitachiAcDry, kHitachiAcFan); - result += addTempToString(getTemp()); - result += addFanToString(getFan(), kHitachiAcFanHigh, kHitachiAcFanLow, - kHitachiAcFanAuto, kHitachiAcFanAuto, - kHitachiAcFanMed); - result += addBoolToString(_.SwingV, kSwingVStr); - result += addBoolToString(_.SwingH, kSwingHStr); - return result; -} - -/// 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? -IRHitachiAc1::IRHitachiAc1(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 IRHitachiAc1::stateReset(void) { - for (uint8_t i = 0; i < kHitachiAc1StateLength; i++) _.raw[i] = 0x00; - // Copy in a known good state. - _.raw[0] = 0xB2; - _.raw[1] = 0xAE; - _.raw[2] = 0x4D; - _.raw[3] = 0x91; - _.raw[4] = 0xF0; - _.raw[5] = 0xE1; - _.raw[6] = 0xA4; - _.raw[11] = 0x61; - _.raw[12] = 0x24; -} - -/// Set up hardware to be able to send a message. -void IRHitachiAc1::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 size/length of the state. -/// @return The calculated checksum value. -uint8_t IRHitachiAc1::calcChecksum(const uint8_t state[], - const uint16_t length) { - uint8_t sum = 0; - for (uint16_t i = kHitachiAc1ChecksumStartByte; i < length - 1; i++) { - sum += reverseBits(GETBITS8(state[i], kLowNibble, kNibbleSize), - kNibbleSize); - sum += reverseBits(GETBITS8(state[i], kHighNibble, kNibbleSize), - kNibbleSize); - } - return reverseBits(sum, 8); -} - -/// Calculate and set the checksum values for the internal state. -/// @param[in] length The size/length of the state. -void IRHitachiAc1::checksum(const uint16_t length) { - _.Sum = calcChecksum(_.raw, 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 IRHitachiAc1::validChecksum(const uint8_t state[], const uint16_t length) { - if (length < 2) return true; // Assume true for lengths that are too short. - return (state[length - 1] == calcChecksum(state, length)); -} - -/// 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 *IRHitachiAc1::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 new_code array. -void IRHitachiAc1::setRaw(const uint8_t new_code[], const uint16_t length) { - std::memcpy(_.raw, new_code, std::min(length, kHitachiAc1StateLength)); -} - -#if SEND_HITACHI_AC -/// Send the current internal state as an IR message. -/// @param[in] repeat Nr. of times the message will be repeated. -void IRHitachiAc1::send(const uint16_t repeat) { - _irsend.sendHitachiAC1(getRaw(), kHitachiAc1StateLength, repeat); - // Clear the toggle bits as we have actioned them by sending them. - setPowerToggle(false); - setSwingToggle(false); -} -#endif // SEND_HITACHI_AC - -/// Get/Detect the model of the A/C. -/// @return The enum of the compatible model. -hitachi_ac1_remote_model_t IRHitachiAc1::getModel(void) const { - switch (_.Model) { - case kHitachiAc1Model_B: return hitachi_ac1_remote_model_t::R_LT0541_HTA_B; - default: return hitachi_ac1_remote_model_t::R_LT0541_HTA_A; - } -} - -/// Set the model of the A/C to emulate. -/// @param[in] model The enum of the appropriate model. -void IRHitachiAc1::setModel(const hitachi_ac1_remote_model_t model) { - uint8_t value = 0; - switch (model) { - case hitachi_ac1_remote_model_t::R_LT0541_HTA_B: - value = kHitachiAc1Model_B; - break; - default: - value = kHitachiAc1Model_A; // i.e. 'A' mode. - } - _.Model = value; -} - -/// Get the value of the current power setting. -/// @return true, the setting is on. false, the setting is off. -bool IRHitachiAc1::getPower(void) const { - return _.Power; -} - -/// Change the power setting. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRHitachiAc1::setPower(const bool on) { - // If the power changes, set the power toggle bit. - if (on != _.Power) setPowerToggle(true); - _.Power = on; -} - -/// Get the value of the current power toggle setting. -/// @return true, the setting is on. false, the setting is off. -bool IRHitachiAc1::getPowerToggle(void) const { - return _.PowerToggle; -} - -/// Change the power toggle setting. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRHitachiAc1::setPowerToggle(const bool on) { - _.PowerToggle = on; -} - -/// Change the power setting to On. -void IRHitachiAc1::on(void) { setPower(true); } - -/// Change the power setting to Off. -void IRHitachiAc1::off(void) { setPower(false); } - -/// Get the operating mode setting of the A/C. -/// @return The current operating mode setting. -uint8_t IRHitachiAc1::getMode(void) const { - return _.Mode; -} - -/// Set the operating mode of the A/C. -/// @param[in] mode The desired operating mode. -void IRHitachiAc1::setMode(const uint8_t mode) { - switch (mode) { - case kHitachiAc1Auto: - setTemp(kHitachiAc1TempAuto); - // FALL THRU - case kHitachiAc1Fan: - case kHitachiAc1Heat: - case kHitachiAc1Cool: - case kHitachiAc1Dry: - _.Mode = mode; - break; - default: - setTemp(kHitachiAc1TempAuto); - _.Mode = kHitachiAc1Auto; - break; - } - setSleep(_.Sleep); // Correct the sleep mode if required. - setFan(_.Fan); // Correct the fan speed if required. -} - -/// Get the current temperature setting. -/// @return The current setting for temp. in degrees celsius. -uint8_t IRHitachiAc1::getTemp(void) const { - return reverseBits(_.Temp, kHitachiAc1TempSize) + kHitachiAc1TempDelta; -} - -/// Set the temperature. -/// @param[in] celsius The temperature in degrees celsius. -void IRHitachiAc1::setTemp(const uint8_t celsius) { - if (_.Mode == kHitachiAc1Auto) return; // Can't change temp in Auto mode. - uint8_t temp = std::min(celsius, kHitachiAcMaxTemp); - temp = std::max(temp, kHitachiAcMinTemp); - temp -= kHitachiAc1TempDelta; - temp = reverseBits(temp, kHitachiAc1TempSize); - _.Temp = temp; -} - -/// Get the current fan speed setting. -/// @return The current fan speed. -uint8_t IRHitachiAc1::getFan(void) const { - return _.Fan; -} - -/// Set the speed of the fan. -/// @param[in] speed The desired setting. -/// @param[in] force Deprecated -void IRHitachiAc1::setFan(const uint8_t speed, const bool /*force*/) { - // restrictions - switch (_.Mode) { - case kHitachiAc1Dry: - _.Fan = kHitachiAc1FanLow; // Dry is locked to Low speed. - return; - case kHitachiAc1Auto: - _.Fan = kHitachiAc1FanAuto; // Auto is locked to Auto speed. - return; - case kHitachiAc1Heat: - case kHitachiAc1Fan: // Auto speed not allowed in these modes. - if (speed == kHitachiAc1FanAuto || _.Fan == kHitachiAc1FanAuto) - _.Fan = kHitachiAc1FanLow; - return; - } - - switch (speed) { - case kHitachiAc1FanAuto: - case kHitachiAc1FanHigh: - case kHitachiAc1FanMed: - case kHitachiAc1FanLow: - _.Fan = speed; - break; - default: _.Fan = kHitachiAc1FanAuto; - } -} - -/// Get the Swing Toggle setting of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRHitachiAc1::getSwingToggle(void) const { - return _.SwingToggle; -} - -/// Set the Swing toggle setting of the A/C. -/// @param[in] toggle true, the setting is on. false, the setting is off. -void IRHitachiAc1::setSwingToggle(const bool toggle) { - _.SwingToggle = toggle; -} - -/// Get the Vertical Swing setting of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRHitachiAc1::getSwingV(void) const { - return _.SwingV; -} - -/// Set the Vertical Swing setting of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRHitachiAc1::setSwingV(const bool on) { - _.SwingV = on; -} - -/// Get the Horizontal Swing setting of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRHitachiAc1::getSwingH(void) const { - return _.SwingH; -} - -/// Set the Horizontal Swing setting of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRHitachiAc1::setSwingH(const bool on) { - _.SwingH = on; -} - -/// Get the Sleep setting of the A/C. -/// @return The currently configured sleep mode. -/// @note Sleep modes only available in Auto & Cool modes, otherwise it's off. -uint8_t IRHitachiAc1::getSleep(void) const { - return _.Sleep; -} - -/// Set the Sleep setting of the A/C. -/// @param[in] mode The mode of sleep to set the A/C to. -/// @note Sleep modes only available in Auto & Cool modes, otherwise it's off. -void IRHitachiAc1::setSleep(const uint8_t mode) { - switch (_.Mode) { - case kHitachiAc1Auto: - case kHitachiAc1Cool: - _.Sleep = std::min(mode, kHitachiAc1Sleep4); - break; - default: - _.Sleep = kHitachiAc1SleepOff; - } -} - -/// Set the On Timer time. -/// @param[in] mins The time expressed in total number of minutes. -void IRHitachiAc1::setOnTimer(const uint16_t mins) { - const uint16_t mins_lsb = reverseBits(mins, kHitachiAc1TimerSize); - _.OnTimerLow = GETBITS16(mins_lsb, 8, 8); - _.OnTimerHigh = GETBITS16(mins_lsb, 0, 8); -} - -/// Get the On Timer vtime of the A/C. -/// @return Nr of minutes the timer is set to. -uint16_t IRHitachiAc1::getOnTimer(void) const { - return reverseBits( - (_.OnTimerLow << 8) | _.OnTimerHigh, kHitachiAc1TimerSize); -} - -/// Set the Off Timer time. -/// @param[in] mins The time expressed in total number of minutes. -void IRHitachiAc1::setOffTimer(const uint16_t mins) { - const uint16_t mins_lsb = reverseBits(mins, kHitachiAc1TimerSize); - _.OffTimerLow = GETBITS16(mins_lsb, 8, 8); - _.OffTimerHigh = GETBITS16(mins_lsb, 0, 8); -} - -/// Get the Off Timer vtime of the A/C. -/// @return Nr of minutes the timer is set to. -uint16_t IRHitachiAc1::getOffTimer(void) const { - return reverseBits( - (_.OffTimerLow << 8) | _.OffTimerHigh, kHitachiAc1TimerSize); -} - -/// 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 IRHitachiAc1::convertMode(const stdAc::opmode_t mode) { - switch (mode) { - case stdAc::opmode_t::kCool: return kHitachiAc1Cool; - case stdAc::opmode_t::kHeat: return kHitachiAc1Heat; - case stdAc::opmode_t::kDry: return kHitachiAc1Dry; - case stdAc::opmode_t::kFan: return kHitachiAc1Fan; - default: return kHitachiAc1Auto; - } -} - -/// 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 IRHitachiAc1::convertFan(const stdAc::fanspeed_t speed) { - switch (speed) { - case stdAc::fanspeed_t::kMin: - case stdAc::fanspeed_t::kLow: return kHitachiAc1FanLow; - case stdAc::fanspeed_t::kMedium: return kHitachiAc1FanMed; - case stdAc::fanspeed_t::kHigh: - case stdAc::fanspeed_t::kMax: return kHitachiAc1FanHigh; - default: return kHitachiAc1FanAuto; - } -} - -/// 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 IRHitachiAc1::toCommonMode(const uint8_t mode) { - switch (mode) { - case kHitachiAc1Cool: return stdAc::opmode_t::kCool; - case kHitachiAc1Heat: return stdAc::opmode_t::kHeat; - case kHitachiAc1Dry: return stdAc::opmode_t::kDry; - case kHitachiAc1Fan: 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 IRHitachiAc1::toCommonFanSpeed(const uint8_t speed) { - switch (speed) { - case kHitachiAc1FanHigh: return stdAc::fanspeed_t::kMax; - case kHitachiAc1FanMed: return stdAc::fanspeed_t::kMedium; - case kHitachiAc1FanLow: return stdAc::fanspeed_t::kLow; - 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 IRHitachiAc1::toCommon(void) const { - stdAc::state_t result; - result.protocol = decode_type_t::HITACHI_AC1; - result.model = getModel(); - 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.sleep = _.Sleep ? 0 : -1; - // Not supported. - result.quiet = false; - result.turbo = false; - result.clean = false; - result.econo = false; - result.filter = false; - result.light = 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 IRHitachiAc1::toString(void) const { - String result = ""; - result.reserve(170); // Reserve some heap for the string to reduce fragging. - result += addModelToString(decode_type_t::HITACHI_AC1, getModel(), false); - result += addBoolToString(_.Power, kPowerStr); - result += addBoolToString(_.PowerToggle, kPowerToggleStr); - result += addModeToString(_.Mode, kHitachiAc1Auto, kHitachiAc1Cool, - kHitachiAc1Heat, kHitachiAc1Dry, kHitachiAc1Fan); - result += addTempToString(getTemp()); - result += addFanToString(_.Fan, kHitachiAc1FanHigh, kHitachiAc1FanLow, - kHitachiAc1FanAuto, kHitachiAc1FanAuto, - kHitachiAc1FanMed); - result += addBoolToString(_.SwingToggle, kSwingVToggleStr); - result += addBoolToString(_.SwingV, kSwingVStr); - result += addBoolToString(_.SwingH, kSwingHStr); - result += addLabeledString(_.Sleep ? uint64ToString(_.Sleep) : kOffStr, - kSleepStr); - result += addLabeledString(getOnTimer() ? minsToString(getOnTimer()) - : kOffStr, - kOnTimerStr); - result += addLabeledString(getOffTimer() ? minsToString(getOffTimer()) - : kOffStr, - kOffTimerStr); - return result; -} - -#if (DECODE_HITACHI_AC || DECODE_HITACHI_AC1 || DECODE_HITACHI_AC2 || \ - DECODE_HITACHI_AC344) -/// Decode the supplied Hitachi A/C message. -/// Status: STABLE / Expected 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. -/// Typically kHitachiAcBits, kHitachiAc1Bits, kHitachiAc2Bits, -/// kHitachiAc344Bits -/// @param[in] strict Flag indicating if we should perform strict matching. -/// @param[in] MSBfirst Is the data per byte stored in MSB First (true) or -/// LSB First order(false)? -/// @return True if it can decode it, false if it can't. -/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/417 -/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/453 -/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1134 -bool IRrecv::decodeHitachiAC(decode_results *results, uint16_t offset, - const uint16_t nbits, const bool strict, - const bool MSBfirst) { - const uint8_t k_tolerance = _tolerance + 5; - - if (strict) { - switch (nbits) { - case kHitachiAcBits: - case kHitachiAc1Bits: - case kHitachiAc2Bits: - case kHitachiAc344Bits: - break; // Okay to continue. - default: - return false; // Not strictly a Hitachi message. - } - } - uint16_t hmark; - uint32_t hspace; - if (nbits == kHitachiAc1Bits) { - hmark = kHitachiAc1HdrMark; - hspace = kHitachiAc1HdrSpace; - } else { - hmark = kHitachiAcHdrMark; - hspace = kHitachiAcHdrSpace; - } - // Match Header + Data + Footer - if (!matchGeneric(results->rawbuf + offset, results->state, - results->rawlen - offset, nbits, - hmark, hspace, - kHitachiAcBitMark, kHitachiAcOneSpace, - kHitachiAcBitMark, kHitachiAcZeroSpace, - kHitachiAcBitMark, kHitachiAcMinGap, true, - k_tolerance, kMarkExcess, MSBfirst)) return false; - - // Compliance - if (strict) { - if (nbits / 8 == kHitachiAcStateLength && - !IRHitachiAc::validChecksum(results->state, kHitachiAcStateLength)) - return false; - if (nbits / 8 == kHitachiAc1StateLength && - !IRHitachiAc1::validChecksum(results->state, kHitachiAc1StateLength)) - return false; - if (nbits / 8 == kHitachiAc344StateLength && - !IRHitachiAc3::hasInvertedStates(results->state, - kHitachiAc344StateLength)) - return false; - } - - // Success - switch (nbits) { - case kHitachiAc1Bits: - results->decode_type = decode_type_t::HITACHI_AC1; - break; - case kHitachiAc2Bits: - results->decode_type = decode_type_t::HITACHI_AC2; - break; - case kHitachiAc344Bits: - results->decode_type = decode_type_t::HITACHI_AC344; - break; - case kHitachiAcBits: - default: - results->decode_type = decode_type_t::HITACHI_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_HITACHI_AC || DECODE_HITACHI_AC1 || DECODE_HITACHI_AC2 || - // DECODE_HITACHI_AC344) - -#if SEND_HITACHI_AC424 -/// Send a Hitachi 53-byte/424-bit A/C formatted message. (HITACHI_AC424) -/// 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. -/// @note This protocol is almost exactly the same as HitachiAC2 except this -/// variant has a leader section as well, and subtle timing differences. -/// It is also in LSBF order (per byte), rather than MSBF order. -void IRsend::sendHitachiAc424(const uint8_t data[], const uint16_t nbytes, - const uint16_t repeat) { - enableIROut(kHitachiAcFreq); - for (uint16_t r = 0; r <= repeat; r++) { - // Leader - mark(kHitachiAc424LdrMark); - space(kHitachiAc424LdrSpace); - // Header + Data + Footer - sendGeneric(kHitachiAc424HdrMark, kHitachiAc424HdrSpace, - kHitachiAc424BitMark, kHitachiAc424OneSpace, - kHitachiAc424BitMark, kHitachiAc424ZeroSpace, - kHitachiAc424BitMark, kHitachiAcMinGap, - data, nbytes, // Bytes - kHitachiAcFreq, false, kNoRepeat, kDutyDefault); - } -} -#endif // SEND_HITACHI_AC424 - -#if DECODE_HITACHI_AC424 -/// Decode the supplied Hitachi 53-byte/424-bit A/C message. -/// Status: STABLE / Reported as 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 This protocol is almost exactly the same as HitachiAC2 except this -/// variant has a leader section as well, and subtle timing differences. -/// It is also in LSBF order (per byte), rather than MSBF order. -/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/973 -/// @see (Japanese Manual) https://kadenfan.hitachi.co.jp/support/raj/item/docs/ras_aj22h_a_tori.pdf -bool IRrecv::decodeHitachiAc424(decode_results *results, uint16_t offset, - const uint16_t nbits, - const bool strict) { - if (results->rawlen < 2 * nbits + kHeader + kHeader + kFooter - 1 + offset) - return false; // Too short a message to match. - if (strict && nbits != kHitachiAc424Bits) - return false; - - uint16_t used; - - // Leader - if (!matchMark(results->rawbuf[offset++], kHitachiAc424LdrMark)) - return false; - if (!matchSpace(results->rawbuf[offset++], kHitachiAc424LdrSpace)) - return false; - - // Header + Data + Footer - used = matchGeneric(results->rawbuf + offset, results->state, - results->rawlen - offset, nbits, - kHitachiAc424HdrMark, kHitachiAc424HdrSpace, - kHitachiAc424BitMark, kHitachiAc424OneSpace, - kHitachiAc424BitMark, kHitachiAc424ZeroSpace, - kHitachiAc424BitMark, kHitachiAcMinGap, true, - kUseDefTol, 0, false); - if (used == 0) return false; // We failed to find any data. - - // Success - results->decode_type = decode_type_t::HITACHI_AC424; - results->bits = nbits; - return true; -} -#endif // DECODE_HITACHI_AC424 - -/// 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? -IRHitachiAc424::IRHitachiAc424(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 Reset to auto fan, cooling, 23° Celsius -void IRHitachiAc424::stateReset(void) { - for (uint8_t i = 0; i < kHitachiAc424StateLength; i++) - _.raw[i] = 0x00; - - _.raw[0] = 0x01; - _.raw[1] = 0x10; - _.raw[3] = 0x40; - _.raw[5] = 0xFF; - _.raw[7] = 0xCC; - _.raw[33] = 0x80; - _.raw[35] = 0x03; - _.raw[37] = 0x01; - _.raw[39] = 0x88; - _.raw[45] = 0xFF; - _.raw[47] = 0xFF; - _.raw[49] = 0xFF; - _.raw[51] = 0xFF; - - setTemp(23); - setPower(true); - setMode(kHitachiAc424Cool); - setFan(kHitachiAc424FanAuto); -} - -/// Update the internal consistency check for the protocol. -void IRHitachiAc424::setInvertedStates(void) { - invertBytePairs(_.raw + 3, kHitachiAc424StateLength - 3); -} - -/// Set up hardware to be able to send a message. -void IRHitachiAc424::begin(void) { _irsend.begin(); } - -/// 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 *IRHitachiAc424::getRaw(void) { - setInvertedStates(); - 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 new_code array. -void IRHitachiAc424::setRaw(const uint8_t new_code[], const uint16_t length) { - std::memcpy(_.raw, new_code, std::min(length, kHitachiAc424StateLength)); -} - -#if SEND_HITACHI_AC424 -/// Send the current internal state as an IR message. -/// @param[in] repeat Nr. of times the message will be repeated. -void IRHitachiAc424::send(const uint16_t repeat) { - _irsend.sendHitachiAc424(getRaw(), kHitachiAc424StateLength, repeat); -} -#endif // SEND_HITACHI_AC424 - -/// Get the value of the current power setting. -/// @return true, the setting is on. false, the setting is off. -bool IRHitachiAc424::getPower(void) const { - return _.Power == kHitachiAc424PowerOn; -} - -/// Change the power setting. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRHitachiAc424::setPower(const bool on) { - setButton(kHitachiAc424ButtonPowerMode); - _.Power = (on ? kHitachiAc424PowerOn : kHitachiAc424PowerOff); -} - -/// Change the power setting to On. -void IRHitachiAc424::on(void) { setPower(true); } - -/// Change the power setting to Off. -void IRHitachiAc424::off(void) { setPower(false); } - -/// Get the operating mode setting of the A/C. -/// @return The current operating mode setting. -uint8_t IRHitachiAc424::getMode(void) const { - return _.Mode; -} - -/// Set the operating mode of the A/C. -/// @param[in] mode The desired operating mode. -void IRHitachiAc424::setMode(const uint8_t mode) { - uint8_t newMode = mode; - switch (mode) { - // Fan mode sets a special temp. - case kHitachiAc424Fan: setTemp(kHitachiAc424FanTemp, false); break; - case kHitachiAc424Heat: - case kHitachiAc424Cool: - case kHitachiAc424Dry: break; - default: newMode = kHitachiAc424Cool; - } - _.Mode = newMode; - if (newMode != kHitachiAc424Fan) setTemp(_previoustemp); - setFan(_.Fan); // Reset the fan speed after the mode change. - setButton(kHitachiAc424ButtonPowerMode); -} - -/// Get the current temperature setting. -/// @return The current setting for temp. in degrees celsius. -uint8_t IRHitachiAc424::getTemp(void) const { - return _.Temp; -} - -/// Set the temperature. -/// @param[in] celsius The temperature in degrees celsius. -/// @param[in] setPrevious true, remember this if we change mode. false, don't. -void IRHitachiAc424::setTemp(const uint8_t celsius, bool setPrevious) { - uint8_t temp; - temp = std::min(celsius, kHitachiAc424MaxTemp); - temp = std::max(temp, kHitachiAc424MinTemp); - _.Temp = temp; - if (_previoustemp > temp) - setButton(kHitachiAc424ButtonTempDown); - else if (_previoustemp < temp) - setButton(kHitachiAc424ButtonTempUp); - if (setPrevious) _previoustemp = temp; -} - -/// Get the current fan speed setting. -/// @return The current fan speed. -uint8_t IRHitachiAc424::getFan(void) const { - return _.Fan; -} - -/// Set the speed of the fan. -/// @param[in] speed The desired setting. -void IRHitachiAc424::setFan(const uint8_t speed) { - uint8_t newSpeed = std::max(speed, kHitachiAc424FanMin); - uint8_t fanMax = kHitachiAc424FanMax; - - // Only 2 x low speeds in Dry mode or Auto - if (_.Mode == kHitachiAc424Dry && speed == kHitachiAc424FanAuto) { - fanMax = kHitachiAc424FanAuto; - } else if (_.Mode == kHitachiAc424Dry) { - fanMax = kHitachiAc424FanMaxDry; - } else if (_.Mode == kHitachiAc424Fan && speed == kHitachiAc424FanAuto) { - // Fan Mode does not have auto. Set to safe low - newSpeed = kHitachiAc424FanMin; - } - - newSpeed = std::min(newSpeed, fanMax); - // Handle the setting the button value if we are going to change the value. - if (newSpeed != _.Fan) setButton(kHitachiAc424ButtonFan); - // Set the values - _.Fan = newSpeed; - _.raw[9] = 0x92; - _.raw[29] = 0x00; - - // When fan is at min/max, additional bytes seem to be set - if (newSpeed == kHitachiAc424FanMin) _.raw[9] = 0x98; - if (newSpeed == kHitachiAc424FanMax) { - _.raw[9] = 0xA9; - _.raw[29] = 0x30; - } -} - -/// Get the Button/Command setting of the A/C. -/// @return The value of the button/command that was pressed. -uint8_t IRHitachiAc424::getButton(void) const { - return _.Button; -} - -/// Set the Button/Command pressed setting of the A/C. -/// @param[in] button The value of the button/command that was pressed. -void IRHitachiAc424::setButton(const uint8_t button) { - _.Button = button; -} - -/// Set the Vertical Swing toggle setting of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -/// @note The remote does not keep state of the vertical swing. -/// A byte is sent indicating the swing button is pressed on the remote -void IRHitachiAc424::setSwingVToggle(const bool on) { - uint8_t button = _.Button; // Get the current button value. - if (on) - button = kHitachiAc424ButtonSwingV; // Set the button to SwingV. - else if (button == kHitachiAc424ButtonSwingV) // Asked to unset it - // It was set previous, so use Power as a default - button = kHitachiAc424ButtonPowerMode; - setButton(button); -} - -/// Get the Vertical Swing toggle setting of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRHitachiAc424::getSwingVToggle(void) const { - return _.Button == kHitachiAc424ButtonSwingV; -} - -/// 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 IRHitachiAc424::convertMode(const stdAc::opmode_t mode) { - switch (mode) { - case stdAc::opmode_t::kCool: return kHitachiAc424Cool; - case stdAc::opmode_t::kHeat: return kHitachiAc424Heat; - case stdAc::opmode_t::kDry: return kHitachiAc424Dry; - case stdAc::opmode_t::kFan: return kHitachiAc424Fan; - default: return kHitachiAc424Cool; - } -} - -/// 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 IRHitachiAc424::convertFan(const stdAc::fanspeed_t speed) { - switch (speed) { - case stdAc::fanspeed_t::kMin: return kHitachiAc424FanMin; - case stdAc::fanspeed_t::kLow: return kHitachiAc424FanLow; - case stdAc::fanspeed_t::kMedium: return kHitachiAc424FanMedium; - case stdAc::fanspeed_t::kHigh: return kHitachiAc424FanHigh; - case stdAc::fanspeed_t::kMax: return kHitachiAc424FanMax; - default: return kHitachiAc424FanAuto; - } -} - -/// 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 IRHitachiAc424::toCommonMode(const uint8_t mode) { - switch (mode) { - case kHitachiAc424Cool: return stdAc::opmode_t::kCool; - case kHitachiAc424Heat: return stdAc::opmode_t::kHeat; - case kHitachiAc424Dry: return stdAc::opmode_t::kDry; - case kHitachiAc424Fan: 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. -/// @return The stdAc equivalent of the native setting. -stdAc::fanspeed_t IRHitachiAc424::toCommonFanSpeed(const uint8_t speed) { - switch (speed) { - case kHitachiAc424FanMax: return stdAc::fanspeed_t::kMax; - case kHitachiAc424FanHigh: return stdAc::fanspeed_t::kHigh; - case kHitachiAc424FanMedium: return stdAc::fanspeed_t::kMedium; - case kHitachiAc424FanLow: return stdAc::fanspeed_t::kLow; - case kHitachiAc424FanMin: 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 IRHitachiAc424::toCommon(void) const { - stdAc::state_t result; - result.protocol = decode_type_t::HITACHI_AC424; - result.model = -1; // No models used. - result.power = getPower(); - result.mode = toCommonMode(_.Mode); - result.celsius = true; - result.degrees = _.Temp; - result.fanspeed = toCommonFanSpeed(_.Fan); - result.swingv = getSwingVToggle() ? stdAc::swingv_t::kAuto - : stdAc::swingv_t::kOff; - // Not supported. - result.swingh = stdAc::swingh_t::kOff; - result.quiet = false; - result.turbo = false; - result.clean = false; - result.econo = false; - result.filter = false; - result.light = false; - result.beep = false; - result.sleep = -1; - result.clock = -1; - return result; -} - -/// Convert the internal state into a human readable string for the settings -/// that are common to protocols of this nature. -/// @return A string containing the common settings in human-readable form. -String IRHitachiAc424::_toString(void) const { - String result = ""; - result.reserve(100); // Reserve some heap for the string to reduce fragging. - result += addBoolToString(getPower(), kPowerStr, false); - result += addModeToString(_.Mode, 0, kHitachiAc424Cool, - kHitachiAc424Heat, kHitachiAc424Dry, - kHitachiAc424Fan); - result += addTempToString(_.Temp); - result += addIntToString(_.Fan, kFanStr); - result += kSpaceLBraceStr; - switch (_.Fan) { - case kHitachiAc424FanAuto: result += kAutoStr; break; - case kHitachiAc424FanMax: result += kMaxStr; break; - case kHitachiAc424FanHigh: result += kHighStr; break; - case kHitachiAc424FanMedium: result += kMedStr; break; - case kHitachiAc424FanLow: result += kLowStr; break; - case kHitachiAc424FanMin: result += kMinStr; break; - default: result += kUnknownStr; - } - result += ')'; - result += addIntToString(_.Button, kButtonStr); - result += kSpaceLBraceStr; - switch (_.Button) { - case kHitachiAc424ButtonPowerMode: - result += kPowerStr; - result += '/'; - result += kModeStr; - break; - case kHitachiAc424ButtonFan: result += kFanStr; break; - case kHitachiAc424ButtonSwingV: result += kSwingVStr; break; - case kHitachiAc344ButtonSwingH: result += kSwingHStr; break; - case kHitachiAc424ButtonTempDown: result += kTempDownStr; break; - case kHitachiAc424ButtonTempUp: result += kTempUpStr; break; - default: result += kUnknownStr; - } - result += ')'; - return result; -} - -/// Convert the internal state into a human readable string. -/// @return A string containing the settings in human-readable form. -String IRHitachiAc424::toString(void) const { - return _toString() + addBoolToString(getSwingVToggle(), kSwingVToggleStr); -} - - -#if SEND_HITACHI_AC3 -/// Send a Hitachi(3) A/C formatted message. (HITACHI_AC3) -/// Status: STABLE / Working fine. -/// @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 almost exactly the same as HitachiAC424 except this -/// variant has subtle timing differences. There are five(5) typical sizes: -/// kHitachiAc3MinStateLength (Cancel Timer), -/// kHitachiAc3MinStateLength + 2 (Change Temp), -/// kHitachiAc3StateLength - 6 (Change Mode), -/// kHitachiAc3StateLength - 4 (Normal), & -/// kHitachiAc3StateLength (Set Timer) -void IRsend::sendHitachiAc3(const uint8_t data[], const uint16_t nbytes, - const uint16_t repeat) { - // Header + Data + Footer - sendGeneric(kHitachiAc3HdrMark, kHitachiAc3HdrSpace, - kHitachiAc3BitMark, kHitachiAc3OneSpace, - kHitachiAc3BitMark, kHitachiAc3ZeroSpace, - kHitachiAc3BitMark, kHitachiAcMinGap, - data, nbytes, // Bytes - kHitachiAcFreq, false, repeat, kDutyDefault); -} -#endif // SEND_HITACHI_AC3 - - -/// 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? -IRHitachiAc3::IRHitachiAc3(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 Reset to auto fan, cooling, 23° Celsius -void IRHitachiAc3::stateReset(void) { - for (uint8_t i = 0; i < kHitachiAc3StateLength; i++) - remote_state[i] = 0x00; - remote_state[0] = 0x01; - remote_state[1] = 0x10; - remote_state[3] = 0x40; - remote_state[5] = 0xFF; - remote_state[7] = 0xE8; - remote_state[9] = 0x89; - remote_state[11] = 0x0B; - remote_state[13] = 0x3F; - remote_state[15] = 0x15; - remote_state[21] = 0x4B; - remote_state[23] = 0x18; - setInvertedStates(); -} - -/// Invert every second byte of the internal state, after the fixed header. -/// @param[in] length The size of the state array. -/// @note This is this protocols integrity check. -void IRHitachiAc3::setInvertedStates(const uint16_t length) { - if (length > 3) invertBytePairs(remote_state + 3, length - 3); -} - -/// Check if every second byte of the state, after the fixed header -/// is inverted to the previous byte. -/// @param[in] state The state array to be checked. -/// @param[in] length The size of the state array. -/// @note This is this protocols integrity check. -bool IRHitachiAc3::hasInvertedStates(const uint8_t state[], - const uint16_t length) { - return (length <= 3 || checkInvertedBytePairs(state + 3, length - 3)); -} - -/// Set up hardware to be able to send a message. -void IRHitachiAc3::begin(void) { _irsend.begin(); } - -/// 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 *IRHitachiAc3::getRaw(void) { - setInvertedStates(); - return remote_state; -} - -/// 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 new_code array. -void IRHitachiAc3::setRaw(const uint8_t new_code[], const uint16_t length) { - memcpy(remote_state, new_code, std::min(length, kHitachiAc3StateLength)); -} - -#if DECODE_HITACHI_AC3 -/// Decode the supplied Hitachi 15to27-byte/120to216-bit A/C message. -/// Status: STABLE / Works fine. -/// @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 almost exactly the same as HitachiAC424 except this -/// variant has subtle timing differences and multiple lengths. -/// There are five(5) typical lengths: -/// kHitachiAc3MinStateLength (Cancel Timer), -/// kHitachiAc3MinStateLength + 2 (Change Temp), -/// kHitachiAc3StateLength - 6 (Change Mode), -/// kHitachiAc3StateLength - 4 (Normal), & -/// kHitachiAc3StateLength (Set Timer) -/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1060 -bool IRrecv::decodeHitachiAc3(decode_results *results, uint16_t offset, - const uint16_t nbits, - const bool strict) { - if (results->rawlen < 2 * nbits + kHeader + kFooter - 1 + offset) - return false; // Too short a message to match. - if (strict) { - // Check the requested bit length. - switch (nbits) { - case kHitachiAc3MinBits: // Cancel Timer (Min Size) - case kHitachiAc3MinBits + 2 * 8: // Change Temp - case kHitachiAc3Bits - 6 * 8: // Change Mode - case kHitachiAc3Bits - 4 * 8: // Normal - case kHitachiAc3Bits: // Set Temp (Max Size) - break; - default: return false; - } - } - - // Header + Data + Footer - if (!matchGeneric(results->rawbuf + offset, results->state, - results->rawlen - offset, nbits, - kHitachiAc3HdrMark, kHitachiAc3HdrSpace, - kHitachiAc3BitMark, kHitachiAc3OneSpace, - kHitachiAc3BitMark, kHitachiAc3ZeroSpace, - kHitachiAc3BitMark, kHitachiAcMinGap, true, - kUseDefTol, 0, false)) - return false; // We failed to find any data. - - // Compliance - if (strict && !IRHitachiAc3::hasInvertedStates(results->state, nbits / 8)) - return false; - // Success - results->decode_type = decode_type_t::HITACHI_AC3; - results->bits = nbits; - return true; -} -#endif // DECODE_HITACHI_AC3 - -/// Class constructor for handling detailed Hitachi_AC344 43 byte 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? -IRHitachiAc344::IRHitachiAc344(const uint16_t pin, const bool inverted, - const bool use_modulation) - : IRHitachiAc424(pin, inverted, use_modulation) { stateReset(); } - -/// Reset the internal state to auto fan, cooling, 23° Celsius -void IRHitachiAc344::stateReset(void) { - IRHitachiAc424::stateReset(); - _.raw[37] = 0x00; - _.raw[39] = 0x00; -} - -#if SEND_HITACHI_AC344 -/// Create and send the IR message to the A/C. -/// @param[in] repeat Nr. of times to repeat the message. -void IRHitachiAc344::send(const uint16_t repeat) { - _irsend.sendHitachiAc344(getRaw(), kHitachiAc344StateLength, repeat); -} -#endif // SEND_HITACHI_AC344 - -/// 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 (in bytes) of the code for this protocol. -void IRHitachiAc344::setRaw(const uint8_t new_code[], const uint16_t length) { - memcpy(_.raw, new_code, std::min(length, kHitachiAc344StateLength)); -} - -/// Control the vertical swing setting. -/// @param[in] on True, turns on the feature. False, turns off the feature. -void IRHitachiAc344::setSwingV(const bool on) { - setSwingVToggle(on); // Set the button value. - _.SwingV = on; -} - -/// Get the current vertical swing setting. -/// @return True, if the setting is on. False, it is off. -bool IRHitachiAc344::getSwingV(void) const { - return _.SwingV; -} - -/// Control the horizontal swing setting. -/// @param[in] position The position to set the horizontal swing to. -void IRHitachiAc344::setSwingH(const uint8_t position) { - if (position > kHitachiAc344SwingHLeftMax) - _.SwingH = kHitachiAc344SwingHMiddle; - else - _.SwingH = position; - setButton(kHitachiAc344ButtonSwingH); -} - -/// Get the current horizontal swing setting. -/// @return The current position horizontal swing is set to. -uint8_t IRHitachiAc344::getSwingH(void) const { - return _.SwingH; -} - -/// Convert a standard A/C horizontal swing into its native setting. -/// @param[in] position A stdAc::swingh_t position to convert. -/// @return The equivilent native horizontal swing position. -uint8_t IRHitachiAc344::convertSwingH(const stdAc::swingh_t position) { - switch (position) { - case stdAc::swingh_t::kAuto: return kHitachiAc344SwingHAuto; - case stdAc::swingh_t::kLeftMax: return kHitachiAc344SwingHLeftMax; - case stdAc::swingh_t::kLeft: return kHitachiAc344SwingHLeft; - case stdAc::swingh_t::kRight: return kHitachiAc344SwingHRight; - case stdAc::swingh_t::kRightMax: return kHitachiAc344SwingHRightMax; - default: return kHitachiAc344SwingHMiddle; - } -} - -/// Convert a native horizontal swing postion to it's common equivalent. -/// @param[in] pos A native position to convert. -/// @return The common horizontal swing position. -stdAc::swingh_t IRHitachiAc344::toCommonSwingH(const uint8_t pos) { - switch (pos) { - case kHitachiAc344SwingHLeftMax: return stdAc::swingh_t::kLeftMax; - case kHitachiAc344SwingHLeft: return stdAc::swingh_t::kLeft; - case kHitachiAc344SwingHRight: return stdAc::swingh_t::kRight; - case kHitachiAc344SwingHRightMax: return stdAc::swingh_t::kRightMax; - case kHitachiAc344SwingHAuto: return stdAc::swingh_t::kAuto; - 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 IRHitachiAc344::toCommon(void) const { - stdAc::state_t result = IRHitachiAc424::toCommon(); - result.protocol = decode_type_t::HITACHI_AC344; - result.swingv = _.SwingV ? stdAc::swingv_t::kAuto : stdAc::swingv_t::kOff; - result.swingh = toCommonSwingH(_.SwingH); - return result; -} - -/// Convert the internal state into a human readable string. -/// @return A string containing the settings in human-readable form. -String IRHitachiAc344::toString(void) const { - String result; - result.reserve(120); // Reserve some heap for the string to reduce fragging. - result += _toString(); - result += addBoolToString(_.SwingV, kSwingVStr); - result += addIntToString(_.SwingH, kSwingHStr); - result += kSpaceLBraceStr; - switch (_.SwingH) { - case kHitachiAc344SwingHLeftMax: result += kLeftMaxStr; break; - case kHitachiAc344SwingHLeft: result += kLeftStr; break; - case kHitachiAc344SwingHMiddle: result += kMiddleStr; break; - case kHitachiAc344SwingHRight: result += kRightStr; break; - case kHitachiAc344SwingHRightMax: result += kRightMaxStr; break; - case kHitachiAc344SwingHAuto: result += kAutoStr; break; - default: result += kUnknownStr; - } - result += ')'; - return result; -} diff --git a/lib/IRremoteESP8266/src/ir_Hitachi.h b/lib/IRremoteESP8266/src/ir_Hitachi.h index 32167596f2..88dae3f9aa 100644 --- a/lib/IRremoteESP8266/src/ir_Hitachi.h +++ b/lib/IRremoteESP8266/src/ir_Hitachi.h @@ -28,10 +28,10 @@ #ifndef UNIT_TEST #include #endif -#include "IRremoteESP8266.h" -#include "IRsend.h" +#include "../src/IRremoteESP8266.h" +#include "../src/IRsend.h" #ifdef UNIT_TEST -#include "IRsend_test.h" +#include "../src/IRsend_test.h" #endif /// Native representation of a Hitachi 224-bit A/C message. diff --git a/lib/IRremoteESP8266/src/ir_Inax.cpp b/lib/IRremoteESP8266/src/ir_Inax.cpp deleted file mode 100644 index bb68ff30d7..0000000000 --- a/lib/IRremoteESP8266/src/ir_Inax.cpp +++ /dev/null @@ -1,73 +0,0 @@ -// 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 -#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 diff --git a/lib/IRremoteESP8266/src/ir_JVC.cpp b/lib/IRremoteESP8266/src/ir_JVC.cpp deleted file mode 100644 index 9afd6a97f1..0000000000 --- a/lib/IRremoteESP8266/src/ir_JVC.cpp +++ /dev/null @@ -1,131 +0,0 @@ -// 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 -#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 diff --git a/lib/IRremoteESP8266/src/ir_Kelon.cpp b/lib/IRremoteESP8266/src/ir_Kelon.cpp deleted file mode 100644 index 39b61744eb..0000000000 --- a/lib/IRremoteESP8266/src/ir_Kelon.cpp +++ /dev/null @@ -1,502 +0,0 @@ -// Copyright 2021 Davide Depau - -/// @file -/// @brief Support for Kelan AC protocol. -/// 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 - -#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; - -#if SEND_KELON - -/// Send a Kelon 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, 50); -} - -#endif // SEND_KELON - -#if DECODE_KELON -/// Decode the supplied Kelon 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->state, - results->rawlen - offset, nbits, - kKelonHdrMark, kKelonHdrSpace, - kKelonBitMark, kKelonOneSpace, - kKelonBitMark, kKelonZeroSpace, - kKelonBitMark, 0, false, - _tolerance, 0, false)) { - return false; - } - - results->decode_type = decode_type_t::KELON; - 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(fan) - 4) * -1) % 4; -} - -/// Get the current fan speed setting. -/// @return The current fan speed. -uint8_t IRKelonAc::getFan() const { - return ((static_cast(_.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(_.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(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; - } -} - -/// 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. - result.mode = toCommonMode(getMode()); - result.celsius = true; - result.degrees = getTemp(); - result.fanspeed = toCommonFanSpeed(getFan()); - result.turbo = getSupercool(); - result.sleep = getSleep() ? 0 : -1; - // Not supported. - // N/A, AC only supports toggling it - result.power = (prev == nullptr || prev->power) ^ _.PowerToggle; - // N/A, 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.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; -} diff --git a/lib/IRremoteESP8266/src/ir_Kelon.h b/lib/IRremoteESP8266/src/ir_Kelon.h index 498650623f..663b25cb9f 100644 --- a/lib/IRremoteESP8266/src/ir_Kelon.h +++ b/lib/IRremoteESP8266/src/ir_Kelon.h @@ -17,12 +17,12 @@ #define IR_KELON_H_ #ifdef UNIT_TEST -#include "IRsend_test.h" +#include "../src/IRsend_test.h" #endif -#include "IRremoteESP8266.h" -#include "IRsend.h" -#include "IRutils.h" +#include "../src/IRremoteESP8266.h" +#include "../src/IRsend.h" +#include "../src/IRutils.h" union KelonProtocol { uint64_t raw; diff --git a/lib/IRremoteESP8266/src/ir_Kelvinator.cpp b/lib/IRremoteESP8266/src/ir_Kelvinator.cpp deleted file mode 100644 index 01d2e544c7..0000000000 --- a/lib/IRremoteESP8266/src/ir_Kelvinator.cpp +++ /dev/null @@ -1,525 +0,0 @@ -// 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 -#include -#ifndef ARDUINO -#include -#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; - -#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; - } -} - -/// Control the current vertical swing setting. -/// @param[in] on The desired setting. -void IRKelvinatorAC::setSwingVertical(const bool on) { - _.SwingV = on; - _.VentSwing = (on || _.SwingH); -} - -/// Is the vertical swing setting on? -/// @return The current value. -bool IRKelvinatorAC::getSwingVertical(void) const { - return _.SwingV; -} - -/// Control the current horizontal swing setting. -/// @param[in] on The desired setting. -void IRKelvinatorAC::setSwingHorizontal(const bool on) { - _.SwingH = on; - _.VentSwing = (on || _.SwingV); -} - -/// 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 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 += addBoolToString(_.SwingV, kSwingVStr); - 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 diff --git a/lib/IRremoteESP8266/src/ir_Kelvinator.h b/lib/IRremoteESP8266/src/ir_Kelvinator.h index 74371d8efb..8da8df345f 100644 --- a/lib/IRremoteESP8266/src/ir_Kelvinator.h +++ b/lib/IRremoteESP8266/src/ir_Kelvinator.h @@ -25,10 +25,10 @@ #ifndef UNIT_TEST #include #endif -#include "IRremoteESP8266.h" -#include "IRsend.h" +#include "../src/IRremoteESP8266.h" +#include "../src/IRsend.h" #ifdef UNIT_TEST -#include "IRsend_test.h" +#include "../src/IRsend_test.h" #endif /// Native representation of a Kelvinator A/C message. diff --git a/lib/IRremoteESP8266/src/ir_LG.cpp b/lib/IRremoteESP8266/src/ir_LG.cpp deleted file mode 100644 index d817fbdb74..0000000000 --- a/lib/IRremoteESP8266/src/ir_LG.cpp +++ /dev/null @@ -1,838 +0,0 @@ -// 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 -#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::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::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: - _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() || 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(); -} - -/// 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: - 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 Swing message. -/// @return true, if it is. Otherwise, false. -bool IRLgAc::isSwing(void) const { - return (_.raw >> 12) == kLgAcSwingSignature; -} - -/// 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); -} - -/// 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 || - 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 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; - result.model = getModel(); - result.power = getPower(); - result.mode = toCommonMode(_.Mode); - result.celsius = true; - result.degrees = getTemp(); - result.fanspeed = toCommonFanSpeed(_.Fan); - result.light = isLightToggle() ? !result.light : _light; - 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()) { - 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); -} diff --git a/lib/IRremoteESP8266/src/ir_LG.h b/lib/IRremoteESP8266/src/ir_LG.h index 2bd7dbc1c2..9578e155fe 100644 --- a/lib/IRremoteESP8266/src/ir_LG.h +++ b/lib/IRremoteESP8266/src/ir_LG.h @@ -28,11 +28,11 @@ #ifndef UNIT_TEST #include #endif -#include "IRremoteESP8266.h" -#include "IRsend.h" -#include "IRutils.h" +#include "../src/IRremoteESP8266.h" +#include "../src/IRsend.h" +#include "../src/IRutils.h" #ifdef UNIT_TEST -#include "IRsend_test.h" +#include "../src/IRsend_test.h" #endif /// Native representation of a LG A/C message. diff --git a/lib/IRremoteESP8266/src/ir_Lasertag.cpp b/lib/IRremoteESP8266/src/ir_Lasertag.cpp deleted file mode 100644 index 3e40de0efd..0000000000 --- a/lib/IRremoteESP8266/src/ir_Lasertag.cpp +++ /dev/null @@ -1,116 +0,0 @@ -// 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 -#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 diff --git a/lib/IRremoteESP8266/src/ir_Lego.cpp b/lib/IRremoteESP8266/src/ir_Lego.cpp deleted file mode 100644 index 3b7144768b..0000000000 --- a/lib/IRremoteESP8266/src/ir_Lego.cpp +++ /dev/null @@ -1,106 +0,0 @@ -// 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 -#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 diff --git a/lib/IRremoteESP8266/src/ir_Lutron.cpp b/lib/IRremoteESP8266/src/ir_Lutron.cpp deleted file mode 100644 index 5d04247843..0000000000 --- a/lib/IRremoteESP8266/src/ir_Lutron.cpp +++ /dev/null @@ -1,143 +0,0 @@ -// 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 -#include -#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 diff --git a/lib/IRremoteESP8266/src/ir_MWM.cpp b/lib/IRremoteESP8266/src/ir_MWM.cpp deleted file mode 100644 index 8aca4a4fd0..0000000000 --- a/lib/IRremoteESP8266/src/ir_MWM.cpp +++ /dev/null @@ -1,197 +0,0 @@ -// 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 -#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 diff --git a/lib/IRremoteESP8266/src/ir_Magiquest.cpp b/lib/IRremoteESP8266/src/ir_Magiquest.cpp deleted file mode 100644 index 3f79a18f64..0000000000 --- a/lib/IRremoteESP8266/src/ir_Magiquest.cpp +++ /dev/null @@ -1,154 +0,0 @@ -// 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 -#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 diff --git a/lib/IRremoteESP8266/src/ir_Magiquest.h b/lib/IRremoteESP8266/src/ir_Magiquest.h index 3999043752..37f928b3f8 100644 --- a/lib/IRremoteESP8266/src/ir_Magiquest.h +++ b/lib/IRremoteESP8266/src/ir_Magiquest.h @@ -15,8 +15,8 @@ #define __STDC_LIMIT_MACROS #include -#include "IRremoteESP8266.h" -#include "IRsend.h" +#include "../src/IRremoteESP8266.h" +#include "../src/IRsend.h" /// MagiQuest packet is both Wand ID and magnitude of swish and flick union magiquest { diff --git a/lib/IRremoteESP8266/src/ir_Metz.cpp b/lib/IRremoteESP8266/src/ir_Metz.cpp deleted file mode 100644 index 0dcc7dafa6..0000000000 --- a/lib/IRremoteESP8266/src/ir_Metz.cpp +++ /dev/null @@ -1,101 +0,0 @@ -// 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 diff --git a/lib/IRremoteESP8266/src/ir_Midea.cpp b/lib/IRremoteESP8266/src/ir_Midea.cpp deleted file mode 100644 index 8b3b645d80..0000000000 --- a/lib/IRremoteESP8266/src/ir_Midea.cpp +++ /dev/null @@ -1,796 +0,0 @@ -// 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 -#ifndef ARDUINO -#include -#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::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; - _SwingVToggle = false; - _EconoToggle = false; - _TurboToggle = false; - _LightToggle = 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; -} -#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; -} - -/// 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.clean = 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.fanspeed = toCommonFanSpeed(_.Fan); - result.sleep = _.Sleep ? 0 : -1; - result.econo = getEconoToggle(); - 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 += addBoolToString(getSwingVToggle(), kSwingVToggleStr); -#if KAYSUN_AC - result += addBoolToString(getSwingVStep(), kStepStr); -#endif // KAYSUN_AC - result += addBoolToString(getEconoToggle(), kEconoToggleStr); - result += addBoolToString(getTurboToggle(), kTurboToggleStr); - result += addBoolToString(getLightToggle(), kLightToggleStr); - 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 diff --git a/lib/IRremoteESP8266/src/ir_Midea.h b/lib/IRremoteESP8266/src/ir_Midea.h index eaeaa04d8d..95a47b0b44 100644 --- a/lib/IRremoteESP8266/src/ir_Midea.h +++ b/lib/IRremoteESP8266/src/ir_Midea.h @@ -33,10 +33,10 @@ #ifdef ARDUINO #include #endif -#include "IRremoteESP8266.h" -#include "IRsend.h" +#include "../src/IRremoteESP8266.h" +#include "../src/IRsend.h" #ifdef UNIT_TEST -#include "IRsend_test.h" +#include "../src/IRsend_test.h" #endif /// @note diff --git a/lib/IRremoteESP8266/src/ir_MilesTag2.cpp b/lib/IRremoteESP8266/src/ir_MilesTag2.cpp deleted file mode 100644 index 013b2fbe28..0000000000 --- a/lib/IRremoteESP8266/src/ir_MilesTag2.cpp +++ /dev/null @@ -1,113 +0,0 @@ -// 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 -#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 diff --git a/lib/IRremoteESP8266/src/ir_Mitsubishi.h b/lib/IRremoteESP8266/src/ir_Mitsubishi.h index 55afcdce66..5b02bca653 100644 --- a/lib/IRremoteESP8266/src/ir_Mitsubishi.h +++ b/lib/IRremoteESP8266/src/ir_Mitsubishi.h @@ -44,10 +44,10 @@ #ifndef UNIT_TEST #include #endif -#include "IRremoteESP8266.h" -#include "IRsend.h" +#include "../src/IRremoteESP8266.h" +#include "../src/IRsend.h" #ifdef UNIT_TEST -#include "IRsend_test.h" +#include "../src/IRsend_test.h" #endif /// Native representation of a Mitsubishi 144-bit A/C message. diff --git a/lib/IRremoteESP8266/src/ir_MitsubishiHeavy.cpp b/lib/IRremoteESP8266/src/ir_MitsubishiHeavy.cpp deleted file mode 100644 index b4a6ffc352..0000000000 --- a/lib/IRremoteESP8266/src/ir_MitsubishiHeavy.cpp +++ /dev/null @@ -1,1050 +0,0 @@ -// 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 - -#include "ir_MitsubishiHeavy.h" -#include -#include -#include "IRremoteESP8266.h" -#include "IRtext.h" -#include "IRutils.h" -#ifndef ARDUINO -#include -#endif - -// Constants -const uint16_t kMitsubishiHeavyHdrMark = 3140; -const uint16_t kMitsubishiHeavyHdrSpace = 1630; -const uint16_t kMitsubishiHeavyBitMark = 370; -const uint16_t kMitsubishiHeavyOneSpace = 420; -const uint16_t kMitsubishiHeavyZeroSpace = 1220; -const uint32_t kMitsubishiHeavyGap = kDefaultMessageGap; // Just a guess. - -using irutils::addBoolToString; -using irutils::addIntToString; -using irutils::addLabeledString; -using irutils::addModeToString; -using irutils::addSwingHToString; -using irutils::addSwingVToString; -using irutils::addTempToString; -using irutils::checkInvertedBytePairs; -using irutils::invertBytePairs; - -#if SEND_MITSUBISHIHEAVY -/// Send a MitsubishiHeavy 88-bit A/C message. -/// Status: BETA / Appears to be working. Needs testing against a real device. -/// @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::sendMitsubishiHeavy88(const unsigned char data[], - const uint16_t nbytes, - const uint16_t repeat) { - if (nbytes < kMitsubishiHeavy88StateLength) - return; // Not enough bytes to send a proper message. - sendGeneric(kMitsubishiHeavyHdrMark, kMitsubishiHeavyHdrSpace, - kMitsubishiHeavyBitMark, kMitsubishiHeavyOneSpace, - kMitsubishiHeavyBitMark, kMitsubishiHeavyZeroSpace, - kMitsubishiHeavyBitMark, kMitsubishiHeavyGap, - data, nbytes, 38000, false, repeat, kDutyDefault); -} - -/// Send a MitsubishiHeavy 152-bit A/C message. -/// Status: BETA / Appears to be working. Needs testing against a real device. -/// @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::sendMitsubishiHeavy152(const unsigned char data[], - const uint16_t nbytes, - const uint16_t repeat) { - if (nbytes < kMitsubishiHeavy152StateLength) - return; // Not enough bytes to send a proper message. - sendMitsubishiHeavy88(data, nbytes, repeat); -} -#endif // SEND_MITSUBISHIHEAVY - -// Class for decoding and constructing MitsubishiHeavy152 AC messages. - -/// 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? -IRMitsubishiHeavy152Ac::IRMitsubishiHeavy152Ac(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 IRMitsubishiHeavy152Ac::begin(void) { _irsend.begin(); } - -#if SEND_MITSUBISHIHEAVY -/// Send the current internal state as an IR message. -/// @param[in] repeat Nr. of times the message will be repeated. -void IRMitsubishiHeavy152Ac::send(const uint16_t repeat) { - _irsend.sendMitsubishiHeavy152(getRaw(), kMitsubishiHeavy152StateLength, - repeat); -} -#endif // SEND_MITSUBISHIHEAVY - -/// Reset the state of the remote to a known good state/sequence. -void IRMitsubishiHeavy152Ac::stateReset(void) { - std::memcpy(_.raw, kMitsubishiHeavyZmsSig, kMitsubishiHeavySigLength); - for (uint8_t i = kMitsubishiHeavySigLength; - i < kMitsubishiHeavy152StateLength - 3; i += 2) _.raw[i] = 0; - _.raw[17] = 0x80; -} - -/// 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 *IRMitsubishiHeavy152Ac::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 IRMitsubishiHeavy152Ac::setRaw(const uint8_t *data) { - std::memcpy(_.raw, data, kMitsubishiHeavy152StateLength); -} - -/// Set the requested power state of the A/C to on. -void IRMitsubishiHeavy152Ac::on(void) { setPower(true); } - -/// Set the requested power state of the A/C to off. -void IRMitsubishiHeavy152Ac::off(void) { setPower(false); } - -/// Change the power setting. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRMitsubishiHeavy152Ac::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 IRMitsubishiHeavy152Ac::getPower(void) const { - return _.Power; -} - -/// Set the temperature. -/// @param[in] temp The temperature in degrees celsius. -void IRMitsubishiHeavy152Ac::setTemp(const uint8_t temp) { - uint8_t newtemp = temp; - newtemp = std::min(newtemp, kMitsubishiHeavyMaxTemp); - newtemp = std::max(newtemp, kMitsubishiHeavyMinTemp); - _.Temp = newtemp - kMitsubishiHeavyMinTemp; -} - -/// Get the current temperature setting. -/// @return The current setting for temp. in degrees celsius. -uint8_t IRMitsubishiHeavy152Ac::getTemp(void) const { - return _.Temp + kMitsubishiHeavyMinTemp; -} - -/// Set the speed of the fan. -/// @param[in] speed The desired setting. -void IRMitsubishiHeavy152Ac::setFan(const uint8_t speed) { - uint8_t newspeed = speed; - switch (speed) { - case kMitsubishiHeavy152FanLow: - case kMitsubishiHeavy152FanMed: - case kMitsubishiHeavy152FanHigh: - case kMitsubishiHeavy152FanMax: - case kMitsubishiHeavy152FanEcono: - case kMitsubishiHeavy152FanTurbo: break; - default: newspeed = kMitsubishiHeavy152FanAuto; - } - _.Fan = newspeed; -} - -/// Get the current fan speed setting. -/// @return The current fan speed/mode. -uint8_t IRMitsubishiHeavy152Ac::getFan(void) const { - return _.Fan; -} - -/// Set the operating mode of the A/C. -/// @param[in] mode The desired operating mode. -void IRMitsubishiHeavy152Ac::setMode(const uint8_t mode) { - uint8_t newmode = mode; - switch (mode) { - case kMitsubishiHeavyCool: - case kMitsubishiHeavyDry: - case kMitsubishiHeavyFan: - case kMitsubishiHeavyHeat: - break; - default: - newmode = kMitsubishiHeavyAuto; - } - _.Mode = newmode; -} - -/// Get the operating mode setting of the A/C. -/// @return The current operating mode setting. -uint8_t IRMitsubishiHeavy152Ac::getMode(void) const { - return _.Mode; -} - -/// Set the Vertical Swing mode of the A/C. -/// @param[in] pos The position/mode to set the swing to. -void IRMitsubishiHeavy152Ac::setSwingVertical(const uint8_t pos) { - _.SwingV = std::min(pos, kMitsubishiHeavy152SwingVOff); -} - -/// Get the Vertical Swing mode of the A/C. -/// @return The native position/mode setting. -uint8_t IRMitsubishiHeavy152Ac::getSwingVertical(void) const { - return _.SwingV; -} - -/// Set the Horizontal Swing mode of the A/C. -/// @param[in] pos The position/mode to set the swing to. -void IRMitsubishiHeavy152Ac::setSwingHorizontal(const uint8_t pos) { - _.SwingH = std::min(pos, kMitsubishiHeavy152SwingHOff); -} - -/// Get the Horizontal Swing mode of the A/C. -/// @return The native position/mode setting. -uint8_t IRMitsubishiHeavy152Ac::getSwingHorizontal(void) const { - return _.SwingH; -} - -/// Set the Night (Sleep) mode of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRMitsubishiHeavy152Ac::setNight(const bool on) { - _.Night = on; -} - -/// Get the Night (Sleep) mode of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRMitsubishiHeavy152Ac::getNight(void) const { - return _.Night; -} - -/// Set the 3D mode of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRMitsubishiHeavy152Ac::set3D(const bool on) { - if (on) - { _.Three = 1; _.D = 1; } - else - { _.Three = 0; _.D = 0; } -} - -/// Get the 3D mode of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRMitsubishiHeavy152Ac::get3D(void) const { - return _.Three && _.D; -} - -/// Set the Silent (Quiet) mode of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRMitsubishiHeavy152Ac::setSilent(const bool on) { - _.Silent = on; -} - -/// Get the Silent (Quiet) mode of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRMitsubishiHeavy152Ac::getSilent(void) const { - return _.Silent; -} - -/// Set the Filter mode of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRMitsubishiHeavy152Ac::setFilter(const bool on) { - _.Filter = on; -} - -/// Get the Filter mode of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRMitsubishiHeavy152Ac::getFilter(void) const { - return _.Filter; -} - -/// Set the Clean mode of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRMitsubishiHeavy152Ac::setClean(const bool on) { - _.Filter = on; - _.Clean = on; -} - -/// Get the Clean mode of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRMitsubishiHeavy152Ac::getClean(void) const { - return _.Clean && _.Filter; -} - -/// Set the Turbo mode of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRMitsubishiHeavy152Ac::setTurbo(const bool on) { - if (on) - setFan(kMitsubishiHeavy152FanTurbo); - else if (getTurbo()) setFan(kMitsubishiHeavy152FanAuto); -} - -/// Get the Turbo mode of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRMitsubishiHeavy152Ac::getTurbo(void) const { - return _.Fan == kMitsubishiHeavy152FanTurbo; -} - -/// Set the Economical mode of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRMitsubishiHeavy152Ac::setEcono(const bool on) { - if (on) - setFan(kMitsubishiHeavy152FanEcono); - else if (getEcono()) setFan(kMitsubishiHeavy152FanAuto); -} - -/// Get the Economical mode of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRMitsubishiHeavy152Ac::getEcono(void) const { - return _.Fan == kMitsubishiHeavy152FanEcono; -} - -/// Verify the given state has a ZM-S signature. -/// @param[in] state A ptr to a state to be checked. -/// @return true, the check passed. Otherwise, false. -bool IRMitsubishiHeavy152Ac::checkZmsSig(const uint8_t *state) { - for (uint8_t i = 0; i < kMitsubishiHeavySigLength; i++) - if (state[i] != kMitsubishiHeavyZmsSig[i]) return false; - return true; -} - -/// Calculate the checksum for the current internal state of the remote. -/// Note: Technically it has no checksum, but does have inverted byte pairs. -void IRMitsubishiHeavy152Ac::checksum(void) { - const uint8_t kOffset = kMitsubishiHeavySigLength - 2; - invertBytePairs(_.raw + kOffset, kMitsubishiHeavy152StateLength - kOffset); -} - -/// 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 state array. -/// @return true, if the state has a valid checksum. Otherwise, false. -/// Note: Technically it has no checksum, but does have inverted byte pairs. -bool IRMitsubishiHeavy152Ac::validChecksum(const uint8_t *state, - const uint16_t length) { - // Assume anything too short is fine. - if (length < kMitsubishiHeavySigLength) return true; - const uint8_t kOffset = kMitsubishiHeavySigLength - 2; - return checkInvertedBytePairs(state + kOffset, length - kOffset); -} - -/// 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 IRMitsubishiHeavy152Ac::convertMode(const stdAc::opmode_t mode) { - switch (mode) { - case stdAc::opmode_t::kCool: return kMitsubishiHeavyCool; - case stdAc::opmode_t::kHeat: return kMitsubishiHeavyHeat; - case stdAc::opmode_t::kDry: return kMitsubishiHeavyDry; - case stdAc::opmode_t::kFan: return kMitsubishiHeavyFan; - default: return kMitsubishiHeavyAuto; - } -} - -/// 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 IRMitsubishiHeavy152Ac::convertFan(const stdAc::fanspeed_t speed) { - switch (speed) { - // Assumes Econo is slower than Low. - case stdAc::fanspeed_t::kMin: return kMitsubishiHeavy152FanEcono; - case stdAc::fanspeed_t::kLow: return kMitsubishiHeavy152FanLow; - case stdAc::fanspeed_t::kMedium: return kMitsubishiHeavy152FanMed; - case stdAc::fanspeed_t::kHigh: return kMitsubishiHeavy152FanHigh; - case stdAc::fanspeed_t::kMax: return kMitsubishiHeavy152FanMax; - default: return kMitsubishiHeavy152FanAuto; - } -} - -/// 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 IRMitsubishiHeavy152Ac::convertSwingV(const stdAc::swingv_t position) { - switch (position) { - case stdAc::swingv_t::kAuto: return kMitsubishiHeavy152SwingVAuto; - case stdAc::swingv_t::kHighest: return kMitsubishiHeavy152SwingVHighest; - case stdAc::swingv_t::kHigh: return kMitsubishiHeavy152SwingVHigh; - case stdAc::swingv_t::kMiddle: return kMitsubishiHeavy152SwingVMiddle; - case stdAc::swingv_t::kLow: return kMitsubishiHeavy152SwingVLow; - case stdAc::swingv_t::kLowest: return kMitsubishiHeavy152SwingVLowest; - default: return kMitsubishiHeavy152SwingVOff; - } -} - -/// Convert a stdAc::swingh_t enum into it's native setting. -/// @param[in] position The enum to be converted. -/// @return The native equivalent of the enum. -uint8_t IRMitsubishiHeavy152Ac::convertSwingH(const stdAc::swingh_t position) { - switch (position) { - case stdAc::swingh_t::kAuto: return kMitsubishiHeavy152SwingHAuto; - case stdAc::swingh_t::kLeftMax: return kMitsubishiHeavy152SwingHLeftMax; - case stdAc::swingh_t::kLeft: return kMitsubishiHeavy152SwingHLeft; - case stdAc::swingh_t::kMiddle: return kMitsubishiHeavy152SwingHMiddle; - case stdAc::swingh_t::kRight: return kMitsubishiHeavy152SwingHRight; - case stdAc::swingh_t::kRightMax: return kMitsubishiHeavy152SwingHRightMax; - default: return kMitsubishiHeavy152SwingHOff; - } -} - -/// 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 IRMitsubishiHeavy152Ac::toCommonMode(const uint8_t mode) { - switch (mode) { - case kMitsubishiHeavyCool: return stdAc::opmode_t::kCool; - case kMitsubishiHeavyHeat: return stdAc::opmode_t::kHeat; - case kMitsubishiHeavyDry: return stdAc::opmode_t::kDry; - case kMitsubishiHeavyFan: return stdAc::opmode_t::kFan; - default: return stdAc::opmode_t::kAuto; - } -} - -/// Convert a native fan speed into its stdAc equivalent. -/// @param[in] spd The native setting to be converted. -/// @return The stdAc equivalent of the native setting. -stdAc::fanspeed_t IRMitsubishiHeavy152Ac::toCommonFanSpeed(const uint8_t spd) { - switch (spd) { - case kMitsubishiHeavy152FanMax: return stdAc::fanspeed_t::kMax; - case kMitsubishiHeavy152FanHigh: return stdAc::fanspeed_t::kHigh; - case kMitsubishiHeavy152FanMed: return stdAc::fanspeed_t::kMedium; - case kMitsubishiHeavy152FanLow: return stdAc::fanspeed_t::kLow; - case kMitsubishiHeavy152FanEcono: return stdAc::fanspeed_t::kMin; - default: return stdAc::fanspeed_t::kAuto; - } -} - -/// Convert a native horizontal swing postion to it's common equivalent. -/// @param[in] pos A native position to convert. -/// @return The common horizontal swing position. -stdAc::swingh_t IRMitsubishiHeavy152Ac::toCommonSwingH(const uint8_t pos) { - switch (pos) { - case kMitsubishiHeavy152SwingHLeftMax: return stdAc::swingh_t::kLeftMax; - case kMitsubishiHeavy152SwingHLeft: return stdAc::swingh_t::kLeft; - case kMitsubishiHeavy152SwingHMiddle: return stdAc::swingh_t::kMiddle; - case kMitsubishiHeavy152SwingHRight: return stdAc::swingh_t::kRight; - case kMitsubishiHeavy152SwingHRightMax: return stdAc::swingh_t::kRightMax; - case kMitsubishiHeavy152SwingHOff: return stdAc::swingh_t::kOff; - default: return stdAc::swingh_t::kAuto; - } -} - -/// 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 IRMitsubishiHeavy152Ac::toCommonSwingV(const uint8_t pos) { - switch (pos) { - case kMitsubishiHeavy152SwingVHighest: return stdAc::swingv_t::kHighest; - case kMitsubishiHeavy152SwingVHigh: return stdAc::swingv_t::kHigh; - case kMitsubishiHeavy152SwingVMiddle: return stdAc::swingv_t::kMiddle; - case kMitsubishiHeavy152SwingVLow: return stdAc::swingv_t::kLow; - case kMitsubishiHeavy152SwingVLowest: return stdAc::swingv_t::kLowest; - case kMitsubishiHeavy152SwingVOff: return stdAc::swingv_t::kOff; - default: return stdAc::swingv_t::kAuto; - } -} - -/// Convert the current internal state into its stdAc::state_t equivalent. -/// @return The stdAc equivalent of the native settings. -stdAc::state_t IRMitsubishiHeavy152Ac::toCommon(void) const { - stdAc::state_t result; - result.protocol = decode_type_t::MITSUBISHI_HEAVY_152; - 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 = toCommonSwingV(_.SwingV); - result.swingh = toCommonSwingH(_.SwingH); - result.turbo = getTurbo(); - result.econo = getEcono(); - result.clean = getClean(); - result.quiet = _.Silent; - result.filter = _.Filter; - result.sleep = _.Night ? 0 : -1; - // Not supported. - result.light = false; - result.beep = false; - result.clock = -1; - return result; -} - -/// Convert the internal state into a human readable string. -/// @return A string containing the settings in human-readable form. -String IRMitsubishiHeavy152Ac::toString(void) const { - String result = ""; - result.reserve(180); // Reserve some heap for the string to reduce fragging. - result += addBoolToString(_.Power, kPowerStr, false); - result += addModeToString(_.Mode, kMitsubishiHeavyAuto, - kMitsubishiHeavyCool, kMitsubishiHeavyHeat, - kMitsubishiHeavyDry, kMitsubishiHeavyFan); - result += addTempToString(getTemp()); - result += addIntToString(_.Fan, kFanStr); - result += kSpaceLBraceStr; - switch (_.Fan) { - case kMitsubishiHeavy152FanAuto: - result += kAutoStr; - break; - case kMitsubishiHeavy152FanHigh: - result += kHighStr; - break; - case kMitsubishiHeavy152FanLow: - result += kLowStr; - break; - case kMitsubishiHeavy152FanMed: - result += kMedStr; - break; - case kMitsubishiHeavy152FanMax: - result += kMaxStr; - break; - case kMitsubishiHeavy152FanEcono: - result += kEconoStr; - break; - case kMitsubishiHeavy152FanTurbo: - result += kTurboStr; - break; - default: - result += kUnknownStr; - } - result += ')'; - result += addSwingVToString(_.SwingV, kMitsubishiHeavy152SwingVAuto, - kMitsubishiHeavy152SwingVHighest, - kMitsubishiHeavy152SwingVHigh, - kMitsubishiHeavy152SwingVAuto, // UpperMid unused - kMitsubishiHeavy152SwingVMiddle, - kMitsubishiHeavy152SwingVAuto, // LowerMid unused - kMitsubishiHeavy152SwingVLow, - kMitsubishiHeavy152SwingVLowest, - kMitsubishiHeavy152SwingVOff, - // Below are unused. - kMitsubishiHeavy152SwingVAuto, - kMitsubishiHeavy152SwingVAuto, - kMitsubishiHeavy152SwingVAuto); - result += addSwingHToString(_.SwingH, kMitsubishiHeavy152SwingHAuto, - kMitsubishiHeavy152SwingHLeftMax, - kMitsubishiHeavy152SwingHLeft, - kMitsubishiHeavy152SwingHMiddle, - kMitsubishiHeavy152SwingHRight, - kMitsubishiHeavy152SwingHRightMax, - kMitsubishiHeavy152SwingHOff, - kMitsubishiHeavy152SwingHLeftRight, - kMitsubishiHeavy152SwingHRightLeft, - // Below are unused. - kMitsubishiHeavy152SwingHAuto, - kMitsubishiHeavy152SwingHAuto); - result += addBoolToString(_.Silent, kSilentStr); - result += addBoolToString(getTurbo(), kTurboStr); - result += addBoolToString(getEcono(), kEconoStr); - result += addBoolToString(_.Night, kNightStr); - result += addBoolToString(_.Filter, kFilterStr); - result += addBoolToString(get3D(), k3DStr); - result += addBoolToString(getClean(), kCleanStr); - return result; -} - - -// Class for decoding and constructing MitsubishiHeavy88 AC messages. - -/// 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? -IRMitsubishiHeavy88Ac::IRMitsubishiHeavy88Ac(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 IRMitsubishiHeavy88Ac::begin(void) { _irsend.begin(); } - -#if SEND_MITSUBISHIHEAVY -/// Send the current internal state as an IR message. -/// @param[in] repeat Nr. of times the message will be repeated. -void IRMitsubishiHeavy88Ac::send(const uint16_t repeat) { - _irsend.sendMitsubishiHeavy88(getRaw(), kMitsubishiHeavy88StateLength, - repeat); -} -#endif // SEND_MITSUBISHIHEAVY - -/// Reset the state of the remote to a known good state/sequence. -void IRMitsubishiHeavy88Ac::stateReset(void) { - std::memcpy(_.raw, kMitsubishiHeavyZjsSig, kMitsubishiHeavySigLength); - for (uint8_t i = kMitsubishiHeavySigLength; i < kMitsubishiHeavy88StateLength; - i++) _.raw[i] = 0; -} - -/// 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 *IRMitsubishiHeavy88Ac::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 IRMitsubishiHeavy88Ac::setRaw(const uint8_t *data) { - std::memcpy(_.raw, data, kMitsubishiHeavy88StateLength); -} - -/// Set the requested power state of the A/C to on. -void IRMitsubishiHeavy88Ac::on(void) { setPower(true); } - -/// Set the requested power state of the A/C to off. -void IRMitsubishiHeavy88Ac::off(void) { setPower(false); } - -/// Change the power setting. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRMitsubishiHeavy88Ac::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 IRMitsubishiHeavy88Ac::getPower(void) const { - return _.Power; -} - -/// Set the temperature. -/// @param[in] temp The temperature in degrees celsius. -void IRMitsubishiHeavy88Ac::setTemp(const uint8_t temp) { - uint8_t newtemp = temp; - newtemp = std::min(newtemp, kMitsubishiHeavyMaxTemp); - newtemp = std::max(newtemp, kMitsubishiHeavyMinTemp); - _.Temp = newtemp - kMitsubishiHeavyMinTemp; -} - -/// Get the current temperature setting. -/// @return The current setting for temp. in degrees celsius. -uint8_t IRMitsubishiHeavy88Ac::getTemp(void) const { - return _.Temp + kMitsubishiHeavyMinTemp; -} - -/// Set the speed of the fan. -/// @param[in] speed The desired setting. -void IRMitsubishiHeavy88Ac::setFan(const uint8_t speed) { - uint8_t newspeed = speed; - switch (speed) { - case kMitsubishiHeavy88FanLow: - case kMitsubishiHeavy88FanMed: - case kMitsubishiHeavy88FanHigh: - case kMitsubishiHeavy88FanTurbo: - case kMitsubishiHeavy88FanEcono: break; - default: newspeed = kMitsubishiHeavy88FanAuto; - } - _.Fan = newspeed; -} - -/// Get the current fan speed setting. -/// @return The current fan speed/mode. -uint8_t IRMitsubishiHeavy88Ac::getFan(void) const { - return _.Fan; -} - -/// Set the operating mode of the A/C. -/// @param[in] mode The desired operating mode. -void IRMitsubishiHeavy88Ac::setMode(const uint8_t mode) { - uint8_t newmode = mode; - switch (mode) { - case kMitsubishiHeavyCool: - case kMitsubishiHeavyDry: - case kMitsubishiHeavyFan: - case kMitsubishiHeavyHeat: - break; - default: - newmode = kMitsubishiHeavyAuto; - } - _.Mode = newmode; -} - -/// Get the operating mode setting of the A/C. -/// @return The current operating mode setting. -uint8_t IRMitsubishiHeavy88Ac::getMode(void) const { - return _.Mode; -} - -/// Set the Vertical Swing mode of the A/C. -/// @param[in] pos The position/mode to set the swing to. -void IRMitsubishiHeavy88Ac::setSwingVertical(const uint8_t pos) { - uint8_t newpos; - switch (pos) { - case kMitsubishiHeavy88SwingVAuto: - case kMitsubishiHeavy88SwingVHighest: - case kMitsubishiHeavy88SwingVHigh: - case kMitsubishiHeavy88SwingVMiddle: - case kMitsubishiHeavy88SwingVLow: - case kMitsubishiHeavy88SwingVLowest: newpos = pos; break; - default: newpos = kMitsubishiHeavy88SwingVOff; - } - _.SwingV5 = newpos; - _.SwingV7 = (newpos >> kMitsubishiHeavy88SwingVByte5Size); -} - -/// Get the Vertical Swing mode of the A/C. -/// @return The native position/mode setting. -uint8_t IRMitsubishiHeavy88Ac::getSwingVertical(void) const { - return _.SwingV5 | (_.SwingV7 << kMitsubishiHeavy88SwingVByte5Size); -} - -/// Set the Horizontal Swing mode of the A/C. -/// @param[in] pos The position/mode to set the swing to. -void IRMitsubishiHeavy88Ac::setSwingHorizontal(const uint8_t pos) { - uint8_t newpos; - switch (pos) { - case kMitsubishiHeavy88SwingHAuto: - case kMitsubishiHeavy88SwingHLeftMax: - case kMitsubishiHeavy88SwingHLeft: - case kMitsubishiHeavy88SwingHMiddle: - case kMitsubishiHeavy88SwingHRight: - case kMitsubishiHeavy88SwingHRightMax: - case kMitsubishiHeavy88SwingHLeftRight: - case kMitsubishiHeavy88SwingHRightLeft: - case kMitsubishiHeavy88SwingH3D: newpos = pos; break; - default: newpos = kMitsubishiHeavy88SwingHOff; - } - _.SwingH1 = newpos; - _.SwingH2 = (newpos >> kMitsubishiHeavy88SwingHSize); -} - -/// Get the Horizontal Swing mode of the A/C. -/// @return The native position/mode setting. -uint8_t IRMitsubishiHeavy88Ac::getSwingHorizontal(void) const { - return _.SwingH1 | (_.SwingH2 << kMitsubishiHeavy88SwingHSize); -} - -/// Set the Turbo mode of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRMitsubishiHeavy88Ac::setTurbo(const bool on) { - if (on) - setFan(kMitsubishiHeavy88FanTurbo); - else if (getTurbo()) setFan(kMitsubishiHeavy88FanAuto); -} - -/// Get the Turbo mode of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRMitsubishiHeavy88Ac::getTurbo(void) const { - return _.Fan == kMitsubishiHeavy88FanTurbo; -} - -/// Set the Economical mode of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRMitsubishiHeavy88Ac::setEcono(const bool on) { - if (on) - setFan(kMitsubishiHeavy88FanEcono); - else if (getEcono()) setFan(kMitsubishiHeavy88FanAuto); -} - -/// Get the Economical mode of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRMitsubishiHeavy88Ac::getEcono(void) const { - return _.Fan == kMitsubishiHeavy88FanEcono; -} - -/// Set the 3D mode of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRMitsubishiHeavy88Ac::set3D(const bool on) { - if (on) - setSwingHorizontal(kMitsubishiHeavy88SwingH3D); - else if (get3D()) - setSwingHorizontal(kMitsubishiHeavy88SwingHOff); -} - -/// Get the 3D mode of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRMitsubishiHeavy88Ac::get3D(void) const { - return getSwingHorizontal() == kMitsubishiHeavy88SwingH3D; -} - -/// Set the Clean mode of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRMitsubishiHeavy88Ac::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 IRMitsubishiHeavy88Ac::getClean(void) const { - return _.Clean; -} - -/// Verify the given state has a ZJ-S signature. -/// @param[in] state A ptr to a state to be checked. -/// @return true, the check passed. Otherwise, false. -bool IRMitsubishiHeavy88Ac::checkZjsSig(const uint8_t *state) { - for (uint8_t i = 0; i < kMitsubishiHeavySigLength; i++) - if (state[i] != kMitsubishiHeavyZjsSig[i]) return false; - return true; -} - -/// Calculate the checksum for the current internal state of the remote. -/// Note: Technically it has no checksum, but does have inverted byte pairs. -void IRMitsubishiHeavy88Ac::checksum(void) { - const uint8_t kOffset = kMitsubishiHeavySigLength - 2; - invertBytePairs(_.raw + kOffset, kMitsubishiHeavy88StateLength - kOffset); -} - -/// 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 state array. -/// @return true, if the state has a valid checksum. Otherwise, false. -/// Note: Technically it has no checksum, but does have inverted byte pairs. -bool IRMitsubishiHeavy88Ac::validChecksum(const uint8_t *state, - const uint16_t length) { - return IRMitsubishiHeavy152Ac::validChecksum(state, length); -} - -/// 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 IRMitsubishiHeavy88Ac::convertMode(const stdAc::opmode_t mode) { - return IRMitsubishiHeavy152Ac::convertMode(mode); -} - -/// 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 IRMitsubishiHeavy88Ac::convertFan(const stdAc::fanspeed_t speed) { - switch (speed) { - // Assumes Econo is slower than Low. - case stdAc::fanspeed_t::kMin: return kMitsubishiHeavy88FanEcono; - case stdAc::fanspeed_t::kLow: return kMitsubishiHeavy88FanLow; - case stdAc::fanspeed_t::kMedium: return kMitsubishiHeavy88FanMed; - case stdAc::fanspeed_t::kHigh: return kMitsubishiHeavy88FanHigh; - case stdAc::fanspeed_t::kMax: return kMitsubishiHeavy88FanTurbo; - default: return kMitsubishiHeavy88FanAuto; - } -} - -/// 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 IRMitsubishiHeavy88Ac::convertSwingV(const stdAc::swingv_t position) { - switch (position) { - case stdAc::swingv_t::kAuto: return kMitsubishiHeavy88SwingVAuto; - case stdAc::swingv_t::kHighest: return kMitsubishiHeavy88SwingVHighest; - case stdAc::swingv_t::kHigh: return kMitsubishiHeavy88SwingVHigh; - case stdAc::swingv_t::kMiddle: return kMitsubishiHeavy88SwingVMiddle; - case stdAc::swingv_t::kLow: return kMitsubishiHeavy88SwingVLow; - case stdAc::swingv_t::kLowest: return kMitsubishiHeavy88SwingVLowest; - default: return kMitsubishiHeavy88SwingVOff; - } -} - -/// Convert a stdAc::swingh_t enum into it's native setting. -/// @param[in] position The enum to be converted. -/// @return The native equivalent of the enum. -uint8_t IRMitsubishiHeavy88Ac::convertSwingH(const stdAc::swingh_t position) { - switch (position) { - case stdAc::swingh_t::kAuto: return kMitsubishiHeavy88SwingHAuto; - case stdAc::swingh_t::kLeftMax: return kMitsubishiHeavy88SwingHLeftMax; - case stdAc::swingh_t::kLeft: return kMitsubishiHeavy88SwingHLeft; - case stdAc::swingh_t::kMiddle: return kMitsubishiHeavy88SwingHMiddle; - case stdAc::swingh_t::kRight: return kMitsubishiHeavy88SwingHRight; - case stdAc::swingh_t::kRightMax: return kMitsubishiHeavy88SwingHRightMax; - default: return kMitsubishiHeavy88SwingHOff; - } -} - -/// 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 IRMitsubishiHeavy88Ac::toCommonFanSpeed(const uint8_t speed) { - switch (speed) { - case kMitsubishiHeavy88FanTurbo: return stdAc::fanspeed_t::kMax; - case kMitsubishiHeavy88FanHigh: return stdAc::fanspeed_t::kHigh; - case kMitsubishiHeavy88FanMed: return stdAc::fanspeed_t::kMedium; - case kMitsubishiHeavy88FanLow: return stdAc::fanspeed_t::kLow; - case kMitsubishiHeavy88FanEcono: return stdAc::fanspeed_t::kMin; - default: return stdAc::fanspeed_t::kAuto; - } -} - -/// Convert a native horizontal swing postion to it's common equivalent. -/// @param[in] pos A native position to convert. -/// @return The common horizontal swing position. -stdAc::swingh_t IRMitsubishiHeavy88Ac::toCommonSwingH(const uint8_t pos) { - switch (pos) { - case kMitsubishiHeavy88SwingHLeftMax: return stdAc::swingh_t::kLeftMax; - case kMitsubishiHeavy88SwingHLeft: return stdAc::swingh_t::kLeft; - case kMitsubishiHeavy88SwingHMiddle: return stdAc::swingh_t::kMiddle; - case kMitsubishiHeavy88SwingHRight: return stdAc::swingh_t::kRight; - case kMitsubishiHeavy88SwingHRightMax: return stdAc::swingh_t::kRightMax; - case kMitsubishiHeavy88SwingHOff: return stdAc::swingh_t::kOff; - default: return stdAc::swingh_t::kAuto; - } -} - -/// 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 IRMitsubishiHeavy88Ac::toCommonSwingV(const uint8_t pos) { - switch (pos) { - case kMitsubishiHeavy88SwingVHighest: return stdAc::swingv_t::kHighest; - case kMitsubishiHeavy88SwingVHigh: return stdAc::swingv_t::kHigh; - case kMitsubishiHeavy88SwingVMiddle: return stdAc::swingv_t::kMiddle; - case kMitsubishiHeavy88SwingVLow: return stdAc::swingv_t::kLow; - case kMitsubishiHeavy88SwingVLowest: return stdAc::swingv_t::kLowest; - case kMitsubishiHeavy88SwingVOff: return stdAc::swingv_t::kOff; - default: return stdAc::swingv_t::kAuto; - } -} - -/// Convert the current internal state into its stdAc::state_t equivalent. -/// @return The stdAc equivalent of the native settings. -stdAc::state_t IRMitsubishiHeavy88Ac::toCommon(void) const { - stdAc::state_t result; - result.protocol = decode_type_t::MITSUBISHI_HEAVY_88; - result.model = -1; // No models used. - result.power = _.Power; - result.mode = IRMitsubishiHeavy152Ac::toCommonMode(_.Mode); - result.celsius = true; - result.degrees = getTemp(); - result.fanspeed = toCommonFanSpeed(_.Fan); - result.swingv = toCommonSwingV(getSwingVertical()); - result.swingh = toCommonSwingH(getSwingHorizontal()); - result.turbo = getTurbo(); - result.econo = getEcono(); - result.clean = _.Clean; - // Not supported. - result.quiet = false; - result.filter = false; - result.light = false; - result.beep = false; - result.sleep = -1; - result.clock = -1; - return result; -} - -/// Convert the internal state into a human readable string. -/// @return A string containing the settings in human-readable form. -String IRMitsubishiHeavy88Ac::toString(void) const { - String result = ""; - result.reserve(140); // Reserve some heap for the string to reduce fragging. - result += addBoolToString(_.Power, kPowerStr, false); - result += addModeToString(_.Mode, kMitsubishiHeavyAuto, - kMitsubishiHeavyCool, kMitsubishiHeavyHeat, - kMitsubishiHeavyDry, kMitsubishiHeavyFan); - result += addTempToString(getTemp()); - result += addIntToString(_.Fan, kFanStr); - result += kSpaceLBraceStr; - switch (_.Fan) { - case kMitsubishiHeavy88FanAuto: - result += kAutoStr; - break; - case kMitsubishiHeavy88FanHigh: - result += kHighStr; - break; - case kMitsubishiHeavy88FanLow: - result += kLowStr; - break; - case kMitsubishiHeavy88FanMed: - result += kMedStr; - break; - case kMitsubishiHeavy88FanEcono: - result += kEconoStr; - break; - case kMitsubishiHeavy88FanTurbo: - result += kTurboStr; - break; - default: - result += kUnknownStr; - } - result += ')'; - result += addSwingVToString(getSwingVertical(), kMitsubishiHeavy88SwingVAuto, - kMitsubishiHeavy88SwingVHighest, - kMitsubishiHeavy88SwingVHigh, - kMitsubishiHeavy88SwingVAuto, // UpperMid unused - kMitsubishiHeavy88SwingVMiddle, - kMitsubishiHeavy88SwingVAuto, // LowerMid unused - kMitsubishiHeavy88SwingVLow, - kMitsubishiHeavy88SwingVLowest, - kMitsubishiHeavy88SwingVOff, - // Below are unused. - kMitsubishiHeavy88SwingVAuto, - kMitsubishiHeavy88SwingVAuto, - kMitsubishiHeavy88SwingVAuto); - result += addSwingHToString(getSwingHorizontal(), - kMitsubishiHeavy88SwingHAuto, - kMitsubishiHeavy88SwingHLeftMax, - kMitsubishiHeavy88SwingHLeft, - kMitsubishiHeavy88SwingHMiddle, - kMitsubishiHeavy88SwingHRight, - kMitsubishiHeavy88SwingHRightMax, - kMitsubishiHeavy88SwingHOff, - kMitsubishiHeavy88SwingHLeftRight, - kMitsubishiHeavy88SwingHRightLeft, - kMitsubishiHeavy88SwingH3D, - // Below are unused. - kMitsubishiHeavy88SwingHAuto); - result += addBoolToString(getTurbo(), kTurboStr); - result += addBoolToString(getEcono(), kEconoStr); - result += addBoolToString(get3D(), k3DStr); - result += addBoolToString(_.Clean, kCleanStr); - return result; -} - -#if DECODE_MITSUBISHIHEAVY -/// Decode the supplied Mitsubishi Heavy Industries A/C message. -/// Status: BETA / Appears to be working. 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 kMitsubishiHeavy88Bits or kMitsubishiHeavy152Bits (def). -/// @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::decodeMitsubishiHeavy(decode_results* results, uint16_t offset, - const uint16_t nbits, const bool strict) { - if (strict) { - switch (nbits) { - case kMitsubishiHeavy88Bits: - case kMitsubishiHeavy152Bits: - break; - default: - return false; // Not what is expected - } - } - - uint16_t used; - used = matchGeneric(results->rawbuf + offset, results->state, - results->rawlen - offset, nbits, - kMitsubishiHeavyHdrMark, kMitsubishiHeavyHdrSpace, - kMitsubishiHeavyBitMark, kMitsubishiHeavyOneSpace, - kMitsubishiHeavyBitMark, kMitsubishiHeavyZeroSpace, - kMitsubishiHeavyBitMark, kMitsubishiHeavyGap, true, - _tolerance, 0, false); - if (used == 0) return false; - offset += used; - // Compliance - switch (nbits) { - case kMitsubishiHeavy88Bits: - if (strict && !(IRMitsubishiHeavy88Ac::checkZjsSig(results->state) && - IRMitsubishiHeavy88Ac::validChecksum(results->state))) - return false; - results->decode_type = MITSUBISHI_HEAVY_88; - break; - case kMitsubishiHeavy152Bits: - if (strict && !(IRMitsubishiHeavy152Ac::checkZmsSig(results->state) && - IRMitsubishiHeavy152Ac::validChecksum(results->state))) - return false; - results->decode_type = MITSUBISHI_HEAVY_152; - break; - default: - return false; - } - - // Success - 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_MITSUBISHIHEAVY diff --git a/lib/IRremoteESP8266/src/ir_MitsubishiHeavy.h b/lib/IRremoteESP8266/src/ir_MitsubishiHeavy.h index b1059f12fa..71b0138476 100644 --- a/lib/IRremoteESP8266/src/ir_MitsubishiHeavy.h +++ b/lib/IRremoteESP8266/src/ir_MitsubishiHeavy.h @@ -23,10 +23,10 @@ #ifndef UNIT_TEST #include #endif -#include "IRremoteESP8266.h" -#include "IRsend.h" +#include "../src/IRremoteESP8266.h" +#include "../src/IRsend.h" #ifdef UNIT_TEST -#include "IRsend_test.h" +#include "../src/IRsend_test.h" #endif /// Native representation of a Mitsubishi Heavy 152-bit A/C message. diff --git a/lib/IRremoteESP8266/src/ir_Multibrackets.cpp b/lib/IRremoteESP8266/src/ir_Multibrackets.cpp deleted file mode 100644 index 2367b49bc9..0000000000 --- a/lib/IRremoteESP8266/src/ir_Multibrackets.cpp +++ /dev/null @@ -1,115 +0,0 @@ -// 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 diff --git a/lib/IRremoteESP8266/src/ir_NEC.cpp b/lib/IRremoteESP8266/src/ir_NEC.cpp deleted file mode 100644 index 9d4af76405..0000000000 --- a/lib/IRremoteESP8266/src/ir_NEC.cpp +++ /dev/null @@ -1,140 +0,0 @@ -// 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 -#include -#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) diff --git a/lib/IRremoteESP8266/src/ir_NEC.h b/lib/IRremoteESP8266/src/ir_NEC.h index 95da064b78..d7603304e1 100644 --- a/lib/IRremoteESP8266/src/ir_NEC.h +++ b/lib/IRremoteESP8266/src/ir_NEC.h @@ -19,7 +19,7 @@ #define IR_NEC_H_ #include -#include "IRremoteESP8266.h" +#include "../src/IRremoteESP8266.h" // Constants const uint16_t kNecTick = 560; diff --git a/lib/IRremoteESP8266/src/ir_Neoclima.cpp b/lib/IRremoteESP8266/src/ir_Neoclima.cpp deleted file mode 100644 index ff26e6c643..0000000000 --- a/lib/IRremoteESP8266/src/ir_Neoclima.cpp +++ /dev/null @@ -1,608 +0,0 @@ -// 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 -#include -#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 diff --git a/lib/IRremoteESP8266/src/ir_Neoclima.h b/lib/IRremoteESP8266/src/ir_Neoclima.h index 90eaa029a9..16d838b385 100644 --- a/lib/IRremoteESP8266/src/ir_Neoclima.h +++ b/lib/IRremoteESP8266/src/ir_Neoclima.h @@ -20,10 +20,10 @@ #ifndef UNIT_TEST #include #endif -#include "IRremoteESP8266.h" -#include "IRsend.h" +#include "../src/IRremoteESP8266.h" +#include "../src/IRsend.h" #ifdef UNIT_TEST -#include "IRsend_test.h" +#include "../src/IRsend_test.h" #endif /// Native representation of a Neoclima A/C message. diff --git a/lib/IRremoteESP8266/src/ir_Nikai.cpp b/lib/IRremoteESP8266/src/ir_Nikai.cpp deleted file mode 100644 index 01c789d70e..0000000000 --- a/lib/IRremoteESP8266/src/ir_Nikai.cpp +++ /dev/null @@ -1,74 +0,0 @@ -// 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 -#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 diff --git a/lib/IRremoteESP8266/src/ir_Panasonic.cpp b/lib/IRremoteESP8266/src/ir_Panasonic.cpp deleted file mode 100644 index e2add03f1f..0000000000 --- a/lib/IRremoteESP8266/src/ir_Panasonic.cpp +++ /dev/null @@ -1,1341 +0,0 @@ -// Copyright 2015 Kristian Lauszus -// Copyright 2017, 2018 David Conran - -/// @file -/// @brief Support for Panasonic protocols. -/// Panasonic protocol originally added by Kristian Lauszus -/// (Thanks to zenwheel and other people at the original blog post) -/// @see Panasonic https://github.com/z3t0/Arduino-IRremote -/// @see http://www.remotecentral.com/cgi-bin/mboard/rc-pronto/thread.cgi?2615 -/// @see Panasonic A/C support heavily influenced by https://github.com/ToniA/ESPEasy/blob/HeatpumpIR/lib/HeatpumpIR/PanasonicHeatpumpIR.cpp -/// Panasonic A/C Clock & Timer support: -/// Reverse Engineering by MikkelTb -/// Code by crankyoldgit - -#include "ir_Panasonic.h" -#include -#include -#ifndef ARDUINO -#include -#endif -#include "IRrecv.h" -#include "IRsend.h" -#include "IRtext.h" -#include "IRutils.h" - -// Constants -/// @see http://www.remotecentral.com/cgi-bin/mboard/rc-pronto/thread.cgi?26152 -const uint16_t kPanasonicHdrMark = 3456; ///< uSeconds. -const uint16_t kPanasonicHdrSpace = 1728; ///< uSeconds. -const uint16_t kPanasonicBitMark = 432; ///< uSeconds. -const uint16_t kPanasonicOneSpace = 1296; ///< uSeconds. -const uint16_t kPanasonicZeroSpace = 432; ///< uSeconds. -const uint32_t kPanasonicMinCommandLength = 163296; ///< uSeconds. -const uint16_t kPanasonicEndGap = 5000; ///< uSeconds. See #245 -const uint32_t kPanasonicMinGap = 74736; ///< uSeconds. - -const uint16_t kPanasonicAcSectionGap = 10000; ///< uSeconds. -const uint16_t kPanasonicAcSection1Length = 8; -const uint32_t kPanasonicAcMessageGap = kDefaultMessageGap; // Just a guess. - -const uint16_t kPanasonicAc32HdrMark = 3543; ///< uSeconds. -const uint16_t kPanasonicAc32BitMark = 920; ///< uSeconds. -const uint16_t kPanasonicAc32HdrSpace = 3450; ///< uSeconds. -const uint16_t kPanasonicAc32OneSpace = 2575; ///< uSeconds. -const uint16_t kPanasonicAc32ZeroSpace = 828; ///< uSeconds. -const uint16_t kPanasonicAc32SectionGap = 13946; ///< uSeconds. -const uint8_t kPanasonicAc32Sections = 2; -const uint8_t kPanasonicAc32BlocksPerSection = 2; - -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::minsToString; -using irutils::setBit; -using irutils::setBits; - -// Used by Denon as well. -#if (SEND_PANASONIC || SEND_DENON) -/// Send a Panasonic 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 This protocol is a modified version of Kaseikyo. -/// @note Use this method if you want to send the results of `decodePanasonic`. -void IRsend::sendPanasonic64(const uint64_t data, const uint16_t nbits, - const uint16_t repeat) { - sendGeneric(kPanasonicHdrMark, kPanasonicHdrSpace, kPanasonicBitMark, - kPanasonicOneSpace, kPanasonicBitMark, kPanasonicZeroSpace, - kPanasonicBitMark, kPanasonicMinGap, kPanasonicMinCommandLength, - data, nbits, kPanasonicFreq, true, repeat, 50); -} - -/// Send a Panasonic formatted message. -/// Status: STABLE, but DEPRECATED -/// @deprecated This is only for legacy use only, please use `sendPanasonic64()` -/// instead. -/// @param[in] address The 16-bit manufacturer code. -/// @param[in] data The 32-bit data portion of 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 a modified version of Kaseikyo. -void IRsend::sendPanasonic(const uint16_t address, const uint32_t data, - const uint16_t nbits, const uint16_t repeat) { - sendPanasonic64(((uint64_t)address << 32) | (uint64_t)data, nbits, repeat); -} - -/// Calculate the raw Panasonic data based on device, subdevice, & function. -/// Status: STABLE / Should be working. -/// @param[in] manufacturer A 16-bit manufacturer code. e.g. 0x4004 is Panasonic -/// @param[in] device An 8-bit code. -/// @param[in] subdevice An 8-bit code. -/// @param[in] function An 8-bit code. -/// @return A value suitable for use with `sendPanasonic64()`. -/// @note Panasonic 48-bit protocol is a modified version of Kaseikyo. -/// @see http://www.remotecentral.com/cgi-bin/mboard/rc-pronto/thread.cgi?2615 -uint64_t IRsend::encodePanasonic(const uint16_t manufacturer, - const uint8_t device, - const uint8_t subdevice, - const uint8_t function) { - uint8_t checksum = device ^ subdevice ^ function; - return (((uint64_t)manufacturer << 32) | ((uint64_t)device << 24) | - ((uint64_t)subdevice << 16) | ((uint64_t)function << 8) | checksum); -} -#endif // (SEND_PANASONIC || SEND_DENON) - -// Used by Denon as well. -#if (DECODE_PANASONIC || DECODE_DENON) -/// Decode the supplied Panasonic message. -/// Status: STABLE / Should 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] manufacturer A 16-bit manufacturer code. e.g. 0x4004 is Panasonic -/// @param[in] strict Flag indicating if we should perform strict matching. -/// @return True if it can decode it, false if it can't. -/// @warning Results to be used with `sendPanasonic64()`, not `sendPanasonic()`. -/// @note Panasonic 48-bit protocol is a modified version of Kaseikyo. -/// @see http://www.remotecentral.com/cgi-bin/mboard/rc-pronto/thread.cgi?2615 -/// @see http://www.hifi-remote.com/wiki/index.php?title=Panasonic -bool IRrecv::decodePanasonic(decode_results *results, uint16_t offset, - const uint16_t nbits, const bool strict, - const uint32_t manufacturer) { - if (strict && nbits != kPanasonicBits) - return false; // Request is out of spec. - - uint64_t data = 0; - - // Match Header + Data + Footer - if (!matchGeneric(results->rawbuf + offset, &data, - results->rawlen - offset, nbits, - kPanasonicHdrMark, kPanasonicHdrSpace, - kPanasonicBitMark, kPanasonicOneSpace, - kPanasonicBitMark, kPanasonicZeroSpace, - kPanasonicBitMark, kPanasonicEndGap, true)) return false; - // Compliance - uint32_t address = data >> 32; - uint32_t command = data; - if (strict) { - if (address != manufacturer) // Verify the Manufacturer code. - return false; - // Verify the checksum. - uint8_t checksumOrig = data; - uint8_t checksumCalc = (data >> 24) ^ (data >> 16) ^ (data >> 8); - if (checksumOrig != checksumCalc) return false; - } - - // Success - results->value = data; - results->address = address; - results->command = command; - results->decode_type = decode_type_t::PANASONIC; - results->bits = nbits; - return true; -} -#endif // (DECODE_PANASONIC || DECODE_DENON) - -#if SEND_PANASONIC_AC -/// Send a Panasonic A/C message. -/// Status: STABLE / Work with real device(s). -/// @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::sendPanasonicAC(const uint8_t data[], const uint16_t nbytes, - const uint16_t repeat) { - if (nbytes < kPanasonicAcSection1Length) return; - for (uint16_t r = 0; r <= repeat; r++) { - // First section. (8 bytes) - sendGeneric(kPanasonicHdrMark, kPanasonicHdrSpace, kPanasonicBitMark, - kPanasonicOneSpace, kPanasonicBitMark, kPanasonicZeroSpace, - kPanasonicBitMark, kPanasonicAcSectionGap, data, - kPanasonicAcSection1Length, kPanasonicFreq, false, 0, 50); - // First section. (The rest of the data bytes) - sendGeneric(kPanasonicHdrMark, kPanasonicHdrSpace, kPanasonicBitMark, - kPanasonicOneSpace, kPanasonicBitMark, kPanasonicZeroSpace, - kPanasonicBitMark, kPanasonicAcMessageGap, - data + kPanasonicAcSection1Length, - nbytes - kPanasonicAcSection1Length, kPanasonicFreq, false, 0, - 50); - } -} -#endif // SEND_PANASONIC_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? -IRPanasonicAc::IRPanasonicAc(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 IRPanasonicAc::stateReset(void) { - memcpy(remote_state, kPanasonicKnownGoodState, kPanasonicAcStateLength); - _temp = 25; // An initial saved desired temp. Completely made up. - _swingh = kPanasonicAcSwingHMiddle; // A similar made up value for H Swing. -} - -/// Set up hardware to be able to send a message. -void IRPanasonicAc::begin(void) { _irsend.begin(); } - -/// 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 IRPanasonicAc::validChecksum(const uint8_t *state, const uint16_t length) { - if (length < 2) return false; // 1 byte of data can't have a checksum. - return (state[length - 1] == - sumBytes(state, length - 1, kPanasonicAcChecksumInit)); -} - -/// Calculate the checksum for a given state. -/// @param[in] state The value to calc the checksum of. -/// @param[in] length The size/length of the state. -/// @return The calculated checksum value. -uint8_t IRPanasonicAc::calcChecksum(const uint8_t *state, - const uint16_t length) { - return sumBytes(state, length - 1, kPanasonicAcChecksumInit); -} - -/// Calculate and set the checksum values for the internal state. -/// @param[in] length The size/length of the state. -void IRPanasonicAc::fixChecksum(const uint16_t length) { - remote_state[length - 1] = calcChecksum(remote_state, length); -} - -#if SEND_PANASONIC_AC -/// Send the current internal state as an IR message. -/// @param[in] repeat Nr. of times the message will be repeated. -void IRPanasonicAc::send(const uint16_t repeat) { - _irsend.sendPanasonicAC(getRaw(), kPanasonicAcStateLength, repeat); -} -#endif // SEND_PANASONIC_AC - -/// Set the model of the A/C to emulate. -/// @param[in] model The enum of the appropriate model. -void IRPanasonicAc::setModel(const panasonic_ac_remote_model_t model) { - switch (model) { - case panasonic_ac_remote_model_t::kPanasonicDke: - case panasonic_ac_remote_model_t::kPanasonicJke: - case panasonic_ac_remote_model_t::kPanasonicLke: - case panasonic_ac_remote_model_t::kPanasonicNke: - case panasonic_ac_remote_model_t::kPanasonicCkp: - case panasonic_ac_remote_model_t::kPanasonicRkr: break; - // Only proceed if we know what to do. - default: return; - } - // clear & set the various bits and bytes. - remote_state[13] &= 0xF0; - remote_state[17] = 0x00; - remote_state[21] &= 0b11101111; - remote_state[23] = 0x81; - remote_state[25] = 0x00; - - switch (model) { - case kPanasonicLke: - remote_state[13] |= 0x02; - remote_state[17] = 0x06; - break; - case kPanasonicDke: - remote_state[23] = 0x01; - remote_state[25] = 0x06; - // Has to be done last as setSwingHorizontal has model check built-in - setSwingHorizontal(_swingh); - break; - case kPanasonicNke: - remote_state[17] = 0x06; - break; - case kPanasonicJke: - break; - case kPanasonicCkp: - remote_state[21] |= 0x10; - remote_state[23] = 0x01; - break; - case kPanasonicRkr: - remote_state[13] |= 0x08; - remote_state[23] = 0x89; - default: - break; - } - // Reset the Ion filter. - setIon(getIon()); -} - -/// Get/Detect the model of the A/C. -/// @return The enum of the compatible model. -panasonic_ac_remote_model_t IRPanasonicAc::getModel(void) { - if (remote_state[23] == 0x89) return kPanasonicRkr; - if (remote_state[17] == 0x00) { - if ((remote_state[21] & 0x10) && (remote_state[23] & 0x01)) - return panasonic_ac_remote_model_t::kPanasonicCkp; - if (remote_state[23] & 0x80) - return panasonic_ac_remote_model_t::kPanasonicJke; - } - if (remote_state[17] == 0x06 && (remote_state[13] & 0x0F) == 0x02) - return panasonic_ac_remote_model_t::kPanasonicLke; - if (remote_state[23] == 0x01) - return panasonic_ac_remote_model_t::kPanasonicDke; - if (remote_state[17] == 0x06) - return panasonic_ac_remote_model_t::kPanasonicNke; - return panasonic_ac_remote_model_t::kPanasonicUnknown; // Default -} - -/// 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 *IRPanasonicAc::getRaw(void) { - fixChecksum(); - return remote_state; -} - -/// Set the internal state from a valid code for this protocol. -/// @param[in] state A valid code for this protocol. -void IRPanasonicAc::setRaw(const uint8_t state[]) { - memcpy(remote_state, state, kPanasonicAcStateLength); -} - -/// Control the power state of the A/C unit. -/// @param[in] on true, the setting is on. false, the setting is off. -/// @warning For CKP models, the remote has no memory of the power state the A/C -/// unit should be in. For those models setting this on/true will toggle the -/// power state of the Panasonic A/C unit with the next message. -/// e.g. If the A/C unit is already on, setPower(true) will turn it off. -/// If the A/C unit is already off, setPower(true) will turn it on. -/// `setPower(false)` will leave the A/C power state as it was. -/// For all other models, setPower(true) should set the internal state to -/// turn it on, and setPower(false) should turn it off. -void IRPanasonicAc::setPower(const bool on) { - setBit(&remote_state[13], kPanasonicAcPowerOffset, on); -} - -/// Get the A/C power state of the remote. -/// @return true, the setting is on. false, the setting is off. -/// @warning Except for CKP models, where it returns if the power state will be -/// toggled on the A/C unit when the next message is sent. -bool IRPanasonicAc::getPower(void) { - return GETBIT8(remote_state[13], kPanasonicAcPowerOffset); -} - -/// Change the power setting to On. -void IRPanasonicAc::on(void) { setPower(true); } - -/// Change the power setting to Off. -void IRPanasonicAc::off(void) { setPower(false); } - -/// Get the operating mode setting of the A/C. -/// @return The current operating mode setting. -uint8_t IRPanasonicAc::getMode(void) { - return GETBITS8(remote_state[13], kHighNibble, kModeBitsSize); -} - -/// Set the operating mode of the A/C. -/// @param[in] desired The desired operating mode. -void IRPanasonicAc::setMode(const uint8_t desired) { - uint8_t mode = kPanasonicAcAuto; // Default to Auto mode. - switch (desired) { - case kPanasonicAcFan: - // Allegedly Fan mode has a temperature of 27. - setTemp(kPanasonicAcFanModeTemp, false); - mode = desired; - break; - case kPanasonicAcAuto: - case kPanasonicAcCool: - case kPanasonicAcHeat: - case kPanasonicAcDry: - mode = desired; - // Set the temp to the saved temp, just incase our previous mode was Fan. - setTemp(_temp); - break; - } - remote_state[13] &= 0x0F; // Clear the previous mode bits. - setBits(&remote_state[13], kHighNibble, kModeBitsSize, mode); -} - -/// Get the current temperature setting. -/// @return The current setting for temp. in degrees celsius. -uint8_t IRPanasonicAc::getTemp(void) { - return GETBITS8(remote_state[14], kPanasonicAcTempOffset, - kPanasonicAcTempSize); -} - -/// Set the temperature. -/// @param[in] celsius The temperature in degrees celsius. -/// @param[in] remember: A flag for the class to remember the temperature. -/// @note Automatically safely limits the temp to the operating range supported. -void IRPanasonicAc::setTemp(const uint8_t celsius, const bool remember) { - uint8_t temperature; - temperature = std::max(celsius, kPanasonicAcMinTemp); - temperature = std::min(temperature, kPanasonicAcMaxTemp); - if (remember) _temp = temperature; - setBits(&remote_state[14], kPanasonicAcTempOffset, kPanasonicAcTempSize, - temperature); -} - -/// Get the current vertical swing setting. -/// @return The current position it is set to. -uint8_t IRPanasonicAc::getSwingVertical(void) { - return GETBITS8(remote_state[16], kLowNibble, kNibbleSize); -} - -/// Control the vertical swing setting. -/// @param[in] desired_elevation The position to set the vertical swing to. -void IRPanasonicAc::setSwingVertical(const uint8_t desired_elevation) { - uint8_t elevation = desired_elevation; - if (elevation != kPanasonicAcSwingVAuto) { - elevation = std::max(elevation, kPanasonicAcSwingVHighest); - elevation = std::min(elevation, kPanasonicAcSwingVLowest); - } - setBits(&remote_state[16], kLowNibble, kNibbleSize, elevation); -} - -/// Get the current horizontal swing setting. -/// @return The current position it is set to. -uint8_t IRPanasonicAc::getSwingHorizontal(void) { - return GETBITS8(remote_state[17], kLowNibble, kNibbleSize); -} - -/// Control the horizontal swing setting. -/// @param[in] desired_direction The position to set the horizontal swing to. -void IRPanasonicAc::setSwingHorizontal(const uint8_t desired_direction) { - switch (desired_direction) { - case kPanasonicAcSwingHAuto: - case kPanasonicAcSwingHMiddle: - case kPanasonicAcSwingHFullLeft: - case kPanasonicAcSwingHLeft: - case kPanasonicAcSwingHRight: - case kPanasonicAcSwingHFullRight: break; - // Ignore anything that isn't valid. - default: return; - } - _swingh = desired_direction; // Store the direction for later. - uint8_t direction = desired_direction; - switch (getModel()) { - case kPanasonicDke: - case kPanasonicRkr: - break; - case kPanasonicNke: - case kPanasonicLke: - direction = kPanasonicAcSwingHMiddle; - break; - default: // Ignore everything else. - return; - } - setBits(&remote_state[17], kLowNibble, kNibbleSize, direction); -} - -/// Set the speed of the fan. -/// @param[in] speed The desired setting. -void IRPanasonicAc::setFan(const uint8_t speed) { - switch (speed) { - case kPanasonicAcFanMin: - case kPanasonicAcFanLow: - case kPanasonicAcFanMed: - case kPanasonicAcFanHigh: - case kPanasonicAcFanMax: - case kPanasonicAcFanAuto: - setBits(&remote_state[16], kHighNibble, kNibbleSize, - speed + kPanasonicAcFanDelta); - break; - default: setFan(kPanasonicAcFanAuto); - } -} - -/// Get the current fan speed setting. -/// @return The current fan speed. -uint8_t IRPanasonicAc::getFan(void) { - return GETBITS8(remote_state[16], kHighNibble, kNibbleSize) - - kPanasonicAcFanDelta; -} - -/// Get the Quiet setting of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRPanasonicAc::getQuiet(void) { - switch (getModel()) { - case kPanasonicRkr: - case kPanasonicCkp: - return GETBIT8(remote_state[21], kPanasonicAcQuietCkpOffset); - default: - return GETBIT8(remote_state[21], kPanasonicAcQuietOffset); - } -} - -/// Set the Quiet setting of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRPanasonicAc::setQuiet(const bool on) { - uint8_t offset; - switch (getModel()) { - case kPanasonicRkr: - case kPanasonicCkp: offset = kPanasonicAcQuietCkpOffset; break; - default: offset = kPanasonicAcQuietOffset; - } - if (on) setPowerful(false); // Powerful is mutually exclusive. - setBit(&remote_state[21], offset, on); -} - -/// Get the Powerful (Turbo) setting of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRPanasonicAc::getPowerful(void) { - switch (getModel()) { - case kPanasonicRkr: - case kPanasonicCkp: - return GETBIT8(remote_state[21], kPanasonicAcPowerfulCkpOffset); - default: - return GETBIT8(remote_state[21], kPanasonicAcPowerfulOffset); - } -} - -/// Set the Powerful (Turbo) setting of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRPanasonicAc::setPowerful(const bool on) { - uint8_t offset; - switch (getModel()) { - case kPanasonicRkr: - case kPanasonicCkp: offset = kPanasonicAcPowerfulCkpOffset; break; - default: offset = kPanasonicAcPowerfulOffset; - } - - if (on) setQuiet(false); // Quiet is mutually exclusive. - setBit(&remote_state[21], offset, on); -} - -/// Convert standard (military/24hr) time to nr. of minutes since midnight. -/// @param[in] hours The hours component of the time. -/// @param[in] mins The minutes component of the time. -/// @return The nr of minutes since midnight. -uint16_t IRPanasonicAc::encodeTime(const uint8_t hours, const uint8_t mins) { - return std::min(hours, (uint8_t)23) * 60 + std::min(mins, (uint8_t)59); -} - -/// Get the time from a given pointer location. -/// @param[in] ptr A pointer to a time location in a state. -/// @return The time expressed as nr. of minutes past midnight. -/// @note Internal use only. -uint16_t IRPanasonicAc::_getTime(const uint8_t ptr[]) { - uint16_t result = (GETBITS8( - ptr[1], kLowNibble, kPanasonicAcTimeOverflowSize) << - (kPanasonicAcTimeSize - kPanasonicAcTimeOverflowSize)) + ptr[0]; - if (result == kPanasonicAcTimeSpecial) return 0; - return result; -} - -/// Get the current clock time value. -/// @return The time expressed as nr. of minutes past midnight. -uint16_t IRPanasonicAc::getClock(void) { return _getTime(&remote_state[24]); } - -/// Set the time at a given pointer location. -/// @param[in, out] ptr A pointer to a time location in a state. -/// @param[in] mins_since_midnight The time as nr. of minutes past midnight. -/// @param[in] round_down Do we round to the nearest 10 minute mark? -/// @note Internal use only. -void IRPanasonicAc::_setTime(uint8_t * const ptr, - const uint16_t mins_since_midnight, - const bool round_down) { - uint16_t corrected = std::min(mins_since_midnight, kPanasonicAcTimeMax); - if (round_down) corrected -= corrected % 10; - if (mins_since_midnight == kPanasonicAcTimeSpecial) - corrected = kPanasonicAcTimeSpecial; - ptr[0] = corrected; - setBits(&ptr[1], kLowNibble, kPanasonicAcTimeOverflowSize, - corrected >> (kPanasonicAcTimeSize - kPanasonicAcTimeOverflowSize)); -} - -/// Set the current clock time value. -/// @param[in] mins_since_midnight The time as nr. of minutes past midnight. -void IRPanasonicAc::setClock(const uint16_t mins_since_midnight) { - _setTime(&remote_state[24], mins_since_midnight, false); -} - -/// Get the On Timer time value. -/// @return The time expressed as nr. of minutes past midnight. -uint16_t IRPanasonicAc::getOnTimer(void) { return _getTime(&remote_state[18]); } - -/// Set/Enable the On Timer. -/// @param[in] mins_since_midnight The time as nr. of minutes past midnight. -/// @param[in] enable Do we enable the timer or not? -void IRPanasonicAc::setOnTimer(const uint16_t mins_since_midnight, - const bool enable) { - // Set the timer flag. - setBit(&remote_state[13], kPanasonicAcOnTimerOffset, enable); - // Store the time. - _setTime(&remote_state[18], mins_since_midnight, true); -} - -/// Cancel the On Timer. -void IRPanasonicAc::cancelOnTimer(void) { setOnTimer(0, false); } - -/// Check if the On Timer is Enabled. -/// @return true, the setting is on. false, the setting is off. -bool IRPanasonicAc::isOnTimerEnabled(void) { - return GETBIT8(remote_state[13], kPanasonicAcOnTimerOffset); -} - -/// Get the Off Timer time value. -/// @return The time expressed as nr. of minutes past midnight. -uint16_t IRPanasonicAc::getOffTimer(void) { - uint16_t result = (GETBITS8(remote_state[20], 0, 7) << kNibbleSize) | - GETBITS8(remote_state[19], kHighNibble, kNibbleSize); - if (result == kPanasonicAcTimeSpecial) return 0; - return result; -} - -/// Set/Enable the Off Timer. -/// @param[in] mins_since_midnight The time as nr. of minutes past midnight. -/// @param[in] enable Do we enable the timer or not? -void IRPanasonicAc::setOffTimer(const uint16_t mins_since_midnight, - const bool enable) { - // Ensure its on a 10 minute boundary and no overflow. - uint16_t corrected = std::min(mins_since_midnight, kPanasonicAcTimeMax); - corrected -= corrected % 10; - if (mins_since_midnight == kPanasonicAcTimeSpecial) - corrected = kPanasonicAcTimeSpecial; - // Set the timer flag. - setBit(&remote_state[13], kPanasonicAcOffTimerOffset, enable); - // Store the time. - setBits(&remote_state[19], kHighNibble, kNibbleSize, corrected); - setBits(&remote_state[20], 0, 7, corrected >> kNibbleSize); -} - -/// Cancel the Off Timer. -void IRPanasonicAc::cancelOffTimer(void) { setOffTimer(0, false); } - -/// Check if the Off Timer is Enabled. -/// @return true, the setting is on. false, the setting is off. -bool IRPanasonicAc::isOffTimerEnabled(void) { - return GETBIT8(remote_state[13], kPanasonicAcOffTimerOffset); -} - -/// Get the Ion (filter) setting of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRPanasonicAc::getIon(void) { - switch (getModel()) { - case kPanasonicDke: - return GETBIT8(remote_state[kPanasonicAcIonFilterByte], - kPanasonicAcIonFilterOffset); - default: - return false; - } -} - -/// Set the Ion (filter) setting of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRPanasonicAc::setIon(const bool on) { - if (getModel() == kPanasonicDke) - setBit(&remote_state[kPanasonicAcIonFilterByte], - kPanasonicAcIonFilterOffset, on); -} - -/// 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 IRPanasonicAc::convertMode(const stdAc::opmode_t mode) { - switch (mode) { - case stdAc::opmode_t::kCool: return kPanasonicAcCool; - case stdAc::opmode_t::kHeat: return kPanasonicAcHeat; - case stdAc::opmode_t::kDry: return kPanasonicAcDry; - case stdAc::opmode_t::kFan: return kPanasonicAcFan; - default: return kPanasonicAcAuto; - } -} - -/// 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 IRPanasonicAc::convertFan(const stdAc::fanspeed_t speed) { - switch (speed) { - case stdAc::fanspeed_t::kMin: return kPanasonicAcFanMin; - case stdAc::fanspeed_t::kLow: return kPanasonicAcFanLow; - case stdAc::fanspeed_t::kMedium: return kPanasonicAcFanMed; - case stdAc::fanspeed_t::kHigh: return kPanasonicAcFanHigh; - case stdAc::fanspeed_t::kMax: return kPanasonicAcFanMax; - default: return kPanasonicAcFanAuto; - } -} - -/// Convert a standard A/C vertical swing into its native setting. -/// @param[in] position A stdAc::swingv_t position to convert. -/// @return The equivalent native horizontal swing position. -uint8_t IRPanasonicAc::convertSwingV(const stdAc::swingv_t position) { - switch (position) { - case stdAc::swingv_t::kHighest: - case stdAc::swingv_t::kHigh: - case stdAc::swingv_t::kMiddle: - case stdAc::swingv_t::kLow: - case stdAc::swingv_t::kLowest: return (uint8_t)position; - default: return kPanasonicAcSwingVAuto; - } -} - -/// Convert a standard A/C horizontal swing into its native setting. -/// @param[in] position A stdAc::swingh_t position to convert. -/// @return The equivalent native horizontal swing position. -uint8_t IRPanasonicAc::convertSwingH(const stdAc::swingh_t position) { - switch (position) { - case stdAc::swingh_t::kLeftMax: return kPanasonicAcSwingHFullLeft; - case stdAc::swingh_t::kLeft: return kPanasonicAcSwingHLeft; - case stdAc::swingh_t::kMiddle: return kPanasonicAcSwingHMiddle; - case stdAc::swingh_t::kRight: return kPanasonicAcSwingHRight; - case stdAc::swingh_t::kRightMax: return kPanasonicAcSwingHFullRight; - default: return kPanasonicAcSwingHAuto; - } -} - -/// 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 IRPanasonicAc::toCommonMode(const uint8_t mode) { - switch (mode) { - case kPanasonicAcCool: return stdAc::opmode_t::kCool; - case kPanasonicAcHeat: return stdAc::opmode_t::kHeat; - case kPanasonicAcDry: return stdAc::opmode_t::kDry; - case kPanasonicAcFan: return stdAc::opmode_t::kFan; - default: return stdAc::opmode_t::kAuto; - } -} - -/// Convert a native fan speed into its stdAc equivalent. -/// @param[in] spd The native setting to be converted. -/// @return The stdAc equivalent of the native setting. -stdAc::fanspeed_t IRPanasonicAc::toCommonFanSpeed(const uint8_t spd) { - switch (spd) { - case kPanasonicAcFanMax: return stdAc::fanspeed_t::kMax; - case kPanasonicAcFanHigh: return stdAc::fanspeed_t::kHigh; - case kPanasonicAcFanMed: return stdAc::fanspeed_t::kMedium; - case kPanasonicAcFanLow: return stdAc::fanspeed_t::kLow; - case kPanasonicAcFanMin: return stdAc::fanspeed_t::kMin; - default: return stdAc::fanspeed_t::kAuto; - } -} - -/// Convert a native horizontal swing postion to it's common equivalent. -/// @param[in] pos A native position to convert. -/// @return The common horizontal swing position. -stdAc::swingh_t IRPanasonicAc::toCommonSwingH(const uint8_t pos) { - switch (pos) { - case kPanasonicAcSwingHFullLeft: return stdAc::swingh_t::kLeftMax; - case kPanasonicAcSwingHLeft: return stdAc::swingh_t::kLeft; - case kPanasonicAcSwingHMiddle: return stdAc::swingh_t::kMiddle; - case kPanasonicAcSwingHRight: return stdAc::swingh_t::kRight; - case kPanasonicAcSwingHFullRight: return stdAc::swingh_t::kRightMax; - default: return stdAc::swingh_t::kAuto; - } -} - -/// 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 IRPanasonicAc::toCommonSwingV(const uint8_t pos) { - if (pos >= kPanasonicAcSwingVHighest && pos <= kPanasonicAcSwingVLowest) - return (stdAc::swingv_t)pos; - else - return stdAc::swingv_t::kAuto; -} - -/// Convert the current internal state into its stdAc::state_t equivalent. -/// @return The stdAc equivalent of the native settings. -stdAc::state_t IRPanasonicAc::toCommon(void) { - stdAc::state_t result; - result.protocol = decode_type_t::PANASONIC_AC; - result.model = getModel(); - result.power = getPower(); - result.mode = toCommonMode(getMode()); - result.celsius = true; - result.degrees = getTemp(); - result.fanspeed = toCommonFanSpeed(getFan()); - result.swingv = toCommonSwingV(getSwingVertical()); - result.swingh = toCommonSwingH(getSwingHorizontal()); - result.quiet = getQuiet(); - result.turbo = getPowerful(); - result.filter = getIon(); - // Not supported. - result.econo = false; - result.clean = false; - result.light = false; - result.beep = false; - result.sleep = -1; - result.clock = -1; - return result; -} - -/// Convert the internal state into a human readable string. -/// @return A string containing the settings in human-readable form. -String IRPanasonicAc::toString(void) { - String result = ""; - result.reserve(180); // Reserve some heap for the string to reduce fragging. - result += addModelToString(decode_type_t::PANASONIC_AC, getModel(), false); - result += addBoolToString(getPower(), kPowerStr); - result += addModeToString(getMode(), kPanasonicAcAuto, kPanasonicAcCool, - kPanasonicAcHeat, kPanasonicAcDry, kPanasonicAcFan); - result += addTempToString(getTemp()); - result += addFanToString(getFan(), kPanasonicAcFanHigh, kPanasonicAcFanLow, - kPanasonicAcFanAuto, kPanasonicAcFanMin, - kPanasonicAcFanMed, kPanasonicAcFanMax); - result += addSwingVToString(getSwingVertical(), kPanasonicAcSwingVAuto, - kPanasonicAcSwingVHighest, - kPanasonicAcSwingVHigh, - kPanasonicAcSwingVAuto, // Upper Middle is unused - kPanasonicAcSwingVMiddle, - kPanasonicAcSwingVAuto, // Lower Middle is unused - kPanasonicAcSwingVLow, - kPanasonicAcSwingVLowest, - // Below are unused. - kPanasonicAcSwingVAuto, - kPanasonicAcSwingVAuto, - kPanasonicAcSwingVAuto, - kPanasonicAcSwingVAuto); - switch (getModel()) { - case kPanasonicJke: - case kPanasonicCkp: - break; // No Horizontal Swing support. - default: - result += addSwingHToString(getSwingHorizontal(), kPanasonicAcSwingHAuto, - kPanasonicAcSwingHFullLeft, - kPanasonicAcSwingHLeft, - kPanasonicAcSwingHMiddle, - kPanasonicAcSwingHRight, - kPanasonicAcSwingHFullRight, - // Below are unused. - kPanasonicAcSwingHAuto, - kPanasonicAcSwingHAuto, - kPanasonicAcSwingHAuto, - kPanasonicAcSwingHAuto, - kPanasonicAcSwingHAuto); - } - result += addBoolToString(getQuiet(), kQuietStr); - result += addBoolToString(getPowerful(), kPowerfulStr); - if (getModel() == kPanasonicDke) - result += addBoolToString(getIon(), kIonStr); - result += addLabeledString(minsToString(getClock()), kClockStr); - result += addLabeledString( - isOnTimerEnabled() ? minsToString(getOnTimer()) : kOffStr, - kOnTimerStr); - result += addLabeledString( - isOffTimerEnabled() ? minsToString(getOffTimer()) : kOffStr, - kOffTimerStr); - return result; -} - -#if DECODE_PANASONIC_AC -/// Decode the supplied Panasonic AC message. -/// Status: STABLE / Works with real device(s). -/// @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::decodePanasonicAC(decode_results *results, uint16_t offset, - const uint16_t nbits, const bool strict) { - uint8_t min_nr_of_messages = 1; - if (strict) { - if (nbits != kPanasonicAcBits && nbits != kPanasonicAcShortBits) - return false; // Not strictly a PANASONIC_AC message. - } - - if (results->rawlen <= - min_nr_of_messages * (2 * nbits + kHeader + kFooter) - 1 + offset) - return false; // Can't possibly be a valid PANASONIC_AC message. - - // Match Header + Data #1 + Footer - uint16_t used; - used = matchGeneric(results->rawbuf + offset, results->state, - results->rawlen - offset, kPanasonicAcSection1Length * 8, - kPanasonicHdrMark, kPanasonicHdrSpace, - kPanasonicBitMark, kPanasonicOneSpace, - kPanasonicBitMark, kPanasonicZeroSpace, - kPanasonicBitMark, kPanasonicAcSectionGap, false, - kPanasonicAcTolerance, kPanasonicAcExcess, false); - if (!used) return false; - offset += used; - - // Match Header + Data #2 + Footer - if (!matchGeneric(results->rawbuf + offset, - results->state + kPanasonicAcSection1Length, - results->rawlen - offset, - nbits - kPanasonicAcSection1Length * 8, - kPanasonicHdrMark, kPanasonicHdrSpace, - kPanasonicBitMark, kPanasonicOneSpace, - kPanasonicBitMark, kPanasonicZeroSpace, - kPanasonicBitMark, kPanasonicAcMessageGap, true, - kPanasonicAcTolerance, kPanasonicAcExcess, false)) - return false; - // Compliance - if (strict) { - // Check the signatures of the section blocks. They start with 0x02& 0x20. - if (results->state[0] != 0x02 || results->state[1] != 0x20 || - results->state[8] != 0x02 || results->state[9] != 0x20) - return false; - if (!IRPanasonicAc::validChecksum(results->state, nbits / 8)) return false; - } - - // Success - results->decode_type = decode_type_t::PANASONIC_AC; - results->bits = nbits; - return true; -} -#endif // DECODE_PANASONIC_AC - -#if SEND_PANASONIC_AC32 -/// Send a Panasonic AC 32/16bit formatted message. -/// Status: STABLE / Confirmed working. -/// @param[in] data containing the IR command. -/// @param[in] nbits Nr. of bits to send. Usually kPanasonicAc32Bits -/// @param[in] repeat Nr. of times the message is to be repeated. -/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1307 -void IRsend::sendPanasonicAC32(const uint64_t data, const uint16_t nbits, - const uint16_t repeat) { - uint16_t section_bits; - uint16_t sections; - uint16_t blocks; - // Calculate the section, block, and bit sizes based on the requested bit size - if (nbits > kPanasonicAc32Bits / 2) { // A long message - section_bits = nbits / kPanasonicAc32Sections; - sections = kPanasonicAc32Sections; - blocks = kPanasonicAc32BlocksPerSection; - } else { // A short message - section_bits = nbits; - sections = kPanasonicAc32Sections - 1; - blocks = kPanasonicAc32BlocksPerSection + 1; - } - for (uint16_t r = 0; r <= repeat; r++) { - for (uint8_t section = 0; section < sections; section++) { - uint64_t section_data; - section_data = GETBITS64(data, section_bits * (sections - section - 1), - section_bits); - - // Duplicate bytes in the data. - uint64_t expanded_data = 0; - for (uint8_t i = 0; i < sizeof(expanded_data); i++) { - const uint8_t first_byte = section_data >> 56; - for (uint8_t i = 0; i < 2; i++) - expanded_data = (expanded_data << 8) | first_byte; - section_data <<= 8; - } - // Two data blocks per section (i.e. 1 + a repeat) - sendGeneric(kPanasonicAc32HdrMark, kPanasonicAc32HdrSpace, // Header - kPanasonicAc32BitMark, kPanasonicAc32OneSpace, // Data - kPanasonicAc32BitMark, kPanasonicAc32ZeroSpace, - 0, 0, // No Footer - expanded_data, section_bits * 2, kPanasonicFreq, false, - blocks - 1, // Repeat - 50); - // Section Footer - sendGeneric(kPanasonicAc32HdrMark, kPanasonicAc32HdrSpace, // Header - 0, 0, 0, 0, // No Data - kPanasonicAc32BitMark, kPanasonicAc32SectionGap, // Footer - data, 0, // No data (bits) - kPanasonicFreq, true, 0, 50); - } - } -} -#endif // SEND_PANASONIC_AC32 - -#if DECODE_PANASONIC_AC32 -/// Decode the supplied Panasonic AC 32/16bit 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. -/// Typically: kPanasonicAc32Bits or kPanasonicAc32Bits/2 -/// @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/1307 -/// @note Protocol has two known configurations: -/// (long) -/// Two sections of identical 32 bit data block pairs. ie. (32+32)+(32+32)=128 -/// or -/// (short) -/// A single section of 3 x identical 32 bit data blocks i.e. (32+32+32)=96 -/// Each data block also has a pair of 8 bits repeated identical bits. -/// e.g. (8+8)+(8+8)=32 -/// -/// So each long version really only has 32 unique bits, and the short version -/// really only has 16 unique bits. -bool IRrecv::decodePanasonicAC32(decode_results *results, uint16_t offset, - const uint16_t nbits, const bool strict) { - if (strict && (nbits != kPanasonicAc32Bits && - nbits != kPanasonicAc32Bits / 2)) - return false; // Not strictly a valid bit size. - - // Determine if this is a long or a short message we are looking for. - const bool is_long = (nbits > kPanasonicAc32Bits / 2); - const uint16_t min_length = is_long ? - kPanasonicAc32Sections * kPanasonicAc32BlocksPerSection * - ((2 * nbits) + kHeader + kFooter) - 1 + offset : - (kPanasonicAc32BlocksPerSection + 1) * ((4 * nbits) + kHeader) + - kFooter - 1 + offset; - - if (results->rawlen < min_length) - return false; // Can't possibly be a valid message. - - // Calculate the parameters for the decode based on it's length. - uint16_t sections; - uint16_t blocks_per_section; - if (is_long) { - sections = kPanasonicAc32Sections; - blocks_per_section = kPanasonicAc32BlocksPerSection; - } else { - sections = kPanasonicAc32Sections - 1; - blocks_per_section = kPanasonicAc32BlocksPerSection + 1; - } - const uint16_t bits_per_block = nbits / sections; - - uint64_t data = 0; - uint64_t section_data = 0; - uint32_t prev_section_data; - - // Match all the expected data blocks. - for (uint16_t block = 0; - block < sections * blocks_per_section; - block++) { - prev_section_data = section_data; - uint16_t used = matchGeneric(results->rawbuf + offset, §ion_data, - results->rawlen - offset, bits_per_block * 2, - kPanasonicAc32HdrMark, kPanasonicAc32HdrSpace, - kPanasonicAc32BitMark, kPanasonicAc32OneSpace, - kPanasonicAc32BitMark, kPanasonicAc32ZeroSpace, - 0, 0, // No Footer - false, kUseDefTol, kMarkExcess, false); - if (!used) return false; - offset += used; - // Is it the first block of the section? - if (block % blocks_per_section == 0) { - // The protocol repeats each byte twice, so to shrink the code we - // remove the duplicate bytes in the collected data. We only need to do - // this for the first block in a section. - uint64_t shrunk_data = 0; - uint64_t data_copy = section_data; - for (uint8_t i = 0; i < sizeof(data_copy); i += 2) { - const uint8_t first_byte = GETBITS64(data_copy, - (sizeof(data_copy) - 1) * 8, 8); - shrunk_data = (shrunk_data << 8) | first_byte; - // Compliance - if (strict) { - // Every second byte must be a duplicate of the previous. - const uint8_t next_byte = GETBITS64(data_copy, - (sizeof(data_copy) - 2) * 8, 8); - if (first_byte != next_byte) return false; - } - data_copy <<= 16; - } - // Keep the data from the first of the block in the section. - data = (data << bits_per_block) | shrunk_data; - } else { // Not the first block in a section. - // Compliance - if (strict) - // Compare the data from the blocks in pairs. - if (section_data != prev_section_data) return false; - // Look for the section footer at the end of the blocks. - if ((block + 1) % blocks_per_section == 0) { - uint64_t junk; - used = matchGeneric(results->rawbuf + offset, &junk, - results->rawlen - offset, 0, - // Header - kPanasonicAc32HdrMark, kPanasonicAc32HdrSpace, - // No Data - 0, 0, - 0, 0, - // Footer - kPanasonicAc32BitMark, kPanasonicAc32SectionGap, - true); - if (!used) return false; - offset += used; - } - } - } - - // Success - results->value = data; - results->decode_type = decode_type_t::PANASONIC_AC32; - results->bits = nbits; - results->address = 0; - results->command = 0; - return true; -} -#endif // DECODE_PANASONIC_AC32 - -/// 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? -IRPanasonicAc32::IRPanasonicAc32(const uint16_t pin, const bool inverted, - const bool use_modulation) - : _irsend(pin, inverted, use_modulation) { stateReset(); } - -#if SEND_PANASONIC_AC32 -/// Send the current internal state as IR messages. -/// @param[in] repeat Nr. of times the message will be repeated. -void IRPanasonicAc32::send(const uint16_t repeat) { - _irsend.sendPanasonicAC32(getRaw(), kPanasonicAc32Bits, repeat); -} -#endif // SEND_PANASONIC_AC32 - -/// Set up hardware to be able to send a message. -void IRPanasonicAc32::begin(void) { _irsend.begin(); } - -/// 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 IRPanasonicAc32::getRaw(void) const { return _.raw; } - -/// Set the internal state from a valid code for this protocol. -/// @param[in] state A valid code for this protocol. -void IRPanasonicAc32::setRaw(const uint32_t state) { _.raw = state; } - -/// Reset the state of the remote to a known good state/sequence. -void IRPanasonicAc32::stateReset(void) { setRaw(kPanasonicAc32KnownGood); } - -/// Set the Power Toggle setting of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRPanasonicAc32::setPowerToggle(const bool on) { _.PowerToggle = !on; } - -/// Get the Power Toggle setting of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRPanasonicAc32::getPowerToggle(void) const { return !_.PowerToggle; } - -/// Set the desired temperature. -/// @param[in] degrees The temperature in degrees celsius. -void IRPanasonicAc32::setTemp(const uint8_t degrees) { - uint8_t temp = std::max((uint8_t)kPanasonicAcMinTemp, degrees); - temp = std::min((uint8_t)kPanasonicAcMaxTemp, temp); - _.Temp = temp - (kPanasonicAcMinTemp - 1); -} - -/// Get the current desired temperature setting. -/// @return The current setting for temp. in degrees celsius. -uint8_t IRPanasonicAc32::getTemp(void) const { - return _.Temp + (kPanasonicAcMinTemp - 1); -} - -/// Get the operating mode setting of the A/C. -/// @return The current operating mode setting. -uint8_t IRPanasonicAc32::getMode(void) const { return _.Mode; } - -/// Set the operating mode of the A/C. -/// @param[in] mode The desired operating mode. -/// @note If we get an unexpected mode, default to AUTO. -void IRPanasonicAc32::setMode(const uint8_t mode) { - switch (mode) { - case kPanasonicAc32Auto: - case kPanasonicAc32Cool: - case kPanasonicAc32Dry: - case kPanasonicAc32Heat: - case kPanasonicAc32Fan: - _.Mode = mode; - break; - default: _.Mode = kPanasonicAc32Auto; - } -} - -/// 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 IRPanasonicAc32::convertMode(const stdAc::opmode_t mode) { - switch (mode) { - case stdAc::opmode_t::kCool: return kPanasonicAc32Cool; - case stdAc::opmode_t::kHeat: return kPanasonicAc32Heat; - case stdAc::opmode_t::kDry: return kPanasonicAc32Dry; - case stdAc::opmode_t::kFan: return kPanasonicAc32Fan; - default: return kPanasonicAc32Auto; - } -} - -/// 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 IRPanasonicAc32::toCommonMode(const uint8_t mode) { - switch (mode) { - case kPanasonicAc32Cool: return stdAc::opmode_t::kCool; - case kPanasonicAc32Heat: return stdAc::opmode_t::kHeat; - case kPanasonicAc32Dry: return stdAc::opmode_t::kDry; - case kPanasonicAc32Fan: return stdAc::opmode_t::kFan; - default: return stdAc::opmode_t::kAuto; - } -} - -/// Set the speed of the fan. -/// @param[in] speed The desired setting. -void IRPanasonicAc32::setFan(const uint8_t speed) { - switch (speed) { - case kPanasonicAc32FanMin: - case kPanasonicAc32FanLow: - case kPanasonicAc32FanMed: - case kPanasonicAc32FanHigh: - case kPanasonicAc32FanMax: - case kPanasonicAc32FanAuto: - _.Fan = speed; - break; - default: _.Fan = kPanasonicAc32FanAuto; - } -} - -/// Get the current fan speed setting. -/// @return The current fan speed. -uint8_t IRPanasonicAc32::getFan(void) const { return _.Fan; } - -/// Convert a native fan speed into its stdAc equivalent. -/// @param[in] spd The native setting to be converted. -/// @return The stdAc equivalent of the native setting. -stdAc::fanspeed_t IRPanasonicAc32::toCommonFanSpeed(const uint8_t spd) { - switch (spd) { - case kPanasonicAc32FanMax: return stdAc::fanspeed_t::kMax; - case kPanasonicAc32FanHigh: return stdAc::fanspeed_t::kHigh; - case kPanasonicAc32FanMed: return stdAc::fanspeed_t::kMedium; - case kPanasonicAc32FanLow: return stdAc::fanspeed_t::kLow; - case kPanasonicAc32FanMin: return stdAc::fanspeed_t::kMin; - default: return stdAc::fanspeed_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 IRPanasonicAc32::convertFan(const stdAc::fanspeed_t speed) { - switch (speed) { - case stdAc::fanspeed_t::kMin: return kPanasonicAc32FanMin; - case stdAc::fanspeed_t::kLow: return kPanasonicAc32FanLow; - case stdAc::fanspeed_t::kMedium: return kPanasonicAc32FanMed; - case stdAc::fanspeed_t::kHigh: return kPanasonicAc32FanHigh; - case stdAc::fanspeed_t::kMax: return kPanasonicAc32FanMax; - default: return kPanasonicAc32FanAuto; - } -} - -/// Get the current horizontal swing setting. -/// @return The current position it is set to. -bool IRPanasonicAc32::getSwingHorizontal(void) const { return _.SwingH; } - -/// Control the horizontal swing setting. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRPanasonicAc32::setSwingHorizontal(const bool on) { _.SwingH = on; } - -/// Get the current vertical swing setting. -/// @return The current position it is set to. -uint8_t IRPanasonicAc32::getSwingVertical(void) const { return _.SwingV; } - -/// Control the vertical swing setting. -/// @param[in] pos The position to set the vertical swing to. -void IRPanasonicAc32::setSwingVertical(const uint8_t pos) { - uint8_t elevation = pos; - if (elevation != kPanasonicAc32SwingVAuto) { - elevation = std::max(elevation, kPanasonicAcSwingVHighest); - elevation = std::min(elevation, kPanasonicAcSwingVLowest); - } - _.SwingV = elevation; -} - -/// 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 IRPanasonicAc32::toCommonSwingV(const uint8_t pos) { - return IRPanasonicAc::toCommonSwingV(pos); -} - -/// Convert a standard A/C vertical swing into its native setting. -/// @param[in] position A stdAc::swingv_t position to convert. -/// @return The equivalent native horizontal swing position. -uint8_t IRPanasonicAc32::convertSwingV(const stdAc::swingv_t position) { - switch (position) { - case stdAc::swingv_t::kHighest: - case stdAc::swingv_t::kHigh: - case stdAc::swingv_t::kMiddle: - case stdAc::swingv_t::kLow: - case stdAc::swingv_t::kLowest: return (uint8_t)position; - default: return kPanasonicAc32SwingVAuto; - } -} - -/// Convert the current internal state into a human readable string. -/// @return A human readable string. -String IRPanasonicAc32::toString(void) const { - String result = ""; - result.reserve(110); - result += addBoolToString(getPowerToggle(), kPowerToggleStr, false); - result += addModeToString(_.Mode, kPanasonicAc32Auto, kPanasonicAc32Cool, - kPanasonicAc32Heat, kPanasonicAc32Dry, - kPanasonicAc32Fan); - result += addTempToString(getTemp()); - result += addFanToString(_.Fan, kPanasonicAc32FanHigh, kPanasonicAc32FanLow, - kPanasonicAc32FanAuto, kPanasonicAc32FanMin, - kPanasonicAc32FanMed, kPanasonicAc32FanMax); - result += addBoolToString(_.SwingH, kSwingHStr); - result += addSwingVToString(getSwingVertical(), - kPanasonicAc32SwingVAuto, - kPanasonicAcSwingVHighest, - kPanasonicAcSwingVHigh, - kPanasonicAc32SwingVAuto, // Upper Middle unused - kPanasonicAcSwingVMiddle, - kPanasonicAc32SwingVAuto, // Lower Middle unused - kPanasonicAcSwingVLow, - kPanasonicAcSwingVLowest, - // Below are unused. - kPanasonicAc32SwingVAuto, - kPanasonicAc32SwingVAuto, - kPanasonicAc32SwingVAuto, - kPanasonicAc32SwingVAuto); - return result; -} - -/// 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 IRPanasonicAc32::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::PANASONIC_AC32; - result.model = -1; - if (getPowerToggle()) result.power = !result.power; - result.mode = toCommonMode(getMode()); - result.celsius = true; - result.degrees = getTemp(); - result.fanspeed = toCommonFanSpeed(getFan()); - result.swingv = toCommonSwingV(getSwingVertical()); - result.swingh = getSwingHorizontal() ? stdAc::swingh_t::kAuto - : stdAc::swingh_t::kOff; - // Not supported. - result.quiet = false; - result.turbo = false; - result.filter = false; - result.econo = false; - result.clean = false; - result.light = false; - result.beep = false; - result.sleep = -1; - result.clock = -1; - return result; -} diff --git a/lib/IRremoteESP8266/src/ir_Panasonic.h b/lib/IRremoteESP8266/src/ir_Panasonic.h index 5668e4a57a..9460a1b0ff 100644 --- a/lib/IRremoteESP8266/src/ir_Panasonic.h +++ b/lib/IRremoteESP8266/src/ir_Panasonic.h @@ -36,10 +36,10 @@ #ifdef ARDUINO #include #endif -#include "IRremoteESP8266.h" -#include "IRsend.h" +#include "../src/IRremoteESP8266.h" +#include "../src/IRsend.h" #ifdef UNIT_TEST -#include "IRsend_test.h" +#include "../src/IRsend_test.h" #endif // Constants diff --git a/lib/IRremoteESP8266/src/ir_Pioneer.cpp b/lib/IRremoteESP8266/src/ir_Pioneer.cpp deleted file mode 100644 index 90f58c6ed9..0000000000 --- a/lib/IRremoteESP8266/src/ir_Pioneer.cpp +++ /dev/null @@ -1,138 +0,0 @@ -// 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 - -// Supports: -// Brand: Pioneer, Model: AV Receivers -// Brand: Pioneer, Model: VSX-324 AV Receiver -// Brand: Pioneer, Model: AXD7690 Remote - -#define __STDC_LIMIT_MACROS -#include -#include -#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. -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 diff --git a/lib/IRremoteESP8266/src/ir_Pronto.cpp b/lib/IRremoteESP8266/src/ir_Pronto.cpp deleted file mode 100644 index 2d4ffa7592..0000000000 --- a/lib/IRremoteESP8266/src/ir_Pronto.cpp +++ /dev/null @@ -1,107 +0,0 @@ -// 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 -#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 diff --git a/lib/IRremoteESP8266/src/ir_RC5_RC6.cpp b/lib/IRremoteESP8266/src/ir_RC5_RC6.cpp deleted file mode 100644 index 1e0c75b3f1..0000000000 --- a/lib/IRremoteESP8266/src/ir_RC5_RC6.cpp +++ /dev/null @@ -1,454 +0,0 @@ -// Copyright 2009 Ken Shirriff -// Copyright 2017 David Conran - -/// @file -/// @brief RC-5 & RC-6 support -/// RC-5 & RC-6 support added from https://github.com/z3t0/Arduino-IRremote -/// RC-5X support added by David Conran -/// @see https://en.wikipedia.org/wiki/RC-5 -/// @see http://www.sbprojects.net/knowledge/ir/rc5.php -/// @see https://en.wikipedia.org/wiki/Manchester_code -/// @see https://en.wikipedia.org/wiki/RC-6 -/// @see https://www.sbprojects.net/knowledge/ir/rc6.php -/// @see http://www.pcbheaven.com/userpages/The_Philips_RC6_Protocol/ -/// @see http://www.righto.com/2010/12/64-bit-rc6-codes-arduino-and-xbox.html - -// Supports: -// Brand: Philips, Model: Standard RC-5 (RC5) -// Brand: Philips, Model: RC-5X (RC5X) -// Brand: Philips, Model: Standard RC-6 (RC6) - -#include -#include "IRrecv.h" -#include "IRsend.h" -#include "IRtimer.h" -#include "IRutils.h" - -// Constants -// RC-5/RC-5X -const uint16_t kRc5T1 = 889; -const uint32_t kRc5MinCommandLength = 113778; -const uint32_t kRc5MinGap = kRc5MinCommandLength - kRC5RawBits * (2 * kRc5T1); -const uint16_t kRc5ToggleMask = 0x800; // The 12th bit. -const uint16_t kRc5SamplesMin = 11; - -// RC-6 -const uint16_t kRc6Tick = 444; -const uint16_t kRc6HdrMarkTicks = 6; -const uint16_t kRc6HdrMark = kRc6HdrMarkTicks * kRc6Tick; -const uint16_t kRc6HdrSpaceTicks = 2; -const uint16_t kRc6HdrSpace = kRc6HdrSpaceTicks * kRc6Tick; -const uint16_t kRc6RptLengthTicks = 187; -const uint32_t kRc6RptLength = kRc6RptLengthTicks * kRc6Tick; -const uint32_t kRc6ToggleMask = 0x10000UL; // The 17th bit. -const uint16_t kRc6_36ToggleMask = 0x8000; // The 16th bit. - -// Common (getRClevel()) -const int16_t kMark = 0; -const int16_t kSpace = 1; - -#if SEND_RC5 -/// Send a Philips RC-5/RC-5X packet. -/// Status: RC-5 (stable), RC-5X (alpha) -/// @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 Caller needs to take care of flipping the toggle bit. -/// That bit differentiates between key press & key release. -/// For RC-5 it is the MSB of the data. -/// For RC-5X it is the 2nd MSB of the data. -/// @todo Testing of the RC-5X components. -void IRsend::sendRC5(const uint64_t data, uint16_t nbits, - const uint16_t repeat) { - if (nbits > sizeof(data) * 8) return; // We can't send something that big. - bool skipSpace = true; - bool field_bit = true; - // Set 36kHz IR carrier frequency & a 1/4 (25%) duty cycle. - enableIROut(36, 25); - - if (nbits >= kRC5XBits) { // Is this a RC-5X message? - // field bit is the inverted MSB of RC-5X data. - field_bit = ((data >> (nbits - 1)) ^ 1) & 1; - nbits--; - } - - IRtimer usecTimer = IRtimer(); - for (uint16_t i = 0; i <= repeat; i++) { - usecTimer.reset(); - - // Header - // First start bit (0x1). space, then mark. - if (skipSpace) - skipSpace = false; // First time through, we assume the leading space(). - else - space(kRc5T1); - mark(kRc5T1); - // Field/Second start bit. - if (field_bit) { // Send a 1. Normal for RC-5. - space(kRc5T1); - mark(kRc5T1); - } else { // Send a 0. Special case for RC-5X. Means 7th command bit is 1. - mark(kRc5T1); - space(kRc5T1); - } - - // Data - for (uint64_t mask = 1ULL << (nbits - 1); mask; mask >>= 1) - if (data & mask) { // 1 - space(kRc5T1); // 1 is space, then mark. - mark(kRc5T1); - } else { // 0 - mark(kRc5T1); // 0 is mark, then space. - space(kRc5T1); - } - // Footer - space(std::max(kRc5MinGap, kRc5MinCommandLength - usecTimer.elapsed())); - } -} - -/// Encode a Philips RC-5 data message. -/// Status: Beta / Should be working. -/// @param[in] address The 5-bit address value for the message. -/// @param[in] command The 6-bit command value for the message. -/// @param[in] key_released Indicate if the remote key has been released. -/// @return A message suitable for use in sendRC5(). -uint16_t IRsend::encodeRC5(const uint8_t address, const uint8_t command, - const bool key_released) { - return (key_released << (kRC5Bits - 1)) | ((address & 0x1f) << 6) | - (command & 0x3F); -} - -/// Encode a Philips RC-5X data message. -/// Status: Beta / Should be working. -/// @param[in] address The 5-bit address value for the message. -/// @param[in] command The 7-bit command value for the message. -/// @param[in] key_released Indicate if the remote key has been released. -/// @return A message suitable for use in sendRC5(). -uint16_t IRsend::encodeRC5X(const uint8_t address, const uint8_t command, - const bool key_released) { - // The 2nd start/field bit (MSB of the return value) is the value of the 7th - // command bit. - bool s2 = (command >> 6) & 1; - return ((uint16_t)s2 << (kRC5XBits - 1)) | - encodeRC5(address, command, key_released); -} - -/// Flip the toggle bit of a Philips RC-5/RC-5X data message. -/// Used to indicate a change of remote button's state. -/// Status: STABLE. -/// @param[in] data The existing RC-5/RC-5X message. -/// @return A data message suitable for use in sendRC5() with the toggle bit -/// flipped. -uint64_t IRsend::toggleRC5(const uint64_t data) { - return data ^ kRc5ToggleMask; -} -#endif // SEND_RC5 - -#if SEND_RC6 -/// Flip the toggle bit of a Philips RC-6 data message. -/// Used to indicate a change of remote button's state. -/// Status: STABLE / Should work fine. -/// @param[in] data The existing RC-6 message. -/// @param [in] nbits Nr. of bits in the RC-6 protocol. -/// @return A data message suitable for use in sendRC6() with the toggle bit -/// flipped. -/// @note For RC-6 (20-bits), it is the 17th least significant bit. -/// @note For RC-6 (36-bits/Xbox-360), it is the 16th least significant bit. -uint64_t IRsend::toggleRC6(const uint64_t data, const uint16_t nbits) { - if (nbits == kRC6_36Bits) return data ^ kRc6_36ToggleMask; - return data ^ kRc6ToggleMask; -} - -/// Encode a Philips RC-6 data message. -/// Status: Beta / Should be working. -/// @param[in] address The address (aka. control) value for the message. -/// Includes the field/mode/toggle bits. -/// @param[in] command The 8-bit command value for the message. -/// (aka. information) -/// @param[in] mode Which protocol to use. -/// Defined by nr. of bits in the protocol. -/// @return A data message suitable for use in `sendRC6()`. -uint64_t IRsend::encodeRC6(const uint32_t address, const uint8_t command, - const uint16_t mode) { - switch (mode) { - case kRC6Mode0Bits: - return ((address & 0xFFF) << 8) | (command & 0xFF); - case kRC6_36Bits: - return ((uint64_t)(address & 0xFFFFFFF) << 8) | (command & 0xFF); - default: - return 0; - } -} - -/// Send a Philips RC-6 packet. -/// Status: Stable. -/// @note Caller needs to take care of flipping the toggle bit (The 4th Most -/// Significant Bit). That bit differentiates between key press & key release. -/// @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::sendRC6(const uint64_t data, const uint16_t nbits, - const uint16_t repeat) { - // Check we can send the number of bits requested. - if (nbits > sizeof(data) * 8) return; - // Set 36kHz IR carrier frequency & a 1/3 (33%) duty cycle. - enableIROut(36, 33); - for (uint16_t r = 0; r <= repeat; r++) { - // Header - mark(kRc6HdrMark); - space(kRc6HdrSpace); - // Start bit. - mark(kRc6Tick); // mark, then space == 0x1. - space(kRc6Tick); - // Data - uint16_t bitTime; - for (uint64_t i = 1, mask = 1ULL << (nbits - 1); mask; i++, mask >>= 1) { - if (i == 4) // The fourth bit we send is a "double width trailer bit". - bitTime = 2 * kRc6Tick; // double-wide trailer bit - else - bitTime = kRc6Tick; // Normal bit - if (data & mask) { // 1 - mark(bitTime); - space(bitTime); - } else { // 0 - space(bitTime); - mark(bitTime); - } - } - // Footer - space(kRc6RptLength); - } -} -#endif // SEND_RC6 - -#if (DECODE_RC5 || DECODE_RC6 || DECODE_LASERTAG) -/// Gets one undecoded level at a time from the raw buffer. -/// The RC5/6 decoding is easier if the data is broken into time intervals. -/// E.g. if the buffer has MARK for 2 time intervals and SPACE for 1, -/// successive calls to getRClevel will return MARK, MARK, SPACE. -/// offset and used are updated to keep track of the current position. -/// @param[in,out] results Ptr to the data to decode and where to store the -/// decode result. -/// @param[in,out] offset Ptr to the currect offset to the rawbuf. -/// @param[in,out] used Ptr to the current used counter. -/// @param[in] bitTime Time interval of single bit in microseconds. -/// @param[in] tolerance Percent tolerance to be used in matching. -/// @param[in] excess Extra useconds to add to Marks & removed from Spaces. -/// @param[in] delta A non-scaling (+/-) error margin (in useconds). -/// @param[in] maxwidth Maximum number of successive levels to find in a single -/// level (default is 3) -/// @return MARK, SPACE, or -1 for error. -/// (The measured time interval is not a multiple of t1.) -/// @see https://en.wikipedia.org/wiki/Manchester_code -int16_t IRrecv::getRClevel(decode_results *results, uint16_t *offset, - uint16_t *used, const uint16_t bitTime, - const uint8_t tolerance, const int16_t excess, - const uint16_t delta, const uint8_t maxwidth) { - DPRINT("DEBUG: getRClevel: offset = "); - DPRINTLN(uint64ToString(*offset)); - DPRINT("DEBUG: getRClevel: rawlen = "); - DPRINTLN(uint64ToString(results->rawlen)); - if (*offset >= results->rawlen) { - DPRINTLN("DEBUG: getRClevel: SPACE, past end of rawbuf"); - return kSpace; // After end of recorded buffer, assume SPACE. - } - uint16_t width = results->rawbuf[*offset]; - // If the value of offset is odd, it's a MARK. Even, it's a SPACE. - uint16_t val = ((*offset) % 2) ? kMark : kSpace; - // Check to see if we have hit an inter-message gap (> 20ms). - if (val == kSpace && - (width > 20000 - delta || width > maxwidth * bitTime + delta)) { - DPRINTLN("DEBUG: getRClevel: SPACE, hit end of mesg gap."); - return kSpace; - } - int16_t correction = (val == kMark) ? excess : -excess; - - // Calculate the look-ahead for our current position in the buffer. - uint16_t avail; - // Note: We want to match in greedy order as the other way leads to - // mismatches due to overlaps induced by the correction and tolerance - // values. - for (avail = maxwidth; avail > 0; avail--) { - if (match(width, avail * bitTime + correction, tolerance, delta)) { - break; - } - } - if (!avail) { - DPRINTLN("DEBUG: getRClevel: Unexpected width. Exiting."); - return -1; // The width is not what we expected. - } - - (*used)++; // Count another one of the avail slots as used. - if (*used >= avail) { // Are we out of look-ahead/avail slots? - // Yes, so reset the used counter, and move the offset ahead. - *used = 0; - (*offset)++; - } - if (val == kMark) { - DPRINTLN("DEBUG: getRClevel: MARK"); - } else { - DPRINTLN("DEBUG: getRClevel: SPACE"); - } - - return val; -} -#endif // (DECODE_RC5 || DECODE_RC6 || DECODE_LASERTAG) - -#if DECODE_RC5 -/// Decode the supplied RC-5/RC5X message. -/// Status: RC-5 (stable), RC-5X (alpha) -/// @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 The 'toggle' bit is included as the 6th (MSB) address bit, the MSB of -/// data, & in the count of bits decoded. -/// @todo Serious testing of the RC-5X and strict aspects needs to be done. -bool IRrecv::decodeRC5(decode_results *results, uint16_t offset, - const uint16_t nbits, const bool strict) { - if (results->rawlen <= kRc5SamplesMin + kHeader - 1 + offset) return false; - - // Compliance - if (strict && nbits != kRC5Bits && nbits != kRC5XBits) - return false; // It's neither RC-5 or RC-5X. - - uint16_t used = 0; - bool is_rc5x = false; - uint64_t data = 0; - - // Header - // Get start bit #1. - if (getRClevel(results, &offset, &used, kRc5T1) != kMark) return false; - // Get field/start bit #2 (inverted bit-7 of the command if RC-5X protocol) - uint16_t actual_bits = 1; - int16_t levelA = getRClevel(results, &offset, &used, kRc5T1); - int16_t levelB = getRClevel(results, &offset, &used, kRc5T1); - if (levelA == kSpace && levelB == kMark) { // Matched a 1. - is_rc5x = false; - } else if (levelA == kMark && levelB == kSpace) { // Matched a 0. - if (nbits <= kRC5Bits) return false; // Field bit must be '1' for RC5. - is_rc5x = true; - data = 1; - } else { - return false; // Not what we expected. - } - - // Data - for (; offset < results->rawlen; actual_bits++) { - int16_t levelA = getRClevel(results, &offset, &used, kRc5T1); - int16_t levelB = getRClevel(results, &offset, &used, kRc5T1); - 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 != kRC5Bits && actual_bits != kRC5XBits) - return false; - - // Success - results->value = data; - results->address = (data >> 6) & 0x1F; - results->command = data & 0x3F; - results->repeat = false; - if (is_rc5x) { - results->decode_type = RC5X; - results->command |= ((uint32_t)is_rc5x) << 6; - } else { - results->decode_type = RC5; - actual_bits--; // RC5 doesn't count the field bit as data. - } - results->bits = actual_bits; - return true; -} -#endif // DECODE_RC5 - -#if DECODE_RC6 -/// Decode the supplied RC6 message. -/// Status: Stable. -/// @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. -/// @todo Testing of the strict compliance aspects. -bool IRrecv::decodeRC6(decode_results *results, uint16_t offset, - const uint16_t nbits, const bool strict) { - if (results->rawlen <= kHeader + 2 + 4 + offset) - // Up to the double-wide T bit. - return false; // Smaller than absolute smallest possible RC6 message. - - if (strict) { // Compliance - // Unlike typical protocols, the ability to have mark+space, and space+mark - // as data bits means it is possible to only have nbits of entries for the - // data portion, rather than the typically required 2 * nbits. - // Also due to potential melding with the start bit, we can only count - // the start bit as 1, instead of a more typical 2 value. The header still - // remains as normal. - if (results->rawlen <= nbits + kHeader + 1 + offset) - return false; // Don't have enough entries/samples to be valid. - switch (nbits) { - case kRC6Mode0Bits: - case kRC6_36Bits: - break; - default: - return false; // Asking for the wrong number of bits. - } - } - - // Header - if (!matchMark(results->rawbuf[offset], kRc6HdrMark)) return false; - // Calculate how long the common tick time is based on the header mark. - uint32_t tick = results->rawbuf[offset++] * kRawTick / kRc6HdrMarkTicks; - if (!matchSpace(results->rawbuf[offset++], kRc6HdrSpaceTicks * tick)) - return false; - - uint16_t used = 0; - - // Get the start bit. e.g. 1. - if (getRClevel(results, &offset, &used, tick) != kMark) return false; - if (getRClevel(results, &offset, &used, tick) != kSpace) return false; - - uint16_t actual_bits; - uint64_t data = 0; - - // Data (Warning: Here be dragons^Wpointers!!) - for (actual_bits = 0; offset < results->rawlen; actual_bits++) { - int16_t levelA, levelB; // Next two levels - levelA = getRClevel(results, &offset, &used, tick); - // T bit is double wide; make sure second half matches - if (actual_bits == 3 && levelA != getRClevel(results, &offset, &used, tick)) - return false; - levelB = getRClevel(results, &offset, &used, tick); - // T bit is double wide; make sure second half matches - if (actual_bits == 3 && levelB != getRClevel(results, &offset, &used, tick)) - return false; - if (levelA == kMark && levelB == kSpace) // reversed compared to RC5 - data = (data << 1) | 1; // 1 - else if (levelA == kSpace && levelB == kMark) - data <<= 1; // 0 - else - break; - } - - // More compliance - if (strict && actual_bits != nbits) - return false; // Actual nr. of bits didn't match expected. - - // Success - results->decode_type = RC6; - results->bits = actual_bits; - results->value = data; - results->address = data >> 8; - results->command = data & 0xFF; - return true; -} -#endif // DECODE_RC6 diff --git a/lib/IRremoteESP8266/src/ir_RCMM.cpp b/lib/IRremoteESP8266/src/ir_RCMM.cpp deleted file mode 100644 index 97a3c79b5b..0000000000 --- a/lib/IRremoteESP8266/src/ir_RCMM.cpp +++ /dev/null @@ -1,164 +0,0 @@ -// Copyright 2017 David Conran - -/// @file -/// @brief Support for the Phillips RC-MM protocol. -/// @see http://www.sbprojects.net/knowledge/ir/rcmm.php - -// Supports: -// Brand: Microsoft, Model: XBOX 360 - -#include -#include "IRrecv.h" -#include "IRsend.h" -#include "IRtimer.h" -#include "IRutils.h" - -// Constants -const uint16_t kRcmmTick = 28; // Technically it would be 27.777* -const uint16_t kRcmmHdrMarkTicks = 15; -const uint16_t kRcmmHdrMark = 416; -const uint16_t kRcmmHdrSpaceTicks = 10; -const uint16_t kRcmmHdrSpace = 277; -const uint16_t kRcmmBitMarkTicks = 6; -const uint16_t kRcmmBitMark = 166; -const uint16_t kRcmmBitSpace0Ticks = 10; -const uint16_t kRcmmBitSpace0 = 277; -const uint16_t kRcmmBitSpace1Ticks = 16; -const uint16_t kRcmmBitSpace1 = 444; -const uint16_t kRcmmBitSpace2Ticks = 22; -const uint16_t kRcmmBitSpace2 = 611; -const uint16_t kRcmmBitSpace3Ticks = 28; -const uint16_t kRcmmBitSpace3 = 777; -const uint16_t kRcmmRptLengthTicks = 992; -const uint32_t kRcmmRptLength = 27778; -const uint16_t kRcmmMinGapTicks = 120; -const uint32_t kRcmmMinGap = 3360; -// Use a tolerance of +/-10% when matching some data spaces. -const uint8_t kRcmmTolerance = 10; -const uint16_t kRcmmExcess = 50; - -#if SEND_RCMM -/// Send a Philips RC-MM packet. -/// 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. -void IRsend::sendRCMM(uint64_t data, uint16_t nbits, uint16_t repeat) { - // Set 36kHz IR carrier frequency & a 1/3 (33%) duty cycle. - enableIROut(36, 33); - IRtimer usecs = IRtimer(); - - for (uint16_t r = 0; r <= repeat; r++) { - usecs.reset(); - // Header - mark(kRcmmHdrMark); - space(kRcmmHdrSpace); - // Data - uint64_t mask = 0b11ULL << (nbits - 2); - // RC-MM sends data 2 bits at a time. - for (int32_t i = nbits; i > 0; i -= 2) { - mark(kRcmmBitMark); - // Grab the next Most Significant Bits to send. - switch ((data & mask) >> (i - 2)) { - case 0b00: - space(kRcmmBitSpace0); - break; - case 0b01: - space(kRcmmBitSpace1); - break; - case 0b10: - space(kRcmmBitSpace2); - break; - case 0b11: - space(kRcmmBitSpace3); - break; - } - mask >>= 2; - } - // Footer - mark(kRcmmBitMark); - // Protocol requires us to wait at least kRcmmRptLength usecs from the - // start or kRcmmMinGap usecs. - space(std::max(kRcmmRptLength - usecs.elapsed(), kRcmmMinGap)); - } -} -#endif // SEND_RCMM - -#if DECODE_RCMM -/// Decode a Philips RC-MM packet (between 12 & 32 bits) if possible. -/// Status: STABLE / Should 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::decodeRCMM(decode_results *results, uint16_t offset, - const uint16_t nbits, const bool strict) { - uint64_t data = 0; - - if (results->rawlen <= 4 + offset - 1) - return false; // Not enough entries to ever be RCMM. - - // Calc the maximum size in bits, the message can be, or that we can accept. - int16_t maxBitSize = - std::min((uint16_t)results->rawlen - 5, (uint16_t)sizeof(data) * 8); - // Compliance - if (strict) { - // Technically the spec says bit sizes should be 12 xor 24. however - // 32 bits has been seen from a device. We are going to assume - // 12 <= bits <= 32 is the 'required' bit length for the spec. - if (maxBitSize < 12 || maxBitSize > 32) return false; - if (maxBitSize < nbits) - return false; // Short cut, we can never reach the expected nr. of bits. - } - // Header decode - if (!matchMark(results->rawbuf[offset], kRcmmHdrMark)) return false; - // Calculate how long the common tick time is based on the header mark. - uint32_t m_tick = results->rawbuf[offset++] * kRawTick / kRcmmHdrMarkTicks; - if (!matchSpace(results->rawbuf[offset], kRcmmHdrSpace)) return false; - // Calculate how long the common tick time is based on the header space. - uint32_t s_tick = results->rawbuf[offset++] * kRawTick / kRcmmHdrSpaceTicks; - - // Data decode - // RC-MM has two bits of data per mark/space pair. - uint16_t actualBits; - for (actualBits = 0; actualBits < maxBitSize; actualBits += 2, offset++) { - if (!match(results->rawbuf[offset++], kRcmmBitMarkTicks * m_tick)) - return false; - - data <<= 2; - // Use non-default tolerance & excess for matching some of the spaces as the - // defaults are too generous and causes mis-matches in some cases. - if (match(results->rawbuf[offset], kRcmmBitSpace0Ticks * s_tick)) - data += 0; - else if (match(results->rawbuf[offset], kRcmmBitSpace1Ticks * s_tick)) - data += 1; - else if (match(results->rawbuf[offset], kRcmmBitSpace2Ticks * s_tick, - kRcmmTolerance)) - data += 2; - else if (match(results->rawbuf[offset], kRcmmBitSpace3Ticks * s_tick, - kRcmmTolerance)) - data += 3; - else - return false; - } - // Footer decode - if (!match(results->rawbuf[offset++], kRcmmBitMarkTicks * m_tick)) - return false; - if (offset < results->rawlen && - !matchAtLeast(results->rawbuf[offset], kRcmmMinGapTicks * s_tick)) - return false; - - // Compliance - if (strict && actualBits != nbits) return false; - - // Success - results->value = data; - results->decode_type = RCMM; - results->bits = actualBits; - results->address = 0; - results->command = 0; - return true; -} -#endif // DECODE_RCMM diff --git a/lib/IRremoteESP8266/src/ir_Rhoss.cpp b/lib/IRremoteESP8266/src/ir_Rhoss.cpp deleted file mode 100644 index f906f49be4..0000000000 --- a/lib/IRremoteESP8266/src/ir_Rhoss.cpp +++ /dev/null @@ -1,364 +0,0 @@ -// Copyright 2021 Tom Rosenback - -/// @file -/// @brief Support for Rhoss protocols. - -#include "ir_Rhoss.h" -#include -#include -#include "IRrecv.h" -#include "IRsend.h" -#include "IRtext.h" -#include "IRutils.h" - -const uint16_t kRhossHdrMark = 3042; -const uint16_t kRhossHdrSpace = 4248; -const uint16_t kRhossBitMark = 648; -const uint16_t kRhossOneSpace = 1545; -const uint16_t kRhossZeroSpace = 457; -const uint32_t kRhossGap = kDefaultMessageGap; -const uint16_t kRhossFreq = 38; - -using irutils::addBoolToString; -using irutils::addModeToString; -using irutils::addFanToString; -using irutils::addTempToString; - -#if SEND_RHOSS -/// Send a Rhoss 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::sendRhoss(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 < kRhossStateLength) return; - - // We always send a message, even for repeat=0, hence '<= repeat'. - for (uint16_t r = 0; r <= repeat; r++) { - sendGeneric(kRhossHdrMark, kRhossHdrSpace, kRhossBitMark, - kRhossOneSpace, kRhossBitMark, kRhossZeroSpace, - kRhossBitMark, kRhossZeroSpace, - data, nbytes, kRhossFreq, false, 0, kDutyDefault); - mark(kRhossBitMark); - // Gap - space(kRhossGap); - } -} -#endif // SEND_RHOSS - -#if DECODE_RHOSS -/// Decode the supplied Rhoss 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::decodeRhoss(decode_results *results, uint16_t offset, - const uint16_t nbits, const bool strict) { - if (strict && nbits != kRhossBits) return false; - - if (results->rawlen <= 2 * nbits + kHeader + kFooter - 1 + offset) { - return false; // Can't possibly be a valid Rhoss message. - } - - uint16_t used; - // Header + Data Block (96 bits) + Footer - used = matchGeneric(results->rawbuf + offset, results->state, - results->rawlen - offset, kRhossBits, - kRhossHdrMark, kRhossHdrSpace, - kRhossBitMark, kRhossOneSpace, - kRhossBitMark, kRhossZeroSpace, - kRhossBitMark, kRhossZeroSpace, - false, kUseDefTol, kMarkExcess, false); - - if (!used) return false; - offset += used; - - // Footer (Part 2) - if (!matchMark(results->rawbuf[offset++], kRhossBitMark)) { - return false; - } - - if (offset < results->rawlen && - !matchAtLeast(results->rawbuf[offset], kRhossGap)) { - return false; - } - - if (strict && !IRRhossAc::validChecksum(results->state)) return false; - - // Success - results->decode_type = decode_type_t::RHOSS; - 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_RHOSS - -/// 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? -IRRhossAc::IRRhossAc(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 IRRhossAc::begin(void) { _irsend.begin(); } - -#if SEND_RHOSS -/// Send the current internal state as an IR message. -/// @param[in] repeat Nr. of times the message will be repeated. -void IRRhossAc::send(const uint16_t repeat) { - _irsend.sendRhoss(getRaw(), kRhossStateLength, repeat); -} -#endif // SEND_RHOSS - -/// 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 IRRhossAc::calcChecksum(const uint8_t state[], const uint16_t length) { - 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 size of the state. -/// @return A boolean indicating if it's checksum is valid. -bool IRRhossAc::validChecksum(const uint8_t state[], const uint16_t length) { - return (state[length - 1] == IRRhossAc::calcChecksum(state, length)); -} - -/// Update the checksum value for the internal state. -void IRRhossAc::checksum(void) { - _.Sum = IRRhossAc::calcChecksum(_.raw, kRhossStateLength); - _.raw[kRhossStateLength - 1] = _.Sum; -} - -/// Reset the internals of the object to a known good state. -void IRRhossAc::stateReset(void) { - for (uint8_t i = 1; i < kRhossStateLength; i++) _.raw[i] = 0x0; - _.raw[0] = 0xAA; - _.raw[2] = 0x60; - _.raw[6] = 0x54; - _.Power = kRhossDefaultPower; - _.Fan = kRhossDefaultFan; - _.Mode = kRhossDefaultMode; - _.Swing = kRhossDefaultSwing; - _.Temp = kRhossDefaultTemp - kRhossTempMin; -} - -/// 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* IRRhossAc::getRaw(void) { - checksum(); // Ensure correct bit array before returning - return _.raw; -} - -/// Set the raw state of the object. -/// @param[state] state The raw state from the native IR message. -void IRRhossAc::setRaw(const uint8_t state[]) { - std::memcpy(_.raw, state, kRhossStateLength); -} - -/// Set the internal state to have the power on. -void IRRhossAc::on(void) { setPower(true); } - -/// Set the internal state to have the power off. -void IRRhossAc::off(void) { setPower(false); } - -/// Set the internal state to have the desired power. -/// @param[in] on The desired power state. -void IRRhossAc::setPower(const bool on) { - _.Power = (on ? kRhossPowerOn : kRhossPowerOff); -} - -/// Get the power setting from the internal state. -/// @return A boolean indicating the power setting. -bool IRRhossAc::getPower(void) const { - return _.Power == kRhossPowerOn; -} - -/// Set the temperature. -/// @param[in] degrees The temperature in degrees celsius. -void IRRhossAc::setTemp(const uint8_t degrees) { - uint8_t temp = std::max(kRhossTempMin, degrees); - _.Temp = std::min(kRhossTempMax, temp) - kRhossTempMin; -} - -/// Get the current temperature setting. -/// @return Get current setting for temp. in degrees celsius. -uint8_t IRRhossAc::getTemp(void) const { - return _.Temp + kRhossTempMin; -} - -/// Set the speed of the fan. -/// @param[in] speed The desired setting. -void IRRhossAc::setFan(const uint8_t speed) { - switch (speed) { - case kRhossFanAuto: - case kRhossFanMin: - case kRhossFanMed: - case kRhossFanMax: - _.Fan = speed; - break; - default: - _.Fan = kRhossFanAuto; - } -} - -/// Get the current fan speed setting. -/// @return The current fan speed. -uint8_t IRRhossAc::getFan(void) const { - return _.Fan; -} - -/// Set the Vertical Swing mode of the A/C. -/// @param[in] state true, the Swing is on. false, the Swing is off. -void IRRhossAc::setSwing(const bool state) { - _.Swing = state; -} - -/// Get the Vertical Swing speed of the A/C. -/// @return The native swing speed setting. -uint8_t IRRhossAc::getSwing(void) const { - return _.Swing; -} - -/// Get the current operation mode setting. -/// @return The current operation mode. -uint8_t IRRhossAc::getMode(void) const { - return _.Mode; -} - -/// Set the desired operation mode. -/// @param[in] mode The desired operation mode. -void IRRhossAc::setMode(const uint8_t mode) { - switch (mode) { - case kRhossModeFan: - case kRhossModeCool: - case kRhossModeDry: - case kRhossModeHeat: - case kRhossModeAuto: - _.Mode = mode; - return; - default: - _.Mode = kRhossDefaultMode; - 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 IRRhossAc::convertMode(const stdAc::opmode_t mode) { - switch (mode) { - case stdAc::opmode_t::kCool: - return kRhossModeCool; - case stdAc::opmode_t::kHeat: - return kRhossModeHeat; - case stdAc::opmode_t::kDry: - return kRhossModeDry; - case stdAc::opmode_t::kFan: - return kRhossModeFan; - case stdAc::opmode_t::kAuto: - return kRhossModeAuto; - default: - return kRhossDefaultMode; - } -} - -/// 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 IRRhossAc::convertFan(const stdAc::fanspeed_t speed) { - switch (speed) { - case stdAc::fanspeed_t::kMin: - case stdAc::fanspeed_t::kLow: - return kRhossFanMin; - case stdAc::fanspeed_t::kMedium: - return kRhossFanMed; - case stdAc::fanspeed_t::kHigh: - case stdAc::fanspeed_t::kMax: - return kRhossFanMax; - default: - return kRhossDefaultFan; - } -} - -/// 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 IRRhossAc::toCommonMode(const uint8_t mode) { - switch (mode) { - case kRhossModeCool: return stdAc::opmode_t::kCool; - case kRhossModeHeat: return stdAc::opmode_t::kHeat; - case kRhossModeDry: return stdAc::opmode_t::kDry; - case kRhossModeFan: return stdAc::opmode_t::kFan; - case kRhossModeAuto: return stdAc::opmode_t::kAuto; - 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 IRRhossAc::toCommonFanSpeed(const uint8_t speed) { - switch (speed) { - case kRhossFanMax: return stdAc::fanspeed_t::kMax; - case kRhossFanMed: return stdAc::fanspeed_t::kMedium; - case kRhossFanMin: return stdAc::fanspeed_t::kMin; - case kRhossFanAuto: - 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 IRRhossAc::toCommon(void) const { - stdAc::state_t result; - result.protocol = decode_type_t::RHOSS; - result.power = getPower(); - result.mode = toCommonMode(_.Mode); - result.celsius = true; - result.degrees = _.Temp; - result.fanspeed = toCommonFanSpeed(_.Fan); - result.swingv = _.Swing ? stdAc::swingv_t::kAuto : stdAc::swingv_t::kOff; - // Not supported. - result.model = -1; - result.turbo = false; - 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 IRRhossAc::toString(void) const { - String result = ""; - result.reserve(70); // Reserve some heap for the string to reduce fragging. - result += addBoolToString(getPower(), kPowerStr, false); - result += addModeToString(getMode(), kRhossModeAuto, kRhossModeCool, - kRhossModeHeat, kRhossModeDry, kRhossModeFan); - result += addTempToString(getTemp()); - result += addFanToString(getFan(), kRhossFanMax, kRhossFanMin, - kRhossFanAuto, kRhossFanAuto, - kRhossFanMed); - result += addBoolToString(getSwing(), kSwingVStr); - return result; -} diff --git a/lib/IRremoteESP8266/src/ir_Rhoss.h b/lib/IRremoteESP8266/src/ir_Rhoss.h index 8f66ce7384..e3a70aa431 100644 --- a/lib/IRremoteESP8266/src/ir_Rhoss.h +++ b/lib/IRremoteESP8266/src/ir_Rhoss.h @@ -13,10 +13,10 @@ #ifndef UNIT_TEST #include #endif -#include "IRremoteESP8266.h" -#include "IRsend.h" +#include "../src/IRremoteESP8266.h" +#include "../src/IRsend.h" #ifdef UNIT_TEST -#include "IRsend_test.h" +#include "../src/IRsend_test.h" #endif diff --git a/lib/IRremoteESP8266/src/ir_Samsung.h b/lib/IRremoteESP8266/src/ir_Samsung.h index acabb36481..5321deae82 100644 --- a/lib/IRremoteESP8266/src/ir_Samsung.h +++ b/lib/IRremoteESP8266/src/ir_Samsung.h @@ -35,10 +35,10 @@ #ifndef UNIT_TEST #include #endif -#include "IRremoteESP8266.h" -#include "IRsend.h" +#include "../src/IRremoteESP8266.h" +#include "../src/IRsend.h" #ifdef UNIT_TEST -#include "IRsend_test.h" +#include "../src/IRsend_test.h" #endif /// Native representation of a Samsung A/C message. diff --git a/lib/IRremoteESP8266/src/ir_Sanyo.cpp b/lib/IRremoteESP8266/src/ir_Sanyo.cpp deleted file mode 100644 index 7dbed5a582..0000000000 --- a/lib/IRremoteESP8266/src/ir_Sanyo.cpp +++ /dev/null @@ -1,978 +0,0 @@ -// Copyright 2009 Ken Shirriff -// Copyright 2016 marcosamarinho -// Copyright 2017-2021 David Conran - -/// @file -/// @brief Support for Sanyo protocols. -/// Sanyo LC7461 support originally by marcosamarinho -/// Sanyo SA 8650B originally added from -/// https://github.com/shirriff/Arduino-IRremote/ -/// @see https://github.com/z3t0/Arduino-IRremote/blob/master/ir_Sanyo.cpp -/// @see http://pdf.datasheetcatalog.com/datasheet/sanyo/LC7461.pdf -/// @see https://github.com/marcosamarinho/IRremoteESP8266/blob/master/ir_Sanyo.cpp -/// @see http://slydiman.narod.ru/scr/kb/sanyo.htm -/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1211 -/// @see https://docs.google.com/spreadsheets/d/1dYfLsnYvpjV-SgO8pdinpfuBIpSzm8Q1R5SabrLeskw/edit?usp=sharing -/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1503 - -#include "ir_Sanyo.h" -#include -#include -#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::addSwingVToString; -using irutils::addTempToString; -using irutils::minsToString; -using irutils::sumNibbles; - -// Constants -// Sanyo SA 8650B -const uint16_t kSanyoSa8650bHdrMark = 3500; // seen range 3500 -const uint16_t kSanyoSa8650bHdrSpace = 950; // seen 950 -const uint16_t kSanyoSa8650bOneMark = 2400; // seen 2400 -const uint16_t kSanyoSa8650bZeroMark = 700; // seen 700 -// usually see 713 - not using ticks as get number wrapround -const uint16_t kSanyoSa8650bDoubleSpaceUsecs = 800; -const uint16_t kSanyoSa8650bRptLength = 45000; - -// Sanyo LC7461 -const uint16_t kSanyoLc7461AddressMask = (1 << kSanyoLC7461AddressBits) - 1; -const uint16_t kSanyoLc7461CommandMask = (1 << kSanyoLC7461CommandBits) - 1; -const uint16_t kSanyoLc7461HdrMark = 9000; -const uint16_t kSanyoLc7461HdrSpace = 4500; -const uint16_t kSanyoLc7461BitMark = 560; // 1T -const uint16_t kSanyoLc7461OneSpace = 1690; // 3T -const uint16_t kSanyoLc7461ZeroSpace = 560; // 1T -const uint32_t kSanyoLc7461MinCommandLength = 108000; - -const uint16_t kSanyoLc7461MinGap = - kSanyoLc7461MinCommandLength - - (kSanyoLc7461HdrMark + kSanyoLc7461HdrSpace + - kSanyoLC7461Bits * (kSanyoLc7461BitMark + - (kSanyoLc7461OneSpace + kSanyoLc7461ZeroSpace) / 2) + - kSanyoLc7461BitMark); - -const uint16_t kSanyoAcHdrMark = 8500; ///< uSeconds -const uint16_t kSanyoAcHdrSpace = 4200; ///< uSeconds -const uint16_t kSanyoAcBitMark = 500; ///< uSeconds -const uint16_t kSanyoAcOneSpace = 1600; ///< uSeconds -const uint16_t kSanyoAcZeroSpace = 550; ///< uSeconds -const uint32_t kSanyoAcGap = kDefaultMessageGap; ///< uSeconds (Guess only) -const uint16_t kSanyoAcFreq = 38000; ///< Hz. (Guess only) - -const uint16_t kSanyoAc88HdrMark = 5400; ///< uSeconds -const uint16_t kSanyoAc88HdrSpace = 2000; ///< uSeconds -const uint16_t kSanyoAc88BitMark = 500; ///< uSeconds -const uint16_t kSanyoAc88OneSpace = 1500; ///< uSeconds -const uint16_t kSanyoAc88ZeroSpace = 750; ///< uSeconds -const uint32_t kSanyoAc88Gap = 3675; ///< uSeconds -const uint16_t kSanyoAc88Freq = 38000; ///< Hz. (Guess only) -const uint8_t kSanyoAc88ExtraTolerance = 5; /// (%) Extra tolerance to use. - -#if SEND_SANYO -/// Construct a Sanyo LC7461 message. -/// @param[in] address The 13 bit value of the address(Custom) portion of the -/// protocol. -/// @param[in] command The 8 bit value of the command(Key) portion of the -/// protocol. -/// @return An uint64_t with the encoded raw 42 bit Sanyo LC7461 data value. -/// @note This protocol uses the NEC protocol timings. However, data is -/// formatted as : address(13 bits), !address, command(8 bits), !command. -/// According with LIRC, this protocol is used on Sanyo, Aiwa and Chinon -uint64_t IRsend::encodeSanyoLC7461(uint16_t address, uint8_t command) { - // Mask our input values to ensure the correct bit sizes. - address &= kSanyoLc7461AddressMask; - command &= kSanyoLc7461CommandMask; - - uint64_t data = address; - address ^= kSanyoLc7461AddressMask; // Invert the 13 LSBs. - // Append the now inverted address. - data = (data << kSanyoLC7461AddressBits) | address; - // Append the command. - data = (data << kSanyoLC7461CommandBits) | command; - command ^= kSanyoLc7461CommandMask; // Invert the command. - // Append the now inverted command. - data = (data << kSanyoLC7461CommandBits) | command; - - return data; -} - -/// Send a Sanyo LC7461 message. -/// Status: BETA / Probably works. -/// @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 Based on \@marcosamarinho's work. -/// This protocol uses the NEC protocol timings. However, data is -/// formatted as : address(13 bits), !address, command (8 bits), !command. -/// According with LIRC, this protocol is used on Sanyo, Aiwa and Chinon -/// Information for this protocol is available at the Sanyo LC7461 datasheet. -/// Repeats are performed similar to the NEC method of sending a special -/// repeat message, rather than duplicating the entire message. -/// @see https://github.com/marcosamarinho/IRremoteESP8266/blob/master/ir_Sanyo.cpp -/// @see http://pdf.datasheetcatalog.com/datasheet/sanyo/LC7461.pdf -void IRsend::sendSanyoLC7461(const uint64_t data, const uint16_t nbits, - const uint16_t repeat) { - // This protocol appears to be another 42-bit variant of the NEC protocol. - sendNEC(data, nbits, repeat); -} -#endif // SEND_SANYO - -#if DECODE_SANYO -/// Decode the supplied SANYO LC7461 message. -/// Status: BETA / Probably works. -/// @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 Based on \@marcosamarinho's work. -/// This protocol uses the NEC protocol. However, data is -/// formatted as : address(13 bits), !address, command (8 bits), !command. -/// According with LIRC, this protocol is used on Sanyo, Aiwa and Chinon -/// Information for this protocol is available at the Sanyo LC7461 datasheet. -/// @see http://slydiman.narod.ru/scr/kb/sanyo.htm -/// @see https://github.com/marcosamarinho/IRremoteESP8266/blob/master/ir_Sanyo.cpp -/// @see http://pdf.datasheetcatalog.com/datasheet/sanyo/LC7461.pdf -bool IRrecv::decodeSanyoLC7461(decode_results *results, uint16_t offset, - const uint16_t nbits, const bool strict) { - if (strict && nbits != kSanyoLC7461Bits) - return false; // Not strictly in spec. - // This protocol is basically a 42-bit variant of the NEC protocol. - if (!decodeNEC(results, offset, nbits, false)) - return false; // Didn't match a NEC format (without strict) - - // Bits 30 to 42+. - uint16_t address = - results->value >> (kSanyoLC7461Bits - kSanyoLC7461AddressBits); - // Bits 9 to 16. - uint8_t command = - (results->value >> kSanyoLC7461CommandBits) & kSanyoLc7461CommandMask; - // Compliance - if (strict) { - if (results->bits != nbits) return false; - // Bits 17 to 29. - uint16_t inverted_address = - (results->value >> (kSanyoLC7461CommandBits * 2)) & - kSanyoLc7461AddressMask; - // Bits 1-8. - uint8_t inverted_command = results->value & kSanyoLc7461CommandMask; - if ((address ^ kSanyoLc7461AddressMask) != inverted_address) - return false; // Address integrity check failed. - if ((command ^ kSanyoLc7461CommandMask) != inverted_command) - return false; // Command integrity check failed. - } - - // Success - results->decode_type = SANYO_LC7461; - results->address = address; - results->command = command; - return true; -} - -/* NOTE: Disabled due to poor quality. -/// Decode the supplied Sanyo SA 8650B message. -/// Status: Depricated. -/// @depricated Disabled due to poor quality. -/// @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. -/// @warning This decoder looks like rubbish. Only keeping it for compatibility -/// with the Arduino IRremote library. Seriously, don't trust it. -/// If someone has a device that this is supposed to be for, please log an -/// Issue on github with a rawData dump please. We should probably remove it. -/// We think this is a Sanyo decoder - serial = SA 8650B -/// @see https://github.com/z3t0/Arduino-IRremote/blob/master/ir_Sanyo.cpp -bool IRrecv::decodeSanyo(decode_results *results, uint16_t nbits, bool strict) { - if (results->rawlen < 2 * nbits + kHeader - 1) - return false; // Shorter than shortest possible. - if (strict && nbits != kSanyoSA8650BBits) - return false; // Doesn't match the spec. - - uint16_t offset = 0; - - // TODO(crankyoldgit): This repeat code looks like garbage, it should never - // match or if it does, it won't be reliable. We should probably just - // remove it. - if (results->rawbuf[offset++] < kSanyoSa8650bDoubleSpaceUsecs) { - results->bits = 0; - results->value = kRepeat; - results->decode_type = SANYO; - results->address = 0; - results->command = 0; - results->repeat = true; - return true; - } - - // Header - if (!matchMark(results->rawbuf[offset++], kSanyoSa8650bHdrMark)) - return false; - // NOTE: These next two lines look very wrong. Treat as suspect. - if (!matchMark(results->rawbuf[offset++], kSanyoSa8650bHdrMark)) - return false; - // Data - uint64_t data = 0; - while (offset + 1 < results->rawlen) { - if (!matchSpace(results->rawbuf[offset], kSanyoSa8650bHdrSpace)) - break; - offset++; - if (matchMark(results->rawbuf[offset], kSanyoSa8650bOneMark)) - data = (data << 1) | 1; // 1 - else if (matchMark(results->rawbuf[offset], kSanyoSa8650bZeroMark)) - data <<= 1; // 0 - else - return false; - offset++; - } - - if (strict && kSanyoSA8650BBits > (offset - 1U) / 2U) - return false; - - // Success - results->bits = (offset - 1) / 2; - results->decode_type = SANYO; - results->value = data; - results->address = 0; - results->command = 0; - return true; -} -*/ -#endif // DECODE_SANYO - - -#if SEND_SANYO_AC -/// Send a SanyoAc 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. -/// @param[in] repeat Nr. of times the message is to be repeated. -/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1211 -void IRsend::sendSanyoAc(const uint8_t data[], const uint16_t nbytes, - const uint16_t repeat) { - // Header + Data + Footer - sendGeneric(kSanyoAcHdrMark, kSanyoAcHdrSpace, - kSanyoAcBitMark, kSanyoAcOneSpace, - kSanyoAcBitMark, kSanyoAcZeroSpace, - kSanyoAcBitMark, kSanyoAcGap, - data, nbytes, kSanyoAcFreq, false, repeat, kDutyDefault); -} -#endif // SEND_SANYO_AC - -#if DECODE_SANYO_AC -/// Decode the supplied SanyoAc 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. -/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1211 -bool IRrecv::decodeSanyoAc(decode_results *results, uint16_t offset, - const uint16_t nbits, const bool strict) { - if (strict && nbits != kSanyoAcBits) - return false; - - // Header + Data + Footer - if (!matchGeneric(results->rawbuf + offset, results->state, - results->rawlen - offset, nbits, - kSanyoAcHdrMark, kSanyoAcHdrSpace, - kSanyoAcBitMark, kSanyoAcOneSpace, - kSanyoAcBitMark, kSanyoAcZeroSpace, - kSanyoAcBitMark, kSanyoAcGap, - true, kUseDefTol, kMarkExcess, false)) return false; - // Compliance - if (strict) - if (!IRSanyoAc::validChecksum(results->state, nbits / 8)) return false; - - // Success - results->decode_type = decode_type_t::SANYO_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_SANYO_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? -IRSanyoAc::IRSanyoAc(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 state/sequence. -void IRSanyoAc::stateReset(void) { - static const uint8_t kReset[kSanyoAcStateLength] = { - 0x6A, 0x6D, 0x51, 0x00, 0x10, 0x45, 0x00, 0x00, 0x33}; - std::memcpy(_.raw, kReset, kSanyoAcStateLength); -} - -/// Set up hardware to be able to send a message. -void IRSanyoAc::begin(void) { _irsend.begin(); } - -#if SEND_SANYO_AC -/// Send the current internal state as IR messages. -/// @param[in] repeat Nr. of times the message will be repeated. -void IRSanyoAc::send(const uint16_t repeat) { - _irsend.sendSanyoAc(getRaw(), kSanyoAcStateLength, repeat); -} -#endif // SEND_SANYO_AC - -/// Get a PTR to the internal state/code for this protocol with all integrity -/// checks passing. -/// @return PTR to a code for this protocol based on the current internal state. -uint8_t* IRSanyoAc::getRaw(void) { - checksum(); - return _.raw; -} - -/// Set the internal state from a valid code for this protocol. -/// @param[in] newState A valid code for this protocol. -void IRSanyoAc::setRaw(const uint8_t newState[]) { - std::memcpy(_.raw, newState, kSanyoAcStateLength); -} - -/// 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 IRSanyoAc::calcChecksum(const uint8_t state[], - const uint16_t length) { - return length ? sumNibbles(state, length - 1) : 0; -} - -/// 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 IRSanyoAc::validChecksum(const uint8_t state[], const uint16_t length) { - return length && state[length - 1] == IRSanyoAc::calcChecksum(state, length); -} - -/// Calculate & set the checksum for the current internal state of the remote. -void IRSanyoAc::checksum(void) { - // Stored the checksum value in the last byte. - _.Sum = calcChecksum(_.raw); -} - - -/// Set the requested power state of the A/C to on. -void IRSanyoAc::on(void) { setPower(true); } - -/// Set the requested power state of the A/C to off. -void IRSanyoAc::off(void) { setPower(false); } - -/// Change the power setting. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRSanyoAc::setPower(const bool on) { - _.Power = (on ? kSanyoAcPowerOn : kSanyoAcPowerOff); -} - -/// Get the value of the current power setting. -/// @return true, the setting is on. false, the setting is off. -bool IRSanyoAc::getPower(void) const { - return _.Power == kSanyoAcPowerOn; -} - -/// Get the operating mode setting of the A/C. -/// @return The current operating mode setting. -uint8_t IRSanyoAc::getMode(void) const { - return _.Mode; -} - -/// Set the operating mode of the A/C. -/// @param[in] mode The desired operating mode. -/// @note If we get an unexpected mode, default to AUTO. -void IRSanyoAc::setMode(const uint8_t mode) { - switch (mode) { - case kSanyoAcAuto: - case kSanyoAcCool: - case kSanyoAcDry: - case kSanyoAcHeat: - _.Mode = mode; - break; - default: _.Mode = kSanyoAcAuto; - } -} - -/// 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 IRSanyoAc::convertMode(const stdAc::opmode_t mode) { - switch (mode) { - case stdAc::opmode_t::kCool: return kSanyoAcCool; - case stdAc::opmode_t::kHeat: return kSanyoAcHeat; - case stdAc::opmode_t::kDry: return kSanyoAcDry; - default: return kSanyoAcAuto; - } -} - -/// 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 IRSanyoAc::toCommonMode(const uint8_t mode) { - switch (mode) { - case kSanyoAcCool: return stdAc::opmode_t::kCool; - case kSanyoAcHeat: return stdAc::opmode_t::kHeat; - case kSanyoAcDry: return stdAc::opmode_t::kDry; - default: return stdAc::opmode_t::kAuto; - } -} - -/// Set the desired temperature. -/// @param[in] degrees The temperature in degrees celsius. -void IRSanyoAc::setTemp(const uint8_t degrees) { - uint8_t temp = std::max((uint8_t)kSanyoAcTempMin, degrees); - temp = std::min((uint8_t)kSanyoAcTempMax, temp); - _.Temp = temp - kSanyoAcTempDelta; -} - -/// Get the current desired temperature setting. -/// @return The current setting for temp. in degrees celsius. -uint8_t IRSanyoAc::getTemp(void) const { - return _.Temp + kSanyoAcTempDelta; -} - -/// Set the sensor temperature. -/// @param[in] degrees The temperature in degrees celsius. -void IRSanyoAc::setSensorTemp(const uint8_t degrees) { - uint8_t temp = std::max((uint8_t)kSanyoAcTempMin, degrees); - temp = std::min((uint8_t)kSanyoAcTempMax, temp); - _.SensorTemp = temp - kSanyoAcTempDelta; -} - -/// Get the current sensor temperature setting. -/// @return The current setting for temp. in degrees celsius. -uint8_t IRSanyoAc::getSensorTemp(void) const { - return _.SensorTemp + kSanyoAcTempDelta; -} - -/// Set the speed of the fan. -/// @param[in] speed The desired setting. -void IRSanyoAc::setFan(const uint8_t speed) { - _.Fan = speed; -} - -/// Get the current fan speed setting. -/// @return The current fan speed/mode. -uint8_t IRSanyoAc::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 IRSanyoAc::convertFan(const stdAc::fanspeed_t speed) { - switch (speed) { - case stdAc::fanspeed_t::kMin: - case stdAc::fanspeed_t::kLow: return kSanyoAcFanLow; - case stdAc::fanspeed_t::kMedium: return kSanyoAcFanMedium; - case stdAc::fanspeed_t::kHigh: - case stdAc::fanspeed_t::kMax: return kSanyoAcFanHigh; - default: return kSanyoAcFanAuto; - } -} - -/// Convert a native fan speed into its stdAc equivalent. -/// @param[in] spd The native setting to be converted. -/// @return The stdAc equivalent of the native setting. -stdAc::fanspeed_t IRSanyoAc::toCommonFanSpeed(const uint8_t spd) { - switch (spd) { - case kSanyoAcFanHigh: return stdAc::fanspeed_t::kHigh; - case kSanyoAcFanMedium: return stdAc::fanspeed_t::kMedium; - case kSanyoAcFanLow: return stdAc::fanspeed_t::kLow; - default: return stdAc::fanspeed_t::kAuto; - } -} - -/// Get the vertical swing setting of the A/C. -/// @return The current swing mode setting. -uint8_t IRSanyoAc::getSwingV(void) const { - return _.SwingV; -} - -/// Set the vertical swing setting of the A/C. -/// @param[in] setting The value of the desired setting. -void IRSanyoAc::setSwingV(const uint8_t setting) { - if (setting == kSanyoAcSwingVAuto || - (setting >= kSanyoAcSwingVLowest && setting <= kSanyoAcSwingVHighest)) - _.SwingV = setting; - else - _.SwingV = kSanyoAcSwingVAuto; -} - -/// 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 IRSanyoAc::convertSwingV(const stdAc::swingv_t position) { - switch (position) { - case stdAc::swingv_t::kHighest: return kSanyoAcSwingVHighest; - case stdAc::swingv_t::kHigh: return kSanyoAcSwingVHigh; - case stdAc::swingv_t::kMiddle: return kSanyoAcSwingVUpperMiddle; - case stdAc::swingv_t::kLow: return kSanyoAcSwingVLow; - case stdAc::swingv_t::kLowest: return kSanyoAcSwingVLowest; - default: return kSanyoAcSwingVAuto; - } -} - -/// Convert a native vertical swing postion to it's common equivalent. -/// @param[in] setting A native position to convert. -/// @return The common vertical swing position. -stdAc::swingv_t IRSanyoAc::toCommonSwingV(const uint8_t setting) { - switch (setting) { - case kSanyoAcSwingVHighest: return stdAc::swingv_t::kHighest; - case kSanyoAcSwingVHigh: return stdAc::swingv_t::kHigh; - case kSanyoAcSwingVUpperMiddle: - case kSanyoAcSwingVLowerMiddle: return stdAc::swingv_t::kMiddle; - case kSanyoAcSwingVLow: return stdAc::swingv_t::kLow; - case kSanyoAcSwingVLowest: return stdAc::swingv_t::kLowest; - default: return stdAc::swingv_t::kAuto; - } -} - -/// Set the Sleep (Night Setback) setting of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRSanyoAc::setSleep(const bool on) { - _.Sleep = on; -} - -/// Get the Sleep (Night Setback) setting of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRSanyoAc::getSleep(void) const { - return _.Sleep; -} - -/// Set the Sensor Location setting of the A/C. -/// i.e. Where the ambient temperature is measured. -/// @param[in] location true is Unit/Wall, false is Remote/Room. -void IRSanyoAc::setSensor(const bool location) { - _.Sensor = location; -} - -/// Get the Sensor Location setting of the A/C. -/// i.e. Where the ambient temperature is measured. -/// @return true is Unit/Wall, false is Remote/Room. -bool IRSanyoAc::getSensor(void) const { - return _.Sensor; -} - -/// Set the Beep setting of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRSanyoAc::setBeep(const bool on) { - _.Beep = on; -} - -/// Get the Beep setting of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRSanyoAc::getBeep(void) const { - return _.Beep; -} - -/// Get the nr of minutes the Off Timer is set to. -/// @return The timer time expressed as the number of minutes. -/// A value of 0 means the Off Timer is off/disabled. -/// @note The internal precission has a resolution of 1 hour. -uint16_t IRSanyoAc::getOffTimer(void) const { - if (_.OffTimer) - return _.OffHour * 60; - else - return 0; -} - -/// Set the nr of minutes for the Off Timer. -/// @param[in] mins The timer time expressed as nr. of minutes. -/// A value of 0 means the Off Timer is off/disabled. -/// @note The internal precission has a resolution of 1 hour. -void IRSanyoAc::setOffTimer(const uint16_t mins) { - const uint8_t hours = std::min((uint8_t)(mins / 60), kSanyoAcHourMax); - _.OffTimer = (hours > 0); - _.OffHour = hours; -} - -/// Convert the current internal state into its stdAc::state_t equivalent. -/// @return The stdAc equivalent of the native settings. -stdAc::state_t IRSanyoAc::toCommon(void) const { - stdAc::state_t result; - result.protocol = decode_type_t::SANYO_AC; - result.model = -1; // Not supported. - result.power = getPower(); - result.mode = toCommonMode(_.Mode); - result.celsius = true; - result.degrees = getTemp(); - result.fanspeed = toCommonFanSpeed(_.Fan); - result.sleep = _.Sleep ? 0 : -1; - result.swingv = toCommonSwingV(_.SwingV); - result.beep = _.Beep; - // Not supported. - result.swingh = stdAc::swingh_t::kOff; - result.turbo = false; - result.econo = false; - result.light = false; - result.filter = false; - result.quiet = false; - result.clean = false; - result.clock = -1; - return result; -} - -/// Convert the current internal state into a human readable string. -/// @return A human readable string. -String IRSanyoAc::toString(void) const { - String result = ""; - result.reserve(140); - result += addBoolToString(getPower(), kPowerStr, false); - result += addModeToString(_.Mode, kSanyoAcAuto, kSanyoAcCool, - kSanyoAcHeat, kSanyoAcDry, kSanyoAcAuto); - result += addTempToString(getTemp()); - result += addFanToString(_.Fan, kSanyoAcFanHigh, kSanyoAcFanLow, - kSanyoAcFanAuto, kSanyoAcFanAuto, - kSanyoAcFanMedium); - result += addSwingVToString(_.SwingV, kSanyoAcSwingVAuto, - kSanyoAcSwingVHighest, kSanyoAcSwingVHigh, - kSanyoAcSwingVUpperMiddle, - kSanyoAcSwingVAuto, // Middle is unused - kSanyoAcSwingVLowerMiddle, - kSanyoAcSwingVLow, kSanyoAcSwingVLowest, - // Below are unused. - kSanyoAcSwingVAuto, - kSanyoAcSwingVAuto, - kSanyoAcSwingVAuto, - kSanyoAcSwingVAuto); - result += addBoolToString(_.Sleep, kSleepStr); - result += addBoolToString(_.Beep, kBeepStr); - result += addLabeledString(_.Sensor ? kRoomStr : kWallStr, kSensorStr); - result += kCommaSpaceStr; - result += kSensorStr; - result += ' '; - result += addTempToString(getSensorTemp(), true, false); - const uint16_t offtime = getOffTimer(); - result += addLabeledString(offtime ? minsToString(offtime) : kOffStr, - kOffTimerStr); - return result; -} - -#if SEND_SANYO_AC88 -/// Send a SanyoAc88 formatted message. -/// Status: ALPHA / Completely untested. -/// @param[in] data An array of bytes containing the IR command. -/// @warning data's bit order may change. It is not yet confirmed. -/// @param[in] nbytes Nr. of bytes of data in the array. -/// @param[in] repeat Nr. of times the message is to be repeated. -/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1503 -void IRsend::sendSanyoAc88(const uint8_t data[], const uint16_t nbytes, - const uint16_t repeat) { - // (Header + Data + Footer) per repeat - sendGeneric(kSanyoAc88HdrMark, kSanyoAc88HdrSpace, - kSanyoAc88BitMark, kSanyoAc88OneSpace, - kSanyoAc88BitMark, kSanyoAc88ZeroSpace, - kSanyoAc88BitMark, kSanyoAc88Gap, - data, nbytes, kSanyoAc88Freq, false, repeat, kDutyDefault); - space(kDefaultMessageGap); // Make a guess at a post message gap. -} -#endif // SEND_SANYO_AC88 - -#if DECODE_SANYO_AC88 -/// Decode the supplied SanyoAc message. -/// Status: ALPHA / Untested. -/// @param[in,out] results Ptr to the data to decode & where to store the decode -/// @warning data's bit order may change. It is not yet confirmed. -/// @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/1503 -bool IRrecv::decodeSanyoAc88(decode_results *results, uint16_t offset, - const uint16_t nbits, const bool strict) { - if (strict && nbits != kSanyoAc88Bits) - return false; - - uint16_t used = 0; - // Compliance - const uint16_t expected_repeats = strict ? kSanyoAc88MinRepeat : 0; - - // Handle the expected nr of repeats. - for (uint16_t r = 0; r <= expected_repeats; r++) { - // Header + Data + Footer - used = matchGeneric(results->rawbuf + offset, results->state, - results->rawlen - offset, nbits, - kSanyoAc88HdrMark, kSanyoAc88HdrSpace, - kSanyoAc88BitMark, kSanyoAc88OneSpace, - kSanyoAc88BitMark, kSanyoAc88ZeroSpace, - kSanyoAc88BitMark, - // Expect an inter-message gap, or just the end of msg? - (r < expected_repeats) ? kSanyoAc88Gap - : kDefaultMessageGap, - r == expected_repeats, - _tolerance + kSanyoAc88ExtraTolerance, - kMarkExcess, false); - if (!used) return false; // No match! - offset += used; - } - - // Success - results->decode_type = decode_type_t::SANYO_AC88; - 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_SANYO_AC88 - -/// 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? -IRSanyoAc88::IRSanyoAc88(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. -/// @see https://docs.google.com/spreadsheets/d/1dYfLsnYvpjV-SgO8pdinpfuBIpSzm8Q1R5SabrLeskw/edit?ts=5f0190a5#gid=1050142776&range=A2:B2 -void IRSanyoAc88::stateReset(void) { - static const uint8_t kReset[kSanyoAc88StateLength] = { - 0xAA, 0x55, 0xA0, 0x16, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x10}; - std::memcpy(_.raw, kReset, kSanyoAc88StateLength); -} - -/// Set up hardware to be able to send a message. -void IRSanyoAc88::begin(void) { _irsend.begin(); } - -#if SEND_SANYO_AC -/// Send the current internal state as IR messages. -/// @param[in] repeat Nr. of times the message will be repeated. -void IRSanyoAc88::send(const uint16_t repeat) { - _irsend.sendSanyoAc88(getRaw(), kSanyoAc88StateLength, repeat); -} -#endif // SEND_SANYO_AC - -/// Get a PTR to the internal state/code for this protocol with all integrity -/// checks passing. -/// @return PTR to a code for this protocol based on the current internal state. -uint8_t* IRSanyoAc88::getRaw(void) { - return _.raw; -} - -/// Set the internal state from a valid code for this protocol. -/// @param[in] newState A valid code for this protocol. -void IRSanyoAc88::setRaw(const uint8_t newState[]) { - std::memcpy(_.raw, newState, kSanyoAc88StateLength); -} - -/// Set the requested power state of the A/C to on. -void IRSanyoAc88::on(void) { setPower(true); } - -/// Set the requested power state of the A/C to off. -void IRSanyoAc88::off(void) { setPower(false); } - -/// Change the power setting. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRSanyoAc88::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 IRSanyoAc88::getPower(void) const { return _.Power; } - -/// Get the operating mode setting of the A/C. -/// @return The current operating mode setting. -uint8_t IRSanyoAc88::getMode(void) const { return _.Mode; } - -/// Set the operating mode of the A/C. -/// @param[in] mode The desired operating mode. -/// @note If we get an unexpected mode, default to AUTO. -void IRSanyoAc88::setMode(const uint8_t mode) { - switch (mode) { - case kSanyoAc88Auto: - case kSanyoAc88FeelCool: - case kSanyoAc88Cool: - case kSanyoAc88FeelHeat: - case kSanyoAc88Heat: - case kSanyoAc88Fan: - _.Mode = mode; - break; - default: _.Mode = kSanyoAc88Auto; - } -} - -/// 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 IRSanyoAc88::convertMode(const stdAc::opmode_t mode) { - switch (mode) { - case stdAc::opmode_t::kCool: return kSanyoAc88Cool; - case stdAc::opmode_t::kHeat: return kSanyoAc88Heat; - case stdAc::opmode_t::kFan: return kSanyoAc88Fan; - default: return kSanyoAc88Auto; - } -} - -/// 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 IRSanyoAc88::toCommonMode(const uint8_t mode) { - switch (mode) { - case kSanyoAc88FeelCool: - case kSanyoAc88Cool: - return stdAc::opmode_t::kCool; - case kSanyoAc88FeelHeat: - case kSanyoAc88Heat: - return stdAc::opmode_t::kHeat; - case kSanyoAc88Fan: - return stdAc::opmode_t::kFan; - default: - return stdAc::opmode_t::kAuto; - } -} - -/// Set the desired temperature. -/// @param[in] degrees The temperature in degrees celsius. -void IRSanyoAc88::setTemp(const uint8_t degrees) { - uint8_t temp = std::max((uint8_t)kSanyoAc88TempMin, degrees); - _.Temp = std::min((uint8_t)kSanyoAc88TempMax, temp); -} - -/// Get the current desired temperature setting. -/// @return The current setting for temp. in degrees celsius. -uint8_t IRSanyoAc88::getTemp(void) const { return _.Temp; } - -/// Set the speed of the fan. -/// @param[in] speed The desired setting. -void IRSanyoAc88::setFan(const uint8_t speed) { _.Fan = speed; } - -/// Get the current fan speed setting. -/// @return The current fan speed/mode. -uint8_t IRSanyoAc88::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 IRSanyoAc88::convertFan(const stdAc::fanspeed_t speed) { - switch (speed) { - case stdAc::fanspeed_t::kMin: - case stdAc::fanspeed_t::kLow: return kSanyoAc88FanLow; - case stdAc::fanspeed_t::kMedium: return kSanyoAc88FanMedium; - case stdAc::fanspeed_t::kHigh: - case stdAc::fanspeed_t::kMax: return kSanyoAc88FanHigh; - default: return kSanyoAc88FanAuto; - } -} - -/// Get the current clock time. -/// @return The time as the nr. of minutes past midnight. -uint16_t IRSanyoAc88::getClock(void) const { - return _.ClockHrs * 60 + _.ClockMins; -} - -/// Set the current clock time. -/// @param[in] mins_since_midnight The time as nr. of minutes past midnight. -void IRSanyoAc88::setClock(const uint16_t mins_since_midnight) { - uint16_t mins = std::min(mins_since_midnight, (uint16_t)(23 * 60 + 59)); - _.ClockMins = mins % 60; - _.ClockHrs = mins / 60; - _.ClockSecs = 0; -} - -/// Convert a native fan speed into its stdAc equivalent. -/// @param[in] spd The native setting to be converted. -/// @return The stdAc equivalent of the native setting. -stdAc::fanspeed_t IRSanyoAc88::toCommonFanSpeed(const uint8_t spd) { - switch (spd) { - case kSanyoAc88FanHigh: return stdAc::fanspeed_t::kHigh; - case kSanyoAc88FanMedium: return stdAc::fanspeed_t::kMedium; - case kSanyoAc88FanLow: return stdAc::fanspeed_t::kLow; - default: return stdAc::fanspeed_t::kAuto; - } -} - -/// Change the SwingV setting. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRSanyoAc88::setSwingV(const bool on) { _.SwingV = on; } - -/// Get the value of the current SwingV setting. -/// @return true, the setting is on. false, the setting is off. -bool IRSanyoAc88::getSwingV(void) const { return _.SwingV; } - -/// Change the Turbo setting. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRSanyoAc88::setTurbo(const bool on) { _.Turbo = on; } - -/// Get the value of the current Turbo setting. -/// @return true, the setting is on. false, the setting is off. -bool IRSanyoAc88::getTurbo(void) const { return _.Turbo; } - -/// Change the Filter setting. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRSanyoAc88::setFilter(const bool on) { _.Filter = on; } - -/// Get the value of the current Filter setting. -/// @return true, the setting is on. false, the setting is off. -bool IRSanyoAc88::getFilter(void) const { return _.Filter; } - -/// Change the Sleep setting. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRSanyoAc88::setSleep(const bool on) { _.Sleep = on; } - -/// Get the value of the current Sleep setting. -/// @return true, the setting is on. false, the setting is off. -bool IRSanyoAc88::getSleep(void) const { return _.Sleep; } - -/// Convert the current internal state into its stdAc::state_t equivalent. -/// @return The stdAc equivalent of the native settings. -stdAc::state_t IRSanyoAc88::toCommon(void) const { - stdAc::state_t result; - result.protocol = decode_type_t::SANYO_AC88; - result.model = -1; // Not supported. - result.power = getPower(); - 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.filter = _.Filter; - result.turbo = _.Turbo; - result.sleep = _.Sleep ? 0 : -1; - result.clock = getClock(); - // Not supported. - result.swingh = stdAc::swingh_t::kOff; - result.econo = false; - result.light = false; - result.quiet = false; - result.beep = false; - result.clean = false; - return result; -} - -/// Convert the current internal state into a human readable string. -/// @return A human readable string. -String IRSanyoAc88::toString(void) const { - String result = ""; - result.reserve(115); - result += addBoolToString(getPower(), kPowerStr, false); - result += addModeToString(_.Mode, kSanyoAc88Auto, kSanyoAc88Cool, - kSanyoAc88Heat, kSanyoAc88Auto, kSanyoAc88Fan); - result += addTempToString(getTemp()); - result += addFanToString(_.Fan, kSanyoAc88FanHigh, kSanyoAc88FanLow, - kSanyoAc88FanAuto, kSanyoAc88FanAuto, - kSanyoAc88FanMedium); - result += addBoolToString(_.SwingV, kSwingVStr); - result += addBoolToString(_.Turbo, kTurboStr); - result += addBoolToString(_.Sleep, kSleepStr); - result += addLabeledString(minsToString(getClock()), kClockStr); - return result; -} diff --git a/lib/IRremoteESP8266/src/ir_Sanyo.h b/lib/IRremoteESP8266/src/ir_Sanyo.h index 66376328f6..206a7314ff 100644 --- a/lib/IRremoteESP8266/src/ir_Sanyo.h +++ b/lib/IRremoteESP8266/src/ir_Sanyo.h @@ -30,10 +30,10 @@ #ifdef ARDUINO #include #endif -#include "IRremoteESP8266.h" -#include "IRsend.h" +#include "../src/IRremoteESP8266.h" +#include "../src/IRsend.h" #ifdef UNIT_TEST -#include "IRsend_test.h" +#include "../src/IRsend_test.h" #endif /// Native representation of a Sanyo A/C message. diff --git a/lib/IRremoteESP8266/src/ir_Sharp.cpp b/lib/IRremoteESP8266/src/ir_Sharp.cpp deleted file mode 100644 index 38f0ac32ce..0000000000 --- a/lib/IRremoteESP8266/src/ir_Sharp.cpp +++ /dev/null @@ -1,976 +0,0 @@ -// Copyright 2009 Ken Shirriff -// Copyright 2017, 2019 David Conran - -/// @file -/// @brief Support for Sharp protocols. -/// @see http://www.sbprojects.net/knowledge/ir/sharp.htm -/// @see http://lirc.sourceforge.net/remotes/sharp/GA538WJSA -/// @see http://www.mwftr.com/ucF08/LEC14%20PIC%20IR.pdf -/// @see http://www.hifi-remote.com/johnsfine/DecodeIR.html#Sharp -/// @see GlobalCache's IR Control Tower data. -/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/638 -/// @see https://github.com/ToniA/arduino-heatpumpir/blob/master/SharpHeatpumpIR.cpp - -#include "ir_Sharp.h" -#include -#include -#ifndef ARDUINO -#include -#endif -#include "IRrecv.h" -#include "IRsend.h" -#include "IRtext.h" -#include "IRutils.h" - -// Constants -// period time = 1/38000Hz = 26.316 microseconds. -const uint16_t kSharpTick = 26; -const uint16_t kSharpBitMarkTicks = 10; -const uint16_t kSharpBitMark = kSharpBitMarkTicks * kSharpTick; -const uint16_t kSharpOneSpaceTicks = 70; -const uint16_t kSharpOneSpace = kSharpOneSpaceTicks * kSharpTick; -const uint16_t kSharpZeroSpaceTicks = 30; -const uint16_t kSharpZeroSpace = kSharpZeroSpaceTicks * kSharpTick; -const uint16_t kSharpGapTicks = 1677; -const uint16_t kSharpGap = kSharpGapTicks * kSharpTick; -// Address(5) + Command(8) + Expansion(1) + Check(1) -const uint64_t kSharpToggleMask = - ((uint64_t)1 << (kSharpBits - kSharpAddressBits)) - 1; -const uint64_t kSharpAddressMask = ((uint64_t)1 << kSharpAddressBits) - 1; -const uint64_t kSharpCommandMask = ((uint64_t)1 << kSharpCommandBits) - 1; - -using irutils::addBoolToString; -using irutils::addFanToString; -using irutils::addIntToString; -using irutils::addLabeledString; -using irutils::addModeToString; -using irutils::addModelToString; -using irutils::addSwingVToString; -using irutils::addTempToString; -using irutils::addToggleToString; -using irutils::minsToString; - -// Also used by Denon protocol -#if (SEND_SHARP || SEND_DENON) -/// Send a (raw) Sharp message -/// @note Status: STABLE / Working fine. -/// @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 his procedure handles the inversion of bits required per protocol. -/// The protocol spec says to send the LSB first, but legacy code & usage -/// has us sending the MSB first. Grrrr. Normal invocation of encodeSharp() -/// handles this for you, assuming you are using the correct/standard values. -/// e.g. sendSharpRaw(encodeSharp(address, command)); -void IRsend::sendSharpRaw(const uint64_t data, const uint16_t nbits, - const uint16_t repeat) { - uint64_t tempdata = data; - for (uint16_t i = 0; i <= repeat; i++) { - // Protocol demands that the data be sent twice; once normally, - // then with all but the address bits inverted. - // Note: Previously this used to be performed 3 times (normal, inverted, - // normal), however all data points to that being incorrect. - for (uint8_t n = 0; n < 2; n++) { - sendGeneric(0, 0, // No Header - kSharpBitMark, kSharpOneSpace, kSharpBitMark, kSharpZeroSpace, - kSharpBitMark, kSharpGap, tempdata, nbits, 38, true, - 0, // Repeats are handled already. - 33); - // Invert the data per protocol. This is always called twice, so it's - // returned to original upon exiting the inner loop. - tempdata ^= kSharpToggleMask; - } - } -} - -/// Encode a (raw) Sharp message from it's components. -/// Status: STABLE / Works okay. -/// @param[in] address The value of the address to be sent. -/// @param[in] command The value of the address to be sent. (8 bits) -/// @param[in] expansion The value of the expansion bit to use. -/// (0 or 1, typically 1) -/// @param[in] check The value of the check bit to use. (0 or 1, typically 0) -/// @param[in] MSBfirst Flag indicating MSB first or LSB first order. -/// @return A uint32_t containing the raw Sharp message for `sendSharpRaw()`. -/// @note Assumes the standard Sharp bit sizes. -/// Historically sendSharp() sends address & command in -/// MSB first order. This is actually incorrect. It should be sent in LSB -/// order. The behaviour of sendSharp() hasn't been changed to maintain -/// backward compatibility. -uint32_t IRsend::encodeSharp(const uint16_t address, const uint16_t command, - const uint16_t expansion, const uint16_t check, - const bool MSBfirst) { - // Mask any unexpected bits. - uint16_t tempaddress = GETBITS16(address, 0, kSharpAddressBits); - uint16_t tempcommand = GETBITS16(command, 0, kSharpCommandBits); - uint16_t tempexpansion = GETBITS16(expansion, 0, 1); - uint16_t tempcheck = GETBITS16(check, 0, 1); - - if (!MSBfirst) { // Correct bit order if needed. - tempaddress = reverseBits(tempaddress, kSharpAddressBits); - tempcommand = reverseBits(tempcommand, kSharpCommandBits); - } - // Concatenate all the bits. - return (tempaddress << (kSharpCommandBits + 2)) | (tempcommand << 2) | - (tempexpansion << 1) | tempcheck; -} - -/// Send a Sharp message -/// Status: DEPRECATED / Previously working fine. -/// @deprecated Only use this if you are using legacy from the original -/// Arduino-IRremote library. 99% of the time, you will want to use -/// `sendSharpRaw()` instead -/// @param[in] address Address value to be sent. -/// @param[in] command Command value 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 procedure has a non-standard invocation style compared to similar -/// sendProtocol() routines. This is due to legacy, compatibility, & historic -/// reasons. Normally the calling syntax version is like sendSharpRaw(). -/// This procedure transmits the address & command in MSB first order, which is -/// incorrect. This behaviour is left as-is to maintain backward -/// compatibility with legacy code. -/// In short, you should use sendSharpRaw(), encodeSharp(), and the correct -/// values of address & command instead of using this, & the wrong values. -void IRsend::sendSharp(const uint16_t address, uint16_t const command, - const uint16_t nbits, const uint16_t repeat) { - sendSharpRaw(encodeSharp(address, command, 1, 0, true), nbits, repeat); -} -#endif // (SEND_SHARP || SEND_DENON) - -// Used by decodeDenon too. -#if (DECODE_SHARP || DECODE_DENON) -/// Decode the supplied Sharp message. -/// Status: STABLE / Working fine. -/// @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. -/// @param[in] expansion Should we expect the expansion bit to be set. -/// Default is true. -/// @return True if it can decode it, false if it can't. -/// @note This procedure returns a value suitable for use in `sendSharpRaw()`. -/// @todo Need to ensure capture of the inverted message as it can -/// be missed due to the interrupt timeout used to detect an end of message. -/// Several compliance checks are disabled until that is resolved. -bool IRrecv::decodeSharp(decode_results *results, uint16_t offset, - const uint16_t nbits, const bool strict, - const bool expansion) { - if (results->rawlen <= 2 * nbits + kFooter - 1 + offset) - return false; // Not enough entries to be a Sharp message. - // Compliance - if (strict) { - if (nbits != kSharpBits) return false; // Request is out of spec. - // DISABLED - See TODO -#ifdef UNIT_TEST - // An in spec message has the data sent normally, then inverted. So we - // expect twice as many entries than to just get the results. - if (results->rawlen <= (2 * (2 * nbits + kFooter)) - 1 + offset) - return false; -#endif - } - - uint64_t data = 0; - - // Match Data + Footer - uint16_t used; - used = matchGeneric(results->rawbuf + offset, &data, - results->rawlen - offset, nbits, - 0, 0, // No Header - kSharpBitMark, kSharpOneSpace, - kSharpBitMark, kSharpZeroSpace, - kSharpBitMark, kSharpGap, true, 35); - if (!used) return false; - offset += used; - // Compliance - if (strict) { - // Check the state of the expansion bit is what we expect. - if ((data & 0b10) >> 1 != expansion) return false; - // The check bit should be cleared in a normal message. - if (data & 0b1) return false; - // DISABLED - See TODO -#ifdef UNIT_TEST - // Grab the second copy of the data (i.e. inverted) - uint64_t second_data = 0; - // Match Data + Footer - if (!matchGeneric(results->rawbuf + offset, &second_data, - results->rawlen - offset, nbits, - 0, 0, - kSharpBitMark, kSharpOneSpace, - kSharpBitMark, kSharpZeroSpace, - kSharpBitMark, kSharpGap, true, 35)) return false; - // Check that second_data has been inverted correctly. - if (data != (second_data ^ kSharpToggleMask)) return false; -#endif // UNIT_TEST - } - - // Success - results->decode_type = SHARP; - results->bits = nbits; - results->value = data; - // Address & command are actually transmitted in LSB first order. - results->address = reverseBits(data, nbits) & kSharpAddressMask; - results->command = - reverseBits((data >> 2) & kSharpCommandMask, kSharpCommandBits); - return true; -} -#endif // (DECODE_SHARP || DECODE_DENON) - -#if SEND_SHARP_AC -/// Send a Sharp A/C message. -/// Status: Alpha / Untested. -/// @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. -/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/638 -/// @see https://github.com/ToniA/arduino-heatpumpir/blob/master/SharpHeatpumpIR.cpp -void IRsend::sendSharpAc(const unsigned char data[], const uint16_t nbytes, - const uint16_t repeat) { - if (nbytes < kSharpAcStateLength) - return; // Not enough bytes to send a proper message. - - sendGeneric(kSharpAcHdrMark, kSharpAcHdrSpace, - kSharpAcBitMark, kSharpAcOneSpace, - kSharpAcBitMark, kSharpAcZeroSpace, - kSharpAcBitMark, kSharpAcGap, - data, nbytes, 38000, false, repeat, 50); -} -#endif // SEND_SHARP_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? -IRSharpAc::IRSharpAc(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 IRSharpAc::begin(void) { _irsend.begin(); } - -#if SEND_SHARP_AC -/// Send the current internal state as an IR message. -/// @param[in] repeat Nr. of times the message will be repeated. -void IRSharpAc::send(const uint16_t repeat) { - _irsend.sendSharpAc(getRaw(), kSharpAcStateLength, repeat); -} -#endif // SEND_SHARP_AC - -/// 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 4-bit checksum value. -uint8_t IRSharpAc::calcChecksum(uint8_t state[], const uint16_t length) { - uint8_t xorsum = xorBytes(state, length - 1); - xorsum ^= GETBITS8(state[length - 1], kLowNibble, kNibbleSize); - xorsum ^= GETBITS8(xorsum, kHighNibble, kNibbleSize); - return GETBITS8(xorsum, kLowNibble, kNibbleSize); -} - -/// 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 IRSharpAc::validChecksum(uint8_t state[], const uint16_t length) { - return GETBITS8(state[length - 1], kHighNibble, kNibbleSize) == - IRSharpAc::calcChecksum(state, length); -} - -/// Calculate and set the checksum values for the internal state. -void IRSharpAc::checksum(void) { - _.Sum = calcChecksum(_.raw); -} - -/// Reset the state of the remote to a known good state/sequence. -void IRSharpAc::stateReset(void) { - static const uint8_t reset[kSharpAcStateLength] = { - 0xAA, 0x5A, 0xCF, 0x10, 0x00, 0x01, 0x00, 0x00, 0x08, 0x80, 0x00, 0xE0, - 0x01}; - std::memcpy(_.raw, reset, kSharpAcStateLength); - _temp = getTemp(); - _mode = _.Mode; - _fan = _.Fan; - _model = getModel(true); -} - -/// 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 *IRSharpAc::getRaw(void) { - checksum(); // Ensure correct settings before sending. - 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 IRSharpAc::setRaw(const uint8_t new_code[], const uint16_t length) { - std::memcpy(_.raw, new_code, std::min(length, kSharpAcStateLength)); - _model = getModel(true); -} - -/// Set the model of the A/C to emulate. -/// @param[in] model The enum of the appropriate model. -void IRSharpAc::setModel(const sharp_ac_remote_model_t model) { - switch (model) { - case sharp_ac_remote_model_t::A705: - case sharp_ac_remote_model_t::A903: - _model = model; - _.Model = true; - break; - default: - _model = sharp_ac_remote_model_t::A907; - _.Model = false; - } - _.Model2 = (_model != sharp_ac_remote_model_t::A907); - // Redo the operating mode as some models don't support all modes. - setMode(_.Mode); -} - -/// Get/Detect the model of the A/C. -/// @param[in] raw Try to determine the model from the raw code only. -/// @return The enum of the compatible model. -sharp_ac_remote_model_t IRSharpAc::getModel(const bool raw) const { - if (raw) { - if (_.Model2) { - if (_.Model) - return sharp_ac_remote_model_t::A705; - else - return sharp_ac_remote_model_t::A903; - } else { - return sharp_ac_remote_model_t::A907; - } - } - return _model; -} - -/// Set the value of the Power Special setting without any checks. -/// @param[in] value The value to set Power Special to. -inline void IRSharpAc::setPowerSpecial(const uint8_t value) { - _.PowerSpecial = value; -} - -/// Get the value of the Power Special setting. -/// @return The setting's value. -uint8_t IRSharpAc::getPowerSpecial(void) const { - return _.PowerSpecial; -} - -/// Clear the "special"/non-normal bits in the power section. -/// e.g. for normal/common command modes. -void IRSharpAc::clearPowerSpecial(void) { - setPowerSpecial(_.PowerSpecial & kSharpAcPowerOn); -} - -/// Is one of the special power states in use? -/// @return true, it is. false, it isn't. -bool IRSharpAc::isPowerSpecial(void) const { - switch (_.PowerSpecial) { - case kSharpAcPowerSetSpecialOff: - case kSharpAcPowerSetSpecialOn: - case kSharpAcPowerTimerSetting: return true; - default: return false; - } -} - -/// Set the requested power state of the A/C to on. -void IRSharpAc::on(void) { setPower(true); } - -/// Set the requested power state of the A/C to off. -void IRSharpAc::off(void) { setPower(false); } - -/// Change the power setting, including the previous power state. -/// @param[in] on true, the setting is on. false, the setting is off. -/// @param[in] prev_on true, the setting is on. false, the setting is off. -void IRSharpAc::setPower(const bool on, const bool prev_on) { - setPowerSpecial(on ? (prev_on ? kSharpAcPowerOn : kSharpAcPowerOnFromOff) - : kSharpAcPowerOff); - // Power operations are incompatible with clean mode. - if (_.Clean) setClean(false); - _.Special = kSharpAcSpecialPower; -} - -/// Get the value of the current power setting. -/// @return true, the setting is on. false, the setting is off. -bool IRSharpAc::getPower(void) const { - switch (_.PowerSpecial) { - case kSharpAcPowerUnknown: - case kSharpAcPowerOff: return false; - default: return true; // Everything else is "probably" on. - } -} - -/// Set the value of the Special (button/command?) setting. -/// @param[in] mode The value to set Special to. -void IRSharpAc::setSpecial(const uint8_t mode) { - switch (mode) { - case kSharpAcSpecialPower: - case kSharpAcSpecialTurbo: - case kSharpAcSpecialTempEcono: - case kSharpAcSpecialFan: - case kSharpAcSpecialSwing: - case kSharpAcSpecialTimer: - case kSharpAcSpecialTimerHalfHour: - _.Special = mode; - break; - default: - _.Special = kSharpAcSpecialPower; - } -} - -/// Get the value of the Special (button/command?) setting. -/// @return The setting's value. -uint8_t IRSharpAc::getSpecial(void) const { return _.Special; } - -/// Set the temperature. -/// @param[in] temp The temperature in degrees celsius. -/// @param[in] save Do we save this setting as a user set one? -void IRSharpAc::setTemp(const uint8_t temp, const bool save) { - switch (_.Mode) { - // Auto & Dry don't allow temp changes and have a special temp. - case kSharpAcAuto: - case kSharpAcDry: - _.raw[kSharpAcByteTemp] = 0; - return; - default: - switch (getModel()) { - case sharp_ac_remote_model_t::A705: - _.raw[kSharpAcByteTemp] = 0xD0; - break; - default: - _.raw[kSharpAcByteTemp] = 0xC0; - } - } - uint8_t degrees = std::max(temp, kSharpAcMinTemp); - degrees = std::min(degrees, kSharpAcMaxTemp); - if (save) _temp = degrees; - _.Temp = degrees - kSharpAcMinTemp; - _.Special = kSharpAcSpecialTempEcono; - clearPowerSpecial(); -} - -/// Get the current temperature setting. -/// @return The current setting for temp. in degrees celsius. -uint8_t IRSharpAc::getTemp(void) const { - return _.Temp + kSharpAcMinTemp; -} - -/// Get the operating mode setting of the A/C. -/// @return The current operating mode setting. -uint8_t IRSharpAc::getMode(void) const { - return _.Mode; -} - -/// Set the operating mode of the A/C. -/// @param[in] mode The desired operating mode. -/// @param[in] save Do we save this setting as a user set one? -void IRSharpAc::setMode(const uint8_t mode, const bool save) { - uint8_t realMode = mode; - if (mode == kSharpAcHeat) { - switch (getModel()) { - case sharp_ac_remote_model_t::A705: - case sharp_ac_remote_model_t::A903: - // These models have no heat mode, use Fan mode instead. - realMode = kSharpAcFan; - break; - default: - break; - } - } - - switch (realMode) { - case kSharpAcAuto: // Also kSharpAcFan - case kSharpAcDry: - // When Dry or Auto, Fan always 2(Auto) - setFan(kSharpAcFanAuto, false); - // FALLTHRU - case kSharpAcCool: - case kSharpAcHeat: - _.Mode = realMode; - break; - default: - setFan(kSharpAcFanAuto, false); - _.Mode = kSharpAcAuto; - } - // Dry/Auto have no temp setting. This step will enforce it. - setTemp(_temp, false); - // Save the mode in case we need to revert to it. eg. Clean - if (save) _mode = _.Mode; - - _.Special = kSharpAcSpecialPower; - clearPowerSpecial(); -} - -/// Set the speed of the fan. -/// @param[in] speed The desired setting. -/// @param[in] save Do we save this setting as a user set one? -void IRSharpAc::setFan(const uint8_t speed, const bool save) { - switch (speed) { - case kSharpAcFanAuto: - case kSharpAcFanMin: - case kSharpAcFanMed: - case kSharpAcFanHigh: - case kSharpAcFanMax: - _.Fan = speed; - if (save) _fan = speed; - break; - default: - _.Fan = kSharpAcFanAuto; - _fan = kSharpAcFanAuto; - } - _.Special = kSharpAcSpecialFan; - clearPowerSpecial(); -} - -/// Get the current fan speed setting. -/// @return The current fan speed/mode. -uint8_t IRSharpAc::getFan(void) const { - return _.Fan; -} - -/// Get the Turbo setting of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRSharpAc::getTurbo(void) const { - return (_.PowerSpecial == kSharpAcPowerSetSpecialOn) && - (_.Special == kSharpAcSpecialTurbo); -} - -/// Set the Turbo setting of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -/// @note If you use this method, you will need to send it before making -/// other changes to the settings, as they may overwrite some of the bits -/// used by this setting. -void IRSharpAc::setTurbo(const bool on) { - if (on) setFan(kSharpAcFanMax); - setPowerSpecial(on ? kSharpAcPowerSetSpecialOn : kSharpAcPowerSetSpecialOff); - _.Special = kSharpAcSpecialTurbo; -} - -/// Get the Vertical Swing setting of the A/C. -/// @return The position of the Vertical Swing setting. -uint8_t IRSharpAc::getSwingV(void) const { return _.Swing; } - -/// Set the Vertical Swing setting of the A/C. -/// @note Some positions may not work on all models. -/// @param[in] position The desired position/setting. -/// @note `setSwingV(kSharpAcSwingVLowest)` will only allow the Lowest setting -/// in Heat mode, it will default to `kSharpAcSwingVLow` otherwise. -/// If you want to set this value in other modes e.g. Cool, you must -/// use `setSwingV`s optional `force` parameter. -/// @param[in] force Do we override the safety checks and just do it? -void IRSharpAc::setSwingV(const uint8_t position, const bool force) { - switch (position) { - case kSharpAcSwingVCoanda: - // Only allowed in Heat mode. - if (!force && getMode() != kSharpAcHeat) { - setSwingV(kSharpAcSwingVLow); // Use the next lowest setting. - return; - } - // FALLTHRU - case kSharpAcSwingVHigh: - case kSharpAcSwingVMid: - case kSharpAcSwingVLow: - case kSharpAcSwingVToggle: - case kSharpAcSwingVOff: - case kSharpAcSwingVLast: // Technically valid, but we don't use it. - // All expected non-positions set the special bits. - _.Special = kSharpAcSpecialSwing; - // FALLTHRU - case kSharpAcSwingVIgnore: - _.Swing = position; - } -} - -/// Convert a standard A/C vertical swing into its native setting. -/// @param[in] position A stdAc::swingv_t position to convert. -/// @return The equivalent native horizontal swing position. -uint8_t IRSharpAc::convertSwingV(const stdAc::swingv_t position) { - switch (position) { - case stdAc::swingv_t::kHighest: - case stdAc::swingv_t::kHigh: return kSharpAcSwingVHigh; - case stdAc::swingv_t::kMiddle: return kSharpAcSwingVMid; - case stdAc::swingv_t::kLow: return kSharpAcSwingVLow; - case stdAc::swingv_t::kLowest: return kSharpAcSwingVCoanda; - case stdAc::swingv_t::kAuto: return kSharpAcSwingVToggle; - case stdAc::swingv_t::kOff: return kSharpAcSwingVOff; - default: return kSharpAcSwingVIgnore; - } -} - -/// Get the (vertical) Swing Toggle setting of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRSharpAc::getSwingToggle(void) const { - return getSwingV() == kSharpAcSwingVToggle; -} - -/// Set the (vertical) Swing Toggle setting of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRSharpAc::setSwingToggle(const bool on) { - setSwingV(on ? kSharpAcSwingVToggle : kSharpAcSwingVIgnore); - if (on) _.Special = kSharpAcSpecialSwing; -} - -/// Get the Ion (Filter) setting of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRSharpAc::getIon(void) const { return _.Ion; } - -/// Set the Ion (Filter) setting of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRSharpAc::setIon(const bool on) { - _.Ion = on; - clearPowerSpecial(); - if (on) _.Special = kSharpAcSpecialSwing; -} - -/// Get the Economical mode toggle setting of the A/C. -/// @return true, the setting is on. false, the setting is off. -/// @note Shares the same location as the Light setting on A705. -bool IRSharpAc::_getEconoToggle(void) const { - return (_.PowerSpecial == kSharpAcPowerSetSpecialOn) && - (_.Special == kSharpAcSpecialTempEcono); -} - -/// Set the Economical mode toggle setting of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -/// @warning Probably incompatible with `setTurbo()` -/// @note Shares the same location as the Light setting on A705. -void IRSharpAc::_setEconoToggle(const bool on) { - if (on) _.Special = kSharpAcSpecialTempEcono; - setPowerSpecial(on ? kSharpAcPowerSetSpecialOn : kSharpAcPowerSetSpecialOff); -} - -/// Set the Economical mode toggle setting of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -/// @warning Probably incompatible with `setTurbo()` -/// @note Available on the A907 models. -void IRSharpAc::setEconoToggle(const bool on) { - if (_model == sharp_ac_remote_model_t::A907) _setEconoToggle(on); -} - -/// Get the Economical mode toggle setting of the A/C. -/// @return true, the setting is on. false, the setting is off. -/// @note Available on the A907 models. -bool IRSharpAc::getEconoToggle(void) const { - return _model == sharp_ac_remote_model_t::A907 && _getEconoToggle(); -} - -/// Set the Light mode toggle setting of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -/// @warning Probably incompatible with `setTurbo()` -/// @note Not available on the A907 model. -void IRSharpAc::setLightToggle(const bool on) { - if (_model != sharp_ac_remote_model_t::A907) _setEconoToggle(on); -} - -/// Get the Light toggle setting of the A/C. -/// @return true, the setting is on. false, the setting is off. -/// @note Not available on the A907 model. -bool IRSharpAc::getLightToggle(void) const { - return _model != sharp_ac_remote_model_t::A907 && _getEconoToggle(); -} - -/// Get how long the timer is set for, in minutes. -/// @return The time in nr of minutes. -uint16_t IRSharpAc::getTimerTime(void) const { - return _.TimerHours * kSharpAcTimerIncrement * 2 + - ((_.Special == kSharpAcSpecialTimerHalfHour) ? kSharpAcTimerIncrement - : 0); -} - -/// Is the Timer enabled? -/// @return true, the setting is on. false, the setting is off. -bool IRSharpAc::getTimerEnabled(void) const { return _.TimerEnabled; } - -/// Get the current timer type. -/// @return true, It's an "On" timer. false, It's an "Off" timer. -bool IRSharpAc::getTimerType(void) const { return _.TimerType; } - -/// Set or cancel the timer function. -/// @param[in] enable Is the timer to be enabled (true) or canceled(false)? -/// @param[in] timer_type An On (true) or an Off (false). Ignored if canceled. -/// @param[in] mins Nr. of minutes the timer is to be set to. -/// @note Rounds down to 30 min increments. (max: 720 mins (12h), 0 is Off) -void IRSharpAc::setTimer(bool enable, bool timer_type, uint16_t mins) { - uint8_t half_hours = std::min(mins / kSharpAcTimerIncrement, - kSharpAcTimerHoursMax * 2); - if (half_hours == 0) enable = false; - if (!enable) { - half_hours = 0; - timer_type = kSharpAcOffTimerType; - } - _.TimerEnabled = enable; - _.TimerType = timer_type; - _.TimerHours = half_hours / 2; - // Handle non-round hours. - _.Special = (half_hours % 2) ? kSharpAcSpecialTimerHalfHour - : kSharpAcSpecialTimer; - setPowerSpecial(kSharpAcPowerTimerSetting); -} - -/// Get the Clean setting of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRSharpAc::getClean(void) const { - return _.Clean; -} - -/// Set the Economical mode toggle setting of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -/// @note Officially A/C unit needs to be "Off" before clean mode can be entered -void IRSharpAc::setClean(const bool on) { - // Clean mode appears to be just default dry mode, with an extra bit set. - if (on) { - setMode(kSharpAcDry, false); - setPower(true, false); - } else { - // Restore the previous operation mode & fan speed. - setMode(_mode, false); - setFan(_fan, false); - } - _.Clean = on; - clearPowerSpecial(); -} - -/// 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 IRSharpAc::convertMode(const stdAc::opmode_t mode) { - switch (mode) { - case stdAc::opmode_t::kCool: return kSharpAcCool; - case stdAc::opmode_t::kHeat: return kSharpAcHeat; - case stdAc::opmode_t::kDry: return kSharpAcDry; - // No Fan mode. - default: return kSharpAcAuto; - } -} - -/// Convert a stdAc::fanspeed_t enum into it's native speed. -/// @param[in] speed The enum to be converted. -/// @param[in] model The enum of the appropriate model. -/// @return The native equivalent of the enum. -uint8_t IRSharpAc::convertFan(const stdAc::fanspeed_t speed, - const sharp_ac_remote_model_t model) { - switch (model) { - case sharp_ac_remote_model_t::A705: - case sharp_ac_remote_model_t::A903: - switch (speed) { - case stdAc::fanspeed_t::kLow: return kSharpAcFanA705Low; - case stdAc::fanspeed_t::kMedium: return kSharpAcFanA705Med; - default: {}; // Fall thru to the next/default clause if not the above - // special cases. - } - // FALL THRU - default: - switch (speed) { - case stdAc::fanspeed_t::kMin: - case stdAc::fanspeed_t::kLow: return kSharpAcFanMin; - case stdAc::fanspeed_t::kMedium: return kSharpAcFanMed; - case stdAc::fanspeed_t::kHigh: return kSharpAcFanHigh; - case stdAc::fanspeed_t::kMax: return kSharpAcFanMax; - default: return kSharpAcFanAuto; - } - } -} - -/// 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 IRSharpAc::toCommonMode(const uint8_t mode) const { - switch (mode) { - case kSharpAcCool: return stdAc::opmode_t::kCool; - case kSharpAcHeat: return stdAc::opmode_t::kHeat; - case kSharpAcDry: return stdAc::opmode_t::kDry; - case kSharpAcAuto: // Also kSharpAcFan - switch (getModel()) { - case sharp_ac_remote_model_t::A705: return stdAc::opmode_t::kFan; - default: return stdAc::opmode_t::kAuto; - } - break; - 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 IRSharpAc::toCommonFanSpeed(const uint8_t speed) const { - switch (getModel()) { - case sharp_ac_remote_model_t::A705: - case sharp_ac_remote_model_t::A903: - switch (speed) { - case kSharpAcFanA705Low: return stdAc::fanspeed_t::kLow; - case kSharpAcFanA705Med: return stdAc::fanspeed_t::kMedium; - } - // FALL-THRU - default: - switch (speed) { - case kSharpAcFanMax: return stdAc::fanspeed_t::kMax; - case kSharpAcFanHigh: return stdAc::fanspeed_t::kHigh; - case kSharpAcFanMed: return stdAc::fanspeed_t::kMedium; - case kSharpAcFanMin: return stdAc::fanspeed_t::kMin; - default: return stdAc::fanspeed_t::kAuto; - } - } -} - -/// Convert a native vertical swing postion to it's common equivalent. -/// @param[in] pos A native position to convert. -/// @param[in] mode What operating mode are we in? -/// @return The common vertical swing position. -stdAc::swingv_t IRSharpAc::toCommonSwingV(const uint8_t pos, - const stdAc::opmode_t mode) const { - switch (pos) { - case kSharpAcSwingVHigh: return stdAc::swingv_t::kHighest; - case kSharpAcSwingVMid: return stdAc::swingv_t::kMiddle; - case kSharpAcSwingVLow: return stdAc::swingv_t::kLow; - case kSharpAcSwingVCoanda: // Coanda has mode dependent positionss - switch (mode) { - case stdAc::opmode_t::kCool: return stdAc::swingv_t::kHighest; - case stdAc::opmode_t::kHeat: return stdAc::swingv_t::kLowest; - default: return stdAc::swingv_t::kOff; - } - case kSharpAcSwingVToggle: return stdAc::swingv_t::kAuto; - default: return stdAc::swingv_t::kOff; - } -} - -/// 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 IRSharpAc::toCommon(const stdAc::state_t *prev) const { - stdAc::state_t result; - // Start with the previous state if given it. - if (prev != NULL) result = *prev; - result.protocol = decode_type_t::SHARP_AC; - result.model = getModel(); - result.power = getPower(); - result.mode = toCommonMode(_.Mode); - result.celsius = true; - result.degrees = getTemp(); - result.fanspeed = toCommonFanSpeed(_.Fan); - result.turbo = getTurbo(); - if (getSwingV() != kSharpAcSwingVIgnore) - result.swingv = toCommonSwingV(getSwingV(), result.mode); - result.filter = _.Ion; - result.econo = getEconoToggle(); - result.light = getLightToggle(); - result.clean = _.Clean; - // Not supported. - result.swingh = stdAc::swingh_t::kOff; - result.quiet = 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 IRSharpAc::toString(void) const { - String result = ""; - const sharp_ac_remote_model_t model = getModel(); - result.reserve(170); // Reserve some heap for the string to reduce fragging. - result += addModelToString(decode_type_t::SHARP_AC, getModel(), false); - - result += addLabeledString(isPowerSpecial() ? String("-") - : String(getPower() ? kOnStr - : kOffStr), - kPowerStr); - const uint8_t mode = _.Mode; - result += addModeToString( - mode, - // Make the value invalid if the model doesn't support an Auto mode. - (model == sharp_ac_remote_model_t::A907) ? kSharpAcAuto : 255, - kSharpAcCool, kSharpAcHeat, kSharpAcDry, kSharpAcFan); - result += addTempToString(getTemp()); - switch (model) { - case sharp_ac_remote_model_t::A705: - case sharp_ac_remote_model_t::A903: - result += addFanToString(_.Fan, kSharpAcFanMax, kSharpAcFanA705Low, - kSharpAcFanAuto, kSharpAcFanAuto, - kSharpAcFanA705Med); - break; - default: - result += addFanToString(_.Fan, kSharpAcFanMax, kSharpAcFanMin, - kSharpAcFanAuto, kSharpAcFanAuto, - kSharpAcFanMed); - } - if (getSwingV() == kSharpAcSwingVIgnore) { - result += addIntToString(kSharpAcSwingVIgnore, kSwingVStr); - result += kSpaceLBraceStr; - result += kNAStr; - result += ')'; - } else { - result += addSwingVToString( - getSwingV(), 0xFF, - // Coanda means Highest when in Cool mode. - (mode == kSharpAcCool) ? kSharpAcSwingVCoanda : kSharpAcSwingVToggle, - kSharpAcSwingVHigh, - 0xFF, // Upper Middle is unused - kSharpAcSwingVMid, - 0xFF, // Lower Middle is unused - kSharpAcSwingVLow, - kSharpAcSwingVCoanda, - kSharpAcSwingVOff, - // Below are unused. - kSharpAcSwingVToggle, - 0xFF, - 0xFF); - } - result += addBoolToString(getTurbo(), kTurboStr); - result += addBoolToString(_.Ion, kIonStr); - switch (model) { - case sharp_ac_remote_model_t::A705: - case sharp_ac_remote_model_t::A903: - result += addToggleToString(getLightToggle(), kLightStr); - break; - default: - result += addToggleToString(getEconoToggle(), kEconoStr); - } - result += addBoolToString(_.Clean, kCleanStr); - if (_.TimerEnabled) - result += addLabeledString(minsToString(getTimerTime()), - _.TimerType ? kOnTimerStr : kOffTimerStr); - return result; -} - -#if DECODE_SHARP_AC -/// Decode the supplied Sharp A/C 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/638 -/// @see https://github.com/ToniA/arduino-heatpumpir/blob/master/SharpHeatpumpIR.cpp -bool IRrecv::decodeSharpAc(decode_results *results, uint16_t offset, - const uint16_t nbits, const bool strict) { - // Compliance - if (strict && nbits != kSharpAcBits) return false; - - // Match Header + Data + Footer - uint16_t used; - used = matchGeneric(results->rawbuf + offset, results->state, - results->rawlen - offset, nbits, - kSharpAcHdrMark, kSharpAcHdrSpace, - kSharpAcBitMark, kSharpAcOneSpace, - kSharpAcBitMark, kSharpAcZeroSpace, - kSharpAcBitMark, kSharpAcGap, true, - _tolerance, kMarkExcess, false); - if (used == 0) return false; - offset += used; - // Compliance - if (strict) { - if (!IRSharpAc::validChecksum(results->state)) return false; - } - - // Success - results->decode_type = SHARP_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_SHARP_AC diff --git a/lib/IRremoteESP8266/src/ir_Sharp.h b/lib/IRremoteESP8266/src/ir_Sharp.h index b3be534e7b..507f63e437 100644 --- a/lib/IRremoteESP8266/src/ir_Sharp.h +++ b/lib/IRremoteESP8266/src/ir_Sharp.h @@ -32,13 +32,13 @@ #ifndef UNIT_TEST #include #endif -#include "IRrecv.h" -#include "IRremoteESP8266.h" -#include "IRsend.h" +#include "../src/IRrecv.h" +#include "../src/IRremoteESP8266.h" +#include "../src/IRsend.h" #ifdef UNIT_TEST -#include "IRsend_test.h" +#include "../src/IRsend_test.h" #endif -#include "IRutils.h" +#include "../src/IRutils.h" /// Native representation of a Sharp A/C message. union SharpProtocol{ diff --git a/lib/IRremoteESP8266/src/ir_Sherwood.cpp b/lib/IRremoteESP8266/src/ir_Sherwood.cpp deleted file mode 100644 index 76ffc35ff0..0000000000 --- a/lib/IRremoteESP8266/src/ir_Sherwood.cpp +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright 2017 David Conran - -/// @file -/// @brief Support for Sherwood protocols. - -// Supports: -// Brand: Sherwood, Model: RC-138 remote -// Brand: Sherwood, Model: RD6505(B) Receiver - -#include -#include "IRsend.h" - -#if SEND_SHERWOOD -/// Send an IR command to a Sherwood device. -/// 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 Sherwood remote codes appear to be NEC codes with a mandatory repeat -/// code. i.e. repeat should be >= kSherwoodMinRepeat (1). -void IRsend::sendSherwood(uint64_t data, uint16_t nbits, uint16_t repeat) { - sendNEC(data, nbits, std::max((uint16_t)kSherwoodMinRepeat, repeat)); -} -#endif // SEND_SHERWOOD diff --git a/lib/IRremoteESP8266/src/ir_Symphony.cpp b/lib/IRremoteESP8266/src/ir_Symphony.cpp deleted file mode 100644 index b629ef72d3..0000000000 --- a/lib/IRremoteESP8266/src/ir_Symphony.cpp +++ /dev/null @@ -1,95 +0,0 @@ -// Copyright 2020 David Conran - -/// @file -/// @brief Support for Symphony protocols. -/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1057 -/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1105 -/// @see https://www.alldatasheet.com/datasheet-pdf/pdf/124369/ANALOGICTECH/SM5021B.html - -// Supports: -// Brand: Symphony, Model: Air Cooler 3Di -// Brand: SamHop, Model: SM3015 Fan Remote Control -// Brand: SamHop, Model: SM5021 Encoder chip -// Brand: SamHop, Model: SM5032 Decoder chip -// Brand: Blyss, Model: Owen-SW-5 3 Fan -// Brand: Blyss, Model: WP-YK8 090218 remote -// Brand: Westinghouse, Model: Ceiling fan -// Brand: Westinghouse, Model: 78095 Remote -// Brand: Satellite Electronic, Model: ID6 Remote -// Brand: Satellite Electronic, Model: JY199I Fan driver -// Brand: Satellite Electronic, Model: JY199I-L Fan driver -// Brand: SilverCrest, Model: SSVS 85 A1 Fan - -// Known Codes: -// SilverCrest SSVS 85 A1 Fan: -// 0x581 - On/Off -// 0x582 - Speed -// 0x584 - Mist -// 0x588 - Timer -// 0x590 - OSC - -#include -#include "IRrecv.h" -#include "IRsend.h" -#include "IRtimer.h" -#include "IRutils.h" - -// Constants -const uint16_t kSymphonyZeroMark = 400; -const uint16_t kSymphonyZeroSpace = 1250; -const uint16_t kSymphonyOneMark = kSymphonyZeroSpace; -const uint16_t kSymphonyOneSpace = kSymphonyZeroMark; -const uint32_t kSymphonyFooterGap = 4 * (kSymphonyZeroMark + - kSymphonyZeroSpace); - -#if SEND_SYMPHONY -/// Send a Symphony packet. -/// 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. -void IRsend::sendSymphony(uint64_t data, uint16_t nbits, uint16_t repeat) { - sendGeneric(0, 0, - kSymphonyOneMark, kSymphonyOneSpace, - kSymphonyZeroMark, kSymphonyZeroSpace, - 0, kSymphonyFooterGap, - data, nbits, 38000, true, repeat, kDutyDefault); -} -#endif // SEND_SYMPHONY - -#if DECODE_SYMPHONY -/// Decode the supplied Symphony packet/message. -/// Status: STABLE / Should 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::decodeSymphony(decode_results *results, uint16_t offset, - const uint16_t nbits, const bool strict) { - uint64_t data = 0; - - if (results->rawlen < 2 * nbits + offset - 1) - return false; // Not enough entries to ever be SYMPHONY. - // Compliance - if (strict && nbits != kSymphonyBits) return false; - - if (!matchGenericConstBitTime(results->rawbuf + offset, &data, - results->rawlen - offset, - nbits, - 0, 0, // No Header - kSymphonyOneMark, kSymphonyZeroMark, - 0, kSymphonyFooterGap, true, - _tolerance, 0)) - return false; - - // Success - results->value = data; - results->decode_type = decode_type_t::SYMPHONY; - results->bits = nbits; - results->address = 0; - results->command = 0; - return true; -} -#endif // DECODE_SYMPHONY diff --git a/lib/IRremoteESP8266/src/ir_Tcl.cpp b/lib/IRremoteESP8266/src/ir_Tcl.cpp deleted file mode 100644 index a9e8784a39..0000000000 --- a/lib/IRremoteESP8266/src/ir_Tcl.cpp +++ /dev/null @@ -1,529 +0,0 @@ -// Copyright 2019, 2021 David Conran - -/// @file -/// @brief Support for TCL protocols. - -#include "ir_Tcl.h" -#include -#include -#ifndef ARDUINO -#include -#endif -#include "IRremoteESP8266.h" -#include "IRtext.h" -#include "IRutils.h" - -// Constants - -const uint8_t kTcl112AcTimerResolution = 20; // Minutes -const uint16_t kTcl112AcTimerMax = 720; // Minutes (12 hrs) - -using irutils::addBoolToString; -using irutils::addFanToString; -using irutils::addIntToString; -using irutils::addLabeledString; -using irutils::addModeToString; -using irutils::addModelToString; -using irutils::addSwingVToString; -using irutils::addTempFloatToString; -using irutils::minsToString; - -#if SEND_TCL112AC -/// Send a TCL 112-bit A/C message. -/// 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::sendTcl112Ac(const unsigned char data[], const uint16_t nbytes, - const uint16_t repeat) { - sendGeneric(kTcl112AcHdrMark, kTcl112AcHdrSpace, - kTcl112AcBitMark, kTcl112AcOneSpace, - kTcl112AcBitMark, kTcl112AcZeroSpace, - kTcl112AcBitMark, kTcl112AcGap, - data, nbytes, 38000, false, repeat, 50); -} -#endif // SEND_TCL112AC - -/// 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? -IRTcl112Ac::IRTcl112Ac(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 IRTcl112Ac::begin(void) { _irsend.begin(); } - -#if SEND_TCL112AC -/// Send the current internal state as an IR message. -/// @param[in] repeat Nr. of times the message will be repeated. -void IRTcl112Ac::send(const uint16_t repeat) { - uint8_t save[kTcl112AcStateLength]; - // Do we need to send the special "quiet" message? - if (_quiet != _quiet_prev) { - // Backup the current state. - std::memcpy(save, _.raw, kTcl112AcStateLength); - const uint8_t quiet_off[kTcl112AcStateLength] = { - 0x23, 0xCB, 0x26, 0x02, 0x00, 0x40, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x65}; - // Use a known good quiet/mute off/type 2 state for the time being. - // Ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/1528#issuecomment-876989044 - setRaw(quiet_off); - setQuiet(_quiet); - // Send it. - _irsend.sendTcl112Ac(getRaw(), kTcl112AcStateLength, repeat); - // Now it's been sent, update the quiet previous state. - _quiet_prev = _quiet; - // Restore the old state. - setRaw(save); - // Make sure it looks like a normal TCL mesg if needed. - if (_.MsgType == kTcl112AcNormal) _.isTcl = true; - } - // Send the normal (type 1) state. - _irsend.sendTcl112Ac(getRaw(), kTcl112AcStateLength, repeat); -} -#endif // SEND_TCL112AC - -/// 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 IRTcl112Ac::calcChecksum(uint8_t state[], const uint16_t length) { - if (length) { - if (length > 4 && state[3] == 0x02) { // Special nessage? - return sumBytes(state, length - 1, 0xF); // Checksum needs an offset. - } else { - return sumBytes(state, length - 1); - } - } else { - return 0; - } -} - -/// Calculate & set the checksum for the current internal state of the remote. -/// @param[in] length The length/size of the internal array to checksum. -void IRTcl112Ac::checksum(const uint16_t length) { - // Stored the checksum value in the last byte. - if (length > 1) - _.Sum = calcChecksum(_.raw, 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/size of the array. -/// @return true, if the state has a valid checksum. Otherwise, false. -bool IRTcl112Ac::validChecksum(uint8_t state[], const uint16_t length) { - return (length > 1 && state[length - 1] == calcChecksum(state, length)); -} - -/// Check the supplied state looks like a TCL112AC message. -/// @param[in] state The array to verify the checksum of. -/// @note Assumes the state is the correct size. -/// @return true, if the state looks like a TCL112AC message. Otherwise, false. -/// @warning This is just a guess. -bool IRTcl112Ac::isTcl(const uint8_t state[]) { - Tcl112Protocol mesg; - std::memcpy(mesg.raw, state, kTcl112AcStateLength); - return (mesg.MsgType != kTcl112AcNormal) || mesg.isTcl; -} - -/// Reset the internal state of the emulation. (On, Cool, 24C) -void IRTcl112Ac::stateReset(void) { - // A known good state. (On, Cool, 24C) - static const uint8_t reset[kTcl112AcStateLength] = { - 0x23, 0xCB, 0x26, 0x01, 0x00, 0x24, 0x03, 0x07, 0x40, 0x00, 0x00, 0x00, - 0x00, 0x03}; - std::memcpy(_.raw, reset, kTcl112AcStateLength); - _quiet = false; - _quiet_prev = false; - _quiet_explictly_set = false; -} - -/// Get/Detect the model of the A/C. -/// @return The enum of the compatible model. -tcl_ac_remote_model_t IRTcl112Ac::getModel(void) const { - return isTcl(_.raw) ? tcl_ac_remote_model_t::TAC09CHSD - : tcl_ac_remote_model_t::GZ055BE1; -} - -/// Set the model of the A/C to emulate. -/// @param[in] model The enum of the appropriate model. -void IRTcl112Ac::setModel(const tcl_ac_remote_model_t model) { - _.isTcl = (model != tcl_ac_remote_model_t::GZ055BE1); -} - -/// 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* IRTcl112Ac::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 IRTcl112Ac::setRaw(const uint8_t new_code[], const uint16_t length) { - std::memcpy(_.raw, new_code, std::min(length, kTcl112AcStateLength)); -} - -/// Set the requested power state of the A/C to on. -void IRTcl112Ac::on(void) { setPower(true); } - -/// Set the requested power state of the A/C to off. -void IRTcl112Ac::off(void) { setPower(false); } - -/// Change the power setting. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRTcl112Ac::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 IRTcl112Ac::getPower(void) const { return _.Power; } - -/// Get the operating mode setting of the A/C. -/// @return The current operating mode setting. -uint8_t IRTcl112Ac::getMode(void) const { return _.Mode; } - -/// Set the operating mode of the A/C. -/// @param[in] mode The desired operating mode. -/// @note Fan/Ventilation mode sets the fan speed to high. -/// Unknown values default to Auto. -void IRTcl112Ac::setMode(const uint8_t mode) { - // If we get an unexpected mode, default to AUTO. - switch (mode) { - case kTcl112AcFan: - setFan(kTcl112AcFanHigh); - // FALLTHRU - case kTcl112AcAuto: - case kTcl112AcCool: - case kTcl112AcHeat: - case kTcl112AcDry: - _.Mode = mode; - break; - default: - _.Mode = kTcl112AcAuto; - } -} - -/// Set the temperature. -/// @param[in] celsius The temperature in degrees celsius. -/// @note The temperature resolution is 0.5 of a degree. -void IRTcl112Ac::setTemp(const float celsius) { - // Make sure we have desired temp in the correct range. - float safecelsius = std::max(celsius, kTcl112AcTempMin); - safecelsius = std::min(safecelsius, kTcl112AcTempMax); - // Convert to integer nr. of half degrees. - uint8_t nrHalfDegrees = safecelsius * 2; - // Do we have a half degree celsius? - _.HalfDegree = nrHalfDegrees & 1; - _.Temp = static_cast(kTcl112AcTempMax - nrHalfDegrees / 2); -} - -/// Get the current temperature setting. -/// @return The current setting for temp. in degrees celsius. -/// @note The temperature resolution is 0.5 of a degree. -float IRTcl112Ac::getTemp(void) const { - float result = kTcl112AcTempMax - _.Temp; - if (_.HalfDegree) result += 0.5; - return result; -} - -/// Set the speed of the fan. -/// @param[in] speed The desired setting. -/// @note Unknown speeds will default to Auto. -void IRTcl112Ac::setFan(const uint8_t speed) { - switch (speed) { - case kTcl112AcFanAuto: - case kTcl112AcFanMin: - case kTcl112AcFanLow: - case kTcl112AcFanMed: - case kTcl112AcFanHigh: - _.Fan = speed; - break; - default: - _.Fan = kTcl112AcFanAuto; - } -} - -/// Get the current fan speed setting. -/// @return The current fan speed/mode. -uint8_t IRTcl112Ac::getFan(void) const { return _.Fan; } - -/// Set the economy setting of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRTcl112Ac::setEcono(const bool on) { _.Econo = on; } - -/// Get the economy setting of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRTcl112Ac::getEcono(void) const { return _.Econo; } - -/// Set the Health (Filter) setting of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRTcl112Ac::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 IRTcl112Ac::getHealth(void) const { return _.Health; } - -/// Set the Light (LED/Display) setting of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRTcl112Ac::setLight(const bool on) { _.Light = !on; } // Cleared when on. - -/// Get the Light (LED/Display) setting of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRTcl112Ac::getLight(void) const { return !_.Light; } - -/// Set the horizontal swing setting of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRTcl112Ac::setSwingHorizontal(const bool on) { _.SwingH = on; } - -/// Get the horizontal swing setting of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRTcl112Ac::getSwingHorizontal(void) const { return _.SwingH; } - -/// Set the vertical swing setting of the A/C. -/// @param[in] setting The value of the desired setting. -void IRTcl112Ac::setSwingVertical(const uint8_t setting) { - switch (setting) { - case kTcl112AcSwingVOff: - case kTcl112AcSwingVHighest: - case kTcl112AcSwingVHigh: - case kTcl112AcSwingVMiddle: - case kTcl112AcSwingVLow: - case kTcl112AcSwingVLowest: - case kTcl112AcSwingVOn: - _.SwingV = setting; - } -} - -/// Get the vertical swing setting of the A/C. -/// @return The current setting. -uint8_t IRTcl112Ac::getSwingVertical(void) const { return _.SwingV; } - -/// Set the Turbo setting of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRTcl112Ac::setTurbo(const bool on) { - _.Turbo = on; - if (on) { - _.Fan = kTcl112AcFanHigh; - _.SwingV = kTcl112AcSwingVOn; - } -} - -/// Get the Turbo setting of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRTcl112Ac::getTurbo(void) const { return _.Turbo; } - -/// Set the Quiet setting of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRTcl112Ac::setQuiet(const bool on) { - _quiet_explictly_set = true; - _quiet = on; - if (_.MsgType == kTcl112AcSpecial) _.Quiet = on; -} - -/// Get the Quiet setting of the A/C. -/// @param[in] def The default value to use if we are not sure. -/// @return true, the setting is on. false, the setting is off. -bool IRTcl112Ac::getQuiet(const bool def) const { - if (_.MsgType == kTcl112AcSpecial) - return _.Quiet; - else - return _quiet_explictly_set ? _quiet : def; -} - -/// Get how long the On Timer is set for, in minutes. -/// @return The time in nr of minutes. -uint16_t IRTcl112Ac::getOnTimer(void) const { - return _.OnTimer * kTcl112AcTimerResolution; -} - -/// Set or cancel the On Timer function. -/// @param[in] mins Nr. of minutes the timer is to be set to. -/// @note Rounds down to 20 min increments. (max: 720 mins (12h), 0 is Off) -void IRTcl112Ac::setOnTimer(const uint16_t mins) { - _.OnTimer = std::min(mins, kTcl112AcTimerMax) / kTcl112AcTimerResolution; - _.OnTimerEnabled = _.OnTimer > 0; - _.TimerIndicator = _.OnTimerEnabled || _.OffTimerEnabled; -} - -/// Get how long the Off Timer is set for, in minutes. -/// @return The time in nr of minutes. -uint16_t IRTcl112Ac::getOffTimer(void) const { - return _.OffTimer * kTcl112AcTimerResolution; -} - -/// Set or cancel the Off Timer function. -/// @param[in] mins Nr. of minutes the timer is to be set to. -/// @note Rounds down to 20 min increments. (max: 720 mins (12h), 0 is Off) -void IRTcl112Ac::setOffTimer(const uint16_t mins) { - _.OffTimer = std::min(mins, kTcl112AcTimerMax) / kTcl112AcTimerResolution; - _.OffTimerEnabled = _.OffTimer > 0; - _.TimerIndicator = _.OnTimerEnabled || _.OffTimerEnabled; -} - -/// 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 IRTcl112Ac::convertMode(const stdAc::opmode_t mode) { - switch (mode) { - case stdAc::opmode_t::kCool: return kTcl112AcCool; - case stdAc::opmode_t::kHeat: return kTcl112AcHeat; - case stdAc::opmode_t::kDry: return kTcl112AcDry; - case stdAc::opmode_t::kFan: return kTcl112AcFan; - default: return kTcl112AcAuto; - } -} - -/// 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 IRTcl112Ac::convertFan(const stdAc::fanspeed_t speed) { - switch (speed) { - case stdAc::fanspeed_t::kMin: return kTcl112AcFanMin; - case stdAc::fanspeed_t::kLow: return kTcl112AcFanLow; - case stdAc::fanspeed_t::kMedium: return kTcl112AcFanMed; - case stdAc::fanspeed_t::kHigh: - case stdAc::fanspeed_t::kMax: return kTcl112AcFanHigh; - default: return kTcl112AcFanAuto; - } -} - -/// 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 IRTcl112Ac::toCommonMode(const uint8_t mode) { - switch (mode) { - case kTcl112AcCool: return stdAc::opmode_t::kCool; - case kTcl112AcHeat: return stdAc::opmode_t::kHeat; - case kTcl112AcDry: return stdAc::opmode_t::kDry; - case kTcl112AcFan: return stdAc::opmode_t::kFan; - default: return stdAc::opmode_t::kAuto; - } -} - -/// 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 IRTcl112Ac::convertSwingV(const stdAc::swingv_t position) { - switch (position) { - case stdAc::swingv_t::kOff: return kTcl112AcSwingVOff; - case stdAc::swingv_t::kHighest: return kTcl112AcSwingVHighest; - case stdAc::swingv_t::kHigh: return kTcl112AcSwingVHigh; - case stdAc::swingv_t::kMiddle: return kTcl112AcSwingVMiddle; - case stdAc::swingv_t::kLow: return kTcl112AcSwingVLow; - case stdAc::swingv_t::kLowest: return kTcl112AcSwingVLowest; - default: return kTcl112AcSwingVOn; - } -} - -/// Convert a native fan speed into its stdAc equivalent. -/// @param[in] spd The native setting to be converted. -/// @return The stdAc equivalent of the native setting. -stdAc::fanspeed_t IRTcl112Ac::toCommonFanSpeed(const uint8_t spd) { - switch (spd) { - case kTcl112AcFanHigh: return stdAc::fanspeed_t::kMax; - case kTcl112AcFanMed: return stdAc::fanspeed_t::kMedium; - case kTcl112AcFanLow: return stdAc::fanspeed_t::kLow; - case kTcl112AcFanMin: return stdAc::fanspeed_t::kMin; - default: return stdAc::fanspeed_t::kAuto; - } -} - -/// Convert a native vertical swing postion to it's common equivalent. -/// @param[in] setting A native position to convert. -/// @return The common vertical swing position. -stdAc::swingv_t IRTcl112Ac::toCommonSwingV(const uint8_t setting) { - switch (setting) { - case kTcl112AcSwingVOff: return stdAc::swingv_t::kOff; - default: return stdAc::swingv_t::kAuto; - } -} -/// 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 IRTcl112Ac::toCommon(const stdAc::state_t *prev) const { - stdAc::state_t result; - // Start with the previous state if given it. - if (prev != NULL) result = *prev; - result.protocol = decode_type_t::TCL112AC; - result.model = getModel(); - result.quiet = getQuiet(result.quiet); - // The rest only get updated if it is a "normal" message. - if (_.MsgType == kTcl112AcNormal) { - result.power = _.Power; - result.mode = toCommonMode(_.Mode); - result.celsius = true; - result.degrees = getTemp(); - result.fanspeed = toCommonFanSpeed(_.Fan); - result.swingv = toCommonSwingV(_.SwingV); - result.swingh = _.SwingH ? stdAc::swingh_t::kAuto : stdAc::swingh_t::kOff; - result.turbo = _.Turbo; - result.filter = _.Health; - result.econo = _.Econo; - result.light = getLight(); - } - // Not supported. - 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 IRTcl112Ac::toString(void) const { - String result = ""; - result.reserve(220); // Reserve some heap for the string to reduce fragging. - tcl_ac_remote_model_t model = getModel(); - result += addModelToString(decode_type_t::TCL112AC, model, false); - result += addIntToString(_.MsgType, D_STR_TYPE); - switch (_.MsgType) { - case kTcl112AcNormal: - result += addBoolToString(_.Power, kPowerStr); - result += addModeToString(_.Mode, kTcl112AcAuto, kTcl112AcCool, - kTcl112AcHeat, kTcl112AcDry, kTcl112AcFan); - result += addTempFloatToString(getTemp()); - result += addFanToString(_.Fan, kTcl112AcFanHigh, kTcl112AcFanLow, - kTcl112AcFanAuto, kTcl112AcFanMin, - kTcl112AcFanMed); - result += addSwingVToString(_.SwingV, kTcl112AcSwingVOff, - kTcl112AcSwingVHighest, - kTcl112AcSwingVHigh, - 0xFF, // unused - kTcl112AcSwingVMiddle, - 0xFF, // unused - kTcl112AcSwingVLow, - kTcl112AcSwingVLowest, - kTcl112AcSwingVOff, - kTcl112AcSwingVOn, // Swing - 0xFF, 0xFF); // Both Unused - if (model != tcl_ac_remote_model_t::GZ055BE1) { - result += addBoolToString(_.SwingH, kSwingHStr); - result += addBoolToString(_.Econo, kEconoStr); - result += addBoolToString(_.Health, kHealthStr); - result += addBoolToString(_.Turbo, kTurboStr); - result += addBoolToString(getLight(), kLightStr); - } - result += addLabeledString( - _.OnTimerEnabled ? minsToString(getOnTimer()) : kOffStr, - kOnTimerStr); - result += addLabeledString( - _.OffTimerEnabled ? minsToString(getOffTimer()) : kOffStr, - kOffTimerStr); - break; - case kTcl112AcSpecial: - result += addBoolToString(_.Quiet, kQuietStr); - break; - } - return result; -} - -#if DECODE_TCL112AC -/// @file -/// @note There is no `decodedecodeTcl112Ac()`. -/// It's the same as `decodeMitsubishi112()`. A shared routine is used. -/// You can find it in: ir_Mitsubishi.cpp -#endif // DECODE_TCL112AC diff --git a/lib/IRremoteESP8266/src/ir_Tcl.h b/lib/IRremoteESP8266/src/ir_Tcl.h index c7ae038d03..34bbcb3055 100644 --- a/lib/IRremoteESP8266/src/ir_Tcl.h +++ b/lib/IRremoteESP8266/src/ir_Tcl.h @@ -17,11 +17,11 @@ #ifndef UNIT_TEST #include #endif -#include "IRremoteESP8266.h" -#include "IRsend.h" -#include "IRrecv.h" +#include "../src/IRremoteESP8266.h" +#include "../src/IRsend.h" +#include "../src/IRrecv.h" #ifdef UNIT_TEST -#include "IRsend_test.h" +#include "../src/IRsend_test.h" #endif /// Native representation of a TCL 112 A/C message. diff --git a/lib/IRremoteESP8266/src/ir_Technibel.cpp b/lib/IRremoteESP8266/src/ir_Technibel.cpp deleted file mode 100644 index d58cc7e055..0000000000 --- a/lib/IRremoteESP8266/src/ir_Technibel.cpp +++ /dev/null @@ -1,408 +0,0 @@ -// Copyright 2020 Quentin Briollant - -/// @file -/// @brief Support for Technibel protocol. - -#include "ir_Technibel.h" -#include "IRrecv.h" -#include "IRsend.h" -#include "IRtext.h" -#include "IRutils.h" -#include - -using irutils::addBoolToString; -using irutils::addModeToString; -using irutils::addFanToString; -using irutils::addLabeledString; -using irutils::addTempToString; -using irutils::minsToString; - -const uint16_t kTechnibelAcHdrMark = 8836; -const uint16_t kTechnibelAcHdrSpace = 4380; -const uint16_t kTechnibelAcBitMark = 523; -const uint16_t kTechnibelAcOneSpace = 1696; -const uint16_t kTechnibelAcZeroSpace = 564; -const uint32_t kTechnibelAcGap = kDefaultMessageGap; -const uint16_t kTechnibelAcFreq = 38000; - - -#if SEND_TECHNIBEL_AC -/// Send an Technibel AC formatted message. -/// Status: STABLE / Reported as working on a real device. -/// @param[in] data containing the IR command. -/// @param[in] nbits Nr. of bits to send. usually kTechnibelAcBits -/// @param[in] repeat Nr. of times the message is to be repeated. -void IRsend::sendTechnibelAc(const uint64_t data, const uint16_t nbits, - const uint16_t repeat) { - sendGeneric(kTechnibelAcHdrMark, kTechnibelAcHdrSpace, - kTechnibelAcBitMark, kTechnibelAcOneSpace, - kTechnibelAcBitMark, kTechnibelAcZeroSpace, - kTechnibelAcBitMark, kTechnibelAcGap, - data, nbits, kTechnibelAcFreq, true, // LSB First. - repeat, kDutyDefault); -} -#endif // SEND_TECHNIBEL_AC - -#if DECODE_TECHNIBEL_AC -/// Status: STABLE / Reported as working on a real device -/// @param[in,out] results Ptr to 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 (kTechnibelAcBits). -/// @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::decodeTechnibelAc(decode_results *results, uint16_t offset, - const uint16_t nbits, const bool strict) { - // Compliance - if (strict && nbits != kTechnibelAcBits) { - return false; - } - - uint64_t data = 0; - - // Header + Data + Footer - if (!matchGeneric(results->rawbuf + offset, &data, - results->rawlen - offset, nbits, - kTechnibelAcHdrMark, kTechnibelAcHdrSpace, - kTechnibelAcBitMark, kTechnibelAcOneSpace, - kTechnibelAcBitMark, kTechnibelAcZeroSpace, - kTechnibelAcBitMark, kTechnibelAcGap, true, - _tolerance, kMarkExcess, true)) return false; - - // Compliance - if (strict && !IRTechnibelAc::validChecksum(data)) return false; - - // Success - results->decode_type = decode_type_t::TECHNIBEL_AC; - results->bits = nbits; - results->value = data; - results->command = 0; - results->address = 0; - return true; -} -#endif // DECODE_TECHNIBEL_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? -IRTechnibelAc::IRTechnibelAc(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 IRTechnibelAc::begin(void) { _irsend.begin(); } - -#if SEND_TECHNIBEL_AC -/// Send the current internal state as an IR message. -/// @param[in] repeat Nr. of times the message will be repeated. -void IRTechnibelAc::send(const uint16_t repeat) { - _irsend.sendTechnibelAc(getRaw(), kTechnibelAcBits, repeat); -} -#endif // SEND_TECHNIBEL_AC - -/// Compute the checksum of the supplied state. -/// @param[in] state A valid code for this protocol. -/// @return The calculated checksum of the supplied state. -uint8_t IRTechnibelAc::calcChecksum(const uint64_t state) { - uint8_t sum = 0; - // Add up all the 8 bit data chunks. - for (uint8_t offset = kTechnibelAcTimerHoursOffset; - offset < kTechnibelAcHeaderOffset; offset += 8) - sum += GETBITS64(state, offset, 8); - return ~sum + 1; -} - -/// Confirm the checksum of the supplied state is valid. -/// @param[in] state A valid code for this protocol. -/// @return `true` if the checksum is correct, otherwise `false`. -bool IRTechnibelAc::validChecksum(const uint64_t state) { - TechnibelProtocol p{.raw = state}; - return calcChecksum(state) == p.Sum; -} - -/// Set the checksum of the internal state. -void IRTechnibelAc::checksum(void) { - _.Sum = calcChecksum(_.raw); -} - -/// Reset the internal state of the emulation. -/// @note Mode:Cool, Power:Off, fan:Low, temp:20, swing:Off, sleep:Off -void IRTechnibelAc::stateReset(void) { - _.raw = kTechnibelAcResetState; - _saved_temp = 20; // DegC (Random reasonable default value) - _saved_temp_units = 0; // Celsius -} - -/// Get a copy of the internal state/code for this protocol. -/// @return A code for this protocol based on the current internal state. -uint64_t IRTechnibelAc::getRaw(void) { - checksum(); - return _.raw; -} - -/// Set the internal state from a valid code for this protocol. -/// @param[in] state A valid code for this protocol. -void IRTechnibelAc::setRaw(const uint64_t state) { - _.raw = state; -} - -/// Set the requested power state of the A/C to on. -void IRTechnibelAc::on(void) { setPower(true); } - -/// Set the requested power state of the A/C to off. -void IRTechnibelAc::off(void) { setPower(false); } - -/// Change the power setting. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRTechnibelAc::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 IRTechnibelAc::getPower(void) const { - return _.Power; -} - -/// Set the temperature unit setting. -/// @param[in] fahrenheit true, the unit is °F. false, the unit is °C. -void IRTechnibelAc::setTempUnit(const bool fahrenheit) { - _saved_temp_units = fahrenheit; - _.UseFah = fahrenheit; -} - -/// Get the temperature unit setting. -/// @return true, the unit is °F. false, the unit is °C. -bool IRTechnibelAc::getTempUnit(void) const { - return _.UseFah; -} - -/// Set the temperature. -/// @param[in] degrees The temperature in degrees. -/// @param[in] fahrenheit The temperature unit: true=°F, false(default)=°C. -void IRTechnibelAc::setTemp(const uint8_t degrees, const bool fahrenheit) { - setTempUnit(fahrenheit); - uint8_t temp_min = fahrenheit ? kTechnibelAcTempMinF : kTechnibelAcTempMinC; - uint8_t temp_max = fahrenheit ? kTechnibelAcTempMaxF : kTechnibelAcTempMaxC; - _saved_temp = std::min(temp_max, std::max(temp_min, degrees)); - _.Temp = _saved_temp; -} - -/// Get the current temperature setting. -/// @return The current setting for temp. in degrees. -uint8_t IRTechnibelAc::getTemp(void) const { - return _.Temp; -} - -/// Set the speed of the fan. -/// @param[in] speed The desired setting. -void IRTechnibelAc::setFan(const uint8_t speed) { - // Mode fan speed rules. - if (_.Mode == kTechnibelAcDry && speed != kTechnibelAcFanLow) { - _.Fan = kTechnibelAcFanLow; - return; - } - switch (speed) { - case kTechnibelAcFanHigh: - case kTechnibelAcFanMedium: - case kTechnibelAcFanLow: - _.Fan = speed; - break; - default: - _.Fan = kTechnibelAcFanLow; - } -} - -/// Get the current fan speed setting. -/// @return The current fan speed/mode. -uint8_t IRTechnibelAc::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 IRTechnibelAc::convertFan(const stdAc::fanspeed_t speed) { - switch (speed) { - case stdAc::fanspeed_t::kMin: - case stdAc::fanspeed_t::kLow: return kTechnibelAcFanLow; - case stdAc::fanspeed_t::kMedium: return kTechnibelAcFanMedium; - case stdAc::fanspeed_t::kHigh: - case stdAc::fanspeed_t::kMax: return kTechnibelAcFanHigh; - default: return kTechnibelAcFanLow; - } -} - -/// 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 IRTechnibelAc::toCommonFanSpeed(const uint8_t speed) { - switch (speed) { - case kTechnibelAcFanHigh: return stdAc::fanspeed_t::kHigh; - case kTechnibelAcFanMedium: return stdAc::fanspeed_t::kMedium; - default: return stdAc::fanspeed_t::kLow; - } -} - -/// Get the operating mode setting of the A/C. -/// @return The current operating mode setting. -uint8_t IRTechnibelAc::getMode(void) const { - return _.Mode; -} - -/// Set the operating mode of the A/C. -/// @param[in] mode The desired operating mode. -void IRTechnibelAc::setMode(const uint8_t mode) { - _.Mode = mode; - switch (mode) { - case kTechnibelAcHeat: - case kTechnibelAcFan: - case kTechnibelAcDry: - case kTechnibelAcCool: - break; - default: - _.Mode = kTechnibelAcCool; - } - setFan(_.Fan); // Re-force any fan speed constraints. - // Restore previous temp settings for cool mode. - setTemp(_saved_temp, _saved_temp_units); -} - -/// 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 IRTechnibelAc::convertMode(const stdAc::opmode_t mode) { - switch (mode) { - case stdAc::opmode_t::kHeat: return kTechnibelAcHeat; - case stdAc::opmode_t::kDry: return kTechnibelAcDry; - case stdAc::opmode_t::kFan: return kTechnibelAcFan; - default: return kTechnibelAcCool; - } -} - -/// 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 IRTechnibelAc::toCommonMode(const uint8_t mode) { - switch (mode) { - case kTechnibelAcHeat: return stdAc::opmode_t::kHeat; - case kTechnibelAcDry: return stdAc::opmode_t::kDry; - case kTechnibelAcFan: return stdAc::opmode_t::kFan; - default: return stdAc::opmode_t::kCool; - } -} - -/// Set the (vertical) swing setting of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRTechnibelAc::setSwing(const bool on) { - _.Swing = on; -} - -/// Get the (vertical) swing setting of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRTechnibelAc::getSwing(void) const { - return _.Swing; -} - -/// Convert a stdAc::swingv_t enum into it's native swing. -/// @param[in] swing The enum to be converted. -/// @return true, the swing is on. false, the swing is off. -bool IRTechnibelAc::convertSwing(const stdAc::swingv_t swing) { - return swing != stdAc::swingv_t::kOff; -} - -/// Convert a native swing into its stdAc equivalent. -/// @param[in] swing true, the swing is on. false, the swing is off. -/// @return The stdAc equivalent of the native setting. -stdAc::swingv_t IRTechnibelAc::toCommonSwing(const bool swing) { - return swing ? stdAc::swingv_t::kAuto : stdAc::swingv_t::kOff; -} - -/// Set the Sleep setting of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRTechnibelAc::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 IRTechnibelAc::getSleep(void) const { - return _.Sleep; -} - -/// Set the enable timer setting. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRTechnibelAc::setTimerEnabled(const bool on) { - _.TimerEnable = on; -} - -/// Is the timer function enabled? -/// @return true, the setting is on. false, the setting is off. -bool IRTechnibelAc::getTimerEnabled(void) const { - return _.TimerEnable; -} - -/// Set the timer for when the A/C unit will switch off. -/// @param[in] nr_of_mins Number of minutes before power off. -/// `0` will clear the timer. Max is 24 hrs (1440 mins). -/// @note Time is stored internally in hours. -void IRTechnibelAc::setTimer(const uint16_t nr_of_mins) { - const uint8_t hours = nr_of_mins / 60; - _.TimerHours = std::min(kTechnibelAcTimerMax, hours); - // Enable or not? - setTimerEnabled(hours); -} - -/// Get the timer time for when the A/C unit will switch power state. -/// @return The number of minutes left on the timer. `0` means off. -uint16_t IRTechnibelAc::getTimer(void) const { - return _.TimerEnable ? _.TimerHours * 60 : 0; -} - -/// Convert the current internal state into its stdAc::state_t equivalent. -/// @return The stdAc equivalent of the native settings. -stdAc::state_t IRTechnibelAc::toCommon(void) const { - stdAc::state_t result; - result.protocol = decode_type_t::TECHNIBEL_AC; - result.power = _.Power; - result.mode = toCommonMode(_.Mode); - result.celsius = !_.UseFah; - result.degrees = _.Temp; - result.fanspeed = toCommonFanSpeed(_.Fan); - result.sleep = _.Sleep ? 0 : -1; - result.swingv = toCommonSwing(_.Swing); - // Not supported. - result.model = -1; - result.turbo = false; - 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 IRTechnibelAc::toString(void) const { - String result = ""; - result.reserve(100); // Reserve some heap for the string to reduce fragging. - result += addBoolToString(_.Power, kPowerStr, false); - result += addModeToString(_.Mode, 255, // No Auto, so use impossible value - kTechnibelAcCool, kTechnibelAcHeat, kTechnibelAcDry, - kTechnibelAcFan); - result += addFanToString(_.Fan, kTechnibelAcFanHigh, kTechnibelAcFanLow, - kTechnibelAcFanLow, kTechnibelAcFanLow, - kTechnibelAcFanMedium); - result += addTempToString(_.Temp, !_.UseFah); - result += addBoolToString(_.Sleep, kSleepStr); - result += addBoolToString(_.Swing, kSwingVStr); - result += addLabeledString(_.TimerEnable ? minsToString(getTimer()) - : kOffStr, - kTimerStr); - return result; -} diff --git a/lib/IRremoteESP8266/src/ir_Technibel.h b/lib/IRremoteESP8266/src/ir_Technibel.h index fb4a08717c..decbee4e87 100644 --- a/lib/IRremoteESP8266/src/ir_Technibel.h +++ b/lib/IRremoteESP8266/src/ir_Technibel.h @@ -11,10 +11,10 @@ #ifndef UNIT_TEST #include #endif -#include "IRremoteESP8266.h" -#include "IRsend.h" +#include "../src/IRremoteESP8266.h" +#include "../src/IRsend.h" #ifdef UNIT_TEST -#include "IRsend_test.h" +#include "../src/IRsend_test.h" #endif // Supports: diff --git a/lib/IRremoteESP8266/src/ir_Teco.cpp b/lib/IRremoteESP8266/src/ir_Teco.cpp deleted file mode 100644 index 031691eddd..0000000000 --- a/lib/IRremoteESP8266/src/ir_Teco.cpp +++ /dev/null @@ -1,375 +0,0 @@ -// Copyright 2019 Fabien Valthier - -/// @file -/// @brief Support for Teco protocols. - -#include "ir_Teco.h" -#include -#include "IRremoteESP8266.h" -#include "IRtext.h" -#include "IRutils.h" -#ifndef ARDUINO -#include -#endif - -// Constants -// using SPACE modulation. -const uint16_t kTecoHdrMark = 9000; -const uint16_t kTecoHdrSpace = 4440; -const uint16_t kTecoBitMark = 620; -const uint16_t kTecoOneSpace = 1650; -const uint16_t kTecoZeroSpace = 580; -const uint32_t kTecoGap = kDefaultMessageGap; // Made-up value. Just a guess. - -using irutils::addBoolToString; -using irutils::addFanToString; -using irutils::addIntToString; -using irutils::addLabeledString; -using irutils::addModeToString; -using irutils::addTempToString; - -#if SEND_TECO -/// Send a Teco A/C 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::sendTeco(const uint64_t data, const uint16_t nbits, - const uint16_t repeat) { - sendGeneric(kTecoHdrMark, kTecoHdrSpace, kTecoBitMark, kTecoOneSpace, - kTecoBitMark, kTecoZeroSpace, kTecoBitMark, kTecoGap, - data, nbits, 38000, false, repeat, kDutyDefault); -} -#endif // SEND_TECO - -/// 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? -IRTecoAc::IRTecoAc(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 IRTecoAc::begin(void) { _irsend.begin(); } - -#if SEND_TECO -/// Send the current internal state as an IR message. -/// @param[in] repeat Nr. of times the message will be repeated. -void IRTecoAc::send(const uint16_t repeat) { - _irsend.sendTeco(_.raw, kTecoBits, repeat); -} -#endif // SEND_TECO - -/// Reset the internal state of the emulation. -/// @note Mode:auto, Power:Off, fan:auto, temp:16, swing:off, sleep:off -void IRTecoAc::stateReset(void) { - _.raw = kTecoReset; -} - -/// Get a copy of the internal state/code for this protocol. -/// @return A code for this protocol based on the current internal state. -uint64_t IRTecoAc::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 IRTecoAc::setRaw(const uint64_t new_code) { _.raw = new_code; } - -/// Set the requested power state of the A/C to on. -void IRTecoAc::on(void) { setPower(true); } - -/// Set the requested power state of the A/C to off. -void IRTecoAc::off(void) { setPower(false); } - -/// Change the power setting. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRTecoAc::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 IRTecoAc::getPower(void) const { - return _.Power; -} - -/// Set the temperature. -/// @param[in] temp The temperature in degrees celsius. -void IRTecoAc::setTemp(const uint8_t temp) { - uint8_t newtemp = temp; - newtemp = std::min(newtemp, kTecoMaxTemp); - newtemp = std::max(newtemp, kTecoMinTemp); - _.Temp = newtemp - kTecoMinTemp; -} - -/// Get the current temperature setting. -/// @return The current setting for temp. in degrees celsius. -uint8_t IRTecoAc::getTemp(void) const { - return _.Temp + kTecoMinTemp; -} - -/// Set the speed of the fan. -/// @param[in] speed The desired setting. -void IRTecoAc::setFan(const uint8_t speed) { - uint8_t newspeed = speed; - switch (speed) { - case kTecoFanAuto: - case kTecoFanHigh: - case kTecoFanMed: - case kTecoFanLow: break; - default: newspeed = kTecoFanAuto; - } - _.Fan = newspeed; -} - -/// Get the current fan speed setting. -/// @return The current fan speed/mode. -uint8_t IRTecoAc::getFan(void) const { - return _.Fan; -} - -/// Set the operating mode of the A/C. -/// @param[in] mode The desired operating mode. -void IRTecoAc::setMode(const uint8_t mode) { - uint8_t newmode = mode; - switch (mode) { - case kTecoAuto: - case kTecoCool: - case kTecoDry: - case kTecoFan: - case kTecoHeat: break; - default: newmode = kTecoAuto; - } - _.Mode = newmode; -} - -/// Get the operating mode setting of the A/C. -/// @return The current operating mode setting. -uint8_t IRTecoAc::getMode(void) const { - return _.Mode; -} - -/// Set the (vertical) swing setting of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRTecoAc::setSwing(const bool on) { - _.Swing = on; -} - -/// Get the (vertical) swing setting of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRTecoAc::getSwing(void) const { - return _.Swing; -} - -/// Set the Sleep setting of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRTecoAc::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 IRTecoAc::getSleep(void) const { - return _.Sleep; -} - -/// Set the Light (LED/Display) setting of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRTecoAc::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 IRTecoAc::getLight(void) const { - return _.Light; -} - -/// Set the Humid setting of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRTecoAc::setHumid(const bool on) { - _.Humid = on; -} - -/// Get the Humid setting of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRTecoAc::getHumid(void) const { - return _.Humid; -} - -/// Set the Save setting of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRTecoAc::setSave(const bool on) { - _.Save = on; -} - -/// Get the Save setting of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRTecoAc::getSave(void) const { - return _.Save; -} - -/// Is the timer function enabled? -/// @return true, the setting is on. false, the setting is off. -inline bool IRTecoAc::getTimerEnabled(void) const { - return _.TimerOn; -} - -/// Get the timer time for when the A/C unit will switch power state. -/// @return The number of minutes left on the timer. `0` means off. -uint16_t IRTecoAc::getTimer(void) const { - uint16_t mins = 0; - if (getTimerEnabled()) { - mins = (_.TensHours * 10 + _.UnitHours) * 60; - if (_.HalfHour) mins += 30; - } - return mins; -} - -/// Set the timer for when the A/C unit will switch power state. -/// @param[in] nr_mins Number of minutes before power state change. -/// `0` will clear the timer. Max is 24 hrs. -/// @note Time is stored internally in increments of 30 mins. -void IRTecoAc::setTimer(const uint16_t nr_mins) { - uint16_t mins = std::min(nr_mins, (uint16_t)(24 * 60)); // Limit to 24 hrs. - uint8_t hours = mins / 60; - _.TimerOn = mins > 0; // Set the timer flag. - _.HalfHour = (mins % 60) >= 30; - _.UnitHours = hours % 10; - _.TensHours = hours / 10; -} - -/// 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 IRTecoAc::convertMode(const stdAc::opmode_t mode) { - switch (mode) { - case stdAc::opmode_t::kCool: return kTecoCool; - case stdAc::opmode_t::kHeat: return kTecoHeat; - case stdAc::opmode_t::kDry: return kTecoDry; - case stdAc::opmode_t::kFan: return kTecoFan; - default: return kTecoAuto; - } -} - -/// 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 IRTecoAc::convertFan(const stdAc::fanspeed_t speed) { - switch (speed) { - case stdAc::fanspeed_t::kMin: - case stdAc::fanspeed_t::kLow: return kTecoFanLow; - case stdAc::fanspeed_t::kMedium: return kTecoFanMed; - case stdAc::fanspeed_t::kHigh: - case stdAc::fanspeed_t::kMax: return kTecoFanHigh; - default: return kTecoFanAuto; - } -} - -/// 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 IRTecoAc::toCommonMode(const uint8_t mode) { - switch (mode) { - case kTecoCool: return stdAc::opmode_t::kCool; - case kTecoHeat: return stdAc::opmode_t::kHeat; - case kTecoDry: return stdAc::opmode_t::kDry; - case kTecoFan: 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 IRTecoAc::toCommonFanSpeed(const uint8_t speed) { - switch (speed) { - case kTecoFanHigh: return stdAc::fanspeed_t::kMax; - case kTecoFanMed: return stdAc::fanspeed_t::kMedium; - case kTecoFanLow: 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 IRTecoAc::toCommon(void) const { - stdAc::state_t result; - result.protocol = decode_type_t::TECO; - result.model = -1; // Not supported. - result.power = _.Power; - result.mode = toCommonMode(_.Mode); - result.celsius = true; - result.degrees = getTemp(); - result.fanspeed = toCommonFanSpeed(_.Fan); - result.swingv = _.Swing ? stdAc::swingv_t::kAuto : stdAc::swingv_t::kOff; - result.sleep = _.Sleep ? 0 : -1; - result.light = _.Light; - // Not supported. - result.swingh = stdAc::swingh_t::kOff; - result.turbo = 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 IRTecoAc::toString(void) const { - String result = ""; - result.reserve(100); // Reserve some heap for the string to reduce fragging. - result += addBoolToString(_.Power, kPowerStr, false); - result += addModeToString(_.Mode, kTecoAuto, kTecoCool, kTecoHeat, - kTecoDry, kTecoFan); - result += addTempToString(getTemp()); - result += addFanToString(_.Fan, kTecoFanHigh, kTecoFanLow, - kTecoFanAuto, kTecoFanAuto, kTecoFanMed); - result += addBoolToString(_.Sleep, kSleepStr); - result += addBoolToString(_.Swing, kSwingStr); - result += addBoolToString(_.Light, kLightStr); - result += addBoolToString(_.Humid, kHumidStr); - result += addBoolToString(_.Save, kSaveStr); - if (getTimerEnabled()) - result += addLabeledString(irutils::minsToString(getTimer()), - kTimerStr); - else - result += addBoolToString(false, kTimerStr); - return result; -} - -#if DECODE_TECO -/// Decode the supplied Teco message. -/// Status: STABLE / Tested. -/// @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::decodeTeco(decode_results* results, uint16_t offset, - const uint16_t nbits, const bool strict) { - if (strict && nbits != kTecoBits) return false; // Not what is expected - - uint64_t data = 0; - // Match Header + Data + Footer - if (!matchGeneric(results->rawbuf + offset, &data, - results->rawlen - offset, nbits, - kTecoHdrMark, kTecoHdrSpace, - kTecoBitMark, kTecoOneSpace, - kTecoBitMark, kTecoZeroSpace, - kTecoBitMark, kTecoGap, true, - _tolerance, kMarkExcess, false)) return false; - - // Success - results->decode_type = TECO; - results->bits = nbits; - results->value = data; - results->address = 0; - results->command = 0; - return true; -} -#endif // DECODE_TECO diff --git a/lib/IRremoteESP8266/src/ir_Teco.h b/lib/IRremoteESP8266/src/ir_Teco.h index c2ea1c561e..0ce034a9ea 100644 --- a/lib/IRremoteESP8266/src/ir_Teco.h +++ b/lib/IRremoteESP8266/src/ir_Teco.h @@ -13,10 +13,10 @@ #ifndef UNIT_TEST #include #endif -#include "IRremoteESP8266.h" -#include "IRsend.h" +#include "../src/IRremoteESP8266.h" +#include "../src/IRsend.h" #ifdef UNIT_TEST -#include "IRsend_test.h" +#include "../src/IRsend_test.h" #endif /// Native representation of a Teco A/C message. diff --git a/lib/IRremoteESP8266/src/ir_Teknopoint.cpp b/lib/IRremoteESP8266/src/ir_Teknopoint.cpp deleted file mode 100644 index 0feee6d704..0000000000 --- a/lib/IRremoteESP8266/src/ir_Teknopoint.cpp +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright 2021 David Conran (crankyoldgit) -/// @file -/// @brief Support for the Teknopoint protocol -/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1486 - -// Supports: -// Brand: Teknopoint, Model: Allegro SSA-09H A/C -// Brand: Teknopoint, Model: GZ-055B-E1 remote - -#include "IRrecv.h" -#include "IRsend.h" -#include "IRutils.h" - -// Protocol timings -const uint16_t kTeknopointHdrMark = 3600; -const uint16_t kTeknopointBitMark = 477; -const uint16_t kTeknopointHdrSpace = 1600; -const uint16_t kTeknopointOneSpace = 1200; -const uint16_t kTeknopointZeroSpace = 530; -const uint16_t kTeknopointFreq = 38000; // Hz. (Guess Only) -const uint8_t kTeknopointExtraTol = 10; // Extra tolerance percentage. - -#if SEND_TEKNOPOINT -/// Send a Teknopoint formatted message. -/// Status: BETA / Probably works. -/// @param[in] data An array of bytes containing the IR command. -/// @param[in] nbytes Nr. of bytes of data in the array. -/// @param[in] repeat Nr. of times the message is to be repeated. -void IRsend::sendTeknopoint(const uint8_t data[], const uint16_t nbytes, - const uint16_t repeat) { - sendGeneric(kTeknopointHdrMark, kTeknopointHdrSpace, - kTeknopointBitMark, kTeknopointOneSpace, - kTeknopointBitMark, kTeknopointZeroSpace, - kTeknopointBitMark, kDefaultMessageGap, - data, nbytes, // Bytes - kTeknopointFreq, false, repeat, kDutyDefault); -} -#endif // SEND_TEKNOPOINT - -#if DECODE_TEKNOPOINT -/// Decode the supplied Teknopoint message. -/// Status: Alpha / 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::decodeTeknopoint(decode_results *results, uint16_t offset, - const uint16_t nbits, const bool strict) { - if (results->rawlen < 2 * nbits + kHeader + kFooter - 1 - offset) - return false; // Too short a message to match. - if (strict && nbits != kTeknopointBits) - return false; - - if (!matchGeneric(results->rawbuf + offset, results->state, - results->rawlen - offset, nbits, - kTeknopointHdrMark, kTeknopointHdrSpace, - kTeknopointBitMark, kTeknopointOneSpace, - kTeknopointBitMark, kTeknopointZeroSpace, - kTeknopointBitMark, kDefaultMessageGap, - true, _tolerance + kTeknopointExtraTol, - kMarkExcess, false)) return false; - // Compliance - if (strict) { - // Is the checksum valid? - if (sumBytes(results->state, kTeknopointStateLength - 1) != - results->state[kTeknopointStateLength - 1]) return false; - } - // Success - results->decode_type = decode_type_t::TEKNOPOINT; - results->bits = nbits; - return true; -} -#endif // DECODE_TEKNOPOINT diff --git a/lib/IRremoteESP8266/src/ir_Toshiba.h b/lib/IRremoteESP8266/src/ir_Toshiba.h index 1314cf54de..b48dab0de0 100644 --- a/lib/IRremoteESP8266/src/ir_Toshiba.h +++ b/lib/IRremoteESP8266/src/ir_Toshiba.h @@ -34,10 +34,10 @@ #ifdef ARDUINO #include #endif -#include "IRremoteESP8266.h" -#include "IRsend.h" +#include "../src/IRremoteESP8266.h" +#include "../src/IRsend.h" #ifdef UNIT_TEST -#include "IRsend_test.h" +#include "../src/IRsend_test.h" #endif /// Native representation of a Toshiba A/C message. diff --git a/lib/IRremoteESP8266/src/ir_Transcold.cpp b/lib/IRremoteESP8266/src/ir_Transcold.cpp deleted file mode 100644 index 14cc7f8bd2..0000000000 --- a/lib/IRremoteESP8266/src/ir_Transcold.cpp +++ /dev/null @@ -1,500 +0,0 @@ -// Copyright 2020 Chandrashekar Shetty (iamDshetty) -// Copyright 2020 crankyoldgit -// Copyright 2021 siriuslzx - -/// @file -/// @brief Support for Transcold A/C protocols. -/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1256 - -#include "ir_Transcold.h" -#include -#ifndef ARDUINO -#include -#endif -#include "IRrecv.h" -#include "IRsend.h" -#include "IRtext.h" -#include "IRutils.h" - -// Constants - -const uint16_t kTranscoldHdrMark = 5944; ///< uSeconds. -const uint16_t kTranscoldBitMark = 555; ///< uSeconds. -const uint16_t kTranscoldHdrSpace = 7563; ///< uSeconds. -const uint16_t kTranscoldOneSpace = 3556; ///< uSeconds. -const uint16_t kTranscoldZeroSpace = 1526; ///< uSeconds. - -using irutils::addBoolToString; -using irutils::addIntToString; -using irutils::addLabeledString; -using irutils::addModeToString; -using irutils::addTempToString; -using irutils::addToggleToString; - -#if SEND_TRANSCOLD -/// Send a Transcold 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. -void IRsend::sendTranscold(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(kTranscoldHdrMark); - space(kTranscoldHdrSpace); - // 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; - uint8_t segment = GETBITS64(data, nbits - i, 8); - // Normal + Inverted - uint16_t both = (segment << 8) | (~segment & 0xFF); - sendData(kTranscoldBitMark, kTranscoldOneSpace, kTranscoldBitMark, - kTranscoldZeroSpace, both, 16, true); - } - // Footer - mark(kTranscoldBitMark); - space(kTranscoldHdrSpace); - mark(kTranscoldBitMark); - space(kDefaultMessageGap); - } -} -#endif // SEND_TRANSCOLD - -/// 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? -IRTranscoldAc::IRTranscoldAc(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 IRTranscoldAc::stateReset(void) { - setRaw(kTranscoldKnownGoodState); - special_state = kTranscoldOff; - swingFlag = false; - swingHFlag = false; - swingVFlag = false; -} - -/// Set up hardware to be able to send a message. -void IRTranscoldAc::begin(void) { _irsend.begin(); } - -#if SEND_TRANSCOLD -/// Send the current internal state as an IR message. -/// @param[in] repeat Nr. of times the message will be repeated. -void IRTranscoldAc::send(uint16_t repeat) { - _irsend.sendTranscold(getRaw(), kTranscoldBits, repeat); - if (isSpecialState()) { - // make sure to remove special state from special_state - // after command has being transmitted. - special_state = kTranscoldKnownGoodState; - } -} -#endif // SEND_TRANSCOLD - -/// 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 IRTranscoldAc::getRaw(void) const { - if (isSpecialState()) { - return special_state; - } - return _.raw; - } - -/// Set the internal state from a valid code for this protocol. -/// @param[in] new_code A valid code for this protocol. -void IRTranscoldAc::setRaw(const uint32_t new_code) { - if (handleSpecialState(new_code)) { - special_state = new_code; - _.raw = kTranscoldKnownGoodState; - } else { - // must be a command changing Temp|Mode|Fan - // it is safe to just copy to remote var - _.raw = new_code; - special_state = kTranscoldKnownGoodState; - // it isn`t special so might affect Temp|mode|Fan - if (new_code == kTranscoldCmdFan) { - setMode(kTranscoldFan); - } - } -} - -/// Is the current state is a special state? -/// @return true, if it is. false if it isn't. -bool IRTranscoldAc::isSpecialState(void) const { - switch (special_state) { - case kTranscoldOff: - case kTranscoldSwing: 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 -/// @return true, if it is a special state. false if it isn't. -bool IRTranscoldAc::handleSpecialState(const uint32_t data) { - switch (data) { - case kTranscoldOff: - break; - case kTranscoldSwing: - swingFlag = !swingFlag; - break; - default: - return false; - } - return true; -} - -/// Set the temperature. -/// @param[in] desired The temperature in degrees celsius. -void IRTranscoldAc::setTemp(const uint8_t desired) { - // Range check. - uint8_t temp = std::min(desired, kTranscoldTempMax); - temp = std::max(temp, kTranscoldTempMin) - kTranscoldTempMin + 1; - _.Temp = reverseBits(invertBits(temp, kTranscoldTempSize), - kTranscoldTempSize); -} - -/// Get the current temperature setting. -/// @return The current setting for temp. in degrees celsius. -uint8_t IRTranscoldAc::getTemp(void) const { - return reverseBits(invertBits(_.Temp, kTranscoldTempSize), - kTranscoldTempSize) + kTranscoldTempMin - 1; -} - -/// Get the value of the current power setting. -/// @return true, the setting is on. false, the setting is off. -bool IRTranscoldAc::getPower(void) const { - // There is only an off state. Everything else is "on". - return special_state != kTranscoldOff; -} - -/// Change the power setting. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRTranscoldAc::setPower(const bool on) { - if (!on) { - special_state = kTranscoldOff; - } else { - special_state = kTranscoldKnownGoodState; - } -} - -/// Change the power setting to On. -void IRTranscoldAc::on(void) { setPower(true); } - -/// Change the power setting to Off. -void IRTranscoldAc::off(void) { setPower(false); } - -/// Get the Swing setting of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRTranscoldAc::getSwing(void) const { return swingFlag; } - -/// Toggle the Swing mode of the A/C. -void IRTranscoldAc::setSwing(void) { - // Assumes that repeated sending "swing" toggles the action on the device. - // if not, the variable "swingFlag" can be removed. - special_state = kTranscoldSwing; - swingFlag = !swingFlag; -} - -/// Set the operating mode of the A/C. -/// @param[in] mode The desired operating mode. -void IRTranscoldAc::setMode(const uint8_t mode) { - uint32_t actualmode = mode; - switch (actualmode) { - case kTranscoldAuto: - case kTranscoldDry: - _.Fan = kTranscoldFanAuto0; - break; - case kTranscoldCool: - case kTranscoldHeat: - case kTranscoldFan: - _.Fan = kTranscoldFanAuto; - break; - default: // Anything else, go with Auto mode. - actualmode = kTranscoldAuto; - _.Fan = kTranscoldFanAuto0; - } - setTemp(getTemp()); - // Fan mode is a special case of Dry. - if (actualmode == kTranscoldFan) { - actualmode = kTranscoldDry; - _.Temp = kTranscoldFanTempCode; - } - _.Mode = actualmode; -} - -/// Get the operating mode setting of the A/C. -/// @return The current operating mode setting. -uint8_t IRTranscoldAc::getMode(void) const { - uint8_t mode = _.Mode; - if (mode == kTranscoldDry) - if (_.Temp == kTranscoldFanTempCode) return kTranscoldFan; - return mode; -} - -/// Get the current fan speed setting. -/// @return The current fan speed. -uint8_t IRTranscoldAc::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 IRTranscoldAc::setFan(const uint8_t speed, const bool modecheck) { - uint8_t newspeed = speed; - if (modecheck) { - switch (getMode()) { - case kTranscoldAuto: - case kTranscoldDry: // Dry & Auto mode can't have speed Auto. - if (speed == kTranscoldFanAuto) - newspeed = kTranscoldFanAuto0; - break; - default: // Only Dry & Auto mode can have speed Auto0. - if (speed == kTranscoldFanAuto0) - newspeed = kTranscoldFanAuto; - } - } - switch (speed) { - case kTranscoldFanAuto: - case kTranscoldFanAuto0: - case kTranscoldFanMin: - case kTranscoldFanMed: - case kTranscoldFanMax: - case kTranscoldFanZoneFollow: - case kTranscoldFanFixed: - break; - default: // Unknown speed requested. - newspeed = kTranscoldFanAuto; - break; - } - _.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 IRTranscoldAc::convertMode(const stdAc::opmode_t mode) { - switch (mode) { - case stdAc::opmode_t::kCool: return kTranscoldCool; - case stdAc::opmode_t::kHeat: return kTranscoldHeat; - case stdAc::opmode_t::kDry: return kTranscoldDry; - case stdAc::opmode_t::kFan: return kTranscoldFan; - default: return kTranscoldAuto; - } -} - -/// 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 IRTranscoldAc::convertFan(const stdAc::fanspeed_t speed) { - switch (speed) { - case stdAc::fanspeed_t::kMin: - case stdAc::fanspeed_t::kLow: return kTranscoldFanMin; - case stdAc::fanspeed_t::kMedium: return kTranscoldFanMed; - case stdAc::fanspeed_t::kHigh: - case stdAc::fanspeed_t::kMax: return kTranscoldFanMax; - default: return kTranscoldFanAuto; - } -} - -/// 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 IRTranscoldAc::toCommonMode(const uint8_t mode) { - switch (mode) { - case kTranscoldCool: return stdAc::opmode_t::kCool; - case kTranscoldHeat: return stdAc::opmode_t::kHeat; - case kTranscoldDry: return stdAc::opmode_t::kDry; - case kTranscoldFan: 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 IRTranscoldAc::toCommonFanSpeed(const uint8_t speed) { - switch (speed) { - case kTranscoldFanMax: return stdAc::fanspeed_t::kMax; - case kTranscoldFanMed: return stdAc::fanspeed_t::kMedium; - case kTranscoldFanMin: 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 IRTranscoldAc::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; - } - // Not supported. - result.model = -1; // No models used. - result.swingh = stdAc::swingh_t::kOff; - result.turbo = false; - result.clean = false; - result.light = false; - result.quiet = false; - result.econo = false; - result.filter = false; - result.beep = false; - result.clock = -1; - result.sleep = -1; - - // Supported. - result.protocol = decode_type_t::TRANSCOLD; - 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; - } - // Back to "normal" stateful messages. - result.mode = toCommonMode(getMode()); - result.degrees = getTemp(); - result.fanspeed = toCommonFanSpeed(_.Fan); - return result; -} - -/// Convert the internal state into a human readable string. -/// @return The current internal state expressed as a human readable String. -String IRTranscoldAc::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. - // Special modes. - if (getSwing()) return result + addToggleToString(true, kSwingStr); - result += addModeToString(getMode(), kTranscoldAuto, kTranscoldCool, - kTranscoldHeat, kTranscoldDry, kTranscoldFan); - result += addIntToString(_.Fan, kFanStr); - result += kSpaceLBraceStr; - switch (_.Fan) { - case kTranscoldFanAuto: - result += kAutoStr; - break; - case kTranscoldFanAuto0: - result += kAutoStr; - result += '0'; - break; - case kTranscoldFanMax: - result += kMaxStr; - break; - case kTranscoldFanMin: - result += kMinStr; - break; - case kTranscoldFanMed: - result += kMedStr; - break; - case kTranscoldFanZoneFollow: - result += kZoneFollowStr; - break; - case kTranscoldFanFixed: - result += kFixedStr; - break; - default: - result += kUnknownStr; - } - result += ')'; - // Fan mode doesn't have a temperature. - if (getMode() != kTranscoldFan) result += addTempToString(getTemp()); - return result; -} - -#if DECODE_TRANSCOLD -/// Decode the supplied Transcold 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::decodeTranscold(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; - if (strict && nbits != kTranscoldBits) return false; - if (nbits % 8 != 0) return false; - - uint64_t data = 0; - uint64_t inverted = 0; - - if (nbits > sizeof(data) * 8) - return false; // We can't possibly capture a Transcold packet that big. - - // Header - if (!matchMark(results->rawbuf[offset++], kTranscoldHdrMark)) return false; - if (!matchSpace(results->rawbuf[offset++], kTranscoldHdrSpace)) return false; - - // Data - // Twice as many bits as there are normal plus inverted bits. - for (uint16_t i = 0; i < nbits * 2; i++, offset++) { - bool flip = (i / 8) % 2; - if (!matchMark(results->rawbuf[offset++], kTranscoldBitMark)) - return false; - if (matchSpace(results->rawbuf[offset], kTranscoldOneSpace)) { - if (flip) - inverted = (inverted << 1) | 1; - else - data = (data << 1) | 1; - } else if (matchSpace(results->rawbuf[offset], kTranscoldZeroSpace)) { - if (flip) - inverted <<= 1; - else - data <<= 1; - } else { - return false; - } - } - - // Footer - if (!matchMark(results->rawbuf[offset++], kTranscoldBitMark)) return false; - if (!matchSpace(results->rawbuf[offset++], kTranscoldHdrSpace)) return false; - if (!matchMark(results->rawbuf[offset++], kTranscoldBitMark)) return false; - if (offset < results->rawlen && - !matchAtLeast(results->rawbuf[offset], kDefaultMessageGap)) - return false; - - // Compliance - if (strict && inverted != invertBits(data, nbits)) return false; - - // Success - results->decode_type = decode_type_t::TRANSCOLD; - results->bits = nbits; - results->value = data; - results->address = 0; - results->command = 0; - return true; -} -#endif // DECODE_TRANSCOLD diff --git a/lib/IRremoteESP8266/src/ir_Transcold.h b/lib/IRremoteESP8266/src/ir_Transcold.h index 8fd924fbaf..bedb9611d5 100644 --- a/lib/IRremoteESP8266/src/ir_Transcold.h +++ b/lib/IRremoteESP8266/src/ir_Transcold.h @@ -63,10 +63,10 @@ temp 16 Auto cool close (right) 11101111000100000110011110011000010101001010101 #ifndef UNIT_TEST #include #endif -#include "IRremoteESP8266.h" -#include "IRsend.h" +#include "../src/IRremoteESP8266.h" +#include "../src/IRsend.h" #ifdef UNIT_TEST -#include "IRsend_test.h" +#include "../src/IRsend_test.h" #endif /// Native representation of a Transcold A/C message. diff --git a/lib/IRremoteESP8266/src/ir_Trotec.cpp b/lib/IRremoteESP8266/src/ir_Trotec.cpp deleted file mode 100644 index 7cd6fff5e6..0000000000 --- a/lib/IRremoteESP8266/src/ir_Trotec.cpp +++ /dev/null @@ -1,642 +0,0 @@ -// Copyright 2017 stufisher -// Copyright 2019 crankyoldgit - -/// @file -/// @brief Support for Trotec protocols. -/// @see https://github.com/crankyoldgit/IRremoteESP8266/pull/279 -/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1176 - -#include "ir_Trotec.h" -#include -#include -#ifndef UNIT_TEST -#include -#endif -#include "IRremoteESP8266.h" -#include "IRtext.h" -#include "IRutils.h" - -// Constants -const uint16_t kTrotecHdrMark = 5952; -const uint16_t kTrotecHdrSpace = 7364; -const uint16_t kTrotecBitMark = 592; -const uint16_t kTrotecOneSpace = 1560; -const uint16_t kTrotecZeroSpace = 592; -const uint16_t kTrotecGap = 6184; -const uint16_t kTrotecGapEnd = 1500; // made up value - -const uint16_t kTrotec3550HdrMark = 12000; -const uint16_t kTrotec3550HdrSpace = 5130; -const uint16_t kTrotec3550BitMark = 550; -const uint16_t kTrotec3550OneSpace = 1950; -const uint16_t kTrotec3550ZeroSpace = 500; - -const uint16_t kTrotec3550TimerMax = 8 * 60; ///< 8 hours in Minutes. - -using irutils::addBoolToString; -using irutils::addFanToString; -using irutils::addIntToString; -using irutils::addLabeledString; -using irutils::addModeToString; -using irutils::addTempToString; -using irutils::minsToString; - -#if SEND_TROTEC -/// Send a Trotec message. -/// 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::sendTrotec(const unsigned char data[], const uint16_t nbytes, - const uint16_t repeat) { - if (nbytes < kTrotecStateLength) return; - - enableIROut(36); - for (uint16_t r = 0; r <= repeat; r++) { - sendGeneric(kTrotecHdrMark, kTrotecHdrSpace, kTrotecBitMark, - kTrotecOneSpace, kTrotecBitMark, kTrotecZeroSpace, - kTrotecBitMark, kTrotecGap, data, nbytes, 36, false, - 0, // Repeats handled elsewhere - 50); - // More footer - mark(kTrotecBitMark); - space(kTrotecGapEnd); - } -} -#endif // SEND_TROTEC - -/// 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? -IRTrotecESP::IRTrotecESP(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 IRTrotecESP::begin(void) { _irsend.begin(); } - -#if SEND_TROTEC -/// Send the current internal state as an IR message. -/// @param[in] repeat Nr. of times the message will be repeated. -void IRTrotecESP::send(const uint16_t repeat) { - _irsend.sendTrotec(getRaw(), kTrotecStateLength, repeat); -} -#endif // SEND_TROTEC - -/// 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 IRTrotecESP::calcChecksum(const uint8_t state[], - const uint16_t length) { - return sumBytes(state + 2, length - 3); -} - -/// 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 IRTrotecESP::validChecksum(const uint8_t state[], const uint16_t length) { - return state[length - 1] == calcChecksum(state, length); -} - -/// Calculate & set the checksum for the current internal state of the remote. -void IRTrotecESP::checksum(void) { - _.Sum = sumBytes(_.raw + 2, kTrotecStateLength - 3); -} - -/// Reset the state of the remote to a known good state/sequence. -void IRTrotecESP::stateReset(void) { - for (uint8_t i = 2; i < kTrotecStateLength; i++) _.raw[i] = 0x0; - - _.Intro1 = kTrotecIntro1; - _.Intro2 = kTrotecIntro2; - - _.Power = false; - setTemp(kTrotecDefTemp); - _.Fan = kTrotecFanMed; - _.Mode = kTrotecAuto; -} - -/// 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* IRTrotecESP::getRaw(void) { - checksum(); - return _.raw; -} - -/// Set the internal state from a valid code for this protocol. -/// @param[in] state A valid code for this protocol. -void IRTrotecESP::setRaw(const uint8_t state[]) { - memcpy(_.raw, state, kTrotecStateLength); -} - -/// Set the requested power state of the A/C to on. -void IRTrotecESP::on(void) { setPower(true); } - -/// Set the requested power state of the A/C to off. -void IRTrotecESP::off(void) { setPower(false); } - -/// Change the power setting. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRTrotecESP::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 IRTrotecESP::getPower(void) const { - return _.Power; -} - -/// Set the speed of the fan. -/// @param[in] fan The desired setting. -void IRTrotecESP::setSpeed(const uint8_t fan) { - uint8_t speed = std::min(fan, kTrotecFanHigh); - _.Fan = speed; -} - -/// Get the current fan speed setting. -/// @return The current fan speed/mode. -uint8_t IRTrotecESP::getSpeed(void) const { - return _.Fan; -} - -/// Set the operating mode of the A/C. -/// @param[in] mode The desired operating mode. -void IRTrotecESP::setMode(const uint8_t mode) { - _.Mode = (mode > kTrotecFan) ? kTrotecAuto : mode; -} - -/// Get the operating mode setting of the A/C. -/// @return The current operating mode setting. -uint8_t IRTrotecESP::getMode(void) const { - return _.Mode; -} - -/// Set the temperature. -/// @param[in] celsius The temperature in degrees celsius. -void IRTrotecESP::setTemp(const uint8_t celsius) { - uint8_t temp = std::max(celsius, kTrotecMinTemp); - temp = std::min(temp, kTrotecMaxTemp); - _.Temp = temp - kTrotecMinTemp; -} - -/// Get the current temperature setting. -/// @return The current setting for temp. in degrees celsius. -uint8_t IRTrotecESP::getTemp(void) const { - return _.Temp + kTrotecMinTemp; -} - -/// Set the Sleep setting of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRTrotecESP::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 IRTrotecESP::getSleep(void) const { - return _.Sleep; -} - -/// Set the timer time in nr. of Hours. -/// @param[in] timer Nr. of Hours. Max is `kTrotecMaxTimer` -void IRTrotecESP::setTimer(const uint8_t timer) { - _.Timer = timer; - _.Hours = (timer > kTrotecMaxTimer) ? kTrotecMaxTimer : timer; -} - -/// Get the timer time in nr. of Hours. -/// @return Nr. of Hours. -uint8_t IRTrotecESP::getTimer(void) const { return _.Hours; } - -/// 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 IRTrotecESP::convertMode(const stdAc::opmode_t mode) { - switch (mode) { - case stdAc::opmode_t::kCool: return kTrotecCool; - case stdAc::opmode_t::kDry: return kTrotecDry; - case stdAc::opmode_t::kFan: return kTrotecFan; - // Note: No Heat mode. - default: return kTrotecAuto; - } -} - -/// 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 IRTrotecESP::convertFan(const stdAc::fanspeed_t speed) { - switch (speed) { - case stdAc::fanspeed_t::kMin: - case stdAc::fanspeed_t::kLow: return kTrotecFanLow; - case stdAc::fanspeed_t::kMedium: return kTrotecFanMed; - case stdAc::fanspeed_t::kHigh: - case stdAc::fanspeed_t::kMax: return kTrotecFanHigh; - default: return kTrotecFanMed; - } -} - -/// 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 IRTrotecESP::toCommonMode(const uint8_t mode) { - switch (mode) { - case kTrotecCool: return stdAc::opmode_t::kCool; - case kTrotecDry: return stdAc::opmode_t::kDry; - case kTrotecFan: return stdAc::opmode_t::kFan; - default: return stdAc::opmode_t::kAuto; - } -} - -/// Convert a native fan speed into its stdAc equivalent. -/// @param[in] spd The native setting to be converted. -/// @return The stdAc equivalent of the native setting. -stdAc::fanspeed_t IRTrotecESP::toCommonFanSpeed(const uint8_t spd) { - switch (spd) { - case kTrotecFanHigh: return stdAc::fanspeed_t::kMax; - case kTrotecFanMed: return stdAc::fanspeed_t::kMedium; - case kTrotecFanLow: 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 IRTrotecESP::toCommon(void) const { - stdAc::state_t result; - result.protocol = decode_type_t::TROTEC; - result.power = _.Power; - result.mode = toCommonMode(_.Mode); - result.celsius = true; - result.degrees = getTemp(); - result.fanspeed = toCommonFanSpeed(_.Fan); - result.sleep = _.Sleep ? 0 : -1; - // Not supported. - result.model = -1; // Not supported. - result.swingv = stdAc::swingv_t::kOff; - result.swingh = stdAc::swingh_t::kOff; - result.turbo = false; - 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 IRTrotecESP::toString(void) const { - String result = ""; - result.reserve(100); // Reserve some heap for the string to reduce fragging. - result += addBoolToString(_.Power, kPowerStr, false); - result += addModeToString(_.Mode, kTrotecAuto, kTrotecCool, kTrotecAuto, - kTrotecDry, kTrotecFan); - result += addTempToString(getTemp()); - result += addFanToString(_.Fan, kTrotecFanHigh, kTrotecFanLow, - kTrotecFanHigh, kTrotecFanHigh, kTrotecFanMed); - result += addBoolToString(_.Sleep, kSleepStr); - return result; -} - -#if DECODE_TROTEC -/// Decode the supplied Trotec message. -/// Status: STABLE / Works. Untested on real devices. -/// @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::decodeTrotec(decode_results *results, uint16_t offset, - const uint16_t nbits, const bool strict) { - if (results->rawlen <= 2 * nbits + kHeader + 2 * kFooter - 1 + offset) - return false; // Can't possibly be a valid Trotec A/C message. - if (strict && nbits != kTrotecBits) return false; - - uint16_t used; - // Header + Data + Footer #1 - used = matchGeneric(results->rawbuf + offset, results->state, - results->rawlen - offset, nbits, - kTrotecHdrMark, kTrotecHdrSpace, - kTrotecBitMark, kTrotecOneSpace, - kTrotecBitMark, kTrotecZeroSpace, - kTrotecBitMark, kTrotecGap, true, - _tolerance, 0, false); - if (used == 0) return false; - offset += used; - - // Footer #2 - if (!matchMark(results->rawbuf[offset++], kTrotecBitMark)) return false; - if (offset <= results->rawlen && - !matchAtLeast(results->rawbuf[offset++], kTrotecGapEnd)) return false; - // Compliance - // Verify we got a valid checksum. - if (strict && !IRTrotecESP::validChecksum(results->state)) return false; - // Success - results->decode_type = TROTEC; - 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_TROTEC - -#if SEND_TROTEC_3550 -/// Send a Trotec 3550 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::sendTrotec3550(const unsigned char data[], const uint16_t nbytes, - const uint16_t repeat) { - sendGeneric(kTrotec3550HdrMark, kTrotec3550HdrSpace, - kTrotec3550BitMark, kTrotec3550OneSpace, - kTrotec3550BitMark, kTrotec3550ZeroSpace, - kTrotec3550BitMark, kDefaultMessageGap, - data, nbytes, 38, true, repeat, kDutyDefault); -} -#endif // SEND_TROTEC_3550 - -#if DECODE_TROTEC_3550 -/// Decode the supplied Trotec 3550 message. -/// Status: STABLE / Known 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::decodeTrotec3550(decode_results *results, uint16_t offset, - const uint16_t nbits, const bool strict) { - if (strict && nbits != kTrotecBits) return false; - - // Header + Data + Footer - if (!matchGeneric(results->rawbuf + offset, results->state, - results->rawlen - offset, nbits, - kTrotec3550HdrMark, kTrotec3550HdrSpace, - kTrotec3550BitMark, kTrotec3550OneSpace, - kTrotec3550BitMark, kTrotec3550ZeroSpace, - kTrotec3550BitMark, kDefaultMessageGap)) return false; - // Compliance - if (strict && !IRTrotec3550::validChecksum(results->state, nbits / 8)) - return false; - // Success - results->decode_type = TROTEC_3550; - 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_TROTEC_3550 - -/// 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? -IRTrotec3550::IRTrotec3550(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 IRTrotec3550::begin(void) { _irsend.begin(); } - -#if SEND_TROTEC_3550 -/// Send the current internal state as an IR message. -/// @param[in] repeat Nr. of times the message will be repeated. -void IRTrotec3550::send(const uint16_t repeat) { - _irsend.sendTrotec3550(getRaw(), kTrotecStateLength, repeat); -} -#endif // SEND_TROTEC_3550 - -/// 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 IRTrotec3550::calcChecksum(const uint8_t state[], - const uint16_t length) { - return length ? sumBytes(state, length - 1) : 0; -} - -/// 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 IRTrotec3550::validChecksum(const uint8_t state[], const uint16_t length) { - return state[length - 1] == calcChecksum(state, length); -} - -/// Calculate & set the checksum for the current internal state of the remote. -void IRTrotec3550::checksum(void) { _.Sum = calcChecksum(_.raw); } - -/// Reset the state of the remote to a known good state/sequence. -void IRTrotec3550::stateReset(void) { - static const uint8_t kReset[kTrotecStateLength] = { - 0x55, 0x60, 0x00, 0x0D, 0x00, 0x00, 0x10, 0x88, 0x5A}; - std::memcpy(_.raw, kReset, kTrotecStateLength); -} - -/// 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* IRTrotec3550::getRaw(void) { - checksum(); - return _.raw; -} - -/// Set the internal state from a valid code for this protocol. -/// @param[in] state A valid code for this protocol. -void IRTrotec3550::setRaw(const uint8_t state[]) { - memcpy(_.raw, state, kTrotecStateLength); -} - -/// Set the requested power state of the A/C to on. -void IRTrotec3550::on(void) { setPower(true); } - -/// Set the requested power state of the A/C to off. -void IRTrotec3550::off(void) { setPower(false); } - -/// Change the power setting. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRTrotec3550::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 IRTrotec3550::getPower(void) const { return _.Power; } - -/// Set the speed of the fan. -/// @param[in] fan The desired setting. -void IRTrotec3550::setFan(const uint8_t fan) { - uint8_t speed = std::min(fan, kTrotecFanHigh); - _.Fan = speed; -} - -/// Get the current fan speed setting. -/// @return The current fan speed/mode. -uint8_t IRTrotec3550::getFan(void) const { return _.Fan; } - -/// Set the operating mode of the A/C. -/// @param[in] mode The desired operating mode. -void IRTrotec3550::setMode(const uint8_t mode) { - _.Mode = (mode > kTrotecFan) ? kTrotecAuto : mode; -} - -/// Get the operating mode setting of the A/C. -/// @return The current operating mode setting. -uint8_t IRTrotec3550::getMode(void) const { return _.Mode; } - -/// Set the temperature. -/// @param[in] degrees The temperature in degrees. -/// @param[in] celsius Use celsius units. True, Celsius; False Fahrenheit. -void IRTrotec3550::setTemp(const uint8_t degrees, const bool celsius) { - setTempUnit(celsius); - uint8_t minTemp = kTrotec3550MinTempC; - uint8_t maxTemp = kTrotec3550MaxTempC; - if (!celsius) { // Fahrenheit? - minTemp = kTrotec3550MinTempF; - maxTemp = kTrotec3550MaxTempF; - } - uint8_t temp = std::max(degrees, minTemp); - temp = std::min(temp, maxTemp); - if (celsius) { - _.TempC = temp - minTemp; - _.TempF = celsiusToFahrenheit(temp) - kTrotec3550MinTempF; - } else { - _.TempF = temp - minTemp; - _.TempC = fahrenheitToCelsius(temp) - kTrotec3550MinTempC; - } -} - -/// Get the current temperature setting. -/// @return The current setting for temp. in degrees. -uint8_t IRTrotec3550::getTemp(void) const { - return getTempUnit() ? _.TempC + kTrotec3550MinTempC - : _.TempF + kTrotec3550MinTempF; -} - -/// Set the temperature unit that the A/C will use.. -/// @param[in] celsius Use celsius units. True, Celsius; False Fahrenheit. -void IRTrotec3550::setTempUnit(const bool celsius) { _.Celsius = celsius; } - -/// Get the current temperature unit setting. -/// @return True, Celsius; False Fahrenheit. -bool IRTrotec3550::getTempUnit(void) const { return _.Celsius; } - -/// Change the Vertical Swing setting. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRTrotec3550::setSwingV(const bool on) { _.SwingV = on; } - -/// Get the value of the current Vertical Swing setting. -/// @return true, the setting is on. false, the setting is off. -bool IRTrotec3550::getSwingV(void) const { return _.SwingV; } - -/// Get the number of minutes of the Timer setting. -/// @return Nr of minutes. -uint16_t IRTrotec3550::getTimer(void) const { return _.TimerHrs * 60; } - -/// Set the number of minutes of the Timer setting. -/// @param[in] mins Nr. of Minutes for the Timer. `0` means disable the timer. -void IRTrotec3550::setTimer(const uint16_t mins) { - _.TimerSet = mins > 0; - _.TimerHrs = (std::min(mins, kTrotec3550TimerMax) / 60); -} - -/// 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 IRTrotec3550::convertMode(const stdAc::opmode_t mode) { - switch (mode) { - case stdAc::opmode_t::kCool: return kTrotecCool; - case stdAc::opmode_t::kDry: return kTrotecDry; - case stdAc::opmode_t::kFan: return kTrotecFan; - // Note: No Heat mode. - default: return kTrotecAuto; - } -} - -/// 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 IRTrotec3550::convertFan(const stdAc::fanspeed_t speed) { - switch (speed) { - case stdAc::fanspeed_t::kMin: - case stdAc::fanspeed_t::kLow: return kTrotecFanLow; - case stdAc::fanspeed_t::kMedium: return kTrotecFanMed; - case stdAc::fanspeed_t::kHigh: - case stdAc::fanspeed_t::kMax: return kTrotecFanHigh; - default: return kTrotecFanMed; - } -} - -/// 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 IRTrotec3550::toCommonMode(const uint8_t mode) { - switch (mode) { - case kTrotecCool: return stdAc::opmode_t::kCool; - case kTrotecDry: return stdAc::opmode_t::kDry; - case kTrotecFan: return stdAc::opmode_t::kFan; - default: return stdAc::opmode_t::kAuto; - } -} - -/// Convert a native fan speed into its stdAc equivalent. -/// @param[in] spd The native setting to be converted. -/// @return The stdAc equivalent of the native setting. -stdAc::fanspeed_t IRTrotec3550::toCommonFanSpeed(const uint8_t spd) { - switch (spd) { - case kTrotecFanHigh: return stdAc::fanspeed_t::kMax; - case kTrotecFanMed: return stdAc::fanspeed_t::kMedium; - case kTrotecFanLow: 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 IRTrotec3550::toCommon(void) const { - stdAc::state_t result; - result.protocol = decode_type_t::TROTEC_3550; - result.power = _.Power; - result.mode = toCommonMode(_.Mode); - result.celsius = getTempUnit(); - result.degrees = getTemp(); - result.fanspeed = toCommonFanSpeed(_.Fan); - result.swingv = _.SwingV ? stdAc::swingv_t::kAuto : stdAc::swingv_t::kOff; - // Not supported. - result.model = -1; - result.swingh = stdAc::swingh_t::kOff; - result.turbo = false; - 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 IRTrotec3550::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, kTrotecAuto, kTrotecCool, kTrotecAuto, - kTrotecDry, kTrotecFan); - result += addTempToString(getTemp(), _.Celsius); - result += addFanToString(_.Fan, kTrotecFanHigh, kTrotecFanLow, - kTrotecFanHigh, kTrotecFanHigh, kTrotecFanMed); - result += addBoolToString(_.SwingV, kSwingVStr); - result += addLabeledString(_.TimerSet ? minsToString(getTimer()) : kOffStr, - kTimerStr); - return result; -} diff --git a/lib/IRremoteESP8266/src/ir_Trotec.h b/lib/IRremoteESP8266/src/ir_Trotec.h index 3381ec3f20..b8586dd77d 100644 --- a/lib/IRremoteESP8266/src/ir_Trotec.h +++ b/lib/IRremoteESP8266/src/ir_Trotec.h @@ -18,10 +18,10 @@ #ifndef UNIT_TEST #include #endif -#include "IRremoteESP8266.h" -#include "IRsend.h" +#include "../src/IRremoteESP8266.h" +#include "../src/IRsend.h" #ifdef UNIT_TEST -#include "IRsend_test.h" +#include "../src/IRsend_test.h" #endif /// Native representation of a Trotec A/C message. diff --git a/lib/IRremoteESP8266/src/ir_Truma.cpp b/lib/IRremoteESP8266/src/ir_Truma.cpp deleted file mode 100644 index 853c640e16..0000000000 --- a/lib/IRremoteESP8266/src/ir_Truma.cpp +++ /dev/null @@ -1,340 +0,0 @@ -// Copyright 2021 David Conran (crankyoldgit) - -/// @file -/// @brief Support for Truma protocol. -/// This protocol uses mark length bit encoding. -/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1440 -/// @see https://docs.google.com/spreadsheets/d/1k-RHu0vSIB6IweiTZSa3Rxy3Z_qPUtqwcqot8uXVO6I/edit?usp=sharing - - -#include "ir_Truma.h" -#include -#include "IRrecv.h" -#include "IRsend.h" -#include "IRtext.h" -#include "IRutils.h" - -using irutils::addBoolToString; -using irutils::addFanToString; -using irutils::addModeToString; -using irutils::addTempToString; - -// Constants - -const uint16_t kTrumaLdrMark = 20200; -const uint16_t kTrumaLdrSpace = 1000; -const uint16_t kTrumaHdrMark = 1800; -const uint16_t kTrumaSpace = 630; -const uint16_t kTrumaOneMark = 600; -const uint16_t kTrumaZeroMark = 1200; -const uint16_t kTrumaFooterMark = kTrumaOneMark; -const uint32_t kTrumaGap = kDefaultMessageGap; // Just a guess. - - -#if SEND_TRUMA -/// Send a Truma formatted message. -/// Status: STABLE / Confirmed 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::sendTruma(const uint64_t data, const uint16_t nbits, - const uint16_t repeat) { - for (uint16_t r = 0; r <= repeat; r++) { - enableIROut(38000); - mark(kTrumaLdrMark); - space(kTrumaLdrSpace); - sendGeneric(kTrumaHdrMark, kTrumaSpace, // Header - kTrumaOneMark, kTrumaSpace, // Data - kTrumaZeroMark, kTrumaSpace, - kTrumaFooterMark, kTrumaGap, // Footer - data, nbits, 38, false, 0, kDutyDefault); - } -} -#endif // SEND_TRUMA - -#if DECODE_TRUMA -/// Decode the supplied Truma message. -/// Status: STABLE / Confirmed working with 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. Typically kTrumaBits. -/// @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::decodeTruma(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 message. - if (strict && nbits != kTrumaBits) - return false; // Not strictly a message. - - // Leader. - if (!matchMark(results->rawbuf[offset++], kTrumaLdrMark)) return false; - if (!matchSpace(results->rawbuf[offset++], kTrumaLdrSpace)) return false; - - uint64_t data = 0; - uint16_t used; - used = matchGeneric(results->rawbuf + offset, &data, - results->rawlen - offset, nbits, - kTrumaHdrMark, kTrumaSpace, - kTrumaOneMark, kTrumaSpace, - kTrumaZeroMark, kTrumaSpace, - kTrumaFooterMark, kTrumaGap, - true, kUseDefTol, kMarkExcess, false); - if (!used) return false; - - // Compliance - if (strict && !IRTrumaAc::validChecksum(data)) return false; // Checksum. - - // Success - results->value = data; - results->decode_type = decode_type_t::TRUMA; - results->bits = nbits; - results->address = 0; - results->command = 0; - return true; -} -#endif // DECODE_TRUMA - - -/// 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? -IRTrumaAc::IRTrumaAc(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 IRTrumaAc::begin(void) { _irsend.begin(); } - -#if SEND_TRUMA -/// Send the current internal state as an IR message. -/// @param[in] repeat Nr. of times the message will be repeated. -void IRTrumaAc::send(const uint16_t repeat) { - _irsend.sendTruma(getRaw(), kTrumaBits, repeat); -} -#endif // SEND_TRUMA - -/// Calculate the checksum for a given state. -/// @param[in] state The value to calc the checksum of. -/// @return The calculated checksum value. -uint8_t IRTrumaAc::calcChecksum(const uint64_t state) { - uint8_t sum = kTrumaChecksumInit; - uint64_t to_checksum = state; - for (uint16_t i = 8; i < kTrumaBits; i += 8) { - sum += (to_checksum & 0xFF); - to_checksum >>= 8; - } - return sum; -} - -/// 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 IRTrumaAc::validChecksum(const uint64_t state) { - TrumaProtocol state_copy; - state_copy.raw = state; - return state_copy.Sum == calcChecksum(state); -} - -/// Calculate & set the checksum for the current internal state of the remote. -void IRTrumaAc::checksum(void) { _.Sum = calcChecksum(_.raw); } - -/// Reset the state of the remote to a known good state/sequence. -void IRTrumaAc::stateReset(void) { setRaw(kTrumaDefaultState); } - -/// 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 IRTrumaAc::getRaw(void) { - checksum(); - return _.raw; -} - -/// Set the internal state from a valid code for this protocol. -/// @param[in] state A valid code for this protocol. -void IRTrumaAc::setRaw(const uint64_t state) { - _.raw = state; - _lastfan = _.Fan; - _lastmode = _.Mode; -} - -/// Set the requested power state of the A/C to on. -void IRTrumaAc::on(void) { setPower(true); } - -/// Set the requested power state of the A/C to off. -void IRTrumaAc::off(void) { setPower(false); } - -/// Change the power setting. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRTrumaAc::setPower(const bool on) { - _.PowerOff = !on; - _.Mode = on ? _lastmode : kTrumaFan; // Off temporarily sets mode to Fan. -} - -/// Get the value of the current power setting. -/// @return true, the setting is on. false, the setting is off. -bool IRTrumaAc::getPower(void) const { return !_.PowerOff; } - -/// Set the speed of the fan. -/// @param[in] speed The desired setting. -void IRTrumaAc::setFan(const uint8_t speed) { - switch (speed) { - case kTrumaFanHigh: - case kTrumaFanMed: - case kTrumaFanLow: - _lastfan = speed; // Never allow _lastfan to be Quiet. - _.Fan = speed; - break; - case kTrumaFanQuiet: - if (_.Mode == kTrumaCool) _.Fan = kTrumaFanQuiet; // Only in Cool mode. - break; - default: - setFan(kTrumaFanHigh); - } -} - -/// Get the current fan speed setting. -/// @return The current fan speed/mode. -uint8_t IRTrumaAc::getFan(void) const { return _.Fan; } - -/// Set the operating mode of the A/C. -/// @param[in] mode The desired operating mode. -void IRTrumaAc::setMode(const uint8_t mode) { - switch (mode) { - case kTrumaAuto: - case kTrumaFan: - if (getQuiet()) setFan(kTrumaFanHigh); // Can only have quiet in Cool. - // FALL THRU - case kTrumaCool: - _.Mode = _.PowerOff ? kTrumaFan : mode; // When Off, only set Fan mode. - _lastmode = mode; - break; - default: - setMode(kTrumaAuto); - } -} - -/// Get the operating mode setting of the A/C. -/// @return The current operating mode setting. -uint8_t IRTrumaAc::getMode(void) const { return _.Mode; } - -/// Set the temperature. -/// @param[in] celsius The temperature in degrees celsius. -void IRTrumaAc::setTemp(const uint8_t celsius) { - uint8_t temp = std::max(celsius, kTrumaMinTemp); - temp = std::min(temp, kTrumaMaxTemp); - _.Temp = temp - kTrumaTempOffset; -} - -/// Get the current temperature setting. -/// @return The current setting for temp. in degrees celsius. -uint8_t IRTrumaAc::getTemp(void) const { return _.Temp + kTrumaTempOffset; } - -/// Change the Quiet setting. -/// @param[in] on true, the setting is on. false, the setting is off. -/// @note Quiet is only available in Cool mode. -void IRTrumaAc::setQuiet(const bool on) { - if (on && _.Mode == kTrumaCool) - setFan(kTrumaFanQuiet); - else - setFan(_lastfan); -} - -/// Get the value of the current quiet setting. -/// @return true, the setting is on. false, the setting is off. -bool IRTrumaAc::getQuiet(void) const { return _.Fan == kTrumaFanQuiet; } - -/// 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 IRTrumaAc::convertMode(const stdAc::opmode_t mode) { - switch (mode) { - case stdAc::opmode_t::kCool: return kTrumaCool; - case stdAc::opmode_t::kFan: return kTrumaFan; - default: return kTrumaAuto; - } -} - -/// 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 IRTrumaAc::convertFan(const stdAc::fanspeed_t speed) { - switch (speed) { - case stdAc::fanspeed_t::kMin: return kTrumaFanQuiet; - case stdAc::fanspeed_t::kLow: return kTrumaFanLow; - case stdAc::fanspeed_t::kMedium: return kTrumaFanMed; - default: return kTrumaFanHigh; - } -} - -/// 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 IRTrumaAc::toCommonMode(const uint8_t mode) { - switch (mode) { - case kTrumaCool: return stdAc::opmode_t::kCool; - case kTrumaFan: return stdAc::opmode_t::kFan; - default: return stdAc::opmode_t::kAuto; - } -} - -/// Convert a native fan speed into its stdAc equivalent. -/// @param[in] spd The native setting to be converted. -/// @return The stdAc equivalent of the native setting. -stdAc::fanspeed_t IRTrumaAc::toCommonFanSpeed(const uint8_t spd) { - switch (spd) { - case kTrumaFanMed: return stdAc::fanspeed_t::kMedium; - case kTrumaFanLow: return stdAc::fanspeed_t::kLow; - case kTrumaFanQuiet: return stdAc::fanspeed_t::kMin; - default: return stdAc::fanspeed_t::kHigh; - } -} - -/// Convert the current internal state into its stdAc::state_t equivalent. -/// @return The stdAc equivalent of the native settings. -stdAc::state_t IRTrumaAc::toCommon(void) const { - stdAc::state_t result; - - result.protocol = decode_type_t::TRUMA; - result.model = -1; // Not supported. - // Do we have enough current state info to override any previous state? - // i.e. Was the class just setRaw()'ed with a short "swing" message. - // This should enables us to also ignore the Swing msg's special 17C setting. - result.power = getPower(); - result.mode = toCommonMode(getMode()); - result.celsius = true; - result.degrees = getTemp(); - result.fanspeed = toCommonFanSpeed(getFan()); - result.quiet = getQuiet(); - // Not supported. - result.turbo = false; - result.econo = false; - result.light = false; - result.filter = false; - result.swingv = stdAc::swingv_t::kOff; - result.swingh = stdAc::swingh_t::kOff; - 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 IRTrumaAc::toString(void) const { - String result = ""; - result.reserve(80); - result += addBoolToString(getPower(), kPowerStr, false); - if (getPower()) // Only show the Operating Mode if the unit is on. - result += addModeToString(_.Mode, kTrumaAuto, kTrumaCool, - kTrumaAuto, kTrumaAuto, kTrumaFan); - - result += addTempToString(getTemp()); - result += addFanToString(_.Fan, kTrumaFanHigh, kTrumaFanLow, kTrumaFanHigh, - kTrumaFanQuiet, kTrumaFanMed); - result += addBoolToString(getQuiet(), kQuietStr); - return result; -} diff --git a/lib/IRremoteESP8266/src/ir_Truma.h b/lib/IRremoteESP8266/src/ir_Truma.h index 9946a03229..58beeeba44 100644 --- a/lib/IRremoteESP8266/src/ir_Truma.h +++ b/lib/IRremoteESP8266/src/ir_Truma.h @@ -15,10 +15,10 @@ #ifndef UNIT_TEST #include #endif -#include "IRremoteESP8266.h" -#include "IRsend.h" +#include "../src/IRremoteESP8266.h" +#include "../src/IRsend.h" #ifdef UNIT_TEST -#include "IRsend_test.h" +#include "../src/IRsend_test.h" #endif /// Native representation of a Truma A/C message. diff --git a/lib/IRremoteESP8266/src/ir_Vestel.cpp b/lib/IRremoteESP8266/src/ir_Vestel.cpp deleted file mode 100644 index 803363a169..0000000000 --- a/lib/IRremoteESP8266/src/ir_Vestel.cpp +++ /dev/null @@ -1,572 +0,0 @@ -// Copyright 2018 Erdem U. Altinyurt -// Copyright 2019 David Conran - -/// @file -/// @brief Support for Vestel protocols. -/// Vestel added by Erdem U. Altinyurt - -#include "ir_Vestel.h" -#include -#ifndef UNIT_TEST -#include -#endif -#include "IRrecv.h" -#include "IRremoteESP8266.h" -#include "IRsend.h" -#include "IRtext.h" -#include "IRutils.h" -#include "ir_Haier.h" - -// Ref: -// None. Totally reverse engineered. - -using irutils::addBoolToString; -using irutils::addIntToString; -using irutils::addLabeledString; -using irutils::addModeToString; -using irutils::addTempToString; -using irutils::minsToString; - -#if SEND_VESTEL_AC -/// Send a Vestel 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::sendVestelAc(const uint64_t data, const uint16_t nbits, - const uint16_t repeat) { - if (nbits % 8 != 0) return; // nbits is required to be a multiple of 8. - - sendGeneric(kVestelAcHdrMark, kVestelAcHdrSpace, // Header - kVestelAcBitMark, kVestelAcOneSpace, // Data - kVestelAcBitMark, kVestelAcZeroSpace, // Data - kVestelAcBitMark, 100000, // Footer + repeat gap - data, nbits, 38, false, repeat, 50); -} -#endif // SEND_VESTEL_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? -IRVestelAc::IRVestelAc(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. -/// @note Power On, Mode Auto, Fan Auto, Temp = 25C/77F -void IRVestelAc::stateReset(void) { - _.cmdState = kVestelAcStateDefault; - _.timeState = kVestelAcTimeStateDefault; -} - -/// Set up hardware to be able to send a message. -void IRVestelAc::begin(void) { _irsend.begin(); } - -#if SEND_VESTEL_AC -/// Send the current internal state as an IR message. -/// @param[in] repeat Nr. of times the message will be repeated. -void IRVestelAc::send(const uint16_t repeat) { - _irsend.sendVestelAc(getRaw(), kVestelAcBits, repeat); -} -#endif // SEND_VESTEL_AC - -/// Get a copy of the internal state/code for this protocol. -/// @return A code for this protocol based on the current internal state. -uint64_t IRVestelAc::getRaw(void) { - checksum(); - if (!_.UseCmd) return _.timeState; - return _.cmdState; -} - -/// Set the internal state from a valid code for this protocol. -/// @param[in] newState A valid code for this protocol. -void IRVestelAc::setRaw(const uint8_t* newState) { - uint64_t upState = 0; - for (int i = 0; i < 7; i++) - upState |= static_cast(newState[i]) << (i * 8); - setRaw(upState); -} - -/// Set the internal state from a valid code for this protocol. -/// @param[in] newState A valid code for this protocol. -void IRVestelAc::setRaw(const uint64_t newState) { - _.cmdState = newState; - _.timeState = newState; - if (isTimeCommand()) { - _.cmdState = kVestelAcStateDefault; - _.UseCmd = false; - } else { - _.timeState = kVestelAcTimeStateDefault; - } -} - -/// Set the requested power state of the A/C to on. -void IRVestelAc::on(void) { setPower(true); } - -/// Set the requested power state of the A/C to off. -void IRVestelAc::off(void) { setPower(false); } - -/// Change the power setting. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRVestelAc::setPower(const bool on) { - _.Power = (on ? 0b11 : 0b00); - _.UseCmd = true; -} - -/// Get the value of the current power setting. -/// @return true, the setting is on. false, the setting is off. -bool IRVestelAc::getPower(void) const { - return _.Power; -} - -/// Set the temperature. -/// @param[in] temp The temperature in degrees celsius. -void IRVestelAc::setTemp(const uint8_t temp) { - uint8_t new_temp = std::max(kVestelAcMinTempC, temp); - new_temp = std::min(kVestelAcMaxTemp, new_temp); - _.Temp = new_temp - kVestelAcMinTempH; - _.UseCmd = true; -} - -/// Get the current temperature setting. -/// @return The current setting for temp. in degrees celsius. -uint8_t IRVestelAc::getTemp(void) const { - return _.Temp + kVestelAcMinTempH; -} - -/// Set the speed of the fan. -/// @param[in] fan The desired setting. -void IRVestelAc::setFan(const uint8_t fan) { - switch (fan) { - case kVestelAcFanLow: - case kVestelAcFanMed: - case kVestelAcFanHigh: - case kVestelAcFanAutoCool: - case kVestelAcFanAutoHot: - case kVestelAcFanAuto: - _.Fan = fan; - break; - default: - _.Fan = kVestelAcFanAuto; - } - _.UseCmd = true; -} - -/// Get the current fan speed setting. -/// @return The current fan speed/mode. -uint8_t IRVestelAc::getFan(void) const { - return _.Fan; -} - -/// Get the operating mode setting of the A/C. -/// @return The current operating mode setting. -uint8_t IRVestelAc::getMode(void) const { - return _.Mode; -} - -/// Set the operating mode of the A/C. -/// @param[in] mode The desired operating mode. -/// @note If we get an unexpected mode, default to AUTO. -void IRVestelAc::setMode(const uint8_t mode) { - switch (mode) { - case kVestelAcAuto: - case kVestelAcCool: - case kVestelAcHeat: - case kVestelAcDry: - case kVestelAcFan: - _.Mode = mode; - break; - default: - _.Mode = kVestelAcAuto; - } - _.UseCmd = true; -} - -/// Set Auto mode/level of the A/C. -/// @param[in] autoLevel The auto mode/level setting. -void IRVestelAc::setAuto(const int8_t autoLevel) { - if (autoLevel < -2 || autoLevel > 2) return; - _.Mode = kVestelAcAuto; - _.Fan = (autoLevel < 0 ? kVestelAcFanAutoCool : kVestelAcFanAutoHot); - if (autoLevel == 2) - setTemp(30); - else if (autoLevel == 1) - setTemp(31); - else if (autoLevel == 0) - setTemp(25); - else if (autoLevel == -1) - setTemp(16); - else if (autoLevel == -2) - setTemp(17); -} - -/// Set the timer to be active on the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRVestelAc::setTimerActive(const bool on) { - _.Timer = on; - _.UseCmd = false; -} - -/// Get if the Timer is active on the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRVestelAc::isTimerActive(void) const { - return _.Timer; -} - -/// Set Timer option of A/C. -/// @param[in] minutes Nr of minutes the timer is to be set for. -/// @note Valid arguments are 0, 0.5, 1, 2, 3 and 5 hours (in minutes). -/// 0 disables the timer. -void IRVestelAc::setTimer(const uint16_t minutes) { - // Clear both On & Off timers. - _.OnHours = 0; - _.OnTenMins = 0; - // Set the "Off" time with the nr of minutes before we turn off. - _.OffHours = minutes / 60; - _.OffTenMins = (minutes % 60) / 10; - setOffTimerActive(false); - // Yes. On Timer instead of Off timer active. - setOnTimerActive(minutes != 0); - setTimerActive(minutes != 0); -} - -/// Get the Timer time of A/C. -/// @return The number of minutes of time on the timer. -uint16_t IRVestelAc::getTimer(void) const { return getOffTimer(); } - -/// Set the A/C's internal clock. -/// @param[in] minutes The time expressed in nr. of minutes past midnight. -void IRVestelAc::setTime(const uint16_t minutes) { - _.Hours = minutes / 60; - _.Minutes = minutes % 60; - _.UseCmd = false; -} - -/// Get the A/C's internal clock's time. -/// @return The time expressed in nr. of minutes past midnight. -uint16_t IRVestelAc::getTime(void) const { - return _.Hours * 60 + _.Minutes; -} - -/// Set the On timer to be active on the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRVestelAc::setOnTimerActive(const bool on) { - _.OnTimer = on; - _.UseCmd = false; -} - -/// Get if the On Timer is active on the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRVestelAc::isOnTimerActive(void) const { - return _.OnTimer; -} - -/// Set the On timer time on the A/C. -/// @param[in] minutes Time in nr. of minutes. -void IRVestelAc::setOnTimer(const uint16_t minutes) { - setOnTimerActive(minutes); - _.OnHours = minutes / 60; - _.OnTenMins = (minutes % 60) / 10; - setTimerActive(false); -} - -/// Get the A/C's On Timer time. -/// @return The time expressed in nr. of minutes. -uint16_t IRVestelAc::getOnTimer(void) const { - return _.OnHours * 60 + _.OnTenMins * 10; -} - -/// Set the Off timer to be active on the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRVestelAc::setOffTimerActive(const bool on) { - _.OffTimer = on; - _.UseCmd = false; -} - -/// Get if the Off Timer is active on the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRVestelAc::isOffTimerActive(void) const { - return _.OffTimer; -} - -/// Set the Off timer time on the A/C. -/// @param[in] minutes Time in nr. of minutes. -void IRVestelAc::setOffTimer(const uint16_t minutes) { - setOffTimerActive(minutes); - _.OffHours = minutes / 60; - _.OffTenMins = (minutes % 60) / 10; - setTimerActive(false); -} - -/// Get the A/C's Off Timer time. -/// @return The time expressed in nr. of minutes. -uint16_t IRVestelAc::getOffTimer(void) const { - return _.OffHours * 60 + _.OffTenMins * 10; -} - -/// Set the Sleep setting of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRVestelAc::setSleep(const bool on) { - _.TurboSleep = (on ? kVestelAcSleep : kVestelAcNormal); - _.UseCmd = true; -} - -/// Get the Sleep setting of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRVestelAc::getSleep(void) const { - return _.TurboSleep == kVestelAcSleep; -} - -/// Set the Turbo setting of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRVestelAc::setTurbo(const bool on) { - _.TurboSleep = (on ? kVestelAcTurbo : kVestelAcNormal); - _.UseCmd = true; -} - -/// Get the Turbo setting of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRVestelAc::getTurbo(void) const { - return _.TurboSleep == kVestelAcTurbo; -} - -/// Set the Ion (Filter) setting of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRVestelAc::setIon(const bool on) { - _.Ion = on; - _.UseCmd = true; -} - -/// Get the Ion (Filter) setting of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRVestelAc::getIon(void) const { - return _.Ion; -} - -/// Set the Swing Roaming setting of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRVestelAc::setSwing(const bool on) { - _.Swing = (on ? kVestelAcSwing : 0xF); - _.UseCmd = true; -} - -/// Get the Swing Roaming setting of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRVestelAc::getSwing(void) const { - return _.Swing == kVestelAcSwing; -} - -/// Calculate the checksum for a given state. -/// @param[in] state The state to calc the checksum of. -/// @return The calculated checksum value. -uint8_t IRVestelAc::calcChecksum(const uint64_t state) { - // Just counts the set bits +1 on stream and take inverse after mask - return 0xFF - countBits(GETBITS64(state, 20, 44), 44, true, 2); -} - -/// 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 IRVestelAc::validChecksum(const uint64_t state) { - VestelProtocol vp; - vp.cmdState = state; - return vp.CmdSum == IRVestelAc::calcChecksum(state); -} - -/// Calculate & set the checksum for the current internal state of the remote. -void IRVestelAc::checksum(void) { - // Stored the checksum value in the last byte. - _.CmdSum = calcChecksum(_.cmdState); - _.TimeSum = calcChecksum(_.timeState); -} - -/// Is the current state a time command? -/// @return true, if the state is a time message. Otherwise, false. -bool IRVestelAc::isTimeCommand(void) const { - return !_.UseCmd; -} - -/// 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 IRVestelAc::convertMode(const stdAc::opmode_t mode) { - switch (mode) { - case stdAc::opmode_t::kCool: return kVestelAcCool; - case stdAc::opmode_t::kHeat: return kVestelAcHeat; - case stdAc::opmode_t::kDry: return kVestelAcDry; - case stdAc::opmode_t::kFan: return kVestelAcFan; - default: return kVestelAcAuto; - } -} - -/// 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 IRVestelAc::convertFan(const stdAc::fanspeed_t speed) { - switch (speed) { - case stdAc::fanspeed_t::kMin: - case stdAc::fanspeed_t::kLow: return kVestelAcFanLow; - case stdAc::fanspeed_t::kMedium: return kVestelAcFanMed; - case stdAc::fanspeed_t::kHigh: - case stdAc::fanspeed_t::kMax: return kVestelAcFanHigh; - default: return kVestelAcFanAuto; - } -} - -/// 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 IRVestelAc::toCommonMode(const uint8_t mode) { - switch (mode) { - case kVestelAcCool: return stdAc::opmode_t::kCool; - case kVestelAcHeat: return stdAc::opmode_t::kHeat; - case kVestelAcDry: return stdAc::opmode_t::kDry; - case kVestelAcFan: return stdAc::opmode_t::kFan; - default: return stdAc::opmode_t::kAuto; - } -} - -/// Convert a native fan speed into its stdAc equivalent. -/// @param[in] spd The native setting to be converted. -/// @return The stdAc equivalent of the native setting. -stdAc::fanspeed_t IRVestelAc::toCommonFanSpeed(const uint8_t spd) { - switch (spd) { - case kVestelAcFanHigh: return stdAc::fanspeed_t::kMax; - case kVestelAcFanMed: return stdAc::fanspeed_t::kMedium; - case kVestelAcFanLow: 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 IRVestelAc::toCommon(void) const { - stdAc::state_t result; - result.protocol = decode_type_t::VESTEL_AC; - result.model = -1; // Not supported. - result.power = _.Power; - result.mode = toCommonMode(_.Mode); - result.celsius = true; - result.degrees = getTemp(); - result.fanspeed = toCommonFanSpeed(_.Fan); - result.swingv = (getSwing() ? stdAc::swingv_t::kAuto - : stdAc::swingv_t::kOff); - result.turbo = getTurbo(); - result.filter = _.Ion; - result.sleep = (getSleep() ? 0 : -1); - // Not supported. - result.swingh = stdAc::swingh_t::kOff; - result.light = 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 IRVestelAc::toString(void) const { - String result = ""; - result.reserve(100); // Reserve some heap for the string to reduce fragging. - if (isTimeCommand()) { - result += addLabeledString(minsToString(getTime()), kClockStr, false); - result += addLabeledString( - (_.Timer ? minsToString(getTimer()) : kOffStr), - kTimerStr); - result += addLabeledString( - (_.OnTimer && !_.Timer) ? minsToString(getOnTimer()) : kOffStr, - kOnTimerStr); - result += addLabeledString( - (_.OffTimer ? minsToString(getOffTimer()) : kOffStr), - kOffTimerStr); - return result; - } - // Not a time command, it's a normal command. - result += addBoolToString(_.Power, kPowerStr, false); - result += addModeToString(_.Mode, kVestelAcAuto, kVestelAcCool, - kVestelAcHeat, kVestelAcDry, kVestelAcFan); - result += addTempToString(getTemp()); - result += addIntToString(_.Fan, kFanStr); - result += kSpaceLBraceStr; - switch (_.Fan) { - case kVestelAcFanAuto: - result += kAutoStr; - break; - case kVestelAcFanLow: - result += kLowStr; - break; - case kVestelAcFanMed: - result += kMedStr; - break; - case kVestelAcFanHigh: - result += kHighStr; - break; - case kVestelAcFanAutoCool: - result += kAutoStr; - result += ' '; - result += kCoolStr; - break; - case kVestelAcFanAutoHot: - result += kAutoStr; - result += ' '; - result += kHeatStr; - break; - default: - result += kUnknownStr; - } - result += ')'; - result += addBoolToString(getSleep(), kSleepStr); - result += addBoolToString(getTurbo(), kTurboStr); - result += addBoolToString(_.Ion, kIonStr); - result += addBoolToString(getSwing(), kSwingStr); - return result; -} - -#if DECODE_VESTEL_AC -/// Decode the supplied Vestel 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. -/// @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::decodeVestelAc(decode_results* results, uint16_t offset, - const uint16_t nbits, const bool strict) { - if (nbits % 8 != 0) // nbits has to be a multiple of nr. of bits in a byte. - return false; - - if (strict) - if (nbits != kVestelAcBits) - return false; // Not strictly a Vestel AC message. - - uint64_t data = 0; - - if (nbits > sizeof(data) * 8) - return false; // We can't possibly capture a Vestel packet that big. - - // Match Header + Data + Footer - if (!matchGeneric(results->rawbuf + offset, &data, - results->rawlen - offset, nbits, - kVestelAcHdrMark, kVestelAcHdrSpace, - kVestelAcBitMark, kVestelAcOneSpace, - kVestelAcBitMark, kVestelAcZeroSpace, - kVestelAcBitMark, 0, false, - kVestelAcTolerance, kMarkExcess, false)) return false; - // Compliance - if (strict) - if (!IRVestelAc::validChecksum(data)) return false; - - // Success - results->decode_type = VESTEL_AC; - results->bits = nbits; - results->value = data; - results->address = 0; - results->command = 0; - - return true; -} -#endif // DECODE_VESTEL_AC diff --git a/lib/IRremoteESP8266/src/ir_Vestel.h b/lib/IRremoteESP8266/src/ir_Vestel.h index 53ebdc7cda..0b3bd2c4b6 100644 --- a/lib/IRremoteESP8266/src/ir_Vestel.h +++ b/lib/IRremoteESP8266/src/ir_Vestel.h @@ -16,10 +16,10 @@ #ifdef ARDUINO #include #endif -#include "IRremoteESP8266.h" -#include "IRsend.h" +#include "../src/IRremoteESP8266.h" +#include "../src/IRsend.h" #ifdef UNIT_TEST -#include "IRsend_test.h" +#include "../src/IRsend_test.h" #endif /// Native representation of a Vestel A/C message. diff --git a/lib/IRremoteESP8266/src/ir_Voltas.cpp b/lib/IRremoteESP8266/src/ir_Voltas.cpp deleted file mode 100644 index 847446f385..0000000000 --- a/lib/IRremoteESP8266/src/ir_Voltas.cpp +++ /dev/null @@ -1,516 +0,0 @@ -// Copyright 2020 David Conran (crankyoldgit) -// Copyright 2020 manj9501 -/// @file -/// @brief Support for Voltas A/C protocol -/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1238 - -#include "ir_Voltas.h" -#include -#include -#include "IRrecv.h" -#include "IRsend.h" -#include "IRtext.h" -#include "IRutils.h" - -using irutils::addBoolToString; -using irutils::addModelToString; -using irutils::addModeToString; -using irutils::addFanToString; -using irutils::addLabeledString; -using irutils::addTempToString; -using irutils::minsToString; - -// Constants -const uint16_t kVoltasBitMark = 1026; ///< uSeconds. -const uint16_t kVoltasOneSpace = 2553; ///< uSeconds. -const uint16_t kVoltasZeroSpace = 554; ///< uSeconds. -const uint16_t kVoltasFreq = 38000; ///< Hz. - -#if SEND_VOLTAS -/// Send a Voltas formatted message. -/// Status: STABLE / Working on real device. -/// @param[in] data An array of bytes containing the IR command. -/// It is assumed to be in MSB order for this code. -/// e.g. -/// @code -/// uint8_t data[kVoltasStateLength] = {0x33, 0x28, 0x88, 0x1A, 0x3B, 0x3B, -/// 0x3B, 0x11, 0x00, 0x40}; -/// @endcode -/// @param[in] nbytes Nr. of bytes of data in the array. (>=kVoltasStateLength) -/// @param[in] repeat Nr. of times the message is to be repeated. -void IRsend::sendVoltas(const uint8_t data[], const uint16_t nbytes, - const uint16_t repeat) { - sendGeneric(0, 0, - kVoltasBitMark, kVoltasOneSpace, - kVoltasBitMark, kVoltasZeroSpace, - kVoltasBitMark, kDefaultMessageGap, - data, nbytes, - kVoltasFreq, true, repeat, kDutyDefault); -} -#endif // SEND_VOLTAS - -#if DECODE_VOLTAS -/// Decode the supplied Voltas message. -/// Status: STABLE / Working on real device. -/// @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::decodeVoltas(decode_results *results, uint16_t offset, - const uint16_t nbits, const bool strict) { - if (strict && nbits != kVoltasBits) return false; - - // Data + Footer - if (!matchGeneric(results->rawbuf + offset, results->state, - results->rawlen - offset, nbits, - 0, 0, // No header - kVoltasBitMark, kVoltasOneSpace, - kVoltasBitMark, kVoltasZeroSpace, - kVoltasBitMark, kDefaultMessageGap, true)) return false; - - // Compliance - if (strict && !IRVoltas::validChecksum(results->state, nbits / 8)) - return false; - // Success - results->decode_type = decode_type_t::VOLTAS; - results->bits = nbits; - return true; -} -#endif // DECODE_VOLTAS - -/// 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? -IRVoltas::IRVoltas(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 IRVoltas::stateReset() { - // This resets to a known-good state. - // ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/1238#issuecomment-674699746 - const uint8_t kReset[kVoltasStateLength] = { - 0x33, 0x28, 0x00, 0x17, 0x3B, 0x3B, 0x3B, 0x11, 0x00, 0xCB}; - setRaw(kReset); -} - -/// Set up hardware to be able to send a message. -void IRVoltas::begin() { _irsend.begin(); } - -#if SEND_VOLTAS -/// Send the current internal state as an IR message. -/// @param[in] repeat Nr. of times the message will be repeated. -void IRVoltas::send(const uint16_t repeat) { - _irsend.sendVoltas(getRaw(), kVoltasStateLength, repeat); -} -#endif // SEND_VOLTAS - -/// Get the model information currently known. -/// @param[in] raw Work out the model info from the current raw state. -/// @return The known model number. -voltas_ac_remote_model_t IRVoltas::getModel(const bool raw) const { - if (raw) { - switch (_.SwingHChange) { - case kVoltasSwingHNoChange: - return voltas_ac_remote_model_t::kVoltas122LZF; - default: - return voltas_ac_remote_model_t::kVoltasUnknown; - } - } else { - return _model; - } -} - -/// Set the current model for the remote. -/// @param[in] model The model number. -void IRVoltas::setModel(const voltas_ac_remote_model_t model) { - switch (model) { - case voltas_ac_remote_model_t::kVoltas122LZF: - _model = model; - setSwingHChange(false); - break; - default: _model = voltas_ac_remote_model_t::kVoltasUnknown; - } -} - -/// 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* IRVoltas::getRaw(void) { - checksum(); // Ensure correct settings before sending. - return _.raw; -} - -/// Set the internal state from a valid code for this protocol. -/// @param[in] new_code A valid code for this protocol. -void IRVoltas::setRaw(const uint8_t new_code[]) { - std::memcpy(_.raw, new_code, kVoltasStateLength); - setModel(getModel(true)); -} - -/// Calculate and set the checksum values for the internal state. -void IRVoltas::checksum(void) { - _.Checksum = calcChecksum(_.raw); -} - -/// 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 IRVoltas::validChecksum(const uint8_t state[], const uint16_t length) { - if (length) return state[length - 1] == calcChecksum(state, length); - return true; -} - -/// Calculate the checksum is valid for a given state. -/// @param[in] state The array to calculate the checksum of. -/// @param[in] length The length of the state array. -/// @return The valid checksum value for the state. -uint8_t IRVoltas::calcChecksum(const uint8_t state[], const uint16_t length) { - uint8_t result = 0; - if (length) - result = sumBytes(state, length - 1); - return ~result; -} - -/// Change the power setting to On. -void IRVoltas::on() { setPower(true); } - -/// Change the power setting to Off. -void IRVoltas::off() { setPower(false); } - -/// Change the power setting. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRVoltas::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 IRVoltas::getPower(void) const { return _.Power; } - -/// Set the operating mode of the A/C. -/// @param[in] mode The desired operating mode. -/// @note If we get an unexpected mode, default to AUTO. -void IRVoltas::setMode(const uint8_t mode) { - _.Mode = mode; - switch (mode) { - case kVoltasFan: - setFan(getFan()); // Force the fan speed to a correct one fo the mode. - break; - case kVoltasDry: - setFan(kVoltasFanLow); - setTemp(kVoltasDryTemp); - break; - case kVoltasHeat: - case kVoltasCool: - break; - default: - setMode(kVoltasCool); - return; - } - // Reset some settings if needed. - setEcono(getEcono()); - setTurbo(getTurbo()); - setSleep(getSleep()); -} - -/// Get the operating mode setting of the A/C. -/// @return The current operating mode setting. -uint8_t IRVoltas::getMode(void) { 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 IRVoltas::convertMode(const stdAc::opmode_t mode) { - switch (mode) { - case stdAc::opmode_t::kHeat: return kVoltasHeat; - case stdAc::opmode_t::kDry: return kVoltasDry; - case stdAc::opmode_t::kFan: return kVoltasFan; - default: return kVoltasCool; - } -} - -/// 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 IRVoltas::toCommonMode(const uint8_t mode) { - switch (mode) { - case kVoltasHeat: return stdAc::opmode_t::kHeat; - case kVoltasDry: return stdAc::opmode_t::kDry; - case kVoltasFan: return stdAc::opmode_t::kFan; - default: return stdAc::opmode_t::kCool; - } -} - -/// Set the temperature. -/// @param[in] temp The temperature in degrees celsius. -void IRVoltas::setTemp(const uint8_t temp) { - uint8_t new_temp = std::max(kVoltasMinTemp, temp); - new_temp = std::min(kVoltasMaxTemp, new_temp); - _.Temp = new_temp - kVoltasMinTemp; -} - -/// Get the current temperature setting. -/// @return The current setting for temp. in degrees celsius. -uint8_t IRVoltas::getTemp(void) { return _.Temp + kVoltasMinTemp; } - -/// Set the speed of the fan. -/// @param[in] fan The desired setting. -void IRVoltas::setFan(const uint8_t fan) { - switch (fan) { - case kVoltasFanAuto: - if (_.Mode == kVoltasFan) { // Auto speed is not available in fan mode. - setFan(kVoltasFanHigh); - return; - } - // FALL-THRU - case kVoltasFanLow: - case kVoltasFanMed: - case kVoltasFanHigh: - _.FanSpeed = fan; - break; - default: - setFan(kVoltasFanAuto); - } -} - -/// Get the current fan speed setting. -/// @return The current fan speed/mode. -uint8_t IRVoltas::getFan(void) { return _.FanSpeed; } - -/// 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 IRVoltas::convertFan(const stdAc::fanspeed_t speed) { - switch (speed) { - case stdAc::fanspeed_t::kMin: - case stdAc::fanspeed_t::kLow: return kVoltasFanLow; - case stdAc::fanspeed_t::kMedium: return kVoltasFanMed; - case stdAc::fanspeed_t::kHigh: - case stdAc::fanspeed_t::kMax: return kVoltasFanHigh; - default: return kVoltasFanAuto; - } -} - -/// Convert a native fan speed into its stdAc equivalent. -/// @param[in] spd The native setting to be converted. -/// @return The stdAc equivalent of the native setting. -stdAc::fanspeed_t IRVoltas::toCommonFanSpeed(const uint8_t spd) { - switch (spd) { - case kVoltasFanHigh: return stdAc::fanspeed_t::kMax; - case kVoltasFanMed: return stdAc::fanspeed_t::kMedium; - case kVoltasFanLow: 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 IRVoltas::setSwingV(const bool on) { _.SwingV = on ? 0b111 : 0b000; } - -/// Get the Vertical Swing setting of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRVoltas::getSwingV(void) const { return _.SwingV == 0b111; } - -/// Set the Horizontal Swing setting of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRVoltas::setSwingH(const bool on) { - switch (_model) { - case voltas_ac_remote_model_t::kVoltas122LZF: - break; // unsupported on these models. - default: - _.SwingH = on; - setSwingHChange(true); - } -} - -/// Get the Horizontal Swing setting of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRVoltas::getSwingH(void) const { - switch (_model) { - case voltas_ac_remote_model_t::kVoltas122LZF: - return false; // unsupported on these models. - default: - return _.SwingH; - } -} - -/// Set the bits for changing the Horizontal Swing setting of the A/C. -/// @param[in] on true, the change bits are set. -/// false, the "no change" bits are set. -void IRVoltas::setSwingHChange(const bool on) { - _.SwingHChange = on ? kVoltasSwingHChange : kVoltasSwingHNoChange; - if (!on) _.SwingH = true; // "No Change" also sets SwingH to 1. -} - -/// Are the Horizontal Swing change bits set in the message? -/// @return true, the correct bits are set. false, the correct bits are not set. -bool IRVoltas::getSwingHChange(void) const { - return _.SwingHChange == kVoltasSwingHChange; -} - -/// Change the Wifi setting. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRVoltas::setWifi(const bool on) { _.Wifi = on; } - -/// Get the value of the current Wifi setting. -/// @return true, the setting is on. false, the setting is off. -bool IRVoltas::getWifi(void) const { return _.Wifi; } - -/// Change the Turbo setting. -/// @param[in] on true, the setting is on. false, the setting is off. -/// @note The Turbo setting is only available in Cool mode. -void IRVoltas::setTurbo(const bool on) { - if (on && _.Mode == kVoltasCool) - _.Turbo = true; - else - _.Turbo = false; -} - -/// Get the value of the current Turbo setting. -/// @return true, the setting is on. false, the setting is off. -bool IRVoltas::getTurbo(void) const { return _.Turbo; } - -/// Change the Economy setting. -/// @param[in] on true, the setting is on. false, the setting is off. -/// @note The Economy setting is only available in Cool mode. -void IRVoltas::setEcono(const bool on) { - if (on && _.Mode == kVoltasCool) - _.Econo = true; - else - _.Econo = false; -} - -/// Get the value of the current Econo setting. -/// @return true, the setting is on. false, the setting is off. -bool IRVoltas::getEcono(void) const { return _.Econo; } - -/// Change the Light setting. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRVoltas::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 IRVoltas::getLight(void) const { return _.Light; } - -/// Change the Sleep setting. -/// @param[in] on true, the setting is on. false, the setting is off. -/// @note The Sleep setting is only available in Cool mode. -void IRVoltas::setSleep(const bool on) { - if (on && _.Mode == kVoltasCool) - _.Sleep = true; - else - _.Sleep = false; -} - -/// Get the value of the current Sleep setting. -/// @return true, the setting is on. false, the setting is off. -bool IRVoltas::getSleep(void) const { return _.Sleep; } - -/// Get the value of the On Timer time. -/// @return Number of minutes before the timer activates. -uint16_t IRVoltas::getOnTime(void) const { - return std::min((unsigned)(12 * _.OnTimer12Hr + _.OnTimerHrs - 1), 23U) * 60 + - _.OnTimerMins; -} - -/// Set the value of the On Timer time. -/// @param[in] nr_of_mins Number of minutes before the timer activates. -/// 0 disables the timer. Max is 23 hrs & 59 mins (1439 mins) -void IRVoltas::setOnTime(const uint16_t nr_of_mins) { - // Cap the total number of mins. - uint16_t mins = std::min(nr_of_mins, (uint16_t)(23 * 60 + 59)); - uint16_t hrs = (mins / 60) + 1; - _.OnTimerMins = mins % 60; - _.OnTimer12Hr = hrs / 12; - _.OnTimerHrs = hrs % 12; - _.OnTimerEnable = (mins > 0); // Is the timer is to be enabled? -} - -/// Get the value of the On Timer time. -/// @return Number of minutes before the timer activates. -uint16_t IRVoltas::getOffTime(void) const { - return std::min((unsigned)(12 * _.OffTimer12Hr + _.OffTimerHrs - 1), 23U) * - 60 + _.OffTimerMins; -} - -/// Set the value of the Off Timer time. -/// @param[in] nr_of_mins Number of minutes before the timer activates. -/// 0 disables the timer. Max is 23 hrs & 59 mins (1439 mins) -void IRVoltas::setOffTime(const uint16_t nr_of_mins) { - // Cap the total number of mins. - uint16_t mins = std::min(nr_of_mins, (uint16_t)(23 * 60 + 59)); - uint16_t hrs = (mins / 60) + 1; - _.OffTimerMins = mins % 60; - _.OffTimer12Hr = hrs / 12; - _.OffTimerHrs = hrs % 12; - _.OffTimerEnable = (mins > 0); // Is the timer is to be enabled? -} - -/// Convert the current internal state into its stdAc::state_t equivalent. -/// @param[in] prev Ptr to the previous state if available. -/// @return The stdAc equivalent of the native settings. -stdAc::state_t IRVoltas::toCommon(const stdAc::state_t *prev) { - 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. - result.swingh = stdAc::swingh_t::kOff; - } - result.model = getModel(); - result.protocol = decode_type_t::VOLTAS; - result.power = _.Power; - result.mode = toCommonMode(_.Mode); - result.celsius = true; - result.degrees = getTemp(); - result.fanspeed = toCommonFanSpeed(_.FanSpeed); - result.swingv = getSwingV() ? stdAc::swingv_t::kAuto : stdAc::swingv_t::kOff; - if (getSwingHChange()) - result.swingh = _.SwingH ? stdAc::swingh_t::kAuto : stdAc::swingh_t::kOff; - result.turbo = _.Turbo; - result.econo = _.Econo; - result.light = _.Light; - result.sleep = _.Sleep ? 0 : -1; - // Not supported. - result.quiet = 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 IRVoltas::toString() { - String result = ""; - result.reserve(200); // Reserve some heap for the string to reduce fragging. - result += addModelToString(decode_type_t::VOLTAS, getModel(), false); - result += addBoolToString(_.Power, kPowerStr); - result += addModeToString(_.Mode, 255, kVoltasCool, kVoltasHeat, - kVoltasDry, kVoltasFan); - result += addTempToString(getTemp()); - result += addFanToString(_.FanSpeed, kVoltasFanHigh, kVoltasFanLow, - kVoltasFanAuto, kVoltasFanAuto, kVoltasFanMed); - result += addBoolToString(getSwingV(), kSwingVStr); - if (getSwingHChange()) - result += addBoolToString(_.SwingH, kSwingHStr); - else - result += addLabeledString(kNAStr, kSwingHStr); - result += addBoolToString(_.Turbo, kTurboStr); - result += addBoolToString(_.Econo, kEconoStr); - result += addBoolToString(_.Wifi, kWifiStr); - result += addBoolToString(_.Light, kLightStr); - result += addBoolToString(_.Sleep, kSleepStr); - result += addLabeledString(_.OnTimerEnable ? minsToString(getOnTime()) - : kOffStr, kOnTimerStr); - result += addLabeledString(_.OffTimerEnable ? minsToString(getOffTime()) - : kOffStr, kOffTimerStr); - return result; -} diff --git a/lib/IRremoteESP8266/src/ir_Voltas.h b/lib/IRremoteESP8266/src/ir_Voltas.h index cf79d3458f..64ab1b5127 100644 --- a/lib/IRremoteESP8266/src/ir_Voltas.h +++ b/lib/IRremoteESP8266/src/ir_Voltas.h @@ -18,10 +18,10 @@ #ifndef UNIT_TEST #include #endif -#include "IRremoteESP8266.h" -#include "IRsend.h" +#include "../src/IRremoteESP8266.h" +#include "../src/IRsend.h" #ifdef UNIT_TEST -#include "IRsend_test.h" +#include "../src/IRsend_test.h" #endif /// Native representation of a Voltas A/C message. diff --git a/lib/IRremoteESP8266/src/ir_Whirlpool.cpp b/lib/IRremoteESP8266/src/ir_Whirlpool.cpp deleted file mode 100644 index 0bba57bfec..0000000000 --- a/lib/IRremoteESP8266/src/ir_Whirlpool.cpp +++ /dev/null @@ -1,657 +0,0 @@ -// Copyright 2018 David Conran - -/// @file -/// @brief Support for Whirlpool protocols. -/// Decoding help from: \@redmusicxd, \@josh929800, \@raducostea -/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/509 -/// @note Smart, iFeel, AroundU, PowerSave, & Silent modes are unsupported. -/// Advanced 6thSense, Dehumidify, & Sleep modes are not supported. -/// @note Dim == !Light, Jet == Super == Turbo - -#include "ir_Whirlpool.h" -#include -#include -#ifndef ARDUINO -#include -#endif -#include "IRrecv.h" -#include "IRremoteESP8266.h" -#include "IRsend.h" -#include "IRtext.h" -#include "IRutils.h" - -// Constants -const uint16_t kWhirlpoolAcHdrMark = 8950; -const uint16_t kWhirlpoolAcHdrSpace = 4484; -const uint16_t kWhirlpoolAcBitMark = 597; -const uint16_t kWhirlpoolAcOneSpace = 1649; -const uint16_t kWhirlpoolAcZeroSpace = 533; -const uint16_t kWhirlpoolAcGap = 7920; -const uint32_t kWhirlpoolAcMinGap = kDefaultMessageGap; // Just a guess. -const uint8_t kWhirlpoolAcSections = 3; - -using irutils::addBoolToString; -using irutils::addFanToString; -using irutils::addIntToString; -using irutils::addLabeledString; -using irutils::addModeToString; -using irutils::addModelToString; -using irutils::addTempToString; -using irutils::minsToString; - -#define GETTIME(x) (_.x##Hours * 60 + _.x##Mins) -#define SETTIME(x, n) do { \ - uint16_t mins = n;\ - _.x##Hours = (mins / 60) % 24;\ - _.x##Mins = mins % 60;\ -} while (0) - -#if SEND_WHIRLPOOL_AC -/// Send a Whirlpool A/C message. -/// Status: BETA / Probably works. -/// @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::sendWhirlpoolAC(const unsigned char data[], const uint16_t nbytes, - const uint16_t repeat) { - if (nbytes < kWhirlpoolAcStateLength) - return; // Not enough bytes to send a proper message. - for (uint16_t r = 0; r <= repeat; r++) { - // Section 1 - sendGeneric(kWhirlpoolAcHdrMark, kWhirlpoolAcHdrSpace, kWhirlpoolAcBitMark, - kWhirlpoolAcOneSpace, kWhirlpoolAcBitMark, - kWhirlpoolAcZeroSpace, kWhirlpoolAcBitMark, kWhirlpoolAcGap, - data, 6, // 6 bytes == 48 bits - 38000, // Complete guess of the modulation frequency. - false, 0, 50); - // Section 2 - sendGeneric(0, 0, kWhirlpoolAcBitMark, kWhirlpoolAcOneSpace, - kWhirlpoolAcBitMark, kWhirlpoolAcZeroSpace, kWhirlpoolAcBitMark, - kWhirlpoolAcGap, data + 6, 8, // 8 bytes == 64 bits - 38000, // Complete guess of the modulation frequency. - false, 0, 50); - // Section 3 - sendGeneric(0, 0, kWhirlpoolAcBitMark, kWhirlpoolAcOneSpace, - kWhirlpoolAcBitMark, kWhirlpoolAcZeroSpace, kWhirlpoolAcBitMark, - kWhirlpoolAcMinGap, data + 14, 7, // 7 bytes == 56 bits - 38000, // Complete guess of the modulation frequency. - false, 0, 50); - } -} -#endif // SEND_WHIRLPOOL_AC - -// Class for emulating a Whirlpool A/C remote. - -/// 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? -IRWhirlpoolAc::IRWhirlpoolAc(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 IRWhirlpoolAc::stateReset(void) { - for (uint8_t i = 2; i < kWhirlpoolAcStateLength; i++) _.raw[i] = 0x0; - _.raw[0] = 0x83; - _.raw[1] = 0x06; - _.raw[6] = 0x80; - _setTemp(kWhirlpoolAcAutoTemp); // Default to a sane value. -} - -/// Set up hardware to be able to send a message. -void IRWhirlpoolAc::begin(void) { _irsend.begin(); } - -/// 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 IRWhirlpoolAc::validChecksum(const uint8_t state[], - const uint16_t length) { - if (length > kWhirlpoolAcChecksumByte1 && - state[kWhirlpoolAcChecksumByte1] != - xorBytes(state + 2, kWhirlpoolAcChecksumByte1 - 1 - 2)) { - DPRINTLN("DEBUG: First Whirlpool AC checksum failed."); - return false; - } - if (length > kWhirlpoolAcChecksumByte2 && - state[kWhirlpoolAcChecksumByte2] != - xorBytes(state + kWhirlpoolAcChecksumByte1 + 1, - kWhirlpoolAcChecksumByte2 - kWhirlpoolAcChecksumByte1 - 1)) { - DPRINTLN("DEBUG: Second Whirlpool AC checksum failed."); - return false; - } - // State is too short to have a checksum or everything checked out. - return true; -} - -/// Calculate & set the checksum for the current internal state of the remote. -/// @param[in] length The length/size of the internal state array. -void IRWhirlpoolAc::checksum(uint16_t length) { - if (length >= kWhirlpoolAcChecksumByte1) - _.Sum1 = xorBytes(_.raw + 2, kWhirlpoolAcChecksumByte1 - 1 - 2); - if (length >= kWhirlpoolAcChecksumByte2) - _.Sum2 = xorBytes(_.raw + kWhirlpoolAcChecksumByte1 + 1, - kWhirlpoolAcChecksumByte2 - kWhirlpoolAcChecksumByte1 - 1); -} - -#if SEND_WHIRLPOOL_AC -/// Send the current internal state as an IR message. -/// @param[in] repeat Nr. of times the message will be repeated. -/// @param[in] calcchecksum Do we need to calculate the checksum?. -void IRWhirlpoolAc::send(const uint16_t repeat, const bool calcchecksum) { - _irsend.sendWhirlpoolAC(getRaw(calcchecksum), kWhirlpoolAcStateLength, - repeat); -} -#endif // SEND_WHIRLPOOL_AC - -/// Get a copy of the internal state/code for this protocol. -/// @param[in] calcchecksum Do we need to calculate the checksum?. -/// @return A code for this protocol based on the current internal state. -uint8_t *IRWhirlpoolAc::getRaw(const bool calcchecksum) { - if (calcchecksum) 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 IRWhirlpoolAc::setRaw(const uint8_t new_code[], const uint16_t length) { - std::memcpy(_.raw, new_code, std::min(length, kWhirlpoolAcStateLength)); -} - -/// Get/Detect the model of the A/C. -/// @return The enum of the compatible model. -whirlpool_ac_remote_model_t IRWhirlpoolAc::getModel(void) const { - if (_.J191) - return DG11J191; - else - return DG11J13A; -} - -/// Set the model of the A/C to emulate. -/// @param[in] model The enum of the appropriate model. -void IRWhirlpoolAc::setModel(const whirlpool_ac_remote_model_t model) { - switch (model) { - case DG11J191: - _.J191 = true; - break; - case DG11J13A: - // FALL THRU - default: - _.J191 = false; - } - _setTemp(_desiredtemp); // Different models have different temp values. -} - -/// Calculate the temp. offset in deg C for the current model. -/// @return The temperature offset. -int8_t IRWhirlpoolAc::getTempOffset(void) const { - switch (getModel()) { - case whirlpool_ac_remote_model_t::DG11J191: return -2; - default: return 0; - } -} - -/// Set the temperature. -/// @param[in] temp The temperature in degrees celsius. -/// @param[in] remember Do we save this temperature? -/// @note Internal use only. -void IRWhirlpoolAc::_setTemp(const uint8_t temp, const bool remember) { - if (remember) _desiredtemp = temp; - int8_t offset = getTempOffset(); // Cache the min temp for the model. - uint8_t newtemp = std::max((uint8_t)(kWhirlpoolAcMinTemp + offset), temp); - newtemp = std::min((uint8_t)(kWhirlpoolAcMaxTemp + offset), newtemp); - _.Temp = newtemp - (kWhirlpoolAcMinTemp + offset); -} - -/// Set the temperature. -/// @param[in] temp The temperature in degrees celsius. -void IRWhirlpoolAc::setTemp(const uint8_t temp) { - _setTemp(temp); - setSuper(false); // Changing temp cancels Super/Jet mode. - _.Cmd = kWhirlpoolAcCommandTemp; -} - -/// Get the current temperature setting. -/// @return The current setting for temp. in degrees celsius. -uint8_t IRWhirlpoolAc::getTemp(void) const { - return _.Temp + kWhirlpoolAcMinTemp + getTempOffset(); -} - -/// Set the operating mode of the A/C. -/// @param[in] mode The desired operating mode. -/// @note Internal use only. -void IRWhirlpoolAc::_setMode(const uint8_t mode) { - switch (mode) { - case kWhirlpoolAcAuto: - setFan(kWhirlpoolAcFanAuto); - _setTemp(kWhirlpoolAcAutoTemp, false); - setSleep(false); // Cancel sleep mode when in auto/6thsense mode. - // FALL THRU - case kWhirlpoolAcHeat: - case kWhirlpoolAcCool: - case kWhirlpoolAcDry: - case kWhirlpoolAcFan: - _.Mode = mode; - _.Cmd = kWhirlpoolAcCommandMode; - break; - default: - return; - } - if (mode == kWhirlpoolAcAuto) _.Cmd = kWhirlpoolAcCommand6thSense; -} - -/// Set the operating mode of the A/C. -/// @param[in] mode The desired operating mode. -void IRWhirlpoolAc::setMode(const uint8_t mode) { - setSuper(false); // Changing mode cancels Super/Jet mode. - _setMode(mode); -} - -/// Get the operating mode setting of the A/C. -/// @return The current operating mode setting. -uint8_t IRWhirlpoolAc::getMode(void) const { - return _.Mode; -} - -/// Set the speed of the fan. -/// @param[in] speed The desired setting. -void IRWhirlpoolAc::setFan(const uint8_t speed) { - switch (speed) { - case kWhirlpoolAcFanAuto: - case kWhirlpoolAcFanLow: - case kWhirlpoolAcFanMedium: - case kWhirlpoolAcFanHigh: - _.Fan = speed; - setSuper(false); // Changing fan speed cancels Super/Jet mode. - _.Cmd = kWhirlpoolAcCommandFanSpeed; - break; - } -} - -/// Get the current fan speed setting. -/// @return The current fan speed/mode. -uint8_t IRWhirlpoolAc::getFan(void) const { - return _.Fan; -} - -/// Set the (vertical) swing setting of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRWhirlpoolAc::setSwing(const bool on) { - _.Swing1 = on; - _.Swing2 = on; - _.Cmd = kWhirlpoolAcCommandSwing; -} - -/// Get the (vertical) swing setting of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRWhirlpoolAc::getSwing(void) const { - return _.Swing1 && _.Swing2; -} - -/// Set the Light (Display/LED) setting of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRWhirlpoolAc::setLight(const bool on) { - // Cleared when on. - _.LightOff = !on; -} - -/// Get the Light (Display/LED) setting of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRWhirlpoolAc::getLight(void) const { - return !_.LightOff; -} - -/// Set the clock time in nr. of minutes past midnight. -/// @param[in] minspastmidnight The time expressed as minutes past midnight. -void IRWhirlpoolAc::setClock(const uint16_t minspastmidnight) { - SETTIME(Clock, minspastmidnight); -} - -/// Get the clock time in nr. of minutes past midnight. -/// @return The time expressed as the Nr. of minutes past midnight. -uint16_t IRWhirlpoolAc::getClock(void) const { - return GETTIME(Clock); -} - -/// Set the Off Timer time. -/// @param[in] minspastmidnight The time expressed as minutes past midnight. -void IRWhirlpoolAc::setOffTimer(const uint16_t minspastmidnight) { - SETTIME(Off, minspastmidnight); -} - -/// Get the Off Timer time.. -/// @return The time expressed as the Nr. of minutes past midnight. -uint16_t IRWhirlpoolAc::getOffTimer(void) const { - return GETTIME(Off); -} - -/// Is the Off timer enabled? -/// @return true, the Timer is enabled. false, the Timer is disabled. -bool IRWhirlpoolAc::isOffTimerEnabled(void) const { - return _.OffTimerEnabled; -} - -/// Enable the Off Timer. -/// @param[in] on true, the timer is enabled. false, the timer is disabled. -void IRWhirlpoolAc::enableOffTimer(const bool on) { - _.OffTimerEnabled = on; - _.Cmd = kWhirlpoolAcCommandOffTimer; -} - -/// Set the On Timer time. -/// @param[in] minspastmidnight The time expressed as minutes past midnight. -void IRWhirlpoolAc::setOnTimer(const uint16_t minspastmidnight) { - SETTIME(On, minspastmidnight); -} - -/// Get the On Timer time.. -/// @return The time expressed as the Nr. of minutes past midnight. -uint16_t IRWhirlpoolAc::getOnTimer(void) const { - return GETTIME(On); -} - -/// Is the On timer enabled? -/// @return true, the Timer is enabled. false, the Timer is disabled. -bool IRWhirlpoolAc::isOnTimerEnabled(void) const { - return _.OnTimerEnabled; -} - -/// Enable the On Timer. -/// @param[in] on true, the timer is enabled. false, the timer is disabled. -void IRWhirlpoolAc::enableOnTimer(const bool on) { - _.OnTimerEnabled = on; - _.Cmd = kWhirlpoolAcCommandOnTimer; -} - -/// Change the power toggle setting. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRWhirlpoolAc::setPowerToggle(const bool on) { - _.Power = on; - setSuper(false); // Changing power cancels Super/Jet mode. - _.Cmd = kWhirlpoolAcCommandPower; -} - -/// Get the value of the current power toggle setting. -/// @return true, the setting is on. false, the setting is off. -bool IRWhirlpoolAc::getPowerToggle(void) const { - return _.Power; -} - -/// Get the Command (Button) setting of the A/C. -/// @return The current Command (Button) of the A/C. -uint8_t IRWhirlpoolAc::getCommand(void) const { - return _.Cmd; -} - -/// Set the Sleep setting of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRWhirlpoolAc::setSleep(const bool on) { - _.Sleep = on; - if (on) setFan(kWhirlpoolAcFanLow); - _.Cmd = kWhirlpoolAcCommandSleep; -} - -/// Get the Sleep setting of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRWhirlpoolAc::getSleep(void) const { - return _.Sleep; -} - -/// Set the Super (Turbo/Jet) setting of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRWhirlpoolAc::setSuper(const bool on) { - if (on) { - setFan(kWhirlpoolAcFanHigh); - switch (_.Mode) { - case kWhirlpoolAcHeat: - setTemp(kWhirlpoolAcMaxTemp + getTempOffset()); - break; - case kWhirlpoolAcCool: - default: - setTemp(kWhirlpoolAcMinTemp + getTempOffset()); - setMode(kWhirlpoolAcCool); - break; - } - _.Super1 = 1; - _.Super2 = 1; - } else { - _.Super1 = 0; - _.Super2 = 0; - } - _.Cmd = kWhirlpoolAcCommandSuper; -} - -/// Get the Super (Turbo/Jet) setting of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRWhirlpoolAc:: getSuper(void) const { - return _.Super1 && _.Super2; -} - -/// Set the Command (Button) setting of the A/C. -/// @param[in] code The current Command (Button) of the A/C. -void IRWhirlpoolAc::setCommand(const uint8_t code) { - _.Cmd = code; -} - -/// 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 IRWhirlpoolAc::convertMode(const stdAc::opmode_t mode) { - switch (mode) { - case stdAc::opmode_t::kAuto: return kWhirlpoolAcAuto; - case stdAc::opmode_t::kHeat: return kWhirlpoolAcHeat; - case stdAc::opmode_t::kDry: return kWhirlpoolAcDry; - case stdAc::opmode_t::kFan: return kWhirlpoolAcFan; - // Default to Cool as some Whirlpool models don't have an Auto mode. - // Ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/1283 - default: return kWhirlpoolAcCool; - } -} - -/// 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 IRWhirlpoolAc::convertFan(const stdAc::fanspeed_t speed) { - switch (speed) { - case stdAc::fanspeed_t::kMin: - case stdAc::fanspeed_t::kLow: return kWhirlpoolAcFanLow; - case stdAc::fanspeed_t::kMedium: return kWhirlpoolAcFanMedium; - case stdAc::fanspeed_t::kHigh: - case stdAc::fanspeed_t::kMax: return kWhirlpoolAcFanHigh; - default: return kWhirlpoolAcFanAuto; - } -} - -/// 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 IRWhirlpoolAc::toCommonMode(const uint8_t mode) { - switch (mode) { - case kWhirlpoolAcCool: return stdAc::opmode_t::kCool; - case kWhirlpoolAcHeat: return stdAc::opmode_t::kHeat; - case kWhirlpoolAcDry: return stdAc::opmode_t::kDry; - case kWhirlpoolAcFan: 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 IRWhirlpoolAc::toCommonFanSpeed(const uint8_t speed) { - switch (speed) { - case kWhirlpoolAcFanHigh: return stdAc::fanspeed_t::kMax; - case kWhirlpoolAcFanMedium: return stdAc::fanspeed_t::kMedium; - case kWhirlpoolAcFanLow: 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 Ptr to the previous state if required. -/// @return The stdAc equivalent of the native settings. -stdAc::state_t IRWhirlpoolAc::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::WHIRLPOOL_AC; - result.model = getModel(); - if (_.Power) result.power = !result.power; - result.mode = toCommonMode(_.Mode); - result.celsius = true; - result.degrees = getTemp(); - result.fanspeed = toCommonFanSpeed(_.Fan); - result.swingv = getSwing() ? stdAc::swingv_t::kAuto : stdAc::swingv_t::kOff; - result.turbo = getSuper(); - result.light = getLight(); - result.sleep = _.Sleep ? 0 : -1; - // Not supported. - result.swingh = stdAc::swingh_t::kOff; - result.quiet = false; - result.filter = false; - result.econo = 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 IRWhirlpoolAc::toString(void) const { - String result = ""; - result.reserve(200); // Reserve some heap for the string to reduce fragging. - result += addModelToString(decode_type_t::WHIRLPOOL_AC, getModel(), false); - result += addBoolToString(_.Power, kPowerToggleStr); - result += addModeToString(_.Mode, kWhirlpoolAcAuto, kWhirlpoolAcCool, - kWhirlpoolAcHeat, kWhirlpoolAcDry, kWhirlpoolAcFan); - result += addTempToString(getTemp()); - result += addFanToString(_.Fan, kWhirlpoolAcFanHigh, kWhirlpoolAcFanLow, - kWhirlpoolAcFanAuto, kWhirlpoolAcFanAuto, - kWhirlpoolAcFanMedium); - result += addBoolToString(getSwing(), kSwingStr); - result += addBoolToString(getLight(), kLightStr); - result += addLabeledString(minsToString(getClock()), kClockStr); - result += addLabeledString( - _.OnTimerEnabled ? minsToString(getOnTimer()) : kOffStr, kOnTimerStr); - result += addLabeledString( - _.OffTimerEnabled ? minsToString(getOffTimer()) : kOffStr, kOffTimerStr); - result += addBoolToString(_.Sleep, kSleepStr); - result += addBoolToString(getSuper(), kSuperStr); - result += addIntToString(_.Cmd, kCommandStr); - result += kSpaceLBraceStr; - switch (_.Cmd) { - case kWhirlpoolAcCommandLight: - result += kLightStr; - break; - case kWhirlpoolAcCommandPower: - result += kPowerStr; - break; - case kWhirlpoolAcCommandTemp: - result += kTempStr; - break; - case kWhirlpoolAcCommandSleep: - result += kSleepStr; - break; - case kWhirlpoolAcCommandSuper: - result += kSuperStr; - break; - case kWhirlpoolAcCommandOnTimer: - result += kOnTimerStr; - break; - case kWhirlpoolAcCommandMode: - result += kModeStr; - break; - case kWhirlpoolAcCommandSwing: - result += kSwingStr; - break; - case kWhirlpoolAcCommandIFeel: - result += kIFeelStr; - break; - case kWhirlpoolAcCommandFanSpeed: - result += kFanStr; - break; - case kWhirlpoolAcCommand6thSense: - result += k6thSenseStr; - break; - case kWhirlpoolAcCommandOffTimer: - result += kOffTimerStr; - break; - default: - result += kUnknownStr; - break; - } - result += ')'; - return result; -} - -#if DECODE_WHIRLPOOL_AC - -/// Decode the supplied Whirlpool A/C message. -/// Status: STABLE / Working as intended. -/// @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::decodeWhirlpoolAC(decode_results *results, uint16_t offset, - const uint16_t nbits, const bool strict) { - if (results->rawlen < 2 * nbits + 4 + kHeader + kFooter - 1 + offset) - return false; // Can't possibly be a valid Whirlpool A/C message. - if (strict) { - if (nbits != kWhirlpoolAcBits) return false; - } - - const uint8_t sectionSize[kWhirlpoolAcSections] = {6, 8, 7}; - - // Header - if (!matchMark(results->rawbuf[offset++], kWhirlpoolAcHdrMark)) return false; - if (!matchSpace(results->rawbuf[offset++], kWhirlpoolAcHdrSpace)) - return false; - - // Data Sections - uint16_t pos = 0; - for (uint8_t section = 0; section < kWhirlpoolAcSections; - section++) { - uint16_t used; - // Section Data - used = matchGeneric(results->rawbuf + offset, results->state + pos, - results->rawlen - offset, sectionSize[section] * 8, - 0, 0, - kWhirlpoolAcBitMark, kWhirlpoolAcOneSpace, - kWhirlpoolAcBitMark, kWhirlpoolAcZeroSpace, - kWhirlpoolAcBitMark, kWhirlpoolAcGap, - section >= kWhirlpoolAcSections - 1, - _tolerance, kMarkExcess, false); - if (used == 0) return false; - offset += used; - pos += sectionSize[section]; - } - - // Compliance - if (strict) { - // Re-check we got the correct size/length due to the way we read the data. - if (pos * 8 != nbits) return false; - if (!IRWhirlpoolAc::validChecksum(results->state, nbits / 8)) - return false; - } - - // Success - results->decode_type = WHIRLPOOL_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 // WHIRLPOOL_AC diff --git a/lib/IRremoteESP8266/src/ir_Whirlpool.h b/lib/IRremoteESP8266/src/ir_Whirlpool.h index ac73be926b..87e7105068 100644 --- a/lib/IRremoteESP8266/src/ir_Whirlpool.h +++ b/lib/IRremoteESP8266/src/ir_Whirlpool.h @@ -26,10 +26,10 @@ #ifndef UNIT_TEST #include #endif -#include "IRremoteESP8266.h" -#include "IRsend.h" +#include "../src/IRremoteESP8266.h" +#include "../src/IRsend.h" #ifdef UNIT_TEST -#include "IRsend_test.h" +#include "../src/IRsend_test.h" #endif /// Native representation of a Whirlpool A/C message. diff --git a/lib/IRremoteESP8266/src/ir_Whynter.cpp b/lib/IRremoteESP8266/src/ir_Whynter.cpp deleted file mode 100644 index 7b184eb0be..0000000000 --- a/lib/IRremoteESP8266/src/ir_Whynter.cpp +++ /dev/null @@ -1,103 +0,0 @@ -// Copyright 2009 Ken Shirriff -// Copyright 2017 David Conran - -/// @file -/// @brief Support for Whynter protocols. -/// Whynter A/C ARC-110WD added by Francesco Meschia -/// Whynter originally added from https://github.com/shirriff/Arduino-IRremote/ - -// Supports: -// Brand: Whynter, Model: ARC-110WD A/C - -#include -#include "IRrecv.h" -#include "IRsend.h" -#include "IRutils.h" - -// Constants -const uint16_t kWhynterTick = 50; -const uint16_t kWhynterHdrMarkTicks = 57; -const uint16_t kWhynterHdrMark = kWhynterHdrMarkTicks * kWhynterTick; -const uint16_t kWhynterHdrSpaceTicks = 57; -const uint16_t kWhynterHdrSpace = kWhynterHdrSpaceTicks * kWhynterTick; -const uint16_t kWhynterBitMarkTicks = 15; -const uint16_t kWhynterBitMark = kWhynterBitMarkTicks * kWhynterTick; -const uint16_t kWhynterOneSpaceTicks = 43; -const uint16_t kWhynterOneSpace = kWhynterOneSpaceTicks * kWhynterTick; -const uint16_t kWhynterZeroSpaceTicks = 15; -const uint16_t kWhynterZeroSpace = kWhynterZeroSpaceTicks * kWhynterTick; -const uint16_t kWhynterMinCommandLengthTicks = 2160; // Totally made up value. -const uint32_t kWhynterMinCommandLength = - kWhynterMinCommandLengthTicks * kWhynterTick; -const uint16_t kWhynterMinGapTicks = - kWhynterMinCommandLengthTicks - - (2 * (kWhynterBitMarkTicks + kWhynterZeroSpaceTicks) + - kWhynterBits * (kWhynterBitMarkTicks + kWhynterOneSpaceTicks)); -const uint16_t kWhynterMinGap = kWhynterMinGapTicks * kWhynterTick; - -#if SEND_WHYNTER -/// Send a Whynter message. -/// Status: STABLE -/// @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_Whynter.cpp -void IRsend::sendWhynter(const uint64_t data, const uint16_t nbits, - const uint16_t repeat) { - // Set IR carrier frequency - enableIROut(38); - - for (uint16_t i = 0; i <= repeat; i++) { - // (Pre-)Header - mark(kWhynterBitMark); - space(kWhynterZeroSpace); - sendGeneric( - kWhynterHdrMark, kWhynterHdrSpace, kWhynterBitMark, kWhynterOneSpace, - kWhynterBitMark, kWhynterZeroSpace, kWhynterBitMark, kWhynterMinGap, - kWhynterMinCommandLength - (kWhynterBitMark + kWhynterZeroSpace), data, - nbits, 38, true, 0, // Repeats are already handled. - 50); - } -} -#endif // SEND_WHYNTER - -#if DECODE_WHYNTER -/// Decode the supplied Whynter message. -/// Status: STABLE / Working. Strict mode is ALPHA. -/// @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/z3t0/Arduino-IRremote/blob/master/ir_Whynter.cpp -bool IRrecv::decodeWhynter(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; // We don't have enough entries to possibly match. - - // Compliance - if (strict && nbits != kWhynterBits) - return false; // Incorrect nr. of bits per spec. - - uint64_t data = 0; - // Pre-Header - // Sequence begins with a bit mark and a zero space. - if (!matchMark(results->rawbuf[offset++], kWhynterBitMark)) return false; - if (!matchSpace(results->rawbuf[offset++], kWhynterZeroSpace)) return false; - // Match Main Header + Data + Footer - if (!matchGeneric(results->rawbuf + offset, &data, - results->rawlen - offset, nbits, - kWhynterHdrMark, kWhynterHdrSpace, - kWhynterBitMark, kWhynterOneSpace, - kWhynterBitMark, kWhynterZeroSpace, - kWhynterBitMark, kWhynterMinGap, true)) return false; - // Success - results->decode_type = WHYNTER; - results->bits = nbits; - results->value = data; - results->address = 0; - results->command = 0; - return true; -} -#endif // DECODE_WHYNTER diff --git a/lib/IRremoteESP8266/src/ir_Xmp.cpp b/lib/IRremoteESP8266/src/ir_Xmp.cpp deleted file mode 100644 index 29d7f6c1bb..0000000000 --- a/lib/IRremoteESP8266/src/ir_Xmp.cpp +++ /dev/null @@ -1,226 +0,0 @@ -// Copyright 2021 David Conran - -/// @file -/// @brief Support for XMP protocols. -/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1414 -/// @see http://www.hifi-remote.com/wiki/index.php/XMP - -// Supports: -// Brand: Xfinity, Model: XR2 remote -// Brand: Xfinity, Model: XR11 remote - - -#include -#include "IRrecv.h" -#include "IRsend.h" -#include "IRutils.h" - -// Constants -const uint16_t kXmpMark = 210; ///< uSeconds. -const uint16_t kXmpBaseSpace = 760; ///< uSeconds -const uint16_t kXmpSpaceStep = 135; ///< uSeconds -const uint16_t kXmpFooterSpace = 13000; ///< uSeconds. -const uint32_t kXmpMessageGap = 80400; ///< uSeconds. -const uint8_t kXmpWordSize = kNibbleSize; ///< nr. of Bits in a word. -const uint8_t kXmpMaxWordValue = (1 << kXmpWordSize) - 1; // Max word value. -const uint8_t kXmpSections = 2; ///< Nr. of Data sections -const uint8_t kXmpRepeatCode = 0b1000; -const uint8_t kXmpRepeatCodeAlt = 0b1001; - -using irutils::setBits; - -namespace IRXmpUtils { - /// Get the current checksum value from an XMP data section. - /// @param[in] data The value of the data section. - /// @param[in] nbits The number of data bits in the section. - /// @return The value of the stored checksum. - /// @warning Returns 0 if we can't obtain a valid checksum. - uint8_t getSectionChecksum(const uint32_t data, const uint16_t nbits) { - // The checksum is the 2nd most significant nibble of a section. - return (nbits < 2 * kNibbleSize) ? 0 : GETBITS32(data, - nbits - (2 * kNibbleSize), - kNibbleSize); - } - - /// Calculate the correct checksum value for an XMP data section. - /// @param[in] data The value of the data section. - /// @param[in] nbits The number of data bits in the section. - /// @return The value of the correct checksum. - uint8_t calcSectionChecksum(const uint32_t data, const uint16_t nbits) { - return (0xF & ~(irutils::sumNibbles(data, nbits / kNibbleSize, 0xF, false) - - getSectionChecksum(data, nbits))); - } - - /// Recalculate a XMP message code ensuring it has the checksums valid. - /// @param[in] data The value of the XMP message code. - /// @param[in] nbits The number of data bits in the entire message code. - /// @return The corrected XMP message with valid checksum sections. - uint64_t updateChecksums(const uint64_t data, const uint16_t nbits) { - const uint16_t sectionbits = nbits / kXmpSections; - uint64_t result = data; - for (uint16_t sectionOffset = 0; sectionOffset < nbits; - sectionOffset += sectionbits) { - const uint16_t checksumOffset = sectionOffset + sectionbits - - (2 * kNibbleSize); - setBits(&result, checksumOffset, kNibbleSize, - calcSectionChecksum(GETBITS64(data, sectionOffset, sectionbits), - sectionbits)); - } - return result; - } - - /// Calculate the bit offset the repeat nibble in an XMP code. - /// @param[in] nbits The number of data bits in the entire message code. - /// @return The offset to the start of the XMP repeat nibble. - uint16_t calcRepeatOffset(const uint16_t nbits) { - return (nbits < 3 * kNibbleSize) ? 0 - : (nbits / kXmpSections) - - (3 * kNibbleSize); - } - - /// Test if an XMP message code is a repeat or not. - /// @param[in] data The value of the XMP message code. - /// @param[in] nbits The number of data bits in the entire message code. - /// @return true, if it looks like a repeat, false if not. - bool isRepeat(const uint64_t data, const uint16_t nbits) { - switch (GETBITS64(data, calcRepeatOffset(nbits), kNibbleSize)) { - case kXmpRepeatCode: - case kXmpRepeatCodeAlt: - return true; - default: - return false; - } - } - - /// Adjust an XMP message code to make it a valid repeat or non-repeat code. - /// @param[in] data The value of the XMP message code. - /// @param[in] nbits The number of data bits in the entire message code. - /// @param[in] repeat_code The value of the XMP repeat nibble to use. - /// A value of `8` is the normal value for a repeat. `9` has also been seen. - /// A value of `0` will convert the code to a non-repeat code. - /// @return The valud of the modified XMP code. - uint64_t adjustRepeat(const uint64_t data, const uint16_t nbits, - const uint8_t repeat_code) { - uint64_t result = data; - setBits(&result, calcRepeatOffset(nbits), kNibbleSize, repeat_code); - return updateChecksums(result, nbits); - } -} // namespace IRXmpUtils - -using IRXmpUtils::calcSectionChecksum; -using IRXmpUtils::getSectionChecksum; -using IRXmpUtils::isRepeat; -using IRXmpUtils::adjustRepeat; - - -#if SEND_XMP -/// Send a XMP packet. -/// Status: Beta / Untested 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::sendXmp(const uint64_t data, const uint16_t nbits, - const uint16_t repeat) { - enableIROut(38000); - if (nbits < 2 * kXmpWordSize) return; // Too small to send, abort! - uint64_t send_data = data; - for (uint16_t r = 0; r <= repeat; r++) { - uint16_t bits_so_far = kXmpWordSize; - for (uint64_t mask = ((uint64_t)kXmpMaxWordValue) << (nbits - kXmpWordSize); - mask; - mask >>= kXmpWordSize) { - uint8_t word = (send_data & mask) >> (nbits - bits_so_far); - mark(kXmpMark); - space(kXmpBaseSpace + word * kXmpSpaceStep); - bits_so_far += kXmpWordSize; - // Are we at a data section boundary? - if ((bits_so_far - kXmpWordSize) % (nbits / kXmpSections) == 0) { // Yes. - mark(kXmpMark); - space(kXmpFooterSpace); - } - } - space(kXmpMessageGap - kXmpFooterSpace); - - // Modify the value if needed, to make it into a valid repeat code. - if (!isRepeat(send_data, nbits)) - send_data = adjustRepeat(send_data, nbits, kXmpRepeatCode); - } -} -#endif // SEND_XMP - -#if DECODE_XMP -/// Decode the supplied XMP packet/message. -/// Status: BETA / Probably works. -/// @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::decodeXmp(decode_results *results, uint16_t offset, - const uint16_t nbits, const bool strict) { - uint64_t data = 0; - - if (results->rawlen < 2 * (nbits / kXmpWordSize) + (kXmpSections * kFooter) + - offset - 1) - return false; // Not enough entries to ever be XMP. - - // Compliance - if (strict && nbits != kXmpBits) return false; - - // Data - // Sections - for (uint8_t section = 1; section <= kXmpSections; section++) { - for (uint16_t bits_so_far = 0; bits_so_far < nbits / kXmpSections; - bits_so_far += kXmpWordSize) { - if (!matchMarkRange(results->rawbuf[offset++], kXmpMark)) return 0; - uint8_t value = 0; - bool found = false; - for (; value <= kXmpMaxWordValue; value++) { - if (matchSpaceRange(results->rawbuf[offset], - kXmpBaseSpace + value * kXmpSpaceStep, - kXmpSpaceStep / 2, 0)) { - found = true; - break; - } - } - if (!found) return 0; // Failure. - data <<= kXmpWordSize; - data += value; - offset++; - } - // Section Footer - if (!matchMarkRange(results->rawbuf[offset++], kXmpMark)) return 0; - if (section < kXmpSections) { - if (!matchSpace(results->rawbuf[offset++], kXmpFooterSpace)) return 0; - } else { // Last section - if (offset < results->rawlen && - !matchAtLeast(results->rawbuf[offset++], kXmpFooterSpace)) return 0; - } - } - - // Compliance - if (strict) { - // Validate checksums. - uint64_t checksum_data = data; - const uint16_t section_size = nbits / kXmpSections; - // Each section has a checksum. - for (uint16_t section = 0; section < kXmpSections; section++) { - if (getSectionChecksum(checksum_data, section_size) != - calcSectionChecksum(checksum_data, section_size)) - return 0; - checksum_data >>= section_size; - } - } - - // Success - results->value = data; - results->decode_type = decode_type_t::XMP; - results->bits = nbits; - results->address = 0; - results->command = 0; - // See if it is a repeat message. - results->repeat = isRepeat(data, nbits); - return true; -} -#endif // DECODE_XMP diff --git a/lib/IRremoteESP8266/src/ir_Zepeal.cpp b/lib/IRremoteESP8266/src/ir_Zepeal.cpp deleted file mode 100644 index 96017226c3..0000000000 --- a/lib/IRremoteESP8266/src/ir_Zepeal.cpp +++ /dev/null @@ -1,94 +0,0 @@ -// Copyright 2020 Christian Nilsson (nikize) - -/// @file -/// @brief Support for Zepeal protocol. -/// This protocol uses fixed length bit encoding. -/// Most official information about Zepeal seems to be from Denkyosha -/// @see https://www.denkyosha.co.jp/ - -// Supports: -// Brand: Zepeal, Model: DRT-A3311(BG) floor fan -// Brand: Zepeal, Model: DRT-A3311(BG) 5 button remote - -#include "IRrecv.h" -#include "IRsend.h" -#include "IRutils.h" - -// Constants - -const uint16_t kZepealHdrMark = 2330; -const uint16_t kZepealHdrSpace = 3380; -const uint16_t kZepealOneMark = 1300; -const uint16_t kZepealZeroMark = 420; -const uint16_t kZepealOneSpace = kZepealZeroMark; -const uint16_t kZepealZeroSpace = kZepealOneMark; -const uint16_t kZepealFooterMark = 420; -const uint16_t kZepealGap = 6750; - -const uint8_t kZepealTolerance = 40; - -// Signature limits possible false possitvies, -// but might need change (removal) if more devices are detected -const uint8_t kZepealSignature = 0x6C; - -// Known Zepeal DRT-A3311(BG) Buttons - documentation rather than actual usage -const uint16_t kZepealCommandSpeed = 0x6C82; -const uint16_t kZepealCommandOffOn = 0x6C81; -const uint16_t kZepealCommandRhythm = 0x6C84; -const uint16_t kZepealCommandOffTimer = 0x6C88; -const uint16_t kZepealCommandOnTimer = 0x6CC3; - -#if SEND_ZEPEAL -/// Send a Zepeal formatted message. -/// Status: STABLE / Works on 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::sendZepeal(const uint64_t data, const uint16_t nbits, - const uint16_t repeat) { - sendGeneric(kZepealHdrMark, kZepealHdrSpace, - kZepealOneMark, kZepealOneSpace, - kZepealZeroMark, kZepealZeroSpace, - kZepealFooterMark, kZepealGap, - data, nbits, 38, true, repeat, kDutyDefault); -} -#endif // SEND_ZEPEAL - -#if DECODE_ZEPEAL -/// Decode the supplied Zepeal 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. Typically kZepealBits. -/// @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::decodeZepeal(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 != kZepealBits) - return false; // Not strictly a message. - - uint64_t data = 0; - uint16_t used; - used = matchGeneric(results->rawbuf + offset, &data, - results->rawlen - offset, nbits, - kZepealHdrMark, kZepealHdrSpace, - kZepealOneMark, kZepealOneSpace, - kZepealZeroMark, kZepealZeroSpace, - kZepealFooterMark, kZepealGap, true, - kZepealTolerance); - if (!used) return false; - if (strict && (data >> 8) != kZepealSignature) return false; - - // Success - results->value = data; - results->decode_type = decode_type_t::ZEPEAL; - results->bits = nbits; - results->address = 0; - results->command = 0; - return true; -} -#endif // DECODE_ZEPEAL diff --git a/lib/IRremoteESP8266/test/IRrecv_test.cpp b/lib/IRremoteESP8266/test/IRrecv_test.cpp index 2d32847d38..aada6aa82b 100644 --- a/lib/IRremoteESP8266/test/IRrecv_test.cpp +++ b/lib/IRremoteESP8266/test/IRrecv_test.cpp @@ -1,10 +1,10 @@ // Copyright 2017 David Conran -#include "IRrecv_test.h" -#include "IRrecv.h" -#include "IRremoteESP8266.h" -#include "IRsend.h" -#include "IRsend_test.h" +#include "../src/IRrecv_test.h" +#include "../src/IRrecv.h" +#include "../src/IRremoteESP8266.h" +#include "../src/IRsend.h" +#include "../src/IRsend_test.h" #include "gtest/gtest.h" // Tests for the IRrecv object. diff --git a/lib/IRremoteESP8266/test/IRrecv_test.h b/lib/IRremoteESP8266/test/IRrecv_test.h index bb366c1ee0..e4e0e70954 100644 --- a/lib/IRremoteESP8266/test/IRrecv_test.h +++ b/lib/IRremoteESP8266/test/IRrecv_test.h @@ -6,7 +6,7 @@ #include #include #include -#include "IRutils.h" +#include "../src/IRutils.h" #define EXPECT_STATE_EQ(a, b, c) \ for (uint8_t i = 0; i < c / 8; ++i) { \ diff --git a/lib/IRremoteESP8266/test/IRsend_test.cpp b/lib/IRremoteESP8266/test/IRsend_test.cpp index 51fae74996..ffd230d2b1 100644 --- a/lib/IRremoteESP8266/test/IRsend_test.cpp +++ b/lib/IRremoteESP8266/test/IRsend_test.cpp @@ -1,9 +1,9 @@ // Copyright 2017,2019 David Conran -#include "IRsend_test.h" -#include "IRrecv_test.h" -#include "IRsend.h" -#include "IRutils.h" +#include "../src/IRsend_test.h" +#include "../src/IRrecv_test.h" +#include "../src/IRsend.h" +#include "../src/IRutils.h" #include "gtest/gtest.h" // Tests sendData(). diff --git a/lib/IRremoteESP8266/test/IRsend_test.h b/lib/IRremoteESP8266/test/IRsend_test.h index f434094332..ec900a44e9 100644 --- a/lib/IRremoteESP8266/test/IRsend_test.h +++ b/lib/IRremoteESP8266/test/IRsend_test.h @@ -8,9 +8,9 @@ #include #include #include -#include "IRrecv.h" -#include "IRsend.h" -#include "IRtimer.h" +#include "../src/IRrecv.h" +#include "../src/IRsend.h" +#include "../src/IRtimer.h" #define OUTPUT_BUF 10000U #define RAW_BUF 10000U diff --git a/lib/IRremoteESP8266/test/IRutils_test.cpp b/lib/IRremoteESP8266/test/IRutils_test.cpp index 73ba569991..8f398f7650 100644 --- a/lib/IRremoteESP8266/test/IRutils_test.cpp +++ b/lib/IRremoteESP8266/test/IRutils_test.cpp @@ -1,11 +1,11 @@ // Copyright 2017-2019 David Conran -#include "IRutils.h" +#include "../src/IRutils.h" #include -#include "IRrecv.h" -#include "IRrecv_test.h" -#include "IRsend.h" -#include "IRsend_test.h" +#include "../src/IRrecv.h" +#include "../src/IRrecv_test.h" +#include "../src/IRsend.h" +#include "../src/IRsend_test.h" #include "gtest/gtest.h" // Tests reverseBits(). diff --git a/lib/IRremoteESP8266/test/ir_Airwell_test.cpp b/lib/IRremoteESP8266/test/ir_Airwell_test.cpp index e5f28d4df4..c9d14f36dd 100644 --- a/lib/IRremoteESP8266/test/ir_Airwell_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Airwell_test.cpp @@ -1,11 +1,11 @@ // Copyright 2020 David Conran -#include "ir_Airwell.h" -#include "IRac.h" -#include "IRrecv.h" -#include "IRrecv_test.h" -#include "IRsend.h" -#include "IRsend_test.h" +#include "../src/ir_Airwell.h" +#include "../src/IRac.h" +#include "../src/IRrecv.h" +#include "../src/IRrecv_test.h" +#include "../src/IRsend.h" +#include "../src/IRsend_test.h" #include "gtest/gtest.h" // Tests for decodeAirwell(). diff --git a/lib/IRremoteESP8266/test/ir_Aiwa_test.cpp b/lib/IRremoteESP8266/test/ir_Aiwa_test.cpp index f87d5c5e28..78e3e33b03 100644 --- a/lib/IRremoteESP8266/test/ir_Aiwa_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Aiwa_test.cpp @@ -1,7 +1,7 @@ // Copyright 2017 David Conran -#include "IRsend.h" -#include "IRsend_test.h" +#include "../src/IRsend.h" +#include "../src/IRsend_test.h" #include "gtest/gtest.h" // Tests for sendAiwaRCT501(). diff --git a/lib/IRremoteESP8266/test/ir_Amcor_test.cpp b/lib/IRremoteESP8266/test/ir_Amcor_test.cpp index 787769516d..e8ba1eb3bc 100644 --- a/lib/IRremoteESP8266/test/ir_Amcor_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Amcor_test.cpp @@ -1,12 +1,12 @@ // Copyright 2019 David Conran -#include "IRac.h" -#include "ir_Amcor.h" -#include "IRrecv.h" -#include "IRrecv_test.h" -#include "IRsend.h" -#include "IRsend_test.h" -#include "IRutils.h" +#include "../src/IRac.h" +#include "../src/ir_Amcor.h" +#include "../src/IRrecv.h" +#include "../src/IRrecv_test.h" +#include "../src/IRsend.h" +#include "../src/IRsend_test.h" +#include "../src/IRutils.h" #include "gtest/gtest.h" TEST(TestUtils, Housekeeping) { diff --git a/lib/IRremoteESP8266/test/ir_Argo_test.cpp b/lib/IRremoteESP8266/test/ir_Argo_test.cpp index 3ab890adca..ea8e16ab7d 100644 --- a/lib/IRremoteESP8266/test/ir_Argo_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Argo_test.cpp @@ -1,10 +1,10 @@ // Copyright 2019 David Conran -#include "ir_Argo.h" -#include "IRac.h" -#include "IRrecv.h" -#include "IRrecv_test.h" -#include "IRsend.h" -#include "IRsend_test.h" +#include "../src/ir_Argo.h" +#include "../src/IRac.h" +#include "../src/IRrecv.h" +#include "../src/IRrecv_test.h" +#include "../src/IRsend.h" +#include "../src/IRsend_test.h" #include "gtest/gtest.h" diff --git a/lib/IRremoteESP8266/test/ir_Arris_test.cpp b/lib/IRremoteESP8266/test/ir_Arris_test.cpp index 2a219b6b22..ab30f3046c 100644 --- a/lib/IRremoteESP8266/test/ir_Arris_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Arris_test.cpp @@ -1,10 +1,10 @@ // Copyright 2021 David Conran -#include "IRac.h" -#include "IRrecv.h" -#include "IRrecv_test.h" -#include "IRsend.h" -#include "IRsend_test.h" +#include "../src/IRac.h" +#include "../src/IRrecv.h" +#include "../src/IRrecv_test.h" +#include "../src/IRsend.h" +#include "../src/IRsend_test.h" #include "gtest/gtest.h" // Tests for decodeArris(). diff --git a/lib/IRremoteESP8266/test/ir_Bose_test.cpp b/lib/IRremoteESP8266/test/ir_Bose_test.cpp index bf3e18c910..80b3204fe8 100644 --- a/lib/IRremoteESP8266/test/ir_Bose_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Bose_test.cpp @@ -1,11 +1,11 @@ // Copyright 2021 parsnip42 // Copyright 2021 David Conran -#include "IRac.h" -#include "IRrecv.h" -#include "IRrecv_test.h" -#include "IRsend.h" -#include "IRsend_test.h" +#include "../src/IRac.h" +#include "../src/IRrecv.h" +#include "../src/IRrecv_test.h" +#include "../src/IRsend.h" +#include "../src/IRsend_test.h" #include "gtest/gtest.h" diff --git a/lib/IRremoteESP8266/test/ir_Carrier_test.cpp b/lib/IRremoteESP8266/test/ir_Carrier_test.cpp index caf7e46d84..10cc435aab 100644 --- a/lib/IRremoteESP8266/test/ir_Carrier_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Carrier_test.cpp @@ -1,10 +1,10 @@ // Copyright 2018, 2020 David Conran -#include "ir_Carrier.h" -#include "IRac.h" -#include "IRrecv.h" -#include "IRsend.h" -#include "IRsend_test.h" +#include "../src/ir_Carrier.h" +#include "../src/IRac.h" +#include "../src/IRrecv.h" +#include "../src/IRsend.h" +#include "../src/IRsend_test.h" #include "gtest/gtest.h" // Tests for sendCarrierAC() diff --git a/lib/IRremoteESP8266/test/ir_Coolix_test.cpp b/lib/IRremoteESP8266/test/ir_Coolix_test.cpp index 62e0d6b0e4..569f670a51 100644 --- a/lib/IRremoteESP8266/test/ir_Coolix_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Coolix_test.cpp @@ -1,9 +1,9 @@ // Copyright 2017-2018 David Conran -#include "ir_Coolix.h" -#include "IRac.h" -#include "IRsend.h" -#include "IRsend_test.h" +#include "../src/ir_Coolix.h" +#include "../src/IRac.h" +#include "../src/IRsend.h" +#include "../src/IRsend_test.h" #include "gtest/gtest.h" diff --git a/lib/IRremoteESP8266/test/ir_Corona_test.cpp b/lib/IRremoteESP8266/test/ir_Corona_test.cpp index 9c16e1f963..72c7758ea0 100644 --- a/lib/IRremoteESP8266/test/ir_Corona_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Corona_test.cpp @@ -1,11 +1,11 @@ // Copyright 2020 Christian Nilsson -#include "ir_Corona.h" -#include "IRac.h" -#include "IRrecv.h" -#include "IRrecv_test.h" -#include "IRsend.h" -#include "IRsend_test.h" +#include "../src/ir_Corona.h" +#include "../src/IRac.h" +#include "../src/IRrecv.h" +#include "../src/IRrecv_test.h" +#include "../src/IRsend.h" +#include "../src/IRsend_test.h" #include "gtest/gtest.h" // Tests for decodeCoronaAc(). diff --git a/lib/IRremoteESP8266/test/ir_Daikin_test.cpp b/lib/IRremoteESP8266/test/ir_Daikin_test.cpp index 783bd4e66d..eadb508ccd 100644 --- a/lib/IRremoteESP8266/test/ir_Daikin_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Daikin_test.cpp @@ -1,10 +1,10 @@ // Copyright 2017-2019 David Conran -#include "ir_Daikin.h" -#include "IRac.h" -#include "IRrecv.h" -#include "IRrecv_test.h" -#include "IRsend.h" -#include "IRsend_test.h" +#include "../src/ir_Daikin.h" +#include "../src/IRac.h" +#include "../src/IRrecv.h" +#include "../src/IRrecv_test.h" +#include "../src/IRsend.h" +#include "../src/IRsend_test.h" #include "gtest/gtest.h" // Tests for sendDaikin(). diff --git a/lib/IRremoteESP8266/test/ir_Delonghi_test.cpp b/lib/IRremoteESP8266/test/ir_Delonghi_test.cpp index 9f069e40ec..fd5a888248 100644 --- a/lib/IRremoteESP8266/test/ir_Delonghi_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Delonghi_test.cpp @@ -1,12 +1,12 @@ // Copyright 2020 David Conran -#include "IRac.h" -#include "ir_Delonghi.h" -#include "IRrecv.h" -#include "IRrecv_test.h" -#include "IRsend.h" -#include "IRsend_test.h" -#include "IRutils.h" +#include "../src/IRac.h" +#include "../src/ir_Delonghi.h" +#include "../src/IRrecv.h" +#include "../src/IRrecv_test.h" +#include "../src/IRsend.h" +#include "../src/IRsend_test.h" +#include "../src/IRutils.h" #include "gtest/gtest.h" TEST(TestUtils, Housekeeping) { diff --git a/lib/IRremoteESP8266/test/ir_Denon_test.cpp b/lib/IRremoteESP8266/test/ir_Denon_test.cpp index 64be43f97e..0f2a4fded8 100644 --- a/lib/IRremoteESP8266/test/ir_Denon_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Denon_test.cpp @@ -1,7 +1,7 @@ // Copyright 2017 David Conran -#include "IRsend.h" -#include "IRsend_test.h" +#include "../src/IRsend.h" +#include "../src/IRsend_test.h" #include "gtest/gtest.h" // Tests for sendDenon(). diff --git a/lib/IRremoteESP8266/test/ir_Dish_test.cpp b/lib/IRremoteESP8266/test/ir_Dish_test.cpp index d745648111..4c6cb5c895 100644 --- a/lib/IRremoteESP8266/test/ir_Dish_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Dish_test.cpp @@ -1,7 +1,7 @@ // Copyright 2017 David Conran -#include "IRsend.h" -#include "IRsend_test.h" +#include "../src/IRsend.h" +#include "../src/IRsend_test.h" #include "gtest/gtest.h" // Tests for sendDISH(). diff --git a/lib/IRremoteESP8266/test/ir_Doshisha_test.cpp b/lib/IRremoteESP8266/test/ir_Doshisha_test.cpp index 8c69bfbb3b..3ee6ee11e6 100644 --- a/lib/IRremoteESP8266/test/ir_Doshisha_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Doshisha_test.cpp @@ -1,10 +1,10 @@ // Copyright 2020 Christian Nilsson -#include "IRac.h" -#include "IRrecv.h" -#include "IRrecv_test.h" -#include "IRsend.h" -#include "IRsend_test.h" +#include "../src/IRac.h" +#include "../src/IRrecv.h" +#include "../src/IRrecv_test.h" +#include "../src/IRsend.h" +#include "../src/IRsend_test.h" #include "gtest/gtest.h" // Tests for decodeDoshisha(). diff --git a/lib/IRremoteESP8266/test/ir_Ecoclim_test.cpp b/lib/IRremoteESP8266/test/ir_Ecoclim_test.cpp index fe031e4e27..2cc2a662bb 100644 --- a/lib/IRremoteESP8266/test/ir_Ecoclim_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Ecoclim_test.cpp @@ -1,13 +1,13 @@ // Copyright 2021 David Conran -#include "ir_Ecoclim.h" +#include "../src/ir_Ecoclim.h" #include -#include "IRac.h" -#include "IRrecv.h" -#include "IRrecv_test.h" -#include "IRsend.h" -#include "IRsend_test.h" -#include "IRutils.h" +#include "../src/IRac.h" +#include "../src/IRrecv.h" +#include "../src/IRrecv_test.h" +#include "../src/IRsend.h" +#include "../src/IRsend_test.h" +#include "../src/IRutils.h" #include "gtest/gtest.h" TEST(TestUtils, Housekeeping) { diff --git a/lib/IRremoteESP8266/test/ir_Electra_test.cpp b/lib/IRremoteESP8266/test/ir_Electra_test.cpp index 9c1dedd09f..bca75f68a9 100644 --- a/lib/IRremoteESP8266/test/ir_Electra_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Electra_test.cpp @@ -1,12 +1,12 @@ // Copyright 2018, 2019 David Conran -#include "ir_Electra.h" +#include "../src/ir_Electra.h" #include -#include "IRac.h" -#include "IRrecv.h" -#include "IRrecv_test.h" -#include "IRsend.h" -#include "IRsend_test.h" +#include "../src/IRac.h" +#include "../src/IRrecv.h" +#include "../src/IRrecv_test.h" +#include "../src/IRsend.h" +#include "../src/IRsend_test.h" #include "gtest/gtest.h" // Tests for sendElectraAC(). diff --git a/lib/IRremoteESP8266/test/ir_EliteScreens_test.cpp b/lib/IRremoteESP8266/test/ir_EliteScreens_test.cpp index 0af0f1f552..7bcda48c25 100644 --- a/lib/IRremoteESP8266/test/ir_EliteScreens_test.cpp +++ b/lib/IRremoteESP8266/test/ir_EliteScreens_test.cpp @@ -1,8 +1,8 @@ // Copyright 2017 David Conran -#include "IRac.h" -#include "IRsend.h" -#include "IRsend_test.h" +#include "../src/IRac.h" +#include "../src/IRsend.h" +#include "../src/IRsend_test.h" #include "gtest/gtest.h" // Housekeeping tests diff --git a/lib/IRremoteESP8266/test/ir_Epson_test.cpp b/lib/IRremoteESP8266/test/ir_Epson_test.cpp index d1df3f9acc..86c0cc185b 100644 --- a/lib/IRremoteESP8266/test/ir_Epson_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Epson_test.cpp @@ -1,10 +1,10 @@ // Copyright 2020 David Conran -#include "IRac.h" -#include "IRrecv.h" -#include "IRrecv_test.h" -#include "IRsend.h" -#include "IRsend_test.h" +#include "../src/IRac.h" +#include "../src/IRrecv.h" +#include "../src/IRrecv_test.h" +#include "../src/IRsend.h" +#include "../src/IRsend_test.h" #include "gtest/gtest.h" // Tests for sendEpson(). diff --git a/lib/IRremoteESP8266/test/ir_Fujitsu_test.cpp b/lib/IRremoteESP8266/test/ir_Fujitsu_test.cpp index caf4e53f1f..c05a2e5a4c 100644 --- a/lib/IRremoteESP8266/test/ir_Fujitsu_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Fujitsu_test.cpp @@ -1,9 +1,9 @@ // Copyright 2017 Jonny Graham, David Conran -#include "IRac.h" -#include "IRrecv_test.h" -#include "IRsend.h" -#include "IRsend_test.h" -#include "ir_Fujitsu.h" +#include "../src/IRac.h" +#include "../src/IRrecv_test.h" +#include "../src/IRsend.h" +#include "../src/IRsend_test.h" +#include "../src/ir_Fujitsu.h" #include "gtest/gtest.h" // Tests for Fujitsu A/C methods. diff --git a/lib/IRremoteESP8266/test/ir_GICable_test.cpp b/lib/IRremoteESP8266/test/ir_GICable_test.cpp index 234a748b5c..e58f14bccc 100644 --- a/lib/IRremoteESP8266/test/ir_GICable_test.cpp +++ b/lib/IRremoteESP8266/test/ir_GICable_test.cpp @@ -1,7 +1,7 @@ // Copyright 2018 David Conran -#include "IRsend.h" -#include "IRsend_test.h" +#include "../src/IRsend.h" +#include "../src/IRsend_test.h" #include "gtest/gtest.h" // Tests for sendGICable(). diff --git a/lib/IRremoteESP8266/test/ir_GlobalCache_test.cpp b/lib/IRremoteESP8266/test/ir_GlobalCache_test.cpp index 00742aedac..c4d492ec0e 100644 --- a/lib/IRremoteESP8266/test/ir_GlobalCache_test.cpp +++ b/lib/IRremoteESP8266/test/ir_GlobalCache_test.cpp @@ -1,7 +1,7 @@ // Copyright 2017 David Conran -#include "IRsend.h" -#include "IRsend_test.h" +#include "../src/IRsend.h" +#include "../src/IRsend_test.h" #include "gtest/gtest.h" // Tests for sendGlobalCache(). diff --git a/lib/IRremoteESP8266/test/ir_Goodweather_test.cpp b/lib/IRremoteESP8266/test/ir_Goodweather_test.cpp index be07b9d093..9dbb729f09 100644 --- a/lib/IRremoteESP8266/test/ir_Goodweather_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Goodweather_test.cpp @@ -1,11 +1,11 @@ // Copyright 2019 David Conran -#include "ir_Goodweather.h" -#include "IRac.h" -#include "IRrecv.h" -#include "IRrecv_test.h" -#include "IRsend.h" -#include "IRsend_test.h" +#include "../src/ir_Goodweather.h" +#include "../src/IRac.h" +#include "../src/IRrecv.h" +#include "../src/IRrecv_test.h" +#include "../src/IRsend.h" +#include "../src/IRsend_test.h" #include "gtest/gtest.h" TEST(TestIRUtils, Goodweather) { diff --git a/lib/IRremoteESP8266/test/ir_Gree_test.cpp b/lib/IRremoteESP8266/test/ir_Gree_test.cpp index cb1832f613..392f1f5817 100644 --- a/lib/IRremoteESP8266/test/ir_Gree_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Gree_test.cpp @@ -1,12 +1,12 @@ // Copyright 2017 David Conran -#include "ir_Gree.h" -#include "IRac.h" -#include "IRrecv.h" -#include "IRrecv_test.h" -#include "IRremoteESP8266.h" -#include "IRsend.h" -#include "IRsend_test.h" +#include "../src/ir_Gree.h" +#include "../src/IRac.h" +#include "../src/IRrecv.h" +#include "../src/IRrecv_test.h" +#include "../src/IRremoteESP8266.h" +#include "../src/IRsend.h" +#include "../src/IRsend_test.h" #include "gtest/gtest.h" // Tests for sendGree(). diff --git a/lib/IRremoteESP8266/test/ir_Haier_test.cpp b/lib/IRremoteESP8266/test/ir_Haier_test.cpp index b60a08f23a..a69fe3ecb9 100644 --- a/lib/IRremoteESP8266/test/ir_Haier_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Haier_test.cpp @@ -1,11 +1,11 @@ // Copyright 2018 David Conran -#include "ir_Haier.h" -#include "IRac.h" -#include "IRrecv.h" -#include "IRrecv_test.h" -#include "IRsend.h" -#include "IRsend_test.h" +#include "../src/ir_Haier.h" +#include "../src/IRac.h" +#include "../src/IRrecv.h" +#include "../src/IRrecv_test.h" +#include "../src/IRsend.h" +#include "../src/IRsend_test.h" #include "gtest/gtest.h" // Tests for sendHaierAC() diff --git a/lib/IRremoteESP8266/test/ir_Hitachi_test.cpp b/lib/IRremoteESP8266/test/ir_Hitachi_test.cpp index 2bbbd29e91..bff5f350c1 100644 --- a/lib/IRremoteESP8266/test/ir_Hitachi_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Hitachi_test.cpp @@ -1,12 +1,12 @@ // Copyright 2018 David Conran -#include "ir_Hitachi.h" -#include "IRac.h" -#include "IRrecv.h" -#include "IRrecv_test.h" -#include "IRremoteESP8266.h" -#include "IRsend.h" -#include "IRsend_test.h" +#include "../src/ir_Hitachi.h" +#include "../src/IRac.h" +#include "../src/IRrecv.h" +#include "../src/IRrecv_test.h" +#include "../src/IRremoteESP8266.h" +#include "../src/IRsend.h" +#include "../src/IRsend_test.h" #include "gtest/gtest.h" // Tests for sendHitachiAC(). diff --git a/lib/IRremoteESP8266/test/ir_Inax_test.cpp b/lib/IRremoteESP8266/test/ir_Inax_test.cpp index b182cce32e..e9523954d0 100644 --- a/lib/IRremoteESP8266/test/ir_Inax_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Inax_test.cpp @@ -1,8 +1,8 @@ // Copyright 2019 crankyoldgit (David Conran) -#include "IRsend.h" -#include "IRsend_test.h" -#include "IRutils.h" +#include "../src/IRsend.h" +#include "../src/IRsend_test.h" +#include "../src/IRutils.h" #include "gtest/gtest.h" diff --git a/lib/IRremoteESP8266/test/ir_JVC_test.cpp b/lib/IRremoteESP8266/test/ir_JVC_test.cpp index 51b16b82ce..3ab51d2101 100644 --- a/lib/IRremoteESP8266/test/ir_JVC_test.cpp +++ b/lib/IRremoteESP8266/test/ir_JVC_test.cpp @@ -1,7 +1,7 @@ // Copyright 2017 David Conran -#include "IRsend.h" -#include "IRsend_test.h" +#include "../src/IRsend.h" +#include "../src/IRsend_test.h" #include "gtest/gtest.h" // Tests for sendJVC(). diff --git a/lib/IRremoteESP8266/test/ir_Kelon_test.cpp b/lib/IRremoteESP8266/test/ir_Kelon_test.cpp index 7212d49ee5..5e02c26235 100644 --- a/lib/IRremoteESP8266/test/ir_Kelon_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Kelon_test.cpp @@ -1,10 +1,10 @@ // Copyright 2021 Davide Depau -#include "IRac.h" -#include "IRrecv.h" -#include "IRrecv_test.h" -#include "IRsend.h" -#include "IRsend_test.h" +#include "../src/IRac.h" +#include "../src/IRrecv.h" +#include "../src/IRrecv_test.h" +#include "../src/IRsend.h" +#include "../src/IRsend_test.h" #include "gtest/gtest.h" // Tests for sendKelon(). diff --git a/lib/IRremoteESP8266/test/ir_Kelvinator_test.cpp b/lib/IRremoteESP8266/test/ir_Kelvinator_test.cpp index 73ad6581d8..a7b4552573 100644 --- a/lib/IRremoteESP8266/test/ir_Kelvinator_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Kelvinator_test.cpp @@ -1,12 +1,12 @@ // Copyright 2017 David Conran -#include "ir_Kelvinator.h" -#include "IRac.h" -#include "IRrecv.h" -#include "IRrecv_test.h" -#include "IRremoteESP8266.h" -#include "IRsend.h" -#include "IRsend_test.h" +#include "../src/ir_Kelvinator.h" +#include "../src/IRac.h" +#include "../src/IRrecv.h" +#include "../src/IRrecv_test.h" +#include "../src/IRremoteESP8266.h" +#include "../src/IRsend.h" +#include "../src/IRsend_test.h" #include "gtest/gtest.h" // Tests for sendKelvinator(). diff --git a/lib/IRremoteESP8266/test/ir_LG_test.cpp b/lib/IRremoteESP8266/test/ir_LG_test.cpp index 808f7b83af..54c4bcfafb 100644 --- a/lib/IRremoteESP8266/test/ir_LG_test.cpp +++ b/lib/IRremoteESP8266/test/ir_LG_test.cpp @@ -1,9 +1,9 @@ // Copyright 2017, 2019 David Conran -#include "ir_LG.h" -#include "IRac.h" -#include "IRsend.h" -#include "IRsend_test.h" +#include "../src/ir_LG.h" +#include "../src/IRac.h" +#include "../src/IRsend.h" +#include "../src/IRsend_test.h" #include "gtest/gtest.h" diff --git a/lib/IRremoteESP8266/test/ir_Lasertag_test.cpp b/lib/IRremoteESP8266/test/ir_Lasertag_test.cpp index bad724f76d..d3d96f299c 100644 --- a/lib/IRremoteESP8266/test/ir_Lasertag_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Lasertag_test.cpp @@ -1,7 +1,7 @@ // Copyright 2017 David Conran -#include "IRsend.h" -#include "IRsend_test.h" +#include "../src/IRsend.h" +#include "../src/IRsend_test.h" #include "gtest/gtest.h" // LL AAA SSSSS EEEEEEE RRRRRR TTTTTTT AAA GGGG diff --git a/lib/IRremoteESP8266/test/ir_Lego_test.cpp b/lib/IRremoteESP8266/test/ir_Lego_test.cpp index 4e859b1708..c2aacb1c9b 100644 --- a/lib/IRremoteESP8266/test/ir_Lego_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Lego_test.cpp @@ -1,9 +1,9 @@ // Copyright 2019 David Conran -#include "IRrecv.h" -#include "IRrecv_test.h" -#include "IRsend.h" -#include "IRsend_test.h" +#include "../src/IRrecv.h" +#include "../src/IRrecv_test.h" +#include "../src/IRsend.h" +#include "../src/IRsend_test.h" #include "gtest/gtest.h" // General housekeeping diff --git a/lib/IRremoteESP8266/test/ir_Lutron_test.cpp b/lib/IRremoteESP8266/test/ir_Lutron_test.cpp index 81cf0df9ce..b0a4e346e5 100644 --- a/lib/IRremoteESP8266/test/ir_Lutron_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Lutron_test.cpp @@ -1,7 +1,7 @@ // Copyright 2018 David Conran -#include "IRsend.h" -#include "IRsend_test.h" +#include "../src/IRsend.h" +#include "../src/IRsend_test.h" #include "gtest/gtest.h" // Tests for sendLutron(). diff --git a/lib/IRremoteESP8266/test/ir_MWM_test.cpp b/lib/IRremoteESP8266/test/ir_MWM_test.cpp index 2ca69ac833..7b70a46b4c 100644 --- a/lib/IRremoteESP8266/test/ir_MWM_test.cpp +++ b/lib/IRremoteESP8266/test/ir_MWM_test.cpp @@ -1,10 +1,10 @@ // Copyright 2017 David Conran // Copyright 2018 Brett T. Warden -#include "IRrecv.h" -#include "IRrecv_test.h" -#include "IRsend.h" -#include "IRsend_test.h" +#include "../src/IRrecv.h" +#include "../src/IRrecv_test.h" +#include "../src/IRsend.h" +#include "../src/IRsend_test.h" #include "gtest/gtest.h" // MM MM WW WW MM MM diff --git a/lib/IRremoteESP8266/test/ir_Magiquest_test.cpp b/lib/IRremoteESP8266/test/ir_Magiquest_test.cpp index 1e5faf5c08..c60a893de0 100644 --- a/lib/IRremoteESP8266/test/ir_Magiquest_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Magiquest_test.cpp @@ -1,9 +1,9 @@ // Copyright 2017 David Conran -#include "ir_Magiquest.h" -#include "IRrecv.h" -#include "IRsend.h" -#include "IRsend_test.h" +#include "../src/ir_Magiquest.h" +#include "../src/IRrecv.h" +#include "../src/IRsend.h" +#include "../src/IRsend_test.h" #include "gtest/gtest.h" // Tests for encodeMagiQuest() diff --git a/lib/IRremoteESP8266/test/ir_Metz_test.cpp b/lib/IRremoteESP8266/test/ir_Metz_test.cpp index 67d2170231..8644e048dd 100644 --- a/lib/IRremoteESP8266/test/ir_Metz_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Metz_test.cpp @@ -1,10 +1,10 @@ // Copyright 2020 crankyoldgit -#include "IRac.h" -#include "IRrecv.h" -#include "IRrecv_test.h" -#include "IRsend.h" -#include "IRsend_test.h" +#include "../src/IRac.h" +#include "../src/IRrecv.h" +#include "../src/IRrecv_test.h" +#include "../src/IRsend.h" +#include "../src/IRsend_test.h" #include "gtest/gtest.h" // Tests for decodeMetz(). diff --git a/lib/IRremoteESP8266/test/ir_Midea_test.cpp b/lib/IRremoteESP8266/test/ir_Midea_test.cpp index 713fcd0e41..2e147fd0df 100644 --- a/lib/IRremoteESP8266/test/ir_Midea_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Midea_test.cpp @@ -1,9 +1,9 @@ // Copyright 2017 David Conran -#include "ir_Midea.h" -#include "IRac.h" -#include "IRsend.h" -#include "IRsend_test.h" +#include "../src/ir_Midea.h" +#include "../src/IRac.h" +#include "../src/IRsend.h" +#include "../src/IRsend_test.h" #include "gtest/gtest.h" // Tests for sendMidea(). diff --git a/lib/IRremoteESP8266/test/ir_Milestag2_test.cpp b/lib/IRremoteESP8266/test/ir_Milestag2_test.cpp index 542852da85..37b74ad896 100644 --- a/lib/IRremoteESP8266/test/ir_Milestag2_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Milestag2_test.cpp @@ -1,11 +1,11 @@ // Copyright 2021 David Conran -#include "IRac.h" -#include "IRrecv.h" -#include "IRrecv_test.h" -#include "IRsend.h" -#include "IRsend_test.h" -#include "IRutils.h" +#include "../src/IRac.h" +#include "../src/IRrecv.h" +#include "../src/IRrecv_test.h" +#include "../src/IRsend.h" +#include "../src/IRsend_test.h" +#include "../src/IRutils.h" #include "gtest/gtest.h" TEST(TestUtils, Housekeeping) { diff --git a/lib/IRremoteESP8266/test/ir_MitsubishiHeavy_test.cpp b/lib/IRremoteESP8266/test/ir_MitsubishiHeavy_test.cpp index d182fd941b..f5e4a29d3a 100644 --- a/lib/IRremoteESP8266/test/ir_MitsubishiHeavy_test.cpp +++ b/lib/IRremoteESP8266/test/ir_MitsubishiHeavy_test.cpp @@ -1,12 +1,12 @@ // Copyright 2019 David Conran -#include "ir_MitsubishiHeavy.h" -#include "IRac.h" -#include "IRrecv.h" -#include "IRrecv_test.h" -#include "IRremoteESP8266.h" -#include "IRsend.h" -#include "IRsend_test.h" +#include "../src/ir_MitsubishiHeavy.h" +#include "../src/IRac.h" +#include "../src/IRrecv.h" +#include "../src/IRrecv_test.h" +#include "../src/IRremoteESP8266.h" +#include "../src/IRsend.h" +#include "../src/IRsend_test.h" #include "gtest/gtest.h" // General housekeeping diff --git a/lib/IRremoteESP8266/test/ir_Mitsubishi_test.cpp b/lib/IRremoteESP8266/test/ir_Mitsubishi_test.cpp index 815d4f9961..b3e59962f4 100644 --- a/lib/IRremoteESP8266/test/ir_Mitsubishi_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Mitsubishi_test.cpp @@ -2,11 +2,11 @@ // Copyright 2019 kuchel77 // Copyright 2018 denxhun -#include "ir_Mitsubishi.h" -#include "IRac.h" -#include "IRrecv_test.h" -#include "IRsend.h" -#include "IRsend_test.h" +#include "../src/ir_Mitsubishi.h" +#include "../src/IRac.h" +#include "../src/IRrecv_test.h" +#include "../src/IRsend.h" +#include "../src/IRsend_test.h" #include "gtest/gtest.h" // Tests for sendMitsubishi(). diff --git a/lib/IRremoteESP8266/test/ir_Multibrackets_test.cpp b/lib/IRremoteESP8266/test/ir_Multibrackets_test.cpp index 7b58b3333a..683072685d 100644 --- a/lib/IRremoteESP8266/test/ir_Multibrackets_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Multibrackets_test.cpp @@ -1,10 +1,10 @@ // Copyright 2020 David Conran -#include "IRac.h" -#include "IRrecv.h" -#include "IRrecv_test.h" -#include "IRsend.h" -#include "IRsend_test.h" +#include "../src/IRac.h" +#include "../src/IRrecv.h" +#include "../src/IRrecv_test.h" +#include "../src/IRsend.h" +#include "../src/IRsend_test.h" #include "gtest/gtest.h" // Tests for decodeMultibrackets(). diff --git a/lib/IRremoteESP8266/test/ir_NEC_test.cpp b/lib/IRremoteESP8266/test/ir_NEC_test.cpp index f7598c9811..b8c2be1b49 100644 --- a/lib/IRremoteESP8266/test/ir_NEC_test.cpp +++ b/lib/IRremoteESP8266/test/ir_NEC_test.cpp @@ -1,7 +1,7 @@ // Copyright 2017 David Conran -#include "IRsend.h" -#include "IRsend_test.h" +#include "../src/IRsend.h" +#include "../src/IRsend_test.h" #include "gtest/gtest.h" // Tests for sendNEC(). diff --git a/lib/IRremoteESP8266/test/ir_Neoclima_test.cpp b/lib/IRremoteESP8266/test/ir_Neoclima_test.cpp index 68fc2ded53..4c2ed04652 100644 --- a/lib/IRremoteESP8266/test/ir_Neoclima_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Neoclima_test.cpp @@ -1,12 +1,12 @@ // Copyright 2019 David Conran (crankyoldgit) -#include "ir_Neoclima.h" +#include "../src/ir_Neoclima.h" #include -#include "IRac.h" -#include "IRsend.h" -#include "IRsend_test.h" -#include "IRrecv.h" -#include "IRrecv_test.h" +#include "../src/IRac.h" +#include "../src/IRsend.h" +#include "../src/IRsend_test.h" +#include "../src/IRrecv.h" +#include "../src/IRrecv_test.h" #include "gtest/gtest.h" TEST(TestUtils, Housekeeping) { diff --git a/lib/IRremoteESP8266/test/ir_Nikai_test.cpp b/lib/IRremoteESP8266/test/ir_Nikai_test.cpp index 98d6d806da..7a89b601cd 100644 --- a/lib/IRremoteESP8266/test/ir_Nikai_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Nikai_test.cpp @@ -1,7 +1,7 @@ // Copyright 2017 David Conran -#include "IRsend.h" -#include "IRsend_test.h" +#include "../src/IRsend.h" +#include "../src/IRsend_test.h" #include "gtest/gtest.h" // Tests for sendNikai(). diff --git a/lib/IRremoteESP8266/test/ir_Panasonic_test.cpp b/lib/IRremoteESP8266/test/ir_Panasonic_test.cpp index 3e52cef8b2..5c2a09dd26 100644 --- a/lib/IRremoteESP8266/test/ir_Panasonic_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Panasonic_test.cpp @@ -1,13 +1,13 @@ // Copyright 2017, 2018 David Conran -#include "ir_Panasonic.h" -#include "IRac.h" -#include "IRrecv.h" -#include "IRrecv_test.h" -#include "IRsend.h" -#include "IRsend_test.h" -#include "IRtext.h" -#include "IRutils.h" +#include "../src/ir_Panasonic.h" +#include "../src/IRac.h" +#include "../src/IRrecv.h" +#include "../src/IRrecv_test.h" +#include "../src/IRsend.h" +#include "../src/IRsend_test.h" +#include "../src/IRtext.h" +#include "../src/IRutils.h" #include "gtest/gtest.h" // Tests for encodePanasonic(). diff --git a/lib/IRremoteESP8266/test/ir_Pioneer_test.cpp b/lib/IRremoteESP8266/test/ir_Pioneer_test.cpp index fce2503e4a..7ac7d15561 100644 --- a/lib/IRremoteESP8266/test/ir_Pioneer_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Pioneer_test.cpp @@ -1,8 +1,8 @@ // Copyright 2018 David Conran -#include "IRsend.h" -#include "IRsend_test.h" -#include "IRutils.h" +#include "../src/IRsend.h" +#include "../src/IRsend_test.h" +#include "../src/IRutils.h" #include "gtest/gtest.h" // Tests for sendPioneer(). diff --git a/lib/IRremoteESP8266/test/ir_Pronto_test.cpp b/lib/IRremoteESP8266/test/ir_Pronto_test.cpp index 8331192bca..f441120f1b 100644 --- a/lib/IRremoteESP8266/test/ir_Pronto_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Pronto_test.cpp @@ -1,6 +1,6 @@ // Copyright 2017 David Conran -#include "IRsend.h" -#include "IRsend_test.h" +#include "../src/IRsend.h" +#include "../src/IRsend_test.h" #include "gtest/gtest.h" // Tests for sendPronto(). diff --git a/lib/IRremoteESP8266/test/ir_RC5_RC6_test.cpp b/lib/IRremoteESP8266/test/ir_RC5_RC6_test.cpp index 0b46bad7f7..8073ea5ab1 100644 --- a/lib/IRremoteESP8266/test/ir_RC5_RC6_test.cpp +++ b/lib/IRremoteESP8266/test/ir_RC5_RC6_test.cpp @@ -1,7 +1,7 @@ // Copyright 2017 David Conran -#include "IRsend.h" -#include "IRsend_test.h" +#include "../src/IRsend.h" +#include "../src/IRsend_test.h" #include "gtest/gtest.h" // RRRRRR CCCCC 555555 RRRRRR CCCCC 555555 XX XX diff --git a/lib/IRremoteESP8266/test/ir_RCMM_test.cpp b/lib/IRremoteESP8266/test/ir_RCMM_test.cpp index ef37d87a6a..b635c6173f 100644 --- a/lib/IRremoteESP8266/test/ir_RCMM_test.cpp +++ b/lib/IRremoteESP8266/test/ir_RCMM_test.cpp @@ -1,7 +1,7 @@ // Copyright 2017 David Conran -#include "IRsend.h" -#include "IRsend_test.h" +#include "../src/IRsend.h" +#include "../src/IRsend_test.h" #include "gtest/gtest.h" // Tests for sendRCMM(). diff --git a/lib/IRremoteESP8266/test/ir_Rhoss_test.cpp b/lib/IRremoteESP8266/test/ir_Rhoss_test.cpp index 855b9a48c8..18428bb40d 100644 --- a/lib/IRremoteESP8266/test/ir_Rhoss_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Rhoss_test.cpp @@ -1,12 +1,12 @@ // Copyright 2021 Tom Rosenback -#include "IRac.h" -#include "ir_Rhoss.h" -#include "IRrecv.h" -#include "IRrecv_test.h" -#include "IRsend.h" -#include "IRsend_test.h" -#include "IRutils.h" +#include "../src/IRac.h" +#include "../src/ir_Rhoss.h" +#include "../src/IRrecv.h" +#include "../src/IRrecv_test.h" +#include "../src/IRsend.h" +#include "../src/IRsend_test.h" +#include "../src/IRutils.h" #include "gtest/gtest.h" TEST(TestUtils, Housekeeping) { diff --git a/lib/IRremoteESP8266/test/ir_Samsung_test.cpp b/lib/IRremoteESP8266/test/ir_Samsung_test.cpp index 061067421f..a65c218d94 100644 --- a/lib/IRremoteESP8266/test/ir_Samsung_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Samsung_test.cpp @@ -1,12 +1,12 @@ // Copyright 2017-2020 David Conran #include -#include "ir_Samsung.h" -#include "IRac.h" -#include "IRrecv.h" -#include "IRrecv_test.h" -#include "IRsend.h" -#include "IRsend_test.h" +#include "../src/ir_Samsung.h" +#include "../src/IRac.h" +#include "../src/IRrecv.h" +#include "../src/IRrecv_test.h" +#include "../src/IRsend.h" +#include "../src/IRsend_test.h" #include "gtest/gtest.h" diff --git a/lib/IRremoteESP8266/test/ir_Sanyo_test.cpp b/lib/IRremoteESP8266/test/ir_Sanyo_test.cpp index 6378e7679d..bb44b3a013 100644 --- a/lib/IRremoteESP8266/test/ir_Sanyo_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Sanyo_test.cpp @@ -1,11 +1,11 @@ // Copyright 2017-2021 David Conran -#include "ir_Sanyo.h" -#include "IRac.h" -#include "IRrecv.h" -#include "IRrecv_test.h" -#include "IRsend.h" -#include "IRsend_test.h" +#include "../src/ir_Sanyo.h" +#include "../src/IRac.h" +#include "../src/IRrecv.h" +#include "../src/IRrecv_test.h" +#include "../src/IRsend.h" +#include "../src/IRsend_test.h" #include "gtest/gtest.h" // Tests for encodeSanyoLC7461(). diff --git a/lib/IRremoteESP8266/test/ir_Sharp_test.cpp b/lib/IRremoteESP8266/test/ir_Sharp_test.cpp index a2acafb9cc..6635e2dfd5 100644 --- a/lib/IRremoteESP8266/test/ir_Sharp_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Sharp_test.cpp @@ -1,11 +1,11 @@ // Copyright 2017 David Conran -#include "ir_Sharp.h" -#include "IRac.h" -#include "IRrecv.h" -#include "IRrecv_test.h" -#include "IRsend.h" -#include "IRsend_test.h" +#include "../src/ir_Sharp.h" +#include "../src/IRac.h" +#include "../src/IRrecv.h" +#include "../src/IRrecv_test.h" +#include "../src/IRsend.h" +#include "../src/IRsend_test.h" #include "gtest/gtest.h" // Tests for encodeSharp(). diff --git a/lib/IRremoteESP8266/test/ir_Sherwood_test.cpp b/lib/IRremoteESP8266/test/ir_Sherwood_test.cpp index f1f41d9c8c..2ac8b4af95 100644 --- a/lib/IRremoteESP8266/test/ir_Sherwood_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Sherwood_test.cpp @@ -1,7 +1,7 @@ // Copyright 2017 David Conran -#include "IRsend.h" -#include "IRsend_test.h" +#include "../src/IRsend.h" +#include "../src/IRsend_test.h" #include "gtest/gtest.h" // Tests for sendSherwood(). diff --git a/lib/IRremoteESP8266/test/ir_Sony_test.cpp b/lib/IRremoteESP8266/test/ir_Sony_test.cpp index c69d40e67a..dd0692f45c 100644 --- a/lib/IRremoteESP8266/test/ir_Sony_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Sony_test.cpp @@ -1,7 +1,7 @@ // Copyright 2017 David Conran -#include "IRsend.h" -#include "IRsend_test.h" +#include "../src/IRsend.h" +#include "../src/IRsend_test.h" #include "gtest/gtest.h" // Tests for sendSony(). diff --git a/lib/IRremoteESP8266/test/ir_Symphony_test.cpp b/lib/IRremoteESP8266/test/ir_Symphony_test.cpp index e3f818136e..7960db61eb 100644 --- a/lib/IRremoteESP8266/test/ir_Symphony_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Symphony_test.cpp @@ -1,10 +1,10 @@ // Copyright 2020 David Conran -#include "IRac.h" -#include "IRrecv.h" -#include "IRrecv_test.h" -#include "IRsend.h" -#include "IRsend_test.h" +#include "../src/IRac.h" +#include "../src/IRrecv.h" +#include "../src/IRrecv_test.h" +#include "../src/IRsend.h" +#include "../src/IRsend_test.h" #include "gtest/gtest.h" // Tests for sendSymphony(). diff --git a/lib/IRremoteESP8266/test/ir_Tcl_test.cpp b/lib/IRremoteESP8266/test/ir_Tcl_test.cpp index b7ab5301f9..37e75f42f6 100644 --- a/lib/IRremoteESP8266/test/ir_Tcl_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Tcl_test.cpp @@ -1,11 +1,11 @@ // Copyright 2019 David Conran -#include "ir_Tcl.h" -#include "IRac.h" -#include "IRrecv.h" -#include "IRrecv_test.h" -#include "IRsend.h" -#include "IRsend_test.h" +#include "../src/ir_Tcl.h" +#include "../src/IRac.h" +#include "../src/IRrecv.h" +#include "../src/IRrecv_test.h" +#include "../src/IRsend.h" +#include "../src/IRsend_test.h" #include "gtest/gtest.h" // General housekeeping diff --git a/lib/IRremoteESP8266/test/ir_Technibel_test.cpp b/lib/IRremoteESP8266/test/ir_Technibel_test.cpp index 808da1e951..420662e8f7 100644 --- a/lib/IRremoteESP8266/test/ir_Technibel_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Technibel_test.cpp @@ -1,12 +1,12 @@ // Copyright 2020 Quentin BRIOLLANT -#include "IRac.h" -#include "ir_Technibel.h" -#include "IRrecv.h" -#include "IRrecv_test.h" -#include "IRsend.h" -#include "IRsend_test.h" -#include "IRutils.h" +#include "../src/IRac.h" +#include "../src/ir_Technibel.h" +#include "../src/IRrecv.h" +#include "../src/IRrecv_test.h" +#include "../src/IRsend.h" +#include "../src/IRsend_test.h" +#include "../src/IRutils.h" #include "gtest/gtest.h" TEST(TestUtils, Housekeeping) { diff --git a/lib/IRremoteESP8266/test/ir_Teco_test.cpp b/lib/IRremoteESP8266/test/ir_Teco_test.cpp index f4196c3aef..2913c3dc6a 100644 --- a/lib/IRremoteESP8266/test/ir_Teco_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Teco_test.cpp @@ -1,11 +1,11 @@ // Copyright 2019 David Conran -#include "ir_Teco.h" -#include "IRac.h" -#include "IRrecv.h" -#include "IRrecv_test.h" -#include "IRsend.h" -#include "IRsend_test.h" +#include "../src/ir_Teco.h" +#include "../src/IRac.h" +#include "../src/IRrecv.h" +#include "../src/IRrecv_test.h" +#include "../src/IRsend.h" +#include "../src/IRsend_test.h" #include "gtest/gtest.h" // General housekeeping diff --git a/lib/IRremoteESP8266/test/ir_Teknopoint_test.cpp b/lib/IRremoteESP8266/test/ir_Teknopoint_test.cpp index 74f29d1468..59a7ee23d6 100644 --- a/lib/IRremoteESP8266/test/ir_Teknopoint_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Teknopoint_test.cpp @@ -1,10 +1,10 @@ // Copyright 2021 David Conran (crankyoldgit) -#include "IRac.h" -#include "IRrecv.h" -#include "IRrecv_test.h" -#include "IRsend.h" -#include "IRsend_test.h" +#include "../src/IRac.h" +#include "../src/IRrecv.h" +#include "../src/IRrecv_test.h" +#include "../src/IRsend.h" +#include "../src/IRsend_test.h" #include "gtest/gtest.h" // Tests for decodeTeknopoint(). diff --git a/lib/IRremoteESP8266/test/ir_Toshiba_test.cpp b/lib/IRremoteESP8266/test/ir_Toshiba_test.cpp index 4a63780d50..a12a502949 100644 --- a/lib/IRremoteESP8266/test/ir_Toshiba_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Toshiba_test.cpp @@ -1,10 +1,10 @@ // Copyright 2017 David Conran -#include "ir_Toshiba.h" -#include "IRac.h" -#include "IRrecv.h" -#include "IRrecv_test.h" -#include "IRsend.h" -#include "IRsend_test.h" +#include "../src/ir_Toshiba.h" +#include "../src/IRac.h" +#include "../src/IRrecv.h" +#include "../src/IRrecv_test.h" +#include "../src/IRsend.h" +#include "../src/IRsend_test.h" #include "gtest/gtest.h" // Tests for Toshiba A/C methods. diff --git a/lib/IRremoteESP8266/test/ir_Transcold_test.cpp b/lib/IRremoteESP8266/test/ir_Transcold_test.cpp index 8a98a28969..93ac4ad03e 100644 --- a/lib/IRremoteESP8266/test/ir_Transcold_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Transcold_test.cpp @@ -1,10 +1,10 @@ // Copyright 2020 crankyoldgit -#include "IRac.h" -#include "IRrecv.h" -#include "IRrecv_test.h" -#include "IRsend.h" -#include "IRsend_test.h" +#include "../src/IRac.h" +#include "../src/IRrecv.h" +#include "../src/IRrecv_test.h" +#include "../src/IRsend.h" +#include "../src/IRsend_test.h" #include "gtest/gtest.h" // Tests for decodeTranscold(). diff --git a/lib/IRremoteESP8266/test/ir_Trotec_test.cpp b/lib/IRremoteESP8266/test/ir_Trotec_test.cpp index b6658f3f52..8e4c069db4 100644 --- a/lib/IRremoteESP8266/test/ir_Trotec_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Trotec_test.cpp @@ -1,10 +1,10 @@ // Copyright 2019 David Conran -#include "ir_Trotec.h" -#include "IRac.h" -#include "IRrecv.h" -#include "IRrecv_test.h" -#include "IRsend.h" -#include "IRsend_test.h" +#include "../src/ir_Trotec.h" +#include "../src/IRac.h" +#include "../src/IRrecv.h" +#include "../src/IRrecv_test.h" +#include "../src/IRsend.h" +#include "../src/IRsend_test.h" #include "gtest/gtest.h" diff --git a/lib/IRremoteESP8266/test/ir_Truma_test.cpp b/lib/IRremoteESP8266/test/ir_Truma_test.cpp index 0d26fb6dbe..aab6fbf8b4 100644 --- a/lib/IRremoteESP8266/test/ir_Truma_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Truma_test.cpp @@ -1,10 +1,10 @@ // Copyright 2021 David Conran (crankyoldgit) -#include "IRac.h" -#include "IRrecv.h" -#include "IRrecv_test.h" -#include "IRsend.h" -#include "IRsend_test.h" +#include "../src/IRac.h" +#include "../src/IRrecv.h" +#include "../src/IRrecv_test.h" +#include "../src/IRsend.h" +#include "../src/IRsend_test.h" #include "gtest/gtest.h" // Tests for decodeTruma(). diff --git a/lib/IRremoteESP8266/test/ir_Vestel_test.cpp b/lib/IRremoteESP8266/test/ir_Vestel_test.cpp index d3f1febf8a..cb8d97554c 100644 --- a/lib/IRremoteESP8266/test/ir_Vestel_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Vestel_test.cpp @@ -1,11 +1,11 @@ // Copyright 2019 David Conran -#include "ir_Vestel.h" -#include "IRac.h" -#include "IRrecv.h" -#include "IRrecv_test.h" -#include "IRsend.h" -#include "IRsend_test.h" +#include "../src/ir_Vestel.h" +#include "../src/IRac.h" +#include "../src/IRrecv.h" +#include "../src/IRrecv_test.h" +#include "../src/IRsend.h" +#include "../src/IRsend_test.h" #include "gtest/gtest.h" // Tests for sendVestelAc() diff --git a/lib/IRremoteESP8266/test/ir_Voltas_test.cpp b/lib/IRremoteESP8266/test/ir_Voltas_test.cpp index 38fa9223b2..2a8de3eb1f 100644 --- a/lib/IRremoteESP8266/test/ir_Voltas_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Voltas_test.cpp @@ -1,11 +1,11 @@ // Copyright 2020 crankyoldgit -#include "ir_Voltas.h" -#include "IRac.h" -#include "IRrecv.h" -#include "IRrecv_test.h" -#include "IRsend.h" -#include "IRsend_test.h" +#include "../src/ir_Voltas.h" +#include "../src/IRac.h" +#include "../src/IRrecv.h" +#include "../src/IRrecv_test.h" +#include "../src/IRsend.h" +#include "../src/IRsend_test.h" #include "gtest/gtest.h" // Tests for decodeVoltas(). diff --git a/lib/IRremoteESP8266/test/ir_Whirlpool_test.cpp b/lib/IRremoteESP8266/test/ir_Whirlpool_test.cpp index 4bc295d4f3..c156e81e7f 100644 --- a/lib/IRremoteESP8266/test/ir_Whirlpool_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Whirlpool_test.cpp @@ -1,11 +1,11 @@ // Copyright 2018 David Conran -#include "ir_Whirlpool.h" -#include "IRac.h" -#include "IRrecv.h" -#include "IRrecv_test.h" -#include "IRsend.h" -#include "IRsend_test.h" +#include "../src/ir_Whirlpool.h" +#include "../src/IRac.h" +#include "../src/IRrecv.h" +#include "../src/IRrecv_test.h" +#include "../src/IRsend.h" +#include "../src/IRsend_test.h" #include "gtest/gtest.h" diff --git a/lib/IRremoteESP8266/test/ir_Whynter_test.cpp b/lib/IRremoteESP8266/test/ir_Whynter_test.cpp index eaccf0ea10..e9f532d4b2 100644 --- a/lib/IRremoteESP8266/test/ir_Whynter_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Whynter_test.cpp @@ -1,7 +1,7 @@ // Copyright 2017 David Conran -#include "IRsend.h" -#include "IRsend_test.h" +#include "../src/IRsend.h" +#include "../src/IRsend_test.h" #include "gtest/gtest.h" // Tests for sendWhynter(). diff --git a/lib/IRremoteESP8266/test/ir_Xmp_test.cpp b/lib/IRremoteESP8266/test/ir_Xmp_test.cpp index 796f2ef903..4c998139b8 100644 --- a/lib/IRremoteESP8266/test/ir_Xmp_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Xmp_test.cpp @@ -1,10 +1,10 @@ // Copyright 2021 David Conran -#include "IRac.h" -#include "IRrecv.h" -#include "IRrecv_test.h" -#include "IRsend.h" -#include "IRsend_test.h" +#include "../src/IRac.h" +#include "../src/IRrecv.h" +#include "../src/IRrecv_test.h" +#include "../src/IRsend.h" +#include "../src/IRsend_test.h" #include "gtest/gtest.h" // Tests for sendXmp(). diff --git a/lib/IRremoteESP8266/test/ir_Zepeal_test.cpp b/lib/IRremoteESP8266/test/ir_Zepeal_test.cpp index 12240d2f7c..986a6986c4 100644 --- a/lib/IRremoteESP8266/test/ir_Zepeal_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Zepeal_test.cpp @@ -1,10 +1,10 @@ // Copyright 2020 Christian Nilsson -#include "IRac.h" -#include "IRrecv.h" -#include "IRrecv_test.h" -#include "IRsend.h" -#include "IRsend_test.h" +#include "../src/IRac.h" +#include "../src/IRrecv.h" +#include "../src/IRrecv_test.h" +#include "../src/IRsend.h" +#include "../src/IRsend_test.h" #include "gtest/gtest.h" // Tests for decodeZepeal(). diff --git a/lib/IRremoteESP8266/tools/auto_analyse_raw_data.py b/lib/IRremoteESP8266/tools/auto_analyse_raw_data.py index e061d3c424..5c797aff4d 100644 --- a/lib/IRremoteESP8266/tools/auto_analyse_raw_data.py +++ b/lib/IRremoteESP8266/tools/auto_analyse_raw_data.py @@ -647,9 +647,9 @@ def generate_code(defines, code, bits_str, name="", output=sys.stdout): f"/// @brief Support for {def_name} protocol\n\n" "// Supports:\n" f"// Brand: {def_name}, Model: TODO add device and remote\n\n" - '#include "IRrecv.h"\n' - '#include "IRsend.h"\n' - '#include "IRutils.h"\n\n' + '#include "../src/IRrecv.h"\n' + '#include "../src/IRsend.h"\n' + '#include "../src/IRutils.h"\n\n' "// WARNING: This probably isn't directly usable." " It's a guide only.\n\n" "// See https://github.com/crankyoldgit/IRremoteESP8266/wiki/" diff --git a/lib/IRremoteESP8266/tools/auto_analyse_raw_data_test.py b/lib/IRremoteESP8266/tools/auto_analyse_raw_data_test.py index 1b080c5a8b..6682dcf766 100644 --- a/lib/IRremoteESP8266/tools/auto_analyse_raw_data_test.py +++ b/lib/IRremoteESP8266/tools/auto_analyse_raw_data_test.py @@ -286,9 +286,9 @@ def test_parse_and_report(self): '// Supports:\n' '// Brand: FOO, Model: TODO add device and remote\n' '\n' - '#include "IRrecv.h"\n' - '#include "IRsend.h"\n' - '#include "IRutils.h"\n' + '#include "../src/IRrecv.h"\n' + '#include "../src/IRsend.h"\n' + '#include "../src/IRutils.h"\n' '\n' "// WARNING: This probably isn't directly usable. It's a guide only.\n" '\n' @@ -567,9 +567,9 @@ def test_leader_marks(self): '// Supports:\n' '// Brand: Hitachi, Model: TODO add device and remote\n' '\n' - '#include "IRrecv.h"\n' - '#include "IRsend.h"\n' - '#include "IRutils.h"\n' + '#include "../src/IRrecv.h"\n' + '#include "../src/IRsend.h"\n' + '#include "../src/IRutils.h"\n' '\n' "// WARNING: This probably isn't directly usable. It's a guide only.\n" '\n' @@ -881,9 +881,9 @@ def test_unusual_gaps(self): '// Supports:\n' '// Brand: FOO, Model: TODO add device and remote\n' '\n' - '#include "IRrecv.h"\n' - '#include "IRsend.h"\n' - '#include "IRutils.h"\n' + '#include "../src/IRrecv.h"\n' + '#include "../src/IRsend.h"\n' + '#include "../src/IRutils.h"\n' '\n' "// WARNING: This probably isn't directly usable. It's a guide only.\n" '\n' @@ -1317,9 +1317,9 @@ def test_no_headers(self): '// Supports:\n' '// Brand: TBD, Model: TODO add device and remote\n' '\n' - '#include "IRrecv.h"\n' - '#include "IRsend.h"\n' - '#include "IRutils.h"\n' + '#include "../src/IRrecv.h"\n' + '#include "../src/IRsend.h"\n' + '#include "../src/IRutils.h"\n' '\n' "// WARNING: This probably isn't directly usable. It's a guide only.\n" '\n' diff --git a/lib/IRremoteESP8266/tools/gc_decode.cpp b/lib/IRremoteESP8266/tools/gc_decode.cpp index f1c374bbef..38ab03ba45 100644 --- a/lib/IRremoteESP8266/tools/gc_decode.cpp +++ b/lib/IRremoteESP8266/tools/gc_decode.cpp @@ -7,10 +7,10 @@ #include #include #include -#include "IRac.h" -#include "IRsend.h" -#include "IRsend_test.h" -#include "IRutils.h" +#include "../src/IRac.h" +#include "../src/IRsend.h" +#include "../src/IRsend_test.h" +#include "../src/IRutils.h" const uint16_t kMaxGcCodeLength = 10000; diff --git a/lib/IRremoteESP8266/tools/mode2_decode.cpp b/lib/IRremoteESP8266/tools/mode2_decode.cpp index 63dfa62210..8ba52aa000 100644 --- a/lib/IRremoteESP8266/tools/mode2_decode.cpp +++ b/lib/IRremoteESP8266/tools/mode2_decode.cpp @@ -24,9 +24,9 @@ space 500000 #include #include #include -#include "IRsend.h" -#include "IRsend_test.h" -#include "IRutils.h" +#include "../src/IRsend.h" +#include "../src/IRsend_test.h" +#include "../src/IRutils.h" const uint16_t kMaxGcCodeLength = 10000; diff --git a/platformio.ini b/platformio.ini index 8090915ae0..31baf3cea1 100644 --- a/platformio.ini +++ b/platformio.ini @@ -89,7 +89,7 @@ monitor_speed = 115200 targets = extra_scripts = pre:tools/pio/pre_default_check.py ${extra_scripts_esp8266.extra_scripts} -src_filter = +<*> -<.git/> -<.svn/> - - - - -<*/Commands/> -<*/ControllerQueue/> -<*/DataTypes/> -<*/Globals/> -<*/Helpers/> -<*/PluginStructs/> -<*/WebServer/> +src_filter = +<*> -<.git/> -<.svn/> - - - - -<*/Commands/> -<*/ControllerQueue/> -<*/DataTypes/> -<*/Globals/> -<*/Helpers/> -<*/PluginStructs/> -<*/WebServer/> -<*/IRremoteESP8266/src/> From 8d747d6054891daa25067f958f5b3e3dee85a83d Mon Sep 17 00:00:00 2001 From: TD-er Date: Sat, 13 Nov 2021 22:07:56 +0100 Subject: [PATCH 073/147] [IR build] Fix build issue on IR plugins --- src/src/PluginStructs/P016_data_struct.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/src/PluginStructs/P016_data_struct.h b/src/src/PluginStructs/P016_data_struct.h index 7d9b106aba..4d117db97a 100644 --- a/src/src/PluginStructs/P016_data_struct.h +++ b/src/src/PluginStructs/P016_data_struct.h @@ -56,9 +56,6 @@ struct tCommandLinesV2 { }; -extern String uint64ToString(uint64_t input, - uint8_t base); - struct P016_data_struct : public PluginTaskData_base { public: From 5b74d84b0f2118285bc3e9e860a7be62de3b70a3 Mon Sep 17 00:00:00 2001 From: TD-er Date: Sat, 13 Nov 2021 22:09:15 +0100 Subject: [PATCH 074/147] [WiFi ESP32] Disable WiFi TX power settings for ESP32 (IDF4.4) Changing WiFi TX power may have negative effect on WiFi stability in IDF 4.4 --- src/src/Helpers/StringProvider.cpp | 4 ++++ src/src/Helpers/StringProvider.h | 4 +++- src/src/WebServer/AdvancedConfigPage.cpp | 4 ++++ src/src/WebServer/JSON.cpp | 2 ++ src/src/WebServer/SysInfoPage.cpp | 2 ++ 5 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/src/Helpers/StringProvider.cpp b/src/src/Helpers/StringProvider.cpp index 4816d70f79..851d46c798 100644 --- a/src/src/Helpers/StringProvider.cpp +++ b/src/src/Helpers/StringProvider.cpp @@ -53,10 +53,12 @@ const __FlashStringHelper * getLabel(LabelType::Enum label) { case LabelType::LOAD_PCT: return F("Load"); case LabelType::LOOP_COUNT: return F("Load LC"); case LabelType::CPU_ECO_MODE: return F("CPU Eco Mode"); +#ifdef ESP8266 // TD-er: Disable setting TX power on ESP32 as it seems to cause issues on IDF4.4 case LabelType::WIFI_TX_MAX_PWR: return F("Max WiFi TX Power"); case LabelType::WIFI_CUR_TX_PWR: return F("Current WiFi TX Power"); case LabelType::WIFI_SENS_MARGIN: return F("WiFi Sensitivity Margin"); case LabelType::WIFI_SEND_AT_MAX_TX_PWR:return F("Send With Max TX Power"); +#endif case LabelType::WIFI_NR_EXTRA_SCANS: return F("Extra WiFi scan loops"); case LabelType::WIFI_USE_LAST_CONN_FROM_RTC: return F("Use Last Connected AP from RTC"); @@ -236,10 +238,12 @@ String getValue(LabelType::Enum label) { case LabelType::LOAD_PCT: return toString(getCPUload(), 2); case LabelType::LOOP_COUNT: return String(getLoopCountPerSec()); case LabelType::CPU_ECO_MODE: return jsonBool(Settings.EcoPowerMode()); +#ifdef ESP8266 // TD-er: Disable setting TX power on ESP32 as it seems to cause issues on IDF4.4 case LabelType::WIFI_TX_MAX_PWR: return toString(Settings.getWiFi_TX_power(), 2); case LabelType::WIFI_CUR_TX_PWR: return toString(WiFiEventData.wifi_TX_pwr, 2); case LabelType::WIFI_SENS_MARGIN: return String(Settings.WiFi_sensitivity_margin); case LabelType::WIFI_SEND_AT_MAX_TX_PWR:return jsonBool(Settings.UseMaxTXpowerForSending()); +#endif case LabelType::WIFI_NR_EXTRA_SCANS: return String(Settings.NumberExtraWiFiScans); case LabelType::WIFI_USE_LAST_CONN_FROM_RTC: return jsonBool(Settings.UseLastWiFiFromRTC()); diff --git a/src/src/Helpers/StringProvider.h b/src/src/Helpers/StringProvider.h index 67c7f7a4da..4082277525 100644 --- a/src/src/Helpers/StringProvider.h +++ b/src/src/Helpers/StringProvider.h @@ -23,11 +23,13 @@ struct LabelType { LOAD_PCT, // 15.10 LOOP_COUNT, // 400 CPU_ECO_MODE, // true +#ifdef ESP8266 // TD-er: Disable setting TX power on ESP32 as it seems to cause issues on IDF4.4 WIFI_TX_MAX_PWR, // Unit: 0.25 dBm, 0 = use default (do not set) WIFI_CUR_TX_PWR, // Unit dBm of current WiFi TX power. WIFI_SENS_MARGIN, // Margin in dB on top of sensitivity WIFI_SEND_AT_MAX_TX_PWR, - WIFI_NR_EXTRA_SCANS, +#endif + WIFI_NR_EXTRA_SCANS, WIFI_USE_LAST_CONN_FROM_RTC, FREE_MEM, // 9876 diff --git a/src/src/WebServer/AdvancedConfigPage.cpp b/src/src/WebServer/AdvancedConfigPage.cpp index 3939c7b344..11cf34752f 100644 --- a/src/src/WebServer/AdvancedConfigPage.cpp +++ b/src/src/WebServer/AdvancedConfigPage.cpp @@ -95,9 +95,11 @@ void handle_advanced() { #ifdef SUPPORT_ARP Settings.gratuitousARP(isFormItemChecked(LabelType::PERIODICAL_GRAT_ARP)); #endif // ifdef SUPPORT_ARP +#ifdef ESP8266 // TD-er: Disable setting TX power on ESP32 as it seems to cause issues on IDF4.4 Settings.setWiFi_TX_power(getFormItemFloat(LabelType::WIFI_TX_MAX_PWR)); Settings.WiFi_sensitivity_margin = getFormItemInt(LabelType::WIFI_SENS_MARGIN); Settings.UseMaxTXpowerForSending(isFormItemChecked(LabelType::WIFI_SEND_AT_MAX_TX_PWR)); +#endif Settings.NumberExtraWiFiScans = getFormItemInt(LabelType::WIFI_NR_EXTRA_SCANS); Settings.UseLastWiFiFromRTC(isFormItemChecked(LabelType::WIFI_USE_LAST_CONN_FROM_RTC)); Settings.JSONBoolWithoutQuotes(isFormItemChecked(LabelType::JSON_BOOL_QUOTES)); @@ -259,6 +261,7 @@ void handle_advanced() { #endif // ifdef SUPPORT_ARP addFormCheckBox(LabelType::CPU_ECO_MODE, Settings.EcoPowerMode()); addFormNote(F("Node may miss receiving packets with Eco mode enabled")); +#ifdef ESP8266 // TD-er: Disable setting TX power on ESP32 as it seems to cause issues on IDF4.4 { float maxTXpwr; float threshold = GetRSSIthreshold(maxTXpwr); @@ -278,6 +281,7 @@ void handle_advanced() { addFormNote(note); } addFormCheckBox(LabelType::WIFI_SEND_AT_MAX_TX_PWR, Settings.UseMaxTXpowerForSending()); +#endif { addFormNumericBox(LabelType::WIFI_NR_EXTRA_SCANS, Settings.NumberExtraWiFiScans, 0, 5); String note = F("Number of extra times to scan all channels to have higher chance of finding the desired AP"); diff --git a/src/src/WebServer/JSON.cpp b/src/src/WebServer/JSON.cpp index 4c0cdd191c..7a2e8223eb 100644 --- a/src/src/WebServer/JSON.cpp +++ b/src/src/WebServer/JSON.cpp @@ -236,10 +236,12 @@ void handle_json() LabelType::PERIODICAL_GRAT_ARP, #endif // ifdef SUPPORT_ARP LabelType::CONNECTION_FAIL_THRESH, +#ifdef ESP8266 // TD-er: Disable setting TX power on ESP32 as it seems to cause issues on IDF4.4 LabelType::WIFI_TX_MAX_PWR, LabelType::WIFI_CUR_TX_PWR, LabelType::WIFI_SENS_MARGIN, LabelType::WIFI_SEND_AT_MAX_TX_PWR, +#endif LabelType::WIFI_NR_EXTRA_SCANS, LabelType::WIFI_USE_LAST_CONN_FROM_RTC, LabelType::WIFI_RSSI, diff --git a/src/src/WebServer/SysInfoPage.cpp b/src/src/WebServer/SysInfoPage.cpp index 2a544f5201..ff40c04608 100644 --- a/src/src/WebServer/SysInfoPage.cpp +++ b/src/src/WebServer/SysInfoPage.cpp @@ -492,10 +492,12 @@ void handle_sysinfo_WiFiSettings() { addRowLabelValue(LabelType::PERIODICAL_GRAT_ARP); # endif // ifdef SUPPORT_ARP addRowLabelValue(LabelType::CONNECTION_FAIL_THRESH); +#ifdef ESP8266 // TD-er: Disable setting TX power on ESP32 as it seems to cause issues on IDF4.4 addRowLabelValue(LabelType::WIFI_TX_MAX_PWR); addRowLabelValue(LabelType::WIFI_CUR_TX_PWR); addRowLabelValue(LabelType::WIFI_SENS_MARGIN); addRowLabelValue(LabelType::WIFI_SEND_AT_MAX_TX_PWR); +#endif addRowLabelValue(LabelType::WIFI_NR_EXTRA_SCANS); addRowLabelValue(LabelType::WIFI_USE_LAST_CONN_FROM_RTC); } From fcc2b23f3dec7beb52e6f4d3ccb8ab4ace25a907 Mon Sep 17 00:00:00 2001 From: TD-er Date: Fri, 3 Dec 2021 23:48:25 +0100 Subject: [PATCH 075/147] [ESP32 IDF4.4] Update to the latest Arduino ESP32 2.0.1.1 --- platformio_core_defs.ini | 10 ++++++++++ platformio_esp32_envs.ini | 8 ++++---- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/platformio_core_defs.ini b/platformio_core_defs.ini index c3de139641..23687cca80 100644 --- a/platformio_core_defs.ini +++ b/platformio_core_defs.ini @@ -172,6 +172,11 @@ platform = espressif32 @ 3.3.2 platform_packages = framework-arduinoespressif32 build_flags = -DESP32_STAGE +[core_esp32_3_4_0] +platform = https://github.com/tasmota/platform-espressif32/releases/download/v3.4.0/Tasmota-platform-espressif32.zip +platform_packages = framework-arduinoespressif32 @ https://github.com/tasmota/arduino-esp32/releases/download/2.0.1.1/framework-arduinoespressif32-release_IDF4.4.tar.gz +build_flags = -DESP32_STAGE + [core_esp32_3_3_2_esp32s2] platform = https://github.com/platformio/platform-espressif32.git#feature/arduino-idf-master @@ -179,6 +184,11 @@ platform_packages = framework-arduinoespressif32 @ https://github.com/ta platformio/tool-esptoolpy @ https://github.com/tasmota/esptool/releases/download/v3.2/esptool-v3.2.zip build_flags = -DESP32_STAGE +[core_esp32_3_4_0_esp32s2] +platform = https://github.com/tasmota/platform-espressif32/releases/download/v3.4.0/Tasmota-platform-espressif32.zip +platform_packages = framework-arduinoespressif32 @ https://github.com/tasmota/arduino-esp32/releases/download/2.0.1.1/framework-arduinoespressif32-release_IDF4.4.tar.gz +build_flags = -DESP32_STAGE + [core_esp32_3_0_0] platform = espressif32@3.0.0 diff --git a/platformio_esp32_envs.ini b/platformio_esp32_envs.ini index 17f2bc4353..6d7c75befd 100644 --- a/platformio_esp32_envs.ini +++ b/platformio_esp32_envs.ini @@ -9,7 +9,7 @@ lib_ignore = ESP8266WiFi, ESP8266Ping, ESP8266WebServer, ESP8266H [esp32_common] -extends = common, core_esp32_stage +extends = common, core_esp32_3_4_0 lib_deps = td-er/ESPeasySerial @ 2.0.8, I2Cdevlib-Core, adafruit/Adafruit ILI9341 @ ^1.5.6, Adafruit GFX Library, LOLIN_EPD, adafruit/Adafruit BusIO @ ^1.10.0, VL53L0X @ 1.3.0, SparkFun VL53L1X 4m Laser Distance Sensor @ 1.2.9, td-er/SparkFun MAX1704x Fuel Gauge Arduino Library @ ^1.0.1, ArduinoOTA, ESP32HTTPUpdateServer, FrogmoreScd30, Multi Channel Relay Arduino Library, SparkFun ADXL345 Arduino Library, ITG3205 lib_ignore = ${esp32_always.lib_ignore}, ESP32_ping, IRremoteESP8266, HeatpumpIR board_build.f_flash = 80000000L @@ -19,7 +19,7 @@ board_build.partitions = esp32_partition_app1810k_spiffs316k.csv extra_scripts = post:tools/pio/post_esp32.py ${extra_scripts_default.extra_scripts} build_unflags = -Wall -build_flags = ${core_esp32_3_3_0.build_flags} +build_flags = ${core_esp32_3_4_0.build_flags} ${mqtt_flags.build_flags} -DCONFIG_FREERTOS_ASSERT_DISABLE -DCONFIG_LWIP_ESP_GRATUITOUS_ARP @@ -59,8 +59,8 @@ build_flags = ${esp32_common.build_flags} board = esp32-s2-saola-1 extra_scripts = ${esp32_common.extra_scripts} pre:tools/pio/pre_custom_esp32.py -platform = ${core_esp32_3_3_2_esp32s2.platform} -platform_packages = ${core_esp32_3_3_2_esp32s2.platform_packages} +platform = ${core_esp32_3_4_0_esp32s2.platform} +platform_packages = ${core_esp32_3_4_0_esp32s2.platform_packages} [env:custom_IR_ESP32_4M316k] From ebd3b94764d17d0b3b752847926432d560b33dd5 Mon Sep 17 00:00:00 2001 From: TD-er Date: Sat, 4 Dec 2021 00:01:00 +0100 Subject: [PATCH 076/147] [Cleanup] Use .clear() on strings instead of assigning empty string --- src/_C015.cpp | 2 +- src/_P050_TCS34725.ino | 4 ++-- src/src/ESPEasyCore/ESPEasyRules.cpp | 4 ++-- src/src/PluginStructs/P104_data_struct.cpp | 6 +++--- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/_C015.cpp b/src/_C015.cpp index 8ca412089f..8a47f09372 100644 --- a/src/_C015.cpp +++ b/src/_C015.cpp @@ -194,7 +194,7 @@ bool CPlugin_015(CPlugin::Function function, struct EventStruct *event, String& if (!isvalid) { // send empty string to Blynk in case of error - formattedValue = EMPTY_STRING; + formattedValue.clear(); } String valueName = ExtraTaskSettings.TaskDeviceValueNames[x]; diff --git a/src/_P050_TCS34725.ino b/src/_P050_TCS34725.ino index 302a37cdc5..26c33b831c 100644 --- a/src/_P050_TCS34725.ino +++ b/src/_P050_TCS34725.ino @@ -430,7 +430,7 @@ boolean Plugin_050(uint8_t function, struct EventStruct *event, String& string) RuleEvent += toString(static_cast(b) / t * sRGBFactor, 4); break; default: - RuleEvent = EMPTY_STRING; + RuleEvent.clear(); break; } if (!RuleEvent.isEmpty()) { @@ -466,7 +466,7 @@ boolean Plugin_050(uint8_t function, struct EventStruct *event, String& string) RuleEvent += c; break; default: - RuleEvent = EMPTY_STRING; + RuleEvent.clear(); break; } if (!RuleEvent.isEmpty()) { diff --git a/src/src/ESPEasyCore/ESPEasyRules.cpp b/src/src/ESPEasyCore/ESPEasyRules.cpp index cc246f5082..01d70b2fae 100644 --- a/src/src/ESPEasyCore/ESPEasyRules.cpp +++ b/src/src/ESPEasyCore/ESPEasyRules.cpp @@ -252,7 +252,7 @@ String rulesProcessingFile(const String& fileName, const String& event) { } // Prepare for new line - line = EMPTY_STRING; + line.clear(); line.reserve(longestLineSize); firstNonSpaceRead = false; commentFound = false; @@ -752,7 +752,7 @@ void parseCompleteNonCommentLine(String& line, const String& event, String eventTrigger; - action = EMPTY_STRING; + action.clear(); if (!codeBlock) // do not check "on" rules if a block of actions is to be // processed diff --git a/src/src/PluginStructs/P104_data_struct.cpp b/src/src/PluginStructs/P104_data_struct.cpp index 5aa1ee2f27..cc5058e240 100644 --- a/src/src/PluginStructs/P104_data_struct.cpp +++ b/src/src/PluginStructs/P104_data_struct.cpp @@ -334,7 +334,7 @@ void P104_data_struct::loadSettings() { zones.push_back(P104_zone_struct(zoneIndex + 1)); if (zones[zoneIndex].text == F("\"\"")) { // Special case - zones[zoneIndex].text = EMPTY_STRING; + zones[zoneIndex].text.clear(); } zoneIndex++; @@ -1566,7 +1566,7 @@ String P104_data_struct::enquoteString(const String& input) { * saveSettings gather the zones data from the UI and store in customsettings **************************************/ bool P104_data_struct::saveSettings() { - error = EMPTY_STRING; // Clear + error.clear(); // Clear String zbuffer; # ifdef P104_DEBUG_DEV @@ -1681,7 +1681,7 @@ bool P104_data_struct::saveSettings() { if (zbuffer.reserve(P104_SETTINGS_BUFFER_V2 + 2)) { for (auto it = zones.begin(); it != zones.end() && error.length() == 0; ++it) { - zbuffer = EMPTY_STRING; + zbuffer.clear(); // WARNING: Order of values should match the numeric order of P104_OFFSET_* values zbuffer += it->size; // 2 From 225c53c18772268e961022543885397a5f6ead18 Mon Sep 17 00:00:00 2001 From: TD-er Date: Sat, 4 Dec 2021 00:04:21 +0100 Subject: [PATCH 077/147] [Web] Allow to stream from file system (e.g. CSS inline) This may prevent additional calls to load the CSS from the file system in a separate HTTP GET call and also not loading the file into memory when streaming. --- src/src/WebServer/LoadFromFS.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/src/WebServer/LoadFromFS.cpp b/src/src/WebServer/LoadFromFS.cpp index 81dd288f2a..e10e71de59 100644 --- a/src/src/WebServer/LoadFromFS.cpp +++ b/src/src/WebServer/LoadFromFS.cpp @@ -176,6 +176,10 @@ size_t streamFromFS(String path, bool htmlEscape) { available = 0; } } + + while (f.available()) { + addHtml((char)f.read()); + } statusLED(true); f.close(); From 8ffea04a713c759be69746dc2a5e77724b6a7f38 Mon Sep 17 00:00:00 2001 From: TD-er Date: Sat, 4 Dec 2021 00:48:01 +0100 Subject: [PATCH 078/147] [ESP32-S2] Add PlatformIO envs for ESP32-s2 --- platformio_esp32_envs.ini | 77 ++++++++++++++++++++++++++++++++------- 1 file changed, 63 insertions(+), 14 deletions(-) diff --git a/platformio_esp32_envs.ini b/platformio_esp32_envs.ini index 6d7c75befd..c0b0c816e6 100644 --- a/platformio_esp32_envs.ini +++ b/platformio_esp32_envs.ini @@ -27,6 +27,18 @@ build_flags = ${core_esp32_3_4_0.build_flags} -DCORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_NONE monitor_filters = esp32_exception_decoder + +[esp32s2_common] +extends = esp32_common +build_flags = ${esp32_common.build_flags} + -DESP32S2 + -DBOARD_HAS_PSRAM + -DESP32_ENABLE_PSRAM +board = esp32-s2-saola-1 +platform = ${core_esp32_3_4_0_esp32s2.platform} +platform_packages = ${core_esp32_3_4_0_esp32s2.platform_packages} + + [esp32_LittleFS] extends = esp32_common platform_packages = ${esp32_common.platform_packages} @@ -49,18 +61,11 @@ extra_scripts = ${esp32_common.extra_scripts} ; ESP32-S2 [env:custom_ESP32s2_4M316k] -extends = esp32_common -build_flags = ${esp32_common.build_flags} +extends = esp32s2_common +build_flags = ${esp32s2_common.build_flags} -DPLUGIN_BUILD_CUSTOM - -DESP32S2 - -DBOARD_HAS_PSRAM - -DESP32_ENABLE_PSRAM - -mfix-esp32-psram-cache-issue -board = esp32-s2-saola-1 -extra_scripts = ${esp32_common.extra_scripts} +extra_scripts = ${esp32s2_common.extra_scripts} pre:tools/pio/pre_custom_esp32.py -platform = ${core_esp32_3_4_0_esp32s2.platform} -platform_packages = ${core_esp32_3_4_0_esp32s2.platform_packages} [env:custom_IR_ESP32_4M316k] @@ -374,12 +379,56 @@ platform = ${env:test_D_ESP32-wrover-kit_4M316k.platform} build_flags = ${env:test_D_ESP32-wrover-kit_4M316k.build_flags} -DHAS_ETHERNET -DTESTING_USE_RTTTL -[env:test_E_ESP32-wrover-kit_4M316k_ETH] -extends = env:test_E_ESP32-wrover-kit_4M316k -platform = ${env:test_E_ESP32-wrover-kit_4M316k.platform} -build_flags = ${env:test_E_ESP32-wrover-kit_4M316k.build_flags} -DHAS_ETHERNET +;;; ESP32-s2 *********************************************************** + +[env:normal_ESP32s2_4M316k] +extends = esp32s2_common +lib_deps = ${esp32s2_common.lib_deps}, ServoESP32 +build_flags = ${esp32s2_common.build_flags} + -DFEATURE_ARDUINO_OTA + +[env:test_A_ESP32s2_4M316k] +extends = esp32s2_common +build_flags = ${esp32s2_common.build_flags} + -DFEATURE_ARDUINO_OTA + -DPLUGIN_SET_TEST_ESP32 + -DTESTING_USE_RTTTL + +[env:test_B_ESP32s2_4M316k] +extends = esp32s2_common +build_flags = ${esp32s2_common.build_flags} + -DFEATURE_ARDUINO_OTA + -DPLUGIN_SET_TEST_B_ESP32 + -DTESTING_USE_RTTTL + +[env:test_C_ESP32s2_4M316k] +extends = esp32s2_common +build_flags = ${esp32s2_common.build_flags} + -DFEATURE_ARDUINO_OTA + -DPLUGIN_SET_TEST_C_ESP32 -DTESTING_USE_RTTTL +[env:test_D_ESP32s2_4M316k] +extends = esp32s2_common +build_flags = ${esp32s2_common.build_flags} + -DFEATURE_ARDUINO_OTA + -DPLUGIN_SET_TEST_D_ESP32 + -DTESTING_USE_RTTTL + + +[env:energy_ESP32s2_4M316k] +extends = esp32s2_common +lib_deps = ${esp32s2_common.lib_deps}, ServoESP32 +build_flags = ${esp32s2_common.build_flags} + -DFEATURE_ARDUINO_OTA + -D PLUGIN_ENERGY_COLLECTION + +[env:display_ESP32s2_4M316k] +extends = esp32s2_common +lib_deps = ${esp32s2_common.lib_deps}, ServoESP32 +build_flags = ${esp32s2_common.build_flags} + -DFEATURE_ARDUINO_OTA + -D PLUGIN_DISPLAY_COLLECTION ; A Lolin D32 PRO with 16MB Flash, allowing 4MB sketch size, and file storage using the default (SPIFFS) filesystem [max_ESP32_16M] From 76841a77cf698ce26ead9f9a05357a272779127d Mon Sep 17 00:00:00 2001 From: TD-er Date: Sat, 4 Dec 2021 02:05:08 +0100 Subject: [PATCH 079/147] [IR] Cleanup check for redefine IR plugin USES --- src/src/CustomBuild/define_plugin_sets.h | 25 ++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/src/src/CustomBuild/define_plugin_sets.h b/src/src/CustomBuild/define_plugin_sets.h index 64017e0cf4..7119f0a8d9 100644 --- a/src/src/CustomBuild/define_plugin_sets.h +++ b/src/src/CustomBuild/define_plugin_sets.h @@ -456,9 +456,13 @@ To create/register a plugin, you have to : #if !defined(PLUGIN_DESCR) && !defined(PLUGIN_BUILD_MAX_ESP32) #define PLUGIN_DESCR "IR" #endif - #define USES_P016 // IR + #ifndef USES_P016 + #define USES_P016 // IR + #endif #define P016_SEND_IR_TO_CONTROLLER false //IF true then the JSON replay solution is transmited back to the condroller. - #define USES_P035 // IRTX + #ifndef USES_P035 + #define USES_P035 // IRTX + #endif #define P016_P035_USE_RAW_RAW2 //Use the RAW and RAW2 encodings, disabling it saves 3.7Kb #endif @@ -466,9 +470,13 @@ To create/register a plugin, you have to : #if !defined(PLUGIN_DESCR) && !defined(PLUGIN_BUILD_MAX_ESP32) #define PLUGIN_DESCR "IR Extended" #endif // PLUGIN_DESCR - #define USES_P016 // IR + #ifndef USES_P016 + #define USES_P016 // IR + #endif #define P016_SEND_IR_TO_CONTROLLER false //IF true then the JSON replay solution is transmited back to the condroller. - #define USES_P035 // IRTX + #ifndef USES_P035 + #define USES_P035 // IRTX + #endif // The following define is needed for extended decoding of A/C Messages and or using standardised common arguments for controlling all deeply supported A/C units #define P016_P035_Extended_AC #define P016_P035_USE_RAW_RAW2 //Use the RAW and RAW2 encodings, disabling it saves 3.7Kb @@ -485,7 +493,9 @@ To create/register a plugin, you have to : #if !defined(PLUGIN_DESCR) && !defined(PLUGIN_BUILD_MAX_ESP32) #define PLUGIN_DESCR "IR Extended, no IR RX" #endif // PLUGIN_DESCR - #define USES_P035 // IRTX + #ifndef USES_P035 + #define USES_P035 // IRTX + #endif // The following define is needed for extended decoding of A/C Messages and or using standardised common arguments for controlling all deeply supported A/C units #define P016_P035_Extended_AC #define P016_P035_USE_RAW_RAW2 //Use the RAW and RAW2 encodings, disabling it saves 3.7Kb @@ -612,7 +622,10 @@ To create/register a plugin, you have to : #ifdef PLUGIN_SET_MAGICHOME_IR #define PLUGIN_SET_ONLY_LEDSTRIP - #define USES_P016 // IR + #ifndef USES_P016 + #define USES_P016 // IR + #endif + #endif From 5f6e2bc3ac7c3594a84827ac88d7884539f668b0 Mon Sep 17 00:00:00 2001 From: TD-er Date: Sat, 4 Dec 2021 02:08:18 +0100 Subject: [PATCH 080/147] Revert "[Windows Build] Convert IRremoteESP8266 to single cpp file" This reverts commit ea3418c75bf833b4f2d99bb8b644fac58a02db04. --- lib/IRremoteESP8266/src/IRrecv.h | 2 +- lib/IRremoteESP8266/src/IRsend.h | 2 +- lib/IRremoteESP8266/src/IRtimer.cpp | 78 + lib/IRremoteESP8266/src/IRutils.h | 4 +- lib/IRremoteESP8266/src/ir_Airwell.cpp | 286 ++ lib/IRremoteESP8266/src/ir_Airwell.h | 6 +- lib/IRremoteESP8266/src/ir_Aiwa.cpp | 105 + lib/IRremoteESP8266/src/ir_Amcor.cpp | 354 ++ lib/IRremoteESP8266/src/ir_Amcor.h | 6 +- lib/IRremoteESP8266/src/ir_Argo.cpp | 470 +++ lib/IRremoteESP8266/src/ir_Argo.h | 6 +- lib/IRremoteESP8266/src/ir_Arris.cpp | 123 + lib/IRremoteESP8266/src/ir_Bose.cpp | 69 + lib/IRremoteESP8266/src/ir_Carrier.cpp | 535 +++ lib/IRremoteESP8266/src/ir_Carrier.h | 6 +- lib/IRremoteESP8266/src/ir_Coolix.h | 6 +- lib/IRremoteESP8266/src/ir_Corona.cpp | 575 +++ lib/IRremoteESP8266/src/ir_Corona.h | 6 +- lib/IRremoteESP8266/src/ir_Daikin.cpp | 3735 +++++++++++++++++ lib/IRremoteESP8266/src/ir_Daikin.h | 8 +- lib/IRremoteESP8266/src/ir_Delonghi.cpp | 470 +++ lib/IRremoteESP8266/src/ir_Delonghi.h | 6 +- lib/IRremoteESP8266/src/ir_Denon.cpp | 122 + lib/IRremoteESP8266/src/ir_Dish.cpp | 103 + lib/IRremoteESP8266/src/ir_Doshisha.cpp | 124 + lib/IRremoteESP8266/src/ir_Ecoclim.cpp | 423 ++ lib/IRremoteESP8266/src/ir_Ecoclim.h | 6 +- lib/IRremoteESP8266/src/ir_Electra.h | 6 +- lib/IRremoteESP8266/src/ir_EliteScreens.cpp | 89 + lib/IRremoteESP8266/src/ir_Epson.cpp | 111 + lib/IRremoteESP8266/src/ir_Fujitsu.cpp | 1043 +++++ lib/IRremoteESP8266/src/ir_Fujitsu.h | 8 +- lib/IRremoteESP8266/src/ir_GICable.cpp | 95 + lib/IRremoteESP8266/src/ir_GlobalCache.cpp | 63 + lib/IRremoteESP8266/src/ir_Goodweather.cpp | 499 +++ lib/IRremoteESP8266/src/ir_Goodweather.h | 6 +- lib/IRremoteESP8266/src/ir_Gree.h | 6 +- lib/IRremoteESP8266/src/ir_Haier.h | 6 +- lib/IRremoteESP8266/src/ir_Hitachi.cpp | 1580 +++++++ lib/IRremoteESP8266/src/ir_Hitachi.h | 6 +- lib/IRremoteESP8266/src/ir_Inax.cpp | 73 + lib/IRremoteESP8266/src/ir_JVC.cpp | 131 + lib/IRremoteESP8266/src/ir_Kelon.cpp | 502 +++ lib/IRremoteESP8266/src/ir_Kelon.h | 8 +- lib/IRremoteESP8266/src/ir_Kelvinator.cpp | 525 +++ lib/IRremoteESP8266/src/ir_Kelvinator.h | 6 +- lib/IRremoteESP8266/src/ir_LG.cpp | 838 ++++ lib/IRremoteESP8266/src/ir_LG.h | 8 +- lib/IRremoteESP8266/src/ir_Lasertag.cpp | 116 + lib/IRremoteESP8266/src/ir_Lego.cpp | 106 + lib/IRremoteESP8266/src/ir_Lutron.cpp | 143 + lib/IRremoteESP8266/src/ir_MWM.cpp | 197 + lib/IRremoteESP8266/src/ir_Magiquest.cpp | 154 + lib/IRremoteESP8266/src/ir_Magiquest.h | 4 +- lib/IRremoteESP8266/src/ir_Metz.cpp | 101 + lib/IRremoteESP8266/src/ir_Midea.cpp | 796 ++++ lib/IRremoteESP8266/src/ir_Midea.h | 6 +- lib/IRremoteESP8266/src/ir_MilesTag2.cpp | 113 + lib/IRremoteESP8266/src/ir_Mitsubishi.h | 6 +- .../src/ir_MitsubishiHeavy.cpp | 1050 +++++ lib/IRremoteESP8266/src/ir_MitsubishiHeavy.h | 6 +- lib/IRremoteESP8266/src/ir_Multibrackets.cpp | 115 + lib/IRremoteESP8266/src/ir_NEC.cpp | 140 + lib/IRremoteESP8266/src/ir_NEC.h | 2 +- lib/IRremoteESP8266/src/ir_Neoclima.cpp | 608 +++ lib/IRremoteESP8266/src/ir_Neoclima.h | 6 +- lib/IRremoteESP8266/src/ir_Nikai.cpp | 74 + lib/IRremoteESP8266/src/ir_Panasonic.cpp | 1341 ++++++ lib/IRremoteESP8266/src/ir_Panasonic.h | 6 +- lib/IRremoteESP8266/src/ir_Pioneer.cpp | 138 + lib/IRremoteESP8266/src/ir_Pronto.cpp | 107 + lib/IRremoteESP8266/src/ir_RC5_RC6.cpp | 454 ++ lib/IRremoteESP8266/src/ir_RCMM.cpp | 164 + lib/IRremoteESP8266/src/ir_Rhoss.cpp | 364 ++ lib/IRremoteESP8266/src/ir_Rhoss.h | 6 +- lib/IRremoteESP8266/src/ir_Samsung.h | 6 +- lib/IRremoteESP8266/src/ir_Sanyo.cpp | 978 +++++ lib/IRremoteESP8266/src/ir_Sanyo.h | 6 +- lib/IRremoteESP8266/src/ir_Sharp.cpp | 976 +++++ lib/IRremoteESP8266/src/ir_Sharp.h | 10 +- lib/IRremoteESP8266/src/ir_Sherwood.cpp | 24 + lib/IRremoteESP8266/src/ir_Symphony.cpp | 95 + lib/IRremoteESP8266/src/ir_Tcl.cpp | 529 +++ lib/IRremoteESP8266/src/ir_Tcl.h | 8 +- lib/IRremoteESP8266/src/ir_Technibel.cpp | 408 ++ lib/IRremoteESP8266/src/ir_Technibel.h | 6 +- lib/IRremoteESP8266/src/ir_Teco.cpp | 375 ++ lib/IRremoteESP8266/src/ir_Teco.h | 6 +- lib/IRremoteESP8266/src/ir_Teknopoint.cpp | 75 + lib/IRremoteESP8266/src/ir_Toshiba.h | 6 +- lib/IRremoteESP8266/src/ir_Transcold.cpp | 500 +++ lib/IRremoteESP8266/src/ir_Transcold.h | 6 +- lib/IRremoteESP8266/src/ir_Trotec.cpp | 642 +++ lib/IRremoteESP8266/src/ir_Trotec.h | 6 +- lib/IRremoteESP8266/src/ir_Truma.cpp | 340 ++ lib/IRremoteESP8266/src/ir_Truma.h | 6 +- lib/IRremoteESP8266/src/ir_Vestel.cpp | 572 +++ lib/IRremoteESP8266/src/ir_Vestel.h | 6 +- lib/IRremoteESP8266/src/ir_Voltas.cpp | 516 +++ lib/IRremoteESP8266/src/ir_Voltas.h | 6 +- lib/IRremoteESP8266/src/ir_Whirlpool.cpp | 657 +++ lib/IRremoteESP8266/src/ir_Whirlpool.h | 6 +- lib/IRremoteESP8266/src/ir_Whynter.cpp | 103 + lib/IRremoteESP8266/src/ir_Xmp.cpp | 226 + lib/IRremoteESP8266/src/ir_Zepeal.cpp | 94 + lib/IRremoteESP8266/test/IRrecv_test.cpp | 10 +- lib/IRremoteESP8266/test/IRrecv_test.h | 2 +- lib/IRremoteESP8266/test/IRsend_test.cpp | 8 +- lib/IRremoteESP8266/test/IRsend_test.h | 6 +- lib/IRremoteESP8266/test/IRutils_test.cpp | 10 +- lib/IRremoteESP8266/test/ir_Airwell_test.cpp | 12 +- lib/IRremoteESP8266/test/ir_Aiwa_test.cpp | 4 +- lib/IRremoteESP8266/test/ir_Amcor_test.cpp | 14 +- lib/IRremoteESP8266/test/ir_Argo_test.cpp | 12 +- lib/IRremoteESP8266/test/ir_Arris_test.cpp | 10 +- lib/IRremoteESP8266/test/ir_Bose_test.cpp | 10 +- lib/IRremoteESP8266/test/ir_Carrier_test.cpp | 10 +- lib/IRremoteESP8266/test/ir_Coolix_test.cpp | 8 +- lib/IRremoteESP8266/test/ir_Corona_test.cpp | 12 +- lib/IRremoteESP8266/test/ir_Daikin_test.cpp | 12 +- lib/IRremoteESP8266/test/ir_Delonghi_test.cpp | 14 +- lib/IRremoteESP8266/test/ir_Denon_test.cpp | 4 +- lib/IRremoteESP8266/test/ir_Dish_test.cpp | 4 +- lib/IRremoteESP8266/test/ir_Doshisha_test.cpp | 10 +- lib/IRremoteESP8266/test/ir_Ecoclim_test.cpp | 14 +- lib/IRremoteESP8266/test/ir_Electra_test.cpp | 12 +- .../test/ir_EliteScreens_test.cpp | 6 +- lib/IRremoteESP8266/test/ir_Epson_test.cpp | 10 +- lib/IRremoteESP8266/test/ir_Fujitsu_test.cpp | 10 +- lib/IRremoteESP8266/test/ir_GICable_test.cpp | 4 +- .../test/ir_GlobalCache_test.cpp | 4 +- .../test/ir_Goodweather_test.cpp | 12 +- lib/IRremoteESP8266/test/ir_Gree_test.cpp | 14 +- lib/IRremoteESP8266/test/ir_Haier_test.cpp | 12 +- lib/IRremoteESP8266/test/ir_Hitachi_test.cpp | 14 +- lib/IRremoteESP8266/test/ir_Inax_test.cpp | 6 +- lib/IRremoteESP8266/test/ir_JVC_test.cpp | 4 +- lib/IRremoteESP8266/test/ir_Kelon_test.cpp | 10 +- .../test/ir_Kelvinator_test.cpp | 14 +- lib/IRremoteESP8266/test/ir_LG_test.cpp | 8 +- lib/IRremoteESP8266/test/ir_Lasertag_test.cpp | 4 +- lib/IRremoteESP8266/test/ir_Lego_test.cpp | 8 +- lib/IRremoteESP8266/test/ir_Lutron_test.cpp | 4 +- lib/IRremoteESP8266/test/ir_MWM_test.cpp | 8 +- .../test/ir_Magiquest_test.cpp | 8 +- lib/IRremoteESP8266/test/ir_Metz_test.cpp | 10 +- lib/IRremoteESP8266/test/ir_Midea_test.cpp | 8 +- .../test/ir_Milestag2_test.cpp | 12 +- .../test/ir_MitsubishiHeavy_test.cpp | 14 +- .../test/ir_Mitsubishi_test.cpp | 10 +- .../test/ir_Multibrackets_test.cpp | 10 +- lib/IRremoteESP8266/test/ir_NEC_test.cpp | 4 +- lib/IRremoteESP8266/test/ir_Neoclima_test.cpp | 12 +- lib/IRremoteESP8266/test/ir_Nikai_test.cpp | 4 +- .../test/ir_Panasonic_test.cpp | 16 +- lib/IRremoteESP8266/test/ir_Pioneer_test.cpp | 6 +- lib/IRremoteESP8266/test/ir_Pronto_test.cpp | 4 +- lib/IRremoteESP8266/test/ir_RC5_RC6_test.cpp | 4 +- lib/IRremoteESP8266/test/ir_RCMM_test.cpp | 4 +- lib/IRremoteESP8266/test/ir_Rhoss_test.cpp | 14 +- lib/IRremoteESP8266/test/ir_Samsung_test.cpp | 12 +- lib/IRremoteESP8266/test/ir_Sanyo_test.cpp | 12 +- lib/IRremoteESP8266/test/ir_Sharp_test.cpp | 12 +- lib/IRremoteESP8266/test/ir_Sherwood_test.cpp | 4 +- lib/IRremoteESP8266/test/ir_Sony_test.cpp | 4 +- lib/IRremoteESP8266/test/ir_Symphony_test.cpp | 10 +- lib/IRremoteESP8266/test/ir_Tcl_test.cpp | 12 +- .../test/ir_Technibel_test.cpp | 14 +- lib/IRremoteESP8266/test/ir_Teco_test.cpp | 12 +- .../test/ir_Teknopoint_test.cpp | 10 +- lib/IRremoteESP8266/test/ir_Toshiba_test.cpp | 12 +- .../test/ir_Transcold_test.cpp | 10 +- lib/IRremoteESP8266/test/ir_Trotec_test.cpp | 12 +- lib/IRremoteESP8266/test/ir_Truma_test.cpp | 10 +- lib/IRremoteESP8266/test/ir_Vestel_test.cpp | 12 +- lib/IRremoteESP8266/test/ir_Voltas_test.cpp | 12 +- .../test/ir_Whirlpool_test.cpp | 12 +- lib/IRremoteESP8266/test/ir_Whynter_test.cpp | 4 +- lib/IRremoteESP8266/test/ir_Xmp_test.cpp | 10 +- lib/IRremoteESP8266/test/ir_Zepeal_test.cpp | 10 +- .../tools/auto_analyse_raw_data.py | 6 +- .../tools/auto_analyse_raw_data_test.py | 24 +- lib/IRremoteESP8266/tools/gc_decode.cpp | 8 +- lib/IRremoteESP8266/tools/mode2_decode.cpp | 6 +- platformio.ini | 2 +- 185 files changed, 27008 insertions(+), 496 deletions(-) create mode 100644 lib/IRremoteESP8266/src/IRtimer.cpp create mode 100644 lib/IRremoteESP8266/src/ir_Airwell.cpp create mode 100644 lib/IRremoteESP8266/src/ir_Aiwa.cpp create mode 100644 lib/IRremoteESP8266/src/ir_Amcor.cpp create mode 100644 lib/IRremoteESP8266/src/ir_Argo.cpp create mode 100644 lib/IRremoteESP8266/src/ir_Arris.cpp create mode 100644 lib/IRremoteESP8266/src/ir_Bose.cpp create mode 100644 lib/IRremoteESP8266/src/ir_Carrier.cpp create mode 100644 lib/IRremoteESP8266/src/ir_Corona.cpp create mode 100644 lib/IRremoteESP8266/src/ir_Daikin.cpp create mode 100644 lib/IRremoteESP8266/src/ir_Delonghi.cpp create mode 100644 lib/IRremoteESP8266/src/ir_Denon.cpp create mode 100644 lib/IRremoteESP8266/src/ir_Dish.cpp create mode 100644 lib/IRremoteESP8266/src/ir_Doshisha.cpp create mode 100644 lib/IRremoteESP8266/src/ir_Ecoclim.cpp create mode 100644 lib/IRremoteESP8266/src/ir_EliteScreens.cpp create mode 100644 lib/IRremoteESP8266/src/ir_Epson.cpp create mode 100644 lib/IRremoteESP8266/src/ir_Fujitsu.cpp create mode 100644 lib/IRremoteESP8266/src/ir_GICable.cpp create mode 100644 lib/IRremoteESP8266/src/ir_GlobalCache.cpp create mode 100644 lib/IRremoteESP8266/src/ir_Goodweather.cpp create mode 100644 lib/IRremoteESP8266/src/ir_Hitachi.cpp create mode 100644 lib/IRremoteESP8266/src/ir_Inax.cpp create mode 100644 lib/IRremoteESP8266/src/ir_JVC.cpp create mode 100644 lib/IRremoteESP8266/src/ir_Kelon.cpp create mode 100644 lib/IRremoteESP8266/src/ir_Kelvinator.cpp create mode 100644 lib/IRremoteESP8266/src/ir_LG.cpp create mode 100644 lib/IRremoteESP8266/src/ir_Lasertag.cpp create mode 100644 lib/IRremoteESP8266/src/ir_Lego.cpp create mode 100644 lib/IRremoteESP8266/src/ir_Lutron.cpp create mode 100644 lib/IRremoteESP8266/src/ir_MWM.cpp create mode 100644 lib/IRremoteESP8266/src/ir_Magiquest.cpp create mode 100644 lib/IRremoteESP8266/src/ir_Metz.cpp create mode 100644 lib/IRremoteESP8266/src/ir_Midea.cpp create mode 100644 lib/IRremoteESP8266/src/ir_MilesTag2.cpp create mode 100644 lib/IRremoteESP8266/src/ir_MitsubishiHeavy.cpp create mode 100644 lib/IRremoteESP8266/src/ir_Multibrackets.cpp create mode 100644 lib/IRremoteESP8266/src/ir_NEC.cpp create mode 100644 lib/IRremoteESP8266/src/ir_Neoclima.cpp create mode 100644 lib/IRremoteESP8266/src/ir_Nikai.cpp create mode 100644 lib/IRremoteESP8266/src/ir_Panasonic.cpp create mode 100644 lib/IRremoteESP8266/src/ir_Pioneer.cpp create mode 100644 lib/IRremoteESP8266/src/ir_Pronto.cpp create mode 100644 lib/IRremoteESP8266/src/ir_RC5_RC6.cpp create mode 100644 lib/IRremoteESP8266/src/ir_RCMM.cpp create mode 100644 lib/IRremoteESP8266/src/ir_Rhoss.cpp create mode 100644 lib/IRremoteESP8266/src/ir_Sanyo.cpp create mode 100644 lib/IRremoteESP8266/src/ir_Sharp.cpp create mode 100644 lib/IRremoteESP8266/src/ir_Sherwood.cpp create mode 100644 lib/IRremoteESP8266/src/ir_Symphony.cpp create mode 100644 lib/IRremoteESP8266/src/ir_Tcl.cpp create mode 100644 lib/IRremoteESP8266/src/ir_Technibel.cpp create mode 100644 lib/IRremoteESP8266/src/ir_Teco.cpp create mode 100644 lib/IRremoteESP8266/src/ir_Teknopoint.cpp create mode 100644 lib/IRremoteESP8266/src/ir_Transcold.cpp create mode 100644 lib/IRremoteESP8266/src/ir_Trotec.cpp create mode 100644 lib/IRremoteESP8266/src/ir_Truma.cpp create mode 100644 lib/IRremoteESP8266/src/ir_Vestel.cpp create mode 100644 lib/IRremoteESP8266/src/ir_Voltas.cpp create mode 100644 lib/IRremoteESP8266/src/ir_Whirlpool.cpp create mode 100644 lib/IRremoteESP8266/src/ir_Whynter.cpp create mode 100644 lib/IRremoteESP8266/src/ir_Xmp.cpp create mode 100644 lib/IRremoteESP8266/src/ir_Zepeal.cpp diff --git a/lib/IRremoteESP8266/src/IRrecv.h b/lib/IRremoteESP8266/src/IRrecv.h index 3ae55e9d19..75070172d4 100644 --- a/lib/IRremoteESP8266/src/IRrecv.h +++ b/lib/IRremoteESP8266/src/IRrecv.h @@ -12,7 +12,7 @@ #include #define __STDC_LIMIT_MACROS #include -#include "../src/IRremoteESP8266.h" +#include "IRremoteESP8266.h" // Constants const uint16_t kHeader = 2; // Usual nr. of header entries. diff --git a/lib/IRremoteESP8266/src/IRsend.h b/lib/IRremoteESP8266/src/IRsend.h index fc2722cab6..3696eb40ff 100644 --- a/lib/IRremoteESP8266/src/IRsend.h +++ b/lib/IRremoteESP8266/src/IRsend.h @@ -6,7 +6,7 @@ #define __STDC_LIMIT_MACROS #include -#include "../src/IRremoteESP8266.h" +#include "IRremoteESP8266.h" // Originally from https://github.com/shirriff/Arduino-IRremote/ // Updated by markszabo (https://github.com/crankyoldgit/IRremoteESP8266) for diff --git a/lib/IRremoteESP8266/src/IRtimer.cpp b/lib/IRremoteESP8266/src/IRtimer.cpp new file mode 100644 index 0000000000..e1f098b280 --- /dev/null +++ b/lib/IRremoteESP8266/src/IRtimer.cpp @@ -0,0 +1,78 @@ +// Copyright 2017 David Conran + +#include "IRtimer.h" +#ifndef UNIT_TEST +#include +#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 diff --git a/lib/IRremoteESP8266/src/IRutils.h b/lib/IRremoteESP8266/src/IRutils.h index 51a44e657a..a5dcde0432 100644 --- a/lib/IRremoteESP8266/src/IRutils.h +++ b/lib/IRremoteESP8266/src/IRutils.h @@ -11,8 +11,8 @@ #ifndef ARDUINO #include #endif -#include "../src/IRremoteESP8266.h" -#include "../src/IRrecv.h" +#include "IRremoteESP8266.h" +#include "IRrecv.h" const uint8_t kNibbleSize = 4; const uint8_t kLowNibble = 0; diff --git a/lib/IRremoteESP8266/src/ir_Airwell.cpp b/lib/IRremoteESP8266/src/ir_Airwell.cpp new file mode 100644 index 0000000000..26053442ee --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_Airwell.cpp @@ -0,0 +1,286 @@ +// Copyright 2020 David Conran +#include "ir_Airwell.h" +#include +#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; +} diff --git a/lib/IRremoteESP8266/src/ir_Airwell.h b/lib/IRremoteESP8266/src/ir_Airwell.h index 68e5100dbe..814719f1f1 100644 --- a/lib/IRremoteESP8266/src/ir_Airwell.h +++ b/lib/IRremoteESP8266/src/ir_Airwell.h @@ -17,10 +17,10 @@ #ifndef UNIT_TEST #include #endif -#include "../src/IRremoteESP8266.h" -#include "../src/IRsend.h" +#include "IRremoteESP8266.h" +#include "IRsend.h" #ifdef UNIT_TEST -#include "../src/IRsend_test.h" +#include "IRsend_test.h" #endif /// Native representation of a Airwell A/C message. diff --git a/lib/IRremoteESP8266/src/ir_Aiwa.cpp b/lib/IRremoteESP8266/src/ir_Aiwa.cpp new file mode 100644 index 0000000000..266fe67d68 --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_Aiwa.cpp @@ -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 diff --git a/lib/IRremoteESP8266/src/ir_Amcor.cpp b/lib/IRremoteESP8266/src/ir_Amcor.cpp new file mode 100644 index 0000000000..c2aea5cdd3 --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_Amcor.cpp @@ -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 +#include +#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; +} diff --git a/lib/IRremoteESP8266/src/ir_Amcor.h b/lib/IRremoteESP8266/src/ir_Amcor.h index 21c4ff2604..62ea50d9d8 100644 --- a/lib/IRremoteESP8266/src/ir_Amcor.h +++ b/lib/IRremoteESP8266/src/ir_Amcor.h @@ -17,10 +17,10 @@ #ifndef UNIT_TEST #include #endif -#include "../src/IRremoteESP8266.h" -#include "../src/IRsend.h" +#include "IRremoteESP8266.h" +#include "IRsend.h" #ifdef UNIT_TEST -#include "../src/IRsend_test.h" +#include "IRsend_test.h" #endif diff --git a/lib/IRremoteESP8266/src/ir_Argo.cpp b/lib/IRremoteESP8266/src/ir_Argo.cpp new file mode 100644 index 0000000000..e1df28efbb --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_Argo.cpp @@ -0,0 +1,470 @@ +// Copyright 2017 Schmolders +// Copyright 2019 crankyoldgit +/// @file +/// @brief Argo A/C protocol. +/// Controls an Argo Ulisse 13 DCI A/C + +#include "ir_Argo.h" +#include +#include +#ifndef UNIT_TEST +#include +#endif // UNIT_TEST +#include "IRremoteESP8266.h" +#include "IRtext.h" +#include "IRutils.h" + +// Constants +// using SPACE modulation. MARK is always const 400u +const uint16_t kArgoHdrMark = 6400; +const uint16_t kArgoHdrSpace = 3300; +const uint16_t kArgoBitMark = 400; +const uint16_t kArgoOneSpace = 2200; +const uint16_t kArgoZeroSpace = 900; +const uint32_t kArgoGap = kDefaultMessageGap; // Made up value. Complete guess. + +using irutils::addBoolToString; +using irutils::addIntToString; +using irutils::addLabeledString; +using irutils::addModeToString; +using irutils::addTempToString; + +#if SEND_ARGO +/// Send a Argo A/C formatted message. +/// Status: BETA / Probably works. +/// @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::sendArgo(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 < kArgoStateLength) return; + // TODO(kaschmo): validate + sendGeneric(kArgoHdrMark, kArgoHdrSpace, kArgoBitMark, kArgoOneSpace, + kArgoBitMark, kArgoZeroSpace, 0, 0, // No Footer. + data, nbytes, 38, false, repeat, kDutyDefault); +} +#endif // SEND_ARGO + +/// 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? +IRArgoAC::IRArgoAC(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 IRArgoAC::begin(void) { _irsend.begin(); } + +#if SEND_ARGO +/// Send the current internal state as an IR message. +/// @param[in] repeat Nr. of times the message will be repeated. +void IRArgoAC::send(const uint16_t repeat) { + _irsend.sendArgo(getRaw(), kArgoStateLength, repeat); +} +#endif // SEND_ARGO + +/// 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. +uint8_t IRArgoAC::calcChecksum(const uint8_t state[], const uint16_t length) { + // Corresponds to byte 11 being constant 0b01 + // Only add up bytes to 9. byte 10 is 0b01 constant anyway. + // Assume that argo array is MSB first (left) + return sumBytes(state, length - 2, 2); +} + +/// 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 IRArgoAC::validChecksum(const uint8_t state[], const uint16_t length) { + return ((state[length - 2] >> 2) + (state[length - 1] << 6)) == + IRArgoAC::calcChecksum(state, length); +} + +/// Update the checksum for the internal state. +void IRArgoAC::checksum(void) { + uint8_t sum = IRArgoAC::calcChecksum(_.raw, kArgoStateLength); + // Append sum to end of array + // Set const part of checksum bit 10 + _.raw[10] = 0b00000010; + _.Sum = sum; +} + +/// Reset the internals of the object to a known good state. +void IRArgoAC::stateReset(void) { + for (uint8_t i = 0; i < kArgoStateLength; i++) _.raw[i] = 0x0; + + // Argo Message. Store MSB left. + // Default message: + _.raw[0] = 0b10101100; // LSB first (as sent) 0b00110101; //const preamble + _.raw[1] = 0b11110101; // LSB first: 0b10101111; //const preamble + // Keep payload 2-9 at zero + _.raw[10] = 0b00000010; // Const 01 + _.Sum = 0; + + off(); + setTemp(20); + setRoomTemp(25); + setMode(kArgoAuto); + setFan(kArgoFanAuto); +} + +/// 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* IRArgoAC::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 IRArgoAC::setRaw(const uint8_t state[]) { + std::memcpy(_.raw, state, kArgoStateLength); +} + +/// Set the internal state to have the power on. +void IRArgoAC::on(void) { setPower(true); } + +/// Set the internal state to have the power off. +void IRArgoAC::off(void) { setPower(false); } + +/// Set the internal state to have the desired power. +/// @param[in] on The desired power state. +void IRArgoAC::setPower(const bool on) { + _.Power = on; +} + +/// Get the power setting from the internal state. +/// @return A boolean indicating the power setting. +bool IRArgoAC::getPower(void) const { return _.Power; } + +/// Control the current Max setting. (i.e. Turbo) +/// @param[in] on The desired setting. +void IRArgoAC::setMax(const bool on) { + _.Max = on; +} + +/// Is the Max (i.e. Turbo) setting on? +/// @return The current value. +bool IRArgoAC::getMax(void) const { return _.Max; } + +/// Set the temperature. +/// @param[in] degrees The temperature in degrees celsius. +/// @note Sending 0 equals +4 +void IRArgoAC::setTemp(const uint8_t degrees) { + uint8_t temp = std::max(kArgoMinTemp, degrees); + // delta 4 degrees. "If I want 12 degrees, I need to send 8" + temp = std::min(kArgoMaxTemp, temp) - kArgoTempDelta; + // mask out bits + // argo[13] & 0x00000100; // mask out ON/OFF Bit + _.Temp = temp; +} + +/// Get the current temperature setting. +/// @return The current setting for temp. in degrees celsius. +uint8_t IRArgoAC::getTemp(void) const { + return _.Temp + kArgoTempDelta; +} + +/// Set the speed of the fan. +/// @param[in] fan The desired setting. +void IRArgoAC::setFan(const uint8_t fan) { + _.Fan = std::min(fan, kArgoFan3); +} + +/// Get the current fan speed setting. +/// @return The current fan speed. +uint8_t IRArgoAC::getFan(void) const { + return _.Fan; +} + +/// Set the flap position. i.e. Swing. +/// @warning Not yet working! +/// @param[in] flap The desired setting. +void IRArgoAC::setFlap(const uint8_t flap) { + flap_mode = flap; + // TODO(kaschmo): set correct bits for flap mode +} + +/// Get the flap position. i.e. Swing. +/// @warning Not yet working! +/// @return The current flap setting. +uint8_t IRArgoAC::getFlap(void) const { return flap_mode; } + +/// Get the current operation mode setting. +/// @return The current operation mode. +uint8_t IRArgoAC::getMode(void) const { + return _.Mode; +} + +/// Set the desired operation mode. +/// @param[in] mode The desired operation mode. +void IRArgoAC::setMode(const uint8_t mode) { + switch (mode) { + case kArgoCool: + case kArgoDry: + case kArgoAuto: + case kArgoOff: + case kArgoHeat: + case kArgoHeatAuto: + _.Mode = mode; + return; + default: + _.Mode = kArgoAuto; + } +} + +/// Turn on/off the Night mode. i.e. Sleep. +/// @param[in] on The desired setting. +void IRArgoAC::setNight(const bool on) { + _.Night = on; +} + +/// Get the status of Night mode. i.e. Sleep. +/// @return true if on, false if off. +bool IRArgoAC::getNight(void) const { return _.Night; } + +/// Turn on/off the iFeel mode. +/// @param[in] on The desired setting. +void IRArgoAC::setiFeel(const bool on) { + _.iFeel = on; +} + +/// Get the status of iFeel mode. +/// @return true if on, false if off. +bool IRArgoAC::getiFeel(void) const { return _.iFeel; } + +/// Set the time for the A/C +/// @warning Not yet working! +void IRArgoAC::setTime(void) { + // TODO(kaschmo): use function call from checksum to set time first +} + +/// Set the value for the current room temperature. +/// @param[in] degrees The temperature in degrees celsius. +void IRArgoAC::setRoomTemp(const uint8_t degrees) { + uint8_t temp = std::min(degrees, kArgoMaxRoomTemp); + temp = std::max(temp, kArgoTempDelta) - kArgoTempDelta; + _.RoomTemp = temp; +} + +/// Get the currently stored value for the room temperature setting. +/// @return The current setting for the room temp. in degrees celsius. +uint8_t IRArgoAC::getRoomTemp(void) const { + return _.RoomTemp + kArgoTempDelta; +} + +/// 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 IRArgoAC::convertMode(const stdAc::opmode_t mode) { + switch (mode) { + case stdAc::opmode_t::kCool: + return kArgoCool; + case stdAc::opmode_t::kHeat: + return kArgoHeat; + case stdAc::opmode_t::kDry: + return kArgoDry; + case stdAc::opmode_t::kOff: + return kArgoOff; + // No fan mode. + default: + return kArgoAuto; + } +} + +/// 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 IRArgoAC::convertFan(const stdAc::fanspeed_t speed) { + switch (speed) { + case stdAc::fanspeed_t::kMin: + case stdAc::fanspeed_t::kLow: + return kArgoFan1; + case stdAc::fanspeed_t::kMedium: + return kArgoFan2; + case stdAc::fanspeed_t::kHigh: + case stdAc::fanspeed_t::kMax: + return kArgoFan3; + default: + return kArgoFanAuto; + } +} + +/// 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 IRArgoAC::convertSwingV(const stdAc::swingv_t position) { + switch (position) { + case stdAc::swingv_t::kHighest: + return kArgoFlapFull; + case stdAc::swingv_t::kHigh: + return kArgoFlap5; + case stdAc::swingv_t::kMiddle: + return kArgoFlap4; + case stdAc::swingv_t::kLow: + return kArgoFlap3; + case stdAc::swingv_t::kLowest: + return kArgoFlap1; + default: + return kArgoFlapAuto; + } +} + +/// 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 IRArgoAC::toCommonMode(const uint8_t mode) { + switch (mode) { + case kArgoCool: return stdAc::opmode_t::kCool; + case kArgoHeat: return stdAc::opmode_t::kHeat; + case kArgoDry: return stdAc::opmode_t::kDry; + // No fan mode. + 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 IRArgoAC::toCommonFanSpeed(const uint8_t speed) { + switch (speed) { + case kArgoFan3: return stdAc::fanspeed_t::kMax; + case kArgoFan2: return stdAc::fanspeed_t::kMedium; + case kArgoFan1: 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 IRArgoAC::toCommon(void) const { + stdAc::state_t result; + result.protocol = decode_type_t::ARGO; + result.power = _.Power; + result.mode = toCommonMode(_.Mode); + result.celsius = true; + result.degrees = getTemp(); + result.fanspeed = toCommonFanSpeed(_.Fan); + result.turbo = _.Max; + result.sleep = _.Night ? 0 : -1; + // Not supported. + result.model = -1; // Not supported. + 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 IRArgoAC::toString(void) const { + String result = ""; + result.reserve(100); // Reserve some heap for the string to reduce fragging. + result += addBoolToString(_.Power, kPowerStr, false); + result += addIntToString(_.Mode, kModeStr); + result += kSpaceLBraceStr; + switch (_.Mode) { + case kArgoAuto: + result += kAutoStr; + break; + case kArgoCool: + result += kCoolStr; + break; + case kArgoHeat: + result += kHeatStr; + break; + case kArgoDry: + result += kDryStr; + break; + case kArgoHeatAuto: + result += kHeatStr; + result += ' '; + result += kAutoStr; + break; + case kArgoOff: + result += kOffStr; + break; + default: + result += kUnknownStr; + } + result += ')'; + result += addIntToString(_.Fan, kFanStr); + result += kSpaceLBraceStr; + switch (_.Fan) { + case kArgoFanAuto: + result += kAutoStr; + break; + case kArgoFan3: + result += kMaxStr; + break; + case kArgoFan1: + result += kMinStr; + break; + case kArgoFan2: + result += kMedStr; + break; + default: + result += kUnknownStr; + } + result += ')'; + result += addTempToString(getTemp()); + result += kCommaSpaceStr; + result += kRoomStr; + result += ' '; + result += addTempToString(getRoomTemp(), true, false); + result += addBoolToString(_.Max, kMaxStr); + result += addBoolToString(_.iFeel, kIFeelStr); + result += addBoolToString(_.Night, kNightStr); + return result; +} + +#if DECODE_ARGO +/// Decode the supplied Argo 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 This decoder is based soley off sendArgo(). We have no actual captures +/// to test this against. If you have one of these units, please let us know. +bool IRrecv::decodeArgo(decode_results *results, uint16_t offset, + const uint16_t nbits, + const bool strict) { + if (strict && nbits != kArgoBits) return false; + + // Match Header + Data + if (!matchGeneric(results->rawbuf + offset, results->state, + results->rawlen - offset, nbits, + kArgoHdrMark, kArgoHdrSpace, + kArgoBitMark, kArgoOneSpace, + kArgoBitMark, kArgoZeroSpace, + 0, 0, // Footer (None, allegedly. This seems very wrong.) + true, _tolerance, 0, false)) return false; + + // Compliance + // Verify we got a valid checksum. + if (strict && !IRArgoAC::validChecksum(results->state)) return false; + // Success + results->decode_type = decode_type_t::ARGO; + 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_ARGO diff --git a/lib/IRremoteESP8266/src/ir_Argo.h b/lib/IRremoteESP8266/src/ir_Argo.h index bc1844789a..6ceb58e421 100644 --- a/lib/IRremoteESP8266/src/ir_Argo.h +++ b/lib/IRremoteESP8266/src/ir_Argo.h @@ -11,10 +11,10 @@ #ifndef UNIT_TEST #include #endif -#include "../src/IRremoteESP8266.h" -#include "../src/IRsend.h" +#include "IRremoteESP8266.h" +#include "IRsend.h" #ifdef UNIT_TEST -#include "../src/IRsend_test.h" +#include "IRsend_test.h" #endif diff --git a/lib/IRremoteESP8266/src/ir_Arris.cpp b/lib/IRremoteESP8266/src/ir_Arris.cpp new file mode 100644 index 0000000000..5b39808c8f --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_Arris.cpp @@ -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(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 diff --git a/lib/IRremoteESP8266/src/ir_Bose.cpp b/lib/IRremoteESP8266/src/ir_Bose.cpp new file mode 100644 index 0000000000..a57d125b3c --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_Bose.cpp @@ -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 diff --git a/lib/IRremoteESP8266/src/ir_Carrier.cpp b/lib/IRremoteESP8266/src/ir_Carrier.cpp new file mode 100644 index 0000000000..92fca4bd2c --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_Carrier.cpp @@ -0,0 +1,535 @@ +// Copyright 2018, 2020 David Conran +/// @file +/// @brief Carrier protocols. +/// @see CarrierAc https://github.com/crankyoldgit/IRremoteESP8266/issues/385 +/// @see CarrierAc64 https://github.com/crankyoldgit/IRremoteESP8266/issues/1127 + +#include "ir_Carrier.h" +#include +#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. + + +#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(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; +} diff --git a/lib/IRremoteESP8266/src/ir_Carrier.h b/lib/IRremoteESP8266/src/ir_Carrier.h index 734e702b6d..aa9ea8447c 100644 --- a/lib/IRremoteESP8266/src/ir_Carrier.h +++ b/lib/IRremoteESP8266/src/ir_Carrier.h @@ -20,10 +20,10 @@ #ifndef UNIT_TEST #include #endif -#include "../src/IRremoteESP8266.h" -#include "../src/IRsend.h" +#include "IRremoteESP8266.h" +#include "IRsend.h" #ifdef UNIT_TEST -#include "../src/IRsend_test.h" +#include "IRsend_test.h" #endif /// Native representation of a Carrier A/C message. diff --git a/lib/IRremoteESP8266/src/ir_Coolix.h b/lib/IRremoteESP8266/src/ir_Coolix.h index da2c514cc1..2155b54b05 100644 --- a/lib/IRremoteESP8266/src/ir_Coolix.h +++ b/lib/IRremoteESP8266/src/ir_Coolix.h @@ -34,10 +34,10 @@ #ifndef UNIT_TEST #include #endif -#include "../src/IRremoteESP8266.h" -#include "../src/IRsend.h" +#include "IRremoteESP8266.h" +#include "IRsend.h" #ifdef UNIT_TEST -#include "../src/IRsend_test.h" +#include "IRsend_test.h" #endif // Constants diff --git a/lib/IRremoteESP8266/src/ir_Corona.cpp b/lib/IRremoteESP8266/src/ir_Corona.cpp new file mode 100644 index 0000000000..ef40c241a0 --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_Corona.cpp @@ -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 +#include +#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(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(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; +} diff --git a/lib/IRremoteESP8266/src/ir_Corona.h b/lib/IRremoteESP8266/src/ir_Corona.h index 43d211dfcd..cbe3e99e47 100644 --- a/lib/IRremoteESP8266/src/ir_Corona.h +++ b/lib/IRremoteESP8266/src/ir_Corona.h @@ -20,10 +20,10 @@ #ifndef UNIT_TEST #include #endif -#include "../src/IRremoteESP8266.h" -#include "../src/IRsend.h" +#include "IRremoteESP8266.h" +#include "IRsend.h" #ifdef UNIT_TEST -#include "../src/IRsend_test.h" +#include "IRsend_test.h" #endif /// Native representation of a section of a Corona A/C message. diff --git a/lib/IRremoteESP8266/src/ir_Daikin.cpp b/lib/IRremoteESP8266/src/ir_Daikin.cpp new file mode 100644 index 0000000000..ea11daf781 --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_Daikin.cpp @@ -0,0 +1,3735 @@ +// Copyright 2016 sillyfrog +// Copyright 2017 sillyfrog, crankyoldgit +// Copyright 2018-2021 crankyoldgit +// Copyright 2019 pasna (IRDaikin160 class / Daikin176 class) + +/// @file +/// @brief Support for Daikin A/C protocols. +/// @see Daikin http://harizanov.com/2012/02/control-daikin-air-conditioner-over-the-internet/ +/// @see Daikin https://github.com/mharizanov/Daikin-AC-remote-control-over-the-Internet/tree/master/IRremote +/// @see Daikin http://rdlab.cdmt.vn/project-2013/daikin-ir-protocol +/// @see Daikin https://github.com/blafois/Daikin-IR-Reverse +/// @see Daikin128 https://github.com/crankyoldgit/IRremoteESP8266/issues/827 +/// @see Daikin152 https://github.com/crankyoldgit/IRremoteESP8266/issues/873 +/// @see Daikin152 https://github.com/ToniA/arduino-heatpumpir/blob/master/DaikinHeatpumpARC480A14IR.cpp +/// @see Daikin152 https://github.com/ToniA/arduino-heatpumpir/blob/master/DaikinHeatpumpARC480A14IR.h +/// @see Daikin160 https://github.com/crankyoldgit/IRremoteESP8266/issues/731 +/// @see Daikin2 https://docs.google.com/spreadsheets/d/1f8EGfIbBUo2B-CzUFdrgKQprWakoYNKM80IKZN4KXQE/edit#gid=236366525&range=B25:D32 +/// @see Daikin2 https://github.com/crankyoldgit/IRremoteESP8266/issues/582 +/// @see Daikin2 https://github.com/crankyoldgit/IRremoteESP8266/issues/1535 +/// @see Daikin2 https://www.daikin.co.nz/sites/default/files/daikin-split-system-US7-FTXZ25-50NV1B.pdf +/// @see Daikin216 https://github.com/crankyoldgit/IRremoteESP8266/issues/689 +/// @see Daikin216 https://github.com/danny-source/Arduino_DY_IRDaikin +/// @see Daikin64 https://github.com/crankyoldgit/IRremoteESP8266/issues/1064 + +#include "ir_Daikin.h" +#include +#include +#ifndef ARDUINO +#include +#endif +#include "IRrecv.h" +#include "IRremoteESP8266.h" +#include "IRsend.h" +#ifdef UNIT_TEST +#include "IRsend_test.h" +#endif +#include "IRtext.h" +#include "IRutils.h" + +using irutils::addBoolToString; +using irutils::addDayToString; +using irutils::addIntToString; +using irutils::addLabeledString; +using irutils::addModeToString; +using irutils::addSwingHToString; +using irutils::addSwingVToString; +using irutils::addTempToString; +using irutils::addFanToString; +using irutils::bcdToUint8; +using irutils::minsToString; +using irutils::setBit; +using irutils::setBits; +using irutils::sumNibbles; +using irutils::uint8ToBcd; + +#if SEND_DAIKIN +/// Send a Daikin 280-bit A/C formatted message. +/// Status: STABLE +/// @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. +/// @see https://github.com/mharizanov/Daikin-AC-remote-control-over-the-Internet/tree/master/IRremote +/// @see https://github.com/blafois/Daikin-IR-Reverse +void IRsend::sendDaikin(const unsigned char data[], const uint16_t nbytes, + const uint16_t repeat) { + if (nbytes < kDaikinStateLengthShort) + return; // Not enough bytes to send a proper message. + + for (uint16_t r = 0; r <= repeat; r++) { + uint16_t offset = 0; + // Send the header, 0b00000 + sendGeneric(0, 0, // No header for the header + kDaikinBitMark, kDaikinOneSpace, kDaikinBitMark, + kDaikinZeroSpace, kDaikinBitMark, kDaikinZeroSpace + kDaikinGap, + (uint64_t)0b00000, kDaikinHeaderLength, 38, false, 0, 50); + // Data #1 + if (nbytes < kDaikinStateLength) { // Are we using the legacy size? + // Do this as a constant to save RAM and keep in flash memory + sendGeneric(kDaikinHdrMark, kDaikinHdrSpace, kDaikinBitMark, + kDaikinOneSpace, kDaikinBitMark, kDaikinZeroSpace, + kDaikinBitMark, kDaikinZeroSpace + kDaikinGap, + kDaikinFirstHeader64, 64, 38, false, 0, 50); + } else { // We are using the newer/more correct size. + sendGeneric(kDaikinHdrMark, kDaikinHdrSpace, kDaikinBitMark, + kDaikinOneSpace, kDaikinBitMark, kDaikinZeroSpace, + kDaikinBitMark, kDaikinZeroSpace + kDaikinGap, + data, kDaikinSection1Length, 38, false, 0, 50); + offset += kDaikinSection1Length; + } + // Data #2 + sendGeneric(kDaikinHdrMark, kDaikinHdrSpace, kDaikinBitMark, + kDaikinOneSpace, kDaikinBitMark, kDaikinZeroSpace, + kDaikinBitMark, kDaikinZeroSpace + kDaikinGap, + data + offset, kDaikinSection2Length, 38, false, 0, 50); + offset += kDaikinSection2Length; + // Data #3 + sendGeneric(kDaikinHdrMark, kDaikinHdrSpace, kDaikinBitMark, + kDaikinOneSpace, kDaikinBitMark, kDaikinZeroSpace, + kDaikinBitMark, kDaikinZeroSpace + kDaikinGap, + data + offset, nbytes - offset, 38, false, 0, 50); + } +} +#endif // SEND_DAIKIN + +/// 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? +IRDaikinESP::IRDaikinESP(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 IRDaikinESP::begin(void) { _irsend.begin(); } + +#if SEND_DAIKIN +/// Send the current internal state as an IR message. +/// @param[in] repeat Nr. of times the message will be repeated. +void IRDaikinESP::send(const uint16_t repeat) { + _irsend.sendDaikin(getRaw(), kDaikinStateLength, repeat); +} +#endif // SEND_DAIKIN + +/// 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 IRDaikinESP::validChecksum(uint8_t state[], const uint16_t length) { + // Data #1 + if (length < kDaikinSection1Length || + state[kDaikinByteChecksum1] != sumBytes(state, kDaikinSection1Length - 1)) + return false; + // Data #2 + if (length < kDaikinSection1Length + kDaikinSection2Length || + state[kDaikinByteChecksum2] != sumBytes(state + kDaikinSection1Length, + kDaikinSection2Length - 1)) + return false; + // Data #3 + if (length < kDaikinSection1Length + kDaikinSection2Length + 2 || + state[length - 1] != sumBytes(state + kDaikinSection1Length + + kDaikinSection2Length, + length - (kDaikinSection1Length + + kDaikinSection2Length) - 1)) + return false; + return true; +} + +/// Calculate and set the checksum values for the internal state. +void IRDaikinESP::checksum(void) { + _.Sum1 = sumBytes(_.raw, kDaikinSection1Length - 1); + _.Sum2 = sumBytes(_.raw + kDaikinSection1Length, kDaikinSection2Length - 1); + _.Sum3 = sumBytes(_.raw + kDaikinSection1Length + kDaikinSection2Length, + kDaikinSection3Length - 1); +} + +/// Reset the internal state to a fixed known good state. +void IRDaikinESP::stateReset(void) { + for (uint8_t i = 0; i < kDaikinStateLength; i++) _.raw[i] = 0x0; + + _.raw[0] = 0x11; + _.raw[1] = 0xDA; + _.raw[2] = 0x27; + _.raw[4] = 0xC5; + // _.raw[7] is a checksum byte, it will be set by checksum(). + _.raw[8] = 0x11; + _.raw[9] = 0xDA; + _.raw[10] = 0x27; + _.raw[12] = 0x42; + // _.raw[15] is a checksum byte, it will be set by checksum(). + _.raw[16] = 0x11; + _.raw[17] = 0xDA; + _.raw[18] = 0x27; + _.raw[21] = 0x49; + _.raw[22] = 0x1E; + _.raw[24] = 0xB0; + _.raw[27] = 0x06; + _.raw[28] = 0x60; + _.raw[31] = 0xC0; + // _.raw[34] is a checksum byte, it will be set by checksum(). + checksum(); +} + +/// 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 *IRDaikinESP::getRaw(void) { + checksum(); // Ensure correct settings before sending. + 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 Length of the code in bytes. +void IRDaikinESP::setRaw(const uint8_t new_code[], const uint16_t length) { + uint8_t offset = 0; + if (length == kDaikinStateLengthShort) { // Handle the "short" length case. + offset = kDaikinStateLength - kDaikinStateLengthShort; + stateReset(); + } + for (uint8_t i = 0; i < length && i < kDaikinStateLength; i++) + _.raw[i + offset] = new_code[i]; +} + +/// Change the power setting to On. +void IRDaikinESP::on(void) { setPower(true); } + +/// Change the power setting to Off. +void IRDaikinESP::off(void) { setPower(false); } + +/// Change the power setting. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRDaikinESP::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 IRDaikinESP::getPower(void) const { + return _.Power; +} + +/// Set the temperature. +/// @param[in] temp The temperature in degrees celsius. +void IRDaikinESP::setTemp(const uint8_t temp) { + uint8_t degrees = std::max(temp, kDaikinMinTemp); + degrees = std::min(degrees, kDaikinMaxTemp); + _.Temp = degrees; +} + +/// Get the current temperature setting. +/// @return The current setting for temp. in degrees celsius. +uint8_t IRDaikinESP::getTemp(void) const { return _.Temp; } + +/// Set the speed of the fan. +/// @param[in] fan The desired setting. +/// @note 1-5 or kDaikinFanAuto or kDaikinFanQuiet +void IRDaikinESP::setFan(const uint8_t fan) { + // Set the fan speed bits, leave low 4 bits alone + uint8_t fanset; + if (fan == kDaikinFanQuiet || fan == kDaikinFanAuto) + fanset = fan; + else if (fan < kDaikinFanMin || fan > kDaikinFanMax) + fanset = kDaikinFanAuto; + else + fanset = 2 + fan; + _.Fan = fanset; +} + +/// Get the current fan speed setting. +/// @return The current fan speed. +uint8_t IRDaikinESP::getFan(void) const { + uint8_t fan = _.Fan; + if (fan != kDaikinFanQuiet && fan != kDaikinFanAuto) fan -= 2; + return fan; +} + +/// Get the operating mode setting of the A/C. +/// @return The current operating mode setting. +uint8_t IRDaikinESP::getMode(void) const { + return _.Mode; +} + +/// Set the operating mode of the A/C. +/// @param[in] mode The desired operating mode. +void IRDaikinESP::setMode(const uint8_t mode) { + switch (mode) { + case kDaikinAuto: + case kDaikinCool: + case kDaikinHeat: + case kDaikinFan: + case kDaikinDry: + _.Mode = mode; + break; + default: + _.Mode = kDaikinAuto; + } +} + +/// Set the Vertical Swing mode of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRDaikinESP::setSwingVertical(const bool on) { + _.SwingV = (on ? kDaikinSwingOn : kDaikinSwingOff); +} + +/// Get the Vertical Swing mode of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRDaikinESP::getSwingVertical(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 IRDaikinESP::setSwingHorizontal(const bool on) { + _.SwingH = (on ? kDaikinSwingOn : kDaikinSwingOff); +} + +/// Get the Horizontal Swing mode of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRDaikinESP::getSwingHorizontal(void) const { + return _.SwingH; +} + +/// Set the Quiet mode of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRDaikinESP::setQuiet(const bool on) { + _.Quiet = on; + // Powerful & Quiet mode being on are mutually exclusive. + if (on) setPowerful(false); +} + +/// Get the Quiet mode status of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRDaikinESP::getQuiet(void) const { + return _.Quiet; +} + +/// Set the Powerful (Turbo) mode of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRDaikinESP::setPowerful(const bool on) { + _.Powerful = on; + if (on) { + // Powerful, Quiet, & Econo mode being on are mutually exclusive. + setQuiet(false); + setEcono(false); + } +} + +/// Get the Powerful (Turbo) mode of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRDaikinESP::getPowerful(void) const { + return _.Powerful; +} + +/// Set the Sensor mode of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRDaikinESP::setSensor(const bool on) { + _.Sensor = on; +} + +/// Get the Sensor mode of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRDaikinESP::getSensor(void) const { + return _.Sensor; +} + +/// Set the Economy mode of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRDaikinESP::setEcono(const bool on) { + _.Econo = on; + // Powerful & Econo mode being on are mutually exclusive. + if (on) setPowerful(false); +} + +/// Get the Economical mode of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRDaikinESP::getEcono(void) const { + return _.Econo; +} + +/// Set the Mould mode of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRDaikinESP::setMold(const bool on) { + _.Mold = on; +} + +/// Get the Mould mode status of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRDaikinESP::getMold(void) const { + return _.Mold; +} + +/// Set the Comfort mode of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRDaikinESP::setComfort(const bool on) { + _.Comfort = on; +} + +/// Get the Comfort mode of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRDaikinESP::getComfort(void) const { + return _.Comfort; +} + +/// Set the enable status & time of the On Timer. +/// @param[in] starttime The number of minutes past midnight. +void IRDaikinESP::enableOnTimer(const uint16_t starttime) { + _.OnTimer = true; + _.OnTime = starttime; +} + +/// Clear and disable the On timer. +void IRDaikinESP::disableOnTimer(void) { + _.OnTimer = false; + _.OnTime = kDaikinUnusedTime; +} + +/// Get the On Timer time to be sent to the A/C unit. +/// @return The number of minutes past midnight. +uint16_t IRDaikinESP::getOnTime(void) const { + return _.OnTime; +} + +/// Get the enable status of the On Timer. +/// @return true, the setting is on. false, the setting is off. +bool IRDaikinESP::getOnTimerEnabled(void) const { + return _.OnTimer; +} + +/// Set the enable status & time of the Off Timer. +/// @param[in] endtime The number of minutes past midnight. +void IRDaikinESP::enableOffTimer(const uint16_t endtime) { + _.OffTimer = true; + _.OffTime = endtime; +} + +/// Clear and disable the Off timer. +void IRDaikinESP::disableOffTimer(void) { + _.OffTimer = false; + _.OffTime = kDaikinUnusedTime; +} + +/// Get the Off Timer time to be sent to the A/C unit. +/// @return The number of minutes past midnight. +uint16_t IRDaikinESP::getOffTime(void) const { + return _.OffTime; +} + +/// Get the enable status of the Off Timer. +/// @return true, the setting is on. false, the setting is off. +bool IRDaikinESP::getOffTimerEnabled(void) const { + return _.OffTimer; +} + +/// Set the clock on the A/C unit. +/// @param[in] mins_since_midnight Nr. of minutes past midnight. +void IRDaikinESP::setCurrentTime(const uint16_t mins_since_midnight) { + uint16_t mins = mins_since_midnight; + if (mins > 24 * 60) mins = 0; // If > 23:59, set to 00:00 + _.CurrentTime = mins; +} + +/// Get the clock time to be sent to the A/C unit. +/// @return The number of minutes past midnight. +uint16_t IRDaikinESP::getCurrentTime(void) const { + return _.CurrentTime; +} + +/// Set the current day of the week to be sent to the A/C unit. +/// @param[in] day_of_week The numerical representation of the day of the week. +/// @note 1 is SUN, 2 is MON, ..., 7 is SAT +void IRDaikinESP::setCurrentDay(const uint8_t day_of_week) { + _.CurrentDay = day_of_week; +} + +/// Get the current day of the week to be sent to the A/C unit. +/// @return The numerical representation of the day of the week. +/// @note 1 is SUN, 2 is MON, ..., 7 is SAT +uint8_t IRDaikinESP::getCurrentDay(void) const { + return _.CurrentDay; +} + +/// Set the enable status of the Weekly Timer. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRDaikinESP::setWeeklyTimerEnable(const bool on) { + // Bit is cleared for `on`. + _.WeeklyTimer = !on; +} + +/// Get the enable status of the Weekly Timer. +/// @return true, the setting is on. false, the setting is off. +bool IRDaikinESP::getWeeklyTimerEnable(void) const { + return !_.WeeklyTimer; +} + +/// 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 IRDaikinESP::convertMode(const stdAc::opmode_t mode) { + switch (mode) { + case stdAc::opmode_t::kCool: return kDaikinCool; + case stdAc::opmode_t::kHeat: return kDaikinHeat; + case stdAc::opmode_t::kDry: return kDaikinDry; + case stdAc::opmode_t::kFan: return kDaikinFan; + default: return kDaikinAuto; + } +} + +/// 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 IRDaikinESP::convertFan(const stdAc::fanspeed_t speed) { + switch (speed) { + case stdAc::fanspeed_t::kMin: return kDaikinFanQuiet; + case stdAc::fanspeed_t::kLow: return kDaikinFanMin; + case stdAc::fanspeed_t::kMedium: return kDaikinFanMed; + case stdAc::fanspeed_t::kHigh: return kDaikinFanMax - 1; + case stdAc::fanspeed_t::kMax: return kDaikinFanMax; + default: return kDaikinFanAuto; + } +} + +/// 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 IRDaikinESP::toCommonMode(const uint8_t mode) { + switch (mode) { + case kDaikinCool: return stdAc::opmode_t::kCool; + case kDaikinHeat: return stdAc::opmode_t::kHeat; + case kDaikinDry: return stdAc::opmode_t::kDry; + case kDaikinFan: 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 IRDaikinESP::toCommonFanSpeed(const uint8_t speed) { + switch (speed) { + case kDaikinFanMax: return stdAc::fanspeed_t::kMax; + case kDaikinFanMax - 1: return stdAc::fanspeed_t::kHigh; + case kDaikinFanMed: + case kDaikinFanMin + 1: return stdAc::fanspeed_t::kMedium; + case kDaikinFanMin: return stdAc::fanspeed_t::kLow; + case kDaikinFanQuiet: 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 IRDaikinESP::toCommon(void) const { + stdAc::state_t result; + result.protocol = decode_type_t::DAIKIN; + result.model = -1; // No models used. + result.power = _.Power; + result.mode = toCommonMode(_.Mode); + result.celsius = true; + result.degrees = _.Temp; + result.fanspeed = toCommonFanSpeed(getFan()); + 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 = _.Powerful; + result.clean = _.Mold; + result.econo = _.Econo; + // Not supported. + result.filter = false; + result.light = 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 IRDaikinESP::toString(void) const { + String result = ""; + result.reserve(230); // Reserve some heap for the string to reduce fragging. + result += addBoolToString(_.Power, kPowerStr, false); + result += addModeToString(_.Mode, kDaikinAuto, kDaikinCool, kDaikinHeat, + kDaikinDry, kDaikinFan); + result += addTempToString(_.Temp); + result += addFanToString(getFan(), kDaikinFanMax, kDaikinFanMin, + kDaikinFanAuto, kDaikinFanQuiet, kDaikinFanMed); + result += addBoolToString(_.Powerful, kPowerfulStr); + result += addBoolToString(_.Quiet, kQuietStr); + result += addBoolToString(getSensor(), kSensorStr); + result += addBoolToString(_.Mold, kMouldStr); + result += addBoolToString(_.Comfort, kComfortStr); + result += addBoolToString(_.SwingH, kSwingHStr); + result += addBoolToString(_.SwingV, kSwingVStr); + result += addLabeledString(minsToString(_.CurrentTime), kClockStr); + result += addDayToString(_.CurrentDay, -1); + result += addLabeledString(_.OnTimer + ? minsToString(_.OnTime) : kOffStr, + kOnTimerStr); + result += addLabeledString(_.OffTimer + ? minsToString(_.OffTime) : kOffStr, + kOffTimerStr); + result += addBoolToString(getWeeklyTimerEnable(), kWeeklyTimerStr); + return result; +} + +#if DECODE_DAIKIN +/// Decode the supplied Daikin 280-bit message. (DAIKIN) +/// 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. +/// @see https://github.com/mharizanov/Daikin-AC-remote-control-over-the-Internet/tree/master/IRremote +bool IRrecv::decodeDaikin(decode_results *results, uint16_t offset, + const uint16_t nbits, const bool strict) { + // Is there enough data to match successfully? + if (results->rawlen < (2 * (nbits + kDaikinHeaderLength) + + kDaikinSections * (kHeader + kFooter) + kFooter - 1) + + offset) + return false; + + // Compliance + if (strict && nbits != kDaikinBits) return false; + + match_result_t data_result; + + // Header #1 - Doesn't count as data. + data_result = matchData(&(results->rawbuf[offset]), kDaikinHeaderLength, + kDaikinBitMark, kDaikinOneSpace, + kDaikinBitMark, kDaikinZeroSpace, + kDaikinTolerance, kDaikinMarkExcess, false); + offset += data_result.used; + if (data_result.success == false) return false; // Fail + if (data_result.data) return false; // The header bits should be zero. + // Footer + if (!matchMark(results->rawbuf[offset++], kDaikinBitMark, + kDaikinTolerance, kDaikinMarkExcess)) return false; + if (!matchSpace(results->rawbuf[offset++], kDaikinZeroSpace + kDaikinGap, + kDaikinTolerance, kDaikinMarkExcess)) return false; + // Sections + const uint8_t ksectionSize[kDaikinSections] = { + kDaikinSection1Length, kDaikinSection2Length, kDaikinSection3Length}; + uint16_t pos = 0; + for (uint8_t section = 0; section < kDaikinSections; section++) { + uint16_t used; + // Section Header + Section Data (7 bytes) + Section Footer + used = matchGeneric(results->rawbuf + offset, results->state + pos, + results->rawlen - offset, ksectionSize[section] * 8, + kDaikinHdrMark, kDaikinHdrSpace, + kDaikinBitMark, kDaikinOneSpace, + kDaikinBitMark, kDaikinZeroSpace, + kDaikinBitMark, kDaikinZeroSpace + kDaikinGap, + section >= kDaikinSections - 1, + kDaikinTolerance, kDaikinMarkExcess, false); + if (used == 0) return false; + offset += used; + pos += ksectionSize[section]; + } + // Compliance + if (strict) { + // Re-check we got the correct size/length due to the way we read the data. + if (pos * 8 != kDaikinBits) return false; + // Validate the checksum. + if (!IRDaikinESP::validChecksum(results->state)) return false; + } + + // Success + results->decode_type = DAIKIN; + 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_DAIKIN + +#if SEND_DAIKIN2 +/// Send a Daikin2 (312-bit) A/C formatted message. +/// Status: STABLE / Expected to work. +/// @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. +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/582 +void IRsend::sendDaikin2(const unsigned char data[], const uint16_t nbytes, + const uint16_t repeat) { + if (nbytes < kDaikin2Section1Length) + return; // Not enough bytes to send a partial message. + + for (uint16_t r = 0; r <= repeat; r++) { + // Leader + sendGeneric(kDaikin2LeaderMark, kDaikin2LeaderSpace, + 0, 0, 0, 0, 0, 0, (uint64_t) 0, // No data payload. + 0, kDaikin2Freq, false, 0, 50); + // Section #1 + sendGeneric(kDaikin2HdrMark, kDaikin2HdrSpace, kDaikin2BitMark, + kDaikin2OneSpace, kDaikin2BitMark, kDaikin2ZeroSpace, + kDaikin2BitMark, kDaikin2Gap, data, kDaikin2Section1Length, + kDaikin2Freq, false, 0, 50); + // Section #2 + sendGeneric(kDaikin2HdrMark, kDaikin2HdrSpace, kDaikin2BitMark, + kDaikin2OneSpace, kDaikin2BitMark, kDaikin2ZeroSpace, + kDaikin2BitMark, kDaikin2Gap, data + kDaikin2Section1Length, + nbytes - kDaikin2Section1Length, + kDaikin2Freq, false, 0, 50); + } +} +#endif // SEND_DAIKIN2 + +/// 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? +IRDaikin2::IRDaikin2(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 IRDaikin2::begin(void) { _irsend.begin(); } + +#if SEND_DAIKIN2 +/// Send the current internal state as an IR message. +/// @param[in] repeat Nr. of times the message will be repeated. +void IRDaikin2::send(const uint16_t repeat) { + _irsend.sendDaikin2(getRaw(), kDaikin2StateLength, repeat); +} +#endif // SEND_DAIKIN2 + +/// 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 IRDaikin2::validChecksum(uint8_t state[], const uint16_t length) { + // Validate the checksum of section #1. + if (length <= kDaikin2Section1Length - 1 || + state[kDaikin2Section1Length - 1] != sumBytes(state, + kDaikin2Section1Length - 1)) + return false; + // Validate the checksum of section #2 (a.k.a. the rest) + if (length <= kDaikin2Section1Length + 1 || + state[length - 1] != sumBytes(state + kDaikin2Section1Length, + length - kDaikin2Section1Length - 1)) + return false; + return true; +} + +/// Calculate and set the checksum values for the internal state. +void IRDaikin2::checksum(void) { + _.Sum1 = sumBytes(_.raw, kDaikin2Section1Length - 1); + _.Sum2 = sumBytes(_.raw + kDaikin2Section1Length, kDaikin2Section2Length - 1); +} + +/// Reset the internal state to a fixed known good state. +void IRDaikin2::stateReset(void) { + for (uint8_t i = 0; i < kDaikin2StateLength; i++) _.raw[i] = 0x0; + + _.raw[0] = 0x11; + _.raw[1] = 0xDA; + _.raw[2] = 0x27; + _.raw[4] = 0x01; + _.raw[6] = 0xC0; + _.raw[7] = 0x70; + _.raw[8] = 0x08; + _.raw[9] = 0x0C; + _.raw[10] = 0x80; + _.raw[11] = 0x04; + _.raw[12] = 0xB0; + _.raw[13] = 0x16; + _.raw[14] = 0x24; + _.raw[17] = 0xBE; + _.raw[18] = 0xD0; + // _.raw[19] is a checksum byte, it will be set by checksum(). + _.raw[20] = 0x11; + _.raw[21] = 0xDA; + _.raw[22] = 0x27; + _.raw[25] = 0x08; + _.raw[28] = 0xA0; + _.raw[35] = 0xC1; + _.raw[36] = 0x80; + _.raw[37] = 0x60; + // _.raw[38] is a checksum byte, it will be set by checksum(). + disableOnTimer(); + disableOffTimer(); + disableSleepTimer(); + checksum(); +} + +/// 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 *IRDaikin2::getRaw(void) { + checksum(); // Ensure correct settings before sending. + return _.raw; +} + +/// Set the internal state from a valid code for this protocol. +/// @param[in] new_code A valid code for this protocol. +void IRDaikin2::setRaw(const uint8_t new_code[]) { + std::memcpy(_.raw, new_code, kDaikin2StateLength); +} + +/// Change the power setting to On. +void IRDaikin2::on(void) { setPower(true); } + +/// Change the power setting to Off. +void IRDaikin2::off(void) { setPower(false); } + +/// Change the power setting. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRDaikin2::setPower(const bool on) { + _.Power = on; + _.Power2 = !on; +} + +/// Get the value of the current power setting. +/// @return true, the setting is on. false, the setting is off. +bool IRDaikin2::getPower(void) const { return _.Power && !_.Power2; } + +/// Get the operating mode setting of the A/C. +/// @return The current operating mode setting. +uint8_t IRDaikin2::getMode(void) const { return _.Mode; } + +/// Set the operating mode of the A/C. +/// @param[in] desired_mode The desired operating mode. +void IRDaikin2::setMode(const uint8_t desired_mode) { + uint8_t mode = desired_mode; + switch (mode) { + case kDaikinCool: + case kDaikinHeat: + case kDaikinFan: + case kDaikinDry: break; + default: mode = kDaikinAuto; + } + _.Mode = mode; + // Redo the temp setting as Cool mode has a different min temp. + if (mode == kDaikinCool) setTemp(getTemp()); + setHumidity(getHumidity()); // Make sure the humidity is okay for this mode. +} + +/// Set the temperature. +/// @param[in] desired The temperature in degrees celsius. +void IRDaikin2::setTemp(const uint8_t desired) { + // The A/C has a different min temp if in cool mode. + uint8_t temp = std::max( + (_.Mode == kDaikinCool) ? kDaikin2MinCoolTemp : kDaikinMinTemp, + desired); + _.Temp = std::min(kDaikinMaxTemp, temp); + // If the humidity setting is in use, the temp is a fixed value. + if (_.HumidOn) _.Temp = kDaikinMaxTemp; +} + +/// Get the current temperature setting. +/// @return The current setting for temp. in degrees celsius. +uint8_t IRDaikin2::getTemp(void) const { return _.Temp; } + +/// Set the speed of the fan. +/// @param[in] fan The desired setting. +/// @note 1-5 or kDaikinFanAuto or kDaikinFanQuiet +void IRDaikin2::setFan(const uint8_t fan) { + uint8_t fanset; + if (fan == kDaikinFanQuiet || fan == kDaikinFanAuto) + fanset = fan; + else if (fan < kDaikinFanMin || fan > kDaikinFanMax) + fanset = kDaikinFanAuto; + else + fanset = 2 + fan; + _.Fan = fanset; +} + +/// Get the current fan speed setting. +/// @return The current fan speed. +uint8_t IRDaikin2::getFan(void) const { + const uint8_t fan = _.Fan; + switch (fan) { + case kDaikinFanAuto: + case kDaikinFanQuiet: return fan; + default: return fan - 2; + } +} + +/// Set the Vertical Swing mode of the A/C. +/// @param[in] position The position/mode to set the swing to. +void IRDaikin2::setSwingVertical(const uint8_t position) { + switch (position) { + case kDaikin2SwingVHighest: + case kDaikin2SwingVHigh: + case kDaikin2SwingVUpperMiddle: + case kDaikin2SwingVLowerMiddle: + case kDaikin2SwingVLow: + case kDaikin2SwingVLowest: + case kDaikin2SwingVOff: + case kDaikin2SwingVBreeze: + case kDaikin2SwingVCirculate: + case kDaikin2SwingVAuto: + _.SwingV = position; + } +} + +/// Get the Vertical Swing mode of the A/C. +/// @return The native position/mode setting. +uint8_t IRDaikin2::getSwingVertical(void) const { return _.SwingV; } + +/// 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 IRDaikin2::convertSwingV(const stdAc::swingv_t position) { + switch (position) { + case stdAc::swingv_t::kHighest: + case stdAc::swingv_t::kHigh: + case stdAc::swingv_t::kMiddle: + case stdAc::swingv_t::kLow: + case stdAc::swingv_t::kLowest: + return (uint8_t)position + kDaikin2SwingVHighest; + case stdAc::swingv_t::kOff: + return kDaikin2SwingVOff; + default: + return kDaikin2SwingVAuto; + } +} + +/// Convert a native vertical swing postion to it's common equivalent. +/// @param[in] setting A native position to convert. +/// @return The common vertical swing position. +stdAc::swingv_t IRDaikin2::toCommonSwingV(const uint8_t setting) { + switch (setting) { + case kDaikin2SwingVHighest: return stdAc::swingv_t::kHighest; + case kDaikin2SwingVHigh: return stdAc::swingv_t::kHigh; + case kDaikin2SwingVUpperMiddle: + case kDaikin2SwingVLowerMiddle: return stdAc::swingv_t::kMiddle; + case kDaikin2SwingVLow: return stdAc::swingv_t::kLow; + case kDaikin2SwingVLowest: return stdAc::swingv_t::kLowest; + case kDaikin2SwingVOff: return stdAc::swingv_t::kOff; + default: return stdAc::swingv_t::kAuto; + } +} + +/// Set the Horizontal Swing mode of the A/C. +/// @param[in] position The position/mode to set the swing to. +void IRDaikin2::setSwingHorizontal(const uint8_t position) { + _.SwingH = position; +} + +/// Get the Horizontal Swing mode of the A/C. +/// @return The native position/mode setting. +uint8_t IRDaikin2::getSwingHorizontal(void) const { return _.SwingH; } + +/// Set the clock on the A/C unit. +/// @param[in] numMins Nr. of minutes past midnight. +void IRDaikin2::setCurrentTime(const uint16_t numMins) { + uint16_t mins = numMins; + if (numMins > 24 * 60) mins = 0; // If > 23:59, set to 00:00 + _.CurrentTime = mins; +} + +/// Get the clock time to be sent to the A/C unit. +/// @return The number of minutes past midnight. +uint16_t IRDaikin2::getCurrentTime(void) const { return _.CurrentTime; } + +/// Set the enable status & time of the On Timer. +/// @param[in] starttime The number of minutes past midnight. +/// @note Timer location is shared with sleep timer. +void IRDaikin2::enableOnTimer(const uint16_t starttime) { + clearSleepTimerFlag(); + _.OnTimer = true; + _.OnTime = starttime; +} + +/// Clear the On Timer flag. +void IRDaikin2::clearOnTimerFlag(void) { _.OnTimer = false; } + +/// Disable the On timer. +void IRDaikin2::disableOnTimer(void) { + _.OnTime = kDaikinUnusedTime; + clearOnTimerFlag(); + clearSleepTimerFlag(); +} + +/// Get the On Timer time to be sent to the A/C unit. +/// @return The number of minutes past midnight. +uint16_t IRDaikin2::getOnTime(void) const { return _.OnTime; } + +/// Get the enable status of the On Timer. +/// @return true, the setting is on. false, the setting is off. +bool IRDaikin2::getOnTimerEnabled(void) const { return _.OnTimer; } + +/// Set the enable status & time of the Off Timer. +/// @param[in] endtime The number of minutes past midnight. +void IRDaikin2::enableOffTimer(const uint16_t endtime) { + // Set the Off Timer flag. + _.OffTimer = true; + _.OffTime = endtime; +} + +/// Disable the Off timer. +void IRDaikin2::disableOffTimer(void) { + _.OffTime = kDaikinUnusedTime; + // Clear the Off Timer flag. + _.OffTimer = false; +} + +/// Get the Off Timer time to be sent to the A/C unit. +/// @return The number of minutes past midnight. +uint16_t IRDaikin2::getOffTime(void) const { return _.OffTime; } + +/// Get the enable status of the Off Timer. +/// @return true, the setting is on. false, the setting is off. +bool IRDaikin2::getOffTimerEnabled(void) const { return _.OffTimer; } + +/// Get the Beep status of the A/C. +/// @return true, the setting is on. false, the setting is off. +uint8_t IRDaikin2::getBeep(void) const { return _.Beep; } + +/// Set the Beep mode of the A/C. +/// @param[in] beep true, the setting is on. false, the setting is off. +void IRDaikin2::setBeep(const uint8_t beep) { _.Beep = beep; } + +/// Get the Light status of the A/C. +/// @return true, the setting is on. false, the setting is off. +uint8_t IRDaikin2::getLight(void) const { return _.Light; } + +/// Set the Light (LED) mode of the A/C. +/// @param[in] light true, the setting is on. false, the setting is off. +void IRDaikin2::setLight(const uint8_t light) { _.Light = light; } + +/// Set the Mould (filter) mode of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRDaikin2::setMold(const bool on) { _.Mold = on; } + +/// Get the Mould (filter) mode status of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRDaikin2::getMold(void) const { return _.Mold; } + +/// Set the Auto clean mode of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRDaikin2::setClean(const bool on) { _.Clean = on; } + +/// Get the Auto Clean mode status of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRDaikin2::getClean(void) const { return _.Clean; } + +/// Set the Fresh Air mode of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRDaikin2::setFreshAir(const bool on) { _.FreshAir = on; } + +/// Get the Fresh Air mode status of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRDaikin2::getFreshAir(void) const { return _.FreshAir; } + +/// Set the (High) Fresh Air mode of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRDaikin2::setFreshAirHigh(const bool on) { _.FreshAirHigh = on; } + +/// Get the (High) Fresh Air mode status of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRDaikin2::getFreshAirHigh(void) const { return _.FreshAirHigh; } + +/// Set the Automatic Eye (Sensor) mode of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRDaikin2::setEyeAuto(bool on) { _.EyeAuto = on; } + +/// Get the Automaitc Eye (Sensor) mode status of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRDaikin2::getEyeAuto(void) const { return _.EyeAuto; } + +/// Set the Eye (Sensor) mode of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRDaikin2::setEye(bool on) { _.Eye = on; } + +/// Get the Eye (Sensor) mode status of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRDaikin2::getEye(void) const { return _.Eye; } + +/// Set the Economy mode of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRDaikin2::setEcono(bool on) { _.Econo = on; } + +/// Get the Economical mode of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRDaikin2::getEcono(void) const { return _.Econo; } + +/// Set the enable status & time of the Sleep Timer. +/// @param[in] sleeptime The number of minutes past midnight. +/// @note The Timer location is shared with On Timer. +void IRDaikin2::enableSleepTimer(const uint16_t sleeptime) { + enableOnTimer(sleeptime); + clearOnTimerFlag(); + _.SleepTimer = true; +} + +/// Clear the sleep timer flag. +void IRDaikin2::clearSleepTimerFlag(void) { _.SleepTimer = false; } + +/// Disable the sleep timer. +void IRDaikin2::disableSleepTimer(void) { disableOnTimer(); } + +/// Get the Sleep Timer time to be sent to the A/C unit. +/// @return The number of minutes past midnight. +uint16_t IRDaikin2::getSleepTime(void) const { return getOnTime(); } + +/// Get the Sleep timer enabled status of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRDaikin2::getSleepTimerEnabled(void) const { return _.SleepTimer; } + +/// Set the Quiet mode of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRDaikin2::setQuiet(const bool on) { + _.Quiet = on; + // Powerful & Quiet mode being on are mutually exclusive. + if (on) setPowerful(false); +} + +/// Get the Quiet mode status of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRDaikin2::getQuiet(void) const { return _.Quiet; } + +/// Set the Powerful (Turbo) mode of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRDaikin2::setPowerful(const bool on) { + _.Powerful = on; + // Powerful & Quiet mode being on are mutually exclusive. + if (on) setQuiet(false); +} + +/// Get the Powerful (Turbo) mode of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRDaikin2::getPowerful(void) const { return _.Powerful; } + +/// Set the Purify (Filter) mode of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRDaikin2::setPurify(const bool on) { _.Purify = on; } + +/// Get the Purify (Filter) mode status of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRDaikin2::getPurify(void) const { return _.Purify; } + +/// Get the Humidity percentage setting of the A/C. +/// @return The setting percentage. 255 is Automatic. 0 is Off. +uint8_t IRDaikin2::getHumidity(void) const { return _.Humidity; } + +/// Set the Humidity percentage setting of the A/C. +/// @param[in] percent Percentage humidty. 255 is Auto. 0 is Off. +/// @note Only available in Dry & Heat modes, otherwise it is Off. +void IRDaikin2::setHumidity(const uint8_t percent) { + _.Humidity = kDaikin2HumidityOff; // Default to off. + switch (getMode()) { + case kDaikinHeat: + switch (percent) { + case kDaikin2HumidityOff: + case kDaikin2HumidityHeatLow: + case kDaikin2HumidityHeatMedium: + case kDaikin2HumidityHeatHigh: + case kDaikin2HumidityAuto: + _.Humidity = percent; + } + break; + case kDaikinDry: + switch (percent) { + case kDaikin2HumidityOff: + case kDaikin2HumidityDryLow: + case kDaikin2HumidityDryMedium: + case kDaikin2HumidityDryHigh: + case kDaikin2HumidityAuto: + _.Humidity = percent; + } + break; + } + _.HumidOn = (_.Humidity != kDaikin2HumidityOff); // Enabled? + setTemp(getTemp()); // Adjust the temperature if we need to. +} + +/// 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 IRDaikin2::convertMode(const stdAc::opmode_t mode) { + return IRDaikinESP::convertMode(mode); +} + +/// 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 IRDaikin2::convertFan(const stdAc::fanspeed_t speed) { + return IRDaikinESP::convertFan(speed); +} + +/// Convert a stdAc::swingh_t enum into it's native setting. +/// @param[in] position The enum to be converted. +/// @return The native equivalent of the enum. +uint8_t IRDaikin2::convertSwingH(const stdAc::swingh_t position) { + switch (position) { + case stdAc::swingh_t::kAuto: return kDaikin2SwingHSwing; + case stdAc::swingh_t::kLeftMax: return kDaikin2SwingHLeftMax; + case stdAc::swingh_t::kLeft: return kDaikin2SwingHLeft; + case stdAc::swingh_t::kMiddle: return kDaikin2SwingHMiddle; + case stdAc::swingh_t::kRight: return kDaikin2SwingHRight; + case stdAc::swingh_t::kRightMax: return kDaikin2SwingHRightMax; + case stdAc::swingh_t::kWide: return kDaikin2SwingHWide; + default: return kDaikin2SwingHAuto; + } +} + +/// Convert a native horizontal swing postion to it's common equivalent. +/// @param[in] setting A native position to convert. +/// @return The common horizontal swing position. +stdAc::swingh_t IRDaikin2::toCommonSwingH(const uint8_t setting) { + switch (setting) { + case kDaikin2SwingHSwing: return stdAc::swingh_t::kAuto; + case kDaikin2SwingHLeftMax: return stdAc::swingh_t::kLeftMax; + case kDaikin2SwingHLeft: return stdAc::swingh_t::kLeft; + case kDaikin2SwingHMiddle: return stdAc::swingh_t::kMiddle; + case kDaikin2SwingHRight: return stdAc::swingh_t::kRight; + case kDaikin2SwingHRightMax: return stdAc::swingh_t::kRightMax; + case kDaikin2SwingHWide: return stdAc::swingh_t::kWide; + 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 IRDaikin2::toCommon(void) const { + stdAc::state_t result; + result.protocol = decode_type_t::DAIKIN2; + result.model = -1; // No models used. + result.power = getPower(); + result.mode = IRDaikinESP::toCommonMode(_.Mode); + result.celsius = true; + result.degrees = _.Temp; + result.fanspeed = IRDaikinESP::toCommonFanSpeed(getFan()); + result.swingv = toCommonSwingV(_.SwingV); + result.swingh = toCommonSwingH(_.SwingH); + result.quiet = _.Quiet; + result.light = _.Light != 3; // 3 is Off, everything else is On. + result.turbo = _.Powerful; + result.clean = _.Mold; + result.econo = _.Econo; + result.filter = _.Purify; + result.beep = _.Beep != 3; // 3 is Off, everything else is On. + result.sleep = _.SleepTimer ? getSleepTime() : -1; + // Not supported. + result.clock = -1; + return result; +} + +/// Convert the current internal state into a human readable string. +/// @return A human readable string. +String IRDaikin2::toString(void) const { + String result = ""; + result.reserve(330); // Reserve some heap for the string to reduce fragging. + result += addBoolToString(getPower(), kPowerStr, false); + result += addModeToString(_.Mode, kDaikinAuto, kDaikinCool, kDaikinHeat, + kDaikinDry, kDaikinFan); + result += addTempToString(_.Temp); + result += addFanToString(getFan(), kDaikinFanMax, kDaikinFanMin, + kDaikinFanAuto, kDaikinFanQuiet, kDaikinFanMed); + result += addSwingVToString(_.SwingV, kDaikin2SwingVAuto, + kDaikin2SwingVHighest, kDaikin2SwingVHigh, + kDaikin2SwingVUpperMiddle, + kDaikin2SwingVAuto, // Middle is unused. + kDaikin2SwingVLowerMiddle, + kDaikin2SwingVLow, kDaikin2SwingVLowest, + kDaikin2SwingVOff, // Off is unused + kDaikin2SwingVSwing, kDaikin2SwingVBreeze, + kDaikin2SwingVCirculate); + result += addSwingHToString(_.SwingH, kDaikin2SwingHAuto, + kDaikin2SwingHLeftMax, + kDaikin2SwingHLeft, + kDaikin2SwingHMiddle, + kDaikin2SwingHRight, + kDaikin2SwingHRightMax, + kDaikin2SwingHOff, + kDaikin2SwingHAuto, // Unused + kDaikin2SwingHAuto, // Unused + kDaikin2SwingHAuto, // Unused + kDaikin2SwingHWide); + result += addLabeledString(minsToString(_.CurrentTime), kClockStr); + result += addLabeledString( + _.OnTimer ? minsToString(_.OnTime) : kOffStr, kOnTimerStr); + result += addLabeledString( + _.OffTimer ? minsToString(_.OffTime) : kOffStr, + kOffTimerStr); + result += addLabeledString( + _.SleepTimer ? minsToString(getSleepTime()) : kOffStr, + kSleepTimerStr); + result += addIntToString(_.Beep, kBeepStr); + result += kSpaceLBraceStr; + switch (_.Beep) { + case kDaikinBeepLoud: + result += kLoudStr; + break; + case kDaikinBeepQuiet: + result += kQuietStr; + break; + case kDaikinBeepOff: + result += kOffStr; + break; + default: + result += kUnknownStr; + } + result += ')'; + result += addIntToString(_.Light, kLightStr); + result += kSpaceLBraceStr; + switch (_.Light) { + case kDaikinLightBright: + result += kHighStr; + break; + case kDaikinLightDim: + result += kLowStr; + break; + case kDaikinLightOff: + result += kOffStr; + break; + default: + result += kUnknownStr; + } + result += ')'; + result += addBoolToString(_.Mold, kMouldStr); + result += addBoolToString(_.Clean, kCleanStr); + result += addLabeledString( + _.FreshAir ? (_.FreshAirHigh ? kHighStr : kOnStr) : kOffStr, + kFreshStr); + result += addBoolToString(_.Eye, kEyeStr); + result += addBoolToString(_.EyeAuto, kEyeAutoStr); + result += addBoolToString(_.Quiet, kQuietStr); + result += addBoolToString(_.Powerful, kPowerfulStr); + result += addBoolToString(_.Purify, kPurifyStr); + result += addBoolToString(_.Econo, kEconoStr); + result += addIntToString(_.Humidity, kHumidStr); + switch (_.Humidity) { + case kDaikin2HumidityOff: + case kDaikin2HumidityAuto: + result += kSpaceLBraceStr; + result += _.Humidity ? kAutoStr : kOffStr; + result += ')'; + break; + default: + result += '%'; + } + return result; +} + +#if DECODE_DAIKIN2 +/// Decode the supplied Daikin 312-bit message. (DAIKIN2) +/// Status: STABLE / Works as expected. +/// @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::decodeDaikin2(decode_results *results, uint16_t offset, + const uint16_t nbits, const bool strict) { + if (results->rawlen < 2 * (nbits + kHeader + kFooter) + kHeader - 1 + offset) + return false; + + // Compliance + if (strict && nbits != kDaikin2Bits) return false; + + const uint8_t ksectionSize[kDaikin2Sections] = {kDaikin2Section1Length, + kDaikin2Section2Length}; + + // Leader + if (!matchMark(results->rawbuf[offset++], kDaikin2LeaderMark, + _tolerance + kDaikin2Tolerance)) return false; + if (!matchSpace(results->rawbuf[offset++], kDaikin2LeaderSpace, + _tolerance + kDaikin2Tolerance)) return false; + + // Sections + uint16_t pos = 0; + for (uint8_t section = 0; section < kDaikin2Sections; section++) { + uint16_t used; + // Section Header + Section Data + Section Footer + used = matchGeneric(results->rawbuf + offset, results->state + pos, + results->rawlen - offset, ksectionSize[section] * 8, + kDaikin2HdrMark, kDaikin2HdrSpace, + kDaikin2BitMark, kDaikin2OneSpace, + kDaikin2BitMark, kDaikin2ZeroSpace, + kDaikin2BitMark, kDaikin2Gap, + section >= kDaikin2Sections - 1, + _tolerance + kDaikin2Tolerance, kDaikinMarkExcess, + false); + if (used == 0) return false; + offset += used; + pos += ksectionSize[section]; + } + // Compliance + if (strict) { + // Re-check we got the correct size/length due to the way we read the data. + if (pos * 8 != kDaikin2Bits) return false; + // Validate the checksum. + if (!IRDaikin2::validChecksum(results->state)) return false; + } + + // Success + results->decode_type = DAIKIN2; + 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_DAIKIN2 + +#if SEND_DAIKIN216 +/// Send a Daikin216 (216-bit) A/C formatted message. +/// Status: Alpha / Untested on a real device. +/// @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. +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/689 +/// @see https://github.com/danny-source/Arduino_DY_IRDaikin +void IRsend::sendDaikin216(const unsigned char data[], const uint16_t nbytes, + const uint16_t repeat) { + if (nbytes < kDaikin216Section1Length) + return; // Not enough bytes to send a partial message. + + for (uint16_t r = 0; r <= repeat; r++) { + // Section #1 + sendGeneric(kDaikin216HdrMark, kDaikin216HdrSpace, kDaikin216BitMark, + kDaikin216OneSpace, kDaikin216BitMark, kDaikin216ZeroSpace, + kDaikin216BitMark, kDaikin216Gap, data, + kDaikin216Section1Length, + kDaikin216Freq, false, 0, kDutyDefault); + // Section #2 + sendGeneric(kDaikin216HdrMark, kDaikin216HdrSpace, kDaikin216BitMark, + kDaikin216OneSpace, kDaikin216BitMark, kDaikin216ZeroSpace, + kDaikin216BitMark, kDaikin216Gap, + data + kDaikin216Section1Length, + nbytes - kDaikin216Section1Length, + kDaikin216Freq, false, 0, kDutyDefault); + } +} +#endif // SEND_DAIKIN216 + +/// 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? +IRDaikin216::IRDaikin216(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 IRDaikin216::begin(void) { _irsend.begin(); } + +#if SEND_DAIKIN216 +/// Send the current internal state as an IR message. +/// @param[in] repeat Nr. of times the message will be repeated. +void IRDaikin216::send(const uint16_t repeat) { + _irsend.sendDaikin216(getRaw(), kDaikin216StateLength, repeat); +} +#endif // SEND_DAIKIN216 + +/// 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 IRDaikin216::validChecksum(uint8_t state[], const uint16_t length) { + // Validate the checksum of section #1. + if (length <= kDaikin216Section1Length - 1 || + state[kDaikin216Section1Length - 1] != sumBytes( + state, kDaikin216Section1Length - 1)) + return false; + // Validate the checksum of section #2 (a.k.a. the rest) + if (length <= kDaikin216Section1Length + 1 || + state[length - 1] != sumBytes(state + kDaikin216Section1Length, + length - kDaikin216Section1Length - 1)) + return false; + return true; +} + +/// Calculate and set the checksum values for the internal state. +void IRDaikin216::checksum(void) { + _.Sum1 = sumBytes(_.raw, kDaikin216Section1Length - 1); + _.Sum2 = sumBytes(_.raw + kDaikin216Section1Length, + kDaikin216Section2Length - 1); +} + +/// Reset the internal state to a fixed known good state. +void IRDaikin216::stateReset(void) { + for (uint8_t i = 0; i < kDaikin216StateLength; i++) _.raw[i] = 0x00; + _.raw[0] = 0x11; + _.raw[1] = 0xDA; + _.raw[2] = 0x27; + _.raw[3] = 0xF0; + // _.raw[7] is a checksum byte, it will be set by checksum(). + _.raw[8] = 0x11; + _.raw[9] = 0xDA; + _.raw[10] = 0x27; + _.raw[23] = 0xC0; + // _.raw[26] is a checksum byte, it will be set by checksum(). +} + +/// 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 *IRDaikin216::getRaw(void) { + checksum(); // Ensure correct settings before sending. + return _.raw; +} + +/// Set the internal state from a valid code for this protocol. +/// @param[in] new_code A valid code for this protocol. +void IRDaikin216::setRaw(const uint8_t new_code[]) { + std::memcpy(_.raw, new_code, kDaikin216StateLength); +} + +/// Change the power setting to On. +void IRDaikin216::on(void) { setPower(true); } + +/// Change the power setting to Off. +void IRDaikin216::off(void) { setPower(false); } + +/// Change the power setting. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRDaikin216::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 IRDaikin216::getPower(void) const { return _.Power; } + +/// Get the operating mode setting of the A/C. +/// @return The current operating mode setting. +uint8_t IRDaikin216::getMode(void) const { return _.Mode; } + +/// Set the operating mode of the A/C. +/// @param[in] mode The desired operating mode. +void IRDaikin216::setMode(const uint8_t mode) { + switch (mode) { + case kDaikinAuto: + case kDaikinCool: + case kDaikinHeat: + case kDaikinFan: + case kDaikinDry: + _.Mode = mode; + break; + default: + _.Mode = kDaikinAuto; + } +} + +/// 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 IRDaikin216::convertMode(const stdAc::opmode_t mode) { + return IRDaikinESP::convertMode(mode); +} + +/// Set the temperature. +/// @param[in] temp The temperature in degrees celsius. +void IRDaikin216::setTemp(const uint8_t temp) { + uint8_t degrees = std::max(temp, kDaikinMinTemp); + degrees = std::min(degrees, kDaikinMaxTemp); + _.Temp = degrees; +} + +/// Get the current temperature setting. +/// @return The current setting for temp. in degrees celsius. +uint8_t IRDaikin216::getTemp(void) const { return _.Temp; } + +/// Set the speed of the fan. +/// @param[in] fan The desired setting. +/// @note 1-5 or kDaikinFanAuto or kDaikinFanQuiet +void IRDaikin216::setFan(const uint8_t fan) { + // Set the fan speed bits, leave low 4 bits alone + uint8_t fanset; + if (fan == kDaikinFanQuiet || fan == kDaikinFanAuto) + fanset = fan; + else if (fan < kDaikinFanMin || fan > kDaikinFanMax) + fanset = kDaikinFanAuto; + else + fanset = 2 + fan; + _.Fan = fanset; +} + +/// Get the current fan speed setting. +/// @return The current fan speed. +uint8_t IRDaikin216::getFan(void) const { + uint8_t fan = _.Fan; + if (fan != kDaikinFanQuiet && fan != kDaikinFanAuto) fan -= 2; + 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 IRDaikin216::convertFan(const stdAc::fanspeed_t speed) { + return IRDaikinESP::convertFan(speed); +} + +/// Set the Vertical Swing mode of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRDaikin216::setSwingVertical(const bool on) { + _.SwingV = (on ? kDaikin216SwingOn : kDaikin216SwingOff); +} + +/// Get the Vertical Swing mode of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRDaikin216::getSwingVertical(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 IRDaikin216::setSwingHorizontal(const bool on) { + _.SwingH = (on ? kDaikin216SwingOn : kDaikin216SwingOff); +} + +/// Get the Horizontal Swing mode of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRDaikin216::getSwingHorizontal(void) const { return _.SwingH; } + +/// Set the Quiet mode of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +/// @note This is a horrible hack till someone works out the quiet mode bit. +void IRDaikin216::setQuiet(const bool on) { + if (on) { + setFan(kDaikinFanQuiet); + // Powerful & Quiet mode being on are mutually exclusive. + setPowerful(false); + } else if (getFan() == kDaikinFanQuiet) { + setFan(kDaikinFanAuto); + } +} + +/// Get the Quiet mode status of the A/C. +/// @return true, the setting is on. false, the setting is off. +/// @note This is a horrible hack till someone works out the quiet mode bit. +bool IRDaikin216::getQuiet(void) const { return getFan() == kDaikinFanQuiet; } + +/// Set the Powerful (Turbo) mode of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRDaikin216::setPowerful(const bool on) { + _.Powerful = on; + // Powerful & Quiet mode being on are mutually exclusive. + if (on) setQuiet(false); +} + +/// Get the Powerful (Turbo) mode of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRDaikin216::getPowerful(void) const { return _.Powerful; } + +/// Convert the current internal state into its stdAc::state_t equivalent. +/// @return The stdAc equivalent of the native settings. +stdAc::state_t IRDaikin216::toCommon(void) const { + stdAc::state_t result; + result.protocol = decode_type_t::DAIKIN216; + result.model = -1; // No models used. + result.power = _.Power; + result.mode = IRDaikinESP::toCommonMode(_.Mode); + result.celsius = true; + result.degrees = _.Temp; + result.fanspeed = IRDaikinESP::toCommonFanSpeed(getFan()); + result.swingv = _.SwingV ? stdAc::swingv_t::kAuto : + stdAc::swingv_t::kOff; + result.swingh = _.SwingH ? stdAc::swingh_t::kAuto : + stdAc::swingh_t::kOff; + result.quiet = getQuiet(); + result.turbo = _.Powerful; + // Not supported. + result.light = false; + result.clean = 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 IRDaikin216::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, kDaikinAuto, kDaikinCool, kDaikinHeat, + kDaikinDry, kDaikinFan); + result += addTempToString(_.Temp); + result += addFanToString(getFan(), kDaikinFanMax, kDaikinFanMin, + kDaikinFanAuto, kDaikinFanQuiet, kDaikinFanMed); + result += addBoolToString(_.SwingH, kSwingHStr); + result += addBoolToString(_.SwingV, kSwingVStr); + result += addBoolToString(getQuiet(), kQuietStr); + result += addBoolToString(_.Powerful, kPowerfulStr); + return result; +} + +#if DECODE_DAIKIN216 +/// Decode the supplied Daikin 216-bit message. (DAIKIN216) +/// Status: STABLE / Should 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/689 +/// @see https://github.com/danny-source/Arduino_DY_IRDaikin +bool IRrecv::decodeDaikin216(decode_results *results, uint16_t offset, + const uint16_t nbits, const bool strict) { + if (results->rawlen < 2 * (nbits + kHeader + kFooter) - 1 + offset) + return false; + + // Compliance + if (strict && nbits != kDaikin216Bits) return false; + + const uint8_t ksectionSize[kDaikin216Sections] = {kDaikin216Section1Length, + kDaikin216Section2Length}; + // Sections + uint16_t pos = 0; + for (uint8_t section = 0; section < kDaikin216Sections; section++) { + uint16_t used; + // Section Header + Section Data + Section Footer + used = matchGeneric(results->rawbuf + offset, results->state + pos, + results->rawlen - offset, ksectionSize[section] * 8, + kDaikin216HdrMark, kDaikin216HdrSpace, + kDaikin216BitMark, kDaikin216OneSpace, + kDaikin216BitMark, kDaikin216ZeroSpace, + kDaikin216BitMark, kDaikin216Gap, + section >= kDaikin216Sections - 1, + kDaikinTolerance, kDaikinMarkExcess, false); + if (used == 0) return false; + offset += used; + pos += ksectionSize[section]; + } + // Compliance + if (strict) { + if (pos * 8 != kDaikin216Bits) return false; + // Validate the checksum. + if (!IRDaikin216::validChecksum(results->state)) return false; + } + + // Success + results->decode_type = decode_type_t::DAIKIN216; + 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_DAIKIN216 + +#if SEND_DAIKIN160 +/// Send a Daikin160 (160-bit) A/C formatted message. +/// Status: STABLE / Confirmed 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. +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/731 +void IRsend::sendDaikin160(const unsigned char data[], const uint16_t nbytes, + const uint16_t repeat) { + if (nbytes < kDaikin160Section1Length) + return; // Not enough bytes to send a partial message. + + for (uint16_t r = 0; r <= repeat; r++) { + // Section #1 + sendGeneric(kDaikin160HdrMark, kDaikin160HdrSpace, kDaikin160BitMark, + kDaikin160OneSpace, kDaikin160BitMark, kDaikin160ZeroSpace, + kDaikin160BitMark, kDaikin160Gap, data, + kDaikin160Section1Length, + kDaikin160Freq, false, 0, kDutyDefault); + // Section #2 + sendGeneric(kDaikin160HdrMark, kDaikin160HdrSpace, kDaikin160BitMark, + kDaikin160OneSpace, kDaikin160BitMark, kDaikin160ZeroSpace, + kDaikin160BitMark, kDaikin160Gap, + data + kDaikin160Section1Length, + nbytes - kDaikin160Section1Length, + kDaikin160Freq, false, 0, kDutyDefault); + } +} +#endif // SEND_DAIKIN160 + +/// 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? +IRDaikin160::IRDaikin160(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 IRDaikin160::begin(void) { _irsend.begin(); } + +/// 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 IRDaikin160::validChecksum(uint8_t state[], const uint16_t length) { + // Validate the checksum of section #1. + if (length <= kDaikin160Section1Length - 1 || + state[kDaikin160Section1Length - 1] != sumBytes( + state, kDaikin160Section1Length - 1)) + return false; + // Validate the checksum of section #2 (a.k.a. the rest) + if (length <= kDaikin160Section1Length + 1 || + state[length - 1] != sumBytes(state + kDaikin160Section1Length, + length - kDaikin160Section1Length - 1)) + return false; + return true; +} + +/// Calculate and set the checksum values for the internal state. +void IRDaikin160::checksum(void) { + _.Sum1 = sumBytes(_.raw, kDaikin160Section1Length - 1); + _.Sum2 = sumBytes(_.raw + kDaikin160Section1Length, + kDaikin160Section2Length - 1); +} + +/// Reset the internal state to a fixed known good state. +void IRDaikin160::stateReset(void) { + for (uint8_t i = 0; i < kDaikin160StateLength; i++) _.raw[i] = 0x00; + _.raw[0] = 0x11; + _.raw[1] = 0xDA; + _.raw[2] = 0x27; + _.raw[3] = 0xF0; + _.raw[4] = 0x0D; + // _.raw[6] is a checksum byte, it will be set by checksum(). + _.raw[7] = 0x11; + _.raw[8] = 0xDA; + _.raw[9] = 0x27; + _.raw[11] = 0xD3; + _.raw[12] = 0x30; + _.raw[13] = 0x11; + _.raw[16] = 0x1E; + _.raw[17] = 0x0A; + _.raw[18] = 0x08; + // _.raw[19] is a checksum byte, it will be set by checksum(). +} + +/// 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 *IRDaikin160::getRaw(void) { + checksum(); // Ensure correct settings before sending. + return _.raw; +} + +/// Set the internal state from a valid code for this protocol. +/// @param[in] new_code A valid code for this protocol. +void IRDaikin160::setRaw(const uint8_t new_code[]) { + std::memcpy(_.raw, new_code, kDaikin160StateLength); +} + +#if SEND_DAIKIN160 +/// Send the current internal state as an IR message. +/// @param[in] repeat Nr. of times the message will be repeated. +void IRDaikin160::send(const uint16_t repeat) { + _irsend.sendDaikin160(getRaw(), kDaikin160StateLength, repeat); +} +#endif // SEND_DAIKIN160 + +/// Change the power setting to On. +void IRDaikin160::on(void) { setPower(true); } + +/// Change the power setting to Off. +void IRDaikin160::off(void) { setPower(false); } + +/// Change the power setting. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRDaikin160::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 IRDaikin160::getPower(void) const { return _.Power; } + +/// Get the operating mode setting of the A/C. +/// @return The current operating mode setting. +uint8_t IRDaikin160::getMode(void) const { return _.Mode; } + +/// Set the operating mode of the A/C. +/// @param[in] mode The desired operating mode. +void IRDaikin160::setMode(const uint8_t mode) { + switch (mode) { + case kDaikinAuto: + case kDaikinCool: + case kDaikinHeat: + case kDaikinFan: + case kDaikinDry: + _.Mode = mode; + break; + default: _.Mode = kDaikinAuto; + } +} + +/// 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 IRDaikin160::convertMode(const stdAc::opmode_t mode) { + return IRDaikinESP::convertMode(mode); +} + +/// Set the temperature. +/// @param[in] temp The temperature in degrees celsius. +void IRDaikin160::setTemp(const uint8_t temp) { + uint8_t degrees = std::max(temp, kDaikinMinTemp); + degrees = std::min(degrees, kDaikinMaxTemp) - 10; + _.Temp = degrees; +} + +/// Get the current temperature setting. +/// @return The current setting for temp. in degrees celsius. +uint8_t IRDaikin160::getTemp(void) const { return _.Temp + 10; } + +/// Set the speed of the fan. +/// @param[in] fan The desired setting. +/// @note 1-5 or kDaikinFanAuto or kDaikinFanQuiet +void IRDaikin160::setFan(const uint8_t fan) { + uint8_t fanset; + if (fan == kDaikinFanQuiet || fan == kDaikinFanAuto) + fanset = fan; + else if (fan < kDaikinFanMin || fan > kDaikinFanMax) + fanset = kDaikinFanAuto; + else + fanset = 2 + fan; + _.Fan = fanset; +} + +/// Get the current fan speed setting. +/// @return The current fan speed. +uint8_t IRDaikin160::getFan(void) const { + uint8_t fan = _.Fan; + if (fan != kDaikinFanQuiet && fan != kDaikinFanAuto) fan -= 2; + 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 IRDaikin160::convertFan(const stdAc::fanspeed_t speed) { + switch (speed) { + case stdAc::fanspeed_t::kMin: return kDaikinFanMin; + case stdAc::fanspeed_t::kLow: return kDaikinFanMin + 1; + case stdAc::fanspeed_t::kMedium: return kDaikinFanMin + 2; + case stdAc::fanspeed_t::kHigh: return kDaikinFanMax - 1; + case stdAc::fanspeed_t::kMax: return kDaikinFanMax; + default: + return kDaikinFanAuto; + } +} + +/// Set the Vertical Swing mode of the A/C. +/// @param[in] position The position/mode to set the swing to. +void IRDaikin160::setSwingVertical(const uint8_t position) { + switch (position) { + case kDaikin160SwingVLowest: + case kDaikin160SwingVLow: + case kDaikin160SwingVMiddle: + case kDaikin160SwingVHigh: + case kDaikin160SwingVHighest: + case kDaikin160SwingVAuto: + _.SwingV = position; + break; + default: _.SwingV = kDaikin160SwingVAuto; + } +} + +/// Get the Vertical Swing mode of the A/C. +/// @return The native position/mode setting. +uint8_t IRDaikin160::getSwingVertical(void) const { return _.SwingV; } + +/// 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 IRDaikin160::convertSwingV(const stdAc::swingv_t position) { + switch (position) { + case stdAc::swingv_t::kHighest: + case stdAc::swingv_t::kHigh: + case stdAc::swingv_t::kMiddle: + case stdAc::swingv_t::kLow: + case stdAc::swingv_t::kLowest: + return kDaikin160SwingVHighest + 1 - (uint8_t)position; + default: + return kDaikin160SwingVAuto; + } +} + +/// Convert a native vertical swing postion to it's common equivalent. +/// @param[in] setting A native position to convert. +/// @return The common vertical swing position. +stdAc::swingv_t IRDaikin160::toCommonSwingV(const uint8_t setting) { + switch (setting) { + case kDaikin160SwingVHighest: return stdAc::swingv_t::kHighest; + case kDaikin160SwingVHigh: return stdAc::swingv_t::kHigh; + case kDaikin160SwingVMiddle: return stdAc::swingv_t::kMiddle; + case kDaikin160SwingVLow: return stdAc::swingv_t::kLow; + case kDaikin160SwingVLowest: return stdAc::swingv_t::kLowest; + default: + return stdAc::swingv_t::kAuto; + } +} + +/// Convert the current internal state into its stdAc::state_t equivalent. +/// @return The stdAc equivalent of the native settings. +stdAc::state_t IRDaikin160::toCommon(void) const { + stdAc::state_t result; + result.protocol = decode_type_t::DAIKIN160; + result.model = -1; // No models used. + result.power = _.Power; + result.mode = IRDaikinESP::toCommonMode(_.Mode); + result.celsius = true; + result.degrees = getTemp(); + result.fanspeed = IRDaikinESP::toCommonFanSpeed(getFan()); + result.swingv = toCommonSwingV(_.SwingV); + // Not supported. + result.swingh = stdAc::swingh_t::kOff; + result.quiet = false; + result.turbo = false; + result.light = false; + result.clean = 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 IRDaikin160::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, kDaikinAuto, kDaikinCool, kDaikinHeat, + kDaikinDry, kDaikinFan); + result += addTempToString(getTemp()); + result += addFanToString(getFan(), kDaikinFanMax, kDaikinFanMin, + kDaikinFanAuto, kDaikinFanQuiet, kDaikinFanMed); + result += addIntToString(_.SwingV, kSwingVStr); + result += kSpaceLBraceStr; + switch (_.SwingV) { + case kDaikin160SwingVHighest: result += kHighestStr; break; + case kDaikin160SwingVHigh: result += kHighStr; break; + case kDaikin160SwingVMiddle: result += kMiddleStr; break; + case kDaikin160SwingVLow: result += kLowStr; break; + case kDaikin160SwingVLowest: result += kLowestStr; break; + case kDaikin160SwingVAuto: result += kAutoStr; break; + default: result += kUnknownStr; + } + result += ')'; + return result; +} + +#if DECODE_DAIKIN160 +/// Decode the supplied Daikin 160-bit message. (DAIKIN160) +/// 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/731 +bool IRrecv::decodeDaikin160(decode_results *results, uint16_t offset, + const uint16_t nbits, const bool strict) { + if (results->rawlen < 2 * (nbits + kHeader + kFooter) - 1 + offset) + return false; + + // Compliance + if (strict && nbits != kDaikin160Bits) return false; + + const uint8_t ksectionSize[kDaikin160Sections] = {kDaikin160Section1Length, + kDaikin160Section2Length}; + + // Sections + uint16_t pos = 0; + for (uint8_t section = 0; section < kDaikin160Sections; section++) { + uint16_t used; + // Section Header + Section Data (7 bytes) + Section Footer + used = matchGeneric(results->rawbuf + offset, results->state + pos, + results->rawlen - offset, ksectionSize[section] * 8, + kDaikin160HdrMark, kDaikin160HdrSpace, + kDaikin160BitMark, kDaikin160OneSpace, + kDaikin160BitMark, kDaikin160ZeroSpace, + kDaikin160BitMark, kDaikin160Gap, + section >= kDaikin160Sections - 1, + kDaikinTolerance, kDaikinMarkExcess, false); + if (used == 0) return false; + offset += used; + pos += ksectionSize[section]; + } + // Compliance + if (strict) { + // Validate the checksum. + if (!IRDaikin160::validChecksum(results->state)) return false; + } + + // Success + results->decode_type = decode_type_t::DAIKIN160; + 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_DAIKIN160 + +#if SEND_DAIKIN176 +/// Send a Daikin176 (176-bit) A/C formatted message. +/// Status: STABLE / Working on a real device. +/// @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::sendDaikin176(const unsigned char data[], const uint16_t nbytes, + const uint16_t repeat) { + if (nbytes < kDaikin176Section1Length) + return; // Not enough bytes to send a partial message. + + for (uint16_t r = 0; r <= repeat; r++) { + // Section #1 + sendGeneric(kDaikin176HdrMark, kDaikin176HdrSpace, kDaikin176BitMark, + kDaikin176OneSpace, kDaikin176BitMark, kDaikin176ZeroSpace, + kDaikin176BitMark, kDaikin176Gap, data, + kDaikin176Section1Length, + kDaikin176Freq, false, 0, kDutyDefault); + // Section #2 + sendGeneric(kDaikin176HdrMark, kDaikin176HdrSpace, kDaikin176BitMark, + kDaikin176OneSpace, kDaikin176BitMark, kDaikin176ZeroSpace, + kDaikin176BitMark, kDaikin176Gap, + data + kDaikin176Section1Length, + nbytes - kDaikin176Section1Length, + kDaikin176Freq, false, 0, kDutyDefault); + } +} +#endif // SEND_DAIKIN176 + +/// 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? +IRDaikin176::IRDaikin176(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 IRDaikin176::begin(void) { _irsend.begin(); } + +/// 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 IRDaikin176::validChecksum(uint8_t state[], const uint16_t length) { + // Validate the checksum of section #1. + if (length <= kDaikin176Section1Length - 1 || + state[kDaikin176Section1Length - 1] != sumBytes( + state, kDaikin176Section1Length - 1)) + return false; + // Validate the checksum of section #2 (a.k.a. the rest) + if (length <= kDaikin176Section1Length + 1 || + state[length - 1] != sumBytes(state + kDaikin176Section1Length, + length - kDaikin176Section1Length - 1)) + return false; + return true; +} + +/// Calculate and set the checksum values for the internal state. +void IRDaikin176::checksum(void) { + _.Sum1 = sumBytes(_.raw, kDaikin176Section1Length - 1); + _.Sum2 = sumBytes(_.raw + kDaikin176Section1Length, + kDaikin176Section2Length - 1); +} + +/// Reset the internal state to a fixed known good state. +void IRDaikin176::stateReset(void) { + for (uint8_t i = 0; i < kDaikin176StateLength; i++) _.raw[i] = 0x00; + _.raw[0] = 0x11; + _.raw[1] = 0xDA; + _.raw[2] = 0x17; + _.raw[3] = 0x18; + _.raw[4] = 0x04; + // _.raw[6] is a checksum byte, it will be set by checksum(). + _.raw[7] = 0x11; + _.raw[8] = 0xDA; + _.raw[9] = 0x17; + _.raw[10] = 0x18; + _.raw[12] = 0x73; + _.raw[14] = 0x20; + _.raw[18] = 0x16; // Fan speed and swing + _.raw[20] = 0x20; + // _.raw[21] is a checksum byte, it will be set by checksum(). + _saved_temp = getTemp(); +} + +/// 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 *IRDaikin176::getRaw(void) { + checksum(); // Ensure correct settings before sending. + return _.raw; +} + +/// Set the internal state from a valid code for this protocol. +/// @param[in] new_code A valid code for this protocol. +void IRDaikin176::setRaw(const uint8_t new_code[]) { + std::memcpy(_.raw, new_code, kDaikin176StateLength); + _saved_temp = getTemp(); +} + +#if SEND_DAIKIN176 +/// Send the current internal state as an IR message. +/// @param[in] repeat Nr. of times the message will be repeated. +void IRDaikin176::send(const uint16_t repeat) { + _irsend.sendDaikin176(getRaw(), kDaikin176StateLength, repeat); +} +#endif // SEND_DAIKIN176 + +/// Change the power setting to On. +void IRDaikin176::on(void) { setPower(true); } + +/// Change the power setting to Off.. +void IRDaikin176::off(void) { setPower(false); } + +/// Change the power setting. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRDaikin176::setPower(const bool on) { + _.ModeButton = 0; + _.Power = on; +} + +/// Get the value of the current power setting. +/// @return true, the setting is on. false, the setting is off. +bool IRDaikin176::getPower(void) const { return _.Power; } + +/// Get the operating mode setting of the A/C. +/// @return The current operating mode setting. +uint8_t IRDaikin176::getMode(void) const { return _.Mode; } + +/// Set the operating mode of the A/C. +/// @param[in] mode The desired operating mode. +void IRDaikin176::setMode(const uint8_t mode) { + uint8_t altmode = 0; + // Set the mode bits. + _.Mode = mode; + // Daikin172 has some alternate/additional mode bits that need to be changed + // in line with the operating mode. The following few lines match up these + // bits with the corresponding operating bits. + switch (mode) { + case kDaikin176Dry: altmode = 2; break; + case kDaikin176Fan: altmode = 6; break; + case kDaikin176Auto: + case kDaikin176Cool: + case kDaikin176Heat: altmode = 7; break; + default: _.Mode = kDaikin176Cool; altmode = 7; break; + } + // Set the additional mode bits. + _.AltMode = altmode; + setTemp(_saved_temp); + // Needs to happen after setTemp() as it will clear it. + _.ModeButton = kDaikin176ModeButton; +} + +/// 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 IRDaikin176::convertMode(const stdAc::opmode_t mode) { + switch (mode) { + case stdAc::opmode_t::kDry: return kDaikin176Dry; + case stdAc::opmode_t::kHeat: return kDaikin176Heat; + case stdAc::opmode_t::kFan: return kDaikin176Fan; + case stdAc::opmode_t::kAuto: return kDaikin176Auto; + default: return kDaikin176Cool; + } +} + +/// 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 IRDaikin176::toCommonMode(const uint8_t mode) { + switch (mode) { + case kDaikin176Dry: return stdAc::opmode_t::kDry; + case kDaikin176Heat: return stdAc::opmode_t::kHeat; + case kDaikin176Fan: return stdAc::opmode_t::kFan; + case kDaikin176Auto: return stdAc::opmode_t::kAuto; + default: return stdAc::opmode_t::kCool; + } +} + +/// Set the temperature. +/// @param[in] temp The temperature in degrees celsius. +void IRDaikin176::setTemp(const uint8_t temp) { + uint8_t degrees = std::min(kDaikinMaxTemp, std::max(temp, kDaikinMinTemp)); + _saved_temp = degrees; + switch (_.Mode) { + case kDaikin176Dry: + case kDaikin176Fan: + degrees = kDaikin176DryFanTemp; break; + } + _.Temp = degrees - 9; + _.ModeButton = 0; +} + +/// Get the current temperature setting. +/// @return The current setting for temp. in degrees celsius. +uint8_t IRDaikin176::getTemp(void) const { return _.Temp + 9; } + +/// Set the speed of the fan. +/// @param[in] fan The desired setting. +/// @note 1 for Min or 3 for Max +void IRDaikin176::setFan(const uint8_t fan) { + switch (fan) { + case kDaikinFanMin: + case kDaikin176FanMax: + _.Fan = fan; + break; + default: + _.Fan = kDaikin176FanMax; + break; + } + _.ModeButton = 0; +} + +/// Get the current fan speed setting. +/// @return The current fan speed. +uint8_t IRDaikin176::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 IRDaikin176::convertFan(const stdAc::fanspeed_t speed) { + switch (speed) { + case stdAc::fanspeed_t::kMin: + case stdAc::fanspeed_t::kLow: return kDaikinFanMin; + default: return kDaikin176FanMax; + } +} + +/// Set the Horizontal Swing mode of the A/C. +/// @param[in] position The position/mode to set the swing to. +void IRDaikin176::setSwingHorizontal(const uint8_t position) { + switch (position) { + case kDaikin176SwingHOff: + case kDaikin176SwingHAuto: + _.SwingH = position; + break; + default: _.SwingH = kDaikin176SwingHAuto; + } +} + +/// Get the Horizontal Swing mode of the A/C. +/// @return The native position/mode setting. +uint8_t IRDaikin176::getSwingHorizontal(void) const { return _.SwingH; } + +/// Get the Unit Id of the A/C. +/// @return The Unit Id the A/C is to use. +uint8_t IRDaikin176::getId(void) const { return _.Id1; } + +/// Set the Unit Id of the A/C. +/// @param[in] num The Unit Id the A/C is to use. +/// @note 0 for Unit A; 1 for Unit B +void IRDaikin176::setId(const uint8_t num) { _.Id1 = _.Id2 = num; } + +/// Convert a stdAc::swingh_t enum into it's native setting. +/// @param[in] position The enum to be converted. +/// @return The native equivalent of the enum. +uint8_t IRDaikin176::convertSwingH(const stdAc::swingh_t position) { + switch (position) { + case stdAc::swingh_t::kOff: return kDaikin176SwingHOff; + case stdAc::swingh_t::kAuto: return kDaikin176SwingHAuto; + default: return kDaikin176SwingHAuto; + } +} + +/// Convert a native horizontal swing postion to it's common equivalent. +/// @param[in] setting A native position to convert. +/// @return The common horizontal swing position. +stdAc::swingh_t IRDaikin176::toCommonSwingH(const uint8_t setting) { + switch (setting) { + case kDaikin176SwingHOff: return stdAc::swingh_t::kOff; + case kDaikin176SwingHAuto: return stdAc::swingh_t::kAuto; + default: + return stdAc::swingh_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 IRDaikin176::toCommonFanSpeed(const uint8_t speed) { + return (speed == kDaikinFanMin) ? stdAc::fanspeed_t::kMin + : stdAc::fanspeed_t::kMax; +} + +/// Convert the current internal state into its stdAc::state_t equivalent. +/// @return The stdAc equivalent of the native settings. +stdAc::state_t IRDaikin176::toCommon(void) const { + stdAc::state_t result; + result.protocol = decode_type_t::DAIKIN176; + result.model = -1; // No models used. + result.power = _.Power; + result.mode = IRDaikin176::toCommonMode(_.Mode); + result.celsius = true; + result.degrees = getTemp(); + result.fanspeed = toCommonFanSpeed(_.Fan); + result.swingh = toCommonSwingH(_.SwingH); + + // Not supported. + result.swingv = stdAc::swingv_t::kOff; + result.quiet = false; + result.turbo = false; + result.light = false; + result.clean = 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 IRDaikin176::toString(void) const { + String result = ""; + result.reserve(90); // Reserve some heap for the string to reduce fragging. + result += addBoolToString(_.Power, kPowerStr, false); + result += addModeToString(_.Mode, kDaikin176Auto, kDaikin176Cool, + kDaikin176Heat, kDaikin176Dry, kDaikin176Fan); + result += addTempToString(getTemp()); + result += addFanToString(_.Fan, kDaikin176FanMax, kDaikinFanMin, + kDaikinFanMin, kDaikinFanMin, kDaikinFanMin); + result += addSwingHToString(_.SwingH, kDaikin176SwingHAuto, + kDaikin176SwingHAuto, // maxleft Unused + kDaikin176SwingHAuto, // left Unused + kDaikin176SwingHAuto, // middle Unused + kDaikin176SwingHAuto, // right Unused + kDaikin176SwingHAuto, // maxright Unused + kDaikin176SwingHOff, + // Below are unused. + kDaikin176SwingHAuto, + kDaikin176SwingHAuto, + kDaikin176SwingHAuto, + kDaikin176SwingHAuto); + result += addIntToString(_.Id1, kIdStr); + return result; +} + +#if DECODE_DAIKIN176 +/// Decode the supplied Daikin 176-bit message. (DAIKIN176) +/// 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::decodeDaikin176(decode_results *results, uint16_t offset, + const uint16_t nbits, + const bool strict) { + if (results->rawlen < 2 * (nbits + kHeader + kFooter) - 1 + offset) + return false; + + // Compliance + if (strict && nbits != kDaikin176Bits) return false; + + const uint8_t ksectionSize[kDaikin176Sections] = {kDaikin176Section1Length, + kDaikin176Section2Length}; + + // Sections + uint16_t pos = 0; + for (uint8_t section = 0; section < kDaikin176Sections; section++) { + uint16_t used; + // Section Header + Section Data (7 bytes) + Section Footer + used = matchGeneric(results->rawbuf + offset, results->state + pos, + results->rawlen - offset, ksectionSize[section] * 8, + kDaikin176HdrMark, kDaikin176HdrSpace, + kDaikin176BitMark, kDaikin176OneSpace, + kDaikin176BitMark, kDaikin176ZeroSpace, + kDaikin176BitMark, kDaikin176Gap, + section >= kDaikin176Sections - 1, + kDaikinTolerance, kDaikinMarkExcess, false); + if (used == 0) return false; + offset += used; + pos += ksectionSize[section]; + } + // Compliance + if (strict) { + // Validate the checksum. + if (!IRDaikin176::validChecksum(results->state)) return false; + } + + // Success + results->decode_type = decode_type_t::DAIKIN176; + 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_DAIKIN176 + +#if SEND_DAIKIN128 +/// Send a Daikin128 (128-bit) A/C formatted 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. +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/827 +void IRsend::sendDaikin128(const unsigned char data[], const uint16_t nbytes, + const uint16_t repeat) { + if (nbytes < kDaikin128SectionLength) + return; // Not enough bytes to send a partial message. + + for (uint16_t r = 0; r <= repeat; r++) { + enableIROut(kDaikin128Freq); + // Leader + for (uint8_t i = 0; i < 2; i++) { + mark(kDaikin128LeaderMark); + space(kDaikin128LeaderSpace); + } + // Section #1 (Header + Data) + sendGeneric(kDaikin128HdrMark, kDaikin128HdrSpace, kDaikin128BitMark, + kDaikin128OneSpace, kDaikin128BitMark, kDaikin128ZeroSpace, + kDaikin128BitMark, kDaikin128Gap, data, + kDaikin128SectionLength, + kDaikin128Freq, false, 0, kDutyDefault); + // Section #2 (Data + Footer) + sendGeneric(0, 0, kDaikin128BitMark, + kDaikin128OneSpace, kDaikin128BitMark, kDaikin128ZeroSpace, + kDaikin128FooterMark, kDaikin128Gap, + data + kDaikin128SectionLength, + nbytes - kDaikin128SectionLength, + kDaikin128Freq, false, 0, kDutyDefault); + } +} +#endif // SEND_DAIKIN128 + +/// 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? +IRDaikin128::IRDaikin128(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 IRDaikin128::begin(void) { _irsend.begin(); } + +uint8_t IRDaikin128::calcFirstChecksum(const uint8_t state[]) { + return sumNibbles(state, kDaikin128SectionLength - 1, + state[kDaikin128SectionLength - 1] & 0x0F) & 0x0F; +} + +uint8_t IRDaikin128::calcSecondChecksum(const uint8_t state[]) { + return sumNibbles(state + kDaikin128SectionLength, + kDaikin128SectionLength - 1); +} + +/// 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 IRDaikin128::validChecksum(uint8_t state[]) { + // Validate the checksum of section #1. + if (state[kDaikin128SectionLength - 1] >> 4 != calcFirstChecksum(state)) + return false; + // Validate the checksum of section #2 + if (state[kDaikin128StateLength - 1] != calcSecondChecksum(state)) + return false; + return true; +} + +/// Calculate and set the checksum values for the internal state. +void IRDaikin128::checksum(void) { + _.Sum1 = calcFirstChecksum(_.raw); + _.Sum2 = calcSecondChecksum(_.raw); +} + +/// Reset the internal state to a fixed known good state. +void IRDaikin128::stateReset(void) { + for (uint8_t i = 0; i < kDaikin128StateLength; i++) _.raw[i] = 0x00; + _.raw[0] = 0x16; + _.raw[7] = 0x04; // Most significant nibble is a checksum. + _.raw[8] = 0xA1; + // _.raw[15] is a checksum byte, it will be set by checksum(). +} + +/// 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 *IRDaikin128::getRaw(void) { + checksum(); // Ensure correct settings before sending. + return _.raw; +} + +/// Set the internal state from a valid code for this protocol. +/// @param[in] new_code A valid code for this protocol. +void IRDaikin128::setRaw(const uint8_t new_code[]) { + std::memcpy(_.raw, new_code, kDaikin128StateLength); +} + +#if SEND_DAIKIN128 +/// Send the current internal state as an IR message. +/// @param[in] repeat Nr. of times the message will be repeated. +void IRDaikin128::send(const uint16_t repeat) { + _irsend.sendDaikin128(getRaw(), kDaikin128StateLength, repeat); +} +#endif // SEND_DAIKIN128 + +/// Set the Power toggle setting of the A/C. +/// @param[in] toggle true, the setting is on. false, the setting is off. +void IRDaikin128::setPowerToggle(const bool toggle) { _.Power = toggle; } + +/// Get the Power toggle setting of the A/C. +/// @return The current operating mode setting. +bool IRDaikin128::getPowerToggle(void) const { return _.Power; } + +/// Get the operating mode setting of the A/C. +/// @return The current operating mode setting. +uint8_t IRDaikin128::getMode(void) const { return _.Mode; } + +/// Set the operating mode of the A/C. +/// @param[in] mode The desired operating mode. +void IRDaikin128::setMode(const uint8_t mode) { + switch (mode) { + case kDaikin128Auto: + case kDaikin128Cool: + case kDaikin128Heat: + case kDaikin128Fan: + case kDaikin128Dry: + _.Mode = mode; + break; + default: + _.Mode = kDaikin128Auto; + break; + } + // Force a reset of mode dependant things. + setFan(getFan()); // Covers Quiet & Powerful too. + 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 IRDaikin128::convertMode(const stdAc::opmode_t mode) { + switch (mode) { + case stdAc::opmode_t::kCool: return kDaikin128Cool; + case stdAc::opmode_t::kHeat: return kDaikin128Heat; + case stdAc::opmode_t::kDry: return kDaikinDry; + case stdAc::opmode_t::kFan: return kDaikin128Fan; + default: return kDaikin128Auto; + } +} + +/// 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 IRDaikin128::toCommonMode(const uint8_t mode) { + switch (mode) { + case kDaikin128Cool: return stdAc::opmode_t::kCool; + case kDaikin128Heat: return stdAc::opmode_t::kHeat; + case kDaikin128Dry: return stdAc::opmode_t::kDry; + case kDaikin128Fan: return stdAc::opmode_t::kFan; + default: return stdAc::opmode_t::kAuto; + } +} + +/// Set the temperature. +/// @param[in] temp The temperature in degrees celsius. +void IRDaikin128::setTemp(const uint8_t temp) { + _.Temp = uint8ToBcd(std::min(kDaikin128MaxTemp, + std::max(temp, kDaikin128MinTemp))); +} + +/// Get the current temperature setting. +/// @return The current setting for temp. in degrees celsius. +uint8_t IRDaikin128::getTemp(void) const { return bcdToUint8(_.Temp); } + +/// Get the current fan speed setting. +/// @return The current fan speed. +uint8_t IRDaikin128::getFan(void) const { return _.Fan; } + +/// Set the speed of the fan. +/// @param[in] speed The desired setting. +void IRDaikin128::setFan(const uint8_t speed) { + uint8_t new_speed = speed; + uint8_t mode = _.Mode; + switch (speed) { + case kDaikin128FanQuiet: + case kDaikin128FanPowerful: + if (mode == kDaikin128Auto) new_speed = kDaikin128FanAuto; + // FALL-THRU + case kDaikin128FanAuto: + case kDaikin128FanHigh: + case kDaikin128FanMed: + case kDaikin128FanLow: + _.Fan = new_speed; + break; + default: + _.Fan = kDaikin128FanAuto; + return; + } +} + +/// 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 IRDaikin128::convertFan(const stdAc::fanspeed_t speed) { + switch (speed) { + case stdAc::fanspeed_t::kMin: return kDaikinFanQuiet; + case stdAc::fanspeed_t::kLow: return kDaikin128FanLow; + case stdAc::fanspeed_t::kMedium: return kDaikin128FanMed; + case stdAc::fanspeed_t::kHigh: return kDaikin128FanHigh; + case stdAc::fanspeed_t::kMax: return kDaikin128FanPowerful; + default: return kDaikin128FanAuto; + } +} + +/// 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 IRDaikin128::toCommonFanSpeed(const uint8_t speed) { + switch (speed) { + case kDaikin128FanPowerful: return stdAc::fanspeed_t::kMax; + case kDaikin128FanHigh: return stdAc::fanspeed_t::kHigh; + case kDaikin128FanMed: return stdAc::fanspeed_t::kMedium; + case kDaikin128FanLow: return stdAc::fanspeed_t::kLow; + case kDaikinFanQuiet: 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 IRDaikin128::setSwingVertical(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 IRDaikin128::getSwingVertical(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 IRDaikin128::setSleep(const bool on) { _.Sleep = on; } + +/// Get the Sleep mode of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRDaikin128::getSleep(void) const { return _.Sleep; } + +/// Set the Economy mode of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRDaikin128::setEcono(const bool on) { + uint8_t mode = _.Mode; + _.Econo = (on && (mode == kDaikin128Cool || mode == kDaikin128Heat)); +} + +/// Get the Economical mode of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRDaikin128::getEcono(void) const { return _.Econo; } + +/// Set the Quiet mode of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRDaikin128::setQuiet(const bool on) { + uint8_t mode = _.Mode; + if (on && (mode == kDaikin128Cool || mode == kDaikin128Heat)) + setFan(kDaikin128FanQuiet); + else if (_.Fan == kDaikin128FanQuiet) + setFan(kDaikin128FanAuto); +} + +/// Get the Quiet mode status of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRDaikin128::getQuiet(void) const { return _.Fan == kDaikin128FanQuiet; } + +/// Set the Powerful (Turbo) mode of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRDaikin128::setPowerful(const bool on) { + uint8_t mode = _.Mode; + if (on && (mode == kDaikin128Cool || mode == kDaikin128Heat)) + setFan(kDaikin128FanPowerful); + else if (_.Fan == kDaikin128FanPowerful) + setFan(kDaikin128FanAuto); +} + +/// Get the Powerful (Turbo) mode of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRDaikin128::getPowerful(void) const { + return _.Fan == kDaikin128FanPowerful; +} + +/// Set the clock on the A/C unit. +/// @param[in] mins_since_midnight Nr. of minutes past midnight. +void IRDaikin128::setClock(const uint16_t mins_since_midnight) { + uint16_t mins = mins_since_midnight; + if (mins_since_midnight >= 24 * 60) mins = 0; // Bounds check. + // Hours. + _.ClockHours = uint8ToBcd(mins / 60); + // Minutes. + _.ClockMins = uint8ToBcd(mins % 60); +} + +/// Get the clock time to be sent to the A/C unit. +/// @return The number of minutes past midnight. +uint16_t IRDaikin128::getClock(void) const { + return bcdToUint8(_.ClockHours) * 60 + bcdToUint8(_.ClockMins); +} + +/// Set the enable status of the On Timer. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRDaikin128::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 IRDaikin128::getOnTimerEnabled(void) const { return _.OnTimer; } + +#define SETTIME(x, n) do { \ + uint16_t mins = n;\ + if (n >= 24 * 60) mins = 0;\ + _.x##HalfHour = (mins % 60) >= 30;\ + _.x##Hours = uint8ToBcd(mins / 60);\ +} while (0) + +#define GETTIME(x) bcdToUint8(_.x##Hours) * 60 + (_.x##HalfHour ? 30 : 0) + +/// Set the On Timer time for the A/C unit. +/// @param[in] mins_since_midnight Nr. of minutes past midnight. +void IRDaikin128::setOnTimer(const uint16_t mins_since_midnight) { + SETTIME(On, mins_since_midnight); +} + +/// Get the On Timer time to be sent to the A/C unit. +/// @return The number of minutes past midnight. +uint16_t IRDaikin128::getOnTimer(void) const { return GETTIME(On); } + +/// Set the enable status of the Off Timer. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRDaikin128::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 IRDaikin128::getOffTimerEnabled(void) const { return _.OffTimer; } + +/// Set the Off Timer time for the A/C unit. +/// @param[in] mins_since_midnight Nr. of minutes past midnight. +void IRDaikin128::setOffTimer(const uint16_t mins_since_midnight) { + SETTIME(Off, mins_since_midnight); +} + +/// Get the Off Timer time to be sent to the A/C unit. +/// @return The number of minutes past midnight. +uint16_t IRDaikin128::getOffTimer(void) const { return GETTIME(Off); } + +/// Set the Light toggle setting of the A/C. +/// @param[in] unit Device to show the LED (Light) Display info about. +/// @note 0 is off. +void IRDaikin128::setLightToggle(const uint8_t unit) { + _.Ceiling = 0; + _.Wall = 0; + switch (unit) { + case kDaikin128BitCeiling: + _.Ceiling = 1; + break; + case kDaikin128BitWall: + _.Wall = 1; + break; + } +} + +/// Get the Light toggle setting of the A/C. +/// @return The current operating mode setting. +uint8_t IRDaikin128::getLightToggle(void) const { + uint8_t code = 0; + if (_.Ceiling) { + code = kDaikin128BitCeiling; + } else if (_.Wall) { + code = kDaikin128BitWall; + } + + return code; +} + +/// Convert the current internal state into a human readable string. +/// @return A human readable string. +String IRDaikin128::toString(void) const { + String result = ""; + result.reserve(240); // Reserve some heap for the string to reduce fragging. + result += addBoolToString(_.Power, kPowerToggleStr, false); + result += addModeToString(_.Mode, kDaikin128Auto, kDaikin128Cool, + kDaikin128Heat, kDaikin128Dry, kDaikin128Fan); + result += addTempToString(getTemp()); + result += addFanToString(_.Fan, kDaikin128FanHigh, kDaikin128FanLow, + kDaikin128FanAuto, kDaikin128FanQuiet, + kDaikin128FanMed); + result += addBoolToString(getPowerful(), kPowerfulStr); + result += addBoolToString(getQuiet(), kQuietStr); + result += addBoolToString(_.SwingV, kSwingVStr); + result += addBoolToString(_.Sleep, kSleepStr); + result += addBoolToString(_.Econo, kEconoStr); + result += addLabeledString(minsToString(getClock()), kClockStr); + result += addBoolToString(_.OnTimer, kOnTimerStr); + result += addLabeledString(minsToString(getOnTimer()), kOnTimerStr); + result += addBoolToString(_.OffTimer, kOffTimerStr); + result += addLabeledString(minsToString(getOffTimer()), kOffTimerStr); + result += addIntToString(getLightToggle(), kLightToggleStr); + result += kSpaceLBraceStr; + switch (getLightToggle()) { + case kDaikin128BitCeiling: result += kCeilingStr; break; + case kDaikin128BitWall: result += kWallStr; break; + case 0: result += kOffStr; break; + default: result += kUnknownStr; + } + result += ')'; + return result; +} + +/// Convert the current internal state into its stdAc::state_t equivalent. +/// @param[in] prev Ptr to a previous state. +/// @return The stdAc equivalent of the native settings. +stdAc::state_t IRDaikin128::toCommon(const stdAc::state_t *prev) const { + stdAc::state_t result; + if (prev != NULL) result = *prev; + result.protocol = decode_type_t::DAIKIN128; + 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.quiet = getQuiet(); + result.turbo = getPowerful(); + result.econo = _.Econo; + result.light ^= (getLightToggle() != 0); + result.sleep = _.Sleep ? 0 : -1; + result.clock = getClock(); + // Not supported. + result.swingh = stdAc::swingh_t::kOff; + result.clean = false; + result.filter = false; + result.beep = false; + return result; +} + +#if DECODE_DAIKIN128 +/// Decode the supplied Daikin 128-bit message. (DAIKIN128) +/// 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. +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/827 +bool IRrecv::decodeDaikin128(decode_results *results, uint16_t offset, + const uint16_t nbits, const bool strict) { + if (results->rawlen < 2 * (nbits + kHeader) + kFooter - 1 + offset) + return false; + if (nbits / 8 <= kDaikin128SectionLength) return false; + + // Compliance + if (strict && nbits != kDaikin128Bits) return false; + + // Leader + for (uint8_t i = 0; i < 2; i++) { + if (!matchMark(results->rawbuf[offset++], kDaikin128LeaderMark, + kDaikinTolerance, kDaikinMarkExcess)) return false; + if (!matchSpace(results->rawbuf[offset++], kDaikin128LeaderSpace, + kDaikinTolerance, kDaikinMarkExcess)) return false; + } + const uint16_t ksectionSize[kDaikin128Sections] = { + kDaikin128SectionLength, (uint16_t)(nbits / 8 - kDaikin128SectionLength)}; + // Data Sections + uint16_t pos = 0; + for (uint8_t section = 0; section < kDaikin128Sections; section++) { + uint16_t used; + // Section Header (first section only) + Section Data (8 bytes) + + // Section Footer (Not for first section) + used = matchGeneric(results->rawbuf + offset, results->state + pos, + results->rawlen - offset, ksectionSize[section] * 8, + section == 0 ? kDaikin128HdrMark : 0, + section == 0 ? kDaikin128HdrSpace : 0, + kDaikin128BitMark, kDaikin128OneSpace, + kDaikin128BitMark, kDaikin128ZeroSpace, + section > 0 ? kDaikin128FooterMark : kDaikin128BitMark, + kDaikin128Gap, + section > 0, + kDaikinTolerance, kDaikinMarkExcess, false); + if (used == 0) return false; + offset += used; + pos += ksectionSize[section]; + } + // Compliance + if (strict) { + if (!IRDaikin128::validChecksum(results->state)) return false; + } + + // Success + results->decode_type = decode_type_t::DAIKIN128; + 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_DAIKIN128 + +#if SEND_DAIKIN152 +/// Send a Daikin152 (152-bit) A/C formatted 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. +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/873 +void IRsend::sendDaikin152(const unsigned char data[], const uint16_t nbytes, + const uint16_t repeat) { + for (uint16_t r = 0; r <= repeat; r++) { + // Leader + sendGeneric(0, 0, kDaikin152BitMark, kDaikin152OneSpace, + kDaikin152BitMark, kDaikin152ZeroSpace, + kDaikin152BitMark, kDaikin152Gap, + (uint64_t)0, kDaikin152LeaderBits, + kDaikin152Freq, false, 0, kDutyDefault); + // Header + Data + Footer + sendGeneric(kDaikin152HdrMark, kDaikin152HdrSpace, kDaikin152BitMark, + kDaikin152OneSpace, kDaikin152BitMark, kDaikin152ZeroSpace, + kDaikin152BitMark, kDaikin152Gap, data, + nbytes, kDaikin152Freq, false, 0, kDutyDefault); + } +} +#endif // SEND_DAIKIN152 + +#if DECODE_DAIKIN152 +/// Decode the supplied Daikin 152-bit message. (DAIKIN152) +/// 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. +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/873 +bool IRrecv::decodeDaikin152(decode_results *results, uint16_t offset, + const uint16_t nbits, const bool strict) { + if (results->rawlen < 2 * (5 + nbits + kFooter) + kHeader - 1 + offset) + return false; + if (nbits / 8 < kDaikin152StateLength) return false; + + // Compliance + if (strict && nbits != kDaikin152Bits) return false; + + uint16_t used; + + // Leader + uint64_t leader = 0; + used = matchGeneric(results->rawbuf + offset, &leader, + results->rawlen - offset, kDaikin152LeaderBits, + 0, 0, // No Header + kDaikin152BitMark, kDaikin152OneSpace, + kDaikin152BitMark, kDaikin152ZeroSpace, + kDaikin152BitMark, kDaikin152Gap, // Footer gap + false, _tolerance, kMarkExcess, false); + if (used == 0 || leader != 0) return false; + offset += used; + + // Header + Data + Footer + used = matchGeneric(results->rawbuf + offset, results->state, + results->rawlen - offset, nbits, + kDaikin152HdrMark, kDaikin152HdrSpace, + kDaikin152BitMark, kDaikin152OneSpace, + kDaikin152BitMark, kDaikin152ZeroSpace, + kDaikin152BitMark, kDaikin152Gap, + true, _tolerance, kMarkExcess, false); + if (used == 0) return false; + + // Compliance + if (strict) { + if (!IRDaikin152::validChecksum(results->state)) return false; + } + + // Success + results->decode_type = decode_type_t::DAIKIN152; + 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_DAIKIN152 + +/// 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? +IRDaikin152::IRDaikin152(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 IRDaikin152::begin(void) { _irsend.begin(); } + +#if SEND_DAIKIN152 +/// Send the current internal state as an IR message. +/// @param[in] repeat Nr. of times the message will be repeated. +void IRDaikin152::send(const uint16_t repeat) { + _irsend.sendDaikin152(getRaw(), kDaikin152StateLength, repeat); +} +#endif // SEND_DAIKIN152 + +/// 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 IRDaikin152::validChecksum(uint8_t state[], const uint16_t length) { + // Validate the checksum of the given state. + if (length <= 1 || state[length - 1] != sumBytes(state, length - 1)) + return false; + else + return true; +} + +/// Calculate and set the checksum values for the internal state. +void IRDaikin152::checksum(void) { + _.Sum = sumBytes(_.raw, kDaikin152StateLength - 1); +} + +/// Reset the internal state to a fixed known good state. +void IRDaikin152::stateReset(void) { + for (uint8_t i = 3; i < kDaikin152StateLength; i++) _.raw[i] = 0x00; + _.raw[0] = 0x11; + _.raw[1] = 0xDA; + _.raw[2] = 0x27; + _.raw[15] = 0xC5; + // _.raw[19] is a checksum byte, it will be set by checksum(). +} + +/// 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 *IRDaikin152::getRaw(void) { + checksum(); // Ensure correct settings before sending. + return _.raw; +} + +/// Set the internal state from a valid code for this protocol. +/// @param[in] new_code A valid code for this protocol. +void IRDaikin152::setRaw(const uint8_t new_code[]) { + std::memcpy(_.raw, new_code, kDaikin152StateLength); +} + +/// Change the power setting to On. +void IRDaikin152::on(void) { setPower(true); } + +/// Change the power setting to Off. +void IRDaikin152::off(void) { setPower(false); } + +/// Change the power setting. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRDaikin152::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 IRDaikin152::getPower(void) const { return _.Power; } + +/// Get the operating mode setting of the A/C. +/// @return The current operating mode setting. +uint8_t IRDaikin152::getMode(void) const { return _.Mode; } + +/// Set the operating mode of the A/C. +/// @param[in] mode The desired operating mode. +void IRDaikin152::setMode(const uint8_t mode) { + switch (mode) { + case kDaikinFan: + setTemp(kDaikin152FanTemp); // Handle special temp for fan mode. + break; + case kDaikinDry: + setTemp(kDaikin152DryTemp); // Handle special temp for dry mode. + break; + case kDaikinAuto: + case kDaikinCool: + case kDaikinHeat: + break; + default: + _.Mode = kDaikinAuto; + return; + } + _.Mode = 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 IRDaikin152::convertMode(const stdAc::opmode_t mode) { + return IRDaikinESP::convertMode(mode); +} + +/// Set the temperature. +/// @param[in] temp The temperature in degrees celsius. +void IRDaikin152::setTemp(const uint8_t temp) { + uint8_t degrees = std::max( + temp, (_.Mode == kDaikinHeat) ? kDaikinMinTemp : kDaikin2MinCoolTemp); + degrees = std::min(degrees, kDaikinMaxTemp); + if (temp == kDaikin152FanTemp) degrees = temp; // Handle fan only temp. + _.Temp = degrees; +} + +/// Get the current temperature setting. +/// @return The current setting for temp. in degrees celsius. +uint8_t IRDaikin152::getTemp(void) const { return _.Temp; } + +/// Set the speed of the fan. +/// @param[in] fan The desired setting. +/// @note 1-5 or kDaikinFanAuto or kDaikinFanQuiet +void IRDaikin152::setFan(const uint8_t fan) { + // Set the fan speed bits, leave low 4 bits alone + uint8_t fanset; + if (fan == kDaikinFanQuiet || fan == kDaikinFanAuto) + fanset = fan; + else if (fan < kDaikinFanMin || fan > kDaikinFanMax) + fanset = kDaikinFanAuto; + else + fanset = 2 + fan; + _.Fan = fanset; +} + +/// Get the current fan speed setting. +/// @return The current fan speed. +uint8_t IRDaikin152::getFan(void) const { + const uint8_t fan = _.Fan; + switch (fan) { + case kDaikinFanAuto: + case kDaikinFanQuiet: return fan; + default: return fan - 2; + } +} + +/// 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 IRDaikin152::convertFan(const stdAc::fanspeed_t speed) { + return IRDaikinESP::convertFan(speed); +} + +/// Set the Vertical Swing mode of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRDaikin152::setSwingV(const bool on) { + _.SwingV = (on ? kDaikinSwingOn : kDaikinSwingOff); +} + +/// Get the Vertical Swing mode of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRDaikin152::getSwingV(void) const { return _.SwingV; } + +/// Set the Quiet mode of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRDaikin152::setQuiet(const bool on) { + _.Quiet = on; + // Powerful & Quiet mode being on are mutually exclusive. + if (on) setPowerful(false); +} + +/// Get the Quiet mode status of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRDaikin152::getQuiet(void) const { return _.Quiet; } + +/// Set the Powerful (Turbo) mode of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRDaikin152::setPowerful(const bool on) { + _.Powerful = on; + if (on) { + // Powerful, Quiet, Comfort & Econo mode being on are mutually exclusive. + setQuiet(false); + setComfort(false); + setEcono(false); + } +} + +/// Get the Powerful (Turbo) mode of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRDaikin152::getPowerful(void) const { return _.Powerful; } + +/// Set the Economy mode of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRDaikin152::setEcono(const bool on) { + _.Econo = on; + // Powerful & Econo mode being on are mutually exclusive. + if (on) setPowerful(false); +} + +/// Get the Economical mode of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRDaikin152::getEcono(void) const { return _.Econo; } + +/// Set the Sensor mode of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRDaikin152::setSensor(const bool on) { _.Sensor = on; } + +/// Get the Sensor mode of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRDaikin152::getSensor(void) const { return _.Sensor; } + +/// Set the Comfort mode of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRDaikin152::setComfort(const bool on) { + _.Comfort = on; + if (on) { + // Comfort mode is incompatible with Powerful mode. + setPowerful(false); + // It also sets the fan to auto and turns off swingv. + setFan(kDaikinFanAuto); + setSwingV(false); + } +} + +/// Get the Comfort mode of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRDaikin152::getComfort(void) const { return _.Comfort; } + +/// Convert the current internal state into its stdAc::state_t equivalent. +/// @return The stdAc equivalent of the native settings. +stdAc::state_t IRDaikin152::toCommon(void) const { + stdAc::state_t result; + result.protocol = decode_type_t::DAIKIN152; + result.model = -1; // No models used. + result.power = _.Power; + result.mode = IRDaikinESP::toCommonMode(_.Mode); + result.celsius = true; + result.degrees = _.Temp; + result.fanspeed = IRDaikinESP::toCommonFanSpeed(getFan()); + result.swingv = _.SwingV ? stdAc::swingv_t::kAuto : stdAc::swingv_t::kOff; + result.quiet = _.Quiet; + result.turbo = _.Powerful; + result.econo = _.Econo; + // Not supported. + result.swingh = stdAc::swingh_t::kOff; + result.clean = false; + result.filter = false; + result.light = 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 IRDaikin152::toString(void) const { + String result = ""; + result.reserve(180); // Reserve some heap for the string to reduce fragging. + result += addBoolToString(_.Power, kPowerStr, false); + result += addModeToString(_.Mode, kDaikinAuto, kDaikinCool, kDaikinHeat, + kDaikinDry, kDaikinFan); + result += addTempToString(_.Temp); + result += addFanToString(getFan(), kDaikinFanMax, kDaikinFanMin, + kDaikinFanAuto, kDaikinFanQuiet, kDaikinFanMed); + result += addBoolToString(_.SwingV, kSwingVStr); + result += addBoolToString(_.Powerful, kPowerfulStr); + result += addBoolToString(_.Quiet, kQuietStr); + result += addBoolToString(_.Econo, kEconoStr); + result += addBoolToString(_.Sensor, kSensorStr); + result += addBoolToString(_.Comfort, kComfortStr); + return result; +} + +#if SEND_DAIKIN64 +/// Send a Daikin64 (64-bit) A/C 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. +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1064 +void IRsend::sendDaikin64(const uint64_t data, const uint16_t nbits, + const uint16_t repeat) { + enableIROut(kDaikin64Freq); + for (uint16_t r = 0; r <= repeat; r++) { + for (uint8_t i = 0; i < 2; i++) { + // Leader + mark(kDaikin64LdrMark); + space(kDaikin64LdrSpace); + } + // Header + Data + Footer #1 + sendGeneric(kDaikin64HdrMark, kDaikin64HdrSpace, + kDaikin64BitMark, kDaikin64OneSpace, + kDaikin64BitMark, kDaikin64ZeroSpace, + kDaikin64BitMark, kDaikin64Gap, + data, nbits, kDaikin64Freq, false, 0, 50); + // Footer #2 + mark(kDaikin64HdrMark); + space(kDefaultMessageGap); // A guess of the gap between messages. + } +} +#endif // SEND_DAIKIN64 + +#if DECODE_DAIKIN64 +/// Decode the supplied Daikin 64-bit message. (DAIKIN64) +/// 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/1064 +bool IRrecv::decodeDaikin64(decode_results *results, uint16_t offset, + const uint16_t nbits, const bool strict) { + if (results->rawlen < 2 * nbits + kDaikin64Overhead - offset) + return false; // Too short a message to match. + // Compliance + if (strict && nbits != kDaikin64Bits) + return false; + + // Leader + for (uint8_t i = 0; i < 2; i++) { + if (!matchMark(results->rawbuf[offset++], kDaikin64LdrMark)) + return false; + if (!matchSpace(results->rawbuf[offset++], kDaikin64LdrSpace)) + return false; + } + // Header + Data + Footer #1 + uint16_t used = matchGeneric(results->rawbuf + offset, &results->value, + results->rawlen - offset, nbits, + kDaikin64HdrMark, kDaikin64HdrSpace, + kDaikin64BitMark, kDaikin64OneSpace, + kDaikin64BitMark, kDaikin64ZeroSpace, + kDaikin64BitMark, kDaikin64Gap, + false, _tolerance + kDaikin64ToleranceDelta, + kMarkExcess, false); + if (used == 0) return false; + offset += used; + // Footer #2 + if (!matchMark(results->rawbuf[offset++], kDaikin64HdrMark)) + return false; + + // Compliance + if (strict && !IRDaikin64::validChecksum(results->value)) return false; + // Success + results->decode_type = decode_type_t::DAIKIN64; + results->bits = nbits; + results->command = 0; + results->address = 0; + return true; +} +#endif // DAIKIN64 + +/// 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? +IRDaikin64::IRDaikin64(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 IRDaikin64::begin(void) { _irsend.begin(); } + +#if SEND_DAIKIN64 +/// Send the current internal state as an IR message. +/// @param[in] repeat Nr. of times the message will be repeated. +void IRDaikin64::send(const uint16_t repeat) { + _irsend.sendDaikin64(getRaw(), kDaikin64Bits, repeat); +} +#endif // SEND_DAIKIN64 + +/// 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 IRDaikin64::calcChecksum(const uint64_t state) { + uint64_t data = GETBITS64(state, 0, kDaikin64ChecksumOffset); + 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 state to verify the checksum of. +/// @return true, if the state has a valid checksum. Otherwise, false. +bool IRDaikin64::validChecksum(const uint64_t state) { + // Validate the checksum of the given state. + return (GETBITS64(state, kDaikin64ChecksumOffset, + kDaikin64ChecksumSize) == calcChecksum(state)); +} + +/// Calculate and set the checksum values for the internal state. +void IRDaikin64::checksum(void) { _.Sum = calcChecksum(_.raw); } + +/// Reset the internal state to a fixed known good state. +void IRDaikin64::stateReset(void) { _.raw = kDaikin64KnownGoodState; } + +/// 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 IRDaikin64::getRaw(void) { + checksum(); // Ensure correct settings before sending. + return _.raw; +} + +/// Set the internal state from a valid code for this protocol. +/// @param[in] new_state A valid code for this protocol. +void IRDaikin64::setRaw(const uint64_t new_state) { _.raw = new_state; } + +/// Set the Power toggle setting of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRDaikin64::setPowerToggle(const bool on) { _.Power = on; } + +/// Get the Power toggle setting of the A/C. +/// @return The current operating mode setting. +bool IRDaikin64::getPowerToggle(void) const { return _.Power; } + +/// Set the temperature. +/// @param[in] temp The temperature in degrees celsius. +void IRDaikin64::setTemp(const uint8_t temp) { + uint8_t degrees = std::max(temp, kDaikin64MinTemp); + degrees = std::min(degrees, kDaikin64MaxTemp); + _.Temp = uint8ToBcd(degrees); +} + +/// Get the current temperature setting. +/// @return The current setting for temp. in degrees celsius. +uint8_t IRDaikin64::getTemp(void) const { return bcdToUint8(_.Temp); } + +/// Get the operating mode setting of the A/C. +/// @return The current operating mode setting. +uint8_t IRDaikin64::getMode(void) const { return _.Mode; } + +/// Set the operating mode of the A/C. +/// @param[in] mode The desired operating mode. +void IRDaikin64::setMode(const uint8_t mode) { + switch (mode) { + case kDaikin64Fan: + case kDaikin64Dry: + case kDaikin64Cool: + case kDaikin64Heat: + _.Mode = mode; + break; + default: + _.Mode = kDaikin64Cool; + } +} + +/// 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 IRDaikin64::convertMode(const stdAc::opmode_t mode) { + switch (mode) { + case stdAc::opmode_t::kDry: return kDaikin64Dry; + case stdAc::opmode_t::kFan: return kDaikin64Fan; + case stdAc::opmode_t::kHeat: return kDaikin64Heat; + default: return kDaikin64Cool; + } +} + +/// 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 IRDaikin64::toCommonMode(const uint8_t mode) { + switch (mode) { + case kDaikin64Cool: return stdAc::opmode_t::kCool; + case kDaikin64Heat: return stdAc::opmode_t::kHeat; + case kDaikin64Dry: return stdAc::opmode_t::kDry; + case kDaikin64Fan: return stdAc::opmode_t::kFan; + default: return stdAc::opmode_t::kAuto; + } +} + +/// Get the current fan speed setting. +/// @return The current fan speed. +uint8_t IRDaikin64::getFan(void) const { return _.Fan; } + +/// Set the speed of the fan. +/// @param[in] speed The desired setting. +void IRDaikin64::setFan(const uint8_t speed) { + switch (speed) { + case kDaikin64FanQuiet: + case kDaikin64FanTurbo: + case kDaikin64FanAuto: + case kDaikin64FanHigh: + case kDaikin64FanMed: + case kDaikin64FanLow: + _.Fan = speed; + break; + default: + _.Fan = kDaikin64FanAuto; + } +} + +/// 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 IRDaikin64::convertFan(const stdAc::fanspeed_t speed) { + switch (speed) { + case stdAc::fanspeed_t::kMin: return kDaikin64FanQuiet; + case stdAc::fanspeed_t::kLow: return kDaikin64FanLow; + case stdAc::fanspeed_t::kMedium: return kDaikin64FanMed; + case stdAc::fanspeed_t::kHigh: return kDaikin64FanHigh; + case stdAc::fanspeed_t::kMax: return kDaikin64FanTurbo; + default: return kDaikin64FanAuto; + } +} + +/// 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 IRDaikin64::toCommonFanSpeed(const uint8_t speed) { + switch (speed) { + case kDaikin64FanTurbo: return stdAc::fanspeed_t::kMax; + case kDaikin64FanHigh: return stdAc::fanspeed_t::kHigh; + case kDaikin64FanMed: return stdAc::fanspeed_t::kMedium; + case kDaikin64FanLow: return stdAc::fanspeed_t::kLow; + case kDaikinFanQuiet: return stdAc::fanspeed_t::kMin; + default: return stdAc::fanspeed_t::kAuto; + } +} + +/// Get the Turbo (Powerful) mode status of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRDaikin64::getTurbo(void) const { return _.Fan == kDaikin64FanTurbo; } + +/// Set the Turbo (Powerful) mode of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRDaikin64::setTurbo(const bool on) { + if (on) { + setFan(kDaikin64FanTurbo); + } else if (_.Fan == kDaikin64FanTurbo) { + setFan(kDaikin64FanAuto); + } +} + +/// Get the Quiet mode status of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRDaikin64::getQuiet(void) const { return _.Fan == kDaikin64FanQuiet; } + +/// Set the Quiet mode of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRDaikin64::setQuiet(const bool on) { + if (on) { + setFan(kDaikin64FanQuiet); + } else if (_.Fan == kDaikin64FanQuiet) { + setFan(kDaikin64FanAuto); + } +} + +/// Set the Vertical Swing mode of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRDaikin64::setSwingVertical(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 IRDaikin64::getSwingVertical(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 IRDaikin64::setSleep(const bool on) { _.Sleep = on; } + +/// Get the Sleep mode of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRDaikin64::getSleep(void) const { return _.Sleep; } + +/// Set the clock on the A/C unit. +/// @param[in] mins_since_midnight Nr. of minutes past midnight. +void IRDaikin64::setClock(const uint16_t mins_since_midnight) { + uint16_t mins = mins_since_midnight; + if (mins_since_midnight >= 24 * 60) mins = 0; // Bounds check. + _.ClockMins = uint8ToBcd(mins % 60); + _.ClockHours = uint8ToBcd(mins / 60); +} + +/// Get the clock time to be sent to the A/C unit. +/// @return The number of minutes past midnight. +uint16_t IRDaikin64::getClock(void) const { + return bcdToUint8(_.ClockHours) * 60 + bcdToUint8(_.ClockMins); +} + +/// Set the enable status of the On Timer. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRDaikin64::setOnTimeEnabled(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 IRDaikin64::getOnTimeEnabled(void) const { return _.OnTimer; } + +/// Get the On Timer time to be sent to the A/C unit. +/// @return The number of minutes past midnight. +uint16_t IRDaikin64::getOnTime(void) const { return GETTIME(On); } + +/// Set the On Timer time for the A/C unit. +/// @param[in] mins_since_midnight Nr. of minutes past midnight. +void IRDaikin64::setOnTime(const uint16_t mins_since_midnight) { + SETTIME(On, mins_since_midnight); +} + +/// Set the enable status of the Off Timer. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRDaikin64::setOffTimeEnabled(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 IRDaikin64::getOffTimeEnabled(void) const { return _.OffTimer; } + +/// Get the Off Timer time to be sent to the A/C unit. +/// @return The number of minutes past midnight. +uint16_t IRDaikin64::getOffTime(void) const { return GETTIME(Off); } + +/// Set the Off Timer time for the A/C unit. +/// @param[in] mins_since_midnight Nr. of minutes past midnight. +void IRDaikin64::setOffTime(const uint16_t mins_since_midnight) { + SETTIME(Off, mins_since_midnight); +} + +/// Convert the current internal state into a human readable string. +/// @return A human readable string. +String IRDaikin64::toString(void) const { + String result = ""; + result.reserve(120); // Reserve some heap for the string to reduce fragging. + result += addBoolToString(_.Power, kPowerToggleStr, false); + result += addModeToString(_.Mode, 0xFF, kDaikin64Cool, + kDaikin64Heat, kDaikin64Dry, kDaikin64Fan); + result += addTempToString(getTemp()); + if (!getTurbo()) { + result += addFanToString(_.Fan, kDaikin64FanHigh, kDaikin64FanLow, + kDaikin64FanAuto, kDaikin64FanQuiet, + kDaikin64FanMed); + } else { + result += addIntToString(_.Fan, kFanStr); + result += kSpaceLBraceStr; + result += kTurboStr; + result += ')'; + } + result += addBoolToString(getTurbo(), kTurboStr); + result += addBoolToString(getQuiet(), kQuietStr); + result += addBoolToString(_.SwingV, kSwingVStr); + result += addBoolToString(_.Sleep, kSleepStr); + result += addLabeledString(minsToString(getClock()), kClockStr); + result += addLabeledString(_.OnTimer + ? minsToString(getOnTime()) : kOffStr, + kOnTimerStr); + result += addLabeledString(_.OffTimer + ? minsToString(getOffTime()) : kOffStr, + kOffTimerStr); + return result; +} + +/// Convert the current internal state into its stdAc::state_t equivalent. +/// @param[in] prev Ptr to a previous state. +/// @return The stdAc equivalent of the native settings. +stdAc::state_t IRDaikin64::toCommon(const stdAc::state_t *prev) const { + stdAc::state_t result; + if (prev != NULL) result = *prev; + result.protocol = decode_type_t::DAIKIN64; + 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.turbo = getTurbo(); + result.quiet = getQuiet(); + result.sleep = _.Sleep ? 0 : -1; + result.clock = getClock(); + // Not supported. + result.swingh = stdAc::swingh_t::kOff; + result.clean = false; + result.filter = false; + result.beep = false; + result.econo = false; + result.light = false; + return result; +} diff --git a/lib/IRremoteESP8266/src/ir_Daikin.h b/lib/IRremoteESP8266/src/ir_Daikin.h index 6487d20d7c..0a12c5b69e 100644 --- a/lib/IRremoteESP8266/src/ir_Daikin.h +++ b/lib/IRremoteESP8266/src/ir_Daikin.h @@ -54,11 +54,11 @@ #ifndef UNIT_TEST #include #endif -#include "../src/IRrecv.h" -#include "../src/IRremoteESP8266.h" -#include "../src/IRsend.h" +#include "IRrecv.h" +#include "IRremoteESP8266.h" +#include "IRsend.h" #ifdef UNIT_TEST -#include "../src/IRsend_test.h" +#include "IRsend_test.h" #endif /// Native representation of a Daikin A/C message. diff --git a/lib/IRremoteESP8266/src/ir_Delonghi.cpp b/lib/IRremoteESP8266/src/ir_Delonghi.cpp new file mode 100644 index 0000000000..21a787799b --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_Delonghi.cpp @@ -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 + +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; +} diff --git a/lib/IRremoteESP8266/src/ir_Delonghi.h b/lib/IRremoteESP8266/src/ir_Delonghi.h index 7954e4ade1..ac2394cb16 100644 --- a/lib/IRremoteESP8266/src/ir_Delonghi.h +++ b/lib/IRremoteESP8266/src/ir_Delonghi.h @@ -16,10 +16,10 @@ #ifndef UNIT_TEST #include #endif -#include "../src/IRremoteESP8266.h" -#include "../src/IRsend.h" +#include "IRremoteESP8266.h" +#include "IRsend.h" #ifdef UNIT_TEST -#include "../src/IRsend_test.h" +#include "IRsend_test.h" #endif /// Native representation of a Delonghi A/C message. diff --git a/lib/IRremoteESP8266/src/ir_Denon.cpp b/lib/IRremoteESP8266/src/ir_Denon.cpp new file mode 100644 index 0000000000..700fd31cbc --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_Denon.cpp @@ -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 +#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 diff --git a/lib/IRremoteESP8266/src/ir_Dish.cpp b/lib/IRremoteESP8266/src/ir_Dish.cpp new file mode 100644 index 0000000000..94f5450b80 --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_Dish.cpp @@ -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 diff --git a/lib/IRremoteESP8266/src/ir_Doshisha.cpp b/lib/IRremoteESP8266/src/ir_Doshisha.cpp new file mode 100644 index 0000000000..0a5fadedb8 --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_Doshisha.cpp @@ -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 diff --git a/lib/IRremoteESP8266/src/ir_Ecoclim.cpp b/lib/IRremoteESP8266/src/ir_Ecoclim.cpp new file mode 100644 index 0000000000..b16f8beb2d --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_Ecoclim.cpp @@ -0,0 +1,423 @@ +// Copyright 2021 David Conran + +/// @file +/// @brief EcoClim A/C protocol. +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1397 + +#include "ir_Ecoclim.h" +#include +#include +#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.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; +} diff --git a/lib/IRremoteESP8266/src/ir_Ecoclim.h b/lib/IRremoteESP8266/src/ir_Ecoclim.h index 63a91c7109..ea243f67cc 100644 --- a/lib/IRremoteESP8266/src/ir_Ecoclim.h +++ b/lib/IRremoteESP8266/src/ir_Ecoclim.h @@ -16,10 +16,10 @@ #ifndef UNIT_TEST #include #endif -#include "../src/IRremoteESP8266.h" -#include "../src/IRsend.h" +#include "IRremoteESP8266.h" +#include "IRsend.h" #ifdef UNIT_TEST -#include "../src/IRsend_test.h" +#include "IRsend_test.h" #endif // Constants diff --git a/lib/IRremoteESP8266/src/ir_Electra.h b/lib/IRremoteESP8266/src/ir_Electra.h index e21d09930c..8fd4ee182a 100644 --- a/lib/IRremoteESP8266/src/ir_Electra.h +++ b/lib/IRremoteESP8266/src/ir_Electra.h @@ -22,10 +22,10 @@ #ifndef UNIT_TEST #include #endif -#include "../src/IRremoteESP8266.h" -#include "../src/IRsend.h" +#include "IRremoteESP8266.h" +#include "IRsend.h" #ifdef UNIT_TEST -#include "../src/IRsend_test.h" +#include "IRsend_test.h" #endif /// Native representation of a Electra A/C message. diff --git a/lib/IRremoteESP8266/src/ir_EliteScreens.cpp b/lib/IRremoteESP8266/src/ir_EliteScreens.cpp new file mode 100644 index 0000000000..bfbdcc1e70 --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_EliteScreens.cpp @@ -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 diff --git a/lib/IRremoteESP8266/src/ir_Epson.cpp b/lib/IRremoteESP8266/src/ir_Epson.cpp new file mode 100644 index 0000000000..a92beef4ff --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_Epson.cpp @@ -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 +#include +#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 diff --git a/lib/IRremoteESP8266/src/ir_Fujitsu.cpp b/lib/IRremoteESP8266/src/ir_Fujitsu.cpp new file mode 100644 index 0000000000..da4b41dcf6 --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_Fujitsu.cpp @@ -0,0 +1,1043 @@ +// Copyright 2017 Jonny Graham +// Copyright 2017-2021 David Conran +// Copyright 2021 siriuslzx + +/// @file +/// @brief Support for Fujitsu A/C protocols. +/// Fujitsu A/C support added by Jonny Graham & David Conran +/// @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 + +#include "ir_Fujitsu.h" +#include +#ifndef ARDUINO +#include +#endif +#include "IRsend.h" +#include "IRtext.h" +#include "IRutils.h" + +// Ref: +// These values are based on averages of measurements +const uint16_t kFujitsuAcHdrMark = 3324; +const uint16_t kFujitsuAcHdrSpace = 1574; +const uint16_t kFujitsuAcBitMark = 448; +const uint16_t kFujitsuAcOneSpace = 1182; +const uint16_t kFujitsuAcZeroSpace = 390; +const uint16_t kFujitsuAcMinGap = 8100; +const uint8_t kFujitsuAcExtraTolerance = 5; // Extra tolerance percentage. + +using irutils::addBoolToString; +using irutils::addIntToString; +using irutils::addLabeledString; +using irutils::addModeToString; +using irutils::addModelToString; +using irutils::addFanToString; +using irutils::addTempFloatToString; +using irutils::minsToString; + +#if SEND_FUJITSU_AC +/// Send a Fujitsu A/C formatted message. +/// Status: STABLE / Known Good. +/// @param[in] data The message to be sent. +/// @param[in] nbytes The number of bytes of message to be sent. +/// Typically one of: +/// kFujitsuAcStateLength, +/// kFujitsuAcStateLength - 1, +/// kFujitsuAcStateLengthShort, +/// kFujitsuAcStateLengthShort - 1 +/// @param[in] repeat The number of times the command is to be repeated. +void IRsend::sendFujitsuAC(const unsigned char data[], const uint16_t nbytes, + const uint16_t repeat) { + sendGeneric(kFujitsuAcHdrMark, kFujitsuAcHdrSpace, kFujitsuAcBitMark, + kFujitsuAcOneSpace, kFujitsuAcBitMark, kFujitsuAcZeroSpace, + kFujitsuAcBitMark, kFujitsuAcMinGap, data, nbytes, 38, false, + repeat, 50); +} +#endif // SEND_FUJITSU_AC + +// Code to emulate Fujitsu A/C IR remote control unit. + +/// Class Constructor +/// @param[in] pin GPIO to be used when sending. +/// @param[in] model The enum for the model of A/C to be emulated. +/// @param[in] inverted Is the output signal to be inverted? +/// @param[in] use_modulation Is frequency modulation to be used? +IRFujitsuAC::IRFujitsuAC(const uint16_t pin, + const fujitsu_ac_remote_model_t model, + const bool inverted, const bool use_modulation) + : _irsend(pin, inverted, use_modulation) { + setModel(model); + stateReset(); +} + +/// Set the currently emulated model of the A/C. +/// @param[in] model An enum representing the model to support/emulate. +void IRFujitsuAC::setModel(const fujitsu_ac_remote_model_t model) { + _model = model; + switch (model) { + case fujitsu_ac_remote_model_t::ARDB1: + case fujitsu_ac_remote_model_t::ARJW2: + _state_length = kFujitsuAcStateLength - 1; + _state_length_short = kFujitsuAcStateLengthShort - 1; + break; + case fujitsu_ac_remote_model_t::ARRY4: + case fujitsu_ac_remote_model_t::ARRAH2E: + case fujitsu_ac_remote_model_t::ARREB1E: + default: + _state_length = kFujitsuAcStateLength; + _state_length_short = kFujitsuAcStateLengthShort; + } +} + +/// Get the currently emulated/detected model of the A/C. +/// @return The enum representing the model of A/C. +fujitsu_ac_remote_model_t IRFujitsuAC::getModel(void) const { return _model; } + +/// Reset the state of the remote to a known good state/sequence. +void IRFujitsuAC::stateReset(void) { + for (size_t i = 0; i < kFujitsuAcStateLength; i++) { + _.longcode[i] = 0; + } + setTemp(24); + _.Fan = kFujitsuAcFanHigh; + _.Mode = kFujitsuAcModeCool; + _.Swing = kFujitsuAcSwingBoth; + _cmd = kFujitsuAcCmdTurnOn; + _.Filter = false; + _.Clean = false; + _.TimerType = kFujitsuAcStopTimers; + _.OnTimer = 0; + _.OffTimer = 0; + _.longcode[0] = 0x14; + _.longcode[1] = 0x63; + _.longcode[3] = 0x10; + _.longcode[4] = 0x10; +} + +/// Set up hardware to be able to send a message. +void IRFujitsuAC::begin(void) { _irsend.begin(); } + +#if SEND_FUJITSU_AC +/// Send the current internal state as an IR message. +/// @param[in] repeat Nr. of times the message will be repeated. +void IRFujitsuAC::send(const uint16_t repeat) { + _irsend.sendFujitsuAC(getRaw(), getStateLength(), repeat); +} +#endif // SEND_FUJITSU_AC + +/// Update the length (size) of the state code for the current configuration. +/// @return true, if use long codes; false, use short codes. +bool IRFujitsuAC::updateUseLongOrShort(void) { + bool fullCmd = false; + switch (_cmd) { + case kFujitsuAcCmdTurnOff: // 0x02 + case kFujitsuAcCmdEcono: // 0x09 + case kFujitsuAcCmdPowerful: // 0x39 + case kFujitsuAcCmdStepVert: // 0x6C + case kFujitsuAcCmdToggleSwingVert: // 0x6D + case kFujitsuAcCmdStepHoriz: // 0x79 + case kFujitsuAcCmdToggleSwingHoriz: // 0x7A + _.Cmd = _cmd; + break; + default: + switch (_model) { + case fujitsu_ac_remote_model_t::ARRY4: + case fujitsu_ac_remote_model_t::ARRAH2E: + case fujitsu_ac_remote_model_t::ARREB1E: + case fujitsu_ac_remote_model_t::ARREW4E: + _.Cmd = 0xFE; + break; + case fujitsu_ac_remote_model_t::ARDB1: + case fujitsu_ac_remote_model_t::ARJW2: + _.Cmd = 0xFC; + break; + } + fullCmd = true; + break; + } + return fullCmd; +} + +/// Calculate and set the checksum values for the internal state. +void IRFujitsuAC::checkSum(void) { + if (updateUseLongOrShort()) { // Is it a long code? + // Nr. of bytes in the message after this byte. + _.RestLength = _state_length - 7; + _.Protocol = (_model == fujitsu_ac_remote_model_t::ARREW4E) ? 0x31 : 0x30; + _.Power = (_cmd == kFujitsuAcCmdTurnOn) || get10CHeat(); + + // These values depend on model + if (_model != fujitsu_ac_remote_model_t::ARREB1E && + _model != fujitsu_ac_remote_model_t::ARREW4E) { + _.OutsideQuiet = 0; + if (_model != fujitsu_ac_remote_model_t::ARRAH2E) { + _.TimerType = kFujitsuAcStopTimers; + } + } + if (_model != fujitsu_ac_remote_model_t::ARRY4) { + if (_model != fujitsu_ac_remote_model_t::ARREW4E) _.Clean = false; + _.Filter = false; + } + // Set the On/Off/Sleep timer Nr of mins. + _.OffTimer = getOffSleepTimer(); + _.OnTimer = getOnTimer(); + // Enable bit for the Off/Sleep timer + _.OffTimerEnable = _.OffTimer > 0; + // Enable bit for the On timer + _.OnTimerEnable = _.OnTimer > 0; + + uint8_t checksum = 0; + uint8_t checksum_complement = 0; + switch (_model) { + case fujitsu_ac_remote_model_t::ARDB1: + case fujitsu_ac_remote_model_t::ARJW2: + _.Swing = kFujitsuAcSwingOff; + checksum = sumBytes(_.longcode, _state_length - 1); + checksum_complement = 0x9B; + break; + case fujitsu_ac_remote_model_t::ARREB1E: + case fujitsu_ac_remote_model_t::ARRAH2E: + case fujitsu_ac_remote_model_t::ARRY4: + _.unknown = 1; + // FALL THRU + default: + checksum = sumBytes(_.longcode + _state_length_short, + _state_length - _state_length_short - 1); + } + // and negate the checksum and store it in the last byte. + _.longcode[_state_length - 1] = checksum_complement - checksum; + } else { // short codes + for (size_t i = 0; i < _state_length_short; i++) { + _.shortcode[i] = _.longcode[i]; + } + switch (_model) { + case fujitsu_ac_remote_model_t::ARRY4: + case fujitsu_ac_remote_model_t::ARRAH2E: + case fujitsu_ac_remote_model_t::ARREB1E: + case fujitsu_ac_remote_model_t::ARREW4E: + // The last byte is the inverse of penultimate byte + _.shortcode[_state_length_short - 1] = + ~_.shortcode[_state_length_short - 2]; + break; + default: + {}; // We don't need to do anything for the others. + } + } +} + +/// Get the length (size) of the state code for the current configuration. +/// @return The length of the state array required for this config. +uint8_t IRFujitsuAC::getStateLength(void) { + return updateUseLongOrShort() ? _state_length : _state_length_short; +} + +/// 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* IRFujitsuAC::getRaw(void) { + checkSum(); + if (_.Cmd == 0xFE || _.Cmd == 0xFC) + return _.longcode; + return _.shortcode; +} + +/// Build the internal state/config from the current (raw) A/C message. +/// @param[in] length Size of the current/used (raw) A/C message array. +void IRFujitsuAC::buildFromState(const uint16_t length) { + switch (length) { + case kFujitsuAcStateLength - 1: + case kFujitsuAcStateLengthShort - 1: + setModel(fujitsu_ac_remote_model_t::ARDB1); + // ARJW2 has horizontal swing. + if (_.Swing > kFujitsuAcSwingVert) + setModel(fujitsu_ac_remote_model_t::ARJW2); + break; + default: + switch (_.Cmd) { + case kFujitsuAcCmdEcono: + case kFujitsuAcCmdPowerful: + setModel(fujitsu_ac_remote_model_t::ARREB1E); + break; + default: + setModel(fujitsu_ac_remote_model_t::ARRAH2E); + } + } + switch (_.RestLength) { + case 8: + if (_model != fujitsu_ac_remote_model_t::ARJW2) + setModel(fujitsu_ac_remote_model_t::ARDB1); + break; + case 9: + if (_model != fujitsu_ac_remote_model_t::ARREB1E) + setModel(fujitsu_ac_remote_model_t::ARRAH2E); + break; + } + if (_.Power) + setCmd(kFujitsuAcCmdTurnOn); + else + setCmd(kFujitsuAcCmdStayOn); + // Currently the only way we know how to tell ARRAH2E & ARRY4 apart is if + // either the raw Filter or Clean setting is on. + if (_model == fujitsu_ac_remote_model_t::ARRAH2E && (_.Filter || _.Clean)) + setModel(fujitsu_ac_remote_model_t::ARRY4); + if (_state_length == kFujitsuAcStateLength && _.OutsideQuiet) + setModel(fujitsu_ac_remote_model_t::ARREB1E); + switch (_.Cmd) { + case kFujitsuAcCmdTurnOff: + case kFujitsuAcCmdStepHoriz: + case kFujitsuAcCmdToggleSwingHoriz: + case kFujitsuAcCmdStepVert: + case kFujitsuAcCmdToggleSwingVert: + case kFujitsuAcCmdEcono: + case kFujitsuAcCmdPowerful: + setCmd(_.Cmd); + break; + } + if (_.Protocol == 0x31) setModel(fujitsu_ac_remote_model_t::ARREW4E); +} + +/// Set the internal state from a valid code for this protocol. +/// @param[in] newState A valid code for this protocol. +/// @param[in] length Size of the newState array. +/// @return true, if successful; Otherwise false. (i.e. size check) +bool IRFujitsuAC::setRaw(const uint8_t newState[], const uint16_t length) { + if (length > kFujitsuAcStateLength) return false; + for (uint16_t i = 0; i < kFujitsuAcStateLength; i++) { + if (i < length) + _.longcode[i] = newState[i]; + else + _.longcode[i] = 0; + } + buildFromState(length); + return true; +} + +/// Request the A/C to step the Horizontal Swing. +void IRFujitsuAC::stepHoriz(void) { setCmd(kFujitsuAcCmdStepHoriz); } + +/// Request the A/C to toggle the Horizontal Swing mode. +/// @param[in] update Do we need to update the general swing config? +void IRFujitsuAC::toggleSwingHoriz(const bool update) { + // Toggle the current setting. + if (update) setSwing(getSwing() ^ kFujitsuAcSwingHoriz); + // and set the appropriate special command. + setCmd(kFujitsuAcCmdToggleSwingHoriz); +} + +/// Request the A/C to step the Vertical Swing. +void IRFujitsuAC::stepVert(void) { setCmd(kFujitsuAcCmdStepVert); } + +/// Request the A/C to toggle the Vertical Swing mode. +/// @param[in] update Do we need to update the general swing config? +void IRFujitsuAC::toggleSwingVert(const bool update) { + // Toggle the current setting. + if (update) setSwing(getSwing() ^ kFujitsuAcSwingVert); + // and set the appropriate special command. + setCmd(kFujitsuAcCmdToggleSwingVert); +} + +/// Set the requested (special) command part for the A/C message. +/// @param[in] cmd The special command code. +void IRFujitsuAC::setCmd(const uint8_t cmd) { + switch (cmd) { + case kFujitsuAcCmdTurnOff: + case kFujitsuAcCmdTurnOn: + case kFujitsuAcCmdStayOn: + case kFujitsuAcCmdStepVert: + case kFujitsuAcCmdToggleSwingVert: + _cmd = cmd; + break; + case kFujitsuAcCmdStepHoriz: + case kFujitsuAcCmdToggleSwingHoriz: + switch (_model) { + // Only these remotes have horizontal. + case fujitsu_ac_remote_model_t::ARRAH2E: + case fujitsu_ac_remote_model_t::ARJW2: + _cmd = cmd; + break; + default: + _cmd = kFujitsuAcCmdStayOn; + } + break; + case kFujitsuAcCmdEcono: + case kFujitsuAcCmdPowerful: + switch (_model) { + // Only these remotes have these commands. + case ARREB1E: + case ARREW4E: + _cmd = cmd; + break; + default: + _cmd = kFujitsuAcCmdStayOn; + } + break; + default: + _cmd = kFujitsuAcCmdStayOn; + } +} + +/// Set the requested (special) command part for the A/C message. +/// @return The special command code. +uint8_t IRFujitsuAC::getCmd(void) const { + return _cmd; +} + +/// Change the power setting. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRFujitsuAC::setPower(const bool on) { + setCmd(on ? kFujitsuAcCmdTurnOn : kFujitsuAcCmdTurnOff); +} + +/// Set the requested power state of the A/C to off. +void IRFujitsuAC::off(void) { setPower(false); } + +/// Set the requested power state of the A/C to on. +void IRFujitsuAC::on(void) { setPower(true); } + +/// Get the value of the current power setting. +/// @return true, the setting is on. false, the setting is off. +bool IRFujitsuAC::getPower(void) const { return _cmd != kFujitsuAcCmdTurnOff; } + +/// Set the Outside Quiet mode of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRFujitsuAC::setOutsideQuiet(const bool on) { + _.OutsideQuiet = on; + setCmd(kFujitsuAcCmdStayOn); // No special command involved. +} + +/// Get the Outside Quiet mode status of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRFujitsuAC::getOutsideQuiet(void) const { + switch (_model) { + // Only ARREB1E & ARREW4E seems to have this mode. + case fujitsu_ac_remote_model_t::ARREB1E: + case fujitsu_ac_remote_model_t::ARREW4E: + return _.OutsideQuiet; + default: return false; + } +} + +/// Set the temperature. +/// @param[in] temp The temperature in degrees. +/// @param[in] useCelsius Use Celsius or Fahrenheit? +void IRFujitsuAC::setTemp(const float temp, const bool useCelsius) { + float mintemp; + float maxtemp; + uint8_t offset; + bool _useCelsius; + float _temp; + + switch (_model) { + // These models have native Fahrenheit & Celsius upport. + case fujitsu_ac_remote_model_t::ARREW4E: + _useCelsius = useCelsius; + _temp = temp; + break; + // Make sure everything else uses Celsius. + default: + _useCelsius = true; + _temp = useCelsius ? temp : fahrenheitToCelsius(temp); + } + setCelsius(_useCelsius); + if (_useCelsius) { + mintemp = kFujitsuAcMinTemp; + maxtemp = kFujitsuAcMaxTemp; + offset = kFujitsuAcTempOffsetC; + } else { + mintemp = kFujitsuAcMinTempF; + maxtemp = kFujitsuAcMaxTempF; + offset = kFujitsuAcTempOffsetF; + } + _temp = std::max(mintemp, _temp); + _temp = std::min(maxtemp, _temp); + if (_useCelsius) { + if (_model == fujitsu_ac_remote_model_t::ARREW4E) + _.Temp = (_temp - (offset / 2)) * 2; + else + _.Temp = (_temp - offset) * 4; + } else { + _.Temp = _temp - offset; + } + setCmd(kFujitsuAcCmdStayOn); // No special command involved. +} + +/// Get the current temperature setting. +/// @return The current setting for temp. in degrees of the currently set units. +float IRFujitsuAC::getTemp(void) const { + if (_model == fujitsu_ac_remote_model_t::ARREW4E) { + if (_.Fahrenheit) // Currently only ARREW4E supports native Fahrenheit. + return _.Temp + kFujitsuAcTempOffsetF; + else + return (_.Temp / 2.0) + (kFujitsuAcMinTemp / 2); + } else { + return _.Temp / 4 + kFujitsuAcMinTemp; + } +} + +/// Set the speed of the fan. +/// @param[in] fanSpeed The desired setting. +void IRFujitsuAC::setFanSpeed(const uint8_t fanSpeed) { + if (fanSpeed > kFujitsuAcFanQuiet) + _.Fan = kFujitsuAcFanHigh; // Set the fan to maximum if out of range. + else + _.Fan = fanSpeed; + setCmd(kFujitsuAcCmdStayOn); // No special command involved. +} + +/// Get the current fan speed setting. +/// @return The current fan speed. +uint8_t IRFujitsuAC::getFanSpeed(void) const { return _.Fan; } + +/// Set the operating mode of the A/C. +/// @param[in] mode The desired operating mode. +void IRFujitsuAC::setMode(const uint8_t mode) { + if (mode > kFujitsuAcModeHeat) + _.Mode = kFujitsuAcModeHeat; // Set the mode to maximum if out of range. + else + _.Mode = mode; + setCmd(kFujitsuAcCmdStayOn); // No special command involved. +} + +/// Get the operating mode setting of the A/C. +/// @return The current operating mode setting. +uint8_t IRFujitsuAC::getMode(void) const { return _.Mode; } + +/// Set the requested swing operation mode of the A/C unit. +/// @param[in] swingMode The swingMode code for the A/C. +/// Vertical, Horizon, or Both. See constants for details. +/// @note Not all models support all possible swing modes. +void IRFujitsuAC::setSwing(const uint8_t swingMode) { + _.Swing = swingMode; + switch (_model) { + // No Horizontal support. + case fujitsu_ac_remote_model_t::ARDB1: + case fujitsu_ac_remote_model_t::ARREB1E: + case fujitsu_ac_remote_model_t::ARRY4: + // Set the mode to max if out of range + if (swingMode > kFujitsuAcSwingVert) _.Swing = kFujitsuAcSwingVert; + break; + // Has Horizontal support. + case fujitsu_ac_remote_model_t::ARRAH2E: + case fujitsu_ac_remote_model_t::ARJW2: + default: + // Set the mode to max if out of range + if (swingMode > kFujitsuAcSwingBoth) _.Swing = kFujitsuAcSwingBoth; + } + setCmd(kFujitsuAcCmdStayOn); // No special command involved. +} + +/// Get the requested swing operation mode of the A/C unit. +/// @return The contents of the swing state/mode. +uint8_t IRFujitsuAC::getSwing(void) const { + return _.Swing; +} + +/// Set the Clean mode of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRFujitsuAC::setClean(const bool on) { + _.Clean = on; + setCmd(kFujitsuAcCmdStayOn); // No special command involved. +} + +/// Get the Clean mode status of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRFujitsuAC::getClean(void) const { + switch (_model) { + case fujitsu_ac_remote_model_t::ARRY4: return _.Clean; + default: return false; + } +} + +/// Set the Filter mode status of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRFujitsuAC::setFilter(const bool on) { + _.Filter = on; + setCmd(kFujitsuAcCmdStayOn); // No special command involved. +} + +/// Get the Filter mode status of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRFujitsuAC::getFilter(void) const { + switch (_model) { + case fujitsu_ac_remote_model_t::ARRY4: return _.Filter; + default: return false; + } +} + +/// Set the 10C heat status of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRFujitsuAC::set10CHeat(const bool on) { + switch (_model) { + // Only selected models support this. + case fujitsu_ac_remote_model_t::ARREW4E: + setClean(on); // 10C Heat uses the same bit as Clean + if (on) { + _.Mode = kFujitsuAcModeFan; + _.Power = true; + _.Fan = kFujitsuAcFanAuto; + _.Swing = kFujitsuAcSwingOff; + } + default: + break; + } +} + +/// Get the 10C heat status of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRFujitsuAC::get10CHeat(void) const { + switch (_model) { + case fujitsu_ac_remote_model_t::ARREW4E: + return (_.Clean && _.Power && _.Mode == kFujitsuAcModeFan && + _.Fan == kFujitsuAcFanAuto && _.Swing == kFujitsuAcSwingOff); + default: return false; + } +} + +/// Get the Timer type of the A/C message. +/// @return The current timer type in numeric form. +uint8_t IRFujitsuAC::getTimerType(void) const { + switch (_model) { + // These models seem to have timer support. + case fujitsu_ac_remote_model_t::ARRAH2E: + case fujitsu_ac_remote_model_t::ARREB1E: + return _.TimerType; + default: return kFujitsuAcStopTimers; + } +} + +/// Set the Timer type of the A/C message. +/// @param[in] timertype The kind of timer to use for the message. +void IRFujitsuAC::setTimerType(const uint8_t timertype) { + switch (timertype) { + case kFujitsuAcSleepTimer: + case kFujitsuAcOnTimer: + case kFujitsuAcOffTimer: + case kFujitsuAcStopTimers: + _.TimerType = timertype; + break; + default: _.TimerType = kFujitsuAcStopTimers; + } +} + +/// Get the On Timer setting of the A/C. +/// @return nr of minutes left on the timer. 0 means disabled/not supported. +uint16_t IRFujitsuAC::getOnTimer(void) const { + if (getTimerType() == kFujitsuAcOnTimer) + return _.OnTimer; + return 0; +} + +/// Set the On Timer setting of the A/C. +/// @param[in] nr_mins Nr. of minutes to set the timer to. 0 means disabled. +void IRFujitsuAC::setOnTimer(const uint16_t nr_mins) { + _.OnTimer = std::min(kFujitsuAcTimerMax, nr_mins); // Bounds check. + if (_.OnTimer) { + _.TimerType = kFujitsuAcOnTimer; + } else if (getTimerType() == kFujitsuAcOnTimer) { + _.TimerType = kFujitsuAcStopTimers; + } +} + +/// Get the Off/Sleep Timer setting of the A/C. +/// @return nr of minutes left on the timer. 0 means disabled/not supported. +uint16_t IRFujitsuAC::getOffSleepTimer(void) const { + switch (getTimerType()) { + case kFujitsuAcOffTimer: + case kFujitsuAcSleepTimer: + return _.OffTimer; + default: + return 0; + } +} + +/// Set the Off/Sleep Timer time for the A/C. +/// @param[in] nr_mins Nr. of minutes to set the timer to. 0 means disabled. +inline void IRFujitsuAC::setOffSleepTimer(const uint16_t nr_mins) { + _.OffTimer = std::min(kFujitsuAcTimerMax, nr_mins); // Bounds check. +} + +/// Set the Off Timer time for the A/C. +/// @param[in] nr_mins Nr. of minutes to set the timer to. 0 means disabled. +void IRFujitsuAC::setOffTimer(const uint16_t nr_mins) { + setOffSleepTimer(nr_mins); + if (nr_mins) + _.TimerType = kFujitsuAcOffTimer; + else if (getTimerType() != kFujitsuAcOnTimer) + _.TimerType = kFujitsuAcStopTimers; +} + +/// Set the Sleep Timer time for the A/C. +/// @param[in] nr_mins Nr. of minutes to set the timer to. 0 means disabled. +void IRFujitsuAC::setSleepTimer(const uint16_t nr_mins) { + setOffSleepTimer(nr_mins); + if (nr_mins) + _.TimerType = kFujitsuAcSleepTimer; + else if (getTimerType() != kFujitsuAcOnTimer) + _.TimerType = kFujitsuAcStopTimers; +} + +/// 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 IRFujitsuAC::validChecksum(uint8_t state[], const uint16_t length) { + uint8_t sum = 0; + uint8_t sum_complement = 0; + uint8_t checksum = state[length - 1]; + switch (length) { + case kFujitsuAcStateLengthShort: // ARRAH2E, ARREB1E, & ARRY4 + return state[length - 1] == (uint8_t)~state[length - 2]; + case kFujitsuAcStateLength - 1: // ARDB1 & ARJW2 + sum = sumBytes(state, length - 1); + sum_complement = 0x9B; + break; + case kFujitsuAcStateLength: // ARRAH2E, ARRY4, & ARREB1E + sum = sumBytes(state + kFujitsuAcStateLengthShort, + length - 1 - kFujitsuAcStateLengthShort); + break; + default: // Includes ARDB1 & ARJW2 short. + return true; // Assume the checksum is valid for other lengths. + } + return checksum == (uint8_t)(sum_complement - sum); // Does it match? +} + +/// Set the device's remote ID number. +/// @param[in] num The ID for the remote. Valid number range is 0 to 3. +void IRFujitsuAC::setId(const uint8_t num) { _.Id = num; } + +/// Get the current device's remote ID number. +/// @return The current device's remote ID number. +uint8_t IRFujitsuAC::getId(void) const { return _.Id; } + +/// Set the Temperature units for the A/C. +/// @param[in] on true, use Celsius. false, use Fahrenheit. +void IRFujitsuAC::setCelsius(const bool on) { _.Fahrenheit = !on; } + +/// Get the Clean mode status of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRFujitsuAC::getCelsius(void) const { return !_.Fahrenheit; } + +/// 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 IRFujitsuAC::convertMode(const stdAc::opmode_t mode) { + switch (mode) { + case stdAc::opmode_t::kCool: return kFujitsuAcModeCool; + case stdAc::opmode_t::kHeat: return kFujitsuAcModeHeat; + case stdAc::opmode_t::kDry: return kFujitsuAcModeDry; + case stdAc::opmode_t::kFan: return kFujitsuAcModeFan; + default: return kFujitsuAcModeAuto; + } +} + +/// 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 IRFujitsuAC::convertFan(stdAc::fanspeed_t speed) { + switch (speed) { + case stdAc::fanspeed_t::kMin: return kFujitsuAcFanQuiet; + case stdAc::fanspeed_t::kLow: return kFujitsuAcFanLow; + case stdAc::fanspeed_t::kMedium: return kFujitsuAcFanMed; + case stdAc::fanspeed_t::kHigh: + case stdAc::fanspeed_t::kMax: return kFujitsuAcFanHigh; + default: return kFujitsuAcFanAuto; + } +} + +/// 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 IRFujitsuAC::toCommonMode(const uint8_t mode) { + switch (mode) { + case kFujitsuAcModeCool: return stdAc::opmode_t::kCool; + case kFujitsuAcModeHeat: return stdAc::opmode_t::kHeat; + case kFujitsuAcModeDry: return stdAc::opmode_t::kDry; + case kFujitsuAcModeFan: 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 IRFujitsuAC::toCommonFanSpeed(const uint8_t speed) { + switch (speed) { + case kFujitsuAcFanHigh: return stdAc::fanspeed_t::kMax; + case kFujitsuAcFanMed: return stdAc::fanspeed_t::kMedium; + case kFujitsuAcFanLow: return stdAc::fanspeed_t::kLow; + case kFujitsuAcFanQuiet: 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 IRFujitsuAC::toCommon(void) const { + stdAc::state_t result; + result.protocol = decode_type_t::FUJITSU_AC; + result.model = _model; + result.power = getPower(); + result.mode = toCommonMode(_.Mode); + result.celsius = getCelsius(); + result.degrees = getTemp(); + result.fanspeed = toCommonFanSpeed(_.Fan); + uint8_t swing = _.Swing; + switch (result.model) { + case fujitsu_ac_remote_model_t::ARREB1E: + case fujitsu_ac_remote_model_t::ARRAH2E: + case fujitsu_ac_remote_model_t::ARRY4: + result.clean = _.Clean; + result.filter = _.Filter; + result.swingv = (swing & kFujitsuAcSwingVert) ? stdAc::swingv_t::kAuto + : stdAc::swingv_t::kOff; + result.swingh = (swing & kFujitsuAcSwingHoriz) ? stdAc::swingh_t::kAuto + : stdAc::swingh_t::kOff; + break; + case fujitsu_ac_remote_model_t::ARDB1: + case fujitsu_ac_remote_model_t::ARJW2: + default: + result.swingv = stdAc::swingv_t::kOff; + result.swingh = stdAc::swingh_t::kOff; + } + + result.quiet = _.Fan == kFujitsuAcFanQuiet; + result.turbo = _cmd == kFujitsuAcCmdPowerful; + result.econo = _cmd == kFujitsuAcCmdEcono; + // Not supported. + result.light = false; + result.filter = 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 IRFujitsuAC::toString(void) const { + String result = ""; + result.reserve(100); // Reserve some heap for the string to reduce fragging. + fujitsu_ac_remote_model_t model = _model; + result += addModelToString(decode_type_t::FUJITSU_AC, model, false); + result += addIntToString(_.Id, kIdStr); + result += addBoolToString(getPower(), kPowerStr); + result += addModeToString(_.Mode, kFujitsuAcModeAuto, kFujitsuAcModeCool, + kFujitsuAcModeHeat, kFujitsuAcModeDry, + kFujitsuAcModeFan); + result += addTempFloatToString(getTemp(), getCelsius()); + result += addFanToString(_.Fan, kFujitsuAcFanHigh, kFujitsuAcFanLow, + kFujitsuAcFanAuto, kFujitsuAcFanQuiet, + kFujitsuAcFanMed); + switch (model) { + // These models have no internal swing, clean. or filter state. + case fujitsu_ac_remote_model_t::ARDB1: + case fujitsu_ac_remote_model_t::ARJW2: + break; + // These models have Clean & Filter, plus Swing (via fall thru) + case fujitsu_ac_remote_model_t::ARRAH2E: + case fujitsu_ac_remote_model_t::ARREB1E: + case fujitsu_ac_remote_model_t::ARRY4: + result += addBoolToString(getClean(), kCleanStr); + result += addBoolToString(getFilter(), kFilterStr); + // FALL THRU + default: // e.g. ARREW4E + if (model == fujitsu_ac_remote_model_t::ARREW4E) + result += addBoolToString(get10CHeat(), k10CHeatStr); + result += addIntToString(_.Swing, kSwingStr); + result += kSpaceLBraceStr; + switch (_.Swing) { + case kFujitsuAcSwingOff: + result += kOffStr; + break; + case kFujitsuAcSwingVert: + result += kSwingVStr; + break; + case kFujitsuAcSwingHoriz: + result += kSwingHStr; + break; + case kFujitsuAcSwingBoth: + result += kSwingVStr; + result += '+'; + result += kSwingHStr; + break; + default: + result += kUnknownStr; + } + result += ')'; + } + result += kCommaSpaceStr; + result += kCommandStr; + result += kColonSpaceStr; + switch (_cmd) { + case kFujitsuAcCmdStepHoriz: + result += kStepStr; + result += ' '; + result += kSwingHStr; + break; + case kFujitsuAcCmdStepVert: + result += kStepStr; + result += ' '; + result += kSwingVStr; + break; + case kFujitsuAcCmdToggleSwingHoriz: + result += kToggleStr; + result += ' '; + result += kSwingHStr; + break; + case kFujitsuAcCmdToggleSwingVert: + result += kToggleStr; + result += ' '; + result += kSwingVStr; + break; + case kFujitsuAcCmdEcono: + result += kEconoStr; + break; + case kFujitsuAcCmdPowerful: + result += kPowerfulStr; + break; + default: + result += kNAStr; + } + uint16_t mins = 0; + String type_str = kTimerStr; + switch (model) { + case fujitsu_ac_remote_model_t::ARREB1E: + case fujitsu_ac_remote_model_t::ARREW4E: + result += addBoolToString(getOutsideQuiet(), kOutsideQuietStr); + // FALL THRU + // These models seem to have timer support. + case fujitsu_ac_remote_model_t::ARRAH2E: + switch (getTimerType()) { + case kFujitsuAcOnTimer: + type_str = kOnTimerStr; + mins = getOnTimer(); + break; + case kFujitsuAcOffTimer: + type_str = kOffTimerStr; + mins = getOffSleepTimer(); + break; + case kFujitsuAcSleepTimer: + type_str = kSleepTimerStr; + mins = getOffSleepTimer(); + break; + } + result += addLabeledString(mins ? minsToString(mins) : kOffStr, type_str); + break; + default: + break; + } + return result; +} + +#if DECODE_FUJITSU_AC +/// Decode the supplied Fujitsu AC IR message if possible. +/// 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::decodeFujitsuAC(decode_results* results, uint16_t offset, + const uint16_t nbits, + const bool strict) { + uint16_t dataBitsSoFar = 0; + + // Have we got enough data to successfully decode? + if (results->rawlen < (2 * kFujitsuAcMinBits) + kHeader + kFooter - 1 + + offset) + return false; // Can't possibly be a valid message. + + // Compliance + if (strict) { + switch (nbits) { + case kFujitsuAcBits: + case kFujitsuAcBits - 8: + case kFujitsuAcMinBits: + case kFujitsuAcMinBits + 8: break; + default: return false; // Must be called with the correct nr. of bits. + } + } + + // Header / Some of the Data + uint16_t used = matchGeneric(results->rawbuf + offset, results->state, + results->rawlen - offset, kFujitsuAcMinBits - 8, + kFujitsuAcHdrMark, kFujitsuAcHdrSpace, // Header + kFujitsuAcBitMark, kFujitsuAcOneSpace, // Data + kFujitsuAcBitMark, kFujitsuAcZeroSpace, + 0, 0, // No Footer (yet) + false, _tolerance + kFujitsuAcExtraTolerance, 0, + false); // LSBF + if (!used) return false; + offset += used; + // Check we have the typical data header. + if (results->state[0] != 0x14 || results->state[1] != 0x63) return false; + dataBitsSoFar += kFujitsuAcMinBits - 8; + + // Keep reading bytes until we either run out of message or state to fill. + match_result_t data_result; + for (uint16_t i = 5; + offset <= results->rawlen - 16 && i < kFujitsuAcStateLength; + i++, dataBitsSoFar += 8, offset += data_result.used) { + data_result = matchData( + &(results->rawbuf[offset]), 8, kFujitsuAcBitMark, kFujitsuAcOneSpace, + kFujitsuAcBitMark, kFujitsuAcZeroSpace, + _tolerance + kFujitsuAcExtraTolerance, 0, false); + if (data_result.success == false) break; // Fail + results->state[i] = data_result.data; + } + + // Footer + if (offset > results->rawlen || + !matchMark(results->rawbuf[offset++], kFujitsuAcBitMark)) + return false; + // The space is optional if we are out of capture. + if (offset < results->rawlen && + !matchAtLeast(results->rawbuf[offset], kFujitsuAcMinGap)) + return false; + + // Compliance + if (strict) { + if (dataBitsSoFar != nbits) return false; + } + + results->decode_type = FUJITSU_AC; + results->bits = dataBitsSoFar; + + // Compliance + switch (dataBitsSoFar) { + case kFujitsuAcMinBits: + // Check if this values indicate that this should have been a long state + // message. + if (results->state[5] == 0xFC) return false; + return true; // Success + case kFujitsuAcMinBits + 8: + // Check if this values indicate that this should have been a long state + // message. + if (results->state[5] == 0xFE) return false; + // The last byte needs to be the inverse of the penultimate byte. + if (results->state[5] != (uint8_t)~results->state[6]) return false; + return true; // Success + case kFujitsuAcBits - 8: + // Long messages of this size require this byte be correct. + if (results->state[5] != 0xFC) return false; + break; + case kFujitsuAcBits: + // Long messages of this size require this byte be correct. + if (results->state[5] != 0xFE) return false; + break; + default: + return false; // Unexpected size. + } + if (!IRFujitsuAC::validChecksum(results->state, dataBitsSoFar / 8)) + return false; + + // Success + return true; // All good. +} +#endif // DECODE_FUJITSU_AC diff --git a/lib/IRremoteESP8266/src/ir_Fujitsu.h b/lib/IRremoteESP8266/src/ir_Fujitsu.h index 1414cab711..70c0a4cf03 100644 --- a/lib/IRremoteESP8266/src/ir_Fujitsu.h +++ b/lib/IRremoteESP8266/src/ir_Fujitsu.h @@ -46,11 +46,11 @@ #ifdef ARDUINO #include #endif -#include "../src/IRrecv.h" -#include "../src/IRremoteESP8266.h" -#include "../src/IRsend.h" +#include "IRrecv.h" +#include "IRremoteESP8266.h" +#include "IRsend.h" #ifdef UNIT_TEST -#include "../src/IRsend_test.h" +#include "IRsend_test.h" #endif /// Native representation of a Fujitsu A/C message. diff --git a/lib/IRremoteESP8266/src/ir_GICable.cpp b/lib/IRremoteESP8266/src/ir_GICable.cpp new file mode 100644 index 0000000000..7b29d71db4 --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_GICable.cpp @@ -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 +#include +#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 diff --git a/lib/IRremoteESP8266/src/ir_GlobalCache.cpp b/lib/IRremoteESP8266/src/ir_GlobalCache.cpp new file mode 100644 index 0000000000..e8ebac4af8 --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_GlobalCache.cpp @@ -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 +#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 diff --git a/lib/IRremoteESP8266/src/ir_Goodweather.cpp b/lib/IRremoteESP8266/src/ir_Goodweather.cpp new file mode 100644 index 0000000000..1d6918e7ff --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_Goodweather.cpp @@ -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 +#ifndef ARDUINO +#include +#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 diff --git a/lib/IRremoteESP8266/src/ir_Goodweather.h b/lib/IRremoteESP8266/src/ir_Goodweather.h index e1f7f5ed5d..7e547edd83 100644 --- a/lib/IRremoteESP8266/src/ir_Goodweather.h +++ b/lib/IRremoteESP8266/src/ir_Goodweather.h @@ -16,10 +16,10 @@ #ifndef UNIT_TEST #include #endif -#include "../src/IRremoteESP8266.h" -#include "../src/IRsend.h" +#include "IRremoteESP8266.h" +#include "IRsend.h" #ifdef UNIT_TEST -#include "../src/IRsend_test.h" +#include "IRsend_test.h" #endif /// Native representation of a Goodweather A/C message. diff --git a/lib/IRremoteESP8266/src/ir_Gree.h b/lib/IRremoteESP8266/src/ir_Gree.h index cd0ef8a848..be5ac31ce5 100644 --- a/lib/IRremoteESP8266/src/ir_Gree.h +++ b/lib/IRremoteESP8266/src/ir_Gree.h @@ -32,10 +32,10 @@ #ifndef UNIT_TEST #include #endif -#include "../src/IRremoteESP8266.h" -#include "../src/IRsend.h" +#include "IRremoteESP8266.h" +#include "IRsend.h" #ifdef UNIT_TEST -#include "../src/IRsend_test.h" +#include "IRsend_test.h" #endif /// Native representation of a Gree A/C message. diff --git a/lib/IRremoteESP8266/src/ir_Haier.h b/lib/IRremoteESP8266/src/ir_Haier.h index 516a0ca0ee..4e5c8e64c2 100644 --- a/lib/IRremoteESP8266/src/ir_Haier.h +++ b/lib/IRremoteESP8266/src/ir_Haier.h @@ -24,10 +24,10 @@ #ifndef UNIT_TEST #include #endif -#include "../src/IRremoteESP8266.h" -#include "../src/IRsend.h" +#include "IRremoteESP8266.h" +#include "IRsend.h" #ifdef UNIT_TEST -#include "../src/IRsend_test.h" +#include "IRsend_test.h" #endif /// Native representation of a Haier HSU07-HEA03 A/C message. diff --git a/lib/IRremoteESP8266/src/ir_Hitachi.cpp b/lib/IRremoteESP8266/src/ir_Hitachi.cpp new file mode 100644 index 0000000000..49781997e1 --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_Hitachi.cpp @@ -0,0 +1,1580 @@ +// Copyright 2018-2019 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 + +#include "ir_Hitachi.h" +#include +#include +#ifndef ARDUINO +#include +#endif +#include "IRrecv.h" +#include "IRremoteESP8266.h" +#include "IRsend.h" +#include "IRtext.h" +#include "IRutils.h" + +// Constants +const uint16_t kHitachiAcHdrMark = 3300; +const uint16_t kHitachiAcHdrSpace = 1700; +const uint16_t kHitachiAc1HdrMark = 3400; +const uint16_t kHitachiAc1HdrSpace = 3400; +const uint16_t kHitachiAcBitMark = 400; +const uint16_t kHitachiAcOneSpace = 1250; +const uint16_t kHitachiAcZeroSpace = 500; +const uint32_t kHitachiAcMinGap = kDefaultMessageGap; // Just a guess. +// Support for HitachiAc424 protocol +const uint16_t kHitachiAc424LdrMark = 29784; // Leader +const uint16_t kHitachiAc424LdrSpace = 49290; // Leader +const uint16_t kHitachiAc424HdrMark = 3416; // Header +const uint16_t kHitachiAc424HdrSpace = 1604; // Header +const uint16_t kHitachiAc424BitMark = 463; +const uint16_t kHitachiAc424OneSpace = 1208; +const uint16_t kHitachiAc424ZeroSpace = 372; + +// Support for HitachiAc3 protocol +const uint16_t kHitachiAc3HdrMark = 3400; // Header +const uint16_t kHitachiAc3HdrSpace = 1660; // Header +const uint16_t kHitachiAc3BitMark = 460; +const uint16_t kHitachiAc3OneSpace = 1250; +const uint16_t kHitachiAc3ZeroSpace = 410; + +using irutils::addBoolToString; +using irutils::addIntToString; +using irutils::addLabeledString; +using irutils::addModeToString; +using irutils::addModelToString; +using irutils::addFanToString; +using irutils::addTempToString; +using irutils::checkInvertedBytePairs; +using irutils::invertBytePairs; +using irutils::minsToString; + +#if (SEND_HITACHI_AC || SEND_HITACHI_AC2 || SEND_HITACHI_AC344) +/// Send a Hitachi 28-byte/224-bit A/C formatted message. (HITACHI_AC) +/// 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. +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/417 +void IRsend::sendHitachiAC(const unsigned char data[], const uint16_t nbytes, + const uint16_t repeat) { + if (nbytes < kHitachiAcStateLength) + return; // Not enough bytes to send a proper message. + + const bool MSBfirst = (nbytes == kHitachiAc344StateLength) ? false : true; + sendGeneric(kHitachiAcHdrMark, kHitachiAcHdrSpace, kHitachiAcBitMark, + kHitachiAcOneSpace, kHitachiAcBitMark, kHitachiAcZeroSpace, + kHitachiAcBitMark, kHitachiAcMinGap, data, nbytes, 38, MSBfirst, + repeat, 50); +} +#endif // (SEND_HITACHI_AC || SEND_HITACHI_AC2 || SEND_HITACHI_AC344) + +#if SEND_HITACHI_AC1 +/// Send a Hitachi 13 byte/224-bit A/C formatted message. (HITACHI_AC1) +/// Status: STABLE / Confirmed 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. +/// @note Basically the same as sendHitatchiAC() except different size & header. +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/453 +void IRsend::sendHitachiAC1(const unsigned char data[], const uint16_t nbytes, + const uint16_t repeat) { + if (nbytes < kHitachiAc1StateLength) + return; // Not enough bytes to send a proper message. + sendGeneric(kHitachiAc1HdrMark, kHitachiAc1HdrSpace, kHitachiAcBitMark, + kHitachiAcOneSpace, kHitachiAcBitMark, kHitachiAcZeroSpace, + kHitachiAcBitMark, kHitachiAcMinGap, data, nbytes, kHitachiAcFreq, + true, repeat, kDutyDefault); +} +#endif // SEND_HITACHI_AC1 + +#if SEND_HITACHI_AC2 +/// Send a Hitachi 53 byte/424-bit A/C formatted message. (HITACHI_AC2) +/// Basically the same as sendHitatchiAC() except different size. +/// Status: STABLE / Expected to work. +/// @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::sendHitachiAC2(const unsigned char data[], const uint16_t nbytes, + const uint16_t repeat) { + if (nbytes < kHitachiAc2StateLength) + return; // Not enough bytes to send a proper message. + sendHitachiAC(data, nbytes, repeat); +} +#endif // SEND_HITACHI_AC2 + +#if SEND_HITACHI_AC344 +/// Send a Hitachi A/C 43-byte/344-bit message. (HITACHI_AC344) +/// Basically the same as sendHitatchiAC() except different size. +/// Status: Beta / Probably works. +/// @param[in] data An array of bytes containing the IR command. +/// @param[in] nbytes Nr. of bytes of data in the array. +/// @param[in] repeat Nr. of times the message is to be repeated. (Default = 0). +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1134 +void IRsend::sendHitachiAc344(const unsigned char data[], const uint16_t nbytes, + const uint16_t repeat) { + if (nbytes < kHitachiAc344StateLength) + return; // Not enough bytes to send a proper message. + sendHitachiAC(data, nbytes, repeat); +} +#endif // SEND_HITACHI_AC344 + +/// 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? +IRHitachiAc::IRHitachiAc(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 IRHitachiAc::stateReset(void) { + _.raw[0] = 0x80; + _.raw[1] = 0x08; + _.raw[2] = 0x0C; + _.raw[3] = 0x02; + _.raw[4] = 0xFD; + _.raw[5] = 0x80; + _.raw[6] = 0x7F; + _.raw[7] = 0x88; + _.raw[8] = 0x48; + _.raw[9] = 0x10; + for (uint8_t i = 10; i < kHitachiAcStateLength; i++) _.raw[i] = 0x00; + _.raw[14] = 0x60; + _.raw[15] = 0x60; + _.raw[24] = 0x80; + setTemp(23); +} + +/// Set up hardware to be able to send a message. +void IRHitachiAc::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 size/length of the state. +/// @return The calculated checksum value. +uint8_t IRHitachiAc::calcChecksum(const uint8_t state[], + const uint16_t length) { + uint8_t sum = 62; + for (uint16_t i = 0; i < length - 1; i++) sum -= reverseBits(state[i], 8); + return reverseBits(sum, 8); +} + +/// Calculate and set the checksum values for the internal state. +/// @param[in] length The size/length of the state. +void IRHitachiAc::checksum(const uint16_t length) { + _.Sum = calcChecksum(_.raw, 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 IRHitachiAc::validChecksum(const uint8_t state[], const uint16_t length) { + if (length < 2) return true; // Assume true for lengths that are too short. + return (state[length - 1] == calcChecksum(state, length)); +} + +/// 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 *IRHitachiAc::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 new_code array. +void IRHitachiAc::setRaw(const uint8_t new_code[], const uint16_t length) { + std::memcpy(_.raw, new_code, std::min(length, kHitachiAcStateLength)); +} + +#if SEND_HITACHI_AC +/// Send the current internal state as an IR message. +/// @param[in] repeat Nr. of times the message will be repeated. +void IRHitachiAc::send(const uint16_t repeat) { + _irsend.sendHitachiAC(getRaw(), kHitachiAcStateLength, repeat); +} +#endif // SEND_HITACHI_AC + +/// Get the value of the current power setting. +/// @return true, the setting is on. false, the setting is off. +bool IRHitachiAc::getPower(void) const { + return _.Power; +} + +/// Change the power setting. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRHitachiAc::setPower(const bool on) { + _.Power = on; +} + +/// Change the power setting to On. +void IRHitachiAc::on(void) { setPower(true); } + +/// Change the power setting to Off. +void IRHitachiAc::off(void) { setPower(false); } + +/// Get the operating mode setting of the A/C. +/// @return The current operating mode setting. +uint8_t IRHitachiAc::getMode(void) const { return reverseBits(_.Mode, 8); } + +/// Set the operating mode of the A/C. +/// @param[in] mode The desired operating mode. +void IRHitachiAc::setMode(const uint8_t mode) { + uint8_t newmode = mode; + switch (mode) { + // Fan mode sets a special temp. + case kHitachiAcFan: setTemp(64); break; + case kHitachiAcAuto: + case kHitachiAcHeat: + case kHitachiAcCool: + case kHitachiAcDry: break; + default: newmode = kHitachiAcAuto; + } + _.Mode = reverseBits(newmode, 8); + if (mode != kHitachiAcFan) setTemp(_previoustemp); + setFan(getFan()); // Reset the fan speed after the mode change. +} + +/// Get the current temperature setting. +/// @return The current setting for temp. in degrees celsius. +uint8_t IRHitachiAc::getTemp(void) const { + return reverseBits(_.Temp, 8) >> 1; +} + +/// Set the temperature. +/// @param[in] celsius The temperature in degrees celsius. +void IRHitachiAc::setTemp(const uint8_t celsius) { + uint8_t temp; + if (celsius != 64) _previoustemp = celsius; + switch (celsius) { + case 64: + temp = celsius; + break; + default: + temp = std::min(celsius, kHitachiAcMaxTemp); + temp = std::max(temp, kHitachiAcMinTemp); + } + _.Temp = reverseBits(temp << 1, 8); + if (temp == kHitachiAcMinTemp) + _.raw[9] = 0x90; + else + _.raw[9] = 0x10; +} + +/// Get the current fan speed setting. +/// @return The current fan speed. +uint8_t IRHitachiAc::getFan(void) const { return reverseBits(_.Fan, 8); } + +/// Set the speed of the fan. +/// @param[in] speed The desired setting. +void IRHitachiAc::setFan(const uint8_t speed) { + uint8_t fanmin = kHitachiAcFanAuto; + uint8_t fanmax = kHitachiAcFanHigh; + switch (getMode()) { + case kHitachiAcDry: // Only 2 x low speeds in Dry mode. + fanmin = kHitachiAcFanLow; + fanmax = kHitachiAcFanLow + 1; + break; + case kHitachiAcFan: + fanmin = kHitachiAcFanLow; // No Auto in Fan mode. + break; + } + uint8_t newspeed = std::max(speed, fanmin); + newspeed = std::min(newspeed, fanmax); + _.Fan = reverseBits(newspeed, 8); +} + +/// Get the Vertical Swing setting of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRHitachiAc::getSwingVertical(void) const { + return _.SwingV; +} + +/// Set the Vertical Swing setting of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRHitachiAc::setSwingVertical(const bool on) { + _.SwingV = on; +} + +/// Get the Horizontal Swing setting of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRHitachiAc::getSwingHorizontal(void) const { + return _.SwingH; +} + +/// Set the Horizontal Swing setting of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRHitachiAc::setSwingHorizontal(const bool on) { + _.SwingH = on; +} + +/// 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 IRHitachiAc::convertMode(const stdAc::opmode_t mode) { + switch (mode) { + case stdAc::opmode_t::kCool: return kHitachiAcCool; + case stdAc::opmode_t::kHeat: return kHitachiAcHeat; + case stdAc::opmode_t::kDry: return kHitachiAcDry; + case stdAc::opmode_t::kFan: return kHitachiAcFan; + default: return kHitachiAcAuto; + } +} + +/// 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 IRHitachiAc::convertFan(const stdAc::fanspeed_t speed) { + switch (speed) { + case stdAc::fanspeed_t::kMin: + case stdAc::fanspeed_t::kLow: return kHitachiAcFanLow; + case stdAc::fanspeed_t::kMedium: return kHitachiAcFanLow + 1; + case stdAc::fanspeed_t::kHigh: return kHitachiAcFanHigh - 1; + case stdAc::fanspeed_t::kMax: return kHitachiAcFanHigh; + default: return kHitachiAcFanAuto; + } +} + +/// 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 IRHitachiAc::toCommonMode(const uint8_t mode) { + switch (mode) { + case kHitachiAcCool: return stdAc::opmode_t::kCool; + case kHitachiAcHeat: return stdAc::opmode_t::kHeat; + case kHitachiAcDry: return stdAc::opmode_t::kDry; + case kHitachiAcFan: 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 IRHitachiAc::toCommonFanSpeed(const uint8_t speed) { + switch (speed) { + case kHitachiAcFanHigh: return stdAc::fanspeed_t::kMax; + case kHitachiAcFanHigh - 1: return stdAc::fanspeed_t::kHigh; + case kHitachiAcFanLow + 1: return stdAc::fanspeed_t::kMedium; + case kHitachiAcFanLow: return stdAc::fanspeed_t::kLow; + 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 IRHitachiAc::toCommon(void) const { + stdAc::state_t result; + result.protocol = decode_type_t::HITACHI_AC; + result.model = -1; // No models used. + result.power = _.Power; + result.mode = toCommonMode(getMode()); + result.celsius = true; + result.degrees = getTemp(); + result.fanspeed = toCommonFanSpeed(getFan()); + result.swingv = (_.SwingV ? stdAc::swingv_t::kAuto : stdAc::swingv_t::kOff); + result.swingh = (_.SwingH ? stdAc::swingh_t::kAuto : stdAc::swingh_t::kOff); + // Not supported. + result.quiet = false; + result.turbo = false; + result.clean = false; + result.econo = false; + result.filter = false; + result.light = 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 IRHitachiAc::toString(void) const { + String result = ""; + result.reserve(110); // Reserve some heap for the string to reduce fragging. + result += addBoolToString(_.Power, kPowerStr, false); + result += addModeToString(getMode(), kHitachiAcAuto, kHitachiAcCool, + kHitachiAcHeat, kHitachiAcDry, kHitachiAcFan); + result += addTempToString(getTemp()); + result += addFanToString(getFan(), kHitachiAcFanHigh, kHitachiAcFanLow, + kHitachiAcFanAuto, kHitachiAcFanAuto, + kHitachiAcFanMed); + result += addBoolToString(_.SwingV, kSwingVStr); + result += addBoolToString(_.SwingH, kSwingHStr); + return result; +} + +/// 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? +IRHitachiAc1::IRHitachiAc1(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 IRHitachiAc1::stateReset(void) { + for (uint8_t i = 0; i < kHitachiAc1StateLength; i++) _.raw[i] = 0x00; + // Copy in a known good state. + _.raw[0] = 0xB2; + _.raw[1] = 0xAE; + _.raw[2] = 0x4D; + _.raw[3] = 0x91; + _.raw[4] = 0xF0; + _.raw[5] = 0xE1; + _.raw[6] = 0xA4; + _.raw[11] = 0x61; + _.raw[12] = 0x24; +} + +/// Set up hardware to be able to send a message. +void IRHitachiAc1::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 size/length of the state. +/// @return The calculated checksum value. +uint8_t IRHitachiAc1::calcChecksum(const uint8_t state[], + const uint16_t length) { + uint8_t sum = 0; + for (uint16_t i = kHitachiAc1ChecksumStartByte; i < length - 1; i++) { + sum += reverseBits(GETBITS8(state[i], kLowNibble, kNibbleSize), + kNibbleSize); + sum += reverseBits(GETBITS8(state[i], kHighNibble, kNibbleSize), + kNibbleSize); + } + return reverseBits(sum, 8); +} + +/// Calculate and set the checksum values for the internal state. +/// @param[in] length The size/length of the state. +void IRHitachiAc1::checksum(const uint16_t length) { + _.Sum = calcChecksum(_.raw, 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 IRHitachiAc1::validChecksum(const uint8_t state[], const uint16_t length) { + if (length < 2) return true; // Assume true for lengths that are too short. + return (state[length - 1] == calcChecksum(state, length)); +} + +/// 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 *IRHitachiAc1::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 new_code array. +void IRHitachiAc1::setRaw(const uint8_t new_code[], const uint16_t length) { + std::memcpy(_.raw, new_code, std::min(length, kHitachiAc1StateLength)); +} + +#if SEND_HITACHI_AC +/// Send the current internal state as an IR message. +/// @param[in] repeat Nr. of times the message will be repeated. +void IRHitachiAc1::send(const uint16_t repeat) { + _irsend.sendHitachiAC1(getRaw(), kHitachiAc1StateLength, repeat); + // Clear the toggle bits as we have actioned them by sending them. + setPowerToggle(false); + setSwingToggle(false); +} +#endif // SEND_HITACHI_AC + +/// Get/Detect the model of the A/C. +/// @return The enum of the compatible model. +hitachi_ac1_remote_model_t IRHitachiAc1::getModel(void) const { + switch (_.Model) { + case kHitachiAc1Model_B: return hitachi_ac1_remote_model_t::R_LT0541_HTA_B; + default: return hitachi_ac1_remote_model_t::R_LT0541_HTA_A; + } +} + +/// Set the model of the A/C to emulate. +/// @param[in] model The enum of the appropriate model. +void IRHitachiAc1::setModel(const hitachi_ac1_remote_model_t model) { + uint8_t value = 0; + switch (model) { + case hitachi_ac1_remote_model_t::R_LT0541_HTA_B: + value = kHitachiAc1Model_B; + break; + default: + value = kHitachiAc1Model_A; // i.e. 'A' mode. + } + _.Model = value; +} + +/// Get the value of the current power setting. +/// @return true, the setting is on. false, the setting is off. +bool IRHitachiAc1::getPower(void) const { + return _.Power; +} + +/// Change the power setting. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRHitachiAc1::setPower(const bool on) { + // If the power changes, set the power toggle bit. + if (on != _.Power) setPowerToggle(true); + _.Power = on; +} + +/// Get the value of the current power toggle setting. +/// @return true, the setting is on. false, the setting is off. +bool IRHitachiAc1::getPowerToggle(void) const { + return _.PowerToggle; +} + +/// Change the power toggle setting. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRHitachiAc1::setPowerToggle(const bool on) { + _.PowerToggle = on; +} + +/// Change the power setting to On. +void IRHitachiAc1::on(void) { setPower(true); } + +/// Change the power setting to Off. +void IRHitachiAc1::off(void) { setPower(false); } + +/// Get the operating mode setting of the A/C. +/// @return The current operating mode setting. +uint8_t IRHitachiAc1::getMode(void) const { + return _.Mode; +} + +/// Set the operating mode of the A/C. +/// @param[in] mode The desired operating mode. +void IRHitachiAc1::setMode(const uint8_t mode) { + switch (mode) { + case kHitachiAc1Auto: + setTemp(kHitachiAc1TempAuto); + // FALL THRU + case kHitachiAc1Fan: + case kHitachiAc1Heat: + case kHitachiAc1Cool: + case kHitachiAc1Dry: + _.Mode = mode; + break; + default: + setTemp(kHitachiAc1TempAuto); + _.Mode = kHitachiAc1Auto; + break; + } + setSleep(_.Sleep); // Correct the sleep mode if required. + setFan(_.Fan); // Correct the fan speed if required. +} + +/// Get the current temperature setting. +/// @return The current setting for temp. in degrees celsius. +uint8_t IRHitachiAc1::getTemp(void) const { + return reverseBits(_.Temp, kHitachiAc1TempSize) + kHitachiAc1TempDelta; +} + +/// Set the temperature. +/// @param[in] celsius The temperature in degrees celsius. +void IRHitachiAc1::setTemp(const uint8_t celsius) { + if (_.Mode == kHitachiAc1Auto) return; // Can't change temp in Auto mode. + uint8_t temp = std::min(celsius, kHitachiAcMaxTemp); + temp = std::max(temp, kHitachiAcMinTemp); + temp -= kHitachiAc1TempDelta; + temp = reverseBits(temp, kHitachiAc1TempSize); + _.Temp = temp; +} + +/// Get the current fan speed setting. +/// @return The current fan speed. +uint8_t IRHitachiAc1::getFan(void) const { + return _.Fan; +} + +/// Set the speed of the fan. +/// @param[in] speed The desired setting. +/// @param[in] force Deprecated +void IRHitachiAc1::setFan(const uint8_t speed, const bool /*force*/) { + // restrictions + switch (_.Mode) { + case kHitachiAc1Dry: + _.Fan = kHitachiAc1FanLow; // Dry is locked to Low speed. + return; + case kHitachiAc1Auto: + _.Fan = kHitachiAc1FanAuto; // Auto is locked to Auto speed. + return; + case kHitachiAc1Heat: + case kHitachiAc1Fan: // Auto speed not allowed in these modes. + if (speed == kHitachiAc1FanAuto || _.Fan == kHitachiAc1FanAuto) + _.Fan = kHitachiAc1FanLow; + return; + } + + switch (speed) { + case kHitachiAc1FanAuto: + case kHitachiAc1FanHigh: + case kHitachiAc1FanMed: + case kHitachiAc1FanLow: + _.Fan = speed; + break; + default: _.Fan = kHitachiAc1FanAuto; + } +} + +/// Get the Swing Toggle setting of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRHitachiAc1::getSwingToggle(void) const { + return _.SwingToggle; +} + +/// Set the Swing toggle setting of the A/C. +/// @param[in] toggle true, the setting is on. false, the setting is off. +void IRHitachiAc1::setSwingToggle(const bool toggle) { + _.SwingToggle = toggle; +} + +/// Get the Vertical Swing setting of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRHitachiAc1::getSwingV(void) const { + return _.SwingV; +} + +/// Set the Vertical Swing setting of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRHitachiAc1::setSwingV(const bool on) { + _.SwingV = on; +} + +/// Get the Horizontal Swing setting of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRHitachiAc1::getSwingH(void) const { + return _.SwingH; +} + +/// Set the Horizontal Swing setting of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRHitachiAc1::setSwingH(const bool on) { + _.SwingH = on; +} + +/// Get the Sleep setting of the A/C. +/// @return The currently configured sleep mode. +/// @note Sleep modes only available in Auto & Cool modes, otherwise it's off. +uint8_t IRHitachiAc1::getSleep(void) const { + return _.Sleep; +} + +/// Set the Sleep setting of the A/C. +/// @param[in] mode The mode of sleep to set the A/C to. +/// @note Sleep modes only available in Auto & Cool modes, otherwise it's off. +void IRHitachiAc1::setSleep(const uint8_t mode) { + switch (_.Mode) { + case kHitachiAc1Auto: + case kHitachiAc1Cool: + _.Sleep = std::min(mode, kHitachiAc1Sleep4); + break; + default: + _.Sleep = kHitachiAc1SleepOff; + } +} + +/// Set the On Timer time. +/// @param[in] mins The time expressed in total number of minutes. +void IRHitachiAc1::setOnTimer(const uint16_t mins) { + const uint16_t mins_lsb = reverseBits(mins, kHitachiAc1TimerSize); + _.OnTimerLow = GETBITS16(mins_lsb, 8, 8); + _.OnTimerHigh = GETBITS16(mins_lsb, 0, 8); +} + +/// Get the On Timer vtime of the A/C. +/// @return Nr of minutes the timer is set to. +uint16_t IRHitachiAc1::getOnTimer(void) const { + return reverseBits( + (_.OnTimerLow << 8) | _.OnTimerHigh, kHitachiAc1TimerSize); +} + +/// Set the Off Timer time. +/// @param[in] mins The time expressed in total number of minutes. +void IRHitachiAc1::setOffTimer(const uint16_t mins) { + const uint16_t mins_lsb = reverseBits(mins, kHitachiAc1TimerSize); + _.OffTimerLow = GETBITS16(mins_lsb, 8, 8); + _.OffTimerHigh = GETBITS16(mins_lsb, 0, 8); +} + +/// Get the Off Timer vtime of the A/C. +/// @return Nr of minutes the timer is set to. +uint16_t IRHitachiAc1::getOffTimer(void) const { + return reverseBits( + (_.OffTimerLow << 8) | _.OffTimerHigh, kHitachiAc1TimerSize); +} + +/// 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 IRHitachiAc1::convertMode(const stdAc::opmode_t mode) { + switch (mode) { + case stdAc::opmode_t::kCool: return kHitachiAc1Cool; + case stdAc::opmode_t::kHeat: return kHitachiAc1Heat; + case stdAc::opmode_t::kDry: return kHitachiAc1Dry; + case stdAc::opmode_t::kFan: return kHitachiAc1Fan; + default: return kHitachiAc1Auto; + } +} + +/// 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 IRHitachiAc1::convertFan(const stdAc::fanspeed_t speed) { + switch (speed) { + case stdAc::fanspeed_t::kMin: + case stdAc::fanspeed_t::kLow: return kHitachiAc1FanLow; + case stdAc::fanspeed_t::kMedium: return kHitachiAc1FanMed; + case stdAc::fanspeed_t::kHigh: + case stdAc::fanspeed_t::kMax: return kHitachiAc1FanHigh; + default: return kHitachiAc1FanAuto; + } +} + +/// 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 IRHitachiAc1::toCommonMode(const uint8_t mode) { + switch (mode) { + case kHitachiAc1Cool: return stdAc::opmode_t::kCool; + case kHitachiAc1Heat: return stdAc::opmode_t::kHeat; + case kHitachiAc1Dry: return stdAc::opmode_t::kDry; + case kHitachiAc1Fan: 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 IRHitachiAc1::toCommonFanSpeed(const uint8_t speed) { + switch (speed) { + case kHitachiAc1FanHigh: return stdAc::fanspeed_t::kMax; + case kHitachiAc1FanMed: return stdAc::fanspeed_t::kMedium; + case kHitachiAc1FanLow: return stdAc::fanspeed_t::kLow; + 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 IRHitachiAc1::toCommon(void) const { + stdAc::state_t result; + result.protocol = decode_type_t::HITACHI_AC1; + result.model = getModel(); + 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.sleep = _.Sleep ? 0 : -1; + // Not supported. + result.quiet = false; + result.turbo = false; + result.clean = false; + result.econo = false; + result.filter = false; + result.light = 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 IRHitachiAc1::toString(void) const { + String result = ""; + result.reserve(170); // Reserve some heap for the string to reduce fragging. + result += addModelToString(decode_type_t::HITACHI_AC1, getModel(), false); + result += addBoolToString(_.Power, kPowerStr); + result += addBoolToString(_.PowerToggle, kPowerToggleStr); + result += addModeToString(_.Mode, kHitachiAc1Auto, kHitachiAc1Cool, + kHitachiAc1Heat, kHitachiAc1Dry, kHitachiAc1Fan); + result += addTempToString(getTemp()); + result += addFanToString(_.Fan, kHitachiAc1FanHigh, kHitachiAc1FanLow, + kHitachiAc1FanAuto, kHitachiAc1FanAuto, + kHitachiAc1FanMed); + result += addBoolToString(_.SwingToggle, kSwingVToggleStr); + result += addBoolToString(_.SwingV, kSwingVStr); + result += addBoolToString(_.SwingH, kSwingHStr); + result += addLabeledString(_.Sleep ? uint64ToString(_.Sleep) : kOffStr, + kSleepStr); + result += addLabeledString(getOnTimer() ? minsToString(getOnTimer()) + : kOffStr, + kOnTimerStr); + result += addLabeledString(getOffTimer() ? minsToString(getOffTimer()) + : kOffStr, + kOffTimerStr); + return result; +} + +#if (DECODE_HITACHI_AC || DECODE_HITACHI_AC1 || DECODE_HITACHI_AC2 || \ + DECODE_HITACHI_AC344) +/// Decode the supplied Hitachi A/C message. +/// Status: STABLE / Expected 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. +/// Typically kHitachiAcBits, kHitachiAc1Bits, kHitachiAc2Bits, +/// kHitachiAc344Bits +/// @param[in] strict Flag indicating if we should perform strict matching. +/// @param[in] MSBfirst Is the data per byte stored in MSB First (true) or +/// LSB First order(false)? +/// @return True if it can decode it, false if it can't. +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/417 +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/453 +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1134 +bool IRrecv::decodeHitachiAC(decode_results *results, uint16_t offset, + const uint16_t nbits, const bool strict, + const bool MSBfirst) { + const uint8_t k_tolerance = _tolerance + 5; + + if (strict) { + switch (nbits) { + case kHitachiAcBits: + case kHitachiAc1Bits: + case kHitachiAc2Bits: + case kHitachiAc344Bits: + break; // Okay to continue. + default: + return false; // Not strictly a Hitachi message. + } + } + uint16_t hmark; + uint32_t hspace; + if (nbits == kHitachiAc1Bits) { + hmark = kHitachiAc1HdrMark; + hspace = kHitachiAc1HdrSpace; + } else { + hmark = kHitachiAcHdrMark; + hspace = kHitachiAcHdrSpace; + } + // Match Header + Data + Footer + if (!matchGeneric(results->rawbuf + offset, results->state, + results->rawlen - offset, nbits, + hmark, hspace, + kHitachiAcBitMark, kHitachiAcOneSpace, + kHitachiAcBitMark, kHitachiAcZeroSpace, + kHitachiAcBitMark, kHitachiAcMinGap, true, + k_tolerance, kMarkExcess, MSBfirst)) return false; + + // Compliance + if (strict) { + if (nbits / 8 == kHitachiAcStateLength && + !IRHitachiAc::validChecksum(results->state, kHitachiAcStateLength)) + return false; + if (nbits / 8 == kHitachiAc1StateLength && + !IRHitachiAc1::validChecksum(results->state, kHitachiAc1StateLength)) + return false; + if (nbits / 8 == kHitachiAc344StateLength && + !IRHitachiAc3::hasInvertedStates(results->state, + kHitachiAc344StateLength)) + return false; + } + + // Success + switch (nbits) { + case kHitachiAc1Bits: + results->decode_type = decode_type_t::HITACHI_AC1; + break; + case kHitachiAc2Bits: + results->decode_type = decode_type_t::HITACHI_AC2; + break; + case kHitachiAc344Bits: + results->decode_type = decode_type_t::HITACHI_AC344; + break; + case kHitachiAcBits: + default: + results->decode_type = decode_type_t::HITACHI_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_HITACHI_AC || DECODE_HITACHI_AC1 || DECODE_HITACHI_AC2 || + // DECODE_HITACHI_AC344) + +#if SEND_HITACHI_AC424 +/// Send a Hitachi 53-byte/424-bit A/C formatted message. (HITACHI_AC424) +/// 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. +/// @note This protocol is almost exactly the same as HitachiAC2 except this +/// variant has a leader section as well, and subtle timing differences. +/// It is also in LSBF order (per byte), rather than MSBF order. +void IRsend::sendHitachiAc424(const uint8_t data[], const uint16_t nbytes, + const uint16_t repeat) { + enableIROut(kHitachiAcFreq); + for (uint16_t r = 0; r <= repeat; r++) { + // Leader + mark(kHitachiAc424LdrMark); + space(kHitachiAc424LdrSpace); + // Header + Data + Footer + sendGeneric(kHitachiAc424HdrMark, kHitachiAc424HdrSpace, + kHitachiAc424BitMark, kHitachiAc424OneSpace, + kHitachiAc424BitMark, kHitachiAc424ZeroSpace, + kHitachiAc424BitMark, kHitachiAcMinGap, + data, nbytes, // Bytes + kHitachiAcFreq, false, kNoRepeat, kDutyDefault); + } +} +#endif // SEND_HITACHI_AC424 + +#if DECODE_HITACHI_AC424 +/// Decode the supplied Hitachi 53-byte/424-bit A/C message. +/// Status: STABLE / Reported as 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 This protocol is almost exactly the same as HitachiAC2 except this +/// variant has a leader section as well, and subtle timing differences. +/// It is also in LSBF order (per byte), rather than MSBF order. +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/973 +/// @see (Japanese Manual) https://kadenfan.hitachi.co.jp/support/raj/item/docs/ras_aj22h_a_tori.pdf +bool IRrecv::decodeHitachiAc424(decode_results *results, uint16_t offset, + const uint16_t nbits, + const bool strict) { + if (results->rawlen < 2 * nbits + kHeader + kHeader + kFooter - 1 + offset) + return false; // Too short a message to match. + if (strict && nbits != kHitachiAc424Bits) + return false; + + uint16_t used; + + // Leader + if (!matchMark(results->rawbuf[offset++], kHitachiAc424LdrMark)) + return false; + if (!matchSpace(results->rawbuf[offset++], kHitachiAc424LdrSpace)) + return false; + + // Header + Data + Footer + used = matchGeneric(results->rawbuf + offset, results->state, + results->rawlen - offset, nbits, + kHitachiAc424HdrMark, kHitachiAc424HdrSpace, + kHitachiAc424BitMark, kHitachiAc424OneSpace, + kHitachiAc424BitMark, kHitachiAc424ZeroSpace, + kHitachiAc424BitMark, kHitachiAcMinGap, true, + kUseDefTol, 0, false); + if (used == 0) return false; // We failed to find any data. + + // Success + results->decode_type = decode_type_t::HITACHI_AC424; + results->bits = nbits; + return true; +} +#endif // DECODE_HITACHI_AC424 + +/// 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? +IRHitachiAc424::IRHitachiAc424(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 Reset to auto fan, cooling, 23° Celsius +void IRHitachiAc424::stateReset(void) { + for (uint8_t i = 0; i < kHitachiAc424StateLength; i++) + _.raw[i] = 0x00; + + _.raw[0] = 0x01; + _.raw[1] = 0x10; + _.raw[3] = 0x40; + _.raw[5] = 0xFF; + _.raw[7] = 0xCC; + _.raw[33] = 0x80; + _.raw[35] = 0x03; + _.raw[37] = 0x01; + _.raw[39] = 0x88; + _.raw[45] = 0xFF; + _.raw[47] = 0xFF; + _.raw[49] = 0xFF; + _.raw[51] = 0xFF; + + setTemp(23); + setPower(true); + setMode(kHitachiAc424Cool); + setFan(kHitachiAc424FanAuto); +} + +/// Update the internal consistency check for the protocol. +void IRHitachiAc424::setInvertedStates(void) { + invertBytePairs(_.raw + 3, kHitachiAc424StateLength - 3); +} + +/// Set up hardware to be able to send a message. +void IRHitachiAc424::begin(void) { _irsend.begin(); } + +/// 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 *IRHitachiAc424::getRaw(void) { + setInvertedStates(); + 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 new_code array. +void IRHitachiAc424::setRaw(const uint8_t new_code[], const uint16_t length) { + std::memcpy(_.raw, new_code, std::min(length, kHitachiAc424StateLength)); +} + +#if SEND_HITACHI_AC424 +/// Send the current internal state as an IR message. +/// @param[in] repeat Nr. of times the message will be repeated. +void IRHitachiAc424::send(const uint16_t repeat) { + _irsend.sendHitachiAc424(getRaw(), kHitachiAc424StateLength, repeat); +} +#endif // SEND_HITACHI_AC424 + +/// Get the value of the current power setting. +/// @return true, the setting is on. false, the setting is off. +bool IRHitachiAc424::getPower(void) const { + return _.Power == kHitachiAc424PowerOn; +} + +/// Change the power setting. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRHitachiAc424::setPower(const bool on) { + setButton(kHitachiAc424ButtonPowerMode); + _.Power = (on ? kHitachiAc424PowerOn : kHitachiAc424PowerOff); +} + +/// Change the power setting to On. +void IRHitachiAc424::on(void) { setPower(true); } + +/// Change the power setting to Off. +void IRHitachiAc424::off(void) { setPower(false); } + +/// Get the operating mode setting of the A/C. +/// @return The current operating mode setting. +uint8_t IRHitachiAc424::getMode(void) const { + return _.Mode; +} + +/// Set the operating mode of the A/C. +/// @param[in] mode The desired operating mode. +void IRHitachiAc424::setMode(const uint8_t mode) { + uint8_t newMode = mode; + switch (mode) { + // Fan mode sets a special temp. + case kHitachiAc424Fan: setTemp(kHitachiAc424FanTemp, false); break; + case kHitachiAc424Heat: + case kHitachiAc424Cool: + case kHitachiAc424Dry: break; + default: newMode = kHitachiAc424Cool; + } + _.Mode = newMode; + if (newMode != kHitachiAc424Fan) setTemp(_previoustemp); + setFan(_.Fan); // Reset the fan speed after the mode change. + setButton(kHitachiAc424ButtonPowerMode); +} + +/// Get the current temperature setting. +/// @return The current setting for temp. in degrees celsius. +uint8_t IRHitachiAc424::getTemp(void) const { + return _.Temp; +} + +/// Set the temperature. +/// @param[in] celsius The temperature in degrees celsius. +/// @param[in] setPrevious true, remember this if we change mode. false, don't. +void IRHitachiAc424::setTemp(const uint8_t celsius, bool setPrevious) { + uint8_t temp; + temp = std::min(celsius, kHitachiAc424MaxTemp); + temp = std::max(temp, kHitachiAc424MinTemp); + _.Temp = temp; + if (_previoustemp > temp) + setButton(kHitachiAc424ButtonTempDown); + else if (_previoustemp < temp) + setButton(kHitachiAc424ButtonTempUp); + if (setPrevious) _previoustemp = temp; +} + +/// Get the current fan speed setting. +/// @return The current fan speed. +uint8_t IRHitachiAc424::getFan(void) const { + return _.Fan; +} + +/// Set the speed of the fan. +/// @param[in] speed The desired setting. +void IRHitachiAc424::setFan(const uint8_t speed) { + uint8_t newSpeed = std::max(speed, kHitachiAc424FanMin); + uint8_t fanMax = kHitachiAc424FanMax; + + // Only 2 x low speeds in Dry mode or Auto + if (_.Mode == kHitachiAc424Dry && speed == kHitachiAc424FanAuto) { + fanMax = kHitachiAc424FanAuto; + } else if (_.Mode == kHitachiAc424Dry) { + fanMax = kHitachiAc424FanMaxDry; + } else if (_.Mode == kHitachiAc424Fan && speed == kHitachiAc424FanAuto) { + // Fan Mode does not have auto. Set to safe low + newSpeed = kHitachiAc424FanMin; + } + + newSpeed = std::min(newSpeed, fanMax); + // Handle the setting the button value if we are going to change the value. + if (newSpeed != _.Fan) setButton(kHitachiAc424ButtonFan); + // Set the values + _.Fan = newSpeed; + _.raw[9] = 0x92; + _.raw[29] = 0x00; + + // When fan is at min/max, additional bytes seem to be set + if (newSpeed == kHitachiAc424FanMin) _.raw[9] = 0x98; + if (newSpeed == kHitachiAc424FanMax) { + _.raw[9] = 0xA9; + _.raw[29] = 0x30; + } +} + +/// Get the Button/Command setting of the A/C. +/// @return The value of the button/command that was pressed. +uint8_t IRHitachiAc424::getButton(void) const { + return _.Button; +} + +/// Set the Button/Command pressed setting of the A/C. +/// @param[in] button The value of the button/command that was pressed. +void IRHitachiAc424::setButton(const uint8_t button) { + _.Button = button; +} + +/// Set the Vertical Swing toggle setting of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +/// @note The remote does not keep state of the vertical swing. +/// A byte is sent indicating the swing button is pressed on the remote +void IRHitachiAc424::setSwingVToggle(const bool on) { + uint8_t button = _.Button; // Get the current button value. + if (on) + button = kHitachiAc424ButtonSwingV; // Set the button to SwingV. + else if (button == kHitachiAc424ButtonSwingV) // Asked to unset it + // It was set previous, so use Power as a default + button = kHitachiAc424ButtonPowerMode; + setButton(button); +} + +/// Get the Vertical Swing toggle setting of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRHitachiAc424::getSwingVToggle(void) const { + return _.Button == kHitachiAc424ButtonSwingV; +} + +/// 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 IRHitachiAc424::convertMode(const stdAc::opmode_t mode) { + switch (mode) { + case stdAc::opmode_t::kCool: return kHitachiAc424Cool; + case stdAc::opmode_t::kHeat: return kHitachiAc424Heat; + case stdAc::opmode_t::kDry: return kHitachiAc424Dry; + case stdAc::opmode_t::kFan: return kHitachiAc424Fan; + default: return kHitachiAc424Cool; + } +} + +/// 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 IRHitachiAc424::convertFan(const stdAc::fanspeed_t speed) { + switch (speed) { + case stdAc::fanspeed_t::kMin: return kHitachiAc424FanMin; + case stdAc::fanspeed_t::kLow: return kHitachiAc424FanLow; + case stdAc::fanspeed_t::kMedium: return kHitachiAc424FanMedium; + case stdAc::fanspeed_t::kHigh: return kHitachiAc424FanHigh; + case stdAc::fanspeed_t::kMax: return kHitachiAc424FanMax; + default: return kHitachiAc424FanAuto; + } +} + +/// 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 IRHitachiAc424::toCommonMode(const uint8_t mode) { + switch (mode) { + case kHitachiAc424Cool: return stdAc::opmode_t::kCool; + case kHitachiAc424Heat: return stdAc::opmode_t::kHeat; + case kHitachiAc424Dry: return stdAc::opmode_t::kDry; + case kHitachiAc424Fan: 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. +/// @return The stdAc equivalent of the native setting. +stdAc::fanspeed_t IRHitachiAc424::toCommonFanSpeed(const uint8_t speed) { + switch (speed) { + case kHitachiAc424FanMax: return stdAc::fanspeed_t::kMax; + case kHitachiAc424FanHigh: return stdAc::fanspeed_t::kHigh; + case kHitachiAc424FanMedium: return stdAc::fanspeed_t::kMedium; + case kHitachiAc424FanLow: return stdAc::fanspeed_t::kLow; + case kHitachiAc424FanMin: 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 IRHitachiAc424::toCommon(void) const { + stdAc::state_t result; + result.protocol = decode_type_t::HITACHI_AC424; + result.model = -1; // No models used. + result.power = getPower(); + result.mode = toCommonMode(_.Mode); + result.celsius = true; + result.degrees = _.Temp; + result.fanspeed = toCommonFanSpeed(_.Fan); + result.swingv = getSwingVToggle() ? stdAc::swingv_t::kAuto + : stdAc::swingv_t::kOff; + // Not supported. + result.swingh = stdAc::swingh_t::kOff; + result.quiet = false; + result.turbo = false; + result.clean = false; + result.econo = false; + result.filter = false; + result.light = false; + result.beep = false; + result.sleep = -1; + result.clock = -1; + return result; +} + +/// Convert the internal state into a human readable string for the settings +/// that are common to protocols of this nature. +/// @return A string containing the common settings in human-readable form. +String IRHitachiAc424::_toString(void) const { + String result = ""; + result.reserve(100); // Reserve some heap for the string to reduce fragging. + result += addBoolToString(getPower(), kPowerStr, false); + result += addModeToString(_.Mode, 0, kHitachiAc424Cool, + kHitachiAc424Heat, kHitachiAc424Dry, + kHitachiAc424Fan); + result += addTempToString(_.Temp); + result += addIntToString(_.Fan, kFanStr); + result += kSpaceLBraceStr; + switch (_.Fan) { + case kHitachiAc424FanAuto: result += kAutoStr; break; + case kHitachiAc424FanMax: result += kMaxStr; break; + case kHitachiAc424FanHigh: result += kHighStr; break; + case kHitachiAc424FanMedium: result += kMedStr; break; + case kHitachiAc424FanLow: result += kLowStr; break; + case kHitachiAc424FanMin: result += kMinStr; break; + default: result += kUnknownStr; + } + result += ')'; + result += addIntToString(_.Button, kButtonStr); + result += kSpaceLBraceStr; + switch (_.Button) { + case kHitachiAc424ButtonPowerMode: + result += kPowerStr; + result += '/'; + result += kModeStr; + break; + case kHitachiAc424ButtonFan: result += kFanStr; break; + case kHitachiAc424ButtonSwingV: result += kSwingVStr; break; + case kHitachiAc344ButtonSwingH: result += kSwingHStr; break; + case kHitachiAc424ButtonTempDown: result += kTempDownStr; break; + case kHitachiAc424ButtonTempUp: result += kTempUpStr; break; + default: result += kUnknownStr; + } + result += ')'; + return result; +} + +/// Convert the internal state into a human readable string. +/// @return A string containing the settings in human-readable form. +String IRHitachiAc424::toString(void) const { + return _toString() + addBoolToString(getSwingVToggle(), kSwingVToggleStr); +} + + +#if SEND_HITACHI_AC3 +/// Send a Hitachi(3) A/C formatted message. (HITACHI_AC3) +/// Status: STABLE / Working fine. +/// @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 almost exactly the same as HitachiAC424 except this +/// variant has subtle timing differences. There are five(5) typical sizes: +/// kHitachiAc3MinStateLength (Cancel Timer), +/// kHitachiAc3MinStateLength + 2 (Change Temp), +/// kHitachiAc3StateLength - 6 (Change Mode), +/// kHitachiAc3StateLength - 4 (Normal), & +/// kHitachiAc3StateLength (Set Timer) +void IRsend::sendHitachiAc3(const uint8_t data[], const uint16_t nbytes, + const uint16_t repeat) { + // Header + Data + Footer + sendGeneric(kHitachiAc3HdrMark, kHitachiAc3HdrSpace, + kHitachiAc3BitMark, kHitachiAc3OneSpace, + kHitachiAc3BitMark, kHitachiAc3ZeroSpace, + kHitachiAc3BitMark, kHitachiAcMinGap, + data, nbytes, // Bytes + kHitachiAcFreq, false, repeat, kDutyDefault); +} +#endif // SEND_HITACHI_AC3 + + +/// 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? +IRHitachiAc3::IRHitachiAc3(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 Reset to auto fan, cooling, 23° Celsius +void IRHitachiAc3::stateReset(void) { + for (uint8_t i = 0; i < kHitachiAc3StateLength; i++) + remote_state[i] = 0x00; + remote_state[0] = 0x01; + remote_state[1] = 0x10; + remote_state[3] = 0x40; + remote_state[5] = 0xFF; + remote_state[7] = 0xE8; + remote_state[9] = 0x89; + remote_state[11] = 0x0B; + remote_state[13] = 0x3F; + remote_state[15] = 0x15; + remote_state[21] = 0x4B; + remote_state[23] = 0x18; + setInvertedStates(); +} + +/// Invert every second byte of the internal state, after the fixed header. +/// @param[in] length The size of the state array. +/// @note This is this protocols integrity check. +void IRHitachiAc3::setInvertedStates(const uint16_t length) { + if (length > 3) invertBytePairs(remote_state + 3, length - 3); +} + +/// Check if every second byte of the state, after the fixed header +/// is inverted to the previous byte. +/// @param[in] state The state array to be checked. +/// @param[in] length The size of the state array. +/// @note This is this protocols integrity check. +bool IRHitachiAc3::hasInvertedStates(const uint8_t state[], + const uint16_t length) { + return (length <= 3 || checkInvertedBytePairs(state + 3, length - 3)); +} + +/// Set up hardware to be able to send a message. +void IRHitachiAc3::begin(void) { _irsend.begin(); } + +/// 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 *IRHitachiAc3::getRaw(void) { + setInvertedStates(); + return remote_state; +} + +/// 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 new_code array. +void IRHitachiAc3::setRaw(const uint8_t new_code[], const uint16_t length) { + memcpy(remote_state, new_code, std::min(length, kHitachiAc3StateLength)); +} + +#if DECODE_HITACHI_AC3 +/// Decode the supplied Hitachi 15to27-byte/120to216-bit A/C message. +/// Status: STABLE / Works fine. +/// @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 almost exactly the same as HitachiAC424 except this +/// variant has subtle timing differences and multiple lengths. +/// There are five(5) typical lengths: +/// kHitachiAc3MinStateLength (Cancel Timer), +/// kHitachiAc3MinStateLength + 2 (Change Temp), +/// kHitachiAc3StateLength - 6 (Change Mode), +/// kHitachiAc3StateLength - 4 (Normal), & +/// kHitachiAc3StateLength (Set Timer) +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1060 +bool IRrecv::decodeHitachiAc3(decode_results *results, uint16_t offset, + const uint16_t nbits, + const bool strict) { + if (results->rawlen < 2 * nbits + kHeader + kFooter - 1 + offset) + return false; // Too short a message to match. + if (strict) { + // Check the requested bit length. + switch (nbits) { + case kHitachiAc3MinBits: // Cancel Timer (Min Size) + case kHitachiAc3MinBits + 2 * 8: // Change Temp + case kHitachiAc3Bits - 6 * 8: // Change Mode + case kHitachiAc3Bits - 4 * 8: // Normal + case kHitachiAc3Bits: // Set Temp (Max Size) + break; + default: return false; + } + } + + // Header + Data + Footer + if (!matchGeneric(results->rawbuf + offset, results->state, + results->rawlen - offset, nbits, + kHitachiAc3HdrMark, kHitachiAc3HdrSpace, + kHitachiAc3BitMark, kHitachiAc3OneSpace, + kHitachiAc3BitMark, kHitachiAc3ZeroSpace, + kHitachiAc3BitMark, kHitachiAcMinGap, true, + kUseDefTol, 0, false)) + return false; // We failed to find any data. + + // Compliance + if (strict && !IRHitachiAc3::hasInvertedStates(results->state, nbits / 8)) + return false; + // Success + results->decode_type = decode_type_t::HITACHI_AC3; + results->bits = nbits; + return true; +} +#endif // DECODE_HITACHI_AC3 + +/// Class constructor for handling detailed Hitachi_AC344 43 byte 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? +IRHitachiAc344::IRHitachiAc344(const uint16_t pin, const bool inverted, + const bool use_modulation) + : IRHitachiAc424(pin, inverted, use_modulation) { stateReset(); } + +/// Reset the internal state to auto fan, cooling, 23° Celsius +void IRHitachiAc344::stateReset(void) { + IRHitachiAc424::stateReset(); + _.raw[37] = 0x00; + _.raw[39] = 0x00; +} + +#if SEND_HITACHI_AC344 +/// Create and send the IR message to the A/C. +/// @param[in] repeat Nr. of times to repeat the message. +void IRHitachiAc344::send(const uint16_t repeat) { + _irsend.sendHitachiAc344(getRaw(), kHitachiAc344StateLength, repeat); +} +#endif // SEND_HITACHI_AC344 + +/// 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 (in bytes) of the code for this protocol. +void IRHitachiAc344::setRaw(const uint8_t new_code[], const uint16_t length) { + memcpy(_.raw, new_code, std::min(length, kHitachiAc344StateLength)); +} + +/// Control the vertical swing setting. +/// @param[in] on True, turns on the feature. False, turns off the feature. +void IRHitachiAc344::setSwingV(const bool on) { + setSwingVToggle(on); // Set the button value. + _.SwingV = on; +} + +/// Get the current vertical swing setting. +/// @return True, if the setting is on. False, it is off. +bool IRHitachiAc344::getSwingV(void) const { + return _.SwingV; +} + +/// Control the horizontal swing setting. +/// @param[in] position The position to set the horizontal swing to. +void IRHitachiAc344::setSwingH(const uint8_t position) { + if (position > kHitachiAc344SwingHLeftMax) + _.SwingH = kHitachiAc344SwingHMiddle; + else + _.SwingH = position; + setButton(kHitachiAc344ButtonSwingH); +} + +/// Get the current horizontal swing setting. +/// @return The current position horizontal swing is set to. +uint8_t IRHitachiAc344::getSwingH(void) const { + return _.SwingH; +} + +/// Convert a standard A/C horizontal swing into its native setting. +/// @param[in] position A stdAc::swingh_t position to convert. +/// @return The equivilent native horizontal swing position. +uint8_t IRHitachiAc344::convertSwingH(const stdAc::swingh_t position) { + switch (position) { + case stdAc::swingh_t::kAuto: return kHitachiAc344SwingHAuto; + case stdAc::swingh_t::kLeftMax: return kHitachiAc344SwingHLeftMax; + case stdAc::swingh_t::kLeft: return kHitachiAc344SwingHLeft; + case stdAc::swingh_t::kRight: return kHitachiAc344SwingHRight; + case stdAc::swingh_t::kRightMax: return kHitachiAc344SwingHRightMax; + default: return kHitachiAc344SwingHMiddle; + } +} + +/// Convert a native horizontal swing postion to it's common equivalent. +/// @param[in] pos A native position to convert. +/// @return The common horizontal swing position. +stdAc::swingh_t IRHitachiAc344::toCommonSwingH(const uint8_t pos) { + switch (pos) { + case kHitachiAc344SwingHLeftMax: return stdAc::swingh_t::kLeftMax; + case kHitachiAc344SwingHLeft: return stdAc::swingh_t::kLeft; + case kHitachiAc344SwingHRight: return stdAc::swingh_t::kRight; + case kHitachiAc344SwingHRightMax: return stdAc::swingh_t::kRightMax; + case kHitachiAc344SwingHAuto: return stdAc::swingh_t::kAuto; + 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 IRHitachiAc344::toCommon(void) const { + stdAc::state_t result = IRHitachiAc424::toCommon(); + result.protocol = decode_type_t::HITACHI_AC344; + result.swingv = _.SwingV ? stdAc::swingv_t::kAuto : stdAc::swingv_t::kOff; + result.swingh = toCommonSwingH(_.SwingH); + return result; +} + +/// Convert the internal state into a human readable string. +/// @return A string containing the settings in human-readable form. +String IRHitachiAc344::toString(void) const { + String result; + result.reserve(120); // Reserve some heap for the string to reduce fragging. + result += _toString(); + result += addBoolToString(_.SwingV, kSwingVStr); + result += addIntToString(_.SwingH, kSwingHStr); + result += kSpaceLBraceStr; + switch (_.SwingH) { + case kHitachiAc344SwingHLeftMax: result += kLeftMaxStr; break; + case kHitachiAc344SwingHLeft: result += kLeftStr; break; + case kHitachiAc344SwingHMiddle: result += kMiddleStr; break; + case kHitachiAc344SwingHRight: result += kRightStr; break; + case kHitachiAc344SwingHRightMax: result += kRightMaxStr; break; + case kHitachiAc344SwingHAuto: result += kAutoStr; break; + default: result += kUnknownStr; + } + result += ')'; + return result; +} diff --git a/lib/IRremoteESP8266/src/ir_Hitachi.h b/lib/IRremoteESP8266/src/ir_Hitachi.h index 88dae3f9aa..32167596f2 100644 --- a/lib/IRremoteESP8266/src/ir_Hitachi.h +++ b/lib/IRremoteESP8266/src/ir_Hitachi.h @@ -28,10 +28,10 @@ #ifndef UNIT_TEST #include #endif -#include "../src/IRremoteESP8266.h" -#include "../src/IRsend.h" +#include "IRremoteESP8266.h" +#include "IRsend.h" #ifdef UNIT_TEST -#include "../src/IRsend_test.h" +#include "IRsend_test.h" #endif /// Native representation of a Hitachi 224-bit A/C message. diff --git a/lib/IRremoteESP8266/src/ir_Inax.cpp b/lib/IRremoteESP8266/src/ir_Inax.cpp new file mode 100644 index 0000000000..bb68ff30d7 --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_Inax.cpp @@ -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 +#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 diff --git a/lib/IRremoteESP8266/src/ir_JVC.cpp b/lib/IRremoteESP8266/src/ir_JVC.cpp new file mode 100644 index 0000000000..9afd6a97f1 --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_JVC.cpp @@ -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 +#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 diff --git a/lib/IRremoteESP8266/src/ir_Kelon.cpp b/lib/IRremoteESP8266/src/ir_Kelon.cpp new file mode 100644 index 0000000000..39b61744eb --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_Kelon.cpp @@ -0,0 +1,502 @@ +// Copyright 2021 Davide Depau + +/// @file +/// @brief Support for Kelan AC protocol. +/// 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 + +#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; + +#if SEND_KELON + +/// Send a Kelon 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, 50); +} + +#endif // SEND_KELON + +#if DECODE_KELON +/// Decode the supplied Kelon 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->state, + results->rawlen - offset, nbits, + kKelonHdrMark, kKelonHdrSpace, + kKelonBitMark, kKelonOneSpace, + kKelonBitMark, kKelonZeroSpace, + kKelonBitMark, 0, false, + _tolerance, 0, false)) { + return false; + } + + results->decode_type = decode_type_t::KELON; + 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(fan) - 4) * -1) % 4; +} + +/// Get the current fan speed setting. +/// @return The current fan speed. +uint8_t IRKelonAc::getFan() const { + return ((static_cast(_.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(_.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(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; + } +} + +/// 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. + result.mode = toCommonMode(getMode()); + result.celsius = true; + result.degrees = getTemp(); + result.fanspeed = toCommonFanSpeed(getFan()); + result.turbo = getSupercool(); + result.sleep = getSleep() ? 0 : -1; + // Not supported. + // N/A, AC only supports toggling it + result.power = (prev == nullptr || prev->power) ^ _.PowerToggle; + // N/A, 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.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; +} diff --git a/lib/IRremoteESP8266/src/ir_Kelon.h b/lib/IRremoteESP8266/src/ir_Kelon.h index 663b25cb9f..498650623f 100644 --- a/lib/IRremoteESP8266/src/ir_Kelon.h +++ b/lib/IRremoteESP8266/src/ir_Kelon.h @@ -17,12 +17,12 @@ #define IR_KELON_H_ #ifdef UNIT_TEST -#include "../src/IRsend_test.h" +#include "IRsend_test.h" #endif -#include "../src/IRremoteESP8266.h" -#include "../src/IRsend.h" -#include "../src/IRutils.h" +#include "IRremoteESP8266.h" +#include "IRsend.h" +#include "IRutils.h" union KelonProtocol { uint64_t raw; diff --git a/lib/IRremoteESP8266/src/ir_Kelvinator.cpp b/lib/IRremoteESP8266/src/ir_Kelvinator.cpp new file mode 100644 index 0000000000..01d2e544c7 --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_Kelvinator.cpp @@ -0,0 +1,525 @@ +// 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 +#include +#ifndef ARDUINO +#include +#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; + +#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; + } +} + +/// Control the current vertical swing setting. +/// @param[in] on The desired setting. +void IRKelvinatorAC::setSwingVertical(const bool on) { + _.SwingV = on; + _.VentSwing = (on || _.SwingH); +} + +/// Is the vertical swing setting on? +/// @return The current value. +bool IRKelvinatorAC::getSwingVertical(void) const { + return _.SwingV; +} + +/// Control the current horizontal swing setting. +/// @param[in] on The desired setting. +void IRKelvinatorAC::setSwingHorizontal(const bool on) { + _.SwingH = on; + _.VentSwing = (on || _.SwingV); +} + +/// 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 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 += addBoolToString(_.SwingV, kSwingVStr); + 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 diff --git a/lib/IRremoteESP8266/src/ir_Kelvinator.h b/lib/IRremoteESP8266/src/ir_Kelvinator.h index 8da8df345f..74371d8efb 100644 --- a/lib/IRremoteESP8266/src/ir_Kelvinator.h +++ b/lib/IRremoteESP8266/src/ir_Kelvinator.h @@ -25,10 +25,10 @@ #ifndef UNIT_TEST #include #endif -#include "../src/IRremoteESP8266.h" -#include "../src/IRsend.h" +#include "IRremoteESP8266.h" +#include "IRsend.h" #ifdef UNIT_TEST -#include "../src/IRsend_test.h" +#include "IRsend_test.h" #endif /// Native representation of a Kelvinator A/C message. diff --git a/lib/IRremoteESP8266/src/ir_LG.cpp b/lib/IRremoteESP8266/src/ir_LG.cpp new file mode 100644 index 0000000000..d817fbdb74 --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_LG.cpp @@ -0,0 +1,838 @@ +// 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 +#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::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::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: + _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() || 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(); +} + +/// 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: + 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 Swing message. +/// @return true, if it is. Otherwise, false. +bool IRLgAc::isSwing(void) const { + return (_.raw >> 12) == kLgAcSwingSignature; +} + +/// 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); +} + +/// 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 || + 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 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; + result.model = getModel(); + result.power = getPower(); + result.mode = toCommonMode(_.Mode); + result.celsius = true; + result.degrees = getTemp(); + result.fanspeed = toCommonFanSpeed(_.Fan); + result.light = isLightToggle() ? !result.light : _light; + 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()) { + 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); +} diff --git a/lib/IRremoteESP8266/src/ir_LG.h b/lib/IRremoteESP8266/src/ir_LG.h index 9578e155fe..2bd7dbc1c2 100644 --- a/lib/IRremoteESP8266/src/ir_LG.h +++ b/lib/IRremoteESP8266/src/ir_LG.h @@ -28,11 +28,11 @@ #ifndef UNIT_TEST #include #endif -#include "../src/IRremoteESP8266.h" -#include "../src/IRsend.h" -#include "../src/IRutils.h" +#include "IRremoteESP8266.h" +#include "IRsend.h" +#include "IRutils.h" #ifdef UNIT_TEST -#include "../src/IRsend_test.h" +#include "IRsend_test.h" #endif /// Native representation of a LG A/C message. diff --git a/lib/IRremoteESP8266/src/ir_Lasertag.cpp b/lib/IRremoteESP8266/src/ir_Lasertag.cpp new file mode 100644 index 0000000000..3e40de0efd --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_Lasertag.cpp @@ -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 +#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 diff --git a/lib/IRremoteESP8266/src/ir_Lego.cpp b/lib/IRremoteESP8266/src/ir_Lego.cpp new file mode 100644 index 0000000000..3b7144768b --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_Lego.cpp @@ -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 +#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 diff --git a/lib/IRremoteESP8266/src/ir_Lutron.cpp b/lib/IRremoteESP8266/src/ir_Lutron.cpp new file mode 100644 index 0000000000..5d04247843 --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_Lutron.cpp @@ -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 +#include +#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 diff --git a/lib/IRremoteESP8266/src/ir_MWM.cpp b/lib/IRremoteESP8266/src/ir_MWM.cpp new file mode 100644 index 0000000000..8aca4a4fd0 --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_MWM.cpp @@ -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 +#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 diff --git a/lib/IRremoteESP8266/src/ir_Magiquest.cpp b/lib/IRremoteESP8266/src/ir_Magiquest.cpp new file mode 100644 index 0000000000..3f79a18f64 --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_Magiquest.cpp @@ -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 +#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 diff --git a/lib/IRremoteESP8266/src/ir_Magiquest.h b/lib/IRremoteESP8266/src/ir_Magiquest.h index 37f928b3f8..3999043752 100644 --- a/lib/IRremoteESP8266/src/ir_Magiquest.h +++ b/lib/IRremoteESP8266/src/ir_Magiquest.h @@ -15,8 +15,8 @@ #define __STDC_LIMIT_MACROS #include -#include "../src/IRremoteESP8266.h" -#include "../src/IRsend.h" +#include "IRremoteESP8266.h" +#include "IRsend.h" /// MagiQuest packet is both Wand ID and magnitude of swish and flick union magiquest { diff --git a/lib/IRremoteESP8266/src/ir_Metz.cpp b/lib/IRremoteESP8266/src/ir_Metz.cpp new file mode 100644 index 0000000000..0dcc7dafa6 --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_Metz.cpp @@ -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 diff --git a/lib/IRremoteESP8266/src/ir_Midea.cpp b/lib/IRremoteESP8266/src/ir_Midea.cpp new file mode 100644 index 0000000000..8b3b645d80 --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_Midea.cpp @@ -0,0 +1,796 @@ +// 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 +#ifndef ARDUINO +#include +#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::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; + _SwingVToggle = false; + _EconoToggle = false; + _TurboToggle = false; + _LightToggle = 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; +} +#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; +} + +/// 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.clean = 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.fanspeed = toCommonFanSpeed(_.Fan); + result.sleep = _.Sleep ? 0 : -1; + result.econo = getEconoToggle(); + 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 += addBoolToString(getSwingVToggle(), kSwingVToggleStr); +#if KAYSUN_AC + result += addBoolToString(getSwingVStep(), kStepStr); +#endif // KAYSUN_AC + result += addBoolToString(getEconoToggle(), kEconoToggleStr); + result += addBoolToString(getTurboToggle(), kTurboToggleStr); + result += addBoolToString(getLightToggle(), kLightToggleStr); + 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 diff --git a/lib/IRremoteESP8266/src/ir_Midea.h b/lib/IRremoteESP8266/src/ir_Midea.h index 95a47b0b44..eaeaa04d8d 100644 --- a/lib/IRremoteESP8266/src/ir_Midea.h +++ b/lib/IRremoteESP8266/src/ir_Midea.h @@ -33,10 +33,10 @@ #ifdef ARDUINO #include #endif -#include "../src/IRremoteESP8266.h" -#include "../src/IRsend.h" +#include "IRremoteESP8266.h" +#include "IRsend.h" #ifdef UNIT_TEST -#include "../src/IRsend_test.h" +#include "IRsend_test.h" #endif /// @note diff --git a/lib/IRremoteESP8266/src/ir_MilesTag2.cpp b/lib/IRremoteESP8266/src/ir_MilesTag2.cpp new file mode 100644 index 0000000000..013b2fbe28 --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_MilesTag2.cpp @@ -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 +#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 diff --git a/lib/IRremoteESP8266/src/ir_Mitsubishi.h b/lib/IRremoteESP8266/src/ir_Mitsubishi.h index 5b02bca653..55afcdce66 100644 --- a/lib/IRremoteESP8266/src/ir_Mitsubishi.h +++ b/lib/IRremoteESP8266/src/ir_Mitsubishi.h @@ -44,10 +44,10 @@ #ifndef UNIT_TEST #include #endif -#include "../src/IRremoteESP8266.h" -#include "../src/IRsend.h" +#include "IRremoteESP8266.h" +#include "IRsend.h" #ifdef UNIT_TEST -#include "../src/IRsend_test.h" +#include "IRsend_test.h" #endif /// Native representation of a Mitsubishi 144-bit A/C message. diff --git a/lib/IRremoteESP8266/src/ir_MitsubishiHeavy.cpp b/lib/IRremoteESP8266/src/ir_MitsubishiHeavy.cpp new file mode 100644 index 0000000000..b4a6ffc352 --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_MitsubishiHeavy.cpp @@ -0,0 +1,1050 @@ +// 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 + +#include "ir_MitsubishiHeavy.h" +#include +#include +#include "IRremoteESP8266.h" +#include "IRtext.h" +#include "IRutils.h" +#ifndef ARDUINO +#include +#endif + +// Constants +const uint16_t kMitsubishiHeavyHdrMark = 3140; +const uint16_t kMitsubishiHeavyHdrSpace = 1630; +const uint16_t kMitsubishiHeavyBitMark = 370; +const uint16_t kMitsubishiHeavyOneSpace = 420; +const uint16_t kMitsubishiHeavyZeroSpace = 1220; +const uint32_t kMitsubishiHeavyGap = kDefaultMessageGap; // Just a guess. + +using irutils::addBoolToString; +using irutils::addIntToString; +using irutils::addLabeledString; +using irutils::addModeToString; +using irutils::addSwingHToString; +using irutils::addSwingVToString; +using irutils::addTempToString; +using irutils::checkInvertedBytePairs; +using irutils::invertBytePairs; + +#if SEND_MITSUBISHIHEAVY +/// Send a MitsubishiHeavy 88-bit A/C message. +/// Status: BETA / Appears to be working. Needs testing against a real device. +/// @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::sendMitsubishiHeavy88(const unsigned char data[], + const uint16_t nbytes, + const uint16_t repeat) { + if (nbytes < kMitsubishiHeavy88StateLength) + return; // Not enough bytes to send a proper message. + sendGeneric(kMitsubishiHeavyHdrMark, kMitsubishiHeavyHdrSpace, + kMitsubishiHeavyBitMark, kMitsubishiHeavyOneSpace, + kMitsubishiHeavyBitMark, kMitsubishiHeavyZeroSpace, + kMitsubishiHeavyBitMark, kMitsubishiHeavyGap, + data, nbytes, 38000, false, repeat, kDutyDefault); +} + +/// Send a MitsubishiHeavy 152-bit A/C message. +/// Status: BETA / Appears to be working. Needs testing against a real device. +/// @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::sendMitsubishiHeavy152(const unsigned char data[], + const uint16_t nbytes, + const uint16_t repeat) { + if (nbytes < kMitsubishiHeavy152StateLength) + return; // Not enough bytes to send a proper message. + sendMitsubishiHeavy88(data, nbytes, repeat); +} +#endif // SEND_MITSUBISHIHEAVY + +// Class for decoding and constructing MitsubishiHeavy152 AC messages. + +/// 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? +IRMitsubishiHeavy152Ac::IRMitsubishiHeavy152Ac(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 IRMitsubishiHeavy152Ac::begin(void) { _irsend.begin(); } + +#if SEND_MITSUBISHIHEAVY +/// Send the current internal state as an IR message. +/// @param[in] repeat Nr. of times the message will be repeated. +void IRMitsubishiHeavy152Ac::send(const uint16_t repeat) { + _irsend.sendMitsubishiHeavy152(getRaw(), kMitsubishiHeavy152StateLength, + repeat); +} +#endif // SEND_MITSUBISHIHEAVY + +/// Reset the state of the remote to a known good state/sequence. +void IRMitsubishiHeavy152Ac::stateReset(void) { + std::memcpy(_.raw, kMitsubishiHeavyZmsSig, kMitsubishiHeavySigLength); + for (uint8_t i = kMitsubishiHeavySigLength; + i < kMitsubishiHeavy152StateLength - 3; i += 2) _.raw[i] = 0; + _.raw[17] = 0x80; +} + +/// 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 *IRMitsubishiHeavy152Ac::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 IRMitsubishiHeavy152Ac::setRaw(const uint8_t *data) { + std::memcpy(_.raw, data, kMitsubishiHeavy152StateLength); +} + +/// Set the requested power state of the A/C to on. +void IRMitsubishiHeavy152Ac::on(void) { setPower(true); } + +/// Set the requested power state of the A/C to off. +void IRMitsubishiHeavy152Ac::off(void) { setPower(false); } + +/// Change the power setting. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRMitsubishiHeavy152Ac::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 IRMitsubishiHeavy152Ac::getPower(void) const { + return _.Power; +} + +/// Set the temperature. +/// @param[in] temp The temperature in degrees celsius. +void IRMitsubishiHeavy152Ac::setTemp(const uint8_t temp) { + uint8_t newtemp = temp; + newtemp = std::min(newtemp, kMitsubishiHeavyMaxTemp); + newtemp = std::max(newtemp, kMitsubishiHeavyMinTemp); + _.Temp = newtemp - kMitsubishiHeavyMinTemp; +} + +/// Get the current temperature setting. +/// @return The current setting for temp. in degrees celsius. +uint8_t IRMitsubishiHeavy152Ac::getTemp(void) const { + return _.Temp + kMitsubishiHeavyMinTemp; +} + +/// Set the speed of the fan. +/// @param[in] speed The desired setting. +void IRMitsubishiHeavy152Ac::setFan(const uint8_t speed) { + uint8_t newspeed = speed; + switch (speed) { + case kMitsubishiHeavy152FanLow: + case kMitsubishiHeavy152FanMed: + case kMitsubishiHeavy152FanHigh: + case kMitsubishiHeavy152FanMax: + case kMitsubishiHeavy152FanEcono: + case kMitsubishiHeavy152FanTurbo: break; + default: newspeed = kMitsubishiHeavy152FanAuto; + } + _.Fan = newspeed; +} + +/// Get the current fan speed setting. +/// @return The current fan speed/mode. +uint8_t IRMitsubishiHeavy152Ac::getFan(void) const { + return _.Fan; +} + +/// Set the operating mode of the A/C. +/// @param[in] mode The desired operating mode. +void IRMitsubishiHeavy152Ac::setMode(const uint8_t mode) { + uint8_t newmode = mode; + switch (mode) { + case kMitsubishiHeavyCool: + case kMitsubishiHeavyDry: + case kMitsubishiHeavyFan: + case kMitsubishiHeavyHeat: + break; + default: + newmode = kMitsubishiHeavyAuto; + } + _.Mode = newmode; +} + +/// Get the operating mode setting of the A/C. +/// @return The current operating mode setting. +uint8_t IRMitsubishiHeavy152Ac::getMode(void) const { + return _.Mode; +} + +/// Set the Vertical Swing mode of the A/C. +/// @param[in] pos The position/mode to set the swing to. +void IRMitsubishiHeavy152Ac::setSwingVertical(const uint8_t pos) { + _.SwingV = std::min(pos, kMitsubishiHeavy152SwingVOff); +} + +/// Get the Vertical Swing mode of the A/C. +/// @return The native position/mode setting. +uint8_t IRMitsubishiHeavy152Ac::getSwingVertical(void) const { + return _.SwingV; +} + +/// Set the Horizontal Swing mode of the A/C. +/// @param[in] pos The position/mode to set the swing to. +void IRMitsubishiHeavy152Ac::setSwingHorizontal(const uint8_t pos) { + _.SwingH = std::min(pos, kMitsubishiHeavy152SwingHOff); +} + +/// Get the Horizontal Swing mode of the A/C. +/// @return The native position/mode setting. +uint8_t IRMitsubishiHeavy152Ac::getSwingHorizontal(void) const { + return _.SwingH; +} + +/// Set the Night (Sleep) mode of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRMitsubishiHeavy152Ac::setNight(const bool on) { + _.Night = on; +} + +/// Get the Night (Sleep) mode of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRMitsubishiHeavy152Ac::getNight(void) const { + return _.Night; +} + +/// Set the 3D mode of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRMitsubishiHeavy152Ac::set3D(const bool on) { + if (on) + { _.Three = 1; _.D = 1; } + else + { _.Three = 0; _.D = 0; } +} + +/// Get the 3D mode of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRMitsubishiHeavy152Ac::get3D(void) const { + return _.Three && _.D; +} + +/// Set the Silent (Quiet) mode of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRMitsubishiHeavy152Ac::setSilent(const bool on) { + _.Silent = on; +} + +/// Get the Silent (Quiet) mode of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRMitsubishiHeavy152Ac::getSilent(void) const { + return _.Silent; +} + +/// Set the Filter mode of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRMitsubishiHeavy152Ac::setFilter(const bool on) { + _.Filter = on; +} + +/// Get the Filter mode of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRMitsubishiHeavy152Ac::getFilter(void) const { + return _.Filter; +} + +/// Set the Clean mode of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRMitsubishiHeavy152Ac::setClean(const bool on) { + _.Filter = on; + _.Clean = on; +} + +/// Get the Clean mode of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRMitsubishiHeavy152Ac::getClean(void) const { + return _.Clean && _.Filter; +} + +/// Set the Turbo mode of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRMitsubishiHeavy152Ac::setTurbo(const bool on) { + if (on) + setFan(kMitsubishiHeavy152FanTurbo); + else if (getTurbo()) setFan(kMitsubishiHeavy152FanAuto); +} + +/// Get the Turbo mode of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRMitsubishiHeavy152Ac::getTurbo(void) const { + return _.Fan == kMitsubishiHeavy152FanTurbo; +} + +/// Set the Economical mode of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRMitsubishiHeavy152Ac::setEcono(const bool on) { + if (on) + setFan(kMitsubishiHeavy152FanEcono); + else if (getEcono()) setFan(kMitsubishiHeavy152FanAuto); +} + +/// Get the Economical mode of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRMitsubishiHeavy152Ac::getEcono(void) const { + return _.Fan == kMitsubishiHeavy152FanEcono; +} + +/// Verify the given state has a ZM-S signature. +/// @param[in] state A ptr to a state to be checked. +/// @return true, the check passed. Otherwise, false. +bool IRMitsubishiHeavy152Ac::checkZmsSig(const uint8_t *state) { + for (uint8_t i = 0; i < kMitsubishiHeavySigLength; i++) + if (state[i] != kMitsubishiHeavyZmsSig[i]) return false; + return true; +} + +/// Calculate the checksum for the current internal state of the remote. +/// Note: Technically it has no checksum, but does have inverted byte pairs. +void IRMitsubishiHeavy152Ac::checksum(void) { + const uint8_t kOffset = kMitsubishiHeavySigLength - 2; + invertBytePairs(_.raw + kOffset, kMitsubishiHeavy152StateLength - kOffset); +} + +/// 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 state array. +/// @return true, if the state has a valid checksum. Otherwise, false. +/// Note: Technically it has no checksum, but does have inverted byte pairs. +bool IRMitsubishiHeavy152Ac::validChecksum(const uint8_t *state, + const uint16_t length) { + // Assume anything too short is fine. + if (length < kMitsubishiHeavySigLength) return true; + const uint8_t kOffset = kMitsubishiHeavySigLength - 2; + return checkInvertedBytePairs(state + kOffset, length - kOffset); +} + +/// 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 IRMitsubishiHeavy152Ac::convertMode(const stdAc::opmode_t mode) { + switch (mode) { + case stdAc::opmode_t::kCool: return kMitsubishiHeavyCool; + case stdAc::opmode_t::kHeat: return kMitsubishiHeavyHeat; + case stdAc::opmode_t::kDry: return kMitsubishiHeavyDry; + case stdAc::opmode_t::kFan: return kMitsubishiHeavyFan; + default: return kMitsubishiHeavyAuto; + } +} + +/// 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 IRMitsubishiHeavy152Ac::convertFan(const stdAc::fanspeed_t speed) { + switch (speed) { + // Assumes Econo is slower than Low. + case stdAc::fanspeed_t::kMin: return kMitsubishiHeavy152FanEcono; + case stdAc::fanspeed_t::kLow: return kMitsubishiHeavy152FanLow; + case stdAc::fanspeed_t::kMedium: return kMitsubishiHeavy152FanMed; + case stdAc::fanspeed_t::kHigh: return kMitsubishiHeavy152FanHigh; + case stdAc::fanspeed_t::kMax: return kMitsubishiHeavy152FanMax; + default: return kMitsubishiHeavy152FanAuto; + } +} + +/// 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 IRMitsubishiHeavy152Ac::convertSwingV(const stdAc::swingv_t position) { + switch (position) { + case stdAc::swingv_t::kAuto: return kMitsubishiHeavy152SwingVAuto; + case stdAc::swingv_t::kHighest: return kMitsubishiHeavy152SwingVHighest; + case stdAc::swingv_t::kHigh: return kMitsubishiHeavy152SwingVHigh; + case stdAc::swingv_t::kMiddle: return kMitsubishiHeavy152SwingVMiddle; + case stdAc::swingv_t::kLow: return kMitsubishiHeavy152SwingVLow; + case stdAc::swingv_t::kLowest: return kMitsubishiHeavy152SwingVLowest; + default: return kMitsubishiHeavy152SwingVOff; + } +} + +/// Convert a stdAc::swingh_t enum into it's native setting. +/// @param[in] position The enum to be converted. +/// @return The native equivalent of the enum. +uint8_t IRMitsubishiHeavy152Ac::convertSwingH(const stdAc::swingh_t position) { + switch (position) { + case stdAc::swingh_t::kAuto: return kMitsubishiHeavy152SwingHAuto; + case stdAc::swingh_t::kLeftMax: return kMitsubishiHeavy152SwingHLeftMax; + case stdAc::swingh_t::kLeft: return kMitsubishiHeavy152SwingHLeft; + case stdAc::swingh_t::kMiddle: return kMitsubishiHeavy152SwingHMiddle; + case stdAc::swingh_t::kRight: return kMitsubishiHeavy152SwingHRight; + case stdAc::swingh_t::kRightMax: return kMitsubishiHeavy152SwingHRightMax; + default: return kMitsubishiHeavy152SwingHOff; + } +} + +/// 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 IRMitsubishiHeavy152Ac::toCommonMode(const uint8_t mode) { + switch (mode) { + case kMitsubishiHeavyCool: return stdAc::opmode_t::kCool; + case kMitsubishiHeavyHeat: return stdAc::opmode_t::kHeat; + case kMitsubishiHeavyDry: return stdAc::opmode_t::kDry; + case kMitsubishiHeavyFan: return stdAc::opmode_t::kFan; + default: return stdAc::opmode_t::kAuto; + } +} + +/// Convert a native fan speed into its stdAc equivalent. +/// @param[in] spd The native setting to be converted. +/// @return The stdAc equivalent of the native setting. +stdAc::fanspeed_t IRMitsubishiHeavy152Ac::toCommonFanSpeed(const uint8_t spd) { + switch (spd) { + case kMitsubishiHeavy152FanMax: return stdAc::fanspeed_t::kMax; + case kMitsubishiHeavy152FanHigh: return stdAc::fanspeed_t::kHigh; + case kMitsubishiHeavy152FanMed: return stdAc::fanspeed_t::kMedium; + case kMitsubishiHeavy152FanLow: return stdAc::fanspeed_t::kLow; + case kMitsubishiHeavy152FanEcono: return stdAc::fanspeed_t::kMin; + default: return stdAc::fanspeed_t::kAuto; + } +} + +/// Convert a native horizontal swing postion to it's common equivalent. +/// @param[in] pos A native position to convert. +/// @return The common horizontal swing position. +stdAc::swingh_t IRMitsubishiHeavy152Ac::toCommonSwingH(const uint8_t pos) { + switch (pos) { + case kMitsubishiHeavy152SwingHLeftMax: return stdAc::swingh_t::kLeftMax; + case kMitsubishiHeavy152SwingHLeft: return stdAc::swingh_t::kLeft; + case kMitsubishiHeavy152SwingHMiddle: return stdAc::swingh_t::kMiddle; + case kMitsubishiHeavy152SwingHRight: return stdAc::swingh_t::kRight; + case kMitsubishiHeavy152SwingHRightMax: return stdAc::swingh_t::kRightMax; + case kMitsubishiHeavy152SwingHOff: return stdAc::swingh_t::kOff; + default: return stdAc::swingh_t::kAuto; + } +} + +/// 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 IRMitsubishiHeavy152Ac::toCommonSwingV(const uint8_t pos) { + switch (pos) { + case kMitsubishiHeavy152SwingVHighest: return stdAc::swingv_t::kHighest; + case kMitsubishiHeavy152SwingVHigh: return stdAc::swingv_t::kHigh; + case kMitsubishiHeavy152SwingVMiddle: return stdAc::swingv_t::kMiddle; + case kMitsubishiHeavy152SwingVLow: return stdAc::swingv_t::kLow; + case kMitsubishiHeavy152SwingVLowest: return stdAc::swingv_t::kLowest; + case kMitsubishiHeavy152SwingVOff: return stdAc::swingv_t::kOff; + default: return stdAc::swingv_t::kAuto; + } +} + +/// Convert the current internal state into its stdAc::state_t equivalent. +/// @return The stdAc equivalent of the native settings. +stdAc::state_t IRMitsubishiHeavy152Ac::toCommon(void) const { + stdAc::state_t result; + result.protocol = decode_type_t::MITSUBISHI_HEAVY_152; + 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 = toCommonSwingV(_.SwingV); + result.swingh = toCommonSwingH(_.SwingH); + result.turbo = getTurbo(); + result.econo = getEcono(); + result.clean = getClean(); + result.quiet = _.Silent; + result.filter = _.Filter; + result.sleep = _.Night ? 0 : -1; + // Not supported. + result.light = false; + result.beep = false; + result.clock = -1; + return result; +} + +/// Convert the internal state into a human readable string. +/// @return A string containing the settings in human-readable form. +String IRMitsubishiHeavy152Ac::toString(void) const { + String result = ""; + result.reserve(180); // Reserve some heap for the string to reduce fragging. + result += addBoolToString(_.Power, kPowerStr, false); + result += addModeToString(_.Mode, kMitsubishiHeavyAuto, + kMitsubishiHeavyCool, kMitsubishiHeavyHeat, + kMitsubishiHeavyDry, kMitsubishiHeavyFan); + result += addTempToString(getTemp()); + result += addIntToString(_.Fan, kFanStr); + result += kSpaceLBraceStr; + switch (_.Fan) { + case kMitsubishiHeavy152FanAuto: + result += kAutoStr; + break; + case kMitsubishiHeavy152FanHigh: + result += kHighStr; + break; + case kMitsubishiHeavy152FanLow: + result += kLowStr; + break; + case kMitsubishiHeavy152FanMed: + result += kMedStr; + break; + case kMitsubishiHeavy152FanMax: + result += kMaxStr; + break; + case kMitsubishiHeavy152FanEcono: + result += kEconoStr; + break; + case kMitsubishiHeavy152FanTurbo: + result += kTurboStr; + break; + default: + result += kUnknownStr; + } + result += ')'; + result += addSwingVToString(_.SwingV, kMitsubishiHeavy152SwingVAuto, + kMitsubishiHeavy152SwingVHighest, + kMitsubishiHeavy152SwingVHigh, + kMitsubishiHeavy152SwingVAuto, // UpperMid unused + kMitsubishiHeavy152SwingVMiddle, + kMitsubishiHeavy152SwingVAuto, // LowerMid unused + kMitsubishiHeavy152SwingVLow, + kMitsubishiHeavy152SwingVLowest, + kMitsubishiHeavy152SwingVOff, + // Below are unused. + kMitsubishiHeavy152SwingVAuto, + kMitsubishiHeavy152SwingVAuto, + kMitsubishiHeavy152SwingVAuto); + result += addSwingHToString(_.SwingH, kMitsubishiHeavy152SwingHAuto, + kMitsubishiHeavy152SwingHLeftMax, + kMitsubishiHeavy152SwingHLeft, + kMitsubishiHeavy152SwingHMiddle, + kMitsubishiHeavy152SwingHRight, + kMitsubishiHeavy152SwingHRightMax, + kMitsubishiHeavy152SwingHOff, + kMitsubishiHeavy152SwingHLeftRight, + kMitsubishiHeavy152SwingHRightLeft, + // Below are unused. + kMitsubishiHeavy152SwingHAuto, + kMitsubishiHeavy152SwingHAuto); + result += addBoolToString(_.Silent, kSilentStr); + result += addBoolToString(getTurbo(), kTurboStr); + result += addBoolToString(getEcono(), kEconoStr); + result += addBoolToString(_.Night, kNightStr); + result += addBoolToString(_.Filter, kFilterStr); + result += addBoolToString(get3D(), k3DStr); + result += addBoolToString(getClean(), kCleanStr); + return result; +} + + +// Class for decoding and constructing MitsubishiHeavy88 AC messages. + +/// 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? +IRMitsubishiHeavy88Ac::IRMitsubishiHeavy88Ac(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 IRMitsubishiHeavy88Ac::begin(void) { _irsend.begin(); } + +#if SEND_MITSUBISHIHEAVY +/// Send the current internal state as an IR message. +/// @param[in] repeat Nr. of times the message will be repeated. +void IRMitsubishiHeavy88Ac::send(const uint16_t repeat) { + _irsend.sendMitsubishiHeavy88(getRaw(), kMitsubishiHeavy88StateLength, + repeat); +} +#endif // SEND_MITSUBISHIHEAVY + +/// Reset the state of the remote to a known good state/sequence. +void IRMitsubishiHeavy88Ac::stateReset(void) { + std::memcpy(_.raw, kMitsubishiHeavyZjsSig, kMitsubishiHeavySigLength); + for (uint8_t i = kMitsubishiHeavySigLength; i < kMitsubishiHeavy88StateLength; + i++) _.raw[i] = 0; +} + +/// 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 *IRMitsubishiHeavy88Ac::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 IRMitsubishiHeavy88Ac::setRaw(const uint8_t *data) { + std::memcpy(_.raw, data, kMitsubishiHeavy88StateLength); +} + +/// Set the requested power state of the A/C to on. +void IRMitsubishiHeavy88Ac::on(void) { setPower(true); } + +/// Set the requested power state of the A/C to off. +void IRMitsubishiHeavy88Ac::off(void) { setPower(false); } + +/// Change the power setting. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRMitsubishiHeavy88Ac::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 IRMitsubishiHeavy88Ac::getPower(void) const { + return _.Power; +} + +/// Set the temperature. +/// @param[in] temp The temperature in degrees celsius. +void IRMitsubishiHeavy88Ac::setTemp(const uint8_t temp) { + uint8_t newtemp = temp; + newtemp = std::min(newtemp, kMitsubishiHeavyMaxTemp); + newtemp = std::max(newtemp, kMitsubishiHeavyMinTemp); + _.Temp = newtemp - kMitsubishiHeavyMinTemp; +} + +/// Get the current temperature setting. +/// @return The current setting for temp. in degrees celsius. +uint8_t IRMitsubishiHeavy88Ac::getTemp(void) const { + return _.Temp + kMitsubishiHeavyMinTemp; +} + +/// Set the speed of the fan. +/// @param[in] speed The desired setting. +void IRMitsubishiHeavy88Ac::setFan(const uint8_t speed) { + uint8_t newspeed = speed; + switch (speed) { + case kMitsubishiHeavy88FanLow: + case kMitsubishiHeavy88FanMed: + case kMitsubishiHeavy88FanHigh: + case kMitsubishiHeavy88FanTurbo: + case kMitsubishiHeavy88FanEcono: break; + default: newspeed = kMitsubishiHeavy88FanAuto; + } + _.Fan = newspeed; +} + +/// Get the current fan speed setting. +/// @return The current fan speed/mode. +uint8_t IRMitsubishiHeavy88Ac::getFan(void) const { + return _.Fan; +} + +/// Set the operating mode of the A/C. +/// @param[in] mode The desired operating mode. +void IRMitsubishiHeavy88Ac::setMode(const uint8_t mode) { + uint8_t newmode = mode; + switch (mode) { + case kMitsubishiHeavyCool: + case kMitsubishiHeavyDry: + case kMitsubishiHeavyFan: + case kMitsubishiHeavyHeat: + break; + default: + newmode = kMitsubishiHeavyAuto; + } + _.Mode = newmode; +} + +/// Get the operating mode setting of the A/C. +/// @return The current operating mode setting. +uint8_t IRMitsubishiHeavy88Ac::getMode(void) const { + return _.Mode; +} + +/// Set the Vertical Swing mode of the A/C. +/// @param[in] pos The position/mode to set the swing to. +void IRMitsubishiHeavy88Ac::setSwingVertical(const uint8_t pos) { + uint8_t newpos; + switch (pos) { + case kMitsubishiHeavy88SwingVAuto: + case kMitsubishiHeavy88SwingVHighest: + case kMitsubishiHeavy88SwingVHigh: + case kMitsubishiHeavy88SwingVMiddle: + case kMitsubishiHeavy88SwingVLow: + case kMitsubishiHeavy88SwingVLowest: newpos = pos; break; + default: newpos = kMitsubishiHeavy88SwingVOff; + } + _.SwingV5 = newpos; + _.SwingV7 = (newpos >> kMitsubishiHeavy88SwingVByte5Size); +} + +/// Get the Vertical Swing mode of the A/C. +/// @return The native position/mode setting. +uint8_t IRMitsubishiHeavy88Ac::getSwingVertical(void) const { + return _.SwingV5 | (_.SwingV7 << kMitsubishiHeavy88SwingVByte5Size); +} + +/// Set the Horizontal Swing mode of the A/C. +/// @param[in] pos The position/mode to set the swing to. +void IRMitsubishiHeavy88Ac::setSwingHorizontal(const uint8_t pos) { + uint8_t newpos; + switch (pos) { + case kMitsubishiHeavy88SwingHAuto: + case kMitsubishiHeavy88SwingHLeftMax: + case kMitsubishiHeavy88SwingHLeft: + case kMitsubishiHeavy88SwingHMiddle: + case kMitsubishiHeavy88SwingHRight: + case kMitsubishiHeavy88SwingHRightMax: + case kMitsubishiHeavy88SwingHLeftRight: + case kMitsubishiHeavy88SwingHRightLeft: + case kMitsubishiHeavy88SwingH3D: newpos = pos; break; + default: newpos = kMitsubishiHeavy88SwingHOff; + } + _.SwingH1 = newpos; + _.SwingH2 = (newpos >> kMitsubishiHeavy88SwingHSize); +} + +/// Get the Horizontal Swing mode of the A/C. +/// @return The native position/mode setting. +uint8_t IRMitsubishiHeavy88Ac::getSwingHorizontal(void) const { + return _.SwingH1 | (_.SwingH2 << kMitsubishiHeavy88SwingHSize); +} + +/// Set the Turbo mode of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRMitsubishiHeavy88Ac::setTurbo(const bool on) { + if (on) + setFan(kMitsubishiHeavy88FanTurbo); + else if (getTurbo()) setFan(kMitsubishiHeavy88FanAuto); +} + +/// Get the Turbo mode of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRMitsubishiHeavy88Ac::getTurbo(void) const { + return _.Fan == kMitsubishiHeavy88FanTurbo; +} + +/// Set the Economical mode of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRMitsubishiHeavy88Ac::setEcono(const bool on) { + if (on) + setFan(kMitsubishiHeavy88FanEcono); + else if (getEcono()) setFan(kMitsubishiHeavy88FanAuto); +} + +/// Get the Economical mode of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRMitsubishiHeavy88Ac::getEcono(void) const { + return _.Fan == kMitsubishiHeavy88FanEcono; +} + +/// Set the 3D mode of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRMitsubishiHeavy88Ac::set3D(const bool on) { + if (on) + setSwingHorizontal(kMitsubishiHeavy88SwingH3D); + else if (get3D()) + setSwingHorizontal(kMitsubishiHeavy88SwingHOff); +} + +/// Get the 3D mode of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRMitsubishiHeavy88Ac::get3D(void) const { + return getSwingHorizontal() == kMitsubishiHeavy88SwingH3D; +} + +/// Set the Clean mode of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRMitsubishiHeavy88Ac::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 IRMitsubishiHeavy88Ac::getClean(void) const { + return _.Clean; +} + +/// Verify the given state has a ZJ-S signature. +/// @param[in] state A ptr to a state to be checked. +/// @return true, the check passed. Otherwise, false. +bool IRMitsubishiHeavy88Ac::checkZjsSig(const uint8_t *state) { + for (uint8_t i = 0; i < kMitsubishiHeavySigLength; i++) + if (state[i] != kMitsubishiHeavyZjsSig[i]) return false; + return true; +} + +/// Calculate the checksum for the current internal state of the remote. +/// Note: Technically it has no checksum, but does have inverted byte pairs. +void IRMitsubishiHeavy88Ac::checksum(void) { + const uint8_t kOffset = kMitsubishiHeavySigLength - 2; + invertBytePairs(_.raw + kOffset, kMitsubishiHeavy88StateLength - kOffset); +} + +/// 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 state array. +/// @return true, if the state has a valid checksum. Otherwise, false. +/// Note: Technically it has no checksum, but does have inverted byte pairs. +bool IRMitsubishiHeavy88Ac::validChecksum(const uint8_t *state, + const uint16_t length) { + return IRMitsubishiHeavy152Ac::validChecksum(state, length); +} + +/// 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 IRMitsubishiHeavy88Ac::convertMode(const stdAc::opmode_t mode) { + return IRMitsubishiHeavy152Ac::convertMode(mode); +} + +/// 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 IRMitsubishiHeavy88Ac::convertFan(const stdAc::fanspeed_t speed) { + switch (speed) { + // Assumes Econo is slower than Low. + case stdAc::fanspeed_t::kMin: return kMitsubishiHeavy88FanEcono; + case stdAc::fanspeed_t::kLow: return kMitsubishiHeavy88FanLow; + case stdAc::fanspeed_t::kMedium: return kMitsubishiHeavy88FanMed; + case stdAc::fanspeed_t::kHigh: return kMitsubishiHeavy88FanHigh; + case stdAc::fanspeed_t::kMax: return kMitsubishiHeavy88FanTurbo; + default: return kMitsubishiHeavy88FanAuto; + } +} + +/// 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 IRMitsubishiHeavy88Ac::convertSwingV(const stdAc::swingv_t position) { + switch (position) { + case stdAc::swingv_t::kAuto: return kMitsubishiHeavy88SwingVAuto; + case stdAc::swingv_t::kHighest: return kMitsubishiHeavy88SwingVHighest; + case stdAc::swingv_t::kHigh: return kMitsubishiHeavy88SwingVHigh; + case stdAc::swingv_t::kMiddle: return kMitsubishiHeavy88SwingVMiddle; + case stdAc::swingv_t::kLow: return kMitsubishiHeavy88SwingVLow; + case stdAc::swingv_t::kLowest: return kMitsubishiHeavy88SwingVLowest; + default: return kMitsubishiHeavy88SwingVOff; + } +} + +/// Convert a stdAc::swingh_t enum into it's native setting. +/// @param[in] position The enum to be converted. +/// @return The native equivalent of the enum. +uint8_t IRMitsubishiHeavy88Ac::convertSwingH(const stdAc::swingh_t position) { + switch (position) { + case stdAc::swingh_t::kAuto: return kMitsubishiHeavy88SwingHAuto; + case stdAc::swingh_t::kLeftMax: return kMitsubishiHeavy88SwingHLeftMax; + case stdAc::swingh_t::kLeft: return kMitsubishiHeavy88SwingHLeft; + case stdAc::swingh_t::kMiddle: return kMitsubishiHeavy88SwingHMiddle; + case stdAc::swingh_t::kRight: return kMitsubishiHeavy88SwingHRight; + case stdAc::swingh_t::kRightMax: return kMitsubishiHeavy88SwingHRightMax; + default: return kMitsubishiHeavy88SwingHOff; + } +} + +/// 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 IRMitsubishiHeavy88Ac::toCommonFanSpeed(const uint8_t speed) { + switch (speed) { + case kMitsubishiHeavy88FanTurbo: return stdAc::fanspeed_t::kMax; + case kMitsubishiHeavy88FanHigh: return stdAc::fanspeed_t::kHigh; + case kMitsubishiHeavy88FanMed: return stdAc::fanspeed_t::kMedium; + case kMitsubishiHeavy88FanLow: return stdAc::fanspeed_t::kLow; + case kMitsubishiHeavy88FanEcono: return stdAc::fanspeed_t::kMin; + default: return stdAc::fanspeed_t::kAuto; + } +} + +/// Convert a native horizontal swing postion to it's common equivalent. +/// @param[in] pos A native position to convert. +/// @return The common horizontal swing position. +stdAc::swingh_t IRMitsubishiHeavy88Ac::toCommonSwingH(const uint8_t pos) { + switch (pos) { + case kMitsubishiHeavy88SwingHLeftMax: return stdAc::swingh_t::kLeftMax; + case kMitsubishiHeavy88SwingHLeft: return stdAc::swingh_t::kLeft; + case kMitsubishiHeavy88SwingHMiddle: return stdAc::swingh_t::kMiddle; + case kMitsubishiHeavy88SwingHRight: return stdAc::swingh_t::kRight; + case kMitsubishiHeavy88SwingHRightMax: return stdAc::swingh_t::kRightMax; + case kMitsubishiHeavy88SwingHOff: return stdAc::swingh_t::kOff; + default: return stdAc::swingh_t::kAuto; + } +} + +/// 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 IRMitsubishiHeavy88Ac::toCommonSwingV(const uint8_t pos) { + switch (pos) { + case kMitsubishiHeavy88SwingVHighest: return stdAc::swingv_t::kHighest; + case kMitsubishiHeavy88SwingVHigh: return stdAc::swingv_t::kHigh; + case kMitsubishiHeavy88SwingVMiddle: return stdAc::swingv_t::kMiddle; + case kMitsubishiHeavy88SwingVLow: return stdAc::swingv_t::kLow; + case kMitsubishiHeavy88SwingVLowest: return stdAc::swingv_t::kLowest; + case kMitsubishiHeavy88SwingVOff: return stdAc::swingv_t::kOff; + default: return stdAc::swingv_t::kAuto; + } +} + +/// Convert the current internal state into its stdAc::state_t equivalent. +/// @return The stdAc equivalent of the native settings. +stdAc::state_t IRMitsubishiHeavy88Ac::toCommon(void) const { + stdAc::state_t result; + result.protocol = decode_type_t::MITSUBISHI_HEAVY_88; + result.model = -1; // No models used. + result.power = _.Power; + result.mode = IRMitsubishiHeavy152Ac::toCommonMode(_.Mode); + result.celsius = true; + result.degrees = getTemp(); + result.fanspeed = toCommonFanSpeed(_.Fan); + result.swingv = toCommonSwingV(getSwingVertical()); + result.swingh = toCommonSwingH(getSwingHorizontal()); + result.turbo = getTurbo(); + result.econo = getEcono(); + result.clean = _.Clean; + // Not supported. + result.quiet = false; + result.filter = false; + result.light = false; + result.beep = false; + result.sleep = -1; + result.clock = -1; + return result; +} + +/// Convert the internal state into a human readable string. +/// @return A string containing the settings in human-readable form. +String IRMitsubishiHeavy88Ac::toString(void) const { + String result = ""; + result.reserve(140); // Reserve some heap for the string to reduce fragging. + result += addBoolToString(_.Power, kPowerStr, false); + result += addModeToString(_.Mode, kMitsubishiHeavyAuto, + kMitsubishiHeavyCool, kMitsubishiHeavyHeat, + kMitsubishiHeavyDry, kMitsubishiHeavyFan); + result += addTempToString(getTemp()); + result += addIntToString(_.Fan, kFanStr); + result += kSpaceLBraceStr; + switch (_.Fan) { + case kMitsubishiHeavy88FanAuto: + result += kAutoStr; + break; + case kMitsubishiHeavy88FanHigh: + result += kHighStr; + break; + case kMitsubishiHeavy88FanLow: + result += kLowStr; + break; + case kMitsubishiHeavy88FanMed: + result += kMedStr; + break; + case kMitsubishiHeavy88FanEcono: + result += kEconoStr; + break; + case kMitsubishiHeavy88FanTurbo: + result += kTurboStr; + break; + default: + result += kUnknownStr; + } + result += ')'; + result += addSwingVToString(getSwingVertical(), kMitsubishiHeavy88SwingVAuto, + kMitsubishiHeavy88SwingVHighest, + kMitsubishiHeavy88SwingVHigh, + kMitsubishiHeavy88SwingVAuto, // UpperMid unused + kMitsubishiHeavy88SwingVMiddle, + kMitsubishiHeavy88SwingVAuto, // LowerMid unused + kMitsubishiHeavy88SwingVLow, + kMitsubishiHeavy88SwingVLowest, + kMitsubishiHeavy88SwingVOff, + // Below are unused. + kMitsubishiHeavy88SwingVAuto, + kMitsubishiHeavy88SwingVAuto, + kMitsubishiHeavy88SwingVAuto); + result += addSwingHToString(getSwingHorizontal(), + kMitsubishiHeavy88SwingHAuto, + kMitsubishiHeavy88SwingHLeftMax, + kMitsubishiHeavy88SwingHLeft, + kMitsubishiHeavy88SwingHMiddle, + kMitsubishiHeavy88SwingHRight, + kMitsubishiHeavy88SwingHRightMax, + kMitsubishiHeavy88SwingHOff, + kMitsubishiHeavy88SwingHLeftRight, + kMitsubishiHeavy88SwingHRightLeft, + kMitsubishiHeavy88SwingH3D, + // Below are unused. + kMitsubishiHeavy88SwingHAuto); + result += addBoolToString(getTurbo(), kTurboStr); + result += addBoolToString(getEcono(), kEconoStr); + result += addBoolToString(get3D(), k3DStr); + result += addBoolToString(_.Clean, kCleanStr); + return result; +} + +#if DECODE_MITSUBISHIHEAVY +/// Decode the supplied Mitsubishi Heavy Industries A/C message. +/// Status: BETA / Appears to be working. 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 kMitsubishiHeavy88Bits or kMitsubishiHeavy152Bits (def). +/// @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::decodeMitsubishiHeavy(decode_results* results, uint16_t offset, + const uint16_t nbits, const bool strict) { + if (strict) { + switch (nbits) { + case kMitsubishiHeavy88Bits: + case kMitsubishiHeavy152Bits: + break; + default: + return false; // Not what is expected + } + } + + uint16_t used; + used = matchGeneric(results->rawbuf + offset, results->state, + results->rawlen - offset, nbits, + kMitsubishiHeavyHdrMark, kMitsubishiHeavyHdrSpace, + kMitsubishiHeavyBitMark, kMitsubishiHeavyOneSpace, + kMitsubishiHeavyBitMark, kMitsubishiHeavyZeroSpace, + kMitsubishiHeavyBitMark, kMitsubishiHeavyGap, true, + _tolerance, 0, false); + if (used == 0) return false; + offset += used; + // Compliance + switch (nbits) { + case kMitsubishiHeavy88Bits: + if (strict && !(IRMitsubishiHeavy88Ac::checkZjsSig(results->state) && + IRMitsubishiHeavy88Ac::validChecksum(results->state))) + return false; + results->decode_type = MITSUBISHI_HEAVY_88; + break; + case kMitsubishiHeavy152Bits: + if (strict && !(IRMitsubishiHeavy152Ac::checkZmsSig(results->state) && + IRMitsubishiHeavy152Ac::validChecksum(results->state))) + return false; + results->decode_type = MITSUBISHI_HEAVY_152; + break; + default: + return false; + } + + // Success + 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_MITSUBISHIHEAVY diff --git a/lib/IRremoteESP8266/src/ir_MitsubishiHeavy.h b/lib/IRremoteESP8266/src/ir_MitsubishiHeavy.h index 71b0138476..b1059f12fa 100644 --- a/lib/IRremoteESP8266/src/ir_MitsubishiHeavy.h +++ b/lib/IRremoteESP8266/src/ir_MitsubishiHeavy.h @@ -23,10 +23,10 @@ #ifndef UNIT_TEST #include #endif -#include "../src/IRremoteESP8266.h" -#include "../src/IRsend.h" +#include "IRremoteESP8266.h" +#include "IRsend.h" #ifdef UNIT_TEST -#include "../src/IRsend_test.h" +#include "IRsend_test.h" #endif /// Native representation of a Mitsubishi Heavy 152-bit A/C message. diff --git a/lib/IRremoteESP8266/src/ir_Multibrackets.cpp b/lib/IRremoteESP8266/src/ir_Multibrackets.cpp new file mode 100644 index 0000000000..2367b49bc9 --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_Multibrackets.cpp @@ -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 diff --git a/lib/IRremoteESP8266/src/ir_NEC.cpp b/lib/IRremoteESP8266/src/ir_NEC.cpp new file mode 100644 index 0000000000..9d4af76405 --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_NEC.cpp @@ -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 +#include +#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) diff --git a/lib/IRremoteESP8266/src/ir_NEC.h b/lib/IRremoteESP8266/src/ir_NEC.h index d7603304e1..95da064b78 100644 --- a/lib/IRremoteESP8266/src/ir_NEC.h +++ b/lib/IRremoteESP8266/src/ir_NEC.h @@ -19,7 +19,7 @@ #define IR_NEC_H_ #include -#include "../src/IRremoteESP8266.h" +#include "IRremoteESP8266.h" // Constants const uint16_t kNecTick = 560; diff --git a/lib/IRremoteESP8266/src/ir_Neoclima.cpp b/lib/IRremoteESP8266/src/ir_Neoclima.cpp new file mode 100644 index 0000000000..ff26e6c643 --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_Neoclima.cpp @@ -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 +#include +#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 diff --git a/lib/IRremoteESP8266/src/ir_Neoclima.h b/lib/IRremoteESP8266/src/ir_Neoclima.h index 16d838b385..90eaa029a9 100644 --- a/lib/IRremoteESP8266/src/ir_Neoclima.h +++ b/lib/IRremoteESP8266/src/ir_Neoclima.h @@ -20,10 +20,10 @@ #ifndef UNIT_TEST #include #endif -#include "../src/IRremoteESP8266.h" -#include "../src/IRsend.h" +#include "IRremoteESP8266.h" +#include "IRsend.h" #ifdef UNIT_TEST -#include "../src/IRsend_test.h" +#include "IRsend_test.h" #endif /// Native representation of a Neoclima A/C message. diff --git a/lib/IRremoteESP8266/src/ir_Nikai.cpp b/lib/IRremoteESP8266/src/ir_Nikai.cpp new file mode 100644 index 0000000000..01c789d70e --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_Nikai.cpp @@ -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 +#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 diff --git a/lib/IRremoteESP8266/src/ir_Panasonic.cpp b/lib/IRremoteESP8266/src/ir_Panasonic.cpp new file mode 100644 index 0000000000..e2add03f1f --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_Panasonic.cpp @@ -0,0 +1,1341 @@ +// Copyright 2015 Kristian Lauszus +// Copyright 2017, 2018 David Conran + +/// @file +/// @brief Support for Panasonic protocols. +/// Panasonic protocol originally added by Kristian Lauszus +/// (Thanks to zenwheel and other people at the original blog post) +/// @see Panasonic https://github.com/z3t0/Arduino-IRremote +/// @see http://www.remotecentral.com/cgi-bin/mboard/rc-pronto/thread.cgi?2615 +/// @see Panasonic A/C support heavily influenced by https://github.com/ToniA/ESPEasy/blob/HeatpumpIR/lib/HeatpumpIR/PanasonicHeatpumpIR.cpp +/// Panasonic A/C Clock & Timer support: +/// Reverse Engineering by MikkelTb +/// Code by crankyoldgit + +#include "ir_Panasonic.h" +#include +#include +#ifndef ARDUINO +#include +#endif +#include "IRrecv.h" +#include "IRsend.h" +#include "IRtext.h" +#include "IRutils.h" + +// Constants +/// @see http://www.remotecentral.com/cgi-bin/mboard/rc-pronto/thread.cgi?26152 +const uint16_t kPanasonicHdrMark = 3456; ///< uSeconds. +const uint16_t kPanasonicHdrSpace = 1728; ///< uSeconds. +const uint16_t kPanasonicBitMark = 432; ///< uSeconds. +const uint16_t kPanasonicOneSpace = 1296; ///< uSeconds. +const uint16_t kPanasonicZeroSpace = 432; ///< uSeconds. +const uint32_t kPanasonicMinCommandLength = 163296; ///< uSeconds. +const uint16_t kPanasonicEndGap = 5000; ///< uSeconds. See #245 +const uint32_t kPanasonicMinGap = 74736; ///< uSeconds. + +const uint16_t kPanasonicAcSectionGap = 10000; ///< uSeconds. +const uint16_t kPanasonicAcSection1Length = 8; +const uint32_t kPanasonicAcMessageGap = kDefaultMessageGap; // Just a guess. + +const uint16_t kPanasonicAc32HdrMark = 3543; ///< uSeconds. +const uint16_t kPanasonicAc32BitMark = 920; ///< uSeconds. +const uint16_t kPanasonicAc32HdrSpace = 3450; ///< uSeconds. +const uint16_t kPanasonicAc32OneSpace = 2575; ///< uSeconds. +const uint16_t kPanasonicAc32ZeroSpace = 828; ///< uSeconds. +const uint16_t kPanasonicAc32SectionGap = 13946; ///< uSeconds. +const uint8_t kPanasonicAc32Sections = 2; +const uint8_t kPanasonicAc32BlocksPerSection = 2; + +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::minsToString; +using irutils::setBit; +using irutils::setBits; + +// Used by Denon as well. +#if (SEND_PANASONIC || SEND_DENON) +/// Send a Panasonic 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 This protocol is a modified version of Kaseikyo. +/// @note Use this method if you want to send the results of `decodePanasonic`. +void IRsend::sendPanasonic64(const uint64_t data, const uint16_t nbits, + const uint16_t repeat) { + sendGeneric(kPanasonicHdrMark, kPanasonicHdrSpace, kPanasonicBitMark, + kPanasonicOneSpace, kPanasonicBitMark, kPanasonicZeroSpace, + kPanasonicBitMark, kPanasonicMinGap, kPanasonicMinCommandLength, + data, nbits, kPanasonicFreq, true, repeat, 50); +} + +/// Send a Panasonic formatted message. +/// Status: STABLE, but DEPRECATED +/// @deprecated This is only for legacy use only, please use `sendPanasonic64()` +/// instead. +/// @param[in] address The 16-bit manufacturer code. +/// @param[in] data The 32-bit data portion of 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 a modified version of Kaseikyo. +void IRsend::sendPanasonic(const uint16_t address, const uint32_t data, + const uint16_t nbits, const uint16_t repeat) { + sendPanasonic64(((uint64_t)address << 32) | (uint64_t)data, nbits, repeat); +} + +/// Calculate the raw Panasonic data based on device, subdevice, & function. +/// Status: STABLE / Should be working. +/// @param[in] manufacturer A 16-bit manufacturer code. e.g. 0x4004 is Panasonic +/// @param[in] device An 8-bit code. +/// @param[in] subdevice An 8-bit code. +/// @param[in] function An 8-bit code. +/// @return A value suitable for use with `sendPanasonic64()`. +/// @note Panasonic 48-bit protocol is a modified version of Kaseikyo. +/// @see http://www.remotecentral.com/cgi-bin/mboard/rc-pronto/thread.cgi?2615 +uint64_t IRsend::encodePanasonic(const uint16_t manufacturer, + const uint8_t device, + const uint8_t subdevice, + const uint8_t function) { + uint8_t checksum = device ^ subdevice ^ function; + return (((uint64_t)manufacturer << 32) | ((uint64_t)device << 24) | + ((uint64_t)subdevice << 16) | ((uint64_t)function << 8) | checksum); +} +#endif // (SEND_PANASONIC || SEND_DENON) + +// Used by Denon as well. +#if (DECODE_PANASONIC || DECODE_DENON) +/// Decode the supplied Panasonic message. +/// Status: STABLE / Should 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] manufacturer A 16-bit manufacturer code. e.g. 0x4004 is Panasonic +/// @param[in] strict Flag indicating if we should perform strict matching. +/// @return True if it can decode it, false if it can't. +/// @warning Results to be used with `sendPanasonic64()`, not `sendPanasonic()`. +/// @note Panasonic 48-bit protocol is a modified version of Kaseikyo. +/// @see http://www.remotecentral.com/cgi-bin/mboard/rc-pronto/thread.cgi?2615 +/// @see http://www.hifi-remote.com/wiki/index.php?title=Panasonic +bool IRrecv::decodePanasonic(decode_results *results, uint16_t offset, + const uint16_t nbits, const bool strict, + const uint32_t manufacturer) { + if (strict && nbits != kPanasonicBits) + return false; // Request is out of spec. + + uint64_t data = 0; + + // Match Header + Data + Footer + if (!matchGeneric(results->rawbuf + offset, &data, + results->rawlen - offset, nbits, + kPanasonicHdrMark, kPanasonicHdrSpace, + kPanasonicBitMark, kPanasonicOneSpace, + kPanasonicBitMark, kPanasonicZeroSpace, + kPanasonicBitMark, kPanasonicEndGap, true)) return false; + // Compliance + uint32_t address = data >> 32; + uint32_t command = data; + if (strict) { + if (address != manufacturer) // Verify the Manufacturer code. + return false; + // Verify the checksum. + uint8_t checksumOrig = data; + uint8_t checksumCalc = (data >> 24) ^ (data >> 16) ^ (data >> 8); + if (checksumOrig != checksumCalc) return false; + } + + // Success + results->value = data; + results->address = address; + results->command = command; + results->decode_type = decode_type_t::PANASONIC; + results->bits = nbits; + return true; +} +#endif // (DECODE_PANASONIC || DECODE_DENON) + +#if SEND_PANASONIC_AC +/// Send a Panasonic A/C message. +/// Status: STABLE / Work with real device(s). +/// @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::sendPanasonicAC(const uint8_t data[], const uint16_t nbytes, + const uint16_t repeat) { + if (nbytes < kPanasonicAcSection1Length) return; + for (uint16_t r = 0; r <= repeat; r++) { + // First section. (8 bytes) + sendGeneric(kPanasonicHdrMark, kPanasonicHdrSpace, kPanasonicBitMark, + kPanasonicOneSpace, kPanasonicBitMark, kPanasonicZeroSpace, + kPanasonicBitMark, kPanasonicAcSectionGap, data, + kPanasonicAcSection1Length, kPanasonicFreq, false, 0, 50); + // First section. (The rest of the data bytes) + sendGeneric(kPanasonicHdrMark, kPanasonicHdrSpace, kPanasonicBitMark, + kPanasonicOneSpace, kPanasonicBitMark, kPanasonicZeroSpace, + kPanasonicBitMark, kPanasonicAcMessageGap, + data + kPanasonicAcSection1Length, + nbytes - kPanasonicAcSection1Length, kPanasonicFreq, false, 0, + 50); + } +} +#endif // SEND_PANASONIC_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? +IRPanasonicAc::IRPanasonicAc(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 IRPanasonicAc::stateReset(void) { + memcpy(remote_state, kPanasonicKnownGoodState, kPanasonicAcStateLength); + _temp = 25; // An initial saved desired temp. Completely made up. + _swingh = kPanasonicAcSwingHMiddle; // A similar made up value for H Swing. +} + +/// Set up hardware to be able to send a message. +void IRPanasonicAc::begin(void) { _irsend.begin(); } + +/// 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 IRPanasonicAc::validChecksum(const uint8_t *state, const uint16_t length) { + if (length < 2) return false; // 1 byte of data can't have a checksum. + return (state[length - 1] == + sumBytes(state, length - 1, kPanasonicAcChecksumInit)); +} + +/// Calculate the checksum for a given state. +/// @param[in] state The value to calc the checksum of. +/// @param[in] length The size/length of the state. +/// @return The calculated checksum value. +uint8_t IRPanasonicAc::calcChecksum(const uint8_t *state, + const uint16_t length) { + return sumBytes(state, length - 1, kPanasonicAcChecksumInit); +} + +/// Calculate and set the checksum values for the internal state. +/// @param[in] length The size/length of the state. +void IRPanasonicAc::fixChecksum(const uint16_t length) { + remote_state[length - 1] = calcChecksum(remote_state, length); +} + +#if SEND_PANASONIC_AC +/// Send the current internal state as an IR message. +/// @param[in] repeat Nr. of times the message will be repeated. +void IRPanasonicAc::send(const uint16_t repeat) { + _irsend.sendPanasonicAC(getRaw(), kPanasonicAcStateLength, repeat); +} +#endif // SEND_PANASONIC_AC + +/// Set the model of the A/C to emulate. +/// @param[in] model The enum of the appropriate model. +void IRPanasonicAc::setModel(const panasonic_ac_remote_model_t model) { + switch (model) { + case panasonic_ac_remote_model_t::kPanasonicDke: + case panasonic_ac_remote_model_t::kPanasonicJke: + case panasonic_ac_remote_model_t::kPanasonicLke: + case panasonic_ac_remote_model_t::kPanasonicNke: + case panasonic_ac_remote_model_t::kPanasonicCkp: + case panasonic_ac_remote_model_t::kPanasonicRkr: break; + // Only proceed if we know what to do. + default: return; + } + // clear & set the various bits and bytes. + remote_state[13] &= 0xF0; + remote_state[17] = 0x00; + remote_state[21] &= 0b11101111; + remote_state[23] = 0x81; + remote_state[25] = 0x00; + + switch (model) { + case kPanasonicLke: + remote_state[13] |= 0x02; + remote_state[17] = 0x06; + break; + case kPanasonicDke: + remote_state[23] = 0x01; + remote_state[25] = 0x06; + // Has to be done last as setSwingHorizontal has model check built-in + setSwingHorizontal(_swingh); + break; + case kPanasonicNke: + remote_state[17] = 0x06; + break; + case kPanasonicJke: + break; + case kPanasonicCkp: + remote_state[21] |= 0x10; + remote_state[23] = 0x01; + break; + case kPanasonicRkr: + remote_state[13] |= 0x08; + remote_state[23] = 0x89; + default: + break; + } + // Reset the Ion filter. + setIon(getIon()); +} + +/// Get/Detect the model of the A/C. +/// @return The enum of the compatible model. +panasonic_ac_remote_model_t IRPanasonicAc::getModel(void) { + if (remote_state[23] == 0x89) return kPanasonicRkr; + if (remote_state[17] == 0x00) { + if ((remote_state[21] & 0x10) && (remote_state[23] & 0x01)) + return panasonic_ac_remote_model_t::kPanasonicCkp; + if (remote_state[23] & 0x80) + return panasonic_ac_remote_model_t::kPanasonicJke; + } + if (remote_state[17] == 0x06 && (remote_state[13] & 0x0F) == 0x02) + return panasonic_ac_remote_model_t::kPanasonicLke; + if (remote_state[23] == 0x01) + return panasonic_ac_remote_model_t::kPanasonicDke; + if (remote_state[17] == 0x06) + return panasonic_ac_remote_model_t::kPanasonicNke; + return panasonic_ac_remote_model_t::kPanasonicUnknown; // Default +} + +/// 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 *IRPanasonicAc::getRaw(void) { + fixChecksum(); + return remote_state; +} + +/// Set the internal state from a valid code for this protocol. +/// @param[in] state A valid code for this protocol. +void IRPanasonicAc::setRaw(const uint8_t state[]) { + memcpy(remote_state, state, kPanasonicAcStateLength); +} + +/// Control the power state of the A/C unit. +/// @param[in] on true, the setting is on. false, the setting is off. +/// @warning For CKP models, the remote has no memory of the power state the A/C +/// unit should be in. For those models setting this on/true will toggle the +/// power state of the Panasonic A/C unit with the next message. +/// e.g. If the A/C unit is already on, setPower(true) will turn it off. +/// If the A/C unit is already off, setPower(true) will turn it on. +/// `setPower(false)` will leave the A/C power state as it was. +/// For all other models, setPower(true) should set the internal state to +/// turn it on, and setPower(false) should turn it off. +void IRPanasonicAc::setPower(const bool on) { + setBit(&remote_state[13], kPanasonicAcPowerOffset, on); +} + +/// Get the A/C power state of the remote. +/// @return true, the setting is on. false, the setting is off. +/// @warning Except for CKP models, where it returns if the power state will be +/// toggled on the A/C unit when the next message is sent. +bool IRPanasonicAc::getPower(void) { + return GETBIT8(remote_state[13], kPanasonicAcPowerOffset); +} + +/// Change the power setting to On. +void IRPanasonicAc::on(void) { setPower(true); } + +/// Change the power setting to Off. +void IRPanasonicAc::off(void) { setPower(false); } + +/// Get the operating mode setting of the A/C. +/// @return The current operating mode setting. +uint8_t IRPanasonicAc::getMode(void) { + return GETBITS8(remote_state[13], kHighNibble, kModeBitsSize); +} + +/// Set the operating mode of the A/C. +/// @param[in] desired The desired operating mode. +void IRPanasonicAc::setMode(const uint8_t desired) { + uint8_t mode = kPanasonicAcAuto; // Default to Auto mode. + switch (desired) { + case kPanasonicAcFan: + // Allegedly Fan mode has a temperature of 27. + setTemp(kPanasonicAcFanModeTemp, false); + mode = desired; + break; + case kPanasonicAcAuto: + case kPanasonicAcCool: + case kPanasonicAcHeat: + case kPanasonicAcDry: + mode = desired; + // Set the temp to the saved temp, just incase our previous mode was Fan. + setTemp(_temp); + break; + } + remote_state[13] &= 0x0F; // Clear the previous mode bits. + setBits(&remote_state[13], kHighNibble, kModeBitsSize, mode); +} + +/// Get the current temperature setting. +/// @return The current setting for temp. in degrees celsius. +uint8_t IRPanasonicAc::getTemp(void) { + return GETBITS8(remote_state[14], kPanasonicAcTempOffset, + kPanasonicAcTempSize); +} + +/// Set the temperature. +/// @param[in] celsius The temperature in degrees celsius. +/// @param[in] remember: A flag for the class to remember the temperature. +/// @note Automatically safely limits the temp to the operating range supported. +void IRPanasonicAc::setTemp(const uint8_t celsius, const bool remember) { + uint8_t temperature; + temperature = std::max(celsius, kPanasonicAcMinTemp); + temperature = std::min(temperature, kPanasonicAcMaxTemp); + if (remember) _temp = temperature; + setBits(&remote_state[14], kPanasonicAcTempOffset, kPanasonicAcTempSize, + temperature); +} + +/// Get the current vertical swing setting. +/// @return The current position it is set to. +uint8_t IRPanasonicAc::getSwingVertical(void) { + return GETBITS8(remote_state[16], kLowNibble, kNibbleSize); +} + +/// Control the vertical swing setting. +/// @param[in] desired_elevation The position to set the vertical swing to. +void IRPanasonicAc::setSwingVertical(const uint8_t desired_elevation) { + uint8_t elevation = desired_elevation; + if (elevation != kPanasonicAcSwingVAuto) { + elevation = std::max(elevation, kPanasonicAcSwingVHighest); + elevation = std::min(elevation, kPanasonicAcSwingVLowest); + } + setBits(&remote_state[16], kLowNibble, kNibbleSize, elevation); +} + +/// Get the current horizontal swing setting. +/// @return The current position it is set to. +uint8_t IRPanasonicAc::getSwingHorizontal(void) { + return GETBITS8(remote_state[17], kLowNibble, kNibbleSize); +} + +/// Control the horizontal swing setting. +/// @param[in] desired_direction The position to set the horizontal swing to. +void IRPanasonicAc::setSwingHorizontal(const uint8_t desired_direction) { + switch (desired_direction) { + case kPanasonicAcSwingHAuto: + case kPanasonicAcSwingHMiddle: + case kPanasonicAcSwingHFullLeft: + case kPanasonicAcSwingHLeft: + case kPanasonicAcSwingHRight: + case kPanasonicAcSwingHFullRight: break; + // Ignore anything that isn't valid. + default: return; + } + _swingh = desired_direction; // Store the direction for later. + uint8_t direction = desired_direction; + switch (getModel()) { + case kPanasonicDke: + case kPanasonicRkr: + break; + case kPanasonicNke: + case kPanasonicLke: + direction = kPanasonicAcSwingHMiddle; + break; + default: // Ignore everything else. + return; + } + setBits(&remote_state[17], kLowNibble, kNibbleSize, direction); +} + +/// Set the speed of the fan. +/// @param[in] speed The desired setting. +void IRPanasonicAc::setFan(const uint8_t speed) { + switch (speed) { + case kPanasonicAcFanMin: + case kPanasonicAcFanLow: + case kPanasonicAcFanMed: + case kPanasonicAcFanHigh: + case kPanasonicAcFanMax: + case kPanasonicAcFanAuto: + setBits(&remote_state[16], kHighNibble, kNibbleSize, + speed + kPanasonicAcFanDelta); + break; + default: setFan(kPanasonicAcFanAuto); + } +} + +/// Get the current fan speed setting. +/// @return The current fan speed. +uint8_t IRPanasonicAc::getFan(void) { + return GETBITS8(remote_state[16], kHighNibble, kNibbleSize) - + kPanasonicAcFanDelta; +} + +/// Get the Quiet setting of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRPanasonicAc::getQuiet(void) { + switch (getModel()) { + case kPanasonicRkr: + case kPanasonicCkp: + return GETBIT8(remote_state[21], kPanasonicAcQuietCkpOffset); + default: + return GETBIT8(remote_state[21], kPanasonicAcQuietOffset); + } +} + +/// Set the Quiet setting of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRPanasonicAc::setQuiet(const bool on) { + uint8_t offset; + switch (getModel()) { + case kPanasonicRkr: + case kPanasonicCkp: offset = kPanasonicAcQuietCkpOffset; break; + default: offset = kPanasonicAcQuietOffset; + } + if (on) setPowerful(false); // Powerful is mutually exclusive. + setBit(&remote_state[21], offset, on); +} + +/// Get the Powerful (Turbo) setting of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRPanasonicAc::getPowerful(void) { + switch (getModel()) { + case kPanasonicRkr: + case kPanasonicCkp: + return GETBIT8(remote_state[21], kPanasonicAcPowerfulCkpOffset); + default: + return GETBIT8(remote_state[21], kPanasonicAcPowerfulOffset); + } +} + +/// Set the Powerful (Turbo) setting of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRPanasonicAc::setPowerful(const bool on) { + uint8_t offset; + switch (getModel()) { + case kPanasonicRkr: + case kPanasonicCkp: offset = kPanasonicAcPowerfulCkpOffset; break; + default: offset = kPanasonicAcPowerfulOffset; + } + + if (on) setQuiet(false); // Quiet is mutually exclusive. + setBit(&remote_state[21], offset, on); +} + +/// Convert standard (military/24hr) time to nr. of minutes since midnight. +/// @param[in] hours The hours component of the time. +/// @param[in] mins The minutes component of the time. +/// @return The nr of minutes since midnight. +uint16_t IRPanasonicAc::encodeTime(const uint8_t hours, const uint8_t mins) { + return std::min(hours, (uint8_t)23) * 60 + std::min(mins, (uint8_t)59); +} + +/// Get the time from a given pointer location. +/// @param[in] ptr A pointer to a time location in a state. +/// @return The time expressed as nr. of minutes past midnight. +/// @note Internal use only. +uint16_t IRPanasonicAc::_getTime(const uint8_t ptr[]) { + uint16_t result = (GETBITS8( + ptr[1], kLowNibble, kPanasonicAcTimeOverflowSize) << + (kPanasonicAcTimeSize - kPanasonicAcTimeOverflowSize)) + ptr[0]; + if (result == kPanasonicAcTimeSpecial) return 0; + return result; +} + +/// Get the current clock time value. +/// @return The time expressed as nr. of minutes past midnight. +uint16_t IRPanasonicAc::getClock(void) { return _getTime(&remote_state[24]); } + +/// Set the time at a given pointer location. +/// @param[in, out] ptr A pointer to a time location in a state. +/// @param[in] mins_since_midnight The time as nr. of minutes past midnight. +/// @param[in] round_down Do we round to the nearest 10 minute mark? +/// @note Internal use only. +void IRPanasonicAc::_setTime(uint8_t * const ptr, + const uint16_t mins_since_midnight, + const bool round_down) { + uint16_t corrected = std::min(mins_since_midnight, kPanasonicAcTimeMax); + if (round_down) corrected -= corrected % 10; + if (mins_since_midnight == kPanasonicAcTimeSpecial) + corrected = kPanasonicAcTimeSpecial; + ptr[0] = corrected; + setBits(&ptr[1], kLowNibble, kPanasonicAcTimeOverflowSize, + corrected >> (kPanasonicAcTimeSize - kPanasonicAcTimeOverflowSize)); +} + +/// Set the current clock time value. +/// @param[in] mins_since_midnight The time as nr. of minutes past midnight. +void IRPanasonicAc::setClock(const uint16_t mins_since_midnight) { + _setTime(&remote_state[24], mins_since_midnight, false); +} + +/// Get the On Timer time value. +/// @return The time expressed as nr. of minutes past midnight. +uint16_t IRPanasonicAc::getOnTimer(void) { return _getTime(&remote_state[18]); } + +/// Set/Enable the On Timer. +/// @param[in] mins_since_midnight The time as nr. of minutes past midnight. +/// @param[in] enable Do we enable the timer or not? +void IRPanasonicAc::setOnTimer(const uint16_t mins_since_midnight, + const bool enable) { + // Set the timer flag. + setBit(&remote_state[13], kPanasonicAcOnTimerOffset, enable); + // Store the time. + _setTime(&remote_state[18], mins_since_midnight, true); +} + +/// Cancel the On Timer. +void IRPanasonicAc::cancelOnTimer(void) { setOnTimer(0, false); } + +/// Check if the On Timer is Enabled. +/// @return true, the setting is on. false, the setting is off. +bool IRPanasonicAc::isOnTimerEnabled(void) { + return GETBIT8(remote_state[13], kPanasonicAcOnTimerOffset); +} + +/// Get the Off Timer time value. +/// @return The time expressed as nr. of minutes past midnight. +uint16_t IRPanasonicAc::getOffTimer(void) { + uint16_t result = (GETBITS8(remote_state[20], 0, 7) << kNibbleSize) | + GETBITS8(remote_state[19], kHighNibble, kNibbleSize); + if (result == kPanasonicAcTimeSpecial) return 0; + return result; +} + +/// Set/Enable the Off Timer. +/// @param[in] mins_since_midnight The time as nr. of minutes past midnight. +/// @param[in] enable Do we enable the timer or not? +void IRPanasonicAc::setOffTimer(const uint16_t mins_since_midnight, + const bool enable) { + // Ensure its on a 10 minute boundary and no overflow. + uint16_t corrected = std::min(mins_since_midnight, kPanasonicAcTimeMax); + corrected -= corrected % 10; + if (mins_since_midnight == kPanasonicAcTimeSpecial) + corrected = kPanasonicAcTimeSpecial; + // Set the timer flag. + setBit(&remote_state[13], kPanasonicAcOffTimerOffset, enable); + // Store the time. + setBits(&remote_state[19], kHighNibble, kNibbleSize, corrected); + setBits(&remote_state[20], 0, 7, corrected >> kNibbleSize); +} + +/// Cancel the Off Timer. +void IRPanasonicAc::cancelOffTimer(void) { setOffTimer(0, false); } + +/// Check if the Off Timer is Enabled. +/// @return true, the setting is on. false, the setting is off. +bool IRPanasonicAc::isOffTimerEnabled(void) { + return GETBIT8(remote_state[13], kPanasonicAcOffTimerOffset); +} + +/// Get the Ion (filter) setting of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRPanasonicAc::getIon(void) { + switch (getModel()) { + case kPanasonicDke: + return GETBIT8(remote_state[kPanasonicAcIonFilterByte], + kPanasonicAcIonFilterOffset); + default: + return false; + } +} + +/// Set the Ion (filter) setting of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRPanasonicAc::setIon(const bool on) { + if (getModel() == kPanasonicDke) + setBit(&remote_state[kPanasonicAcIonFilterByte], + kPanasonicAcIonFilterOffset, on); +} + +/// 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 IRPanasonicAc::convertMode(const stdAc::opmode_t mode) { + switch (mode) { + case stdAc::opmode_t::kCool: return kPanasonicAcCool; + case stdAc::opmode_t::kHeat: return kPanasonicAcHeat; + case stdAc::opmode_t::kDry: return kPanasonicAcDry; + case stdAc::opmode_t::kFan: return kPanasonicAcFan; + default: return kPanasonicAcAuto; + } +} + +/// 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 IRPanasonicAc::convertFan(const stdAc::fanspeed_t speed) { + switch (speed) { + case stdAc::fanspeed_t::kMin: return kPanasonicAcFanMin; + case stdAc::fanspeed_t::kLow: return kPanasonicAcFanLow; + case stdAc::fanspeed_t::kMedium: return kPanasonicAcFanMed; + case stdAc::fanspeed_t::kHigh: return kPanasonicAcFanHigh; + case stdAc::fanspeed_t::kMax: return kPanasonicAcFanMax; + default: return kPanasonicAcFanAuto; + } +} + +/// Convert a standard A/C vertical swing into its native setting. +/// @param[in] position A stdAc::swingv_t position to convert. +/// @return The equivalent native horizontal swing position. +uint8_t IRPanasonicAc::convertSwingV(const stdAc::swingv_t position) { + switch (position) { + case stdAc::swingv_t::kHighest: + case stdAc::swingv_t::kHigh: + case stdAc::swingv_t::kMiddle: + case stdAc::swingv_t::kLow: + case stdAc::swingv_t::kLowest: return (uint8_t)position; + default: return kPanasonicAcSwingVAuto; + } +} + +/// Convert a standard A/C horizontal swing into its native setting. +/// @param[in] position A stdAc::swingh_t position to convert. +/// @return The equivalent native horizontal swing position. +uint8_t IRPanasonicAc::convertSwingH(const stdAc::swingh_t position) { + switch (position) { + case stdAc::swingh_t::kLeftMax: return kPanasonicAcSwingHFullLeft; + case stdAc::swingh_t::kLeft: return kPanasonicAcSwingHLeft; + case stdAc::swingh_t::kMiddle: return kPanasonicAcSwingHMiddle; + case stdAc::swingh_t::kRight: return kPanasonicAcSwingHRight; + case stdAc::swingh_t::kRightMax: return kPanasonicAcSwingHFullRight; + default: return kPanasonicAcSwingHAuto; + } +} + +/// 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 IRPanasonicAc::toCommonMode(const uint8_t mode) { + switch (mode) { + case kPanasonicAcCool: return stdAc::opmode_t::kCool; + case kPanasonicAcHeat: return stdAc::opmode_t::kHeat; + case kPanasonicAcDry: return stdAc::opmode_t::kDry; + case kPanasonicAcFan: return stdAc::opmode_t::kFan; + default: return stdAc::opmode_t::kAuto; + } +} + +/// Convert a native fan speed into its stdAc equivalent. +/// @param[in] spd The native setting to be converted. +/// @return The stdAc equivalent of the native setting. +stdAc::fanspeed_t IRPanasonicAc::toCommonFanSpeed(const uint8_t spd) { + switch (spd) { + case kPanasonicAcFanMax: return stdAc::fanspeed_t::kMax; + case kPanasonicAcFanHigh: return stdAc::fanspeed_t::kHigh; + case kPanasonicAcFanMed: return stdAc::fanspeed_t::kMedium; + case kPanasonicAcFanLow: return stdAc::fanspeed_t::kLow; + case kPanasonicAcFanMin: return stdAc::fanspeed_t::kMin; + default: return stdAc::fanspeed_t::kAuto; + } +} + +/// Convert a native horizontal swing postion to it's common equivalent. +/// @param[in] pos A native position to convert. +/// @return The common horizontal swing position. +stdAc::swingh_t IRPanasonicAc::toCommonSwingH(const uint8_t pos) { + switch (pos) { + case kPanasonicAcSwingHFullLeft: return stdAc::swingh_t::kLeftMax; + case kPanasonicAcSwingHLeft: return stdAc::swingh_t::kLeft; + case kPanasonicAcSwingHMiddle: return stdAc::swingh_t::kMiddle; + case kPanasonicAcSwingHRight: return stdAc::swingh_t::kRight; + case kPanasonicAcSwingHFullRight: return stdAc::swingh_t::kRightMax; + default: return stdAc::swingh_t::kAuto; + } +} + +/// 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 IRPanasonicAc::toCommonSwingV(const uint8_t pos) { + if (pos >= kPanasonicAcSwingVHighest && pos <= kPanasonicAcSwingVLowest) + return (stdAc::swingv_t)pos; + else + return stdAc::swingv_t::kAuto; +} + +/// Convert the current internal state into its stdAc::state_t equivalent. +/// @return The stdAc equivalent of the native settings. +stdAc::state_t IRPanasonicAc::toCommon(void) { + stdAc::state_t result; + result.protocol = decode_type_t::PANASONIC_AC; + result.model = getModel(); + result.power = getPower(); + result.mode = toCommonMode(getMode()); + result.celsius = true; + result.degrees = getTemp(); + result.fanspeed = toCommonFanSpeed(getFan()); + result.swingv = toCommonSwingV(getSwingVertical()); + result.swingh = toCommonSwingH(getSwingHorizontal()); + result.quiet = getQuiet(); + result.turbo = getPowerful(); + result.filter = getIon(); + // Not supported. + result.econo = false; + result.clean = false; + result.light = false; + result.beep = false; + result.sleep = -1; + result.clock = -1; + return result; +} + +/// Convert the internal state into a human readable string. +/// @return A string containing the settings in human-readable form. +String IRPanasonicAc::toString(void) { + String result = ""; + result.reserve(180); // Reserve some heap for the string to reduce fragging. + result += addModelToString(decode_type_t::PANASONIC_AC, getModel(), false); + result += addBoolToString(getPower(), kPowerStr); + result += addModeToString(getMode(), kPanasonicAcAuto, kPanasonicAcCool, + kPanasonicAcHeat, kPanasonicAcDry, kPanasonicAcFan); + result += addTempToString(getTemp()); + result += addFanToString(getFan(), kPanasonicAcFanHigh, kPanasonicAcFanLow, + kPanasonicAcFanAuto, kPanasonicAcFanMin, + kPanasonicAcFanMed, kPanasonicAcFanMax); + result += addSwingVToString(getSwingVertical(), kPanasonicAcSwingVAuto, + kPanasonicAcSwingVHighest, + kPanasonicAcSwingVHigh, + kPanasonicAcSwingVAuto, // Upper Middle is unused + kPanasonicAcSwingVMiddle, + kPanasonicAcSwingVAuto, // Lower Middle is unused + kPanasonicAcSwingVLow, + kPanasonicAcSwingVLowest, + // Below are unused. + kPanasonicAcSwingVAuto, + kPanasonicAcSwingVAuto, + kPanasonicAcSwingVAuto, + kPanasonicAcSwingVAuto); + switch (getModel()) { + case kPanasonicJke: + case kPanasonicCkp: + break; // No Horizontal Swing support. + default: + result += addSwingHToString(getSwingHorizontal(), kPanasonicAcSwingHAuto, + kPanasonicAcSwingHFullLeft, + kPanasonicAcSwingHLeft, + kPanasonicAcSwingHMiddle, + kPanasonicAcSwingHRight, + kPanasonicAcSwingHFullRight, + // Below are unused. + kPanasonicAcSwingHAuto, + kPanasonicAcSwingHAuto, + kPanasonicAcSwingHAuto, + kPanasonicAcSwingHAuto, + kPanasonicAcSwingHAuto); + } + result += addBoolToString(getQuiet(), kQuietStr); + result += addBoolToString(getPowerful(), kPowerfulStr); + if (getModel() == kPanasonicDke) + result += addBoolToString(getIon(), kIonStr); + result += addLabeledString(minsToString(getClock()), kClockStr); + result += addLabeledString( + isOnTimerEnabled() ? minsToString(getOnTimer()) : kOffStr, + kOnTimerStr); + result += addLabeledString( + isOffTimerEnabled() ? minsToString(getOffTimer()) : kOffStr, + kOffTimerStr); + return result; +} + +#if DECODE_PANASONIC_AC +/// Decode the supplied Panasonic AC message. +/// Status: STABLE / Works with real device(s). +/// @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::decodePanasonicAC(decode_results *results, uint16_t offset, + const uint16_t nbits, const bool strict) { + uint8_t min_nr_of_messages = 1; + if (strict) { + if (nbits != kPanasonicAcBits && nbits != kPanasonicAcShortBits) + return false; // Not strictly a PANASONIC_AC message. + } + + if (results->rawlen <= + min_nr_of_messages * (2 * nbits + kHeader + kFooter) - 1 + offset) + return false; // Can't possibly be a valid PANASONIC_AC message. + + // Match Header + Data #1 + Footer + uint16_t used; + used = matchGeneric(results->rawbuf + offset, results->state, + results->rawlen - offset, kPanasonicAcSection1Length * 8, + kPanasonicHdrMark, kPanasonicHdrSpace, + kPanasonicBitMark, kPanasonicOneSpace, + kPanasonicBitMark, kPanasonicZeroSpace, + kPanasonicBitMark, kPanasonicAcSectionGap, false, + kPanasonicAcTolerance, kPanasonicAcExcess, false); + if (!used) return false; + offset += used; + + // Match Header + Data #2 + Footer + if (!matchGeneric(results->rawbuf + offset, + results->state + kPanasonicAcSection1Length, + results->rawlen - offset, + nbits - kPanasonicAcSection1Length * 8, + kPanasonicHdrMark, kPanasonicHdrSpace, + kPanasonicBitMark, kPanasonicOneSpace, + kPanasonicBitMark, kPanasonicZeroSpace, + kPanasonicBitMark, kPanasonicAcMessageGap, true, + kPanasonicAcTolerance, kPanasonicAcExcess, false)) + return false; + // Compliance + if (strict) { + // Check the signatures of the section blocks. They start with 0x02& 0x20. + if (results->state[0] != 0x02 || results->state[1] != 0x20 || + results->state[8] != 0x02 || results->state[9] != 0x20) + return false; + if (!IRPanasonicAc::validChecksum(results->state, nbits / 8)) return false; + } + + // Success + results->decode_type = decode_type_t::PANASONIC_AC; + results->bits = nbits; + return true; +} +#endif // DECODE_PANASONIC_AC + +#if SEND_PANASONIC_AC32 +/// Send a Panasonic AC 32/16bit formatted message. +/// Status: STABLE / Confirmed working. +/// @param[in] data containing the IR command. +/// @param[in] nbits Nr. of bits to send. Usually kPanasonicAc32Bits +/// @param[in] repeat Nr. of times the message is to be repeated. +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1307 +void IRsend::sendPanasonicAC32(const uint64_t data, const uint16_t nbits, + const uint16_t repeat) { + uint16_t section_bits; + uint16_t sections; + uint16_t blocks; + // Calculate the section, block, and bit sizes based on the requested bit size + if (nbits > kPanasonicAc32Bits / 2) { // A long message + section_bits = nbits / kPanasonicAc32Sections; + sections = kPanasonicAc32Sections; + blocks = kPanasonicAc32BlocksPerSection; + } else { // A short message + section_bits = nbits; + sections = kPanasonicAc32Sections - 1; + blocks = kPanasonicAc32BlocksPerSection + 1; + } + for (uint16_t r = 0; r <= repeat; r++) { + for (uint8_t section = 0; section < sections; section++) { + uint64_t section_data; + section_data = GETBITS64(data, section_bits * (sections - section - 1), + section_bits); + + // Duplicate bytes in the data. + uint64_t expanded_data = 0; + for (uint8_t i = 0; i < sizeof(expanded_data); i++) { + const uint8_t first_byte = section_data >> 56; + for (uint8_t i = 0; i < 2; i++) + expanded_data = (expanded_data << 8) | first_byte; + section_data <<= 8; + } + // Two data blocks per section (i.e. 1 + a repeat) + sendGeneric(kPanasonicAc32HdrMark, kPanasonicAc32HdrSpace, // Header + kPanasonicAc32BitMark, kPanasonicAc32OneSpace, // Data + kPanasonicAc32BitMark, kPanasonicAc32ZeroSpace, + 0, 0, // No Footer + expanded_data, section_bits * 2, kPanasonicFreq, false, + blocks - 1, // Repeat + 50); + // Section Footer + sendGeneric(kPanasonicAc32HdrMark, kPanasonicAc32HdrSpace, // Header + 0, 0, 0, 0, // No Data + kPanasonicAc32BitMark, kPanasonicAc32SectionGap, // Footer + data, 0, // No data (bits) + kPanasonicFreq, true, 0, 50); + } + } +} +#endif // SEND_PANASONIC_AC32 + +#if DECODE_PANASONIC_AC32 +/// Decode the supplied Panasonic AC 32/16bit 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. +/// Typically: kPanasonicAc32Bits or kPanasonicAc32Bits/2 +/// @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/1307 +/// @note Protocol has two known configurations: +/// (long) +/// Two sections of identical 32 bit data block pairs. ie. (32+32)+(32+32)=128 +/// or +/// (short) +/// A single section of 3 x identical 32 bit data blocks i.e. (32+32+32)=96 +/// Each data block also has a pair of 8 bits repeated identical bits. +/// e.g. (8+8)+(8+8)=32 +/// +/// So each long version really only has 32 unique bits, and the short version +/// really only has 16 unique bits. +bool IRrecv::decodePanasonicAC32(decode_results *results, uint16_t offset, + const uint16_t nbits, const bool strict) { + if (strict && (nbits != kPanasonicAc32Bits && + nbits != kPanasonicAc32Bits / 2)) + return false; // Not strictly a valid bit size. + + // Determine if this is a long or a short message we are looking for. + const bool is_long = (nbits > kPanasonicAc32Bits / 2); + const uint16_t min_length = is_long ? + kPanasonicAc32Sections * kPanasonicAc32BlocksPerSection * + ((2 * nbits) + kHeader + kFooter) - 1 + offset : + (kPanasonicAc32BlocksPerSection + 1) * ((4 * nbits) + kHeader) + + kFooter - 1 + offset; + + if (results->rawlen < min_length) + return false; // Can't possibly be a valid message. + + // Calculate the parameters for the decode based on it's length. + uint16_t sections; + uint16_t blocks_per_section; + if (is_long) { + sections = kPanasonicAc32Sections; + blocks_per_section = kPanasonicAc32BlocksPerSection; + } else { + sections = kPanasonicAc32Sections - 1; + blocks_per_section = kPanasonicAc32BlocksPerSection + 1; + } + const uint16_t bits_per_block = nbits / sections; + + uint64_t data = 0; + uint64_t section_data = 0; + uint32_t prev_section_data; + + // Match all the expected data blocks. + for (uint16_t block = 0; + block < sections * blocks_per_section; + block++) { + prev_section_data = section_data; + uint16_t used = matchGeneric(results->rawbuf + offset, §ion_data, + results->rawlen - offset, bits_per_block * 2, + kPanasonicAc32HdrMark, kPanasonicAc32HdrSpace, + kPanasonicAc32BitMark, kPanasonicAc32OneSpace, + kPanasonicAc32BitMark, kPanasonicAc32ZeroSpace, + 0, 0, // No Footer + false, kUseDefTol, kMarkExcess, false); + if (!used) return false; + offset += used; + // Is it the first block of the section? + if (block % blocks_per_section == 0) { + // The protocol repeats each byte twice, so to shrink the code we + // remove the duplicate bytes in the collected data. We only need to do + // this for the first block in a section. + uint64_t shrunk_data = 0; + uint64_t data_copy = section_data; + for (uint8_t i = 0; i < sizeof(data_copy); i += 2) { + const uint8_t first_byte = GETBITS64(data_copy, + (sizeof(data_copy) - 1) * 8, 8); + shrunk_data = (shrunk_data << 8) | first_byte; + // Compliance + if (strict) { + // Every second byte must be a duplicate of the previous. + const uint8_t next_byte = GETBITS64(data_copy, + (sizeof(data_copy) - 2) * 8, 8); + if (first_byte != next_byte) return false; + } + data_copy <<= 16; + } + // Keep the data from the first of the block in the section. + data = (data << bits_per_block) | shrunk_data; + } else { // Not the first block in a section. + // Compliance + if (strict) + // Compare the data from the blocks in pairs. + if (section_data != prev_section_data) return false; + // Look for the section footer at the end of the blocks. + if ((block + 1) % blocks_per_section == 0) { + uint64_t junk; + used = matchGeneric(results->rawbuf + offset, &junk, + results->rawlen - offset, 0, + // Header + kPanasonicAc32HdrMark, kPanasonicAc32HdrSpace, + // No Data + 0, 0, + 0, 0, + // Footer + kPanasonicAc32BitMark, kPanasonicAc32SectionGap, + true); + if (!used) return false; + offset += used; + } + } + } + + // Success + results->value = data; + results->decode_type = decode_type_t::PANASONIC_AC32; + results->bits = nbits; + results->address = 0; + results->command = 0; + return true; +} +#endif // DECODE_PANASONIC_AC32 + +/// 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? +IRPanasonicAc32::IRPanasonicAc32(const uint16_t pin, const bool inverted, + const bool use_modulation) + : _irsend(pin, inverted, use_modulation) { stateReset(); } + +#if SEND_PANASONIC_AC32 +/// Send the current internal state as IR messages. +/// @param[in] repeat Nr. of times the message will be repeated. +void IRPanasonicAc32::send(const uint16_t repeat) { + _irsend.sendPanasonicAC32(getRaw(), kPanasonicAc32Bits, repeat); +} +#endif // SEND_PANASONIC_AC32 + +/// Set up hardware to be able to send a message. +void IRPanasonicAc32::begin(void) { _irsend.begin(); } + +/// 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 IRPanasonicAc32::getRaw(void) const { return _.raw; } + +/// Set the internal state from a valid code for this protocol. +/// @param[in] state A valid code for this protocol. +void IRPanasonicAc32::setRaw(const uint32_t state) { _.raw = state; } + +/// Reset the state of the remote to a known good state/sequence. +void IRPanasonicAc32::stateReset(void) { setRaw(kPanasonicAc32KnownGood); } + +/// Set the Power Toggle setting of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRPanasonicAc32::setPowerToggle(const bool on) { _.PowerToggle = !on; } + +/// Get the Power Toggle setting of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRPanasonicAc32::getPowerToggle(void) const { return !_.PowerToggle; } + +/// Set the desired temperature. +/// @param[in] degrees The temperature in degrees celsius. +void IRPanasonicAc32::setTemp(const uint8_t degrees) { + uint8_t temp = std::max((uint8_t)kPanasonicAcMinTemp, degrees); + temp = std::min((uint8_t)kPanasonicAcMaxTemp, temp); + _.Temp = temp - (kPanasonicAcMinTemp - 1); +} + +/// Get the current desired temperature setting. +/// @return The current setting for temp. in degrees celsius. +uint8_t IRPanasonicAc32::getTemp(void) const { + return _.Temp + (kPanasonicAcMinTemp - 1); +} + +/// Get the operating mode setting of the A/C. +/// @return The current operating mode setting. +uint8_t IRPanasonicAc32::getMode(void) const { return _.Mode; } + +/// Set the operating mode of the A/C. +/// @param[in] mode The desired operating mode. +/// @note If we get an unexpected mode, default to AUTO. +void IRPanasonicAc32::setMode(const uint8_t mode) { + switch (mode) { + case kPanasonicAc32Auto: + case kPanasonicAc32Cool: + case kPanasonicAc32Dry: + case kPanasonicAc32Heat: + case kPanasonicAc32Fan: + _.Mode = mode; + break; + default: _.Mode = kPanasonicAc32Auto; + } +} + +/// 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 IRPanasonicAc32::convertMode(const stdAc::opmode_t mode) { + switch (mode) { + case stdAc::opmode_t::kCool: return kPanasonicAc32Cool; + case stdAc::opmode_t::kHeat: return kPanasonicAc32Heat; + case stdAc::opmode_t::kDry: return kPanasonicAc32Dry; + case stdAc::opmode_t::kFan: return kPanasonicAc32Fan; + default: return kPanasonicAc32Auto; + } +} + +/// 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 IRPanasonicAc32::toCommonMode(const uint8_t mode) { + switch (mode) { + case kPanasonicAc32Cool: return stdAc::opmode_t::kCool; + case kPanasonicAc32Heat: return stdAc::opmode_t::kHeat; + case kPanasonicAc32Dry: return stdAc::opmode_t::kDry; + case kPanasonicAc32Fan: return stdAc::opmode_t::kFan; + default: return stdAc::opmode_t::kAuto; + } +} + +/// Set the speed of the fan. +/// @param[in] speed The desired setting. +void IRPanasonicAc32::setFan(const uint8_t speed) { + switch (speed) { + case kPanasonicAc32FanMin: + case kPanasonicAc32FanLow: + case kPanasonicAc32FanMed: + case kPanasonicAc32FanHigh: + case kPanasonicAc32FanMax: + case kPanasonicAc32FanAuto: + _.Fan = speed; + break; + default: _.Fan = kPanasonicAc32FanAuto; + } +} + +/// Get the current fan speed setting. +/// @return The current fan speed. +uint8_t IRPanasonicAc32::getFan(void) const { return _.Fan; } + +/// Convert a native fan speed into its stdAc equivalent. +/// @param[in] spd The native setting to be converted. +/// @return The stdAc equivalent of the native setting. +stdAc::fanspeed_t IRPanasonicAc32::toCommonFanSpeed(const uint8_t spd) { + switch (spd) { + case kPanasonicAc32FanMax: return stdAc::fanspeed_t::kMax; + case kPanasonicAc32FanHigh: return stdAc::fanspeed_t::kHigh; + case kPanasonicAc32FanMed: return stdAc::fanspeed_t::kMedium; + case kPanasonicAc32FanLow: return stdAc::fanspeed_t::kLow; + case kPanasonicAc32FanMin: return stdAc::fanspeed_t::kMin; + default: return stdAc::fanspeed_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 IRPanasonicAc32::convertFan(const stdAc::fanspeed_t speed) { + switch (speed) { + case stdAc::fanspeed_t::kMin: return kPanasonicAc32FanMin; + case stdAc::fanspeed_t::kLow: return kPanasonicAc32FanLow; + case stdAc::fanspeed_t::kMedium: return kPanasonicAc32FanMed; + case stdAc::fanspeed_t::kHigh: return kPanasonicAc32FanHigh; + case stdAc::fanspeed_t::kMax: return kPanasonicAc32FanMax; + default: return kPanasonicAc32FanAuto; + } +} + +/// Get the current horizontal swing setting. +/// @return The current position it is set to. +bool IRPanasonicAc32::getSwingHorizontal(void) const { return _.SwingH; } + +/// Control the horizontal swing setting. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRPanasonicAc32::setSwingHorizontal(const bool on) { _.SwingH = on; } + +/// Get the current vertical swing setting. +/// @return The current position it is set to. +uint8_t IRPanasonicAc32::getSwingVertical(void) const { return _.SwingV; } + +/// Control the vertical swing setting. +/// @param[in] pos The position to set the vertical swing to. +void IRPanasonicAc32::setSwingVertical(const uint8_t pos) { + uint8_t elevation = pos; + if (elevation != kPanasonicAc32SwingVAuto) { + elevation = std::max(elevation, kPanasonicAcSwingVHighest); + elevation = std::min(elevation, kPanasonicAcSwingVLowest); + } + _.SwingV = elevation; +} + +/// 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 IRPanasonicAc32::toCommonSwingV(const uint8_t pos) { + return IRPanasonicAc::toCommonSwingV(pos); +} + +/// Convert a standard A/C vertical swing into its native setting. +/// @param[in] position A stdAc::swingv_t position to convert. +/// @return The equivalent native horizontal swing position. +uint8_t IRPanasonicAc32::convertSwingV(const stdAc::swingv_t position) { + switch (position) { + case stdAc::swingv_t::kHighest: + case stdAc::swingv_t::kHigh: + case stdAc::swingv_t::kMiddle: + case stdAc::swingv_t::kLow: + case stdAc::swingv_t::kLowest: return (uint8_t)position; + default: return kPanasonicAc32SwingVAuto; + } +} + +/// Convert the current internal state into a human readable string. +/// @return A human readable string. +String IRPanasonicAc32::toString(void) const { + String result = ""; + result.reserve(110); + result += addBoolToString(getPowerToggle(), kPowerToggleStr, false); + result += addModeToString(_.Mode, kPanasonicAc32Auto, kPanasonicAc32Cool, + kPanasonicAc32Heat, kPanasonicAc32Dry, + kPanasonicAc32Fan); + result += addTempToString(getTemp()); + result += addFanToString(_.Fan, kPanasonicAc32FanHigh, kPanasonicAc32FanLow, + kPanasonicAc32FanAuto, kPanasonicAc32FanMin, + kPanasonicAc32FanMed, kPanasonicAc32FanMax); + result += addBoolToString(_.SwingH, kSwingHStr); + result += addSwingVToString(getSwingVertical(), + kPanasonicAc32SwingVAuto, + kPanasonicAcSwingVHighest, + kPanasonicAcSwingVHigh, + kPanasonicAc32SwingVAuto, // Upper Middle unused + kPanasonicAcSwingVMiddle, + kPanasonicAc32SwingVAuto, // Lower Middle unused + kPanasonicAcSwingVLow, + kPanasonicAcSwingVLowest, + // Below are unused. + kPanasonicAc32SwingVAuto, + kPanasonicAc32SwingVAuto, + kPanasonicAc32SwingVAuto, + kPanasonicAc32SwingVAuto); + return result; +} + +/// 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 IRPanasonicAc32::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::PANASONIC_AC32; + result.model = -1; + if (getPowerToggle()) result.power = !result.power; + result.mode = toCommonMode(getMode()); + result.celsius = true; + result.degrees = getTemp(); + result.fanspeed = toCommonFanSpeed(getFan()); + result.swingv = toCommonSwingV(getSwingVertical()); + result.swingh = getSwingHorizontal() ? stdAc::swingh_t::kAuto + : stdAc::swingh_t::kOff; + // Not supported. + result.quiet = false; + result.turbo = false; + result.filter = false; + result.econo = false; + result.clean = false; + result.light = false; + result.beep = false; + result.sleep = -1; + result.clock = -1; + return result; +} diff --git a/lib/IRremoteESP8266/src/ir_Panasonic.h b/lib/IRremoteESP8266/src/ir_Panasonic.h index 9460a1b0ff..5668e4a57a 100644 --- a/lib/IRremoteESP8266/src/ir_Panasonic.h +++ b/lib/IRremoteESP8266/src/ir_Panasonic.h @@ -36,10 +36,10 @@ #ifdef ARDUINO #include #endif -#include "../src/IRremoteESP8266.h" -#include "../src/IRsend.h" +#include "IRremoteESP8266.h" +#include "IRsend.h" #ifdef UNIT_TEST -#include "../src/IRsend_test.h" +#include "IRsend_test.h" #endif // Constants diff --git a/lib/IRremoteESP8266/src/ir_Pioneer.cpp b/lib/IRremoteESP8266/src/ir_Pioneer.cpp new file mode 100644 index 0000000000..90f58c6ed9 --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_Pioneer.cpp @@ -0,0 +1,138 @@ +// 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 + +// Supports: +// Brand: Pioneer, Model: AV Receivers +// Brand: Pioneer, Model: VSX-324 AV Receiver +// Brand: Pioneer, Model: AXD7690 Remote + +#define __STDC_LIMIT_MACROS +#include +#include +#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. +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 diff --git a/lib/IRremoteESP8266/src/ir_Pronto.cpp b/lib/IRremoteESP8266/src/ir_Pronto.cpp new file mode 100644 index 0000000000..2d4ffa7592 --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_Pronto.cpp @@ -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 +#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 diff --git a/lib/IRremoteESP8266/src/ir_RC5_RC6.cpp b/lib/IRremoteESP8266/src/ir_RC5_RC6.cpp new file mode 100644 index 0000000000..1e0c75b3f1 --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_RC5_RC6.cpp @@ -0,0 +1,454 @@ +// Copyright 2009 Ken Shirriff +// Copyright 2017 David Conran + +/// @file +/// @brief RC-5 & RC-6 support +/// RC-5 & RC-6 support added from https://github.com/z3t0/Arduino-IRremote +/// RC-5X support added by David Conran +/// @see https://en.wikipedia.org/wiki/RC-5 +/// @see http://www.sbprojects.net/knowledge/ir/rc5.php +/// @see https://en.wikipedia.org/wiki/Manchester_code +/// @see https://en.wikipedia.org/wiki/RC-6 +/// @see https://www.sbprojects.net/knowledge/ir/rc6.php +/// @see http://www.pcbheaven.com/userpages/The_Philips_RC6_Protocol/ +/// @see http://www.righto.com/2010/12/64-bit-rc6-codes-arduino-and-xbox.html + +// Supports: +// Brand: Philips, Model: Standard RC-5 (RC5) +// Brand: Philips, Model: RC-5X (RC5X) +// Brand: Philips, Model: Standard RC-6 (RC6) + +#include +#include "IRrecv.h" +#include "IRsend.h" +#include "IRtimer.h" +#include "IRutils.h" + +// Constants +// RC-5/RC-5X +const uint16_t kRc5T1 = 889; +const uint32_t kRc5MinCommandLength = 113778; +const uint32_t kRc5MinGap = kRc5MinCommandLength - kRC5RawBits * (2 * kRc5T1); +const uint16_t kRc5ToggleMask = 0x800; // The 12th bit. +const uint16_t kRc5SamplesMin = 11; + +// RC-6 +const uint16_t kRc6Tick = 444; +const uint16_t kRc6HdrMarkTicks = 6; +const uint16_t kRc6HdrMark = kRc6HdrMarkTicks * kRc6Tick; +const uint16_t kRc6HdrSpaceTicks = 2; +const uint16_t kRc6HdrSpace = kRc6HdrSpaceTicks * kRc6Tick; +const uint16_t kRc6RptLengthTicks = 187; +const uint32_t kRc6RptLength = kRc6RptLengthTicks * kRc6Tick; +const uint32_t kRc6ToggleMask = 0x10000UL; // The 17th bit. +const uint16_t kRc6_36ToggleMask = 0x8000; // The 16th bit. + +// Common (getRClevel()) +const int16_t kMark = 0; +const int16_t kSpace = 1; + +#if SEND_RC5 +/// Send a Philips RC-5/RC-5X packet. +/// Status: RC-5 (stable), RC-5X (alpha) +/// @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 Caller needs to take care of flipping the toggle bit. +/// That bit differentiates between key press & key release. +/// For RC-5 it is the MSB of the data. +/// For RC-5X it is the 2nd MSB of the data. +/// @todo Testing of the RC-5X components. +void IRsend::sendRC5(const uint64_t data, uint16_t nbits, + const uint16_t repeat) { + if (nbits > sizeof(data) * 8) return; // We can't send something that big. + bool skipSpace = true; + bool field_bit = true; + // Set 36kHz IR carrier frequency & a 1/4 (25%) duty cycle. + enableIROut(36, 25); + + if (nbits >= kRC5XBits) { // Is this a RC-5X message? + // field bit is the inverted MSB of RC-5X data. + field_bit = ((data >> (nbits - 1)) ^ 1) & 1; + nbits--; + } + + IRtimer usecTimer = IRtimer(); + for (uint16_t i = 0; i <= repeat; i++) { + usecTimer.reset(); + + // Header + // First start bit (0x1). space, then mark. + if (skipSpace) + skipSpace = false; // First time through, we assume the leading space(). + else + space(kRc5T1); + mark(kRc5T1); + // Field/Second start bit. + if (field_bit) { // Send a 1. Normal for RC-5. + space(kRc5T1); + mark(kRc5T1); + } else { // Send a 0. Special case for RC-5X. Means 7th command bit is 1. + mark(kRc5T1); + space(kRc5T1); + } + + // Data + for (uint64_t mask = 1ULL << (nbits - 1); mask; mask >>= 1) + if (data & mask) { // 1 + space(kRc5T1); // 1 is space, then mark. + mark(kRc5T1); + } else { // 0 + mark(kRc5T1); // 0 is mark, then space. + space(kRc5T1); + } + // Footer + space(std::max(kRc5MinGap, kRc5MinCommandLength - usecTimer.elapsed())); + } +} + +/// Encode a Philips RC-5 data message. +/// Status: Beta / Should be working. +/// @param[in] address The 5-bit address value for the message. +/// @param[in] command The 6-bit command value for the message. +/// @param[in] key_released Indicate if the remote key has been released. +/// @return A message suitable for use in sendRC5(). +uint16_t IRsend::encodeRC5(const uint8_t address, const uint8_t command, + const bool key_released) { + return (key_released << (kRC5Bits - 1)) | ((address & 0x1f) << 6) | + (command & 0x3F); +} + +/// Encode a Philips RC-5X data message. +/// Status: Beta / Should be working. +/// @param[in] address The 5-bit address value for the message. +/// @param[in] command The 7-bit command value for the message. +/// @param[in] key_released Indicate if the remote key has been released. +/// @return A message suitable for use in sendRC5(). +uint16_t IRsend::encodeRC5X(const uint8_t address, const uint8_t command, + const bool key_released) { + // The 2nd start/field bit (MSB of the return value) is the value of the 7th + // command bit. + bool s2 = (command >> 6) & 1; + return ((uint16_t)s2 << (kRC5XBits - 1)) | + encodeRC5(address, command, key_released); +} + +/// Flip the toggle bit of a Philips RC-5/RC-5X data message. +/// Used to indicate a change of remote button's state. +/// Status: STABLE. +/// @param[in] data The existing RC-5/RC-5X message. +/// @return A data message suitable for use in sendRC5() with the toggle bit +/// flipped. +uint64_t IRsend::toggleRC5(const uint64_t data) { + return data ^ kRc5ToggleMask; +} +#endif // SEND_RC5 + +#if SEND_RC6 +/// Flip the toggle bit of a Philips RC-6 data message. +/// Used to indicate a change of remote button's state. +/// Status: STABLE / Should work fine. +/// @param[in] data The existing RC-6 message. +/// @param [in] nbits Nr. of bits in the RC-6 protocol. +/// @return A data message suitable for use in sendRC6() with the toggle bit +/// flipped. +/// @note For RC-6 (20-bits), it is the 17th least significant bit. +/// @note For RC-6 (36-bits/Xbox-360), it is the 16th least significant bit. +uint64_t IRsend::toggleRC6(const uint64_t data, const uint16_t nbits) { + if (nbits == kRC6_36Bits) return data ^ kRc6_36ToggleMask; + return data ^ kRc6ToggleMask; +} + +/// Encode a Philips RC-6 data message. +/// Status: Beta / Should be working. +/// @param[in] address The address (aka. control) value for the message. +/// Includes the field/mode/toggle bits. +/// @param[in] command The 8-bit command value for the message. +/// (aka. information) +/// @param[in] mode Which protocol to use. +/// Defined by nr. of bits in the protocol. +/// @return A data message suitable for use in `sendRC6()`. +uint64_t IRsend::encodeRC6(const uint32_t address, const uint8_t command, + const uint16_t mode) { + switch (mode) { + case kRC6Mode0Bits: + return ((address & 0xFFF) << 8) | (command & 0xFF); + case kRC6_36Bits: + return ((uint64_t)(address & 0xFFFFFFF) << 8) | (command & 0xFF); + default: + return 0; + } +} + +/// Send a Philips RC-6 packet. +/// Status: Stable. +/// @note Caller needs to take care of flipping the toggle bit (The 4th Most +/// Significant Bit). That bit differentiates between key press & key release. +/// @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::sendRC6(const uint64_t data, const uint16_t nbits, + const uint16_t repeat) { + // Check we can send the number of bits requested. + if (nbits > sizeof(data) * 8) return; + // Set 36kHz IR carrier frequency & a 1/3 (33%) duty cycle. + enableIROut(36, 33); + for (uint16_t r = 0; r <= repeat; r++) { + // Header + mark(kRc6HdrMark); + space(kRc6HdrSpace); + // Start bit. + mark(kRc6Tick); // mark, then space == 0x1. + space(kRc6Tick); + // Data + uint16_t bitTime; + for (uint64_t i = 1, mask = 1ULL << (nbits - 1); mask; i++, mask >>= 1) { + if (i == 4) // The fourth bit we send is a "double width trailer bit". + bitTime = 2 * kRc6Tick; // double-wide trailer bit + else + bitTime = kRc6Tick; // Normal bit + if (data & mask) { // 1 + mark(bitTime); + space(bitTime); + } else { // 0 + space(bitTime); + mark(bitTime); + } + } + // Footer + space(kRc6RptLength); + } +} +#endif // SEND_RC6 + +#if (DECODE_RC5 || DECODE_RC6 || DECODE_LASERTAG) +/// Gets one undecoded level at a time from the raw buffer. +/// The RC5/6 decoding is easier if the data is broken into time intervals. +/// E.g. if the buffer has MARK for 2 time intervals and SPACE for 1, +/// successive calls to getRClevel will return MARK, MARK, SPACE. +/// offset and used are updated to keep track of the current position. +/// @param[in,out] results Ptr to the data to decode and where to store the +/// decode result. +/// @param[in,out] offset Ptr to the currect offset to the rawbuf. +/// @param[in,out] used Ptr to the current used counter. +/// @param[in] bitTime Time interval of single bit in microseconds. +/// @param[in] tolerance Percent tolerance to be used in matching. +/// @param[in] excess Extra useconds to add to Marks & removed from Spaces. +/// @param[in] delta A non-scaling (+/-) error margin (in useconds). +/// @param[in] maxwidth Maximum number of successive levels to find in a single +/// level (default is 3) +/// @return MARK, SPACE, or -1 for error. +/// (The measured time interval is not a multiple of t1.) +/// @see https://en.wikipedia.org/wiki/Manchester_code +int16_t IRrecv::getRClevel(decode_results *results, uint16_t *offset, + uint16_t *used, const uint16_t bitTime, + const uint8_t tolerance, const int16_t excess, + const uint16_t delta, const uint8_t maxwidth) { + DPRINT("DEBUG: getRClevel: offset = "); + DPRINTLN(uint64ToString(*offset)); + DPRINT("DEBUG: getRClevel: rawlen = "); + DPRINTLN(uint64ToString(results->rawlen)); + if (*offset >= results->rawlen) { + DPRINTLN("DEBUG: getRClevel: SPACE, past end of rawbuf"); + return kSpace; // After end of recorded buffer, assume SPACE. + } + uint16_t width = results->rawbuf[*offset]; + // If the value of offset is odd, it's a MARK. Even, it's a SPACE. + uint16_t val = ((*offset) % 2) ? kMark : kSpace; + // Check to see if we have hit an inter-message gap (> 20ms). + if (val == kSpace && + (width > 20000 - delta || width > maxwidth * bitTime + delta)) { + DPRINTLN("DEBUG: getRClevel: SPACE, hit end of mesg gap."); + return kSpace; + } + int16_t correction = (val == kMark) ? excess : -excess; + + // Calculate the look-ahead for our current position in the buffer. + uint16_t avail; + // Note: We want to match in greedy order as the other way leads to + // mismatches due to overlaps induced by the correction and tolerance + // values. + for (avail = maxwidth; avail > 0; avail--) { + if (match(width, avail * bitTime + correction, tolerance, delta)) { + break; + } + } + if (!avail) { + DPRINTLN("DEBUG: getRClevel: Unexpected width. Exiting."); + return -1; // The width is not what we expected. + } + + (*used)++; // Count another one of the avail slots as used. + if (*used >= avail) { // Are we out of look-ahead/avail slots? + // Yes, so reset the used counter, and move the offset ahead. + *used = 0; + (*offset)++; + } + if (val == kMark) { + DPRINTLN("DEBUG: getRClevel: MARK"); + } else { + DPRINTLN("DEBUG: getRClevel: SPACE"); + } + + return val; +} +#endif // (DECODE_RC5 || DECODE_RC6 || DECODE_LASERTAG) + +#if DECODE_RC5 +/// Decode the supplied RC-5/RC5X message. +/// Status: RC-5 (stable), RC-5X (alpha) +/// @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 The 'toggle' bit is included as the 6th (MSB) address bit, the MSB of +/// data, & in the count of bits decoded. +/// @todo Serious testing of the RC-5X and strict aspects needs to be done. +bool IRrecv::decodeRC5(decode_results *results, uint16_t offset, + const uint16_t nbits, const bool strict) { + if (results->rawlen <= kRc5SamplesMin + kHeader - 1 + offset) return false; + + // Compliance + if (strict && nbits != kRC5Bits && nbits != kRC5XBits) + return false; // It's neither RC-5 or RC-5X. + + uint16_t used = 0; + bool is_rc5x = false; + uint64_t data = 0; + + // Header + // Get start bit #1. + if (getRClevel(results, &offset, &used, kRc5T1) != kMark) return false; + // Get field/start bit #2 (inverted bit-7 of the command if RC-5X protocol) + uint16_t actual_bits = 1; + int16_t levelA = getRClevel(results, &offset, &used, kRc5T1); + int16_t levelB = getRClevel(results, &offset, &used, kRc5T1); + if (levelA == kSpace && levelB == kMark) { // Matched a 1. + is_rc5x = false; + } else if (levelA == kMark && levelB == kSpace) { // Matched a 0. + if (nbits <= kRC5Bits) return false; // Field bit must be '1' for RC5. + is_rc5x = true; + data = 1; + } else { + return false; // Not what we expected. + } + + // Data + for (; offset < results->rawlen; actual_bits++) { + int16_t levelA = getRClevel(results, &offset, &used, kRc5T1); + int16_t levelB = getRClevel(results, &offset, &used, kRc5T1); + 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 != kRC5Bits && actual_bits != kRC5XBits) + return false; + + // Success + results->value = data; + results->address = (data >> 6) & 0x1F; + results->command = data & 0x3F; + results->repeat = false; + if (is_rc5x) { + results->decode_type = RC5X; + results->command |= ((uint32_t)is_rc5x) << 6; + } else { + results->decode_type = RC5; + actual_bits--; // RC5 doesn't count the field bit as data. + } + results->bits = actual_bits; + return true; +} +#endif // DECODE_RC5 + +#if DECODE_RC6 +/// Decode the supplied RC6 message. +/// Status: Stable. +/// @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. +/// @todo Testing of the strict compliance aspects. +bool IRrecv::decodeRC6(decode_results *results, uint16_t offset, + const uint16_t nbits, const bool strict) { + if (results->rawlen <= kHeader + 2 + 4 + offset) + // Up to the double-wide T bit. + return false; // Smaller than absolute smallest possible RC6 message. + + if (strict) { // Compliance + // Unlike typical protocols, the ability to have mark+space, and space+mark + // as data bits means it is possible to only have nbits of entries for the + // data portion, rather than the typically required 2 * nbits. + // Also due to potential melding with the start bit, we can only count + // the start bit as 1, instead of a more typical 2 value. The header still + // remains as normal. + if (results->rawlen <= nbits + kHeader + 1 + offset) + return false; // Don't have enough entries/samples to be valid. + switch (nbits) { + case kRC6Mode0Bits: + case kRC6_36Bits: + break; + default: + return false; // Asking for the wrong number of bits. + } + } + + // Header + if (!matchMark(results->rawbuf[offset], kRc6HdrMark)) return false; + // Calculate how long the common tick time is based on the header mark. + uint32_t tick = results->rawbuf[offset++] * kRawTick / kRc6HdrMarkTicks; + if (!matchSpace(results->rawbuf[offset++], kRc6HdrSpaceTicks * tick)) + return false; + + uint16_t used = 0; + + // Get the start bit. e.g. 1. + if (getRClevel(results, &offset, &used, tick) != kMark) return false; + if (getRClevel(results, &offset, &used, tick) != kSpace) return false; + + uint16_t actual_bits; + uint64_t data = 0; + + // Data (Warning: Here be dragons^Wpointers!!) + for (actual_bits = 0; offset < results->rawlen; actual_bits++) { + int16_t levelA, levelB; // Next two levels + levelA = getRClevel(results, &offset, &used, tick); + // T bit is double wide; make sure second half matches + if (actual_bits == 3 && levelA != getRClevel(results, &offset, &used, tick)) + return false; + levelB = getRClevel(results, &offset, &used, tick); + // T bit is double wide; make sure second half matches + if (actual_bits == 3 && levelB != getRClevel(results, &offset, &used, tick)) + return false; + if (levelA == kMark && levelB == kSpace) // reversed compared to RC5 + data = (data << 1) | 1; // 1 + else if (levelA == kSpace && levelB == kMark) + data <<= 1; // 0 + else + break; + } + + // More compliance + if (strict && actual_bits != nbits) + return false; // Actual nr. of bits didn't match expected. + + // Success + results->decode_type = RC6; + results->bits = actual_bits; + results->value = data; + results->address = data >> 8; + results->command = data & 0xFF; + return true; +} +#endif // DECODE_RC6 diff --git a/lib/IRremoteESP8266/src/ir_RCMM.cpp b/lib/IRremoteESP8266/src/ir_RCMM.cpp new file mode 100644 index 0000000000..97a3c79b5b --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_RCMM.cpp @@ -0,0 +1,164 @@ +// Copyright 2017 David Conran + +/// @file +/// @brief Support for the Phillips RC-MM protocol. +/// @see http://www.sbprojects.net/knowledge/ir/rcmm.php + +// Supports: +// Brand: Microsoft, Model: XBOX 360 + +#include +#include "IRrecv.h" +#include "IRsend.h" +#include "IRtimer.h" +#include "IRutils.h" + +// Constants +const uint16_t kRcmmTick = 28; // Technically it would be 27.777* +const uint16_t kRcmmHdrMarkTicks = 15; +const uint16_t kRcmmHdrMark = 416; +const uint16_t kRcmmHdrSpaceTicks = 10; +const uint16_t kRcmmHdrSpace = 277; +const uint16_t kRcmmBitMarkTicks = 6; +const uint16_t kRcmmBitMark = 166; +const uint16_t kRcmmBitSpace0Ticks = 10; +const uint16_t kRcmmBitSpace0 = 277; +const uint16_t kRcmmBitSpace1Ticks = 16; +const uint16_t kRcmmBitSpace1 = 444; +const uint16_t kRcmmBitSpace2Ticks = 22; +const uint16_t kRcmmBitSpace2 = 611; +const uint16_t kRcmmBitSpace3Ticks = 28; +const uint16_t kRcmmBitSpace3 = 777; +const uint16_t kRcmmRptLengthTicks = 992; +const uint32_t kRcmmRptLength = 27778; +const uint16_t kRcmmMinGapTicks = 120; +const uint32_t kRcmmMinGap = 3360; +// Use a tolerance of +/-10% when matching some data spaces. +const uint8_t kRcmmTolerance = 10; +const uint16_t kRcmmExcess = 50; + +#if SEND_RCMM +/// Send a Philips RC-MM packet. +/// 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. +void IRsend::sendRCMM(uint64_t data, uint16_t nbits, uint16_t repeat) { + // Set 36kHz IR carrier frequency & a 1/3 (33%) duty cycle. + enableIROut(36, 33); + IRtimer usecs = IRtimer(); + + for (uint16_t r = 0; r <= repeat; r++) { + usecs.reset(); + // Header + mark(kRcmmHdrMark); + space(kRcmmHdrSpace); + // Data + uint64_t mask = 0b11ULL << (nbits - 2); + // RC-MM sends data 2 bits at a time. + for (int32_t i = nbits; i > 0; i -= 2) { + mark(kRcmmBitMark); + // Grab the next Most Significant Bits to send. + switch ((data & mask) >> (i - 2)) { + case 0b00: + space(kRcmmBitSpace0); + break; + case 0b01: + space(kRcmmBitSpace1); + break; + case 0b10: + space(kRcmmBitSpace2); + break; + case 0b11: + space(kRcmmBitSpace3); + break; + } + mask >>= 2; + } + // Footer + mark(kRcmmBitMark); + // Protocol requires us to wait at least kRcmmRptLength usecs from the + // start or kRcmmMinGap usecs. + space(std::max(kRcmmRptLength - usecs.elapsed(), kRcmmMinGap)); + } +} +#endif // SEND_RCMM + +#if DECODE_RCMM +/// Decode a Philips RC-MM packet (between 12 & 32 bits) if possible. +/// Status: STABLE / Should 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::decodeRCMM(decode_results *results, uint16_t offset, + const uint16_t nbits, const bool strict) { + uint64_t data = 0; + + if (results->rawlen <= 4 + offset - 1) + return false; // Not enough entries to ever be RCMM. + + // Calc the maximum size in bits, the message can be, or that we can accept. + int16_t maxBitSize = + std::min((uint16_t)results->rawlen - 5, (uint16_t)sizeof(data) * 8); + // Compliance + if (strict) { + // Technically the spec says bit sizes should be 12 xor 24. however + // 32 bits has been seen from a device. We are going to assume + // 12 <= bits <= 32 is the 'required' bit length for the spec. + if (maxBitSize < 12 || maxBitSize > 32) return false; + if (maxBitSize < nbits) + return false; // Short cut, we can never reach the expected nr. of bits. + } + // Header decode + if (!matchMark(results->rawbuf[offset], kRcmmHdrMark)) return false; + // Calculate how long the common tick time is based on the header mark. + uint32_t m_tick = results->rawbuf[offset++] * kRawTick / kRcmmHdrMarkTicks; + if (!matchSpace(results->rawbuf[offset], kRcmmHdrSpace)) return false; + // Calculate how long the common tick time is based on the header space. + uint32_t s_tick = results->rawbuf[offset++] * kRawTick / kRcmmHdrSpaceTicks; + + // Data decode + // RC-MM has two bits of data per mark/space pair. + uint16_t actualBits; + for (actualBits = 0; actualBits < maxBitSize; actualBits += 2, offset++) { + if (!match(results->rawbuf[offset++], kRcmmBitMarkTicks * m_tick)) + return false; + + data <<= 2; + // Use non-default tolerance & excess for matching some of the spaces as the + // defaults are too generous and causes mis-matches in some cases. + if (match(results->rawbuf[offset], kRcmmBitSpace0Ticks * s_tick)) + data += 0; + else if (match(results->rawbuf[offset], kRcmmBitSpace1Ticks * s_tick)) + data += 1; + else if (match(results->rawbuf[offset], kRcmmBitSpace2Ticks * s_tick, + kRcmmTolerance)) + data += 2; + else if (match(results->rawbuf[offset], kRcmmBitSpace3Ticks * s_tick, + kRcmmTolerance)) + data += 3; + else + return false; + } + // Footer decode + if (!match(results->rawbuf[offset++], kRcmmBitMarkTicks * m_tick)) + return false; + if (offset < results->rawlen && + !matchAtLeast(results->rawbuf[offset], kRcmmMinGapTicks * s_tick)) + return false; + + // Compliance + if (strict && actualBits != nbits) return false; + + // Success + results->value = data; + results->decode_type = RCMM; + results->bits = actualBits; + results->address = 0; + results->command = 0; + return true; +} +#endif // DECODE_RCMM diff --git a/lib/IRremoteESP8266/src/ir_Rhoss.cpp b/lib/IRremoteESP8266/src/ir_Rhoss.cpp new file mode 100644 index 0000000000..f906f49be4 --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_Rhoss.cpp @@ -0,0 +1,364 @@ +// Copyright 2021 Tom Rosenback + +/// @file +/// @brief Support for Rhoss protocols. + +#include "ir_Rhoss.h" +#include +#include +#include "IRrecv.h" +#include "IRsend.h" +#include "IRtext.h" +#include "IRutils.h" + +const uint16_t kRhossHdrMark = 3042; +const uint16_t kRhossHdrSpace = 4248; +const uint16_t kRhossBitMark = 648; +const uint16_t kRhossOneSpace = 1545; +const uint16_t kRhossZeroSpace = 457; +const uint32_t kRhossGap = kDefaultMessageGap; +const uint16_t kRhossFreq = 38; + +using irutils::addBoolToString; +using irutils::addModeToString; +using irutils::addFanToString; +using irutils::addTempToString; + +#if SEND_RHOSS +/// Send a Rhoss 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::sendRhoss(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 < kRhossStateLength) return; + + // We always send a message, even for repeat=0, hence '<= repeat'. + for (uint16_t r = 0; r <= repeat; r++) { + sendGeneric(kRhossHdrMark, kRhossHdrSpace, kRhossBitMark, + kRhossOneSpace, kRhossBitMark, kRhossZeroSpace, + kRhossBitMark, kRhossZeroSpace, + data, nbytes, kRhossFreq, false, 0, kDutyDefault); + mark(kRhossBitMark); + // Gap + space(kRhossGap); + } +} +#endif // SEND_RHOSS + +#if DECODE_RHOSS +/// Decode the supplied Rhoss 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::decodeRhoss(decode_results *results, uint16_t offset, + const uint16_t nbits, const bool strict) { + if (strict && nbits != kRhossBits) return false; + + if (results->rawlen <= 2 * nbits + kHeader + kFooter - 1 + offset) { + return false; // Can't possibly be a valid Rhoss message. + } + + uint16_t used; + // Header + Data Block (96 bits) + Footer + used = matchGeneric(results->rawbuf + offset, results->state, + results->rawlen - offset, kRhossBits, + kRhossHdrMark, kRhossHdrSpace, + kRhossBitMark, kRhossOneSpace, + kRhossBitMark, kRhossZeroSpace, + kRhossBitMark, kRhossZeroSpace, + false, kUseDefTol, kMarkExcess, false); + + if (!used) return false; + offset += used; + + // Footer (Part 2) + if (!matchMark(results->rawbuf[offset++], kRhossBitMark)) { + return false; + } + + if (offset < results->rawlen && + !matchAtLeast(results->rawbuf[offset], kRhossGap)) { + return false; + } + + if (strict && !IRRhossAc::validChecksum(results->state)) return false; + + // Success + results->decode_type = decode_type_t::RHOSS; + 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_RHOSS + +/// 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? +IRRhossAc::IRRhossAc(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 IRRhossAc::begin(void) { _irsend.begin(); } + +#if SEND_RHOSS +/// Send the current internal state as an IR message. +/// @param[in] repeat Nr. of times the message will be repeated. +void IRRhossAc::send(const uint16_t repeat) { + _irsend.sendRhoss(getRaw(), kRhossStateLength, repeat); +} +#endif // SEND_RHOSS + +/// 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 IRRhossAc::calcChecksum(const uint8_t state[], const uint16_t length) { + 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 size of the state. +/// @return A boolean indicating if it's checksum is valid. +bool IRRhossAc::validChecksum(const uint8_t state[], const uint16_t length) { + return (state[length - 1] == IRRhossAc::calcChecksum(state, length)); +} + +/// Update the checksum value for the internal state. +void IRRhossAc::checksum(void) { + _.Sum = IRRhossAc::calcChecksum(_.raw, kRhossStateLength); + _.raw[kRhossStateLength - 1] = _.Sum; +} + +/// Reset the internals of the object to a known good state. +void IRRhossAc::stateReset(void) { + for (uint8_t i = 1; i < kRhossStateLength; i++) _.raw[i] = 0x0; + _.raw[0] = 0xAA; + _.raw[2] = 0x60; + _.raw[6] = 0x54; + _.Power = kRhossDefaultPower; + _.Fan = kRhossDefaultFan; + _.Mode = kRhossDefaultMode; + _.Swing = kRhossDefaultSwing; + _.Temp = kRhossDefaultTemp - kRhossTempMin; +} + +/// 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* IRRhossAc::getRaw(void) { + checksum(); // Ensure correct bit array before returning + return _.raw; +} + +/// Set the raw state of the object. +/// @param[state] state The raw state from the native IR message. +void IRRhossAc::setRaw(const uint8_t state[]) { + std::memcpy(_.raw, state, kRhossStateLength); +} + +/// Set the internal state to have the power on. +void IRRhossAc::on(void) { setPower(true); } + +/// Set the internal state to have the power off. +void IRRhossAc::off(void) { setPower(false); } + +/// Set the internal state to have the desired power. +/// @param[in] on The desired power state. +void IRRhossAc::setPower(const bool on) { + _.Power = (on ? kRhossPowerOn : kRhossPowerOff); +} + +/// Get the power setting from the internal state. +/// @return A boolean indicating the power setting. +bool IRRhossAc::getPower(void) const { + return _.Power == kRhossPowerOn; +} + +/// Set the temperature. +/// @param[in] degrees The temperature in degrees celsius. +void IRRhossAc::setTemp(const uint8_t degrees) { + uint8_t temp = std::max(kRhossTempMin, degrees); + _.Temp = std::min(kRhossTempMax, temp) - kRhossTempMin; +} + +/// Get the current temperature setting. +/// @return Get current setting for temp. in degrees celsius. +uint8_t IRRhossAc::getTemp(void) const { + return _.Temp + kRhossTempMin; +} + +/// Set the speed of the fan. +/// @param[in] speed The desired setting. +void IRRhossAc::setFan(const uint8_t speed) { + switch (speed) { + case kRhossFanAuto: + case kRhossFanMin: + case kRhossFanMed: + case kRhossFanMax: + _.Fan = speed; + break; + default: + _.Fan = kRhossFanAuto; + } +} + +/// Get the current fan speed setting. +/// @return The current fan speed. +uint8_t IRRhossAc::getFan(void) const { + return _.Fan; +} + +/// Set the Vertical Swing mode of the A/C. +/// @param[in] state true, the Swing is on. false, the Swing is off. +void IRRhossAc::setSwing(const bool state) { + _.Swing = state; +} + +/// Get the Vertical Swing speed of the A/C. +/// @return The native swing speed setting. +uint8_t IRRhossAc::getSwing(void) const { + return _.Swing; +} + +/// Get the current operation mode setting. +/// @return The current operation mode. +uint8_t IRRhossAc::getMode(void) const { + return _.Mode; +} + +/// Set the desired operation mode. +/// @param[in] mode The desired operation mode. +void IRRhossAc::setMode(const uint8_t mode) { + switch (mode) { + case kRhossModeFan: + case kRhossModeCool: + case kRhossModeDry: + case kRhossModeHeat: + case kRhossModeAuto: + _.Mode = mode; + return; + default: + _.Mode = kRhossDefaultMode; + 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 IRRhossAc::convertMode(const stdAc::opmode_t mode) { + switch (mode) { + case stdAc::opmode_t::kCool: + return kRhossModeCool; + case stdAc::opmode_t::kHeat: + return kRhossModeHeat; + case stdAc::opmode_t::kDry: + return kRhossModeDry; + case stdAc::opmode_t::kFan: + return kRhossModeFan; + case stdAc::opmode_t::kAuto: + return kRhossModeAuto; + default: + return kRhossDefaultMode; + } +} + +/// 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 IRRhossAc::convertFan(const stdAc::fanspeed_t speed) { + switch (speed) { + case stdAc::fanspeed_t::kMin: + case stdAc::fanspeed_t::kLow: + return kRhossFanMin; + case stdAc::fanspeed_t::kMedium: + return kRhossFanMed; + case stdAc::fanspeed_t::kHigh: + case stdAc::fanspeed_t::kMax: + return kRhossFanMax; + default: + return kRhossDefaultFan; + } +} + +/// 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 IRRhossAc::toCommonMode(const uint8_t mode) { + switch (mode) { + case kRhossModeCool: return stdAc::opmode_t::kCool; + case kRhossModeHeat: return stdAc::opmode_t::kHeat; + case kRhossModeDry: return stdAc::opmode_t::kDry; + case kRhossModeFan: return stdAc::opmode_t::kFan; + case kRhossModeAuto: return stdAc::opmode_t::kAuto; + 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 IRRhossAc::toCommonFanSpeed(const uint8_t speed) { + switch (speed) { + case kRhossFanMax: return stdAc::fanspeed_t::kMax; + case kRhossFanMed: return stdAc::fanspeed_t::kMedium; + case kRhossFanMin: return stdAc::fanspeed_t::kMin; + case kRhossFanAuto: + 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 IRRhossAc::toCommon(void) const { + stdAc::state_t result; + result.protocol = decode_type_t::RHOSS; + result.power = getPower(); + result.mode = toCommonMode(_.Mode); + result.celsius = true; + result.degrees = _.Temp; + result.fanspeed = toCommonFanSpeed(_.Fan); + result.swingv = _.Swing ? stdAc::swingv_t::kAuto : stdAc::swingv_t::kOff; + // Not supported. + result.model = -1; + result.turbo = false; + 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 IRRhossAc::toString(void) const { + String result = ""; + result.reserve(70); // Reserve some heap for the string to reduce fragging. + result += addBoolToString(getPower(), kPowerStr, false); + result += addModeToString(getMode(), kRhossModeAuto, kRhossModeCool, + kRhossModeHeat, kRhossModeDry, kRhossModeFan); + result += addTempToString(getTemp()); + result += addFanToString(getFan(), kRhossFanMax, kRhossFanMin, + kRhossFanAuto, kRhossFanAuto, + kRhossFanMed); + result += addBoolToString(getSwing(), kSwingVStr); + return result; +} diff --git a/lib/IRremoteESP8266/src/ir_Rhoss.h b/lib/IRremoteESP8266/src/ir_Rhoss.h index e3a70aa431..8f66ce7384 100644 --- a/lib/IRremoteESP8266/src/ir_Rhoss.h +++ b/lib/IRremoteESP8266/src/ir_Rhoss.h @@ -13,10 +13,10 @@ #ifndef UNIT_TEST #include #endif -#include "../src/IRremoteESP8266.h" -#include "../src/IRsend.h" +#include "IRremoteESP8266.h" +#include "IRsend.h" #ifdef UNIT_TEST -#include "../src/IRsend_test.h" +#include "IRsend_test.h" #endif diff --git a/lib/IRremoteESP8266/src/ir_Samsung.h b/lib/IRremoteESP8266/src/ir_Samsung.h index 5321deae82..acabb36481 100644 --- a/lib/IRremoteESP8266/src/ir_Samsung.h +++ b/lib/IRremoteESP8266/src/ir_Samsung.h @@ -35,10 +35,10 @@ #ifndef UNIT_TEST #include #endif -#include "../src/IRremoteESP8266.h" -#include "../src/IRsend.h" +#include "IRremoteESP8266.h" +#include "IRsend.h" #ifdef UNIT_TEST -#include "../src/IRsend_test.h" +#include "IRsend_test.h" #endif /// Native representation of a Samsung A/C message. diff --git a/lib/IRremoteESP8266/src/ir_Sanyo.cpp b/lib/IRremoteESP8266/src/ir_Sanyo.cpp new file mode 100644 index 0000000000..7dbed5a582 --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_Sanyo.cpp @@ -0,0 +1,978 @@ +// Copyright 2009 Ken Shirriff +// Copyright 2016 marcosamarinho +// Copyright 2017-2021 David Conran + +/// @file +/// @brief Support for Sanyo protocols. +/// Sanyo LC7461 support originally by marcosamarinho +/// Sanyo SA 8650B originally added from +/// https://github.com/shirriff/Arduino-IRremote/ +/// @see https://github.com/z3t0/Arduino-IRremote/blob/master/ir_Sanyo.cpp +/// @see http://pdf.datasheetcatalog.com/datasheet/sanyo/LC7461.pdf +/// @see https://github.com/marcosamarinho/IRremoteESP8266/blob/master/ir_Sanyo.cpp +/// @see http://slydiman.narod.ru/scr/kb/sanyo.htm +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1211 +/// @see https://docs.google.com/spreadsheets/d/1dYfLsnYvpjV-SgO8pdinpfuBIpSzm8Q1R5SabrLeskw/edit?usp=sharing +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1503 + +#include "ir_Sanyo.h" +#include +#include +#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::addSwingVToString; +using irutils::addTempToString; +using irutils::minsToString; +using irutils::sumNibbles; + +// Constants +// Sanyo SA 8650B +const uint16_t kSanyoSa8650bHdrMark = 3500; // seen range 3500 +const uint16_t kSanyoSa8650bHdrSpace = 950; // seen 950 +const uint16_t kSanyoSa8650bOneMark = 2400; // seen 2400 +const uint16_t kSanyoSa8650bZeroMark = 700; // seen 700 +// usually see 713 - not using ticks as get number wrapround +const uint16_t kSanyoSa8650bDoubleSpaceUsecs = 800; +const uint16_t kSanyoSa8650bRptLength = 45000; + +// Sanyo LC7461 +const uint16_t kSanyoLc7461AddressMask = (1 << kSanyoLC7461AddressBits) - 1; +const uint16_t kSanyoLc7461CommandMask = (1 << kSanyoLC7461CommandBits) - 1; +const uint16_t kSanyoLc7461HdrMark = 9000; +const uint16_t kSanyoLc7461HdrSpace = 4500; +const uint16_t kSanyoLc7461BitMark = 560; // 1T +const uint16_t kSanyoLc7461OneSpace = 1690; // 3T +const uint16_t kSanyoLc7461ZeroSpace = 560; // 1T +const uint32_t kSanyoLc7461MinCommandLength = 108000; + +const uint16_t kSanyoLc7461MinGap = + kSanyoLc7461MinCommandLength - + (kSanyoLc7461HdrMark + kSanyoLc7461HdrSpace + + kSanyoLC7461Bits * (kSanyoLc7461BitMark + + (kSanyoLc7461OneSpace + kSanyoLc7461ZeroSpace) / 2) + + kSanyoLc7461BitMark); + +const uint16_t kSanyoAcHdrMark = 8500; ///< uSeconds +const uint16_t kSanyoAcHdrSpace = 4200; ///< uSeconds +const uint16_t kSanyoAcBitMark = 500; ///< uSeconds +const uint16_t kSanyoAcOneSpace = 1600; ///< uSeconds +const uint16_t kSanyoAcZeroSpace = 550; ///< uSeconds +const uint32_t kSanyoAcGap = kDefaultMessageGap; ///< uSeconds (Guess only) +const uint16_t kSanyoAcFreq = 38000; ///< Hz. (Guess only) + +const uint16_t kSanyoAc88HdrMark = 5400; ///< uSeconds +const uint16_t kSanyoAc88HdrSpace = 2000; ///< uSeconds +const uint16_t kSanyoAc88BitMark = 500; ///< uSeconds +const uint16_t kSanyoAc88OneSpace = 1500; ///< uSeconds +const uint16_t kSanyoAc88ZeroSpace = 750; ///< uSeconds +const uint32_t kSanyoAc88Gap = 3675; ///< uSeconds +const uint16_t kSanyoAc88Freq = 38000; ///< Hz. (Guess only) +const uint8_t kSanyoAc88ExtraTolerance = 5; /// (%) Extra tolerance to use. + +#if SEND_SANYO +/// Construct a Sanyo LC7461 message. +/// @param[in] address The 13 bit value of the address(Custom) portion of the +/// protocol. +/// @param[in] command The 8 bit value of the command(Key) portion of the +/// protocol. +/// @return An uint64_t with the encoded raw 42 bit Sanyo LC7461 data value. +/// @note This protocol uses the NEC protocol timings. However, data is +/// formatted as : address(13 bits), !address, command(8 bits), !command. +/// According with LIRC, this protocol is used on Sanyo, Aiwa and Chinon +uint64_t IRsend::encodeSanyoLC7461(uint16_t address, uint8_t command) { + // Mask our input values to ensure the correct bit sizes. + address &= kSanyoLc7461AddressMask; + command &= kSanyoLc7461CommandMask; + + uint64_t data = address; + address ^= kSanyoLc7461AddressMask; // Invert the 13 LSBs. + // Append the now inverted address. + data = (data << kSanyoLC7461AddressBits) | address; + // Append the command. + data = (data << kSanyoLC7461CommandBits) | command; + command ^= kSanyoLc7461CommandMask; // Invert the command. + // Append the now inverted command. + data = (data << kSanyoLC7461CommandBits) | command; + + return data; +} + +/// Send a Sanyo LC7461 message. +/// Status: BETA / Probably works. +/// @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 Based on \@marcosamarinho's work. +/// This protocol uses the NEC protocol timings. However, data is +/// formatted as : address(13 bits), !address, command (8 bits), !command. +/// According with LIRC, this protocol is used on Sanyo, Aiwa and Chinon +/// Information for this protocol is available at the Sanyo LC7461 datasheet. +/// Repeats are performed similar to the NEC method of sending a special +/// repeat message, rather than duplicating the entire message. +/// @see https://github.com/marcosamarinho/IRremoteESP8266/blob/master/ir_Sanyo.cpp +/// @see http://pdf.datasheetcatalog.com/datasheet/sanyo/LC7461.pdf +void IRsend::sendSanyoLC7461(const uint64_t data, const uint16_t nbits, + const uint16_t repeat) { + // This protocol appears to be another 42-bit variant of the NEC protocol. + sendNEC(data, nbits, repeat); +} +#endif // SEND_SANYO + +#if DECODE_SANYO +/// Decode the supplied SANYO LC7461 message. +/// Status: BETA / Probably works. +/// @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 Based on \@marcosamarinho's work. +/// This protocol uses the NEC protocol. However, data is +/// formatted as : address(13 bits), !address, command (8 bits), !command. +/// According with LIRC, this protocol is used on Sanyo, Aiwa and Chinon +/// Information for this protocol is available at the Sanyo LC7461 datasheet. +/// @see http://slydiman.narod.ru/scr/kb/sanyo.htm +/// @see https://github.com/marcosamarinho/IRremoteESP8266/blob/master/ir_Sanyo.cpp +/// @see http://pdf.datasheetcatalog.com/datasheet/sanyo/LC7461.pdf +bool IRrecv::decodeSanyoLC7461(decode_results *results, uint16_t offset, + const uint16_t nbits, const bool strict) { + if (strict && nbits != kSanyoLC7461Bits) + return false; // Not strictly in spec. + // This protocol is basically a 42-bit variant of the NEC protocol. + if (!decodeNEC(results, offset, nbits, false)) + return false; // Didn't match a NEC format (without strict) + + // Bits 30 to 42+. + uint16_t address = + results->value >> (kSanyoLC7461Bits - kSanyoLC7461AddressBits); + // Bits 9 to 16. + uint8_t command = + (results->value >> kSanyoLC7461CommandBits) & kSanyoLc7461CommandMask; + // Compliance + if (strict) { + if (results->bits != nbits) return false; + // Bits 17 to 29. + uint16_t inverted_address = + (results->value >> (kSanyoLC7461CommandBits * 2)) & + kSanyoLc7461AddressMask; + // Bits 1-8. + uint8_t inverted_command = results->value & kSanyoLc7461CommandMask; + if ((address ^ kSanyoLc7461AddressMask) != inverted_address) + return false; // Address integrity check failed. + if ((command ^ kSanyoLc7461CommandMask) != inverted_command) + return false; // Command integrity check failed. + } + + // Success + results->decode_type = SANYO_LC7461; + results->address = address; + results->command = command; + return true; +} + +/* NOTE: Disabled due to poor quality. +/// Decode the supplied Sanyo SA 8650B message. +/// Status: Depricated. +/// @depricated Disabled due to poor quality. +/// @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. +/// @warning This decoder looks like rubbish. Only keeping it for compatibility +/// with the Arduino IRremote library. Seriously, don't trust it. +/// If someone has a device that this is supposed to be for, please log an +/// Issue on github with a rawData dump please. We should probably remove it. +/// We think this is a Sanyo decoder - serial = SA 8650B +/// @see https://github.com/z3t0/Arduino-IRremote/blob/master/ir_Sanyo.cpp +bool IRrecv::decodeSanyo(decode_results *results, uint16_t nbits, bool strict) { + if (results->rawlen < 2 * nbits + kHeader - 1) + return false; // Shorter than shortest possible. + if (strict && nbits != kSanyoSA8650BBits) + return false; // Doesn't match the spec. + + uint16_t offset = 0; + + // TODO(crankyoldgit): This repeat code looks like garbage, it should never + // match or if it does, it won't be reliable. We should probably just + // remove it. + if (results->rawbuf[offset++] < kSanyoSa8650bDoubleSpaceUsecs) { + results->bits = 0; + results->value = kRepeat; + results->decode_type = SANYO; + results->address = 0; + results->command = 0; + results->repeat = true; + return true; + } + + // Header + if (!matchMark(results->rawbuf[offset++], kSanyoSa8650bHdrMark)) + return false; + // NOTE: These next two lines look very wrong. Treat as suspect. + if (!matchMark(results->rawbuf[offset++], kSanyoSa8650bHdrMark)) + return false; + // Data + uint64_t data = 0; + while (offset + 1 < results->rawlen) { + if (!matchSpace(results->rawbuf[offset], kSanyoSa8650bHdrSpace)) + break; + offset++; + if (matchMark(results->rawbuf[offset], kSanyoSa8650bOneMark)) + data = (data << 1) | 1; // 1 + else if (matchMark(results->rawbuf[offset], kSanyoSa8650bZeroMark)) + data <<= 1; // 0 + else + return false; + offset++; + } + + if (strict && kSanyoSA8650BBits > (offset - 1U) / 2U) + return false; + + // Success + results->bits = (offset - 1) / 2; + results->decode_type = SANYO; + results->value = data; + results->address = 0; + results->command = 0; + return true; +} +*/ +#endif // DECODE_SANYO + + +#if SEND_SANYO_AC +/// Send a SanyoAc 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. +/// @param[in] repeat Nr. of times the message is to be repeated. +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1211 +void IRsend::sendSanyoAc(const uint8_t data[], const uint16_t nbytes, + const uint16_t repeat) { + // Header + Data + Footer + sendGeneric(kSanyoAcHdrMark, kSanyoAcHdrSpace, + kSanyoAcBitMark, kSanyoAcOneSpace, + kSanyoAcBitMark, kSanyoAcZeroSpace, + kSanyoAcBitMark, kSanyoAcGap, + data, nbytes, kSanyoAcFreq, false, repeat, kDutyDefault); +} +#endif // SEND_SANYO_AC + +#if DECODE_SANYO_AC +/// Decode the supplied SanyoAc 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. +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1211 +bool IRrecv::decodeSanyoAc(decode_results *results, uint16_t offset, + const uint16_t nbits, const bool strict) { + if (strict && nbits != kSanyoAcBits) + return false; + + // Header + Data + Footer + if (!matchGeneric(results->rawbuf + offset, results->state, + results->rawlen - offset, nbits, + kSanyoAcHdrMark, kSanyoAcHdrSpace, + kSanyoAcBitMark, kSanyoAcOneSpace, + kSanyoAcBitMark, kSanyoAcZeroSpace, + kSanyoAcBitMark, kSanyoAcGap, + true, kUseDefTol, kMarkExcess, false)) return false; + // Compliance + if (strict) + if (!IRSanyoAc::validChecksum(results->state, nbits / 8)) return false; + + // Success + results->decode_type = decode_type_t::SANYO_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_SANYO_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? +IRSanyoAc::IRSanyoAc(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 state/sequence. +void IRSanyoAc::stateReset(void) { + static const uint8_t kReset[kSanyoAcStateLength] = { + 0x6A, 0x6D, 0x51, 0x00, 0x10, 0x45, 0x00, 0x00, 0x33}; + std::memcpy(_.raw, kReset, kSanyoAcStateLength); +} + +/// Set up hardware to be able to send a message. +void IRSanyoAc::begin(void) { _irsend.begin(); } + +#if SEND_SANYO_AC +/// Send the current internal state as IR messages. +/// @param[in] repeat Nr. of times the message will be repeated. +void IRSanyoAc::send(const uint16_t repeat) { + _irsend.sendSanyoAc(getRaw(), kSanyoAcStateLength, repeat); +} +#endif // SEND_SANYO_AC + +/// Get a PTR to the internal state/code for this protocol with all integrity +/// checks passing. +/// @return PTR to a code for this protocol based on the current internal state. +uint8_t* IRSanyoAc::getRaw(void) { + checksum(); + return _.raw; +} + +/// Set the internal state from a valid code for this protocol. +/// @param[in] newState A valid code for this protocol. +void IRSanyoAc::setRaw(const uint8_t newState[]) { + std::memcpy(_.raw, newState, kSanyoAcStateLength); +} + +/// 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 IRSanyoAc::calcChecksum(const uint8_t state[], + const uint16_t length) { + return length ? sumNibbles(state, length - 1) : 0; +} + +/// 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 IRSanyoAc::validChecksum(const uint8_t state[], const uint16_t length) { + return length && state[length - 1] == IRSanyoAc::calcChecksum(state, length); +} + +/// Calculate & set the checksum for the current internal state of the remote. +void IRSanyoAc::checksum(void) { + // Stored the checksum value in the last byte. + _.Sum = calcChecksum(_.raw); +} + + +/// Set the requested power state of the A/C to on. +void IRSanyoAc::on(void) { setPower(true); } + +/// Set the requested power state of the A/C to off. +void IRSanyoAc::off(void) { setPower(false); } + +/// Change the power setting. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRSanyoAc::setPower(const bool on) { + _.Power = (on ? kSanyoAcPowerOn : kSanyoAcPowerOff); +} + +/// Get the value of the current power setting. +/// @return true, the setting is on. false, the setting is off. +bool IRSanyoAc::getPower(void) const { + return _.Power == kSanyoAcPowerOn; +} + +/// Get the operating mode setting of the A/C. +/// @return The current operating mode setting. +uint8_t IRSanyoAc::getMode(void) const { + return _.Mode; +} + +/// Set the operating mode of the A/C. +/// @param[in] mode The desired operating mode. +/// @note If we get an unexpected mode, default to AUTO. +void IRSanyoAc::setMode(const uint8_t mode) { + switch (mode) { + case kSanyoAcAuto: + case kSanyoAcCool: + case kSanyoAcDry: + case kSanyoAcHeat: + _.Mode = mode; + break; + default: _.Mode = kSanyoAcAuto; + } +} + +/// 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 IRSanyoAc::convertMode(const stdAc::opmode_t mode) { + switch (mode) { + case stdAc::opmode_t::kCool: return kSanyoAcCool; + case stdAc::opmode_t::kHeat: return kSanyoAcHeat; + case stdAc::opmode_t::kDry: return kSanyoAcDry; + default: return kSanyoAcAuto; + } +} + +/// 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 IRSanyoAc::toCommonMode(const uint8_t mode) { + switch (mode) { + case kSanyoAcCool: return stdAc::opmode_t::kCool; + case kSanyoAcHeat: return stdAc::opmode_t::kHeat; + case kSanyoAcDry: return stdAc::opmode_t::kDry; + default: return stdAc::opmode_t::kAuto; + } +} + +/// Set the desired temperature. +/// @param[in] degrees The temperature in degrees celsius. +void IRSanyoAc::setTemp(const uint8_t degrees) { + uint8_t temp = std::max((uint8_t)kSanyoAcTempMin, degrees); + temp = std::min((uint8_t)kSanyoAcTempMax, temp); + _.Temp = temp - kSanyoAcTempDelta; +} + +/// Get the current desired temperature setting. +/// @return The current setting for temp. in degrees celsius. +uint8_t IRSanyoAc::getTemp(void) const { + return _.Temp + kSanyoAcTempDelta; +} + +/// Set the sensor temperature. +/// @param[in] degrees The temperature in degrees celsius. +void IRSanyoAc::setSensorTemp(const uint8_t degrees) { + uint8_t temp = std::max((uint8_t)kSanyoAcTempMin, degrees); + temp = std::min((uint8_t)kSanyoAcTempMax, temp); + _.SensorTemp = temp - kSanyoAcTempDelta; +} + +/// Get the current sensor temperature setting. +/// @return The current setting for temp. in degrees celsius. +uint8_t IRSanyoAc::getSensorTemp(void) const { + return _.SensorTemp + kSanyoAcTempDelta; +} + +/// Set the speed of the fan. +/// @param[in] speed The desired setting. +void IRSanyoAc::setFan(const uint8_t speed) { + _.Fan = speed; +} + +/// Get the current fan speed setting. +/// @return The current fan speed/mode. +uint8_t IRSanyoAc::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 IRSanyoAc::convertFan(const stdAc::fanspeed_t speed) { + switch (speed) { + case stdAc::fanspeed_t::kMin: + case stdAc::fanspeed_t::kLow: return kSanyoAcFanLow; + case stdAc::fanspeed_t::kMedium: return kSanyoAcFanMedium; + case stdAc::fanspeed_t::kHigh: + case stdAc::fanspeed_t::kMax: return kSanyoAcFanHigh; + default: return kSanyoAcFanAuto; + } +} + +/// Convert a native fan speed into its stdAc equivalent. +/// @param[in] spd The native setting to be converted. +/// @return The stdAc equivalent of the native setting. +stdAc::fanspeed_t IRSanyoAc::toCommonFanSpeed(const uint8_t spd) { + switch (spd) { + case kSanyoAcFanHigh: return stdAc::fanspeed_t::kHigh; + case kSanyoAcFanMedium: return stdAc::fanspeed_t::kMedium; + case kSanyoAcFanLow: return stdAc::fanspeed_t::kLow; + default: return stdAc::fanspeed_t::kAuto; + } +} + +/// Get the vertical swing setting of the A/C. +/// @return The current swing mode setting. +uint8_t IRSanyoAc::getSwingV(void) const { + return _.SwingV; +} + +/// Set the vertical swing setting of the A/C. +/// @param[in] setting The value of the desired setting. +void IRSanyoAc::setSwingV(const uint8_t setting) { + if (setting == kSanyoAcSwingVAuto || + (setting >= kSanyoAcSwingVLowest && setting <= kSanyoAcSwingVHighest)) + _.SwingV = setting; + else + _.SwingV = kSanyoAcSwingVAuto; +} + +/// 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 IRSanyoAc::convertSwingV(const stdAc::swingv_t position) { + switch (position) { + case stdAc::swingv_t::kHighest: return kSanyoAcSwingVHighest; + case stdAc::swingv_t::kHigh: return kSanyoAcSwingVHigh; + case stdAc::swingv_t::kMiddle: return kSanyoAcSwingVUpperMiddle; + case stdAc::swingv_t::kLow: return kSanyoAcSwingVLow; + case stdAc::swingv_t::kLowest: return kSanyoAcSwingVLowest; + default: return kSanyoAcSwingVAuto; + } +} + +/// Convert a native vertical swing postion to it's common equivalent. +/// @param[in] setting A native position to convert. +/// @return The common vertical swing position. +stdAc::swingv_t IRSanyoAc::toCommonSwingV(const uint8_t setting) { + switch (setting) { + case kSanyoAcSwingVHighest: return stdAc::swingv_t::kHighest; + case kSanyoAcSwingVHigh: return stdAc::swingv_t::kHigh; + case kSanyoAcSwingVUpperMiddle: + case kSanyoAcSwingVLowerMiddle: return stdAc::swingv_t::kMiddle; + case kSanyoAcSwingVLow: return stdAc::swingv_t::kLow; + case kSanyoAcSwingVLowest: return stdAc::swingv_t::kLowest; + default: return stdAc::swingv_t::kAuto; + } +} + +/// Set the Sleep (Night Setback) setting of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRSanyoAc::setSleep(const bool on) { + _.Sleep = on; +} + +/// Get the Sleep (Night Setback) setting of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRSanyoAc::getSleep(void) const { + return _.Sleep; +} + +/// Set the Sensor Location setting of the A/C. +/// i.e. Where the ambient temperature is measured. +/// @param[in] location true is Unit/Wall, false is Remote/Room. +void IRSanyoAc::setSensor(const bool location) { + _.Sensor = location; +} + +/// Get the Sensor Location setting of the A/C. +/// i.e. Where the ambient temperature is measured. +/// @return true is Unit/Wall, false is Remote/Room. +bool IRSanyoAc::getSensor(void) const { + return _.Sensor; +} + +/// Set the Beep setting of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRSanyoAc::setBeep(const bool on) { + _.Beep = on; +} + +/// Get the Beep setting of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRSanyoAc::getBeep(void) const { + return _.Beep; +} + +/// Get the nr of minutes the Off Timer is set to. +/// @return The timer time expressed as the number of minutes. +/// A value of 0 means the Off Timer is off/disabled. +/// @note The internal precission has a resolution of 1 hour. +uint16_t IRSanyoAc::getOffTimer(void) const { + if (_.OffTimer) + return _.OffHour * 60; + else + return 0; +} + +/// Set the nr of minutes for the Off Timer. +/// @param[in] mins The timer time expressed as nr. of minutes. +/// A value of 0 means the Off Timer is off/disabled. +/// @note The internal precission has a resolution of 1 hour. +void IRSanyoAc::setOffTimer(const uint16_t mins) { + const uint8_t hours = std::min((uint8_t)(mins / 60), kSanyoAcHourMax); + _.OffTimer = (hours > 0); + _.OffHour = hours; +} + +/// Convert the current internal state into its stdAc::state_t equivalent. +/// @return The stdAc equivalent of the native settings. +stdAc::state_t IRSanyoAc::toCommon(void) const { + stdAc::state_t result; + result.protocol = decode_type_t::SANYO_AC; + result.model = -1; // Not supported. + result.power = getPower(); + result.mode = toCommonMode(_.Mode); + result.celsius = true; + result.degrees = getTemp(); + result.fanspeed = toCommonFanSpeed(_.Fan); + result.sleep = _.Sleep ? 0 : -1; + result.swingv = toCommonSwingV(_.SwingV); + result.beep = _.Beep; + // Not supported. + result.swingh = stdAc::swingh_t::kOff; + result.turbo = false; + result.econo = false; + result.light = false; + result.filter = false; + result.quiet = false; + result.clean = false; + result.clock = -1; + return result; +} + +/// Convert the current internal state into a human readable string. +/// @return A human readable string. +String IRSanyoAc::toString(void) const { + String result = ""; + result.reserve(140); + result += addBoolToString(getPower(), kPowerStr, false); + result += addModeToString(_.Mode, kSanyoAcAuto, kSanyoAcCool, + kSanyoAcHeat, kSanyoAcDry, kSanyoAcAuto); + result += addTempToString(getTemp()); + result += addFanToString(_.Fan, kSanyoAcFanHigh, kSanyoAcFanLow, + kSanyoAcFanAuto, kSanyoAcFanAuto, + kSanyoAcFanMedium); + result += addSwingVToString(_.SwingV, kSanyoAcSwingVAuto, + kSanyoAcSwingVHighest, kSanyoAcSwingVHigh, + kSanyoAcSwingVUpperMiddle, + kSanyoAcSwingVAuto, // Middle is unused + kSanyoAcSwingVLowerMiddle, + kSanyoAcSwingVLow, kSanyoAcSwingVLowest, + // Below are unused. + kSanyoAcSwingVAuto, + kSanyoAcSwingVAuto, + kSanyoAcSwingVAuto, + kSanyoAcSwingVAuto); + result += addBoolToString(_.Sleep, kSleepStr); + result += addBoolToString(_.Beep, kBeepStr); + result += addLabeledString(_.Sensor ? kRoomStr : kWallStr, kSensorStr); + result += kCommaSpaceStr; + result += kSensorStr; + result += ' '; + result += addTempToString(getSensorTemp(), true, false); + const uint16_t offtime = getOffTimer(); + result += addLabeledString(offtime ? minsToString(offtime) : kOffStr, + kOffTimerStr); + return result; +} + +#if SEND_SANYO_AC88 +/// Send a SanyoAc88 formatted message. +/// Status: ALPHA / Completely untested. +/// @param[in] data An array of bytes containing the IR command. +/// @warning data's bit order may change. It is not yet confirmed. +/// @param[in] nbytes Nr. of bytes of data in the array. +/// @param[in] repeat Nr. of times the message is to be repeated. +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1503 +void IRsend::sendSanyoAc88(const uint8_t data[], const uint16_t nbytes, + const uint16_t repeat) { + // (Header + Data + Footer) per repeat + sendGeneric(kSanyoAc88HdrMark, kSanyoAc88HdrSpace, + kSanyoAc88BitMark, kSanyoAc88OneSpace, + kSanyoAc88BitMark, kSanyoAc88ZeroSpace, + kSanyoAc88BitMark, kSanyoAc88Gap, + data, nbytes, kSanyoAc88Freq, false, repeat, kDutyDefault); + space(kDefaultMessageGap); // Make a guess at a post message gap. +} +#endif // SEND_SANYO_AC88 + +#if DECODE_SANYO_AC88 +/// Decode the supplied SanyoAc message. +/// Status: ALPHA / Untested. +/// @param[in,out] results Ptr to the data to decode & where to store the decode +/// @warning data's bit order may change. It is not yet confirmed. +/// @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/1503 +bool IRrecv::decodeSanyoAc88(decode_results *results, uint16_t offset, + const uint16_t nbits, const bool strict) { + if (strict && nbits != kSanyoAc88Bits) + return false; + + uint16_t used = 0; + // Compliance + const uint16_t expected_repeats = strict ? kSanyoAc88MinRepeat : 0; + + // Handle the expected nr of repeats. + for (uint16_t r = 0; r <= expected_repeats; r++) { + // Header + Data + Footer + used = matchGeneric(results->rawbuf + offset, results->state, + results->rawlen - offset, nbits, + kSanyoAc88HdrMark, kSanyoAc88HdrSpace, + kSanyoAc88BitMark, kSanyoAc88OneSpace, + kSanyoAc88BitMark, kSanyoAc88ZeroSpace, + kSanyoAc88BitMark, + // Expect an inter-message gap, or just the end of msg? + (r < expected_repeats) ? kSanyoAc88Gap + : kDefaultMessageGap, + r == expected_repeats, + _tolerance + kSanyoAc88ExtraTolerance, + kMarkExcess, false); + if (!used) return false; // No match! + offset += used; + } + + // Success + results->decode_type = decode_type_t::SANYO_AC88; + 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_SANYO_AC88 + +/// 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? +IRSanyoAc88::IRSanyoAc88(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. +/// @see https://docs.google.com/spreadsheets/d/1dYfLsnYvpjV-SgO8pdinpfuBIpSzm8Q1R5SabrLeskw/edit?ts=5f0190a5#gid=1050142776&range=A2:B2 +void IRSanyoAc88::stateReset(void) { + static const uint8_t kReset[kSanyoAc88StateLength] = { + 0xAA, 0x55, 0xA0, 0x16, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x10}; + std::memcpy(_.raw, kReset, kSanyoAc88StateLength); +} + +/// Set up hardware to be able to send a message. +void IRSanyoAc88::begin(void) { _irsend.begin(); } + +#if SEND_SANYO_AC +/// Send the current internal state as IR messages. +/// @param[in] repeat Nr. of times the message will be repeated. +void IRSanyoAc88::send(const uint16_t repeat) { + _irsend.sendSanyoAc88(getRaw(), kSanyoAc88StateLength, repeat); +} +#endif // SEND_SANYO_AC + +/// Get a PTR to the internal state/code for this protocol with all integrity +/// checks passing. +/// @return PTR to a code for this protocol based on the current internal state. +uint8_t* IRSanyoAc88::getRaw(void) { + return _.raw; +} + +/// Set the internal state from a valid code for this protocol. +/// @param[in] newState A valid code for this protocol. +void IRSanyoAc88::setRaw(const uint8_t newState[]) { + std::memcpy(_.raw, newState, kSanyoAc88StateLength); +} + +/// Set the requested power state of the A/C to on. +void IRSanyoAc88::on(void) { setPower(true); } + +/// Set the requested power state of the A/C to off. +void IRSanyoAc88::off(void) { setPower(false); } + +/// Change the power setting. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRSanyoAc88::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 IRSanyoAc88::getPower(void) const { return _.Power; } + +/// Get the operating mode setting of the A/C. +/// @return The current operating mode setting. +uint8_t IRSanyoAc88::getMode(void) const { return _.Mode; } + +/// Set the operating mode of the A/C. +/// @param[in] mode The desired operating mode. +/// @note If we get an unexpected mode, default to AUTO. +void IRSanyoAc88::setMode(const uint8_t mode) { + switch (mode) { + case kSanyoAc88Auto: + case kSanyoAc88FeelCool: + case kSanyoAc88Cool: + case kSanyoAc88FeelHeat: + case kSanyoAc88Heat: + case kSanyoAc88Fan: + _.Mode = mode; + break; + default: _.Mode = kSanyoAc88Auto; + } +} + +/// 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 IRSanyoAc88::convertMode(const stdAc::opmode_t mode) { + switch (mode) { + case stdAc::opmode_t::kCool: return kSanyoAc88Cool; + case stdAc::opmode_t::kHeat: return kSanyoAc88Heat; + case stdAc::opmode_t::kFan: return kSanyoAc88Fan; + default: return kSanyoAc88Auto; + } +} + +/// 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 IRSanyoAc88::toCommonMode(const uint8_t mode) { + switch (mode) { + case kSanyoAc88FeelCool: + case kSanyoAc88Cool: + return stdAc::opmode_t::kCool; + case kSanyoAc88FeelHeat: + case kSanyoAc88Heat: + return stdAc::opmode_t::kHeat; + case kSanyoAc88Fan: + return stdAc::opmode_t::kFan; + default: + return stdAc::opmode_t::kAuto; + } +} + +/// Set the desired temperature. +/// @param[in] degrees The temperature in degrees celsius. +void IRSanyoAc88::setTemp(const uint8_t degrees) { + uint8_t temp = std::max((uint8_t)kSanyoAc88TempMin, degrees); + _.Temp = std::min((uint8_t)kSanyoAc88TempMax, temp); +} + +/// Get the current desired temperature setting. +/// @return The current setting for temp. in degrees celsius. +uint8_t IRSanyoAc88::getTemp(void) const { return _.Temp; } + +/// Set the speed of the fan. +/// @param[in] speed The desired setting. +void IRSanyoAc88::setFan(const uint8_t speed) { _.Fan = speed; } + +/// Get the current fan speed setting. +/// @return The current fan speed/mode. +uint8_t IRSanyoAc88::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 IRSanyoAc88::convertFan(const stdAc::fanspeed_t speed) { + switch (speed) { + case stdAc::fanspeed_t::kMin: + case stdAc::fanspeed_t::kLow: return kSanyoAc88FanLow; + case stdAc::fanspeed_t::kMedium: return kSanyoAc88FanMedium; + case stdAc::fanspeed_t::kHigh: + case stdAc::fanspeed_t::kMax: return kSanyoAc88FanHigh; + default: return kSanyoAc88FanAuto; + } +} + +/// Get the current clock time. +/// @return The time as the nr. of minutes past midnight. +uint16_t IRSanyoAc88::getClock(void) const { + return _.ClockHrs * 60 + _.ClockMins; +} + +/// Set the current clock time. +/// @param[in] mins_since_midnight The time as nr. of minutes past midnight. +void IRSanyoAc88::setClock(const uint16_t mins_since_midnight) { + uint16_t mins = std::min(mins_since_midnight, (uint16_t)(23 * 60 + 59)); + _.ClockMins = mins % 60; + _.ClockHrs = mins / 60; + _.ClockSecs = 0; +} + +/// Convert a native fan speed into its stdAc equivalent. +/// @param[in] spd The native setting to be converted. +/// @return The stdAc equivalent of the native setting. +stdAc::fanspeed_t IRSanyoAc88::toCommonFanSpeed(const uint8_t spd) { + switch (spd) { + case kSanyoAc88FanHigh: return stdAc::fanspeed_t::kHigh; + case kSanyoAc88FanMedium: return stdAc::fanspeed_t::kMedium; + case kSanyoAc88FanLow: return stdAc::fanspeed_t::kLow; + default: return stdAc::fanspeed_t::kAuto; + } +} + +/// Change the SwingV setting. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRSanyoAc88::setSwingV(const bool on) { _.SwingV = on; } + +/// Get the value of the current SwingV setting. +/// @return true, the setting is on. false, the setting is off. +bool IRSanyoAc88::getSwingV(void) const { return _.SwingV; } + +/// Change the Turbo setting. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRSanyoAc88::setTurbo(const bool on) { _.Turbo = on; } + +/// Get the value of the current Turbo setting. +/// @return true, the setting is on. false, the setting is off. +bool IRSanyoAc88::getTurbo(void) const { return _.Turbo; } + +/// Change the Filter setting. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRSanyoAc88::setFilter(const bool on) { _.Filter = on; } + +/// Get the value of the current Filter setting. +/// @return true, the setting is on. false, the setting is off. +bool IRSanyoAc88::getFilter(void) const { return _.Filter; } + +/// Change the Sleep setting. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRSanyoAc88::setSleep(const bool on) { _.Sleep = on; } + +/// Get the value of the current Sleep setting. +/// @return true, the setting is on. false, the setting is off. +bool IRSanyoAc88::getSleep(void) const { return _.Sleep; } + +/// Convert the current internal state into its stdAc::state_t equivalent. +/// @return The stdAc equivalent of the native settings. +stdAc::state_t IRSanyoAc88::toCommon(void) const { + stdAc::state_t result; + result.protocol = decode_type_t::SANYO_AC88; + result.model = -1; // Not supported. + result.power = getPower(); + 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.filter = _.Filter; + result.turbo = _.Turbo; + result.sleep = _.Sleep ? 0 : -1; + result.clock = getClock(); + // Not supported. + result.swingh = stdAc::swingh_t::kOff; + result.econo = false; + result.light = false; + result.quiet = false; + result.beep = false; + result.clean = false; + return result; +} + +/// Convert the current internal state into a human readable string. +/// @return A human readable string. +String IRSanyoAc88::toString(void) const { + String result = ""; + result.reserve(115); + result += addBoolToString(getPower(), kPowerStr, false); + result += addModeToString(_.Mode, kSanyoAc88Auto, kSanyoAc88Cool, + kSanyoAc88Heat, kSanyoAc88Auto, kSanyoAc88Fan); + result += addTempToString(getTemp()); + result += addFanToString(_.Fan, kSanyoAc88FanHigh, kSanyoAc88FanLow, + kSanyoAc88FanAuto, kSanyoAc88FanAuto, + kSanyoAc88FanMedium); + result += addBoolToString(_.SwingV, kSwingVStr); + result += addBoolToString(_.Turbo, kTurboStr); + result += addBoolToString(_.Sleep, kSleepStr); + result += addLabeledString(minsToString(getClock()), kClockStr); + return result; +} diff --git a/lib/IRremoteESP8266/src/ir_Sanyo.h b/lib/IRremoteESP8266/src/ir_Sanyo.h index 206a7314ff..66376328f6 100644 --- a/lib/IRremoteESP8266/src/ir_Sanyo.h +++ b/lib/IRremoteESP8266/src/ir_Sanyo.h @@ -30,10 +30,10 @@ #ifdef ARDUINO #include #endif -#include "../src/IRremoteESP8266.h" -#include "../src/IRsend.h" +#include "IRremoteESP8266.h" +#include "IRsend.h" #ifdef UNIT_TEST -#include "../src/IRsend_test.h" +#include "IRsend_test.h" #endif /// Native representation of a Sanyo A/C message. diff --git a/lib/IRremoteESP8266/src/ir_Sharp.cpp b/lib/IRremoteESP8266/src/ir_Sharp.cpp new file mode 100644 index 0000000000..38f0ac32ce --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_Sharp.cpp @@ -0,0 +1,976 @@ +// Copyright 2009 Ken Shirriff +// Copyright 2017, 2019 David Conran + +/// @file +/// @brief Support for Sharp protocols. +/// @see http://www.sbprojects.net/knowledge/ir/sharp.htm +/// @see http://lirc.sourceforge.net/remotes/sharp/GA538WJSA +/// @see http://www.mwftr.com/ucF08/LEC14%20PIC%20IR.pdf +/// @see http://www.hifi-remote.com/johnsfine/DecodeIR.html#Sharp +/// @see GlobalCache's IR Control Tower data. +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/638 +/// @see https://github.com/ToniA/arduino-heatpumpir/blob/master/SharpHeatpumpIR.cpp + +#include "ir_Sharp.h" +#include +#include +#ifndef ARDUINO +#include +#endif +#include "IRrecv.h" +#include "IRsend.h" +#include "IRtext.h" +#include "IRutils.h" + +// Constants +// period time = 1/38000Hz = 26.316 microseconds. +const uint16_t kSharpTick = 26; +const uint16_t kSharpBitMarkTicks = 10; +const uint16_t kSharpBitMark = kSharpBitMarkTicks * kSharpTick; +const uint16_t kSharpOneSpaceTicks = 70; +const uint16_t kSharpOneSpace = kSharpOneSpaceTicks * kSharpTick; +const uint16_t kSharpZeroSpaceTicks = 30; +const uint16_t kSharpZeroSpace = kSharpZeroSpaceTicks * kSharpTick; +const uint16_t kSharpGapTicks = 1677; +const uint16_t kSharpGap = kSharpGapTicks * kSharpTick; +// Address(5) + Command(8) + Expansion(1) + Check(1) +const uint64_t kSharpToggleMask = + ((uint64_t)1 << (kSharpBits - kSharpAddressBits)) - 1; +const uint64_t kSharpAddressMask = ((uint64_t)1 << kSharpAddressBits) - 1; +const uint64_t kSharpCommandMask = ((uint64_t)1 << kSharpCommandBits) - 1; + +using irutils::addBoolToString; +using irutils::addFanToString; +using irutils::addIntToString; +using irutils::addLabeledString; +using irutils::addModeToString; +using irutils::addModelToString; +using irutils::addSwingVToString; +using irutils::addTempToString; +using irutils::addToggleToString; +using irutils::minsToString; + +// Also used by Denon protocol +#if (SEND_SHARP || SEND_DENON) +/// Send a (raw) Sharp message +/// @note Status: STABLE / Working fine. +/// @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 his procedure handles the inversion of bits required per protocol. +/// The protocol spec says to send the LSB first, but legacy code & usage +/// has us sending the MSB first. Grrrr. Normal invocation of encodeSharp() +/// handles this for you, assuming you are using the correct/standard values. +/// e.g. sendSharpRaw(encodeSharp(address, command)); +void IRsend::sendSharpRaw(const uint64_t data, const uint16_t nbits, + const uint16_t repeat) { + uint64_t tempdata = data; + for (uint16_t i = 0; i <= repeat; i++) { + // Protocol demands that the data be sent twice; once normally, + // then with all but the address bits inverted. + // Note: Previously this used to be performed 3 times (normal, inverted, + // normal), however all data points to that being incorrect. + for (uint8_t n = 0; n < 2; n++) { + sendGeneric(0, 0, // No Header + kSharpBitMark, kSharpOneSpace, kSharpBitMark, kSharpZeroSpace, + kSharpBitMark, kSharpGap, tempdata, nbits, 38, true, + 0, // Repeats are handled already. + 33); + // Invert the data per protocol. This is always called twice, so it's + // returned to original upon exiting the inner loop. + tempdata ^= kSharpToggleMask; + } + } +} + +/// Encode a (raw) Sharp message from it's components. +/// Status: STABLE / Works okay. +/// @param[in] address The value of the address to be sent. +/// @param[in] command The value of the address to be sent. (8 bits) +/// @param[in] expansion The value of the expansion bit to use. +/// (0 or 1, typically 1) +/// @param[in] check The value of the check bit to use. (0 or 1, typically 0) +/// @param[in] MSBfirst Flag indicating MSB first or LSB first order. +/// @return A uint32_t containing the raw Sharp message for `sendSharpRaw()`. +/// @note Assumes the standard Sharp bit sizes. +/// Historically sendSharp() sends address & command in +/// MSB first order. This is actually incorrect. It should be sent in LSB +/// order. The behaviour of sendSharp() hasn't been changed to maintain +/// backward compatibility. +uint32_t IRsend::encodeSharp(const uint16_t address, const uint16_t command, + const uint16_t expansion, const uint16_t check, + const bool MSBfirst) { + // Mask any unexpected bits. + uint16_t tempaddress = GETBITS16(address, 0, kSharpAddressBits); + uint16_t tempcommand = GETBITS16(command, 0, kSharpCommandBits); + uint16_t tempexpansion = GETBITS16(expansion, 0, 1); + uint16_t tempcheck = GETBITS16(check, 0, 1); + + if (!MSBfirst) { // Correct bit order if needed. + tempaddress = reverseBits(tempaddress, kSharpAddressBits); + tempcommand = reverseBits(tempcommand, kSharpCommandBits); + } + // Concatenate all the bits. + return (tempaddress << (kSharpCommandBits + 2)) | (tempcommand << 2) | + (tempexpansion << 1) | tempcheck; +} + +/// Send a Sharp message +/// Status: DEPRECATED / Previously working fine. +/// @deprecated Only use this if you are using legacy from the original +/// Arduino-IRremote library. 99% of the time, you will want to use +/// `sendSharpRaw()` instead +/// @param[in] address Address value to be sent. +/// @param[in] command Command value 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 procedure has a non-standard invocation style compared to similar +/// sendProtocol() routines. This is due to legacy, compatibility, & historic +/// reasons. Normally the calling syntax version is like sendSharpRaw(). +/// This procedure transmits the address & command in MSB first order, which is +/// incorrect. This behaviour is left as-is to maintain backward +/// compatibility with legacy code. +/// In short, you should use sendSharpRaw(), encodeSharp(), and the correct +/// values of address & command instead of using this, & the wrong values. +void IRsend::sendSharp(const uint16_t address, uint16_t const command, + const uint16_t nbits, const uint16_t repeat) { + sendSharpRaw(encodeSharp(address, command, 1, 0, true), nbits, repeat); +} +#endif // (SEND_SHARP || SEND_DENON) + +// Used by decodeDenon too. +#if (DECODE_SHARP || DECODE_DENON) +/// Decode the supplied Sharp message. +/// Status: STABLE / Working fine. +/// @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. +/// @param[in] expansion Should we expect the expansion bit to be set. +/// Default is true. +/// @return True if it can decode it, false if it can't. +/// @note This procedure returns a value suitable for use in `sendSharpRaw()`. +/// @todo Need to ensure capture of the inverted message as it can +/// be missed due to the interrupt timeout used to detect an end of message. +/// Several compliance checks are disabled until that is resolved. +bool IRrecv::decodeSharp(decode_results *results, uint16_t offset, + const uint16_t nbits, const bool strict, + const bool expansion) { + if (results->rawlen <= 2 * nbits + kFooter - 1 + offset) + return false; // Not enough entries to be a Sharp message. + // Compliance + if (strict) { + if (nbits != kSharpBits) return false; // Request is out of spec. + // DISABLED - See TODO +#ifdef UNIT_TEST + // An in spec message has the data sent normally, then inverted. So we + // expect twice as many entries than to just get the results. + if (results->rawlen <= (2 * (2 * nbits + kFooter)) - 1 + offset) + return false; +#endif + } + + uint64_t data = 0; + + // Match Data + Footer + uint16_t used; + used = matchGeneric(results->rawbuf + offset, &data, + results->rawlen - offset, nbits, + 0, 0, // No Header + kSharpBitMark, kSharpOneSpace, + kSharpBitMark, kSharpZeroSpace, + kSharpBitMark, kSharpGap, true, 35); + if (!used) return false; + offset += used; + // Compliance + if (strict) { + // Check the state of the expansion bit is what we expect. + if ((data & 0b10) >> 1 != expansion) return false; + // The check bit should be cleared in a normal message. + if (data & 0b1) return false; + // DISABLED - See TODO +#ifdef UNIT_TEST + // Grab the second copy of the data (i.e. inverted) + uint64_t second_data = 0; + // Match Data + Footer + if (!matchGeneric(results->rawbuf + offset, &second_data, + results->rawlen - offset, nbits, + 0, 0, + kSharpBitMark, kSharpOneSpace, + kSharpBitMark, kSharpZeroSpace, + kSharpBitMark, kSharpGap, true, 35)) return false; + // Check that second_data has been inverted correctly. + if (data != (second_data ^ kSharpToggleMask)) return false; +#endif // UNIT_TEST + } + + // Success + results->decode_type = SHARP; + results->bits = nbits; + results->value = data; + // Address & command are actually transmitted in LSB first order. + results->address = reverseBits(data, nbits) & kSharpAddressMask; + results->command = + reverseBits((data >> 2) & kSharpCommandMask, kSharpCommandBits); + return true; +} +#endif // (DECODE_SHARP || DECODE_DENON) + +#if SEND_SHARP_AC +/// Send a Sharp A/C message. +/// Status: Alpha / Untested. +/// @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. +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/638 +/// @see https://github.com/ToniA/arduino-heatpumpir/blob/master/SharpHeatpumpIR.cpp +void IRsend::sendSharpAc(const unsigned char data[], const uint16_t nbytes, + const uint16_t repeat) { + if (nbytes < kSharpAcStateLength) + return; // Not enough bytes to send a proper message. + + sendGeneric(kSharpAcHdrMark, kSharpAcHdrSpace, + kSharpAcBitMark, kSharpAcOneSpace, + kSharpAcBitMark, kSharpAcZeroSpace, + kSharpAcBitMark, kSharpAcGap, + data, nbytes, 38000, false, repeat, 50); +} +#endif // SEND_SHARP_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? +IRSharpAc::IRSharpAc(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 IRSharpAc::begin(void) { _irsend.begin(); } + +#if SEND_SHARP_AC +/// Send the current internal state as an IR message. +/// @param[in] repeat Nr. of times the message will be repeated. +void IRSharpAc::send(const uint16_t repeat) { + _irsend.sendSharpAc(getRaw(), kSharpAcStateLength, repeat); +} +#endif // SEND_SHARP_AC + +/// 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 4-bit checksum value. +uint8_t IRSharpAc::calcChecksum(uint8_t state[], const uint16_t length) { + uint8_t xorsum = xorBytes(state, length - 1); + xorsum ^= GETBITS8(state[length - 1], kLowNibble, kNibbleSize); + xorsum ^= GETBITS8(xorsum, kHighNibble, kNibbleSize); + return GETBITS8(xorsum, kLowNibble, kNibbleSize); +} + +/// 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 IRSharpAc::validChecksum(uint8_t state[], const uint16_t length) { + return GETBITS8(state[length - 1], kHighNibble, kNibbleSize) == + IRSharpAc::calcChecksum(state, length); +} + +/// Calculate and set the checksum values for the internal state. +void IRSharpAc::checksum(void) { + _.Sum = calcChecksum(_.raw); +} + +/// Reset the state of the remote to a known good state/sequence. +void IRSharpAc::stateReset(void) { + static const uint8_t reset[kSharpAcStateLength] = { + 0xAA, 0x5A, 0xCF, 0x10, 0x00, 0x01, 0x00, 0x00, 0x08, 0x80, 0x00, 0xE0, + 0x01}; + std::memcpy(_.raw, reset, kSharpAcStateLength); + _temp = getTemp(); + _mode = _.Mode; + _fan = _.Fan; + _model = getModel(true); +} + +/// 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 *IRSharpAc::getRaw(void) { + checksum(); // Ensure correct settings before sending. + 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 IRSharpAc::setRaw(const uint8_t new_code[], const uint16_t length) { + std::memcpy(_.raw, new_code, std::min(length, kSharpAcStateLength)); + _model = getModel(true); +} + +/// Set the model of the A/C to emulate. +/// @param[in] model The enum of the appropriate model. +void IRSharpAc::setModel(const sharp_ac_remote_model_t model) { + switch (model) { + case sharp_ac_remote_model_t::A705: + case sharp_ac_remote_model_t::A903: + _model = model; + _.Model = true; + break; + default: + _model = sharp_ac_remote_model_t::A907; + _.Model = false; + } + _.Model2 = (_model != sharp_ac_remote_model_t::A907); + // Redo the operating mode as some models don't support all modes. + setMode(_.Mode); +} + +/// Get/Detect the model of the A/C. +/// @param[in] raw Try to determine the model from the raw code only. +/// @return The enum of the compatible model. +sharp_ac_remote_model_t IRSharpAc::getModel(const bool raw) const { + if (raw) { + if (_.Model2) { + if (_.Model) + return sharp_ac_remote_model_t::A705; + else + return sharp_ac_remote_model_t::A903; + } else { + return sharp_ac_remote_model_t::A907; + } + } + return _model; +} + +/// Set the value of the Power Special setting without any checks. +/// @param[in] value The value to set Power Special to. +inline void IRSharpAc::setPowerSpecial(const uint8_t value) { + _.PowerSpecial = value; +} + +/// Get the value of the Power Special setting. +/// @return The setting's value. +uint8_t IRSharpAc::getPowerSpecial(void) const { + return _.PowerSpecial; +} + +/// Clear the "special"/non-normal bits in the power section. +/// e.g. for normal/common command modes. +void IRSharpAc::clearPowerSpecial(void) { + setPowerSpecial(_.PowerSpecial & kSharpAcPowerOn); +} + +/// Is one of the special power states in use? +/// @return true, it is. false, it isn't. +bool IRSharpAc::isPowerSpecial(void) const { + switch (_.PowerSpecial) { + case kSharpAcPowerSetSpecialOff: + case kSharpAcPowerSetSpecialOn: + case kSharpAcPowerTimerSetting: return true; + default: return false; + } +} + +/// Set the requested power state of the A/C to on. +void IRSharpAc::on(void) { setPower(true); } + +/// Set the requested power state of the A/C to off. +void IRSharpAc::off(void) { setPower(false); } + +/// Change the power setting, including the previous power state. +/// @param[in] on true, the setting is on. false, the setting is off. +/// @param[in] prev_on true, the setting is on. false, the setting is off. +void IRSharpAc::setPower(const bool on, const bool prev_on) { + setPowerSpecial(on ? (prev_on ? kSharpAcPowerOn : kSharpAcPowerOnFromOff) + : kSharpAcPowerOff); + // Power operations are incompatible with clean mode. + if (_.Clean) setClean(false); + _.Special = kSharpAcSpecialPower; +} + +/// Get the value of the current power setting. +/// @return true, the setting is on. false, the setting is off. +bool IRSharpAc::getPower(void) const { + switch (_.PowerSpecial) { + case kSharpAcPowerUnknown: + case kSharpAcPowerOff: return false; + default: return true; // Everything else is "probably" on. + } +} + +/// Set the value of the Special (button/command?) setting. +/// @param[in] mode The value to set Special to. +void IRSharpAc::setSpecial(const uint8_t mode) { + switch (mode) { + case kSharpAcSpecialPower: + case kSharpAcSpecialTurbo: + case kSharpAcSpecialTempEcono: + case kSharpAcSpecialFan: + case kSharpAcSpecialSwing: + case kSharpAcSpecialTimer: + case kSharpAcSpecialTimerHalfHour: + _.Special = mode; + break; + default: + _.Special = kSharpAcSpecialPower; + } +} + +/// Get the value of the Special (button/command?) setting. +/// @return The setting's value. +uint8_t IRSharpAc::getSpecial(void) const { return _.Special; } + +/// Set the temperature. +/// @param[in] temp The temperature in degrees celsius. +/// @param[in] save Do we save this setting as a user set one? +void IRSharpAc::setTemp(const uint8_t temp, const bool save) { + switch (_.Mode) { + // Auto & Dry don't allow temp changes and have a special temp. + case kSharpAcAuto: + case kSharpAcDry: + _.raw[kSharpAcByteTemp] = 0; + return; + default: + switch (getModel()) { + case sharp_ac_remote_model_t::A705: + _.raw[kSharpAcByteTemp] = 0xD0; + break; + default: + _.raw[kSharpAcByteTemp] = 0xC0; + } + } + uint8_t degrees = std::max(temp, kSharpAcMinTemp); + degrees = std::min(degrees, kSharpAcMaxTemp); + if (save) _temp = degrees; + _.Temp = degrees - kSharpAcMinTemp; + _.Special = kSharpAcSpecialTempEcono; + clearPowerSpecial(); +} + +/// Get the current temperature setting. +/// @return The current setting for temp. in degrees celsius. +uint8_t IRSharpAc::getTemp(void) const { + return _.Temp + kSharpAcMinTemp; +} + +/// Get the operating mode setting of the A/C. +/// @return The current operating mode setting. +uint8_t IRSharpAc::getMode(void) const { + return _.Mode; +} + +/// Set the operating mode of the A/C. +/// @param[in] mode The desired operating mode. +/// @param[in] save Do we save this setting as a user set one? +void IRSharpAc::setMode(const uint8_t mode, const bool save) { + uint8_t realMode = mode; + if (mode == kSharpAcHeat) { + switch (getModel()) { + case sharp_ac_remote_model_t::A705: + case sharp_ac_remote_model_t::A903: + // These models have no heat mode, use Fan mode instead. + realMode = kSharpAcFan; + break; + default: + break; + } + } + + switch (realMode) { + case kSharpAcAuto: // Also kSharpAcFan + case kSharpAcDry: + // When Dry or Auto, Fan always 2(Auto) + setFan(kSharpAcFanAuto, false); + // FALLTHRU + case kSharpAcCool: + case kSharpAcHeat: + _.Mode = realMode; + break; + default: + setFan(kSharpAcFanAuto, false); + _.Mode = kSharpAcAuto; + } + // Dry/Auto have no temp setting. This step will enforce it. + setTemp(_temp, false); + // Save the mode in case we need to revert to it. eg. Clean + if (save) _mode = _.Mode; + + _.Special = kSharpAcSpecialPower; + clearPowerSpecial(); +} + +/// Set the speed of the fan. +/// @param[in] speed The desired setting. +/// @param[in] save Do we save this setting as a user set one? +void IRSharpAc::setFan(const uint8_t speed, const bool save) { + switch (speed) { + case kSharpAcFanAuto: + case kSharpAcFanMin: + case kSharpAcFanMed: + case kSharpAcFanHigh: + case kSharpAcFanMax: + _.Fan = speed; + if (save) _fan = speed; + break; + default: + _.Fan = kSharpAcFanAuto; + _fan = kSharpAcFanAuto; + } + _.Special = kSharpAcSpecialFan; + clearPowerSpecial(); +} + +/// Get the current fan speed setting. +/// @return The current fan speed/mode. +uint8_t IRSharpAc::getFan(void) const { + return _.Fan; +} + +/// Get the Turbo setting of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRSharpAc::getTurbo(void) const { + return (_.PowerSpecial == kSharpAcPowerSetSpecialOn) && + (_.Special == kSharpAcSpecialTurbo); +} + +/// Set the Turbo setting of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +/// @note If you use this method, you will need to send it before making +/// other changes to the settings, as they may overwrite some of the bits +/// used by this setting. +void IRSharpAc::setTurbo(const bool on) { + if (on) setFan(kSharpAcFanMax); + setPowerSpecial(on ? kSharpAcPowerSetSpecialOn : kSharpAcPowerSetSpecialOff); + _.Special = kSharpAcSpecialTurbo; +} + +/// Get the Vertical Swing setting of the A/C. +/// @return The position of the Vertical Swing setting. +uint8_t IRSharpAc::getSwingV(void) const { return _.Swing; } + +/// Set the Vertical Swing setting of the A/C. +/// @note Some positions may not work on all models. +/// @param[in] position The desired position/setting. +/// @note `setSwingV(kSharpAcSwingVLowest)` will only allow the Lowest setting +/// in Heat mode, it will default to `kSharpAcSwingVLow` otherwise. +/// If you want to set this value in other modes e.g. Cool, you must +/// use `setSwingV`s optional `force` parameter. +/// @param[in] force Do we override the safety checks and just do it? +void IRSharpAc::setSwingV(const uint8_t position, const bool force) { + switch (position) { + case kSharpAcSwingVCoanda: + // Only allowed in Heat mode. + if (!force && getMode() != kSharpAcHeat) { + setSwingV(kSharpAcSwingVLow); // Use the next lowest setting. + return; + } + // FALLTHRU + case kSharpAcSwingVHigh: + case kSharpAcSwingVMid: + case kSharpAcSwingVLow: + case kSharpAcSwingVToggle: + case kSharpAcSwingVOff: + case kSharpAcSwingVLast: // Technically valid, but we don't use it. + // All expected non-positions set the special bits. + _.Special = kSharpAcSpecialSwing; + // FALLTHRU + case kSharpAcSwingVIgnore: + _.Swing = position; + } +} + +/// Convert a standard A/C vertical swing into its native setting. +/// @param[in] position A stdAc::swingv_t position to convert. +/// @return The equivalent native horizontal swing position. +uint8_t IRSharpAc::convertSwingV(const stdAc::swingv_t position) { + switch (position) { + case stdAc::swingv_t::kHighest: + case stdAc::swingv_t::kHigh: return kSharpAcSwingVHigh; + case stdAc::swingv_t::kMiddle: return kSharpAcSwingVMid; + case stdAc::swingv_t::kLow: return kSharpAcSwingVLow; + case stdAc::swingv_t::kLowest: return kSharpAcSwingVCoanda; + case stdAc::swingv_t::kAuto: return kSharpAcSwingVToggle; + case stdAc::swingv_t::kOff: return kSharpAcSwingVOff; + default: return kSharpAcSwingVIgnore; + } +} + +/// Get the (vertical) Swing Toggle setting of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRSharpAc::getSwingToggle(void) const { + return getSwingV() == kSharpAcSwingVToggle; +} + +/// Set the (vertical) Swing Toggle setting of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRSharpAc::setSwingToggle(const bool on) { + setSwingV(on ? kSharpAcSwingVToggle : kSharpAcSwingVIgnore); + if (on) _.Special = kSharpAcSpecialSwing; +} + +/// Get the Ion (Filter) setting of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRSharpAc::getIon(void) const { return _.Ion; } + +/// Set the Ion (Filter) setting of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRSharpAc::setIon(const bool on) { + _.Ion = on; + clearPowerSpecial(); + if (on) _.Special = kSharpAcSpecialSwing; +} + +/// Get the Economical mode toggle setting of the A/C. +/// @return true, the setting is on. false, the setting is off. +/// @note Shares the same location as the Light setting on A705. +bool IRSharpAc::_getEconoToggle(void) const { + return (_.PowerSpecial == kSharpAcPowerSetSpecialOn) && + (_.Special == kSharpAcSpecialTempEcono); +} + +/// Set the Economical mode toggle setting of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +/// @warning Probably incompatible with `setTurbo()` +/// @note Shares the same location as the Light setting on A705. +void IRSharpAc::_setEconoToggle(const bool on) { + if (on) _.Special = kSharpAcSpecialTempEcono; + setPowerSpecial(on ? kSharpAcPowerSetSpecialOn : kSharpAcPowerSetSpecialOff); +} + +/// Set the Economical mode toggle setting of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +/// @warning Probably incompatible with `setTurbo()` +/// @note Available on the A907 models. +void IRSharpAc::setEconoToggle(const bool on) { + if (_model == sharp_ac_remote_model_t::A907) _setEconoToggle(on); +} + +/// Get the Economical mode toggle setting of the A/C. +/// @return true, the setting is on. false, the setting is off. +/// @note Available on the A907 models. +bool IRSharpAc::getEconoToggle(void) const { + return _model == sharp_ac_remote_model_t::A907 && _getEconoToggle(); +} + +/// Set the Light mode toggle setting of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +/// @warning Probably incompatible with `setTurbo()` +/// @note Not available on the A907 model. +void IRSharpAc::setLightToggle(const bool on) { + if (_model != sharp_ac_remote_model_t::A907) _setEconoToggle(on); +} + +/// Get the Light toggle setting of the A/C. +/// @return true, the setting is on. false, the setting is off. +/// @note Not available on the A907 model. +bool IRSharpAc::getLightToggle(void) const { + return _model != sharp_ac_remote_model_t::A907 && _getEconoToggle(); +} + +/// Get how long the timer is set for, in minutes. +/// @return The time in nr of minutes. +uint16_t IRSharpAc::getTimerTime(void) const { + return _.TimerHours * kSharpAcTimerIncrement * 2 + + ((_.Special == kSharpAcSpecialTimerHalfHour) ? kSharpAcTimerIncrement + : 0); +} + +/// Is the Timer enabled? +/// @return true, the setting is on. false, the setting is off. +bool IRSharpAc::getTimerEnabled(void) const { return _.TimerEnabled; } + +/// Get the current timer type. +/// @return true, It's an "On" timer. false, It's an "Off" timer. +bool IRSharpAc::getTimerType(void) const { return _.TimerType; } + +/// Set or cancel the timer function. +/// @param[in] enable Is the timer to be enabled (true) or canceled(false)? +/// @param[in] timer_type An On (true) or an Off (false). Ignored if canceled. +/// @param[in] mins Nr. of minutes the timer is to be set to. +/// @note Rounds down to 30 min increments. (max: 720 mins (12h), 0 is Off) +void IRSharpAc::setTimer(bool enable, bool timer_type, uint16_t mins) { + uint8_t half_hours = std::min(mins / kSharpAcTimerIncrement, + kSharpAcTimerHoursMax * 2); + if (half_hours == 0) enable = false; + if (!enable) { + half_hours = 0; + timer_type = kSharpAcOffTimerType; + } + _.TimerEnabled = enable; + _.TimerType = timer_type; + _.TimerHours = half_hours / 2; + // Handle non-round hours. + _.Special = (half_hours % 2) ? kSharpAcSpecialTimerHalfHour + : kSharpAcSpecialTimer; + setPowerSpecial(kSharpAcPowerTimerSetting); +} + +/// Get the Clean setting of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRSharpAc::getClean(void) const { + return _.Clean; +} + +/// Set the Economical mode toggle setting of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +/// @note Officially A/C unit needs to be "Off" before clean mode can be entered +void IRSharpAc::setClean(const bool on) { + // Clean mode appears to be just default dry mode, with an extra bit set. + if (on) { + setMode(kSharpAcDry, false); + setPower(true, false); + } else { + // Restore the previous operation mode & fan speed. + setMode(_mode, false); + setFan(_fan, false); + } + _.Clean = on; + clearPowerSpecial(); +} + +/// 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 IRSharpAc::convertMode(const stdAc::opmode_t mode) { + switch (mode) { + case stdAc::opmode_t::kCool: return kSharpAcCool; + case stdAc::opmode_t::kHeat: return kSharpAcHeat; + case stdAc::opmode_t::kDry: return kSharpAcDry; + // No Fan mode. + default: return kSharpAcAuto; + } +} + +/// Convert a stdAc::fanspeed_t enum into it's native speed. +/// @param[in] speed The enum to be converted. +/// @param[in] model The enum of the appropriate model. +/// @return The native equivalent of the enum. +uint8_t IRSharpAc::convertFan(const stdAc::fanspeed_t speed, + const sharp_ac_remote_model_t model) { + switch (model) { + case sharp_ac_remote_model_t::A705: + case sharp_ac_remote_model_t::A903: + switch (speed) { + case stdAc::fanspeed_t::kLow: return kSharpAcFanA705Low; + case stdAc::fanspeed_t::kMedium: return kSharpAcFanA705Med; + default: {}; // Fall thru to the next/default clause if not the above + // special cases. + } + // FALL THRU + default: + switch (speed) { + case stdAc::fanspeed_t::kMin: + case stdAc::fanspeed_t::kLow: return kSharpAcFanMin; + case stdAc::fanspeed_t::kMedium: return kSharpAcFanMed; + case stdAc::fanspeed_t::kHigh: return kSharpAcFanHigh; + case stdAc::fanspeed_t::kMax: return kSharpAcFanMax; + default: return kSharpAcFanAuto; + } + } +} + +/// 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 IRSharpAc::toCommonMode(const uint8_t mode) const { + switch (mode) { + case kSharpAcCool: return stdAc::opmode_t::kCool; + case kSharpAcHeat: return stdAc::opmode_t::kHeat; + case kSharpAcDry: return stdAc::opmode_t::kDry; + case kSharpAcAuto: // Also kSharpAcFan + switch (getModel()) { + case sharp_ac_remote_model_t::A705: return stdAc::opmode_t::kFan; + default: return stdAc::opmode_t::kAuto; + } + break; + 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 IRSharpAc::toCommonFanSpeed(const uint8_t speed) const { + switch (getModel()) { + case sharp_ac_remote_model_t::A705: + case sharp_ac_remote_model_t::A903: + switch (speed) { + case kSharpAcFanA705Low: return stdAc::fanspeed_t::kLow; + case kSharpAcFanA705Med: return stdAc::fanspeed_t::kMedium; + } + // FALL-THRU + default: + switch (speed) { + case kSharpAcFanMax: return stdAc::fanspeed_t::kMax; + case kSharpAcFanHigh: return stdAc::fanspeed_t::kHigh; + case kSharpAcFanMed: return stdAc::fanspeed_t::kMedium; + case kSharpAcFanMin: return stdAc::fanspeed_t::kMin; + default: return stdAc::fanspeed_t::kAuto; + } + } +} + +/// Convert a native vertical swing postion to it's common equivalent. +/// @param[in] pos A native position to convert. +/// @param[in] mode What operating mode are we in? +/// @return The common vertical swing position. +stdAc::swingv_t IRSharpAc::toCommonSwingV(const uint8_t pos, + const stdAc::opmode_t mode) const { + switch (pos) { + case kSharpAcSwingVHigh: return stdAc::swingv_t::kHighest; + case kSharpAcSwingVMid: return stdAc::swingv_t::kMiddle; + case kSharpAcSwingVLow: return stdAc::swingv_t::kLow; + case kSharpAcSwingVCoanda: // Coanda has mode dependent positionss + switch (mode) { + case stdAc::opmode_t::kCool: return stdAc::swingv_t::kHighest; + case stdAc::opmode_t::kHeat: return stdAc::swingv_t::kLowest; + default: return stdAc::swingv_t::kOff; + } + case kSharpAcSwingVToggle: return stdAc::swingv_t::kAuto; + default: return stdAc::swingv_t::kOff; + } +} + +/// 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 IRSharpAc::toCommon(const stdAc::state_t *prev) const { + stdAc::state_t result; + // Start with the previous state if given it. + if (prev != NULL) result = *prev; + result.protocol = decode_type_t::SHARP_AC; + result.model = getModel(); + result.power = getPower(); + result.mode = toCommonMode(_.Mode); + result.celsius = true; + result.degrees = getTemp(); + result.fanspeed = toCommonFanSpeed(_.Fan); + result.turbo = getTurbo(); + if (getSwingV() != kSharpAcSwingVIgnore) + result.swingv = toCommonSwingV(getSwingV(), result.mode); + result.filter = _.Ion; + result.econo = getEconoToggle(); + result.light = getLightToggle(); + result.clean = _.Clean; + // Not supported. + result.swingh = stdAc::swingh_t::kOff; + result.quiet = 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 IRSharpAc::toString(void) const { + String result = ""; + const sharp_ac_remote_model_t model = getModel(); + result.reserve(170); // Reserve some heap for the string to reduce fragging. + result += addModelToString(decode_type_t::SHARP_AC, getModel(), false); + + result += addLabeledString(isPowerSpecial() ? String("-") + : String(getPower() ? kOnStr + : kOffStr), + kPowerStr); + const uint8_t mode = _.Mode; + result += addModeToString( + mode, + // Make the value invalid if the model doesn't support an Auto mode. + (model == sharp_ac_remote_model_t::A907) ? kSharpAcAuto : 255, + kSharpAcCool, kSharpAcHeat, kSharpAcDry, kSharpAcFan); + result += addTempToString(getTemp()); + switch (model) { + case sharp_ac_remote_model_t::A705: + case sharp_ac_remote_model_t::A903: + result += addFanToString(_.Fan, kSharpAcFanMax, kSharpAcFanA705Low, + kSharpAcFanAuto, kSharpAcFanAuto, + kSharpAcFanA705Med); + break; + default: + result += addFanToString(_.Fan, kSharpAcFanMax, kSharpAcFanMin, + kSharpAcFanAuto, kSharpAcFanAuto, + kSharpAcFanMed); + } + if (getSwingV() == kSharpAcSwingVIgnore) { + result += addIntToString(kSharpAcSwingVIgnore, kSwingVStr); + result += kSpaceLBraceStr; + result += kNAStr; + result += ')'; + } else { + result += addSwingVToString( + getSwingV(), 0xFF, + // Coanda means Highest when in Cool mode. + (mode == kSharpAcCool) ? kSharpAcSwingVCoanda : kSharpAcSwingVToggle, + kSharpAcSwingVHigh, + 0xFF, // Upper Middle is unused + kSharpAcSwingVMid, + 0xFF, // Lower Middle is unused + kSharpAcSwingVLow, + kSharpAcSwingVCoanda, + kSharpAcSwingVOff, + // Below are unused. + kSharpAcSwingVToggle, + 0xFF, + 0xFF); + } + result += addBoolToString(getTurbo(), kTurboStr); + result += addBoolToString(_.Ion, kIonStr); + switch (model) { + case sharp_ac_remote_model_t::A705: + case sharp_ac_remote_model_t::A903: + result += addToggleToString(getLightToggle(), kLightStr); + break; + default: + result += addToggleToString(getEconoToggle(), kEconoStr); + } + result += addBoolToString(_.Clean, kCleanStr); + if (_.TimerEnabled) + result += addLabeledString(minsToString(getTimerTime()), + _.TimerType ? kOnTimerStr : kOffTimerStr); + return result; +} + +#if DECODE_SHARP_AC +/// Decode the supplied Sharp A/C 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/638 +/// @see https://github.com/ToniA/arduino-heatpumpir/blob/master/SharpHeatpumpIR.cpp +bool IRrecv::decodeSharpAc(decode_results *results, uint16_t offset, + const uint16_t nbits, const bool strict) { + // Compliance + if (strict && nbits != kSharpAcBits) return false; + + // Match Header + Data + Footer + uint16_t used; + used = matchGeneric(results->rawbuf + offset, results->state, + results->rawlen - offset, nbits, + kSharpAcHdrMark, kSharpAcHdrSpace, + kSharpAcBitMark, kSharpAcOneSpace, + kSharpAcBitMark, kSharpAcZeroSpace, + kSharpAcBitMark, kSharpAcGap, true, + _tolerance, kMarkExcess, false); + if (used == 0) return false; + offset += used; + // Compliance + if (strict) { + if (!IRSharpAc::validChecksum(results->state)) return false; + } + + // Success + results->decode_type = SHARP_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_SHARP_AC diff --git a/lib/IRremoteESP8266/src/ir_Sharp.h b/lib/IRremoteESP8266/src/ir_Sharp.h index 507f63e437..b3be534e7b 100644 --- a/lib/IRremoteESP8266/src/ir_Sharp.h +++ b/lib/IRremoteESP8266/src/ir_Sharp.h @@ -32,13 +32,13 @@ #ifndef UNIT_TEST #include #endif -#include "../src/IRrecv.h" -#include "../src/IRremoteESP8266.h" -#include "../src/IRsend.h" +#include "IRrecv.h" +#include "IRremoteESP8266.h" +#include "IRsend.h" #ifdef UNIT_TEST -#include "../src/IRsend_test.h" +#include "IRsend_test.h" #endif -#include "../src/IRutils.h" +#include "IRutils.h" /// Native representation of a Sharp A/C message. union SharpProtocol{ diff --git a/lib/IRremoteESP8266/src/ir_Sherwood.cpp b/lib/IRremoteESP8266/src/ir_Sherwood.cpp new file mode 100644 index 0000000000..76ffc35ff0 --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_Sherwood.cpp @@ -0,0 +1,24 @@ +// Copyright 2017 David Conran + +/// @file +/// @brief Support for Sherwood protocols. + +// Supports: +// Brand: Sherwood, Model: RC-138 remote +// Brand: Sherwood, Model: RD6505(B) Receiver + +#include +#include "IRsend.h" + +#if SEND_SHERWOOD +/// Send an IR command to a Sherwood device. +/// 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 Sherwood remote codes appear to be NEC codes with a mandatory repeat +/// code. i.e. repeat should be >= kSherwoodMinRepeat (1). +void IRsend::sendSherwood(uint64_t data, uint16_t nbits, uint16_t repeat) { + sendNEC(data, nbits, std::max((uint16_t)kSherwoodMinRepeat, repeat)); +} +#endif // SEND_SHERWOOD diff --git a/lib/IRremoteESP8266/src/ir_Symphony.cpp b/lib/IRremoteESP8266/src/ir_Symphony.cpp new file mode 100644 index 0000000000..b629ef72d3 --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_Symphony.cpp @@ -0,0 +1,95 @@ +// Copyright 2020 David Conran + +/// @file +/// @brief Support for Symphony protocols. +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1057 +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1105 +/// @see https://www.alldatasheet.com/datasheet-pdf/pdf/124369/ANALOGICTECH/SM5021B.html + +// Supports: +// Brand: Symphony, Model: Air Cooler 3Di +// Brand: SamHop, Model: SM3015 Fan Remote Control +// Brand: SamHop, Model: SM5021 Encoder chip +// Brand: SamHop, Model: SM5032 Decoder chip +// Brand: Blyss, Model: Owen-SW-5 3 Fan +// Brand: Blyss, Model: WP-YK8 090218 remote +// Brand: Westinghouse, Model: Ceiling fan +// Brand: Westinghouse, Model: 78095 Remote +// Brand: Satellite Electronic, Model: ID6 Remote +// Brand: Satellite Electronic, Model: JY199I Fan driver +// Brand: Satellite Electronic, Model: JY199I-L Fan driver +// Brand: SilverCrest, Model: SSVS 85 A1 Fan + +// Known Codes: +// SilverCrest SSVS 85 A1 Fan: +// 0x581 - On/Off +// 0x582 - Speed +// 0x584 - Mist +// 0x588 - Timer +// 0x590 - OSC + +#include +#include "IRrecv.h" +#include "IRsend.h" +#include "IRtimer.h" +#include "IRutils.h" + +// Constants +const uint16_t kSymphonyZeroMark = 400; +const uint16_t kSymphonyZeroSpace = 1250; +const uint16_t kSymphonyOneMark = kSymphonyZeroSpace; +const uint16_t kSymphonyOneSpace = kSymphonyZeroMark; +const uint32_t kSymphonyFooterGap = 4 * (kSymphonyZeroMark + + kSymphonyZeroSpace); + +#if SEND_SYMPHONY +/// Send a Symphony packet. +/// 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. +void IRsend::sendSymphony(uint64_t data, uint16_t nbits, uint16_t repeat) { + sendGeneric(0, 0, + kSymphonyOneMark, kSymphonyOneSpace, + kSymphonyZeroMark, kSymphonyZeroSpace, + 0, kSymphonyFooterGap, + data, nbits, 38000, true, repeat, kDutyDefault); +} +#endif // SEND_SYMPHONY + +#if DECODE_SYMPHONY +/// Decode the supplied Symphony packet/message. +/// Status: STABLE / Should 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::decodeSymphony(decode_results *results, uint16_t offset, + const uint16_t nbits, const bool strict) { + uint64_t data = 0; + + if (results->rawlen < 2 * nbits + offset - 1) + return false; // Not enough entries to ever be SYMPHONY. + // Compliance + if (strict && nbits != kSymphonyBits) return false; + + if (!matchGenericConstBitTime(results->rawbuf + offset, &data, + results->rawlen - offset, + nbits, + 0, 0, // No Header + kSymphonyOneMark, kSymphonyZeroMark, + 0, kSymphonyFooterGap, true, + _tolerance, 0)) + return false; + + // Success + results->value = data; + results->decode_type = decode_type_t::SYMPHONY; + results->bits = nbits; + results->address = 0; + results->command = 0; + return true; +} +#endif // DECODE_SYMPHONY diff --git a/lib/IRremoteESP8266/src/ir_Tcl.cpp b/lib/IRremoteESP8266/src/ir_Tcl.cpp new file mode 100644 index 0000000000..a9e8784a39 --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_Tcl.cpp @@ -0,0 +1,529 @@ +// Copyright 2019, 2021 David Conran + +/// @file +/// @brief Support for TCL protocols. + +#include "ir_Tcl.h" +#include +#include +#ifndef ARDUINO +#include +#endif +#include "IRremoteESP8266.h" +#include "IRtext.h" +#include "IRutils.h" + +// Constants + +const uint8_t kTcl112AcTimerResolution = 20; // Minutes +const uint16_t kTcl112AcTimerMax = 720; // Minutes (12 hrs) + +using irutils::addBoolToString; +using irutils::addFanToString; +using irutils::addIntToString; +using irutils::addLabeledString; +using irutils::addModeToString; +using irutils::addModelToString; +using irutils::addSwingVToString; +using irutils::addTempFloatToString; +using irutils::minsToString; + +#if SEND_TCL112AC +/// Send a TCL 112-bit A/C message. +/// 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::sendTcl112Ac(const unsigned char data[], const uint16_t nbytes, + const uint16_t repeat) { + sendGeneric(kTcl112AcHdrMark, kTcl112AcHdrSpace, + kTcl112AcBitMark, kTcl112AcOneSpace, + kTcl112AcBitMark, kTcl112AcZeroSpace, + kTcl112AcBitMark, kTcl112AcGap, + data, nbytes, 38000, false, repeat, 50); +} +#endif // SEND_TCL112AC + +/// 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? +IRTcl112Ac::IRTcl112Ac(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 IRTcl112Ac::begin(void) { _irsend.begin(); } + +#if SEND_TCL112AC +/// Send the current internal state as an IR message. +/// @param[in] repeat Nr. of times the message will be repeated. +void IRTcl112Ac::send(const uint16_t repeat) { + uint8_t save[kTcl112AcStateLength]; + // Do we need to send the special "quiet" message? + if (_quiet != _quiet_prev) { + // Backup the current state. + std::memcpy(save, _.raw, kTcl112AcStateLength); + const uint8_t quiet_off[kTcl112AcStateLength] = { + 0x23, 0xCB, 0x26, 0x02, 0x00, 0x40, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x65}; + // Use a known good quiet/mute off/type 2 state for the time being. + // Ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/1528#issuecomment-876989044 + setRaw(quiet_off); + setQuiet(_quiet); + // Send it. + _irsend.sendTcl112Ac(getRaw(), kTcl112AcStateLength, repeat); + // Now it's been sent, update the quiet previous state. + _quiet_prev = _quiet; + // Restore the old state. + setRaw(save); + // Make sure it looks like a normal TCL mesg if needed. + if (_.MsgType == kTcl112AcNormal) _.isTcl = true; + } + // Send the normal (type 1) state. + _irsend.sendTcl112Ac(getRaw(), kTcl112AcStateLength, repeat); +} +#endif // SEND_TCL112AC + +/// 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 IRTcl112Ac::calcChecksum(uint8_t state[], const uint16_t length) { + if (length) { + if (length > 4 && state[3] == 0x02) { // Special nessage? + return sumBytes(state, length - 1, 0xF); // Checksum needs an offset. + } else { + return sumBytes(state, length - 1); + } + } else { + return 0; + } +} + +/// Calculate & set the checksum for the current internal state of the remote. +/// @param[in] length The length/size of the internal array to checksum. +void IRTcl112Ac::checksum(const uint16_t length) { + // Stored the checksum value in the last byte. + if (length > 1) + _.Sum = calcChecksum(_.raw, 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/size of the array. +/// @return true, if the state has a valid checksum. Otherwise, false. +bool IRTcl112Ac::validChecksum(uint8_t state[], const uint16_t length) { + return (length > 1 && state[length - 1] == calcChecksum(state, length)); +} + +/// Check the supplied state looks like a TCL112AC message. +/// @param[in] state The array to verify the checksum of. +/// @note Assumes the state is the correct size. +/// @return true, if the state looks like a TCL112AC message. Otherwise, false. +/// @warning This is just a guess. +bool IRTcl112Ac::isTcl(const uint8_t state[]) { + Tcl112Protocol mesg; + std::memcpy(mesg.raw, state, kTcl112AcStateLength); + return (mesg.MsgType != kTcl112AcNormal) || mesg.isTcl; +} + +/// Reset the internal state of the emulation. (On, Cool, 24C) +void IRTcl112Ac::stateReset(void) { + // A known good state. (On, Cool, 24C) + static const uint8_t reset[kTcl112AcStateLength] = { + 0x23, 0xCB, 0x26, 0x01, 0x00, 0x24, 0x03, 0x07, 0x40, 0x00, 0x00, 0x00, + 0x00, 0x03}; + std::memcpy(_.raw, reset, kTcl112AcStateLength); + _quiet = false; + _quiet_prev = false; + _quiet_explictly_set = false; +} + +/// Get/Detect the model of the A/C. +/// @return The enum of the compatible model. +tcl_ac_remote_model_t IRTcl112Ac::getModel(void) const { + return isTcl(_.raw) ? tcl_ac_remote_model_t::TAC09CHSD + : tcl_ac_remote_model_t::GZ055BE1; +} + +/// Set the model of the A/C to emulate. +/// @param[in] model The enum of the appropriate model. +void IRTcl112Ac::setModel(const tcl_ac_remote_model_t model) { + _.isTcl = (model != tcl_ac_remote_model_t::GZ055BE1); +} + +/// 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* IRTcl112Ac::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 IRTcl112Ac::setRaw(const uint8_t new_code[], const uint16_t length) { + std::memcpy(_.raw, new_code, std::min(length, kTcl112AcStateLength)); +} + +/// Set the requested power state of the A/C to on. +void IRTcl112Ac::on(void) { setPower(true); } + +/// Set the requested power state of the A/C to off. +void IRTcl112Ac::off(void) { setPower(false); } + +/// Change the power setting. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRTcl112Ac::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 IRTcl112Ac::getPower(void) const { return _.Power; } + +/// Get the operating mode setting of the A/C. +/// @return The current operating mode setting. +uint8_t IRTcl112Ac::getMode(void) const { return _.Mode; } + +/// Set the operating mode of the A/C. +/// @param[in] mode The desired operating mode. +/// @note Fan/Ventilation mode sets the fan speed to high. +/// Unknown values default to Auto. +void IRTcl112Ac::setMode(const uint8_t mode) { + // If we get an unexpected mode, default to AUTO. + switch (mode) { + case kTcl112AcFan: + setFan(kTcl112AcFanHigh); + // FALLTHRU + case kTcl112AcAuto: + case kTcl112AcCool: + case kTcl112AcHeat: + case kTcl112AcDry: + _.Mode = mode; + break; + default: + _.Mode = kTcl112AcAuto; + } +} + +/// Set the temperature. +/// @param[in] celsius The temperature in degrees celsius. +/// @note The temperature resolution is 0.5 of a degree. +void IRTcl112Ac::setTemp(const float celsius) { + // Make sure we have desired temp in the correct range. + float safecelsius = std::max(celsius, kTcl112AcTempMin); + safecelsius = std::min(safecelsius, kTcl112AcTempMax); + // Convert to integer nr. of half degrees. + uint8_t nrHalfDegrees = safecelsius * 2; + // Do we have a half degree celsius? + _.HalfDegree = nrHalfDegrees & 1; + _.Temp = static_cast(kTcl112AcTempMax - nrHalfDegrees / 2); +} + +/// Get the current temperature setting. +/// @return The current setting for temp. in degrees celsius. +/// @note The temperature resolution is 0.5 of a degree. +float IRTcl112Ac::getTemp(void) const { + float result = kTcl112AcTempMax - _.Temp; + if (_.HalfDegree) result += 0.5; + return result; +} + +/// Set the speed of the fan. +/// @param[in] speed The desired setting. +/// @note Unknown speeds will default to Auto. +void IRTcl112Ac::setFan(const uint8_t speed) { + switch (speed) { + case kTcl112AcFanAuto: + case kTcl112AcFanMin: + case kTcl112AcFanLow: + case kTcl112AcFanMed: + case kTcl112AcFanHigh: + _.Fan = speed; + break; + default: + _.Fan = kTcl112AcFanAuto; + } +} + +/// Get the current fan speed setting. +/// @return The current fan speed/mode. +uint8_t IRTcl112Ac::getFan(void) const { return _.Fan; } + +/// Set the economy setting of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRTcl112Ac::setEcono(const bool on) { _.Econo = on; } + +/// Get the economy setting of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRTcl112Ac::getEcono(void) const { return _.Econo; } + +/// Set the Health (Filter) setting of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRTcl112Ac::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 IRTcl112Ac::getHealth(void) const { return _.Health; } + +/// Set the Light (LED/Display) setting of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRTcl112Ac::setLight(const bool on) { _.Light = !on; } // Cleared when on. + +/// Get the Light (LED/Display) setting of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRTcl112Ac::getLight(void) const { return !_.Light; } + +/// Set the horizontal swing setting of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRTcl112Ac::setSwingHorizontal(const bool on) { _.SwingH = on; } + +/// Get the horizontal swing setting of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRTcl112Ac::getSwingHorizontal(void) const { return _.SwingH; } + +/// Set the vertical swing setting of the A/C. +/// @param[in] setting The value of the desired setting. +void IRTcl112Ac::setSwingVertical(const uint8_t setting) { + switch (setting) { + case kTcl112AcSwingVOff: + case kTcl112AcSwingVHighest: + case kTcl112AcSwingVHigh: + case kTcl112AcSwingVMiddle: + case kTcl112AcSwingVLow: + case kTcl112AcSwingVLowest: + case kTcl112AcSwingVOn: + _.SwingV = setting; + } +} + +/// Get the vertical swing setting of the A/C. +/// @return The current setting. +uint8_t IRTcl112Ac::getSwingVertical(void) const { return _.SwingV; } + +/// Set the Turbo setting of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRTcl112Ac::setTurbo(const bool on) { + _.Turbo = on; + if (on) { + _.Fan = kTcl112AcFanHigh; + _.SwingV = kTcl112AcSwingVOn; + } +} + +/// Get the Turbo setting of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRTcl112Ac::getTurbo(void) const { return _.Turbo; } + +/// Set the Quiet setting of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRTcl112Ac::setQuiet(const bool on) { + _quiet_explictly_set = true; + _quiet = on; + if (_.MsgType == kTcl112AcSpecial) _.Quiet = on; +} + +/// Get the Quiet setting of the A/C. +/// @param[in] def The default value to use if we are not sure. +/// @return true, the setting is on. false, the setting is off. +bool IRTcl112Ac::getQuiet(const bool def) const { + if (_.MsgType == kTcl112AcSpecial) + return _.Quiet; + else + return _quiet_explictly_set ? _quiet : def; +} + +/// Get how long the On Timer is set for, in minutes. +/// @return The time in nr of minutes. +uint16_t IRTcl112Ac::getOnTimer(void) const { + return _.OnTimer * kTcl112AcTimerResolution; +} + +/// Set or cancel the On Timer function. +/// @param[in] mins Nr. of minutes the timer is to be set to. +/// @note Rounds down to 20 min increments. (max: 720 mins (12h), 0 is Off) +void IRTcl112Ac::setOnTimer(const uint16_t mins) { + _.OnTimer = std::min(mins, kTcl112AcTimerMax) / kTcl112AcTimerResolution; + _.OnTimerEnabled = _.OnTimer > 0; + _.TimerIndicator = _.OnTimerEnabled || _.OffTimerEnabled; +} + +/// Get how long the Off Timer is set for, in minutes. +/// @return The time in nr of minutes. +uint16_t IRTcl112Ac::getOffTimer(void) const { + return _.OffTimer * kTcl112AcTimerResolution; +} + +/// Set or cancel the Off Timer function. +/// @param[in] mins Nr. of minutes the timer is to be set to. +/// @note Rounds down to 20 min increments. (max: 720 mins (12h), 0 is Off) +void IRTcl112Ac::setOffTimer(const uint16_t mins) { + _.OffTimer = std::min(mins, kTcl112AcTimerMax) / kTcl112AcTimerResolution; + _.OffTimerEnabled = _.OffTimer > 0; + _.TimerIndicator = _.OnTimerEnabled || _.OffTimerEnabled; +} + +/// 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 IRTcl112Ac::convertMode(const stdAc::opmode_t mode) { + switch (mode) { + case stdAc::opmode_t::kCool: return kTcl112AcCool; + case stdAc::opmode_t::kHeat: return kTcl112AcHeat; + case stdAc::opmode_t::kDry: return kTcl112AcDry; + case stdAc::opmode_t::kFan: return kTcl112AcFan; + default: return kTcl112AcAuto; + } +} + +/// 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 IRTcl112Ac::convertFan(const stdAc::fanspeed_t speed) { + switch (speed) { + case stdAc::fanspeed_t::kMin: return kTcl112AcFanMin; + case stdAc::fanspeed_t::kLow: return kTcl112AcFanLow; + case stdAc::fanspeed_t::kMedium: return kTcl112AcFanMed; + case stdAc::fanspeed_t::kHigh: + case stdAc::fanspeed_t::kMax: return kTcl112AcFanHigh; + default: return kTcl112AcFanAuto; + } +} + +/// 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 IRTcl112Ac::toCommonMode(const uint8_t mode) { + switch (mode) { + case kTcl112AcCool: return stdAc::opmode_t::kCool; + case kTcl112AcHeat: return stdAc::opmode_t::kHeat; + case kTcl112AcDry: return stdAc::opmode_t::kDry; + case kTcl112AcFan: return stdAc::opmode_t::kFan; + default: return stdAc::opmode_t::kAuto; + } +} + +/// 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 IRTcl112Ac::convertSwingV(const stdAc::swingv_t position) { + switch (position) { + case stdAc::swingv_t::kOff: return kTcl112AcSwingVOff; + case stdAc::swingv_t::kHighest: return kTcl112AcSwingVHighest; + case stdAc::swingv_t::kHigh: return kTcl112AcSwingVHigh; + case stdAc::swingv_t::kMiddle: return kTcl112AcSwingVMiddle; + case stdAc::swingv_t::kLow: return kTcl112AcSwingVLow; + case stdAc::swingv_t::kLowest: return kTcl112AcSwingVLowest; + default: return kTcl112AcSwingVOn; + } +} + +/// Convert a native fan speed into its stdAc equivalent. +/// @param[in] spd The native setting to be converted. +/// @return The stdAc equivalent of the native setting. +stdAc::fanspeed_t IRTcl112Ac::toCommonFanSpeed(const uint8_t spd) { + switch (spd) { + case kTcl112AcFanHigh: return stdAc::fanspeed_t::kMax; + case kTcl112AcFanMed: return stdAc::fanspeed_t::kMedium; + case kTcl112AcFanLow: return stdAc::fanspeed_t::kLow; + case kTcl112AcFanMin: return stdAc::fanspeed_t::kMin; + default: return stdAc::fanspeed_t::kAuto; + } +} + +/// Convert a native vertical swing postion to it's common equivalent. +/// @param[in] setting A native position to convert. +/// @return The common vertical swing position. +stdAc::swingv_t IRTcl112Ac::toCommonSwingV(const uint8_t setting) { + switch (setting) { + case kTcl112AcSwingVOff: return stdAc::swingv_t::kOff; + default: return stdAc::swingv_t::kAuto; + } +} +/// 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 IRTcl112Ac::toCommon(const stdAc::state_t *prev) const { + stdAc::state_t result; + // Start with the previous state if given it. + if (prev != NULL) result = *prev; + result.protocol = decode_type_t::TCL112AC; + result.model = getModel(); + result.quiet = getQuiet(result.quiet); + // The rest only get updated if it is a "normal" message. + if (_.MsgType == kTcl112AcNormal) { + result.power = _.Power; + result.mode = toCommonMode(_.Mode); + result.celsius = true; + result.degrees = getTemp(); + result.fanspeed = toCommonFanSpeed(_.Fan); + result.swingv = toCommonSwingV(_.SwingV); + result.swingh = _.SwingH ? stdAc::swingh_t::kAuto : stdAc::swingh_t::kOff; + result.turbo = _.Turbo; + result.filter = _.Health; + result.econo = _.Econo; + result.light = getLight(); + } + // Not supported. + 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 IRTcl112Ac::toString(void) const { + String result = ""; + result.reserve(220); // Reserve some heap for the string to reduce fragging. + tcl_ac_remote_model_t model = getModel(); + result += addModelToString(decode_type_t::TCL112AC, model, false); + result += addIntToString(_.MsgType, D_STR_TYPE); + switch (_.MsgType) { + case kTcl112AcNormal: + result += addBoolToString(_.Power, kPowerStr); + result += addModeToString(_.Mode, kTcl112AcAuto, kTcl112AcCool, + kTcl112AcHeat, kTcl112AcDry, kTcl112AcFan); + result += addTempFloatToString(getTemp()); + result += addFanToString(_.Fan, kTcl112AcFanHigh, kTcl112AcFanLow, + kTcl112AcFanAuto, kTcl112AcFanMin, + kTcl112AcFanMed); + result += addSwingVToString(_.SwingV, kTcl112AcSwingVOff, + kTcl112AcSwingVHighest, + kTcl112AcSwingVHigh, + 0xFF, // unused + kTcl112AcSwingVMiddle, + 0xFF, // unused + kTcl112AcSwingVLow, + kTcl112AcSwingVLowest, + kTcl112AcSwingVOff, + kTcl112AcSwingVOn, // Swing + 0xFF, 0xFF); // Both Unused + if (model != tcl_ac_remote_model_t::GZ055BE1) { + result += addBoolToString(_.SwingH, kSwingHStr); + result += addBoolToString(_.Econo, kEconoStr); + result += addBoolToString(_.Health, kHealthStr); + result += addBoolToString(_.Turbo, kTurboStr); + result += addBoolToString(getLight(), kLightStr); + } + result += addLabeledString( + _.OnTimerEnabled ? minsToString(getOnTimer()) : kOffStr, + kOnTimerStr); + result += addLabeledString( + _.OffTimerEnabled ? minsToString(getOffTimer()) : kOffStr, + kOffTimerStr); + break; + case kTcl112AcSpecial: + result += addBoolToString(_.Quiet, kQuietStr); + break; + } + return result; +} + +#if DECODE_TCL112AC +/// @file +/// @note There is no `decodedecodeTcl112Ac()`. +/// It's the same as `decodeMitsubishi112()`. A shared routine is used. +/// You can find it in: ir_Mitsubishi.cpp +#endif // DECODE_TCL112AC diff --git a/lib/IRremoteESP8266/src/ir_Tcl.h b/lib/IRremoteESP8266/src/ir_Tcl.h index 34bbcb3055..c7ae038d03 100644 --- a/lib/IRremoteESP8266/src/ir_Tcl.h +++ b/lib/IRremoteESP8266/src/ir_Tcl.h @@ -17,11 +17,11 @@ #ifndef UNIT_TEST #include #endif -#include "../src/IRremoteESP8266.h" -#include "../src/IRsend.h" -#include "../src/IRrecv.h" +#include "IRremoteESP8266.h" +#include "IRsend.h" +#include "IRrecv.h" #ifdef UNIT_TEST -#include "../src/IRsend_test.h" +#include "IRsend_test.h" #endif /// Native representation of a TCL 112 A/C message. diff --git a/lib/IRremoteESP8266/src/ir_Technibel.cpp b/lib/IRremoteESP8266/src/ir_Technibel.cpp new file mode 100644 index 0000000000..d58cc7e055 --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_Technibel.cpp @@ -0,0 +1,408 @@ +// Copyright 2020 Quentin Briollant + +/// @file +/// @brief Support for Technibel protocol. + +#include "ir_Technibel.h" +#include "IRrecv.h" +#include "IRsend.h" +#include "IRtext.h" +#include "IRutils.h" +#include + +using irutils::addBoolToString; +using irutils::addModeToString; +using irutils::addFanToString; +using irutils::addLabeledString; +using irutils::addTempToString; +using irutils::minsToString; + +const uint16_t kTechnibelAcHdrMark = 8836; +const uint16_t kTechnibelAcHdrSpace = 4380; +const uint16_t kTechnibelAcBitMark = 523; +const uint16_t kTechnibelAcOneSpace = 1696; +const uint16_t kTechnibelAcZeroSpace = 564; +const uint32_t kTechnibelAcGap = kDefaultMessageGap; +const uint16_t kTechnibelAcFreq = 38000; + + +#if SEND_TECHNIBEL_AC +/// Send an Technibel AC formatted message. +/// Status: STABLE / Reported as working on a real device. +/// @param[in] data containing the IR command. +/// @param[in] nbits Nr. of bits to send. usually kTechnibelAcBits +/// @param[in] repeat Nr. of times the message is to be repeated. +void IRsend::sendTechnibelAc(const uint64_t data, const uint16_t nbits, + const uint16_t repeat) { + sendGeneric(kTechnibelAcHdrMark, kTechnibelAcHdrSpace, + kTechnibelAcBitMark, kTechnibelAcOneSpace, + kTechnibelAcBitMark, kTechnibelAcZeroSpace, + kTechnibelAcBitMark, kTechnibelAcGap, + data, nbits, kTechnibelAcFreq, true, // LSB First. + repeat, kDutyDefault); +} +#endif // SEND_TECHNIBEL_AC + +#if DECODE_TECHNIBEL_AC +/// Status: STABLE / Reported as working on a real device +/// @param[in,out] results Ptr to 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 (kTechnibelAcBits). +/// @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::decodeTechnibelAc(decode_results *results, uint16_t offset, + const uint16_t nbits, const bool strict) { + // Compliance + if (strict && nbits != kTechnibelAcBits) { + return false; + } + + uint64_t data = 0; + + // Header + Data + Footer + if (!matchGeneric(results->rawbuf + offset, &data, + results->rawlen - offset, nbits, + kTechnibelAcHdrMark, kTechnibelAcHdrSpace, + kTechnibelAcBitMark, kTechnibelAcOneSpace, + kTechnibelAcBitMark, kTechnibelAcZeroSpace, + kTechnibelAcBitMark, kTechnibelAcGap, true, + _tolerance, kMarkExcess, true)) return false; + + // Compliance + if (strict && !IRTechnibelAc::validChecksum(data)) return false; + + // Success + results->decode_type = decode_type_t::TECHNIBEL_AC; + results->bits = nbits; + results->value = data; + results->command = 0; + results->address = 0; + return true; +} +#endif // DECODE_TECHNIBEL_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? +IRTechnibelAc::IRTechnibelAc(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 IRTechnibelAc::begin(void) { _irsend.begin(); } + +#if SEND_TECHNIBEL_AC +/// Send the current internal state as an IR message. +/// @param[in] repeat Nr. of times the message will be repeated. +void IRTechnibelAc::send(const uint16_t repeat) { + _irsend.sendTechnibelAc(getRaw(), kTechnibelAcBits, repeat); +} +#endif // SEND_TECHNIBEL_AC + +/// Compute the checksum of the supplied state. +/// @param[in] state A valid code for this protocol. +/// @return The calculated checksum of the supplied state. +uint8_t IRTechnibelAc::calcChecksum(const uint64_t state) { + uint8_t sum = 0; + // Add up all the 8 bit data chunks. + for (uint8_t offset = kTechnibelAcTimerHoursOffset; + offset < kTechnibelAcHeaderOffset; offset += 8) + sum += GETBITS64(state, offset, 8); + return ~sum + 1; +} + +/// Confirm the checksum of the supplied state is valid. +/// @param[in] state A valid code for this protocol. +/// @return `true` if the checksum is correct, otherwise `false`. +bool IRTechnibelAc::validChecksum(const uint64_t state) { + TechnibelProtocol p{.raw = state}; + return calcChecksum(state) == p.Sum; +} + +/// Set the checksum of the internal state. +void IRTechnibelAc::checksum(void) { + _.Sum = calcChecksum(_.raw); +} + +/// Reset the internal state of the emulation. +/// @note Mode:Cool, Power:Off, fan:Low, temp:20, swing:Off, sleep:Off +void IRTechnibelAc::stateReset(void) { + _.raw = kTechnibelAcResetState; + _saved_temp = 20; // DegC (Random reasonable default value) + _saved_temp_units = 0; // Celsius +} + +/// Get a copy of the internal state/code for this protocol. +/// @return A code for this protocol based on the current internal state. +uint64_t IRTechnibelAc::getRaw(void) { + checksum(); + return _.raw; +} + +/// Set the internal state from a valid code for this protocol. +/// @param[in] state A valid code for this protocol. +void IRTechnibelAc::setRaw(const uint64_t state) { + _.raw = state; +} + +/// Set the requested power state of the A/C to on. +void IRTechnibelAc::on(void) { setPower(true); } + +/// Set the requested power state of the A/C to off. +void IRTechnibelAc::off(void) { setPower(false); } + +/// Change the power setting. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRTechnibelAc::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 IRTechnibelAc::getPower(void) const { + return _.Power; +} + +/// Set the temperature unit setting. +/// @param[in] fahrenheit true, the unit is °F. false, the unit is °C. +void IRTechnibelAc::setTempUnit(const bool fahrenheit) { + _saved_temp_units = fahrenheit; + _.UseFah = fahrenheit; +} + +/// Get the temperature unit setting. +/// @return true, the unit is °F. false, the unit is °C. +bool IRTechnibelAc::getTempUnit(void) const { + return _.UseFah; +} + +/// Set the temperature. +/// @param[in] degrees The temperature in degrees. +/// @param[in] fahrenheit The temperature unit: true=°F, false(default)=°C. +void IRTechnibelAc::setTemp(const uint8_t degrees, const bool fahrenheit) { + setTempUnit(fahrenheit); + uint8_t temp_min = fahrenheit ? kTechnibelAcTempMinF : kTechnibelAcTempMinC; + uint8_t temp_max = fahrenheit ? kTechnibelAcTempMaxF : kTechnibelAcTempMaxC; + _saved_temp = std::min(temp_max, std::max(temp_min, degrees)); + _.Temp = _saved_temp; +} + +/// Get the current temperature setting. +/// @return The current setting for temp. in degrees. +uint8_t IRTechnibelAc::getTemp(void) const { + return _.Temp; +} + +/// Set the speed of the fan. +/// @param[in] speed The desired setting. +void IRTechnibelAc::setFan(const uint8_t speed) { + // Mode fan speed rules. + if (_.Mode == kTechnibelAcDry && speed != kTechnibelAcFanLow) { + _.Fan = kTechnibelAcFanLow; + return; + } + switch (speed) { + case kTechnibelAcFanHigh: + case kTechnibelAcFanMedium: + case kTechnibelAcFanLow: + _.Fan = speed; + break; + default: + _.Fan = kTechnibelAcFanLow; + } +} + +/// Get the current fan speed setting. +/// @return The current fan speed/mode. +uint8_t IRTechnibelAc::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 IRTechnibelAc::convertFan(const stdAc::fanspeed_t speed) { + switch (speed) { + case stdAc::fanspeed_t::kMin: + case stdAc::fanspeed_t::kLow: return kTechnibelAcFanLow; + case stdAc::fanspeed_t::kMedium: return kTechnibelAcFanMedium; + case stdAc::fanspeed_t::kHigh: + case stdAc::fanspeed_t::kMax: return kTechnibelAcFanHigh; + default: return kTechnibelAcFanLow; + } +} + +/// 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 IRTechnibelAc::toCommonFanSpeed(const uint8_t speed) { + switch (speed) { + case kTechnibelAcFanHigh: return stdAc::fanspeed_t::kHigh; + case kTechnibelAcFanMedium: return stdAc::fanspeed_t::kMedium; + default: return stdAc::fanspeed_t::kLow; + } +} + +/// Get the operating mode setting of the A/C. +/// @return The current operating mode setting. +uint8_t IRTechnibelAc::getMode(void) const { + return _.Mode; +} + +/// Set the operating mode of the A/C. +/// @param[in] mode The desired operating mode. +void IRTechnibelAc::setMode(const uint8_t mode) { + _.Mode = mode; + switch (mode) { + case kTechnibelAcHeat: + case kTechnibelAcFan: + case kTechnibelAcDry: + case kTechnibelAcCool: + break; + default: + _.Mode = kTechnibelAcCool; + } + setFan(_.Fan); // Re-force any fan speed constraints. + // Restore previous temp settings for cool mode. + setTemp(_saved_temp, _saved_temp_units); +} + +/// 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 IRTechnibelAc::convertMode(const stdAc::opmode_t mode) { + switch (mode) { + case stdAc::opmode_t::kHeat: return kTechnibelAcHeat; + case stdAc::opmode_t::kDry: return kTechnibelAcDry; + case stdAc::opmode_t::kFan: return kTechnibelAcFan; + default: return kTechnibelAcCool; + } +} + +/// 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 IRTechnibelAc::toCommonMode(const uint8_t mode) { + switch (mode) { + case kTechnibelAcHeat: return stdAc::opmode_t::kHeat; + case kTechnibelAcDry: return stdAc::opmode_t::kDry; + case kTechnibelAcFan: return stdAc::opmode_t::kFan; + default: return stdAc::opmode_t::kCool; + } +} + +/// Set the (vertical) swing setting of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRTechnibelAc::setSwing(const bool on) { + _.Swing = on; +} + +/// Get the (vertical) swing setting of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRTechnibelAc::getSwing(void) const { + return _.Swing; +} + +/// Convert a stdAc::swingv_t enum into it's native swing. +/// @param[in] swing The enum to be converted. +/// @return true, the swing is on. false, the swing is off. +bool IRTechnibelAc::convertSwing(const stdAc::swingv_t swing) { + return swing != stdAc::swingv_t::kOff; +} + +/// Convert a native swing into its stdAc equivalent. +/// @param[in] swing true, the swing is on. false, the swing is off. +/// @return The stdAc equivalent of the native setting. +stdAc::swingv_t IRTechnibelAc::toCommonSwing(const bool swing) { + return swing ? stdAc::swingv_t::kAuto : stdAc::swingv_t::kOff; +} + +/// Set the Sleep setting of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRTechnibelAc::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 IRTechnibelAc::getSleep(void) const { + return _.Sleep; +} + +/// Set the enable timer setting. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRTechnibelAc::setTimerEnabled(const bool on) { + _.TimerEnable = on; +} + +/// Is the timer function enabled? +/// @return true, the setting is on. false, the setting is off. +bool IRTechnibelAc::getTimerEnabled(void) const { + return _.TimerEnable; +} + +/// Set the timer for when the A/C unit will switch off. +/// @param[in] nr_of_mins Number of minutes before power off. +/// `0` will clear the timer. Max is 24 hrs (1440 mins). +/// @note Time is stored internally in hours. +void IRTechnibelAc::setTimer(const uint16_t nr_of_mins) { + const uint8_t hours = nr_of_mins / 60; + _.TimerHours = std::min(kTechnibelAcTimerMax, hours); + // Enable or not? + setTimerEnabled(hours); +} + +/// Get the timer time for when the A/C unit will switch power state. +/// @return The number of minutes left on the timer. `0` means off. +uint16_t IRTechnibelAc::getTimer(void) const { + return _.TimerEnable ? _.TimerHours * 60 : 0; +} + +/// Convert the current internal state into its stdAc::state_t equivalent. +/// @return The stdAc equivalent of the native settings. +stdAc::state_t IRTechnibelAc::toCommon(void) const { + stdAc::state_t result; + result.protocol = decode_type_t::TECHNIBEL_AC; + result.power = _.Power; + result.mode = toCommonMode(_.Mode); + result.celsius = !_.UseFah; + result.degrees = _.Temp; + result.fanspeed = toCommonFanSpeed(_.Fan); + result.sleep = _.Sleep ? 0 : -1; + result.swingv = toCommonSwing(_.Swing); + // Not supported. + result.model = -1; + result.turbo = false; + 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 IRTechnibelAc::toString(void) const { + String result = ""; + result.reserve(100); // Reserve some heap for the string to reduce fragging. + result += addBoolToString(_.Power, kPowerStr, false); + result += addModeToString(_.Mode, 255, // No Auto, so use impossible value + kTechnibelAcCool, kTechnibelAcHeat, kTechnibelAcDry, + kTechnibelAcFan); + result += addFanToString(_.Fan, kTechnibelAcFanHigh, kTechnibelAcFanLow, + kTechnibelAcFanLow, kTechnibelAcFanLow, + kTechnibelAcFanMedium); + result += addTempToString(_.Temp, !_.UseFah); + result += addBoolToString(_.Sleep, kSleepStr); + result += addBoolToString(_.Swing, kSwingVStr); + result += addLabeledString(_.TimerEnable ? minsToString(getTimer()) + : kOffStr, + kTimerStr); + return result; +} diff --git a/lib/IRremoteESP8266/src/ir_Technibel.h b/lib/IRremoteESP8266/src/ir_Technibel.h index decbee4e87..fb4a08717c 100644 --- a/lib/IRremoteESP8266/src/ir_Technibel.h +++ b/lib/IRremoteESP8266/src/ir_Technibel.h @@ -11,10 +11,10 @@ #ifndef UNIT_TEST #include #endif -#include "../src/IRremoteESP8266.h" -#include "../src/IRsend.h" +#include "IRremoteESP8266.h" +#include "IRsend.h" #ifdef UNIT_TEST -#include "../src/IRsend_test.h" +#include "IRsend_test.h" #endif // Supports: diff --git a/lib/IRremoteESP8266/src/ir_Teco.cpp b/lib/IRremoteESP8266/src/ir_Teco.cpp new file mode 100644 index 0000000000..031691eddd --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_Teco.cpp @@ -0,0 +1,375 @@ +// Copyright 2019 Fabien Valthier + +/// @file +/// @brief Support for Teco protocols. + +#include "ir_Teco.h" +#include +#include "IRremoteESP8266.h" +#include "IRtext.h" +#include "IRutils.h" +#ifndef ARDUINO +#include +#endif + +// Constants +// using SPACE modulation. +const uint16_t kTecoHdrMark = 9000; +const uint16_t kTecoHdrSpace = 4440; +const uint16_t kTecoBitMark = 620; +const uint16_t kTecoOneSpace = 1650; +const uint16_t kTecoZeroSpace = 580; +const uint32_t kTecoGap = kDefaultMessageGap; // Made-up value. Just a guess. + +using irutils::addBoolToString; +using irutils::addFanToString; +using irutils::addIntToString; +using irutils::addLabeledString; +using irutils::addModeToString; +using irutils::addTempToString; + +#if SEND_TECO +/// Send a Teco A/C 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::sendTeco(const uint64_t data, const uint16_t nbits, + const uint16_t repeat) { + sendGeneric(kTecoHdrMark, kTecoHdrSpace, kTecoBitMark, kTecoOneSpace, + kTecoBitMark, kTecoZeroSpace, kTecoBitMark, kTecoGap, + data, nbits, 38000, false, repeat, kDutyDefault); +} +#endif // SEND_TECO + +/// 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? +IRTecoAc::IRTecoAc(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 IRTecoAc::begin(void) { _irsend.begin(); } + +#if SEND_TECO +/// Send the current internal state as an IR message. +/// @param[in] repeat Nr. of times the message will be repeated. +void IRTecoAc::send(const uint16_t repeat) { + _irsend.sendTeco(_.raw, kTecoBits, repeat); +} +#endif // SEND_TECO + +/// Reset the internal state of the emulation. +/// @note Mode:auto, Power:Off, fan:auto, temp:16, swing:off, sleep:off +void IRTecoAc::stateReset(void) { + _.raw = kTecoReset; +} + +/// Get a copy of the internal state/code for this protocol. +/// @return A code for this protocol based on the current internal state. +uint64_t IRTecoAc::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 IRTecoAc::setRaw(const uint64_t new_code) { _.raw = new_code; } + +/// Set the requested power state of the A/C to on. +void IRTecoAc::on(void) { setPower(true); } + +/// Set the requested power state of the A/C to off. +void IRTecoAc::off(void) { setPower(false); } + +/// Change the power setting. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRTecoAc::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 IRTecoAc::getPower(void) const { + return _.Power; +} + +/// Set the temperature. +/// @param[in] temp The temperature in degrees celsius. +void IRTecoAc::setTemp(const uint8_t temp) { + uint8_t newtemp = temp; + newtemp = std::min(newtemp, kTecoMaxTemp); + newtemp = std::max(newtemp, kTecoMinTemp); + _.Temp = newtemp - kTecoMinTemp; +} + +/// Get the current temperature setting. +/// @return The current setting for temp. in degrees celsius. +uint8_t IRTecoAc::getTemp(void) const { + return _.Temp + kTecoMinTemp; +} + +/// Set the speed of the fan. +/// @param[in] speed The desired setting. +void IRTecoAc::setFan(const uint8_t speed) { + uint8_t newspeed = speed; + switch (speed) { + case kTecoFanAuto: + case kTecoFanHigh: + case kTecoFanMed: + case kTecoFanLow: break; + default: newspeed = kTecoFanAuto; + } + _.Fan = newspeed; +} + +/// Get the current fan speed setting. +/// @return The current fan speed/mode. +uint8_t IRTecoAc::getFan(void) const { + return _.Fan; +} + +/// Set the operating mode of the A/C. +/// @param[in] mode The desired operating mode. +void IRTecoAc::setMode(const uint8_t mode) { + uint8_t newmode = mode; + switch (mode) { + case kTecoAuto: + case kTecoCool: + case kTecoDry: + case kTecoFan: + case kTecoHeat: break; + default: newmode = kTecoAuto; + } + _.Mode = newmode; +} + +/// Get the operating mode setting of the A/C. +/// @return The current operating mode setting. +uint8_t IRTecoAc::getMode(void) const { + return _.Mode; +} + +/// Set the (vertical) swing setting of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRTecoAc::setSwing(const bool on) { + _.Swing = on; +} + +/// Get the (vertical) swing setting of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRTecoAc::getSwing(void) const { + return _.Swing; +} + +/// Set the Sleep setting of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRTecoAc::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 IRTecoAc::getSleep(void) const { + return _.Sleep; +} + +/// Set the Light (LED/Display) setting of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRTecoAc::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 IRTecoAc::getLight(void) const { + return _.Light; +} + +/// Set the Humid setting of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRTecoAc::setHumid(const bool on) { + _.Humid = on; +} + +/// Get the Humid setting of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRTecoAc::getHumid(void) const { + return _.Humid; +} + +/// Set the Save setting of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRTecoAc::setSave(const bool on) { + _.Save = on; +} + +/// Get the Save setting of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRTecoAc::getSave(void) const { + return _.Save; +} + +/// Is the timer function enabled? +/// @return true, the setting is on. false, the setting is off. +inline bool IRTecoAc::getTimerEnabled(void) const { + return _.TimerOn; +} + +/// Get the timer time for when the A/C unit will switch power state. +/// @return The number of minutes left on the timer. `0` means off. +uint16_t IRTecoAc::getTimer(void) const { + uint16_t mins = 0; + if (getTimerEnabled()) { + mins = (_.TensHours * 10 + _.UnitHours) * 60; + if (_.HalfHour) mins += 30; + } + return mins; +} + +/// Set the timer for when the A/C unit will switch power state. +/// @param[in] nr_mins Number of minutes before power state change. +/// `0` will clear the timer. Max is 24 hrs. +/// @note Time is stored internally in increments of 30 mins. +void IRTecoAc::setTimer(const uint16_t nr_mins) { + uint16_t mins = std::min(nr_mins, (uint16_t)(24 * 60)); // Limit to 24 hrs. + uint8_t hours = mins / 60; + _.TimerOn = mins > 0; // Set the timer flag. + _.HalfHour = (mins % 60) >= 30; + _.UnitHours = hours % 10; + _.TensHours = hours / 10; +} + +/// 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 IRTecoAc::convertMode(const stdAc::opmode_t mode) { + switch (mode) { + case stdAc::opmode_t::kCool: return kTecoCool; + case stdAc::opmode_t::kHeat: return kTecoHeat; + case stdAc::opmode_t::kDry: return kTecoDry; + case stdAc::opmode_t::kFan: return kTecoFan; + default: return kTecoAuto; + } +} + +/// 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 IRTecoAc::convertFan(const stdAc::fanspeed_t speed) { + switch (speed) { + case stdAc::fanspeed_t::kMin: + case stdAc::fanspeed_t::kLow: return kTecoFanLow; + case stdAc::fanspeed_t::kMedium: return kTecoFanMed; + case stdAc::fanspeed_t::kHigh: + case stdAc::fanspeed_t::kMax: return kTecoFanHigh; + default: return kTecoFanAuto; + } +} + +/// 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 IRTecoAc::toCommonMode(const uint8_t mode) { + switch (mode) { + case kTecoCool: return stdAc::opmode_t::kCool; + case kTecoHeat: return stdAc::opmode_t::kHeat; + case kTecoDry: return stdAc::opmode_t::kDry; + case kTecoFan: 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 IRTecoAc::toCommonFanSpeed(const uint8_t speed) { + switch (speed) { + case kTecoFanHigh: return stdAc::fanspeed_t::kMax; + case kTecoFanMed: return stdAc::fanspeed_t::kMedium; + case kTecoFanLow: 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 IRTecoAc::toCommon(void) const { + stdAc::state_t result; + result.protocol = decode_type_t::TECO; + result.model = -1; // Not supported. + result.power = _.Power; + result.mode = toCommonMode(_.Mode); + result.celsius = true; + result.degrees = getTemp(); + result.fanspeed = toCommonFanSpeed(_.Fan); + result.swingv = _.Swing ? stdAc::swingv_t::kAuto : stdAc::swingv_t::kOff; + result.sleep = _.Sleep ? 0 : -1; + result.light = _.Light; + // Not supported. + result.swingh = stdAc::swingh_t::kOff; + result.turbo = 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 IRTecoAc::toString(void) const { + String result = ""; + result.reserve(100); // Reserve some heap for the string to reduce fragging. + result += addBoolToString(_.Power, kPowerStr, false); + result += addModeToString(_.Mode, kTecoAuto, kTecoCool, kTecoHeat, + kTecoDry, kTecoFan); + result += addTempToString(getTemp()); + result += addFanToString(_.Fan, kTecoFanHigh, kTecoFanLow, + kTecoFanAuto, kTecoFanAuto, kTecoFanMed); + result += addBoolToString(_.Sleep, kSleepStr); + result += addBoolToString(_.Swing, kSwingStr); + result += addBoolToString(_.Light, kLightStr); + result += addBoolToString(_.Humid, kHumidStr); + result += addBoolToString(_.Save, kSaveStr); + if (getTimerEnabled()) + result += addLabeledString(irutils::minsToString(getTimer()), + kTimerStr); + else + result += addBoolToString(false, kTimerStr); + return result; +} + +#if DECODE_TECO +/// Decode the supplied Teco message. +/// Status: STABLE / Tested. +/// @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::decodeTeco(decode_results* results, uint16_t offset, + const uint16_t nbits, const bool strict) { + if (strict && nbits != kTecoBits) return false; // Not what is expected + + uint64_t data = 0; + // Match Header + Data + Footer + if (!matchGeneric(results->rawbuf + offset, &data, + results->rawlen - offset, nbits, + kTecoHdrMark, kTecoHdrSpace, + kTecoBitMark, kTecoOneSpace, + kTecoBitMark, kTecoZeroSpace, + kTecoBitMark, kTecoGap, true, + _tolerance, kMarkExcess, false)) return false; + + // Success + results->decode_type = TECO; + results->bits = nbits; + results->value = data; + results->address = 0; + results->command = 0; + return true; +} +#endif // DECODE_TECO diff --git a/lib/IRremoteESP8266/src/ir_Teco.h b/lib/IRremoteESP8266/src/ir_Teco.h index 0ce034a9ea..c2ea1c561e 100644 --- a/lib/IRremoteESP8266/src/ir_Teco.h +++ b/lib/IRremoteESP8266/src/ir_Teco.h @@ -13,10 +13,10 @@ #ifndef UNIT_TEST #include #endif -#include "../src/IRremoteESP8266.h" -#include "../src/IRsend.h" +#include "IRremoteESP8266.h" +#include "IRsend.h" #ifdef UNIT_TEST -#include "../src/IRsend_test.h" +#include "IRsend_test.h" #endif /// Native representation of a Teco A/C message. diff --git a/lib/IRremoteESP8266/src/ir_Teknopoint.cpp b/lib/IRremoteESP8266/src/ir_Teknopoint.cpp new file mode 100644 index 0000000000..0feee6d704 --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_Teknopoint.cpp @@ -0,0 +1,75 @@ +// Copyright 2021 David Conran (crankyoldgit) +/// @file +/// @brief Support for the Teknopoint protocol +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1486 + +// Supports: +// Brand: Teknopoint, Model: Allegro SSA-09H A/C +// Brand: Teknopoint, Model: GZ-055B-E1 remote + +#include "IRrecv.h" +#include "IRsend.h" +#include "IRutils.h" + +// Protocol timings +const uint16_t kTeknopointHdrMark = 3600; +const uint16_t kTeknopointBitMark = 477; +const uint16_t kTeknopointHdrSpace = 1600; +const uint16_t kTeknopointOneSpace = 1200; +const uint16_t kTeknopointZeroSpace = 530; +const uint16_t kTeknopointFreq = 38000; // Hz. (Guess Only) +const uint8_t kTeknopointExtraTol = 10; // Extra tolerance percentage. + +#if SEND_TEKNOPOINT +/// Send a Teknopoint formatted message. +/// Status: BETA / Probably works. +/// @param[in] data An array of bytes containing the IR command. +/// @param[in] nbytes Nr. of bytes of data in the array. +/// @param[in] repeat Nr. of times the message is to be repeated. +void IRsend::sendTeknopoint(const uint8_t data[], const uint16_t nbytes, + const uint16_t repeat) { + sendGeneric(kTeknopointHdrMark, kTeknopointHdrSpace, + kTeknopointBitMark, kTeknopointOneSpace, + kTeknopointBitMark, kTeknopointZeroSpace, + kTeknopointBitMark, kDefaultMessageGap, + data, nbytes, // Bytes + kTeknopointFreq, false, repeat, kDutyDefault); +} +#endif // SEND_TEKNOPOINT + +#if DECODE_TEKNOPOINT +/// Decode the supplied Teknopoint message. +/// Status: Alpha / 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::decodeTeknopoint(decode_results *results, uint16_t offset, + const uint16_t nbits, const bool strict) { + if (results->rawlen < 2 * nbits + kHeader + kFooter - 1 - offset) + return false; // Too short a message to match. + if (strict && nbits != kTeknopointBits) + return false; + + if (!matchGeneric(results->rawbuf + offset, results->state, + results->rawlen - offset, nbits, + kTeknopointHdrMark, kTeknopointHdrSpace, + kTeknopointBitMark, kTeknopointOneSpace, + kTeknopointBitMark, kTeknopointZeroSpace, + kTeknopointBitMark, kDefaultMessageGap, + true, _tolerance + kTeknopointExtraTol, + kMarkExcess, false)) return false; + // Compliance + if (strict) { + // Is the checksum valid? + if (sumBytes(results->state, kTeknopointStateLength - 1) != + results->state[kTeknopointStateLength - 1]) return false; + } + // Success + results->decode_type = decode_type_t::TEKNOPOINT; + results->bits = nbits; + return true; +} +#endif // DECODE_TEKNOPOINT diff --git a/lib/IRremoteESP8266/src/ir_Toshiba.h b/lib/IRremoteESP8266/src/ir_Toshiba.h index b48dab0de0..1314cf54de 100644 --- a/lib/IRremoteESP8266/src/ir_Toshiba.h +++ b/lib/IRremoteESP8266/src/ir_Toshiba.h @@ -34,10 +34,10 @@ #ifdef ARDUINO #include #endif -#include "../src/IRremoteESP8266.h" -#include "../src/IRsend.h" +#include "IRremoteESP8266.h" +#include "IRsend.h" #ifdef UNIT_TEST -#include "../src/IRsend_test.h" +#include "IRsend_test.h" #endif /// Native representation of a Toshiba A/C message. diff --git a/lib/IRremoteESP8266/src/ir_Transcold.cpp b/lib/IRremoteESP8266/src/ir_Transcold.cpp new file mode 100644 index 0000000000..14cc7f8bd2 --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_Transcold.cpp @@ -0,0 +1,500 @@ +// Copyright 2020 Chandrashekar Shetty (iamDshetty) +// Copyright 2020 crankyoldgit +// Copyright 2021 siriuslzx + +/// @file +/// @brief Support for Transcold A/C protocols. +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1256 + +#include "ir_Transcold.h" +#include +#ifndef ARDUINO +#include +#endif +#include "IRrecv.h" +#include "IRsend.h" +#include "IRtext.h" +#include "IRutils.h" + +// Constants + +const uint16_t kTranscoldHdrMark = 5944; ///< uSeconds. +const uint16_t kTranscoldBitMark = 555; ///< uSeconds. +const uint16_t kTranscoldHdrSpace = 7563; ///< uSeconds. +const uint16_t kTranscoldOneSpace = 3556; ///< uSeconds. +const uint16_t kTranscoldZeroSpace = 1526; ///< uSeconds. + +using irutils::addBoolToString; +using irutils::addIntToString; +using irutils::addLabeledString; +using irutils::addModeToString; +using irutils::addTempToString; +using irutils::addToggleToString; + +#if SEND_TRANSCOLD +/// Send a Transcold 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. +void IRsend::sendTranscold(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(kTranscoldHdrMark); + space(kTranscoldHdrSpace); + // 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; + uint8_t segment = GETBITS64(data, nbits - i, 8); + // Normal + Inverted + uint16_t both = (segment << 8) | (~segment & 0xFF); + sendData(kTranscoldBitMark, kTranscoldOneSpace, kTranscoldBitMark, + kTranscoldZeroSpace, both, 16, true); + } + // Footer + mark(kTranscoldBitMark); + space(kTranscoldHdrSpace); + mark(kTranscoldBitMark); + space(kDefaultMessageGap); + } +} +#endif // SEND_TRANSCOLD + +/// 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? +IRTranscoldAc::IRTranscoldAc(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 IRTranscoldAc::stateReset(void) { + setRaw(kTranscoldKnownGoodState); + special_state = kTranscoldOff; + swingFlag = false; + swingHFlag = false; + swingVFlag = false; +} + +/// Set up hardware to be able to send a message. +void IRTranscoldAc::begin(void) { _irsend.begin(); } + +#if SEND_TRANSCOLD +/// Send the current internal state as an IR message. +/// @param[in] repeat Nr. of times the message will be repeated. +void IRTranscoldAc::send(uint16_t repeat) { + _irsend.sendTranscold(getRaw(), kTranscoldBits, repeat); + if (isSpecialState()) { + // make sure to remove special state from special_state + // after command has being transmitted. + special_state = kTranscoldKnownGoodState; + } +} +#endif // SEND_TRANSCOLD + +/// 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 IRTranscoldAc::getRaw(void) const { + if (isSpecialState()) { + return special_state; + } + return _.raw; + } + +/// Set the internal state from a valid code for this protocol. +/// @param[in] new_code A valid code for this protocol. +void IRTranscoldAc::setRaw(const uint32_t new_code) { + if (handleSpecialState(new_code)) { + special_state = new_code; + _.raw = kTranscoldKnownGoodState; + } else { + // must be a command changing Temp|Mode|Fan + // it is safe to just copy to remote var + _.raw = new_code; + special_state = kTranscoldKnownGoodState; + // it isn`t special so might affect Temp|mode|Fan + if (new_code == kTranscoldCmdFan) { + setMode(kTranscoldFan); + } + } +} + +/// Is the current state is a special state? +/// @return true, if it is. false if it isn't. +bool IRTranscoldAc::isSpecialState(void) const { + switch (special_state) { + case kTranscoldOff: + case kTranscoldSwing: 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 +/// @return true, if it is a special state. false if it isn't. +bool IRTranscoldAc::handleSpecialState(const uint32_t data) { + switch (data) { + case kTranscoldOff: + break; + case kTranscoldSwing: + swingFlag = !swingFlag; + break; + default: + return false; + } + return true; +} + +/// Set the temperature. +/// @param[in] desired The temperature in degrees celsius. +void IRTranscoldAc::setTemp(const uint8_t desired) { + // Range check. + uint8_t temp = std::min(desired, kTranscoldTempMax); + temp = std::max(temp, kTranscoldTempMin) - kTranscoldTempMin + 1; + _.Temp = reverseBits(invertBits(temp, kTranscoldTempSize), + kTranscoldTempSize); +} + +/// Get the current temperature setting. +/// @return The current setting for temp. in degrees celsius. +uint8_t IRTranscoldAc::getTemp(void) const { + return reverseBits(invertBits(_.Temp, kTranscoldTempSize), + kTranscoldTempSize) + kTranscoldTempMin - 1; +} + +/// Get the value of the current power setting. +/// @return true, the setting is on. false, the setting is off. +bool IRTranscoldAc::getPower(void) const { + // There is only an off state. Everything else is "on". + return special_state != kTranscoldOff; +} + +/// Change the power setting. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRTranscoldAc::setPower(const bool on) { + if (!on) { + special_state = kTranscoldOff; + } else { + special_state = kTranscoldKnownGoodState; + } +} + +/// Change the power setting to On. +void IRTranscoldAc::on(void) { setPower(true); } + +/// Change the power setting to Off. +void IRTranscoldAc::off(void) { setPower(false); } + +/// Get the Swing setting of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRTranscoldAc::getSwing(void) const { return swingFlag; } + +/// Toggle the Swing mode of the A/C. +void IRTranscoldAc::setSwing(void) { + // Assumes that repeated sending "swing" toggles the action on the device. + // if not, the variable "swingFlag" can be removed. + special_state = kTranscoldSwing; + swingFlag = !swingFlag; +} + +/// Set the operating mode of the A/C. +/// @param[in] mode The desired operating mode. +void IRTranscoldAc::setMode(const uint8_t mode) { + uint32_t actualmode = mode; + switch (actualmode) { + case kTranscoldAuto: + case kTranscoldDry: + _.Fan = kTranscoldFanAuto0; + break; + case kTranscoldCool: + case kTranscoldHeat: + case kTranscoldFan: + _.Fan = kTranscoldFanAuto; + break; + default: // Anything else, go with Auto mode. + actualmode = kTranscoldAuto; + _.Fan = kTranscoldFanAuto0; + } + setTemp(getTemp()); + // Fan mode is a special case of Dry. + if (actualmode == kTranscoldFan) { + actualmode = kTranscoldDry; + _.Temp = kTranscoldFanTempCode; + } + _.Mode = actualmode; +} + +/// Get the operating mode setting of the A/C. +/// @return The current operating mode setting. +uint8_t IRTranscoldAc::getMode(void) const { + uint8_t mode = _.Mode; + if (mode == kTranscoldDry) + if (_.Temp == kTranscoldFanTempCode) return kTranscoldFan; + return mode; +} + +/// Get the current fan speed setting. +/// @return The current fan speed. +uint8_t IRTranscoldAc::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 IRTranscoldAc::setFan(const uint8_t speed, const bool modecheck) { + uint8_t newspeed = speed; + if (modecheck) { + switch (getMode()) { + case kTranscoldAuto: + case kTranscoldDry: // Dry & Auto mode can't have speed Auto. + if (speed == kTranscoldFanAuto) + newspeed = kTranscoldFanAuto0; + break; + default: // Only Dry & Auto mode can have speed Auto0. + if (speed == kTranscoldFanAuto0) + newspeed = kTranscoldFanAuto; + } + } + switch (speed) { + case kTranscoldFanAuto: + case kTranscoldFanAuto0: + case kTranscoldFanMin: + case kTranscoldFanMed: + case kTranscoldFanMax: + case kTranscoldFanZoneFollow: + case kTranscoldFanFixed: + break; + default: // Unknown speed requested. + newspeed = kTranscoldFanAuto; + break; + } + _.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 IRTranscoldAc::convertMode(const stdAc::opmode_t mode) { + switch (mode) { + case stdAc::opmode_t::kCool: return kTranscoldCool; + case stdAc::opmode_t::kHeat: return kTranscoldHeat; + case stdAc::opmode_t::kDry: return kTranscoldDry; + case stdAc::opmode_t::kFan: return kTranscoldFan; + default: return kTranscoldAuto; + } +} + +/// 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 IRTranscoldAc::convertFan(const stdAc::fanspeed_t speed) { + switch (speed) { + case stdAc::fanspeed_t::kMin: + case stdAc::fanspeed_t::kLow: return kTranscoldFanMin; + case stdAc::fanspeed_t::kMedium: return kTranscoldFanMed; + case stdAc::fanspeed_t::kHigh: + case stdAc::fanspeed_t::kMax: return kTranscoldFanMax; + default: return kTranscoldFanAuto; + } +} + +/// 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 IRTranscoldAc::toCommonMode(const uint8_t mode) { + switch (mode) { + case kTranscoldCool: return stdAc::opmode_t::kCool; + case kTranscoldHeat: return stdAc::opmode_t::kHeat; + case kTranscoldDry: return stdAc::opmode_t::kDry; + case kTranscoldFan: 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 IRTranscoldAc::toCommonFanSpeed(const uint8_t speed) { + switch (speed) { + case kTranscoldFanMax: return stdAc::fanspeed_t::kMax; + case kTranscoldFanMed: return stdAc::fanspeed_t::kMedium; + case kTranscoldFanMin: 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 IRTranscoldAc::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; + } + // Not supported. + result.model = -1; // No models used. + result.swingh = stdAc::swingh_t::kOff; + result.turbo = false; + result.clean = false; + result.light = false; + result.quiet = false; + result.econo = false; + result.filter = false; + result.beep = false; + result.clock = -1; + result.sleep = -1; + + // Supported. + result.protocol = decode_type_t::TRANSCOLD; + 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; + } + // Back to "normal" stateful messages. + result.mode = toCommonMode(getMode()); + result.degrees = getTemp(); + result.fanspeed = toCommonFanSpeed(_.Fan); + return result; +} + +/// Convert the internal state into a human readable string. +/// @return The current internal state expressed as a human readable String. +String IRTranscoldAc::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. + // Special modes. + if (getSwing()) return result + addToggleToString(true, kSwingStr); + result += addModeToString(getMode(), kTranscoldAuto, kTranscoldCool, + kTranscoldHeat, kTranscoldDry, kTranscoldFan); + result += addIntToString(_.Fan, kFanStr); + result += kSpaceLBraceStr; + switch (_.Fan) { + case kTranscoldFanAuto: + result += kAutoStr; + break; + case kTranscoldFanAuto0: + result += kAutoStr; + result += '0'; + break; + case kTranscoldFanMax: + result += kMaxStr; + break; + case kTranscoldFanMin: + result += kMinStr; + break; + case kTranscoldFanMed: + result += kMedStr; + break; + case kTranscoldFanZoneFollow: + result += kZoneFollowStr; + break; + case kTranscoldFanFixed: + result += kFixedStr; + break; + default: + result += kUnknownStr; + } + result += ')'; + // Fan mode doesn't have a temperature. + if (getMode() != kTranscoldFan) result += addTempToString(getTemp()); + return result; +} + +#if DECODE_TRANSCOLD +/// Decode the supplied Transcold 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::decodeTranscold(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; + if (strict && nbits != kTranscoldBits) return false; + if (nbits % 8 != 0) return false; + + uint64_t data = 0; + uint64_t inverted = 0; + + if (nbits > sizeof(data) * 8) + return false; // We can't possibly capture a Transcold packet that big. + + // Header + if (!matchMark(results->rawbuf[offset++], kTranscoldHdrMark)) return false; + if (!matchSpace(results->rawbuf[offset++], kTranscoldHdrSpace)) return false; + + // Data + // Twice as many bits as there are normal plus inverted bits. + for (uint16_t i = 0; i < nbits * 2; i++, offset++) { + bool flip = (i / 8) % 2; + if (!matchMark(results->rawbuf[offset++], kTranscoldBitMark)) + return false; + if (matchSpace(results->rawbuf[offset], kTranscoldOneSpace)) { + if (flip) + inverted = (inverted << 1) | 1; + else + data = (data << 1) | 1; + } else if (matchSpace(results->rawbuf[offset], kTranscoldZeroSpace)) { + if (flip) + inverted <<= 1; + else + data <<= 1; + } else { + return false; + } + } + + // Footer + if (!matchMark(results->rawbuf[offset++], kTranscoldBitMark)) return false; + if (!matchSpace(results->rawbuf[offset++], kTranscoldHdrSpace)) return false; + if (!matchMark(results->rawbuf[offset++], kTranscoldBitMark)) return false; + if (offset < results->rawlen && + !matchAtLeast(results->rawbuf[offset], kDefaultMessageGap)) + return false; + + // Compliance + if (strict && inverted != invertBits(data, nbits)) return false; + + // Success + results->decode_type = decode_type_t::TRANSCOLD; + results->bits = nbits; + results->value = data; + results->address = 0; + results->command = 0; + return true; +} +#endif // DECODE_TRANSCOLD diff --git a/lib/IRremoteESP8266/src/ir_Transcold.h b/lib/IRremoteESP8266/src/ir_Transcold.h index bedb9611d5..8fd924fbaf 100644 --- a/lib/IRremoteESP8266/src/ir_Transcold.h +++ b/lib/IRremoteESP8266/src/ir_Transcold.h @@ -63,10 +63,10 @@ temp 16 Auto cool close (right) 11101111000100000110011110011000010101001010101 #ifndef UNIT_TEST #include #endif -#include "../src/IRremoteESP8266.h" -#include "../src/IRsend.h" +#include "IRremoteESP8266.h" +#include "IRsend.h" #ifdef UNIT_TEST -#include "../src/IRsend_test.h" +#include "IRsend_test.h" #endif /// Native representation of a Transcold A/C message. diff --git a/lib/IRremoteESP8266/src/ir_Trotec.cpp b/lib/IRremoteESP8266/src/ir_Trotec.cpp new file mode 100644 index 0000000000..7cd6fff5e6 --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_Trotec.cpp @@ -0,0 +1,642 @@ +// Copyright 2017 stufisher +// Copyright 2019 crankyoldgit + +/// @file +/// @brief Support for Trotec protocols. +/// @see https://github.com/crankyoldgit/IRremoteESP8266/pull/279 +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1176 + +#include "ir_Trotec.h" +#include +#include +#ifndef UNIT_TEST +#include +#endif +#include "IRremoteESP8266.h" +#include "IRtext.h" +#include "IRutils.h" + +// Constants +const uint16_t kTrotecHdrMark = 5952; +const uint16_t kTrotecHdrSpace = 7364; +const uint16_t kTrotecBitMark = 592; +const uint16_t kTrotecOneSpace = 1560; +const uint16_t kTrotecZeroSpace = 592; +const uint16_t kTrotecGap = 6184; +const uint16_t kTrotecGapEnd = 1500; // made up value + +const uint16_t kTrotec3550HdrMark = 12000; +const uint16_t kTrotec3550HdrSpace = 5130; +const uint16_t kTrotec3550BitMark = 550; +const uint16_t kTrotec3550OneSpace = 1950; +const uint16_t kTrotec3550ZeroSpace = 500; + +const uint16_t kTrotec3550TimerMax = 8 * 60; ///< 8 hours in Minutes. + +using irutils::addBoolToString; +using irutils::addFanToString; +using irutils::addIntToString; +using irutils::addLabeledString; +using irutils::addModeToString; +using irutils::addTempToString; +using irutils::minsToString; + +#if SEND_TROTEC +/// Send a Trotec message. +/// 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::sendTrotec(const unsigned char data[], const uint16_t nbytes, + const uint16_t repeat) { + if (nbytes < kTrotecStateLength) return; + + enableIROut(36); + for (uint16_t r = 0; r <= repeat; r++) { + sendGeneric(kTrotecHdrMark, kTrotecHdrSpace, kTrotecBitMark, + kTrotecOneSpace, kTrotecBitMark, kTrotecZeroSpace, + kTrotecBitMark, kTrotecGap, data, nbytes, 36, false, + 0, // Repeats handled elsewhere + 50); + // More footer + mark(kTrotecBitMark); + space(kTrotecGapEnd); + } +} +#endif // SEND_TROTEC + +/// 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? +IRTrotecESP::IRTrotecESP(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 IRTrotecESP::begin(void) { _irsend.begin(); } + +#if SEND_TROTEC +/// Send the current internal state as an IR message. +/// @param[in] repeat Nr. of times the message will be repeated. +void IRTrotecESP::send(const uint16_t repeat) { + _irsend.sendTrotec(getRaw(), kTrotecStateLength, repeat); +} +#endif // SEND_TROTEC + +/// 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 IRTrotecESP::calcChecksum(const uint8_t state[], + const uint16_t length) { + return sumBytes(state + 2, length - 3); +} + +/// 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 IRTrotecESP::validChecksum(const uint8_t state[], const uint16_t length) { + return state[length - 1] == calcChecksum(state, length); +} + +/// Calculate & set the checksum for the current internal state of the remote. +void IRTrotecESP::checksum(void) { + _.Sum = sumBytes(_.raw + 2, kTrotecStateLength - 3); +} + +/// Reset the state of the remote to a known good state/sequence. +void IRTrotecESP::stateReset(void) { + for (uint8_t i = 2; i < kTrotecStateLength; i++) _.raw[i] = 0x0; + + _.Intro1 = kTrotecIntro1; + _.Intro2 = kTrotecIntro2; + + _.Power = false; + setTemp(kTrotecDefTemp); + _.Fan = kTrotecFanMed; + _.Mode = kTrotecAuto; +} + +/// 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* IRTrotecESP::getRaw(void) { + checksum(); + return _.raw; +} + +/// Set the internal state from a valid code for this protocol. +/// @param[in] state A valid code for this protocol. +void IRTrotecESP::setRaw(const uint8_t state[]) { + memcpy(_.raw, state, kTrotecStateLength); +} + +/// Set the requested power state of the A/C to on. +void IRTrotecESP::on(void) { setPower(true); } + +/// Set the requested power state of the A/C to off. +void IRTrotecESP::off(void) { setPower(false); } + +/// Change the power setting. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRTrotecESP::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 IRTrotecESP::getPower(void) const { + return _.Power; +} + +/// Set the speed of the fan. +/// @param[in] fan The desired setting. +void IRTrotecESP::setSpeed(const uint8_t fan) { + uint8_t speed = std::min(fan, kTrotecFanHigh); + _.Fan = speed; +} + +/// Get the current fan speed setting. +/// @return The current fan speed/mode. +uint8_t IRTrotecESP::getSpeed(void) const { + return _.Fan; +} + +/// Set the operating mode of the A/C. +/// @param[in] mode The desired operating mode. +void IRTrotecESP::setMode(const uint8_t mode) { + _.Mode = (mode > kTrotecFan) ? kTrotecAuto : mode; +} + +/// Get the operating mode setting of the A/C. +/// @return The current operating mode setting. +uint8_t IRTrotecESP::getMode(void) const { + return _.Mode; +} + +/// Set the temperature. +/// @param[in] celsius The temperature in degrees celsius. +void IRTrotecESP::setTemp(const uint8_t celsius) { + uint8_t temp = std::max(celsius, kTrotecMinTemp); + temp = std::min(temp, kTrotecMaxTemp); + _.Temp = temp - kTrotecMinTemp; +} + +/// Get the current temperature setting. +/// @return The current setting for temp. in degrees celsius. +uint8_t IRTrotecESP::getTemp(void) const { + return _.Temp + kTrotecMinTemp; +} + +/// Set the Sleep setting of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRTrotecESP::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 IRTrotecESP::getSleep(void) const { + return _.Sleep; +} + +/// Set the timer time in nr. of Hours. +/// @param[in] timer Nr. of Hours. Max is `kTrotecMaxTimer` +void IRTrotecESP::setTimer(const uint8_t timer) { + _.Timer = timer; + _.Hours = (timer > kTrotecMaxTimer) ? kTrotecMaxTimer : timer; +} + +/// Get the timer time in nr. of Hours. +/// @return Nr. of Hours. +uint8_t IRTrotecESP::getTimer(void) const { return _.Hours; } + +/// 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 IRTrotecESP::convertMode(const stdAc::opmode_t mode) { + switch (mode) { + case stdAc::opmode_t::kCool: return kTrotecCool; + case stdAc::opmode_t::kDry: return kTrotecDry; + case stdAc::opmode_t::kFan: return kTrotecFan; + // Note: No Heat mode. + default: return kTrotecAuto; + } +} + +/// 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 IRTrotecESP::convertFan(const stdAc::fanspeed_t speed) { + switch (speed) { + case stdAc::fanspeed_t::kMin: + case stdAc::fanspeed_t::kLow: return kTrotecFanLow; + case stdAc::fanspeed_t::kMedium: return kTrotecFanMed; + case stdAc::fanspeed_t::kHigh: + case stdAc::fanspeed_t::kMax: return kTrotecFanHigh; + default: return kTrotecFanMed; + } +} + +/// 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 IRTrotecESP::toCommonMode(const uint8_t mode) { + switch (mode) { + case kTrotecCool: return stdAc::opmode_t::kCool; + case kTrotecDry: return stdAc::opmode_t::kDry; + case kTrotecFan: return stdAc::opmode_t::kFan; + default: return stdAc::opmode_t::kAuto; + } +} + +/// Convert a native fan speed into its stdAc equivalent. +/// @param[in] spd The native setting to be converted. +/// @return The stdAc equivalent of the native setting. +stdAc::fanspeed_t IRTrotecESP::toCommonFanSpeed(const uint8_t spd) { + switch (spd) { + case kTrotecFanHigh: return stdAc::fanspeed_t::kMax; + case kTrotecFanMed: return stdAc::fanspeed_t::kMedium; + case kTrotecFanLow: 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 IRTrotecESP::toCommon(void) const { + stdAc::state_t result; + result.protocol = decode_type_t::TROTEC; + result.power = _.Power; + result.mode = toCommonMode(_.Mode); + result.celsius = true; + result.degrees = getTemp(); + result.fanspeed = toCommonFanSpeed(_.Fan); + result.sleep = _.Sleep ? 0 : -1; + // Not supported. + result.model = -1; // Not supported. + result.swingv = stdAc::swingv_t::kOff; + result.swingh = stdAc::swingh_t::kOff; + result.turbo = false; + 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 IRTrotecESP::toString(void) const { + String result = ""; + result.reserve(100); // Reserve some heap for the string to reduce fragging. + result += addBoolToString(_.Power, kPowerStr, false); + result += addModeToString(_.Mode, kTrotecAuto, kTrotecCool, kTrotecAuto, + kTrotecDry, kTrotecFan); + result += addTempToString(getTemp()); + result += addFanToString(_.Fan, kTrotecFanHigh, kTrotecFanLow, + kTrotecFanHigh, kTrotecFanHigh, kTrotecFanMed); + result += addBoolToString(_.Sleep, kSleepStr); + return result; +} + +#if DECODE_TROTEC +/// Decode the supplied Trotec message. +/// Status: STABLE / Works. Untested on real devices. +/// @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::decodeTrotec(decode_results *results, uint16_t offset, + const uint16_t nbits, const bool strict) { + if (results->rawlen <= 2 * nbits + kHeader + 2 * kFooter - 1 + offset) + return false; // Can't possibly be a valid Trotec A/C message. + if (strict && nbits != kTrotecBits) return false; + + uint16_t used; + // Header + Data + Footer #1 + used = matchGeneric(results->rawbuf + offset, results->state, + results->rawlen - offset, nbits, + kTrotecHdrMark, kTrotecHdrSpace, + kTrotecBitMark, kTrotecOneSpace, + kTrotecBitMark, kTrotecZeroSpace, + kTrotecBitMark, kTrotecGap, true, + _tolerance, 0, false); + if (used == 0) return false; + offset += used; + + // Footer #2 + if (!matchMark(results->rawbuf[offset++], kTrotecBitMark)) return false; + if (offset <= results->rawlen && + !matchAtLeast(results->rawbuf[offset++], kTrotecGapEnd)) return false; + // Compliance + // Verify we got a valid checksum. + if (strict && !IRTrotecESP::validChecksum(results->state)) return false; + // Success + results->decode_type = TROTEC; + 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_TROTEC + +#if SEND_TROTEC_3550 +/// Send a Trotec 3550 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::sendTrotec3550(const unsigned char data[], const uint16_t nbytes, + const uint16_t repeat) { + sendGeneric(kTrotec3550HdrMark, kTrotec3550HdrSpace, + kTrotec3550BitMark, kTrotec3550OneSpace, + kTrotec3550BitMark, kTrotec3550ZeroSpace, + kTrotec3550BitMark, kDefaultMessageGap, + data, nbytes, 38, true, repeat, kDutyDefault); +} +#endif // SEND_TROTEC_3550 + +#if DECODE_TROTEC_3550 +/// Decode the supplied Trotec 3550 message. +/// Status: STABLE / Known 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::decodeTrotec3550(decode_results *results, uint16_t offset, + const uint16_t nbits, const bool strict) { + if (strict && nbits != kTrotecBits) return false; + + // Header + Data + Footer + if (!matchGeneric(results->rawbuf + offset, results->state, + results->rawlen - offset, nbits, + kTrotec3550HdrMark, kTrotec3550HdrSpace, + kTrotec3550BitMark, kTrotec3550OneSpace, + kTrotec3550BitMark, kTrotec3550ZeroSpace, + kTrotec3550BitMark, kDefaultMessageGap)) return false; + // Compliance + if (strict && !IRTrotec3550::validChecksum(results->state, nbits / 8)) + return false; + // Success + results->decode_type = TROTEC_3550; + 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_TROTEC_3550 + +/// 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? +IRTrotec3550::IRTrotec3550(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 IRTrotec3550::begin(void) { _irsend.begin(); } + +#if SEND_TROTEC_3550 +/// Send the current internal state as an IR message. +/// @param[in] repeat Nr. of times the message will be repeated. +void IRTrotec3550::send(const uint16_t repeat) { + _irsend.sendTrotec3550(getRaw(), kTrotecStateLength, repeat); +} +#endif // SEND_TROTEC_3550 + +/// 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 IRTrotec3550::calcChecksum(const uint8_t state[], + const uint16_t length) { + return length ? sumBytes(state, length - 1) : 0; +} + +/// 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 IRTrotec3550::validChecksum(const uint8_t state[], const uint16_t length) { + return state[length - 1] == calcChecksum(state, length); +} + +/// Calculate & set the checksum for the current internal state of the remote. +void IRTrotec3550::checksum(void) { _.Sum = calcChecksum(_.raw); } + +/// Reset the state of the remote to a known good state/sequence. +void IRTrotec3550::stateReset(void) { + static const uint8_t kReset[kTrotecStateLength] = { + 0x55, 0x60, 0x00, 0x0D, 0x00, 0x00, 0x10, 0x88, 0x5A}; + std::memcpy(_.raw, kReset, kTrotecStateLength); +} + +/// 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* IRTrotec3550::getRaw(void) { + checksum(); + return _.raw; +} + +/// Set the internal state from a valid code for this protocol. +/// @param[in] state A valid code for this protocol. +void IRTrotec3550::setRaw(const uint8_t state[]) { + memcpy(_.raw, state, kTrotecStateLength); +} + +/// Set the requested power state of the A/C to on. +void IRTrotec3550::on(void) { setPower(true); } + +/// Set the requested power state of the A/C to off. +void IRTrotec3550::off(void) { setPower(false); } + +/// Change the power setting. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRTrotec3550::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 IRTrotec3550::getPower(void) const { return _.Power; } + +/// Set the speed of the fan. +/// @param[in] fan The desired setting. +void IRTrotec3550::setFan(const uint8_t fan) { + uint8_t speed = std::min(fan, kTrotecFanHigh); + _.Fan = speed; +} + +/// Get the current fan speed setting. +/// @return The current fan speed/mode. +uint8_t IRTrotec3550::getFan(void) const { return _.Fan; } + +/// Set the operating mode of the A/C. +/// @param[in] mode The desired operating mode. +void IRTrotec3550::setMode(const uint8_t mode) { + _.Mode = (mode > kTrotecFan) ? kTrotecAuto : mode; +} + +/// Get the operating mode setting of the A/C. +/// @return The current operating mode setting. +uint8_t IRTrotec3550::getMode(void) const { return _.Mode; } + +/// Set the temperature. +/// @param[in] degrees The temperature in degrees. +/// @param[in] celsius Use celsius units. True, Celsius; False Fahrenheit. +void IRTrotec3550::setTemp(const uint8_t degrees, const bool celsius) { + setTempUnit(celsius); + uint8_t minTemp = kTrotec3550MinTempC; + uint8_t maxTemp = kTrotec3550MaxTempC; + if (!celsius) { // Fahrenheit? + minTemp = kTrotec3550MinTempF; + maxTemp = kTrotec3550MaxTempF; + } + uint8_t temp = std::max(degrees, minTemp); + temp = std::min(temp, maxTemp); + if (celsius) { + _.TempC = temp - minTemp; + _.TempF = celsiusToFahrenheit(temp) - kTrotec3550MinTempF; + } else { + _.TempF = temp - minTemp; + _.TempC = fahrenheitToCelsius(temp) - kTrotec3550MinTempC; + } +} + +/// Get the current temperature setting. +/// @return The current setting for temp. in degrees. +uint8_t IRTrotec3550::getTemp(void) const { + return getTempUnit() ? _.TempC + kTrotec3550MinTempC + : _.TempF + kTrotec3550MinTempF; +} + +/// Set the temperature unit that the A/C will use.. +/// @param[in] celsius Use celsius units. True, Celsius; False Fahrenheit. +void IRTrotec3550::setTempUnit(const bool celsius) { _.Celsius = celsius; } + +/// Get the current temperature unit setting. +/// @return True, Celsius; False Fahrenheit. +bool IRTrotec3550::getTempUnit(void) const { return _.Celsius; } + +/// Change the Vertical Swing setting. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRTrotec3550::setSwingV(const bool on) { _.SwingV = on; } + +/// Get the value of the current Vertical Swing setting. +/// @return true, the setting is on. false, the setting is off. +bool IRTrotec3550::getSwingV(void) const { return _.SwingV; } + +/// Get the number of minutes of the Timer setting. +/// @return Nr of minutes. +uint16_t IRTrotec3550::getTimer(void) const { return _.TimerHrs * 60; } + +/// Set the number of minutes of the Timer setting. +/// @param[in] mins Nr. of Minutes for the Timer. `0` means disable the timer. +void IRTrotec3550::setTimer(const uint16_t mins) { + _.TimerSet = mins > 0; + _.TimerHrs = (std::min(mins, kTrotec3550TimerMax) / 60); +} + +/// 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 IRTrotec3550::convertMode(const stdAc::opmode_t mode) { + switch (mode) { + case stdAc::opmode_t::kCool: return kTrotecCool; + case stdAc::opmode_t::kDry: return kTrotecDry; + case stdAc::opmode_t::kFan: return kTrotecFan; + // Note: No Heat mode. + default: return kTrotecAuto; + } +} + +/// 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 IRTrotec3550::convertFan(const stdAc::fanspeed_t speed) { + switch (speed) { + case stdAc::fanspeed_t::kMin: + case stdAc::fanspeed_t::kLow: return kTrotecFanLow; + case stdAc::fanspeed_t::kMedium: return kTrotecFanMed; + case stdAc::fanspeed_t::kHigh: + case stdAc::fanspeed_t::kMax: return kTrotecFanHigh; + default: return kTrotecFanMed; + } +} + +/// 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 IRTrotec3550::toCommonMode(const uint8_t mode) { + switch (mode) { + case kTrotecCool: return stdAc::opmode_t::kCool; + case kTrotecDry: return stdAc::opmode_t::kDry; + case kTrotecFan: return stdAc::opmode_t::kFan; + default: return stdAc::opmode_t::kAuto; + } +} + +/// Convert a native fan speed into its stdAc equivalent. +/// @param[in] spd The native setting to be converted. +/// @return The stdAc equivalent of the native setting. +stdAc::fanspeed_t IRTrotec3550::toCommonFanSpeed(const uint8_t spd) { + switch (spd) { + case kTrotecFanHigh: return stdAc::fanspeed_t::kMax; + case kTrotecFanMed: return stdAc::fanspeed_t::kMedium; + case kTrotecFanLow: 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 IRTrotec3550::toCommon(void) const { + stdAc::state_t result; + result.protocol = decode_type_t::TROTEC_3550; + result.power = _.Power; + result.mode = toCommonMode(_.Mode); + result.celsius = getTempUnit(); + result.degrees = getTemp(); + result.fanspeed = toCommonFanSpeed(_.Fan); + result.swingv = _.SwingV ? stdAc::swingv_t::kAuto : stdAc::swingv_t::kOff; + // Not supported. + result.model = -1; + result.swingh = stdAc::swingh_t::kOff; + result.turbo = false; + 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 IRTrotec3550::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, kTrotecAuto, kTrotecCool, kTrotecAuto, + kTrotecDry, kTrotecFan); + result += addTempToString(getTemp(), _.Celsius); + result += addFanToString(_.Fan, kTrotecFanHigh, kTrotecFanLow, + kTrotecFanHigh, kTrotecFanHigh, kTrotecFanMed); + result += addBoolToString(_.SwingV, kSwingVStr); + result += addLabeledString(_.TimerSet ? minsToString(getTimer()) : kOffStr, + kTimerStr); + return result; +} diff --git a/lib/IRremoteESP8266/src/ir_Trotec.h b/lib/IRremoteESP8266/src/ir_Trotec.h index b8586dd77d..3381ec3f20 100644 --- a/lib/IRremoteESP8266/src/ir_Trotec.h +++ b/lib/IRremoteESP8266/src/ir_Trotec.h @@ -18,10 +18,10 @@ #ifndef UNIT_TEST #include #endif -#include "../src/IRremoteESP8266.h" -#include "../src/IRsend.h" +#include "IRremoteESP8266.h" +#include "IRsend.h" #ifdef UNIT_TEST -#include "../src/IRsend_test.h" +#include "IRsend_test.h" #endif /// Native representation of a Trotec A/C message. diff --git a/lib/IRremoteESP8266/src/ir_Truma.cpp b/lib/IRremoteESP8266/src/ir_Truma.cpp new file mode 100644 index 0000000000..853c640e16 --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_Truma.cpp @@ -0,0 +1,340 @@ +// Copyright 2021 David Conran (crankyoldgit) + +/// @file +/// @brief Support for Truma protocol. +/// This protocol uses mark length bit encoding. +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1440 +/// @see https://docs.google.com/spreadsheets/d/1k-RHu0vSIB6IweiTZSa3Rxy3Z_qPUtqwcqot8uXVO6I/edit?usp=sharing + + +#include "ir_Truma.h" +#include +#include "IRrecv.h" +#include "IRsend.h" +#include "IRtext.h" +#include "IRutils.h" + +using irutils::addBoolToString; +using irutils::addFanToString; +using irutils::addModeToString; +using irutils::addTempToString; + +// Constants + +const uint16_t kTrumaLdrMark = 20200; +const uint16_t kTrumaLdrSpace = 1000; +const uint16_t kTrumaHdrMark = 1800; +const uint16_t kTrumaSpace = 630; +const uint16_t kTrumaOneMark = 600; +const uint16_t kTrumaZeroMark = 1200; +const uint16_t kTrumaFooterMark = kTrumaOneMark; +const uint32_t kTrumaGap = kDefaultMessageGap; // Just a guess. + + +#if SEND_TRUMA +/// Send a Truma formatted message. +/// Status: STABLE / Confirmed 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::sendTruma(const uint64_t data, const uint16_t nbits, + const uint16_t repeat) { + for (uint16_t r = 0; r <= repeat; r++) { + enableIROut(38000); + mark(kTrumaLdrMark); + space(kTrumaLdrSpace); + sendGeneric(kTrumaHdrMark, kTrumaSpace, // Header + kTrumaOneMark, kTrumaSpace, // Data + kTrumaZeroMark, kTrumaSpace, + kTrumaFooterMark, kTrumaGap, // Footer + data, nbits, 38, false, 0, kDutyDefault); + } +} +#endif // SEND_TRUMA + +#if DECODE_TRUMA +/// Decode the supplied Truma message. +/// Status: STABLE / Confirmed working with 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. Typically kTrumaBits. +/// @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::decodeTruma(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 message. + if (strict && nbits != kTrumaBits) + return false; // Not strictly a message. + + // Leader. + if (!matchMark(results->rawbuf[offset++], kTrumaLdrMark)) return false; + if (!matchSpace(results->rawbuf[offset++], kTrumaLdrSpace)) return false; + + uint64_t data = 0; + uint16_t used; + used = matchGeneric(results->rawbuf + offset, &data, + results->rawlen - offset, nbits, + kTrumaHdrMark, kTrumaSpace, + kTrumaOneMark, kTrumaSpace, + kTrumaZeroMark, kTrumaSpace, + kTrumaFooterMark, kTrumaGap, + true, kUseDefTol, kMarkExcess, false); + if (!used) return false; + + // Compliance + if (strict && !IRTrumaAc::validChecksum(data)) return false; // Checksum. + + // Success + results->value = data; + results->decode_type = decode_type_t::TRUMA; + results->bits = nbits; + results->address = 0; + results->command = 0; + return true; +} +#endif // DECODE_TRUMA + + +/// 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? +IRTrumaAc::IRTrumaAc(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 IRTrumaAc::begin(void) { _irsend.begin(); } + +#if SEND_TRUMA +/// Send the current internal state as an IR message. +/// @param[in] repeat Nr. of times the message will be repeated. +void IRTrumaAc::send(const uint16_t repeat) { + _irsend.sendTruma(getRaw(), kTrumaBits, repeat); +} +#endif // SEND_TRUMA + +/// Calculate the checksum for a given state. +/// @param[in] state The value to calc the checksum of. +/// @return The calculated checksum value. +uint8_t IRTrumaAc::calcChecksum(const uint64_t state) { + uint8_t sum = kTrumaChecksumInit; + uint64_t to_checksum = state; + for (uint16_t i = 8; i < kTrumaBits; i += 8) { + sum += (to_checksum & 0xFF); + to_checksum >>= 8; + } + return sum; +} + +/// 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 IRTrumaAc::validChecksum(const uint64_t state) { + TrumaProtocol state_copy; + state_copy.raw = state; + return state_copy.Sum == calcChecksum(state); +} + +/// Calculate & set the checksum for the current internal state of the remote. +void IRTrumaAc::checksum(void) { _.Sum = calcChecksum(_.raw); } + +/// Reset the state of the remote to a known good state/sequence. +void IRTrumaAc::stateReset(void) { setRaw(kTrumaDefaultState); } + +/// 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 IRTrumaAc::getRaw(void) { + checksum(); + return _.raw; +} + +/// Set the internal state from a valid code for this protocol. +/// @param[in] state A valid code for this protocol. +void IRTrumaAc::setRaw(const uint64_t state) { + _.raw = state; + _lastfan = _.Fan; + _lastmode = _.Mode; +} + +/// Set the requested power state of the A/C to on. +void IRTrumaAc::on(void) { setPower(true); } + +/// Set the requested power state of the A/C to off. +void IRTrumaAc::off(void) { setPower(false); } + +/// Change the power setting. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRTrumaAc::setPower(const bool on) { + _.PowerOff = !on; + _.Mode = on ? _lastmode : kTrumaFan; // Off temporarily sets mode to Fan. +} + +/// Get the value of the current power setting. +/// @return true, the setting is on. false, the setting is off. +bool IRTrumaAc::getPower(void) const { return !_.PowerOff; } + +/// Set the speed of the fan. +/// @param[in] speed The desired setting. +void IRTrumaAc::setFan(const uint8_t speed) { + switch (speed) { + case kTrumaFanHigh: + case kTrumaFanMed: + case kTrumaFanLow: + _lastfan = speed; // Never allow _lastfan to be Quiet. + _.Fan = speed; + break; + case kTrumaFanQuiet: + if (_.Mode == kTrumaCool) _.Fan = kTrumaFanQuiet; // Only in Cool mode. + break; + default: + setFan(kTrumaFanHigh); + } +} + +/// Get the current fan speed setting. +/// @return The current fan speed/mode. +uint8_t IRTrumaAc::getFan(void) const { return _.Fan; } + +/// Set the operating mode of the A/C. +/// @param[in] mode The desired operating mode. +void IRTrumaAc::setMode(const uint8_t mode) { + switch (mode) { + case kTrumaAuto: + case kTrumaFan: + if (getQuiet()) setFan(kTrumaFanHigh); // Can only have quiet in Cool. + // FALL THRU + case kTrumaCool: + _.Mode = _.PowerOff ? kTrumaFan : mode; // When Off, only set Fan mode. + _lastmode = mode; + break; + default: + setMode(kTrumaAuto); + } +} + +/// Get the operating mode setting of the A/C. +/// @return The current operating mode setting. +uint8_t IRTrumaAc::getMode(void) const { return _.Mode; } + +/// Set the temperature. +/// @param[in] celsius The temperature in degrees celsius. +void IRTrumaAc::setTemp(const uint8_t celsius) { + uint8_t temp = std::max(celsius, kTrumaMinTemp); + temp = std::min(temp, kTrumaMaxTemp); + _.Temp = temp - kTrumaTempOffset; +} + +/// Get the current temperature setting. +/// @return The current setting for temp. in degrees celsius. +uint8_t IRTrumaAc::getTemp(void) const { return _.Temp + kTrumaTempOffset; } + +/// Change the Quiet setting. +/// @param[in] on true, the setting is on. false, the setting is off. +/// @note Quiet is only available in Cool mode. +void IRTrumaAc::setQuiet(const bool on) { + if (on && _.Mode == kTrumaCool) + setFan(kTrumaFanQuiet); + else + setFan(_lastfan); +} + +/// Get the value of the current quiet setting. +/// @return true, the setting is on. false, the setting is off. +bool IRTrumaAc::getQuiet(void) const { return _.Fan == kTrumaFanQuiet; } + +/// 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 IRTrumaAc::convertMode(const stdAc::opmode_t mode) { + switch (mode) { + case stdAc::opmode_t::kCool: return kTrumaCool; + case stdAc::opmode_t::kFan: return kTrumaFan; + default: return kTrumaAuto; + } +} + +/// 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 IRTrumaAc::convertFan(const stdAc::fanspeed_t speed) { + switch (speed) { + case stdAc::fanspeed_t::kMin: return kTrumaFanQuiet; + case stdAc::fanspeed_t::kLow: return kTrumaFanLow; + case stdAc::fanspeed_t::kMedium: return kTrumaFanMed; + default: return kTrumaFanHigh; + } +} + +/// 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 IRTrumaAc::toCommonMode(const uint8_t mode) { + switch (mode) { + case kTrumaCool: return stdAc::opmode_t::kCool; + case kTrumaFan: return stdAc::opmode_t::kFan; + default: return stdAc::opmode_t::kAuto; + } +} + +/// Convert a native fan speed into its stdAc equivalent. +/// @param[in] spd The native setting to be converted. +/// @return The stdAc equivalent of the native setting. +stdAc::fanspeed_t IRTrumaAc::toCommonFanSpeed(const uint8_t spd) { + switch (spd) { + case kTrumaFanMed: return stdAc::fanspeed_t::kMedium; + case kTrumaFanLow: return stdAc::fanspeed_t::kLow; + case kTrumaFanQuiet: return stdAc::fanspeed_t::kMin; + default: return stdAc::fanspeed_t::kHigh; + } +} + +/// Convert the current internal state into its stdAc::state_t equivalent. +/// @return The stdAc equivalent of the native settings. +stdAc::state_t IRTrumaAc::toCommon(void) const { + stdAc::state_t result; + + result.protocol = decode_type_t::TRUMA; + result.model = -1; // Not supported. + // Do we have enough current state info to override any previous state? + // i.e. Was the class just setRaw()'ed with a short "swing" message. + // This should enables us to also ignore the Swing msg's special 17C setting. + result.power = getPower(); + result.mode = toCommonMode(getMode()); + result.celsius = true; + result.degrees = getTemp(); + result.fanspeed = toCommonFanSpeed(getFan()); + result.quiet = getQuiet(); + // Not supported. + result.turbo = false; + result.econo = false; + result.light = false; + result.filter = false; + result.swingv = stdAc::swingv_t::kOff; + result.swingh = stdAc::swingh_t::kOff; + 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 IRTrumaAc::toString(void) const { + String result = ""; + result.reserve(80); + result += addBoolToString(getPower(), kPowerStr, false); + if (getPower()) // Only show the Operating Mode if the unit is on. + result += addModeToString(_.Mode, kTrumaAuto, kTrumaCool, + kTrumaAuto, kTrumaAuto, kTrumaFan); + + result += addTempToString(getTemp()); + result += addFanToString(_.Fan, kTrumaFanHigh, kTrumaFanLow, kTrumaFanHigh, + kTrumaFanQuiet, kTrumaFanMed); + result += addBoolToString(getQuiet(), kQuietStr); + return result; +} diff --git a/lib/IRremoteESP8266/src/ir_Truma.h b/lib/IRremoteESP8266/src/ir_Truma.h index 58beeeba44..9946a03229 100644 --- a/lib/IRremoteESP8266/src/ir_Truma.h +++ b/lib/IRremoteESP8266/src/ir_Truma.h @@ -15,10 +15,10 @@ #ifndef UNIT_TEST #include #endif -#include "../src/IRremoteESP8266.h" -#include "../src/IRsend.h" +#include "IRremoteESP8266.h" +#include "IRsend.h" #ifdef UNIT_TEST -#include "../src/IRsend_test.h" +#include "IRsend_test.h" #endif /// Native representation of a Truma A/C message. diff --git a/lib/IRremoteESP8266/src/ir_Vestel.cpp b/lib/IRremoteESP8266/src/ir_Vestel.cpp new file mode 100644 index 0000000000..803363a169 --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_Vestel.cpp @@ -0,0 +1,572 @@ +// Copyright 2018 Erdem U. Altinyurt +// Copyright 2019 David Conran + +/// @file +/// @brief Support for Vestel protocols. +/// Vestel added by Erdem U. Altinyurt + +#include "ir_Vestel.h" +#include +#ifndef UNIT_TEST +#include +#endif +#include "IRrecv.h" +#include "IRremoteESP8266.h" +#include "IRsend.h" +#include "IRtext.h" +#include "IRutils.h" +#include "ir_Haier.h" + +// Ref: +// None. Totally reverse engineered. + +using irutils::addBoolToString; +using irutils::addIntToString; +using irutils::addLabeledString; +using irutils::addModeToString; +using irutils::addTempToString; +using irutils::minsToString; + +#if SEND_VESTEL_AC +/// Send a Vestel 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::sendVestelAc(const uint64_t data, const uint16_t nbits, + const uint16_t repeat) { + if (nbits % 8 != 0) return; // nbits is required to be a multiple of 8. + + sendGeneric(kVestelAcHdrMark, kVestelAcHdrSpace, // Header + kVestelAcBitMark, kVestelAcOneSpace, // Data + kVestelAcBitMark, kVestelAcZeroSpace, // Data + kVestelAcBitMark, 100000, // Footer + repeat gap + data, nbits, 38, false, repeat, 50); +} +#endif // SEND_VESTEL_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? +IRVestelAc::IRVestelAc(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. +/// @note Power On, Mode Auto, Fan Auto, Temp = 25C/77F +void IRVestelAc::stateReset(void) { + _.cmdState = kVestelAcStateDefault; + _.timeState = kVestelAcTimeStateDefault; +} + +/// Set up hardware to be able to send a message. +void IRVestelAc::begin(void) { _irsend.begin(); } + +#if SEND_VESTEL_AC +/// Send the current internal state as an IR message. +/// @param[in] repeat Nr. of times the message will be repeated. +void IRVestelAc::send(const uint16_t repeat) { + _irsend.sendVestelAc(getRaw(), kVestelAcBits, repeat); +} +#endif // SEND_VESTEL_AC + +/// Get a copy of the internal state/code for this protocol. +/// @return A code for this protocol based on the current internal state. +uint64_t IRVestelAc::getRaw(void) { + checksum(); + if (!_.UseCmd) return _.timeState; + return _.cmdState; +} + +/// Set the internal state from a valid code for this protocol. +/// @param[in] newState A valid code for this protocol. +void IRVestelAc::setRaw(const uint8_t* newState) { + uint64_t upState = 0; + for (int i = 0; i < 7; i++) + upState |= static_cast(newState[i]) << (i * 8); + setRaw(upState); +} + +/// Set the internal state from a valid code for this protocol. +/// @param[in] newState A valid code for this protocol. +void IRVestelAc::setRaw(const uint64_t newState) { + _.cmdState = newState; + _.timeState = newState; + if (isTimeCommand()) { + _.cmdState = kVestelAcStateDefault; + _.UseCmd = false; + } else { + _.timeState = kVestelAcTimeStateDefault; + } +} + +/// Set the requested power state of the A/C to on. +void IRVestelAc::on(void) { setPower(true); } + +/// Set the requested power state of the A/C to off. +void IRVestelAc::off(void) { setPower(false); } + +/// Change the power setting. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRVestelAc::setPower(const bool on) { + _.Power = (on ? 0b11 : 0b00); + _.UseCmd = true; +} + +/// Get the value of the current power setting. +/// @return true, the setting is on. false, the setting is off. +bool IRVestelAc::getPower(void) const { + return _.Power; +} + +/// Set the temperature. +/// @param[in] temp The temperature in degrees celsius. +void IRVestelAc::setTemp(const uint8_t temp) { + uint8_t new_temp = std::max(kVestelAcMinTempC, temp); + new_temp = std::min(kVestelAcMaxTemp, new_temp); + _.Temp = new_temp - kVestelAcMinTempH; + _.UseCmd = true; +} + +/// Get the current temperature setting. +/// @return The current setting for temp. in degrees celsius. +uint8_t IRVestelAc::getTemp(void) const { + return _.Temp + kVestelAcMinTempH; +} + +/// Set the speed of the fan. +/// @param[in] fan The desired setting. +void IRVestelAc::setFan(const uint8_t fan) { + switch (fan) { + case kVestelAcFanLow: + case kVestelAcFanMed: + case kVestelAcFanHigh: + case kVestelAcFanAutoCool: + case kVestelAcFanAutoHot: + case kVestelAcFanAuto: + _.Fan = fan; + break; + default: + _.Fan = kVestelAcFanAuto; + } + _.UseCmd = true; +} + +/// Get the current fan speed setting. +/// @return The current fan speed/mode. +uint8_t IRVestelAc::getFan(void) const { + return _.Fan; +} + +/// Get the operating mode setting of the A/C. +/// @return The current operating mode setting. +uint8_t IRVestelAc::getMode(void) const { + return _.Mode; +} + +/// Set the operating mode of the A/C. +/// @param[in] mode The desired operating mode. +/// @note If we get an unexpected mode, default to AUTO. +void IRVestelAc::setMode(const uint8_t mode) { + switch (mode) { + case kVestelAcAuto: + case kVestelAcCool: + case kVestelAcHeat: + case kVestelAcDry: + case kVestelAcFan: + _.Mode = mode; + break; + default: + _.Mode = kVestelAcAuto; + } + _.UseCmd = true; +} + +/// Set Auto mode/level of the A/C. +/// @param[in] autoLevel The auto mode/level setting. +void IRVestelAc::setAuto(const int8_t autoLevel) { + if (autoLevel < -2 || autoLevel > 2) return; + _.Mode = kVestelAcAuto; + _.Fan = (autoLevel < 0 ? kVestelAcFanAutoCool : kVestelAcFanAutoHot); + if (autoLevel == 2) + setTemp(30); + else if (autoLevel == 1) + setTemp(31); + else if (autoLevel == 0) + setTemp(25); + else if (autoLevel == -1) + setTemp(16); + else if (autoLevel == -2) + setTemp(17); +} + +/// Set the timer to be active on the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRVestelAc::setTimerActive(const bool on) { + _.Timer = on; + _.UseCmd = false; +} + +/// Get if the Timer is active on the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRVestelAc::isTimerActive(void) const { + return _.Timer; +} + +/// Set Timer option of A/C. +/// @param[in] minutes Nr of minutes the timer is to be set for. +/// @note Valid arguments are 0, 0.5, 1, 2, 3 and 5 hours (in minutes). +/// 0 disables the timer. +void IRVestelAc::setTimer(const uint16_t minutes) { + // Clear both On & Off timers. + _.OnHours = 0; + _.OnTenMins = 0; + // Set the "Off" time with the nr of minutes before we turn off. + _.OffHours = minutes / 60; + _.OffTenMins = (minutes % 60) / 10; + setOffTimerActive(false); + // Yes. On Timer instead of Off timer active. + setOnTimerActive(minutes != 0); + setTimerActive(minutes != 0); +} + +/// Get the Timer time of A/C. +/// @return The number of minutes of time on the timer. +uint16_t IRVestelAc::getTimer(void) const { return getOffTimer(); } + +/// Set the A/C's internal clock. +/// @param[in] minutes The time expressed in nr. of minutes past midnight. +void IRVestelAc::setTime(const uint16_t minutes) { + _.Hours = minutes / 60; + _.Minutes = minutes % 60; + _.UseCmd = false; +} + +/// Get the A/C's internal clock's time. +/// @return The time expressed in nr. of minutes past midnight. +uint16_t IRVestelAc::getTime(void) const { + return _.Hours * 60 + _.Minutes; +} + +/// Set the On timer to be active on the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRVestelAc::setOnTimerActive(const bool on) { + _.OnTimer = on; + _.UseCmd = false; +} + +/// Get if the On Timer is active on the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRVestelAc::isOnTimerActive(void) const { + return _.OnTimer; +} + +/// Set the On timer time on the A/C. +/// @param[in] minutes Time in nr. of minutes. +void IRVestelAc::setOnTimer(const uint16_t minutes) { + setOnTimerActive(minutes); + _.OnHours = minutes / 60; + _.OnTenMins = (minutes % 60) / 10; + setTimerActive(false); +} + +/// Get the A/C's On Timer time. +/// @return The time expressed in nr. of minutes. +uint16_t IRVestelAc::getOnTimer(void) const { + return _.OnHours * 60 + _.OnTenMins * 10; +} + +/// Set the Off timer to be active on the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRVestelAc::setOffTimerActive(const bool on) { + _.OffTimer = on; + _.UseCmd = false; +} + +/// Get if the Off Timer is active on the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRVestelAc::isOffTimerActive(void) const { + return _.OffTimer; +} + +/// Set the Off timer time on the A/C. +/// @param[in] minutes Time in nr. of minutes. +void IRVestelAc::setOffTimer(const uint16_t minutes) { + setOffTimerActive(minutes); + _.OffHours = minutes / 60; + _.OffTenMins = (minutes % 60) / 10; + setTimerActive(false); +} + +/// Get the A/C's Off Timer time. +/// @return The time expressed in nr. of minutes. +uint16_t IRVestelAc::getOffTimer(void) const { + return _.OffHours * 60 + _.OffTenMins * 10; +} + +/// Set the Sleep setting of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRVestelAc::setSleep(const bool on) { + _.TurboSleep = (on ? kVestelAcSleep : kVestelAcNormal); + _.UseCmd = true; +} + +/// Get the Sleep setting of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRVestelAc::getSleep(void) const { + return _.TurboSleep == kVestelAcSleep; +} + +/// Set the Turbo setting of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRVestelAc::setTurbo(const bool on) { + _.TurboSleep = (on ? kVestelAcTurbo : kVestelAcNormal); + _.UseCmd = true; +} + +/// Get the Turbo setting of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRVestelAc::getTurbo(void) const { + return _.TurboSleep == kVestelAcTurbo; +} + +/// Set the Ion (Filter) setting of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRVestelAc::setIon(const bool on) { + _.Ion = on; + _.UseCmd = true; +} + +/// Get the Ion (Filter) setting of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRVestelAc::getIon(void) const { + return _.Ion; +} + +/// Set the Swing Roaming setting of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRVestelAc::setSwing(const bool on) { + _.Swing = (on ? kVestelAcSwing : 0xF); + _.UseCmd = true; +} + +/// Get the Swing Roaming setting of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRVestelAc::getSwing(void) const { + return _.Swing == kVestelAcSwing; +} + +/// Calculate the checksum for a given state. +/// @param[in] state The state to calc the checksum of. +/// @return The calculated checksum value. +uint8_t IRVestelAc::calcChecksum(const uint64_t state) { + // Just counts the set bits +1 on stream and take inverse after mask + return 0xFF - countBits(GETBITS64(state, 20, 44), 44, true, 2); +} + +/// 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 IRVestelAc::validChecksum(const uint64_t state) { + VestelProtocol vp; + vp.cmdState = state; + return vp.CmdSum == IRVestelAc::calcChecksum(state); +} + +/// Calculate & set the checksum for the current internal state of the remote. +void IRVestelAc::checksum(void) { + // Stored the checksum value in the last byte. + _.CmdSum = calcChecksum(_.cmdState); + _.TimeSum = calcChecksum(_.timeState); +} + +/// Is the current state a time command? +/// @return true, if the state is a time message. Otherwise, false. +bool IRVestelAc::isTimeCommand(void) const { + return !_.UseCmd; +} + +/// 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 IRVestelAc::convertMode(const stdAc::opmode_t mode) { + switch (mode) { + case stdAc::opmode_t::kCool: return kVestelAcCool; + case stdAc::opmode_t::kHeat: return kVestelAcHeat; + case stdAc::opmode_t::kDry: return kVestelAcDry; + case stdAc::opmode_t::kFan: return kVestelAcFan; + default: return kVestelAcAuto; + } +} + +/// 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 IRVestelAc::convertFan(const stdAc::fanspeed_t speed) { + switch (speed) { + case stdAc::fanspeed_t::kMin: + case stdAc::fanspeed_t::kLow: return kVestelAcFanLow; + case stdAc::fanspeed_t::kMedium: return kVestelAcFanMed; + case stdAc::fanspeed_t::kHigh: + case stdAc::fanspeed_t::kMax: return kVestelAcFanHigh; + default: return kVestelAcFanAuto; + } +} + +/// 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 IRVestelAc::toCommonMode(const uint8_t mode) { + switch (mode) { + case kVestelAcCool: return stdAc::opmode_t::kCool; + case kVestelAcHeat: return stdAc::opmode_t::kHeat; + case kVestelAcDry: return stdAc::opmode_t::kDry; + case kVestelAcFan: return stdAc::opmode_t::kFan; + default: return stdAc::opmode_t::kAuto; + } +} + +/// Convert a native fan speed into its stdAc equivalent. +/// @param[in] spd The native setting to be converted. +/// @return The stdAc equivalent of the native setting. +stdAc::fanspeed_t IRVestelAc::toCommonFanSpeed(const uint8_t spd) { + switch (spd) { + case kVestelAcFanHigh: return stdAc::fanspeed_t::kMax; + case kVestelAcFanMed: return stdAc::fanspeed_t::kMedium; + case kVestelAcFanLow: 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 IRVestelAc::toCommon(void) const { + stdAc::state_t result; + result.protocol = decode_type_t::VESTEL_AC; + result.model = -1; // Not supported. + result.power = _.Power; + result.mode = toCommonMode(_.Mode); + result.celsius = true; + result.degrees = getTemp(); + result.fanspeed = toCommonFanSpeed(_.Fan); + result.swingv = (getSwing() ? stdAc::swingv_t::kAuto + : stdAc::swingv_t::kOff); + result.turbo = getTurbo(); + result.filter = _.Ion; + result.sleep = (getSleep() ? 0 : -1); + // Not supported. + result.swingh = stdAc::swingh_t::kOff; + result.light = 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 IRVestelAc::toString(void) const { + String result = ""; + result.reserve(100); // Reserve some heap for the string to reduce fragging. + if (isTimeCommand()) { + result += addLabeledString(minsToString(getTime()), kClockStr, false); + result += addLabeledString( + (_.Timer ? minsToString(getTimer()) : kOffStr), + kTimerStr); + result += addLabeledString( + (_.OnTimer && !_.Timer) ? minsToString(getOnTimer()) : kOffStr, + kOnTimerStr); + result += addLabeledString( + (_.OffTimer ? minsToString(getOffTimer()) : kOffStr), + kOffTimerStr); + return result; + } + // Not a time command, it's a normal command. + result += addBoolToString(_.Power, kPowerStr, false); + result += addModeToString(_.Mode, kVestelAcAuto, kVestelAcCool, + kVestelAcHeat, kVestelAcDry, kVestelAcFan); + result += addTempToString(getTemp()); + result += addIntToString(_.Fan, kFanStr); + result += kSpaceLBraceStr; + switch (_.Fan) { + case kVestelAcFanAuto: + result += kAutoStr; + break; + case kVestelAcFanLow: + result += kLowStr; + break; + case kVestelAcFanMed: + result += kMedStr; + break; + case kVestelAcFanHigh: + result += kHighStr; + break; + case kVestelAcFanAutoCool: + result += kAutoStr; + result += ' '; + result += kCoolStr; + break; + case kVestelAcFanAutoHot: + result += kAutoStr; + result += ' '; + result += kHeatStr; + break; + default: + result += kUnknownStr; + } + result += ')'; + result += addBoolToString(getSleep(), kSleepStr); + result += addBoolToString(getTurbo(), kTurboStr); + result += addBoolToString(_.Ion, kIonStr); + result += addBoolToString(getSwing(), kSwingStr); + return result; +} + +#if DECODE_VESTEL_AC +/// Decode the supplied Vestel 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. +/// @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::decodeVestelAc(decode_results* results, uint16_t offset, + const uint16_t nbits, const bool strict) { + if (nbits % 8 != 0) // nbits has to be a multiple of nr. of bits in a byte. + return false; + + if (strict) + if (nbits != kVestelAcBits) + return false; // Not strictly a Vestel AC message. + + uint64_t data = 0; + + if (nbits > sizeof(data) * 8) + return false; // We can't possibly capture a Vestel packet that big. + + // Match Header + Data + Footer + if (!matchGeneric(results->rawbuf + offset, &data, + results->rawlen - offset, nbits, + kVestelAcHdrMark, kVestelAcHdrSpace, + kVestelAcBitMark, kVestelAcOneSpace, + kVestelAcBitMark, kVestelAcZeroSpace, + kVestelAcBitMark, 0, false, + kVestelAcTolerance, kMarkExcess, false)) return false; + // Compliance + if (strict) + if (!IRVestelAc::validChecksum(data)) return false; + + // Success + results->decode_type = VESTEL_AC; + results->bits = nbits; + results->value = data; + results->address = 0; + results->command = 0; + + return true; +} +#endif // DECODE_VESTEL_AC diff --git a/lib/IRremoteESP8266/src/ir_Vestel.h b/lib/IRremoteESP8266/src/ir_Vestel.h index 0b3bd2c4b6..53ebdc7cda 100644 --- a/lib/IRremoteESP8266/src/ir_Vestel.h +++ b/lib/IRremoteESP8266/src/ir_Vestel.h @@ -16,10 +16,10 @@ #ifdef ARDUINO #include #endif -#include "../src/IRremoteESP8266.h" -#include "../src/IRsend.h" +#include "IRremoteESP8266.h" +#include "IRsend.h" #ifdef UNIT_TEST -#include "../src/IRsend_test.h" +#include "IRsend_test.h" #endif /// Native representation of a Vestel A/C message. diff --git a/lib/IRremoteESP8266/src/ir_Voltas.cpp b/lib/IRremoteESP8266/src/ir_Voltas.cpp new file mode 100644 index 0000000000..847446f385 --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_Voltas.cpp @@ -0,0 +1,516 @@ +// Copyright 2020 David Conran (crankyoldgit) +// Copyright 2020 manj9501 +/// @file +/// @brief Support for Voltas A/C protocol +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1238 + +#include "ir_Voltas.h" +#include +#include +#include "IRrecv.h" +#include "IRsend.h" +#include "IRtext.h" +#include "IRutils.h" + +using irutils::addBoolToString; +using irutils::addModelToString; +using irutils::addModeToString; +using irutils::addFanToString; +using irutils::addLabeledString; +using irutils::addTempToString; +using irutils::minsToString; + +// Constants +const uint16_t kVoltasBitMark = 1026; ///< uSeconds. +const uint16_t kVoltasOneSpace = 2553; ///< uSeconds. +const uint16_t kVoltasZeroSpace = 554; ///< uSeconds. +const uint16_t kVoltasFreq = 38000; ///< Hz. + +#if SEND_VOLTAS +/// Send a Voltas formatted message. +/// Status: STABLE / Working on real device. +/// @param[in] data An array of bytes containing the IR command. +/// It is assumed to be in MSB order for this code. +/// e.g. +/// @code +/// uint8_t data[kVoltasStateLength] = {0x33, 0x28, 0x88, 0x1A, 0x3B, 0x3B, +/// 0x3B, 0x11, 0x00, 0x40}; +/// @endcode +/// @param[in] nbytes Nr. of bytes of data in the array. (>=kVoltasStateLength) +/// @param[in] repeat Nr. of times the message is to be repeated. +void IRsend::sendVoltas(const uint8_t data[], const uint16_t nbytes, + const uint16_t repeat) { + sendGeneric(0, 0, + kVoltasBitMark, kVoltasOneSpace, + kVoltasBitMark, kVoltasZeroSpace, + kVoltasBitMark, kDefaultMessageGap, + data, nbytes, + kVoltasFreq, true, repeat, kDutyDefault); +} +#endif // SEND_VOLTAS + +#if DECODE_VOLTAS +/// Decode the supplied Voltas message. +/// Status: STABLE / Working on real device. +/// @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::decodeVoltas(decode_results *results, uint16_t offset, + const uint16_t nbits, const bool strict) { + if (strict && nbits != kVoltasBits) return false; + + // Data + Footer + if (!matchGeneric(results->rawbuf + offset, results->state, + results->rawlen - offset, nbits, + 0, 0, // No header + kVoltasBitMark, kVoltasOneSpace, + kVoltasBitMark, kVoltasZeroSpace, + kVoltasBitMark, kDefaultMessageGap, true)) return false; + + // Compliance + if (strict && !IRVoltas::validChecksum(results->state, nbits / 8)) + return false; + // Success + results->decode_type = decode_type_t::VOLTAS; + results->bits = nbits; + return true; +} +#endif // DECODE_VOLTAS + +/// 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? +IRVoltas::IRVoltas(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 IRVoltas::stateReset() { + // This resets to a known-good state. + // ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/1238#issuecomment-674699746 + const uint8_t kReset[kVoltasStateLength] = { + 0x33, 0x28, 0x00, 0x17, 0x3B, 0x3B, 0x3B, 0x11, 0x00, 0xCB}; + setRaw(kReset); +} + +/// Set up hardware to be able to send a message. +void IRVoltas::begin() { _irsend.begin(); } + +#if SEND_VOLTAS +/// Send the current internal state as an IR message. +/// @param[in] repeat Nr. of times the message will be repeated. +void IRVoltas::send(const uint16_t repeat) { + _irsend.sendVoltas(getRaw(), kVoltasStateLength, repeat); +} +#endif // SEND_VOLTAS + +/// Get the model information currently known. +/// @param[in] raw Work out the model info from the current raw state. +/// @return The known model number. +voltas_ac_remote_model_t IRVoltas::getModel(const bool raw) const { + if (raw) { + switch (_.SwingHChange) { + case kVoltasSwingHNoChange: + return voltas_ac_remote_model_t::kVoltas122LZF; + default: + return voltas_ac_remote_model_t::kVoltasUnknown; + } + } else { + return _model; + } +} + +/// Set the current model for the remote. +/// @param[in] model The model number. +void IRVoltas::setModel(const voltas_ac_remote_model_t model) { + switch (model) { + case voltas_ac_remote_model_t::kVoltas122LZF: + _model = model; + setSwingHChange(false); + break; + default: _model = voltas_ac_remote_model_t::kVoltasUnknown; + } +} + +/// 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* IRVoltas::getRaw(void) { + checksum(); // Ensure correct settings before sending. + return _.raw; +} + +/// Set the internal state from a valid code for this protocol. +/// @param[in] new_code A valid code for this protocol. +void IRVoltas::setRaw(const uint8_t new_code[]) { + std::memcpy(_.raw, new_code, kVoltasStateLength); + setModel(getModel(true)); +} + +/// Calculate and set the checksum values for the internal state. +void IRVoltas::checksum(void) { + _.Checksum = calcChecksum(_.raw); +} + +/// 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 IRVoltas::validChecksum(const uint8_t state[], const uint16_t length) { + if (length) return state[length - 1] == calcChecksum(state, length); + return true; +} + +/// Calculate the checksum is valid for a given state. +/// @param[in] state The array to calculate the checksum of. +/// @param[in] length The length of the state array. +/// @return The valid checksum value for the state. +uint8_t IRVoltas::calcChecksum(const uint8_t state[], const uint16_t length) { + uint8_t result = 0; + if (length) + result = sumBytes(state, length - 1); + return ~result; +} + +/// Change the power setting to On. +void IRVoltas::on() { setPower(true); } + +/// Change the power setting to Off. +void IRVoltas::off() { setPower(false); } + +/// Change the power setting. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRVoltas::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 IRVoltas::getPower(void) const { return _.Power; } + +/// Set the operating mode of the A/C. +/// @param[in] mode The desired operating mode. +/// @note If we get an unexpected mode, default to AUTO. +void IRVoltas::setMode(const uint8_t mode) { + _.Mode = mode; + switch (mode) { + case kVoltasFan: + setFan(getFan()); // Force the fan speed to a correct one fo the mode. + break; + case kVoltasDry: + setFan(kVoltasFanLow); + setTemp(kVoltasDryTemp); + break; + case kVoltasHeat: + case kVoltasCool: + break; + default: + setMode(kVoltasCool); + return; + } + // Reset some settings if needed. + setEcono(getEcono()); + setTurbo(getTurbo()); + setSleep(getSleep()); +} + +/// Get the operating mode setting of the A/C. +/// @return The current operating mode setting. +uint8_t IRVoltas::getMode(void) { 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 IRVoltas::convertMode(const stdAc::opmode_t mode) { + switch (mode) { + case stdAc::opmode_t::kHeat: return kVoltasHeat; + case stdAc::opmode_t::kDry: return kVoltasDry; + case stdAc::opmode_t::kFan: return kVoltasFan; + default: return kVoltasCool; + } +} + +/// 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 IRVoltas::toCommonMode(const uint8_t mode) { + switch (mode) { + case kVoltasHeat: return stdAc::opmode_t::kHeat; + case kVoltasDry: return stdAc::opmode_t::kDry; + case kVoltasFan: return stdAc::opmode_t::kFan; + default: return stdAc::opmode_t::kCool; + } +} + +/// Set the temperature. +/// @param[in] temp The temperature in degrees celsius. +void IRVoltas::setTemp(const uint8_t temp) { + uint8_t new_temp = std::max(kVoltasMinTemp, temp); + new_temp = std::min(kVoltasMaxTemp, new_temp); + _.Temp = new_temp - kVoltasMinTemp; +} + +/// Get the current temperature setting. +/// @return The current setting for temp. in degrees celsius. +uint8_t IRVoltas::getTemp(void) { return _.Temp + kVoltasMinTemp; } + +/// Set the speed of the fan. +/// @param[in] fan The desired setting. +void IRVoltas::setFan(const uint8_t fan) { + switch (fan) { + case kVoltasFanAuto: + if (_.Mode == kVoltasFan) { // Auto speed is not available in fan mode. + setFan(kVoltasFanHigh); + return; + } + // FALL-THRU + case kVoltasFanLow: + case kVoltasFanMed: + case kVoltasFanHigh: + _.FanSpeed = fan; + break; + default: + setFan(kVoltasFanAuto); + } +} + +/// Get the current fan speed setting. +/// @return The current fan speed/mode. +uint8_t IRVoltas::getFan(void) { return _.FanSpeed; } + +/// 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 IRVoltas::convertFan(const stdAc::fanspeed_t speed) { + switch (speed) { + case stdAc::fanspeed_t::kMin: + case stdAc::fanspeed_t::kLow: return kVoltasFanLow; + case stdAc::fanspeed_t::kMedium: return kVoltasFanMed; + case stdAc::fanspeed_t::kHigh: + case stdAc::fanspeed_t::kMax: return kVoltasFanHigh; + default: return kVoltasFanAuto; + } +} + +/// Convert a native fan speed into its stdAc equivalent. +/// @param[in] spd The native setting to be converted. +/// @return The stdAc equivalent of the native setting. +stdAc::fanspeed_t IRVoltas::toCommonFanSpeed(const uint8_t spd) { + switch (spd) { + case kVoltasFanHigh: return stdAc::fanspeed_t::kMax; + case kVoltasFanMed: return stdAc::fanspeed_t::kMedium; + case kVoltasFanLow: 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 IRVoltas::setSwingV(const bool on) { _.SwingV = on ? 0b111 : 0b000; } + +/// Get the Vertical Swing setting of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRVoltas::getSwingV(void) const { return _.SwingV == 0b111; } + +/// Set the Horizontal Swing setting of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRVoltas::setSwingH(const bool on) { + switch (_model) { + case voltas_ac_remote_model_t::kVoltas122LZF: + break; // unsupported on these models. + default: + _.SwingH = on; + setSwingHChange(true); + } +} + +/// Get the Horizontal Swing setting of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRVoltas::getSwingH(void) const { + switch (_model) { + case voltas_ac_remote_model_t::kVoltas122LZF: + return false; // unsupported on these models. + default: + return _.SwingH; + } +} + +/// Set the bits for changing the Horizontal Swing setting of the A/C. +/// @param[in] on true, the change bits are set. +/// false, the "no change" bits are set. +void IRVoltas::setSwingHChange(const bool on) { + _.SwingHChange = on ? kVoltasSwingHChange : kVoltasSwingHNoChange; + if (!on) _.SwingH = true; // "No Change" also sets SwingH to 1. +} + +/// Are the Horizontal Swing change bits set in the message? +/// @return true, the correct bits are set. false, the correct bits are not set. +bool IRVoltas::getSwingHChange(void) const { + return _.SwingHChange == kVoltasSwingHChange; +} + +/// Change the Wifi setting. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRVoltas::setWifi(const bool on) { _.Wifi = on; } + +/// Get the value of the current Wifi setting. +/// @return true, the setting is on. false, the setting is off. +bool IRVoltas::getWifi(void) const { return _.Wifi; } + +/// Change the Turbo setting. +/// @param[in] on true, the setting is on. false, the setting is off. +/// @note The Turbo setting is only available in Cool mode. +void IRVoltas::setTurbo(const bool on) { + if (on && _.Mode == kVoltasCool) + _.Turbo = true; + else + _.Turbo = false; +} + +/// Get the value of the current Turbo setting. +/// @return true, the setting is on. false, the setting is off. +bool IRVoltas::getTurbo(void) const { return _.Turbo; } + +/// Change the Economy setting. +/// @param[in] on true, the setting is on. false, the setting is off. +/// @note The Economy setting is only available in Cool mode. +void IRVoltas::setEcono(const bool on) { + if (on && _.Mode == kVoltasCool) + _.Econo = true; + else + _.Econo = false; +} + +/// Get the value of the current Econo setting. +/// @return true, the setting is on. false, the setting is off. +bool IRVoltas::getEcono(void) const { return _.Econo; } + +/// Change the Light setting. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRVoltas::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 IRVoltas::getLight(void) const { return _.Light; } + +/// Change the Sleep setting. +/// @param[in] on true, the setting is on. false, the setting is off. +/// @note The Sleep setting is only available in Cool mode. +void IRVoltas::setSleep(const bool on) { + if (on && _.Mode == kVoltasCool) + _.Sleep = true; + else + _.Sleep = false; +} + +/// Get the value of the current Sleep setting. +/// @return true, the setting is on. false, the setting is off. +bool IRVoltas::getSleep(void) const { return _.Sleep; } + +/// Get the value of the On Timer time. +/// @return Number of minutes before the timer activates. +uint16_t IRVoltas::getOnTime(void) const { + return std::min((unsigned)(12 * _.OnTimer12Hr + _.OnTimerHrs - 1), 23U) * 60 + + _.OnTimerMins; +} + +/// Set the value of the On Timer time. +/// @param[in] nr_of_mins Number of minutes before the timer activates. +/// 0 disables the timer. Max is 23 hrs & 59 mins (1439 mins) +void IRVoltas::setOnTime(const uint16_t nr_of_mins) { + // Cap the total number of mins. + uint16_t mins = std::min(nr_of_mins, (uint16_t)(23 * 60 + 59)); + uint16_t hrs = (mins / 60) + 1; + _.OnTimerMins = mins % 60; + _.OnTimer12Hr = hrs / 12; + _.OnTimerHrs = hrs % 12; + _.OnTimerEnable = (mins > 0); // Is the timer is to be enabled? +} + +/// Get the value of the On Timer time. +/// @return Number of minutes before the timer activates. +uint16_t IRVoltas::getOffTime(void) const { + return std::min((unsigned)(12 * _.OffTimer12Hr + _.OffTimerHrs - 1), 23U) * + 60 + _.OffTimerMins; +} + +/// Set the value of the Off Timer time. +/// @param[in] nr_of_mins Number of minutes before the timer activates. +/// 0 disables the timer. Max is 23 hrs & 59 mins (1439 mins) +void IRVoltas::setOffTime(const uint16_t nr_of_mins) { + // Cap the total number of mins. + uint16_t mins = std::min(nr_of_mins, (uint16_t)(23 * 60 + 59)); + uint16_t hrs = (mins / 60) + 1; + _.OffTimerMins = mins % 60; + _.OffTimer12Hr = hrs / 12; + _.OffTimerHrs = hrs % 12; + _.OffTimerEnable = (mins > 0); // Is the timer is to be enabled? +} + +/// Convert the current internal state into its stdAc::state_t equivalent. +/// @param[in] prev Ptr to the previous state if available. +/// @return The stdAc equivalent of the native settings. +stdAc::state_t IRVoltas::toCommon(const stdAc::state_t *prev) { + 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. + result.swingh = stdAc::swingh_t::kOff; + } + result.model = getModel(); + result.protocol = decode_type_t::VOLTAS; + result.power = _.Power; + result.mode = toCommonMode(_.Mode); + result.celsius = true; + result.degrees = getTemp(); + result.fanspeed = toCommonFanSpeed(_.FanSpeed); + result.swingv = getSwingV() ? stdAc::swingv_t::kAuto : stdAc::swingv_t::kOff; + if (getSwingHChange()) + result.swingh = _.SwingH ? stdAc::swingh_t::kAuto : stdAc::swingh_t::kOff; + result.turbo = _.Turbo; + result.econo = _.Econo; + result.light = _.Light; + result.sleep = _.Sleep ? 0 : -1; + // Not supported. + result.quiet = 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 IRVoltas::toString() { + String result = ""; + result.reserve(200); // Reserve some heap for the string to reduce fragging. + result += addModelToString(decode_type_t::VOLTAS, getModel(), false); + result += addBoolToString(_.Power, kPowerStr); + result += addModeToString(_.Mode, 255, kVoltasCool, kVoltasHeat, + kVoltasDry, kVoltasFan); + result += addTempToString(getTemp()); + result += addFanToString(_.FanSpeed, kVoltasFanHigh, kVoltasFanLow, + kVoltasFanAuto, kVoltasFanAuto, kVoltasFanMed); + result += addBoolToString(getSwingV(), kSwingVStr); + if (getSwingHChange()) + result += addBoolToString(_.SwingH, kSwingHStr); + else + result += addLabeledString(kNAStr, kSwingHStr); + result += addBoolToString(_.Turbo, kTurboStr); + result += addBoolToString(_.Econo, kEconoStr); + result += addBoolToString(_.Wifi, kWifiStr); + result += addBoolToString(_.Light, kLightStr); + result += addBoolToString(_.Sleep, kSleepStr); + result += addLabeledString(_.OnTimerEnable ? minsToString(getOnTime()) + : kOffStr, kOnTimerStr); + result += addLabeledString(_.OffTimerEnable ? minsToString(getOffTime()) + : kOffStr, kOffTimerStr); + return result; +} diff --git a/lib/IRremoteESP8266/src/ir_Voltas.h b/lib/IRremoteESP8266/src/ir_Voltas.h index 64ab1b5127..cf79d3458f 100644 --- a/lib/IRremoteESP8266/src/ir_Voltas.h +++ b/lib/IRremoteESP8266/src/ir_Voltas.h @@ -18,10 +18,10 @@ #ifndef UNIT_TEST #include #endif -#include "../src/IRremoteESP8266.h" -#include "../src/IRsend.h" +#include "IRremoteESP8266.h" +#include "IRsend.h" #ifdef UNIT_TEST -#include "../src/IRsend_test.h" +#include "IRsend_test.h" #endif /// Native representation of a Voltas A/C message. diff --git a/lib/IRremoteESP8266/src/ir_Whirlpool.cpp b/lib/IRremoteESP8266/src/ir_Whirlpool.cpp new file mode 100644 index 0000000000..0bba57bfec --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_Whirlpool.cpp @@ -0,0 +1,657 @@ +// Copyright 2018 David Conran + +/// @file +/// @brief Support for Whirlpool protocols. +/// Decoding help from: \@redmusicxd, \@josh929800, \@raducostea +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/509 +/// @note Smart, iFeel, AroundU, PowerSave, & Silent modes are unsupported. +/// Advanced 6thSense, Dehumidify, & Sleep modes are not supported. +/// @note Dim == !Light, Jet == Super == Turbo + +#include "ir_Whirlpool.h" +#include +#include +#ifndef ARDUINO +#include +#endif +#include "IRrecv.h" +#include "IRremoteESP8266.h" +#include "IRsend.h" +#include "IRtext.h" +#include "IRutils.h" + +// Constants +const uint16_t kWhirlpoolAcHdrMark = 8950; +const uint16_t kWhirlpoolAcHdrSpace = 4484; +const uint16_t kWhirlpoolAcBitMark = 597; +const uint16_t kWhirlpoolAcOneSpace = 1649; +const uint16_t kWhirlpoolAcZeroSpace = 533; +const uint16_t kWhirlpoolAcGap = 7920; +const uint32_t kWhirlpoolAcMinGap = kDefaultMessageGap; // Just a guess. +const uint8_t kWhirlpoolAcSections = 3; + +using irutils::addBoolToString; +using irutils::addFanToString; +using irutils::addIntToString; +using irutils::addLabeledString; +using irutils::addModeToString; +using irutils::addModelToString; +using irutils::addTempToString; +using irutils::minsToString; + +#define GETTIME(x) (_.x##Hours * 60 + _.x##Mins) +#define SETTIME(x, n) do { \ + uint16_t mins = n;\ + _.x##Hours = (mins / 60) % 24;\ + _.x##Mins = mins % 60;\ +} while (0) + +#if SEND_WHIRLPOOL_AC +/// Send a Whirlpool A/C message. +/// Status: BETA / Probably works. +/// @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::sendWhirlpoolAC(const unsigned char data[], const uint16_t nbytes, + const uint16_t repeat) { + if (nbytes < kWhirlpoolAcStateLength) + return; // Not enough bytes to send a proper message. + for (uint16_t r = 0; r <= repeat; r++) { + // Section 1 + sendGeneric(kWhirlpoolAcHdrMark, kWhirlpoolAcHdrSpace, kWhirlpoolAcBitMark, + kWhirlpoolAcOneSpace, kWhirlpoolAcBitMark, + kWhirlpoolAcZeroSpace, kWhirlpoolAcBitMark, kWhirlpoolAcGap, + data, 6, // 6 bytes == 48 bits + 38000, // Complete guess of the modulation frequency. + false, 0, 50); + // Section 2 + sendGeneric(0, 0, kWhirlpoolAcBitMark, kWhirlpoolAcOneSpace, + kWhirlpoolAcBitMark, kWhirlpoolAcZeroSpace, kWhirlpoolAcBitMark, + kWhirlpoolAcGap, data + 6, 8, // 8 bytes == 64 bits + 38000, // Complete guess of the modulation frequency. + false, 0, 50); + // Section 3 + sendGeneric(0, 0, kWhirlpoolAcBitMark, kWhirlpoolAcOneSpace, + kWhirlpoolAcBitMark, kWhirlpoolAcZeroSpace, kWhirlpoolAcBitMark, + kWhirlpoolAcMinGap, data + 14, 7, // 7 bytes == 56 bits + 38000, // Complete guess of the modulation frequency. + false, 0, 50); + } +} +#endif // SEND_WHIRLPOOL_AC + +// Class for emulating a Whirlpool A/C remote. + +/// 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? +IRWhirlpoolAc::IRWhirlpoolAc(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 IRWhirlpoolAc::stateReset(void) { + for (uint8_t i = 2; i < kWhirlpoolAcStateLength; i++) _.raw[i] = 0x0; + _.raw[0] = 0x83; + _.raw[1] = 0x06; + _.raw[6] = 0x80; + _setTemp(kWhirlpoolAcAutoTemp); // Default to a sane value. +} + +/// Set up hardware to be able to send a message. +void IRWhirlpoolAc::begin(void) { _irsend.begin(); } + +/// 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 IRWhirlpoolAc::validChecksum(const uint8_t state[], + const uint16_t length) { + if (length > kWhirlpoolAcChecksumByte1 && + state[kWhirlpoolAcChecksumByte1] != + xorBytes(state + 2, kWhirlpoolAcChecksumByte1 - 1 - 2)) { + DPRINTLN("DEBUG: First Whirlpool AC checksum failed."); + return false; + } + if (length > kWhirlpoolAcChecksumByte2 && + state[kWhirlpoolAcChecksumByte2] != + xorBytes(state + kWhirlpoolAcChecksumByte1 + 1, + kWhirlpoolAcChecksumByte2 - kWhirlpoolAcChecksumByte1 - 1)) { + DPRINTLN("DEBUG: Second Whirlpool AC checksum failed."); + return false; + } + // State is too short to have a checksum or everything checked out. + return true; +} + +/// Calculate & set the checksum for the current internal state of the remote. +/// @param[in] length The length/size of the internal state array. +void IRWhirlpoolAc::checksum(uint16_t length) { + if (length >= kWhirlpoolAcChecksumByte1) + _.Sum1 = xorBytes(_.raw + 2, kWhirlpoolAcChecksumByte1 - 1 - 2); + if (length >= kWhirlpoolAcChecksumByte2) + _.Sum2 = xorBytes(_.raw + kWhirlpoolAcChecksumByte1 + 1, + kWhirlpoolAcChecksumByte2 - kWhirlpoolAcChecksumByte1 - 1); +} + +#if SEND_WHIRLPOOL_AC +/// Send the current internal state as an IR message. +/// @param[in] repeat Nr. of times the message will be repeated. +/// @param[in] calcchecksum Do we need to calculate the checksum?. +void IRWhirlpoolAc::send(const uint16_t repeat, const bool calcchecksum) { + _irsend.sendWhirlpoolAC(getRaw(calcchecksum), kWhirlpoolAcStateLength, + repeat); +} +#endif // SEND_WHIRLPOOL_AC + +/// Get a copy of the internal state/code for this protocol. +/// @param[in] calcchecksum Do we need to calculate the checksum?. +/// @return A code for this protocol based on the current internal state. +uint8_t *IRWhirlpoolAc::getRaw(const bool calcchecksum) { + if (calcchecksum) 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 IRWhirlpoolAc::setRaw(const uint8_t new_code[], const uint16_t length) { + std::memcpy(_.raw, new_code, std::min(length, kWhirlpoolAcStateLength)); +} + +/// Get/Detect the model of the A/C. +/// @return The enum of the compatible model. +whirlpool_ac_remote_model_t IRWhirlpoolAc::getModel(void) const { + if (_.J191) + return DG11J191; + else + return DG11J13A; +} + +/// Set the model of the A/C to emulate. +/// @param[in] model The enum of the appropriate model. +void IRWhirlpoolAc::setModel(const whirlpool_ac_remote_model_t model) { + switch (model) { + case DG11J191: + _.J191 = true; + break; + case DG11J13A: + // FALL THRU + default: + _.J191 = false; + } + _setTemp(_desiredtemp); // Different models have different temp values. +} + +/// Calculate the temp. offset in deg C for the current model. +/// @return The temperature offset. +int8_t IRWhirlpoolAc::getTempOffset(void) const { + switch (getModel()) { + case whirlpool_ac_remote_model_t::DG11J191: return -2; + default: return 0; + } +} + +/// Set the temperature. +/// @param[in] temp The temperature in degrees celsius. +/// @param[in] remember Do we save this temperature? +/// @note Internal use only. +void IRWhirlpoolAc::_setTemp(const uint8_t temp, const bool remember) { + if (remember) _desiredtemp = temp; + int8_t offset = getTempOffset(); // Cache the min temp for the model. + uint8_t newtemp = std::max((uint8_t)(kWhirlpoolAcMinTemp + offset), temp); + newtemp = std::min((uint8_t)(kWhirlpoolAcMaxTemp + offset), newtemp); + _.Temp = newtemp - (kWhirlpoolAcMinTemp + offset); +} + +/// Set the temperature. +/// @param[in] temp The temperature in degrees celsius. +void IRWhirlpoolAc::setTemp(const uint8_t temp) { + _setTemp(temp); + setSuper(false); // Changing temp cancels Super/Jet mode. + _.Cmd = kWhirlpoolAcCommandTemp; +} + +/// Get the current temperature setting. +/// @return The current setting for temp. in degrees celsius. +uint8_t IRWhirlpoolAc::getTemp(void) const { + return _.Temp + kWhirlpoolAcMinTemp + getTempOffset(); +} + +/// Set the operating mode of the A/C. +/// @param[in] mode The desired operating mode. +/// @note Internal use only. +void IRWhirlpoolAc::_setMode(const uint8_t mode) { + switch (mode) { + case kWhirlpoolAcAuto: + setFan(kWhirlpoolAcFanAuto); + _setTemp(kWhirlpoolAcAutoTemp, false); + setSleep(false); // Cancel sleep mode when in auto/6thsense mode. + // FALL THRU + case kWhirlpoolAcHeat: + case kWhirlpoolAcCool: + case kWhirlpoolAcDry: + case kWhirlpoolAcFan: + _.Mode = mode; + _.Cmd = kWhirlpoolAcCommandMode; + break; + default: + return; + } + if (mode == kWhirlpoolAcAuto) _.Cmd = kWhirlpoolAcCommand6thSense; +} + +/// Set the operating mode of the A/C. +/// @param[in] mode The desired operating mode. +void IRWhirlpoolAc::setMode(const uint8_t mode) { + setSuper(false); // Changing mode cancels Super/Jet mode. + _setMode(mode); +} + +/// Get the operating mode setting of the A/C. +/// @return The current operating mode setting. +uint8_t IRWhirlpoolAc::getMode(void) const { + return _.Mode; +} + +/// Set the speed of the fan. +/// @param[in] speed The desired setting. +void IRWhirlpoolAc::setFan(const uint8_t speed) { + switch (speed) { + case kWhirlpoolAcFanAuto: + case kWhirlpoolAcFanLow: + case kWhirlpoolAcFanMedium: + case kWhirlpoolAcFanHigh: + _.Fan = speed; + setSuper(false); // Changing fan speed cancels Super/Jet mode. + _.Cmd = kWhirlpoolAcCommandFanSpeed; + break; + } +} + +/// Get the current fan speed setting. +/// @return The current fan speed/mode. +uint8_t IRWhirlpoolAc::getFan(void) const { + return _.Fan; +} + +/// Set the (vertical) swing setting of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRWhirlpoolAc::setSwing(const bool on) { + _.Swing1 = on; + _.Swing2 = on; + _.Cmd = kWhirlpoolAcCommandSwing; +} + +/// Get the (vertical) swing setting of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRWhirlpoolAc::getSwing(void) const { + return _.Swing1 && _.Swing2; +} + +/// Set the Light (Display/LED) setting of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRWhirlpoolAc::setLight(const bool on) { + // Cleared when on. + _.LightOff = !on; +} + +/// Get the Light (Display/LED) setting of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRWhirlpoolAc::getLight(void) const { + return !_.LightOff; +} + +/// Set the clock time in nr. of minutes past midnight. +/// @param[in] minspastmidnight The time expressed as minutes past midnight. +void IRWhirlpoolAc::setClock(const uint16_t minspastmidnight) { + SETTIME(Clock, minspastmidnight); +} + +/// Get the clock time in nr. of minutes past midnight. +/// @return The time expressed as the Nr. of minutes past midnight. +uint16_t IRWhirlpoolAc::getClock(void) const { + return GETTIME(Clock); +} + +/// Set the Off Timer time. +/// @param[in] minspastmidnight The time expressed as minutes past midnight. +void IRWhirlpoolAc::setOffTimer(const uint16_t minspastmidnight) { + SETTIME(Off, minspastmidnight); +} + +/// Get the Off Timer time.. +/// @return The time expressed as the Nr. of minutes past midnight. +uint16_t IRWhirlpoolAc::getOffTimer(void) const { + return GETTIME(Off); +} + +/// Is the Off timer enabled? +/// @return true, the Timer is enabled. false, the Timer is disabled. +bool IRWhirlpoolAc::isOffTimerEnabled(void) const { + return _.OffTimerEnabled; +} + +/// Enable the Off Timer. +/// @param[in] on true, the timer is enabled. false, the timer is disabled. +void IRWhirlpoolAc::enableOffTimer(const bool on) { + _.OffTimerEnabled = on; + _.Cmd = kWhirlpoolAcCommandOffTimer; +} + +/// Set the On Timer time. +/// @param[in] minspastmidnight The time expressed as minutes past midnight. +void IRWhirlpoolAc::setOnTimer(const uint16_t minspastmidnight) { + SETTIME(On, minspastmidnight); +} + +/// Get the On Timer time.. +/// @return The time expressed as the Nr. of minutes past midnight. +uint16_t IRWhirlpoolAc::getOnTimer(void) const { + return GETTIME(On); +} + +/// Is the On timer enabled? +/// @return true, the Timer is enabled. false, the Timer is disabled. +bool IRWhirlpoolAc::isOnTimerEnabled(void) const { + return _.OnTimerEnabled; +} + +/// Enable the On Timer. +/// @param[in] on true, the timer is enabled. false, the timer is disabled. +void IRWhirlpoolAc::enableOnTimer(const bool on) { + _.OnTimerEnabled = on; + _.Cmd = kWhirlpoolAcCommandOnTimer; +} + +/// Change the power toggle setting. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRWhirlpoolAc::setPowerToggle(const bool on) { + _.Power = on; + setSuper(false); // Changing power cancels Super/Jet mode. + _.Cmd = kWhirlpoolAcCommandPower; +} + +/// Get the value of the current power toggle setting. +/// @return true, the setting is on. false, the setting is off. +bool IRWhirlpoolAc::getPowerToggle(void) const { + return _.Power; +} + +/// Get the Command (Button) setting of the A/C. +/// @return The current Command (Button) of the A/C. +uint8_t IRWhirlpoolAc::getCommand(void) const { + return _.Cmd; +} + +/// Set the Sleep setting of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRWhirlpoolAc::setSleep(const bool on) { + _.Sleep = on; + if (on) setFan(kWhirlpoolAcFanLow); + _.Cmd = kWhirlpoolAcCommandSleep; +} + +/// Get the Sleep setting of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRWhirlpoolAc::getSleep(void) const { + return _.Sleep; +} + +/// Set the Super (Turbo/Jet) setting of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRWhirlpoolAc::setSuper(const bool on) { + if (on) { + setFan(kWhirlpoolAcFanHigh); + switch (_.Mode) { + case kWhirlpoolAcHeat: + setTemp(kWhirlpoolAcMaxTemp + getTempOffset()); + break; + case kWhirlpoolAcCool: + default: + setTemp(kWhirlpoolAcMinTemp + getTempOffset()); + setMode(kWhirlpoolAcCool); + break; + } + _.Super1 = 1; + _.Super2 = 1; + } else { + _.Super1 = 0; + _.Super2 = 0; + } + _.Cmd = kWhirlpoolAcCommandSuper; +} + +/// Get the Super (Turbo/Jet) setting of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRWhirlpoolAc:: getSuper(void) const { + return _.Super1 && _.Super2; +} + +/// Set the Command (Button) setting of the A/C. +/// @param[in] code The current Command (Button) of the A/C. +void IRWhirlpoolAc::setCommand(const uint8_t code) { + _.Cmd = code; +} + +/// 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 IRWhirlpoolAc::convertMode(const stdAc::opmode_t mode) { + switch (mode) { + case stdAc::opmode_t::kAuto: return kWhirlpoolAcAuto; + case stdAc::opmode_t::kHeat: return kWhirlpoolAcHeat; + case stdAc::opmode_t::kDry: return kWhirlpoolAcDry; + case stdAc::opmode_t::kFan: return kWhirlpoolAcFan; + // Default to Cool as some Whirlpool models don't have an Auto mode. + // Ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/1283 + default: return kWhirlpoolAcCool; + } +} + +/// 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 IRWhirlpoolAc::convertFan(const stdAc::fanspeed_t speed) { + switch (speed) { + case stdAc::fanspeed_t::kMin: + case stdAc::fanspeed_t::kLow: return kWhirlpoolAcFanLow; + case stdAc::fanspeed_t::kMedium: return kWhirlpoolAcFanMedium; + case stdAc::fanspeed_t::kHigh: + case stdAc::fanspeed_t::kMax: return kWhirlpoolAcFanHigh; + default: return kWhirlpoolAcFanAuto; + } +} + +/// 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 IRWhirlpoolAc::toCommonMode(const uint8_t mode) { + switch (mode) { + case kWhirlpoolAcCool: return stdAc::opmode_t::kCool; + case kWhirlpoolAcHeat: return stdAc::opmode_t::kHeat; + case kWhirlpoolAcDry: return stdAc::opmode_t::kDry; + case kWhirlpoolAcFan: 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 IRWhirlpoolAc::toCommonFanSpeed(const uint8_t speed) { + switch (speed) { + case kWhirlpoolAcFanHigh: return stdAc::fanspeed_t::kMax; + case kWhirlpoolAcFanMedium: return stdAc::fanspeed_t::kMedium; + case kWhirlpoolAcFanLow: 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 Ptr to the previous state if required. +/// @return The stdAc equivalent of the native settings. +stdAc::state_t IRWhirlpoolAc::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::WHIRLPOOL_AC; + result.model = getModel(); + if (_.Power) result.power = !result.power; + result.mode = toCommonMode(_.Mode); + result.celsius = true; + result.degrees = getTemp(); + result.fanspeed = toCommonFanSpeed(_.Fan); + result.swingv = getSwing() ? stdAc::swingv_t::kAuto : stdAc::swingv_t::kOff; + result.turbo = getSuper(); + result.light = getLight(); + result.sleep = _.Sleep ? 0 : -1; + // Not supported. + result.swingh = stdAc::swingh_t::kOff; + result.quiet = false; + result.filter = false; + result.econo = 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 IRWhirlpoolAc::toString(void) const { + String result = ""; + result.reserve(200); // Reserve some heap for the string to reduce fragging. + result += addModelToString(decode_type_t::WHIRLPOOL_AC, getModel(), false); + result += addBoolToString(_.Power, kPowerToggleStr); + result += addModeToString(_.Mode, kWhirlpoolAcAuto, kWhirlpoolAcCool, + kWhirlpoolAcHeat, kWhirlpoolAcDry, kWhirlpoolAcFan); + result += addTempToString(getTemp()); + result += addFanToString(_.Fan, kWhirlpoolAcFanHigh, kWhirlpoolAcFanLow, + kWhirlpoolAcFanAuto, kWhirlpoolAcFanAuto, + kWhirlpoolAcFanMedium); + result += addBoolToString(getSwing(), kSwingStr); + result += addBoolToString(getLight(), kLightStr); + result += addLabeledString(minsToString(getClock()), kClockStr); + result += addLabeledString( + _.OnTimerEnabled ? minsToString(getOnTimer()) : kOffStr, kOnTimerStr); + result += addLabeledString( + _.OffTimerEnabled ? minsToString(getOffTimer()) : kOffStr, kOffTimerStr); + result += addBoolToString(_.Sleep, kSleepStr); + result += addBoolToString(getSuper(), kSuperStr); + result += addIntToString(_.Cmd, kCommandStr); + result += kSpaceLBraceStr; + switch (_.Cmd) { + case kWhirlpoolAcCommandLight: + result += kLightStr; + break; + case kWhirlpoolAcCommandPower: + result += kPowerStr; + break; + case kWhirlpoolAcCommandTemp: + result += kTempStr; + break; + case kWhirlpoolAcCommandSleep: + result += kSleepStr; + break; + case kWhirlpoolAcCommandSuper: + result += kSuperStr; + break; + case kWhirlpoolAcCommandOnTimer: + result += kOnTimerStr; + break; + case kWhirlpoolAcCommandMode: + result += kModeStr; + break; + case kWhirlpoolAcCommandSwing: + result += kSwingStr; + break; + case kWhirlpoolAcCommandIFeel: + result += kIFeelStr; + break; + case kWhirlpoolAcCommandFanSpeed: + result += kFanStr; + break; + case kWhirlpoolAcCommand6thSense: + result += k6thSenseStr; + break; + case kWhirlpoolAcCommandOffTimer: + result += kOffTimerStr; + break; + default: + result += kUnknownStr; + break; + } + result += ')'; + return result; +} + +#if DECODE_WHIRLPOOL_AC + +/// Decode the supplied Whirlpool A/C message. +/// Status: STABLE / Working as intended. +/// @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::decodeWhirlpoolAC(decode_results *results, uint16_t offset, + const uint16_t nbits, const bool strict) { + if (results->rawlen < 2 * nbits + 4 + kHeader + kFooter - 1 + offset) + return false; // Can't possibly be a valid Whirlpool A/C message. + if (strict) { + if (nbits != kWhirlpoolAcBits) return false; + } + + const uint8_t sectionSize[kWhirlpoolAcSections] = {6, 8, 7}; + + // Header + if (!matchMark(results->rawbuf[offset++], kWhirlpoolAcHdrMark)) return false; + if (!matchSpace(results->rawbuf[offset++], kWhirlpoolAcHdrSpace)) + return false; + + // Data Sections + uint16_t pos = 0; + for (uint8_t section = 0; section < kWhirlpoolAcSections; + section++) { + uint16_t used; + // Section Data + used = matchGeneric(results->rawbuf + offset, results->state + pos, + results->rawlen - offset, sectionSize[section] * 8, + 0, 0, + kWhirlpoolAcBitMark, kWhirlpoolAcOneSpace, + kWhirlpoolAcBitMark, kWhirlpoolAcZeroSpace, + kWhirlpoolAcBitMark, kWhirlpoolAcGap, + section >= kWhirlpoolAcSections - 1, + _tolerance, kMarkExcess, false); + if (used == 0) return false; + offset += used; + pos += sectionSize[section]; + } + + // Compliance + if (strict) { + // Re-check we got the correct size/length due to the way we read the data. + if (pos * 8 != nbits) return false; + if (!IRWhirlpoolAc::validChecksum(results->state, nbits / 8)) + return false; + } + + // Success + results->decode_type = WHIRLPOOL_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 // WHIRLPOOL_AC diff --git a/lib/IRremoteESP8266/src/ir_Whirlpool.h b/lib/IRremoteESP8266/src/ir_Whirlpool.h index 87e7105068..ac73be926b 100644 --- a/lib/IRremoteESP8266/src/ir_Whirlpool.h +++ b/lib/IRremoteESP8266/src/ir_Whirlpool.h @@ -26,10 +26,10 @@ #ifndef UNIT_TEST #include #endif -#include "../src/IRremoteESP8266.h" -#include "../src/IRsend.h" +#include "IRremoteESP8266.h" +#include "IRsend.h" #ifdef UNIT_TEST -#include "../src/IRsend_test.h" +#include "IRsend_test.h" #endif /// Native representation of a Whirlpool A/C message. diff --git a/lib/IRremoteESP8266/src/ir_Whynter.cpp b/lib/IRremoteESP8266/src/ir_Whynter.cpp new file mode 100644 index 0000000000..7b184eb0be --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_Whynter.cpp @@ -0,0 +1,103 @@ +// Copyright 2009 Ken Shirriff +// Copyright 2017 David Conran + +/// @file +/// @brief Support for Whynter protocols. +/// Whynter A/C ARC-110WD added by Francesco Meschia +/// Whynter originally added from https://github.com/shirriff/Arduino-IRremote/ + +// Supports: +// Brand: Whynter, Model: ARC-110WD A/C + +#include +#include "IRrecv.h" +#include "IRsend.h" +#include "IRutils.h" + +// Constants +const uint16_t kWhynterTick = 50; +const uint16_t kWhynterHdrMarkTicks = 57; +const uint16_t kWhynterHdrMark = kWhynterHdrMarkTicks * kWhynterTick; +const uint16_t kWhynterHdrSpaceTicks = 57; +const uint16_t kWhynterHdrSpace = kWhynterHdrSpaceTicks * kWhynterTick; +const uint16_t kWhynterBitMarkTicks = 15; +const uint16_t kWhynterBitMark = kWhynterBitMarkTicks * kWhynterTick; +const uint16_t kWhynterOneSpaceTicks = 43; +const uint16_t kWhynterOneSpace = kWhynterOneSpaceTicks * kWhynterTick; +const uint16_t kWhynterZeroSpaceTicks = 15; +const uint16_t kWhynterZeroSpace = kWhynterZeroSpaceTicks * kWhynterTick; +const uint16_t kWhynterMinCommandLengthTicks = 2160; // Totally made up value. +const uint32_t kWhynterMinCommandLength = + kWhynterMinCommandLengthTicks * kWhynterTick; +const uint16_t kWhynterMinGapTicks = + kWhynterMinCommandLengthTicks - + (2 * (kWhynterBitMarkTicks + kWhynterZeroSpaceTicks) + + kWhynterBits * (kWhynterBitMarkTicks + kWhynterOneSpaceTicks)); +const uint16_t kWhynterMinGap = kWhynterMinGapTicks * kWhynterTick; + +#if SEND_WHYNTER +/// Send a Whynter message. +/// Status: STABLE +/// @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_Whynter.cpp +void IRsend::sendWhynter(const uint64_t data, const uint16_t nbits, + const uint16_t repeat) { + // Set IR carrier frequency + enableIROut(38); + + for (uint16_t i = 0; i <= repeat; i++) { + // (Pre-)Header + mark(kWhynterBitMark); + space(kWhynterZeroSpace); + sendGeneric( + kWhynterHdrMark, kWhynterHdrSpace, kWhynterBitMark, kWhynterOneSpace, + kWhynterBitMark, kWhynterZeroSpace, kWhynterBitMark, kWhynterMinGap, + kWhynterMinCommandLength - (kWhynterBitMark + kWhynterZeroSpace), data, + nbits, 38, true, 0, // Repeats are already handled. + 50); + } +} +#endif // SEND_WHYNTER + +#if DECODE_WHYNTER +/// Decode the supplied Whynter message. +/// Status: STABLE / Working. Strict mode is ALPHA. +/// @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/z3t0/Arduino-IRremote/blob/master/ir_Whynter.cpp +bool IRrecv::decodeWhynter(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; // We don't have enough entries to possibly match. + + // Compliance + if (strict && nbits != kWhynterBits) + return false; // Incorrect nr. of bits per spec. + + uint64_t data = 0; + // Pre-Header + // Sequence begins with a bit mark and a zero space. + if (!matchMark(results->rawbuf[offset++], kWhynterBitMark)) return false; + if (!matchSpace(results->rawbuf[offset++], kWhynterZeroSpace)) return false; + // Match Main Header + Data + Footer + if (!matchGeneric(results->rawbuf + offset, &data, + results->rawlen - offset, nbits, + kWhynterHdrMark, kWhynterHdrSpace, + kWhynterBitMark, kWhynterOneSpace, + kWhynterBitMark, kWhynterZeroSpace, + kWhynterBitMark, kWhynterMinGap, true)) return false; + // Success + results->decode_type = WHYNTER; + results->bits = nbits; + results->value = data; + results->address = 0; + results->command = 0; + return true; +} +#endif // DECODE_WHYNTER diff --git a/lib/IRremoteESP8266/src/ir_Xmp.cpp b/lib/IRremoteESP8266/src/ir_Xmp.cpp new file mode 100644 index 0000000000..29d7f6c1bb --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_Xmp.cpp @@ -0,0 +1,226 @@ +// Copyright 2021 David Conran + +/// @file +/// @brief Support for XMP protocols. +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1414 +/// @see http://www.hifi-remote.com/wiki/index.php/XMP + +// Supports: +// Brand: Xfinity, Model: XR2 remote +// Brand: Xfinity, Model: XR11 remote + + +#include +#include "IRrecv.h" +#include "IRsend.h" +#include "IRutils.h" + +// Constants +const uint16_t kXmpMark = 210; ///< uSeconds. +const uint16_t kXmpBaseSpace = 760; ///< uSeconds +const uint16_t kXmpSpaceStep = 135; ///< uSeconds +const uint16_t kXmpFooterSpace = 13000; ///< uSeconds. +const uint32_t kXmpMessageGap = 80400; ///< uSeconds. +const uint8_t kXmpWordSize = kNibbleSize; ///< nr. of Bits in a word. +const uint8_t kXmpMaxWordValue = (1 << kXmpWordSize) - 1; // Max word value. +const uint8_t kXmpSections = 2; ///< Nr. of Data sections +const uint8_t kXmpRepeatCode = 0b1000; +const uint8_t kXmpRepeatCodeAlt = 0b1001; + +using irutils::setBits; + +namespace IRXmpUtils { + /// Get the current checksum value from an XMP data section. + /// @param[in] data The value of the data section. + /// @param[in] nbits The number of data bits in the section. + /// @return The value of the stored checksum. + /// @warning Returns 0 if we can't obtain a valid checksum. + uint8_t getSectionChecksum(const uint32_t data, const uint16_t nbits) { + // The checksum is the 2nd most significant nibble of a section. + return (nbits < 2 * kNibbleSize) ? 0 : GETBITS32(data, + nbits - (2 * kNibbleSize), + kNibbleSize); + } + + /// Calculate the correct checksum value for an XMP data section. + /// @param[in] data The value of the data section. + /// @param[in] nbits The number of data bits in the section. + /// @return The value of the correct checksum. + uint8_t calcSectionChecksum(const uint32_t data, const uint16_t nbits) { + return (0xF & ~(irutils::sumNibbles(data, nbits / kNibbleSize, 0xF, false) - + getSectionChecksum(data, nbits))); + } + + /// Recalculate a XMP message code ensuring it has the checksums valid. + /// @param[in] data The value of the XMP message code. + /// @param[in] nbits The number of data bits in the entire message code. + /// @return The corrected XMP message with valid checksum sections. + uint64_t updateChecksums(const uint64_t data, const uint16_t nbits) { + const uint16_t sectionbits = nbits / kXmpSections; + uint64_t result = data; + for (uint16_t sectionOffset = 0; sectionOffset < nbits; + sectionOffset += sectionbits) { + const uint16_t checksumOffset = sectionOffset + sectionbits - + (2 * kNibbleSize); + setBits(&result, checksumOffset, kNibbleSize, + calcSectionChecksum(GETBITS64(data, sectionOffset, sectionbits), + sectionbits)); + } + return result; + } + + /// Calculate the bit offset the repeat nibble in an XMP code. + /// @param[in] nbits The number of data bits in the entire message code. + /// @return The offset to the start of the XMP repeat nibble. + uint16_t calcRepeatOffset(const uint16_t nbits) { + return (nbits < 3 * kNibbleSize) ? 0 + : (nbits / kXmpSections) - + (3 * kNibbleSize); + } + + /// Test if an XMP message code is a repeat or not. + /// @param[in] data The value of the XMP message code. + /// @param[in] nbits The number of data bits in the entire message code. + /// @return true, if it looks like a repeat, false if not. + bool isRepeat(const uint64_t data, const uint16_t nbits) { + switch (GETBITS64(data, calcRepeatOffset(nbits), kNibbleSize)) { + case kXmpRepeatCode: + case kXmpRepeatCodeAlt: + return true; + default: + return false; + } + } + + /// Adjust an XMP message code to make it a valid repeat or non-repeat code. + /// @param[in] data The value of the XMP message code. + /// @param[in] nbits The number of data bits in the entire message code. + /// @param[in] repeat_code The value of the XMP repeat nibble to use. + /// A value of `8` is the normal value for a repeat. `9` has also been seen. + /// A value of `0` will convert the code to a non-repeat code. + /// @return The valud of the modified XMP code. + uint64_t adjustRepeat(const uint64_t data, const uint16_t nbits, + const uint8_t repeat_code) { + uint64_t result = data; + setBits(&result, calcRepeatOffset(nbits), kNibbleSize, repeat_code); + return updateChecksums(result, nbits); + } +} // namespace IRXmpUtils + +using IRXmpUtils::calcSectionChecksum; +using IRXmpUtils::getSectionChecksum; +using IRXmpUtils::isRepeat; +using IRXmpUtils::adjustRepeat; + + +#if SEND_XMP +/// Send a XMP packet. +/// Status: Beta / Untested 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::sendXmp(const uint64_t data, const uint16_t nbits, + const uint16_t repeat) { + enableIROut(38000); + if (nbits < 2 * kXmpWordSize) return; // Too small to send, abort! + uint64_t send_data = data; + for (uint16_t r = 0; r <= repeat; r++) { + uint16_t bits_so_far = kXmpWordSize; + for (uint64_t mask = ((uint64_t)kXmpMaxWordValue) << (nbits - kXmpWordSize); + mask; + mask >>= kXmpWordSize) { + uint8_t word = (send_data & mask) >> (nbits - bits_so_far); + mark(kXmpMark); + space(kXmpBaseSpace + word * kXmpSpaceStep); + bits_so_far += kXmpWordSize; + // Are we at a data section boundary? + if ((bits_so_far - kXmpWordSize) % (nbits / kXmpSections) == 0) { // Yes. + mark(kXmpMark); + space(kXmpFooterSpace); + } + } + space(kXmpMessageGap - kXmpFooterSpace); + + // Modify the value if needed, to make it into a valid repeat code. + if (!isRepeat(send_data, nbits)) + send_data = adjustRepeat(send_data, nbits, kXmpRepeatCode); + } +} +#endif // SEND_XMP + +#if DECODE_XMP +/// Decode the supplied XMP packet/message. +/// Status: BETA / Probably works. +/// @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::decodeXmp(decode_results *results, uint16_t offset, + const uint16_t nbits, const bool strict) { + uint64_t data = 0; + + if (results->rawlen < 2 * (nbits / kXmpWordSize) + (kXmpSections * kFooter) + + offset - 1) + return false; // Not enough entries to ever be XMP. + + // Compliance + if (strict && nbits != kXmpBits) return false; + + // Data + // Sections + for (uint8_t section = 1; section <= kXmpSections; section++) { + for (uint16_t bits_so_far = 0; bits_so_far < nbits / kXmpSections; + bits_so_far += kXmpWordSize) { + if (!matchMarkRange(results->rawbuf[offset++], kXmpMark)) return 0; + uint8_t value = 0; + bool found = false; + for (; value <= kXmpMaxWordValue; value++) { + if (matchSpaceRange(results->rawbuf[offset], + kXmpBaseSpace + value * kXmpSpaceStep, + kXmpSpaceStep / 2, 0)) { + found = true; + break; + } + } + if (!found) return 0; // Failure. + data <<= kXmpWordSize; + data += value; + offset++; + } + // Section Footer + if (!matchMarkRange(results->rawbuf[offset++], kXmpMark)) return 0; + if (section < kXmpSections) { + if (!matchSpace(results->rawbuf[offset++], kXmpFooterSpace)) return 0; + } else { // Last section + if (offset < results->rawlen && + !matchAtLeast(results->rawbuf[offset++], kXmpFooterSpace)) return 0; + } + } + + // Compliance + if (strict) { + // Validate checksums. + uint64_t checksum_data = data; + const uint16_t section_size = nbits / kXmpSections; + // Each section has a checksum. + for (uint16_t section = 0; section < kXmpSections; section++) { + if (getSectionChecksum(checksum_data, section_size) != + calcSectionChecksum(checksum_data, section_size)) + return 0; + checksum_data >>= section_size; + } + } + + // Success + results->value = data; + results->decode_type = decode_type_t::XMP; + results->bits = nbits; + results->address = 0; + results->command = 0; + // See if it is a repeat message. + results->repeat = isRepeat(data, nbits); + return true; +} +#endif // DECODE_XMP diff --git a/lib/IRremoteESP8266/src/ir_Zepeal.cpp b/lib/IRremoteESP8266/src/ir_Zepeal.cpp new file mode 100644 index 0000000000..96017226c3 --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_Zepeal.cpp @@ -0,0 +1,94 @@ +// Copyright 2020 Christian Nilsson (nikize) + +/// @file +/// @brief Support for Zepeal protocol. +/// This protocol uses fixed length bit encoding. +/// Most official information about Zepeal seems to be from Denkyosha +/// @see https://www.denkyosha.co.jp/ + +// Supports: +// Brand: Zepeal, Model: DRT-A3311(BG) floor fan +// Brand: Zepeal, Model: DRT-A3311(BG) 5 button remote + +#include "IRrecv.h" +#include "IRsend.h" +#include "IRutils.h" + +// Constants + +const uint16_t kZepealHdrMark = 2330; +const uint16_t kZepealHdrSpace = 3380; +const uint16_t kZepealOneMark = 1300; +const uint16_t kZepealZeroMark = 420; +const uint16_t kZepealOneSpace = kZepealZeroMark; +const uint16_t kZepealZeroSpace = kZepealOneMark; +const uint16_t kZepealFooterMark = 420; +const uint16_t kZepealGap = 6750; + +const uint8_t kZepealTolerance = 40; + +// Signature limits possible false possitvies, +// but might need change (removal) if more devices are detected +const uint8_t kZepealSignature = 0x6C; + +// Known Zepeal DRT-A3311(BG) Buttons - documentation rather than actual usage +const uint16_t kZepealCommandSpeed = 0x6C82; +const uint16_t kZepealCommandOffOn = 0x6C81; +const uint16_t kZepealCommandRhythm = 0x6C84; +const uint16_t kZepealCommandOffTimer = 0x6C88; +const uint16_t kZepealCommandOnTimer = 0x6CC3; + +#if SEND_ZEPEAL +/// Send a Zepeal formatted message. +/// Status: STABLE / Works on 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::sendZepeal(const uint64_t data, const uint16_t nbits, + const uint16_t repeat) { + sendGeneric(kZepealHdrMark, kZepealHdrSpace, + kZepealOneMark, kZepealOneSpace, + kZepealZeroMark, kZepealZeroSpace, + kZepealFooterMark, kZepealGap, + data, nbits, 38, true, repeat, kDutyDefault); +} +#endif // SEND_ZEPEAL + +#if DECODE_ZEPEAL +/// Decode the supplied Zepeal 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. Typically kZepealBits. +/// @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::decodeZepeal(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 != kZepealBits) + return false; // Not strictly a message. + + uint64_t data = 0; + uint16_t used; + used = matchGeneric(results->rawbuf + offset, &data, + results->rawlen - offset, nbits, + kZepealHdrMark, kZepealHdrSpace, + kZepealOneMark, kZepealOneSpace, + kZepealZeroMark, kZepealZeroSpace, + kZepealFooterMark, kZepealGap, true, + kZepealTolerance); + if (!used) return false; + if (strict && (data >> 8) != kZepealSignature) return false; + + // Success + results->value = data; + results->decode_type = decode_type_t::ZEPEAL; + results->bits = nbits; + results->address = 0; + results->command = 0; + return true; +} +#endif // DECODE_ZEPEAL diff --git a/lib/IRremoteESP8266/test/IRrecv_test.cpp b/lib/IRremoteESP8266/test/IRrecv_test.cpp index aada6aa82b..2d32847d38 100644 --- a/lib/IRremoteESP8266/test/IRrecv_test.cpp +++ b/lib/IRremoteESP8266/test/IRrecv_test.cpp @@ -1,10 +1,10 @@ // Copyright 2017 David Conran -#include "../src/IRrecv_test.h" -#include "../src/IRrecv.h" -#include "../src/IRremoteESP8266.h" -#include "../src/IRsend.h" -#include "../src/IRsend_test.h" +#include "IRrecv_test.h" +#include "IRrecv.h" +#include "IRremoteESP8266.h" +#include "IRsend.h" +#include "IRsend_test.h" #include "gtest/gtest.h" // Tests for the IRrecv object. diff --git a/lib/IRremoteESP8266/test/IRrecv_test.h b/lib/IRremoteESP8266/test/IRrecv_test.h index e4e0e70954..bb366c1ee0 100644 --- a/lib/IRremoteESP8266/test/IRrecv_test.h +++ b/lib/IRremoteESP8266/test/IRrecv_test.h @@ -6,7 +6,7 @@ #include #include #include -#include "../src/IRutils.h" +#include "IRutils.h" #define EXPECT_STATE_EQ(a, b, c) \ for (uint8_t i = 0; i < c / 8; ++i) { \ diff --git a/lib/IRremoteESP8266/test/IRsend_test.cpp b/lib/IRremoteESP8266/test/IRsend_test.cpp index ffd230d2b1..51fae74996 100644 --- a/lib/IRremoteESP8266/test/IRsend_test.cpp +++ b/lib/IRremoteESP8266/test/IRsend_test.cpp @@ -1,9 +1,9 @@ // Copyright 2017,2019 David Conran -#include "../src/IRsend_test.h" -#include "../src/IRrecv_test.h" -#include "../src/IRsend.h" -#include "../src/IRutils.h" +#include "IRsend_test.h" +#include "IRrecv_test.h" +#include "IRsend.h" +#include "IRutils.h" #include "gtest/gtest.h" // Tests sendData(). diff --git a/lib/IRremoteESP8266/test/IRsend_test.h b/lib/IRremoteESP8266/test/IRsend_test.h index ec900a44e9..f434094332 100644 --- a/lib/IRremoteESP8266/test/IRsend_test.h +++ b/lib/IRremoteESP8266/test/IRsend_test.h @@ -8,9 +8,9 @@ #include #include #include -#include "../src/IRrecv.h" -#include "../src/IRsend.h" -#include "../src/IRtimer.h" +#include "IRrecv.h" +#include "IRsend.h" +#include "IRtimer.h" #define OUTPUT_BUF 10000U #define RAW_BUF 10000U diff --git a/lib/IRremoteESP8266/test/IRutils_test.cpp b/lib/IRremoteESP8266/test/IRutils_test.cpp index 8f398f7650..73ba569991 100644 --- a/lib/IRremoteESP8266/test/IRutils_test.cpp +++ b/lib/IRremoteESP8266/test/IRutils_test.cpp @@ -1,11 +1,11 @@ // Copyright 2017-2019 David Conran -#include "../src/IRutils.h" +#include "IRutils.h" #include -#include "../src/IRrecv.h" -#include "../src/IRrecv_test.h" -#include "../src/IRsend.h" -#include "../src/IRsend_test.h" +#include "IRrecv.h" +#include "IRrecv_test.h" +#include "IRsend.h" +#include "IRsend_test.h" #include "gtest/gtest.h" // Tests reverseBits(). diff --git a/lib/IRremoteESP8266/test/ir_Airwell_test.cpp b/lib/IRremoteESP8266/test/ir_Airwell_test.cpp index c9d14f36dd..e5f28d4df4 100644 --- a/lib/IRremoteESP8266/test/ir_Airwell_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Airwell_test.cpp @@ -1,11 +1,11 @@ // Copyright 2020 David Conran -#include "../src/ir_Airwell.h" -#include "../src/IRac.h" -#include "../src/IRrecv.h" -#include "../src/IRrecv_test.h" -#include "../src/IRsend.h" -#include "../src/IRsend_test.h" +#include "ir_Airwell.h" +#include "IRac.h" +#include "IRrecv.h" +#include "IRrecv_test.h" +#include "IRsend.h" +#include "IRsend_test.h" #include "gtest/gtest.h" // Tests for decodeAirwell(). diff --git a/lib/IRremoteESP8266/test/ir_Aiwa_test.cpp b/lib/IRremoteESP8266/test/ir_Aiwa_test.cpp index 78e3e33b03..f87d5c5e28 100644 --- a/lib/IRremoteESP8266/test/ir_Aiwa_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Aiwa_test.cpp @@ -1,7 +1,7 @@ // Copyright 2017 David Conran -#include "../src/IRsend.h" -#include "../src/IRsend_test.h" +#include "IRsend.h" +#include "IRsend_test.h" #include "gtest/gtest.h" // Tests for sendAiwaRCT501(). diff --git a/lib/IRremoteESP8266/test/ir_Amcor_test.cpp b/lib/IRremoteESP8266/test/ir_Amcor_test.cpp index e8ba1eb3bc..787769516d 100644 --- a/lib/IRremoteESP8266/test/ir_Amcor_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Amcor_test.cpp @@ -1,12 +1,12 @@ // Copyright 2019 David Conran -#include "../src/IRac.h" -#include "../src/ir_Amcor.h" -#include "../src/IRrecv.h" -#include "../src/IRrecv_test.h" -#include "../src/IRsend.h" -#include "../src/IRsend_test.h" -#include "../src/IRutils.h" +#include "IRac.h" +#include "ir_Amcor.h" +#include "IRrecv.h" +#include "IRrecv_test.h" +#include "IRsend.h" +#include "IRsend_test.h" +#include "IRutils.h" #include "gtest/gtest.h" TEST(TestUtils, Housekeeping) { diff --git a/lib/IRremoteESP8266/test/ir_Argo_test.cpp b/lib/IRremoteESP8266/test/ir_Argo_test.cpp index ea8e16ab7d..3ab890adca 100644 --- a/lib/IRremoteESP8266/test/ir_Argo_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Argo_test.cpp @@ -1,10 +1,10 @@ // Copyright 2019 David Conran -#include "../src/ir_Argo.h" -#include "../src/IRac.h" -#include "../src/IRrecv.h" -#include "../src/IRrecv_test.h" -#include "../src/IRsend.h" -#include "../src/IRsend_test.h" +#include "ir_Argo.h" +#include "IRac.h" +#include "IRrecv.h" +#include "IRrecv_test.h" +#include "IRsend.h" +#include "IRsend_test.h" #include "gtest/gtest.h" diff --git a/lib/IRremoteESP8266/test/ir_Arris_test.cpp b/lib/IRremoteESP8266/test/ir_Arris_test.cpp index ab30f3046c..2a219b6b22 100644 --- a/lib/IRremoteESP8266/test/ir_Arris_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Arris_test.cpp @@ -1,10 +1,10 @@ // Copyright 2021 David Conran -#include "../src/IRac.h" -#include "../src/IRrecv.h" -#include "../src/IRrecv_test.h" -#include "../src/IRsend.h" -#include "../src/IRsend_test.h" +#include "IRac.h" +#include "IRrecv.h" +#include "IRrecv_test.h" +#include "IRsend.h" +#include "IRsend_test.h" #include "gtest/gtest.h" // Tests for decodeArris(). diff --git a/lib/IRremoteESP8266/test/ir_Bose_test.cpp b/lib/IRremoteESP8266/test/ir_Bose_test.cpp index 80b3204fe8..bf3e18c910 100644 --- a/lib/IRremoteESP8266/test/ir_Bose_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Bose_test.cpp @@ -1,11 +1,11 @@ // Copyright 2021 parsnip42 // Copyright 2021 David Conran -#include "../src/IRac.h" -#include "../src/IRrecv.h" -#include "../src/IRrecv_test.h" -#include "../src/IRsend.h" -#include "../src/IRsend_test.h" +#include "IRac.h" +#include "IRrecv.h" +#include "IRrecv_test.h" +#include "IRsend.h" +#include "IRsend_test.h" #include "gtest/gtest.h" diff --git a/lib/IRremoteESP8266/test/ir_Carrier_test.cpp b/lib/IRremoteESP8266/test/ir_Carrier_test.cpp index 10cc435aab..caf7e46d84 100644 --- a/lib/IRremoteESP8266/test/ir_Carrier_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Carrier_test.cpp @@ -1,10 +1,10 @@ // Copyright 2018, 2020 David Conran -#include "../src/ir_Carrier.h" -#include "../src/IRac.h" -#include "../src/IRrecv.h" -#include "../src/IRsend.h" -#include "../src/IRsend_test.h" +#include "ir_Carrier.h" +#include "IRac.h" +#include "IRrecv.h" +#include "IRsend.h" +#include "IRsend_test.h" #include "gtest/gtest.h" // Tests for sendCarrierAC() diff --git a/lib/IRremoteESP8266/test/ir_Coolix_test.cpp b/lib/IRremoteESP8266/test/ir_Coolix_test.cpp index 569f670a51..62e0d6b0e4 100644 --- a/lib/IRremoteESP8266/test/ir_Coolix_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Coolix_test.cpp @@ -1,9 +1,9 @@ // Copyright 2017-2018 David Conran -#include "../src/ir_Coolix.h" -#include "../src/IRac.h" -#include "../src/IRsend.h" -#include "../src/IRsend_test.h" +#include "ir_Coolix.h" +#include "IRac.h" +#include "IRsend.h" +#include "IRsend_test.h" #include "gtest/gtest.h" diff --git a/lib/IRremoteESP8266/test/ir_Corona_test.cpp b/lib/IRremoteESP8266/test/ir_Corona_test.cpp index 72c7758ea0..9c16e1f963 100644 --- a/lib/IRremoteESP8266/test/ir_Corona_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Corona_test.cpp @@ -1,11 +1,11 @@ // Copyright 2020 Christian Nilsson -#include "../src/ir_Corona.h" -#include "../src/IRac.h" -#include "../src/IRrecv.h" -#include "../src/IRrecv_test.h" -#include "../src/IRsend.h" -#include "../src/IRsend_test.h" +#include "ir_Corona.h" +#include "IRac.h" +#include "IRrecv.h" +#include "IRrecv_test.h" +#include "IRsend.h" +#include "IRsend_test.h" #include "gtest/gtest.h" // Tests for decodeCoronaAc(). diff --git a/lib/IRremoteESP8266/test/ir_Daikin_test.cpp b/lib/IRremoteESP8266/test/ir_Daikin_test.cpp index eadb508ccd..783bd4e66d 100644 --- a/lib/IRremoteESP8266/test/ir_Daikin_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Daikin_test.cpp @@ -1,10 +1,10 @@ // Copyright 2017-2019 David Conran -#include "../src/ir_Daikin.h" -#include "../src/IRac.h" -#include "../src/IRrecv.h" -#include "../src/IRrecv_test.h" -#include "../src/IRsend.h" -#include "../src/IRsend_test.h" +#include "ir_Daikin.h" +#include "IRac.h" +#include "IRrecv.h" +#include "IRrecv_test.h" +#include "IRsend.h" +#include "IRsend_test.h" #include "gtest/gtest.h" // Tests for sendDaikin(). diff --git a/lib/IRremoteESP8266/test/ir_Delonghi_test.cpp b/lib/IRremoteESP8266/test/ir_Delonghi_test.cpp index fd5a888248..9f069e40ec 100644 --- a/lib/IRremoteESP8266/test/ir_Delonghi_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Delonghi_test.cpp @@ -1,12 +1,12 @@ // Copyright 2020 David Conran -#include "../src/IRac.h" -#include "../src/ir_Delonghi.h" -#include "../src/IRrecv.h" -#include "../src/IRrecv_test.h" -#include "../src/IRsend.h" -#include "../src/IRsend_test.h" -#include "../src/IRutils.h" +#include "IRac.h" +#include "ir_Delonghi.h" +#include "IRrecv.h" +#include "IRrecv_test.h" +#include "IRsend.h" +#include "IRsend_test.h" +#include "IRutils.h" #include "gtest/gtest.h" TEST(TestUtils, Housekeeping) { diff --git a/lib/IRremoteESP8266/test/ir_Denon_test.cpp b/lib/IRremoteESP8266/test/ir_Denon_test.cpp index 0f2a4fded8..64be43f97e 100644 --- a/lib/IRremoteESP8266/test/ir_Denon_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Denon_test.cpp @@ -1,7 +1,7 @@ // Copyright 2017 David Conran -#include "../src/IRsend.h" -#include "../src/IRsend_test.h" +#include "IRsend.h" +#include "IRsend_test.h" #include "gtest/gtest.h" // Tests for sendDenon(). diff --git a/lib/IRremoteESP8266/test/ir_Dish_test.cpp b/lib/IRremoteESP8266/test/ir_Dish_test.cpp index 4c6cb5c895..d745648111 100644 --- a/lib/IRremoteESP8266/test/ir_Dish_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Dish_test.cpp @@ -1,7 +1,7 @@ // Copyright 2017 David Conran -#include "../src/IRsend.h" -#include "../src/IRsend_test.h" +#include "IRsend.h" +#include "IRsend_test.h" #include "gtest/gtest.h" // Tests for sendDISH(). diff --git a/lib/IRremoteESP8266/test/ir_Doshisha_test.cpp b/lib/IRremoteESP8266/test/ir_Doshisha_test.cpp index 3ee6ee11e6..8c69bfbb3b 100644 --- a/lib/IRremoteESP8266/test/ir_Doshisha_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Doshisha_test.cpp @@ -1,10 +1,10 @@ // Copyright 2020 Christian Nilsson -#include "../src/IRac.h" -#include "../src/IRrecv.h" -#include "../src/IRrecv_test.h" -#include "../src/IRsend.h" -#include "../src/IRsend_test.h" +#include "IRac.h" +#include "IRrecv.h" +#include "IRrecv_test.h" +#include "IRsend.h" +#include "IRsend_test.h" #include "gtest/gtest.h" // Tests for decodeDoshisha(). diff --git a/lib/IRremoteESP8266/test/ir_Ecoclim_test.cpp b/lib/IRremoteESP8266/test/ir_Ecoclim_test.cpp index 2cc2a662bb..fe031e4e27 100644 --- a/lib/IRremoteESP8266/test/ir_Ecoclim_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Ecoclim_test.cpp @@ -1,13 +1,13 @@ // Copyright 2021 David Conran -#include "../src/ir_Ecoclim.h" +#include "ir_Ecoclim.h" #include -#include "../src/IRac.h" -#include "../src/IRrecv.h" -#include "../src/IRrecv_test.h" -#include "../src/IRsend.h" -#include "../src/IRsend_test.h" -#include "../src/IRutils.h" +#include "IRac.h" +#include "IRrecv.h" +#include "IRrecv_test.h" +#include "IRsend.h" +#include "IRsend_test.h" +#include "IRutils.h" #include "gtest/gtest.h" TEST(TestUtils, Housekeeping) { diff --git a/lib/IRremoteESP8266/test/ir_Electra_test.cpp b/lib/IRremoteESP8266/test/ir_Electra_test.cpp index bca75f68a9..9c1dedd09f 100644 --- a/lib/IRremoteESP8266/test/ir_Electra_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Electra_test.cpp @@ -1,12 +1,12 @@ // Copyright 2018, 2019 David Conran -#include "../src/ir_Electra.h" +#include "ir_Electra.h" #include -#include "../src/IRac.h" -#include "../src/IRrecv.h" -#include "../src/IRrecv_test.h" -#include "../src/IRsend.h" -#include "../src/IRsend_test.h" +#include "IRac.h" +#include "IRrecv.h" +#include "IRrecv_test.h" +#include "IRsend.h" +#include "IRsend_test.h" #include "gtest/gtest.h" // Tests for sendElectraAC(). diff --git a/lib/IRremoteESP8266/test/ir_EliteScreens_test.cpp b/lib/IRremoteESP8266/test/ir_EliteScreens_test.cpp index 7bcda48c25..0af0f1f552 100644 --- a/lib/IRremoteESP8266/test/ir_EliteScreens_test.cpp +++ b/lib/IRremoteESP8266/test/ir_EliteScreens_test.cpp @@ -1,8 +1,8 @@ // Copyright 2017 David Conran -#include "../src/IRac.h" -#include "../src/IRsend.h" -#include "../src/IRsend_test.h" +#include "IRac.h" +#include "IRsend.h" +#include "IRsend_test.h" #include "gtest/gtest.h" // Housekeeping tests diff --git a/lib/IRremoteESP8266/test/ir_Epson_test.cpp b/lib/IRremoteESP8266/test/ir_Epson_test.cpp index 86c0cc185b..d1df3f9acc 100644 --- a/lib/IRremoteESP8266/test/ir_Epson_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Epson_test.cpp @@ -1,10 +1,10 @@ // Copyright 2020 David Conran -#include "../src/IRac.h" -#include "../src/IRrecv.h" -#include "../src/IRrecv_test.h" -#include "../src/IRsend.h" -#include "../src/IRsend_test.h" +#include "IRac.h" +#include "IRrecv.h" +#include "IRrecv_test.h" +#include "IRsend.h" +#include "IRsend_test.h" #include "gtest/gtest.h" // Tests for sendEpson(). diff --git a/lib/IRremoteESP8266/test/ir_Fujitsu_test.cpp b/lib/IRremoteESP8266/test/ir_Fujitsu_test.cpp index c05a2e5a4c..caf4e53f1f 100644 --- a/lib/IRremoteESP8266/test/ir_Fujitsu_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Fujitsu_test.cpp @@ -1,9 +1,9 @@ // Copyright 2017 Jonny Graham, David Conran -#include "../src/IRac.h" -#include "../src/IRrecv_test.h" -#include "../src/IRsend.h" -#include "../src/IRsend_test.h" -#include "../src/ir_Fujitsu.h" +#include "IRac.h" +#include "IRrecv_test.h" +#include "IRsend.h" +#include "IRsend_test.h" +#include "ir_Fujitsu.h" #include "gtest/gtest.h" // Tests for Fujitsu A/C methods. diff --git a/lib/IRremoteESP8266/test/ir_GICable_test.cpp b/lib/IRremoteESP8266/test/ir_GICable_test.cpp index e58f14bccc..234a748b5c 100644 --- a/lib/IRremoteESP8266/test/ir_GICable_test.cpp +++ b/lib/IRremoteESP8266/test/ir_GICable_test.cpp @@ -1,7 +1,7 @@ // Copyright 2018 David Conran -#include "../src/IRsend.h" -#include "../src/IRsend_test.h" +#include "IRsend.h" +#include "IRsend_test.h" #include "gtest/gtest.h" // Tests for sendGICable(). diff --git a/lib/IRremoteESP8266/test/ir_GlobalCache_test.cpp b/lib/IRremoteESP8266/test/ir_GlobalCache_test.cpp index c4d492ec0e..00742aedac 100644 --- a/lib/IRremoteESP8266/test/ir_GlobalCache_test.cpp +++ b/lib/IRremoteESP8266/test/ir_GlobalCache_test.cpp @@ -1,7 +1,7 @@ // Copyright 2017 David Conran -#include "../src/IRsend.h" -#include "../src/IRsend_test.h" +#include "IRsend.h" +#include "IRsend_test.h" #include "gtest/gtest.h" // Tests for sendGlobalCache(). diff --git a/lib/IRremoteESP8266/test/ir_Goodweather_test.cpp b/lib/IRremoteESP8266/test/ir_Goodweather_test.cpp index 9dbb729f09..be07b9d093 100644 --- a/lib/IRremoteESP8266/test/ir_Goodweather_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Goodweather_test.cpp @@ -1,11 +1,11 @@ // Copyright 2019 David Conran -#include "../src/ir_Goodweather.h" -#include "../src/IRac.h" -#include "../src/IRrecv.h" -#include "../src/IRrecv_test.h" -#include "../src/IRsend.h" -#include "../src/IRsend_test.h" +#include "ir_Goodweather.h" +#include "IRac.h" +#include "IRrecv.h" +#include "IRrecv_test.h" +#include "IRsend.h" +#include "IRsend_test.h" #include "gtest/gtest.h" TEST(TestIRUtils, Goodweather) { diff --git a/lib/IRremoteESP8266/test/ir_Gree_test.cpp b/lib/IRremoteESP8266/test/ir_Gree_test.cpp index 392f1f5817..cb1832f613 100644 --- a/lib/IRremoteESP8266/test/ir_Gree_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Gree_test.cpp @@ -1,12 +1,12 @@ // Copyright 2017 David Conran -#include "../src/ir_Gree.h" -#include "../src/IRac.h" -#include "../src/IRrecv.h" -#include "../src/IRrecv_test.h" -#include "../src/IRremoteESP8266.h" -#include "../src/IRsend.h" -#include "../src/IRsend_test.h" +#include "ir_Gree.h" +#include "IRac.h" +#include "IRrecv.h" +#include "IRrecv_test.h" +#include "IRremoteESP8266.h" +#include "IRsend.h" +#include "IRsend_test.h" #include "gtest/gtest.h" // Tests for sendGree(). diff --git a/lib/IRremoteESP8266/test/ir_Haier_test.cpp b/lib/IRremoteESP8266/test/ir_Haier_test.cpp index a69fe3ecb9..b60a08f23a 100644 --- a/lib/IRremoteESP8266/test/ir_Haier_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Haier_test.cpp @@ -1,11 +1,11 @@ // Copyright 2018 David Conran -#include "../src/ir_Haier.h" -#include "../src/IRac.h" -#include "../src/IRrecv.h" -#include "../src/IRrecv_test.h" -#include "../src/IRsend.h" -#include "../src/IRsend_test.h" +#include "ir_Haier.h" +#include "IRac.h" +#include "IRrecv.h" +#include "IRrecv_test.h" +#include "IRsend.h" +#include "IRsend_test.h" #include "gtest/gtest.h" // Tests for sendHaierAC() diff --git a/lib/IRremoteESP8266/test/ir_Hitachi_test.cpp b/lib/IRremoteESP8266/test/ir_Hitachi_test.cpp index bff5f350c1..2bbbd29e91 100644 --- a/lib/IRremoteESP8266/test/ir_Hitachi_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Hitachi_test.cpp @@ -1,12 +1,12 @@ // Copyright 2018 David Conran -#include "../src/ir_Hitachi.h" -#include "../src/IRac.h" -#include "../src/IRrecv.h" -#include "../src/IRrecv_test.h" -#include "../src/IRremoteESP8266.h" -#include "../src/IRsend.h" -#include "../src/IRsend_test.h" +#include "ir_Hitachi.h" +#include "IRac.h" +#include "IRrecv.h" +#include "IRrecv_test.h" +#include "IRremoteESP8266.h" +#include "IRsend.h" +#include "IRsend_test.h" #include "gtest/gtest.h" // Tests for sendHitachiAC(). diff --git a/lib/IRremoteESP8266/test/ir_Inax_test.cpp b/lib/IRremoteESP8266/test/ir_Inax_test.cpp index e9523954d0..b182cce32e 100644 --- a/lib/IRremoteESP8266/test/ir_Inax_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Inax_test.cpp @@ -1,8 +1,8 @@ // Copyright 2019 crankyoldgit (David Conran) -#include "../src/IRsend.h" -#include "../src/IRsend_test.h" -#include "../src/IRutils.h" +#include "IRsend.h" +#include "IRsend_test.h" +#include "IRutils.h" #include "gtest/gtest.h" diff --git a/lib/IRremoteESP8266/test/ir_JVC_test.cpp b/lib/IRremoteESP8266/test/ir_JVC_test.cpp index 3ab51d2101..51b16b82ce 100644 --- a/lib/IRremoteESP8266/test/ir_JVC_test.cpp +++ b/lib/IRremoteESP8266/test/ir_JVC_test.cpp @@ -1,7 +1,7 @@ // Copyright 2017 David Conran -#include "../src/IRsend.h" -#include "../src/IRsend_test.h" +#include "IRsend.h" +#include "IRsend_test.h" #include "gtest/gtest.h" // Tests for sendJVC(). diff --git a/lib/IRremoteESP8266/test/ir_Kelon_test.cpp b/lib/IRremoteESP8266/test/ir_Kelon_test.cpp index 5e02c26235..7212d49ee5 100644 --- a/lib/IRremoteESP8266/test/ir_Kelon_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Kelon_test.cpp @@ -1,10 +1,10 @@ // Copyright 2021 Davide Depau -#include "../src/IRac.h" -#include "../src/IRrecv.h" -#include "../src/IRrecv_test.h" -#include "../src/IRsend.h" -#include "../src/IRsend_test.h" +#include "IRac.h" +#include "IRrecv.h" +#include "IRrecv_test.h" +#include "IRsend.h" +#include "IRsend_test.h" #include "gtest/gtest.h" // Tests for sendKelon(). diff --git a/lib/IRremoteESP8266/test/ir_Kelvinator_test.cpp b/lib/IRremoteESP8266/test/ir_Kelvinator_test.cpp index a7b4552573..73ad6581d8 100644 --- a/lib/IRremoteESP8266/test/ir_Kelvinator_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Kelvinator_test.cpp @@ -1,12 +1,12 @@ // Copyright 2017 David Conran -#include "../src/ir_Kelvinator.h" -#include "../src/IRac.h" -#include "../src/IRrecv.h" -#include "../src/IRrecv_test.h" -#include "../src/IRremoteESP8266.h" -#include "../src/IRsend.h" -#include "../src/IRsend_test.h" +#include "ir_Kelvinator.h" +#include "IRac.h" +#include "IRrecv.h" +#include "IRrecv_test.h" +#include "IRremoteESP8266.h" +#include "IRsend.h" +#include "IRsend_test.h" #include "gtest/gtest.h" // Tests for sendKelvinator(). diff --git a/lib/IRremoteESP8266/test/ir_LG_test.cpp b/lib/IRremoteESP8266/test/ir_LG_test.cpp index 54c4bcfafb..808f7b83af 100644 --- a/lib/IRremoteESP8266/test/ir_LG_test.cpp +++ b/lib/IRremoteESP8266/test/ir_LG_test.cpp @@ -1,9 +1,9 @@ // Copyright 2017, 2019 David Conran -#include "../src/ir_LG.h" -#include "../src/IRac.h" -#include "../src/IRsend.h" -#include "../src/IRsend_test.h" +#include "ir_LG.h" +#include "IRac.h" +#include "IRsend.h" +#include "IRsend_test.h" #include "gtest/gtest.h" diff --git a/lib/IRremoteESP8266/test/ir_Lasertag_test.cpp b/lib/IRremoteESP8266/test/ir_Lasertag_test.cpp index d3d96f299c..bad724f76d 100644 --- a/lib/IRremoteESP8266/test/ir_Lasertag_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Lasertag_test.cpp @@ -1,7 +1,7 @@ // Copyright 2017 David Conran -#include "../src/IRsend.h" -#include "../src/IRsend_test.h" +#include "IRsend.h" +#include "IRsend_test.h" #include "gtest/gtest.h" // LL AAA SSSSS EEEEEEE RRRRRR TTTTTTT AAA GGGG diff --git a/lib/IRremoteESP8266/test/ir_Lego_test.cpp b/lib/IRremoteESP8266/test/ir_Lego_test.cpp index c2aacb1c9b..4e859b1708 100644 --- a/lib/IRremoteESP8266/test/ir_Lego_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Lego_test.cpp @@ -1,9 +1,9 @@ // Copyright 2019 David Conran -#include "../src/IRrecv.h" -#include "../src/IRrecv_test.h" -#include "../src/IRsend.h" -#include "../src/IRsend_test.h" +#include "IRrecv.h" +#include "IRrecv_test.h" +#include "IRsend.h" +#include "IRsend_test.h" #include "gtest/gtest.h" // General housekeeping diff --git a/lib/IRremoteESP8266/test/ir_Lutron_test.cpp b/lib/IRremoteESP8266/test/ir_Lutron_test.cpp index b0a4e346e5..81cf0df9ce 100644 --- a/lib/IRremoteESP8266/test/ir_Lutron_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Lutron_test.cpp @@ -1,7 +1,7 @@ // Copyright 2018 David Conran -#include "../src/IRsend.h" -#include "../src/IRsend_test.h" +#include "IRsend.h" +#include "IRsend_test.h" #include "gtest/gtest.h" // Tests for sendLutron(). diff --git a/lib/IRremoteESP8266/test/ir_MWM_test.cpp b/lib/IRremoteESP8266/test/ir_MWM_test.cpp index 7b70a46b4c..2ca69ac833 100644 --- a/lib/IRremoteESP8266/test/ir_MWM_test.cpp +++ b/lib/IRremoteESP8266/test/ir_MWM_test.cpp @@ -1,10 +1,10 @@ // Copyright 2017 David Conran // Copyright 2018 Brett T. Warden -#include "../src/IRrecv.h" -#include "../src/IRrecv_test.h" -#include "../src/IRsend.h" -#include "../src/IRsend_test.h" +#include "IRrecv.h" +#include "IRrecv_test.h" +#include "IRsend.h" +#include "IRsend_test.h" #include "gtest/gtest.h" // MM MM WW WW MM MM diff --git a/lib/IRremoteESP8266/test/ir_Magiquest_test.cpp b/lib/IRremoteESP8266/test/ir_Magiquest_test.cpp index c60a893de0..1e5faf5c08 100644 --- a/lib/IRremoteESP8266/test/ir_Magiquest_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Magiquest_test.cpp @@ -1,9 +1,9 @@ // Copyright 2017 David Conran -#include "../src/ir_Magiquest.h" -#include "../src/IRrecv.h" -#include "../src/IRsend.h" -#include "../src/IRsend_test.h" +#include "ir_Magiquest.h" +#include "IRrecv.h" +#include "IRsend.h" +#include "IRsend_test.h" #include "gtest/gtest.h" // Tests for encodeMagiQuest() diff --git a/lib/IRremoteESP8266/test/ir_Metz_test.cpp b/lib/IRremoteESP8266/test/ir_Metz_test.cpp index 8644e048dd..67d2170231 100644 --- a/lib/IRremoteESP8266/test/ir_Metz_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Metz_test.cpp @@ -1,10 +1,10 @@ // Copyright 2020 crankyoldgit -#include "../src/IRac.h" -#include "../src/IRrecv.h" -#include "../src/IRrecv_test.h" -#include "../src/IRsend.h" -#include "../src/IRsend_test.h" +#include "IRac.h" +#include "IRrecv.h" +#include "IRrecv_test.h" +#include "IRsend.h" +#include "IRsend_test.h" #include "gtest/gtest.h" // Tests for decodeMetz(). diff --git a/lib/IRremoteESP8266/test/ir_Midea_test.cpp b/lib/IRremoteESP8266/test/ir_Midea_test.cpp index 2e147fd0df..713fcd0e41 100644 --- a/lib/IRremoteESP8266/test/ir_Midea_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Midea_test.cpp @@ -1,9 +1,9 @@ // Copyright 2017 David Conran -#include "../src/ir_Midea.h" -#include "../src/IRac.h" -#include "../src/IRsend.h" -#include "../src/IRsend_test.h" +#include "ir_Midea.h" +#include "IRac.h" +#include "IRsend.h" +#include "IRsend_test.h" #include "gtest/gtest.h" // Tests for sendMidea(). diff --git a/lib/IRremoteESP8266/test/ir_Milestag2_test.cpp b/lib/IRremoteESP8266/test/ir_Milestag2_test.cpp index 37b74ad896..542852da85 100644 --- a/lib/IRremoteESP8266/test/ir_Milestag2_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Milestag2_test.cpp @@ -1,11 +1,11 @@ // Copyright 2021 David Conran -#include "../src/IRac.h" -#include "../src/IRrecv.h" -#include "../src/IRrecv_test.h" -#include "../src/IRsend.h" -#include "../src/IRsend_test.h" -#include "../src/IRutils.h" +#include "IRac.h" +#include "IRrecv.h" +#include "IRrecv_test.h" +#include "IRsend.h" +#include "IRsend_test.h" +#include "IRutils.h" #include "gtest/gtest.h" TEST(TestUtils, Housekeeping) { diff --git a/lib/IRremoteESP8266/test/ir_MitsubishiHeavy_test.cpp b/lib/IRremoteESP8266/test/ir_MitsubishiHeavy_test.cpp index f5e4a29d3a..d182fd941b 100644 --- a/lib/IRremoteESP8266/test/ir_MitsubishiHeavy_test.cpp +++ b/lib/IRremoteESP8266/test/ir_MitsubishiHeavy_test.cpp @@ -1,12 +1,12 @@ // Copyright 2019 David Conran -#include "../src/ir_MitsubishiHeavy.h" -#include "../src/IRac.h" -#include "../src/IRrecv.h" -#include "../src/IRrecv_test.h" -#include "../src/IRremoteESP8266.h" -#include "../src/IRsend.h" -#include "../src/IRsend_test.h" +#include "ir_MitsubishiHeavy.h" +#include "IRac.h" +#include "IRrecv.h" +#include "IRrecv_test.h" +#include "IRremoteESP8266.h" +#include "IRsend.h" +#include "IRsend_test.h" #include "gtest/gtest.h" // General housekeeping diff --git a/lib/IRremoteESP8266/test/ir_Mitsubishi_test.cpp b/lib/IRremoteESP8266/test/ir_Mitsubishi_test.cpp index b3e59962f4..815d4f9961 100644 --- a/lib/IRremoteESP8266/test/ir_Mitsubishi_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Mitsubishi_test.cpp @@ -2,11 +2,11 @@ // Copyright 2019 kuchel77 // Copyright 2018 denxhun -#include "../src/ir_Mitsubishi.h" -#include "../src/IRac.h" -#include "../src/IRrecv_test.h" -#include "../src/IRsend.h" -#include "../src/IRsend_test.h" +#include "ir_Mitsubishi.h" +#include "IRac.h" +#include "IRrecv_test.h" +#include "IRsend.h" +#include "IRsend_test.h" #include "gtest/gtest.h" // Tests for sendMitsubishi(). diff --git a/lib/IRremoteESP8266/test/ir_Multibrackets_test.cpp b/lib/IRremoteESP8266/test/ir_Multibrackets_test.cpp index 683072685d..7b58b3333a 100644 --- a/lib/IRremoteESP8266/test/ir_Multibrackets_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Multibrackets_test.cpp @@ -1,10 +1,10 @@ // Copyright 2020 David Conran -#include "../src/IRac.h" -#include "../src/IRrecv.h" -#include "../src/IRrecv_test.h" -#include "../src/IRsend.h" -#include "../src/IRsend_test.h" +#include "IRac.h" +#include "IRrecv.h" +#include "IRrecv_test.h" +#include "IRsend.h" +#include "IRsend_test.h" #include "gtest/gtest.h" // Tests for decodeMultibrackets(). diff --git a/lib/IRremoteESP8266/test/ir_NEC_test.cpp b/lib/IRremoteESP8266/test/ir_NEC_test.cpp index b8c2be1b49..f7598c9811 100644 --- a/lib/IRremoteESP8266/test/ir_NEC_test.cpp +++ b/lib/IRremoteESP8266/test/ir_NEC_test.cpp @@ -1,7 +1,7 @@ // Copyright 2017 David Conran -#include "../src/IRsend.h" -#include "../src/IRsend_test.h" +#include "IRsend.h" +#include "IRsend_test.h" #include "gtest/gtest.h" // Tests for sendNEC(). diff --git a/lib/IRremoteESP8266/test/ir_Neoclima_test.cpp b/lib/IRremoteESP8266/test/ir_Neoclima_test.cpp index 4c2ed04652..68fc2ded53 100644 --- a/lib/IRremoteESP8266/test/ir_Neoclima_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Neoclima_test.cpp @@ -1,12 +1,12 @@ // Copyright 2019 David Conran (crankyoldgit) -#include "../src/ir_Neoclima.h" +#include "ir_Neoclima.h" #include -#include "../src/IRac.h" -#include "../src/IRsend.h" -#include "../src/IRsend_test.h" -#include "../src/IRrecv.h" -#include "../src/IRrecv_test.h" +#include "IRac.h" +#include "IRsend.h" +#include "IRsend_test.h" +#include "IRrecv.h" +#include "IRrecv_test.h" #include "gtest/gtest.h" TEST(TestUtils, Housekeeping) { diff --git a/lib/IRremoteESP8266/test/ir_Nikai_test.cpp b/lib/IRremoteESP8266/test/ir_Nikai_test.cpp index 7a89b601cd..98d6d806da 100644 --- a/lib/IRremoteESP8266/test/ir_Nikai_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Nikai_test.cpp @@ -1,7 +1,7 @@ // Copyright 2017 David Conran -#include "../src/IRsend.h" -#include "../src/IRsend_test.h" +#include "IRsend.h" +#include "IRsend_test.h" #include "gtest/gtest.h" // Tests for sendNikai(). diff --git a/lib/IRremoteESP8266/test/ir_Panasonic_test.cpp b/lib/IRremoteESP8266/test/ir_Panasonic_test.cpp index 5c2a09dd26..3e52cef8b2 100644 --- a/lib/IRremoteESP8266/test/ir_Panasonic_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Panasonic_test.cpp @@ -1,13 +1,13 @@ // Copyright 2017, 2018 David Conran -#include "../src/ir_Panasonic.h" -#include "../src/IRac.h" -#include "../src/IRrecv.h" -#include "../src/IRrecv_test.h" -#include "../src/IRsend.h" -#include "../src/IRsend_test.h" -#include "../src/IRtext.h" -#include "../src/IRutils.h" +#include "ir_Panasonic.h" +#include "IRac.h" +#include "IRrecv.h" +#include "IRrecv_test.h" +#include "IRsend.h" +#include "IRsend_test.h" +#include "IRtext.h" +#include "IRutils.h" #include "gtest/gtest.h" // Tests for encodePanasonic(). diff --git a/lib/IRremoteESP8266/test/ir_Pioneer_test.cpp b/lib/IRremoteESP8266/test/ir_Pioneer_test.cpp index 7ac7d15561..fce2503e4a 100644 --- a/lib/IRremoteESP8266/test/ir_Pioneer_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Pioneer_test.cpp @@ -1,8 +1,8 @@ // Copyright 2018 David Conran -#include "../src/IRsend.h" -#include "../src/IRsend_test.h" -#include "../src/IRutils.h" +#include "IRsend.h" +#include "IRsend_test.h" +#include "IRutils.h" #include "gtest/gtest.h" // Tests for sendPioneer(). diff --git a/lib/IRremoteESP8266/test/ir_Pronto_test.cpp b/lib/IRremoteESP8266/test/ir_Pronto_test.cpp index f441120f1b..8331192bca 100644 --- a/lib/IRremoteESP8266/test/ir_Pronto_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Pronto_test.cpp @@ -1,6 +1,6 @@ // Copyright 2017 David Conran -#include "../src/IRsend.h" -#include "../src/IRsend_test.h" +#include "IRsend.h" +#include "IRsend_test.h" #include "gtest/gtest.h" // Tests for sendPronto(). diff --git a/lib/IRremoteESP8266/test/ir_RC5_RC6_test.cpp b/lib/IRremoteESP8266/test/ir_RC5_RC6_test.cpp index 8073ea5ab1..0b46bad7f7 100644 --- a/lib/IRremoteESP8266/test/ir_RC5_RC6_test.cpp +++ b/lib/IRremoteESP8266/test/ir_RC5_RC6_test.cpp @@ -1,7 +1,7 @@ // Copyright 2017 David Conran -#include "../src/IRsend.h" -#include "../src/IRsend_test.h" +#include "IRsend.h" +#include "IRsend_test.h" #include "gtest/gtest.h" // RRRRRR CCCCC 555555 RRRRRR CCCCC 555555 XX XX diff --git a/lib/IRremoteESP8266/test/ir_RCMM_test.cpp b/lib/IRremoteESP8266/test/ir_RCMM_test.cpp index b635c6173f..ef37d87a6a 100644 --- a/lib/IRremoteESP8266/test/ir_RCMM_test.cpp +++ b/lib/IRremoteESP8266/test/ir_RCMM_test.cpp @@ -1,7 +1,7 @@ // Copyright 2017 David Conran -#include "../src/IRsend.h" -#include "../src/IRsend_test.h" +#include "IRsend.h" +#include "IRsend_test.h" #include "gtest/gtest.h" // Tests for sendRCMM(). diff --git a/lib/IRremoteESP8266/test/ir_Rhoss_test.cpp b/lib/IRremoteESP8266/test/ir_Rhoss_test.cpp index 18428bb40d..855b9a48c8 100644 --- a/lib/IRremoteESP8266/test/ir_Rhoss_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Rhoss_test.cpp @@ -1,12 +1,12 @@ // Copyright 2021 Tom Rosenback -#include "../src/IRac.h" -#include "../src/ir_Rhoss.h" -#include "../src/IRrecv.h" -#include "../src/IRrecv_test.h" -#include "../src/IRsend.h" -#include "../src/IRsend_test.h" -#include "../src/IRutils.h" +#include "IRac.h" +#include "ir_Rhoss.h" +#include "IRrecv.h" +#include "IRrecv_test.h" +#include "IRsend.h" +#include "IRsend_test.h" +#include "IRutils.h" #include "gtest/gtest.h" TEST(TestUtils, Housekeeping) { diff --git a/lib/IRremoteESP8266/test/ir_Samsung_test.cpp b/lib/IRremoteESP8266/test/ir_Samsung_test.cpp index a65c218d94..061067421f 100644 --- a/lib/IRremoteESP8266/test/ir_Samsung_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Samsung_test.cpp @@ -1,12 +1,12 @@ // Copyright 2017-2020 David Conran #include -#include "../src/ir_Samsung.h" -#include "../src/IRac.h" -#include "../src/IRrecv.h" -#include "../src/IRrecv_test.h" -#include "../src/IRsend.h" -#include "../src/IRsend_test.h" +#include "ir_Samsung.h" +#include "IRac.h" +#include "IRrecv.h" +#include "IRrecv_test.h" +#include "IRsend.h" +#include "IRsend_test.h" #include "gtest/gtest.h" diff --git a/lib/IRremoteESP8266/test/ir_Sanyo_test.cpp b/lib/IRremoteESP8266/test/ir_Sanyo_test.cpp index bb44b3a013..6378e7679d 100644 --- a/lib/IRremoteESP8266/test/ir_Sanyo_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Sanyo_test.cpp @@ -1,11 +1,11 @@ // Copyright 2017-2021 David Conran -#include "../src/ir_Sanyo.h" -#include "../src/IRac.h" -#include "../src/IRrecv.h" -#include "../src/IRrecv_test.h" -#include "../src/IRsend.h" -#include "../src/IRsend_test.h" +#include "ir_Sanyo.h" +#include "IRac.h" +#include "IRrecv.h" +#include "IRrecv_test.h" +#include "IRsend.h" +#include "IRsend_test.h" #include "gtest/gtest.h" // Tests for encodeSanyoLC7461(). diff --git a/lib/IRremoteESP8266/test/ir_Sharp_test.cpp b/lib/IRremoteESP8266/test/ir_Sharp_test.cpp index 6635e2dfd5..a2acafb9cc 100644 --- a/lib/IRremoteESP8266/test/ir_Sharp_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Sharp_test.cpp @@ -1,11 +1,11 @@ // Copyright 2017 David Conran -#include "../src/ir_Sharp.h" -#include "../src/IRac.h" -#include "../src/IRrecv.h" -#include "../src/IRrecv_test.h" -#include "../src/IRsend.h" -#include "../src/IRsend_test.h" +#include "ir_Sharp.h" +#include "IRac.h" +#include "IRrecv.h" +#include "IRrecv_test.h" +#include "IRsend.h" +#include "IRsend_test.h" #include "gtest/gtest.h" // Tests for encodeSharp(). diff --git a/lib/IRremoteESP8266/test/ir_Sherwood_test.cpp b/lib/IRremoteESP8266/test/ir_Sherwood_test.cpp index 2ac8b4af95..f1f41d9c8c 100644 --- a/lib/IRremoteESP8266/test/ir_Sherwood_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Sherwood_test.cpp @@ -1,7 +1,7 @@ // Copyright 2017 David Conran -#include "../src/IRsend.h" -#include "../src/IRsend_test.h" +#include "IRsend.h" +#include "IRsend_test.h" #include "gtest/gtest.h" // Tests for sendSherwood(). diff --git a/lib/IRremoteESP8266/test/ir_Sony_test.cpp b/lib/IRremoteESP8266/test/ir_Sony_test.cpp index dd0692f45c..c69d40e67a 100644 --- a/lib/IRremoteESP8266/test/ir_Sony_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Sony_test.cpp @@ -1,7 +1,7 @@ // Copyright 2017 David Conran -#include "../src/IRsend.h" -#include "../src/IRsend_test.h" +#include "IRsend.h" +#include "IRsend_test.h" #include "gtest/gtest.h" // Tests for sendSony(). diff --git a/lib/IRremoteESP8266/test/ir_Symphony_test.cpp b/lib/IRremoteESP8266/test/ir_Symphony_test.cpp index 7960db61eb..e3f818136e 100644 --- a/lib/IRremoteESP8266/test/ir_Symphony_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Symphony_test.cpp @@ -1,10 +1,10 @@ // Copyright 2020 David Conran -#include "../src/IRac.h" -#include "../src/IRrecv.h" -#include "../src/IRrecv_test.h" -#include "../src/IRsend.h" -#include "../src/IRsend_test.h" +#include "IRac.h" +#include "IRrecv.h" +#include "IRrecv_test.h" +#include "IRsend.h" +#include "IRsend_test.h" #include "gtest/gtest.h" // Tests for sendSymphony(). diff --git a/lib/IRremoteESP8266/test/ir_Tcl_test.cpp b/lib/IRremoteESP8266/test/ir_Tcl_test.cpp index 37e75f42f6..b7ab5301f9 100644 --- a/lib/IRremoteESP8266/test/ir_Tcl_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Tcl_test.cpp @@ -1,11 +1,11 @@ // Copyright 2019 David Conran -#include "../src/ir_Tcl.h" -#include "../src/IRac.h" -#include "../src/IRrecv.h" -#include "../src/IRrecv_test.h" -#include "../src/IRsend.h" -#include "../src/IRsend_test.h" +#include "ir_Tcl.h" +#include "IRac.h" +#include "IRrecv.h" +#include "IRrecv_test.h" +#include "IRsend.h" +#include "IRsend_test.h" #include "gtest/gtest.h" // General housekeeping diff --git a/lib/IRremoteESP8266/test/ir_Technibel_test.cpp b/lib/IRremoteESP8266/test/ir_Technibel_test.cpp index 420662e8f7..808da1e951 100644 --- a/lib/IRremoteESP8266/test/ir_Technibel_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Technibel_test.cpp @@ -1,12 +1,12 @@ // Copyright 2020 Quentin BRIOLLANT -#include "../src/IRac.h" -#include "../src/ir_Technibel.h" -#include "../src/IRrecv.h" -#include "../src/IRrecv_test.h" -#include "../src/IRsend.h" -#include "../src/IRsend_test.h" -#include "../src/IRutils.h" +#include "IRac.h" +#include "ir_Technibel.h" +#include "IRrecv.h" +#include "IRrecv_test.h" +#include "IRsend.h" +#include "IRsend_test.h" +#include "IRutils.h" #include "gtest/gtest.h" TEST(TestUtils, Housekeeping) { diff --git a/lib/IRremoteESP8266/test/ir_Teco_test.cpp b/lib/IRremoteESP8266/test/ir_Teco_test.cpp index 2913c3dc6a..f4196c3aef 100644 --- a/lib/IRremoteESP8266/test/ir_Teco_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Teco_test.cpp @@ -1,11 +1,11 @@ // Copyright 2019 David Conran -#include "../src/ir_Teco.h" -#include "../src/IRac.h" -#include "../src/IRrecv.h" -#include "../src/IRrecv_test.h" -#include "../src/IRsend.h" -#include "../src/IRsend_test.h" +#include "ir_Teco.h" +#include "IRac.h" +#include "IRrecv.h" +#include "IRrecv_test.h" +#include "IRsend.h" +#include "IRsend_test.h" #include "gtest/gtest.h" // General housekeeping diff --git a/lib/IRremoteESP8266/test/ir_Teknopoint_test.cpp b/lib/IRremoteESP8266/test/ir_Teknopoint_test.cpp index 59a7ee23d6..74f29d1468 100644 --- a/lib/IRremoteESP8266/test/ir_Teknopoint_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Teknopoint_test.cpp @@ -1,10 +1,10 @@ // Copyright 2021 David Conran (crankyoldgit) -#include "../src/IRac.h" -#include "../src/IRrecv.h" -#include "../src/IRrecv_test.h" -#include "../src/IRsend.h" -#include "../src/IRsend_test.h" +#include "IRac.h" +#include "IRrecv.h" +#include "IRrecv_test.h" +#include "IRsend.h" +#include "IRsend_test.h" #include "gtest/gtest.h" // Tests for decodeTeknopoint(). diff --git a/lib/IRremoteESP8266/test/ir_Toshiba_test.cpp b/lib/IRremoteESP8266/test/ir_Toshiba_test.cpp index a12a502949..4a63780d50 100644 --- a/lib/IRremoteESP8266/test/ir_Toshiba_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Toshiba_test.cpp @@ -1,10 +1,10 @@ // Copyright 2017 David Conran -#include "../src/ir_Toshiba.h" -#include "../src/IRac.h" -#include "../src/IRrecv.h" -#include "../src/IRrecv_test.h" -#include "../src/IRsend.h" -#include "../src/IRsend_test.h" +#include "ir_Toshiba.h" +#include "IRac.h" +#include "IRrecv.h" +#include "IRrecv_test.h" +#include "IRsend.h" +#include "IRsend_test.h" #include "gtest/gtest.h" // Tests for Toshiba A/C methods. diff --git a/lib/IRremoteESP8266/test/ir_Transcold_test.cpp b/lib/IRremoteESP8266/test/ir_Transcold_test.cpp index 93ac4ad03e..8a98a28969 100644 --- a/lib/IRremoteESP8266/test/ir_Transcold_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Transcold_test.cpp @@ -1,10 +1,10 @@ // Copyright 2020 crankyoldgit -#include "../src/IRac.h" -#include "../src/IRrecv.h" -#include "../src/IRrecv_test.h" -#include "../src/IRsend.h" -#include "../src/IRsend_test.h" +#include "IRac.h" +#include "IRrecv.h" +#include "IRrecv_test.h" +#include "IRsend.h" +#include "IRsend_test.h" #include "gtest/gtest.h" // Tests for decodeTranscold(). diff --git a/lib/IRremoteESP8266/test/ir_Trotec_test.cpp b/lib/IRremoteESP8266/test/ir_Trotec_test.cpp index 8e4c069db4..b6658f3f52 100644 --- a/lib/IRremoteESP8266/test/ir_Trotec_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Trotec_test.cpp @@ -1,10 +1,10 @@ // Copyright 2019 David Conran -#include "../src/ir_Trotec.h" -#include "../src/IRac.h" -#include "../src/IRrecv.h" -#include "../src/IRrecv_test.h" -#include "../src/IRsend.h" -#include "../src/IRsend_test.h" +#include "ir_Trotec.h" +#include "IRac.h" +#include "IRrecv.h" +#include "IRrecv_test.h" +#include "IRsend.h" +#include "IRsend_test.h" #include "gtest/gtest.h" diff --git a/lib/IRremoteESP8266/test/ir_Truma_test.cpp b/lib/IRremoteESP8266/test/ir_Truma_test.cpp index aab6fbf8b4..0d26fb6dbe 100644 --- a/lib/IRremoteESP8266/test/ir_Truma_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Truma_test.cpp @@ -1,10 +1,10 @@ // Copyright 2021 David Conran (crankyoldgit) -#include "../src/IRac.h" -#include "../src/IRrecv.h" -#include "../src/IRrecv_test.h" -#include "../src/IRsend.h" -#include "../src/IRsend_test.h" +#include "IRac.h" +#include "IRrecv.h" +#include "IRrecv_test.h" +#include "IRsend.h" +#include "IRsend_test.h" #include "gtest/gtest.h" // Tests for decodeTruma(). diff --git a/lib/IRremoteESP8266/test/ir_Vestel_test.cpp b/lib/IRremoteESP8266/test/ir_Vestel_test.cpp index cb8d97554c..d3f1febf8a 100644 --- a/lib/IRremoteESP8266/test/ir_Vestel_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Vestel_test.cpp @@ -1,11 +1,11 @@ // Copyright 2019 David Conran -#include "../src/ir_Vestel.h" -#include "../src/IRac.h" -#include "../src/IRrecv.h" -#include "../src/IRrecv_test.h" -#include "../src/IRsend.h" -#include "../src/IRsend_test.h" +#include "ir_Vestel.h" +#include "IRac.h" +#include "IRrecv.h" +#include "IRrecv_test.h" +#include "IRsend.h" +#include "IRsend_test.h" #include "gtest/gtest.h" // Tests for sendVestelAc() diff --git a/lib/IRremoteESP8266/test/ir_Voltas_test.cpp b/lib/IRremoteESP8266/test/ir_Voltas_test.cpp index 2a8de3eb1f..38fa9223b2 100644 --- a/lib/IRremoteESP8266/test/ir_Voltas_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Voltas_test.cpp @@ -1,11 +1,11 @@ // Copyright 2020 crankyoldgit -#include "../src/ir_Voltas.h" -#include "../src/IRac.h" -#include "../src/IRrecv.h" -#include "../src/IRrecv_test.h" -#include "../src/IRsend.h" -#include "../src/IRsend_test.h" +#include "ir_Voltas.h" +#include "IRac.h" +#include "IRrecv.h" +#include "IRrecv_test.h" +#include "IRsend.h" +#include "IRsend_test.h" #include "gtest/gtest.h" // Tests for decodeVoltas(). diff --git a/lib/IRremoteESP8266/test/ir_Whirlpool_test.cpp b/lib/IRremoteESP8266/test/ir_Whirlpool_test.cpp index c156e81e7f..4bc295d4f3 100644 --- a/lib/IRremoteESP8266/test/ir_Whirlpool_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Whirlpool_test.cpp @@ -1,11 +1,11 @@ // Copyright 2018 David Conran -#include "../src/ir_Whirlpool.h" -#include "../src/IRac.h" -#include "../src/IRrecv.h" -#include "../src/IRrecv_test.h" -#include "../src/IRsend.h" -#include "../src/IRsend_test.h" +#include "ir_Whirlpool.h" +#include "IRac.h" +#include "IRrecv.h" +#include "IRrecv_test.h" +#include "IRsend.h" +#include "IRsend_test.h" #include "gtest/gtest.h" diff --git a/lib/IRremoteESP8266/test/ir_Whynter_test.cpp b/lib/IRremoteESP8266/test/ir_Whynter_test.cpp index e9f532d4b2..eaccf0ea10 100644 --- a/lib/IRremoteESP8266/test/ir_Whynter_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Whynter_test.cpp @@ -1,7 +1,7 @@ // Copyright 2017 David Conran -#include "../src/IRsend.h" -#include "../src/IRsend_test.h" +#include "IRsend.h" +#include "IRsend_test.h" #include "gtest/gtest.h" // Tests for sendWhynter(). diff --git a/lib/IRremoteESP8266/test/ir_Xmp_test.cpp b/lib/IRremoteESP8266/test/ir_Xmp_test.cpp index 4c998139b8..796f2ef903 100644 --- a/lib/IRremoteESP8266/test/ir_Xmp_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Xmp_test.cpp @@ -1,10 +1,10 @@ // Copyright 2021 David Conran -#include "../src/IRac.h" -#include "../src/IRrecv.h" -#include "../src/IRrecv_test.h" -#include "../src/IRsend.h" -#include "../src/IRsend_test.h" +#include "IRac.h" +#include "IRrecv.h" +#include "IRrecv_test.h" +#include "IRsend.h" +#include "IRsend_test.h" #include "gtest/gtest.h" // Tests for sendXmp(). diff --git a/lib/IRremoteESP8266/test/ir_Zepeal_test.cpp b/lib/IRremoteESP8266/test/ir_Zepeal_test.cpp index 986a6986c4..12240d2f7c 100644 --- a/lib/IRremoteESP8266/test/ir_Zepeal_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Zepeal_test.cpp @@ -1,10 +1,10 @@ // Copyright 2020 Christian Nilsson -#include "../src/IRac.h" -#include "../src/IRrecv.h" -#include "../src/IRrecv_test.h" -#include "../src/IRsend.h" -#include "../src/IRsend_test.h" +#include "IRac.h" +#include "IRrecv.h" +#include "IRrecv_test.h" +#include "IRsend.h" +#include "IRsend_test.h" #include "gtest/gtest.h" // Tests for decodeZepeal(). diff --git a/lib/IRremoteESP8266/tools/auto_analyse_raw_data.py b/lib/IRremoteESP8266/tools/auto_analyse_raw_data.py index 5c797aff4d..e061d3c424 100644 --- a/lib/IRremoteESP8266/tools/auto_analyse_raw_data.py +++ b/lib/IRremoteESP8266/tools/auto_analyse_raw_data.py @@ -647,9 +647,9 @@ def generate_code(defines, code, bits_str, name="", output=sys.stdout): f"/// @brief Support for {def_name} protocol\n\n" "// Supports:\n" f"// Brand: {def_name}, Model: TODO add device and remote\n\n" - '#include "../src/IRrecv.h"\n' - '#include "../src/IRsend.h"\n' - '#include "../src/IRutils.h"\n\n' + '#include "IRrecv.h"\n' + '#include "IRsend.h"\n' + '#include "IRutils.h"\n\n' "// WARNING: This probably isn't directly usable." " It's a guide only.\n\n" "// See https://github.com/crankyoldgit/IRremoteESP8266/wiki/" diff --git a/lib/IRremoteESP8266/tools/auto_analyse_raw_data_test.py b/lib/IRremoteESP8266/tools/auto_analyse_raw_data_test.py index 6682dcf766..1b080c5a8b 100644 --- a/lib/IRremoteESP8266/tools/auto_analyse_raw_data_test.py +++ b/lib/IRremoteESP8266/tools/auto_analyse_raw_data_test.py @@ -286,9 +286,9 @@ def test_parse_and_report(self): '// Supports:\n' '// Brand: FOO, Model: TODO add device and remote\n' '\n' - '#include "../src/IRrecv.h"\n' - '#include "../src/IRsend.h"\n' - '#include "../src/IRutils.h"\n' + '#include "IRrecv.h"\n' + '#include "IRsend.h"\n' + '#include "IRutils.h"\n' '\n' "// WARNING: This probably isn't directly usable. It's a guide only.\n" '\n' @@ -567,9 +567,9 @@ def test_leader_marks(self): '// Supports:\n' '// Brand: Hitachi, Model: TODO add device and remote\n' '\n' - '#include "../src/IRrecv.h"\n' - '#include "../src/IRsend.h"\n' - '#include "../src/IRutils.h"\n' + '#include "IRrecv.h"\n' + '#include "IRsend.h"\n' + '#include "IRutils.h"\n' '\n' "// WARNING: This probably isn't directly usable. It's a guide only.\n" '\n' @@ -881,9 +881,9 @@ def test_unusual_gaps(self): '// Supports:\n' '// Brand: FOO, Model: TODO add device and remote\n' '\n' - '#include "../src/IRrecv.h"\n' - '#include "../src/IRsend.h"\n' - '#include "../src/IRutils.h"\n' + '#include "IRrecv.h"\n' + '#include "IRsend.h"\n' + '#include "IRutils.h"\n' '\n' "// WARNING: This probably isn't directly usable. It's a guide only.\n" '\n' @@ -1317,9 +1317,9 @@ def test_no_headers(self): '// Supports:\n' '// Brand: TBD, Model: TODO add device and remote\n' '\n' - '#include "../src/IRrecv.h"\n' - '#include "../src/IRsend.h"\n' - '#include "../src/IRutils.h"\n' + '#include "IRrecv.h"\n' + '#include "IRsend.h"\n' + '#include "IRutils.h"\n' '\n' "// WARNING: This probably isn't directly usable. It's a guide only.\n" '\n' diff --git a/lib/IRremoteESP8266/tools/gc_decode.cpp b/lib/IRremoteESP8266/tools/gc_decode.cpp index 38ab03ba45..f1c374bbef 100644 --- a/lib/IRremoteESP8266/tools/gc_decode.cpp +++ b/lib/IRremoteESP8266/tools/gc_decode.cpp @@ -7,10 +7,10 @@ #include #include #include -#include "../src/IRac.h" -#include "../src/IRsend.h" -#include "../src/IRsend_test.h" -#include "../src/IRutils.h" +#include "IRac.h" +#include "IRsend.h" +#include "IRsend_test.h" +#include "IRutils.h" const uint16_t kMaxGcCodeLength = 10000; diff --git a/lib/IRremoteESP8266/tools/mode2_decode.cpp b/lib/IRremoteESP8266/tools/mode2_decode.cpp index 8ba52aa000..63dfa62210 100644 --- a/lib/IRremoteESP8266/tools/mode2_decode.cpp +++ b/lib/IRremoteESP8266/tools/mode2_decode.cpp @@ -24,9 +24,9 @@ space 500000 #include #include #include -#include "../src/IRsend.h" -#include "../src/IRsend_test.h" -#include "../src/IRutils.h" +#include "IRsend.h" +#include "IRsend_test.h" +#include "IRutils.h" const uint16_t kMaxGcCodeLength = 10000; diff --git a/platformio.ini b/platformio.ini index 31baf3cea1..8090915ae0 100644 --- a/platformio.ini +++ b/platformio.ini @@ -89,7 +89,7 @@ monitor_speed = 115200 targets = extra_scripts = pre:tools/pio/pre_default_check.py ${extra_scripts_esp8266.extra_scripts} -src_filter = +<*> -<.git/> -<.svn/> - - - - -<*/Commands/> -<*/ControllerQueue/> -<*/DataTypes/> -<*/Globals/> -<*/Helpers/> -<*/PluginStructs/> -<*/WebServer/> -<*/IRremoteESP8266/src/> +src_filter = +<*> -<.git/> -<.svn/> - - - - -<*/Commands/> -<*/ControllerQueue/> -<*/DataTypes/> -<*/Globals/> -<*/Helpers/> -<*/PluginStructs/> -<*/WebServer/> From 651e0b79544f285172351961bae4a47c3f57d647 Mon Sep 17 00:00:00 2001 From: TD-er Date: Sun, 5 Dec 2021 22:22:21 +0100 Subject: [PATCH 081/147] [Build] Fix build of ESP32 max LittleFS build on Windows In order to be able to build in Windows, we need to concatenate all .cpp files in some folders to reduce the number of compiled object files. This is needed due to the limitation of Windows to have a command line longer than 32767 characters. The linker command was becoming longer than this maximum. However the DataStructs folder does have at least one templated .cpp file. Since you may need to include a .cpp file if it contains a templated class, the DataStruct folder could not be concatenated into a single .cpp file. Therefore the templated class(es) will be stored in a folder postfixed with _templ --- platformio.ini | 2 +- src/src/DataStructs_templ/README.txt | 13 +++++++++++++ .../SettingsStruct.cpp | 0 src/src/Globals/Settings.h | 4 +++- tools/pio/concat_cpp_files.py | 1 + tools/pio/remove_concat_cpp_files.py | 1 + 6 files changed, 19 insertions(+), 2 deletions(-) create mode 100644 src/src/DataStructs_templ/README.txt rename src/src/{DataStructs => DataStructs_templ}/SettingsStruct.cpp (100%) diff --git a/platformio.ini b/platformio.ini index 8090915ae0..03dc492f77 100644 --- a/platformio.ini +++ b/platformio.ini @@ -89,7 +89,7 @@ monitor_speed = 115200 targets = extra_scripts = pre:tools/pio/pre_default_check.py ${extra_scripts_esp8266.extra_scripts} -src_filter = +<*> -<.git/> -<.svn/> - - - - -<*/Commands/> -<*/ControllerQueue/> -<*/DataTypes/> -<*/Globals/> -<*/Helpers/> -<*/PluginStructs/> -<*/WebServer/> +src_filter = +<*> -<.git/> -<.svn/> - - - - -<*/Commands/> -<*/ControllerQueue/> -<*/DataStructs/> -<*/DataTypes/> -<*/Globals/> -<*/Helpers/> -<*/PluginStructs/> -<*/WebServer/> diff --git a/src/src/DataStructs_templ/README.txt b/src/src/DataStructs_templ/README.txt new file mode 100644 index 0000000000..2c8611fd45 --- /dev/null +++ b/src/src/DataStructs_templ/README.txt @@ -0,0 +1,13 @@ +In order to be able to build in Windows, we need to concatenate all .cpp files +in some folders to reduce the number of compiled object files. +This is needed due to the limitation of Windows to have a command line longer than 32767 characters. +The linker command was becoming longer than this maximum. + +However the DataStructs folder does have at least one templated .cpp file. +Since you may need to include a .cpp file if it contains a templated class, +the DataStruct folder could not be concatenated into a single .cpp file. + +Therefore the templated class(es) will be stored in a folder postfixed with +_templ + + diff --git a/src/src/DataStructs/SettingsStruct.cpp b/src/src/DataStructs_templ/SettingsStruct.cpp similarity index 100% rename from src/src/DataStructs/SettingsStruct.cpp rename to src/src/DataStructs_templ/SettingsStruct.cpp diff --git a/src/src/Globals/Settings.h b/src/src/Globals/Settings.h index 736141b8d4..9930b528e9 100644 --- a/src/src/Globals/Settings.h +++ b/src/src/Globals/Settings.h @@ -4,7 +4,9 @@ #include "../DataStructs/SettingsStruct.h" // include the source file since it is a template class. -#include "../DataStructs/SettingsStruct.cpp" +// Moved to a separate folder to allow concatenating all *.cpp files +// in the DataStructs folder to make Windows builds work again. +#include "../DataStructs_templ/SettingsStruct.cpp" extern SettingsStruct Settings; diff --git a/tools/pio/concat_cpp_files.py b/tools/pio/concat_cpp_files.py index ad4be8eb8b..2f2a32e3e1 100644 --- a/tools/pio/concat_cpp_files.py +++ b/tools/pio/concat_cpp_files.py @@ -52,6 +52,7 @@ def concat_cpp_files(path_to_concat): concat_cpp_files('./src/src/Commands') concat_cpp_files('./src/src/ControllerQueue') +concat_cpp_files('./src/src/DataStructs') concat_cpp_files('./src/src/DataTypes') concat_cpp_files('./src/src/Globals') concat_cpp_files('./src/src/Helpers') diff --git a/tools/pio/remove_concat_cpp_files.py b/tools/pio/remove_concat_cpp_files.py index 3dfbd6700e..3f2f293eb0 100644 --- a/tools/pio/remove_concat_cpp_files.py +++ b/tools/pio/remove_concat_cpp_files.py @@ -19,6 +19,7 @@ def clear_all_concat_cpp_files(source, target, env): print("\u001b[32m Remove temp concatenated files \u001b[0m") clear_concat_cpp_files('./src/src/Commands') clear_concat_cpp_files('./src/src/ControllerQueue') + clear_concat_cpp_files('./src/src/DataStructs') clear_concat_cpp_files('./src/src/DataTypes') clear_concat_cpp_files('./src/src/Globals') clear_concat_cpp_files('./src/src/Helpers') From bad626219971525254ca63943de7c5d00df1df6e Mon Sep 17 00:00:00 2001 From: TD-er Date: Mon, 6 Dec 2021 00:26:48 +0100 Subject: [PATCH 082/147] [PIO] Simplify ESP32 env definitions. --- platformio_esp32_envs.ini | 49 +++++++------------------------------ platformio_esp82xx_base.ini | 5 ---- 2 files changed, 9 insertions(+), 45 deletions(-) diff --git a/platformio_esp32_envs.ini b/platformio_esp32_envs.ini index c0b0c816e6..63e3f256d8 100644 --- a/platformio_esp32_envs.ini +++ b/platformio_esp32_envs.ini @@ -25,6 +25,7 @@ build_flags = ${core_esp32_3_4_0.build_flags} -DCONFIG_LWIP_ESP_GRATUITOUS_ARP -fno-strict-aliasing -DCORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_NONE +board = esp32dev monitor_filters = esp32_exception_decoder @@ -52,9 +53,7 @@ board_build.filesystem = littlefs ; Custom: 4096k version -------------------------- [env:custom_ESP32_4M316k] extends = esp32_common -platform = ${esp32_common.platform} build_flags = ${esp32_common.build_flags} -DPLUGIN_BUILD_CUSTOM -board = esp32dev extra_scripts = ${esp32_common.extra_scripts} pre:tools/pio/pre_custom_esp32.py @@ -70,21 +69,16 @@ extra_scripts = ${esp32s2_common.extra_scripts} [env:custom_IR_ESP32_4M316k] extends = esp32_common -platform = ${esp32_common.platform} build_flags = ${esp32_common.build_flags} -DPLUGIN_BUILD_CUSTOM -DPLUGIN_BUILD_IR -board = esp32dev lib_ignore = ${esp32_always.lib_ignore}, ESP32_ping, HeatpumpIR extra_scripts = ${esp32_common.extra_scripts} pre:tools/pio/pre_custom_esp32.py [env:normal_ESP32_4M316k] extends = esp32_common -platform = ${esp32_common.platform} lib_deps = ${esp32_common.lib_deps}, ServoESP32 build_flags = ${esp32_common.build_flags} -DFEATURE_ARDUINO_OTA -board = esp32dev -extra_scripts = ${esp32_common.extra_scripts} [env:test_A_ESP32_4M316k] extends = esp32_common @@ -93,38 +87,27 @@ build_flags = ${esp32_common.build_flags} -DFEATURE_ARDUINO_OTA -DPLUGIN_SET_TEST_ESP32 -DTESTING_USE_RTTTL -board = esp32dev -extra_scripts = ${esp32_common.extra_scripts} [env:test_B_ESP32_4M316k] extends = esp32_common -platform = ${esp32_common.platform} build_flags = ${esp32_common.build_flags} -DFEATURE_ARDUINO_OTA -DPLUGIN_SET_TEST_B_ESP32 -DTESTING_USE_RTTTL -board = esp32dev -extra_scripts = ${esp32_common.extra_scripts} [env:test_C_ESP32_4M316k] extends = esp32_common -platform = ${esp32_common.platform} build_flags = ${esp32_common.build_flags} -DFEATURE_ARDUINO_OTA -DPLUGIN_SET_TEST_C_ESP32 -DTESTING_USE_RTTTL -board = esp32dev -extra_scripts = ${esp32_common.extra_scripts} [env:test_D_ESP32_4M316k] extends = esp32_common -platform = ${esp32_common.platform} build_flags = ${esp32_common.build_flags} -DFEATURE_ARDUINO_OTA -DPLUGIN_SET_TEST_D_ESP32 -DTESTING_USE_RTTTL -board = esp32dev -extra_scripts = ${esp32_common.extra_scripts} [env:test_E_ESP32_4M316k] extends = esp32_common @@ -139,7 +122,6 @@ extra_scripts = ${esp32_common.extra_scripts} [env:test_A_ESP32-wrover-kit_4M316k] extends = esp32_common -platform = ${esp32_common.platform} build_flags = ${esp32_common.build_flags} -DFEATURE_ARDUINO_OTA -DPLUGIN_SET_TEST_ESP32 @@ -148,11 +130,10 @@ board = esp-wrover-kit upload_protocol = ftdi debug_tool = ftdi debug_extra_cmds = break Misc.ino:3011 -extra_scripts = ${esp32_common.extra_scripts} + [env:test_B_ESP32-wrover-kit_4M316k] extends = esp32_common -platform = ${esp32_common.platform} build_flags = ${esp32_common.build_flags} -DFEATURE_ARDUINO_OTA -DPLUGIN_SET_TEST_B_ESP32 @@ -161,11 +142,10 @@ board = esp-wrover-kit upload_protocol = ftdi debug_tool = ftdi debug_extra_cmds = break Misc.ino:3011 -extra_scripts = ${esp32_common.extra_scripts} + [env:test_C_ESP32-wrover-kit_4M316k] extends = esp32_common -platform = ${esp32_common.platform} build_flags = ${esp32_common.build_flags} -DFEATURE_ARDUINO_OTA -DPLUGIN_SET_TEST_C_ESP32 @@ -174,11 +154,10 @@ board = esp-wrover-kit upload_protocol = ftdi debug_tool = ftdi debug_extra_cmds = break Misc.ino:3011 -extra_scripts = ${esp32_common.extra_scripts} + [env:test_D_ESP32-wrover-kit_4M316k] extends = esp32_common -platform = ${esp32_common.platform} build_flags = ${esp32_common.build_flags} -DFEATURE_ARDUINO_OTA -DPLUGIN_SET_TEST_D_ESP32 @@ -187,7 +166,7 @@ board = esp-wrover-kit upload_protocol = ftdi debug_tool = ftdi debug_extra_cmds = break Misc.ino:3011 -extra_scripts = ${esp32_common.extra_scripts} + [env:test_E_ESP32-wrover-kit_4M316k] extends = esp32_common @@ -204,47 +183,43 @@ extra_scripts = ${esp32_common.extra_scripts} [env:test_A_ESP32_4M316k_lolin_d32_pro] extends = esp32_common -platform = ${esp32_common.platform} build_flags = ${esp32_common.build_flags} ; -DFEATURE_ARDUINO_OTA -DPLUGIN_SET_TEST_ESP32 ; -DUSES_P096 -DTESTING_USE_RTTTL board = lolin_d32_pro -extra_scripts = ${esp32_common.extra_scripts} + [env:test_B_ESP32_4M316k_lolin_d32_pro] extends = esp32_common -platform = ${esp32_common.platform} build_flags = ${esp32_common.build_flags} ; -DFEATURE_ARDUINO_OTA -DPLUGIN_SET_TEST_B_ESP32 -DUSES_P096 -DTESTING_USE_RTTTL board = lolin_d32_pro -extra_scripts = ${esp32_common.extra_scripts} + [env:test_C_ESP32_4M316k_lolin_d32_pro] extends = esp32_common -platform = ${esp32_common.platform} build_flags = ${esp32_common.build_flags} ; -DFEATURE_ARDUINO_OTA -DPLUGIN_SET_TEST_C_ESP32 ; -DUSES_P096 -DTESTING_USE_RTTTL board = lolin_d32_pro -extra_scripts = ${esp32_common.extra_scripts} + [env:test_D_ESP32_4M316k_lolin_d32_pro] extends = esp32_common -platform = ${esp32_common.platform} build_flags = ${esp32_common.build_flags} ; -DFEATURE_ARDUINO_OTA -DPLUGIN_SET_TEST_D_ESP32 -DUSES_P096 -DTESTING_USE_RTTTL board = lolin_d32_pro -extra_scripts = ${esp32_common.extra_scripts} + [env:test_E_ESP32_4M316k_lolin_d32_pro] extends = esp32_common @@ -294,23 +269,17 @@ build_flags = ${esp32_common.build_flags} [env:energy_ESP32_4M316k] extends = esp32_common -platform = ${esp32_common.platform} lib_deps = ${esp32_common.lib_deps}, ServoESP32 build_flags = ${esp32_common.build_flags} -DFEATURE_ARDUINO_OTA -D PLUGIN_ENERGY_COLLECTION -board = esp32dev -extra_scripts = ${esp32_common.extra_scripts} [env:display_ESP32_4M316k] extends = esp32_common -platform = ${esp32_common.platform} lib_deps = ${esp32_common.lib_deps}, ServoESP32 build_flags = ${esp32_common.build_flags} -DFEATURE_ARDUINO_OTA -D PLUGIN_DISPLAY_COLLECTION -board = esp32dev -extra_scripts = ${esp32_common.extra_scripts} ; Custom: 4096k version -------------------------- diff --git a/platformio_esp82xx_base.ini b/platformio_esp82xx_base.ini index bf25bb2b4a..a2c2899abe 100644 --- a/platformio_esp82xx_base.ini +++ b/platformio_esp82xx_base.ini @@ -198,7 +198,6 @@ board = esp12e board_build.flash_mode = dout board_upload.maximum_size = 1044464 board_build.ldscript = eagle.flash.2m256.ld -build_flags = ${esp82xx_common.build_flags} [espWroom2M] extends = esp82xx_common @@ -206,7 +205,6 @@ board = esp12e board_build.flash_mode = dout board_upload.maximum_size = 1044464 board_build.ldscript = eagle.flash.2m.ld -build_flags = ${esp82xx_common.build_flags} [espWroom2M256] extends = esp82xx_common @@ -214,7 +212,6 @@ board_build.flash_mode = dout board_upload.maximum_size = 1044464 board = esp_wroom_02 board_build.ldscript = eagle.flash.2m256.ld -build_flags = ${esp82xx_common.build_flags} ;;; 4MB flash nodes ************************************************** @@ -227,7 +224,6 @@ board = esp12e board_build.flash_mode = dout board_upload.maximum_size = 1044464 board_build.ldscript = eagle.flash.4m1m.ld -build_flags = ${esp82xx_common.build_flags} [esp8266_4M2M] extends = esp82xx_common @@ -235,7 +231,6 @@ board = esp12e board_build.flash_mode = dout board_upload.maximum_size = 1044464 board_build.ldscript = eagle.flash.4m2m.ld -build_flags = ${esp82xx_common.build_flags} From 8824ca095c4fcb474dceb7ef54d0face749dfd67 Mon Sep 17 00:00:00 2001 From: TD-er Date: Mon, 6 Dec 2021 00:28:12 +0100 Subject: [PATCH 083/147] [PIO] Simplify WROVER kit envs and remove WROVER kit ETH builds The WROVER kit doesn't have ethernet support. --- platformio_esp32_envs.ini | 63 ++++++++++----------------------------- 1 file changed, 15 insertions(+), 48 deletions(-) diff --git a/platformio_esp32_envs.ini b/platformio_esp32_envs.ini index 63e3f256d8..871ca35021 100644 --- a/platformio_esp32_envs.ini +++ b/platformio_esp32_envs.ini @@ -40,6 +40,13 @@ platform = ${core_esp32_3_4_0_esp32s2.platform} platform_packages = ${core_esp32_3_4_0_esp32s2.platform_packages} +[ESP32-wrover-kit] +extends = esp32_common +board = esp-wrover-kit +upload_protocol = ftdi +debug_tool = ftdi +;debug_extra_cmds = break Misc.ino:3011 + [esp32_LittleFS] extends = esp32_common platform_packages = ${esp32_common.platform_packages} @@ -121,51 +128,35 @@ extra_scripts = ${esp32_common.extra_scripts} [env:test_A_ESP32-wrover-kit_4M316k] -extends = esp32_common -build_flags = ${esp32_common.build_flags} +extends = ESP32-wrover-kit +build_flags = ${ESP32-wrover-kit.build_flags} -DFEATURE_ARDUINO_OTA -DPLUGIN_SET_TEST_ESP32 -DTESTING_USE_RTTTL -board = esp-wrover-kit -upload_protocol = ftdi -debug_tool = ftdi -debug_extra_cmds = break Misc.ino:3011 [env:test_B_ESP32-wrover-kit_4M316k] -extends = esp32_common -build_flags = ${esp32_common.build_flags} +extends = ESP32-wrover-kit +build_flags = ${ESP32-wrover-kit.build_flags} -DFEATURE_ARDUINO_OTA -DPLUGIN_SET_TEST_B_ESP32 -DTESTING_USE_RTTTL -board = esp-wrover-kit -upload_protocol = ftdi -debug_tool = ftdi -debug_extra_cmds = break Misc.ino:3011 [env:test_C_ESP32-wrover-kit_4M316k] -extends = esp32_common -build_flags = ${esp32_common.build_flags} +extends = ESP32-wrover-kit +build_flags = ${ESP32-wrover-kit.build_flags} -DFEATURE_ARDUINO_OTA -DPLUGIN_SET_TEST_C_ESP32 -DTESTING_USE_RTTTL -board = esp-wrover-kit -upload_protocol = ftdi -debug_tool = ftdi -debug_extra_cmds = break Misc.ino:3011 [env:test_D_ESP32-wrover-kit_4M316k] -extends = esp32_common -build_flags = ${esp32_common.build_flags} +extends = ESP32-wrover-kit +build_flags = ${ESP32-wrover-kit.build_flags} -DFEATURE_ARDUINO_OTA -DPLUGIN_SET_TEST_D_ESP32 -DTESTING_USE_RTTTL -board = esp-wrover-kit -upload_protocol = ftdi -debug_tool = ftdi -debug_extra_cmds = break Misc.ino:3011 [env:test_E_ESP32-wrover-kit_4M316k] @@ -324,30 +315,6 @@ build_flags = ${env:test_E_ESP32_4M316k.build_flags} -DHAS_ETHERNE -DTESTING_USE_RTTTL -[env:test_A_ESP32-wrover-kit_4M316k_ETH] -extends = env:test_A_ESP32-wrover-kit_4M316k -platform = ${env:test_A_ESP32-wrover-kit_4M316k.platform} -build_flags = ${env:test_A_ESP32-wrover-kit_4M316k.build_flags} -DHAS_ETHERNET - -DTESTING_USE_RTTTL - -[env:test_B_ESP32-wrover-kit_4M316k_ETH] -extends = env:test_B_ESP32-wrover-kit_4M316k -platform = ${env:test_B_ESP32-wrover-kit_4M316k.platform} -build_flags = ${env:test_B_ESP32-wrover-kit_4M316k.build_flags} -DHAS_ETHERNET - -DTESTING_USE_RTTTL - -[env:test_C_ESP32-wrover-kit_4M316k_ETH] -extends = env:test_C_ESP32-wrover-kit_4M316k -platform = ${env:test_C_ESP32-wrover-kit_4M316k.platform} -build_flags = ${env:test_C_ESP32-wrover-kit_4M316k.build_flags} -DHAS_ETHERNET - -DTESTING_USE_RTTTL - -[env:test_D_ESP32-wrover-kit_4M316k_ETH] -extends = env:test_D_ESP32-wrover-kit_4M316k -platform = ${env:test_D_ESP32-wrover-kit_4M316k.platform} -build_flags = ${env:test_D_ESP32-wrover-kit_4M316k.build_flags} -DHAS_ETHERNET - -DTESTING_USE_RTTTL - ;;; ESP32-s2 *********************************************************** [env:normal_ESP32s2_4M316k] From 53f0419ed5266113372499224c680282231f5410 Mon Sep 17 00:00:00 2001 From: TD-er Date: Mon, 6 Dec 2021 00:31:17 +0100 Subject: [PATCH 084/147] [PIO] Add missing ESP32-S2 build envs Not sure if there is already an official 16M board present with the ESP32-S2, but if there is, there will be support for it :) --- platformio_esp32_envs.ini | 47 +++++++++++++++++++++++++++++++++++---- tools/pio/copy_files.py | 7 +++--- 2 files changed, 47 insertions(+), 7 deletions(-) diff --git a/platformio_esp32_envs.ini b/platformio_esp32_envs.ini index 871ca35021..873f4e9d01 100644 --- a/platformio_esp32_envs.ini +++ b/platformio_esp32_envs.ini @@ -47,6 +47,7 @@ upload_protocol = ftdi debug_tool = ftdi ;debug_extra_cmds = break Misc.ino:3011 + [esp32_LittleFS] extends = esp32_common platform_packages = ${esp32_common.platform_packages} @@ -54,6 +55,12 @@ platform_packages = ${esp32_common.platform_packages} ;lib_deps = ${esp32_common.lib_deps}, https://github.com/lorol/LITTLEFS.git board_build.filesystem = littlefs +[esp32s2_LittleFS] +extends = esp32s2_common +platform_packages = ${esp32s2_common.platform_packages} + platformio/tool-mklittlefs @ ~1.203.200522 +;lib_deps = ${esp32_common.lib_deps}, https://github.com/lorol/LITTLEFS.git +board_build.filesystem = littlefs @@ -64,6 +71,13 @@ build_flags = ${esp32_common.build_flags} -DPLUGIN_BUILD_CUSTOM extra_scripts = ${esp32_common.extra_scripts} pre:tools/pio/pre_custom_esp32.py +[env:custom_IR_ESP32_4M316k] +extends = esp32_common +build_flags = ${esp32_common.build_flags} -DPLUGIN_BUILD_CUSTOM -DPLUGIN_BUILD_IR +lib_ignore = ${esp32_always.lib_ignore}, ESP32_ping, HeatpumpIR +extra_scripts = ${esp32_common.extra_scripts} + pre:tools/pio/pre_custom_esp32.py + ; ESP32-S2 [env:custom_ESP32s2_4M316k] @@ -74,11 +88,11 @@ extra_scripts = ${esp32s2_common.extra_scripts} pre:tools/pio/pre_custom_esp32.py -[env:custom_IR_ESP32_4M316k] -extends = esp32_common -build_flags = ${esp32_common.build_flags} -DPLUGIN_BUILD_CUSTOM -DPLUGIN_BUILD_IR +[env:custom_IR_ESP32s2_4M316k] +extends = esp32s2_common +build_flags = ${esp32s2_common.build_flags} -DPLUGIN_BUILD_CUSTOM -DPLUGIN_BUILD_IR lib_ignore = ${esp32_always.lib_ignore}, ESP32_ping, HeatpumpIR -extra_scripts = ${esp32_common.extra_scripts} +extra_scripts = ${esp32s2_common.extra_scripts} pre:tools/pio/pre_custom_esp32.py [env:normal_ESP32_4M316k] @@ -396,6 +410,26 @@ build_flags = ${esp32_LittleFS.build_flags} ; -mfix-esp32-psram-cache-issue board = lolin_d32_pro +[max_ESP32s2_16M] +extends = esp32s2_common +lib_ignore = ${esp32_always.lib_ignore}, ESP32_ping +lib_deps = ${esp32s2_common.lib_deps}, VL53L0X +board_upload.maximum_size = 4194304 +build_flags = ${esp32s2_common.build_flags} + -DFEATURE_ARDUINO_OTA + -DPLUGIN_BUILD_MAX_ESP32 + -DPLUGIN_BUILD_IR_EXTENDED + +; A Lolin D32 PRO with 16MB Flash, allowing 4MB sketch size, and file storage using LittleFS filesystem +[max_ESP32s2_16M_LittleFS] +extends = esp32s2_LittleFS +lib_ignore = ${esp32_always.lib_ignore}, ESP32_ping +lib_deps = ${esp32s2_LittleFS.lib_deps}, VL53L0X +board_upload.maximum_size = 4194304 +build_flags = ${esp32s2_LittleFS.build_flags} + -DFEATURE_ARDUINO_OTA + -DPLUGIN_BUILD_MAX_ESP32 + -DPLUGIN_BUILD_IR_EXTENDED ; A Lolin D32 PRO with 16MB Flash, allowing 4MB sketch size, and 1MB assigned to file storage using SPIFFS filesystem [env:max_ESP32_16M1M] @@ -426,3 +460,8 @@ build_flags = ${env:max_ESP32_16M2M_LittleFS.build_flags} -DHAS_ET [env:max_ESP32_16M8M_LittleFS_ETH] extends = env:max_ESP32_16M8M_LittleFS build_flags = ${env:max_ESP32_16M8M_LittleFS.build_flags} -DHAS_ETHERNET + +[env:max_ESP32s2_16M8M_LittleFS] +extends = max_ESP32s2_16M_LittleFS +board_build.partitions = esp32_partition_app4096k_spiffs8124k.csv + diff --git a/tools/pio/copy_files.py b/tools/pio/copy_files.py index d0458fe412..10d1943618 100644 --- a/tools/pio/copy_files.py +++ b/tools/pio/copy_files.py @@ -24,9 +24,10 @@ def get_max_bin_size(env_name, file_suffix): if "factory" in file_suffix: # Factory bin files include a part which is not overwritten via OTA max_bin_size = max_bin_size + 65536 - if "_ESP32_16M8M" in env_name or "_ESP32_16M2M" in env_name or "_ESP32_16M1M" in env_name: - # ESP32 with 4096k of sketch space. - max_bin_size = 4194304 + if "_ESP32_" in env_name or "_ESP32s2_" in env_name: + if "_16M8M" in env_name or "_16M2M" in env_name or "_16M1M" in env_name: + # ESP32 with 4096k of sketch space. + max_bin_size = 4194304 if "debug_" in env_name: # Debug env, used for analysis, not to be run on a node. max_bin_size = 0 From 46e1ef9b049d762b0c337ea46cb85c45aad81866 Mon Sep 17 00:00:00 2001 From: TD-er Date: Mon, 27 Dec 2021 23:56:32 +0100 Subject: [PATCH 085/147] [IDF4.4] Use Tasmota's IDF 4.4 platform-tasmota-espressif32 As suggested by @Jason2866 here: https://github.com/letscontrolit/ESPEasy/issues/3863#issuecomment-1001231966 --- platformio_core_defs.ini | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/platformio_core_defs.ini b/platformio_core_defs.ini index 23687cca80..811e6f7977 100644 --- a/platformio_core_defs.ini +++ b/platformio_core_defs.ini @@ -172,8 +172,10 @@ platform = espressif32 @ 3.3.2 platform_packages = framework-arduinoespressif32 build_flags = -DESP32_STAGE +; IDF 4.4 = platform-espressif32 3.4.x = Tasmota platform-espressif32 2.0.2 +; Just for those who lost track of the extremely confusing numbering schema. [core_esp32_3_4_0] -platform = https://github.com/tasmota/platform-espressif32/releases/download/v3.4.0/Tasmota-platform-espressif32.zip +platform = https://github.com/tasmota/platform-espressif32/releases/download/v2.0.2/platform-tasmota-espressif32-2.0.2.zip platform_packages = framework-arduinoespressif32 @ https://github.com/tasmota/arduino-esp32/releases/download/2.0.1.1/framework-arduinoespressif32-release_IDF4.4.tar.gz build_flags = -DESP32_STAGE @@ -185,7 +187,7 @@ platform_packages = framework-arduinoespressif32 @ https://github.com/ta build_flags = -DESP32_STAGE [core_esp32_3_4_0_esp32s2] -platform = https://github.com/tasmota/platform-espressif32/releases/download/v3.4.0/Tasmota-platform-espressif32.zip +platform = https://github.com/tasmota/platform-espressif32/releases/download/v2.0.2/platform-tasmota-espressif32-2.0.2.zip platform_packages = framework-arduinoespressif32 @ https://github.com/tasmota/arduino-esp32/releases/download/2.0.1.1/framework-arduinoespressif32-release_IDF4.4.tar.gz build_flags = -DESP32_STAGE From 0551245c102780b38fdf190ba097756eebacf7bc Mon Sep 17 00:00:00 2001 From: TD-er Date: Tue, 28 Dec 2021 13:26:03 +0100 Subject: [PATCH 086/147] [IDF4.4-v2.0.2] Now actually using the latest espressif/Arduino-esp32 --- platformio_core_defs.ini | 37 ++++--------------------------------- platformio_esp32_envs.ini | 16 ++++++++-------- 2 files changed, 12 insertions(+), 41 deletions(-) diff --git a/platformio_core_defs.ini b/platformio_core_defs.ini index 811e6f7977..5d0e7ff8f5 100644 --- a/platformio_core_defs.ini +++ b/platformio_core_defs.ini @@ -153,50 +153,21 @@ platform_packages = mcspr/toolchain-xtensa @ ~5.100300.211127 -; Updated ESP-IDF to the latest stable 4.0.1 -; See: https://github.com/platformio/platform-espressif32/releases -[core_esp32_1_12_2] -platform = espressif32@1.12.4 [core_esp32_2_1_0] platform = espressif32@2.1.0 build_flags = -DESP32_STAGE -[core_esp32_3_3_0] -platform = espressif32 @ 3.3.0 -platform_packages = framework-arduinoespressif32 -build_flags = -DESP32_STAGE - -[core_esp32_3_3_2] -platform = espressif32 @ 3.3.2 -platform_packages = framework-arduinoespressif32 -build_flags = -DESP32_STAGE -; IDF 4.4 = platform-espressif32 3.4.x = Tasmota platform-espressif32 2.0.2 +; Updated ESP-IDF to the latest stable 4.0.1 +; See: https://github.com/platformio/platform-espressif32/releases +; IDF 4.4 = platform-espressif32 3.4.x = espressif/arduino-esp32 tag 2.0.2 ; Just for those who lost track of the extremely confusing numbering schema. -[core_esp32_3_4_0] +[core_esp32_IDF4_4__2_0_2] platform = https://github.com/tasmota/platform-espressif32/releases/download/v2.0.2/platform-tasmota-espressif32-2.0.2.zip -platform_packages = framework-arduinoespressif32 @ https://github.com/tasmota/arduino-esp32/releases/download/2.0.1.1/framework-arduinoespressif32-release_IDF4.4.tar.gz build_flags = -DESP32_STAGE -[core_esp32_3_3_2_esp32s2] -platform = https://github.com/platformio/platform-espressif32.git#feature/arduino-idf-master -platform_packages = framework-arduinoespressif32 @ https://github.com/tasmota/arduino-esp32/releases/download/2.0.1/framework-arduinoespressif32-release_IDF4.4.tar.gz - platformio/tool-esptoolpy @ https://github.com/tasmota/esptool/releases/download/v3.2/esptool-v3.2.zip -build_flags = -DESP32_STAGE - -[core_esp32_3_4_0_esp32s2] -platform = https://github.com/tasmota/platform-espressif32/releases/download/v2.0.2/platform-tasmota-espressif32-2.0.2.zip -platform_packages = framework-arduinoespressif32 @ https://github.com/tasmota/arduino-esp32/releases/download/2.0.1.1/framework-arduinoespressif32-release_IDF4.4.tar.gz -build_flags = -DESP32_STAGE - - -[core_esp32_3_0_0] -platform = espressif32@3.0.0 -platform_packages = framework-arduinoespressif32 @ https://github.com/Jason2866/arduino-esp32/releases/download/1.0.5-rc6/esp32-1.0.5-rc6.zip - - [core_esp32_stage] platform = https://github.com/platformio/platform-espressif32.git#feature/arduino-idf-master platform_packages = framework-arduinoespressif32 @ https://github.com/tasmota/arduino-esp32/releases/download/2.0.1/framework-arduinoespressif32-release_IDF4.4.tar.gz diff --git a/platformio_esp32_envs.ini b/platformio_esp32_envs.ini index 873f4e9d01..44450b53bd 100644 --- a/platformio_esp32_envs.ini +++ b/platformio_esp32_envs.ini @@ -9,7 +9,7 @@ lib_ignore = ESP8266WiFi, ESP8266Ping, ESP8266WebServer, ESP8266H [esp32_common] -extends = common, core_esp32_3_4_0 +extends = common, core_esp32_IDF4_4__2_0_2 lib_deps = td-er/ESPeasySerial @ 2.0.8, I2Cdevlib-Core, adafruit/Adafruit ILI9341 @ ^1.5.6, Adafruit GFX Library, LOLIN_EPD, adafruit/Adafruit BusIO @ ^1.10.0, VL53L0X @ 1.3.0, SparkFun VL53L1X 4m Laser Distance Sensor @ 1.2.9, td-er/SparkFun MAX1704x Fuel Gauge Arduino Library @ ^1.0.1, ArduinoOTA, ESP32HTTPUpdateServer, FrogmoreScd30, Multi Channel Relay Arduino Library, SparkFun ADXL345 Arduino Library, ITG3205 lib_ignore = ${esp32_always.lib_ignore}, ESP32_ping, IRremoteESP8266, HeatpumpIR board_build.f_flash = 80000000L @@ -19,7 +19,7 @@ board_build.partitions = esp32_partition_app1810k_spiffs316k.csv extra_scripts = post:tools/pio/post_esp32.py ${extra_scripts_default.extra_scripts} build_unflags = -Wall -build_flags = ${core_esp32_3_4_0.build_flags} +build_flags = ${core_esp32_IDF4_4__2_0_2.build_flags} ${mqtt_flags.build_flags} -DCONFIG_FREERTOS_ASSERT_DISABLE -DCONFIG_LWIP_ESP_GRATUITOUS_ARP @@ -27,6 +27,7 @@ build_flags = ${core_esp32_3_4_0.build_flags} -DCORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_NONE board = esp32dev monitor_filters = esp32_exception_decoder +platform_packages = [esp32s2_common] @@ -36,8 +37,7 @@ build_flags = ${esp32_common.build_flags} -DBOARD_HAS_PSRAM -DESP32_ENABLE_PSRAM board = esp32-s2-saola-1 -platform = ${core_esp32_3_4_0_esp32s2.platform} -platform_packages = ${core_esp32_3_4_0_esp32s2.platform_packages} + [ESP32-wrover-kit] @@ -50,15 +50,15 @@ debug_tool = ftdi [esp32_LittleFS] extends = esp32_common -platform_packages = ${esp32_common.platform_packages} - platformio/tool-mklittlefs @ ~1.203.200522 +;platform_packages = ${esp32_common.platform_packages} +; platformio/tool-mklittlefs @ ~1.203.200522 ;lib_deps = ${esp32_common.lib_deps}, https://github.com/lorol/LITTLEFS.git board_build.filesystem = littlefs [esp32s2_LittleFS] extends = esp32s2_common -platform_packages = ${esp32s2_common.platform_packages} - platformio/tool-mklittlefs @ ~1.203.200522 +;platform_packages = ${esp32s2_common.platform_packages} +; platformio/tool-mklittlefs @ ~1.203.200522 ;lib_deps = ${esp32_common.lib_deps}, https://github.com/lorol/LITTLEFS.git board_build.filesystem = littlefs From 26e8aa6e02e17229eb41aa4188f204d0eacec472 Mon Sep 17 00:00:00 2001 From: TD-er Date: Tue, 28 Dec 2021 17:22:55 +0100 Subject: [PATCH 087/147] [LittleFS] Make sure to always define USE_LITTLEFS on LittleFS envs --- platformio_esp32_envs.ini | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/platformio_esp32_envs.ini b/platformio_esp32_envs.ini index 44450b53bd..c71aa7a8d5 100644 --- a/platformio_esp32_envs.ini +++ b/platformio_esp32_envs.ini @@ -53,6 +53,8 @@ extends = esp32_common ;platform_packages = ${esp32_common.platform_packages} ; platformio/tool-mklittlefs @ ~1.203.200522 ;lib_deps = ${esp32_common.lib_deps}, https://github.com/lorol/LITTLEFS.git +build_flags = ${esp32_common.build_flags} + -DUSE_LITTLEFS board_build.filesystem = littlefs [esp32s2_LittleFS] @@ -60,6 +62,8 @@ extends = esp32s2_common ;platform_packages = ${esp32s2_common.platform_packages} ; platformio/tool-mklittlefs @ ~1.203.200522 ;lib_deps = ${esp32_common.lib_deps}, https://github.com/lorol/LITTLEFS.git +build_flags = ${esp32s2_common.build_flags} + -DUSE_LITTLEFS board_build.filesystem = littlefs From 51449c6dc2bd9819f23fb533b956459296e42b80 Mon Sep 17 00:00:00 2001 From: TD-er Date: Sun, 2 Jan 2022 02:16:46 +0100 Subject: [PATCH 088/147] [PIO build ESP32] Attempt to fix boot loop on ESP32 with IDF4.4 16M flash ESP32 units keep rebooting when built with a partitioning schema for 16M flash units. This does not happen on older IDF versions, but does happen on IDF 4.4 --- .gitignore | 8 ++ boards/esp32_16M.json | 38 ++++++ .../configure/esp32/spi_download.conf | 125 ------------------ platformio_esp32_envs.ini | 11 +- src/src/Helpers/ESPEasy_time.cpp | 2 +- tools/pio/post_esp32.py | 9 +- 6 files changed, 63 insertions(+), 130 deletions(-) create mode 100644 boards/esp32_16M.json delete mode 100644 dist/Espressif_flash_download_tool_v3.8.5/configure/esp32/spi_download.conf diff --git a/.gitignore b/.gitignore index 981c8ae045..70cc10dd02 100644 --- a/.gitignore +++ b/.gitignore @@ -53,3 +53,11 @@ build_output/ **/__tmpfile.cpp src/_Custom.h + +dist/Espressif_flash_download_tool_v3.8.5/logs/ + +dist/Espressif_flash_download_tool_v3.8.5/dl_temp/bin_tmp/ + +dist/Espressif_flash_download_tool_v3.8.5/dl_temp/_temp_by_dltool/ + +dist/Espressif_flash_download_tool_v3.8.5/configure/esp32/spi_download.conf diff --git a/boards/esp32_16M.json b/boards/esp32_16M.json new file mode 100644 index 0000000000..bd61eb4e9b --- /dev/null +++ b/boards/esp32_16M.json @@ -0,0 +1,38 @@ +{ + "build": { + "arduino":{ + "ldscript": "esp32_out.ld" + }, + "core": "esp32", + "extra_flags": "-DARDUINO_ESP32_DEV -DBOARD_HAS_PSRAM -DARDUINO_USB_CDC_ON_BOOT=0 -DESP32_16M", + "f_cpu": "240000000L", + "f_flash": "40000000L", + "flash_mode": "dout", + "mcu": "esp32", + "variant": "esp32", + "partitions": "esp32_partition_app4096k_spiffs8124k.csv" + }, + "connectivity": [ + "wifi", + "bluetooth", + "ethernet", + "can" + ], + "debug": { + "openocd_target": "esp32.cfg" + }, + "frameworks": [ + "arduino", + "espidf" + ], + "name": "Espressif Generic ESP32 16M Flash, ESPEasy 4096 Code/OTA, 8M FS", + "upload": { + "flash_size": "16MB", + "maximum_ram_size": 327680, + "maximum_size": 16777216, + "require_upload_port": true, + "speed": 460800 + }, + "url": "https://en.wikipedia.org/wiki/ESP32", + "vendor": "Espressif" + } \ No newline at end of file diff --git a/dist/Espressif_flash_download_tool_v3.8.5/configure/esp32/spi_download.conf b/dist/Espressif_flash_download_tool_v3.8.5/configure/esp32/spi_download.conf deleted file mode 100644 index 16bc6a9fd1..0000000000 --- a/dist/Espressif_flash_download_tool_v3.8.5/configure/esp32/spi_download.conf +++ /dev/null @@ -1,125 +0,0 @@ -[EFUSE CHECK] -efuse_mode = 1 -efuse_err_halt = 1 - -[DOWNLOAD PATH] -file_sel0 = 0 -file_path0 = -file_offset0 = -file_sel1 = 0 -file_path1 = -file_offset1 = -file_sel2 = 0 -file_path2 = -file_offset2 = -file_sel3 = 0 -file_path3 = -file_offset3 = -file_sel4 = 0 -file_path4 = -file_offset4 = -file_sel5 = 0 -file_path5 = -file_offset5 = -file_sel6 = 0 -file_path6 = -file_offset6 = -file_sel7 = 0 -file_path7 = -file_offset7 = -file_sel8 = 0 -file_path8 = -file_offset8 = -file_sel9 = 0 -file_path9 = -file_offset9 = -file_sel10 = 0 -file_path10 = -file_offset10 = -file_sel11 = 0 -file_path11 = -file_offset11 = -file_sel12 = 0 -file_path12 = -file_offset12 = -file_sel13 = 0 -file_path13 = -file_offset13 = -file_sel14 = 0 -file_path14 = -file_offset14 = -file_sel15 = 0 -file_path15 = -file_offset15 = -file_sel16 = 0 -file_path16 = -file_offset16 = -file_sel17 = 0 -file_path17 = -file_offset17 = -file_sel18 = 0 -file_path18 = -file_offset18 = -default_path = C:\bin - -[LOCK] -lock_setting_password = 12345678 -lock_settings = 0 - -[FLASH_CRYSTAL] -spiautoset = 0 -spicfgdis = 0 -spispeed = 0 -spimode = 2 -flashsize = 2 -crystal = 0 - -[DOWNLOAD] -autostart1 = 0 -com_port1 = COM90 -baudrate1 = 4 -checkmac1 = 1 -erase_flash_en = True -new_status = 0 -num_bytes = 0 -non_volatile = False - -[FACTORY_MODE] -factory_mode_enable = False - -[LOCK_CHECK] -log_check_enable = False -log_check_baud = 115200 -log_check_str = -log_check_timeout = 3 - -[LOG_CHECK] -log_check_enable = False -log_check_baud = 115200 -log_check_str = 1.0.26 -log_check_timeout = 3 -log_check_port = COM6 -log_check_cmd_str = AT+GMR -log_check_enable_cmd = False -log_check_delaytime = 3 - -[ESPTOOL_PARAM] -after = hard_reset -baud = 115200 -before = default_reset -chip = auto -compress = 0 -flash_freq = keep -flash_mode = keep -flash_size = keep -no_compress = False -no_progress = False -no_stub = False -operation = write_flash -port = /dev/cu.SLAB_USBtoUART -spi_connection = 0 -verify = False - -[MAC SAVE] -mac_save_enable = False - diff --git a/platformio_esp32_envs.ini b/platformio_esp32_envs.ini index c71aa7a8d5..a5b16dfe43 100644 --- a/platformio_esp32_envs.ini +++ b/platformio_esp32_envs.ini @@ -12,7 +12,6 @@ lib_ignore = ESP8266WiFi, ESP8266Ping, ESP8266WebServer, ESP8266H extends = common, core_esp32_IDF4_4__2_0_2 lib_deps = td-er/ESPeasySerial @ 2.0.8, I2Cdevlib-Core, adafruit/Adafruit ILI9341 @ ^1.5.6, Adafruit GFX Library, LOLIN_EPD, adafruit/Adafruit BusIO @ ^1.10.0, VL53L0X @ 1.3.0, SparkFun VL53L1X 4m Laser Distance Sensor @ 1.2.9, td-er/SparkFun MAX1704x Fuel Gauge Arduino Library @ ^1.0.1, ArduinoOTA, ESP32HTTPUpdateServer, FrogmoreScd30, Multi Channel Relay Arduino Library, SparkFun ADXL345 Arduino Library, ITG3205 lib_ignore = ${esp32_always.lib_ignore}, ESP32_ping, IRremoteESP8266, HeatpumpIR -board_build.f_flash = 80000000L board_build.flash_mode = dout board_upload.maximum_size = 1900544 board_build.partitions = esp32_partition_app1810k_spiffs316k.csv @@ -75,6 +74,12 @@ build_flags = ${esp32_common.build_flags} -DPLUGIN_BUILD_CUSTOM extra_scripts = ${esp32_common.extra_scripts} pre:tools/pio/pre_custom_esp32.py +[env:custom_ESP32_4M316k_LittleFS] +extends = esp32_LittleFS +build_flags = ${esp32_LittleFS.build_flags} -DPLUGIN_BUILD_CUSTOM +extra_scripts = ${esp32_LittleFS.extra_scripts} + pre:tools/pio/pre_custom_esp32.py + [env:custom_IR_ESP32_4M316k] extends = esp32_common build_flags = ${esp32_common.build_flags} -DPLUGIN_BUILD_CUSTOM -DPLUGIN_BUILD_IR @@ -397,8 +402,10 @@ build_flags = ${esp32_common.build_flags} ; TODO: To enable PS-RAM Support needs more build flags than these 2, for now define ESP32_ENABLE_PSRAM is used to en/disable detecting PS-Ram size on Info page ; -DBOARD_HAS_PSRAM // both flags already enabled for Lolin D32 Pro board by PlatformIO ; -mfix-esp32-psram-cache-issue +;board = esp32_16M board = lolin_d32_pro + ; A Lolin D32 PRO with 16MB Flash, allowing 4MB sketch size, and file storage using LittleFS filesystem [max_ESP32_16M_LittleFS] extends = esp32_LittleFS @@ -412,7 +419,7 @@ build_flags = ${esp32_LittleFS.build_flags} ; TODO: To enable PS-RAM Support needs more build flags than these 2, for now define ESP32_ENABLE_PSRAM is used to en/disable detecting PS-Ram size on Info page ; -DBOARD_HAS_PSRAM // both flags already enabled for Lolin D32 Pro board by PlatformIO ; -mfix-esp32-psram-cache-issue -board = lolin_d32_pro +board = esp32_16M [max_ESP32s2_16M] extends = esp32s2_common diff --git a/src/src/Helpers/ESPEasy_time.cpp b/src/src/Helpers/ESPEasy_time.cpp index 9e8e38eb8a..b2970f7cbe 100644 --- a/src/src/Helpers/ESPEasy_time.cpp +++ b/src/src/Helpers/ESPEasy_time.cpp @@ -659,7 +659,7 @@ float ESPEasy_time::diurnalArc(float dec, float lat) { float height = -50.0f / 60.0f * rad; float latRad = lat * rad; - return 12.0 * acos((sin(height) - sin(latRad) * sin(dec)) / (cos(latRad) * cos(dec))) / M_PI; + return 12.0f * acos((sin(height) - sin(latRad) * sin(dec)) / (cos(latRad) * cos(dec))) / M_PI; } float ESPEasy_time::equationOfTime(int doy) { diff --git a/tools/pio/post_esp32.py b/tools/pio/post_esp32.py index 611b7dc570..c0b5ce6dfc 100644 --- a/tools/pio/post_esp32.py +++ b/tools/pio/post_esp32.py @@ -7,15 +7,20 @@ def esp32_create_factory_bin(source, target, env): new_file_name = env.subst("$BUILD_DIR/${PROGNAME}-factory.bin") sections = env.subst(env.get('FLASH_EXTRA_IMAGES')) new_file = open(new_file_name,"wb") + print(" {} | {}".format("Offset", "File")) for section in sections: sect_adr,sect_file = section.split(" ",1) + print(" - {} | {}".format(sect_adr,sect_file)) source = open(sect_file,"rb") new_file.seek(int(sect_adr,0)-offset) new_file.write(source.read()); source.close() - firmware = open(env.subst("$BUILD_DIR/${PROGNAME}.bin"),"rb") - new_file.seek(0x10000-offset) + firmware_name = env.subst("$BUILD_DIR/${PROGNAME}.bin") + firmware_start = 0x10000-offset + firmware = open(firmware_name,"rb") + print(" - {} | {}".format(hex(firmware_start),firmware_name)) + new_file.seek(firmware_start) new_file.write(firmware.read()) new_file.close() firmware.close() From 7fe3f745437bdbde744b54a197ed16865fbd0c81 Mon Sep 17 00:00:00 2001 From: TD-er Date: Sun, 2 Jan 2022 02:17:36 +0100 Subject: [PATCH 089/147] [Cleanup] Misc optimisations to reduce build size --- src/_P109_ThermOLED.ino | 6 +++--- src/src/ESPEasyCore/ESPEasyRules.cpp | 7 +++---- src/src/WebServer/DevicesPage.cpp | 9 +++++---- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/_P109_ThermOLED.ino b/src/_P109_ThermOLED.ino index edd254c2db..fce565150a 100644 --- a/src/_P109_ThermOLED.ino +++ b/src/_P109_ThermOLED.ino @@ -744,7 +744,7 @@ void P109_display_current_temp() { if (atempstr.length() > 0) { float atemp = atempstr.toFloat(); - atemp = (round(atemp * 10)) / 10.0; + atemp = (round(atemp * 10)) / 10.0f; if (Plugin_109_prev_temp != atemp) { P109_display->setColor(BLACK); @@ -760,7 +760,7 @@ void P109_display_current_temp() { void P109_display_setpoint_temp(byte force) { if (UserVar[Plugin_109_varindex + 2] == 1) { - float stemp = (round(UserVar[Plugin_109_varindex] * 10)) / 10.0; + float stemp = (round(UserVar[Plugin_109_varindex] * 10)) / 10.0f; if ((Plugin_109_prev_setpoint != stemp) || (force == 1)) { P109_display->setColor(BLACK); @@ -875,7 +875,7 @@ void P109_display_page() { } void P109_setSetpoint(String sptemp) { - float stemp = (round(UserVar[Plugin_109_varindex] * 10)) / 10.0; + float stemp = (round(UserVar[Plugin_109_varindex] * 10)) / 10.0f; if ((sptemp.charAt(0) == '+') || (sptemp.charAt(0) == 'p')) { stemp = stemp + sptemp.substring(1).toFloat(); diff --git a/src/src/ESPEasyCore/ESPEasyRules.cpp b/src/src/ESPEasyCore/ESPEasyRules.cpp index 01d70b2fae..1281a5b275 100644 --- a/src/src/ESPEasyCore/ESPEasyRules.cpp +++ b/src/src/ESPEasyCore/ESPEasyRules.cpp @@ -522,8 +522,7 @@ bool parse_bitwise_functions(const String& cmd_s_lower, const String& arg1, cons } bool parse_math_functions(const String& cmd_s_lower, const String& arg1, const String& arg2, const String& arg3, double& result) { - double farg1; - float farg2, farg3 = 0.0f; + double farg1, farg2, farg3 = 0.0f; if (!validDoubleFromString(arg1, farg1)) { return false; @@ -532,9 +531,9 @@ bool parse_math_functions(const String& cmd_s_lower, const String& arg1, const S if (cmd_s_lower.equals(F("constrain"))) { // Contrain a value X to be within range of A to B // Syntax like {constrain:x:a:b} to constrain x in range a...b - if (validFloatFromString(arg2, farg2) && validFloatFromString(arg3, farg3)) { + if (validDoubleFromString(arg2, farg2) && validDoubleFromString(arg3, farg3)) { if (farg2 > farg3) { - const float tmp = farg2; + const double tmp = farg2; farg2 = farg3; farg3 = tmp; } diff --git a/src/src/WebServer/DevicesPage.cpp b/src/src/WebServer/DevicesPage.cpp index 25f19c2b5b..4ee86572a7 100644 --- a/src/src/WebServer/DevicesPage.cpp +++ b/src/src/WebServer/DevicesPage.cpp @@ -1286,10 +1286,11 @@ void devicePage_show_task_values(taskIndex_t taskIndex, deviceIndex_t DeviceInde html_TR_TD(); addHtmlInt(varNr + 1); html_TD(); - String id = F("TDVN"); // ="taskdevicevaluename" - id += (varNr + 1); - addTextBox(id, ExtraTaskSettings.TaskDeviceValueNames[varNr], NAME_FORMULA_LENGTH_MAX); - + { + String id = F("TDVN"); // ="taskdevicevaluename" + id += (varNr + 1); + addTextBox(id, ExtraTaskSettings.TaskDeviceValueNames[varNr], NAME_FORMULA_LENGTH_MAX); + } if (Device[DeviceIndex].FormulaOption) { html_TD(); From decdca7ce4e9c3e6732441e01d7fa8c0a2332e5c Mon Sep 17 00:00:00 2001 From: TD-er Date: Sun, 2 Jan 2022 02:20:03 +0100 Subject: [PATCH 090/147] [LittleFS] Some cleanup to fix boot loops ESP32 IDF4.4 for 16M flash --- src/src/Helpers/ESPEasy_FactoryDefault.cpp | 4 +- src/src/Helpers/ESPEasy_Storage.cpp | 73 +++++++++++++++++----- src/src/Helpers/ESPEasy_Storage.h | 8 +++ src/src/WebServer/WebServer.h | 2 - 4 files changed, 67 insertions(+), 20 deletions(-) diff --git a/src/src/Helpers/ESPEasy_FactoryDefault.cpp b/src/src/Helpers/ESPEasy_FactoryDefault.cpp index 6d1dcd3275..b1931b9a97 100644 --- a/src/src/Helpers/ESPEasy_FactoryDefault.cpp +++ b/src/src/Helpers/ESPEasy_FactoryDefault.cpp @@ -63,9 +63,9 @@ void ResetFactory() saveToRTC(); // always format on factory reset, in case of corrupt FS - ESPEASY_FS.end(); +// ESPEASY_FS.end(); serialPrintln(F("RESET: formatting...")); - ESPEASY_FS.format(); + FS_format(); serialPrintln(F("RESET: formatting done...")); if (!ESPEASY_FS.begin()) diff --git a/src/src/Helpers/ESPEasy_Storage.cpp b/src/src/Helpers/ESPEasy_Storage.cpp index 3201a6b441..25953ff8e4 100644 --- a/src/src/Helpers/ESPEasy_Storage.cpp +++ b/src/src/Helpers/ESPEasy_Storage.cpp @@ -330,19 +330,19 @@ void fileSystemCheck() checkRAM(F("fileSystemCheck")); #endif addLog(LOG_LEVEL_INFO, F("FS : Mounting...")); - +#if defined(ESP32) && defined(USE_LITTLEFS) + if (getPartionCount(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_SPIFFS) != 0 + && ESPEASY_FS.begin()) +#else if (ESPEASY_FS.begin()) +#endif { clearAllCaches(); - #if defined(ESP8266) - fs::FSInfo fs_info; - ESPEASY_FS.info(fs_info); - if (loglevelActiveFor(LOG_LEVEL_INFO)) { String log = F("FS : Mount successful, used "); - log = log + fs_info.usedBytes; - log = log + F(" bytes of "); - log = log + fs_info.totalBytes; + log += SpiffsUsedBytes(); + log += F(" bytes of "); + log += SpiffsTotalBytes(); addLog(LOG_LEVEL_INFO, log); } @@ -352,26 +352,61 @@ void fileSystemCheck() while (retries > 0 && GarbageCollection()) { --retries; } - #endif // if defined(ESP8266) fs::File f = tryOpenFile(SettingsType::getSettingsFileName(SettingsType::Enum::BasicSettings_Type).c_str(), "r"); - - if (!f) - { + if (f) { + f.close(); + } else { ResetFactory(); } - - if (f) { f.close(); } } else { - String log = F("FS : Mount failed"); + const __FlashStringHelper * log = F("FS : Mount failed"); serialPrintln(log); addLog(LOG_LEVEL_ERROR, log); ResetFactory(); } } +bool FS_format() { + #ifdef USE_LITTLEFS + #ifdef ESP32 + disableCore1WDT(); + const bool res = ESPEASY_FS.begin(true); + ESPEASY_FS.end(); + enableCore1WDT(); + return res; + #else + return ESPEASY_FS.format(); + #endif + #else + return ESPEASY_FS.format(); + #endif +} + +#ifdef ESP32 + +# include + +int getPartionCount(uint8_t pType, uint8_t pSubType) { + esp_partition_type_t partitionType = static_cast(pType); + esp_partition_subtype_t subtype = static_cast(pSubType); + esp_partition_iterator_t _mypartiterator = esp_partition_find(partitionType, subtype, NULL); + int nrPartitions = 0; + + if (_mypartiterator) { + do { + ++nrPartitions; + } while ((_mypartiterator = esp_partition_next(_mypartiterator)) != NULL); + } + esp_partition_iterator_release(_mypartiterator); + return nrPartitions; +} + + +#endif + /********************************************************************************************\ Garbage collection \*********************************************************************************************/ @@ -1568,7 +1603,13 @@ String getPartitionType(uint8_t pType, uint8_t pSubType) { case ESP_PARTITION_SUBTYPE_DATA_COREDUMP: return F("COREDUMP"); case ESP_PARTITION_SUBTYPE_DATA_ESPHTTPD: return F("ESPHTTPD"); case ESP_PARTITION_SUBTYPE_DATA_FAT: return F("FAT"); - case ESP_PARTITION_SUBTYPE_DATA_SPIFFS: return F("SPIFFS"); + case ESP_PARTITION_SUBTYPE_DATA_SPIFFS: + #ifdef USE_LITTLEFS + return F("LittleFS"); + #else + return F("SPIFFS"); + #endif + case 0x99: return F("EEPROM"); // Not defined in esp_partition_subtype_t default: break; } } diff --git a/src/src/Helpers/ESPEasy_Storage.h b/src/src/Helpers/ESPEasy_Storage.h index c9d88ceac6..18bffc11d4 100644 --- a/src/src/Helpers/ESPEasy_Storage.h +++ b/src/src/Helpers/ESPEasy_Storage.h @@ -45,6 +45,14 @@ String BuildFixes(); \*********************************************************************************************/ void fileSystemCheck(); +bool FS_format(); + +#ifdef ESP32 + +int getPartionCount(uint8_t pType, uint8_t pSubType = 0xFF); + +#endif + /********************************************************************************************\ Garbage collection \*********************************************************************************************/ diff --git a/src/src/WebServer/WebServer.h b/src/src/WebServer/WebServer.h index 7ee05c568f..f09d606810 100644 --- a/src/src/WebServer/WebServer.h +++ b/src/src/WebServer/WebServer.h @@ -178,8 +178,6 @@ void getStorageTableSVG(SettingsType::Enum settingsType); #ifdef ESP32 -int getPartionCount(uint8_t pType); - void getPartitionTableSVG(uint8_t pType, unsigned int partitionColor); From 64328e9160ab9b9086386069a45f12b32f924012 Mon Sep 17 00:00:00 2001 From: TD-er Date: Tue, 4 Jan 2022 00:30:16 +0100 Subject: [PATCH 091/147] [IDF4.4 + 16M] Make normal builds with IDF4.4 work on 16M flash --- boards/esp32-cam.json | 38 ++ boards/esp32-m5core2.json | 38 ++ boards/esp32-odroid.json | 38 ++ boards/esp32_16M.json | 72 ++-- boards/esp32_4M.json | 38 ++ boards/esp32_8M.json | 38 ++ boards/esp32_solo1_4M.json | 38 ++ boards/esp32c3.json | 35 ++ boards/esp32s2.json | 62 ++-- boards/esp8266_16M14M_board.json | 32 ++ boards/esp8266_1M128k.json | 32 ++ boards/esp8266_1M128k_OTA.json | 32 ++ boards/esp8266_2M1M.json | 32 ++ boards/esp8266_2M256.json | 32 ++ boards/esp8266_4M1M_board.json | 32 ++ boards/esp8266_4M2M_board.json | 32 ++ boards/esp8266_4M3M.json | 32 ++ boards/esp8266_zbbridge.json | 32 ++ boards/esp8285_1M128k.json | 32 ++ boards/esp8285_1M128k_OTA.json | 32 ++ esp32_partition_app1572k_spiffs983k.csv | 6 + esp32_partition_app1856k_spiffs320k.csv | 6 + esp32_partition_app2944k_spiffs10M.csv | 6 + esp32_partition_app2944k_spiffs2M.csv | 6 + platformio.ini | 3 +- platformio_esp32_envs.ini | 465 ++---------------------- platformio_esp82xx_base.ini | 42 +-- platformio_esp82xx_envs.ini | 181 ++++----- src/ESPEasy_common.h | 29 +- src/src/ESPEasyCore/ESPEasyRules.cpp | 7 +- src/src/Helpers/ESPEasy_Storage.cpp | 2 - src/src/Helpers/Hardware.cpp | 12 +- src/src/Helpers/Hardware.h | 2 + src/src/Helpers/OTA.cpp | 4 +- src/src/Helpers/StringProvider.cpp | 2 + src/src/Helpers/StringProvider.h | 1 + src/src/WebServer/CustomPage.cpp | 4 +- src/src/WebServer/FileList.cpp | 2 + src/src/WebServer/LoadFromFS.cpp | 4 +- src/src/WebServer/SysInfoPage.cpp | 6 +- 40 files changed, 855 insertions(+), 684 deletions(-) create mode 100644 boards/esp32-cam.json create mode 100644 boards/esp32-m5core2.json create mode 100644 boards/esp32-odroid.json create mode 100644 boards/esp32_4M.json create mode 100644 boards/esp32_8M.json create mode 100644 boards/esp32_solo1_4M.json create mode 100644 boards/esp32c3.json create mode 100644 boards/esp8266_16M14M_board.json create mode 100644 boards/esp8266_1M128k.json create mode 100644 boards/esp8266_1M128k_OTA.json create mode 100644 boards/esp8266_2M1M.json create mode 100644 boards/esp8266_2M256.json create mode 100644 boards/esp8266_4M1M_board.json create mode 100644 boards/esp8266_4M2M_board.json create mode 100644 boards/esp8266_4M3M.json create mode 100644 boards/esp8266_zbbridge.json create mode 100644 boards/esp8285_1M128k.json create mode 100644 boards/esp8285_1M128k_OTA.json create mode 100644 esp32_partition_app1572k_spiffs983k.csv create mode 100644 esp32_partition_app1856k_spiffs320k.csv create mode 100644 esp32_partition_app2944k_spiffs10M.csv create mode 100644 esp32_partition_app2944k_spiffs2M.csv diff --git a/boards/esp32-cam.json b/boards/esp32-cam.json new file mode 100644 index 0000000000..a0abdbbfef --- /dev/null +++ b/boards/esp32-cam.json @@ -0,0 +1,38 @@ +{ + "build": { + "arduino":{ + "ldscript": "esp32_out.ld" + }, + "core": "esp32", + "extra_flags": "-DARDUINO_ESP32_DEV -DBOARD_HAS_PSRAM -DHAS_PSRAM_FIX -mfix-esp32-psram-cache-issue -mfix-esp32-psram-cache-strategy=memw -DARDUINO_USB_CDC_ON_BOOT=0 -DESP32_4M", + "f_cpu": "240000000L", + "f_flash": "80000000L", + "flash_mode": "dout", + "mcu": "esp32", + "variant": "esp32", + "partitions": "esp32_partition_app1810k_spiffs316k.csv" + }, + "connectivity": [ + "wifi", + "bluetooth", + "ethernet", + "can" + ], + "debug": { + "openocd_target": "esp32.cfg" + }, + "frameworks": [ + "arduino", + "espidf" + ], + "name": "AI Thinker ESP32-CAM, 4M Flash 4MB PSRAM, ESPEasy 1810k Code/OTA, 316k FS", + "upload": { + "flash_size": "4MB", + "maximum_ram_size": 327680, + "maximum_size": 1900544, + "require_upload_port": true, + "speed": 460800 + }, + "url": "https://wiki.ai-thinker.com/esp32-cam", + "vendor": "AI Thinker" +} diff --git a/boards/esp32-m5core2.json b/boards/esp32-m5core2.json new file mode 100644 index 0000000000..03ccbb52de --- /dev/null +++ b/boards/esp32-m5core2.json @@ -0,0 +1,38 @@ +{ + "build": { + "arduino":{ + "ldscript": "esp32_out.ld" + }, + "core": "esp32", + "extra_flags": "-DARDUINO_M5STACK_Core2 -DBOARD_HAS_PSRAM -DARDUINO_USB_CDC_ON_BOOT=0 -DESP32_16M", + "f_cpu": "240000000L", + "f_flash": "80000000L", + "flash_mode": "dout", + "mcu": "esp32", + "variant": "m5stack_core2", + "partitions": "esp32_partition_app4096k_spiffs8124k.csv" + }, + "connectivity": [ + "wifi", + "bluetooth", + "ethernet", + "can" + ], + "debug": { + "openocd_target": "esp32.cfg" + }, + "frameworks": [ + "arduino", + "espidf" + ], + "name": "M5Stack Core2 16M Flash, 4MB PSRAM, ESPEasy 4M Code/OTA, 8M FS", + "upload": { + "flash_size": "16MB", + "maximum_ram_size": 327680, + "maximum_size": 16777216, + "require_upload_port": true, + "speed": 2000000 + }, + "url": "http://www.m5stack.com", + "vendor": "M5Stack" +} diff --git a/boards/esp32-odroid.json b/boards/esp32-odroid.json new file mode 100644 index 0000000000..91236379aa --- /dev/null +++ b/boards/esp32-odroid.json @@ -0,0 +1,38 @@ +{ + "build": { + "arduino":{ + "ldscript": "esp32_out.ld" + }, + "core": "esp32", + "extra_flags": "-DARDUINO_ODROID_ESP32 -DBOARD_HAS_PSRAM -DHAS_PSRAM_FIX -mfix-esp32-psram-cache-issue -mfix-esp32-psram-cache-strategy=memw -DARDUINO_USB_CDC_ON_BOOT=0 -DESP32_16M", + "f_cpu": "240000000L", + "f_flash": "80000000L", + "flash_mode": "dout", + "mcu": "esp32", + "variant": "odroid_esp32", + "partitions": "esp32_partition_app4096k_spiffs8124k.csv" + }, + "connectivity": [ + "wifi", + "bluetooth", + "ethernet", + "can" + ], + "debug": { + "openocd_target": "esp32.cfg" + }, + "frameworks": [ + "arduino", + "espidf" + ], + "name": "ESP32 ODROID-GO 16M Flash, 4MB PSRAM, ESPEasy 4M Code/OTA, 8M FS", + "upload": { + "flash_size": "16MB", + "maximum_ram_size": 327680, + "maximum_size": 16777216, + "require_upload_port": true, + "speed": 2000000 + }, + "url": "https://www.hardkernel.com/main/products/prdt_info.php?g_code=G152875062626", + "vendor": "Hardkernel" +} diff --git a/boards/esp32_16M.json b/boards/esp32_16M.json index bd61eb4e9b..745a513249 100644 --- a/boards/esp32_16M.json +++ b/boards/esp32_16M.json @@ -1,38 +1,38 @@ { - "build": { - "arduino":{ - "ldscript": "esp32_out.ld" - }, - "core": "esp32", - "extra_flags": "-DARDUINO_ESP32_DEV -DBOARD_HAS_PSRAM -DARDUINO_USB_CDC_ON_BOOT=0 -DESP32_16M", - "f_cpu": "240000000L", - "f_flash": "40000000L", - "flash_mode": "dout", - "mcu": "esp32", - "variant": "esp32", - "partitions": "esp32_partition_app4096k_spiffs8124k.csv" + "build": { + "arduino":{ + "ldscript": "esp32_out.ld" }, - "connectivity": [ - "wifi", - "bluetooth", - "ethernet", - "can" - ], - "debug": { - "openocd_target": "esp32.cfg" - }, - "frameworks": [ - "arduino", - "espidf" - ], - "name": "Espressif Generic ESP32 16M Flash, ESPEasy 4096 Code/OTA, 8M FS", - "upload": { - "flash_size": "16MB", - "maximum_ram_size": 327680, - "maximum_size": 16777216, - "require_upload_port": true, - "speed": 460800 - }, - "url": "https://en.wikipedia.org/wiki/ESP32", - "vendor": "Espressif" - } \ No newline at end of file + "core": "esp32", + "extra_flags": "-DARDUINO_ESP32_DEV -DBOARD_HAS_PSRAM -DARDUINO_USB_CDC_ON_BOOT=0 -DESP32_16M", + "f_cpu": "80000000L", + "f_flash": "40000000L", + "flash_mode": "dout", + "mcu": "esp32", + "variant": "esp32", + "partitions": "esp32_partition_app4096k_spiffs8124k.csv" + }, + "connectivity": [ + "wifi", + "bluetooth", + "ethernet", + "can" + ], + "debug": { + "openocd_target": "esp32.cfg" + }, + "frameworks": [ + "arduino", + "espidf" + ], + "name": "Espressif Generic ESP32 16M Flash, ESPEasy 4M Code/OTA, 8M FS", + "upload": { + "flash_size": "16MB", + "maximum_ram_size": 327680, + "maximum_size": 4194304, + "require_upload_port": true, + "speed": 460800 + }, + "url": "https://en.wikipedia.org/wiki/ESP32", + "vendor": "Espressif" +} diff --git a/boards/esp32_4M.json b/boards/esp32_4M.json new file mode 100644 index 0000000000..8448cd05a1 --- /dev/null +++ b/boards/esp32_4M.json @@ -0,0 +1,38 @@ +{ + "build": { + "arduino":{ + "ldscript": "esp32_out.ld" + }, + "core": "esp32", + "extra_flags": "-DARDUINO_ESP32_DEV -DBOARD_HAS_PSRAM -DARDUINO_USB_CDC_ON_BOOT=0 -DESP32_4M", + "f_cpu": "80000000L", + "f_flash": "40000000L", + "flash_mode": "dout", + "mcu": "esp32", + "variant": "esp32", + "partitions": "esp32_partition_app1810k_spiffs316k.csv" + }, + "connectivity": [ + "wifi", + "bluetooth", + "ethernet", + "can" + ], + "debug": { + "openocd_target": "esp32.cfg" + }, + "frameworks": [ + "arduino", + "espidf" + ], + "name": "Espressif Generic ESP32 4M Flash, ESPEasy 1810k Code/OTA, 316k FS", + "upload": { + "flash_size": "4MB", + "maximum_ram_size": 327680, + "maximum_size": 1900544, + "require_upload_port": true, + "speed": 460800 + }, + "url": "https://en.wikipedia.org/wiki/ESP32", + "vendor": "Espressif" +} diff --git a/boards/esp32_8M.json b/boards/esp32_8M.json new file mode 100644 index 0000000000..4f17b22946 --- /dev/null +++ b/boards/esp32_8M.json @@ -0,0 +1,38 @@ +{ + "build": { + "arduino":{ + "ldscript": "esp32_out.ld" + }, + "core": "esp32", + "extra_flags": "-DARDUINO_ESP32_DEV -DBOARD_HAS_PSRAM -DARDUINO_USB_CDC_ON_BOOT=0 -DESP32_8M", + "f_cpu": "80000000L", + "f_flash": "40000000L", + "flash_mode": "dout", + "mcu": "esp32", + "variant": "esp32", + "partitions": "esp32_partition_app2944k_spiffs2M.csv" + }, + "connectivity": [ + "wifi", + "bluetooth", + "ethernet", + "can" + ], + "debug": { + "openocd_target": "esp32.cfg" + }, + "frameworks": [ + "arduino", + "espidf" + ], + "name": "Espressif Generic ESP32 8M Flash, ESPEasy 2944k Code/OTA, 2112k FS", + "upload": { + "flash_size": "8MB", + "maximum_ram_size": 327680, + "maximum_size": 8388608, + "require_upload_port": true, + "speed": 460800 + }, + "url": "https://en.wikipedia.org/wiki/ESP32", + "vendor": "Espressif" +} diff --git a/boards/esp32_solo1_4M.json b/boards/esp32_solo1_4M.json new file mode 100644 index 0000000000..0137459fea --- /dev/null +++ b/boards/esp32_solo1_4M.json @@ -0,0 +1,38 @@ +{ + "build": { + "arduino":{ + "ldscript": "esp32_out.ld" + }, + "core": "esp32", + "extra_flags": "-DARDUINO_ESP32_DEV -DARDUINO_USB_CDC_ON_BOOT=0 -DESP32_4M -DCORE32SOLO1", + "f_cpu": "80000000L", + "f_flash": "40000000L", + "flash_mode": "dout", + "mcu": "esp32", + "variant": "esp32", + "partitions": "esp32_partition_app1810k_spiffs316k.csv" + }, + "connectivity": [ + "wifi", + "bluetooth", + "ethernet", + "can" + ], + "debug": { + "openocd_target": "esp32-solo-1.cfg" + }, + "frameworks": [ + "arduino", + "espidf" + ], + "name": "Espressif Generic ESP32 4M Flash, ESPEasy 1810k Code/OTA, 316k FS", + "upload": { + "flash_size": "4MB", + "maximum_ram_size": 327680, + "maximum_size": 1900544, + "require_upload_port": true, + "speed": 460800 + }, + "url": "https://en.wikipedia.org/wiki/ESP32", + "vendor": "Espressif" +} diff --git a/boards/esp32c3.json b/boards/esp32c3.json new file mode 100644 index 0000000000..ebac09c3e3 --- /dev/null +++ b/boards/esp32c3.json @@ -0,0 +1,35 @@ +{ + "build": { + "arduino":{ + "ldscript": "esp32c3_out.ld" + }, + "core": "esp32", + "extra_flags": "-DARDUINO_USB_CDC_ON_BOOT=0 -DESP32_4M -DESP32C3", + "f_cpu": "160000000L", + "f_flash": "80000000L", + "flash_mode": "dout", + "mcu": "esp32c3", + "variant": "esp32c3", + "partitions": "esp32_partition_app1810k_spiffs316k.csv" + }, + "connectivity": [ + "wifi" + ], + "debug": { + "openocd_target": "esp32c3.cfg" + }, + "frameworks": [ + "arduino", + "espidf" + ], + "name": "Espressif Generic ESP32-C3 4M Flash, ESPEasy 1810k Code/OTA, 316k FS", + "upload": { + "flash_size": "4MB", + "maximum_ram_size": 327680, + "maximum_size": 1900544, + "require_upload_port": true, + "speed": 460800 + }, + "url": "https://docs.espressif.com/projects/esp-idf/en/latest/esp32c3/hw-reference/esp32c3/user-guide-devkitm-1.html", + "vendor": "Espressif" +} diff --git a/boards/esp32s2.json b/boards/esp32s2.json index 7aff095fce..f80f87a567 100644 --- a/boards/esp32s2.json +++ b/boards/esp32s2.json @@ -1,31 +1,35 @@ { - "build": { - "arduino":{ - "ldscript": "esp32s2_out.ld" - }, - "core": "esp32", - "mcu": "esp32s2", - "extra_flags": "-Desp32S2_dev_module", - "f_cpu": "240000000L", - "f_flash": "80000000L", - "flash_mode": "qio", - "mcu": "esp32s2", - "variant": "esp32s2" + "build": { + "arduino":{ + "ldscript": "esp32s2_out.ld" }, - "connectivity": [ - "wifi" - ], - "frameworks": [ - "arduino" - ], - "name": "ESP32S2 Dev Module", - "upload": { - "flash_size": "4MB", - "maximum_ram_size": 327680, - "maximum_size": 4194304, - "require_upload_port": true, - "speed": 460800 - }, - "url": "https://espressif.com", - "vendor": "espressif" - } \ No newline at end of file + "core": "esp32", + "extra_flags": "-DBOARD_HAS_PSRAM -DESP32_4M -DESP32S2", + "f_cpu": "240000000L", + "f_flash": "80000000L", + "flash_mode": "dout", + "mcu": "esp32s2", + "variant": "esp32s2", + "partitions": "esp32_partition_app1810k_spiffs316k.csv" + }, + "connectivity": [ + "wifi" + ], + "debug": { + "openocd_target": "esp32s2.cfg" + }, + "frameworks": [ + "espidf", + "arduino" + ], + "name": "Espressif Generic ESP32-S2 4M Flash, ESPEasy 1810k Code/OTA, 316k FS", + "upload": { + "flash_size": "4MB", + "maximum_ram_size": 327680, + "maximum_size": 1900544, + "require_upload_port": true, + "speed": 460800 + }, + "url": "https://docs.espressif.com/projects/esp-idf/en/latest/esp32s2/hw-reference/esp32s2/user-guide-saola-1-v1.2.html", + "vendor": "Espressif" +} diff --git a/boards/esp8266_16M14M_board.json b/boards/esp8266_16M14M_board.json new file mode 100644 index 0000000000..a620a464b8 --- /dev/null +++ b/boards/esp8266_16M14M_board.json @@ -0,0 +1,32 @@ +{ + "build": { + "arduino": { + "ldscript": "eagle.flash.16m14m.ld" + }, + "core": "esp8266", + "extra_flags": "-DESP8266 -DARDUINO_ARCH_ESP8266 -DARDUINO_ESP8266_ESP01 -DESP8266_4M -DESP8266_4M3M", + "f_cpu": "80000000L", + "f_flash": "40000000L", + "flash_mode": "dout", + "mcu": "esp8266", + "variant": "generic" + }, + "connectivity": [ + "wifi" + ], + "frameworks": [ + "arduino", + "esp8266-rtos-sdk", + "esp8266-nonos-sdk" + ], + "name": "Espressif Generic ESP8266 ESPEasy 16M Flash 14M FS", + "upload": { + "maximum_ram_size": 81920, + "maximum_size": 1040316, + "require_upload_port": true, + "resetmethod": "ck", + "speed": 115200 + }, + "url": "http://www.esp8266.com/wiki/doku.php?id=esp8266-module-family", + "vendor": "Espressif" +} diff --git a/boards/esp8266_1M128k.json b/boards/esp8266_1M128k.json new file mode 100644 index 0000000000..67ac758c84 --- /dev/null +++ b/boards/esp8266_1M128k.json @@ -0,0 +1,32 @@ +{ + "build": { + "arduino": { + "ldscript": "eagle.flash.1m128.ld" + }, + "core": "esp8266", + "extra_flags": "-DESP8266 -DARDUINO_ARCH_ESP8266 -DARDUINO_ESP8266_ESP01 -DESP8266_1M", + "f_cpu": "80000000L", + "f_flash": "40000000L", + "flash_mode": "dout", + "mcu": "esp8266", + "variant": "generic" + }, + "connectivity": [ + "wifi" + ], + "frameworks": [ + "arduino", + "esp8266-rtos-sdk", + "esp8266-nonos-sdk" + ], + "name": "Espressif Generic ESP8266 ESPEasy 1M Flash 128k FS", + "upload": { + "maximum_ram_size": 81920, + "maximum_size": 892912, + "require_upload_port": true, + "resetmethod": "ck", + "speed": 115200 + }, + "url": "http://www.esp8266.com/wiki/doku.php?id=esp8266-module-family", + "vendor": "Espressif" +} diff --git a/boards/esp8266_1M128k_OTA.json b/boards/esp8266_1M128k_OTA.json new file mode 100644 index 0000000000..217d424733 --- /dev/null +++ b/boards/esp8266_1M128k_OTA.json @@ -0,0 +1,32 @@ +{ + "build": { + "arduino": { + "ldscript": "eagle.flash.1m128.ld" + }, + "core": "esp8266", + "extra_flags": "-DESP8266 -DARDUINO_ARCH_ESP8266 -DARDUINO_ESP8266_ESP01 -DESP8266_1M", + "f_cpu": "80000000L", + "f_flash": "40000000L", + "flash_mode": "dout", + "mcu": "esp8266", + "variant": "generic" + }, + "connectivity": [ + "wifi" + ], + "frameworks": [ + "arduino", + "esp8266-rtos-sdk", + "esp8266-nonos-sdk" + ], + "name": "Espressif Generic ESP8266 ESPEasy 1M Flash 128k FS", + "upload": { + "maximum_ram_size": 81920, + "maximum_size": 616448, + "require_upload_port": true, + "resetmethod": "ck", + "speed": 115200 + }, + "url": "http://www.esp8266.com/wiki/doku.php?id=esp8266-module-family", + "vendor": "Espressif" +} diff --git a/boards/esp8266_2M1M.json b/boards/esp8266_2M1M.json new file mode 100644 index 0000000000..942bab84f6 --- /dev/null +++ b/boards/esp8266_2M1M.json @@ -0,0 +1,32 @@ +{ + "build": { + "arduino": { + "ldscript": "eagle.flash.2m1m.ld" + }, + "core": "esp8266", + "extra_flags": "-DESP8266 -DARDUINO_ARCH_ESP8266 -DARDUINO_ESP8266_ESP01 -DESP8266_2M -DESP8266_2M1M", + "f_cpu": "80000000L", + "f_flash": "40000000L", + "flash_mode": "dout", + "mcu": "esp8266", + "variant": "generic" + }, + "connectivity": [ + "wifi" + ], + "frameworks": [ + "arduino", + "esp8266-rtos-sdk", + "esp8266-nonos-sdk" + ], + "name": "Espressif Generic ESP8266 ESPEasy 1M sketch 1M FS", + "upload": { + "maximum_ram_size": 81920, + "maximum_size": 1040316, + "require_upload_port": true, + "resetmethod": "ck", + "speed": 115200 + }, + "url": "http://www.esp8266.com/wiki/doku.php?id=esp8266-module-family", + "vendor": "Espressif" +} diff --git a/boards/esp8266_2M256.json b/boards/esp8266_2M256.json new file mode 100644 index 0000000000..88ddd31092 --- /dev/null +++ b/boards/esp8266_2M256.json @@ -0,0 +1,32 @@ +{ + "build": { + "arduino": { + "ldscript": "eagle.flash.2m256.ld" + }, + "core": "esp8266", + "extra_flags": "-DESP8266 -DARDUINO_ARCH_ESP8266 -DARDUINO_ESP8266_ESP01 -DESP8266_2M -DESP8266_2M256", + "f_cpu": "80000000L", + "f_flash": "40000000L", + "flash_mode": "dout", + "mcu": "esp8266", + "variant": "generic" + }, + "connectivity": [ + "wifi" + ], + "frameworks": [ + "arduino", + "esp8266-rtos-sdk", + "esp8266-nonos-sdk" + ], + "name": "Espressif Generic ESP8266 ESPEasy 2M Flash 256k FS", + "upload": { + "maximum_ram_size": 81920, + "maximum_size": 1040316, + "require_upload_port": true, + "resetmethod": "ck", + "speed": 115200 + }, + "url": "http://www.esp8266.com/wiki/doku.php?id=esp8266-module-family", + "vendor": "Espressif" +} diff --git a/boards/esp8266_4M1M_board.json b/boards/esp8266_4M1M_board.json new file mode 100644 index 0000000000..6e8d75fd76 --- /dev/null +++ b/boards/esp8266_4M1M_board.json @@ -0,0 +1,32 @@ +{ + "build": { + "arduino": { + "ldscript": "eagle.flash.4m1m.ld" + }, + "core": "esp8266", + "extra_flags": "-DESP8266 -DARDUINO_ARCH_ESP8266 -DARDUINO_ESP8266_ESP01 -DESP8266_4M -DESP8266_4M3M", + "f_cpu": "80000000L", + "f_flash": "40000000L", + "flash_mode": "dout", + "mcu": "esp8266", + "variant": "generic" + }, + "connectivity": [ + "wifi" + ], + "frameworks": [ + "arduino", + "esp8266-rtos-sdk", + "esp8266-nonos-sdk" + ], + "name": "Espressif Generic ESP8266 ESPEasy 4M Flash 1M FS", + "upload": { + "maximum_ram_size": 81920, + "maximum_size": 1040316, + "require_upload_port": true, + "resetmethod": "ck", + "speed": 115200 + }, + "url": "http://www.esp8266.com/wiki/doku.php?id=esp8266-module-family", + "vendor": "Espressif" +} diff --git a/boards/esp8266_4M2M_board.json b/boards/esp8266_4M2M_board.json new file mode 100644 index 0000000000..2025df330b --- /dev/null +++ b/boards/esp8266_4M2M_board.json @@ -0,0 +1,32 @@ +{ + "build": { + "arduino": { + "ldscript": "eagle.flash.4m2m.ld" + }, + "core": "esp8266", + "extra_flags": "-DESP8266 -DARDUINO_ARCH_ESP8266 -DARDUINO_ESP8266_ESP01 -DESP8266_4M -DESP8266_4M2M", + "f_cpu": "80000000L", + "f_flash": "40000000L", + "flash_mode": "dout", + "mcu": "esp8266", + "variant": "generic" + }, + "connectivity": [ + "wifi" + ], + "frameworks": [ + "arduino", + "esp8266-rtos-sdk", + "esp8266-nonos-sdk" + ], + "name": "Espressif Generic ESP8266 ESPEasy 4M Flash 2M FS", + "upload": { + "maximum_ram_size": 81920, + "maximum_size": 1040316, + "require_upload_port": true, + "resetmethod": "ck", + "speed": 115200 + }, + "url": "http://www.esp8266.com/wiki/doku.php?id=esp8266-module-family", + "vendor": "Espressif" +} diff --git a/boards/esp8266_4M3M.json b/boards/esp8266_4M3M.json new file mode 100644 index 0000000000..d036e0527b --- /dev/null +++ b/boards/esp8266_4M3M.json @@ -0,0 +1,32 @@ +{ + "build": { + "arduino": { + "ldscript": "eagle.flash.4m3m.ld" + }, + "core": "esp8266", + "extra_flags": "-DESP8266 -DARDUINO_ARCH_ESP8266 -DARDUINO_ESP8266_ESP01 -DESP8266_4M -DESP8266_4M3M", + "f_cpu": "80000000L", + "f_flash": "40000000L", + "flash_mode": "dout", + "mcu": "esp8266", + "variant": "generic" + }, + "connectivity": [ + "wifi" + ], + "frameworks": [ + "arduino", + "esp8266-rtos-sdk", + "esp8266-nonos-sdk" + ], + "name": "Espressif Generic ESP8266 ESPEasy 4M Flash 3M FS", + "upload": { + "maximum_ram_size": 81920, + "maximum_size": 1040316, + "require_upload_port": true, + "resetmethod": "ck", + "speed": 115200 + }, + "url": "http://www.esp8266.com/wiki/doku.php?id=esp8266-module-family", + "vendor": "Espressif" +} diff --git a/boards/esp8266_zbbridge.json b/boards/esp8266_zbbridge.json new file mode 100644 index 0000000000..e02039015f --- /dev/null +++ b/boards/esp8266_zbbridge.json @@ -0,0 +1,32 @@ +{ + "build": { + "arduino": { + "ldscript": "eagle.flash.2m256.ld" + }, + "core": "esp8266", + "extra_flags": "-DESP8266 -DARDUINO_ARCH_ESP8266 -DARDUINO_ESP8266_ESP01 -DESP8266_2M -DESP8266_2M256", + "f_cpu": "160000000L", + "f_flash": "40000000L", + "flash_mode": "dout", + "mcu": "esp8266", + "variant": "generic" + }, + "connectivity": [ + "wifi" + ], + "frameworks": [ + "arduino", + "esp8266-rtos-sdk", + "esp8266-nonos-sdk" + ], + "name": "Sonoff ZbBridge ESPEasy 2M Flash 256k FS", + "upload": { + "maximum_ram_size": 81920, + "maximum_size": 1040316, + "require_upload_port": true, + "resetmethod": "ck", + "speed": 115200 + }, + "url": "https://templates.blakadder.com/sonoff_ZBBridge.html", + "vendor": "Espressif" +} diff --git a/boards/esp8285_1M128k.json b/boards/esp8285_1M128k.json new file mode 100644 index 0000000000..476ad73f27 --- /dev/null +++ b/boards/esp8285_1M128k.json @@ -0,0 +1,32 @@ +{ + "build": { + "arduino": { + "ldscript": "eagle.flash.1m128.ld" + }, + "core": "esp8266", + "extra_flags": "-DESP8266 -DESP8285 -DARDUINO_ARCH_ESP8266 -DARDUINO_ESP8266_ESP01 -DESP8266_1M", + "f_cpu": "80000000L", + "f_flash": "40000000L", + "flash_mode": "dout", + "mcu": "esp8266", + "variant": "generic" + }, + "connectivity": [ + "wifi" + ], + "frameworks": [ + "arduino", + "esp8266-rtos-sdk", + "esp8266-nonos-sdk" + ], + "name": "Espressif Generic ESP8285 ESPEasy 1M Flash 128k FS", + "upload": { + "maximum_ram_size": 81920, + "maximum_size": 892912, + "require_upload_port": true, + "resetmethod": "ck", + "speed": 115200 + }, + "url": "http://www.esp8266.com/wiki/doku.php?id=esp8266-module-family", + "vendor": "Espressif" +} diff --git a/boards/esp8285_1M128k_OTA.json b/boards/esp8285_1M128k_OTA.json new file mode 100644 index 0000000000..cc1ed3f674 --- /dev/null +++ b/boards/esp8285_1M128k_OTA.json @@ -0,0 +1,32 @@ +{ + "build": { + "arduino": { + "ldscript": "eagle.flash.1m128.ld" + }, + "core": "esp8266", + "extra_flags": "-DESP8266 -DESP8285 -DARDUINO_ARCH_ESP8266 -DARDUINO_ESP8266_ESP01 -DESP8266_1M", + "f_cpu": "80000000L", + "f_flash": "40000000L", + "flash_mode": "dout", + "mcu": "esp8266", + "variant": "generic" + }, + "connectivity": [ + "wifi" + ], + "frameworks": [ + "arduino", + "esp8266-rtos-sdk", + "esp8266-nonos-sdk" + ], + "name": "Espressif Generic ESP8285 ESPEasy 1M Flash 128k FS", + "upload": { + "maximum_ram_size": 81920, + "maximum_size": 616448, + "require_upload_port": true, + "resetmethod": "ck", + "speed": 115200 + }, + "url": "http://www.esp8266.com/wiki/doku.php?id=esp8266-module-family", + "vendor": "Espressif" +} diff --git a/esp32_partition_app1572k_spiffs983k.csv b/esp32_partition_app1572k_spiffs983k.csv new file mode 100644 index 0000000000..3a317ea7bb --- /dev/null +++ b/esp32_partition_app1572k_spiffs983k.csv @@ -0,0 +1,6 @@ +# Name, Type, SubType, Offset, Size, Flags +nvs, data, nvs, 0x9000, 0x5000, +otadata, data, ota, 0xe000, 0x2000, +app0, app, ota_0, 0x10000, 0x180000, +app1, app, ota_1, 0x190000, 0x180000, +spiffs, data, spiffs, 0x310000,0x0F0000, diff --git a/esp32_partition_app1856k_spiffs320k.csv b/esp32_partition_app1856k_spiffs320k.csv new file mode 100644 index 0000000000..09659603f9 --- /dev/null +++ b/esp32_partition_app1856k_spiffs320k.csv @@ -0,0 +1,6 @@ +# Name, Type, SubType, Offset, Size, Flags +nvs, data, nvs, 0x9000, 0x5000, +otadata, data, ota, 0xe000, 0x2000, +app0, app, ota_0, 0x10000, 0x1D0000, +app1, app, ota_1, 0x1E0000, 0x1D0000, +spiffs, data, spiffs, 0x3B0000,0x50000, diff --git a/esp32_partition_app2944k_spiffs10M.csv b/esp32_partition_app2944k_spiffs10M.csv new file mode 100644 index 0000000000..f87b84a9c2 --- /dev/null +++ b/esp32_partition_app2944k_spiffs10M.csv @@ -0,0 +1,6 @@ +# Name, Type, SubType, Offset, Size, Flags +nvs, data, nvs, 0x9000, 0x5000, +otadata, data, ota, 0xe000, 0x2000, +app0, app, ota_0, 0x10000, 0x2F0000, +app1, app, ota_1, 0x300000, 0x2F0000, +spiffs, data, spiffs, 0x5F0000,0xA10000, diff --git a/esp32_partition_app2944k_spiffs2M.csv b/esp32_partition_app2944k_spiffs2M.csv new file mode 100644 index 0000000000..9c0897d514 --- /dev/null +++ b/esp32_partition_app2944k_spiffs2M.csv @@ -0,0 +1,6 @@ +# Name, Type, SubType, Offset, Size, Flags +nvs, data, nvs, 0x9000, 0x5000, +otadata, data, ota, 0xe000, 0x2000, +app0, app, ota_0, 0x10000, 0x2F0000, +app1, app, ota_1, 0x300000, 0x2F0000, +spiffs, data, spiffs, 0x5F0000,0x210000, diff --git a/platformio.ini b/platformio.ini index 03dc492f77..847b6c033c 100644 --- a/platformio.ini +++ b/platformio.ini @@ -20,7 +20,8 @@ extra_configs = platformio_esp32_envs.ini platformio_special_envs.ini -default_envs = custom_ESP8266_4M1M, custom_ESP32_4M316k +;default_envs = normal_ESP32_4M +default_envs = normal_ESP32_16M8M_LittleFS ; default_envs = custom_ESP8266_4M1M ;default_envs = normal_ESP8266_4M1M diff --git a/platformio_esp32_envs.ini b/platformio_esp32_envs.ini index a5b16dfe43..8339cfec6f 100644 --- a/platformio_esp32_envs.ini +++ b/platformio_esp32_envs.ini @@ -10,469 +10,52 @@ lib_ignore = ESP8266WiFi, ESP8266Ping, ESP8266WebServer, ESP8266H [esp32_common] extends = common, core_esp32_IDF4_4__2_0_2 -lib_deps = td-er/ESPeasySerial @ 2.0.8, I2Cdevlib-Core, adafruit/Adafruit ILI9341 @ ^1.5.6, Adafruit GFX Library, LOLIN_EPD, adafruit/Adafruit BusIO @ ^1.10.0, VL53L0X @ 1.3.0, SparkFun VL53L1X 4m Laser Distance Sensor @ 1.2.9, td-er/SparkFun MAX1704x Fuel Gauge Arduino Library @ ^1.0.1, ArduinoOTA, ESP32HTTPUpdateServer, FrogmoreScd30, Multi Channel Relay Arduino Library, SparkFun ADXL345 Arduino Library, ITG3205 -lib_ignore = ${esp32_always.lib_ignore}, ESP32_ping, IRremoteESP8266, HeatpumpIR -board_build.flash_mode = dout -board_upload.maximum_size = 1900544 -board_build.partitions = esp32_partition_app1810k_spiffs316k.csv +lib_deps = td-er/ESPeasySerial @ 2.0.8, adafruit/Adafruit ILI9341 @ ^1.5.6, Adafruit GFX Library, LOLIN_EPD, Adafruit BusIO, VL53L0X @ 1.3.0, SparkFun VL53L1X 4m Laser Distance Sensor @ 1.2.9, td-er/SparkFun MAX1704x Fuel Gauge Arduino Library @ ^1.0.1, ArduinoOTA, ESP32HTTPUpdateServer, FrogmoreScd30, Multi Channel Relay Arduino Library +lib_ignore = ${esp32_always.lib_ignore}, ESP32_ping, IRremoteESP8266, HeatpumpIR, ESP32 BLE Arduino extra_scripts = post:tools/pio/post_esp32.py ${extra_scripts_default.extra_scripts} build_unflags = -Wall -build_flags = ${core_esp32_IDF4_4__2_0_2.build_flags} - ${mqtt_flags.build_flags} +build_flags = ${core_esp32_IDF4_4__2_0_2.build_flags} + ${mqtt_flags.build_flags} -DCONFIG_FREERTOS_ASSERT_DISABLE -DCONFIG_LWIP_ESP_GRATUITOUS_ARP -fno-strict-aliasing -DCORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_NONE -board = esp32dev monitor_filters = esp32_exception_decoder -platform_packages = - -[esp32s2_common] -extends = esp32_common -build_flags = ${esp32_common.build_flags} - -DESP32S2 - -DBOARD_HAS_PSRAM - -DESP32_ENABLE_PSRAM -board = esp32-s2-saola-1 - - - -[ESP32-wrover-kit] -extends = esp32_common -board = esp-wrover-kit -upload_protocol = ftdi -debug_tool = ftdi -;debug_extra_cmds = break Misc.ino:3011 - - -[esp32_LittleFS] +[esp32_common_LittleFS] extends = esp32_common -;platform_packages = ${esp32_common.platform_packages} -; platformio/tool-mklittlefs @ ~1.203.200522 -;lib_deps = ${esp32_common.lib_deps}, https://github.com/lorol/LITTLEFS.git build_flags = ${esp32_common.build_flags} -DUSE_LITTLEFS +lib_deps = ${esp32_common.lib_deps}, LittleFS board_build.filesystem = littlefs -[esp32s2_LittleFS] -extends = esp32s2_common -;platform_packages = ${esp32s2_common.platform_packages} -; platformio/tool-mklittlefs @ ~1.203.200522 -;lib_deps = ${esp32_common.lib_deps}, https://github.com/lorol/LITTLEFS.git -build_flags = ${esp32s2_common.build_flags} - -DUSE_LITTLEFS -board_build.filesystem = littlefs - - - -; Custom: 4096k version -------------------------- -[env:custom_ESP32_4M316k] -extends = esp32_common -build_flags = ${esp32_common.build_flags} -DPLUGIN_BUILD_CUSTOM -extra_scripts = ${esp32_common.extra_scripts} - pre:tools/pio/pre_custom_esp32.py - -[env:custom_ESP32_4M316k_LittleFS] -extends = esp32_LittleFS -build_flags = ${esp32_LittleFS.build_flags} -DPLUGIN_BUILD_CUSTOM -extra_scripts = ${esp32_LittleFS.extra_scripts} - pre:tools/pio/pre_custom_esp32.py - -[env:custom_IR_ESP32_4M316k] -extends = esp32_common -build_flags = ${esp32_common.build_flags} -DPLUGIN_BUILD_CUSTOM -DPLUGIN_BUILD_IR -lib_ignore = ${esp32_always.lib_ignore}, ESP32_ping, HeatpumpIR -extra_scripts = ${esp32_common.extra_scripts} - pre:tools/pio/pre_custom_esp32.py - - -; ESP32-S2 -[env:custom_ESP32s2_4M316k] -extends = esp32s2_common -build_flags = ${esp32s2_common.build_flags} - -DPLUGIN_BUILD_CUSTOM -extra_scripts = ${esp32s2_common.extra_scripts} - pre:tools/pio/pre_custom_esp32.py - - -[env:custom_IR_ESP32s2_4M316k] -extends = esp32s2_common -build_flags = ${esp32s2_common.build_flags} -DPLUGIN_BUILD_CUSTOM -DPLUGIN_BUILD_IR -lib_ignore = ${esp32_always.lib_ignore}, ESP32_ping, HeatpumpIR -extra_scripts = ${esp32s2_common.extra_scripts} - pre:tools/pio/pre_custom_esp32.py - -[env:normal_ESP32_4M316k] -extends = esp32_common -lib_deps = ${esp32_common.lib_deps}, ServoESP32 -build_flags = ${esp32_common.build_flags} - -DFEATURE_ARDUINO_OTA -[env:test_A_ESP32_4M316k] +[esp32s2_common_LittleFS] extends = esp32_common -platform = ${esp32_common.platform} -build_flags = ${esp32_common.build_flags} - -DFEATURE_ARDUINO_OTA - -DPLUGIN_SET_TEST_ESP32 - -DTESTING_USE_RTTTL - -[env:test_B_ESP32_4M316k] -extends = esp32_common -build_flags = ${esp32_common.build_flags} - -DFEATURE_ARDUINO_OTA - -DPLUGIN_SET_TEST_B_ESP32 - -DTESTING_USE_RTTTL - -[env:test_C_ESP32_4M316k] -extends = esp32_common -build_flags = ${esp32_common.build_flags} - -DFEATURE_ARDUINO_OTA - -DPLUGIN_SET_TEST_C_ESP32 - -DTESTING_USE_RTTTL - -[env:test_D_ESP32_4M316k] -extends = esp32_common -build_flags = ${esp32_common.build_flags} - -DFEATURE_ARDUINO_OTA - -DPLUGIN_SET_TEST_D_ESP32 - -DTESTING_USE_RTTTL - -[env:test_E_ESP32_4M316k] -extends = esp32_common -platform = ${esp32_common.platform} -build_flags = ${esp32_common.build_flags} - -DFEATURE_ARDUINO_OTA - -DPLUGIN_SET_TEST_E_ESP32 - -DTESTING_USE_RTTTL -board = esp32dev -extra_scripts = ${esp32_common.extra_scripts} - - -[env:test_A_ESP32-wrover-kit_4M316k] -extends = ESP32-wrover-kit -build_flags = ${ESP32-wrover-kit.build_flags} - -DFEATURE_ARDUINO_OTA - -DPLUGIN_SET_TEST_ESP32 - -DTESTING_USE_RTTTL - - -[env:test_B_ESP32-wrover-kit_4M316k] -extends = ESP32-wrover-kit -build_flags = ${ESP32-wrover-kit.build_flags} - -DFEATURE_ARDUINO_OTA - -DPLUGIN_SET_TEST_B_ESP32 - -DTESTING_USE_RTTTL - - -[env:test_C_ESP32-wrover-kit_4M316k] -extends = ESP32-wrover-kit -build_flags = ${ESP32-wrover-kit.build_flags} - -DFEATURE_ARDUINO_OTA - -DPLUGIN_SET_TEST_C_ESP32 - -DTESTING_USE_RTTTL - - -[env:test_D_ESP32-wrover-kit_4M316k] -extends = ESP32-wrover-kit -build_flags = ${ESP32-wrover-kit.build_flags} - -DFEATURE_ARDUINO_OTA - -DPLUGIN_SET_TEST_D_ESP32 - -DTESTING_USE_RTTTL - - -[env:test_E_ESP32-wrover-kit_4M316k] -extends = esp32_common -platform = ${esp32_common.platform} build_flags = ${esp32_common.build_flags} - -DFEATURE_ARDUINO_OTA - -DPLUGIN_SET_TEST_E_ESP32 - -DTESTING_USE_RTTTL -board = esp-wrover-kit -upload_protocol = ftdi -debug_tool = ftdi -debug_extra_cmds = break Misc.ino:3011 -extra_scripts = ${esp32_common.extra_scripts} - -[env:test_A_ESP32_4M316k_lolin_d32_pro] -extends = esp32_common -build_flags = ${esp32_common.build_flags} -; -DFEATURE_ARDUINO_OTA - -DPLUGIN_SET_TEST_ESP32 -; -DUSES_P096 - -DTESTING_USE_RTTTL -board = lolin_d32_pro - - -[env:test_B_ESP32_4M316k_lolin_d32_pro] -extends = esp32_common -build_flags = ${esp32_common.build_flags} -; -DFEATURE_ARDUINO_OTA - -DPLUGIN_SET_TEST_B_ESP32 - -DUSES_P096 - -DTESTING_USE_RTTTL -board = lolin_d32_pro - - -[env:test_C_ESP32_4M316k_lolin_d32_pro] -extends = esp32_common -build_flags = ${esp32_common.build_flags} -; -DFEATURE_ARDUINO_OTA - -DPLUGIN_SET_TEST_C_ESP32 -; -DUSES_P096 - -DTESTING_USE_RTTTL -board = lolin_d32_pro - - -[env:test_D_ESP32_4M316k_lolin_d32_pro] -extends = esp32_common -build_flags = ${esp32_common.build_flags} -; -DFEATURE_ARDUINO_OTA - -DPLUGIN_SET_TEST_D_ESP32 - -DUSES_P096 - -DTESTING_USE_RTTTL -board = lolin_d32_pro - - -[env:test_E_ESP32_4M316k_lolin_d32_pro] -extends = esp32_common -platform = ${esp32_common.platform} -build_flags = ${esp32_common.build_flags} -; -DFEATURE_ARDUINO_OTA - -DPLUGIN_SET_TEST_E_ESP32 - -DUSES_P096 - -DTESTING_USE_RTTTL -board = lolin_d32_pro -extra_scripts = ${esp32_common.extra_scripts} - -[env:test_A_ESP32_IRExt_4M316k] -extends = env:test_A_ESP32_4M316k -lib_ignore = AS_BH1750, ${esp32_always.lib_ignore}, ESP32_ping -build_flags = ${esp32_common.build_flags} - -DPLUGIN_BUILD_NORMAL_IRext - -DTESTING_USE_RTTTL - -[env:test_B_ESP32_IRExt_4M316k] -extends = env:test_B_ESP32_4M316k -lib_ignore = AS_BH1750, ${esp32_always.lib_ignore}, ESP32_ping -build_flags = ${esp32_common.build_flags} - -DPLUGIN_BUILD_NORMAL_IRext - -DTESTING_USE_RTTTL - -[env:test_C_ESP32_IRExt_4M316k] -extends = env:test_C_ESP32_4M316k -lib_ignore = AS_BH1750, ${esp32_always.lib_ignore}, ESP32_ping -build_flags = ${esp32_common.build_flags} - -DPLUGIN_BUILD_NORMAL_IRext - -DTESTING_USE_RTTTL - -[env:test_D_ESP32_IRExt_4M316k] -extends = env:test_D_ESP32_4M316k -lib_ignore = AS_BH1750, ${esp32_always.lib_ignore}, ESP32_ping -build_flags = ${esp32_common.build_flags} - -DPLUGIN_BUILD_NORMAL_IRext - -DTESTING_USE_RTTTL - -[env:test_E_ESP32_IRExt_4M316k] -extends = env:test_E_ESP32_4M316k -lib_ignore = AS_BH1750, ${esp32_always.lib_ignore}, ESP32_ping -build_flags = ${esp32_common.build_flags} - -DPLUGIN_BUILD_NORMAL_IRext - -DTESTING_USE_RTTTL - -[env:energy_ESP32_4M316k] -extends = esp32_common -lib_deps = ${esp32_common.lib_deps}, ServoESP32 -build_flags = ${esp32_common.build_flags} - -DFEATURE_ARDUINO_OTA - -D PLUGIN_ENERGY_COLLECTION - -[env:display_ESP32_4M316k] -extends = esp32_common -lib_deps = ${esp32_common.lib_deps}, ServoESP32 -build_flags = ${esp32_common.build_flags} - -DFEATURE_ARDUINO_OTA - -D PLUGIN_DISPLAY_COLLECTION - - -; Custom: 4096k version -------------------------- -[env:custom_ESP32_4M316k_ETH] -extends = env:custom_ESP32_4M316k -platform = ${env:custom_ESP32_4M316k.platform} -build_flags = ${env:custom_ESP32_4M316k.build_flags} -DHAS_ETHERNET - -[env:normal_ESP32_4M316k_ETH] -extends = env:normal_ESP32_4M316k -platform = ${env:normal_ESP32_4M316k.platform} -build_flags = ${env:normal_ESP32_4M316k.build_flags} -DHAS_ETHERNET - -[env:test_A_ESP32_4M316k_ETH] -extends = env:test_A_ESP32_4M316k -platform = ${env:test_A_ESP32_4M316k.platform} -build_flags = ${env:test_A_ESP32_4M316k.build_flags} -DHAS_ETHERNET - -DTESTING_USE_RTTTL - -[env:test_B_ESP32_4M316k_ETH] -extends = env:test_B_ESP32_4M316k -platform = ${env:test_B_ESP32_4M316k.platform} -build_flags = ${env:test_B_ESP32_4M316k.build_flags} -DHAS_ETHERNET - -DTESTING_USE_RTTTL - -[env:test_C_ESP32_4M316k_ETH] -extends = env:test_C_ESP32_4M316k -platform = ${env:test_C_ESP32_4M316k.platform} -build_flags = ${env:test_C_ESP32_4M316k.build_flags} -DHAS_ETHERNET - -DTESTING_USE_RTTTL - -[env:test_D_ESP32_4M316k_ETH] -extends = env:test_D_ESP32_4M316k -platform = ${env:test_D_ESP32_4M316k.platform} -build_flags = ${env:test_D_ESP32_4M316k.build_flags} -DHAS_ETHERNET - -DTESTING_USE_RTTTL - -[env:test_E_ESP32_4M316k_ETH] -extends = env:test_E_ESP32_4M316k -platform = ${env:test_E_ESP32_4M316k.platform} -build_flags = ${env:test_E_ESP32_4M316k.build_flags} -DHAS_ETHERNET - -DTESTING_USE_RTTTL - - -;;; ESP32-s2 *********************************************************** - -[env:normal_ESP32s2_4M316k] -extends = esp32s2_common -lib_deps = ${esp32s2_common.lib_deps}, ServoESP32 -build_flags = ${esp32s2_common.build_flags} - -DFEATURE_ARDUINO_OTA - -[env:test_A_ESP32s2_4M316k] -extends = esp32s2_common -build_flags = ${esp32s2_common.build_flags} - -DFEATURE_ARDUINO_OTA - -DPLUGIN_SET_TEST_ESP32 - -DTESTING_USE_RTTTL - -[env:test_B_ESP32s2_4M316k] -extends = esp32s2_common -build_flags = ${esp32s2_common.build_flags} - -DFEATURE_ARDUINO_OTA - -DPLUGIN_SET_TEST_B_ESP32 - -DTESTING_USE_RTTTL - -[env:test_C_ESP32s2_4M316k] -extends = esp32s2_common -build_flags = ${esp32s2_common.build_flags} - -DFEATURE_ARDUINO_OTA - -DPLUGIN_SET_TEST_C_ESP32 - -DTESTING_USE_RTTTL - -[env:test_D_ESP32s2_4M316k] -extends = esp32s2_common -build_flags = ${esp32s2_common.build_flags} - -DFEATURE_ARDUINO_OTA - -DPLUGIN_SET_TEST_D_ESP32 - -DTESTING_USE_RTTTL - - -[env:energy_ESP32s2_4M316k] -extends = esp32s2_common -lib_deps = ${esp32s2_common.lib_deps}, ServoESP32 -build_flags = ${esp32s2_common.build_flags} - -DFEATURE_ARDUINO_OTA - -D PLUGIN_ENERGY_COLLECTION + -DESP32S2 + -DBOARD_HAS_PSRAM + -DESP32_ENABLE_PSRAM + -DUSE_LITTLEFS +lib_deps = ${esp32_common.lib_deps}, LittleFS +board_build.filesystem = littlefs -[env:display_ESP32s2_4M316k] -extends = esp32s2_common -lib_deps = ${esp32s2_common.lib_deps}, ServoESP32 -build_flags = ${esp32s2_common.build_flags} - -DFEATURE_ARDUINO_OTA - -D PLUGIN_DISPLAY_COLLECTION -; A Lolin D32 PRO with 16MB Flash, allowing 4MB sketch size, and file storage using the default (SPIFFS) filesystem -[max_ESP32_16M] -extends = esp32_common -lib_ignore = ${esp32_always.lib_ignore}, ESP32_ping -lib_deps = ${esp32_common.lib_deps}, VL53L0X -board_upload.maximum_size = 4194304 -build_flags = ${esp32_common.build_flags} - -DFEATURE_ARDUINO_OTA - -DPLUGIN_BUILD_MAX_ESP32 - -DPLUGIN_BUILD_IR_EXTENDED -; TODO: To enable PS-RAM Support needs more build flags than these 2, for now define ESP32_ENABLE_PSRAM is used to en/disable detecting PS-Ram size on Info page -; -DBOARD_HAS_PSRAM // both flags already enabled for Lolin D32 Pro board by PlatformIO -; -mfix-esp32-psram-cache-issue -;board = esp32_16M -board = lolin_d32_pro +[env:normal_ESP32_4M316k_LittleFS] +extends = esp32_common_LittleFS +board = esp32_4M +lib_deps = ${esp32_common_LittleFS.lib_deps}, ServoESP32 -; A Lolin D32 PRO with 16MB Flash, allowing 4MB sketch size, and file storage using LittleFS filesystem -[max_ESP32_16M_LittleFS] -extends = esp32_LittleFS -lib_ignore = ${esp32_always.lib_ignore}, ESP32_ping -lib_deps = ${esp32_LittleFS.lib_deps}, VL53L0X -board_upload.maximum_size = 4194304 -build_flags = ${esp32_LittleFS.build_flags} - -DFEATURE_ARDUINO_OTA - -DPLUGIN_BUILD_MAX_ESP32 - -DPLUGIN_BUILD_IR_EXTENDED -; TODO: To enable PS-RAM Support needs more build flags than these 2, for now define ESP32_ENABLE_PSRAM is used to en/disable detecting PS-Ram size on Info page -; -DBOARD_HAS_PSRAM // both flags already enabled for Lolin D32 Pro board by PlatformIO -; -mfix-esp32-psram-cache-issue +[env:normal_ESP32_16M8M_LittleFS] +extends = esp32_common_LittleFS board = esp32_16M +lib_deps = ${esp32_common_LittleFS.lib_deps}, ServoESP32 -[max_ESP32s2_16M] -extends = esp32s2_common -lib_ignore = ${esp32_always.lib_ignore}, ESP32_ping -lib_deps = ${esp32s2_common.lib_deps}, VL53L0X -board_upload.maximum_size = 4194304 -build_flags = ${esp32s2_common.build_flags} - -DFEATURE_ARDUINO_OTA - -DPLUGIN_BUILD_MAX_ESP32 - -DPLUGIN_BUILD_IR_EXTENDED - -; A Lolin D32 PRO with 16MB Flash, allowing 4MB sketch size, and file storage using LittleFS filesystem -[max_ESP32s2_16M_LittleFS] -extends = esp32s2_LittleFS -lib_ignore = ${esp32_always.lib_ignore}, ESP32_ping -lib_deps = ${esp32s2_LittleFS.lib_deps}, VL53L0X -board_upload.maximum_size = 4194304 -build_flags = ${esp32s2_LittleFS.build_flags} - -DFEATURE_ARDUINO_OTA - -DPLUGIN_BUILD_MAX_ESP32 - -DPLUGIN_BUILD_IR_EXTENDED - -; A Lolin D32 PRO with 16MB Flash, allowing 4MB sketch size, and 1MB assigned to file storage using SPIFFS filesystem -[env:max_ESP32_16M1M] -extends = max_ESP32_16M -board_build.partitions = esp32_partition_app4096k_spiffs1024k.csv -; A Lolin D32 PRO with 16MB Flash, allowing 4MB sketch size, and 2MB assigned to file storage using LittleFS filesystem -[env:max_ESP32_16M2M_LittleFS] -extends = max_ESP32_16M_LittleFS -board_build.partitions = esp32_partition_app4096k_spiffs2048k.csv - -; A Lolin D32 PRO with 16MB Flash, allowing 4MB sketch size, and the rest (nearly 8MB) assigned to file storage using LittleFS filesystem -[env:max_ESP32_16M8M_LittleFS] -extends = max_ESP32_16M_LittleFS -board_build.partitions = esp32_partition_app4096k_spiffs8124k.csv - -; If you have a board with Ethernet integrated and 16MB Flash, then this configuration could be enabled, it's based on the max_ESP32_16M1M definition -[env:max_ESP32_16M1M_ETH] -extends = env:max_ESP32_16M1M -build_flags = ${env:max_ESP32_16M1M.build_flags} -DHAS_ETHERNET - -; If you have a board with Ethernet integrated and 16MB Flash, then this configuration could be enabled, it's based on the max_ESP32_16M2M_LittleFS definition -[env:max_ESP32_16M2M_LittleFS_ETH] -extends = env:max_ESP32_16M2M_LittleFS -build_flags = ${env:max_ESP32_16M2M_LittleFS.build_flags} -DHAS_ETHERNET - -; If you have a board with Ethernet integrated and 16MB Flash, then this configuration could be enabled, it's based on the max_ESP32_16M8M_LittleFS definition -[env:max_ESP32_16M8M_LittleFS_ETH] -extends = env:max_ESP32_16M8M_LittleFS -build_flags = ${env:max_ESP32_16M8M_LittleFS.build_flags} -DHAS_ETHERNET - -[env:max_ESP32s2_16M8M_LittleFS] -extends = max_ESP32s2_16M_LittleFS -board_build.partitions = esp32_partition_app4096k_spiffs8124k.csv +; ESP32-S2 +[env:normal_ESP32s2_4M316k_LittleFS] +extends = esp32s2_common_LittleFS +board = esp32s2 diff --git a/platformio_esp82xx_base.ini b/platformio_esp82xx_base.ini index a2c2899abe..536ef1fb1f 100644 --- a/platformio_esp82xx_base.ini +++ b/platformio_esp82xx_base.ini @@ -45,13 +45,11 @@ lib_ignore = ${no_ir.lib_ignore}, ${no_sd.lib_ignore}, ${no_littl [esp82xx_common] extends = common -board_build.f_cpu = 80000000L build_flags = ${debug_flags.build_flags} ${mqtt_flags.build_flags} -DHTTPCLIENT_1_1_COMPATIBLE=0 build_unflags = -DDEBUG_ESP_PORT -fexceptions lib_deps = td-er/ESPeasySerial @ 2.0.8, I2Cdevlib-Core, adafruit/Adafruit ILI9341 @ ^1.5.6, Adafruit GFX Library, LOLIN_EPD, adafruit/Adafruit BusIO @ ^1.10.0, bblanchon/ArduinoJson @ ^6.17.2, VL53L0X @ 1.3.0, SparkFun VL53L1X 4m Laser Distance Sensor @ 1.2.9, td-er/RABurton ESP8266 Mutex @ ^1.0.2, td-er/SparkFun MAX1704x Fuel Gauge Arduino Library @ ^1.0.1, ESP8266HTTPUpdateServer, FrogmoreScd30, Multi Channel Relay Arduino Library, SparkFun ADXL345 Arduino Library, ITG3205 lib_ignore = ${esp82xx_defaults.lib_ignore}, IRremoteESP8266, HeatpumpIR, LittleFS(esp8266), ServoESP32, TinyWireM -board = esp12e monitor_filters = esp8266_exception_decoder @@ -149,9 +147,6 @@ build_flags = ${ir.build_flags} -DPLUGIN_BUILD_IR_EXTENDED_NO_RX ; ********************************************************************* [esp82xx_1M] extends = esp82xx_common -board_build.flash_mode = dout -board_upload.maximum_size = 786432 -board_build.ldscript = eagle.flash.1m128.ld build_flags = -DSIZE_1M -DBUILD_NO_DEBUG -Os @@ -160,11 +155,11 @@ build_flags = -DSIZE_1M [esp8266_1M] extends = esp82xx_1M -board = esp01_1m +board = esp8266_1M128k [esp8285_1M] extends = esp82xx_1M -board = esp8285 +board = esp8285_1M128k build_flags = ${esp8266_1M.build_flags} -DESP8285 @@ -173,18 +168,16 @@ build_flags = ${esp8266_1M.build_flags} -DESP8285 ; ********************************************************************* [esp82xx_1M_OTA] extends = esp82xx_1M -board_build.flash_mode = dout -board_upload.maximum_size = 616448 build_flags = ${esp82xx_1M.build_flags} -DPLUGIN_BUILD_MINIMAL_OTA -DDISABLE_SC16IS752_Serial [esp8266_1M_OTA] extends = esp82xx_1M_OTA -board = esp01_1m +board = esp8266_1M128k_OTA [esp8285_1M_OTA] extends = esp82xx_1M_OTA -board = esp8285 +board = esp8285_1M128k_OTA build_flags = ${esp82xx_1M_OTA.build_flags} -DESP8285 @@ -194,17 +187,7 @@ build_flags = ${esp82xx_1M_OTA.build_flags} -DESP8285 [esp8266_2M256] extends = esp82xx_common -board = esp12e -board_build.flash_mode = dout -board_upload.maximum_size = 1044464 -board_build.ldscript = eagle.flash.2m256.ld - -[espWroom2M] -extends = esp82xx_common -board = esp12e -board_build.flash_mode = dout -board_upload.maximum_size = 1044464 -board_build.ldscript = eagle.flash.2m.ld +board = esp8266_2M256 [espWroom2M256] extends = esp82xx_common @@ -220,17 +203,11 @@ board_build.ldscript = eagle.flash.2m256.ld [esp8266_4M1M] extends = esp82xx_common -board = esp12e -board_build.flash_mode = dout -board_upload.maximum_size = 1044464 -board_build.ldscript = eagle.flash.4m1m.ld +board = esp8266_4M1M_board [esp8266_4M2M] extends = esp82xx_common -board = esp12e -board_build.flash_mode = dout -board_upload.maximum_size = 1044464 -board_build.ldscript = eagle.flash.4m2m.ld +board = esp8266_4M2M_board @@ -244,10 +221,7 @@ board_build.ldscript = eagle.flash.4m2m.ld ; See https://github.com/esp8266/Arduino/issues/5932 [esp8266_16M] extends = esp82xx_common -board = esp12e -board_build.flash_mode = dout -board_upload.maximum_size = 1044464 -board_build.ldscript = eagle.flash.16m14m.ld +board = esp8266_16M14M_board build_flags = -DSPIFFS_MAX_OPEN_FILES=20 ${esp82xx_common.build_flags} diff --git a/platformio_esp82xx_envs.ini b/platformio_esp82xx_envs.ini index 33f3b11b1f..fd84acc9fa 100644 --- a/platformio_esp82xx_envs.ini +++ b/platformio_esp82xx_envs.ini @@ -234,15 +234,6 @@ build_flags = ${regular_platform.build_flags} ${esp8285_1M.build_flags} -; NORMAL: 2048k WROOM02 version -------------------------- -[env:normal_WROOM02_2M] -extends = espWroom2M -platform = ${regular_platform.platform} -platform_packages = ${regular_platform.platform_packages} -build_flags = ${regular_platform.build_flags} - ${espWroom2M.build_flags} - - ; NORMAL: 2048k WROOM02 version 256k SPIFFS -------------------------- [env:normal_WROOM02_2M256] extends = espWroom2M256 @@ -443,37 +434,57 @@ build_flags = ${normal_ir_extended_no_rx.build_flags} ; Includes "normal" + "testing" plugins ; ; ********************************************************************* -[env:test_A_ESP8266_4M1M] +[testing_ESP8266_4M1M] extends = esp8266_4M1M platform = ${testing.platform} platform_packages = ${testing.platform_packages} build_flags = ${testing.build_flags} ${esp8266_4M1M.build_flags} - -DTESTING_USE_RTTTL -[env:test_B_ESP8266_4M1M] +[testing_alt_wifi_ESP8266_4M1M] extends = esp8266_4M1M -platform = ${testing.platform} -platform_packages = ${testing.platform_packages} -build_flags = ${testing.build_flags} +platform = ${testing_alt_wifi.platform} +platform_packages = ${testing_alt_wifi.platform_packages} +build_flags = ${testing_alt_wifi.build_flags} ${esp8266_4M1M.build_flags} - -DPLUGIN_BUILD_TESTING_B -[env:test_C_ESP8266_4M1M] +[testing_beta_ESP8266_4M1M] extends = esp8266_4M1M -platform = ${testing.platform} -platform_packages = ${testing.platform_packages} -build_flags = ${testing.build_flags} +platform = ${testing_beta.platform} +platform_packages = ${testing_beta.platform_packages} +build_flags = ${testing_beta.build_flags} ${esp8266_4M1M.build_flags} + +[testing_beta_ESP8266_16M_LittleFS] +extends = esp8266_16M +platform = ${testing_beta.platform} +platform_packages = ${testing_beta.platform_packages} +build_flags = ${testing_beta.build_flags} + ${esp8266_16M.build_flags} + -DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK22y +board_build.filesystem = littlefs +lib_ignore = ESP32_ping, ESP32WebServer, ServoESP32, ESP32HTTPUpdateServer, IRremoteESP8266, HeatpumpIR + + +[env:test_A_ESP8266_4M1M] +extends = testing_ESP8266_4M1M +build_flags = ${testing_ESP8266_4M1M.build_flags} + -DTESTING_USE_RTTTL + +[env:test_B_ESP8266_4M1M] +extends = testing_ESP8266_4M1M +build_flags = ${testing_ESP8266_4M1M.build_flags} + -DPLUGIN_BUILD_TESTING_B + +[env:test_C_ESP8266_4M1M] +extends = testing_ESP8266_4M1M +build_flags = ${testing_ESP8266_4M1M.build_flags} -DPLUGIN_BUILD_TESTING_C -DTESTING_USE_RTTTL [env:test_D_ESP8266_4M1M] -extends = esp8266_4M1M -platform = ${testing.platform} -platform_packages = ${testing.platform_packages} -build_flags = ${testing.build_flags} - ${esp8266_4M1M.build_flags} +extends = testing_ESP8266_4M1M +build_flags = ${testing_ESP8266_4M1M.build_flags} -DPLUGIN_BUILD_TESTING_D -DTESTING_USE_RTTTL @@ -489,39 +500,27 @@ build_flags = ${testing.build_flags} ; TEST: 4096k version + FEATURE_ADC_VCC ---------- [env:test_A_ESP8266_4M1M_VCC] -extends = esp8266_4M1M -platform = ${testing.platform} -platform_packages = ${testing.platform_packages} -build_flags = ${testing.build_flags} - ${esp8266_4M1M.build_flags} +extends = testing_ESP8266_4M1M +build_flags = ${testing_ESP8266_4M1M.build_flags} -DFEATURE_ADC_VCC=true -DTESTING_USE_RTTTL [env:test_B_ESP8266_4M1M_VCC] -extends = esp8266_4M1M -platform = ${testing.platform} -platform_packages = ${testing.platform_packages} -build_flags = ${testing.build_flags} - ${esp8266_4M1M.build_flags} +extends = testing_ESP8266_4M1M +build_flags = ${testing_ESP8266_4M1M.build_flags} -DFEATURE_ADC_VCC=true -DPLUGIN_BUILD_TESTING_B [env:test_C_ESP8266_4M1M_VCC] -extends = esp8266_4M1M -platform = ${testing.platform} -platform_packages = ${testing.platform_packages} -build_flags = ${testing.build_flags} - ${esp8266_4M1M.build_flags} +extends = testing_ESP8266_4M1M +build_flags = ${testing_ESP8266_4M1M.build_flags} -DFEATURE_ADC_VCC=true -DPLUGIN_BUILD_TESTING_C -DTESTING_USE_RTTTL [env:test_D_ESP8266_4M1M_VCC] -extends = esp8266_4M1M -platform = ${testing.platform} -platform_packages = ${testing.platform_packages} -build_flags = ${testing.build_flags} - ${esp8266_4M1M.build_flags} +extends = testing_ESP8266_4M1M +build_flags = ${testing_ESP8266_4M1M.build_flags} -DFEATURE_ADC_VCC=true -DPLUGIN_BUILD_TESTING_D -DTESTING_USE_RTTTL @@ -537,37 +536,25 @@ build_flags = ${testing.build_flags} -DTESTING_USE_RTTTL [env:test_A_alt_wifi_ESP8266_4M1M_VCC] -extends = esp8266_4M1M -platform = ${testing_alt_wifi.platform} -platform_packages = ${testing_alt_wifi.platform_packages} -build_flags = ${testing_alt_wifi.build_flags} - ${esp8266_4M1M.build_flags} +extends = testing_alt_wifi_ESP8266_4M1M +build_flags = ${testing_alt_wifi_ESP8266_4M1M.build_flags} -DFEATURE_ADC_VCC=true [env:test_B_alt_wifi_ESP8266_4M1M_VCC] -extends = esp8266_4M1M -platform = ${testing_alt_wifi.platform} -platform_packages = ${testing_alt_wifi.platform_packages} -build_flags = ${testing_alt_wifi.build_flags} - ${esp8266_4M1M.build_flags} +extends = testing_alt_wifi_ESP8266_4M1M +build_flags = ${testing_alt_wifi_ESP8266_4M1M.build_flags} -DFEATURE_ADC_VCC=true -DPLUGIN_BUILD_TESTING_B [env:test_C_alt_wifi_ESP8266_4M1M_VCC] -extends = esp8266_4M1M -platform = ${testing_alt_wifi.platform} -platform_packages = ${testing_alt_wifi.platform_packages} -build_flags = ${testing_alt_wifi.build_flags} - ${esp8266_4M1M.build_flags} +extends = testing_alt_wifi_ESP8266_4M1M +build_flags = ${testing_alt_wifi_ESP8266_4M1M.build_flags} -DFEATURE_ADC_VCC=true -DPLUGIN_BUILD_TESTING_C [env:test_D_alt_wifi_ESP8266_4M1M_VCC] -extends = esp8266_4M1M -platform = ${testing_alt_wifi.platform} -platform_packages = ${testing_alt_wifi.platform_packages} -build_flags = ${testing_alt_wifi.build_flags} - ${esp8266_4M1M.build_flags} +extends = testing_alt_wifi_ESP8266_4M1M +build_flags = ${testing_alt_wifi_ESP8266_4M1M.build_flags} -DFEATURE_ADC_VCC=true -DPLUGIN_BUILD_TESTING_D @@ -597,39 +584,27 @@ build_flags = ${testing_alt_wifi.build_flags} [env:test_A_beta_ESP8266_4M1M] -extends = esp8266_4M1M -platform = ${testing_beta.platform} -platform_packages = ${testing_beta.platform_packages} -build_flags = ${testing_beta.build_flags} - ${esp8266_4M1M.build_flags} +extends = testing_beta_ESP8266_4M1M +build_flags = ${testing_beta_ESP8266_4M1M.build_flags} -DLIMIT_BUILD_SIZE -DTESTING_USE_RTTTL [env:test_B_beta_ESP8266_4M1M] -extends = esp8266_4M1M -platform = ${testing_beta.platform} -platform_packages = ${testing_beta.platform_packages} -build_flags = ${testing_beta.build_flags} - ${esp8266_4M1M.build_flags} +extends = testing_beta_ESP8266_4M1M +build_flags = ${testing_beta_ESP8266_4M1M.build_flags} -DLIMIT_BUILD_SIZE -DPLUGIN_BUILD_TESTING_B [env:test_C_beta_ESP8266_4M1M] -extends = esp8266_4M1M -platform = ${testing_beta.platform} -platform_packages = ${testing_beta.platform_packages} -build_flags = ${testing_beta.build_flags} - ${esp8266_4M1M.build_flags} +extends = testing_beta_ESP8266_4M1M +build_flags = ${testing_beta_ESP8266_4M1M.build_flags} -DLIMIT_BUILD_SIZE -DPLUGIN_BUILD_TESTING_C -DTESTING_USE_RTTTL [env:test_D_beta_ESP8266_4M1M] -extends = esp8266_4M1M -platform = ${testing_beta.platform} -platform_packages = ${testing_beta.platform_packages} -build_flags = ${testing_beta.build_flags} - ${esp8266_4M1M.build_flags} +extends = testing_beta_ESP8266_4M1M +build_flags = ${testing_beta_ESP8266_4M1M.build_flags} -DLIMIT_BUILD_SIZE -DPLUGIN_BUILD_TESTING_D -DTESTING_USE_RTTTL @@ -647,45 +622,25 @@ build_flags = ${testing_beta.build_flags} ; Test: 16M version -- LittleFS -------------- ; LittleFS is determined by using "LittleFS" in the pio env name [env:test_A_beta_ESP8266_16M_LittleFS] -extends = esp8266_16M -platform = ${testing_beta.platform} -platform_packages = ${testing_beta.platform_packages} -build_flags = ${testing_beta.build_flags} - ${esp8266_16M.build_flags} - -DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK22y -lib_ignore = ESP32_ping, ESP32WebServer, ServoESP32, ESP32HTTPUpdateServer, IRremoteESP8266, HeatpumpIR +extends = testing_beta_ESP8266_16M_LittleFS +build_flags = ${testing_beta_ESP8266_16M_LittleFS.build_flags} [env:test_B_beta_ESP8266_16M_LittleFS] -extends = esp8266_16M -platform = ${testing_beta.platform} -platform_packages = ${testing_beta.platform_packages} -build_flags = ${testing_beta.build_flags} - ${esp8266_16M.build_flags} +extends = testing_beta_ESP8266_16M_LittleFS +build_flags = ${testing_beta_ESP8266_16M_LittleFS.build_flags} -DPLUGIN_BUILD_TESTING_B - -DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK22y -lib_ignore = ESP32_ping, ESP32WebServer, ServoESP32, ESP32HTTPUpdateServer, IRremoteESP8266, HeatpumpIR [env:test_C_beta_ESP8266_16M_LittleFS] -extends = esp8266_16M -platform = ${testing_beta.platform} -platform_packages = ${testing_beta.platform_packages} -build_flags = ${testing_beta.build_flags} - ${esp8266_16M.build_flags} - -DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK22y +extends = testing_beta_ESP8266_16M_LittleFS +build_flags = ${testing_beta_ESP8266_16M_LittleFS.build_flags} -DPLUGIN_BUILD_TESTING_C -DTESTING_USE_RTTTL -lib_ignore = ESP32_ping, ESP32WebServer, ServoESP32, ESP32HTTPUpdateServer, IRremoteESP8266, HeatpumpIR [env:test_D_beta_ESP8266_16M_LittleFS] -extends = esp8266_16M -platform = ${testing_beta.platform} -platform_packages = ${testing_beta.platform_packages} -build_flags = ${testing_beta.build_flags} - ${esp8266_16M.build_flags} - -DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK22y +extends = testing_beta_ESP8266_16M_LittleFS +build_flags = ${testing_beta_ESP8266_16M_LittleFS.build_flags} -DPLUGIN_BUILD_TESTING_D -DTESTING_USE_RTTTL -lib_ignore = ESP32_ping, ESP32WebServer, ServoESP32, ESP32HTTPUpdateServer, IRremoteESP8266, HeatpumpIR [env:test_E_beta_ESP8266_16M_LittleFS] extends = esp8266_16M diff --git a/src/ESPEasy_common.h b/src/ESPEasy_common.h index f6705e67c3..dce90ef0d2 100644 --- a/src/ESPEasy_common.h +++ b/src/ESPEasy_common.h @@ -140,24 +140,8 @@ namespace std #endif #include // Needed to call ESP-IDF functions like esp_wifi_.... - #ifdef PLUGIN_BUILD_MAX_ESP32 - #define MAX_SKETCH_SIZE 4194304 // 0x400000 look at partitions in csv file - #else // PLUGIN_BUILD_MAX_ESP32 - #define MAX_SKETCH_SIZE 1900544 // 0x1d0000 look at partitions in csv file - #endif // PLUGIN_BUILD_MAX_ESP32 #endif -#include -#include -#include -#include -#ifdef FEATURE_SD -#include -#else -using namespace fs; -#endif -#include - #ifdef USE_LITTLEFS #ifdef ESP32 @@ -179,6 +163,19 @@ using namespace fs; #define ESPEASY_FS SPIFFS #endif + +#include +#include +#include +#include +#ifdef FEATURE_SD +#include +#else +using namespace fs; +#endif +#include + + // Include custom first, then build info. (one may want to set BUILD_GIT for example) #include "src/CustomBuild/ESPEasy_buildinfo.h" #include "src/CustomBuild/ESPEasyLimits.h" diff --git a/src/src/ESPEasyCore/ESPEasyRules.cpp b/src/src/ESPEasyCore/ESPEasyRules.cpp index 1281a5b275..01d70b2fae 100644 --- a/src/src/ESPEasyCore/ESPEasyRules.cpp +++ b/src/src/ESPEasyCore/ESPEasyRules.cpp @@ -522,7 +522,8 @@ bool parse_bitwise_functions(const String& cmd_s_lower, const String& arg1, cons } bool parse_math_functions(const String& cmd_s_lower, const String& arg1, const String& arg2, const String& arg3, double& result) { - double farg1, farg2, farg3 = 0.0f; + double farg1; + float farg2, farg3 = 0.0f; if (!validDoubleFromString(arg1, farg1)) { return false; @@ -531,9 +532,9 @@ bool parse_math_functions(const String& cmd_s_lower, const String& arg1, const S if (cmd_s_lower.equals(F("constrain"))) { // Contrain a value X to be within range of A to B // Syntax like {constrain:x:a:b} to constrain x in range a...b - if (validDoubleFromString(arg2, farg2) && validDoubleFromString(arg3, farg3)) { + if (validFloatFromString(arg2, farg2) && validFloatFromString(arg3, farg3)) { if (farg2 > farg3) { - const double tmp = farg2; + const float tmp = farg2; farg2 = farg3; farg3 = tmp; } diff --git a/src/src/Helpers/ESPEasy_Storage.cpp b/src/src/Helpers/ESPEasy_Storage.cpp index 25953ff8e4..83e44160df 100644 --- a/src/src/Helpers/ESPEasy_Storage.cpp +++ b/src/src/Helpers/ESPEasy_Storage.cpp @@ -372,10 +372,8 @@ void fileSystemCheck() bool FS_format() { #ifdef USE_LITTLEFS #ifdef ESP32 - disableCore1WDT(); const bool res = ESPEASY_FS.begin(true); ESPEASY_FS.end(); - enableCore1WDT(); return res; #else return ESPEASY_FS.format(); diff --git a/src/src/Helpers/Hardware.cpp b/src/src/Helpers/Hardware.cpp index a10bc7dc46..7bb954ae5d 100644 --- a/src/src/Helpers/Hardware.cpp +++ b/src/src/Helpers/Hardware.cpp @@ -485,7 +485,12 @@ uint32_t getFlashChipId() { static uint32_t flashChipId = 0; if (flashChipId == 0) { #ifdef ESP32 - flashChipId = g_rom_flashchip.device_id; + uint32_t tmp = g_rom_flashchip.device_id; + for (int i = 0; i < 3; ++i) { + flashChipId = flashChipId << 8; + flashChipId |= (tmp & 0xFF); + tmp = tmp >> 8; + } // esp_flash_read_id(nullptr, &flashChipId); #elif defined(ESP8266) flashChipId = ESP.getFlashChipId(); @@ -499,7 +504,7 @@ uint32_t getFlashRealSizeInBytes() { static uint32_t res = 0; if (res == 0) { #if defined(ESP32) - res = ESP.getFlashChipSize(); + res = (1 << ((getFlashChipId() >> 16) & 0xFF)); #else // if defined(ESP32) res = ESP.getFlashChipRealSize(); // ESP.getFlashChipSize(); #endif // if defined(ESP32) @@ -507,6 +512,9 @@ uint32_t getFlashRealSizeInBytes() { return res; } +uint32_t getFlashChipSpeed() { + return ESP.getFlashChipSpeed(); +} bool puyaSupport() { bool supported = false; diff --git a/src/src/Helpers/Hardware.h b/src/src/Helpers/Hardware.h index 6cbfe96865..399bbae051 100644 --- a/src/src/Helpers/Hardware.h +++ b/src/src/Helpers/Hardware.h @@ -67,6 +67,8 @@ uint32_t getFlashChipId(); uint32_t getFlashRealSizeInBytes(); +uint32_t getFlashChipSpeed(); + bool puyaSupport(); uint8_t getFlashChipVendorId(); diff --git a/src/src/Helpers/OTA.cpp b/src/src/Helpers/OTA.cpp index 5ef2797e03..955c17ef09 100644 --- a/src/src/Helpers/OTA.cpp +++ b/src/src/Helpers/OTA.cpp @@ -30,7 +30,9 @@ bool OTA_possible(uint32_t& maxSketchSize, bool& use2step) { if (maxSketchSize > MAX_SKETCH_SIZE) { maxSketchSize = MAX_SKETCH_SIZE; } return otaPossible; #elif defined(ESP32) - maxSketchSize = MAX_SKETCH_SIZE; + // ESP32 writes an OTA image to the "other" app partition. + // Thus what is reported as "free" sketch space is the size of the not used app partition. + maxSketchSize = getFreeSketchSpace(); use2step = false; return true; #else // if defined(ESP8266) diff --git a/src/src/Helpers/StringProvider.cpp b/src/src/Helpers/StringProvider.cpp index 851d46c798..1ffe77a92b 100644 --- a/src/src/Helpers/StringProvider.cpp +++ b/src/src/Helpers/StringProvider.cpp @@ -174,6 +174,7 @@ const __FlashStringHelper * getLabel(LabelType::Enum label) { case LabelType::FLASH_CHIP_ID: return F("Flash Chip ID"); case LabelType::FLASH_CHIP_REAL_SIZE: return F("Flash Chip Real Size"); + case LabelType::FLASH_CHIP_SPEED: return F("Flash Chip Speed"); case LabelType::FLASH_IDE_SIZE: return F("Flash IDE Size"); case LabelType::FLASH_IDE_SPEED: return F("Flash IDE Speed"); case LabelType::FLASH_IDE_MODE: return F("Flash IDE Mode"); @@ -372,6 +373,7 @@ String getValue(LabelType::Enum label) { case LabelType::FLASH_CHIP_ID: break; case LabelType::FLASH_CHIP_REAL_SIZE: break; + case LabelType::FLASH_CHIP_SPEED: return String(getFlashChipSpeed()); case LabelType::FLASH_IDE_SIZE: break; case LabelType::FLASH_IDE_SPEED: break; case LabelType::FLASH_IDE_MODE: break; diff --git a/src/src/Helpers/StringProvider.h b/src/src/Helpers/StringProvider.h index 4082277525..7f3a6a1750 100644 --- a/src/src/Helpers/StringProvider.h +++ b/src/src/Helpers/StringProvider.h @@ -141,6 +141,7 @@ struct LabelType { FLASH_CHIP_ID, FLASH_CHIP_REAL_SIZE, + FLASH_CHIP_SPEED, FLASH_IDE_SIZE, FLASH_IDE_SPEED, FLASH_IDE_MODE, diff --git a/src/src/WebServer/CustomPage.cpp b/src/src/WebServer/CustomPage.cpp index 4f77ed7c8e..590a7aa403 100644 --- a/src/src/WebServer/CustomPage.cpp +++ b/src/src/WebServer/CustomPage.cpp @@ -146,9 +146,9 @@ bool handle_custom(const String& path) { chunksize = available; } uint8_t buf[64] = {0}; - const size_t read = dataFile.read(buf, chunksize); + const int read = dataFile.read(buf, chunksize); if (read == chunksize) { - for (uint32_t i = 0; i < chunksize; ++i) { + for (int32_t i = 0; i < chunksize; ++i) { const char c = (char)buf[i]; line += c; if (c == '\n') { diff --git a/src/src/WebServer/FileList.cpp b/src/src/WebServer/FileList.cpp index 0ca451cfc8..9add083305 100644 --- a/src/src/WebServer/FileList.cpp +++ b/src/src/WebServer/FileList.cpp @@ -11,6 +11,8 @@ #include "../Helpers/ESPEasy_Storage.h" #include "../Helpers/Numerical.h" +#include "../../ESPEasy_common.h" + #ifdef USES_C016 diff --git a/src/src/WebServer/LoadFromFS.cpp b/src/src/WebServer/LoadFromFS.cpp index e10e71de59..4a2a5c907c 100644 --- a/src/src/WebServer/LoadFromFS.cpp +++ b/src/src/WebServer/LoadFromFS.cpp @@ -160,9 +160,9 @@ size_t streamFromFS(String path, bool htmlEscape) { chunksize = available; } uint8_t buf[64] = {0}; - const size_t read = f.read(buf, chunksize); + const int read = f.read(buf, chunksize); if (read == chunksize) { - for (uint32_t i = 0; i < chunksize; ++i) { + for (int32_t i = 0; i < chunksize; ++i) { const char c = (char)buf[i]; if (htmlEscape && htmlEscapeChar(c, escaped)) { addHtml(escaped); diff --git a/src/src/WebServer/SysInfoPage.cpp b/src/src/WebServer/SysInfoPage.cpp index ff40c04608..5c5fc440fd 100644 --- a/src/src/WebServer/SysInfoPage.cpp +++ b/src/src/WebServer/SysInfoPage.cpp @@ -189,7 +189,6 @@ void handle_sysinfo_json() { json_number(F("ide_size"), String(ESP.getFlashChipSize() / 1024)); // Please check what is supported for the ESP32 - # if defined(ESP8266) json_number(F("flash_speed"), String(ESP.getFlashChipSpeed() / 1000000)); FlashMode_t ideMode = ESP.getFlashChipMode(); @@ -202,7 +201,6 @@ void handle_sysinfo_json() { default: json_prop(F("mode"), getUnknownString()); break; } - # endif // if defined(ESP8266) json_number(F("writes"), String(RTC.flashDayCounter)); json_number(F("flash_counter"), String(RTC.flashCounter)); @@ -631,6 +629,10 @@ void handle_sysinfo_Storage() { addHtmlInt(ideSize / 1024); addHtml(F(" kB")); + addRowLabel(LabelType::FLASH_CHIP_SPEED); + addHtmlInt(getFlashChipSpeed() / 1000000); + addHtml(F(" MHz")); + // Please check what is supported for the ESP32 # if defined(ESP8266) addRowLabel(LabelType::FLASH_IDE_SPEED); From 79c31a41cee75910848f70bfed5384e4570b5cf6 Mon Sep 17 00:00:00 2001 From: TD-er Date: Tue, 4 Jan 2022 00:31:32 +0100 Subject: [PATCH 092/147] [IDF4.4 + 16M] Restore missing ESP32 builds Somehow the max 16M8M builds do end up again in boot loops.... --- boards/esp32_16M1M.json | 38 +++ boards/{esp32_16M.json => esp32_16M8M.json} | 0 platformio_esp32_envs.ini | 306 +++++++++++++++++++- 3 files changed, 341 insertions(+), 3 deletions(-) create mode 100644 boards/esp32_16M1M.json rename boards/{esp32_16M.json => esp32_16M8M.json} (100%) diff --git a/boards/esp32_16M1M.json b/boards/esp32_16M1M.json new file mode 100644 index 0000000000..2d483c3ded --- /dev/null +++ b/boards/esp32_16M1M.json @@ -0,0 +1,38 @@ +{ + "build": { + "arduino":{ + "ldscript": "esp32_out.ld" + }, + "core": "esp32", + "extra_flags": "-DARDUINO_ESP32_DEV -DBOARD_HAS_PSRAM -DARDUINO_USB_CDC_ON_BOOT=0 -DESP32_16M", + "f_cpu": "80000000L", + "f_flash": "40000000L", + "flash_mode": "dout", + "mcu": "esp32", + "variant": "esp32", + "partitions": "esp32_partition_app4096k_spiffs1024k.csv" + }, + "connectivity": [ + "wifi", + "bluetooth", + "ethernet", + "can" + ], + "debug": { + "openocd_target": "esp32.cfg" + }, + "frameworks": [ + "arduino", + "espidf" + ], + "name": "Espressif Generic ESP32 16M Flash, ESPEasy 4M Code/OTA, 1M FS", + "upload": { + "flash_size": "16MB", + "maximum_ram_size": 327680, + "maximum_size": 4194304, + "require_upload_port": true, + "speed": 460800 + }, + "url": "https://en.wikipedia.org/wiki/ESP32", + "vendor": "Espressif" +} diff --git a/boards/esp32_16M.json b/boards/esp32_16M8M.json similarity index 100% rename from boards/esp32_16M.json rename to boards/esp32_16M8M.json diff --git a/platformio_esp32_envs.ini b/platformio_esp32_envs.ini index 8339cfec6f..fd4c4711d0 100644 --- a/platformio_esp32_envs.ini +++ b/platformio_esp32_envs.ini @@ -31,17 +31,89 @@ lib_deps = ${esp32_common.lib_deps}, LittleFS board_build.filesystem = littlefs -[esp32s2_common_LittleFS] +[esp32s2_common] extends = esp32_common build_flags = ${esp32_common.build_flags} -DESP32S2 -DBOARD_HAS_PSRAM -DESP32_ENABLE_PSRAM + -DFEATURE_ARDUINO_OTA + +[esp32s2_common_LittleFS] +extends = esp32s2_common +build_flags = ${esp32s2_common.build_flags} -DUSE_LITTLEFS -lib_deps = ${esp32_common.lib_deps}, LittleFS +lib_deps = ${esp32s2_common.lib_deps}, LittleFS board_build.filesystem = littlefs + + +; Custom: 4096k version -------------------------- +[env:custom_ESP32_4M316k] +extends = esp32_common +board = esp32_4M +build_flags = ${esp32_common.build_flags} + -DPLUGIN_BUILD_CUSTOM +extra_scripts = ${esp32_common.extra_scripts} + pre:tools/pio/pre_custom_esp32.py + +[env:custom_ESP32_4M316k_LittleFS] +extends = esp32_common_LittleFS +board = esp32_4M +build_flags = ${esp32_common_LittleFS.build_flags} + -DPLUGIN_BUILD_CUSTOM +extra_scripts = ${esp32_common_LittleFS.extra_scripts} + pre:tools/pio/pre_custom_esp32.py + +[env:custom_ESP32_16M8M_LittleFS] +extends = esp32_common_LittleFS +board = esp32_16M8M +build_flags = ${esp32_common_LittleFS.build_flags} + -DPLUGIN_BUILD_CUSTOM +extra_scripts = ${esp32_common_LittleFS.extra_scripts} + pre:tools/pio/pre_custom_esp32.py + +[env:custom_IR_ESP32_4M316k] +extends = esp32_common +board = esp32_4M +build_flags = ${esp32_common.build_flags} + -DPLUGIN_BUILD_CUSTOM + -DPLUGIN_BUILD_IR +lib_ignore = ${esp32_always.lib_ignore}, ESP32_ping, HeatpumpIR +extra_scripts = ${esp32_common.extra_scripts} + pre:tools/pio/pre_custom_esp32.py + + +; ESP32-S2 +[env:custom_ESP32s2_4M316k] +extends = esp32s2_common +board = esp32s2 +build_flags = ${esp32s2_common.build_flags} + -DPLUGIN_BUILD_CUSTOM +extra_scripts = ${esp32s2_common.extra_scripts} + pre:tools/pio/pre_custom_esp32.py + + +[env:custom_IR_ESP32s2_4M316k] +extends = esp32s2_common +board = esp32s2 +build_flags = ${esp32s2_common.build_flags} + -DPLUGIN_BUILD_CUSTOM + -DPLUGIN_BUILD_IR +lib_ignore = ${esp32_always.lib_ignore}, ESP32_ping, HeatpumpIR +extra_scripts = ${esp32s2_common.extra_scripts} + pre:tools/pio/pre_custom_esp32.py + + + +; Normal: 4096k version -------------------------- +[env:normal_ESP32_4M316k] +extends = esp32_common +board = esp32_4M +lib_deps = ${esp32_common.lib_deps}, ServoESP32 + + [env:normal_ESP32_4M316k_LittleFS] extends = esp32_common_LittleFS board = esp32_4M @@ -50,7 +122,7 @@ lib_deps = ${esp32_common_LittleFS.lib_deps}, ServoESP32 [env:normal_ESP32_16M8M_LittleFS] extends = esp32_common_LittleFS -board = esp32_16M +board = esp32_16M8M lib_deps = ${esp32_common_LittleFS.lib_deps}, ServoESP32 @@ -59,3 +131,231 @@ lib_deps = ${esp32_common_LittleFS.lib_deps}, ServoESP32 extends = esp32s2_common_LittleFS board = esp32s2 + + + +; Test A....E builds -------------------------- +[env:test_A_ESP32_4M316k] +extends = esp32_common +board = esp32_4M +build_flags = ${esp32_common.build_flags} + -DFEATURE_ARDUINO_OTA + -DPLUGIN_SET_TEST_ESP32 + -DTESTING_USE_RTTTL + +[env:test_B_ESP32_4M316k] +extends = esp32_common +board = esp32_4M +build_flags = ${esp32_common.build_flags} + -DFEATURE_ARDUINO_OTA + -DPLUGIN_SET_TEST_B_ESP32 + -DTESTING_USE_RTTTL + +[env:test_C_ESP32_4M316k] +extends = esp32_common +board = esp32_4M +build_flags = ${esp32_common.build_flags} + -DFEATURE_ARDUINO_OTA + -DPLUGIN_SET_TEST_C_ESP32 + -DTESTING_USE_RTTTL + +[env:test_D_ESP32_4M316k] +extends = esp32_common +board = esp32_4M +build_flags = ${esp32_common.build_flags} + -DFEATURE_ARDUINO_OTA + -DPLUGIN_SET_TEST_D_ESP32 + -DTESTING_USE_RTTTL + + +[env:test_A_ESP32_IRExt_4M316k] +extends = test_A_ESP32_4M316k +board = esp32_4M +lib_ignore = AS_BH1750, ${esp32_always.lib_ignore}, ESP32_ping +build_flags = ${esp32_common.build_flags} + -DPLUGIN_BUILD_NORMAL_IRext + -DTESTING_USE_RTTTL + +[env:test_B_ESP32_IRExt_4M316k] +extends = test_B_ESP32_4M316k +board = esp32_4M +lib_ignore = AS_BH1750, ${esp32_always.lib_ignore}, ESP32_ping +build_flags = ${esp32_common.build_flags} + -DPLUGIN_BUILD_NORMAL_IRext + -DTESTING_USE_RTTTL + +[env:test_C_ESP32_IRExt_4M316k] +extends = test_C_ESP32_4M316k +board = esp32_4M +lib_ignore = AS_BH1750, ${esp32_always.lib_ignore}, ESP32_ping +build_flags = ${esp32_common.build_flags} + -DPLUGIN_BUILD_NORMAL_IRext + -DTESTING_USE_RTTTL + +[env:test_D_ESP32_IRExt_4M316k] +extends = test_D_ESP32_4M316k +board = esp32_4M +lib_ignore = AS_BH1750, ${esp32_always.lib_ignore}, ESP32_ping +build_flags = ${esp32_common.build_flags} + -DPLUGIN_BUILD_NORMAL_IRext + -DTESTING_USE_RTTTL + +[env:energy_ESP32_4M316k] +extends = esp32_common +board = esp32_4M +lib_deps = ${esp32_common.lib_deps}, ServoESP32 +build_flags = ${esp32_common.build_flags} + -DFEATURE_ARDUINO_OTA + -D PLUGIN_ENERGY_COLLECTION + +[env:display_ESP32_4M316k] +extends = esp32_common +board = esp32_4M +lib_deps = ${esp32_common.lib_deps}, ServoESP32 +build_flags = ${esp32_common.build_flags} + -DFEATURE_ARDUINO_OTA + + + +; ETH builds -------------------------- +[env:custom_ESP32_4M316k_ETH] +extends = custom_ESP32_4M316k +board = esp32_4M +platform = ${custom_ESP32_4M316k.platform} +build_flags = ${custom_ESP32_4M316k.build_flags} -DHAS_ETHERNET + +[env:normal_ESP32_4M316k_ETH] +extends = normal_ESP32_4M316k +board = esp32_4M +platform = ${normal_ESP32_4M316k.platform} +build_flags = ${normal_ESP32_4M316k.build_flags} -DHAS_ETHERNET + +[env:test_A_ESP32_4M316k_ETH] +extends = test_A_ESP32_4M316k +board = esp32_4M +platform = ${test_A_ESP32_4M316k.platform} +build_flags = ${test_A_ESP32_4M316k.build_flags} -DHAS_ETHERNET + -DTESTING_USE_RTTTL + +[env:test_B_ESP32_4M316k_ETH] +extends = test_B_ESP32_4M316k +board = esp32_4M +platform = ${test_B_ESP32_4M316k.platform} +build_flags = ${test_B_ESP32_4M316k.build_flags} -DHAS_ETHERNET + -DTESTING_USE_RTTTL + +[env:test_C_ESP32_4M316k_ETH] +extends = test_C_ESP32_4M316k +board = esp32_4M +platform = ${test_C_ESP32_4M316k.platform} +build_flags = ${test_C_ESP32_4M316k.build_flags} -DHAS_ETHERNET + -DTESTING_USE_RTTTL + +[env:test_D_ESP32_4M316k_ETH] +extends = test_D_ESP32_4M316k +board = esp32_4M +platform = ${test_D_ESP32_4M316k.platform} +build_flags = ${test_D_ESP32_4M316k.build_flags} -DHAS_ETHERNET + -DTESTING_USE_RTTTL + + + +;;; ESP32-s2 *********************************************************** + +[env:normal_ESP32s2_4M316k] +extends = esp32s2_common +board = esp32s2 +lib_deps = ${esp32s2_common.lib_deps}, ServoESP32 + +[env:test_A_ESP32s2_4M316k] +extends = esp32s2_common +board = esp32s2 +build_flags = ${esp32s2_common.build_flags} + -DPLUGIN_SET_TEST_ESP32 + -DTESTING_USE_RTTTL + +[env:test_B_ESP32s2_4M316k] +extends = esp32s2_common +board = esp32s2 +build_flags = ${esp32s2_common.build_flags} + -DPLUGIN_SET_TEST_B_ESP32 + -DTESTING_USE_RTTTL + +[env:test_C_ESP32s2_4M316k] +extends = esp32s2_common +board = esp32s2 +build_flags = ${esp32s2_common.build_flags} + -DPLUGIN_SET_TEST_C_ESP32 + -DTESTING_USE_RTTTL + +[env:test_D_ESP32s2_4M316k] +extends = esp32s2_common +board = esp32s2 +build_flags = ${esp32s2_common.build_flags} + -DPLUGIN_SET_TEST_D_ESP32 + -DTESTING_USE_RTTTL + + +[env:energy_ESP32s2_4M316k] +extends = esp32s2_common +board = esp32s2 +lib_deps = ${esp32s2_common.lib_deps}, ServoESP32 +build_flags = ${esp32s2_common.build_flags} + -D PLUGIN_ENERGY_COLLECTION + +[env:display_ESP32s2_4M316k] +extends = esp32s2_common +board = esp32s2 +lib_deps = ${esp32s2_common.lib_deps}, ServoESP32 +build_flags = ${esp32s2_common.build_flags} + -D PLUGIN_DISPLAY_COLLECTION + + + + + +;;; ESP32 MAX builds 16M flash ------------------------------ + +; A Lolin D32 PRO with 16MB Flash, allowing 4MB sketch size, and file storage using the default (SPIFFS) filesystem +[env:max_ESP32_16M1M] +extends = esp32_common +board = esp32_16M1M +lib_ignore = ${esp32_always.lib_ignore}, ESP32_ping +lib_deps = ${esp32_common.lib_deps}, VL53L0X +build_flags = ${esp32_common.build_flags} + -DFEATURE_ARDUINO_OTA + -DPLUGIN_BUILD_MAX_ESP32 + -DPLUGIN_BUILD_IR_EXTENDED +; TODO: To enable PS-RAM Support needs more build flags than these 2, for now define ESP32_ENABLE_PSRAM is used to en/disable detecting PS-Ram size on Info page +; -DBOARD_HAS_PSRAM // both flags already enabled for Lolin D32 Pro board by PlatformIO +; -mfix-esp32-psram-cache-issue + + +; A Lolin D32 PRO with 16MB Flash, allowing 4MB sketch size, and file storage using LittleFS filesystem +[env:max_ESP32_16M8M_LittleFS] +extends = esp32_common_LittleFS +board = esp32_16M8M +lib_ignore = ${esp32_always.lib_ignore}, ESP32_ping +lib_deps = ${esp32_common_LittleFS.lib_deps}, VL53L0X +build_flags = ${esp32_common_LittleFS.build_flags} + -DFEATURE_ARDUINO_OTA + -DPLUGIN_BUILD_MAX_ESP32 + -DPLUGIN_BUILD_IR_EXTENDED +; TODO: To enable PS-RAM Support needs more build flags than these 2, for now define ESP32_ENABLE_PSRAM is used to en/disable detecting PS-Ram size on Info page +; -DBOARD_HAS_PSRAM // both flags already enabled for Lolin D32 Pro board by PlatformIO +; -mfix-esp32-psram-cache-issue + + +; If you have a board with Ethernet integrated and 16MB Flash, then this configuration could be enabled, it's based on the max_ESP32_16M8M_LittleFS definition +[env:max_ESP32_16M8M_LittleFS_ETH] +extends = max_ESP32_16M8M_LittleFS +build_flags = ${max_ESP32_16M8M_LittleFS.build_flags} -DHAS_ETHERNET + + + + + + + + + From d01808bc09908b63dcf842ba200f45a958a40ed1 Mon Sep 17 00:00:00 2001 From: TD-er Date: Tue, 4 Jan 2022 01:20:01 +0100 Subject: [PATCH 093/147] [Cleanup] Make ESP32 PIO envs a bit more readable --- platformio_esp32_envs.ini | 105 ++++++++++++++++---------------------- 1 file changed, 45 insertions(+), 60 deletions(-) diff --git a/platformio_esp32_envs.ini b/platformio_esp32_envs.ini index fd4c4711d0..12fb4ccfd7 100644 --- a/platformio_esp32_envs.ini +++ b/platformio_esp32_envs.ini @@ -7,79 +7,82 @@ [esp32_always] lib_ignore = ESP8266WiFi, ESP8266Ping, ESP8266WebServer, ESP8266HTTPUpdateServer, ESP8266mDNS, ESPEasy_ESP8266Ping, RABurton ESP8266 Mutex, TinyWireM - -[esp32_common] +[esp32_base] extends = common, core_esp32_IDF4_4__2_0_2 lib_deps = td-er/ESPeasySerial @ 2.0.8, adafruit/Adafruit ILI9341 @ ^1.5.6, Adafruit GFX Library, LOLIN_EPD, Adafruit BusIO, VL53L0X @ 1.3.0, SparkFun VL53L1X 4m Laser Distance Sensor @ 1.2.9, td-er/SparkFun MAX1704x Fuel Gauge Arduino Library @ ^1.0.1, ArduinoOTA, ESP32HTTPUpdateServer, FrogmoreScd30, Multi Channel Relay Arduino Library -lib_ignore = ${esp32_always.lib_ignore}, ESP32_ping, IRremoteESP8266, HeatpumpIR, ESP32 BLE Arduino extra_scripts = post:tools/pio/post_esp32.py ${extra_scripts_default.extra_scripts} build_unflags = -Wall -build_flags = ${core_esp32_IDF4_4__2_0_2.build_flags} - ${mqtt_flags.build_flags} - -DCONFIG_FREERTOS_ASSERT_DISABLE - -DCONFIG_LWIP_ESP_GRATUITOUS_ARP - -fno-strict-aliasing - -DCORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_NONE +build_flags = ${core_esp32_IDF4_4__2_0_2.build_flags}, ${mqtt_flags.build_flags}, -DCONFIG_FREERTOS_ASSERT_DISABLE, -DCONFIG_LWIP_ESP_GRATUITOUS_ARP, -fno-strict-aliasing, -DCORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_NONE monitor_filters = esp32_exception_decoder + +[esp32_common] +extends = esp32_base +lib_ignore = ${esp32_always.lib_ignore}, ESP32_ping, IRremoteESP8266, HeatpumpIR, ESP32 BLE Arduino + + [esp32_common_LittleFS] extends = esp32_common -build_flags = ${esp32_common.build_flags} - -DUSE_LITTLEFS +build_flags = ${esp32_common.build_flags}, -DUSE_LITTLEFS lib_deps = ${esp32_common.lib_deps}, LittleFS board_build.filesystem = littlefs +[esp32_max_SPIFFS] +extends = esp32_base +lib_ignore = ${esp32_always.lib_ignore}, ESP32_ping +build_flags = ${esp32_base.build_flags}, -DFEATURE_ARDUINO_OTA, -DPLUGIN_BUILD_MAX_ESP32, -DPLUGIN_BUILD_IR_EXTENDED + + +[esp32_max_LittleFS] +extends = esp32_base +lib_ignore = ${esp32_always.lib_ignore}, ESP32_ping +build_flags = ${esp32_base.build_flags}, -DUSE_LITTLEFS, -DFEATURE_ARDUINO_OTA, -DPLUGIN_BUILD_MAX_ESP32, -DPLUGIN_BUILD_IR_EXTENDED +lib_deps = ${esp32_base.lib_deps}, LittleFS +board_build.filesystem = littlefs + [esp32s2_common] extends = esp32_common -build_flags = ${esp32_common.build_flags} - -DESP32S2 - -DBOARD_HAS_PSRAM - -DESP32_ENABLE_PSRAM - -DFEATURE_ARDUINO_OTA +build_flags = ${esp32_common.build_flags}, -DESP32S2, -DBOARD_HAS_PSRAM, -DESP32_ENABLE_PSRAM, -DFEATURE_ARDUINO_OTA [esp32s2_common_LittleFS] extends = esp32s2_common -build_flags = ${esp32s2_common.build_flags} - -DUSE_LITTLEFS +build_flags = ${esp32s2_common.build_flags}, -DUSE_LITTLEFS lib_deps = ${esp32s2_common.lib_deps}, LittleFS board_build.filesystem = littlefs +[esp32_custom_base] +extends = esp32_common +build_flags = ${esp32_common.build_flags}, -DPLUGIN_BUILD_CUSTOM +extra_scripts = ${esp32_common.extra_scripts} + pre:tools/pio/pre_custom_esp32.py + +[esp32_custom_base_LittleFS] +extends = esp32_common_LittleFS +build_flags = ${esp32_common.build_flags}, -DPLUGIN_BUILD_CUSTOM +extra_scripts = ${esp32_common.extra_scripts} + pre:tools/pio/pre_custom_esp32.py ; Custom: 4096k version -------------------------- [env:custom_ESP32_4M316k] -extends = esp32_common +extends = esp32_custom_base board = esp32_4M -build_flags = ${esp32_common.build_flags} - -DPLUGIN_BUILD_CUSTOM -extra_scripts = ${esp32_common.extra_scripts} - pre:tools/pio/pre_custom_esp32.py [env:custom_ESP32_4M316k_LittleFS] -extends = esp32_common_LittleFS +extends = esp32_custom_base_LittleFS board = esp32_4M -build_flags = ${esp32_common_LittleFS.build_flags} - -DPLUGIN_BUILD_CUSTOM -extra_scripts = ${esp32_common_LittleFS.extra_scripts} - pre:tools/pio/pre_custom_esp32.py [env:custom_ESP32_16M8M_LittleFS] -extends = esp32_common_LittleFS +extends = esp32_custom_base_LittleFS board = esp32_16M8M -build_flags = ${esp32_common_LittleFS.build_flags} - -DPLUGIN_BUILD_CUSTOM -extra_scripts = ${esp32_common_LittleFS.extra_scripts} - pre:tools/pio/pre_custom_esp32.py [env:custom_IR_ESP32_4M316k] -extends = esp32_common +extends = esp32_base board = esp32_4M -build_flags = ${esp32_common.build_flags} - -DPLUGIN_BUILD_CUSTOM - -DPLUGIN_BUILD_IR +build_flags = ${esp32_common.build_flags}, -DPLUGIN_BUILD_CUSTOM, -DPLUGIN_BUILD_IR lib_ignore = ${esp32_always.lib_ignore}, ESP32_ping, HeatpumpIR extra_scripts = ${esp32_common.extra_scripts} pre:tools/pio/pre_custom_esp32.py @@ -318,38 +321,20 @@ build_flags = ${esp32s2_common.build_flags} ; A Lolin D32 PRO with 16MB Flash, allowing 4MB sketch size, and file storage using the default (SPIFFS) filesystem [env:max_ESP32_16M1M] -extends = esp32_common +extends = esp32_max_SPIFFS board = esp32_16M1M -lib_ignore = ${esp32_always.lib_ignore}, ESP32_ping -lib_deps = ${esp32_common.lib_deps}, VL53L0X -build_flags = ${esp32_common.build_flags} - -DFEATURE_ARDUINO_OTA - -DPLUGIN_BUILD_MAX_ESP32 - -DPLUGIN_BUILD_IR_EXTENDED -; TODO: To enable PS-RAM Support needs more build flags than these 2, for now define ESP32_ENABLE_PSRAM is used to en/disable detecting PS-Ram size on Info page -; -DBOARD_HAS_PSRAM // both flags already enabled for Lolin D32 Pro board by PlatformIO -; -mfix-esp32-psram-cache-issue ; A Lolin D32 PRO with 16MB Flash, allowing 4MB sketch size, and file storage using LittleFS filesystem [env:max_ESP32_16M8M_LittleFS] -extends = esp32_common_LittleFS +extends = esp32_max_LittleFS board = esp32_16M8M -lib_ignore = ${esp32_always.lib_ignore}, ESP32_ping -lib_deps = ${esp32_common_LittleFS.lib_deps}, VL53L0X -build_flags = ${esp32_common_LittleFS.build_flags} - -DFEATURE_ARDUINO_OTA - -DPLUGIN_BUILD_MAX_ESP32 - -DPLUGIN_BUILD_IR_EXTENDED -; TODO: To enable PS-RAM Support needs more build flags than these 2, for now define ESP32_ENABLE_PSRAM is used to en/disable detecting PS-Ram size on Info page -; -DBOARD_HAS_PSRAM // both flags already enabled for Lolin D32 Pro board by PlatformIO -; -mfix-esp32-psram-cache-issue - ; If you have a board with Ethernet integrated and 16MB Flash, then this configuration could be enabled, it's based on the max_ESP32_16M8M_LittleFS definition [env:max_ESP32_16M8M_LittleFS_ETH] -extends = max_ESP32_16M8M_LittleFS -build_flags = ${max_ESP32_16M8M_LittleFS.build_flags} -DHAS_ETHERNET +extends = esp32_max_LittleFS +board = esp32_16M8M +build_flags = ${esp32_max_LittleFS.build_flags} -DHAS_ETHERNET From dabc906077c50479970b57ed396a254f51936067 Mon Sep 17 00:00:00 2001 From: TD-er Date: Tue, 4 Jan 2022 02:08:28 +0100 Subject: [PATCH 094/147] [Cleanup] Split ESP32S2 envs to separate ini file. --- platformio.ini | 13 +++-- platformio_esp32_envs.ini | 94 +------------------------------------ platformio_esp32s2_envs.ini | 83 ++++++++++++++++++++++++++++++++ platformio_esp82xx_base.ini | 4 +- 4 files changed, 92 insertions(+), 102 deletions(-) create mode 100644 platformio_esp32s2_envs.ini diff --git a/platformio.ini b/platformio.ini index 847b6c033c..507868fe22 100644 --- a/platformio.ini +++ b/platformio.ini @@ -18,6 +18,7 @@ extra_configs = platformio_esp82xx_base.ini platformio_esp82xx_envs.ini platformio_esp32_envs.ini + platformio_esp32s2_envs.ini platformio_special_envs.ini ;default_envs = normal_ESP32_4M @@ -68,11 +69,6 @@ build_flags = [mqtt_flags] build_flags = -DMQTT_MAX_PACKET_SIZE=1024 -[extra_scripts_esp8266] -extra_scripts = tools/pio/gzip-firmware.py - ${extra_scripts_default.extra_scripts} - - [extra_scripts_default] extra_scripts = pre:tools/pio/set-ci-defines.py pre:tools/pio/concat_cpp_files.py @@ -80,6 +76,11 @@ extra_scripts = pre:tools/pio/set-ci-defines.py tools/pio/copy_files.py post:tools/pio/remove_concat_cpp_files.py +[extra_scripts_esp8266] +extra_scripts = tools/pio/gzip-firmware.py + ${extra_scripts_default.extra_scripts} + + [common] lib_ldf_mode = deep+ lib_archive = false @@ -88,8 +89,6 @@ upload_speed = 115200 monitor_speed = 115200 ;targets = size, checkprogsize targets = -extra_scripts = pre:tools/pio/pre_default_check.py - ${extra_scripts_esp8266.extra_scripts} src_filter = +<*> -<.git/> -<.svn/> - - - - -<*/Commands/> -<*/ControllerQueue/> -<*/DataStructs/> -<*/DataTypes/> -<*/Globals/> -<*/Helpers/> -<*/PluginStructs/> -<*/WebServer/> diff --git a/platformio_esp32_envs.ini b/platformio_esp32_envs.ini index 12fb4ccfd7..f098f53fa0 100644 --- a/platformio_esp32_envs.ini +++ b/platformio_esp32_envs.ini @@ -42,17 +42,6 @@ lib_deps = ${esp32_base.lib_deps}, LittleFS board_build.filesystem = littlefs -[esp32s2_common] -extends = esp32_common -build_flags = ${esp32_common.build_flags}, -DESP32S2, -DBOARD_HAS_PSRAM, -DESP32_ENABLE_PSRAM, -DFEATURE_ARDUINO_OTA - -[esp32s2_common_LittleFS] -extends = esp32s2_common -build_flags = ${esp32s2_common.build_flags}, -DUSE_LITTLEFS -lib_deps = ${esp32s2_common.lib_deps}, LittleFS -board_build.filesystem = littlefs - - [esp32_custom_base] extends = esp32_common build_flags = ${esp32_common.build_flags}, -DPLUGIN_BUILD_CUSTOM @@ -88,27 +77,6 @@ extra_scripts = ${esp32_common.extra_scripts} pre:tools/pio/pre_custom_esp32.py -; ESP32-S2 -[env:custom_ESP32s2_4M316k] -extends = esp32s2_common -board = esp32s2 -build_flags = ${esp32s2_common.build_flags} - -DPLUGIN_BUILD_CUSTOM -extra_scripts = ${esp32s2_common.extra_scripts} - pre:tools/pio/pre_custom_esp32.py - - -[env:custom_IR_ESP32s2_4M316k] -extends = esp32s2_common -board = esp32s2 -build_flags = ${esp32s2_common.build_flags} - -DPLUGIN_BUILD_CUSTOM - -DPLUGIN_BUILD_IR -lib_ignore = ${esp32_always.lib_ignore}, ESP32_ping, HeatpumpIR -extra_scripts = ${esp32s2_common.extra_scripts} - pre:tools/pio/pre_custom_esp32.py - - ; Normal: 4096k version -------------------------- [env:normal_ESP32_4M316k] @@ -129,12 +97,6 @@ board = esp32_16M8M lib_deps = ${esp32_common_LittleFS.lib_deps}, ServoESP32 -; ESP32-S2 -[env:normal_ESP32s2_4M316k_LittleFS] -extends = esp32s2_common_LittleFS -board = esp32s2 - - ; Test A....E builds -------------------------- @@ -263,61 +225,7 @@ build_flags = ${test_D_ESP32_4M316k.build_flags} -DHAS_ETHERNET -;;; ESP32-s2 *********************************************************** - -[env:normal_ESP32s2_4M316k] -extends = esp32s2_common -board = esp32s2 -lib_deps = ${esp32s2_common.lib_deps}, ServoESP32 - -[env:test_A_ESP32s2_4M316k] -extends = esp32s2_common -board = esp32s2 -build_flags = ${esp32s2_common.build_flags} - -DPLUGIN_SET_TEST_ESP32 - -DTESTING_USE_RTTTL - -[env:test_B_ESP32s2_4M316k] -extends = esp32s2_common -board = esp32s2 -build_flags = ${esp32s2_common.build_flags} - -DPLUGIN_SET_TEST_B_ESP32 - -DTESTING_USE_RTTTL - -[env:test_C_ESP32s2_4M316k] -extends = esp32s2_common -board = esp32s2 -build_flags = ${esp32s2_common.build_flags} - -DPLUGIN_SET_TEST_C_ESP32 - -DTESTING_USE_RTTTL - -[env:test_D_ESP32s2_4M316k] -extends = esp32s2_common -board = esp32s2 -build_flags = ${esp32s2_common.build_flags} - -DPLUGIN_SET_TEST_D_ESP32 - -DTESTING_USE_RTTTL - - -[env:energy_ESP32s2_4M316k] -extends = esp32s2_common -board = esp32s2 -lib_deps = ${esp32s2_common.lib_deps}, ServoESP32 -build_flags = ${esp32s2_common.build_flags} - -D PLUGIN_ENERGY_COLLECTION - -[env:display_ESP32s2_4M316k] -extends = esp32s2_common -board = esp32s2 -lib_deps = ${esp32s2_common.lib_deps}, ServoESP32 -build_flags = ${esp32s2_common.build_flags} - -D PLUGIN_DISPLAY_COLLECTION - - - - - -;;; ESP32 MAX builds 16M flash ------------------------------ +; ESP32 MAX builds 16M flash ------------------------------ ; A Lolin D32 PRO with 16MB Flash, allowing 4MB sketch size, and file storage using the default (SPIFFS) filesystem [env:max_ESP32_16M1M] diff --git a/platformio_esp32s2_envs.ini b/platformio_esp32s2_envs.ini new file mode 100644 index 0000000000..ba3d442e3f --- /dev/null +++ b/platformio_esp32s2_envs.ini @@ -0,0 +1,83 @@ + + + + +[esp32s2_common] +extends = esp32_common +build_flags = ${esp32_common.build_flags}, -DESP32S2, -DBOARD_HAS_PSRAM, -DESP32_ENABLE_PSRAM, -DFEATURE_ARDUINO_OTA + +[esp32s2_common_LittleFS] +extends = esp32s2_common +build_flags = ${esp32s2_common.build_flags}, -DUSE_LITTLEFS +lib_deps = ${esp32s2_common.lib_deps}, LittleFS +board_build.filesystem = littlefs + + +[env:custom_ESP32s2_4M316k] +extends = esp32s2_common +board = esp32s2 +build_flags = ${esp32s2_common.build_flags} + -DPLUGIN_BUILD_CUSTOM +extra_scripts = ${esp32s2_common.extra_scripts} + pre:tools/pio/pre_custom_esp32.py + + +[env:custom_IR_ESP32s2_4M316k] +extends = esp32s2_common +board = esp32s2 +build_flags = ${esp32s2_common.build_flags} + -DPLUGIN_BUILD_CUSTOM + -DPLUGIN_BUILD_IR +lib_ignore = ${esp32_always.lib_ignore}, ESP32_ping, HeatpumpIR +extra_scripts = ${esp32s2_common.extra_scripts} + pre:tools/pio/pre_custom_esp32.py + + + +[env:normal_ESP32s2_4M316k] +extends = esp32s2_common +board = esp32s2 +lib_deps = ${esp32s2_common.lib_deps}, ServoESP32 + +[env:test_A_ESP32s2_4M316k] +extends = esp32s2_common +board = esp32s2 +build_flags = ${esp32s2_common.build_flags} + -DPLUGIN_SET_TEST_ESP32 + -DTESTING_USE_RTTTL + +[env:test_B_ESP32s2_4M316k] +extends = esp32s2_common +board = esp32s2 +build_flags = ${esp32s2_common.build_flags} + -DPLUGIN_SET_TEST_B_ESP32 + -DTESTING_USE_RTTTL + +[env:test_C_ESP32s2_4M316k] +extends = esp32s2_common +board = esp32s2 +build_flags = ${esp32s2_common.build_flags} + -DPLUGIN_SET_TEST_C_ESP32 + -DTESTING_USE_RTTTL + +[env:test_D_ESP32s2_4M316k] +extends = esp32s2_common +board = esp32s2 +build_flags = ${esp32s2_common.build_flags} + -DPLUGIN_SET_TEST_D_ESP32 + -DTESTING_USE_RTTTL + + +[env:energy_ESP32s2_4M316k] +extends = esp32s2_common +board = esp32s2 +lib_deps = ${esp32s2_common.lib_deps}, ServoESP32 +build_flags = ${esp32s2_common.build_flags} + -D PLUGIN_ENERGY_COLLECTION + +[env:display_ESP32s2_4M316k] +extends = esp32s2_common +board = esp32s2 +lib_deps = ${esp32s2_common.lib_deps}, ServoESP32 +build_flags = ${esp32s2_common.build_flags} + -D PLUGIN_DISPLAY_COLLECTION diff --git a/platformio_esp82xx_base.ini b/platformio_esp82xx_base.ini index 536ef1fb1f..56832ec6c7 100644 --- a/platformio_esp82xx_base.ini +++ b/platformio_esp82xx_base.ini @@ -3,7 +3,6 @@ ; ********************************************************************* - [regular_platform] build_flags = ${core_2_7_4.build_flags} platform = ${core_2_7_4.platform} @@ -51,7 +50,8 @@ build_unflags = -DDEBUG_ESP_PORT lib_deps = td-er/ESPeasySerial @ 2.0.8, I2Cdevlib-Core, adafruit/Adafruit ILI9341 @ ^1.5.6, Adafruit GFX Library, LOLIN_EPD, adafruit/Adafruit BusIO @ ^1.10.0, bblanchon/ArduinoJson @ ^6.17.2, VL53L0X @ 1.3.0, SparkFun VL53L1X 4m Laser Distance Sensor @ 1.2.9, td-er/RABurton ESP8266 Mutex @ ^1.0.2, td-er/SparkFun MAX1704x Fuel Gauge Arduino Library @ ^1.0.1, ESP8266HTTPUpdateServer, FrogmoreScd30, Multi Channel Relay Arduino Library, SparkFun ADXL345 Arduino Library, ITG3205 lib_ignore = ${esp82xx_defaults.lib_ignore}, IRremoteESP8266, HeatpumpIR, LittleFS(esp8266), ServoESP32, TinyWireM monitor_filters = esp8266_exception_decoder - +extra_scripts = pre:tools/pio/pre_default_check.py + ${extra_scripts_esp8266.extra_scripts} From 845c0bd04b2b4ba32ef1bf5194907cb523489530 Mon Sep 17 00:00:00 2001 From: TD-er Date: Tue, 4 Jan 2022 11:19:35 +0100 Subject: [PATCH 095/147] [PIO build] Do not use commas in build_flags (Linux build fail) --- platformio_esp32_envs.ini | 55 +++++++++++++++++++++++++++---------- platformio_esp32s2_envs.ini | 9 ++++-- platformio_esp82xx_base.ini | 38 +++++++++++++++++-------- 3 files changed, 73 insertions(+), 29 deletions(-) diff --git a/platformio_esp32_envs.ini b/platformio_esp32_envs.ini index f098f53fa0..9e4926ddbc 100644 --- a/platformio_esp32_envs.ini +++ b/platformio_esp32_envs.ini @@ -13,7 +13,12 @@ lib_deps = td-er/ESPeasySerial @ 2.0.8, adafruit/Adafruit ILI93 extra_scripts = post:tools/pio/post_esp32.py ${extra_scripts_default.extra_scripts} build_unflags = -Wall -build_flags = ${core_esp32_IDF4_4__2_0_2.build_flags}, ${mqtt_flags.build_flags}, -DCONFIG_FREERTOS_ASSERT_DISABLE, -DCONFIG_LWIP_ESP_GRATUITOUS_ARP, -fno-strict-aliasing, -DCORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_NONE +build_flags = ${core_esp32_IDF4_4__2_0_2.build_flags} + ${mqtt_flags.build_flags} + -DCONFIG_FREERTOS_ASSERT_DISABLE + -DCONFIG_LWIP_ESP_GRATUITOUS_ARP + -fno-strict-aliasing + -DCORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_NONE monitor_filters = esp32_exception_decoder @@ -24,33 +29,43 @@ lib_ignore = ${esp32_always.lib_ignore}, ESP32_ping, IRremoteESP8 [esp32_common_LittleFS] extends = esp32_common -build_flags = ${esp32_common.build_flags}, -DUSE_LITTLEFS +build_flags = ${esp32_common.build_flags} + -DUSE_LITTLEFS lib_deps = ${esp32_common.lib_deps}, LittleFS board_build.filesystem = littlefs [esp32_max_SPIFFS] extends = esp32_base lib_ignore = ${esp32_always.lib_ignore}, ESP32_ping -build_flags = ${esp32_base.build_flags}, -DFEATURE_ARDUINO_OTA, -DPLUGIN_BUILD_MAX_ESP32, -DPLUGIN_BUILD_IR_EXTENDED +build_flags = ${esp32_base.build_flags} + -DFEATURE_ARDUINO_OTA + -DPLUGIN_BUILD_MAX_ESP32 + -DPLUGIN_BUILD_IR_EXTENDED [esp32_max_LittleFS] extends = esp32_base lib_ignore = ${esp32_always.lib_ignore}, ESP32_ping -build_flags = ${esp32_base.build_flags}, -DUSE_LITTLEFS, -DFEATURE_ARDUINO_OTA, -DPLUGIN_BUILD_MAX_ESP32, -DPLUGIN_BUILD_IR_EXTENDED +build_flags = ${esp32_base.build_flags} + -DUSE_LITTLEFS + -DFEATURE_ARDUINO_OTA + -DPLUGIN_BUILD_MAX_ESP32 + -DPLUGIN_BUILD_IR_EXTENDED lib_deps = ${esp32_base.lib_deps}, LittleFS board_build.filesystem = littlefs [esp32_custom_base] extends = esp32_common -build_flags = ${esp32_common.build_flags}, -DPLUGIN_BUILD_CUSTOM +build_flags = ${esp32_common.build_flags} + -DPLUGIN_BUILD_CUSTOM extra_scripts = ${esp32_common.extra_scripts} pre:tools/pio/pre_custom_esp32.py [esp32_custom_base_LittleFS] extends = esp32_common_LittleFS -build_flags = ${esp32_common.build_flags}, -DPLUGIN_BUILD_CUSTOM +build_flags = ${esp32_common.build_flags} + -DPLUGIN_BUILD_CUSTOM extra_scripts = ${esp32_common.extra_scripts} pre:tools/pio/pre_custom_esp32.py @@ -71,7 +86,9 @@ board = esp32_16M8M [env:custom_IR_ESP32_4M316k] extends = esp32_base board = esp32_4M -build_flags = ${esp32_common.build_flags}, -DPLUGIN_BUILD_CUSTOM, -DPLUGIN_BUILD_IR +build_flags = ${esp32_common.build_flags} + -DPLUGIN_BUILD_CUSTOM + -DPLUGIN_BUILD_IR lib_ignore = ${esp32_always.lib_ignore}, ESP32_ping, HeatpumpIR extra_scripts = ${esp32_common.extra_scripts} pre:tools/pio/pre_custom_esp32.py @@ -82,7 +99,8 @@ extra_scripts = ${esp32_common.extra_scripts} [env:normal_ESP32_4M316k] extends = esp32_common board = esp32_4M -lib_deps = ${esp32_common.lib_deps}, ServoESP32 +lib_deps = ${esp32_common.lib_deps} + ServoESP32 [env:normal_ESP32_4M316k_LittleFS] @@ -187,40 +205,46 @@ build_flags = ${esp32_common.build_flags} extends = custom_ESP32_4M316k board = esp32_4M platform = ${custom_ESP32_4M316k.platform} -build_flags = ${custom_ESP32_4M316k.build_flags} -DHAS_ETHERNET +build_flags = ${custom_ESP32_4M316k.build_flags} + -DHAS_ETHERNET [env:normal_ESP32_4M316k_ETH] extends = normal_ESP32_4M316k board = esp32_4M platform = ${normal_ESP32_4M316k.platform} -build_flags = ${normal_ESP32_4M316k.build_flags} -DHAS_ETHERNET +build_flags = ${normal_ESP32_4M316k.build_flags} + -DHAS_ETHERNET [env:test_A_ESP32_4M316k_ETH] extends = test_A_ESP32_4M316k board = esp32_4M platform = ${test_A_ESP32_4M316k.platform} -build_flags = ${test_A_ESP32_4M316k.build_flags} -DHAS_ETHERNET +build_flags = ${test_A_ESP32_4M316k.build_flags} + -DHAS_ETHERNET -DTESTING_USE_RTTTL [env:test_B_ESP32_4M316k_ETH] extends = test_B_ESP32_4M316k board = esp32_4M platform = ${test_B_ESP32_4M316k.platform} -build_flags = ${test_B_ESP32_4M316k.build_flags} -DHAS_ETHERNET +build_flags = ${test_B_ESP32_4M316k.build_flags} + -DHAS_ETHERNET -DTESTING_USE_RTTTL [env:test_C_ESP32_4M316k_ETH] extends = test_C_ESP32_4M316k board = esp32_4M platform = ${test_C_ESP32_4M316k.platform} -build_flags = ${test_C_ESP32_4M316k.build_flags} -DHAS_ETHERNET +build_flags = ${test_C_ESP32_4M316k.build_flags} + -DHAS_ETHERNET -DTESTING_USE_RTTTL [env:test_D_ESP32_4M316k_ETH] extends = test_D_ESP32_4M316k board = esp32_4M platform = ${test_D_ESP32_4M316k.platform} -build_flags = ${test_D_ESP32_4M316k.build_flags} -DHAS_ETHERNET +build_flags = ${test_D_ESP32_4M316k.build_flags} + -DHAS_ETHERNET -DTESTING_USE_RTTTL @@ -242,7 +266,8 @@ board = esp32_16M8M [env:max_ESP32_16M8M_LittleFS_ETH] extends = esp32_max_LittleFS board = esp32_16M8M -build_flags = ${esp32_max_LittleFS.build_flags} -DHAS_ETHERNET +build_flags = ${esp32_max_LittleFS.build_flags} + -DHAS_ETHERNET diff --git a/platformio_esp32s2_envs.ini b/platformio_esp32s2_envs.ini index ba3d442e3f..cb0095f282 100644 --- a/platformio_esp32s2_envs.ini +++ b/platformio_esp32s2_envs.ini @@ -4,11 +4,16 @@ [esp32s2_common] extends = esp32_common -build_flags = ${esp32_common.build_flags}, -DESP32S2, -DBOARD_HAS_PSRAM, -DESP32_ENABLE_PSRAM, -DFEATURE_ARDUINO_OTA +build_flags = ${esp32_common.build_flags} + -DESP32S2 + -DBOARD_HAS_PSRAM + -DESP32_ENABLE_PSRAM + -DFEATURE_ARDUINO_OTA [esp32s2_common_LittleFS] extends = esp32s2_common -build_flags = ${esp32s2_common.build_flags}, -DUSE_LITTLEFS +build_flags = ${esp32s2_common.build_flags} + -DUSE_LITTLEFS lib_deps = ${esp32s2_common.lib_deps}, LittleFS board_build.filesystem = littlefs diff --git a/platformio_esp82xx_base.ini b/platformio_esp82xx_base.ini index 56832ec6c7..bc8e62e797 100644 --- a/platformio_esp82xx_base.ini +++ b/platformio_esp82xx_base.ini @@ -44,7 +44,9 @@ lib_ignore = ${no_ir.lib_ignore}, ${no_sd.lib_ignore}, ${no_littl [esp82xx_common] extends = common -build_flags = ${debug_flags.build_flags} ${mqtt_flags.build_flags} -DHTTPCLIENT_1_1_COMPATIBLE=0 +build_flags = ${debug_flags.build_flags} + ${mqtt_flags.build_flags} + -DHTTPCLIENT_1_1_COMPATIBLE=0 build_unflags = -DDEBUG_ESP_PORT -fexceptions lib_deps = td-er/ESPeasySerial @ 2.0.8, I2Cdevlib-Core, adafruit/Adafruit ILI9341 @ ^1.5.6, Adafruit GFX Library, LOLIN_EPD, adafruit/Adafruit BusIO @ ^1.10.0, bblanchon/ArduinoJson @ ^6.17.2, VL53L0X @ 1.3.0, SparkFun VL53L1X 4m Laser Distance Sensor @ 1.2.9, td-er/RABurton ESP8266 Mutex @ ^1.0.2, td-er/SparkFun MAX1704x Fuel Gauge Arduino Library @ ^1.0.1, ESP8266HTTPUpdateServer, FrogmoreScd30, Multi Channel Relay Arduino Library, SparkFun ADXL345 Arduino Library, ITG3205 @@ -87,19 +89,22 @@ lib_ignore = ${beta_platform.lib_ignore} SD(esp8266), SDFS [testing] platform = ${regular_platform.platform} platform_packages = ${regular_platform.platform_packages} -build_flags = ${regular_platform.build_flags} -DPLUGIN_BUILD_TESTING +build_flags = ${regular_platform.build_flags} + -DPLUGIN_BUILD_TESTING lib_ignore = ${regular_platform.lib_ignore} [testing_alt_wifi] platform = ${regular_platform_alt_wifi.platform} platform_packages = ${regular_platform_alt_wifi.platform_packages} -build_flags = ${regular_platform_alt_wifi.build_flags} -DPLUGIN_BUILD_TESTING +build_flags = ${regular_platform_alt_wifi.build_flags} + -DPLUGIN_BUILD_TESTING lib_ignore = ${regular_platform_alt_wifi.lib_ignore} [testing_beta] platform = ${beta_platform.platform} platform_packages = ${beta_platform.platform_packages} -build_flags = ${beta_platform.build_flags} -DPLUGIN_BUILD_TESTING +build_flags = ${beta_platform.build_flags} + -DPLUGIN_BUILD_TESTING lib_ignore = ${beta_platform.lib_ignore} @@ -122,23 +127,28 @@ lib_ignore = ESP32_ping, ESP32WebServer, ServoESP32, ESP32HTTPUpd [minimal_ir] extends = ir -build_flags = ${ir.build_flags} -DPLUGIN_BUILD_MINIMAL_IR +build_flags = ${ir.build_flags} + -DPLUGIN_BUILD_MINIMAL_IR [minimal_ir_extended] extends = ir -build_flags = ${ir.build_flags} -DPLUGIN_BUILD_MINIMAL_IRext +build_flags = ${ir.build_flags} + -DPLUGIN_BUILD_MINIMAL_IRext [normal_ir] extends = ir -build_flags = ${ir.build_flags} -DPLUGIN_BUILD_NORMAL_IR +build_flags = ${ir.build_flags} + -DPLUGIN_BUILD_NORMAL_IR [normal_ir_extended] extends = ir -build_flags = ${ir.build_flags} -DPLUGIN_BUILD_IR_EXTENDED +build_flags = ${ir.build_flags} + -DPLUGIN_BUILD_IR_EXTENDED [normal_ir_extended_no_rx] extends = ir -build_flags = ${ir.build_flags} -DPLUGIN_BUILD_IR_EXTENDED_NO_RX +build_flags = ${ir.build_flags} + -DPLUGIN_BUILD_IR_EXTENDED_NO_RX @@ -160,7 +170,8 @@ board = esp8266_1M128k [esp8285_1M] extends = esp82xx_1M board = esp8285_1M128k -build_flags = ${esp8266_1M.build_flags} -DESP8285 +build_flags = ${esp8266_1M.build_flags} + -DESP8285 ;;; Minimal *********************************************************** @@ -168,7 +179,9 @@ build_flags = ${esp8266_1M.build_flags} -DESP8285 ; ********************************************************************* [esp82xx_1M_OTA] extends = esp82xx_1M -build_flags = ${esp82xx_1M.build_flags} -DPLUGIN_BUILD_MINIMAL_OTA -DDISABLE_SC16IS752_Serial +build_flags = ${esp82xx_1M.build_flags} + -DPLUGIN_BUILD_MINIMAL_OTA + -DDISABLE_SC16IS752_Serial [esp8266_1M_OTA] @@ -178,7 +191,8 @@ board = esp8266_1M128k_OTA [esp8285_1M_OTA] extends = esp82xx_1M_OTA board = esp8285_1M128k_OTA -build_flags = ${esp82xx_1M_OTA.build_flags} -DESP8285 +build_flags = ${esp82xx_1M_OTA.build_flags} + -DESP8285 ;;; 2MB flash nodes ************************************************** From 65f7d7249354e50753d9cc946b58bea7db091fd7 Mon Sep 17 00:00:00 2001 From: TD-er Date: Tue, 4 Jan 2022 11:58:22 +0100 Subject: [PATCH 096/147] [PIO] fix incorrect esp32 IRExt envs --- platformio_esp32_envs.ini | 56 +++++++++++++++------------------------ 1 file changed, 21 insertions(+), 35 deletions(-) diff --git a/platformio_esp32_envs.ini b/platformio_esp32_envs.ini index 9e4926ddbc..c369b29280 100644 --- a/platformio_esp32_envs.ini +++ b/platformio_esp32_envs.ini @@ -54,6 +54,16 @@ build_flags = ${esp32_base.build_flags} lib_deps = ${esp32_base.lib_deps}, LittleFS board_build.filesystem = littlefs +[esp32_IRExt] +extends = esp32_base +lib_ignore = AS_BH1750, ${esp32_always.lib_ignore}, ESP32_ping +build_flags = ${esp32_base.build_flags} + -DFEATURE_ARDUINO_OTA + -DPLUGIN_BUILD_NORMAL_IRext + -DTESTING_USE_RTTTL +lib_deps = ${esp32_base.lib_deps}, LittleFS + + [esp32_custom_base] extends = esp32_common @@ -116,8 +126,6 @@ lib_deps = ${esp32_common_LittleFS.lib_deps}, ServoESP32 - -; Test A....E builds -------------------------- [env:test_A_ESP32_4M316k] extends = esp32_common board = esp32_4M @@ -152,36 +160,26 @@ build_flags = ${esp32_common.build_flags} [env:test_A_ESP32_IRExt_4M316k] -extends = test_A_ESP32_4M316k +extends = esp32_IRExt board = esp32_4M -lib_ignore = AS_BH1750, ${esp32_always.lib_ignore}, ESP32_ping -build_flags = ${esp32_common.build_flags} - -DPLUGIN_BUILD_NORMAL_IRext - -DTESTING_USE_RTTTL [env:test_B_ESP32_IRExt_4M316k] -extends = test_B_ESP32_4M316k +extends = esp32_IRExt board = esp32_4M -lib_ignore = AS_BH1750, ${esp32_always.lib_ignore}, ESP32_ping -build_flags = ${esp32_common.build_flags} - -DPLUGIN_BUILD_NORMAL_IRext - -DTESTING_USE_RTTTL +build_flags = ${esp32_IRExt.build_flags} + -DPLUGIN_SET_TEST_B_ESP32 [env:test_C_ESP32_IRExt_4M316k] -extends = test_C_ESP32_4M316k +extends = esp32_IRExt board = esp32_4M -lib_ignore = AS_BH1750, ${esp32_always.lib_ignore}, ESP32_ping -build_flags = ${esp32_common.build_flags} - -DPLUGIN_BUILD_NORMAL_IRext - -DTESTING_USE_RTTTL +build_flags = ${esp32_IRExt.build_flags} + -DPLUGIN_SET_TEST_C_ESP32 [env:test_D_ESP32_IRExt_4M316k] -extends = test_D_ESP32_4M316k +extends = esp32_IRExt board = esp32_4M -lib_ignore = AS_BH1750, ${esp32_always.lib_ignore}, ESP32_ping -build_flags = ${esp32_common.build_flags} - -DPLUGIN_BUILD_NORMAL_IRext - -DTESTING_USE_RTTTL +build_flags = ${esp32_IRExt.build_flags} + -DPLUGIN_SET_TEST_D_ESP32 [env:energy_ESP32_4M316k] extends = esp32_common @@ -189,7 +187,7 @@ board = esp32_4M lib_deps = ${esp32_common.lib_deps}, ServoESP32 build_flags = ${esp32_common.build_flags} -DFEATURE_ARDUINO_OTA - -D PLUGIN_ENERGY_COLLECTION + -DPLUGIN_ENERGY_COLLECTION [env:display_ESP32_4M316k] extends = esp32_common @@ -203,46 +201,34 @@ build_flags = ${esp32_common.build_flags} ; ETH builds -------------------------- [env:custom_ESP32_4M316k_ETH] extends = custom_ESP32_4M316k -board = esp32_4M -platform = ${custom_ESP32_4M316k.platform} build_flags = ${custom_ESP32_4M316k.build_flags} -DHAS_ETHERNET [env:normal_ESP32_4M316k_ETH] extends = normal_ESP32_4M316k -board = esp32_4M -platform = ${normal_ESP32_4M316k.platform} build_flags = ${normal_ESP32_4M316k.build_flags} -DHAS_ETHERNET [env:test_A_ESP32_4M316k_ETH] extends = test_A_ESP32_4M316k -board = esp32_4M -platform = ${test_A_ESP32_4M316k.platform} build_flags = ${test_A_ESP32_4M316k.build_flags} -DHAS_ETHERNET -DTESTING_USE_RTTTL [env:test_B_ESP32_4M316k_ETH] extends = test_B_ESP32_4M316k -board = esp32_4M -platform = ${test_B_ESP32_4M316k.platform} build_flags = ${test_B_ESP32_4M316k.build_flags} -DHAS_ETHERNET -DTESTING_USE_RTTTL [env:test_C_ESP32_4M316k_ETH] extends = test_C_ESP32_4M316k -board = esp32_4M -platform = ${test_C_ESP32_4M316k.platform} build_flags = ${test_C_ESP32_4M316k.build_flags} -DHAS_ETHERNET -DTESTING_USE_RTTTL [env:test_D_ESP32_4M316k_ETH] extends = test_D_ESP32_4M316k -board = esp32_4M -platform = ${test_D_ESP32_4M316k.platform} build_flags = ${test_D_ESP32_4M316k.build_flags} -DHAS_ETHERNET -DTESTING_USE_RTTTL From adedc55cff862d0c65c367a6013910231e51025e Mon Sep 17 00:00:00 2001 From: TD-er Date: Tue, 4 Jan 2022 12:18:40 +0100 Subject: [PATCH 097/147] [PIO] add labels to envs which wil later adjusted in extends --- platformio_esp32_envs.ini | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/platformio_esp32_envs.ini b/platformio_esp32_envs.ini index c369b29280..81a448cfe2 100644 --- a/platformio_esp32_envs.ini +++ b/platformio_esp32_envs.ini @@ -25,6 +25,9 @@ monitor_filters = esp32_exception_decoder [esp32_common] extends = esp32_base lib_ignore = ${esp32_always.lib_ignore}, ESP32_ping, IRremoteESP8266, HeatpumpIR, ESP32 BLE Arduino +build_flags = ${esp32_base.build_flags} +extra_scripts = ${esp32_base.extra_scripts} +lib_deps = ${esp32_base.lib_deps} [esp32_common_LittleFS] @@ -80,7 +83,6 @@ extra_scripts = ${esp32_common.extra_scripts} pre:tools/pio/pre_custom_esp32.py -; Custom: 4096k version -------------------------- [env:custom_ESP32_4M316k] extends = esp32_custom_base board = esp32_4M @@ -104,8 +106,6 @@ extra_scripts = ${esp32_common.extra_scripts} pre:tools/pio/pre_custom_esp32.py - -; Normal: 4096k version -------------------------- [env:normal_ESP32_4M316k] extends = esp32_common board = esp32_4M @@ -197,8 +197,6 @@ build_flags = ${esp32_common.build_flags} -DFEATURE_ARDUINO_OTA - -; ETH builds -------------------------- [env:custom_ESP32_4M316k_ETH] extends = custom_ESP32_4M316k build_flags = ${custom_ESP32_4M316k.build_flags} From c76419ae3d99b8ac27f3ba79c8e9bd4ba2ba02aa Mon Sep 17 00:00:00 2001 From: TD-er Date: Tue, 4 Jan 2022 13:17:55 +0100 Subject: [PATCH 098/147] [PIO] Use shorter extends chain for ESP32 max builds --- platformio_esp32_envs.ini | 53 +++++++++++++++++---------------------- 1 file changed, 23 insertions(+), 30 deletions(-) diff --git a/platformio_esp32_envs.ini b/platformio_esp32_envs.ini index 81a448cfe2..7ecd618d22 100644 --- a/platformio_esp32_envs.ini +++ b/platformio_esp32_envs.ini @@ -34,28 +34,10 @@ lib_deps = ${esp32_base.lib_deps} extends = esp32_common build_flags = ${esp32_common.build_flags} -DUSE_LITTLEFS +extra_scripts = ${esp32_common.extra_scripts} lib_deps = ${esp32_common.lib_deps}, LittleFS board_build.filesystem = littlefs -[esp32_max_SPIFFS] -extends = esp32_base -lib_ignore = ${esp32_always.lib_ignore}, ESP32_ping -build_flags = ${esp32_base.build_flags} - -DFEATURE_ARDUINO_OTA - -DPLUGIN_BUILD_MAX_ESP32 - -DPLUGIN_BUILD_IR_EXTENDED - - -[esp32_max_LittleFS] -extends = esp32_base -lib_ignore = ${esp32_always.lib_ignore}, ESP32_ping -build_flags = ${esp32_base.build_flags} - -DUSE_LITTLEFS - -DFEATURE_ARDUINO_OTA - -DPLUGIN_BUILD_MAX_ESP32 - -DPLUGIN_BUILD_IR_EXTENDED -lib_deps = ${esp32_base.lib_deps}, LittleFS -board_build.filesystem = littlefs [esp32_IRExt] extends = esp32_base @@ -77,9 +59,9 @@ extra_scripts = ${esp32_common.extra_scripts} [esp32_custom_base_LittleFS] extends = esp32_common_LittleFS -build_flags = ${esp32_common.build_flags} +build_flags = ${esp32_common_LittleFS.build_flags} -DPLUGIN_BUILD_CUSTOM -extra_scripts = ${esp32_common.extra_scripts} +extra_scripts = ${esp32_common_LittleFS.extra_scripts} pre:tools/pio/pre_custom_esp32.py @@ -98,19 +80,18 @@ board = esp32_16M8M [env:custom_IR_ESP32_4M316k] extends = esp32_base board = esp32_4M -build_flags = ${esp32_common.build_flags} +build_flags = ${esp32_base.build_flags} -DPLUGIN_BUILD_CUSTOM -DPLUGIN_BUILD_IR lib_ignore = ${esp32_always.lib_ignore}, ESP32_ping, HeatpumpIR -extra_scripts = ${esp32_common.extra_scripts} +extra_scripts = ${esp32_base.extra_scripts} pre:tools/pio/pre_custom_esp32.py [env:normal_ESP32_4M316k] extends = esp32_common board = esp32_4M -lib_deps = ${esp32_common.lib_deps} - ServoESP32 +lib_deps = ${esp32_common.lib_deps}, ServoESP32 [env:normal_ESP32_4M316k_LittleFS] @@ -237,20 +218,32 @@ build_flags = ${test_D_ESP32_4M316k.build_flags} ; A Lolin D32 PRO with 16MB Flash, allowing 4MB sketch size, and file storage using the default (SPIFFS) filesystem [env:max_ESP32_16M1M] -extends = esp32_max_SPIFFS +extends = esp32_base board = esp32_16M1M +lib_ignore = ${esp32_always.lib_ignore}, ESP32_ping +build_flags = ${esp32_base.build_flags} + -DFEATURE_ARDUINO_OTA + -DPLUGIN_BUILD_MAX_ESP32 + -DPLUGIN_BUILD_IR_EXTENDED ; A Lolin D32 PRO with 16MB Flash, allowing 4MB sketch size, and file storage using LittleFS filesystem [env:max_ESP32_16M8M_LittleFS] -extends = esp32_max_LittleFS +extends = esp32_base board = esp32_16M8M +lib_ignore = ${esp32_always.lib_ignore}, ESP32_ping +build_flags = ${esp32_base.build_flags} + -DUSE_LITTLEFS + -DFEATURE_ARDUINO_OTA + -DPLUGIN_BUILD_MAX_ESP32 + -DPLUGIN_BUILD_IR_EXTENDED +lib_deps = ${esp32_base.lib_deps}, LittleFS +board_build.filesystem = littlefs ; If you have a board with Ethernet integrated and 16MB Flash, then this configuration could be enabled, it's based on the max_ESP32_16M8M_LittleFS definition [env:max_ESP32_16M8M_LittleFS_ETH] -extends = esp32_max_LittleFS -board = esp32_16M8M -build_flags = ${esp32_max_LittleFS.build_flags} +extends = max_ESP32_16M8M_LittleFS +build_flags = ${max_ESP32_16M8M_LittleFS.build_flags} -DHAS_ETHERNET From 8765ad33a59c5fd10ef29919d8b22e547a028b84 Mon Sep 17 00:00:00 2001 From: TD-er Date: Tue, 4 Jan 2022 15:40:22 +0100 Subject: [PATCH 099/147] [PIO] Apparently when referring an env, the 'env:' prefix is needed Or at least in Linux, not in Windows.... --- platformio_esp32_envs.ini | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/platformio_esp32_envs.ini b/platformio_esp32_envs.ini index 7ecd618d22..caa5d37b5e 100644 --- a/platformio_esp32_envs.ini +++ b/platformio_esp32_envs.ini @@ -179,36 +179,36 @@ build_flags = ${esp32_common.build_flags} [env:custom_ESP32_4M316k_ETH] -extends = custom_ESP32_4M316k -build_flags = ${custom_ESP32_4M316k.build_flags} +extends = env:custom_ESP32_4M316k +build_flags = ${env:custom_ESP32_4M316k.build_flags} -DHAS_ETHERNET [env:normal_ESP32_4M316k_ETH] -extends = normal_ESP32_4M316k -build_flags = ${normal_ESP32_4M316k.build_flags} +extends = env:normal_ESP32_4M316k +build_flags = ${env:normal_ESP32_4M316k.build_flags} -DHAS_ETHERNET [env:test_A_ESP32_4M316k_ETH] -extends = test_A_ESP32_4M316k -build_flags = ${test_A_ESP32_4M316k.build_flags} +extends = env:test_A_ESP32_4M316k +build_flags = ${env:test_A_ESP32_4M316k.build_flags} -DHAS_ETHERNET -DTESTING_USE_RTTTL [env:test_B_ESP32_4M316k_ETH] -extends = test_B_ESP32_4M316k -build_flags = ${test_B_ESP32_4M316k.build_flags} +extends = env:test_B_ESP32_4M316k +build_flags = ${env:test_B_ESP32_4M316k.build_flags} -DHAS_ETHERNET -DTESTING_USE_RTTTL [env:test_C_ESP32_4M316k_ETH] -extends = test_C_ESP32_4M316k -build_flags = ${test_C_ESP32_4M316k.build_flags} +extends = env:test_C_ESP32_4M316k +build_flags = ${env:test_C_ESP32_4M316k.build_flags} -DHAS_ETHERNET -DTESTING_USE_RTTTL [env:test_D_ESP32_4M316k_ETH] -extends = test_D_ESP32_4M316k -build_flags = ${test_D_ESP32_4M316k.build_flags} +extends = env:test_D_ESP32_4M316k +build_flags = ${env:test_D_ESP32_4M316k.build_flags} -DHAS_ETHERNET -DTESTING_USE_RTTTL @@ -242,8 +242,8 @@ board_build.filesystem = littlefs ; If you have a board with Ethernet integrated and 16MB Flash, then this configuration could be enabled, it's based on the max_ESP32_16M8M_LittleFS definition [env:max_ESP32_16M8M_LittleFS_ETH] -extends = max_ESP32_16M8M_LittleFS -build_flags = ${max_ESP32_16M8M_LittleFS.build_flags} +extends = env:max_ESP32_16M8M_LittleFS +build_flags = ${env:max_ESP32_16M8M_LittleFS.build_flags} -DHAS_ETHERNET From f1895c6ae5f003a7c824703ce2c32e7fd2cf788c Mon Sep 17 00:00:00 2001 From: TD-er Date: Tue, 4 Jan 2022 15:58:39 +0100 Subject: [PATCH 100/147] [P096 eink] Fix failing build --- src/_P096_eInk.ino | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/_P096_eInk.ino b/src/_P096_eInk.ino index 083a18d493..20407bab48 100644 --- a/src/_P096_eInk.ino +++ b/src/_P096_eInk.ino @@ -111,8 +111,8 @@ Examples: #define EPD_BUSY -1 // can set to -1 to not use a pin (will wait a fixed delay) #else //for D1 Mini with shield connection - #define EPD_CS D0 - #define EPD_DC D8 + #define EPD_CS 16 // D0 + #define EPD_DC 15 // D8 #define EPD_RST -1 // can set to -1 and share with microcontroller Reset! #define EPD_BUSY -1 // can set to -1 to not use a pin (will wait a fixed delay) #endif From bd3c350e421386315b3705c2ec5e70249131cbca Mon Sep 17 00:00:00 2001 From: TD-er Date: Tue, 4 Jan 2022 19:18:13 +0100 Subject: [PATCH 101/147] [ESP32 LittleFS] Add old LittleFS library to lib_ignore For some reason it failed to build on Linux, but completes on Windows. Still the builds will not work when flashed (max builds) --- platformio_esp32_envs.ini | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/platformio_esp32_envs.ini b/platformio_esp32_envs.ini index caa5d37b5e..08b4f33b7f 100644 --- a/platformio_esp32_envs.ini +++ b/platformio_esp32_envs.ini @@ -5,7 +5,7 @@ ; ***************************************************************************************; [esp32_always] -lib_ignore = ESP8266WiFi, ESP8266Ping, ESP8266WebServer, ESP8266HTTPUpdateServer, ESP8266mDNS, ESPEasy_ESP8266Ping, RABurton ESP8266 Mutex, TinyWireM +lib_ignore = ESP8266WiFi, ESP8266Ping, ESP8266WebServer, ESP8266HTTPUpdateServer, ESP8266mDNS, ESPEasy_ESP8266Ping, RABurton ESP8266 Mutex, TinyWireM, LittleFS_esp32 [esp32_base] extends = common, core_esp32_IDF4_4__2_0_2 @@ -237,6 +237,7 @@ build_flags = ${esp32_base.build_flags} -DFEATURE_ARDUINO_OTA -DPLUGIN_BUILD_MAX_ESP32 -DPLUGIN_BUILD_IR_EXTENDED +extra_scripts = ${esp32_base.extra_scripts} lib_deps = ${esp32_base.lib_deps}, LittleFS board_build.filesystem = littlefs From 62443fc32b7ad24e1b87a1ebbef13083fe486e53 Mon Sep 17 00:00:00 2001 From: TD-er Date: Wed, 5 Jan 2022 01:39:34 +0100 Subject: [PATCH 102/147] [P118] Disable P118 ITHO for now as it crashes on IDF4.4 --- src/_P118_Itho.ino | 5 +++-- src/src/CustomBuild/ESPEasyLimits.h | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/_P118_Itho.ino b/src/_P118_Itho.ino index 4eb970dd39..fe523b8823 100644 --- a/src/_P118_Itho.ino +++ b/src/_P118_Itho.ino @@ -59,12 +59,13 @@ // See https://gathering.tweakers.net/forum/list_messages/1690945 for more information // code/idea was inspired by first release of code from 'Thinkpad' -#ifdef USES_P118 +#include "_Plugin_Helper.h" + +#ifdef USES_P118_disabled #include #include "IthoCC1101.h" #include "IthoPacket.h" -#include "_Plugin_Helper.h" // This extra settings struct is needed because the default settingsstruct doesn't support strings struct PLUGIN__ExtraSettingsStruct diff --git a/src/src/CustomBuild/ESPEasyLimits.h b/src/src/CustomBuild/ESPEasyLimits.h index 1d8b41b7b9..22858eda4c 100644 --- a/src/src/CustomBuild/ESPEasyLimits.h +++ b/src/src/CustomBuild/ESPEasyLimits.h @@ -84,8 +84,8 @@ # endif // ifdef PLUGIN_BUILD_MAX_ESP32 # else // ifdef ESP32 # define DEVICES_MAX 60 - # endif // ifdef ESP32 - #endif // if defined(PLUGIN_BUILD_TESTING) || defined(PLUGIN_BUILD_DEV) + # endif + #endif #endif #ifndef PLUGIN_MAX From 4164f14ec99b023a24e2c85b8ddea74dffd3eb88 Mon Sep 17 00:00:00 2001 From: TD-er Date: Wed, 5 Jan 2022 17:34:05 +0100 Subject: [PATCH 103/147] [ESP32 Factory] Fix boot loop on 16M flash nodes using factory image See discussion: https://github.com/letscontrolit/ESPEasy/pull/3845#issuecomment-1005656587 --- tools/pio/post_esp32.py | 76 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 73 insertions(+), 3 deletions(-) diff --git a/tools/pio/post_esp32.py b/tools/pio/post_esp32.py index c0b5ce6dfc..68a040e28e 100644 --- a/tools/pio/post_esp32.py +++ b/tools/pio/post_esp32.py @@ -1,23 +1,93 @@ Import("env") +import os + +def decode_flash_size_speed(byte_value): + speed = byte_value[0] & 0x0f + size = int((byte_value[0] & 0xf0) / 16) + + # As defined in esp_image_spi_freq_t + spi_speed = { + 0: "40", + 1: "26", + 2: "20", + 0xf: "80" + } + speed_mhz = spi_speed.get(speed, "??") + size_mb = str(1<\partitions.bin + # - 0xe000 | ~\.platformio\packages\framework-arduinoespressif32\tools\partitions\boot_app0.bin + # - 0x10000 | ~\ESPEasy\.pio\build\/.bin + new_file_name = env.subst("$BUILD_DIR/${PROGNAME}-factory.bin") sections = env.subst(env.get('FLASH_EXTRA_IMAGES')) new_file = open(new_file_name,"wb") + new_file.truncate() # Make sure no left over data is present from a previous build + + firmware_name = env.subst("$BUILD_DIR/${PROGNAME}.bin") + + # fill the file with 0xFF binary data to reflect 'erased and not yet written' data. + # Make sure to use exactly the size that normally would be erased + new_file.seek(0) + total_size = sketch_partition_start + os.path.getsize(firmware_name) + total_size = total_size - (total_size % flash_page_size) + flash_page_size + + new_file.write(bytes([0xff] * total_size)) + print(" {} | {}".format("Offset", "File")) for section in sections: sect_adr,sect_file = section.split(" ",1) print(" - {} | {}".format(sect_adr,sect_file)) source = open(sect_file,"rb") new_file.seek(int(sect_adr,0)-offset) - new_file.write(source.read()); + new_file.write(source.read()) + if "bootloader" in sect_file: + # Need to patch the bootloader to match the flash size + flash_size = env.BoardConfig().get("upload.flash_size") + + # See esp_image_header_t for struct definition + spi_speed_size_byte_offset = 3 + source.seek(spi_speed_size_byte_offset) + spi_speed_size_byte = source.read(1) + new_spi_speed_size_byte = spi_speed_size_byte[0] & 0x0f + + # As defined in esp_image_flash_size_t + spi_size = { + "1MB": 0x00, + "2MB": 0x10, + "4MB": 0x20, + "8MB": 0x30, + "16MB": 0x40 + } + new_spi_speed_size_byte |= spi_size.get(flash_size, spi_speed_size_byte[0] & 0xf0) + + if new_spi_speed_size_byte != spi_speed_size_byte: + new_byte = bytes([new_spi_speed_size_byte]) + print(" | Patch flash size in bootloader: {} -> {}".format(decode_flash_size_speed(spi_speed_size_byte), decode_flash_size_speed(new_byte))) + new_file.seek(int(sect_adr,0)-offset + spi_speed_size_byte_offset) + new_file.write(new_byte) + source.close() + - firmware_name = env.subst("$BUILD_DIR/${PROGNAME}.bin") - firmware_start = 0x10000-offset + firmware_start = sketch_partition_start-offset firmware = open(firmware_name,"rb") print(" - {} | {}".format(hex(firmware_start),firmware_name)) new_file.seek(firmware_start) From 337f038d91bd554cae31da0754a774f0027fde50 Mon Sep 17 00:00:00 2001 From: TD-er Date: Thu, 6 Jan 2022 16:56:36 +0100 Subject: [PATCH 104/147] [ESP32 factory] Use esptool.py to generate and patch combined binary --- tools/pio/post_esp32.py | 138 +++++++++++++++------------------------- 1 file changed, 52 insertions(+), 86 deletions(-) diff --git a/tools/pio/post_esp32.py b/tools/pio/post_esp32.py index 68a040e28e..5ca8d18e35 100644 --- a/tools/pio/post_esp32.py +++ b/tools/pio/post_esp32.py @@ -1,98 +1,64 @@ -Import("env") -import os - -def decode_flash_size_speed(byte_value): - speed = byte_value[0] & 0x0f - size = int((byte_value[0] & 0xf0) / 16) - - # As defined in esp_image_spi_freq_t - spi_speed = { - 0: "40", - 1: "26", - 2: "20", - 0xf: "80" - } - speed_mhz = spi_speed.get(speed, "??") - size_mb = str(1<4MB flash +# Thanks @jesserockz (esphome) for adapting to use esptool.py with merge_bin +# +# Typical layout of the generated file: +# Offset | File +# - 0x1000 | ~\.platformio\packages\framework-arduinoespressif32\tools\sdk\esp32\bin\bootloader_dout_40m.bin +# - 0x8000 | ~\ESPEasy\.pio\build\\partitions.bin +# - 0xe000 | ~\.platformio\packages\framework-arduinoespressif32\tools\partitions\boot_app0.bin +# - 0x10000 | ~\ESPEasy\.pio\build\/.bin + +import subprocess + +# pylint: disable=E0602 +Import("env") # noqa + +import esptool + + +def esp32_create_combined_bin(source, target, env): + print("Generating combined binary for serial flashing") # The offset from begin of the file where the app0 partition starts # This is defined in the partition .csv file - sketch_partition_start = 0x10000 + app_offset = 0x10000 - # Typical layout of the generated file: - # Offset | File - # - 0x1000 | ~\.platformio\packages\framework-arduinoespressif32\tools\sdk\esp32\bin\bootloader_dout_40m.bin - # - 0x8000 | ~\ESPEasy\.pio\build\\partitions.bin - # - 0xe000 | ~\.platformio\packages\framework-arduinoespressif32\tools\partitions\boot_app0.bin - # - 0x10000 | ~\ESPEasy\.pio\build\/.bin - new_file_name = env.subst("$BUILD_DIR/${PROGNAME}-factory.bin") - sections = env.subst(env.get('FLASH_EXTRA_IMAGES')) - new_file = open(new_file_name,"wb") - new_file.truncate() # Make sure no left over data is present from a previous build - + sections = env.subst(env.get("FLASH_EXTRA_IMAGES")) firmware_name = env.subst("$BUILD_DIR/${PROGNAME}.bin") - - # fill the file with 0xFF binary data to reflect 'erased and not yet written' data. - # Make sure to use exactly the size that normally would be erased - new_file.seek(0) - total_size = sketch_partition_start + os.path.getsize(firmware_name) - total_size = total_size - (total_size % flash_page_size) + flash_page_size - - new_file.write(bytes([0xff] * total_size)) - - print(" {} | {}".format("Offset", "File")) + chip = env.get("BOARD_MCU") + flash_size = env.BoardConfig().get("upload.flash_size") + cmd = [ + "--chip", + chip, + "merge_bin", + "-o", + new_file_name, + "--flash_size", + flash_size, + ] + + print(" Offset | File") for section in sections: - sect_adr,sect_file = section.split(" ",1) - print(" - {} | {}".format(sect_adr,sect_file)) - source = open(sect_file,"rb") - new_file.seek(int(sect_adr,0)-offset) - new_file.write(source.read()) - if "bootloader" in sect_file: - # Need to patch the bootloader to match the flash size - flash_size = env.BoardConfig().get("upload.flash_size") - - # See esp_image_header_t for struct definition - spi_speed_size_byte_offset = 3 - source.seek(spi_speed_size_byte_offset) - spi_speed_size_byte = source.read(1) - new_spi_speed_size_byte = spi_speed_size_byte[0] & 0x0f + sect_adr, sect_file = section.split(" ", 1) + print(f" - {sect_adr} | {sect_file}") + cmd += [sect_adr, sect_file] - # As defined in esp_image_flash_size_t - spi_size = { - "1MB": 0x00, - "2MB": 0x10, - "4MB": 0x20, - "8MB": 0x30, - "16MB": 0x40 - } - new_spi_speed_size_byte |= spi_size.get(flash_size, spi_speed_size_byte[0] & 0xf0) + print(f" - {hex(app_offset)} | {firmware_name}") + cmd += [hex(app_offset), firmware_name] - if new_spi_speed_size_byte != spi_speed_size_byte: - new_byte = bytes([new_spi_speed_size_byte]) - print(" | Patch flash size in bootloader: {} -> {}".format(decode_flash_size_speed(spi_speed_size_byte), decode_flash_size_speed(new_byte))) - new_file.seek(int(sect_adr,0)-offset + spi_speed_size_byte_offset) - new_file.write(new_byte) + print('Using esptool.py arguments: %s' % ' '.join(cmd)) - source.close() + esptool.main(cmd) - - firmware_start = sketch_partition_start-offset - firmware = open(firmware_name,"rb") - print(" - {} | {}".format(hex(firmware_start),firmware_name)) - new_file.seek(firmware_start) - new_file.write(firmware.read()) - new_file.close() - firmware.close() -env.AddPostAction("$BUILD_DIR/${PROGNAME}.bin", esp32_create_factory_bin) +# pylint: disable=E0602 +env.AddPostAction("$BUILD_DIR/${PROGNAME}.bin", esp32_create_combined_bin) # noqa \ No newline at end of file From 53ca4a1539e5705c1d6bd37fd71bb4328872a8c5 Mon Sep 17 00:00:00 2001 From: TD-er Date: Thu, 6 Jan 2022 19:23:13 +0100 Subject: [PATCH 105/147] [Build] Add missing esptool==3.2 in requirements.txt --- requirements.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/requirements.txt b/requirements.txt index d010247cf7..b712c1634b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -10,6 +10,7 @@ click==8.0.3 colorama==0.4.4 commonmark==0.9.1 docutils==0.17.1 +esptool==3.2 idna==3.3 imagesize==1.2.0 Jinja2==3.0.2 From 3a610bafed20a7634b135e503f387a2659ee91ef Mon Sep 17 00:00:00 2001 From: TD-er Date: Thu, 6 Jan 2022 23:11:51 +0100 Subject: [PATCH 106/147] [ESP32 factory] Revert factory image script to not use esptool.py As it seems next to impossible to call the already installed esptool.py from the PlatformIO package dir. --- tools/pio/post_esp32.py | 108 ++++++++++++++++++++++---------- tools/pio/post_esp32_esptool.py | 70 +++++++++++++++++++++ 2 files changed, 146 insertions(+), 32 deletions(-) create mode 100644 tools/pio/post_esp32_esptool.py diff --git a/tools/pio/post_esp32.py b/tools/pio/post_esp32.py index 5ca8d18e35..334e3a2b27 100644 --- a/tools/pio/post_esp32.py +++ b/tools/pio/post_esp32.py @@ -7,7 +7,6 @@ # Maintainer: Gijs Noorlander (@TD-er) # # Special thanks to @Jason2866 for helping debug flashing to >4MB flash -# Thanks @jesserockz (esphome) for adapting to use esptool.py with merge_bin # # Typical layout of the generated file: # Offset | File @@ -16,49 +15,94 @@ # - 0xe000 | ~\.platformio\packages\framework-arduinoespressif32\tools\partitions\boot_app0.bin # - 0x10000 | ~\ESPEasy\.pio\build\/.bin -import subprocess +Import("env") +import os -# pylint: disable=E0602 -Import("env") # noqa +def decode_flash_size_speed(byte_value): + speed = byte_value[0] & 0x0f + size = int((byte_value[0] & 0xf0) / 16) -import esptool + # As defined in esp_image_spi_freq_t + spi_speed = { + 0: "40", + 1: "26", + 2: "20", + 0xf: "80" + } + speed_mhz = spi_speed.get(speed, "??") + size_mb = str(1< {}".format(decode_flash_size_speed(spi_speed_size_byte), decode_flash_size_speed(new_byte))) + new_file.seek(int(sect_adr,0)-offset + spi_speed_size_byte_offset) + new_file.write(new_byte) - esptool.main(cmd) + source.close() + + firmware_start = sketch_partition_start-offset + firmware = open(firmware_name,"rb") + print(" - {} | {}".format(hex(firmware_start),firmware_name)) + new_file.seek(firmware_start) + new_file.write(firmware.read()) + new_file.close() + firmware.close() -# pylint: disable=E0602 -env.AddPostAction("$BUILD_DIR/${PROGNAME}.bin", esp32_create_combined_bin) # noqa \ No newline at end of file +env.AddPostAction("$BUILD_DIR/${PROGNAME}.bin", esp32_create_factory_bin) \ No newline at end of file diff --git a/tools/pio/post_esp32_esptool.py b/tools/pio/post_esp32_esptool.py new file mode 100644 index 0000000000..3842047a57 --- /dev/null +++ b/tools/pio/post_esp32_esptool.py @@ -0,0 +1,70 @@ +# Disabled for now, as it is unclear how to use esptool.py from the installed PlatformIO +# It is working fine as long as you install esptool.py in your Python virtual env. +# But that needs an extra user action to complete. +# +# For now we use post_esp32.py + +# Part of ESPEasy build toolchain. +# +# Combines separate bin files with their respective offsets into a single file +# This single file must then be flashed to an ESP32 node with 0 offset. +# +# Original implementation: Bartłomiej Zimoń (@uzi18) +# Maintainer: Gijs Noorlander (@TD-er) +# +# Special thanks to @Jason2866 for helping debug flashing to >4MB flash +# Thanks @jesserockz (esphome) for adapting to use esptool.py with merge_bin +# +# Typical layout of the generated file: +# Offset | File +# - 0x1000 | ~\.platformio\packages\framework-arduinoespressif32\tools\sdk\esp32\bin\bootloader_dout_40m.bin +# - 0x8000 | ~\ESPEasy\.pio\build\\partitions.bin +# - 0xe000 | ~\.platformio\packages\framework-arduinoespressif32\tools\partitions\boot_app0.bin +# - 0x10000 | ~\ESPEasy\.pio\build\/.bin + +import subprocess + +# pylint: disable=E0602 +Import("env") # noqa + +import esptool + + +def esp32_create_combined_bin(source, target, env): + print("Generating combined binary for serial flashing") + + # The offset from begin of the file where the app0 partition starts + # This is defined in the partition .csv file + app_offset = 0x10000 + + new_file_name = env.subst("$BUILD_DIR/${PROGNAME}-factory.bin") + sections = env.subst(env.get("FLASH_EXTRA_IMAGES")) + firmware_name = env.subst("$BUILD_DIR/${PROGNAME}.bin") + chip = env.get("BOARD_MCU") + flash_size = env.BoardConfig().get("upload.flash_size") + cmd = [ + "--chip", + chip, + "merge_bin", + "-o", + new_file_name, + "--flash_size", + flash_size, + ] + + print(" Offset | File") + for section in sections: + sect_adr, sect_file = section.split(" ", 1) + print(f" - {sect_adr} | {sect_file}") + cmd += [sect_adr, sect_file] + + print(f" - {hex(app_offset)} | {firmware_name}") + cmd += [hex(app_offset), firmware_name] + + print('Using esptool.py arguments: %s' % ' '.join(cmd)) + + esptool.main(cmd) + + +# pylint: disable=E0602 +env.AddPostAction("$BUILD_DIR/${PROGNAME}.bin", esp32_create_combined_bin) # noqa \ No newline at end of file From ac97fee69ed25c89afbfc2dcf48a2e90622e07d1 Mon Sep 17 00:00:00 2001 From: TD-er Date: Thu, 6 Jan 2022 23:41:25 +0100 Subject: [PATCH 107/147] [ESP32 factory] Final fix using esptool.py by Jason2866 See: https://github.com/letscontrolit/ESPEasy/pull/3845#issuecomment-1006971300 --- tools/pio/post_esp32.py | 111 ++++++++++---------------------- tools/pio/post_esp32_esptool.py | 70 -------------------- 2 files changed, 35 insertions(+), 146 deletions(-) delete mode 100644 tools/pio/post_esp32_esptool.py diff --git a/tools/pio/post_esp32.py b/tools/pio/post_esp32.py index 334e3a2b27..9f6a5454d7 100644 --- a/tools/pio/post_esp32.py +++ b/tools/pio/post_esp32.py @@ -6,7 +6,8 @@ # Original implementation: Bartłomiej Zimoń (@uzi18) # Maintainer: Gijs Noorlander (@TD-er) # -# Special thanks to @Jason2866 for helping debug flashing to >4MB flash +# Special thanks to @Jason2866 (Tasmota) for helping debug flashing to >4MB flash +# Thanks @jesserockz (esphome) for adapting to use esptool.py with merge_bin # # Typical layout of the generated file: # Offset | File @@ -16,93 +17,51 @@ # - 0x10000 | ~\ESPEasy\.pio\build\/.bin Import("env") -import os -def decode_flash_size_speed(byte_value): - speed = byte_value[0] & 0x0f - size = int((byte_value[0] & 0xf0) / 16) +import subprocess +import sys - # As defined in esp_image_spi_freq_t - spi_speed = { - 0: "40", - 1: "26", - 2: "20", - 0xf: "80" - } - speed_mhz = spi_speed.get(speed, "??") - size_mb = str(1< {}".format(decode_flash_size_speed(spi_speed_size_byte), decode_flash_size_speed(new_byte))) - new_file.seek(int(sect_adr,0)-offset + spi_speed_size_byte_offset) - new_file.write(new_byte) + print('Using esptool.py arguments: %s' % ' '.join(cmd)) - source.close() + esptool.main(cmd) - - firmware_start = sketch_partition_start-offset - firmware = open(firmware_name,"rb") - print(" - {} | {}".format(hex(firmware_start),firmware_name)) - new_file.seek(firmware_start) - new_file.write(firmware.read()) - new_file.close() - firmware.close() -env.AddPostAction("$BUILD_DIR/${PROGNAME}.bin", esp32_create_factory_bin) \ No newline at end of file +env.AddPostAction("$BUILD_DIR/${PROGNAME}.bin", esp32_create_combined_bin) \ No newline at end of file diff --git a/tools/pio/post_esp32_esptool.py b/tools/pio/post_esp32_esptool.py deleted file mode 100644 index 3842047a57..0000000000 --- a/tools/pio/post_esp32_esptool.py +++ /dev/null @@ -1,70 +0,0 @@ -# Disabled for now, as it is unclear how to use esptool.py from the installed PlatformIO -# It is working fine as long as you install esptool.py in your Python virtual env. -# But that needs an extra user action to complete. -# -# For now we use post_esp32.py - -# Part of ESPEasy build toolchain. -# -# Combines separate bin files with their respective offsets into a single file -# This single file must then be flashed to an ESP32 node with 0 offset. -# -# Original implementation: Bartłomiej Zimoń (@uzi18) -# Maintainer: Gijs Noorlander (@TD-er) -# -# Special thanks to @Jason2866 for helping debug flashing to >4MB flash -# Thanks @jesserockz (esphome) for adapting to use esptool.py with merge_bin -# -# Typical layout of the generated file: -# Offset | File -# - 0x1000 | ~\.platformio\packages\framework-arduinoespressif32\tools\sdk\esp32\bin\bootloader_dout_40m.bin -# - 0x8000 | ~\ESPEasy\.pio\build\\partitions.bin -# - 0xe000 | ~\.platformio\packages\framework-arduinoespressif32\tools\partitions\boot_app0.bin -# - 0x10000 | ~\ESPEasy\.pio\build\/.bin - -import subprocess - -# pylint: disable=E0602 -Import("env") # noqa - -import esptool - - -def esp32_create_combined_bin(source, target, env): - print("Generating combined binary for serial flashing") - - # The offset from begin of the file where the app0 partition starts - # This is defined in the partition .csv file - app_offset = 0x10000 - - new_file_name = env.subst("$BUILD_DIR/${PROGNAME}-factory.bin") - sections = env.subst(env.get("FLASH_EXTRA_IMAGES")) - firmware_name = env.subst("$BUILD_DIR/${PROGNAME}.bin") - chip = env.get("BOARD_MCU") - flash_size = env.BoardConfig().get("upload.flash_size") - cmd = [ - "--chip", - chip, - "merge_bin", - "-o", - new_file_name, - "--flash_size", - flash_size, - ] - - print(" Offset | File") - for section in sections: - sect_adr, sect_file = section.split(" ", 1) - print(f" - {sect_adr} | {sect_file}") - cmd += [sect_adr, sect_file] - - print(f" - {hex(app_offset)} | {firmware_name}") - cmd += [hex(app_offset), firmware_name] - - print('Using esptool.py arguments: %s' % ' '.join(cmd)) - - esptool.main(cmd) - - -# pylint: disable=E0602 -env.AddPostAction("$BUILD_DIR/${PROGNAME}.bin", esp32_create_combined_bin) # noqa \ No newline at end of file From 2c933e5a42eff077e01532020325fb59df5866f0 Mon Sep 17 00:00:00 2001 From: TD-er Date: Fri, 7 Jan 2022 00:03:13 +0100 Subject: [PATCH 108/147] [ESP32 factory] Change -factory.bin to .factory.bin --- dist/README.txt | 6 +++--- docs/source/Participate/ProjectStructure.rst | 6 +++--- tools/create_index.sh | 22 ++++++++++---------- tools/pio/copy_files.py | 8 +++---- tools/pio/post_esp32.py | 2 +- 5 files changed, 22 insertions(+), 22 deletions(-) diff --git a/dist/README.txt b/dist/README.txt index 9405875f08..5b6622405d 100644 --- a/dist/README.txt +++ b/dist/README.txt @@ -73,12 +73,12 @@ Ethernet support is included in similar builds as mentioned before, only ending Since ESP32 does have its flash partitioned in several blocks, we have 2 bin files of each ESP32 build: - test_ESP32_4M316k.bin -- test_ESP32_4M316k-factory.bin +- test_ESP32_4M316k.factory.bin -The binary with "-factory" in the name must be flashed on a new node, via the serial interface of the board. +The binary with ".factory" in the name must be flashed on a new node, via the serial interface of the board. This flash must be started at address 0. -The binary without "-factory" can be used for OTA updates. (OTA for ESP32 is added in May 2020) +The binary without ".factory" can be used for OTA updates. (OTA for ESP32 is added in May 2020) Please note that changing between those versions will destroy the settings! diff --git a/docs/source/Participate/ProjectStructure.rst b/docs/source/Participate/ProjectStructure.rst index 960aab774c..db4fcd4465 100644 --- a/docs/source/Participate/ProjectStructure.rst +++ b/docs/source/Participate/ProjectStructure.rst @@ -189,12 +189,12 @@ There are several builds for ESP32: Since ESP32 does have its flash partitioned in several blocks, we have 2 bin files of each ESP32 build, f.e.: * ``test_D_ESP32_4M316k.bin`` Use for OTA upgrades. -* ``test_D_ESP32_4M316k-factory.bin`` Use on clean nodes as initial inistall. +* ``test_D_ESP32_4M316k.factory.bin`` Use on clean nodes as initial inistall. -The binary with ``-factory`` in the name must be flashed on a new node, via the serial interface of the board. +The binary with ``.factory`` in the name must be flashed on a new node, via the serial interface of the board. This flash must be started at address 0. -The binary without ``-factory`` can be used for OTA updates. (OTA for ESP32 is added in May 2020) +The binary without ``.factory`` can be used for OTA updates. (OTA for ESP32 is added in May 2020) Blank Images diff --git a/tools/create_index.sh b/tools/create_index.sh index 8e430f29ae..82283d38b0 100644 --- a/tools/create_index.sh +++ b/tools/create_index.sh @@ -65,47 +65,47 @@ echo "