diff --git a/API.md b/API.md index caec908c6700..dc3a7f6e3f13 100644 --- a/API.md +++ b/API.md @@ -1,4 +1,7 @@ -## Sonoff-Tasmota basic API information +Logo + +# Basic API information + Sonoff-Tasmota can easily be extended by developers using provided function pointers as callback Ids. This document lists the available callback function Ids. See the wiki (https://github.com/arendst/Sonoff-Tasmota/wiki/Sensor-API) for more information. Callback availability can be checked by searching for either XdrvCall, XsnsCall, XdspCall and XnrgCall. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 3ecb5a9bc66d..96e1c3173d5c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,4 +1,6 @@ -# Contributing to Sonoff-Tasmota +Logo + +# Contributing **Any contribution helps our team and makes Tasmota better for the entire community!** @@ -26,7 +28,7 @@ This document describes rules that are in effect for this repository, meant for 1. Any contributor to the project can participate in the triaging process, if he/she chooses to do so. 2. An issue that needs to be closed, either due to not complying with this policy, or for other reasons, should be closed by a contributor. 3. Issues that are accepted should be marked with appropriate labels. -4. Issues that could impact functionality for many users should be considered severe. +4. Issues that could impact functionality for many users should be considered severe. 5. Issues caused by the SDK or chip should not be marked severe, as there usually isn’t much to be done. Common sense should be applied when deciding. Such issues should be documented in the Wiki, for reference by users. 6. Issues with feature requests should be discussed for viability/desirability. 7. Feature requests or changes that are meant to address a very specific/limited use case, especially if at the expense of increased code complexity, may be denied, or may be required to be redesigned, generalized, or simplified. @@ -100,7 +102,7 @@ A CLA is a legal document in which you state _you are entitled to contribute the CLA is a safety because it also ensures that once you have provided a contribution, you cannot try to withdraw permission for its use at a later date. People can therefore use that software, confident that they will not be asked to stop using pieces of the code at a later date. -A __license__ grants "outbound" rights to the user of project. +A __license__ grants "outbound" rights to the user of project. A __CLA__ enables a contributor to grant "inbound" rights to a project. diff --git a/README.md b/README.md index aaf844a2bae0..dc30dfccbacd 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ -## Sonoff-Tasmota +# Sonoff-Tasmota -Alternative firmware for _ESP8266 based devices_ like [iTead](https://www.itead.cc/) _**Sonoff**_ with **web UI, rules and timers, OTA updates, custom device templates and sensor support**. Allows control over **MQTT**, **HTTP**, **Serial** and **KNX** for integrations with smart home systems. Written for Arduino IDE and PlatformIO. +LogoAlternative firmware for _ESP8266 based devices_ like [iTead](https://www.itead.cc/) _**Sonoff**_ with **web UI, rules and timers, OTA updates, custom device templates and sensor support**. Allows control over **MQTT**, **HTTP**, **Serial** and **KNX** for integrations with smart home systems. Written for Arduino IDE and PlatformIO. [![GitHub version](https://img.shields.io/github/release/arendst/Sonoff-Tasmota.svg)](https://github.com/arendst/Sonoff-Tasmota/releases/latest) [![GitHub download](https://img.shields.io/github/downloads/arendst/Sonoff-Tasmota/total.svg)](https://github.com/arendst/Sonoff-Tasmota/releases/latest) @@ -15,7 +15,7 @@ If you like **Sonoff-Tasmota**, give it a star, or fork it and contribute! See [RELEASENOTES.md](https://github.com/arendst/Sonoff-Tasmota/blob/development/RELEASENOTES.md) for release information. -In addition to the [release webpage](https://github.com/arendst/Sonoff-Tasmota/releases/latest), the binaries can also be OTA downloaded from http://thehackbox.org/tasmota/release/ +In addition to the [release webpage](https://github.com/arendst/Sonoff-Tasmota/releases/latest), the binaries can be downloaded from http://thehackbox.org/tasmota/release/ ### Development [![Dev Version](https://img.shields.io/badge/development%20version-6.5.0.x-blue.svg)](https://github.com/arendst/Sonoff-Tasmota) @@ -34,10 +34,10 @@ A Sonoff device is not a toy. It uses Mains AC so there is a danger of electrocu We don't take any responsibility nor liability for using this software nor for the installation or any tips, advice, videos, etc. given by any member of this site or any related site. ### Note -Please do not ask to add devices where you can't provide a basic working configuration (other than sonoff). Since there are thousands of them. +Please do not ask to add new devices unless it requires additional code for new features. If the device is not listed as a module, try using [Templates](https://github.com/arendst/Sonoff-Tasmota/wiki/Templates) first. If it is not listed in the [Tasmota Device Templates Repository](http://blakadder.github.io/templates) create your own [Template](https://github.com/arendst/Sonoff-Tasmota/wiki/Templates#creating-your-template-). ### Quick Install -Download one of the released binaries from https://github.com/arendst/Sonoff-Tasmota/releases and flash it to your hardware as documented in the wiki. +Download one of the released binaries from https://github.com/arendst/Sonoff-Tasmota/releases and flash it to your hardware as [documented in the wiki](https://github.com/arendst/Sonoff-Tasmota/wiki/Flashing). ### Important User Compilation Information If you want to compile Sonoff-Tasmota yourself keep in mind the following: @@ -48,7 +48,7 @@ If you want to compile Sonoff-Tasmota yourself keep in mind the following: ### Version Information - Sonoff-Tasmota provides all (Sonoff) modules in one file and starts with module Sonoff Basic. -- Once uploaded select module using the configuration webpage or the commands ```Modules``` and ```Module```. +- Once uploaded, select [Module](https://github.com/arendst/Sonoff-Tasmota/wiki/Modules) using the configuration webpage, the commands ```Modules``` and ```Module``` or configure the [Template](https://github.com/arendst/Sonoff-Tasmota/wiki/Templates) for your device - After reboot select config menu again or use commands ```GPIOs``` and ```GPIO``` to change GPIO with desired sensor. ### Migration Information @@ -62,52 +62,12 @@ See [wiki migration path](https://github.com/arendst/Sonoff-Tasmota/wiki/Upgrade ### Support Information -See [Wiki](https://github.com/arendst/Sonoff-Tasmota/wiki) for more information.
+For a database of supported devices see [Tasmota Device Templates Repository](https://blakadder.github.io/templates) + +See [Wiki](https://github.com/arendst/Sonoff-Tasmota/wiki) for use instructions and how-to's.
See [Community](https://groups.google.com/d/forum/sonoffusers) for forum.
Visit [Discord Chat](https://discord.gg/Ks2Kzd4) for discussions and troubleshooting. -The following devices are supported: -- [iTead Sonoff Basic (R2)](https://www.itead.cc/smart-home/sonoff-wifi-wireless-switch-1.html) -- [iTead Sonoff RF](https://www.itead.cc/smart-home/sonoff-rf.html) -- [iTead Sonoff SV](https://www.itead.cc/smart-home/sonoff-sv.html) -- [iTead Sonoff TH10/TH16 with temperature sensor](https://www.itead.cc/smart-home/sonoff-th.html) -- [iTead Sonoff Dual (R2)](https://www.itead.cc/smart-home/sonoff-dual.html) -- [iTead Sonoff Pow with Energy Monitoring](https://www.itead.cc/smart-home/sonoff-pow.html) -- [iTead Sonoff Pow R2 with Energy Monitoring](https://www.itead.cc/sonoff-pow-r2.html) -- [iTead Sonoff 4CH (R2)](https://www.itead.cc/smart-home/sonoff-4ch.html) -- [iTead Sonoff 4CH Pro (R2)](https://www.itead.cc/smart-home/sonoff-4ch-pro.html) -- [iTead Sonoff S20 Smart Socket](https://www.itead.cc/smart-socket.html) -- [Sonoff S22 Smart Socket](https://github.com/arendst/Sonoff-Tasmota/issues/627) -- [iTead Sonoff S26 Smart Socket](https://www.itead.cc/sonoff-s26-wifi-smart-plug.html) -- [iTead Sonoff S31 Smart Socket with Energy Monitoring](https://www.itead.cc/sonoff-s31.html) -- [iTead Slampher](https://www.itead.cc/slampher.html) -- [iTead Sonoff Touch](https://www.itead.cc/sonoff-touch.html) -- [iTead Sonoff T1](https://www.itead.cc/sonoff-t1.html) -- [iTead Sonoff SC](https://www.itead.cc/sonoff-sc.html) -- [iTead Sonoff Led](https://www.itead.cc/sonoff-led.html) -- [iTead Sonoff BN-SZ01 Ceiling Led](https://www.itead.cc/bn-sz01.html) -- [iTead Sonoff B1](https://www.itead.cc/sonoff-b1.html) -- [iTead Sonoff iFan02](https://www.itead.cc/sonoff-ifan02-wifi-smart-ceiling-fan-with-light.html) -- [iTead Sonoff RF Bridge 433](https://www.itead.cc/sonoff-rf-bridge-433.html) -- [iTead Sonoff Dev](https://www.itead.cc/sonoff-dev.html) -- [iTead 1 Channel Switch 5V / 12V](https://www.itead.cc/smart-home/inching-self-locking-wifi-wireless-switch.html) -- [iTead Motor Clockwise/Anticlockwise](https://www.itead.cc/smart-home/motor-reversing-wifi-wireless-switch.html) -- [Electrodragon IoT Relay Board](http://www.electrodragon.com/product/wifi-iot-relay-board-based-esp8266/) -- AI Light or any my9291 compatible RGBW LED bulb -- H801 PWM LED controller -- [MagicHome PWM LED controller](https://github.com/arendst/Sonoff-Tasmota/wiki/MagicHome-LED-strip-controller) -- AriLux AL-LC01, AL-LC06 and AL-LC11 PWM LED controller -- [Supla device - Espablo-inCan mod. for electrical Installation box](https://forum.supla.org/viewtopic.php?f=33&t=2188) -- [BlitzWolf BW-SHP2 Smart Socket with Energy Monitoring](https://www.banggood.com/BlitzWolf-BW-SHP2-Smart-WIFI-Socket-EU-Plug-220V-16A-Work-with-Amazon-Alexa-Google-Assistant-p-1292899.html) -- [Luani HVIO board](https://luani.de/projekte/esp8266-hvio/) -- [Wemos D1 mini](https://wiki.wemos.cc/products:d1:d1_mini) -- [HuaFan Smart Socket](https://github.com/arendst/Sonoff-Tasmota/wiki/HuaFan-Smart-Socket) -- [Hyleton-313 Smart Plug](https://github.com/arendst/Sonoff-Tasmota/wiki/Hyleton-313-Smart-Plug) -- [Allterco Shelly 1](https://shelly.cloud/shelly1-open-source/) -- [Allterco Shelly 2 with Energy Monitoring](https://shelly.cloud/shelly2/) -- NodeMcu and Ledunia -- [KS-602 based switches like GresaTek, Jesiya, NewRice, Lyasi etc](https://ucexperiment.wordpress.com/2017/11/14/reprogramming-a-lyasi-wifi-wall-switch-with-esp8285/) - ### Contribute You can contribute to Sonoff-Tasmota by - providing Pull Requests (Features, Proof of Concepts, Language files or Fixes) @@ -162,7 +122,7 @@ People helping to keep the show on the road: - Emontnemery for his HomeAssistant Discovery concept and many code tuning tips - Aidan Mountford for his HSB support - Daniel Ztolnai for his Serial Bridge implementation -- Gerhard Mutz for his SGP30, Sunrise/Sunset and display support drivers +- Gerhard Mutz for multiple sensor & display drivers, Sunrise/Sunset, and scripting - Nuno Ferreira for his HC-SR04 driver - Adrian Scillato for his (security)fixes and implementing and maintaining KNX - Gennaro Tortone for implementing and maintaining Eastron drivers @@ -173,6 +133,7 @@ People helping to keep the show on the road: - Frogmore42 and Jason2866 for providing many issue answers - Blakadder for editing the wiki and providing template management - Stephan Hadinger for refactoring light driver and enhancing HueEmulation +- tmo for designing the official logo - Many more providing Tips, Wips, Pocs or PRs ### License diff --git a/REFERENCE.md b/REFERENCE.md index 22bd77899db7..6d2c08747e11 100644 --- a/REFERENCE.md +++ b/REFERENCE.md @@ -1,4 +1,7 @@ -## Tasmota Reference +Logo + +# Reference + Tasmota backgound information. ## Supported Smart Switch with Energy Monitoring GPIO usage diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 0df82245601b..1209b76bf3da 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -1,3 +1,7 @@ +Logo + +# RELEASE NOTES + ## Migration Information See [wiki migration path](https://github.com/arendst/Sonoff-Tasmota/wiki/Upgrade#migration-path) for instructions how to migrate to a major version. Pay attention to the following version breaks due to dynamic settings updates: @@ -6,11 +10,10 @@ See [wiki migration path](https://github.com/arendst/Sonoff-Tasmota/wiki/Upgrade 3. Migrate to **Sonoff-Tasmota 5.14** 4. Migrate to **Sonoff-Tasmota 6.x** -## Release notes -### Core version 2.3.0 vs 2.4.2 +## Core version 2.3.0 vs 2.4.2 This release is based on ESP8266/Arduino library core 2.3.0 (again) as some people encountered wifi related issues on core 2.4.2. For others core 2.4.2 is working just fine. Both version are available from http://thehackbox.org/tasmota/release/ -### Change in default initial configuration tool +## Change in default initial configuration tool Firmware binary **sonoff-classic.bin** supports **WifiManager, Wps and SmartConfig** for initial configuration. The default tool is **Wps**. To save memory space all other binaries support **WifiManager only**. @@ -104,7 +107,7 @@ The following binary downloads have been compiled with ESP8266/Arduino library c Core version **2.4.2** binaries can be found at http://thehackbox.org/tasmota/release/020402/ -### Available Features and Sensors +## Available Features and Sensors | Feature or Sensor | minimal | basic | classic | sonoff | knx | sensors | display | Remarks |-----------------------|---------|-------|---------|--------|------|---------|---------|-------- diff --git a/SUPPORT.md b/SUPPORT.md index a0780c77c552..26c770b80499 100644 --- a/SUPPORT.md +++ b/SUPPORT.md @@ -1,21 +1,23 @@ -# Sonoff-Tasmota Support +Logo + +# Support If you're looking for support on **Sonoff-Tasmota** there are some options available: -### Documentation: +## Documentation: * [Wiki Pages](https://github.com/arendst/Sonoff-Tasmota/wiki): For information on how to Flash Tasmota, configure and use it. * [FAQ](https://github.com/arendst/Sonoff-Tasmota/wiki/FAQ): For information on common problems and solutions. * [Troubleshooting Information](https://github.com/arendst/Sonoff-Tasmota/wiki/Troubleshooting): For ways to debug and troubleshoot. * [Commands Information](https://github.com/arendst/Sonoff-Tasmota/wiki/Commands): For information on all the commands supported by Tasmota. -### Support's Community: +## Support's Community: * [Tasmota Forum](https://groups.google.com/d/forum/sonoffusers): For usage and discussions. * [Tasmota Support Chat](https://discord.gg/Ks2Kzd4): For support, troubleshooting and general questions. You have better chances to get fast answers from members of the Tasmota Community. * [Search in Issues](https://github.com/arendst/Sonoff-Tasmota/issues): You might find an answer to your question by searching current or closed issues. -### Developers' Community: +## Developers' Community: * [Bug Report](https://github.com/arendst/Sonoff-Tasmota/issues/new?template=Bug_report.md): For reporting Bugs of Tasmota Software. * [Feature Request](https://github.com/arendst/Sonoff-Tasmota/issues/new?template=Feature_request.md): For requesting features/functions to Tasmota Software. diff --git a/TEMPLATE.md b/TEMPLATE.md index 67431f651bf6..33465a0e90fa 100644 --- a/TEMPLATE.md +++ b/TEMPLATE.md @@ -1,4 +1,7 @@ -## Sonoff-Tasmota template information +Logo + +# Template information + Sonoff-Tasmota uses Device or Module information to control peripherals connected to GPIOs. This information is stored in the ``sonoff_template.h`` file as a device specific template. The template contains information about what GPIO should be connected to what peripheral and what GPIO may be configured online using the ``GPIO`` command or GUI Configure Module menu. In addition a device may need specific coding to process the data from these peripherals. The module number as provided by the ``Modules`` command is used to select this coding. Starting with version 6.4.1.16 Sonoff-Tasmota Modules can be extended by users online using a template. To provide easy processing by Sonoff-Tasmota a user template is written as JSON text and could look like this: @@ -83,4 +86,4 @@ The following command will update the flag of a stored template ``Template {"FLAG":1}`` The following command will update the base of a stored template to Generic -``Template {"BASE":0}`` \ No newline at end of file +``Template {"BASE":0}`` diff --git a/lib/IRremoteESP8266-2.5.2.03/examples/IRMQTTServer/IRMQTTServer.ino b/lib/IRremoteESP8266-2.5.2.03/examples/IRMQTTServer/IRMQTTServer.ino deleted file mode 100644 index 7851cf5dc27d..000000000000 --- a/lib/IRremoteESP8266-2.5.2.03/examples/IRMQTTServer/IRMQTTServer.ino +++ /dev/null @@ -1,1608 +0,0 @@ -/* - * Send & receive arbitrary IR codes via a web server or MQTT. - * Copyright David Conran 2016, 2017, 2018 - * - * NOTE: An IR LED circuit *MUST* be connected to ESP8266 GPIO4 (D2) if - * you want to send IR messages. See IR_LED below. - * A compatible IR RX modules *MUST* be connected to ESP8266 GPIO14 (D5) - * if you want to capture & decode IR nessages. See IR_RX below. - * - * WARN: This is very advanced & complicated example code. Not for beginners. - * You are strongly suggested to try & look at other example code first. - * - * # Instructions - * - * ## Before First Boot (i.e. Compile time) - * - Either: - * o Set the MQTT_SERVER define below to the address of your MQTT server. - * or - * o Disable MQTT by commenting out the line "#define MQTT_ENABLE" down below. - * - * - Arduino IDE: - * o Install the following libraries via Library Manager - * - WiFiManager (https://github.com/tzapu/WiFiManager) (Version >= 0.14) - * - PubSubClient (https://pubsubclient.knolleary.net/) - * o You MUST change to have the following (or larger) value: - * #define MQTT_MAX_PACKET_SIZE 512 - * - PlatformIO IDE: - * If you are using PlatformIO, this should already been done for you in - * the accompanying platformio.ini file. - * - * ## First Boot (Initial setup) - * The ESP8266 board will boot into the WiFiManager's AP mode. - * i.e. It will create a WiFi Access Point with a SSID like: "ESP123456" etc. - * Connect to that SSID. Then point your browser to http://192.168.4.1/ and - * configure the ESP8266 to connect to your desired WiFi network. - * It will remember the new WiFi connection details on next boot. - * More information can be found here: - * https://github.com/tzapu/WiFiManager#how-it-works - * - * If you need to reset the WiFi settings, visit: - * http:///reset - * - * ## Normal Use (After setup) - * Enter 'http:///ir?type=7&code=E0E09966 - * http:///ir?type=4&code=0xf50&bits=12 - * http:///ir?code=C1A2E21D&repeats=8&type=19 - * http:///ir?type=31&code=40000,1,1,96,24,24,24,48,24,24,24,24,24,48,24,24,24,24,24,48,24,24,24,24,24,24,24,24,1058 - * http:///ir?type=18&code=190B8050000000E0190B8070000010f0 - * http:///ir?repeats=1&type=25&code=0000,006E,0022,0002,0155,00AA,0015,0040,0015,0040,0015,0015,0015,0015,0015,0015,0015,0015,0015,0015,0015,0040,0015,0040,0015,0015,0015,0040,0015,0015,0015,0015,0015,0015,0015,0040,0015,0015,0015,0015,0015,0040,0015,0040,0015,0015,0015,0015,0015,0015,0015,0015,0015,0015,0015,0040,0015,0015,0015,0015,0015,0040,0015,0040,0015,0040,0015,0040,0015,0040,0015,0640,0155,0055,0015,0E40 - * - * or - * - * Send a MQTT message to the topic 'ir_server/send' using the following - * format (Order is important): - * protocol_num,hexcode e.g. 7,E0E09966 which is Samsung(7), Power On code, - * default bit size, default nr. of repeats. - * protocol_num,hexcode,bits e.g. 4,f50,12 which is Sony(4), Power Off code, - * 12 bits & default nr. of repeats. - * protocol_num,hexcode,bits,repeats e.g. 19,C1A2E21D,0,8 which is - * Sherwood(19), Vol Up, default bit size & - * repeated 8 times. - * 30,frequency,raw_string e.g. 30,38000,9000,4500,500,1500,500,750,500,750 - * Raw (30) @ 38kHz with a raw code of "9000,4500,500,1500,500,750,500,750" - * 31,code_string e.g. 31,40000,1,1,96,24,24,24,48,24,24,24,24,24,48,24,24,24,24,24,48,24,24,24,24,24,24,24,24,1058 - * GlobalCache (31) & "40000,1,1,96,..." (Sony Vol Up) - * 25,Rrepeats,hex_code_string e.g. 25,R1,0000,006E,0022,0002,0155,00AA,0015,0040,0015,0040,0015,0015,0015,0015,0015,0015,0015,0015,0015,0015,0015,0040,0015,0040,0015,0015,0015,0040,0015,0015,0015,0015,0015,0015,0015,0040,0015,0015,0015,0015,0015,0040,0015,0040,0015,0015,0015,0015,0015,0015,0015,0015,0015,0015,0015,0040,0015,0015,0015,0015,0015,0040,0015,0040,0015,0040,0015,0040,0015,0040,0015,0640,0155,0055,0015,0E40 - * Pronto (25), 1 repeat, & "0000 006E 0022 0002 ..." (Sherwood Amp Tape Input) - * ac_protocol_num,really_long_hexcode e.g. 18,190B8050000000E0190B8070000010F0 - * Kelvinator (18) Air Con on, Low Fan, 25 deg etc. - * NOTE: Ensure you zero-pad to the correct number of - * digits for the bit/byte size you want to send - * as some A/C units have units have different - * sized messages. e.g. Fujitsu A/C units. - * In short: - * No spaces after/before commas. - * Values are comma separated. - * The first value is always in Decimal. - * For simple protocols, the next value (hexcode) is always hexadecimal. - * The optional bit size is in decimal. - * - * Unix command line usage example: - * # Install a MQTT client - * $ sudo apt install mosquitto-clients - * # Send a 32-bit NEC code of 0x1234abcd via MQTT. - * $ mosquitto_pub -h 10.20.0.253 -t ir_server/send -m '3,1234abcd,32' - * - * This server will send (back) what ever IR message it just transmitted to - * the MQTT topic 'ir_server/sent' to confirm it has been performed. This works - * for messages requested via MQTT or via HTTP. - * Note: Other status messages are also sent to 'ir_server/sent' from time to - * time. - * Unix command line usage example: - * # Listen to MQTT acknowledgements. - * $ mosquitto_sub -h 10.20.0.253 -t ir_server/sent - * - * Incoming IR messages (from an IR remote control) will be transmitted to - * the MQTT topic 'ir_server/received'. The MQTT message will be formatted - * similar to what is required to for the 'sent' topic. - * e.g. "3,C1A2F00F,32" (Protocol,Value,Bits) for simple codes - * or "18,110B805000000060110B807000001070" (Protocol,Value) for complex codes - * Note: If the protocol is listed as -1, then that is an UNKNOWN IR protocol. - * You can't use that to recreate/resend an IR message. It's only for - * matching purposes and shouldn't be trusted. - * - * Unix command line usage example: - * # Listen via MQTT for IR messages captured by this server. - * $ mosquitto_sub -h 10.20.0.253 -t ir_server/received - * - * If DEBUG is turned on, there is additional information printed on the Serial - * Port. - * - * ## Updates - * You can upload new firmware over the air (OTA) via the form on the device's - * main page. No need to connect to the device again via USB. \o/ - * Your WiFi settings should be remembered between updates. \o/ \o/ - * - * Copyright Notice: - * Code for this has been borrowed from lots of other OpenSource projects & - * resources. I'm *NOT* claiming complete Copyright ownership of all the code. - * Likewise, feel free to borrow from this as much as you want. - */ - -#define MQTT_ENABLE // Comment this out if you don't want to use MQTT at all. - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#ifdef MQTT_ENABLE -// -------------------------------------------------------------------- -// * * * IMPORTANT * * * -// You must change to have the following value. -// #define MQTT_MAX_PACKET_SIZE 512 -// -------------------------------------------------------------------- -#include -#endif // MQTT_ENABLE -#include -#include - -// Configuration parameters -// GPIO the IR LED is connected to/controlled by. GPIO 4 = D2. -#define IR_LED 4 -// define IR_LED 3 // For an ESP-01 we suggest you use RX/GPIO3/Pin 7. -// -// GPIO the IR RX module is connected to/controlled by. GPIO 14 = D5. -// Comment this out to disable receiving/decoding IR messages entirely. -#define IR_RX 14 -const uint16_t kHttpPort = 80; // The TCP port the HTTP server is listening on. -// Name of the device you want in mDNS. -// NOTE: Changing this will change the MQTT path too unless you override it -// via MQTTprefix below. -#define HOSTNAME "ir_server" - -// We obtain our network config via DHCP by default but allow an easy way to -// use a static IP config. -#define USE_STATIC_IP false // Change to 'true' if you don't want to use DHCP. -#if USE_STATIC_IP -const IPAddress kIPAddress = IPAddress(10, 0, 1, 78); -const IPAddress kGateway = IPAddress(10, 0, 1, 1); -const IPAddress kSubnetMask = IPAddress(255, 255, 255, 0); -#endif // USE_STATIC_IP - -#ifdef MQTT_ENABLE -// Address of your MQTT server. -#define MQTT_SERVER "10.20.0.253" // <=- CHANGE ME -const uint16_t kMqttPort = 1883; // Default port used by MQTT servers. -// Set if your MQTT server requires a Username & Password to connect. -const char* mqtt_user = ""; -const char* mqtt_password = ""; -const uint32_t kMqttReconnectTime = 5000; // Delay(ms) between reconnect tries. - -#define MQTTprefix HOSTNAME // Change this if you want the MQTT topic to be - // independent of the hostname. -#define MQTTack MQTTprefix "/sent" // Topic we send back acknowledgements on -#define MQTTcommand MQTTprefix "/send" // Topic we get new commands from. -#define MQTTrecv MQTTprefix "/received" // Topic we send received IRs to. -#endif // MQTT_ENABLE - -// HTML arguments we will parse for IR code information. -#define argType "type" -#define argData "code" -#define argBits "bits" -#define argRepeat "repeats" -// Let's use a larger than normal buffer so we can handle AirCon remote codes. -const uint16_t kCaptureBufferSize = 1024; -#if DECODE_AC -// Some A/C units have gaps in their protocols of ~40ms. e.g. Kelvinator -// A value this large may swallow repeats of some protocols -const uint8_t kCaptureTimeout = 50; -#else // DECODE_AC -// Suits most messages, while not swallowing many repeats. -const uint8_t kCaptureTimeout = 15; -#endif // DECODE_AC -// Ignore unknown messages with <10 pulses -const uint16_t kMinUnknownSize = 20; - -#define _MY_VERSION_ "v0.7.0" - -// Disable debug output if any of the IR pins are on the TX (D1) pin. -#if (IR_LED != 1 && IR_RX != 1) -#undef DEBUG -#define DEBUG true // Change to 'false' to disable all serial output. -#else -#undef DEBUG -#define DEBUG false -#endif -// NOTE: Make sure you set your Serial Monitor to the same speed. -#define BAUD_RATE 115200 // Serial port Baud rate. - -// Globals -ESP8266WebServer server(kHttpPort); -IRsend irsend = IRsend(IR_LED); -#ifdef IR_RX -IRrecv irrecv(IR_RX, kCaptureBufferSize, kCaptureTimeout, true); -decode_results capture; // Somewhere to store inbound IR messages. -#endif // IR_RX -MDNSResponder mdns; -WiFiClient espClient; -WiFiManager wifiManager; - -uint16_t *codeArray; -uint32_t lastReconnectAttempt = 0; // MQTT last attempt reconnection number -bool boot = true; -bool ir_lock = false; // Primitive locking for gating the IR LED. -uint32_t sendReqCounter = 0; -bool lastSendSucceeded = false; // Store the success status of the last send. -uint32_t lastSendTime = 0; -int8_t offset; // The calculated period offset for this chip and library. - -#ifdef MQTT_ENABLE -String lastMqttCmd = "None"; -uint32_t lastMqttCmdTime = 0; -uint32_t lastConnectedTime = 0; -uint32_t lastDisconnectedTime = 0; -uint32_t mqttDisconnectCounter = 0; -bool wasConnected = true; -#ifdef IR_RX -String lastIrReceived = "None"; -uint32_t lastIrReceivedTime = 0; -uint32_t irRecvCounter = 0; -#endif // IR_RX - - -// MQTT client parameters -void callback(char* topic, byte* payload, unsigned int length); -PubSubClient mqtt_client(MQTT_SERVER, kMqttPort, callback, espClient); -// Create a unique MQTT client id. -String mqtt_clientid = MQTTprefix + String(ESP.getChipId(), HEX); -#endif // MQTT_ENABLE - -// Debug messages get sent to the serial port. -void debug(String str) { -#ifdef DEBUG - uint32_t now = millis(); - Serial.printf("%07u.%03u: %s\n", now / 1000, now % 1000, str.c_str()); -#endif // DEBUG -} - -String timeSince(uint32_t const start) { - if (start == 0) - return "Never"; - uint32_t diff = 0; - uint32_t now = millis(); - if (start < now) - diff = now - start; - else - diff = UINT32_MAX - start + now; - diff /= 1000; // Convert to seconds. - if (diff == 0) return "Now"; - - // Note: millis() can only count up to 45 days, so uint8_t is safe. - uint8_t days = diff / (60 * 60 * 24); - uint8_t hours = (diff / (60 * 60)) % 24; - uint8_t minutes = (diff / 60) % 60; - uint8_t seconds = diff % 60; - - String result = ""; - if (days) - result += String(days) + " day"; - if (days > 1) result += "s"; - if (hours) - result += " " + String(hours) + " hour"; - if (hours > 1) result += "s"; - if (minutes) - result += " " + String(minutes) + " minute"; - if (minutes > 1) result += "s"; - if (seconds) - result += " " + String(seconds) + " second"; - if (seconds > 1) result += "s"; - result.trim(); - return result + " ago"; -} - -// Quick and dirty check for any unsafe chars in a string -// that may cause HTML shenanigans. e.g. An XSS. -bool hasUnsafeHTMLChars(String input) { - static char unsafe[] = "';!-\"<>=&{}()"; - for (uint8_t i = 0; unsafe[i]; i++) - if (input.indexOf(unsafe[i]) != -1) return true; - return false; -} - -// Root web page with example usage etc. -void handleRoot() { - server.send(200, "text/html", - "IR MQTT server" - "" - "

ESP8266 IR MQTT Server

" - "

" - "

Information

" - "

IP address: " + WiFi.localIP().toString() + "
" - "Booted: " + timeSince(1) + "
" + - "Version: " _MY_VERSION_ "
" - "Period Offset: " + String(offset) + "us
" - "IR Lib Version: " _IRREMOTEESP8266_VERSION_ "
" - "ESP8266 Core Version: " + ESP.getCoreVersion() + "
" - "IR Send GPIO: " + String(IR_LED) + "
" - "Total send requests: " + String(sendReqCounter) + "
" - "Last message sent: " + String(lastSendSucceeded ? "Ok" : "FAILED") + - " (" + timeSince(lastSendTime) + ")
" -#ifdef IR_RX - "IR Recv GPIO: " + String(IR_RX) + "
" - "Total IR Received: " + String(irRecvCounter) + "
" - "Last IR Received: " + lastIrReceived + - " (" + timeSince(lastIrReceivedTime) + ")
" -#endif // IR_RX - "

" -#ifdef MQTT_ENABLE - "

MQTT Information

" - "

Server: " MQTT_SERVER ":" + String(kMqttPort) + " (" + - (mqtt_client.connected() ? "Connected " + timeSince(lastDisconnectedTime) - : "Disconnected " + timeSince(lastConnectedTime)) + - ")
" - "Disconnections: " + String(mqttDisconnectCounter - 1) + "
" - "Client id: " + mqtt_clientid + "
" - "Command topic: " MQTTcommand "
" - "Acknowledgements topic: " MQTTack "
" -#ifdef IR_RX - "IR Received topic: " MQTTrecv "
" -#endif // IR_RX - "Last MQTT command seen: " + - // lastMqttCmd is unescaped untrusted input. - // Avoid any possible HTML/XSS when displaying it. - (hasUnsafeHTMLChars(lastMqttCmd) ? - "Contains unsafe HTML characters" : lastMqttCmd) + - " (" + timeSince(lastMqttCmdTime) + ")

" -#endif // MQTT_ENABLE - "

" - "

Hardcoded examples

" - "

" - "Sherwood Amp On (GlobalCache)

" - "

" - "Sherwood Amp Off (Raw)

" - "

" - "Sherwood Amp Input TAPE (Pronto)

" - "

TV on (Samsung)

" - "

Power Off (Sony 12bit)

" - "

" - "

Send a simple IR message

" - "

" - "Type: " - "" - " Code: 0x" - " Bit size: " - "" - " Repeats: " - " " - "
" - "

" - "

Send an IRremote Raw IR message

" - "

" - "" - "String: (freq,array data) " - " " - "
" - "

" - "

Send a GlobalCache" - " IR message

" - "

" - "" - "String: 1:1,1," - " " - "
" - "

" - "

Send a Pronto code IR message

" - "

" - "" - "String (comma separated): " - " Repeats: " - " " - "
" - "

" - "

Send an Air Conditioner IR message

" - "

" - "Type: " - "" - " State code: 0x" - "" - " " - "
" - "

" - "

Update IR Server firmware

" - "Warning:
" - "Updating your firmware may screw up your access to the device. " - "If you are going to use this, know what you are doing first " - "(and you probably do).
" - "

" - "Firmware to upload: " - "" - "
" - ""); -} - -// Reset web page -void handleReset() { - server.send(200, "text/html", - "Reset Config" - "" - "

Resetting the WiFiManager config back to defaults.

" - "

Device restarting. Try connecting in a few seconds.

" - ""); - // Do the reset. - wifiManager.resetSettings(); - delay(10); - ESP.restart(); - delay(1000); -} - -// Parse an Air Conditioner A/C Hex String/code and send it. -// Args: -// irType: Nr. of the protocol we need to send. -// str: A hexadecimal string containing the state to be sent. -// Returns: -// bool: Successfully sent or not. -bool parseStringAndSendAirCon(const uint16_t irType, const String str) { - uint8_t strOffset = 0; - uint8_t state[kStateSizeMax] = {0}; // All array elements are set to 0. - uint16_t stateSize = 0; - - if (str.startsWith("0x") || str.startsWith("0X")) - strOffset = 2; - // Calculate how many hexadecimal characters there are. - uint16_t inputLength = str.length() - strOffset; - if (inputLength == 0) { - debug("Zero length AirCon code encountered. Ignored."); - return false; // No input. Abort. - } - - switch (irType) { // Get the correct state size for the protocol. - case KELVINATOR: - stateSize = kKelvinatorStateLength; - break; - case TOSHIBA_AC: - stateSize = kToshibaACStateLength; - break; - case DAIKIN: - stateSize = kDaikinStateLength; - break; - case ELECTRA_AC: - stateSize = kElectraAcStateLength; - break; - case MITSUBISHI_AC: - stateSize = kMitsubishiACStateLength; - break; - case PANASONIC_AC: - stateSize = kPanasonicAcStateLength; - break; - case TROTEC: - stateSize = kTrotecStateLength; - break; - case ARGO: - stateSize = kArgoStateLength; - break; - case GREE: - stateSize = kGreeStateLength; - break; - case FUJITSU_AC: - // Fujitsu has four distinct & different size states, so make a best guess - // which one we are being presented with based on the number of - // hexadecimal digits provided. i.e. Zero-pad if you need to to get - // the correct length/byte size. - stateSize = inputLength / 2; // Every two hex chars is a byte. - // Use at least the minimum size. - stateSize = std::max(stateSize, - (uint16_t) (kFujitsuAcStateLengthShort - 1)); - // If we think it isn't a "short" message. - if (stateSize > kFujitsuAcStateLengthShort) - // Then it has to be at least the smaller version of the "normal" size. - stateSize = std::max(stateSize, (uint16_t) (kFujitsuAcStateLength - 1)); - // Lastly, it should never exceed the maximum "normal" size. - stateSize = std::min(stateSize, kFujitsuAcStateLength); - break; - case HAIER_AC: - stateSize = kHaierACStateLength; - break; - case HAIER_AC_YRW02: - stateSize = kHaierACYRW02StateLength; - break; - case HITACHI_AC: - stateSize = kHitachiAcStateLength; - break; - case HITACHI_AC1: - stateSize = kHitachiAc1StateLength; - break; - case HITACHI_AC2: - stateSize = kHitachiAc2StateLength; - break; - case WHIRLPOOL_AC: - stateSize = kWhirlpoolAcStateLength; - break; - case SAMSUNG_AC: - // Samsung has two distinct & different size states, so make a best guess - // which one we are being presented with based on the number of - // hexadecimal digits provided. i.e. Zero-pad if you need to to get - // the correct length/byte size. - stateSize = inputLength / 2; // Every two hex chars is a byte. - // Use at least the minimum size. - stateSize = std::max(stateSize, (uint16_t) (kSamsungAcStateLength)); - // If we think it isn't a "normal" message. - if (stateSize > kSamsungAcStateLength) - // Then it probably the extended size. - stateSize = std::max(stateSize, - (uint16_t) (kSamsungAcExtendedStateLength)); - // Lastly, it should never exceed the maximum "extended" size. - stateSize = std::min(stateSize, kSamsungAcExtendedStateLength); - break; - case MWM: - // MWM has variable size states, so make a best guess - // which one we are being presented with based on the number of - // hexadecimal digits provided. i.e. Zero-pad if you need to to get - // the correct length/byte size. - stateSize = inputLength / 2; // Every two hex chars is a byte. - // Use at least the minimum size. - stateSize = std::max(stateSize, (uint16_t) 3); - // Cap the maximum size. - stateSize = std::min(stateSize, kStateSizeMax); - break; - default: // Not a protocol we expected. Abort. - debug("Unexpected AirCon protocol detected. Ignoring."); - return false; - } - if (inputLength > stateSize * 2) { - debug("AirCon code to large for the given protocol."); - return false; - } - - // Ptr to the least significant byte of the resulting state for this protocol. - uint8_t *statePtr = &state[stateSize - 1]; - - // Convert the string into a state array of the correct length. - for (uint16_t i = 0; i < inputLength; i++) { - // Grab the next least sigificant hexadecimal digit from the string. - uint8_t c = tolower(str[inputLength + strOffset - i - 1]); - if (isxdigit(c)) { - if (isdigit(c)) - c -= '0'; - else - c = c - 'a' + 10; - } else { - debug("Aborting! Non-hexadecimal char found in AirCon state: " + str); - return false; - } - if (i % 2 == 1) { // Odd: Upper half of the byte. - *statePtr += (c << 4); - statePtr--; // Advance up to the next least significant byte of state. - } else { // Even: Lower half of the byte. - *statePtr = c; - } - } - - // Make the appropriate call for the protocol type. - switch (irType) { -#if SEND_KELVINATOR - case KELVINATOR: - irsend.sendKelvinator(reinterpret_cast(state)); - break; -#endif -#if SEND_TOSHIBA_AC - case TOSHIBA_AC: - irsend.sendToshibaAC(reinterpret_cast(state)); - break; -#endif -#if SEND_DAIKIN - case DAIKIN: - irsend.sendDaikin(reinterpret_cast(state)); - break; -#endif -#if MITSUBISHI_AC - case MITSUBISHI_AC: - irsend.sendMitsubishiAC(reinterpret_cast(state)); - break; -#endif -#if SEND_TROTEC - case TROTEC: - irsend.sendTrotec(reinterpret_cast(state)); - break; -#endif -#if SEND_ARGO - case ARGO: - irsend.sendArgo(reinterpret_cast(state)); - break; -#endif -#if SEND_GREE - case GREE: - irsend.sendGree(reinterpret_cast(state)); - break; -#endif -#if SEND_FUJITSU_AC - case FUJITSU_AC: - irsend.sendFujitsuAC(reinterpret_cast(state), stateSize); - break; -#endif -#if SEND_HAIER_AC - case HAIER_AC: - irsend.sendHaierAC(reinterpret_cast(state)); - break; -#endif -#if SEND_HAIER_AC_YRW02 - case HAIER_AC_YRW02: - irsend.sendHaierACYRW02(reinterpret_cast(state)); - break; -#endif -#if SEND_HITACHI_AC - case HITACHI_AC: - irsend.sendHitachiAC(reinterpret_cast(state)); - break; -#endif -#if SEND_HITACHI_AC1 - case HITACHI_AC1: - irsend.sendHitachiAC1(reinterpret_cast(state)); - break; -#endif -#if SEND_HITACHI_AC2 - case HITACHI_AC2: - irsend.sendHitachiAC2(reinterpret_cast(state)); - break; -#endif -#if SEND_WHIRLPOOL_AC - case WHIRLPOOL_AC: - irsend.sendWhirlpoolAC(reinterpret_cast(state)); - break; -#endif -#if SEND_SAMSUNG_AC - case SAMSUNG_AC: - irsend.sendSamsungAC(reinterpret_cast(state), stateSize); - break; -#endif -#if SEND_ELECTRA_AC - case ELECTRA_AC: - irsend.sendElectraAC(reinterpret_cast(state)); - break; -#endif -#if SEND_PANASONIC_AC - case PANASONIC_AC: - irsend.sendPanasonicAC(reinterpret_cast(state)); - break; -#endif -#if SEND_MWM_ - case MWM: - irsend.sendMWM(reinterpret_cast(state), stateSize); - break; -#endif - default: - debug("Unexpected AirCon type in send request. Not sent."); - return false; - } - return true; // We were successful as far as we can tell. -} - -// Count how many values are in the String. -// Args: -// str: String containing the values. -// sep: Character that separates the values. -// Returns: -// The number of values found in the String. -uint16_t countValuesInStr(const String str, char sep) { - int16_t index = -1; - uint16_t count = 1; - do { - index = str.indexOf(sep, index + 1); - count++; - } while (index != -1); - return count; -} - -// Dynamically allocate an array of uint16_t's. -// Args: -// size: Nr. of uint16_t's need to be in the new array. -// Returns: -// A Ptr to the new array. Restarts the ESP8266 if it fails. -uint16_t * newCodeArray(const uint16_t size) { - uint16_t *result; - - result = reinterpret_cast(malloc(size * sizeof(uint16_t))); - // Check we malloc'ed successfully. - if (result == NULL) { // malloc failed, so give up. - Serial.printf("\nCan't allocate %d bytes. (%d bytes free)\n", - size * sizeof(uint16_t), ESP.getFreeHeap()); - Serial.println("Giving up & forcing a reboot."); - ESP.restart(); // Reboot. - delay(500); // Wait for the restart to happen. - return result; // Should never get here, but just in case. - } - return result; -} - -#if SEND_GLOBALCACHE -// Parse a GlobalCache String/code and send it. -// Args: -// str: A GlobalCache formatted String of comma separated numbers. -// e.g. "38000,1,1,170,170,20,63,20,63,20,63,20,20,20,20,20,20,20,20,20, -// 20,20,63,20,63,20,63,20,20,20,20,20,20,20,20,20,20,20,20,20,63, -// 20,20,20,20,20,20,20,20,20,20,20,20,20,63,20,20,20,63,20,63,20, -// 63,20,63,20,63,20,63,20,1798" -// Note: The leading "1:1,1," of normal GC codes should be removed. -// Returns: -// bool: Successfully sent or not. -bool parseStringAndSendGC(const String str) { - uint16_t count; - uint16_t *code_array; - String tmp_str; - - // Remove the leading "1:1,1," if present. - if (str.startsWith("1:1,1,")) - tmp_str = str.substring(6); - else - tmp_str = str; - - // Find out how many items there are in the string. - count = countValuesInStr(tmp_str, ','); - - // Now we know how many there are, allocate the memory to store them all. - code_array = newCodeArray(count); - - // Now convert the strings to integers and place them in code_array. - count = 0; - uint16_t start_from = 0; - int16_t index = -1; - do { - index = tmp_str.indexOf(',', start_from); - code_array[count] = tmp_str.substring(start_from, index).toInt(); - start_from = index + 1; - count++; - } while (index != -1); - - irsend.sendGC(code_array, count); // All done. Send it. - free(code_array); // Free up the memory allocated. - if (count > 0) - return true; // We sent something. - return false; // We probably didn't. -} -#endif // SEND_GLOBALCACHE - -#if SEND_PRONTO -// Parse a Pronto Hex String/code and send it. -// Args: -// str: A comma-separated String of nr. of repeats, then hexadecimal numbers. -// e.g. "R1,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" -// or -// "0000,0067,0000,0015,0060,0018". i.e. without the Repeat value -// Requires at least kProntoMinLength comma-separated values. -// sendPronto() only supports raw pronto code types, thus so does this. -// repeats: Nr. of times the message is to be repeated. -// This value is ignored if an embeddd repeat is found in str. -// Returns: -// bool: Successfully sent or not. -bool parseStringAndSendPronto(const String str, uint16_t repeats) { - uint16_t count; - uint16_t *code_array; - int16_t index = -1; - uint16_t start_from = 0; - - // Find out how many items there are in the string. - count = countValuesInStr(str, ','); - - // Check if we have the optional embedded repeats value in the code string. - if (str.startsWith("R") || str.startsWith("r")) { - // Grab the first value from the string, as it is the nr. of repeats. - index = str.indexOf(',', start_from); - repeats = str.substring(start_from + 1, index).toInt(); // Skip the 'R'. - start_from = index + 1; - count--; // We don't count the repeats value as part of the code array. - } - - // We need at least kProntoMinLength values for the code part. - if (count < kProntoMinLength) return false; - - // Now we know how many there are, allocate the memory to store them all. - code_array = newCodeArray(count); - - // Rest of the string are values for the code array. - // Now convert the hex strings to integers and place them in code_array. - count = 0; - do { - index = str.indexOf(',', start_from); - // Convert the hexadecimal value string to an unsigned integer. - code_array[count] = strtoul(str.substring(start_from, index).c_str(), - NULL, 16); - start_from = index + 1; - count++; - } while (index != -1); - - irsend.sendPronto(code_array, count, repeats); // All done. Send it. - free(code_array); // Free up the memory allocated. - if (count > 0) - return true; // We sent something. - return false; // We probably didn't. -} -#endif // SEND_PRONTO - -#if SEND_RAW -// Parse an IRremote Raw Hex String/code and send it. -// Args: -// str: A comma-separated String containing the freq and raw IR data. -// e.g. "38000,9000,4500,600,1450,600,900,650,1500,..." -// Requires at least two comma-separated values. -// First value is the transmission frequency in Hz or kHz. -// Returns: -// bool: Successfully sent or not. -bool parseStringAndSendRaw(const String str) { - uint16_t count; - uint16_t freq = 38000; // Default to 38kHz. - uint16_t *raw_array; - - // Find out how many items there are in the string. - count = countValuesInStr(str, ','); - - // We expect the frequency as the first comma separated value, so we need at - // least two values. If not, bail out. - if (count < 2) return false; - count--; // We don't count the frequency value as part of the raw array. - - // Now we know how many there are, allocate the memory to store them all. - raw_array = newCodeArray(count); - - // Grab the first value from the string, as it is the frequency. - int16_t index = str.indexOf(',', 0); - freq = str.substring(0, index).toInt(); - uint16_t start_from = index + 1; - // Rest of the string are values for the raw array. - // Now convert the strings to integers and place them in raw_array. - count = 0; - do { - index = str.indexOf(',', start_from); - raw_array[count] = str.substring(start_from, index).toInt(); - start_from = index + 1; - count++; - } while (index != -1); - - irsend.sendRaw(raw_array, count, freq); // All done. Send it. - free(raw_array); // Free up the memory allocated. - if (count > 0) - return true; // We sent something. - return false; // We probably didn't. -} -#endif // SEND_RAW - -// Parse the URL args to find the IR code. -void handleIr() { - uint64_t data = 0; - String data_str = ""; - int ir_type = 3; // Default to NEC codes. - uint16_t nbits = 0; - uint16_t repeat = 0; - - for (uint16_t i = 0; i < server.args(); i++) { - if (server.argName(i) == argType) - ir_type = atoi(server.arg(i).c_str()); - if (server.argName(i) == argData) { - data = getUInt64fromHex(server.arg(i).c_str()); - data_str = server.arg(i); - } - if (server.argName(i) == argBits) - nbits = atoi(server.arg(i).c_str()); - if (server.argName(i) == argRepeat) - repeat = atoi(server.arg(i).c_str()); - } - debug("New code received via HTTP"); - lastSendSucceeded = sendIRCode(ir_type, data, data_str.c_str(), nbits, - repeat); - handleRoot(); -} - -void handleNotFound() { - String message = "File Not Found\n\n"; - message += "URI: "; - message += server.uri(); - message += "\nMethod: "; - message += (server.method() == HTTP_GET)?"GET":"POST"; - message += "\nArguments: "; - message += server.args(); - message += "\n"; - for (uint8_t i=0; i < server.args(); i++) - message += " " + server.argName(i) + ": " + server.arg(i) + "\n"; - server.send(404, "text/plain", message); -} - -void setup_wifi() { - delay(10); - // We start by connecting to a WiFi network - - wifiManager.setTimeout(300); // Time out after 5 mins. -#if USE_STATIC_IP - // Use a static IP config rather than the one supplied via DHCP. - wifiManager.setSTAStaticIPConfig(kIPAddress, kGateway, kSubnetMask); -#endif // USE_STATIC_IP - if (!wifiManager.autoConnect()) { - debug("Wifi failed to connect and hit timeout."); - delay(3000); - // Reboot. A.k.a. "Have you tried turning it Off and On again?" - ESP.reset(); - delay(5000); - } - - debug("WiFi connected. IP address: " + WiFi.localIP().toString()); -} - -void setup(void) { - irsend.begin(); - offset = irsend.calibrate(); -#if IR_RX -#if DECODE_HASH - // Ignore messages with less than minimum on or off pulses. - irrecv.setUnknownThreshold(kMinUnknownSize); -#endif // DECODE_HASH - irrecv.enableIRIn(); // Start the receiver -#endif // IR_RX - - #ifdef DEBUG - // Use SERIAL_TX_ONLY so that the RX pin can be freed up for GPIO/IR use. - Serial.begin(BAUD_RATE, SERIAL_8N1, SERIAL_TX_ONLY); - while (!Serial) // Wait for the serial connection to be establised. - delay(50); - Serial.println(); - debug("IRMQTTServer " _MY_VERSION_" has booted."); - #endif // DEBUG - - setup_wifi(); - - // Wait a bit for things to settle. - delay(1500); - - lastReconnectAttempt = 0; - - if (mdns.begin(HOSTNAME, WiFi.localIP())) { - debug("MDNS responder started"); - } - - // Setup the root web page. - server.on("/", handleRoot); - // Setup the page to handle web-based IR codes. - server.on("/ir", handleIr); - // Setup a reset page to cause WiFiManager information to be reset. - server.on("/reset", handleReset); - - // Setup the URL to allow Over-The-Air (OTA) firmware updates. - server.on("/update", HTTP_POST, [](){ - server.sendHeader("Connection", "close"); - server.send(200, "text/plain", (Update.hasError())?"FAIL":"OK"); - ESP.restart(); - }, [](){ - HTTPUpload& upload = server.upload(); - if (upload.status == UPLOAD_FILE_START) { - WiFiUDP::stopAll(); - debug("Update: " + upload.filename); - uint32_t maxSketchSpace = (ESP.getFreeSketchSpace() - 0x1000) & - 0xFFFFF000; - if (!Update.begin(maxSketchSpace)) { // start with max available size -#ifdef DEBUG - Update.printError(Serial); -#endif // DEBUG - } - } else if (upload.status == UPLOAD_FILE_WRITE) { - if (Update.write(upload.buf, upload.currentSize) != - upload.currentSize) { -#ifdef DEBUG - Update.printError(Serial); -#endif // DEBUG - } - } else if (upload.status == UPLOAD_FILE_END) { - if (Update.end(true)) { // true to set the size to the current progress - debug("Update Success: " + (String) upload.totalSize + - "\nRebooting..."); - } - } - yield(); - }); - - // Set up an error page. - server.onNotFound(handleNotFound); - - server.begin(); - debug("HTTP server started"); -} - -#ifdef MQTT_ENABLE -// MQTT subscribing to topic -void subscribing(const String topic_name) { - // subscription to topic for receiving data - if (mqtt_client.subscribe(topic_name.c_str())) { - debug("Subscription OK to " + topic_name); - } -} - -bool reconnect() { - // Loop a few times or until we're reconnected - uint16_t tries = 1; - while (!mqtt_client.connected() && tries <= 3) { - int connected = false; - // Attempt to connect - debug("Attempting MQTT connection to " MQTT_SERVER ":" + String(kMqttPort) + - "... "); - if (mqtt_user && mqtt_password) - connected = mqtt_client.connect(mqtt_clientid.c_str(), mqtt_user, - mqtt_password); - else - connected = mqtt_client.connect(mqtt_clientid.c_str()); - if (connected) { - // Once connected, publish an announcement... - mqtt_client.publish(MQTTack, "Connected"); - debug("connected."); - // Subscribing to topic(s) - subscribing(MQTTcommand); - } else { - debug("failed, rc=" + String(mqtt_client.state()) + - " Try again in a bit."); - // Wait for a bit before retrying - delay(tries << 7); // Linear increasing back-off (x128) - } - tries++; - } - return mqtt_client.connected(); -} -#endif // MQTT_ENABLE - -void loop(void) { - server.handleClient(); // Handle any web activity - -#ifdef MQTT_ENABLE - uint32_t now = millis(); - // MQTT client connection management - if (!mqtt_client.connected()) { - if (wasConnected) { - lastDisconnectedTime = now; - wasConnected = false; - mqttDisconnectCounter++; - } - // Reconnect if it's longer than kMqttReconnectTime since we last tried. - if (now - lastReconnectAttempt > kMqttReconnectTime) { - lastReconnectAttempt = now; - debug("client mqtt not connected, trying to connect"); - // Attempt to reconnect - if (reconnect()) { - lastReconnectAttempt = 0; - wasConnected = true; - if (boot) { - mqtt_client.publish(MQTTack, "IR Server just booted"); - boot = false; - } else { - String text = "IR Server just (re)connected to MQTT. " - "Lost connection about " + timeSince(lastConnectedTime); - mqtt_client.publish(MQTTack, text.c_str()); - } - lastConnectedTime = now; - debug("successful client mqtt connection"); - } - } - } else { - lastConnectedTime = now; - // MQTT loop - mqtt_client.loop(); - } -#endif // MQTT_ENABLE -#ifdef IR_RX - // Check if an IR code has been received via the IR RX module. - if (irrecv.decode(&capture)) { - lastIrReceivedTime = millis(); - lastIrReceived = String(capture.decode_type) + "," + - resultToHexidecimal(&capture); - // If it isn't an AC code, add the bits. - if (!hasACState(capture.decode_type)) - lastIrReceived += "," + String(capture.bits); - mqtt_client.publish(MQTTrecv, lastIrReceived.c_str()); - irRecvCounter++; - debug("Incoming IR message sent to MQTT: " + lastIrReceived); - } -#endif // IR_RX - delay(100); -} - -// Arduino framework doesn't support strtoull(), so make our own one. -uint64_t getUInt64fromHex(char const *str) { - uint64_t result = 0; - uint16_t offset = 0; - // Skip any leading '0x' or '0X' prefix. - if (str[0] == '0' && (str[1] == 'x' || str[1] == 'X')) - offset = 2; - for (; isxdigit((unsigned char)str[offset]); offset++) { - char c = str[offset]; - result *= 16; - if (isdigit(c)) /* '0' .. '9' */ - result += c - '0'; - else if (isupper(c)) /* 'A' .. 'F' */ - result += c - 'A' + 10; - else /* 'a' .. 'f'*/ - result += c - 'a' + 10; - } - return result; -} - -// Transmit the given IR message. -// -// Args: -// ir_type: enum of the protocol to be sent. -// code: Numeric payload of the IR message. Most protocols use this. -// code_str: The unparsed code to be sent. Used by complex protocol encodings. -// bits: Nr. of bits in the protocol. 0 means use the protocol's default. -// repeat: Nr. of times the message is to be repeated. (Not all protcols.) -// Returns: -// bool: Successfully sent or not. -bool sendIRCode(int const ir_type, uint64_t const code, char const * code_str, - uint16_t bits, uint16_t repeat) { - // Create a pseudo-lock so we don't try to send two codes at the same time. - while (ir_lock) - delay(20); - ir_lock = true; - - bool success = true; // Assume success. - - // send the IR message. - switch (ir_type) { -#if SEND_RC5 - case RC5: // 1 - if (bits == 0) - bits = kRC5Bits; - irsend.sendRC5(code, bits, repeat); - break; -#endif -#if SEND_RC6 - case RC6: // 2 - if (bits == 0) - bits = kRC6Mode0Bits; - irsend.sendRC6(code, bits, repeat); - break; -#endif -#if SEND_NEC - case NEC: // 3 - if (bits == 0) - bits = kNECBits; - irsend.sendNEC(code, bits, repeat); - break; -#endif -#if SEND_SONY - case SONY: // 4 - if (bits == 0) - bits = kSony12Bits; - repeat = std::max(repeat, kSonyMinRepeat); - irsend.sendSony(code, bits, repeat); - break; -#endif -#if SEND_PANASONIC - case PANASONIC: // 5 - if (bits == 0) - bits = kPanasonicBits; - irsend.sendPanasonic64(code, bits, repeat); - break; -#endif -#if SEND_JVC - case JVC: // 6 - if (bits == 0) - bits = kJvcBits; - irsend.sendJVC(code, bits, repeat); - break; -#endif -#if SEND_SAMSUNG - case SAMSUNG: // 7 - if (bits == 0) - bits = kSamsungBits; - irsend.sendSAMSUNG(code, bits, repeat); - break; -#endif -#if SEND_WHYNTER - case WHYNTER: // 8 - if (bits == 0) - bits = kWhynterBits; - irsend.sendWhynter(code, bits, repeat); - break; -#endif -#if SEND_AIWA_RC_T501 - case AIWA_RC_T501: // 9 - if (bits == 0) - bits = kAiwaRcT501Bits; - repeat = std::max(repeat, kAiwaRcT501MinRepeats); - irsend.sendAiwaRCT501(code, bits, repeat); - break; -#endif -#if SEND_LG - case LG: // 10 - if (bits == 0) - bits = kLgBits; - irsend.sendLG(code, bits, repeat); - break; -#endif -#if SEND_MITSUBISHI - case MITSUBISHI: // 12 - if (bits == 0) - bits = kMitsubishiBits; - repeat = std::max(repeat, kMitsubishiMinRepeat); - irsend.sendMitsubishi(code, bits, repeat); - break; -#endif -#if SEND_DISH - case DISH: // 13 - if (bits == 0) - bits = kDishBits; - repeat = std::max(repeat, kDishMinRepeat); - irsend.sendDISH(code, bits, repeat); - break; -#endif -#if SEND_SHARP - case SHARP: // 14 - if (bits == 0) - bits = kSharpBits; - irsend.sendSharpRaw(code, bits, repeat); - break; -#endif -#if SEND_COOLIX - case COOLIX: // 15 - if (bits == 0) - bits = kCoolixBits; - irsend.sendCOOLIX(code, bits, repeat); - break; -#endif - case DAIKIN: // 16 - case KELVINATOR: // 18 - case MITSUBISHI_AC: // 20 - case GREE: // 24 - case ARGO: // 27 - case TROTEC: // 28 - case TOSHIBA_AC: // 32 - case FUJITSU_AC: // 33 - case HAIER_AC: // 38 - case HAIER_AC_YRW02: // 44 - case HITACHI_AC: // 40 - case HITACHI_AC1: // 41 - case HITACHI_AC2: // 42 - case WHIRLPOOL_AC: // 45 - case SAMSUNG_AC: // 46 - case ELECTRA_AC: // 48 - case PANASONIC_AC: // 49 - case MWM: // 52 - success = parseStringAndSendAirCon(ir_type, code_str); - break; -#if SEND_DENON - case DENON: // 17 - if (bits == 0) - bits = DENON_BITS; - irsend.sendDenon(code, bits, repeat); - break; -#endif -#if SEND_SHERWOOD - case SHERWOOD: // 19 - if (bits == 0) - bits = kSherwoodBits; - repeat = std::max(repeat, kSherwoodMinRepeat); - irsend.sendSherwood(code, bits, repeat); - break; -#endif -#if SEND_RCMM - case RCMM: // 21 - if (bits == 0) - bits = kRCMMBits; - irsend.sendRCMM(code, bits, repeat); - break; -#endif -#if SEND_SANYO - case SANYO_LC7461: // 22 - if (bits == 0) - bits = kSanyoLC7461Bits; - irsend.sendSanyoLC7461(code, bits, repeat); - break; -#endif -#if SEND_RC5 - case RC5X: // 23 - if (bits == 0) - bits = kRC5XBits; - irsend.sendRC5(code, bits, repeat); - break; -#endif -#if SEND_PRONTO - case PRONTO: // 25 - success = parseStringAndSendPronto(code_str, repeat); - break; -#endif -#if SEND_NIKAI - case NIKAI: // 29 - if (bits == 0) - bits = kNikaiBits; - irsend.sendNikai(code, bits, repeat); - break; -#endif -#if SEND_RAW - case RAW: // 30 - success = parseStringAndSendRaw(code_str); - break; -#endif -#if SEND_GLOBALCACHE - case GLOBALCACHE: // 31 - success = parseStringAndSendGC(code_str); - break; -#endif -#if SEND_MIDEA - case MIDEA: // 34 - if (bits == 0) - bits = kMideaBits; - irsend.sendMidea(code, bits, repeat); - break; -#endif -#if SEND_MAGIQUEST - case MAGIQUEST: // 35 - if (bits == 0) - bits = kMagiquestBits; - irsend.sendMagiQuest(code, bits, repeat); - break; -#endif -#if SEND_LASERTAG - case LASERTAG: // 36 - if (bits == 0) - bits = kLasertagBits; - irsend.sendLasertag(code, bits, repeat); - break; -#endif -#if SEND_CARRIER_AC - case CARRIER_AC: // 37 - if (bits == 0) - bits = kCarrierAcBits; - irsend.sendCarrierAC(code, bits, repeat); - break; -#endif -#if SEND_MITSUBISHI2 - case MITSUBISHI2: // 39 - if (bits == 0) - bits = kMitsubishiBits; - repeat = std::max(repeat, kMitsubishiMinRepeat); - irsend.sendMitsubishi2(code, bits, repeat); - break; -#endif -#if SEND_GICABLE - case GICABLE: // 43 - if (bits == 0) - bits = kGicableBits; - repeat = std::max(repeat, kGicableMinRepeat); - irsend.sendGICable(code, bits, repeat); - break; -#endif -#if SEND_LUTRON - case LUTRON: // 47 - if (bits == 0) - bits = kLutronBits; - irsend.sendLutron(code, bits, repeat); - break; -#endif -#if SEND_PIONEER - case PIONEER: // 50 - if (bits == 0) - bits = kPioneerBits; - irsend.sendPioneer(code, bits, repeat); - break; -#endif - -#if SEND_LG - case LG2: // 51 - if (bits == 0) - bits = kLgBits; - irsend.sendLG2(code, bits, repeat); - break; -#endif - default: - // If we got here, we didn't know how to send it. - success = false; - } - lastSendTime = millis(); - // Release the lock. - ir_lock = false; - - // Indicate that we sent the message or not. - if (success) { - sendReqCounter++; - debug("Sent the IR message:"); - } else { - debug("Failed to send IR Message:"); - } - debug("Type: " + String(ir_type)); - // For "long" codes we basically repeat what we got. - if (hasACState((decode_type_t) ir_type) || - ir_type == PRONTO || - ir_type == RAW || - ir_type == GLOBALCACHE) { - debug("Code: "); - debug(code_str); - // Confirm what we were asked to send was sent. -#ifdef MQTT_ENABLE - if (success) { - if (ir_type == PRONTO && repeat > 0) - mqtt_client.publish(MQTTack, (String(ir_type) + ",R" + - String(repeat) + "," + - String(code_str)).c_str()); - else - mqtt_client.publish(MQTTack, (String(ir_type) + "," + - String(code_str)).c_str()); - } -#endif // MQTT_ENABLE - } else { // For "short" codes, we break it down a bit more before we report. - debug("Code: 0x" + uint64ToString(code, 16)); - debug("Bits: " + String(bits)); - debug("Repeats: " + String(repeat)); -#ifdef MQTT_ENABLE - if (success) - mqtt_client.publish(MQTTack, (String(ir_type) + "," + - uint64ToString(code, 16) - + "," + String(bits) + "," + - String(repeat)).c_str()); -#endif // MQTT_ENABLE - } - return success; -} - -#ifdef MQTT_ENABLE -void receivingMQTT(String const topic_name, String const callback_str) { - char* tok_ptr; - uint64_t code = 0; - uint16_t nbits = 0; - uint16_t repeat = 0; - - debug("Receiving data by MQTT topic " + topic_name); - - // Make a copy of the callback string as strtok destroys it. - char* callback_c_str = strdup(callback_str.c_str()); - debug("MQTT Payload (raw): " + callback_str); - // Save the message as the last command seen (global). - lastMqttCmd = callback_str; - lastMqttCmdTime = millis(); - - // Get the numeric protocol type. - int ir_type = strtoul(strtok_r(callback_c_str, ",", &tok_ptr), NULL, 10); - char* next = strtok_r(NULL, ",", &tok_ptr); - // If there is unparsed string left, try to convert it assuming it's hex. - if (next != NULL) { - code = getUInt64fromHex(next); - next = strtok_r(NULL, ",", &tok_ptr); - } else { - // We require at least two value in the string. Give up. - return; - } - // If there is still string left, assume it is the bit size. - if (next != NULL) { - nbits = atoi(next); - next = strtok_r(NULL, ",", &tok_ptr); - } - // If there is still string left, assume it is the repeat count. - if (next != NULL) - repeat = atoi(next); - - free(callback_c_str); - - - // send received MQTT value by IR signal - lastSendSucceeded = sendIRCode( - ir_type, code, - callback_str.substring(callback_str.indexOf(",") + 1).c_str(), - nbits, repeat); -} - -// Callback function, when the gateway receive an MQTT value on the topics -// subscribed this function is called -void callback(char* topic, byte* payload, unsigned int length) { - // In order to republish this payload, a copy must be made - // as the orignal payload buffer will be overwritten whilst - // constructing the PUBLISH packet. - // Allocate the correct amount of memory for the payload copy - byte* payload_copy = reinterpret_cast(malloc(length + 1)); - // Copy the payload to the new buffer - memcpy(payload_copy, payload, length); - - // Conversion to a printable string - payload_copy[length] = '\0'; - String callback_string = String(reinterpret_cast(payload_copy)); - String topic_name = String(reinterpret_cast(topic)); - - // launch the function to treat received data - receivingMQTT(topic_name, callback_string); - - // Free the memory - free(payload_copy); -} -#endif // MQTT_ENABLE diff --git a/lib/IRremoteESP8266-2.5.2.03/examples/IRrecvDump/platformio.ini b/lib/IRremoteESP8266-2.5.2.03/examples/IRrecvDump/platformio.ini deleted file mode 100644 index eeb8d1f2eb93..000000000000 --- a/lib/IRremoteESP8266-2.5.2.03/examples/IRrecvDump/platformio.ini +++ /dev/null @@ -1,17 +0,0 @@ -[platformio] -lib_extra_dirs = ../../ -src_dir=. - -[common] -build_flags = -lib_deps_builtin = -lib_deps_external = - -[env:nodemcuv2] -platform = espressif8266 -framework = arduino -board = nodemcuv2 -build_flags = ${common.build_flags} -lib_deps = - ${common.lib_deps_builtin} - ${common.lib_deps_external} diff --git a/lib/IRremoteESP8266-2.5.2.03/examples/IRrecvDumpV2/platformio.ini b/lib/IRremoteESP8266-2.5.2.03/examples/IRrecvDumpV2/platformio.ini deleted file mode 100644 index eeb8d1f2eb93..000000000000 --- a/lib/IRremoteESP8266-2.5.2.03/examples/IRrecvDumpV2/platformio.ini +++ /dev/null @@ -1,17 +0,0 @@ -[platformio] -lib_extra_dirs = ../../ -src_dir=. - -[common] -build_flags = -lib_deps_builtin = -lib_deps_external = - -[env:nodemcuv2] -platform = espressif8266 -framework = arduino -board = nodemcuv2 -build_flags = ${common.build_flags} -lib_deps = - ${common.lib_deps_builtin} - ${common.lib_deps_external} diff --git a/lib/IRremoteESP8266-2.5.2.03/examples/IRsendDemo/platformio.ini b/lib/IRremoteESP8266-2.5.2.03/examples/IRsendDemo/platformio.ini deleted file mode 100644 index eeb8d1f2eb93..000000000000 --- a/lib/IRremoteESP8266-2.5.2.03/examples/IRsendDemo/platformio.ini +++ /dev/null @@ -1,17 +0,0 @@ -[platformio] -lib_extra_dirs = ../../ -src_dir=. - -[common] -build_flags = -lib_deps_builtin = -lib_deps_external = - -[env:nodemcuv2] -platform = espressif8266 -framework = arduino -board = nodemcuv2 -build_flags = ${common.build_flags} -lib_deps = - ${common.lib_deps_builtin} - ${common.lib_deps_external} diff --git a/lib/IRremoteESP8266-2.5.2.03/examples/IRsendProntoDemo/platformio.ini b/lib/IRremoteESP8266-2.5.2.03/examples/IRsendProntoDemo/platformio.ini deleted file mode 100644 index eeb8d1f2eb93..000000000000 --- a/lib/IRremoteESP8266-2.5.2.03/examples/IRsendProntoDemo/platformio.ini +++ /dev/null @@ -1,17 +0,0 @@ -[platformio] -lib_extra_dirs = ../../ -src_dir=. - -[common] -build_flags = -lib_deps_builtin = -lib_deps_external = - -[env:nodemcuv2] -platform = espressif8266 -framework = arduino -board = nodemcuv2 -build_flags = ${common.build_flags} -lib_deps = - ${common.lib_deps_builtin} - ${common.lib_deps_external} diff --git a/lib/IRremoteESP8266-2.5.2.03/examples/JVCPanasonicSendDemo/platformio.ini b/lib/IRremoteESP8266-2.5.2.03/examples/JVCPanasonicSendDemo/platformio.ini deleted file mode 100644 index eeb8d1f2eb93..000000000000 --- a/lib/IRremoteESP8266-2.5.2.03/examples/JVCPanasonicSendDemo/platformio.ini +++ /dev/null @@ -1,17 +0,0 @@ -[platformio] -lib_extra_dirs = ../../ -src_dir=. - -[common] -build_flags = -lib_deps_builtin = -lib_deps_external = - -[env:nodemcuv2] -platform = espressif8266 -framework = arduino -board = nodemcuv2 -build_flags = ${common.build_flags} -lib_deps = - ${common.lib_deps_builtin} - ${common.lib_deps_external} diff --git a/lib/IRremoteESP8266-2.5.2.03/examples/LGACSend/platformio.ini b/lib/IRremoteESP8266-2.5.2.03/examples/LGACSend/platformio.ini deleted file mode 100644 index eeb8d1f2eb93..000000000000 --- a/lib/IRremoteESP8266-2.5.2.03/examples/LGACSend/platformio.ini +++ /dev/null @@ -1,17 +0,0 @@ -[platformio] -lib_extra_dirs = ../../ -src_dir=. - -[common] -build_flags = -lib_deps_builtin = -lib_deps_external = - -[env:nodemcuv2] -platform = espressif8266 -framework = arduino -board = nodemcuv2 -build_flags = ${common.build_flags} -lib_deps = - ${common.lib_deps_builtin} - ${common.lib_deps_external} diff --git a/lib/IRremoteESP8266-2.5.2.03/examples/TurnOnArgoAC/platformio.ini b/lib/IRremoteESP8266-2.5.2.03/examples/TurnOnArgoAC/platformio.ini deleted file mode 100644 index eeb8d1f2eb93..000000000000 --- a/lib/IRremoteESP8266-2.5.2.03/examples/TurnOnArgoAC/platformio.ini +++ /dev/null @@ -1,17 +0,0 @@ -[platformio] -lib_extra_dirs = ../../ -src_dir=. - -[common] -build_flags = -lib_deps_builtin = -lib_deps_external = - -[env:nodemcuv2] -platform = espressif8266 -framework = arduino -board = nodemcuv2 -build_flags = ${common.build_flags} -lib_deps = - ${common.lib_deps_builtin} - ${common.lib_deps_external} diff --git a/lib/IRremoteESP8266-2.5.2.03/examples/TurnOnDaikinAC/platformio.ini b/lib/IRremoteESP8266-2.5.2.03/examples/TurnOnDaikinAC/platformio.ini deleted file mode 100644 index eeb8d1f2eb93..000000000000 --- a/lib/IRremoteESP8266-2.5.2.03/examples/TurnOnDaikinAC/platformio.ini +++ /dev/null @@ -1,17 +0,0 @@ -[platformio] -lib_extra_dirs = ../../ -src_dir=. - -[common] -build_flags = -lib_deps_builtin = -lib_deps_external = - -[env:nodemcuv2] -platform = espressif8266 -framework = arduino -board = nodemcuv2 -build_flags = ${common.build_flags} -lib_deps = - ${common.lib_deps_builtin} - ${common.lib_deps_external} diff --git a/lib/IRremoteESP8266-2.5.2.03/examples/TurnOnFujitsuAC/platformio.ini b/lib/IRremoteESP8266-2.5.2.03/examples/TurnOnFujitsuAC/platformio.ini deleted file mode 100644 index eeb8d1f2eb93..000000000000 --- a/lib/IRremoteESP8266-2.5.2.03/examples/TurnOnFujitsuAC/platformio.ini +++ /dev/null @@ -1,17 +0,0 @@ -[platformio] -lib_extra_dirs = ../../ -src_dir=. - -[common] -build_flags = -lib_deps_builtin = -lib_deps_external = - -[env:nodemcuv2] -platform = espressif8266 -framework = arduino -board = nodemcuv2 -build_flags = ${common.build_flags} -lib_deps = - ${common.lib_deps_builtin} - ${common.lib_deps_external} diff --git a/lib/IRremoteESP8266-2.5.2.03/examples/TurnOnKelvinatorAC/platformio.ini b/lib/IRremoteESP8266-2.5.2.03/examples/TurnOnKelvinatorAC/platformio.ini deleted file mode 100644 index eeb8d1f2eb93..000000000000 --- a/lib/IRremoteESP8266-2.5.2.03/examples/TurnOnKelvinatorAC/platformio.ini +++ /dev/null @@ -1,17 +0,0 @@ -[platformio] -lib_extra_dirs = ../../ -src_dir=. - -[common] -build_flags = -lib_deps_builtin = -lib_deps_external = - -[env:nodemcuv2] -platform = espressif8266 -framework = arduino -board = nodemcuv2 -build_flags = ${common.build_flags} -lib_deps = - ${common.lib_deps_builtin} - ${common.lib_deps_external} diff --git a/lib/IRremoteESP8266-2.5.2.03/examples/TurnOnMitsubishiAC/platformio.ini b/lib/IRremoteESP8266-2.5.2.03/examples/TurnOnMitsubishiAC/platformio.ini deleted file mode 100644 index eeb8d1f2eb93..000000000000 --- a/lib/IRremoteESP8266-2.5.2.03/examples/TurnOnMitsubishiAC/platformio.ini +++ /dev/null @@ -1,17 +0,0 @@ -[platformio] -lib_extra_dirs = ../../ -src_dir=. - -[common] -build_flags = -lib_deps_builtin = -lib_deps_external = - -[env:nodemcuv2] -platform = espressif8266 -framework = arduino -board = nodemcuv2 -build_flags = ${common.build_flags} -lib_deps = - ${common.lib_deps_builtin} - ${common.lib_deps_external} diff --git a/lib/IRremoteESP8266-2.5.2.03/examples/TurnOnToshibaAC/platformio.ini b/lib/IRremoteESP8266-2.5.2.03/examples/TurnOnToshibaAC/platformio.ini deleted file mode 100644 index eeb8d1f2eb93..000000000000 --- a/lib/IRremoteESP8266-2.5.2.03/examples/TurnOnToshibaAC/platformio.ini +++ /dev/null @@ -1,17 +0,0 @@ -[platformio] -lib_extra_dirs = ../../ -src_dir=. - -[common] -build_flags = -lib_deps_builtin = -lib_deps_external = - -[env:nodemcuv2] -platform = espressif8266 -framework = arduino -board = nodemcuv2 -build_flags = ${common.build_flags} -lib_deps = - ${common.lib_deps_builtin} - ${common.lib_deps_external} diff --git a/lib/IRremoteESP8266-2.5.2.03/examples/TurnOnTrotecAC/platformio.ini b/lib/IRremoteESP8266-2.5.2.03/examples/TurnOnTrotecAC/platformio.ini deleted file mode 100644 index eeb8d1f2eb93..000000000000 --- a/lib/IRremoteESP8266-2.5.2.03/examples/TurnOnTrotecAC/platformio.ini +++ /dev/null @@ -1,17 +0,0 @@ -[platformio] -lib_extra_dirs = ../../ -src_dir=. - -[common] -build_flags = -lib_deps_builtin = -lib_deps_external = - -[env:nodemcuv2] -platform = espressif8266 -framework = arduino -board = nodemcuv2 -build_flags = ${common.build_flags} -lib_deps = - ${common.lib_deps_builtin} - ${common.lib_deps_external} diff --git a/lib/IRremoteESP8266-2.5.2.03/src/IRutils.cpp b/lib/IRremoteESP8266-2.5.2.03/src/IRutils.cpp deleted file mode 100644 index 7864625a58f7..000000000000 --- a/lib/IRremoteESP8266-2.5.2.03/src/IRutils.cpp +++ /dev/null @@ -1,470 +0,0 @@ -// Copyright 2017 David Conran - -#include "IRutils.h" -#ifndef UNIT_TEST -#include -#endif - -#define __STDC_LIMIT_MACROS -#include -#include -#ifndef ARDUINO -#include -#endif -#include "IRrecv.h" -#include "IRremoteESP8266.h" - -// Reverse the order of the requested least significant nr. of bits. -// Args: -// input: Bit pattern/integer to reverse. -// nbits: Nr. of bits to reverse. -// Returns: -// 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. -// -// Args: -// input: The value to print -// base: The output base. -// Returns: -// A string representation of the integer. -// Note: Based on Arduino's Print::printNumber() -#ifdef ARDUINO // Arduino's & C++'s string implementations can't co-exist. -String uint64ToString(uint64_t input, uint8_t base) { - String result = ""; -#else -std::string uint64ToString(uint64_t input, uint8_t base) { - std::string result = ""; -#endif - // 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; - - do { - char c = input % base; - input /= base; - - if (c < 10) - c += '0'; - else - c += 'A' - 10; - result = c + result; - } while (input); - return result; -} - -#ifdef ARDUINO -// Print a uint64_t/unsigned long long to the Serial port -// Serial.print() can't handle printing long longs. (uint64_t) -// -// Args: -// input: The value to print -// base: The output base. -void serialPrintUint64(uint64_t input, uint8_t base) { - Serial.print(uint64ToString(input, base)); -} -#endif - -// Convert a protocol type (enum etc) to a human readable string. -// Args: -// protocol: Nr. (enum) of the protocol. -// isRepeat: A flag indicating if it is a repeat message of the protocol. -// Returns: -// A string containing the protocol name. -#ifdef ARDUINO // Arduino's & C++'s string implementations can't co-exist. -String typeToString(const decode_type_t protocol, const bool isRepeat) { - String result = ""; -#else -std::string typeToString(const decode_type_t protocol, const bool isRepeat) { - std::string result = ""; -#endif - switch (protocol) { - default: - case UNKNOWN: - result = "UNKNOWN"; - break; - case UNUSED: - result = "UNUSED"; - break; - case AIWA_RC_T501: - result = "AIWA_RC_T501"; - break; - case ARGO: - result = "ARGO"; - break; - case CARRIER_AC: - result = "CARRIER_AC"; - break; - case COOLIX: - result = "COOLIX"; - break; - case DAIKIN: - result = "DAIKIN"; - break; - case DENON: - result = "DENON"; - break; - case DISH: - result = "DISH"; - break; - case ELECTRA_AC: - result = "ELECTRA_AC"; - break; - case FUJITSU_AC: - result = "FUJITSU_AC"; - break; - case GICABLE: - result = "GICABLE"; - break; - case GLOBALCACHE: - result = "GLOBALCACHE"; - break; - case GREE: - result = "GREE"; - break; - case HAIER_AC: - result = "HAIER_AC"; - break; - case HAIER_AC_YRW02: - result = "HAIER_AC_YRW02"; - break; - case HITACHI_AC: - result = "HITACHI_AC"; - break; - case HITACHI_AC1: - result = "HITACHI_AC1"; - break; - case HITACHI_AC2: - result = "HITACHI_AC2"; - break; - case JVC: - result = "JVC"; - break; - case KELVINATOR: - result = "KELVINATOR"; - break; - case LG: - result = "LG"; - break; - case LG2: - result = "LG2"; - break; - case LASERTAG: - result = "LASERTAG"; - break; - case LUTRON: - result = "LUTRON"; - break; - case MAGIQUEST: - result = "MAGIQUEST"; - break; - case MIDEA: - result = "MIDEA"; - break; - case MITSUBISHI: - result = "MITSUBISHI"; - break; - case MITSUBISHI2: - result = "MITSUBISHI2"; - break; - case MITSUBISHI_AC: - result = "MITSUBISHI_AC"; - break; - case MWM: - result = "MWM"; - break; - case NEC: - result = "NEC"; - break; - case NEC_LIKE: - result = "NEC (non-strict)"; - break; - case NIKAI: - result = "NIKAI"; - break; - case PANASONIC: - result = "PANASONIC"; - break; - case PANASONIC_AC: - result = "PANASONIC_AC"; - break; - case PIONEER: - result = "PIONEER"; - break; - case PRONTO: - result = "PRONTO"; - break; - case RAW: - result = "RAW"; - break; - case RC5: - result = "RC5"; - break; - case RC5X: - result = "RC5X"; - break; - case RC6: - result = "RC6"; - break; - case RCMM: - result = "RCMM"; - break; - case SAMSUNG: - result = "SAMSUNG"; - break; - case SAMSUNG_AC: - result = "SAMSUNG_AC"; - break; - case SANYO: - result = "SANYO"; - break; - case SANYO_LC7461: - result = "SANYO_LC7461"; - break; - case SHARP: - result = "SHARP"; - break; - case SHERWOOD: - result = "SHERWOOD"; - break; - case SONY: - result = "SONY"; - break; - case TOSHIBA_AC: - result = "TOSHIBA_AC"; - break; - case TROTEC: - result = "TROTEC"; - break; - case WHIRLPOOL_AC: - result = "WHIRLPOOL_AC"; - break; - case WHYNTER: - result = "WHYNTER"; - break; - } - if (isRepeat) result += " (Repeat)"; - return result; -} - -// Does the given protocol use a complex state as part of the decode? -bool hasACState(const decode_type_t protocol) { - switch (protocol) { - case DAIKIN: - case ELECTRA_AC: - case FUJITSU_AC: - case GREE: - case HAIER_AC: - case HAIER_AC_YRW02: - case HITACHI_AC: - case HITACHI_AC1: - case HITACHI_AC2: - case KELVINATOR: - case MITSUBISHI_AC: - case MWM: - case PANASONIC_AC: - case SAMSUNG_AC: - case TOSHIBA_AC: - 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. -// Args: -// results: A ptr to a decode result. -// Returns: -// A uint16_t containing the length. -uint16_t getCorrectedRawLength(const decode_results *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. -#ifdef ARDUINO -String resultToSourceCode(const decode_results *results) { - String output = ""; -#else -std::string resultToSourceCode(const decode_results *results) { - std::string output = ""; -#endif - // Start declaration - output += "uint16_t "; // variable type - output += "rawData["; // array name - output += uint64ToString(getCorrectedRawLength(results), 10); - // array size - output += "] = {"; // 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 += ", 0, "; - else - output += ", 0, "; - } - output += uint64ToString(usecs, 10); - if (i < results->rawlen - 1) - output += ", "; // ',' not needed on the last one - if (i % 2 == 0) output += " "; // Extra if it was even. - } - - // End declaration - output += "};"; - - // Comment - output += " // " + typeToString(results->decode_type, results->repeat); - // Only display the value if the decode type doesn't have an A/C state. - if (!hasACState(results->decode_type)) - output += " " + uint64ToString(results->value, 16); - output += "\n"; - - // Now dump "known" codes - if (results->decode_type != UNKNOWN) { - if (hasACState(results->decode_type)) { -#if DECODE_AC - uint16_t nbytes = results->bits / 8; - output += "uint8_t state[" + uint64ToString(nbytes) + "] = {"; - for (uint16_t i = 0; i < nbytes; i++) { - output += "0x"; - if (results->state[i] < 0x10) output += "0"; - output += uint64ToString(results->state[i], 16); - if (i < nbytes - 1) output += ", "; - } - output += "};\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 += "uint32_t address = 0x" + - uint64ToString(results->address, 16) + ";\n"; - output += "uint32_t command = 0x" + - uint64ToString(results->command, 16) + ";\n"; - } - // Most protocols have data - output += - "uint64_t data = 0x" + uint64ToString(results->value, 16) + ";\n"; - } - } - return output; -} - -// Dump out the decode_results structure. -// -#ifdef ARDUINO -String resultToTimingInfo(const decode_results *results) { - String output = ""; - String value = ""; -#else -std::string resultToTimingInfo(const decode_results *results) { - std::string output = ""; - std::string value = ""; -#endif - output += "Raw Timing[" + uint64ToString(results->rawlen - 1, 10) + "]:\n"; - - for (uint16_t i = 1; i < results->rawlen; i++) { - if (i % 2 == 0) - output += "-"; // even - else - output += " +"; // 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 += ", "; // ',' 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. -// -#ifdef ARDUINO -String resultToHexidecimal(const decode_results *result) { - String output = ""; -#else -std::string resultToHexidecimal(const decode_results *result) { - std::string output = ""; -#endif - 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. -// -#ifdef ARDUINO -String resultToHumanReadableBasic(const decode_results *results) { - String output = ""; -#else -std::string resultToHumanReadableBasic(const decode_results *results) { - std::string output = ""; -#endif - // Show Encoding standard - output += - "Encoding : " + typeToString(results->decode_type, results->repeat) + - "\n"; - - // Show Code & length - output += "Code : "; - output += resultToHexidecimal(results); - output += " (" + uint64ToString(results->bits) + " bits)\n"; - return output; -} - -uint8_t sumBytes(uint8_t *start, const uint16_t length, const uint8_t init) { - uint8_t checksum = init; - uint8_t *ptr; - for (ptr = start; ptr - start < length; ptr++) checksum += *ptr; - return checksum; -} - -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)); -} diff --git a/lib/IRremoteESP8266-2.5.2.03/src/ir_Daikin.cpp b/lib/IRremoteESP8266-2.5.2.03/src/ir_Daikin.cpp deleted file mode 100644 index b94b4a63aa00..000000000000 --- a/lib/IRremoteESP8266-2.5.2.03/src/ir_Daikin.cpp +++ /dev/null @@ -1,750 +0,0 @@ -/* -An Arduino sketch to emulate IR Daikin ARC433** remote control unit -Read more at: -http://harizanov.com/2012/02/control-daikin-air-conditioner-over-the-internet/ - -Copyright 2016 sillyfrog -Copyright 2017 sillyfrog, crankyoldgit -*/ - -#include "ir_Daikin.h" -#include -#ifndef ARDUINO -#include -#endif -#include "IRrecv.h" -#include "IRremoteESP8266.h" -#include "IRsend.h" -#include "IRutils.h" - -// DDDDD AAA IIIII KK KK IIIII NN NN -// DD DD AAAAA III KK KK III NNN NN -// DD DD AA AA III KKKK III NN N NN -// DD DD AAAAAAA III KK KK III NN NNN -// DDDDDD AA AA IIIII KK KK IIIII NN NN - -// Constants -// Ref: -// https://github.com/mharizanov/Daikin-AC-remote-control-over-the-Internet/tree/master/IRremote -// http://rdlab.cdmt.vn/project-2013/daikin-ir-protocol - -#if SEND_DAIKIN -// Original header -// static uint8_t header1[DAIKIN_HEADER1_LENGTH]; -// header1[0] = 0b00010001; -// header1[1] = 0b11011010; -// header1[2] = 0b00100111; -// header1[3] = 0b00000000; -// header1[4] = 0b11000101; -// header1[5] = 0b00000000; -// header1[6] = 0b00000000; -// header1[7] = 0b11010111; - -// Send a Daikin A/C message. -// -// Args: -// data: An array of kDaikinStateLength bytes containing the IR command. -// -// Status: STABLE -// -// Ref: -// IRDaikinESP.cpp -// https://github.com/mharizanov/Daikin-AC-remote-control-over-the-Internet/tree/master/IRremote -void IRsend::sendDaikin(unsigned char data[], uint16_t nbytes, - uint16_t repeat) { - if (nbytes < kDaikinStateLength) - return; // Not enough bytes to send a proper message. - - for (uint16_t r = 0; r <= repeat; r++) { - // Send the header, 0b00000 - sendGeneric(0, 0, // No header for the header - kDaikinBitMark, kDaikinOneSpace, kDaikinBitMark, - kDaikinZeroSpace, kDaikinBitMark, kDaikinZeroSpace + kDaikinGap, - (uint64_t)0b00000, 5, 38, false, 0, 50); - // Leading header - // 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); - // Data #1 - sendGeneric(kDaikinHdrMark, kDaikinHdrSpace, kDaikinBitMark, - kDaikinOneSpace, kDaikinBitMark, kDaikinZeroSpace, - kDaikinBitMark, kDaikinZeroSpace + kDaikinGap, data, 8, 38, - false, 0, 50); - // Data #2 - sendGeneric(kDaikinHdrMark, kDaikinHdrSpace, kDaikinBitMark, - kDaikinOneSpace, kDaikinBitMark, kDaikinZeroSpace, - kDaikinBitMark, kDaikinZeroSpace + kDaikinGap, data + 8, - nbytes - 8, 38, false, 0, 50); - } -} -#endif // SEND_DAIKIN - -IRDaikinESP::IRDaikinESP(uint16_t pin) : _irsend(pin) { stateReset(); } - -void IRDaikinESP::begin() { _irsend.begin(); } - -#if SEND_DAIKIN -void IRDaikinESP::send() { - checksum(); - _irsend.sendDaikin(daikin); -} -#endif // SEND_DAIKIN - -// Calculate the checksum for a given data block. -// Args: -// block: Ptr to the start of the data block. -// length: Nr. of bytes to checksum. -// Returns: -// A byte containing the calculated checksum. -uint8_t IRDaikinESP::calcBlockChecksum(const uint8_t *block, - const uint16_t length) { - uint8_t sum = 0; - // Daikin checksum is just the addition of all the data bytes - // in the block but capped to 8 bits. - for (uint16_t i = 0; i < length; i++, block++) sum += *block; - return sum & 0xFFU; -} - -// Verify the checksum is valid for a given state. -// Args: -// state: The array to verify the checksum of. -// length: The size of the state. -// Returns: -// A boolean. -bool IRDaikinESP::validChecksum(const uint8_t state[], const uint16_t length) { - if (length < 8 || state[7] != calcBlockChecksum(state, 7)) return false; - if (length < 10 || - state[length - 1] != calcBlockChecksum(state + 8, length - 9)) - return false; - return true; -} - -// Calculate and set the checksum values for the internal state. -void IRDaikinESP::checksum() { - daikin[7] = calcBlockChecksum(daikin, 7); - daikin[26] = calcBlockChecksum(daikin + 8, 17); -} - -void IRDaikinESP::stateReset() { - for (uint8_t i = 0; i < kDaikinStateLength; i++) daikin[i] = 0x0; - - daikin[0] = 0x11; - daikin[1] = 0xDA; - daikin[2] = 0x27; - daikin[4] = 0x42; - // daikin[7] is a checksum byte, it will be set by checksum(). - daikin[8] = 0x11; - daikin[9] = 0xDA; - daikin[10] = 0x27; - daikin[13] = 0x49; - daikin[14] = 0x1E; - daikin[16] = 0xB0; - daikin[19] = 0x06; - daikin[20] = 0x60; - daikin[23] = 0xC0; - // daikin[26] is a checksum byte, it will be set by checksum(). - checksum(); -} - -uint8_t *IRDaikinESP::getRaw() { - checksum(); // Ensure correct settings before sending. - return daikin; -} - -void IRDaikinESP::setRaw(uint8_t new_code[]) { - for (uint8_t i = 0; i < kDaikinStateLength; i++) daikin[i] = new_code[i]; -} - -void IRDaikinESP::on() { - // state = ON; - setBit(kDaikinBytePower, kDaikinBitPower); -} - -void IRDaikinESP::off() { - // state = OFF; - clearBit(kDaikinBytePower, kDaikinBitPower); -} - -void IRDaikinESP::setPower(bool state) { - if (state) - on(); - else - off(); -} - -bool IRDaikinESP::getPower() { - return (getBit(kDaikinBytePower, kDaikinBitPower) > 0); -} - -// Set the temp in deg C -void IRDaikinESP::setTemp(uint8_t temp) { - if (temp < kDaikinMinTemp) - temp = kDaikinMinTemp; - else if (temp > kDaikinMaxTemp) - temp = kDaikinMaxTemp; - daikin[14] = temp * 2; -} - -uint8_t IRDaikinESP::getTemp() { return daikin[14] / 2; } - -// Set the speed of the fan, 1-5 or kDaikinFanAuto or kDaikinFanQuiet -void IRDaikinESP::setFan(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; - daikin[16] &= 0x0F; - daikin[16] |= (fanset << 4); -} - -uint8_t IRDaikinESP::getFan() { - uint8_t fan = daikin[16] >> 4; - if (fan != kDaikinFanQuiet && fan != kDaikinFanAuto) fan -= 2; - return fan; -} - -uint8_t IRDaikinESP::getMode() { - /* - kDaikinCool - kDaikinHeat - kDaikinFan - kDaikinAuto - kDaikinDry - */ - return daikin[13] >> 4; -} - -void IRDaikinESP::setMode(uint8_t mode) { - switch (mode) { - case kDaikinCool: - case kDaikinHeat: - case kDaikinFan: - case kDaikinDry: - break; - default: - mode = kDaikinAuto; - } - mode <<= 4; - daikin[13] &= 0b10001111; - daikin[13] |= mode; -} - -void IRDaikinESP::setSwingVertical(bool state) { - if (state) - daikin[16] |= 0x0F; - else - daikin[16] &= 0xF0; -} - -bool IRDaikinESP::getSwingVertical() { return daikin[16] & 0x01; } - -void IRDaikinESP::setSwingHorizontal(bool state) { - if (state) - daikin[17] |= 0x0F; - else - daikin[17] &= 0xF0; -} - -bool IRDaikinESP::getSwingHorizontal() { return daikin[17] & 0x01; } - -void IRDaikinESP::setQuiet(bool state) { - if (state) { - setBit(kDaikinByteSilent, kDaikinBitSilent); - // Powerful & Quiet mode being on are mutually exclusive. - setPowerful(false); - } else { - clearBit(kDaikinByteSilent, kDaikinBitSilent); - } -} - -bool IRDaikinESP::getQuiet() { - return (getBit(kDaikinByteSilent, kDaikinBitSilent) > 0); -} - -void IRDaikinESP::setPowerful(bool state) { - if (state) { - setBit(kDaikinBytePowerful, kDaikinBitPowerful); - // Powerful, Quiet, & Econo mode being on are mutually exclusive. - setQuiet(false); - setEcono(false); - } else { - clearBit(kDaikinBytePowerful, kDaikinBitPowerful); - } -} - -bool IRDaikinESP::getPowerful() { - return (getBit(kDaikinBytePowerful, kDaikinBitPowerful) > 0); -} - -void IRDaikinESP::setSensor(bool state) { - if (state) - setBit(kDaikinByteSensor, kDaikinBitSensor); - else - clearBit(kDaikinByteSensor, kDaikinBitSensor); -} - -bool IRDaikinESP::getSensor() { - return (getBit(kDaikinByteSensor, kDaikinBitSensor) > 0); -} - -void IRDaikinESP::setEcono(bool state) { - if (state) { - setBit(kDaikinByteEcono, kDaikinBitEcono); - // Powerful & Econo mode being on are mutually exclusive. - setPowerful(false); - } else { - clearBit(kDaikinByteEcono, kDaikinBitEcono); - } -} - -bool IRDaikinESP::getEcono() { - return (getBit(kDaikinByteEcono, kDaikinBitEcono) > 0); -} - -void IRDaikinESP::setEye(bool state) { - if (state) - setBit(kDaikinByteEye, kDaikinBitEye); - else - clearBit(kDaikinByteEye, kDaikinBitEye); -} - -bool IRDaikinESP::getEye() { - return (getBit(kDaikinByteEye, kDaikinBitEye) > 0); -} - -void IRDaikinESP::setMold(bool state) { - if (state) - setBit(kDaikinByteMold, kDaikinBitMold); - else - clearBit(kDaikinByteMold, kDaikinBitMold); -} - -bool IRDaikinESP::getMold() { - return (getBit(kDaikinByteMold, kDaikinBitMold) > 0); -} - -void IRDaikinESP::setBit(uint8_t byte, uint8_t bitmask) { - daikin[byte] |= bitmask; -} - -void IRDaikinESP::clearBit(uint8_t byte, uint8_t bitmask) { - bitmask = ~bitmask; - daikin[byte] &= bitmask; -} - -uint8_t IRDaikinESP::getBit(uint8_t byte, uint8_t bitmask) { - return daikin[byte] & bitmask; -} - -// starttime: Number of minutes after midnight, in 10 minutes increments -void IRDaikinESP::enableOnTimer(uint16_t starttime) { - setBit(kDaikinByteOnTimer, kDaikinBitOnTimer); - daikin[18] = (uint8_t)(starttime & 0x00FF); - // only keep 4 bits - daikin[19] &= 0xF0; - daikin[19] |= (uint8_t)((starttime >> 8) & 0x0F); -} - -void IRDaikinESP::disableOnTimer() { - enableOnTimer(0x600); - clearBit(kDaikinByteOnTimer, kDaikinBitOnTimer); -} - -uint16_t IRDaikinESP::getOnTime() { - uint16_t ret; - ret = daikin[19] & 0x0F; - ret = ret << 8; - ret += daikin[18]; - return ret; -} - -bool IRDaikinESP::getOnTimerEnabled() { - return getBit(kDaikinByteOnTimer, kDaikinBitOnTimer); -} - -// endtime: Number of minutes after midnight, in 10 minutes increments -void IRDaikinESP::enableOffTimer(uint16_t endtime) { - setBit(kDaikinByteOffTimer, kDaikinBitOffTimer); - daikin[20] = (uint8_t)((endtime >> 4) & 0xFF); - daikin[19] &= 0x0F; - daikin[19] |= (uint8_t)((endtime & 0x000F) << 4); -} - -void IRDaikinESP::disableOffTimer() { - enableOffTimer(0x600); - clearBit(kDaikinByteOffTimer, kDaikinBitOffTimer); -} - -uint16_t IRDaikinESP::getOffTime() { - uint16_t ret, tmp; - ret = daikin[20]; - ret <<= 4; - tmp = daikin[19] & 0xF0; - tmp >>= 4; - ret += tmp; - return ret; -} - -bool IRDaikinESP::getOffTimerEnabled() { - return getBit(kDaikinByteOffTimer, kDaikinBitOffTimer); -} - -void IRDaikinESP::setCurrentTime(uint16_t numMins) { - if (numMins > 24 * 60) numMins = 0; // If > 23:59, set to 00:00 - daikin[5] = (uint8_t)(numMins & 0x00FF); - // only keep 4 bits - daikin[6] &= 0xF0; - daikin[6] |= (uint8_t)((numMins >> 8) & 0x0F); -} - -uint16_t IRDaikinESP::getCurrentTime() { - uint16_t ret; - ret = daikin[6] & 0x0F; - ret <<= 8; - ret += daikin[5]; - return ret; -} - -#ifdef ARDUINO -String IRDaikinESP::renderTime(uint16_t timemins) { - String ret; -#else // ARDUINO -std::string IRDaikinESP::renderTime(uint16_t timemins) { - std::string ret; -#endif // ARDUINO - uint16_t hours, mins; - hours = timemins / 60; - ret = uint64ToString(hours) + ":"; - mins = timemins - (hours * 60); - if (mins < 10) ret += "0"; - ret += uint64ToString(mins); - return ret; -} - -// Convert the internal state into a human readable string. -#ifdef ARDUINO -String IRDaikinESP::toString() { - String result = ""; -#else // ARDUINO -std::string IRDaikinESP::toString() { - std::string result = ""; -#endif // ARDUINO - result += "Power: "; - if (getPower()) - result += "On"; - else - result += "Off"; - result += ", Mode: " + uint64ToString(getMode()); - switch (getMode()) { - case kDaikinAuto: - result += " (AUTO)"; - break; - case kDaikinCool: - result += " (COOL)"; - break; - case kDaikinHeat: - result += " (HEAT)"; - break; - case kDaikinDry: - result += " (DRY)"; - break; - case kDaikinFan: - result += " (FAN)"; - break; - default: - result += " (UNKNOWN)"; - } - result += ", Temp: " + uint64ToString(getTemp()) + "C"; - result += ", Fan: " + uint64ToString(getFan()); - switch (getFan()) { - case kDaikinFanAuto: - result += " (AUTO)"; - break; - case kDaikinFanQuiet: - result += " (QUIET)"; - break; - case kDaikinFanMin: - result += " (MIN)"; - break; - case kDaikinFanMax: - result += " (MAX)"; - break; - } - result += ", Powerful: "; - if (getPowerful()) - result += "On"; - else - result += "Off"; - result += ", Quiet: "; - if (getQuiet()) - result += "On"; - else - result += "Off"; - result += ", Sensor: "; - if (getSensor()) - result += "On"; - else - result += "Off"; - result += ", Eye: "; - if (getEye()) - result += "On"; - else - result += "Off"; - result += ", Mold: "; - if (getMold()) - result += "On"; - else - result += "Off"; - result += ", Swing (Horizontal): "; - if (getSwingHorizontal()) - result += "On"; - else - result += "Off"; - result += ", Swing (Vertical): "; - if (getSwingVertical()) - result += "On"; - else - result += "Off"; - result += ", Current Time: " + renderTime(getCurrentTime()); - result += ", On Time: "; - if (getOnTimerEnabled()) - result += renderTime(getOnTime()); - else - result += "Off"; - result += ", Off Time: "; - if (getOffTimerEnabled()) - result += renderTime(getOffTime()); - else - result += "Off"; - - return result; -} - -#if DAIKIN_DEBUG -// Print what we have -void IRDaikinESP::printState() { -#ifdef ARDUINO - String strbits; -#else // ARDUINO - std::string strbits; -#endif // ARDUINO - DPRINTLN("Raw Bits:"); - for (uint8_t i = 0; i < kDaikinStateLength; i++) { - strbits = uint64ToString(daikin[i], BIN); - while (strbits.length() < 8) strbits = "0" + strbits; - DPRINT(strbits); - DPRINT(" "); - } - DPRINTLN(""); - DPRINTLN(toString()); -} -#endif // DAIKIN_DEBUG - -/* - * Return most important bits to allow replay - * layout is: - * 0: Power - * 1-3: Mode - * 4-7: Fan speed/mode - * 8-14: Target Temperature - * 15: Econo - * 16: Powerful - * 17: Quiet - * 18: Sensor - * 19: Swing Vertical - * 20-31: Current time (mins since midnight) - * */ -uint32_t IRDaikinESP::getCommand() { - uint32_t ret = 0; - uint32_t tmp = 0; - if (getPower()) ret |= 0b00000000000000000000000000000001; - tmp = getMode(); - tmp = tmp << 1; - ret |= tmp; - - tmp = getFan(); - tmp <<= 4; - ret |= tmp; - - tmp = getTemp(); - tmp <<= 8; - ret |= tmp; - - if (getEcono()) ret |= 0b00000000000000001000000000000000; - if (getPowerful()) ret |= 0b00000000000000010000000000000000; - if (getQuiet()) ret |= 0b00000000000000100000000000000000; - if (getSensor()) ret |= 0b00000000000001000000000000000000; - if (getSwingVertical()) ret |= 0b00000000000010000000000000000000; - ret |= (getCurrentTime() << 20); - return ret; -} - -void IRDaikinESP::setCommand(uint32_t value) { - uint32_t tmp = 0; - if (value & 0b00000000000000000000000000000001) setPower(true); - tmp = value & 0b00000000000000000000000000001110; - tmp >>= 1; - setMode(tmp); - - tmp = value & 0b00000000000000000000000011110000; - tmp >>= 4; - setFan(tmp); - - tmp = value & 0b00000000000000000111111100000000; - tmp >>= 8; - setTemp(tmp); - - if (value & 0b00000000000000001000000000000000) setEcono(true); - if (value & 0b00000000000000010000000000000000) setPowerful(true); - if (value & 0b00000000000000100000000000000000) setQuiet(true); - if (value & 0b00000000000001000000000000000000) setSensor(true); - if (value & 0b00000000000010000000000000000000) setSwingVertical(true); - - value >>= 20; - setCurrentTime(value); -} - -#if DECODE_DAIKIN - -void addbit(bool val, unsigned char data[]) { - uint8_t curbit = data[kDaikinCurBit]; - uint8_t curindex = data[kDaikinCurIndex]; - if (val) { - unsigned char bit = 1; - bit = bit << curbit; - data[curindex] |= bit; - } - curbit++; - if (curbit == 8) { - curbit = 0; - curindex++; - } - data[kDaikinCurBit] = curbit; - data[kDaikinCurIndex] = curindex; -} - -bool checkheader(decode_results *results, uint16_t *offset) { - if (!IRrecv::matchMark(results->rawbuf[(*offset)++], kDaikinBitMark, - kDaikinTolerance, kDaikinMarkExcess)) - return false; - if (!IRrecv::matchSpace(results->rawbuf[(*offset)++], - kDaikinZeroSpace + kDaikinGap, kDaikinTolerance, - kDaikinMarkExcess)) - return false; - if (!IRrecv::matchMark(results->rawbuf[(*offset)++], kDaikinHdrMark, - kDaikinTolerance, kDaikinMarkExcess)) - return false; - if (!IRrecv::matchSpace(results->rawbuf[(*offset)++], kDaikinHdrSpace, - kDaikinTolerance, kDaikinMarkExcess)) - return false; - - return true; -} - -bool readbits(decode_results *results, uint16_t *offset, - unsigned char daikin_code[], uint16_t countbits) { - for (uint16_t i = 0; i < countbits && *offset < results->rawlen - 1; - i++, (*offset)++) { - if (!IRrecv::matchMark(results->rawbuf[(*offset)++], kDaikinBitMark, - kDaikinTolerance, kDaikinMarkExcess)) - return false; - if (IRrecv::matchSpace(results->rawbuf[*offset], kDaikinOneSpace, - kDaikinTolerance, kDaikinMarkExcess)) - addbit(1, daikin_code); - else if (IRrecv::matchSpace(results->rawbuf[*offset], kDaikinZeroSpace, - kDaikinTolerance, kDaikinMarkExcess)) - addbit(0, daikin_code); - else - return false; - } - return true; -} - -// Decode the supplied Daikin A/C message. -// Args: -// results: Ptr to the data to decode and where to store the decode result. -// nbits: Nr. of bits to expect in the data portion. (kDaikinRawBits) -// strict: Flag to indicate if we strictly adhere to the specification. -// Returns: -// boolean: True if it can decode it, false if it can't. -// -// Status: BETA / Should be working. -// -// Notes: -// If DAIKIN_DEBUG enabled, will print all the set options and values. -// -// Ref: -// https://github.com/mharizanov/Daikin-AC-remote-control-over-the-Internet/tree/master/IRremote -bool IRrecv::decodeDaikin(decode_results *results, uint16_t nbits, - bool strict) { - if (results->rawlen < kDaikinRawBits) return false; - - // Compliance - if (strict && nbits != kDaikinRawBits) return false; - - uint16_t offset = kStartOffset; - unsigned char daikin_code[kDaikinStateLength + 2]; - for (uint8_t i = 0; i < kDaikinStateLength + 2; i++) daikin_code[i] = 0; - - // Header (#1) - for (uint8_t i = 0; i < 10; i++) { - if (!matchMark(results->rawbuf[offset++], kDaikinBitMark)) return false; - } - if (!checkheader(results, &offset)) return false; - - // Data (#1) - if (!readbits(results, &offset, daikin_code, 8 * 8)) return false; - - // Ignore everything that has just been captured as it is not needed. - // Some remotes may not send this portion, my remote did, but it's not - // required. - for (uint8_t i = 0; i < kDaikinStateLength + 2; i++) daikin_code[i] = 0; - - // Header (#2) - if (!checkheader(results, &offset)) return false; - - // Data (#2) - if (!readbits(results, &offset, daikin_code, 8 * 8)) return false; - - // Header (#3) - if (!checkheader(results, &offset)) return false; - - // Data (#3), read up everything else - if (!readbits(results, &offset, daikin_code, kDaikinBits - (8 * 8))) - return false; - - // Footer - if (!matchMark(results->rawbuf[offset++], kDaikinBitMark)) return false; - if (offset < results->rawlen && - !matchAtLeast(results->rawbuf[offset], kDaikinGap)) - return false; - - // Compliance - if (strict) { - if (!IRDaikinESP::validChecksum(daikin_code)) return false; - } - - // Success -#if DAIKIN_DEBUG - IRDaikinESP dako = IRDaikinESP(0); - dako.setRaw(daikin_code); -#ifdef ARDUINO - yield(); -#endif // ARDUINO - dako.printState(); -#endif // DAIKIN_DEBUG - - // Copy across the bits to state - for (uint8_t i = 0; i < kDaikinStateLength; i++) - results->state[i] = daikin_code[i]; - results->bits = kDaikinStateLength * 8; - results->decode_type = DAIKIN; - return true; -} -#endif // DECODE_DAIKIN diff --git a/lib/IRremoteESP8266-2.5.2.03/src/ir_Daikin.h b/lib/IRremoteESP8266-2.5.2.03/src/ir_Daikin.h deleted file mode 100644 index 7094990d85a4..000000000000 --- a/lib/IRremoteESP8266-2.5.2.03/src/ir_Daikin.h +++ /dev/null @@ -1,208 +0,0 @@ -// Copyright 2016 sillyfrog -// Copyright 2017 sillyfrog, crankyoldgit -#ifndef IR_DAIKIN_H_ -#define IR_DAIKIN_H_ - -#ifndef UNIT_TEST -#include -#else -#include -#endif -#include "IRrecv.h" -#include "IRremoteESP8266.h" -#include "IRsend.h" - -// Option to disable the additional Daikin debug info to conserve memory -#define DAIKIN_DEBUG false - -// DDDDD AAA IIIII KK KK IIIII NN NN -// DD DD AAAAA III KK KK III NNN NN -// DD DD AA AA III KKKK III NN N NN -// DD DD AAAAAAA III KK KK III NN NNN -// DDDDDD AA AA IIIII KK KK IIIII NN NN - -/* - Daikin AC map - byte 5=Current time, mins past midnight, low bits - byte 6 - b0-b3=Current time, mins past midnight, high bits - byte 7= checksum of the first part (and last byte before a 29ms pause) - byte 13=mode - b7 = 0 - b6+b5+b4 = Mode - Modes: b6+b5+b4 - 011 = Cool - 100 = Heat (temp 23) - 110 = FAN (temp not shown, but 25) - 000 = Fully Automatic (temp 25) - 010 = DRY (temp 0xc0 = 96 degrees c) - b3 = 1 - b2 = OFF timer set - b1 = ON timer set - b0 = Air Conditioner ON - byte 14=temp*2 (Temp should be between 10 - 32) - byte 16=Fan - FAN control - b7+b6+b5+b4 = Fan speed - Fan: b7+b6+b5+b4 - 0Ă—3 = 1 bar - 0Ă—4 = 2 bar - 0Ă—5 = 3 bar - 0Ă—6 = 4 bar - 0Ă—7 = 5 bar - 0xa = Auto - 0xb = Quite - b3+b2+b1+b0 = Swing control up/down - Swing control up/down: - 0000 = Swing up/down off - 1111 = Swing up/down on - byte 17 - Swing control left/right: - 0000 = Swing left/right off - 1111 = Swing left/right on - byte 18=On timer mins past midnight, low bits - byte 19 - b0-b3=On timer mins past midnight, high bits - b4-b7=Off timer mins past midnight, low bits - byte 20=Off timer mins past midnight, high bits - byte 21=Aux -> Powerful (bit 1), Silent (bit 5) - byte 24=Aux2 - b1: Sensor - b2: Econo mode - b7: Intelligent eye on - byte 25=Aux3 - b1: Mold Proof - byte 26= checksum of the second part -*/ - -// Constants -const uint8_t kDaikinAuto = 0b000; -const uint8_t kDaikinDry = 0b010; -const uint8_t kDaikinCool = 0b011; -const uint8_t kDaikinHeat = 0b100; -const uint8_t kDaikinFan = 0b110; -const uint8_t kDaikinMinTemp = 10; // Celsius -const uint8_t kDaikinMaxTemp = 32; // Celsius -const uint8_t kDaikinFanMin = 1; -const uint8_t kDaikinFanMax = 5; -const uint8_t kDaikinFanAuto = 0b1010; -const uint8_t kDaikinFanQuiet = 0b1011; -const uint8_t kDaikinBytePower = 13; -const uint8_t kDaikinBitPower = 0b00000001; -const uint8_t kDaikinBytePowerful = 21; -const uint8_t kDaikinBitPowerful = 0b00000001; -const uint8_t kDaikinByteSilent = 21; -const uint8_t kDaikinBitSilent = 0b00100000; -const uint8_t kDaikinByteSensor = 24; -const uint8_t kDaikinBitSensor = 0b00000010; -const uint8_t kDaikinByteEcono = 24; -const uint8_t kDaikinBitEcono = 0b00000100; -const uint8_t kDaikinByteEye = 24; -const uint8_t kDaikinBitEye = 0b10000000; -const uint8_t kDaikinByteMold = 25; -const uint8_t kDaikinBitMold = 0b00000010; -const uint8_t kDaikinByteOffTimer = 13; -const uint8_t kDaikinBitOffTimer = 0b00000100; -const uint8_t kDaikinByteOnTimer = 13; -const uint8_t kDaikinBitOnTimer = 0b00000010; -const uint8_t kDaikinCurBit = kDaikinStateLength; -const uint8_t kDaikinCurIndex = kDaikinStateLength + 1; -const uint8_t kDaikinTolerance = 35; -const uint16_t kDaikinMarkExcess = kMarkExcess; -const uint16_t kDaikinHdrMark = 3650; // kDaikinBitMark * 8 -const uint16_t kDaikinHdrSpace = 1623; // kDaikinBitMark * 4 -const uint16_t kDaikinBitMark = 428; -const uint16_t kDaikinZeroSpace = 428; -const uint16_t kDaikinOneSpace = 1280; -const uint16_t kDaikinGap = 29000; -// Note bits in each octet swapped so can be sent as a single value -const uint64_t kDaikinFirstHeader64 = - 0b1101011100000000000000001100010100000000001001111101101000010001; - -// Legacy defines. -#define DAIKIN_COOL kDaikinCool -#define DAIKIN_HEAT kDaikinHeat -#define DAIKIN_FAN kDaikinFan -#define DAIKIN_AUTO kDaikinAuto -#define DAIKIN_DRY kDaikinDry -#define DAIKIN_MIN_TEMP kDaikinMinTemp -#define DAIKIN_MAX_TEMP kDaikinMaxTemp -#define DAIKIN_FAN_MIN kDaikinFanMin -#define DAIKIN_FAN_MAX kDaikinFanMax -#define DAIKIN_FAN_AUTO kDaikinFanAuto -#define DAIKIN_FAN_QUIET kDaikinFanQuiet - -class IRDaikinESP { - public: - explicit IRDaikinESP(uint16_t pin); - -#if SEND_DAIKIN - void send(); -#endif - void begin(); - void on(); - void off(); - void setPower(bool state); - bool getPower(); - void setTemp(uint8_t temp); - uint8_t getTemp(); - void setFan(uint8_t fan); - uint8_t getFan(); - uint8_t getMode(); - void setMode(uint8_t mode); - void setSwingVertical(bool state); - bool getSwingVertical(); - void setSwingHorizontal(bool state); - bool getSwingHorizontal(); - bool getQuiet(); - void setQuiet(bool state); - bool getPowerful(); - void setPowerful(bool state); - void setSensor(bool state); - bool getSensor(); - void setEcono(bool state); - bool getEcono(); - void setEye(bool state); - bool getEye(); - void setMold(bool state); - bool getMold(); - void enableOnTimer(uint16_t starttime); - void disableOnTimer(); - uint16_t getOnTime(); - bool getOnTimerEnabled(); - void enableOffTimer(uint16_t endtime); - void disableOffTimer(); - uint16_t getOffTime(); - bool getOffTimerEnabled(); - void setCurrentTime(uint16_t time); - uint16_t getCurrentTime(); - uint8_t* getRaw(); - void setRaw(uint8_t new_code[]); -#if DAIKIN_DEBUG - void printState(); -#endif // DAIKIN_DEBUG - uint32_t getCommand(); - void setCommand(uint32_t value); - static bool validChecksum(const uint8_t state[], - const uint16_t length = kDaikinStateLength); -#ifdef ARDUINO - String toString(); - static String renderTime(uint16_t timemins); -#else - std::string toString(); - static std::string renderTime(uint16_t timemins); -#endif - - private: - // # of bytes per command - uint8_t daikin[kDaikinStateLength]; - void stateReset(); - static uint8_t calcBlockChecksum(const uint8_t* block, const uint16_t length); - void checksum(); - void setBit(uint8_t byte, uint8_t bitmask); - void clearBit(uint8_t byte, uint8_t bitmask); - uint8_t getBit(uint8_t byte, uint8_t bitmask); - IRsend _irsend; -}; - -#endif // IR_DAIKIN_H_ diff --git a/lib/IRremoteESP8266-2.5.2.03/src/ir_Trotec.cpp b/lib/IRremoteESP8266-2.5.2.03/src/ir_Trotec.cpp deleted file mode 100644 index 0bece2664a60..000000000000 --- a/lib/IRremoteESP8266-2.5.2.03/src/ir_Trotec.cpp +++ /dev/null @@ -1,127 +0,0 @@ -// Copyright 2017 stufisher - -#include "ir_Trotec.h" -#include "IRremoteESP8266.h" -#include "IRutils.h" - -// Constants -const uint16_t kTrotecHdrMark = 5952; -const uint16_t kTrotecHdrSpace = 7364; -const uint16_t kTrotecOneMark = 592; -const uint16_t kTrotecOneSpace = 1560; -const uint16_t kTrotecZeroMark = 592; -const uint16_t kTrotecZeroSpace = 592; -const uint16_t kTrotecGap = 6184; -const uint16_t kTrotecGapEnd = 1500; // made up value - -#if SEND_TROTEC - -void IRsend::sendTrotec(unsigned char data[], uint16_t nbytes, - uint16_t repeat) { - if (nbytes < kTrotecStateLength) return; - - for (uint16_t r = 0; r <= repeat; r++) { - sendGeneric(kTrotecHdrMark, kTrotecHdrSpace, kTrotecOneMark, - kTrotecOneSpace, kTrotecZeroMark, kTrotecZeroSpace, - kTrotecOneMark, kTrotecGap, data, nbytes, 36, false, - 0, // Repeats handled elsewhere - 50); - // More footer - enableIROut(36); - mark(kTrotecOneMark); - space(kTrotecGapEnd); - } -} -#endif // SEND_TROTEC - -IRTrotecESP::IRTrotecESP(uint16_t pin) : _irsend(pin) { stateReset(); } - -void IRTrotecESP::begin() { _irsend.begin(); } - -#if SEND_TROTEC -void IRTrotecESP::send() { - checksum(); - _irsend.sendTrotec(trotec); -} -#endif // SEND_TROTEC - -void IRTrotecESP::checksum() { - uint8_t sum = 0; - uint8_t i; - - for (i = 2; i < 8; i++) sum += trotec[i]; - - trotec[8] = sum & 0xFF; -} - -void IRTrotecESP::stateReset() { - for (uint8_t i = 2; i < kTrotecStateLength; i++) trotec[i] = 0x0; - - trotec[0] = kTrotecIntro1; - trotec[1] = kTrotecIntro2; - - setPower(false); - setTemp(kTrotecDefTemp); - setSpeed(kTrotecFanMed); - setMode(kTrotecAuto); -} - -uint8_t* IRTrotecESP::getRaw() { - checksum(); - return trotec; -} - -void IRTrotecESP::setPower(bool state) { - if (state) - trotec[2] |= (kTrotecOn << 3); - else - trotec[2] &= ~(kTrotecOn << 3); -} - -uint8_t IRTrotecESP::getPower() { return trotec[2] & (kTrotecOn << 3); } - -void IRTrotecESP::setSpeed(uint8_t speed) { - trotec[2] = (trotec[2] & 0xcf) | (speed << 4); -} - -uint8_t IRTrotecESP::getSpeed() { return trotec[2] & 0x30; } - -void IRTrotecESP::setMode(uint8_t mode) { - trotec[2] = (trotec[2] & 0xfc) | mode; -} - -uint8_t IRTrotecESP::getMode() { return trotec[2] & 0x03; } - -void IRTrotecESP::setTemp(uint8_t temp) { - if (temp < kTrotecMinTemp) - temp = kTrotecMinTemp; - else if (temp > kTrotecMaxTemp) - temp = kTrotecMaxTemp; - - trotec[3] = (trotec[3] & 0x80) | (temp - kTrotecMinTemp); -} - -uint8_t IRTrotecESP::getTemp() { return trotec[3] & 0x7f; } - -void IRTrotecESP::setSleep(bool sleep) { - if (sleep) - trotec[3] |= (kTrotecSleepOn << 7); - else - trotec[3] &= ~(kTrotecSleepOn << 7); -} - -bool IRTrotecESP::getSleep(void) { return trotec[3] & (kTrotecSleepOn << 7); } - -void IRTrotecESP::setTimer(uint8_t timer) { - if (timer > kTrotecMaxTimer) timer = kTrotecMaxTimer; - - if (timer) { - trotec[5] |= (kTrotecTimerOn << 6); - trotec[6] = timer; - } else { - trotec[5] &= ~(kTrotecTimerOn << 6); - trotec[6] = 0; - } -} - -uint8_t IRTrotecESP::getTimer() { return trotec[6]; } diff --git a/lib/IRremoteESP8266-2.5.2.03/src/ir_Whirlpool.cpp b/lib/IRremoteESP8266-2.5.2.03/src/ir_Whirlpool.cpp deleted file mode 100644 index 67151399103f..000000000000 --- a/lib/IRremoteESP8266-2.5.2.03/src/ir_Whirlpool.cpp +++ /dev/null @@ -1,149 +0,0 @@ -// Copyright 2018 David Conran -// -// Code to emulate Whirlpool protocol compatible devices. -// Should be compatible with: -// * SPIS409L, SPIS412L, SPIW409L, SPIW412L, SPIW418L -// - -#include -#ifndef ARDUINO -#include -#endif -#include "IRrecv.h" -#include "IRremoteESP8266.h" -#include "IRsend.h" -#include "IRutils.h" - -// WW WW HH HH IIIII RRRRRR LL PPPPPP OOOOO OOOOO LL -// WW WW HH HH III RR RR LL PP PP OO OO OO OO LL -// WW W WW HHHHHHH III RRRRRR LL PPPPPP OO OO OO OO LL -// WW WWW WW HH HH III RR RR LL PP OO OO OO OO LL -// WW WW HH HH IIIII RR RR LLLLLLL PP OOOO0 OOOO0 LLLLLLL - -// Constants -// Ref: https://github.com/markszabo/IRremoteESP8266/issues/509 -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 = 100000; // Completely made up value. -const uint8_t kWhirlpoolAcSections = 3; - -#if SEND_WHIRLPOOL_AC -// Send a Whirlpool A/C message. -// -// Args: -// data: An array of bytes containing the IR command. -// nbytes: Nr. of bytes of data in the array. (>=kWhirlpoolAcStateLength) -// repeat: Nr. of times the message is to be repeated. (Default = 0). -// -// Status: ALPHA / Untested. -// -// Ref: -// https://github.com/markszabo/IRremoteESP8266/issues/509 -void IRsend::sendWhirlpoolAC(unsigned char data[], uint16_t nbytes, - 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 - -#if DECODE_WHIRLPOOL_AC -// Decode the supplied Whirlpool A/C message. -// -// Args: -// results: Ptr to the data to decode and where to store the decode result. -// nbits: The number of data bits to expect. Typically kWhirlpoolAcBits -// strict: Flag indicating if we should perform strict matching. -// Returns: -// boolean: True if it can decode it, false if it can't. -// -// Status: ALPHA / Untested. -// -// -// Ref: -// https://github.com/markszabo/IRremoteESP8266/issues/509 -bool IRrecv::decodeWhirlpoolAC(decode_results *results, uint16_t nbits, - bool strict) { - if (results->rawlen < 2 * nbits + 4 + kHeader + kFooter - 1) - return false; // Can't possibly be a valid Whirlpool A/C message. - if (strict) { - if (nbits != kWhirlpoolAcBits) return false; - } - - uint16_t offset = kStartOffset; - uint16_t dataBitsSoFar = 0; - uint16_t i = 0; - match_result_t data_result; - 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 Section - // Keep reading bytes until we either run out of section or state to fill. - for (uint8_t section = 0, pos = 0; section < kWhirlpoolAcSections; - section++) { - pos += sectionSize[section]; - for (; offset <= results->rawlen - 16 && i < pos; - i++, dataBitsSoFar += 8, offset += data_result.used) { - data_result = - matchData(&(results->rawbuf[offset]), 8, kWhirlpoolAcBitMark, - kWhirlpoolAcOneSpace, kWhirlpoolAcBitMark, - kWhirlpoolAcZeroSpace, kTolerance, kMarkExcess, false); - if (data_result.success == false) break; // Fail - // Data is in LSB order. We need to reverse it. - results->state[i] = (uint8_t)data_result.data; - } - // Section Footer - if (!matchMark(results->rawbuf[offset++], kWhirlpoolAcBitMark)) - return false; - if (section < kWhirlpoolAcSections - 1) { // Inter-section gaps. - if (!matchSpace(results->rawbuf[offset++], kWhirlpoolAcGap)) return false; - } else { // Last section / End of message gap. - if (offset <= results->rawlen && - !matchAtLeast(results->rawbuf[offset++], kWhirlpoolAcGap)) - return false; - } - } - - // Compliance - if (strict) { - // Re-check we got the correct size/length due to the way we read the data. - if (dataBitsSoFar != kWhirlpoolAcBits) return false; - } - - // Success - results->decode_type = WHIRLPOOL_AC; - results->bits = dataBitsSoFar; - // 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-2.5.2.03/test/IRsend_test.cpp b/lib/IRremoteESP8266-2.5.2.03/test/IRsend_test.cpp deleted file mode 100644 index 3536399189a8..000000000000 --- a/lib/IRremoteESP8266-2.5.2.03/test/IRsend_test.cpp +++ /dev/null @@ -1,290 +0,0 @@ -// Copyright 2017 David Conran - -#include "IRsend_test.h" -#include "IRsend.h" -#include "gtest/gtest.h" - -// Tests sendData(). - -// Test sending zero bits. -TEST(TestSendData, SendZeroBits) { - IRsendTest irsend(4); - irsend.begin(); - irsend.sendData(1, 2, 3, 4, 0b1, 0, true); - EXPECT_EQ("", irsend.outputStr()); -} - -// Test sending zero and one. -TEST(TestSendData, SendSingleBit) { - IRsendTest irsend(4); - irsend.begin(); - irsend.sendData(1, 2, 3, 4, 0b1, 1, true); - EXPECT_EQ("m1s2", irsend.outputStr()); - irsend.sendData(1, 2, 3, 4, 0b0, 1, true); - EXPECT_EQ("m3s4", irsend.outputStr()); -} - -// Test sending bit order. -TEST(TestSendData, TestingBitSendOrder) { - IRsendTest irsend(4); - irsend.begin(); - irsend.sendData(1, 2, 3, 4, 0b10, 2, true); - EXPECT_EQ("m1s2m3s4", irsend.outputStr()); - irsend.sendData(1, 2, 3, 4, 0b10, 2, false); - EXPECT_EQ("m3s4m1s2", irsend.outputStr()); - irsend.sendData(1, 2, 3, 4, 0b0001, 4, false); - EXPECT_EQ("m1s2m3s4m3s4m3s4", irsend.outputStr()); -} - -// Test sending typical data. -TEST(TestSendData, SendTypicalData) { - IRsendTest irsend(4); - irsend.begin(); - irsend.sendData(1, 2, 3, 4, 0b1010110011110000, 16, true); - EXPECT_EQ("m1s2m3s4m1s2m3s4m1s2m1s2m3s4m3s4m1s2m1s2m1s2m1s2m3s4m3s4m3s4m3s4", - irsend.outputStr()); - irsend.sendData(1, 2, 3, 4, 0x1234567890ABCDEF, 64, true); - EXPECT_EQ( - "m3s4m3s4m3s4m1s2m3s4m3s4m1s2m3s4m3s4m3s4m1s2m1s2m3s4m1s2m3s4m3s4" - "m3s4m1s2m3s4m1s2m3s4m1s2m1s2m3s4m3s4m1s2m1s2m1s2m1s2m3s4m3s4m3s4" - "m1s2m3s4m3s4m1s2m3s4m3s4m3s4m3s4m1s2m3s4m1s2m3s4m1s2m3s4m1s2m1s2" - "m1s2m1s2m3s4m3s4m1s2m1s2m3s4m1s2m1s2m1s2m1s2m3s4m1s2m1s2m1s2m1s2", - irsend.outputStr()); -} - -// Test sending more than expected bits. -TEST(TestSendData, SendOverLargeData) { - IRsendTest irsend(4); - irsend.begin(); - irsend.sendData(1, 2, 3, 4, 0xFFFFFFFFFFFFFFFF, 70, true); - EXPECT_EQ( - "m3s4m3s4m3s4m3s4m3s4m3s4" - "m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2" - "m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2" - "m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2" - "m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2", - irsend.outputStr()); -} - -// Test inverting the output. -TEST(TestIRSend, InvertedOutput) { - IRsendTest irsend(4, true); - irsend.begin(); - irsend.sendData(1, 2, 3, 4, 0b1, 1, true); - EXPECT_EQ("s1m2", irsend.outputStr()); - irsend.sendData(1, 2, 3, 4, 0b0, 1, true); - EXPECT_EQ("s3m4", irsend.outputStr()); -} - -// Test typical use of sendRaw(). -TEST(TestSendRaw, GeneralUse) { - IRsendTest irsend(4); - IRrecv irrecv(0); - - irsend.begin(); - // NEC C3E0E0E8 as measured in #204 - uint16_t rawData[67] = { - 8950, 4500, 550, 1650, 600, 1650, 550, 550, 600, 500, 600, 550, - 550, 550, 600, 1650, 550, 1650, 600, 1650, 600, 1650, 550, 1700, - 550, 550, 600, 550, 550, 550, 600, 500, 600, 550, 550, 1650, - 600, 1650, 600, 1650, 550, 550, 600, 500, 600, 500, 600, 550, - 550, 550, 600, 1650, 550, 1650, 600, 1650, 600, 500, 650, 1600, - 600, 500, 600, 550, 550, 550, 600}; - - irsend.sendRaw(rawData, 67, 38); - EXPECT_EQ( - "m8950s4500" - "m550s1650m600s1650m550s550m600s500m600s550m550s550m600s1650m550s1650" - "m600s1650m600s1650m550s1700m550s550m600s550m550s550m600s500m600s550" - "m550s1650m600s1650m600s1650m550s550m600s500m600s500m600s550m550s550" - "m600s1650m550s1650m600s1650m600s500m650s1600m600s500m600s550m550s550" - "m600", - irsend.outputStr()); - - irsend.reset(); - irsend.sendRaw(rawData, 67, 38); - irsend.makeDecodeResult(); - ASSERT_TRUE(irrecv.decodeNEC(&irsend.capture, kNECBits, false)); - EXPECT_EQ(NEC, irsend.capture.decode_type); - EXPECT_EQ(32, irsend.capture.bits); - EXPECT_EQ(0xC3E0E0E8, irsend.capture.value); - EXPECT_EQ( - "m8950s4500" - "m550s1650m600s1650m550s550m600s500m600s550m550s550m600s1650m550s1650" - "m600s1650m600s1650m550s1700m550s550m600s550m550s550m600s500m600s550" - "m550s1650m600s1650m600s1650m550s550m600s500m600s500m600s550m550s550" - "m600s1650m550s1650m600s1650m600s500m650s1600m600s500m600s550m550s550" - "m600", - irsend.outputStr()); -} - -// Incorrect handling of decodes from Raw. i.e. There is no gap recorded at -// the end of a command when using the interrupt code. sendRaw() best emulates -// this for unit testing purposes. sendGC() and sendXXX() will add the trailing -// gap. Users won't see this in normal use. -TEST(TestSendRaw, NoTrailingGap) { - IRsendTest irsend(4); - IRrecv irrecv(4); - irsend.begin(); - - irsend.reset(); - uint16_t rawData[67] = { - 9000, 4500, 650, 550, 650, 1650, 600, 550, 650, 550, 600, 1650, - 650, 550, 600, 1650, 650, 1650, 650, 1650, 600, 550, 650, 1650, - 650, 1650, 650, 550, 600, 1650, 650, 1650, 650, 550, 650, 550, - 650, 1650, 650, 550, 650, 550, 650, 550, 600, 550, 650, 550, - 650, 550, 650, 1650, 600, 550, 650, 1650, 650, 1650, 650, 1650, - 650, 1650, 650, 1650, 650, 1650, 600}; - irsend.sendRaw(rawData, 67, 38); - irsend.makeDecodeResult(); - EXPECT_TRUE(irrecv.decodeNEC(&irsend.capture)); - EXPECT_EQ(NEC, irsend.capture.decode_type); - EXPECT_EQ(kNECBits, irsend.capture.bits); -} - -TEST(TestLowLevelSend, MarkFrequencyModulationAt38kHz) { - IRsendLowLevelTest irsend(0); - - irsend.begin(); - - irsend.reset(); - irsend.enableIROut(38000, 50); - EXPECT_EQ(5, irsend.mark(100)); - EXPECT_EQ( - "[On]10usecs[Off]11usecs[On]10usecs[Off]11usecs[On]10usecs[Off]11usecs" - "[On]10usecs[Off]11usecs[On]10usecs[Off]6usecs", - irsend.low_level_sequence); - - irsend.reset(); - irsend.enableIROut(38000, 33); - EXPECT_EQ(5, irsend.mark(100)); - EXPECT_EQ( - "[On]6usecs[Off]15usecs[On]6usecs[Off]15usecs[On]6usecs[Off]15usecs" - "[On]6usecs[Off]15usecs[On]6usecs[Off]10usecs", - irsend.low_level_sequence); - - irsend.reset(); - irsend.enableIROut(38000, 100); - EXPECT_EQ(1, irsend.mark(1000)); - EXPECT_EQ("[On]1000usecs[Off]", irsend.low_level_sequence); -} - -TEST(TestLowLevelSend, MarkFrequencyModulationAt36_7kHz) { - IRsendLowLevelTest irsend(0); - - irsend.begin(); - - irsend.reset(); - irsend.enableIROut(36700, 50); - EXPECT_EQ(5, irsend.mark(100)); - EXPECT_EQ( - "[On]11usecs[Off]11usecs[On]11usecs[Off]11usecs[On]11usecs[Off]11usecs" - "[On]11usecs[Off]11usecs[On]11usecs[Off]1usecs", - irsend.low_level_sequence); - - irsend.reset(); - irsend.enableIROut(36700, 33); - EXPECT_EQ(5, irsend.mark(100)); - EXPECT_EQ( - "[On]7usecs[Off]15usecs[On]7usecs[Off]15usecs[On]7usecs[Off]15usecs" - "[On]7usecs[Off]15usecs[On]7usecs[Off]5usecs", - irsend.low_level_sequence); - - irsend.reset(); - irsend.enableIROut(36700, 100); - EXPECT_EQ(1, irsend.mark(1000)); - EXPECT_EQ("[On]1000usecs[Off]", irsend.low_level_sequence); -} - -TEST(TestLowLevelSend, MarkFrequencyModulationAt40kHz) { - IRsendLowLevelTest irsend(0); - - irsend.begin(); - - irsend.reset(); - irsend.enableIROut(40000, 50); - EXPECT_EQ(5, irsend.mark(100)); - EXPECT_EQ( - "[On]10usecs[Off]10usecs[On]10usecs[Off]10usecs[On]10usecs[Off]10usecs" - "[On]10usecs[Off]10usecs[On]10usecs[Off]10usecs", - irsend.low_level_sequence); - - irsend.reset(); - irsend.enableIROut(40000, 33); - EXPECT_EQ(5, irsend.mark(100)); - EXPECT_EQ( - "[On]6usecs[Off]14usecs[On]6usecs[Off]14usecs[On]6usecs[Off]14usecs" - "[On]6usecs[Off]14usecs[On]6usecs[Off]14usecs", - irsend.low_level_sequence); - - irsend.reset(); - irsend.enableIROut(40000, 100); - EXPECT_EQ(1, irsend.mark(1000)); - EXPECT_EQ("[On]1000usecs[Off]", irsend.low_level_sequence); -} - -TEST(TestLowLevelSend, MarkNoModulation) { - IRsendLowLevelTest irsend(0, false, false); - - irsend.begin(); - - irsend.reset(); - irsend.enableIROut(38000, 50); - EXPECT_EQ(1, irsend.mark(1000)); - EXPECT_EQ("[On]1000usecs[Off]", irsend.low_level_sequence); - - irsend.reset(); - irsend.enableIROut(36700, 25); - EXPECT_EQ(1, irsend.mark(1000)); - EXPECT_EQ("[On]1000usecs[Off]", irsend.low_level_sequence); - - irsend.reset(); - irsend.enableIROut(40000, 75); - EXPECT_EQ(1, irsend.mark(1000)); - EXPECT_EQ("[On]1000usecs[Off]", irsend.low_level_sequence); -} - -TEST(TestLowLevelSend, SpaceFrequencyModulation) { - IRsendLowLevelTest irsend(0); - - irsend.reset(); - irsend.enableIROut(38000); - irsend.space(1000); - EXPECT_EQ("[Off]1000usecs", irsend.low_level_sequence); - - irsend.reset(); - irsend.enableIROut(40000, 75); - irsend.space(1000); - EXPECT_EQ("[Off]1000usecs", irsend.low_level_sequence); - - irsend.reset(); - irsend.enableIROut(38000, 100); - irsend.space(1000); - EXPECT_EQ("[Off]1000usecs", irsend.low_level_sequence); - - irsend.reset(); - irsend.enableIROut(38000, 33); - irsend.space(1000); - EXPECT_EQ("[Off]1000usecs", irsend.low_level_sequence); -} - -TEST(TestLowLevelSend, SpaceNoModulation) { - IRsendLowLevelTest irsend(0, false, false); - - irsend.begin(); - - irsend.reset(); - irsend.enableIROut(38000, 50); - irsend.space(1000); - EXPECT_EQ("[Off]1000usecs", irsend.low_level_sequence); - - irsend.reset(); - irsend.enableIROut(36700, 25); - irsend.space(1000); - EXPECT_EQ("[Off]1000usecs", irsend.low_level_sequence); - - irsend.reset(); - irsend.enableIROut(40000, 75); - irsend.space(1000); - EXPECT_EQ("[Off]1000usecs", irsend.low_level_sequence); -} diff --git a/lib/IRremoteESP8266-2.5.2.03/test/ir_Daikin_test.cpp b/lib/IRremoteESP8266-2.5.2.03/test/ir_Daikin_test.cpp deleted file mode 100644 index c8192fc8235d..000000000000 --- a/lib/IRremoteESP8266-2.5.2.03/test/ir_Daikin_test.cpp +++ /dev/null @@ -1,838 +0,0 @@ -// Copyright 2017 David Conran -#include "ir_Daikin.h" -#include "IRrecv.h" -#include "IRrecv_test.h" -#include "IRsend.h" -#include "IRsend_test.h" -#include "gtest/gtest.h" - -// Tests for sendDaikin(). - -// Test sending typical data only. -TEST(TestSendDaikin, SendDataOnly) { - IRsendTest irsend(4); - irsend.begin(); - - uint8_t daikin_code[kDaikinStateLength] = { - 0x11, 0xDA, 0x27, 0xF0, 0x00, 0x00, 0x00, 0x20, 0x11, - 0xDA, 0x27, 0x00, 0x00, 0x41, 0x1E, 0x00, 0xB0, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xE3}; - - irsend.reset(); - irsend.sendDaikin(daikin_code); - EXPECT_EQ( - "m428s428m428s428m428s428m428s428m428s428" - "m428s29428m3650s1623" - "m428s1280m428s428m428s428m428s428m428s1280m428s428m428s428m428s428" - "m428s428m428s1280m428s428m428s1280m428s1280m428s428m428s1280m428s1280" - "m428s1280m428s1280m428s1280m428s428m428s428m428s1280m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s1280m428s428m428s1280m428s428m428s428m428s428m428s1280m428s1280" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s1280m428s1280m428s1280m428s428m428s1280m428s428m428s1280m428s1280" - "m428s29428m3650s1623" - "m428s1280m428s428m428s428m428s428m428s1280m428s428m428s428m428s428" - "m428s428m428s1280m428s428m428s1280m428s1280m428s428m428s1280m428s1280" - "m428s1280m428s1280m428s1280m428s428m428s428m428s1280m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s1280m428s1280m428s1280m428s1280" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s428m428s1280m428s428m428s428" - "m428s29428m3650s1623" - "m428s1280m428s428m428s428m428s428m428s1280m428s428m428s428m428s428" - "m428s428m428s1280m428s428m428s1280m428s1280m428s428m428s1280m428s1280" - "m428s1280m428s1280m428s1280m428s428m428s428m428s1280m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s1280m428s428m428s428m428s428m428s428m428s428m428s1280m428s428" - "m428s428m428s1280m428s1280m428s1280m428s1280m428s428m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s1280m428s1280m428s428m428s1280" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s1280m428s1280" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s1280m428s1280m428s428m428s428m428s428m428s1280m428s1280m428s1280" - "m428s29428", - irsend.outputStr()); -} - -// Test sending with repeats. -TEST(TestSendDaikin, SendWithRepeats) { - IRsendTest irsend(4); - irsend.begin(); - - irsend.reset(); - uint8_t daikin_code[kDaikinStateLength] = { - 0x11, 0xDA, 0x27, 0xF0, 0x00, 0x00, 0x00, 0x20, 0x11, - 0xDA, 0x27, 0x00, 0x00, 0x41, 0x1E, 0x00, 0xB0, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xE3}; - irsend.reset(); - - irsend.sendDaikin(daikin_code, kDaikinStateLength, 1); - EXPECT_EQ( - "m428s428m428s428m428s428m428s428m428s428" - "m428s29428m3650s1623" - "m428s1280m428s428m428s428m428s428m428s1280m428s428m428s428m428s428" - "m428s428m428s1280m428s428m428s1280m428s1280m428s428m428s1280m428s1280" - "m428s1280m428s1280m428s1280m428s428m428s428m428s1280m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s1280m428s428m428s1280m428s428m428s428m428s428m428s1280m428s1280" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s1280m428s1280m428s1280m428s428m428s1280m428s428m428s1280m428s1280" - "m428s29428m3650s1623" - "m428s1280m428s428m428s428m428s428m428s1280m428s428m428s428m428s428" - "m428s428m428s1280m428s428m428s1280m428s1280m428s428m428s1280m428s1280" - "m428s1280m428s1280m428s1280m428s428m428s428m428s1280m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s1280m428s1280m428s1280m428s1280" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s428m428s1280m428s428m428s428" - "m428s29428m3650s1623" - "m428s1280m428s428m428s428m428s428m428s1280m428s428m428s428m428s428" - "m428s428m428s1280m428s428m428s1280m428s1280m428s428m428s1280m428s1280" - "m428s1280m428s1280m428s1280m428s428m428s428m428s1280m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s1280m428s428m428s428m428s428m428s428m428s428m428s1280m428s428" - "m428s428m428s1280m428s1280m428s1280m428s1280m428s428m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s1280m428s1280m428s428m428s1280" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s1280m428s1280" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s1280m428s1280m428s428m428s428m428s428m428s1280m428s1280m428s1280" - "m428s29428" - "m428s428m428s428m428s428m428s428m428s428" - "m428s29428m3650s1623" - "m428s1280m428s428m428s428m428s428m428s1280m428s428m428s428m428s428" - "m428s428m428s1280m428s428m428s1280m428s1280m428s428m428s1280m428s1280" - "m428s1280m428s1280m428s1280m428s428m428s428m428s1280m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s1280m428s428m428s1280m428s428m428s428m428s428m428s1280m428s1280" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s1280m428s1280m428s1280m428s428m428s1280m428s428m428s1280m428s1280" - "m428s29428m3650s1623" - "m428s1280m428s428m428s428m428s428m428s1280m428s428m428s428m428s428" - "m428s428m428s1280m428s428m428s1280m428s1280m428s428m428s1280m428s1280" - "m428s1280m428s1280m428s1280m428s428m428s428m428s1280m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s1280m428s1280m428s1280m428s1280" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s428m428s1280m428s428m428s428" - "m428s29428m3650s1623" - "m428s1280m428s428m428s428m428s428m428s1280m428s428m428s428m428s428" - "m428s428m428s1280m428s428m428s1280m428s1280m428s428m428s1280m428s1280" - "m428s1280m428s1280m428s1280m428s428m428s428m428s1280m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s1280m428s428m428s428m428s428m428s428m428s428m428s1280m428s428" - "m428s428m428s1280m428s1280m428s1280m428s1280m428s428m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s1280m428s1280m428s428m428s1280" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s1280m428s1280" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s1280m428s1280m428s428m428s428m428s428m428s1280m428s1280m428s1280" - "m428s29428", - irsend.outputStr()); -} - -// Test sending atypical sizes. -TEST(TestSendDaikin, SendUnexpectedSizes) { - IRsendTest irsend(4); - irsend.begin(); - - uint8_t daikin_short_code[kDaikinStateLength - 1] = { - 0x11, 0xDA, 0x27, 0xF0, 0x00, 0x00, 0x00, 0x20, 0x11, - 0xDA, 0x27, 0x00, 0x00, 0x41, 0x1E, 0x00, 0xB0, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00}; - - irsend.reset(); - irsend.sendDaikin(daikin_short_code, kDaikinStateLength - 1); - ASSERT_EQ("", irsend.outputStr()); - - uint8_t daikin_long_code[kDaikinStateLength + 1] = { - 0x11, 0xDA, 0x27, 0xF0, 0x00, 0x00, 0x00, 0x20, 0x11, 0xDA, - 0x27, 0x00, 0x00, 0x41, 0x1E, 0x00, 0xB0, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xE3, 0x11}; - irsend.reset(); - irsend.sendDaikin(daikin_long_code, kDaikinStateLength + 1); - ASSERT_EQ( - "m428s428m428s428m428s428m428s428m428s428" - "m428s29428m3650s1623" - "m428s1280m428s428m428s428m428s428m428s1280m428s428m428s428m428s428" - "m428s428m428s1280m428s428m428s1280m428s1280m428s428m428s1280m428s1280" - "m428s1280m428s1280m428s1280m428s428m428s428m428s1280m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s1280m428s428m428s1280m428s428m428s428m428s428m428s1280m428s1280" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s1280m428s1280m428s1280m428s428m428s1280m428s428m428s1280m428s1280" - "m428s29428m3650s1623" - "m428s1280m428s428m428s428m428s428m428s1280m428s428m428s428m428s428" - "m428s428m428s1280m428s428m428s1280m428s1280m428s428m428s1280m428s1280" - "m428s1280m428s1280m428s1280m428s428m428s428m428s1280m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s1280m428s1280m428s1280m428s1280" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s428m428s1280m428s428m428s428" - "m428s29428m3650s1623" - "m428s1280m428s428m428s428m428s428m428s1280m428s428m428s428m428s428" - "m428s428m428s1280m428s428m428s1280m428s1280m428s428m428s1280m428s1280" - "m428s1280m428s1280m428s1280m428s428m428s428m428s1280m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s1280m428s428m428s428m428s428m428s428m428s428m428s1280m428s428" - "m428s428m428s1280m428s1280m428s1280m428s1280m428s428m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s1280m428s1280m428s428m428s1280" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s1280m428s1280" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s1280m428s1280m428s428m428s428m428s428m428s1280m428s1280m428s1280" - "m428s1280m428s428m428s428m428s428m428s1280m428s428m428s428m428s428" - "m428s29428", - irsend.outputStr()); -} - -// Tests for IRDaikinESP class. - -TEST(TestDaikinClass, Power) { - IRDaikinESP irdaikin(0); - irdaikin.begin(); - - irdaikin.on(); - EXPECT_TRUE(irdaikin.getPower()); - - irdaikin.off(); - EXPECT_FALSE(irdaikin.getPower()); - - irdaikin.setPower(true); - EXPECT_TRUE(irdaikin.getPower()); - - irdaikin.setPower(false); - EXPECT_FALSE(irdaikin.getPower()); -} - -TEST(TestDaikinClass, Temperature) { - IRDaikinESP irdaikin(0); - irdaikin.begin(); - - irdaikin.setTemp(0); - EXPECT_EQ(kDaikinMinTemp, irdaikin.getTemp()); - - irdaikin.setTemp(255); - EXPECT_EQ(kDaikinMaxTemp, irdaikin.getTemp()); - - irdaikin.setTemp(kDaikinMinTemp); - EXPECT_EQ(kDaikinMinTemp, irdaikin.getTemp()); - - irdaikin.setTemp(kDaikinMaxTemp); - EXPECT_EQ(kDaikinMaxTemp, irdaikin.getTemp()); - - irdaikin.setTemp(kDaikinMinTemp - 1); - EXPECT_EQ(kDaikinMinTemp, irdaikin.getTemp()); - - irdaikin.setTemp(kDaikinMaxTemp + 1); - EXPECT_EQ(kDaikinMaxTemp, irdaikin.getTemp()); - - irdaikin.setTemp(kDaikinMinTemp + 1); - EXPECT_EQ(kDaikinMinTemp + 1, irdaikin.getTemp()); - - irdaikin.setTemp(21); - EXPECT_EQ(21, irdaikin.getTemp()); - - irdaikin.setTemp(25); - EXPECT_EQ(25, irdaikin.getTemp()); - - irdaikin.setTemp(29); - EXPECT_EQ(29, irdaikin.getTemp()); -} - -TEST(TestDaikinClass, OperatingMode) { - IRDaikinESP irdaikin(0); - irdaikin.begin(); - - irdaikin.setMode(kDaikinAuto); - EXPECT_EQ(kDaikinAuto, irdaikin.getMode()); - - irdaikin.setMode(kDaikinCool); - EXPECT_EQ(kDaikinCool, irdaikin.getMode()); - - irdaikin.setMode(kDaikinHeat); - EXPECT_EQ(kDaikinHeat, irdaikin.getMode()); - - irdaikin.setMode(kDaikinDry); - EXPECT_EQ(kDaikinDry, irdaikin.getMode()); - - irdaikin.setMode(kDaikinFan); - EXPECT_EQ(kDaikinFan, irdaikin.getMode()); - - irdaikin.setMode(kDaikinFan + 1); - EXPECT_EQ(kDaikinAuto, irdaikin.getMode()); - - irdaikin.setMode(kDaikinAuto + 1); - EXPECT_EQ(kDaikinAuto, irdaikin.getMode()); - - irdaikin.setMode(255); - EXPECT_EQ(kDaikinAuto, irdaikin.getMode()); -} - -TEST(TestDaikinClass, VaneSwing) { - IRDaikinESP irdaikin(0); - irdaikin.begin(); - - irdaikin.setSwingHorizontal(true); - irdaikin.setSwingVertical(false); - - irdaikin.setSwingHorizontal(true); - EXPECT_TRUE(irdaikin.getSwingHorizontal()); - EXPECT_FALSE(irdaikin.getSwingVertical()); - - irdaikin.setSwingVertical(true); - EXPECT_TRUE(irdaikin.getSwingHorizontal()); - EXPECT_TRUE(irdaikin.getSwingVertical()); - - irdaikin.setSwingHorizontal(false); - EXPECT_FALSE(irdaikin.getSwingHorizontal()); - EXPECT_TRUE(irdaikin.getSwingVertical()); - - irdaikin.setSwingVertical(false); - EXPECT_FALSE(irdaikin.getSwingHorizontal()); - EXPECT_FALSE(irdaikin.getSwingVertical()); -} - -TEST(TestDaikinClass, QuietMode) { - IRDaikinESP irdaikin(0); - irdaikin.begin(); - - irdaikin.setQuiet(true); - EXPECT_TRUE(irdaikin.getQuiet()); - - irdaikin.setQuiet(false); - EXPECT_FALSE(irdaikin.getQuiet()); - - irdaikin.setQuiet(true); - EXPECT_TRUE(irdaikin.getQuiet()); - - // Setting Econo mode should NOT change out of quiet mode. - irdaikin.setEcono(true); - EXPECT_TRUE(irdaikin.getQuiet()); - irdaikin.setEcono(false); - EXPECT_TRUE(irdaikin.getQuiet()); - - // But setting Powerful mode should exit out of quiet mode. - irdaikin.setPowerful(true); - EXPECT_FALSE(irdaikin.getQuiet()); -} - -TEST(TestDaikinClass, PowerfulMode) { - IRDaikinESP irdaikin(0); - irdaikin.begin(); - - irdaikin.setPowerful(true); - EXPECT_TRUE(irdaikin.getPowerful()); - - irdaikin.setPowerful(false); - EXPECT_FALSE(irdaikin.getPowerful()); - - irdaikin.setPowerful(true); - EXPECT_TRUE(irdaikin.getPowerful()); - - irdaikin.setQuiet(true); - EXPECT_FALSE(irdaikin.getPowerful()); - - irdaikin.setPowerful(true); - irdaikin.setEcono(true); - EXPECT_FALSE(irdaikin.getPowerful()); -} - -TEST(TestDaikinClass, EconoMode) { - IRDaikinESP irdaikin(0); - irdaikin.begin(); - - irdaikin.setEcono(true); - EXPECT_TRUE(irdaikin.getEcono()); - - irdaikin.setEcono(false); - EXPECT_FALSE(irdaikin.getEcono()); - - irdaikin.setEcono(true); - EXPECT_TRUE(irdaikin.getEcono()); - - // Setting Quiet mode should NOT change out of Econo mode. - irdaikin.setQuiet(true); - EXPECT_TRUE(irdaikin.getEcono()); - irdaikin.setQuiet(false); - EXPECT_TRUE(irdaikin.getEcono()); - - // But setting Powerful mode should exit out of Econo mode. - irdaikin.setPowerful(true); - EXPECT_FALSE(irdaikin.getEcono()); -} - -TEST(TestDaikinClass, FanSpeed) { - IRDaikinESP irdaikin(0); - irdaikin.begin(); - - // Unexpected value should default to Auto. - irdaikin.setFan(0); - EXPECT_EQ(kDaikinFanAuto, irdaikin.getFan()); - - // Unexpected value should default to Auto. - irdaikin.setFan(255); - EXPECT_EQ(kDaikinFanAuto, irdaikin.getFan()); - - irdaikin.setFan(kDaikinFanMax); - EXPECT_EQ(kDaikinFanMax, irdaikin.getFan()); - - // Beyond Max should default to Auto. - irdaikin.setFan(kDaikinFanMax + 1); - EXPECT_EQ(kDaikinFanAuto, irdaikin.getFan()); - - irdaikin.setFan(kDaikinFanMax - 1); - EXPECT_EQ(kDaikinFanMax - 1, irdaikin.getFan()); - - irdaikin.setFan(kDaikinFanMin); - EXPECT_EQ(kDaikinFanMin, irdaikin.getFan()); - - irdaikin.setFan(kDaikinFanMin + 1); - EXPECT_EQ(kDaikinFanMin + 1, irdaikin.getFan()); - - // Beyond Min should default to Auto. - irdaikin.setFan(kDaikinFanMin - 1); - EXPECT_EQ(kDaikinFanAuto, irdaikin.getFan()); - - irdaikin.setFan(3); - EXPECT_EQ(3, irdaikin.getFan()); - - irdaikin.setFan(kDaikinFanAuto); - EXPECT_EQ(kDaikinFanAuto, irdaikin.getFan()); - - irdaikin.setFan(kDaikinFanQuiet); - EXPECT_EQ(kDaikinFanQuiet, irdaikin.getFan()); -} - -TEST(TestDaikinClass, CurrentTime) { - IRDaikinESP irdaikin(0); - irdaikin.begin(); - - irdaikin.setCurrentTime(0); // 00:00 - EXPECT_EQ(0, irdaikin.getCurrentTime()); - - irdaikin.setCurrentTime(754); // 12:34 - EXPECT_EQ(754, irdaikin.getCurrentTime()); - - irdaikin.setCurrentTime(1439); // 23:59 - EXPECT_EQ(1439, irdaikin.getCurrentTime()); -} - -TEST(TestDaikinClass, OnOffTimers) { - IRDaikinESP irdaikin(0); - irdaikin.begin(); - - // Both timers turned off. - irdaikin.disableOnTimer(); - irdaikin.disableOffTimer(); - EXPECT_FALSE(irdaikin.getOnTimerEnabled()); - EXPECT_EQ(0x600, irdaikin.getOnTime()); - EXPECT_FALSE(irdaikin.getOffTimerEnabled()); - EXPECT_EQ(0x600, irdaikin.getOffTime()); - - // Turn on just the On Timer. - irdaikin.enableOnTimer(123); - EXPECT_TRUE(irdaikin.getOnTimerEnabled()); - EXPECT_EQ(123, irdaikin.getOnTime()); - EXPECT_FALSE(irdaikin.getOffTimerEnabled()); - EXPECT_EQ(0x600, irdaikin.getOffTime()); - - // Now turn on the Off Timer. - irdaikin.enableOffTimer(754); - EXPECT_TRUE(irdaikin.getOffTimerEnabled()); - EXPECT_EQ(754, irdaikin.getOffTime()); - EXPECT_TRUE(irdaikin.getOnTimerEnabled()); - EXPECT_EQ(123, irdaikin.getOnTime()); - - // Turn off the just the On Timer. - irdaikin.disableOnTimer(); - EXPECT_FALSE(irdaikin.getOnTimerEnabled()); - EXPECT_EQ(0x600, irdaikin.getOnTime()); - EXPECT_TRUE(irdaikin.getOffTimerEnabled()); - EXPECT_EQ(754, irdaikin.getOffTime()); - - // Now turn off the Off Timer. - irdaikin.disableOffTimer(); - EXPECT_FALSE(irdaikin.getOffTimerEnabled()); - EXPECT_EQ(0x600, irdaikin.getOffTime()); - EXPECT_FALSE(irdaikin.getOnTimerEnabled()); - EXPECT_EQ(0x600, irdaikin.getOnTime()); - - // Use some canary values around the timers to ensure no accidental - // bit flips happen. i.e. Neighbouring bytes in the state. - // (Found some during testing on systems with different endian-ness) - // Tests here to make sure it never happens again. - irdaikin.setSwingHorizontal(true); - irdaikin.setPowerful(true); - irdaikin.disableOffTimer(); - irdaikin.disableOnTimer(); - ASSERT_TRUE(irdaikin.getSwingHorizontal()); - ASSERT_TRUE(irdaikin.getPowerful()); - irdaikin.enableOnTimer(123); - irdaikin.enableOffTimer(456); - ASSERT_TRUE(irdaikin.getSwingHorizontal()); - ASSERT_TRUE(irdaikin.getPowerful()); - irdaikin.disableOffTimer(); - irdaikin.disableOnTimer(); - ASSERT_TRUE(irdaikin.getSwingHorizontal()); - ASSERT_TRUE(irdaikin.getPowerful()); - - irdaikin.setSwingHorizontal(false); - irdaikin.setPowerful(false); - irdaikin.disableOffTimer(); - irdaikin.disableOnTimer(); - ASSERT_FALSE(irdaikin.getSwingHorizontal()); - ASSERT_FALSE(irdaikin.getPowerful()); - irdaikin.enableOnTimer(123); - irdaikin.enableOffTimer(456); - ASSERT_FALSE(irdaikin.getSwingHorizontal()); - ASSERT_FALSE(irdaikin.getPowerful()); - irdaikin.disableOffTimer(); - irdaikin.disableOnTimer(); - ASSERT_FALSE(irdaikin.getSwingHorizontal()); - ASSERT_FALSE(irdaikin.getPowerful()); -} - -// Test Eye mode. -TEST(TestDaikinClass, EyeSetting) { - IRDaikinESP irdaikin(0); - irdaikin.begin(); - - // The Eye setting is stored in the same byte as Econo mode. - // Econo mode tests are there to make sure it isn't harmed and vice-versa. - irdaikin.setEcono(false); - irdaikin.setEye(false); - ASSERT_FALSE(irdaikin.getEye()); - EXPECT_FALSE(irdaikin.getEcono()); - - irdaikin.setEye(true); - ASSERT_TRUE(irdaikin.getEye()); - EXPECT_FALSE(irdaikin.getEcono()); - - irdaikin.setEcono(false); - ASSERT_TRUE(irdaikin.getEye()); - EXPECT_FALSE(irdaikin.getEcono()); - - irdaikin.setEcono(true); - ASSERT_TRUE(irdaikin.getEye()); - EXPECT_TRUE(irdaikin.getEcono()); - - irdaikin.setEye(false); - ASSERT_FALSE(irdaikin.getEye()); - EXPECT_TRUE(irdaikin.getEcono()); -} - -// Test Mold mode. -TEST(TestDaikinClass, MoldSetting) { - IRDaikinESP irdaikin(0); - irdaikin.begin(); - - irdaikin.setMold(false); - ASSERT_FALSE(irdaikin.getMold()); - - irdaikin.setMold(true); - ASSERT_TRUE(irdaikin.getMold()); - - irdaikin.setMold(false); - ASSERT_FALSE(irdaikin.getMold()); -} - -// Test Sensor mode. -TEST(TestDaikinClass, SensorSetting) { - IRDaikinESP irdaikin(0); - irdaikin.begin(); - - irdaikin.setSensor(false); - ASSERT_FALSE(irdaikin.getSensor()); - - irdaikin.setSensor(true); - ASSERT_TRUE(irdaikin.getSensor()); - - irdaikin.setSensor(false); - ASSERT_FALSE(irdaikin.getSensor()); -} - -TEST(TestDaikinClass, RenderTime) { - EXPECT_EQ("0:00", IRDaikinESP::renderTime(0)); - EXPECT_EQ("0:10", IRDaikinESP::renderTime(10)); - EXPECT_EQ("1:00", IRDaikinESP::renderTime(1 * 60 + 0)); - EXPECT_EQ("23:59", IRDaikinESP::renderTime(23 * 60 + 59)); -} - -TEST(TestDaikinClass, SetAndGetRaw) { - IRDaikinESP irdaikin(0); - uint8_t initialState[kDaikinStateLength] = { - 0x11, 0xDA, 0x27, 0x00, 0x42, 0x00, 0x00, 0x54, 0x11, - 0xDA, 0x27, 0x00, 0x00, 0x49, 0x1E, 0x00, 0xB0, 0x00, - 0x00, 0x06, 0x60, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x4F}; - uint8_t expectedState[kDaikinStateLength] = { - 0x11, 0xDA, 0x27, 0x00, 0x42, 0x00, 0x00, 0x54, 0x11, - 0xDA, 0x27, 0x00, 0x00, 0x48, 0x2A, 0x00, 0xB0, 0x00, - 0x00, 0x06, 0x60, 0x00, 0x00, 0xC0, 0x00, 0x02, 0x5A}; - - EXPECT_STATE_EQ(initialState, irdaikin.getRaw(), kDaikinBits); - // toggle the power state. - irdaikin.setPower(!irdaikin.getPower()); - irdaikin.setTemp(21); - irdaikin.setMold(true); - EXPECT_STATE_EQ(expectedState, irdaikin.getRaw(), kDaikinBits); - irdaikin.setRaw(initialState); - EXPECT_STATE_EQ(initialState, irdaikin.getRaw(), kDaikinBits); -} - -TEST(TestDaikinClass, ChecksumValidation) { - uint8_t daikin_code[kDaikinStateLength] = { - 0x11, 0xDA, 0x27, 0xF0, 0x00, 0x00, 0x00, 0x02, 0x11, - 0xDA, 0x27, 0x00, 0x00, 0x41, 0x1E, 0x00, 0xB0, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xE1}; - - EXPECT_TRUE(IRDaikinESP::validChecksum(daikin_code)); - // Change the array so the checksum is invalid. - daikin_code[0] ^= 0xFF; - EXPECT_FALSE(IRDaikinESP::validChecksum(daikin_code)); - // Restore the previous change, and change another byte. - daikin_code[0] ^= 0xFF; - daikin_code[4] ^= 0xFF; - EXPECT_FALSE(IRDaikinESP::validChecksum(daikin_code)); - daikin_code[4] ^= 0xFF; - // Change something in the 2nd block. - daikin_code[10] ^= 0xFF; - EXPECT_FALSE(IRDaikinESP::validChecksum(daikin_code)); - daikin_code[10] ^= 0xFF; - EXPECT_TRUE(IRDaikinESP::validChecksum(daikin_code)); -} - -// Test human readable output. -TEST(TestDaikinClass, HumanReadable) { - IRDaikinESP irdaikin(0); - - EXPECT_EQ( - "Power: On, Mode: 4 (HEAT), Temp: 15C, Fan: 11 (QUIET), " - "Powerful: Off, Quiet: Off, Sensor: Off, Eye: Off, Mold: Off, " - "Swing (Horizontal): Off, Swing (Vertical): Off, " - "Current Time: 0:00, On Time: Off, Off Time: Off", - irdaikin.toString()); - irdaikin.setMode(kDaikinAuto); - irdaikin.setTemp(25); - irdaikin.setFan(kDaikinFanAuto); - irdaikin.setQuiet(true); - irdaikin.setSensor(true); - irdaikin.setEye(true); - irdaikin.setMold(true); - irdaikin.setSwingVertical(true); - irdaikin.setSwingHorizontal(true); - irdaikin.setCurrentTime(9 * 60 + 15); - irdaikin.enableOnTimer(8 * 60 + 0); - irdaikin.enableOffTimer(17 * 60 + 30); - irdaikin.off(); - EXPECT_EQ( - "Power: Off, Mode: 0 (AUTO), Temp: 25C, Fan: 10 (AUTO), " - "Powerful: Off, Quiet: On, Sensor: On, Eye: On, Mold: On, " - "Swing (Horizontal): On, Swing (Vertical): On, " - "Current Time: 9:15, On Time: 8:00, Off Time: 17:30", - irdaikin.toString()); -} - -// Test general message construction after tweaking some settings. -TEST(TestDaikinClass, MessageConstuction) { - IRDaikinESP irdaikin(0); - IRsendTest irsend(4); - irdaikin.begin(); - irsend.begin(); - - irdaikin.setFan(kDaikinFanMin); - irdaikin.setMode(kDaikinCool); - irdaikin.setTemp(27); - irdaikin.setSwingVertical(false); - irdaikin.setSwingHorizontal(true); - irdaikin.setQuiet(false); - irdaikin.setPower(true); - - // Check everything for kicks. - EXPECT_EQ(kDaikinFanMin, irdaikin.getFan()); - EXPECT_EQ(kDaikinCool, irdaikin.getMode()); - EXPECT_EQ(27, irdaikin.getTemp()); - EXPECT_FALSE(irdaikin.getSwingVertical()); - EXPECT_TRUE(irdaikin.getSwingHorizontal()); - EXPECT_FALSE(irdaikin.getQuiet()); - EXPECT_TRUE(irdaikin.getPower()); - - irsend.reset(); - irsend.sendDaikin(irdaikin.getRaw()); - EXPECT_EQ( - "m428s428m428s428m428s428m428s428m428s428" - "m428s29428m3650s1623" - "m428s1280m428s428m428s428m428s428m428s1280m428s428m428s428m428s428" - "m428s428m428s1280m428s428m428s1280m428s1280m428s428m428s1280m428s1280" - "m428s1280m428s1280m428s1280m428s428m428s428m428s1280m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s1280m428s428m428s1280m428s428m428s428m428s428m428s1280m428s1280" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s1280m428s1280m428s1280m428s428m428s1280m428s428m428s1280m428s1280" - "m428s29428m3650s1623" - "m428s1280m428s428m428s428m428s428m428s1280m428s428m428s428m428s428" - "m428s428m428s1280m428s428m428s1280m428s1280m428s428m428s1280m428s1280" - "m428s1280m428s1280m428s1280m428s428m428s428m428s1280m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s428m428s1280m428s428m428s428m428s428m428s428m428s1280m428s428" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s428m428s428m428s1280m428s428m428s1280m428s428m428s1280m428s428" - "m428s29428m3650s1623" - "m428s1280m428s428m428s428m428s428m428s1280m428s428m428s428m428s428" - "m428s428m428s1280m428s428m428s1280m428s1280m428s428m428s1280m428s1280" - "m428s1280m428s1280m428s1280m428s428m428s428m428s1280m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s1280m428s428m428s428m428s1280m428s1280m428s1280m428s428m428s428" - "m428s428m428s1280m428s1280m428s428m428s1280m428s1280m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s1280m428s1280m428s428m428s428" - "m428s1280m428s1280m428s1280m428s1280m428s428m428s428m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s428m428s1280m428s1280m428s428m428s428m428s428m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s428m428s1280m428s1280m428s428" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s1280m428s1280" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s428m428s1280m428s1280m428s428m428s428m428s1280m428s1280m428s1280" - "m428s29428", - irsend.outputStr()); -} - -// Tests for decodeDaikin(). - -// Test decoding a message captured from a real IR remote. -TEST(TestDecodeDaikin, RealExample) { - IRDaikinESP irdaikin(0); - IRsendTest irsend(4); - IRrecv irrecv(4); - irsend.begin(); - - uint8_t expectedState[kDaikinStateLength] = { - 0x11, 0xDA, 0x27, 0x00, 0x42, 0x3A, 0x05, 0x93, 0x11, - 0xDA, 0x27, 0x00, 0x00, 0x3F, 0x3A, 0x00, 0xA0, 0x00, - 0x0A, 0x25, 0x17, 0x01, 0x00, 0xC0, 0x00, 0x00, 0x32}; - uint16_t rawData[kDaikinRawBits] = { - 416, 446, 416, 446, 416, 446, 418, 446, 416, 446, 416, 25434, - 3436, 1768, 390, 1336, 390, 446, 416, 446, 416, 446, 416, 1336, - 390, 446, 416, 446, 416, 446, 416, 446, 416, 1336, 390, 448, - 416, 1336, 390, 1336, 390, 448, 416, 1336, 390, 1336, 390, 1338, - 388, 1338, 390, 1336, 390, 446, 416, 446, 416, 1336, 390, 446, - 416, 446, 416, 446, 416, 446, 416, 446, 416, 446, 416, 448, - 416, 446, 416, 446, 416, 446, 416, 1336, 390, 446, 416, 1336, - 390, 448, 416, 446, 416, 446, 416, 1336, 390, 1336, 390, 446, - 416, 446, 416, 446, 416, 446, 416, 446, 416, 446, 416, 446, - 416, 446, 416, 446, 416, 448, 416, 446, 416, 446, 416, 446, - 416, 448, 414, 448, 416, 448, 416, 1336, 390, 1336, 390, 1336, - 390, 446, 414, 1336, 390, 448, 414, 1336, 390, 1336, 390, 34878, - 3436, 1768, 390, 1336, 390, 446, 416, 448, 416, 446, 416, 1336, - 390, 446, 416, 448, 416, 446, 416, 446, 416, 1336, 390, 446, - 416, 1336, 390, 1336, 390, 446, 416, 1336, 390, 1336, 390, 1336, - 390, 1336, 390, 1336, 392, 446, 414, 448, 416, 1336, 390, 446, - 416, 446, 416, 446, 416, 446, 414, 448, 416, 446, 416, 448, - 414, 448, 416, 446, 416, 446, 416, 446, 414, 1336, 390, 448, - 416, 446, 416, 446, 416, 448, 416, 1336, 390, 446, 416, 446, - 416, 1336, 390, 446, 416, 1336, 390, 1336, 390, 1336, 390, 446, - 416, 446, 414, 1338, 390, 446, 416, 1336, 390, 446, 416, 446, - 416, 446, 416, 446, 416, 446, 416, 1336, 390, 1336, 390, 446, - 416, 446, 416, 1336, 390, 446, 416, 446, 416, 1336, 390, 34876, - 3436, 1768, 388, 1336, 390, 446, 416, 446, 416, 448, 416, 1336, - 390, 446, 416, 446, 416, 446, 416, 448, 416, 1336, 390, 448, - 414, 1336, 390, 1336, 390, 446, 416, 1336, 388, 1338, 388, 1336, - 390, 1336, 390, 1336, 390, 446, 416, 446, 416, 1336, 390, 446, - 420, 442, 416, 446, 416, 446, 416, 446, 416, 446, 416, 446, - 416, 446, 416, 446, 416, 446, 416, 446, 416, 446, 416, 448, - 416, 446, 416, 448, 416, 446, 416, 448, 416, 446, 416, 1336, - 390, 1336, 390, 1336, 388, 1338, 390, 1336, 390, 1336, 392, 446, - 416, 446, 416, 448, 416, 1334, 390, 446, 416, 1338, 388, 1336, - 390, 1336, 390, 446, 416, 446, 416, 448, 414, 446, 416, 446, - 416, 446, 416, 448, 416, 446, 416, 446, 416, 446, 416, 446, - 416, 446, 416, 446, 416, 446, 416, 446, 416, 1336, 390, 446, - 416, 1336, 390, 446, 414, 448, 416, 446, 416, 446, 416, 446, - 416, 448, 416, 446, 416, 446, 416, 446, 416, 1336, 390, 446, - 416, 1336, 390, 446, 416, 446, 416, 446, 416, 448, 416, 1338, - 390, 444, 418, 1336, 390, 448, 416, 446, 416, 1336, 390, 446, - 416, 446, 416, 1336, 390, 1336, 388, 1336, 390, 446, 416, 1336, - 390, 448, 414, 448, 414, 448, 416, 1334, 390, 446, 416, 446, - 416, 446, 416, 448, 416, 446, 416, 446, 416, 448, 416, 446, - 416, 446, 416, 446, 416, 446, 416, 446, 416, 446, 416, 446, - 416, 446, 416, 446, 416, 446, 416, 446, 416, 446, 416, 446, - 416, 448, 416, 1336, 390, 1336, 390, 446, 416, 446, 416, 446, - 416, 446, 414, 446, 416, 448, 416, 446, 416, 448, 414, 446, - 418, 446, 416, 446, 416, 448, 416, 446, 416, 448, 416, 446, - 416, 448, 416, 446, 416, 1336, 390, 446, 416, 446, 416, 1338, - 390, 1336, 390, 446, 416, 446, 416}; // Captured by @sillyfrog - - irsend.reset(); - irsend.sendRaw(rawData, kDaikinRawBits, 38000); - irsend.makeDecodeResult(); - EXPECT_TRUE(irrecv.decode(&irsend.capture)); - ASSERT_EQ(DAIKIN, irsend.capture.decode_type); - ASSERT_EQ(kDaikinBits, irsend.capture.bits); - EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits); -} - -// Decoding a message we entirely constructed based solely on a given state. -TEST(TestDecodeDaikin, SyntheticExample) { - IRDaikinESP irdaikin(0); - IRsendTest irsend(4); - IRrecv irrecv(4); - irsend.begin(); - - uint8_t expectedState[kDaikinStateLength] = { - 0x11, 0xDA, 0x27, 0x00, 0x42, 0x3A, 0x05, 0x93, 0x11, - 0xDA, 0x27, 0x00, 0x00, 0x3F, 0x3A, 0x00, 0xA0, 0x00, - 0x0A, 0x25, 0x17, 0x01, 0x00, 0xC0, 0x00, 0x00, 0x32}; - - irsend.reset(); - irsend.sendDaikin(expectedState); - irsend.makeDecodeResult(); - EXPECT_TRUE(irrecv.decode(&irsend.capture)); - ASSERT_EQ(DAIKIN, irsend.capture.decode_type); - ASSERT_EQ(kDaikinBits, irsend.capture.bits); - EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits); -} diff --git a/lib/IRremoteESP8266-2.5.2.03/test/ir_Whirlpool_test.cpp b/lib/IRremoteESP8266-2.5.2.03/test/ir_Whirlpool_test.cpp deleted file mode 100644 index c30cb21d335f..000000000000 --- a/lib/IRremoteESP8266-2.5.2.03/test/ir_Whirlpool_test.cpp +++ /dev/null @@ -1,118 +0,0 @@ -// Copyright 2018 David Conran - -#include "IRrecv.h" -#include "IRrecv_test.h" -#include "IRsend.h" -#include "IRsend_test.h" -#include "gtest/gtest.h" - -// Tests for sendWhirlpoolAC(). - -// Test sending typical data only. -TEST(TestSendWhirlpoolAC, SendDataOnly) { - IRsendTest irsend(0); - irsend.begin(); - uint8_t data[kWhirlpoolAcStateLength] = { - 0x83, 0x06, 0x10, 0x71, 0x00, 0x00, 0x91, 0x1F, 0x00, 0x00, 0x00, - 0x00, 0x00, 0xEF, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x02}; - - irsend.sendWhirlpoolAC(data); - EXPECT_EQ( - "m8950s4484" - "m597s1649m597s1649m597s533m597s533m597s533m597s533m597s533m597s1649" - "m597s533m597s1649m597s1649m597s533m597s533m597s533m597s533m597s533" - "m597s533m597s533m597s533m597s533m597s1649m597s533m597s533m597s533" - "m597s1649m597s533m597s533m597s533m597s1649m597s1649m597s1649m597s533" - "m597s533m597s533m597s533m597s533m597s533m597s533m597s533m597s533" - "m597s533m597s533m597s533m597s533m597s533m597s533m597s533m597s533" - "m597s7920" - "m597s1649m597s533m597s533m597s533m597s1649m597s533m597s533m597s1649" - "m597s1649m597s1649m597s1649m597s1649m597s1649m597s533m597s533m597s533" - "m597s533m597s533m597s533m597s533m597s533m597s533m597s533m597s533" - "m597s533m597s533m597s533m597s533m597s533m597s533m597s533m597s533" - "m597s533m597s533m597s533m597s533m597s533m597s533m597s533m597s533" - "m597s533m597s533m597s533m597s533m597s533m597s533m597s533m597s533" - "m597s533m597s533m597s533m597s533m597s533m597s533m597s533m597s533" - "m597s1649m597s1649m597s1649m597s1649m597s533m597s1649m597s1649m597s1649" - "m597s7920" - "m597s533m597s533m597s533m597s533m597s533m597s533m597s533m597s533" - "m597s533m597s1649m597s533m597s533m597s533m597s533m597s533m597s533" - "m597s533m597s533m597s533m597s533m597s533m597s533m597s533m597s533" - "m597s533m597s533m597s533m597s533m597s533m597s533m597s533m597s533" - "m597s533m597s533m597s533m597s533m597s533m597s533m597s533m597s533" - "m597s533m597s533m597s533m597s533m597s533m597s533m597s533m597s533" - "m597s533m597s1649m597s533m597s533m597s533m597s533m597s533m597s533" - "m597s100000", - irsend.outputStr()); -} - -// Tests for decodeWhirlpoolAC(). -// Decode normal WhirlpoolAC messages. -TEST(TestDecodeWhirlpoolAC, SyntheticDecode) { - IRsendTest irsend(0); - IRrecv irrecv(0); - irsend.begin(); - - // Synthesised Normal WhirlpoolAC message. - irsend.reset(); - uint8_t expectedState[kWhirlpoolAcStateLength] = { - 0x83, 0x06, 0x10, 0x71, 0x00, 0x00, 0x91, 0x1F, 0x00, 0x00, 0x00, - 0x00, 0x00, 0xEF, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x02}; - irsend.sendWhirlpoolAC(expectedState); - irsend.makeDecodeResult(); - EXPECT_TRUE(irrecv.decode(&irsend.capture)); - EXPECT_EQ(WHIRLPOOL_AC, irsend.capture.decode_type); - EXPECT_EQ(kWhirlpoolAcBits, irsend.capture.bits); - EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits); -} - -// Decode a recorded example -TEST(TestDecodeWhirlpoolAC, RealExampleDecode) { - IRsendTest irsend(0); - IRrecv irrecv(0); - irsend.begin(); - - // Real WhirlpoolAC message. - // Ref: https://github.com/markszabo/IRremoteESP8266/issues/509 - uint16_t rawData[343] = { - 8950, 4484, 598, 1642, 598, 1646, 594, 534, 594, 538, 602, 532, - 598, 540, 600, 542, 598, 1650, 600, 522, 598, 1644, 596, 1650, - 600, 532, 598, 538, 602, 536, 594, 548, 592, 538, 602, 518, - 600, 524, 596, 532, 598, 532, 598, 1654, 596, 544, 596, 544, - 596, 536, 594, 1644, 596, 528, 600, 528, 592, 538, 602, 1648, - 602, 1654, 596, 1664, 598, 534, 594, 526, 594, 530, 598, 528, - 602, 530, 600, 534, 596, 542, 598, 542, 598, 534, 596, 526, - 594, 530, 600, 528, 602, 530, 600, 534, 596, 542, 598, 544, - 596, 518, 602, 7916, 598, 1642, 598, 528, 600, 528, 602, 530, - 600, 1652, 598, 542, 598, 544, 596, 1654, 596, 1644, 596, 1648, - 602, 1644, 596, 1654, 596, 1656, 604, 536, 594, 548, 602, 528, - 600, 520, 600, 524, 596, 532, 598, 532, 596, 538, 602, 536, - 594, 546, 594, 538, 602, 518, 600, 524, 596, 532, 598, 532, - 598, 536, 594, 544, 596, 544, 596, 536, 594, 526, 592, 530, - 600, 528, 600, 530, 602, 532, 596, 542, 598, 542, 598, 534, - 596, 524, 596, 528, 600, 526, 592, 538, 592, 542, 598, 540, - 600, 540, 600, 530, 598, 522, 598, 526, 594, 534, 596, 534, - 594, 540, 602, 536, 592, 548, 592, 538, 600, 1636, 594, 1648, - 602, 1642, 598, 1652, 598, 538, 602, 1680, 570, 1662, 598, 1634, - 596, 7924, 600, 520, 598, 526, 592, 534, 596, 534, 596, 540, - 600, 536, 604, 538, 602, 530, 600, 520, 598, 1640, 600, 528, - 600, 530, 600, 534, 594, 544, 596, 544, 596, 534, 596, 526, - 594, 528, 600, 526, 594, 536, 592, 542, 598, 538, 602, 538, - 602, 528, 600, 520, 600, 524, 596, 530, 600, 532, 598, 534, - 596, 542, 598, 542, 598, 532, 598, 524, 596, 528, 602, 526, - 594, 536, 594, 540, 600, 536, 594, 548, 592, 538, 602, 518, - 602, 522, 596, 530, 600, 530, 600, 534, 596, 542, 598, 544, - 596, 534, 596, 524, 594, 1644, 596, 532, 596, 534, 596, 538, - 602, 536, 594, 546, 594, 520, 600}; - uint8_t expectedState[kWhirlpoolAcStateLength] = { - 0x83, 0x06, 0x10, 0x71, 0x00, 0x00, 0x91, 0x1F, 0x00, 0x00, 0x00, - 0x00, 0x00, 0xEF, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x02}; - - irsend.reset(); - irsend.sendRaw(rawData, 343, 38000); - irsend.makeDecodeResult(); - EXPECT_TRUE(irrecv.decode(&irsend.capture)); - EXPECT_EQ(WHIRLPOOL_AC, irsend.capture.decode_type); - EXPECT_EQ(kWhirlpoolAcBits, irsend.capture.bits); - EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits); -} diff --git a/lib/IRremoteESP8266-2.5.2.03/.github/CONTRIBUTING.md b/lib/IRremoteESP8266-2.6.0/.github/CONTRIBUTING.md similarity index 100% rename from lib/IRremoteESP8266-2.5.2.03/.github/CONTRIBUTING.md rename to lib/IRremoteESP8266-2.6.0/.github/CONTRIBUTING.md diff --git a/lib/IRremoteESP8266-2.5.2.03/.github/Contributors.md b/lib/IRremoteESP8266-2.6.0/.github/Contributors.md similarity index 87% rename from lib/IRremoteESP8266-2.5.2.03/.github/Contributors.md rename to lib/IRremoteESP8266-2.6.0/.github/Contributors.md index 5f75ea3b49bd..af9734d69ab7 100644 --- a/lib/IRremoteESP8266-2.5.2.03/.github/Contributors.md +++ b/lib/IRremoteESP8266-2.6.0/.github/Contributors.md @@ -12,7 +12,9 @@ - [Jorge Cisneros](https://github.com/jorgecis/) - [Denes Varga](https://github.com/denxhun/) - [Brett T. Warden](https://github.com/bwarden/) +- [Fabien Valthier](https://github.com/hcoohb) +- [Ajay Pala](https://github.com/ajaypala/) -All contributors can be found on the [contributors site](https://github.com/markszabo/IRremoteESP8266/graphs/contributors). +All contributors can be found on the [contributors site](https://github.com/markszabo/IRremoteESP8266/graphs/contributors). ### Contributors of the [original project](https://github.com/z3t0/Arduino-IRremote) can be found on the [original project's contributors page](https://github.com/z3t0/Arduino-IRremote/blob/master/Contributors.md) diff --git a/lib/IRremoteESP8266-2.5.2.03/.github/issue_template.md b/lib/IRremoteESP8266-2.6.0/.github/issue_template.md similarity index 100% rename from lib/IRremoteESP8266-2.5.2.03/.github/issue_template.md rename to lib/IRremoteESP8266-2.6.0/.github/issue_template.md diff --git a/lib/IRremoteESP8266-2.5.2.03/.gitignore b/lib/IRremoteESP8266-2.6.0/.gitignore similarity index 100% rename from lib/IRremoteESP8266-2.5.2.03/.gitignore rename to lib/IRremoteESP8266-2.6.0/.gitignore diff --git a/lib/IRremoteESP8266-2.5.2.03/.gitmodules b/lib/IRremoteESP8266-2.6.0/.gitmodules similarity index 100% rename from lib/IRremoteESP8266-2.5.2.03/.gitmodules rename to lib/IRremoteESP8266-2.6.0/.gitmodules diff --git a/lib/IRremoteESP8266-2.5.2.03/.style.yapf b/lib/IRremoteESP8266-2.6.0/.style.yapf similarity index 100% rename from lib/IRremoteESP8266-2.5.2.03/.style.yapf rename to lib/IRremoteESP8266-2.6.0/.style.yapf diff --git a/lib/IRremoteESP8266-2.5.2.03/.travis.yml b/lib/IRremoteESP8266-2.6.0/.travis.yml similarity index 81% rename from lib/IRremoteESP8266-2.5.2.03/.travis.yml rename to lib/IRremoteESP8266-2.6.0/.travis.yml index 4331425e9ca4..ae2d9fe3c76c 100644 --- a/lib/IRremoteESP8266-2.5.2.03/.travis.yml +++ b/lib/IRremoteESP8266-2.6.0/.travis.yml @@ -1,20 +1,21 @@ language: c env: - - BD=esp8266:esp8266:nodemcuv2:CpuFrequency=80,FlashSize=4M3M - - BD=esp8266:esp8266:d1_mini:CpuFrequency=80,FlashSize=4M3M + - BD=esp8266:esp8266:nodemcuv2:xtal=80,eesz=4M3M,ip=lm2f,exception=disabled + - BD=esp8266:esp8266:d1_mini:xtal=80,eesz=4M3M,ip=lm2f,exception=disabled before_install: - "/sbin/start-stop-daemon --start --quiet --pidfile /tmp/custom_xvfb_1.pid --make-pidfile --background --exec /usr/bin/Xvfb -- :1 -ac -screen 0 1280x1024x16" - sleep 3 - export DISPLAY=:1.0 - - wget http://downloads.arduino.cc/arduino-1.8.2-linux64.tar.xz - - tar xf arduino-1.8.2-linux64.tar.xz - - sudo mv arduino-1.8.2 /usr/local/share/arduino + - wget http://downloads.arduino.cc/arduino-1.8.8-linux64.tar.xz + - tar xf arduino-1.8.8-linux64.tar.xz + - sudo mv arduino-1.8.8 /usr/local/share/arduino - sudo ln -s /usr/local/share/arduino/arduino /usr/local/bin/arduino - wget https://raw.githubusercontent.com/google/styleguide/gh-pages/cpplint/cpplint.py install: - ln -s $PWD /usr/local/share/arduino/libraries/ - git clone https://github.com/tzapu/WiFiManager.git /usr/local/share/arduino/libraries/WiFiManager - git clone https://github.com/knolleary/pubsubclient.git /usr/local/share/arduino/libraries/PubSubClient + - git clone https://github.com/bblanchon/ArduinoJson.git --branch 5.x /usr/local/share/arduino/libraries/ArduinoJson - arduino --pref "boardsmanager.additional.urls=http://arduino.esp8266.com/stable/package_esp8266com_index.json" --save-prefs - arduino --install-boards esp8266:esp8266 - arduino --board $BD --save-prefs @@ -40,6 +41,10 @@ script: - arduino --verify --board $BD $PWD/examples/TurnOnArgoAC/TurnOnArgoAC.ino - arduino --verify --board $BD $PWD/examples/IRMQTTServer/IRMQTTServer.ino - arduino --verify --board $BD $PWD/examples/TurnOnToshibaAC/TurnOnToshibaAC.ino + - arduino --verify --board $BD $PWD/examples/ControlSamsungAC/ControlSamsungAC.ino + - arduino --verify --board $BD $PWD/examples/TurnOnPanasonicAC/TurnOnPanasonicAC.ino + - arduino --verify --board $BD $PWD/examples/TurnOnMitsubishiHeavyAc/TurnOnMitsubishiHeavyAc.ino + # Also check the tools programs compile. - (cd tools; make all) # Check for lint issues. diff --git a/lib/IRremoteESP8266-2.5.2.03/CPPLINT.cfg b/lib/IRremoteESP8266-2.6.0/CPPLINT.cfg similarity index 100% rename from lib/IRremoteESP8266-2.5.2.03/CPPLINT.cfg rename to lib/IRremoteESP8266-2.6.0/CPPLINT.cfg diff --git a/lib/IRremoteESP8266-2.5.2.03/LICENSE.txt b/lib/IRremoteESP8266-2.6.0/LICENSE.txt similarity index 100% rename from lib/IRremoteESP8266-2.5.2.03/LICENSE.txt rename to lib/IRremoteESP8266-2.6.0/LICENSE.txt diff --git a/lib/IRremoteESP8266-2.5.2.03/README.md b/lib/IRremoteESP8266-2.6.0/README.md similarity index 95% rename from lib/IRremoteESP8266-2.5.2.03/README.md rename to lib/IRremoteESP8266-2.6.0/README.md index bb9d5a9d8137..1eaaa21b447e 100644 --- a/lib/IRremoteESP8266-2.5.2.03/README.md +++ b/lib/IRremoteESP8266-2.6.0/README.md @@ -1,14 +1,15 @@ # IRremote ESP8266 Library [![Build Status](https://travis-ci.org/markszabo/IRremoteESP8266.svg?branch=master)](https://travis-ci.org/markszabo/IRremoteESP8266) +[![arduino-library-badge](https://www.ardu-badge.com/badge/IRremoteESP8266.svg?)](https://www.ardu-badge.com/IRremoteESP8266) [![Average time to resolve an issue](http://isitmaintained.com/badge/resolution/markszabo/IRremoteESP8266.svg)](http://isitmaintained.com/project/markszabo/IRremoteESP8266 "Average time to resolve an issue") [![Percentage of issues still open](http://isitmaintained.com/badge/open/markszabo/IRremoteESP8266.svg)](http://isitmaintained.com/project/markszabo/IRremoteESP8266 "Percentage of issues still open") [![GitLicense](https://gitlicense.com/badge/markszabo/IRremoteESP8266)](https://gitlicense.com/license/markszabo/IRremoteESP8266) This library enables you to **send _and_ receive** infra-red signals on an [ESP8266 using the Arduino framework](https://github.com/esp8266/Arduino) using common 940nm IR LEDs and common IR receiver modules. e.g. TSOP{17,22,24,36,38,44,48}* etc. -## v2.5.2 Now Available -Version 2.5.2 of the library is now [available](https://github.com/markszabo/IRremoteESP8266/releases/latest). You can view the [Release Notes](ReleaseNotes.md) for all the significant changes. +## v2.6.0 Now Available +Version 2.6.0 of the library is now [available](https://github.com/markszabo/IRremoteESP8266/releases/latest). You can view the [Release Notes](ReleaseNotes.md) for all the significant changes. #### Upgrading from pre-v2.0 Usage of the library has been slightly changed in v2.0. You will need to change your usage to work with v2.0 and beyond. You can read more about the changes required on our [Upgrade to v2.0](https://github.com/markszabo/IRremoteESP8266/wiki/Upgrading-to-v2.0) page. diff --git a/lib/IRremoteESP8266-2.5.2.03/ReleaseNotes.md b/lib/IRremoteESP8266-2.6.0/ReleaseNotes.md similarity index 72% rename from lib/IRremoteESP8266-2.5.2.03/ReleaseNotes.md rename to lib/IRremoteESP8266-2.6.0/ReleaseNotes.md index 56e84dd89a96..98416a12af5d 100644 --- a/lib/IRremoteESP8266-2.5.2.03/ReleaseNotes.md +++ b/lib/IRremoteESP8266-2.6.0/ReleaseNotes.md @@ -1,5 +1,107 @@ # Release Notes +## _v2.6.0 (20190430)_ + +**[Bug Fixes]** +- Fixed problem where LG protocol used wrong duty cycle for repeat. (#687) +- Fix checksum calculation for Daikin protocols. (#678) +- Fix the byte array version of sendGree() (#684, #685) +- Fix artificial vs. real state creation on HaierAC. (#668, #671) +- Fix issues caused by having `MQTT_ENABLE` set to false. (#677) +- Fix compile problem when DEBUG is defined. (#673, #674) +- Fix Minor bug with MQTT_ENABLE False condition (#654) + +**[Features]** +- Experimental support for DAIKIN216 (ARC433B69) (#690) +- Experimental support for Mitsubishi Heavy Industries A/Cs. (#660, #665, #667) +- Support more features of TCL A/C (#656) +- Add LEGO(TM) Power Functions IR protocol. (#655) +- Add Panasonic AC RKR model & Example (#649) +- DAIKIN/IRDaikinESP overhaul and add Comfort mode support. (#678) + **WARNING**: Previous `sendDaikin()` calls may not work. + Please recapture codes or use `kDaikinStateLengthShort` for + `nbytes` in those calls. +- IRMQTTServer: Move MQTT server and other parameters to WifiManager. (#680) + **WARNING**: Previous users may need to fully wipe/reset the + SPIFFS/WifiManager settings by visiting + `http:///reset` prior to or + after update. +- Add Wifi filtering options to IRMQTTServer. (#679) +- Add advanced aircon/climate functionality to IRMQTTServer (#677) +- Initial prototype of a common interface for all A/Cs. (#664) +- Improve MQTT topic usage for feedback messages. (#663) +- Add multiple independent GPIO sending support via MQTT. (#661) + +**[Misc]** +- Adjust kGreeHdrSpace to 4500 (#684, #686) +- Add Home Assistant mqtt climate instructions. (#682) +- Implement htmlEscape() to prevent XSS etc. (#681) +- Add F() Macros (#670) +- Update Daikin2's Cool mode min temp to 18C (#658) +- Change per byte bit-order in Electra protocol. (#648) +- Improve Daikin2 power on/off. (#647) + + +## _v2.5.6 (20190316)_ + +**[Bug Fixes]** +- Fix Coolix A/C Class to handle special states better. (#633, #624) + +**[Features]** +- Fix case style for recent A/C protocols. (#631) +- Update `IRsend::send()` to include all simple protocols. (#629, #628) +- Experimental basic support for 112 bit TCL AC messages (#627, #619) +- Add support for TECO AC (#622) +- Experimental support for Samsung 36 bit protocol (#625, #621) + +**[Misc]** +- Set Coolix to default to 1 repeat. (#637, #636, #624, #439) +- Set Daikin2 modulation to 36.7kHz. (#635) +- Refactor IRVestelAC class to use portable code. (#617) +- Adjust Daikin2 timings and tolerance. (#616, #582) + + +## _v2.5.5 (20190207)_ + +**[Bug Fixes]** +- Fix decoding of Samsung A/C Extended messages. (#610) +- Fix IRMQTTServer example to work with GPIO0 as IR_RX (#608) +- Fix incorrect #define usage. (#597, #596) + +**[Features]** +- Add deep decoding/construction of Daikin2 messages (#600) +- Added Old Vestel A/C support (56 Bits) with full functions. (#607) + +**[Misc]** +- Add ControlSamsungAC example code. (#599) +- Add how to send a state/air-con to IRsendDemo.ino (#594) + + +## _v2.5.4 (20190102)_ + +**[Features]** +- Experimental basic support for 39 Byte Daikin A/C (#583) +- Handle send() repeats in A/C classes. Improve Coolix support. (#580) +- Add optional RX pin pullup and dump raw messages in IRMQTTServer.ino (#589) + +**[Misc]** +- Make auto_analyse_raw_data.py work with Python3 (#581) +- Update CI/travis config due to esp8266 core 2.5.0 changes (#591) + + +## _v2.5.3 (20181123)_ + +**[Features]** +- Add deep support for the Hitachi 28-Byte A/C Protocol (#563) +- Deep decoding for Whirlpool A/C (#572) +- Improve security options for IRMQTTServer example. (#575) +- Require a changed firmware password before upload. (#576) + +**[Misc]** +- Add missing '}' in output of Auto analyse. (#562) +- Make A/C example code a bit more simple. (#571) + + ## _v2.5.2 (20181021)_ **[Bug Fixes]** diff --git a/lib/IRremoteESP8266-2.6.0/examples/ControlSamsungAC/ControlSamsungAC.ino b/lib/IRremoteESP8266-2.6.0/examples/ControlSamsungAC/ControlSamsungAC.ino new file mode 100644 index 000000000000..df910fe870f2 --- /dev/null +++ b/lib/IRremoteESP8266-2.6.0/examples/ControlSamsungAC/ControlSamsungAC.ino @@ -0,0 +1,99 @@ +/* Copyright 2019 David Conran +* +* An IR LED circuit *MUST* be connected to the ESP8266 on a pin +* as specified by kIrLed below. +* +* TL;DR: The IR LED needs to be driven by a transistor for a good result. +* +* Suggested circuit: +* https://github.com/markszabo/IRremoteESP8266/wiki#ir-sending +* +* Common mistakes & tips: +* * Don't just connect the IR LED directly to the pin, it won't +* have enough current to drive the IR LED effectively. +* * Make sure you have the IR LED polarity correct. +* See: https://learn.sparkfun.com/tutorials/polarity/diode-and-led-polarity +* * Typical digital camera/phones can be used to see if the IR LED is flashed. +* Replace the IR LED with a normal LED if you don't have a digital camera +* when debugging. +* * Avoid using the following pins unless you really know what you are doing: +* * Pin 0/D3: Can interfere with the boot/program mode & support circuits. +* * Pin 1/TX/TXD0: Any serial transmissions from the ESP8266 will interfere. +* * Pin 3/RX/RXD0: Any serial transmissions to the ESP8266 will interfere. +* * ESP-01 modules are tricky. We suggest you use a module with more GPIOs +* for your first time. e.g. ESP-12 etc. +*/ +#ifndef UNIT_TEST +#include +#endif +#include +#include +#include + +const uint16_t kIrLed = 4; // ESP8266 GPIO pin to use. Recommended: 4 (D2). +IRSamsungAc ac(kIrLed); // Set the GPIO used for sending messages. + +void printState() { + // Display the settings. + Serial.println("Samsung A/C remote is in the following state:"); + Serial.printf(" %s\n", ac.toString().c_str()); +} + +void setup() { + ac.begin(); + Serial.begin(115200); + delay(200); + + // Set up what we want to send. See ir_Samsung.cpp for all the options. + Serial.println("Default state of the remote."); + printState(); + Serial.println("Setting initial state for A/C."); + ac.off(); + ac.setFan(kSamsungAcFanLow); + ac.setMode(kSamsungAcCool); + ac.setTemp(25); + ac.setSwing(false); + printState(); +} + +void loop() { + // Turn the A/C unit on and set to cooling mode. + // Power changes require we send an extended message. + Serial.println("Sending an extended IR command to A/C ..."); + ac.on(); + ac.setMode(kSamsungAcCool); + ac.sendExtended(); + printState(); + delay(15000); // wait 15 seconds + + // Increase the fan speed. + Serial.println("Sending a normal IR command to A/C ..."); + ac.setFan(kSamsungAcFanHigh); + ac.send(); + printState(); + delay(15000); + + // Change to swing the fan. + Serial.println("Sending a normal IR command to A/C ..."); + ac.setSwing(true); + ac.send(); + printState(); + delay(15000); + + // Change to Fan mode, lower the speed, and stop the swing. + Serial.println("Sending a normal IR command to A/C ..."); + ac.setSwing(false); + ac.setMode(kSamsungAcFan); + ac.setFan(kSamsungAcFanLow); + ac.send(); + printState(); + delay(15000); + + // Turn the A/C unit off. + // Power changes require we send an extended message. + Serial.println("Sending an extended IR command to A/C ..."); + ac.off(); + ac.sendExtended(); + printState(); + delay(15000); // wait 15 seconds +} diff --git a/lib/IRremoteESP8266-2.5.2.03/examples/IRGCTCPServer/platformio.ini b/lib/IRremoteESP8266-2.6.0/examples/ControlSamsungAC/platformio.ini similarity index 83% rename from lib/IRremoteESP8266-2.5.2.03/examples/IRGCTCPServer/platformio.ini rename to lib/IRremoteESP8266-2.6.0/examples/ControlSamsungAC/platformio.ini index eeb8d1f2eb93..ec84f22f3b05 100644 --- a/lib/IRremoteESP8266-2.5.2.03/examples/IRGCTCPServer/platformio.ini +++ b/lib/IRremoteESP8266-2.6.0/examples/ControlSamsungAC/platformio.ini @@ -6,11 +6,13 @@ src_dir=. build_flags = lib_deps_builtin = lib_deps_external = +lib_ldf_mode = chain+ [env:nodemcuv2] platform = espressif8266 framework = arduino board = nodemcuv2 +lib_ldf_mode = ${common.lib_ldf_mode} build_flags = ${common.build_flags} lib_deps = ${common.lib_deps_builtin} diff --git a/lib/IRremoteESP8266-2.5.2.03/examples/IRGCSendDemo/IRGCSendDemo.ino b/lib/IRremoteESP8266-2.6.0/examples/IRGCSendDemo/IRGCSendDemo.ino similarity index 100% rename from lib/IRremoteESP8266-2.5.2.03/examples/IRGCSendDemo/IRGCSendDemo.ino rename to lib/IRremoteESP8266-2.6.0/examples/IRGCSendDemo/IRGCSendDemo.ino diff --git a/lib/IRremoteESP8266-2.5.2.03/examples/IRrecvDemo/platformio.ini b/lib/IRremoteESP8266-2.6.0/examples/IRGCSendDemo/platformio.ini similarity index 83% rename from lib/IRremoteESP8266-2.5.2.03/examples/IRrecvDemo/platformio.ini rename to lib/IRremoteESP8266-2.6.0/examples/IRGCSendDemo/platformio.ini index eeb8d1f2eb93..ec84f22f3b05 100644 --- a/lib/IRremoteESP8266-2.5.2.03/examples/IRrecvDemo/platformio.ini +++ b/lib/IRremoteESP8266-2.6.0/examples/IRGCSendDemo/platformio.ini @@ -6,11 +6,13 @@ src_dir=. build_flags = lib_deps_builtin = lib_deps_external = +lib_ldf_mode = chain+ [env:nodemcuv2] platform = espressif8266 framework = arduino board = nodemcuv2 +lib_ldf_mode = ${common.lib_ldf_mode} build_flags = ${common.build_flags} lib_deps = ${common.lib_deps_builtin} diff --git a/lib/IRremoteESP8266-2.5.2.03/examples/IRGCTCPServer/IRGCTCPServer.ino b/lib/IRremoteESP8266-2.6.0/examples/IRGCTCPServer/IRGCTCPServer.ino similarity index 100% rename from lib/IRremoteESP8266-2.5.2.03/examples/IRGCTCPServer/IRGCTCPServer.ino rename to lib/IRremoteESP8266-2.6.0/examples/IRGCTCPServer/IRGCTCPServer.ino diff --git a/lib/IRremoteESP8266-2.5.2.03/examples/IRServer/platformio.ini b/lib/IRremoteESP8266-2.6.0/examples/IRGCTCPServer/platformio.ini similarity index 83% rename from lib/IRremoteESP8266-2.5.2.03/examples/IRServer/platformio.ini rename to lib/IRremoteESP8266-2.6.0/examples/IRGCTCPServer/platformio.ini index eeb8d1f2eb93..ec84f22f3b05 100644 --- a/lib/IRremoteESP8266-2.5.2.03/examples/IRServer/platformio.ini +++ b/lib/IRremoteESP8266-2.6.0/examples/IRGCTCPServer/platformio.ini @@ -6,11 +6,13 @@ src_dir=. build_flags = lib_deps_builtin = lib_deps_external = +lib_ldf_mode = chain+ [env:nodemcuv2] platform = espressif8266 framework = arduino board = nodemcuv2 +lib_ldf_mode = ${common.lib_ldf_mode} build_flags = ${common.build_flags} lib_deps = ${common.lib_deps_builtin} diff --git a/lib/IRremoteESP8266-2.6.0/examples/IRMQTTServer/IRMQTTServer.h b/lib/IRremoteESP8266-2.6.0/examples/IRMQTTServer/IRMQTTServer.h new file mode 100644 index 000000000000..9494dbe2bbf7 --- /dev/null +++ b/lib/IRremoteESP8266-2.6.0/examples/IRMQTTServer/IRMQTTServer.h @@ -0,0 +1,258 @@ +/* + * Send & receive arbitrary IR codes via a web server or MQTT. + * Copyright David Conran 2016, 2017, 2018, 2019 + */ +#ifndef EXAMPLES_IRMQTTSERVER_IRMQTTSERVER_H_ +#define EXAMPLES_IRMQTTSERVER_IRMQTTSERVER_H_ + +#include +#include +#include +#include +#include +#include + +// ---------------- Start of User Configuration Section ------------------------ + +#ifndef MQTT_ENABLE +#define MQTT_ENABLE true // Whether or not MQTT is used at all. +#endif // MQTT_ENABLE + +// ---------------------- Board Related Settings ------------------------------- +// NOTE: Make sure you set your Serial Monitor to the same speed. +#define BAUD_RATE 115200 // Serial port Baud rate. + +// GPIO the IR LED is connected to/controlled by. GPIO 4 = D2. +#define IR_LED 4 // <=- CHANGE_ME (optional) +// define IR_LED 3 // For an ESP-01 we suggest you use RX/GPIO3/Pin 7. + +// GPIO the IR RX module is connected to/controlled by. e.g. GPIO 14 = D5. +// Comment this out to disable receiving/decoding IR messages entirely. +#define IR_RX 14 // <=- CHANGE_ME (optional) +#define IR_RX_PULLUP false + +// --------------------- Network Related Settings ------------------------------ +const uint16_t kHttpPort = 80; // The TCP port the HTTP server is listening on. +// Change to 'true'/'false' if you do/don't want these features or functions. +#define USE_STATIC_IP false // Change to 'true' if you don't want to use DHCP. +// We obtain our network config via DHCP by default but allow an easy way to +// use a static IP config. +#if USE_STATIC_IP +const IPAddress kIPAddress = IPAddress(10, 0, 1, 78); +const IPAddress kGateway = IPAddress(10, 0, 1, 1); +const IPAddress kSubnetMask = IPAddress(255, 255, 255, 0); +#endif // USE_STATIC_IP + +// See: https://github.com/tzapu/WiFiManager#filter-networks for these settings. +#define HIDE_DUPLIATE_NETWORKS false // Should WifiManager hide duplicate SSIDs +// #define MIN_SIGNAL_STRENGTH 20 // Minimum WiFi signal stength (percentage) + // before we will connect. + // The unset default is 8%. + // (Uncomment to enable) + +// ----------------------- HTTP Related Settings ------------------------------- +#define FIRMWARE_OTA true // Allow remote update of the firmware via http. + // Less secure if enabled. + // Note: Firmware OTA is also disabled until + // a password is set. +#define HTML_PASSWORD_ENABLE false // Protect access to the HTML interface. + // Note: OTA update is always passworded. +// If you do not set a password, Firmware OTA updates will be blocked. + +// ----------------------- MQTT Related Settings ------------------------------- +#if MQTT_ENABLE +const uint32_t kMqttReconnectTime = 5000; // Delay(ms) between reconnect tries. + +#define MQTT_ACK "sent" // Sub-topic we send back acknowledgements on. +#define MQTT_SEND "send" // Sub-topic we get new commands from. +#define MQTT_RECV "received" // Topic we send received IRs to. +#define MQTT_LOG "log" // Topic we send log messages to. +#define MQTT_LWT "status" // Topic for the Last Will & Testament. +#define MQTT_CLIMATE "ac" // Sub-topic for the climate topics. +#define MQTT_CLIMATE_CMND "cmnd" // Sub-topic for the climate command topics. +#define MQTT_CLIMATE_STAT "stat" // Sub-topic for the climate stat topics. +#define MQTTbroadcastInterval 10 * 60 // Seconds between rebroadcasts + +#define QOS 1 // MQTT broker should queue up any unreceived messages for us +// #define QOS 0 // MQTT broker WON'T queue up messages for us. Fire & Forget. +#endif // MQTT_ENABLE + +// ------------------------ IR Capture Settings -------------------------------- +// Let's use a larger than normal buffer so we can handle AirCon remote codes. +const uint16_t kCaptureBufferSize = 1024; +#if DECODE_AC +// Some A/C units have gaps in their protocols of ~40ms. e.g. Kelvinator +// A value this large may swallow repeats of some protocols +const uint8_t kCaptureTimeout = 50; // Milliseconds +#else // DECODE_AC +// Suits most messages, while not swallowing many repeats. +const uint8_t kCaptureTimeout = 15; // Milliseconds +#endif // DECODE_AC +// Ignore unknown messages with <10 pulses (see also REPORT_UNKNOWNS) +const uint16_t kMinUnknownSize = 2 * 10; +#define REPORT_UNKNOWNS false // Report inbound IR messages that we don't know. +#define REPORT_RAW_UNKNOWNS false // Report the whole buffer, recommended: + // MQTT_MAX_PACKET_SIZE of 1024 or more + +// ------------------------ Advanced Usage Only -------------------------------- +// Change if you need multiple independent send gpio/topics. +const uint8_t gpioTable[] = { + IR_LED, // Default GPIO. e.g. ir_server/send or ir_server/send_0 + // Uncomment the following as needed. + // NOTE: Remember to disable DEBUG if you are using one of the serial pins. + // 5, // GPIO 5 / D1 e.g. ir_server/send_1 + // 14, // GPIO 14 / D5 e.g. ir_server/send_2 + // 16, // GPIO 16 / D0 e.g. ir_server/send_3 +}; + +#define KEY_PROTOCOL "protocol" +#define KEY_MODEL "model" +#define KEY_POWER "power" +#define KEY_MODE "mode" +#define KEY_TEMP "temp" +#define KEY_FANSPEED "fanspeed" +#define KEY_SWINGV "swingv" +#define KEY_SWINGH "swingh" +#define KEY_QUIET "quiet" +#define KEY_TURBO "turbo" +#define KEY_LIGHT "light" +#define KEY_BEEP "beep" +#define KEY_ECONO "econo" +#define KEY_SLEEP "sleep" +#define KEY_CLOCK "clock" +#define KEY_FILTER "filter" +#define KEY_CLEAN "clean" +#define KEY_CELSIUS "use_celsius" + +// HTML arguments we will parse for IR code information. +#define KEY_TYPE "type" // KEY_PROTOCOL is also checked too. +#define KEY_CODE "code" +#define KEY_BITS "bits" +#define KEY_REPEAT "repeats" + +// Text for Last Will & Testament status messages. +const char* kLwtOnline = "Online"; +const char* kLwtOffline = "Offline"; + +const uint8_t kHostnameLength = 30; +const uint8_t kPortLength = 5; // Largest value of uint16_t is "65535". +const uint8_t kUsernameLength = 15; +const uint8_t kPasswordLength = 20; + +// -------------------------- Debug Settings ----------------------------------- +// Disable debug output if any of the IR pins are on the TX (D1) pin. +// Note: This is a crude method to catch the common use cases. +// See `isSerialGpioUsedByIr()` for the better method. +#if (IR_LED != 1 && IR_RX != 1) +#ifndef DEBUG +#define DEBUG true // Change to 'false' to disable all serial output. +#endif // DEBUG +#else // (IR_LED != 1 && IR_RX != 1) +#undef DEBUG +#define DEBUG false +#endif + +// ----------------- End of User Configuration Section ------------------------- + +// Constants +#define _MY_VERSION_ "v1.0.0" + +const uint8_t kSendTableSize = sizeof(gpioTable); +// JSON stuff +// Name of the json config file in SPIFFS. +const char* kConfigFile = "/config.json"; +const char* kMqttServerKey = "mqtt_server"; +const char* kMqttPortKey = "mqtt_port"; +const char* kMqttUserKey = "mqtt_user"; +const char* kMqttPassKey = "mqtt_pass"; +const char* kMqttPrefixKey = "mqtt_prefix"; +const char* kHostnameKey = "hostname"; +const char* kHttpUserKey = "http_user"; +const char* kHttpPassKey = "http_pass"; + +#if MQTT_ENABLE +const uint32_t kBroadcastPeriodMs = MQTTbroadcastInterval * 1000; // mSeconds. +const uint32_t kStatListenPeriodMs = 5 * 1000; // mSeconds + +void mqttCallback(char* topic, byte* payload, unsigned int length); +String listOfCommandTopics(void); +void handleSendMqttDiscovery(void); +void subscribing(const String topic_name); +void unsubscribing(const String topic_name); +void mqttLog(const String mesg); +bool reconnect(void); +void receivingMQTT(String const topic_name, String const callback_str); +void callback(char* topic, byte* payload, unsigned int length); +void sendMQTTDiscovery(const char *topic); +void doBroadcast(TimerMs *timer, const uint32_t interval, + const commonAcState_t state, const bool retain, + const bool force); +#endif // MQTT_ENABLE +bool isSerialGpioUsedByIr(void); +void debug(const char *str); +void saveWifiConfigCallback(void); +void saveWifiConfig(void); +void loadWifiConfigFile(void); +String msToHumanString(uint32_t const msecs); +String timeElapsed(uint32_t const msec); +String timeSince(uint32_t const start); +String listOfSendGpios(void); +bool hasUnsafeHTMLChars(String input); +String htmlMenu(void); +void handleRoot(void); +String addJsReloadUrl(const String url, const uint16_t timeout_s, + const bool notify); +void handleExamples(void); +String boolToString(const bool value); +String opmodeToString(const stdAc::opmode_t mode); +String fanspeedToString(const stdAc::fanspeed_t speed); +String swingvToString(const stdAc::swingv_t swingv); +String swinghToString(const stdAc::swingh_t swingh); +String htmlSelectBool(const String name, const bool def); +String htmlSelectProtocol(const String name, const decode_type_t def); +String htmlSelectModel(const String name, const int16_t def); +String htmlSelectMode(const String name, const stdAc::opmode_t def); +String htmlSelectFanspeed(const String name, const stdAc::fanspeed_t def); +String htmlSelectSwingv(const String name, const stdAc::swingv_t def); +String htmlSelectSwingh(const String name, const stdAc::swingh_t def); +void handleAirCon(void); +void handleAirConSet(void); +void handleAdmin(void); +void handleInfo(void); +void handleReset(void); +void handleReboot(void); +bool parseStringAndSendAirCon(IRsend *irsend, const uint16_t irType, + const String str); +uint16_t countValuesInStr(const String str, char sep); +uint16_t * newCodeArray(const uint16_t size); +#if SEND_GLOBALCACHE +bool parseStringAndSendGC(IRsend *irsend, const String str); +#endif // SEND_GLOBALCACHE +#if SEND_PRONTO +bool parseStringAndSendPronto(IRsend *irsend, const String str, + uint16_t repeats); +#endif // SEND_PRONTO +#if SEND_RAW +bool parseStringAndSendRaw(IRsend *irsend, const String str); +#endif // SEND_RAW +void handleIr(void); +void handleNotFound(void); +void setup_wifi(void); +void init_vars(void); +void setup(void); +void loop(void); +uint64_t getUInt64fromHex(char const *str); +bool sendIRCode(IRsend *irsend, int const ir_type, + uint64_t const code, char const * code_str, uint16_t bits, + uint16_t repeat); +bool sendInt(const String topic, const int32_t num, const bool retain); +bool sendBool(const String topic, const bool on, const bool retain); +bool sendString(const String topic, const String str, const bool retain); +bool sendFloat(const String topic, const float_t temp, const bool retain); +commonAcState_t updateClimate(commonAcState_t current, const String str, + const String prefix, const String payload); +bool cmpClimate(const commonAcState_t a, const commonAcState_t b); +bool sendClimate(const commonAcState_t prev, const commonAcState_t next, + const String topic_prefix, const bool retain, + const bool forceMQTT, const bool forceIR); +#endif // EXAMPLES_IRMQTTSERVER_IRMQTTSERVER_H_ diff --git a/lib/IRremoteESP8266-2.6.0/examples/IRMQTTServer/IRMQTTServer.ino b/lib/IRremoteESP8266-2.6.0/examples/IRMQTTServer/IRMQTTServer.ino new file mode 100644 index 000000000000..31e40432d840 --- /dev/null +++ b/lib/IRremoteESP8266-2.6.0/examples/IRMQTTServer/IRMQTTServer.ino @@ -0,0 +1,3034 @@ +/* + * Send & receive arbitrary IR codes via a web server or MQTT. + * Copyright David Conran 2016, 2017, 2018, 2019 + * + * Copyright: + * Code for this has been borrowed from lots of other OpenSource projects & + * resources. I'm *NOT* claiming complete Copyright ownership of all the code. + * Likewise, feel free to borrow from this as much as you want. + * + * NOTE: An IR LED circuit SHOULD be connected to ESP8266 GPIO4 (D2) if + * you want to send IR messages. + * A compatible IR RX modules SHOULD be connected to ESP8266 GPIO14 (D5) + * if you want to capture & decode IR nessages. + * See 'IR_LED' & 'IR_RX' in IRMQTTServer.h. + * + * WARN: This is *very* advanced & complicated example code. Not for beginners. + * You are strongly suggested to try & look at other example code first + * to understand how this library works. + * + * # Instructions + * + * ## Before First Boot (i.e. Compile time) + * - Disable MQTT if desired. (see '#define MQTT_ENABLE' in IRMQTTServer.h). + * + * - Site specific settings: + * o Search for 'CHANGE_ME' in IRMQTTServer.h for the things you probably + * need to change for your particular situation. + * o All user changable settings are in the file IRMQTTServer.h. + * + * - Arduino IDE: + * o Install the following libraries via Library Manager + * - ArduinoJson (https://arduinojson.org/) (Version >= 5.x and < 6) + * - PubSubClient (https://pubsubclient.knolleary.net/) + * - WiFiManager (https://github.com/tzapu/WiFiManager) (Version >= 0.14) + * o You MUST change to have the following (or larger) value: + * (with REPORT_RAW_UNKNOWNS 1024 or more is recommended) + * #define MQTT_MAX_PACKET_SIZE 768 + * - PlatformIO IDE: + * If you are using PlatformIO, this should already been done for you in + * the accompanying platformio.ini file. + * + * ## First Boot (Initial setup) + * The ESP8266 board will boot into the WiFiManager's AP mode. + * i.e. It will create a WiFi Access Point with a SSID like: "ESP123456" etc. + * Connect to that SSID. Then point your browser to http://192.168.4.1/ and + * configure the ESP8266 to connect to your desired WiFi network and associated + * required settings. It will remember these details on next boot if the device + * connects successfully. + * More information can be found here: + * https://github.com/tzapu/WiFiManager#how-it-works + * + * If you need to reset the WiFi and saved settings to go back to "First Boot", + * visit: http:///reset + * + * ## Normal Use (After initial setup) + * Enter 'http:///ir?type=7&code=E0E09966 + * http:///ir?type=4&code=0xf50&bits=12 + * http:///ir?code=C1A2E21D&repeats=8&type=19 + * http:///ir?type=31&code=40000,1,1,96,24,24,24,48,24,24,24,24,24,48,24,24,24,24,24,48,24,24,24,24,24,24,24,24,1058 + * http:///ir?type=18&code=190B8050000000E0190B8070000010f0 + * http:///ir?repeats=1&type=25&code=0000,006E,0022,0002,0155,00AA,0015,0040,0015,0040,0015,0015,0015,0015,0015,0015,0015,0015,0015,0015,0015,0040,0015,0040,0015,0015,0015,0040,0015,0015,0015,0015,0015,0015,0015,0040,0015,0015,0015,0015,0015,0040,0015,0040,0015,0015,0015,0015,0015,0015,0015,0015,0015,0015,0015,0040,0015,0015,0015,0015,0015,0040,0015,0040,0015,0040,0015,0040,0015,0040,0015,0640,0155,0055,0015,0E40 + * + * or + * + * Send a MQTT message to the topic 'ir_server/send' (or 'ir_server/send_0' etc) + * using the following format (Order is important): + * protocol_num,hexcode + * e.g. 7,E0E09966 + * which is: Samsung(7), Power On code, default bit size, + * default nr. of repeats. + * + * protocol_num,hexcode,bits + * e.g. 4,f50,12 + * which is: Sony(4), Power Off code, 12 bits & default nr. of repeats. + * + * protocol_num,hexcode,bits,repeats + * e.g. 19,C1A2E21D,0,8 + * which is: Sherwood(19), Vol Up, default bit size & repeated 8 times. + * + * 30,frequency,raw_string + * e.g. 30,38000,9000,4500,500,1500,500,750,500,750 + * which is: Raw (30) @ 38kHz with a raw code of + * "9000,4500,500,1500,500,750,500,750" + * + * 31,code_string + * e.g. 31,40000,1,1,96,24,24,24,48,24,24,24,24,24,48,24,24,24,24,24,48,24,24,24,24,24,24,24,24,1058 + * which is: GlobalCache (31) & "40000,1,1,96,..." (Sony Vol Up) + * + * 25,Rrepeats,hex_code_string + * e.g. 25,R1,0000,006E,0022,0002,0155,00AA,0015,0040,0015,0040,0015,0015,0015,0015,0015,0015,0015,0015,0015,0015,0015,0040,0015,0040,0015,0015,0015,0040,0015,0015,0015,0015,0015,0015,0015,0040,0015,0015,0015,0015,0015,0040,0015,0040,0015,0015,0015,0015,0015,0015,0015,0015,0015,0015,0015,0040,0015,0015,0015,0015,0015,0040,0015,0040,0015,0040,0015,0040,0015,0040,0015,0640,0155,0055,0015,0E40 + * which is: Pronto (25), 1 repeat, & "0000 006E 0022 0002 ..." + * aka a "Sherwood Amp Tape Input" message. + * + * ac_protocol_num,really_long_hexcode + * e.g. 18,190B8050000000E0190B8070000010F0 + * which is: Kelvinator (18) Air Con on, Low Fan, 25 deg etc. + * NOTE: Ensure you zero-pad to the correct number of digits for the + * bit/byte size you want to send as some A/C units have units + * have different sized messages. e.g. Fujitsu A/C units. + * + * In short: + * No spaces after/before commas. + * Values are comma separated. + * The first value is always in Decimal. + * For simple protocols, the next value (hexcode) is always hexadecimal. + * The optional bit size is in decimal. + * CAUTION: Some AC protocols DO NOT use the really_long_hexcode method. + * e.g. < 64bit AC protocols. + * + * Unix command line usage example: + * # Install a MQTT client + * $ sudo apt install mosquitto-clients + * # Send a 32-bit NEC code of 0x1234abcd via MQTT. + * $ mosquitto_pub -h 10.0.0.4 -t ir_server/send -m '3,1234abcd,32' + * + * This server will send (back) what ever IR message it just transmitted to + * the MQTT topic 'ir_server/sent' to confirm it has been performed. This works + * for messages requested via MQTT or via HTTP. + * + * Unix command line usage example: + * # Listen to MQTT acknowledgements. + * $ mosquitto_sub -h 10.0.0.4 -t ir_server/sent + * + * Incoming IR messages (from an IR remote control) will be transmitted to + * the MQTT topic 'ir_server/received'. The MQTT message will be formatted + * similar to what is required to for the 'sent' topic. + * e.g. "3,C1A2F00F,32" (Protocol,Value,Bits) for simple codes + * or "18,110B805000000060110B807000001070" (Protocol,Value) for complex codes + * Note: If the protocol is listed as -1, then that is an UNKNOWN IR protocol. + * You can't use that to recreate/resend an IR message. It's only for + * matching purposes and shouldn't be trusted. + * + * Unix command line usage example: + * # Listen via MQTT for IR messages captured by this server. + * $ mosquitto_sub -h 10.0.0.4 -t ir_server/received + * + * Note: General logging messages are also sent to 'ir_server/log' from + * time to time. + * + * ## Climate (AirCon) interface. (Advanced use) + * You can now control Air Conditioner devices that have full/detailed support + * from the IRremoteESP8266 library. See the "Aircon" page for list of supported + * devices. You can do this via HTTP/HTML or via MQTT. + * + * NOTE: It will only change the attributes you change/set. It's up to you to + * maintain a consistent set of attributes for your particular aircon. + * + * TIP: Use "-1" for 'model' if your A/C doesn't have a specific `setModel()` + * or IR class attribute. Most don't. Some do. + * e.g. PANASONIC_AC, FUJITSU_AC, WHIRLPOOL_AC + * + * ### via MQTT: + * The code listen for commands (via wildcard) on the MQTT topics at the + * `ir_server/ac/cmnd/+` level, such as: + * i.e. protocol, model, power, mode, temp, fanspeed, swingv, swingh, quiet, + * turbo, light, beep, econo, sleep, filter, clean, use_celsius + * e.g. ir_server/ac/cmnd/power, ir_server/ac/cmnd/temp, etc. + * It will process them, and if successful and it caused a change, it will + * acknowledge this via the relevant state topic for that command. + * e.g. If the aircon/climate changes from power off to power on, it will + * send an "on" payload to "ir_server/ac/stat/power" + * NOTE: These "stat" messages have the MQTT retain flag set to on. Thus the + * MQTT broker will remember them until reset/restarted etc. + * + * The code will also periodically broadcast all possible aircon/climate state + * attributes to their corresponding "ir_server/ac/stat" topics. This ensures + * any updates to the ESP's knowledge that may have been lost in transmission + * are re-communicated. e.g. The MQTT broker being offline. + * This also helps with Home Assistant MQTT discovery. + * + * The program on boot & first successful connection to the MQTT broker, will + * try to re-acquire any previous aircon/climate state information and act + * accordingly. This will typically result in A/C IR message being sent as and + * saved state will probably be different from the defaults. + * + * NOTE: Command attributes are processed sequentially. + * e.g. Going from "25C, cool, fan low" to "27C, heat, fan high" may go + * via "27C, cool, fan low" & "27C, heat, fan low" depending on the order + * of arrival & processing of the MQTT commands. + * + * ### Home Assistant (HA) MQTT climate integration + * After you have set the Protocol (required) & Model (if needed) and any of + * the other misc aircon settings you desire, you can then add the following to + * your Home Assistant configuration, and it should allow you to + * control most of the important settings. Google Home/Assistant (via HA) + * can also control the device, but you will need to configure Home Assistant + * via it's documentation for that. It has even more limited control. + * It's far beyond the scope of these instructions to guide you through setting + * up HA and Google Home integration. See https://www.home-assistant.io/ + * + * In HA's configuration.yaml, add: + * + * climate: + * platform: mqtt + * name: Living Room Aircon + * modes: + * - "off" + * - "auto" + * - "cool" + * - "heat" + * - "dry" + * - "fan_only" + * fan_modes: + * - "auto" + * - "min" + * - "low" + * - "medium" + * - "high" + * - "max" + * swing_modes: + * - "off" + * - "auto" + * - "highest" + * - "high" + * - "middle" + * - "low" + * - "lowest" + * power_command_topic: "ir_server/ac/cmnd/power" + * mode_command_topic: "ir_server/ac/cmnd/mode" + * mode_state_topic: "ir_server/ac/stat/mode" + * temperature_command_topic: "ir_server/ac/cmnd/temp" + * temperature_state_topic: "ir_server/ac/stat/temp" + * fan_mode_command_topic: "ir_server/ac/cmnd/fanspeed" + * fan_mode_state_topic: "ir_server/ac/stat/fanspeed" + * swing_mode_command_topic: "ir_server/ac/cmnd/swingv" + * swing_mode_state_topic: "ir_server/ac/stat/swingv" + * min_temp: 16 + * max_temp: 32 + * temp_step: 1 + * retain: false + * + * ### via HTTP: + * Use the "http:///aircon/set" URL and pass on + * the arguments as needed to control your device. See the `KEY_*` #defines + * in the code for all the parameters. + * i.e. protocol, model, power, mode, temp, fanspeed, swingv, swingh, quiet, + * turbo, light, beep, econo, sleep, filter, clean, use_celsius + * Example: + * http:///aircon/set?protocol=PANASONIC_AC&model=LKE&power=on&mode=auto&fanspeed=min&temp=23 + * + * ## Debugging & Logging + * If DEBUG is turned on, there is additional information printed on the Serial + * Port. Serial Port output may be disabled if the GPIO is used for IR. + * + * If MQTT is enabled, some information/logging is sent to the MQTT topic: + * `ir_server/log` + * + * ## Updates + * You can upload new firmware over the air (OTA) via the form on the device's + * main page. No need to connect to the device again via USB. \o/ + * Your WiFi settings should be remembered between updates. \o/ \o/ + * + * ## Security + * + * There is NO authentication set on the HTTP/HTML interface by default (see + * `HTML_PASSWORD_ENABLE` to change that), and there is NO SSL/TLS (encryption) + * used by this example code. + * i.e. All usernames & passwords are sent in clear text. + * All communication to the MQTT server is in clear text. + * e.g. This on/using the public Internet is a 'Really Bad Idea'! + * You should NOT have or use this code or device exposed on an untrusted and/or + * unprotected network. + * If you allow access to OTA firmware updates, then a 'Bad Guy' could + * potentially compromise your network. OTA updates are password protected by + * default. If you are sufficiently paranoid, you SHOULD disable uploading + * firmware via OTA. (see 'FIRMWARE_OTA') + * You SHOULD also set/change all usernames & passwords. + * For extra bonus points: Use a separate untrusted SSID/vlan/network/ segment + * for your IoT stuff, including this device. + * Caveat Emptor. You have now been suitably warned. + * + */ + +#include "IRMQTTServer.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if MQTT_ENABLE +// -------------------------------------------------------------------- +// * * * IMPORTANT * * * +// You must change to have the following value. +// #define MQTT_MAX_PACKET_SIZE 768 +// -------------------------------------------------------------------- +#include +#endif // MQTT_ENABLE +#include // NOLINT(build/include) +#include +#include + +// Globals +ESP8266WebServer server(kHttpPort); +#ifdef IR_RX +IRrecv irrecv(IR_RX, kCaptureBufferSize, kCaptureTimeout, true); +decode_results capture; // Somewhere to store inbound IR messages. +#endif // IR_RX +MDNSResponder mdns; +WiFiClient espClient; +WiFiManager wifiManager; +bool flagSaveWifiConfig = false; +char HttpUsername[kUsernameLength + 1] = "admin"; // Default HTT username. +char HttpPassword[kPasswordLength + 1] = ""; // No HTTP password by default. +char Hostname[kHostnameLength + 1] = "ir_server"; // Default hostname. +uint16_t *codeArray; +uint32_t lastReconnectAttempt = 0; // MQTT last attempt reconnection number +bool boot = true; +bool lockIr = false; // Primitive locking for gating the IR LED. +uint32_t sendReqCounter = 0; +bool lastSendSucceeded = false; // Store the success status of the last send. +uint32_t lastSendTime = 0; +int8_t offset; // The calculated period offset for this chip and library. +IRsend *IrSendTable[kSendTableSize]; + +#ifdef IR_RX +String lastIrReceived = "None"; +uint32_t lastIrReceivedTime = 0; +uint32_t irRecvCounter = 0; +#endif // IR_RX + +// Climate stuff +commonAcState_t climate; +commonAcState_t climate_prev; +IRac commonAc(gpioTable[0]); +TimerMs lastClimateIr = TimerMs(); // When we last sent the IR Climate mesg. +uint32_t irClimateCounter = 0; // How many have we sent? +// Store the success status of the last climate send. +bool lastClimateSucceeded = false; +bool hasClimateBeenSent = false; // Has the Climate ever been sent? + +#if MQTT_ENABLE +PubSubClient mqtt_client(espClient); +String lastMqttCmd = "None"; +String lastMqttCmdTopic = "None"; +uint32_t lastMqttCmdTime = 0; +uint32_t lastConnectedTime = 0; +uint32_t lastDisconnectedTime = 0; +uint32_t mqttDisconnectCounter = 0; +uint32_t mqttSentCounter = 0; +uint32_t mqttRecvCounter = 0; +bool wasConnected = true; + +char MqttServer[kHostnameLength + 1] = "10.0.0.4"; +char MqttPort[kPortLength + 1] = "1883"; +char MqttUsername[kUsernameLength + 1] = ""; +char MqttPassword[kPasswordLength + 1] = ""; +char MqttPrefix[kHostnameLength + 1] = ""; + +String MqttAck; // Sub-topic we send back acknowledgements on. +String MqttSend; // Sub-topic we get new commands from. +String MqttRecv; // Topic we send received IRs to. +String MqttLog; // Topic we send log messages to. +String MqttLwt; // Topic for the Last Will & Testament. +String MqttClimate; // Sub-topic for the climate topics. +String MqttClimateCmnd; // Sub-topic for the climate command topics. +String MqttClimateStat; // Sub-topic for the climate stat topics. +String MqttDiscovery; +String MqttHAName; +String MqttClientId; + +// Primative lock file for gating MQTT state broadcasts. +bool lockMqttBroadcast = true; +TimerMs lastBroadcast = TimerMs(); // When we last sent a broadcast. +bool hasBroadcastBeenSent = false; +TimerMs lastDiscovery = TimerMs(); // When we last sent a Discovery. +bool hasDiscoveryBeenSent = false; +TimerMs statListenTime = TimerMs(); // How long we've been listening for. +#endif // MQTT_ENABLE + +bool isSerialGpioUsedByIr(void) { + const uint8_t kSerialTxGpio = 1; // The GPIO serial output is sent too. + // Note: *DOES NOT* control Serial output. + // Ensure we are not trodding on anything IR related. +#ifdef IR_RX + if (IR_RX == kSerialTxGpio) + return true; // Serial port is in use by IR capture. Abort. +#endif // IR_RX + for (uint8_t i = 0; i < kSendTableSize; i++) + if (gpioTable[i] == kSerialTxGpio) + return true; // Serial port is in use for IR sending. Abort. + return false; // Not in use as far as we can tell. +} + +// Debug messages get sent to the serial port. +void debug(const char *str) { +#if DEBUG + if (isSerialGpioUsedByIr()) return; // Abort. + uint32_t now = millis(); + Serial.printf("%07u.%03u: %s\n", now / 1000, now % 1000, str); +#endif // DEBUG +} + +// callback notifying us of the need to save the wifi config +void saveWifiConfigCallback(void) { + debug("saveWifiConfigCallback called."); + flagSaveWifiConfig = true; +} + +void saveWifiConfig(void) { + debug("Saving the wifi config."); + DynamicJsonBuffer jsonBuffer; + JsonObject& json = jsonBuffer.createObject(); +#if MQTT_ENABLE + json[kMqttServerKey] = MqttServer; + json[kMqttPortKey] = MqttPort; + json[kMqttUserKey] = MqttUsername; + json[kMqttPassKey] = MqttPassword; + json[kMqttPrefixKey] = MqttPrefix; +#endif // MQTT_ENABLE + json[kHostnameKey] = Hostname; + json[kHttpUserKey] = HttpUsername; + json[kHttpPassKey] = HttpPassword; + + if (SPIFFS.begin()) { + File configFile = SPIFFS.open(kConfigFile, "w"); + if (!configFile) { + debug("Failed to open config file for writing."); + } else { + debug("Writing out the config file."); + json.printTo(configFile); + configFile.close(); + debug("Finished writing config file."); + } + SPIFFS.end(); + } +} + +void loadWifiConfigFile(void) { + debug("Trying to mount SPIFFS"); + if (SPIFFS.begin()) { + debug("mounted file system"); + if (SPIFFS.exists(kConfigFile)) { + debug("config file exists"); + + File configFile = SPIFFS.open(kConfigFile, "r"); + if (configFile) { + debug("Opened config file"); + size_t size = configFile.size(); + // Allocate a buffer to store contents of the file. + std::unique_ptr buf(new char[size]); + + configFile.readBytes(buf.get(), size); + DynamicJsonBuffer jsonBuffer; + JsonObject& json = jsonBuffer.parseObject(buf.get()); + if (json.success()) { + debug("Json config file parsed ok."); +#if MQTT_ENABLE + strncpy(MqttServer, json[kMqttServerKey] | "", kHostnameLength); + strncpy(MqttPort, json[kMqttPortKey] | "1883", kPortLength); + strncpy(MqttUsername, json[kMqttUserKey] | "", kUsernameLength); + strncpy(MqttPassword, json[kMqttPassKey] | "", kPasswordLength); + strncpy(MqttPrefix, json[kMqttPrefixKey] | "", kHostnameLength); +#endif // MQTT_ENABLE + strncpy(Hostname, json[kHostnameKey] | "", kHostnameLength); + strncpy(HttpUsername, json[kHttpUserKey] | "", kUsernameLength); + strncpy(HttpPassword, json[kHttpPassKey] | "", kPasswordLength); + debug("Recovered Json fields."); + } else { + debug("Failed to load json config"); + } + debug("Closing the config file."); + configFile.close(); + } + } else { + debug("Config file doesn't exist!"); + } + debug("Unmounting SPIFFS."); + SPIFFS.end(); + } else { + debug("Failed to mount SPIFFS"); + } +} + +String msToHumanString(uint32_t const msecs) { + uint32_t totalseconds = msecs / 1000; + if (totalseconds == 0) return "Now"; + + // Note: millis() can only count 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 = ""; + if (days) result += String(days) + " day"; + if (days > 1) result += 's'; + if (hours) result += ' ' + String(hours) + " hour"; + if (hours > 1) result += 's'; + if (minutes) result += ' ' + String(minutes) + " minute"; + if (minutes > 1) result += 's'; + if (seconds) result += ' ' + String(seconds) + " second"; + if (seconds > 1) result += 's'; + result.trim(); + return result; +} + +String timeElapsed(uint32_t const msec) { + String result = msToHumanString(msec); + if (result.equalsIgnoreCase("Now")) + return result; + else + return result + " ago"; +} + +String timeSince(uint32_t const start) { + if (start == 0) + return "Never"; + uint32_t diff = 0; + uint32_t now = millis(); + if (start < now) + diff = now - start; + else + diff = UINT32_MAX - start + now; + return msToHumanString(diff) + " ago"; +} + +// Return a string containing the comma separated list of sending gpios. +String listOfSendGpios(void) { + String result = String(gpioTable[0]); + if (kSendTableSize > 1) result += " (default)"; + for (uint8_t i = 1; i < kSendTableSize; i++) { + result += ", " + String(gpioTable[i]); + } + return result; +} + +String htmlMenu(void) { + return F( + "
" + "" + "" + "" + "" + "" + "
" + "
"); +} + +// Root web page with example usage etc. +void handleRoot(void) { +#if HTML_PASSWORD_ENABLE + if (!server.authenticate(HttpUsername, HttpPassword)) { + debug("Basic HTTP authentication failure for /."); + return server.requestAuthentication(); + } +#endif + String html = F( + "IR MQTT server" + "" + "

ESP8266 IR MQTT Server

" + "
" _MY_VERSION_ "
"); + html += htmlMenu(); + html += F( + "

Send a simple IR message

" + "

" + "Type: " + "" + " Code: 0x" + " Bit size: " + "" + " Repeats: " + " " + "
" + "

" + "

Send a complex (Air Conditioner) IR message

" + "

" + "Type: " + "" + " State code: 0x" + "" + " " + "
" + "

" + "

Send an IRremote Raw IR message

" + "

" + "" + "String: (freq,array data) " + " " + "
" + "

" + "

Send a GlobalCache" + " IR message

" + "

" + "" + "String: 1:1,1," + " " + "
" + "

" + "

Send a Pronto code IR message

" + "

" + "" + "String (comma separated): " + " Repeats: " + " " + "
" + "
"); + server.send(200, "text/html", html); +} + +String addJsReloadUrl(const String url, const uint16_t timeout_s, + const bool notify) { + String html = F( + "\n"); + return html; +} + +// Web page with hardcoded example usage etc. +void handleExamples(void) { +#if HTML_PASSWORD_ENABLE + if (!server.authenticate(HttpUsername, HttpPassword)) { + debug("Basic HTTP authentication failure for /examples."); + return server.requestAuthentication(); + } +#endif + String html = F( + "IR MQTT examples" + "" + "

ESP8266 IR MQTT Server

" + "
" _MY_VERSION_ "
"); + html += htmlMenu(); + html += F( + "

Hardcoded examples

" + "

" + "Sherwood Amp On (GlobalCache)

" + "

" + "Sherwood Amp Off (Raw)

" + "

" + "Sherwood Amp Input TAPE (Pronto)

" + "

TV on (Samsung)

" + "

Power Off (Sony 12bit)

" + "

" + "Panasonic A/C LKE model, On, Auto mode, Min fan, 23C" + " (via HTTP aircon interface)

" + "

" + "Change just the temp to 27C (via HTTP aircon interface)

" + "

" + "Turn OFF the current A/C (via HTTP aircon interface)

" + "

"); + server.send(200, "text/html", html); +} + +String boolToString(const bool value) { + return value ? F("on") : F("off"); +} + + +String opmodeToString(const stdAc::opmode_t mode) { + switch (mode) { + case stdAc::opmode_t::kOff: + return F("off"); + case stdAc::opmode_t::kAuto: + return F("auto"); + case stdAc::opmode_t::kCool: + return F("cool"); + case stdAc::opmode_t::kHeat: + return F("heat"); + case stdAc::opmode_t::kDry: + return F("dry"); + case stdAc::opmode_t::kFan: + return F("fan_only"); + default: + return F("unknown"); + } +} + +String fanspeedToString(const stdAc::fanspeed_t speed) { + switch (speed) { + case stdAc::fanspeed_t::kAuto: + return F("auto"); + case stdAc::fanspeed_t::kMax: + return F("max"); + case stdAc::fanspeed_t::kHigh: + return F("high"); + case stdAc::fanspeed_t::kMedium: + return F("medium"); + case stdAc::fanspeed_t::kLow: + return F("low"); + case stdAc::fanspeed_t::kMin: + return F("min"); + default: + return F("unknown"); + } +} + +String swingvToString(const stdAc::swingv_t swingv) { + switch (swingv) { + case stdAc::swingv_t::kOff: + return F("off"); + case stdAc::swingv_t::kAuto: + return F("auto"); + case stdAc::swingv_t::kHighest: + return F("highest"); + case stdAc::swingv_t::kHigh: + return F("high"); + case stdAc::swingv_t::kMiddle: + return F("middle"); + case stdAc::swingv_t::kLow: + return F("low"); + case stdAc::swingv_t::kLowest: + return F("lowest"); + default: + return F("unknown"); + } +} + +String swinghToString(const stdAc::swingh_t swingh) { + switch (swingh) { + case stdAc::swingh_t::kOff: + return F("off"); + case stdAc::swingh_t::kAuto: + return F("auto"); + case stdAc::swingh_t::kLeftMax: + return F("leftmax"); + case stdAc::swingh_t::kLeft: + return F("left"); + case stdAc::swingh_t::kMiddle: + return F("middle"); + case stdAc::swingh_t::kRight: + return F("right"); + case stdAc::swingh_t::kRightMax: + return F("rightmax"); + default: + return F("unknown"); + } +} + +String htmlSelectBool(const String name, const bool def) { + String html = ""); + return html; +} + +String htmlSelectProtocol(const String name, const decode_type_t def) { + String html = ""); + return html; +} + +String htmlSelectModel(const String name, const int16_t def) { + String html = ""); + return html; +} + +String htmlSelectMode(const String name, const stdAc::opmode_t def) { + String html = ""); + return html; +} + +String htmlSelectFanspeed(const String name, const stdAc::fanspeed_t def) { + String html = ""); + return html; +} + +String htmlSelectSwingv(const String name, const stdAc::swingv_t def) { + String html = ""); + return html; +} + +String htmlSelectSwingh(const String name, const stdAc::swingh_t def) { + String html = ""); + return html; +} + +// Admin web page +void handleAirCon(void) { + String html = F( + "AirCon control" + "" + "

Air Conditioner Control

"); + html += htmlMenu(); + html += "

Current Settings

" + "
" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "
Protocol" + + htmlSelectProtocol(KEY_PROTOCOL, climate.protocol) + "
Model" + htmlSelectModel(KEY_MODEL, climate.model) + + "
Power" + htmlSelectBool(KEY_POWER, climate.power) + + "
Mode" + htmlSelectMode(KEY_MODE, climate.mode) + + "
Temp" + "" + "
Fan Speed" + + htmlSelectFanspeed(KEY_FANSPEED, climate.fanspeed) + "
Swing (V)" + + htmlSelectSwingv(KEY_SWINGV, climate.swingv) + "
Swing (H)" + + htmlSelectSwingh(KEY_SWINGH, climate.swingh) + "
Quiet" + htmlSelectBool(KEY_QUIET, climate.quiet) + + "
Turbo" + htmlSelectBool(KEY_TURBO, climate.turbo) + + "
Econo" + htmlSelectBool(KEY_ECONO, climate.econo) + + "
Light" + htmlSelectBool(KEY_LIGHT, climate.light) + + "
Filter" + htmlSelectBool(KEY_FILTER, climate.filter) + + "
Clean" + htmlSelectBool(KEY_CLEAN, climate.clean) + + "
Beep" + htmlSelectBool(KEY_BEEP, climate.beep) + + "
" + "" + "
"; + // Display the current settings. + html += F(""); + server.send(200, "text/html", html); +} + +// Parse the URL args to find the Common A/C arguments. +void handleAirConSet(void) { +#if HTML_PASSWORD_ENABLE + if (!server.authenticate(HttpUsername, HttpPassword)) { + debug("Basic HTTP authentication failure for /aircon/set."); + return server.requestAuthentication(); + } +#endif + commonAcState_t result = climate; + debug("New common a/c received via HTTP"); + for (uint16_t i = 0; i < server.args(); i++) + result = updateClimate(result, server.argName(i), "", server.arg(i)); + +#if MQTT_ENABLE + sendClimate(climate, result, MqttClimateStat, + true, false, false); +#else // MQTT_ENABLE + sendClimate(climate, result, "", false, false, false); +#endif // MQTT_ENABLE + // Update the old climate state with the new one. + climate = result; + // Redirect back to the aircon page. + String html = F( + "Update Aircon" + "" + "

Aircon updated!

"); + html += addJsReloadUrl("/aircon", 2, false); + html += F(""); + server.send(200, "text/html", html); +} + +// Admin web page +void handleAdmin(void) { + String html = F( + "IR MQTT server admin" + "" + "

Administration

"); + html += htmlMenu(); + html += F( + "

Special commands

" +#if MQTT_ENABLE + " " + "Send a Climate MQTT discovery message to Home Assistant.

" +#endif // MQTT_ENABLE + " A simple reboot of the ESP8266. " + "ie. No changes

" + " Warning: " + "Resets the device back to original settings. " + "ie. Goes back to AP/Setup mode.
"); +#if FIRMWARE_OTA + html += F("

Update firmware

" + "Warning:
"); + if (!strlen(HttpPassword)) // Deny if password not set + html += F("OTA firmware is disabled until you set a password. " + "You will need to wipe & reset to set one." + "

"); + else // default password has been changed, so allow it. + html += F( + "Updating your firmware may screw up your access to the device. " + "If you are going to use this, know what you are doing first " + "(and you probably do).
" + "

" + "Firmware to upload: " + "" + "
"); +#endif // FIRMWARE_OTA + html += F(""); + server.send(200, "text/html", html); +} + +// Info web page +void handleInfo(void) { + String html = + "IR MQTT server info" + "" + "

Information

"; + html += htmlMenu(); + html += + "

General

" + "

Hostname: " + String(Hostname) + "
" + "IP address: " + WiFi.localIP().toString() + "
" + "Booted: " + timeSince(1) + "
" + + "Version: " _MY_VERSION_ "
" + "Built: " __DATE__ + " " __TIME__ "
" + "Period Offset: " + String(offset) + "us
" + "IR Lib Version: " _IRREMOTEESP8266_VERSION_ "
" + "ESP8266 Core Version: " + ESP.getCoreVersion() + "
" + "IR Send GPIO(s): " + listOfSendGpios() + "
" + "Total send requests: " + String(sendReqCounter) + "
" + "Last message sent: " + String(lastSendSucceeded ? "Ok" : "FAILED") + + " (" + timeSince(lastSendTime) + ")
" +#ifdef IR_RX + "IR Recv GPIO: " + String(IR_RX) + +#if IR_RX_PULLUP + " (pullup)" +#endif // IR_RX_PULLUP + "
" + "Total IR Received: " + String(irRecvCounter) + "
" + "Last IR Received: " + lastIrReceived + + " (" + timeSince(lastIrReceivedTime) + ")
" +#endif // IR_RX + "Duplicate Wifi networks: " + + String(HIDE_DUPLIATE_NETWORKS ? "Hide" : "Show") + "
" + "Min Wifi signal required: " +#ifdef MIN_SIGNAL_STRENGTH + + String(static_cast(MIN_SIGNAL_STRENGTH)) + +#else // MIN_SIGNAL_STRENGTH + "8" +#endif // MIN_SIGNAL_STRENGTH + "%
" + "Serial debugging: " +#if DEBUG + + String(isSerialGpioUsedByIr() ? "Off" : "On") + +#else // DEBUG + "Off" +#endif // DEBUG + "
" + "

" +#if MQTT_ENABLE + "

MQTT Information

" + "

Server: " + String(MqttServer) + ":" + String(MqttPort) + " (" + + (mqtt_client.connected() ? "Connected " + timeSince(lastDisconnectedTime) + : "Disconnected " + timeSince(lastConnectedTime)) + + ")
" + "Disconnections: " + String(mqttDisconnectCounter - 1) + "
" + "Client id: " + MqttClientId + "
" + "Command topic(s): " + listOfCommandTopics() + "
" + "Acknowledgements topic: " + MqttAck + "
" +#ifdef IR_RX + "IR Received topic: " + MqttRecv + "
" +#endif // IR_RX + "Log topic: " + MqttLog + "
" + "LWT topic: " + MqttLwt + "
" + "QoS: " + String(QOS) + "
" + // lastMqttCmd* is unescaped untrusted input. + // Avoid any possible HTML/XSS when displaying it. + "Last MQTT command seen: (topic) '" + htmlEscape(lastMqttCmdTopic) + + "' (payload) '" + htmlEscape(lastMqttCmd) + "' (" + + timeSince(lastMqttCmdTime) + ")
" + "Total published: " + String(mqttSentCounter) + "
" + "Total received: " + String(mqttRecvCounter) + "
" + "

" +#endif // MQTT_ENABLE + "

Climate Information

" + "

" + "IR Send GPIO: " + String(gpioTable[0]) + "
" + "Total sent: " + String(irClimateCounter) + "
" + "Last send: " + String(hasClimateBeenSent ? + (String(lastClimateSucceeded ? "Ok" : "FAILED") + + " (" + timeElapsed(lastClimateIr.elapsed()) + ")") : + "Never") + "
" +#if MQTT_ENABLE + "State listen period: " + msToHumanString(kStatListenPeriodMs) + "
" + "State broadcast period: " + msToHumanString(kBroadcastPeriodMs) + "
" + "Last state broadcast: " + (hasBroadcastBeenSent ? + timeElapsed(lastBroadcast.elapsed()) : + String("Never")) + "
" + "Last discovery sent: " + (lockMqttBroadcast ? + String("Locked") : + (hasDiscoveryBeenSent ? + timeElapsed(lastDiscovery.elapsed()) : + String("Never"))) + + "
" + "Command topics: " + MqttClimateCmnd + + "(" KEY_PROTOCOL "|" KEY_MODEL "|" KEY_POWER "|" KEY_MODE "|" KEY_TEMP "|" + KEY_FANSPEED "|" KEY_SWINGV "|" KEY_SWINGH "|" KEY_QUIET "|" + KEY_TURBO "|" KEY_LIGHT "|" KEY_BEEP "|" KEY_ECONO "|" KEY_SLEEP "|" + KEY_CLOCK "|" KEY_FILTER "|" KEY_CLEAN "|" KEY_CELSIUS ")
" + "State topics: " + MqttClimateStat + + "(" KEY_PROTOCOL "|" KEY_MODEL "|" KEY_POWER "|" KEY_MODE "|" KEY_TEMP "|" + KEY_FANSPEED "|" KEY_SWINGV "|" KEY_SWINGH "|" KEY_QUIET "|" + KEY_TURBO "|" KEY_LIGHT "|" KEY_BEEP "|" KEY_ECONO "|" KEY_SLEEP "|" + KEY_CLOCK "|" KEY_FILTER "|" KEY_CLEAN "|" KEY_CELSIUS ")
" +#endif // MQTT_ENABLE + "

" + // Page footer + "

" + "(Note: Page will refresh every 60 seconds.)" + "

"; + html += addJsReloadUrl("/info", 60, false); + html += ""; + server.send(200, "text/html", html); +} +// Reset web page +void handleReset(void) { +#if HTML_PASSWORD_ENABLE + if (!server.authenticate(HttpUsername, HttpPassword)) { + debug("Basic HTTP authentication failure for /reset."); + return server.requestAuthentication(); + } +#endif + server.send(200, "text/html", + "Reset WiFi Config" + "" + "

Resetting the WiFiManager config back to defaults.

" + "

Device restarting. Try connecting in a few seconds.

" + + addJsReloadUrl("/", 10, true) + + ""); + // Do the reset. +#if MQTT_ENABLE + mqttLog("Wiping all saved config settings."); +#endif // MQTT_ENABLE + debug("Trying to mount SPIFFS"); + if (SPIFFS.begin()) { + debug("Removing JSON config file"); + SPIFFS.remove(kConfigFile); + SPIFFS.end(); + } + delay(1000); + debug("Reseting wifiManager's settings."); + wifiManager.resetSettings(); + delay(1000); + debug("rebooting..."); + ESP.restart(); + delay(1000); +} + +// Reboot web page +void handleReboot() { +#if HTML_PASSWORD_ENABLE + if (!server.authenticate(HttpUsername, HttpPassword)) { + debug("Basic HTTP authentication failure for /quitquitquit."); + return server.requestAuthentication(); + } +#endif + server.send(200, "text/html", + "Rebooting" + "" + "

Device restarting.

" + "

Try connecting in a few seconds.

" + + addJsReloadUrl("/", 15, true) + + ""); +#if MQTT_ENABLE + mqttLog("Reboot requested"); +#endif // MQTT_ENABLE + // Do the reset. + delay(1000); + ESP.restart(); + delay(1000); +} + +// Parse an Air Conditioner A/C Hex String/code and send it. +// Args: +// irsend: A Ptr to the IRsend object to transmit via. +// irType: Nr. of the protocol we need to send. +// str: A hexadecimal string containing the state to be sent. +// Returns: +// bool: Successfully sent or not. +bool parseStringAndSendAirCon(IRsend *irsend, const uint16_t irType, + const String str) { + uint8_t strOffset = 0; + uint8_t state[kStateSizeMax] = {0}; // All array elements are set to 0. + uint16_t stateSize = 0; + + if (str.startsWith("0x") || str.startsWith("0X")) + strOffset = 2; + // Calculate how many hexadecimal characters there are. + uint16_t inputLength = str.length() - strOffset; + if (inputLength == 0) { + debug("Zero length AirCon code encountered. Ignored."); + return false; // No input. Abort. + } + + switch (irType) { // Get the correct state size for the protocol. + case KELVINATOR: + stateSize = kKelvinatorStateLength; + break; + case TOSHIBA_AC: + stateSize = kToshibaACStateLength; + break; + case DAIKIN: + // Daikin has 2 different possible size states. + // (The correct size, and a legacy shorter size.) + // Guess which one we are being presented with based on the number of + // hexadecimal digits provided. i.e. Zero-pad if you need to to get + // the correct length/byte size. + // This should provide backward compatiblity with legacy messages. + stateSize = inputLength / 2; // Every two hex chars is a byte. + // Use at least the minimum size. + stateSize = std::max(stateSize, kDaikinStateLengthShort); + // If we think it isn't a "short" message. + if (stateSize > kDaikinStateLengthShort) + // Then it has to be at least the version of the "normal" size. + stateSize = std::max(stateSize, kDaikinStateLength); + // Lastly, it should never exceed the "normal" size. + stateSize = std::min(stateSize, kDaikinStateLength); + break; + case DAIKIN2: + stateSize = kDaikin2StateLength; + break; + case DAIKIN216: + stateSize = kDaikin216StateLength; + break; + case ELECTRA_AC: + stateSize = kElectraAcStateLength; + break; + case MITSUBISHI_AC: + stateSize = kMitsubishiACStateLength; + break; + case MITSUBISHI_HEAVY_88: + stateSize = kMitsubishiHeavy88StateLength; + break; + case MITSUBISHI_HEAVY_152: + stateSize = kMitsubishiHeavy152StateLength; + break; + case PANASONIC_AC: + stateSize = kPanasonicAcStateLength; + break; + case TROTEC: + stateSize = kTrotecStateLength; + break; + case ARGO: + stateSize = kArgoStateLength; + break; + case GREE: + stateSize = kGreeStateLength; + break; + case FUJITSU_AC: + // Fujitsu has four distinct & different size states, so make a best guess + // which one we are being presented with based on the number of + // hexadecimal digits provided. i.e. Zero-pad if you need to to get + // the correct length/byte size. + stateSize = inputLength / 2; // Every two hex chars is a byte. + // Use at least the minimum size. + stateSize = std::max(stateSize, + (uint16_t) (kFujitsuAcStateLengthShort - 1)); + // If we think it isn't a "short" message. + if (stateSize > kFujitsuAcStateLengthShort) + // Then it has to be at least the smaller version of the "normal" size. + stateSize = std::max(stateSize, (uint16_t) (kFujitsuAcStateLength - 1)); + // Lastly, it should never exceed the maximum "normal" size. + stateSize = std::min(stateSize, kFujitsuAcStateLength); + break; + case HAIER_AC: + stateSize = kHaierACStateLength; + break; + case HAIER_AC_YRW02: + stateSize = kHaierACYRW02StateLength; + break; + case HITACHI_AC: + stateSize = kHitachiAcStateLength; + break; + case HITACHI_AC1: + stateSize = kHitachiAc1StateLength; + break; + case HITACHI_AC2: + stateSize = kHitachiAc2StateLength; + break; + case WHIRLPOOL_AC: + stateSize = kWhirlpoolAcStateLength; + break; + case SAMSUNG_AC: + // Samsung has two distinct & different size states, so make a best guess + // which one we are being presented with based on the number of + // hexadecimal digits provided. i.e. Zero-pad if you need to to get + // the correct length/byte size. + stateSize = inputLength / 2; // Every two hex chars is a byte. + // Use at least the minimum size. + stateSize = std::max(stateSize, (uint16_t) (kSamsungAcStateLength)); + // If we think it isn't a "normal" message. + if (stateSize > kSamsungAcStateLength) + // Then it probably the extended size. + stateSize = std::max(stateSize, + (uint16_t) (kSamsungAcExtendedStateLength)); + // Lastly, it should never exceed the maximum "extended" size. + stateSize = std::min(stateSize, kSamsungAcExtendedStateLength); + break; + case MWM: + // MWM has variable size states, so make a best guess + // which one we are being presented with based on the number of + // hexadecimal digits provided. i.e. Zero-pad if you need to to get + // the correct length/byte size. + stateSize = inputLength / 2; // Every two hex chars is a byte. + // Use at least the minimum size. + stateSize = std::max(stateSize, (uint16_t) 3); + // Cap the maximum size. + stateSize = std::min(stateSize, kStateSizeMax); + break; + case TCL112AC: + stateSize = kTcl112AcStateLength; + break; + default: // Not a protocol we expected. Abort. + debug("Unexpected AirCon protocol detected. Ignoring."); + return false; + } + if (inputLength > stateSize * 2) { + debug("AirCon code to large for the given protocol."); + return false; + } + + // Ptr to the least significant byte of the resulting state for this protocol. + uint8_t *statePtr = &state[stateSize - 1]; + + // Convert the string into a state array of the correct length. + for (uint16_t i = 0; i < inputLength; i++) { + // Grab the next least sigificant hexadecimal digit from the string. + uint8_t c = tolower(str[inputLength + strOffset - i - 1]); + if (isxdigit(c)) { + if (isdigit(c)) + c -= '0'; + else + c = c - 'a' + 10; + } else { + debug("Aborting! Non-hexadecimal char found in AirCon state:"); + debug(str.c_str()); + return false; + } + if (i % 2 == 1) { // Odd: Upper half of the byte. + *statePtr += (c << 4); + statePtr--; // Advance up to the next least significant byte of state. + } else { // Even: Lower half of the byte. + *statePtr = c; + } + } + + // Make the appropriate call for the protocol type. + switch (irType) { +#if SEND_KELVINATOR + case KELVINATOR: + irsend->sendKelvinator(reinterpret_cast(state)); + break; +#endif +#if SEND_TOSHIBA_AC + case TOSHIBA_AC: + irsend->sendToshibaAC(reinterpret_cast(state)); + break; +#endif +#if SEND_DAIKIN + case DAIKIN: + irsend->sendDaikin(reinterpret_cast(state)); + break; +#endif +#if SEND_DAIKIN2 + case DAIKIN2: + irsend->sendDaikin2(reinterpret_cast(state)); + break; +#endif +#if SEND_DAIKIN216 + case DAIKIN216: + irsend->sendDaikin216(reinterpret_cast(state)); + break; +#endif // SEND_DAIKIN216 +#if SEND_MITSUBISHI_AC + case MITSUBISHI_AC: + irsend->sendMitsubishiAC(reinterpret_cast(state)); + break; +#endif +#if SEND_MITSUBISHIHEAVY + case MITSUBISHI_HEAVY_88: // 59 + irsend->sendMitsubishiHeavy88(reinterpret_cast(state)); + break; + case MITSUBISHI_HEAVY_152: // 60 + irsend->sendMitsubishiHeavy152(reinterpret_cast(state)); + break; +#endif // SEND_MITSUBISHIHEAVY +#if SEND_TROTEC + case TROTEC: + irsend->sendTrotec(reinterpret_cast(state)); + break; +#endif +#if SEND_ARGO + case ARGO: + irsend->sendArgo(reinterpret_cast(state)); + break; +#endif +#if SEND_GREE + case GREE: + irsend->sendGree(reinterpret_cast(state)); + break; +#endif +#if SEND_FUJITSU_AC + case FUJITSU_AC: + irsend->sendFujitsuAC(reinterpret_cast(state), stateSize); + break; +#endif +#if SEND_HAIER_AC + case HAIER_AC: + irsend->sendHaierAC(reinterpret_cast(state)); + break; +#endif +#if SEND_HAIER_AC_YRW02 + case HAIER_AC_YRW02: + irsend->sendHaierACYRW02(reinterpret_cast(state)); + break; +#endif +#if SEND_HITACHI_AC + case HITACHI_AC: + irsend->sendHitachiAC(reinterpret_cast(state)); + break; +#endif +#if SEND_HITACHI_AC1 + case HITACHI_AC1: + irsend->sendHitachiAC1(reinterpret_cast(state)); + break; +#endif +#if SEND_HITACHI_AC2 + case HITACHI_AC2: + irsend->sendHitachiAC2(reinterpret_cast(state)); + break; +#endif +#if SEND_WHIRLPOOL_AC + case WHIRLPOOL_AC: + irsend->sendWhirlpoolAC(reinterpret_cast(state)); + break; +#endif +#if SEND_SAMSUNG_AC + case SAMSUNG_AC: + irsend->sendSamsungAC(reinterpret_cast(state), stateSize); + break; +#endif +#if SEND_ELECTRA_AC + case ELECTRA_AC: + irsend->sendElectraAC(reinterpret_cast(state)); + break; +#endif +#if SEND_PANASONIC_AC + case PANASONIC_AC: + irsend->sendPanasonicAC(reinterpret_cast(state)); + break; +#endif +#if SEND_MWM + case MWM: + irsend->sendMWM(reinterpret_cast(state), stateSize); + break; +#endif +#if SEND_TCL112AC + case TCL112AC: + irsend->sendTcl112Ac(reinterpret_cast(state)); + break; +#endif + default: + debug("Unexpected AirCon type in send request. Not sent."); + return false; + } + return true; // We were successful as far as we can tell. +} + +// Count how many values are in the String. +// Args: +// str: String containing the values. +// sep: Character that separates the values. +// Returns: +// The number of values found in the String. +uint16_t countValuesInStr(const String str, char sep) { + int16_t index = -1; + uint16_t count = 1; + do { + index = str.indexOf(sep, index + 1); + count++; + } while (index != -1); + return count; +} + +// Dynamically allocate an array of uint16_t's. +// Args: +// size: Nr. of uint16_t's need to be in the new array. +// Returns: +// A Ptr to the new array. Restarts the ESP8266 if it fails. +uint16_t * newCodeArray(const uint16_t size) { + uint16_t *result; + + result = reinterpret_cast(malloc(size * sizeof(uint16_t))); + // Check we malloc'ed successfully. + if (result == NULL) { // malloc failed, so give up. + Serial.printf("\nCan't allocate %d bytes. (%d bytes free)\n", + size * sizeof(uint16_t), ESP.getFreeHeap()); + Serial.println("Giving up & forcing a reboot."); + ESP.restart(); // Reboot. + delay(500); // Wait for the restart to happen. + return result; // Should never get here, but just in case. + } + return result; +} + +#if SEND_GLOBALCACHE +// Parse a GlobalCache String/code and send it. +// Args: +// irsend: A ptr to the IRsend object to transmit via. +// str: A GlobalCache formatted String of comma separated numbers. +// e.g. "38000,1,1,170,170,20,63,20,63,20,63,20,20,20,20,20,20,20,20,20, +// 20,20,63,20,63,20,63,20,20,20,20,20,20,20,20,20,20,20,20,20,63, +// 20,20,20,20,20,20,20,20,20,20,20,20,20,63,20,20,20,63,20,63,20, +// 63,20,63,20,63,20,63,20,1798" +// Note: The leading "1:1,1," of normal GC codes should be removed. +// Returns: +// bool: Successfully sent or not. +bool parseStringAndSendGC(IRsend *irsend, const String str) { + uint16_t count; + uint16_t *code_array; + String tmp_str; + + // Remove the leading "1:1,1," if present. + if (str.startsWith("1:1,1,")) + tmp_str = str.substring(6); + else + tmp_str = str; + + // Find out how many items there are in the string. + count = countValuesInStr(tmp_str, ','); + + // Now we know how many there are, allocate the memory to store them all. + code_array = newCodeArray(count); + + // Now convert the strings to integers and place them in code_array. + count = 0; + uint16_t start_from = 0; + int16_t index = -1; + do { + index = tmp_str.indexOf(',', start_from); + code_array[count] = tmp_str.substring(start_from, index).toInt(); + start_from = index + 1; + count++; + } while (index != -1); + + irsend->sendGC(code_array, count); // All done. Send it. + free(code_array); // Free up the memory allocated. + if (count > 0) + return true; // We sent something. + return false; // We probably didn't. +} +#endif // SEND_GLOBALCACHE + +#if SEND_PRONTO +// Parse a Pronto Hex String/code and send it. +// Args: +// irsend: A ptr to the IRsend object to transmit via. +// str: A comma-separated String of nr. of repeats, then hexadecimal numbers. +// e.g. "R1,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" +// or +// "0000,0067,0000,0015,0060,0018". i.e. without the Repeat value +// Requires at least kProntoMinLength comma-separated values. +// sendPronto() only supports raw pronto code types, thus so does this. +// repeats: Nr. of times the message is to be repeated. +// This value is ignored if an embeddd repeat is found in str. +// Returns: +// bool: Successfully sent or not. +bool parseStringAndSendPronto(IRsend *irsend, const String str, + uint16_t repeats) { + uint16_t count; + uint16_t *code_array; + int16_t index = -1; + uint16_t start_from = 0; + + // Find out how many items there are in the string. + count = countValuesInStr(str, ','); + + // Check if we have the optional embedded repeats value in the code string. + if (str.startsWith("R") || str.startsWith("r")) { + // Grab the first value from the string, as it is the nr. of repeats. + index = str.indexOf(',', start_from); + repeats = str.substring(start_from + 1, index).toInt(); // Skip the 'R'. + start_from = index + 1; + count--; // We don't count the repeats value as part of the code array. + } + + // We need at least kProntoMinLength values for the code part. + if (count < kProntoMinLength) return false; + + // Now we know how many there are, allocate the memory to store them all. + code_array = newCodeArray(count); + + // Rest of the string are values for the code array. + // Now convert the hex strings to integers and place them in code_array. + count = 0; + do { + index = str.indexOf(',', start_from); + // Convert the hexadecimal value string to an unsigned integer. + code_array[count] = strtoul(str.substring(start_from, index).c_str(), + NULL, 16); + start_from = index + 1; + count++; + } while (index != -1); + + irsend->sendPronto(code_array, count, repeats); // All done. Send it. + free(code_array); // Free up the memory allocated. + if (count > 0) + return true; // We sent something. + return false; // We probably didn't. +} +#endif // SEND_PRONTO + +#if SEND_RAW +// Parse an IRremote Raw Hex String/code and send it. +// Args: +// irsend: A ptr to the IRsend object to transmit via. +// str: A comma-separated String containing the freq and raw IR data. +// e.g. "38000,9000,4500,600,1450,600,900,650,1500,..." +// Requires at least two comma-separated values. +// First value is the transmission frequency in Hz or kHz. +// Returns: +// bool: Successfully sent or not. +bool parseStringAndSendRaw(IRsend *irsend, const String str) { + uint16_t count; + uint16_t freq = 38000; // Default to 38kHz. + uint16_t *raw_array; + + // Find out how many items there are in the string. + count = countValuesInStr(str, ','); + + // We expect the frequency as the first comma separated value, so we need at + // least two values. If not, bail out. + if (count < 2) return false; + count--; // We don't count the frequency value as part of the raw array. + + // Now we know how many there are, allocate the memory to store them all. + raw_array = newCodeArray(count); + + // Grab the first value from the string, as it is the frequency. + int16_t index = str.indexOf(',', 0); + freq = str.substring(0, index).toInt(); + uint16_t start_from = index + 1; + // Rest of the string are values for the raw array. + // Now convert the strings to integers and place them in raw_array. + count = 0; + do { + index = str.indexOf(',', start_from); + raw_array[count] = str.substring(start_from, index).toInt(); + start_from = index + 1; + count++; + } while (index != -1); + + irsend->sendRaw(raw_array, count, freq); // All done. Send it. + free(raw_array); // Free up the memory allocated. + if (count > 0) + return true; // We sent something. + return false; // We probably didn't. +} +#endif // SEND_RAW + +// Parse the URL args to find the IR code. +void handleIr(void) { +#if HTML_PASSWORD_ENABLE + if (!server.authenticate(HttpUsername, HttpPassword)) { + debug("Basic HTTP authentication failure for /ir."); + return server.requestAuthentication(); + } +#endif + uint64_t data = 0; + String data_str = ""; + int16_t ir_type = decode_type_t::NEC; // Default to NEC codes. + uint16_t nbits = 0; + uint16_t repeat = 0; + + for (uint16_t i = 0; i < server.args(); i++) { + if (server.argName(i).equals(KEY_TYPE) || + server.argName(i).equals(KEY_PROTOCOL)) { + ir_type = strToDecodeType(server.arg(i).c_str()); + } else if (server.argName(i).equals(KEY_CODE)) { + data = getUInt64fromHex(server.arg(i).c_str()); + data_str = server.arg(i); + } else if (server.argName(i).equals(KEY_BITS)) { + nbits = server.arg(i).toInt(); + } else if (server.argName(i).equals(KEY_REPEAT)) { + repeat = server.arg(i).toInt(); + } + } + debug("New code received via HTTP"); + lastSendSucceeded = sendIRCode(IrSendTable[0], ir_type, data, + data_str.c_str(), nbits, repeat); + String html = F( + "Send IR command" + "" + "

IR command sent!

"); + html += addJsReloadUrl("/", 2, true); + html += F(""); + server.send(200, "text/html", html); +} + +void handleNotFound(void) { + String message = "File Not Found\n\n"; + message += "URI: "; + message += server.uri(); + message += "\nMethod: "; + message += (server.method() == HTTP_GET)?"GET":"POST"; + message += "\nArguments: "; + message += server.args(); + message += "\n"; + for (uint8_t i=0; i < server.args(); i++) + message += " " + server.argName(i) + ": " + server.arg(i) + "\n"; + server.send(404, "text/plain", message); +} + +void setup_wifi(void) { + delay(10); + loadWifiConfigFile(); + // We start by connecting to a WiFi network + wifiManager.setTimeout(300); // Time out after 5 mins. + // Set up additional parameters for WiFiManager config menu page. + wifiManager.setSaveConfigCallback(saveWifiConfigCallback); + WiFiManagerParameter custom_hostname_text( + "
Hostname
"); + wifiManager.addParameter(&custom_hostname_text); + WiFiManagerParameter custom_hostname( + kHostnameKey, kHostnameKey, Hostname, kHostnameLength); + wifiManager.addParameter(&custom_hostname); + WiFiManagerParameter custom_authentication_text( + "

Web/OTA authentication
"); + wifiManager.addParameter(&custom_authentication_text); + WiFiManagerParameter custom_http_username( + kHttpUserKey, "username", HttpUsername, kUsernameLength); + wifiManager.addParameter(&custom_http_username); + WiFiManagerParameter custom_http_password( + kHttpPassKey, "password (No OTA if blank)", HttpPassword, kPasswordLength, + " type='password'"); + wifiManager.addParameter(&custom_http_password); +#if MQTT_ENABLE + WiFiManagerParameter custom_mqtt_text( + "

MQTT Broker details
"); + wifiManager.addParameter(&custom_mqtt_text); + WiFiManagerParameter custom_mqtt_server( + kMqttServerKey, "mqtt server", MqttServer, kHostnameLength); + wifiManager.addParameter(&custom_mqtt_server); + WiFiManagerParameter custom_mqtt_port( + kMqttPortKey, "mqtt port", MqttPort, kPortLength, + " input type='number' min='1' max='65535'"); + wifiManager.addParameter(&custom_mqtt_port); + WiFiManagerParameter custom_mqtt_user( + kMqttUserKey, "mqtt username", MqttUsername, kUsernameLength); + wifiManager.addParameter(&custom_mqtt_user); + WiFiManagerParameter custom_mqtt_pass( + kMqttPassKey, "mqtt password", MqttPassword, kPasswordLength, + " type='password'"); + wifiManager.addParameter(&custom_mqtt_pass); + WiFiManagerParameter custom_prefix_text( + "

MQTT Prefix
"); + wifiManager.addParameter(&custom_prefix_text); + WiFiManagerParameter custom_mqtt_prefix( + kMqttPrefixKey, "Leave empty to use Hostname", MqttPrefix, + kHostnameLength); + wifiManager.addParameter(&custom_mqtt_prefix); + #endif // MQTT_ENABLE +#if USE_STATIC_IP + // Use a static IP config rather than the one supplied via DHCP. + wifiManager.setSTAStaticIPConfig(kIPAddress, kGateway, kSubnetMask); +#endif // USE_STATIC_IP +#if MIN_SIGNAL_STRENGTH + wifiManager.setMinimumSignalQuality(MIN_SIGNAL_STRENGTH); +#endif // MIN_SIGNAL_STRENGTH + wifiManager.setRemoveDuplicateAPs(HIDE_DUPLIATE_NETWORKS); + + if (!wifiManager.autoConnect()) { + debug("Wifi failed to connect and hit timeout. Rebooting..."); + delay(3000); + // Reboot. A.k.a. "Have you tried turning it Off and On again?" + ESP.reset(); + delay(5000); + } + +#if MQTT_ENABLE + strncpy(MqttServer, custom_mqtt_server.getValue(), kHostnameLength); + strncpy(MqttPort, custom_mqtt_port.getValue(), kPortLength); + strncpy(MqttUsername, custom_mqtt_user.getValue(), kUsernameLength); + strncpy(MqttPassword, custom_mqtt_pass.getValue(), kPasswordLength); + strncpy(MqttPrefix, custom_mqtt_prefix.getValue(), kHostnameLength); +#endif // MQTT_ENABLE + strncpy(Hostname, custom_hostname.getValue(), kHostnameLength); + strncpy(HttpUsername, custom_http_username.getValue(), kUsernameLength); + strncpy(HttpPassword, custom_http_password.getValue(), kPasswordLength); + if (flagSaveWifiConfig) { + saveWifiConfig(); + } + debug("WiFi connected. IP address:"); + debug(WiFi.localIP().toString().c_str()); +} + +void init_vars(void) { +#if MQTT_ENABLE + // If we have a prefix already, use it. Otherwise use the hostname. + if (!strlen(MqttPrefix)) strncpy(MqttPrefix, Hostname, kHostnameLength); + // Topic we send back acknowledgements on. + MqttAck = String(MqttPrefix) + '/' + MQTT_ACK; + // Sub-topic we get new commands from. + MqttSend = String(MqttPrefix) + '/' + MQTT_SEND; + // Topic we send received IRs to. + MqttRecv = String(MqttPrefix) + '/' + MQTT_RECV; + // Topic we send log messages to. + MqttLog = String(MqttPrefix) + '/' + MQTT_LOG; + // Topic for the Last Will & Testament. + MqttLwt = String(MqttPrefix) + '/' + MQTT_LWT; + // Sub-topic for the climate topics. + MqttClimate = String(MqttPrefix) + '/' + MQTT_CLIMATE; + // Sub-topic for the climate command topics. + MqttClimateCmnd = MqttClimate + '/' + MQTT_CLIMATE_CMND + '/'; + // Sub-topic for the climate stat topics. + MqttClimateStat = MqttClimate + '/' + MQTT_CLIMATE_STAT + '/'; + MqttDiscovery = "homeassistant/climate/" + String(Hostname) + "/config"; + MqttHAName = String(Hostname) + "_aircon"; + // Create a unique MQTT client id. + MqttClientId = String(Hostname) + String(ESP.getChipId(), HEX); +#endif // MQTT_ENABLE +} + +void setup(void) { + // Set the default climate settings. + climate.protocol = decode_type_t::UNKNOWN; + climate.model = -1; // Unknown. + climate.power = false; + climate.mode = stdAc::opmode_t::kAuto; + climate.celsius = true; + climate.degrees = 25; // 25C + climate.fanspeed = stdAc::fanspeed_t::kAuto; + climate.swingv = stdAc::swingv_t::kAuto; + climate.swingh = stdAc::swingh_t::kAuto; + climate.quiet = false; + climate.turbo = false; + climate.econo = false; + climate.light = false; + climate.filter = false; + climate.clean = false; + climate.beep = false; + climate.sleep = -1; // Off + climate.clock = -1; // Don't set. + climate_prev = climate; + + // Initialise all the IR transmitters. + for (uint8_t i = 0; i < kSendTableSize; i++) { + IrSendTable[i] = new IRsend(gpioTable[i]); + IrSendTable[i]->begin(); + offset = IrSendTable[i]->calibrate(); + } +#ifdef IR_RX +#if IR_RX_PULLUP + pinMode(IR_RX, INPUT_PULLUP); +#endif // IR_RX_PULLUP +#if DECODE_HASH + // Ignore messages with less than minimum on or off pulses. + irrecv.setUnknownThreshold(kMinUnknownSize); +#endif // DECODE_HASH + irrecv.enableIRIn(); // Start the receiver +#endif // IR_RX + +#if DEBUG + if (!isSerialGpioUsedByIr()) { + // Use SERIAL_TX_ONLY so that the RX pin can be freed up for GPIO/IR use. + Serial.begin(BAUD_RATE, SERIAL_8N1, SERIAL_TX_ONLY); + while (!Serial) // Wait for the serial connection to be establised. + delay(50); + Serial.println(); + debug("IRMQTTServer " _MY_VERSION_" has booted."); + } +#endif // DEBUG + + setup_wifi(); + + // Wait a bit for things to settle. + delay(500); + + lastReconnectAttempt = 0; + + if (mdns.begin(Hostname, WiFi.localIP())) { + debug("MDNS responder started"); + } + + // Setup the root web page. + server.on("/", handleRoot); + // Setup the examples web page. + server.on("/examples", handleExamples); + // Setup the page to handle web-based IR codes. + server.on("/ir", handleIr); + // Setup the aircon page. + server.on("/aircon", handleAirCon); + // Setup the aircon update page. + server.on("/aircon/set", handleAirConSet); + // Setup the info page. + server.on("/info", handleInfo); + // Setup the admin page. + server.on("/admin", handleAdmin); + // Setup a reset page to cause WiFiManager information to be reset. + server.on("/reset", handleReset); + // Reboot url + server.on("/quitquitquit", handleReboot); +#if MQTT_ENABLE + // MQTT Discovery url + server.on("/send_discovery", handleSendMqttDiscovery); + // Finish setup of the mqtt clent object. + mqtt_client.setServer(MqttServer, atoi(MqttPort)); + mqtt_client.setCallback(mqttCallback); + // Set various variables + init_vars(); +#endif // MQTT_ENABLE + +#if FIRMWARE_OTA + // Setup the URL to allow Over-The-Air (OTA) firmware updates. + if (strlen(HttpPassword)) { // Allow if password is set. + server.on("/update", HTTP_POST, [](){ +#if MQTT_ENABLE + mqttLog("Attempting firmware update & reboot"); + delay(1000); +#endif // MQTT_ENABLE + server.send(200, "text/html", + "Updating firmware." + "" + "

Updating firmware

" + "
" + "

Warning! Don't power off the device for 60 seconds!

" + "

The firmware is uploading and will try to flash itself. " + "It is important to not interrupt the process.

" + "

The firmware upload seems to have " + + String(Update.hasError() ? "FAILED!" : "SUCCEEDED!") + + " Rebooting!

" + + addJsReloadUrl("/", 20, true) + + ""); + delay(1000); + ESP.restart(); + delay(1000); + }, [](){ + if (!server.authenticate(HttpUsername, HttpPassword)) { + debug("Basic HTTP authentication failure for /update."); + return server.requestAuthentication(); + } + HTTPUpload& upload = server.upload(); + if (upload.status == UPLOAD_FILE_START) { + WiFiUDP::stopAll(); + debug("Update:"); + debug(upload.filename.c_str()); + uint32_t maxSketchSpace = (ESP.getFreeSketchSpace() - 0x1000) & + 0xFFFFF000; + if (!Update.begin(maxSketchSpace)) { // start with max available size +#if DEBUG + if (!isSerialGpioUsedByIr()) + Update.printError(Serial); +#endif // DEBUG + } + } else if (upload.status == UPLOAD_FILE_WRITE) { + if (Update.write(upload.buf, upload.currentSize) != + upload.currentSize) { +#if DEBUG + if (!isSerialGpioUsedByIr()) + Update.printError(Serial); +#endif // DEBUG + } + } else if (upload.status == UPLOAD_FILE_END) { + // true to set the size to the current progress + if (Update.end(true)) { + debug("Update Success:"); + debug(String(upload.totalSize).c_str()); + debug("Rebooting..."); + } + } + yield(); + }); + } +#endif // FIRMWARE_OTA + + // Set up an error page. + server.onNotFound(handleNotFound); + + server.begin(); + debug("HTTP server started"); +} + +#if MQTT_ENABLE +// MQTT subscribing to topic +void subscribing(const String topic_name) { + // subscription to topic for receiving data with QoS. + if (mqtt_client.subscribe(topic_name.c_str(), QOS)) + debug("Subscription OK to:"); + else + debug("Subscription FAILED to:"); + debug(topic_name.c_str()); +} + +// Un-subscribe from a MQTT topic +void unsubscribing(const String topic_name) { + // subscription to topic for receiving data with QoS. + if (mqtt_client.unsubscribe(topic_name.c_str())) + debug("Unsubscribed OK from:"); + else + debug("FAILED to unsubscribe from:"); + debug(topic_name.c_str()); +} + +void mqttLog(const String mesg) { + debug(mesg.c_str()); + mqtt_client.publish(MqttLog.c_str(), mesg.c_str()); + mqttSentCounter++; +} + +bool reconnect(void) { + // Loop a few times or until we're reconnected + uint16_t tries = 1; + while (!mqtt_client.connected() && tries <= 3) { + int connected = false; + // Attempt to connect + debug(("Attempting MQTT connection to " + String(MqttServer) + ":" + + String(MqttPort) + "... ").c_str()); + if (strcmp(MqttUsername, "") && strcmp(MqttPassword, "")) { + debug("Using mqtt username/password to connect."); + connected = mqtt_client.connect(MqttClientId.c_str(), + MqttUsername, MqttPassword, + MqttLwt.c_str(), + QOS, true, kLwtOffline); + + } else { + debug("Using password-less mqtt connection."); + connected = mqtt_client.connect(MqttClientId.c_str(), MqttLwt.c_str(), + QOS, true, kLwtOffline); + } + if (connected) { + // Once connected, publish an announcement... + mqttLog("(Re)Connected."); + + // Update Last Will & Testament to say we are back online. + mqtt_client.publish(MqttLwt.c_str(), kLwtOnline, true); + mqttSentCounter++; + + // Subscribing to topic(s) + subscribing(MqttSend); + for (uint8_t i = 0; i < kSendTableSize; i++) { + subscribing(MqttSend + '_' + String(static_cast(i))); + } + // Climate command topics. + subscribing(MqttClimateCmnd + '+'); + } else { + debug(("failed, rc=" + String(mqtt_client.state()) + + " Try again in a bit.").c_str()); + // Wait for a bit before retrying + delay(tries << 7); // Linear increasing back-off (x128) + } + tries++; + } + return mqtt_client.connected(); +} + +// Return a string containing the comma separated list of MQTT command topics. +String listOfCommandTopics(void) { + String result = MqttSend; + for (uint16_t i = 0; i < kSendTableSize; i++) { + result += ", " + MqttSend + '_' + String(i); + } + return result; +} + +// MQTT Discovery web page +void handleSendMqttDiscovery(void) { +#if HTML_PASSWORD_ENABLE + if (!server.authenticate(HttpUsername, HttpPassword)) { + debug("Basic HTTP authentication failure for /send_discovery."); + return server.requestAuthentication(); + } +#endif // HTML_PASSWORD_ENABLE + server.send(200, "text/html", + "Sending MQTT Discovery message" + "" + "

Sending MQTT Discovery message.

" + + htmlMenu() + + "

The Home Assistant MQTT Discovery message is being sent to topic: " + + MqttDiscovery + ". It will show up in Home Assistant in a few seconds." + "

" + "

Warning!

" + "

Home Assistant's config for this device is reset each time this is " + " is sent.

" + + addJsReloadUrl("/", 15, true) + + ""); + sendMQTTDiscovery(MqttDiscovery.c_str()); +} + +void doBroadcast(TimerMs *timer, const uint32_t interval, + const commonAcState_t state, const bool retain, + const bool force) { + if (force || (!lockMqttBroadcast && timer->elapsed() > interval)) { + debug("Sending MQTT stat update broadcast."); + sendClimate(state, state, MqttClimateStat, + retain, true, false); + timer->reset(); // It's been sent, so reset the timer. + hasBroadcastBeenSent = true; + } +} + +void receivingMQTT(String const topic_name, String const callback_str) { + char* tok_ptr; + uint64_t code = 0; + uint16_t nbits = 0; + uint16_t repeat = 0; + uint8_t channel = 0; // Default to the first channel. e.g. "*_0" + + debug("Receiving data by MQTT topic:"); + debug(topic_name.c_str()); + debug("with payload:"); + debug(callback_str.c_str()); + // Save the message as the last command seen (global). + lastMqttCmdTopic = topic_name; + lastMqttCmd = callback_str; + lastMqttCmdTime = millis(); + mqttRecvCounter++; + + if (topic_name.startsWith(MqttClimate)) { + if (topic_name.startsWith(MqttClimateCmnd)) { + debug("It's a climate command topic"); + commonAcState_t updated = updateClimate( + climate, topic_name, MqttClimateCmnd, callback_str); + sendClimate(climate, updated, MqttClimateStat, + true, false, false); + climate = updated; + } else if (topic_name.startsWith(MqttClimateStat)) { + debug("It's a climate state topic. Update internal state and DON'T send"); + climate = updateClimate( + climate, topic_name, MqttClimateStat, callback_str); + } + return; // We are done for now. + } + // Check if a specific channel was requested by looking for a "*_[0-9]" suffix + for (uint8_t i = 0; i < kSendTableSize; i++) { + debug(("Checking if " + topic_name + " ends with _" + String(i)).c_str()); + if (topic_name.endsWith("_" + String(i))) { + channel = i; + debug("It does!"); + break; + } + } + + debug(("Using transmit channel " + String(static_cast(channel)) + + " / GPIO " + String(static_cast(gpioTable[channel]))).c_str()); + // Make a copy of the callback string as strtok destroys it. + char* callback_c_str = strdup(callback_str.c_str()); + debug("MQTT Payload (raw):"); + debug(callback_c_str); + + // Get the numeric protocol type. + int ir_type = strtoul(strtok_r(callback_c_str, ",", &tok_ptr), NULL, 10); + char* next = strtok_r(NULL, ",", &tok_ptr); + // If there is unparsed string left, try to convert it assuming it's hex. + if (next != NULL) { + code = getUInt64fromHex(next); + next = strtok_r(NULL, ",", &tok_ptr); + } else { + // We require at least two value in the string. Give up. + return; + } + // If there is still string left, assume it is the bit size. + if (next != NULL) { + nbits = atoi(next); + next = strtok_r(NULL, ",", &tok_ptr); + } + // If there is still string left, assume it is the repeat count. + if (next != NULL) + repeat = atoi(next); + + free(callback_c_str); + + // send received MQTT value by IR signal + lastSendSucceeded = sendIRCode( + IrSendTable[channel], ir_type, code, + callback_str.substring(callback_str.indexOf(",") + 1).c_str(), + nbits, repeat); +} + +// Callback function, when we receive an MQTT value on the topics +// subscribed this function is called +void mqttCallback(char* topic, byte* payload, unsigned int length) { + // In order to republish this payload, a copy must be made + // as the orignal payload buffer will be overwritten whilst + // constructing the PUBLISH packet. + // Allocate the correct amount of memory for the payload copy + byte* payload_copy = reinterpret_cast(malloc(length + 1)); + // Copy the payload to the new buffer + memcpy(payload_copy, payload, length); + + // Conversion to a printable string + payload_copy[length] = '\0'; + String callback_string = String(reinterpret_cast(payload_copy)); + String topic_name = String(reinterpret_cast(topic)); + + // launch the function to treat received data + receivingMQTT(topic_name, callback_string); + + // Free the memory + free(payload_copy); +} + +void sendMQTTDiscovery(const char *topic) { + if (mqtt_client.publish( + topic, String( + "{" + "\"~\":\"" + MqttClimate + "\"," + "\"name\":\"" + MqttHAName + "\"," + "\"pow_cmd_t\":\"~" MQTT_CLIMATE "/" MQTT_CLIMATE_CMND "/" KEY_POWER "\"," + "\"mode_cmd_t\":\"~" MQTT_CLIMATE "/" MQTT_CLIMATE_CMND "/" KEY_MODE "\"," + "\"mode_stat_t\":\"~" MQTT_CLIMATE "/" MQTT_CLIMATE_STAT "/" KEY_MODE + "\"," + "\"modes\":[\"off\",\"auto\",\"cool\",\"heat\",\"dry\",\"fan_only\"]," + "\"temp_cmd_t\":\"~" MQTT_CLIMATE "/" MQTT_CLIMATE_CMND "/" KEY_TEMP "\"," + "\"temp_stat_t\":\"~" MQTT_CLIMATE "/" MQTT_CLIMATE_STAT "/" KEY_TEMP + "\"," + "\"min_temp\":\"16\"," + "\"max_temp\":\"30\"," + "\"temp_step\":\"1\"," + "\"fan_mode_cmd_t\":\"~" MQTT_CLIMATE "/" MQTT_CLIMATE_CMND "/" + KEY_FANSPEED "\"," + "\"fan_mode_stat_t\":\"~" MQTT_CLIMATE "/" MQTT_CLIMATE_STAT "/" + KEY_FANSPEED "\"," + "\"fan_modes\":[\"auto\",\"min\",\"low\",\"medium\",\"high\",\"max\"]," + "\"swing_mode_cmd_t\":\"~" MQTT_CLIMATE "/" MQTT_CLIMATE_CMND "/" + KEY_SWINGV "\"," + "\"swing_mode_stat_t\":\"~" MQTT_CLIMATE "/" MQTT_CLIMATE_STAT "/" + KEY_SWINGV "\"," + "\"swing_modes\":[" + "\"off\",\"auto\",\"highest\",\"high\",\"middle\",\"low\",\"lowest\"" + "]" + "}").c_str())) { + mqttLog("MQTT climate discovery successful sent."); + hasDiscoveryBeenSent = true; + lastDiscovery.reset(); + mqttSentCounter++; + } else { + mqttLog("MQTT climate discovery FAILED to send."); + } +} +#endif // MQTT_ENABLE + +void loop(void) { + server.handleClient(); // Handle any web activity + +#if MQTT_ENABLE + uint32_t now = millis(); + // MQTT client connection management + if (!mqtt_client.connected()) { + if (wasConnected) { + lastDisconnectedTime = now; + wasConnected = false; + mqttDisconnectCounter++; + } + // Reconnect if it's longer than kMqttReconnectTime since we last tried. + if (now - lastReconnectAttempt > kMqttReconnectTime) { + lastReconnectAttempt = now; + debug("client mqtt not connected, trying to connect"); + // Attempt to reconnect + if (reconnect()) { + lastReconnectAttempt = 0; + wasConnected = true; + if (boot) { + mqttLog("IR Server just booted"); + boot = false; + } else { + mqttLog("IR Server just (re)connected to MQTT. " + "Lost connection about " + timeSince(lastConnectedTime)); + } + lastConnectedTime = now; + debug("successful client mqtt connection"); + if (lockMqttBroadcast) { + // Attempt to fetch back any Climate state stored in MQTT retained + // messages on the MQTT broker. + mqttLog("Started listening for previous state."); + climate_prev = climate; // Make a copy so we can compare afterwards. + subscribing(MqttClimateStat + '+'); + statListenTime.reset(); + } + } + } + } else { + // MQTT loop + lastConnectedTime = now; + mqtt_client.loop(); + if (lockMqttBroadcast && statListenTime.elapsed() > kStatListenPeriodMs) { + unsubscribing(MqttClimateStat + '+'); + mqttLog("Finished listening for previous state."); + if (cmpClimate(climate, climate_prev)) { // Something changed. + mqttLog("The state was recovered from MQTT broker. Updating."); + sendClimate(climate_prev, climate, MqttClimateStat, + true, false, false); + } + lockMqttBroadcast = false; // Release the lock so we can broadcast again. + } + // Periodically send all of the climate state via MQTT. + doBroadcast(&lastBroadcast, kBroadcastPeriodMs, climate, false, false); + } +#endif // MQTT_ENABLE +#ifdef IR_RX + // Check if an IR code has been received via the IR RX module. +#if REPORT_UNKNOWNS + if (irrecv.decode(&capture)) { +#else // REPORT_UNKNOWNS + if (irrecv.decode(&capture) && capture.decode_type != UNKNOWN) { +#endif // REPORT_UNKNOWNS + lastIrReceivedTime = millis(); + lastIrReceived = String(capture.decode_type) + "," + + resultToHexidecimal(&capture); +#if REPORT_RAW_UNKNOWNS + if (capture.decode_type == UNKNOWN) { + lastIrReceived += ";"; + for (uint16_t i = 1; i < capture.rawlen; i++) { + uint32_t usecs; + for (usecs = capture.rawbuf[i] * kRawTick; usecs > UINT16_MAX; + usecs -= UINT16_MAX) { + lastIrReceived += uint64ToString(UINT16_MAX); + lastIrReceived += ",0,"; + } + lastIrReceived += uint64ToString(usecs, 10); + if (i < capture.rawlen - 1) + lastIrReceived += ","; + } + } +#endif // REPORT_RAW_UNKNOWNS + // If it isn't an AC code, add the bits. + if (!hasACState(capture.decode_type)) + lastIrReceived += "," + String(capture.bits); +#if MQTT_ENABLE + mqtt_client.publish(MqttRecv.c_str(), lastIrReceived.c_str()); + mqttSentCounter++; +#endif // MQTT_ENABLE + irRecvCounter++; + debug("Incoming IR message sent to MQTT:"); + debug(lastIrReceived.c_str()); + } +#endif // IR_RX + delay(100); +} + +// Arduino framework doesn't support strtoull(), so make our own one. +uint64_t getUInt64fromHex(char const *str) { + uint64_t result = 0; + uint16_t offset = 0; + // Skip any leading '0x' or '0X' prefix. + if (str[0] == '0' && (str[1] == 'x' || str[1] == 'X')) + offset = 2; + for (; isxdigit((unsigned char)str[offset]); offset++) { + char c = str[offset]; + result *= 16; + if (isdigit(c)) /* '0' .. '9' */ + result += c - '0'; + else if (isupper(c)) /* 'A' .. 'F' */ + result += c - 'A' + 10; + else /* 'a' .. 'f'*/ + result += c - 'a' + 10; + } + return result; +} + +// Transmit the given IR message. +// +// Args: +// irsend: A pointer to a IRsend object to transmit via. +// ir_type: enum of the protocol to be sent. +// code: Numeric payload of the IR message. Most protocols use this. +// code_str: The unparsed code to be sent. Used by complex protocol encodings. +// bits: Nr. of bits in the protocol. 0 means use the protocol's default. +// repeat: Nr. of times the message is to be repeated. (Not all protcols.) +// Returns: +// bool: Successfully sent or not. +bool sendIRCode(IRsend *irsend, int const ir_type, + uint64_t const code, char const * code_str, uint16_t bits, + uint16_t repeat) { + // Create a pseudo-lock so we don't try to send two codes at the same time. + while (lockIr) + delay(20); + lockIr = true; + + bool success = true; // Assume success. + + // send the IR message. + switch (ir_type) { +#if SEND_RC5 + case RC5: // 1 + if (bits == 0) + bits = kRC5Bits; + irsend->sendRC5(code, bits, repeat); + break; +#endif +#if SEND_RC6 + case RC6: // 2 + if (bits == 0) + bits = kRC6Mode0Bits; + irsend->sendRC6(code, bits, repeat); + break; +#endif +#if SEND_NEC + case NEC: // 3 + if (bits == 0) + bits = kNECBits; + irsend->sendNEC(code, bits, repeat); + break; +#endif +#if SEND_SONY + case SONY: // 4 + if (bits == 0) + bits = kSony12Bits; + repeat = std::max(repeat, kSonyMinRepeat); + irsend->sendSony(code, bits, repeat); + break; +#endif +#if SEND_PANASONIC + case PANASONIC: // 5 + if (bits == 0) + bits = kPanasonicBits; + irsend->sendPanasonic64(code, bits, repeat); + break; +#endif +#if SEND_JVC + case JVC: // 6 + if (bits == 0) + bits = kJvcBits; + irsend->sendJVC(code, bits, repeat); + break; +#endif +#if SEND_SAMSUNG + case SAMSUNG: // 7 + if (bits == 0) + bits = kSamsungBits; + irsend->sendSAMSUNG(code, bits, repeat); + break; +#endif +#if SEND_SAMSUNG36 + case SAMSUNG36: // 56 + if (bits == 0) + bits = kSamsung36Bits; + irsend->sendSamsung36(code, bits, repeat); + break; +#endif +#if SEND_WHYNTER + case WHYNTER: // 8 + if (bits == 0) + bits = kWhynterBits; + irsend->sendWhynter(code, bits, repeat); + break; +#endif +#if SEND_AIWA_RC_T501 + case AIWA_RC_T501: // 9 + if (bits == 0) + bits = kAiwaRcT501Bits; + repeat = std::max(repeat, kAiwaRcT501MinRepeats); + irsend->sendAiwaRCT501(code, bits, repeat); + break; +#endif +#if SEND_LG + case LG: // 10 + if (bits == 0) + bits = kLgBits; + irsend->sendLG(code, bits, repeat); + break; +#endif +#if SEND_MITSUBISHI + case MITSUBISHI: // 12 + if (bits == 0) + bits = kMitsubishiBits; + repeat = std::max(repeat, kMitsubishiMinRepeat); + irsend->sendMitsubishi(code, bits, repeat); + break; +#endif +#if SEND_DISH + case DISH: // 13 + if (bits == 0) + bits = kDishBits; + repeat = std::max(repeat, kDishMinRepeat); + irsend->sendDISH(code, bits, repeat); + break; +#endif +#if SEND_SHARP + case SHARP: // 14 + if (bits == 0) + bits = kSharpBits; + irsend->sendSharpRaw(code, bits, repeat); + break; +#endif +#if SEND_COOLIX + case COOLIX: // 15 + if (bits == 0) + bits = kCoolixBits; + irsend->sendCOOLIX(code, bits, repeat); + break; +#endif + case DAIKIN: // 16 + case DAIKIN2: // 53 + case DAIKIN216: // 61 + case KELVINATOR: // 18 + case MITSUBISHI_AC: // 20 + case GREE: // 24 + case ARGO: // 27 + case TROTEC: // 28 + case TOSHIBA_AC: // 32 + case FUJITSU_AC: // 33 + case HAIER_AC: // 38 + case HAIER_AC_YRW02: // 44 + case HITACHI_AC: // 40 + case HITACHI_AC1: // 41 + case HITACHI_AC2: // 42 + case WHIRLPOOL_AC: // 45 + case SAMSUNG_AC: // 46 + case ELECTRA_AC: // 48 + case PANASONIC_AC: // 49 + case MWM: // 52 + success = parseStringAndSendAirCon(irsend, ir_type, code_str); + break; +#if SEND_DENON + case DENON: // 17 + if (bits == 0) + bits = DENON_BITS; + irsend->sendDenon(code, bits, repeat); + break; +#endif +#if SEND_SHERWOOD + case SHERWOOD: // 19 + if (bits == 0) + bits = kSherwoodBits; + repeat = std::max(repeat, kSherwoodMinRepeat); + irsend->sendSherwood(code, bits, repeat); + break; +#endif +#if SEND_RCMM + case RCMM: // 21 + if (bits == 0) + bits = kRCMMBits; + irsend->sendRCMM(code, bits, repeat); + break; +#endif +#if SEND_SANYO + case SANYO_LC7461: // 22 + if (bits == 0) + bits = kSanyoLC7461Bits; + irsend->sendSanyoLC7461(code, bits, repeat); + break; +#endif +#if SEND_RC5 + case RC5X: // 23 + if (bits == 0) + bits = kRC5XBits; + irsend->sendRC5(code, bits, repeat); + break; +#endif +#if SEND_PRONTO + case PRONTO: // 25 + success = parseStringAndSendPronto(irsend, code_str, repeat); + break; +#endif +#if SEND_NIKAI + case NIKAI: // 29 + if (bits == 0) + bits = kNikaiBits; + irsend->sendNikai(code, bits, repeat); + break; +#endif +#if SEND_RAW + case RAW: // 30 + success = parseStringAndSendRaw(irsend, code_str); + break; +#endif +#if SEND_GLOBALCACHE + case GLOBALCACHE: // 31 + success = parseStringAndSendGC(irsend, code_str); + break; +#endif +#if SEND_MIDEA + case MIDEA: // 34 + if (bits == 0) + bits = kMideaBits; + irsend->sendMidea(code, bits, repeat); + break; +#endif +#if SEND_MAGIQUEST + case MAGIQUEST: // 35 + if (bits == 0) + bits = kMagiquestBits; + irsend->sendMagiQuest(code, bits, repeat); + break; +#endif +#if SEND_LASERTAG + case LASERTAG: // 36 + if (bits == 0) + bits = kLasertagBits; + irsend->sendLasertag(code, bits, repeat); + break; +#endif +#if SEND_CARRIER_AC + case CARRIER_AC: // 37 + if (bits == 0) + bits = kCarrierAcBits; + irsend->sendCarrierAC(code, bits, repeat); + break; +#endif +#if SEND_MITSUBISHI2 + case MITSUBISHI2: // 39 + if (bits == 0) + bits = kMitsubishiBits; + repeat = std::max(repeat, kMitsubishiMinRepeat); + irsend->sendMitsubishi2(code, bits, repeat); + break; +#endif +#if SEND_GICABLE + case GICABLE: // 43 + if (bits == 0) + bits = kGicableBits; + repeat = std::max(repeat, kGicableMinRepeat); + irsend->sendGICable(code, bits, repeat); + break; +#endif +#if SEND_LUTRON + case LUTRON: // 47 + if (bits == 0) + bits = kLutronBits; + irsend->sendLutron(code, bits, repeat); + break; +#endif +#if SEND_PIONEER + case PIONEER: // 50 + if (bits == 0) + bits = kPioneerBits; + irsend->sendPioneer(code, bits, repeat); + break; +#endif +#if SEND_LG + case LG2: // 51 + if (bits == 0) + bits = kLgBits; + irsend->sendLG2(code, bits, repeat); + break; +#endif +#if SEND_VESTEL_AC + case VESTEL_AC: // 54 + if (bits == 0) + bits = kVestelAcBits; + irsend->sendVestelAc(code, bits, repeat); + break; +#endif +#if SEND_TECO + case TECO: // 55 + if (bits == 0) + bits = kTecoBits; + irsend->sendTeco(code, bits, repeat); + break; +#endif +#if SEND_LEGOPF + case LEGOPF: // 58 + if (bits == 0) + bits = kLegoPfBits; + irsend->sendLegoPf(code, bits, repeat); + break; +#endif + default: + // If we got here, we didn't know how to send it. + success = false; + } + lastSendTime = millis(); + // Release the lock. + lockIr = false; + + // Indicate that we sent the message or not. + if (success) { + sendReqCounter++; + debug("Sent the IR message:"); + } else { + debug("Failed to send IR Message:"); + } + debug("Type:"); + debug(String(ir_type).c_str()); + // For "long" codes we basically repeat what we got. + if (hasACState((decode_type_t) ir_type) || + ir_type == PRONTO || + ir_type == RAW || + ir_type == GLOBALCACHE) { + debug("Code: "); + debug(code_str); + // Confirm what we were asked to send was sent. +#if MQTT_ENABLE + if (success) { + if (ir_type == PRONTO && repeat > 0) + mqtt_client.publish(MqttAck.c_str(), (String(ir_type) + ",R" + + String(repeat) + "," + + String(code_str)).c_str()); + else + mqtt_client.publish(MqttAck.c_str(), (String(ir_type) + "," + + String(code_str)).c_str()); + mqttSentCounter++; + } +#endif // MQTT_ENABLE + } else { // For "short" codes, we break it down a bit more before we report. + debug(("Code: 0x" + uint64ToString(code, 16)).c_str()); + debug(("Bits: " + String(bits)).c_str()); + debug(("Repeats: " + String(repeat)).c_str()); +#if MQTT_ENABLE + if (success) { + mqtt_client.publish(MqttAck.c_str(), (String(ir_type) + "," + + uint64ToString(code, 16) + + "," + String(bits) + "," + + String(repeat)).c_str()); + mqttSentCounter++; + } +#endif // MQTT_ENABLE + } + return success; +} + +bool sendInt(const String topic, const int32_t num, const bool retain) { +#if MQTT_ENABLE + mqttSentCounter++; + return mqtt_client.publish(topic.c_str(), String(num).c_str(), retain); +#else // MQTT_ENABLE + return true; +#endif // MQTT_ENABLE +} + +bool sendBool(const String topic, const bool on, const bool retain) { +#if MQTT_ENABLE + mqttSentCounter++; + return mqtt_client.publish(topic.c_str(), (on ? "on" : "off"), retain); +#else // MQTT_ENABLE + return true; +#endif // MQTT_ENABLE +} + +bool sendString(const String topic, const String str, const bool retain) { +#if MQTT_ENABLE + mqttSentCounter++; + return mqtt_client.publish(topic.c_str(), str.c_str(), retain); +#else // MQTT_ENABLE + return true; +#endif // MQTT_ENABLE +} + +bool sendFloat(const String topic, const float_t temp, const bool retain) { +#if MQTT_ENABLE + mqttSentCounter++; + return mqtt_client.publish(topic.c_str(), String(temp, 1).c_str(), retain); +#else // MQTT_ENABLE + return true; +#endif // MQTT_ENABLE +} + +commonAcState_t updateClimate(commonAcState_t current, const String str, + const String prefix, const String payload) { + commonAcState_t result = current; + String value = payload; + value.toUpperCase(); + if (str.equals(prefix + KEY_PROTOCOL)) + result.protocol = strToDecodeType(value.c_str()); + else if (str.equals(prefix + KEY_MODEL)) + result.model = IRac::strToModel(value.c_str()); + else if (str.equals(prefix + KEY_POWER)) + result.power = IRac::strToBool(value.c_str()); + else if (str.equals(prefix + KEY_MODE)) + result.mode = IRac::strToOpmode(value.c_str()); + else if (str.equals(prefix + KEY_TEMP)) + result.degrees = value.toFloat(); + else if (str.equals(prefix + KEY_FANSPEED)) + result.fanspeed = IRac::strToFanspeed(value.c_str()); + else if (str.equals(prefix + KEY_SWINGV)) + result.swingv = IRac::strToSwingV(value.c_str()); + else if (str.equals(prefix + KEY_SWINGH)) + result.swingh = IRac::strToSwingH(value.c_str()); + else if (str.equals(prefix + KEY_QUIET)) + result.quiet = IRac::strToBool(value.c_str()); + else if (str.equals(prefix + KEY_TURBO)) + result.turbo = IRac::strToBool(value.c_str()); + else if (str.equals(prefix + KEY_ECONO)) + result.econo = IRac::strToBool(value.c_str()); + else if (str.equals(prefix + KEY_LIGHT)) + result.light = IRac::strToBool(value.c_str()); + else if (str.equals(prefix + KEY_BEEP)) + result.beep = IRac::strToBool(value.c_str()); + else if (str.equals(prefix + KEY_FILTER)) + result.filter = IRac::strToBool(value.c_str()); + else if (str.equals(prefix + KEY_CLEAN)) + result.clean = IRac::strToBool(value.c_str()); + else if (str.equals(prefix + KEY_SLEEP)) + result.sleep = value.toInt(); + else if (str.equals(prefix + KEY_CLOCK)) + result.clock = value.toInt(); + return result; +} + +// Compare two AirCon states (climates). +// Returns: True if they differ, False if they don't. +bool cmpClimate(const commonAcState_t a, const commonAcState_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; +} + +bool sendClimate(const commonAcState_t prev, const commonAcState_t next, + const String topic_prefix, const bool retain, + const bool forceMQTT, const bool forceIR) { + bool diff = false; + bool success = true; + + if (prev.protocol != next.protocol || forceMQTT) { + diff = true; + success &= sendString(topic_prefix + KEY_PROTOCOL, + typeToString(next.protocol), retain); + } + if (prev.model != next.model || forceMQTT) { + diff = true; + success &= sendInt(topic_prefix + KEY_MODEL, next.model, retain); + } + if (prev.power != next.power || prev.mode != next.mode || forceMQTT) { + diff = true; + success &= sendBool(topic_prefix + KEY_POWER, next.power, retain); + success &= sendString(topic_prefix + KEY_MODE, + (next.power ? opmodeToString(next.mode) : F("off")), + retain); + } + if (prev.degrees != next.degrees || forceMQTT) { + diff = true; + success &= sendFloat(topic_prefix + KEY_TEMP, next.degrees, retain); + } + if (prev.celsius != next.celsius || forceMQTT) { + diff = true; + success &= sendBool(topic_prefix + KEY_CELSIUS, next.celsius, retain); + } + if (prev.fanspeed != next.fanspeed || forceMQTT) { + diff = true; + success &= sendString(topic_prefix + KEY_FANSPEED, + fanspeedToString(next.fanspeed), retain); + } + if (prev.swingv != next.swingv || forceMQTT) { + diff = true; + success &= sendString(topic_prefix + KEY_SWINGV, + swingvToString(next.swingv), retain); + } + if (prev.swingh != next.swingh || forceMQTT) { + diff = true; + success &= sendString(topic_prefix + KEY_SWINGH, + swinghToString(next.swingh), retain); + } + if (prev.quiet != next.quiet || forceMQTT) { + diff = true; + success &= sendBool(topic_prefix + KEY_QUIET, next.quiet, retain); + } + if (prev.turbo != next.turbo || forceMQTT) { + diff = true; + success &= sendBool(topic_prefix + KEY_TURBO, next.turbo, retain); + } + if (prev.econo != next.econo || forceMQTT) { + diff = true; + success &= sendBool(topic_prefix + KEY_ECONO, next.econo, retain); + } + if (prev.light != next.light || forceMQTT) { + diff = true; + success &= sendBool(topic_prefix + KEY_LIGHT, next.light, retain); + } + if (prev.filter != next.filter || forceMQTT) { + diff = true; + success &= sendBool(topic_prefix + KEY_FILTER, next.filter, retain); + } + if (prev.clean != next.clean || forceMQTT) { + diff = true; + success &= sendBool(topic_prefix + KEY_CLEAN, next.clean, retain); + } + if (prev.beep != next.beep || forceMQTT) { + diff = true; + success &= sendBool(topic_prefix + KEY_BEEP, next.beep, retain); + } + if (prev.sleep != next.sleep || forceMQTT) { + diff = true; + success &= sendInt(topic_prefix + KEY_SLEEP, next.sleep, retain); + } + if (diff && !forceMQTT) + debug("Difference in common A/C state detected."); + else + debug("NO difference in common A/C state detected."); + // Only send an IR message if we need to. + if ((diff && !forceMQTT) || forceIR) { + debug("Sending common A/C state via IR."); + lastClimateSucceeded = commonAc.sendAc( + next.protocol, next.model, next.power, next.mode, + next.degrees, next.celsius, next.fanspeed, next.swingv, next.swingh, + next.quiet, next.turbo, next.econo, next.light, next.filter, next.clean, + next.beep, next.sleep, -1); + if (lastClimateSucceeded) hasClimateBeenSent = true; + success &= lastClimateSucceeded; + lastClimateIr.reset(); + irClimateCounter++; + sendReqCounter++; + } + return success; +} diff --git a/lib/IRremoteESP8266-2.5.2.03/examples/IRMQTTServer/platformio.ini b/lib/IRremoteESP8266-2.6.0/examples/IRMQTTServer/platformio.ini similarity index 55% rename from lib/IRremoteESP8266-2.5.2.03/examples/IRMQTTServer/platformio.ini rename to lib/IRremoteESP8266-2.6.0/examples/IRMQTTServer/platformio.ini index 27b44ddca5fd..243b36a99fad 100644 --- a/lib/IRremoteESP8266-2.5.2.03/examples/IRMQTTServer/platformio.ini +++ b/lib/IRremoteESP8266-2.6.0/examples/IRMQTTServer/platformio.ini @@ -3,16 +3,19 @@ lib_extra_dirs = ../../ src_dir=. [common] -build_flags = -DMQTT_MAX_PACKET_SIZE=512 +build_flags = -DMQTT_MAX_PACKET_SIZE=768 +lib_ldf_mode = chain+ lib_deps_builtin = lib_deps_external = PubSubClient WifiManager@0.14 + ArduinoJson [env:nodemcuv2] platform = espressif8266 framework = arduino board = nodemcuv2 +lib_ldf_mode = ${common.lib_ldf_mode} build_flags = ${common.build_flags} lib_deps = ${common.lib_deps_builtin} @@ -22,7 +25,18 @@ lib_deps = platform=espressif8266 framework=arduino board=d1_mini +lib_ldf_mode = ${common.lib_ldf_mode} build_flags = ${common.build_flags} lib_deps = ${common.lib_deps_builtin} ${common.lib_deps_external} + +[env:d1_mini_no_mqtt] +platform=espressif8266 +framework=arduino +board=d1_mini +lib_ldf_mode = ${common.lib_ldf_mode} +build_flags = ${common.build_flags} -DMQTT_ENABLE=false +lib_deps = + ${common.lib_deps_builtin} + ${common.lib_deps_external} diff --git a/lib/IRremoteESP8266-2.5.2.03/examples/IRServer/IRServer.ino b/lib/IRremoteESP8266-2.6.0/examples/IRServer/IRServer.ino similarity index 100% rename from lib/IRremoteESP8266-2.5.2.03/examples/IRServer/IRServer.ino rename to lib/IRremoteESP8266-2.6.0/examples/IRServer/IRServer.ino diff --git a/lib/IRremoteESP8266-2.5.2.03/examples/IRGCSendDemo/platformio.ini b/lib/IRremoteESP8266-2.6.0/examples/IRServer/platformio.ini similarity index 83% rename from lib/IRremoteESP8266-2.5.2.03/examples/IRGCSendDemo/platformio.ini rename to lib/IRremoteESP8266-2.6.0/examples/IRServer/platformio.ini index eeb8d1f2eb93..ec84f22f3b05 100644 --- a/lib/IRremoteESP8266-2.5.2.03/examples/IRGCSendDemo/platformio.ini +++ b/lib/IRremoteESP8266-2.6.0/examples/IRServer/platformio.ini @@ -6,11 +6,13 @@ src_dir=. build_flags = lib_deps_builtin = lib_deps_external = +lib_ldf_mode = chain+ [env:nodemcuv2] platform = espressif8266 framework = arduino board = nodemcuv2 +lib_ldf_mode = ${common.lib_ldf_mode} build_flags = ${common.build_flags} lib_deps = ${common.lib_deps_builtin} diff --git a/lib/IRremoteESP8266-2.5.2.03/examples/IRrecvDemo/IRrecvDemo.ino b/lib/IRremoteESP8266-2.6.0/examples/IRrecvDemo/IRrecvDemo.ino similarity index 100% rename from lib/IRremoteESP8266-2.5.2.03/examples/IRrecvDemo/IRrecvDemo.ino rename to lib/IRremoteESP8266-2.6.0/examples/IRrecvDemo/IRrecvDemo.ino diff --git a/lib/IRremoteESP8266-2.6.0/examples/IRrecvDemo/platformio.ini b/lib/IRremoteESP8266-2.6.0/examples/IRrecvDemo/platformio.ini new file mode 100644 index 000000000000..ec84f22f3b05 --- /dev/null +++ b/lib/IRremoteESP8266-2.6.0/examples/IRrecvDemo/platformio.ini @@ -0,0 +1,19 @@ +[platformio] +lib_extra_dirs = ../../ +src_dir=. + +[common] +build_flags = +lib_deps_builtin = +lib_deps_external = +lib_ldf_mode = chain+ + +[env:nodemcuv2] +platform = espressif8266 +framework = arduino +board = nodemcuv2 +lib_ldf_mode = ${common.lib_ldf_mode} +build_flags = ${common.build_flags} +lib_deps = + ${common.lib_deps_builtin} + ${common.lib_deps_external} diff --git a/lib/IRremoteESP8266-2.5.2.03/examples/IRrecvDump/IRrecvDump.ino b/lib/IRremoteESP8266-2.6.0/examples/IRrecvDump/IRrecvDump.ino similarity index 100% rename from lib/IRremoteESP8266-2.5.2.03/examples/IRrecvDump/IRrecvDump.ino rename to lib/IRremoteESP8266-2.6.0/examples/IRrecvDump/IRrecvDump.ino diff --git a/lib/IRremoteESP8266-2.6.0/examples/IRrecvDump/platformio.ini b/lib/IRremoteESP8266-2.6.0/examples/IRrecvDump/platformio.ini new file mode 100644 index 000000000000..ec84f22f3b05 --- /dev/null +++ b/lib/IRremoteESP8266-2.6.0/examples/IRrecvDump/platformio.ini @@ -0,0 +1,19 @@ +[platformio] +lib_extra_dirs = ../../ +src_dir=. + +[common] +build_flags = +lib_deps_builtin = +lib_deps_external = +lib_ldf_mode = chain+ + +[env:nodemcuv2] +platform = espressif8266 +framework = arduino +board = nodemcuv2 +lib_ldf_mode = ${common.lib_ldf_mode} +build_flags = ${common.build_flags} +lib_deps = + ${common.lib_deps_builtin} + ${common.lib_deps_external} diff --git a/lib/IRremoteESP8266-2.5.2.03/examples/IRrecvDumpV2/IRrecvDumpV2.ino b/lib/IRremoteESP8266-2.6.0/examples/IRrecvDumpV2/IRrecvDumpV2.ino similarity index 84% rename from lib/IRremoteESP8266-2.5.2.03/examples/IRrecvDumpV2/IRrecvDumpV2.ino rename to lib/IRremoteESP8266-2.6.0/examples/IRrecvDumpV2/IRrecvDumpV2.ino index d72e0814ce45..2dee0597c17a 100644 --- a/lib/IRremoteESP8266-2.5.2.03/examples/IRrecvDumpV2/IRrecvDumpV2.ino +++ b/lib/IRremoteESP8266-2.6.0/examples/IRrecvDumpV2/IRrecvDumpV2.ino @@ -35,9 +35,15 @@ #include #include #include +#include #include #include +#include +#include #include +#include +#include + // ==================== start of TUNEABLE PARAMETERS ==================== // An IR detector/demodulator is connected to GPIO pin 14 @@ -121,6 +127,20 @@ void dumpACInfo(decode_results *results) { description = ac.toString(); } #endif // DECODE_DAIKIN +#if DECODE_DAIKIN2 + if (results->decode_type == DAIKIN2) { + IRDaikin2 ac(0); + ac.setRaw(results->state); + description = ac.toString(); + } +#endif // DECODE_DAIKIN2 +#if DECODE_DAIKIN216 + if (results->decode_type == DAIKIN216) { + IRDaikin216 ac(0); + ac.setRaw(results->state); + description = ac.toString(); + } +#endif // DECODE_DAIKIN216 #if DECODE_FUJITSU_AC if (results->decode_type == FUJITSU_AC) { IRFujitsuAC ac(0); @@ -142,6 +162,18 @@ void dumpACInfo(decode_results *results) { description = ac.toString(); } #endif // DECODE_MITSUBISHI_AC +#if DECODE_MITSUBISHIHEAVY + if (results->decode_type == MITSUBISHI_HEAVY_88) { + IRMitsubishiHeavy88Ac ac(0); + ac.setRaw(results->state); + description = ac.toString(); + } + if (results->decode_type == MITSUBISHI_HEAVY_152) { + IRMitsubishiHeavy152Ac ac(0); + ac.setRaw(results->state); + description = ac.toString(); + } +#endif // DECODE_MITSUBISHIHEAVY #if DECODE_TOSHIBA_AC if (results->decode_type == TOSHIBA_AC) { IRToshibaAC ac(0); @@ -180,7 +212,7 @@ void dumpACInfo(decode_results *results) { #if DECODE_SAMSUNG_AC if (results->decode_type == SAMSUNG_AC) { IRSamsungAc ac(0); - ac.setRaw(results->state); + ac.setRaw(results->state, results->bits / 8); description = ac.toString(); } #endif // DECODE_SAMSUNG_AC @@ -206,6 +238,34 @@ void dumpACInfo(decode_results *results) { description = ac.toString(); } #endif // DECODE_HITACHI_AC +#if DECODE_WHIRLPOOL_AC + if (results->decode_type == WHIRLPOOL_AC) { + IRWhirlpoolAc ac(0); + ac.setRaw(results->state); + description = ac.toString(); + } +#endif // DECODE_WHIRLPOOL_AC +#if DECODE_VESTEL_AC + if (results->decode_type == VESTEL_AC) { + IRVestelAc ac(0); + ac.setRaw(results->value); // Like Coolix, use value instead of state. + description = ac.toString(); + } +#endif // DECODE_VESTEL_AC +#if DECODE_TECO + if (results->decode_type == TECO) { + IRTecoAc ac(0); + ac.setRaw(results->value); // Like Coolix, use value instead of state. + description = ac.toString(); + } +#endif // DECODE_TECO +#if DECODE_TCL112AC + if (results->decode_type == TCL112AC) { + IRTcl112Ac ac(0); + ac.setRaw(results->state); + description = ac.toString(); + } +#endif // DECODE_TCL112AC // If we got a human-readable description of the message, display it. if (description != "") Serial.println("Mesg Desc.: " + description); } diff --git a/lib/IRremoteESP8266-2.6.0/examples/IRrecvDumpV2/platformio.ini b/lib/IRremoteESP8266-2.6.0/examples/IRrecvDumpV2/platformio.ini new file mode 100644 index 000000000000..ec84f22f3b05 --- /dev/null +++ b/lib/IRremoteESP8266-2.6.0/examples/IRrecvDumpV2/platformio.ini @@ -0,0 +1,19 @@ +[platformio] +lib_extra_dirs = ../../ +src_dir=. + +[common] +build_flags = +lib_deps_builtin = +lib_deps_external = +lib_ldf_mode = chain+ + +[env:nodemcuv2] +platform = espressif8266 +framework = arduino +board = nodemcuv2 +lib_ldf_mode = ${common.lib_ldf_mode} +build_flags = ${common.build_flags} +lib_deps = + ${common.lib_deps_builtin} + ${common.lib_deps_external} diff --git a/lib/IRremoteESP8266-2.5.2.03/examples/IRsendDemo/IRsendDemo.ino b/lib/IRremoteESP8266-2.6.0/examples/IRsendDemo/IRsendDemo.ino similarity index 85% rename from lib/IRremoteESP8266-2.5.2.03/examples/IRsendDemo/IRsendDemo.ino rename to lib/IRremoteESP8266-2.6.0/examples/IRsendDemo/IRsendDemo.ino index 892016b3eec1..19f118671e8c 100644 --- a/lib/IRremoteESP8266-2.5.2.03/examples/IRsendDemo/IRsendDemo.ino +++ b/lib/IRremoteESP8266-2.6.0/examples/IRsendDemo/IRsendDemo.ino @@ -1,6 +1,6 @@ /* IRremoteESP8266: IRsendDemo - demonstrates sending IR codes with IRsend. * - * Version 1.0 April, 2017 + * Version 1.1 January, 2019 * Based on Ken Shirriff's IrsendDemo Version 0.1 July, 2009, * Copyright 2009 Ken Shirriff, http://arcfn.com * @@ -46,6 +46,10 @@ uint16_t rawData[67] = {9000, 4500, 650, 550, 650, 1650, 600, 550, 650, 550, 650, 550, 650, 550, 600, 550, 650, 550, 650, 550, 650, 1650, 600, 550, 650, 1650, 650, 1650, 650, 1650, 650, 1650, 650, 1650, 650, 1650, 600}; +// Example Samsung A/C state captured from IRrecvDumpV2.ino +uint8_t samsungState[kSamsungAcStateLength] = { + 0x02, 0x92, 0x0F, 0x00, 0x00, 0x00, 0xF0, + 0x01, 0xE2, 0xFE, 0x71, 0x40, 0x11, 0xF0}; void setup() { irsend.begin(); @@ -53,19 +57,16 @@ void setup() { } void loop() { -#if SEND_NEC Serial.println("NEC"); - irsend.sendNEC(0x00FFE01FUL, 32); -#endif // SEND_NEC + irsend.sendNEC(0x00FFE01FUL); delay(2000); -#if SEND_SONY Serial.println("Sony"); - irsend.sendSony(0xa90, 12, 2); -#endif // SEND_SONY + irsend.sendSony(0xa90, 12, 2); // 12 bits & 2 repeats delay(2000); -#if SEND_RAW Serial.println("a rawData capture from IRrecvDumpV2"); irsend.sendRaw(rawData, 67, 38); // Send a raw data capture at 38kHz. -#endif // SEND_RAW + delay(2000); + Serial.println("a Samsung A/C state from IRrecvDumpV2"); + irsend.sendSamsungAC(samsungState); delay(2000); } diff --git a/lib/IRremoteESP8266-2.6.0/examples/IRsendDemo/platformio.ini b/lib/IRremoteESP8266-2.6.0/examples/IRsendDemo/platformio.ini new file mode 100644 index 000000000000..ec84f22f3b05 --- /dev/null +++ b/lib/IRremoteESP8266-2.6.0/examples/IRsendDemo/platformio.ini @@ -0,0 +1,19 @@ +[platformio] +lib_extra_dirs = ../../ +src_dir=. + +[common] +build_flags = +lib_deps_builtin = +lib_deps_external = +lib_ldf_mode = chain+ + +[env:nodemcuv2] +platform = espressif8266 +framework = arduino +board = nodemcuv2 +lib_ldf_mode = ${common.lib_ldf_mode} +build_flags = ${common.build_flags} +lib_deps = + ${common.lib_deps_builtin} + ${common.lib_deps_external} diff --git a/lib/IRremoteESP8266-2.5.2.03/examples/IRsendProntoDemo/IRsendProntoDemo.ino b/lib/IRremoteESP8266-2.6.0/examples/IRsendProntoDemo/IRsendProntoDemo.ino similarity index 100% rename from lib/IRremoteESP8266-2.5.2.03/examples/IRsendProntoDemo/IRsendProntoDemo.ino rename to lib/IRremoteESP8266-2.6.0/examples/IRsendProntoDemo/IRsendProntoDemo.ino diff --git a/lib/IRremoteESP8266-2.6.0/examples/IRsendProntoDemo/platformio.ini b/lib/IRremoteESP8266-2.6.0/examples/IRsendProntoDemo/platformio.ini new file mode 100644 index 000000000000..ec84f22f3b05 --- /dev/null +++ b/lib/IRremoteESP8266-2.6.0/examples/IRsendProntoDemo/platformio.ini @@ -0,0 +1,19 @@ +[platformio] +lib_extra_dirs = ../../ +src_dir=. + +[common] +build_flags = +lib_deps_builtin = +lib_deps_external = +lib_ldf_mode = chain+ + +[env:nodemcuv2] +platform = espressif8266 +framework = arduino +board = nodemcuv2 +lib_ldf_mode = ${common.lib_ldf_mode} +build_flags = ${common.build_flags} +lib_deps = + ${common.lib_deps_builtin} + ${common.lib_deps_external} diff --git a/lib/IRremoteESP8266-2.5.2.03/examples/JVCPanasonicSendDemo/JVCPanasonicSendDemo.ino b/lib/IRremoteESP8266-2.6.0/examples/JVCPanasonicSendDemo/JVCPanasonicSendDemo.ino similarity index 100% rename from lib/IRremoteESP8266-2.5.2.03/examples/JVCPanasonicSendDemo/JVCPanasonicSendDemo.ino rename to lib/IRremoteESP8266-2.6.0/examples/JVCPanasonicSendDemo/JVCPanasonicSendDemo.ino diff --git a/lib/IRremoteESP8266-2.6.0/examples/JVCPanasonicSendDemo/platformio.ini b/lib/IRremoteESP8266-2.6.0/examples/JVCPanasonicSendDemo/platformio.ini new file mode 100644 index 000000000000..ec84f22f3b05 --- /dev/null +++ b/lib/IRremoteESP8266-2.6.0/examples/JVCPanasonicSendDemo/platformio.ini @@ -0,0 +1,19 @@ +[platformio] +lib_extra_dirs = ../../ +src_dir=. + +[common] +build_flags = +lib_deps_builtin = +lib_deps_external = +lib_ldf_mode = chain+ + +[env:nodemcuv2] +platform = espressif8266 +framework = arduino +board = nodemcuv2 +lib_ldf_mode = ${common.lib_ldf_mode} +build_flags = ${common.build_flags} +lib_deps = + ${common.lib_deps_builtin} + ${common.lib_deps_external} diff --git a/lib/IRremoteESP8266-2.5.2.03/examples/LGACSend/LGACSend.ino b/lib/IRremoteESP8266-2.6.0/examples/LGACSend/LGACSend.ino similarity index 100% rename from lib/IRremoteESP8266-2.5.2.03/examples/LGACSend/LGACSend.ino rename to lib/IRremoteESP8266-2.6.0/examples/LGACSend/LGACSend.ino diff --git a/lib/IRremoteESP8266-2.6.0/examples/LGACSend/platformio.ini b/lib/IRremoteESP8266-2.6.0/examples/LGACSend/platformio.ini new file mode 100644 index 000000000000..ec84f22f3b05 --- /dev/null +++ b/lib/IRremoteESP8266-2.6.0/examples/LGACSend/platformio.ini @@ -0,0 +1,19 @@ +[platformio] +lib_extra_dirs = ../../ +src_dir=. + +[common] +build_flags = +lib_deps_builtin = +lib_deps_external = +lib_ldf_mode = chain+ + +[env:nodemcuv2] +platform = espressif8266 +framework = arduino +board = nodemcuv2 +lib_ldf_mode = ${common.lib_ldf_mode} +build_flags = ${common.build_flags} +lib_deps = + ${common.lib_deps_builtin} + ${common.lib_deps_external} diff --git a/lib/IRremoteESP8266-2.5.2.03/examples/TurnOnArgoAC/TurnOnArgoAC.ino b/lib/IRremoteESP8266-2.6.0/examples/TurnOnArgoAC/TurnOnArgoAC.ino similarity index 100% rename from lib/IRremoteESP8266-2.5.2.03/examples/TurnOnArgoAC/TurnOnArgoAC.ino rename to lib/IRremoteESP8266-2.6.0/examples/TurnOnArgoAC/TurnOnArgoAC.ino diff --git a/lib/IRremoteESP8266-2.6.0/examples/TurnOnArgoAC/platformio.ini b/lib/IRremoteESP8266-2.6.0/examples/TurnOnArgoAC/platformio.ini new file mode 100644 index 000000000000..ec84f22f3b05 --- /dev/null +++ b/lib/IRremoteESP8266-2.6.0/examples/TurnOnArgoAC/platformio.ini @@ -0,0 +1,19 @@ +[platformio] +lib_extra_dirs = ../../ +src_dir=. + +[common] +build_flags = +lib_deps_builtin = +lib_deps_external = +lib_ldf_mode = chain+ + +[env:nodemcuv2] +platform = espressif8266 +framework = arduino +board = nodemcuv2 +lib_ldf_mode = ${common.lib_ldf_mode} +build_flags = ${common.build_flags} +lib_deps = + ${common.lib_deps_builtin} + ${common.lib_deps_external} diff --git a/lib/IRremoteESP8266-2.5.2.03/examples/TurnOnDaikinAC/TurnOnDaikinAC.ino b/lib/IRremoteESP8266-2.6.0/examples/TurnOnDaikinAC/TurnOnDaikinAC.ino similarity index 100% rename from lib/IRremoteESP8266-2.5.2.03/examples/TurnOnDaikinAC/TurnOnDaikinAC.ino rename to lib/IRremoteESP8266-2.6.0/examples/TurnOnDaikinAC/TurnOnDaikinAC.ino diff --git a/lib/IRremoteESP8266-2.6.0/examples/TurnOnDaikinAC/platformio.ini b/lib/IRremoteESP8266-2.6.0/examples/TurnOnDaikinAC/platformio.ini new file mode 100644 index 000000000000..ec84f22f3b05 --- /dev/null +++ b/lib/IRremoteESP8266-2.6.0/examples/TurnOnDaikinAC/platformio.ini @@ -0,0 +1,19 @@ +[platformio] +lib_extra_dirs = ../../ +src_dir=. + +[common] +build_flags = +lib_deps_builtin = +lib_deps_external = +lib_ldf_mode = chain+ + +[env:nodemcuv2] +platform = espressif8266 +framework = arduino +board = nodemcuv2 +lib_ldf_mode = ${common.lib_ldf_mode} +build_flags = ${common.build_flags} +lib_deps = + ${common.lib_deps_builtin} + ${common.lib_deps_external} diff --git a/lib/IRremoteESP8266-2.5.2.03/examples/TurnOnFujitsuAC/TurnOnFujitsuAC.ino b/lib/IRremoteESP8266-2.6.0/examples/TurnOnFujitsuAC/TurnOnFujitsuAC.ino similarity index 100% rename from lib/IRremoteESP8266-2.5.2.03/examples/TurnOnFujitsuAC/TurnOnFujitsuAC.ino rename to lib/IRremoteESP8266-2.6.0/examples/TurnOnFujitsuAC/TurnOnFujitsuAC.ino diff --git a/lib/IRremoteESP8266-2.6.0/examples/TurnOnFujitsuAC/platformio.ini b/lib/IRremoteESP8266-2.6.0/examples/TurnOnFujitsuAC/platformio.ini new file mode 100644 index 000000000000..ec84f22f3b05 --- /dev/null +++ b/lib/IRremoteESP8266-2.6.0/examples/TurnOnFujitsuAC/platformio.ini @@ -0,0 +1,19 @@ +[platformio] +lib_extra_dirs = ../../ +src_dir=. + +[common] +build_flags = +lib_deps_builtin = +lib_deps_external = +lib_ldf_mode = chain+ + +[env:nodemcuv2] +platform = espressif8266 +framework = arduino +board = nodemcuv2 +lib_ldf_mode = ${common.lib_ldf_mode} +build_flags = ${common.build_flags} +lib_deps = + ${common.lib_deps_builtin} + ${common.lib_deps_external} diff --git a/lib/IRremoteESP8266-2.5.2.03/examples/TurnOnKelvinatorAC/TurnOnKelvinatorAC.ino b/lib/IRremoteESP8266-2.6.0/examples/TurnOnKelvinatorAC/TurnOnKelvinatorAC.ino similarity index 100% rename from lib/IRremoteESP8266-2.5.2.03/examples/TurnOnKelvinatorAC/TurnOnKelvinatorAC.ino rename to lib/IRremoteESP8266-2.6.0/examples/TurnOnKelvinatorAC/TurnOnKelvinatorAC.ino diff --git a/lib/IRremoteESP8266-2.6.0/examples/TurnOnKelvinatorAC/platformio.ini b/lib/IRremoteESP8266-2.6.0/examples/TurnOnKelvinatorAC/platformio.ini new file mode 100644 index 000000000000..ec84f22f3b05 --- /dev/null +++ b/lib/IRremoteESP8266-2.6.0/examples/TurnOnKelvinatorAC/platformio.ini @@ -0,0 +1,19 @@ +[platformio] +lib_extra_dirs = ../../ +src_dir=. + +[common] +build_flags = +lib_deps_builtin = +lib_deps_external = +lib_ldf_mode = chain+ + +[env:nodemcuv2] +platform = espressif8266 +framework = arduino +board = nodemcuv2 +lib_ldf_mode = ${common.lib_ldf_mode} +build_flags = ${common.build_flags} +lib_deps = + ${common.lib_deps_builtin} + ${common.lib_deps_external} diff --git a/lib/IRremoteESP8266-2.5.2.03/examples/TurnOnMitsubishiAC/TurnOnMitsubishiAC.ino b/lib/IRremoteESP8266-2.6.0/examples/TurnOnMitsubishiAC/TurnOnMitsubishiAC.ino similarity index 100% rename from lib/IRremoteESP8266-2.5.2.03/examples/TurnOnMitsubishiAC/TurnOnMitsubishiAC.ino rename to lib/IRremoteESP8266-2.6.0/examples/TurnOnMitsubishiAC/TurnOnMitsubishiAC.ino diff --git a/lib/IRremoteESP8266-2.6.0/examples/TurnOnMitsubishiAC/platformio.ini b/lib/IRremoteESP8266-2.6.0/examples/TurnOnMitsubishiAC/platformio.ini new file mode 100644 index 000000000000..ec84f22f3b05 --- /dev/null +++ b/lib/IRremoteESP8266-2.6.0/examples/TurnOnMitsubishiAC/platformio.ini @@ -0,0 +1,19 @@ +[platformio] +lib_extra_dirs = ../../ +src_dir=. + +[common] +build_flags = +lib_deps_builtin = +lib_deps_external = +lib_ldf_mode = chain+ + +[env:nodemcuv2] +platform = espressif8266 +framework = arduino +board = nodemcuv2 +lib_ldf_mode = ${common.lib_ldf_mode} +build_flags = ${common.build_flags} +lib_deps = + ${common.lib_deps_builtin} + ${common.lib_deps_external} diff --git a/lib/IRremoteESP8266-2.6.0/examples/TurnOnMitsubishiHeavyAc/TurnOnMitsubishiHeavyAc.ino b/lib/IRremoteESP8266-2.6.0/examples/TurnOnMitsubishiHeavyAc/TurnOnMitsubishiHeavyAc.ino new file mode 100644 index 000000000000..2ad2d7bc303a --- /dev/null +++ b/lib/IRremoteESP8266-2.6.0/examples/TurnOnMitsubishiHeavyAc/TurnOnMitsubishiHeavyAc.ino @@ -0,0 +1,72 @@ +/* Copyright 2019 David Conran +* +* An IR LED circuit *MUST* be connected to the ESP8266 on a pin +* as specified by kIrLed below. +* +* TL;DR: The IR LED needs to be driven by a transistor for a good result. +* +* Suggested circuit: +* https://github.com/markszabo/IRremoteESP8266/wiki#ir-sending +* +* Common mistakes & tips: +* * Don't just connect the IR LED directly to the pin, it won't +* have enough current to drive the IR LED effectively. +* * Make sure you have the IR LED polarity correct. +* See: https://learn.sparkfun.com/tutorials/polarity/diode-and-led-polarity +* * Typical digital camera/phones can be used to see if the IR LED is flashed. +* Replace the IR LED with a normal LED if you don't have a digital camera +* when debugging. +* * Avoid using the following pins unless you really know what you are doing: +* * Pin 0/D3: Can interfere with the boot/program mode & support circuits. +* * Pin 1/TX/TXD0: Any serial transmissions from the ESP8266 will interfere. +* * Pin 3/RX/RXD0: Any serial transmissions to the ESP8266 will interfere. +* * ESP-01 modules are tricky. We suggest you use a module with more GPIOs +* for your first time. e.g. ESP-12 etc. +*/ +#ifndef UNIT_TEST +#include +#endif +#include +#include +#include + +const uint16_t kIrLed = 4; // ESP8266 GPIO pin to use. Recommended: 4 (D2). +IRMitsubishiHeavy152Ac ac(kIrLed); // Set the GPIO used for sending messages. + +void printState() { + // Display the settings. + Serial.println("Mitsubishi Heavy A/C remote is in the following state:"); + Serial.printf(" %s\n", ac.toString().c_str()); + // Display the encoded IR sequence. + unsigned char* ir_code = ac.getRaw(); + Serial.print("IR Code: 0x"); + for (uint8_t i = 0; i < kMitsubishiHeavy152StateLength; i++) + Serial.printf("%02X", ir_code[i]); + Serial.println(); +} + +void setup() { + ac.begin(); + Serial.begin(115200); + delay(200); + + // Set up what we want to send. See ir_MitsubishiHeavy.(cpp|h) for all the + // options. + Serial.println("Default state of the remote."); + printState(); + Serial.println("Setting desired state for A/C."); + ac.setPower(true); // Turn it on. + ac.setFan(kMitsubishiHeavy152FanMed); // Medium Fan + ac.setMode(kMitsubishiHeavyCool); // Cool mode + ac.setTemp(26); // Celsius + ac.setSwingVertical(kMitsubishiHeavy152SwingVAuto); // Swing vertically + ac.setSwingHorizontal(kMitsubishiHeavy152SwingHMiddle); // Swing Horizontally +} + +void loop() { + // Now send the IR signal. + Serial.println("Sending IR command to A/C ..."); + ac.send(); + printState(); + delay(5000); +} diff --git a/lib/IRremoteESP8266-2.6.0/examples/TurnOnMitsubishiHeavyAc/platformio.ini b/lib/IRremoteESP8266-2.6.0/examples/TurnOnMitsubishiHeavyAc/platformio.ini new file mode 100644 index 000000000000..ec84f22f3b05 --- /dev/null +++ b/lib/IRremoteESP8266-2.6.0/examples/TurnOnMitsubishiHeavyAc/platformio.ini @@ -0,0 +1,19 @@ +[platformio] +lib_extra_dirs = ../../ +src_dir=. + +[common] +build_flags = +lib_deps_builtin = +lib_deps_external = +lib_ldf_mode = chain+ + +[env:nodemcuv2] +platform = espressif8266 +framework = arduino +board = nodemcuv2 +lib_ldf_mode = ${common.lib_ldf_mode} +build_flags = ${common.build_flags} +lib_deps = + ${common.lib_deps_builtin} + ${common.lib_deps_external} diff --git a/lib/IRremoteESP8266-2.6.0/examples/TurnOnPanasonicAC/TurnOnPanasonicAC.ino b/lib/IRremoteESP8266-2.6.0/examples/TurnOnPanasonicAC/TurnOnPanasonicAC.ino new file mode 100644 index 000000000000..ea39ac5e2b84 --- /dev/null +++ b/lib/IRremoteESP8266-2.6.0/examples/TurnOnPanasonicAC/TurnOnPanasonicAC.ino @@ -0,0 +1,74 @@ +/* Copyright 2017, 2018 David Conran +* +* An IR LED circuit *MUST* be connected to the ESP8266 on a pin +* as specified by kIrLed below. +* +* TL;DR: The IR LED needs to be driven by a transistor for a good result. +* +* Suggested circuit: +* https://github.com/markszabo/IRremoteESP8266/wiki#ir-sending +* +* Common mistakes & tips: +* * Don't just connect the IR LED directly to the pin, it won't +* have enough current to drive the IR LED effectively. +* * Make sure you have the IR LED polarity correct. +* See: https://learn.sparkfun.com/tutorials/polarity/diode-and-led-polarity +* * Typical digital camera/phones can be used to see if the IR LED is flashed. +* Replace the IR LED with a normal LED if you don't have a digital camera +* when debugging. +* * Avoid using the following pins unless you really know what you are doing: +* * Pin 0/D3: Can interfere with the boot/program mode & support circuits. +* * Pin 1/TX/TXD0: Any serial transmissions from the ESP8266 will interfere. +* * Pin 3/RX/RXD0: Any serial transmissions to the ESP8266 will interfere. +* * ESP-01 modules are tricky. We suggest you use a module with more GPIOs +* for your first time. e.g. ESP-12 etc. +*/ +#ifndef UNIT_TEST +#include +#endif +#include +#include +#include + +const uint16_t kIrLed = 4; // ESP8266 GPIO pin to use. Recommended: 4 (D2). +IRPanasonicAc ac(kIrLed); // Set the GPIO used for sending messages. + +void printState() { + // Display the settings. + Serial.println("Panasonic A/C remote is in the following state:"); + Serial.printf(" %s\n", ac.toString().c_str()); + // Display the encoded IR sequence. + unsigned char* ir_code = ac.getRaw(); + Serial.print("IR Code: 0x"); + for (uint8_t i = 0; i < kPanasonicAcStateLength; i++) + Serial.printf("%02X", ir_code[i]); + Serial.println(); +} + +void setup() { + ac.begin(); + Serial.begin(115200); + delay(200); + + // Set up what we want to send. See ir_Panasonic.cpp for all the options. + Serial.println("Default state of the remote."); + printState(); + Serial.println("Setting desired state for A/C."); + ac.setModel(kPanasonicRkr); + ac.on(); + ac.setFan(kPanasonicAcFanAuto); + ac.setMode(kPanasonicAcCool); + ac.setTemp(26); + ac.setSwingVertical(kPanasonicAcSwingVAuto); + ac.setSwingHorizontal(kPanasonicAcSwingHAuto); +} + +void loop() { + // Now send the IR signal. +#if SEND_PANASONIC_AC + Serial.println("Sending IR command to A/C ..."); + ac.send(); +#endif // SEND_PANASONIC_AC + printState(); + delay(5000); +} diff --git a/lib/IRremoteESP8266-2.6.0/examples/TurnOnPanasonicAC/platformio.ini b/lib/IRremoteESP8266-2.6.0/examples/TurnOnPanasonicAC/platformio.ini new file mode 100644 index 000000000000..ec84f22f3b05 --- /dev/null +++ b/lib/IRremoteESP8266-2.6.0/examples/TurnOnPanasonicAC/platformio.ini @@ -0,0 +1,19 @@ +[platformio] +lib_extra_dirs = ../../ +src_dir=. + +[common] +build_flags = +lib_deps_builtin = +lib_deps_external = +lib_ldf_mode = chain+ + +[env:nodemcuv2] +platform = espressif8266 +framework = arduino +board = nodemcuv2 +lib_ldf_mode = ${common.lib_ldf_mode} +build_flags = ${common.build_flags} +lib_deps = + ${common.lib_deps_builtin} + ${common.lib_deps_external} diff --git a/lib/IRremoteESP8266-2.5.2.03/examples/TurnOnToshibaAC/TurnOnToshibaAC.ino b/lib/IRremoteESP8266-2.6.0/examples/TurnOnToshibaAC/TurnOnToshibaAC.ino similarity index 100% rename from lib/IRremoteESP8266-2.5.2.03/examples/TurnOnToshibaAC/TurnOnToshibaAC.ino rename to lib/IRremoteESP8266-2.6.0/examples/TurnOnToshibaAC/TurnOnToshibaAC.ino diff --git a/lib/IRremoteESP8266-2.6.0/examples/TurnOnToshibaAC/platformio.ini b/lib/IRremoteESP8266-2.6.0/examples/TurnOnToshibaAC/platformio.ini new file mode 100644 index 000000000000..ec84f22f3b05 --- /dev/null +++ b/lib/IRremoteESP8266-2.6.0/examples/TurnOnToshibaAC/platformio.ini @@ -0,0 +1,19 @@ +[platformio] +lib_extra_dirs = ../../ +src_dir=. + +[common] +build_flags = +lib_deps_builtin = +lib_deps_external = +lib_ldf_mode = chain+ + +[env:nodemcuv2] +platform = espressif8266 +framework = arduino +board = nodemcuv2 +lib_ldf_mode = ${common.lib_ldf_mode} +build_flags = ${common.build_flags} +lib_deps = + ${common.lib_deps_builtin} + ${common.lib_deps_external} diff --git a/lib/IRremoteESP8266-2.5.2.03/examples/TurnOnTrotecAC/TurnOnTrotecAC.ino b/lib/IRremoteESP8266-2.6.0/examples/TurnOnTrotecAC/TurnOnTrotecAC.ino similarity index 100% rename from lib/IRremoteESP8266-2.5.2.03/examples/TurnOnTrotecAC/TurnOnTrotecAC.ino rename to lib/IRremoteESP8266-2.6.0/examples/TurnOnTrotecAC/TurnOnTrotecAC.ino diff --git a/lib/IRremoteESP8266-2.6.0/examples/TurnOnTrotecAC/platformio.ini b/lib/IRremoteESP8266-2.6.0/examples/TurnOnTrotecAC/platformio.ini new file mode 100644 index 000000000000..ec84f22f3b05 --- /dev/null +++ b/lib/IRremoteESP8266-2.6.0/examples/TurnOnTrotecAC/platformio.ini @@ -0,0 +1,19 @@ +[platformio] +lib_extra_dirs = ../../ +src_dir=. + +[common] +build_flags = +lib_deps_builtin = +lib_deps_external = +lib_ldf_mode = chain+ + +[env:nodemcuv2] +platform = espressif8266 +framework = arduino +board = nodemcuv2 +lib_ldf_mode = ${common.lib_ldf_mode} +build_flags = ${common.build_flags} +lib_deps = + ${common.lib_deps_builtin} + ${common.lib_deps_external} diff --git a/lib/IRremoteESP8266-2.5.2.03/keywords.txt b/lib/IRremoteESP8266-2.6.0/keywords.txt similarity index 74% rename from lib/IRremoteESP8266-2.5.2.03/keywords.txt rename to lib/IRremoteESP8266-2.6.0/keywords.txt index ac3f43fe1809..a498c5d61cae 100644 --- a/lib/IRremoteESP8266-2.5.2.03/keywords.txt +++ b/lib/IRremoteESP8266-2.6.0/keywords.txt @@ -22,21 +22,32 @@ IRArgoAC KEYWORD1 IRCoolixAC KEYWORD1 +IRDaikin2 KEYWORD1 +IRDaikin216 KEYWORD1 IRDaikinESP KEYWORD1 IRFujitsuAC KEYWORD1 IRGreeAC KEYWORD1 IRHaierAC KEYWORD1 IRHaierACYRW02 KEYWORD1 +IRHitachiAc KEYWORD1 IRKelvinatorAC KEYWORD1 IRMideaAC KEYWORD1 IRMitsubishiAC KEYWORD1 +IRMitsubishiHeavy152Ac KEYWORD1 +IRMitsubishiHeavy88Ac KEYWORD1 IRPanasonicAc KEYWORD1 IRSamsungAc KEYWORD1 +IRTcl112Ac KEYWORD1 +IRTecoAc KEYWORD1 IRToshibaAC KEYWORD1 IRTrotecESP KEYWORD1 +IRVestelAc KEYWORD1 +IRWhirlpoolAc KEYWORD1 +IRac KEYWORD1 IRrecv KEYWORD1 IRsend KEYWORD1 IRtimer KEYWORD1 +TimerMs KEYWORD1 decode_results KEYWORD1 ir_params_t KEYWORD1 match_result_t KEYWORD1 @@ -46,8 +57,10 @@ match_result_t KEYWORD1 ####################################### _delayMicroseconds KEYWORD2 +_setMode KEYWORD2 +_setTemp KEYWORD2 add KEYWORD2 -addbit KEYWORD2 +argo KEYWORD2 begin KEYWORD2 buildFromState KEYWORD2 buildState KEYWORD2 @@ -60,18 +73,27 @@ calibrate KEYWORD2 cancelOffTimer KEYWORD2 cancelOnTimer KEYWORD2 cancelTimers KEYWORD2 -checkheader KEYWORD2 +checkZjsSig KEYWORD2 +checkZmsSig KEYWORD2 checksum KEYWORD2 -clearBit KEYWORD2 +clearOnTimerFlag KEYWORD2 clearSensorTemp KEYWORD2 +clearSleepTimerFlag KEYWORD2 compare KEYWORD2 +coolix KEYWORD2 copyIrParams KEYWORD2 +countBits KEYWORD2 +daikin KEYWORD2 +daikin2 KEYWORD2 +daikin216 KEYWORD2 decode KEYWORD2 decodeAiwaRCT501 KEYWORD2 decodeCOOLIX KEYWORD2 decodeCarrierAC KEYWORD2 decodeDISH KEYWORD2 decodeDaikin KEYWORD2 +decodeDaikin2 KEYWORD2 +decodeDaikin216 KEYWORD2 decodeDenon KEYWORD2 decodeElectraAC KEYWORD2 decodeFujitsuAC KEYWORD2 @@ -85,6 +107,7 @@ decodeJVC KEYWORD2 decodeKelvinator KEYWORD2 decodeLG KEYWORD2 decodeLasertag KEYWORD2 +decodeLegoPf KEYWORD2 decodeLutron KEYWORD2 decodeMWM KEYWORD2 decodeMagiQuest KEYWORD2 @@ -92,6 +115,7 @@ decodeMidea KEYWORD2 decodeMitsubishi KEYWORD2 decodeMitsubishi2 KEYWORD2 decodeMitsubishiAC KEYWORD2 +decodeMitsubishiHeavy KEYWORD2 decodeNEC KEYWORD2 decodeNikai KEYWORD2 decodePanasonic KEYWORD2 @@ -101,22 +125,29 @@ decodeRC5 KEYWORD2 decodeRC6 KEYWORD2 decodeRCMM KEYWORD2 decodeSAMSUNG KEYWORD2 +decodeSamsung36 KEYWORD2 decodeSamsungAC KEYWORD2 decodeSanyo KEYWORD2 decodeSanyoLC7461 KEYWORD2 decodeSharp KEYWORD2 decodeSony KEYWORD2 +decodeTcl112Ac KEYWORD2 +decodeTeco KEYWORD2 decodeToshibaAC KEYWORD2 +decodeVestelAc KEYWORD2 decodeWhirlpoolAC KEYWORD2 decodeWhynter KEYWORD2 disableIRIn KEYWORD2 disableOffTimer KEYWORD2 disableOnTimer KEYWORD2 +disableSleepTimer KEYWORD2 elapsed KEYWORD2 enableIRIn KEYWORD2 enableIROut KEYWORD2 enableOffTimer KEYWORD2 enableOnTimer KEYWORD2 +enableSleepTimer KEYWORD2 +enableTimer KEYWORD2 encodeJVC KEYWORD2 encodeLG KEYWORD2 encodeMagiQuest KEYWORD2 @@ -131,15 +162,18 @@ encodeSanyoLC7461 KEYWORD2 encodeSharp KEYWORD2 encodeSony KEYWORD2 encodeTime KEYWORD2 +fanspeed_t KEYWORD2 fixChecksum KEYWORD2 fixup KEYWORD2 +fujitsu KEYWORD2 +get3D KEYWORD2 getBeep KEYWORD2 -getBit KEYWORD2 getBufSize KEYWORD2 getButton KEYWORD2 getClean KEYWORD2 getClock KEYWORD2 getCmd KEYWORD2 +getComfort KEYWORD2 getCommand KEYWORD2 getCoolMode KEYWORD2 getCorrectedRawLength KEYWORD2 @@ -147,11 +181,16 @@ getCurrTime KEYWORD2 getCurrentTime KEYWORD2 getEcono KEYWORD2 getEye KEYWORD2 +getEyeAuto KEYWORD2 getFan KEYWORD2 getFanSpeed KEYWORD2 +getFilter KEYWORD2 getFlap KEYWORD2 +getFreshAir KEYWORD2 +getFreshAirHigh KEYWORD2 getHealth KEYWORD2 getHeatMode KEYWORD2 +getIon KEYWORD2 getIonFilter KEYWORD2 getLed KEYWORD2 getLight KEYWORD2 @@ -159,6 +198,7 @@ getMax KEYWORD2 getMode KEYWORD2 getMold KEYWORD2 getNight KEYWORD2 +getNormalState KEYWORD2 getOffTime KEYWORD2 getOffTimer KEYWORD2 getOffTimerEnabled KEYWORD2 @@ -166,23 +206,30 @@ getOnTime KEYWORD2 getOnTimer KEYWORD2 getOnTimerEnabled KEYWORD2 getPower KEYWORD2 +getPowerToggle KEYWORD2 getPowerful KEYWORD2 +getPurify KEYWORD2 getQuiet KEYWORD2 getRClevel KEYWORD2 getRaw KEYWORD2 getSensor KEYWORD2 getSensorTemp KEYWORD2 +getSilent KEYWORD2 getSleep KEYWORD2 +getSleepTime KEYWORD2 +getSleepTimerEnabled KEYWORD2 getSpeed KEYWORD2 getStartClock KEYWORD2 getStateLength KEYWORD2 getStopClock KEYWORD2 +getSuper KEYWORD2 getSwing KEYWORD2 getSwingHorizontal KEYWORD2 getSwingVertical KEYWORD2 getSwingVerticalAuto KEYWORD2 getSwingVerticalPosition KEYWORD2 getTemp KEYWORD2 +getTempOffset KEYWORD2 getTempRaw KEYWORD2 getTime KEYWORD2 getTimer KEYWORD2 @@ -191,10 +238,23 @@ getVane KEYWORD2 getXFan KEYWORD2 getZoneFollow KEYWORD2 getiFeel KEYWORD2 +gree KEYWORD2 +haier KEYWORD2 +haierYrwo2 KEYWORD2 hasACState KEYWORD2 +hitachi KEYWORD2 +htmlEscape KEYWORD2 invertBits KEYWORD2 +isOffTimerActive KEYWORD2 isOffTimerEnabled KEYWORD2 +isOnTimerActive KEYWORD2 isOnTimerEnabled KEYWORD2 +isProtocolSupported KEYWORD2 +isSpecialState KEYWORD2 +isTimeCommand KEYWORD2 +isTimerActive KEYWORD2 +isTimerEnabled KEYWORD2 +kelvinator KEYWORD2 ledOff KEYWORD2 ledOn KEYWORD2 mark KEYWORD2 @@ -203,10 +263,17 @@ matchAtLeast KEYWORD2 matchData KEYWORD2 matchMark KEYWORD2 matchSpace KEYWORD2 +midea KEYWORD2 +mitsubishi KEYWORD2 +mitsubishiHeavy152 KEYWORD2 +mitsubishiHeavy88 KEYWORD2 +mode) KEYWORD2 off KEYWORD2 on KEYWORD2 -printState KEYWORD2 -readbits KEYWORD2 +opmode_t KEYWORD2 +panasonic KEYWORD2 +position) KEYWORD2 +recoverSavedState KEYWORD2 renderTime KEYWORD2 reset KEYWORD2 resultToHexidecimal KEYWORD2 @@ -215,13 +282,17 @@ resultToSourceCode KEYWORD2 resultToTimingInfo KEYWORD2 resume KEYWORD2 reverseBits KEYWORD2 +samsung KEYWORD2 send KEYWORD2 +sendAc KEYWORD2 sendAiwaRCT501 KEYWORD2 sendArgo KEYWORD2 sendCOOLIX KEYWORD2 sendCarrierAC KEYWORD2 sendDISH KEYWORD2 sendDaikin KEYWORD2 +sendDaikin2 KEYWORD2 +sendDaikin216 KEYWORD2 sendData KEYWORD2 sendDenon KEYWORD2 sendElectraAC KEYWORD2 @@ -241,6 +312,7 @@ sendKelvinator KEYWORD2 sendLG KEYWORD2 sendLG2 KEYWORD2 sendLasertag KEYWORD2 +sendLegoPf KEYWORD2 sendLutron KEYWORD2 sendMWM KEYWORD2 sendMagiQuest KEYWORD2 @@ -248,8 +320,12 @@ sendMidea KEYWORD2 sendMitsubishi KEYWORD2 sendMitsubishi2 KEYWORD2 sendMitsubishiAC KEYWORD2 +sendMitsubishiHeavy152 KEYWORD2 +sendMitsubishiHeavy88 KEYWORD2 sendNEC KEYWORD2 sendNikai KEYWORD2 +sendOff KEYWORD2 +sendOn KEYWORD2 sendPanasonic KEYWORD2 sendPanasonic64 KEYWORD2 sendPanasonicAC KEYWORD2 @@ -260,34 +336,45 @@ sendRC6 KEYWORD2 sendRCMM KEYWORD2 sendRaw KEYWORD2 sendSAMSUNG KEYWORD2 +sendSamsung36 KEYWORD2 sendSamsungAC KEYWORD2 sendSanyoLC7461 KEYWORD2 sendSharp KEYWORD2 sendSharpRaw KEYWORD2 sendSherwood KEYWORD2 sendSony KEYWORD2 +sendTcl112Ac KEYWORD2 +sendTeco KEYWORD2 sendToshibaAC KEYWORD2 sendTrotec KEYWORD2 +sendVestelAc KEYWORD2 sendWhirlpoolAC KEYWORD2 sendWhynter KEYWORD2 serialPrintUint64 KEYWORD2 +set3D KEYWORD2 +setAuto KEYWORD2 setBeep KEYWORD2 -setBit KEYWORD2 setButton KEYWORD2 setClean KEYWORD2 setClock KEYWORD2 setCmd KEYWORD2 +setComfort KEYWORD2 setCommand KEYWORD2 setCoolMode KEYWORD2 setCurrTime KEYWORD2 setCurrentTime KEYWORD2 setEcono KEYWORD2 setEye KEYWORD2 +setEyeAuto KEYWORD2 setFan KEYWORD2 setFanSpeed KEYWORD2 +setFilter KEYWORD2 setFlap KEYWORD2 +setFreshAir KEYWORD2 +setFreshAirHigh KEYWORD2 setHealth KEYWORD2 setHeatMode KEYWORD2 +setIon KEYWORD2 setIonFilter KEYWORD2 setLed KEYWORD2 setLight KEYWORD2 @@ -297,19 +384,25 @@ setModel KEYWORD2 setMold KEYWORD2 setNight KEYWORD2 setOffTimer KEYWORD2 +setOffTimerActive KEYWORD2 setOnTimer KEYWORD2 +setOnTimerActive KEYWORD2 setPower KEYWORD2 +setPowerToggle KEYWORD2 setPowerful KEYWORD2 +setPurify KEYWORD2 setQuiet KEYWORD2 setRaw KEYWORD2 setRoomTemp KEYWORD2 setSensor KEYWORD2 setSensorTemp KEYWORD2 setSensorTempRaw KEYWORD2 +setSilent KEYWORD2 setSleep KEYWORD2 setSpeed KEYWORD2 setStartClock KEYWORD2 setStopClock KEYWORD2 +setSuper KEYWORD2 setSwing KEYWORD2 setSwingHorizontal KEYWORD2 setSwingVertical KEYWORD2 @@ -317,6 +410,7 @@ setTemp KEYWORD2 setTempRaw KEYWORD2 setTime KEYWORD2 setTimer KEYWORD2 +setTimerActive KEYWORD2 setTurbo KEYWORD2 setUnknownThreshold KEYWORD2 setVane KEYWORD2 @@ -324,19 +418,33 @@ setXFan KEYWORD2 setZoneFollow KEYWORD2 setiFeel KEYWORD2 space KEYWORD2 +speed) KEYWORD2 stateReset KEYWORD2 stepHoriz KEYWORD2 stepVert KEYWORD2 +strToBool KEYWORD2 +strToModel KEYWORD2 sumBytes KEYWORD2 +swingh_t KEYWORD2 +swingv) KEYWORD2 +swingv_t KEYWORD2 +tcl112 KEYWORD2 +teco KEYWORD2 ticksHigh KEYWORD2 ticksLow KEYWORD2 timeToString KEYWORD2 toString KEYWORD2 toggleRC5 KEYWORD2 toggleRC6 KEYWORD2 +toshiba KEYWORD2 +trotec KEYWORD2 typeToString KEYWORD2 uint64ToString KEYWORD2 +updateSavedState KEYWORD2 validChecksum KEYWORD2 +vestel KEYWORD2 +whirlpool KEYWORD2 +xorBytes KEYWORD2 ####################################### # Constants (LITERAL1) @@ -349,9 +457,9 @@ ARDB1 LITERAL1 ARGO LITERAL1 ARGO_COMMAND_LENGTH LITERAL1 ARGO_COOL_AUTO LITERAL1 +ARGO_COOL_HUM LITERAL1 ARGO_COOL_OFF LITERAL1 ARGO_COOL_ON LITERAL1 -ARGO_COOl_HUM LITERAL1 ARGO_FAN_1 LITERAL1 ARGO_FAN_2 LITERAL1 ARGO_FAN_3 LITERAL1 @@ -375,10 +483,11 @@ CARRIER_AC_BITS LITERAL1 COOLIX LITERAL1 COOLIX_BITS LITERAL1 DAIKIN LITERAL1 +DAIKIN2 LITERAL1 +DAIKIN216 LITERAL1 DAIKIN_AUTO LITERAL1 DAIKIN_COMMAND_LENGTH LITERAL1 DAIKIN_COOL LITERAL1 -DAIKIN_DEBUG LITERAL1 DAIKIN_DRY LITERAL1 DAIKIN_FAN LITERAL1 DAIKIN_FAN_AUTO LITERAL1 @@ -394,6 +503,8 @@ DECODE_ARGO LITERAL1 DECODE_CARRIER_AC LITERAL1 DECODE_COOLIX LITERAL1 DECODE_DAIKIN LITERAL1 +DECODE_DAIKIN2 LITERAL1 +DECODE_DAIKIN216 LITERAL1 DECODE_DENON LITERAL1 DECODE_DISH LITERAL1 DECODE_ELECTRA_AC LITERAL1 @@ -410,12 +521,14 @@ DECODE_HITACHI_AC2 LITERAL1 DECODE_JVC LITERAL1 DECODE_KELVINATOR LITERAL1 DECODE_LASERTAG LITERAL1 +DECODE_LEGOPF LITERAL1 DECODE_LG LITERAL1 DECODE_LUTRON LITERAL1 DECODE_MAGIQUEST LITERAL1 DECODE_MIDEA LITERAL1 DECODE_MITSUBISHI LITERAL1 DECODE_MITSUBISHI2 LITERAL1 +DECODE_MITSUBISHIHEAVY LITERAL1 DECODE_MITSUBISHI_AC LITERAL1 DECODE_MWM LITERAL1 DECODE_NEC LITERAL1 @@ -428,19 +541,25 @@ DECODE_RC5 LITERAL1 DECODE_RC6 LITERAL1 DECODE_RCMM LITERAL1 DECODE_SAMSUNG LITERAL1 +DECODE_SAMSUNG36 LITERAL1 DECODE_SAMSUNG_AC LITERAL1 DECODE_SANYO LITERAL1 DECODE_SHARP LITERAL1 DECODE_SHERWOOD LITERAL1 DECODE_SONY LITERAL1 +DECODE_TCL112AC LITERAL1 +DECODE_TECO LITERAL1 DECODE_TOSHIBA_AC LITERAL1 DECODE_TROTEC LITERAL1 +DECODE_VESTEL_AC LITERAL1 DECODE_WHIRLPOOL_AC LITERAL1 DECODE_WHYNTER LITERAL1 DENON LITERAL1 DENON_48_BITS LITERAL1 DENON_BITS LITERAL1 DENON_LEGACY_BITS LITERAL1 +DG11J13A LITERAL1 +DG11J191 LITERAL1 DISH LITERAL1 DISH_BITS LITERAL1 ELECTRA_AC LITERAL1 @@ -580,6 +699,7 @@ KELVINATOR_MIN_TEMP LITERAL1 KELVINATOR_STATE_LENGTH LITERAL1 LASERTAG LITERAL1 LASERTAG_BITS LITERAL1 +LEGOPF LITERAL1 LG LITERAL1 LG2 LITERAL1 LG32_BITS LITERAL1 @@ -623,6 +743,8 @@ MITSUBISHI_AC_STATE_LENGTH LITERAL1 MITSUBISHI_AC_VANE_AUTO LITERAL1 MITSUBISHI_AC_VANE_AUTO_MOVE LITERAL1 MITSUBISHI_BITS LITERAL1 +MITSUBISHI_HEAVY_152 LITERAL1 +MITSUBISHI_HEAVY_88 LITERAL1 MWM LITERAL1 NEC LITERAL1 NEC_BITS LITERAL1 @@ -647,6 +769,7 @@ RC6_MODE0_BITS LITERAL1 RCMM LITERAL1 RCMM_BITS LITERAL1 SAMSUNG LITERAL1 +SAMSUNG36 LITERAL1 SAMSUNG_AC LITERAL1 SAMSUNG_BITS LITERAL1 SANYO LITERAL1 @@ -658,6 +781,8 @@ SEND_ARGO LITERAL1 SEND_CARRIER_AC LITERAL1 SEND_COOLIX LITERAL1 SEND_DAIKIN LITERAL1 +SEND_DAIKIN2 LITERAL1 +SEND_DAIKIN216 LITERAL1 SEND_DENON LITERAL1 SEND_DISH LITERAL1 SEND_ELECTRA_AC LITERAL1 @@ -673,12 +798,14 @@ SEND_HITACHI_AC2 LITERAL1 SEND_JVC LITERAL1 SEND_KELVINATOR LITERAL1 SEND_LASERTAG LITERAL1 +SEND_LEGOPF LITERAL1 SEND_LG LITERAL1 SEND_LUTRON LITERAL1 SEND_MAGIQUEST LITERAL1 SEND_MIDEA LITERAL1 SEND_MITSUBISHI LITERAL1 SEND_MITSUBISHI2 LITERAL1 +SEND_MITSUBISHIHEAVY LITERAL1 SEND_MITSUBISHI_AC LITERAL1 SEND_MWM LITERAL1 SEND_NEC LITERAL1 @@ -692,13 +819,17 @@ SEND_RC5 LITERAL1 SEND_RC6 LITERAL1 SEND_RCMM LITERAL1 SEND_SAMSUNG LITERAL1 +SEND_SAMSUNG36 LITERAL1 SEND_SAMSUNG_AC LITERAL1 SEND_SANYO LITERAL1 SEND_SHARP LITERAL1 SEND_SHERWOOD LITERAL1 SEND_SONY LITERAL1 +SEND_TCL112AC LITERAL1 +SEND_TECO LITERAL1 SEND_TOSHIBA_AC LITERAL1 SEND_TROTEC LITERAL1 +SEND_VESTEL_AC LITERAL1 SEND_WHIRLPOOL_AC LITERAL1 SEND_WHYNTER LITERAL1 SHARP LITERAL1 @@ -709,6 +840,8 @@ SONY LITERAL1 SONY_12_BITS LITERAL1 SONY_15_BITS LITERAL1 SONY_20_BITS LITERAL1 +TCL112AC LITERAL1 +TECO LITERAL1 TIMEOUT_MS LITERAL1 TOSHIBA_AC LITERAL1 TOSHIBA_AC_AUTO LITERAL1 @@ -733,9 +866,9 @@ TROTEC_FAN_MED LITERAL1 TROTEC_MAX_TEMP LITERAL1 TROTEC_MAX_TIMER LITERAL1 TROTEC_MIN_TEMP LITERAL1 -TROTEC_MIN_TIMER LITERAL1 UNKNOWN LITERAL1 UNUSED LITERAL1 +VESTEL_AC LITERAL1 WHIRLPOOL_AC LITERAL1 WHYNTER LITERAL1 WHYNTER_BITS LITERAL1 @@ -750,6 +883,7 @@ kArgoCoolAuto LITERAL1 kArgoCoolHum LITERAL1 kArgoCoolOff LITERAL1 kArgoCoolOn LITERAL1 +kArgoDefaultRepeat LITERAL1 kArgoFan1 LITERAL1 kArgoFan2 LITERAL1 kArgoFan3 LITERAL1 @@ -772,6 +906,7 @@ kArgoMinTemp LITERAL1 kArgoOneSpace LITERAL1 kArgoStateLength LITERAL1 kArgoZeroSpace LITERAL1 +kAuto LITERAL1 kCarrierAcBitMark LITERAL1 kCarrierAcBits LITERAL1 kCarrierAcGap LITERAL1 @@ -780,16 +915,19 @@ kCarrierAcHdrSpace LITERAL1 kCarrierAcMinRepeat LITERAL1 kCarrierAcOneSpace LITERAL1 kCarrierAcZeroSpace LITERAL1 +kCool LITERAL1 kCoolixAuto LITERAL1 kCoolixBitMark LITERAL1 kCoolixBitMarkTicks LITERAL1 kCoolixBits LITERAL1 kCoolixClean LITERAL1 kCoolixCool LITERAL1 +kCoolixDefaultRepeat LITERAL1 kCoolixDefaultState LITERAL1 kCoolixDry LITERAL1 kCoolixFan LITERAL1 kCoolixFanAuto LITERAL1 +kCoolixFanAuto0 LITERAL1 kCoolixFanFixed LITERAL1 kCoolixFanMask LITERAL1 kCoolixFanMax LITERAL1 @@ -827,7 +965,70 @@ kCoolixUnknown LITERAL1 kCoolixZeroSpace LITERAL1 kCoolixZeroSpaceTicks LITERAL1 kCoolixZoneFollowMask LITERAL1 +kDaikin216BitMark LITERAL1 +kDaikin216Bits LITERAL1 +kDaikin216ByteFan LITERAL1 +kDaikin216ByteMode LITERAL1 +kDaikin216BytePower LITERAL1 +kDaikin216ByteSwingH LITERAL1 +kDaikin216ByteSwingV LITERAL1 +kDaikin216ByteTemp LITERAL1 +kDaikin216DefaultRepeat LITERAL1 +kDaikin216Freq LITERAL1 +kDaikin216Gap LITERAL1 +kDaikin216HdrMark LITERAL1 +kDaikin216HdrSpace LITERAL1 +kDaikin216MaskFan LITERAL1 +kDaikin216MaskMode LITERAL1 +kDaikin216MaskSwingH LITERAL1 +kDaikin216MaskSwingV LITERAL1 +kDaikin216MaskTemp LITERAL1 +kDaikin216OneSpace LITERAL1 +kDaikin216Section1Length LITERAL1 +kDaikin216Section2Length LITERAL1 +kDaikin216Sections LITERAL1 +kDaikin216StateLength LITERAL1 +kDaikin216ZeroSpace LITERAL1 +kDaikin2BeepMask LITERAL1 +kDaikin2BitClean LITERAL1 +kDaikin2BitEye LITERAL1 +kDaikin2BitEyeAuto LITERAL1 +kDaikin2BitFreshAir LITERAL1 +kDaikin2BitFreshAirHigh LITERAL1 +kDaikin2BitMark LITERAL1 +kDaikin2BitMold LITERAL1 +kDaikin2BitPower LITERAL1 +kDaikin2BitPurify LITERAL1 +kDaikin2BitSleepTimer LITERAL1 +kDaikin2Bits LITERAL1 +kDaikin2DefaultRepeat LITERAL1 +kDaikin2Freq LITERAL1 +kDaikin2Gap LITERAL1 +kDaikin2HdrMark LITERAL1 +kDaikin2HdrSpace LITERAL1 +kDaikin2LeaderMark LITERAL1 +kDaikin2LeaderSpace LITERAL1 +kDaikin2LightMask LITERAL1 +kDaikin2MinCoolTemp LITERAL1 +kDaikin2OneSpace LITERAL1 +kDaikin2Section1Length LITERAL1 +kDaikin2Section2Length LITERAL1 +kDaikin2Sections LITERAL1 +kDaikin2StateLength LITERAL1 +kDaikin2SwingHAuto LITERAL1 +kDaikin2SwingHSwing LITERAL1 +kDaikin2SwingVAuto LITERAL1 +kDaikin2SwingVBreeze LITERAL1 +kDaikin2SwingVCirculate LITERAL1 +kDaikin2SwingVHigh LITERAL1 +kDaikin2SwingVLow LITERAL1 +kDaikin2Tolerance LITERAL1 +kDaikin2ZeroSpace LITERAL1 kDaikinAuto LITERAL1 +kDaikinBeepLoud LITERAL1 +kDaikinBeepOff LITERAL1 +kDaikinBeepQuiet LITERAL1 +kDaikinBitComfort LITERAL1 kDaikinBitEcono LITERAL1 kDaikinBitEye LITERAL1 kDaikinBitMark LITERAL1 @@ -839,18 +1040,33 @@ kDaikinBitPowerful LITERAL1 kDaikinBitSensor LITERAL1 kDaikinBitSilent LITERAL1 kDaikinBits LITERAL1 +kDaikinBitsShort LITERAL1 +kDaikinByteChecksum1 LITERAL1 +kDaikinByteChecksum2 LITERAL1 +kDaikinByteChecksum3 LITERAL1 +kDaikinByteClockMinsHigh LITERAL1 +kDaikinByteClockMinsLow LITERAL1 +kDaikinByteComfort LITERAL1 kDaikinByteEcono LITERAL1 kDaikinByteEye LITERAL1 +kDaikinByteFan LITERAL1 kDaikinByteMold LITERAL1 kDaikinByteOffTimer LITERAL1 +kDaikinByteOffTimerMinsHigh LITERAL1 +kDaikinByteOffTimerMinsLow LITERAL1 kDaikinByteOnTimer LITERAL1 +kDaikinByteOnTimerMinsHigh LITERAL1 +kDaikinByteOnTimerMinsLow LITERAL1 kDaikinBytePower LITERAL1 kDaikinBytePowerful LITERAL1 kDaikinByteSensor LITERAL1 kDaikinByteSilent LITERAL1 +kDaikinByteSwingH LITERAL1 +kDaikinByteTemp LITERAL1 kDaikinCool LITERAL1 kDaikinCurBit LITERAL1 kDaikinCurIndex LITERAL1 +kDaikinDefaultRepeat LITERAL1 kDaikinDry LITERAL1 kDaikinFan LITERAL1 kDaikinFanAuto LITERAL1 @@ -861,15 +1077,25 @@ kDaikinFirstHeader64 LITERAL1 kDaikinGap LITERAL1 kDaikinHdrMark LITERAL1 kDaikinHdrSpace LITERAL1 +kDaikinHeaderLength LITERAL1 kDaikinHeat LITERAL1 +kDaikinLightBright LITERAL1 +kDaikinLightDim LITERAL1 +kDaikinLightOff LITERAL1 kDaikinMarkExcess LITERAL1 kDaikinMaxTemp LITERAL1 kDaikinMinTemp LITERAL1 kDaikinOneSpace LITERAL1 -kDaikinRawBits LITERAL1 +kDaikinSection1Length LITERAL1 +kDaikinSection2Length LITERAL1 +kDaikinSection3Length LITERAL1 +kDaikinSections LITERAL1 kDaikinStateLength LITERAL1 +kDaikinStateLengthShort LITERAL1 kDaikinTolerance LITERAL1 +kDaikinUnusedTime LITERAL1 kDaikinZeroSpace LITERAL1 +kDefaultMessageGap LITERAL1 kDenonBitMark LITERAL1 kDenonBitMarkTicks LITERAL1 kDenonBits LITERAL1 @@ -902,6 +1128,7 @@ kDishRptSpaceTicks LITERAL1 kDishTick LITERAL1 kDishZeroSpace LITERAL1 kDishZeroSpaceTicks LITERAL1 +kDry LITERAL1 kDutyDefault LITERAL1 kDutyMax LITERAL1 kElectraAcBitMark LITERAL1 @@ -912,6 +1139,7 @@ kElectraAcMessageGap LITERAL1 kElectraAcOneSpace LITERAL1 kElectraAcStateLength LITERAL1 kElectraAcZeroSpace LITERAL1 +kFan LITERAL1 kFnvBasis32 LITERAL1 kFnvPrime32 LITERAL1 kFooter LITERAL1 @@ -969,10 +1197,13 @@ kGreeBits LITERAL1 kGreeBlockFooter LITERAL1 kGreeBlockFooterBits LITERAL1 kGreeCool LITERAL1 +kGreeDefaultRepeat LITERAL1 kGreeDry LITERAL1 kGreeFan LITERAL1 +kGreeFanAuto LITERAL1 kGreeFanMask LITERAL1 kGreeFanMax LITERAL1 +kGreeFanMin LITERAL1 kGreeHdrMark LITERAL1 kGreeHdrSpace LITERAL1 kGreeHeat LITERAL1 @@ -1020,6 +1251,7 @@ kHaierAcCmdTimerCancel LITERAL1 kHaierAcCmdTimerSet LITERAL1 kHaierAcCool LITERAL1 kHaierAcDefTemp LITERAL1 +kHaierAcDefaultRepeat LITERAL1 kHaierAcDry LITERAL1 kHaierAcFan LITERAL1 kHaierAcFanAuto LITERAL1 @@ -1050,6 +1282,7 @@ kHaierAcYrw02ButtonTempDown LITERAL1 kHaierAcYrw02ButtonTempUp LITERAL1 kHaierAcYrw02ButtonTurbo LITERAL1 kHaierAcYrw02Cool LITERAL1 +kHaierAcYrw02DefaultRepeat LITERAL1 kHaierAcYrw02Dry LITERAL1 kHaierAcYrw02Fan LITERAL1 kHaierAcYrw02FanAuto LITERAL1 @@ -1071,17 +1304,32 @@ kHaierAcYrw02TurboLow LITERAL1 kHaierAcYrw02TurboOff LITERAL1 kHaierAcZeroSpace LITERAL1 kHeader LITERAL1 +kHeat LITERAL1 +kHigh LITERAL1 +kHighest LITERAL1 kHitachiAc1Bits LITERAL1 kHitachiAc1HdrMark LITERAL1 kHitachiAc1HdrSpace LITERAL1 kHitachiAc1StateLength LITERAL1 kHitachiAc2Bits LITERAL1 kHitachiAc2StateLength LITERAL1 +kHitachiAcAuto LITERAL1 +kHitachiAcAutoTemp LITERAL1 kHitachiAcBitMark LITERAL1 kHitachiAcBits LITERAL1 +kHitachiAcCool LITERAL1 +kHitachiAcDefaultRepeat LITERAL1 +kHitachiAcDry LITERAL1 +kHitachiAcFan LITERAL1 +kHitachiAcFanAuto LITERAL1 +kHitachiAcFanHigh LITERAL1 +kHitachiAcFanLow LITERAL1 kHitachiAcHdrMark LITERAL1 kHitachiAcHdrSpace LITERAL1 +kHitachiAcHeat LITERAL1 +kHitachiAcMaxTemp LITERAL1 kHitachiAcMinGap LITERAL1 +kHitachiAcMinTemp LITERAL1 kHitachiAcOneSpace LITERAL1 kHitachiAcStateLength LITERAL1 kHitachiAcZeroSpace LITERAL1 @@ -1113,6 +1361,7 @@ kKelvinatorChecksumStart LITERAL1 kKelvinatorCmdFooter LITERAL1 kKelvinatorCmdFooterBits LITERAL1 kKelvinatorCool LITERAL1 +kKelvinatorDefaultRepeat LITERAL1 kKelvinatorDry LITERAL1 kKelvinatorFan LITERAL1 kKelvinatorFanAuto LITERAL1 @@ -1159,6 +1408,16 @@ kLasertagMinRepeat LITERAL1 kLasertagMinSamples LITERAL1 kLasertagTick LITERAL1 kLasertagTolerance LITERAL1 +kLastDecodeType LITERAL1 +kLeft LITERAL1 +kLeftMax LITERAL1 +kLegoPfBitMark LITERAL1 +kLegoPfBits LITERAL1 +kLegoPfHdrSpace LITERAL1 +kLegoPfMinCommandLength LITERAL1 +kLegoPfMinRepeat LITERAL1 +kLegoPfOneSpace LITERAL1 +kLegoPfZeroSpace LITERAL1 kLg2BitMark LITERAL1 kLg2BitMarkTicks LITERAL1 kLg2HdrMark LITERAL1 @@ -1190,6 +1449,8 @@ kLgRptSpaceTicks LITERAL1 kLgTick LITERAL1 kLgZeroSpace LITERAL1 kLgZeroSpaceTicks LITERAL1 +kLow LITERAL1 +kLowest LITERAL1 kLutronBits LITERAL1 kLutronDelta LITERAL1 kLutronGap LITERAL1 @@ -1213,8 +1474,11 @@ kMagiquestBits LITERAL1 kMark LITERAL1 kMarkExcess LITERAL1 kMarkState LITERAL1 +kMax LITERAL1 kMaxAccurateUsecDelay LITERAL1 kMaxTimeoutMs LITERAL1 +kMedium LITERAL1 +kMiddle LITERAL1 kMideaACAuto LITERAL1 kMideaACChecksumMask LITERAL1 kMideaACCool LITERAL1 @@ -1251,6 +1515,7 @@ kMideaTick LITERAL1 kMideaTolerance LITERAL1 kMideaZeroSpace LITERAL1 kMideaZeroSpaceTicks LITERAL1 +kMin LITERAL1 kMitsubishi2BitMark LITERAL1 kMitsubishi2HdrMark LITERAL1 kMitsubishi2HdrSpace LITERAL1 @@ -1287,6 +1552,91 @@ kMitsubishiAcZeroSpace LITERAL1 kMitsubishiBitMark LITERAL1 kMitsubishiBitMarkTicks LITERAL1 kMitsubishiBits LITERAL1 +kMitsubishiHeavy152Bits LITERAL1 +kMitsubishiHeavy152FanAuto LITERAL1 +kMitsubishiHeavy152FanEcono LITERAL1 +kMitsubishiHeavy152FanHigh LITERAL1 +kMitsubishiHeavy152FanLow LITERAL1 +kMitsubishiHeavy152FanMax LITERAL1 +kMitsubishiHeavy152FanMed LITERAL1 +kMitsubishiHeavy152FanTurbo LITERAL1 +kMitsubishiHeavy152MinRepeat LITERAL1 +kMitsubishiHeavy152StateLength LITERAL1 +kMitsubishiHeavy152SwingHAuto LITERAL1 +kMitsubishiHeavy152SwingHLeft LITERAL1 +kMitsubishiHeavy152SwingHLeftMax LITERAL1 +kMitsubishiHeavy152SwingHLeftRight LITERAL1 +kMitsubishiHeavy152SwingHMask LITERAL1 +kMitsubishiHeavy152SwingHMiddle LITERAL1 +kMitsubishiHeavy152SwingHOff LITERAL1 +kMitsubishiHeavy152SwingHRight LITERAL1 +kMitsubishiHeavy152SwingHRightLeft LITERAL1 +kMitsubishiHeavy152SwingHRightMax LITERAL1 +kMitsubishiHeavy152SwingVAuto LITERAL1 +kMitsubishiHeavy152SwingVHigh LITERAL1 +kMitsubishiHeavy152SwingVHighest LITERAL1 +kMitsubishiHeavy152SwingVLow LITERAL1 +kMitsubishiHeavy152SwingVLowest LITERAL1 +kMitsubishiHeavy152SwingVMask LITERAL1 +kMitsubishiHeavy152SwingVMiddle LITERAL1 +kMitsubishiHeavy152SwingVOff LITERAL1 +kMitsubishiHeavy3DMask LITERAL1 +kMitsubishiHeavy88Bits LITERAL1 +kMitsubishiHeavy88CleanBit LITERAL1 +kMitsubishiHeavy88FanAuto LITERAL1 +kMitsubishiHeavy88FanEcono LITERAL1 +kMitsubishiHeavy88FanHigh LITERAL1 +kMitsubishiHeavy88FanLow LITERAL1 +kMitsubishiHeavy88FanMask LITERAL1 +kMitsubishiHeavy88FanMed LITERAL1 +kMitsubishiHeavy88FanTurbo LITERAL1 +kMitsubishiHeavy88MinRepeat LITERAL1 +kMitsubishiHeavy88StateLength LITERAL1 +kMitsubishiHeavy88SwingH3D LITERAL1 +kMitsubishiHeavy88SwingHAuto LITERAL1 +kMitsubishiHeavy88SwingHLeft LITERAL1 +kMitsubishiHeavy88SwingHLeftMax LITERAL1 +kMitsubishiHeavy88SwingHLeftRight LITERAL1 +kMitsubishiHeavy88SwingHMask LITERAL1 +kMitsubishiHeavy88SwingHMiddle LITERAL1 +kMitsubishiHeavy88SwingHOff LITERAL1 +kMitsubishiHeavy88SwingHRight LITERAL1 +kMitsubishiHeavy88SwingHRightLeft LITERAL1 +kMitsubishiHeavy88SwingHRightMax LITERAL1 +kMitsubishiHeavy88SwingVAuto LITERAL1 +kMitsubishiHeavy88SwingVHigh LITERAL1 +kMitsubishiHeavy88SwingVHighest LITERAL1 +kMitsubishiHeavy88SwingVLow LITERAL1 +kMitsubishiHeavy88SwingVLowest LITERAL1 +kMitsubishiHeavy88SwingVMask LITERAL1 +kMitsubishiHeavy88SwingVMaskByte5 LITERAL1 +kMitsubishiHeavy88SwingVMaskByte7 LITERAL1 +kMitsubishiHeavy88SwingVMiddle LITERAL1 +kMitsubishiHeavy88SwingVOff LITERAL1 +kMitsubishiHeavyAuto LITERAL1 +kMitsubishiHeavyBitMark LITERAL1 +kMitsubishiHeavyCleanBit LITERAL1 +kMitsubishiHeavyCool LITERAL1 +kMitsubishiHeavyDry LITERAL1 +kMitsubishiHeavyFan LITERAL1 +kMitsubishiHeavyFanMask LITERAL1 +kMitsubishiHeavyFilterBit LITERAL1 +kMitsubishiHeavyGap LITERAL1 +kMitsubishiHeavyHdrMark LITERAL1 +kMitsubishiHeavyHdrSpace LITERAL1 +kMitsubishiHeavyHeat LITERAL1 +kMitsubishiHeavyMaxTemp LITERAL1 +kMitsubishiHeavyMinTemp LITERAL1 +kMitsubishiHeavyModeMask LITERAL1 +kMitsubishiHeavyNightBit LITERAL1 +kMitsubishiHeavyOneSpace LITERAL1 +kMitsubishiHeavyPowerBit LITERAL1 +kMitsubishiHeavySigLength LITERAL1 +kMitsubishiHeavySilentBit LITERAL1 +kMitsubishiHeavyTempMask LITERAL1 +kMitsubishiHeavyZeroSpace LITERAL1 +kMitsubishiHeavyZjsSig LITERAL1 +kMitsubishiHeavyZmsSig LITERAL1 kMitsubishiMinCommandLength LITERAL1 kMitsubishiMinCommandLengthTicks LITERAL1 kMitsubishiMinGap LITERAL1 @@ -1331,10 +1681,12 @@ kNikaiTick LITERAL1 kNikaiZeroSpace LITERAL1 kNikaiZeroSpaceTicks LITERAL1 kNoRepeat LITERAL1 +kOff LITERAL1 kPanasonicAcAuto LITERAL1 kPanasonicAcBits LITERAL1 kPanasonicAcChecksumInit LITERAL1 kPanasonicAcCool LITERAL1 +kPanasonicAcDefaultRepeat LITERAL1 kPanasonicAcDry LITERAL1 kPanasonicAcExcess LITERAL1 kPanasonicAcFan LITERAL1 @@ -1393,6 +1745,7 @@ kPanasonicMinGapTicks LITERAL1 kPanasonicNke LITERAL1 kPanasonicOneSpace LITERAL1 kPanasonicOneSpaceTicks LITERAL1 +kPanasonicRkr LITERAL1 kPanasonicTick LITERAL1 kPanasonicUnknown LITERAL1 kPanasonicZeroSpace LITERAL1 @@ -1450,6 +1803,9 @@ kRcmmRptLengthTicks LITERAL1 kRcmmTick LITERAL1 kRcmmTolerance LITERAL1 kRepeat LITERAL1 +kRight LITERAL1 +kRightMax LITERAL1 +kSamsung36Bits LITERAL1 kSamsungACSectionLength LITERAL1 kSamsungAcAuto LITERAL1 kSamsungAcAutoTemp LITERAL1 @@ -1459,6 +1815,7 @@ kSamsungAcBits LITERAL1 kSamsungAcCleanMask10 LITERAL1 kSamsungAcCleanMask11 LITERAL1 kSamsungAcCool LITERAL1 +kSamsungAcDefaultRepeat LITERAL1 kSamsungAcDry LITERAL1 kSamsungAcExtendedBits LITERAL1 kSamsungAcExtendedStateLength LITERAL1 @@ -1569,6 +1926,65 @@ kSpaceState LITERAL1 kStartOffset LITERAL1 kStateSizeMax LITERAL1 kStopState LITERAL1 +kTcl112AcAuto LITERAL1 +kTcl112AcBitEcono LITERAL1 +kTcl112AcBitHealth LITERAL1 +kTcl112AcBitLight LITERAL1 +kTcl112AcBitMark LITERAL1 +kTcl112AcBitSwingH LITERAL1 +kTcl112AcBitSwingV LITERAL1 +kTcl112AcBitTurbo LITERAL1 +kTcl112AcBits LITERAL1 +kTcl112AcCool LITERAL1 +kTcl112AcDefaultRepeat LITERAL1 +kTcl112AcDry LITERAL1 +kTcl112AcFan LITERAL1 +kTcl112AcFanAuto LITERAL1 +kTcl112AcFanHigh LITERAL1 +kTcl112AcFanLow LITERAL1 +kTcl112AcFanMask LITERAL1 +kTcl112AcFanMed LITERAL1 +kTcl112AcGap LITERAL1 +kTcl112AcHalfDegree LITERAL1 +kTcl112AcHdrMark LITERAL1 +kTcl112AcHdrSpace LITERAL1 +kTcl112AcHeat LITERAL1 +kTcl112AcOneSpace LITERAL1 +kTcl112AcPowerMask LITERAL1 +kTcl112AcStateLength LITERAL1 +kTcl112AcTempMax LITERAL1 +kTcl112AcTempMin LITERAL1 +kTcl112AcZeroSpace LITERAL1 +kTecoAuto LITERAL1 +kTecoBitMark LITERAL1 +kTecoBits LITERAL1 +kTecoCool LITERAL1 +kTecoDefaultRepeat LITERAL1 +kTecoDry LITERAL1 +kTecoFan LITERAL1 +kTecoFanAuto LITERAL1 +kTecoFanHigh LITERAL1 +kTecoFanLow LITERAL1 +kTecoFanMask LITERAL1 +kTecoFanMed LITERAL1 +kTecoGap LITERAL1 +kTecoHdrMark LITERAL1 +kTecoHdrSpace LITERAL1 +kTecoHeat LITERAL1 +kTecoMaxTemp LITERAL1 +kTecoMinTemp LITERAL1 +kTecoModeMask LITERAL1 +kTecoOneSpace LITERAL1 +kTecoPower LITERAL1 +kTecoReset LITERAL1 +kTecoSleep LITERAL1 +kTecoSwing LITERAL1 +kTecoTempMask LITERAL1 +kTecoTimerHalfH LITERAL1 +kTecoTimerOn LITERAL1 +kTecoTimerTenHr LITERAL1 +kTecoTimerUniHr LITERAL1 +kTecoZeroSpace LITERAL1 kTimeoutMs LITERAL1 kTolerance LITERAL1 kToshibaACBits LITERAL1 @@ -1592,6 +2008,7 @@ kToshibaAcZeroSpace LITERAL1 kTrotecAuto LITERAL1 kTrotecCool LITERAL1 kTrotecDefTemp LITERAL1 +kTrotecDefaultRepeat LITERAL1 kTrotecDry LITERAL1 kTrotecFan LITERAL1 kTrotecFanHigh LITERAL1 @@ -1606,26 +2023,119 @@ kTrotecIntro2 LITERAL1 kTrotecMaxTemp LITERAL1 kTrotecMaxTimer LITERAL1 kTrotecMinTemp LITERAL1 -kTrotecMinTimer LITERAL1 -kTrotecOff LITERAL1 -kTrotecOn LITERAL1 kTrotecOneMark LITERAL1 kTrotecOneSpace LITERAL1 -kTrotecSleepOn LITERAL1 +kTrotecPowerBit LITERAL1 +kTrotecSleepBit LITERAL1 kTrotecStateLength LITERAL1 -kTrotecTimerOn LITERAL1 +kTrotecTimerBit LITERAL1 kTrotecZeroMark LITERAL1 kTrotecZeroSpace LITERAL1 kUnknownThreshold LITERAL1 +kVestelAcAuto LITERAL1 +kVestelAcBitMark LITERAL1 +kVestelAcBits LITERAL1 +kVestelAcCRCMask LITERAL1 +kVestelAcChecksumOffset LITERAL1 +kVestelAcCool LITERAL1 +kVestelAcDry LITERAL1 +kVestelAcFan LITERAL1 +kVestelAcFanAuto LITERAL1 +kVestelAcFanAutoCool LITERAL1 +kVestelAcFanAutoHot LITERAL1 +kVestelAcFanHigh LITERAL1 +kVestelAcFanLow LITERAL1 +kVestelAcFanMed LITERAL1 +kVestelAcFanOffset LITERAL1 +kVestelAcHdrMark LITERAL1 +kVestelAcHdrSpace LITERAL1 +kVestelAcHeat LITERAL1 +kVestelAcHourOffset LITERAL1 +kVestelAcIon LITERAL1 +kVestelAcIonOffset LITERAL1 +kVestelAcMaxTemp LITERAL1 +kVestelAcMinTempC LITERAL1 +kVestelAcMinTempH LITERAL1 +kVestelAcMinuteOffset LITERAL1 +kVestelAcModeOffset LITERAL1 +kVestelAcNormal LITERAL1 +kVestelAcOffTimeOffset LITERAL1 +kVestelAcOffTimerFlagOffset LITERAL1 +kVestelAcOnTimeOffset LITERAL1 +kVestelAcOnTimerFlagOffset LITERAL1 +kVestelAcOneSpace LITERAL1 +kVestelAcPowerOffset LITERAL1 +kVestelAcSleep LITERAL1 +kVestelAcStateDefault LITERAL1 +kVestelAcSwing LITERAL1 +kVestelAcSwingOffset LITERAL1 +kVestelAcTempOffset LITERAL1 +kVestelAcTimeStateDefault LITERAL1 +kVestelAcTimerFlagOffset LITERAL1 +kVestelAcTolerance LITERAL1 +kVestelAcTurbo LITERAL1 +kVestelAcTurboSleepOffset LITERAL1 +kVestelAcZeroSpace LITERAL1 +kWhirlpoolAcAltTempMask LITERAL1 +kWhirlpoolAcAltTempPos LITERAL1 +kWhirlpoolAcAuto LITERAL1 +kWhirlpoolAcAutoTemp LITERAL1 kWhirlpoolAcBitMark LITERAL1 kWhirlpoolAcBits LITERAL1 +kWhirlpoolAcChecksumByte1 LITERAL1 +kWhirlpoolAcChecksumByte2 LITERAL1 +kWhirlpoolAcClockPos LITERAL1 +kWhirlpoolAcCommand6thSense LITERAL1 +kWhirlpoolAcCommandFanSpeed LITERAL1 +kWhirlpoolAcCommandIFeel LITERAL1 +kWhirlpoolAcCommandLight LITERAL1 +kWhirlpoolAcCommandMode LITERAL1 +kWhirlpoolAcCommandOffTimer LITERAL1 +kWhirlpoolAcCommandOnTimer LITERAL1 +kWhirlpoolAcCommandPos LITERAL1 +kWhirlpoolAcCommandPower LITERAL1 +kWhirlpoolAcCommandSleep LITERAL1 +kWhirlpoolAcCommandSuper LITERAL1 +kWhirlpoolAcCommandSwing LITERAL1 +kWhirlpoolAcCommandTemp LITERAL1 +kWhirlpoolAcCool LITERAL1 +kWhirlpoolAcDefaultRepeat LITERAL1 +kWhirlpoolAcDry LITERAL1 +kWhirlpoolAcFan LITERAL1 +kWhirlpoolAcFanAuto LITERAL1 +kWhirlpoolAcFanHigh LITERAL1 +kWhirlpoolAcFanLow LITERAL1 +kWhirlpoolAcFanMask LITERAL1 +kWhirlpoolAcFanMedium LITERAL1 +kWhirlpoolAcFanPos LITERAL1 kWhirlpoolAcGap LITERAL1 kWhirlpoolAcHdrMark LITERAL1 kWhirlpoolAcHdrSpace LITERAL1 +kWhirlpoolAcHeat LITERAL1 +kWhirlpoolAcHourMask LITERAL1 +kWhirlpoolAcLightMask LITERAL1 +kWhirlpoolAcMaxTemp LITERAL1 kWhirlpoolAcMinGap LITERAL1 +kWhirlpoolAcMinTemp LITERAL1 +kWhirlpoolAcMinuteMask LITERAL1 +kWhirlpoolAcModeMask LITERAL1 +kWhirlpoolAcModePos LITERAL1 +kWhirlpoolAcOffTimerPos LITERAL1 +kWhirlpoolAcOnTimerPos LITERAL1 kWhirlpoolAcOneSpace LITERAL1 +kWhirlpoolAcPowerToggleMask LITERAL1 +kWhirlpoolAcPowerTogglePos LITERAL1 kWhirlpoolAcSections LITERAL1 +kWhirlpoolAcSleepMask LITERAL1 +kWhirlpoolAcSleepPos LITERAL1 kWhirlpoolAcStateLength LITERAL1 +kWhirlpoolAcSuperMask LITERAL1 +kWhirlpoolAcSuperPos LITERAL1 +kWhirlpoolAcSwing1Mask LITERAL1 +kWhirlpoolAcSwing2Mask LITERAL1 +kWhirlpoolAcTempMask LITERAL1 +kWhirlpoolAcTempPos LITERAL1 +kWhirlpoolAcTimerEnableMask LITERAL1 kWhirlpoolAcZeroSpace LITERAL1 kWhynterBitMark LITERAL1 kWhynterBitMarkTicks LITERAL1 diff --git a/lib/IRremoteESP8266-2.5.2.03/library.json b/lib/IRremoteESP8266-2.6.0/library.json similarity index 97% rename from lib/IRremoteESP8266-2.5.2.03/library.json rename to lib/IRremoteESP8266-2.6.0/library.json index 3fc14f027e16..95867de1db7f 100644 --- a/lib/IRremoteESP8266-2.5.2.03/library.json +++ b/lib/IRremoteESP8266-2.6.0/library.json @@ -1,6 +1,6 @@ { "name": "IRremoteESP8266", - "version": "2.5.2", + "version": "2.6.0", "keywords": "infrared, ir, remote, esp8266", "description": "Send and receive infrared signals with multiple protocols (ESP8266)", "repository": diff --git a/lib/IRremoteESP8266-2.5.2.03/library.properties b/lib/IRremoteESP8266-2.6.0/library.properties similarity index 96% rename from lib/IRremoteESP8266-2.5.2.03/library.properties rename to lib/IRremoteESP8266-2.6.0/library.properties index e71dc41542ef..f122067c5386 100644 --- a/lib/IRremoteESP8266-2.5.2.03/library.properties +++ b/lib/IRremoteESP8266-2.6.0/library.properties @@ -1,5 +1,5 @@ name=IRremoteESP8266 -version=2.5.2 +version=2.6.0 author=Sebastien Warin, Mark Szabo, Ken Shirriff, David Conran maintainer=Mark Szabo, David Conran, Sebastien Warin, Roi Dayan, Massimiliano Pinto sentence=Send and receive infrared signals with multiple protocols (ESP8266) diff --git a/lib/IRremoteESP8266-2.5.2.03/platformio.ini b/lib/IRremoteESP8266-2.6.0/platformio.ini similarity index 83% rename from lib/IRremoteESP8266-2.5.2.03/platformio.ini rename to lib/IRremoteESP8266-2.6.0/platformio.ini index 63c3781e1167..b6020c165c4e 100644 --- a/lib/IRremoteESP8266-2.5.2.03/platformio.ini +++ b/lib/IRremoteESP8266-2.6.0/platformio.ini @@ -6,11 +6,13 @@ src_dir = examples/IRrecvDumpV2 build_flags = lib_deps_builtin = lib_deps_external = +lib_ldf_mode = chain+ [env:nodemcuv2] platform = espressif8266 framework = arduino board = nodemcuv2 +lib_ldf_mode = ${common.lib_ldf_mode} build_flags = ${common.build_flags} lib_deps = ${common.lib_deps_builtin} @@ -20,6 +22,7 @@ lib_deps = platform = espressif8266 framework = arduino board = d1_mini +lib_ldf_mode = ${common.lib_ldf_mode} build_flags = ${common.build_flags} lib_deps = ${common.lib_deps_builtin} diff --git a/lib/IRremoteESP8266-2.5.2.03/pylintrc b/lib/IRremoteESP8266-2.6.0/pylintrc similarity index 100% rename from lib/IRremoteESP8266-2.5.2.03/pylintrc rename to lib/IRremoteESP8266-2.6.0/pylintrc diff --git a/lib/IRremoteESP8266-2.5.2.03/src/CPPLINT.cfg b/lib/IRremoteESP8266-2.6.0/src/CPPLINT.cfg similarity index 100% rename from lib/IRremoteESP8266-2.5.2.03/src/CPPLINT.cfg rename to lib/IRremoteESP8266-2.6.0/src/CPPLINT.cfg diff --git a/lib/IRremoteESP8266-2.6.0/src/IRac.cpp b/lib/IRremoteESP8266-2.6.0/src/IRac.cpp new file mode 100644 index 000000000000..782c147c20d6 --- /dev/null +++ b/lib/IRremoteESP8266-2.6.0/src/IRac.cpp @@ -0,0 +1,1125 @@ +// 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 off most common functionallity across all supported devices. + +#include "IRac.h" +#ifndef UNIT_TEST +#include +#endif + +#include +#ifndef ARDUINO +#include +#endif +#include "IRsend.h" +#include "IRremoteESP8266.h" +#include "ir_Argo.h" +#include "ir_Coolix.h" +#include "ir_Daikin.h" +#include "ir_Fujitsu.h" +#include "ir_Haier.h" +#include "ir_Hitachi.h" +#include "ir_Kelvinator.h" +#include "ir_Midea.h" +#include "ir_Mitsubishi.h" +#include "ir_MitsubishiHeavy.h" +#include "ir_Panasonic.h" +#include "ir_Samsung.h" +#include "ir_Tcl.h" +#include "ir_Teco.h" +#include "ir_Toshiba.h" +#include "ir_Trotec.h" +#include "ir_Vestel.h" +#include "ir_Whirlpool.h" + +IRac::IRac(uint8_t pin) { _pin = pin; } + +// Is the given protocol supported by the IRac class? +bool IRac::isProtocolSupported(const decode_type_t protocol) { + switch (protocol) { +#if SEND_ARGO + case decode_type_t::ARGO: +#endif +#if SEND_COOLIX + case decode_type_t::COOLIX: +#endif +#if SEND_DAIKIN + case decode_type_t::DAIKIN: +#endif +#if SEND_DAIKIN2 + case decode_type_t::DAIKIN2: +#endif +#if SEND_DAIKIN216 + case decode_type_t::DAIKIN216: +#endif +#if SEND_FUJITSU_AC + case decode_type_t::FUJITSU_AC: +#endif +#if SEND_GREE + case decode_type_t::GREE: +#endif +#if SEND_HAIER_AC + case decode_type_t::HAIER_AC: +#endif +#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_KELVINATOR + case decode_type_t::KELVINATOR: +#endif +#if SEND_MIDEA + case decode_type_t::MIDEA: +#endif +#if SEND_MITSUBISHI_AC + case decode_type_t::MITSUBISHI_AC: +#endif +#if SEND_MITSUBISHIHEAVY + case decode_type_t::MITSUBISHI_HEAVY_88: + case decode_type_t::MITSUBISHI_HEAVY_152: +#endif +#if SEND_PANASONIC_AC + case decode_type_t::PANASONIC_AC: +#endif +#if SEND_SAMSUNG_AC + case decode_type_t::SAMSUNG_AC: +#endif +#if SEND_TCL112AC + case decode_type_t::TCL112AC: +#endif +#if SEND_TECO + case decode_type_t::TECO: +#endif +#if SEND_TOSHIBA_AC + case decode_type_t::TOSHIBA_AC: +#endif +#if SEND_TROTEC + case decode_type_t::TROTEC: +#endif +#if SEND_VESTEL_AC + case decode_type_t::VESTEL_AC: +#endif +#if SEND_WHIRLPOOL_AC + case decode_type_t::WHIRLPOOL_AC: +#endif + return true; + default: + return false; + } +} + +#if SEND_ARGO +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->setPower(on); + switch (mode) { + case stdAc::opmode_t::kCool: + ac->setCoolMode(kArgoCoolOn); + break; + case stdAc::opmode_t::kHeat: + ac->setHeatMode(kArgoHeatOn); + break; + case stdAc::opmode_t::kDry: + ac->setCoolMode(kArgoCoolHum); + break; + default: // No idea how to set Fan mode. + ac->setCoolMode(kArgoCoolAuto); + } + 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_COOLIX +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->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(); + } + 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(); + } + // Power gets done last, as off has a special command. + ac->setPower(on); + ac->send(); +} +#endif // SEND_COOLIX + +#if SEND_DAIKIN +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->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_DAIKIN2 +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->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setSwingVertical(ac->convertSwingV(swingv)); + ac->setSwingHorizontal((int8_t)swingh >= 0); + ac->setQuiet(quiet); + ac->setLight(light); + ac->setPowerful(turbo); + ac->setEcono(econo); + ac->setPurify(filter); + ac->setMold(clean); + ac->setBeep(beep); + if (sleep > 0) ac->enableSleepTimer(sleep); + if (clock >= 0) ac->setCurrentTime(clock); + ac->send(); +} +#endif // SEND_DAIKIN2 + +#if SEND_DAIKIN216 +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) { + 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->send(); +} +#endif // SEND_DAIKIN216 + +#if SEND_FUJITSU_AC +void IRac::fujitsu(IRFujitsuAC *ac, const fujitsu_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) { + ac->setModel(model); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + 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 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. + // No Clock setting available. + if (!on) ac->off(); + ac->send(); +} +#endif // SEND_FUJITSU_AC + +#if SEND_GREE +void IRac::gree(IRGreeAC *ac, + const bool on, const stdAc::opmode_t mode, const float degrees, + const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv, + const bool turbo, const bool light, const bool clean, + const int16_t sleep) { + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + 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 Filter setting available. + // No Beep setting available. + // No Quiet setting available. + // No Clock setting available. + ac->send(); +} +#endif // SEND_GREE + +#if SEND_HAIER_AC +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->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_AC_YRW02 +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 filter, const int16_t sleep) { + 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. + 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 +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->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_KELVINATOR +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->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_MIDEA +void IRac::midea(IRMideaAC *ac, + const bool on, const stdAc::opmode_t mode, const float degrees, + const stdAc::fanspeed_t fan, const int16_t sleep) { + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees, true); // true means use Celsius. + ac->setFan(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 on this A/C is either on or off. + // No Clock setting available. + ac->send(); +} +#endif // SEND_MIDEA + +#if SEND_MITSUBISHI_AC +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 bool quiet, const int16_t clock) { + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setVane(ac->convertSwingV(swingv)); + // No Horizontal swing setting available. + 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_MITSUBISHIHEAVY +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->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(); +} + +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->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_PANASONIC_AC +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 int16_t clock) { + 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); + // 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_PANASONIC_AC + +#if SEND_SAMSUNG_AC +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 clean, + const bool beep, const bool sendOnOffHack) { + if (sendOnOffHack) { + // Use a hack to for the unit on or off. + // See: https://github.com/markszabo/IRremoteESP8266/issues/604#issuecomment-475020036 + if (on) + ac->sendOn(); + else + ac->sendOff(); + } + 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); + if (turbo) ac->setFan(kSamsungAcFanTurbo); + // No Light setting available. + // No Econo setting available. + // No Filter setting available. + 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_TCL112AC +void IRac::tcl112(IRTcl112Ac *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 econo, + const bool filter) { + 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. + 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_TECO +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 int16_t sleep) { + 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. + // 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_TECO + +#if SEND_TOSHIBA_AC +void IRac::toshiba(IRToshibaAC *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan) { + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(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. + // No Sleep setting available. + // No Clock setting available. + ac->send(); +} +#endif // SEND_TOSHIBA_AC + +#if SEND_TROTEC +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->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_VESTEL_AC +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->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_WHIRLPOOL_AC +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->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 + +// Send A/C message for a given device using common A/C settings. +// Args: +// vendor: The type of A/C protocol to use. +// model: The specific model of A/C if supported/applicable. +// on: Should the unit be powered on? (or in some cases, toggled) +// mode: What operating mode should the unit perform? e.g. Cool, Heat etc. +// degrees: What temperature should the unit be set to? +// celsius: Use degreees Celsius, otherwise Fahrenheit. +// fan: Fan speed. +// The following args are all "if supported" by the underlying A/C classes. +// swingv: Control the vertical swing of the vanes. +// swingh: Control the horizontal swing of the vanes. +// quiet: Set the unit to quiet (fan) operation mode. +// turbo: Set the unit to turbo operating mode. e.g. Max fan & cooling etc. +// econo: Set the unit to economical operating mode. +// light: Turn on the display/LEDs etc. +// filter: Turn on any particle/ion/allergy filter etc. +// clean: Turn on any settings to reduce mold etc. (Not self-clean mode.) +// beep: Control if the unit beeps upon receiving commands. +// sleep: Nr. of mins of sleep mode, or use sleep mode. (< 0 means off.) +// clock: Nr. of mins past midnight to set the clock to. (< 0 means off.) +// Returns: +// boolean: True, if accepted/converted/attempted. 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) { + // Convert the temperature to Celsius. + float degC; + bool on = power; + if (celsius) + degC = degrees; + else + degC = (degrees - 32.0) * (5.0 / 9.0); + // A hack for Home Assistant, it appears to need/want an Off opmode. + if (mode == stdAc::opmode_t::kOff) on = false; + // Per vendor settings & setup. + switch (vendor) { +#if SEND_ARGO + case ARGO: + { + IRArgoAC ac(_pin); + argo(&ac, on, mode, degC, fan, swingv, turbo, sleep); + break; + } +#endif // SEND_DAIKIN +#if SEND_COOLIX + case COOLIX: + { + IRCoolixAC ac(_pin); + coolix(&ac, on, mode, degC, fan, swingv, swingh, + quiet, turbo, econo, clean); + break; + } +#endif // SEND_DAIKIN +#if SEND_DAIKIN + case DAIKIN: + { + IRDaikinESP ac(_pin); + daikin(&ac, on, mode, degC, fan, swingv, swingh, + quiet, turbo, econo, clean); + break; + } +#endif // SEND_DAIKIN +#if SEND_DAIKIN2 + case DAIKIN2: + { + IRDaikin2 ac(_pin); + daikin2(&ac, on, mode, degC, fan, swingv, swingh, quiet, turbo, + light, econo, filter, clean, beep, sleep, clock); + break; + } +#endif // SEND_DAIKIN216 +#if SEND_DAIKIN216 + case DAIKIN216: + { + IRDaikin216 ac(_pin); + daikin216(&ac, on, mode, degC, fan, swingv, swingh, quiet); + break; + } +#endif // SEND_DAIKIN216 +#if SEND_FUJITSU_AC + case FUJITSU_AC: + { + IRFujitsuAC ac(_pin); + ac.begin(); + fujitsu(&ac, (fujitsu_ac_remote_model_t)model, on, mode, degC, fan, + swingv, swingh, quiet); + break; + } +#endif // SEND_FUJITSU_AC +#if SEND_GREE + case GREE: + { + IRGreeAC ac(_pin); + ac.begin(); + gree(&ac, on, mode, degC, fan, swingv, light, turbo, clean, sleep); + break; + } +#endif // SEND_GREE +#if SEND_HAIER_AC + case HAIER_AC: + { + IRHaierAC ac(_pin); + ac.begin(); + haier(&ac, on, mode, degC, fan, swingv, filter, sleep, clock); + break; + } +#endif // SEND_HAIER_AC +#if SEND_HAIER_AC_YRW02 + case HAIER_AC_YRW02: + { + IRHaierACYRW02 ac(_pin); + ac.begin(); + haierYrwo2(&ac, on, mode, degC, fan, swingv, turbo, filter, sleep); + break; + } +#endif // SEND_HAIER_AC_YRW02 +#if SEND_HITACHI_AC + case HITACHI_AC: + { + IRHitachiAc ac(_pin); + ac.begin(); + hitachi(&ac, on, mode, degC, fan, swingv, swingh); + break; + } +#endif // SEND_HITACHI_AC +#if SEND_KELVINATOR + case KELVINATOR: + { + IRKelvinatorAC ac(_pin); + ac.begin(); + kelvinator(&ac, on, mode, degC, fan, swingv, swingh, quiet, turbo, + light, filter, clean); + break; + } +#endif // SEND_KELVINATOR +#if SEND_MIDEA + case MIDEA: + { + IRMideaAC ac(_pin); + ac.begin(); + midea(&ac, on, mode, degC, fan, sleep); + break; + } +#endif // SEND_MIDEA +#if SEND_MITSUBISHI_AC + case MITSUBISHI_AC: + { + IRMitsubishiAC ac(_pin); + ac.begin(); + mitsubishi(&ac, on, mode, degC, fan, swingv, quiet, clock); + break; + } +#endif // SEND_MITSUBISHI_AC +#if SEND_MITSUBISHIHEAVY + case MITSUBISHI_HEAVY_88: + { + IRMitsubishiHeavy88Ac ac(_pin); + ac.begin(); + mitsubishiHeavy88(&ac, on, mode, degC, fan, swingv, swingh, + turbo, econo, clean); + break; + } + case MITSUBISHI_HEAVY_152: + { + IRMitsubishiHeavy152Ac ac(_pin); + ac.begin(); + mitsubishiHeavy152(&ac, on, mode, degC, fan, swingv, swingh, + quiet, turbo, econo, filter, clean, sleep); + break; + } +#endif // SEND_MITSUBISHIHEAVY +#if SEND_PANASONIC_AC + case PANASONIC_AC: + { + IRPanasonicAc ac(_pin); + ac.begin(); + panasonic(&ac, (panasonic_ac_remote_model_t)model, on, mode, degC, fan, + swingv, swingh, quiet, turbo, clock); + break; + } +#endif // SEND_PANASONIC_AC +#if SEND_SAMSUNG_AC + case SAMSUNG_AC: + { + IRSamsungAc ac(_pin); + ac.begin(); + samsung(&ac, on, mode, degC, fan, swingv, quiet, turbo, clean, beep); + break; + } +#endif // SEND_SAMSUNG_AC +#if SEND_TCL112AC + case TCL112AC: + { + IRTcl112Ac ac(_pin); + ac.begin(); + tcl112(&ac, on, mode, degC, fan, swingv, swingh, turbo, light, econo, + filter); + break; + } +#endif // SEND_TCL112AC +#if SEND_TECO + case TECO: + { + IRTecoAc ac(_pin); + ac.begin(); + teco(&ac, on, mode, degC, fan, swingv, sleep); + break; + } +#endif // SEND_TECO +#if SEND_TOSHIBA_AC + case TOSHIBA_AC: + { + IRToshibaAC ac(_pin); + ac.begin(); + toshiba(&ac, on, mode, degC, fan); + break; + } +#endif // SEND_TOSHIBA_AC +#if SEND_TROTEC + case TROTEC: + { + IRTrotecESP ac(_pin); + ac.begin(); + trotec(&ac, on, mode, degC, fan, sleep); + break; + } +#endif // SEND_TROTEC +#if SEND_VESTEL_AC + case VESTEL_AC: + { + IRVestelAc ac(_pin); + ac.begin(); + vestel(&ac, on, mode, degC, fan, swingv, turbo, filter, sleep, clock); + break; + } +#endif // SEND_VESTEL_AC +#if SEND_WHIRLPOOL_AC + case WHIRLPOOL_AC: + { + IRWhirlpoolAc ac(_pin); + ac.begin(); + whirlpool(&ac, (whirlpool_ac_remote_model_t)model, on, mode, degC, fan, + swingv, turbo, light, sleep, clock); + break; + } +#endif // SEND_WHIRLPOOL_AC + default: + return false; // Fail, didn't match anything. + } + return true; // Success. +} + +stdAc::opmode_t IRac::strToOpmode(const char *str, + const stdAc::opmode_t def) { + if (!strcmp(str, "AUTO") || !strcmp(str, "AUTOMATIC")) + return stdAc::opmode_t::kAuto; + else if (!strcmp(str, "OFF") || !strcmp(str, "STOP")) + return stdAc::opmode_t::kOff; + else if (!strcmp(str, "COOL") || !strcmp(str, "COOLING")) + return stdAc::opmode_t::kCool; + else if (!strcmp(str, "HEAT") || !strcmp(str, "HEATING")) + return stdAc::opmode_t::kHeat; + else if (!strcmp(str, "DRY") || !strcmp(str, "DRYING") || + !strcmp(str, "DEHUMIDIFY")) + return stdAc::opmode_t::kDry; + else if (!strcmp(str, "FAN") || !strcmp(str, "FANONLY") || + !strcmp(str, "FAN_ONLY")) + return stdAc::opmode_t::kFan; + else + return def; +} + +stdAc::fanspeed_t IRac::strToFanspeed(const char *str, + const stdAc::fanspeed_t def) { + if (!strcmp(str, "AUTO") || !strcmp(str, "AUTOMATIC")) + return stdAc::fanspeed_t::kAuto; + else if (!strcmp(str, "MIN") || !strcmp(str, "MINIMUM") || + !strcmp(str, "LOWEST")) + return stdAc::fanspeed_t::kMin; + else if (!strcmp(str, "LOW")) + return stdAc::fanspeed_t::kLow; + else if (!strcmp(str, "MED") || !strcmp(str, "MEDIUM") || + !strcmp(str, "MID")) + return stdAc::fanspeed_t::kMedium; + else if (!strcmp(str, "HIGH") || !strcmp(str, "HI")) + return stdAc::fanspeed_t::kHigh; + else if (!strcmp(str, "MAX") || !strcmp(str, "MAXIMUM") || + !strcmp(str, "HIGHEST")) + return stdAc::fanspeed_t::kMax; + else + return def; +} + +stdAc::swingv_t IRac::strToSwingV(const char *str, + const stdAc::swingv_t def) { + if (!strcmp(str, "AUTO") || !strcmp(str, "AUTOMATIC") || + !strcmp(str, "ON") || !strcmp(str, "SWING")) + return stdAc::swingv_t::kAuto; + else if (!strcmp(str, "OFF") || !strcmp(str, "STOP")) + return stdAc::swingv_t::kOff; + else if (!strcmp(str, "MIN") || !strcmp(str, "MINIMUM") || + !strcmp(str, "LOWEST") || !strcmp(str, "BOTTOM") || + !strcmp(str, "DOWN")) + return stdAc::swingv_t::kLowest; + else if (!strcmp(str, "LOW")) + return stdAc::swingv_t::kLow; + else if (!strcmp(str, "MID") || !strcmp(str, "MIDDLE") || + !strcmp(str, "MED") || !strcmp(str, "MEDIUM") || + !strcmp(str, "CENTRE") || !strcmp(str, "CENTER")) + return stdAc::swingv_t::kMiddle; + else if (!strcmp(str, "HIGH") || !strcmp(str, "HI")) + return stdAc::swingv_t::kHigh; + else if (!strcmp(str, "HIGHEST") || !strcmp(str, "MAX") || + !strcmp(str, "MAXIMUM") || !strcmp(str, "TOP") || + !strcmp(str, "UP")) + return stdAc::swingv_t::kHighest; + else + return def; +} + +stdAc::swingh_t IRac::strToSwingH(const char *str, + const stdAc::swingh_t def) { + if (!strcmp(str, "AUTO") || !strcmp(str, "AUTOMATIC") || + !strcmp(str, "ON") || !strcmp(str, "SWING")) + return stdAc::swingh_t::kAuto; + else if (!strcmp(str, "OFF") || !strcmp(str, "STOP")) + return stdAc::swingh_t::kOff; + else if (!strcmp(str, "LEFTMAX") || !strcmp(str, "LEFT MAX") || + !strcmp(str, "MAXLEFT") || !strcmp(str, "MAX LEFT") || + !strcmp(str, "FARLEFT") || !strcmp(str, "FAR LEFT")) + return stdAc::swingh_t::kLeftMax; + else if (!strcmp(str, "LEFT")) + return stdAc::swingh_t::kLeft; + else if (!strcmp(str, "MID") || !strcmp(str, "MIDDLE") || + !strcmp(str, "MED") || !strcmp(str, "MEDIUM") || + !strcmp(str, "CENTRE") || !strcmp(str, "CENTER")) + return stdAc::swingh_t::kMiddle; + else if (!strcmp(str, "RIGHT")) + return stdAc::swingh_t::kRight; + else if (!strcmp(str, "RIGHTMAX") || !strcmp(str, "RIGHT MAX") || + !strcmp(str, "MAXRIGHT") || !strcmp(str, "MAX RIGHT") || + !strcmp(str, "FARRIGHT") || !strcmp(str, "FAR RIGHT")) + return stdAc::swingh_t::kRightMax; + else + return def; +} + +// Assumes str is upper case or an integer >= 1. +int16_t IRac::strToModel(const char *str, const int16_t def) { + // Fujitsu A/C models + if (!strcmp(str, "ARRAH2E")) { + return fujitsu_ac_remote_model_t::ARRAH2E; + } else if (!strcmp(str, "ARDB1")) { + return fujitsu_ac_remote_model_t::ARDB1; + // Panasonic A/C families + } else if (!strcmp(str, "LKE") || !strcmp(str, "PANASONICLKE")) { + return panasonic_ac_remote_model_t::kPanasonicLke; + } else if (!strcmp(str, "NKE") || !strcmp(str, "PANASONICNKE")) { + return panasonic_ac_remote_model_t::kPanasonicNke; + } else if (!strcmp(str, "DKE") || !strcmp(str, "PANASONICDKE")) { + return panasonic_ac_remote_model_t::kPanasonicDke; + } else if (!strcmp(str, "JKE") || !strcmp(str, "PANASONICJKE")) { + return panasonic_ac_remote_model_t::kPanasonicJke; + } else if (!strcmp(str, "CKP") || !strcmp(str, "PANASONICCKP")) { + return panasonic_ac_remote_model_t::kPanasonicCkp; + } else if (!strcmp(str, "RKR") || !strcmp(str, "PANASONICRKR")) { + return panasonic_ac_remote_model_t::kPanasonicRkr; + // Whirlpool A/C models + } else if (!strcmp(str, "DG11J13A") || !strcmp(str, "DG11J104") || + !strcmp(str, "DG11J1-04")) { + return whirlpool_ac_remote_model_t::DG11J13A; + } else if (!strcmp(str, "DG11J191")) { + return whirlpool_ac_remote_model_t::DG11J191; + } else { + int16_t number = atoi(str); + if (number > 0) + return number; + else + return def; + } +} + +// Assumes str is upper case. +bool IRac::strToBool(const char *str, const bool def) { + if (!strcmp(str, "ON") || !strcmp(str, "1") || !strcmp(str, "YES") || + !strcmp(str, "TRUE")) + return true; + else if (!strcmp(str, "OFF") || !strcmp(str, "0") || + !strcmp(str, "NO") || !strcmp(str, "FALSE")) + return false; + else + return def; +} diff --git a/lib/IRremoteESP8266-2.6.0/src/IRac.h b/lib/IRremoteESP8266-2.6.0/src/IRac.h new file mode 100644 index 000000000000..ce8d50507e9e --- /dev/null +++ b/lib/IRremoteESP8266-2.6.0/src/IRac.h @@ -0,0 +1,248 @@ +#ifndef IRAC_H_ +#define IRAC_H_ + +// Copyright 2019 David Conran + +#ifndef UNIT_TEST +#include +#endif +#ifndef ARDUINO +#include +#endif +#include "IRremoteESP8266.h" +#include "ir_Argo.h" +#include "ir_Coolix.h" +#include "ir_Daikin.h" +#include "ir_Fujitsu.h" +#include "ir_Gree.h" +#include "ir_Haier.h" +#include "ir_Hitachi.h" +#include "ir_Kelvinator.h" +#include "ir_Midea.h" +#include "ir_Mitsubishi.h" +#include "ir_MitsubishiHeavy.h" +#include "ir_Panasonic.h" +#include "ir_Samsung.h" +#include "ir_Tcl.h" +#include "ir_Teco.h" +#include "ir_Toshiba.h" +#include "ir_Trotec.h" +#include "ir_Vestel.h" +#include "ir_Whirlpool.h" + +class IRac { + public: + explicit IRac(uint8_t pin); + static bool isProtocolSupported(const decode_type_t protocol); + bool sendAc(const decode_type_t vendor, const int16_t model, + const bool power, const stdAc::opmode_t mode, const float degrees, + const bool celsius, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, + const bool quiet, const bool turbo, const bool econo, + const bool light, const bool filter, const bool clean, + const bool beep, const int16_t sleep = -1, + const int16_t clock = -1); + + static bool strToBool(const char *str, const bool def = false); + static int16_t strToModel(const char *str, const int16_t def = -1); + static stdAc::opmode_t strToOpmode( + const char *str, const stdAc::opmode_t def = stdAc::opmode_t::kAuto); + static stdAc::fanspeed_t strToFanspeed( + const char *str, + const stdAc::fanspeed_t def = stdAc::fanspeed_t::kAuto); + static stdAc::swingv_t strToSwingV( + const char *str, const stdAc::swingv_t def = stdAc::swingv_t::kOff); + static stdAc::swingh_t strToSwingH( + const char *str, const stdAc::swingh_t def = stdAc::swingh_t::kOff); +#ifndef UNIT_TEST + + private: +#endif + uint8_t _pin; +#if SEND_ARGO + void 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 = -1); +#endif // SEND_ARGO +#if SEND_COOLIX + void 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 = -1); +#endif // SEND_COOLIX +#if SEND_DAIKIN + void daikin(IRDaikinESP *ac, + const bool on, const stdAc::opmode_t mode, const float degrees, + const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, + const bool quiet, const bool turbo, const bool econo, + const bool clean); +#endif // SEND_DAIKIN +#if SEND_DAIKIN2 + void daikin2(IRDaikin2 *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, + const bool quiet, const bool turbo, const bool light, + const bool econo, const bool filter, const bool clean, + const bool beep, const int16_t sleep = -1, + const int16_t clock = -1); +#endif // SEND_DAIKIN2 +#if SEND_DAIKIN216 +void daikin216(IRDaikin216 *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, + const bool quiet); +#endif // SEND_DAIKIN216 +#if SEND_FUJITSU_AC + void fujitsu(IRFujitsuAC *ac, const fujitsu_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); +#endif // SEND_FUJITSU_AC +#if SEND_GREE + void gree(IRGreeAC *ac, + const bool on, const stdAc::opmode_t mode, const float degrees, + const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv, + const bool turbo, const bool light, const bool clean, + const int16_t sleep = -1); +#endif // SEND_GREE +#if SEND_HAIER_AC + void haier(IRHaierAC *ac, + const bool on, const stdAc::opmode_t mode, const float degrees, + const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv, + const bool filter, const int16_t sleep = -1, + const int16_t clock = -1); +#endif // SEND_HAIER_AC +#if SEND_HAIER_AC_YRW02 + void 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 filter, + const int16_t sleep = -1); +#endif // SEND_HAIER_AC_YRW02 +#if SEND_HITACHI_AC + void hitachi(IRHitachiAc *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const stdAc::swingh_t swingh); +#endif // SEND_HITACHI_AC +#if SEND_KELVINATOR + void kelvinator(IRKelvinatorAC *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, + const bool quiet, const bool turbo, const bool light, + const bool filter, const bool clean); +#endif // SEND_KELVINATOR +#if SEND_MIDEA + void midea(IRMideaAC *ac, + const bool on, const stdAc::opmode_t mode, const float degrees, + const stdAc::fanspeed_t fan, const int16_t sleep = -1); +#endif // SEND_MIDEA +#if SEND_MITSUBISHI_AC + void mitsubishi(IRMitsubishiAC *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, + const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv, + const bool quiet, const int16_t clock = -1); +#endif // SEND_MITSUBISHI_AC +#if SEND_MITSUBISHIHEAVY + void mitsubishiHeavy88(IRMitsubishiHeavy88Ac *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, + const stdAc::swingh_t swingh, + const bool turbo, const bool econo, const bool clean); + void mitsubishiHeavy152(IRMitsubishiHeavy152Ac *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, + const stdAc::swingh_t swingh, + const bool quiet, const bool turbo, const bool econo, + const bool filter, const bool clean, + const int16_t sleep = -1); +#endif // SEND_MITSUBISHIHEAVY +#if SEND_PANASONIC_AC + void panasonic(IRPanasonicAc *ac, const panasonic_ac_remote_model_t model, + const bool on, const stdAc::opmode_t mode, const float degrees, + const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, + const bool quiet, const bool turbo, const int16_t clock = -1); +#endif // SEND_PANASONIC_AC +#if SEND_SAMSUNG_AC + void samsung(IRSamsungAc *ac, + const bool on, const stdAc::opmode_t mode, const float degrees, + const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv, + const bool quiet, const bool turbo, const bool clean, + const bool beep, const bool sendOnOffHack = true); +#endif // SEND_SAMSUNG_AC +#if SEND_TCL112AC + void tcl112(IRTcl112Ac *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 econo, + const bool filter); +#endif // SEND_TCL112AC +#if SEND_TECO + void teco(IRTecoAc *ac, + const bool on, const stdAc::opmode_t mode, const float degrees, + const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv, + const int16_t sleep = -1); +#endif // SEND_TECO +#if SEND_TOSHIBA_AC + void toshiba(IRToshibaAC *ac, + const bool on, const stdAc::opmode_t mode, const float degrees, + const stdAc::fanspeed_t fan); +#endif // SEND_TOSHIBA_AC +#if SEND_TROTEC + void trotec(IRTrotecESP *ac, + const bool on, const stdAc::opmode_t mode, const float degrees, + const stdAc::fanspeed_t fan, const int16_t sleep = -1); +#endif // SEND_TROTEC +#if SEND_VESTEL_AC + void vestel(IRVestelAc *ac, + const bool on, const stdAc::opmode_t mode, const float degrees, + const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv, + const bool turbo, const bool filter, + const int16_t sleep = -1, const int16_t clock = -1, + const bool sendNormal = true); +#endif // SEND_VESTEL_AC +#if SEND_WHIRLPOOL_AC + void whirlpool(IRWhirlpoolAc *ac, const whirlpool_ac_remote_model_t model, + const bool on, const stdAc::opmode_t mode, const float degrees, + const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv, + const bool turbo, const bool light, + const int16_t sleep = -1, const int16_t clock = -1); +#endif // SEND_WHIRLPOOL_AC +}; // IRac class + +// Structure to hold a common A/C state. +typedef struct { + decode_type_t protocol; + int16_t model; + bool power; + stdAc::opmode_t mode; + float degrees; + bool celsius; + stdAc::fanspeed_t fanspeed; + stdAc::swingv_t swingv; + stdAc::swingh_t swingh; + bool quiet; + bool turbo; + bool econo; + bool light; + bool filter; + bool clean; + bool beep; + int16_t sleep; + int16_t clock; +} commonAcState_t; +#endif // IRAC_H_ diff --git a/lib/IRremoteESP8266-2.5.2.03/src/IRrecv.cpp b/lib/IRremoteESP8266-2.6.0/src/IRrecv.cpp similarity index 95% rename from lib/IRremoteESP8266-2.5.2.03/src/IRrecv.cpp rename to lib/IRremoteESP8266-2.6.0/src/IRrecv.cpp index b2c98439694b..eac86808415d 100644 --- a/lib/IRremoteESP8266-2.5.2.03/src/IRrecv.cpp +++ b/lib/IRremoteESP8266-2.6.0/src/IRrecv.cpp @@ -364,6 +364,10 @@ bool IRrecv::decode(decode_results *results, irparams_t *save) { DPRINTLN("Attempting SAMSUNG decode"); if (decodeSAMSUNG(results)) return true; #endif +#if DECODE_SAMSUNG36 + DPRINTLN("Attempting Samsung36 decode"); + if (decodeSamsung36(results)) return true; +#endif #if DECODE_WHYNTER DPRINTLN("Attempting Whynter decode"); if (decodeWhynter(results)) return true; @@ -394,6 +398,14 @@ bool IRrecv::decode(decode_results *results, irparams_t *save) { DPRINTLN("Attempting Daikin decode"); if (decodeDaikin(results)) return true; #endif +#if DECODE_DAIKIN2 + DPRINTLN("Attempting Daikin2 decode"); + if (decodeDaikin2(results)) return true; +#endif +#if DECODE_DAIKIN216 + DPRINTLN("Attempting Daikin216 decode"); + if (decodeDaikin216(results)) return true; +#endif #if DECODE_TOSHIBA_AC DPRINTLN("Attempting Toshiba AC decode"); if (decodeToshibaAC(results)) return true; @@ -489,6 +501,28 @@ bool IRrecv::decode(decode_results *results, irparams_t *save) { DPRINTLN("Attempting MWM decode"); if (decodeMWM(results)) return true; #endif +#if DECODE_VESTEL_AC + DPRINTLN("Attempting Vestel AC decode"); + if (decodeVestelAc(results)) return true; +#endif +#if DECODE_TCL112AC + DPRINTLN("Attempting TCL112AC decode"); + if (decodeTcl112Ac(results)) return true; +#endif +#if DECODE_TECO + DPRINTLN("Attempting Teco decode"); + if (decodeTeco(results)) return true; +#endif +#if DECODE_LEGOPF + DPRINTLN("Attempting LEGOPF decode"); + if (decodeLegoPf(results)) return true; +#endif +#if DECODE_MITSUBISHIHEAVY + DPRINTLN("Attempting MITSUBISHIHEAVY (152 bit) decode"); + if (decodeMitsubishiHeavy(results, kMitsubishiHeavy152Bits)) return true; + DPRINTLN("Attempting MITSUBISHIHEAVY (88 bit) decode"); + if (decodeMitsubishiHeavy(results, kMitsubishiHeavy88Bits)) return true; +#endif #if DECODE_HASH // decodeHash returns a hash on any input. // Thus, it needs to be last in the list. diff --git a/lib/IRremoteESP8266-2.5.2.03/src/IRrecv.h b/lib/IRremoteESP8266-2.6.0/src/IRrecv.h similarity index 90% rename from lib/IRremoteESP8266-2.5.2.03/src/IRrecv.h rename to lib/IRremoteESP8266-2.6.0/src/IRrecv.h index c0f5e781aa90..0659f093eb03 100644 --- a/lib/IRremoteESP8266-2.5.2.03/src/IRrecv.h +++ b/lib/IRremoteESP8266-2.6.0/src/IRrecv.h @@ -181,6 +181,10 @@ class IRrecv { uint16_t nbits = kMitsubishiACBits, bool strict = false); #endif +#if DECODE_MITSUBISHIHEAVY + bool decodeMitsubishiHeavy(decode_results *results, const uint16_t nbits, + const bool strict = true); +#endif #if (DECODE_RC5 || DECODE_R6 || DECODE_LASERTAG || DECODE_MWM) int16_t getRClevel(decode_results *results, uint16_t *offset, uint16_t *used, uint16_t bitTime, uint8_t tolerance = kTolerance, @@ -216,6 +220,11 @@ class IRrecv { bool decodeSAMSUNG(decode_results *results, uint16_t nbits = kSamsungBits, bool strict = true); #endif +#if DECODE_SAMSUNG + bool decodeSamsung36(decode_results *results, + const uint16_t nbits = kSamsung36Bits, + const bool strict = true); +#endif #if DECODE_SAMSUNG_AC bool decodeSamsungAC(decode_results *results, uint16_t nbits = kSamsungAcBits, bool strict = true); @@ -257,8 +266,17 @@ class IRrecv { uint16_t nbits = kKelvinatorBits, bool strict = true); #endif #if DECODE_DAIKIN - bool decodeDaikin(decode_results *results, uint16_t nbits = kDaikinRawBits, - bool strict = true); + bool decodeDaikin(decode_results *results, const uint16_t nbits = kDaikinBits, + const bool strict = true); +#endif +#if DECODE_DAIKIN2 + bool decodeDaikin2(decode_results *results, uint16_t nbits = kDaikin2Bits, + bool strict = true); +#endif +#if DECODE_DAIKIN216 + bool decodeDaikin216(decode_results *results, + const uint16_t nbits = kDaikin216Bits, + const bool strict = true); #endif #if DECODE_TOSHIBA_AC bool decodeToshibaAC(decode_results *results, @@ -330,6 +348,22 @@ class IRrecv { bool decodeMWM(decode_results *results, uint16_t nbits = 24, bool strict = true); #endif +#if DECODE_VESTEL_AC + bool decodeVestelAc(decode_results *results, uint16_t nbits = kVestelAcBits, + bool strict = true); +#endif +#if DECODE_TCL112AC + bool decodeTcl112Ac(decode_results *results, uint16_t nbits = kTcl112AcBits, + bool strict = true); +#endif +#if DECODE_TECO + bool decodeTeco(decode_results *results, uint16_t nbits = kTecoBits, + bool strict = false); +#endif +#if DECODE_LEGOPF + bool decodeLegoPf(decode_results *results, const uint16_t nbits = kLegoPfBits, + const bool strict = true); +#endif }; #endif // IRRECV_H_ diff --git a/lib/IRremoteESP8266-2.5.2.03/src/IRremoteESP8266.h b/lib/IRremoteESP8266-2.6.0/src/IRremoteESP8266.h similarity index 82% rename from lib/IRremoteESP8266-2.5.2.03/src/IRremoteESP8266.h rename to lib/IRremoteESP8266-2.6.0/src/IRremoteESP8266.h index e228cbcb0a4f..b532cb1c0ded 100644 --- a/lib/IRremoteESP8266-2.5.2.03/src/IRremoteESP8266.h +++ b/lib/IRremoteESP8266-2.6.0/src/IRremoteESP8266.h @@ -34,6 +34,8 @@ * Fujitsu A/C code added by jonnygraham * Trotec AC code by stufisher * Carrier & Haier AC code by crankyoldgit + * Vestel AC code by Erdem U. Altınyurt + * Teco AC code by Fabien Valthier (hcoohb) * * GPL license, all text above must be included in any redistribution ****************************************************/ @@ -48,7 +50,7 @@ #endif // Library Version -#define _IRREMOTEESP8266_VERSION_ "2.5.2" +#define _IRREMOTEESP8266_VERSION_ "2.6.0" // Supported IR protocols // Each protocol you include costs memory and, during decode, costs time // Disable (set to false) all the protocols you do not need/want! @@ -86,6 +88,9 @@ #define DECODE_SAMSUNG true #define SEND_SAMSUNG true +#define DECODE_SAMSUNG36 true +#define SEND_SAMSUNG36 true + #define DECODE_SAMSUNG_AC true #define SEND_SAMSUNG_AC true @@ -199,6 +204,27 @@ #define DECODE_PIONEER true #define SEND_PIONEER true + +#define DECODE_DAIKIN2 true +#define SEND_DAIKIN2 true + +#define DECODE_VESTEL_AC true +#define SEND_VESTEL_AC true + +#define DECODE_TECO true +#define SEND_TECO true + +#define DECODE_TCL112AC true +#define SEND_TCL112AC true + +#define DECODE_LEGOPF true +#define SEND_LEGOPF true + +#define DECODE_MITSUBISHIHEAVY true +#define SEND_MITSUBISHIHEAVY true + +#define DECODE_DAIKIN216 true +#define SEND_DAIKIN216 true */ // Tasmota supported protocols (less protocols is less code size) @@ -233,6 +259,9 @@ #define DECODE_SAMSUNG true #define SEND_SAMSUNG true +#define DECODE_SAMSUNG36 false +#define SEND_SAMSUNG36 false + #define DECODE_SAMSUNG_AC false #define SEND_SAMSUNG_AC false @@ -347,12 +376,35 @@ #define DECODE_PIONEER false #define SEND_PIONEER false +#define DECODE_DAIKIN2 false +#define SEND_DAIKIN2 false + +#define DECODE_VESTEL_AC false +#define SEND_VESTEL_AC false + +#define DECODE_TECO false +#define SEND_TECO false + +#define DECODE_TCL112AC false +#define SEND_TCL112AC false + +#define DECODE_LEGOPF false +#define SEND_LEGOPF false + +#define DECODE_MITSUBISHIHEAVY false +#define SEND_MITSUBISHIHEAVY false + +#define DECODE_DAIKIN216 false +#define SEND_DAIKIN216 false + #if (DECODE_ARGO || DECODE_DAIKIN || DECODE_FUJITSU_AC || DECODE_GREE || \ DECODE_KELVINATOR || DECODE_MITSUBISHI_AC || DECODE_TOSHIBA_AC || \ DECODE_TROTEC || DECODE_HAIER_AC || DECODE_HITACHI_AC || \ DECODE_HITACHI_AC1 || DECODE_HITACHI_AC2 || DECODE_HAIER_AC_YRW02 || \ DECODE_WHIRLPOOL_AC || DECODE_SAMSUNG_AC || DECODE_ELECTRA_AC || \ - DECODE_PANASONIC_AC || DECODE_MWM) + DECODE_PANASONIC_AC || DECODE_MWM || DECODE_DAIKIN2 || \ + DECODE_VESTEL_AC || DECODE_TCL112AC || DECODE_MITSUBISHIHEAVY || \ + DECODE_DAIKIN216) #define DECODE_AC true // We need some common infrastructure for decoding A/Cs. #else #define DECODE_AC false // We don't need that infrastructure. @@ -376,54 +428,65 @@ enum decode_type_t { RC6, NEC, SONY, - PANASONIC, + PANASONIC, // (5) JVC, SAMSUNG, WHYNTER, AIWA_RC_T501, - LG, + LG, // (10) SANYO, MITSUBISHI, DISH, SHARP, - COOLIX, + COOLIX, // (15) DAIKIN, DENON, KELVINATOR, SHERWOOD, - MITSUBISHI_AC, + MITSUBISHI_AC, // (20) RCMM, SANYO_LC7461, RC5X, GREE, - PRONTO, // Technically not a protocol, but an encoding. + PRONTO, // Technically not a protocol, but an encoding. (25) NEC_LIKE, ARGO, TROTEC, NIKAI, - RAW, // Technically not a protocol, but an encoding. + RAW, // Technically not a protocol, but an encoding. (30) GLOBALCACHE, // Technically not a protocol, but an encoding. TOSHIBA_AC, FUJITSU_AC, MIDEA, - MAGIQUEST, + MAGIQUEST, // (35) LASERTAG, CARRIER_AC, HAIER_AC, MITSUBISHI2, - HITACHI_AC, + HITACHI_AC, // (40) HITACHI_AC1, HITACHI_AC2, GICABLE, HAIER_AC_YRW02, - WHIRLPOOL_AC, + WHIRLPOOL_AC, // (45) SAMSUNG_AC, LUTRON, ELECTRA_AC, PANASONIC_AC, - PIONEER, + PIONEER, // (50) LG2, MWM, + DAIKIN2, + VESTEL_AC, + TECO, // (55) + SAMSUNG36, + TCL112AC, + LEGOPF, + MITSUBISHI_HEAVY_88, + MITSUBISHI_HEAVY_152, // 60 + DAIKIN216, + // Add new entries before this one, and update it to point to the last entry. + kLastDecodeType = DAIKIN216, }; // Message lengths & required repeat values @@ -433,13 +496,22 @@ const uint16_t kSingleRepeat = 1; const uint16_t kAiwaRcT501Bits = 15; const uint16_t kAiwaRcT501MinRepeats = kSingleRepeat; const uint16_t kArgoStateLength = 12; +const uint16_t kArgoDefaultRepeat = kNoRepeat; const uint16_t kCoolixBits = 24; +const uint16_t kCoolixDefaultRepeat = 1; const uint16_t kCarrierAcBits = 32; const uint16_t kCarrierAcMinRepeat = kNoRepeat; -// Daikin has a lot of static stuff that is discarded -const uint16_t kDaikinRawBits = 583; -const uint16_t kDaikinStateLength = 27; +const uint16_t kDaikinStateLength = 35; const uint16_t kDaikinBits = kDaikinStateLength * 8; +const uint16_t kDaikinStateLengthShort = kDaikinStateLength - 8; +const uint16_t kDaikinBitsShort = kDaikinStateLengthShort * 8; +const uint16_t kDaikinDefaultRepeat = kNoRepeat; +const uint16_t kDaikin2StateLength = 39; +const uint16_t kDaikin2Bits = kDaikin2StateLength * 8; +const uint16_t kDaikin2DefaultRepeat = kNoRepeat; +const uint16_t kDaikin216StateLength = 27; +const uint16_t kDaikin216Bits = kDaikin216StateLength * 8; +const uint16_t kDaikin216DefaultRepeat = kNoRepeat; const uint16_t kDenonBits = 15; const uint16_t kDenonLegacyBits = 14; const uint16_t kDishBits = 16; @@ -455,12 +527,16 @@ const uint16_t kGicableBits = 16; const uint16_t kGicableMinRepeat = kSingleRepeat; const uint16_t kGreeStateLength = 8; const uint16_t kGreeBits = kGreeStateLength * 8; +const uint16_t kGreeDefaultRepeat = kNoRepeat; const uint16_t kHaierACStateLength = 9; const uint16_t kHaierACBits = kHaierACStateLength * 8; +const uint16_t kHaierAcDefaultRepeat = kNoRepeat; const uint16_t kHaierACYRW02StateLength = 14; const uint16_t kHaierACYRW02Bits = kHaierACYRW02StateLength * 8; +const uint16_t kHaierAcYrw02DefaultRepeat = kNoRepeat; const uint16_t kHitachiAcStateLength = 28; const uint16_t kHitachiAcBits = kHitachiAcStateLength * 8; +const uint16_t kHitachiAcDefaultRepeat = kNoRepeat; const uint16_t kHitachiAc1StateLength = 13; const uint16_t kHitachiAc1Bits = kHitachiAc1StateLength * 8; const uint16_t kHitachiAc2StateLength = 53; @@ -468,8 +544,11 @@ const uint16_t kHitachiAc2Bits = kHitachiAc2StateLength * 8; const uint16_t kJvcBits = 16; const uint16_t kKelvinatorStateLength = 16; const uint16_t kKelvinatorBits = kKelvinatorStateLength * 8; +const uint16_t kKelvinatorDefaultRepeat = kNoRepeat; const uint16_t kLasertagBits = 13; const uint16_t kLasertagMinRepeat = kNoRepeat; +const uint16_t kLegoPfBits = 16; +const uint16_t kLegoPfMinRepeat = kNoRepeat; const uint16_t kLgBits = 28; const uint16_t kLg32Bits = 32; const uint16_t kLutronBits = 35; @@ -483,6 +562,12 @@ const uint16_t kMitsubishiMinRepeat = kSingleRepeat; const uint16_t kMitsubishiACStateLength = 18; const uint16_t kMitsubishiACBits = kMitsubishiACStateLength * 8; const uint16_t kMitsubishiACMinRepeat = kSingleRepeat; +const uint16_t kMitsubishiHeavy88StateLength = 11; +const uint16_t kMitsubishiHeavy88Bits = kMitsubishiHeavy88StateLength * 8; +const uint16_t kMitsubishiHeavy88MinRepeat = kNoRepeat; +const uint16_t kMitsubishiHeavy152StateLength = 19; +const uint16_t kMitsubishiHeavy152Bits = kMitsubishiHeavy152StateLength * 8; +const uint16_t kMitsubishiHeavy152MinRepeat = kNoRepeat; const uint16_t kNikaiBits = 24; const uint16_t kNECBits = 32; const uint16_t kPanasonicBits = 48; @@ -491,6 +576,7 @@ const uint16_t kPanasonicAcStateLength = 27; const uint16_t kPanasonicAcStateShortLength = 16; const uint16_t kPanasonicAcBits = kPanasonicAcStateLength * 8; const uint16_t kPanasonicAcShortBits = kPanasonicAcStateShortLength * 8; +const uint16_t kPanasonicAcDefaultRepeat = kNoRepeat; const uint16_t kPioneerBits = 64; const uint16_t kProntoMinLength = 6; const uint16_t kRC5RawBits = 14; @@ -500,10 +586,12 @@ const uint16_t kRC6Mode0Bits = 20; // Excludes the 'start' bit. const uint16_t kRC6_36Bits = 36; // Excludes the 'start' bit. const uint16_t kRCMMBits = 24; const uint16_t kSamsungBits = 32; +const uint16_t kSamsung36Bits = 36; const uint16_t kSamsungAcStateLength = 14; const uint16_t kSamsungAcBits = kSamsungAcStateLength * 8; const uint16_t kSamsungAcExtendedStateLength = 21; const uint16_t kSamsungAcExtendedBits = kSamsungAcExtendedStateLength * 8; +const uint16_t kSamsungAcDefaultRepeat = kNoRepeat; const uint16_t kSanyoSA8650BBits = 12; const uint16_t kSanyoLC7461AddressBits = 13; const uint16_t kSanyoLC7461CommandBits = 8; @@ -519,13 +607,22 @@ const uint16_t kSony15Bits = 15; const uint16_t kSony20Bits = 20; const uint16_t kSonyMinBits = 12; const uint16_t kSonyMinRepeat = 2; +const uint16_t kTcl112AcStateLength = 14; +const uint16_t kTcl112AcBits = kTcl112AcStateLength * 8; +const uint16_t kTcl112AcDefaultRepeat = kNoRepeat; +const uint16_t kTecoBits = 35; +const uint16_t kTecoDefaultRepeat = kNoRepeat; const uint16_t kToshibaACStateLength = 9; const uint16_t kToshibaACBits = kToshibaACStateLength * 8; const uint16_t kToshibaACMinRepeat = kSingleRepeat; const uint16_t kTrotecStateLength = 9; +const uint16_t kTrotecDefaultRepeat = kNoRepeat; const uint16_t kWhirlpoolAcStateLength = 21; const uint16_t kWhirlpoolAcBits = kWhirlpoolAcStateLength * 8; +const uint16_t kWhirlpoolAcDefaultRepeat = kNoRepeat; const uint16_t kWhynterBits = 32; +const uint8_t kVestelAcBits = 56; + // Legacy defines. (Deprecated) #define AIWA_RC_T501_BITS kAiwaRcT501Bits @@ -598,4 +695,14 @@ const uint16_t kWhynterBits = 32; #define DPRINTLN(x) #endif // DEBUG +#ifdef UNIT_TEST +#ifndef F +// Create a no-op F() macro so the code base still compiles outside of the +// Arduino framework. Thus we can safely use the Arduino 'F()' macro through-out +// the code base. That macro stores constants in Flash (PROGMEM) memory. +// See: https://github.com/markszabo/IRremoteESP8266/issues/667 +#define F(x) x +#endif // F +#endif // UNIT_TEST + #endif // IRREMOTEESP8266_H_ diff --git a/lib/IRremoteESP8266-2.5.2.03/src/IRsend.cpp b/lib/IRremoteESP8266-2.6.0/src/IRsend.cpp similarity index 92% rename from lib/IRremoteESP8266-2.5.2.03/src/IRsend.cpp rename to lib/IRremoteESP8266-2.6.0/src/IRsend.cpp index 96f95172da0f..22c0c874b474 100644 --- a/lib/IRremoteESP8266-2.5.2.03/src/IRsend.cpp +++ b/lib/IRremoteESP8266-2.6.0/src/IRsend.cpp @@ -1,6 +1,6 @@ // Copyright 2009 Ken Shirriff // Copyright 2015 Mark Szabo -// Copyright 2017 David Conran +// Copyright 2017,2019 David Conran #include "IRsend.h" #ifndef UNIT_TEST @@ -110,6 +110,9 @@ void IRsend::enableIROut(uint32_t freq, uint8_t duty) { } 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; @@ -488,27 +491,34 @@ void IRsend::sendRaw(uint16_t buf[], uint16_t len, uint16_t hz) { } #endif // SEND_RAW -#ifndef UNIT_TEST -void IRsend::send(uint16_t type, uint64_t data, uint16_t nbits) { +// Send a simple (up to 64 bits) IR message of a given type. +// An unknown/unsupported type will do nothing. +// Args: +// type: Protocol number/type of the message you want to send. +// data: The data you want to send (up to 64 bits). +// nbits: How many bits long the message is to be. +// Returns: +// bool: True if it is a type we can attempt to send, false if not. +bool IRsend::send(decode_type_t type, uint64_t data, uint16_t nbits) { switch (type) { -#if SEND_NEC - case NEC: - sendNEC(data, nbits); +#if SEND_AIWA_RC_T501 + case AIWA_RC_T501: + sendAiwaRCT501(data, nbits); break; #endif -#if SEND_SONY - case SONY: - sendSony(data, nbits); +#if SEND_CARRIER_AC + case CARRIER_AC: + sendCarrierAC(data, nbits); break; #endif -#if SEND_RC5 - case RC5: - sendRC5(data, nbits); +#if SEND_COOLIX + case COOLIX: + sendCOOLIX(data, nbits); break; #endif -#if SEND_RC6 - case RC6: - sendRC6(data, nbits); +#if SEND_DENON + case DENON: + sendDenon(data, nbits); break; #endif #if SEND_DISH @@ -516,44 +526,93 @@ void IRsend::send(uint16_t type, uint64_t data, uint16_t nbits) { sendDISH(data, nbits); break; #endif +#if SEND_GICABLE + case GICABLE: + sendGICable(data, nbits); + break; +#endif +#if SEND_GREE + case GREE: + sendGree(data, nbits); + break; +#endif #if SEND_JVC case JVC: sendJVC(data, nbits); break; #endif -#if SEND_SAMSUNG - case SAMSUNG: - sendSAMSUNG(data, nbits); +#if SEND_LASERTAG + case LASERTAG: + sendLasertag(data, nbits); + break; +#endif +#if SEND_LEGOPF + case LEGOPF: + sendLegoPf(data, nbits); break; #endif #if SEND_LG case LG: sendLG(data, nbits); break; -#endif -#if SEND_LG case LG2: sendLG2(data, nbits); break; #endif -#if SEND_WHYNTER - case WHYNTER: - sendWhynter(data, nbits); +#if SEND_LUTRON + case LUTRON: + sendLutron(data, nbits); break; #endif -#if SEND_COOLIX - case COOLIX: - sendCOOLIX(data, nbits); +#if SEND_MAGIQUEST + case MAGIQUEST: + sendMagiQuest(data, nbits); break; #endif -#if SEND_DENON - case DENON: - sendDenon(data, nbits); +#if SEND_MIDEA + case MIDEA: + sendMidea(data, nbits); break; #endif -#if SEND_SHERWOOD - case SHERWOOD: - sendSherwood(data, nbits); +#if SEND_MITSUBISHI + case MITSUBISHI: + sendMitsubishi(data, nbits); + break; +#endif +#if SEND_MITSUBISHI2 + case MITSUBISHI2: + sendMitsubishi2(data, nbits); + break; +#endif +#if SEND_NIKAI + case NIKAI: + sendNikai(data, nbits); + break; +#endif +#if SEND_NEC + case NEC: + case NEC_LIKE: + sendNEC(data, nbits); + break; +#endif +#if SEND_PANASONIC + case PANASONIC: + sendPanasonic64(data, nbits); + break; +#endif +#if SEND_PIONEER + case PIONEER: + sendPioneer(data, nbits); + break; +#endif +#if SEND_RC5 + case RC5: + sendRC5(data, nbits); + break; +#endif +#if SEND_RC6 + case RC6: + sendRC6(data, nbits); break; #endif #if SEND_RCMM @@ -561,14 +620,19 @@ void IRsend::send(uint16_t type, uint64_t data, uint16_t nbits) { sendRCMM(data, nbits); break; #endif -#if SEND_MITSUBISHI - case MITSUBISHI: - sendMitsubishi(data, nbits); +#if SEND_SAMSUNG + case SAMSUNG: + sendSAMSUNG(data, nbits); break; #endif -#if SEND_MITSUBISHI2 - case MITSUBISHI2: - sendMitsubishi2(data, nbits); +#if SEND_SAMSUNG36 + case SAMSUNG36: + sendSamsung36(data, nbits); + break; +#endif +#if SEND_SANYO + case SANYO_LC7461: + sendSanyoLC7461(data, nbits); break; #endif #if SEND_SHARP @@ -576,26 +640,33 @@ void IRsend::send(uint16_t type, uint64_t data, uint16_t nbits) { sendSharpRaw(data, nbits); break; #endif -#if SEND_AIWA_RC_T501 - case AIWA_RC_T501: - sendAiwaRCT501(data, nbits); +#if SEND_SHERWOOD + case SHERWOOD: + sendSherwood(data, nbits); break; #endif -#if SEND_MIDEA - case MIDEA: - sendMidea(data, nbits); +#if SEND_SONY + case SONY: + sendSony(data, nbits); break; #endif -#if SEND_GICABLE - case GICABLE: - sendGICable(data, nbits); +#if SEND_TECO + case TECO: + sendTeco(data, nbits); break; #endif -#if SEND_PIONEER - case PIONEER: - sendPioneer(data, nbits); +#if SEND_VESTEL_AC + case VESTEL_AC: + sendVestelAc(data, nbits); break; #endif +#if SEND_WHYNTER + case WHYNTER: + sendWhynter(data, nbits); + break; +#endif + default: + return false; } + return true; } -#endif diff --git a/lib/IRremoteESP8266-2.5.2.03/src/IRsend.h b/lib/IRremoteESP8266-2.6.0/src/IRsend.h similarity index 77% rename from lib/IRremoteESP8266-2.5.2.03/src/IRsend.h rename to lib/IRremoteESP8266-2.6.0/src/IRsend.h index 8e2dc248e178..b065f6582302 100644 --- a/lib/IRremoteESP8266-2.5.2.03/src/IRsend.h +++ b/lib/IRremoteESP8266-2.6.0/src/IRsend.h @@ -28,6 +28,49 @@ const uint8_t kDutyMax = 100; // Percentage // delayMicroseconds() is only accurate to 16383us. // Ref: https://www.arduino.cc/en/Reference/delayMicroseconds const uint16_t kMaxAccurateUsecDelay = 16383; +// Usecs to wait between messages we don't know the proper gap time. +const uint32_t kDefaultMessageGap = 100000; + + +namespace stdAc { + enum class opmode_t { + kOff = -1, + kAuto = 0, + kCool = 1, + kHeat = 2, + kDry = 3, + kFan = 4, + }; + + enum class fanspeed_t { + kAuto = 0, + kMin = 1, + kLow = 2, + kMedium = 3, + kHigh = 4, + kMax = 5, + }; + + enum class swingv_t { + kOff = -1, + kAuto = 0, + kHighest = 1, + kHigh = 2, + kMiddle = 3, + kLow = 4, + kLowest = 5, + }; + + enum class swingh_t { + kOff = -1, + kAuto = 0, // a.k.a. On. + kLeftMax = 1, + kLeft = 2, + kMiddle = 3, + kRight = 4, + kRightMax = 5, + }; +}; // namespace stdAc // Classes class IRsend { @@ -66,7 +109,7 @@ class IRsend { const uint8_t *dataptr, const uint16_t nbytes, const uint16_t frequency, const bool MSBfirst, const uint16_t repeat, const uint8_t dutycycle); - void send(uint16_t type, uint64_t data, uint16_t nbits); + bool send(decode_type_t type, uint64_t data, uint16_t nbits); #if (SEND_NEC || SEND_SHERWOOD || SEND_AIWA_RC_T501 || SEND_SANYO) void sendNEC(uint64_t data, uint16_t nbits = kNECBits, uint16_t repeat = kNoRepeat); @@ -92,10 +135,14 @@ class IRsend { uint16_t repeat = kNoRepeat); uint32_t encodeSAMSUNG(uint8_t customer, uint8_t command); #endif +#if SEND_SAMSUNG36 + void sendSamsung36(const uint64_t data, const uint16_t nbits = kSamsung36Bits, + const uint16_t repeat = kNoRepeat); +#endif #if SEND_SAMSUNG_AC - void sendSamsungAC(unsigned char data[], - uint16_t nbytes = kSamsungAcStateLength, - uint16_t repeat = kNoRepeat); + void sendSamsungAC(const unsigned char data[], + const uint16_t nbytes = kSamsungAcStateLength, + const uint16_t repeat = kSamsungAcDefaultRepeat); #endif #if SEND_LG void sendLG(uint64_t data, uint16_t nbits = kLgBits, @@ -166,7 +213,7 @@ class IRsend { #endif #if SEND_COOLIX void sendCOOLIX(uint64_t data, uint16_t nbits = kCoolixBits, - uint16_t repeat = kNoRepeat); + uint16_t repeat = kCoolixDefaultRepeat); #endif #if SEND_WHYNTER void sendWhynter(uint64_t data, uint16_t nbits = kWhynterBits, @@ -185,6 +232,16 @@ class IRsend { uint16_t nbytes = kMitsubishiACStateLength, uint16_t repeat = kMitsubishiACMinRepeat); #endif +#if SEND_MITSUBISHIHEAVY + void sendMitsubishiHeavy88( + const unsigned char data[], + const uint16_t nbytes = kMitsubishiHeavy88StateLength, + const uint16_t repeat = kMitsubishiHeavy88MinRepeat); + void sendMitsubishiHeavy152( + const unsigned char data[], + const uint16_t nbytes = kMitsubishiHeavy152StateLength, + const uint16_t repeat = kMitsubishiHeavy152MinRepeat); +#endif #if SEND_FUJITSU_AC void sendFujitsuAC(unsigned char data[], uint16_t nbytes, uint16_t repeat = kFujitsuAcMinRepeat); @@ -195,12 +252,21 @@ class IRsend { #if SEND_KELVINATOR void sendKelvinator(unsigned char data[], uint16_t nbytes = kKelvinatorStateLength, - uint16_t repeat = kNoRepeat); + uint16_t repeat = kKelvinatorDefaultRepeat); #endif #if SEND_DAIKIN - void sendDaikin(unsigned char data[], uint16_t nbytes = kDaikinStateLength, - uint16_t repeat = kNoRepeat); - void sendDaikinGapHeader(); + void sendDaikin(const unsigned char data[], + const uint16_t nbytes = kDaikinStateLength, + const uint16_t repeat = kDaikinDefaultRepeat); +#endif +#if SEND_DAIKIN2 + void sendDaikin2(unsigned char data[], uint16_t nbytes = kDaikin2StateLength, + uint16_t repeat = kDaikin2DefaultRepeat); +#endif +#if SEND_DAIKIN216 + void sendDaikin216(const unsigned char data[], + const uint16_t nbytes = kDaikin216StateLength, + const uint16_t repeat = kDaikin216DefaultRepeat); #endif #if SEND_AIWA_RC_T501 void sendAiwaRCT501(uint64_t data, uint16_t nbits = kAiwaRcT501Bits, @@ -208,20 +274,20 @@ class IRsend { #endif #if SEND_GREE void sendGree(uint64_t data, uint16_t nbits = kGreeBits, - uint16_t repeat = kNoRepeat); + uint16_t repeat = kGreeDefaultRepeat); void sendGree(uint8_t data[], uint16_t nbytes = kGreeStateLength, - uint16_t repeat = kNoRepeat); + uint16_t repeat = kGreeDefaultRepeat); #endif #if SEND_PRONTO void sendPronto(uint16_t data[], uint16_t len, uint16_t repeat = kNoRepeat); #endif #if SEND_ARGO void sendArgo(unsigned char data[], uint16_t nbytes = kArgoStateLength, - uint16_t repeat = kNoRepeat); + uint16_t repeat = kArgoDefaultRepeat); #endif #if SEND_TROTEC void sendTrotec(unsigned char data[], uint16_t nbytes = kTrotecStateLength, - uint16_t repeat = kNoRepeat); + uint16_t repeat = kTrotecDefaultRepeat); #endif #if SEND_NIKAI void sendNikai(uint64_t data, uint16_t nbits = kNikaiBits, @@ -251,17 +317,17 @@ class IRsend { #endif #if (SEND_HAIER_AC || SEND_HAIER_AC_YRW02) void sendHaierAC(unsigned char data[], uint16_t nbytes = kHaierACStateLength, - uint16_t repeat = kNoRepeat); + uint16_t repeat = kHaierAcDefaultRepeat); #endif #if SEND_HAIER_AC_YRW02 void sendHaierACYRW02(unsigned char data[], uint16_t nbytes = kHaierACYRW02StateLength, - uint16_t repeat = kNoRepeat); + uint16_t repeat = kHaierAcYrw02DefaultRepeat); #endif #if SEND_HITACHI_AC void sendHitachiAC(unsigned char data[], uint16_t nbytes = kHitachiAcStateLength, - uint16_t repeat = kNoRepeat); + uint16_t repeat = kHitachiAcDefaultRepeat); #endif #if SEND_HITACHI_AC1 void sendHitachiAC1(unsigned char data[], @@ -280,7 +346,7 @@ class IRsend { #if SEND_WHIRLPOOL_AC void sendWhirlpoolAC(unsigned char data[], uint16_t nbytes = kWhirlpoolAcStateLength, - uint16_t repeat = kNoRepeat); + uint16_t repeat = kWhirlpoolAcDefaultRepeat); #endif #if SEND_LUTRON void sendLutron(uint64_t data, uint16_t nbits = kLutronBits, @@ -294,7 +360,7 @@ class IRsend { #if SEND_PANASONIC_AC void sendPanasonicAC(unsigned char data[], uint16_t nbytes = kPanasonicAcStateLength, - uint16_t repeat = kNoRepeat); + uint16_t repeat = kPanasonicAcDefaultRepeat); #endif #if SEND_PIONEER void sendPioneer(const uint64_t data, const uint16_t nbits = kPioneerBits, @@ -305,6 +371,24 @@ class IRsend { void sendMWM(unsigned char data[], uint16_t nbytes, uint16_t repeat = kNoRepeat); #endif +#if SEND_VESTEL_AC + void sendVestelAc(const uint64_t data, const uint16_t nbits = kVestelAcBits, + const uint16_t repeat = kNoRepeat); +#endif +#if SEND_TCL112AC + void sendTcl112Ac(const unsigned char data[], + const uint16_t nbytes = kTcl112AcStateLength, + const uint16_t repeat = kTcl112AcDefaultRepeat); +#endif +#if SEND_TECO + void sendTeco(uint64_t data, uint16_t nbits = kTecoBits, + uint16_t repeat = kNoRepeat); +#endif +#if SEND_LEGOPF + void sendLegoPf(const uint64_t data, const uint16_t nbits = kLegoPfBits, + const uint16_t repeat = kLegoPfMinRepeat); +#endif + protected: #ifdef UNIT_TEST @@ -319,8 +403,12 @@ class IRsend { uint8_t outputOff; VIRTUAL void ledOff(); VIRTUAL void ledOn(); +#ifndef UNIT_TEST private: +#else + uint32_t _freq_unittest; +#endif // UNIT_TEST uint16_t onTimePeriod; uint16_t offTimePeriod; uint16_t IRpin; diff --git a/lib/IRremoteESP8266-2.5.2.03/src/IRtimer.cpp b/lib/IRremoteESP8266-2.6.0/src/IRtimer.cpp similarity index 53% rename from lib/IRremoteESP8266-2.5.2.03/src/IRtimer.cpp rename to lib/IRremoteESP8266-2.6.0/src/IRtimer.cpp index 029637cbb94d..4173d763bae8 100644 --- a/lib/IRremoteESP8266-2.5.2.03/src/IRtimer.cpp +++ b/lib/IRremoteESP8266-2.6.0/src/IRtimer.cpp @@ -7,12 +7,12 @@ #ifdef UNIT_TEST // Used to help simulate elapsed time in unit tests. -extern uint32_t _IRtimer_unittest_now; +uint32_t _IRtimer_unittest_now = 0; +uint32_t _TimerMs_unittest_now = 0; #endif // UNIT_TEST // This class performs a simple time in useconds since instantiated. // Handles when the system timer wraps around (once). - IRtimer::IRtimer() { reset(); } void IRtimer::reset() { @@ -39,3 +39,32 @@ uint32_t IRtimer::elapsed() { #ifdef UNIT_TEST void IRtimer::add(uint32_t usecs) { _IRtimer_unittest_now += usecs; } #endif // UNIT_TEST + +// This class performs a simple time in milli-seoncds since instantiated. +// Handles when the system timer wraps around (once). +TimerMs::TimerMs() { reset(); } + +void TimerMs::reset() { +#ifndef UNIT_TEST + start = millis(); +#else + start = _TimerMs_unittest_now; +#endif +} + +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. +} + +// 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-2.5.2.03/src/IRtimer.h b/lib/IRremoteESP8266-2.6.0/src/IRtimer.h similarity index 64% rename from lib/IRremoteESP8266-2.5.2.03/src/IRtimer.h rename to lib/IRremoteESP8266-2.6.0/src/IRtimer.h index baca1cf74d3e..d00e1d0fad04 100644 --- a/lib/IRremoteESP8266-2.5.2.03/src/IRtimer.h +++ b/lib/IRremoteESP8266-2.6.0/src/IRtimer.h @@ -20,4 +20,16 @@ class IRtimer { uint32_t start; }; +class TimerMs { + public: + TimerMs(); + void reset(); + uint32_t elapsed(); +#ifdef UNIT_TEST + static void add(uint32_t msecs); +#endif // UNIT_TEST + + private: + uint32_t start; +}; #endif // IRTIMER_H_ diff --git a/lib/IRremoteESP8266-2.6.0/src/IRutils.cpp b/lib/IRremoteESP8266-2.6.0/src/IRutils.cpp new file mode 100644 index 000000000000..d909252413c5 --- /dev/null +++ b/lib/IRremoteESP8266-2.6.0/src/IRutils.cpp @@ -0,0 +1,768 @@ +// Copyright 2017 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" + +// Reverse the order of the requested least significant nr. of bits. +// Args: +// input: Bit pattern/integer to reverse. +// nbits: Nr. of bits to reverse. +// Returns: +// 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. +// +// Args: +// input: The value to print +// base: The output base. +// Returns: +// A string representation of the integer. +// Note: Based on Arduino's Print::printNumber() +#ifdef ARDUINO // Arduino's & C++'s string implementations can't co-exist. +String uint64ToString(uint64_t input, uint8_t base) { + String result = ""; +#else +std::string uint64ToString(uint64_t input, uint8_t base) { + std::string result = ""; +#endif + // 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; + + do { + char c = input % base; + input /= base; + + if (c < 10) + c += '0'; + else + c += 'A' - 10; + result = c + result; + } while (input); + return result; +} + +#ifdef ARDUINO +// Print a uint64_t/unsigned long long to the Serial port +// Serial.print() can't handle printing long longs. (uint64_t) +// +// Args: +// input: The value to print +// base: The output base. +void serialPrintUint64(uint64_t input, uint8_t base) { + Serial.print(uint64ToString(input, base)); +} +#endif + +// Convert a c-style str to a decode_type_t +// Note: Assumes str is upper case. +// +// Args: +// str: An upper-case C-style string. +// Returns: +// A decode_type_t enum. +decode_type_t strToDecodeType(const char *str) { + if (!strcmp(str, "UNKNOWN")) + return decode_type_t::UNKNOWN; + else if (!strcmp(str, "UNUSED")) + return decode_type_t::UNUSED; + else if (!strcmp(str, "AIWA_RC_T501")) + return decode_type_t::AIWA_RC_T501; + else if (!strcmp(str, "ARGO")) + return decode_type_t::ARGO; + else if (!strcmp(str, "CARRIER_AC")) + return decode_type_t::CARRIER_AC; + else if (!strcmp(str, "COOLIX")) + return decode_type_t::COOLIX; + else if (!strcmp(str, "DAIKIN")) + return decode_type_t::DAIKIN; + else if (!strcmp(str, "DAIKIN2")) + return decode_type_t::DAIKIN2; + else if (!strcmp(str, "DAIKIN216")) + return decode_type_t::DAIKIN216; + else if (!strcmp(str, "DENON")) + return decode_type_t::DENON; + else if (!strcmp(str, "DISH")) + return decode_type_t::DISH; + else if (!strcmp(str, "ELECTRA_AC")) + return decode_type_t::ELECTRA_AC; + else if (!strcmp(str, "FUJITSU_AC")) + return decode_type_t::FUJITSU_AC; + else if (!strcmp(str, "GICABLE")) + return decode_type_t::GICABLE; + else if (!strcmp(str, "GLOBALCACHE")) + return decode_type_t::GLOBALCACHE; + else if (!strcmp(str, "GREE")) + return decode_type_t::GREE; + else if (!strcmp(str, "HAIER_AC")) + return decode_type_t::HAIER_AC; + else if (!strcmp(str, "HAIER_AC_YRW02")) + return decode_type_t::HAIER_AC_YRW02; + else if (!strcmp(str, "HITACHI_AC")) + return decode_type_t::HITACHI_AC; + else if (!strcmp(str, "HITACHI_AC1")) + return decode_type_t::HITACHI_AC1; + else if (!strcmp(str, "HITACHI_AC2")) + return decode_type_t::HITACHI_AC2; + else if (!strcmp(str, "JVC")) + return decode_type_t::JVC; + else if (!strcmp(str, "KELVINATOR")) + return decode_type_t::KELVINATOR; + else if (!strcmp(str, "LEGOPF")) + return decode_type_t::LEGOPF; + else if (!strcmp(str, "LG")) + return decode_type_t::LG; + else if (!strcmp(str, "LG2")) + return decode_type_t::LG2; + else if (!strcmp(str, "LASERTAG")) + return decode_type_t::LASERTAG; + else if (!strcmp(str, "LUTRON")) + return decode_type_t::LUTRON; + else if (!strcmp(str, "MAGIQUEST")) + return decode_type_t::MAGIQUEST; + else if (!strcmp(str, "MIDEA")) + return decode_type_t::MIDEA; + else if (!strcmp(str, "MITSUBISHI")) + return decode_type_t::MITSUBISHI; + else if (!strcmp(str, "MITSUBISHI2")) + return decode_type_t::MITSUBISHI2; + else if (!strcmp(str, "MITSUBISHI_AC")) + return decode_type_t::MITSUBISHI_AC; + else if (!strcmp(str, "MWM")) + return decode_type_t::MWM; + else if (!strcmp(str, "NEC") || !strcmp(str, "NEC (NON-STRICT")) + return decode_type_t::NEC; + else if (!strcmp(str, "NIKAI")) + return decode_type_t::NIKAI; + else if (!strcmp(str, "PANASONIC")) + return decode_type_t::PANASONIC; + else if (!strcmp(str, "PANASONIC_AC")) + return decode_type_t::PANASONIC_AC; + else if (!strcmp(str, "PIONEER")) + return decode_type_t::PIONEER; + else if (!strcmp(str, "PRONTO")) + return decode_type_t::PRONTO; + else if (!strcmp(str, "RAW")) + return decode_type_t::RAW; + else if (!strcmp(str, "RC5")) + return decode_type_t::RC5; + else if (!strcmp(str, "RC5X")) + return decode_type_t::RC5X; + else if (!strcmp(str, "RC6")) + return decode_type_t::RC6; + else if (!strcmp(str, "RCMM")) + return decode_type_t::RCMM; + else if (!strcmp(str, "SAMSUNG")) + return decode_type_t::SAMSUNG; + else if (!strcmp(str, "SAMSUNG36")) + return decode_type_t::SAMSUNG36; + else if (!strcmp(str, "SAMSUNG_AC")) + return decode_type_t::SAMSUNG_AC; + else if (!strcmp(str, "SANYO")) + return decode_type_t::SANYO; + else if (!strcmp(str, "SANYO_LC7461")) + return decode_type_t::SANYO_LC7461; + else if (!strcmp(str, "SHARP")) + return decode_type_t::SHARP; + else if (!strcmp(str, "SHERWOOD")) + return decode_type_t::SHERWOOD; + else if (!strcmp(str, "SONY")) + return decode_type_t::SONY; + else if (!strcmp(str, "TCL112AC")) + return decode_type_t::TCL112AC; + else if (!strcmp(str, "TECO")) + return decode_type_t::TECO; + else if (!strcmp(str, "TOSHIBA_AC")) + return decode_type_t::TOSHIBA_AC; + else if (!strcmp(str, "TROTEC")) + return decode_type_t::TROTEC; + else if (!strcmp(str, "VESTEL_AC")) + return decode_type_t::VESTEL_AC; + else if (!strcmp(str, "WHIRLPOOL_AC")) + return decode_type_t::WHIRLPOOL_AC; + else if (!strcmp(str, "WHYNTER")) + return decode_type_t::WHYNTER; + // 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; + else + return decode_type_t::UNKNOWN; +} + +// Escape any special HTML (unsafe) characters in a string. e.g. anti-XSS. +// Args: +// unescaped: A string containing text to make HTML safe. +// Returns: +// A string that is HTML safe. +#ifdef ARDUINO // Arduino's & C++'s string implementations can't co-exist. +String htmlEscape(const String unescaped) { + String result = ""; +#else +std::string htmlEscape(const std::string unescaped) { + std::string result = ""; +#endif + 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 protocol type (enum etc) to a human readable string. +// Args: +// protocol: Nr. (enum) of the protocol. +// isRepeat: A flag indicating if it is a repeat message of the protocol. +// Returns: +// A string containing the protocol name. +#ifdef ARDUINO // Arduino's & C++'s string implementations can't co-exist. +String typeToString(const decode_type_t protocol, const bool isRepeat) { + String result = ""; +#else +std::string typeToString(const decode_type_t protocol, const bool isRepeat) { + std::string result = ""; +#endif + switch (protocol) { + case UNUSED: + result = F("UNUSED"); + break; + case AIWA_RC_T501: + result = F("AIWA_RC_T501"); + break; + case ARGO: + result = F("ARGO"); + break; + case CARRIER_AC: + result = F("CARRIER_AC"); + break; + case COOLIX: + result = F("COOLIX"); + break; + case DAIKIN: + result = F("DAIKIN"); + break; + case DAIKIN2: + result = F("DAIKIN2"); + break; + case DAIKIN216: + result = F("DAIKIN216"); + break; + case DENON: + result = F("DENON"); + break; + case DISH: + result = F("DISH"); + break; + case ELECTRA_AC: + result = F("ELECTRA_AC"); + break; + case FUJITSU_AC: + result = F("FUJITSU_AC"); + break; + case GICABLE: + result = F("GICABLE"); + break; + case GLOBALCACHE: + result = F("GLOBALCACHE"); + break; + case GREE: + result = F("GREE"); + break; + case HAIER_AC: + result = F("HAIER_AC"); + break; + case HAIER_AC_YRW02: + result = F("HAIER_AC_YRW02"); + break; + case HITACHI_AC: + result = F("HITACHI_AC"); + break; + case HITACHI_AC1: + result = F("HITACHI_AC1"); + break; + case HITACHI_AC2: + result = F("HITACHI_AC2"); + break; + case JVC: + result = F("JVC"); + break; + case KELVINATOR: + result = F("KELVINATOR"); + break; + case LEGOPF: + result = F("LEGOPF"); + break; + case LG: + result = F("LG"); + break; + case LG2: + result = F("LG2"); + break; + case LASERTAG: + result = F("LASERTAG"); + break; + case LUTRON: + result = F("LUTRON"); + break; + case MAGIQUEST: + result = F("MAGIQUEST"); + break; + case MIDEA: + result = F("MIDEA"); + break; + case MITSUBISHI: + result = F("MITSUBISHI"); + break; + case MITSUBISHI2: + result = F("MITSUBISHI2"); + break; + case MITSUBISHI_AC: + result = F("MITSUBISHI_AC"); + break; + case MITSUBISHI_HEAVY_88: + result = F("MITSUBISHI_HEAVY_88"); + break; + case MITSUBISHI_HEAVY_152: + result = F("MITSUBISHI_HEAVY_152"); + break; + case MWM: + result = F("MWM"); + break; + case NEC: + result = F("NEC"); + break; + case NEC_LIKE: + result = F("NEC (non-strict)"); + break; + case NIKAI: + result = F("NIKAI"); + break; + case PANASONIC: + result = F("PANASONIC"); + break; + case PANASONIC_AC: + result = F("PANASONIC_AC"); + break; + case PIONEER: + result = F("PIONEER"); + break; + case PRONTO: + result = F("PRONTO"); + break; + case RAW: + result = F("RAW"); + break; + case RC5: + result = F("RC5"); + break; + case RC5X: + result = F("RC5X"); + break; + case RC6: + result = F("RC6"); + break; + case RCMM: + result = F("RCMM"); + break; + case SAMSUNG: + result = F("SAMSUNG"); + break; + case SAMSUNG36: + result = F("SAMSUNG36"); + break; + case SAMSUNG_AC: + result = F("SAMSUNG_AC"); + break; + case SANYO: + result = F("SANYO"); + break; + case SANYO_LC7461: + result = F("SANYO_LC7461"); + break; + case SHARP: + result = F("SHARP"); + break; + case SHERWOOD: + result = F("SHERWOOD"); + break; + case SONY: + result = F("SONY"); + break; + case TCL112AC: + result = F("TCL112AC"); + break; + case TECO: + result = F("TECO"); + break; + case TOSHIBA_AC: + result = F("TOSHIBA_AC"); + break; + case TROTEC: + result = F("TROTEC"); + break; + case VESTEL_AC: + result = F("VESTEL_AC"); + break; + case WHIRLPOOL_AC: + result = F("WHIRLPOOL_AC"); + break; + case WHYNTER: + result = F("WHYNTER"); + break; + case UNKNOWN: + default: + result = F("UNKNOWN"); + break; + } + if (isRepeat) result += F(" (Repeat)"); + return result; +} + +// Does the given protocol use a complex state as part of the decode? +bool hasACState(const decode_type_t protocol) { + switch (protocol) { + case DAIKIN: + case DAIKIN2: + case DAIKIN216: + case ELECTRA_AC: + case FUJITSU_AC: + case GREE: + case HAIER_AC: + case HAIER_AC_YRW02: + case HITACHI_AC: + case HITACHI_AC1: + case HITACHI_AC2: + case KELVINATOR: + case MITSUBISHI_AC: + case MITSUBISHI_HEAVY_88: + case MITSUBISHI_HEAVY_152: + case MWM: + case PANASONIC_AC: + case SAMSUNG_AC: + case TCL112AC: + case TOSHIBA_AC: + 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. +// Args: +// results: A ptr to a decode result. +// Returns: +// A uint16_t containing the length. +uint16_t getCorrectedRawLength(const decode_results *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. +#ifdef ARDUINO +String resultToSourceCode(const decode_results *results) { + String output = ""; +#else +std::string resultToSourceCode(const decode_results *results) { + std::string output = ""; +#endif + // Start declaration + output += F("uint16_t "); // variable type + output += F("rawData["); // array name + output += uint64ToString(getCorrectedRawLength(results), 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 += F(", "); // ',' 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 (!hasACState(results->decode_type)) + output += ' ' + uint64ToString(results->value, 16); + output += F("\n"); + + // Now dump "known" codes + if (results->decode_type != UNKNOWN) { + if (hasACState(results->decode_type)) { +#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 += F(", "); + } + 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. +// +#ifdef ARDUINO +String resultToTimingInfo(const decode_results *results) { + String output = ""; + String value = ""; +#else +std::string resultToTimingInfo(const decode_results *results) { + std::string output = ""; + std::string value = ""; +#endif + 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 += '-'; // 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 += F(", "); // ',' 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. +// +#ifdef ARDUINO +String resultToHexidecimal(const decode_results *result) { + String output = ""; +#else +std::string resultToHexidecimal(const decode_results *result) { + std::string output = ""; +#endif + 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. +// +#ifdef ARDUINO +String resultToHumanReadableBasic(const decode_results *results) { + String output = ""; +#else +std::string resultToHumanReadableBasic(const decode_results *results) { + std::string output = ""; +#endif + // Show Encoding standard + output += F("Encoding : "); + output += typeToString(results->decode_type, results->repeat); + output += '\n'; + + // Show Code & length + output += F("Code : "); + output += resultToHexidecimal(results); + output += F(" ("); + output += uint64ToString(results->bits); + output += F(" bits)\n"); + return output; +} + +uint8_t sumBytes(uint8_t *start, const uint16_t length, const uint8_t init) { + uint8_t checksum = init; + uint8_t *ptr; + for (ptr = start; ptr - start < length; ptr++) checksum += *ptr; + return checksum; +} + +uint8_t xorBytes(uint8_t *start, const uint16_t length, const uint8_t init) { + uint8_t checksum = init; + uint8_t *ptr; + for (ptr = start; ptr - start < length; ptr++) checksum ^= *ptr; + return checksum; +} + +// Count the number of bits of a certain type. +// Args: +// start: Ptr to the start of data to count bits in. +// length: How many bytes to count. +// ones: Count the binary 1 bits. False for counting the 0 bits. +// init: Start the counting from this value. +// Returns: +// Nr. of bits found. +uint16_t countBits(const uint8_t *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. +// Args: +// data: The value you want bits counted for, starting from the LSB. +// length: How many bits to count. +// ones: Count the binary 1 bits. False for counting the 0 bits. +// init: Start the counting from this value. +// Returns: +// Nr. of bits found. +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; +} + +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)); +} diff --git a/lib/IRremoteESP8266-2.5.2.03/src/IRutils.h b/lib/IRremoteESP8266-2.6.0/src/IRutils.h similarity index 74% rename from lib/IRremoteESP8266-2.5.2.03/src/IRutils.h rename to lib/IRremoteESP8266-2.6.0/src/IRutils.h index c17375d98121..0d0b677b57b1 100644 --- a/lib/IRremoteESP8266-2.5.2.03/src/IRutils.h +++ b/lib/IRremoteESP8266-2.6.0/src/IRutils.h @@ -24,7 +24,8 @@ String resultToSourceCode(const decode_results *results); String resultToTimingInfo(const decode_results *results); String resultToHumanReadableBasic(const decode_results *results); String resultToHexidecimal(const decode_results *result); -#else +String htmlEscape(const String unescaped); +#else // ARDUINO std::string uint64ToString(uint64_t input, uint8_t base = 10); std::string typeToString(const decode_type_t protocol, const bool isRepeat = false); @@ -32,10 +33,16 @@ std::string resultToSourceCode(const decode_results *results); std::string resultToTimingInfo(const decode_results *results); std::string resultToHumanReadableBasic(const decode_results *results); std::string resultToHexidecimal(const decode_results *result); -#endif +std::string htmlEscape(const std::string unescaped); +#endif // ARDUINO bool hasACState(const decode_type_t protocol); uint16_t getCorrectedRawLength(const decode_results *results); uint8_t sumBytes(uint8_t *start, const uint16_t length, const uint8_t init = 0); +uint8_t xorBytes(uint8_t *start, const uint16_t length, const uint8_t init = 0); +uint16_t countBits(const uint8_t *start, const uint16_t length, + const bool ones = true, const uint16_t init = 0); +uint16_t countBits(const uint64_t data, const uint8_t length, + const bool ones = true, const uint16_t init = 0); uint64_t invertBits(const uint64_t data, const uint16_t nbits); - +decode_type_t strToDecodeType(const char *str); #endif // IRUTILS_H_ diff --git a/lib/IRremoteESP8266-2.5.2.03/src/ir_Aiwa.cpp b/lib/IRremoteESP8266-2.6.0/src/ir_Aiwa.cpp similarity index 100% rename from lib/IRremoteESP8266-2.5.2.03/src/ir_Aiwa.cpp rename to lib/IRremoteESP8266-2.6.0/src/ir_Aiwa.cpp diff --git a/lib/IRremoteESP8266-2.5.2.03/src/ir_Argo.cpp b/lib/IRremoteESP8266-2.6.0/src/ir_Argo.cpp similarity index 84% rename from lib/IRremoteESP8266-2.5.2.03/src/ir_Argo.cpp rename to lib/IRremoteESP8266-2.6.0/src/ir_Argo.cpp index 8a3e69f72ef8..d6711acd3872 100644 --- a/lib/IRremoteESP8266-2.5.2.03/src/ir_Argo.cpp +++ b/lib/IRremoteESP8266-2.6.0/src/ir_Argo.cpp @@ -40,9 +40,9 @@ IRArgoAC::IRArgoAC(uint16_t pin) : _irsend(pin) { stateReset(); } void IRArgoAC::begin() { _irsend.begin(); } #if SEND_ARGO -void IRArgoAC::send() { +void IRArgoAC::send(const uint16_t repeat) { checksum(); // Create valid checksum before sending - _irsend.sendArgo(argo); + _irsend.sendArgo(argo, kArgoStateLength, repeat); } #endif // SEND_ARGO @@ -228,3 +228,37 @@ void IRArgoAC::setRoomTemp(uint8_t temp) { argo[3] += temp << 5; // Append to bit 5,6,7 argo[4] += temp >> 3; // Remove lowest 3 bits and append in 0,1 } + +// Convert a standard A/C Fan speed into its native fan speed. +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 standard A/C Fan speed into its native fan speed. +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; + } +} diff --git a/lib/IRremoteESP8266-2.5.2.03/src/ir_Argo.h b/lib/IRremoteESP8266-2.6.0/src/ir_Argo.h similarity index 91% rename from lib/IRremoteESP8266-2.5.2.03/src/ir_Argo.h rename to lib/IRremoteESP8266-2.6.0/src/ir_Argo.h index b49fc35177cd..883c2ddfdb5d 100644 --- a/lib/IRremoteESP8266-2.5.2.03/src/ir_Argo.h +++ b/lib/IRremoteESP8266-2.6.0/src/ir_Argo.h @@ -6,6 +6,10 @@ #include "IRremoteESP8266.h" #include "IRsend.h" +#ifdef UNIT_TEST +#include "IRsend_test.h" +#endif + // ARGO Ulisse DCI @@ -55,7 +59,7 @@ const uint8_t kArgoFlapFull = 7; // 0b111 #define ARGO_COOL_ON kArgoCoolOn #define ARGO_COOL_OFF kArgoCoolOff #define ARGO_COOL_AUTO kArgoCoolAuto -#define ARGO_COOl_HUM kArgoCoolHum +#define ARGO_COOL_HUM kArgoCoolHum #define ARGO_HEAT_ON kArgoHeatOn #define ARGO_HEAT_AUTO kArgoHeatAuto #define ARGO_HEAT_BLINK kArgoHeatBlink @@ -80,7 +84,7 @@ class IRArgoAC { explicit IRArgoAC(uint16_t pin); #if SEND_ARGO - void send(); + void send(const uint16_t repeat = kArgoDefaultRepeat); #endif // SEND_ARGO void begin(); void on(); @@ -118,13 +122,19 @@ class IRArgoAC { void setRoomTemp(uint8_t temp); uint8_t* getRaw(); + uint8_t convertFan(const stdAc::fanspeed_t speed); + uint8_t convertSwingV(const stdAc::swingv_t position); +#ifndef UNIT_TEST private: + IRsend _irsend; // instance of the IR send class +#else + IRsendTest _irsend; // instance of the testing IR send class +#endif // # of bytes per command uint8_t argo[kArgoStateLength]; // Defined in IRremoteESP8266.h void stateReset(); void checksum(); - IRsend _irsend; // instance of the IR send class // Attributes uint8_t set_temp; diff --git a/lib/IRremoteESP8266-2.5.2.03/src/ir_Carrier.cpp b/lib/IRremoteESP8266-2.6.0/src/ir_Carrier.cpp similarity index 100% rename from lib/IRremoteESP8266-2.5.2.03/src/ir_Carrier.cpp rename to lib/IRremoteESP8266-2.6.0/src/ir_Carrier.cpp diff --git a/lib/IRremoteESP8266-2.5.2.03/src/ir_Coolix.cpp b/lib/IRremoteESP8266-2.6.0/src/ir_Coolix.cpp similarity index 72% rename from lib/IRremoteESP8266-2.5.2.03/src/ir_Coolix.cpp rename to lib/IRremoteESP8266-2.6.0/src/ir_Coolix.cpp index ee539af25660..2659a1d88176 100644 --- a/lib/IRremoteESP8266-2.5.2.03/src/ir_Coolix.cpp +++ b/lib/IRremoteESP8266-2.6.0/src/ir_Coolix.cpp @@ -93,25 +93,62 @@ void IRsend::sendCOOLIX(uint64_t data, uint16_t nbits, uint16_t repeat) { // https://github.com/markszabo/IRremoteESP8266/issues/484 IRCoolixAC::IRCoolixAC(uint16_t pin) : _irsend(pin) { stateReset(); } -void IRCoolixAC::stateReset() { remote_state = kCoolixDefaultState; } +void IRCoolixAC::stateReset() { setRaw(kCoolixDefaultState); } void IRCoolixAC::begin() { _irsend.begin(); } #if SEND_COOLIX -void IRCoolixAC::send() { _irsend.sendCOOLIX(remote_state); } +void IRCoolixAC::send(const uint16_t repeat) { + _irsend.sendCOOLIX(remote_state, kCoolixBits, repeat); +} #endif // SEND_COOLIX uint32_t IRCoolixAC::getRaw() { return remote_state; } -void IRCoolixAC::setRaw(const uint32_t new_code) { remote_state = new_code; } +void IRCoolixAC::setRaw(const uint32_t new_code) { + remote_state = new_code; + saved_state = new_code; +} + +// Return true if the current state is a special state. +bool IRCoolixAC::isSpecialState(void) { + switch (remote_state) { + case kCoolixClean: + case kCoolixLed: + case kCoolixOff: + case kCoolixSwing: + case kCoolixSleep: + case kCoolixTurbo: + return true; + default: + return false; + } +} + +void IRCoolixAC::updateSavedState(void) { + if (!isSpecialState()) saved_state = remote_state; +} + +void IRCoolixAC::recoverSavedState(void) { + // If the current state is a special one, last known normal one. + if (isSpecialState()) remote_state = saved_state; + // If the saved_state was also a special state, reset as we expect a normal + // state out of all this. + if (isSpecialState()) stateReset(); +} + +uint32_t IRCoolixAC::getNormalState(void) { + return isSpecialState() ? saved_state : remote_state; +} void IRCoolixAC::setTempRaw(const uint8_t code) { + recoverSavedState(); remote_state &= ~kCoolixTempMask; // Clear the old temp. remote_state |= (code << 4); } uint8_t IRCoolixAC::getTempRaw() { - return (remote_state & kCoolixTempMask) >> 4; + return (getNormalState() & kCoolixTempMask) >> 4; } void IRCoolixAC::setTemp(const uint8_t desired) { @@ -130,6 +167,7 @@ uint8_t IRCoolixAC::getTemp() { } void IRCoolixAC::setSensorTempRaw(const uint8_t code) { + recoverSavedState(); remote_state &= ~kCoolixSensorTempMask; // Clear previous sensor temp. remote_state |= ((code & 0xF) << 8); } @@ -143,7 +181,8 @@ void IRCoolixAC::setSensorTemp(const uint8_t desired) { } uint8_t IRCoolixAC::getSensorTemp() { - return ((remote_state & kCoolixSensorTempMask) >> 8) + kCoolixSensorTempMin; + return ((getNormalState() & kCoolixSensorTempMask) >> 8) + + kCoolixSensorTempMin; } bool IRCoolixAC::getPower() { @@ -152,25 +191,35 @@ bool IRCoolixAC::getPower() { } void IRCoolixAC::setPower(const bool power) { - if (!power) remote_state = kCoolixOff; - // There really is no distinct "on" setting, so do nothing. + if (power) { + // There really is no distinct "on" setting, just ensure it a normal state. + recoverSavedState(); + } else { + updateSavedState(); + remote_state = kCoolixOff; + } } bool IRCoolixAC::getSwing() { return remote_state == kCoolixSwing; } void IRCoolixAC::setSwing() { // Assumes that repeated sending "swing" toggles the action on the device. + updateSavedState(); remote_state = kCoolixSwing; } bool IRCoolixAC::getSleep() { return remote_state == kCoolixSleep; } -void IRCoolixAC::setSleep() { remote_state = kCoolixSleep; } +void IRCoolixAC::setSleep() { + updateSavedState(); + remote_state = kCoolixSleep; +} bool IRCoolixAC::getTurbo() { return remote_state == kCoolixTurbo; } void IRCoolixAC::setTurbo() { // Assumes that repeated sending "turbo" toggles the action on the device. + updateSavedState(); remote_state = kCoolixTurbo; } @@ -178,19 +227,24 @@ bool IRCoolixAC::getLed() { return remote_state == kCoolixLed; } void IRCoolixAC::setLed() { // Assumes that repeated sending "Led" toggles the action on the device. + updateSavedState(); remote_state = kCoolixLed; } bool IRCoolixAC::getClean() { return remote_state == kCoolixClean; } -void IRCoolixAC::setClean() { remote_state = kCoolixClean; } +void IRCoolixAC::setClean() { + updateSavedState(); + remote_state = kCoolixClean; +} bool IRCoolixAC::getZoneFollow() { - return remote_state & kCoolixZoneFollowMask; + return getNormalState() & kCoolixZoneFollowMask; } // Internal use only. void IRCoolixAC::setZoneFollow(bool state) { + recoverSavedState(); if (state) { remote_state |= kCoolixZoneFollowMask; } else { @@ -199,6 +253,7 @@ void IRCoolixAC::setZoneFollow(bool state) { } void IRCoolixAC::clearSensorTemp() { + recoverSavedState(); setZoneFollow(false); setSensorTempRaw(kCoolixSensorTempIgnoreCode); } @@ -212,6 +267,7 @@ void IRCoolixAC::setMode(const uint8_t mode) { case kCoolixAuto: case kCoolixHeat: case kCoolixDry: + recoverSavedState(); remote_state = (remote_state & ~kCoolixModeMask) | (actualmode << 2); // Force the temp into a known-good state. setTemp(getTemp()); @@ -220,21 +276,25 @@ void IRCoolixAC::setMode(const uint8_t mode) { } uint8_t IRCoolixAC::getMode() { - uint8_t mode = (remote_state & kCoolixModeMask) >> 2; + uint8_t mode = (getNormalState() & kCoolixModeMask) >> 2; if (mode == kCoolixDry) if (getTempRaw() == kCoolixFanTempCode) return kCoolixFan; return mode; } -uint8_t IRCoolixAC::getFan() { return (remote_state & kCoolixFanMask) >> 13; } +uint8_t IRCoolixAC::getFan() { + return (getNormalState() & kCoolixFanMask) >> 13; +} void IRCoolixAC::setFan(const uint8_t speed) { + recoverSavedState(); uint8_t newspeed = speed; switch (speed) { case kCoolixFanMin: case kCoolixFanMed: case kCoolixFanMax: case kCoolixFanAuto: + case kCoolixFanAuto0: case kCoolixFanZoneFollow: case kCoolixFanFixed: break; @@ -245,6 +305,38 @@ void IRCoolixAC::setFan(const uint8_t speed) { remote_state |= ((newspeed << 13) & kCoolixFanMask); } +// Convert a standard A/C mode into its 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 standard A/C Fan speed into its native fan speed. +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 the internal state into a human readable string. #ifdef ARDUINO String IRCoolixAC::toString() { @@ -253,89 +345,97 @@ String IRCoolixAC::toString() { std::string IRCoolixAC::toString() { std::string result = ""; #endif // ARDUINO - result += "Power: "; + result += F("Power: "); if (getPower()) { - result += "On"; + result += F("On"); } else { - result += "Off"; + result += F("Off"); return result; // If it's off, there is no other info. } - result += ", Fan: " + uint64ToString(getFan()); - switch (getFan()) { - case kCoolixFanAuto: - result += " (AUTO)"; - break; - case kCoolixFanMax: - result += " (MAX)"; - break; - case kCoolixFanMin: - result += " (MIN)"; - break; - case kCoolixFanMed: - result += " (MED)"; - break; - case kCoolixFanZoneFollow: - result += " (ZONEFOLLOW)"; - break; - case kCoolixFanFixed: - result += " (FIXED)"; - break; - default: - result += " (UNKNOWN)"; - } // Special modes. if (getSwing()) { - result += ", Swing: Toggle"; + result += F(", Swing: Toggle"); return result; } if (getSleep()) { - result += ", Sleep: Toggle"; + result += F(", Sleep: Toggle"); return result; } if (getTurbo()) { - result += ", Turbo: Toggle"; + result += F(", Turbo: Toggle"); return result; } if (getLed()) { - result += ", Led: Toggle"; + result += F(", Led: Toggle"); return result; } if (getClean()) { - result += ", Mode: Self clean"; + result += F(", Clean: Toggle"); return result; } - result += ", Mode: " + uint64ToString(getMode()); + result += F(", Mode: "); + result += uint64ToString(getMode()); switch (getMode()) { case kCoolixAuto: - result += " (AUTO)"; + result += F(" (AUTO)"); break; case kCoolixCool: - result += " (COOL)"; + result += F(" (COOL)"); break; case kCoolixHeat: - result += " (HEAT)"; + result += F(" (HEAT)"); break; case kCoolixDry: - result += " (DRY)"; + result += F(" (DRY)"); break; case kCoolixFan: - result += " (FAN)"; + result += F(" (FAN)"); break; default: - result += " (UNKNOWN)"; + result += F(" (UNKNOWN)"); + } + result += F(", Fan: "); + result += uint64ToString(getFan()); + switch (getFan()) { + case kCoolixFanAuto: + result += F(" (AUTO)"); + break; + case kCoolixFanAuto0: + result += F(" (AUTO0)"); + break; + case kCoolixFanMax: + result += F(" (MAX)"); + break; + case kCoolixFanMin: + result += F(" (MIN)"); + break; + case kCoolixFanMed: + result += F(" (MED)"); + break; + case kCoolixFanZoneFollow: + result += F(" (ZONEFOLLOW)"); + break; + case kCoolixFanFixed: + result += F(" (FIXED)"); + break; + default: + result += F(" (UNKNOWN)"); + } + if (getMode() != kCoolixFan) { // Fan mode doesn't have a temperature. + result += F(", Temp: "); + result += uint64ToString(getTemp()); + result += 'C'; } - if (getMode() != kCoolixFan) // Fan mode doesn't have a temperature. - result += ", Temp: " + uint64ToString(getTemp()) + "C"; - result += ", Zone Follow: "; + result += F(", Zone Follow: "); if (getZoneFollow()) - result += "On"; + result += F("On"); else - result += "Off"; - result += ", Sensor Temp: "; + result += F("Off"); + result += F(", Sensor Temp: "); if (getSensorTemp() > kCoolixSensorTempMax) - result += "Ignored"; + result += F("Ignored"); else - result += uint64ToString(getSensorTemp()) + "C"; + result += uint64ToString(getSensorTemp()) + F("C"); return result; } diff --git a/lib/IRremoteESP8266-2.5.2.03/src/ir_Coolix.h b/lib/IRremoteESP8266-2.6.0/src/ir_Coolix.h similarity index 82% rename from lib/IRremoteESP8266-2.5.2.03/src/ir_Coolix.h rename to lib/IRremoteESP8266-2.6.0/src/ir_Coolix.h index ee455207485c..d85db98d7da0 100644 --- a/lib/IRremoteESP8266-2.5.2.03/src/ir_Coolix.h +++ b/lib/IRremoteESP8266-2.6.0/src/ir_Coolix.h @@ -14,6 +14,9 @@ #endif #include "IRremoteESP8266.h" #include "IRsend.h" +#ifdef UNIT_TEST +#include "IRsend_test.h" +#endif // CCCCC OOOOO OOOOO LL IIIII XX XX // CC C OO OO OO OO LL III XX XX @@ -30,11 +33,11 @@ // Constants // Modes -const uint8_t kCoolixCool = 0b00; -const uint8_t kCoolixDry = 0b01; -const uint8_t kCoolixAuto = 0b10; -const uint8_t kCoolixHeat = 0b11; -const uint8_t kCoolixFan = 4; // Synthetic. +const uint8_t kCoolixCool = 0b000; +const uint8_t kCoolixDry = 0b001; +const uint8_t kCoolixAuto = 0b010; +const uint8_t kCoolixHeat = 0b011; +const uint8_t kCoolixFan = 0b100; // Synthetic. const uint32_t kCoolixModeMask = 0b000000000000000000001100; // 0xC const uint32_t kCoolixZoneFollowMask = 0b000010000000000000000000; // 0x80000 // Fan Control @@ -42,6 +45,7 @@ const uint8_t kCoolixFanMin = 0b100; const uint8_t kCoolixFanMed = 0b010; const uint8_t kCoolixFanMax = 0b001; const uint8_t kCoolixFanAuto = 0b101; +const uint8_t kCoolixFanAuto0 = 0b000; const uint8_t kCoolixFanZoneFollow = 0b110; const uint8_t kCoolixFanFixed = 0b111; const uint32_t kCoolixFanMask = 0b000000001110000000000000; // 0x00E000 @@ -90,7 +94,7 @@ class IRCoolixAC { void stateReset(); #if SEND_COOLIX - void send(); + void send(const uint16_t repeat = kCoolixDefaultRepeat); #endif // SEND_COOLIX void begin(); void on(); @@ -119,21 +123,30 @@ class IRCoolixAC { bool getZoneFollow(); uint32_t getRaw(); void setRaw(const uint32_t new_code); - + uint8_t convertMode(const stdAc::opmode_t mode); + uint8_t convertFan(const stdAc::fanspeed_t speed); #ifdef ARDUINO String toString(); #else std::string toString(); #endif +#ifndef UNIT_TEST private: - // The state of the IR remote in IR code form. - uint32_t remote_state; IRsend _irsend; +#else + IRsendTest _irsend; +#endif + uint32_t remote_state; // The state of the IR remote in IR code form. + uint32_t saved_state; // Copy of the state if we required a special mode. void setTempRaw(const uint8_t code); uint8_t getTempRaw(); void setSensorTempRaw(const uint8_t code); void setZoneFollow(const bool state); + bool isSpecialState(void); + void updateSavedState(void); + void recoverSavedState(void); + uint32_t getNormalState(void); }; #endif // IR_COOLIX_H_ diff --git a/lib/IRremoteESP8266-2.6.0/src/ir_Daikin.cpp b/lib/IRremoteESP8266-2.6.0/src/ir_Daikin.cpp new file mode 100644 index 000000000000..358dbd603c39 --- /dev/null +++ b/lib/IRremoteESP8266-2.6.0/src/ir_Daikin.cpp @@ -0,0 +1,1712 @@ +/* +An Arduino sketch to emulate IR Daikin ARC433** & ARC477A1 remote control unit +Read more at: +http://harizanov.com/2012/02/control-daikin-air-conditioner-over-the-internet/ + +Copyright 2016 sillyfrog +Copyright 2017 sillyfrog, crankyoldgit +Copyright 2018-2019 crankyoldgit +*/ + +#include "ir_Daikin.h" +#include +#ifndef ARDUINO +#include +#endif +#include "IRrecv.h" +#include "IRremoteESP8266.h" +#include "IRsend.h" +#ifdef UNIT_TEST +#include "IRsend_test.h" +#endif +#include "IRutils.h" + +// DDDDD AAA IIIII KK KK IIIII NN NN +// DD DD AAAAA III KK KK III NNN NN +// DD DD AA AA III KKKK III NN N NN +// DD DD AAAAAAA III KK KK III NN NNN +// DDDDDD AA AA IIIII KK KK IIIII NN NN + +// Constants +// Ref: +// https://github.com/mharizanov/Daikin-AC-remote-control-over-the-Internet/tree/master/IRremote +// http://rdlab.cdmt.vn/project-2013/daikin-ir-protocol +// https://github.com/markszabo/IRremoteESP8266/issues/582 + +#if SEND_DAIKIN +// Send a Daikin A/C message. +// +// Args: +// data: An array of kDaikinStateLength bytes containing the IR command. +// +// Status: STABLE +// +// Ref: +// IRDaikinESP.cpp +// https://github.com/mharizanov/Daikin-AC-remote-control-over-the-Internet/tree/master/IRremote +// 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 + +IRDaikinESP::IRDaikinESP(uint16_t pin) : _irsend(pin) { stateReset(); } + +void IRDaikinESP::begin(void) { _irsend.begin(); } + +#if SEND_DAIKIN +void IRDaikinESP::send(const uint16_t repeat) { + this->checksum(); + _irsend.sendDaikin(remote, kDaikinStateLength, repeat); +} +#endif // SEND_DAIKIN + +// Verify the checksums are valid for a given state. +// Args: +// state: The array to verify the checksums of. +// length: The size of the state. +// Returns: +// A boolean. +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) { + remote[kDaikinByteChecksum1] = sumBytes(remote, kDaikinSection1Length - 1); + remote[kDaikinByteChecksum2] = sumBytes(remote + kDaikinSection1Length, + kDaikinSection2Length - 1); + remote[kDaikinByteChecksum3] = sumBytes(remote + kDaikinSection1Length + + kDaikinSection2Length, + kDaikinSection3Length - 1); +} + +void IRDaikinESP::stateReset(void) { + for (uint8_t i = 0; i < kDaikinStateLength; i++) remote[i] = 0x0; + + remote[0] = 0x11; + remote[1] = 0xDA; + remote[2] = 0x27; + remote[4] = 0xC5; + // remote[7] is a checksum byte, it will be set by checksum(). + + remote[8] = 0x11; + remote[9] = 0xDA; + remote[10] = 0x27; + remote[12] = 0x42; + // remote[15] is a checksum byte, it will be set by checksum(). + remote[16] = 0x11; + remote[17] = 0xDA; + remote[18] = 0x27; + remote[21] = 0x49; + remote[22] = 0x1E; + remote[24] = 0xB0; + remote[27] = 0x06; + remote[28] = 0x60; + remote[31] = 0xC0; + // remote[34] is a checksum byte, it will be set by checksum(). + this->checksum(); +} + +uint8_t *IRDaikinESP::getRaw(void) { + this->checksum(); // Ensure correct settings before sending. + return remote; +} + +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; + this->stateReset(); + } + for (uint8_t i = 0; i < length && i < kDaikinStateLength; i++) + remote[i + offset] = new_code[i]; +} + +void IRDaikinESP::on(void) { remote[kDaikinBytePower] |= kDaikinBitPower; } + +void IRDaikinESP::off(void) { remote[kDaikinBytePower] &= ~kDaikinBitPower; } + +void IRDaikinESP::setPower(const bool on) { + if (on) + this->on(); + else + this->off(); +} + +bool IRDaikinESP::getPower(void) { + return remote[kDaikinBytePower] & kDaikinBitPower; +} + +// Set the temp in deg C +void IRDaikinESP::setTemp(const uint8_t temp) { + uint8_t degrees = std::max(temp, kDaikinMinTemp); + degrees = std::min(degrees, kDaikinMaxTemp); + remote[kDaikinByteTemp] = degrees << 1; +} + +uint8_t IRDaikinESP::getTemp(void) { return remote[kDaikinByteTemp] >> 1; } + +// Set the speed of the fan, 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; + remote[kDaikinByteFan] &= 0x0F; + remote[kDaikinByteFan] |= (fanset << 4); +} + +uint8_t IRDaikinESP::getFan(void) { + uint8_t fan = remote[kDaikinByteFan] >> 4; + if (fan != kDaikinFanQuiet && fan != kDaikinFanAuto) fan -= 2; + return fan; +} + +uint8_t IRDaikinESP::getMode(void) { return remote[kDaikinBytePower] >> 4; } + +void IRDaikinESP::setMode(const uint8_t mode) { + switch (mode) { + case kDaikinAuto: + case kDaikinCool: + case kDaikinHeat: + case kDaikinFan: + case kDaikinDry: + remote[kDaikinBytePower] &= 0b10001111; + remote[kDaikinBytePower] |= (mode << 4); + break; + default: + this->setMode(kDaikinAuto); + } +} + +void IRDaikinESP::setSwingVertical(const bool on) { + if (on) + remote[kDaikinByteFan] |= 0x0F; + else + remote[kDaikinByteFan] &= 0xF0; +} + +bool IRDaikinESP::getSwingVertical(void) { + return remote[kDaikinByteFan] & 0x0F; +} + +void IRDaikinESP::setSwingHorizontal(const bool on) { + if (on) + remote[kDaikinByteSwingH] |= 0x0F; + else + remote[kDaikinByteSwingH] &= 0xF0; +} + +bool IRDaikinESP::getSwingHorizontal(void) { + return remote[kDaikinByteSwingH] & 0x0F; +} + +void IRDaikinESP::setQuiet(const bool on) { + if (on) { + remote[kDaikinByteSilent] |= kDaikinBitSilent; + // Powerful & Quiet mode being on are mutually exclusive. + this->setPowerful(false); + } else { + remote[kDaikinByteSilent] &= ~kDaikinBitSilent; + } +} + +bool IRDaikinESP::getQuiet(void) { + return remote[kDaikinByteSilent] & kDaikinBitSilent; +} + +void IRDaikinESP::setPowerful(const bool on) { + if (on) { + remote[kDaikinBytePowerful] |= kDaikinBitPowerful; + // Powerful, Quiet, & Econo mode being on are mutually exclusive. + this->setQuiet(false); + this->setEcono(false); + } else { + remote[kDaikinBytePowerful] &= ~kDaikinBitPowerful; + } +} + +bool IRDaikinESP::getPowerful(void) { + return remote[kDaikinBytePowerful] & kDaikinBitPowerful; +} + +void IRDaikinESP::setSensor(const bool on) { + if (on) + remote[kDaikinByteSensor] |= kDaikinBitSensor; + else + remote[kDaikinByteSensor] &= ~kDaikinBitSensor; +} + +bool IRDaikinESP::getSensor(void) { + return remote[kDaikinByteSensor] & kDaikinBitSensor; +} + +void IRDaikinESP::setEcono(const bool on) { + if (on) { + remote[kDaikinByteEcono] |= kDaikinBitEcono; + // Powerful & Econo mode being on are mutually exclusive. + this->setPowerful(false); + } else { + remote[kDaikinByteEcono] &= ~kDaikinBitEcono; + } +} + +bool IRDaikinESP::getEcono(void) { + return remote[kDaikinByteEcono] & kDaikinBitEcono; +} + +void IRDaikinESP::setEye(const bool on) { + if (on) + remote[kDaikinByteEye] |= kDaikinBitEye; + else + remote[kDaikinByteEye] &= ~kDaikinBitEye; +} + +bool IRDaikinESP::getEye(void) { + return remote[kDaikinByteEye] & kDaikinBitEye; +} + +void IRDaikinESP::setMold(const bool on) { + if (on) + remote[kDaikinByteMold] |= kDaikinBitMold; + else + remote[kDaikinByteMold] &= ~kDaikinBitMold; +} + +bool IRDaikinESP::getMold(void) { + return remote[kDaikinByteMold] & kDaikinBitMold; +} + +void IRDaikinESP::setComfort(const bool on) { + if (on) + remote[kDaikinByteComfort] |= kDaikinBitComfort; + else + remote[kDaikinByteComfort] &= ~kDaikinBitComfort; +} + +bool IRDaikinESP::getComfort(void) { + return remote[kDaikinByteComfort] & kDaikinBitComfort; +} + +// starttime: Number of minutes after midnight. +void IRDaikinESP::enableOnTimer(const uint16_t starttime) { + remote[kDaikinByteOnTimer] |= kDaikinBitOnTimer; + remote[kDaikinByteOnTimerMinsLow] = starttime; + // only keep 4 bits + remote[kDaikinByteOnTimerMinsHigh] &= 0xF0; + remote[kDaikinByteOnTimerMinsHigh] |= ((starttime >> 8) & 0x0F); +} + +void IRDaikinESP::disableOnTimer(void) { + this->enableOnTimer(kDaikinUnusedTime); + remote[kDaikinByteOnTimer] &= ~kDaikinBitOnTimer; +} + +uint16_t IRDaikinESP::getOnTime(void) { + return ((remote[kDaikinByteOnTimerMinsHigh] & 0x0F) << 8) + + remote[kDaikinByteOnTimerMinsLow]; +} + +bool IRDaikinESP::getOnTimerEnabled(void) { + return remote[kDaikinByteOnTimer] & kDaikinBitOnTimer; +} + +// endtime: Number of minutes after midnight. +void IRDaikinESP::enableOffTimer(const uint16_t endtime) { + remote[kDaikinByteOffTimer] |= kDaikinBitOffTimer; + remote[kDaikinByteOffTimerMinsHigh] = endtime >> 4; + remote[kDaikinByteOffTimerMinsLow] &= 0x0F; + remote[kDaikinByteOffTimerMinsLow] |= ((endtime & 0x0F) << 4); +} + +void IRDaikinESP::disableOffTimer(void) { + this->enableOffTimer(kDaikinUnusedTime); + remote[kDaikinByteOffTimer] &= ~kDaikinBitOffTimer; +} + +uint16_t IRDaikinESP::getOffTime(void) { + return (remote[kDaikinByteOffTimerMinsHigh] << 4) + + ((remote[kDaikinByteOffTimerMinsLow] & 0xF0) >> 4); +} + +bool IRDaikinESP::getOffTimerEnabled(void) { + return remote[kDaikinByteOffTimer] & kDaikinBitOffTimer; +} + +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 + remote[kDaikinByteClockMinsLow] = mins; + // only keep 4 bits + remote[kDaikinByteClockMinsHigh] &= 0xF0; + remote[kDaikinByteClockMinsHigh] |= ((mins >> 8) & 0x0F); +} + +uint16_t IRDaikinESP::getCurrentTime(void) { + return ((remote[kDaikinByteClockMinsHigh] & 0x0F) << 8) + + remote[kDaikinByteClockMinsLow]; +} + +#ifdef ARDUINO +String IRDaikinESP::renderTime(const uint16_t timemins) { + String ret; +#else // ARDUINO +std::string IRDaikinESP::renderTime(const uint16_t timemins) { + std::string ret; +#endif // ARDUINO + ret = uint64ToString(timemins / 60) + ':'; + uint8_t mins = timemins % 60; + if (mins < 10) ret += '0'; + ret += uint64ToString(mins); + return ret; +} + +// Convert the internal state into a human readable string. +#ifdef ARDUINO +String IRDaikinESP::toString(void) { + String result = ""; +#else // ARDUINO +std::string IRDaikinESP::toString(void) { + std::string result = ""; +#endif // ARDUINO + result += F("Power: "); + result += this->getPower() ? F("On") : F("Off"); + result += F(", Mode: "); + result += uint64ToString(this->getMode()); + switch (this->getMode()) { + case kDaikinAuto: + result += F(" (AUTO)"); + break; + case kDaikinCool: + result += F(" (COOL)"); + break; + case kDaikinHeat: + result += F(" (HEAT)"); + break; + case kDaikinDry: + result += F(" (DRY)"); + break; + case kDaikinFan: + result += F(" (FAN)"); + break; + default: + result += F(" (UNKNOWN)"); + } + result += F(", Temp: "); + result += uint64ToString(this->getTemp()); + result += F("C, Fan: "); + result += uint64ToString(this->getFan()); + switch (this->getFan()) { + case kDaikinFanAuto: + result += F(" (AUTO)"); + break; + case kDaikinFanQuiet: + result += F(" (QUIET)"); + break; + case kDaikinFanMin: + result += F(" (MIN)"); + break; + case kDaikinFanMax: + result += F(" (MAX)"); + break; + } + result += F(", Powerful: "); + result += this->getPowerful() ? F("On") : F("Off"); + result += F(", Quiet: "); + result += this->getQuiet() ? F("On") : F("Off"); + result += F(", Sensor: "); + result += this->getSensor() ? F("On") : F("Off"); + result += F(", Eye: "); + result += this->getEye() ? F("On") : F("Off"); + result += F(", Mold: "); + result += this->getMold() ? F("On") : F("Off"); + result += F(", Comfort: "); + result += this->getComfort() ? F("On") : F("Off"); + result += F(", Swing (Horizontal): "); + result += this->getSwingHorizontal() ? F("On") : F("Off"); + result += F(", Swing (Vertical): "); + result += this->getSwingVertical() ? F("On") : F("Off"); + result += F(", Current Time: "); + result += this->renderTime(this->getCurrentTime()); + result += F(", On Time: "); + if (this->getOnTimerEnabled()) + result += this->renderTime(this->getOnTime()); + else + result += F("Off"); + result += F(", Off Time: "); + if (this->getOffTimerEnabled()) + result += this->renderTime(this->getOffTime()); + else + result += F("Off"); + return result; +} + +// Convert a standard A/C mode into its native mode. +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 standard A/C Fan speed into its native fan speed. +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 kDaikinFanMin + 1; + case stdAc::fanspeed_t::kHigh: + return kDaikinFanMax - 1; + case stdAc::fanspeed_t::kMax: + return kDaikinFanMax; + default: + return kDaikinFanAuto; + } +} + +#if DECODE_DAIKIN +// Decode the supplied Daikin A/C message. +// Args: +// results: Ptr to the data to decode and where to store the decode result. +// nbits: Nr. of bits to expect in the data portion. (kDaikinBits) +// strict: Flag to indicate if we strictly adhere to the specification. +// Returns: +// boolean: True if it can decode it, false if it can't. +// +// Status: BETA / Should be working. +// +// Ref: +// https://github.com/mharizanov/Daikin-AC-remote-control-over-the-Internet/tree/master/IRremote +bool IRrecv::decodeDaikin(decode_results *results, 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)) + return false; + + // Compliance + if (strict && nbits != kDaikinBits) return false; + + uint16_t offset = kStartOffset; + match_result_t data_result; + uint16_t dataBitsSoFar = 0; + uint16_t i = 0; + + // 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. + + // Read the Data sections. + // Keep reading bytes until we either run out of section or state to fill. + const uint8_t kSectionSize[kDaikinSections] = { + kDaikinSection1Length, kDaikinSection2Length, kDaikinSection3Length}; + for (uint8_t section = 0, pos = 0; section < kDaikinSections; + section++) { + pos += kSectionSize[section]; + // Section Footer + if (!matchMark(results->rawbuf[offset++], kDaikinBitMark, + kDaikinTolerance, kDaikinMarkExcess)) return false; + if (!matchSpace(results->rawbuf[offset++], kDaikinZeroSpace + kDaikinGap, + kDaikinTolerance, kDaikinMarkExcess)) return false; + // Section Header + if (!matchMark(results->rawbuf[offset++], kDaikinHdrMark, + kDaikinTolerance, kDaikinMarkExcess)) return false; + if (!matchSpace(results->rawbuf[offset++], kDaikinHdrSpace, + kDaikinTolerance, kDaikinMarkExcess)) return false; + + // Section Data + for (; offset <= results->rawlen - 16 && i < pos; + i++, dataBitsSoFar += 8, offset += data_result.used) { + // Read in a byte at a time. + data_result = + matchData(&(results->rawbuf[offset]), 8, + kDaikinBitMark, kDaikinOneSpace, + kDaikinBitMark, kDaikinZeroSpace, + kDaikinTolerance, kDaikinMarkExcess, false); + if (data_result.success == false) break; // Fail + results->state[i] = (uint8_t)data_result.data; + } + } + + // Footer + if (!matchMark(results->rawbuf[offset++], kDaikinBitMark)) return false; + if (offset < results->rawlen && + !matchAtLeast(results->rawbuf[offset], kDaikinGap)) + return false; + + // Compliance + if (strict) { + // Re-check we got the correct size/length due to the way we read the data. + if (dataBitsSoFar != kDaikinBits) return false; + // Validate the checksum. + if (!IRDaikinESP::validChecksum(results->state)) return false; + } + + // Success + results->decode_type = DAIKIN; + results->bits = dataBitsSoFar; + // 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 A/C message. +// +// Args: +// data: An array of kDaikin2StateLength bytes containing the IR command. +// +// Status: BETA/Appears to work. +// +// Ref: +// https://github.com/markszabo/IRremoteESP8266/issues/582 +void IRsend::sendDaikin2(unsigned char data[], uint16_t nbytes, + 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 for handling Daikin2 A/C messages. +// +// Code by crankyoldgit, Reverse engineering analysis by sheppy99 +// +// Supported Remotes: Daikin ARC477A1 remote +// +// Ref: +// https://github.com/markszabo/IRremoteESP8266/issues/582 +// https://docs.google.com/spreadsheets/d/1f8EGfIbBUo2B-CzUFdrgKQprWakoYNKM80IKZN4KXQE/edit?usp=sharing +// https://www.daikin.co.nz/sites/default/files/daikin-split-system-US7-FTXZ25-50NV1B.pdf +IRDaikin2::IRDaikin2(uint16_t pin) : _irsend(pin) { stateReset(); } + +void IRDaikin2::begin() { _irsend.begin(); } + +#if SEND_DAIKIN2 +void IRDaikin2::send(const uint16_t repeat) { + checksum(); + _irsend.sendDaikin2(remote_state, kDaikin2StateLength, repeat); +} +#endif // SEND_DAIKIN2 + +// Verify the checksum is valid for a given state. +// Args: +// state: The array to verify the checksum of. +// length: The size of the state. +// Returns: +// A boolean. +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() { + remote_state[kDaikin2Section1Length - 1] = sumBytes( + remote_state, kDaikin2Section1Length - 1); + remote_state[kDaikin2StateLength -1 ] = sumBytes( + remote_state + kDaikin2Section1Length, kDaikin2Section2Length - 1); +} + +void IRDaikin2::stateReset() { + for (uint8_t i = 0; i < kDaikin2StateLength; i++) remote_state[i] = 0x0; + + remote_state[0] = 0x11; + remote_state[1] = 0xDA; + remote_state[2] = 0x27; + remote_state[4] = 0x01; + remote_state[6] = 0xC0; + remote_state[7] = 0x70; + remote_state[8] = 0x08; + remote_state[9] = 0x0C; + remote_state[10] = 0x80; + remote_state[11] = 0x04; + remote_state[12] = 0xB0; + remote_state[13] = 0x16; + remote_state[14] = 0x24; + remote_state[17] = 0xBE; + remote_state[18] = 0xD0; + // remote_state[19] is a checksum byte, it will be set by checksum(). + remote_state[20] = 0x11; + remote_state[21] = 0xDA; + remote_state[22] = 0x27; + remote_state[25] = 0x08; + remote_state[28] = 0xA0; + remote_state[35] = 0xC1; + remote_state[36] = 0x80; + remote_state[37] = 0x60; + // remote_state[38] is a checksum byte, it will be set by checksum(). + disableOnTimer(); + disableOffTimer(); + disableSleepTimer(); + checksum(); +} + +uint8_t *IRDaikin2::getRaw() { + checksum(); // Ensure correct settings before sending. + return remote_state; +} + +void IRDaikin2::setRaw(const uint8_t new_code[]) { + for (uint8_t i = 0; i < kDaikin2StateLength; i++) + remote_state[i] = new_code[i]; +} + +void IRDaikin2::on() { + remote_state[25] |= kDaikinBitPower; + remote_state[6] &= ~kDaikin2BitPower; +} + +void IRDaikin2::off() { + remote_state[25] &= ~kDaikinBitPower; + remote_state[6] |= kDaikin2BitPower; +} + +void IRDaikin2::setPower(const bool state) { + if (state) + on(); + else + off(); +} + +bool IRDaikin2::getPower() { + return (remote_state[25] & kDaikinBitPower) && + !(remote_state[6] & kDaikin2BitPower); +} + +uint8_t IRDaikin2::getMode() { return remote_state[25] >> 4; } + +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; + } + remote_state[25] &= 0b10001111; + remote_state[25] |= (mode << 4); + // Redo the temp setting as Cool mode has a different min temp. + if (mode == kDaikinCool) this->setTemp(this->getTemp()); +} + +// Set the temp in deg C +void IRDaikin2::setTemp(const uint8_t desired) { + // The A/C has a different min temp if in cool mode. + uint8_t temp = std::max( + (this->getMode() == kDaikinCool) ? kDaikin2MinCoolTemp : kDaikinMinTemp, + desired); + temp = std::min(kDaikinMaxTemp, temp); + remote_state[26] = temp * 2; +} + +// Set the speed of the fan, 1-5 or kDaikinFanAuto or kDaikinFanQuiet +void IRDaikin2::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; + remote_state[28] &= 0x0F; + remote_state[28] |= (fanset << 4); +} + +uint8_t IRDaikin2::getFan() { + uint8_t fan = remote_state[28] >> 4; + if (fan != kDaikinFanQuiet && fan != kDaikinFanAuto) fan -= 2; + return fan; +} + +uint8_t IRDaikin2::getTemp() { return remote_state[26] / 2; } + +void IRDaikin2::setSwingVertical(const uint8_t position) { + switch (position) { + case kDaikin2SwingVHigh: + case 2: + case 3: + case 4: + case 5: + case kDaikin2SwingVLow: + case kDaikin2SwingVBreeze: + case kDaikin2SwingVCirculate: + case kDaikin2SwingVAuto: + remote_state[18] &= 0xF0; + remote_state[18] |= (position & 0x0F); + } +} + +uint8_t IRDaikin2::getSwingVertical() { return remote_state[18] & 0x0F; } + +void IRDaikin2::setSwingHorizontal(const uint8_t position) { + remote_state[17] = position; +} + +uint8_t IRDaikin2::getSwingHorizontal() { return remote_state[17]; } + +void IRDaikin2::setCurrentTime(const uint16_t numMins) { + uint16_t mins = numMins; + if (numMins > 24 * 60) mins = 0; // If > 23:59, set to 00:00 + remote_state[5] = (uint8_t)(mins & 0xFF); + // only keep 4 bits + remote_state[6] &= 0xF0; + remote_state[6] |= (uint8_t)((mins >> 8) & 0x0F); +} + +uint16_t IRDaikin2::getCurrentTime() { + return ((remote_state[6] & 0x0F) << 8) + remote_state[5]; +} + +// starttime: Number of minutes after midnight. +// Note: Timer location is shared with sleep timer. +void IRDaikin2::enableOnTimer(const uint16_t starttime) { + clearSleepTimerFlag(); + remote_state[25] |= kDaikinBitOnTimer; // Set the On Timer flag. + remote_state[30] = (uint8_t)(starttime & 0xFF); + // only keep 4 bits + remote_state[31] &= 0xF0; + remote_state[31] |= (uint8_t)((starttime >> 8) & 0x0F); +} + +void IRDaikin2::clearOnTimerFlag() { + remote_state[25] &= ~kDaikinBitOnTimer; +} + +void IRDaikin2::disableOnTimer() { + enableOnTimer(kDaikinUnusedTime); + clearOnTimerFlag(); + clearSleepTimerFlag(); +} + +uint16_t IRDaikin2::getOnTime() { + return ((remote_state[31] & 0x0F) << 8) + remote_state[30]; +} + +bool IRDaikin2::getOnTimerEnabled() { + return remote_state[25] & kDaikinBitOnTimer; +} + +// endtime: Number of minutes after midnight. +void IRDaikin2::enableOffTimer(const uint16_t endtime) { + remote_state[25] |= kDaikinBitOffTimer; // Set the Off Timer flag. + remote_state[32] = (uint8_t)((endtime >> 4) & 0xFF); + remote_state[31] &= 0x0F; + remote_state[31] |= (uint8_t)((endtime & 0xF) << 4); +} + +void IRDaikin2::disableOffTimer() { + enableOffTimer(kDaikinUnusedTime); + remote_state[25] &= ~kDaikinBitOffTimer; // Clear the Off Timer flag. +} + +uint16_t IRDaikin2::getOffTime() { + return (remote_state[32] << 4) + (remote_state[31] >> 4); +} + +bool IRDaikin2::getOffTimerEnabled() { + return remote_state[25] & kDaikinBitOffTimer; +} + +uint8_t IRDaikin2::getBeep() { + return remote_state[7] >> 6; +} + +void IRDaikin2::setBeep(const uint8_t beep) { + remote_state[7] &= ~kDaikin2BeepMask; + remote_state[7] |= ((beep << 6) & kDaikin2BeepMask); +} + +uint8_t IRDaikin2::getLight() { + return (remote_state[7] & kDaikin2LightMask) >> 4; +} + +void IRDaikin2::setLight(const uint8_t light) { + remote_state[7] &= ~kDaikin2LightMask; + remote_state[7] |= ((light << 4) & kDaikin2LightMask); +} + +void IRDaikin2::setMold(const bool on) { + if (on) + remote_state[8] |= kDaikin2BitMold; + else + remote_state[8] &= ~kDaikin2BitMold; +} + +bool IRDaikin2::getMold() { + return remote_state[8] & kDaikin2BitMold; +} + +// Auto clean setting. +void IRDaikin2::setClean(const bool on) { + if (on) + remote_state[8] |= kDaikin2BitClean; + else + remote_state[8] &= ~kDaikin2BitClean; +} + +bool IRDaikin2::getClean() { + return remote_state[8] & kDaikin2BitClean; +} + +// Fresh Air settings. +void IRDaikin2::setFreshAir(const bool on) { + if (on) + remote_state[8] |= kDaikin2BitFreshAir; + else + remote_state[8] &= ~kDaikin2BitFreshAir; +} + +bool IRDaikin2::getFreshAir() { + return remote_state[8] & kDaikin2BitFreshAir; +} + +void IRDaikin2::setFreshAirHigh(const bool on) { + if (on) + remote_state[8] |= kDaikin2BitFreshAirHigh; + else + remote_state[8] &= ~kDaikin2BitFreshAirHigh; +} + +bool IRDaikin2::getFreshAirHigh() { + return remote_state[8] & kDaikin2BitFreshAirHigh; +} + +void IRDaikin2::setEyeAuto(bool on) { + if (on) + remote_state[13] |= kDaikin2BitEyeAuto; + else + remote_state[13] &= ~kDaikin2BitEyeAuto; +} + +bool IRDaikin2::getEyeAuto() { + return remote_state[13] & kDaikin2BitEyeAuto; +} + +void IRDaikin2::setEye(bool on) { + if (on) + remote_state[36] |= kDaikin2BitEye; + else + remote_state[36] &= ~kDaikin2BitEye; +} + +bool IRDaikin2::getEye() { + return remote_state[36] & kDaikin2BitEye; +} + +void IRDaikin2::setEcono(bool on) { + if (on) + remote_state[36] |= kDaikinBitEcono; + else + remote_state[36] &= ~kDaikinBitEcono; +} + +bool IRDaikin2::getEcono() { + return remote_state[36] & kDaikinBitEcono; +} + +// sleeptime: Number of minutes. +// Note: Timer location is shared with On Timer. +void IRDaikin2::enableSleepTimer(const uint16_t sleeptime) { + enableOnTimer(sleeptime); + clearOnTimerFlag(); + remote_state[36] |= kDaikin2BitSleepTimer; // Set the Sleep Timer flag. +} + +void IRDaikin2::clearSleepTimerFlag() { + remote_state[36] &= ~kDaikin2BitSleepTimer; +} + +void IRDaikin2::disableSleepTimer() { + disableOnTimer(); +} + +uint16_t IRDaikin2::getSleepTime() { + return getOnTime(); +} + +bool IRDaikin2::getSleepTimerEnabled() { + return remote_state[36] & kDaikin2BitSleepTimer; +} + +void IRDaikin2::setQuiet(const bool on) { + if (on) { + remote_state[33] |= kDaikinBitSilent; + // Powerful & Quiet mode being on are mutually exclusive. + setPowerful(false); + } else { + remote_state[33] &= ~kDaikinBitSilent; + } +} + +bool IRDaikin2::getQuiet() { return remote_state[33] & kDaikinBitSilent; } + +void IRDaikin2::setPowerful(const bool on) { + if (on) { + remote_state[33] |= kDaikinBitPowerful; + // Powerful & Quiet mode being on are mutually exclusive. + setQuiet(false); + } else { + remote_state[33] &= ~kDaikinBitPowerful; + } +} + +bool IRDaikin2::getPowerful() { return remote_state[33] & kDaikinBitPowerful; } + +void IRDaikin2::setPurify(const bool on) { + if (on) + remote_state[36] |= kDaikin2BitPurify; + else + remote_state[36] &= ~kDaikin2BitPurify; +} + +bool IRDaikin2::getPurify() { return remote_state[36] & kDaikin2BitPurify; } + +// Convert a standard A/C mode into its native mode. +uint8_t IRDaikin2::convertMode(const stdAc::opmode_t mode) { + return IRDaikinESP::convertMode(mode); +} + +// Convert a standard A/C Fan speed into its native fan speed. +uint8_t IRDaikin2::convertFan(const stdAc::fanspeed_t speed) { + return IRDaikinESP::convertFan(speed); +} + +// Convert a standard A/C vertical swing into its native version. +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 + kDaikin2SwingVHigh; + default: + return kDaikin2SwingVAuto; + } +} + +// Convert the internal state into a human readable string. +#ifdef ARDUINO +String IRDaikin2::toString() { + String result = ""; +#else // ARDUINO +std::string IRDaikin2::toString() { + std::string result = ""; +#endif // ARDUINO + result += F("Power: "); + if (getPower()) + result += F("On"); + else + result += F("Off"); + result += F(", Mode: "); + result += uint64ToString(getMode()); + switch (getMode()) { + case kDaikinAuto: + result += F(" (AUTO)"); + break; + case kDaikinCool: + result += F(" (COOL)"); + break; + case kDaikinHeat: + result += F(" (HEAT)"); + break; + case kDaikinDry: + result += F(" (DRY)"); + break; + case kDaikinFan: + result += F(" (FAN)"); + break; + default: + result += F(" (UNKNOWN)"); + } + result += F(", Temp: "); + result += uint64ToString(getTemp()); + result += F("C, Fan: "); + result += uint64ToString(getFan()); + switch (getFan()) { + case kDaikinFanAuto: + result += F(" (Auto)"); + break; + case kDaikinFanQuiet: + result += F(" (Quiet)"); + break; + case kDaikinFanMin: + result += F(" (Min)"); + break; + case kDaikinFanMax: + result += F(" (Max)"); + break; + } + result += F(", Swing (V): "); + result += uint64ToString(getSwingVertical()); + switch (getSwingVertical()) { + case kDaikin2SwingVHigh: + result += F(" (Highest)"); + break; + case 2: + case 3: + case 4: + case 5: + break; + case kDaikin2SwingVLow: + result += F(" (Lowest)"); + break; + case kDaikin2SwingVBreeze: + result += F(" (Breeze)"); + break; + case kDaikin2SwingVCirculate: + result += F(" (Circulate)"); + break; + case kDaikin2SwingVAuto: + result += F(" (Auto)"); + break; + default: + result += F(" (Unknown)"); + } + result += F(", Swing (H): "); + result += uint64ToString(getSwingHorizontal()); + switch (getSwingHorizontal()) { + case kDaikin2SwingHAuto: + result += F(" (Auto)"); + break; + case kDaikin2SwingHSwing: + result += F(" (Swing)"); + break; + } + result += F(", Clock: "); + result += IRDaikinESP::renderTime(getCurrentTime()); + result += F(", On Time: "); + if (getOnTimerEnabled()) + result += IRDaikinESP::renderTime(getOnTime()); + else + result += F("Off"); + result += F(", Off Time: "); + if (getOffTimerEnabled()) + result += IRDaikinESP::renderTime(getOffTime()); + else + result += F("Off"); + result += F(", Sleep Time: "); + if (getSleepTimerEnabled()) + result += IRDaikinESP::renderTime(getSleepTime()); + else + result += F("Off"); + result += F(", Beep: "); + result += uint64ToString(getBeep()); + switch (getBeep()) { + case kDaikinBeepLoud: + result += F(" (Loud)"); + break; + case kDaikinBeepQuiet: + result += F(" (Quiet)"); + break; + case kDaikinBeepOff: + result += F(" (Off)"); + break; + default: + result += F(" (UNKNOWN)"); + } + result += F(", Light: "); + result += uint64ToString(getLight()); + switch (getLight()) { + case kDaikinLightBright: + result += F(" (Bright)"); + break; + case kDaikinLightDim: + result += F(" (Dim)"); + break; + case kDaikinLightOff: + result += F(" (Off)"); + break; + default: + result += F(" (UNKNOWN)"); + } + result += F(", Mold: "); + result += (getMold() ? F("On") : F("Off")); + result += F(", Clean: "); + result += (getClean() ? F("On") : F("Off")); + result += F(", Fresh Air: "); + if (getFreshAir()) + result += (getFreshAirHigh() ? "High" : "On"); + else + result += F("Off"); + result += F(", Eye: "); + result += (getEye() ? F("On") : F("Off")); + result += F(", Eye Auto: "); + result += (getEyeAuto() ? F("On") : F("Off")); + result += F(", Quiet: "); + result += (getQuiet() ? F("On") : F("Off")); + result += F(", Powerful: "); + result += (getPowerful() ? F("On") : F("Off")); + result += ", Purify: "; + result += (getPurify() ? F("On") : F("Off")); + result += F(", Econo: "); + result += (getEcono() ? F("On") : F("Off")); + return result; +} + +#if DECODE_DAIKIN2 +// Decode the supplied Daikin2 A/C message. +// Args: +// results: Ptr to the data to decode and where to store the decode result. +// nbits: Nr. of bits to expect in the data portion. (kDaikin2Bits) +// strict: Flag to indicate if we strictly adhere to the specification. +// Returns: +// boolean: True if it can decode it, false if it can't. +// +// Supported devices: +// - Daikin FTXZ25NV1B, FTXZ35NV1B, FTXZ50NV1B Aircon +// - Daikin ARC477A1 remote +// +// Status: BETA / Work as expected. +// +// Ref: +// https://github.com/mharizanov/Daikin-AC-remote-control-over-the-Internet/tree/master/IRremote +bool IRrecv::decodeDaikin2(decode_results *results, uint16_t nbits, + bool strict) { + if (results->rawlen < 2 * (nbits + kHeader + kFooter) + kHeader - 1) + return false; + + // Compliance + if (strict && nbits != kDaikin2Bits) return false; + + uint16_t offset = kStartOffset; + uint16_t dataBitsSoFar = 0; + uint16_t i = 0; + match_result_t data_result; + uint8_t sectionSize[kDaikin2Sections] = {kDaikin2Section1Length, + kDaikin2Section2Length}; + + // Leader + if (!matchMark(results->rawbuf[offset++], kDaikin2LeaderMark, + kDaikin2Tolerance)) return false; + if (!matchSpace(results->rawbuf[offset++], kDaikin2LeaderSpace, + kDaikin2Tolerance)) return false; + + // Sections + // Keep reading bytes until we either run out of section or state to fill. + for (uint8_t section = 0, pos = 0; section < kDaikin2Sections; + section++) { + pos += sectionSize[section]; + + // Section Header + if (!matchMark(results->rawbuf[offset++], kDaikin2HdrMark, + kDaikin2Tolerance)) return false; + if (!matchSpace(results->rawbuf[offset++], kDaikin2HdrSpace, + kDaikin2Tolerance)) return false; + + // Section Data + for (; offset <= results->rawlen - 16 && i < pos; + i++, dataBitsSoFar += 8, offset += data_result.used) { + // Read in a byte at a time. + data_result = + matchData(&(results->rawbuf[offset]), 8, kDaikin2BitMark, + kDaikin2OneSpace, kDaikin2BitMark, + kDaikin2ZeroSpace, kDaikin2Tolerance, kMarkExcess, false); + if (data_result.success == false) break; // Fail + results->state[i] = (uint8_t)data_result.data; + } + + // Section Footer + if (!matchMark(results->rawbuf[offset++], kDaikin2BitMark, + kDaikin2Tolerance)) return false; + if (section < kDaikin2Sections - 1) { // Inter-section gaps. + if (!matchSpace(results->rawbuf[offset++], kDaikin2Gap, + kDaikin2Tolerance)) return false; + } else { // Last section / End of message gap. + if (offset <= results->rawlen && + !matchAtLeast(results->rawbuf[offset++], kDaikin2Gap, + kDaikin2Tolerance)) return false; + } + } + + // Compliance + if (strict) { + // Re-check we got the correct size/length due to the way we read the data. + if (dataBitsSoFar != kDaikin2Bits) return false; + // Validate the checksum. + if (!IRDaikin2::validChecksum(results->state)) return false; + } + + // Success + results->decode_type = DAIKIN2; + results->bits = dataBitsSoFar; + // 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 Daikin 216 bit A/C message. +// +// Args: +// data: An array of kDaikin216StateLength bytes containing the IR command. +// +// Status: Alpha/Untested on a real device. +// +// Supported devices: +// - Daikin ARC433B69 remote. +// +// Ref: +// https://github.com/markszabo/IRremoteESP8266/issues/689 +// 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 for handling Daikin 216 bit / 27 byte A/C messages. +// +// Code by crankyoldgit. +// +// Supported Remotes: Daikin ARC433B69 remote +// +// Ref: +// https://github.com/markszabo/IRremoteESP8266/issues/689 +// https://github.com/danny-source/Arduino_DY_IRDaikin +IRDaikin216::IRDaikin216(uint16_t pin) : _irsend(pin) { stateReset(); } + +void IRDaikin216::begin() { _irsend.begin(); } + +#if SEND_DAIKIN216 +void IRDaikin216::send(const uint16_t repeat) { + checksum(); + _irsend.sendDaikin216(remote_state, kDaikin216StateLength, repeat); +} +#endif // SEND_DAIKIN216 + +// Verify the checksum is valid for a given state. +// Args: +// state: The array to verify the checksum of. +// length: The size of the state. +// Returns: +// A boolean. +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() { + remote_state[kDaikin216Section1Length - 1] = sumBytes( + remote_state, kDaikin216Section1Length - 1); + remote_state[kDaikin216StateLength - 1] = sumBytes( + remote_state + kDaikin216Section1Length, kDaikin216Section2Length - 1); +} + +void IRDaikin216::stateReset() { + for (uint8_t i = 0; i < kDaikin216StateLength; i++) remote_state[i] = 0x00; + remote_state[0] = 0x11; + remote_state[1] = 0xDA; + remote_state[2] = 0x27; + remote_state[3] = 0xF0; + // remote_state[7] is a checksum byte, it will be set by checksum(). + remote_state[8] = 0x11; + remote_state[9] = 0xDA; + remote_state[10] = 0x27; + remote_state[23] = 0xC0; + // remote_state[26] is a checksum byte, it will be set by checksum(). +} + +uint8_t *IRDaikin216::getRaw() { + checksum(); // Ensure correct settings before sending. + return remote_state; +} + +void IRDaikin216::setRaw(const uint8_t new_code[]) { + for (uint8_t i = 0; i < kDaikin216StateLength; i++) + remote_state[i] = new_code[i]; +} + + +void IRDaikin216::on() { + remote_state[kDaikin216BytePower] |= kDaikinBitPower; +} + +void IRDaikin216::off() { + remote_state[kDaikin216BytePower] &= ~kDaikinBitPower; +} + +void IRDaikin216::setPower(const bool state) { + if (state) + on(); + else + off(); +} + +bool IRDaikin216::getPower() { + return remote_state[kDaikin216BytePower] & kDaikinBitPower; +} + +uint8_t IRDaikin216::getMode() { + return (remote_state[kDaikin216ByteMode] & kDaikin216MaskMode) >> 4; +} + +void IRDaikin216::setMode(const uint8_t mode) { + switch (mode) { + case kDaikinAuto: + case kDaikinCool: + case kDaikinHeat: + case kDaikinFan: + case kDaikinDry: + remote_state[kDaikin216ByteMode] &= ~kDaikin216MaskMode; + remote_state[kDaikin216ByteMode] |= (mode << 4); + break; + default: + this->setMode(kDaikinAuto); + } +} + +// Convert a standard A/C mode into its native mode. +uint8_t IRDaikin216::convertMode(const stdAc::opmode_t mode) { + return IRDaikinESP::convertMode(mode); +} + +// Set the temp in deg C +void IRDaikin216::setTemp(const uint8_t temp) { + uint8_t degrees = std::max(temp, kDaikinMinTemp); + degrees = std::min(degrees, kDaikinMaxTemp); + remote_state[kDaikin216ByteTemp] &= ~kDaikin216MaskTemp; + remote_state[kDaikin216ByteTemp] |= (degrees << 1); +} + +uint8_t IRDaikin216::getTemp(void) { + return (remote_state[kDaikin216ByteTemp] & kDaikin216MaskTemp) >> 1; +} + +// Set the speed of the fan, 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; + remote_state[kDaikin216ByteFan] &= ~kDaikin216MaskFan; + remote_state[kDaikin216ByteFan] |= (fanset << 4); +} + +uint8_t IRDaikin216::getFan() { + uint8_t fan = remote_state[kDaikin216ByteFan] >> 4; + if (fan != kDaikinFanQuiet && fan != kDaikinFanAuto) fan -= 2; + return fan; +} + +// Convert a standard A/C Fan speed into its native fan speed. +uint8_t IRDaikin216::convertFan(const stdAc::fanspeed_t speed) { + return IRDaikinESP::convertFan(speed); +} + +void IRDaikin216::setSwingVertical(const bool on) { + if (on) + remote_state[kDaikin216ByteSwingV] |= kDaikin216MaskSwingV; + else + remote_state[kDaikin216ByteSwingV] &= ~kDaikin216MaskSwingV; +} + +bool IRDaikin216::getSwingVertical(void) { + return remote_state[kDaikin216ByteSwingV] & kDaikin216MaskSwingV; +} + +void IRDaikin216::setSwingHorizontal(const bool on) { + if (on) + remote_state[kDaikin216ByteSwingH] |= kDaikin216MaskSwingH; + else + remote_state[kDaikin216ByteSwingH] &= ~kDaikin216MaskSwingH; +} + +bool IRDaikin216::getSwingHorizontal(void) { + return remote_state[kDaikin216ByteSwingH] & kDaikin216MaskSwingH; +} + +// This is a horrible hack till someone works out the quiet mode bit. +void IRDaikin216::setQuiet(const bool on) { + if (on) + this->setFan(kDaikinFanQuiet); + else if (this->getFan() == kDaikinFanQuiet) + this->setFan(kDaikinFanAuto); +} + +// This is a horrible hack till someone works out the quiet mode bit. +bool IRDaikin216::getQuiet(void) { + return this->getFan() == kDaikinFanQuiet; +} + +// Convert the internal state into a human readable string. +#ifdef ARDUINO +String IRDaikin216::toString() { + String result = ""; +#else // ARDUINO +std::string IRDaikin216::toString() { + std::string result = ""; +#endif // ARDUINO + result += F("Power: "); + if (this->getPower()) + result += F("On"); + else + result += F("Off"); + result += F(", Mode: "); + result += uint64ToString(this->getMode()); + switch (getMode()) { + case kDaikinAuto: + result += F(" (AUTO)"); + break; + case kDaikinCool: + result += F(" (COOL)"); + break; + case kDaikinHeat: + result += F(" (HEAT)"); + break; + case kDaikinDry: + result += F(" (DRY)"); + break; + case kDaikinFan: + result += F(" (FAN)"); + break; + default: + result += F(" (UNKNOWN)"); + } + result += F(", Temp: "); + result += uint64ToString(this->getTemp()); + result += F("C, Fan: "); + result += uint64ToString(this->getFan()); + switch (this->getFan()) { + case kDaikinFanAuto: + result += F(" (AUTO)"); + break; + case kDaikinFanQuiet: + result += F(" (QUIET)"); + break; + case kDaikinFanMin: + result += F(" (MIN)"); + break; + case kDaikinFanMax: + result += F(" (MAX)"); + break; + } + result += F(", Swing (Horizontal): "); + result += this->getSwingHorizontal() ? F("On") : F("Off"); + result += F(", Swing (Vertical): "); + result += this->getSwingVertical() ? F("On") : F("Off"); + result += F(", Quiet: "); + result += (getQuiet() ? F("On") : F("Off")); + return result; +} + +#if DECODE_DAIKIN216 +// Decode the supplied Daikin 216 bit A/C message. +// Args: +// results: Ptr to the data to decode and where to store the decode result. +// nbits: Nr. of bits to expect in the data portion. (kDaikin216Bits) +// strict: Flag to indicate if we strictly adhere to the specification. +// Returns: +// boolean: True if it can decode it, false if it can't. +// +// Supported devices: +// - Daikin ARC433B69 remote. +// +// Status: BETA / Should be working. +// +// Ref: +// https://github.com/markszabo/IRremoteESP8266/issues/689 +// https://github.com/danny-source/Arduino_DY_IRDaikin +bool IRrecv::decodeDaikin216(decode_results *results, const uint16_t nbits, + const bool strict) { + if (results->rawlen < 2 * (nbits + kHeader + kFooter) - 1) + return false; + + // Compliance + if (strict && nbits != kDaikin216Bits) return false; + + uint16_t offset = kStartOffset; + uint16_t dataBitsSoFar = 0; + uint16_t i = 0; + match_result_t data_result; + uint8_t sectionSize[kDaikin216Sections] = {kDaikin216Section1Length, + kDaikin216Section2Length}; + + // Sections + // Keep reading bytes until we either run out of section or state to fill. + for (uint8_t section = 0, pos = 0; section < kDaikin216Sections; + section++) { + pos += sectionSize[section]; + + // Section Header + if (!matchMark(results->rawbuf[offset++], kDaikin216HdrMark)) return false; + if (!matchSpace(results->rawbuf[offset++], kDaikin2HdrSpace)) return false; + + // Section Data + for (; offset <= results->rawlen - 16 && i < pos; + i++, dataBitsSoFar += 8, offset += data_result.used) { + // Read in a byte at a time. + data_result = + matchData(&(results->rawbuf[offset]), 8, kDaikin216BitMark, + kDaikin216OneSpace, kDaikin216BitMark, + kDaikin216ZeroSpace, kTolerance, kMarkExcess, false); + if (data_result.success == false) break; // Fail + results->state[i] = (uint8_t)data_result.data; + } + + // Section Footer + if (!matchMark(results->rawbuf[offset++], kDaikin216BitMark)) return false; + if (section < kDaikin216Sections - 1) { // Inter-section gaps. + if (!matchSpace(results->rawbuf[offset++], kDaikin216Gap)) return false; + } else { // Last section / End of message gap. + if (offset <= results->rawlen && + !matchAtLeast(results->rawbuf[offset++], kDaikin216Gap)) return false; + } + } + + // Compliance + if (strict) { + // Re-check we got the correct size/length due to the way we read the data. + if (dataBitsSoFar != kDaikin216Bits) return false; + // Validate the checksum. + if (!IRDaikin216::validChecksum(results->state)) return false; + } + + // Success + results->decode_type = decode_type_t::DAIKIN216; + results->bits = dataBitsSoFar; + // 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 diff --git a/lib/IRremoteESP8266-2.6.0/src/ir_Daikin.h b/lib/IRremoteESP8266-2.6.0/src/ir_Daikin.h new file mode 100644 index 000000000000..038e8edd97f3 --- /dev/null +++ b/lib/IRremoteESP8266-2.6.0/src/ir_Daikin.h @@ -0,0 +1,444 @@ +// Copyright 2016 sillyfrog +// Copyright 2017 sillyfrog, crankyoldgit +// Copyright 2018-2019 crankyoldgit +#ifndef IR_DAIKIN_H_ +#define IR_DAIKIN_H_ + +#ifndef UNIT_TEST +#include +#else +#include +#endif +#include "IRrecv.h" +#include "IRremoteESP8266.h" +#include "IRsend.h" +#ifdef UNIT_TEST +#include "IRsend_test.h" +#endif + +// DDDDD AAA IIIII KK KK IIIII NN NN +// DD DD AAAAA III KK KK III NNN NN +// DD DD AA AA III KKKK III NN N NN +// DD DD AAAAAAA III KK KK III NN NNN +// DDDDDD AA AA IIIII KK KK IIIII NN NN + +/* + Daikin AC map + byte 6= + b4:Comfort + byte 7= checksum of the first part (and last byte before a 29ms pause) + byte 13=Current time, mins past midnight, low bits + byte 14 + b0-b3=Current time, mins past midnight, high bits + byte 15= checksum of the second part (and last byte before a 29ms pause) + byte 21=mode + b7 = 0 + b6+b5+b4 = Mode + Modes: b6+b5+b4 + 011 = Cool + 100 = Heat (temp 23) + 110 = FAN (temp not shown, but 25) + 000 = Fully Automatic (temp 25) + 010 = DRY (temp 0xc0 = 96 degrees c) + b3 = 1 + b2 = OFF timer set + b1 = ON timer set + b0 = Air Conditioner ON + byte 22=temp*2 (Temp should be between 10 - 32) + byte 24=Fan + FAN control + b7+b6+b5+b4 = Fan speed + Fan: b7+b6+b5+b4 + 0Ă—3 = 1 bar + 0Ă—4 = 2 bar + 0Ă—5 = 3 bar + 0Ă—6 = 4 bar + 0Ă—7 = 5 bar + 0xa = Auto + 0xb = Quite + b3+b2+b1+b0 = Swing control up/down + Swing control up/down: + 0000 = Swing up/down off + 1111 = Swing up/down on + byte 25 + Swing control left/right: + 0000 = Swing left/right off + 1111 = Swing left/right on + byte 26=On timer mins past midnight, low bits + byte 27 + b0-b3=On timer mins past midnight, high bits + b4-b7=Off timer mins past midnight, low bits + byte 28=Off timer mins past midnight, high bits + byte 29=Aux -> Powerful (bit 1), Silent (bit 5) + byte 32=Aux2 + b1: Sensor + b2: Econo mode + b7: Intelligent eye on + byte 33=Aux3 + b1: Mold Proof + byte 34= checksum of the third part +*/ + +// Constants +const uint8_t kDaikinAuto = 0b000; +const uint8_t kDaikinDry = 0b010; +const uint8_t kDaikinCool = 0b011; +const uint8_t kDaikinHeat = 0b100; +const uint8_t kDaikinFan = 0b110; +const uint8_t kDaikinMinTemp = 10; // Celsius +const uint8_t kDaikinMaxTemp = 32; // Celsius +const uint8_t kDaikinFanMin = 1; +const uint8_t kDaikinFanMax = 5; +const uint8_t kDaikinFanAuto = 0b1010; +const uint8_t kDaikinFanQuiet = 0b1011; +const uint16_t kDaikinHeaderLength = 5; +const uint8_t kDaikinSections = 3; +const uint8_t kDaikinSection1Length = 8; +const uint8_t kDaikinSection2Length = 8; +const uint8_t kDaikinSection3Length = + kDaikinStateLength - kDaikinSection1Length - kDaikinSection2Length; +const uint8_t kDaikinByteComfort = 6; +const uint8_t kDaikinByteChecksum1 = 7; +const uint8_t kDaikinBitComfort = 0b00010000; +const uint8_t kDaikinByteClockMinsLow = 13; +const uint8_t kDaikinByteClockMinsHigh = 14; +const uint8_t kDaikinByteChecksum2 = 15; +const uint8_t kDaikinBytePower = 21; +const uint8_t kDaikinBitPower = 0b00000001; +const uint8_t kDaikinByteTemp = 22; +const uint8_t kDaikinByteFan = 24; +const uint8_t kDaikinByteSwingH = 25; +const uint8_t kDaikinByteOnTimerMinsLow = 26; +const uint8_t kDaikinByteOnTimerMinsHigh = 27; +const uint8_t kDaikinByteOffTimerMinsLow = kDaikinByteOnTimerMinsHigh; +const uint8_t kDaikinByteOffTimerMinsHigh = 28; +const uint8_t kDaikinBytePowerful = 29; +const uint8_t kDaikinBitPowerful = 0b00000001; +const uint8_t kDaikinByteSilent = kDaikinBytePowerful; +const uint8_t kDaikinBitSilent = 0b00100000; +const uint8_t kDaikinByteSensor = 32; +const uint8_t kDaikinBitSensor = 0b00000010; +const uint8_t kDaikinByteEcono = kDaikinByteSensor; +const uint8_t kDaikinBitEcono = 0b00000100; +const uint8_t kDaikinByteEye = kDaikinByteSensor; +const uint8_t kDaikinBitEye = 0b10000000; +const uint8_t kDaikinByteMold = 33; +const uint8_t kDaikinBitMold = 0b00000010; +const uint8_t kDaikinByteOffTimer = kDaikinBytePower; +const uint8_t kDaikinBitOffTimer = 0b00000100; +const uint8_t kDaikinByteOnTimer = kDaikinByteOffTimer; +const uint8_t kDaikinBitOnTimer = 0b00000010; +const uint8_t kDaikinByteChecksum3 = kDaikinStateLength - 1; +const uint16_t kDaikinUnusedTime = 0x600; +const uint8_t kDaikinBeepQuiet = 1; +const uint8_t kDaikinBeepLoud = 2; +const uint8_t kDaikinBeepOff = 3; +const uint8_t kDaikinLightBright = 1; +const uint8_t kDaikinLightDim = 2; +const uint8_t kDaikinLightOff = 3; +const uint8_t kDaikinCurBit = kDaikinStateLength; +const uint8_t kDaikinCurIndex = kDaikinStateLength + 1; +const uint8_t kDaikinTolerance = 35; +const uint16_t kDaikinMarkExcess = kMarkExcess; +const uint16_t kDaikinHdrMark = 3650; // kDaikinBitMark * 8 +const uint16_t kDaikinHdrSpace = 1623; // kDaikinBitMark * 4 +const uint16_t kDaikinBitMark = 428; +const uint16_t kDaikinZeroSpace = 428; +const uint16_t kDaikinOneSpace = 1280; +const uint16_t kDaikinGap = 29000; +// Note bits in each octet swapped so can be sent as a single value +const uint64_t kDaikinFirstHeader64 = + 0b1101011100000000000000001100010100000000001001111101101000010001; + +// Another variant of the protocol for the Daikin ARC477A1 remote. +const uint16_t kDaikin2Freq = 36700; // Modulation Frequency in Hz. +const uint16_t kDaikin2LeaderMark = 10024; +const uint16_t kDaikin2LeaderSpace = 25180; +const uint16_t kDaikin2Gap = kDaikin2LeaderMark + kDaikin2LeaderSpace; +const uint16_t kDaikin2HdrMark = 3500; +const uint16_t kDaikin2HdrSpace = 1728; +const uint16_t kDaikin2BitMark = 460; +const uint16_t kDaikin2OneSpace = 1270; +const uint16_t kDaikin2ZeroSpace = 420; +const uint16_t kDaikin2Sections = 2; +const uint16_t kDaikin2Section1Length = 20; +const uint16_t kDaikin2Section2Length = 19; +const uint8_t kDaikin2Tolerance = kTolerance + 5; + +const uint8_t kDaikin2BitSleepTimer = 0b00100000; +const uint8_t kDaikin2BitPurify = 0b00010000; +const uint8_t kDaikin2BitEye = 0b00000010; +const uint8_t kDaikin2BitEyeAuto = 0b10000000; +const uint8_t kDaikin2BitMold = 0b00001000; +const uint8_t kDaikin2BitClean = 0b00100000; +const uint8_t kDaikin2BitFreshAir = 0b00000001; +const uint8_t kDaikin2BitFreshAirHigh = 0b10000000; +const uint8_t kDaikin2BitPower = 0b10000000; +const uint8_t kDaikin2LightMask = 0b00110000; +const uint8_t kDaikin2BeepMask = 0b11000000; +const uint8_t kDaikin2SwingVHigh = 0x1; +const uint8_t kDaikin2SwingVLow = 0x6; +const uint8_t kDaikin2SwingVBreeze = 0xC; +const uint8_t kDaikin2SwingVCirculate = 0xD; +const uint8_t kDaikin2SwingVAuto = 0xE; +const uint8_t kDaikin2SwingHAuto = 0xBE; +const uint8_t kDaikin2SwingHSwing = 0xBF; +const uint8_t kDaikin2MinCoolTemp = 18; // Min temp (in C) when in Cool mode. + +// Another variant of the protocol for the Daikin ARC433B69 remote. +const uint16_t kDaikin216Freq = 38000; // Modulation Frequency in Hz. +const uint16_t kDaikin216HdrMark = 3400; +const uint16_t kDaikin216HdrSpace = 1800; +const uint16_t kDaikin216BitMark = 380; +const uint16_t kDaikin216OneSpace = 1350; +const uint16_t kDaikin216ZeroSpace = 480; +const uint16_t kDaikin216Gap = 29650; +const uint16_t kDaikin216Sections = 2; +const uint16_t kDaikin216Section1Length = 8; +const uint16_t kDaikin216Section2Length = kDaikin216StateLength - + kDaikin216Section1Length; +const uint8_t kDaikin216BytePower = 13; +const uint8_t kDaikin216ByteMode = kDaikin216BytePower; +const uint8_t kDaikin216MaskMode = 0b01110000; +const uint8_t kDaikin216ByteTemp = 14; +const uint8_t kDaikin216MaskTemp = 0b01111110; +const uint8_t kDaikin216ByteFan = 16; +const uint8_t kDaikin216MaskFan = 0b11110000; +const uint8_t kDaikin216ByteSwingV = 16; +const uint8_t kDaikin216MaskSwingV = 0b00001111; +const uint8_t kDaikin216ByteSwingH = 17; +const uint8_t kDaikin216MaskSwingH = kDaikin216MaskSwingV; + + +// Legacy defines. +#define DAIKIN_COOL kDaikinCool +#define DAIKIN_HEAT kDaikinHeat +#define DAIKIN_FAN kDaikinFan +#define DAIKIN_AUTO kDaikinAuto +#define DAIKIN_DRY kDaikinDry +#define DAIKIN_MIN_TEMP kDaikinMinTemp +#define DAIKIN_MAX_TEMP kDaikinMaxTemp +#define DAIKIN_FAN_MIN kDaikinFanMin +#define DAIKIN_FAN_MAX kDaikinFanMax +#define DAIKIN_FAN_AUTO kDaikinFanAuto +#define DAIKIN_FAN_QUIET kDaikinFanQuiet + +class IRDaikinESP { + public: + explicit IRDaikinESP(uint16_t pin); + +#if SEND_DAIKIN + void send(const uint16_t repeat = kDaikinDefaultRepeat); +#endif + void begin(void); + void on(void); + void off(void); + void setPower(const bool on); + bool getPower(void); + void setTemp(const uint8_t temp); + uint8_t getTemp(); + void setFan(const uint8_t fan); + uint8_t getFan(void); + void setMode(const uint8_t mode); + uint8_t getMode(void); + void setSwingVertical(const bool on); + bool getSwingVertical(void); + void setSwingHorizontal(const bool on); + bool getSwingHorizontal(void); + bool getQuiet(void); + void setQuiet(const bool on); + bool getPowerful(void); + void setPowerful(const bool on); + void setSensor(const bool on); + bool getSensor(void); + void setEcono(const bool on); + bool getEcono(void); + void setEye(const bool on); + bool getEye(void); + void setMold(const bool on); + bool getMold(void); + void setComfort(const bool on); + bool getComfort(void); + void enableOnTimer(const uint16_t starttime); + void disableOnTimer(void); + uint16_t getOnTime(void); + bool getOnTimerEnabled(); + void enableOffTimer(const uint16_t endtime); + void disableOffTimer(void); + uint16_t getOffTime(void); + bool getOffTimerEnabled(void); + void setCurrentTime(const uint16_t mins_since_midnight); + uint16_t getCurrentTime(void); + uint8_t* getRaw(void); + void setRaw(const uint8_t new_code[], + const uint16_t length = kDaikinStateLength); + static bool validChecksum(uint8_t state[], + const uint16_t length = kDaikinStateLength); + static uint8_t convertMode(const stdAc::opmode_t mode); + static uint8_t convertFan(const stdAc::fanspeed_t speed); +#ifdef ARDUINO + String toString(void); + static String renderTime(const uint16_t timemins); +#else + std::string toString(void); + static std::string renderTime(const uint16_t timemins); +#endif +#ifndef UNIT_TEST + + private: + IRsend _irsend; +#else + IRsendTest _irsend; +#endif + // # of bytes per command + uint8_t remote[kDaikinStateLength]; + void stateReset(void); + void checksum(void); +}; + +// Class to emulate a Daikin ARC477A1 remote. +class IRDaikin2 { + public: + explicit IRDaikin2(uint16_t pin); + +#if SEND_DAIKIN2 + void send(const uint16_t repeat = kDaikin2DefaultRepeat); +#endif + void begin(); + void on(); + void off(); + void setPower(const bool state); + bool getPower(); + void setTemp(const uint8_t temp); + uint8_t getTemp(); + void setFan(const uint8_t fan); + uint8_t getFan(); + uint8_t getMode(); + void setMode(const uint8_t mode); + void setSwingVertical(const uint8_t position); + uint8_t getSwingVertical(); + void setSwingHorizontal(const uint8_t position); + uint8_t getSwingHorizontal(); + bool getQuiet(); + void setQuiet(const bool on); + bool getPowerful(); + void setPowerful(const bool on); + void setSensor(const bool on); + bool getSensor(); + void setEcono(const bool on); + bool getEcono(); + void setEye(const bool on); + bool getEye(); + void setEyeAuto(const bool on); + bool getEyeAuto(); + void setPurify(const bool on); + bool getPurify(); + void setMold(const bool on); + bool getMold(); + void enableOnTimer(const uint16_t starttime); + void disableOnTimer(); + uint16_t getOnTime(); + bool getOnTimerEnabled(); + void enableSleepTimer(const uint16_t sleeptime); + void disableSleepTimer(); + uint16_t getSleepTime(); + bool getSleepTimerEnabled(); + void enableOffTimer(const uint16_t endtime); + void disableOffTimer(); + uint16_t getOffTime(); + bool getOffTimerEnabled(); + void setCurrentTime(const uint16_t time); + uint16_t getCurrentTime(); + void setBeep(const uint8_t beep); + uint8_t getBeep(); + void setLight(const uint8_t light); + uint8_t getLight(); + void setClean(const bool on); + bool getClean(); + void setFreshAir(const bool on); + bool getFreshAir(); + void setFreshAirHigh(const bool on); + bool getFreshAirHigh(); + uint8_t* getRaw(); + void setRaw(const uint8_t new_code[]); + uint32_t getCommand(); + void setCommand(uint32_t value); + static bool validChecksum(uint8_t state[], + const uint16_t length = kDaikin2StateLength); + static uint8_t convertMode(const stdAc::opmode_t mode); + static uint8_t convertFan(const stdAc::fanspeed_t speed); + uint8_t convertSwingV(const stdAc::swingv_t position); +#ifdef ARDUINO + String toString(); + static String renderTime(uint16_t timemins); +#else + std::string toString(); + static std::string renderTime(uint16_t timemins); +#endif +#ifndef UNIT_TEST + + private: + IRsend _irsend; +#else + IRsendTest _irsend; +#endif + // # of bytes per command + uint8_t remote_state[kDaikin2StateLength]; + void stateReset(); + void checksum(); + void clearOnTimerFlag(); + void clearSleepTimerFlag(); +}; + +// Class to emulate a Daikin ARC433B69 remote. +class IRDaikin216 { + public: + explicit IRDaikin216(uint16_t pin); + +#if SEND_DAIKIN216 + void send(const uint16_t repeat = kDaikin216DefaultRepeat); +#endif + void begin(); + uint8_t* getRaw(); + void setRaw(const uint8_t new_code[]); + static bool validChecksum(uint8_t state[], + const uint16_t length = kDaikin216StateLength); + void on(void); + void off(void); + void setPower(const bool on); + bool getPower(void); + void setTemp(const uint8_t temp); + uint8_t getTemp(); + void setMode(const uint8_t mode); + uint8_t getMode(void); + static uint8_t convertMode(const stdAc::opmode_t mode); + void setFan(const uint8_t fan); + uint8_t getFan(void); + static uint8_t convertFan(const stdAc::fanspeed_t speed); + void setSwingVertical(const bool on); + bool getSwingVertical(void); + void setSwingHorizontal(const bool on); + bool getSwingHorizontal(void); + void setQuiet(const bool on); + bool getQuiet(void); +#ifdef ARDUINO + String toString(void); + static String renderTime(const uint16_t timemins); +#else + std::string toString(void); + static std::string renderTime(const uint16_t timemins); +#endif +#ifndef UNIT_TEST + + private: + IRsend _irsend; +#else + IRsendTest _irsend; +#endif + // # of bytes per command + uint8_t remote_state[kDaikin216StateLength]; + void stateReset(); + void checksum(); +}; + +#endif // IR_DAIKIN_H_ diff --git a/lib/IRremoteESP8266-2.5.2.03/src/ir_Denon.cpp b/lib/IRremoteESP8266-2.6.0/src/ir_Denon.cpp similarity index 100% rename from lib/IRremoteESP8266-2.5.2.03/src/ir_Denon.cpp rename to lib/IRremoteESP8266-2.6.0/src/ir_Denon.cpp diff --git a/lib/IRremoteESP8266-2.5.2.03/src/ir_Dish.cpp b/lib/IRremoteESP8266-2.6.0/src/ir_Dish.cpp similarity index 100% rename from lib/IRremoteESP8266-2.5.2.03/src/ir_Dish.cpp rename to lib/IRremoteESP8266-2.6.0/src/ir_Dish.cpp diff --git a/lib/IRremoteESP8266-2.5.2.03/src/ir_Electra.cpp b/lib/IRremoteESP8266-2.6.0/src/ir_Electra.cpp similarity index 87% rename from lib/IRremoteESP8266-2.5.2.03/src/ir_Electra.cpp rename to lib/IRremoteESP8266-2.6.0/src/ir_Electra.cpp index df69be748dab..0700ab69856a 100644 --- a/lib/IRremoteESP8266-2.5.2.03/src/ir_Electra.cpp +++ b/lib/IRremoteESP8266-2.6.0/src/ir_Electra.cpp @@ -1,4 +1,4 @@ -// Copyright 2018 David Conran +// Copyright 2018, 2019 David Conran #include "IRrecv.h" #include "IRsend.h" @@ -17,6 +17,7 @@ // Ref: // https://github.com/markszabo/IRremoteESP8266/issues/527 +// https://github.com/markszabo/IRremoteESP8266/issues/642 // Constants const uint16_t kElectraAcHdrMark = 9166; @@ -24,7 +25,7 @@ const uint16_t kElectraAcBitMark = 646; const uint16_t kElectraAcHdrSpace = 4470; const uint16_t kElectraAcOneSpace = 1647; const uint16_t kElectraAcZeroSpace = 547; -const uint32_t kElectraAcMessageGap = 100000; // Completely made-up guess. +const uint32_t kElectraAcMessageGap = kDefaultMessageGap; // Just a guess. #if SEND_ELECTRA_AC // Send a Electra message @@ -42,7 +43,8 @@ void IRsend::sendElectraAC(uint8_t data[], uint16_t nbytes, uint16_t repeat) { kElectraAcOneSpace, kElectraAcBitMark, kElectraAcZeroSpace, kElectraAcBitMark, kElectraAcMessageGap, data, nbytes, 38000, // Complete guess of the modulation frequency. - true, 0, 50); + false, // Send data in LSB order per byte + 0, 50); } #endif @@ -56,7 +58,7 @@ void IRsend::sendElectraAC(uint8_t data[], uint16_t nbytes, uint16_t repeat) { // Returns: // boolean: True if it can decode it, false if it can't. // -// Status: Alpha / Needs testing against a real device. +// Status: Beta / Probably works. // bool IRrecv::decodeElectraAC(decode_results *results, uint16_t nbits, bool strict) { @@ -68,8 +70,6 @@ bool IRrecv::decodeElectraAC(decode_results *results, uint16_t nbits, return false; // Not strictly a ELECTRA_AC message. } - // The protocol sends the data normal + inverted, alternating on - // each byte. Hence twice the number of expected data bits. if (results->rawlen < 2 * nbits + kHeader + kFooter - 1) return false; // Can't possibly be a valid ELECTRA_AC message. @@ -87,7 +87,7 @@ bool IRrecv::decodeElectraAC(decode_results *results, uint16_t nbits, i++, dataBitsSoFar += 8, offset += data_result.used) { data_result = matchData(&(results->rawbuf[offset]), 8, kElectraAcBitMark, kElectraAcOneSpace, kElectraAcBitMark, - kElectraAcZeroSpace, kTolerance, 0, true); + kElectraAcZeroSpace, kTolerance, 0, false); if (data_result.success == false) return false; // Fail results->state[i] = data_result.data; } @@ -99,7 +99,12 @@ bool IRrecv::decodeElectraAC(decode_results *results, uint16_t nbits, return false; // Compliance - if (strict && dataBitsSoFar != nbits) return false; + if (strict) { + if (dataBitsSoFar != nbits) return false; + // Verify the checksum. + if (sumBytes(results->state, (dataBitsSoFar / 8) - 1) != + results->state[(dataBitsSoFar / 8) - 1]) return false; + } // Success results->decode_type = ELECTRA_AC; diff --git a/lib/IRremoteESP8266-2.5.2.03/src/ir_Fujitsu.cpp b/lib/IRremoteESP8266-2.6.0/src/ir_Fujitsu.cpp similarity index 88% rename from lib/IRremoteESP8266-2.5.2.03/src/ir_Fujitsu.cpp rename to lib/IRremoteESP8266-2.6.0/src/ir_Fujitsu.cpp index 7c1b998348b4..de1b97e87f69 100644 --- a/lib/IRremoteESP8266-2.5.2.03/src/ir_Fujitsu.cpp +++ b/lib/IRremoteESP8266-2.6.0/src/ir_Fujitsu.cpp @@ -84,9 +84,9 @@ void IRFujitsuAC::begin() { _irsend.begin(); } #if SEND_FUJITSU_AC // Send the current desired state to the IR LED. -void IRFujitsuAC::send() { +void IRFujitsuAC::send(const uint16_t repeat) { getRaw(); - _irsend.sendFujitsuAC(remote_state, getStateLength()); + _irsend.sendFujitsuAC(remote_state, getStateLength(), repeat); } #endif // SEND_FUJITSU_AC @@ -281,6 +281,7 @@ void IRFujitsuAC::setMode(uint8_t mode) { } uint8_t IRFujitsuAC::getMode() { return _mode; } + // Set the requested swing operation mode of the a/c unit. void IRFujitsuAC::setSwing(uint8_t swingMode) { switch (_model) { @@ -319,6 +320,39 @@ bool IRFujitsuAC::validChecksum(uint8_t state[], uint16_t length) { return checksum == (uint8_t)(sum_complement - sum); // Does it match? } +// Convert a standard A/C mode into its native mode. +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 standard A/C Fan speed into its native fan speed. +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 the internal state into a human readable string. #ifdef ARDUINO String IRFujitsuAC::toString() { @@ -327,77 +361,80 @@ String IRFujitsuAC::toString() { std::string IRFujitsuAC::toString() { std::string result = ""; #endif // ARDUINO - result += "Power: "; + result += F("Power: "); if (getPower()) - result += "On"; + result += F("On"); else - result += "Off"; - result += ", Mode: " + uint64ToString(getMode()); + result += F("Off"); + result += F(", Mode: "); + result += uint64ToString(getMode()); switch (getMode()) { case kFujitsuAcModeAuto: - result += " (AUTO)"; + result += F(" (AUTO)"); break; case kFujitsuAcModeCool: - result += " (COOL)"; + result += F(" (COOL)"); break; case kFujitsuAcModeHeat: - result += " (HEAT)"; + result += F(" (HEAT)"); break; case kFujitsuAcModeDry: - result += " (DRY)"; + result += F(" (DRY)"); break; case kFujitsuAcModeFan: - result += " (FAN)"; + result += F(" (FAN)"); break; default: - result += " (UNKNOWN)"; + result += F(" (UNKNOWN)"); } - result += ", Temp: " + uint64ToString(getTemp()) + "C"; - result += ", Fan: " + uint64ToString(getFanSpeed()); + result += F(", Temp: "); + result += uint64ToString(getTemp()); + result += F("C, Fan: "); + result += uint64ToString(getFanSpeed()); switch (getFanSpeed()) { case kFujitsuAcFanAuto: - result += " (AUTO)"; + result += F(" (AUTO)"); break; case kFujitsuAcFanHigh: - result += " (HIGH)"; + result += F(" (HIGH)"); break; case kFujitsuAcFanMed: - result += " (MED)"; + result += F(" (MED)"); break; case kFujitsuAcFanLow: - result += " (LOW)"; + result += F(" (LOW)"); break; case kFujitsuAcFanQuiet: - result += " (QUIET)"; + result += F(" (QUIET)"); break; } - result += ", Swing: "; + result += F(", Swing: "); switch (getSwing()) { case kFujitsuAcSwingOff: - result += "Off"; + result += F("Off"); break; case kFujitsuAcSwingVert: - result += "Vert"; + result += F("Vert"); break; case kFujitsuAcSwingHoriz: - result += "Horiz"; + result += F("Horiz"); break; case kFujitsuAcSwingBoth: - result += "Vert + Horiz"; + result += F("Vert + Horiz"); break; default: - result += "UNKNOWN"; + result += F("UNKNOWN"); } - result += ", Command: "; + result += F(", Command: "); switch (getCmd()) { case kFujitsuAcCmdStepHoriz: - result += "Step vane horizontally"; + result += F("Step vane horizontally"); break; case kFujitsuAcCmdStepVert: - result += "Step vane vertically"; + result += F("Step vane vertically"); break; default: - result += "N/A"; + result += F("N/A"); } return result; } diff --git a/lib/IRremoteESP8266-2.5.2.03/src/ir_Fujitsu.h b/lib/IRremoteESP8266-2.6.0/src/ir_Fujitsu.h similarity index 93% rename from lib/IRremoteESP8266-2.5.2.03/src/ir_Fujitsu.h rename to lib/IRremoteESP8266-2.6.0/src/ir_Fujitsu.h index bba634be622b..78a4f8951710 100644 --- a/lib/IRremoteESP8266-2.5.2.03/src/ir_Fujitsu.h +++ b/lib/IRremoteESP8266-2.6.0/src/ir_Fujitsu.h @@ -13,6 +13,9 @@ #include "IRrecv.h" #include "IRremoteESP8266.h" #include "IRsend.h" +#ifdef UNIT_TEST +#include "IRsend_test.h" +#endif // FUJITSU A/C support added by Jonny Graham @@ -78,7 +81,7 @@ class IRFujitsuAC { void setModel(fujitsu_ac_remote_model_t model); void stateReset(); #if SEND_FUJITSU_AC - void send(); + void send(const uint16_t repeat = kFujitsuAcMinRepeat); #endif // SEND_FUJITSU_AC void begin(); void off(); @@ -99,15 +102,21 @@ class IRFujitsuAC { uint8_t getStateLength(); static bool validChecksum(uint8_t* state, uint16_t length); bool getPower(); + uint8_t convertMode(const stdAc::opmode_t mode); + uint8_t convertFan(stdAc::fanspeed_t speed); #ifdef ARDUINO String toString(); #else std::string toString(); #endif +#ifndef UNIT_TEST private: - uint8_t remote_state[kFujitsuAcStateLength]; IRsend _irsend; +#else + IRsendTest _irsend; +#endif + uint8_t remote_state[kFujitsuAcStateLength]; uint8_t _temp; uint8_t _fanSpeed; uint8_t _mode; diff --git a/lib/IRremoteESP8266-2.5.2.03/src/ir_GICable.cpp b/lib/IRremoteESP8266-2.6.0/src/ir_GICable.cpp similarity index 100% rename from lib/IRremoteESP8266-2.5.2.03/src/ir_GICable.cpp rename to lib/IRremoteESP8266-2.6.0/src/ir_GICable.cpp diff --git a/lib/IRremoteESP8266-2.5.2.03/src/ir_GlobalCache.cpp b/lib/IRremoteESP8266-2.6.0/src/ir_GlobalCache.cpp similarity index 100% rename from lib/IRremoteESP8266-2.5.2.03/src/ir_GlobalCache.cpp rename to lib/IRremoteESP8266-2.6.0/src/ir_GlobalCache.cpp diff --git a/lib/IRremoteESP8266-2.5.2.03/src/ir_Gree.cpp b/lib/IRremoteESP8266-2.6.0/src/ir_Gree.cpp similarity index 82% rename from lib/IRremoteESP8266-2.5.2.03/src/ir_Gree.cpp rename to lib/IRremoteESP8266-2.6.0/src/ir_Gree.cpp index df8afada6fa9..756f008d42b2 100644 --- a/lib/IRremoteESP8266-2.5.2.03/src/ir_Gree.cpp +++ b/lib/IRremoteESP8266-2.6.0/src/ir_Gree.cpp @@ -27,7 +27,7 @@ // Constants // Ref: https://github.com/ToniA/arduino-heatpumpir/blob/master/GreeHeatpumpIR.h const uint16_t kGreeHdrMark = 9000; -const uint16_t kGreeHdrSpace = 4000; +const uint16_t kGreeHdrSpace = 4500; // See #684 and real example in unit tests const uint16_t kGreeBitMark = 620; const uint16_t kGreeOneSpace = 1600; const uint16_t kGreeZeroSpace = 540; @@ -59,7 +59,7 @@ void IRsend::sendGree(unsigned char data[], uint16_t nbytes, uint16_t repeat) { // Footer #1 sendGeneric(0, 0, // No Header kGreeBitMark, kGreeOneSpace, kGreeBitMark, kGreeZeroSpace, - kGreeBitMark, kGreeMsgSpace, 0b010, 3, 38, true, 0, false); + kGreeBitMark, kGreeMsgSpace, 0b010, 3, 38, false, 0, 50); // Block #2 sendGeneric(0, 0, // No Header for Block #2 @@ -129,9 +129,9 @@ void IRGreeAC::fixup() { void IRGreeAC::begin() { _irsend.begin(); } #if SEND_GREE -void IRGreeAC::send() { +void IRGreeAC::send(const uint16_t repeat) { fixup(); // Ensure correct settings before sending. - _irsend.sendGree(remote_state); + _irsend.sendGree(remote_state, kGreeStateLength, repeat); } #endif // SEND_GREE @@ -305,6 +305,57 @@ uint8_t IRGreeAC::getSwingVerticalPosition() { return remote_state[4] & kGreeSwingPosMask; } + +// Convert a standard A/C mode into its native mode. +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 standard A/C Fan speed into its native fan speed. +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 standard A/C Vertical Swing into its native version. +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 the internal state into a human readable string. #ifdef ARDUINO String IRGreeAC::toString() { @@ -313,74 +364,77 @@ String IRGreeAC::toString() { std::string IRGreeAC::toString() { std::string result = ""; #endif // ARDUINO - result += "Power: "; + result += F("Power: "); if (getPower()) - result += "On"; + result += F("On"); else - result += "Off"; - result += ", Mode: " + uint64ToString(getMode()); + result += F("Off"); + result += F(", Mode: "); + result += uint64ToString(getMode()); switch (getMode()) { case kGreeAuto: - result += " (AUTO)"; + result += F(" (AUTO)"); break; case kGreeCool: - result += " (COOL)"; + result += F(" (COOL)"); break; case kGreeHeat: - result += " (HEAT)"; + result += F(" (HEAT)"); break; case kGreeDry: - result += " (DRY)"; + result += F(" (DRY)"); break; case kGreeFan: - result += " (FAN)"; + result += F(" (FAN)"); break; default: - result += " (UNKNOWN)"; + result += F(" (UNKNOWN)"); } - result += ", Temp: " + uint64ToString(getTemp()) + "C"; - result += ", Fan: " + uint64ToString(getFan()); + result += F(", Temp: "); + result += uint64ToString(getTemp()); + result += F("C, Fan: "); + result += uint64ToString(getFan()); switch (getFan()) { case 0: - result += " (AUTO)"; + result += F(" (AUTO)"); break; case kGreeFanMax: - result += " (MAX)"; + result += F(" (MAX)"); break; } - result += ", Turbo: "; + result += F(", Turbo: "); if (getTurbo()) - result += "On"; + result += F("On"); else - result += "Off"; - result += ", XFan: "; + result += F("Off"); + result += F(", XFan: "); if (getXFan()) - result += "On"; + result += F("On"); else - result += "Off"; - result += ", Light: "; + result += F("Off"); + result += F(", Light: "); if (getLight()) - result += "On"; + result += F("On"); else - result += "Off"; - result += ", Sleep: "; + result += F("Off"); + result += F(", Sleep: "); if (getSleep()) - result += "On"; + result += F("On"); else - result += "Off"; - result += ", Swing Vertical Mode: "; + result += F("Off"); + result += F(", Swing Vertical Mode: "); if (getSwingVerticalAuto()) - result += "Auto"; + result += F("Auto"); else - result += "Manual"; - result += - ", Swing Vertical Pos: " + uint64ToString(getSwingVerticalPosition()); + result += F("Manual"); + result += F(", Swing Vertical Pos: "); + result += uint64ToString(getSwingVerticalPosition()); switch (getSwingVerticalPosition()) { case kGreeSwingLastPos: - result += " (Last Pos)"; + result += F(" (Last Pos)"); break; case kGreeSwingAuto: - result += " (Auto)"; + result += F(" (Auto)"); break; } return result; diff --git a/lib/IRremoteESP8266-2.5.2.03/src/ir_Gree.h b/lib/IRremoteESP8266-2.6.0/src/ir_Gree.h similarity index 89% rename from lib/IRremoteESP8266-2.5.2.03/src/ir_Gree.h rename to lib/IRremoteESP8266-2.6.0/src/ir_Gree.h index 73f69eb31b47..c3c5916dc4f2 100644 --- a/lib/IRremoteESP8266-2.5.2.03/src/ir_Gree.h +++ b/lib/IRremoteESP8266-2.6.0/src/ir_Gree.h @@ -14,6 +14,9 @@ #endif #include "IRremoteESP8266.h" #include "IRsend.h" +#ifdef UNIT_TEST +#include "IRsend_test.h" +#endif // GGGG RRRRRR EEEEEEE EEEEEEE // GG GG RR RR EE EE @@ -44,6 +47,8 @@ const uint8_t kGreeSwingPosMask = 0b00001111; const uint8_t kGreeMinTemp = 16; // Celsius const uint8_t kGreeMaxTemp = 30; // Celsius +const uint8_t kGreeFanAuto = 0; +const uint8_t kGreeFanMin = 1; const uint8_t kGreeFanMax = 3; const uint8_t kGreeSwingLastPos = 0b00000000; @@ -84,7 +89,7 @@ class IRGreeAC { void stateReset(); #if SEND_GREE - void send(); + void send(const uint16_t repeat = kGreeDefaultRepeat); #endif // SEND_GREE void begin(); void on(); @@ -108,7 +113,9 @@ class IRGreeAC { void setSwingVertical(const bool automatic, const uint8_t position); bool getSwingVerticalAuto(); uint8_t getSwingVerticalPosition(); - + uint8_t convertMode(const stdAc::opmode_t mode); + uint8_t convertFan(const stdAc::fanspeed_t speed); + uint8_t convertSwingV(const stdAc::swingv_t swingv); uint8_t* getRaw(); void setRaw(uint8_t new_code[]); static bool validChecksum(const uint8_t state[], @@ -118,13 +125,17 @@ class IRGreeAC { #else std::string toString(); #endif +#ifndef UNIT_TEST private: + IRsend _irsend; +#else // UNIT_TEST + IRsendTest _irsend; +#endif // UNIT_TEST // The state of the IR remote in IR code form. uint8_t remote_state[kGreeStateLength]; void checksum(const uint16_t length = kGreeStateLength); void fixup(); - IRsend _irsend; }; #endif // IR_GREE_H_ diff --git a/lib/IRremoteESP8266-2.5.2.03/src/ir_Haier.cpp b/lib/IRremoteESP8266-2.6.0/src/ir_Haier.cpp similarity index 75% rename from lib/IRremoteESP8266-2.5.2.03/src/ir_Haier.cpp rename to lib/IRremoteESP8266-2.6.0/src/ir_Haier.cpp index 2c47e4eac2a5..f76bb3447a58 100644 --- a/lib/IRremoteESP8266-2.5.2.03/src/ir_Haier.cpp +++ b/lib/IRremoteESP8266-2.6.0/src/ir_Haier.cpp @@ -45,7 +45,7 @@ const uint32_t kHaierAcMinGap = 150000; // Completely made up value. // nbytes: Nr. of bytes of data in the array. (>=kHaierACStateLength) // repeat: Nr. of times the message is to be repeated. (Default = 0). // -// Status: Beta / Probably working. +// Status: STABLE / Known to be working. // void IRsend::sendHaierAC(unsigned char data[], uint16_t nbytes, uint16_t repeat) { @@ -86,9 +86,9 @@ IRHaierAC::IRHaierAC(uint16_t pin) : _irsend(pin) { stateReset(); } void IRHaierAC::begin() { _irsend.begin(); } #if SEND_HAIER_AC -void IRHaierAC::send() { +void IRHaierAC::send(const uint16_t repeat) { checksum(); - _irsend.sendHaierAC(remote_state); + _irsend.sendHaierAC(remote_state, kHaierACStateLength, repeat); } #endif // SEND_HAIER_AC @@ -104,7 +104,10 @@ bool IRHaierAC::validChecksum(uint8_t state[], const uint16_t length) { void IRHaierAC::stateReset() { for (uint8_t i = 1; i < kHaierACStateLength; i++) remote_state[i] = 0x0; remote_state[0] = kHaierAcPrefix; - remote_state[2] = 0b00100000; + remote_state[2] = 0x20; + remote_state[4] = 0x0C; + remote_state[5] = 0xC0; + remote_state[6] = 0x20; setTemp(kHaierAcDefTemp); setFan(kHaierAcFanAuto); @@ -304,14 +307,63 @@ std::string IRHaierAC::timeToString(const uint16_t nr_mins) { std::string result = ""; #endif // ARDUINO - if (nr_mins / 24 < 10) result += "0"; // Zero pad. + if (nr_mins / 24 < 10) result += '0'; // Zero pad. result += uint64ToString(nr_mins / 60); - result += ":"; - if (nr_mins % 60 < 10) result += "0"; // Zero pad. + result += ':'; + if (nr_mins % 60 < 10) result += '0'; // Zero pad. result += uint64ToString(nr_mins % 60); return result; } +// Convert a standard A/C mode into its native mode. +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 standard A/C Fan speed into its native fan speed. +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 standard A/C vertical swing into its native setting. +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 the internal state into a human readable string. #ifdef ARDUINO String IRHaierAC::toString() { @@ -321,114 +373,122 @@ std::string IRHaierAC::toString() { std::string result = ""; #endif // ARDUINO uint8_t cmd = getCommand(); - result += "Command: " + uint64ToString(cmd) + " ("; + result += F("Command: "); + result += uint64ToString(cmd); + result += F(" ("); switch (cmd) { case kHaierAcCmdOff: - result += "Off"; + result += F("Off"); break; case kHaierAcCmdOn: - result += "On"; + result += F("On"); break; case kHaierAcCmdMode: - result += "Mode"; + result += F("Mode"); break; case kHaierAcCmdFan: - result += "Fan"; + result += F("Fan"); break; case kHaierAcCmdTempUp: - result += "Temp Up"; + result += F("Temp Up"); break; case kHaierAcCmdTempDown: - result += "Temp Down"; + result += F("Temp Down"); break; case kHaierAcCmdSleep: - result += "Sleep"; + result += F("Sleep"); break; case kHaierAcCmdTimerSet: - result += "Timer Set"; + result += F("Timer Set"); break; case kHaierAcCmdTimerCancel: - result += "Timer Cancel"; + result += F("Timer Cancel"); break; case kHaierAcCmdHealth: - result += "Health"; + result += F("Health"); break; case kHaierAcCmdSwing: - result += "Swing"; + result += F("Swing"); break; default: - result += "Unknown"; + result += F("Unknown"); } - result += ")"; - result += ", Mode: " + uint64ToString(getMode()); + result += ')'; + result += F(", Mode: "); + result += uint64ToString(getMode()); switch (getMode()) { case kHaierAcAuto: - result += " (AUTO)"; + result += F(" (AUTO)"); break; case kHaierAcCool: - result += " (COOL)"; + result += F(" (COOL)"); break; case kHaierAcHeat: - result += " (HEAT)"; + result += F(" (HEAT)"); break; case kHaierAcDry: - result += " (DRY)"; + result += F(" (DRY)"); break; case kHaierAcFan: - result += " (FAN)"; + result += F(" (FAN)"); break; default: - result += " (UNKNOWN)"; + result += F(" (UNKNOWN)"); } - result += ", Temp: " + uint64ToString(getTemp()) + "C"; - result += ", Fan: " + uint64ToString(getFan()); + result += F(", Temp: "); + result += uint64ToString(getTemp()); + result += F("C, Fan: "); + result += uint64ToString(getFan()); switch (getFan()) { case kHaierAcFanAuto: - result += " (AUTO)"; + result += F(" (AUTO)"); break; case kHaierAcFanHigh: - result += " (MAX)"; + result += F(" (MAX)"); break; } - result += ", Swing: " + uint64ToString(getSwing()) + " ("; + result += F(", Swing: "); + result += uint64ToString(getSwing()); + result += F(" ("); switch (getSwing()) { case kHaierAcSwingOff: - result += "Off"; + result += F("Off"); break; case kHaierAcSwingUp: - result += "Up"; + result += F("Up"); break; case kHaierAcSwingDown: - result += "Down"; + result += F("Down"); break; case kHaierAcSwingChg: - result += "Chg"; + result += F("Chg"); break; default: - result += "Unknown"; + result += F("Unknown"); } - result += ")"; - result += ", Sleep: "; + result += ')'; + result += F(", Sleep: "); if (getSleep()) - result += "On"; + result += F("On"); else - result += "Off"; - result += ", Health: "; + result += F("Off"); + result += F(", Health: "); if (getHealth()) - result += "On"; + result += F("On"); else - result += "Off"; - result += ", Current Time: " + timeToString(getCurrTime()); - result += ", On Timer: "; + result += F("Off"); + result += F(", Current Time: "); + result += timeToString(getCurrTime()); + result += F(", On Timer: "); if (getOnTimer() >= 0) result += timeToString(getOnTimer()); else - result += "Off"; - result += ", Off Timer: "; + result += F("Off"); + result += F(", Off Timer: "); if (getOffTimer() >= 0) result += timeToString(getOffTimer()); else - result += "Off"; + result += F("Off"); return result; } @@ -440,9 +500,9 @@ IRHaierACYRW02::IRHaierACYRW02(uint16_t pin) : _irsend(pin) { stateReset(); } void IRHaierACYRW02::begin() { _irsend.begin(); } #if SEND_HAIER_AC_YRW02 -void IRHaierACYRW02::send() { +void IRHaierACYRW02::send(const uint16_t repeat) { checksum(); - _irsend.sendHaierACYRW02(remote_state); + _irsend.sendHaierACYRW02(remote_state, kHaierACYRW02StateLength, repeat); } #endif // SEND_HAIER_AC_YRW02 @@ -628,6 +688,57 @@ void IRHaierACYRW02::setSwing(uint8_t state) { remote_state[1] |= newstate; } +// Convert a standard A/C mode into its native mode. +uint8_t IRHaierACYRW02::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 standard A/C Fan speed into its native fan speed. +uint8_t IRHaierACYRW02::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 standard A/C vertical swing into its native setting. +uint8_t IRHaierACYRW02::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 the internal state into a human readable string. #ifdef ARDUINO String IRHaierACYRW02::toString() { @@ -636,132 +747,141 @@ String IRHaierACYRW02::toString() { std::string IRHaierACYRW02::toString() { std::string result = ""; #endif // ARDUINO - result += "Power: "; + result += F("Power: "); if (getPower()) - result += "On"; + result += F("On"); else - result += "Off"; + result += F("Off"); uint8_t cmd = getButton(); - result += ", Button: " + uint64ToString(cmd) + " ("; + result += F(", Button: "); + result += uint64ToString(cmd); + result += F(" ("); switch (cmd) { case kHaierAcYrw02ButtonPower: - result += "Power"; + result += F("Power"); break; case kHaierAcYrw02ButtonMode: - result += "Mode"; + result += F("Mode"); break; case kHaierAcYrw02ButtonFan: - result += "Fan"; + result += F("Fan"); break; case kHaierAcYrw02ButtonTempUp: - result += "Temp Up"; + result += F("Temp Up"); break; case kHaierAcYrw02ButtonTempDown: - result += "Temp Down"; + result += F("Temp Down"); break; case kHaierAcYrw02ButtonSleep: - result += "Sleep"; + result += F("Sleep"); break; case kHaierAcYrw02ButtonHealth: result += "Health"; break; case kHaierAcYrw02ButtonSwing: - result += "Swing"; + result += F("Swing"); break; case kHaierAcYrw02ButtonTurbo: - result += "Turbo"; + result += F("Turbo"); break; default: - result += "Unknown"; + result += F("Unknown"); } - result += ")"; - result += ", Mode: " + uint64ToString(getMode()); + result += ')'; + result += F(", Mode: "); + result += uint64ToString(getMode()); switch (getMode()) { case kHaierAcYrw02Auto: - result += " (Auto)"; + result += F(" (Auto)"); break; case kHaierAcYrw02Cool: - result += " (Cool)"; + result += F(" (Cool)"); break; case kHaierAcYrw02Heat: - result += " (Heat)"; + result += F(" (Heat)"); break; case kHaierAcYrw02Dry: - result += " (Dry)"; + result += F(" (Dry)"); break; case kHaierAcYrw02Fan: - result += " (Fan)"; + result += F(" (Fan)"); break; default: - result += " (UNKNOWN)"; + result += F(" (UNKNOWN)"); } - result += ", Temp: " + uint64ToString(getTemp()) + "C"; - result += ", Fan: " + uint64ToString(getFan()); + result += F(", Temp: "); + result += uint64ToString(getTemp()); + result += F("C, Fan: "); + result += uint64ToString(getFan()); switch (getFan()) { case kHaierAcYrw02FanAuto: - result += " (Auto)"; + result += F(" (Auto)"); break; case kHaierAcYrw02FanHigh: - result += " (High)"; + result += F(" (High)"); break; case kHaierAcYrw02FanLow: - result += " (Low)"; + result += F(" (Low)"); break; case kHaierAcYrw02FanMed: - result += " (Med)"; + result += F(" (Med)"); break; default: - result += " (Unknown)"; + result += F(" (Unknown)"); } - result += ", Turbo: " + uint64ToString(getTurbo()) + " ("; + result += F(", Turbo: "); + result += uint64ToString(getTurbo()); + result += F(" ("); switch (getTurbo()) { case kHaierAcYrw02TurboOff: - result += "Off"; + result += F("Off"); break; case kHaierAcYrw02TurboLow: - result += "Low"; + result += F("Low"); break; case kHaierAcYrw02TurboHigh: - result += "High"; + result += F("High"); break; default: - result += "Unknown"; + result += F("Unknown"); } - result += ")"; - result += ", Swing: " + uint64ToString(getSwing()) + " ("; + result += ')'; + result += F(", Swing: "); + result += uint64ToString(getSwing()); + result += F(" ("); switch (getSwing()) { case kHaierAcYrw02SwingOff: - result += "Off"; + result += F("Off"); break; case kHaierAcYrw02SwingAuto: - result += "Auto"; + result += F("Auto"); break; case kHaierAcYrw02SwingBottom: - result += "Bottom"; + result += F("Bottom"); break; case kHaierAcYrw02SwingDown: - result += "Down"; + result += F("Down"); break; case kHaierAcYrw02SwingTop: - result += "Top"; + result += F("Top"); break; case kHaierAcYrw02SwingMiddle: - result += "Middle"; + result += F("Middle"); break; default: - result += "Unknown"; + result += F("Unknown"); } - result += ")"; - result += ", Sleep: "; + result += ')'; + result += F(", Sleep: "); if (getSleep()) - result += "On"; + result += F("On"); else - result += "Off"; - result += ", Health: "; + result += F("Off"); + result += F(", Health: "); if (getHealth()) - result += "On"; + result += F("On"); else - result += "Off"; + result += F("Off"); return result; } @@ -777,7 +897,7 @@ std::string IRHaierACYRW02::toString() { // Returns: // boolean: True if it can decode it, false if it can't. // -// Status: BETA / Appears to be working. +// Status: STABLE / Known to be working. // bool IRrecv::decodeHaierAC(decode_results* results, uint16_t nbits, bool strict) { diff --git a/lib/IRremoteESP8266-2.5.2.03/src/ir_Haier.h b/lib/IRremoteESP8266-2.6.0/src/ir_Haier.h similarity index 93% rename from lib/IRremoteESP8266-2.5.2.03/src/ir_Haier.h rename to lib/IRremoteESP8266-2.6.0/src/ir_Haier.h index fdc15a3a8ebe..8f7b351965c6 100644 --- a/lib/IRremoteESP8266-2.5.2.03/src/ir_Haier.h +++ b/lib/IRremoteESP8266-2.6.0/src/ir_Haier.h @@ -11,6 +11,9 @@ #endif #include "IRremoteESP8266.h" #include "IRsend.h" +#ifdef UNIT_TEST +#include "IRsend_test.h" +#endif // HH HH AAA IIIII EEEEEEE RRRRRR // HH HH AAAAA III EE RR RR @@ -186,7 +189,7 @@ class IRHaierAC { explicit IRHaierAC(uint16_t pin); #if SEND_HAIER_AC - void send(); + void send(const uint16_t repeat = kHaierAcDefaultRepeat); #endif // SEND_HAIER_AC void begin(); @@ -223,6 +226,10 @@ class IRHaierAC { void setRaw(uint8_t new_code[]); static bool validChecksum(uint8_t state[], const uint16_t length = kHaierACStateLength); + uint8_t convertMode(const stdAc::opmode_t mode); + uint8_t convertFan(const stdAc::fanspeed_t speed); + uint8_t convertSwingV(const stdAc::swingv_t position); + #ifdef ARDUINO String toString(); static String timeToString(const uint16_t nr_mins); @@ -230,14 +237,18 @@ class IRHaierAC { std::string toString(); static std::string timeToString(const uint16_t nr_mins); #endif +#ifndef UNIT_TEST private: + IRsend _irsend; +#else + IRsendTest _irsend; +#endif uint8_t remote_state[kHaierACStateLength]; void stateReset(); void checksum(); static uint16_t getTime(const uint8_t ptr[]); static void setTime(uint8_t ptr[], const uint16_t nr_mins); - IRsend _irsend; }; class IRHaierACYRW02 { @@ -245,7 +256,7 @@ class IRHaierACYRW02 { explicit IRHaierACYRW02(uint16_t pin); #if SEND_HAIER_AC_YRW02 - void send(); + void send(const uint16_t repeat = kHaierAcYrw02DefaultRepeat); #endif // SEND_HAIER_AC_YRW02 void begin(); @@ -281,17 +292,24 @@ class IRHaierACYRW02 { void setRaw(uint8_t new_code[]); static bool validChecksum(uint8_t state[], const uint16_t length = kHaierACYRW02StateLength); + uint8_t convertMode(const stdAc::opmode_t mode); + uint8_t convertFan(const stdAc::fanspeed_t speed); + uint8_t convertSwingV(const stdAc::swingv_t position); #ifdef ARDUINO String toString(); #else std::string toString(); #endif +#ifndef UNIT_TEST private: + IRsend _irsend; +#else + IRsendTest _irsend; +#endif uint8_t remote_state[kHaierACYRW02StateLength]; void stateReset(); void checksum(); - IRsend _irsend; }; #endif // IR_HAIER_H_ diff --git a/lib/IRremoteESP8266-2.5.2.03/src/ir_Hitachi.cpp b/lib/IRremoteESP8266-2.6.0/src/ir_Hitachi.cpp similarity index 86% rename from lib/IRremoteESP8266-2.5.2.03/src/ir_Hitachi.cpp rename to lib/IRremoteESP8266-2.6.0/src/ir_Hitachi.cpp index 111051974c84..b88189f4a03b 100644 --- a/lib/IRremoteESP8266-2.5.2.03/src/ir_Hitachi.cpp +++ b/lib/IRremoteESP8266-2.6.0/src/ir_Hitachi.cpp @@ -30,7 +30,7 @@ const uint16_t kHitachiAc1HdrSpace = 3400; const uint16_t kHitachiAcBitMark = 400; const uint16_t kHitachiAcOneSpace = 1250; const uint16_t kHitachiAcZeroSpace = 500; -const uint32_t kHitachiAcMinGap = 100000; // Completely made up value. +const uint32_t kHitachiAcMinGap = kDefaultMessageGap; // Just a guess. #if (SEND_HITACHI_AC || SEND_HITACHI_AC2) // Send a Hitachi A/C message. @@ -106,7 +106,7 @@ void IRsend::sendHitachiAC2(unsigned char data[], uint16_t nbytes, } #endif // SEND_HITACHI_AC2 -// Class for handling the remote control oh a Hitachi 28 byte A/C message. +// Class for handling the remote control on a Hitachi 28 byte A/C message. // Inspired by: // https://github.com/ToniA/arduino-heatpumpir/blob/master/HitachiHeatpumpIR.cpp @@ -159,9 +159,9 @@ void IRHitachiAc::setRaw(const uint8_t new_code[], const uint16_t length) { } #if SEND_HITACHI_AC -void IRHitachiAc::send() { +void IRHitachiAc::send(const uint16_t repeat) { checksum(); - _irsend.sendHitachiAC(remote_state); + _irsend.sendHitachiAC(remote_state, kHitachiAcStateLength, repeat); } #endif // SEND_HITACHI_AC @@ -257,6 +257,40 @@ void IRHitachiAc::setSwingHorizontal(const bool on) { remote_state[15] &= 0x7F; } + +// Convert a standard A/C mode into its native mode. +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 standard A/C Fan speed into its native fan speed. +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 the internal state into a human readable string. #ifdef ARDUINO String IRHitachiAc::toString() { @@ -265,57 +299,60 @@ String IRHitachiAc::toString() { std::string IRHitachiAc::toString() { std::string result = ""; #endif // ARDUINO - result += "Power: "; + result += F("Power: "); if (getPower()) - result += "On"; + result += F("On"); else - result += "Off"; - result += ", Mode: " + uint64ToString(getMode()); + result += F("Off"); + result += F(", Mode: "); + result += uint64ToString(getMode()); switch (getMode()) { case kHitachiAcAuto: - result += " (AUTO)"; + result += F(" (AUTO)"); break; case kHitachiAcCool: - result += " (COOL)"; + result += F(" (COOL)"); break; case kHitachiAcHeat: - result += " (HEAT)"; + result += F(" (HEAT)"); break; case kHitachiAcDry: - result += " (DRY)"; + result += F(" (DRY)"); break; case kHitachiAcFan: - result += " (FAN)"; + result += F(" (FAN)"); break; default: - result += " (UNKNOWN)"; + result += F(" (UNKNOWN)"); } - result += ", Temp: " + uint64ToString(getTemp()) + "C"; - result += ", Fan: " + uint64ToString(getFan()); + result += F(", Temp: "); + result += uint64ToString(getTemp()); + result += F("C, Fan: "); + result += uint64ToString(getFan()); switch (getFan()) { case kHitachiAcFanAuto: - result += " (AUTO)"; + result += F(" (AUTO)"); break; case kHitachiAcFanLow: - result += " (LOW)"; + result += F(" (LOW)"); break; case kHitachiAcFanHigh: - result += " (HIGH)"; + result += F(" (HIGH)"); break; default: - result += " (UNKNOWN)"; + result += F(" (UNKNOWN)"); break; } - result += ", Swing (Vertical): "; + result += F(", Swing (Vertical): "); if (getSwingVertical()) - result += "On"; + result += F("On"); else - result += "Off"; - result += ", Swing (Horizontal): "; + result += F("Off"); + result += F(", Swing (Horizontal): "); if (getSwingHorizontal()) - result += "On"; + result += F("On"); else - result += "Off"; + result += F("Off"); return result; } diff --git a/lib/IRremoteESP8266-2.5.2.03/src/ir_Hitachi.h b/lib/IRremoteESP8266-2.6.0/src/ir_Hitachi.h similarity index 87% rename from lib/IRremoteESP8266-2.5.2.03/src/ir_Hitachi.h rename to lib/IRremoteESP8266-2.6.0/src/ir_Hitachi.h index eddab59e43c6..532717447df9 100644 --- a/lib/IRremoteESP8266-2.5.2.03/src/ir_Hitachi.h +++ b/lib/IRremoteESP8266-2.6.0/src/ir_Hitachi.h @@ -14,6 +14,9 @@ #endif #include "IRremoteESP8266.h" #include "IRsend.h" +#ifdef UNIT_TEST +#include "IRsend_test.h" +#endif // Constants const uint8_t kHitachiAcAuto = 2; @@ -35,7 +38,7 @@ class IRHitachiAc { void stateReset(); #if SEND_HITACHI_AC - void send(); + void send(const uint16_t repeat = kHitachiAcDefaultRepeat); #endif // SEND_HITACHI_AC void begin(); void on(); @@ -59,17 +62,23 @@ class IRHitachiAc { const uint16_t length = kHitachiAcStateLength); static uint8_t calcChecksum(const uint8_t state[], const uint16_t length = kHitachiAcStateLength); + uint8_t convertMode(const stdAc::opmode_t mode); + uint8_t convertFan(const stdAc::fanspeed_t speed); #ifdef ARDUINO String toString(); #else std::string toString(); #endif +#ifndef UNIT_TEST private: + IRsend _irsend; +#else + IRsendTest _irsend; +#endif // The state of the IR remote in IR code form. uint8_t remote_state[kHitachiAcStateLength]; void checksum(const uint16_t length = kHitachiAcStateLength); - IRsend _irsend; uint8_t _previoustemp; }; diff --git a/lib/IRremoteESP8266-2.5.2.03/src/ir_JVC.cpp b/lib/IRremoteESP8266-2.6.0/src/ir_JVC.cpp similarity index 100% rename from lib/IRremoteESP8266-2.5.2.03/src/ir_JVC.cpp rename to lib/IRremoteESP8266-2.6.0/src/ir_JVC.cpp diff --git a/lib/IRremoteESP8266-2.5.2.03/src/ir_Kelvinator.cpp b/lib/IRremoteESP8266-2.6.0/src/ir_Kelvinator.cpp similarity index 91% rename from lib/IRremoteESP8266-2.5.2.03/src/ir_Kelvinator.cpp rename to lib/IRremoteESP8266-2.6.0/src/ir_Kelvinator.cpp index ddf61b097aba..c69f4cb8ad13 100644 --- a/lib/IRremoteESP8266-2.5.2.03/src/ir_Kelvinator.cpp +++ b/lib/IRremoteESP8266-2.6.0/src/ir_Kelvinator.cpp @@ -19,6 +19,7 @@ #ifndef ARDUINO #include #endif +#include "IRac.h" #include "IRrecv.h" #include "IRsend.h" #include "IRutils.h" @@ -141,9 +142,9 @@ void IRKelvinatorAC::fixup() { } #if SEND_KELVINATOR -void IRKelvinatorAC::send() { +void IRKelvinatorAC::send(const uint16_t repeat) { fixup(); // Ensure correct settings before sending. - _irsend.sendKelvinator(remote_state); + _irsend.sendKelvinator(remote_state, kKelvinatorStateLength, repeat); } #endif // SEND_KELVINATOR @@ -347,6 +348,22 @@ bool IRKelvinatorAC::getTurbo() { return ((remote_state[2] & kKelvinatorTurbo) != 0); } +// Convert a standard A/C mode into its native mode. +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 the internal state into a human readable string. #ifdef ARDUINO String IRKelvinatorAC::toString() { @@ -355,76 +372,79 @@ String IRKelvinatorAC::toString() { std::string IRKelvinatorAC::toString() { std::string result = ""; #endif // ARDUINO - result += "Power: "; + result += F("Power: "); if (getPower()) - result += "On"; + result += F("On"); else - result += "Off"; - result += ", Mode: " + uint64ToString(getMode()); + result += F("Off"); + result += F(", Mode: "); + result += uint64ToString(getMode()); switch (getMode()) { case kKelvinatorAuto: - result += " (AUTO)"; + result += F(" (AUTO)"); break; case kKelvinatorCool: - result += " (COOL)"; + result += F(" (COOL)"); break; case kKelvinatorHeat: - result += " (HEAT)"; + result += F(" (HEAT)"); break; case kKelvinatorDry: - result += " (DRY)"; + result += F(" (DRY)"); break; case kKelvinatorFan: - result += " (FAN)"; + result += F(" (FAN)"); break; default: - result += " (UNKNOWN)"; + result += F(" (UNKNOWN)"); } - result += ", Temp: " + uint64ToString(getTemp()) + "C"; - result += ", Fan: " + uint64ToString(getFan()); + result += F(", Temp: "); + result += uint64ToString(getTemp()); + result += F("C, Fan: "); + result += uint64ToString(getFan()); switch (getFan()) { case kKelvinatorFanAuto: - result += " (AUTO)"; + result += F(" (AUTO)"); break; case kKelvinatorFanMax: - result += " (MAX)"; + result += F(" (MAX)"); break; } - result += ", Turbo: "; + result += F(", Turbo: "); if (getTurbo()) - result += "On"; + result += F("On"); else - result += "Off"; - result += ", Quiet: "; + result += F("Off"); + result += F(", Quiet: "); if (getQuiet()) - result += "On"; + result += F("On"); else - result += "Off"; - result += ", XFan: "; + result += F("Off"); + result += F(", XFan: "); if (getXFan()) - result += "On"; + result += F("On"); else - result += "Off"; - result += ", IonFilter: "; + result += F("Off"); + result += F(", IonFilter: "); if (getIonFilter()) - result += "On"; + result += F("On"); else - result += "Off"; - result += ", Light: "; + result += F("Off"); + result += F(", Light: "); if (getLight()) - result += "On"; + result += F("On"); else - result += "Off"; - result += ", Swing (Horizontal): "; + result += F("Off"); + result += F(", Swing (Horizontal): "); if (getSwingHorizontal()) - result += "On"; + result += F("On"); else - result += "Off"; - result += ", Swing (Vertical): "; + result += F("Off"); + result += F(", Swing (Vertical): "); if (getSwingVertical()) - result += "On"; + result += F("On"); else - result += "Off"; + result += F("Off"); return result; } diff --git a/lib/IRremoteESP8266-2.5.2.03/src/ir_Kelvinator.h b/lib/IRremoteESP8266-2.6.0/src/ir_Kelvinator.h similarity index 96% rename from lib/IRremoteESP8266-2.5.2.03/src/ir_Kelvinator.h rename to lib/IRremoteESP8266-2.6.0/src/ir_Kelvinator.h index 1508d6cdc8f2..ce830c70af69 100644 --- a/lib/IRremoteESP8266-2.5.2.03/src/ir_Kelvinator.h +++ b/lib/IRremoteESP8266-2.6.0/src/ir_Kelvinator.h @@ -14,6 +14,9 @@ #endif #include "IRremoteESP8266.h" #include "IRsend.h" +#ifdef UNIT_TEST +#include "IRsend_test.h" +#endif // KK KK EEEEEEE LL VV VV IIIII NN NN AAA TTTTTTT OOOOO RRRRRR // KK KK EE LL VV VV III NNN NN AAAAA TTT OO OO RR RR @@ -130,7 +133,7 @@ class IRKelvinatorAC { void stateReset(); #if SEND_KELVINATOR - void send(); + void send(const uint16_t repeat = kKelvinatorDefaultRepeat); #endif // SEND_KELVINATOR void begin(); void on(); @@ -163,18 +166,23 @@ class IRKelvinatorAC { const uint8_t* block, const uint16_t length = kKelvinatorStateLength / 2); static bool validChecksum(const uint8_t state[], const uint16_t length = kKelvinatorStateLength); + uint8_t convertMode(const stdAc::opmode_t mode); #ifdef ARDUINO String toString(); #else std::string toString(); #endif +#ifndef UNIT_TEST private: + IRsend _irsend; +#else + IRsendTest _irsend; +#endif // The state of the IR remote in IR code form. uint8_t remote_state[kKelvinatorStateLength]; void checksum(const uint16_t length = kKelvinatorStateLength); void fixup(); - IRsend _irsend; }; #endif // IR_KELVINATOR_H_ diff --git a/lib/IRremoteESP8266-2.5.2.03/src/ir_LG.cpp b/lib/IRremoteESP8266-2.6.0/src/ir_LG.cpp similarity index 98% rename from lib/IRremoteESP8266-2.5.2.03/src/ir_LG.cpp rename to lib/IRremoteESP8266-2.6.0/src/ir_LG.cpp index f9d922fc740e..36c85ff1518e 100644 --- a/lib/IRremoteESP8266-2.5.2.03/src/ir_LG.cpp +++ b/lib/IRremoteESP8266-2.6.0/src/ir_LG.cpp @@ -85,11 +85,13 @@ uint8_t calcLGChecksum(uint16_t data) { // IR Remote models: 6711A20083V 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. @@ -97,7 +99,7 @@ void IRsend::sendLG(uint64_t data, uint16_t nbits, uint16_t repeat) { sendGeneric(kLgHdrMark, kLgHdrSpace, kLgBitMark, kLgOneSpace, kLgBitMark, kLgZeroSpace, kLgBitMark, kLgMinGap, kLgMinMessageLength, data, nbits, 38, true, 0, // Repeats are handled later. - 50); + duty); } // Repeat @@ -105,7 +107,7 @@ void IRsend::sendLG(uint64_t data, uint16_t nbits, uint16_t repeat) { if (repeat) sendGeneric(repeatHeaderMark, kLgRptSpace, 0, 0, 0, 0, // No data is sent. kLgBitMark, kLgMinGap, kLgMinMessageLength, 0, 0, // No data. - 38, true, repeat - 1, 50); + 38, true, repeat - 1, duty); } // Send an LG Variant-2 formatted message. diff --git a/lib/IRremoteESP8266-2.5.2.03/src/ir_LG.h b/lib/IRremoteESP8266-2.6.0/src/ir_LG.h similarity index 100% rename from lib/IRremoteESP8266-2.5.2.03/src/ir_LG.h rename to lib/IRremoteESP8266-2.6.0/src/ir_LG.h diff --git a/lib/IRremoteESP8266-2.5.2.03/src/ir_Lasertag.cpp b/lib/IRremoteESP8266-2.6.0/src/ir_Lasertag.cpp similarity index 98% rename from lib/IRremoteESP8266-2.5.2.03/src/ir_Lasertag.cpp rename to lib/IRremoteESP8266-2.6.0/src/ir_Lasertag.cpp index 7f0b89ae9110..b1cbdc9b1d83 100644 --- a/lib/IRremoteESP8266-2.5.2.03/src/ir_Lasertag.cpp +++ b/lib/IRremoteESP8266-2.6.0/src/ir_Lasertag.cpp @@ -14,7 +14,7 @@ // Constants const uint16_t kLasertagMinSamples = 13; const uint16_t kLasertagTick = 333; -const uint32_t kLasertagMinGap = 100000; // Completely made up amount. +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 = 150; // Use instead of Excess and Tolerance. diff --git a/lib/IRremoteESP8266-2.6.0/src/ir_Lego.cpp b/lib/IRremoteESP8266-2.6.0/src/ir_Lego.cpp new file mode 100644 index 000000000000..b051aba512e7 --- /dev/null +++ b/lib/IRremoteESP8266-2.6.0/src/ir_Lego.cpp @@ -0,0 +1,128 @@ +// Copyright 2019 David Conran + +#include +#include "IRrecv.h" +#include "IRsend.h" +#include "IRutils.h" + +// LEGO +// (LEGO is a Registrated Trademark of the Lego Group.) +// +// Supported Devices: +// - LEGO Power Functions IR Receiver +// +// Ref: +// - https://github.com/markszabo/IRremoteESP8266/issues/641 +// - https://github.com/markszabo/IRremoteESP8266/files/2974525/LEGO_Power_Functions_RC_v120.pdf + +// 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. +// +// Args: +// data: Contents of the message to be sent. +// nbits: Nr. of bits of data to be sent. Typically kLegoPfBits. +// repeat: Nr. of additional times the message is to be sent. +// Note: Non-zero repeats results in at least 5 messages per spec. +// +// Status: Beta / Should work. +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. +// +// Args: +// results: Ptr to the data to decode and where to store the decode result. +// nbits: The number of data bits to expect. Typically kLegoPfBits. +// strict: Flag indicating if we should perform strict matching. +// Returns: +// boolean: True if it can decode it, false if it can't. +// +// Status: Alpha / Untested. +bool IRrecv::decodeLegoPf(decode_results* results, + const uint16_t nbits, const bool strict) { + // Check if can possibly be a valid LEGO message. + if (results->rawlen < 2 * nbits + kHeader + kFooter - 1) return false; + if (strict && nbits != kLegoPfBits) return false; // Not what is expected + + uint64_t data = 0; + uint16_t offset = kStartOffset; + match_result_t data_result; + + // Header + if (!matchMark(results->rawbuf[offset++], kLegoPfBitMark)) return false; + if (!matchSpace(results->rawbuf[offset++], kLegoPfHdrSpace)) return false; + // Data (Typically 16 bits) + data_result = + matchData(&(results->rawbuf[offset]), nbits, + kLegoPfBitMark, kLegoPfOneSpace, + kLegoPfBitMark, kLegoPfZeroSpace, + kTolerance, kMarkExcess, true); + if (data_result.success == false) return false; + data = data_result.data; + offset += data_result.used; + uint16_t actualBits = data_result.used / 2; + + // Footer. + if (!matchMark(results->rawbuf[offset++], kLegoPfBitMark)) return false; + if (offset < results->rawlen && + !matchAtLeast(results->rawbuf[offset], kLegoPfMinCommandLength)) + return false; + + // Compliance + if (actualBits < nbits) return false; + if (strict) { + if (actualBits != nbits) return false; // Not as we expected. + // 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 = actualBits; + 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-2.5.2.03/src/ir_Lutron.cpp b/lib/IRremoteESP8266-2.6.0/src/ir_Lutron.cpp similarity index 100% rename from lib/IRremoteESP8266-2.5.2.03/src/ir_Lutron.cpp rename to lib/IRremoteESP8266-2.6.0/src/ir_Lutron.cpp diff --git a/lib/IRremoteESP8266-2.5.2.03/src/ir_MWM.cpp b/lib/IRremoteESP8266-2.6.0/src/ir_MWM.cpp similarity index 98% rename from lib/IRremoteESP8266-2.5.2.03/src/ir_MWM.cpp rename to lib/IRremoteESP8266-2.6.0/src/ir_MWM.cpp index a75e99e3a51d..61eac49e2744 100644 --- a/lib/IRremoteESP8266-2.5.2.03/src/ir_MWM.cpp +++ b/lib/IRremoteESP8266-2.6.0/src/ir_MWM.cpp @@ -105,7 +105,7 @@ bool IRrecv::decodeMWM(decode_results *results, uint16_t nbits, bool strict) { for (; offset < results->rawlen && results->bits < 8 * kStateSizeMax; frame_bits++) { DPRINT("DEBUG: decodeMWM: offset = "); - DPRINTLN(uint64ToString(offset)); + DPRINTLN(offset); int16_t level = getRClevel(results, &offset, &used, kMWMTick, kMWMTolerance, kMWMExcess, kMWMDelta, kMWMMaxWidth); if (level < 0) { @@ -129,7 +129,7 @@ bool IRrecv::decodeMWM(decode_results *results, uint16_t nbits, bool strict) { DPRINT("DEBUG: decodeMWM: data_bits = "); DPRINTLN(data_bits); DPRINT("DEBUG: decodeMWM: Finished byte: "); - DPRINTLN(data); + DPRINTLN(uint64ToString(data)); results->state[data_bits / 8 - 1] = data & 0xFF; results->bits = data_bits; data = 0; diff --git a/lib/IRremoteESP8266-2.5.2.03/src/ir_Magiquest.cpp b/lib/IRremoteESP8266-2.6.0/src/ir_Magiquest.cpp similarity index 100% rename from lib/IRremoteESP8266-2.5.2.03/src/ir_Magiquest.cpp rename to lib/IRremoteESP8266-2.6.0/src/ir_Magiquest.cpp diff --git a/lib/IRremoteESP8266-2.5.2.03/src/ir_Magiquest.h b/lib/IRremoteESP8266-2.6.0/src/ir_Magiquest.h similarity index 86% rename from lib/IRremoteESP8266-2.5.2.03/src/ir_Magiquest.h rename to lib/IRremoteESP8266-2.6.0/src/ir_Magiquest.h index d2d82d152767..81fff53adf13 100644 --- a/lib/IRremoteESP8266-2.5.2.03/src/ir_Magiquest.h +++ b/lib/IRremoteESP8266-2.6.0/src/ir_Magiquest.h @@ -31,5 +31,5 @@ const uint16_t kMagiQuestMarkZero = 280; const uint16_t kMagiQuestSpaceZero = 850; const uint16_t kMagiQuestMarkOne = 580; const uint16_t kMagiQuestSpaceOne = 600; -const uint32_t kMagiQuestGap = 100000; // A guess of the gap between messages -#endif // IR_MAGIQUEST_H_ +const uint32_t kMagiQuestGap = kDefaultMessageGap; // Just a guess. +#endif // IR_MAGIQUEST_H_ diff --git a/lib/IRremoteESP8266-2.5.2.03/src/ir_Midea.cpp b/lib/IRremoteESP8266-2.6.0/src/ir_Midea.cpp similarity index 87% rename from lib/IRremoteESP8266-2.5.2.03/src/ir_Midea.cpp rename to lib/IRremoteESP8266-2.6.0/src/ir_Midea.cpp index 8e55c7d227be..8d5d9494fd84 100644 --- a/lib/IRremoteESP8266-2.5.2.03/src/ir_Midea.cpp +++ b/lib/IRremoteESP8266-2.6.0/src/ir_Midea.cpp @@ -104,9 +104,9 @@ void IRMideaAC::begin() { _irsend.begin(); } #if SEND_MIDEA // Send the current desired state to the IR LED. -void IRMideaAC::send() { +void IRMideaAC::send(const uint16_t repeat) { checksum(); // Ensure correct checksum before sending. - _irsend.sendMidea(remote_state); + _irsend.sendMidea(remote_state, kMideaBits, repeat); } #endif // SEND_MIDEA @@ -257,6 +257,39 @@ void IRMideaAC::checksum() { remote_state |= calcChecksum(remote_state); } + +// Convert a standard A/C mode into its native mode. +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 standard A/C Fan speed into its native fan speed. +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 the internal state into a human readable string. #ifdef ARDUINO String IRMideaAC::toString() { @@ -265,53 +298,57 @@ String IRMideaAC::toString() { std::string IRMideaAC::toString() { std::string result = ""; #endif // ARDUINO - result += "Power: "; + result += F("Power: "); if (getPower()) - result += "On"; + result += F("On"); else - result += "Off"; - result += ", Mode: " + uint64ToString(getMode()); + result += F("Off"); + result += F(", Mode: "); + result += uint64ToString(getMode()); switch (getMode()) { case kMideaACAuto: - result += " (AUTO)"; + result += F(" (AUTO)"); break; case kMideaACCool: - result += " (COOL)"; + result += F(" (COOL)"); break; case kMideaACHeat: - result += " (HEAT)"; + result += F(" (HEAT)"); break; case kMideaACDry: - result += " (DRY)"; + result += F(" (DRY)"); break; case kMideaACFan: - result += " (FAN)"; + result += F(" (FAN)"); break; default: - result += " (UNKNOWN)"; + result += F(" (UNKNOWN)"); } - result += ", Temp: " + uint64ToString(getTemp(true)) + "C/" + - uint64ToString(getTemp(false)) + "F"; - result += ", Fan: " + uint64ToString(getFan()); + result += F(", Temp: "); + result += uint64ToString(getTemp(true)); + result += F("C/"); + result += uint64ToString(getTemp(false)); + result += F("F, Fan: "); + result += uint64ToString(getFan()); switch (getFan()) { case kMideaACFanAuto: - result += " (AUTO)"; + result += F(" (AUTO)"); break; case kMideaACFanLow: - result += " (LOW)"; + result += F(" (LOW)"); break; case kMideaACFanMed: - result += " (MED)"; + result += F(" (MED)"); break; case kMideaACFanHigh: - result += " (HI)"; + result += F(" (HI)"); break; } - result += ", Sleep: "; + result += F(", Sleep: "); if (getSleep()) - result += "On"; + result += F("On"); else - result += "Off"; + result += F("Off"); return result; } diff --git a/lib/IRremoteESP8266-2.5.2.03/src/ir_Midea.h b/lib/IRremoteESP8266-2.6.0/src/ir_Midea.h similarity index 93% rename from lib/IRremoteESP8266-2.5.2.03/src/ir_Midea.h rename to lib/IRremoteESP8266-2.6.0/src/ir_Midea.h index aa9f94a928a2..ab14eb252e2c 100644 --- a/lib/IRremoteESP8266-2.5.2.03/src/ir_Midea.h +++ b/lib/IRremoteESP8266-2.6.0/src/ir_Midea.h @@ -11,6 +11,9 @@ #endif #include "IRremoteESP8266.h" #include "IRsend.h" +#ifdef UNIT_TEST +#include "IRsend_test.h" +#endif // MM MM IIIII DDDDD EEEEEEE AAA // MMM MMM III DD DD EE AAAAA @@ -67,7 +70,7 @@ class IRMideaAC { void stateReset(); #if SEND_MIDEA - void send(); + void send(const uint16_t repeat = kMideaMinRepeat); #endif // SEND_MIDEA void begin(); void on(); @@ -85,6 +88,8 @@ class IRMideaAC { static bool validChecksum(const uint64_t state); void setSleep(const bool state); bool getSleep(); + uint8_t convertMode(const stdAc::opmode_t mode); + uint8_t convertFan(const stdAc::fanspeed_t speed); #ifdef ARDUINO String toString(); #else @@ -93,11 +98,13 @@ class IRMideaAC { #ifndef UNIT_TEST private: + IRsend _irsend; +#else + IRsendTest _irsend; #endif uint64_t remote_state; void checksum(); static uint8_t calcChecksum(const uint64_t state); - IRsend _irsend; }; #endif // IR_MIDEA_H_ diff --git a/lib/IRremoteESP8266-2.5.2.03/src/ir_Mitsubishi.cpp b/lib/IRremoteESP8266-2.6.0/src/ir_Mitsubishi.cpp similarity index 90% rename from lib/IRremoteESP8266-2.5.2.03/src/ir_Mitsubishi.cpp rename to lib/IRremoteESP8266-2.6.0/src/ir_Mitsubishi.cpp index b092c27b9573..ca9bef5d98c9 100644 --- a/lib/IRremoteESP8266-2.5.2.03/src/ir_Mitsubishi.cpp +++ b/lib/IRremoteESP8266-2.6.0/src/ir_Mitsubishi.cpp @@ -165,7 +165,7 @@ bool IRrecv::decodeMitsubishi(decode_results *results, uint16_t nbits, // This protocol appears to have a manditory 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. +// i.e. Allegedly, the real remote requires the "Off" button pressed twice. // You will need to add a suitable gap yourself. // Ref: // https://github.com/markszabo/IRremoteESP8266/issues/441 @@ -453,9 +453,9 @@ void IRMitsubishiAC::begin() { _irsend.begin(); } #if SEND_MITSUBISHI_AC // Send the current desired state to the IR LED. -void IRMitsubishiAC::send() { +void IRMitsubishiAC::send(const uint16_t repeat) { checksum(); // Ensure correct checksum before sending. - _irsend.sendMitsubishiAC(remote_state); + _irsend.sendMitsubishiAC(remote_state, kMitsubishiACStateLength, repeat); } #endif // SEND_MITSUBISHI_AC @@ -615,6 +615,52 @@ void IRMitsubishiAC::setTimer(uint8_t timer) { remote_state[13] = timer & 0b111; } +// Convert a standard A/C mode into its native mode. +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; + default: + return kMitsubishiAcAuto; + } +} + +// Convert a standard A/C Fan speed into its native fan speed. +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 standard A/C vertical swing into its native setting. +uint8_t IRMitsubishiAC::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 kMitsubishiAcVaneAutoMove; + default: + return kMitsubishiAcVaneAuto; + } +} + #ifdef ARDUINO String IRMitsubishiAC::timeToString(uint64_t time) { String result = ""; @@ -622,10 +668,10 @@ String IRMitsubishiAC::timeToString(uint64_t time) { std::string IRMitsubishiAC::timeToString(uint64_t time) { std::string result = ""; #endif // ARDUINO - if (time / 6 < 10) result += "0"; + if (time / 6 < 10) result += '0'; result += uint64ToString(time / 6); - result += ":"; - if (time * 10 % 60 < 10) result += "0"; + result += ':'; + if (time * 10 % 60 < 10) result += '0'; result += uint64ToString(time * 10 % 60); return result; } @@ -638,77 +684,78 @@ String IRMitsubishiAC::toString() { std::string IRMitsubishiAC::toString() { std::string result = ""; #endif // ARDUINO - result += "Power: "; + result += F("Power: "); if (getPower()) - result += "On"; + result += F("On"); else - result += "Off"; + result += F("Off"); switch (getMode()) { case MITSUBISHI_AC_AUTO: - result += " (AUTO)"; + result += F(" (AUTO)"); break; case MITSUBISHI_AC_COOL: - result += " (COOL)"; + result += F(" (COOL)"); break; case MITSUBISHI_AC_DRY: - result += " (DRY)"; + result += F(" (DRY)"); break; case MITSUBISHI_AC_HEAT: - result += " (HEAT)"; + result += F(" (HEAT)"); break; default: - result += " (UNKNOWN)"; + result += F(" (UNKNOWN)"); } - result += ", Temp: " + uint64ToString(getTemp()) + "C"; - result += ", FAN: "; + result += F(", Temp: "); + result += uint64ToString(getTemp()); + result += F("C, FAN: "); switch (getFan()) { case MITSUBISHI_AC_FAN_AUTO: - result += "AUTO"; + result += F("AUTO"); break; case MITSUBISHI_AC_FAN_MAX: - result += "MAX"; + result += F("MAX"); break; case MITSUBISHI_AC_FAN_SILENT: - result += "SILENT"; + result += F("SILENT"); break; default: result += uint64ToString(getFan()); } - result += ", VANE: "; + result += F(", VANE: "); switch (getVane()) { case MITSUBISHI_AC_VANE_AUTO: - result += "AUTO"; + result += F("AUTO"); break; case MITSUBISHI_AC_VANE_AUTO_MOVE: - result += "AUTO MOVE"; + result += F("AUTO MOVE"); break; default: result += uint64ToString(getVane()); } - result += ", Time: "; + result += F(", Time: "); result += timeToString(getClock()); - result += ", On timer: "; + result += F(", On timer: "); result += timeToString(getStartClock()); - result += ", Off timer: "; + result += F(", Off timer: "); result += timeToString(getStopClock()); - result += ", Timer: "; + result += F(", Timer: "); switch (getTimer()) { case kMitsubishiAcNoTimer: - result += "-"; + result += '-'; break; case kMitsubishiAcStartTimer: - result += "Start"; + result += F("Start"); break; case kMitsubishiAcStopTimer: - result += "Stop"; + result += F("Stop"); break; case kMitsubishiAcStartStopTimer: - result += "Start+Stop"; + result += F("Start+Stop"); break; default: - result += "? ("; + result += F("? ("); result += getTimer(); - result += ")\n"; + result += F(")\n"); } return result; } diff --git a/lib/IRremoteESP8266-2.5.2.03/src/ir_Mitsubishi.h b/lib/IRremoteESP8266-2.6.0/src/ir_Mitsubishi.h similarity index 91% rename from lib/IRremoteESP8266-2.5.2.03/src/ir_Mitsubishi.h rename to lib/IRremoteESP8266-2.6.0/src/ir_Mitsubishi.h index 7b03efce6463..c8dca5dbcd47 100644 --- a/lib/IRremoteESP8266-2.5.2.03/src/ir_Mitsubishi.h +++ b/lib/IRremoteESP8266-2.6.0/src/ir_Mitsubishi.h @@ -12,6 +12,9 @@ #endif #include "IRremoteESP8266.h" #include "IRsend.h" +#ifdef UNIT_TEST +#include "IRsend_test.h" +#endif // MMMMM IIIII TTTTT SSSS U U BBBB IIIII SSSS H H IIIII // M M M I T S U U B B I S H H I @@ -64,7 +67,7 @@ class IRMitsubishiAC { void stateReset(); #if SEND_MITSUBISHI_AC - void send(); + void send(const uint16_t repeat = kMitsubishiACMinRepeat); #endif // SEND_MITSUBISHI_AC void begin(); void on(); @@ -89,13 +92,21 @@ class IRMitsubishiAC { void setStopClock(uint8_t clock); uint8_t getTimer(); void setTimer(uint8_t timer); + uint8_t convertMode(const stdAc::opmode_t mode); + uint8_t convertFan(const stdAc::fanspeed_t speed); + uint8_t convertSwingV(const stdAc::swingv_t position); #ifdef ARDUINO String toString(); #else std::string toString(); #endif +#ifndef UNIT_TEST private: + IRsend _irsend; +#else + IRsendTest _irsend; +#endif #ifdef ARDUINO String timeToString(uint64_t time); #else @@ -103,7 +114,6 @@ class IRMitsubishiAC { #endif uint8_t remote_state[kMitsubishiACStateLength]; void checksum(); - IRsend _irsend; }; #endif // IR_MITSUBISHI_H_ diff --git a/lib/IRremoteESP8266-2.6.0/src/ir_MitsubishiHeavy.cpp b/lib/IRremoteESP8266-2.6.0/src/ir_MitsubishiHeavy.cpp new file mode 100644 index 000000000000..9048124d4bda --- /dev/null +++ b/lib/IRremoteESP8266-2.6.0/src/ir_MitsubishiHeavy.cpp @@ -0,0 +1,1014 @@ +// Copyright 2019 David Conran +// Mitsubishi Heavy Industries A/C remote emulation. + +// Code to emulate Mitsubishi Heavy Industries A/C IR remote control units, +// which should control at least the following A/C units: +// Remote Control RLA502A700B: +// Model SRKxxZM-S +// Model SRKxxZMXA-S +// Remote Control RKX502A001C: +// Model SRKxxZJ-S + +// 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. + +#include "ir_MitsubishiHeavy.h" +#include +#include "IRremoteESP8266.h" +#include "IRutils.h" +#ifndef ARDUINO +#include +#endif + +// Ref: +// https://github.com/markszabo/IRremoteESP8266/issues/660 +// https://github.com/ToniA/Raw-IR-decoder-for-Arduino/blob/master/MitsubishiHeavy.cpp +// https://github.com/ToniA/arduino-heatpumpir/blob/master/MitsubishiHeavyHeatpumpIR.cpp + +// 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. + +#if SEND_MITSUBISHIHEAVY +// Send a MitsubishiHeavy 88 bit A/C message. +// +// Args: +// data: Contents of the message to be sent. +// nbits: Nr. of bits of data to be sent. Typically kMitsubishiHeavy88Bits. +// repeat: Nr. of additional times the message is to be sent. +// +// Status: BETA / Appears to be working. Needs testing against a real device. +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. +// +// Args: +// data: Contents of the message to be sent. +// nbits: Nr. of bits of data to be sent. Typically kMitsubishiHeavy152Bits. +// repeat: Nr. of additional times the message is to be sent. +// +// Status: BETA / Appears to be working. Needs testing against a real device. +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. +IRMitsubishiHeavy152Ac::IRMitsubishiHeavy152Ac( + const uint16_t pin) : _irsend(pin) { stateReset(); } + +void IRMitsubishiHeavy152Ac::begin() { _irsend.begin(); } + +#if SEND_MITSUBISHIHEAVY +void IRMitsubishiHeavy152Ac::send(const uint16_t repeat) { + _irsend.sendMitsubishiHeavy152(this->getRaw(), kMitsubishiHeavy152StateLength, + repeat); +} +#endif // SEND_MITSUBISHIHEAVY + +void IRMitsubishiHeavy152Ac::stateReset(void) { + uint8_t i = 0; + for (; i < kMitsubishiHeavySigLength; i++) + remote_state[i] = kMitsubishiHeavyZmsSig[i]; + for (; i < kMitsubishiHeavy152StateLength - 3; i += 2) remote_state[i] = 0; + remote_state[17] = 0x80; +} + +uint8_t *IRMitsubishiHeavy152Ac::getRaw(void) { + checksum(); + return remote_state; +} + +void IRMitsubishiHeavy152Ac::setRaw(const uint8_t *data) { + for (uint8_t i = 0; i < kMitsubishiHeavy152StateLength; i++) + remote_state[i] = data[i]; +} + +void IRMitsubishiHeavy152Ac::on(void) { + remote_state[5] |= kMitsubishiHeavyPowerBit; +} + +void IRMitsubishiHeavy152Ac::off(void) { + remote_state[5] &= ~kMitsubishiHeavyPowerBit; +} + +void IRMitsubishiHeavy152Ac::setPower(const bool on) { + if (on) + this->on(); + else + this->off(); +} + +bool IRMitsubishiHeavy152Ac::getPower(void) { + return remote_state[5] & kMitsubishiHeavyPowerBit; +} + +void IRMitsubishiHeavy152Ac::setTemp(const uint8_t temp) { + uint8_t newtemp = temp; + newtemp = std::min(newtemp, kMitsubishiHeavyMaxTemp); + newtemp = std::max(newtemp, kMitsubishiHeavyMinTemp); + + remote_state[7] &= ~kMitsubishiHeavyTempMask; + remote_state[7] |= newtemp - kMitsubishiHeavyMinTemp; +} + +uint8_t IRMitsubishiHeavy152Ac::getTemp(void) { + return (remote_state[7] & kMitsubishiHeavyTempMask) + kMitsubishiHeavyMinTemp; +} + +// Set the speed of the fan +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; + } + remote_state[9] &= ~kMitsubishiHeavyFanMask; + remote_state[9] |= newspeed; +} + +uint8_t IRMitsubishiHeavy152Ac::getFan(void) { + return remote_state[9] & kMitsubishiHeavyFanMask; +} + +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; + } + remote_state[5] &= ~kMitsubishiHeavyModeMask; + remote_state[5] |= newmode; +} + +uint8_t IRMitsubishiHeavy152Ac::getMode(void) { + return remote_state[5] & kMitsubishiHeavyModeMask; +} + +void IRMitsubishiHeavy152Ac::setSwingVertical(const uint8_t pos) { + uint8_t newpos = std::min(pos, kMitsubishiHeavy152SwingVOff); + remote_state[11] &= ~kMitsubishiHeavy152SwingVMask; + remote_state[11] |= (newpos << 5); +} + +uint8_t IRMitsubishiHeavy152Ac::getSwingVertical(void) { + return remote_state[11] >> 5; +} + +void IRMitsubishiHeavy152Ac::setSwingHorizontal(const uint8_t pos) { + uint8_t newpos = std::min(pos, kMitsubishiHeavy152SwingHOff); + remote_state[13] &= ~kMitsubishiHeavy152SwingHMask; + remote_state[13] |= (newpos & kMitsubishiHeavy152SwingHMask); +} + +uint8_t IRMitsubishiHeavy152Ac::getSwingHorizontal(void) { + return remote_state[13] & kMitsubishiHeavy152SwingHMask; +} + +void IRMitsubishiHeavy152Ac::setNight(const bool on) { + if (on) + remote_state[15] |= kMitsubishiHeavyNightBit; + else + remote_state[15] &= ~kMitsubishiHeavyNightBit; +} + +bool IRMitsubishiHeavy152Ac::getNight(void) { + return remote_state[15] & kMitsubishiHeavyNightBit; +} + +void IRMitsubishiHeavy152Ac::set3D(const bool on) { + if (on) + remote_state[11] |= kMitsubishiHeavy3DMask; + else + remote_state[11] &= ~kMitsubishiHeavy3DMask; +} + +bool IRMitsubishiHeavy152Ac::get3D(void) { + return (remote_state[11] & kMitsubishiHeavy3DMask) == kMitsubishiHeavy3DMask; +} + +void IRMitsubishiHeavy152Ac::setSilent(const bool on) { + if (on) + remote_state[15] |= kMitsubishiHeavySilentBit; + else + remote_state[15] &= ~kMitsubishiHeavySilentBit; +} + +bool IRMitsubishiHeavy152Ac::getSilent(void) { + return remote_state[15] & kMitsubishiHeavySilentBit; +} + +void IRMitsubishiHeavy152Ac::setFilter(const bool on) { + if (on) + remote_state[5] |= kMitsubishiHeavyFilterBit; + else + remote_state[5] &= ~kMitsubishiHeavyFilterBit; +} + +bool IRMitsubishiHeavy152Ac::getFilter(void) { + return remote_state[5] & kMitsubishiHeavyFilterBit; +} + +void IRMitsubishiHeavy152Ac::setClean(const bool on) { + this->setFilter(on); + if (on) + remote_state[5] |= kMitsubishiHeavyCleanBit; + else + remote_state[5] &= ~kMitsubishiHeavyCleanBit; +} + +bool IRMitsubishiHeavy152Ac::getClean(void) { + return remote_state[5] & kMitsubishiHeavyCleanBit && this->getFilter(); +} + +void IRMitsubishiHeavy152Ac::setTurbo(const bool on) { + if (on) + this->setFan(kMitsubishiHeavy152FanTurbo); + else if (this->getTurbo()) this->setFan(kMitsubishiHeavy152FanAuto); +} + +bool IRMitsubishiHeavy152Ac::getTurbo(void) { + return this->getFan() == kMitsubishiHeavy152FanTurbo; +} + +void IRMitsubishiHeavy152Ac::setEcono(const bool on) { + if (on) + this->setFan(kMitsubishiHeavy152FanEcono); + else if (this->getEcono()) this->setFan(kMitsubishiHeavy152FanAuto); +} + +bool IRMitsubishiHeavy152Ac::getEcono(void) { + return this->getFan() == kMitsubishiHeavy152FanEcono; +} + +// Verify the given state has a ZM-S signature. +bool IRMitsubishiHeavy152Ac::checkZmsSig(const uint8_t *state) { + for (uint8_t i = 0; i < kMitsubishiHeavySigLength; i++) + if (state[i] != kMitsubishiHeavyZmsSig[i]) return false; + return true; +} + +// Protocol technically has no checksum, but does has inverted byte pairs. +void IRMitsubishiHeavy152Ac::checksum(void) { + for (uint8_t i = kMitsubishiHeavySigLength - 2; + i < kMitsubishiHeavy152StateLength; + i += 2) { + remote_state[i + 1] = ~remote_state[i]; + } +} + +// Protocol technically has no checksum, but does has 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; + // Check all the byte pairs. + for (uint16_t i = kMitsubishiHeavySigLength - 2; + i < length; + i += 2) { + // XOR of a byte and it's self inverted should be 0xFF; + if ((state[i] ^ state[i + 1]) != 0xFF) return false; + } + return true; +} + + +// Convert a standard A/C mode into its native mode. +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 standard A/C Fan speed into its native fan speed. +uint8_t IRMitsubishiHeavy152Ac::convertFan(const stdAc::fanspeed_t speed) { + switch (speed) { + case stdAc::fanspeed_t::kMin: + return kMitsubishiHeavy152FanEcono; // Assumes Econo is slower than Low. + 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 standard A/C vertical swing into its native setting. +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 standard A/C horizontal swing into its native setting. +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 the internal state into a human readable string. +#ifdef ARDUINO +String IRMitsubishiHeavy152Ac::toString(void) { + String result = ""; +#else +std::string IRMitsubishiHeavy152Ac::toString(void) { + std::string result = ""; +#endif // ARDUINO + result += F("Power: "); + result += (this->getPower() ? F("On") : F("Off")); + result += F(", Mode: "); + result += uint64ToString(this->getMode()); + switch (this->getMode()) { + case kMitsubishiHeavyAuto: + result += F(" (Auto)"); + break; + case kMitsubishiHeavyCool: + result += F(" (Cool)"); + break; + case kMitsubishiHeavyHeat: + result += F(" (Heat)"); + break; + case kMitsubishiHeavyDry: + result += F(" (Dry)"); + break; + case kMitsubishiHeavyFan: + result += F(" (Fan)"); + break; + default: + result += F(" (UNKNOWN)"); + } + result += F(", Temp: "); + result += uint64ToString(this->getTemp()) + 'C'; + result += F(", Fan: "); + result += uint64ToString(this->getFan()); + switch (this->getFan()) { + case kMitsubishiHeavy152FanAuto: + result += F(" (Auto)"); + break; + case kMitsubishiHeavy152FanHigh: + result += F(" (High)"); + break; + case kMitsubishiHeavy152FanLow: + result += F(" (Low)"); + break; + case kMitsubishiHeavy152FanMed: + result += F(" (Med)"); + break; + case kMitsubishiHeavy152FanMax: + result += F(" (Max)"); + break; + case kMitsubishiHeavy152FanEcono: + result += F(" (Econo)"); + break; + case kMitsubishiHeavy152FanTurbo: + result += F(" (Turbo)"); + break; + default: + result += F(" (UNKNOWN)"); + } + result += F(", Swing (V): "); + result += uint64ToString(this->getSwingVertical()); + switch (this->getSwingVertical()) { + case kMitsubishiHeavy152SwingVAuto: + result += F(" (Auto)"); + break; + case kMitsubishiHeavy152SwingVHighest: + result += F(" (Highest)"); + break; + case kMitsubishiHeavy152SwingVHigh: + result += F(" (High)"); + break; + case kMitsubishiHeavy152SwingVMiddle: + result += F(" (Middle)"); + break; + case kMitsubishiHeavy152SwingVLow: + result += F(" (Low)"); + break; + case kMitsubishiHeavy152SwingVLowest: + result += F(" (Lowest)"); + break; + case kMitsubishiHeavy152SwingVOff: + result += F(" (Off)"); + break; + default: + result += F(" (UNKNOWN)"); + } + result += F(", Swing (H): "); + result += uint64ToString(this->getSwingHorizontal()); + switch (this->getSwingHorizontal()) { + case kMitsubishiHeavy152SwingHAuto: + result += F(" (Auto)"); + break; + case kMitsubishiHeavy152SwingHLeftMax: + result += F(" (Max Left)"); + break; + case kMitsubishiHeavy152SwingHLeft: + result += F(" (Left)"); + break; + case kMitsubishiHeavy152SwingHMiddle: + result += F(" (Middle)"); + break; + case kMitsubishiHeavy152SwingHRight: + result += F(" (Right)"); + break; + case kMitsubishiHeavy152SwingHRightMax: + result += F(" (Max Right)"); + break; + case kMitsubishiHeavy152SwingHLeftRight: + result += F(" (Left Right)"); + break; + case kMitsubishiHeavy152SwingHRightLeft: + result += F(" (Right Left)"); + break; + case kMitsubishiHeavy152SwingHOff: + result += F(" (Off)"); + break; + default: + result += F(" (UNKNOWN)"); + } + result += F(", Silent: "); + result += (this->getSilent() ? F("On") : F("Off")); + result += F(", Turbo: "); + result += (this->getTurbo() ? F("On") : F("Off")); + result += F(", Econo: "); + result += (this->getEcono() ? F("On") : F("Off")); + result += F(", Night: "); + result += (this->getNight() ? F("On") : F("Off")); + result += F(", Filter: "); + result += (this->getFilter() ? F("On") : F("Off")); + result += F(", 3D: "); + result += (this->get3D() ? F("On") : F("Off")); + result += F(", Clean: "); + result += (this->getClean() ? F("On") : F("Off")); + return result; +} + + +// Class for decoding and constructing MitsubishiHeavy88 AC messages. +IRMitsubishiHeavy88Ac::IRMitsubishiHeavy88Ac( + const uint16_t pin) : _irsend(pin) { stateReset(); } + +void IRMitsubishiHeavy88Ac::begin() { _irsend.begin(); } + +#if SEND_MITSUBISHIHEAVY +void IRMitsubishiHeavy88Ac::send(const uint16_t repeat) { + _irsend.sendMitsubishiHeavy88(this->getRaw(), kMitsubishiHeavy88StateLength, + repeat); +} +#endif // SEND_MITSUBISHIHEAVY + +void IRMitsubishiHeavy88Ac::stateReset(void) { + uint8_t i = 0; + for (; i < kMitsubishiHeavySigLength; i++) + remote_state[i] = kMitsubishiHeavyZjsSig[i]; + for (; i < kMitsubishiHeavy88StateLength; i++) remote_state[i] = 0; +} + +uint8_t *IRMitsubishiHeavy88Ac::getRaw(void) { + checksum(); + return remote_state; +} + +void IRMitsubishiHeavy88Ac::setRaw(const uint8_t *data) { + for (uint8_t i = 0; i < kMitsubishiHeavy88StateLength; i++) + remote_state[i] = data[i]; +} + +void IRMitsubishiHeavy88Ac::on(void) { + remote_state[9] |= kMitsubishiHeavyPowerBit; +} + +void IRMitsubishiHeavy88Ac::off(void) { + remote_state[9] &= ~kMitsubishiHeavyPowerBit; +} + +void IRMitsubishiHeavy88Ac::setPower(const bool on) { + if (on) + this->on(); + else + this->off(); +} + +bool IRMitsubishiHeavy88Ac::getPower(void) { + return remote_state[9] & kMitsubishiHeavyPowerBit; +} + +void IRMitsubishiHeavy88Ac::setTemp(const uint8_t temp) { + uint8_t newtemp = temp; + newtemp = std::min(newtemp, kMitsubishiHeavyMaxTemp); + newtemp = std::max(newtemp, kMitsubishiHeavyMinTemp); + + remote_state[9] &= kMitsubishiHeavyTempMask; + remote_state[9] |= ((newtemp - kMitsubishiHeavyMinTemp) << 4); +} + +uint8_t IRMitsubishiHeavy88Ac::getTemp(void) { + return (remote_state[9] >> 4) + kMitsubishiHeavyMinTemp; +} + +// Set the speed of the fan +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; + } + remote_state[7] &= ~kMitsubishiHeavy88FanMask; + remote_state[7] |= (newspeed << 5); +} + +uint8_t IRMitsubishiHeavy88Ac::getFan(void) { + return remote_state[7] >> 5; +} + +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; + } + remote_state[9] &= ~kMitsubishiHeavyModeMask; + remote_state[9] |= newmode; +} + +uint8_t IRMitsubishiHeavy88Ac::getMode(void) { + return remote_state[9] & kMitsubishiHeavyModeMask; +} + +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; + } + remote_state[5] &= ~kMitsubishiHeavy88SwingVMaskByte5; + remote_state[5] |= (newpos & kMitsubishiHeavy88SwingVMaskByte5); + remote_state[7] &= ~kMitsubishiHeavy88SwingVMaskByte7; + remote_state[7] |= (newpos & kMitsubishiHeavy88SwingVMaskByte7); +} + +uint8_t IRMitsubishiHeavy88Ac::getSwingVertical(void) { + return (remote_state[5] & kMitsubishiHeavy88SwingVMaskByte5) | + (remote_state[7] & kMitsubishiHeavy88SwingVMaskByte7); +} + +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; + } + remote_state[5] &= ~kMitsubishiHeavy88SwingHMask; + remote_state[5] |= newpos; +} + +uint8_t IRMitsubishiHeavy88Ac::getSwingHorizontal(void) { + return remote_state[5] & kMitsubishiHeavy88SwingHMask; +} + +void IRMitsubishiHeavy88Ac::setTurbo(const bool on) { + if (on) + this->setFan(kMitsubishiHeavy88FanTurbo); + else if (this->getTurbo()) this->setFan(kMitsubishiHeavy88FanAuto); +} + +bool IRMitsubishiHeavy88Ac::getTurbo(void) { + return this->getFan() == kMitsubishiHeavy88FanTurbo; +} + +void IRMitsubishiHeavy88Ac::setEcono(const bool on) { + if (on) + this->setFan(kMitsubishiHeavy88FanEcono); + else if (this->getEcono()) this->setFan(kMitsubishiHeavy88FanAuto); +} + +bool IRMitsubishiHeavy88Ac::getEcono(void) { + return this->getFan() == kMitsubishiHeavy88FanEcono; +} + +void IRMitsubishiHeavy88Ac::set3D(const bool on) { + if (on) + this->setSwingHorizontal(kMitsubishiHeavy88SwingH3D); + else if (this->get3D()) + this->setSwingHorizontal(kMitsubishiHeavy88SwingHOff); +} + +bool IRMitsubishiHeavy88Ac::get3D(void) { + return this->getSwingHorizontal() == kMitsubishiHeavy88SwingH3D; +} + +void IRMitsubishiHeavy88Ac::setClean(const bool on) { + if (on) + remote_state[5] |= kMitsubishiHeavy88CleanBit; + else + remote_state[5] &= ~kMitsubishiHeavy88CleanBit; +} + +bool IRMitsubishiHeavy88Ac::getClean(void) { + return remote_state[5] & kMitsubishiHeavy88CleanBit; +} + +// Verify the given state has a ZJ-S signature. +bool IRMitsubishiHeavy88Ac::checkZjsSig(const uint8_t *state) { + for (uint8_t i = 0; i < kMitsubishiHeavySigLength; i++) + if (state[i] != kMitsubishiHeavyZjsSig[i]) return false; + return true; +} + +// Protocol technically has no checksum, but does has inverted byte pairs. +void IRMitsubishiHeavy88Ac::checksum(void) { + for (uint8_t i = kMitsubishiHeavySigLength - 2; + i < kMitsubishiHeavy88StateLength; + i += 2) { + remote_state[i + 1] = ~remote_state[i]; + } +} + +// Protocol technically has no checksum, but does has inverted byte pairs. +bool IRMitsubishiHeavy88Ac::validChecksum(const uint8_t *state, + const uint16_t length) { + return IRMitsubishiHeavy152Ac::validChecksum(state, length); +} + +// Convert a standard A/C mode into its native mode. +uint8_t IRMitsubishiHeavy88Ac::convertMode(const stdAc::opmode_t mode) { + return IRMitsubishiHeavy152Ac::convertMode(mode); +} + + +// Convert a standard A/C Fan speed into its native fan speed. +uint8_t IRMitsubishiHeavy88Ac::convertFan(const stdAc::fanspeed_t speed) { + switch (speed) { + case stdAc::fanspeed_t::kMin: + return kMitsubishiHeavy88FanEcono; // Assumes Econo is slower than Low. + 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 standard A/C vertical swing into its native setting. +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 standard A/C horizontal swing into its native setting. +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 the internal state into a human readable string. +#ifdef ARDUINO +String IRMitsubishiHeavy88Ac::toString(void) { + String result = ""; +#else +std::string IRMitsubishiHeavy88Ac::toString(void) { + std::string result = ""; +#endif // ARDUINO + result += F("Power: "); + result += (this->getPower() ? F("On") : F("Off")); + result += F(", Mode: "); + result += uint64ToString(this->getMode()); + switch (this->getMode()) { + case kMitsubishiHeavyAuto: + result += F(" (Auto)"); + break; + case kMitsubishiHeavyCool: + result += F(" (Cool)"); + break; + case kMitsubishiHeavyHeat: + result += F(" (Heat)"); + break; + case kMitsubishiHeavyDry: + result += F(" (Dry)"); + break; + case kMitsubishiHeavyFan: + result += F(" (Fan)"); + break; + default: + result += F(" (UNKNOWN)"); + } + result += F(", Temp: "); + result += uint64ToString(this->getTemp()) + 'C'; + result += F(", Fan: "); + result += uint64ToString(this->getFan()); + switch (this->getFan()) { + case kMitsubishiHeavy88FanAuto: + result += F(" (Auto)"); + break; + case kMitsubishiHeavy88FanHigh: + result += F(" (High)"); + break; + case kMitsubishiHeavy88FanLow: + result += F(" (Low)"); + break; + case kMitsubishiHeavy88FanMed: + result += F(" (Med)"); + break; + case kMitsubishiHeavy88FanEcono: + result += F(" (Econo)"); + break; + case kMitsubishiHeavy88FanTurbo: + result += F(" (Turbo)"); + break; + default: + result += F(" (UNKNOWN)"); + } + result += F(", Swing (V): "); + result += uint64ToString(this->getSwingVertical()); + switch (this->getSwingVertical()) { + case kMitsubishiHeavy88SwingVAuto: + result += F(" (Auto)"); + break; + case kMitsubishiHeavy88SwingVHighest: + result += F(" (Highest)"); + break; + case kMitsubishiHeavy88SwingVHigh: + result += F(" (High)"); + break; + case kMitsubishiHeavy88SwingVMiddle: + result += F(" (Middle)"); + break; + case kMitsubishiHeavy88SwingVLow: + result += F(" (Low)"); + break; + case kMitsubishiHeavy88SwingVLowest: + result += F(" (Lowest)"); + break; + case kMitsubishiHeavy88SwingVOff: + result += F(" (Off)"); + break; + default: + result += F(" (UNKNOWN)"); + } + result += F(", Swing (H): "); + result += uint64ToString(this->getSwingHorizontal()); + switch (this->getSwingHorizontal()) { + case kMitsubishiHeavy88SwingHAuto: + result += F(" (Auto)"); + break; + case kMitsubishiHeavy88SwingHLeftMax: + result += F(" (Max Left)"); + break; + case kMitsubishiHeavy88SwingHLeft: + result += F(" (Left)"); + break; + case kMitsubishiHeavy88SwingHMiddle: + result += F(" (Middle)"); + break; + case kMitsubishiHeavy88SwingHRight: + result += F(" (Right)"); + break; + case kMitsubishiHeavy88SwingHRightMax: + result += F(" (Max Right)"); + break; + case kMitsubishiHeavy88SwingHLeftRight: + result += F(" (Left Right)"); + break; + case kMitsubishiHeavy88SwingHRightLeft: + result += F(" (Right Left)"); + break; + case kMitsubishiHeavy88SwingH3D: + result += F(" (3D)"); + break; + case kMitsubishiHeavy88SwingHOff: + result += F(" (Off)"); + break; + default: + result += F(" (UNKNOWN)"); + } + result += F(", Turbo: "); + result += (this->getTurbo() ? F("On") : F("Off")); + result += F(", Econo: "); + result += (this->getEcono() ? F("On") : F("Off")); + result += F(", 3D: "); + result += (this->get3D() ? F("On") : F("Off")); + result += F(", Clean: "); + result += (this->getClean() ? F("On") : F("Off")); + return result; +} + +#if DECODE_MITSUBISHIHEAVY +// Decode the supplied MitsubishiHeavy message. +// +// Args: +// results: Ptr to the data to decode and where to store the decode result. +// nbits: The number of data bits to expect. +// Typically kMitsubishiHeavy88Bits or kMitsubishiHeavy152Bits. +// strict: Flag indicating if we should perform strict matching. +// Returns: +// boolean: True if it can decode it, false if it can't. +// +// Status: BETA / Appears to be working. Needs testing against a real device. +bool IRrecv::decodeMitsubishiHeavy(decode_results* results, + const uint16_t nbits, const bool strict) { + // Check if can possibly be a valid MitsubishiHeavy message. + if (results->rawlen < 2 * nbits + kHeader + kFooter - 1) return false; + if (strict) { + switch (nbits) { + case kMitsubishiHeavy88Bits: + case kMitsubishiHeavy152Bits: + break; + default: + return false; // Not what is expected + } + } + + uint16_t actualBits = 0; + uint16_t offset = kStartOffset; + match_result_t data_result; + + // Header + if (!matchMark(results->rawbuf[offset++], kMitsubishiHeavyHdrMark)) + return false; + if (!matchSpace(results->rawbuf[offset++], kMitsubishiHeavyHdrSpace)) + return false; + // Data + // Keep reading bytes until we either run out of section or state to fill. + for (uint16_t i = 0; + offset <= results->rawlen - 16 && actualBits < nbits; + i++, actualBits += 8, offset += data_result.used) { + data_result = matchData(&(results->rawbuf[offset]), 8, + kMitsubishiHeavyBitMark, kMitsubishiHeavyOneSpace, + kMitsubishiHeavyBitMark, kMitsubishiHeavyZeroSpace, + kTolerance, 0, false); + if (data_result.success == false) { + DPRINT("DEBUG: offset = "); + DPRINTLN(offset + data_result.used); + return false; // Fail + } + results->state[i] = data_result.data; + } + // Footer. + if (!matchMark(results->rawbuf[offset++], kMitsubishiHeavyBitMark)) + return false; + if (offset < results->rawlen && + !matchAtLeast(results->rawbuf[offset], kMitsubishiHeavyGap)) return false; + + // Compliance + if (actualBits < nbits) return false; + if (strict && actualBits != nbits) return false; // Not as we expected. + switch (actualBits) { + 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 = actualBits; + // 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-2.6.0/src/ir_MitsubishiHeavy.h b/lib/IRremoteESP8266-2.6.0/src/ir_MitsubishiHeavy.h new file mode 100644 index 000000000000..bcd85c6e015d --- /dev/null +++ b/lib/IRremoteESP8266-2.6.0/src/ir_MitsubishiHeavy.h @@ -0,0 +1,264 @@ +// Copyright 2019 David Conran + +#ifndef IR_MITSUBISHIHEAVY_H_ +#define IR_MITSUBISHIHEAVY_H_ + +#ifndef UNIT_TEST +#include +#else +#include +#endif +#include "IRremoteESP8266.h" +#include "IRsend.h" +#ifdef UNIT_TEST +#include "IRsend_test.h" +#endif + +// Ref: +// https://github.com/markszabo/IRremoteESP8266/issues/660 +// https://github.com/ToniA/Raw-IR-decoder-for-Arduino/blob/master/MitsubishiHeavy.cpp +// https://github.com/ToniA/arduino-heatpumpir/blob/master/MitsubishiHeavyHeatpumpIR.cpp + +// Constants. +const uint8_t kMitsubishiHeavySigLength = 5; + + +// ZMS (152 bit) +const uint8_t kMitsubishiHeavyZmsSig[kMitsubishiHeavySigLength] = { + 0xAD, 0x51, 0x3C, 0xE5, 0x1A}; +// Byte[5] +const uint8_t kMitsubishiHeavyFilterBit = 0b01000000; +const uint8_t kMitsubishiHeavyCleanBit = 0b00100000; +const uint8_t kMitsubishiHeavyPowerBit = 0b00001000; // Byte 9 on ZJS +const uint8_t kMitsubishiHeavyModeMask = 0b00000111; // Byte 9 on ZJS +const uint8_t kMitsubishiHeavyAuto = 0; // 0b000 +const uint8_t kMitsubishiHeavyCool = 1; // 0b001 +const uint8_t kMitsubishiHeavyDry = 2; // 0b010 +const uint8_t kMitsubishiHeavyFan = 3; // 0b011 +const uint8_t kMitsubishiHeavyHeat = 4; // 0b100 +// Byte[7] +const uint8_t kMitsubishiHeavyTempMask = 0b00001111; +const uint8_t kMitsubishiHeavyMinTemp = 17; // 17C +const uint8_t kMitsubishiHeavyMaxTemp = 31; // 31C +// Byte[9] +const uint8_t kMitsubishiHeavyFanMask = 0b00001111; // ~Byte 7 on ZJS. +const uint8_t kMitsubishiHeavy152FanAuto = 0x0; // 0b0000 +const uint8_t kMitsubishiHeavy152FanLow = 0x1; // 0b0001 +const uint8_t kMitsubishiHeavy152FanMed = 0x2; // 0b0010 +const uint8_t kMitsubishiHeavy152FanHigh = 0x3; // 0b0011 +const uint8_t kMitsubishiHeavy152FanMax = 0x4; // 0b0100 +const uint8_t kMitsubishiHeavy152FanEcono = 0x6; // 0b0110 +const uint8_t kMitsubishiHeavy152FanTurbo = 0x8; // 0b1000 +// Byte[11] +const uint8_t kMitsubishiHeavy3DMask = 0b00010010; +const uint8_t kMitsubishiHeavy152SwingVMask = 0b11100000; +const uint8_t kMitsubishiHeavy152SwingVAuto = 0; // 0b000 +const uint8_t kMitsubishiHeavy152SwingVHighest = 1; // 0b001 +const uint8_t kMitsubishiHeavy152SwingVHigh = 2; // 0b010 +const uint8_t kMitsubishiHeavy152SwingVMiddle = 3; // 0b011 +const uint8_t kMitsubishiHeavy152SwingVLow = 4; // 0b100 +const uint8_t kMitsubishiHeavy152SwingVLowest = 5; // 0b101 +const uint8_t kMitsubishiHeavy152SwingVOff = 6; // 0b110 +// Byte[13] +const uint8_t kMitsubishiHeavy152SwingHMask = 0b00001111; +const uint8_t kMitsubishiHeavy152SwingHAuto = 0; // 0b0000 +const uint8_t kMitsubishiHeavy152SwingHLeftMax = 1; // 0b0001 +const uint8_t kMitsubishiHeavy152SwingHLeft = 2; // 0b0010 +const uint8_t kMitsubishiHeavy152SwingHMiddle = 3; // 0b0011 +const uint8_t kMitsubishiHeavy152SwingHRight = 4; // 0b0100 +const uint8_t kMitsubishiHeavy152SwingHRightMax = 5; // 0b0101 +const uint8_t kMitsubishiHeavy152SwingHRightLeft = 6; // 0b0110 +const uint8_t kMitsubishiHeavy152SwingHLeftRight = 7; // 0b0111 +const uint8_t kMitsubishiHeavy152SwingHOff = 8; // 0b1000 +// Byte[15] +const uint8_t kMitsubishiHeavyNightBit = 0b01000000; +const uint8_t kMitsubishiHeavySilentBit = 0b10000000; + + +// ZJS (88 bit) +const uint8_t kMitsubishiHeavyZjsSig[kMitsubishiHeavySigLength] = { + 0xAD, 0x51, 0x3C, 0xD9, 0x26}; +// Byte [5] +const uint8_t kMitsubishiHeavy88CleanBit = 0b00100000; +const uint8_t kMitsubishiHeavy88SwingHMask = 0b11001100; +const uint8_t kMitsubishiHeavy88SwingHAuto = 0x80; // 0b10000000 +const uint8_t kMitsubishiHeavy88SwingHLeftMax = 0x04; // 0b00000100 +const uint8_t kMitsubishiHeavy88SwingHLeft = 0x44; // 0b01000100 +const uint8_t kMitsubishiHeavy88SwingHMiddle = 0x84; // 0b10000100 +const uint8_t kMitsubishiHeavy88SwingHRight = 0xC4; // 0b11000100 +const uint8_t kMitsubishiHeavy88SwingHRightMax = 0x08; // 0b00001000 +const uint8_t kMitsubishiHeavy88SwingHRightLeft = 0x88; // 0b10001000 +const uint8_t kMitsubishiHeavy88SwingHLeftRight = 0x48; // 0b01001000 +const uint8_t kMitsubishiHeavy88SwingHOff = 0x00; // 0b00000000 +const uint8_t kMitsubishiHeavy88SwingH3D = 0xC8; // 0b11001000 +// Byte[7] +const uint8_t kMitsubishiHeavy88FanMask = 0b11100000; +const uint8_t kMitsubishiHeavy88FanAuto = 0; // 0b000 +const uint8_t kMitsubishiHeavy88FanLow = 2; // 0b010 +const uint8_t kMitsubishiHeavy88FanMed = 3; // 0b011 +const uint8_t kMitsubishiHeavy88FanHigh = 4; // 0b100 +const uint8_t kMitsubishiHeavy88FanTurbo = 6; // 0b110 +const uint8_t kMitsubishiHeavy88FanEcono = 7; // 0b111 +const uint8_t kMitsubishiHeavy88SwingVMaskByte5 = 0b00000010; +const uint8_t kMitsubishiHeavy88SwingVMaskByte7 = 0b00011000; +const uint8_t kMitsubishiHeavy88SwingVMask = + kMitsubishiHeavy88SwingVMaskByte5 | kMitsubishiHeavy88SwingVMaskByte7; + // i.e. 0b00011010 +const uint8_t kMitsubishiHeavy88SwingVAuto = 0b00010000; // 0x10 +const uint8_t kMitsubishiHeavy88SwingVHighest = 0b00011000; // 0x18 +const uint8_t kMitsubishiHeavy88SwingVHigh = 0b00000010; // 0x02 +const uint8_t kMitsubishiHeavy88SwingVMiddle = 0b00001010; // 0x0A +const uint8_t kMitsubishiHeavy88SwingVLow = 0b00010010; // 0x12 +const uint8_t kMitsubishiHeavy88SwingVLowest = 0b00011010; // 0x1A +const uint8_t kMitsubishiHeavy88SwingVOff = 0b00000000; // 0x00 +// Byte[9] is Power & Mode & Temp. + + +// Classes +class IRMitsubishiHeavy152Ac { + public: + explicit IRMitsubishiHeavy152Ac(const uint16_t pin); + + void stateReset(void); +#if SEND_MITSUBISHIHEAVY + void send(const uint16_t repeat = kMitsubishiHeavy152MinRepeat); +#endif // SEND_MITSUBISHIHEAVY + void begin(void); + void on(void); + void off(void); + + void setPower(const bool on); + bool getPower(void); + + void setTemp(const uint8_t temp); + uint8_t getTemp(void); + + void setFan(const uint8_t fan); + uint8_t getFan(void); + + void setMode(const uint8_t mode); + uint8_t getMode(void); + + void setSwingVertical(const uint8_t pos); + uint8_t getSwingVertical(void); + void setSwingHorizontal(const uint8_t pos); + uint8_t getSwingHorizontal(void); + + void setNight(const bool on); + bool getNight(void); + + void set3D(const bool on); + bool get3D(void); + + void setSilent(const bool on); + bool getSilent(void); + + void setFilter(const bool on); + bool getFilter(void); + + void setClean(const bool on); + bool getClean(void); + + void setTurbo(const bool on); + bool getTurbo(void); + + void setEcono(const bool on); + bool getEcono(void); + + uint8_t* getRaw(void); + void setRaw(const uint8_t* data); + + static bool checkZmsSig(const uint8_t *state); + static bool validChecksum( + const uint8_t *state, + const uint16_t length = kMitsubishiHeavy152StateLength); + static uint8_t convertMode(const stdAc::opmode_t mode); + static uint8_t convertFan(const stdAc::fanspeed_t speed); + static uint8_t convertSwingV(const stdAc::swingv_t position); + static uint8_t convertSwingH(const stdAc::swingh_t position); +#ifdef ARDUINO + String toString(void); +#else // ARDUINO + std::string toString(void); +#endif // ARDUINO +#ifndef UNIT_TEST + + private: + IRsend _irsend; +#else // UNIT_TEST + IRsendTest _irsend; +#endif // UNIT_TEST + // The state of the IR remote in IR code form. + uint8_t remote_state[kMitsubishiHeavy152StateLength]; + void checksum(); +}; + +class IRMitsubishiHeavy88Ac { + public: + explicit IRMitsubishiHeavy88Ac(const uint16_t pin); + + void stateReset(void); +#if SEND_MITSUBISHIHEAVY + void send(const uint16_t repeat = kMitsubishiHeavy88MinRepeat); +#endif // SEND_MITSUBISHIHEAVY + void begin(void); + void on(void); + void off(void); + + void setPower(const bool on); + bool getPower(void); + + void setTemp(const uint8_t temp); + uint8_t getTemp(void); + + void setFan(const uint8_t fan); + uint8_t getFan(void); + + void setMode(const uint8_t mode); + uint8_t getMode(void); + + void setSwingVertical(const uint8_t pos); + uint8_t getSwingVertical(void); + void setSwingHorizontal(const uint8_t pos); + uint8_t getSwingHorizontal(void); + + void setTurbo(const bool on); + bool getTurbo(void); + + void setEcono(const bool on); + bool getEcono(void); + + void set3D(const bool on); + bool get3D(void); + + void setClean(const bool on); + bool getClean(void); + + uint8_t* getRaw(void); + void setRaw(const uint8_t* data); + + static bool checkZjsSig(const uint8_t *state); + static bool validChecksum( + const uint8_t *state, + const uint16_t length = kMitsubishiHeavy88StateLength); + static uint8_t convertMode(const stdAc::opmode_t mode); + static uint8_t convertFan(const stdAc::fanspeed_t speed); + static uint8_t convertSwingV(const stdAc::swingv_t position); + static uint8_t convertSwingH(const stdAc::swingh_t position); +#ifdef ARDUINO + String toString(void); +#else // ARDUINO + std::string toString(void); +#endif // ARDUINO +#ifndef UNIT_TEST + + private: + IRsend _irsend; +#else // UNIT_TEST + IRsendTest _irsend; +#endif // UNIT_TEST + // The state of the IR remote in IR code form. + uint8_t remote_state[kMitsubishiHeavy152StateLength]; + void checksum(); +}; +#endif // IR_MITSUBISHIHEAVY_H_ diff --git a/lib/IRremoteESP8266-2.5.2.03/src/ir_NEC.cpp b/lib/IRremoteESP8266-2.6.0/src/ir_NEC.cpp similarity index 100% rename from lib/IRremoteESP8266-2.5.2.03/src/ir_NEC.cpp rename to lib/IRremoteESP8266-2.6.0/src/ir_NEC.cpp diff --git a/lib/IRremoteESP8266-2.5.2.03/src/ir_NEC.h b/lib/IRremoteESP8266-2.6.0/src/ir_NEC.h similarity index 100% rename from lib/IRremoteESP8266-2.5.2.03/src/ir_NEC.h rename to lib/IRremoteESP8266-2.6.0/src/ir_NEC.h diff --git a/lib/IRremoteESP8266-2.5.2.03/src/ir_Nikai.cpp b/lib/IRremoteESP8266-2.6.0/src/ir_Nikai.cpp similarity index 100% rename from lib/IRremoteESP8266-2.5.2.03/src/ir_Nikai.cpp rename to lib/IRremoteESP8266-2.6.0/src/ir_Nikai.cpp diff --git a/lib/IRremoteESP8266-2.5.2.03/src/ir_Panasonic.cpp b/lib/IRremoteESP8266-2.6.0/src/ir_Panasonic.cpp similarity index 83% rename from lib/IRremoteESP8266-2.5.2.03/src/ir_Panasonic.cpp rename to lib/IRremoteESP8266-2.6.0/src/ir_Panasonic.cpp index e79b136a5430..47aa51c96dbb 100644 --- a/lib/IRremoteESP8266-2.5.2.03/src/ir_Panasonic.cpp +++ b/lib/IRremoteESP8266-2.6.0/src/ir_Panasonic.cpp @@ -27,8 +27,8 @@ // Code by crankyoldgit // Panasonic A/C models supported: // A/C Series/models: -// JKE, LKE, DKE, CKP, & NKE series. (In theory) -// CS-YW9MKD (confirmed) +// JKE, LKE, DKE, CKP, RKR, & NKE series. (In theory) +// CS-YW9MKD, CS-Z9RKR (confirmed) // CS-ME14CKPG / CS-ME12CKPG / CS-ME10CKPG // A/C Remotes: // A75C3747 (confirmed) @@ -63,7 +63,7 @@ const uint32_t kPanasonicMinGap = kPanasonicMinGapTicks * kPanasonicTick; const uint16_t kPanasonicAcSectionGap = 10000; const uint16_t kPanasonicAcSection1Length = 8; -const uint32_t kPanasonicAcMessageGap = 100000; // A complete guess. +const uint32_t kPanasonicAcMessageGap = kDefaultMessageGap; // Just a guess. #if (SEND_PANASONIC || SEND_DENON) // Send a Panasonic formatted message. @@ -211,7 +211,7 @@ bool IRrecv::decodePanasonic(decode_results *results, uint16_t nbits, //: // Panasonic A/C models supported: // A/C Series/models: -// JKE, LKE, DKE, & NKE series. +// JKE, LKE, DKE, CKP, RKR, & NKE series. // CS-YW9MKD // A/C Remotes: // A75C3747 @@ -268,9 +268,9 @@ void IRPanasonicAc::fixChecksum(const uint16_t length) { } #if SEND_PANASONIC_AC -void IRPanasonicAc::send() { +void IRPanasonicAc::send(const uint16_t repeat) { fixChecksum(); - _irsend.sendPanasonicAC(remote_state); + _irsend.sendPanasonicAC(remote_state, kPanasonicAcStateLength, repeat); } #endif // SEND_PANASONIC_AC @@ -281,6 +281,7 @@ void IRPanasonicAc::setModel(const panasonic_ac_remote_model_t model) { case kPanasonicLke: case kPanasonicNke: case kPanasonicCkp: + case kPanasonicRkr: break; default: // Only proceed if we know what to do. return; @@ -311,12 +312,17 @@ void IRPanasonicAc::setModel(const panasonic_ac_remote_model_t model) { case kPanasonicCkp: remote_state[21] |= 0x10; remote_state[23] = 0x01; + break; + case kPanasonicRkr: + remote_state[13] |= 0x08; + remote_state[23] = 0x89; default: break; } } panasonic_ac_remote_model_t IRPanasonicAc::getModel() { + if (remote_state[23] == 0x89) return kPanasonicRkr; if (remote_state[17] == 0x00) { if ((remote_state[21] & 0x10) && (remote_state[23] & 0x01)) return kPanasonicCkp; @@ -438,6 +444,7 @@ void IRPanasonicAc::setSwingHorizontal(const uint8_t desired_direction) { uint8_t direction = desired_direction; switch (getModel()) { case kPanasonicDke: + case kPanasonicRkr: break; case kPanasonicNke: case kPanasonicLke: @@ -460,18 +467,25 @@ uint8_t IRPanasonicAc::getFan() { } bool IRPanasonicAc::getQuiet() { - if (getModel() == kPanasonicCkp) - return remote_state[21] & kPanasonicAcQuietCkp; - else - return remote_state[21] & kPanasonicAcQuiet; + switch (getModel()) { + case kPanasonicRkr: + case kPanasonicCkp: + return remote_state[21] & kPanasonicAcQuietCkp; + default: + return remote_state[21] & kPanasonicAcQuiet; + } } void IRPanasonicAc::setQuiet(const bool state) { uint8_t quiet; - if (getModel() == kPanasonicCkp) - quiet = kPanasonicAcQuietCkp; - else - quiet = kPanasonicAcQuiet; + switch (getModel()) { + case kPanasonicRkr: + case kPanasonicCkp: + quiet = kPanasonicAcQuietCkp; + break; + default: + quiet = kPanasonicAcQuiet; + } if (state) { setPowerful(false); // Powerful is mutually exclusive. @@ -482,18 +496,25 @@ void IRPanasonicAc::setQuiet(const bool state) { } bool IRPanasonicAc::getPowerful() { - if (getModel() == kPanasonicCkp) - return remote_state[21] & kPanasonicAcPowerfulCkp; - else - return remote_state[21] & kPanasonicAcPowerful; + switch (getModel()) { + case kPanasonicRkr: + case kPanasonicCkp: + return remote_state[21] & kPanasonicAcPowerfulCkp; + default: + return remote_state[21] & kPanasonicAcPowerful; + } } void IRPanasonicAc::setPowerful(const bool state) { uint8_t powerful; - if (getModel() == kPanasonicCkp) - powerful = kPanasonicAcPowerfulCkp; - else - powerful = kPanasonicAcPowerful; + switch (getModel()) { + case kPanasonicRkr: + case kPanasonicCkp: + powerful = kPanasonicAcPowerfulCkp; + break; + default: + powerful = kPanasonicAcPowerful; + } if (state) { setQuiet(false); // Quiet is mutually exclusive. @@ -591,12 +612,79 @@ String IRPanasonicAc::timeToString(const uint16_t mins_since_midnight) { std::string IRPanasonicAc::timeToString(const uint16_t mins_since_midnight) { std::string result = ""; #endif // ARDUINO - result += uint64ToString(mins_since_midnight / 60) + ":"; + result += uint64ToString(mins_since_midnight / 60) + ':'; uint8_t mins = mins_since_midnight % 60; - if (mins < 10) result += "0"; // Zero pad the minutes. + if (mins < 10) result += '0'; // Zero pad the minutes. return result + uint64ToString(mins); } +// Convert a standard A/C mode into its native mode. +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 standard A/C Fan speed into its native fan speed. +uint8_t IRPanasonicAc::convertFan(const stdAc::fanspeed_t speed) { + switch (speed) { + case stdAc::fanspeed_t::kMin: + return kPanasonicAcFanMin; + case stdAc::fanspeed_t::kLow: + return kPanasonicAcFanMin + 1; + case stdAc::fanspeed_t::kMedium: + return kPanasonicAcFanMin + 2; + case stdAc::fanspeed_t::kHigh: + return kPanasonicAcFanMin + 3; + case stdAc::fanspeed_t::kMax: + return kPanasonicAcFanMax; + default: + return kPanasonicAcFanAuto; + } +} + +// Convert a standard A/C vertical swing into its native setting. +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: + return kPanasonicAcSwingVUp; + case stdAc::swingv_t::kLow: + case stdAc::swingv_t::kLowest: + return kPanasonicAcSwingVDown; + default: + return kPanasonicAcSwingVAuto; + } +} + +// Convert a standard A/C horizontal swing into its native setting. +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 the internal state into a human readable string. #ifdef ARDUINO String IRPanasonicAc::toString() { @@ -605,84 +693,92 @@ String IRPanasonicAc::toString() { std::string IRPanasonicAc::toString() { std::string result = ""; #endif // ARDUINO - result += "Model: " + uint64ToString(getModel()); + result += F("Model: "); + result += uint64ToString(getModel()); switch (getModel()) { case kPanasonicDke: - result += " (DKE)"; + result += F(" (DKE)"); break; case kPanasonicJke: - result += " (JKE)"; + result += F(" (JKE)"); break; case kPanasonicNke: - result += " (NKE)"; + result += F(" (NKE)"); break; case kPanasonicLke: - result += " (LKE)"; + result += F(" (LKE)"); break; case kPanasonicCkp: - result += " (CKP)"; + result += F(" (CKP)"); + break; + case kPanasonicRkr: + result += F(" (RKR)"); break; default: - result += " (UNKNOWN)"; + result += F(" (UNKNOWN)"); } - result += ", Power: "; + result += F(", Power: "); if (getPower()) - result += "On"; + result += F("On"); else - result += "Off"; - result += ", Mode: " + uint64ToString(getMode()); + result += F("Off"); + result += F(", Mode: "); + result += uint64ToString(getMode()); switch (getMode()) { case kPanasonicAcAuto: - result += " (AUTO)"; + result += F(" (AUTO)"); break; case kPanasonicAcCool: - result += " (COOL)"; + result += F(" (COOL)"); break; case kPanasonicAcHeat: - result += " (HEAT)"; + result += F(" (HEAT)"); break; case kPanasonicAcDry: - result += " (DRY)"; + result += F(" (DRY)"); break; case kPanasonicAcFan: - result += " (FAN)"; + result += F(" (FAN)"); break; default: - result += " (UNKNOWN)"; + result += F(" (UNKNOWN)"); } - result += ", Temp: " + uint64ToString(getTemp()) + "C"; - result += ", Fan: " + uint64ToString(getFan()); + result += F(", Temp: "); + result += uint64ToString(getTemp()); + result += F("C, Fan: "); + result += uint64ToString(getFan()); switch (getFan()) { case kPanasonicAcFanAuto: - result += " (AUTO)"; + result += F(" (AUTO)"); break; case kPanasonicAcFanMax: - result += " (MAX)"; + result += F(" (MAX)"); break; case kPanasonicAcFanMin: - result += " (MIN)"; + result += F(" (MIN)"); break; default: - result += " (UNKNOWN)"; + result += F(" (UNKNOWN)"); break; } - result += ", Swing (Vertical): " + uint64ToString(getSwingVertical()); + result += F(", Swing (Vertical): "); + result += uint64ToString(getSwingVertical()); switch (getSwingVertical()) { case kPanasonicAcSwingVAuto: - result += " (AUTO)"; + result += F(" (AUTO)"); break; case kPanasonicAcSwingVUp: - result += " (Full Up)"; + result += F(" (Full Up)"); break; case kPanasonicAcSwingVDown: - result += " (Full Down)"; + result += F(" (Full Down)"); break; case 2: case 3: case 4: break; default: - result += " (UNKNOWN)"; + result += F(" (UNKNOWN)"); break; } switch (getModel()) { @@ -690,52 +786,54 @@ std::string IRPanasonicAc::toString() { case kPanasonicCkp: break; // No Horizontal Swing support. default: - result += ", Swing (Horizontal): " + uint64ToString(getSwingHorizontal()); + result += F(", Swing (Horizontal): "); + result += uint64ToString(getSwingHorizontal()); switch (getSwingHorizontal()) { case kPanasonicAcSwingHAuto: - result += " (AUTO)"; + result += F(" (AUTO)"); break; case kPanasonicAcSwingHFullLeft: - result += " (Full Left)"; + result += F(" (Full Left)"); break; case kPanasonicAcSwingHLeft: - result += " (Left)"; + result += F(" (Left)"); break; case kPanasonicAcSwingHMiddle: - result += " (Middle)"; + result += F(" (Middle)"); break; case kPanasonicAcSwingHFullRight: - result += " (Full Right)"; + result += F(" (Full Right)"); break; case kPanasonicAcSwingHRight: - result += " (Right)"; + result += F(" (Right)"); break; default: - result += " (UNKNOWN)"; + result += F(" (UNKNOWN)"); break; } } - result += ", Quiet: "; + result += F(", Quiet: "); if (getQuiet()) - result += "On"; + result += F("On"); else - result += "Off"; - result += ", Powerful: "; + result += F("Off"); + result += F(", Powerful: "); if (getPowerful()) - result += "On"; + result += F("On"); else - result += "Off"; - result += ", Clock: " + timeToString(getClock()); - result += ", On Timer: "; + result += F("Off"); + result += F(", Clock: "); + result += timeToString(getClock()); + result += F(", On Timer: "); if (isOnTimerEnabled()) result += timeToString(getOnTimer()); else - result += "Off"; - result += ", Off Timer: "; + result += F("Off"); + result += F(", Off Timer: "); if (isOffTimerEnabled()) result += timeToString(getOffTimer()); else - result += "Off"; + result += F("Off"); return result; } diff --git a/lib/IRremoteESP8266-2.5.2.03/src/ir_Panasonic.h b/lib/IRremoteESP8266-2.6.0/src/ir_Panasonic.h similarity index 91% rename from lib/IRremoteESP8266-2.5.2.03/src/ir_Panasonic.h rename to lib/IRremoteESP8266-2.6.0/src/ir_Panasonic.h index 762631fe79ea..1a7b4e11449f 100644 --- a/lib/IRremoteESP8266-2.5.2.03/src/ir_Panasonic.h +++ b/lib/IRremoteESP8266-2.6.0/src/ir_Panasonic.h @@ -12,6 +12,9 @@ #endif #include "IRremoteESP8266.h" #include "IRsend.h" +#ifdef UNIT_TEST +#include "IRsend_test.h" +#endif // PPPP AAA N N AAA SSSS OOO N N IIIII CCCC // P P A A NN N A A S O O NN N I C @@ -43,7 +46,7 @@ const uint8_t kPanasonicAcMaxTemp = 30; // Celsius const uint8_t kPanasonicAcFanModeTemp = 27; // Celsius const uint8_t kPanasonicAcQuiet = 1; // 0b1 const uint8_t kPanasonicAcPowerful = 0x20; // 0b100000 -// CKP models have Powerful and Quiet bits swapped. +// CKP & RKR models have Powerful and Quiet bits swapped. const uint8_t kPanasonicAcQuietCkp = 0x20; // 0b100000 const uint8_t kPanasonicAcPowerfulCkp = 1; // 0b1 const uint8_t kPanasonicAcSwingVAuto = 0xF; @@ -73,6 +76,7 @@ enum panasonic_ac_remote_model_t { kPanasonicDke = 3, kPanasonicJke = 4, kPanasonicCkp = 5, + kPanasonicRkr = 6, }; class IRPanasonicAc { @@ -81,7 +85,7 @@ class IRPanasonicAc { void stateReset(); #if SEND_PANASONIC - void send(); + void send(const uint16_t repeat = kPanasonicAcDefaultRepeat); #endif // SEND_PANASONIC void begin(); void on(); @@ -122,6 +126,10 @@ class IRPanasonicAc { const bool enable = true); void cancelOffTimer(); bool isOffTimerEnabled(); + uint8_t convertMode(const stdAc::opmode_t mode); + uint8_t convertFan(const stdAc::fanspeed_t speed); + uint8_t convertSwingV(const stdAc::swingv_t position); + uint8_t convertSwingH(const stdAc::swingh_t position); #ifdef ARDUINO String toString(); static String timeToString(const uint16_t mins_since_midnight); @@ -132,6 +140,9 @@ class IRPanasonicAc { #ifndef UNIT_TEST private: + IRsend _irsend; +#else + IRsendTest _irsend; #endif uint8_t remote_state[kPanasonicAcStateLength]; uint8_t _swingh; @@ -139,7 +150,6 @@ class IRPanasonicAc { void fixChecksum(const uint16_t length = kPanasonicAcStateLength); static uint8_t calcChecksum(const uint8_t *state, const uint16_t length = kPanasonicAcStateLength); - IRsend _irsend; }; #endif // IR_PANASONIC_H_ diff --git a/lib/IRremoteESP8266-2.5.2.03/src/ir_Pioneer.cpp b/lib/IRremoteESP8266-2.6.0/src/ir_Pioneer.cpp similarity index 100% rename from lib/IRremoteESP8266-2.5.2.03/src/ir_Pioneer.cpp rename to lib/IRremoteESP8266-2.6.0/src/ir_Pioneer.cpp diff --git a/lib/IRremoteESP8266-2.5.2.03/src/ir_Pronto.cpp b/lib/IRremoteESP8266-2.6.0/src/ir_Pronto.cpp similarity index 100% rename from lib/IRremoteESP8266-2.5.2.03/src/ir_Pronto.cpp rename to lib/IRremoteESP8266-2.6.0/src/ir_Pronto.cpp diff --git a/lib/IRremoteESP8266-2.5.2.03/src/ir_RC5_RC6.cpp b/lib/IRremoteESP8266-2.6.0/src/ir_RC5_RC6.cpp similarity index 100% rename from lib/IRremoteESP8266-2.5.2.03/src/ir_RC5_RC6.cpp rename to lib/IRremoteESP8266-2.6.0/src/ir_RC5_RC6.cpp diff --git a/lib/IRremoteESP8266-2.5.2.03/src/ir_RCMM.cpp b/lib/IRremoteESP8266-2.6.0/src/ir_RCMM.cpp similarity index 100% rename from lib/IRremoteESP8266-2.5.2.03/src/ir_RCMM.cpp rename to lib/IRremoteESP8266-2.6.0/src/ir_RCMM.cpp diff --git a/lib/IRremoteESP8266-2.5.2.03/src/ir_Samsung.cpp b/lib/IRremoteESP8266-2.6.0/src/ir_Samsung.cpp similarity index 66% rename from lib/IRremoteESP8266-2.5.2.03/src/ir_Samsung.cpp rename to lib/IRremoteESP8266-2.6.0/src/ir_Samsung.cpp index d943f8cf976e..7e54d17df60d 100644 --- a/lib/IRremoteESP8266-2.5.2.03/src/ir_Samsung.cpp +++ b/lib/IRremoteESP8266-2.6.0/src/ir_Samsung.cpp @@ -1,5 +1,5 @@ // Copyright 2009 Ken Shirriff -// Copyright 2017 David Conran +// Copyright 2017, 2018, 2019 David Conran #include "ir_Samsung.h" #include @@ -168,6 +168,127 @@ bool IRrecv::decodeSAMSUNG(decode_results *results, uint16_t nbits, } #endif +#if SEND_SAMSUNG36 +// Send a Samsung 36-bit formatted message. +// +// Args: +// data: The message to be sent. +// nbits: The bit size of the message being sent. typically kSamsung36Bits. +// repeat: The number of times the message is to be repeated. +// +// Status: Alpha / Experimental. +// +// Note: +// Protocol is used by Samsung Bluray Remote: ak59-00167a +// +// Ref: +// https://github.com/markszabo/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(kSamsungHdrMark, kSamsungHdrSpace, + kSamsungBitMark, kSamsungOneSpace, + kSamsungBitMark, kSamsungZeroSpace, + kSamsungBitMark, kSamsungHdrSpace, + data >> (nbits - 16), 16, 38, true, 0, kDutyDefault); + // Block #2 (The rest, typically 20 bits) + sendGeneric(0, 0, // No header + kSamsungBitMark, kSamsungOneSpace, + kSamsungBitMark, kSamsungZeroSpace, + kSamsungBitMark, 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. +// +// Args: +// results: Ptr to the data to decode and where to store the decode result. +// nbits: Nr. of bits to expect in the data portion. +// Typically kSamsung36Bits. +// strict: Flag to indicate if we strictly adhere to the specification. +// Returns: +// boolean: True if it can decode it, false if it can't. +// +// Status: Alpha / Experimental +// +// Note: +// Protocol is used by Samsung Bluray Remote: ak59-00167a +// +// Ref: +// https://github.com/markszabo/IRremoteESP8266/issues/621 +bool IRrecv::decodeSamsung36(decode_results *results, const uint16_t nbits, + const bool strict) { + if (results->rawlen < 2 * nbits + kHeader + kFooter * 2 - 1) + 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; + uint16_t offset = kStartOffset; + + // Header + if (!matchMark(results->rawbuf[offset], kSamsungHdrMark)) return false; + // Calculate how long the common tick time is based on the header mark. + uint32_t m_tick = results->rawbuf[offset++] * kRawTick / kSamsungHdrMarkTicks; + if (!matchSpace(results->rawbuf[offset], kSamsungHdrSpace)) return false; + // Calculate how long the common tick time is based on the header space. + uint32_t s_tick = + results->rawbuf[offset++] * kRawTick / kSamsungHdrSpaceTicks; + // Data (Block #1) + match_result_t data_result = + matchData(&(results->rawbuf[offset]), 16, + kSamsungBitMarkTicks * m_tick, kSamsungOneSpaceTicks * s_tick, + kSamsungBitMarkTicks * m_tick, kSamsungZeroSpaceTicks * s_tick); + if (data_result.success == false) return false; + data = data_result.data; + offset += data_result.used; + uint16_t bitsSoFar = data_result.used / 2; + // Footer (Block #1) + if (!matchMark(results->rawbuf[offset++], kSamsungBitMarkTicks * m_tick)) + return false; + if (!matchSpace(results->rawbuf[offset++], kSamsungHdrSpaceTicks * s_tick)) + return false; + // Data (Block #2) + data_result = matchData(&(results->rawbuf[offset]), + nbits - 16, + kSamsungBitMarkTicks * m_tick, + kSamsungOneSpaceTicks * s_tick, + kSamsungBitMarkTicks * m_tick, + kSamsungZeroSpaceTicks * s_tick); + if (data_result.success == false) return false; + data <<= (nbits - 16); + data += data_result.data; + offset += data_result.used; + bitsSoFar += data_result.used / 2; + // Footer (Block #2) + if (!matchMark(results->rawbuf[offset++], kSamsungBitMarkTicks * m_tick)) + return false; + if (offset < results->rawlen && + !matchAtLeast(results->rawbuf[offset], kSamsungMinGapTicks * s_tick)) + return false; + + // Compliance + if (nbits != bitsSoFar) return false; + + // Success + results->bits = bitsSoFar; + 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. // @@ -180,7 +301,8 @@ bool IRrecv::decodeSAMSUNG(decode_results *results, uint16_t nbits, // // Ref: // https://github.com/markszabo/IRremoteESP8266/issues/505 -void IRsend::sendSamsungAC(uint8_t data[], uint16_t nbytes, uint16_t repeat) { +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. @@ -226,53 +348,85 @@ void IRSamsungAc::begin() { _irsend.begin(); } uint8_t IRSamsungAc::calcChecksum(const uint8_t state[], const uint16_t length) { uint8_t sum = 0; - uint8_t currentbyte; // Safety check so we don't go outside the array. - if (length <= 5) return 255; + if (length < 7) return 255; // Shamelessly inspired by: // https://github.com/adafruit/Raw-IR-decoder-for-Arduino/pull/3/files // Count most of the '1' bits after the checksum location. - for (uint8_t i = length - 5; i < length - 1; i++) { - currentbyte = state[i]; - if (i == length - 5) currentbyte = state[length - 5] & 0b11111110; - for (; currentbyte; currentbyte >>= 1) - if (currentbyte & 1) sum++; - } + sum += countBits(state[length - 7], 8); + sum -= countBits(state[length - 6] & 0xF, 8); + sum += countBits(state[length - 5] & 0b11111110, 8); + sum += countBits(state + length - 4, 3); return (28 - sum) & 0xF; } bool IRSamsungAc::validChecksum(const uint8_t state[], const uint16_t length) { - if (length <= 5) return true; // No checksum to compare with. Assume okay. - return (state[length - 6] >> 4) == calcChecksum(state, length); + if (length < kSamsungAcStateLength) + return true; // No checksum to compare with. Assume okay. + uint8_t offset = 0; + if (length >= kSamsungAcExtendedStateLength) offset = 7; + return ((state[length - 6] >> 4) == calcChecksum(state, length) && + (state[length - (13 + offset)] >> 4) == calcChecksum(state, length - + (7 + offset))); } // Update the checksum for the internal state. void IRSamsungAc::checksum(uint16_t length) { - if (length < 9) return; + if (length < 13) return; remote_state[length - 6] &= 0x0F; remote_state[length - 6] |= (calcChecksum(remote_state, length) << 4); + remote_state[length - 13] &= 0x0F; + remote_state[length - 13] |= (calcChecksum(remote_state, length - 7) << 4); } #if SEND_SAMSUNG_AC -void IRSamsungAc::send(const bool calcchecksum) { +// 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) { if (calcchecksum) checksum(); - _irsend.sendSamsungAC(remote_state); + _irsend.sendSamsungAC(remote_state, kSamsungAcStateLength, repeat); } -#endif // SEND_SAMSUNG_AC -#if SEND_SAMSUNG_AC -void IRSamsungAc::sendExtended(const bool calcchecksum) { +// 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) { if (calcchecksum) checksum(); uint8_t extended_state[kSamsungAcExtendedStateLength] = { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xD2, 0x0F, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0xD2, 0x0F, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; // Copy/convert the internal state to an extended state. for (uint16_t i = 0; i < kSamsungACSectionLength; i++) extended_state[i] = remote_state[i]; for (uint16_t i = kSamsungACSectionLength; i < kSamsungAcStateLength; i++) extended_state[i + kSamsungACSectionLength] = remote_state[i]; + // extended_state[8] seems special. This is a guess on how to calculate it. + extended_state[8] = (extended_state[1] & 0x9F) | 0x40; // Send it. - _irsend.sendSamsungAC(extended_state, kSamsungAcExtendedStateLength); + _irsend.sendSamsungAC(extended_state, kSamsungAcExtendedStateLength, repeat); +} + +// Send the special extended "On" message as the library can't seem to reproduce +// this message automatically. +// See: https://github.com/markszabo/IRremoteESP8266/issues/604#issuecomment-475020036 +void IRSamsungAc::sendOn(const uint16_t repeat) { + const uint8_t extended_state[21] = { + 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); +} + +// Send the special extended "Off" message as the library can't seem to +// reproduce this message automatically. +// See: https://github.com/markszabo/IRremoteESP8266/issues/604#issuecomment-475020036 +void IRSamsungAc::sendOff(const uint16_t repeat) { + const uint8_t extended_state[21] = { + 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); } #endif // SEND_SAMSUNG_AC @@ -423,6 +577,39 @@ void IRSamsungAc::setQuiet(const bool state) { } } +// Convert a standard A/C mode into its native mode. +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 standard A/C Fan speed into its native fan speed. +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 the internal state into a human readable string. #ifdef ARDUINO String IRSamsungAc::toString() { @@ -431,74 +618,77 @@ String IRSamsungAc::toString() { std::string IRSamsungAc::toString() { std::string result = ""; #endif // ARDUINO - result += "Power: "; + result += F("Power: "); if (getPower()) - result += "On"; + result += F("On"); else - result += "Off"; - result += ", Mode: " + uint64ToString(getMode()); + result += F("Off"); + result += F(", Mode: "); + result += uint64ToString(getMode()); switch (getMode()) { case kSamsungAcAuto: - result += " (AUTO)"; + result += F(" (AUTO)"); break; case kSamsungAcCool: - result += " (COOL)"; + result += F(" (COOL)"); break; case kSamsungAcHeat: - result += " (HEAT)"; + result += F(" (HEAT)"); break; case kSamsungAcDry: - result += " (DRY)"; + result += F(" (DRY)"); break; case kSamsungAcFan: - result += " (FAN)"; + result += F(" (FAN)"); break; default: - result += " (UNKNOWN)"; + result += F(" (UNKNOWN)"); } - result += ", Temp: " + uint64ToString(getTemp()) + "C"; - result += ", Fan: " + uint64ToString(getFan()); + result += F(", Temp: "); + result += uint64ToString(getTemp()); + result += F("C, Fan: "); + result += uint64ToString(getFan()); switch (getFan()) { case kSamsungAcFanAuto: case kSamsungAcFanAuto2: - result += " (AUTO)"; + result += F(" (AUTO)"); break; case kSamsungAcFanLow: - result += " (LOW)"; + result += F(" (LOW)"); break; case kSamsungAcFanMed: - result += " (MED)"; + result += F(" (MED)"); break; case kSamsungAcFanHigh: - result += " (HIGH)"; + result += F(" (HIGH)"); break; case kSamsungAcFanTurbo: - result += " (TURBO)"; + result += F(" (TURBO)"); break; default: - result += " (UNKNOWN)"; + result += F(" (UNKNOWN)"); break; } - result += ", Swing: "; + result += F(", Swing: "); if (getSwing()) - result += "On"; + result += F("On"); else - result += "Off"; - result += ", Beep: "; + result += F("Off"); + result += F(", Beep: "); if (getBeep()) - result += "On"; + result += F("On"); else - result += "Off"; - result += ", Clean: "; + result += F("Off"); + result += F(", Clean: "); if (getBeep()) - result += "On"; + result += F("On"); else - result += "Off"; - result += ", Quiet: "; + result += F("Off"); + result += F(", Quiet: "); if (getQuiet()) - result += "On"; + result += F("On"); else - result += "Off"; + result += F("Off"); return result; } @@ -571,7 +761,6 @@ bool IRrecv::decodeSamsungAC(decode_results *results, uint16_t nbits, // Is the signature correct? DPRINTLN("DEBUG: Checking signature."); if (results->state[0] != 0x02 || results->state[2] != 0x0F) return false; - if (results->state[1] != 0x92 && results->state[1] != 0xB2) return false; if (strict) { // Is the checksum valid? if (!IRSamsungAc::validChecksum(results->state, nbits / 8)) { diff --git a/lib/IRremoteESP8266-2.5.2.03/src/ir_Samsung.h b/lib/IRremoteESP8266-2.6.0/src/ir_Samsung.h similarity index 84% rename from lib/IRremoteESP8266-2.5.2.03/src/ir_Samsung.h rename to lib/IRremoteESP8266-2.6.0/src/ir_Samsung.h index f80b47d20baf..9df427c6ff9e 100644 --- a/lib/IRremoteESP8266-2.5.2.03/src/ir_Samsung.h +++ b/lib/IRremoteESP8266-2.6.0/src/ir_Samsung.h @@ -14,6 +14,9 @@ #endif #include "IRremoteESP8266.h" #include "IRsend.h" +#ifdef UNIT_TEST +#include "IRsend_test.h" +#endif // SSSS AAA MMM SSSS U U N N GGGG // S A A M M M S U U NN N G @@ -62,8 +65,12 @@ class IRSamsungAc { void stateReset(); #if SEND_SAMSUNG_AC - void send(const bool calcchecksum = true); - void sendExtended(const bool calcchecksum = true); + void send(const uint16_t repeat = kSamsungAcDefaultRepeat, + const bool calcchecksum = true); + void sendExtended(const uint16_t repeat = kSamsungAcDefaultRepeat, + const bool calcchecksum = true); + void sendOn(const uint16_t repeat = kSamsungAcDefaultRepeat); + void sendOff(const uint16_t repeat = kSamsungAcDefaultRepeat); #endif // SEND_SAMSUNG_AC void begin(); void on(); @@ -91,17 +98,23 @@ class IRSamsungAc { const uint16_t length = kSamsungAcStateLength); static uint8_t calcChecksum(const uint8_t state[], const uint16_t length = kSamsungAcStateLength); + uint8_t convertMode(const stdAc::opmode_t mode); + uint8_t convertFan(const stdAc::fanspeed_t speed); #ifdef ARDUINO String toString(); #else std::string toString(); #endif +#ifndef UNIT_TEST private: + IRsend _irsend; +#else + IRsendTest _irsend; +#endif // The state of the IR remote in IR code form. uint8_t remote_state[kSamsungAcExtendedStateLength]; void checksum(const uint16_t length = kSamsungAcStateLength); - IRsend _irsend; }; #endif // IR_SAMSUNG_H_ diff --git a/lib/IRremoteESP8266-2.5.2.03/src/ir_Sanyo.cpp b/lib/IRremoteESP8266-2.6.0/src/ir_Sanyo.cpp similarity index 100% rename from lib/IRremoteESP8266-2.5.2.03/src/ir_Sanyo.cpp rename to lib/IRremoteESP8266-2.6.0/src/ir_Sanyo.cpp diff --git a/lib/IRremoteESP8266-2.5.2.03/src/ir_Sharp.cpp b/lib/IRremoteESP8266-2.6.0/src/ir_Sharp.cpp similarity index 100% rename from lib/IRremoteESP8266-2.5.2.03/src/ir_Sharp.cpp rename to lib/IRremoteESP8266-2.6.0/src/ir_Sharp.cpp diff --git a/lib/IRremoteESP8266-2.5.2.03/src/ir_Sherwood.cpp b/lib/IRremoteESP8266-2.6.0/src/ir_Sherwood.cpp similarity index 100% rename from lib/IRremoteESP8266-2.5.2.03/src/ir_Sherwood.cpp rename to lib/IRremoteESP8266-2.6.0/src/ir_Sherwood.cpp diff --git a/lib/IRremoteESP8266-2.5.2.03/src/ir_Sony.cpp b/lib/IRremoteESP8266-2.6.0/src/ir_Sony.cpp similarity index 100% rename from lib/IRremoteESP8266-2.5.2.03/src/ir_Sony.cpp rename to lib/IRremoteESP8266-2.6.0/src/ir_Sony.cpp diff --git a/lib/IRremoteESP8266-2.6.0/src/ir_Tcl.cpp b/lib/IRremoteESP8266-2.6.0/src/ir_Tcl.cpp new file mode 100644 index 000000000000..79fb23cf1af2 --- /dev/null +++ b/lib/IRremoteESP8266-2.6.0/src/ir_Tcl.cpp @@ -0,0 +1,420 @@ +// Copyright 2019 David Conran + +#include "ir_Tcl.h" +#include +#ifndef ARDUINO +#include +#endif +#include "IRremoteESP8266.h" +#include "IRutils.h" + +// Constants + + +#if SEND_TCL112AC +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 + +IRTcl112Ac::IRTcl112Ac(uint16_t pin) : _irsend(pin) { stateReset(); } + +void IRTcl112Ac::begin() { this->_irsend.begin(); } + +#if SEND_TCL112AC +void IRTcl112Ac::send(const uint16_t repeat) { + this->checksum(); + this->_irsend.sendTcl112Ac(remote_state, kTcl112AcStateLength, repeat); +} +#endif // SEND_TCL112AC + +// Calculate the checksum for a given array. +// Args: +// state: The array to calculate the checksum over. +// length: The size of the array. +// Returns: +// The 8 bit checksum value. +uint8_t IRTcl112Ac::calcChecksum(uint8_t state[], + const uint16_t length) { + if (length) + return sumBytes(state, length - 1); + else + return 0; +} + +// Calculate & set the checksum for the current internal state of the remote. +void IRTcl112Ac::checksum(const uint16_t length) { + // Stored the checksum value in the last byte. + if (length > 1) + remote_state[length - 1] = calcChecksum(remote_state, length); +} + +// Verify the checksum is valid for a given state. +// Args: +// state: The array to verify the checksum of. +// length: The size of the state. +// Returns: +// A boolean. +bool IRTcl112Ac::validChecksum(uint8_t state[], const uint16_t length) { + return (length > 1 && state[length - 1] == calcChecksum(state, length)); +} + +void IRTcl112Ac::stateReset() { + for (uint8_t i = 0; i < kTcl112AcStateLength; i++) + remote_state[i] = 0x0; + // A known good state. (On, Cool, 24C) + remote_state[0] = 0x23; + remote_state[1] = 0xCB; + remote_state[2] = 0x26; + remote_state[3] = 0x01; + remote_state[5] = 0x24; + remote_state[6] = 0x03; + remote_state[7] = 0x07; + remote_state[8] = 0x40; + remote_state[13] = 0x03; +} + +uint8_t* IRTcl112Ac::getRaw() { + this->checksum(); + return remote_state; +} + +void IRTcl112Ac::setRaw(const uint8_t new_code[], const uint16_t length) { + for (uint8_t i = 0; i < length && i < kTcl112AcStateLength; i++) { + remote_state[i] = new_code[i]; + } +} + +// Set the requested power state of the A/C to on. +void IRTcl112Ac::on(void) { this->setPower(true); } + +// Set the requested power state of the A/C to off. +void IRTcl112Ac::off(void) { this->setPower(false); } + +// Set the requested power state of the A/C. +void IRTcl112Ac::setPower(const bool on) { + if (on) + remote_state[5] |= kTcl112AcPowerMask; + else + remote_state[5] &= ~kTcl112AcPowerMask; +} + +// Return the requested power state of the A/C. +bool IRTcl112Ac::getPower(void) { + return remote_state[5] & kTcl112AcPowerMask; +} + +// Get the requested climate operation mode of the a/c unit. +// Returns: +// A uint8_t containing the A/C mode. +uint8_t IRTcl112Ac::getMode() { + return remote_state[6] & 0xF; +} + +// Set the requested climate operation mode of the a/c unit. +// 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: + this->setFan(kTcl112AcFanHigh); + // FALLTHRU + case kTcl112AcAuto: + case kTcl112AcCool: + case kTcl112AcHeat: + case kTcl112AcDry: + remote_state[6] &= 0xF0; + remote_state[6] |= mode; + break; + default: + setMode(kTcl112AcAuto); + } +} + +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; + if (nrHalfDegrees & 1) // Do we have a half degree celsius? + remote_state[12] |= kTcl112AcHalfDegree; // Add 0.5 degrees + else + remote_state[12] &= ~kTcl112AcHalfDegree; // Clear the half degree. + remote_state[7] &= 0xF0; // Clear temp bits. + remote_state[7] |= ((uint8_t)kTcl112AcTempMax - nrHalfDegrees / 2); +} + +float IRTcl112Ac::getTemp() { + float result = kTcl112AcTempMax - (remote_state[7] & 0xF); + if (remote_state[12] & kTcl112AcHalfDegree) result += 0.5; + return result; +} + +// Set the speed of the fan. +// Unknown speeds will default to Auto. +void IRTcl112Ac::setFan(const uint8_t speed) { + switch (speed) { + case kTcl112AcFanAuto: + case kTcl112AcFanLow: + case kTcl112AcFanMed: + case kTcl112AcFanHigh: + remote_state[8] &= ~kTcl112AcFanMask; + remote_state[8] |= speed; + break; + default: + this->setFan(kTcl112AcFanAuto); + } +} + +// Return the currect fan speed. +uint8_t IRTcl112Ac::getFan() { + return remote_state[8] & kTcl112AcFanMask; +} + +// Control economy mode. +void IRTcl112Ac::setEcono(const bool on) { + if (on) + remote_state[5] |= kTcl112AcBitEcono; + else + remote_state[5] &= ~kTcl112AcBitEcono; +} + +// Return the economy state of the A/C. +bool IRTcl112Ac::getEcono(void) { + return remote_state[5] & kTcl112AcBitEcono; +} + +// Control Health mode. +void IRTcl112Ac::setHealth(const bool on) { + if (on) + remote_state[6] |= kTcl112AcBitHealth; + else + remote_state[6] &= ~kTcl112AcBitHealth; +} + +// Return the Health mode state of the A/C. +bool IRTcl112Ac::getHealth(void) { + return remote_state[6] & kTcl112AcBitHealth; +} + +// Control Light/Display mode. +void IRTcl112Ac::setLight(const bool on) { + if (on) + remote_state[5] &= ~kTcl112AcBitLight; + else + remote_state[5] |= kTcl112AcBitLight; +} + +// Return the Light/Display mode state of the A/C. +bool IRTcl112Ac::getLight(void) { + return !(remote_state[5] & kTcl112AcBitLight); +} + +// Control Horizontal Swing. +void IRTcl112Ac::setSwingHorizontal(const bool on) { + if (on) + remote_state[12] |= kTcl112AcBitSwingH; + else + remote_state[12] &= ~kTcl112AcBitSwingH; +} + +// Return the Horizontal Swing state of the A/C. +bool IRTcl112Ac::getSwingHorizontal(void) { + return remote_state[12] & kTcl112AcBitSwingH; +} + +// Control Vertical Swing. +void IRTcl112Ac::setSwingVertical(const bool on) { + if (on) + remote_state[8] |= kTcl112AcBitSwingV; + else + remote_state[8] &= ~kTcl112AcBitSwingV; +} + +// Return the Vertical Swing state of the A/C. +bool IRTcl112Ac::getSwingVertical(void) { + return remote_state[8] & kTcl112AcBitSwingV; +} + +// Control the Turbo setting. +void IRTcl112Ac::setTurbo(const bool on) { + if (on) { + remote_state[6] |= kTcl112AcBitTurbo; + this->setFan(kTcl112AcFanHigh); + this->setSwingVertical(true); + } else { + remote_state[6] &= ~kTcl112AcBitTurbo; + } +} + +// Return the Turbo setting state of the A/C. +bool IRTcl112Ac::getTurbo(void) { + return remote_state[6] & kTcl112AcBitTurbo; +} + +// Convert a standard A/C mode into its native mode. +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 standard A/C Fan speed into its native fan speed. +uint8_t IRTcl112Ac::convertFan(const stdAc::fanspeed_t speed) { + switch (speed) { + case stdAc::fanspeed_t::kMin: + 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 the internal state into a human readable string. +#ifdef ARDUINO +String IRTcl112Ac::toString() { + String result = ""; +#else +std::string IRTcl112Ac::toString() { + std::string result = ""; +#endif // ARDUINO + result += F("Power: "); + result += (this->getPower() ? F("On") : F("Off")); + result += F(", Mode: "); + result += uint64ToString(getMode()); + switch (this->getMode()) { + case kTcl112AcAuto: + result += F(" (AUTO)"); + break; + case kTcl112AcCool: + result += F(" (COOL)"); + break; + case kTcl112AcHeat: + result += F(" (HEAT)"); + break; + case kTcl112AcDry: + result += F(" (DRY)"); + break; + case kTcl112AcFan: + result += F(" (FAN)"); + break; + default: + result += F(" (UNKNOWN)"); + } + uint16_t nrHalfDegrees = this->getTemp() * 2; + result += F(", Temp: "); + result += uint64ToString(nrHalfDegrees / 2); + if (nrHalfDegrees & 1) result += F(".5"); + result += F("C, Fan: "); + result += uint64ToString(getFan()); + switch (getFan()) { + case kTcl112AcFanAuto: + result += F(" (Auto)"); + break; + case kTcl112AcFanLow: + result += F(" (Low)"); + break; + case kTcl112AcFanMed: + result += F(" (Med)"); + break; + case kTcl112AcFanHigh: + result += F(" (High)"); + break; + } + result += F(", Econo: "); + result += (this->getEcono() ? F("On") : F("Off")); + result += ", Health: "; + result += (this->getHealth() ? F("On") : F("Off")); + result += F(", Light: "); + result += (this->getLight() ? F("On") : F("Off")); + result += F(", Turbo: "); + result += (this->getTurbo() ? F("On") : F("Off")); + result += ", Swing (H): "; + result += (this->getSwingHorizontal() ? F("On") : F("Off")); + result += F(", Swing (V): "); + result += (this->getSwingVertical() ? F("On") : F("Off")); + return result; +} + +#if DECODE_TCL112AC +// Decode the supplied TCL112AC message. +// +// Args: +// results: Ptr to the data to decode and where to store the decode result. +// nbits: The number of data bits to expect. Typically kTcl112AcBits. +// strict: Flag indicating if we should perform strict matching. +// Returns: +// boolean: True if it can decode it, false if it can't. +// +// Status: BETA / Appears to mostly work. +// +// Ref: +// https://github.com/markszabo/IRremoteESP8266/issues/619 +bool IRrecv::decodeTcl112Ac(decode_results *results, uint16_t nbits, + bool strict) { + if (results->rawlen < 2 * nbits + kHeader + kFooter - 1) + return false; // Can't possibly be a valid Samsung A/C message. + if (strict && nbits != kTcl112AcBits) return false; + + uint16_t offset = kStartOffset; + uint16_t dataBitsSoFar = 0; + match_result_t data_result; + + // Message Header + if (!matchMark(results->rawbuf[offset++], kTcl112AcHdrMark)) return false; + if (!matchSpace(results->rawbuf[offset++], kTcl112AcHdrSpace)) return false; + + // Data + // Keep reading bytes until we either run out of section or state to fill. + for (uint16_t i = 0; offset <= results->rawlen - 16 && i < nbits / 8; + i++, dataBitsSoFar += 8, offset += data_result.used) { + data_result = matchData(&(results->rawbuf[offset]), 8, kTcl112AcBitMark, + kTcl112AcOneSpace, kTcl112AcBitMark, + kTcl112AcZeroSpace, kTolerance, 0, false); + if (data_result.success == false) { + DPRINT("DEBUG: offset = "); + DPRINTLN(offset + data_result.used); + return false; // Fail + } + results->state[i] = data_result.data; + } + + // Footer + if (!matchMark(results->rawbuf[offset++], kTcl112AcBitMark)) return false; + if (offset <= results->rawlen && + !matchAtLeast(results->rawbuf[offset++], kTcl112AcGap)) return false; + // Compliance + // Re-check we got the correct size/length due to the way we read the data. + if (dataBitsSoFar != nbits) return false; + // Verify we got a valid checksum. + if (strict && !IRTcl112Ac::validChecksum(results->state)) return false; + // Success + results->decode_type = TCL112AC; + results->bits = dataBitsSoFar; + // 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_TCL112AC diff --git a/lib/IRremoteESP8266-2.6.0/src/ir_Tcl.h b/lib/IRremoteESP8266-2.6.0/src/ir_Tcl.h new file mode 100644 index 000000000000..a1595451d1d2 --- /dev/null +++ b/lib/IRremoteESP8266-2.6.0/src/ir_Tcl.h @@ -0,0 +1,105 @@ +// Copyright 2019 David Conran + +#ifndef IR_TCL_H_ +#define IR_TCL_H_ + +#ifndef UNIT_TEST +#include +#else +#include +#endif +#include "IRremoteESP8266.h" +#include "IRsend.h" +#ifdef UNIT_TEST +#include "IRsend_test.h" +#endif + +// Constants +const uint16_t kTcl112AcHdrMark = 3000; +const uint16_t kTcl112AcHdrSpace = 1650; +const uint16_t kTcl112AcBitMark = 500; +const uint16_t kTcl112AcOneSpace = 1050; +const uint16_t kTcl112AcZeroSpace = 325; +const uint32_t kTcl112AcGap = kDefaultMessageGap; // Just a guess. + +const uint8_t kTcl112AcHeat = 1; +const uint8_t kTcl112AcDry = 2; +const uint8_t kTcl112AcCool = 3; +const uint8_t kTcl112AcFan = 7; +const uint8_t kTcl112AcAuto = 8; +const uint8_t kTcl112AcFanMask = 0b00000111; +const uint8_t kTcl112AcFanAuto = 0b00000000; +const uint8_t kTcl112AcFanLow = 0b00000010; +const uint8_t kTcl112AcFanMed = 0b00000011; +const uint8_t kTcl112AcFanHigh = 0b00000101; + +const uint8_t kTcl112AcHalfDegree = 0b00100000; +const float kTcl112AcTempMax = 31.0; +const float kTcl112AcTempMin = 16.0; + +const uint8_t kTcl112AcPowerMask = 0b00000100; +const uint8_t kTcl112AcBitEcono = 0b10000000; +const uint8_t kTcl112AcBitLight = 0b01000000; +const uint8_t kTcl112AcBitHealth = 0b00010000; +const uint8_t kTcl112AcBitSwingH = 0b00001000; +const uint8_t kTcl112AcBitSwingV = 0b00111000; +const uint8_t kTcl112AcBitTurbo = 0b01000000; + + +class IRTcl112Ac { + public: + explicit IRTcl112Ac(uint16_t pin); + +#if SEND_TCL112AC + void send(const uint16_t repeat = kTcl112AcDefaultRepeat); +#endif // SEND_TCL + void begin(void); + uint8_t* getRaw(void); + void setRaw(const uint8_t new_code[], + const uint16_t length = kTcl112AcStateLength); + void on(void); + void off(void); + void setPower(const bool on); + bool getPower(void); + void setTemp(const float celsius); // Celsius in 0.5 increments + float getTemp(void); + void setMode(const uint8_t mode); + uint8_t getMode(void); + static uint8_t calcChecksum(uint8_t state[], + const uint16_t length = kTcl112AcStateLength); + static bool validChecksum(uint8_t state[], + const uint16_t length = kTcl112AcStateLength); + void setFan(const uint8_t speed); + uint8_t getFan(void); + void setEcono(const bool on); + bool getEcono(void); + void setHealth(const bool on); + bool getHealth(void); + void setLight(const bool on); + bool getLight(void); + void setSwingHorizontal(const bool on); + bool getSwingHorizontal(void); + void setSwingVertical(const bool on); + bool getSwingVertical(void); + void setTurbo(const bool on); + bool getTurbo(void); + uint8_t convertMode(const stdAc::opmode_t mode); + uint8_t convertFan(const stdAc::fanspeed_t speed); +#ifdef ARDUINO + String toString(); +#else + std::string toString(); +#endif +#ifndef UNIT_TEST + + private: + IRsend _irsend; +#else + IRsendTest _irsend; +#endif + uint8_t remote_state[kTcl112AcStateLength]; + void stateReset(); + void checksum(const uint16_t length = kTcl112AcStateLength); +}; + +#endif // IR_TCL_H_ diff --git a/lib/IRremoteESP8266-2.6.0/src/ir_Teco.cpp b/lib/IRremoteESP8266-2.6.0/src/ir_Teco.cpp new file mode 100644 index 000000000000..779bf8f8ff79 --- /dev/null +++ b/lib/IRremoteESP8266-2.6.0/src/ir_Teco.cpp @@ -0,0 +1,278 @@ +// Copyright 2019 Fabien Valthier +/* +Node MCU/ESP8266 Sketch to emulate Teco +*/ + +#include "ir_Teco.h" +#include +#include "IRremoteESP8266.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. + +#if SEND_TECO +// Send a Teco A/C message. +// +// Args: +// data: Contents of the message to be sent. +// nbits: Nr. of bits of data to be sent. Typically kTecoBits. +// repeat: Nr. of additional times the message is to be sent. +void IRsend::sendTeco(uint64_t data, uint16_t nbits, uint16_t repeat) { + sendGeneric(kTecoHdrMark, kTecoHdrSpace, kTecoBitMark, kTecoOneSpace, + kTecoBitMark, kTecoZeroSpace, kTecoBitMark, kTecoGap, + data, nbits, 38000, false, repeat, kDutyDefault); +} +#endif // SEND_TECO + +// Class for decoding and constructing Teco AC messages. +IRTecoAc::IRTecoAc(const uint16_t pin) : _irsend(pin) { stateReset(); } + +void IRTecoAc::begin() { _irsend.begin(); } + +#if SEND_TECO +void IRTecoAc::send(const uint16_t repeat) { + _irsend.sendTeco(remote_state, kTecoBits, repeat); +} +#endif // SEND_TECO + +void IRTecoAc::stateReset(void) { + // Mode:auto, Power:Off, fan:auto, temp:16, swing:off, sleep:off + remote_state = kTecoReset; +} + +uint64_t IRTecoAc::getRaw(void) { return remote_state; } + +void IRTecoAc::setRaw(const uint64_t new_code) { remote_state = new_code; } + +void IRTecoAc::on(void) { remote_state |= kTecoPower; } + +void IRTecoAc::off(void) { remote_state &= ~kTecoPower; } + +void IRTecoAc::setPower(const bool on) { + if (on) + this->on(); + else + this->off(); +} + +bool IRTecoAc::getPower(void) { + return (remote_state & kTecoPower) == kTecoPower; } + +void IRTecoAc::setTemp(const uint8_t temp) { + uint8_t newtemp = temp; + newtemp = std::min(newtemp, kTecoMaxTemp); + newtemp = std::max(newtemp, kTecoMinTemp); + newtemp -= kTecoMinTemp; // 16=0b000 + + remote_state &= ~kTecoTempMask; // reinit temp + remote_state |= (newtemp << 8); +} + +uint8_t IRTecoAc::getTemp(void) { + return ((remote_state & kTecoTempMask) >> 8) + kTecoMinTemp; +} + +// Set the speed of the fan +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; + } + remote_state &= ~kTecoFanMask; // reinit fan + remote_state |= (newspeed << 4); +} + +uint8_t IRTecoAc::getFan(void) { return (remote_state & kTecoFanMask) >> 4; } + +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; + } + remote_state &= ~kTecoModeMask; // reinit mode + remote_state |= newmode; +} + +uint8_t IRTecoAc::getMode(void) { return remote_state & kTecoModeMask; } + +void IRTecoAc::setSwing(const bool on) { + if (on) + remote_state |= kTecoSwing; + else + remote_state &= ~kTecoSwing; +} + +bool IRTecoAc::getSwing(void) { return remote_state & kTecoSwing; } + +void IRTecoAc::setSleep(const bool on) { + if (on) + remote_state |= kTecoSleep; + else + remote_state &= ~kTecoSleep; +} + +bool IRTecoAc::getSleep(void) { return remote_state & kTecoSleep; } + +// Convert a standard A/C mode into its native mode. +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 standard A/C Fan speed into its native fan speed. +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 the internal state into a human readable string. +#ifdef ARDUINO +String IRTecoAc::toString(void) { + String result = ""; +#else +std::string IRTecoAc::toString(void) { + std::string result = ""; +#endif // ARDUINO + result += F("Power: "); + result += (this->getPower() ? F("On") : F("Off")); + result += F(", Mode: "); + result += uint64ToString(this->getMode()); + switch (this->getMode()) { + case kTecoAuto: + result += F(" (AUTO)"); + break; + case kTecoCool: + result += F(" (COOL)"); + break; + case kTecoHeat: + result += F(" (HEAT)"); + break; + case kTecoDry: + result += F(" (DRY)"); + break; + case kTecoFan: + result += F(" (FAN)"); + break; + default: + result += F(" (UNKNOWN)"); + } + result += F(", Temp: "); + result += uint64ToString(getTemp()); + result += F("C, Fan: "); + result += uint64ToString(getFan()); + switch (this->getFan()) { + case kTecoFanAuto: + result += F(" (Auto)"); + break; + case kTecoFanHigh: + result += F(" (High)"); + break; + case kTecoFanLow: + result += F(" (Low)"); + break; + case kTecoFanMed: + result += F(" (Med)"); + break; + default: + result += F(" (UNKNOWN)"); + } + result += F(", Sleep: "); + result += (this->getSleep() ? F("On") : F("Off")); + result += F(", Swing: "); + result += (this->getSwing() ? F("On") : F("Off")); + return result; +} + +#if DECODE_TECO +// Decode the supplied Teco message. +// +// Args: +// results: Ptr to the data to decode and where to store the decode result. +// nbits: The number of data bits to expect. Typically kTecoBits. +// strict: Flag indicating if we should perform strict matching. +// Returns: +// boolean: True if it can decode it, false if it can't. +// +// Status: STABLE / Tested. +bool IRrecv::decodeTeco(decode_results* results, uint16_t nbits, bool strict) { + // Check if can possibly be a valid Teco message. + if (results->rawlen < 2 * nbits + kHeader + kFooter - 1) return false; + if (strict && nbits != kTecoBits) return false; // Not what is expected + + uint64_t data = 0; + uint16_t offset = kStartOffset; + match_result_t data_result; + + // Header + if (!matchMark(results->rawbuf[offset++], kTecoHdrMark)) return false; + if (!matchSpace(results->rawbuf[offset++], kTecoHdrSpace)) return false; + // Data (35 bits) + data_result = + matchData(&(results->rawbuf[offset]), 35, kTecoBitMark, kTecoOneSpace, + kTecoBitMark, kTecoZeroSpace, kTolerance, kMarkExcess, false); + if (data_result.success == false) return false; + data = data_result.data; + offset += data_result.used; + uint16_t actualBits = data_result.used / 2; + + // Footer. + if (!matchMark(results->rawbuf[offset++], kTecoBitMark)) return false; + if (offset < results->rawlen && + !matchAtLeast(results->rawbuf[offset], kTecoGap)) return false; + + // Compliance + if (actualBits < nbits) return false; + if (strict && actualBits != nbits) return false; // Not as we expected. + + // Success + results->decode_type = TECO; + results->bits = actualBits; + results->value = data; + results->address = 0; + results->command = 0; + return true; +} +#endif // DECODE_TECO diff --git a/lib/IRremoteESP8266-2.6.0/src/ir_Teco.h b/lib/IRremoteESP8266-2.6.0/src/ir_Teco.h new file mode 100644 index 000000000000..65a0050ae251 --- /dev/null +++ b/lib/IRremoteESP8266-2.6.0/src/ir_Teco.h @@ -0,0 +1,144 @@ +// Copyright 2019 Fabien Valthier + +#ifndef IR_TECO_H_ +#define IR_TECO_H_ + +#ifndef UNIT_TEST +#include +#else +#include +#endif +#include "IRremoteESP8266.h" +#include "IRsend.h" +#ifdef UNIT_TEST +#include "IRsend_test.h" +#endif + +// Constants. Using LSB to be able to send only 35bits. +const uint8_t kTecoAuto = 0; // 0b000 +const uint8_t kTecoCool = 1; // 0b001 +const uint8_t kTecoDry = 2; // 0b010 +const uint8_t kTecoFan = 3; // 0b110 +const uint8_t kTecoHeat = 4; // 0b001 +const uint8_t kTecoFanAuto = 0; // 0b00 +const uint8_t kTecoFanHigh = 3; // 0b11 +const uint8_t kTecoFanMed = 2; // 0b10 +const uint8_t kTecoFanLow = 1; // 0b01 +const uint8_t kTecoMinTemp = 16; // 16C +const uint8_t kTecoMaxTemp = 30; // 30C + +const uint64_t kTecoModeMask = 0b00000000000000000000000000000000111; +const uint64_t kTecoPower = 0b00000000000000000000000000000001000; +const uint64_t kTecoFanMask = 0b00000000000000000000000000000110000; +const uint64_t kTecoSwing = 0b00000000000000000000000000001000000; +const uint64_t kTecoSleep = 0b00000000000000000000000000010000000; +const uint64_t kTecoTempMask = 0b00000000000000000000000111100000000; +const uint64_t kTecoTimerHalfH = 0b00000000000000000000001000000000000; +const uint64_t kTecoTimerTenHr = 0b00000000000000000000110000000000000; +const uint64_t kTecoTimerOn = 0b00000000000000000001000000000000000; +const uint64_t kTecoTimerUniHr = 0b00000000000000011110000000000000000; +const uint64_t kTecoReset = 0b01001010000000000000010000000000000; +/* + (header mark and space) + Teco AC map read and to be sent in LSB with number of bits + + byte 0 = Cst 0x02 + byte 1 = Cst 0x50 + byte 2: + b0-3 = 0b0000 + b4-7 = Timer hours (unit, not thenth) + hours: + 0000 (0) = +0 hour + 0001 (1) = +1 hour + ... + 1001 (9) = +9 hours + byte 3: = timer and Temperature + b0 = Timer (1 = On, 0 = Off) + b1-2 = Timer - number of 10hours + 10Hours: + 00 = 0 * 10hours of timer + 01 = 1 * 10 hours of timer + 10 = 2 * 10hours of timer + b3 = Timer - half hour (1=half hour on, 0 = round hour) + b4-7: Degrees C. + 0000 (0) = 16C + 0001 (1) = 17C + 0010 (2) = 18C + ... + 1101 (13) = 29C + 1110 (14) = 30C + byte 4: Basics + b0 = Sleep Mode (1 = On, 0 = Off) + b1 = Vent swing (1 = On, 0 = Off) + b2-3 = Fan + Fan: + 00 = Auto + 01 = Fan 1 + 10 = Fan 2 + 11 = Fan 3 or higher + b4 = Power Status (1 = On, 0 = Off) + b5-7 = Modes LSB first + Modes: + 000 = Auto (temp = 25C) + 001 = Cool + 010 = Dry (temp = 25C, but not shown) + 011 = Fan + 100 = Heat +*/ + +// Classes +class IRTecoAc { + public: + explicit IRTecoAc(const uint16_t pin); + + void stateReset(void); +#if SEND_TECO + void send(const uint16_t repeat = kTecoDefaultRepeat); +#endif // SEND_TECO + void begin(void); + void on(void); + void off(void); + + void setPower(const bool on); + bool getPower(void); + + void setTemp(const uint8_t temp); + uint8_t getTemp(void); + + void setFan(const uint8_t fan); + uint8_t getFan(void); + + void setMode(const uint8_t mode); + uint8_t getMode(void); + + void setSwing(const bool state); + bool getSwing(void); + + void setSleep(const bool state); + bool getSleep(void); + + // void setTimer(uint8_t time); // To check unit + // uint8_t getTimer(uint8_t); + + uint64_t getRaw(void); + void setRaw(const uint64_t new_code); + + uint8_t convertMode(const stdAc::opmode_t mode); + uint8_t convertFan(const stdAc::fanspeed_t speed); +#ifdef ARDUINO + String toString(void); +#else + std::string toString(void); +#endif +#ifndef UNIT_TEST + + private: + IRsend _irsend; +#else + IRsendTest _irsend; +#endif + // The state of the IR remote in IR code form. + uint64_t remote_state; +}; + +#endif // IR_TECO_H_ diff --git a/lib/IRremoteESP8266-2.5.2.03/src/ir_Toshiba.cpp b/lib/IRremoteESP8266-2.6.0/src/ir_Toshiba.cpp similarity index 86% rename from lib/IRremoteESP8266-2.5.2.03/src/ir_Toshiba.cpp rename to lib/IRremoteESP8266-2.6.0/src/ir_Toshiba.cpp index 817b5fbaa598..a82a2fb24d93 100644 --- a/lib/IRremoteESP8266-2.5.2.03/src/ir_Toshiba.cpp +++ b/lib/IRremoteESP8266-2.6.0/src/ir_Toshiba.cpp @@ -89,9 +89,9 @@ void IRToshibaAC::begin() { _irsend.begin(); } #if SEND_TOSHIBA_AC // Send the current desired state to the IR LED. -void IRToshibaAC::send() { +void IRToshibaAC::send(const uint16_t repeat) { checksum(); // Ensure correct checksum before sending. - _irsend.sendToshibaAC(remote_state); + _irsend.sendToshibaAC(remote_state, kToshibaACStateLength, repeat); } #endif // SEND_TOSHIBA_AC @@ -233,6 +233,39 @@ void IRToshibaAC::setMode(uint8_t mode) { } } +// Convert a standard A/C mode into its native mode. +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; + // No Fan mode. + default: + return kToshibaAcAuto; + } +} + +// Convert a standard A/C Fan speed into its native fan speed. +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 the internal state into a human readable string. #ifdef ARDUINO String IRToshibaAC::toString() { @@ -241,36 +274,39 @@ String IRToshibaAC::toString() { std::string IRToshibaAC::toString() { std::string result = ""; #endif // ARDUINO - result += "Power: "; + result += F("Power: "); if (getPower()) - result += "On"; + result += F("On"); else - result += "Off"; - result += ", Mode: " + uint64ToString(getMode()); + result += F("Off"); + result += F(", Mode: "); + result += uint64ToString(getMode()); switch (getMode()) { case kToshibaAcAuto: - result += " (AUTO)"; + result += F(" (AUTO)"); break; case kToshibaAcCool: - result += " (COOL)"; + result += F(" (COOL)"); break; case kToshibaAcHeat: - result += " (HEAT)"; + result += F(" (HEAT)"); break; case kToshibaAcDry: - result += " (DRY)"; + result += F(" (DRY)"); break; default: - result += " (UNKNOWN)"; + result += F(" (UNKNOWN)"); } - result += ", Temp: " + uint64ToString(getTemp()) + "C"; - result += ", Fan: " + uint64ToString(getFan()); + result += F(", Temp: "); + result += uint64ToString(getTemp()); + result += F("C, Fan: "); + result += uint64ToString(getFan()); switch (getFan()) { case kToshibaAcFanAuto: - result += " (AUTO)"; + result += F(" (AUTO)"); break; case kToshibaAcFanMax: - result += " (MAX)"; + result += F(" (MAX)"); break; } return result; diff --git a/lib/IRremoteESP8266-2.5.2.03/src/ir_Toshiba.h b/lib/IRremoteESP8266-2.6.0/src/ir_Toshiba.h similarity index 90% rename from lib/IRremoteESP8266-2.5.2.03/src/ir_Toshiba.h rename to lib/IRremoteESP8266-2.6.0/src/ir_Toshiba.h index 1a1e6cdc81a1..03b461add8ec 100644 --- a/lib/IRremoteESP8266-2.5.2.03/src/ir_Toshiba.h +++ b/lib/IRremoteESP8266-2.6.0/src/ir_Toshiba.h @@ -11,6 +11,9 @@ #endif #include "IRremoteESP8266.h" #include "IRsend.h" +#ifdef UNIT_TEST +#include "IRsend_test.h" +#endif // TTTTTTT OOOOO SSSSS HH HH IIIII BBBBB AAA // TTT OO OO SS HH HH III BB B AAAAA @@ -48,7 +51,7 @@ class IRToshibaAC { void stateReset(); #if SEND_TOSHIBA_AC - void send(); + void send(const uint16_t repeat = kToshibaACMinRepeat); #endif // SEND_TOSHIBA_AC void begin(); void on(); @@ -65,6 +68,8 @@ class IRToshibaAC { uint8_t* getRaw(); static bool validChecksum(const uint8_t state[], const uint16_t length = kToshibaACStateLength); + uint8_t convertMode(const stdAc::opmode_t mode); + uint8_t convertFan(const stdAc::fanspeed_t speed); #ifdef ARDUINO String toString(); #else @@ -73,13 +78,15 @@ class IRToshibaAC { #ifndef UNIT_TEST private: + IRsend _irsend; +#else + IRsendTest _irsend; #endif uint8_t remote_state[kToshibaACStateLength]; void checksum(const uint16_t length = kToshibaACStateLength); static uint8_t calcChecksum(const uint8_t state[], const uint16_t length = kToshibaACStateLength); uint8_t mode_state; - IRsend _irsend; }; #endif // IR_TOSHIBA_H_ diff --git a/lib/IRremoteESP8266-2.6.0/src/ir_Trotec.cpp b/lib/IRremoteESP8266-2.6.0/src/ir_Trotec.cpp new file mode 100644 index 000000000000..b5c15e7fdbd1 --- /dev/null +++ b/lib/IRremoteESP8266-2.6.0/src/ir_Trotec.cpp @@ -0,0 +1,162 @@ +// Copyright 2017 stufisher + +#include "ir_Trotec.h" +#include +#include "IRremoteESP8266.h" +#include "IRutils.h" + +// Constants +const uint16_t kTrotecHdrMark = 5952; +const uint16_t kTrotecHdrSpace = 7364; +const uint16_t kTrotecOneMark = 592; +const uint16_t kTrotecOneSpace = 1560; +const uint16_t kTrotecZeroMark = 592; +const uint16_t kTrotecZeroSpace = 592; +const uint16_t kTrotecGap = 6184; +const uint16_t kTrotecGapEnd = 1500; // made up value + +#if SEND_TROTEC + +void IRsend::sendTrotec(unsigned char data[], uint16_t nbytes, + uint16_t repeat) { + if (nbytes < kTrotecStateLength) return; + + for (uint16_t r = 0; r <= repeat; r++) { + sendGeneric(kTrotecHdrMark, kTrotecHdrSpace, kTrotecOneMark, + kTrotecOneSpace, kTrotecZeroMark, kTrotecZeroSpace, + kTrotecOneMark, kTrotecGap, data, nbytes, 36, false, + 0, // Repeats handled elsewhere + 50); + // More footer + enableIROut(36); + mark(kTrotecOneMark); + space(kTrotecGapEnd); + } +} +#endif // SEND_TROTEC + +IRTrotecESP::IRTrotecESP(uint16_t pin) : _irsend(pin) { stateReset(); } + +void IRTrotecESP::begin() { _irsend.begin(); } + +#if SEND_TROTEC +void IRTrotecESP::send(const uint16_t repeat) { + checksum(); + _irsend.sendTrotec(remote_state, kTrotecStateLength, repeat); +} +#endif // SEND_TROTEC + +void IRTrotecESP::checksum() { + uint8_t sum = 0; + + for (uint8_t i = 2; i < 8; i++) sum += remote_state[i]; + remote_state[8] = sum & 0xFF; +} + +void IRTrotecESP::stateReset() { + for (uint8_t i = 2; i < kTrotecStateLength; i++) remote_state[i] = 0x0; + + remote_state[0] = kTrotecIntro1; + remote_state[1] = kTrotecIntro2; + + setPower(false); + setTemp(kTrotecDefTemp); + setSpeed(kTrotecFanMed); + setMode(kTrotecAuto); +} + +uint8_t* IRTrotecESP::getRaw() { + checksum(); + return remote_state; +} + +void IRTrotecESP::setPower(const bool on) { + if (on) + remote_state[2] |= kTrotecPowerBit; + else + remote_state[2] &= ~kTrotecPowerBit; +} + +bool IRTrotecESP::getPower() { return remote_state[2] & kTrotecPowerBit; } + +void IRTrotecESP::setSpeed(const uint8_t fan) { + uint8_t speed = std::min(fan, kTrotecFanHigh); + remote_state[2] = (remote_state[2] & 0b11001111) | (speed << 4); +} + +uint8_t IRTrotecESP::getSpeed() { return (remote_state[2] & 0b00110000) >> 4; } + +void IRTrotecESP::setMode(const uint8_t mode) { + switch (mode) { + case kTrotecAuto: + case kTrotecCool: + case kTrotecDry: + case kTrotecFan: + remote_state[2] = (remote_state[2] & 0b11111100) | mode; + return; + default: + this->setMode(kTrotecAuto); + } +} + +uint8_t IRTrotecESP::getMode() { return remote_state[2] & 0b00000011; } + +void IRTrotecESP::setTemp(const uint8_t celsius) { + uint8_t temp = std::max(celsius, kTrotecMinTemp); + temp = std::min(temp, kTrotecMaxTemp); + remote_state[3] = (remote_state[3] & 0x80) | (temp - kTrotecMinTemp); +} + +uint8_t IRTrotecESP::getTemp() { + return (remote_state[3] & 0b01111111) + kTrotecMinTemp; +} + +void IRTrotecESP::setSleep(bool sleep) { + if (sleep) + remote_state[3] |= kTrotecSleepBit; + else + remote_state[3] &= ~kTrotecSleepBit; +} + +bool IRTrotecESP::getSleep(void) { return remote_state[3] & kTrotecSleepBit; } + +void IRTrotecESP::setTimer(const uint8_t timer) { + if (timer) + remote_state[5] |= kTrotecTimerBit; + else + remote_state[5] &= ~kTrotecTimerBit; + remote_state[6] = (timer > kTrotecMaxTimer) ? kTrotecMaxTimer : timer; +} + +uint8_t IRTrotecESP::getTimer() { return remote_state[6]; } + +// Convert a standard A/C mode into its native mode. +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 standard A/C Fan speed into its native fan speed. +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; + } +} diff --git a/lib/IRremoteESP8266-2.5.2.03/src/ir_Trotec.h b/lib/IRremoteESP8266-2.6.0/src/ir_Trotec.h similarity index 68% rename from lib/IRremoteESP8266-2.5.2.03/src/ir_Trotec.h rename to lib/IRremoteESP8266-2.6.0/src/ir_Trotec.h index 040d9a722d5d..dfbc26c074ca 100644 --- a/lib/IRremoteESP8266-2.5.2.03/src/ir_Trotec.h +++ b/lib/IRremoteESP8266-2.6.0/src/ir_Trotec.h @@ -5,6 +5,9 @@ #include "IRremoteESP8266.h" #include "IRsend.h" +#ifdef UNIT_TEST +#include "IRsend_test.h" +#endif // Constants // Byte 0 @@ -19,8 +22,7 @@ const uint8_t kTrotecCool = 1; const uint8_t kTrotecDry = 2; const uint8_t kTrotecFan = 3; -const uint8_t kTrotecOn = 1; -const uint8_t kTrotecOff = 0; +const uint8_t kTrotecPowerBit = 0b00001000; const uint8_t kTrotecFanLow = 1; const uint8_t kTrotecFanMed = 2; @@ -31,13 +33,12 @@ const uint8_t kTrotecMinTemp = 18; const uint8_t kTrotecDefTemp = 25; const uint8_t kTrotecMaxTemp = 32; -const uint8_t kTrotecSleepOn = 1; +const uint8_t kTrotecSleepBit = 0b10000000; // Byte 5 -const uint8_t kTrotecTimerOn = 1; +const uint8_t kTrotecTimerBit = 0b01000000; // Byte 6 -const uint8_t kTrotecMinTimer = 0; const uint8_t kTrotecMaxTimer = 23; // Legacy defines. (Deperecated) @@ -50,7 +51,6 @@ const uint8_t kTrotecMaxTimer = 23; #define TROTEC_FAN_HIGH kTrotecFanHigh #define TROTEC_MIN_TEMP kTrotecMinTemp #define TROTEC_MAX_TEMP kTrotecMaxTemp -#define TROTEC_MIN_TIMER kTrotecMinTimer #define TROTEC_MAX_TIMER kTrotecMaxTimer class IRTrotecESP { @@ -58,35 +58,42 @@ class IRTrotecESP { explicit IRTrotecESP(uint16_t pin); #if SEND_TROTEC - void send(); + void send(const uint16_t repeat = kTrotecDefaultRepeat); #endif // SEND_TROTEC void begin(); - void setPower(bool state); - uint8_t getPower(); + void setPower(const bool state); + bool getPower(); - void setTemp(uint8_t temp); + void setTemp(const uint8_t celsius); uint8_t getTemp(); - void setSpeed(uint8_t fan); + void setSpeed(const uint8_t fan); uint8_t getSpeed(); uint8_t getMode(); - void setMode(uint8_t mode); + void setMode(const uint8_t mode); bool getSleep(); void setSleep(bool sleep); uint8_t getTimer(); - void setTimer(uint8_t timer); + void setTimer(const uint8_t timer); uint8_t* getRaw(); + uint8_t convertMode(const stdAc::opmode_t mode); + uint8_t convertFan(const stdAc::fanspeed_t speed); +#ifndef UNIT_TEST + private: - uint8_t trotec[kTrotecStateLength]; + IRsend _irsend; +#else + IRsendTest _irsend; +#endif + uint8_t remote_state[kTrotecStateLength]; void stateReset(); void checksum(); - IRsend _irsend; }; #endif // IR_TROTEC_H_ diff --git a/lib/IRremoteESP8266-2.6.0/src/ir_Vestel.cpp b/lib/IRremoteESP8266-2.6.0/src/ir_Vestel.cpp new file mode 100644 index 000000000000..1fbb822cf65d --- /dev/null +++ b/lib/IRremoteESP8266-2.6.0/src/ir_Vestel.cpp @@ -0,0 +1,583 @@ +// Copyright 2018 Erdem U. Altinyurt +// Copyright 2019 David Conran + +#include "ir_Vestel.h" +#include +#ifndef UNIT_TEST +#include +#else +#include +#endif +#include "IRrecv.h" +#include "IRremoteESP8266.h" +#include "IRsend.h" +#include "IRutils.h" +#include "ir_Haier.h" + +// VV VV EEEEEEE SSSSS TTTTTTTT EEEEEEE LL +// VV VV EE S TT EE LL +// VV VV EEEEE SSSS TT EEEEE LL +// VV VV EE S TT EE LL +// VVV EEEEEEE SSSSS TT EEEEEEE LLLLLLL + +// Vestel added by Erdem U. Altinyurt + +// Equipment it seems compatible with: +// * Vestel AC Model BIOX CXP-9 (9K BTU) +// * + +// Ref: +// None. Totally reverse engineered. + +#if SEND_VESTEL_AC +// Send a Vestel message +// +// Args: +// data: Contents of the message to be sent. +// nbits: Nr. of bits of data to be sent. Typically kVestelBits. +// +// Status: STABLE / Working. +// +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 + +// Code to emulate Vestel A/C IR remote control unit. + +// Initialise the object. +IRVestelAc::IRVestelAc(uint16_t pin) : _irsend(pin) { stateReset(); } + +// Reset the state of the remote to a known good state/sequence. +void IRVestelAc::stateReset() { + // Power On, Mode Auto, Fan Auto, Temp = 25C/77F + remote_state = kVestelAcStateDefault; + remote_time_state = kVestelAcTimeStateDefault; + use_time_state = false; +} + +// Configure the pin for output. +void IRVestelAc::begin() { + _irsend.begin(); +} + +#if SEND_VESTEL_AC +// Send the current desired state to the IR LED. +void IRVestelAc::send() { + checksum(); // Ensure correct checksum before sending. + uint64_t code_to_send; + if (use_time_state) + code_to_send = remote_time_state; + else + code_to_send = remote_state; + _irsend.sendVestelAc(code_to_send); +} +#endif // SEND_VESTEL_AC + +// Return the internal state date of the remote. +uint64_t IRVestelAc::getRaw() { + checksum(); + if (use_time_state) return remote_time_state; + return remote_state; +} + +// Override the internal state with the new state. +void IRVestelAc::setRaw(uint8_t* newState) { + uint64_t upState = 0; + for (int i = 0; i < 7; i++) + upState |= static_cast(newState[i]) << (i * 8); + this->setRaw(upState); +} + +void IRVestelAc::setRaw(const uint64_t newState) { + use_time_state = false; + remote_state = newState; + remote_time_state = newState; + if (this->isTimeCommand()) { + use_time_state = true; + remote_state = kVestelAcStateDefault; + } else { + remote_time_state = kVestelAcTimeStateDefault; + } +} + +// Set the requested power state of the A/C to on. +void IRVestelAc::on() { setPower(true); } + +// Set the requested power state of the A/C to off. +void IRVestelAc::off() { setPower(false); } + +// Set the requested power state of the A/C. +void IRVestelAc::setPower(const bool state) { + remote_state &= ~((uint64_t)0xF << kVestelAcPowerOffset); + if (state) + remote_state |= ((uint64_t)0xF << kVestelAcPowerOffset); + else + remote_state |= ((uint64_t)0xC << kVestelAcPowerOffset); + use_time_state = false; +} + +// Return the requested power state of the A/C. +bool IRVestelAc::getPower() { + return (remote_state >> kVestelAcPowerOffset == 0xF); +} + +// Set the temperature in Celsius degrees. +void IRVestelAc::setTemp(const uint8_t temp) { + uint8_t new_temp = temp; + new_temp = std::max(kVestelAcMinTempC, new_temp); + // new_temp = std::max(kVestelAcMinTempH, new_temp); Check MODE + new_temp = std::min(kVestelAcMaxTemp, new_temp); + remote_state &= ~((uint64_t)0xF << kVestelAcTempOffset); + remote_state |= (uint64_t)(new_temp - 16) << kVestelAcTempOffset; + use_time_state = false; +} + +// Return the set temperature. +uint8_t IRVestelAc::getTemp(void) { + return ((remote_state >> kVestelAcTempOffset) & 0xF) + 16; +} + +// Set the speed of the fan, +// 1-3 set the fan speed, 0 or anything else set it to auto. +void IRVestelAc::setFan(const uint8_t fan) { + switch (fan) { + case kVestelAcFanLow: + case kVestelAcFanMed: + case kVestelAcFanHigh: + case kVestelAcFanAutoCool: + case kVestelAcFanAutoHot: + case kVestelAcFanAuto: + remote_state &= ~((uint64_t)0xF << kVestelAcFanOffset); + remote_state |= (uint64_t)fan << kVestelAcFanOffset; + break; + default: + setFan(kVestelAcFanAuto); + } + use_time_state = false; +} + +// Return the requested state of the unit's fan. +uint8_t IRVestelAc::getFan() { + return (remote_state >> kVestelAcFanOffset) & 0xF; +} + +// Get the requested climate operation mode of the a/c unit. +// Returns: +// A uint8_t containing the A/C mode. +uint8_t IRVestelAc::getMode() { + return (remote_state >> kVestelAcModeOffset) & 0xF; +} + +// Set the requested climate operation mode of the a/c unit. +void IRVestelAc::setMode(const uint8_t mode) { + // If we get an unexpected mode, default to AUTO. + switch (mode) { + case kVestelAcAuto: + case kVestelAcCool: + case kVestelAcHeat: + case kVestelAcDry: + case kVestelAcFan: + remote_state &= ~((uint64_t)0xF << kVestelAcModeOffset); + remote_state |= (uint64_t)mode << kVestelAcModeOffset; + break; + default: + setMode(kVestelAcAuto); + } + use_time_state = false; +} + +// Set Auto mode of AC. +void IRVestelAc::setAuto(const int8_t autoLevel) { + if (autoLevel < -2 || autoLevel > 2) return; + setMode(kVestelAcAuto); + setFan((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); +} + +void IRVestelAc::setTimerActive(const bool on) { + if (on) // activation + remote_time_state |= ((uint64_t)1 << kVestelAcTimerFlagOffset); + else // deactivate + remote_time_state &= ~((uint64_t)1 << kVestelAcTimerFlagOffset); + use_time_state = true; +} + +bool IRVestelAc::isTimerActive(void) { + return (remote_time_state >> kVestelAcTimerFlagOffset) & 1; +} + +// Set Timer option of AC. +// Valid time arguments are 0, 0.5, 1, 2, 3 and 5 hours (in min). 0 disables the +// timer. +void IRVestelAc::setTimer(const uint16_t minutes) { + // Clear both On & Off timers. + remote_time_state &= ~((uint64_t)0xFFFF << kVestelAcOffTimeOffset); + // Set the "Off" time with the nr of minutes before we turn off. + remote_time_state |= (uint64_t)(((minutes / 60) << 3) + (minutes % 60) / 10) + << kVestelAcOffTimeOffset; + setOffTimerActive(false); + // Yes. On Timer instead of Off timer active. + setOnTimerActive(minutes != 0); + setTimerActive(minutes != 0); + use_time_state = true; +} + +uint16_t IRVestelAc::getTimer(void) { return getOffTimer(); } + +// Set the AC's internal clock +void IRVestelAc::setTime(const uint16_t minutes) { + remote_time_state &= ~((uint64_t)0x1F << kVestelAcHourOffset); + remote_time_state |= (uint64_t)((minutes / 60) & 0x1F) + << kVestelAcHourOffset; + remote_time_state &= ~((uint64_t)0xFF << kVestelAcMinuteOffset); + remote_time_state |= (uint64_t)((minutes % 60) & 0xFF) + << kVestelAcMinuteOffset; + use_time_state = true; +} + +uint16_t IRVestelAc::getTime(void) { + return ((remote_time_state >> kVestelAcHourOffset) & 0x1F) * 60 + + ((remote_time_state >> kVestelAcMinuteOffset) & 0xFF); +} + +void IRVestelAc::setOnTimerActive(const bool on) { + if (on) // activation + remote_time_state |= ((uint64_t)1 << kVestelAcOnTimerFlagOffset); + else // deactivate + remote_time_state &= ~((uint64_t)1 << kVestelAcOnTimerFlagOffset); + use_time_state = true; +} + +bool IRVestelAc::isOnTimerActive(void) { + return (remote_time_state >> kVestelAcOnTimerFlagOffset) & 1; +} + +// Set AC's wake up time. Takes time in minute. +void IRVestelAc::setOnTimer(const uint16_t minutes) { + remote_time_state &= ~((uint64_t)0xFF << kVestelAcOnTimeOffset); + remote_time_state |= (uint64_t)(((minutes / 60) << 3) + (minutes % 60) / 10) + << kVestelAcOnTimeOffset; + setOnTimerActive(minutes != 0); + setTimerActive(false); + use_time_state = true; +} + +uint16_t IRVestelAc::getOnTimer(void) { + uint8_t ontime = (remote_time_state >> kVestelAcOnTimeOffset) & 0xFF; + return (ontime >> 3) * 60 + (ontime & 0x7) * 10; +} + +void IRVestelAc::setOffTimerActive(const bool on) { + if (on) // activation + remote_time_state |= ((uint64_t)1 << kVestelAcOffTimerFlagOffset); + else // deactivate + remote_time_state &= ~((uint64_t)1 << kVestelAcOffTimerFlagOffset); + use_time_state = true; +} + +bool IRVestelAc::isOffTimerActive(void) { + return (remote_time_state >> kVestelAcOffTimerFlagOffset) & 1; +} + +// Set AC's turn off time. Takes time in minute. +void IRVestelAc::setOffTimer(const uint16_t minutes) { + remote_time_state &= ~((uint64_t)0xFF << kVestelAcOffTimeOffset); + remote_time_state |= + (uint64_t)((((minutes / 60) << 3) + (minutes % 60) / 10) & 0xFF) + << kVestelAcOffTimeOffset; + setOffTimerActive(minutes != 0); + setTimerActive(false); + use_time_state = true; +} + +uint16_t IRVestelAc::getOffTimer(void) { + uint8_t offtime = (remote_time_state >> kVestelAcOffTimeOffset) & 0xFF; + return (offtime >> 3) * 60 + (offtime & 0x7) * 10; +} + +// Set the Sleep state of the A/C. +void IRVestelAc::setSleep(const bool state) { + remote_state &= ~((uint64_t)0xF << kVestelAcTurboSleepOffset); + remote_state |= (uint64_t)(state ? kVestelAcSleep : kVestelAcNormal) + << kVestelAcTurboSleepOffset; + use_time_state = false; +} + +// Return the Sleep state of the A/C. +bool IRVestelAc::getSleep() { + return ((remote_state >> kVestelAcTurboSleepOffset) & 0xF) == kVestelAcSleep; +} + +// Set the Turbo state of the A/C. +void IRVestelAc::setTurbo(const bool state) { + remote_state &= ~((uint64_t)0xF << kVestelAcTurboSleepOffset); + remote_state |= (uint64_t)(state ? kVestelAcTurbo : kVestelAcNormal) + << kVestelAcTurboSleepOffset; + use_time_state = false; +} + +// Return the Turbo state of the A/C. +bool IRVestelAc::getTurbo() { + return ((remote_state >> kVestelAcTurboSleepOffset) & 0xF) == kVestelAcTurbo; +} + +// Set the Ion state of the A/C. +void IRVestelAc::setIon(const bool state) { + remote_state &= ~((uint64_t)0x1 << kVestelAcIonOffset); + + remote_state |= (uint64_t)(state ? 1 : 0) << kVestelAcIonOffset; + use_time_state = false; +} + +// Return the Ion state of the A/C. +bool IRVestelAc::getIon() { return (remote_state >> kVestelAcIonOffset) & 1; } + +// Set the Swing Roaming state of the A/C. +void IRVestelAc::setSwing(const bool state) { + remote_state &= ~((uint64_t)0xF << kVestelAcSwingOffset); + + remote_state |= (uint64_t)(state ? kVestelAcSwing : 0xF) + << kVestelAcSwingOffset; + use_time_state = false; +} + +// Return the Swing Roaming state of the A/C. +bool IRVestelAc::getSwing() { + return ((remote_state >> kVestelAcSwingOffset) & 0xF) == kVestelAcSwing; +} + +// Calculate the checksum for a given array. +// Args: +// state: The state to calculate the checksum over. +// Returns: +// The 8 bit checksum value. +uint8_t IRVestelAc::calcChecksum(const uint64_t state) { + // Just counts the set bits +1 on stream and take inverse after mask + uint8_t sum = 0; + uint64_t temp_state = state & kVestelAcCRCMask; + for (; temp_state; temp_state >>= 1) + if (temp_state & 1) sum++; + sum += 2; + sum = 0xff - sum; + return sum; +} + +// Verify the checksum is valid for a given state. +// Args: +// state: The state to verify the checksum of. +// Returns: +// A boolean. +bool IRVestelAc::validChecksum(const uint64_t state) { + return (((state >> kVestelAcChecksumOffset) & 0xFF) == calcChecksum(state)); +} + +// Calculate & set the checksum for the current internal state of the remote. +void IRVestelAc::checksum() { + // Stored the checksum value in the last byte. + remote_state &= ~((uint64_t)0xFF << kVestelAcChecksumOffset); + remote_state |= (uint64_t)calcChecksum(remote_state) + << kVestelAcChecksumOffset; + + remote_time_state &= ~((uint64_t)0xFF << kVestelAcChecksumOffset); + remote_time_state |= (uint64_t)calcChecksum(remote_time_state) + << kVestelAcChecksumOffset; +} + +bool IRVestelAc::isTimeCommand() { + return (remote_state >> kVestelAcPowerOffset == 0x00 || use_time_state); +} + + +// Convert a standard A/C mode into its native mode. +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 standard A/C Fan speed into its native fan speed. +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 the internal state into a human readable string. +#ifdef ARDUINO +String IRVestelAc::toString() { + String result = ""; +#else +std::string IRVestelAc::toString() { + std::string result = ""; +#endif // ARDUINO + if (isTimeCommand()) { + result += F("Time: "); + result += IRHaierAC::timeToString(getTime()); + + result += F(", Timer: "); + result += isTimerActive() ? IRHaierAC::timeToString(getTimer()) : F("Off"); + + result += F(", On Timer: "); + result += (isOnTimerActive() && !isTimerActive()) + ? IRHaierAC::timeToString(getOnTimer()) + : F("Off"); + + result += F(", Off Timer: "); + result += + isOffTimerActive() ? IRHaierAC::timeToString(getOffTimer()) : F("Off"); + return result; + } + // Not a time command, it's a normal command. + result += F("Power: "); + result += (getPower() ? F("On") : F("Off")); + result += F(", Mode: "); + result += uint64ToString(getMode()); + switch (getMode()) { + case kVestelAcAuto: + result += F(" (AUTO)"); + break; + case kVestelAcCool: + result += F(" (COOL)"); + break; + case kVestelAcHeat: + result += F(" (HEAT)"); + break; + case kVestelAcDry: + result += F(" (DRY)"); + break; + case kVestelAcFan: + result += F(" (FAN)"); + break; + default: + result += F(" (UNKNOWN)"); + } + result += F(", Temp: "); + result += uint64ToString(getTemp()); + result += F("C, Fan: "); + result += uint64ToString(getFan()); + switch (getFan()) { + case kVestelAcFanAuto: + result += F(" (AUTO)"); + break; + case kVestelAcFanLow: + result += F(" (LOW)"); + break; + case kVestelAcFanMed: + result += F(" (MEDIUM)"); + break; + case kVestelAcFanHigh: + result += F(" (HIGH)"); + break; + case kVestelAcFanAutoCool: + result += F(" (AUTO COOL)"); + break; + case kVestelAcFanAutoHot: + result += F(" (AUTO HOT)"); + break; + default: + result += F(" (UNKNOWN)"); + } + result += F(", Sleep: "); + result += (getSleep() ? F("On") : F("Off")); + result += F(", Turbo: "); + result += (getTurbo() ? F("On") : F("Off")); + result += F(", Ion: "); + result += (getIon() ? F("On") : F("Off")); + result += F(", Swing: "); + result += (getSwing() ? F("On") : F("Off")); + return result; +} + +#if DECODE_VESTEL_AC +// Decode the supplied Vestel message. +// +// Args: +// results: Ptr to the data to decode and where to store the decode result. +// nbits: The number of data bits to expect. Typically kVestelBits. +// strict: Flag indicating if we should perform strict matching. +// Returns: +// boolean: True if it can decode it, false if it can't. +// +// Status: Alpha / Needs testing against a real device. +// +bool IRrecv::decodeVestelAc(decode_results* results, uint16_t nbits, + 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; + uint16_t offset = kStartOffset; + + if (nbits > sizeof(data) * 8) + return false; // We can't possibly capture a Vestel packet that big. + + // Header + if (!matchMark(results->rawbuf[offset++], kVestelAcHdrMark)) return false; + if (!matchSpace(results->rawbuf[offset++], kVestelAcHdrSpace)) return false; + + // Data (Normal) + match_result_t data_result = + matchData(&(results->rawbuf[offset]), nbits, kVestelAcBitMark, + kVestelAcOneSpace, kVestelAcBitMark, kVestelAcZeroSpace, + kVestelAcTolerance, kMarkExcess, false); + + if (data_result.success == false) return false; + offset += data_result.used; + data = data_result.data; + + // Footer + if (!matchMark(results->rawbuf[offset++], kVestelAcBitMark)) return false; + + // Compliance + if (strict) + if (!IRVestelAc::validChecksum(data_result.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-2.6.0/src/ir_Vestel.h b/lib/IRremoteESP8266-2.6.0/src/ir_Vestel.h new file mode 100644 index 000000000000..ab04e8b35a83 --- /dev/null +++ b/lib/IRremoteESP8266-2.6.0/src/ir_Vestel.h @@ -0,0 +1,177 @@ +// Copyright 2018 Erdem U. Altinyurt +// Copyright 2019 David Conran + +#ifndef IR_VESTEL_H_ +#define IR_VESTEL_H_ + +#define __STDC_LIMIT_MACROS +#include +#ifdef ARDUINO +#include +#else +#include +#endif +#include "IRremoteESP8266.h" +#include "IRsend.h" +#ifdef UNIT_TEST +#include "IRsend_test.h" +#endif + +// VV VV EEEEEEE SSSSS TTTTTTTT EEEEEEE LL +// VV VV EE S TT EE LL +// VV VV EEEEE SSSS TT EEEEE LL +// VV VV EE S TT EE LL +// VVV EEEEEEE SSSSS TT EEEEEEE LLLLLLL + +// Vestel added by Erdem U. Altinyurt + +// Structure of a Command message (56 bits) +// Signature: 12 bits. e.g. 0x201 +// Checksum: 8 bits +// Swing: 4 bits. (auto 0xA, stop 0xF) +// turbo_sleep_normal: 4bits. (normal 0x1, sleep 0x3, turbo 0x7) +// Unused: 8 bits. (0x00) +// Temperature: 4 bits. (Celcius, but offset by -16 degrees. e.g. 0x0 = 16C) +// Fan Speed: 4 bits (auto 0x1, low 0x5, mid 0x9, high 0xB, 0xD auto hot, +// 0xC auto cool) +// Mode: 3 bits. (auto 0x0, cold 0x1, dry 0x2, fan 0x3, hot 0x4) +// unknown/unused: 6 bits. +// Ion flag: 1 bit. +// unknown/unused: 1 bit. +// Power/message type: 4 bits. (on 0xF, off 0xC, 0x0 == Timer mesage) +// +// Structure of a Time(r) message (56 bits) +// Signature: 12 bits. e.g. 0x201 +// Checksum: 8 bits +// Off Minutes: 3 bits. (Stored in 10 min increments. eg. xx:20 is 0x2) +// Off Hours: 5 bits. (0x17 == 11PM / 23:00) +// On Minutes: 3 bits. (Stored in 10 min increments. eg. xx:20 is 0x2) +// On Hours: 5 bits. (0x9 == 9AM / 09:00) +// Clock Hours: 5 bits. +// On Timer flag: 1 bit. +// Off Timer flag: 1 bit. +// Timer mode flag: 1 bit. (Off after X many hours/mins, not at clock time.) +// Clock Minutes: 8 bits. (0-59) +// Power/message type: 4 bits. (0x0 == Timer mesage, else see Comman message) + +// Constants +const uint16_t kVestelAcHdrMark = 3110; +const uint16_t kVestelAcHdrSpace = 9066; +const uint16_t kVestelAcBitMark = 520; +const uint16_t kVestelAcOneSpace = 1535; +const uint16_t kVestelAcZeroSpace = 480; +const uint16_t kVestelAcTolerance = 30; + +const uint8_t kVestelAcMinTempH = 16; +const uint8_t kVestelAcMinTempC = 18; +const uint8_t kVestelAcMaxTemp = 30; + +const uint64_t kVestelAcCRCMask = 0xFFFFFFFFFFF00000; + +const uint8_t kVestelAcAuto = 0; +const uint8_t kVestelAcCool = 1; +const uint8_t kVestelAcDry = 2; +const uint8_t kVestelAcFan = 3; +const uint8_t kVestelAcHeat = 4; + +const uint8_t kVestelAcFanAuto = 1; +const uint8_t kVestelAcFanLow = 5; +const uint8_t kVestelAcFanMed = 9; +const uint8_t kVestelAcFanHigh = 0xB; +const uint8_t kVestelAcFanAutoCool = 0xC; +const uint8_t kVestelAcFanAutoHot = 0xD; + +const uint8_t kVestelAcNormal = 1; +const uint8_t kVestelAcSleep = 3; +const uint8_t kVestelAcTurbo = 7; +const uint8_t kVestelAcIon = 4; +const uint8_t kVestelAcSwing = 0xA; + +const uint8_t kVestelAcChecksumOffset = 12; +const uint8_t kVestelAcSwingOffset = 20; +const uint8_t kVestelAcTurboSleepOffset = 24; +const uint8_t kVestelAcTempOffset = 36; +const uint8_t kVestelAcFanOffset = 40; +const uint8_t kVestelAcModeOffset = 44; +const uint8_t kVestelAcIonOffset = 50; +const uint8_t kVestelAcPowerOffset = 52; +const uint8_t kVestelAcOffTimeOffset = 20; +const uint8_t kVestelAcOnTimeOffset = 28; +const uint8_t kVestelAcHourOffset = 36; // 5 bits +const uint8_t kVestelAcOnTimerFlagOffset = kVestelAcHourOffset + 5; +const uint8_t kVestelAcOffTimerFlagOffset = kVestelAcHourOffset + 6; +const uint8_t kVestelAcTimerFlagOffset = kVestelAcHourOffset + 7; +const uint8_t kVestelAcMinuteOffset = 44; + +const uint64_t kVestelAcStateDefault = 0x0F00D9001FEF201ULL; +const uint64_t kVestelAcTimeStateDefault = 0x201ULL; + +class IRVestelAc { + public: + explicit IRVestelAc(uint16_t pin); + + void stateReset(); +#if SEND_VESTEL_AC + void send(); +#endif // SEND_VESTEL_AC + void begin(void); + void on(void); + void off(void); + void setPower(const bool state); + bool getPower(); + void setAuto(const int8_t autoLevel); + void setTimer(const uint16_t minutes); + uint16_t getTimer(void); + void setTime(const uint16_t minutes); + uint16_t getTime(void); + void setOnTimer(const uint16_t minutes); + uint16_t getOnTimer(void); + void setOffTimer(const uint16_t minutes); + uint16_t getOffTimer(void); + void setTemp(const uint8_t temp); + uint8_t getTemp(void); + void setFan(const uint8_t fan); + uint8_t getFan(void); + void setMode(const uint8_t mode); + uint8_t getMode(void); + void setRaw(uint8_t* newState); + void setRaw(const uint64_t newState); + uint64_t getRaw(void); + static bool validChecksum(const uint64_t state); + void setSwing(const bool state); + bool getSwing(void); + void setSleep(const bool state); + bool getSleep(void); + void setTurbo(const bool state); + bool getTurbo(void); + void setIon(const bool state); + bool getIon(void); + bool isTimeCommand(void); + bool isOnTimerActive(void); + void setOnTimerActive(const bool on); + bool isOffTimerActive(void); + void setOffTimerActive(const bool on); + bool isTimerActive(void); + void setTimerActive(const bool on); + static uint8_t calcChecksum(const uint64_t state); + uint8_t convertMode(const stdAc::opmode_t mode); + uint8_t convertFan(const stdAc::fanspeed_t speed); +#ifdef ARDUINO + String toString(); +#else + std::string toString(); +#endif +#ifndef UNIT_TEST + + private: + IRsend _irsend; +#else + IRsendTest _irsend; +#endif + uint64_t remote_state; + uint64_t remote_time_state; + bool use_time_state; + void checksum(); +}; + +#endif // IR_VESTEL_H_ diff --git a/lib/IRremoteESP8266-2.6.0/src/ir_Whirlpool.cpp b/lib/IRremoteESP8266-2.6.0/src/ir_Whirlpool.cpp new file mode 100644 index 000000000000..048c1a1eb9e4 --- /dev/null +++ b/lib/IRremoteESP8266-2.6.0/src/ir_Whirlpool.cpp @@ -0,0 +1,671 @@ +// Copyright 2018 David Conran +// +// Code to emulate Whirlpool protocol compatible devices. +// Should be compatible with: +// * SPIS409L, SPIS412L, SPIW409L, SPIW412L, SPIW418L +// Remotes: +// * DG11J1-3A / DG11J1-04 +// * DG11J1-91 +// +// Note: Smart, iFeel, AroundU, PowerSave, & Silent modes are unsupported. +// Advanced 6thSense, Dehumidify, & Sleep modes are not supported. +// FYI: +// Dim == !Light +// Jet == Super == Turbo +// + +#include "ir_Whirlpool.h" +#include +#ifndef ARDUINO +#include +#endif +#include "IRrecv.h" +#include "IRremoteESP8266.h" +#include "IRsend.h" +#include "IRutils.h" + +// WW WW HH HH IIIII RRRRRR LL PPPPPP OOOOO OOOOO LL +// WW WW HH HH III RR RR LL PP PP OO OO OO OO LL +// WW W WW HHHHHHH III RRRRRR LL PPPPPP OO OO OO OO LL +// WW WWW WW HH HH III RR RR LL PP OO OO OO OO LL +// WW WW HH HH IIIII RR RR LLLLLLL PP OOOO0 OOOO0 LLLLLLL + +// Constants +// Ref: https://github.com/markszabo/IRremoteESP8266/issues/509 +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; + +#if SEND_WHIRLPOOL_AC +// Send a Whirlpool A/C message. +// +// Args: +// data: An array of bytes containing the IR command. +// nbytes: Nr. of bytes of data in the array. (>=kWhirlpoolAcStateLength) +// repeat: Nr. of times the message is to be repeated. (Default = 0). +// +// Status: ALPHA / Untested. +// +// Ref: +// https://github.com/markszabo/IRremoteESP8266/issues/509 +void IRsend::sendWhirlpoolAC(unsigned char data[], uint16_t nbytes, + 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. +// Decoding help from: +// @redmusicxd, @josh929800, @raducostea + +IRWhirlpoolAc::IRWhirlpoolAc(uint16_t pin) : _irsend(pin) { stateReset(); } + +void IRWhirlpoolAc::stateReset() { + for (uint8_t i = 2; i < kWhirlpoolAcStateLength; i++) remote_state[i] = 0x0; + remote_state[0] = 0x83; + remote_state[1] = 0x06; + remote_state[6] = 0x80; + _setTemp(kWhirlpoolAcAutoTemp); // Default to a sane value. +} + +void IRWhirlpoolAc::begin() { _irsend.begin(); } + +bool IRWhirlpoolAc::validChecksum(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; +} + +// Update the checksum for the internal state. +void IRWhirlpoolAc::checksum(uint16_t length) { + if (length >= kWhirlpoolAcChecksumByte1) + remote_state[kWhirlpoolAcChecksumByte1] = + xorBytes(remote_state + 2, kWhirlpoolAcChecksumByte1 - 1 - 2); + if (length >= kWhirlpoolAcChecksumByte2) + remote_state[kWhirlpoolAcChecksumByte2] = + xorBytes(remote_state + kWhirlpoolAcChecksumByte1 + 1, + kWhirlpoolAcChecksumByte2 - kWhirlpoolAcChecksumByte1 - 1); +} + +#if SEND_WHIRLPOOL_AC +void IRWhirlpoolAc::send(const uint16_t repeat, const bool calcchecksum) { + if (calcchecksum) checksum(); + _irsend.sendWhirlpoolAC(remote_state, kWhirlpoolAcStateLength, repeat); +} +#endif // SEND_WHIRLPOOL_AC + +uint8_t *IRWhirlpoolAc::getRaw(const bool calcchecksum) { + if (calcchecksum) checksum(); + return remote_state; +} + +void IRWhirlpoolAc::setRaw(const uint8_t new_code[], const uint16_t length) { + for (uint8_t i = 0; i < length && i < kWhirlpoolAcStateLength; i++) + remote_state[i] = new_code[i]; +} + +whirlpool_ac_remote_model_t IRWhirlpoolAc::getModel() { + if (remote_state[kWhirlpoolAcAltTempPos] & kWhirlpoolAcAltTempMask) + return DG11J191; + else + return DG11J13A; +} + +void IRWhirlpoolAc::setModel(const whirlpool_ac_remote_model_t model) { + switch (model) { + case DG11J191: + remote_state[kWhirlpoolAcAltTempPos] |= kWhirlpoolAcAltTempMask; + break; + case DG11J13A: + // FALL THRU + default: + remote_state[kWhirlpoolAcAltTempPos] &= ~kWhirlpoolAcAltTempMask; + } + _setTemp(_desiredtemp); // Different models have different temp values. +} + +// Return the temp. offset in deg C for the current model. +int8_t IRWhirlpoolAc::getTempOffset() { + switch (getModel()) { + case DG11J191: + return -2; + break; + default: + return 0; + } +} + +// Set the temp. in deg C +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); + remote_state[kWhirlpoolAcTempPos] = + (remote_state[kWhirlpoolAcTempPos] & ~kWhirlpoolAcTempMask) | + ((newtemp - (kWhirlpoolAcMinTemp + offset)) << 4); +} + +// Set the temp. in deg C +void IRWhirlpoolAc::setTemp(const uint8_t temp) { + _setTemp(temp); + setSuper(false); // Changing temp cancels Super/Jet mode. + setCommand(kWhirlpoolAcCommandTemp); +} + +// Return the set temp. in deg C +uint8_t IRWhirlpoolAc::getTemp() { + return ((remote_state[kWhirlpoolAcTempPos] & kWhirlpoolAcTempMask) >> 4) + + + kWhirlpoolAcMinTemp + getTempOffset(); +} + +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: + remote_state[kWhirlpoolAcModePos] &= ~kWhirlpoolAcModeMask; + remote_state[kWhirlpoolAcModePos] |= mode; + setCommand(kWhirlpoolAcCommandMode); + break; + default: + return; + } + if (mode == kWhirlpoolAcAuto) setCommand(kWhirlpoolAcCommand6thSense); +} + +void IRWhirlpoolAc::setMode(const uint8_t mode) { + setSuper(false); // Changing mode cancels Super/Jet mode. + _setMode(mode); +} + +uint8_t IRWhirlpoolAc::getMode() { + return remote_state[kWhirlpoolAcModePos] & kWhirlpoolAcModeMask; +} + +void IRWhirlpoolAc::setFan(const uint8_t speed) { + switch (speed) { + case kWhirlpoolAcFanAuto: + case kWhirlpoolAcFanLow: + case kWhirlpoolAcFanMedium: + case kWhirlpoolAcFanHigh: + remote_state[kWhirlpoolAcFanPos] = + (remote_state[kWhirlpoolAcFanPos] & ~kWhirlpoolAcFanMask) | speed; + setSuper(false); // Changing fan speed cancels Super/Jet mode. + setCommand(kWhirlpoolAcCommandFanSpeed); + break; + } +} + +uint8_t IRWhirlpoolAc::getFan() { + return remote_state[kWhirlpoolAcFanPos] & kWhirlpoolAcFanMask; +} + +void IRWhirlpoolAc::setSwing(const bool on) { + if (on) { + remote_state[kWhirlpoolAcFanPos] |= kWhirlpoolAcSwing1Mask; + remote_state[kWhirlpoolAcOffTimerPos] |= kWhirlpoolAcSwing2Mask; + } else { + remote_state[kWhirlpoolAcFanPos] &= ~kWhirlpoolAcSwing1Mask; + remote_state[kWhirlpoolAcOffTimerPos] &= ~kWhirlpoolAcSwing2Mask; + } + setCommand(kWhirlpoolAcCommandSwing); +} + +bool IRWhirlpoolAc::getSwing() { + return (remote_state[kWhirlpoolAcFanPos] & kWhirlpoolAcSwing1Mask) && + (remote_state[kWhirlpoolAcOffTimerPos] & kWhirlpoolAcSwing2Mask); +} + +void IRWhirlpoolAc::setLight(const bool on) { + if (on) + remote_state[kWhirlpoolAcClockPos] &= ~kWhirlpoolAcLightMask; + else + remote_state[kWhirlpoolAcClockPos] |= kWhirlpoolAcLightMask; +} + +bool IRWhirlpoolAc::getLight() { + return !(remote_state[kWhirlpoolAcClockPos] & kWhirlpoolAcLightMask); +} + +void IRWhirlpoolAc::setTime(const uint16_t pos, + const uint16_t minspastmidnight) { + // Hours + remote_state[pos] &= ~kWhirlpoolAcHourMask; + remote_state[pos] |= (minspastmidnight / 60) % 24; + // Minutes + remote_state[pos + 1] &= ~kWhirlpoolAcMinuteMask; + remote_state[pos + 1] |= minspastmidnight % 60; +} + +uint16_t IRWhirlpoolAc::getTime(const uint16_t pos) { + return (remote_state[pos] & kWhirlpoolAcHourMask) * 60 + + (remote_state[pos + 1] & kWhirlpoolAcMinuteMask); +} + +bool IRWhirlpoolAc::isTimerEnabled(const uint16_t pos) { + return remote_state[pos - 1] & kWhirlpoolAcTimerEnableMask; +} + +void IRWhirlpoolAc::enableTimer(const uint16_t pos, const bool state) { + if (state) + remote_state[pos - 1] |= kWhirlpoolAcTimerEnableMask; + else + remote_state[pos - 1] &= ~kWhirlpoolAcTimerEnableMask; +} + +void IRWhirlpoolAc::setClock(const uint16_t minspastmidnight) { + setTime(kWhirlpoolAcClockPos, minspastmidnight); +} + +uint16_t IRWhirlpoolAc::getClock() { return getTime(kWhirlpoolAcClockPos); } + +void IRWhirlpoolAc::setOffTimer(const uint16_t minspastmidnight) { + setTime(kWhirlpoolAcOffTimerPos, minspastmidnight); +} + +uint16_t IRWhirlpoolAc::getOffTimer() { + return getTime(kWhirlpoolAcOffTimerPos); +} + +bool IRWhirlpoolAc::isOffTimerEnabled() { + return isTimerEnabled(kWhirlpoolAcOffTimerPos); +} + +void IRWhirlpoolAc::enableOffTimer(const bool state) { + enableTimer(kWhirlpoolAcOffTimerPos, state); + setCommand(kWhirlpoolAcCommandOffTimer); +} + +void IRWhirlpoolAc::setOnTimer(const uint16_t minspastmidnight) { + setTime(kWhirlpoolAcOnTimerPos, minspastmidnight); +} + +uint16_t IRWhirlpoolAc::getOnTimer() { return getTime(kWhirlpoolAcOnTimerPos); } + +bool IRWhirlpoolAc::isOnTimerEnabled() { + return isTimerEnabled(kWhirlpoolAcOnTimerPos); +} + +void IRWhirlpoolAc::enableOnTimer(const bool state) { + enableTimer(kWhirlpoolAcOnTimerPos, state); + setCommand(kWhirlpoolAcCommandOnTimer); +} + +void IRWhirlpoolAc::setPowerToggle(const bool on) { + if (on) + remote_state[kWhirlpoolAcPowerTogglePos] |= kWhirlpoolAcPowerToggleMask; + else + remote_state[kWhirlpoolAcPowerTogglePos] &= ~kWhirlpoolAcPowerToggleMask; + setSuper(false); // Changing power cancels Super/Jet mode. + setCommand(kWhirlpoolAcCommandPower); +} + +bool IRWhirlpoolAc::getPowerToggle() { + return remote_state[kWhirlpoolAcPowerTogglePos] & kWhirlpoolAcPowerToggleMask; +} + +uint8_t IRWhirlpoolAc::getCommand() { + return remote_state[kWhirlpoolAcCommandPos]; +} + +void IRWhirlpoolAc::setSleep(const bool on) { + if (on) { + remote_state[kWhirlpoolAcSleepPos] |= kWhirlpoolAcSleepMask; + setFan(kWhirlpoolAcFanLow); + } else { + remote_state[kWhirlpoolAcSleepPos] &= ~kWhirlpoolAcSleepMask; + } + setCommand(kWhirlpoolAcCommandSleep); +} + +bool IRWhirlpoolAc::getSleep() { + return remote_state[kWhirlpoolAcSleepPos] & kWhirlpoolAcSleepMask; +} + +// AKA Jet/Turbo mode. +void IRWhirlpoolAc::setSuper(const bool on) { + if (on) { + setFan(kWhirlpoolAcFanHigh); + switch (getMode()) { + case kWhirlpoolAcHeat: + setTemp(kWhirlpoolAcMaxTemp + getTempOffset()); + break; + case kWhirlpoolAcCool: + default: + setTemp(kWhirlpoolAcMinTemp + getTempOffset()); + setMode(kWhirlpoolAcCool); + break; + } + remote_state[kWhirlpoolAcSuperPos] |= kWhirlpoolAcSuperMask; + } else { + remote_state[kWhirlpoolAcSuperPos] &= ~kWhirlpoolAcSuperMask; + } + setCommand(kWhirlpoolAcCommandSuper); +} + +bool IRWhirlpoolAc::getSuper() { + return remote_state[kWhirlpoolAcSuperPos] & kWhirlpoolAcSuperMask; +} + +void IRWhirlpoolAc::setCommand(const uint8_t code) { + remote_state[kWhirlpoolAcCommandPos] = code; +} + +// Convert a standard A/C mode into its native mode. +uint8_t IRWhirlpoolAc::convertMode(const stdAc::opmode_t mode) { + switch (mode) { + case stdAc::opmode_t::kCool: + return kWhirlpoolAcCool; + case stdAc::opmode_t::kHeat: + return kWhirlpoolAcHeat; + case stdAc::opmode_t::kDry: + return kWhirlpoolAcDry; + case stdAc::opmode_t::kFan: + return kWhirlpoolAcFan; + default: + return kWhirlpoolAcAuto; + } +} + +// Convert a standard A/C Fan speed into its native fan speed. +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; + } +} + +#ifdef ARDUINO +String IRWhirlpoolAc::timeToString(const uint16_t minspastmidnight) { + String result = ""; +#else +std::string IRWhirlpoolAc::timeToString(const uint16_t minspastmidnight) { + std::string result = ""; +#endif // ARDUINO + uint8_t hours = minspastmidnight / 60; + if (hours < 10) result += '0'; + result += uint64ToString(hours); + result += ':'; + uint8_t mins = minspastmidnight % 60; + if (mins < 10) result += '0'; + result += uint64ToString(mins); + return result; +} + +// Convert the internal state into a human readable string. +#ifdef ARDUINO +String IRWhirlpoolAc::toString() { + String result = ""; +#else +std::string IRWhirlpoolAc::toString() { + std::string result = ""; +#endif // ARDUINO + result += F("Model: "); + result += uint64ToString(getModel()); + switch (getModel()) { + case DG11J191: + result += F(" (DG11J191)"); + break; + case DG11J13A: + result += F(" (DG11J13A)"); + break; + default: + result += F(" (UNKNOWN)"); + } + result += F(", Power toggle: "); + if (getPowerToggle()) + result += F("On"); + else + result += F("Off"); + result += F(", Mode: "); + result += uint64ToString(getMode()); + switch (getMode()) { + case kWhirlpoolAcHeat: + result += F(" (HEAT)"); + break; + case kWhirlpoolAcAuto: + result += F(" (AUTO)"); + break; + case kWhirlpoolAcCool: + result += F(" (COOL)"); + break; + case kWhirlpoolAcDry: + result += F(" (DRY)"); + break; + case kWhirlpoolAcFan: + result += F(" (FAN)"); + break; + default: + result += F(" (UNKNOWN)"); + } + result += F(", Temp: "); + result += uint64ToString(getTemp()); + result += F("C, Fan: "); + result += uint64ToString(getFan()); + switch (getFan()) { + case kWhirlpoolAcFanAuto: + result += F(" (AUTO)"); + break; + case kWhirlpoolAcFanHigh: + result += F(" (HIGH)"); + break; + case kWhirlpoolAcFanMedium: + result += F(" (MEDIUM)"); + break; + case kWhirlpoolAcFanLow: + result += F(" (LOW)"); + break; + default: + result += F(" (UNKNOWN)"); + break; + } + result += F(", Swing: "); + if (getSwing()) + result += F("On"); + else + result += F("Off"); + result += F(", Light: "); + if (getLight()) + result += F("On"); + else + result += F("Off"); + result += F(", Clock: "); + result += timeToString(getClock()); + result += F(", On Timer: "); + if (isOnTimerEnabled()) + result += timeToString(getOnTimer()); + else + result += F("Off"); + result += F(", Off Timer: "); + if (isOffTimerEnabled()) + result += timeToString(getOffTimer()); + else + result += F("Off"); + result += F(", Sleep: "); + if (getSleep()) + result += F("On"); + else + result += F("Off"); + result += F(", Super: "); + if (getSuper()) + result += F("On"); + else + result += F("Off"); + result += F(", Command: "); + result += uint64ToString(getCommand()); + switch (getCommand()) { + case kWhirlpoolAcCommandLight: + result += F(" (LIGHT)"); + break; + case kWhirlpoolAcCommandPower: + result += F(" (POWER)"); + break; + case kWhirlpoolAcCommandTemp: + result += F(" (TEMP)"); + break; + case kWhirlpoolAcCommandSleep: + result += F(" (SLEEP)"); + break; + case kWhirlpoolAcCommandSuper: + result += F(" (SUPER)"); + break; + case kWhirlpoolAcCommandOnTimer: + result += F(" (ONTIMER)"); + break; + case kWhirlpoolAcCommandMode: + result += F(" (MODE)"); + break; + case kWhirlpoolAcCommandSwing: + result += F(" (SWING)"); + break; + case kWhirlpoolAcCommandIFeel: + result += F(" (IFEEL)"); + break; + case kWhirlpoolAcCommandFanSpeed: + result += F(" (FANSPEED)"); + break; + case kWhirlpoolAcCommand6thSense: + result += F(" (6THSENSE)"); + break; + case kWhirlpoolAcCommandOffTimer: + result += F(" (OFFTIMER)"); + break; + default: + result += F(" (UNKNOWN)"); + break; + } + return result; +} + +#if DECODE_WHIRLPOOL_AC +// Decode the supplied Whirlpool A/C message. +// +// Args: +// results: Ptr to the data to decode and where to store the decode result. +// nbits: The number of data bits to expect. Typically kWhirlpoolAcBits +// strict: Flag indicating if we should perform strict matching. +// Returns: +// boolean: True if it can decode it, false if it can't. +// +// Status: STABLE / Working as intended. +// +// +// Ref: +// https://github.com/markszabo/IRremoteESP8266/issues/509 +bool IRrecv::decodeWhirlpoolAC(decode_results *results, uint16_t nbits, + bool strict) { + if (results->rawlen < 2 * nbits + 4 + kHeader + kFooter - 1) + return false; // Can't possibly be a valid Whirlpool A/C message. + if (strict) { + if (nbits != kWhirlpoolAcBits) return false; + } + + uint16_t offset = kStartOffset; + uint16_t dataBitsSoFar = 0; + uint16_t i = 0; + match_result_t data_result; + 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 Section + // Keep reading bytes until we either run out of section or state to fill. + for (uint8_t section = 0, pos = 0; section < kWhirlpoolAcSections; + section++) { + pos += sectionSize[section]; + for (; offset <= results->rawlen - 16 && i < pos; + i++, dataBitsSoFar += 8, offset += data_result.used) { + data_result = + matchData(&(results->rawbuf[offset]), 8, kWhirlpoolAcBitMark, + kWhirlpoolAcOneSpace, kWhirlpoolAcBitMark, + kWhirlpoolAcZeroSpace, kTolerance, kMarkExcess, false); + if (data_result.success == false) break; // Fail + // Data is in LSB order. We need to reverse it. + results->state[i] = (uint8_t)data_result.data; + } + // Section Footer + if (!matchMark(results->rawbuf[offset++], kWhirlpoolAcBitMark)) + return false; + if (section < kWhirlpoolAcSections - 1) { // Inter-section gaps. + if (!matchSpace(results->rawbuf[offset++], kWhirlpoolAcGap)) return false; + } else { // Last section / End of message gap. + if (offset <= results->rawlen && + !matchAtLeast(results->rawbuf[offset++], kWhirlpoolAcGap)) + return false; + } + } + + // Compliance + if (strict) { + // Re-check we got the correct size/length due to the way we read the data. + if (dataBitsSoFar != kWhirlpoolAcBits) return false; + if (!IRWhirlpoolAc::validChecksum(results->state, dataBitsSoFar / 8)) + return false; + } + + // Success + results->decode_type = WHIRLPOOL_AC; + results->bits = dataBitsSoFar; + // 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-2.6.0/src/ir_Whirlpool.h b/lib/IRremoteESP8266-2.6.0/src/ir_Whirlpool.h new file mode 100644 index 000000000000..9604d025c594 --- /dev/null +++ b/lib/IRremoteESP8266-2.6.0/src/ir_Whirlpool.h @@ -0,0 +1,167 @@ +// Whirlpool A/C +// +// Copyright 2018 David Conran + +#ifndef IR_WHIRLPOOL_H_ +#define IR_WHIRLPOOL_H_ + +#define __STDC_LIMIT_MACROS +#include +#ifndef UNIT_TEST +#include +#else +#include +#endif +#include "IRremoteESP8266.h" +#include "IRsend.h" +#ifdef UNIT_TEST +#include "IRsend_test.h" +#endif + +// WW WW HH HH IIIII RRRRRR LL PPPPPP OOOOO OOOOO LL +// WW WW HH HH III RR RR LL PP PP OO OO OO OO LL +// WW W WW HHHHHHH III RRRRRR LL PPPPPP OO OO OO OO LL +// WW WWW WW HH HH III RR RR LL PP OO OO OO OO LL +// WW WW HH HH IIIII RR RR LLLLLLL PP OOOO0 OOOO0 LLLLLLL + +// Ref: +// https://github.com/markszabo/IRremoteESP8266/issues/509 + +// Constants +const uint8_t kWhirlpoolAcChecksumByte1 = 13; +const uint8_t kWhirlpoolAcChecksumByte2 = kWhirlpoolAcStateLength - 1; +const uint8_t kWhirlpoolAcHeat = 0; +const uint8_t kWhirlpoolAcAuto = 1; +const uint8_t kWhirlpoolAcCool = 2; +const uint8_t kWhirlpoolAcDry = 3; +const uint8_t kWhirlpoolAcFan = 4; +const uint8_t kWhirlpoolAcModeMask = 0b00000111; +const uint8_t kWhirlpoolAcModePos = 3; +const uint8_t kWhirlpoolAcFanAuto = 0; +const uint8_t kWhirlpoolAcFanHigh = 1; +const uint8_t kWhirlpoolAcFanMedium = 2; +const uint8_t kWhirlpoolAcFanLow = 3; +const uint8_t kWhirlpoolAcFanMask = 0b00000011; +const uint8_t kWhirlpoolAcFanPos = 2; +const uint8_t kWhirlpoolAcMinTemp = 18; // 18C (DG11J1-3A), 16C (DG11J1-91) +const uint8_t kWhirlpoolAcMaxTemp = 32; // 32C (DG11J1-3A), 30C (DG11J1-91) +const uint8_t kWhirlpoolAcAutoTemp = 23; // 23C +const uint8_t kWhirlpoolAcTempMask = 0b11110000; +const uint8_t kWhirlpoolAcTempPos = 3; +const uint8_t kWhirlpoolAcSwing1Mask = 0b10000000; +const uint8_t kWhirlpoolAcSwing2Mask = 0b01000000; +const uint8_t kWhirlpoolAcLightMask = 0b00100000; +const uint8_t kWhirlpoolAcPowerToggleMask = 0b00000100; +const uint8_t kWhirlpoolAcPowerTogglePos = 2; +const uint8_t kWhirlpoolAcSleepMask = 0b00001000; +const uint8_t kWhirlpoolAcSleepPos = 2; +const uint8_t kWhirlpoolAcSuperMask = 0b10010000; +const uint8_t kWhirlpoolAcSuperPos = 5; +const uint8_t kWhirlpoolAcHourMask = 0b00011111; +const uint8_t kWhirlpoolAcMinuteMask = 0b00111111; +const uint8_t kWhirlpoolAcTimerEnableMask = 0b10000000; +const uint8_t kWhirlpoolAcClockPos = 6; +const uint8_t kWhirlpoolAcOffTimerPos = 8; +const uint8_t kWhirlpoolAcOnTimerPos = 10; +const uint8_t kWhirlpoolAcCommandPos = 15; +const uint8_t kWhirlpoolAcCommandLight = 0x00; +const uint8_t kWhirlpoolAcCommandPower = 0x01; +const uint8_t kWhirlpoolAcCommandTemp = 0x02; +const uint8_t kWhirlpoolAcCommandSleep = 0x03; +const uint8_t kWhirlpoolAcCommandSuper = 0x04; +const uint8_t kWhirlpoolAcCommandOnTimer = 0x05; +const uint8_t kWhirlpoolAcCommandMode = 0x06; +const uint8_t kWhirlpoolAcCommandSwing = 0x07; +const uint8_t kWhirlpoolAcCommandIFeel = 0x0D; +const uint8_t kWhirlpoolAcCommandFanSpeed = 0x11; +const uint8_t kWhirlpoolAcCommand6thSense = 0x17; +const uint8_t kWhirlpoolAcCommandOffTimer = 0x1D; +const uint8_t kWhirlpoolAcAltTempMask = 0b00001000; +const uint8_t kWhirlpoolAcAltTempPos = 18; + +enum whirlpool_ac_remote_model_t { + DG11J13A = 1, // DG11J1-04 too + DG11J191, +}; + +// Classes +class IRWhirlpoolAc { + public: + explicit IRWhirlpoolAc(uint16_t pin); + + void stateReset(); +#if SEND_WHIRLPOOL_AC + void send(const uint16_t repeat = kWhirlpoolAcDefaultRepeat, + const bool calcchecksum = true); +#endif // SEND_WHIRLPOOL_AC + void begin(); + void on(); + void off(); + void setPowerToggle(const bool on); + bool getPowerToggle(); + void setSleep(const bool on); + bool getSleep(); + void setSuper(const bool on); + bool getSuper(); + void setTemp(const uint8_t temp); + uint8_t getTemp(); + void setFan(const uint8_t speed); + uint8_t getFan(); + void setMode(const uint8_t mode); + uint8_t getMode(); + void setSwing(const bool on); + bool getSwing(); + void setLight(const bool on); + bool getLight(); + uint16_t getClock(); + void setClock(const uint16_t minspastmidnight); + uint16_t getOnTimer(); + void setOnTimer(const uint16_t minspastmidnight); + void enableOnTimer(const bool state); + bool isOnTimerEnabled(); + uint16_t getOffTimer(); + void setOffTimer(const uint16_t minspastmidnight); + void enableOffTimer(const bool state); + bool isOffTimerEnabled(); + void setCommand(const uint8_t code); + uint8_t getCommand(); + whirlpool_ac_remote_model_t getModel(); + void setModel(const whirlpool_ac_remote_model_t model); + uint8_t* getRaw(const bool calcchecksum = true); + void setRaw(const uint8_t new_code[], + const uint16_t length = kWhirlpoolAcStateLength); + static bool validChecksum(uint8_t state[], + const uint16_t length = kWhirlpoolAcStateLength); + uint8_t convertMode(const stdAc::opmode_t mode); + uint8_t convertFan(const stdAc::fanspeed_t speed); +#ifdef ARDUINO + String toString(); +#else + std::string toString(); +#endif +#ifndef UNIT_TEST + + private: + IRsend _irsend; +#else + IRsendTest _irsend; +#endif + // The state of the IR remote in IR code form. + uint8_t remote_state[kWhirlpoolAcStateLength]; + uint8_t _desiredtemp; + void checksum(const uint16_t length = kWhirlpoolAcStateLength); + uint16_t getTime(const uint16_t pos); + void setTime(const uint16_t pos, const uint16_t minspastmidnight); + bool isTimerEnabled(const uint16_t pos); + void enableTimer(const uint16_t pos, const bool state); + void _setTemp(const uint8_t temp, const bool remember = true); + void _setMode(const uint8_t mode); + int8_t getTempOffset(); +#ifdef ARDUINO + String timeToString(uint16_t minspastmidnight); +#else + std::string timeToString(uint16_t minspastmidnight); +#endif +}; + +#endif // IR_WHIRLPOOL_H_ diff --git a/lib/IRremoteESP8266-2.5.2.03/src/ir_Whynter.cpp b/lib/IRremoteESP8266-2.6.0/src/ir_Whynter.cpp similarity index 100% rename from lib/IRremoteESP8266-2.5.2.03/src/ir_Whynter.cpp rename to lib/IRremoteESP8266-2.6.0/src/ir_Whynter.cpp diff --git a/lib/IRremoteESP8266-2.6.0/test/IRac_test.cpp b/lib/IRremoteESP8266-2.6.0/test/IRac_test.cpp new file mode 100644 index 000000000000..39c17a84b470 --- /dev/null +++ b/lib/IRremoteESP8266-2.6.0/test/IRac_test.cpp @@ -0,0 +1,865 @@ +// Copyright 2019 David Conran + +#include "ir_Argo.h" +#include "ir_Daikin.h" +#include "ir_Fujitsu.h" +#include "ir_Gree.h" +#include "ir_Haier.h" +#include "ir_Hitachi.h" +#include "ir_Kelvinator.h" +#include "ir_Midea.h" +#include "ir_Mitsubishi.h" +#include "ir_MitsubishiHeavy.h" +#include "ir_Panasonic.h" +#include "ir_Samsung.h" +#include "ir_Tcl.h" +#include "ir_Teco.h" +#include "ir_Toshiba.h" +#include "ir_Trotec.h" +#include "ir_Vestel.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. + +TEST(TestIRac, Argo) { + IRArgoAC ac(0); + IRac irac(0); + + ac.begin(); + irac.argo(&ac, + true, // Power + stdAc::opmode_t::kHeat, // Mode + 21, // Celsius + stdAc::fanspeed_t::kHigh, // Fan speed + stdAc::swingv_t::kOff, // Veritcal swing + false, // Turbo + -1); // Sleep + EXPECT_TRUE(ac.getPower()); + EXPECT_EQ(1, ac.getMode()); + EXPECT_EQ(21, ac.getTemp()); + EXPECT_EQ(kArgoFlapAuto, ac.getFlap()); + EXPECT_FALSE(ac.getMax()); // Turbo + EXPECT_FALSE(ac.getNight()); // Sleep +} + +TEST(TestIRac, Coolix) { + IRCoolixAC ac(0); + IRac irac(0); + IRrecv capture(0); + char expected[] = + "Power: On, Mode: 3 (HEAT), Fan: 1 (MAX), Temp: 21C, Zone Follow: Off, " + "Sensor Temp: Ignored"; + + ac.begin(); + irac.coolix(&ac, + true, // Power + stdAc::opmode_t::kHeat, // Mode + 21, // Celsius + stdAc::fanspeed_t::kHigh, // Fan speed + stdAc::swingv_t::kOff, // Veritcal swing + stdAc::swingh_t::kOff, // Horizontal swing + false, // Turbo + false, // Light + false, // Clean + -1); // Sleep + ASSERT_EQ(expected, ac.toString()); + ac._irsend.makeDecodeResult(); + EXPECT_TRUE(capture.decode(&ac._irsend.capture)); + ASSERT_EQ(COOLIX, ac._irsend.capture.decode_type); + ASSERT_EQ(kCoolixBits, ac._irsend.capture.bits); + ac.setRaw(ac._irsend.capture.value); + ASSERT_EQ(expected, ac.toString()); +} + +TEST(TestIRac, Daikin) { + IRDaikinESP ac(0); + IRac irac(0); + char expected[] = + "Power: On, Mode: 3 (COOL), Temp: 19C, Fan: 2, Powerful: Off, " + "Quiet: Off, Sensor: Off, Eye: Off, Mold: On, Comfort: Off, " + "Swing (Horizontal): Off, Swing (Vertical): Off, " + "Current Time: 0:00, On Time: Off, Off Time: Off"; + + ac.begin(); + irac.daikin(&ac, + true, // Power + stdAc::opmode_t::kCool, // Mode + 19, // Celsius + stdAc::fanspeed_t::kMedium, // Fan speed + stdAc::swingv_t::kOff, // Veritcal swing + stdAc::swingh_t::kOff, // Horizontal swing + false, // Quiet + false, // Turbo + true, // Filter + true); // Clean + ASSERT_EQ(expected, ac.toString()); +} + +TEST(TestIRac, Daikin2) { + IRDaikin2 ac(0); + IRac irac(0); + IRrecv capture(0); + char expected[] = + "Power: On, Mode: 3 (COOL), Temp: 19C, Fan: 2, Swing (V): 14 (Auto), " + "Swing (H): 0, Clock: 0:00, On Time: Off, Off Time: Off, " + "Sleep Time: Off, Beep: 1 (Quiet), Light: 1 (Bright), Mold: On, " + "Clean: Off, Fresh Air: Off, Eye: Off, Eye Auto: Off, Quiet: Off, " + "Powerful: Off, Purify: On, Econo: Off"; + + ac.begin(); + irac.daikin2(&ac, + true, // Power + stdAc::opmode_t::kCool, // Mode + 19, // Celsius + stdAc::fanspeed_t::kMedium, // Fan speed + stdAc::swingv_t::kOff, // Veritcal swing + stdAc::swingh_t::kOff, // Horizontal swing + false, // Quiet + false, // Turbo + true, // Light + false, // Econo + true, // Filter + true, // Clean (aka Mold) + -1, // Sleep time + -1); // Current time + ASSERT_EQ(expected, ac.toString()); + ac._irsend.makeDecodeResult(); + EXPECT_TRUE(capture.decode(&ac._irsend.capture)); + ASSERT_EQ(DAIKIN2, ac._irsend.capture.decode_type); + ASSERT_EQ(kDaikin2Bits, ac._irsend.capture.bits); + ac.setRaw(ac._irsend.capture.state); + ASSERT_EQ(expected, ac.toString()); +} + +TEST(TestIRac, Daikin216) { + IRDaikin216 ac(0); + IRac irac(0); + IRrecv capture(0); + char expected[] = + "Power: On, Mode: 4 (HEAT), Temp: 31C, Fan: 11 (QUIET), " + "Swing (Horizontal): On, Swing (Vertical): On, Quiet: On"; + + ac.begin(); + irac.daikin216(&ac, + true, // Power + stdAc::opmode_t::kHeat, // Mode + 31, // Celsius + stdAc::fanspeed_t::kMedium, // Fan speed + stdAc::swingv_t::kAuto, // Veritcal swing + stdAc::swingh_t::kLeft, // Horizontal swing + true); // Quiet + ASSERT_EQ(expected, ac.toString()); + ac._irsend.makeDecodeResult(); + EXPECT_TRUE(capture.decode(&ac._irsend.capture)); + ASSERT_EQ(DAIKIN216, ac._irsend.capture.decode_type); + ASSERT_EQ(kDaikin216Bits, ac._irsend.capture.bits); + ac.setRaw(ac._irsend.capture.state); + ASSERT_EQ(expected, ac.toString()); +} + +TEST(TestIRac, Fujitsu) { + IRFujitsuAC ac(0); + IRac irac(0); + IRrecv capture(0); + char expected[] = + "Power: On, Mode: 1 (COOL), Temp: 19C, Fan: 2 (MED), " + "Swing: Off, Command: N/A"; + + ac.begin(); + irac.fujitsu(&ac, + ARDB1, // Model + true, // Power + stdAc::opmode_t::kCool, // Mode + 19, // Celsius + stdAc::fanspeed_t::kMedium, // Fan speed + stdAc::swingv_t::kOff, // Veritcal swing + stdAc::swingh_t::kOff, // Horizontal swing + false); // Quiet + ASSERT_EQ(expected, ac.toString()); + ac._irsend.makeDecodeResult(); + EXPECT_TRUE(capture.decode(&ac._irsend.capture)); + ASSERT_EQ(FUJITSU_AC, ac._irsend.capture.decode_type); + ASSERT_EQ(kFujitsuAcBits - 8, ac._irsend.capture.bits); + ac.setRaw(ac._irsend.capture.state, ac._irsend.capture.bits / 8); + ASSERT_EQ(expected, ac.toString()); + + ac._irsend.reset(); + irac.fujitsu(&ac, + ARRAH2E, // Model + true, // Power + stdAc::opmode_t::kCool, // Mode + 19, // Celsius + stdAc::fanspeed_t::kMedium, // Fan speed + stdAc::swingv_t::kOff, // Veritcal swing + stdAc::swingh_t::kOff, // Horizontal swing + false); // Quiet + ASSERT_EQ(expected, ac.toString()); + ac._irsend.makeDecodeResult(); + EXPECT_TRUE(capture.decode(&ac._irsend.capture)); + ASSERT_EQ(FUJITSU_AC, ac._irsend.capture.decode_type); + ASSERT_EQ(kFujitsuAcBits, ac._irsend.capture.bits); + ac.setRaw(ac._irsend.capture.state, ac._irsend.capture.bits / 8); + ASSERT_EQ(expected, ac.toString()); +} + +TEST(TestIRac, Gree) { + IRGreeAC ac(0); + IRac irac(0); + IRrecv capture(0); + char expected[] = + "Power: On, Mode: 1 (COOL), Temp: 22C, Fan: 2, Turbo: Off, XFan: On, " + "Light: On, Sleep: On, Swing Vertical Mode: Manual, " + "Swing Vertical Pos: 3"; + + ac.begin(); + irac.gree(&ac, + true, // Power + stdAc::opmode_t::kCool, // Mode + 22, // Celsius + stdAc::fanspeed_t::kMedium, // Fan speed + stdAc::swingv_t::kHigh, // Veritcal swing + false, // Turbo + true, // Light + true, // Clean (aka Mold/XFan) + 8 * 60 + 0); // Sleep time + ASSERT_EQ(expected, ac.toString()); + ac._irsend.makeDecodeResult(); + EXPECT_TRUE(capture.decode(&ac._irsend.capture)); + ASSERT_EQ(GREE, ac._irsend.capture.decode_type); + ASSERT_EQ(kGreeBits, ac._irsend.capture.bits); + ac.setRaw(ac._irsend.capture.state); + ASSERT_EQ(expected, ac.toString()); +} + +TEST(TestIRac, Haier) { + IRHaierAC ac(0); + IRac irac(0); + IRrecv capture(0); + char expected[] = + "Command: 1 (On), Mode: 3 (HEAT), Temp: 24C, Fan: 2, Swing: 1 (Up), " + "Sleep: On, Health: On, Current Time: 13:45, On Timer: Off, " + "Off Timer: Off"; + + ac.begin(); + irac.haier(&ac, + true, // Power + stdAc::opmode_t::kCool, // Mode + 24, // Celsius + stdAc::fanspeed_t::kMedium, // Fan speed + stdAc::swingv_t::kHigh, // Veritcal swing + true, // Filter + 8 * 60 + 0, // Sleep time + 13 * 60 + 45); // Clock + ASSERT_EQ(expected, ac.toString()); + ac._irsend.makeDecodeResult(); + EXPECT_TRUE(capture.decode(&ac._irsend.capture)); + ASSERT_EQ(HAIER_AC, ac._irsend.capture.decode_type); + ASSERT_EQ(kHaierACBits, ac._irsend.capture.bits); + ac.setRaw(ac._irsend.capture.state); + ASSERT_EQ(expected, ac.toString()); +} + + +TEST(TestIRac, HaierYrwo2) { + IRHaierACYRW02 ac(0); + IRac irac(0); + IRrecv capture(0); + char expected[] = + "Power: On, Button: 5 (Power), Mode: 2 (Cool), Temp: 23C, Fan: 4 (Med), " + "Turbo: 1 (High), Swing: 1 (Top), Sleep: On, Health: On"; + + ac.begin(); + irac.haierYrwo2(&ac, + true, // Power + stdAc::opmode_t::kCool, // Mode + 23, // Celsius + stdAc::fanspeed_t::kMedium, // Fan speed + stdAc::swingv_t::kHigh, // Veritcal swing + true, // Turbo + true, // Filter + 8 * 60 + 0); // Sleep time + ASSERT_EQ(expected, ac.toString()); + ac._irsend.makeDecodeResult(); + EXPECT_TRUE(capture.decode(&ac._irsend.capture)); + ASSERT_EQ(HAIER_AC_YRW02, ac._irsend.capture.decode_type); + ASSERT_EQ(kHaierACYRW02Bits, ac._irsend.capture.bits); + ac.setRaw(ac._irsend.capture.state); + ASSERT_EQ(expected, ac.toString()); +} + +TEST(TestIRac, Hitachi) { + IRHitachiAc ac(0); + IRac irac(0); + IRrecv capture(0); + char expected[] = + "Power: On, Mode: 2 (AUTO), Temp: 22C, Fan: 3 (UNKNOWN), " + "Swing (Vertical): Off, Swing (Horizontal): On"; + + ac.begin(); + irac.hitachi(&ac, + true, // Power + stdAc::opmode_t::kAuto, // Mode + 22, // Celsius + stdAc::fanspeed_t::kMedium, // Fan speed + stdAc::swingv_t::kOff, // Veritcal swing + stdAc::swingh_t::kAuto); // Horizontal swing + + ASSERT_EQ(expected, ac.toString()); + ac._irsend.makeDecodeResult(); + EXPECT_TRUE(capture.decode(&ac._irsend.capture)); + ASSERT_EQ(HITACHI_AC, ac._irsend.capture.decode_type); + ASSERT_EQ(kHitachiAcBits, ac._irsend.capture.bits); + ac.setRaw(ac._irsend.capture.state); + ASSERT_EQ(expected, ac.toString()); +} + +TEST(TestIRac, Kelvinator) { + IRKelvinatorAC ac(0); + IRac irac(0); + IRrecv capture(0); + char expected[] = + "Power: On, Mode: 1 (COOL), Temp: 19C, Fan: 3, Turbo: Off, Quiet: Off, " + "XFan: On, IonFilter: On, Light: On, Swing (Horizontal): Off, " + "Swing (Vertical): Off"; + + ac.begin(); + irac.kelvinator(&ac, + true, // Power + stdAc::opmode_t::kCool, // Mode + 19, // Celsius + stdAc::fanspeed_t::kMedium, // Fan speed + stdAc::swingv_t::kOff, // Veritcal swing + stdAc::swingh_t::kOff, // Horizontal swing + false, // Quiet + false, // Turbo + true, // Light + true, // Filter + true); // Clean + + ASSERT_EQ(expected, ac.toString()); + ac._irsend.makeDecodeResult(); + EXPECT_TRUE(capture.decode(&ac._irsend.capture)); + ASSERT_EQ(KELVINATOR, ac._irsend.capture.decode_type); + ASSERT_EQ(kKelvinatorBits, ac._irsend.capture.bits); + ac.setRaw(ac._irsend.capture.state); + ASSERT_EQ(expected, ac.toString()); +} + +TEST(TestIRac, Midea) { + IRMideaAC ac(0); + IRac irac(0); + IRrecv capture(0); + char expected[] = + "Power: On, Mode: 1 (DRY), Temp: 27C/81F, Fan: 2 (MED), Sleep: On"; + + ac.begin(); + irac.midea(&ac, + true, // Power + stdAc::opmode_t::kDry, // Mode + 27, // Celsius + stdAc::fanspeed_t::kMedium, // Fan speed + 8 * 60 + 0); // Sleep time + + ASSERT_EQ(expected, ac.toString()); + ac._irsend.makeDecodeResult(); + EXPECT_TRUE(capture.decode(&ac._irsend.capture)); + ASSERT_EQ(MIDEA, ac._irsend.capture.decode_type); + ASSERT_EQ(kMideaBits, ac._irsend.capture.bits); + ac.setRaw(ac._irsend.capture.value); + ASSERT_EQ(expected, ac.toString()); +} + +TEST(TestIRac, Mitsubishi) { + IRMitsubishiAC ac(0); + IRac irac(0); + IRrecv capture(0); + char expected[] = + "Power: On (COOL), Temp: 20C, FAN: 2, VANE: AUTO, Time: 14:30, " + "On timer: 00:00, Off timer: 00:00, Timer: -"; + + ac.begin(); + irac.mitsubishi(&ac, + true, // Power + stdAc::opmode_t::kCool, // Mode + 20, // Celsius + stdAc::fanspeed_t::kMedium, // Fan speed + stdAc::swingv_t::kOff, // Veritcal swing + false, // Silent + 14 * 60 + 35); // Clock + ASSERT_EQ(expected, ac.toString()); + ac._irsend.makeDecodeResult(); + EXPECT_TRUE(capture.decode(&ac._irsend.capture)); + ASSERT_EQ(MITSUBISHI_AC, ac._irsend.capture.decode_type); + ASSERT_EQ(kMitsubishiACBits, ac._irsend.capture.bits); + ac.setRaw(ac._irsend.capture.state); + ASSERT_EQ(expected, ac.toString()); +} + +TEST(TestIRac, MitsubishiHeavy88) { + IRMitsubishiHeavy88Ac ac(0); + IRac irac(0); + IRrecv capture(0); + char expected[] = + "Power: On, Mode: 1 (Cool), Temp: 21C, Fan: 3 (Med), " + "Swing (V): 16 (Auto), Swing (H): 0 (Off), Turbo: Off, Econo: Off, " + "3D: Off, Clean: On"; + + ac.begin(); + irac.mitsubishiHeavy88(&ac, + true, // Power + stdAc::opmode_t::kCool, // Mode + 21, // Celsius + stdAc::fanspeed_t::kMedium, // Fan speed + stdAc::swingv_t::kAuto, // Veritcal swing + stdAc::swingh_t::kOff, // Horizontal swing + false, // Turbo + false, // Econo + true); // Clean + ASSERT_EQ(expected, ac.toString()); + ac._irsend.makeDecodeResult(); + EXPECT_TRUE(capture.decode(&ac._irsend.capture)); + ASSERT_EQ(MITSUBISHI_HEAVY_88, ac._irsend.capture.decode_type); + ASSERT_EQ(kMitsubishiHeavy88Bits, ac._irsend.capture.bits); + ac.setRaw(ac._irsend.capture.state); + ASSERT_EQ(expected, ac.toString()); +} + +TEST(TestIRac, MitsubishiHeavy152) { + IRMitsubishiHeavy152Ac ac(0); + IRac irac(0); + IRrecv capture(0); + char expected[] = + "Power: On, Mode: 1 (Cool), Temp: 20C, Fan: 6 (Econo), " + "Swing (V): 6 (Off), Swing (H): 0 (Auto), Silent: On, Turbo: Off, " + "Econo: On, Night: On, Filter: On, 3D: Off, Clean: Off"; + + ac.begin(); + irac.mitsubishiHeavy152(&ac, + true, // Power + stdAc::opmode_t::kCool, // Mode + 20, // Celsius + stdAc::fanspeed_t::kLow, // Fan speed + stdAc::swingv_t::kOff, // Veritcal swing + stdAc::swingh_t::kAuto, // Horizontal swing + true, // Silent + false, // Turbo + true, // Econo + true, // Filter + false, // Clean + 8 * 60); // Sleep + ASSERT_EQ(expected, ac.toString()); + ac._irsend.makeDecodeResult(); + EXPECT_TRUE(capture.decode(&ac._irsend.capture)); + ASSERT_EQ(MITSUBISHI_HEAVY_152, ac._irsend.capture.decode_type); + ASSERT_EQ(kMitsubishiHeavy152Bits, ac._irsend.capture.bits); + ac.setRaw(ac._irsend.capture.state); + ASSERT_EQ(expected, ac.toString()); +} + +TEST(TestIRac, Panasonic) { + IRPanasonicAc ac(0); + IRac irac(0); + IRrecv capture(0); + char expected_nke[] = + "Model: 2 (NKE), Power: On, Mode: 4 (HEAT), Temp: 28C, Fan: 2 (UNKNOWN), " + "Swing (Vertical): 15 (AUTO), Swing (Horizontal): 6 (Middle), Quiet: On, " + "Powerful: Off, Clock: 19:17, On Timer: Off, Off Timer: Off"; + + ac.begin(); + irac.panasonic(&ac, + kPanasonicNke, // Model + true, // Power + stdAc::opmode_t::kHeat, // Mode + 28, // Celsius + stdAc::fanspeed_t::kMedium, // Fan speed + stdAc::swingv_t::kAuto, // Veritcal swing + stdAc::swingh_t::kLeft, // Horizontal swing + true, // Quiet + false, // Turbo + 19 * 60 + 17); // Clock + ASSERT_EQ(expected_nke, ac.toString()); + ac._irsend.makeDecodeResult(); + EXPECT_TRUE(capture.decode(&ac._irsend.capture)); + ASSERT_EQ(PANASONIC_AC, ac._irsend.capture.decode_type); + ASSERT_EQ(kPanasonicAcBits, ac._irsend.capture.bits); + ac.setRaw(ac._irsend.capture.state); + ASSERT_EQ(expected_nke, ac.toString()); + + char expected_dke[] = + "Model: 3 (DKE), Power: On, Mode: 3 (COOL), Temp: 18C, Fan: 4 (MAX), " + "Swing (Vertical): 1 (Full Up), Swing (Horizontal): 6 (Middle), " + "Quiet: Off, Powerful: On, Clock: 19:17, On Timer: Off, Off Timer: Off"; + ac._irsend.reset(); + irac.panasonic(&ac, + kPanasonicDke, // Model + true, // Power + stdAc::opmode_t::kCool, // Mode + 18, // Celsius + stdAc::fanspeed_t::kMax, // Fan speed + stdAc::swingv_t::kHigh, // Veritcal swing + stdAc::swingh_t::kMiddle, // Horizontal swing + false, // Quiet + true, // Turbo + 19 * 60 + 17); // Clock + ASSERT_EQ(expected_dke, ac.toString()); + ac._irsend.makeDecodeResult(); + EXPECT_TRUE(capture.decode(&ac._irsend.capture)); + ASSERT_EQ(PANASONIC_AC, ac._irsend.capture.decode_type); + ASSERT_EQ(kPanasonicAcBits, ac._irsend.capture.bits); + ac.setRaw(ac._irsend.capture.state); + ASSERT_EQ(expected_dke, ac.toString()); +} + +TEST(TestIRac, Samsung) { + IRSamsungAc ac(0); + IRac irac(0); + IRrecv capture(0); + char expected[] = + "Power: On, Mode: 0 (AUTO), Temp: 28C, Fan: 6 (AUTO), Swing: On, " + "Beep: On, Clean: On, Quiet: On"; + + ac.begin(); + irac.samsung(&ac, + true, // Power + stdAc::opmode_t::kAuto, // Mode + 28, // Celsius + stdAc::fanspeed_t::kMedium, // Fan speed + stdAc::swingv_t::kAuto, // Veritcal swing + true, // Quiet + false, // Turbo + true, // Clean + true, // Beep + false); // with the Hack Off + ASSERT_EQ(expected, ac.toString()); + ac._irsend.makeDecodeResult(); + EXPECT_TRUE(capture.decode(&ac._irsend.capture)); + ASSERT_EQ(SAMSUNG_AC, ac._irsend.capture.decode_type); + ASSERT_EQ(kSamsungAcBits, ac._irsend.capture.bits); + ac.setRaw(ac._irsend.capture.state); + ASSERT_EQ(expected, ac.toString()); + + ac._irsend.reset(); + irac.samsung(&ac, + true, // Power + stdAc::opmode_t::kAuto, // Mode + 28, // Celsius + stdAc::fanspeed_t::kMedium, // Fan speed + stdAc::swingv_t::kAuto, // Veritcal swing + true, // Quiet + false, // Turbo + true, // Clean + true, // Beep + true); // with the Hack On + ASSERT_EQ(expected, ac.toString()); // Class should be in the desired mode. + ac._irsend.makeDecodeResult(); + EXPECT_TRUE(capture.decode(&ac._irsend.capture)); + ASSERT_EQ(SAMSUNG_AC, ac._irsend.capture.decode_type); + ASSERT_EQ(kSamsungAcExtendedBits, ac._irsend.capture.bits); + ac.setRaw(ac._irsend.capture.state); + // However, we expect a plain "on" state as it should be sent before the + // desired state. + char expected_on[] = + "Power: On, Mode: 0 (AUTO), Temp: 16C, Fan: 0 (AUTO), Swing: Off, " + "Beep: Off, Clean: Off, Quiet: Off"; + ASSERT_EQ(expected_on, ac.toString()); +} + +TEST(TestIRac, Tcl112) { + IRTcl112Ac ac(0); + IRac irac(0); + IRrecv capture(0); + char expected[] = + "Power: On, Mode: 3 (COOL), Temp: 20C, Fan: 3 (Med), Econo: On, " + "Health: On, Light: On, Turbo: Off, Swing (H): On, Swing (V): Off"; + + ac.begin(); + irac.tcl112(&ac, + true, // Power + stdAc::opmode_t::kCool, // Mode + 20, // Celsius + stdAc::fanspeed_t::kMedium, // Fan speed + stdAc::swingv_t::kOff, // Veritcal swing + stdAc::swingh_t::kAuto, // Horizontal swing + false, // Turbo + true, // Light + true, // Econo + true); // Filter (aka. Health) + ASSERT_EQ(expected, ac.toString()); + ac._irsend.makeDecodeResult(); + EXPECT_TRUE(capture.decode(&ac._irsend.capture)); + ASSERT_EQ(TCL112AC, ac._irsend.capture.decode_type); + ASSERT_EQ(kTcl112AcBits, ac._irsend.capture.bits); + ac.setRaw(ac._irsend.capture.state); + ASSERT_EQ(expected, ac.toString()); +} + +TEST(TestIRac, Teco) { + IRTecoAc ac(0); + IRac irac(0); + IRrecv capture(0); + char expected[] = + "Power: On, Mode: 0 (AUTO), Temp: 21C, Fan: 2 (Med), Sleep: On, " + "Swing: On"; + + ac.begin(); + irac.teco(&ac, + true, // Power + stdAc::opmode_t::kAuto, // Mode + 21, // Celsius + stdAc::fanspeed_t::kMedium, // Fan speed + stdAc::swingv_t::kAuto, // Veritcal swing + 8 * 60 + 30); // Sleep + ASSERT_EQ(expected, ac.toString()); + ac._irsend.makeDecodeResult(); + EXPECT_TRUE(capture.decode(&ac._irsend.capture)); + ASSERT_EQ(TECO, ac._irsend.capture.decode_type); + ASSERT_EQ(kTecoBits, ac._irsend.capture.bits); + ac.setRaw(ac._irsend.capture.value); + ASSERT_EQ(expected, ac.toString()); +} + +TEST(TestIRac, Toshiba) { + IRToshibaAC ac(0); + IRac irac(0); + IRrecv capture(0); + char expected[] = "Power: On, Mode: 2 (DRY), Temp: 29C, Fan: 2"; + + ac.begin(); + irac.toshiba(&ac, + true, // Power + stdAc::opmode_t::kDry, // Mode + 29, // Celsius + stdAc::fanspeed_t::kLow); // Fan speed + ASSERT_EQ(expected, ac.toString()); + ac._irsend.makeDecodeResult(); + EXPECT_TRUE(capture.decode(&ac._irsend.capture)); + ASSERT_EQ(TOSHIBA_AC, ac._irsend.capture.decode_type); + ASSERT_EQ(kToshibaACBits, ac._irsend.capture.bits); + ac.setRaw(ac._irsend.capture.state); + ASSERT_EQ(expected, ac.toString()); +} + +TEST(TestIRac, Trotec) { + IRTrotecESP ac(0); + IRac irac(0); + + ac.begin(); + irac.trotec(&ac, + true, // Power + stdAc::opmode_t::kCool, // Mode + 18, // Celsius + stdAc::fanspeed_t::kHigh, // Fan speed + 8 * 60 + 17); // Sleep + EXPECT_TRUE(ac.getPower()); + EXPECT_EQ(kTrotecCool, ac.getMode()); + EXPECT_EQ(18, ac.getTemp()); + EXPECT_EQ(kTrotecFanHigh, ac.getSpeed()); + EXPECT_TRUE(ac.getSleep()); +} + +TEST(TestIRac, Vestel) { + IRVestelAc ac(0); + IRac irac(0); + IRrecv capture(0); + char expected[] = + "Power: On, Mode: 0 (AUTO), Temp: 22C, Fan: 5 (LOW), Sleep: On, " + "Turbo: Off, Ion: On, Swing: On"; + + ac.begin(); + irac.vestel(&ac, + true, // Power + stdAc::opmode_t::kAuto, // Mode + 22, // Celsius + stdAc::fanspeed_t::kLow, // Fan speed + stdAc::swingv_t::kHigh, // Veritcal swing + false, // Turbo + true, // Filter + 8 * 60 + 0); // Sleep time + // 13 * 60 + 45); // Clock + ASSERT_EQ(expected, ac.toString()); + ac._irsend.makeDecodeResult(); + EXPECT_TRUE(capture.decode(&ac._irsend.capture)); + ASSERT_EQ(VESTEL_AC, ac._irsend.capture.decode_type); + ASSERT_EQ(kVestelAcBits, ac._irsend.capture.bits); + ac.setRaw(ac._irsend.capture.state); + ASSERT_EQ(expected, ac.toString()); + + ac._irsend.reset(); + char expected_clocks[] = + "Time: 13:45, Timer: Off, On Timer: Off, Off Timer: Off"; + + ac.begin(); + irac.vestel(&ac, + true, // Power + stdAc::opmode_t::kAuto, // Mode + 22, // Celsius + stdAc::fanspeed_t::kLow, // Fan speed + stdAc::swingv_t::kHigh, // Veritcal swing + false, // Turbo + true, // Filter + 8 * 60 + 0, // Sleep time + 13 * 60 + 45, // Clock + false); // Don't send the normal message. + // Just for testing purposes. + ASSERT_EQ(expected_clocks, ac.toString()); + ac._irsend.makeDecodeResult(); + EXPECT_TRUE(capture.decode(&ac._irsend.capture)); + ASSERT_EQ(VESTEL_AC, ac._irsend.capture.decode_type); + ASSERT_EQ(kVestelAcBits, ac._irsend.capture.bits); + ac.setRaw(ac._irsend.capture.state); + ASSERT_EQ(expected_clocks, ac.toString()); + + // Now check it sends both messages during normal operation when the + // clock is set. + ac._irsend.reset(); + ac.begin(); + irac.vestel(&ac, + true, // Power + stdAc::opmode_t::kAuto, // Mode + 22, // Celsius + stdAc::fanspeed_t::kLow, // Fan speed + stdAc::swingv_t::kHigh, // Veritcal swing + false, // Turbo + true, // Filter + 8 * 60 + 0, // Sleep time + 13 * 60 + 45); // Clock + + EXPECT_EQ( + "f38000d50" + "m3110s9066" + "m520s1535m520s480m520s480m520s480m520s480m520s480m520s480m520s480" + "m520s480m520s1535m520s480m520s480m520s480m520s480m520s480m520s480" + "m520s1535m520s1535m520s1535m520s1535m520s480m520s1535m520s480m520s1535" + "m520s1535m520s1535m520s480m520s480m520s480m520s480m520s480m520s480" + "m520s480m520s480m520s480m520s480m520s480m520s1535m520s1535m520s480" + "m520s1535m520s480m520s1535m520s480m520s480m520s480m520s480m520s480" + "m520s480m520s480m520s1535m520s480m520s1535m520s1535m520s1535m520s1535" + "m520s100000" + "m3110s9066" + "m520s1535m520s480m520s480m520s480m520s480m520s480m520s480m520s480" + "m520s480m520s1535m520s480m520s480m520s480m520s1535m520s1535m520s480" + "m520s1535m520s1535m520s1535m520s1535m520s480m520s480m520s480m520s480" + "m520s480m520s480m520s480m520s480m520s480m520s480m520s480m520s480" + "m520s480m520s480m520s480m520s480m520s1535m520s480m520s1535m520s1535" + "m520s480m520s480m520s480m520s480m520s1535m520s480m520s1535m520s1535" + "m520s480m520s1535m520s480m520s480m520s480m520s480m520s480m520s480" + "m520s100000", ac._irsend.outputStr()); +} + + +TEST(TestIRac, Whirlpool) { + IRWhirlpoolAc ac(0); + IRac irac(0); + IRrecv capture(0); + char expected[] = + "Model: 1 (DG11J13A), Power toggle: On, Mode: 1 (AUTO), Temp: 21C, " + "Fan: 3 (LOW), Swing: On, Light: On, Clock: 23:58, On Timer: Off, " + "Off Timer: Off, Sleep: On, Super: Off, Command: 1 (POWER)"; + + ac.begin(); + irac.whirlpool(&ac, + DG11J13A, + true, // Power + stdAc::opmode_t::kAuto, // Mode + 21, // Celsius + stdAc::fanspeed_t::kMedium, // Fan speed + stdAc::swingv_t::kAuto, // Veritcal swing + false, // Turbo + true, // Light + 8 * 60 + 30, // Sleep + 23 * 60 + 58); // Clock + ASSERT_EQ(expected, ac.toString()); + ac._irsend.makeDecodeResult(); + EXPECT_TRUE(capture.decode(&ac._irsend.capture)); + ASSERT_EQ(WHIRLPOOL_AC, ac._irsend.capture.decode_type); + ASSERT_EQ(kWhirlpoolAcBits, ac._irsend.capture.bits); + ac.setRaw(ac._irsend.capture.state); + ASSERT_EQ(expected, ac.toString()); +} + +TEST(TestIRac, strToBool) { + EXPECT_TRUE(IRac::strToBool("ON")); + EXPECT_TRUE(IRac::strToBool("1")); + EXPECT_TRUE(IRac::strToBool("TRUE")); + EXPECT_TRUE(IRac::strToBool("YES")); + EXPECT_FALSE(IRac::strToBool("OFF")); + EXPECT_FALSE(IRac::strToBool("0")); + EXPECT_FALSE(IRac::strToBool("FALSE")); + EXPECT_FALSE(IRac::strToBool("NO")); + EXPECT_FALSE(IRac::strToBool("FOOBAR")); + EXPECT_TRUE(IRac::strToBool("FOOBAR", true)); +} + +TEST(TestIRac, strToOpmode) { + EXPECT_EQ(stdAc::opmode_t::kAuto, IRac::strToOpmode("AUTO")); + EXPECT_EQ(stdAc::opmode_t::kCool, IRac::strToOpmode("COOL")); + EXPECT_EQ(stdAc::opmode_t::kHeat, IRac::strToOpmode("HEAT")); + EXPECT_EQ(stdAc::opmode_t::kDry, IRac::strToOpmode("DRY")); + EXPECT_EQ(stdAc::opmode_t::kFan, IRac::strToOpmode("FAN")); + EXPECT_EQ(stdAc::opmode_t::kFan, IRac::strToOpmode("FAN_ONLY")); + EXPECT_EQ(stdAc::opmode_t::kAuto, IRac::strToOpmode("FOOBAR")); + EXPECT_EQ(stdAc::opmode_t::kOff, IRac::strToOpmode("OFF")); + EXPECT_EQ(stdAc::opmode_t::kOff, IRac::strToOpmode("FOOBAR", + stdAc::opmode_t::kOff)); +} + +TEST(TestIRac, strToFanspeed) { + EXPECT_EQ(stdAc::fanspeed_t::kAuto, IRac::strToFanspeed("AUTO")); + EXPECT_EQ(stdAc::fanspeed_t::kMin, IRac::strToFanspeed("MIN")); + EXPECT_EQ(stdAc::fanspeed_t::kLow, IRac::strToFanspeed("LOW")); + EXPECT_EQ(stdAc::fanspeed_t::kMedium, IRac::strToFanspeed("MEDIUM")); + EXPECT_EQ(stdAc::fanspeed_t::kHigh, IRac::strToFanspeed("HIGH")); + EXPECT_EQ(stdAc::fanspeed_t::kMax, IRac::strToFanspeed("MAX")); + EXPECT_EQ(stdAc::fanspeed_t::kAuto, IRac::strToFanspeed("FOOBAR")); + EXPECT_EQ(stdAc::fanspeed_t::kMin, + IRac::strToFanspeed("FOOBAR", stdAc::fanspeed_t::kMin)); +} + +TEST(TestIRac, strToSwingV) { + EXPECT_EQ(stdAc::swingv_t::kAuto, IRac::strToSwingV("AUTO")); + EXPECT_EQ(stdAc::swingv_t::kLowest, IRac::strToSwingV("LOWEST")); + EXPECT_EQ(stdAc::swingv_t::kLow, IRac::strToSwingV("LOW")); + EXPECT_EQ(stdAc::swingv_t::kMiddle, IRac::strToSwingV("MIDDLE")); + EXPECT_EQ(stdAc::swingv_t::kHigh, IRac::strToSwingV("HIGH")); + EXPECT_EQ(stdAc::swingv_t::kHighest, IRac::strToSwingV("HIGHEST")); + EXPECT_EQ(stdAc::swingv_t::kOff, IRac::strToSwingV("OFF")); + EXPECT_EQ(stdAc::swingv_t::kOff, IRac::strToSwingV("FOOBAR")); + EXPECT_EQ(stdAc::swingv_t::kAuto, + IRac::strToSwingV("FOOBAR", stdAc::swingv_t::kAuto)); +} + +TEST(TestIRac, strToSwingH) { + EXPECT_EQ(stdAc::swingh_t::kAuto, IRac::strToSwingH("AUTO")); + EXPECT_EQ(stdAc::swingh_t::kLeftMax, IRac::strToSwingH("MAX LEFT")); + EXPECT_EQ(stdAc::swingh_t::kLeft, IRac::strToSwingH("LEFT")); + EXPECT_EQ(stdAc::swingh_t::kMiddle, IRac::strToSwingH("CENTRE")); + EXPECT_EQ(stdAc::swingh_t::kRight, IRac::strToSwingH("RIGHT")); + EXPECT_EQ(stdAc::swingh_t::kRightMax, IRac::strToSwingH("RIGHTMAX")); + EXPECT_EQ(stdAc::swingh_t::kOff, IRac::strToSwingH("OFF")); + EXPECT_EQ(stdAc::swingh_t::kOff, IRac::strToSwingH("FOOBAR")); + EXPECT_EQ(stdAc::swingh_t::kAuto, + IRac::strToSwingH("FOOBAR", stdAc::swingh_t::kAuto)); +} + +TEST(TestIRac, strToModel) { + EXPECT_EQ(panasonic_ac_remote_model_t::kPanasonicLke, + IRac::strToModel("LKE")); + EXPECT_EQ(panasonic_ac_remote_model_t::kPanasonicLke, + IRac::strToModel("PANASONICLKE")); + EXPECT_EQ(fujitsu_ac_remote_model_t::ARRAH2E, + IRac::strToModel("ARRAH2E")); + EXPECT_EQ(whirlpool_ac_remote_model_t::DG11J13A, + IRac::strToModel("DG11J13A")); + EXPECT_EQ(1, IRac::strToModel("1")); + EXPECT_EQ(10, IRac::strToModel("10")); + EXPECT_EQ(-1, IRac::strToModel("0")); + EXPECT_EQ(-1, IRac::strToModel("FOOBAR")); + EXPECT_EQ(0, IRac::strToModel("FOOBAR", 0)); +} diff --git a/lib/IRremoteESP8266-2.5.2.03/test/IRrecv_test.cpp b/lib/IRremoteESP8266-2.6.0/test/IRrecv_test.cpp similarity index 100% rename from lib/IRremoteESP8266-2.5.2.03/test/IRrecv_test.cpp rename to lib/IRremoteESP8266-2.6.0/test/IRrecv_test.cpp diff --git a/lib/IRremoteESP8266-2.5.2.03/test/IRrecv_test.h b/lib/IRremoteESP8266-2.6.0/test/IRrecv_test.h similarity index 100% rename from lib/IRremoteESP8266-2.5.2.03/test/IRrecv_test.h rename to lib/IRremoteESP8266-2.6.0/test/IRrecv_test.h diff --git a/lib/IRremoteESP8266-2.6.0/test/IRsend_test.cpp b/lib/IRremoteESP8266-2.6.0/test/IRsend_test.cpp new file mode 100644 index 000000000000..ffd69cf71d11 --- /dev/null +++ b/lib/IRremoteESP8266-2.6.0/test/IRsend_test.cpp @@ -0,0 +1,686 @@ +// Copyright 2017,2019 David Conran + +#include "IRsend_test.h" +#include "IRsend.h" +#include "IRutils.h" +#include "gtest/gtest.h" + +// Tests sendData(). + +// Test sending zero bits. +TEST(TestSendData, SendZeroBits) { + IRsendTest irsend(4); + irsend.begin(); + irsend.sendData(1, 2, 3, 4, 0b1, 0, true); + EXPECT_EQ("", irsend.outputStr()); +} + +// Test sending zero and one. +TEST(TestSendData, SendSingleBit) { + IRsendTest irsend(4); + irsend.begin(); + irsend.sendData(1, 2, 3, 4, 0b1, 1, true); + EXPECT_EQ("d50m1s2", irsend.outputStr()); + irsend.sendData(1, 2, 3, 4, 0b0, 1, true); + EXPECT_EQ("d50m3s4", irsend.outputStr()); +} + +// Test sending bit order. +TEST(TestSendData, TestingBitSendOrder) { + IRsendTest irsend(4); + irsend.begin(); + irsend.sendData(1, 2, 3, 4, 0b10, 2, true); + EXPECT_EQ("d50m1s2m3s4", irsend.outputStr()); + irsend.sendData(1, 2, 3, 4, 0b10, 2, false); + EXPECT_EQ("d50m3s4m1s2", irsend.outputStr()); + irsend.sendData(1, 2, 3, 4, 0b0001, 4, false); + EXPECT_EQ("d50m1s2m3s4m3s4m3s4", irsend.outputStr()); +} + +// Test sending typical data. +TEST(TestSendData, SendTypicalData) { + IRsendTest irsend(4); + irsend.begin(); + irsend.sendData(1, 2, 3, 4, 0b1010110011110000, 16, true); + EXPECT_EQ( + "d50m1s2m3s4m1s2m3s4m1s2m1s2m3s4m3s4m1s2m1s2m1s2m1s2m3s4m3s4m3s4m3s4", + irsend.outputStr()); + irsend.sendData(1, 2, 3, 4, 0x1234567890ABCDEF, 64, true); + EXPECT_EQ( + "d50" + "m3s4m3s4m3s4m1s2m3s4m3s4m1s2m3s4m3s4m3s4m1s2m1s2m3s4m1s2m3s4m3s4" + "m3s4m1s2m3s4m1s2m3s4m1s2m1s2m3s4m3s4m1s2m1s2m1s2m1s2m3s4m3s4m3s4" + "m1s2m3s4m3s4m1s2m3s4m3s4m3s4m3s4m1s2m3s4m1s2m3s4m1s2m3s4m1s2m1s2" + "m1s2m1s2m3s4m3s4m1s2m1s2m3s4m1s2m1s2m1s2m1s2m3s4m1s2m1s2m1s2m1s2", + irsend.outputStr()); +} + +// Test sending more than expected bits. +TEST(TestSendData, SendOverLargeData) { + IRsendTest irsend(4); + irsend.begin(); + irsend.sendData(1, 2, 3, 4, 0xFFFFFFFFFFFFFFFF, 70, true); + EXPECT_EQ( + "d50" + "m3s4m3s4m3s4m3s4m3s4m3s4" + "m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2" + "m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2" + "m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2" + "m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2", + irsend.outputStr()); +} + +// Test inverting the output. +TEST(TestIRSend, InvertedOutput) { + IRsendTest irsend(4, true); + irsend.begin(); + irsend.sendData(1, 2, 3, 4, 0b1, 1, true); + EXPECT_EQ("d50s1m2", irsend.outputStr()); + irsend.sendData(1, 2, 3, 4, 0b0, 1, true); + EXPECT_EQ("d50s3m4", irsend.outputStr()); +} + +// Test we correctly pick up frequency changes. +TEST(TestIRSend, DetectFreqChanges) { + IRsendTest irsend(0); + + irsend.begin(); + irsend.enableIROut(40); // 40kHz + irsend.sendData(1, 2, 3, 4, 0b1, 1, true); + irsend.enableIROut(38); // 40kHz + irsend.sendData(1, 2, 3, 4, 0b1, 1, true); + irsend.enableIROut(40); // 40kHz + irsend.sendData(1, 2, 3, 4, 0b1, 1, true); + irsend.enableIROut(38); // 40kHz + irsend.sendData(1, 2, 3, 4, 0b1, 1, true); + EXPECT_EQ( + "f40000d50" + "m1s2" + "f38000" + "m1s2" + "f40000" + "m1s2" + "f38000" + "m1s2", + irsend.outputStr()); + irsend.reset(); + irsend.enableIROut(40); // 40kHz + irsend.sendData(1, 2, 3, 4, 0b1, 1, true); + irsend.enableIROut(40); // 40kHz + irsend.sendData(1, 2, 3, 4, 0b1, 1, true); + irsend.enableIROut(38); // 40kHz + irsend.sendData(1, 2, 3, 4, 0b1, 1, true); + irsend.enableIROut(38); // 40kHz + irsend.sendData(1, 2, 3, 4, 0b1, 1, true); + EXPECT_EQ( + "f40000d50" + "m1s2m1s2" + "f38000m1s2m1s2", + irsend.outputStr()); +} + +// Test we correctly pick up duty cycle changes. +TEST(TestIRSend, DetectDutyChanges) { + IRsendTest irsend(0); + + irsend.begin(); + irsend.sendGeneric(1, 2, 3, 4, 5, 6, 7, 8, 0b1, 1, 38000, true, 0, 33); + EXPECT_EQ( + "f38000d33" + "m1s2m3s4m7s8", + irsend.outputStr()); + + irsend.reset(); + irsend.sendGeneric(1, 2, 3, 4, 5, 6, 7, 8, 0b1, 1, 38000, true, 0, 50); + irsend.sendGeneric(1, 2, 3, 4, 5, 6, 7, 8, 0b1, 1, 38000, true, 0, 33); + irsend.sendGeneric(1, 2, 3, 4, 5, 6, 7, 8, 0b1, 1, 38000, true, 0, 25); + EXPECT_EQ( + "f38000d50" + "m1s2m3s4m7s8" + "d33" + "m1s2m3s4m7s8" + "d25" + "m1s2m3s4m7s8", + irsend.outputStr()); +} + + +// Test we correctly pick up frequency AND duty changes. +TEST(TestIRSend, DetectFreqAndDutyChanges) { + IRsendTest irsend(0); + + irsend.begin(); + irsend.sendGeneric(1, 2, 3, 4, 5, 6, 7, 8, 0b1, 1, 38000, true, 0, 50); + irsend.sendGeneric(1, 2, 3, 4, 5, 6, 7, 8, 0b1, 1, 38000, true, 0, 33); + irsend.sendGeneric(1, 2, 3, 4, 5, 6, 7, 8, 0b1, 1, 40000, true, 0, 25); + EXPECT_EQ( + "f38000d50" + "m1s2m3s4m7s8" + "d33" + "m1s2m3s4m7s8" + "f40000d25" + "m1s2m3s4m7s8", + irsend.outputStr()); +} + +// Test typical use of sendRaw(). +TEST(TestSendRaw, GeneralUse) { + IRsendTest irsend(4); + IRrecv irrecv(0); + + irsend.begin(); + // NEC C3E0E0E8 as measured in #204 + uint16_t rawData[67] = { + 8950, 4500, 550, 1650, 600, 1650, 550, 550, 600, 500, 600, 550, + 550, 550, 600, 1650, 550, 1650, 600, 1650, 600, 1650, 550, 1700, + 550, 550, 600, 550, 550, 550, 600, 500, 600, 550, 550, 1650, + 600, 1650, 600, 1650, 550, 550, 600, 500, 600, 500, 600, 550, + 550, 550, 600, 1650, 550, 1650, 600, 1650, 600, 500, 650, 1600, + 600, 500, 600, 550, 550, 550, 600}; + + irsend.sendRaw(rawData, 67, 38); + EXPECT_EQ( + "f38000d50" + "m8950s4500" + "m550s1650m600s1650m550s550m600s500m600s550m550s550m600s1650m550s1650" + "m600s1650m600s1650m550s1700m550s550m600s550m550s550m600s500m600s550" + "m550s1650m600s1650m600s1650m550s550m600s500m600s500m600s550m550s550" + "m600s1650m550s1650m600s1650m600s500m650s1600m600s500m600s550m550s550" + "m600", + irsend.outputStr()); + + irsend.reset(); + irsend.sendRaw(rawData, 67, 38); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decodeNEC(&irsend.capture, kNECBits, false)); + EXPECT_EQ(NEC, irsend.capture.decode_type); + EXPECT_EQ(32, irsend.capture.bits); + EXPECT_EQ(0xC3E0E0E8, irsend.capture.value); + EXPECT_EQ( + "f38000d50" + "m8950s4500" + "m550s1650m600s1650m550s550m600s500m600s550m550s550m600s1650m550s1650" + "m600s1650m600s1650m550s1700m550s550m600s550m550s550m600s500m600s550" + "m550s1650m600s1650m600s1650m550s550m600s500m600s500m600s550m550s550" + "m600s1650m550s1650m600s1650m600s500m650s1600m600s500m600s550m550s550" + "m600", + irsend.outputStr()); +} + +// Incorrect handling of decodes from Raw. i.e. There is no gap recorded at +// the end of a command when using the interrupt code. sendRaw() best emulates +// this for unit testing purposes. sendGC() and sendXXX() will add the trailing +// gap. Users won't see this in normal use. +TEST(TestSendRaw, NoTrailingGap) { + IRsendTest irsend(4); + IRrecv irrecv(4); + irsend.begin(); + + irsend.reset(); + uint16_t rawData[67] = { + 9000, 4500, 650, 550, 650, 1650, 600, 550, 650, 550, 600, 1650, + 650, 550, 600, 1650, 650, 1650, 650, 1650, 600, 550, 650, 1650, + 650, 1650, 650, 550, 600, 1650, 650, 1650, 650, 550, 650, 550, + 650, 1650, 650, 550, 650, 550, 650, 550, 600, 550, 650, 550, + 650, 550, 650, 1650, 600, 550, 650, 1650, 650, 1650, 650, 1650, + 650, 1650, 650, 1650, 650, 1650, 600}; + irsend.sendRaw(rawData, 67, 38); + irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decodeNEC(&irsend.capture)); + EXPECT_EQ(NEC, irsend.capture.decode_type); + EXPECT_EQ(kNECBits, irsend.capture.bits); +} + +TEST(TestLowLevelSend, MarkFrequencyModulationAt38kHz) { + IRsendLowLevelTest irsend(0); + + irsend.begin(); + + irsend.reset(); + irsend.enableIROut(38000, 50); + EXPECT_EQ(5, irsend.mark(100)); + EXPECT_EQ( + "[On]10usecs[Off]11usecs[On]10usecs[Off]11usecs[On]10usecs[Off]11usecs" + "[On]10usecs[Off]11usecs[On]10usecs[Off]6usecs", + irsend.low_level_sequence); + + irsend.reset(); + irsend.enableIROut(38000, 33); + EXPECT_EQ(5, irsend.mark(100)); + EXPECT_EQ( + "[On]6usecs[Off]15usecs[On]6usecs[Off]15usecs[On]6usecs[Off]15usecs" + "[On]6usecs[Off]15usecs[On]6usecs[Off]10usecs", + irsend.low_level_sequence); + + irsend.reset(); + irsend.enableIROut(38000, 100); + EXPECT_EQ(1, irsend.mark(1000)); + EXPECT_EQ("[On]1000usecs[Off]", irsend.low_level_sequence); +} + +TEST(TestLowLevelSend, MarkFrequencyModulationAt36_7kHz) { + IRsendLowLevelTest irsend(0); + + irsend.begin(); + + irsend.reset(); + irsend.enableIROut(36700, 50); + EXPECT_EQ(5, irsend.mark(100)); + EXPECT_EQ( + "[On]11usecs[Off]11usecs[On]11usecs[Off]11usecs[On]11usecs[Off]11usecs" + "[On]11usecs[Off]11usecs[On]11usecs[Off]1usecs", + irsend.low_level_sequence); + + irsend.reset(); + irsend.enableIROut(36700, 33); + EXPECT_EQ(5, irsend.mark(100)); + EXPECT_EQ( + "[On]7usecs[Off]15usecs[On]7usecs[Off]15usecs[On]7usecs[Off]15usecs" + "[On]7usecs[Off]15usecs[On]7usecs[Off]5usecs", + irsend.low_level_sequence); + + irsend.reset(); + irsend.enableIROut(36700, 100); + EXPECT_EQ(1, irsend.mark(1000)); + EXPECT_EQ("[On]1000usecs[Off]", irsend.low_level_sequence); +} + +TEST(TestLowLevelSend, MarkFrequencyModulationAt40kHz) { + IRsendLowLevelTest irsend(0); + + irsend.begin(); + + irsend.reset(); + irsend.enableIROut(40000, 50); + EXPECT_EQ(5, irsend.mark(100)); + EXPECT_EQ( + "[On]10usecs[Off]10usecs[On]10usecs[Off]10usecs[On]10usecs[Off]10usecs" + "[On]10usecs[Off]10usecs[On]10usecs[Off]10usecs", + irsend.low_level_sequence); + + irsend.reset(); + irsend.enableIROut(40000, 33); + EXPECT_EQ(5, irsend.mark(100)); + EXPECT_EQ( + "[On]6usecs[Off]14usecs[On]6usecs[Off]14usecs[On]6usecs[Off]14usecs" + "[On]6usecs[Off]14usecs[On]6usecs[Off]14usecs", + irsend.low_level_sequence); + + irsend.reset(); + irsend.enableIROut(40000, 100); + EXPECT_EQ(1, irsend.mark(1000)); + EXPECT_EQ("[On]1000usecs[Off]", irsend.low_level_sequence); +} + +TEST(TestLowLevelSend, MarkNoModulation) { + IRsendLowLevelTest irsend(0, false, false); + + irsend.begin(); + + irsend.reset(); + irsend.enableIROut(38000, 50); + EXPECT_EQ(1, irsend.mark(1000)); + EXPECT_EQ("[On]1000usecs[Off]", irsend.low_level_sequence); + + irsend.reset(); + irsend.enableIROut(36700, 25); + EXPECT_EQ(1, irsend.mark(1000)); + EXPECT_EQ("[On]1000usecs[Off]", irsend.low_level_sequence); + + irsend.reset(); + irsend.enableIROut(40000, 75); + EXPECT_EQ(1, irsend.mark(1000)); + EXPECT_EQ("[On]1000usecs[Off]", irsend.low_level_sequence); +} + +TEST(TestLowLevelSend, SpaceFrequencyModulation) { + IRsendLowLevelTest irsend(0); + + irsend.reset(); + irsend.enableIROut(38000); + irsend.space(1000); + EXPECT_EQ("[Off]1000usecs", irsend.low_level_sequence); + + irsend.reset(); + irsend.enableIROut(40000, 75); + irsend.space(1000); + EXPECT_EQ("[Off]1000usecs", irsend.low_level_sequence); + + irsend.reset(); + irsend.enableIROut(38000, 100); + irsend.space(1000); + EXPECT_EQ("[Off]1000usecs", irsend.low_level_sequence); + + irsend.reset(); + irsend.enableIROut(38000, 33); + irsend.space(1000); + EXPECT_EQ("[Off]1000usecs", irsend.low_level_sequence); +} + +TEST(TestLowLevelSend, SpaceNoModulation) { + IRsendLowLevelTest irsend(0, false, false); + + irsend.begin(); + + irsend.reset(); + irsend.enableIROut(38000, 50); + irsend.space(1000); + EXPECT_EQ("[Off]1000usecs", irsend.low_level_sequence); + + irsend.reset(); + irsend.enableIROut(36700, 25); + irsend.space(1000); + EXPECT_EQ("[Off]1000usecs", irsend.low_level_sequence); + + irsend.reset(); + irsend.enableIROut(40000, 75); + irsend.space(1000); + EXPECT_EQ("[Off]1000usecs", irsend.low_level_sequence); +} + +// Test expected to work/produce a message for irsend:send() +TEST(TestSend, GenericSimpleSendMethod) { + IRsendTest irsend(0); + IRrecv irrecv(0); + irsend.begin(); + + irsend.reset(); + ASSERT_TRUE(irsend.send(AIWA_RC_T501, 0x1234, kAiwaRcT501Bits)); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(AIWA_RC_T501, irsend.capture.decode_type); + EXPECT_EQ(kAiwaRcT501Bits, irsend.capture.bits); + EXPECT_EQ(0x1234, irsend.capture.value); + + irsend.reset(); + ASSERT_TRUE(irsend.send(CARRIER_AC, 0x1234, kCarrierAcBits)); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(CARRIER_AC, irsend.capture.decode_type); + EXPECT_EQ(kCarrierAcBits, irsend.capture.bits); + EXPECT_EQ(0x1234, irsend.capture.value); + + irsend.reset(); + ASSERT_TRUE(irsend.send(COOLIX, 0x1234, kCoolixBits)); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(COOLIX, irsend.capture.decode_type); + EXPECT_EQ(kCoolixBits, irsend.capture.bits); + EXPECT_EQ(0x1234, irsend.capture.value); + + irsend.reset(); + ASSERT_TRUE(irsend.send(DENON, 0x1234, kDenonBits)); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(DENON, irsend.capture.decode_type); + EXPECT_EQ(kDenonBits, irsend.capture.bits); + EXPECT_EQ(0x1234, irsend.capture.value); + + irsend.reset(); + ASSERT_TRUE(irsend.send(DISH, 0x1234, kDishBits)); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(DISH, irsend.capture.decode_type); + EXPECT_EQ(kDishBits, irsend.capture.bits); + EXPECT_EQ(0x1234, irsend.capture.value); + + irsend.reset(); + ASSERT_TRUE(irsend.send(GICABLE, 0x1234, kGicableBits)); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(GICABLE, irsend.capture.decode_type); + EXPECT_EQ(kGicableBits, irsend.capture.bits); + EXPECT_EQ(0x1234, irsend.capture.value); + + irsend.reset(); + ASSERT_TRUE(irsend.send(GREE, 0x0009205000200050, kGreeBits)); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(GREE, irsend.capture.decode_type); + EXPECT_EQ(kGreeBits, irsend.capture.bits); + // No simple value test for gree as it decodes to an Array. + + irsend.reset(); + ASSERT_TRUE(irsend.send(JVC, 0x1234, kJvcBits)); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(JVC, irsend.capture.decode_type); + EXPECT_EQ(kJvcBits, irsend.capture.bits); + EXPECT_EQ(0x1234, irsend.capture.value); + + irsend.reset(); + ASSERT_TRUE(irsend.send(LASERTAG, 0x123, kLasertagBits)); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(LASERTAG, irsend.capture.decode_type); + EXPECT_EQ(kLasertagBits, irsend.capture.bits); + EXPECT_EQ(0x123, irsend.capture.value); + + irsend.reset(); + ASSERT_TRUE(irsend.send(LG, 0x700992, kLgBits)); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(LG, irsend.capture.decode_type); + EXPECT_EQ(kLgBits, irsend.capture.bits); + EXPECT_EQ(0x700992, irsend.capture.value); + + irsend.reset(); + ASSERT_TRUE(irsend.send(LG, 0x700992, kLg32Bits)); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(LG, irsend.capture.decode_type); + EXPECT_EQ(kLg32Bits, irsend.capture.bits); + EXPECT_EQ(0x700992, irsend.capture.value); + + irsend.reset(); + ASSERT_TRUE(irsend.send(LG2, 0x880094D, kLgBits)); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(LG2, irsend.capture.decode_type); + EXPECT_EQ(kLgBits, irsend.capture.bits); + EXPECT_EQ(0x880094D, irsend.capture.value); + + irsend.reset(); + ASSERT_TRUE(irsend.send(LUTRON, 0x1234, kLutronBits)); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(LUTRON, irsend.capture.decode_type); + EXPECT_EQ(kLutronBits, irsend.capture.bits); + EXPECT_EQ(0x1234, irsend.capture.value); + + irsend.reset(); + ASSERT_TRUE(irsend.send(MAGIQUEST, 0x560F40020455, kMagiquestBits)); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(MAGIQUEST, irsend.capture.decode_type); + EXPECT_EQ(kMagiquestBits, irsend.capture.bits); + EXPECT_EQ(0x560F40020455, irsend.capture.value); + + irsend.reset(); + ASSERT_TRUE(irsend.send(MIDEA, 0xA18263FFFF6E, kMideaBits)); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(MIDEA, irsend.capture.decode_type); + EXPECT_EQ(kMideaBits, irsend.capture.bits); + EXPECT_EQ(0xA18263FFFF6E, irsend.capture.value); + + irsend.reset(); + ASSERT_TRUE(irsend.send(MITSUBISHI, 0x1234, kMitsubishiBits)); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(MITSUBISHI, irsend.capture.decode_type); + EXPECT_EQ(kMitsubishiBits, irsend.capture.bits); + EXPECT_EQ(0x1234, irsend.capture.value); + + irsend.reset(); + ASSERT_TRUE(irsend.send(MITSUBISHI2, 0x1234, kMitsubishiBits)); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(MITSUBISHI2, irsend.capture.decode_type); + EXPECT_EQ(kMitsubishiBits, irsend.capture.bits); + EXPECT_EQ(0x1234, irsend.capture.value); + + irsend.reset(); + ASSERT_TRUE(irsend.send(NIKAI, 0x1234, kNikaiBits)); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(NIKAI, irsend.capture.decode_type); + EXPECT_EQ(kNikaiBits, irsend.capture.bits); + EXPECT_EQ(0x1234, irsend.capture.value); + + irsend.reset(); + ASSERT_TRUE(irsend.send(NEC, 0x4BB640BF, kNECBits)); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(NEC, irsend.capture.decode_type); + EXPECT_EQ(kNECBits, irsend.capture.bits); + EXPECT_EQ(0x4BB640BF, irsend.capture.value); + + irsend.reset(); + ASSERT_TRUE(irsend.send(NEC_LIKE, 0x12345678, kNECBits)); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(NEC_LIKE, irsend.capture.decode_type); + EXPECT_EQ(kNECBits, irsend.capture.bits); + EXPECT_EQ(0x12345678, irsend.capture.value); + + irsend.reset(); + ASSERT_TRUE(irsend.send(PANASONIC, 0x40040190ED7C, kPanasonicBits)); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(PANASONIC, irsend.capture.decode_type); + EXPECT_EQ(kPanasonicBits, irsend.capture.bits); + EXPECT_EQ(0x40040190ED7C, irsend.capture.value); + + irsend.reset(); + ASSERT_TRUE(irsend.send(PIONEER, 0x659A05FAF50AC53A, kPioneerBits)); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(PIONEER, irsend.capture.decode_type); + EXPECT_EQ(kPioneerBits, irsend.capture.bits); + EXPECT_EQ(0x659A05FAF50AC53A, irsend.capture.value); + + irsend.reset(); + ASSERT_TRUE(irsend.send(RC5, 0x175, kRC5Bits)); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(RC5, irsend.capture.decode_type); + EXPECT_EQ(kRC5Bits, irsend.capture.bits); + EXPECT_EQ(0x175, irsend.capture.value); + + irsend.reset(); + ASSERT_TRUE(irsend.send(RC6, 0xC800F740C, kRC6_36Bits)); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(RC6, irsend.capture.decode_type); + EXPECT_EQ(kRC6_36Bits, irsend.capture.bits); + EXPECT_EQ(0xC800F740C, irsend.capture.value); + + irsend.reset(); + ASSERT_TRUE(irsend.send(RCMM, 0x1234, kRCMMBits)); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(RCMM, irsend.capture.decode_type); + EXPECT_EQ(kRCMMBits, irsend.capture.bits); + EXPECT_EQ(0x1234, irsend.capture.value); + + irsend.reset(); + ASSERT_TRUE(irsend.send(SAMSUNG, 0xE0E09966, kSamsungBits)); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(SAMSUNG, irsend.capture.decode_type); + EXPECT_EQ(kSamsungBits, irsend.capture.bits); + EXPECT_EQ(0xE0E09966, irsend.capture.value); + + irsend.reset(); + ASSERT_TRUE(irsend.send(SAMSUNG36, 0x1234, kSamsung36Bits)); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(SAMSUNG36, irsend.capture.decode_type); + EXPECT_EQ(kSamsung36Bits, irsend.capture.bits); + EXPECT_EQ(0x1234, irsend.capture.value); + + irsend.reset(); + ASSERT_TRUE(irsend.send(SANYO_LC7461, 0x2468DCB56A9, kSanyoLC7461Bits)); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(SANYO_LC7461, irsend.capture.decode_type); + EXPECT_EQ(kSanyoLC7461Bits, irsend.capture.bits); + EXPECT_EQ(0x2468DCB56A9, irsend.capture.value); + + irsend.reset(); + ASSERT_TRUE(irsend.send(SHARP, 0x7266, kSharpBits)); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(SHARP, irsend.capture.decode_type); + EXPECT_EQ(kSharpBits, irsend.capture.bits); + EXPECT_EQ(0x7266, irsend.capture.value); + + irsend.reset(); + ASSERT_TRUE(irsend.send(SHERWOOD, 0x4BB640BF, kSherwoodBits)); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(NEC, irsend.capture.decode_type); + EXPECT_EQ(kSherwoodBits, irsend.capture.bits); + EXPECT_EQ(0x4BB640BF, irsend.capture.value); + + irsend.reset(); + ASSERT_TRUE(irsend.send(SONY, 0x1234, kSony20Bits)); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(SONY, irsend.capture.decode_type); + EXPECT_EQ(kSony20Bits, irsend.capture.bits); + EXPECT_EQ(0x1234, irsend.capture.value); + + irsend.reset(); + ASSERT_TRUE(irsend.send(TECO, 0x1234, kTecoBits)); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(TECO, irsend.capture.decode_type); + EXPECT_EQ(kTecoBits, irsend.capture.bits); + EXPECT_EQ(0x1234, irsend.capture.value); + + irsend.reset(); + ASSERT_TRUE(irsend.send(VESTEL_AC, 0xF4410001FF1201, kVestelAcBits)); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(VESTEL_AC, irsend.capture.decode_type); + EXPECT_EQ(kVestelAcBits, irsend.capture.bits); + EXPECT_EQ(0xF4410001FF1201, irsend.capture.value); + + irsend.reset(); + ASSERT_TRUE(irsend.send(WHYNTER, 0x1234, kWhynterBits)); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(WHYNTER, irsend.capture.decode_type); + EXPECT_EQ(kWhynterBits, irsend.capture.bits); + EXPECT_EQ(0x1234, irsend.capture.value); +} + +// Test some expected types to NOT work/produce a message via irsend:send() +TEST(TestSend, GenericSimpleSendMethodFailure) { + IRsendTest irsend(0); + IRrecv irrecv(0); + irsend.begin(); + + // Check nothing is sent for unexpected protocols + irsend.reset(); + ASSERT_FALSE(irsend.send(KELVINATOR, 0x1234, kKelvinatorBits)); + irsend.makeDecodeResult(); + ASSERT_FALSE(irrecv.decode(&irsend.capture)); + + // For every A/C protocol which decodes to having a state[]. + for (int i = 0; i < 200; i++) { + if (hasACState((decode_type_t)i) && i != GREE) { // Gree is an exception. + // Check it fails. + ASSERT_FALSE(irsend.send((decode_type_t)i, 0, 0)); + } + } + + // Test some other special cases. + ASSERT_FALSE(irsend.send(UNKNOWN, 0, 0)); + ASSERT_FALSE(irsend.send(UNUSED, 0, 0)); + ASSERT_FALSE(irsend.send(RAW, 0, 0)); + ASSERT_FALSE(irsend.send(PRONTO, 0, 0)); + ASSERT_FALSE(irsend.send(GLOBALCACHE, 0, 0)); +} diff --git a/lib/IRremoteESP8266-2.5.2.03/test/IRsend_test.h b/lib/IRremoteESP8266-2.6.0/test/IRsend_test.h similarity index 82% rename from lib/IRremoteESP8266-2.5.2.03/test/IRsend_test.h rename to lib/IRremoteESP8266-2.6.0/test/IRsend_test.h index 6d9fe51b81ee..f434094332ee 100644 --- a/lib/IRremoteESP8266-2.5.2.03/test/IRsend_test.h +++ b/lib/IRremoteESP8266-2.6.0/test/IRsend_test.h @@ -17,12 +17,14 @@ #ifdef UNIT_TEST // Used to help simulate elapsed time in unit tests. -uint32_t _IRtimer_unittest_now = 0; +extern uint32_t _IRtimer_unittest_now; #endif // UNIT_TEST class IRsendTest : public IRsend { public: uint32_t output[OUTPUT_BUF]; + uint32_t freq[OUTPUT_BUF]; + uint8_t duty[OUTPUT_BUF]; uint16_t last; uint16_t rawbuf[RAW_BUF]; decode_results capture; @@ -40,8 +42,22 @@ class IRsendTest : public IRsend { std::string outputStr() { std::stringstream result; + uint8_t lastduty = UINT8_MAX; // An impossible duty cycle value. + uint32_t lastfreq = 0; // An impossible frequency value. if (last == 0 && output[0] == 0) return ""; for (uint16_t i = 0; i <= last; i++) { + // Display the frequency only if it changes. + if (freq[i] != lastfreq) { + result << "f"; + result << freq[i]; + lastfreq = freq[i]; + } + // Display the duty cycle only if it changes. + if (duty[i] != lastduty) { + result << "d"; + result << static_cast(duty[i]); + lastduty = duty[i]; + } if ((i & 1) != outputOff) // Odd XOR outputOff result << "s"; else @@ -92,6 +108,8 @@ class IRsendTest : public IRsend { output[++last] = usec; else output[last] += usec; + duty[last] = _dutycycle; + freq[last] = _freq_unittest; return 0; } @@ -103,6 +121,8 @@ class IRsendTest : public IRsend { } else { output[++last] = time; } + duty[last] = _dutycycle; + freq[last] = _freq_unittest; } }; diff --git a/lib/IRremoteESP8266-2.5.2.03/test/IRutils_test.cpp b/lib/IRremoteESP8266-2.6.0/test/IRutils_test.cpp similarity index 85% rename from lib/IRremoteESP8266-2.5.2.03/test/IRutils_test.cpp rename to lib/IRremoteESP8266-2.6.0/test/IRutils_test.cpp index 91cf4725c5c5..4a490764977d 100644 --- a/lib/IRremoteESP8266-2.5.2.03/test/IRutils_test.cpp +++ b/lib/IRremoteESP8266-2.6.0/test/IRutils_test.cpp @@ -350,3 +350,68 @@ TEST(TestInvertBits, MoreThan64Bits) { ASSERT_EQ(0xAAAA5555AAAA5555, invertBits(0x5555AAAA5555AAAA, 70)); ASSERT_EQ(0xFFFFFFFFFFFFFFFF, invertBits(0x0, 128)); } + +TEST(TestCountBits, Pointer) { + uint8_t data[14] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13}; + + ASSERT_EQ(0, countBits(data, 0)); + ASSERT_EQ(0, countBits(data, 1)); + ASSERT_EQ(0, countBits(data, 1, true)); + ASSERT_EQ(8, countBits(data, 1, false)); + ASSERT_EQ(1, countBits(data, 2)); + ASSERT_EQ(15, countBits(data, 2, false)); + ASSERT_EQ(1, countBits(data + 1, 1)); + ASSERT_EQ(2, countBits(data, 3)); + ASSERT_EQ(4, countBits(data, 4)); + ASSERT_EQ(25, countBits(data, 14)); + ASSERT_EQ(25, countBits(data, 14)); + ASSERT_EQ(14 * 8, countBits(data, 14, true) + countBits(data, 14, false)); + ASSERT_EQ(125, countBits(data, 14, true, 100)); +} + +TEST(TestCountBits, Integer) { + uint64_t data = 0xAAAAAAAAAAAAAAAA; + + ASSERT_EQ(0, countBits(data, 0)); + ASSERT_EQ(0, countBits(data, 1)); + ASSERT_EQ(0, countBits(data, 1, true)); + ASSERT_EQ(1, countBits(data, 1, false)); + ASSERT_EQ(1, countBits(data, 3)); + ASSERT_EQ(2, countBits(data, 3, false)); + ASSERT_EQ(4, countBits(data, 8)); + ASSERT_EQ(4, countBits(data, 8, false)); + ASSERT_EQ(32, countBits(data, 64)); + ASSERT_EQ(32, countBits(data, 64, false)); + + data = 0; + ASSERT_EQ(0, countBits(data, 1, true)); + ASSERT_EQ(1, countBits(data, 1, false)); + ASSERT_EQ(0, countBits(data, 64)); + ASSERT_EQ(64, countBits(data, 64, false)); + + data = 0xFFFFFFFFFFFFFFFF; + ASSERT_EQ(1, countBits(data, 1, true)); + ASSERT_EQ(0, countBits(data, 1, false)); + ASSERT_EQ(64, countBits(data, 64)); + ASSERT_EQ(0, countBits(data, 64, false)); +} + +TEST(TestStrToDecodeType, strToDecodeType) { + EXPECT_EQ(decode_type_t::NEC, strToDecodeType("NEC")); + EXPECT_EQ(decode_type_t::KELVINATOR, strToDecodeType("KELVINATOR")); + EXPECT_EQ(decode_type_t::UNKNOWN, strToDecodeType("foo")); +} + +TEST(TestUtils, htmlEscape) { + EXPECT_EQ("", htmlEscape("")); + EXPECT_EQ("No Changes", htmlEscape("No Changes")); + EXPECT_EQ("No\tChanges+_%^$@~`\n:\\", htmlEscape("No\tChanges+_%^$@~`\n:\\")); + EXPECT_EQ(""With Changes"", htmlEscape("\"With Changes\"")); + EXPECT_EQ( + "';!‐"<>&#equals;&#{}" + "()", htmlEscape("';!-\"<>=&#{}()")); + EXPECT_EQ("""", htmlEscape("\"\"")); + EXPECT_EQ( + "&quot;&lt;&apos;&gt;&amp;", + htmlEscape(""<'>&")); +} diff --git a/lib/IRremoteESP8266-2.5.2.03/test/Makefile b/lib/IRremoteESP8266-2.6.0/test/Makefile similarity index 71% rename from lib/IRremoteESP8266-2.5.2.03/test/Makefile rename to lib/IRremoteESP8266-2.6.0/test/Makefile index d53014183d92..9a64aaaaa714 100644 --- a/lib/IRremoteESP8266-2.5.2.03/test/Makefile +++ b/lib/IRremoteESP8266-2.6.0/test/Makefile @@ -16,6 +16,7 @@ GTEST_DIR = ../lib/googletest/googletest # Where to find user code. USER_DIR = ../src +INCLUDES = -I$(USER_DIR) -I. # Flags passed to the preprocessor. # Set Google Test's header directory as a system directory, such that @@ -23,7 +24,7 @@ USER_DIR = ../src CPPFLAGS += -isystem $(GTEST_DIR)/include -DUNIT_TEST # Flags passed to the C++ compiler. -CXXFLAGS += -g -Wall -Wextra -pthread +CXXFLAGS += -g -Wall -Wextra -pthread -std=gnu++11 # All tests produced by this Makefile. Remember to add new tests you # created to the list. @@ -36,7 +37,8 @@ TESTS = IRutils_test IRsend_test ir_NEC_test ir_GlobalCache_test \ ir_Toshiba_test ir_Midea_test ir_Magiquest_test ir_Lasertag_test \ ir_Carrier_test ir_Haier_test ir_Hitachi_test ir_GICable_test \ ir_Whirlpool_test ir_Lutron_test ir_Electra_test ir_Pioneer_test \ - ir_MWM_test + ir_MWM_test ir_Vestel_test ir_Teco_test ir_Tcl_test ir_Lego_test IRac_test \ + ir_MitsubishiHeavy_test # All Google Test headers. Usually you shouldn't change this # definition. @@ -80,7 +82,8 @@ PROTOCOLS = ir_NEC.o ir_Sony.o ir_Samsung.o ir_JVC.o ir_RCMM.o ir_RC5_RC6.o \ ir_Kelvinator.o ir_Daikin.o ir_Gree.o ir_Pronto.o ir_Nikai.o ir_Toshiba.o \ ir_Midea.o ir_Magiquest.o ir_Lasertag.o ir_Carrier.o ir_Haier.o \ ir_Hitachi.o ir_GICable.o ir_Whirlpool.o ir_Lutron.o ir_Electra.o \ - ir_Pioneer.o ir_MWM.o + ir_Pioneer.o ir_MWM.o ir_Vestel.o ir_Teco.o ir_Tcl.o ir_Lego.o ir_Argo.o \ + ir_Trotec.o ir_MitsubishiHeavy.o # All the IR Protocol header files. PROTOCOLS_H = $(USER_DIR)/ir_Argo.h \ @@ -93,19 +96,24 @@ PROTOCOLS_H = $(USER_DIR)/ir_Argo.h \ $(USER_DIR)/ir_Daikin.h \ $(USER_DIR)/ir_Kelvinator.h \ $(USER_DIR)/ir_Mitsubishi.h \ + $(USER_DIR)/ir_MitsubishiHeavy.h \ $(USER_DIR)/ir_NEC.h \ $(USER_DIR)/ir_Samsung.h \ $(USER_DIR)/ir_Trotec.h \ $(USER_DIR)/ir_Fujitsu.h \ $(USER_DIR)/ir_LG.h \ - $(USER_DIR)/ir_Panasonic.h + $(USER_DIR)/ir_Panasonic.h \ + $(USER_DIR)/ir_Whirlpool.h \ + $(USER_DIR)/ir_Vestel.h \ + $(USER_DIR)/ir_Tcl.h \ + $(USER_DIR)/ir_Teco.h # Common object files -COMMON_OBJ = IRutils.o IRtimer.o IRsend.o IRrecv.o ir_GlobalCache.o \ +COMMON_OBJ = IRutils.o IRtimer.o IRsend.o IRrecv.o IRac.o ir_GlobalCache.o \ $(PROTOCOLS) gtest_main.a # Common dependencies COMMON_DEPS = $(USER_DIR)/IRrecv.h $(USER_DIR)/IRsend.h $(USER_DIR)/IRtimer.h \ $(USER_DIR)/IRutils.h $(USER_DIR)/IRremoteESP8266.h \ - $(PROTOCOLS_H) + $(USER_DIR)/IRac.h $(PROTOCOLS_H) # Common test dependencies COMMON_TEST_DEPS = $(COMMON_DEPS) IRrecv_test.h IRsend_test.h @@ -136,7 +144,7 @@ IRutils.o : $(USER_DIR)/IRutils.cpp $(USER_DIR)/IRutils.h $(USER_DIR)/IRremoteES $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/IRutils.cpp IRutils_test.o : IRutils_test.cpp $(COMMON_TEST_DEPS) $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -I$(USER_DIR) -c IRutils_test.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c IRutils_test.cpp IRutils_test : IRutils_test.o ir_NEC.o ir_Nikai.o ir_Toshiba.o $(COMMON_OBJ) gtest_main.a $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ @@ -148,7 +156,7 @@ IRsend.o : $(USER_DIR)/IRsend.cpp $(USER_DIR)/IRsend.h $(USER_DIR)/IRremoteESP82 $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/IRsend.cpp IRsend_test.o : IRsend_test.cpp $(USER_DIR)/IRsend.h $(USER_DIR)/IRrecv.h IRsend_test.h $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -I$(USER_DIR) -c IRsend_test.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c IRsend_test.cpp IRsend_test : IRsend_test.o $(COMMON_OBJ) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ @@ -157,16 +165,25 @@ IRrecv.o : $(USER_DIR)/IRrecv.cpp $(USER_DIR)/IRrecv.h $(USER_DIR)/IRremoteESP82 $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/IRrecv.cpp IRrecv_test.o : IRrecv_test.cpp $(USER_DIR)/IRsend.h $(USER_DIR)/IRrecv.h IRsend_test.h $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -I$(USER_DIR) -c IRrecv_test.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c IRrecv_test.cpp IRrecv_test : IRrecv_test.o $(COMMON_OBJ) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ +IRac.o : $(USER_DIR)/IRac.cpp $(USER_DIR)/IRac.h $(COMMON_DEPS) $(GTEST_HEADERS) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/IRac.cpp + +IRac_test.o : IRac_test.cpp $(USER_DIR)/IRac.h $(COMMON_DEPS) $(GTEST_HEADERS) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c IRac_test.cpp + +IRac_test : IRac_test.o $(COMMON_OBJ) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ + ir_NEC.o : $(USER_DIR)/ir_NEC.cpp $(USER_DIR)/ir_NEC.h $(COMMON_DEPS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_NEC.cpp ir_NEC_test.o : ir_NEC_test.cpp $(COMMON_TEST_DEPS) $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -I$(USER_DIR) -c ir_NEC_test.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c ir_NEC_test.cpp ir_NEC_test : $(COMMON_OBJ) ir_NEC_test.o $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ @@ -175,7 +192,7 @@ ir_GlobalCache.o : $(USER_DIR)/ir_GlobalCache.cpp $(COMMON_DEPS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_GlobalCache.cpp ir_GlobalCache_test.o : ir_GlobalCache_test.cpp $(COMMON_TEST_DEPS) $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -I$(USER_DIR) -c ir_GlobalCache_test.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c ir_GlobalCache_test.cpp ir_GlobalCache_test : $(COMMON_OBJ) ir_GlobalCache_test.o $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ @@ -184,7 +201,7 @@ ir_Sherwood.o : $(USER_DIR)/ir_Sherwood.cpp $(COMMON_DEPS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Sherwood.cpp ir_Sherwood_test.o : ir_Sherwood_test.cpp $(COMMON_TEST_DEPS) $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -I$(USER_DIR) -c ir_Sherwood_test.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c ir_Sherwood_test.cpp ir_Sherwood_test : $(COMMON_OBJ) ir_Sherwood_test.o $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ @@ -193,25 +210,25 @@ ir_Sony.o : $(USER_DIR)/ir_Sony.cpp $(COMMON_DEPS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Sony.cpp ir_Sony_test.o : ir_Sony_test.cpp $(COMMON_TEST_DEPS) $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -I$(USER_DIR) -c ir_Sony_test.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c ir_Sony_test.cpp ir_Sony_test : $(COMMON_OBJ) ir_Sony_test.o $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ ir_Samsung.o : $(USER_DIR)/ir_Samsung.cpp $(USER_DIR)/ir_Samsung.h $(COMMON_DEPS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Samsung.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/ir_Samsung.cpp ir_Samsung_test.o : ir_Samsung_test.cpp $(USER_DIR)/ir_Samsung.h $(COMMON_TEST_DEPS) $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -I$(USER_DIR) -c ir_Samsung_test.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c ir_Samsung_test.cpp ir_Samsung_test : $(COMMON_OBJ) ir_Samsung_test.o $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ ir_Kelvinator.o : $(USER_DIR)/ir_Kelvinator.cpp $(USER_DIR)/ir_Kelvinator.h $(COMMON_DEPS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Kelvinator.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/ir_Kelvinator.cpp ir_Kelvinator_test.o : ir_Kelvinator_test.cpp $(USER_DIR)/ir_Kelvinator.h $(COMMON_TEST_DEPS) $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -I$(USER_DIR) -c ir_Kelvinator_test.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c ir_Kelvinator_test.cpp ir_Kelvinator_test : $(COMMON_OBJ) ir_Kelvinator_test.o $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ @@ -220,7 +237,7 @@ ir_JVC.o : $(USER_DIR)/ir_JVC.cpp $(COMMON_DEPS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_JVC.cpp ir_JVC_test.o : ir_JVC_test.cpp $(COMMON_TEST_DEPS) $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -I$(USER_DIR) -c ir_JVC_test.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c ir_JVC_test.cpp ir_JVC_test : $(COMMON_OBJ) ir_JVC_test.o $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ @@ -229,7 +246,7 @@ ir_RCMM.o : $(USER_DIR)/ir_RCMM.cpp $(COMMON_DEPS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_RCMM.cpp ir_RCMM_test.o : ir_RCMM_test.cpp $(COMMON_TEST_DEPS) $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -I$(USER_DIR) -c ir_RCMM_test.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c ir_RCMM_test.cpp ir_RCMM_test : $(COMMON_OBJ) ir_RCMM_test.o $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ @@ -238,25 +255,34 @@ ir_LG.o : $(USER_DIR)/ir_LG.h $(USER_DIR)/ir_LG.cpp $(COMMON_DEPS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_LG.cpp ir_LG_test.o : ir_LG_test.cpp $(USER_DIR)/ir_LG.h $(COMMON_TEST_DEPS) $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -I$(USER_DIR) -c ir_LG_test.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c ir_LG_test.cpp ir_LG_test : $(COMMON_OBJ) ir_LG_test.o $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ ir_Mitsubishi.o : $(USER_DIR)/ir_Mitsubishi.h $(USER_DIR)/ir_Mitsubishi.cpp $(COMMON_DEPS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Mitsubishi.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/ir_Mitsubishi.cpp ir_Mitsubishi_test.o : ir_Mitsubishi_test.cpp $(USER_DIR)/ir_Mitsubishi.h $(COMMON_TEST_DEPS) $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -I$(USER_DIR) -c ir_Mitsubishi_test.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c ir_Mitsubishi_test.cpp ir_Mitsubishi_test : $(COMMON_OBJ) ir_Mitsubishi_test.o $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ +ir_MitsubishiHeavy.o : $(USER_DIR)/ir_MitsubishiHeavy.h $(USER_DIR)/ir_MitsubishiHeavy.cpp $(COMMON_DEPS) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/ir_MitsubishiHeavy.cpp + +ir_MitsubishiHeavy_test.o : ir_MitsubishiHeavy_test.cpp $(USER_DIR)/ir_MitsubishiHeavy.h $(COMMON_TEST_DEPS) $(GTEST_HEADERS) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c ir_MitsubishiHeavy_test.cpp + +ir_MitsubishiHeavy_test : $(COMMON_OBJ) ir_MitsubishiHeavy_test.o + $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ + ir_Fujitsu.o : $(USER_DIR)/ir_Fujitsu.h $(USER_DIR)/ir_Fujitsu.cpp $(COMMON_DEPS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Fujitsu.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/ir_Fujitsu.cpp ir_Fujitsu_test.o : ir_Fujitsu_test.cpp $(USER_DIR)/ir_Fujitsu.h $(COMMON_TEST_DEPS) $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -I$(USER_DIR) -c ir_Fujitsu_test.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c ir_Fujitsu_test.cpp ir_Fujitsu_test : $(COMMON_OBJ) ir_Fujitsu_test.o $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ @@ -265,7 +291,7 @@ ir_Sharp.o : $(USER_DIR)/ir_Sharp.cpp $(COMMON_DEPS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Sharp.cpp ir_Sharp_test.o : ir_Sharp_test.cpp $(COMMON_TEST_DEPS) $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -I$(USER_DIR) -c ir_Sharp_test.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c ir_Sharp_test.cpp ir_Sharp_test : $(COMMON_OBJ) ir_Sharp_test.o $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ @@ -274,16 +300,16 @@ ir_RC5_RC6.o : $(USER_DIR)/ir_RC5_RC6.cpp $(COMMON_DEPS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_RC5_RC6.cpp ir_RC5_RC6_test.o : ir_RC5_RC6_test.cpp $(COMMON_TEST_DEPS) $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -I$(USER_DIR) -c ir_RC5_RC6_test.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c ir_RC5_RC6_test.cpp ir_RC5_RC6_test : $(COMMON_OBJ) ir_RC5_RC6_test.o $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ ir_Panasonic.o : $(USER_DIR)/ir_Panasonic.cpp $(COMMON_DEPS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Panasonic.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/ir_Panasonic.cpp ir_Panasonic_test.o : ir_Panasonic_test.cpp $(COMMON_TEST_DEPS) $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -I$(USER_DIR) -c ir_Panasonic_test.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c ir_Panasonic_test.cpp ir_Panasonic_test : $(COMMON_OBJ) ir_Panasonic_test.o $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ @@ -292,7 +318,7 @@ ir_Dish.o : $(USER_DIR)/ir_Dish.cpp $(COMMON_DEPS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Dish.cpp ir_Dish_test.o : ir_Dish_test.cpp $(COMMON_TEST_DEPS) $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -I$(USER_DIR) -c ir_Dish_test.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c ir_Dish_test.cpp ir_Dish_test : $(COMMON_OBJ) ir_Dish_test.o $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ @@ -301,16 +327,16 @@ ir_Whynter.o : $(USER_DIR)/ir_Whynter.cpp $(COMMON_DEPS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Whynter.cpp ir_Whynter_test.o : ir_Whynter_test.cpp $(COMMON_TEST_DEPS) $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -I$(USER_DIR) -c ir_Whynter_test.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c ir_Whynter_test.cpp ir_Whynter_test : $(COMMON_OBJ) ir_Whynter_test.o $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ ir_Coolix.o : $(USER_DIR)/ir_Coolix.cpp $(COMMON_DEPS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Coolix.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/ir_Coolix.cpp ir_Coolix_test.o : ir_Coolix_test.cpp $(USER_DIR)/ir_Coolix.h $(COMMON_TEST_DEPS) $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -I$(USER_DIR) -c ir_Coolix_test.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c ir_Coolix_test.cpp ir_Coolix_test : $(COMMON_OBJ) ir_Coolix_test.o $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ @@ -319,7 +345,7 @@ ir_Aiwa.o : $(USER_DIR)/ir_Aiwa.cpp $(COMMON_DEPS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Aiwa.cpp ir_Aiwa_test.o : ir_Aiwa_test.cpp $(COMMON_TEST_DEPS) $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -I$(USER_DIR) -c ir_Aiwa_test.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c ir_Aiwa_test.cpp ir_Aiwa_test : $(COMMON_OBJ) ir_Aiwa_test.o $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ @@ -328,7 +354,7 @@ ir_Denon.o : $(USER_DIR)/ir_Denon.cpp $(COMMON_DEPS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Denon.cpp ir_Denon_test.o : ir_Denon_test.cpp $(COMMON_TEST_DEPS) $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -I$(USER_DIR) -c ir_Denon_test.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c ir_Denon_test.cpp ir_Denon_test : $(COMMON_OBJ) ir_Denon_test.o $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ @@ -337,25 +363,25 @@ ir_Sanyo.o : $(USER_DIR)/ir_Sanyo.cpp $(COMMON_DEPS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Sanyo.cpp ir_Sanyo_test.o : ir_Sanyo_test.cpp $(COMMON_TEST_DEPS) $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -I$(USER_DIR) -c ir_Sanyo_test.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c ir_Sanyo_test.cpp ir_Sanyo_test : $(COMMON_OBJ) ir_Sanyo_test.o $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ ir_Daikin.o : $(USER_DIR)/ir_Daikin.cpp $(USER_DIR)/ir_Daikin.h $(COMMON_DEPS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Daikin.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/ir_Daikin.cpp ir_Daikin_test.o : ir_Daikin_test.cpp $(USER_DIR)/ir_Daikin.h $(COMMON_TEST_DEPS) $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -I$(USER_DIR) -c ir_Daikin_test.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c ir_Daikin_test.cpp ir_Daikin_test : $(COMMON_OBJ) ir_Daikin_test.o $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ ir_Gree.o : $(USER_DIR)/ir_Gree.cpp $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Gree.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/ir_Gree.cpp ir_Gree_test.o : ir_Gree_test.cpp $(COMMON_TEST_DEPS) $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -I$(USER_DIR) -c ir_Gree_test.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c ir_Gree_test.cpp ir_Gree_test : $(COMMON_OBJ) ir_Gree_test.o ir_Kelvinator.o $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ @@ -364,7 +390,7 @@ ir_Pronto.o : $(USER_DIR)/ir_Pronto.cpp $(GTEST_HEADERS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Pronto.cpp ir_Pronto_test.o : ir_Pronto_test.cpp $(COMMON_TEST_DEPS) $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -I$(USER_DIR) -c ir_Pronto_test.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c ir_Pronto_test.cpp ir_Pronto_test : $(COMMON_OBJ) ir_Pronto_test.o $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ @@ -373,25 +399,25 @@ ir_Nikai.o : $(USER_DIR)/ir_Nikai.cpp $(GTEST_HEADERS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Nikai.cpp ir_Nikai_test.o : ir_Nikai_test.cpp $(COMMON_TEST_DEPS) $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -I$(USER_DIR) -c ir_Nikai_test.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c ir_Nikai_test.cpp ir_Nikai_test : $(COMMON_OBJ) ir_Nikai_test.o $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ ir_Toshiba.o : $(USER_DIR)/ir_Toshiba.cpp $(USER_DIR)/ir_Toshiba.h $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Toshiba.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/ir_Toshiba.cpp ir_Toshiba_test.o : ir_Toshiba_test.cpp $(USER_DIR)/ir_Toshiba.h $(COMMON_TEST_DEPS) $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -I$(USER_DIR) -c ir_Toshiba_test.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c ir_Toshiba_test.cpp ir_Toshiba_test : $(COMMON_OBJ) ir_Toshiba_test.o $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ ir_Midea.o : $(USER_DIR)/ir_Midea.cpp $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Midea.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/ir_Midea.cpp ir_Midea_test.o : ir_Midea_test.cpp $(COMMON_TEST_DEPS) $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -I$(USER_DIR) -c ir_Midea_test.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c ir_Midea_test.cpp ir_Midea_test : $(COMMON_OBJ) ir_Midea_test.o $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ @@ -400,7 +426,7 @@ ir_Magiquest.o : $(USER_DIR)/ir_Magiquest.cpp $(GTEST_HEADERS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Magiquest.cpp ir_Magiquest_test.o : ir_Magiquest_test.cpp $(COMMON_TEST_DEPS) $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -I$(USER_DIR) -c ir_Magiquest_test.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c ir_Magiquest_test.cpp ir_Magiquest_test : $(COMMON_OBJ) ir_Magiquest_test.o $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ @@ -409,7 +435,7 @@ ir_Lasertag.o : $(USER_DIR)/ir_Lasertag.cpp $(USER_DIR)/ir_RC5_RC6.cpp $(GTEST_H $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Lasertag.cpp ir_Lasertag_test.o : ir_Lasertag_test.cpp $(USER_DIR)/ir_RC5_RC6.cpp $(COMMON_TEST_DEPS) $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -I$(USER_DIR) -c ir_Lasertag_test.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c ir_Lasertag_test.cpp ir_Lasertag_test : $(COMMON_OBJ) ir_Lasertag_test.o ir_RC5_RC6.o $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ @@ -418,25 +444,25 @@ ir_Carrier.o : $(USER_DIR)/ir_Carrier.cpp $(COMMON_DEPS) $(GTEST_HEADERS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Carrier.cpp ir_Carrier_test.o : ir_Carrier_test.cpp $(COMMON_TEST_DEPS) $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -I$(USER_DIR) -c ir_Carrier_test.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c ir_Carrier_test.cpp ir_Carrier_test : $(COMMON_OBJ) ir_Carrier_test.o $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ ir_Haier.o : $(USER_DIR)/ir_Haier.cpp $(USER_DIR)/ir_Haier.h $(COMMON_DEPS) $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Haier.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/ir_Haier.cpp ir_Haier_test.o : ir_Haier_test.cpp $(COMMON_TEST_DEPS) $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -I$(USER_DIR) -c ir_Haier_test.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c ir_Haier_test.cpp ir_Haier_test : $(COMMON_OBJ) ir_Haier_test.o $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ ir_Hitachi.o : $(USER_DIR)/ir_Hitachi.cpp $(COMMON_DEPS) $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Hitachi.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/ir_Hitachi.cpp ir_Hitachi_test.o : ir_Hitachi_test.cpp $(COMMON_TEST_DEPS) $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -I$(USER_DIR) -c ir_Hitachi_test.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c ir_Hitachi_test.cpp ir_Hitachi_test : $(COMMON_OBJ) ir_Hitachi_test.o $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ @@ -445,16 +471,16 @@ ir_GICable.o : $(USER_DIR)/ir_GICable.cpp $(COMMON_DEPS) $(GTEST_HEADERS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_GICable.cpp ir_GICable_test.o : ir_GICable_test.cpp $(COMMON_TEST_DEPS) $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -I$(USER_DIR) -c ir_GICable_test.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c ir_GICable_test.cpp ir_GICable_test : $(COMMON_OBJ) ir_GICable_test.o $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ ir_Whirlpool.o : $(USER_DIR)/ir_Whirlpool.cpp $(COMMON_DEPS) $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Whirlpool.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/ir_Whirlpool.cpp ir_Whirlpool_test.o : ir_Whirlpool_test.cpp $(COMMON_TEST_DEPS) $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -I$(USER_DIR) -c ir_Whirlpool_test.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c ir_Whirlpool_test.cpp ir_Whirlpool_test : $(COMMON_OBJ) ir_Whirlpool_test.o $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ @@ -463,7 +489,7 @@ ir_Lutron.o : $(USER_DIR)/ir_Lutron.cpp $(COMMON_DEPS) $(GTEST_HEADERS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Lutron.cpp ir_Lutron_test.o : ir_Lutron_test.cpp $(COMMON_TEST_DEPS) $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -I$(USER_DIR) -c ir_Lutron_test.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c ir_Lutron_test.cpp ir_Lutron_test : $(COMMON_OBJ) ir_Lutron_test.o $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ @@ -472,7 +498,7 @@ ir_Electra.o : $(USER_DIR)/ir_Electra.cpp $(COMMON_DEPS) $(GTEST_HEADERS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Electra.cpp ir_Electra_test.o : ir_Electra_test.cpp $(COMMON_TEST_DEPS) $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -I$(USER_DIR) -c ir_Electra_test.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c ir_Electra_test.cpp ir_Electra_test : $(COMMON_OBJ) ir_Electra_test.o $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ @@ -481,7 +507,7 @@ ir_Pioneer.o : $(USER_DIR)/ir_Pioneer.cpp $(COMMON_DEPS) $(GTEST_HEADERS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Pioneer.cpp ir_Pioneer_test.o : ir_Pioneer_test.cpp $(COMMON_TEST_DEPS) $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -I$(USER_DIR) -c ir_Pioneer_test.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c ir_Pioneer_test.cpp ir_Pioneer_test : $(COMMON_OBJ) ir_Pioneer_test.o $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ @@ -490,7 +516,49 @@ ir_MWM.o : $(USER_DIR)/ir_MWM.cpp $(COMMON_DEPS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_MWM.cpp ir_MWM_test.o : ir_MWM_test.cpp $(COMMON_TEST_DEPS) $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -I$(USER_DIR) -c ir_MWM_test.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c ir_MWM_test.cpp ir_MWM_test : $(COMMON_OBJ) ir_MWM_test.o $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ + +ir_Vestel.o : $(USER_DIR)/ir_Vestel.cpp $(COMMON_DEPS) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/ir_Vestel.cpp + +ir_Vestel_test.o : ir_Vestel_test.cpp $(COMMON_TEST_DEPS) $(GTEST_HEADERS) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c ir_Vestel_test.cpp + +ir_Vestel_test : $(COMMON_OBJ) ir_Vestel_test.o + $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ + +ir_Teco.o : $(USER_DIR)/ir_Teco.cpp $(COMMON_DEPS) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/ir_Teco.cpp + +ir_Teco_test.o : ir_Teco_test.cpp $(COMMON_TEST_DEPS) $(GTEST_HEADERS) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c ir_Teco_test.cpp + +ir_Teco_test : $(COMMON_OBJ) ir_Teco_test.o + $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ + +ir_Tcl.o : $(USER_DIR)/ir_Tcl.cpp $(COMMON_DEPS) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/ir_Tcl.cpp + +ir_Tcl_test.o : ir_Tcl_test.cpp $(COMMON_TEST_DEPS) $(GTEST_HEADERS) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c ir_Tcl_test.cpp + +ir_Tcl_test : $(COMMON_OBJ) ir_Tcl_test.o + $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ + +ir_Lego.o : $(USER_DIR)/ir_Lego.cpp $(COMMON_DEPS) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Lego.cpp + +ir_Lego_test.o : ir_Lego_test.cpp $(COMMON_TEST_DEPS) $(GTEST_HEADERS) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c ir_Lego_test.cpp + +ir_Lego_test : $(COMMON_OBJ) ir_Lego_test.o + $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ + +ir_Argo.o : $(USER_DIR)/ir_Argo.cpp $(COMMON_DEPS) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/ir_Argo.cpp + +ir_Trotec.o : $(USER_DIR)/ir_Trotec.cpp $(COMMON_DEPS) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/ir_Trotec.cpp diff --git a/lib/IRremoteESP8266-2.5.2.03/test/ir_Aiwa_test.cpp b/lib/IRremoteESP8266-2.6.0/test/ir_Aiwa_test.cpp similarity index 99% rename from lib/IRremoteESP8266-2.5.2.03/test/ir_Aiwa_test.cpp rename to lib/IRremoteESP8266-2.6.0/test/ir_Aiwa_test.cpp index c5469d4a5249..2fa63afe625e 100644 --- a/lib/IRremoteESP8266-2.5.2.03/test/ir_Aiwa_test.cpp +++ b/lib/IRremoteESP8266-2.6.0/test/ir_Aiwa_test.cpp @@ -14,6 +14,7 @@ TEST(TestSendAiwa, SendDataOnly) { irsend.reset(); irsend.sendAiwaRCT501(0x7F); // Aiwa Power Toggle. EXPECT_EQ( + "f38000d33" "m8960s4480" "m560s560m560s1680m560s1680m560s1680m560s560m560s1680m560s1680m560s560" "m560s560m560s560m560s560m560s560m560s560m560s1680m560s560m560s560" @@ -43,6 +44,7 @@ TEST(TestSendAiwa, SendWithRepeats) { irsend.reset(); irsend.sendAiwaRCT501(0x7F, kAiwaRcT501Bits, 0); // No repeats. EXPECT_EQ( + "f38000d33" "m8960s4480" "m560s560m560s1680m560s1680m560s1680m560s560m560s1680m560s1680m560s560" "m560s560m560s560m560s560m560s560m560s560m560s1680m560s560m560s560" @@ -54,6 +56,7 @@ TEST(TestSendAiwa, SendWithRepeats) { irsend.reset(); irsend.sendAiwaRCT501(0x7F, kAiwaRcT501Bits, 1); // 1 repeat. EXPECT_EQ( + "f38000d33" "m8960s4480" "m560s560m560s1680m560s1680m560s1680m560s560m560s1680m560s1680m560s560" "m560s560m560s560m560s560m560s560m560s560m560s1680m560s560m560s560" @@ -66,6 +69,7 @@ TEST(TestSendAiwa, SendWithRepeats) { irsend.reset(); irsend.sendAiwaRCT501(0x7F, kAiwaRcT501Bits, 2); // 2 repeats. EXPECT_EQ( + "f38000d33" "m8960s4480" "m560s560m560s1680m560s1680m560s1680m560s560m560s1680m560s1680m560s560" "m560s560m560s560m560s560m560s560m560s560m560s1680m560s560m560s560" @@ -86,6 +90,7 @@ TEST(TestSendAiwa, SendUnusualSize) { irsend.reset(); irsend.sendAiwaRCT501(0x12, 8); EXPECT_EQ( + "f38000d33" "m8960s4480" "m560s560m560s1680m560s1680m560s1680m560s560m560s1680m560s1680m560s560" "m560s560m560s560m560s560m560s560m560s560m560s1680m560s560m560s560" @@ -98,6 +103,7 @@ TEST(TestSendAiwa, SendUnusualSize) { irsend.reset(); irsend.sendAiwaRCT501(0x1234567890, 37); EXPECT_EQ( + "f38000d33" "m8960s4480" "m560s560m560s1680m560s1680m560s1680m560s560m560s1680m560s1680m560s560" "m560s560m560s560m560s560m560s560m560s560m560s1680m560s560m560s560" diff --git a/lib/IRremoteESP8266-2.5.2.03/test/ir_Carrier_test.cpp b/lib/IRremoteESP8266-2.6.0/test/ir_Carrier_test.cpp similarity index 99% rename from lib/IRremoteESP8266-2.5.2.03/test/ir_Carrier_test.cpp rename to lib/IRremoteESP8266-2.6.0/test/ir_Carrier_test.cpp index 24bdc232ab59..053b31dd489b 100644 --- a/lib/IRremoteESP8266-2.5.2.03/test/ir_Carrier_test.cpp +++ b/lib/IRremoteESP8266-2.6.0/test/ir_Carrier_test.cpp @@ -15,6 +15,7 @@ TEST(TestSendCarrierAC, SendDataOnly) { irsend.reset(); irsend.sendCarrierAC(0x0); EXPECT_EQ( + "f38000d50" "m8532s4228" "m628s532m628s532m628s532m628s532m628s532m628s532m628s532m628s532" "m628s532m628s532m628s532m628s532m628s532m628s532m628s532m628s532" @@ -37,6 +38,7 @@ TEST(TestSendCarrierAC, SendDataOnly) { irsend.reset(); irsend.sendCarrierAC(0x12345678); EXPECT_EQ( + "f38000d50" "m8532s4228" "m628s532m628s532m628s532m628s1320m628s532m628s532m628s1320m628s532" "m628s532m628s532m628s1320m628s1320m628s532m628s1320m628s532m628s532" @@ -60,6 +62,7 @@ TEST(TestSendCarrierAC, SendDataOnly) { irsend.reset(); irsend.sendCarrierAC(0x4CCA541D); EXPECT_EQ( + "f38000d50" "m8532s4228" "m628s532m628s1320m628s532m628s532m628s1320m628s1320m628s532m628s532" "m628s1320m628s1320m628s532m628s532m628s1320m628s532m628s1320m628s532" @@ -89,6 +92,7 @@ TEST(TestSendCarrierAC, SendWithRepeats) { irsend.reset(); irsend.sendCarrierAC(0x12345678, kCarrierAcBits, 2); // two repeats. EXPECT_EQ( + "f38000d50" "m8532s4228" "m628s532m628s532m628s532m628s1320m628s532m628s532m628s1320m628s532" "m628s532m628s532m628s1320m628s1320m628s532m628s1320m628s532m628s532" diff --git a/lib/IRremoteESP8266-2.5.2.03/test/ir_Coolix_test.cpp b/lib/IRremoteESP8266-2.6.0/test/ir_Coolix_test.cpp similarity index 70% rename from lib/IRremoteESP8266-2.5.2.03/test/ir_Coolix_test.cpp rename to lib/IRremoteESP8266-2.6.0/test/ir_Coolix_test.cpp index 8b096ffca824..0f97c5eade54 100644 --- a/lib/IRremoteESP8266-2.5.2.03/test/ir_Coolix_test.cpp +++ b/lib/IRremoteESP8266-2.6.0/test/ir_Coolix_test.cpp @@ -15,6 +15,15 @@ TEST(TestSendCoolix, SendDataOnly) { irsend.reset(); irsend.sendCOOLIX(0x0); EXPECT_EQ( + "f38000d50" + "m4480s4480" + "m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560" + "m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680" + "m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560" + "m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680" + "m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560" + "m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680" + "m560s5040" "m4480s4480" "m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560" "m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680" @@ -28,6 +37,15 @@ TEST(TestSendCoolix, SendDataOnly) { irsend.reset(); irsend.sendCOOLIX(0xAA55AA); EXPECT_EQ( + "f38000d50" + "m4480s4480" + "m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560" + "m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680" + "m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680" + "m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560" + "m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560" + "m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680" + "m560s5040" "m4480s4480" "m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560" "m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680" @@ -41,6 +59,15 @@ TEST(TestSendCoolix, SendDataOnly) { irsend.reset(); irsend.sendCOOLIX(0xFFFFFF); EXPECT_EQ( + "f38000d50" + "m4480s4480" + "m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680" + "m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560" + "m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680" + "m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560" + "m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680" + "m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560" + "m560s5040" "m4480s4480" "m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680" "m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560" @@ -60,6 +87,7 @@ TEST(TestSendCoolix, SendWithRepeats) { irsend.reset(); irsend.sendCOOLIX(0xAA55AA, kCoolixBits, 1); // 1 repeat. EXPECT_EQ( + "f38000d50" "m4480s4480" "m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560" "m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680" @@ -79,6 +107,7 @@ TEST(TestSendCoolix, SendWithRepeats) { irsend.outputStr()); irsend.sendCOOLIX(0xAA55AA, kCoolixBits, 2); // 2 repeats. EXPECT_EQ( + "f38000d50" "m4480s4480" "m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560" "m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680" @@ -114,6 +143,11 @@ TEST(TestSendCoolix, SendUnusualSize) { irsend.reset(); irsend.sendCOOLIX(0x0, 8); EXPECT_EQ( + "f38000d50" + "m4480s4480" + "m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560" + "m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680" + "m560s5040" "m4480s4480" "m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560" "m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680" @@ -123,6 +157,25 @@ TEST(TestSendCoolix, SendUnusualSize) { irsend.reset(); irsend.sendCOOLIX(0x1234567890ABCDEF, 64); EXPECT_EQ( + "f38000d50" + "m4480s4480" + "m560s560m560s560m560s560m560s1680m560s560m560s560m560s1680m560s560" + "m560s1680m560s1680m560s1680m560s560m560s1680m560s1680m560s560m560s1680" + "m560s560m560s560m560s1680m560s1680m560s560m560s1680m560s560m560s560" + "m560s1680m560s1680m560s560m560s560m560s1680m560s560m560s1680m560s1680" + "m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s1680m560s560" + "m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s560m560s1680" + "m560s560m560s1680m560s1680m560s1680m560s1680m560s560m560s560m560s560" + "m560s1680m560s560m560s560m560s560m560s560m560s1680m560s1680m560s1680" + "m560s1680m560s560m560s560m560s1680m560s560m560s560m560s560m560s560" + "m560s560m560s1680m560s1680m560s560m560s1680m560s1680m560s1680m560s1680" + "m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s1680" + "m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s560" + "m560s1680m560s1680m560s560m560s560m560s1680m560s1680m560s560m560s1680" + "m560s560m560s560m560s1680m560s1680m560s560m560s560m560s1680m560s560" + "m560s1680m560s1680m560s1680m560s560m560s1680m560s1680m560s1680m560s1680" + "m560s560m560s560m560s560m560s1680m560s560m560s560m560s560m560s560" + "m560s5040" "m4480s4480" "m560s560m560s560m560s560m560s1680m560s560m560s560m560s1680m560s560" "m560s1680m560s1680m560s1680m560s560m560s1680m560s1680m560s560m560s1680" @@ -411,7 +464,7 @@ TEST(TestCoolixACClass, HumanReadable) { // Initial starting point. EXPECT_EQ( - "Power: On, Fan: 5 (AUTO), Mode: 2 (AUTO), Temp: 25C, " + "Power: On, Mode: 2 (AUTO), Fan: 5 (AUTO), Temp: 25C, " "Zone Follow: Off, Sensor Temp: Ignored", ircoolix.toString()); @@ -420,11 +473,11 @@ TEST(TestCoolixACClass, HumanReadable) { ircoolix.setMode(kCoolixCool); ircoolix.setFan(kCoolixFanMin); EXPECT_EQ( - "Power: On, Fan: 4 (MIN), Mode: 0 (COOL), Temp: 22C, " + "Power: On, Mode: 0 (COOL), Fan: 4 (MIN), Temp: 22C, " "Zone Follow: On, Sensor Temp: 24C", ircoolix.toString()); ircoolix.setSwing(); - EXPECT_EQ("Power: On, Fan: 3 (UNKNOWN), Swing: Toggle", ircoolix.toString()); + EXPECT_EQ("Power: On, Swing: Toggle", ircoolix.toString()); ircoolix.setPower(false); EXPECT_EQ("Power: Off", ircoolix.toString()); } @@ -434,12 +487,113 @@ TEST(TestCoolixACClass, KnownExamples) { ircoolix.setRaw(0b101100101011111111100100); EXPECT_EQ( - "Power: On, Fan: 5 (AUTO), Mode: 4 (FAN), Zone Follow: Off, " + "Power: On, Mode: 4 (FAN), Fan: 5 (AUTO), Zone Follow: Off, " "Sensor Temp: Ignored", ircoolix.toString()); ircoolix.setRaw(0b101100101001111100000000); EXPECT_EQ( - "Power: On, Fan: 4 (MIN), Mode: 0 (COOL), Temp: 17C, " + "Power: On, Mode: 0 (COOL), Fan: 4 (MIN), Temp: 17C, " "Zone Follow: Off, Sensor Temp: Ignored", ircoolix.toString()); } + +TEST(TestCoolixACClass, Issue579FanAuto0) { + IRCoolixAC ircoolix(0); + + ircoolix.setRaw(0xB21F28); + EXPECT_EQ( + "Power: On, Mode: 2 (AUTO), Fan: 0 (AUTO0), Temp: 20C, " + "Zone Follow: Off, Sensor Temp: Ignored", + ircoolix.toString()); +} + +TEST(TestCoolixACClass, RealCaptureExample) { + IRsendTest irsend(0); + IRrecv irrecv(0); + + // From Issue #579 + uint16_t powerOffRawData[199] = { + 4444, 4434, 590, 1578, 698, 446, 590, 1578, 622, 1596, 622, 500, + 644, 476, 644, 1548, 588, 532, 594, 530, 612, 1578, 590, 532, + 588, 534, 672, 1518, 594, 1598, 590, 510, 612, 1580, 644, 480, + 612, 1578, 644, 1548, 644, 1548, 594, 1598, 642, 506, 644, 1550, + 644, 1548, 594, 1600, 644, 478, 644, 478, 642, 480, 644, 478, + 642, 1548, 594, 530, 590, 532, 614, 1578, 644, 1548, 594, 1600, + 588, 534, 566, 556, 588, 530, 590, 532, 586, 514, 612, 532, + 588, 532, 590, 534, 588, 1578, 642, 1576, 642, 1550, 588, 1602, + 588, 1580, 642, 4712, 4546, 4406, 588, 1606, 642, 478, 644, 1550, + 590, 1604, 588, 534, 586, 532, 586, 1582, 642, 480, 642, 480, + 668, 1550, 642, 480, 642, 478, 642, 1552, 612, 1578, 586, 538, + 588, 1580, 674, 472, 590, 1602, 586, 1580, 618, 1576, 642, 1548, + 594, 530, 590, 1584, 608, 1578, 644, 1550, 642, 480, 642, 478, + 642, 480, 642, 480, 642, 1550, 590, 530, 592, 528, 592, 1602, + 642, 1548, 592, 1604, 586, 584, 642, 480, 640, 480, 640, 480, + 642, 480, 642, 480, 642, 480, 642, 480, 642, 1552, 590, 1604, + 588, 1578, 642, 1552, 640, 1550, 592}; // COOLIX B27BE0 + + irsend.begin(); + + irsend.reset(); + + irsend.sendRaw(powerOffRawData, 199, 38000); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(COOLIX, irsend.capture.decode_type); + EXPECT_EQ(kCoolixBits, irsend.capture.bits); + EXPECT_EQ(kCoolixOff, irsend.capture.value); + EXPECT_EQ(0x0, irsend.capture.address); + EXPECT_EQ(0x0, irsend.capture.command); +} + + +// Tests to debug/fix: +// https://github.com/markszabo/IRremoteESP8266/issues/624 +TEST(TestCoolixACClass, Issue624HandleSpecialStatesBetter) { + IRCoolixAC ac(0); + ac.begin(); + // Default + EXPECT_EQ( + "Power: On, Mode: 2 (AUTO), Fan: 5 (AUTO), Temp: 25C, Zone Follow: Off, " + "Sensor Temp: Ignored", + ac.toString()); + EXPECT_EQ(0xB2BFC8, ac.getRaw()); + // Change of settings. + ac.setPower(true); + ac.setTemp(24); + ac.setMode(kCoolixCool); + ac.setFan(kCoolixFanAuto); + EXPECT_EQ( + "Power: On, Mode: 0 (COOL), Fan: 5 (AUTO), Temp: 24C, Zone Follow: Off, " + "Sensor Temp: Ignored", + ac.toString()); + EXPECT_EQ(0xB2BF40, ac.getRaw()); + // Turn the unit off. + ac.setPower(false); + EXPECT_EQ( + "Power: Off", + ac.toString()); + EXPECT_EQ(kCoolixOff, ac.getRaw()); + // Repeat change of settings. + ac.setPower(true); + ac.setTemp(24); + ac.setMode(kCoolixCool); + ac.setFan(kCoolixFanAuto); + EXPECT_EQ( + "Power: On, Mode: 0 (COOL), Fan: 5 (AUTO), Temp: 24C, Zone Follow: Off, " + "Sensor Temp: Ignored", + ac.toString()); + EXPECT_EQ(0xB2BF40, ac.getRaw()); + + // Now test if we setRaw() a special state first. + ac.setRaw(kCoolixSwing); + // Repeat change of settings. + ac.setPower(true); + ac.setTemp(24); + ac.setMode(kCoolixCool); + ac.setFan(kCoolixFanAuto); + EXPECT_EQ( + "Power: On, Mode: 0 (COOL), Fan: 5 (AUTO), Temp: 24C, Zone Follow: Off, " + "Sensor Temp: Ignored", + ac.toString()); + EXPECT_EQ(0xB2BF40, ac.getRaw()); +} diff --git a/lib/IRremoteESP8266-2.6.0/test/ir_Daikin_test.cpp b/lib/IRremoteESP8266-2.6.0/test/ir_Daikin_test.cpp new file mode 100644 index 000000000000..67d144d54404 --- /dev/null +++ b/lib/IRremoteESP8266-2.6.0/test/ir_Daikin_test.cpp @@ -0,0 +1,1880 @@ +// Copyright 2017 David Conran +#include "ir_Daikin.h" +#include "IRrecv.h" +#include "IRrecv_test.h" +#include "IRsend.h" +#include "IRsend_test.h" +#include "gtest/gtest.h" + +// Tests for sendDaikin(). + +// Test sending typical data only. +TEST(TestSendDaikin, SendDataOnly) { + IRsendTest irsend(0); + irsend.begin(); + + uint8_t daikin_code[kDaikinStateLength] = { + 0x11, 0xDA, 0x27, 0x00, 0xC5, 0x00, 0x00, 0xD7, + 0x11, 0xDA, 0x27, 0xF0, 0x00, 0x00, 0x00, 0x20, 0x11, + 0xDA, 0x27, 0x00, 0x00, 0x41, 0x1E, 0x00, 0xB0, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xE3}; + + irsend.reset(); + irsend.sendDaikin(daikin_code); + EXPECT_EQ( + "f38000d50" + "m428s428m428s428m428s428m428s428m428s428" + "m428s29428" + "m3650s1623" + "m428s1280m428s428m428s428m428s428m428s1280m428s428m428s428m428s428" + "m428s428m428s1280m428s428m428s1280m428s1280m428s428m428s1280m428s1280" + "m428s1280m428s1280m428s1280m428s428m428s428m428s1280m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s1280m428s428m428s1280m428s428m428s428m428s428m428s1280m428s1280" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s1280m428s1280m428s1280m428s428m428s1280m428s428m428s1280m428s1280" + "m428s29428" + "m3650s1623" + "m428s1280m428s428m428s428m428s428m428s1280m428s428m428s428m428s428" + "m428s428m428s1280m428s428m428s1280m428s1280m428s428m428s1280m428s1280" + "m428s1280m428s1280m428s1280m428s428m428s428m428s1280m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s1280m428s1280m428s1280m428s1280" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s1280m428s428m428s428" + "m428s29428" + "m3650s1623" + "m428s1280m428s428m428s428m428s428m428s1280m428s428m428s428m428s428" + "m428s428m428s1280m428s428m428s1280m428s1280m428s428m428s1280m428s1280" + "m428s1280m428s1280m428s1280m428s428m428s428m428s1280m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s1280m428s428m428s428m428s428m428s428m428s428m428s1280m428s428" + "m428s428m428s1280m428s1280m428s1280m428s1280m428s428m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s1280m428s1280m428s428m428s1280" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s1280m428s1280" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s1280m428s1280m428s428m428s428m428s428m428s1280m428s1280m428s1280" + "m428s29428", + irsend.outputStr()); +} + +// Test sending with repeats. +TEST(TestSendDaikin, SendWithRepeats) { + IRsendTest irsend(0); + irsend.begin(); + + irsend.reset(); + uint8_t daikin_code[kDaikinStateLengthShort] = { + 0x11, 0xDA, 0x27, 0xF0, 0x00, 0x00, 0x00, 0x20, 0x11, + 0xDA, 0x27, 0x00, 0x00, 0x41, 0x1E, 0x00, 0xB0, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xE3}; + irsend.reset(); + + irsend.sendDaikin(daikin_code, kDaikinStateLengthShort, 1); + EXPECT_EQ( + "f38000d50" + "m428s428m428s428m428s428m428s428m428s428" + "m428s29428m3650s1623" + "m428s1280m428s428m428s428m428s428m428s1280m428s428m428s428m428s428" + "m428s428m428s1280m428s428m428s1280m428s1280m428s428m428s1280m428s1280" + "m428s1280m428s1280m428s1280m428s428m428s428m428s1280m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s1280m428s428m428s1280m428s428m428s428m428s428m428s1280m428s1280" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s1280m428s1280m428s1280m428s428m428s1280m428s428m428s1280m428s1280" + "m428s29428m3650s1623" + "m428s1280m428s428m428s428m428s428m428s1280m428s428m428s428m428s428" + "m428s428m428s1280m428s428m428s1280m428s1280m428s428m428s1280m428s1280" + "m428s1280m428s1280m428s1280m428s428m428s428m428s1280m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s1280m428s1280m428s1280m428s1280" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s1280m428s428m428s428" + "m428s29428m3650s1623" + "m428s1280m428s428m428s428m428s428m428s1280m428s428m428s428m428s428" + "m428s428m428s1280m428s428m428s1280m428s1280m428s428m428s1280m428s1280" + "m428s1280m428s1280m428s1280m428s428m428s428m428s1280m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s1280m428s428m428s428m428s428m428s428m428s428m428s1280m428s428" + "m428s428m428s1280m428s1280m428s1280m428s1280m428s428m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s1280m428s1280m428s428m428s1280" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s1280m428s1280" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s1280m428s1280m428s428m428s428m428s428m428s1280m428s1280m428s1280" + "m428s29428" + "m428s428m428s428m428s428m428s428m428s428" + "m428s29428m3650s1623" + "m428s1280m428s428m428s428m428s428m428s1280m428s428m428s428m428s428" + "m428s428m428s1280m428s428m428s1280m428s1280m428s428m428s1280m428s1280" + "m428s1280m428s1280m428s1280m428s428m428s428m428s1280m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s1280m428s428m428s1280m428s428m428s428m428s428m428s1280m428s1280" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s1280m428s1280m428s1280m428s428m428s1280m428s428m428s1280m428s1280" + "m428s29428m3650s1623" + "m428s1280m428s428m428s428m428s428m428s1280m428s428m428s428m428s428" + "m428s428m428s1280m428s428m428s1280m428s1280m428s428m428s1280m428s1280" + "m428s1280m428s1280m428s1280m428s428m428s428m428s1280m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s1280m428s1280m428s1280m428s1280" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s1280m428s428m428s428" + "m428s29428m3650s1623" + "m428s1280m428s428m428s428m428s428m428s1280m428s428m428s428m428s428" + "m428s428m428s1280m428s428m428s1280m428s1280m428s428m428s1280m428s1280" + "m428s1280m428s1280m428s1280m428s428m428s428m428s1280m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s1280m428s428m428s428m428s428m428s428m428s428m428s1280m428s428" + "m428s428m428s1280m428s1280m428s1280m428s1280m428s428m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s1280m428s1280m428s428m428s1280" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s1280m428s1280" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s1280m428s1280m428s428m428s428m428s428m428s1280m428s1280m428s1280" + "m428s29428", + irsend.outputStr()); +} + +// Test sending atypical sizes. +TEST(TestSendDaikin, SendUnexpectedSizes) { + IRsendTest irsend(4); + irsend.begin(); + + uint8_t daikin_short_code[kDaikinStateLengthShort - 1] = { + 0x11, 0xDA, 0x27, 0xF0, 0x00, 0x00, 0x00, 0x20, 0x11, + 0xDA, 0x27, 0x00, 0x00, 0x41, 0x1E, 0x00, 0xB0, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00}; + + irsend.reset(); + irsend.sendDaikin(daikin_short_code, kDaikinStateLengthShort - 1); + ASSERT_EQ("", irsend.outputStr()); + + uint8_t daikin_long_code[kDaikinStateLengthShort + 1] = { + 0x11, 0xDA, 0x27, 0xF0, 0x00, 0x00, 0x00, 0x20, 0x11, 0xDA, + 0x27, 0x00, 0x00, 0x41, 0x1E, 0x00, 0xB0, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xE3, 0x11}; + irsend.reset(); + irsend.sendDaikin(daikin_long_code, kDaikinStateLengthShort + 1); + ASSERT_EQ( + "f38000d50" + "m428s428m428s428m428s428m428s428m428s428" + "m428s29428m3650s1623" + "m428s1280m428s428m428s428m428s428m428s1280m428s428m428s428m428s428" + "m428s428m428s1280m428s428m428s1280m428s1280m428s428m428s1280m428s1280" + "m428s1280m428s1280m428s1280m428s428m428s428m428s1280m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s1280m428s428m428s1280m428s428m428s428m428s428m428s1280m428s1280" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s1280m428s1280m428s1280m428s428m428s1280m428s428m428s1280m428s1280" + "m428s29428m3650s1623" + "m428s1280m428s428m428s428m428s428m428s1280m428s428m428s428m428s428" + "m428s428m428s1280m428s428m428s1280m428s1280m428s428m428s1280m428s1280" + "m428s1280m428s1280m428s1280m428s428m428s428m428s1280m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s1280m428s1280m428s1280m428s1280" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s1280m428s428m428s428" + "m428s29428m3650s1623" + "m428s1280m428s428m428s428m428s428m428s1280m428s428m428s428m428s428" + "m428s428m428s1280m428s428m428s1280m428s1280m428s428m428s1280m428s1280" + "m428s1280m428s1280m428s1280m428s428m428s428m428s1280m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s1280m428s428m428s428m428s428m428s428m428s428m428s1280m428s428" + "m428s428m428s1280m428s1280m428s1280m428s1280m428s428m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s1280m428s1280m428s428m428s1280" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s1280m428s1280" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s1280m428s1280m428s428m428s428m428s428m428s1280m428s1280m428s1280" + "m428s1280m428s428m428s428m428s428m428s1280m428s428m428s428m428s428" + "m428s29428", + irsend.outputStr()); +} + +// Tests for IRDaikinESP class. + +TEST(TestDaikinClass, Power) { + IRDaikinESP ac(0); + ac.begin(); + + ac.on(); + EXPECT_TRUE(ac.getPower()); + + ac.off(); + EXPECT_FALSE(ac.getPower()); + + ac.setPower(true); + EXPECT_TRUE(ac.getPower()); + + ac.setPower(false); + EXPECT_FALSE(ac.getPower()); +} + +TEST(TestDaikinClass, Temperature) { + IRDaikinESP ac(0); + ac.begin(); + + ac.setTemp(0); + EXPECT_EQ(kDaikinMinTemp, ac.getTemp()); + + ac.setTemp(255); + EXPECT_EQ(kDaikinMaxTemp, ac.getTemp()); + + ac.setTemp(kDaikinMinTemp); + EXPECT_EQ(kDaikinMinTemp, ac.getTemp()); + + ac.setTemp(kDaikinMaxTemp); + EXPECT_EQ(kDaikinMaxTemp, ac.getTemp()); + + ac.setTemp(kDaikinMinTemp - 1); + EXPECT_EQ(kDaikinMinTemp, ac.getTemp()); + + ac.setTemp(kDaikinMaxTemp + 1); + EXPECT_EQ(kDaikinMaxTemp, ac.getTemp()); + + ac.setTemp(kDaikinMinTemp + 1); + EXPECT_EQ(kDaikinMinTemp + 1, ac.getTemp()); + + ac.setTemp(21); + EXPECT_EQ(21, ac.getTemp()); + + ac.setTemp(25); + EXPECT_EQ(25, ac.getTemp()); + + ac.setTemp(29); + EXPECT_EQ(29, ac.getTemp()); +} + +TEST(TestDaikinClass, OperatingMode) { + IRDaikinESP ac(0); + ac.begin(); + + ac.setMode(kDaikinAuto); + EXPECT_EQ(kDaikinAuto, ac.getMode()); + + ac.setMode(kDaikinCool); + EXPECT_EQ(kDaikinCool, ac.getMode()); + + ac.setMode(kDaikinHeat); + EXPECT_EQ(kDaikinHeat, ac.getMode()); + + ac.setMode(kDaikinDry); + EXPECT_EQ(kDaikinDry, ac.getMode()); + + ac.setMode(kDaikinFan); + EXPECT_EQ(kDaikinFan, ac.getMode()); + + ac.setMode(kDaikinFan + 1); + EXPECT_EQ(kDaikinAuto, ac.getMode()); + + ac.setMode(kDaikinAuto + 1); + EXPECT_EQ(kDaikinAuto, ac.getMode()); + + ac.setMode(255); + EXPECT_EQ(kDaikinAuto, ac.getMode()); +} + +TEST(TestDaikinClass, VaneSwing) { + IRDaikinESP ac(0); + ac.begin(); + + ac.setSwingHorizontal(true); + ac.setSwingVertical(false); + + ac.setSwingHorizontal(true); + EXPECT_TRUE(ac.getSwingHorizontal()); + EXPECT_FALSE(ac.getSwingVertical()); + + ac.setSwingVertical(true); + EXPECT_TRUE(ac.getSwingHorizontal()); + EXPECT_TRUE(ac.getSwingVertical()); + + ac.setSwingHorizontal(false); + EXPECT_FALSE(ac.getSwingHorizontal()); + EXPECT_TRUE(ac.getSwingVertical()); + + ac.setSwingVertical(false); + EXPECT_FALSE(ac.getSwingHorizontal()); + EXPECT_FALSE(ac.getSwingVertical()); +} + +TEST(TestDaikinClass, QuietMode) { + IRDaikinESP ac(0); + ac.begin(); + + ac.setQuiet(true); + EXPECT_TRUE(ac.getQuiet()); + + ac.setQuiet(false); + EXPECT_FALSE(ac.getQuiet()); + + ac.setQuiet(true); + EXPECT_TRUE(ac.getQuiet()); + + // Setting Econo mode should NOT change out of quiet mode. + ac.setEcono(true); + EXPECT_TRUE(ac.getQuiet()); + ac.setEcono(false); + EXPECT_TRUE(ac.getQuiet()); + + // But setting Powerful mode should exit out of quiet mode. + ac.setPowerful(true); + EXPECT_FALSE(ac.getQuiet()); +} + +TEST(TestDaikinClass, PowerfulMode) { + IRDaikinESP ac(0); + ac.begin(); + + ac.setPowerful(true); + EXPECT_TRUE(ac.getPowerful()); + + ac.setPowerful(false); + EXPECT_FALSE(ac.getPowerful()); + + ac.setPowerful(true); + EXPECT_TRUE(ac.getPowerful()); + + ac.setQuiet(true); + EXPECT_FALSE(ac.getPowerful()); + + ac.setPowerful(true); + ac.setEcono(true); + EXPECT_FALSE(ac.getPowerful()); +} + +TEST(TestDaikinClass, EconoMode) { + IRDaikinESP ac(0); + ac.begin(); + + ac.setEcono(true); + EXPECT_TRUE(ac.getEcono()); + + ac.setEcono(false); + EXPECT_FALSE(ac.getEcono()); + + ac.setEcono(true); + EXPECT_TRUE(ac.getEcono()); + + // Setting Quiet mode should NOT change out of Econo mode. + ac.setQuiet(true); + EXPECT_TRUE(ac.getEcono()); + ac.setQuiet(false); + EXPECT_TRUE(ac.getEcono()); + + // But setting Powerful mode should exit out of Econo mode. + ac.setPowerful(true); + EXPECT_FALSE(ac.getEcono()); +} + +TEST(TestDaikinClass, FanSpeed) { + IRDaikinESP ac(0); + ac.begin(); + + // Unexpected value should default to Auto. + ac.setFan(0); + EXPECT_EQ(kDaikinFanAuto, ac.getFan()); + + // Unexpected value should default to Auto. + ac.setFan(255); + EXPECT_EQ(kDaikinFanAuto, ac.getFan()); + + ac.setFan(kDaikinFanMax); + EXPECT_EQ(kDaikinFanMax, ac.getFan()); + + // Beyond Max should default to Auto. + ac.setFan(kDaikinFanMax + 1); + EXPECT_EQ(kDaikinFanAuto, ac.getFan()); + + ac.setFan(kDaikinFanMax - 1); + EXPECT_EQ(kDaikinFanMax - 1, ac.getFan()); + + ac.setFan(kDaikinFanMin); + EXPECT_EQ(kDaikinFanMin, ac.getFan()); + + ac.setFan(kDaikinFanMin + 1); + EXPECT_EQ(kDaikinFanMin + 1, ac.getFan()); + + // Beyond Min should default to Auto. + ac.setFan(kDaikinFanMin - 1); + EXPECT_EQ(kDaikinFanAuto, ac.getFan()); + + ac.setFan(3); + EXPECT_EQ(3, ac.getFan()); + + ac.setFan(kDaikinFanAuto); + EXPECT_EQ(kDaikinFanAuto, ac.getFan()); + + ac.setFan(kDaikinFanQuiet); + EXPECT_EQ(kDaikinFanQuiet, ac.getFan()); +} + +TEST(TestDaikinClass, CurrentTime) { + IRDaikinESP ac(0); + ac.begin(); + + ac.setCurrentTime(0); // 00:00 + EXPECT_EQ(0, ac.getCurrentTime()); + + ac.setCurrentTime(754); // 12:34 + EXPECT_EQ(754, ac.getCurrentTime()); + + ac.setCurrentTime(1439); // 23:59 + EXPECT_EQ(1439, ac.getCurrentTime()); +} + +TEST(TestDaikinClass, OnOffTimers) { + IRDaikinESP ac(0); + ac.begin(); + + // Both timers turned off. + ac.disableOnTimer(); + ac.disableOffTimer(); + EXPECT_FALSE(ac.getOnTimerEnabled()); + EXPECT_EQ(kDaikinUnusedTime, ac.getOnTime()); + EXPECT_FALSE(ac.getOffTimerEnabled()); + EXPECT_EQ(kDaikinUnusedTime, ac.getOffTime()); + + // Turn on just the On Timer. + ac.enableOnTimer(123); + EXPECT_TRUE(ac.getOnTimerEnabled()); + EXPECT_EQ(123, ac.getOnTime()); + EXPECT_FALSE(ac.getOffTimerEnabled()); + EXPECT_EQ(kDaikinUnusedTime, ac.getOffTime()); + + // Now turn on the Off Timer. + ac.enableOffTimer(754); + EXPECT_TRUE(ac.getOffTimerEnabled()); + EXPECT_EQ(754, ac.getOffTime()); + EXPECT_TRUE(ac.getOnTimerEnabled()); + EXPECT_EQ(123, ac.getOnTime()); + + // Turn off the just the On Timer. + ac.disableOnTimer(); + EXPECT_FALSE(ac.getOnTimerEnabled()); + EXPECT_EQ(kDaikinUnusedTime, ac.getOnTime()); + EXPECT_TRUE(ac.getOffTimerEnabled()); + EXPECT_EQ(754, ac.getOffTime()); + + // Now turn off the Off Timer. + ac.disableOffTimer(); + EXPECT_FALSE(ac.getOffTimerEnabled()); + EXPECT_EQ(kDaikinUnusedTime, ac.getOffTime()); + EXPECT_FALSE(ac.getOnTimerEnabled()); + EXPECT_EQ(kDaikinUnusedTime, ac.getOnTime()); + + // Use some canary values around the timers to ensure no accidental + // bit flips happen. i.e. Neighbouring bytes in the state. + // (Found some during testing on systems with different endian-ness) + // Tests here to make sure it never happens again. + ac.setSwingHorizontal(true); + ac.setPowerful(true); + ac.disableOffTimer(); + ac.disableOnTimer(); + ASSERT_TRUE(ac.getSwingHorizontal()); + ASSERT_TRUE(ac.getPowerful()); + ac.enableOnTimer(123); + ac.enableOffTimer(456); + ASSERT_TRUE(ac.getSwingHorizontal()); + ASSERT_TRUE(ac.getPowerful()); + ac.disableOffTimer(); + ac.disableOnTimer(); + ASSERT_TRUE(ac.getSwingHorizontal()); + ASSERT_TRUE(ac.getPowerful()); + + ac.setSwingHorizontal(false); + ac.setPowerful(false); + ac.disableOffTimer(); + ac.disableOnTimer(); + ASSERT_FALSE(ac.getSwingHorizontal()); + ASSERT_FALSE(ac.getPowerful()); + ac.enableOnTimer(123); + ac.enableOffTimer(456); + ASSERT_FALSE(ac.getSwingHorizontal()); + ASSERT_FALSE(ac.getPowerful()); + ac.disableOffTimer(); + ac.disableOnTimer(); + ASSERT_FALSE(ac.getSwingHorizontal()); + ASSERT_FALSE(ac.getPowerful()); +} + +// Test Eye mode. +TEST(TestDaikinClass, EyeSetting) { + IRDaikinESP ac(0); + ac.begin(); + + // The Eye setting is stored in the same byte as Econo mode. + // Econo mode tests are there to make sure it isn't harmed and vice-versa. + ac.setEcono(false); + ac.setEye(false); + ASSERT_FALSE(ac.getEye()); + EXPECT_FALSE(ac.getEcono()); + + ac.setEye(true); + ASSERT_TRUE(ac.getEye()); + EXPECT_FALSE(ac.getEcono()); + + ac.setEcono(false); + ASSERT_TRUE(ac.getEye()); + EXPECT_FALSE(ac.getEcono()); + + ac.setEcono(true); + ASSERT_TRUE(ac.getEye()); + EXPECT_TRUE(ac.getEcono()); + + ac.setEye(false); + ASSERT_FALSE(ac.getEye()); + EXPECT_TRUE(ac.getEcono()); +} + +// Test Mold mode. +TEST(TestDaikinClass, MoldSetting) { + IRDaikinESP ac(0); + ac.begin(); + + ac.setMold(false); + ASSERT_FALSE(ac.getMold()); + + ac.setMold(true); + ASSERT_TRUE(ac.getMold()); + + ac.setMold(false); + ASSERT_FALSE(ac.getMold()); +} + +// Test Comfort mode. +TEST(TestDaikinClass, ComfortSetting) { + IRDaikinESP ac(0); + ac.begin(); + + ac.setComfort(false); + ASSERT_FALSE(ac.getComfort()); + + ac.setComfort(true); + ASSERT_TRUE(ac.getComfort()); + + ac.setComfort(false); + ASSERT_FALSE(ac.getComfort()); +} + +// Test Sensor mode. +TEST(TestDaikinClass, SensorSetting) { + IRDaikinESP ac(0); + ac.begin(); + + ac.setSensor(false); + ASSERT_FALSE(ac.getSensor()); + + ac.setSensor(true); + ASSERT_TRUE(ac.getSensor()); + + ac.setSensor(false); + ASSERT_FALSE(ac.getSensor()); +} + +TEST(TestDaikinClass, RenderTime) { + EXPECT_EQ("0:00", IRDaikinESP::renderTime(0)); + EXPECT_EQ("0:10", IRDaikinESP::renderTime(10)); + EXPECT_EQ("1:00", IRDaikinESP::renderTime(1 * 60 + 0)); + EXPECT_EQ("23:59", IRDaikinESP::renderTime(23 * 60 + 59)); +} + +TEST(TestDaikinClass, SetAndGetRaw) { + IRDaikinESP ac(0); + uint8_t shortState[kDaikinStateLengthShort] = { + 0x11, 0xDA, 0x27, 0x00, 0x42, 0x00, 0x00, 0x54, 0x11, + 0xDA, 0x27, 0x00, 0x00, 0x49, 0x1E, 0x00, 0xB0, 0x00, + 0x00, 0x06, 0x60, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x4F}; + uint8_t longState[kDaikinStateLength] = { + 0x11, 0xDA, 0x27, 0x00, 0xC5, 0x00, 0x00, 0xD7, + 0x11, 0xDA, 0x27, 0x00, 0x42, 0x00, 0x00, 0x54, 0x11, + 0xDA, 0x27, 0x00, 0x00, 0x49, 0x1E, 0x00, 0xB0, 0x00, + 0x00, 0x06, 0x60, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x4F}; + uint8_t expectedState[kDaikinStateLength] = { + 0x11, 0xDA, 0x27, 0x00, 0xC5, 0x00, 0x00, 0xD7, + 0x11, 0xDA, 0x27, 0x00, 0x42, 0x00, 0x00, 0x54, 0x11, + 0xDA, 0x27, 0x00, 0x00, 0x48, 0x2A, 0x00, 0xB0, 0x00, + 0x00, 0x06, 0x60, 0x00, 0x00, 0xC0, 0x00, 0x02, 0x5C}; + + EXPECT_STATE_EQ(longState, ac.getRaw(), kDaikinBits); + // toggle the power state. + ac.setPower(!ac.getPower()); + ac.setTemp(21); + ac.setMold(true); + EXPECT_STATE_EQ(expectedState, ac.getRaw(), kDaikinBits); + ac.setRaw(longState); + EXPECT_STATE_EQ(longState, ac.getRaw(), kDaikinBits); + ac.setRaw(shortState, kDaikinStateLengthShort); + EXPECT_STATE_EQ(longState, ac.getRaw(), kDaikinBits); +} + +TEST(TestDaikinClass, ChecksumValidation) { + uint8_t daikin_code[kDaikinStateLength] = { + 0x11, 0xDA, 0x27, 0x00, 0xC5, 0x00, 0x00, 0xD7, + 0x11, 0xDA, 0x27, 0xF0, 0x00, 0x00, 0x00, 0x02, 0x11, + 0xDA, 0x27, 0x00, 0x00, 0x41, 0x1E, 0x00, 0xB0, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xE1}; + + EXPECT_TRUE(IRDaikinESP::validChecksum(daikin_code)); + // Change the array so the checksum is invalid. + daikin_code[0] ^= 0xFF; + EXPECT_FALSE(IRDaikinESP::validChecksum(daikin_code)); + // Restore the previous change, and change another byte. + daikin_code[0] ^= 0xFF; + daikin_code[4] ^= 0xFF; + EXPECT_FALSE(IRDaikinESP::validChecksum(daikin_code)); + daikin_code[4] ^= 0xFF; + // Change something in the 2nd block. + daikin_code[10] ^= 0xFF; + EXPECT_FALSE(IRDaikinESP::validChecksum(daikin_code)); + daikin_code[10] ^= 0xFF; + EXPECT_TRUE(IRDaikinESP::validChecksum(daikin_code)); + // Change something in the 3rd block. + daikin_code[20] ^= 0xFF; + EXPECT_FALSE(IRDaikinESP::validChecksum(daikin_code)); + daikin_code[20] ^= 0xFF; + EXPECT_TRUE(IRDaikinESP::validChecksum(daikin_code)); +} + +// Test human readable output. +TEST(TestDaikinClass, HumanReadable) { + IRDaikinESP ac(0); + + EXPECT_EQ( + "Power: On, Mode: 4 (HEAT), Temp: 15C, Fan: 11 (QUIET), " + "Powerful: Off, Quiet: Off, Sensor: Off, Eye: Off, Mold: Off, " + "Comfort: Off, Swing (Horizontal): Off, Swing (Vertical): Off, " + "Current Time: 0:00, On Time: Off, Off Time: Off", + ac.toString()); + ac.setMode(kDaikinAuto); + ac.setTemp(25); + ac.setFan(kDaikinFanAuto); + ac.setQuiet(true); + ac.setSensor(true); + ac.setEye(true); + ac.setMold(true); + ac.setSwingVertical(true); + ac.setSwingHorizontal(true); + ac.setCurrentTime(9 * 60 + 15); + ac.enableOnTimer(8 * 60 + 0); + ac.enableOffTimer(17 * 60 + 30); + ac.setComfort(true); + ac.off(); + EXPECT_EQ( + "Power: Off, Mode: 0 (AUTO), Temp: 25C, Fan: 10 (AUTO), " + "Powerful: Off, Quiet: On, Sensor: On, Eye: On, Mold: On, Comfort: On, " + "Swing (Horizontal): On, Swing (Vertical): On, " + "Current Time: 9:15, On Time: 8:00, Off Time: 17:30", + ac.toString()); +} + +// Test general message construction after tweaking some settings. +TEST(TestDaikinClass, MessageConstuction) { + IRDaikinESP ac(0); + IRsendTest irsend(4); + ac.begin(); + irsend.begin(); + + ac.setFan(kDaikinFanMin); + ac.setMode(kDaikinCool); + ac.setTemp(27); + ac.setSwingVertical(false); + ac.setSwingHorizontal(true); + ac.setQuiet(false); + ac.setPower(true); + + // Check everything for kicks. + EXPECT_EQ(kDaikinFanMin, ac.getFan()); + EXPECT_EQ(kDaikinCool, ac.getMode()); + EXPECT_EQ(27, ac.getTemp()); + EXPECT_FALSE(ac.getSwingVertical()); + EXPECT_TRUE(ac.getSwingHorizontal()); + EXPECT_FALSE(ac.getQuiet()); + EXPECT_TRUE(ac.getPower()); + + irsend.reset(); + irsend.sendDaikin(ac.getRaw()); + EXPECT_EQ( + "f38000d50" + "m428s428m428s428m428s428m428s428m428s428" + "m428s29428m3650s1623" + "m428s1280m428s428m428s428m428s428m428s1280m428s428m428s428m428s428" + "m428s428m428s1280m428s428m428s1280m428s1280m428s428m428s1280m428s1280" + "m428s1280m428s1280m428s1280m428s428m428s428m428s1280m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s1280m428s428m428s1280m428s428m428s428m428s428m428s1280m428s1280" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s1280m428s1280m428s1280m428s428m428s1280m428s428m428s1280m428s1280" + "m428s29428m3650s1623" + "m428s1280m428s428m428s428m428s428m428s1280m428s428m428s428m428s428" + "m428s428m428s1280m428s428m428s1280m428s1280m428s428m428s1280m428s1280" + "m428s1280m428s1280m428s1280m428s428m428s428m428s1280m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s428m428s1280m428s428m428s428m428s428m428s428m428s1280m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s428m428s428m428s1280m428s428m428s1280m428s428m428s1280m428s428" + "m428s29428m3650s1623" + "m428s1280m428s428m428s428m428s428m428s1280m428s428m428s428m428s428" + "m428s428m428s1280m428s428m428s1280m428s1280m428s428m428s1280m428s1280" + "m428s1280m428s1280m428s1280m428s428m428s428m428s1280m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s1280m428s428m428s428m428s1280m428s1280m428s1280m428s428m428s428" + "m428s428m428s1280m428s1280m428s428m428s1280m428s1280m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s1280m428s1280m428s428m428s428" + "m428s1280m428s1280m428s1280m428s1280m428s428m428s428m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s428m428s1280m428s1280m428s428m428s428m428s428m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s1280m428s1280m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s1280m428s1280" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s428m428s1280m428s1280m428s428m428s428m428s1280m428s1280m428s1280" + "m428s29428", + irsend.outputStr()); +} + +// Tests for decodeDaikin(). + +// Test decoding a message captured from a real IR remote. +TEST(TestDecodeDaikin, RealExample) { + IRDaikinESP ac(0); + IRsendTest irsend(0); + IRrecv irrecv(0); + irsend.begin(); + + uint8_t expectedState[kDaikinStateLength] = { + 0x11, 0xDA, 0x27, 0x00, 0xC5, 0x00, 0x00, 0xD7, + 0x11, 0xDA, 0x27, 0x00, 0x42, 0x3A, 0x05, 0x93, 0x11, + 0xDA, 0x27, 0x00, 0x00, 0x3F, 0x3A, 0x00, 0xA0, 0x00, + 0x0A, 0x25, 0x17, 0x01, 0x00, 0xC0, 0x00, 0x00, 0x32}; + uint16_t rawData[583] = { + 416, 446, 416, 446, 416, 446, 418, 446, 416, 446, 416, 25434, + 3436, 1768, 390, 1336, 390, 446, 416, 446, 416, 446, 416, 1336, + 390, 446, 416, 446, 416, 446, 416, 446, 416, 1336, 390, 448, + 416, 1336, 390, 1336, 390, 448, 416, 1336, 390, 1336, 390, 1338, + 388, 1338, 390, 1336, 390, 446, 416, 446, 416, 1336, 390, 446, + 416, 446, 416, 446, 416, 446, 416, 446, 416, 446, 416, 448, + 416, 446, 416, 446, 416, 446, 416, 1336, 390, 446, 416, 1336, + 390, 448, 416, 446, 416, 446, 416, 1336, 390, 1336, 390, 446, + 416, 446, 416, 446, 416, 446, 416, 446, 416, 446, 416, 446, + 416, 446, 416, 446, 416, 448, 416, 446, 416, 446, 416, 446, + 416, 448, 414, 448, 416, 448, 416, 1336, 390, 1336, 390, 1336, + 390, 446, 414, 1336, 390, 448, 414, 1336, 390, 1336, 390, 34878, + 3436, 1768, 390, 1336, 390, 446, 416, 448, 416, 446, 416, 1336, + 390, 446, 416, 448, 416, 446, 416, 446, 416, 1336, 390, 446, + 416, 1336, 390, 1336, 390, 446, 416, 1336, 390, 1336, 390, 1336, + 390, 1336, 390, 1336, 392, 446, 414, 448, 416, 1336, 390, 446, + 416, 446, 416, 446, 416, 446, 414, 448, 416, 446, 416, 448, + 414, 448, 416, 446, 416, 446, 416, 446, 414, 1336, 390, 448, + 416, 446, 416, 446, 416, 448, 416, 1336, 390, 446, 416, 446, + 416, 1336, 390, 446, 416, 1336, 390, 1336, 390, 1336, 390, 446, + 416, 446, 414, 1338, 390, 446, 416, 1336, 390, 446, 416, 446, + 416, 446, 416, 446, 416, 446, 416, 1336, 390, 1336, 390, 446, + 416, 446, 416, 1336, 390, 446, 416, 446, 416, 1336, 390, 34876, + 3436, 1768, 388, 1336, 390, 446, 416, 446, 416, 448, 416, 1336, + 390, 446, 416, 446, 416, 446, 416, 448, 416, 1336, 390, 448, + 414, 1336, 390, 1336, 390, 446, 416, 1336, 388, 1338, 388, 1336, + 390, 1336, 390, 1336, 390, 446, 416, 446, 416, 1336, 390, 446, + 420, 442, 416, 446, 416, 446, 416, 446, 416, 446, 416, 446, + 416, 446, 416, 446, 416, 446, 416, 446, 416, 446, 416, 448, + 416, 446, 416, 448, 416, 446, 416, 448, 416, 446, 416, 1336, + 390, 1336, 390, 1336, 388, 1338, 390, 1336, 390, 1336, 392, 446, + 416, 446, 416, 448, 416, 1334, 390, 446, 416, 1338, 388, 1336, + 390, 1336, 390, 446, 416, 446, 416, 448, 414, 446, 416, 446, + 416, 446, 416, 448, 416, 446, 416, 446, 416, 446, 416, 446, + 416, 446, 416, 446, 416, 446, 416, 446, 416, 1336, 390, 446, + 416, 1336, 390, 446, 414, 448, 416, 446, 416, 446, 416, 446, + 416, 448, 416, 446, 416, 446, 416, 446, 416, 1336, 390, 446, + 416, 1336, 390, 446, 416, 446, 416, 446, 416, 448, 416, 1338, + 390, 444, 418, 1336, 390, 448, 416, 446, 416, 1336, 390, 446, + 416, 446, 416, 1336, 390, 1336, 388, 1336, 390, 446, 416, 1336, + 390, 448, 414, 448, 414, 448, 416, 1334, 390, 446, 416, 446, + 416, 446, 416, 448, 416, 446, 416, 446, 416, 448, 416, 446, + 416, 446, 416, 446, 416, 446, 416, 446, 416, 446, 416, 446, + 416, 446, 416, 446, 416, 446, 416, 446, 416, 446, 416, 446, + 416, 448, 416, 1336, 390, 1336, 390, 446, 416, 446, 416, 446, + 416, 446, 414, 446, 416, 448, 416, 446, 416, 448, 414, 446, + 418, 446, 416, 446, 416, 448, 416, 446, 416, 448, 416, 446, + 416, 448, 416, 446, 416, 1336, 390, 446, 416, 446, 416, 1338, + 390, 1336, 390, 446, 416, 446, 416}; // Captured by @sillyfrog + + irsend.reset(); + irsend.sendRaw(rawData, 583, 38000); + irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(DAIKIN, irsend.capture.decode_type); + ASSERT_EQ(kDaikinBits, irsend.capture.bits); + EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits); + ac.setRaw(irsend.capture.state); + EXPECT_EQ( + "Power: On, Mode: 3 (COOL), Temp: 29C, Fan: 10 (AUTO), Powerful: On, " + "Quiet: Off, Sensor: Off, Eye: Off, Mold: Off, Comfort: Off, " + "Swing (Horizontal): Off, Swing (Vertical): Off, " + "Current Time: 22:18, On Time: 21:30, Off Time: 6:10", ac.toString()); +} + +// Decoding a message we entirely constructed based solely on a given state. +TEST(TestDecodeDaikin, ShortSyntheticExample) { + IRDaikinESP ac(0); + IRsendTest irsend(0); + IRrecv irrecv(0); + irsend.begin(); + + uint8_t shortState[kDaikinStateLengthShort] = { + 0x11, 0xDA, 0x27, 0x00, 0x42, 0x3A, 0x05, 0x93, 0x11, + 0xDA, 0x27, 0x00, 0x00, 0x3F, 0x3A, 0x00, 0xA0, 0x00, + 0x0A, 0x25, 0x17, 0x01, 0x00, 0xC0, 0x00, 0x00, 0x32}; + + uint8_t longState[kDaikinStateLength] = { + 0x11, 0xDA, 0x27, 0x00, 0xC5, 0x00, 0x00, 0xD7, + 0x11, 0xDA, 0x27, 0x00, 0x42, 0x3A, 0x05, 0x93, 0x11, + 0xDA, 0x27, 0x00, 0x00, 0x3F, 0x3A, 0x00, 0xA0, 0x00, + 0x0A, 0x25, 0x17, 0x01, 0x00, 0xC0, 0x00, 0x00, 0x32}; + irsend.reset(); + irsend.sendDaikin(shortState, kDaikinStateLengthShort); + irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(DAIKIN, irsend.capture.decode_type); + ASSERT_EQ(kDaikinBits, irsend.capture.bits); + EXPECT_STATE_EQ(longState, irsend.capture.state, irsend.capture.bits); + ac.setRaw(irsend.capture.state); + EXPECT_EQ( + "Power: On, Mode: 3 (COOL), Temp: 29C, Fan: 10 (AUTO), Powerful: On, " + "Quiet: Off, Sensor: Off, Eye: Off, Mold: Off, Comfort: Off, " + "Swing (Horizontal): Off, Swing (Vertical): Off, " + "Current Time: 22:18, On Time: 21:30, Off Time: 6:10", ac.toString()); +} + +// Decoding a message we entirely constructed based solely on a given state. +TEST(TestDecodeDaikin, LongSyntheticExample) { + IRDaikinESP ac(0); + IRsendTest irsend(0); + IRrecv irrecv(0); + irsend.begin(); + + uint8_t expectedState[kDaikinStateLength] = { + 0x11, 0xDA, 0x27, 0x00, 0xC5, 0x00, 0x00, 0xD7, + 0x11, 0xDA, 0x27, 0x00, 0x42, 0x3A, 0x05, 0x93, + 0x11, 0xDA, 0x27, 0x00, 0x00, 0x3F, 0x3A, 0x00, 0xA0, 0x00, + 0x0A, 0x25, 0x17, 0x01, 0x00, 0xC0, 0x00, 0x00, 0x32}; + + irsend.reset(); + irsend.sendDaikin(expectedState); + irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decodeDaikin(&irsend.capture)); + ASSERT_EQ(DAIKIN, irsend.capture.decode_type); + ASSERT_EQ(kDaikinBits, irsend.capture.bits); + EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits); + ac.setRaw(irsend.capture.state); + EXPECT_EQ( + "Power: On, Mode: 3 (COOL), Temp: 29C, Fan: 10 (AUTO), Powerful: On, " + "Quiet: Off, Sensor: Off, Eye: Off, Mold: Off, Comfort: Off, " + "Swing (Horizontal): Off, Swing (Vertical): Off, " + "Current Time: 22:18, On Time: 21:30, Off Time: 6:10", ac.toString()); +} + +// Test decoding a message captured from a real IR remote. +TEST(TestDecodeDaikin2, RealExample) { + IRsendTest irsend(0); + IRrecv irrecv(0); + irsend.begin(); + + uint8_t expectedState[kDaikin2StateLength] = { + 0x11, 0xDA, 0x27, 0x00, 0x01, 0x7A, 0xC3, 0x70, 0x28, 0x0C, + 0x80, 0x04, 0xB0, 0x16, 0x24, 0x00, 0x00, 0xBE, 0xD5, 0xF5, + 0x11, 0xDA, 0x27, 0x00, 0x00, 0x08, 0x26, 0x00, 0xA0, 0x00, + 0x00, 0x06, 0x60, 0x00, 0x00, 0xC1, 0x80, 0x60, 0xE7}; + // "Off" Data from https://github.com/markszabo/IRremoteESP8266/issues/582 + uint16_t rawData[633] = { // Data supplied by @sheppy99 + 10024, 25180, 3494, 1732, 436, 1300, 436, 436, 432, 438, 430, 438, + 426, 1306, 430, 442, 430, 438, 428, 440, 430, 440, 430, 1304, + 432, 442, 428, 1308, 424, 1312, 428, 442, 428, 1306, 424, 1314, + 426, 1308, 434, 1306, 426, 1308, 428, 444, 426, 442, 428, 1310, + 428, 442, 424, 444, 426, 442, 426, 444, 424, 444, 426, 444, + 424, 446, 422, 446, 422, 446, 422, 446, 418, 1318, 418, 450, + 420, 448, 420, 448, 422, 448, 420, 450, 420, 448, 420, 450, + 420, 452, 418, 1318, 420, 450, 420, 1318, 420, 1314, 418, 1318, + 424, 1314, 424, 448, 422, 1316, 424, 1312, 426, 446, 422, 448, + 420, 448, 422, 448, 422, 1314, 418, 1320, 416, 452, 420, 448, + 420, 448, 422, 448, 422, 1314, 416, 1320, 422, 1316, 422, 450, + 418, 450, 420, 448, 420, 448, 416, 1320, 418, 452, 418, 1316, + 422, 448, 420, 450, 420, 450, 420, 448, 422, 1314, 418, 1320, + 418, 450, 420, 448, 420, 448, 420, 450, 420, 450, 418, 450, + 418, 450, 420, 450, 418, 452, 416, 452, 420, 450, 418, 1318, + 420, 452, 418, 452, 418, 1322, 416, 452, 416, 452, 418, 452, + 418, 452, 416, 454, 418, 452, 416, 456, 414, 452, 418, 454, + 416, 1320, 410, 1324, 418, 452, 418, 1320, 416, 452, 418, 1320, + 418, 1318, 420, 448, 420, 1316, 420, 450, 420, 450, 418, 450, + 420, 450, 418, 452, 418, 1320, 418, 450, 418, 450, 416, 1322, + 412, 458, 420, 450, 416, 452, 418, 452, 416, 452, 418, 452, + 416, 454, 416, 452, 418, 452, 416, 454, 414, 454, 416, 454, + 416, 454, 414, 456, 414, 454, 414, 456, 412, 454, 416, 456, + 414, 456, 412, 1326, 412, 1320, 412, 1322, 414, 1322, 418, 1320, + 420, 452, 418, 1318, 420, 1316, 422, 450, 420, 1314, 424, 448, + 422, 1314, 422, 448, 422, 1314, 418, 1318, 424, 1316, 422, 448, + 422, 1312, 424, 446, 422, 1314, 420, 1318, 422, 1316, 426, 1310, + 426, 35166, 3500, 1724, 446, 1296, 444, 432, 436, 432, 438, 432, + 436, 1296, 440, 434, 434, 436, 432, 436, 434, 436, 434, 1298, + 438, 438, 432, 1304, 428, 1304, 432, 442, 430, 1302, 430, 1308, + 430, 1306, 434, 1302, 432, 1306, 430, 440, 430, 438, 430, 1308, + 434, 438, 430, 440, 428, 440, 430, 440, 428, 442, 426, 444, + 428, 442, 426, 444, 426, 442, 426, 444, 424, 446, 422, 446, + 424, 446, 424, 446, 422, 446, 424, 448, 420, 448, 422, 446, + 422, 448, 422, 450, 420, 450, 414, 1320, 420, 450, 418, 450, + 418, 448, 420, 450, 418, 452, 418, 1320, 418, 1316, 422, 450, + 418, 452, 418, 1320, 420, 448, 418, 450, 420, 450, 418, 452, + 416, 452, 418, 450, 418, 452, 416, 452, 418, 452, 416, 454, + 416, 452, 416, 454, 416, 454, 414, 456, 416, 454, 414, 1322, + 416, 454, 416, 1320, 418, 452, 416, 454, 414, 454, 416, 454, + 414, 454, 414, 454, 414, 456, 414, 456, 412, 456, 414, 456, + 414, 456, 412, 456, 414, 458, 406, 464, 410, 458, 412, 458, + 410, 460, 410, 1326, 412, 1324, 414, 456, 412, 458, 412, 456, + 414, 456, 412, 458, 410, 458, 414, 458, 410, 458, 408, 460, + 410, 470, 400, 1324, 408, 1328, 410, 458, 410, 460, 414, 456, + 410, 456, 414, 458, 412, 460, 410, 458, 412, 458, 412, 460, + 408, 460, 410, 460, 408, 472, 396, 462, 408, 470, 402, 470, + 396, 472, 400, 470, 398, 1326, 412, 460, 408, 472, 396, 472, + 400, 470, 400, 472, 396, 1328, 410, 1324, 414, 458, 410, 458, + 410, 458, 412, 458, 412, 460, 408, 460, 410, 460, 410, 1324, + 414, 458, 410, 460, 408, 460, 410, 458, 410, 460, 410, 1326, + 412, 1322, 416, 456, 412, 1322, 412, 1326, 416, 1322, 418, 452, + 416, 454, 412, 1324, 418, 1320, 420, 1316, 420}; + irsend.reset(); + irsend.sendRaw(rawData, 633, 38000); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(DAIKIN2, irsend.capture.decode_type); + ASSERT_EQ(kDaikin2Bits, irsend.capture.bits); + EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits); +} + +// Decoding a message we entirely constructed based solely on a given state. +TEST(TestDecodeDaikin2, SyntheticExample) { + IRDaikin2 ac(0); + IRsendTest irsend(0); + IRrecv irrecv(0); + irsend.begin(); + + uint8_t expectedState[kDaikin2StateLength] = { + 0x11, 0xDA, 0x27, 0x00, 0x01, 0x7A, 0xC3, 0x70, 0x28, 0x0C, + 0x80, 0x04, 0xB0, 0x16, 0x24, 0x00, 0x00, 0xBE, 0xD5, 0xF5, + 0x11, 0xDA, 0x27, 0x00, 0x00, 0x08, 0x26, 0x00, 0xA0, 0x00, + 0x00, 0x06, 0x60, 0x00, 0x00, 0xC1, 0x80, 0x60, 0xE7}; + + irsend.reset(); + irsend.sendDaikin2(expectedState); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(DAIKIN2, irsend.capture.decode_type); + ASSERT_EQ(kDaikin2Bits, irsend.capture.bits); + EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits); + ac.setRaw(irsend.capture.state); + EXPECT_EQ( + "Power: Off, Mode: 0 (AUTO), Temp: 19C, Fan: 10 (Auto), " + "Swing (V): 5, Swing (H): 190 (Auto), " + "Clock: 14:50, On Time: Off, Off Time: Off, Sleep Time: Off, " + "Beep: 1 (Quiet), Light: 3 (Off), Mold: On, Clean: On, Fresh Air: Off, " + "Eye: Off, Eye Auto: Off, Quiet: Off, Powerful: Off, Purify: Off, " + "Econo: Off", + ac.toString()); +} + +TEST(TestDaikin2Class, CurrentTime) { + IRDaikin2 ac(0); + ac.begin(); + + ac.setCurrentTime(0); // 00:00 + EXPECT_EQ(0, ac.getCurrentTime()); + + ac.setCurrentTime(754); // 12:34 + EXPECT_EQ(754, ac.getCurrentTime()); + + ac.setCurrentTime(1439); // 23:59 + EXPECT_EQ(1439, ac.getCurrentTime()); +} + +TEST(TestDaikin2Class, OnOffTimers) { + IRDaikin2 ac(0); + ac.begin(); + + // Both timers turned off. + ac.disableOnTimer(); + ac.disableOffTimer(); + EXPECT_FALSE(ac.getOnTimerEnabled()); + EXPECT_EQ(kDaikinUnusedTime, ac.getOnTime()); + EXPECT_FALSE(ac.getOffTimerEnabled()); + EXPECT_EQ(kDaikinUnusedTime, ac.getOffTime()); + + // Turn on just the On Timer. + ac.enableOnTimer(123); + EXPECT_TRUE(ac.getOnTimerEnabled()); + EXPECT_EQ(123, ac.getOnTime()); + EXPECT_FALSE(ac.getOffTimerEnabled()); + EXPECT_EQ(kDaikinUnusedTime, ac.getOffTime()); + + // Now turn on the Off Timer. + ac.enableOffTimer(754); + EXPECT_TRUE(ac.getOffTimerEnabled()); + EXPECT_EQ(754, ac.getOffTime()); + EXPECT_TRUE(ac.getOnTimerEnabled()); + EXPECT_EQ(123, ac.getOnTime()); + + // Turn off the just the On Timer. + ac.disableOnTimer(); + EXPECT_FALSE(ac.getOnTimerEnabled()); + EXPECT_EQ(kDaikinUnusedTime, ac.getOnTime()); + EXPECT_TRUE(ac.getOffTimerEnabled()); + EXPECT_EQ(754, ac.getOffTime()); + + // Now turn off the Off Timer. + ac.disableOffTimer(); + EXPECT_FALSE(ac.getOffTimerEnabled()); + EXPECT_EQ(kDaikinUnusedTime, ac.getOffTime()); + EXPECT_FALSE(ac.getOnTimerEnabled()); + EXPECT_EQ(kDaikinUnusedTime, ac.getOnTime()); +} + +TEST(TestDaikin2Class, LightAndBeep) { + IRDaikin2 ac(0); + ac.begin(); + + ac.setLight(kDaikinLightOff); + EXPECT_EQ(kDaikinLightOff, ac.getLight()); + ac.setBeep(kDaikinBeepOff); + EXPECT_EQ(kDaikinBeepOff, ac.getBeep()); + ac.setLight(kDaikinLightBright); + EXPECT_EQ(kDaikinLightBright, ac.getLight()); + EXPECT_EQ(kDaikinBeepOff, ac.getBeep()); + ac.setBeep(kDaikinBeepLoud); + EXPECT_EQ(kDaikinBeepLoud, ac.getBeep()); + EXPECT_EQ(kDaikinLightBright, ac.getLight()); +} + +TEST(TestDaikin2Class, FanSpeed) { + IRDaikin2 ac(0); + ac.begin(); + + // Unexpected value should default to Auto. + ac.setFan(0); + EXPECT_EQ(kDaikinFanAuto, ac.getFan()); + + // Unexpected value should default to Auto. + ac.setFan(255); + EXPECT_EQ(kDaikinFanAuto, ac.getFan()); + + ac.setFan(kDaikinFanMax); + EXPECT_EQ(kDaikinFanMax, ac.getFan()); + + // Beyond Max should default to Auto. + ac.setFan(kDaikinFanMax + 1); + EXPECT_EQ(kDaikinFanAuto, ac.getFan()); + + ac.setFan(kDaikinFanMax - 1); + EXPECT_EQ(kDaikinFanMax - 1, ac.getFan()); + + ac.setFan(kDaikinFanMin); + EXPECT_EQ(kDaikinFanMin, ac.getFan()); + + ac.setFan(kDaikinFanMin + 1); + EXPECT_EQ(kDaikinFanMin + 1, ac.getFan()); + + // Beyond Min should default to Auto. + ac.setFan(kDaikinFanMin - 1); + EXPECT_EQ(kDaikinFanAuto, ac.getFan()); + + ac.setFan(3); + EXPECT_EQ(3, ac.getFan()); + + ac.setFan(kDaikinFanAuto); + EXPECT_EQ(kDaikinFanAuto, ac.getFan()); + + ac.setFan(kDaikinFanQuiet); + EXPECT_EQ(kDaikinFanQuiet, ac.getFan()); +} + +// Test Mold mode. +TEST(TestDaikin2Class, MoldSetting) { + IRDaikin2 ac(0); + ac.begin(); + + ac.setMold(false); + ASSERT_FALSE(ac.getMold()); + + ac.setMold(true); + ASSERT_TRUE(ac.getMold()); + + ac.setMold(false); + ASSERT_FALSE(ac.getMold()); +} + +// Test Auto Clean setting. +TEST(TestDaikin2Class, CleanSetting) { + IRDaikin2 ac(0); + ac.begin(); + + ac.setClean(false); + ASSERT_FALSE(ac.getClean()); + + ac.setClean(true); + ASSERT_TRUE(ac.getClean()); + + ac.setClean(false); + ASSERT_FALSE(ac.getClean()); +} + + +TEST(TestDaikin2Class, Temperature) { + IRDaikin2 ac(0); + ac.begin(); + + ac.setMode(kDaikinAuto); + ac.setTemp(0); + EXPECT_EQ(kDaikinMinTemp, ac.getTemp()); + + ac.setTemp(255); + EXPECT_EQ(kDaikinMaxTemp, ac.getTemp()); + + ac.setTemp(kDaikinMinTemp); + EXPECT_EQ(kDaikinMinTemp, ac.getTemp()); + + ac.setTemp(kDaikinMaxTemp); + EXPECT_EQ(kDaikinMaxTemp, ac.getTemp()); + + ac.setTemp(kDaikinMinTemp - 1); + EXPECT_EQ(kDaikinMinTemp, ac.getTemp()); + + ac.setTemp(kDaikinMaxTemp + 1); + EXPECT_EQ(kDaikinMaxTemp, ac.getTemp()); + + ac.setTemp(kDaikinMinTemp + 1); + EXPECT_EQ(kDaikinMinTemp + 1, ac.getTemp()); + + // Now try it with Cool mode, which should set the temp to kDaikin2MinCoolTemp + ASSERT_TRUE(kDaikinMinTemp + 1 < kDaikin2MinCoolTemp); + ac.setMode(kDaikinCool); + EXPECT_EQ(kDaikin2MinCoolTemp, ac.getTemp()); + ac.setTemp(kDaikin2MinCoolTemp - 1); + EXPECT_EQ(kDaikin2MinCoolTemp, ac.getTemp()); + ac.setTemp(kDaikin2MinCoolTemp + 1); + EXPECT_EQ(kDaikin2MinCoolTemp + 1, ac.getTemp()); + // Should be released from that requirement in other modes. + ac.setMode(kDaikinAuto); + ac.setTemp(kDaikin2MinCoolTemp - 1); + EXPECT_EQ(kDaikin2MinCoolTemp - 1, ac.getTemp()); + + ac.setTemp(21); + EXPECT_EQ(21, ac.getTemp()); + + ac.setTemp(25); + EXPECT_EQ(25, ac.getTemp()); + + ac.setTemp(29); + EXPECT_EQ(29, ac.getTemp()); +} + +// Test Fresh Air settings. +TEST(TestDaikin2Class, FreshAirSettings) { + IRDaikin2 ac(0); + ac.begin(); + + ac.setFreshAir(false); + ac.setFreshAirHigh(false); + ASSERT_FALSE(ac.getFreshAir()); + ASSERT_FALSE(ac.getFreshAirHigh()); + + ac.setFreshAir(true); + ASSERT_TRUE(ac.getFreshAir()); + ASSERT_FALSE(ac.getFreshAirHigh()); + + ac.setFreshAirHigh(true); + ASSERT_TRUE(ac.getFreshAir()); + ASSERT_TRUE(ac.getFreshAirHigh()); + + ac.setFreshAir(false); + ASSERT_FALSE(ac.getFreshAir()); + ASSERT_TRUE(ac.getFreshAirHigh()); + + ac.setFreshAirHigh(false); + ASSERT_FALSE(ac.getFreshAir()); + ASSERT_FALSE(ac.getFreshAirHigh()); +} + +// Test Eye mode. +TEST(TestDaikin2Class, EyeSetting) { + IRDaikin2 ac(0); + ac.begin(); + + ac.setEye(false); + ASSERT_FALSE(ac.getEye()); + ac.setEye(true); + ASSERT_TRUE(ac.getEye()); + ac.setEye(false); + ASSERT_FALSE(ac.getEye()); +} + +// Test Econo setting. +TEST(TestDaikin2Class, EconoSetting) { + IRDaikin2 ac(0); + ac.begin(); + + ac.setEcono(false); + ASSERT_FALSE(ac.getEcono()); + ac.setEcono(true); + ASSERT_TRUE(ac.getEcono()); + ac.setEcono(false); + ASSERT_FALSE(ac.getEcono()); +} + +TEST(TestDaikin2Class, SleepTimer) { + IRDaikin2 ac(0); + ac.begin(); + + // NOTE: On & Sleep timer share the same time location. + + // Both timers turned off. + ac.disableOnTimer(); + ac.disableSleepTimer(); + EXPECT_FALSE(ac.getOnTimerEnabled()); + EXPECT_EQ(kDaikinUnusedTime, ac.getOnTime()); + EXPECT_FALSE(ac.getSleepTimerEnabled()); + EXPECT_EQ(kDaikinUnusedTime, ac.getSleepTime()); + + // Turn on just the On Timer. + ac.enableOnTimer(123); + EXPECT_TRUE(ac.getOnTimerEnabled()); + EXPECT_EQ(123, ac.getOnTime()); + EXPECT_FALSE(ac.getSleepTimerEnabled()); + EXPECT_EQ(123, ac.getSleepTime()); + + // Now turn on the Sleep Timer. This shoud disable the On Timer. + ac.enableSleepTimer(754); + EXPECT_TRUE(ac.getSleepTimerEnabled()); + EXPECT_EQ(754, ac.getSleepTime()); + EXPECT_FALSE(ac.getOnTimerEnabled()); + EXPECT_EQ(754, ac.getOnTime()); + + // Turn off the just the On Timer. + ac.disableOnTimer(); + EXPECT_FALSE(ac.getOnTimerEnabled()); + EXPECT_EQ(kDaikinUnusedTime, ac.getOnTime()); + EXPECT_FALSE(ac.getSleepTimerEnabled()); + EXPECT_EQ(kDaikinUnusedTime, ac.getSleepTime()); + + // Now turn on the On Timer and turn off the Sleep Timer. + // Both should be off afterwards. + ac.enableOnTimer(123); + ac.disableSleepTimer(); + EXPECT_FALSE(ac.getSleepTimerEnabled()); + EXPECT_EQ(kDaikinUnusedTime, ac.getSleepTime()); + EXPECT_FALSE(ac.getOnTimerEnabled()); + EXPECT_EQ(kDaikinUnusedTime, ac.getOnTime()); +} + +// Test Vertical Swing. +TEST(TestDaikin2Class, Swing) { + IRDaikin2 ac(0); + ac.begin(); + + // Vertical + ac.setSwingVertical(1); + ASSERT_EQ(1, ac.getSwingVertical()); + ac.setSwingVertical(3); + ASSERT_EQ(3, ac.getSwingVertical()); + ac.setSwingVertical(6); + ASSERT_EQ(6, ac.getSwingVertical()); + ac.setSwingVertical(kDaikin2SwingVBreeze); + ASSERT_EQ(kDaikin2SwingVBreeze, ac.getSwingVertical()); + ac.setSwingVertical(kDaikin2SwingVCirculate); + ASSERT_EQ(kDaikin2SwingVCirculate, ac.getSwingVertical()); + ac.setSwingVertical(kDaikin2SwingVAuto); + ASSERT_EQ(kDaikin2SwingVAuto, ac.getSwingVertical()); + ac.setSwingVertical(0); + ASSERT_EQ(kDaikin2SwingVAuto, ac.getSwingVertical()); + ac.setSwingVertical(7); + ASSERT_EQ(kDaikin2SwingVAuto, ac.getSwingVertical()); + ac.setSwingVertical(255); + ASSERT_EQ(kDaikin2SwingVAuto, ac.getSwingVertical()); + + // Horizontal + ac.setSwingHorizontal(kDaikin2SwingHAuto); + ASSERT_EQ(kDaikin2SwingHAuto, ac.getSwingHorizontal()); + ac.setSwingHorizontal(kDaikin2SwingHSwing); + ASSERT_EQ(kDaikin2SwingHSwing, ac.getSwingHorizontal()); + + ac.setSwingHorizontal(0); + ASSERT_EQ(0, ac.getSwingHorizontal()); + ac.setSwingHorizontal(255); + ASSERT_EQ(255, ac.getSwingHorizontal()); +} + +TEST(TestDaikin2Class, QuietMode) { + IRDaikin2 ac(0); + ac.begin(); + + ac.setQuiet(true); + EXPECT_TRUE(ac.getQuiet()); + + ac.setQuiet(false); + EXPECT_FALSE(ac.getQuiet()); + + ac.setQuiet(true); + EXPECT_TRUE(ac.getQuiet()); + + // But setting Powerful mode should exit out of quiet mode. + ac.setPowerful(true); + EXPECT_FALSE(ac.getQuiet()); +} + +TEST(TestDaikin2Class, PowerfulMode) { + IRDaikin2 ac(0); + ac.begin(); + + ac.setPowerful(true); + EXPECT_TRUE(ac.getPowerful()); + + ac.setPowerful(false); + EXPECT_FALSE(ac.getPowerful()); + + ac.setPowerful(true); + EXPECT_TRUE(ac.getPowerful()); + + ac.setQuiet(true); + EXPECT_FALSE(ac.getPowerful()); +} + +// Test Purify mode. +TEST(TestDaikin2Class, PurifySetting) { + IRDaikin2 ac(0); + ac.begin(); + + ac.setPurify(false); + ASSERT_FALSE(ac.getPurify()); + ac.setPurify(true); + ASSERT_TRUE(ac.getPurify()); + ac.setPurify(false); + ASSERT_FALSE(ac.getPurify()); +} + +TEST(TestDaikin2Class, HumanReadable) { + IRDaikin2 ac(0); + ac.begin(); + ac.setPower(true); + ac.setMode(kDaikinCool); + ac.setTemp(21); + ac.setFan(kDaikinFanMax); + ac.setSwingVertical(kDaikin2SwingVAuto); + ac.setSwingHorizontal(kDaikin2SwingHSwing); + ac.setCurrentTime(12 * 60 + 34); // 12:34 + ac.disableOnTimer(); + ac.enableOffTimer(20 * 60); // 20:00 + ac.enableSleepTimer(4 * 60); // 4:00 + ac.setBeep(kDaikinBeepLoud); + ac.setLight(kDaikinLightDim); + ac.setMold(true); + ac.setClean(false); + ac.setFreshAir(true); + ac.setEye(true); + ac.setEyeAuto(true); + ac.setQuiet(false); + ac.setPowerful(true); + ac.setPurify(true); + ac.setEcono(false); + EXPECT_EQ( + "Power: On, Mode: 3 (COOL), Temp: 21C, Fan: 5 (Max), " + "Swing (V): 14 (Auto), Swing (H): 191 (Swing), Clock: 12:34, " + "On Time: Off, Off Time: 20:00, Sleep Time: 4:00, Beep: 2 (Loud), " + "Light: 2 (Dim), Mold: On, Clean: Off, Fresh Air: On, Eye: On, " + "Eye Auto: On, Quiet: Off, Powerful: On, Purify: On, Econo: Off", + ac.toString()); + ac.setQuiet(true); + ac.setMode(kDaikinHeat); + ac.setBeep(kDaikinBeepQuiet); + ac.setLight(kDaikinLightBright); + ac.setTemp(32); + ac.setFan(kDaikinFanMin); + ac.setCurrentTime(23 * 60 + 45); // 23:45 + ac.enableOnTimer(9 * 60 + 11); // 9:11 + EXPECT_EQ( + "Power: On, Mode: 4 (HEAT), Temp: 32C, Fan: 1 (Min), " + "Swing (V): 14 (Auto), Swing (H): 191 (Swing), Clock: 23:45, " + "On Time: 9:11, Off Time: 20:00, Sleep Time: Off, Beep: 1 (Quiet), " + "Light: 1 (Bright), Mold: On, Clean: Off, Fresh Air: On, Eye: On, " + "Eye Auto: On, Quiet: On, Powerful: Off, Purify: On, Econo: Off", + ac.toString()); +} + +// See if we can construct a known state. +TEST(TestDaikin2Class, KnownConstruction) { + IRDaikin2 ac(0); + + uint8_t expectedState[kDaikin2StateLength] = { + 0x11, 0xDA, 0x27, 0x00, 0x01, 0x7A, 0xC3, 0x70, 0x28, 0x0C, + 0x80, 0x04, 0xB0, 0x16, 0x24, 0x00, 0x00, 0xBE, 0xD5, 0xF5, + 0x11, 0xDA, 0x27, 0x00, 0x00, 0x08, 0x26, 0x00, 0xA0, 0x00, + 0x00, 0x06, 0x60, 0x00, 0x00, 0xC1, 0x80, 0x60, 0xE7}; + + ac.begin(); + ac.setPower(false); + ac.setMode(kDaikinAuto); + ac.setTemp(19); + ac.setFan(kDaikinFanAuto); + ac.setSwingVertical(5); + ac.setSwingHorizontal(kDaikin2SwingHAuto); + ac.setCurrentTime(14 * 60 + 50); // 14:50 + ac.disableOnTimer(); + ac.disableOffTimer(); + ac.disableSleepTimer(); + ac.setBeep(kDaikinBeepQuiet); + ac.setLight(kDaikinLightOff); + ac.setMold(true); + ac.setClean(true); + ac.setFreshAir(false); + ac.setEye(false); + ac.setEyeAuto(false); + ac.setQuiet(false); + ac.setPowerful(false); + ac.setPurify(false); + ac.setEcono(false); + EXPECT_EQ( + "Power: Off, Mode: 0 (AUTO), Temp: 19C, Fan: 10 (Auto), " + "Swing (V): 5, Swing (H): 190 (Auto), " + "Clock: 14:50, On Time: Off, Off Time: Off, Sleep Time: Off, " + "Beep: 1 (Quiet), Light: 3 (Off), Mold: On, Clean: On, Fresh Air: Off, " + "Eye: Off, Eye Auto: Off, Quiet: Off, Powerful: Off, Purify: Off, " + "Econo: Off", + ac.toString()); + EXPECT_STATE_EQ(expectedState, ac.getRaw(), kDaikin2Bits); +} + +TEST(TestUtils, Housekeeping) { + ASSERT_EQ("DAIKIN", typeToString(decode_type_t::DAIKIN)); + ASSERT_EQ(decode_type_t::DAIKIN, strToDecodeType("DAIKIN")); + ASSERT_TRUE(hasACState(decode_type_t::DAIKIN)); + + ASSERT_EQ("DAIKIN2", typeToString(decode_type_t::DAIKIN2)); + ASSERT_EQ(decode_type_t::DAIKIN2, strToDecodeType("DAIKIN2")); + ASSERT_TRUE(hasACState(decode_type_t::DAIKIN2)); + + ASSERT_EQ("DAIKIN216", typeToString(decode_type_t::DAIKIN216)); + ASSERT_EQ(decode_type_t::DAIKIN216, strToDecodeType("DAIKIN216")); + ASSERT_TRUE(hasACState(decode_type_t::DAIKIN216)); +} + +// https://github.com/markszabo/IRremoteESP8266/issues/582#issuecomment-453863879 +TEST(TestDecodeDaikin2, Issue582DeepDecodeExample) { + IRDaikin2 ac(0); + + const uint8_t state[kDaikin2StateLength] = { + 0x11, 0xDA, 0x27, 0x00, 0x01, 0x30, 0x42, 0xF0, 0x28, 0x0C, + 0x80, 0x04, 0xB0, 0x16, 0x24, 0x00, 0x00, 0xBE, 0xCE, 0xA3, + 0x11, 0xDA, 0x27, 0x00, 0x00, 0x09, 0x26, 0x00, 0xA0, 0x00, + 0x00, 0x06, 0x60, 0x00, 0x00, 0xC1, 0x92, 0x60, 0xFA}; + + ac.setRaw(state); + ASSERT_TRUE(ac.getMold()); + ASSERT_TRUE(ac.getEye()); + ASSERT_TRUE(ac.getPurify()); + EXPECT_EQ( + "Power: On, Mode: 0 (AUTO), Temp: 19C, Fan: 10 (Auto), " + "Swing (V): 14 (Auto), Swing (H): 190 (Auto), Clock: 9:20, On Time: Off, " + "Off Time: Off, Sleep Time: Off, Beep: 3 (Off), Light: 3 (Off), " + "Mold: On, Clean: On, Fresh Air: Off, Eye: On, Eye Auto: Off, " + "Quiet: Off, Powerful: Off, Purify: On, Econo: Off", + ac.toString()); +} + +// https://docs.google.com/spreadsheets/d/1f8EGfIbBUo2B-CzUFdrgKQprWakoYNKM80IKZN4KXQE/edit?ts=5c317775#gid=1023395743 +TEST(TestDecodeDaikin2, Issue582PowerfulEconoFix) { + IRDaikin2 ac(0); + + const uint8_t PowerfulOn[39] = { + 0x11, 0xDA, 0x27, 0x00, 0x01, 0x3A, 0x43, 0xF0, 0x28, 0x0C, + 0x80, 0x04, 0xB0, 0x16, 0x24, 0x00, 0x00, 0xBE, 0xCE, 0xAE, + 0x11, 0xDA, 0x27, 0x00, 0x00, 0x39, 0x28, 0x00, 0xA0, 0x00, + 0x00, 0x06, 0x60, 0x01, 0x00, 0xC1, 0x90, 0x60, 0x2B}; + const uint8_t PowerfulOff[39] = { + 0x11, 0xDA, 0x27, 0x00, 0x01, 0x3A, 0x43, 0xF0, 0x28, 0x0C, + 0x80, 0x04, 0xB0, 0x16, 0x24, 0x00, 0x00, 0xBE, 0xCE, 0xAE, + 0x11, 0xDA, 0x27, 0x00, 0x00, 0x39, 0x28, 0x00, 0xA0, 0x00, + 0x00, 0x06, 0x60, 0x00, 0x00, 0xC1, 0x90, 0x60, 0x2A}; + ac.setRaw(PowerfulOn); + ASSERT_TRUE(ac.getPowerful()); + EXPECT_EQ( + "Power: On, Mode: 3 (COOL), Temp: 20C, Fan: 10 (Auto), " + "Swing (V): 14 (Auto), Swing (H): 190 (Auto), Clock: 13:46, " + "On Time: Off, Off Time: Off, Sleep Time: Off, Beep: 3 (Off), " + "Light: 3 (Off), Mold: On, Clean: On, Fresh Air: Off, Eye: Off, " + "Eye Auto: Off, Quiet: Off, Powerful: On, Purify: On, Econo: Off", + ac.toString()); + ac.setRaw(PowerfulOff); + ASSERT_FALSE(ac.getPowerful()); + EXPECT_EQ( + "Power: On, Mode: 3 (COOL), Temp: 20C, Fan: 10 (Auto), " + "Swing (V): 14 (Auto), Swing (H): 190 (Auto), Clock: 13:46, " + "On Time: Off, Off Time: Off, Sleep Time: Off, Beep: 3 (Off), " + "Light: 3 (Off), Mold: On, Clean: On, Fresh Air: Off, Eye: Off, " + "Eye Auto: Off, Quiet: Off, Powerful: Off, Purify: On, Econo: Off", + ac.toString()); + + const uint8_t EconoOn[39] = { + 0x11, 0xDA, 0x27, 0x00, 0x01, 0x3B, 0x43, 0xF0, 0x28, 0x0C, + 0x80, 0x04, 0xB0, 0x16, 0x24, 0x00, 0x00, 0xBE, 0xCE, 0xAF, + 0x11, 0xDA, 0x27, 0x00, 0x00, 0x39, 0x28, 0x00, 0xA0, 0x00, + 0x00, 0x06, 0x60, 0x00, 0x00, 0xC1, 0x94, 0x60, 0x2E}; + const uint8_t EconoOff[39] = { + 0x11, 0xDA, 0x27, 0x00, 0x01, 0x3B, 0x43, 0xF0, 0x28, 0x0C, + 0x80, 0x04, 0xB0, 0x16, 0x24, 0x00, 0x00, 0xBE, 0xCE, 0xAF, + 0x11, 0xDA, 0x27, 0x00, 0x00, 0x39, 0x28, 0x00, 0xA0, 0x00, + 0x00, 0x06, 0x60, 0x00, 0x00, 0xC1, 0x90, 0x60, 0x2A}; + ac.setRaw(EconoOn); + ASSERT_TRUE(ac.getEcono()); + EXPECT_EQ( + "Power: On, Mode: 3 (COOL), Temp: 20C, Fan: 10 (Auto), " + "Swing (V): 14 (Auto), Swing (H): 190 (Auto), Clock: 13:47, " + "On Time: Off, Off Time: Off, Sleep Time: Off, Beep: 3 (Off), " + "Light: 3 (Off), Mold: On, Clean: On, Fresh Air: Off, Eye: Off, " + "Eye Auto: Off, Quiet: Off, Powerful: Off, Purify: On, Econo: On", + ac.toString()); + ac.setRaw(EconoOff); + ASSERT_FALSE(ac.getEcono()); + EXPECT_EQ( + "Power: On, Mode: 3 (COOL), Temp: 20C, Fan: 10 (Auto), " + "Swing (V): 14 (Auto), Swing (H): 190 (Auto), Clock: 13:47, " + "On Time: Off, Off Time: Off, Sleep Time: Off, Beep: 3 (Off), " + "Light: 3 (Off), Mold: On, Clean: On, Fresh Air: Off, Eye: Off, " + "Eye Auto: Off, Quiet: Off, Powerful: Off, Purify: On, Econo: Off", + ac.toString()); +} + +// Tests for IRDaikin216 class. + +TEST(TestDaikin216Class, Power) { + IRDaikin216 ac(0); + ac.begin(); + + ac.on(); + EXPECT_TRUE(ac.getPower()); + + ac.off(); + EXPECT_FALSE(ac.getPower()); + + ac.setPower(true); + EXPECT_TRUE(ac.getPower()); + + ac.setPower(false); + EXPECT_FALSE(ac.getPower()); +} + +TEST(TestDaikin216Class, Temperature) { + IRDaikin216 ac(0); + ac.begin(); + + ac.setTemp(0); + EXPECT_EQ(kDaikinMinTemp, ac.getTemp()); + + ac.setTemp(255); + EXPECT_EQ(kDaikinMaxTemp, ac.getTemp()); + + ac.setTemp(kDaikinMinTemp); + EXPECT_EQ(kDaikinMinTemp, ac.getTemp()); + + ac.setTemp(kDaikinMaxTemp); + EXPECT_EQ(kDaikinMaxTemp, ac.getTemp()); + + ac.setTemp(kDaikinMinTemp - 1); + EXPECT_EQ(kDaikinMinTemp, ac.getTemp()); + + ac.setTemp(kDaikinMaxTemp + 1); + EXPECT_EQ(kDaikinMaxTemp, ac.getTemp()); + + ac.setTemp(kDaikinMinTemp + 1); + EXPECT_EQ(kDaikinMinTemp + 1, ac.getTemp()); + + ac.setTemp(21); + EXPECT_EQ(21, ac.getTemp()); + + ac.setTemp(25); + EXPECT_EQ(25, ac.getTemp()); + + ac.setTemp(29); + EXPECT_EQ(29, ac.getTemp()); +} + +TEST(TestDaikin216Class, OperatingMode) { + IRDaikin216 ac(0); + ac.begin(); + + ac.setMode(kDaikinAuto); + EXPECT_EQ(kDaikinAuto, ac.getMode()); + + ac.setMode(kDaikinCool); + EXPECT_EQ(kDaikinCool, ac.getMode()); + + ac.setMode(kDaikinHeat); + EXPECT_EQ(kDaikinHeat, ac.getMode()); + + ac.setMode(kDaikinDry); + EXPECT_EQ(kDaikinDry, ac.getMode()); + + ac.setMode(kDaikinFan); + EXPECT_EQ(kDaikinFan, ac.getMode()); + + ac.setMode(kDaikinFan + 1); + EXPECT_EQ(kDaikinAuto, ac.getMode()); + + ac.setMode(kDaikinAuto + 1); + EXPECT_EQ(kDaikinAuto, ac.getMode()); + + ac.setMode(255); + EXPECT_EQ(kDaikinAuto, ac.getMode()); +} + + +TEST(TestDaikin216Class, VaneSwing) { + IRDaikin216 ac(0); + ac.begin(); + + ac.setSwingHorizontal(true); + ac.setSwingVertical(false); + + ac.setSwingHorizontal(true); + EXPECT_TRUE(ac.getSwingHorizontal()); + EXPECT_FALSE(ac.getSwingVertical()); + + ac.setSwingVertical(true); + EXPECT_TRUE(ac.getSwingHorizontal()); + EXPECT_TRUE(ac.getSwingVertical()); + + ac.setSwingHorizontal(false); + EXPECT_FALSE(ac.getSwingHorizontal()); + EXPECT_TRUE(ac.getSwingVertical()); + + ac.setSwingVertical(false); + EXPECT_FALSE(ac.getSwingHorizontal()); + EXPECT_FALSE(ac.getSwingVertical()); +} + +TEST(TestDaikin216Class, FanSpeed) { + IRDaikin216 ac(0); + ac.begin(); + + // Unexpected value should default to Auto. + ac.setFan(0); + EXPECT_EQ(kDaikinFanAuto, ac.getFan()); + + // Unexpected value should default to Auto. + ac.setFan(255); + EXPECT_EQ(kDaikinFanAuto, ac.getFan()); + + ac.setFan(kDaikinFanMax); + EXPECT_EQ(kDaikinFanMax, ac.getFan()); + + // Beyond Max should default to Auto. + ac.setFan(kDaikinFanMax + 1); + EXPECT_EQ(kDaikinFanAuto, ac.getFan()); + + ac.setFan(kDaikinFanMax - 1); + EXPECT_EQ(kDaikinFanMax - 1, ac.getFan()); + + ac.setFan(kDaikinFanMin); + EXPECT_EQ(kDaikinFanMin, ac.getFan()); + + ac.setFan(kDaikinFanMin + 1); + EXPECT_EQ(kDaikinFanMin + 1, ac.getFan()); + + // Beyond Min should default to Auto. + ac.setFan(kDaikinFanMin - 1); + EXPECT_EQ(kDaikinFanAuto, ac.getFan()); + + ac.setFan(3); + EXPECT_EQ(3, ac.getFan()); + + ac.setFan(kDaikinFanAuto); + EXPECT_EQ(kDaikinFanAuto, ac.getFan()); + + ac.setFan(kDaikinFanQuiet); + EXPECT_EQ(kDaikinFanQuiet, ac.getFan()); +} + +TEST(TestDaikin216Class, Quiet) { + IRDaikin216 ac(0); + ac.begin(); + + ac.setQuiet(true); + EXPECT_TRUE(ac.getQuiet()); + + ac.setQuiet(false); + EXPECT_FALSE(ac.getQuiet()); + + ac.setQuiet(true); + EXPECT_TRUE(ac.getQuiet()); +} + +TEST(TestDaikin216Class, ExampleStates) { + IRDaikin216 ac(0); + ac.begin(); + // https://github.com/markszabo/IRremoteESP8266/pull/690#issuecomment-487770194 + uint8_t state[kDaikin216StateLength] = { + 0x11, 0xDA, 0x27, 0xF0, 0x00, 0x00, 0x00, 0x02, + 0x11, 0xDA, 0x27, 0x00, 0x00, 0x21, 0xC0, 0x00, 0xA0, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x53}; + ac.setRaw(state); + EXPECT_EQ( + "Power: On, Mode: 2 (DRY), Temp: 32C, Fan: 10 (AUTO), " + "Swing (Horizontal): Off, Swing (Vertical): Off, Quiet: Off", + ac.toString()); +} + +TEST(TestDaikin216Class, ReconstructKnownState) { + IRDaikin216 ac(0); + ac.begin(); + // https://github.com/markszabo/IRremoteESP8266/issues/689#issue-438086949 + uint8_t expectedState[kDaikin216StateLength] = { + 0x11, 0xDA, 0x27, 0xF0, 0x00, 0x00, 0x00, 0x02, + 0x11, 0xDA, 0x27, 0x00, 0x00, 0x00, 0x26, 0x00, 0xA0, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x98}; + ac.setPower(false); + ac.setMode(kDaikinAuto); + ac.setTemp(19); + ac.setFan(kDaikinFanAuto); + ac.setSwingHorizontal(false); + ac.setSwingVertical(false); + ac.setQuiet(false); + EXPECT_EQ( + "Power: Off, Mode: 0 (AUTO), Temp: 19C, Fan: 10 (AUTO), " + "Swing (Horizontal): Off, Swing (Vertical): Off, Quiet: Off", + ac.toString()); + + EXPECT_STATE_EQ(expectedState, ac.getRaw(), kDaikin216Bits); +} + +// https://github.com/markszabo/IRremoteESP8266/issues/689 +TEST(TestDecodeDaikin216, RealExample) { + IRsendTest irsend(0); + IRrecv irrecv(0); + // https://github.com/markszabo/IRremoteESP8266/issues/689#issue-438086949 + uint16_t rawData[439] = { + 3402, 1770, 382, 1340, 382, 480, 382, 478, 382, 480, 380, 1342, 382, 478, + 356, 504, 382, 480, 380, 478, 384, 1342, 380, 480, 380, 1342, 382, 1342, + 382, 478, 382, 1340, 382, 1340, 384, 1340, 382, 1342, 382, 1340, 380, 480, + 382, 480, 382, 1296, 426, 480, 380, 480, 382, 480, 380, 480, 382, 480, + 382, 478, 382, 1342, 382, 1342, 382, 1340, 356, 1368, 382, 478, 382, 480, + 382, 478, 380, 480, 382, 480, 382, 480, 382, 478, 382, 480, 382, 478, 358, + 504, 382, 480, 380, 480, 382, 480, 382, 480, 380, 480, 382, 478, 382, 480, + 382, 478, 382, 480, 354, 506, 354, 506, 380, 480, 382, 480, 382, 480, 382, + 480, 380, 1342, 382, 480, 382, 480, 382, 478, 382, 478, 382, 478, 384, + 478, 382, 29652, 3426, 1772, 382, 1340, 382, 480, 380, 478, 382, 480, 382, + 1342, 382, 480, 382, 480, 382, 478, 356, 506, 382, 1342, 380, 480, 382, + 1340, 382, 1340, 382, 478, 356, 1366, 382, 1340, 384, 1340, 382, 1340, + 382, 1342, 382, 478, 382, 478, 382, 1340, 382, 478, 382, 478, 382, 478, + 382, 480, 382, 480, 384, 478, 358, 504, 382, 478, 382, 480, 382, 478, 382, + 480, 382, 480, 382, 478, 382, 480, 382, 478, 382, 478, 382, 478, 382, 478, + 384, 478, 382, 478, 360, 500, 358, 504, 382, 478, 382, 480, 382, 480, 382, + 478, 382, 478, 382, 1340, 382, 1342, 382, 480, 380, 480, 382, 1342, 382, + 478, 382, 480, 356, 506, 382, 478, 382, 480, 382, 480, 356, 506, 382, 478, + 382, 480, 382, 478, 382, 480, 382, 478, 382, 480, 380, 480, 380, 480, 382, + 1342, 382, 478, 382, 1342, 382, 480, 382, 480, 382, 478, 382, 478, 382, + 480, 382, 478, 382, 480, 356, 504, 384, 478, 382, 480, 382, 480, 380, 480, + 382, 478, 382, 480, 382, 480, 382, 478, 356, 504, 384, 478, 380, 480, 382, + 480, 382, 480, 382, 478, 356, 506, 382, 478, 382, 480, 380, 480, 382, 478, + 382, 480, 382, 478, 382, 480, 358, 504, 382, 478, 382, 478, 356, 504, 382, + 478, 382, 480, 382, 478, 382, 478, 382, 478, 382, 480, 380, 480, 382, 480, + 380, 480, 356, 506, 356, 504, 382, 480, 382, 478, 382, 478, 382, 478, 382, + 478, 382, 480, 382, 478, 382, 480, 382, 480, 382, 1340, 382, 1342, 382, + 478, 384, 478, 382, 478, 382, 480, 380, 480, 382, 478, 382, 480, 356, 506, + 382, 478, 382, 480, 382, 478, 356, 506, 380, 480, 382, 478, 382, 478, 382, + 478, 382, 480, 382, 480, 380, 480, 382, 1342, 382, 1340, 382, 480, 356, + 504, 382, 1342, 382}; // UNKNOWN E0E32232 + uint8_t expectedState[kDaikin216StateLength] = { + // 8 bytes + 0x11, 0xDA, 0x27, 0xF0, 0x00, 0x00, 0x00, 0x02, + // 19 bytes + 0x11, 0xDA, 0x27, 0x00, 0x00, 0x00, 0x26, 0x00, 0xA0, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x98}; + + irsend.begin(); + irsend.reset(); + irsend.sendRaw(rawData, 439, 38000); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(DAIKIN216, irsend.capture.decode_type); + ASSERT_EQ(kDaikin216Bits, irsend.capture.bits); + EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits); + + IRDaikin216 ac(0); + ac.setRaw(irsend.capture.state); + EXPECT_EQ( + "Power: Off, Mode: 0 (AUTO), Temp: 19C, Fan: 10 (AUTO), " + "Swing (Horizontal): Off, Swing (Vertical): Off, Quiet: Off", + ac.toString()); +} + +// https://github.com/markszabo/IRremoteESP8266/issues/689 +TEST(TestDecodeDaikin216, SyntheticExample) { + IRsendTest irsend(0); + IRrecv irrecv(0); + // https://github.com/markszabo/IRremoteESP8266/issues/689#issue-438086949 + uint8_t expectedState[kDaikin216StateLength] = { + // 8 bytes + 0x11, 0xDA, 0x27, 0xF0, 0x00, 0x00, 0x00, 0x02, + // 19 bytes + 0x11, 0xDA, 0x27, 0x00, 0x00, 0x00, 0x26, 0x00, 0xA0, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x98}; + + irsend.begin(); + irsend.reset(); + irsend.sendDaikin216(expectedState); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(DAIKIN216, irsend.capture.decode_type); + ASSERT_EQ(kDaikin216Bits, irsend.capture.bits); + EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits); +} diff --git a/lib/IRremoteESP8266-2.5.2.03/test/ir_Denon_test.cpp b/lib/IRremoteESP8266-2.6.0/test/ir_Denon_test.cpp similarity index 98% rename from lib/IRremoteESP8266-2.5.2.03/test/ir_Denon_test.cpp rename to lib/IRremoteESP8266-2.6.0/test/ir_Denon_test.cpp index 911fd75281e5..6b80eae027fe 100644 --- a/lib/IRremoteESP8266-2.5.2.03/test/ir_Denon_test.cpp +++ b/lib/IRremoteESP8266-2.6.0/test/ir_Denon_test.cpp @@ -14,6 +14,7 @@ TEST(TestSendDenon, SendDataOnly) { irsend.reset(); irsend.sendDenon(0x2278); // Denon AVR Power On. (Sharp) EXPECT_EQ( + "f38000d33" "m260s780m260s1820m260s780m260s780m260s780m260s1820m260s780m260s780" "m260s1820m260s1820m260s1820m260s1820m260s780m260s780m260s780" "m260s43602" @@ -26,6 +27,7 @@ TEST(TestSendDenon, SendDataOnly) { // Denon Eco Mode On. (Panasonic/Kaseikyo) irsend.sendDenon(0x2A4C028D6CE3, DENON_48_BITS); EXPECT_EQ( + "f36700d50" "m3456s1728" "m432s432m432s432m432s1296m432s432m432s1296m432s432m432s1296m432s432" "m432s432m432s1296m432s432m432s432m432s1296m432s1296m432s432m432s432" @@ -45,6 +47,7 @@ TEST(TestSendDenon, SendNormalWithRepeats) { irsend.reset(); irsend.sendDenon(0x2278, DENON_BITS, 1); // 1 repeat. EXPECT_EQ( + "f38000d33" "m260s780m260s1820m260s780m260s780m260s780m260s1820m260s780m260s780" "m260s1820m260s1820m260s1820m260s1820m260s780m260s780m260s780" "m260s43602" @@ -60,6 +63,7 @@ TEST(TestSendDenon, SendNormalWithRepeats) { irsend.outputStr()); irsend.sendDenon(0x2278, DENON_BITS, 2); // 2 repeats. EXPECT_EQ( + "f38000d33" "m260s780m260s1820m260s780m260s780m260s780m260s1820m260s780m260s780" "m260s1820m260s1820m260s1820m260s1820m260s780m260s780m260s780" "m260s43602" @@ -88,6 +92,7 @@ TEST(TestSendDenon, Send48BitWithRepeats) { irsend.reset(); irsend.sendDenon(0x2A4C028D6CE3, DENON_48_BITS, 1); // 1 repeat. EXPECT_EQ( + "f36700d50" "m3456s1728" "m432s432m432s432m432s1296m432s432m432s1296m432s432m432s1296m432s432" "m432s432m432s1296m432s432m432s432m432s1296m432s1296m432s432m432s432" @@ -107,6 +112,7 @@ TEST(TestSendDenon, Send48BitWithRepeats) { irsend.outputStr()); irsend.sendDenon(0x2A4C028D6CE3, DENON_48_BITS, 2); // 2 repeats. EXPECT_EQ( + "f36700d50" "m3456s1728" "m432s432m432s432m432s1296m432s432m432s1296m432s432m432s1296m432s432" "m432s432m432s1296m432s432m432s432m432s1296m432s1296m432s432m432s432" @@ -142,6 +148,7 @@ TEST(TestSendDenon, SendUnusualSize) { irsend.reset(); irsend.sendDenon(0x12, 8); EXPECT_EQ( + "f38000d33" "m260s780m260s780m260s780m260s1820m260s780m260s780m260s1820m260s780" "m260s43602" "m260s1820m260s1820m260s1820m260s780m260s1820m260s1820m260s780m260s1820" @@ -151,6 +158,7 @@ TEST(TestSendDenon, SendUnusualSize) { irsend.reset(); irsend.sendDenon(0x1234567890ABCDEF, 64); EXPECT_EQ( + "f36700d50" "m3456s1728" "m432s432m432s432m432s432m432s1296m432s432m432s432m432s1296m432s432" "m432s432m432s432m432s1296m432s1296m432s432m432s1296m432s432m432s432" diff --git a/lib/IRremoteESP8266-2.5.2.03/test/ir_Dish_test.cpp b/lib/IRremoteESP8266-2.6.0/test/ir_Dish_test.cpp similarity index 98% rename from lib/IRremoteESP8266-2.5.2.03/test/ir_Dish_test.cpp rename to lib/IRremoteESP8266-2.6.0/test/ir_Dish_test.cpp index 0c58496cedfc..21286b7ac672 100644 --- a/lib/IRremoteESP8266-2.5.2.03/test/ir_Dish_test.cpp +++ b/lib/IRremoteESP8266-2.6.0/test/ir_Dish_test.cpp @@ -14,6 +14,7 @@ TEST(TestSendDish, SendDataOnly) { irsend.reset(); irsend.sendDISH(0x0); EXPECT_EQ( + "f57600d50" "m400s6100" "m400s2800m400s2800m400s2800m400s2800m400s2800m400s2800m400s2800m400s2800" "m400s2800m400s2800m400s2800m400s2800m400s2800m400s2800m400s2800m400s2800" @@ -32,6 +33,7 @@ TEST(TestSendDish, SendDataOnly) { irsend.reset(); irsend.sendDISH(0x9C00); // Power on. EXPECT_EQ( + "f57600d50" "m400s6100" "m400s1700m400s2800m400s2800m400s1700m400s1700m400s1700m400s2800m400s2800" "m400s2800m400s2800m400s2800m400s2800m400s2800m400s2800m400s2800m400s2800" @@ -50,6 +52,7 @@ TEST(TestSendDish, SendDataOnly) { irsend.reset(); irsend.sendDISH(0xFFFF); EXPECT_EQ( + "f57600d50" "m400s6100" "m400s1700m400s1700m400s1700m400s1700m400s1700m400s1700m400s1700m400s1700" "m400s1700m400s1700m400s1700m400s1700m400s1700m400s1700m400s1700m400s1700" @@ -74,6 +77,7 @@ TEST(TestSendDish, SendWithRepeats) { irsend.reset(); irsend.sendDISH(0x9C00, kDishBits, 0); // 0 repeats. EXPECT_EQ( + "f57600d50" "m400s6100" "m400s1700m400s2800m400s2800m400s1700m400s1700m400s1700m400s2800m400s2800" "m400s2800m400s2800m400s2800m400s2800m400s2800m400s2800m400s2800m400s2800" @@ -83,6 +87,7 @@ TEST(TestSendDish, SendWithRepeats) { irsend.reset(); irsend.sendDISH(0x9C00, kDishBits, 1); // 1 repeat. EXPECT_EQ( + "f57600d50" "m400s6100" "m400s1700m400s2800m400s2800m400s1700m400s1700m400s1700m400s2800m400s2800" "m400s2800m400s2800m400s2800m400s2800m400s2800m400s2800m400s2800m400s2800" @@ -94,6 +99,7 @@ TEST(TestSendDish, SendWithRepeats) { irsend.sendDISH(0x9C00, kDishBits, 2); // 2 repeats. EXPECT_EQ( + "f57600d50" "m400s6100" "m400s1700m400s2800m400s2800m400s1700m400s1700m400s1700m400s2800m400s2800" "m400s2800m400s2800m400s2800m400s2800m400s2800m400s2800m400s2800m400s2800" @@ -115,6 +121,7 @@ TEST(TestSendDish, SendUnusualSize) { irsend.reset(); irsend.sendDISH(0x0, 8); EXPECT_EQ( + "f57600d50" "m400s6100" "m400s2800m400s2800m400s2800m400s2800m400s2800m400s2800m400s2800m400s2800" "m400s6100" @@ -129,6 +136,7 @@ TEST(TestSendDish, SendUnusualSize) { irsend.reset(); irsend.sendDISH(0x1234567890ABCDEF, 64); EXPECT_EQ( + "f57600d50" "m400s6100" "m400s2800m400s2800m400s2800m400s1700m400s2800m400s2800m400s1700m400s2800" "m400s2800m400s2800m400s1700m400s1700m400s2800m400s1700m400s2800m400s2800" diff --git a/lib/IRremoteESP8266-2.5.2.03/test/ir_Electra_test.cpp b/lib/IRremoteESP8266-2.6.0/test/ir_Electra_test.cpp similarity index 84% rename from lib/IRremoteESP8266-2.5.2.03/test/ir_Electra_test.cpp rename to lib/IRremoteESP8266-2.6.0/test/ir_Electra_test.cpp index df5dd7a5c169..7d6d0c915d95 100644 --- a/lib/IRremoteESP8266-2.5.2.03/test/ir_Electra_test.cpp +++ b/lib/IRremoteESP8266-2.6.0/test/ir_Electra_test.cpp @@ -12,12 +12,13 @@ TEST(TestSendElectraAC, SendDataOnly) { IRsendTest irsend(0); irsend.begin(); - uint8_t data[kElectraAcStateLength] = {0xC3, 0xE1, 0x6F, 0x14, 0x06, - 0x00, 0x04, 0x00, 0x00, 0x04, - 0x00, 0xA0, 0xB0}; + uint8_t data[kElectraAcStateLength] = {0xC3, 0x87, 0xF6, 0x28, 0x60, + 0x00, 0x20, 0x00, 0x00, 0x20, + 0x00, 0x05, 0x0D}; irsend.sendElectraAC(data); EXPECT_EQ( + "f38000d50" "m9166s4470" "m646s1647m646s1647m646s547m646s547m646s547m646s547m646s1647m646s1647" "m646s1647m646s1647m646s1647m646s547m646s547m646s547m646s547m646s1647" @@ -46,9 +47,9 @@ TEST(TestDecodeElectraAC, SyntheticDecode) { // Synthesised Normal ElectraAC message. irsend.reset(); - uint8_t expectedState[kElectraAcStateLength] = {0xC3, 0xE1, 0x6F, 0x14, 0x06, - 0x00, 0x04, 0x00, 0x00, 0x04, - 0x00, 0xA0, 0xB0}; + uint8_t expectedState[kElectraAcStateLength] = {0xC3, 0x87, 0xF6, 0x28, 0x60, + 0x00, 0x20, 0x00, 0x00, 0x20, + 0x00, 0x05, 0x0D}; irsend.sendElectraAC(expectedState); irsend.makeDecodeResult(); EXPECT_TRUE(irrecv.decode(&irsend.capture)); @@ -84,9 +85,9 @@ TEST(TestDecodeElectraAC, RealExampleDecode) { 662, 562, 642, 1686, 582, 570, 634, 566, 604, 576, 636, 566, 610, 578, 634, 1664, 584, 590, 660, 1636, 610, 1642, 664, 590, 610, 590, 636, 566, 634, 568, 686}; // UNKNOWN 9AD8CDB5 - uint8_t expectedState[kElectraAcStateLength] = {0xC3, 0xE1, 0x6F, 0x14, 0x06, - 0x00, 0x04, 0x00, 0x00, 0x04, - 0x00, 0xA0, 0xB0}; + uint8_t expectedState[kElectraAcStateLength] = {0xC3, 0x87, 0xF6, 0x28, 0x60, + 0x00, 0x20, 0x00, 0x00, 0x20, + 0x00, 0x05, 0x0D}; irsend.reset(); irsend.sendRaw(rawData, 211, 38000); diff --git a/lib/IRremoteESP8266-2.5.2.03/test/ir_Fujitsu_test.cpp b/lib/IRremoteESP8266-2.6.0/test/ir_Fujitsu_test.cpp similarity index 99% rename from lib/IRremoteESP8266-2.5.2.03/test/ir_Fujitsu_test.cpp rename to lib/IRremoteESP8266-2.6.0/test/ir_Fujitsu_test.cpp index 23fa3e7a7998..b895e4d9be3c 100644 --- a/lib/IRremoteESP8266-2.5.2.03/test/ir_Fujitsu_test.cpp +++ b/lib/IRremoteESP8266-2.6.0/test/ir_Fujitsu_test.cpp @@ -183,6 +183,7 @@ TEST(TestSendFujitsuAC, GenerateMessage) { irsend.reset(); irsend.sendFujitsuAC(fujitsu.getRaw(), kFujitsuAcStateLength); EXPECT_EQ( + "f38000d50" "m3324s1574" "m448s390m448s390m448s1182m448s390m448s1182m448s390m448s390m448s390" "m448s1182m448s1182m448s390m448s390m448s390m448s1182m448s1182m448s390" @@ -217,6 +218,7 @@ TEST(TestSendFujitsuAC, GenerateShortMessage) { irsend.reset(); irsend.sendFujitsuAC(fujitsu.getRaw(), kFujitsuAcStateLengthShort); EXPECT_EQ( + "f38000d50" "m3324s1574m448s390m448s390m448s1182m448s390m448s1182m448s390m448s390m448" "s390m448s1182m448s1182m448s390m448s390m448s390m448s1182m448s1182m448s390" "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" @@ -238,6 +240,7 @@ TEST(TestSendFujitsuAC, Issue275) { fujitsu.setCmd(kFujitsuAcCmdTurnOff); irsend.sendFujitsuAC(fujitsu.getRaw(), kFujitsuAcStateLengthShort); EXPECT_EQ( + "f38000d50" // Header "m3324s1574" // 0 0 1 0 1 0 0 0 (0x28) @@ -272,6 +275,7 @@ TEST(TestSendFujitsuAC, Issue275) { 450, 1250, 450}; irsend.sendRaw(off, 115, 38); EXPECT_EQ( + "f38000d50" // Header "m3350s1650" // 0 0 1 0 1 0 0 0 (0x28) @@ -534,6 +538,7 @@ TEST(TestDecodeFujitsuAC, Issue414) { ASSERT_EQ(kFujitsuAcBits, irsend.capture.bits); EXPECT_TRUE(ArraysMatch(state, irsend.capture.state)); EXPECT_EQ( + "f38000d50" "m3324s1574" "m448s390m448s390m448s1182m448s390m448s1182m448s390m448s390m448s390" "m448s1182m448s1182m448s390m448s390m448s390m448s1182m448s1182m448s390" diff --git a/lib/IRremoteESP8266-2.5.2.03/test/ir_GICable_test.cpp b/lib/IRremoteESP8266-2.6.0/test/ir_GICable_test.cpp similarity index 98% rename from lib/IRremoteESP8266-2.5.2.03/test/ir_GICable_test.cpp rename to lib/IRremoteESP8266-2.6.0/test/ir_GICable_test.cpp index b9bfce997464..bad9bbdede9c 100644 --- a/lib/IRremoteESP8266-2.5.2.03/test/ir_GICable_test.cpp +++ b/lib/IRremoteESP8266-2.6.0/test/ir_GICable_test.cpp @@ -12,6 +12,7 @@ TEST(TestSendGICable, SendDataOnly) { irsend.begin(); irsend.sendGICable(0); EXPECT_EQ( + "f39000d50" "m9000s4400" "m550s2200m550s2200m550s2200m550s2200m550s2200m550s2200m550s2200m550s2200" "m550s2200m550s2200m550s2200m550s2200m550s2200m550s2200m550s2200m550s2200" @@ -20,6 +21,7 @@ TEST(TestSendGICable, SendDataOnly) { irsend.outputStr()); irsend.sendGICable(0x8807); EXPECT_EQ( + "f39000d50" "m9000s4400" "m550s4400m550s2200m550s2200m550s2200m550s4400m550s2200m550s2200m550s2200" "m550s2200m550s2200m550s2200m550s2200m550s2200m550s4400m550s4400m550s4400" @@ -28,6 +30,7 @@ TEST(TestSendGICable, SendDataOnly) { irsend.outputStr()); irsend.sendGICable(0xFFFF); EXPECT_EQ( + "f39000d50" "m9000s4400" "m550s4400m550s4400m550s4400m550s4400m550s4400m550s4400m550s4400m550s4400" "m550s4400m550s4400m550s4400m550s4400m550s4400m550s4400m550s4400m550s4400" @@ -43,6 +46,7 @@ TEST(TestSendGICable, SendWithRepeats) { // Send a command with 0 repeats. irsend.sendGICable(0x8807, kGicableBits, 0); EXPECT_EQ( + "f39000d50" "m9000s4400" "m550s4400m550s2200m550s2200m550s2200m550s4400m550s2200m550s2200m550s2200" "m550s2200m550s2200m550s2200m550s2200m550s2200m550s4400m550s4400m550s4400" @@ -51,6 +55,7 @@ TEST(TestSendGICable, SendWithRepeats) { // Send a command with 1 repeat. irsend.sendGICable(0x8807, kGicableBits, 1); EXPECT_EQ( + "f39000d50" "m9000s4400" "m550s4400m550s2200m550s2200m550s2200m550s4400m550s2200m550s2200m550s2200" "m550s2200m550s2200m550s2200m550s2200m550s2200m550s4400m550s4400m550s4400" @@ -60,6 +65,7 @@ TEST(TestSendGICable, SendWithRepeats) { // Send a command with 3 repeats. irsend.sendGICable(0x8807, kGicableBits, 3); EXPECT_EQ( + "f39000d50" "m9000s4400" "m550s4400m550s2200m550s2200m550s2200m550s4400m550s2200m550s2200m550s2200" "m550s2200m550s2200m550s2200m550s2200m550s2200m550s4400m550s4400m550s4400" diff --git a/lib/IRremoteESP8266-2.5.2.03/test/ir_GlobalCache_test.cpp b/lib/IRremoteESP8266-2.6.0/test/ir_GlobalCache_test.cpp similarity index 98% rename from lib/IRremoteESP8266-2.5.2.03/test/ir_GlobalCache_test.cpp rename to lib/IRremoteESP8266-2.6.0/test/ir_GlobalCache_test.cpp index 16a556b57f95..00742aedac49 100644 --- a/lib/IRremoteESP8266-2.5.2.03/test/ir_GlobalCache_test.cpp +++ b/lib/IRremoteESP8266-2.6.0/test/ir_GlobalCache_test.cpp @@ -29,6 +29,7 @@ TEST(TestSendGlobalCache, NonRepeatingCode) { EXPECT_EQ(0x4, irsend.capture.address); EXPECT_EQ(0x41, irsend.capture.command); EXPECT_EQ( + "f38000d50" "m8892s4472m546s572m546s546m546s1690m546s546m546s572m546s572" "m546s546m546s572m546s1690m546s1690m546s572m546s1690m546s1690" "m546s1690m546s1690m546s1690m546s1690m546s572m546s572m546s546" @@ -60,6 +61,7 @@ TEST(TestSendGlobalCache, RepeatCode) { EXPECT_EQ(0x4583, irsend.capture.address); EXPECT_EQ(0x11, irsend.capture.command); EXPECT_EQ( + "f38000d50" "m8866s4446m546s1664m546s1664m546s546m546s546m546s546m546s546" "m546s546m546s1664m546s1664m546s546m546s1664m546s546m546s546" "m546s546m546s1664m546s546m546s1664m546s546m546s546m546s546" diff --git a/lib/IRremoteESP8266-2.5.2.03/test/ir_Gree_test.cpp b/lib/IRremoteESP8266-2.6.0/test/ir_Gree_test.cpp similarity index 98% rename from lib/IRremoteESP8266-2.5.2.03/test/ir_Gree_test.cpp rename to lib/IRremoteESP8266-2.6.0/test/ir_Gree_test.cpp index 6c7a1f637fdb..a05b0639172d 100644 --- a/lib/IRremoteESP8266-2.5.2.03/test/ir_Gree_test.cpp +++ b/lib/IRremoteESP8266-2.6.0/test/ir_Gree_test.cpp @@ -20,7 +20,8 @@ TEST(TestSendGreeChars, SendData) { irsend.reset(); irsend.sendGree(gree_code); EXPECT_EQ( - "m9000s4000" + "f38000d50" + "m9000s4500" "m620s540m620s1600m620s540m620s540m620s1600m620s540m620s540m620s540" "m620s540m620s540m620s1600m620s540m620s1600m620s1600m620s540m620s540" "m620s540m620s1600m620s1600m620s540m620s1600m620s540m620s1600m620s540" @@ -42,7 +43,8 @@ TEST(TestSendGreeUint64, SendData) { irsend.reset(); irsend.sendGree(0x1234567890ABCDEF); EXPECT_EQ( - "m9000s4000" + "f38000d50" + "m9000s4500" "m620s540m620s1600m620s540m620s540m620s1600m620s540m620s540m620s540" "m620s540m620s540m620s1600m620s540m620s1600m620s1600m620s540m620s540" "m620s540m620s1600m620s1600m620s540m620s1600m620s540m620s1600m620s540" @@ -69,7 +71,8 @@ TEST(TestSendGreeChars, SendWithRepeats) { irsend.sendGree(gree_code, kGreeStateLength, 1); EXPECT_EQ( - "m9000s4000" + "f38000d50" + "m9000s4500" "m620s540m620s1600m620s540m620s540m620s1600m620s540m620s540m620s540" "m620s540m620s540m620s1600m620s540m620s1600m620s1600m620s540m620s540" "m620s540m620s1600m620s1600m620s540m620s1600m620s540m620s1600m620s540" @@ -81,7 +84,7 @@ TEST(TestSendGreeChars, SendWithRepeats) { "m620s1600m620s540m620s1600m620s1600m620s540m620s540m620s1600m620s1600" "m620s1600m620s1600m620s1600m620s1600m620s540m620s1600m620s1600m620s1600" "m620s19000" - "m9000s4000" + "m9000s4500" "m620s540m620s1600m620s540m620s540m620s1600m620s540m620s540m620s540" "m620s540m620s540m620s1600m620s540m620s1600m620s1600m620s540m620s540" "m620s540m620s1600m620s1600m620s540m620s1600m620s540m620s1600m620s540" @@ -103,7 +106,8 @@ TEST(TestSendGreeUint64, SendWithRepeats) { irsend.reset(); irsend.sendGree(0x1234567890ABCDEF, kGreeBits, 1); EXPECT_EQ( - "m9000s4000" + "f38000d50" + "m9000s4500" "m620s540m620s1600m620s540m620s540m620s1600m620s540m620s540m620s540" "m620s540m620s540m620s1600m620s540m620s1600m620s1600m620s540m620s540" "m620s540m620s1600m620s1600m620s540m620s1600m620s540m620s1600m620s540" @@ -115,7 +119,7 @@ TEST(TestSendGreeUint64, SendWithRepeats) { "m620s1600m620s540m620s1600m620s1600m620s540m620s540m620s1600m620s1600" "m620s1600m620s1600m620s1600m620s1600m620s540m620s1600m620s1600m620s1600" "m620s19000" - "m9000s4000" + "m9000s4500" "m620s540m620s1600m620s540m620s540m620s1600m620s540m620s540m620s540" "m620s540m620s540m620s1600m620s540m620s1600m620s1600m620s540m620s540" "m620s540m620s1600m620s1600m620s540m620s1600m620s540m620s1600m620s540" @@ -146,7 +150,8 @@ TEST(TestSendGreeChars, SendUnexpectedSizes) { irsend.reset(); irsend.sendGree(gree_long_code, kGreeStateLength + 1); ASSERT_EQ( - "m9000s4000" + "f38000d50" + "m9000s4500" "m620s540m620s1600m620s540m620s540m620s1600m620s540m620s540m620s540" "m620s540m620s540m620s1600m620s540m620s1600m620s1600m620s540m620s540" "m620s540m620s1600m620s1600m620s540m620s1600m620s540m620s1600m620s540" @@ -486,7 +491,7 @@ TEST(TestDecodeGree, NormalSynthetic) { EXPECT_STATE_EQ(gree_code, irsend.capture.state, kGreeBits); } -// Decode a synthetic Gree message. +// Decode a real Gree message. TEST(TestDecodeGree, NormalRealExample) { IRsendTest irsend(4); IRrecv irrecv(4); diff --git a/lib/IRremoteESP8266-2.5.2.03/test/ir_Haier_test.cpp b/lib/IRremoteESP8266-2.6.0/test/ir_Haier_test.cpp similarity index 90% rename from lib/IRremoteESP8266-2.5.2.03/test/ir_Haier_test.cpp rename to lib/IRremoteESP8266-2.6.0/test/ir_Haier_test.cpp index 11848e00a3ea..8d306cb0bb98 100644 --- a/lib/IRremoteESP8266-2.5.2.03/test/ir_Haier_test.cpp +++ b/lib/IRremoteESP8266-2.6.0/test/ir_Haier_test.cpp @@ -19,6 +19,7 @@ TEST(TestSendHaierAC, SendDataOnly) { irsend.reset(); irsend.sendHaierAC(haier_zero); EXPECT_EQ( + "f38000d50" "m3000s3000m3000s4300" "m520s650m520s650m520s650m520s650m520s650m520s650m520s650m520s650" "m520s650m520s650m520s650m520s650m520s650m520s650m520s650m520s650" @@ -37,6 +38,7 @@ TEST(TestSendHaierAC, SendDataOnly) { irsend.reset(); irsend.sendHaierAC(haier_test); EXPECT_EQ( + "f38000d50" "m3000s3000m3000s4300" "m520s1650m520s650m520s1650m520s650m520s650m520s1650m520s650m520s1650" "m520s650m520s650m520s650m520s650m520s650m520s650m520s650m520s1650" @@ -62,6 +64,7 @@ TEST(TestSendHaierAC, SendWithRepeats) { irsend.reset(); irsend.sendHaierAC(haier_test, kHaierACStateLength, 2); // two repeats. EXPECT_EQ( + "f38000d50" "m3000s3000m3000s4300" "m520s1650m520s650m520s1650m520s650m520s650m520s1650m520s650m520s1650" "m520s650m520s650m520s650m520s650m520s650m520s650m520s650m520s1650" @@ -404,7 +407,7 @@ TEST(TestHaierACClass, MessageConstuction) { haier.toString()); uint8_t expectedState[kHaierACStateLength] = {0xA5, 0x96, 0xEA, 0xCF, 0x32, - 0x2D, 0x0D, 0x74, 0xD4}; + 0xED, 0x2D, 0x74, 0xB4}; EXPECT_STATE_EQ(expectedState, haier.getRaw(), kHaierACBits); // Check that the checksum is valid. @@ -987,3 +990,91 @@ TEST(TestDecodeHaierAC_YRW02, RealExample) { " Health: On", haier.toString()); } + +// Default state of the remote needed to include hidden data. +// Ref: https://github.com/markszabo/IRremoteESP8266/issues/668 +TEST(TestHaierAcIssues, Issue668) { + IRHaierAC ac(0); + IRHaierAC acText(1); + IRrecv irrecv(0); + ac.begin(); + + // Turn on the AC. + ac._irsend.reset(); + char expected_on[] = + "Command: 1 (On), Mode: 0 (AUTO), Temp: 25C, Fan: 0 (AUTO), " + "Swing: 0 (Off), Sleep: Off, Health: Off, Current Time: 00:00, " + "On Timer: Off, Off Timer: Off"; + // State taken from real capture: + // https://github.com/markszabo/IRremoteESP8266/issues/668#issuecomment-483531895 + uint8_t expected_on_state[9] = { + 0xA5, 0x91, 0x20, 0x00, 0x0C, 0xC0, 0x20, 0x00, 0x42}; + ac.setCommand(kHaierAcCmdOn); + EXPECT_EQ(expected_on, ac.toString()); + ac.send(); + ac._irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&ac._irsend.capture)); + ASSERT_EQ(HAIER_AC, ac._irsend.capture.decode_type); + EXPECT_EQ(kHaierACBits, ac._irsend.capture.bits); + EXPECT_STATE_EQ(expected_on_state, + ac._irsend.capture.state, ac._irsend.capture.bits); + acText.setRaw(ac._irsend.capture.state); + EXPECT_EQ(expected_on, acText.toString()); + + // Set the temp to 25C + ac._irsend.reset(); + ac.setTemp(25); + EXPECT_EQ(expected_on, ac.toString()); + ASSERT_EQ(25, ac.getTemp()); + ac.send(); + ac._irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&ac._irsend.capture)); + ASSERT_EQ(HAIER_AC, ac._irsend.capture.decode_type); + EXPECT_EQ(kHaierACBits, ac._irsend.capture.bits); + EXPECT_STATE_EQ(expected_on_state, + ac._irsend.capture.state, ac._irsend.capture.bits); + acText.setRaw(ac._irsend.capture.state); + EXPECT_EQ(expected_on, acText.toString()); + + // Increase the temp by 1. + ac._irsend.reset(); + char expected_temp_plus_one[] = + "Command: 6 (Temp Up), Mode: 0 (AUTO), Temp: 26C, Fan: 0 (AUTO), " + "Swing: 0 (Off), Sleep: Off, Health: Off, Current Time: 00:00, " + "On Timer: Off, Off Timer: Off"; + // State taken from real capture: + // https://github.com/markszabo/IRremoteESP8266/issues/668#issuecomment-483531895 + uint8_t expected_temp_plus_one_state[9] = { + 0xA5, 0xA6, 0x20, 0x00, 0x0C, 0xC0, 0x20, 0x00, 0x57}; + ASSERT_EQ(25, ac.getTemp()); + ac.setTemp(ac.getTemp() + 1); + ASSERT_EQ(26, ac.getTemp()); + EXPECT_EQ(expected_temp_plus_one, ac.toString()); + ac.send(); + ac._irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&ac._irsend.capture)); + ASSERT_EQ(HAIER_AC, ac._irsend.capture.decode_type); + EXPECT_EQ(kHaierACBits, ac._irsend.capture.bits); + EXPECT_STATE_EQ(expected_temp_plus_one_state, + ac._irsend.capture.state, ac._irsend.capture.bits); + acText.setRaw(ac._irsend.capture.state); + EXPECT_EQ(expected_temp_plus_one, acText.toString()); + + // Decrease the temp by 1. + ac._irsend.reset(); + char expected_temp_minus_one[] = + "Command: 7 (Temp Down), Mode: 0 (AUTO), Temp: 25C, Fan: 0 (AUTO), " + "Swing: 0 (Off), Sleep: Off, Health: Off, Current Time: 00:00, " + "On Timer: Off, Off Timer: Off"; + ASSERT_EQ(26, ac.getTemp()); + ac.setTemp(ac.getTemp() - 1); + ASSERT_EQ(25, ac.getTemp()); + EXPECT_EQ(expected_temp_minus_one, ac.toString()); + ac.send(); + ac._irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&ac._irsend.capture)); + ASSERT_EQ(HAIER_AC, ac._irsend.capture.decode_type); + EXPECT_EQ(kHaierACBits, ac._irsend.capture.bits); + acText.setRaw(ac._irsend.capture.state); + EXPECT_EQ(expected_temp_minus_one, acText.toString()); +} diff --git a/lib/IRremoteESP8266-2.5.2.03/test/ir_Hitachi_test.cpp b/lib/IRremoteESP8266-2.6.0/test/ir_Hitachi_test.cpp similarity index 99% rename from lib/IRremoteESP8266-2.5.2.03/test/ir_Hitachi_test.cpp rename to lib/IRremoteESP8266-2.6.0/test/ir_Hitachi_test.cpp index de0a4a2a186d..a2471c4aa10e 100644 --- a/lib/IRremoteESP8266-2.5.2.03/test/ir_Hitachi_test.cpp +++ b/lib/IRremoteESP8266-2.6.0/test/ir_Hitachi_test.cpp @@ -22,6 +22,7 @@ TEST(TestSendHitachiAC, SendData) { irsend.reset(); irsend.sendHitachiAC(hitachi_code); EXPECT_EQ( + "f38000d50" "m3300s1700" "m400s1250m400s500m400s500m400s500m400s500m400s500m400s500m400s500" "m400s500m400s500m400s500m400s500m400s1250m400s500m400s500m400s500" @@ -68,6 +69,7 @@ TEST(TestSendHitachiAC, SendWithRepeats) { irsend.sendHitachiAC(hitachi_code, kHitachiAcStateLength, 1); EXPECT_EQ( + "f38000d50" "m3300s1700" "m400s1250m400s500m400s500m400s500m400s500m400s500m400s500m400s500" "m400s500m400s500m400s500m400s500m400s1250m400s500m400s500m400s500" @@ -151,6 +153,7 @@ TEST(TestSendHitachiAC, SendUnexpectedSizes) { irsend.reset(); irsend.sendHitachiAC(hitachi_long_code, kHitachiAcStateLength + 1); ASSERT_EQ( + "f38000d50" "m3300s1700" "m400s1250m400s500m400s500m400s500m400s500m400s500m400s500m400s500" "m400s500m400s500m400s500m400s500m400s1250m400s500m400s500m400s500" @@ -511,6 +514,7 @@ TEST(TestSendHitachiAC1, SendData) { irsend.reset(); irsend.sendHitachiAC1(hitachi_code); EXPECT_EQ( + "f38000d50" "m3400s3400" "m400s1250m400s500m400s1250m400s1250m400s500m400s500m400s1250m400s500" "m400s1250m400s500m400s1250m400s500m400s1250m400s1250m400s1250m400s500" @@ -585,6 +589,7 @@ TEST(TestSendHitachiAC2, SendData) { irsend.reset(); irsend.sendHitachiAC2(hitachi_code); EXPECT_EQ( + "f38000d50" "m3300s1700" "m400s1250m400s500m400s500m400s500m400s500m400s500m400s500m400s500" "m400s500m400s500m400s500m400s500m400s1250m400s500m400s500m400s500" diff --git a/lib/IRremoteESP8266-2.5.2.03/test/ir_JVC_test.cpp b/lib/IRremoteESP8266-2.6.0/test/ir_JVC_test.cpp similarity index 99% rename from lib/IRremoteESP8266-2.5.2.03/test/ir_JVC_test.cpp rename to lib/IRremoteESP8266-2.6.0/test/ir_JVC_test.cpp index c899fa8c60a9..3cd99c3aa326 100644 --- a/lib/IRremoteESP8266-2.5.2.03/test/ir_JVC_test.cpp +++ b/lib/IRremoteESP8266-2.6.0/test/ir_JVC_test.cpp @@ -14,6 +14,7 @@ TEST(TestSendJVC, SendDataOnly) { irsend.reset(); irsend.sendJVC(0xC2B8); // JVC VCR Power On. EXPECT_EQ( + "f38000d33" "m8400s4200" "m525s1725m525s1725m525s525m525s525m525s525m525s525m525s1725m525s525" "m525s1725m525s525m525s1725m525s1725m525s1725m525s525m525s525m525s525" @@ -29,6 +30,7 @@ TEST(TestSendJVC, SendWithRepeats) { irsend.reset(); irsend.sendJVC(0xC2B8, kJvcBits, 1); // 1 repeat. EXPECT_EQ( + "f38000d33" "m8400s4200" "m525s1725m525s1725m525s525m525s525m525s525m525s525m525s1725m525s525" "m525s1725m525s525m525s1725m525s1725m525s1725m525s525m525s525m525s525" @@ -39,6 +41,7 @@ TEST(TestSendJVC, SendWithRepeats) { irsend.outputStr()); irsend.sendJVC(0xC2B8, kJvcBits, 2); // 2 repeats. EXPECT_EQ( + "f38000d33" "m8400s4200" "m525s1725m525s1725m525s525m525s525m525s525m525s525m525s1725m525s525" "m525s1725m525s525m525s1725m525s1725m525s1725m525s525m525s525m525s525" @@ -60,6 +63,7 @@ TEST(TestSendJVC, SendUnusualSize) { irsend.reset(); irsend.sendJVC(0x0, 8); EXPECT_EQ( + "f38000d33" "m8400s4200" "m525s525m525s525m525s525m525s525m525s525m525s525m525s525m525s525" "m525s38475", @@ -68,6 +72,7 @@ TEST(TestSendJVC, SendUnusualSize) { irsend.reset(); irsend.sendJVC(0x1234567890ABCDEF, 64); EXPECT_EQ( + "f38000d33" "m8400s4200" "m525s525m525s525m525s525m525s1725m525s525m525s525m525s1725m525s525" "m525s525m525s525m525s1725m525s1725m525s525m525s1725m525s525m525s525" diff --git a/lib/IRremoteESP8266-2.5.2.03/test/ir_Kelvinator_test.cpp b/lib/IRremoteESP8266-2.6.0/test/ir_Kelvinator_test.cpp similarity index 99% rename from lib/IRremoteESP8266-2.5.2.03/test/ir_Kelvinator_test.cpp rename to lib/IRremoteESP8266-2.6.0/test/ir_Kelvinator_test.cpp index 001f8bcf242c..38a298e58baa 100644 --- a/lib/IRremoteESP8266-2.5.2.03/test/ir_Kelvinator_test.cpp +++ b/lib/IRremoteESP8266-2.6.0/test/ir_Kelvinator_test.cpp @@ -21,6 +21,7 @@ TEST(TestSendKelvinator, SendDataOnly) { irsend.reset(); irsend.sendKelvinator(kelv_code); EXPECT_EQ( + "f38000d50" "m9010s4505" "m680s1530m680s510m680s510m680s1530m680s1530m680s510m680s510m680s510" "m680s1530m680s1530m680s510m680s1530m680s510m680s510m680s510m680s510" @@ -61,6 +62,7 @@ TEST(TestSendKelvinator, SendWithRepeats) { irsend.sendKelvinator(kelv_code, kKelvinatorStateLength, 1); EXPECT_EQ( + "f38000d50" "m9010s4505" "m680s1530m680s510m680s510m680s1530m680s1530m680s510m680s510m680s510" "m680s1530m680s1530m680s510m680s1530m680s510m680s510m680s510m680s510" @@ -131,6 +133,7 @@ TEST(TestSendKelvinator, SendUnexpectedSizes) { // extra data. irsend.sendKelvinator(kelv_long_code, 17); ASSERT_EQ( + "f38000d50" "m9010s4505" "m680s1530m680s510m680s510m680s1530m680s1530m680s510m680s510m680s510" "m680s1530m680s1530m680s510m680s1530m680s510m680s510m680s510m680s510" @@ -472,6 +475,7 @@ TEST(TestKelvinatorClass, MessageConstuction) { irsend.reset(); irsend.sendKelvinator(irkelv.getRaw()); EXPECT_EQ( + "f38000d50" "m9010s4505" "m680s1530m680s510m680s510m680s1530m680s1530m680s510m680s1530m680s510" "m680s1530m680s1530m680s510m680s1530m680s510m680s510m680s510m680s510" diff --git a/lib/IRremoteESP8266-2.5.2.03/test/ir_LG_test.cpp b/lib/IRremoteESP8266-2.6.0/test/ir_LG_test.cpp similarity index 88% rename from lib/IRremoteESP8266-2.5.2.03/test/ir_LG_test.cpp rename to lib/IRremoteESP8266-2.6.0/test/ir_LG_test.cpp index 8ab24a7319e8..2925494b94c4 100644 --- a/lib/IRremoteESP8266-2.5.2.03/test/ir_LG_test.cpp +++ b/lib/IRremoteESP8266-2.6.0/test/ir_LG_test.cpp @@ -30,6 +30,7 @@ TEST(TestSendLG, SendDataOnly) { irsend.reset(); irsend.sendLG(0x4B4AE51); EXPECT_EQ( + "f38000d50" "m8500s4250" "m550s550m550s1600m550s550m550s550" "m550s1600m550s550m550s1600m550s1600m550s550m550s1600m550s550m550s550" @@ -41,6 +42,7 @@ TEST(TestSendLG, SendDataOnly) { irsend.reset(); irsend.sendLG(0xB4B4AE51, kLg32Bits); EXPECT_EQ( + "f38000d33" "m4480s4480" "m560s1680m560s560m560s1680m560s1680m560s560m560s1680m560s560m560s560" "m560s1680m560s560m560s1680m560s1680m560s560m560s1680m560s560m560s560" @@ -59,6 +61,7 @@ TEST(TestSendLG, SendWithRepeats) { irsend.reset(); irsend.sendLG(0x4B4AE51, kLgBits, 1); EXPECT_EQ( + "f38000d50" "m8500s4250" "m550s550m550s1600m550s550m550s550" "m550s1600m550s550m550s1600m550s1600m550s550m550s1600m550s550m550s550" @@ -71,6 +74,7 @@ TEST(TestSendLG, SendWithRepeats) { irsend.reset(); irsend.sendLG(0xB4B4AE51, kLg32Bits, 1); EXPECT_EQ( + "f38000d33" "m4480s4480" "m560s1680m560s560m560s1680m560s1680m560s560m560s1680m560s560m560s560" "m560s1680m560s560m560s1680m560s1680m560s560m560s1680m560s560m560s560" @@ -90,6 +94,7 @@ TEST(TestSendLG, SendUnusualSize) { irsend.reset(); irsend.sendLG(0x0, 31); EXPECT_EQ( + "f38000d50" "m8500s4250" "m550s550m550s550m550s550m550s550m550s550m550s550m550s550m550s550" "m550s550m550s550m550s550m550s550m550s550m550s550m550s550m550s550" @@ -101,6 +106,7 @@ TEST(TestSendLG, SendUnusualSize) { irsend.reset(); irsend.sendLG(0x0, 64); EXPECT_EQ( + "f38000d33" "m4480s4480" "m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560" "m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560" @@ -373,6 +379,7 @@ TEST(TestSendLG2, SendDataOnly) { irsend.reset(); irsend.sendLG2(0x880094D); EXPECT_EQ( + "f38000d50" "m3200s9850" "m550s1600m550s550m550s550m550s550m550s1600m550s550m550s550m550s550" "m550s550m550s550m550s550m550s550m550s550m550s550m550s550m550s550" @@ -420,3 +427,54 @@ TEST(TestDecodeLG2, RealLG2Example) { EXPECT_EQ(kLgBits, irsend.capture.bits); EXPECT_EQ(0x880094D, irsend.capture.value); } + +// Tests for issue reported in +// https://github.com/markszabo/IRremoteESP8266/issues/620 +TEST(TestDecodeLG, Issue620) { + IRsendTest irsend(0); + IRrecv irrecv(0); + irsend.begin(); + + // Raw data as reported in initial comment of Issue #620 + uint16_t rawData[59] = { + 8886, 4152, + 560, 1538, 532, 502, 532, 504, 530, 484, 558, 1536, + 508, 516, 558, 502, 532, 484, 558, 502, 532, 500, + 534, 508, 532, 502, 532, 1518, 558, 510, 532, 484, + 556, 486, 556, 510, 532, 1518, 558, 1560, 532, 1528, + 556, 504, 530, 506, 530, 1520, 558, 508, 534, 500, + 532, 512, 530, 484, 556, 1536, 532}; // LG 8808721 + irsend.sendRaw(rawData, 59, 38000); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(LG, irsend.capture.decode_type); + EXPECT_EQ(28, irsend.capture.bits); + EXPECT_EQ(0x8808721, irsend.capture.value); + EXPECT_EQ(0x88, irsend.capture.address); + EXPECT_EQ(0x872, irsend.capture.command); + + irsend.reset(); + + // Resend the same code as the report is a sent code doesn't decode + // to the same message code. + irsend.sendLG(0x8808721); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(LG, irsend.capture.decode_type); + EXPECT_EQ(28, irsend.capture.bits); + EXPECT_EQ(0x8808721, irsend.capture.value); + EXPECT_EQ(0x88, irsend.capture.address); + EXPECT_EQ(0x872, irsend.capture.command); + // The following seems to match the rawData above. + EXPECT_EQ( + "f38000d50" + "m8500s4250" + "m550s1600m550s550m550s550m550s550m550s1600" + "m550s550m550s550m550s550m550s550m550s550" + "m550s550m550s550m550s1600m550s550m550s550" + "m550s550m550s550m550s1600m550s1600m550s1600" + "m550s550m550s550m550s1600m550s550m550s550" + "m550s550m550s550m550s1600m550" + "s55550", + irsend.outputStr()); +} diff --git a/lib/IRremoteESP8266-2.5.2.03/test/ir_Lasertag_test.cpp b/lib/IRremoteESP8266-2.6.0/test/ir_Lasertag_test.cpp similarity index 98% rename from lib/IRremoteESP8266-2.5.2.03/test/ir_Lasertag_test.cpp rename to lib/IRremoteESP8266-2.6.0/test/ir_Lasertag_test.cpp index 041109fb8e59..bad724f76d16 100644 --- a/lib/IRremoteESP8266-2.5.2.03/test/ir_Lasertag_test.cpp +++ b/lib/IRremoteESP8266-2.6.0/test/ir_Lasertag_test.cpp @@ -20,6 +20,7 @@ TEST(TestSendLasertag, SendDataOnly) { irsend.reset(); irsend.sendLasertag(0x1); // Red 1 EXPECT_EQ( + "f36000d25" "m333s333m333s333m333s333m333s333m333s333m333s333m333s333m333s333m333" "s333m333s333m333s333m333s666m333s100000", irsend.outputStr()); @@ -27,6 +28,7 @@ TEST(TestSendLasertag, SendDataOnly) { irsend.reset(); irsend.sendLasertag(0x2); // Red 2 EXPECT_EQ( + "f36000d25" "m333s333m333s333m333s333m333s333m333s333m333s333m333s333" "m333s333m333s333m333s333m333s666m666s100333", irsend.outputStr()); @@ -38,6 +40,7 @@ TEST(TestSendLasertag, SendDataOnly) { EXPECT_EQ( // m364s364m332s336m384s276m332s364m332s304m416s584 // m692s724m640s360m304s332m392s612m380 + "f36000d25" "m333s333m333s333m333s333m333s333m333s333m333s666" "m666s666m666s333m333s333m333s666m333s100000", irsend.outputStr()); @@ -49,6 +52,7 @@ TEST(TestSendLasertag, SendDataOnly) { EXPECT_EQ( // m332s308m412s280m360s336m332s304m444s248m332s644 // m744s612m696s692m668s636m360 + "f36000d25" "m333s333m333s333m333s333m333s333m333s333m333s666" "m666s666m666s666m666s666m333s100000", irsend.outputStr()); @@ -61,6 +65,7 @@ TEST(TestSendLasertag, SendDataWithRepeat) { irsend.reset(); irsend.sendLasertag(0x1, kLasertagBits, 1); // Red 1, one repeat. EXPECT_EQ( + "f36000d25" "m333s333m333s333m333s333m333s333m333s333m333s333m333s333m333s333" "m333s333m333s333m333s333m333s666m333s100000" "m333s333m333s333m333s333m333s333m333s333m333s333m333s333m333s333" @@ -70,6 +75,7 @@ TEST(TestSendLasertag, SendDataWithRepeat) { irsend.reset(); irsend.sendLasertag(0x52, kLasertagBits, 2); // Green 2, two repeats. EXPECT_EQ( + "f36000d25" "m333s333m333s333m333s333m333s333m333s333m333s666m666s666m666s333" "m333s666m666s100333" "m333s333m333s333m333s333m333s333m333s333m333s666m666s666m666s333" @@ -86,7 +92,8 @@ TEST(TestSendLasertag, SmallestMessageSize) { irsend.reset(); irsend.sendLasertag(0x1555); // Alternating bit pattern will be the smallest. // i.e. 7 actual 'mark' pulses, which is a rawlen of 13. - EXPECT_EQ("m0s333m666s666m666s666m666s666m666s666m666s666m666s666m333s100000", + EXPECT_EQ("f36000d25" + "m0s333m666s666m666s666m666s666m666s666m666s666m666s666m333s100000", irsend.outputStr()); } diff --git a/lib/IRremoteESP8266-2.6.0/test/ir_Lego_test.cpp b/lib/IRremoteESP8266-2.6.0/test/ir_Lego_test.cpp new file mode 100644 index 000000000000..4e859b170878 --- /dev/null +++ b/lib/IRremoteESP8266-2.6.0/test/ir_Lego_test.cpp @@ -0,0 +1,196 @@ +// Copyright 2019 David Conran + +#include "IRrecv.h" +#include "IRrecv_test.h" +#include "IRsend.h" +#include "IRsend_test.h" +#include "gtest/gtest.h" + +// General housekeeping +TEST(TestLego, Housekeeping) { + ASSERT_EQ("LEGOPF", typeToString(LEGOPF)); + ASSERT_FALSE(hasACState(LEGOPF)); // Uses uint64_t, not uint8_t*. +} + +// Tests for sendLego(). + +// Test sending typical data only. +TEST(TestSendLegoPf, SendDataOnly) { + IRsendTest irsend(0); + irsend.begin(); + + irsend.reset(); + irsend.sendLegoPf(0x1234); + EXPECT_EQ( + "f38000d50" + "m158s1026" + "m158s263m158s263m158s263m158s553m158s263m158s263m158s553m158s263" + "m158s263m158s263m158s553m158s553m158s263m158s553m158s263m158s263" + "m158s70472", irsend.outputStr()); + + irsend.reset(); + irsend.send(LEGOPF, 0x1234, kLegoPfBits); + EXPECT_EQ( + "f38000d50" + "m158s1026" + "m158s263m158s263m158s263m158s553m158s263m158s263m158s553m158s263" + "m158s263m158s263m158s553m158s553m158s263m158s553m158s263m158s263" + "m158s70472", irsend.outputStr()); +} + +// Test sending typical repeat data. +TEST(TestSendLegoPf, SendDataWithRepeats) { + IRsendTest irsend(0); + irsend.begin(); + + irsend.reset(); + irsend.sendLegoPf(0x1234, kLegoPfBits, 1); + EXPECT_EQ( + "f38000d50" + "m0s32000" + "m158s1026" + "m158s263m158s263m158s263m158s553m158s263m158s263m158s553m158s263" + "m158s263m158s263m158s553m158s553m158s263m158s553m158s263m158s263" + "m158s70472" + "m158s1026" + "m158s263m158s263m158s263m158s553m158s263m158s263m158s553m158s263" + "m158s263m158s263m158s553m158s553m158s263m158s553m158s263m158s263" + "m158s70472" + "m158s1026" + "m158s263m158s263m158s263m158s553m158s263m158s263m158s553m158s263" + "m158s263m158s263m158s553m158s553m158s263m158s553m158s263m158s263" + "m158s150472" + "m158s1026" + "m158s263m158s263m158s263m158s553m158s263m158s263m158s553m158s263" + "m158s263m158s263m158s553m158s553m158s263m158s553m158s263m158s263" + "m158s150472" + "m158s1026" + "m158s263m158s263m158s263m158s553m158s263m158s263m158s553m158s263" + "m158s263m158s263m158s553m158s553m158s263m158s553m158s263m158s263" + "m158s150472", irsend.outputStr()); + + irsend.reset(); + irsend.sendLegoPf(0x2345, kLegoPfBits, 2); + EXPECT_EQ( + "f38000d50" + "m0s16000" + "m158s1026" + "m158s263m158s263m158s553m158s263m158s263m158s263m158s553m158s553" + "m158s263m158s553m158s263m158s263m158s263m158s553m158s263m158s553" + "m158s70182" + "m158s1026" + "m158s263m158s263m158s553m158s263m158s263m158s263m158s553m158s553" + "m158s263m158s553m158s263m158s263m158s263m158s553m158s263m158s553" + "m158s70182" + "m158s1026" + "m158s263m158s263m158s553m158s263m158s263m158s263m158s553m158s553" + "m158s263m158s553m158s263m158s263m158s263m158s553m158s263m158s553" + "m158s182182" + "m158s1026" + "m158s263m158s263m158s553m158s263m158s263m158s263m158s553m158s553" + "m158s263m158s553m158s263m158s263m158s263m158s553m158s263m158s553" + "m158s182182" + "m158s1026" + "m158s263m158s263m158s553m158s263m158s263m158s263m158s553m158s553" + "m158s263m158s553m158s263m158s263m158s263m158s553m158s263m158s553" + "m158s182182", irsend.outputStr()); + + irsend.reset(); + irsend.sendLegoPf(0x3456, kLegoPfBits, 7); + EXPECT_EQ( + "f38000d50" + "m158s1026" + "m158s263m158s263m158s553m158s553m158s263m158s553m158s263m158s263" + "m158s263m158s553m158s263m158s553m158s263m158s553m158s553m158s263" + "m158s69892" + "m158s1026" + "m158s263m158s263m158s553m158s553m158s263m158s553m158s263m158s263" + "m158s263m158s553m158s263m158s553m158s263m158s553m158s553m158s263" + "m158s69892" + "m158s1026" + "m158s263m158s263m158s553m158s553m158s263m158s553m158s263m158s263" + "m158s263m158s553m158s263m158s553m158s263m158s553m158s553m158s263" + "m158s213892" + "m158s1026" + "m158s263m158s263m158s553m158s553m158s263m158s553m158s263m158s263" + "m158s263m158s553m158s263m158s553m158s263m158s553m158s553m158s263" + "m158s213892" + "m158s1026" + "m158s263m158s263m158s553m158s553m158s263m158s553m158s263m158s263" + "m158s263m158s553m158s263m158s553m158s263m158s553m158s553m158s263" + "m158s213892" + "m158s1026" + "m158s263m158s263m158s553m158s553m158s263m158s553m158s263m158s263" + "m158s263m158s553m158s263m158s553m158s263m158s553m158s553m158s263" + "m158s213892" + "m158s1026" + "m158s263m158s263m158s553m158s553m158s263m158s553m158s263m158s263" + "m158s263m158s553m158s263m158s553m158s263m158s553m158s553m158s263" + "m158s213892", irsend.outputStr()); +} + +// Tests for decodeLego(). + +// Decode normal "synthetic" messages. +TEST(TestDecodeLegoPf, SyntheticDecode) { + IRsendTest irsend(0); + IRrecv irrecv(0); + irsend.begin(); + + irsend.reset(); + irsend.sendLegoPf(0x000F); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(LEGOPF, irsend.capture.decode_type); + EXPECT_EQ(kLegoPfBits, irsend.capture.bits); + EXPECT_FALSE(irsend.capture.repeat); + EXPECT_EQ(0x000F, irsend.capture.value); + EXPECT_EQ(1, irsend.capture.address); + EXPECT_EQ(0, irsend.capture.command); + + irsend.reset(); + irsend.sendLegoPf(0x100E); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(LEGOPF, irsend.capture.decode_type); + EXPECT_EQ(kLegoPfBits, irsend.capture.bits); + EXPECT_FALSE(irsend.capture.repeat); + EXPECT_EQ(0x100E, irsend.capture.value); + EXPECT_EQ(2, irsend.capture.address); + EXPECT_EQ(0, irsend.capture.command); + + irsend.reset(); + irsend.sendLegoPf(0x221E); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(LEGOPF, irsend.capture.decode_type); + EXPECT_EQ(kLegoPfBits, irsend.capture.bits); + EXPECT_FALSE(irsend.capture.repeat); + EXPECT_EQ(0x221E, irsend.capture.value); + EXPECT_EQ(3, irsend.capture.address); + EXPECT_EQ(0x21, irsend.capture.command); + + // Test a bad LRC is not matched. + irsend.reset(); + irsend.sendLegoPf(0x001F); // LRC should be 0xE, not 0xF. + irsend.makeDecodeResult(); + irrecv.decode(&irsend.capture); + EXPECT_NE(LEGOPF, irsend.capture.decode_type); +} + +// Decode normal "synthetic" message with releats. +TEST(TestDecodeLegoPf, SyntheticDecodeWithRepeat) { + IRsendTest irsend(0); + IRrecv irrecv(0); + irsend.begin(); + + irsend.reset(); + irsend.sendLegoPf(0x330F, kLegoPfBits, 1); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(LEGOPF, irsend.capture.decode_type); + EXPECT_EQ(kLegoPfBits, irsend.capture.bits); + EXPECT_EQ(0x330F, irsend.capture.value); + EXPECT_EQ(4, irsend.capture.address); + EXPECT_EQ(0x30, irsend.capture.command); +} diff --git a/lib/IRremoteESP8266-2.5.2.03/test/ir_Lutron_test.cpp b/lib/IRremoteESP8266-2.6.0/test/ir_Lutron_test.cpp similarity index 95% rename from lib/IRremoteESP8266-2.5.2.03/test/ir_Lutron_test.cpp rename to lib/IRremoteESP8266-2.6.0/test/ir_Lutron_test.cpp index 6c99b9904630..d682967ca4ef 100644 --- a/lib/IRremoteESP8266-2.5.2.03/test/ir_Lutron_test.cpp +++ b/lib/IRremoteESP8266-2.6.0/test/ir_Lutron_test.cpp @@ -11,17 +11,19 @@ TEST(TestSendLutron, SendDataOnly) { IRsendTest irsend(0); irsend.begin(); irsend.sendLutron(0); - EXPECT_EQ("m2288s230080", irsend.outputStr()); + EXPECT_EQ("f40000d40m2288s230080", irsend.outputStr()); irsend.sendLutron(0xAAAAAAAAA); // Longest possible sequence. (I think) EXPECT_EQ( + "f40000d40" "m2288s2288m2288s2288m2288s2288m2288s2288m2288s2288m2288s2288m2288s2288" "m2288s2288m2288s2288m2288s2288m2288s2288m2288s2288m2288s2288m2288s2288" "m2288s2288m2288s2288m2288s2288m2288s152288", irsend.outputStr()); irsend.sendLutron(0x7FFFFFFFF); - EXPECT_EQ("m82368s150000", irsend.outputStr()); + EXPECT_EQ("f40000d40m82368s150000", irsend.outputStr()); irsend.sendLutron(0x7F88BD120); EXPECT_EQ( + "f40000d40" "m20592s6864m2288s6864m2288s2288m9152s2288m2288s6864m2288s4576m2288" "s161440", irsend.outputStr()); @@ -34,12 +36,14 @@ TEST(TestSendLutron, SendWithRepeats) { // Send a command with 0 repeats. irsend.sendLutron(0x7F88BD120, kLutronBits, 0); EXPECT_EQ( + "f40000d40" "m20592s6864m2288s6864m2288s2288m9152s2288m2288s6864m2288s4576m2288" "s161440", irsend.outputStr()); // Send a command with 1 repeat. irsend.sendLutron(0x7F88BD120, kLutronBits, 1); EXPECT_EQ( + "f40000d40" "m20592s6864m2288s6864m2288s2288m9152s2288m2288s6864m2288s4576m2288" "s161440" "m20592s6864m2288s6864m2288s2288m9152s2288m2288s6864m2288s4576m2288" @@ -48,6 +52,7 @@ TEST(TestSendLutron, SendWithRepeats) { // Send a command with 3 repeats. irsend.sendLutron(0x7F88BD120, kLutronBits, 3); EXPECT_EQ( + "f40000d40" "m20592s6864m2288s6864m2288s2288m9152s2288m2288s6864m2288s4576m2288" "s161440" "m20592s6864m2288s6864m2288s2288m9152s2288m2288s6864m2288s4576m2288" diff --git a/lib/IRremoteESP8266-2.5.2.03/test/ir_MWM_test.cpp b/lib/IRremoteESP8266-2.6.0/test/ir_MWM_test.cpp similarity index 99% rename from lib/IRremoteESP8266-2.5.2.03/test/ir_MWM_test.cpp rename to lib/IRremoteESP8266-2.6.0/test/ir_MWM_test.cpp index 9ecd0eac146a..2ca69ac833cd 100644 --- a/lib/IRremoteESP8266-2.5.2.03/test/ir_MWM_test.cpp +++ b/lib/IRremoteESP8266-2.6.0/test/ir_MWM_test.cpp @@ -36,6 +36,7 @@ TEST(TestSendMWM, SendDataOnly) { */ irsend.sendMWM(test1, sizeof(test1), 0); EXPECT_EQ( + "f38000d25" "m834s834m417s417m834s834" "m417s417m834s834m1251s417" "m2085s417m1251s417" @@ -65,6 +66,7 @@ TEST(TestSendMWM, SendDataOnly) { }; irsend.sendMWM(test2, sizeof(test2), 0); EXPECT_EQ( + "f38000d25" "m417s417m834s834m834s834" "m834s834m834s417m834s417" "m834s834m834s834m417s417" diff --git a/lib/IRremoteESP8266-2.5.2.03/test/ir_Magiquest_test.cpp b/lib/IRremoteESP8266-2.6.0/test/ir_Magiquest_test.cpp similarity index 99% rename from lib/IRremoteESP8266-2.5.2.03/test/ir_Magiquest_test.cpp rename to lib/IRremoteESP8266-2.6.0/test/ir_Magiquest_test.cpp index e1c3da83dede..bbc5f3366379 100644 --- a/lib/IRremoteESP8266-2.5.2.03/test/ir_Magiquest_test.cpp +++ b/lib/IRremoteESP8266-2.6.0/test/ir_Magiquest_test.cpp @@ -26,6 +26,7 @@ TEST(TestSendMagiQuest, SendDataOnly) { irsend.reset(); irsend.sendMagiQuest(0x0); EXPECT_EQ( + "f36000d50" "m280s850m280s850m280s850m280s850m280s850m280s850m280s850m280s850" "m280s850m280s850m280s850m280s850m280s850m280s850m280s850m280s850" "m280s850m280s850m280s850m280s850m280s850m280s850m280s850m280s850" @@ -37,6 +38,7 @@ TEST(TestSendMagiQuest, SendDataOnly) { irsend.reset(); irsend.sendMagiQuest(0x123456789ABC); EXPECT_EQ( + "f36000d50" "m280s850m280s850m280s850m280s850m280s850m280s850m280s850m280s850" "m280s850m280s850m280s850m580s600m280s850m280s850m580s600m280s850" "m280s850m280s850m580s600m580s600m280s850m580s600m280s850m280s850" @@ -55,6 +57,7 @@ TEST(TestSendMagiQuest, SendWithRepeats) { irsend.reset(); irsend.sendMagiQuest(0x12345678ABCD, kMagiquestBits, 2); // two repeats. EXPECT_EQ( + "f36000d50" "m280s850m280s850m280s850m280s850m280s850m280s850m280s850m280s850" "m280s850m280s850m280s850m580s600m280s850m280s850m580s600m280s850" "m280s850m280s850m580s600m580s600m280s850m580s600m280s850m280s850" diff --git a/lib/IRremoteESP8266-2.5.2.03/test/ir_Midea_test.cpp b/lib/IRremoteESP8266-2.6.0/test/ir_Midea_test.cpp similarity index 99% rename from lib/IRremoteESP8266-2.5.2.03/test/ir_Midea_test.cpp rename to lib/IRremoteESP8266-2.6.0/test/ir_Midea_test.cpp index 5d5f5e932f44..ced3ea10cec9 100644 --- a/lib/IRremoteESP8266-2.5.2.03/test/ir_Midea_test.cpp +++ b/lib/IRremoteESP8266-2.6.0/test/ir_Midea_test.cpp @@ -15,6 +15,7 @@ TEST(TestSendMidea, SendDataOnly) { irsend.reset(); irsend.sendMidea(0x0); EXPECT_EQ( + "f38000d50" "m4480s4480" "m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560" "m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560" @@ -36,6 +37,7 @@ TEST(TestSendMidea, SendDataOnly) { irsend.reset(); irsend.sendMidea(0x55AA55AA55AA); EXPECT_EQ( + "f38000d50" "m4480s4480" "m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680" "m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560" @@ -57,6 +59,7 @@ TEST(TestSendMidea, SendDataOnly) { irsend.reset(); irsend.sendMidea(0xFFFFFFFFFFFF); EXPECT_EQ( + "f38000d50" "m4480s4480" "m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680" "m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680" @@ -84,6 +87,7 @@ TEST(TestSendMidea, SendWithRepeats) { irsend.reset(); irsend.sendMidea(0x55AA55AA55AA, kMideaBits, 1); // 1 repeat. EXPECT_EQ( + "f38000d50" "m4480s4480" "m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680" "m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560" @@ -119,6 +123,7 @@ TEST(TestSendMidea, SendWithRepeats) { irsend.outputStr()); irsend.sendMidea(0x55AA55AA55AA, kMideaBits, 2); // 2 repeats. EXPECT_EQ( + "f38000d50" "m4480s4480" "m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680" "m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560" @@ -178,6 +183,7 @@ TEST(TestSendMidea, SendUnusualSize) { irsend.reset(); irsend.sendMidea(0x0, 8); EXPECT_EQ( + "f38000d50" "m4480s4480" "m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560" "m560s5600" @@ -189,6 +195,7 @@ TEST(TestSendMidea, SendUnusualSize) { irsend.reset(); irsend.sendMidea(0x1234567890ABCDEF, 64); EXPECT_EQ( + "f38000d50" "m4480s4480" "m560s560m560s560m560s560m560s1680m560s560m560s560m560s1680m560s560" "m560s560m560s560m560s1680m560s1680m560s560m560s1680m560s560m560s560" diff --git a/lib/IRremoteESP8266-2.6.0/test/ir_MitsubishiHeavy_test.cpp b/lib/IRremoteESP8266-2.6.0/test/ir_MitsubishiHeavy_test.cpp new file mode 100644 index 000000000000..340a04078246 --- /dev/null +++ b/lib/IRremoteESP8266-2.6.0/test/ir_MitsubishiHeavy_test.cpp @@ -0,0 +1,851 @@ +// Copyright 2019 David Conran + +#include "ir_MitsubishiHeavy.h" +#include "IRrecv.h" +#include "IRrecv_test.h" +#include "IRremoteESP8266.h" +#include "IRsend.h" +#include "IRsend_test.h" +#include "gtest/gtest.h" + +// General housekeeping +TEST(TestMitsubishiHeavy, Housekeeping) { + ASSERT_EQ("MITSUBISHI_HEAVY_88", typeToString(MITSUBISHI_HEAVY_88)); + ASSERT_TRUE(hasACState(MITSUBISHI_HEAVY_88)); + ASSERT_EQ("MITSUBISHI_HEAVY_152", typeToString(MITSUBISHI_HEAVY_152)); + ASSERT_TRUE(hasACState(MITSUBISHI_HEAVY_152)); +} + +// Tests for IRMitsubishiHeavy152Ac class. + +TEST(TestMitsubishiHeavy152AcClass, Power) { + IRMitsubishiHeavy152Ac ac(0); + ac.begin(); + + ac.on(); + EXPECT_TRUE(ac.getPower()); + + ac.off(); + EXPECT_FALSE(ac.getPower()); + + ac.setPower(true); + EXPECT_TRUE(ac.getPower()); + + ac.setPower(false); + EXPECT_FALSE(ac.getPower()); +} + +TEST(TestMitsubishiHeavy152AcClass, Temperature) { + IRMitsubishiHeavy152Ac ac(0); + ac.begin(); + + ac.setMode(kMitsubishiHeavyCool); + + ac.setTemp(0); + EXPECT_EQ(kMitsubishiHeavyMinTemp, ac.getTemp()); + + ac.setTemp(255); + EXPECT_EQ(kMitsubishiHeavyMaxTemp, ac.getTemp()); + + ac.setTemp(kMitsubishiHeavyMinTemp); + EXPECT_EQ(kMitsubishiHeavyMinTemp, ac.getTemp()); + + ac.setTemp(kMitsubishiHeavyMaxTemp); + EXPECT_EQ(kMitsubishiHeavyMaxTemp, ac.getTemp()); + + ac.setTemp(kMitsubishiHeavyMinTemp - 1); + EXPECT_EQ(kMitsubishiHeavyMinTemp, ac.getTemp()); + + ac.setTemp(kMitsubishiHeavyMaxTemp + 1); + EXPECT_EQ(kMitsubishiHeavyMaxTemp, ac.getTemp()); + + ac.setTemp(19); + EXPECT_EQ(19, ac.getTemp()); + + ac.setTemp(21); + EXPECT_EQ(21, ac.getTemp()); + + ac.setTemp(25); + EXPECT_EQ(25, ac.getTemp()); + + ac.setTemp(29); + EXPECT_EQ(29, ac.getTemp()); +} + +TEST(TestMitsubishiHeavy152AcClass, OperatingMode) { + IRMitsubishiHeavy152Ac ac(0); + ac.begin(); + + ac.setMode(kMitsubishiHeavyAuto); + EXPECT_EQ(kMitsubishiHeavyAuto, ac.getMode()); + + ac.setMode(kMitsubishiHeavyCool); + EXPECT_EQ(kMitsubishiHeavyCool, ac.getMode()); + + ac.setMode(kMitsubishiHeavyHeat); + EXPECT_EQ(kMitsubishiHeavyHeat, ac.getMode()); + + ac.setMode(kMitsubishiHeavyDry); + EXPECT_EQ(kMitsubishiHeavyDry, ac.getMode()); + + ac.setMode(kMitsubishiHeavyFan); + EXPECT_EQ(kMitsubishiHeavyFan, ac.getMode()); + + ac.setMode(kMitsubishiHeavyHeat + 1); + EXPECT_EQ(kMitsubishiHeavyAuto, ac.getMode()); + + ac.setMode(255); + EXPECT_EQ(kMitsubishiHeavyAuto, ac.getMode()); +} + + +TEST(TestMitsubishiHeavy152AcClass, Filter) { + IRMitsubishiHeavy152Ac ac(0); + ac.begin(); + + ac.setFilter(true); + EXPECT_TRUE(ac.getFilter()); + + ac.setFilter(false); + EXPECT_FALSE(ac.getFilter()); + + ac.setFilter(true); + EXPECT_TRUE(ac.getFilter()); +} + +TEST(TestMitsubishiHeavy152AcClass, Turbo) { + IRMitsubishiHeavy152Ac ac(0); + ac.begin(); + + ac.setTurbo(true); + EXPECT_TRUE(ac.getTurbo()); + + ac.setTurbo(false); + EXPECT_FALSE(ac.getTurbo()); + + ac.setTurbo(true); + EXPECT_TRUE(ac.getTurbo()); +} + +TEST(TestMitsubishiHeavy152AcClass, Econo) { + IRMitsubishiHeavy152Ac ac(0); + ac.begin(); + + ac.setEcono(true); + EXPECT_TRUE(ac.getEcono()); + + ac.setEcono(false); + EXPECT_FALSE(ac.getEcono()); + + ac.setEcono(true); + EXPECT_TRUE(ac.getEcono()); +} + +TEST(TestMitsubishiHeavy152AcClass, 3D) { + IRMitsubishiHeavy152Ac ac(0); + ac.begin(); + + ac.set3D(true); + EXPECT_TRUE(ac.get3D()); + + ac.set3D(false); + EXPECT_FALSE(ac.get3D()); + + ac.set3D(true); + EXPECT_TRUE(ac.get3D()); +} + +TEST(TestMitsubishiHeavy152AcClass, Night) { + IRMitsubishiHeavy152Ac ac(0); + ac.begin(); + + ac.setNight(true); + EXPECT_TRUE(ac.getNight()); + + ac.setNight(false); + EXPECT_FALSE(ac.getNight()); + + ac.setNight(true); + EXPECT_TRUE(ac.getNight()); +} + +TEST(TestMitsubishiHeavy152AcClass, Clean) { + IRMitsubishiHeavy152Ac ac(0); + ac.begin(); + + ac.setClean(true); + EXPECT_TRUE(ac.getClean()); + + ac.setClean(false); + EXPECT_FALSE(ac.getClean()); + + ac.setClean(true); + EXPECT_TRUE(ac.getClean()); +} + +TEST(TestMitsubishiHeavy152AcClass, FanSpeed) { + IRMitsubishiHeavy152Ac ac(0); + ac.begin(); + + ac.setFan(kMitsubishiHeavy152FanLow); + EXPECT_EQ(kMitsubishiHeavy152FanLow, ac.getFan()); + + ac.setFan(kMitsubishiHeavy152FanAuto); + EXPECT_EQ(kMitsubishiHeavy152FanAuto, ac.getFan()); + + + ac.setFan(255); + EXPECT_EQ(kMitsubishiHeavy152FanAuto, ac.getFan()); + + ac.setFan(kMitsubishiHeavy152FanMax); + EXPECT_EQ(kMitsubishiHeavy152FanMax, ac.getFan()); + + ac.setFan(kMitsubishiHeavy152FanMax + 1); + EXPECT_EQ(kMitsubishiHeavy152FanAuto, ac.getFan()); + + ac.setFan(kMitsubishiHeavy152FanMax - 1); + EXPECT_EQ(kMitsubishiHeavy152FanMax - 1, ac.getFan()); + + ac.setFan(kMitsubishiHeavy152FanLow + 1); + EXPECT_EQ(kMitsubishiHeavy152FanLow + 1, ac.getFan()); + + ac.setFan(kMitsubishiHeavy152FanEcono); + EXPECT_EQ(kMitsubishiHeavy152FanEcono, ac.getFan()); + + ac.setFan(kMitsubishiHeavy152FanTurbo); + EXPECT_EQ(kMitsubishiHeavy152FanTurbo, ac.getFan()); +} + +TEST(TestMitsubishiHeavy152AcClass, VerticalSwing) { + IRMitsubishiHeavy152Ac ac(0); + ac.begin(); + ac.setSwingVertical(kMitsubishiHeavy152SwingVAuto); + EXPECT_EQ(kMitsubishiHeavy152SwingVAuto, ac.getSwingVertical()); + + ac.setSwingVertical(kMitsubishiHeavy152SwingVHighest); + EXPECT_EQ(kMitsubishiHeavy152SwingVHighest, ac.getSwingVertical()); + + ac.setSwingVertical(kMitsubishiHeavy152SwingVHighest + 1); + EXPECT_EQ(kMitsubishiHeavy152SwingVHighest + 1, ac.getSwingVertical()); + + ac.setSwingVertical(kMitsubishiHeavy152SwingVOff); + EXPECT_EQ(kMitsubishiHeavy152SwingVOff, ac.getSwingVertical()); + + ac.setSwingVertical(kMitsubishiHeavy152SwingVOff + 1); + EXPECT_EQ(kMitsubishiHeavy152SwingVOff, ac.getSwingVertical()); + + // Out of bounds. + ac.setSwingVertical(255); + EXPECT_EQ(kMitsubishiHeavy152SwingVOff, ac.getSwingVertical()); +} + +TEST(TestMitsubishiHeavy152AcClass, HorizontalSwing) { + IRMitsubishiHeavy152Ac ac(0); + ac.begin(); + ac.setSwingHorizontal(kMitsubishiHeavy152SwingHAuto); + EXPECT_EQ(kMitsubishiHeavy152SwingHAuto, ac.getSwingHorizontal()); + + ac.setSwingHorizontal(kMitsubishiHeavy152SwingHLeftMax); + EXPECT_EQ(kMitsubishiHeavy152SwingHLeftMax, ac.getSwingHorizontal()); + + ac.setSwingHorizontal(kMitsubishiHeavy152SwingHLeftMax + 1); + EXPECT_EQ(kMitsubishiHeavy152SwingHLeftMax + 1, ac.getSwingHorizontal()); + + ac.setSwingHorizontal(kMitsubishiHeavy152SwingHRightMax); + EXPECT_EQ(kMitsubishiHeavy152SwingHRightMax, ac.getSwingHorizontal()); + + ac.setSwingHorizontal(kMitsubishiHeavy152SwingHRightMax - 1); + EXPECT_EQ(kMitsubishiHeavy152SwingHRightMax - 1, ac.getSwingHorizontal()); + + ac.setSwingHorizontal(kMitsubishiHeavy152SwingHOff); + EXPECT_EQ(kMitsubishiHeavy152SwingHOff, ac.getSwingHorizontal()); + + ac.setSwingHorizontal(kMitsubishiHeavy152SwingHOff + 1); + EXPECT_EQ(kMitsubishiHeavy152SwingHOff, ac.getSwingHorizontal()); + + // Out of bounds. + ac.setSwingHorizontal(255); + EXPECT_EQ(kMitsubishiHeavy152SwingHOff, ac.getSwingHorizontal()); +} + +TEST(TestMitsubishiHeavy152AcClass, Checksums) { + IRMitsubishiHeavy152Ac ac(0); + ac.begin(); + + EXPECT_TRUE(ac.validChecksum(ac.getRaw())); + + uint8_t expected[kMitsubishiHeavy152StateLength] = { + 0xAD, 0x51, 0x3C, 0xE5, 0x1A, 0x0C, 0xF3, 0x07, + 0xF8, 0x04, 0xFB, 0x00, 0xFF, 0x00, 0xFF, 0x00, + 0xFF, 0x80, 0x7F}; + EXPECT_TRUE(IRMitsubishiHeavy152Ac::validChecksum(expected)); + + // Screw up the "checksum" to test it fails. + expected[kMitsubishiHeavy152StateLength - 1] = 0x55; + EXPECT_FALSE(IRMitsubishiHeavy152Ac::validChecksum(expected)); + // getting the after getRaw() should repair it. + ac.setRaw(expected); + EXPECT_TRUE(ac.validChecksum(ac.getRaw())); + EXPECT_TRUE(IRMitsubishiHeavy152Ac::validChecksum(ac.getRaw())); +} + +TEST(TestMitsubishiHeavy152AcClass, HumanReadable) { + IRMitsubishiHeavy152Ac ac(0); + + EXPECT_EQ( + "Power: Off, Mode: 0 (Auto), Temp: 17C, Fan: 0 (Auto), " + "Swing (V): 0 (Auto), Swing (H): 0 (Auto), Silent: Off, Turbo: Off, " + "Econo: Off, Night: Off, Filter: Off, 3D: Off, Clean: Off", + ac.toString()); + ac.on(); + ac.setMode(kMitsubishiHeavyCool); + ac.setTemp(kMitsubishiHeavyMinTemp); + ac.setFan(kMitsubishiHeavy152FanMax); + ac.setFilter(true); + ac.setNight(true); + ac.setTurbo(false); + ac.setSilent(true); + ac.setEcono(false); + ac.set3D(true); + ac.setSwingVertical(kMitsubishiHeavy152SwingVAuto); + ac.setSwingHorizontal(kMitsubishiHeavy152SwingHAuto); + EXPECT_EQ( + "Power: On, Mode: 1 (Cool), Temp: 17C, Fan: 4 (Max), " + "Swing (V): 0 (Auto), Swing (H): 0 (Auto), Silent: On, Turbo: Off, " + "Econo: Off, Night: On, Filter: On, 3D: On, Clean: Off", + ac.toString()); + + ac.setMode(kMitsubishiHeavyHeat); + ac.setTemp(kMitsubishiHeavyMaxTemp); + ac.setFilter(true); + ac.setNight(false); + ac.setTurbo(true); + ac.setEcono(false); + ac.setSilent(false); + ac.set3D(false); + ac.setSwingVertical(kMitsubishiHeavy152SwingVLowest); + ac.setSwingHorizontal(kMitsubishiHeavy152SwingHLeftMax); + + EXPECT_EQ( + "Power: On, Mode: 4 (Heat), Temp: 31C, Fan: 8 (Turbo), " + "Swing (V): 5 (Lowest), Swing (H): 1 (Max Left), Silent: Off, Turbo: On, " + "Econo: Off, Night: Off, Filter: On, 3D: Off, Clean: Off", + ac.toString()); + + ac.setClean(true); + ac.setEcono(true); + ac.setMode(kMitsubishiHeavyAuto); + ac.setSwingVertical(kMitsubishiHeavy152SwingVOff); + + EXPECT_EQ( + "Power: On, Mode: 0 (Auto), Temp: 31C, Fan: 6 (Econo), " + "Swing (V): 6 (Off), Swing (H): 1 (Max Left), Silent: Off, " + "Turbo: Off, Econo: On, Night: Off, Filter: On, 3D: Off, Clean: On", + ac.toString()); + + ac.setClean(false); + ac.setTemp(25); + ac.setEcono(false); + ac.setMode(kMitsubishiHeavyDry); + ac.setSwingHorizontal(kMitsubishiHeavy152SwingHLeftRight); + EXPECT_EQ( + "Power: On, Mode: 2 (Dry), Temp: 25C, Fan: 0 (Auto), " + "Swing (V): 6 (Off), Swing (H): 7 (Left Right), Silent: Off, " + "Turbo: Off, Econo: Off, Night: Off, Filter: Off, 3D: Off, Clean: Off", + ac.toString()); +} + +TEST(TestMitsubishiHeavy152AcClass, ReconstructKnownExample) { + IRMitsubishiHeavy152Ac ac(0); + + EXPECT_EQ( + "Power: Off, Mode: 0 (Auto), Temp: 17C, Fan: 0 (Auto), " + "Swing (V): 0 (Auto), Swing (H): 0 (Auto), Silent: Off, Turbo: Off, " + "Econo: Off, Night: Off, Filter: Off, 3D: Off, Clean: Off", + ac.toString()); + ac.on(); + ac.setMode(kMitsubishiHeavyHeat); + ac.setTemp(24); + ac.setFan(kMitsubishiHeavy152FanMax); + ac.setFilter(true); + ac.setNight(false); + ac.setTurbo(false); + ac.setSilent(false); + ac.setEcono(false); + ac.set3D(false); + ac.setClean(false); + ac.setSwingVertical(kMitsubishiHeavy152SwingVAuto); + ac.setSwingHorizontal(kMitsubishiHeavy152SwingHAuto); + EXPECT_EQ( + "Power: On, Mode: 4 (Heat), Temp: 24C, Fan: 4 (Max), " + "Swing (V): 0 (Auto), Swing (H): 0 (Auto), Silent: Off, Turbo: Off, " + "Econo: Off, Night: Off, Filter: Off, 3D: Off, Clean: Off", + ac.toString()); + + uint8_t expected[kMitsubishiHeavy152StateLength] = { + 0xAD, 0x51, 0x3C, 0xE5, 0x1A, 0x0C, 0xF3, 0x07, + 0xF8, 0x04, 0xFB, 0x00, 0xFF, 0x00, 0xFF, 0x00, + 0xFF, 0x80, 0x7F}; + EXPECT_STATE_EQ(expected, ac.getRaw(), kMitsubishiHeavy152Bits); +} + +// Tests for IRMitsubishiHeavy88Ac class. + +TEST(TestMitsubishiHeavy88AcClass, Power) { + IRMitsubishiHeavy88Ac ac(0); + ac.begin(); + + ac.on(); + EXPECT_TRUE(ac.getPower()); + + ac.off(); + EXPECT_FALSE(ac.getPower()); + + ac.setPower(true); + EXPECT_TRUE(ac.getPower()); + + ac.setPower(false); + EXPECT_FALSE(ac.getPower()); +} + +TEST(TestMitsubishiHeavy88AcClass, Temperature) { + IRMitsubishiHeavy88Ac ac(0); + ac.begin(); + + ac.setMode(kMitsubishiHeavyCool); + + ac.setTemp(0); + EXPECT_EQ(kMitsubishiHeavyMinTemp, ac.getTemp()); + + ac.setTemp(255); + EXPECT_EQ(kMitsubishiHeavyMaxTemp, ac.getTemp()); + + ac.setTemp(kMitsubishiHeavyMinTemp); + EXPECT_EQ(kMitsubishiHeavyMinTemp, ac.getTemp()); + + ac.setTemp(kMitsubishiHeavyMaxTemp); + EXPECT_EQ(kMitsubishiHeavyMaxTemp, ac.getTemp()); + + ac.setTemp(kMitsubishiHeavyMinTemp - 1); + EXPECT_EQ(kMitsubishiHeavyMinTemp, ac.getTemp()); + + ac.setTemp(kMitsubishiHeavyMaxTemp + 1); + EXPECT_EQ(kMitsubishiHeavyMaxTemp, ac.getTemp()); + + ac.setTemp(19); + EXPECT_EQ(19, ac.getTemp()); + + ac.setTemp(21); + EXPECT_EQ(21, ac.getTemp()); + + ac.setTemp(25); + EXPECT_EQ(25, ac.getTemp()); + + ac.setTemp(29); + EXPECT_EQ(29, ac.getTemp()); +} + +TEST(TestMitsubishiHeavy88AcClass, OperatingMode) { + IRMitsubishiHeavy88Ac ac(0); + ac.begin(); + + ac.setMode(kMitsubishiHeavyAuto); + EXPECT_EQ(kMitsubishiHeavyAuto, ac.getMode()); + + ac.setMode(kMitsubishiHeavyCool); + EXPECT_EQ(kMitsubishiHeavyCool, ac.getMode()); + + ac.setMode(kMitsubishiHeavyHeat); + EXPECT_EQ(kMitsubishiHeavyHeat, ac.getMode()); + + ac.setMode(kMitsubishiHeavyDry); + EXPECT_EQ(kMitsubishiHeavyDry, ac.getMode()); + + ac.setMode(kMitsubishiHeavyFan); + EXPECT_EQ(kMitsubishiHeavyFan, ac.getMode()); + + ac.setMode(kMitsubishiHeavyHeat + 1); + EXPECT_EQ(kMitsubishiHeavyAuto, ac.getMode()); + + ac.setMode(255); + EXPECT_EQ(kMitsubishiHeavyAuto, ac.getMode()); +} + +TEST(TestMitsubishiHeavy88AcClass, Turbo) { + IRMitsubishiHeavy88Ac ac(0); + ac.begin(); + + ac.setTurbo(true); + EXPECT_TRUE(ac.getTurbo()); + + ac.setTurbo(false); + EXPECT_FALSE(ac.getTurbo()); + + ac.setTurbo(true); + EXPECT_TRUE(ac.getTurbo()); +} + +TEST(TestMitsubishiHeavy88AcClass, Econo) { + IRMitsubishiHeavy88Ac ac(0); + ac.begin(); + + ac.setEcono(true); + EXPECT_TRUE(ac.getEcono()); + + ac.setEcono(false); + EXPECT_FALSE(ac.getEcono()); + + ac.setEcono(true); + EXPECT_TRUE(ac.getEcono()); +} + +TEST(TestMitsubishiHeavy88AcClass, 3D) { + IRMitsubishiHeavy88Ac ac(0); + ac.begin(); + + ac.set3D(true); + EXPECT_TRUE(ac.get3D()); + + ac.set3D(false); + EXPECT_FALSE(ac.get3D()); + + ac.set3D(true); + EXPECT_TRUE(ac.get3D()); +} + +TEST(TestMitsubishiHeavy88AcClass, Clean) { + IRMitsubishiHeavy88Ac ac(0); + ac.begin(); + + ac.setClean(true); + EXPECT_TRUE(ac.getClean()); + + ac.setClean(false); + EXPECT_FALSE(ac.getClean()); + + ac.setClean(true); + EXPECT_TRUE(ac.getClean()); +} + +TEST(TestMitsubishiHeavy88AcClass, FanSpeed) { + IRMitsubishiHeavy88Ac ac(0); + ac.begin(); + + ac.setFan(kMitsubishiHeavy88FanLow); + EXPECT_EQ(kMitsubishiHeavy88FanLow, ac.getFan()); + + ac.setFan(kMitsubishiHeavy88FanAuto); + EXPECT_EQ(kMitsubishiHeavy88FanAuto, ac.getFan()); + + + ac.setFan(255); + EXPECT_EQ(kMitsubishiHeavy88FanAuto, ac.getFan()); + + ac.setFan(kMitsubishiHeavy88FanHigh); + EXPECT_EQ(kMitsubishiHeavy88FanHigh, ac.getFan()); + + ac.setFan(kMitsubishiHeavy88FanHigh + 1); + EXPECT_EQ(kMitsubishiHeavy88FanAuto, ac.getFan()); + + ac.setFan(kMitsubishiHeavy88FanHigh - 1); + EXPECT_EQ(kMitsubishiHeavy88FanHigh - 1, ac.getFan()); + + ac.setFan(kMitsubishiHeavy88FanLow + 1); + EXPECT_EQ(kMitsubishiHeavy88FanLow + 1, ac.getFan()); + + ac.setFan(kMitsubishiHeavy88FanEcono); + EXPECT_EQ(kMitsubishiHeavy88FanEcono, ac.getFan()); + + ac.setFan(kMitsubishiHeavy88FanTurbo); + EXPECT_EQ(kMitsubishiHeavy88FanTurbo, ac.getFan()); +} + +TEST(TestMitsubishiHeavy88AcClass, VerticalSwing) { + IRMitsubishiHeavy88Ac ac(0); + ac.begin(); + ac.setSwingVertical(kMitsubishiHeavy88SwingVAuto); + EXPECT_EQ(kMitsubishiHeavy88SwingVAuto, ac.getSwingVertical()); + + ac.setSwingVertical(kMitsubishiHeavy88SwingVHighest); + EXPECT_EQ(kMitsubishiHeavy88SwingVHighest, ac.getSwingVertical()); + + ac.setSwingVertical(kMitsubishiHeavy88SwingVOff); + EXPECT_EQ(kMitsubishiHeavy88SwingVOff, ac.getSwingVertical()); + + ac.setSwingVertical(kMitsubishiHeavy88SwingVHighest + 1); + EXPECT_EQ(kMitsubishiHeavy88SwingVOff, ac.getSwingVertical()); + + ac.setSwingVertical(kMitsubishiHeavy88SwingVOff + 1); + EXPECT_EQ(kMitsubishiHeavy88SwingVOff, ac.getSwingVertical()); + + // Out of bounds. + ac.setSwingVertical(255); + EXPECT_EQ(kMitsubishiHeavy88SwingVOff, ac.getSwingVertical()); +} + +TEST(TestMitsubishiHeavy88AcClass, HorizontalSwing) { + IRMitsubishiHeavy88Ac ac(0); + ac.begin(); + ac.setSwingHorizontal(kMitsubishiHeavy88SwingHAuto); + EXPECT_EQ(kMitsubishiHeavy88SwingHAuto, ac.getSwingHorizontal()); + + ac.setSwingHorizontal(kMitsubishiHeavy88SwingHLeftMax); + EXPECT_EQ(kMitsubishiHeavy88SwingHLeftMax, ac.getSwingHorizontal()); + + ac.setSwingHorizontal(kMitsubishiHeavy88SwingHLeftMax + 1); + EXPECT_EQ(kMitsubishiHeavy88SwingHOff, ac.getSwingHorizontal()); + + ac.setSwingHorizontal(kMitsubishiHeavy88SwingHRightMax); + EXPECT_EQ(kMitsubishiHeavy88SwingHRightMax, ac.getSwingHorizontal()); + + ac.setSwingHorizontal(kMitsubishiHeavy88SwingHRightMax - 1); + EXPECT_EQ(kMitsubishiHeavy88SwingHOff, ac.getSwingHorizontal()); + + ac.setSwingHorizontal(kMitsubishiHeavy88SwingHOff); + EXPECT_EQ(kMitsubishiHeavy88SwingHOff, ac.getSwingHorizontal()); + + ac.setSwingHorizontal(kMitsubishiHeavy88SwingHOff + 1); + EXPECT_EQ(kMitsubishiHeavy88SwingHOff, ac.getSwingHorizontal()); + + // Out of bounds. + ac.setSwingHorizontal(255); + EXPECT_EQ(kMitsubishiHeavy88SwingHOff, ac.getSwingHorizontal()); +} + +TEST(TestMitsubishiHeavy88AcClass, Checksums) { + IRMitsubishiHeavy88Ac ac(0); + ac.begin(); + + EXPECT_TRUE(ac.validChecksum(ac.getRaw())); + + uint8_t expected[kMitsubishiHeavy88StateLength] = { + 0xAD, 0x51, 0x3C, 0xD9, 0x26, 0x48, 0xB7, 0x00, 0xFF, 0x8A, 0x75}; + EXPECT_TRUE(IRMitsubishiHeavy88Ac::validChecksum(expected)); + + // Screw up the "checksum" to test it fails. + expected[kMitsubishiHeavy88StateLength - 1] = 0x55; + EXPECT_FALSE(IRMitsubishiHeavy88Ac::validChecksum(expected)); + // getting the after getRaw() should repair it. + ac.setRaw(expected); + EXPECT_TRUE(ac.validChecksum(ac.getRaw())); + EXPECT_TRUE(IRMitsubishiHeavy88Ac::validChecksum(ac.getRaw())); +} + +TEST(TestMitsubishiHeavy88AcClass, HumanReadable) { + IRMitsubishiHeavy88Ac ac(0); + + EXPECT_EQ( + "Power: Off, Mode: 0 (Auto), Temp: 17C, Fan: 0 (Auto), " + "Swing (V): 0 (Off), Swing (H): 0 (Off), " + "Turbo: Off, Econo: Off, 3D: Off, Clean: Off", + ac.toString()); + ac.on(); + ac.setMode(kMitsubishiHeavyCool); + ac.setTemp(kMitsubishiHeavyMinTemp); + ac.setFan(kMitsubishiHeavy88FanHigh); + ac.setTurbo(false); + ac.setEcono(false); + ac.set3D(true); + ac.setSwingVertical(kMitsubishiHeavy88SwingVAuto); + EXPECT_EQ( + "Power: On, Mode: 1 (Cool), Temp: 17C, Fan: 4 (High), " + "Swing (V): 16 (Auto), Swing (H): 200 (3D), " + "Turbo: Off, Econo: Off, 3D: On, Clean: Off", + ac.toString()); + + ac.setMode(kMitsubishiHeavyHeat); + ac.setTemp(kMitsubishiHeavyMaxTemp); + ac.setTurbo(true); + ac.setEcono(false); + ac.set3D(false); + ac.setSwingVertical(kMitsubishiHeavy88SwingVLowest); + ac.setSwingHorizontal(kMitsubishiHeavy88SwingHLeftMax); + + EXPECT_EQ( + "Power: On, Mode: 4 (Heat), Temp: 31C, Fan: 6 (Turbo), " + "Swing (V): 26 (Lowest), Swing (H): 4 (Max Left), Turbo: On, Econo: Off, " + "3D: Off, Clean: Off", + ac.toString()); + + ac.setClean(true); + ac.setEcono(true); + ac.setMode(kMitsubishiHeavyAuto); + ac.setSwingVertical(kMitsubishiHeavy88SwingVOff); + + EXPECT_EQ( + "Power: On, Mode: 0 (Auto), Temp: 31C, Fan: 7 (Econo), " + "Swing (V): 0 (Off), Swing (H): 4 (Max Left), Turbo: Off, Econo: On, " + "3D: Off, Clean: On", + ac.toString()); + + ac.setClean(false); + ac.setTemp(25); + ac.setEcono(false); + ac.setMode(kMitsubishiHeavyDry); + ac.setSwingHorizontal(kMitsubishiHeavy88SwingHLeftRight); + EXPECT_EQ( + "Power: On, Mode: 2 (Dry), Temp: 25C, Fan: 0 (Auto), " + "Swing (V): 0 (Off), Swing (H): 72 (Left Right), Turbo: Off, Econo: Off, " + "3D: Off, Clean: Off", + ac.toString()); +} + +// Tests for decodeMitsubishiHeavy(). + +// Decode a real MitsubishiHeavy 152Bit message. +TEST(TestDecodeMitsubishiHeavy, ZmsRealExample) { + IRsendTest irsend(0); + IRrecv irrecv(0); + IRMitsubishiHeavy152Ac ac(0); + irsend.begin(); + + uint8_t expected[kMitsubishiHeavy152StateLength] = { + 0xAD, 0x51, 0x3C, 0xE5, 0x1A, 0x0C, 0xF3, 0x07, + 0xF8, 0x04, 0xFB, 0x00, 0xFF, 0x00, 0xFF, 0x00, + 0xFF, 0x80, 0x7F}; + + // Ref: https://github.com/markszabo/IRremoteESP8266/issues/660#issuecomment-480571466 + uint16_t rawData[307] = { + 3136, 1638, 364, 428, 366, 1224, 362, 432, 364, 430, 364, 1226, 362, 432, + 364, 1224, 366, 428, 366, 430, 366, 1224, 362, 1228, 362, 1228, 362, 432, + 364, 1224, 364, 432, 364, 1226, 364, 1224, 366, 1226, 364, 428, 364, 430, + 364, 430, 364, 432, 366, 1226, 364, 1224, 364, 430, 364, 1226, 364, 428, + 364, 1224, 368, 1224, 364, 428, 364, 430, 366, 430, 364, 1158, 430, 432, + 366, 1222, 366, 430, 366, 430, 364, 1226, 364, 1224, 364, 1224, 364, 1224, + 366, 1224, 364, 430, 364, 430, 364, 1228, 362, 1226, 364, 1226, 366, 1222, + 366, 430, 364, 430, 364, 1224, 366, 1224, 364, 430, 364, 430, 364, 432, + 364, 430, 364, 428, 364, 430, 364, 430, 366, 1226, 362, 1154, 434, 1228, + 364, 1226, 362, 1226, 364, 1226, 364, 1228, 362, 1226, 362, 432, 364, 430, + 364, 428, 364, 430, 364, 430, 364, 1228, 362, 1228, 362, 432, 364, 1224, + 368, 1224, 364, 1226, 362, 1226, 364, 1226, 366, 428, 366, 430, 364, 1224, + 364, 430, 366, 430, 366, 430, 364, 430, 364, 430, 364, 1226, 364, 1226, + 366, 1224, 366, 1224, 366, 1226, 364, 1224, 366, 1224, 366, 1224, 366, + 428, 364, 430, 366, 428, 364, 430, 364, 430, 366, 428, 364, 430, 364, 432, + 364, 1226, 364, 1226, 364, 1226, 364, 1228, 364, 1222, 370, 1222, 362, + 1228, 362, 1226, 362, 430, 364, 430, 364, 430, 364, 432, 364, 428, 364, + 432, 364, 428, 364, 430, 366, 1226, 362, 1224, 364, 1226, 364, 1226, 364, + 1226, 362, 1226, 366, 1224, 366, 1224, 364, 430, 364, 432, 364, 428, 364, + 432, 364, 428, 364, 430, 366, 430, 364, 430, 364, 1226, 362, 1226, 364, + 1224, 366, 1226, 362, 1228, 364, 1224, 366, 1224, 364, 430, 364, 432, 364, + 428, 364, 430, 364, 430, 364, 430, 366, 430, 364, 430, 338, 1252, 362 + }; // UNKNOWN 5138D49D + + irsend.reset(); + irsend.sendRaw(rawData, 307, 38000); + irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(MITSUBISHI_HEAVY_152, irsend.capture.decode_type); + ASSERT_EQ(kMitsubishiHeavy152Bits, irsend.capture.bits); + EXPECT_STATE_EQ(expected, irsend.capture.state, irsend.capture.bits); + ac.setRaw(irsend.capture.state); + EXPECT_EQ( + "Power: On, Mode: 4 (Heat), Temp: 24C, Fan: 4 (Max), " + "Swing (V): 0 (Auto), Swing (H): 0 (Auto), Silent: Off, Turbo: Off, " + "Econo: Off, Night: Off, Filter: Off, 3D: Off, Clean: Off", + ac.toString()); +} + +// Decode a Synthetic MitsubishiHeavy 152Bit message. +TEST(TestDecodeMitsubishiHeavy, ZmsSyntheticExample) { + IRsendTest irsend(0); + IRrecv irrecv(0); + IRMitsubishiHeavy152Ac ac(0); + irsend.begin(); + + uint8_t expected[kMitsubishiHeavy152StateLength] = { + 0xAD, 0x51, 0x3C, 0xE5, 0x1A, 0x0C, 0xF3, 0x07, + 0xF8, 0x04, 0xFB, 0x00, 0xFF, 0x00, 0xFF, 0x00, + 0xFF, 0x80, 0x7F}; + + irsend.reset(); + irsend.sendMitsubishiHeavy152(expected); + irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(MITSUBISHI_HEAVY_152, irsend.capture.decode_type); + ASSERT_EQ(kMitsubishiHeavy152Bits, irsend.capture.bits); + EXPECT_STATE_EQ(expected, irsend.capture.state, irsend.capture.bits); + ac.setRaw(irsend.capture.state); + EXPECT_EQ( + "Power: On, Mode: 4 (Heat), Temp: 24C, Fan: 4 (Max), " + "Swing (V): 0 (Auto), Swing (H): 0 (Auto), Silent: Off, Turbo: Off, " + "Econo: Off, Night: Off, Filter: Off, 3D: Off, Clean: Off", + ac.toString()); +} + +// Decode a real MitsubishiHeavy 152Bit message. +TEST(TestDecodeMitsubishiHeavy, ZmsRealExample2) { + IRsendTest irsend(0); + IRrecv irrecv(0); + IRMitsubishiHeavy152Ac ac(0); + irsend.begin(); + + uint8_t expected[kMitsubishiHeavy152StateLength] = { + 0xAD, 0x51, 0x3C, 0xE5, 0x1A, 0x04, 0xFB, 0x07, + 0xF8, 0x04, 0xFB, 0x00, 0xFF, 0x00, 0xFF, 0x00, + 0xFF, 0x80, 0x7F}; + + // Ref: https://github.com/markszabo/IRremoteESP8266/issues/660#issuecomment-480571466 + uint16_t rawData[307] = { + 3196, 1580, 398, 390, 404, 1190, 400, 390, 402, 390, 402, 1192, 402, 388, + 402, 1192, 400, 390, 402, 392, 402, 1192, 400, 1188, 400, 1188, 400, 390, + 404, 1192, 398, 392, 400, 1192, 402, 1188, 400, 1190, 402, 388, 402, 392, + 404, 392, 402, 392, 404, 1188, 400, 1190, 398, 392, 404, 1188, 398, 392, + 402, 1192, 398, 1190, 400, 390, 404, 390, 402, 392, 404, 1188, 398, 392, + 404, 1190, 400, 392, 400, 394, 402, 1192, 398, 1190, 398, 1192, 398, 1190, + 400, 1190, 398, 392, 402, 1192, 398, 1190, 398, 1190, 398, 1192, 396, + 1192, 398, 396, 400, 394, 398, 1194, 396, 394, 400, 394, 398, 396, 398, + 396, 400, 402, 390, 394, 402, 392, 398, 396, 398, 1194, 396, 1194, 398, + 1192, 398, 1192, 396, 1194, 396, 1192, 396, 1196, 398, 1190, 398, 392, + 402, 392, 402, 394, 398, 394, 400, 394, 400, 1192, 398, 1192, 400, 390, + 402, 1190, 398, 1190, 398, 1192, 402, 1188, 398, 1190, 400, 390, 402, 392, + 402, 1190, 400, 390, 404, 390, 402, 394, 402, 392, 402, 390, 404, 1190, + 400, 1188, 400, 1190, 400, 1190, 402, 1188, 402, 1188, 400, 1188, 402, + 1190, 400, 388, 402, 394, 404, 392, 404, 388, 404, 390, 404, 392, 402, + 394, 402, 390, 402, 1190, 402, 1186, 402, 1190, 400, 1190, 398, 1190, 402, + 1186, 402, 1190, 400, 1188, 400, 390, 404, 392, 404, 390, 402, 392, 402, + 392, 400, 394, 402, 392, 402, 394, 400, 1192, 400, 1190, 400, 1188, 400, + 1192, 400, 1186, 402, 1190, 400, 1190, 400, 1188, 402, 388, 402, 390, 404, + 392, 402, 392, 402, 392, 402, 392, 404, 392, 402, 392, 404, 1190, 400, + 1190, 398, 1190, 400, 1190, 400, 1190, 400, 1188, 400, 1188, 400, 392, + 402, 392, 404, 390, 402, 392, 402, 392, 402, 392, 402, 390, 402, 392, 402, + 1192, 398}; // UNKNOWN A650F2C1 + + irsend.reset(); + irsend.sendRaw(rawData, 307, 38000); + irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(MITSUBISHI_HEAVY_152, irsend.capture.decode_type); + ASSERT_EQ(kMitsubishiHeavy152Bits, irsend.capture.bits); + EXPECT_STATE_EQ(expected, irsend.capture.state, irsend.capture.bits); + ac.setRaw(irsend.capture.state); + EXPECT_EQ( + "Power: Off, Mode: 4 (Heat), Temp: 24C, Fan: 4 (Max), " + "Swing (V): 0 (Auto), Swing (H): 0 (Auto), Silent: Off, Turbo: Off, " + "Econo: Off, Night: Off, Filter: Off, 3D: Off, Clean: Off", + ac.toString()); +} + +// Decode a Synthetic MitsubishiHeavy 88 Bit message. +TEST(TestDecodeMitsubishiHeavy, ZjsSyntheticExample) { + IRsendTest irsend(0); + IRrecv irrecv(0); + IRMitsubishiHeavy88Ac ac(0); + irsend.begin(); + + uint8_t expected[kMitsubishiHeavy88StateLength] = { + 0xAD, 0x51, 0x3C, 0xD9, 0x26, 0x48, 0xB7, 0x00, 0xFF, 0x8A, 0x75}; + + irsend.reset(); + irsend.sendMitsubishiHeavy88(expected); + irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(MITSUBISHI_HEAVY_88, irsend.capture.decode_type); + ASSERT_EQ(kMitsubishiHeavy88Bits, irsend.capture.bits); + EXPECT_STATE_EQ(expected, irsend.capture.state, irsend.capture.bits); + ac.setRaw(irsend.capture.state); + EXPECT_EQ( + "Power: On, Mode: 2 (Dry), Temp: 25C, Fan: 0 (Auto), " + "Swing (V): 0 (Off), Swing (H): 72 (Left Right), Turbo: Off, Econo: Off, " + "3D: Off, Clean: Off", + ac.toString()); +} diff --git a/lib/IRremoteESP8266-2.5.2.03/test/ir_Mitsubishi_test.cpp b/lib/IRremoteESP8266-2.6.0/test/ir_Mitsubishi_test.cpp similarity index 99% rename from lib/IRremoteESP8266-2.5.2.03/test/ir_Mitsubishi_test.cpp rename to lib/IRremoteESP8266-2.6.0/test/ir_Mitsubishi_test.cpp index 7b8eb2192fad..6c9480b319ae 100644 --- a/lib/IRremoteESP8266-2.5.2.03/test/ir_Mitsubishi_test.cpp +++ b/lib/IRremoteESP8266-2.6.0/test/ir_Mitsubishi_test.cpp @@ -17,6 +17,7 @@ TEST(TestSendMitsubishi, SendDataOnly) { irsend.reset(); irsend.sendMitsubishi(0xE242); EXPECT_EQ( + "f33000d50" "m300s2100m300s2100m300s2100m300s900m300s900m300s900m300s2100m300s900" "m300s900m300s2100m300s900m300s900m300s900m300s900m300s2100m300s900" "m300s28080" @@ -28,6 +29,7 @@ TEST(TestSendMitsubishi, SendDataOnly) { irsend.reset(); irsend.sendMitsubishi(0x0); EXPECT_EQ( + "f33000d50" "m300s900m300s900m300s900m300s900m300s900m300s900m300s900m300s900" "m300s900m300s900m300s900m300s900m300s900m300s900m300s900m300s900" "m300s34080" @@ -39,6 +41,7 @@ TEST(TestSendMitsubishi, SendDataOnly) { irsend.reset(); irsend.sendMitsubishi(0x4321); EXPECT_EQ( + "f33000d50" "m300s900m300s2100m300s900m300s900m300s900m300s900m300s2100m300s2100" "m300s900m300s900m300s2100m300s900m300s900m300s900m300s900m300s2100" "m300s28080" @@ -56,6 +59,7 @@ TEST(TestSendMitsubishi, SendWithRepeats) { irsend.reset(); irsend.sendMitsubishi(0xE242, kMitsubishiBits, 0); // 0 repeat. EXPECT_EQ( + "f33000d50" "m300s2100m300s2100m300s2100m300s900m300s900m300s900m300s2100m300s900" "m300s900m300s2100m300s900m300s900m300s900m300s900m300s2100m300s900" "m300s28080", @@ -64,6 +68,7 @@ TEST(TestSendMitsubishi, SendWithRepeats) { irsend.reset(); irsend.sendMitsubishi(0xE242, kMitsubishiBits, 1); // 1 repeat. EXPECT_EQ( + "f33000d50" "m300s2100m300s2100m300s2100m300s900m300s900m300s900m300s2100m300s900" "m300s900m300s2100m300s900m300s900m300s900m300s900m300s2100m300s900" "m300s28080" @@ -73,6 +78,7 @@ TEST(TestSendMitsubishi, SendWithRepeats) { irsend.outputStr()); irsend.sendMitsubishi(0xE242, kMitsubishiBits, 2); // 2 repeats. EXPECT_EQ( + "f33000d50" "m300s2100m300s2100m300s2100m300s900m300s900m300s900m300s2100m300s900" "m300s900m300s2100m300s900m300s900m300s900m300s900m300s2100m300s900" "m300s28080" @@ -93,6 +99,7 @@ TEST(TestSendMitsubishi, SendUnusualSize) { irsend.reset(); irsend.sendMitsubishi(0x0, 8); EXPECT_EQ( + "f33000d50" "m300s900m300s900m300s900m300s900m300s900m300s900m300s900m300s900" "m300s43680" "m300s900m300s900m300s900m300s900m300s900m300s900m300s900m300s900" @@ -102,6 +109,7 @@ TEST(TestSendMitsubishi, SendUnusualSize) { irsend.reset(); irsend.sendMitsubishi(0x1234567890ABCDEF, 64); EXPECT_EQ( + "f33000d50" "m300s900m300s900m300s900m300s2100m300s900m300s900m300s2100m300s900" "m300s900m300s900m300s2100m300s2100m300s900m300s2100m300s900m300s900" "m300s900m300s2100m300s900m300s2100m300s900m300s2100m300s2100m300s900" @@ -305,6 +313,7 @@ TEST(TestSendMitsubishiAC, SendDataOnly) { irsend.reset(); irsend.sendMitsubishiAC(mitsub_code); EXPECT_EQ( + "f38000d50" "m3400s1750" "m450s1300m450s1300m450s420m450s420m450s420m450s1300m450s420m450s420" "m450s1300m450s1300m450s420m450s1300m450s420m450s420m450s1300m450s1300" @@ -360,6 +369,7 @@ TEST(TestSendMitsubishiAC, SendWithRepeats) { irsend.sendMitsubishiAC(mitsub_code, kMitsubishiACStateLength, 0); EXPECT_EQ( + "f38000d50" "m3400s1750" "m450s1300m450s1300m450s420m450s420m450s420m450s1300m450s420m450s420" "m450s1300m450s1300m450s420m450s1300m450s420m450s420m450s1300m450s1300" @@ -385,6 +395,7 @@ TEST(TestSendMitsubishiAC, SendWithRepeats) { irsend.reset(); irsend.sendMitsubishiAC(mitsub_code, kMitsubishiACStateLength, 2); EXPECT_EQ( + "f38000d50" "m3400s1750" "m450s1300m450s1300m450s420m450s420m450s420m450s1300m450s420m450s420" "m450s1300m450s1300m450s420m450s1300m450s420m450s420m450s1300m450s1300" @@ -466,6 +477,7 @@ TEST(TestSendMitsubishiAC, SendUnexpectedSizes) { irsend.reset(); irsend.sendMitsubishiAC(mitsub_long_code, 19); ASSERT_EQ( + "f38000d50" "m3400s1750" "m450s1300m450s1300m450s420m450s420m450s420m450s1300m450s420m450s420" "m450s1300m450s1300m450s420m450s1300m450s420m450s420m450s1300m450s1300" @@ -665,6 +677,7 @@ TEST(TestMitsubishiACClass, MessageConstuction) { irsend.reset(); irsend.sendMitsubishiAC(mitsub.getRaw()); EXPECT_EQ( + "f38000d50" "m3400s1750" "m450s1300m450s1300m450s420m450s420m450s420m450s1300m450s420m450s420" "m450s1300m450s1300m450s420m450s1300m450s420m450s420m450s1300m450s1300" @@ -984,6 +997,7 @@ TEST(TestSendMitsubishi2, SendDataOnly) { irsend.reset(); irsend.sendMitsubishi2(0xF82); EXPECT_EQ( + "f33000d50" "m8400s4200" "m560s520m560s520m560s520m560s520m560s1560m560s1560m560s1560m560s1560" "m560s4200" @@ -999,6 +1013,7 @@ TEST(TestSendMitsubishi2, SendDataOnly) { irsend.reset(); irsend.sendMitsubishi2(0x0); EXPECT_EQ( + "f33000d50" "m8400s4200" "m560s520m560s520m560s520m560s520m560s520m560s520m560s520m560s520" "m560s4200" @@ -1020,6 +1035,7 @@ TEST(TestSendMitsubishi2, Repeats) { irsend.reset(); irsend.sendMitsubishi2(0xF82, kMitsubishiBits, 0); EXPECT_EQ( + "f33000d50" "m8400s4200" "m560s520m560s520m560s520m560s520m560s1560m560s1560m560s1560m560s1560" "m560s4200" @@ -1030,6 +1046,7 @@ TEST(TestSendMitsubishi2, Repeats) { irsend.reset(); irsend.sendMitsubishi2(0xF82, kMitsubishiBits, 2); EXPECT_EQ( + "f33000d50" "m8400s4200" "m560s520m560s520m560s520m560s520m560s1560m560s1560m560s1560m560s1560" "m560s4200" diff --git a/lib/IRremoteESP8266-2.5.2.03/test/ir_NEC_test.cpp b/lib/IRremoteESP8266-2.6.0/test/ir_NEC_test.cpp similarity index 98% rename from lib/IRremoteESP8266-2.5.2.03/test/ir_NEC_test.cpp rename to lib/IRremoteESP8266-2.6.0/test/ir_NEC_test.cpp index 6b84b0ec9157..c881b7b44750 100644 --- a/lib/IRremoteESP8266-2.5.2.03/test/ir_NEC_test.cpp +++ b/lib/IRremoteESP8266-2.6.0/test/ir_NEC_test.cpp @@ -12,6 +12,7 @@ TEST(TestSendNEC, SendDataOnly) { irsend.begin(); irsend.sendNEC(0); EXPECT_EQ( + "f38000d33" "m8960s4480m560s560m560s560m560s560m560s560m560s560m560s560m560s560" "m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560" "m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560" @@ -20,6 +21,7 @@ TEST(TestSendNEC, SendDataOnly) { irsend.outputStr()); irsend.sendNEC(0xAA00FF55); EXPECT_EQ( + "f38000d33" "m8960s4480m560s1680m560s560m560s1680m560s560m560s1680m560s560" "m560s1680m560s560m560s560m560s560m560s560m560s560m560s560m560s560" "m560s560m560s560m560s1680m560s1680m560s1680m560s1680m560s1680" @@ -33,15 +35,17 @@ TEST(TestSendNEC, SendSmallData) { IRsendTest irsend(4); irsend.begin(); irsend.sendNEC(0xA, 4); // Send only 4 data bits. - EXPECT_EQ("m8960s4480m560s1680m560s560m560s1680m560s560m560s87360", + EXPECT_EQ("f38000d33m8960s4480m560s1680m560s560m560s1680m560s560m560s87360", irsend.outputStr()); irsend.sendNEC(0, 8); // Send only 8 data bits. EXPECT_EQ( + "f38000d33" "m8960s4480m560s560m560s560m560s560m560s560m560s560m560s560m560s560" "m560s560m560s85120", irsend.outputStr()); irsend.sendNEC(0x1234567890ABCDEF, 64); // Send 64 data bits. EXPECT_EQ( + "f38000d33" "m8960s4480m560s560m560s560m560s560m560s1680m560s560m560s560" "m560s1680m560s560m560s560m560s560m560s1680m560s1680m560s560" "m560s1680m560s560m560s560m560s560m560s1680m560s560m560s1680" @@ -61,17 +65,20 @@ TEST(TestSendNEC, SendWithRepeats) { irsend.begin(); irsend.sendNEC(0, 8, 0); // Send a command with 0 repeats. EXPECT_EQ( + "f38000d33" "m8960s4480m560s560m560s560m560s560m560s560m560s560m560s560m560s560" "m560s560m560s85120", irsend.outputStr()); irsend.sendNEC(0xAA, 8, 1); // Send a command with 1 repeat. EXPECT_EQ( + "f38000d33" "m8960s4480m560s1680m560s560m560s1680m560s560m560s1680m560s560" "m560s1680m560s560m560s80640" "m8960s2240m560s96320", irsend.outputStr()); irsend.sendNEC(0xAA, 8, 3); // Send a command with 3 repeats. EXPECT_EQ( + "f38000d33" "m8960s4480m560s1680m560s560m560s1680m560s560m560s1680m560s560" "m560s1680m560s560m560s80640" "m8960s2240m560s96320" diff --git a/lib/IRremoteESP8266-2.5.2.03/test/ir_Nikai_test.cpp b/lib/IRremoteESP8266-2.6.0/test/ir_Nikai_test.cpp similarity index 99% rename from lib/IRremoteESP8266-2.5.2.03/test/ir_Nikai_test.cpp rename to lib/IRremoteESP8266-2.6.0/test/ir_Nikai_test.cpp index 4a4ea05bb07a..fff242326e65 100644 --- a/lib/IRremoteESP8266-2.5.2.03/test/ir_Nikai_test.cpp +++ b/lib/IRremoteESP8266-2.6.0/test/ir_Nikai_test.cpp @@ -13,6 +13,7 @@ TEST(TestSendNikai, SendDataOnly) { irsend.reset(); irsend.sendNikai(0xD5F2A); // Nikai TV Power Off. EXPECT_EQ( + "f38000d33" "m4000s4000" "m500s2000m500s2000m500s2000m500s2000m500s1000m500s1000m500s2000" "m500s1000m500s2000m500s1000m500s2000m500s1000m500s1000m500s1000" @@ -31,6 +32,7 @@ TEST(TestSendNikai, SendWithRepeats) { irsend.reset(); irsend.sendNikai(0xD5F2A, kNikaiBits, 1); // 1 repeat. EXPECT_EQ( + "f38000d33" "m4000s4000" "m500s2000m500s2000m500s2000m500s2000m500s1000m500s1000m500s2000" "m500s1000m500s2000m500s1000m500s2000m500s1000m500s1000m500s1000" @@ -44,6 +46,7 @@ TEST(TestSendNikai, SendWithRepeats) { irsend.outputStr()); irsend.sendNikai(0xD5F2A, kNikaiBits, 2); // 2 repeat. EXPECT_EQ( + "f38000d33" "m4000s4000" "m500s2000m500s2000m500s2000m500s2000m500s1000m500s1000m500s2000" "m500s1000m500s2000m500s1000m500s2000m500s1000m500s1000m500s1000" diff --git a/lib/IRremoteESP8266-2.5.2.03/test/ir_Panasonic_test.cpp b/lib/IRremoteESP8266-2.6.0/test/ir_Panasonic_test.cpp similarity index 99% rename from lib/IRremoteESP8266-2.5.2.03/test/ir_Panasonic_test.cpp rename to lib/IRremoteESP8266-2.6.0/test/ir_Panasonic_test.cpp index a1d8a7979407..4d10f8fb18bb 100644 --- a/lib/IRremoteESP8266-2.5.2.03/test/ir_Panasonic_test.cpp +++ b/lib/IRremoteESP8266-2.6.0/test/ir_Panasonic_test.cpp @@ -32,6 +32,7 @@ TEST(TestSendPanasonic64, SendDataOnly) { irsend.reset(); irsend.sendPanasonic64(0x0); EXPECT_EQ( + "f36700d50" "m3456s1728" "m432s432m432s432m432s432m432s432m432s432m432s432m432s432m432s432" "m432s432m432s432m432s432m432s432m432s432m432s432m432s432m432s432" @@ -45,6 +46,7 @@ TEST(TestSendPanasonic64, SendDataOnly) { irsend.reset(); irsend.sendPanasonic64(0x40040190ED7C); EXPECT_EQ( + "f36700d50" "m3456s1728" "m432s432m432s1296m432s432m432s432m432s432m432s432m432s432m432s432" "m432s432m432s432m432s432m432s432m432s432m432s1296m432s432m432s432" @@ -58,6 +60,7 @@ TEST(TestSendPanasonic64, SendDataOnly) { irsend.reset(); irsend.sendPanasonic64(0xFFFFFFFFFFFF); EXPECT_EQ( + "f36700d50" "m3456s1728" "m432s1296m432s1296m432s1296m432s1296m432s1296m432s1296m432s1296m432s1296" "m432s1296m432s1296m432s1296m432s1296m432s1296m432s1296m432s1296m432s1296" @@ -77,6 +80,7 @@ TEST(TestSendPanasonic64, SendWithRepeats) { irsend.reset(); irsend.sendPanasonic64(0x40040190ED7C, kPanasonicBits, 0); // 0 repeats. EXPECT_EQ( + "f36700d50" "m3456s1728" "m432s432m432s1296m432s432m432s432m432s432m432s432m432s432m432s432" "m432s432m432s432m432s432m432s432m432s432m432s1296m432s432m432s432" @@ -90,6 +94,7 @@ TEST(TestSendPanasonic64, SendWithRepeats) { irsend.reset(); irsend.sendPanasonic64(0x40040190ED7C, kPanasonicBits, 1); // 1 repeat. EXPECT_EQ( + "f36700d50" "m3456s1728" "m432s432m432s1296m432s432m432s432m432s432m432s432m432s432m432s432" "m432s432m432s432m432s432m432s432m432s432m432s1296m432s432m432s432" @@ -110,6 +115,7 @@ TEST(TestSendPanasonic64, SendWithRepeats) { irsend.sendPanasonic64(0x40040190ED7C, kPanasonicBits, 2); // 2 repeats. EXPECT_EQ( + "f36700d50" "m3456s1728" "m432s432m432s1296m432s432m432s432m432s432m432s432m432s432m432s432" "m432s432m432s432m432s432m432s432m432s432m432s1296m432s432m432s432" @@ -145,6 +151,7 @@ TEST(TestSendPanasonic64, SendUnusualSize) { irsend.reset(); irsend.sendPanasonic64(0x0, 8); EXPECT_EQ( + "f36700d50" "m3456s1728" "m432s432m432s432m432s432m432s432m432s432m432s432m432s432m432s432" "m432s150768", @@ -153,6 +160,7 @@ TEST(TestSendPanasonic64, SendUnusualSize) { irsend.reset(); irsend.sendPanasonic64(0x1234567890ABCDEF, 64); EXPECT_EQ( + "f36700d50" "m3456s1728" "m432s432m432s432m432s432m432s1296m432s432m432s432m432s1296m432s432" "m432s432m432s432m432s1296m432s1296m432s432m432s1296m432s432m432s432" @@ -483,6 +491,7 @@ TEST(TestSendPanasonicAC, SendDataOnly) { 0x00, 0x06, 0x60, 0x00, 0x00, 0x80, 0x00, 0x06, 0x83}; irsend.sendPanasonicAC(state); EXPECT_EQ( + "f36700d50" "m3456s1728" "m432s432m432s1296m432s432m432s432m432s432m432s432m432s432m432s432" "m432s432m432s432m432s432m432s432m432s432m432s1296m432s432m432s432" diff --git a/lib/IRremoteESP8266-2.5.2.03/test/ir_Pioneer_test.cpp b/lib/IRremoteESP8266-2.6.0/test/ir_Pioneer_test.cpp similarity index 99% rename from lib/IRremoteESP8266-2.5.2.03/test/ir_Pioneer_test.cpp rename to lib/IRremoteESP8266-2.6.0/test/ir_Pioneer_test.cpp index b78469adda1f..36d61c706f2a 100644 --- a/lib/IRremoteESP8266-2.5.2.03/test/ir_Pioneer_test.cpp +++ b/lib/IRremoteESP8266-2.6.0/test/ir_Pioneer_test.cpp @@ -13,6 +13,7 @@ TEST(TestSendPioneer, SendDataOnly) { irsend.begin(); irsend.sendPioneer(0); EXPECT_EQ( + "f38000d33" "m8960s4480" "m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560" "m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560" @@ -28,6 +29,7 @@ TEST(TestSendPioneer, SendDataOnly) { irsend.outputStr()); irsend.sendPioneer(0x55FF00AAAA00FF55); EXPECT_EQ( + "f38000d33" "m8960s4480" "m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680" "m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680" @@ -136,6 +138,7 @@ TEST(TestDecodePioneer, SyntheticPioneerMessage) { irsend.reset(); irsend.sendPioneer(0x659A857AF50A3DC2, 64, 0); EXPECT_EQ( + "f38000d33" "m8960s4480" "m560s560m560s1680m560s1680m560s560m560s560m560s1680m560s560m560s1680" "m560s1680m560s560m560s560m560s1680m560s1680m560s560m560s1680m560s560" diff --git a/lib/IRremoteESP8266-2.5.2.03/test/ir_Pronto_test.cpp b/lib/IRremoteESP8266-2.6.0/test/ir_Pronto_test.cpp similarity index 96% rename from lib/IRremoteESP8266-2.5.2.03/test/ir_Pronto_test.cpp rename to lib/IRremoteESP8266-2.6.0/test/ir_Pronto_test.cpp index e52c6dd908f9..513f985da7e8 100644 --- a/lib/IRremoteESP8266-2.5.2.03/test/ir_Pronto_test.cpp +++ b/lib/IRremoteESP8266-2.6.0/test/ir_Pronto_test.cpp @@ -58,7 +58,8 @@ TEST(TestSendPronto, MoreDataThanNeededInNormal) { uint16_t pronto_test[8] = {0x0000, 0x0067, 0x0001, 0x0000, 0x0001, 0x0002, 0x0003, 0x0004}; irsend.sendPronto(pronto_test, 8); - EXPECT_EQ("m25s50", irsend.outputStr()); // Only send the data required. + EXPECT_EQ("f40244d50m25s50", + irsend.outputStr()); // Only send the data required. } TEST(TestSendPronto, MoreDataThanNeededInRepeat) { @@ -70,7 +71,8 @@ TEST(TestSendPronto, MoreDataThanNeededInRepeat) { uint16_t pronto_test[8] = {0x0000, 0x0067, 0x0000, 0x0001, 0x0001, 0x0002, 0x0003, 0x0004}; irsend.sendPronto(pronto_test, 8); - EXPECT_EQ("m25s50", irsend.outputStr()); // Only send the data required. + EXPECT_EQ("f40244d50m25s50", + irsend.outputStr()); // Only send the data required. } TEST(TestSendPronto, MoreDataThanNeededInBoth) { @@ -82,9 +84,11 @@ TEST(TestSendPronto, MoreDataThanNeededInBoth) { uint16_t pronto_test[10] = {0x0000, 0x0067, 0x0001, 0x0001, 0x0001, 0x0002, 0x0003, 0x0004, 0x5, 0x6}; irsend.sendPronto(pronto_test, 10); - EXPECT_EQ("m25s50", irsend.outputStr()); // Only send the data required. + EXPECT_EQ("f40244d50m25s50", + irsend.outputStr()); // Only send the data required. irsend.sendPronto(pronto_test, 10, 1); - EXPECT_EQ("m25s50m75s100", irsend.outputStr()); // Only the data required. + EXPECT_EQ("f40244d50m25s50m75s100", + irsend.outputStr()); // Only the data required. } TEST(TestSendPronto, ShortestValidCodeThatSendsNothing) { @@ -134,6 +138,7 @@ TEST(TestSendPronto, NonRepeatingCode) { EXPECT_EQ(0x1, irsend.capture.address); EXPECT_EQ(0x0, irsend.capture.command); EXPECT_EQ( + "f40244d50" "m2400s600" "m600s600m600s600m600s600m600s600m600s600m600s600m600s600m1200s600" "m600s600m600s600m600s600m600s27650" @@ -160,6 +165,7 @@ TEST(TestSendPronto, NonRepeatingCode) { EXPECT_EQ(0x1, irsend.capture.address); EXPECT_EQ(0x0, irsend.capture.command); EXPECT_EQ( + "f40244d50" "m2400s600" "m600s600m600s600m600s600m600s600m600s600m600s600m600s600m1200s600" "m600s600m600s600m600s600m600s27650" @@ -201,6 +207,7 @@ TEST(TestSendPronto, RepeatSequenceOnlyForSony) { EXPECT_EQ(0x1A, irsend.capture.address); EXPECT_EQ(0x24AE, irsend.capture.command); EXPECT_EQ( + "f40244d50" "m2400s600" "m600s600m1200s600m1200s600m1200s600m600s600m1200s600m600s600m600s600" "m1200s600m600s600m1200s600m1200s600m1200s600m600s600m600s600m1200s600" @@ -218,6 +225,7 @@ TEST(TestSendPronto, RepeatSequenceOnlyForSony) { EXPECT_EQ(0x1A, irsend.capture.address); EXPECT_EQ(0x24AE, irsend.capture.command); EXPECT_EQ( + "f40244d50" "m2400s600" "m600s600m1200s600m1200s600m1200s600m600s600m1200s600m600s600m600s600" "m1200s600m600s600m1200s600m1200s600m1200s600m600s600m600s600m1200s600" @@ -265,6 +273,7 @@ TEST(TestSendPronto, RepeatSequenceOnlyForPanasonic) { EXPECT_EQ(0x4004, irsend.capture.address); EXPECT_EQ(0x1007C7D, irsend.capture.command); EXPECT_EQ( + "f36682d50" "m3456s1701" "m432s432m432s1296m432s432m432s432m432s432m432s432m432s432m432s432" "m432s432m432s432m432s432m432s432m432s432m432s1296m432s432m432s432" @@ -305,6 +314,7 @@ TEST(TestSendPronto, NormalPlusRepeatSequence) { EXPECT_EQ(0x18, irsend.capture.address); EXPECT_EQ(0x8, irsend.capture.command); EXPECT_EQ( + "f38028d50" "m8892s4446" "m546s546m546s546m546s546m546s1664m546s1664m546s546m546s546m546s546" "m546s1664m546s1664m546s1664m546s546m546s546m546s1664m546s1664m546s1664" @@ -324,6 +334,7 @@ TEST(TestSendPronto, NormalPlusRepeatSequence) { EXPECT_EQ(0x18, irsend.capture.address); EXPECT_EQ(0x8, irsend.capture.command); EXPECT_EQ( + "f38028d50" "m8892s4446" "m546s546m546s546m546s546m546s1664m546s1664m546s546m546s546m546s546" "m546s1664m546s1664m546s1664m546s546m546s546m546s1664m546s1664m546s1664" @@ -344,6 +355,7 @@ TEST(TestSendPronto, NormalPlusRepeatSequence) { EXPECT_EQ(0x18, irsend.capture.address); EXPECT_EQ(0x8, irsend.capture.command); EXPECT_EQ( + "f38028d50" "m8892s4446" "m546s546m546s546m546s546m546s1664m546s1664m546s546m546s546m546s546" "m546s1664m546s1664m546s1664m546s546m546s546m546s1664m546s1664m546s1664" diff --git a/lib/IRremoteESP8266-2.5.2.03/test/ir_RC5_RC6_test.cpp b/lib/IRremoteESP8266-2.6.0/test/ir_RC5_RC6_test.cpp similarity index 98% rename from lib/IRremoteESP8266-2.5.2.03/test/ir_RC5_RC6_test.cpp rename to lib/IRremoteESP8266-2.6.0/test/ir_RC5_RC6_test.cpp index e8aa9bfc1a85..da9fa027cb62 100644 --- a/lib/IRremoteESP8266-2.5.2.03/test/ir_RC5_RC6_test.cpp +++ b/lib/IRremoteESP8266-2.6.0/test/ir_RC5_RC6_test.cpp @@ -79,6 +79,7 @@ TEST(TestSendRC5, SendDataOnly) { irsend.reset(); irsend.sendRC5(0x0, kRC5Bits); EXPECT_EQ( + "f36000d25" "m889s889m1778s889m889s889m889s889m889s889m889s889m889" "s889m889s889m889s889m889s889m889s889m889s889m889s90664", irsend.outputStr()); @@ -86,6 +87,7 @@ TEST(TestSendRC5, SendDataOnly) { irsend.reset(); irsend.sendRC5(0x1AAA, kRC5Bits); EXPECT_EQ( + "f36000d25" "m889s889m889s889m1778s1778m1778s1778m1778s1778" "m1778s1778m1778s1778m1778s90664", irsend.outputStr()); @@ -93,6 +95,7 @@ TEST(TestSendRC5, SendDataOnly) { irsend.reset(); irsend.sendRC5(0x175, kRC5Bits); EXPECT_EQ( + "f36000d25" "m889s889m1778s889m889s889m889s1778m1778s1778" "m889s889m889s889m1778s1778m1778s1778m889s89775", irsend.outputStr()); @@ -100,6 +103,7 @@ TEST(TestSendRC5, SendDataOnly) { irsend.reset(); irsend.sendRC5(0x3FFF, kRC5Bits); EXPECT_EQ( + "f36000d25" "m889s889m889s889m889s889m889s889m889s889m889s889m889s889" "m889s889m889s889m889s889m889s889m889s889m889s889m889s89775", irsend.outputStr()); @@ -107,6 +111,7 @@ TEST(TestSendRC5, SendDataOnly) { irsend.reset(); irsend.sendRC5(0x0, kRC5XBits); EXPECT_EQ( + "f36000d25" "m889s889m1778s889m889s889m889s889m889s889m889s889m889" "s889m889s889m889s889m889s889m889s889m889s889m889s90664", irsend.outputStr()); @@ -114,6 +119,7 @@ TEST(TestSendRC5, SendDataOnly) { irsend.reset(); irsend.sendRC5(0x1AAA, kRC5XBits); EXPECT_EQ( + "f36000d25" "m1778s1778m1778s1778m1778s1778m1778" "s1778m1778s1778m1778s1778m1778s90664", irsend.outputStr()); @@ -121,6 +127,7 @@ TEST(TestSendRC5, SendDataOnly) { irsend.reset(); irsend.sendRC5(0x175, kRC5XBits); EXPECT_EQ( + "f36000d25" "m889s889m1778s889m889s889m889s1778m1778s1778" "m889s889m889s889m1778s1778m1778s1778m889s89775", irsend.outputStr()); @@ -128,6 +135,7 @@ TEST(TestSendRC5, SendDataOnly) { irsend.reset(); irsend.sendRC5(0x3FFF, kRC5XBits); EXPECT_EQ( + "f36000d25" "m1778s1778m889s889m889s889m889s889m889s889m889s889m889" "s889m889s889m889s889m889s889m889s889m889s889m889s89775", irsend.outputStr()); @@ -141,6 +149,7 @@ TEST(TestSendRC5, SendWithRepeats) { irsend.reset(); irsend.sendRC5(0x175, kRC5Bits, 1); EXPECT_EQ( + "f36000d25" "m889s889m1778s889m889s889m889s1778m1778s1778" "m889s889m889s889m1778s1778m1778s1778m889s90664" "m889s889m1778s889m889s889m889s1778m1778s1778" @@ -150,6 +159,7 @@ TEST(TestSendRC5, SendWithRepeats) { irsend.reset(); irsend.sendRC5(0x175, kRC5Bits, 2); EXPECT_EQ( + "f36000d25" "m889s889m1778s889m889s889m889s1778m1778s1778" "m889s889m889s889m1778s1778m1778s1778m889s90664" "m889s889m1778s889m889s889m889s1778m1778s1778" @@ -161,6 +171,7 @@ TEST(TestSendRC5, SendWithRepeats) { irsend.reset(); irsend.sendRC5(0x175, kRC5XBits, 1); EXPECT_EQ( + "f36000d25" "m889s889m1778s889m889s889m889s1778m1778s1778" "m889s889m889s889m1778s1778m1778s1778m889s90664" "m889s889m1778s889m889s889m889s1778m1778s1778" @@ -170,6 +181,7 @@ TEST(TestSendRC5, SendWithRepeats) { irsend.reset(); irsend.sendRC5(0x1175, kRC5XBits, 2); EXPECT_EQ( + "f36000d25" "m1778s889m889s889m889s889m889s1778m1778s1778" "m889s889m889s889m1778s1778m1778s1778m889s90664" "m1778s889m889s889m889s889m889s1778m1778s1778" @@ -444,6 +456,7 @@ TEST(TestSendRC6, SendMode0DataOnly) { irsend.reset(); irsend.sendRC6(0x0); EXPECT_EQ( + "f36000d33" "m2664s888" "m444s888m444s444m444s444m444s888m888s444m444s444m444s444" "m444s444m444s444m444s444m444s444m444s444m444s444m444s444" @@ -454,6 +467,7 @@ TEST(TestSendRC6, SendMode0DataOnly) { irsend.reset(); irsend.sendRC6(0x1FFFF); EXPECT_EQ( + "f36000d33" "m2664s888" "m444s888m444s444m444s444m1332s888m444s444m444s444m444s444" "m444s444m444s444m444s444m444s444m444s444m444s444m444s444" @@ -464,6 +478,7 @@ TEST(TestSendRC6, SendMode0DataOnly) { irsend.reset(); irsend.sendRC6(0x15555); EXPECT_EQ( + "f36000d33" "m2664s888" "m444s888m444s444m444s444m1332s1332m888s888m888s888" "m888s888m888s888m888s888m888s888m888s888m888" @@ -479,6 +494,7 @@ TEST(TestSendRC6, Send36BitDataOnly) { irsend.reset(); irsend.sendRC6(0x0, kRC6_36Bits); EXPECT_EQ( + "f36000d33" "m2664s888" "m444s888m444s444m444s444m444" "s888m888" @@ -492,6 +508,7 @@ TEST(TestSendRC6, Send36BitDataOnly) { irsend.reset(); irsend.sendRC6(0xFFFFFFFFF, kRC6_36Bits); EXPECT_EQ( + "f36000d33" "m2664s888" "m444s444m444s444m444s444m444s444" "m888s888" @@ -505,6 +522,7 @@ TEST(TestSendRC6, Send36BitDataOnly) { irsend.reset(); irsend.sendRC6(0xAAAAAAAAAA, kRC6_36Bits); EXPECT_EQ( + "f36000d33" "m2664s888m444s444m444s888m888" "s1332m1332" "s888m888s888m888s888m888s888m888s888m888s888m888s888m888s888m888s888m888" @@ -514,6 +532,7 @@ TEST(TestSendRC6, Send36BitDataOnly) { irsend.reset(); irsend.sendRC6(0xC800F740C, kRC6_36Bits); // Xbox 360 OnOff code EXPECT_EQ( + "f36000d33" "m2664s888" "m444s444m444s444m444s888m444" "s888m1332" @@ -526,6 +545,7 @@ TEST(TestSendRC6, Send36BitDataOnly) { irsend.sendRC6(irsend.toggleRC6(0xC800F740C, kRC6_36Bits), kRC6_36Bits); // Xbox 360 OnOff code (toggled) EXPECT_EQ( + "f36000d33" "m2664s888" "m444s444m444s444m444s888m444" "s888m1332" @@ -544,6 +564,7 @@ TEST(TestSendRC6, SendMode0WithRepeats) { irsend.reset(); irsend.sendRC6(0x175, kRC6Mode0Bits, 0); EXPECT_EQ( + "f36000d33" "m2664s888" "m444s888m444s444m444s444m444" "s888m888" @@ -554,6 +575,7 @@ TEST(TestSendRC6, SendMode0WithRepeats) { irsend.reset(); irsend.sendRC6(0x175, kRC6Mode0Bits, 1); EXPECT_EQ( + "f36000d33" "m2664s888" "m444s888m444s444m444s444m444" "s888m888" @@ -569,6 +591,7 @@ TEST(TestSendRC6, SendMode0WithRepeats) { irsend.reset(); irsend.sendRC6(0x175, kRC6Mode0Bits, 2); EXPECT_EQ( + "f36000d33" "m2664s888" "m444s888m444s444m444s444m444" "s888m888" @@ -595,6 +618,7 @@ TEST(TestSendRC6, Send36BitWithRepeats) { irsend.reset(); irsend.sendRC6(0x175, kRC6_36Bits, 0); EXPECT_EQ( + "f36000d33" "m2664s888" "m444s888m444s444m444s444m444" "s888m888" @@ -607,6 +631,7 @@ TEST(TestSendRC6, Send36BitWithRepeats) { irsend.reset(); irsend.sendRC6(0x175, kRC6_36Bits, 1); EXPECT_EQ( + "f36000d33" "m2664s888" "m444s888m444s444m444s444m444" "s888m888" @@ -626,6 +651,7 @@ TEST(TestSendRC6, Send36BitWithRepeats) { irsend.reset(); irsend.sendRC6(0x175, kRC6_36Bits, 2); EXPECT_EQ( + "f36000d33" "m2664s888" "m444s888m444s444m444s444m444" "s888m888" diff --git a/lib/IRremoteESP8266-2.5.2.03/test/ir_RCMM_test.cpp b/lib/IRremoteESP8266-2.6.0/test/ir_RCMM_test.cpp similarity index 98% rename from lib/IRremoteESP8266-2.5.2.03/test/ir_RCMM_test.cpp rename to lib/IRremoteESP8266-2.6.0/test/ir_RCMM_test.cpp index 028dbd8b3b6f..22306a59b996 100644 --- a/lib/IRremoteESP8266-2.5.2.03/test/ir_RCMM_test.cpp +++ b/lib/IRremoteESP8266-2.6.0/test/ir_RCMM_test.cpp @@ -14,6 +14,7 @@ TEST(TestSendRCMM, SendDataOnly) { irsend.reset(); irsend.sendRCMM(0xe0a600); EXPECT_EQ( + "f36000d33" "m416s277" "m166s777m166s611m166s277m166s277" "m166s611m166s611m166s444m166s611m166s277m166s277m166s277m166s277" @@ -22,6 +23,7 @@ TEST(TestSendRCMM, SendDataOnly) { irsend.reset(); irsend.sendRCMM(0x28e0a600UL, 32); EXPECT_EQ( + "f36000d33" "m416s277" "m166s277m166s611m166s611m166s277m166s777m166s611m166s277m166s277" "m166s611m166s611m166s444m166s611m166s277m166s277m166s277m166s277" @@ -37,6 +39,7 @@ TEST(TestSendRCMM, SendWithRepeats) { irsend.reset(); irsend.sendRCMM(0x28e0a600, 32, 2); // 2 repeats. EXPECT_EQ( + "f36000d33" "m416s277" "m166s277m166s611m166s611m166s277m166s777m166s611m166s277m166s277" "m166s611m166s611m166s444m166s611m166s277m166s277m166s277m166s277" @@ -60,6 +63,7 @@ TEST(TestSendRCMM, SendUnusualSize) { irsend.reset(); irsend.sendRCMM(0xE0, 8); EXPECT_EQ( + "f36000d33" "m416s277" "m166s777m166s611m166s277m166s277" "m166s24313", @@ -67,6 +71,7 @@ TEST(TestSendRCMM, SendUnusualSize) { irsend.reset(); irsend.sendRCMM(0x28e0a60000UL, 40); EXPECT_EQ( + "f36000d33" "m416s277" "m166s277m166s611m166s611m166s277m166s777m166s611m166s277m166s277" "m166s611m166s611m166s444m166s611m166s277m166s277m166s277m166s277" diff --git a/lib/IRremoteESP8266-2.5.2.03/test/ir_Samsung_test.cpp b/lib/IRremoteESP8266-2.6.0/test/ir_Samsung_test.cpp similarity index 77% rename from lib/IRremoteESP8266-2.5.2.03/test/ir_Samsung_test.cpp rename to lib/IRremoteESP8266-2.6.0/test/ir_Samsung_test.cpp index 9ee1fcabbc6a..8670ac4abbe0 100644 --- a/lib/IRremoteESP8266-2.5.2.03/test/ir_Samsung_test.cpp +++ b/lib/IRremoteESP8266-2.6.0/test/ir_Samsung_test.cpp @@ -1,4 +1,4 @@ -// Copyright 2017 David Conran +// Copyright 2017, 2018, 2019 David Conran #include "ir_Samsung.h" #include "IRrecv.h" @@ -7,6 +7,13 @@ #include "IRsend_test.h" #include "gtest/gtest.h" + +// General housekeeping +TEST(TestSamsung, Housekeeping) { + ASSERT_EQ("SAMSUNG", typeToString(SAMSUNG)); + ASSERT_FALSE(hasACState(SAMSUNG)); +} + // Tests for sendSAMSUNG(). // Test sending typical data only. @@ -17,6 +24,7 @@ TEST(TestSendSamsung, SendDataOnly) { irsend.reset(); irsend.sendSAMSUNG(0xE0E09966); // Samsung TV Power On. EXPECT_EQ( + "f38000d33" "m4480s4480" "m560s1680m560s1680m560s1680m560s560m560s560m560s560m560s560" "m560s560m560s1680m560s1680m560s1680m560s560m560s560m560s560" @@ -36,6 +44,7 @@ TEST(TestSendSamsung, SendWithRepeats) { irsend.reset(); irsend.sendSAMSUNG(0xE0E09966, kSamsungBits, 1); // 1 repeat. EXPECT_EQ( + "f38000d33" "m4480s4480" "m560s1680m560s1680m560s1680m560s560m560s560m560s560m560s560" "m560s560m560s1680m560s1680m560s1680m560s560m560s560m560s560" @@ -51,6 +60,7 @@ TEST(TestSendSamsung, SendWithRepeats) { irsend.outputStr()); irsend.sendSAMSUNG(0xE0E09966, kSamsungBits, 2); // 2 repeats. EXPECT_EQ( + "f38000d33" "m4480s4480" "m560s1680m560s1680m560s1680m560s560m560s560m560s560m560s560" "m560s560m560s1680m560s1680m560s1680m560s560m560s560m560s560" @@ -281,17 +291,24 @@ TEST(TestDecodeSamsung, FailToDecodeNonSamsungExample) { ASSERT_FALSE(irrecv.decodeSAMSUNG(&irsend.capture, kSamsungBits, false)); } +// General housekeeping +TEST(TestSamsungAC, Housekeeping) { + ASSERT_EQ("SAMSUNG_AC", typeToString(SAMSUNG_AC)); + ASSERT_TRUE(hasACState(SAMSUNG_AC)); +} + // Tests for sendSamsungAC(). // Test sending typical data only. TEST(TestSendSamsungAC, SendDataOnly) { IRsendTest irsend(0); irsend.begin(); - uint8_t data[kSamsungAcStateLength] = {0x02, 0x92, 0x0F, 0x00, 0x00, - 0x00, 0xF0, 0x01, 0x02, 0xAF, - 0x71, 0x00, 0x15, 0xF0}; + uint8_t data[kSamsungAcStateLength] = { + 0x02, 0x92, 0x0F, 0x00, 0x00, 0x00, 0xF0, + 0x01, 0x02, 0xAF, 0x71, 0x00, 0x15, 0xF0}; irsend.sendSamsungAC(data); EXPECT_EQ( + "f38000d50" "m690s17844" "m3086s8864" "m586s436m586s1432m586s436m586s436m586s436m586s436m586s436m586s436" @@ -320,10 +337,12 @@ TEST(TestSendSamsungAC, SendExtendedData) { irsend.begin(); // "Off" message. uint8_t data[kSamsungAcExtendedStateLength] = { - 0x02, 0xB2, 0x0F, 0x00, 0x00, 0x00, 0xC0, 0x01, 0xD2, 0x0F, 0x00, - 0x00, 0x00, 0x00, 0x01, 0x02, 0xFF, 0x71, 0x80, 0x11, 0xC0}; + 0x02, 0xB2, 0x0F, 0x00, 0x00, 0x00, 0xC0, + 0x01, 0xD2, 0x0F, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x02, 0xFF, 0x71, 0x80, 0x11, 0xC0}; irsend.sendSamsungAC(data, kSamsungAcExtendedStateLength); EXPECT_EQ( + "f38000d50" "m690s17844" "m3086s8864" "m586s436m586s1432m586s436m586s436m586s436m586s436m586s436m586s436" @@ -358,15 +377,16 @@ TEST(TestSendSamsungAC, SendExtendedData) { // Tests for IRSamsungAc class. TEST(TestIRSamsungAcClass, SetAndGetRaw) { - uint8_t expectedState[kSamsungAcStateLength] = {0x02, 0x92, 0x0F, 0x00, 0x00, - 0x00, 0xF0, 0x01, 0xE2, 0xFE, - 0x71, 0x40, 0x11, 0xF0}; + uint8_t expectedState[kSamsungAcStateLength] = { + 0x02, 0x92, 0x0F, 0x00, 0x00, 0x00, 0xF0, + 0x01, 0xE2, 0xFE, 0x71, 0x40, 0x11, 0xF0}; IRSamsungAc samsung(0); samsung.setRaw(expectedState); EXPECT_STATE_EQ(expectedState, samsung.getRaw(), kSamsungAcBits); uint8_t extendedState[kSamsungAcExtendedStateLength] = { - 0x02, 0x92, 0x0F, 0x00, 0x00, 0x00, 0xF0, 0x01, 0xD2, 0x0F, 0x00, - 0x00, 0x00, 0x00, 0x01, 0xE2, 0xFE, 0x71, 0x40, 0x11, 0xF0}; + 0x02, 0x92, 0x0F, 0x00, 0x00, 0x00, 0xF0, + 0x01, 0xD2, 0x0F, 0x00, 0x00, 0x00, 0x00, + 0x01, 0xE2, 0xFE, 0x71, 0x40, 0x11, 0xF0}; samsung.setRaw(extendedState, kSamsungAcExtendedStateLength); // We should NOT get the extended state back. EXPECT_STATE_EQ(expectedState, samsung.getRaw(), kSamsungAcBits); @@ -496,9 +516,15 @@ TEST(TestIRSamsungAcClass, ChecksumCalculation) { const uint8_t originalstate[kSamsungAcStateLength] = { 0x02, 0x92, 0x0F, 0x00, 0x00, 0x00, 0xF0, 0x01, 0x02, 0xAF, 0x71, 0x00, 0x15, 0xF0}; - uint8_t examplestate[kSamsungAcStateLength] = {0x02, 0x92, 0x0F, 0x00, 0x00, - 0x00, 0xF0, 0x01, 0x02, 0xAF, - 0x71, 0x00, 0x15, 0xF0}; + uint8_t examplestate[kSamsungAcStateLength] = { + 0x02, 0x92, 0x0F, 0x00, 0x00, 0x00, 0xF0, + 0x01, 0x02, 0xAF, 0x71, 0x00, 0x15, 0xF0}; + + const uint8_t extendedstate[kSamsungAcExtendedStateLength] = { + 0x02, 0xA9, 0x0F, 0x00, 0x00, 0x00, 0xC0, + 0x01, 0xC9, 0x0F, 0x00, 0x00, 0x00, 0x00, + 0x01, 0xF9, 0xCE, 0x71, 0xE0, 0x41, 0xC0}; + EXPECT_TRUE(IRSamsungAc::validChecksum(examplestate)); EXPECT_EQ(0, IRSamsungAc::calcChecksum(examplestate)); @@ -516,6 +542,9 @@ TEST(TestIRSamsungAcClass, ChecksumCalculation) { examplestate[11] = 0x01; EXPECT_FALSE(IRSamsungAc::validChecksum(examplestate)); EXPECT_EQ(0xF, IRSamsungAc::calcChecksum(examplestate)); + + // Check an extended state is valid. + EXPECT_TRUE(IRSamsungAc::validChecksum(extendedstate, 21)); } TEST(TestIRSamsungAcClass, HumanReadable) { @@ -585,9 +614,9 @@ TEST(TestDecodeSamsungAC, SyntheticDecode) { IRrecv irrecv(0); irsend.begin(); irsend.reset(); - uint8_t expectedState[kSamsungAcStateLength] = {0x02, 0x92, 0x0F, 0x00, 0x00, - 0x00, 0xF0, 0x01, 0x02, 0xAF, - 0x71, 0x00, 0x15, 0xF0}; + uint8_t expectedState[kSamsungAcStateLength] = { + 0x02, 0x92, 0x0F, 0x00, 0x00, 0x00, 0xF0, + 0x01, 0x02, 0xAF, 0x71, 0x00, 0x15, 0xF0}; // Synthesised Normal Samsung A/C message. irsend.sendSamsungAC(expectedState); irsend.makeDecodeResult(); @@ -626,9 +655,9 @@ TEST(TestDecodeSamsungAC, DecodeRealExample) { 584, 1406, 586, 410, 584, 1384, 606, 410, 586, 410, 584, 408, 586, 408, 586, 408, 586, 408, 588, 410, 584, 1408, 590, 1400, 592, 1398, 602, 1388, 612}; - uint8_t expectedState[kSamsungAcStateLength] = {0x02, 0x92, 0x0F, 0x00, 0x00, - 0x00, 0xF0, 0x01, 0x02, 0xAF, - 0x71, 0x00, 0x15, 0xF0}; + uint8_t expectedState[kSamsungAcStateLength] = { + 0x02, 0x92, 0x0F, 0x00, 0x00, 0x00, 0xF0, + 0x01, 0x02, 0xAF, 0x71, 0x00, 0x15, 0xF0}; irsend.sendRaw(rawData, 233, 38000); irsend.makeDecodeResult(); @@ -675,9 +704,9 @@ TEST(TestDecodeSamsungAC, DecodeRealExample2) { 560, 436, 486, 510, 566, 1400, 598, 420, 576, 418, 582, 414, 586, 410, 584, 410, 584, 410, 586, 410, 584, 1382, 608, 1384, 606, 1384, 606, 1408, 600}; - uint8_t expectedState[kSamsungAcStateLength] = {0x02, 0x92, 0x0F, 0x00, 0x00, - 0x00, 0xF0, 0x01, 0xE2, 0xFE, - 0x71, 0x80, 0x11, 0xF0}; + uint8_t expectedState[kSamsungAcStateLength] = { + 0x02, 0x92, 0x0F, 0x00, 0x00, 0x00, 0xF0, + 0x01, 0xE2, 0xFE, 0x71, 0x80, 0x11, 0xF0}; irsend.sendRaw(rawData, 233, 38000); irsend.makeDecodeResult(); @@ -735,8 +764,9 @@ TEST(TestDecodeSamsungAC, DecodePowerOnSample) { 518, 480, 520, 480, 520, 1454, 568, 1430, 566, 1432, 566, 1454, 594}; uint8_t expectedState[kSamsungAcExtendedStateLength] = { - 0x02, 0x92, 0x0F, 0x00, 0x00, 0x00, 0xF0, 0x01, 0xD2, 0x0F, 0x00, - 0x00, 0x00, 0x00, 0x01, 0xE2, 0xFE, 0x71, 0x80, 0x11, 0xF0}; + 0x02, 0x92, 0x0F, 0x00, 0x00, 0x00, 0xF0, + 0x01, 0xD2, 0x0F, 0x00, 0x00, 0x00, 0x00, + 0x01, 0xE2, 0xFE, 0x71, 0x80, 0x11, 0xF0}; irsend.sendRaw(rawData, 349, 38000); irsend.makeDecodeResult(); @@ -794,8 +824,9 @@ TEST(TestDecodeSamsungAC, DecodePowerOffSample) { 608}; uint8_t expectedState[kSamsungAcExtendedStateLength] = { - 0x02, 0xB2, 0x0F, 0x00, 0x00, 0x00, 0xC0, 0x01, 0xD2, 0x0F, 0x00, - 0x00, 0x00, 0x00, 0x01, 0x02, 0xFF, 0x71, 0x80, 0x11, 0xC0}; + 0x02, 0xB2, 0x0F, 0x00, 0x00, 0x00, 0xC0, + 0x01, 0xD2, 0x0F, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x02, 0xFF, 0x71, 0x80, 0x11, 0xC0}; irsend.sendRaw(rawData, 349, 38000); irsend.makeDecodeResult(); @@ -840,9 +871,9 @@ TEST(TestDecodeSamsungAC, DecodeHeatSample) { 512, 482, 512, 482, 510, 484, 510, 484, 510, 1478, 512, 1504, 488, 1480, 560, 1454, 514}; - uint8_t expectedState[kSamsungAcStateLength] = {0x02, 0x92, 0x0F, 0x00, 0x00, - 0x00, 0xF0, 0x01, 0x02, 0xAF, - 0x71, 0x10, 0x41, 0xF0}; + uint8_t expectedState[kSamsungAcStateLength] = { + 0x02, 0x92, 0x0F, 0x00, 0x00, 0x00, 0xF0, + 0x01, 0x02, 0xAF, 0x71, 0x10, 0x41, 0xF0}; irsend.sendRaw(rawData, 233, 38000); irsend.makeDecodeResult(); @@ -887,9 +918,9 @@ TEST(TestDecodeSamsungAC, DecodeCoolSample) { 584, 412, 584, 408, 586, 410, 586, 408, 586, 1404, 586, 1408, 582, 1410, 562, 1426, 610}; - uint8_t expectedState[kSamsungAcStateLength] = {0x02, 0x92, 0x0F, 0x00, 0x00, - 0x00, 0xF0, 0x01, 0xE2, 0xFE, - 0x71, 0x40, 0x11, 0xF0}; + uint8_t expectedState[kSamsungAcStateLength] = { + 0x02, 0x92, 0x0F, 0x00, 0x00, 0x00, 0xF0, + 0x01, 0xE2, 0xFE, 0x71, 0x40, 0x11, 0xF0}; irsend.sendRaw(rawData, 233, 38000); irsend.makeDecodeResult(); @@ -905,3 +936,195 @@ TEST(TestDecodeSamsungAC, DecodeCoolSample) { "Beep: Off, Clean: Off, Quiet: Off", samsung.toString()); } + +TEST(TestDecodeSamsungAC, Issue604DecodeExtended) { + IRsendTest irsend(0); + IRrecv irrecv(0); + irsend.begin(); + + irsend.reset(); + uint16_t sendOff[349] = { + 642, 17730, 3056, 8916, 542, 448, 552, 1440, 552, 444, 552, 444, + 552, 444, 552, 440, 556, 440, 556, 440, 556, 1436, 552, 444, + 552, 444, 552, 1440, 548, 470, 526, 1464, 470, 526, 516, 1470, + 552, 1440, 552, 1440, 550, 1436, 556, 1434, 552, 444, 552, 444, + 552, 444, 552, 442, 552, 444, 546, 470, 526, 470, 526, 470, + 470, 524, 518, 474, 548, 448, 552, 444, 552, 442, 552, 444, + 550, 444, 552, 440, 556, 440, 556, 438, 556, 440, 552, 442, + 552, 444, 552, 442, 552, 444, 550, 470, 526, 466, 524, 470, + 470, 524, 470, 524, 518, 476, 548, 444, 552, 444, 556, 440, + 552, 442, 552, 444, 550, 1436, 556, 1436, 552, 2946, 3026, 8918, + 550, 1440, 552, 444, 548, 468, 526, 468, 470, 526, 470, 526, + 542, 452, 548, 444, 552, 1440, 550, 444, 552, 444, 552, 1436, + 556, 438, 552, 442, 552, 1440, 552, 1440, 552, 1460, 526, 1464, + 470, 1516, 548, 1444, 552, 444, 552, 442, 552, 444, 552, 438, + 556, 440, 556, 440, 552, 444, 552, 444, 552, 444, 552, 444, + 552, 444, 548, 448, 546, 470, 526, 468, 526, 470, 470, 524, + 520, 470, 548, 448, 552, 444, 552, 444, 552, 444, 552, 444, + 552, 438, 556, 440, 556, 438, 552, 444, 552, 442, 552, 444, + 552, 444, 552, 444, 552, 470, 526, 466, 526, 470, 470, 524, + 518, 478, 546, 448, 552, 2920, 3052, 8916, 552, 1434, 556, 440, + 556, 438, 552, 444, 552, 442, 552, 442, 552, 442, 552, 444, + 548, 1444, 548, 470, 526, 470, 522, 1466, 470, 1520, 548, 1438, + 556, 1436, 552, 1440, 552, 442, 552, 1436, 552, 1440, 552, 1440, + 552, 442, 552, 470, 522, 1466, 526, 1466, 470, 1516, 552, 444, + 552, 442, 552, 444, 552, 1436, 556, 1436, 552, 1440, 550, 444, + 552, 444, 552, 444, 548, 448, 546, 448, 548, 470, 526, 1462, + 474, 1518, 548, 1440, 552, 1438, 556, 440, 550, 444, 552, 444, + 552, 444, 552, 440, 556, 1436, 552, 444, 552, 444, 552, 444, + 550, 470, 522, 470, 524, 470, 470, 524, 518, 1474, 548, 1440, + 556}; + + uint8_t expectedState[kSamsungAcExtendedStateLength] = { + 0x02, 0xA9, 0x0F, 0x00, 0x00, 0x00, 0xC0, + 0x01, 0xC9, 0x0F, 0x00, 0x00, 0x00, 0x00, + 0x01, 0xF9, 0xCE, 0x71, 0xE0, 0x41, 0xC0}; + + irsend.sendRaw(sendOff, 349, 38000); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(SAMSUNG_AC, irsend.capture.decode_type); + EXPECT_EQ(kSamsungAcExtendedBits, irsend.capture.bits); + EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits); + + IRSamsungAc samsung(0); + samsung.setRaw(irsend.capture.state, irsend.capture.bits / 8); + EXPECT_EQ( + "Power: Off, Mode: 4 (HEAT), Temp: 30C, Fan: 0 (AUTO), Swing: Off, " + "Beep: Off, Clean: Off, Quiet: Off", + samsung.toString()); +} + +TEST(TestSendSamsung36, SendDataOnly) { + IRsendTest irsend(0); + irsend.begin(); + + irsend.reset(); + irsend.sendSamsung36(0); + EXPECT_EQ( + "f38000d50" + "m4480s4480" + "m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560" + "m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560" + "m560s4480" + "m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560" + "m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560" + "m560s560m560s560m560s560m560s560" + "m560s26880", + irsend.outputStr()); + irsend.sendSamsung36(0x400E00FF); + EXPECT_EQ( + "f38000d50" + "m4480s4480" + "m560s560m560s560m560s560m560s560m560s560m560s1680m560s560m560s560" + "m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560" + "m560s4480" + "m560s1680m560s1680m560s1680m560s560m560s560m560s560m560s560m560s560" + "m560s560m560s560m560s560m560s560m560s1680m560s1680m560s1680m560s1680" + "m560s1680m560s1680m560s1680m560s1680" + "m560s26880", + irsend.outputStr()); + irsend.reset(); +} + +// General housekeeping +TEST(TestSamsung36, Housekeeping) { + ASSERT_EQ("SAMSUNG36", typeToString(SAMSUNG36)); + ASSERT_FALSE(hasACState(SAMSUNG36)); +} + +// Test sending with different repeats. +TEST(TestSendSamsung36, SendWithRepeats) { + IRsendTest irsend(0); + irsend.begin(); + + irsend.reset(); + irsend.sendSamsung36(0x400E00FF, kSamsung36Bits, 1); // 1 repeat. + EXPECT_EQ( + "f38000d50" + "m4480s4480" + "m560s560m560s560m560s560m560s560m560s560m560s1680m560s560m560s560" + "m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560" + "m560s4480" + "m560s1680m560s1680m560s1680m560s560m560s560m560s560m560s560m560s560" + "m560s560m560s560m560s560m560s560m560s1680m560s1680m560s1680m560s1680" + "m560s1680m560s1680m560s1680m560s1680" + "m560s26880" + "m4480s4480" + "m560s560m560s560m560s560m560s560m560s560m560s1680m560s560m560s560" + "m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560" + "m560s4480" + "m560s1680m560s1680m560s1680m560s560m560s560m560s560m560s560m560s560" + "m560s560m560s560m560s560m560s560m560s1680m560s1680m560s1680m560s1680" + "m560s1680m560s1680m560s1680m560s1680" + "m560s26880", + irsend.outputStr()); + irsend.sendSamsung36(0x400E00FF, kSamsung36Bits, 2); // 2 repeats. + EXPECT_EQ( + "f38000d50" + "m4480s4480" + "m560s560m560s560m560s560m560s560m560s560m560s1680m560s560m560s560" + "m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560" + "m560s4480" + "m560s1680m560s1680m560s1680m560s560m560s560m560s560m560s560m560s560" + "m560s560m560s560m560s560m560s560m560s1680m560s1680m560s1680m560s1680" + "m560s1680m560s1680m560s1680m560s1680" + "m560s26880" + "m4480s4480" + "m560s560m560s560m560s560m560s560m560s560m560s1680m560s560m560s560" + "m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560" + "m560s4480" + "m560s1680m560s1680m560s1680m560s560m560s560m560s560m560s560m560s560" + "m560s560m560s560m560s560m560s560m560s1680m560s1680m560s1680m560s1680" + "m560s1680m560s1680m560s1680m560s1680" + "m560s26880" + "m4480s4480" + "m560s560m560s560m560s560m560s560m560s560m560s1680m560s560m560s560" + "m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560" + "m560s4480" + "m560s1680m560s1680m560s1680m560s560m560s560m560s560m560s560m560s560" + "m560s560m560s560m560s560m560s560m560s1680m560s1680m560s1680m560s1680" + "m560s1680m560s1680m560s1680m560s1680" + "m560s26880", + irsend.outputStr()); +} + +TEST(TestDecodeSamsung36, RealExample) { + IRsendTest irsend(0); + IRrecv irrecv(0); + irsend.begin(); + + irsend.reset(); + uint16_t rawData[77] = { + 4542, 4438, 568, 432, 562, 436, 536, 462, 538, 460, 538, 460, 564, 1434, + 564, 434, 534, 464, 536, 462, 562, 436, 536, 464, 564, 432, 538, 462, 536, + 464, 534, 464, 564, 420, 566, 4414, 538, 1462, 566, 1432, 562, 1436, 536, + 462, 564, 436, 562, 436, 560, 436, 562, 436, 562, 436, 560, 438, 536, 462, + 562, 436, 562, 1436, 562, 1434, 536, 1462, 564, 1434, 562, 1436, 564, + 1436, 534, 1462, 534, 1464, 536}; // UNKNOWN E4CD1208 + + irsend.sendRaw(rawData, 77, 38000); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(SAMSUNG36, irsend.capture.decode_type); + EXPECT_EQ(kSamsung36Bits, irsend.capture.bits); + EXPECT_EQ(0x400E00FF, irsend.capture.value); + EXPECT_EQ(0xE00FF, irsend.capture.command); + EXPECT_EQ(0x400, irsend.capture.address); +} + +TEST(TestDecodeSamsung36, SyntheticExample) { + IRsendTest irsend(0); + IRrecv irrecv(0); + irsend.begin(); + irsend.reset(); + + irsend.sendSamsung36(0x400E00FF); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decodeSamsung36(&irsend.capture)); + ASSERT_EQ(SAMSUNG36, irsend.capture.decode_type); + EXPECT_EQ(kSamsung36Bits, irsend.capture.bits); + EXPECT_EQ(0x400E00FF, irsend.capture.value); + EXPECT_EQ(0xE00FF, irsend.capture.command); + EXPECT_EQ(0x400, irsend.capture.address); +} diff --git a/lib/IRremoteESP8266-2.5.2.03/test/ir_Sanyo_test.cpp b/lib/IRremoteESP8266-2.6.0/test/ir_Sanyo_test.cpp similarity index 99% rename from lib/IRremoteESP8266-2.5.2.03/test/ir_Sanyo_test.cpp rename to lib/IRremoteESP8266-2.6.0/test/ir_Sanyo_test.cpp index 14c1c7da08df..165e29f17dc2 100644 --- a/lib/IRremoteESP8266-2.5.2.03/test/ir_Sanyo_test.cpp +++ b/lib/IRremoteESP8266-2.6.0/test/ir_Sanyo_test.cpp @@ -27,6 +27,7 @@ TEST(TestEncodeSanyoLC7461, SendDataOnly) { irsend.reset(); irsend.sendSanyoLC7461(0x1D8113F00FF); EXPECT_EQ( + "f38000d33" "m8960s4480" "m560s560m560s1680m560s1680m560s1680m560s560m560s1680m560s1680m560s560" "m560s560m560s560m560s560m560s560m560s560m560s1680m560s560m560s560" @@ -45,6 +46,7 @@ TEST(TestEncodeSanyoLC7461, SendWithRepeats) { irsend.reset(); irsend.sendSanyoLC7461(0x1D8113F00FF, kSanyoLC7461Bits, 1); // 1 repeat. EXPECT_EQ( + "f38000d33" "m8960s4480" "m560s560m560s1680m560s1680m560s1680m560s560m560s1680m560s1680m560s560" "m560s560m560s560m560s560m560s560m560s560m560s1680m560s560m560s560" diff --git a/lib/IRremoteESP8266-2.5.2.03/test/ir_Sharp_test.cpp b/lib/IRremoteESP8266-2.6.0/test/ir_Sharp_test.cpp similarity index 98% rename from lib/IRremoteESP8266-2.5.2.03/test/ir_Sharp_test.cpp rename to lib/IRremoteESP8266-2.6.0/test/ir_Sharp_test.cpp index 8481a464997d..c9d3e851b21b 100644 --- a/lib/IRremoteESP8266-2.5.2.03/test/ir_Sharp_test.cpp +++ b/lib/IRremoteESP8266-2.6.0/test/ir_Sharp_test.cpp @@ -47,6 +47,7 @@ TEST(TestSendSharp, SendDataOnly) { irsend.reset(); irsend.sendSharp(0x11, 0x52); EXPECT_EQ( + "f38000d33" "m260s1820m260s780m260s780m260s780m260s1820m260s780m260s1820m260s780" "m260s1820m260s780m260s780m260s1820m260s780m260s1820m260s780" "m260s43602" @@ -64,6 +65,7 @@ TEST(TestSendSharp, SendWithRepeats) { irsend.reset(); irsend.sendSharp(0x11, 0x52, kSharpBits, 1); // 1 repeat. EXPECT_EQ( + "f38000d33" "m260s1820m260s780m260s780m260s780m260s1820m260s780m260s1820m260s780" "m260s1820m260s780m260s780m260s1820m260s780m260s1820m260s780" "m260s43602" @@ -87,6 +89,7 @@ TEST(TestSendSharp, SendUnusualSize) { irsend.reset(); irsend.sendSharp(0x0, 0x0, 8); EXPECT_EQ( + "f38000d33" "m260s780m260s780m260s780m260s780m260s780m260s780m260s1820m260s780" "m260s43602" "m260s1820m260s1820m260s1820m260s1820m260s1820m260s1820m260s780m260s1820" @@ -96,6 +99,7 @@ TEST(TestSendSharp, SendUnusualSize) { irsend.reset(); irsend.sendSharp(0x0, 0x0, 16); EXPECT_EQ( + "f38000d33" "m260s780m260s780m260s780m260s780m260s780m260s780m260s780m260s780" "m260s780m260s780m260s780m260s780m260s780m260s780m260s1820m260s780" "m260s43602" @@ -115,6 +119,7 @@ TEST(TestSendSharpRaw, SendDataOnly) { irsend.reset(); irsend.sendSharpRaw(0x454A); EXPECT_EQ( + "f38000d33" "m260s1820m260s780m260s780m260s780m260s1820m260s780m260s1820m260s780" "m260s1820m260s780m260s780m260s1820m260s780m260s1820m260s780" "m260s43602" @@ -132,6 +137,7 @@ TEST(TestSendSharpRaw, SendWithRepeats) { irsend.reset(); irsend.sendSharpRaw(0x454A, kSharpBits, 1); // 1 repeat. EXPECT_EQ( + "f38000d33" "m260s1820m260s780m260s780m260s780m260s1820m260s780m260s1820m260s780" "m260s1820m260s780m260s780m260s1820m260s780m260s1820m260s780" "m260s43602" @@ -155,6 +161,7 @@ TEST(TestSendSharpRaw, SendUnusualSize) { irsend.reset(); irsend.sendSharpRaw(0x2, 8); EXPECT_EQ( + "f38000d33" "m260s780m260s780m260s780m260s780m260s780m260s780m260s1820m260s780" "m260s43602" "m260s1820m260s1820m260s1820m260s1820m260s1820m260s1820m260s780m260s1820" @@ -164,6 +171,7 @@ TEST(TestSendSharpRaw, SendUnusualSize) { irsend.reset(); irsend.sendSharpRaw(0x2, 16); EXPECT_EQ( + "f38000d33" "m260s780m260s780m260s780m260s780m260s780m260s780m260s780m260s780" "m260s780m260s780m260s780m260s780m260s780m260s780m260s1820m260s780" "m260s43602" diff --git a/lib/IRremoteESP8266-2.5.2.03/test/ir_Sherwood_test.cpp b/lib/IRremoteESP8266-2.6.0/test/ir_Sherwood_test.cpp similarity index 97% rename from lib/IRremoteESP8266-2.5.2.03/test/ir_Sherwood_test.cpp rename to lib/IRremoteESP8266-2.6.0/test/ir_Sherwood_test.cpp index 22d9ead3826d..f1f41d9c8ce8 100644 --- a/lib/IRremoteESP8266-2.5.2.03/test/ir_Sherwood_test.cpp +++ b/lib/IRremoteESP8266-2.6.0/test/ir_Sherwood_test.cpp @@ -14,6 +14,7 @@ TEST(TestSendSherwood, SendDataOnly) { irsend.reset(); irsend.sendSherwood(0xC1A28877); EXPECT_EQ( + "f38000d33" "m8960s4480m560s1680m560s1680m560s560m560s560m560s560m560s560" "m560s560m560s1680m560s1680m560s560m560s1680m560s560m560s560" "m560s560m560s1680m560s560m560s1680m560s560m560s560m560s560" @@ -31,6 +32,7 @@ TEST(TestSendSherwood, SendDataWithRepeats) { irsend.reset(); irsend.sendSherwood(0xC1A28877, 32, 2); EXPECT_EQ( + "f38000d33" "m8960s4480m560s1680m560s1680m560s560m560s560m560s560m560s560" "m560s560m560s1680m560s1680m560s560m560s1680m560s560m560s560" "m560s560m560s1680m560s560m560s1680m560s560m560s560m560s560" @@ -50,6 +52,7 @@ TEST(TestSendSherwood, SendDataWithZeroRepeats) { irsend.sendSherwood(0xC1A28877, 32, 0); // Should have a single NEC repeat, as we always send one. EXPECT_EQ( + "f38000d33" "m8960s4480m560s1680m560s1680m560s560m560s560m560s560m560s560" "m560s560m560s1680m560s1680m560s560m560s1680m560s560m560s560" "m560s560m560s1680m560s560m560s1680m560s560m560s560m560s560" diff --git a/lib/IRremoteESP8266-2.5.2.03/test/ir_Sony_test.cpp b/lib/IRremoteESP8266-2.6.0/test/ir_Sony_test.cpp similarity index 99% rename from lib/IRremoteESP8266-2.5.2.03/test/ir_Sony_test.cpp rename to lib/IRremoteESP8266-2.6.0/test/ir_Sony_test.cpp index c79ff6175e26..35c3287b0f3b 100644 --- a/lib/IRremoteESP8266-2.5.2.03/test/ir_Sony_test.cpp +++ b/lib/IRremoteESP8266-2.6.0/test/ir_Sony_test.cpp @@ -15,6 +15,7 @@ TEST(TestSendSony, SendDataOnly) { irsend.sendSony(0); // We expect three 20-bit commands to be sent. EXPECT_EQ( + "f40000d33" "m2400s600m600s600m600s600m600s600m600s600m600s600m600s600m600s600" "m600s600m600s600m600s600m600s600m600s600m600s600m600s600m600s600" "m600s600m600s600m600s600m600s600m600s18600" @@ -30,6 +31,7 @@ TEST(TestSendSony, SendDataOnly) { irsend.sendSony(0x240C, kSony20Bits); // We expect three 20-bit commands to be sent. EXPECT_EQ( + "f40000d33" "m2400s600m600s600m600s600m600s600m600s600m600s600m600s600m1200s600" "m600s600m600s600m1200s600m600s600m600s600m600s600m600s600m600s600" "m600s600m1200s600m1200s600m600s600m600s16200" @@ -45,6 +47,7 @@ TEST(TestSendSony, SendDataOnly) { irsend.sendSony(0x240C, kSony15Bits); // We expect three 15-bit commands to be sent. EXPECT_EQ( + "f40000d33" "m2400s600m600s600m1200s600m600s600m600s600m1200s600m600s600" "m600s600m600s600m600s600m600s600m600s600m1200s600m1200s600m600s600" "m600s22200" @@ -60,6 +63,7 @@ TEST(TestSendSony, SendDataOnly) { irsend.sendSony(0xA90, kSony12Bits); // We expect three 15-bit commands to be sent. EXPECT_EQ( + "f40000d33" "m2400s600m1200s600m600s600m1200s600m600s600m1200s600m600s600" "m600s600m1200s600m600s600m600s600m600s600m600s25800" "m2400s600m1200s600m600s600m1200s600m600s600m1200s600m600s600" @@ -77,12 +81,14 @@ TEST(TestSendSony, SendWithDiffRepeats) { irsend.reset(); irsend.sendSony(0x240C, kSony20Bits, 0); // Send a command with 0 repeats. EXPECT_EQ( + "f40000d33" "m2400s600m600s600m600s600m600s600m600s600m600s600m600s600m1200s600" "m600s600m600s600m1200s600m600s600m600s600m600s600m600s600m600s600" "m600s600m1200s600m1200s600m600s600m600s16200", irsend.outputStr()); irsend.sendSony(0x240C, kSony20Bits, 1); // Send a command with 1 repeat. EXPECT_EQ( + "f40000d33" "m2400s600m600s600m600s600m600s600m600s600m600s600m600s600m1200s600" "m600s600m600s600m1200s600m600s600m600s600m600s600m600s600m600s600" "m600s600m1200s600m1200s600m600s600m600s16200" @@ -92,6 +98,7 @@ TEST(TestSendSony, SendWithDiffRepeats) { irsend.outputStr()); irsend.sendSony(0x240C, kSony20Bits, 3); // Send a command with 3 repeats. EXPECT_EQ( + "f40000d33" "m2400s600m600s600m600s600m600s600m600s600m600s600m600s600m1200s600" "m600s600m600s600m1200s600m600s600m600s600m600s600m600s600m600s600" "m600s600m1200s600m1200s600m600s600m600s16200" diff --git a/lib/IRremoteESP8266-2.6.0/test/ir_Tcl_test.cpp b/lib/IRremoteESP8266-2.6.0/test/ir_Tcl_test.cpp new file mode 100644 index 000000000000..249dcc63728a --- /dev/null +++ b/lib/IRremoteESP8266-2.6.0/test/ir_Tcl_test.cpp @@ -0,0 +1,384 @@ +// Copyright 2019 David Conran + +#include "ir_Tcl.h" +#include "IRrecv.h" +#include "IRrecv_test.h" +#include "IRsend.h" +#include "IRsend_test.h" +#include "gtest/gtest.h" + +// General housekeeping +TEST(TestTcl112Ac, Housekeeping) { + ASSERT_EQ("TCL112AC", typeToString(TCL112AC)); + ASSERT_TRUE(hasACState(TCL112AC)); +} + +// Tests for decodeTcl112Ac(). + +// Decode a real Tcl112Ac A/C example from Issue #619 +TEST(TestDecodeTcl112Ac, DecodeRealExample) { + IRsendTest irsend(0); + IRrecv irrecv(0); + irsend.begin(); + + irsend.reset(); + // Tcl112Ac A/C example from Issue #619 On.txt + uint16_t rawData[227] = { + 3030, 1658, 494, 1066, 494, 1068, 498, 320, 494, + 326, 498, 320, 494, 1068, 500, 320, 494, 332, + 494, 1068, 500, 1062, 496, 324, 492, 1044, 524, + 322, 492, 326, 498, 1062, 494, 1074, 494, 326, + 500, 1062, 496, 1066, 490, 328, 496, 322, 492, + 1070, 498, 322, 494, 332, 492, 1068, 498, 320, + 494, 326, 498, 320, 496, 324, 500, 320, 494, + 324, 490, 336, 500, 320, 496, 324, 490, 328, + 496, 322, 492, 328, 498, 322, 492, 326, 498, + 328, 496, 322, 492, 328, 498, 1064, 494, 326, + 498, 320, 494, 1066, 490, 330, 496, 330, 494, + 1066, 490, 1070, 498, 322, 492, 328, 498, 322, + 492, 326, 498, 322, 492, 332, 492, 1068, 498, + 1062, 494, 1066, 500, 318, 496, 324, 490, 328, + 496, 324, 492, 334, 490, 328, 496, 324, 492, + 328, 496, 322, 492, 328, 498, 320, 494, 1068, + 500, 326, 500, 320, 492, 326, 500, 320, 496, + 324, 500, 318, 496, 324, 490, 328, 496, 330, + 496, 324, 490, 328, 496, 324, 490, 328, 498, + 322, 492, 328, 498, 320, 492, 334, 492, 328, + 498, 322, 494, 326, 498, 320, 494, 324, 500, + 322, 492, 324, 490, 336, 498, 320, 494, 324, + 500, 320, 496, 324, 490, 328, 498, 322, 492, + 328, 496, 1070, 496, 1064, 492, 1070, 498, 322, + 494, 326, 500, 320, 494, 324, 500, 320, 494, + 324, 470}; // UNKNOWN CE60D6B9 + uint8_t expectedState[kTcl112AcStateLength] = { + 0x23, 0xCB, 0x26, 0x01, 0x00, 0x24, 0x03, + 0x07, 0x40, 0x00, 0x00, 0x00, 0x80, 0x03}; + + irsend.sendRaw(rawData, 227, 38000); + irsend.makeDecodeResult(); + + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(TCL112AC, irsend.capture.decode_type); + EXPECT_EQ(kTcl112AcBits, irsend.capture.bits); + EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits); + + IRTcl112Ac ac(0); + ac.setRaw(irsend.capture.state); + EXPECT_EQ( + "Power: On, Mode: 3 (COOL), Temp: 24C, Fan: 0 (Auto), Econo: Off, " + "Health: Off, Light: On, Turbo: Off, Swing (H): Off, Swing (V): Off", + ac.toString()); +} + +// Decode a synthetic Tcl112Ac A/C example from Issue #619 +TEST(TestDecodeTcl112Ac, DecodeSyntheticExample) { + IRsendTest irsend(0); + IRrecv irrecv(0); + irsend.begin(); + + irsend.reset(); + + uint8_t expectedState[kTcl112AcStateLength] = {0x23, 0xCB, 0x26, 0x01, 0x00, + 0x24, 0x03, 0x07, 0x40, 0x00, + 0x00, 0x00, 0x80, 0x03}; + + irsend.sendTcl112Ac(expectedState); + irsend.makeDecodeResult(); + + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(TCL112AC, irsend.capture.decode_type); + EXPECT_EQ(kTcl112AcBits, irsend.capture.bits); + EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits); +} + +TEST(TestTcl112AcClass, Temperature) { + const uint8_t temp16C[kTcl112AcStateLength] = { + 0x23, 0xCB, 0x26, 0x01, 0x00, 0x24, 0x03, + 0x0F, 0x00, 0x00, 0x00, 0x00, 0x80, 0xCB}; + const uint8_t temp16point5C[kTcl112AcStateLength] = { + 0x23, 0xCB, 0x26, 0x01, 0x00, 0x24, 0x03, + 0x0F, 0x00, 0x00, 0x00, 0x00, 0xA0, 0xEB}; + const uint8_t temp19point5C[kTcl112AcStateLength] = { + 0x23, 0xCB, 0x26, 0x01, 0x00, 0x24, 0x03, + 0x0C, 0x00, 0x00, 0x00, 0x00, 0xA0, 0xE8}; + const uint8_t temp31C[kTcl112AcStateLength] = { + 0x23, 0xCB, 0x26, 0x01, 0x00, 0x24, 0x03, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xBC}; + IRTcl112Ac ac(0); + ac.setRaw(temp16C); + EXPECT_EQ( + "Power: On, Mode: 3 (COOL), Temp: 16C, Fan: 0 (Auto), Econo: Off, " + "Health: Off, Light: On, Turbo: Off, Swing (H): Off, Swing (V): Off", + ac.toString()); + ac.setRaw(temp16point5C); + EXPECT_EQ( + "Power: On, Mode: 3 (COOL), Temp: 16.5C, Fan: 0 (Auto), Econo: Off, " + "Health: Off, Light: On, Turbo: Off, Swing (H): Off, Swing (V): Off", + ac.toString()); + ac.setRaw(temp19point5C); + EXPECT_EQ( + "Power: On, Mode: 3 (COOL), Temp: 19.5C, Fan: 0 (Auto), Econo: Off, " + "Health: Off, Light: On, Turbo: Off, Swing (H): Off, Swing (V): Off", + ac.toString()); + ac.setRaw(temp31C); + EXPECT_EQ( + "Power: On, Mode: 3 (COOL), Temp: 31C, Fan: 0 (Auto), Econo: Off, " + "Health: Off, Light: On, Turbo: Off, Swing (H): Off, Swing (V): Off", + ac.toString()); + + ac.setTemp(kTcl112AcTempMin); + EXPECT_EQ(kTcl112AcTempMin, ac.getTemp()); + + ac.setTemp(kTcl112AcTempMin + 1); + EXPECT_EQ(kTcl112AcTempMin + 1, ac.getTemp()); + + ac.setTemp(kTcl112AcTempMax); + EXPECT_EQ(kTcl112AcTempMax, ac.getTemp()); + + ac.setTemp(kTcl112AcTempMin - 1); + EXPECT_EQ(kTcl112AcTempMin, ac.getTemp()); + + ac.setTemp(kTcl112AcTempMax + 0.5); + EXPECT_EQ(kTcl112AcTempMax, ac.getTemp()); + + ac.setTemp(23); + EXPECT_EQ(23, ac.getTemp()); + + ac.setTemp(27.4); + EXPECT_EQ(27, ac.getTemp()); + + ac.setTemp(22.5); + EXPECT_EQ(22.5, ac.getTemp()); + + ac.setTemp(25.6); + EXPECT_EQ(25.5, ac.getTemp()); + + ac.setTemp(0); + EXPECT_EQ(kTcl112AcTempMin, ac.getTemp()); + + ac.setTemp(255); + EXPECT_EQ(kTcl112AcTempMax, ac.getTemp()); +} + +TEST(TestTcl112AcClass, OperatingMode) { + IRTcl112Ac ac(0); + ac.begin(); + + ac.setMode(kTcl112AcAuto); + EXPECT_EQ(kTcl112AcAuto, ac.getMode()); + + ac.setMode(kTcl112AcCool); + EXPECT_EQ(kTcl112AcCool, ac.getMode()); + + ac.setMode(kTcl112AcHeat); + EXPECT_EQ(kTcl112AcHeat, ac.getMode()); + + ac.setFan(kTcl112AcFanAuto); + ac.setMode(kTcl112AcFan); // Should set fan speed to High. + EXPECT_EQ(kTcl112AcFan, ac.getMode()); + EXPECT_EQ(kTcl112AcFanHigh, ac.getFan()); + + ac.setMode(kTcl112AcDry); + EXPECT_EQ(kTcl112AcDry, ac.getMode()); + + ac.setMode(kTcl112AcHeat - 1); + EXPECT_EQ(kTcl112AcAuto, ac.getMode()); + + ac.setMode(kTcl112AcCool); + EXPECT_EQ(kTcl112AcCool, ac.getMode()); + + ac.setMode(kTcl112AcAuto + 1); + EXPECT_EQ(kTcl112AcAuto, ac.getMode()); + + ac.setMode(kTcl112AcCool); + ac.setMode(255); + EXPECT_EQ(kTcl112AcAuto, ac.getMode()); + + ac.setMode(kTcl112AcCool); + ac.setMode(0); + EXPECT_EQ(kTcl112AcAuto, ac.getMode()); + + const uint8_t automode[] = { + 0x23, 0xCB, 0x26, 0x01, 0x00, 0x24, 0x08, + 0x07, 0x00, 0x00, 0x00, 0x00, 0x80, 0x48}; + ac.setRaw(automode); + EXPECT_EQ( + "Power: On, Mode: 8 (AUTO), Temp: 24C, Fan: 0 (Auto), Econo: Off, " + "Health: Off, Light: On, Turbo: Off, Swing (H): Off, Swing (V): Off", + ac.toString()); +} + +TEST(TestTcl112AcClass, Power) { + IRTcl112Ac ac(0); + ac.begin(); + + ac.setPower(true); + EXPECT_TRUE(ac.getPower()); + + ac.setPower(false); + EXPECT_EQ(false, ac.getPower()); + + ac.setPower(true); + EXPECT_TRUE(ac.getPower()); + + ac.off(); + EXPECT_EQ(false, ac.getPower()); + + ac.on(); + EXPECT_TRUE(ac.getPower()); + + const uint8_t on[kTcl112AcStateLength] = { + 0x23, 0xCB, 0x26, 0x01, 0x00, 0x24, 0x03, + 0x0F, 0x00, 0x00, 0x00, 0x00, 0x80, 0xCB}; + ac.setRaw(on); + EXPECT_EQ( + "Power: On, Mode: 3 (COOL), Temp: 16C, Fan: 0 (Auto), Econo: Off, " + "Health: Off, Light: On, Turbo: Off, Swing (H): Off, Swing (V): Off", + ac.toString()); + + const uint8_t off[kTcl112AcStateLength] = { + 0x23, 0xCB, 0x26, 0x01, 0x00, 0x20, 0x03, + 0x07, 0x40, 0x00, 0x00, 0x00, 0x80, 0xCB}; + ac.setRaw(off); + EXPECT_EQ( + "Power: Off, Mode: 3 (COOL), Temp: 24C, Fan: 0 (Auto), Econo: Off, " + "Health: Off, Light: On, Turbo: Off, Swing (H): Off, Swing (V): Off", + ac.toString()); +} + + +TEST(TestTcl112AcClass, Checksum) { + uint8_t temp16C[kTcl112AcStateLength] = { + 0x23, 0xCB, 0x26, 0x01, 0x00, 0x24, 0x03, + 0x0F, 0x00, 0x00, 0x00, 0x00, 0x80, 0xCB}; + uint8_t temp31C[kTcl112AcStateLength] = { + 0x23, 0xCB, 0x26, 0x01, 0x00, 0x24, 0x03, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xBC}; + IRTcl112Ac ac(0); + EXPECT_EQ(0xCB, ac.calcChecksum(temp16C)); + ac.setRaw(temp16C); + EXPECT_EQ( + "Power: On, Mode: 3 (COOL), Temp: 16C, Fan: 0 (Auto), Econo: Off, " + "Health: Off, Light: On, Turbo: Off, Swing (H): Off, Swing (V): Off", + ac.toString()); + ac.setRaw(temp31C); + EXPECT_EQ( + "Power: On, Mode: 3 (COOL), Temp: 31C, Fan: 0 (Auto), Econo: Off, " + "Health: Off, Light: On, Turbo: Off, Swing (H): Off, Swing (V): Off", + ac.toString()); + EXPECT_EQ(0xBC, ac.calcChecksum(temp31C)); + + EXPECT_TRUE(IRTcl112Ac::validChecksum(temp16C)); + EXPECT_TRUE(IRTcl112Ac::validChecksum(temp31C)); + EXPECT_TRUE(ac.validChecksum(temp31C)); + ac.setRaw(temp16C); + EXPECT_TRUE(ac.validChecksum(ac.getRaw())); + ac.setTemp(31); + EXPECT_TRUE(ac.validChecksum(ac.getRaw())); + EXPECT_EQ(0xBC, ac.calcChecksum(ac.getRaw())); +} + +TEST(TestTcl112AcClass, Econo) { + IRTcl112Ac ac(0); + ac.begin(); + + ac.setEcono(true); + EXPECT_TRUE(ac.getEcono()); + ac.setEcono(false); + EXPECT_EQ(false, ac.getEcono()); + ac.setEcono(true); + EXPECT_TRUE(ac.getEcono()); +} + +TEST(TestTcl112AcClass, Health) { + IRTcl112Ac ac(0); + ac.begin(); + + ac.setHealth(true); + EXPECT_TRUE(ac.getHealth()); + ac.setHealth(false); + EXPECT_EQ(false, ac.getHealth()); + ac.setHealth(true); + EXPECT_TRUE(ac.getHealth()); +} + +TEST(TestTcl112AcClass, Light) { + IRTcl112Ac ac(0); + ac.begin(); + + ac.setLight(true); + EXPECT_TRUE(ac.getLight()); + ac.setLight(false); + EXPECT_EQ(false, ac.getLight()); + ac.setLight(true); + EXPECT_TRUE(ac.getLight()); +} + +TEST(TestTcl112AcClass, SwingHorizontal) { + IRTcl112Ac ac(0); + ac.begin(); + + ac.setSwingHorizontal(true); + EXPECT_TRUE(ac.getSwingHorizontal()); + ac.setSwingHorizontal(false); + EXPECT_EQ(false, ac.getSwingHorizontal()); + ac.setSwingHorizontal(true); + EXPECT_TRUE(ac.getSwingHorizontal()); +} + +TEST(TestTcl112AcClass, SwingVertical) { + IRTcl112Ac ac(0); + ac.begin(); + + ac.setSwingVertical(true); + EXPECT_TRUE(ac.getSwingVertical()); + ac.setSwingVertical(false); + EXPECT_EQ(false, ac.getSwingVertical()); + ac.setSwingVertical(true); + EXPECT_TRUE(ac.getSwingVertical()); +} + +TEST(TestTcl112AcClass, Turbo) { + IRTcl112Ac ac(0); + ac.begin(); + + ac.setFan(kTcl112AcFanLow); + ac.setSwingHorizontal(false); + + ac.setTurbo(false); + EXPECT_FALSE(ac.getTurbo()); + EXPECT_FALSE(ac.getSwingVertical()); + EXPECT_EQ(kTcl112AcFanLow, ac.getFan()); + + ac.setTurbo(true); + EXPECT_TRUE(ac.getTurbo()); + EXPECT_TRUE(ac.getSwingVertical()); + EXPECT_EQ(kTcl112AcFanHigh, ac.getFan()); + + ac.setTurbo(false); + EXPECT_FALSE(ac.getTurbo()); + ac.setTurbo(true); + EXPECT_TRUE(ac.getTurbo()); +} + +TEST(TestTcl112AcClass, FanSpeed) { + IRTcl112Ac ac(0); + ac.begin(); + + // Unexpected value should default to Auto. + ac.setFan(255); + EXPECT_EQ(kTcl112AcFanAuto, ac.getFan()); + + ac.setFan(kTcl112AcFanLow); + EXPECT_EQ(kTcl112AcFanLow, ac.getFan()); + ac.setFan(kTcl112AcFanMed); + EXPECT_EQ(kTcl112AcFanMed, ac.getFan()); + ac.setFan(kTcl112AcFanHigh); + EXPECT_EQ(kTcl112AcFanHigh, ac.getFan()); + ac.setFan(kTcl112AcFanAuto); + EXPECT_EQ(kTcl112AcFanAuto, ac.getFan()); + + // Beyond High should default to Auto. + ac.setFan(kTcl112AcFanHigh + 1); + EXPECT_EQ(kTcl112AcFanAuto, ac.getFan()); +} diff --git a/lib/IRremoteESP8266-2.6.0/test/ir_Teco_test.cpp b/lib/IRremoteESP8266-2.6.0/test/ir_Teco_test.cpp new file mode 100644 index 000000000000..6b03a671de28 --- /dev/null +++ b/lib/IRremoteESP8266-2.6.0/test/ir_Teco_test.cpp @@ -0,0 +1,358 @@ +// Copyright 2019 David Conran + +#include "ir_Teco.h" +#include "IRrecv.h" +#include "IRrecv_test.h" +#include "IRsend.h" +#include "IRsend_test.h" +#include "gtest/gtest.h" + +// General housekeeping +TEST(TestTeco, Housekeeping) { + ASSERT_EQ("TECO", typeToString(TECO)); + ASSERT_FALSE(hasACState(TECO)); // Uses uint64_t, not uint8_t*. +} + +// Tests for sendTeco() + +// Test sending typical data only. +TEST(TestSendTeco, SendDataOnly) { + IRsendTest irsend(0); + irsend.begin(); + + irsend.reset(); + irsend.sendTeco(0x250002BC9); + EXPECT_EQ( + "f38000d50" + "m9000s4440" + "m620s1650m620s580m620s580m620s1650m620s580m620s580m620s1650m620s1650" + "m620s1650m620s1650m620s580m620s1650m620s580m620s1650m620s580m620s580" + "m620s580m620s580m620s580m620s580m620s580m620s580m620s580m620s580" + "m620s580m620s580m620s580m620s580m620s1650m620s580m620s1650m620s580" + "m620s580m620s1650m620s580" + "m620s100000", + irsend.outputStr()); +} + +// Test sending typical data with repeats. +TEST(TestSendTeco, SendWithRepeats) { + IRsendTest irsend(0); + irsend.begin(); + + irsend.reset(); + irsend.sendTeco(0x250002BC9, kTecoBits, 2); // two repeats. + EXPECT_EQ( + "f38000d50" + "m9000s4440" + "m620s1650m620s580m620s580m620s1650m620s580m620s580m620s1650m620s1650" + "m620s1650m620s1650m620s580m620s1650m620s580m620s1650m620s580m620s580" + "m620s580m620s580m620s580m620s580m620s580m620s580m620s580m620s580" + "m620s580m620s580m620s580m620s580m620s1650m620s580m620s1650m620s580" + "m620s580m620s1650m620s580" + "m620s100000" + "m9000s4440" + "m620s1650m620s580m620s580m620s1650m620s580m620s580m620s1650m620s1650" + "m620s1650m620s1650m620s580m620s1650m620s580m620s1650m620s580m620s580" + "m620s580m620s580m620s580m620s580m620s580m620s580m620s580m620s580" + "m620s580m620s580m620s580m620s580m620s1650m620s580m620s1650m620s580" + "m620s580m620s1650m620s580" + "m620s100000" + "m9000s4440" + "m620s1650m620s580m620s580m620s1650m620s580m620s580m620s1650m620s1650" + "m620s1650m620s1650m620s580m620s1650m620s580m620s1650m620s580m620s580" + "m620s580m620s580m620s580m620s580m620s580m620s580m620s580m620s580" + "m620s580m620s580m620s580m620s580m620s1650m620s580m620s1650m620s580" + "m620s580m620s1650m620s580" + "m620s100000", + irsend.outputStr()); +} + + +// Tests for IRTeco class. + +TEST(TestTecoACClass, Power) { + IRTecoAc ac(0); + ac.begin(); + + ac.setPower(true); + EXPECT_TRUE(ac.getPower()); + + ac.setPower(false); + EXPECT_EQ(false, ac.getPower()); + + ac.setPower(true); + EXPECT_TRUE(ac.getPower()); + + ac.off(); + EXPECT_EQ(false, ac.getPower()); + + ac.on(); + EXPECT_TRUE(ac.getPower()); +} + +TEST(TestTecoACClass, OperatingMode) { + IRTecoAc ac(0); + ac.begin(); + + ac.setMode(kTecoAuto); + EXPECT_EQ(kTecoAuto, ac.getMode()); + + ac.setMode(kTecoCool); + EXPECT_EQ(kTecoCool, ac.getMode()); + + ac.setMode(kTecoHeat); + EXPECT_EQ(kTecoHeat, ac.getMode()); + + ac.setMode(kTecoFan); + EXPECT_EQ(kTecoFan, ac.getMode()); + + ac.setMode(kTecoDry); + EXPECT_EQ(kTecoDry, ac.getMode()); + + ac.setMode(kTecoAuto - 1); + EXPECT_EQ(kTecoAuto, ac.getMode()); + + ac.setMode(kTecoCool); + EXPECT_EQ(kTecoCool, ac.getMode()); + + ac.setMode(kTecoHeat + 1); + EXPECT_EQ(kTecoAuto, ac.getMode()); + + ac.setMode(255); + EXPECT_EQ(kTecoAuto, ac.getMode()); +} + +TEST(TestTecoACClass, Temperature) { + IRTecoAc ac(0); + ac.begin(); + + ac.setTemp(kTecoMinTemp); + EXPECT_EQ(kTecoMinTemp, ac.getTemp()); + + ac.setTemp(kTecoMinTemp + 1); + EXPECT_EQ(kTecoMinTemp + 1, ac.getTemp()); + + ac.setTemp(kTecoMaxTemp); + EXPECT_EQ(kTecoMaxTemp, ac.getTemp()); + + ac.setTemp(kTecoMinTemp - 1); + EXPECT_EQ(kTecoMinTemp, ac.getTemp()); + + ac.setTemp(kTecoMaxTemp + 1); + EXPECT_EQ(kTecoMaxTemp, ac.getTemp()); + + ac.setTemp(23); + EXPECT_EQ(23, ac.getTemp()); + + ac.setTemp(0); + EXPECT_EQ(kTecoMinTemp, ac.getTemp()); + + ac.setTemp(255); + EXPECT_EQ(kTecoMaxTemp, ac.getTemp()); +} + +TEST(TestTecoACClass, FanSpeed) { + IRTecoAc ac(0); + ac.begin(); + ac.setFan(kTecoFanLow); + + ac.setFan(kTecoFanAuto); + EXPECT_EQ(kTecoFanAuto, ac.getFan()); + + ac.setFan(kTecoFanLow); + EXPECT_EQ(kTecoFanLow, ac.getFan()); + ac.setFan(kTecoFanMed); + EXPECT_EQ(kTecoFanMed, ac.getFan()); + ac.setFan(kTecoFanHigh); + EXPECT_EQ(kTecoFanHigh, ac.getFan()); + + ac.setFan(kTecoFanHigh); + EXPECT_EQ(kTecoFanHigh, ac.getFan()); +} + +TEST(TestTecoACClass, Swing) { + IRTecoAc ac(0); + ac.begin(); + + ac.setSwing(true); + EXPECT_TRUE(ac.getSwing()); + + ac.setSwing(false); + EXPECT_EQ(false, ac.getSwing()); + + ac.setSwing(true); + EXPECT_TRUE(ac.getSwing()); +} + +TEST(TestTecoACClass, Sleep) { + IRTecoAc ac(0); + ac.begin(); + + ac.setSleep(true); + EXPECT_TRUE(ac.getSleep()); + + ac.setSleep(false); + EXPECT_EQ(false, ac.getSleep()); + + ac.setSleep(true); + EXPECT_TRUE(ac.getSleep()); +} + +TEST(TestTecoACClass, MessageConstuction) { + IRTecoAc ac(0); + + EXPECT_EQ( + "Power: Off, Mode: 0 (AUTO), Temp: 16C, Fan: 0 (Auto), Sleep: Off, " + "Swing: Off", + ac.toString()); + ac.setPower(true); + ac.setMode(kTecoCool); + ac.setTemp(21); + ac.setFan(kTecoFanHigh); + ac.setSwing(false); + EXPECT_EQ( + "Power: On, Mode: 1 (COOL), Temp: 21C, Fan: 3 (High), Sleep: Off, " + "Swing: Off", + ac.toString()); + ac.setSwing(true); + EXPECT_EQ( + "Power: On, Mode: 1 (COOL), Temp: 21C, Fan: 3 (High), Sleep: Off, " + "Swing: On", + ac.toString()); + ac.setSwing(false); + ac.setFan(kTecoFanLow); + ac.setSleep(true); + ac.setMode(kTecoHeat); + EXPECT_EQ( + "Power: On, Mode: 4 (HEAT), Temp: 21C, Fan: 1 (Low), Sleep: On, " + "Swing: Off", + ac.toString()); + ac.setSleep(false); + EXPECT_EQ( + "Power: On, Mode: 4 (HEAT), Temp: 21C, Fan: 1 (Low), Sleep: Off, " + "Swing: Off", + ac.toString()); + ac.setTemp(25); + EXPECT_EQ( + "Power: On, Mode: 4 (HEAT), Temp: 25C, Fan: 1 (Low), Sleep: Off, " + "Swing: Off", + ac.toString()); +} + +TEST(TestTecoACClass, ReconstructKnownMessage) { + IRTecoAc ac(0); + + const uint64_t expected = 0x250002BC9; + ASSERT_FALSE(ac.getRaw() == expected); + ac.setPower(true); + ac.setMode(kTecoCool); + ac.setTemp(27); + ac.setFan(kTecoFanAuto); + ac.setSleep(true); + ac.setSwing(true); + EXPECT_EQ(expected, ac.getRaw()); + EXPECT_EQ( + "Power: On, Mode: 1 (COOL), Temp: 27C, Fan: 0 (Auto), Sleep: On, " + "Swing: On", + ac.toString()); +} + +// Tests for decodeTeco(). + +// Decode normal "synthetic" messages. +TEST(TestDecodeTeco, NormalDecodeWithStrict) { + IRsendTest irsend(0); + IRrecv irrecv(0); + irsend.begin(); + + // With the specific decoder. + uint64_t expectedState = kTecoReset; + irsend.reset(); + irsend.sendTeco(expectedState); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decodeTeco(&irsend.capture, kTecoBits, true)); + EXPECT_EQ(TECO, irsend.capture.decode_type); + EXPECT_EQ(kTecoBits, irsend.capture.bits); + EXPECT_FALSE(irsend.capture.repeat); + EXPECT_EQ(expectedState, irsend.capture.value); + EXPECT_EQ(0, irsend.capture.address); + EXPECT_EQ(0, irsend.capture.command); + + // With the all the decoders. + irsend.reset(); + irsend.sendTeco(expectedState); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(TECO, irsend.capture.decode_type); + EXPECT_EQ(kTecoBits, irsend.capture.bits); + EXPECT_FALSE(irsend.capture.repeat); + EXPECT_EQ(expectedState, irsend.capture.value); + EXPECT_EQ(0, irsend.capture.address); + EXPECT_EQ(0, irsend.capture.command); + + IRTecoAc ac(0); + ac.begin(); + ac.setRaw(irsend.capture.value); + EXPECT_EQ( + "Power: Off, Mode: 0 (AUTO), Temp: 16C, Fan: 0 (Auto), Sleep: Off, " + "Swing: Off", + ac.toString()); +} + +// Decode a real message from Raw Data. +TEST(TestDecodeTeco, RealNormalExample) { + IRsendTest irsend(0); + IRrecv irrecv(0); + IRTecoAc ac(0); + irsend.begin(); + + uint16_t rawData1[73] = { + 9076, 4442, 670, 1620, 670, 516, 670, 516, 666, 1626, 670, 516, + 664, 520, 666, 1626, 666, 1626, 664, 1626, 666, 1626, 666, 520, + 666, 1626, 666, 520, 666, 1626, 666, 520, 666, 516, 670, 514, + 670, 516, 666, 520, 670, 516, 666, 520, 666, 516, 672, 514, 670, + 516, 666, 520, 666, 516, 672, 514, 670, 516, 666, 1624, 666, 520, + 666, 1626, 666, 520, 666, 516, 672, 1620, 670, 516, 670}; + uint64_t expected1 = 0b01001010000000000000010101111001001; // 0x250002BC9 + irsend.reset(); + irsend.sendRaw(rawData1, 73, 38); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(TECO, irsend.capture.decode_type); + EXPECT_EQ(kTecoBits, irsend.capture.bits); + EXPECT_FALSE(irsend.capture.repeat); + EXPECT_EQ(expected1, irsend.capture.value); + EXPECT_EQ(0, irsend.capture.address); + EXPECT_EQ(0, irsend.capture.command); + ac.begin(); + ac.setRaw(irsend.capture.value); + EXPECT_EQ( + "Power: On, Mode: 1 (COOL), Temp: 27C, Fan: 0 (Auto), Sleep: On, " + "Swing: On", + ac.toString()); + + uint16_t rawData2[73] = { + 9048, 4472, 636, 548, 636, 1654, 638, 546, 642, 1650, 642, 546, 638, + 1654, 638, 1654, 638, 546, 638, 1654, 636, 546, 642, 1650, 640, 548, + 636, 548, 638, 546, 636, 546, 642, 542, 642, 546, 638, 546, 638, 546, + 636, 548, 642, 542, 642, 546, 636, 548, 636, 546, 642, 542, 642, 546, + 638, 546, 638, 546, 636, 1654, 642, 542, 642, 1650, 642, 546, 638, 546, + 638, 1654, 638, 546, 642}; // TECO 25000056A + uint64_t expected2 = 0b01001010000000000000000010101101010; // 0x25000056A + irsend.reset(); + irsend.sendRaw(rawData2, 73, 38); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(TECO, irsend.capture.decode_type); + EXPECT_EQ(kTecoBits, irsend.capture.bits); + EXPECT_FALSE(irsend.capture.repeat); + EXPECT_EQ(expected2, irsend.capture.value); + EXPECT_EQ(0, irsend.capture.address); + EXPECT_EQ(0, irsend.capture.command); + ac.begin(); + ac.setRaw(irsend.capture.value); + EXPECT_EQ( + "Power: On, Mode: 2 (DRY), Temp: 21C, Fan: 2 (Med), Sleep: Off, " + "Swing: On", + ac.toString()); +} diff --git a/lib/IRremoteESP8266-2.5.2.03/test/ir_Toshiba_test.cpp b/lib/IRremoteESP8266-2.6.0/test/ir_Toshiba_test.cpp similarity index 99% rename from lib/IRremoteESP8266-2.5.2.03/test/ir_Toshiba_test.cpp rename to lib/IRremoteESP8266-2.6.0/test/ir_Toshiba_test.cpp index b5e1e07a988a..d74866f92f79 100644 --- a/lib/IRremoteESP8266-2.5.2.03/test/ir_Toshiba_test.cpp +++ b/lib/IRremoteESP8266-2.6.0/test/ir_Toshiba_test.cpp @@ -18,6 +18,7 @@ TEST(TestSendToshibaAC, SendDataOnly) { irsend.reset(); irsend.sendToshibaAC(toshiba_code); EXPECT_EQ( + "f38000d50" "m4400s4300" "m543s1623m543s1623m543s1623m543s1623m543s472m543s472m543s1623m543s472" "m543s472m543s472m543s472m543s472m543s1623m543s1623m543s472m543s1623" @@ -54,6 +55,7 @@ TEST(TestSendToshibaAC, SendWithRepeats) { irsend.sendToshibaAC(toshiba_code, kToshibaACStateLength, 0); EXPECT_EQ( + "f38000d50" "m4400s4300" "m543s1623m543s1623m543s1623m543s1623m543s472m543s472m543s1623m543s472" "m543s472m543s472m543s472m543s472m543s1623m543s1623m543s472m543s1623" @@ -70,6 +72,7 @@ TEST(TestSendToshibaAC, SendWithRepeats) { irsend.reset(); irsend.sendToshibaAC(toshiba_code, kToshibaACStateLength, 2); EXPECT_EQ( + "f38000d50" "m4400s4300" "m543s1623m543s1623m543s1623m543s1623m543s472m543s472m543s1623m543s472" "m543s472m543s472m543s472m543s472m543s1623m543s1623m543s472m543s1623" @@ -122,6 +125,7 @@ TEST(TestSendToshibaAC, SendUnexpectedSizes) { irsend.reset(); irsend.sendToshibaAC(toshiba_long_code, kToshibaACStateLength + 1); ASSERT_EQ( + "f38000d50" "m4400s4300" "m543s472m543s472m543s472m543s472m543s472m543s472m543s472m543s1623" "m543s472m543s472m543s472m543s472m543s472m543s472m543s1623m543s472" @@ -380,6 +384,7 @@ TEST(TestToshibaACClass, MessageConstuction) { irsend.reset(); irsend.sendToshibaAC(toshiba.getRaw()); EXPECT_EQ( + "f38000d50" "m4400s4300" "m543s1623m543s1623m543s1623m543s1623m543s472m543s472m543s1623m543s472" "m543s472m543s472m543s472m543s472m543s1623m543s1623m543s472m543s1623" @@ -415,6 +420,7 @@ TEST(TestToshibaACClass, MessageConstuction) { irsend.reset(); irsend.sendToshibaAC(toshiba.getRaw()); EXPECT_EQ( + "f38000d50" "m4400s4300" "m543s1623m543s1623m543s1623m543s1623m543s472m543s472m543s1623m543s472" "m543s472m543s472m543s472m543s472m543s1623m543s1623m543s472m543s1623" @@ -450,6 +456,7 @@ TEST(TestToshibaACClass, MessageConstuction) { irsend.reset(); irsend.sendToshibaAC(toshiba.getRaw()); EXPECT_EQ( + "f38000d50" "m4400s4300" "m543s1623m543s1623m543s1623m543s1623m543s472m543s472m543s1623m543s472" "m543s472m543s472m543s472m543s472m543s1623m543s1623m543s472m543s1623" diff --git a/lib/IRremoteESP8266-2.6.0/test/ir_Vestel_test.cpp b/lib/IRremoteESP8266-2.6.0/test/ir_Vestel_test.cpp new file mode 100644 index 000000000000..077a0a25e772 --- /dev/null +++ b/lib/IRremoteESP8266-2.6.0/test/ir_Vestel_test.cpp @@ -0,0 +1,513 @@ +// Copyright 2019 David Conran + +#include "ir_Vestel.h" +#include "IRrecv.h" +#include "IRrecv_test.h" +#include "IRsend.h" +#include "IRsend_test.h" +#include "gtest/gtest.h" + +// Tests for sendVestelAc() + +// Test sending typical data only. +TEST(TestSendVestelAc, SendDataOnly) { + IRsendTest irsend(0); + irsend.begin(); + + irsend.reset(); + irsend.sendVestelAc(0x0F00D9001FEF201ULL); + EXPECT_EQ( + "f38000d50" + "m3110s9066" + "m520s1535m520s480m520s480m520s480m520s480m520s480m520s480m520s480" + "m520s480m520s1535m520s480m520s480m520s1535m520s1535m520s1535m520s1535" + "m520s480m520s1535m520s1535m520s1535m520s1535m520s1535m520s1535m520s1535" + "m520s1535m520s480m520s480m520s480m520s480m520s480m520s480m520s480" + "m520s480m520s480m520s480m520s480m520s1535m520s480m520s480m520s1535" + "m520s1535m520s480m520s1535m520s1535m520s480m520s480m520s480m520s480" + "m520s480m520s480m520s480m520s480m520s1535m520s1535m520s1535m520s1535" + "m520s100000", + irsend.outputStr()); +} + +// Test sending typical data with repeats. +TEST(TestSendVestelAc, SendWithRepeats) { + IRsendTest irsend(0); + irsend.begin(); + + irsend.reset(); + irsend.sendVestelAc(0x0F00D9001FEF201ULL, kVestelAcBits, 2); // two repeats. + EXPECT_EQ( + "f38000d50" + "m3110s9066" + "m520s1535m520s480m520s480m520s480m520s480m520s480m520s480m520s480" + "m520s480m520s1535m520s480m520s480m520s1535m520s1535m520s1535m520s1535" + "m520s480m520s1535m520s1535m520s1535m520s1535m520s1535m520s1535m520s1535" + "m520s1535m520s480m520s480m520s480m520s480m520s480m520s480m520s480" + "m520s480m520s480m520s480m520s480m520s1535m520s480m520s480m520s1535" + "m520s1535m520s480m520s1535m520s1535m520s480m520s480m520s480m520s480" + "m520s480m520s480m520s480m520s480m520s1535m520s1535m520s1535m520s1535" + "m520s100000" + "m3110s9066" + "m520s1535m520s480m520s480m520s480m520s480m520s480m520s480m520s480" + "m520s480m520s1535m520s480m520s480m520s1535m520s1535m520s1535m520s1535" + "m520s480m520s1535m520s1535m520s1535m520s1535m520s1535m520s1535m520s1535" + "m520s1535m520s480m520s480m520s480m520s480m520s480m520s480m520s480" + "m520s480m520s480m520s480m520s480m520s1535m520s480m520s480m520s1535" + "m520s1535m520s480m520s1535m520s1535m520s480m520s480m520s480m520s480" + "m520s480m520s480m520s480m520s480m520s1535m520s1535m520s1535m520s1535" + "m520s100000" + "m3110s9066" + "m520s1535m520s480m520s480m520s480m520s480m520s480m520s480m520s480" + "m520s480m520s1535m520s480m520s480m520s1535m520s1535m520s1535m520s1535" + "m520s480m520s1535m520s1535m520s1535m520s1535m520s1535m520s1535m520s1535" + "m520s1535m520s480m520s480m520s480m520s480m520s480m520s480m520s480" + "m520s480m520s480m520s480m520s480m520s1535m520s480m520s480m520s1535" + "m520s1535m520s480m520s1535m520s1535m520s480m520s480m520s480m520s480" + "m520s480m520s480m520s480m520s480m520s1535m520s1535m520s1535m520s1535" + "m520s100000", + irsend.outputStr()); +} + +// Tests for IRVestelAc class. + +TEST(TestVestelAcClass, Power) { + IRVestelAc ac(0); + ac.begin(); + + ac.setPower(true); + EXPECT_TRUE(ac.getPower()); + + ac.setPower(false); + EXPECT_EQ(false, ac.getPower()); + + ac.setPower(true); + EXPECT_TRUE(ac.getPower()); + + ac.off(); + EXPECT_EQ(false, ac.getPower()); + + ac.on(); + EXPECT_TRUE(ac.getPower()); + EXPECT_FALSE(ac.isTimeCommand()); +} + +TEST(TestVestelAcClass, OperatingMode) { + IRVestelAc ac(0); + ac.begin(); + + ac.setMode(kVestelAcAuto); + EXPECT_EQ(kVestelAcAuto, ac.getMode()); + + ac.setMode(kVestelAcCool); + EXPECT_EQ(kVestelAcCool, ac.getMode()); + + ac.setMode(kVestelAcHeat); + EXPECT_EQ(kVestelAcHeat, ac.getMode()); + + ac.setMode(kVestelAcFan); + EXPECT_EQ(kVestelAcFan, ac.getMode()); + + ac.setMode(kVestelAcDry); + EXPECT_EQ(kVestelAcDry, ac.getMode()); + + ac.setMode(kVestelAcAuto - 1); + EXPECT_EQ(kVestelAcAuto, ac.getMode()); + + ac.setMode(kVestelAcCool); + EXPECT_EQ(kVestelAcCool, ac.getMode()); + + ac.setMode(kVestelAcHeat + 1); + EXPECT_EQ(kVestelAcAuto, ac.getMode()); + + ac.setMode(255); + EXPECT_EQ(kVestelAcAuto, ac.getMode()); + EXPECT_FALSE(ac.isTimeCommand()); +} + +TEST(TestVestelAcClass, Temperature) { + IRVestelAc ac(0); + ac.begin(); + + ac.setTemp(kVestelAcMinTempC); + EXPECT_EQ(kVestelAcMinTempC, ac.getTemp()); + + ac.setTemp(kVestelAcMinTempC + 1); + EXPECT_EQ(kVestelAcMinTempC + 1, ac.getTemp()); + + ac.setTemp(kVestelAcMaxTemp); + EXPECT_EQ(kVestelAcMaxTemp, ac.getTemp()); + + ac.setTemp(kVestelAcMinTempC - 1); + EXPECT_EQ(kVestelAcMinTempC, ac.getTemp()); + + ac.setTemp(kVestelAcMaxTemp + 1); + EXPECT_EQ(kVestelAcMaxTemp, ac.getTemp()); + + ac.setTemp(23); + EXPECT_EQ(23, ac.getTemp()); + + ac.setTemp(0); + EXPECT_EQ(kVestelAcMinTempC, ac.getTemp()); + + ac.setTemp(255); + EXPECT_EQ(kVestelAcMaxTemp, ac.getTemp()); + EXPECT_FALSE(ac.isTimeCommand()); +} + +TEST(TestVestelAcClass, FanSpeed) { + IRVestelAc ac(0); + ac.begin(); + ac.setFan(kVestelAcFanLow); + + ac.setFan(kVestelAcFanAuto); + EXPECT_EQ(kVestelAcFanAuto, ac.getFan()); + + ac.setFan(kVestelAcFanLow); + EXPECT_EQ(kVestelAcFanLow, ac.getFan()); + ac.setFan(kVestelAcFanMed); + EXPECT_EQ(kVestelAcFanMed, ac.getFan()); + ac.setFan(kVestelAcFanHigh); + EXPECT_EQ(kVestelAcFanHigh, ac.getFan()); + + ac.setFan(kVestelAcFanHigh); + EXPECT_EQ(kVestelAcFanHigh, ac.getFan()); + EXPECT_FALSE(ac.isTimeCommand()); +} + +TEST(TestVestelAcClass, Swing) { + IRVestelAc ac(0); + ac.begin(); + + ac.setSwing(true); + EXPECT_TRUE(ac.getSwing()); + + ac.setSwing(false); + EXPECT_EQ(false, ac.getSwing()); + + ac.setSwing(true); + EXPECT_TRUE(ac.getSwing()); + EXPECT_FALSE(ac.isTimeCommand()); +} + +TEST(TestVestelAcClass, Ion) { + IRVestelAc ac(0); + ac.begin(); + + ac.setIon(true); + EXPECT_TRUE(ac.getIon()); + + ac.setIon(false); + EXPECT_EQ(false, ac.getIon()); + + ac.setIon(true); + EXPECT_TRUE(ac.getIon()); + EXPECT_FALSE(ac.isTimeCommand()); +} + +TEST(TestVestelAcClass, Turbo) { + IRVestelAc ac(0); + ac.begin(); + + ac.setTurbo(true); + EXPECT_TRUE(ac.getTurbo()); + + ac.setTurbo(false); + EXPECT_EQ(false, ac.getTurbo()); + + ac.setTurbo(true); + EXPECT_TRUE(ac.getTurbo()); + EXPECT_FALSE(ac.isTimeCommand()); +} + +TEST(TestVestelAcClass, Sleep) { + IRVestelAc ac(0); + ac.begin(); + + ac.setSleep(true); + EXPECT_TRUE(ac.getSleep()); + + ac.setSleep(false); + EXPECT_EQ(false, ac.getSleep()); + + ac.setSleep(true); + EXPECT_TRUE(ac.getSleep()); + EXPECT_FALSE(ac.isTimeCommand()); +} + +TEST(TestVestelAcClass, Time) { + IRVestelAc ac(0); + ac.begin(); + + ac.setTime(0); + EXPECT_EQ(0, ac.getTime()); + EXPECT_TRUE(ac.isTimeCommand()); + + ac.setTime(1); + EXPECT_EQ(1, ac.getTime()); + + ac.setTime(1234); + EXPECT_EQ(1234, ac.getTime()); + + ac.setTime(23 * 60 + 59); + EXPECT_EQ(23 * 60 + 59, ac.getTime()); +} + +TEST(TestVestelAcClass, OnTimer) { + IRVestelAc ac(0); + ac.begin(); + + ac.setOnTimer(0); + EXPECT_EQ(0, ac.getOnTimer()); + EXPECT_TRUE(ac.isTimeCommand()); + + ac.setOnTimer(1); + EXPECT_EQ(0, ac.getOnTimer()); + + ac.setOnTimer(10); + EXPECT_EQ(10, ac.getOnTimer()); + + ac.setOnTimer(12 * 60 + 15); // we will round down to 10 min increments. + EXPECT_EQ(12 * 60 + 10, ac.getOnTimer()); + + ac.setOnTimer(23 * 60 + 50); + EXPECT_EQ(23 * 60 + 50, ac.getOnTimer()); +} + +TEST(TestVestelAcClass, OffTimer) { + IRVestelAc ac(0); + ac.begin(); + + ac.setOffTimer(0); + EXPECT_EQ(0, ac.getOffTimer()); + EXPECT_TRUE(ac.isTimeCommand()); + + ac.setOffTimer(1); + EXPECT_EQ(0, ac.getOffTimer()); + + ac.setOffTimer(10); + EXPECT_EQ(10, ac.getOffTimer()); + + ac.setOffTimer(12 * 60 + 15); // we will round down to 10 min increments. + EXPECT_EQ(12 * 60 + 10, ac.getOffTimer()); + + ac.setOffTimer(23 * 60 + 50); + EXPECT_EQ(23 * 60 + 50, ac.getOffTimer()); +} + +TEST(TestVestelAcClass, Timer) { + IRVestelAc ac(0); + ac.begin(); + + ac.setTimer(0); + EXPECT_EQ(0, ac.getTimer()); + EXPECT_EQ(0, ac.getOnTimer()); + EXPECT_TRUE(ac.isTimeCommand()); + + ac.setTimer(10); + EXPECT_EQ(10, ac.getTimer()); + EXPECT_EQ(0, ac.getOnTimer()); + + ac.setTimer(12 * 60 + 15); // we will round down to 10 min increments. + EXPECT_EQ(12 * 60 + 10, ac.getTimer()); + EXPECT_EQ(0, ac.getOnTimer()); + + ac.setTimer(23 * 60 + 50); + EXPECT_EQ(23 * 60 + 50, ac.getTimer()); + EXPECT_EQ(0, ac.getOnTimer()); +} + +TEST(TestVestelAcClass, MessageConstuction) { + IRVestelAc ac(0); + + EXPECT_EQ( + "Power: On, Mode: 0 (AUTO), Temp: 25C, Fan: 13 (AUTO HOT), Sleep: Off, " + "Turbo: Off, Ion: Off, Swing: Off", + ac.toString()); + ac.setMode(kVestelAcCool); + ac.setTemp(21); + ac.setFan(kVestelAcFanHigh); + EXPECT_FALSE(ac.isTimeCommand()); + EXPECT_EQ( + "Power: On, Mode: 1 (COOL), Temp: 21C, Fan: 11 (HIGH), Sleep: Off, " + "Turbo: Off, Ion: Off, Swing: Off", + ac.toString()); + ac.setSwing(true); + ac.setIon(true); + ac.setTurbo(true); + EXPECT_FALSE(ac.isTimeCommand()); + EXPECT_EQ( + "Power: On, Mode: 1 (COOL), Temp: 21C, Fan: 11 (HIGH), Sleep: Off, " + "Turbo: On, Ion: On, Swing: On", + ac.toString()); + + // Now change a few already set things. + ac.setSleep(true); + ac.setMode(kVestelAcHeat); + EXPECT_EQ( + "Power: On, Mode: 4 (HEAT), Temp: 21C, Fan: 11 (HIGH), Sleep: On, " + "Turbo: Off, Ion: On, Swing: On", + ac.toString()); + EXPECT_FALSE(ac.isTimeCommand()); + + ac.setTemp(25); + ac.setPower(false); + EXPECT_EQ( + "Power: Off, Mode: 4 (HEAT), Temp: 25C, Fan: 11 (HIGH), Sleep: On, " + "Turbo: Off, Ion: On, Swing: On", + ac.toString()); + EXPECT_FALSE(ac.isTimeCommand()); + + // Check that the checksum is valid. + EXPECT_TRUE(IRVestelAc::validChecksum(ac.getRaw())); + ac.setTime(23 * 60 + 59); + EXPECT_TRUE(ac.isTimeCommand()); + EXPECT_EQ( + "Time: 23:59, Timer: Off, On Timer: Off, Off Timer: Off", + ac.toString()); + ac.setTimer(8 * 60 + 0); + EXPECT_TRUE(ac.isTimeCommand()); + EXPECT_EQ( + "Time: 23:59, Timer: 8:00, On Timer: Off, Off Timer: Off", + ac.toString()); + ac.setOnTimer(7 * 60 + 40); + EXPECT_EQ( + "Time: 23:59, Timer: Off, On Timer: 7:40, Off Timer: Off", + ac.toString()); + ac.setOffTimer(17 * 60 + 10); + EXPECT_EQ( + "Time: 23:59, Timer: Off, On Timer: 7:40, Off Timer: 17:10", + ac.toString()); + ac.setTimer(8 * 60 + 0); + EXPECT_EQ( + "Time: 23:59, Timer: 8:00, On Timer: Off, Off Timer: Off", + ac.toString()); + ac.setTimer(0); + EXPECT_EQ( + "Time: 23:59, Timer: Off, On Timer: Off, Off Timer: Off", + ac.toString()); + ac.on(); + EXPECT_FALSE(ac.isTimeCommand()); + EXPECT_EQ( + "Power: On, Mode: 4 (HEAT), Temp: 25C, Fan: 11 (HIGH), Sleep: On, " + "Turbo: Off, Ion: On, Swing: On", + ac.toString()); +} + +// Tests for decodeVestelAc(). + +// Decode normal "synthetic" messages. +TEST(TestDecodeVestelAc, NormalDecodeWithStrict) { + IRsendTest irsend(0); + IRrecv irrecv(0); + irsend.begin(); + + // With the specific decoder. + uint64_t expectedState = 0x0F00D9001FEF201ULL; + irsend.reset(); + irsend.sendVestelAc(expectedState); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decodeVestelAc(&irsend.capture, kVestelAcBits, true)); + EXPECT_EQ(VESTEL_AC, irsend.capture.decode_type); + EXPECT_EQ(kVestelAcBits, irsend.capture.bits); + EXPECT_FALSE(irsend.capture.repeat); + EXPECT_EQ(expectedState, irsend.capture.value); + EXPECT_EQ(0, irsend.capture.address); + EXPECT_EQ(0, irsend.capture.command); + + // With the all the decoders. + irsend.reset(); + irsend.sendVestelAc(expectedState); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(VESTEL_AC, irsend.capture.decode_type); + EXPECT_EQ(kVestelAcBits, irsend.capture.bits); + EXPECT_FALSE(irsend.capture.repeat); + EXPECT_EQ(expectedState, irsend.capture.value); + EXPECT_EQ(0, irsend.capture.address); + EXPECT_EQ(0, irsend.capture.command); + + IRVestelAc ac(0); + ac.begin(); + ac.setRaw(irsend.capture.value); + EXPECT_EQ( + "Power: On, Mode: 0 (AUTO), Temp: 25C, Fan: 13 (AUTO HOT), Sleep: Off, " + "Turbo: Off, Ion: Off, Swing: Off", + ac.toString()); +} + +// Decode a real message from Raw Data. +TEST(TestDecodeVestelAc, RealNormalExample) { + IRsendTest irsend(0); + IRrecv irrecv(0); + IRVestelAc ac(0); + irsend.begin(); + + uint16_t rawData[115] = { + 3098, 9080, 548, 1538, 526, 492, 526, 468, 524, 468, 526, 468, + 550, 466, 526, 466, 526, 504, 540, 466, 526, 1538, 526, 466, + 526, 466, 552, 1540, 522, 466, 526, 492, 526, 544, 526, 1536, + 526, 1536, 552, 1536, 526, 1536, 552, 1536, 552, 1536, 526, 1536, + 526, 1574, 542, 1536, 526, 492, 526, 466, 526, 494, 524, 468, + 524, 468, 526, 492, 526, 502, 540, 468, 524, 494, 524, 468, + 526, 468, 524, 468, 526, 492, 526, 468, 524, 520, 524, 1538, + 524, 468, 524, 468, 524, 468, 524, 468, 524, 468, 524, 1538, + 524, 506, 538, 468, 524, 468, 524, 1538, 524, 468, 550, 1538, + 550, 1538, 524, 1538, 534, 1528, 544}; // VESTEL_AC + irsend.reset(); + irsend.sendRaw(rawData, 115, 38); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(VESTEL_AC, irsend.capture.decode_type); + EXPECT_EQ(kVestelAcBits, irsend.capture.bits); + EXPECT_FALSE(irsend.capture.repeat); + EXPECT_EQ(0xF4410001FF1201ULL, irsend.capture.value); + EXPECT_EQ(0, irsend.capture.address); + EXPECT_EQ(0, irsend.capture.command); + ac.begin(); + ac.setRaw(irsend.capture.value); + EXPECT_EQ( + "Power: On, Mode: 4 (HEAT), Temp: 16C, Fan: 1 (AUTO), Sleep: Off, " + "Turbo: Off, Ion: On, Swing: Off", + ac.toString()); +} + +TEST(TestDecodeVestelAc, RealTimerExample) { + IRsendTest irsend(0); + IRrecv irrecv(0); + IRVestelAc ac(0); + irsend.begin(); + + uint16_t rawData[115] = { + 3022, 9080, 546, 1536, 526, 466, 526, 492, 526, 468, 526, 492, + 524, 468, 524, 494, 524, 504, 540, 492, 524, 1538, 526, 468, + 524, 492, 526, 466, 552, 1536, 526, 1536, 526, 1570, 542, 492, + 524, 1538, 550, 1538, 524, 1536, 526, 494, 524, 466, 526, 468, + 524, 1574, 540, 1536, 550, 1536, 526, 468, 550, 1536, 526, 492, + 526, 468, 524, 492, 526, 518, 526, 1536, 552, 1536, 550, 1536, + 526, 494, 550, 1538, 526, 492, 524, 1538, 526, 504, 540, 466, + 526, 1536, 526, 1536, 526, 468, 550, 1538, 524, 468, 524, 1538, + 550, 1574, 540, 468, 550, 1538, 526, 492, 524, 468, 526, 466, + 526, 468, 524, 494, 524, 468, 546}; // VESTEL_AC 2D6570B8EE201 + irsend.reset(); + irsend.sendRaw(rawData, 115, 38); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(VESTEL_AC, irsend.capture.decode_type); + EXPECT_EQ(kVestelAcBits, irsend.capture.bits); + EXPECT_FALSE(irsend.capture.repeat); + EXPECT_EQ(0x2D6570B8EE201ULL, irsend.capture.value); + EXPECT_EQ(0, irsend.capture.address); + EXPECT_EQ(0, irsend.capture.command); + ac.begin(); + ac.setRaw(irsend.capture.value); + EXPECT_EQ( + "Time: 5:45, Timer: Off, On Timer: 14:00, Off Timer: 23:00", + ac.toString()); +} + +// General housekeeping +TEST(TestDecodeVestelAc, Housekeeping) { + ASSERT_EQ("VESTEL_AC", typeToString(VESTEL_AC)); + ASSERT_FALSE(hasACState(VESTEL_AC)); // Uses uint64_t, not uint8_t*. +} diff --git a/lib/IRremoteESP8266-2.6.0/test/ir_Whirlpool_test.cpp b/lib/IRremoteESP8266-2.6.0/test/ir_Whirlpool_test.cpp new file mode 100644 index 000000000000..e282989f04b3 --- /dev/null +++ b/lib/IRremoteESP8266-2.6.0/test/ir_Whirlpool_test.cpp @@ -0,0 +1,585 @@ +// Copyright 2018 David Conran + +#include "ir_Whirlpool.h" +#include "IRrecv.h" +#include "IRrecv_test.h" +#include "IRsend.h" +#include "IRsend_test.h" +#include "gtest/gtest.h" + +// Tests for sendWhirlpoolAC(). + +// Test sending typical data only. +TEST(TestSendWhirlpoolAC, SendDataOnly) { + IRsendTest irsend(0); + irsend.begin(); + uint8_t data[kWhirlpoolAcStateLength] = { + 0x83, 0x06, 0x10, 0x71, 0x00, 0x00, 0x91, 0x1F, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xEF, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x02}; + + irsend.sendWhirlpoolAC(data); + EXPECT_EQ( + "f38000d50" + "m8950s4484" + "m597s1649m597s1649m597s533m597s533m597s533m597s533m597s533m597s1649" + "m597s533m597s1649m597s1649m597s533m597s533m597s533m597s533m597s533" + "m597s533m597s533m597s533m597s533m597s1649m597s533m597s533m597s533" + "m597s1649m597s533m597s533m597s533m597s1649m597s1649m597s1649m597s533" + "m597s533m597s533m597s533m597s533m597s533m597s533m597s533m597s533" + "m597s533m597s533m597s533m597s533m597s533m597s533m597s533m597s533" + "m597s7920" + "m597s1649m597s533m597s533m597s533m597s1649m597s533m597s533m597s1649" + "m597s1649m597s1649m597s1649m597s1649m597s1649m597s533m597s533m597s533" + "m597s533m597s533m597s533m597s533m597s533m597s533m597s533m597s533" + "m597s533m597s533m597s533m597s533m597s533m597s533m597s533m597s533" + "m597s533m597s533m597s533m597s533m597s533m597s533m597s533m597s533" + "m597s533m597s533m597s533m597s533m597s533m597s533m597s533m597s533" + "m597s533m597s533m597s533m597s533m597s533m597s533m597s533m597s533" + "m597s1649m597s1649m597s1649m597s1649m597s533m597s1649m597s1649m597s1649" + "m597s7920" + "m597s533m597s533m597s533m597s533m597s533m597s533m597s533m597s533" + "m597s533m597s1649m597s533m597s533m597s533m597s533m597s533m597s533" + "m597s533m597s533m597s533m597s533m597s533m597s533m597s533m597s533" + "m597s533m597s533m597s533m597s533m597s533m597s533m597s533m597s533" + "m597s533m597s533m597s533m597s533m597s533m597s533m597s533m597s533" + "m597s533m597s533m597s533m597s533m597s533m597s533m597s533m597s533" + "m597s533m597s1649m597s533m597s533m597s533m597s533m597s533m597s533" + "m597s100000", + irsend.outputStr()); +} + +// Tests for decodeWhirlpoolAC(). +// Decode normal WhirlpoolAC messages. +TEST(TestDecodeWhirlpoolAC, SyntheticDecode) { + IRsendTest irsend(0); + IRrecv irrecv(0); + irsend.begin(); + + // Synthesised Normal WhirlpoolAC message. + irsend.reset(); + uint8_t expectedState[kWhirlpoolAcStateLength] = { + 0x83, 0x06, 0x10, 0x71, 0x00, 0x00, 0x91, 0x1F, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xEF, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x02}; + irsend.sendWhirlpoolAC(expectedState); + irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(WHIRLPOOL_AC, irsend.capture.decode_type); + EXPECT_EQ(kWhirlpoolAcBits, irsend.capture.bits); + EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits); + IRWhirlpoolAc ac(0); + ac.setRaw(irsend.capture.state); + EXPECT_EQ( + "Model: 1 (DG11J13A), Power toggle: Off, Mode: 1 (AUTO), Temp: 25C, " + "Fan: 0 (AUTO), Swing: Off, Light: On, Clock: 17:31, On Timer: Off, " + "Off Timer: Off, Sleep: Off, Super: Off, Command: 2 (TEMP)", + ac.toString()); +} + +TEST(TestDecodeWhirlpoolAC, Real26CFanAutoCoolingSwingOnClock1918) { + IRsendTest irsend(0); + IRrecv irrecv(0); + irsend.begin(); + + irsend.reset(); + uint8_t expectedState[kWhirlpoolAcStateLength] = { + 0x83, 0x06, 0x80, 0x82, 0x00, 0x00, 0x93, 0x12, 0x40, 0x00, 0x00, + 0x00, 0x00, 0xC3, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x07}; + irsend.sendWhirlpoolAC(expectedState); + irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(WHIRLPOOL_AC, irsend.capture.decode_type); + EXPECT_EQ(kWhirlpoolAcBits, irsend.capture.bits); + EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits); + IRWhirlpoolAc ac(0); + ac.setRaw(irsend.capture.state); + EXPECT_EQ( + "Model: 1 (DG11J13A), Power toggle: Off, Mode: 2 (COOL), Temp: 26C, " + "Fan: 0 (AUTO), Swing: On, Light: On, Clock: 19:18, On Timer: Off, " + "Off Timer: Off, Sleep: Off, Super: Off, Command: 7 (SWING)", + ac.toString()); +} + +TEST(TestDecodeWhirlpoolAC, RealTimerExample) { + IRsendTest irsend(0); + IRrecv irrecv(0); + irsend.begin(); + + irsend.reset(); + // Dehumidify timer on 7:40 off 8:05 + uint16_t rawData[343] = { + 9092, 4556, 604, 1664, 604, 1674, 630, 514, 630, 518, 628, 522, + 604, 550, 628, 530, 602, 1680, 630, 508, 630, 1644, 604, 1674, + 604, 544, 604, 548, 630, 524, 604, 554, 620, 530, 630, 506, + 602, 538, 602, 542, 604, 542, 604, 546, 630, 524, 602, 556, + 628, 518, 604, 1666, 632, 1644, 604, 540, 602, 546, 604, 1680, + 604, 1684, 604, 1686, 630, 520, 602, 534, 606, 538, 602, 540, + 604, 544, 604, 548, 602, 552, 630, 528, 602, 546, 602, 536, + 628, 510, 606, 540, 604, 544, 630, 522, 604, 554, 600, 554, + 602, 528, 602, 8032, 604, 1666, 604, 1668, 602, 1676, 630, 518, + 630, 520, 602, 550, 604, 554, 604, 1678, 630, 1640, 602, 1672, + 602, 542, 602, 544, 628, 522, 630, 1658, 604, 554, 628, 1652, + 630, 508, 602, 538, 630, 514, 630, 1652, 602, 546, 604, 550, + 602, 554, 602, 546, 630, 1638, 604, 536, 630, 1646, 602, 544, + 628, 522, 632, 524, 628, 528, 602, 1686, 594, 1666, 604, 1670, + 602, 1674, 632, 516, 604, 546, 638, 518, 622, 534, 628, 518, + 604, 532, 604, 536, 600, 550, 622, 1652, 630, 520, 602, 1684, + 602, 554, 602, 544, 630, 506, 628, 512, 602, 540, 628, 518, + 602, 550, 602, 552, 604, 554, 602, 544, 628, 1642, 602, 536, + 632, 1646, 630, 516, 602, 1680, 630, 1656, 604, 1688, 602, 1660, + 602, 8030, 604, 532, 604, 536, 604, 540, 602, 544, 628, 522, + 602, 552, 602, 556, 602, 544, 602, 1666, 630, 510, 602, 1674, + 604, 544, 628, 522, 602, 552, 630, 526, 628, 520, 602, 534, + 630, 510, 604, 540, 602, 544, 606, 544, 604, 550, 604, 554, + 602, 544, 604, 534, 602, 538, 602, 542, 604, 542, 604, 546, + 604, 550, 632, 526, 604, 544, 630, 506, 604, 536, 604, 540, + 628, 518, 602, 548, 604, 550, 604, 552, 630, 516, 602, 534, + 604, 536, 630, 512, 604, 544, 602, 548, 630, 524, 602, 554, + 602, 542, 604, 1666, 606, 532, 630, 1644, 602, 544, 630, 520, + 604, 550, 604, 554, 602, 526, 598}; + uint8_t expectedState[kWhirlpoolAcStateLength] = { + 0x83, 0x06, 0x00, 0x73, 0x00, 0x00, 0x87, 0xA3, 0x08, 0x85, 0x07, + 0x28, 0x00, 0xF5, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x05}; + irsend.sendRaw(rawData, 343, 38000); + irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(WHIRLPOOL_AC, irsend.capture.decode_type); + EXPECT_EQ(kWhirlpoolAcBits, irsend.capture.bits); + EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits); + IRWhirlpoolAc ac(0); + ac.setRaw(irsend.capture.state); + EXPECT_EQ( + "Model: 1 (DG11J13A), Power toggle: Off, Mode: 3 (DRY), Temp: 25C, " + "Fan: 0 (AUTO), Swing: Off, Light: On, Clock: 07:35, On Timer: 07:40, " + "Off Timer: 08:05, Sleep: Off, Super: Off, Command: 5 (ONTIMER)", + ac.toString()); +} + +// Decode a recorded example +TEST(TestDecodeWhirlpoolAC, RealExampleDecode) { + IRsendTest irsend(0); + IRrecv irrecv(0); + irsend.begin(); + + // Real WhirlpoolAC message. + // Ref: https://github.com/markszabo/IRremoteESP8266/issues/509 + uint16_t rawData[343] = { + 8950, 4484, 598, 1642, 598, 1646, 594, 534, 594, 538, 602, 532, + 598, 540, 600, 542, 598, 1650, 600, 522, 598, 1644, 596, 1650, + 600, 532, 598, 538, 602, 536, 594, 548, 592, 538, 602, 518, + 600, 524, 596, 532, 598, 532, 598, 1654, 596, 544, 596, 544, + 596, 536, 594, 1644, 596, 528, 600, 528, 592, 538, 602, 1648, + 602, 1654, 596, 1664, 598, 534, 594, 526, 594, 530, 598, 528, + 602, 530, 600, 534, 596, 542, 598, 542, 598, 534, 596, 526, + 594, 530, 600, 528, 602, 530, 600, 534, 596, 542, 598, 544, + 596, 518, 602, 7916, 598, 1642, 598, 528, 600, 528, 602, 530, + 600, 1652, 598, 542, 598, 544, 596, 1654, 596, 1644, 596, 1648, + 602, 1644, 596, 1654, 596, 1656, 604, 536, 594, 548, 602, 528, + 600, 520, 600, 524, 596, 532, 598, 532, 596, 538, 602, 536, + 594, 546, 594, 538, 602, 518, 600, 524, 596, 532, 598, 532, + 598, 536, 594, 544, 596, 544, 596, 536, 594, 526, 592, 530, + 600, 528, 600, 530, 602, 532, 596, 542, 598, 542, 598, 534, + 596, 524, 596, 528, 600, 526, 592, 538, 592, 542, 598, 540, + 600, 540, 600, 530, 598, 522, 598, 526, 594, 534, 596, 534, + 594, 540, 602, 536, 592, 548, 592, 538, 600, 1636, 594, 1648, + 602, 1642, 598, 1652, 598, 538, 602, 1680, 570, 1662, 598, 1634, + 596, 7924, 600, 520, 598, 526, 592, 534, 596, 534, 596, 540, + 600, 536, 604, 538, 602, 530, 600, 520, 598, 1640, 600, 528, + 600, 530, 600, 534, 594, 544, 596, 544, 596, 534, 596, 526, + 594, 528, 600, 526, 594, 536, 592, 542, 598, 538, 602, 538, + 602, 528, 600, 520, 600, 524, 596, 530, 600, 532, 598, 534, + 596, 542, 598, 542, 598, 532, 598, 524, 596, 528, 602, 526, + 594, 536, 594, 540, 600, 536, 594, 548, 592, 538, 602, 518, + 602, 522, 596, 530, 600, 530, 600, 534, 596, 542, 598, 544, + 596, 534, 596, 524, 594, 1644, 596, 532, 596, 534, 596, 538, + 602, 536, 594, 546, 594, 520, 600}; + uint8_t expectedState[kWhirlpoolAcStateLength] = { + 0x83, 0x06, 0x10, 0x71, 0x00, 0x00, 0x91, 0x1F, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xEF, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x02}; + + irsend.reset(); + irsend.sendRaw(rawData, 343, 38000); + irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(WHIRLPOOL_AC, irsend.capture.decode_type); + EXPECT_EQ(kWhirlpoolAcBits, irsend.capture.bits); + EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits); + IRWhirlpoolAc ac(0); + ac.setRaw(irsend.capture.state); + EXPECT_EQ( + "Model: 1 (DG11J13A), Power toggle: Off, Mode: 1 (AUTO), Temp: 25C, " + "Fan: 0 (AUTO), Swing: Off, Light: On, Clock: 17:31, On Timer: Off, " + "Off Timer: Off, Sleep: Off, Super: Off, Command: 2 (TEMP)", + ac.toString()); +} + +// Tests for IRWhirlpoolAc class. + +TEST(TestIRWhirlpoolAcClass, SetAndGetRaw) { + uint8_t expectedState[kWhirlpoolAcStateLength] = { + 0x83, 0x06, 0x10, 0x71, 0x00, 0x00, 0x91, 0x1F, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xEF, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x02}; + IRWhirlpoolAc ac(0); + ac.setRaw(expectedState); + EXPECT_STATE_EQ(expectedState, ac.getRaw(), kWhirlpoolAcBits); +} + +TEST(TestIRWhirlpoolAcClass, SetAndGetTemp) { + IRWhirlpoolAc ac(0); + ac.setCommand(0); // Clear the previous command. + + ac.setModel(DG11J13A); + ac.setTemp(25); + EXPECT_EQ(25, ac.getTemp()); + EXPECT_EQ(kWhirlpoolAcCommandTemp, ac.getCommand()); + ac.setTemp(kWhirlpoolAcMinTemp); + EXPECT_EQ(kWhirlpoolAcMinTemp, ac.getTemp()); + ac.setTemp(kWhirlpoolAcMinTemp - 1); + EXPECT_EQ(kWhirlpoolAcMinTemp, ac.getTemp()); + ac.setTemp(kWhirlpoolAcMaxTemp); + EXPECT_EQ(kWhirlpoolAcMaxTemp, ac.getTemp()); + ac.setTemp(kWhirlpoolAcMaxTemp + 1); + EXPECT_EQ(kWhirlpoolAcMaxTemp, ac.getTemp()); + + ac.setModel(DG11J191); // Has a -2 offset on min/max temps. + ac.setTemp(25); + EXPECT_EQ(25, ac.getTemp()); + EXPECT_EQ(kWhirlpoolAcCommandTemp, ac.getCommand()); + ac.setTemp(kWhirlpoolAcMinTemp - 2); + EXPECT_EQ(kWhirlpoolAcMinTemp - 2, ac.getTemp()); + ac.setTemp(kWhirlpoolAcMinTemp - 2 - 1); + EXPECT_EQ(kWhirlpoolAcMinTemp - 2 , ac.getTemp()); + ac.setTemp(kWhirlpoolAcMaxTemp - 2); + EXPECT_EQ(kWhirlpoolAcMaxTemp - 2, ac.getTemp()); + ac.setTemp(kWhirlpoolAcMaxTemp - 2 + 1); + EXPECT_EQ(kWhirlpoolAcMaxTemp - 2, ac.getTemp()); +} + +TEST(TestIRWhirlpoolAcClass, SetAndGetMode) { + IRWhirlpoolAc ac(0); + ac.setCommand(0); // Clear the previous command. + + ac.setMode(kWhirlpoolAcCool); + EXPECT_EQ(kWhirlpoolAcCool, ac.getMode()); + EXPECT_EQ(kWhirlpoolAcCommandMode, ac.getCommand()); + ac.setMode(kWhirlpoolAcHeat); + EXPECT_EQ(kWhirlpoolAcHeat, ac.getMode()); + ac.setMode(kWhirlpoolAcAuto); + EXPECT_EQ(kWhirlpoolAcAuto, ac.getMode()); + EXPECT_EQ(kWhirlpoolAcCommand6thSense, ac.getCommand()); + ac.setMode(kWhirlpoolAcDry); + EXPECT_EQ(kWhirlpoolAcDry, ac.getMode()); + EXPECT_EQ(kWhirlpoolAcCommandMode, ac.getCommand()); +} + +TEST(TestIRWhirlpoolAcClass, SetAndGetFan) { + IRWhirlpoolAc ac(0); + ac.setCommand(0); // Clear the previous command. + + ac.setFan(kWhirlpoolAcFanAuto); + EXPECT_EQ(kWhirlpoolAcFanAuto, ac.getFan()); + EXPECT_EQ(kWhirlpoolAcCommandFanSpeed, ac.getCommand()); + ac.setFan(kWhirlpoolAcFanLow); + EXPECT_EQ(kWhirlpoolAcFanLow, ac.getFan()); + ac.setFan(kWhirlpoolAcFanMedium); + EXPECT_EQ(kWhirlpoolAcFanMedium, ac.getFan()); + ac.setFan(kWhirlpoolAcFanHigh); + EXPECT_EQ(kWhirlpoolAcFanHigh, ac.getFan()); + ac.setFan(kWhirlpoolAcFanAuto); + EXPECT_EQ(kWhirlpoolAcFanAuto, ac.getFan()); + + // Known state with a non-auto fan mode. + const uint8_t state[21] = {0x83, 0x06, 0x0B, 0x82, 0x00, 0x00, 0x93, + 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E, + 0x00, 0x03, 0x00, 0x00, 0x08, 0x00, 0x0B}; + ac.setRaw(state); + EXPECT_EQ(kWhirlpoolAcFanLow, ac.getFan()); +} + +TEST(TestIRWhirlpoolAcClass, SetAndGetSwing) { + IRWhirlpoolAc ac(0); + ac.setCommand(0); // Clear the previous command. + + ac.setSwing(true); + EXPECT_TRUE(ac.getSwing()); + EXPECT_EQ(kWhirlpoolAcCommandSwing, ac.getCommand()); + ac.setSwing(false); + EXPECT_FALSE(ac.getSwing()); + ac.setSwing(true); + EXPECT_TRUE(ac.getSwing()); +} + +TEST(TestIRWhirlpoolAcClass, SetAndGetLight) { + IRWhirlpoolAc ac(0); + ac.setCommand(0); // Clear the previous command. + + ac.setLight(true); + EXPECT_TRUE(ac.getLight()); + ac.setLight(false); + EXPECT_FALSE(ac.getLight()); + ac.setLight(true); + EXPECT_TRUE(ac.getLight()); +} + +TEST(TestIRWhirlpoolAcClass, SetAndGetClock) { + IRWhirlpoolAc ac(0); + ac.setClock(0); + EXPECT_EQ(0, ac.getClock()); + EXPECT_EQ("00:00", ac.timeToString(ac.getClock())); + ac.setClock(1); + EXPECT_EQ(1, ac.getClock()); + EXPECT_EQ("00:01", ac.timeToString(ac.getClock())); + ac.setClock(12 * 60 + 34); + EXPECT_EQ(12 * 60 + 34, ac.getClock()); + EXPECT_EQ("12:34", ac.timeToString(ac.getClock())); + ac.setClock(7 * 60 + 5); + EXPECT_EQ(7 * 60 + 5, ac.getClock()); + EXPECT_EQ("07:05", ac.timeToString(ac.getClock())); + ac.setClock(23 * 60 + 59); + EXPECT_EQ(23 * 60 + 59, ac.getClock()); + EXPECT_EQ("23:59", ac.timeToString(ac.getClock())); + ac.setClock(24 * 60 + 0); + EXPECT_EQ(0, ac.getClock()); + EXPECT_EQ("00:00", ac.timeToString(ac.getClock())); + ac.setClock(25 * 60 + 23); + EXPECT_EQ(1 * 60 + 23, ac.getClock()); + EXPECT_EQ("01:23", ac.timeToString(ac.getClock())); +} + +TEST(TestIRWhirlpoolAcClass, OnOffTimers) { + IRWhirlpoolAc ac(0); + ac.setCommand(0); // Clear the previous command. + + // On Timer + ac.enableOnTimer(false); + ac.setOnTimer(0); + EXPECT_EQ(0, ac.getOnTimer()); + EXPECT_EQ("00:00", ac.timeToString(ac.getOnTimer())); + EXPECT_FALSE(ac.isOnTimerEnabled()); + EXPECT_EQ(kWhirlpoolAcCommandOnTimer, ac.getCommand()); + ac.setOnTimer(1); + EXPECT_EQ(1, ac.getOnTimer()); + EXPECT_EQ("00:01", ac.timeToString(ac.getOnTimer())); + ac.enableOnTimer(true); + ac.setOnTimer(12 * 60 + 34); + EXPECT_EQ(12 * 60 + 34, ac.getOnTimer()); + EXPECT_EQ("12:34", ac.timeToString(ac.getOnTimer())); + EXPECT_TRUE(ac.isOnTimerEnabled()); + ac.setOnTimer(7 * 60 + 5); + EXPECT_EQ(7 * 60 + 5, ac.getOnTimer()); + EXPECT_EQ("07:05", ac.timeToString(ac.getOnTimer())); + ac.setOnTimer(23 * 60 + 59); + EXPECT_EQ(23 * 60 + 59, ac.getOnTimer()); + EXPECT_EQ("23:59", ac.timeToString(ac.getOnTimer())); + ac.setOnTimer(24 * 60 + 0); + EXPECT_EQ(0, ac.getOnTimer()); + EXPECT_EQ("00:00", ac.timeToString(ac.getOnTimer())); + ac.setOnTimer(25 * 60 + 23); + EXPECT_EQ(1 * 60 + 23, ac.getOnTimer()); + EXPECT_EQ("01:23", ac.timeToString(ac.getOnTimer())); + // Off Timer + ac.enableOffTimer(false); + ac.setOffTimer(0); + EXPECT_EQ(0, ac.getOffTimer()); + EXPECT_EQ("00:00", ac.timeToString(ac.getOffTimer())); + EXPECT_FALSE(ac.isOffTimerEnabled()); + EXPECT_EQ(kWhirlpoolAcCommandOffTimer, ac.getCommand()); + ac.setOffTimer(1); + EXPECT_EQ(1, ac.getOffTimer()); + EXPECT_EQ("00:01", ac.timeToString(ac.getOffTimer())); + ac.enableOffTimer(true); + ac.setOffTimer(12 * 60 + 34); + EXPECT_EQ(12 * 60 + 34, ac.getOffTimer()); + EXPECT_EQ("12:34", ac.timeToString(ac.getOffTimer())); + EXPECT_TRUE(ac.isOffTimerEnabled()); + ac.setOffTimer(7 * 60 + 5); + EXPECT_EQ(7 * 60 + 5, ac.getOffTimer()); + EXPECT_EQ("07:05", ac.timeToString(ac.getOffTimer())); + ac.setOffTimer(23 * 60 + 59); + EXPECT_EQ(23 * 60 + 59, ac.getOffTimer()); + EXPECT_EQ("23:59", ac.timeToString(ac.getOffTimer())); + ac.setOffTimer(24 * 60 + 0); + EXPECT_EQ(0, ac.getOffTimer()); + EXPECT_EQ("00:00", ac.timeToString(ac.getOffTimer())); + ac.setOffTimer(25 * 60 + 23); + EXPECT_EQ(1 * 60 + 23, ac.getOffTimer()); + EXPECT_EQ("01:23", ac.timeToString(ac.getOffTimer())); +} + +TEST(TestIRWhirlpoolAcClass, SetAndGetCommand) { + IRWhirlpoolAc ac(0); + ac.setCommand(0); + EXPECT_EQ(0, ac.getCommand()); + ac.setCommand(kWhirlpoolAcCommandFanSpeed); + EXPECT_EQ(kWhirlpoolAcCommandFanSpeed, ac.getCommand()); + ac.setCommand(255); + EXPECT_EQ(255, ac.getCommand()); +} + +TEST(TestIRWhirlpoolAcClass, SetAndGetPowerToggle) { + IRWhirlpoolAc ac(0); + ac.setCommand(0); + + ac.setPowerToggle(false); + EXPECT_FALSE(ac.getPowerToggle()); + ac.setPowerToggle(true); + EXPECT_TRUE(ac.getPowerToggle()); + ac.setPowerToggle(false); + EXPECT_FALSE(ac.getPowerToggle()); + + // Known state with a power toggle in it. + uint8_t state[21] = {0x83, 0x06, 0x07, 0x82, 0x00, 0x00, 0x93, + 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, + 0x00, 0x01, 0x00, 0x00, 0x08, 0x00, 0x09}; + ac.setRaw(state); + EXPECT_TRUE(ac.getPowerToggle()); +} + +TEST(TestIRWhirlpoolAcClass, SetAndGetModel) { + IRWhirlpoolAc ac(0); + ac.setTemp(19); + ac.setCommand(0); // Set model shouldn't change the command setting. + + ac.setModel(DG11J191); + EXPECT_EQ(DG11J191, ac.getModel()); + EXPECT_EQ(19, ac.getTemp()); + EXPECT_EQ(0, ac.getCommand()); + ac.setModel(DG11J13A); + EXPECT_EQ(DG11J13A, ac.getModel()); + EXPECT_EQ(19, ac.getTemp()); + ac.setModel(DG11J191); + EXPECT_EQ(DG11J191, ac.getModel()); + EXPECT_EQ(19, ac.getTemp()); + EXPECT_EQ(0, ac.getCommand()); + + // One of the models has a lower min temp. Check that desired temp is kept. + ac.setTemp(16); + ac.setCommand(0); // Set model shouldn't change the command setting. + EXPECT_EQ(16, ac.getTemp()); + EXPECT_EQ(0, ac.getCommand()); + ac.setModel(DG11J13A); + EXPECT_EQ(DG11J13A, ac.getModel()); + EXPECT_EQ(18, ac.getTemp()); + ac.setModel(DG11J191); + EXPECT_EQ(DG11J191, ac.getModel()); + EXPECT_EQ(16, ac.getTemp()); + EXPECT_EQ(0, ac.getCommand()); + + // Known states with different models. + uint8_t state_1[21] = {0x83, 0x06, 0x01, 0x30, 0x00, 0x00, 0x92, + 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x95, + 0x00, 0x02, 0x00, 0x00, 0x08, 0x00, 0x0A}; + uint8_t state_2[21] = {0x83, 0x06, 0x00, 0x30, 0x00, 0x00, 0x8B, + 0x35, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8E, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x02}; + + ac.setRaw(state_1); + EXPECT_EQ(DG11J191, ac.getModel()); + ac.setRaw(state_2); + EXPECT_EQ(DG11J13A, ac.getModel()); +} + +TEST(TestIRWhirlpoolAcClass, SetAndGetSleep) { + IRWhirlpoolAc ac(0); + ac.setFan(kWhirlpoolAcFanAuto); + ac.setCommand(0); + + ac.setSleep(false); + EXPECT_FALSE(ac.getSleep()); + EXPECT_EQ(kWhirlpoolAcCommandSleep, ac.getCommand()); + ac.setSleep(true); + EXPECT_TRUE(ac.getSleep()); + EXPECT_EQ(kWhirlpoolAcCommandSleep, ac.getCommand()); + EXPECT_EQ(kWhirlpoolAcFanLow, ac.getFan()); + ac.setSleep(false); + EXPECT_FALSE(ac.getSleep()); + + // Known state with sleep mode in it. + uint8_t state[21] = {0x83, 0x06, 0x0B, 0x73, 0x00, 0x00, 0x90, + 0x9E, 0x00, 0xA0, 0x17, 0x3A, 0x00, 0xFB, + 0x00, 0x03, 0x00, 0x00, 0x08, 0x00, 0x0B}; + ac.setRaw(state); + EXPECT_TRUE(ac.getSleep()); +} + +TEST(TestIRWhirlpoolAcClass, SetAndGetSuper) { + IRWhirlpoolAc ac(0); + ac.setFan(kWhirlpoolAcFanAuto); + ac.setMode(kWhirlpoolAcDry); + ac.setCommand(0); + + ac.setSuper(false); + EXPECT_FALSE(ac.getSuper()); + EXPECT_EQ(kWhirlpoolAcCommandSuper, ac.getCommand()); + ac.setSuper(true); + EXPECT_TRUE(ac.getSuper()); + EXPECT_EQ(kWhirlpoolAcCommandSuper, ac.getCommand()); + EXPECT_EQ(kWhirlpoolAcFanHigh, ac.getFan()); + EXPECT_EQ(kWhirlpoolAcCool, ac.getMode()); + EXPECT_EQ(kWhirlpoolAcMinTemp, ac.getTemp()); + + ac.setSuper(false); + EXPECT_FALSE(ac.getSuper()); + EXPECT_EQ(kWhirlpoolAcFanHigh, ac.getFan()); + EXPECT_EQ(kWhirlpoolAcCool, ac.getMode()); + EXPECT_EQ(kWhirlpoolAcMinTemp, ac.getTemp()); + + // When in heat mode, it should stay in heat mode. + ac.setFan(kWhirlpoolAcFanAuto); + ac.setMode(kWhirlpoolAcHeat); + ac.setSuper(true); + EXPECT_TRUE(ac.getSuper()); + EXPECT_EQ(kWhirlpoolAcCommandSuper, ac.getCommand()); + EXPECT_EQ(kWhirlpoolAcFanHigh, ac.getFan()); + EXPECT_EQ(kWhirlpoolAcHeat, ac.getMode()); + EXPECT_EQ(kWhirlpoolAcMaxTemp, ac.getTemp()); + + // Changing mode/temp/fan/power should cancel super, + ac.setMode(kWhirlpoolAcCool); + EXPECT_FALSE(ac.getSuper()); + ac.setSuper(true); + ac.setTemp(25); + EXPECT_FALSE(ac.getSuper()); + ac.setSuper(true); + ac.setFan(kWhirlpoolAcFanMedium); + EXPECT_FALSE(ac.getSuper()); + ac.setSuper(true); + ac.setPowerToggle(true); + EXPECT_FALSE(ac.getSuper()); + + // Known state with Super mode in it. + uint8_t state[21] = {0x83, 0x06, 0x01, 0x02, 0x00, 0x90, 0x90, + 0x9F, 0x00, 0xA0, 0x17, 0x3A, 0x00, 0x11, + 0x00, 0x04, 0x00, 0x00, 0x08, 0x00, 0x0C}; + ac.setRaw(state); + EXPECT_TRUE(ac.getSuper()); +} + +// Build a known good message from scratch. +TEST(TestIRWhirlpoolAcClass, MessageConstruction) { + // Real example captured from a remote. (ref: RealTimerExample) + uint8_t expectedState[kWhirlpoolAcStateLength] = { + 0x83, 0x06, 0x00, 0x73, 0x00, 0x00, 0x87, 0xA3, 0x08, 0x85, 0x07, + 0x28, 0x00, 0xF5, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x05}; + IRWhirlpoolAc ac(0); + ac.setModel(DG11J13A); + ac.setTemp(25); + ac.setPowerToggle(false); + ac.setMode(kWhirlpoolAcDry); + ac.setFan(kWhirlpoolAcFanAuto); + ac.setSwing(false); + ac.setLight(true); + ac.setClock(7 * 60 + 35); + ac.setOnTimer(7 * 60 + 40); + ac.setOffTimer(8 * 60 + 5); + ac.enableOffTimer(true); + ac.setSleep(false); + ac.setSuper(false); + ac.enableOnTimer(true); + + EXPECT_EQ( + "Model: 1 (DG11J13A), Power toggle: Off, Mode: 3 (DRY), Temp: 25C, " + "Fan: 0 (AUTO), Swing: Off, Light: On, Clock: 07:35, On Timer: 07:40, " + "Off Timer: 08:05, Sleep: Off, Super: Off, Command: 5 (ONTIMER)", + ac.toString()); + EXPECT_STATE_EQ(expectedState, ac.getRaw(), kWhirlpoolAcBits); +} diff --git a/lib/IRremoteESP8266-2.5.2.03/test/ir_Whynter_test.cpp b/lib/IRremoteESP8266-2.6.0/test/ir_Whynter_test.cpp similarity index 98% rename from lib/IRremoteESP8266-2.5.2.03/test/ir_Whynter_test.cpp rename to lib/IRremoteESP8266-2.6.0/test/ir_Whynter_test.cpp index 748a4c9bf460..92ced5cf613a 100644 --- a/lib/IRremoteESP8266-2.5.2.03/test/ir_Whynter_test.cpp +++ b/lib/IRremoteESP8266-2.6.0/test/ir_Whynter_test.cpp @@ -14,6 +14,7 @@ TEST(TestSendWhynter, SendDataOnly) { irsend.reset(); irsend.sendWhynter(0x0); EXPECT_EQ( + "f38000d50" "m750s750m2850s2850" "m750s750m750s750m750s750m750s750m750s750m750s750m750s750m750s750" "m750s750m750s750m750s750m750s750m750s750m750s750m750s750m750s750" @@ -25,6 +26,7 @@ TEST(TestSendWhynter, SendDataOnly) { irsend.reset(); irsend.sendWhynter(0xFFFFFFFF); EXPECT_EQ( + "f38000d50" "m750s750m2850s2850" "m750s2150m750s2150m750s2150m750s2150m750s2150m750s2150m750s2150m750s2150" "m750s2150m750s2150m750s2150m750s2150m750s2150m750s2150m750s2150m750s2150" @@ -36,6 +38,7 @@ TEST(TestSendWhynter, SendDataOnly) { irsend.reset(); irsend.sendWhynter(0x87654321); EXPECT_EQ( + "f38000d50" "m750s750m2850s2850" "m750s2150m750s750m750s750m750s750m750s750m750s2150m750s2150m750s2150" "m750s750m750s2150m750s2150m750s750m750s750m750s2150m750s750m750s2150" @@ -53,6 +56,7 @@ TEST(TestSendWhynter, SendWithRepeats) { irsend.reset(); irsend.sendWhynter(0x87654321, kWhynterBits, 0); // 0 repeats. EXPECT_EQ( + "f38000d50" "m750s750m2850s2850" "m750s2150m750s750m750s750m750s750m750s750m750s2150m750s2150m750s2150" "m750s750m750s2150m750s2150m750s750m750s750m750s2150m750s750m750s2150" @@ -64,6 +68,7 @@ TEST(TestSendWhynter, SendWithRepeats) { irsend.reset(); irsend.sendWhynter(0x87654321, kWhynterBits, 1); // 1 repeat. EXPECT_EQ( + "f38000d50" "m750s750m2850s2850" "m750s2150m750s750m750s750m750s750m750s750m750s2150m750s2150m750s2150" "m750s750m750s2150m750s2150m750s750m750s750m750s2150m750s750m750s2150" @@ -81,6 +86,7 @@ TEST(TestSendWhynter, SendWithRepeats) { irsend.reset(); irsend.sendWhynter(0x87654321, kWhynterBits, 2); // 2 repeats. EXPECT_EQ( + "f38000d50" "m750s750m2850s2850" "m750s2150m750s750m750s750m750s750m750s750m750s2150m750s2150m750s2150" "m750s750m750s2150m750s2150m750s750m750s750m750s2150m750s750m750s2150" @@ -110,6 +116,7 @@ TEST(TestSendWhynter, SendUnusualSize) { irsend.reset(); irsend.sendWhynter(0x0, 8); EXPECT_EQ( + "f38000d50" "m750s750m2850s2850" "m750s750m750s750m750s750m750s750m750s750m750s750m750s750m750s750" "m750s88050", @@ -118,6 +125,7 @@ TEST(TestSendWhynter, SendUnusualSize) { irsend.reset(); irsend.sendWhynter(0x1234567890ABCDEF, 64); EXPECT_EQ( + "f38000d50" "m750s750m2850s2850" "m750s750m750s750m750s750m750s2150m750s750m750s750m750s2150m750s750" "m750s750m750s750m750s2150m750s2150m750s750m750s2150m750s750m750s750" diff --git a/lib/IRremoteESP8266-2.5.2.03/tools/Makefile b/lib/IRremoteESP8266-2.6.0/tools/Makefile similarity index 76% rename from lib/IRremoteESP8266-2.5.2.03/tools/Makefile rename to lib/IRremoteESP8266-2.6.0/tools/Makefile index c303e051db39..08488949c6a9 100644 --- a/lib/IRremoteESP8266-2.5.2.03/tools/Makefile +++ b/lib/IRremoteESP8266-2.6.0/tools/Makefile @@ -14,13 +14,14 @@ USER_DIR = ../src # Where to find test code. TEST_DIR = ../test +INCLUDES = -I$(USER_DIR) -I$(TEST_DIR) # Flags passed to the preprocessor. # Set Google Test's header directory as a system directory, such that # the compiler doesn't generate warnings in Google Test headers. CPPFLAGS += -DUNIT_TEST # Flags passed to the C++ compiler. -CXXFLAGS += -g -Wall -Wextra -pthread +CXXFLAGS += -g -Wall -Wextra -pthread -std=gnu++11 all : gc_decode mode2_decode @@ -48,25 +49,27 @@ PROTOCOLS = ir_NEC.o ir_Sony.o ir_Samsung.o ir_JVC.o ir_RCMM.o ir_RC5_RC6.o \ ir_Pronto.o ir_GlobalCache.o ir_Nikai.o ir_Toshiba.o ir_Midea.o \ ir_Magiquest.o ir_Lasertag.o ir_Carrier.o ir_Haier.o ir_Hitachi.o \ ir_GICable.o ir_Whirlpool.o ir_Lutron.o ir_Electra.o ir_Pioneer.o \ - ir_MWM.o + ir_MWM.o ir_Vestel.o ir_Teco.o ir_Tcl.o ir_Lego.o \ + ir_MitsubishiHeavy.o # Common object files COMMON_OBJ = IRutils.o IRtimer.o IRsend.o IRrecv.o $(PROTOCOLS) # Common dependencies COMMON_DEPS = $(USER_DIR)/IRrecv.h $(USER_DIR)/IRsend.h $(USER_DIR)/IRtimer.h \ - $(USER_DIR)/IRutils.h $(USER_DIR)/IRremoteESP8266.h + $(USER_DIR)/IRutils.h $(USER_DIR)/IRremoteESP8266.h \ + $(TEST_DIR)/IRsend_test.h # Common test dependencies COMMON_TEST_DEPS = $(COMMON_DEPS) $(TEST_DIR)/IRsend_test.h gc_decode.o : gc_decode.cpp $(COMMON_TEST_DEPS) $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -I$(USER_DIR) -I$(TEST_DIR) -c gc_decode.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c gc_decode.cpp gc_decode : $(COMMON_OBJ) gc_decode.o $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ mode2_decode.o : mode2_decode.cpp $(COMMON_TEST_DEPS) $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -I$(USER_DIR) -I$(TEST_DIR) -c mode2_decode.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c mode2_decode.cpp mode2_decode : $(COMMON_OBJ) mode2_decode.o $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ @@ -83,7 +86,6 @@ IRsend.o : $(USER_DIR)/IRsend.cpp $(USER_DIR)/IRsend.h $(USER_DIR)/IRremoteESP82 IRrecv.o : $(USER_DIR)/IRrecv.cpp $(USER_DIR)/IRrecv.h $(USER_DIR)/IRremoteESP8266.h $(GTEST_HEADERS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/IRrecv.cpp - ir_NEC.o : $(USER_DIR)/ir_NEC.cpp $(COMMON_DEPS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_NEC.cpp @@ -97,10 +99,10 @@ ir_Sony.o : $(USER_DIR)/ir_Sony.cpp $(COMMON_DEPS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Sony.cpp ir_Samsung.o : $(USER_DIR)/ir_Samsung.cpp $(USER_DIR)/ir_Samsung.h $(COMMON_DEPS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Samsung.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/ir_Samsung.cpp ir_Kelvinator.o : $(USER_DIR)/ir_Kelvinator.cpp $(USER_DIR)/ir_Kelvinator.h $(COMMON_DEPS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Kelvinator.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/ir_Kelvinator.cpp ir_JVC.o : $(USER_DIR)/ir_JVC.cpp $(COMMON_DEPS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_JVC.cpp @@ -112,10 +114,13 @@ ir_LG.o : $(USER_DIR)/ir_LG.h $(USER_DIR)/ir_LG.cpp $(COMMON_DEPS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_LG.cpp ir_Mitsubishi.o : $(USER_DIR)/ir_Mitsubishi.h $(USER_DIR)/ir_Mitsubishi.cpp $(COMMON_DEPS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Mitsubishi.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/ir_Mitsubishi.cpp + +ir_MitsubishiHeavy.o : $(USER_DIR)/ir_MitsubishiHeavy.h $(USER_DIR)/ir_MitsubishiHeavy.cpp $(COMMON_DEPS) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/ir_MitsubishiHeavy.cpp ir_Fujitsu.o : $(USER_DIR)/ir_Fujitsu.h $(USER_DIR)/ir_Fujitsu.cpp $(COMMON_DEPS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Fujitsu.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/ir_Fujitsu.cpp ir_Sharp.o : $(USER_DIR)/ir_Sharp.cpp $(COMMON_DEPS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Sharp.cpp @@ -124,7 +129,7 @@ ir_RC5_RC6.o : $(USER_DIR)/ir_RC5_RC6.cpp $(COMMON_DEPS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_RC5_RC6.cpp ir_Panasonic.o : $(USER_DIR)/ir_Panasonic.cpp $(COMMON_DEPS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Panasonic.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/ir_Panasonic.cpp ir_Dish.o : $(USER_DIR)/ir_Dish.cpp $(COMMON_DEPS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Dish.cpp @@ -133,7 +138,7 @@ ir_Whynter.o : $(USER_DIR)/ir_Whynter.cpp $(COMMON_DEPS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Whynter.cpp ir_Coolix.o : $(USER_DIR)/ir_Coolix.cpp $(COMMON_DEPS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Coolix.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/ir_Coolix.cpp ir_Aiwa.o : $(USER_DIR)/ir_Aiwa.cpp $(COMMON_DEPS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Aiwa.cpp @@ -145,10 +150,10 @@ ir_Sanyo.o : $(USER_DIR)/ir_Sanyo.cpp $(COMMON_DEPS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Sanyo.cpp ir_Daikin.o : $(USER_DIR)/ir_Daikin.cpp $(USER_DIR)/ir_Daikin.h $(COMMON_DEPS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Daikin.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/ir_Daikin.cpp ir_Gree.o : $(USER_DIR)/ir_Gree.cpp $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Gree.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/ir_Gree.cpp ir_Pronto.o : $(USER_DIR)/ir_Pronto.cpp $(GTEST_HEADERS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Pronto.cpp @@ -157,10 +162,10 @@ ir_Nikai.o : $(USER_DIR)/ir_Nikai.cpp $(GTEST_HEADERS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Nikai.cpp ir_Toshiba.o : $(USER_DIR)/ir_Toshiba.h $(USER_DIR)/ir_Toshiba.cpp $(COMMON_DEPS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Toshiba.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/ir_Toshiba.cpp ir_Midea.o : $(USER_DIR)/ir_Midea.cpp $(COMMON_DEPS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Midea.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/ir_Midea.cpp ir_Magiquest.o : $(USER_DIR)/ir_Magiquest.cpp $(GTEST_HEADERS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Magiquest.cpp @@ -172,16 +177,16 @@ ir_Carrier.o : $(USER_DIR)/ir_Carrier.cpp $(GTEST_HEADERS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Carrier.cpp ir_Haier.o : $(USER_DIR)/ir_Haier.cpp $(USER_DIR)/ir_Haier.h $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Haier.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/ir_Haier.cpp ir_Hitachi.o : $(USER_DIR)/ir_Hitachi.cpp $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Hitachi.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/ir_Hitachi.cpp ir_GICable.o : $(USER_DIR)/ir_GICable.cpp $(GTEST_HEADERS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_GICable.cpp ir_Whirlpool.o : $(USER_DIR)/ir_Whirlpool.cpp $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Whirlpool.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/ir_Whirlpool.cpp ir_Lutron.o : $(USER_DIR)/ir_Lutron.cpp $(GTEST_HEADERS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Lutron.cpp @@ -194,3 +199,15 @@ ir_Pioneer.o : $(USER_DIR)/ir_Pioneer.cpp $(GTEST_HEADERS) ir_MWM.o : $(USER_DIR)/ir_MWM.cpp $(USER_DIR)/ir_RC5_RC6.cpp $(COMMON_DEPS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_MWM.cpp + +ir_Vestel.o : $(USER_DIR)/ir_Vestel.cpp $(GTEST_HEADERS) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/ir_Vestel.cpp + +ir_Teco.o : $(USER_DIR)/ir_Teco.cpp $(GTEST_HEADERS) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/ir_Teco.cpp + +ir_Tcl.o : $(USER_DIR)/ir_Tcl.cpp $(USER_DIR)/ir_Tcl.h $(GTEST_HEADERS) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/ir_Tcl.cpp + +ir_Lego.o : $(USER_DIR)/ir_Lego.cpp $(GTEST_HEADERS) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Lego.cpp diff --git a/lib/IRremoteESP8266-2.5.2.03/tools/RawToGlobalCache.sh b/lib/IRremoteESP8266-2.6.0/tools/RawToGlobalCache.sh similarity index 100% rename from lib/IRremoteESP8266-2.5.2.03/tools/RawToGlobalCache.sh rename to lib/IRremoteESP8266-2.6.0/tools/RawToGlobalCache.sh diff --git a/lib/IRremoteESP8266-2.5.2.03/tools/auto_analyse_raw_data.py b/lib/IRremoteESP8266-2.6.0/tools/auto_analyse_raw_data.py similarity index 98% rename from lib/IRremoteESP8266-2.5.2.03/tools/auto_analyse_raw_data.py rename to lib/IRremoteESP8266-2.6.0/tools/auto_analyse_raw_data.py index 5fd399807a60..b23cdb46fecb 100644 --- a/lib/IRremoteESP8266-2.5.2.03/tools/auto_analyse_raw_data.py +++ b/lib/IRremoteESP8266-2.6.0/tools/auto_analyse_raw_data.py @@ -85,9 +85,9 @@ def display_binary(self, binary_str): " %s (LSB first)\n" " Bin: 0b%s (MSB first)\n" " 0b%s (LSB first)\n" % - (bits, "0x{0:0{1}X}".format(num, bits / 4), - "0x{0:0{1}X}".format(rev_num, bits / 4), num, rev_num, - binary_str, rev_binary_str)) + (bits, ("0x{0:0%dX}" % (bits / 4)).format(num), + ("0x{0:0%dX}" % (bits / 4)).format(rev_num), num, + rev_num, binary_str, rev_binary_str)) def add_data_code(self, bin_str, footer=True): """Add the common "data" sequence of code to send the bulk of a message.""" diff --git a/lib/IRremoteESP8266-2.5.2.03/tools/auto_analyse_raw_data_test.py b/lib/IRremoteESP8266-2.6.0/tools/auto_analyse_raw_data_test.py similarity index 100% rename from lib/IRremoteESP8266-2.5.2.03/tools/auto_analyse_raw_data_test.py rename to lib/IRremoteESP8266-2.6.0/tools/auto_analyse_raw_data_test.py diff --git a/lib/IRremoteESP8266-2.5.2.03/tools/gc_decode.cpp b/lib/IRremoteESP8266-2.6.0/tools/gc_decode.cpp similarity index 100% rename from lib/IRremoteESP8266-2.5.2.03/tools/gc_decode.cpp rename to lib/IRremoteESP8266-2.6.0/tools/gc_decode.cpp diff --git a/lib/IRremoteESP8266-2.5.2.03/tools/mkkeywords b/lib/IRremoteESP8266-2.6.0/tools/mkkeywords similarity index 100% rename from lib/IRremoteESP8266-2.5.2.03/tools/mkkeywords rename to lib/IRremoteESP8266-2.6.0/tools/mkkeywords diff --git a/lib/IRremoteESP8266-2.5.2.03/tools/mode2_decode.cpp b/lib/IRremoteESP8266-2.6.0/tools/mode2_decode.cpp similarity index 100% rename from lib/IRremoteESP8266-2.5.2.03/tools/mode2_decode.cpp rename to lib/IRremoteESP8266-2.6.0/tools/mode2_decode.cpp diff --git a/lib/PubSubClient-EspEasy-2.6.09/tests/testcases/mqtt_basic.py b/lib/PubSubClient-EspEasy-2.6.09/tests/testcases/mqtt_basic.py deleted file mode 100644 index 1b0cc65bb432..000000000000 --- a/lib/PubSubClient-EspEasy-2.6.09/tests/testcases/mqtt_basic.py +++ /dev/null @@ -1,43 +0,0 @@ -import unittest -import settings - -import time -import mosquitto - -import serial - -def on_message(mosq, obj, msg): - obj.message_queue.append(msg) - -class mqtt_basic(unittest.TestCase): - - message_queue = [] - - @classmethod - def setUpClass(self): - self.client = mosquitto.Mosquitto("pubsubclient_ut", clean_session=True,obj=self) - self.client.connect(settings.server_ip) - self.client.on_message = on_message - self.client.subscribe("outTopic",0) - - @classmethod - def tearDownClass(self): - self.client.disconnect() - - def test_one(self): - i=30 - while len(self.message_queue) == 0 and i > 0: - self.client.loop() - time.sleep(0.5) - i -= 1 - self.assertTrue(i>0, "message receive timed-out") - self.assertEqual(len(self.message_queue), 1, "unexpected number of messages received") - msg = self.message_queue[0] - self.assertEqual(msg.mid,0,"message id not 0") - self.assertEqual(msg.topic,"outTopic","message topic incorrect") - self.assertEqual(msg.payload,"hello world") - self.assertEqual(msg.qos,0,"message qos not 0") - self.assertEqual(msg.retain,False,"message retain flag incorrect") - - - diff --git a/lib/PubSubClient-EspEasy-2.6.09/tests/testcases/mqtt_publish_in_callback.py b/lib/PubSubClient-EspEasy-2.6.09/tests/testcases/mqtt_publish_in_callback.py deleted file mode 100644 index 7989f7f17bed..000000000000 --- a/lib/PubSubClient-EspEasy-2.6.09/tests/testcases/mqtt_publish_in_callback.py +++ /dev/null @@ -1,64 +0,0 @@ -import unittest -import settings - -import time -import mosquitto - -import serial - -def on_message(mosq, obj, msg): - obj.message_queue.append(msg) - -class mqtt_publish_in_callback(unittest.TestCase): - - message_queue = [] - - @classmethod - def setUpClass(self): - self.client = mosquitto.Mosquitto("pubsubclient_ut", clean_session=True,obj=self) - self.client.connect(settings.server_ip) - self.client.on_message = on_message - self.client.subscribe("outTopic",0) - - @classmethod - def tearDownClass(self): - self.client.disconnect() - - def test_connect(self): - i=30 - while len(self.message_queue) == 0 and i > 0: - self.client.loop() - time.sleep(0.5) - i -= 1 - self.assertTrue(i>0, "message receive timed-out") - self.assertEqual(len(self.message_queue), 1, "unexpected number of messages received") - msg = self.message_queue.pop(0) - self.assertEqual(msg.mid,0,"message id not 0") - self.assertEqual(msg.topic,"outTopic","message topic incorrect") - self.assertEqual(msg.payload,"hello world") - self.assertEqual(msg.qos,0,"message qos not 0") - self.assertEqual(msg.retain,False,"message retain flag incorrect") - - - def test_publish(self): - self.assertEqual(len(self.message_queue), 0, "message queue not empty") - payload = "abcdefghij" - self.client.publish("inTopic",payload) - - i=30 - while len(self.message_queue) == 0 and i > 0: - self.client.loop() - time.sleep(0.5) - i -= 1 - - self.assertTrue(i>0, "message receive timed-out") - self.assertEqual(len(self.message_queue), 1, "unexpected number of messages received") - msg = self.message_queue.pop(0) - self.assertEqual(msg.mid,0,"message id not 0") - self.assertEqual(msg.topic,"outTopic","message topic incorrect") - self.assertEqual(msg.payload,payload) - self.assertEqual(msg.qos,0,"message qos not 0") - self.assertEqual(msg.retain,False,"message retain flag incorrect") - - - diff --git a/lib/PubSubClient-EspEasy-2.6.09/tests/testsuite.py b/lib/PubSubClient-EspEasy-2.6.09/tests/testsuite.py deleted file mode 100644 index 0a8e70dfd986..000000000000 --- a/lib/PubSubClient-EspEasy-2.6.09/tests/testsuite.py +++ /dev/null @@ -1,179 +0,0 @@ -#!/usr/bin/env python -import os -import os.path -import sys -import shutil -from subprocess import call -import importlib -import unittest -import re - -from testcases import settings - -class Workspace(object): - - def __init__(self): - self.root_dir = os.getcwd() - self.build_dir = os.path.join(self.root_dir,"tmpbin"); - self.log_dir = os.path.join(self.root_dir,"logs"); - self.tests_dir = os.path.join(self.root_dir,"testcases"); - self.examples_dir = os.path.join(self.root_dir,"../PubSubClient/examples") - self.examples = [] - self.tests = [] - if not os.path.isdir("../PubSubClient"): - raise Exception("Cannot find PubSubClient library") - try: - import ino - except: - raise Exception("ino tool not installed") - - def init(self): - if os.path.isdir(self.build_dir): - shutil.rmtree(self.build_dir) - os.mkdir(self.build_dir) - if os.path.isdir(self.log_dir): - shutil.rmtree(self.log_dir) - os.mkdir(self.log_dir) - - os.chdir(self.build_dir) - call(["ino","init"]) - - shutil.copytree("../../PubSubClient","lib/PubSubClient") - - filenames = [] - for root, dirs, files in os.walk(self.examples_dir): - filenames += [os.path.join(root,f) for f in files if f.endswith(".ino")] - filenames.sort() - for e in filenames: - self.examples.append(Sketch(self,e)) - - filenames = [] - for root, dirs, files in os.walk(self.tests_dir): - filenames += [os.path.join(root,f) for f in files if f.endswith(".ino")] - filenames.sort() - for e in filenames: - self.tests.append(Sketch(self,e)) - - def clean(self): - shutil.rmtree(self.build_dir) - -class Sketch(object): - def __init__(self,wksp,fn): - self.w = wksp - self.filename = fn - self.basename = os.path.basename(self.filename) - self.build_log = os.path.join(self.w.log_dir,"%s.log"%(os.path.basename(self.filename),)) - self.build_err_log = os.path.join(self.w.log_dir,"%s.err.log"%(os.path.basename(self.filename),)) - self.build_upload_log = os.path.join(self.w.log_dir,"%s.upload.log"%(os.path.basename(self.filename),)) - - def build(self): - sys.stdout.write(" Build: ") - sys.stdout.flush() - - # Copy sketch over, replacing IP addresses as necessary - fin = open(self.filename,"r") - lines = fin.readlines() - fin.close() - fout = open(os.path.join(self.w.build_dir,"src","sketch.ino"),"w") - for l in lines: - if re.match(r"^byte server\[\] = {",l): - fout.write("byte server[] = { %s };\n"%(settings.server_ip.replace(".",", "),)) - elif re.match(r"^byte ip\[\] = {",l): - fout.write("byte ip[] = { %s };\n"%(settings.arduino_ip.replace(".",", "),)) - else: - fout.write(l) - fout.flush() - fout.close() - - # Run build - fout = open(self.build_log, "w") - ferr = open(self.build_err_log, "w") - rc = call(["ino","build"],stdout=fout,stderr=ferr) - fout.close() - ferr.close() - if rc == 0: - sys.stdout.write("pass") - sys.stdout.write("\n") - return True - else: - sys.stdout.write("fail") - sys.stdout.write("\n") - with open(self.build_err_log) as f: - for line in f: - print " ",line, - return False - - def upload(self): - sys.stdout.write(" Upload: ") - sys.stdout.flush() - fout = open(self.build_upload_log, "w") - rc = call(["ino","upload"],stdout=fout,stderr=fout) - fout.close() - if rc == 0: - sys.stdout.write("pass") - sys.stdout.write("\n") - return True - else: - sys.stdout.write("fail") - sys.stdout.write("\n") - with open(self.build_upload_log) as f: - for line in f: - print " ",line, - return False - - - def test(self): - # import the matching test case, if it exists - try: - basename = os.path.basename(self.filename)[:-4] - i = importlib.import_module("testcases."+basename) - except: - sys.stdout.write(" Test: no tests found") - sys.stdout.write("\n") - return - c = getattr(i,basename) - - testmethods = [m for m in dir(c) if m.startswith("test_")] - testmethods.sort() - tests = [] - for m in testmethods: - tests.append(c(m)) - - result = unittest.TestResult() - c.setUpClass() - if self.upload(): - sys.stdout.write(" Test: ") - sys.stdout.flush() - for t in tests: - t.run(result) - print "%d/%d"%(result.testsRun-len(result.failures)-len(result.errors),result.testsRun) - if not result.wasSuccessful(): - if len(result.failures) > 0: - for f in result.failures: - print "-- %s"%(str(f[0]),) - print f[1] - if len(result.errors) > 0: - print " Errors:" - for f in result.errors: - print "-- %s"%(str(f[0]),) - print f[1] - c.tearDownClass() - -if __name__ == '__main__': - run_tests = True - - w = Workspace() - w.init() - - for e in w.examples: - print "--------------------------------------" - print "[%s]"%(e.basename,) - if e.build() and run_tests: - e.test() - for e in w.tests: - print "--------------------------------------" - print "[%s]"%(e.basename,) - if e.build() and run_tests: - e.test() - - w.clean() diff --git a/lib/PubSubClient-EspEasy-2.6.09/.gitignore b/lib/PubSubClient-EspEasy-2.7.11/.gitignore similarity index 100% rename from lib/PubSubClient-EspEasy-2.6.09/.gitignore rename to lib/PubSubClient-EspEasy-2.7.11/.gitignore diff --git a/lib/PubSubClient-EspEasy-2.6.09/.travis.yml b/lib/PubSubClient-EspEasy-2.7.11/.travis.yml similarity index 100% rename from lib/PubSubClient-EspEasy-2.6.09/.travis.yml rename to lib/PubSubClient-EspEasy-2.7.11/.travis.yml diff --git a/lib/PubSubClient-EspEasy-2.6.09/CHANGES.txt b/lib/PubSubClient-EspEasy-2.7.11/CHANGES.txt similarity index 86% rename from lib/PubSubClient-EspEasy-2.6.09/CHANGES.txt rename to lib/PubSubClient-EspEasy-2.7.11/CHANGES.txt index 8c8bef64ec33..ff4da62ab1ae 100644 --- a/lib/PubSubClient-EspEasy-2.6.09/CHANGES.txt +++ b/lib/PubSubClient-EspEasy-2.7.11/CHANGES.txt @@ -1,8 +1,16 @@ +2.7 + * Fix remaining-length handling to prevent buffer overrun + * Add large-payload API - beginPublish/write/publish/endPublish + * Add yield call to improve reliability on ESP + * Add Clean Session flag to connect options + * Add ESP32 support for functional callback signature + * Various other fixes + 2.4 * Add MQTT_SOCKET_TIMEOUT to prevent it blocking indefinitely whilst waiting for inbound data * Fixed return code when publishing >256 bytes - + 2.3 * Add publish(topic,payload,retained) function diff --git a/lib/PubSubClient-EspEasy-2.6.09/LICENSE.txt b/lib/PubSubClient-EspEasy-2.7.11/LICENSE.txt similarity index 100% rename from lib/PubSubClient-EspEasy-2.6.09/LICENSE.txt rename to lib/PubSubClient-EspEasy-2.7.11/LICENSE.txt diff --git a/lib/PubSubClient-EspEasy-2.6.09/README.md b/lib/PubSubClient-EspEasy-2.7.11/README.md similarity index 95% rename from lib/PubSubClient-EspEasy-2.6.09/README.md rename to lib/PubSubClient-EspEasy-2.7.11/README.md index 83176919cd23..69cbb8f0ce50 100644 --- a/lib/PubSubClient-EspEasy-2.6.09/README.md +++ b/lib/PubSubClient-EspEasy-2.7.11/README.md @@ -8,7 +8,7 @@ a server that supports MQTT. The library comes with a number of example sketches. See File > Examples > PubSubClient within the Arduino application. -Full API documentation is available here: http://pubsubclient.knolleary.net +Full API documentation is available here: https://pubsubclient.knolleary.net ## Limitations @@ -37,6 +37,7 @@ boards and shields, including: - TI CC3000 WiFi - [library](https://github.com/sparkfun/SFE_CC3000_Library) - Intel Galileo/Edison - ESP8266 + - ESP32 The library cannot currently be used with hardware based on the ENC28J60 chip – such as the Nanode or the Nuelectronics Ethernet Shield. For those, there is an diff --git a/lib/PubSubClient-EspEasy-2.6.09/examples/mqtt_auth/mqtt_auth.ino b/lib/PubSubClient-EspEasy-2.7.11/examples/mqtt_auth/mqtt_auth.ino similarity index 100% rename from lib/PubSubClient-EspEasy-2.6.09/examples/mqtt_auth/mqtt_auth.ino rename to lib/PubSubClient-EspEasy-2.7.11/examples/mqtt_auth/mqtt_auth.ino diff --git a/lib/PubSubClient-EspEasy-2.6.09/examples/mqtt_basic/mqtt_basic.ino b/lib/PubSubClient-EspEasy-2.7.11/examples/mqtt_basic/mqtt_basic.ino similarity index 100% rename from lib/PubSubClient-EspEasy-2.6.09/examples/mqtt_basic/mqtt_basic.ino rename to lib/PubSubClient-EspEasy-2.7.11/examples/mqtt_basic/mqtt_basic.ino diff --git a/lib/PubSubClient-EspEasy-2.6.09/examples/mqtt_esp8266/mqtt_esp8266.ino b/lib/PubSubClient-EspEasy-2.7.11/examples/mqtt_esp8266/mqtt_esp8266.ino similarity index 92% rename from lib/PubSubClient-EspEasy-2.6.09/examples/mqtt_esp8266/mqtt_esp8266.ino rename to lib/PubSubClient-EspEasy-2.7.11/examples/mqtt_esp8266/mqtt_esp8266.ino index e46f85f3e412..e7357b507dbf 100644 --- a/lib/PubSubClient-EspEasy-2.6.09/examples/mqtt_esp8266/mqtt_esp8266.ino +++ b/lib/PubSubClient-EspEasy-2.7.11/examples/mqtt_esp8266/mqtt_esp8266.ino @@ -38,14 +38,6 @@ long lastMsg = 0; char msg[50]; int value = 0; -void setup() { - pinMode(BUILTIN_LED, OUTPUT); // Initialize the BUILTIN_LED pin as an output - Serial.begin(115200); - setup_wifi(); - client.setServer(mqtt_server, 1883); - client.setCallback(callback); -} - void setup_wifi() { delay(10); @@ -61,6 +53,8 @@ void setup_wifi() { Serial.print("."); } + randomSeed(micros()); + Serial.println(""); Serial.println("WiFi connected"); Serial.println("IP address: "); @@ -80,7 +74,7 @@ void callback(char* topic, byte* payload, unsigned int length) { if ((char)payload[0] == '1') { digitalWrite(BUILTIN_LED, LOW); // Turn the LED on (Note that LOW is the voltage level // but actually the LED is on; this is because - // it is acive low on the ESP-01) + // it is active low on the ESP-01) } else { digitalWrite(BUILTIN_LED, HIGH); // Turn the LED off by making the voltage HIGH } @@ -91,8 +85,11 @@ void reconnect() { // Loop until we're reconnected while (!client.connected()) { Serial.print("Attempting MQTT connection..."); + // Create a random client ID + String clientId = "ESP8266Client-"; + clientId += String(random(0xffff), HEX); // Attempt to connect - if (client.connect("ESP8266Client")) { + if (client.connect(clientId.c_str())) { Serial.println("connected"); // Once connected, publish an announcement... client.publish("outTopic", "hello world"); @@ -107,6 +104,15 @@ void reconnect() { } } } + +void setup() { + pinMode(BUILTIN_LED, OUTPUT); // Initialize the BUILTIN_LED pin as an output + Serial.begin(115200); + setup_wifi(); + client.setServer(mqtt_server, 1883); + client.setCallback(callback); +} + void loop() { if (!client.connected()) { @@ -118,7 +124,7 @@ void loop() { if (now - lastMsg > 2000) { lastMsg = now; ++value; - snprintf (msg, 75, "hello world #%ld", value); + snprintf (msg, 50, "hello world #%ld", value); Serial.print("Publish message: "); Serial.println(msg); client.publish("outTopic", msg); diff --git a/lib/PubSubClient-EspEasy-2.7.11/examples/mqtt_large_message/mqtt_large_message.ino b/lib/PubSubClient-EspEasy-2.7.11/examples/mqtt_large_message/mqtt_large_message.ino new file mode 100644 index 000000000000..e048c3ed3630 --- /dev/null +++ b/lib/PubSubClient-EspEasy-2.7.11/examples/mqtt_large_message/mqtt_large_message.ino @@ -0,0 +1,179 @@ +/* + Long message ESP8266 MQTT example + + This sketch demonstrates sending arbitrarily large messages in combination + with the ESP8266 board/library. + + It connects to an MQTT server then: + - publishes "hello world" to the topic "outTopic" + - subscribes to the topic "greenBottles/#", printing out any messages + it receives. NB - it assumes the received payloads are strings not binary + - If the sub-topic is a number, it publishes a "greenBottles/lyrics" message + with a payload consisting of the lyrics to "10 green bottles", replacing + 10 with the number given in the sub-topic. + + It will reconnect to the server if the connection is lost using a blocking + reconnect function. See the 'mqtt_reconnect_nonblocking' example for how to + achieve the same result without blocking the main loop. + + To install the ESP8266 board, (using Arduino 1.6.4+): + - Add the following 3rd party board manager under "File -> Preferences -> Additional Boards Manager URLs": + http://arduino.esp8266.com/stable/package_esp8266com_index.json + - Open the "Tools -> Board -> Board Manager" and click install for the ESP8266" + - Select your ESP8266 in "Tools -> Board" + +*/ + +#include +#include + +// Update these with values suitable for your network. + +const char* ssid = "........"; +const char* password = "........"; +const char* mqtt_server = "broker.mqtt-dashboard.com"; + +WiFiClient espClient; +PubSubClient client(espClient); +long lastMsg = 0; +char msg[50]; +int value = 0; + +void setup_wifi() { + + delay(10); + // We start by connecting to a WiFi network + Serial.println(); + Serial.print("Connecting to "); + Serial.println(ssid); + + WiFi.begin(ssid, password); + + while (WiFi.status() != WL_CONNECTED) { + delay(500); + Serial.print("."); + } + + randomSeed(micros()); + + Serial.println(""); + Serial.println("WiFi connected"); + Serial.println("IP address: "); + Serial.println(WiFi.localIP()); +} + +void callback(char* topic, byte* payload, unsigned int length) { + Serial.print("Message arrived ["); + Serial.print(topic); + Serial.print("] "); + for (int i = 0; i < length; i++) { + Serial.print((char)payload[i]); + } + Serial.println(); + + // Find out how many bottles we should generate lyrics for + String topicStr(topic); + int bottleCount = 0; // assume no bottles unless we correctly parse a value from the topic + if (topicStr.indexOf('/') >= 0) { + // The topic includes a '/', we'll try to read the number of bottles from just after that + topicStr.remove(0, topicStr.indexOf('/')+1); + // Now see if there's a number of bottles after the '/' + bottleCount = topicStr.toInt(); + } + + if (bottleCount > 0) { + // Work out how big our resulting message will be + int msgLen = 0; + for (int i = bottleCount; i > 0; i--) { + String numBottles(i); + msgLen += 2*numBottles.length(); + if (i == 1) { + msgLen += 2*String(" green bottle, standing on the wall\n").length(); + } else { + msgLen += 2*String(" green bottles, standing on the wall\n").length(); + } + msgLen += String("And if one green bottle should accidentally fall\nThere'll be ").length(); + switch (i) { + case 1: + msgLen += String("no green bottles, standing on the wall\n\n").length(); + break; + case 2: + msgLen += String("1 green bottle, standing on the wall\n\n").length(); + break; + default: + numBottles = i-1; + msgLen += numBottles.length(); + msgLen += String(" green bottles, standing on the wall\n\n").length(); + break; + }; + } + + // Now we can start to publish the message + client.beginPublish("greenBottles/lyrics", msgLen, false); + for (int i = bottleCount; i > 0; i--) { + for (int j = 0; j < 2; j++) { + client.print(i); + if (i == 1) { + client.print(" green bottle, standing on the wall\n"); + } else { + client.print(" green bottles, standing on the wall\n"); + } + } + client.print("And if one green bottle should accidentally fall\nThere'll be "); + switch (i) { + case 1: + client.print("no green bottles, standing on the wall\n\n"); + break; + case 2: + client.print("1 green bottle, standing on the wall\n\n"); + break; + default: + client.print(i-1); + client.print(" green bottles, standing on the wall\n\n"); + break; + }; + } + // Now we're done! + client.endPublish(); + } +} + +void reconnect() { + // Loop until we're reconnected + while (!client.connected()) { + Serial.print("Attempting MQTT connection..."); + // Create a random client ID + String clientId = "ESP8266Client-"; + clientId += String(random(0xffff), HEX); + // Attempt to connect + if (client.connect(clientId.c_str())) { + Serial.println("connected"); + // Once connected, publish an announcement... + client.publish("outTopic", "hello world"); + // ... and resubscribe + client.subscribe("greenBottles/#"); + } else { + Serial.print("failed, rc="); + Serial.print(client.state()); + Serial.println(" try again in 5 seconds"); + // Wait 5 seconds before retrying + delay(5000); + } + } +} + +void setup() { + pinMode(BUILTIN_LED, OUTPUT); // Initialize the BUILTIN_LED pin as an output + Serial.begin(115200); + setup_wifi(); + client.setServer(mqtt_server, 1883); + client.setCallback(callback); +} + +void loop() { + + if (!client.connected()) { + reconnect(); + } + client.loop(); +} diff --git a/lib/PubSubClient-EspEasy-2.6.09/examples/mqtt_publish_in_callback/mqtt_publish_in_callback.ino b/lib/PubSubClient-EspEasy-2.7.11/examples/mqtt_publish_in_callback/mqtt_publish_in_callback.ino similarity index 100% rename from lib/PubSubClient-EspEasy-2.6.09/examples/mqtt_publish_in_callback/mqtt_publish_in_callback.ino rename to lib/PubSubClient-EspEasy-2.7.11/examples/mqtt_publish_in_callback/mqtt_publish_in_callback.ino diff --git a/lib/PubSubClient-EspEasy-2.6.09/examples/mqtt_reconnect_nonblocking/mqtt_reconnect_nonblocking.ino b/lib/PubSubClient-EspEasy-2.7.11/examples/mqtt_reconnect_nonblocking/mqtt_reconnect_nonblocking.ino similarity index 100% rename from lib/PubSubClient-EspEasy-2.6.09/examples/mqtt_reconnect_nonblocking/mqtt_reconnect_nonblocking.ino rename to lib/PubSubClient-EspEasy-2.7.11/examples/mqtt_reconnect_nonblocking/mqtt_reconnect_nonblocking.ino diff --git a/lib/PubSubClient-EspEasy-2.6.09/examples/mqtt_stream/mqtt_stream.ino b/lib/PubSubClient-EspEasy-2.7.11/examples/mqtt_stream/mqtt_stream.ino similarity index 100% rename from lib/PubSubClient-EspEasy-2.6.09/examples/mqtt_stream/mqtt_stream.ino rename to lib/PubSubClient-EspEasy-2.7.11/examples/mqtt_stream/mqtt_stream.ino diff --git a/lib/PubSubClient-EspEasy-2.6.09/keywords.txt b/lib/PubSubClient-EspEasy-2.7.11/keywords.txt similarity index 91% rename from lib/PubSubClient-EspEasy-2.6.09/keywords.txt rename to lib/PubSubClient-EspEasy-2.7.11/keywords.txt index b979588fe4cc..1ee23d0fadda 100644 --- a/lib/PubSubClient-EspEasy-2.6.09/keywords.txt +++ b/lib/PubSubClient-EspEasy-2.7.11/keywords.txt @@ -16,6 +16,9 @@ connect KEYWORD2 disconnect KEYWORD2 publish KEYWORD2 publish_P KEYWORD2 +beginPublish KEYWORD2 +endPublish KEYWORD2 +write KEYWORD2 subscribe KEYWORD2 unsubscribe KEYWORD2 loop KEYWORD2 diff --git a/lib/PubSubClient-EspEasy-2.6.09/library.json b/lib/PubSubClient-EspEasy-2.7.11/library.json similarity index 97% rename from lib/PubSubClient-EspEasy-2.6.09/library.json rename to lib/PubSubClient-EspEasy-2.7.11/library.json index b96739078a98..8a36a1c5e214 100644 --- a/lib/PubSubClient-EspEasy-2.6.09/library.json +++ b/lib/PubSubClient-EspEasy-2.7.11/library.json @@ -6,7 +6,7 @@ "type": "git", "url": "https://github.com/knolleary/pubsubclient.git" }, - "version": "2.6", + "version": "2.7", "exclude": "tests", "examples": "examples/*/*.ino", "frameworks": "arduino", diff --git a/lib/PubSubClient-EspEasy-2.6.09/library.properties b/lib/PubSubClient-EspEasy-2.7.11/library.properties similarity index 98% rename from lib/PubSubClient-EspEasy-2.6.09/library.properties rename to lib/PubSubClient-EspEasy-2.7.11/library.properties index 3ceeda81c093..1ae97882ebcb 100644 --- a/lib/PubSubClient-EspEasy-2.6.09/library.properties +++ b/lib/PubSubClient-EspEasy-2.7.11/library.properties @@ -1,5 +1,5 @@ name=PubSubClient -version=2.6 +version=2.7 author=Nick O'Leary maintainer=Nick O'Leary sentence=A client library for MQTT messaging. diff --git a/lib/PubSubClient-EspEasy-2.6.09/src/PubSubClient.cpp b/lib/PubSubClient-EspEasy-2.7.11/src/PubSubClient.cpp similarity index 79% rename from lib/PubSubClient-EspEasy-2.6.09/src/PubSubClient.cpp rename to lib/PubSubClient-EspEasy-2.7.11/src/PubSubClient.cpp index 79eb2d52ee88..9fe15006aad9 100644 --- a/lib/PubSubClient-EspEasy-2.6.09/src/PubSubClient.cpp +++ b/lib/PubSubClient-EspEasy-2.7.11/src/PubSubClient.cpp @@ -102,30 +102,41 @@ PubSubClient::PubSubClient(const char* domain, uint16_t port, MQTT_CALLBACK_SIGN } boolean PubSubClient::connect(const char *id) { - return connect(id,NULL,NULL,0,0,0,0); + return connect(id,NULL,NULL,0,0,0,0,1); } boolean PubSubClient::connect(const char *id, const char *user, const char *pass) { - return connect(id,user,pass,0,0,0,0); + return connect(id,user,pass,0,0,0,0,1); } boolean PubSubClient::connect(const char *id, const char* willTopic, uint8_t willQos, boolean willRetain, const char* willMessage) { - return connect(id,NULL,NULL,willTopic,willQos,willRetain,willMessage); + return connect(id,NULL,NULL,willTopic,willQos,willRetain,willMessage,1); } boolean PubSubClient::connect(const char *id, const char *user, const char *pass, const char* willTopic, uint8_t willQos, boolean willRetain, const char* willMessage) { + return connect(id,user,pass,willTopic,willQos,willRetain,willMessage,1); +} + +boolean PubSubClient::connect(const char *id, const char *user, const char *pass, const char* willTopic, uint8_t willQos, boolean willRetain, const char* willMessage, boolean cleanSession) { if (!connected()) { int result = 0; - if (domain.length() != 0) { - result = _client->connect(this->domain.c_str(), this->port); + if (_client == nullptr) { + return false; + } + if (_client->connected()) { + result = 1; } else { - result = _client->connect(this->ip, this->port); + if (domain != NULL) { + result = _client->connect(this->domain.c_str(), this->port); + } else { + result = _client->connect(this->ip, this->port); + } } if (result == 1) { nextMsgId = 1; // Leave room in the buffer for header and variable length field - uint16_t length = 5; + uint16_t length = MQTT_MAX_HEADER_SIZE; unsigned int j; #if MQTT_VERSION == MQTT_VERSION_3_1 @@ -141,9 +152,12 @@ boolean PubSubClient::connect(const char *id, const char *user, const char *pass uint8_t v; if (willTopic) { - v = 0x06|(willQos<<3)|(willRetain<<5); + v = 0x04|(willQos<<3)|(willRetain<<5); } else { - v = 0x02; + v = 0x00; + } + if (cleanSession) { + v = v|0x02; } if(user != NULL) { @@ -158,24 +172,31 @@ boolean PubSubClient::connect(const char *id, const char *user, const char *pass buffer[length++] = ((MQTT_KEEPALIVE) >> 8); buffer[length++] = ((MQTT_KEEPALIVE) & 0xFF); + + CHECK_STRING_LENGTH(length,id) length = writeString(id,buffer,length); if (willTopic) { + CHECK_STRING_LENGTH(length,willTopic) length = writeString(willTopic,buffer,length); + CHECK_STRING_LENGTH(length,willMessage) length = writeString(willMessage,buffer,length); } if(user != NULL) { + CHECK_STRING_LENGTH(length,user) length = writeString(user,buffer,length); if(pass != NULL) { + CHECK_STRING_LENGTH(length,pass) length = writeString(pass,buffer,length); } } - write(MQTTCONNECT,buffer,length-5); + write(MQTTCONNECT,buffer,length-MQTT_MAX_HEADER_SIZE); lastInActivity = lastOutActivity = millis(); while (!_client->available()) { + delay(0); // Prevent watchdog crashes unsigned long t = millis(); if (t-lastInActivity >= ((int32_t) MQTT_SOCKET_TIMEOUT*1000UL)) { _state = MQTT_CONNECTION_TIMEOUT; @@ -207,9 +228,12 @@ boolean PubSubClient::connect(const char *id, const char *user, const char *pass // reads a byte into result boolean PubSubClient::readByte(uint8_t * result) { + if (_client == nullptr) { + return false; + } uint32_t previousMillis = millis(); while(!_client->available()) { - delay(1); // Add esp8266 de-blocking (Tasmota #790, EspEasy #1943) + delay(1); // Prevent watchdog crashes uint32_t currentMillis = millis(); if(currentMillis - previousMillis >= ((int32_t) MQTT_SOCKET_TIMEOUT * 1000)){ return false; @@ -241,7 +265,7 @@ uint16_t PubSubClient::readPacket(uint8_t* lengthLength) { uint8_t start = 0; do { - if (len == 6) { + if (len == 5) { // Invalid remaining length encoding - kill the connection _state = MQTT_DISCONNECTED; _client->stop(); @@ -353,11 +377,13 @@ boolean PubSubClient::loop() { } boolean PubSubClient::publish(const char* topic, const char* payload) { - return publish(topic,(const uint8_t*)payload,strlen(payload),false); + size_t plength = (payload != nullptr) ? strlen(payload) : 0; + return publish(topic,(const uint8_t*)payload,plength,false); } boolean PubSubClient::publish(const char* topic, const char* payload, boolean retained) { - return publish(topic,(const uint8_t*)payload,strlen(payload),retained); + size_t plength = (payload != nullptr) ? strlen(payload) : 0; + return publish(topic,(const uint8_t*)payload,plength,retained); } boolean PubSubClient::publish(const char* topic, const uint8_t* payload, unsigned int plength) { @@ -366,12 +392,12 @@ boolean PubSubClient::publish(const char* topic, const uint8_t* payload, unsigne boolean PubSubClient::publish(const char* topic, const uint8_t* payload, unsigned int plength, boolean retained) { if (connected()) { - if (MQTT_MAX_PACKET_SIZE < 5 + 2+strlen(topic) + plength) { + if (MQTT_MAX_PACKET_SIZE < MQTT_MAX_HEADER_SIZE + 2+strlen(topic) + plength) { // Too long return false; } // Leave room in the buffer for header and variable length field - uint16_t length = 5; + uint16_t length = MQTT_MAX_HEADER_SIZE; length = writeString(topic,buffer,length); uint16_t i; for (i=0;iwrite(buffer+(MQTT_MAX_HEADER_SIZE-hlen),length-(MQTT_MAX_HEADER_SIZE-hlen)); + lastOutActivity = millis(); + return (rc == (length-(MQTT_MAX_HEADER_SIZE-hlen))); + } + return false; +} + +int PubSubClient::endPublish() { + return 1; +} + +size_t PubSubClient::write(uint8_t data) { + lastOutActivity = millis(); + if (_client == nullptr) { + return 0; + } + return _client->write(data); +} + +size_t PubSubClient::write(const uint8_t *buffer, size_t size) { + lastOutActivity = millis(); + if (_client == nullptr) { + return 0; + } + return _client->write(buffer,size); +} + +size_t PubSubClient::buildHeader(uint8_t header, uint8_t* buf, uint16_t length) { uint8_t lenBuf[4]; uint8_t llen = 0; uint8_t digit; uint8_t pos = 0; - uint16_t rc; uint16_t len = length; do { digit = len % 128; @@ -450,15 +519,22 @@ boolean PubSubClient::write(uint8_t header, uint8_t* buf, uint16_t length) { buf[4-llen] = header; for (int i=0;i 0) && result) { + delay(0); // Prevent watchdog crashes bytesToWrite = (bytesRemaining > MQTT_MAX_TRANSFER_SIZE)?MQTT_MAX_TRANSFER_SIZE:bytesRemaining; rc = _client->write(writeBuf,bytesToWrite); result = (rc == bytesToWrite); @@ -467,9 +543,9 @@ boolean PubSubClient::write(uint8_t header, uint8_t* buf, uint16_t length) { } return result; #else - rc = _client->write(buf+(4-llen),length+1+llen); + rc = _client->write(buf+(MQTT_MAX_HEADER_SIZE-hlen),length+hlen); lastOutActivity = millis(); - return (rc == 1+llen+length); + return (rc == hlen+length); #endif } @@ -487,7 +563,7 @@ boolean PubSubClient::subscribe(const char* topic, uint8_t qos) { } if (connected()) { // Leave room in the buffer for header and variable length field - uint16_t length = 5; + uint16_t length = MQTT_MAX_HEADER_SIZE; nextMsgId++; if (nextMsgId == 0) { nextMsgId = 1; @@ -496,7 +572,7 @@ boolean PubSubClient::subscribe(const char* topic, uint8_t qos) { buffer[length++] = (nextMsgId & 0xFF); length = writeString((char*)topic, buffer,length); buffer[length++] = qos; - return write(MQTTSUBSCRIBE|MQTTQOS1,buffer,length-5); + return write(MQTTSUBSCRIBE|MQTTQOS1,buffer,length-MQTT_MAX_HEADER_SIZE); } return false; } @@ -507,7 +583,7 @@ boolean PubSubClient::unsubscribe(const char* topic) { return false; } if (connected()) { - uint16_t length = 5; + uint16_t length = MQTT_MAX_HEADER_SIZE; nextMsgId++; if (nextMsgId == 0) { nextMsgId = 1; @@ -515,7 +591,7 @@ boolean PubSubClient::unsubscribe(const char* topic) { buffer[length++] = (nextMsgId >> 8); buffer[length++] = (nextMsgId & 0xFF); length = writeString(topic, buffer,length); - return write(MQTTUNSUBSCRIBE|MQTTQOS1,buffer,length-5); + return write(MQTTUNSUBSCRIBE|MQTTQOS1,buffer,length-MQTT_MAX_HEADER_SIZE); } return false; } @@ -523,7 +599,7 @@ boolean PubSubClient::unsubscribe(const char* topic) { void PubSubClient::disconnect() { buffer[0] = MQTTDISCONNECT; buffer[1] = 0; - if (_client != NULL) { + if (_client != nullptr) { _client->write(buffer,2); _client->flush(); _client->stop(); @@ -558,6 +634,8 @@ boolean PubSubClient::connected() { _client->flush(); _client->stop(); } + } else { + return this->_state == MQTT_CONNECTED; } } return rc; diff --git a/lib/PubSubClient-EspEasy-2.6.09/src/PubSubClient.h b/lib/PubSubClient-EspEasy-2.7.11/src/PubSubClient.h similarity index 76% rename from lib/PubSubClient-EspEasy-2.6.09/src/PubSubClient.h rename to lib/PubSubClient-EspEasy-2.7.11/src/PubSubClient.h index 003df770ed2d..a519f75d7df8 100644 --- a/lib/PubSubClient-EspEasy-2.6.09/src/PubSubClient.h +++ b/lib/PubSubClient-EspEasy-2.7.11/src/PubSubClient.h @@ -75,6 +75,9 @@ #define MQTTQOS1 (1 << 1) #define MQTTQOS2 (2 << 1) +// Maximum size of fixed header and variable length size header +#define MQTT_MAX_HEADER_SIZE 5 + #if defined(ESP8266) || defined(ESP32) #include #define MQTT_CALLBACK_SIGNATURE std::function callback @@ -82,7 +85,9 @@ #define MQTT_CALLBACK_SIGNATURE void (*callback)(char*, uint8_t*, unsigned int) #endif -class PubSubClient { +#define CHECK_STRING_LENGTH(l,s) if (l+2+strlen(s) > MQTT_MAX_PACKET_SIZE) {_client->stop();return false;} + +class PubSubClient : public Print { private: Client* _client; uint8_t buffer[MQTT_MAX_PACKET_SIZE]; @@ -96,6 +101,11 @@ class PubSubClient { boolean readByte(uint8_t * result, uint16_t * index); boolean write(uint8_t header, uint8_t* buf, uint16_t length); uint16_t writeString(const char* string, uint8_t* buf, uint16_t pos); + // Build up the header ready to send + // Returns the size of the header + // Note: the header is built at the end of the first MQTT_MAX_HEADER_SIZE bytes, so will start + // (MQTT_MAX_HEADER_SIZE - ) bytes into the buffer + size_t buildHeader(uint8_t header, uint8_t* buf, uint16_t length); IPAddress ip; String domain; uint16_t port; @@ -129,12 +139,31 @@ class PubSubClient { boolean connect(const char* id, const char* user, const char* pass); boolean connect(const char* id, const char* willTopic, uint8_t willQos, boolean willRetain, const char* willMessage); boolean connect(const char* id, const char* user, const char* pass, const char* willTopic, uint8_t willQos, boolean willRetain, const char* willMessage); + boolean connect(const char* id, const char* user, const char* pass, const char* willTopic, uint8_t willQos, boolean willRetain, const char* willMessage, boolean cleanSession); void disconnect(); boolean publish(const char* topic, const char* payload); boolean publish(const char* topic, const char* payload, boolean retained); boolean publish(const char* topic, const uint8_t * payload, unsigned int plength); boolean publish(const char* topic, const uint8_t * payload, unsigned int plength, boolean retained); + boolean publish_P(const char* topic, const char* payload, boolean retained); boolean publish_P(const char* topic, const uint8_t * payload, unsigned int plength, boolean retained); + // Start to publish a message. + // This API: + // beginPublish(...) + // one or more calls to write(...) + // endPublish() + // Allows for arbitrarily large payloads to be sent without them having to be copied into + // a new buffer and held in memory at one time + // Returns 1 if the message was started successfully, 0 if there was an error + boolean beginPublish(const char* topic, unsigned int plength, boolean retained); + // Finish off this publish message (started with beginPublish) + // Returns 1 if the packet was sent successfully, 0 if there was an error + int endPublish(); + // Write a single byte of payload (only to be used with beginPublish/endPublish) + virtual size_t write(uint8_t); + // Write size bytes from buffer into the payload (only to be used with beginPublish/endPublish) + // Returns the number of bytes written + virtual size_t write(const uint8_t *buffer, size_t size); boolean subscribe(const char* topic); boolean subscribe(const char* topic, uint8_t qos); boolean unsubscribe(const char* topic); diff --git a/lib/PubSubClient-EspEasy-2.6.09/tests/.gitignore b/lib/PubSubClient-EspEasy-2.7.11/tests/.gitignore similarity index 100% rename from lib/PubSubClient-EspEasy-2.6.09/tests/.gitignore rename to lib/PubSubClient-EspEasy-2.7.11/tests/.gitignore diff --git a/lib/PubSubClient-EspEasy-2.6.09/tests/Makefile b/lib/PubSubClient-EspEasy-2.7.11/tests/Makefile similarity index 100% rename from lib/PubSubClient-EspEasy-2.6.09/tests/Makefile rename to lib/PubSubClient-EspEasy-2.7.11/tests/Makefile diff --git a/lib/PubSubClient-EspEasy-2.6.09/tests/README.md b/lib/PubSubClient-EspEasy-2.7.11/tests/README.md similarity index 100% rename from lib/PubSubClient-EspEasy-2.6.09/tests/README.md rename to lib/PubSubClient-EspEasy-2.7.11/tests/README.md diff --git a/lib/PubSubClient-EspEasy-2.6.09/tests/src/connect_spec.cpp b/lib/PubSubClient-EspEasy-2.7.11/tests/src/connect_spec.cpp similarity index 83% rename from lib/PubSubClient-EspEasy-2.6.09/tests/src/connect_spec.cpp rename to lib/PubSubClient-EspEasy-2.7.11/tests/src/connect_spec.cpp index 69f18646fb35..e27a1f59ff4d 100644 --- a/lib/PubSubClient-EspEasy-2.6.09/tests/src/connect_spec.cpp +++ b/lib/PubSubClient-EspEasy-2.7.11/tests/src/connect_spec.cpp @@ -98,6 +98,33 @@ int test_connect_fails_on_bad_rc() { END_IT } +int test_connect_non_clean_session() { + IT("sends a properly formatted non-clean session connect packet and succeeds"); + ShimClient shimClient; + + shimClient.setAllowConnect(true); + byte expectServer[] = { 172, 16, 0, 2 }; + shimClient.expectConnect(expectServer,1883); + byte connect[] = {0x10,0x18,0x0,0x4,0x4d,0x51,0x54,0x54,0x4,0x0,0x0,0xf,0x0,0xc,0x63,0x6c,0x69,0x65,0x6e,0x74,0x5f,0x74,0x65,0x73,0x74,0x31}; + byte connack[] = { 0x20, 0x02, 0x00, 0x00 }; + + shimClient.expect(connect,26); + shimClient.respond(connack,4); + + PubSubClient client(server, 1883, callback, shimClient); + int state = client.state(); + IS_TRUE(state == MQTT_DISCONNECTED); + + int rc = client.connect((char*)"client_test1",0,0,0,0,0,0,0); + IS_TRUE(rc); + IS_FALSE(shimClient.error()); + + state = client.state(); + IS_TRUE(state == MQTT_CONNECTED); + + END_IT +} + int test_connect_accepts_username_password() { IT("accepts a username and password"); ShimClient shimClient; @@ -133,6 +160,23 @@ int test_connect_accepts_username_no_password() { END_IT } +int test_connect_accepts_username_blank_password() { + IT("accepts a username and blank password"); + ShimClient shimClient; + shimClient.setAllowConnect(true); + + byte connect[] = { 0x10,0x20,0x0,0x4,0x4d,0x51,0x54,0x54,0x4,0xc2,0x0,0xf,0x0,0xc,0x63,0x6c,0x69,0x65,0x6e,0x74,0x5f,0x74,0x65,0x73,0x74,0x31,0x0,0x4,0x75,0x73,0x65,0x72,0x0,0x0}; + byte connack[] = { 0x20, 0x02, 0x00, 0x00 }; + shimClient.expect(connect,0x26); + shimClient.respond(connack,4); + + PubSubClient client(server, 1883, callback, shimClient); + int rc = client.connect((char*)"client_test1",(char*)"user",(char*)"pass"); + IS_TRUE(rc); + IS_FALSE(shimClient.error()); + + END_IT +} int test_connect_ignores_password_no_username() { IT("ignores a password but no username"); @@ -239,10 +283,12 @@ int test_connect_disconnect_connect() { int main() { SUITE("Connect"); + test_connect_fails_no_network(); test_connect_fails_on_no_response(); test_connect_properly_formatted(); + test_connect_non_clean_session(); test_connect_accepts_username_password(); test_connect_fails_on_bad_rc(); test_connect_properly_formatted_hostname(); diff --git a/lib/PubSubClient-EspEasy-2.6.09/tests/src/keepalive_spec.cpp b/lib/PubSubClient-EspEasy-2.7.11/tests/src/keepalive_spec.cpp similarity index 100% rename from lib/PubSubClient-EspEasy-2.6.09/tests/src/keepalive_spec.cpp rename to lib/PubSubClient-EspEasy-2.7.11/tests/src/keepalive_spec.cpp diff --git a/lib/PubSubClient-EspEasy-2.6.09/tests/src/lib/Arduino.h b/lib/PubSubClient-EspEasy-2.7.11/tests/src/lib/Arduino.h similarity index 90% rename from lib/PubSubClient-EspEasy-2.6.09/tests/src/lib/Arduino.h rename to lib/PubSubClient-EspEasy-2.7.11/tests/src/lib/Arduino.h index c6752801a7be..2a00f24bceba 100644 --- a/lib/PubSubClient-EspEasy-2.6.09/tests/src/lib/Arduino.h +++ b/lib/PubSubClient-EspEasy-2.7.11/tests/src/lib/Arduino.h @@ -5,6 +5,7 @@ #include #include #include +#include "Print.h" extern "C"{ @@ -20,4 +21,6 @@ extern "C"{ #define PROGMEM #define pgm_read_byte_near(x) *(x) +#define yield(x) {} + #endif // Arduino_h diff --git a/lib/PubSubClient-EspEasy-2.6.09/tests/src/lib/BDDTest.cpp b/lib/PubSubClient-EspEasy-2.7.11/tests/src/lib/BDDTest.cpp similarity index 100% rename from lib/PubSubClient-EspEasy-2.6.09/tests/src/lib/BDDTest.cpp rename to lib/PubSubClient-EspEasy-2.7.11/tests/src/lib/BDDTest.cpp diff --git a/lib/PubSubClient-EspEasy-2.6.09/tests/src/lib/BDDTest.h b/lib/PubSubClient-EspEasy-2.7.11/tests/src/lib/BDDTest.h similarity index 100% rename from lib/PubSubClient-EspEasy-2.6.09/tests/src/lib/BDDTest.h rename to lib/PubSubClient-EspEasy-2.7.11/tests/src/lib/BDDTest.h diff --git a/lib/PubSubClient-EspEasy-2.6.09/tests/src/lib/Buffer.cpp b/lib/PubSubClient-EspEasy-2.7.11/tests/src/lib/Buffer.cpp similarity index 86% rename from lib/PubSubClient-EspEasy-2.6.09/tests/src/lib/Buffer.cpp rename to lib/PubSubClient-EspEasy-2.7.11/tests/src/lib/Buffer.cpp index 59a2fbbbdc53..f07759a3a523 100644 --- a/lib/PubSubClient-EspEasy-2.6.09/tests/src/lib/Buffer.cpp +++ b/lib/PubSubClient-EspEasy-2.7.11/tests/src/lib/Buffer.cpp @@ -2,9 +2,13 @@ #include "Arduino.h" Buffer::Buffer() { + this->pos = 0; + this->length = 0; } Buffer::Buffer(uint8_t* buf, size_t size) { + this->pos = 0; + this->length = 0; this->add(buf,size); } bool Buffer::available() { diff --git a/lib/PubSubClient-EspEasy-2.6.09/tests/src/lib/Buffer.h b/lib/PubSubClient-EspEasy-2.7.11/tests/src/lib/Buffer.h similarity index 100% rename from lib/PubSubClient-EspEasy-2.6.09/tests/src/lib/Buffer.h rename to lib/PubSubClient-EspEasy-2.7.11/tests/src/lib/Buffer.h diff --git a/lib/PubSubClient-EspEasy-2.6.09/tests/src/lib/Client.h b/lib/PubSubClient-EspEasy-2.7.11/tests/src/lib/Client.h similarity index 100% rename from lib/PubSubClient-EspEasy-2.6.09/tests/src/lib/Client.h rename to lib/PubSubClient-EspEasy-2.7.11/tests/src/lib/Client.h diff --git a/lib/PubSubClient-EspEasy-2.6.09/tests/src/lib/IPAddress.cpp b/lib/PubSubClient-EspEasy-2.7.11/tests/src/lib/IPAddress.cpp similarity index 100% rename from lib/PubSubClient-EspEasy-2.6.09/tests/src/lib/IPAddress.cpp rename to lib/PubSubClient-EspEasy-2.7.11/tests/src/lib/IPAddress.cpp diff --git a/lib/PubSubClient-EspEasy-2.6.09/tests/src/lib/IPAddress.h b/lib/PubSubClient-EspEasy-2.7.11/tests/src/lib/IPAddress.h similarity index 100% rename from lib/PubSubClient-EspEasy-2.6.09/tests/src/lib/IPAddress.h rename to lib/PubSubClient-EspEasy-2.7.11/tests/src/lib/IPAddress.h diff --git a/lib/PubSubClient-EspEasy-2.7.11/tests/src/lib/Print.h b/lib/PubSubClient-EspEasy-2.7.11/tests/src/lib/Print.h new file mode 100644 index 000000000000..02ef77c2cb76 --- /dev/null +++ b/lib/PubSubClient-EspEasy-2.7.11/tests/src/lib/Print.h @@ -0,0 +1,28 @@ +/* + Print.h - Base class that provides print() and println() + Copyright (c) 2008 David A. Mellis. All right reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef Print_h +#define Print_h + +class Print { + public: + virtual size_t write(uint8_t) = 0; +}; + +#endif diff --git a/lib/PubSubClient-EspEasy-2.6.09/tests/src/lib/ShimClient.cpp b/lib/PubSubClient-EspEasy-2.7.11/tests/src/lib/ShimClient.cpp similarity index 100% rename from lib/PubSubClient-EspEasy-2.6.09/tests/src/lib/ShimClient.cpp rename to lib/PubSubClient-EspEasy-2.7.11/tests/src/lib/ShimClient.cpp diff --git a/lib/PubSubClient-EspEasy-2.6.09/tests/src/lib/ShimClient.h b/lib/PubSubClient-EspEasy-2.7.11/tests/src/lib/ShimClient.h similarity index 100% rename from lib/PubSubClient-EspEasy-2.6.09/tests/src/lib/ShimClient.h rename to lib/PubSubClient-EspEasy-2.7.11/tests/src/lib/ShimClient.h diff --git a/lib/PubSubClient-EspEasy-2.6.09/tests/src/lib/Stream.cpp b/lib/PubSubClient-EspEasy-2.7.11/tests/src/lib/Stream.cpp similarity index 100% rename from lib/PubSubClient-EspEasy-2.6.09/tests/src/lib/Stream.cpp rename to lib/PubSubClient-EspEasy-2.7.11/tests/src/lib/Stream.cpp diff --git a/lib/PubSubClient-EspEasy-2.6.09/tests/src/lib/Stream.h b/lib/PubSubClient-EspEasy-2.7.11/tests/src/lib/Stream.h similarity index 100% rename from lib/PubSubClient-EspEasy-2.6.09/tests/src/lib/Stream.h rename to lib/PubSubClient-EspEasy-2.7.11/tests/src/lib/Stream.h diff --git a/lib/PubSubClient-EspEasy-2.6.09/tests/src/lib/trace.h b/lib/PubSubClient-EspEasy-2.7.11/tests/src/lib/trace.h similarity index 100% rename from lib/PubSubClient-EspEasy-2.6.09/tests/src/lib/trace.h rename to lib/PubSubClient-EspEasy-2.7.11/tests/src/lib/trace.h diff --git a/lib/PubSubClient-EspEasy-2.6.09/tests/src/publish_spec.cpp b/lib/PubSubClient-EspEasy-2.7.11/tests/src/publish_spec.cpp similarity index 100% rename from lib/PubSubClient-EspEasy-2.6.09/tests/src/publish_spec.cpp rename to lib/PubSubClient-EspEasy-2.7.11/tests/src/publish_spec.cpp diff --git a/lib/PubSubClient-EspEasy-2.6.09/tests/src/receive_spec.cpp b/lib/PubSubClient-EspEasy-2.7.11/tests/src/receive_spec.cpp similarity index 89% rename from lib/PubSubClient-EspEasy-2.6.09/tests/src/receive_spec.cpp rename to lib/PubSubClient-EspEasy-2.7.11/tests/src/receive_spec.cpp index 54a62ee5c9e6..9a18af042d8b 100644 --- a/lib/PubSubClient-EspEasy-2.6.09/tests/src/receive_spec.cpp +++ b/lib/PubSubClient-EspEasy-2.7.11/tests/src/receive_spec.cpp @@ -160,6 +160,35 @@ int test_receive_oversized_message() { END_IT } +int test_drop_invalid_remaining_length_message() { + IT("drops invalid remaining length message"); + reset_callback(); + + ShimClient shimClient; + shimClient.setAllowConnect(true); + + byte connack[] = { 0x20, 0x02, 0x00, 0x00 }; + shimClient.respond(connack,4); + + PubSubClient client(server, 1883, callback, shimClient); + int rc = client.connect((char*)"client_test1"); + IS_TRUE(rc); + + byte publish[] = {0x30,0x92,0x92,0x92,0x92,0x01,0x0,0x5,0x74,0x6f,0x70,0x69,0x63,0x70,0x61,0x79,0x6c,0x6f,0x61,0x64}; + shimClient.respond(publish,20); + + rc = client.loop(); + + IS_FALSE(rc); + + IS_FALSE(callback_called); + + IS_FALSE(shimClient.error()); + + END_IT +} + + int test_receive_oversized_stream_message() { IT("drops an oversized message"); reset_callback(); @@ -241,6 +270,7 @@ int main() test_receive_callback(); test_receive_stream(); test_receive_max_sized_message(); + test_drop_invalid_remaining_length_message(); test_receive_oversized_message(); test_receive_oversized_stream_message(); test_receive_qos1(); diff --git a/lib/PubSubClient-EspEasy-2.6.09/tests/src/subscribe_spec.cpp b/lib/PubSubClient-EspEasy-2.7.11/tests/src/subscribe_spec.cpp similarity index 100% rename from lib/PubSubClient-EspEasy-2.6.09/tests/src/subscribe_spec.cpp rename to lib/PubSubClient-EspEasy-2.7.11/tests/src/subscribe_spec.cpp diff --git a/lib/PubSubClient-EspEasy-2.6.09/tests/testcases/__init__.py b/lib/PubSubClient-EspEasy-2.7.11/tests/testcases/__init__.py similarity index 100% rename from lib/PubSubClient-EspEasy-2.6.09/tests/testcases/__init__.py rename to lib/PubSubClient-EspEasy-2.7.11/tests/testcases/__init__.py diff --git a/lib/PubSubClient-EspEasy-2.7.11/tests/testcases/mqtt_basic.py b/lib/PubSubClient-EspEasy-2.7.11/tests/testcases/mqtt_basic.py new file mode 100644 index 000000000000..f23ef71c13ea --- /dev/null +++ b/lib/PubSubClient-EspEasy-2.7.11/tests/testcases/mqtt_basic.py @@ -0,0 +1,39 @@ +import unittest +import settings +import time +import mosquitto + + +def on_message(mosq, obj, msg): + obj.message_queue.append(msg) + + +class mqtt_basic(unittest.TestCase): + + message_queue = [] + + @classmethod + def setUpClass(self): + self.client = mosquitto.Mosquitto("pubsubclient_ut", clean_session=True, obj=self) + self.client.connect(settings.server_ip) + self.client.on_message = on_message + self.client.subscribe("outTopic", 0) + + @classmethod + def tearDownClass(self): + self.client.disconnect() + + def test_one(self): + i = 30 + while len(self.message_queue) == 0 and i > 0: + self.client.loop() + time.sleep(0.5) + i -= 1 + self.assertTrue(i > 0, "message receive timed-out") + self.assertEqual(len(self.message_queue), 1, "unexpected number of messages received") + msg = self.message_queue[0] + self.assertEqual(msg.mid, 0, "message id not 0") + self.assertEqual(msg.topic, "outTopic", "message topic incorrect") + self.assertEqual(msg.payload, "hello world") + self.assertEqual(msg.qos, 0, "message qos not 0") + self.assertEqual(msg.retain, False, "message retain flag incorrect") diff --git a/lib/PubSubClient-EspEasy-2.7.11/tests/testcases/mqtt_publish_in_callback.py b/lib/PubSubClient-EspEasy-2.7.11/tests/testcases/mqtt_publish_in_callback.py new file mode 100644 index 000000000000..45b0a8515520 --- /dev/null +++ b/lib/PubSubClient-EspEasy-2.7.11/tests/testcases/mqtt_publish_in_callback.py @@ -0,0 +1,59 @@ +import unittest +import settings +import time +import mosquitto + + +def on_message(mosq, obj, msg): + obj.message_queue.append(msg) + + +class mqtt_publish_in_callback(unittest.TestCase): + + message_queue = [] + + @classmethod + def setUpClass(self): + self.client = mosquitto.Mosquitto("pubsubclient_ut", clean_session=True, obj=self) + self.client.connect(settings.server_ip) + self.client.on_message = on_message + self.client.subscribe("outTopic", 0) + + @classmethod + def tearDownClass(self): + self.client.disconnect() + + def test_connect(self): + i = 30 + while len(self.message_queue) == 0 and i > 0: + self.client.loop() + time.sleep(0.5) + i -= 1 + self.assertTrue(i > 0, "message receive timed-out") + self.assertEqual(len(self.message_queue), 1, "unexpected number of messages received") + msg = self.message_queue.pop(0) + self.assertEqual(msg.mid, 0, "message id not 0") + self.assertEqual(msg.topic, "outTopic", "message topic incorrect") + self.assertEqual(msg.payload, "hello world") + self.assertEqual(msg.qos, 0, "message qos not 0") + self.assertEqual(msg.retain, False, "message retain flag incorrect") + + def test_publish(self): + self.assertEqual(len(self.message_queue), 0, "message queue not empty") + payload = "abcdefghij" + self.client.publish("inTopic", payload) + + i = 30 + while len(self.message_queue) == 0 and i > 0: + self.client.loop() + time.sleep(0.5) + i -= 1 + + self.assertTrue(i > 0, "message receive timed-out") + self.assertEqual(len(self.message_queue), 1, "unexpected number of messages received") + msg = self.message_queue.pop(0) + self.assertEqual(msg.mid, 0, "message id not 0") + self.assertEqual(msg.topic, "outTopic", "message topic incorrect") + self.assertEqual(msg.payload, payload) + self.assertEqual(msg.qos, 0, "message qos not 0") + self.assertEqual(msg.retain, False, "message retain flag incorrect") diff --git a/lib/PubSubClient-EspEasy-2.6.09/tests/testcases/settings.py b/lib/PubSubClient-EspEasy-2.7.11/tests/testcases/settings.py similarity index 100% rename from lib/PubSubClient-EspEasy-2.6.09/tests/testcases/settings.py rename to lib/PubSubClient-EspEasy-2.7.11/tests/testcases/settings.py diff --git a/lib/PubSubClient-EspEasy-2.7.11/tests/testsuite.py b/lib/PubSubClient-EspEasy-2.7.11/tests/testsuite.py new file mode 100644 index 000000000000..788fc5d97c7b --- /dev/null +++ b/lib/PubSubClient-EspEasy-2.7.11/tests/testsuite.py @@ -0,0 +1,181 @@ +#!/usr/bin/env python +import os +import os.path +import sys +import shutil +from subprocess import call +import importlib +import unittest +import re + +from testcases import settings + + +class Workspace(object): + + def __init__(self): + self.root_dir = os.getcwd() + self.build_dir = os.path.join(self.root_dir, "tmpbin") + self.log_dir = os.path.join(self.root_dir, "logs") + self.tests_dir = os.path.join(self.root_dir, "testcases") + self.examples_dir = os.path.join(self.root_dir, "../PubSubClient/examples") + self.examples = [] + self.tests = [] + if not os.path.isdir("../PubSubClient"): + raise Exception("Cannot find PubSubClient library") + try: + return __import__('ino') + except ImportError: + raise Exception("ino tool not installed") + + def init(self): + if os.path.isdir(self.build_dir): + shutil.rmtree(self.build_dir) + os.mkdir(self.build_dir) + if os.path.isdir(self.log_dir): + shutil.rmtree(self.log_dir) + os.mkdir(self.log_dir) + + os.chdir(self.build_dir) + call(["ino", "init"]) + + shutil.copytree("../../PubSubClient", "lib/PubSubClient") + + filenames = [] + for root, dirs, files in os.walk(self.examples_dir): + filenames += [os.path.join(root, f) for f in files if f.endswith(".ino")] + filenames.sort() + for e in filenames: + self.examples.append(Sketch(self, e)) + + filenames = [] + for root, dirs, files in os.walk(self.tests_dir): + filenames += [os.path.join(root, f) for f in files if f.endswith(".ino")] + filenames.sort() + for e in filenames: + self.tests.append(Sketch(self, e)) + + def clean(self): + shutil.rmtree(self.build_dir) + + +class Sketch(object): + def __init__(self, wksp, fn): + self.w = wksp + self.filename = fn + self.basename = os.path.basename(self.filename) + self.build_log = os.path.join(self.w.log_dir, "%s.log" % (os.path.basename(self.filename),)) + self.build_err_log = os.path.join(self.w.log_dir, "%s.err.log" % (os.path.basename(self.filename),)) + self.build_upload_log = os.path.join(self.w.log_dir, "%s.upload.log" % (os.path.basename(self.filename),)) + + def build(self): + sys.stdout.write(" Build: ") + sys.stdout.flush() + + # Copy sketch over, replacing IP addresses as necessary + fin = open(self.filename, "r") + lines = fin.readlines() + fin.close() + fout = open(os.path.join(self.w.build_dir, "src", "sketch.ino"), "w") + for l in lines: + if re.match(r"^byte server\[\] = {", l): + fout.write("byte server[] = { %s };\n" % (settings.server_ip.replace(".", ", "),)) + elif re.match(r"^byte ip\[\] = {", l): + fout.write("byte ip[] = { %s };\n" % (settings.arduino_ip.replace(".", ", "),)) + else: + fout.write(l) + fout.flush() + fout.close() + + # Run build + fout = open(self.build_log, "w") + ferr = open(self.build_err_log, "w") + rc = call(["ino", "build"], stdout=fout, stderr=ferr) + fout.close() + ferr.close() + if rc == 0: + sys.stdout.write("pass") + sys.stdout.write("\n") + return True + else: + sys.stdout.write("fail") + sys.stdout.write("\n") + with open(self.build_err_log) as f: + for line in f: + print(" " + line) + return False + + def upload(self): + sys.stdout.write(" Upload: ") + sys.stdout.flush() + fout = open(self.build_upload_log, "w") + rc = call(["ino", "upload"], stdout=fout, stderr=fout) + fout.close() + if rc == 0: + sys.stdout.write("pass") + sys.stdout.write("\n") + return True + else: + sys.stdout.write("fail") + sys.stdout.write("\n") + with open(self.build_upload_log) as f: + for line in f: + print(" " + line) + return False + + def test(self): + # import the matching test case, if it exists + try: + basename = os.path.basename(self.filename)[:-4] + i = importlib.import_module("testcases." + basename) + except: + sys.stdout.write(" Test: no tests found") + sys.stdout.write("\n") + return + c = getattr(i, basename) + + testmethods = [m for m in dir(c) if m.startswith("test_")] + testmethods.sort() + tests = [] + for m in testmethods: + tests.append(c(m)) + + result = unittest.TestResult() + c.setUpClass() + if self.upload(): + sys.stdout.write(" Test: ") + sys.stdout.flush() + for t in tests: + t.run(result) + print(str(result.testsRun - len(result.failures) - len(result.errors)) + "/" + str(result.testsRun)) + if not result.wasSuccessful(): + if len(result.failures) > 0: + for f in result.failures: + print("-- " + str(f[0])) + print(f[1]) + if len(result.errors) > 0: + print(" Errors:") + for f in result.errors: + print("-- " + str(f[0])) + print(f[1]) + c.tearDownClass() + + +if __name__ == '__main__': + run_tests = True + + w = Workspace() + w.init() + + for e in w.examples: + print("--------------------------------------") + print("[" + e.basename + "]") + if e.build() and run_tests: + e.test() + for e in w.tests: + print("--------------------------------------") + print("[" + e.basename + "]") + if e.build() and run_tests: + e.test() + + w.clean() diff --git a/lib/vl53l0x-arduino-1.02/.travis.yml b/lib/vl53l0x-arduino-1.02/.travis.yml new file mode 100755 index 000000000000..f3ed70c21819 --- /dev/null +++ b/lib/vl53l0x-arduino-1.02/.travis.yml @@ -0,0 +1,24 @@ +language: python + +cache: + directories: + - "~/.platformio" + +install: +- pip install -U platformio + +env: +- BOARD=uno +- BOARD=leonardo +- BOARD=micro +- BOARD=megaatmega2560 +- BOARD=due +- BOARD=yun +- BOARD=genuino101 +- BOARD=zero + +script: +- set -eo pipefail; + for e in examples/*; do + platformio ci --board=$BOARD --lib=. $e/*; + done diff --git a/lib/vl53l0x-arduino-1.02/LICENSE.txt b/lib/vl53l0x-arduino-1.02/LICENSE.txt new file mode 100755 index 000000000000..bea985af6629 --- /dev/null +++ b/lib/vl53l0x-arduino-1.02/LICENSE.txt @@ -0,0 +1,71 @@ +Copyright (c) 2017 Pololu Corporation. For more information, see + +https://www.pololu.com/ +https://forum.pololu.com/ + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +================================================================= + +Most of the functionality of this library is based on the VL53L0X +API provided by ST (STSW-IMG005), and some of the explanatory +comments are quoted or paraphrased from the API source code, API +user manual (UM2039), and the VL53L0X datasheet. + +The following applies to source code reproduced or derived from +the API: + +----------------------------------------------------------------- + +Copyright © 2016, STMicroelectronics International N.V. All +rights reserved. + +Redistribution and use in source and binary forms, with or +without modification, are permitted provided that the following +conditions are met: +* Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following +disclaimer in the documentation and/or other materials provided +with the distribution. +* Neither the name of STMicroelectronics nor the +names of its contributors may be used to endorse or promote +products derived from this software without specific prior +written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND +CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND +NON-INFRINGEMENT OF INTELLECTUAL PROPERTY RIGHTS ARE DISCLAIMED. +IN NO EVENT SHALL STMICROELECTRONICS INTERNATIONAL N.V. BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT +OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. + +----------------------------------------------------------------- diff --git a/lib/vl53l0x-arduino-1.02/README.md b/lib/vl53l0x-arduino-1.02/README.md new file mode 100755 index 000000000000..917514d7080a --- /dev/null +++ b/lib/vl53l0x-arduino-1.02/README.md @@ -0,0 +1,164 @@ +# VL53L0X library for Arduino + +Version: 1.0.2
+Release date: 2017 Jun 27
+[![Build Status](https://travis-ci.org/pololu/vl53l0x-arduino.svg?branch=master)](https://travis-ci.org/pololu/vl53l0x-arduino)
+[www.pololu.com](https://www.pololu.com/) + +## Summary + +This is a library for the Arduino IDE that helps interface with ST's [VL53L0X time-of-flight distance sensor](https://www.pololu.com/product/2490). The library makes it simple to configure the sensor and read range data from it via I²C. + +## Supported platforms + +This library is designed to work with the Arduino IDE versions 1.6.x or later; we have not tested it with earlier versions. This library should support any Arduino-compatible board, including the [Pololu A-Star 32U4 controllers](https://www.pololu.com/category/149/a-star-programmable-controllers). + +## Getting started + +### Hardware + +A [VL53L0X carrier](https://www.pololu.com/product/2490) can be purchased from Pololu's website. Before continuing, careful reading of the [product page](https://www.pololu.com/product/2490) as well as the VL53L0X datasheet is recommended. + +Make the following connections between the Arduino and the VL53L0X board: + +#### 5V Arduino boards + +(including Arduino Uno, Leonardo, Mega; Pololu A-Star 32U4) + + Arduino VL53L0X board + ------- ------------- + 5V - VIN + GND - GND + SDA - SDA + SCL - SCL + +#### 3.3V Arduino boards + +(including Arduino Due) + + Arduino VL53L0X board + ------- ------------- + 3V3 - VIN + GND - GND + SDA - SDA + SCL - SCL + +### Software + +If you are using version 1.6.2 or later of the [Arduino software (IDE)](http://www.arduino.cc/en/Main/Software), you can use the Library Manager to install this library: + +1. In the Arduino IDE, open the "Sketch" menu, select "Include Library", then "Manage Libraries...". +2. Search for "VL53L0X". +3. Click the VL53L0X entry in the list. +4. Click "Install". + +If this does not work, you can manually install the library: + +1. Download the [latest release archive from GitHub](https://github.com/pololu/vl53l0x-arduino/releases) and decompress it. +2. Rename the folder "vl53l0x-arduino-master" to "VL53L0X". +3. Move the "VL53L0X" folder into the "libraries" directory inside your Arduino sketchbook directory. You can view your sketchbook location by opening the "File" menu and selecting "Preferences" in the Arduino IDE. If there is not already a "libraries" folder in that location, you should make the folder yourself. +4. After installing the library, restart the Arduino IDE. + +## Examples + +Several example sketches are available that show how to use the library. You can access them from the Arduino IDE by opening the "File" menu, selecting "Examples", and then selecting "VL53L0X". If you cannot find these examples, the library was probably installed incorrectly and you should retry the installation instructions above. + +## ST's VL53L0X API and this library + +Most of the functionality of this library is based on the [VL53L0X API](http://www.st.com/content/st_com/en/products/embedded-software/proximity-sensors-software/stsw-img005.html) provided by ST (STSW-IMG005), and some of the explanatory comments in the code are quoted or paraphrased from the API source code, API user manual (UM2039), and the VL53L0X datasheet. For more explanation about the library code and how it was derived from the API, see the comments in VL53L0X.cpp. + +This library is intended to provide a quicker and easier way to get started using the VL53L0X with an Arduino-compatible controller, in contrast to customizing and compiling ST's API for the Arduino. The library has a more streamlined interface, as well as smaller storage and memory footprints. However, it does not implement some of the more advanced functionality available in the API (for example, calibrating the sensor to work well under a cover glass), and it has less robust error checking. For advanced applications, especially when storage and memory are less of an issue, consider using the VL53L0X API directly. + +## Library reference + +* `uint8_t last_status`
+ The status of the last I²C write transmission. See the [`Wire.endTransmission()` documentation](http://arduino.cc/en/Reference/WireEndTransmission) for return values. + +* `VL53L0X(void)`
+ Constructor. + +* `void setAddress(uint8_t new_addr)`
+ Changes the I²C slave device address of the VL53L0X to the given value (7-bit). + +* `uint8_t getAddress(void)`
+ Returns the current I²C address. + +* `bool init(bool io_2v8 = true)`
+ Iniitializes and configures the sensor. If the optional argument `io_2v8` is true (the default if not specified), the sensor is configured for 2V8 mode (2.8 V I/O); if false, the sensor is left in 1V8 mode. The return value is a boolean indicating whether the initialization completed successfully. + +* `void writeReg(uint8_t reg, uint8_t value)`
+ Writes an 8-bit sensor register with the given value. + + Register address constants are defined by the regAddr enumeration type in VL53L0X.h.
+ Example use: `sensor.writeReg(VL53L0X::SYSRANGE_START, 0x01);` + +* `void writeReg16Bit(uint8_t reg, uint16_t value)`
+ Writes a 16-bit sensor register with the given value. + +* `void writeReg32Bit(uint8_t reg, uint32_t value)`
+ Writes a 32-bit sensor register with the given value. + +* `uint8_t readReg(uint8_t reg)`
+ Reads an 8-bit sensor register and returns the value read. + +* `uint16_t readReg16Bit(uint8_t reg)`
+ Reads a 16-bit sensor register and returns the value read. + +* `uint32_t readReg32Bit(uint8_t reg)`
+ Reads a 32-bit sensor register and returns the value read. + +* `void writeMulti(uint8_t reg, uint8_t const * src, uint8_t count)`
+ Writes an arbitrary number of bytes from the given array to the sensor, starting at the given register. + +* `void readMulti(uint8_t reg, uint8_t * dst, uint8_t count)`
+ Reads an arbitrary number of bytes from the sensor, starting at the given register, into the given array. + +* `bool setSignalRateLimit(float limit_Mcps)`
+ Sets the return signal rate limit to the given value in units of MCPS (mega counts per second). This is the minimum amplitude of the signal reflected from the target and received by the sensor necessary for it to report a valid reading. Setting a lower limit increases the potential range of the sensor but also increases the likelihood of getting an inaccurate reading because of reflections from objects other than the intended target. This limit is initialized to 0.25 MCPS by default. The return value is a boolean indicating whether the requested limit was valid. + +* `float getSignalRateLimit(void)`
+ Returns the current return signal rate limit in MCPS. + +* `bool setMeasurementTimingBudget(uint32_t budget_us)`
+ Sets the measurement timing budget to the given value in microseconds. This is the time allowed for one range measurement; a longer timing budget allows for more accurate measurements. The default budget is about 33000 microseconds, or 33 ms; the minimum is 20 ms. The return value is a boolean indicating whether the requested budget was valid. + +* `uint32_t getMeasurementTimingBudget(void)`
+ Returns the current measurement timing budget in microseconds. + +* `bool setVcselPulsePeriod(vcselPeriodType type, uint8_t period_pclks)` + Sets the VCSEL (vertical cavity surface emitting laser) pulse period for the given period type (`VL53L0X::VcselPeriodPreRange` or `VL53L0X::VcselPeriodFinalRange`) to the given value (in PCLKs). Longer periods increase the potential range of the sensor. Valid values are (even numbers only): + + Pre: 12 to 18 (initialized to 14 by default)
+ Final: 8 to 14 (initialized to 10 by default) + + The return value is a boolean indicating whether the requested period was valid. + +* `uint8_t getVcselPulsePeriod(vcselPeriodType type)`
+ Returns the current VCSEL pulse period for the given period type. + +* `void startContinuous(uint32_t period_ms = 0)`
+ Starts continuous ranging measurements. If the optional argument `period_ms` is 0 (the default if not specified), continuous back-to-back mode is used (the sensor takes measurements as often as possible); if it is nonzero, continuous timed mode is used, with the specified inter-measurement period in milliseconds determining how often the sensor takes a measurement. + +* `void stopContinuous(void)`
+ Stops continuous mode. + +* `uint16_t readRangeContinuousMillimeters(void)`
+ Returns a range reading in millimeters when continuous mode is active. + +* `uint16_t readRangeSingleMillimeters(void)`
+ Performs a single-shot ranging measurement and returns the reading in millimeters. + +* `void setTimeout(uint16_t timeout)`
+ Sets a timeout period in milliseconds after which read operations will abort if the sensor is not ready. A value of 0 disables the timeout. + +* `uint16_t getTimeout(void)`
+ Returns the current timeout period setting. + +* `bool timeoutOccurred(void)`
+ Indicates whether a read timeout has occurred since the last call to `timeoutOccurred()`. + +## Version history + +* 1.0.2 (2017 Jun 27): Fixed a typo in a register modification in `getSpadInfo()` (thanks @tridge). +* 1.0.1 (2016 Dec 08): Fixed type error in `readReg32Bit()`. +* 1.0.0 (2016 Aug 12): Original release. diff --git a/lib/vl53l0x-arduino-1.02/VL53L0X.cpp b/lib/vl53l0x-arduino-1.02/VL53L0X.cpp new file mode 100755 index 000000000000..fb2ed30286d4 --- /dev/null +++ b/lib/vl53l0x-arduino-1.02/VL53L0X.cpp @@ -0,0 +1,1036 @@ +// Most of the functionality of this library is based on the VL53L0X API +// provided by ST (STSW-IMG005), and some of the explanatory comments are quoted +// or paraphrased from the API source code, API user manual (UM2039), and the +// VL53L0X datasheet. + +#include +#include + +// Defines ///////////////////////////////////////////////////////////////////// + +// The Arduino two-wire interface uses a 7-bit number for the address, +// and sets the last bit correctly based on reads and writes +#define ADDRESS_DEFAULT 0b0101001 + +// Record the current time to check an upcoming timeout against +#define startTimeout() (timeout_start_ms = millis()) + +// Check if timeout is enabled (set to nonzero value) and has expired +#define checkTimeoutExpired() (io_timeout > 0 && ((uint16_t)millis() - timeout_start_ms) > io_timeout) + +// Decode VCSEL (vertical cavity surface emitting laser) pulse period in PCLKs +// from register value +// based on VL53L0X_decode_vcsel_period() +#define decodeVcselPeriod(reg_val) (((reg_val) + 1) << 1) + +// Encode VCSEL pulse period register value from period in PCLKs +// based on VL53L0X_encode_vcsel_period() +#define encodeVcselPeriod(period_pclks) (((period_pclks) >> 1) - 1) + +// Calculate macro period in *nanoseconds* from VCSEL period in PCLKs +// based on VL53L0X_calc_macro_period_ps() +// PLL_period_ps = 1655; macro_period_vclks = 2304 +#define calcMacroPeriod(vcsel_period_pclks) ((((uint32_t)2304 * (vcsel_period_pclks) * 1655) + 500) / 1000) + +// Constructors //////////////////////////////////////////////////////////////// + +VL53L0X::VL53L0X(void) + : address(ADDRESS_DEFAULT) + , io_timeout(0) // no timeout + , did_timeout(false) +{ +} + +// Public Methods ////////////////////////////////////////////////////////////// + +void VL53L0X::setAddress(uint8_t new_addr) +{ + writeReg(I2C_SLAVE_DEVICE_ADDRESS, new_addr & 0x7F); + address = new_addr; +} + +// Initialize sensor using sequence based on VL53L0X_DataInit(), +// VL53L0X_StaticInit(), and VL53L0X_PerformRefCalibration(). +// This function does not perform reference SPAD calibration +// (VL53L0X_PerformRefSpadManagement()), since the API user manual says that it +// is performed by ST on the bare modules; it seems like that should work well +// enough unless a cover glass is added. +// If io_2v8 (optional) is true or not given, the sensor is configured for 2V8 +// mode. +bool VL53L0X::init(bool io_2v8) +{ + // VL53L0X_DataInit() begin + + // sensor uses 1V8 mode for I/O by default; switch to 2V8 mode if necessary + if (io_2v8) + { + writeReg(VHV_CONFIG_PAD_SCL_SDA__EXTSUP_HV, + readReg(VHV_CONFIG_PAD_SCL_SDA__EXTSUP_HV) | 0x01); // set bit 0 + } + + // "Set I2C standard mode" + writeReg(0x88, 0x00); + + writeReg(0x80, 0x01); + writeReg(0xFF, 0x01); + writeReg(0x00, 0x00); + stop_variable = readReg(0x91); + writeReg(0x00, 0x01); + writeReg(0xFF, 0x00); + writeReg(0x80, 0x00); + + // disable SIGNAL_RATE_MSRC (bit 1) and SIGNAL_RATE_PRE_RANGE (bit 4) limit checks + writeReg(MSRC_CONFIG_CONTROL, readReg(MSRC_CONFIG_CONTROL) | 0x12); + + // set final range signal rate limit to 0.25 MCPS (million counts per second) + setSignalRateLimit(0.25); + + writeReg(SYSTEM_SEQUENCE_CONFIG, 0xFF); + + // VL53L0X_DataInit() end + + // VL53L0X_StaticInit() begin + + uint8_t spad_count; + bool spad_type_is_aperture; + if (!getSpadInfo(&spad_count, &spad_type_is_aperture)) { return false; } + + // The SPAD map (RefGoodSpadMap) is read by VL53L0X_get_info_from_device() in + // the API, but the same data seems to be more easily readable from + // GLOBAL_CONFIG_SPAD_ENABLES_REF_0 through _6, so read it from there + uint8_t ref_spad_map[6]; + readMulti(GLOBAL_CONFIG_SPAD_ENABLES_REF_0, ref_spad_map, 6); + + // -- VL53L0X_set_reference_spads() begin (assume NVM values are valid) + + writeReg(0xFF, 0x01); + writeReg(DYNAMIC_SPAD_REF_EN_START_OFFSET, 0x00); + writeReg(DYNAMIC_SPAD_NUM_REQUESTED_REF_SPAD, 0x2C); + writeReg(0xFF, 0x00); + writeReg(GLOBAL_CONFIG_REF_EN_START_SELECT, 0xB4); + + uint8_t first_spad_to_enable = spad_type_is_aperture ? 12 : 0; // 12 is the first aperture spad + uint8_t spads_enabled = 0; + + for (uint8_t i = 0; i < 48; i++) + { + if (i < first_spad_to_enable || spads_enabled == spad_count) + { + // This bit is lower than the first one that should be enabled, or + // (reference_spad_count) bits have already been enabled, so zero this bit + ref_spad_map[i / 8] &= ~(1 << (i % 8)); + } + else if ((ref_spad_map[i / 8] >> (i % 8)) & 0x1) + { + spads_enabled++; + } + } + + writeMulti(GLOBAL_CONFIG_SPAD_ENABLES_REF_0, ref_spad_map, 6); + + // -- VL53L0X_set_reference_spads() end + + // -- VL53L0X_load_tuning_settings() begin + // DefaultTuningSettings from vl53l0x_tuning.h + + writeReg(0xFF, 0x01); + writeReg(0x00, 0x00); + + writeReg(0xFF, 0x00); + writeReg(0x09, 0x00); + writeReg(0x10, 0x00); + writeReg(0x11, 0x00); + + writeReg(0x24, 0x01); + writeReg(0x25, 0xFF); + writeReg(0x75, 0x00); + + writeReg(0xFF, 0x01); + writeReg(0x4E, 0x2C); + writeReg(0x48, 0x00); + writeReg(0x30, 0x20); + + writeReg(0xFF, 0x00); + writeReg(0x30, 0x09); + writeReg(0x54, 0x00); + writeReg(0x31, 0x04); + writeReg(0x32, 0x03); + writeReg(0x40, 0x83); + writeReg(0x46, 0x25); + writeReg(0x60, 0x00); + writeReg(0x27, 0x00); + writeReg(0x50, 0x06); + writeReg(0x51, 0x00); + writeReg(0x52, 0x96); + writeReg(0x56, 0x08); + writeReg(0x57, 0x30); + writeReg(0x61, 0x00); + writeReg(0x62, 0x00); + writeReg(0x64, 0x00); + writeReg(0x65, 0x00); + writeReg(0x66, 0xA0); + + writeReg(0xFF, 0x01); + writeReg(0x22, 0x32); + writeReg(0x47, 0x14); + writeReg(0x49, 0xFF); + writeReg(0x4A, 0x00); + + writeReg(0xFF, 0x00); + writeReg(0x7A, 0x0A); + writeReg(0x7B, 0x00); + writeReg(0x78, 0x21); + + writeReg(0xFF, 0x01); + writeReg(0x23, 0x34); + writeReg(0x42, 0x00); + writeReg(0x44, 0xFF); + writeReg(0x45, 0x26); + writeReg(0x46, 0x05); + writeReg(0x40, 0x40); + writeReg(0x0E, 0x06); + writeReg(0x20, 0x1A); + writeReg(0x43, 0x40); + + writeReg(0xFF, 0x00); + writeReg(0x34, 0x03); + writeReg(0x35, 0x44); + + writeReg(0xFF, 0x01); + writeReg(0x31, 0x04); + writeReg(0x4B, 0x09); + writeReg(0x4C, 0x05); + writeReg(0x4D, 0x04); + + writeReg(0xFF, 0x00); + writeReg(0x44, 0x00); + writeReg(0x45, 0x20); + writeReg(0x47, 0x08); + writeReg(0x48, 0x28); + writeReg(0x67, 0x00); + writeReg(0x70, 0x04); + writeReg(0x71, 0x01); + writeReg(0x72, 0xFE); + writeReg(0x76, 0x00); + writeReg(0x77, 0x00); + + writeReg(0xFF, 0x01); + writeReg(0x0D, 0x01); + + writeReg(0xFF, 0x00); + writeReg(0x80, 0x01); + writeReg(0x01, 0xF8); + + writeReg(0xFF, 0x01); + writeReg(0x8E, 0x01); + writeReg(0x00, 0x01); + writeReg(0xFF, 0x00); + writeReg(0x80, 0x00); + + // -- VL53L0X_load_tuning_settings() end + + // "Set interrupt config to new sample ready" + // -- VL53L0X_SetGpioConfig() begin + + writeReg(SYSTEM_INTERRUPT_CONFIG_GPIO, 0x04); + writeReg(GPIO_HV_MUX_ACTIVE_HIGH, readReg(GPIO_HV_MUX_ACTIVE_HIGH) & ~0x10); // active low + writeReg(SYSTEM_INTERRUPT_CLEAR, 0x01); + + // -- VL53L0X_SetGpioConfig() end + + measurement_timing_budget_us = getMeasurementTimingBudget(); + + // "Disable MSRC and TCC by default" + // MSRC = Minimum Signal Rate Check + // TCC = Target CentreCheck + // -- VL53L0X_SetSequenceStepEnable() begin + + writeReg(SYSTEM_SEQUENCE_CONFIG, 0xE8); + + // -- VL53L0X_SetSequenceStepEnable() end + + // "Recalculate timing budget" + setMeasurementTimingBudget(measurement_timing_budget_us); + + // VL53L0X_StaticInit() end + + // VL53L0X_PerformRefCalibration() begin (VL53L0X_perform_ref_calibration()) + + // -- VL53L0X_perform_vhv_calibration() begin + + writeReg(SYSTEM_SEQUENCE_CONFIG, 0x01); + if (!performSingleRefCalibration(0x40)) { return false; } + + // -- VL53L0X_perform_vhv_calibration() end + + // -- VL53L0X_perform_phase_calibration() begin + + writeReg(SYSTEM_SEQUENCE_CONFIG, 0x02); + if (!performSingleRefCalibration(0x00)) { return false; } + + // -- VL53L0X_perform_phase_calibration() end + + // "restore the previous Sequence Config" + writeReg(SYSTEM_SEQUENCE_CONFIG, 0xE8); + + // VL53L0X_PerformRefCalibration() end + + return true; +} + +// Write an 8-bit register +void VL53L0X::writeReg(uint8_t reg, uint8_t value) +{ + Wire.beginTransmission(address); + Wire.write(reg); + Wire.write(value); + last_status = Wire.endTransmission(); +} + +// Write a 16-bit register +void VL53L0X::writeReg16Bit(uint8_t reg, uint16_t value) +{ + Wire.beginTransmission(address); + Wire.write(reg); + Wire.write((value >> 8) & 0xFF); // value high byte + Wire.write( value & 0xFF); // value low byte + last_status = Wire.endTransmission(); +} + +// Write a 32-bit register +void VL53L0X::writeReg32Bit(uint8_t reg, uint32_t value) +{ + Wire.beginTransmission(address); + Wire.write(reg); + Wire.write((value >> 24) & 0xFF); // value highest byte + Wire.write((value >> 16) & 0xFF); + Wire.write((value >> 8) & 0xFF); + Wire.write( value & 0xFF); // value lowest byte + last_status = Wire.endTransmission(); +} + +// Read an 8-bit register +uint8_t VL53L0X::readReg(uint8_t reg) +{ + uint8_t value; + + Wire.beginTransmission(address); + Wire.write(reg); + last_status = Wire.endTransmission(); + + Wire.requestFrom(address, (uint8_t)1); + value = Wire.read(); + + return value; +} + +// Read a 16-bit register +uint16_t VL53L0X::readReg16Bit(uint8_t reg) +{ + uint16_t value; + + Wire.beginTransmission(address); + Wire.write(reg); + last_status = Wire.endTransmission(); + + Wire.requestFrom(address, (uint8_t)2); + value = (uint16_t)Wire.read() << 8; // value high byte + value |= Wire.read(); // value low byte + + return value; +} + +// Read a 32-bit register +uint32_t VL53L0X::readReg32Bit(uint8_t reg) +{ + uint32_t value; + + Wire.beginTransmission(address); + Wire.write(reg); + last_status = Wire.endTransmission(); + + Wire.requestFrom(address, (uint8_t)4); + value = (uint32_t)Wire.read() << 24; // value highest byte + value |= (uint32_t)Wire.read() << 16; + value |= (uint16_t)Wire.read() << 8; + value |= Wire.read(); // value lowest byte + + return value; +} + +// Write an arbitrary number of bytes from the given array to the sensor, +// starting at the given register +void VL53L0X::writeMulti(uint8_t reg, uint8_t const * src, uint8_t count) +{ + Wire.beginTransmission(address); + Wire.write(reg); + + while (count-- > 0) + { + Wire.write(*(src++)); + } + + last_status = Wire.endTransmission(); +} + +// Read an arbitrary number of bytes from the sensor, starting at the given +// register, into the given array +void VL53L0X::readMulti(uint8_t reg, uint8_t * dst, uint8_t count) +{ + Wire.beginTransmission(address); + Wire.write(reg); + last_status = Wire.endTransmission(); + + Wire.requestFrom(address, count); + + while (count-- > 0) + { + *(dst++) = Wire.read(); + } +} + +// Set the return signal rate limit check value in units of MCPS (mega counts +// per second). "This represents the amplitude of the signal reflected from the +// target and detected by the device"; setting this limit presumably determines +// the minimum measurement necessary for the sensor to report a valid reading. +// Setting a lower limit increases the potential range of the sensor but also +// seems to increase the likelihood of getting an inaccurate reading because of +// unwanted reflections from objects other than the intended target. +// Defaults to 0.25 MCPS as initialized by the ST API and this library. +bool VL53L0X::setSignalRateLimit(float limit_Mcps) +{ + if (limit_Mcps < 0 || limit_Mcps > 511.99) { return false; } + + // Q9.7 fixed point format (9 integer bits, 7 fractional bits) + writeReg16Bit(FINAL_RANGE_CONFIG_MIN_COUNT_RATE_RTN_LIMIT, limit_Mcps * (1 << 7)); + return true; +} + +// Get the return signal rate limit check value in MCPS +float VL53L0X::getSignalRateLimit(void) +{ + return (float)readReg16Bit(FINAL_RANGE_CONFIG_MIN_COUNT_RATE_RTN_LIMIT) / (1 << 7); +} + +// Set the measurement timing budget in microseconds, which is the time allowed +// for one measurement; the ST API and this library take care of splitting the +// timing budget among the sub-steps in the ranging sequence. A longer timing +// budget allows for more accurate measurements. Increasing the budget by a +// factor of N decreases the range measurement standard deviation by a factor of +// sqrt(N). Defaults to about 33 milliseconds; the minimum is 20 ms. +// based on VL53L0X_set_measurement_timing_budget_micro_seconds() +bool VL53L0X::setMeasurementTimingBudget(uint32_t budget_us) +{ + SequenceStepEnables enables; + SequenceStepTimeouts timeouts; + + uint16_t const StartOverhead = 1320; // note that this is different than the value in get_ + uint16_t const EndOverhead = 960; + uint16_t const MsrcOverhead = 660; + uint16_t const TccOverhead = 590; + uint16_t const DssOverhead = 690; + uint16_t const PreRangeOverhead = 660; + uint16_t const FinalRangeOverhead = 550; + + uint32_t const MinTimingBudget = 20000; + + if (budget_us < MinTimingBudget) { return false; } + + uint32_t used_budget_us = StartOverhead + EndOverhead; + + getSequenceStepEnables(&enables); + getSequenceStepTimeouts(&enables, &timeouts); + + if (enables.tcc) + { + used_budget_us += (timeouts.msrc_dss_tcc_us + TccOverhead); + } + + if (enables.dss) + { + used_budget_us += 2 * (timeouts.msrc_dss_tcc_us + DssOverhead); + } + else if (enables.msrc) + { + used_budget_us += (timeouts.msrc_dss_tcc_us + MsrcOverhead); + } + + if (enables.pre_range) + { + used_budget_us += (timeouts.pre_range_us + PreRangeOverhead); + } + + if (enables.final_range) + { + used_budget_us += FinalRangeOverhead; + + // "Note that the final range timeout is determined by the timing + // budget and the sum of all other timeouts within the sequence. + // If there is no room for the final range timeout, then an error + // will be set. Otherwise the remaining time will be applied to + // the final range." + + if (used_budget_us > budget_us) + { + // "Requested timeout too big." + return false; + } + + uint32_t final_range_timeout_us = budget_us - used_budget_us; + + // set_sequence_step_timeout() begin + // (SequenceStepId == VL53L0X_SEQUENCESTEP_FINAL_RANGE) + + // "For the final range timeout, the pre-range timeout + // must be added. To do this both final and pre-range + // timeouts must be expressed in macro periods MClks + // because they have different vcsel periods." + + uint16_t final_range_timeout_mclks = + timeoutMicrosecondsToMclks(final_range_timeout_us, + timeouts.final_range_vcsel_period_pclks); + + if (enables.pre_range) + { + final_range_timeout_mclks += timeouts.pre_range_mclks; + } + + writeReg16Bit(FINAL_RANGE_CONFIG_TIMEOUT_MACROP_HI, + encodeTimeout(final_range_timeout_mclks)); + + // set_sequence_step_timeout() end + + measurement_timing_budget_us = budget_us; // store for internal reuse + } + return true; +} + +// Get the measurement timing budget in microseconds +// based on VL53L0X_get_measurement_timing_budget_micro_seconds() +// in us +uint32_t VL53L0X::getMeasurementTimingBudget(void) +{ + SequenceStepEnables enables; + SequenceStepTimeouts timeouts; + + uint16_t const StartOverhead = 1910; // note that this is different than the value in set_ + uint16_t const EndOverhead = 960; + uint16_t const MsrcOverhead = 660; + uint16_t const TccOverhead = 590; + uint16_t const DssOverhead = 690; + uint16_t const PreRangeOverhead = 660; + uint16_t const FinalRangeOverhead = 550; + + // "Start and end overhead times always present" + uint32_t budget_us = StartOverhead + EndOverhead; + + getSequenceStepEnables(&enables); + getSequenceStepTimeouts(&enables, &timeouts); + + if (enables.tcc) + { + budget_us += (timeouts.msrc_dss_tcc_us + TccOverhead); + } + + if (enables.dss) + { + budget_us += 2 * (timeouts.msrc_dss_tcc_us + DssOverhead); + } + else if (enables.msrc) + { + budget_us += (timeouts.msrc_dss_tcc_us + MsrcOverhead); + } + + if (enables.pre_range) + { + budget_us += (timeouts.pre_range_us + PreRangeOverhead); + } + + if (enables.final_range) + { + budget_us += (timeouts.final_range_us + FinalRangeOverhead); + } + + measurement_timing_budget_us = budget_us; // store for internal reuse + return budget_us; +} + +// Set the VCSEL (vertical cavity surface emitting laser) pulse period for the +// given period type (pre-range or final range) to the given value in PCLKs. +// Longer periods seem to increase the potential range of the sensor. +// Valid values are (even numbers only): +// pre: 12 to 18 (initialized default: 14) +// final: 8 to 14 (initialized default: 10) +// based on VL53L0X_set_vcsel_pulse_period() +bool VL53L0X::setVcselPulsePeriod(vcselPeriodType type, uint8_t period_pclks) +{ + uint8_t vcsel_period_reg = encodeVcselPeriod(period_pclks); + + SequenceStepEnables enables; + SequenceStepTimeouts timeouts; + + getSequenceStepEnables(&enables); + getSequenceStepTimeouts(&enables, &timeouts); + + // "Apply specific settings for the requested clock period" + // "Re-calculate and apply timeouts, in macro periods" + + // "When the VCSEL period for the pre or final range is changed, + // the corresponding timeout must be read from the device using + // the current VCSEL period, then the new VCSEL period can be + // applied. The timeout then must be written back to the device + // using the new VCSEL period. + // + // For the MSRC timeout, the same applies - this timeout being + // dependant on the pre-range vcsel period." + + + if (type == VcselPeriodPreRange) + { + // "Set phase check limits" + switch (period_pclks) + { + case 12: + writeReg(PRE_RANGE_CONFIG_VALID_PHASE_HIGH, 0x18); + break; + + case 14: + writeReg(PRE_RANGE_CONFIG_VALID_PHASE_HIGH, 0x30); + break; + + case 16: + writeReg(PRE_RANGE_CONFIG_VALID_PHASE_HIGH, 0x40); + break; + + case 18: + writeReg(PRE_RANGE_CONFIG_VALID_PHASE_HIGH, 0x50); + break; + + default: + // invalid period + return false; + } + writeReg(PRE_RANGE_CONFIG_VALID_PHASE_LOW, 0x08); + + // apply new VCSEL period + writeReg(PRE_RANGE_CONFIG_VCSEL_PERIOD, vcsel_period_reg); + + // update timeouts + + // set_sequence_step_timeout() begin + // (SequenceStepId == VL53L0X_SEQUENCESTEP_PRE_RANGE) + + uint16_t new_pre_range_timeout_mclks = + timeoutMicrosecondsToMclks(timeouts.pre_range_us, period_pclks); + + writeReg16Bit(PRE_RANGE_CONFIG_TIMEOUT_MACROP_HI, + encodeTimeout(new_pre_range_timeout_mclks)); + + // set_sequence_step_timeout() end + + // set_sequence_step_timeout() begin + // (SequenceStepId == VL53L0X_SEQUENCESTEP_MSRC) + + uint16_t new_msrc_timeout_mclks = + timeoutMicrosecondsToMclks(timeouts.msrc_dss_tcc_us, period_pclks); + + writeReg(MSRC_CONFIG_TIMEOUT_MACROP, + (new_msrc_timeout_mclks > 256) ? 255 : (new_msrc_timeout_mclks - 1)); + + // set_sequence_step_timeout() end + } + else if (type == VcselPeriodFinalRange) + { + switch (period_pclks) + { + case 8: + writeReg(FINAL_RANGE_CONFIG_VALID_PHASE_HIGH, 0x10); + writeReg(FINAL_RANGE_CONFIG_VALID_PHASE_LOW, 0x08); + writeReg(GLOBAL_CONFIG_VCSEL_WIDTH, 0x02); + writeReg(ALGO_PHASECAL_CONFIG_TIMEOUT, 0x0C); + writeReg(0xFF, 0x01); + writeReg(ALGO_PHASECAL_LIM, 0x30); + writeReg(0xFF, 0x00); + break; + + case 10: + writeReg(FINAL_RANGE_CONFIG_VALID_PHASE_HIGH, 0x28); + writeReg(FINAL_RANGE_CONFIG_VALID_PHASE_LOW, 0x08); + writeReg(GLOBAL_CONFIG_VCSEL_WIDTH, 0x03); + writeReg(ALGO_PHASECAL_CONFIG_TIMEOUT, 0x09); + writeReg(0xFF, 0x01); + writeReg(ALGO_PHASECAL_LIM, 0x20); + writeReg(0xFF, 0x00); + break; + + case 12: + writeReg(FINAL_RANGE_CONFIG_VALID_PHASE_HIGH, 0x38); + writeReg(FINAL_RANGE_CONFIG_VALID_PHASE_LOW, 0x08); + writeReg(GLOBAL_CONFIG_VCSEL_WIDTH, 0x03); + writeReg(ALGO_PHASECAL_CONFIG_TIMEOUT, 0x08); + writeReg(0xFF, 0x01); + writeReg(ALGO_PHASECAL_LIM, 0x20); + writeReg(0xFF, 0x00); + break; + + case 14: + writeReg(FINAL_RANGE_CONFIG_VALID_PHASE_HIGH, 0x48); + writeReg(FINAL_RANGE_CONFIG_VALID_PHASE_LOW, 0x08); + writeReg(GLOBAL_CONFIG_VCSEL_WIDTH, 0x03); + writeReg(ALGO_PHASECAL_CONFIG_TIMEOUT, 0x07); + writeReg(0xFF, 0x01); + writeReg(ALGO_PHASECAL_LIM, 0x20); + writeReg(0xFF, 0x00); + break; + + default: + // invalid period + return false; + } + + // apply new VCSEL period + writeReg(FINAL_RANGE_CONFIG_VCSEL_PERIOD, vcsel_period_reg); + + // update timeouts + + // set_sequence_step_timeout() begin + // (SequenceStepId == VL53L0X_SEQUENCESTEP_FINAL_RANGE) + + // "For the final range timeout, the pre-range timeout + // must be added. To do this both final and pre-range + // timeouts must be expressed in macro periods MClks + // because they have different vcsel periods." + + uint16_t new_final_range_timeout_mclks = + timeoutMicrosecondsToMclks(timeouts.final_range_us, period_pclks); + + if (enables.pre_range) + { + new_final_range_timeout_mclks += timeouts.pre_range_mclks; + } + + writeReg16Bit(FINAL_RANGE_CONFIG_TIMEOUT_MACROP_HI, + encodeTimeout(new_final_range_timeout_mclks)); + + // set_sequence_step_timeout end + } + else + { + // invalid type + return false; + } + + // "Finally, the timing budget must be re-applied" + + setMeasurementTimingBudget(measurement_timing_budget_us); + + // "Perform the phase calibration. This is needed after changing on vcsel period." + // VL53L0X_perform_phase_calibration() begin + + uint8_t sequence_config = readReg(SYSTEM_SEQUENCE_CONFIG); + writeReg(SYSTEM_SEQUENCE_CONFIG, 0x02); + performSingleRefCalibration(0x0); + writeReg(SYSTEM_SEQUENCE_CONFIG, sequence_config); + + // VL53L0X_perform_phase_calibration() end + + return true; +} + +// Get the VCSEL pulse period in PCLKs for the given period type. +// based on VL53L0X_get_vcsel_pulse_period() +uint8_t VL53L0X::getVcselPulsePeriod(vcselPeriodType type) +{ + if (type == VcselPeriodPreRange) + { + return decodeVcselPeriod(readReg(PRE_RANGE_CONFIG_VCSEL_PERIOD)); + } + else if (type == VcselPeriodFinalRange) + { + return decodeVcselPeriod(readReg(FINAL_RANGE_CONFIG_VCSEL_PERIOD)); + } + else { return 255; } +} + +// Start continuous ranging measurements. If period_ms (optional) is 0 or not +// given, continuous back-to-back mode is used (the sensor takes measurements as +// often as possible); otherwise, continuous timed mode is used, with the given +// inter-measurement period in milliseconds determining how often the sensor +// takes a measurement. +// based on VL53L0X_StartMeasurement() +void VL53L0X::startContinuous(uint32_t period_ms) +{ + writeReg(0x80, 0x01); + writeReg(0xFF, 0x01); + writeReg(0x00, 0x00); + writeReg(0x91, stop_variable); + writeReg(0x00, 0x01); + writeReg(0xFF, 0x00); + writeReg(0x80, 0x00); + + if (period_ms != 0) + { + // continuous timed mode + + // VL53L0X_SetInterMeasurementPeriodMilliSeconds() begin + + uint16_t osc_calibrate_val = readReg16Bit(OSC_CALIBRATE_VAL); + + if (osc_calibrate_val != 0) + { + period_ms *= osc_calibrate_val; + } + + writeReg32Bit(SYSTEM_INTERMEASUREMENT_PERIOD, period_ms); + + // VL53L0X_SetInterMeasurementPeriodMilliSeconds() end + + writeReg(SYSRANGE_START, 0x04); // VL53L0X_REG_SYSRANGE_MODE_TIMED + } + else + { + // continuous back-to-back mode + writeReg(SYSRANGE_START, 0x02); // VL53L0X_REG_SYSRANGE_MODE_BACKTOBACK + } +} + +// Stop continuous measurements +// based on VL53L0X_StopMeasurement() +void VL53L0X::stopContinuous(void) +{ + writeReg(SYSRANGE_START, 0x01); // VL53L0X_REG_SYSRANGE_MODE_SINGLESHOT + + writeReg(0xFF, 0x01); + writeReg(0x00, 0x00); + writeReg(0x91, 0x00); + writeReg(0x00, 0x01); + writeReg(0xFF, 0x00); +} + +// Returns a range reading in millimeters when continuous mode is active +// (readRangeSingleMillimeters() also calls this function after starting a +// single-shot range measurement) +uint16_t VL53L0X::readRangeContinuousMillimeters(void) +{ + startTimeout(); + while ((readReg(RESULT_INTERRUPT_STATUS) & 0x07) == 0) + { + if (checkTimeoutExpired()) + { + did_timeout = true; + return 65535; + } + } + + // assumptions: Linearity Corrective Gain is 1000 (default); + // fractional ranging is not enabled + uint16_t range = readReg16Bit(RESULT_RANGE_STATUS + 10); + + writeReg(SYSTEM_INTERRUPT_CLEAR, 0x01); + + return range; +} + +// Performs a single-shot range measurement and returns the reading in +// millimeters +// based on VL53L0X_PerformSingleRangingMeasurement() +uint16_t VL53L0X::readRangeSingleMillimeters(void) +{ + writeReg(0x80, 0x01); + writeReg(0xFF, 0x01); + writeReg(0x00, 0x00); + writeReg(0x91, stop_variable); + writeReg(0x00, 0x01); + writeReg(0xFF, 0x00); + writeReg(0x80, 0x00); + + writeReg(SYSRANGE_START, 0x01); + + // "Wait until start bit has been cleared" + startTimeout(); + while (readReg(SYSRANGE_START) & 0x01) + { + if (checkTimeoutExpired()) + { + did_timeout = true; + return 65535; + } + } + + return readRangeContinuousMillimeters(); +} + +// Did a timeout occur in one of the read functions since the last call to +// timeoutOccurred()? +bool VL53L0X::timeoutOccurred() +{ + bool tmp = did_timeout; + did_timeout = false; + return tmp; +} + +// Private Methods ///////////////////////////////////////////////////////////// + +// Get reference SPAD (single photon avalanche diode) count and type +// based on VL53L0X_get_info_from_device(), +// but only gets reference SPAD count and type +bool VL53L0X::getSpadInfo(uint8_t * count, bool * type_is_aperture) +{ + uint8_t tmp; + + writeReg(0x80, 0x01); + writeReg(0xFF, 0x01); + writeReg(0x00, 0x00); + + writeReg(0xFF, 0x06); + writeReg(0x83, readReg(0x83) | 0x04); + writeReg(0xFF, 0x07); + writeReg(0x81, 0x01); + + writeReg(0x80, 0x01); + + writeReg(0x94, 0x6b); + writeReg(0x83, 0x00); + startTimeout(); + while (readReg(0x83) == 0x00) + { + if (checkTimeoutExpired()) { return false; } + } + writeReg(0x83, 0x01); + tmp = readReg(0x92); + + *count = tmp & 0x7f; + *type_is_aperture = (tmp >> 7) & 0x01; + + writeReg(0x81, 0x00); + writeReg(0xFF, 0x06); + writeReg(0x83, readReg(0x83) & ~0x04); + writeReg(0xFF, 0x01); + writeReg(0x00, 0x01); + + writeReg(0xFF, 0x00); + writeReg(0x80, 0x00); + + return true; +} + +// Get sequence step enables +// based on VL53L0X_GetSequenceStepEnables() +void VL53L0X::getSequenceStepEnables(SequenceStepEnables * enables) +{ + uint8_t sequence_config = readReg(SYSTEM_SEQUENCE_CONFIG); + + enables->tcc = (sequence_config >> 4) & 0x1; + enables->dss = (sequence_config >> 3) & 0x1; + enables->msrc = (sequence_config >> 2) & 0x1; + enables->pre_range = (sequence_config >> 6) & 0x1; + enables->final_range = (sequence_config >> 7) & 0x1; +} + +// Get sequence step timeouts +// based on get_sequence_step_timeout(), +// but gets all timeouts instead of just the requested one, and also stores +// intermediate values +void VL53L0X::getSequenceStepTimeouts(SequenceStepEnables const * enables, SequenceStepTimeouts * timeouts) +{ + timeouts->pre_range_vcsel_period_pclks = getVcselPulsePeriod(VcselPeriodPreRange); + + timeouts->msrc_dss_tcc_mclks = readReg(MSRC_CONFIG_TIMEOUT_MACROP) + 1; + timeouts->msrc_dss_tcc_us = + timeoutMclksToMicroseconds(timeouts->msrc_dss_tcc_mclks, + timeouts->pre_range_vcsel_period_pclks); + + timeouts->pre_range_mclks = + decodeTimeout(readReg16Bit(PRE_RANGE_CONFIG_TIMEOUT_MACROP_HI)); + timeouts->pre_range_us = + timeoutMclksToMicroseconds(timeouts->pre_range_mclks, + timeouts->pre_range_vcsel_period_pclks); + + timeouts->final_range_vcsel_period_pclks = getVcselPulsePeriod(VcselPeriodFinalRange); + + timeouts->final_range_mclks = + decodeTimeout(readReg16Bit(FINAL_RANGE_CONFIG_TIMEOUT_MACROP_HI)); + + if (enables->pre_range) + { + timeouts->final_range_mclks -= timeouts->pre_range_mclks; + } + + timeouts->final_range_us = + timeoutMclksToMicroseconds(timeouts->final_range_mclks, + timeouts->final_range_vcsel_period_pclks); +} + +// Decode sequence step timeout in MCLKs from register value +// based on VL53L0X_decode_timeout() +// Note: the original function returned a uint32_t, but the return value is +// always stored in a uint16_t. +uint16_t VL53L0X::decodeTimeout(uint16_t reg_val) +{ + // format: "(LSByte * 2^MSByte) + 1" + return (uint16_t)((reg_val & 0x00FF) << + (uint16_t)((reg_val & 0xFF00) >> 8)) + 1; +} + +// Encode sequence step timeout register value from timeout in MCLKs +// based on VL53L0X_encode_timeout() +// Note: the original function took a uint16_t, but the argument passed to it +// is always a uint16_t. +uint16_t VL53L0X::encodeTimeout(uint16_t timeout_mclks) +{ + // format: "(LSByte * 2^MSByte) + 1" + + uint32_t ls_byte = 0; + uint16_t ms_byte = 0; + + if (timeout_mclks > 0) + { + ls_byte = timeout_mclks - 1; + + while ((ls_byte & 0xFFFFFF00) > 0) + { + ls_byte >>= 1; + ms_byte++; + } + + return (ms_byte << 8) | (ls_byte & 0xFF); + } + else { return 0; } +} + +// Convert sequence step timeout from MCLKs to microseconds with given VCSEL period in PCLKs +// based on VL53L0X_calc_timeout_us() +uint32_t VL53L0X::timeoutMclksToMicroseconds(uint16_t timeout_period_mclks, uint8_t vcsel_period_pclks) +{ + uint32_t macro_period_ns = calcMacroPeriod(vcsel_period_pclks); + + return ((timeout_period_mclks * macro_period_ns) + (macro_period_ns / 2)) / 1000; +} + +// Convert sequence step timeout from microseconds to MCLKs with given VCSEL period in PCLKs +// based on VL53L0X_calc_timeout_mclks() +uint32_t VL53L0X::timeoutMicrosecondsToMclks(uint32_t timeout_period_us, uint8_t vcsel_period_pclks) +{ + uint32_t macro_period_ns = calcMacroPeriod(vcsel_period_pclks); + + return (((timeout_period_us * 1000) + (macro_period_ns / 2)) / macro_period_ns); +} + + +// based on VL53L0X_perform_single_ref_calibration() +bool VL53L0X::performSingleRefCalibration(uint8_t vhv_init_byte) +{ + writeReg(SYSRANGE_START, 0x01 | vhv_init_byte); // VL53L0X_REG_SYSRANGE_MODE_START_STOP + + startTimeout(); + while ((readReg(RESULT_INTERRUPT_STATUS) & 0x07) == 0) + { + if (checkTimeoutExpired()) { return false; } + } + + writeReg(SYSTEM_INTERRUPT_CLEAR, 0x01); + + writeReg(SYSRANGE_START, 0x00); + + return true; +} diff --git a/lib/vl53l0x-arduino-1.02/VL53L0X.h b/lib/vl53l0x-arduino-1.02/VL53L0X.h new file mode 100755 index 000000000000..b531ff96a224 --- /dev/null +++ b/lib/vl53l0x-arduino-1.02/VL53L0X.h @@ -0,0 +1,176 @@ +#ifndef VL53L0X_h +#define VL53L0X_h + +#include + +class VL53L0X +{ + public: + // register addresses from API vl53l0x_device.h (ordered as listed there) + enum regAddr + { + SYSRANGE_START = 0x00, + + SYSTEM_THRESH_HIGH = 0x0C, + SYSTEM_THRESH_LOW = 0x0E, + + SYSTEM_SEQUENCE_CONFIG = 0x01, + SYSTEM_RANGE_CONFIG = 0x09, + SYSTEM_INTERMEASUREMENT_PERIOD = 0x04, + + SYSTEM_INTERRUPT_CONFIG_GPIO = 0x0A, + + GPIO_HV_MUX_ACTIVE_HIGH = 0x84, + + SYSTEM_INTERRUPT_CLEAR = 0x0B, + + RESULT_INTERRUPT_STATUS = 0x13, + RESULT_RANGE_STATUS = 0x14, + + RESULT_CORE_AMBIENT_WINDOW_EVENTS_RTN = 0xBC, + RESULT_CORE_RANGING_TOTAL_EVENTS_RTN = 0xC0, + RESULT_CORE_AMBIENT_WINDOW_EVENTS_REF = 0xD0, + RESULT_CORE_RANGING_TOTAL_EVENTS_REF = 0xD4, + RESULT_PEAK_SIGNAL_RATE_REF = 0xB6, + + ALGO_PART_TO_PART_RANGE_OFFSET_MM = 0x28, + + I2C_SLAVE_DEVICE_ADDRESS = 0x8A, + + MSRC_CONFIG_CONTROL = 0x60, + + PRE_RANGE_CONFIG_MIN_SNR = 0x27, + PRE_RANGE_CONFIG_VALID_PHASE_LOW = 0x56, + PRE_RANGE_CONFIG_VALID_PHASE_HIGH = 0x57, + PRE_RANGE_MIN_COUNT_RATE_RTN_LIMIT = 0x64, + + FINAL_RANGE_CONFIG_MIN_SNR = 0x67, + FINAL_RANGE_CONFIG_VALID_PHASE_LOW = 0x47, + FINAL_RANGE_CONFIG_VALID_PHASE_HIGH = 0x48, + FINAL_RANGE_CONFIG_MIN_COUNT_RATE_RTN_LIMIT = 0x44, + + PRE_RANGE_CONFIG_SIGMA_THRESH_HI = 0x61, + PRE_RANGE_CONFIG_SIGMA_THRESH_LO = 0x62, + + PRE_RANGE_CONFIG_VCSEL_PERIOD = 0x50, + PRE_RANGE_CONFIG_TIMEOUT_MACROP_HI = 0x51, + PRE_RANGE_CONFIG_TIMEOUT_MACROP_LO = 0x52, + + SYSTEM_HISTOGRAM_BIN = 0x81, + HISTOGRAM_CONFIG_INITIAL_PHASE_SELECT = 0x33, + HISTOGRAM_CONFIG_READOUT_CTRL = 0x55, + + FINAL_RANGE_CONFIG_VCSEL_PERIOD = 0x70, + FINAL_RANGE_CONFIG_TIMEOUT_MACROP_HI = 0x71, + FINAL_RANGE_CONFIG_TIMEOUT_MACROP_LO = 0x72, + CROSSTALK_COMPENSATION_PEAK_RATE_MCPS = 0x20, + + MSRC_CONFIG_TIMEOUT_MACROP = 0x46, + + SOFT_RESET_GO2_SOFT_RESET_N = 0xBF, + IDENTIFICATION_MODEL_ID = 0xC0, + IDENTIFICATION_REVISION_ID = 0xC2, + + OSC_CALIBRATE_VAL = 0xF8, + + GLOBAL_CONFIG_VCSEL_WIDTH = 0x32, + GLOBAL_CONFIG_SPAD_ENABLES_REF_0 = 0xB0, + GLOBAL_CONFIG_SPAD_ENABLES_REF_1 = 0xB1, + GLOBAL_CONFIG_SPAD_ENABLES_REF_2 = 0xB2, + GLOBAL_CONFIG_SPAD_ENABLES_REF_3 = 0xB3, + GLOBAL_CONFIG_SPAD_ENABLES_REF_4 = 0xB4, + GLOBAL_CONFIG_SPAD_ENABLES_REF_5 = 0xB5, + + GLOBAL_CONFIG_REF_EN_START_SELECT = 0xB6, + DYNAMIC_SPAD_NUM_REQUESTED_REF_SPAD = 0x4E, + DYNAMIC_SPAD_REF_EN_START_OFFSET = 0x4F, + POWER_MANAGEMENT_GO1_POWER_FORCE = 0x80, + + VHV_CONFIG_PAD_SCL_SDA__EXTSUP_HV = 0x89, + + ALGO_PHASECAL_LIM = 0x30, + ALGO_PHASECAL_CONFIG_TIMEOUT = 0x30, + }; + + enum vcselPeriodType { VcselPeriodPreRange, VcselPeriodFinalRange }; + + uint8_t last_status; // status of last I2C transmission + + VL53L0X(void); + + void setAddress(uint8_t new_addr); + inline uint8_t getAddress(void) { return address; } + + bool init(bool io_2v8 = true); + + void writeReg(uint8_t reg, uint8_t value); + void writeReg16Bit(uint8_t reg, uint16_t value); + void writeReg32Bit(uint8_t reg, uint32_t value); + uint8_t readReg(uint8_t reg); + uint16_t readReg16Bit(uint8_t reg); + uint32_t readReg32Bit(uint8_t reg); + + void writeMulti(uint8_t reg, uint8_t const * src, uint8_t count); + void readMulti(uint8_t reg, uint8_t * dst, uint8_t count); + + bool setSignalRateLimit(float limit_Mcps); + float getSignalRateLimit(void); + + bool setMeasurementTimingBudget(uint32_t budget_us); + uint32_t getMeasurementTimingBudget(void); + + bool setVcselPulsePeriod(vcselPeriodType type, uint8_t period_pclks); + uint8_t getVcselPulsePeriod(vcselPeriodType type); + + void startContinuous(uint32_t period_ms = 0); + void stopContinuous(void); + uint16_t readRangeContinuousMillimeters(void); + uint16_t readRangeSingleMillimeters(void); + + inline void setTimeout(uint16_t timeout) { io_timeout = timeout; } + inline uint16_t getTimeout(void) { return io_timeout; } + bool timeoutOccurred(void); + + private: + // TCC: Target CentreCheck + // MSRC: Minimum Signal Rate Check + // DSS: Dynamic Spad Selection + + struct SequenceStepEnables + { + boolean tcc, msrc, dss, pre_range, final_range; + }; + + struct SequenceStepTimeouts + { + uint16_t pre_range_vcsel_period_pclks, final_range_vcsel_period_pclks; + + uint16_t msrc_dss_tcc_mclks, pre_range_mclks, final_range_mclks; + uint32_t msrc_dss_tcc_us, pre_range_us, final_range_us; + }; + + uint8_t address; + uint16_t io_timeout; + bool did_timeout; + uint16_t timeout_start_ms; + + uint8_t stop_variable; // read by init and used when starting measurement; is StopVariable field of VL53L0X_DevData_t structure in API + uint32_t measurement_timing_budget_us; + + bool getSpadInfo(uint8_t * count, bool * type_is_aperture); + + void getSequenceStepEnables(SequenceStepEnables * enables); + void getSequenceStepTimeouts(SequenceStepEnables const * enables, SequenceStepTimeouts * timeouts); + + bool performSingleRefCalibration(uint8_t vhv_init_byte); + + static uint16_t decodeTimeout(uint16_t value); + static uint16_t encodeTimeout(uint16_t timeout_mclks); + static uint32_t timeoutMclksToMicroseconds(uint16_t timeout_period_mclks, uint8_t vcsel_period_pclks); + static uint32_t timeoutMicrosecondsToMclks(uint32_t timeout_period_us, uint8_t vcsel_period_pclks); +}; + +#endif + + + diff --git a/lib/vl53l0x-arduino-1.02/examples/Continuous/Continuous.ino b/lib/vl53l0x-arduino-1.02/examples/Continuous/Continuous.ino new file mode 100755 index 000000000000..33fcd05c48c7 --- /dev/null +++ b/lib/vl53l0x-arduino-1.02/examples/Continuous/Continuous.ino @@ -0,0 +1,33 @@ +/* This example shows how to use continuous mode to take +range measurements with the VL53L0X. It is based on +vl53l0x_ContinuousRanging_Example.c from the VL53L0X API. + +The range readings are in units of mm. */ + +#include +#include + +VL53L0X sensor; + +void setup() +{ + Serial.begin(9600); + Wire.begin(); + + sensor.init(); + sensor.setTimeout(500); + + // Start continuous back-to-back mode (take readings as + // fast as possible). To use continuous timed mode + // instead, provide a desired inter-measurement period in + // ms (e.g. sensor.startContinuous(100)). + sensor.startContinuous(); +} + +void loop() +{ + Serial.print(sensor.readRangeContinuousMillimeters()); + if (sensor.timeoutOccurred()) { Serial.print(" TIMEOUT"); } + + Serial.println(); +} diff --git a/lib/vl53l0x-arduino-1.02/examples/Single/Single.ino b/lib/vl53l0x-arduino-1.02/examples/Single/Single.ino new file mode 100755 index 000000000000..9f24463fc620 --- /dev/null +++ b/lib/vl53l0x-arduino-1.02/examples/Single/Single.ino @@ -0,0 +1,65 @@ +/* This example shows how to get single-shot range + measurements from the VL53L0X. The sensor can optionally be + configured with different ranging profiles, as described in + the VL53L0X API user manual, to get better performance for + a certain application. This code is based on the four + "SingleRanging" examples in the VL53L0X API. + + The range readings are in units of mm. */ + +#include +#include + +VL53L0X sensor; + + +// Uncomment this line to use long range mode. This +// increases the sensitivity of the sensor and extends its +// potential range, but increases the likelihood of getting +// an inaccurate reading because of reflections from objects +// other than the intended target. It works best in dark +// conditions. + +//#define LONG_RANGE + + +// Uncomment ONE of these two lines to get +// - higher speed at the cost of lower accuracy OR +// - higher accuracy at the cost of lower speed + +//#define HIGH_SPEED +//#define HIGH_ACCURACY + + +void setup() +{ + Serial.begin(9600); + Wire.begin(); + + sensor.init(); + sensor.setTimeout(500); + +#if defined LONG_RANGE + // lower the return signal rate limit (default is 0.25 MCPS) + sensor.setSignalRateLimit(0.1); + // increase laser pulse periods (defaults are 14 and 10 PCLKs) + sensor.setVcselPulsePeriod(VL53L0X::VcselPeriodPreRange, 18); + sensor.setVcselPulsePeriod(VL53L0X::VcselPeriodFinalRange, 14); +#endif + +#if defined HIGH_SPEED + // reduce timing budget to 20 ms (default is about 33 ms) + sensor.setMeasurementTimingBudget(20000); +#elif defined HIGH_ACCURACY + // increase timing budget to 200 ms + sensor.setMeasurementTimingBudget(200000); +#endif +} + +void loop() +{ + Serial.print(sensor.readRangeSingleMillimeters()); + if (sensor.timeoutOccurred()) { Serial.print(" TIMEOUT"); } + + Serial.println(); +} diff --git a/lib/vl53l0x-arduino-1.02/keywords.txt b/lib/vl53l0x-arduino-1.02/keywords.txt new file mode 100755 index 000000000000..437add908642 --- /dev/null +++ b/lib/vl53l0x-arduino-1.02/keywords.txt @@ -0,0 +1,90 @@ +VL53L0X KEYWORD1 + +setAddress KEYWORD2 +getAddress KEYWORD2 +init KEYWORD2 +writeReg KEYWORD2 +writeReg16Bit KEYWORD2 +writeReg32Bit KEYWORD2 +readReg KEYWORD2 +readReg16Bit KEYWORD2 +readReg32Bit KEYWORD2 +writeMulti KEYWORD2 +readMulti KEYWORD2 +setSignalRateLimit KEYWORD2 +getSignalRateLimit KEYWORD2 +setMeasurementTimingBudget KEYWORD2 +getMeasurementTimingBudget KEYWORD2 +setVcselPulsePeriod KEYWORD2 +getVcselPulsePeriod KEYWORD2 +startContinuous KEYWORD2 +stopContinuous KEYWORD2 +readRangeContinuousMillimeters KEYWORD2 +readRangeSingleMillimeters KEYWORD2 +setTimeout KEYWORD2 +getTimeout KEYWORD2 +timeoutOccurred KEYWORD2 + +SYSRANGE_START LITERAL1 +SYSTEM_THRESH_HIGH LITERAL1 +SYSTEM_THRESH_LOW LITERAL1 +SYSTEM_SEQUENCE_CONFIG LITERAL1 +SYSTEM_RANGE_CONFIG LITERAL1 +SYSTEM_INTERMEASUREMENT_PERIOD LITERAL1 +SYSTEM_INTERRUPT_CONFIG_GPIO LITERAL1 +GPIO_HV_MUX_ACTIVE_HIGH LITERAL1 +SYSTEM_INTERRUPT_CLEAR LITERAL1 +RESULT_INTERRUPT_STATUS LITERAL1 +RESULT_RANGE_STATUS LITERAL1 +RESULT_CORE_AMBIENT_WINDOW_EVENTS_RTN LITERAL1 +RESULT_CORE_RANGING_TOTAL_EVENTS_RTN LITERAL1 +RESULT_CORE_AMBIENT_WINDOW_EVENTS_REF LITERAL1 +RESULT_CORE_RANGING_TOTAL_EVENTS_REF LITERAL1 +RESULT_PEAK_SIGNAL_RATE_REF LITERAL1 +ALGO_PART_TO_PART_RANGE_OFFSET_MM LITERAL1 +I2C_SLAVE_DEVICE_ADDRESS LITERAL1 +MSRC_CONFIG_CONTROL LITERAL1 +PRE_RANGE_CONFIG_MIN_SNR LITERAL1 +PRE_RANGE_CONFIG_VALID_PHASE_LOW LITERAL1 +PRE_RANGE_CONFIG_VALID_PHASE_HIGH LITERAL1 +PRE_RANGE_MIN_COUNT_RATE_RTN_LIMIT LITERAL1 +FINAL_RANGE_CONFIG_MIN_SNR LITERAL1 +FINAL_RANGE_CONFIG_VALID_PHASE_LOW LITERAL1 +FINAL_RANGE_CONFIG_VALID_PHASE_HIGH LITERAL1 +FINAL_RANGE_CONFIG_MIN_COUNT_RATE_RTN_LIMIT LITERAL1 +PRE_RANGE_CONFIG_SIGMA_THRESH_HI LITERAL1 +PRE_RANGE_CONFIG_SIGMA_THRESH_LO LITERAL1 +PRE_RANGE_CONFIG_VCSEL_PERIOD LITERAL1 +PRE_RANGE_CONFIG_TIMEOUT_MACROP_HI LITERAL1 +PRE_RANGE_CONFIG_TIMEOUT_MACROP_LO LITERAL1 +SYSTEM_HISTOGRAM_BIN LITERAL1 +HISTOGRAM_CONFIG_INITIAL_PHASE_SELECT LITERAL1 +HISTOGRAM_CONFIG_READOUT_CTRL LITERAL1 +FINAL_RANGE_CONFIG_VCSEL_PERIOD LITERAL1 +FINAL_RANGE_CONFIG_TIMEOUT_MACROP_HI LITERAL1 +FINAL_RANGE_CONFIG_TIMEOUT_MACROP_LO LITERAL1 +CROSSTALK_COMPENSATION_PEAK_RATE_MCPS LITERAL1 +MSRC_CONFIG_TIMEOUT_MACROP LITERAL1 +SOFT_RESET_GO2_SOFT_RESET_N LITERAL1 +IDENTIFICATION_MODEL_ID LITERAL1 +IDENTIFICATION_REVISION_ID LITERAL1 +OSC_CALIBRATE_VAL LITERAL1 +GLOBAL_CONFIG_VCSEL_WIDTH LITERAL1 +GLOBAL_CONFIG_SPAD_ENABLES_REF_0 LITERAL1 +GLOBAL_CONFIG_SPAD_ENABLES_REF_1 LITERAL1 +GLOBAL_CONFIG_SPAD_ENABLES_REF_2 LITERAL1 +GLOBAL_CONFIG_SPAD_ENABLES_REF_3 LITERAL1 +GLOBAL_CONFIG_SPAD_ENABLES_REF_4 LITERAL1 +GLOBAL_CONFIG_SPAD_ENABLES_REF_5 LITERAL1 +GLOBAL_CONFIG_REF_EN_START_SELECT LITERAL1 +DYNAMIC_SPAD_NUM_REQUESTED_REF_SPAD LITERAL1 +DYNAMIC_SPAD_REF_EN_START_OFFSET LITERAL1 +POWER_MANAGEMENT_GO1_POWER_FORCE LITERAL1 +VHV_CONFIG_PAD_SCL_SDA__EXTSUP_HV LITERAL1 +ALGO_PHASECAL_LIM LITERAL1 +ALGO_PHASECAL_CONFIG_TIMEOUT LITERAL1 + +VcselPeriodPreRange LITERAL1 +VcselPeriodFinalRange LITERAL1 + + diff --git a/lib/vl53l0x-arduino-1.02/library.properties b/lib/vl53l0x-arduino-1.02/library.properties new file mode 100755 index 000000000000..43dd4d3a2d4f --- /dev/null +++ b/lib/vl53l0x-arduino-1.02/library.properties @@ -0,0 +1,9 @@ +name=VL53L0X +version=1.0.2 +author=Pololu +maintainer=Pololu +sentence=VL53L0X distance sensor library +paragraph=This is a library for the Arduino IDE that helps interface with ST's VL53L0X distance sensor. +category=Sensors +url=https://github.com/pololu/vl53l0x-arduino +architectures=* diff --git a/platformio.ini b/platformio.ini index 351d1ba02d84..a5c0946d14bb 100755 --- a/platformio.ini +++ b/platformio.ini @@ -67,7 +67,7 @@ build_flags = ${esp82xx_defaults.build_flags} [core_2_5_2] ; *** Esp8266 core for Arduino version 2.5.2 -platform = espressif8266@~2.2.0 +platform = espressif8266@~2.2.1 build_flags = ${esp82xx_defaults.build_flags} -Wl,-Teagle.flash.1m.ld ; Code optimization see https://github.com/esp8266/Arduino/issues/5790#issuecomment-475672473 diff --git a/scripter.md b/scripter.md index 5b9ac7840418..40ad3451e817 100644 --- a/scripter.md +++ b/scripter.md @@ -1,6 +1,6 @@ **Script Language for Tasmota** -As an alternative to rules. (about 14,2k flash size, variable ram size) +As an alternative to rules. (about 17k flash size, variable ram size) In submenu Configuration =\> edit script 1535 bytes max script size (uses rules buffer) @@ -39,7 +39,9 @@ numeric var=4 bytes, string var=lenght of string+1) **i:**vname specifies auto increment counters if >=0 (in seconds) **m:**vname specifies a median filter variable with 5 entries (for elimination of outliers) **M:**vname specifies a moving average filter variable with 8 entries (for smoothing data) -(max 5 filters in total m+M) +(max 5 filters in total m+M) optional another filter lenght (1..127) can be given after the definition. +filter vars can be accessed also in indexed mode vname[x] (index = 1...N, index 0 returns current array index pointer) +by this filter vars can be used as arrays >all variable names length taken together may not exceed 256 characters, so keep variable names as short as possible. memory is dynamically allocated as a result of the D section. @@ -84,7 +86,9 @@ special variables (read only): **pow(x y)** = calculates the power of x^y **med(n x)** = calculates a 5 value median filter of x (2 filters possible n=0,1) **int(x)** = gets the integer part of x (like floor) -**hn(x)** = converts x (0..255) zu a hex nibble string +**hn(x)** = converts x (0..255) to a hex nibble string +**st(svar c n)** = stringtoken gets the n th substring of svar separated by c +**s(x)** = explicit conversion from number x to string **mqtts** = state of mqtt disconnected=0, connected>0 **wifis** = state of wifi disconnected=0, connected>0 @@ -184,6 +188,24 @@ specifies a for next loop, (loop count must not be less then 1) **ends** specifies a switch case selector +**sd card support** +enable by CARD_CS = gpio pin of card chip select +\#define USE_SCRIPT_FATFS CARD_CS +sd card uses standard hardware spi gpios: mosi,miso,sclk +max 4 files open at a time +allows for e.g. logging sensors to a tab delimited file and then download (see example below) +script itself is also stored on sdcard with a default size of 4096 chars +requires additional 10k flash + +>**fr=fo("fname" m)** open file fname, mode 0=read, 1=write (returns file reference (0-3) or -1 for error) +**res=fw("text" fr)** writes text to (the end of) file fr, returns number of bytes written +**res=fr(svar fr)** reads a string into svar, returns bytes read (string is read until delimiter \t \n \r or eof) +**fc(fr)** close file +**fd("fname")** delete file fname +**flx(fname)** create download link for file (x=1 or 2) fname = file name of file to download +**fsm** return 1 if filesystem is mounted, (valid sd card found) + + **konsole script cmds** >**script 1 or 0** switch script on or off **script >cmdline** executes the script cmdline @@ -226,6 +248,8 @@ hour=0 state=1 m:med5=0 M:movav=0 +; define array with 10 entries +m:array=0 10 **\>B** @@ -258,15 +282,14 @@ delay(100) =>power 0 **\>T** - hum=BME280#Humidity temp=BME280#Temperature rssi=Wifi#RSSI string=SleepMode -; add to median filter +; add to median filter median=temp -; add to moving average filter +; add to moving average filter movav=hum ; show filtered results @@ -274,7 +297,7 @@ movav=hum if chg[rssi]>0 then =>print rssi changed to %rssi% -endif +endif if temp\>30 and hum\>70 @@ -286,6 +309,11 @@ endif ; every second but not completely reliable time here ; use upsecs and uptime or best t: for reliable timers +; arrays +array[1]=4 +array[2]=5 +tmp=array[1]+array[2] + ; call subrountines with parameters =#sub1("hallo") =#sub2(999) @@ -427,6 +455,8 @@ ends **\>E** =\>print event executed! +; get HSBColor 1. component +tmp=st(HSBColor , 1) ; check if switch changed state sw=sw[1] @@ -462,11 +492,54 @@ col=hn(255)+hn(0)+hn(0) **\>R** =\>print restarting now +**a log sensor example** +; define all vars here +; reserve large strings +**\>D** 48 +hum=0 +temp=0 +fr=0 +res=0 +; moving average for 60 seconds +M:mhum=0 60 +M:mtemp=0 60 +str="" + +**\>B** +; open file for write +fr=fo("slog.txt" 1) +; set sensor file download link +fl1("slog.txt") + +**\>T** +; get sensor values +temp=BME280#Temperature +hum=BME280#Humidity + +**\>S** +; average sensor values every second +mhum=hum +mtemp=temp + +; write average to sensor log every minute +if upsecs%60==0 +then +; compose string for tab delimited file entry +str=s(upsecs)+"\t"+s(mhum)+"\t"+s(mtemp)+"\n" +; write string to log file +res=fw(str fr) +endif + +**\>R** +; close file +fc(fr) + + **a real example** epaper 29 with sgp30 and bme280 some vars are set from iobroker DisplayText substituted to save script space -\>D +**\>D** hum=0 temp=0 press=0 @@ -485,13 +558,13 @@ DT="DisplayText" punit="hPa" tunit="C" -\>B +**\>B** ;reset auto draw =>%DT% [zD0] ;clr display and draw a frame =>%DT% [x0y20h296x0y40h296] -\>T +**\>T** ; get tele vars temp=BME280#Temperature hum=BME280#Humidity @@ -502,7 +575,7 @@ ahum=SGP30#aHumidity tunit=TempUnit punit=PressureUnit -\>S +**\>S** // update display every teleperiod time if upsecs%tper==0 then @@ -525,9 +598,9 @@ dprec0 endif -\>E +**\>E** -\>R +**\>R** **another real example** ILI 9488 color LCD Display shows various energy graphs diff --git a/sonoff/_changelog.ino b/sonoff/_changelog.ino index 450ef78592ed..6bed2c3b55fa 100644 --- a/sonoff/_changelog.ino +++ b/sonoff/_changelog.ino @@ -1,9 +1,19 @@ -/* 6.5.0.12 20190521 +/* 6.5.0.13 20190527 + * Add command SetOption38 6..255 to set IRReceive protocol detection sensitivity mimizing UNKNOWN protocols (#5853) + * Fix missing white channel for WS2812 (#5869) + * Add reset of Energy values when connection to sensor is lost for over 4 seconds (#5874, #5881) + * Work-around for Philips Hue emulation issue by using part of MAC address for LightId (#5849) + * Add support to Stage Arduino Core (next 2.6.0) + * + * 6.5.0.12 20190521 * Add AriLux RF control GPIO option "ALux IrSel" (159) replacing "Led4i" (59) for full LED control (#5709) * Add LED GPIO option "LedLink" (157) and "LedLinki" (158) to select dedicated link status LED (#5709) * Add support for up to four LEDs related to four power outputs. Enabled when "LedLink(i)" is configured too (#5709) * Add extended LED power control using command LedPowerX where X is 1 to 4. Enabled when "LedLink(i)" is configured too (#5709) * Fix core 2.5.x ISR not in IRAM exception (#5837) + * Add support for VL53L0x time of flight sensor. Might interfere with TSL2561 using same I2C address (#5845) + * Add command AdcParam to control ADC0 Temperature and Light formula parameters + * Change default PowerDelta from 80% to 0% on new installations (#5858, #5028, #4813, #4130, #4145, #3795, #3778, #3660, #3648) * * 6.5.0.11 20190517 * Add command SetOption64 0/1 to switch between "-" or "_" as sensor index separator impacting DS18X20, DHT, BMP and SHT3X sensor names (#5689) diff --git a/sonoff/my_user_config.h b/sonoff/my_user_config.h index 6786f755bac6..2dce8ed845b4 100644 --- a/sonoff/my_user_config.h +++ b/sonoff/my_user_config.h @@ -279,7 +279,7 @@ #define USE_EMULATION_WEMO // Enable Belkin WeMo emulation for Alexa (+6k code, +2k mem common) // -- mDNS ---------------------------------------- -#define USE_DISCOVERY // Enable mDNS for the following services (+8k code, +0.3k mem) +#define USE_DISCOVERY // Enable mDNS for the following services (+8k code or +23.5k code with core 2_5_x, +0.3k mem) #define WEBSERVER_ADVERTISE // Provide access to webserver by name .local/ #define MQTT_HOST_DISCOVERY // Find MQTT host server (overrides MQTT_HOST if found) @@ -292,7 +292,8 @@ // -- Rules or Script ---------------------------- // Select none or only one of the below defines #define USE_RULES // Add support for rules (+8k code) -//#define USE_SCRIPT // Add support for script (+15k code) +//#define USE_SCRIPT // Add support for script (+17k code) + #define USE_SCRIPT_FATFS 4 // #define USE_EXPRESSION // Add support for expression evaluation in rules (+3k2 code, +64 bytes mem) // #define SUPPORT_MQTT_EVENT // Support trigger event with MQTT subscriptions (+3k5 code) @@ -348,6 +349,7 @@ // #define USE_SCD30 // Enable Sensiron SCd30 CO2 sensor (I2C address 0x61) (+3k3 code) #define USE_SPS30 // Enable Sensiron SPS30 particle sensor (I2C address 0x69) (+1.7 code) #define USE_ADE7953 // Enable ADE7953 Energy monitor as used on Shelly 2.5 (I2C address 0x38) (+1k5) + #define USE_VL53L0X // Enable VL53L0x time of flight sensor (I2C address 0x29) (+4k code) // #define USE_DISPLAY // Add I2C Display Support (+2k code) #define USE_DISPLAY_MODES1TO5 // Enable display mode 1 to 5 in addition to mode 0 @@ -416,7 +418,7 @@ #define USE_IR_RECEIVE // Support for IR receiver (+7k2 code, 264 iram) #define IR_RCV_BUFFER_SIZE 100 // Max number of packets allowed in capture buffer (default 100 (*2 bytes ram)) #define IR_RCV_TIMEOUT 15 // Number of milli-Seconds of no-more-data before we consider a message ended (default 15) - #define IR_RCV_MIN_UNKNOWN_SIZE 6 // Set the smallest sized "UNKNOWN" message packets we actually care about (default 6) + #define IR_RCV_MIN_UNKNOWN_SIZE 6 // Set the smallest sized "UNKNOWN" message packets we actually care about (default 6, max 255) #define USE_WS2812 // WS2812 Led string using library NeoPixelBus (+5k code, +1k mem, 232 iram) - Disable by // #define USE_WS2812_CTYPE NEO_GRB // WS2812 Color type (NEO_RGB, NEO_GRB, NEO_BRG, NEO_RBG, NEO_RGBW, NEO_GRBW) diff --git a/sonoff/settings.h b/sonoff/settings.h index 03529e8faa13..5f635011615c 100644 --- a/sonoff/settings.h +++ b/sonoff/settings.h @@ -210,8 +210,9 @@ struct SYSCFG { uint8_t webserver; // 1AB uint8_t weblog_level; // 1AC uint8_t mqtt_fingerprint[2][20]; // 1AD + uint8_t adc_param_type; // 1D5 - uint8_t free_1D5[19]; // 1D5 Free since 5.12.0e + uint8_t free_1D6[18]; // 1D6 Free since 5.12.0e uint8_t sps30_inuse_hours; // 1E8 char mqtt_host[33]; // 1E9 - Keep together with below as being copied as one chunck with reset 6 @@ -336,7 +337,10 @@ struct SYSCFG { uint8_t free_774[32]; // 774 - uint32_t drivers[3]; // 794 +// uint32_t drivers[3]; // 794 - 6.5.0.12 replaced by below three entries + uint32_t adc_param1; // 794 + uint32_t adc_param2; // 798 + int adc_param3; // 79C uint32_t monitors; // 7A0 uint32_t sensors[3]; // 7A4 uint32_t displays; // 7B0 diff --git a/sonoff/settings.ino b/sonoff/settings.ino index a8445b232c3c..f2bf1290e7aa 100644 --- a/sonoff/settings.ino +++ b/sonoff/settings.ino @@ -235,11 +235,20 @@ extern "C" { } #include "eboot_command.h" -extern "C" uint32_t _SPIFFS_end; +#if defined(ARDUINO_ESP8266_RELEASE_2_3_0) || defined(ARDUINO_ESP8266_RELEASE_2_4_0) || defined(ARDUINO_ESP8266_RELEASE_2_4_1) || defined(ARDUINO_ESP8266_RELEASE_2_4_2) || defined(ARDUINO_ESP8266_RELEASE_2_5_0) || defined(ARDUINO_ESP8266_RELEASE_2_5_1) || defined(ARDUINO_ESP8266_RELEASE_2_5_2) +extern "C" uint32_t _SPIFFS_end; // From libraries/EEPROM/EEPROM.cpp EEPROMClass const uint32_t SPIFFS_END = ((uint32_t)&_SPIFFS_end - 0x40200000) / SPI_FLASH_SEC_SIZE; +#else // Core > 2.5.2 and STAGE + +extern "C" uint32_t _FS_end; +// From libraries/EEPROM/EEPROM.cpp EEPROMClass +const uint32_t SPIFFS_END = ((uint32_t)&_FS_end - 0x40200000) / SPI_FLASH_SEC_SIZE; + +#endif + // Version 4.2 config = eeprom area const uint32_t SETTINGS_LOCATION = SPIFFS_END; // No need for SPIFFS as it uses EEPROM area // Version 5.2 allow for more flash space @@ -789,7 +798,7 @@ void SettingsDefaultSet2(void) // Settings.flag2.wattage_resolution = 0; Settings.flag2.energy_resolution = ENERGY_RESOLUTION; Settings.param[P_MAX_POWER_RETRY] = MAX_POWER_RETRY; - Settings.energy_power_delta = DEFAULT_POWER_DELTA; +// Settings.energy_power_delta = 0; Settings.energy_power_calibration = HLW_PREF_PULSE; Settings.energy_voltage_calibration = HLW_UREF_PULSE; Settings.energy_current_calibration = HLW_IREF_PULSE; @@ -813,6 +822,9 @@ void SettingsDefaultSet2(void) // Settings.energy_kWhtotal = 0; RtcSettings.energy_kWhtotal = 0; + // IRRemote + Settings.param[P_IR_UNKNOW_THRESHOLD] = IR_RCV_MIN_UNKNOWN_SIZE; + // RF Bridge // for (uint8_t i = 0; i < 17; i++) { Settings.rf_code[i][0] = 0; } memcpy_P(Settings.rf_code[0], kDefaultRfCode, 9); @@ -908,7 +920,7 @@ void SettingsDefaultSet2(void) SettingsDefaultWebColor(); - memset(&Settings.drivers, 0xFF, 32); // Enable all possible monitors, displays, drivers and sensors + memset(&Settings.monitors, 0xFF, 20); // Enable all possible monitors, displays and sensors } /********************************************************************************************/ @@ -1047,7 +1059,7 @@ void SettingsDelta(void) } if (Settings.version < 0x050C0005) { Settings.light_rotation = 0; - Settings.energy_power_delta = DEFAULT_POWER_DELTA; + Settings.energy_power_delta = 0; char fingerprint[60]; memcpy(fingerprint, Settings.mqtt_fingerprint, sizeof(fingerprint)); char *p = fingerprint; @@ -1129,7 +1141,7 @@ void SettingsDelta(void) Settings.timezone_minutes = 0; } if (Settings.version < 0x06030004) { - memset(&Settings.drivers, 0xFF, 32); // Enable all possible monitors, displays, drivers and sensors + memset(&Settings.monitors, 0xFF, 20); // Enable all possible monitors, displays and sensors } if (Settings.version < 0x0603000E) { Settings.flag2.calc_resolution = CALC_RESOLUTION; @@ -1168,6 +1180,9 @@ void SettingsDelta(void) if (Settings.version < 0x0605000A) { Settings.my_adc0 = ADC0_NONE; } + if (Settings.version < 0x0605000D) { + Settings.param[P_IR_UNKNOW_THRESHOLD] = IR_RCV_MIN_UNKNOWN_SIZE; + } Settings.version = VERSION; SettingsSave(1); diff --git a/sonoff/sonoff.h b/sonoff/sonoff.h index 9b3ed00206fe..d7d4ef9050e1 100644 --- a/sonoff/sonoff.h +++ b/sonoff/sonoff.h @@ -99,7 +99,6 @@ const uint16_t PWM_MIN = 100; // [PWM_MIN] Minimum frequency - Def // For Controlling Servos use 50 and also set PWM_FREQ as 50 (DO NOT USE THESE VALUES FOR DIMMERS) //#define PWM_LIGHTSCHEME0_IGNORE_SLEEP // Do not change sleep value for LightAnimate() scheme 0 -const uint8_t DEFAULT_POWER_DELTA = 80; // Power change percentage const uint16_t MAX_POWER_HOLD = 10; // Time in SECONDS to allow max agreed power const uint16_t MAX_POWER_WINDOW = 30; // Time in SECONDS to disable allow max agreed power const uint16_t SAFE_POWER_HOLD = 10; // Time in SECONDS to allow max unit safe power @@ -237,7 +236,7 @@ enum ButtonStates { PRESSED, NOT_PRESSED }; enum Shortcuts { SC_CLEAR, SC_DEFAULT, SC_USER }; -enum SettingsParmaIndex {P_HOLD_TIME, P_MAX_POWER_RETRY, P_TUYA_DIMMER_ID, P_MDNS_DELAYED_START, P_BOOT_LOOP_OFFSET, P_RGB_REMAP, P_MAX_PARAM8}; // Max is PARAM8_SIZE (18) - SetOption32 until SetOption49 +enum SettingsParmaIndex {P_HOLD_TIME, P_MAX_POWER_RETRY, P_TUYA_DIMMER_ID, P_MDNS_DELAYED_START, P_BOOT_LOOP_OFFSET, P_RGB_REMAP, P_IR_UNKNOW_THRESHOLD, P_MAX_PARAM8}; // Max is PARAM8_SIZE (18) - SetOption32 until SetOption49 enum DomoticzSensors {DZ_TEMP, DZ_TEMP_HUM, DZ_TEMP_HUM_BARO, DZ_POWER_ENERGY, DZ_ILLUMINANCE, DZ_COUNT, DZ_VOLTAGE, DZ_CURRENT, DZ_AIRQUALITY, DZ_MAX_SENSORS}; diff --git a/sonoff/sonoff.ino b/sonoff/sonoff.ino index ef9845c81262..38f28df0e2c0 100755 --- a/sonoff/sonoff.ino +++ b/sonoff/sonoff.ino @@ -125,7 +125,7 @@ int blinks = 201; // Number of LED blinks uint32_t uptime = 0; // Counting every second until 4294967295 = 130 year uint32_t loop_load_avg = 0; // Indicative loop load average uint32_t global_update = 0; // Timestamp of last global temperature and humidity update -float global_temperature = 0; // Provide a global temperature to be used by some sensors +float global_temperature = 9999; // Provide a global temperature to be used by some sensors float global_humidity = 0; // Provide a global humidity to be used by some sensors float global_pressure = 0; // Provide a global pressure to be used by some sensors char *ota_url; // OTA url string pointer @@ -851,6 +851,11 @@ void MqttDataHandler(char* topic, uint8_t* data, unsigned int data_len) case P_RGB_REMAP: LightUpdateColorMapping(); break; +#if defined(USE_IR_REMOTE) && defined(USE_IR_RECEIVE) + case P_IR_UNKNOW_THRESHOLD: + IrReceiveUpdateThreshold(); + break; +#endif } } } diff --git a/sonoff/sonoff_version.h b/sonoff/sonoff_version.h index e06fab8d0e3d..8b7c27bfcb90 100644 --- a/sonoff/sonoff_version.h +++ b/sonoff/sonoff_version.h @@ -20,6 +20,6 @@ #ifndef _SONOFF_VERSION_H_ #define _SONOFF_VERSION_H_ -const uint32_t VERSION = 0x0605000C; +const uint32_t VERSION = 0x0605000D; #endif // _SONOFF_VERSION_H_ diff --git a/sonoff/support.ino b/sonoff/support.ino index da79be0e02b4..f3faa1798426 100644 --- a/sonoff/support.ino +++ b/sonoff/support.ino @@ -644,7 +644,7 @@ void ResetGlobalValues(void) { if ((uptime - global_update) > GLOBAL_VALUES_VALID) { // Reset after 5 minutes global_update = 0; - global_temperature = 0; + global_temperature = 9999; global_humidity = 0; global_pressure = 0; } diff --git a/sonoff/support_rotary.ino b/sonoff/support_rotary.ino index 35974251b572..64a7bc738b85 100644 --- a/sonoff/support_rotary.ino +++ b/sonoff/support_rotary.ino @@ -62,7 +62,7 @@ void update_position(void) } #ifndef ARDUINO_ESP8266_RELEASE_2_3_0 // Fix core 2.5.x ISR not in IRAM Exception -void update_rotary(void) ICACHE_RAM_ATTR; // As iram is tight and it works this way too +void update_rotary(void) ICACHE_RAM_ATTR; #endif // ARDUINO_ESP8266_RELEASE_2_3_0 void update_rotary(void) diff --git a/sonoff/xdrv_01_webserver.ino b/sonoff/xdrv_01_webserver.ino index fc5ffa467236..28250adf116d 100644 --- a/sonoff/xdrv_01_webserver.ino +++ b/sonoff/xdrv_01_webserver.ino @@ -480,7 +480,8 @@ void StartWebserver(int type, IPAddress ipweb) WebServer->on("/u1", HandleUpgradeFirmwareStart); // OTA WebServer->on("/u2", HTTP_POST, HandleUploadDone, HandleUploadLoop); WebServer->on("/u2", HTTP_OPTIONS, HandlePreflightRequest); - WebServer->on("/cs", HandleConsole); + WebServer->on("/cs", HTTP_GET, HandleConsole); + WebServer->on("/cs", HTTP_OPTIONS, HandlePreflightRequest); WebServer->on("/cm", HandleHttpCommand); #ifndef FIRMWARE_MINIMAL WebServer->on("/cn", HandleConfiguration); @@ -1293,17 +1294,28 @@ void ModuleSaveSettings(void) /*-------------------------------------------------------------------------------------------*/ -String htmlEscape(String s) -{ - s.replace("&", "&"); - s.replace("<", "<"); - s.replace(">", ">"); - s.replace("\"", """); - s.replace("'", "'"); - s.replace("/", "/"); - return s; +const char kUnescapeCode[] = "&><\"\'"; +const char kEscapeCode[] PROGMEM = "&|>|<|"|'"; + +String HtmlEscape(const String unescaped) { + char escaped[10]; + uint16_t ulen = unescaped.length(); + String result = ""; + for (size_t i = 0; i < ulen; i++) { + char c = unescaped[i]; + char *p = strchr(kUnescapeCode, c); + if (p != nullptr) { + result += GetTextIndexed(escaped, sizeof(escaped), p - kUnescapeCode, kEscapeCode); + } else { + result += c; + } + } + return result; } +// Indexed by enum wl_enc_type in file wl_definitions.h starting from -1 +const char kEncryptionType[] PROGMEM = "|||" D_WPA_PSK "||" D_WPA2_PSK "|" D_WEP "||" D_NONE "|" D_AUTO; + void HandleWifiConfiguration(void) { if (!HttpCheckPriviledgedAccess(!WifiIsInManagerMode())) { return; } @@ -1370,11 +1382,12 @@ void HandleWifiConfiguration(void) int quality = WifiGetRssiAsQuality(WiFi.RSSI(indices[i])); if (minimum_signal_quality == -1 || minimum_signal_quality < quality) { - uint8_t auth = WiFi.encryptionType(indices[i]); + int auth = WiFi.encryptionType(indices[i]); + char encryption[20]; WSContentSend_P(PSTR("
%s (%d) %s %d%%
"), - htmlEscape(WiFi.SSID(indices[i])).c_str(), + HtmlEscape(WiFi.SSID(indices[i])).c_str(), WiFi.channel(indices[i]), - (ENC_TYPE_WEP == auth) ? D_WEP : (ENC_TYPE_TKIP == auth) ? D_WPA_PSK : (ENC_TYPE_CCMP == auth) ? D_WPA2_PSK : (ENC_TYPE_AUTO == auth) ? D_AUTO : "", + GetTextIndexed(encryption, sizeof(encryption), auth +1, kEncryptionType), quality ); delay(0); diff --git a/sonoff/xdrv_03_energy.ino b/sonoff/xdrv_03_energy.ino index 2793c36633da..52585a632b80 100644 --- a/sonoff/xdrv_03_energy.ino +++ b/sonoff/xdrv_03_energy.ino @@ -28,6 +28,7 @@ #define ENERGY_NONE 0 #define ENERGY_OVERTEMP 73.0 // Industry standard lowest overtemp in Celsius +#define ENERGY_WATCHDOG 4 // Allow up to 4 seconds before deciding no valid data present #define FEATURE_POWER_LIMIT true @@ -71,6 +72,7 @@ unsigned long energy_period = 0; // 12312312 Wh * 10^-2 (deca milli Watt hour float energy_power_last[3] = { 0 }; uint8_t energy_power_delta = 0; +uint8_t energy_data_valid = 0; bool energy_voltage_available = true; // Enable if voltage is measured bool energy_current_available = true; // Enable if current is measured @@ -323,10 +325,22 @@ void EnergyMqttShow(void) void EnergyOverTempCheck() { if (global_update) { - if (power && (global_temperature > ENERGY_OVERTEMP)) { // Device overtemp, turn off relays + if (power && (global_temperature != 9999) && (global_temperature > ENERGY_OVERTEMP)) { // Device overtemp, turn off relays SetAllPower(POWER_ALL_OFF, SRC_OVERTEMP); } } + if (energy_data_valid <= ENERGY_WATCHDOG) { + energy_data_valid++; + if (energy_data_valid > ENERGY_WATCHDOG) { + // Reset energy registers + energy_voltage = 0; + energy_current = 0; + energy_active_power = 0; + if (!isnan(energy_frequency)) { energy_frequency = 0; } + if (!isnan(energy_power_factor)) { energy_power_factor = 0; } + energy_start = 0; + } + } } /*********************************************************************************************\ @@ -349,7 +363,7 @@ bool EnergyCommand(void) } else if (CMND_POWERDELTA == command_code) { if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 101)) { - Settings.energy_power_delta = (1 == XdrvMailbox.payload) ? DEFAULT_POWER_DELTA : XdrvMailbox.payload; + Settings.energy_power_delta = XdrvMailbox.payload; } nvalue = Settings.energy_power_delta; unit = UNIT_PERCENTAGE; diff --git a/sonoff/xdrv_04_light.ino b/sonoff/xdrv_04_light.ino index 17a0806bb249..b737fcc1dfd4 100644 --- a/sonoff/xdrv_04_light.ino +++ b/sonoff/xdrv_04_light.ino @@ -1326,6 +1326,12 @@ void LightInit(void) light_device = devices_present; light_subtype = (light_type & 7) > LST_MAX ? LST_MAX : (light_type & 7); // Always 0 - LST_MAX (5) +#if defined(USE_WS2812) && (USE_WS2812_CTYPE > NEO_3LED) + if (LT_WS2812 == light_type) { + light_subtype++; // from RGB to RGBW + } +#endif + light_controller.setSubType(light_subtype); light_controller.loadSettings(); @@ -1362,9 +1368,6 @@ void LightInit(void) } #ifdef USE_WS2812 // ************************************************************************ else if (LT_WS2812 == light_type) { -#if (USE_WS2812_CTYPE > NEO_3LED) - light_subtype++; // from RGB to RGBW -#endif Ws2812Init(); max_scheme = LS_MAX + WS2812_SCHEMES; } diff --git a/sonoff/xdrv_05_irremote.ino b/sonoff/xdrv_05_irremote.ino index 9f1c33a10f3e..9bd0694e3b7d 100644 --- a/sonoff/xdrv_05_irremote.ino +++ b/sonoff/xdrv_05_irremote.ino @@ -116,11 +116,17 @@ IRrecv *irrecv = nullptr; unsigned long ir_lasttime = 0; +void IrReceiveUpdateThreshold() +{ + if (Settings.param[P_IR_UNKNOW_THRESHOLD] < 6) { Settings.param[P_IR_UNKNOW_THRESHOLD] = 6; } + irrecv->setUnknownThreshold(Settings.param[P_IR_UNKNOW_THRESHOLD]); +} + void IrReceiveInit(void) { // an IR led is at GPIO_IRRECV irrecv = new IRrecv(pin[GPIO_IRRECV], IR_RCV_BUFFER_SIZE, IR_RCV_TIMEOUT, IR_RCV_SAVE_BUFFER); - irrecv->setUnknownThreshold(IR_RCV_MIN_UNKNOWN_SIZE); + irrecv->setUnknownThreshold(Settings.param[P_IR_UNKNOW_THRESHOLD]); irrecv->enableIRIn(); // Start the receiver // AddLog_P(LOG_LEVEL_DEBUG, PSTR("IrReceive initialized")); diff --git a/sonoff/xdrv_10_scripter.ino b/sonoff/xdrv_10_scripter.ino index 9edd27a9f8cc..3b21603de586 100644 --- a/sonoff/xdrv_10_scripter.ino +++ b/sonoff/xdrv_10_scripter.ino @@ -20,17 +20,14 @@ #ifdef USE_SCRIPT #ifndef USE_RULES /*********************************************************************************************\ - for documentation see up to date docs in file SCRIPTER.md - -uses about 14,2 k of flash -more stack could be needed for sendmail => -D CONT_STACKSIZE=4800 = +0.8k stack -0.8k heap - +uses about 17 k of flash to do optimize code for space remarks + goal is fast execution time, minimal use of ram and intuitive syntax therefore => case sensitive cmds and vars (lowercase uses time and code) @@ -38,6 +35,14 @@ no math hierarchy (costs ram and execution time, better group with brackets, an (will probably make math hierarchy an ifdefed option) keywords if then else endif, or, and are better readable for beginners (others may use {}) +changelog after merging to Tasmota +1 show remaining chars in webui, +2 now can expand script space to 2048 chars by setting MAX_RULE_SETS to 4 +3 at24256 eeprom support #ifdef defaults to 4095 bytes script size (reduces heap by this amount) +4 some housekeeping +5 sd card support #ifdef allows eg for sensor logging +6 download link for sdcard files + \*********************************************************************************************/ #define XDRV_10 10 @@ -53,11 +58,18 @@ keywords if then else endif, or, and are better readable for beginners (others m #define SCRIPT_EOL '\n' #define SCRIPT_FLOAT_PRECISION 2 #define SCRIPT_MAXPERM (MAX_RULE_MEMS*10)-4/sizeof(float) - +#define MAX_SCRIPT_SIZE MAX_RULE_SIZE*MAX_RULE_SETS enum {OPER_EQU=1,OPER_PLS,OPER_MIN,OPER_MUL,OPER_DIV,OPER_PLSEQU,OPER_MINEQU,OPER_MULEQU,OPER_DIVEQU,OPER_EQUEQU,OPER_NOTEQU,OPER_GRTEQU,OPER_LOWEQU,OPER_GRT,OPER_LOW,OPER_PERC,OPER_XOR,OPER_AND,OPER_OR,OPER_ANDEQU,OPER_OREQU,OPER_XOREQU,OPER_PERCEQU}; enum {SCRIPT_LOGLEVEL=1,SCRIPT_TELEPERIOD}; +#ifdef USE_SCRIPT_FATFS +#include +#include +#define FAT_SCRIPT_SIZE 4096 +#define FAT_SCRIPT_NAME "script.txt" +#endif + typedef union { uint8_t data; struct { @@ -84,6 +96,7 @@ struct M_FILT { float rbuff[1]; }; +#define SFS_MAX 4 // global memory struct SCRIPT_MEM { float *fvars; // number var pointer @@ -94,6 +107,10 @@ struct SCRIPT_MEM { uint8_t *vnp_offset; char *glob_snp; // string vars pointer char *scriptptr; + char *script_ram; + uint16_t script_size; + uint8_t *script_pram; + uint16_t script_pram_size; uint8_t numvars; void *script_mem; uint16_t script_mem_size; @@ -102,13 +119,26 @@ struct SCRIPT_MEM { uint8_t glob_error; uint8_t max_ssize; uint8_t script_loglevel; + uint8_t flags; +#ifdef USE_SCRIPT_FATFS + File files[SFS_MAX]; + uint8_t file_flags[SFS_MAX]; + uint8_t script_sd_found; + char flink[2][14]; +#endif } glob_script_mem; + +int16_t last_findex; uint8_t tasm_cmd_activ=0; uint32_t script_lastmillis; + char *GetNumericResult(char *lp,uint8_t lastop,float *fp,JsonObject *jo); +char *GetStringResult(char *lp,uint8_t lastop,char *cp,JsonObject *jo); +char *ForceStringVar(char *lp,char *dstr); +void send_download(void); void ScriptEverySecond(void) { @@ -142,11 +172,31 @@ void RulesTeleperiod(void) { if (bitRead(Settings.rule_enabled, 0)) Run_Scripter(">T",2, mqtt_data); } +//#define USE_24C256 + +// EEPROM MACROS +#ifdef USE_24C256 +// i2c eeprom +#include +#define EEPROM_ADDRESS 0x50 +// strange bug, crashes with powers of 2 ??? 4096 crashes +#define EEP_SCRIPT_SIZE 4095 +static Eeprom24C128_256 eeprom(EEPROM_ADDRESS); +// eeprom.writeBytes(address, length, buffer); +#define EEP_WRITE(A,B,C) eeprom.writeBytes(A,B,(uint8_t*)C); +// eeprom.readBytes(address, length, buffer); +#define EEP_READ(A,B,C) eeprom.readBytes(A,B,(uint8_t*)C); +#endif + #define SCRIPT_SKIP_SPACES while (*lp==' ' || *lp=='\t') lp++; #define SCRIPT_SKIP_EOL while (*lp==SCRIPT_EOL) lp++; -// allocates all variable and presets them -int16_t Init_Scripter(char *script) { +// allocates all variables and presets them +int16_t Init_Scripter(void) { +char *script; + + script=glob_script_mem.script_ram; + // scan lines for >DEF uint16_t lines=0,nvars=0,svars=0,vars=0; char *lp=script; @@ -243,6 +293,19 @@ int16_t Init_Scripter(char *script) { if (nvars>MAXNVARS) { return -1; } + if (vtypes[vars].bits.is_filter) { + while (isdigit(*op) || *op=='.' || *op=='-') { + op++; + } + while (*op==' ') op++; + if (isdigit(*op)) { + // lenght define follows + uint8_t flen=atoi(op); + mfilt[numflt-1].numvals&=0x7f; + mfilt[numflt-1].numvals|=flen&0x7f; + } + } + } else { // string vars op++; @@ -399,10 +462,7 @@ int16_t Init_Scripter(char *script) { #endif // now preset permanent vars - uint32_t lptr=(uint32_t)Settings.mems[0]; - lptr&=0xfffffffc; - float *fp=(float*)lptr; - fp++; + float *fp=(float*)glob_script_mem.script_pram; struct T_INDEX *vtp=glob_script_mem.type; for (uint8_t count=0; count0 ClaimSerial(); SetSerialBaudrate(9600); @@ -445,9 +518,70 @@ int16_t Init_Scripter(char *script) { #define NTYPE 0 #define STYPE 0x80 +#define FLT_MAX 99999999 + +float median_array(float *array,uint8_t len) { + uint8_t ind[len]; + uint8_t mind=0,index=0,flg; + float min=FLT_MAX; + + for (uint8_t hcnt=0; hcntnumvals&0x7f; + if (!bind) { + return mflp->index; + } + if (bind<1 || bind>maxind) bind=maxind; + return mflp->rbuff[bind-1]; + } + mp+=sizeof(struct M_FILT)+((mflp->numvals&0x7f)-1)*sizeof(float); + } + return 0; +} + +void Set_MFVal(uint8_t index,uint8_t bind,float val) { + uint8_t *mp=(uint8_t*)glob_script_mem.mfilt; + for (uint8_t count=0; countnumvals&0x7f; + if (!bind) { + mflp->index=val; + } else { + if (bind<1 || bind>maxind) bind=maxind; + mflp->rbuff[bind-1]=val; + } + return; + } + mp+=sizeof(struct M_FILT)+((mflp->numvals&0x7f)-1)*sizeof(float); + } +} -//Settings.seriallog_level -//Settings.weblog_level float Get_MFilter(uint8_t index) { uint8_t *mp=(uint8_t*)glob_script_mem.mfilt; @@ -458,23 +592,8 @@ float Get_MFilter(uint8_t index) { // moving average return mflp->maccu/(mflp->numvals&0x7f); } else { - // median, sort array - float tbuff[mflp->numvals],tmp; - uint8_t flag; - memmove(tbuff,mflp->rbuff,sizeof(tbuff)); - for (uint8_t ocnt=0; ocntnumvals; ocnt++) { - flag=0; - for (uint8_t count=0; countnumvals-1; count++) { - if (tbuff[count]>tbuff[count+1]) { - tmp=tbuff[count]; - tbuff[count]=tbuff[count+1]; - tbuff[count+1]=tmp; - flag=1; - } - } - if (!flag) break; - } - return mflp->rbuff[mflp->numvals/2]; + // median, sort array indices + return median_array(mflp->rbuff,mflp->numvals); } } mp+=sizeof(struct M_FILT)+((mflp->numvals&0x7f)-1)*sizeof(float); @@ -519,27 +638,10 @@ float DoMedian5(uint8_t index, float in) { if (index>=MEDIAN_FILTER_NUM) index=0; struct MEDIAN_FILTER* mf=&script_mf[index]; - - float tbuff[MEDIAN_SIZE],tmp; - uint8_t flag; mf->buffer[mf->index]=in; mf->index++; if (mf->index>=MEDIAN_SIZE) mf->index=0; - // sort list and take median - memmove(tbuff,mf->buffer,sizeof(tbuff)); - for (uint8_t ocnt=0; ocnttbuff[count+1]) { - tmp=tbuff[count]; - tbuff[count]=tbuff[count+1]; - tbuff[count+1]=tmp; - flag=1; - } - } - if (!flag) break; - } - return tbuff[MEDIAN_SIZE/2]; + return median_array(mf->buffer,MEDIAN_SIZE); } @@ -570,7 +672,24 @@ char *isvar(char *lp, uint8_t *vtype,struct T_INDEX *tind,float *fp,char *sp,Jso lp++; while (*lp!='"') { if (*lp==0 || *lp==SCRIPT_EOL) break; - if (sp) *sp++=*lp; + uint8_t iob=*lp; + if (iob=='\\') { + lp++; + if (*lp=='t') { + iob='\t'; + } else if (*lp=='n') { + iob='\n'; + } else if (*lp=='r') { + iob='\r'; + } else if (*lp=='\\') { + iob='\\'; + } else { + lp--; + } + if (sp) *sp++=iob; + } else { + if (sp) *sp++=iob; + } lp++; } if (sp) *sp=0; @@ -606,19 +725,35 @@ char *isvar(char *lp, uint8_t *vtype,struct T_INDEX *tind,float *fp,char *sp,Jso } struct T_INDEX *vtp=glob_script_mem.type; + char dvnam[32]; + strcpy (dvnam,vname); + uint8_t olen=len; + last_findex=-1; + char *ja=strchr(dvnam,'['); + if (ja) { + *ja=0; + ja++; + olen=strlen(dvnam); + } for (count=0; countindex=count; // overwrite with global var index if (vtp[count].bits.is_string==0) { *vtype=NTYPE|index; if (vtp[count].bits.is_filter) { - fvar=Get_MFilter(index); + if (ja) { + GetNumericResult(ja,OPER_EQU,&fvar,0); + last_findex=fvar; + fvar=Get_MFVal(index,fvar); + len++; + } else { + fvar=Get_MFilter(index); + } } else { fvar=glob_script_mem.fvars[index]; } @@ -741,6 +876,141 @@ chknext: goto exit; } break; +#ifdef USE_SCRIPT_FATFS + case 'f': + if (!strncmp(vname,"fo(",3)) { + lp+=3; + char str[SCRIPT_MAXSSIZE]; + lp=GetStringResult(lp,OPER_EQU,str,0); + while (*lp==' ') lp++; + lp=GetNumericResult(lp,OPER_EQU,&fvar,0); + uint8_t mode=fvar; + fvar=-1; + for (uint8_t cnt=0;cnt=SFS_MAX) ind=SFS_MAX-1; + glob_script_mem.files[ind].close(); + glob_script_mem.file_flags[ind]=0; + fvar=0; + lp++; + len=0; + goto exit; + } + if (!strncmp(vname,"fw(",3)) { + lp+=3; + char str[SCRIPT_MAXSSIZE]; + lp=ForceStringVar(lp,str); + while (*lp==' ') lp++; + lp=GetNumericResult(lp,OPER_EQU,&fvar,0); + uint8_t ind=fvar; + if (ind>=SFS_MAX) ind=SFS_MAX-1; + if (glob_script_mem.file_flags[ind]&1) { + fvar=glob_script_mem.files[ind].print(str); + } else { + fvar=0; + } + lp++; + len=0; + goto exit; + } + if (!strncmp(vname,"fr(",3)) { + lp+=3; + struct T_INDEX ind; + uint8_t vtype; + uint8_t sindex=0; + lp=isvar(lp,&vtype,&ind,0,0,0); + if (vtype!=VAR_NV) { + // found variable as result + if ((vtype&STYPE)==0) { + // error + fvar=0; + goto exit; + } else { + // string result + sindex=glob_script_mem.type[ind.index].index; + } + } else { + // error + fvar=0; + goto exit; + } + while (*lp==' ') lp++; + lp=GetNumericResult(lp,OPER_EQU,&fvar,0); + uint8_t find=fvar; + if (find>=SFS_MAX) find=SFS_MAX-1; + uint8_t index=0; + char str[glob_script_mem.max_ssize+1]; + char *cp=str; + if (glob_script_mem.file_flags[find]&1) { + while (glob_script_mem.files[find].available()) { + uint8_t buf[1]; + glob_script_mem.files[find].read(buf,1); + if (buf[0]=='\t' || buf[0]==',' || buf[0]=='\n' || buf[0]=='\r') { + break; + } else { + *cp++=buf[0]; + index++; + if (index>=glob_script_mem.max_ssize-1) break; + } + } + *cp=0; + } else { + strcpy(str,"file error"); + } + lp++; + strlcpy(glob_script_mem.glob_snp+(sindex*glob_script_mem.max_ssize),str,glob_script_mem.max_ssize); + fvar=index; + len=0; + goto exit; + } + if (!strncmp(vname,"fd(",3)) { + lp+=3; + char str[glob_script_mem.max_ssize+1]; + lp=GetStringResult(lp,OPER_EQU,str,0); + SD.remove(str); + lp++; + len=0; + goto exit; + } + if (!strncmp(vname,"fl1(",4) || !strncmp(vname,"fl2(",4) ) { + uint8_t lknum=*(lp+2)&3; + lp+=4; + char str[glob_script_mem.max_ssize+1]; + lp=GetStringResult(lp,OPER_EQU,str,0); + if (lknum<1 || lknum>2) lknum=1; + strlcpy(glob_script_mem.flink[lknum-1],str,14); + lp++; + fvar=0; + len=0; + goto exit; + } + if (!strncmp(vname,"fsm",3)) { + fvar=glob_script_mem.script_sd_found; + //card_init(); + goto exit; + } + break; + +#endif //USE_SCRIPT_FATFS case 'g': if (!strncmp(vname,"gtmp",4)) { fvar=global_temperature; @@ -918,7 +1188,7 @@ chknext: break; case 'r': if (!strncmp(vname,"ram",3)) { - fvar=glob_script_mem.script_mem_size+(MAX_RULE_SETS*MAX_RULE_SIZE)+(MAX_RULE_MEMS*10); + fvar=glob_script_mem.script_mem_size+(glob_script_mem.script_size)+(MAX_RULE_MEMS*10); goto exit; } break; @@ -940,9 +1210,53 @@ chknext: goto exit; } if (!strncmp(vname,"slen",4)) { - fvar=strlen(Settings.rules[0]); + fvar=strlen(glob_script_mem.script_ram); goto exit; } + if (!strncmp(vname,"st(",3)) { + lp+=3; + char str[SCRIPT_MAXSSIZE]; + lp=GetStringResult(lp,OPER_EQU,str,0); + while (*lp==' ') lp++; + char token[2]; + token[0]=*lp++; + token[1]=0; + while (*lp==' ') lp++; + lp=GetNumericResult(lp,OPER_EQU,&fvar,0); + // skip ) bracket + lp++; + len=0; + if (sp) { + // get stringtoken + char *st=strtok(str,token); + if (!st) { + *sp=0; + } else { + for (uint8_t cnt=1; cnt<=fvar; cnt++) { + if (cnt==fvar) { + strcpy(sp,st); + break; + } + st=strtok(NULL,token); + if (!st) { + *sp=0; + break; + } + } + } + } + goto strexit; + } + if (!strncmp(vname,"s(",2)) { + lp+=2; + lp=GetNumericResult(lp,OPER_EQU,&fvar,0); + char str[glob_script_mem.max_ssize+1]; + dtostrfd(fvar,glob_script_mem.script_dprec,str); + if (sp) strlcpy(sp,str,glob_script_mem.max_ssize); + lp++; + len=0; + goto strexit; + } #if defined(USE_TIMERS) && defined(USE_SUNRISE) if (!strncmp(vname,"sunrise",7)) { fvar=SunMinutes(0); @@ -1326,6 +1640,20 @@ struct T_INDEX ind; } +char *ForceStringVar(char *lp,char *dstr) { + float fvar; + char *slp=lp; + glob_script_mem.var_not_found=0; + lp=GetStringResult(lp,OPER_EQU,dstr,0); + if (glob_script_mem.var_not_found) { + // mismatch + lp=GetNumericResult(slp,OPER_EQU,&fvar,0); + dtostrfd(fvar,6,dstr); + glob_script_mem.var_not_found=0; + } + return lp; +} + // replace vars in cmd %var% void Replace_Cmd_Vars(char *srcbuf,char *dstbuf,uint16_t dstsize) { char *cp; @@ -1416,10 +1744,13 @@ void toSLog(const char *str) { #endif } + + + #define IF_NEST 8 // execute section of scripter int16_t Run_Scripter(const char *type, uint8_t tlen, char *js) { - uint8_t vtype=0,sindex,xflg,floop=0,globvindex; + uint8_t vtype=0,sindex,xflg,floop=0,globvindex,globaindex; struct T_INDEX ind; uint8_t operand,lastop,numeric=1,if_state[IF_NEST],if_result[IF_NEST],and_or,ifstck=0,s_ifstck=0; if_state[ifstck]=0; @@ -1681,7 +2012,7 @@ int16_t Run_Scripter(const char *type, uint8_t tlen, char *js) { toLog(&tmp[5]); } else { snprintf_P(log_data, sizeof(log_data), PSTR("Script: performs \"%s\""), tmp); - AddLog(glob_script_mem.script_loglevel); + AddLog(glob_script_mem.script_loglevel&0x7f); tasm_cmd_activ=1; ExecuteCommand((char*)tmp, SRC_RULE); tasm_cmd_activ=0; @@ -1726,6 +2057,7 @@ int16_t Run_Scripter(const char *type, uint8_t tlen, char *js) { if (vtype!=VAR_NV) { // found variable as result globvindex=ind.index; // save destination var index here + globaindex=last_findex; uint8_t index=glob_script_mem.type[ind.index].index; if ((vtype&STYPE)==0) { // numeric result @@ -1860,7 +2192,11 @@ int16_t Run_Scripter(const char *type, uint8_t tlen, char *js) { // var was changed glob_script_mem.type[globvindex].bits.changed=1; if (glob_script_mem.type[globvindex].bits.is_filter) { - Set_MFilter(glob_script_mem.type[globvindex].index,*dfvar); + if (globaindex>=0) { + Set_MFVal(glob_script_mem.type[globvindex].index,globaindex,*dfvar); + } else { + Set_MFilter(glob_script_mem.type[globvindex].index,*dfvar); + } } if (sysv_type) { @@ -1980,8 +2316,8 @@ int16_t Run_Scripter(const char *type, uint8_t tlen, char *js) { uint8_t script_xsns_index = 0; -void ScripterEvery100ms(void) -{ +void ScripterEvery100ms(void) { + if (Settings.rule_enabled && (uptime > 4)) { mqtt_data[0] = '\0'; uint16_t script_tele_period_save = tele_period; @@ -2000,11 +2336,8 @@ void ScripterEvery100ms(void) // can hold 11 floats or floats + strings // should report overflow later void Scripter_save_pvars(void) { - uint32_t lptr=(uint32_t)Settings.mems[0]; int16_t mlen=0; - lptr&=0xfffffffc; - float *fp=(float*)lptr; - fp++; + float *fp=(float*)glob_script_mem.script_pram; mlen+=sizeof(float); struct T_INDEX *vtp=glob_script_mem.type; for (uint8_t count=0; count " D_RULEVARS " " + "
 " D_SCRIPT " " "
"; const char HTTP_FORM_SCRIPT1[] PROGMEM = - "
script enable
" - "
"; + "" + ""; + +#ifdef USE_SCRIPT_FATFS +const char HTTP_FORM_SCRIPT1c[] PROGMEM = +""; +#endif + +#ifdef USE_SCRIPT_FATFS + +void script_download(uint8_t num) { + File download_file; + WiFiClient download_Client; + + if (!HttpCheckPriviledgedAccess()) { return; } + + if (!SD.exists(glob_script_mem.flink[num-1])) { + toLog("file not found"); + return; + } + + download_file=SD.open(glob_script_mem.flink[num-1],FILE_READ); + if (!download_file) { + toLog("could not open file"); + } + uint32_t flen=download_file.size(); + + download_Client = WebServer->client(); + WebServer->setContentLength(flen); + + char attachment[100]; + snprintf_P(attachment, sizeof(attachment), PSTR("attachment; filename=%s"),glob_script_mem.flink[num-1]); + WebServer->sendHeader(F("Content-Disposition"), attachment); + WSSend(200, CT_STREAM, ""); + + uint8_t buff[512]; + uint16_t bread; + + // transfer is about 150kb/s + uint8_t cnt=0; + while (download_file.available()) { + bread=download_file.read(buff,sizeof(buff)); + uint16_t bw=download_Client.write((const char*)buff,bread); + if (!bw) break; + cnt++; + if (cnt>7) { + cnt=0; + if (glob_script_mem.script_loglevel&0x80) { + // this indeed multitasks, but is slower 50 kB/s + loop(); + } + } + } + download_file.close(); + download_Client.stop(); +} +#endif void HandleScriptConfiguration(void) { @@ -2071,16 +2475,31 @@ void HandleScriptConfiguration(void) return; } +#ifdef USE_SCRIPT_FATFS + if (WebServer->hasArg("d1")) { + script_download(1); + } + if (WebServer->hasArg("d2")) { + script_download(2); + } +#endif + WSContentStart_P(S_CONFIGURE_SCRIPT); WSContentSendStyle(); WSContentSend_P(HTTP_FORM_SCRIPT); - WSContentSend_P(HTTP_FORM_SCRIPT1,1,1,bitRead(Settings.rule_enabled,0) ? " checked" : "",1,1,MAX_RULE_SIZE*3); + WSContentSend_P(HTTP_FORM_SCRIPT1,1,1,bitRead(Settings.rule_enabled,0) ? " checked" : "",glob_script_mem.script_size); // script is to larg for WSContentSend_P - if (Settings.rules[0][0]) { - _WSContentSend(Settings.rules[0]); + if (glob_script_mem.script_ram[0]) { + _WSContentSend(glob_script_mem.script_ram); } WSContentSend_P(HTTP_FORM_SCRIPT1b); + +#ifdef USE_SCRIPT_FATFS + if (glob_script_mem.flink[0][0]) WSContentSend_P(HTTP_FORM_SCRIPT1c,1,glob_script_mem.flink[0]); + if (glob_script_mem.flink[1][0]) WSContentSend_P(HTTP_FORM_SCRIPT1c,2,glob_script_mem.flink[1]); +#endif + WSContentSend_P(HTTP_FORM_END); WSContentSpaceButton(BUTTON_CONFIGURATION); WSContentStop(); @@ -2097,7 +2516,7 @@ void strrepl_inplace(char *str, const char *a, const char *b) { } } -#define MAX_SCRIPT_SIZE MAX_RULE_SIZE*3 + void ScriptSaveSettings(void) { @@ -2117,7 +2536,23 @@ void ScriptSaveSettings(void) { str.replace("\r\n","\n"); str.replace("\r","\n"); #endif - strlcpy(Settings.rules[0],str.c_str(), MAX_RULE_SIZE*3); + strlcpy(glob_script_mem.script_ram,str.c_str(), glob_script_mem.script_size); + +#ifdef USE_24C256 + if (glob_script_mem.flags&1) { + EEP_WRITE(0,EEP_SCRIPT_SIZE,glob_script_mem.script_ram); + } +#endif + +#ifdef USE_SCRIPT_FATFS + if (glob_script_mem.flags&1) { + SD.remove(FAT_SCRIPT_NAME); + File file=SD.open(FAT_SCRIPT_NAME,FILE_WRITE); + file.write(glob_script_mem.script_ram,FAT_SCRIPT_SIZE); + file.close(); + } +#endif + } if (glob_script_mem.script_mem) { @@ -2128,7 +2563,7 @@ void ScriptSaveSettings(void) { } if (bitRead(Settings.rule_enabled, 0)) { - int16_t res=Init_Scripter(Settings.rules[0]); + int16_t res=Init_Scripter(); if (res) { snprintf_P(log_data, sizeof(log_data), PSTR("script init error: %d"),res); AddLog(LOG_LEVEL_INFO); @@ -2182,7 +2617,7 @@ bool ScriptCommand(void) { }*/ } } - snprintf_P (mqtt_data, sizeof(mqtt_data), PSTR("{\"%s\":\"%s\",\"Free\":%d}"),command, GetStateText(bitRead(Settings.rule_enabled,0)),MAX_RULE_SIZE*3-strlen(Settings.rules[0])); + snprintf_P (mqtt_data, sizeof(mqtt_data), PSTR("{\"%s\":\"%s\",\"Free\":%d}"),command, GetStateText(bitRead(Settings.rule_enabled,0)),glob_script_mem.script_size-strlen(glob_script_mem.script_ram)); } else serviced = false; return serviced; @@ -2192,13 +2627,76 @@ bool ScriptCommand(void) { * Interface \*********************************************************************************************/ -bool Xdrv10(byte function) +bool Xdrv10(uint8_t function) { bool result = false; switch (function) { case FUNC_PRE_INIT: - if (bitRead(Settings.rule_enabled, 0)) Init_Scripter(Settings.rules[0]); + // set defaults to rules memory + glob_script_mem.script_ram=Settings.rules[0]; + glob_script_mem.script_size=MAX_SCRIPT_SIZE; + glob_script_mem.flags=0; + glob_script_mem.script_pram=(uint8_t*)Settings.mems[0]; + glob_script_mem.script_pram_size=MAX_RULE_MEMS*10; + +#ifdef USE_24C256 + if (i2c_flg) { + if (I2cDevice(EEPROM_ADDRESS)) { + // found 32kb eeprom + char *script; + script=(char*)calloc(EEP_SCRIPT_SIZE+4,1); + if (!script) break; + glob_script_mem.script_ram=script; + glob_script_mem.script_size=EEP_SCRIPT_SIZE; + EEP_READ(0,EEP_SCRIPT_SIZE,script); + if (*script==0xff) { + memset(script,EEP_SCRIPT_SIZE,0); + } + script[EEP_SCRIPT_SIZE-1]=0; + // use rules storage for permanent vars + glob_script_mem.script_pram=(uint8_t*)Settings.rules[0]; + glob_script_mem.script_pram_size=MAX_SCRIPT_SIZE; + + glob_script_mem.flags=1; + } + } +#endif + + + +#ifdef USE_SCRIPT_FATFS + if (SD.begin(USE_SCRIPT_FATFS)) { + glob_script_mem.script_sd_found=1; + char *script; + script=(char*)calloc(FAT_SCRIPT_SIZE+4,1); + if (!script) break; + glob_script_mem.script_ram=script; + glob_script_mem.script_size=FAT_SCRIPT_SIZE; + if (SD.exists(FAT_SCRIPT_NAME)) { + File file=SD.open(FAT_SCRIPT_NAME,FILE_READ); + file.read((uint8_t*)script,FAT_SCRIPT_SIZE); + file.close(); + } + script[FAT_SCRIPT_SIZE-1]=0; + // use rules storage for permanent vars + glob_script_mem.script_pram=(uint8_t*)Settings.rules[0]; + glob_script_mem.script_pram_size=MAX_SCRIPT_SIZE; + + glob_script_mem.flags=1; + } else { + glob_script_mem.script_sd_found=0; + } +#endif + + // assure permanent memory is 4 byte aligned + { uint32_t ptr=(uint32_t)glob_script_mem.script_pram; + ptr&=0xfffffffc; + glob_script_mem.script_pram=(uint8_t*)ptr; + glob_script_mem.script_pram_size-=4; + } + + if (bitRead(Settings.rule_enabled, 0)) Init_Scripter(); break; case FUNC_INIT: if (bitRead(Settings.rule_enabled, 0)) Run_Scripter(">B",2,0); diff --git a/sonoff/xdrv_20_hue.ino b/sonoff/xdrv_20_hue.ino index d54becd666fc..6a4617ffc20d 100644 --- a/sonoff/xdrv_20_hue.ino +++ b/sonoff/xdrv_20_hue.ino @@ -342,6 +342,21 @@ void HueLightStatus2(uint8_t device, String *response) response->replace("{j2", GetHueDeviceId(device)); } +// generate a unique lightId mixing local IP address and device number +// it is limited to 16 devices. +// last 16 bits of Mac address + 4 bits of local light +uint32_t EncodeLightId(uint8_t idx) +{ + uint8_t mac[6]; + WiFi.macAddress(mac); + uint32_t id = (mac[3] << 20) | (mac[4] << 12) | (mac[5] << 4) | (idx & 0xF); + return id; +} + +uint32_t DecodeLightId(uint32_t id) { + return id & 0xF; +} + void HueGlobalConfig(String *path) { String response; @@ -350,7 +365,7 @@ void HueGlobalConfig(String *path) path->remove(0,1); // cut leading / to get response = F("{\"lights\":{\""); for (uint8_t i = 1; i <= maxhue; i++) { - response += i; + response += EncodeLightId(i); response += F("\":{\"state\":"); HueLightStatus1(i, &response); HueLightStatus2(i, &response); @@ -394,7 +409,7 @@ void HueLights(String *path) if (path->endsWith("/lights")) { // Got /lights response = "{\""; for (uint8_t i = 1; i <= maxhue; i++) { - response += i; + response += EncodeLightId(i); response += F("\":{\"state\":"); HueLightStatus1(i, &response); HueLightStatus2(i, &response); @@ -562,7 +577,7 @@ void HueLights(String *path) } else if(path->indexOf("/lights/") >= 0) { // Got /lights/ID path->remove(0,8); // Remove /lights/ - device = atoi(path->c_str()); + device = DecodeLightId(atoi(path->c_str())); if ((device < 1) || (device > maxhue)) { device = 1; } @@ -591,7 +606,7 @@ void HueGroups(String *path) String lights = F("\"1\""); for (uint8_t i = 2; i <= maxhue; i++) { lights += ",\""; - lights += String(i); + lights += EncodeLightId(i); lights += "\""; } response.replace("{l1", lights); diff --git a/sonoff/xnrg_01_hlw8012.ino b/sonoff/xnrg_01_hlw8012.ino index c098870e40c9..e26f195fbaf7 100644 --- a/sonoff/xnrg_01_hlw8012.ino +++ b/sonoff/xnrg_01_hlw8012.ino @@ -86,6 +86,7 @@ void HlwCfInterrupt(void) // Service Power hlw_cf_pulse_last_time = us; hlw_energy_period_counter++; } + energy_data_valid = 0; } void HlwCf1Interrupt(void) // Service Voltage and Current @@ -104,6 +105,7 @@ void HlwCf1Interrupt(void) // Service Voltage and Current hlw_cf1_timer = 8; // We need up to HLW_SAMPLE_COUNT samples within 1 second (low current could take up to 0.3 second) } } + energy_data_valid = 0; } /********************************************************************************************/ @@ -189,14 +191,20 @@ void HlwEvery200ms(void) void HlwEverySecond(void) { - unsigned long hlw_len; - - if (hlw_energy_period_counter) { - hlw_len = 10000 / hlw_energy_period_counter; - hlw_energy_period_counter = 0; - if (hlw_len) { - energy_kWhtoday_delta += ((hlw_power_ratio * Settings.energy_power_calibration) / hlw_len) / 36; - EnergyUpdateToday(); + if (energy_data_valid > ENERGY_WATCHDOG) { + hlw_cf1_voltage_pulse_length = 0; + hlw_cf1_current_pulse_length = 0; + hlw_cf_power_pulse_length = 0; + } else { + unsigned long hlw_len; + + if (hlw_energy_period_counter) { + hlw_len = 10000 / hlw_energy_period_counter; + hlw_energy_period_counter = 0; + if (hlw_len) { + energy_kWhtoday_delta += ((hlw_power_ratio * Settings.energy_power_calibration) / hlw_len) / 36; + EnergyUpdateToday(); + } } } } diff --git a/sonoff/xnrg_02_cse7766.ino b/sonoff/xnrg_02_cse7766.ino index 55d87a163d29..b2b2ceccaa0c 100644 --- a/sonoff/xnrg_02_cse7766.ino +++ b/sonoff/xnrg_02_cse7766.ino @@ -139,6 +139,7 @@ bool CseSerialInput(void) uint8_t checksum = 0; for (uint8_t i = 2; i < 23; i++) { checksum += serial_in_buffer[i]; } if (checksum == serial_in_buffer[23]) { + energy_data_valid = 0; CseReceived(); cse_receive_flag = 0; return 1; @@ -170,28 +171,34 @@ bool CseSerialInput(void) void CseEverySecond(void) { - long cf_frequency = 0; - - if (CSE_PULSES_NOT_INITIALIZED == cf_pulses_last_time) { - cf_pulses_last_time = cf_pulses; // Init after restart + if (energy_data_valid > ENERGY_WATCHDOG) { + voltage_cycle = 0; + current_cycle = 0; + power_cycle = 0; } else { - if (cf_pulses < cf_pulses_last_time) { // Rolled over after 65535 pulses - cf_frequency = (65536 - cf_pulses_last_time) + cf_pulses; + long cf_frequency = 0; + + if (CSE_PULSES_NOT_INITIALIZED == cf_pulses_last_time) { + cf_pulses_last_time = cf_pulses; // Init after restart } else { - cf_frequency = cf_pulses - cf_pulses_last_time; - } - if (cf_frequency && energy_active_power) { - unsigned long delta = (cf_frequency * Settings.energy_power_calibration) / 36; - // prevent invalid load delta steps even checksum is valid (issue #5789): - if (delta <= (3680*100/36) * 10 ) { // max load for S31/Pow R2: 3.68kW - cf_pulses_last_time = cf_pulses; - energy_kWhtoday_delta += delta; + if (cf_pulses < cf_pulses_last_time) { // Rolled over after 65535 pulses + cf_frequency = (65536 - cf_pulses_last_time) + cf_pulses; + } else { + cf_frequency = cf_pulses - cf_pulses_last_time; } - else { - AddLog_P(LOG_LEVEL_DEBUG, PSTR("CSE: Load overflow")); - cf_pulses_last_time = CSE_PULSES_NOT_INITIALIZED; + if (cf_frequency && energy_active_power) { + unsigned long delta = (cf_frequency * Settings.energy_power_calibration) / 36; + // prevent invalid load delta steps even checksum is valid (issue #5789): + if (delta <= (3680*100/36) * 10 ) { // max load for S31/Pow R2: 3.68kW + cf_pulses_last_time = cf_pulses; + energy_kWhtoday_delta += delta; + } + else { + AddLog_P(LOG_LEVEL_DEBUG, PSTR("CSE: Load overflow")); + cf_pulses_last_time = CSE_PULSES_NOT_INITIALIZED; + } + EnergyUpdateToday(); } - EnergyUpdateToday(); } } } diff --git a/sonoff/xnrg_03_pzem004t.ino b/sonoff/xnrg_03_pzem004t.ino index b2c72bfa8d90..8340b09dc4d2 100644 --- a/sonoff/xnrg_03_pzem004t.ino +++ b/sonoff/xnrg_03_pzem004t.ino @@ -169,6 +169,7 @@ void PzemEvery200ms(void) if (data_ready) { float value = 0; if (PzemRecieve(pzem_responses[pzem_read_state], &value)) { + energy_data_valid = 0; switch (pzem_read_state) { case 1: // Voltage as 230.2V energy_voltage = value; diff --git a/sonoff/xnrg_04_mcp39f501.ino b/sonoff/xnrg_04_mcp39f501.ino index af6e8657cc51..cbffbf10bba3 100644 --- a/sonoff/xnrg_04_mcp39f501.ino +++ b/sonoff/xnrg_04_mcp39f501.ino @@ -469,6 +469,7 @@ void McpParseData(void) energy_active_power = 0; energy_current = 0; } + energy_data_valid = 0; } /********************************************************************************************/ @@ -526,6 +527,13 @@ void McpSerialInput(void) void McpEverySecond(void) { + if (energy_data_valid > ENERGY_WATCHDOG) { + mcp_voltage_rms = 0; + mcp_current_rms = 0; + mcp_active_power = 0; + mcp_line_frequency = 0; + } + if (mcp_active_power) { energy_kWhtoday_delta += ((mcp_active_power * 10) / 36); EnergyUpdateToday(); diff --git a/sonoff/xnrg_05_pzem_ac.ino b/sonoff/xnrg_05_pzem_ac.ino index bb8c129d5824..653b4ef63cd2 100644 --- a/sonoff/xnrg_05_pzem_ac.ino +++ b/sonoff/xnrg_05_pzem_ac.ino @@ -51,6 +51,8 @@ void PzemAcEverySecond(void) if (error) { AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "PzemAc response error %d"), error); } else { + energy_data_valid = 0; + // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 // 01 04 14 08 D1 00 6C 00 00 00 F4 00 00 00 26 00 00 01 F4 00 64 00 00 51 34 // Id Cc Sz Volt- Current---- Power------ Energy----- Frequ PFact Alarm Crc-- diff --git a/sonoff/xnrg_06_pzem_dc.ino b/sonoff/xnrg_06_pzem_dc.ino index 00e75a5ca554..f9817577a8ec 100644 --- a/sonoff/xnrg_06_pzem_dc.ino +++ b/sonoff/xnrg_06_pzem_dc.ino @@ -51,6 +51,8 @@ void PzemDcEverySecond(void) if (error) { AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "PzemDc response error %d"), error); } else { + energy_data_valid = 0; + // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 // 01 04 10 05 40 00 0A 00 0D 00 00 00 02 00 00 00 00 00 00 D6 29 // Id Cc Sz Volt- Curre Power------ Energy----- HiAlm LoAlm Crc-- diff --git a/sonoff/xsns_01_counter.ino b/sonoff/xsns_01_counter.ino index 069016a879e1..84b1c29ea8b9 100644 --- a/sonoff/xsns_01_counter.ino +++ b/sonoff/xsns_01_counter.ino @@ -25,6 +25,14 @@ unsigned long last_counter_timer[MAX_COUNTERS]; // Last counter time in micro seconds +#ifndef ARDUINO_ESP8266_RELEASE_2_3_0 // Fix core 2.5.x ISR not in IRAM Exception +void CounterUpdate(uint8_t index) ICACHE_RAM_ATTR; +void CounterUpdate1(void) ICACHE_RAM_ATTR; +void CounterUpdate2(void) ICACHE_RAM_ATTR; +void CounterUpdate3(void) ICACHE_RAM_ATTR; +void CounterUpdate4(void) ICACHE_RAM_ATTR; +#endif // ARDUINO_ESP8266_RELEASE_2_3_0 + void CounterUpdate(uint8_t index) { unsigned long counter_debounce_time = micros() - last_counter_timer[index -1]; @@ -40,13 +48,6 @@ void CounterUpdate(uint8_t index) } } -#ifndef ARDUINO_ESP8266_RELEASE_2_3_0 // Fix core 2.5.x ISR not in IRAM Exception -void CounterUpdate1(void) ICACHE_RAM_ATTR; // As iram is tight and it works this way too -void CounterUpdate2(void) ICACHE_RAM_ATTR; // As iram is tight and it works this way too -void CounterUpdate3(void) ICACHE_RAM_ATTR; // As iram is tight and it works this way too -void CounterUpdate4(void) ICACHE_RAM_ATTR; // As iram is tight and it works this way too -#endif // ARDUINO_ESP8266_RELEASE_2_3_0 - void CounterUpdate1(void) { CounterUpdate(1); diff --git a/sonoff/xsns_02_analog.ino b/sonoff/xsns_02_analog.ino index 85560e3d3414..0fe674c3a438 100644 --- a/sonoff/xsns_02_analog.ino +++ b/sonoff/xsns_02_analog.ino @@ -35,21 +35,40 @@ // 3V3 --- ANALOG_NTC_BRIDGE_RESISTANCE ---v--- NTC --- Gnd // | // ADC0 -#define ANALOG_NTC_BRIDGE_RESISTANCE 32000.0 // NTC Voltage bridge resistor -#define ANALOG_NTC_RESISTANCE 10000.0 // NTC Resistance -#define ANALOG_NTC_B_COEFFICIENT 3350.0 // NTC Beta Coefficient +#define ANALOG_NTC_BRIDGE_RESISTANCE 32000 // NTC Voltage bridge resistor +#define ANALOG_NTC_RESISTANCE 10000 // NTC Resistance +#define ANALOG_NTC_B_COEFFICIENT 3350 // NTC Beta Coefficient // LDR parameters // 3V3 --- LDR ---v--- ANALOG_LDR_BRIDGE_RESISTANCE --- Gnd // | // ADC0 -#define ANALOG_LDR_BRIDGE_RESISTANCE 10000.0 // LDR Voltage bridge resistor +#define ANALOG_LDR_BRIDGE_RESISTANCE 10000 // LDR Voltage bridge resistor #define ANALOG_LDR_LUX_CALC_SCALAR 12518931 // Experimental -#define ANALOG_LDR_LUX_CALC_EXPONENT -1.405 // Experimental +#define ANALOG_LDR_LUX_CALC_EXPONENT -1.4050 // Experimental uint16_t adc_last_value = 0; float adc_temp = 0; +void AdcInit(void) +{ + if ((Settings.adc_param_type != my_adc0) || (Settings.adc_param1 > 1000000) || (Settings.adc_param1 < 100)) { + if (ADC0_TEMP == my_adc0) { + // Default Shelly 2.5 and 1PM parameters + Settings.adc_param_type = ADC0_TEMP; + Settings.adc_param1 = ANALOG_NTC_BRIDGE_RESISTANCE; + Settings.adc_param2 = ANALOG_NTC_RESISTANCE; + Settings.adc_param3 = ANALOG_NTC_B_COEFFICIENT * 10000; + } + else if (ADC0_LIGHT == my_adc0) { + Settings.adc_param_type = ADC0_LIGHT; + Settings.adc_param1 = ANALOG_LDR_BRIDGE_RESISTANCE; + Settings.adc_param2 = ANALOG_LDR_LUX_CALC_SCALAR; + Settings.adc_param3 = ANALOG_LDR_LUX_CALC_EXPONENT * 10000; + } + } +} + uint16_t AdcRead(uint8_t factor) { // factor 1 = 2 samples @@ -88,8 +107,8 @@ uint16_t AdcGetLux() // Source: https://www.allaboutcircuits.com/projects/design-a-luxmeter-using-a-light-dependent-resistor/ double resistorVoltage = ((double)adc / 1023) * ANALOG_V33; double ldrVoltage = ANALOG_V33 - resistorVoltage; - double ldrResistance = ldrVoltage / resistorVoltage * ANALOG_LDR_BRIDGE_RESISTANCE; - double ldrLux = ANALOG_LDR_LUX_CALC_SCALAR * FastPrecisePow(ldrResistance, ANALOG_LDR_LUX_CALC_EXPONENT); + double ldrResistance = ldrVoltage / resistorVoltage * (double)Settings.adc_param1; + double ldrLux = (double)Settings.adc_param2 * FastPrecisePow(ldrResistance, (double)Settings.adc_param3 / 10000); return (uint16_t)ldrLux; } @@ -99,12 +118,66 @@ void AdcEverySecond(void) if (ADC0_TEMP == my_adc0) { int adc = AdcRead(2); // Steinhart-Hart equation for thermistor as temperature sensor - double Rt = (adc * ANALOG_NTC_BRIDGE_RESISTANCE) / (1024.0 * ANALOG_V33 - (double)adc); - double T = ANALOG_NTC_B_COEFFICIENT / (ANALOG_NTC_B_COEFFICIENT / ANALOG_T0 + TaylorLog(Rt / ANALOG_NTC_RESISTANCE)); + double Rt = (adc * Settings.adc_param1) / (1024.0 * ANALOG_V33 - (double)adc); + double BC = (double)Settings.adc_param3 / 10000; + double T = BC / (BC / ANALOG_T0 + TaylorLog(Rt / (double)Settings.adc_param2)); adc_temp = ConvertTemp(TO_CELSIUS(T)); } } +/*********************************************************************************************\ + * Commands +\*********************************************************************************************/ + +#define D_CMND_ADCPARAM "AdcParam" +enum AdcCommands { CMND_ADCPARAM }; +const char kAdcCommands[] PROGMEM = D_CMND_ADCPARAM; + +bool AdcCommand(void) +{ + char command[CMDSZ]; + bool serviced = true; + + int command_code = GetCommandCode(command, sizeof(command), XdrvMailbox.topic, kAdcCommands); + if (CMND_ADCPARAM == command_code) { + if (XdrvMailbox.data_len) { + if ((ADC0_TEMP == XdrvMailbox.payload) || (ADC0_LIGHT == XdrvMailbox.payload)) { +// if ((XdrvMailbox.payload == my_adc0) && ((ADC0_TEMP == my_adc0) || (ADC0_LIGHT == my_adc0))) { + if (strstr(XdrvMailbox.data, ",") != nullptr) { // Process parameter entry + char sub_string[XdrvMailbox.data_len +1]; + // AdcParam 2, 32000, 10000, 3350 + // AdcParam 3, 10000, 12518931, -1.405 + Settings.adc_param_type = XdrvMailbox.payload; +// Settings.adc_param_type = my_adc0; + Settings.adc_param1 = strtol(subStr(sub_string, XdrvMailbox.data, ",", 2), nullptr, 10); + Settings.adc_param2 = strtol(subStr(sub_string, XdrvMailbox.data, ",", 3), nullptr, 10); + Settings.adc_param3 = (int)(CharToDouble(subStr(sub_string, XdrvMailbox.data, ",", 4)) * 10000); + } else { // Set default values based on current adc type + // AdcParam 2 + // AdcParam 3 + Settings.adc_param_type = 0; + AdcInit(); + } + } + } + + // AdcParam + int value = Settings.adc_param3; + uint8_t precision; + for (precision = 4; precision > 0; precision--) { + if (value % 10) { break; } + value /= 10; + } + char param3[33]; + dtostrfd(((double)Settings.adc_param3)/10000, precision, param3); + Response_P(PSTR("{\"" D_CMND_ADCPARAM "\":[%d,%d,%d,%s]}"), + Settings.adc_param_type, Settings.adc_param1, Settings.adc_param2, param3); + } + else serviced = false; // Unknown command + + return serviced; +} + void AdcShow(bool json) { if (ADC0_INPUT == my_adc0) { @@ -176,6 +249,12 @@ bool Xsns02(uint8_t function) case FUNC_EVERY_SECOND: AdcEverySecond(); break; + case FUNC_INIT: + AdcInit(); + break; + case FUNC_COMMAND: + result = AdcCommand(); + break; case FUNC_JSON_APPEND: AdcShow(1); break; diff --git a/sonoff/xsns_21_sgp30.ino b/sonoff/xsns_21_sgp30.ino index 27d1a43f9730..e9c097dc4845 100644 --- a/sonoff/xsns_21_sgp30.ino +++ b/sonoff/xsns_21_sgp30.ino @@ -89,7 +89,7 @@ void Sgp30Update(void) // Perform every second to ensure proper operation of th } return; // Measurement failed } - if (global_update) { + if (global_update && global_humidity>0 && global_temperature!=9999) { // abs hum in mg/m3 sgp30_abshum=sgp30_AbsoluteHumidity(global_temperature,global_humidity,TempUnit()); sgp.setHumidity(sgp30_abshum*1000); @@ -124,7 +124,7 @@ void Sgp30Show(bool json) if (sgp30_ready) { char abs_hum[33]; - if (global_update) { + if (global_update && global_humidity>0 && global_temperature!=9999) { // has humidity + temperature dtostrfd(sgp30_abshum,4,abs_hum); } diff --git a/sonoff/xsns_23_sdm120.ino b/sonoff/xsns_23_sdm120.ino index 8f0b6b74fcaf..7c8e064de751 100644 --- a/sonoff/xsns_23_sdm120.ino +++ b/sonoff/xsns_23_sdm120.ino @@ -27,8 +27,20 @@ #define XSNS_23 23 +// can be user defined in my_user_config.h +#ifndef SDM120_SPEED + #define SDM120_SPEED 2400 // default SDM120 Modbus address +#endif +// can be user defined in my_user_config.h +#ifndef SDM120_ADDR + #define SDM120_ADDR 1 // default SDM120 Modbus address +#endif + + #include +enum SDM120_Error {SDM120_ERR_NO_ERROR=0, SDM120_ERR_CRC_ERROR, SDM120_ERR_WRONG_BYTES, SDM120_ERR_NOT_ENOUGHT_BYTES}; + TasmotaSerial *SDM120Serial; uint8_t sdm120_type = 1; @@ -58,7 +70,7 @@ void SDM120_ModbusSend(uint8_t function_code, uint16_t start_address, uint16_t r { uint8_t frame[8]; - frame[0] = 0x01; // default SDM120 Modbus address + frame[0] = SDM120_ADDR; frame[1] = function_code; frame[2] = (uint8_t)(start_address >> 8); frame[3] = (uint8_t)(start_address); @@ -87,13 +99,12 @@ uint8_t SDM120_ModbusReceive(float *value) buffer[len++] = (uint8_t)SDM120Serial->read(); } - if (len < 9) - return 3; // SDM_ERR_NOT_ENOUGHT_BYTES - - if (len == 9) { - - if (buffer[0] == 0x01 && buffer[1] == 0x04 && buffer[2] == 4) { // check node number, op code and reply bytes count + if (len < 9) { + return SDM120_ERR_NOT_ENOUGHT_BYTES; + } + if (9 == len) { + if (0x01 == buffer[0] && 0x04 == buffer[1] && 4 == buffer[2]) { // check node number, op code and reply bytes count if((SDM120_calculateCRC(buffer, 7)) == ((buffer[8] << 8) | buffer[7])) { //calculate crc from first 7 bytes and compare with received crc (bytes 7 & 8) ((uint8_t*)value)[3] = buffer[3]; @@ -101,12 +112,16 @@ uint8_t SDM120_ModbusReceive(float *value) ((uint8_t*)value)[1] = buffer[5]; ((uint8_t*)value)[0] = buffer[6]; - } else return 1; // SDM_ERR_CRC_ERROR + } else { + return SDM120_ERR_CRC_ERROR; + } - } else return 2; // SDM_ERR_WRONG_BYTES + } else { + return SDM120_ERR_WRONG_BYTES; + } } - return 0; // SDM_ERR_NO_ERROR + return SDM120_ERR_NO_ERROR; } uint16_t SDM120_calculateCRC(uint8_t *frame, uint8_t num) @@ -152,6 +167,7 @@ const uint16_t sdm120_start_addresses[] { uint8_t sdm120_read_state = 0; uint8_t sdm120_send_retry = 0; +uint8_t sdm120_nodata_count = 0; void SDM120250ms(void) // Every 250 mSec { @@ -163,6 +179,7 @@ void SDM120250ms(void) // Every 250 mSec bool data_ready = SDM120_ModbusReceiveReady(); if (data_ready) { + sdm120_nodata_count = 0; uint8_t error = SDM120_ModbusReceive(&value); if (error) { AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "SDM120 response error %d"), error); @@ -233,6 +250,18 @@ void SDM120250ms(void) // Every 250 mSec } } } // end data ready + else { + if (sdm120_nodata_count <= (1000/250) * 4) { // max. 4 sec without data + sdm120_nodata_count++; + } else if (sdm120_nodata_count != 255) { + // no data from modbus, reset values to 0 + sdm120_nodata_count = 255; + sdm120_voltage = sdm120_current = sdm120_active_power = sdm120_apparent_power = sdm120_reactive_power = sdm120_power_factor = sdm120_frequency = sdm120_energy_total = 0; +#ifdef USE_SDM220 + sdm120_phase_angle = sdm120_import_active = sdm120_export_active = sdm120_import_reactive = sdm120_export_reactive = sdm120_total_reactive = 0; +#endif + } + } if (0 == sdm120_send_retry || data_ready) { sdm120_send_retry = 5; @@ -248,11 +277,7 @@ void SDM120Init(void) sdm120_type = 0; if ((pin[GPIO_SDM120_RX] < 99) && (pin[GPIO_SDM120_TX] < 99)) { SDM120Serial = new TasmotaSerial(pin[GPIO_SDM120_RX], pin[GPIO_SDM120_TX], 1); -#ifdef SDM120_SPEED if (SDM120Serial->begin(SDM120_SPEED)) { -#else - if (SDM120Serial->begin(2400)) { -#endif if (SDM120Serial->hardwareSerial()) { ClaimSerial(); } sdm120_type = 1; } diff --git a/sonoff/xsns_31_ccs811.ino b/sonoff/xsns_31_ccs811.ino index 2624d0b8c5c6..550313b510dc 100644 --- a/sonoff/xsns_31_ccs811.ino +++ b/sonoff/xsns_31_ccs811.ino @@ -62,7 +62,7 @@ void CCS811Update(void) // Perform every n second TVOC = ccs.getTVOC(); eCO2 = ccs.geteCO2(); CCS811_ready = 1; - if (global_update) { ccs.setEnvironmentalData((uint8_t)global_humidity, global_temperature); } + if (global_update && global_humidity>0 && global_temperature!=9999) { ccs.setEnvironmentalData((uint8_t)global_humidity, global_temperature); } ecnt = 0; } } else { diff --git a/sonoff/xsns_45_vl53l0x.ino b/sonoff/xsns_45_vl53l0x.ino new file mode 100755 index 000000000000..1f9248709ee4 --- /dev/null +++ b/sonoff/xsns_45_vl53l0x.ino @@ -0,0 +1,162 @@ +/* + xsns_99_vl53l0x.ino - VL53L0X + + Copyright (C) 2018 Theo Arends and Gerhard Mutz + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifdef USE_I2C +#ifdef USE_VL53L0X + +#include +#include "VL53L0X.h" +VL53L0X sensor; + +uint8_t vl53l0x_ready = 0; +uint16_t vl53l0x_distance; +uint16_t Vl53l0_buffer[5]; +uint8_t Vl53l0_index; + + +/********************************************************************************************/ + +void Vl53l0Detect() +{ + + if (!I2cDevice(0x29)) { + return; + } + + if (vl53l0x_ready) { + AddLog_P(LOG_LEVEL_DEBUG, PSTR("VL53L1X is ready")); + return; + } + + if (sensor.init()==true) { + snprintf_P(log_data, sizeof(log_data), S_LOG_I2C_FOUND_AT, "VL53L0X", sensor.getAddress()); + AddLog(LOG_LEVEL_DEBUG); + } else { + return; + } + + sensor.setTimeout(500); + + // Start continuous back-to-back mode (take readings as + // fast as possible). To use continuous timed mode + // instead, provide a desired inter-measurement period in + // ms (e.g. sensor.startContinuous(100)). + sensor.startContinuous(); + vl53l0x_ready = 1; + + Vl53l0_index=0; + +} + +#define D_UNIT_MILLIMETER "mm" + +#ifdef USE_WEBSERVER +const char HTTP_SNS_VL53L0X[] PROGMEM = + "{s}VL53L0X " D_DISTANCE "{m}%d" D_UNIT_MILLIMETER "{e}"; // {s} = , {m} = , {e} = +#endif // USE_WEBSERVER + +#define USE_VL_MEDIAN + +void Vl53l0Every_250MSecond() { + uint16_t tbuff[5],tmp; + uint8_t flag; + + if (!vl53l0x_ready) return; + + // every 200 ms + uint16_t dist = sensor.readRangeContinuousMillimeters(); + if (dist==0 || dist>2000) { + dist=9999; + } + +#ifdef USE_VL_MEDIAN + // store in ring buffer + Vl53l0_buffer[Vl53l0_index]=dist; + Vl53l0_index++; + if (Vl53l0_index>=5) Vl53l0_index=0; + + // sort list and take median + memmove(tbuff,Vl53l0_buffer,sizeof(tbuff)); + for (byte ocnt=0; ocnt<5; ocnt++) { + flag=0; + for (byte count=0; count<4; count++) { + if (tbuff[count]>tbuff[count+1]) { + tmp=tbuff[count]; + tbuff[count]=tbuff[count+1]; + tbuff[count+1]=tmp; + flag=1; + } + } + if (!flag) break; + } + vl53l0x_distance=tbuff[2]; +#else + vl53l0x_distance=dist; +#endif +} + +void Vl53l0Show(boolean json) +{ + if (!vl53l0x_ready) { + return; + } + + if (json) { + ResponseAppend_P(PSTR(",\"VL53L0X\":{\"" D_JSON_DISTANCE "\":%d}"), vl53l0x_distance); +#ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_SNS_VL53L0X, vl53l0x_distance); +#endif + } + +} + +/*********************************************************************************************\ + * Interface +\*********************************************************************************************/ + +#define XSNS_45 + +bool Xsns45(byte function) +{ + bool result = false; + + if (i2c_flg) { + switch (function) { + case FUNC_INIT: + Vl53l0Detect(); + break; + case FUNC_EVERY_250_MSECOND: + Vl53l0Every_250MSecond(); + break; + case FUNC_JSON_APPEND: + Vl53l0Show(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + Vl53l0Show(0); + break; +#endif // USE_WEBSERVER + } + } + return result; +} + +#endif // USE_VL53L0X +#endif // USE_I2C diff --git a/tools/decode-config.py b/tools/decode-config.py index f3e2b526b86f..94d0cc4fa8b6 100755 --- a/tools/decode-config.py +++ b/tools/decode-config.py @@ -1,6 +1,6 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -VER = '2.2.0026' +VER = '2.2.0027' """ decode-config.py - Backup/Restore Sonoff-Tasmota configuration data @@ -908,7 +908,19 @@ def MqttFingerprint(value, idx=None): 'use_underscore': ('> sys.stderr, '{styp}{sdelimiter}{sstatus}{slineno}{scolon}{smgs}'.format(\ - styp=typ if typ is not None else '', - sdelimiter=' ' if status is not None and status > 0 and typ is not None else '', + styp=type_ if type_ is not None else '', + sdelimiter=' ' if status is not None and status > 0 and type_ is not None else '', sstatus=status if status is not None and status > 0 else '', - scolon=': ' if typ is not None or line is not None else '', + scolon=': ' if type_ is not None or line is not None else '', smgs=msg, slineno=' (@{:04d})'.format(line) if line is not None else '') -def exit(status=0, msg="end", typ=LogType.ERROR, src=None, doexit=True, line=None): +def exit(status=0, msg="end", type_=LogType.ERROR, src=None, doexit=True, line=None): """ Called when the program should be exit @@ -987,7 +999,7 @@ def exit(status=0, msg="end", typ=LogType.ERROR, src=None, doexit=True, line=Non the exit status program returns to callert @param msg: the msg logged before exit - @param typ: + @param type_: msg type: 'INFO', 'WARNING' or 'ERROR' @param doexit: True to exit program, otherwise return @@ -995,7 +1007,7 @@ def exit(status=0, msg="end", typ=LogType.ERROR, src=None, doexit=True, line=Non if src is not None: msg = '{} ({})'.format(src, msg) - message(msg, typ=typ if status!=ExitCode.OK else LogType.INFO, status=status, line=line) + message(msg, type_=type_ if status!=ExitCode.OK else LogType.INFO, status=status, line=line) exitcode = status if doexit: sys.exit(exitcode) @@ -1451,7 +1463,7 @@ def GetTasmotaHostname(host, port, username=DEFAULTS['source']['username'], pass if "StatusNET" in jsonbody and "Hostname" in jsonbody["StatusNET"]: hostname = jsonbody["StatusNET"]["Hostname"] if args.verbose: - message("Hostname for '{}' retrieved: '{}'".format(host, hostname), typ=LogType.INFO) + message("Hostname for '{}' retrieved: '{}'".format(host, hostname), type_=LogType.INFO) return hostname @@ -1759,7 +1771,7 @@ def ReadWriteConverter(value, fielddef, read=True, raw=False): elif callable(conv): # use as format function return conv(value) except Exception, e: - exit(e[0], e[1], typ=LogType.WARNING, line=inspect.getlineno(inspect.currentframe())) + exit(e[0], e[1], type_=LogType.WARNING, line=inspect.getlineno(inspect.currentframe())) return value @@ -2082,15 +2094,33 @@ def SetFieldValue(fielddef, dobj, addr, value): addr += (bitsize / 8) * formatcnt for _ in range(0, formatcnt): addr -= (bitsize / 8) - val = value & ((2**bitsize) - 1) + maxunsigned = ((2**bitsize) - 1) + maxsigned = ((2**bitsize)>>1)-1 + val = value & maxunsigned + if isinstance(value,int) and value < 0 and val > maxsigned: + val = ((maxunsigned+1)-val) * (-1) if args.debug >= 3: print >> sys.stderr, "SetFieldValue(): Single type - fielddef {}, addr 0x{:04x} value {} singletype {} bitsize {}".format(fielddef,addr,val,singletype,bitsize) - struct.pack_into(singletype, dobj, addr, val) + try: + struct.pack_into(singletype, dobj, addr, val) + except struct.error as e: + exit(ExitCode.RESTORE_DATA_ERROR, + "Single type {} [fielddef={}, addr=0x{:04x}, value={}] - skipped!".format(e,fielddef,addr,val), + type_=LogType.WARNING, + doexit=not args.ignorewarning, + line=inspect.getlineno(inspect.currentframe())) value >>= bitsize else: if args.debug >= 3: print >> sys.stderr, "SetFieldValue(): String type - fielddef {}, addr 0x{:04x} value {} format_ {}".format(fielddef,addr,value,format_) - struct.pack_into(format_, dobj, addr, value) + try: + struct.pack_into(format_, dobj, addr, value) + except struct.error as e: + exit(ExitCode.RESTORE_DATA_ERROR, + "String type {} [fielddef={}, addr=0x{:04x}, value={}} - skipped!".format(e,fielddef,addr,value), + type_=LogType.WARNING, + doexit=not args.ignorewarning, + line=inspect.getlineno(inspect.currentframe())) return dobj @@ -2156,7 +2186,7 @@ def GetField(dobj, fieldname, fielddef, raw=False, addroffset=0): valuemapping = ReadWriteConverter(GetFieldValue(fielddef, dobj, baseaddr+addroffset), fielddef, read=True, raw=raw) else: - exit(ExitCode.INTERNAL_ERROR, "Wrong mapping format definition: '{}'".format(format_), typ=LogType.WARNING, doexit=not args.ignorewarning, line=inspect.getlineno(inspect.currentframe())) + exit(ExitCode.INTERNAL_ERROR, "Wrong mapping format definition: '{}'".format(format_), type_=LogType.WARNING, doexit=not args.ignorewarning, line=inspect.getlineno(inspect.currentframe())) return valuemapping @@ -2199,7 +2229,7 @@ def SetField(dobj, fieldname, fielddef, restore, addroffset=0, filename=""): if isinstance(arraydef, list) and len(arraydef) > 0: offset = 0 if len(restore) > arraydef[0]: - exit(ExitCode.RESTORE_DATA_ERROR, "file '{sfile}', array '{sname}[{selem}]' exceeds max number of elements [{smax}]".format(sfile=filename, sname=fieldname, selem=len(restore), smax=arraydef[0]), typ=LogType.WARNING, doexit=not args.ignorewarning, line=inspect.getlineno(inspect.currentframe())) + exit(ExitCode.RESTORE_DATA_ERROR, "file '{sfile}', array '{sname}[{selem}]' exceeds max number of elements [{smax}]".format(sfile=filename, sname=fieldname, selem=len(restore), smax=arraydef[0]), type_=LogType.WARNING, doexit=not args.ignorewarning, line=inspect.getlineno(inspect.currentframe())) for i in range(0, arraydef[0]): subfielddef = GetSubfieldDef(fielddef) length = GetFieldLength(subfielddef) @@ -2231,7 +2261,7 @@ def SetField(dobj, fieldname, fielddef, restore, addroffset=0, filename=""): try: value = ReadWriteConverter(restore.encode(STR_ENCODING)[0], fielddef, read=False) except Exception, e: - exit(e[0], e[1], typ=LogType.WARNING, line=inspect.getlineno(inspect.currentframe())) + exit(e[0], e[1], type_=LogType.WARNING, line=inspect.getlineno(inspect.currentframe())) valid = False # bool @@ -2239,7 +2269,7 @@ def SetField(dobj, fieldname, fielddef, restore, addroffset=0, filename=""): try: value = ReadWriteConverter(bool(restore), fielddef, read=False) except Exception, e: - exit(e[0], e[1], typ=LogType.WARNING, line=inspect.getlineno(inspect.currentframe())) + exit(e[0], e[1], type_=LogType.WARNING, line=inspect.getlineno(inspect.currentframe())) valid = False # integer @@ -2331,13 +2361,13 @@ def SetField(dobj, fieldname, fielddef, restore, addroffset=0, filename=""): dobj = SetFieldValue(fielddef, dobj, baseaddr+addroffset, value) curvalue = GetFieldValue(fielddef, dobj, baseaddr+addroffset) if prevvalue != curvalue and args.verbose: - message("Value for '{}' changed from {} to {}".format(fieldname, prevvalue, curvalue), typ=LogType.INFO) + message("Value for '{}' changed from {} to {}".format(fieldname, prevvalue, curvalue), type_=LogType.INFO) else: if args.debug >= 2: print >> sys.stderr, "SetField(): Special field '{}' using '{}'/{}{} @{} skipped".format(fieldname, format_, arraydef, bits, hex(baseaddr+addroffset)) else: sformat = "file '{sfile}' - {{'{sname}': {svalue}}} ({serror})"+errformat - exit(ExitCode.RESTORE_DATA_ERROR, sformat.format(sfile=filename, sname=fieldname, serror=err, svalue=_value, smin=min_, smax=max_), typ=LogType.WARNING, doexit=not args.ignorewarning) + exit(ExitCode.RESTORE_DATA_ERROR, sformat.format(sfile=filename, sname=fieldname, serror=err, svalue=_value, smin=min_, smax=max_), type_=LogType.WARNING, doexit=not args.ignorewarning) return dobj @@ -2377,7 +2407,7 @@ def SetCmnd(cmnds, fieldname, fielddef, valuemapping, mappedvalue, addroffset=0, if isinstance(arraydef, list) and len(arraydef) > 0: offset = 0 if len(mappedvalue) > arraydef[0]: - exit(ExitCode.RESTORE_DATA_ERROR, "array '{sname}[{selem}]' exceeds max number of elements [{smax}]".format(sname=fieldname, selem=len(mappedvalue), smax=arraydef[0]), typ=LogType.WARNING, doexit=not args.ignorewarning, line=inspect.getlineno(inspect.currentframe())) + exit(ExitCode.RESTORE_DATA_ERROR, "array '{sname}[{selem}]' exceeds max number of elements [{smax}]".format(sname=fieldname, selem=len(mappedvalue), smax=arraydef[0]), type_=LogType.WARNING, doexit=not args.ignorewarning, line=inspect.getlineno(inspect.currentframe())) for i in range(0, arraydef[0]): subfielddef = GetSubfieldDef(fielddef) length = GetFieldLength(subfielddef) @@ -2443,10 +2473,10 @@ def Bin2Mapping(decode_cfg): # read size should be same as definied in setting if cfg_size > size: # may be processed - exit(ExitCode.DATA_SIZE_MISMATCH, "Number of bytes read does ot match - read {}, expected {} byte".format(cfg_size, size), typ=LogType.ERROR,line=inspect.getlineno(inspect.currentframe())) + exit(ExitCode.DATA_SIZE_MISMATCH, "Number of bytes read does ot match - read {}, expected {} byte".format(cfg_size, size), type_=LogType.ERROR,line=inspect.getlineno(inspect.currentframe())) elif cfg_size < size: # less number of bytes can not be processed - exit(ExitCode.DATA_SIZE_MISMATCH, "Number of bytes read to small to process - read {}, expected {} byte".format(cfg_size, size), typ=LogType.ERROR,line=inspect.getlineno(inspect.currentframe())) + exit(ExitCode.DATA_SIZE_MISMATCH, "Number of bytes read to small to process - read {}, expected {} byte".format(cfg_size, size), type_=LogType.ERROR,line=inspect.getlineno(inspect.currentframe())) # check crc if exists if 'cfg_crc' in setting: @@ -2454,7 +2484,7 @@ def Bin2Mapping(decode_cfg): else: cfg_crc = GetSettingsCrc(decode_cfg) if cfg_crc != GetSettingsCrc(decode_cfg): - exit(ExitCode.DATA_CRC_ERROR, 'Data CRC error, read 0x{:x} should be 0x{:x}'.format(cfg_crc, GetSettingsCrc(decode_cfg)), typ=LogType.WARNING, doexit=not args.ignorewarning,line=inspect.getlineno(inspect.currentframe())) + exit(ExitCode.DATA_CRC_ERROR, 'Data CRC error, read 0x{:x} should be 0x{:x}'.format(cfg_crc, GetSettingsCrc(decode_cfg)), type_=LogType.WARNING, doexit=not args.ignorewarning,line=inspect.getlineno(inspect.currentframe())) # get valuemapping valuemapping = GetField(decode_cfg, None, (setting,0,(None, None, (INTERNAL, None)))) @@ -2525,7 +2555,7 @@ def Mapping2Bin(decode_cfg, jsonconfig, filename=""): SetField(_buffer, name, setting[name], jsonconfig[name], addroffset=0, filename=filename) else: if name != 'header': - exit(ExitCode.RESTORE_DATA_ERROR, "Restore file '{}' contains obsolete name '{}', skipped".format(filename, name), typ=LogType.WARNING, doexit=not args.ignorewarning) + exit(ExitCode.RESTORE_DATA_ERROR, "Restore file '{}' contains obsolete name '{}', skipped".format(filename, name), type_=LogType.WARNING, doexit=not args.ignorewarning) if 'cfg_crc' in setting: crc = GetSettingsCrc(_buffer) @@ -2533,7 +2563,7 @@ def Mapping2Bin(decode_cfg, jsonconfig, filename=""): return _buffer else: - exit(ExitCode.UNSUPPORTED_VERSION,"File '{}', Tasmota configuration version 0x{:x} not supported".format(filename, version), typ=LogType.WARNING, doexit=not args.ignorewarning) + exit(ExitCode.UNSUPPORTED_VERSION,"File '{}', Tasmota configuration version 0x{:x} not supported".format(filename, version), type_=LogType.WARNING, doexit=not args.ignorewarning) return None @@ -2568,12 +2598,12 @@ def Mapping2Cmnd(decode_cfg, valuemapping, filename=""): cmnds = SetCmnd(cmnds, name, setting[name], valuemapping, valuemapping[name], addroffset=0) else: if name != 'header': - exit(ExitCode.RESTORE_DATA_ERROR, "Restore file '{}' contains obsolete name '{}', skipped".format(filename, name), typ=LogType.WARNING, doexit=not args.ignorewarning) + exit(ExitCode.RESTORE_DATA_ERROR, "Restore file '{}' contains obsolete name '{}', skipped".format(filename, name), type_=LogType.WARNING, doexit=not args.ignorewarning) return cmnds else: - exit(ExitCode.UNSUPPORTED_VERSION,"File '{}', Tasmota configuration version 0x{:x} not supported".format(filename, version), typ=LogType.WARNING, doexit=not args.ignorewarning) + exit(ExitCode.UNSUPPORTED_VERSION,"File '{}', Tasmota configuration version 0x{:x} not supported".format(filename, version), type_=LogType.WARNING, doexit=not args.ignorewarning) return None @@ -2608,7 +2638,7 @@ def Backup(backupfile, backupfileformat, encode_cfg, decode_cfg, configmapping): fileformat = "Tasmota" backup_filename = MakeFilename(backupfile, FileType.DMP, configmapping) if args.verbose: - message("Writing backup file '{}' ({} format)".format(backup_filename, fileformat), typ=LogType.INFO) + message("Writing backup file '{}' ({} format)".format(backup_filename, fileformat), type_=LogType.INFO) try: with open(backup_filename, "wb") as backupfp: backupfp.write(encode_cfg) @@ -2620,7 +2650,7 @@ def Backup(backupfile, backupfileformat, encode_cfg, decode_cfg, configmapping): fileformat = "binary" backup_filename = MakeFilename(backupfile, FileType.BIN, configmapping) if args.verbose: - message("Writing backup file '{}' ({} format)".format(backup_filename, fileformat), typ=LogType.INFO) + message("Writing backup file '{}' ({} format)".format(backup_filename, fileformat), type_=LogType.INFO) try: with open(backup_filename, "wb") as backupfp: backupfp.write(struct.pack('