diff --git a/ZgatewayIR.ino b/ZgatewayIR.ino index a4e0e12..7a9c11e 100644 --- a/ZgatewayIR.ino +++ b/ZgatewayIR.ino @@ -191,7 +191,7 @@ void MQTTtoIR(char * topicOri, char * datacallback) { irsend.sendRaw(Raw, j, RawFrequency); signalSent = true; } -#endif + #endif //We look into the subject to see if a special Bits number is defined String topic = topicOri; @@ -204,7 +204,7 @@ void MQTTtoIR(char * topicOri, char * datacallback) { trc(valueBITS); } //We look into the subject to see if a special repeat number is defined - int valueRPT = 0; + uint16_t valueRPT = 0; int pos2 = topic.lastIndexOf(IRRptKey); if (pos2 != -1) { pos2 = pos2 + strlen(IRRptKey); @@ -214,122 +214,227 @@ void MQTTtoIR(char * topicOri, char * datacallback) { } #ifdef ESP8266 // send coolix not available for arduino IRRemote library - #ifdef IR_COOLIX - if (strstr(topicOri, "IR_COOLIX") != NULL){ - if (valueBITS == 0) valueBITS = 24; - irsend.sendCOOLIX(data, valueBITS, valueRPT); - signalSent = true; - } - #endif + #ifdef IR_COOLIX + if (strstr(topicOri, "IR_COOLIX") != NULL){ + if (valueBITS == 0) valueBITS = COOLIX_BITS; + irsend.sendCOOLIX(data, valueBITS, valueRPT); + signalSent = true; + } + #endif #endif if (strstr(topicOri, "IR_NEC") != NULL || strstr(topicOri, subjectMQTTtoIR) != NULL ){ - if (valueBITS == 0) valueBITS = 32; -#ifdef ESP8266 - irsend.sendNEC(data, valueBITS, valueRPT); -#else - for (int i=0; i <= valueRPT; i++) irsend.sendNEC(data, valueBITS); -#endif + if (valueBITS == 0) valueBITS = NEC_BITS; + #ifdef ESP8266 + irsend.sendNEC(data, valueBITS, valueRPT); + #else + for (int i=0; i <= valueRPT; i++) irsend.sendNEC(data, valueBITS); + #endif signalSent = true; } #ifdef IR_Whynter if (strstr(topicOri, "IR_Whynter") != NULL){ - if (valueBITS == 0) valueBITS = 32; -#ifdef ESP8266 - irsend.sendWhynter(data, valueBITS, valueRPT); -#else - for (int i=0; i <= valueRPT; i++) irsend.sendWhynter(data, valueBITS); -#endif + if (valueBITS == 0) valueBITS = WHYNTER_BITS; + #ifdef ESP8266 + irsend.sendWhynter(data, valueBITS, valueRPT); + #else + for (int i=0; i <= valueRPT; i++) irsend.sendWhynter(data, valueBITS); + #endif signalSent = true; } #endif #ifdef IR_LG if (strstr(topicOri, "IR_LG") != NULL){ - if (valueBITS == 0) valueBITS = 28; -#ifdef ESP8266 - irsend.sendLG(data, valueBITS, valueRPT); -#else - for (int i=0; i <= valueRPT; i++) irsend.sendLG(data, valueBITS); -#endif + if (valueBITS == 0) valueBITS = LG_BITS; + #ifdef ESP8266 + irsend.sendLG(data, valueBITS, valueRPT); + #else + for (int i=0; i <= valueRPT; i++) irsend.sendLG(data, valueBITS); + #endif signalSent = true; } #endif #ifdef IR_Sony if (strstr(topicOri, "IR_Sony") != NULL){ - if (valueBITS == 0) valueBITS = 12; + if (valueBITS == 0) valueBITS = SONY_12_BITS; if (valueRPT == 0) valueRPT = 2; -#ifdef ESP8266 - irsend.sendSony(data, valueBITS, valueRPT); -#else - for (int i=0; i <= valueRPT; i++) irsend.sendSony(data, valueBITS); -#endif + #ifdef ESP8266 + irsend.sendSony(data, valueBITS, valueRPT); + #else + for (int i=0; i <= valueRPT; i++) irsend.sendSony(data, valueBITS); + #endif signalSent = true; } #endif #ifdef IR_DISH if (strstr(topicOri, "IR_DISH") != NULL){ - if (valueBITS == 0) valueBITS = 16; -#ifdef ESP8266 - irsend.sendDISH(data, valueBITS, valueRPT); -#else - for (int i=0; i <= valueRPT; i++) irsend.sendDISH(data, valueBITS); -#endif + if (valueBITS == 0) valueBITS = DISH_BITS; + #ifdef ESP8266 + irsend.sendDISH(data, valueBITS, valueRPT); + #else + for (int i=0; i <= valueRPT; i++) irsend.sendDISH(data, valueBITS); + #endif signalSent = true; } #endif #ifdef IR_RC5 if (strstr(topicOri, "IR_RC5") != NULL){ - if (valueBITS == 0) valueBITS = 12; -#ifdef ESP8266 - irsend.sendRC5(data, valueBITS, valueRPT); -#else - for (int i=0; i <= valueRPT; i++) irsend.sendRC5(data, valueBITS); -#endif + if (valueBITS == 0) valueBITS = RC5_BITS; + #ifdef ESP8266 + irsend.sendRC5(data, valueBITS, valueRPT); + #else + for (int i=0; i <= valueRPT; i++) irsend.sendRC5(data, valueBITS); + #endif + signalSent = true; + } + #endif + #ifdef IR_RC6 + if (strstr(topicOri, "IR_RC6") != NULL){ + if (valueBITS == 0) valueBITS = RC6_MODE0_BITS; + #ifdef ESP8266 + irsend.sendRC6(data, valueBITS, valueRPT); + #else + for (int i=0; i <= valueRPT; i++) irsend.sendRC6(data, valueBITS); + #endif signalSent = true; } #endif #ifdef IR_Sharp if (strstr(topicOri, "IR_Sharp") != NULL){ - if (valueBITS == 0) valueBITS = 15; -#ifdef ESP8266 - irsend.sendSharpRaw(data, valueBITS, valueRPT); -#else - for (int i=0; i <= valueRPT; i++) irsend.sendSharpRaw(data, valueBITS); -#endif + if (valueBITS == 0) valueBITS = SHARP_BITS; + #ifdef ESP8266 + irsend.sendSharpRaw(data, valueBITS, valueRPT); + #else + for (int i=0; i <= valueRPT; i++) irsend.sendSharpRaw(data, valueBITS); + #endif signalSent = true; } #endif #ifdef IR_SAMSUNG if (strstr(topicOri, "IR_SAMSUNG") != NULL){ - if (valueBITS == 0) valueBITS = 32; -#ifdef ESP8266 - irsend.sendSAMSUNG(data, valueBITS, valueRPT); -#else - for (int i=0; i <= valueRPT; i++) irsend.sendSAMSUNG(data, valueBITS); -#endif + if (valueBITS == 0) valueBITS = SAMSUNG_BITS; + #ifdef ESP8266 + irsend.sendSAMSUNG(data, valueBITS, valueRPT); + #else + for (int i=0; i <= valueRPT; i++) irsend.sendSAMSUNG(data, valueBITS); + #endif + signalSent = true; + } + #endif + #ifdef IR_JVC + if (strstr(topicOri, "IR_JVC") != NULL){ + if (valueBITS == 0) valueBITS = JVC_BITS; + if (valueRPT == 0) valueRPT = 2; + #ifdef ESP8266 + irsend.sendJVC(data, valueBITS, valueRPT); + #else + for (int i=0; i <= valueRPT; i++) irsend.sendJVC(data, valueBITS); + #endif signalSent = true; } #endif #ifdef IR_PANASONIC if (strstr(topicOri, "IR_PANASONIC") != NULL){ - if (valueBITS == 0) valueBITS = PanasonicBits; if (valueRPT == 0) valueRPT = 2; -#ifdef ESP8266 - irsend.sendPanasonic(PanasonicAddress, data, valueBITS, valueRPT); -#else - for (int i=0; i <= valueRPT; i++) irsend.sendPanasonic(PanasonicAddress, data); -#endif + #ifdef ESP8266 + if (valueBITS == 0) valueBITS = PANASONIC_BITS; + irsend.sendPanasonic(PanasonicAddress, data, valueBITS, valueRPT); + #else + for (int i=0; i <= valueRPT; i++) irsend.sendPanasonic(PanasonicAddress, data); + #endif signalSent = true; } #endif -#ifdef ESP8266 // IR_RCMM not available on arduino +#ifdef ESP8266 // sendings not available on arduino #ifdef IR_RCMM if (strstr(topicOri, "IR_RCMM") != NULL){ - if (valueBITS == 0) valueBITS = 32; + if (valueBITS == 0) valueBITS = RCMM_BITS; irsend.sendRCMM(data, valueBITS, valueRPT); signalSent = true; } #endif + #ifdef IR_DENON + if (strstr(topicOri, "IR_DENON") != NULL){ + if (valueBITS == 0) valueBITS = DENON_BITS; + irsend.sendDenon(data, valueBITS, valueRPT); + signalSent = true; + } + #endif + #ifdef IR_GICABLE + if (strstr(topicOri, "IR_GICABLE") != NULL){ + if (valueBITS == 0) valueBITS = GICABLE_BITS; + if (valueRPT == 0) valueRPT = std::max(valueRPT, (uint16_t) GICABLE_MIN_REPEAT); + irsend.sendGICable(data, valueBITS, valueRPT); + signalSent = true; + } + #endif + #ifdef IR_SHERWOOD + if (strstr(topicOri, "IR_SHERWOOD") != NULL){ + if (valueBITS == 0) valueBITS = SHERWOOD_BITS; + if (valueRPT == 0) valueRPT = std::max(valueRPT, (uint16_t) SHERWOOD_MIN_REPEAT); + irsend.sendSherwood(data, valueBITS, valueRPT); + signalSent = true; + } + #endif + #ifdef IR_MITSUBISHI + if (strstr(topicOri, "IR_MITSUBISHI") != NULL){ + if (valueBITS == 0) valueBITS = MITSUBISHI_BITS; + if (valueRPT == 0) valueRPT = std::max(valueRPT, (uint16_t) MITSUBISHI_MIN_REPEAT); + irsend.sendMitsubishi(data, valueBITS, valueRPT); + signalSent = true; + } + #endif + #ifdef IR_NIKAI + if (strstr(topicOri, "IR_NIKAI") != NULL){ + if (valueBITS == 0) valueBITS = NIKAI_BITS; + irsend.sendNikai(data, valueBITS, valueRPT); + signalSent = true; + } + #endif + #ifdef IR_MIDEA + if (strstr(topicOri, "IR_MIDEA") != NULL){ + if (valueBITS == 0) valueBITS = MIDEA_BITS; + irsend.sendMidea(data, valueBITS, valueRPT); + signalSent = true; + } + #endif + #ifdef IR_MAGIQUEST + if (strstr(topicOri, "IR_MAGIQUEST") != NULL){ + if (valueBITS == 0) valueBITS = MAGIQUEST_BITS; + irsend.sendMagiQuest(data, valueBITS, valueRPT); + signalSent = true; + } + #endif + #ifdef IR_LASERTAG + if (strstr(topicOri, "IR_LASERTAG") != NULL){ + if (valueBITS == 0) valueBITS = LASERTAG_BITS; + irsend.sendLasertag(data, valueBITS, valueRPT); + signalSent = true; + } + #endif + #ifdef IR_CARRIER_AC + if (strstr(topicOri, "IR_CARRIER_AC") != NULL){ + if (valueBITS == 0) valueBITS = CARRIER_AC_BITS; + irsend.sendCarrierAC(data, valueBITS, valueRPT); + signalSent = true; + } + #endif + #ifdef IR_MITSUBISHI2 + if (strstr(topicOri, "IR_MITSUBISHI2") != NULL){ + if (valueBITS == 0) valueBITS = MITSUBISHI_BITS; + if (valueRPT == 0) valueRPT = std::max(valueRPT, (uint16_t) MITSUBISHI_MIN_REPEAT); + irsend.sendMitsubishi2(data, valueBITS, valueRPT); + signalSent = true; + } + #endif + #ifdef IR_AIWA_RC_T501 + if (strstr(topicOri, "IR_MITSUBISHI") != NULL){ + if (valueBITS == 0) valueBITS = AIWA_RC_T501_BITS; + if (valueRPT == 0) valueRPT = std::max(valueRPT, (uint16_t) AIWA_RC_T501_MIN_REPEAT); + irsend.sendAiwaRCT501(data, valueBITS, valueRPT); + signalSent = true; + } + #endif #endif if (signalSent){ // we acknowledge the sending by publishing the value to an acknowledgement topic, for the moment even if it is a signal repetition we acknowledge also diff --git a/config_IR.h b/config_IR.h index 8158eef..7aff8b9 100644 --- a/config_IR.h +++ b/config_IR.h @@ -42,7 +42,6 @@ #define pubIRunknownPrtcl false // key to avoid mqtt publication of unknown IR protocol (set to true if you want to publish unknown protocol) #define PanasonicAddress 0x4004 // Panasonic address (Pre data) -#define PanasonicBits 48U // Panasonic nr of bits (Pre data) #ifdef ESP8266 //IR supported protocols on ESP8266, all supported per default #define IR_GC @@ -53,19 +52,33 @@ #define IR_Sony #define IR_DISH #define IR_RC5 + #define IR_RC6 #define IR_Sharp #define IR_SAMSUNG #define IR_PANASONIC #define IR_RCMM + #define IR_MITSUBISHI + #define IR_GICABLE + #define IR_MITSUBISHI2 + #define IR_LASERTAG + #define IR_CARRIER_AC + #define IR_MIDEA + #define IR_NIKAI + #define IR_SHERWOOD + #define IR_DENON + #define IR_AIWA_RC_T501 + #define IR_JVC #elif ESP32 #define IR_Raw #define IR_COOLIX #define IR_Whynter + #define IR_Sony #define IR_LG #define IR_DISH #define IR_RC5 #define IR_Sharp #define IR_SAMSUNG + #define IR_PANASONIC #else //IR supported protocols on arduino uncomment if you want to send with this protocol, NEC protocol is available per default //#define IR_COOLIX //#define IR_Whynter diff --git a/lib/IRremoteESP8266/.github/CONTRIBUTING.md b/lib/IRremoteESP8266/.github/CONTRIBUTING.md new file mode 100644 index 0000000..9614d90 --- /dev/null +++ b/lib/IRremoteESP8266/.github/CONTRIBUTING.md @@ -0,0 +1,82 @@ +# Contributing to the IRremoteESP8266 library + +:+1::tada: First off, thanks for taking the time to contribute! :tada::+1: + +The following is a set of guidelines for contributing to the IRremoteESP8266 library, hosted on GitHub. These are guidelines, [not rules](http://imgur.com/mSHi8). Use your best judgment, and feel free to propose changes to this document in a pull request. + +#### Table Of Contents + +[Code of Conduct](#code-of-conduct) + +[How Can I Contribute?](#how-can-i-contribute) + * [Reporting Bugs](#reporting-bugs) + * [Pull Requests](#pull-requests) + +[Styleguides](#styleguides) + * [Google C++ Style Guide](https://google.github.io/styleguide/cppguide.html) + * [Git Commit Messages](#git-commit-messages) + + +## Code of Conduct + +This project and everyone participating in it is governed by the principle of ["Be excellent to each other"](http://www.imdb.com/title/tt0096928/quotes). That's it. TL;DR: _Don't be a jerk._ + +## How Can I Contribute? + +### Reporting Bugs + +This section guides you through submitting a bug report for the library. Following these guidelines helps maintainers and the community understand your report :pencil:, reproduce the behavior :computer: :computer:, and find related reports :mag_right:. + +Before creating bug reports, please check [this list](#before-submitting-a-bug-report) as you might find out that you don't need to create one. When you are creating a bug report, please [include as much detail as possible](#how-do-i-submit-a-good-bug-report). Fill out [the required template](issue_template.md), the information it asks for helps us resolve issues faster. + +> **Note:** If you find a **Closed** issue that seems like it's the same thing that you're experiencing, open a new issue and include a link to the original issue in the body of your new one. + +#### Before Submitting A Bug Report + +* **Check the [Troubleshooting Guide](https://github.com/markszabo/IRremoteESP8266/wiki/Troubleshooting-Guide).** You might be able to find the cause of the problem and fix it yourself. Most importantly, check if you can reproduce the problem in the latest version (a.k.a. 'master') of the library. +* **Perform a [cursory search](https://github.com/issues?q=+is%3Aissue+repo%3Amarkszabo/IRremoteESP8266)** to see if the problem is already reported. If it has **and the issue is still open**, add a comment to the existing issue instead of opening a new one. + +#### How Do I Submit A (Good) Bug Report? + +Bugs are tracked as [GitHub issues](https://guides.github.com/features/issues/). Create an issue and provide the following information by filling in [the template](issue_template.md). + +Explain the problem and include any additional details to help maintainers reproduce the problem: + +* **Use a clear and descriptive title** for the issue to identify the problem. +* **Describe the exact steps which reproduce the problem** in as much detail as possible. +* **Provide specific examples to demonstrate the steps**. Include links to files or GitHub projects, or copy/pasteable snippets, which you use in those examples. If you're providing snippets in the issue, use [Markdown code blocks](https://help.github.com/articles/markdown-basics/#multiple-lines). +* **Describe the behavior you observed after following the steps** and point out what exactly is the problem with that behavior. +* **Explain which behavior you expected to see instead and why.** +* **If the problem wasn't triggered by a specific action**, describe what you were doing before the problem happened and share more information using the guidelines below. + +Provide more context by answering these questions: + +* **Can you reproduce the problem in one of the code examples?** +* **Did the problem start happening recently** (e.g. after updating to a new version of Arduino or the library) or was this always a problem? +* If the problem started happening recently, **can you reproduce the problem in an older version of the library?** What's the most recent version in which the problem doesn't happen? You can download older versions of the library from [the releases page](https://github.com/markszabo/IRremoteESP8266/releases). +* **Can you reliably reproduce the issue?** If not, provide details about how often the problem happens and under which conditions it normally happens. + +Include details about your configuration, circuit and environment: + +* **Which version of the library are you using?** You can get the exact version by inspecting the `library.json` file in the root directory of the library. +* **What board are you running this on?** + +### Pull Requests + +* Do not include issue numbers in the PR title +* Include as much data and comments as practicle. +* Follow the [C++ style guide](https://google.github.io/styleguide/cppguide.html). +* Please write or ensure Unit Tests cover the change you are making, if you can. +* End all files with a newline +* Avoid platform-dependent code. +* Use c98 types where possible for better portablity. +* In almost all cases, code & documentation should be peer-reviewed by at least one other contributor. +* The code should pass all the existing testing infrastructure in Travis. e.g. Unit tests, cpplint, and basic compilation. +* State if you have tested this under real conditions if you have, and what other tests you may have carried out. + +### Git Commit Messages + +* Limit the first line to 72 characters or less +* Reference issues and pull requests liberally after the first line +* Humour is always acceptable. Be liberal with it. ;-) +* While not required, a comprehensive description of all the changes in the PR is best. diff --git a/lib/IRremoteESP8266/.github/Contributors.md b/lib/IRremoteESP8266/.github/Contributors.md new file mode 100644 index 0000000..151a0c0 --- /dev/null +++ b/lib/IRremoteESP8266/.github/Contributors.md @@ -0,0 +1,16 @@ +## Contributors of this project +### Main contributors & maintainers +- [Mark Szabo](https://github.com/markszabo/) : Initial IR sending on ESP8266 +- [Sébastien Warin](https://github.com/sebastienwarin/) (http://sebastien.warin.fr) : Initial IR receiving on ESP8266 +- [David Conran](https://github.com/crankyoldgit/) +- [Roi Dayan](https://github.com/roidayan/) +- [Marcos de Alcântara Marinho](https://github.com/marcosamarinho/) +- [Massimiliano Pinto](https://github.com/pintomax/) +- [Darsh Patel](https://github.com/darshkpatel/) +- [Jonny Graham](https://github.com/jonnygraham/) +- [Stu Fisher](https://github.com/stufisher/) +- [Jorge Cisneros](https://github.com/jorgecis/) + +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/.github/issue_template.md b/lib/IRremoteESP8266/.github/issue_template.md new file mode 100644 index 0000000..024a039 --- /dev/null +++ b/lib/IRremoteESP8266/.github/issue_template.md @@ -0,0 +1,42 @@ +_(Please use this template for reporting issues. You can delete what ever is not relevant. Giving us this information will help us help you faster. Please also read the [FAQ](https://github.com/markszabo/IRremoteESP8266/wiki/Frequently-Asked-Questions) & [Troubleshooting Guide](https://github.com/markszabo/IRremoteESP8266/wiki/Troubleshooting-Guide). Your problem may already have an answer there.)_ + +### Version/revison of the library used +_Typically located in the `library.json` & `src/IRremoteESP8266.h` files in the root directory of the library. +e.g. v2.0.0, or 'master' as at 1st of June, 2017. etc._ + +### Expected behavior +_What steps did you do and what should it have done?_ + +e.g. +1. Initialise the IRsend class. +2. IRsend.sendFoobar(0xdeadbeef); +3. Foobar branded BBQ turns on and cooks me some ribs. + +### Actual behavior +_What steps did you do, and what did or didn't actually happen?_ + +e.g. +1. Initialise the IRsend class. +2. IRsend.sendFoobar(0xdeadbeef); +3. Foobar BBQ went into Cow(er)-saving mode and fried me a couple of eggs instead. + +#### Output of raw data from IRrecvDumpV2.ino (if applicable) +_Include some raw dumps of what the device saw._ + +### Steps to reproduce the behavior +_What can we do to (pref. reliably) repeat what is happening?_ + +#### Example code used +_Include all relevant code snippets or links to the actual code files. Tip: [How to quote your code so it is still readable](https://github.com/adam-p/markdown-here/wiki/Markdown-Cheatsheet#code)._ + +#### Circuit diagram and hardware used (if applicable) +_Link to an image of the circuit diagram used. Part number of the IR receiver module etc._ + +### I have followed the steps in the [Troubleshooting Guide](https://github.com/markszabo/IRremoteESP8266/wiki/Troubleshooting-Guide) & read the [FAQ](https://github.com/markszabo/IRremoteESP8266/wiki/Frequently-Asked-Questions) +_Yes/No._ + +### Has this library/code previously worked as expected for you? +_Yes/No. If "Yes", which version last worked for you?_ + +### Other useful information +_More information is always welcome. Be verbose._ diff --git a/lib/IRremoteESP8266/.gitignore b/lib/IRremoteESP8266/.gitignore new file mode 100644 index 0000000..c9dfea7 --- /dev/null +++ b/lib/IRremoteESP8266/.gitignore @@ -0,0 +1,42 @@ +#----------------------------------------# +# .gitingore for IRremoteESP8266 library # +#----------------------------------------# + +### Files to ignore. + +## Editors +# vi/vim +**/*.swp + +## Build environments +# Platformio +**/.pioenvs/ +**/.piolibdeps/ +**/.clang_complete +**/.gcc-flags.json +examples/**/lib +examples/**/.travis.yml +examples/**/.gitignore +lib/readme.txt +lib/googletest/**/* + +# GCC pre-compiled headers. +**/*.gch + +# Python compiled files +**/*.pyc + +# Unit Test builds +test/*.o +test/*.a +test/*_test + +# Tools builds +tools/*.o +tools/*.a +tools/gc_decode + +.pioenvs +.piolibdeps +.clang_complete +.gcc-flags.json diff --git a/lib/IRremoteESP8266/.gitmodules b/lib/IRremoteESP8266/.gitmodules new file mode 100644 index 0000000..80925b8 --- /dev/null +++ b/lib/IRremoteESP8266/.gitmodules @@ -0,0 +1,3 @@ +[submodule "lib/googletest"] + path = lib/googletest + url = https://github.com/google/googletest.git diff --git a/lib/IRremoteESP8266/.style.yapf b/lib/IRremoteESP8266/.style.yapf new file mode 100644 index 0000000..65fa0ee --- /dev/null +++ b/lib/IRremoteESP8266/.style.yapf @@ -0,0 +1,3 @@ +[style] +based_on_style: google +indent_width: 2 diff --git a/lib/IRremoteESP8266/.travis.yml b/lib/IRremoteESP8266/.travis.yml new file mode 100644 index 0000000..4331425 --- /dev/null +++ b/lib/IRremoteESP8266/.travis.yml @@ -0,0 +1,61 @@ +language: c +env: + - BD=esp8266:esp8266:nodemcuv2:CpuFrequency=80,FlashSize=4M3M + - BD=esp8266:esp8266:d1_mini:CpuFrequency=80,FlashSize=4M3M +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 + - 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 + - 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 + - arduino --pref "compiler.warning_level=all" --save-prefs + - sudo apt-get install jq + - sudo pip install pylint +script: + # Check that everything compiles. + - arduino --verify --board $BD $PWD/examples/IRrecvDemo/IRrecvDemo.ino + - arduino --verify --board $BD $PWD/examples/IRGCSendDemo/IRGCSendDemo.ino + - arduino --verify --board $BD $PWD/examples/IRGCTCPServer/IRGCTCPServer.ino + - arduino --verify --board $BD $PWD/examples/IRServer/IRServer.ino + - arduino --verify --board $BD $PWD/examples/IRrecvDumpV2/IRrecvDumpV2.ino + - arduino --verify --board $BD $PWD/examples/IRsendDemo/IRsendDemo.ino + - arduino --verify --board $BD $PWD/examples/JVCPanasonicSendDemo/JVCPanasonicSendDemo.ino + - arduino --verify --board $BD $PWD/examples/TurnOnDaikinAC/TurnOnDaikinAC.ino + - arduino --verify --board $BD $PWD/examples/TurnOnFujitsuAC/TurnOnFujitsuAC.ino + - arduino --verify --board $BD $PWD/examples/TurnOnKelvinatorAC/TurnOnKelvinatorAC.ino + - arduino --verify --board $BD $PWD/examples/TurnOnMitsubishiAC/TurnOnMitsubishiAC.ino + - arduino --verify --board $BD $PWD/examples/IRsendProntoDemo/IRsendProntoDemo.ino + - arduino --verify --board $BD $PWD/examples/TurnOnTrotecAC/TurnOnTrotecAC.ino + - arduino --verify --board $BD $PWD/examples/LGACSend/LGACSend.ino + - 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 + # Also check the tools programs compile. + - (cd tools; make all) + # Check for lint issues. + - shopt -s nullglob + - python cpplint.py --extensions=c,cc,cpp,ino --headers=h,hpp {src,test,tools}/*.{h,c,cc,cpp,hpp,ino} examples/*/*.{h,c,cc,cpp,hpp,ino} + - pylint {src,test,tools}/*.py + - shopt -u nullglob + # Build and run the unit tests. + - (cd test; make run) + - (cd tools; make run_tests) + # Check the version numbers match. + - LIB_VERSION=$(egrep "^#define\s+_IRREMOTEESP8266_VERSION_\s+" src/IRremoteESP8266.h | cut -d\" -f2) + - test ${LIB_VERSION} == "$(jq -r .version library.json)" + - grep -q "^version=${LIB_VERSION}$" library.properties + +notifications: + email: + on_success: change + on_failure: change diff --git a/lib/IRremoteESP8266/README.md b/lib/IRremoteESP8266/README.md index ea916b2..ad039a9 100644 --- a/lib/IRremoteESP8266/README.md +++ b/lib/IRremoteESP8266/README.md @@ -1,71 +1,63 @@ # IRremote ESP8266 Library [![Build Status](https://travis-ci.org/markszabo/IRremoteESP8266.svg?branch=master)](https://travis-ci.org/markszabo/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 Arduino framework (https://github.com/esp8266/Arduino) +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.2 Now Available -Version 2.2 of the library is now available. This is a significant internal change to existing versions. +## v2.4.2 Now Available +Version 2.4.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. -## Upgrading from versions prior to v2.0 -You will need to change your pre-v2.0 code slightly to work with post-v2.0 versions of the library. You can read more about the changes on our [wiki](https://github.com/markszabo/IRremoteESP8266/wiki/Upgrading-to-v2.0) page. -Please [report an issue](https://github.com/markszabo/IRremoteESP8266/issues/new) if you find any problems with the documentation or the library itself. +#### Upgrading from pre-v2.0 +Usage of the library slight changed at 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. ## Troubleshooting Before reporting an issue or asking for help, please try to follow our [Troubleshooting Guide](https://github.com/markszabo/IRremoteESP8266/wiki/Troubleshooting-Guide) first. ## Frequently Asked Questions -Some common answers to common questions and problems can be found on our [F.A.Q. wiki page](https://github.com/markszabo/IRremoteESP8266/wiki/Frequently-Asked-Questions). - -## History -This library was originally based on Ken Shirriff's work (https://github.com/shirriff/Arduino-IRremote/) - -[Mark Szabo](https://github.com/markszabo/IRremoteESP8266) has updated the IRsend class to work on ESP8266 and [Sebastien Warin](https://github.com/sebastienwarin/IRremoteESP8266) the receiving & decoding part (IRrecv class). +Some common answers to common questions and problems are on our [F.A.Q. wiki page](https://github.com/markszabo/IRremoteESP8266/wiki/Frequently-Asked-Questions). ## Installation -1. Click "Download ZIP" -2. Extract the downloaded zip file -3. Rename the extracted folder to "IRremoteESP8266" -4. Move this folder to your libraries directory (under windows: C:\Users\YOURNAME\Documents\Arduino\libraries\) -5. Restart your Arduino ide -6. Check out the examples +##### Official releases via the Arduino IDE v1.8+ (Windows & Linux) +1. Click the _"Sketch"_ -> _"Include Library"_ -> _"Manage Libraries..."_ Menu items. +1. Enter `IRremoteESP8266` into the _"Filter your search..."_ top right search box. +1. Click on the IRremoteESP8266 result of the search. +1. Select the version you wish to install and click _"Install"_. -###### Using Git to install library ( Linux ) +##### Manual Installation for Windows +1. Click on _"Clone or Download"_ button, then _"[Download ZIP](https://github.com/markszabo/IRremoteESP8266/archive->master.zip)"_ on the page. +1. Extract the contents of the downloaded zip file. +1. Rename the extracted folder to _"IRremoteESP8266"_. +1. Move this folder to your libraries directory. (under windows: `C:\Users\YOURNAME\Documents\Arduino\libraries\`) +1. Restart your Arduino IDE. +1. Check out the examples. + +##### Using Git to install library ( Linux ) ``` cd ~/Arduino/libraries git clone https://github.com/markszabo/IRremoteESP8266.git ``` ###### To Update to the latest version of the library -` +``` cd ~/Arduino/libraries/IRremoteESP8266 && git pull -` - -## Unit Tests -The [Unit Tests](https://en.wikipedia.org/wiki/Unit_testing) under the test/ directory are for a Unix machine, **not** the micro-controller (ESP8266). -This allows execution under Travis and on the developer's machine. -We can do this from v2.0 of the library onwards, as everything now uses c98-style type definitions. -e.g. uint16_t etc. -Any Arduino/ESP8266 specific code needs to be disabled using something similar to the following lines: -``` -#ifndef UNIT_TEST - -#endif -``` - -This is not a perfect situation as we can not obviously emulate hardware specific features and differences. e.g. Interrupts, GPIOs, CPU instruction timing etc, etc. - -If you want to run all the tests yourself, try the following: -``` -$ cd test -$ make run ``` ## Contributing If you want to [contribute](.github/CONTRIBUTING.md#how-can-i-contribute) to this project, consider: - [Report](.github/CONTRIBUTING.md#reporting-bugs) bugs and errors - Ask for enhancements +- Improve our documentation - [Create issues](.github/CONTRIBUTING.md#reporting-bugs) and [pull requests](.github/CONTRIBUTING.md#pull-requests) - Tell other people about this library ## Contributors Available [here](.github/Contributors.md) + +## Library History +This library was originally based on Ken Shirriff's work (https://github.com/shirriff/Arduino-IRremote/) + +[Mark Szabo](https://github.com/markszabo/IRremoteESP8266) has updated the IRsend class to work on ESP8266 and [Sebastien Warin](https://github.com/sebastienwarin/IRremoteESP8266) the receiving & decoding part (IRrecv class). + +As of v2.0, the library was almost entirely re-written with the ESP8266's resources in mind. diff --git a/lib/IRremoteESP8266/ReleaseNotes.md b/lib/IRremoteESP8266/ReleaseNotes.md new file mode 100644 index 0000000..23bb971 --- /dev/null +++ b/lib/IRremoteESP8266/ReleaseNotes.md @@ -0,0 +1,229 @@ +# Release Notes + +## _v2.4.2 (20180601)_ + +**[Bug Fixes]** +- Timing Fix: Update the period offset compensation. + +**[Features]** +- Improvements for IRMQTTServer example (#466) + + +## _v2.4.1 (20180520)_ + +**[Bug Fixes]** +- Fix crash in IRMQTTServer when compiled under Arduino IDE. (#455) +- Default bit length not set for RCMM in IRMQTTServer example. (#456) +- Bad acknowledgements for some A/C protocols in IRMQTTServer example. (#460) + +**[Features]** +- Allow disabling the use of delay() calls. (#450) +- Initial support for G.I. Cable protocol. (#458) +- Support of Hitachi A/C 13 & 53 byte messages. (#461) + +**[Misc]** +- Auto Analyse Raw Data script converted to Python. (#454) + +## _v2.4.0 (20180407)_ + +**[Bug Fixes]** +- Add missing WiFi.begin() call to IRGCTCPServer example. (#433) +- Add missing sendHaierAC() to IRMQTTServer example. (#434 & #444) +- Make mqtt clientid unique in IRMQTTServer example. (#444) + +**[Features]** + +- Initial Mitsubishi projector protocol support. (#442) +- Experimental support of Hitachi A/C messages. (#445) +- Improve transmission pulse modulation support. + Allow disabling of transmission frequency modulation.(#439) + +**[Misc]** +- IRMQTTServer example improvements. (#444) + + +## _v2.3.3 (20180302)_ + +**[Bug Fixes]** +- Ensure the IR LED is off before we start. (#405) + +**[Features]** +- Experimental decode support for Gree HVAC units (#397) +- Initial support for Haier A/Cs. (#409) +- Improve timing accuracy of unit tests. (#403) +- Rework matchData() to handle equal total data bit time protocols. (#408) + +**[Misc]** +- Add startup text to IRrecvDumpV2 and IRrecvDemo (#412) +- Tweak timings on Fujitsu A/C header (#418) +- AutoAnalyseRawData.sh: Add some support for handling larger than 64 bit codes. (#419) +- Use better comments for send GPIO in examples. (#425) + + +## _v2.3.2 (20180126)_ + +**[Bug Fixes]** +- Integer underflow caused device not to respond in `sendJVC()` (#401) + +**[Features]** +- Initial support for sending & receiving Carrier HVAC codes. (#387) +- Add Pronto HEX code support to _gc_decode_ tool. (#388) + +**[Misc]** +- Make mDNS independent of MQTT in IRMQTTServer example code. (#390 #391) + + +## _v2.3.1 (20171229)_ + +**[Bug Fixes]** +- Setting `#define SEND_FUJITSU_AC false` caused a compilation error (#375) +- Integer underflow caused huge `space()` in `sendGeneric()` (#381) + +**[Features]** +- Support sending & receiving Lasertag codes. (#374) +- Reduce the library footprint by using a new `sendGeneric()` routine. (#373) + +**[Misc]** +- Lots of grammar & typo fixes. (#378) +- Update keywords.txt for Arduino IDE users (#371) +- Update pins in examples so they are compatible with Adafruit boards. (#383) + + +## _v2.3.0 (20171208)_ + +**[Bug Fixes]** +- Panasonic-based protocols had incorrect message gap. (#358) +- Formatting error for large rawData values in example code. (#355) +- Off-by-one error in payload_copy malloc. (#337) +- Off-by-one error in unit test helper routines (#363) + +**[Features]** +- Support sending and receiving Midea A/C codes. +- Support for receiving Kelvinator A/C codes. (#332) +- Support more operation features for Daikin A/Cs. +- Support for decoding Daikin A/Cs. +- Support sending and receiving Toshiba A/Cs. (#333) +- Support sending and receiving AR-DB1 Fujitsu A/C codes. (#367) +- Add new AutoAnalyseRawData.sh & RawToGlobalCache.sh tools (#345) (#343) +- Support for MagiQuest wands. (#365) + +**[Misc]** +- Add checksum verification to Kelvinator A/C decodes. (#348) +- Changes to the threshold reporting of UNKNOWN messages (#347) +- Major re-work of Daikin A/C support. +- Sending for all A/Cs added to MQTT example code. +- MQTT example code improvements. (#334) +- IRrecvDumpV2 significant output improvements. (#363) +- Improved unit test coverage for the library. + + +## _v2.2.1 (20171025)_ + +**[Features]** +- Support for sending and decoding Nikai TV messages. (#311, #313) +- gc_decode: External utility to decode Global Cache codes. (#308, #312) +- IRMQTTServer: Example code to send IR messages via HTTP & MQTT. (#316, #323) +- Improve converting 64bit values to hexadecimal. (#318) + +**[Misc]** +- IRrecvDump.ino code is now deprecated. Use IRrecvDumpV2.ino instead. (#314) + + +## _v2.2.0 (20170922)_ + +**[Bug Fixes]** +- Add printing output of RC-MM and RC-5X protocols in example code. (#284) +- LG timing improvements based on observations (#291) + +**[Features]** +- Automatic capture timing calibration for some protocols. (#268) +- Support for creating & sending Trotec AC codes. (#279) +- Support for creating & sending Argo Ulisse 13 DCI codes. (#280 #300) +- Move to 2 microsecond timing resolution for capture of codes. (#287) +- Capture buffer changes: +- Size at runtime. (#276) +- Message timeout at runtime. (#294) +- Simplify creating & using a second buffer (#303) +- New example code: + - Trotec A/C (#279) + - LG A/C units (#289) + - Argo Ulisse 13 DCI codes. (#300) + + +## _v2.1.1 (20170711)_ + +**[Bug Fixes]** +- GlobalCache incorrectly using hardware offset for period calc. (#267) + +**[Features]** +- Support reporting of 'NEC'-like 32-bit protocols. e.g. Apple TV remote (#265) +- Add an example of sendRaw() to IRsendDemo.ino (#270) + + +## _v2.1.0 (20170704)_ + +**[Features]** +- Support for sending Pronto IR codes. (#248) +- Support for sending Fujitsu A/C codes. (#88) +- Minor improvements to examples. + + +## _v2.0.3 (20170618)_ + +**[Bug fixes]** +- Capture buffer could become corrupt after large message, breaking subsequent decodes. (#253) + + +## _v2.0.2 (20170615)_ + +**[Bug fixes]** +- Correct decode issue introduced in v2.0.1 affecting multiple protocol decoders (#243) +- Correct post-message gap for the Panasonic protocol(s) (#245) +- Incorrect display of the decoded uint64_t value in the example code. (#245) + + +## _v2.0.1 (20170614)_ + +**[Bug fixes]** +- Decoding protocols when it doesn't detect a post-command gap, and there is no more data. (#243) +- Incorrect minimum size calculation when there is no post-command gap. (#243) + + +## _v2.0.0 - 64 bit support and major improvements (20170612)_ + +**[Misc]** +- This is almost a complete re-write of the library. + +**[Features]** +- All suitable protocols now handle 64-bit data messages and are repeatable via an optional argument. +- Unit tests for all protocols. +- Far better and stricter decoding for most protocols. +- Address & command decoding for protocols where that information is available. +- Much more precise timing for generation of signals sent. +- Lower duty-cycles for some protocols. +- Several new protocols added, and some new sending and decoding routines for existing ones. +- Ability to optionally chose which protocols are included, enabling faster decoding and smaller code footprints if desired. +- Support for far larger capture buffers. (e.g. RAWLEN > 256) + +**[Bug fixes]** +- Numerous bug fixes. + + +## _v1.2.0 (20170429)_ + +**[Features]** +- Add ability to copy IR capture buffer, and continue capturing. Means faster and better IR command decoding. +- Reduce IRAM usage by 28 bytes. +- Improve capture of RC-MM & Panasonic protocols. +- Upgrade IRrecvDumpV2 to new IR capture buffer. Much fewer corrupted/truncated IR messages. + + +## _v1.1.1 (20170413)_ + +**[Bug fixes]** +- Fix a reported problem when sending the LG protocol. Preemptive fix for possible similar cases. +- Fix minor issues in examples. + +**[Features]** +- Add documentation to some examples to aid new people. +- Add ALPHA support for RC-MM protocol. (Known to be currently not 100% working.) diff --git a/lib/IRremoteESP8266/examples/IRGCSendDemo/IRGCSendDemo.ino b/lib/IRremoteESP8266/examples/IRGCSendDemo/IRGCSendDemo.ino index b36938e..cf576f7 100644 --- a/lib/IRremoteESP8266/examples/IRGCSendDemo/IRGCSendDemo.ino +++ b/lib/IRremoteESP8266/examples/IRGCSendDemo/IRGCSendDemo.ino @@ -11,7 +11,8 @@ * Based on Ken Shirriff's IrsendDemo * Version 0.1 July, 2009 * - * An IR LED circuit *MUST* be connected to ESP8266 pin 4 (D2). + * An IR LED circuit *MUST* be connected to the ESP8266 on a pin + * as specified by IR_LED below. * * TL;DR: The IR LED needs to be driven by a transistor for a good result. * @@ -49,7 +50,10 @@ uint16_t Samsung_power_toggle[71] = { 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}; -IRsend irsend(4); // An IR LED is controlled by GPIO pin 4 (D2) + +#define IR_LED 4 // ESP8266 GPIO pin to use. Recommended: 4 (D2). + +IRsend irsend(IR_LED); // Set the GPIO to be used to sending the message. void setup() { irsend.begin(); @@ -58,6 +62,10 @@ void setup() { void loop() { Serial.println("Toggling power"); +#if SEND_GLOBALCACHE irsend.sendGC(Samsung_power_toggle, 71); +#else // SEND_GLOBALCACHE + Serial.println("Can't send because SEND_GLOBALCACHE has been disabled."); +#endif // SEND_GLOBALCACHE delay(10000); } diff --git a/lib/IRremoteESP8266/examples/IRGCTCPServer/IRGCTCPServer.ino b/lib/IRremoteESP8266/examples/IRGCTCPServer/IRGCTCPServer.ino index 2fd38be..0361091 100644 --- a/lib/IRremoteESP8266/examples/IRGCTCPServer/IRGCTCPServer.ino +++ b/lib/IRremoteESP8266/examples/IRGCTCPServer/IRGCTCPServer.ino @@ -53,7 +53,9 @@ WiFiServer server(4998); // Uses port 4998. WiFiClient client; uint16_t *code_array; -IRsend irsend(4); // An IR LED is controlled by GPIO pin 4 (D2) +#define IR_LED 4 // ESP8266 GPIO pin to use. Recommended: 4 (D2). + +IRsend irsend(IR_LED); // Set the GPIO to be used to sending the message. void sendGCString(String str) { int16_t index; @@ -89,7 +91,9 @@ void sendGCString(String str) { count++; } while (index != -1); +#if SEND_GLOBALCACHE irsend.sendGC(code_array, count); // All done. Send it. +#endif // SEND_GLOBALCACHE free(code_array); // Free up the memory allocated. } @@ -99,7 +103,7 @@ void setup() { delay(100); Serial.println(" "); Serial.println("IR TCP Server"); - + WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { delay(900); Serial.print("."); @@ -107,7 +111,7 @@ void setup() { server.begin(); IPAddress myAddress = WiFi.localIP(); - Serial.println(myAddress); + Serial.println(myAddress.toString()); irsend.begin(); } diff --git a/lib/IRremoteESP8266/examples/IRMQTTServer/IRMQTTServer.ino b/lib/IRremoteESP8266/examples/IRMQTTServer/IRMQTTServer.ino new file mode 100644 index 0000000..88c3444 --- /dev/null +++ b/lib/IRremoteESP8266/examples/IRMQTTServer/IRMQTTServer.ino @@ -0,0 +1,1348 @@ +/* + * Send 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). See IR_LED + * + * 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) + * - Set the MQTT_SERVER define below to the address of your MQTT server. + * - Arduino IDE: + * o Install the following libraries via Library Manager + * - WiFiManager (https://github.com/tzapu/WiFiManager) + * - 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 + * + * 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. +#define HTTP_PORT 80 // The port the HTTP server is listening on. +#define HOSTNAME "ir_server" // Name of the device you want in mDNS. + +#ifdef MQTT_ENABLE +// Address of your MQTT server. +#define MQTT_SERVER "10.20.0.253" // <=- CHANGE ME +#define MQTT_PORT 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 = ""; +#define MQTT_RECONNECT_TIME 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. +#endif // MQTT_ENABLE + +// HTML arguments we will parse for IR code information. +#define argType "type" +#define argData "code" +#define argBits "bits" +#define argRepeat "repeats" + +#define _MY_VERSION_ "v0.5" + +#if IR_LED != 1 // Disable debug output if the LED is on the TX (D1) pin. +#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(HTTP_PORT); +IRsend irsend = IRsend(IR_LED); +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; +int8_t offset; // The calculated period offset for this chip and library. + +#ifdef MQTT_ENABLE +String lastMqttCmd = "None"; +uint32_t lastMqttCmdTime = 0; + + +// MQTT client parameters +void callback(char* topic, byte* payload, unsigned int length); +PubSubClient mqtt_client(MQTT_SERVER, MQTT_PORT, 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() + "
" + "Total send requests: " + String(sendReqCounter) + "

" +#ifdef MQTT_ENABLE + "

MQTT Information

" + "

Server: " MQTT_SERVER ":" + String(MQTT_PORT) + " (" + + (mqtt_client.connected() ? "Connected" : "Disconnected") + ")
" + "Client id: " + mqtt_clientid + "
" + "Command topic: " MQTTcommand "
" + "Acknowledgements topic: " MQTTack "
" + "Last 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. +void parseStringAndSendAirCon(const uint16_t irType, const String str) { + uint8_t strOffset = 0; + uint8_t state[STATE_SIZE_MAX] = {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; // No input. Abort. + } + + switch (irType) { // Get the correct state size for the protocol. + case KELVINATOR: + stateSize = KELVINATOR_STATE_LENGTH; + break; + case TOSHIBA_AC: + stateSize = TOSHIBA_AC_STATE_LENGTH; + break; + case DAIKIN: + stateSize = DAIKIN_COMMAND_LENGTH; + break; + case MITSUBISHI_AC: + stateSize = MITSUBISHI_AC_STATE_LENGTH; + break; + case TROTEC: + stateSize = TROTEC_COMMAND_LENGTH; + break; + case ARGO: + stateSize = ARGO_COMMAND_LENGTH; + break; + case GREE: + stateSize = GREE_STATE_LENGTH; + 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) (FUJITSU_AC_STATE_LENGTH_SHORT - 1)); + // If we think it isn't a "short" message. + if (stateSize > FUJITSU_AC_STATE_LENGTH_SHORT) + // Then it has to be at least the smaller version of the "normal" size. + stateSize = std::max(stateSize, + (uint16_t) (FUJITSU_AC_STATE_LENGTH - 1)); + // Lastly, it should never exceed the maximum "normal" size. + stateSize = std::min(stateSize, (uint16_t) FUJITSU_AC_STATE_LENGTH); + break; + case HAIER_AC: + stateSize = HAIER_AC_STATE_LENGTH; + break; + case HITACHI_AC: + stateSize = HITACHI_AC_STATE_LENGTH; + break; + case HITACHI_AC1: + stateSize = HITACHI_AC1_STATE_LENGTH; + break; + case HITACHI_AC2: + stateSize = HITACHI_AC2_STATE_LENGTH; + break; + default: // Not a protocol we expected. Abort. + debug("Unexpected AirCon protocol detected. Ignoring."); + return; + } + if (inputLength > stateSize * 2) { + debug("AirCon code to large for the given protocol."); + return; + } + + // 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; + } + 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_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 + } +} + +// 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. +void 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. +} +#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 PRONTO_MIN_LENGTH 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. +void 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 PRONTO_MIN_LENGTH values for the code part. + if (count < PRONTO_MIN_LENGTH) return; + + // 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. +} +#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. +void 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; + 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. +} +#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"); + 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 (!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(); + + #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(MQTT_PORT) + + "... "); + 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(); + +#ifdef MQTT_ENABLE + // MQTT client connection management + if (!mqtt_client.connected()) { + uint32_t now = millis(); + // Reconnect if it's longer than MQTT_RECONNECT_TIME since we last tried. + if (now - lastReconnectAttempt > MQTT_RECONNECT_TIME) { + lastReconnectAttempt = now; + debug("client mqtt not connected, trying to connect"); + // Attempt to reconnect + if (reconnect()) { + lastReconnectAttempt = 0; + if (boot) { + mqtt_client.publish(MQTTack, "IR Server just booted"); + boot = false; + } else { + mqtt_client.publish(MQTTack, "IR Server just (re)connected to MQTT"); + } + } + } + } else { + // MQTT loop + mqtt_client.loop(); + } +#endif // MQTT_ENABLE + 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.) +void 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; + + // send the IR message. + switch (ir_type) { +#if SEND_RC5 + case RC5: // 1 + if (bits == 0) + bits = RC5_BITS; + irsend.sendRC5(code, bits, repeat); + break; +#endif +#if SEND_RC6 + case RC6: // 2 + if (bits == 0) + bits = RC6_MODE0_BITS; + irsend.sendRC6(code, bits, repeat); + break; +#endif +#if SEND_NEC + case NEC: // 3 + if (bits == 0) + bits = NEC_BITS; + irsend.sendNEC(code, bits, repeat); + break; +#endif +#if SEND_SONY + case SONY: // 4 + if (bits == 0) + bits = SONY_12_BITS; + repeat = std::max(repeat, (uint16_t) SONY_MIN_REPEAT); + irsend.sendSony(code, bits, repeat); + break; +#endif +#if SEND_PANASONIC + case PANASONIC: // 5 + if (bits == 0) + bits = PANASONIC_BITS; + irsend.sendPanasonic64(code, bits, repeat); + break; +#endif +#if SEND_JVC + case JVC: // 6 + if (bits == 0) + bits = JVC_BITS; + irsend.sendJVC(code, bits, repeat); + break; +#endif +#if SEND_SAMSUNG + case SAMSUNG: // 7 + if (bits == 0) + bits = SAMSUNG_BITS; + irsend.sendSAMSUNG(code, bits, repeat); + break; +#endif +#if SEND_WHYNTER + case WHYNTER: // 8 + if (bits == 0) + bits = WHYNTER_BITS; + irsend.sendWhynter(code, bits, repeat); + break; +#endif +#if SEND_AIWA_RC_T501 + case AIWA_RC_T501: // 9 + if (bits == 0) + bits = AIWA_RC_T501_BITS; + repeat = std::max(repeat, (uint16_t) AIWA_RC_T501_MIN_REPEAT); + irsend.sendAiwaRCT501(code, bits, repeat); + break; +#endif +#if SEND_LG + case LG: // 10 + if (bits == 0) + bits = LG_BITS; + irsend.sendLG(code, bits, repeat); + break; +#endif +#if SEND_MITSUBISHI + case MITSUBISHI: // 12 + if (bits == 0) + bits = MITSUBISHI_BITS; + repeat = std::max(repeat, (uint16_t) MITSUBISHI_MIN_REPEAT); + irsend.sendMitsubishi(code, bits, repeat); + break; +#endif +#if SEND_DISH + case DISH: // 13 + if (bits == 0) + bits = DISH_BITS; + repeat = std::max(repeat, (uint16_t) DISH_MIN_REPEAT); + irsend.sendDISH(code, bits, repeat); + break; +#endif +#if SEND_SHARP + case SHARP: // 14 + if (bits == 0) + bits = SHARP_BITS; + irsend.sendSharpRaw(code, bits, repeat); + break; +#endif +#if SEND_COOLIX + case COOLIX: // 15 + if (bits == 0) + bits = COOLIX_BITS; + 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 HITACHI_AC: // 40 + case HITACHI_AC1: // 41 + case HITACHI_AC2: // 42 + 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 = SHERWOOD_BITS; + repeat = std::max(repeat, (uint16_t) SHERWOOD_MIN_REPEAT); + irsend.sendSherwood(code, bits, repeat); + break; +#endif +#if SEND_RCMM + case RCMM: // 21 + if (bits == 0) + bits = RCMM_BITS; + irsend.sendRCMM(code, bits, repeat); + break; +#endif +#if SEND_SANYO + case SANYO_LC7461: // 22 + if (bits == 0) + bits = SANYO_LC7461_BITS; + irsend.sendSanyoLC7461(code, bits, repeat); + break; +#endif +#if SEND_RC5 + case RC5X: // 23 + if (bits == 0) + bits = RC5X_BITS; + irsend.sendRC5(code, bits, repeat); + break; +#endif +#if SEND_PRONTO + case PRONTO: // 25 + parseStringAndSendPronto(code_str, repeat); + break; +#endif +#if SEND_NIKAI + case NIKAI: // 29 + if (bits == 0) + bits = NIKAI_BITS; + irsend.sendNikai(code, bits, repeat); + break; +#endif +#if SEND_RAW + case RAW: // 30 + parseStringAndSendRaw(code_str); + break; +#endif +#if SEND_GLOBALCACHE + case GLOBALCACHE: // 31 + parseStringAndSendGC(code_str); + break; +#endif +#if SEND_MIDEA + case MIDEA: // 34 + if (bits == 0) + bits = MIDEA_BITS; + irsend.sendMidea(code, bits, repeat); + break; +#endif +#if SEND_MAGIQUEST + case MAGIQUEST: // 35 + if (bits == 0) + bits = MAGIQUEST_BITS; + irsend.sendMagiQuest(code, bits, repeat); + break; +#endif +#if SEND_LASERTAG + case LASERTAG: // 36 + if (bits == 0) + bits = LASERTAG_BITS; + irsend.sendLasertag(code, bits, repeat); + break; +#endif +#if SEND_CARRIER_AC + case CARRIER_AC: // 37 + if (bits == 0) + bits = CARRIER_AC_BITS; + irsend.sendCarrierAC(code, bits, repeat); + break; +#endif +#if SEND_MITSUBISHI2 + case MITSUBISHI2: // 39 + if (bits == 0) + bits = MITSUBISHI_BITS; + repeat = std::max(repeat, (uint16_t) MITSUBISHI_MIN_REPEAT); + irsend.sendMitsubishi2(code, bits, repeat); + break; +#endif +#if SEND_GICABLE + case GICABLE: // 43 + if (bits == 0) + bits = GICABLE_BITS; + repeat = std::max(repeat, (uint16_t) GICABLE_BITS); + irsend.sendGICable(code, bits, repeat); + break; +#endif + } + sendReqCounter++; + // Release the lock. + ir_lock = false; + + // Indicate that we sent the message. + debug("Sent the 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); + debug("Repeats: " + String(repeat)); + // Confirm what we were asked to send was sent. +#ifdef MQTT_ENABLE + 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 + mqtt_client.publish(MQTTack, (String(ir_type) + "," + + uint64ToString(code, 16) + + "," + String(bits) + "," + + String(repeat)).c_str()); +#endif // MQTT_ENABLE + } +} + +#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 + 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/examples/IRMQTTServer/platformio.ini b/lib/IRremoteESP8266/examples/IRMQTTServer/platformio.ini new file mode 100644 index 0000000..c87e569 --- /dev/null +++ b/lib/IRremoteESP8266/examples/IRMQTTServer/platformio.ini @@ -0,0 +1,28 @@ +[platformio] +lib_extra_dirs = ../../ +src_dir=. + +[common] +build_flags = -DMQTT_MAX_PACKET_SIZE=512 +lib_deps_builtin = +lib_deps_external = + PubSubClient + WifiManager@0.12 + +[env:nodemcuv2] +platform = espressif8266 +framework = arduino +board = nodemcuv2 +build_flags = ${common.build_flags} +lib_deps = + ${common.lib_deps_builtin} + ${common.lib_deps_external} + +[env:d1_mini] +platform=espressif8266 +framework=arduino +board=d1_mini +build_flags = ${common.build_flags} +lib_deps = + ${common.lib_deps_builtin} + ${common.lib_deps_external} diff --git a/lib/IRremoteESP8266/examples/IRServer/IRServer.ino b/lib/IRremoteESP8266/examples/IRServer/IRServer.ino index 82f2e7d..87e0d2c 100644 --- a/lib/IRremoteESP8266/examples/IRServer/IRServer.ino +++ b/lib/IRremoteESP8266/examples/IRServer/IRServer.ino @@ -3,7 +3,8 @@ * Version 0.2 June, 2017 * Copyright 2015 Mark Szabo * - * An IR LED circuit *MUST* be connected to ESP8266 pin 4 (D2). + * An IR LED circuit *MUST* be connected to the ESP8266 on a pin + * as specified by IR_LED below. * * TL;DR: The IR LED needs to be driven by a transistor for a good result. * @@ -41,7 +42,9 @@ MDNSResponder mdns; ESP8266WebServer server(80); -IRsend irsend(4); // An IR LED is controlled by GPIO pin 4 (D2) +#define IR_LED 4 // ESP8266 GPIO pin to use. Recommended: 4 (D2). + +IRsend irsend(IR_LED); // Set the GPIO to be used to sending the message. void handleRoot() { server.send(200, "text/html", @@ -61,7 +64,9 @@ void handleIr() { for (uint8_t i = 0; i < server.args(); i++) { if (server.argName(i) == "code") { uint32_t code = strtoul(server.arg(i).c_str(), NULL, 10); +#if SEND_NEC irsend.sendNEC(code, 32); +#endif // SEND_NEC } } handleRoot(); @@ -97,7 +102,7 @@ void setup(void) { Serial.print("Connected to "); Serial.println(ssid); Serial.print("IP address: "); - Serial.println(WiFi.localIP()); + Serial.println(WiFi.localIP().toString()); if (mdns.begin("esp8266", WiFi.localIP())) { Serial.println("MDNS responder started"); diff --git a/lib/IRremoteESP8266/examples/IRrecvDemo/IRrecvDemo.ino b/lib/IRremoteESP8266/examples/IRrecvDemo/IRrecvDemo.ino index ae9ed66..c7936e5 100644 --- a/lib/IRremoteESP8266/examples/IRrecvDemo/IRrecvDemo.ino +++ b/lib/IRremoteESP8266/examples/IRrecvDemo/IRrecvDemo.ino @@ -1,5 +1,8 @@ /* * IRremoteESP8266: IRrecvDemo - demonstrates receiving IR codes with IRrecv + * This is very simple teaching code to show you how to use the library. + * If you are trying to decode your Infra-Red remote(s) for later replay, + * use the IRrecvDumpV2.ino example code instead of this. * An IR detector/demodulator must be connected to the input RECV_PIN. * Copyright 2009 Ken Shirriff, http://arcfn.com * Example circuit diagram: @@ -31,6 +34,11 @@ decode_results results; void setup() { Serial.begin(115200); irrecv.enableIRIn(); // Start the receiver + while (!Serial) // Wait for the serial connection to be establised. + delay(50); + Serial.println(); + Serial.print("IRrecvDemo is now running and waiting for IR message on Pin "); + Serial.println(RECV_PIN); } void loop() { diff --git a/lib/IRremoteESP8266/examples/IRrecvDump/IRrecvDump.ino b/lib/IRremoteESP8266/examples/IRrecvDump/IRrecvDump.ino index b018ec0..8564496 100644 --- a/lib/IRremoteESP8266/examples/IRrecvDump/IRrecvDump.ino +++ b/lib/IRremoteESP8266/examples/IRrecvDump/IRrecvDump.ino @@ -1,9 +1,22 @@ /* * IRremoteESP8266: IRrecvDump - dump details of IR codes with IRrecv - * An IR detector/demodulator must be connected to the input RECV_PIN. - * Version 0.1 Sept, 2015 - * Based on Ken Shirriff's IrsendDemo Version 0.1 July, 2009, * Copyright 2009 Ken Shirriff, http://arcfn.com + * + ***** DEPRECATED - DO NOT USE ***** + * Unless you know what you are doing, you should be using the + * IRrecvDumpV2.ino sketch/example instead for capturing & decoding IR messages. + * In almost ALL ways it is BETTER, FASTER, and MORE DETAILED. + * + * This code is left only for legacy reasons, and as another simple example of + * how to use the IRremoteESP8266 library. + * + * As of November 2017 it will no longer be updated or supported. + * You have been warned. + ***** DEPRECATED - DO NOT USE ***** + * + * An IR detector/demodulator must be connected to the input RECV_PIN. + * Version 0.2 Oct 2017 + * Based on Ken Shirriff's IrsendDemo Version 0.1 July, 2009, * JVC and Panasonic protocol added by Kristian Lauszus * (Thanks to zenwheel and other people at the original blog post) * LG added by Darryl Smith (based on the JVC protocol) @@ -58,6 +71,8 @@ void dump(decode_results *results) { Serial.print("Decoded AIWA RC T501: "); } else if (results->decode_type == WHYNTER) { Serial.print("Decoded Whynter: "); + } else if (results->decode_type == NIKAI) { + Serial.print("Decoded Nikai: "); } serialPrintUint64(results->value, 16); Serial.print(" ("); @@ -65,7 +80,7 @@ void dump(decode_results *results) { Serial.println(" bits)"); Serial.print("Raw ("); Serial.print(count, DEC); - Serial.print("): "); + Serial.print("): {"); for (uint16_t i = 1; i < count; i++) { if (i % 100 == 0) @@ -73,18 +88,17 @@ void dump(decode_results *results) { if (i & 1) { Serial.print(results->rawbuf[i] * RAWTICK, DEC); } else { - Serial.write('-'); + Serial.print(", "); Serial.print((uint32_t) results->rawbuf[i] * RAWTICK, DEC); } - Serial.print(" "); } - Serial.println(); + Serial.println("};"); } void loop() { if (irrecv.decode(&results)) { - serialPrintUint64(results.value, 16); dump(&results); + Serial.println("DEPRECATED: Please use IRrecvDumpV2.ino instead!"); irrecv.resume(); // Receive the next value } } diff --git a/lib/IRremoteESP8266/examples/IRrecvDumpV2/IRrecvDumpV2.ino b/lib/IRremoteESP8266/examples/IRrecvDumpV2/IRrecvDumpV2.ino index d3ed15b..105a7d4 100644 --- a/lib/IRremoteESP8266/examples/IRrecvDumpV2/IRrecvDumpV2.ino +++ b/lib/IRremoteESP8266/examples/IRrecvDumpV2/IRrecvDumpV2.ino @@ -1,13 +1,20 @@ /* * IRremoteESP8266: IRrecvDumpV2 - dump details of IR codes with IRrecv * An IR detector/demodulator must be connected to the input RECV_PIN. + * + * Copyright 2009 Ken Shirriff, http://arcfn.com + * Copyright 2017 David Conran + * * Example circuit diagram: * https://github.com/markszabo/IRremoteESP8266/wiki#ir-receiving + * * Changes: + * Version 0.3 November, 2017 + * - Support for A/C decoding for some protcols. * Version 0.2 April, 2017 * - Decode from a copy of the data so we can start capturing faster thus * reduce the likelihood of miscaptures. - * Based on Ken Shirriff's IrsendDemo Version 0.1 July, 2009, Copyright 2009 Ken Shirriff, http://arcfn.com + * Based on Ken Shirriff's IrsendDemo Version 0.1 July, 2009, */ #ifndef UNIT_TEST @@ -16,183 +23,190 @@ #include #include #include +#if DECODE_AC +#include +#include +#include +#include +#include +#include +#include +#endif // DECODE_AC + +// ==================== start of TUNEABLE PARAMETERS ==================== +// An IR detector/demodulator is connected to GPIO pin 14 +// e.g. D5 on a NodeMCU board. +#define RECV_PIN 14 + +// The Serial connection baud rate. +// i.e. Status message will be sent to the PC at this baud rate. +// Try to avoid slow speeds like 9600, as you will miss messages and +// cause other problems. 115200 (or faster) is recommended. +// NOTE: Make sure you set your Serial Monitor to the same speed. +#define BAUD_RATE 115200 -// An IR detector/demodulator is connected to GPIO pin 14(D5 on a NodeMCU -// board). -uint16_t RECV_PIN = 14; // As this program is a special purpose capture/decoder, let us use a larger // than normal buffer so we can handle Air Conditioner remote codes. -uint16_t CAPTURE_BUFFER_SIZE = 1024; +#define CAPTURE_BUFFER_SIZE 1024 -// Nr. of milli-Seconds of no-more-data before we consider a message ended. +// TIMEOUT is the Nr. of milli-Seconds of no-more-data before we consider a +// message ended. +// This parameter is an interesting trade-off. The longer the timeout, the more +// complex a message it can capture. e.g. Some device protocols will send +// multiple message packets in quick succession, like Air Conditioner remotes. +// Air Coniditioner protocols often have a considerable gap (20-40+ms) between +// packets. +// The downside of a large timeout value is a lot of less complex protocols +// send multiple messages when the remote's button is held down. The gap between +// them is often also around 20+ms. This can result in the raw data be 2-3+ +// times larger than needed as it has captured 2-3+ messages in a single +// capture. Setting a low timeout value can resolve this. +// So, choosing the best TIMEOUT value for your use particular case is +// quite nuanced. Good luck and happy hunting. // NOTE: Don't exceed MAX_TIMEOUT_MS. Typically 130ms. -#define TIMEOUT 15U // Suits most messages, while not swallowing repeats. +#if DECODE_AC +#define TIMEOUT 50U // Some A/C units have gaps in their protocols of ~40ms. + // e.g. Kelvinator + // A value this large may swallow repeats of some protocols +#else // DECODE_AC +#define TIMEOUT 15U // Suits most messages, while not swallowing many repeats. +#endif // DECODE_AC +// Alternatives: // #define TIMEOUT 90U // Suits messages with big gaps like XMP-1 & some aircon - // units, but can accidently swallow repeated messages + // units, but can accidentally swallow repeated messages // in the rawData[] output. +// #define TIMEOUT MAX_TIMEOUT_MS // This will set it to our currently allowed + // maximum. Values this high are problematic + // because it is roughly the typical boundary + // where most messages repeat. + // e.g. It will stop decoding a message and + // start sending it to serial at precisely + // the time when the next message is likely + // to be transmitted, and may miss it. + +// Set the smallest sized "UNKNOWN" message packets we actually care about. +// This value helps reduce the false-positive detection rate of IR background +// noise as real messages. The chances of background IR noise getting detected +// as a message increases with the length of the TIMEOUT value. (See above) +// The downside of setting this message too large is you can miss some valid +// short messages for protocols that this library doesn't yet decode. +// +// Set higher if you get lots of random short UNKNOWN messages when nothing +// should be sending a message. +// Set lower if you are sure your setup is working, but it doesn't see messages +// from your device. (e.g. Other IR remotes work.) +// NOTE: Set this value very high to effectively turn off UNKNOWN detection. +#define MIN_UNKNOWN_SIZE 12 +// ==================== end of TUNEABLE PARAMETERS ==================== + // Use turn on the save buffer feature for more complete capture coverage. IRrecv irrecv(RECV_PIN, CAPTURE_BUFFER_SIZE, TIMEOUT, true); decode_results results; // Somewhere to store the results +// Display the human readable state of an A/C message if we can. +void dumpACInfo(decode_results *results) { + String description = ""; +#if DECODE_DAIKIN + if (results->decode_type == DAIKIN) { + IRDaikinESP ac(0); + ac.setRaw(results->state); + description = ac.toString(); + } +#endif // DECODE_DAIKIN +#if DECODE_FUJITSU_AC + if (results->decode_type == FUJITSU_AC) { + IRFujitsuAC ac(0); + ac.setRaw(results->state, results->bits / 8); + description = ac.toString(); + } +#endif // DECODE_FUJITSU_AC +#if DECODE_KELVINATOR + if (results->decode_type == KELVINATOR) { + IRKelvinatorAC ac(0); + ac.setRaw(results->state); + description = ac.toString(); + } +#endif // DECODE_KELVINATOR +#if DECODE_TOSHIBA_AC + if (results->decode_type == TOSHIBA_AC) { + IRToshibaAC ac(0); + ac.setRaw(results->state); + description = ac.toString(); + } +#endif // DECODE_TOSHIBA_AC +#if DECODE_GREE + if (results->decode_type == GREE) { + IRGreeAC ac(0); + ac.setRaw(results->state); + description = ac.toString(); + } +#endif // DECODE_GREE +#if DECODE_MIDEA + if (results->decode_type == MIDEA) { + IRMideaAC ac(0); + ac.setRaw(results->value); // Midea uses value instead of state. + description = ac.toString(); + } +#endif // DECODE_MIDEA +#if DECODE_HAIER_AC + if (results->decode_type == HAIER_AC) { + IRHaierAC ac(0); + ac.setRaw(results->state); + description = ac.toString(); + } +#endif // DECODE_HAIER_AC + // If we got a human-readable description of the message, display it. + if (description != "") Serial.println("Mesg Desc.: " + description); +} + +// The section of code run only once at start-up. void setup() { - // Status message will be sent to the PC at 115200 baud - Serial.begin(115200, SERIAL_8N1, SERIAL_TX_ONLY); - delay(500); // Wait a bit for the serial connection to be establised. + Serial.begin(BAUD_RATE, SERIAL_8N1, SERIAL_TX_ONLY); + while (!Serial) // Wait for the serial connection to be establised. + delay(50); + Serial.println(); + Serial.print("IRrecvDumpV2 is now running and waiting for IR input on Pin "); + Serial.println(RECV_PIN); +#if DECODE_HASH + // Ignore messages with less than minimum on or off pulses. + irrecv.setUnknownThreshold(MIN_UNKNOWN_SIZE); +#endif // DECODE_HASH irrecv.enableIRIn(); // Start the receiver } -// Display encoding type -// -void encoding(decode_results *results) { - switch (results->decode_type) { - default: - case UNKNOWN: Serial.print("UNKNOWN"); break; - case NEC: Serial.print("NEC"); break; - case NEC_LIKE: Serial.print("NEC (non-strict)"); break; - case SONY: Serial.print("SONY"); break; - case RC5: Serial.print("RC5"); break; - case RC5X: Serial.print("RC5X"); break; - case RC6: Serial.print("RC6"); break; - case RCMM: Serial.print("RCMM"); break; - case DISH: Serial.print("DISH"); break; - case SHARP: Serial.print("SHARP"); break; - case JVC: Serial.print("JVC"); break; - case SANYO: Serial.print("SANYO"); break; - case SANYO_LC7461: Serial.print("SANYO_LC7461"); break; - case MITSUBISHI: Serial.print("MITSUBISHI"); break; - case SAMSUNG: Serial.print("SAMSUNG"); break; - case LG: Serial.print("LG"); break; - case WHYNTER: Serial.print("WHYNTER"); break; - case AIWA_RC_T501: Serial.print("AIWA_RC_T501"); break; - case PANASONIC: Serial.print("PANASONIC"); break; - case DENON: Serial.print("DENON"); break; - case COOLIX: Serial.print("COOLIX"); break; - } - if (results->repeat) Serial.print(" (Repeat)"); -} - -// Dump out the decode_results structure. -// -void dumpInfo(decode_results *results) { - if (results->overflow) - Serial.printf("WARNING: IR code too big for buffer (>= %d). " - "These results shouldn't be trusted until this is resolved. " - "Edit & increase CAPTURE_BUFFER_SIZE.\n", - CAPTURE_BUFFER_SIZE); - - // Show Encoding standard - Serial.print("Encoding : "); - encoding(results); - Serial.println(""); - - // Show Code & length - Serial.print("Code : "); - serialPrintUint64(results->value, 16); - Serial.print(" ("); - Serial.print(results->bits, DEC); - Serial.println(" bits)"); -} - -uint16_t getCookedLength(decode_results *results) { - uint16_t length = results->rawlen - 1; - for (uint16_t i = 0; i < results->rawlen - 1; i++) { - uint32_t usecs = results->rawbuf[i] * RAWTICK; - // Add two extra entries for multiple larger than UINT16_MAX it is. - length += (usecs / UINT16_MAX) * 2; - } - return length; -} - -// Dump out the decode_results structure. -// -void dumpRaw(decode_results *results) { - // Print Raw data - Serial.print("Timing["); - Serial.print(results->rawlen - 1, DEC); - Serial.println("]: "); - - for (uint16_t i = 1; i < results->rawlen; i++) { - if (i % 100 == 0) - yield(); // Preemptive yield every 100th entry to feed the WDT. - if (i % 2 == 0) { // even - Serial.print("-"); - } else { // odd - Serial.print(" +"); - } - Serial.printf("%6d", results->rawbuf[i] * RAWTICK); - if (i < results->rawlen - 1) - Serial.print(", "); // ',' not needed for last one - if (!(i % 8)) Serial.println(""); - } - Serial.println(""); // Newline -} - -// Dump out the decode_results structure. -// -void dumpCode(decode_results *results) { - // Start declaration - Serial.print("uint16_t "); // variable type - Serial.print("rawData["); // array name - Serial.print(getCookedLength(results), DEC); // array size - Serial.print("] = {"); // Start declaration - - // Dump data - for (uint16_t i = 1; i < results->rawlen; i++) { - uint32_t usecs; - for (usecs = results->rawbuf[i] * RAWTICK; - usecs > UINT16_MAX; - usecs -= UINT16_MAX) - Serial.printf("%d, 0", UINT16_MAX); - Serial.print(usecs, DEC); - if (i < results->rawlen - 1) - Serial.print(", "); // ',' not needed on last one - if (i % 2 == 0) Serial.print(" "); // Extra if it was even. - } - - // End declaration - Serial.print("};"); // - - // Comment - Serial.print(" // "); - encoding(results); - Serial.print(" "); - serialPrintUint64(results->value, HEX); - - // Newline - Serial.println(""); - - // Now dump "known" codes - if (results->decode_type != UNKNOWN) { - // 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) { - Serial.print("uint32_t address = 0x"); - Serial.print(results->address, HEX); - Serial.println(";"); - Serial.print("uint32_t command = 0x"); - Serial.print(results->command, HEX); - Serial.println(";"); - } - - // All protocols have data - Serial.print("uint64_t data = 0x"); - serialPrintUint64(results->value, 16); - Serial.println(";"); - } -} - // The repeating section of the code // void loop() { // Check if the IR code has been received. if (irrecv.decode(&results)) { - dumpInfo(&results); // Output the results - dumpRaw(&results); // Output the results in RAW format - dumpCode(&results); // Output the results as source code - Serial.println(""); // Blank line between entries + // Display a crude timestamp. + uint32_t now = millis(); + Serial.printf("Timestamp : %06u.%03u\n", now / 1000, now % 1000); + if (results.overflow) + Serial.printf("WARNING: IR code is too big for buffer (>= %d). " + "This result shouldn't be trusted until this is resolved. " + "Edit & increase CAPTURE_BUFFER_SIZE.\n", + CAPTURE_BUFFER_SIZE); + // Display the basic output of what we found. + Serial.print(resultToHumanReadableBasic(&results)); + dumpACInfo(&results); // Display any extra A/C info if we have it. + yield(); // Feed the WDT as the text output can take a while to print. + + // Display the library version the message was captured with. + Serial.print("Library : v"); + Serial.println(_IRREMOTEESP8266_VERSION_); + Serial.println(); + + // Output RAW timing info of the result. + Serial.println(resultToTimingInfo(&results)); + yield(); // Feed the WDT (again) + + // Output the results as source code + Serial.println(resultToSourceCode(&results)); + Serial.println(""); // Blank line between entries + yield(); // Feed the WDT (again) } } diff --git a/lib/IRremoteESP8266/examples/IRsendDemo/IRsendDemo.ino b/lib/IRremoteESP8266/examples/IRsendDemo/IRsendDemo.ino index 1a688cb..51d1979 100644 --- a/lib/IRremoteESP8266/examples/IRsendDemo/IRsendDemo.ino +++ b/lib/IRremoteESP8266/examples/IRsendDemo/IRsendDemo.ino @@ -4,7 +4,8 @@ * Based on Ken Shirriff's IrsendDemo Version 0.1 July, 2009, * Copyright 2009 Ken Shirriff, http://arcfn.com * - * An IR LED circuit *MUST* be connected to ESP8266 pin 4 (D2). + * An IR LED circuit *MUST* be connected to the ESP8266 on a pin + * as specified by IR_LED below. * * TL;DR: The IR LED needs to be driven by a transistor for a good result. * @@ -33,7 +34,9 @@ #include #include -IRsend irsend(4); // An IR LED is controlled by GPIO pin 4 (D2) +#define IR_LED 4 // ESP8266 GPIO pin to use. Recommended: 4 (D2). + +IRsend irsend(IR_LED); // Set the GPIO to be used to sending the message. // Example of data captured by IRrecvDumpV2.ino uint16_t rawData[67] = {9000, 4500, 650, 550, 650, 1650, 600, 550, 650, 550, @@ -50,13 +53,19 @@ void setup() { } void loop() { +#if SEND_NEC Serial.println("NEC"); irsend.sendNEC(0x00FFE01FUL, 32); +#endif // SEND_NEC delay(2000); +#if SEND_SONY Serial.println("Sony"); irsend.sendSony(0xa90, 12, 2); +#endif // SEND_SONY 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); } diff --git a/lib/IRremoteESP8266/examples/IRsendProntoDemo/IRsendProntoDemo.ino b/lib/IRremoteESP8266/examples/IRsendProntoDemo/IRsendProntoDemo.ino index 0a7a014..c527d89 100644 --- a/lib/IRremoteESP8266/examples/IRsendProntoDemo/IRsendProntoDemo.ino +++ b/lib/IRremoteESP8266/examples/IRsendProntoDemo/IRsendProntoDemo.ino @@ -6,7 +6,7 @@ * Version 1.0 June, 2017 * * An IR LED circuit *MUST* be connected to ESP8266 pin 4 (D2), unless you - * change the irsend() value below. + * change the IR_LED value below. * * TL;DR: The IR LED needs to be driven by a transistor for a good result. * @@ -35,7 +35,9 @@ #include #include -IRsend irsend(4); // An IR LED is controlled by GPIO pin 4 (D2) +#define IR_LED 4 // ESP8266 GPIO pin to use. Recommended: 4 (D2). + +IRsend irsend(IR_LED); // Set the GPIO to be used to sending the message. // Panasonic Plasma TV Descrete code (Power On). // Acquired from: @@ -96,10 +98,15 @@ void setup() { } void loop() { +#if SEND_PRONTO Serial.println("Sending a Samsung TV 'on' command."); irsend.sendPronto(samsungProntoCode, 72); delay(2000); Serial.println("Sending a Panasonic Plasma TV 'on' command."); irsend.sendPronto(panasonicProntoCode, 104); delay(2000); +#else // SEND_PRONTO + Serial.println("Can't send because SEND_PRONTO has been disabled."); + delay(10000); +#endif // SEND_PRONTO } diff --git a/lib/IRremoteESP8266/examples/JVCPanasonicSendDemo/JVCPanasonicSendDemo.ino b/lib/IRremoteESP8266/examples/JVCPanasonicSendDemo/JVCPanasonicSendDemo.ino index 9c70680..c98db14 100644 --- a/lib/IRremoteESP8266/examples/JVCPanasonicSendDemo/JVCPanasonicSendDemo.ino +++ b/lib/IRremoteESP8266/examples/JVCPanasonicSendDemo/JVCPanasonicSendDemo.ino @@ -4,7 +4,8 @@ * Based on Ken Shirriff's IrsendDemo Version 0.1 July, 2009, Copyright 2009 Ken Shirriff, http://arcfn.com * JVC and Panasonic protocol added by Kristian Lauszus (Thanks to zenwheel and other people at the original blog post) * - * An IR LED circuit *MUST* be connected to ESP8266 pin 4 (D2). + * An IR LED circuit *MUST* be connected to the ESP8266 on a pin + * as specified by IR_LED below. * * TL;DR: The IR LED needs to be driven by a transistor for a good result. * @@ -38,7 +39,9 @@ #define JVCPower 0xC5E8 -IRsend irsend(4); // An IR LED is controlled by GPIO pin 4 (D2) +#define IR_LED 4 // ESP8266 GPIO pin to use. Recommended: 4 (D2). + +IRsend irsend(IR_LED); // Set the GPIO to be used to sending the message. void setup() { irsend.begin(); @@ -46,11 +49,19 @@ void setup() { void loop() { // This should turn your TV on and off +#if SEND_PANASONIC irsend.sendPanasonic(PanasonicAddress, PanasonicPower); +#else // SEND_PANASONIC + Serial.println("Can't send because SEND_PANASONIC has been disabled."); +#endif // SEND_PANASONIC +#if SEND_JVC irsend.sendJVC(JVCPower, 16, 0); // hex value, 16 bits, no repeat // see http://www.sbprojects.com/knowledge/ir/jvc.php for information delayMicroseconds(50); irsend.sendJVC(JVCPower, 16, 1); // hex value, 16 bits, repeat delayMicroseconds(50); +#else // SEND_JVC + Serial.println("Can't send because SEND_JVC has been disabled."); +#endif // SEND_JVC } diff --git a/lib/IRremoteESP8266/examples/LGACSend/LGACSend.ino b/lib/IRremoteESP8266/examples/LGACSend/LGACSend.ino index 7cced0c..493d2a8 100644 --- a/lib/IRremoteESP8266/examples/LGACSend/LGACSend.ino +++ b/lib/IRremoteESP8266/examples/LGACSend/LGACSend.ino @@ -45,7 +45,11 @@ void Ac_Send_Code(uint32_t code) { Serial.print(" : "); Serial.println(code, HEX); +#if SEND_LG irsend.sendLG(code, 28); +#else // SEND_LG + Serial.println("Can't send because SEND_LG has been disabled."); +#endif // SEND_LG } void Ac_Activate(unsigned int temperature, unsigned int air_flow, @@ -171,8 +175,8 @@ void loop() { # 4 : air_flow 0 ~ 3 : flow # + : temp + 1 # - : temp - 1 - # c : cooling - # h : heating + # c : cooling + # h : heating # m : change cooling to air clean, air clean to cooling */ Serial.print("a : "); diff --git a/lib/IRremoteESP8266/examples/TurnOnArgoAC/TurnOnArgoAC.ino b/lib/IRremoteESP8266/examples/TurnOnArgoAC/TurnOnArgoAC.ino index a2e450a..1f37ecb 100644 --- a/lib/IRremoteESP8266/examples/TurnOnArgoAC/TurnOnArgoAC.ino +++ b/lib/IRremoteESP8266/examples/TurnOnArgoAC/TurnOnArgoAC.ino @@ -1,5 +1,6 @@ /* Copyright 2017 crankyoldgit -* An IR LED circuit *MUST* be connected to ESP8266 pin 4 (D2). +* An IR LED circuit *MUST* be connected to the ESP8266 on a pin +* as specified by IR_LED below. * * TL;DR: The IR LED needs to be driven by a transistor for a good result. * @@ -29,7 +30,8 @@ #include #include -IRArgoAC argoir(4); // An IR LED is controlled by GPIO pin 4 (D2) +#define IR_LED 4 // ESP8266 GPIO pin to use. Recommended: 4 (D2). +IRArgoAC argoir(IR_LED); // Set the GPIO to be used to sending the message. void setup() { argoir.begin(); @@ -45,8 +47,12 @@ void loop() { argoir.setCoolMode(ARGO_COOL_AUTO); argoir.setTemp(25); +#if SEND_ARGO // Now send the IR signal. argoir.send(); +#else // SEND_ARGO + Serial.println("Can't send because SEND_ARGO has been disabled."); +#endif // SEND_ARGO delay(5000); } diff --git a/lib/IRremoteESP8266/examples/TurnOnDaikinAC/TurnOnDaikinAC.ino b/lib/IRremoteESP8266/examples/TurnOnDaikinAC/TurnOnDaikinAC.ino index eb5cda5..ac58721 100644 --- a/lib/IRremoteESP8266/examples/TurnOnDaikinAC/TurnOnDaikinAC.ino +++ b/lib/IRremoteESP8266/examples/TurnOnDaikinAC/TurnOnDaikinAC.ino @@ -1,6 +1,7 @@ -/* Copyright 2016 sillyfrog +/* Copyright 2017 sillyfrog * -* An IR LED circuit *MUST* be connected to ESP8266 pin 4 (D2). +* An IR LED circuit *MUST* be connected to the ESP8266 on a pin +* as specified by IR_LED below. * * TL;DR: The IR LED needs to be driven by a transistor for a good result. * @@ -30,10 +31,11 @@ #include #include -IRDaikinESP dakinir(D2); // An IR LED is controlled by GPIO pin 4 (D2) +#define IR_LED 4 // ESP8266 GPIO pin to use. Recommended: 4 (D2). +IRDaikinESP daikinir(IR_LED); // Set the GPIO to be used to sending the message void setup() { - dakinir.begin(); + daikinir.begin(); Serial.begin(115200); } @@ -42,15 +44,26 @@ void loop() { Serial.println("Sending..."); // Set up what we want to send. See ir_Daikin.cpp for all the options. - dakinir.on(); - dakinir.setFan(1); - dakinir.setMode(DAIKIN_COOL); - dakinir.setTemp(25); - dakinir.setSwingVertical(0); - dakinir.setSwingHorizontal(0); + daikinir.on(); + daikinir.setFan(1); + daikinir.setMode(DAIKIN_COOL); + daikinir.setTemp(25); + daikinir.setSwingVertical(false); + daikinir.setSwingHorizontal(false); + + // Set the current time to 1:33PM (13:33) + // Time works in minutes past midnight + daikinir.setCurrentTime((13*60) + 33); + // Turn off about 1 hour later at 2:30PM (15:30) + daikinir.enableOffTimer((14*60) + 30); + + // Display what we are going to send. + Serial.println(daikinir.toString()); // Now send the IR signal. - dakinir.send(); +#if SEND_DAIKIN + daikinir.send(); +#endif // SEND_DAIKIN - delay(5000); + delay(15000); } diff --git a/lib/IRremoteESP8266/examples/TurnOnFujitsuAC/TurnOnFujitsuAC.ino b/lib/IRremoteESP8266/examples/TurnOnFujitsuAC/TurnOnFujitsuAC.ino index 52e31b5..86c6ab9 100644 --- a/lib/IRremoteESP8266/examples/TurnOnFujitsuAC/TurnOnFujitsuAC.ino +++ b/lib/IRremoteESP8266/examples/TurnOnFujitsuAC/TurnOnFujitsuAC.ino @@ -14,7 +14,7 @@ void printState() { // Display the encoded IR sequence. unsigned char* ir_code = fujitsu.getRaw(); Serial.print("IR Code: 0x"); - for (uint8_t i = 0; i < FUJITSU_AC_STATE_LENGTH; i++) + for (uint8_t i = 0; i < fujitsu.getStateLength(); i++) Serial.printf("%02X", ir_code[i]); Serial.println(); } @@ -24,7 +24,7 @@ void setup() { Serial.begin(115200); delay(200); - // Set up what we want to send. See ir_Mitsubishi.cpp for all the options. + // Set up what we want to send. See ir_Fujitsu.cpp for all the options. Serial.println("Default state of the remote."); printState(); Serial.println("Setting desired state for A/C."); @@ -38,7 +38,11 @@ void setup() { void loop() { // Now send the IR signal. Serial.println("Sending IR command to A/C ..."); +#if SEND_FUJITSU_AC fujitsu.send(); +#else // SEND_FUJITSU_AC + Serial.println("Can't send because SEND_FUJITSU_AC has been disabled."); +#endif // SEND_FUJITSU_AC printState(); delay(5000); } diff --git a/lib/IRremoteESP8266/examples/TurnOnKelvinatorAC/TurnOnKelvinatorAC.ino b/lib/IRremoteESP8266/examples/TurnOnKelvinatorAC/TurnOnKelvinatorAC.ino index 393ba28..069cd2d 100644 --- a/lib/IRremoteESP8266/examples/TurnOnKelvinatorAC/TurnOnKelvinatorAC.ino +++ b/lib/IRremoteESP8266/examples/TurnOnKelvinatorAC/TurnOnKelvinatorAC.ino @@ -1,6 +1,7 @@ /* Copyright 2016 David Conran * -* An IR LED circuit *MUST* be connected to ESP8266 pin 4 (D2). +* An IR LED circuit *MUST* be connected to the ESP8266 on a pin +* as specified by IR_LED below. * * TL;DR: The IR LED needs to be driven by a transistor for a good result. * @@ -29,7 +30,8 @@ #include #include -IRKelvinatorAC kelvir(D2); // An IR LED is controlled by GPIO pin 4 (D2) +#define IR_LED 4 // ESP8266 GPIO pin to use. Recommended: 4 (D2). +IRKelvinatorAC kelvir(IR_LED); // Set the GPIO to be used for sending messages. void printState() { // Display the settings. @@ -73,8 +75,10 @@ void setup() { void loop() { // Now send the IR signal. +#if SEND_KELVINATOR Serial.println("Sending IR command to A/C ..."); kelvir.send(); +#endif // SEND_KELVINATOR printState(); delay(5000); } diff --git a/lib/IRremoteESP8266/examples/TurnOnMitsubishiAC/TurnOnMitsubishiAC.ino b/lib/IRremoteESP8266/examples/TurnOnMitsubishiAC/TurnOnMitsubishiAC.ino index 1ea9b0d..598a91d 100644 --- a/lib/IRremoteESP8266/examples/TurnOnMitsubishiAC/TurnOnMitsubishiAC.ino +++ b/lib/IRremoteESP8266/examples/TurnOnMitsubishiAC/TurnOnMitsubishiAC.ino @@ -1,6 +1,7 @@ /* Copyright 2017 David Conran * -* An IR LED circuit *MUST* be connected to ESP8266 pin 4 (D2). +* An IR LED circuit *MUST* be connected to the ESP8266 on a pin +* as specified by IR_LED below. * * TL;DR: The IR LED needs to be driven by a transistor for a good result. * @@ -29,7 +30,8 @@ #include #include -IRMitsubishiAC mitsubir(D2); // An IR LED is controlled by GPIO pin 4 (D2) +#define IR_LED 4 // ESP8266 GPIO pin to use. Recommended: 4 (D2). +IRMitsubishiAC mitsubir(IR_LED); // Set the GPIO used for sending messages. void printState() { // Display the settings. @@ -64,8 +66,10 @@ void setup() { void loop() { // Now send the IR signal. +#if SEND_MITSUBISHI_AC Serial.println("Sending IR command to A/C ..."); mitsubir.send(); +#endif // SEND_MITSUBISHI_AC printState(); delay(5000); } diff --git a/lib/IRremoteESP8266/examples/TurnOnToshibaAC/TurnOnToshibaAC.ino b/lib/IRremoteESP8266/examples/TurnOnToshibaAC/TurnOnToshibaAC.ino new file mode 100644 index 0000000..89e5125 --- /dev/null +++ b/lib/IRremoteESP8266/examples/TurnOnToshibaAC/TurnOnToshibaAC.ino @@ -0,0 +1,73 @@ +/* Copyright 2017 David Conran +* +* An IR LED circuit *MUST* be connected to the ESP8266 on a pin +* as specified by IR_LED 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 + +#define IR_LED 4 // ESP8266 GPIO pin to use. Recommended: 4 (D2). +IRToshibaAC toshibair(IR_LED); // Set the GPIO to be used for sending messages. + +void printState() { + // Display the settings. + Serial.println("Toshiba A/C remote is in the following state:"); + Serial.printf(" Power: %d, Mode: %d, Temp: %dC, Fan Speed: %d\n", + toshibair.getPower(), toshibair.getMode(), toshibair.getTemp(), + toshibair.getFan()); + // Display the encoded IR sequence. + unsigned char* ir_code = toshibair.getRaw(); + Serial.print("IR Code: 0x"); + for (uint8_t i = 0; i < TOSHIBA_AC_STATE_LENGTH; i++) + Serial.printf("%02X", ir_code[i]); + Serial.println(); +} + +void setup() { + toshibair.begin(); + Serial.begin(115200); + delay(200); + + // Set up what we want to send. See ir_Toshiba.cpp for all the options. + Serial.println("Default state of the remote."); + printState(); + Serial.println("Setting desired state for A/C."); + toshibair.on(); + toshibair.setFan(1); + toshibair.setMode(TOSHIBA_AC_COOL); + toshibair.setTemp(26); +} + +void loop() { + // Now send the IR signal. +#if SEND_TOSHIBA_AC + Serial.println("Sending IR command to A/C ..."); + toshibair.send(); +#endif // SEND_TOSHIBA_AC + printState(); + delay(5000); +} diff --git a/lib/IRremoteESP8266/examples/TurnOnToshibaAC/platformio.ini b/lib/IRremoteESP8266/examples/TurnOnToshibaAC/platformio.ini new file mode 100644 index 0000000..eeb8d1f --- /dev/null +++ b/lib/IRremoteESP8266/examples/TurnOnToshibaAC/platformio.ini @@ -0,0 +1,17 @@ +[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/examples/TurnOnTrotecAC/TurnOnTrotecAC.ino b/lib/IRremoteESP8266/examples/TurnOnTrotecAC/TurnOnTrotecAC.ino index 2476ff5..28bd3f3 100644 --- a/lib/IRremoteESP8266/examples/TurnOnTrotecAC/TurnOnTrotecAC.ino +++ b/lib/IRremoteESP8266/examples/TurnOnTrotecAC/TurnOnTrotecAC.ino @@ -1,5 +1,6 @@ /* Copyright 2017 stufisher -* An IR LED circuit *MUST* be connected to ESP8266 pin 4 (D2). +* An IR LED circuit *MUST* be connected to the ESP8266 on a pin +* as specified by IR_LED below. * * TL;DR: The IR LED needs to be driven by a transistor for a good result. * @@ -29,7 +30,8 @@ #include #include -IRTrotecESP trotecir(D2); // An IR LED is controlled by GPIO pin 4 (D2) +#define IR_LED 4 // ESP8266 GPIO pin to use. Recommended: 4 (D2). +IRTrotecESP trotecir(IR_LED); // Set the GPIO to be used for sending messages. void setup() { trotecir.begin(); @@ -46,7 +48,11 @@ void loop() { trotecir.setTemp(25); // Now send the IR signal. +#if SEND_TROTEC trotecir.send(); +#else // SEND_TROTEC + Serial.println("Can't send because SEND_TROTEC has been disabled."); +#endif // SEND_TROTEC delay(5000); } diff --git a/lib/IRremoteESP8266/keywords.txt b/lib/IRremoteESP8266/keywords.txt index ab1b3f7..6f01f11 100644 --- a/lib/IRremoteESP8266/keywords.txt +++ b/lib/IRremoteESP8266/keywords.txt @@ -2,6 +2,12 @@ # Syntax Coloring Map For IRremoteESP8266 ######################################### +################################################ +# WARNING: Do NOT edit this file directly. +# It is generated by 'tools/mkkeywords' +# e.g. tools/mkkeywords > keywords.txt +################################################ + ####################################################### # The Arduino IDE requires the use of a tab separator # between the name and identifier. Without this tab the @@ -11,118 +17,1094 @@ ####################################################### ####################################### -# Datatypes (KEYWORD1) +# Datatypes & Classes (KEYWORD1) ####################################### -decode_results KEYWORD1 +IRArgoAC KEYWORD1 +IRDaikinESP KEYWORD1 +IRFujitsuAC KEYWORD1 +IRGreeAC KEYWORD1 +IRHaierAC KEYWORD1 +IRKelvinatorAC KEYWORD1 +IRMideaAC KEYWORD1 +IRMitsubishiAC KEYWORD1 +IRToshibaAC KEYWORD1 +IRTrotecESP KEYWORD1 IRrecv KEYWORD1 IRsend KEYWORD1 IRtimer KEYWORD1 +decode_results KEYWORD1 +ir_params_t KEYWORD1 +match_result_t KEYWORD1 ####################################### # Methods and Functions (KEYWORD2) ####################################### -decode KEYWORD2 -enableIRIn KEYWORD2 -disableIRIn KEYWORD2 -resume KEYWORD2 +_delayMicroseconds KEYWORD2 +add KEYWORD2 +addbit KEYWORD2 begin KEYWORD2 -send KEYWORD2 +buildFromState KEYWORD2 +buildState KEYWORD2 +calcBlockChecksum KEYWORD2 +calcChecksum KEYWORD2 +calcLGChecksum KEYWORD2 +calcUSecPeriod KEYWORD2 +calibrate KEYWORD2 +cancelTimers KEYWORD2 +checkheader KEYWORD2 +checksum KEYWORD2 +clearBit KEYWORD2 +compare KEYWORD2 +copyIrParams KEYWORD2 +decode KEYWORD2 +decodeAiwaRCT501 KEYWORD2 +decodeCOOLIX KEYWORD2 +decodeCarrierAC KEYWORD2 +decodeDISH KEYWORD2 +decodeDaikin KEYWORD2 +decodeDenon KEYWORD2 +decodeFujitsuAC KEYWORD2 +decodeGICable KEYWORD2 +decodeGree KEYWORD2 +decodeHaierAC KEYWORD2 +decodeHash KEYWORD2 +decodeHitachiAC KEYWORD2 +decodeJVC KEYWORD2 +decodeKelvinator KEYWORD2 +decodeLG KEYWORD2 +decodeLasertag KEYWORD2 +decodeMagiQuest KEYWORD2 +decodeMidea KEYWORD2 +decodeMitsubishi KEYWORD2 +decodeMitsubishi2 KEYWORD2 +decodeNEC KEYWORD2 +decodeNikai KEYWORD2 +decodePanasonic KEYWORD2 +decodeRC5 KEYWORD2 +decodeRC6 KEYWORD2 +decodeRCMM KEYWORD2 +decodeSAMSUNG KEYWORD2 +decodeSanyo KEYWORD2 +decodeSanyoLC7461 KEYWORD2 +decodeSharp KEYWORD2 +decodeSony KEYWORD2 +decodeToshibaAC KEYWORD2 +decodeWhynter KEYWORD2 +disableIRIn KEYWORD2 +disableOffTimer KEYWORD2 +disableOnTimer KEYWORD2 +elapsed KEYWORD2 +enableIRIn KEYWORD2 enableIROut KEYWORD2 -sendNEC KEYWORD2 -encodeNEC KEYWORD2 -sendLG KEYWORD2 +enableOffTimer KEYWORD2 +enableOnTimer KEYWORD2 +encodeJVC KEYWORD2 encodeLG KEYWORD2 -sendSony KEYWORD2 -encodeSony KEYWORD2 -sendSanyo KEYWORD2 -sendSanyoLC7461 KEYWORD2 +encodeMagiQuest KEYWORD2 +encodeNEC KEYWORD2 +encodePanasonic KEYWORD2 +encodeRC5 KEYWORD2 +encodeRC5X KEYWORD2 +encodeRC6 KEYWORD2 +encodeSAMSUNG KEYWORD2 encodeSanyoLC7461 KEYWORD2 -sendMitsubishi KEYWORD2 -sendRaw KEYWORD2 +encodeSharp KEYWORD2 +encodeSony KEYWORD2 +fixup KEYWORD2 +getBit KEYWORD2 +getBufSize KEYWORD2 +getCmd KEYWORD2 +getCommand KEYWORD2 +getCoolMode KEYWORD2 +getCorrectedRawLength KEYWORD2 +getCurrTime KEYWORD2 +getCurrentTime KEYWORD2 +getEcono KEYWORD2 +getEye KEYWORD2 +getFan KEYWORD2 +getFanSpeed KEYWORD2 +getFlap KEYWORD2 +getHealth KEYWORD2 +getHeatMode KEYWORD2 +getIonFilter KEYWORD2 +getLight KEYWORD2 +getMax KEYWORD2 +getMode KEYWORD2 +getMold KEYWORD2 +getNight KEYWORD2 +getOffTime KEYWORD2 +getOffTimer KEYWORD2 +getOffTimerEnabled KEYWORD2 +getOnTime KEYWORD2 +getOnTimer KEYWORD2 +getOnTimerEnabled KEYWORD2 +getPower KEYWORD2 +getPowerful KEYWORD2 +getQuiet KEYWORD2 +getRClevel KEYWORD2 +getRaw KEYWORD2 +getSensor KEYWORD2 +getSleep KEYWORD2 +getSpeed KEYWORD2 +getStateLength KEYWORD2 +getSwing KEYWORD2 +getSwingHorizontal KEYWORD2 +getSwingVertical KEYWORD2 +getSwingVerticalAuto KEYWORD2 +getSwingVerticalPosition KEYWORD2 +getTemp KEYWORD2 +getTime KEYWORD2 +getTimer KEYWORD2 +getTurbo KEYWORD2 +getVane KEYWORD2 +getXFan KEYWORD2 +getiFeel KEYWORD2 +hasACState KEYWORD2 +invertBits KEYWORD2 +ledOff KEYWORD2 +ledOn KEYWORD2 +mark KEYWORD2 +match KEYWORD2 +matchAtLeast KEYWORD2 +matchData KEYWORD2 +matchMark KEYWORD2 +matchSpace KEYWORD2 +off KEYWORD2 +on KEYWORD2 +printState KEYWORD2 +readbits KEYWORD2 +renderTime KEYWORD2 +reset KEYWORD2 +resultToHumanReadableBasic KEYWORD2 +resultToSourceCode KEYWORD2 +resultToTimingInfo KEYWORD2 +resume KEYWORD2 +reverseBits KEYWORD2 +send KEYWORD2 +sendAiwaRCT501 KEYWORD2 +sendArgo KEYWORD2 +sendCOOLIX KEYWORD2 +sendCarrierAC KEYWORD2 +sendDISH KEYWORD2 +sendDaikin KEYWORD2 +sendData KEYWORD2 +sendDenon KEYWORD2 +sendFujitsuAC KEYWORD2 sendGC KEYWORD2 +sendGICable KEYWORD2 +sendGeneric KEYWORD2 +sendGree KEYWORD2 +sendHaierAC KEYWORD2 +sendHitachiAC KEYWORD2 +sendHitachiAC1 KEYWORD2 +sendHitachiAC2 KEYWORD2 +sendJVC KEYWORD2 +sendKelvinator KEYWORD2 +sendLG KEYWORD2 +sendLasertag KEYWORD2 +sendMagiQuest KEYWORD2 +sendMidea KEYWORD2 +sendMitsubishi KEYWORD2 +sendMitsubishi2 KEYWORD2 +sendMitsubishiAC KEYWORD2 +sendNEC KEYWORD2 +sendNikai KEYWORD2 +sendPanasonic KEYWORD2 +sendPanasonic64 KEYWORD2 +sendPronto KEYWORD2 sendRC5 KEYWORD2 sendRC6 KEYWORD2 sendRCMM KEYWORD2 -sendDISH KEYWORD2 +sendRaw KEYWORD2 +sendSAMSUNG KEYWORD2 +sendSanyoLC7461 KEYWORD2 sendSharp KEYWORD2 sendSharpRaw KEYWORD2 -encodeSharp KEYWORD2 -sendPanasonic KEYWORD2 -sendPanasonic64 KEYWORD2 -encodePanasonic KEYWORD2 -sendJVC KEYWORD2 -encodeJVC KEYWORD2 -sendWhynter KEYWORD2 -sendSAMSUNG KEYWORD2 -encodeSAMSUNG KEYWORD2 -sendDaikin KEYWORD2 -sendCOOLIX KEYWORD2 -sendDenon KEYWORD2 -sendKelvinator KEYWORD2 sendSherwood KEYWORD2 -sendMitsubishiAC KEYWORD2 -mark KEYWORD2 +sendSony KEYWORD2 +sendToshibaAC KEYWORD2 +sendTrotec KEYWORD2 +sendWhynter KEYWORD2 +serialPrintUint64 KEYWORD2 +setBit KEYWORD2 +setCmd KEYWORD2 +setCommand KEYWORD2 +setCoolMode KEYWORD2 +setCurrTime KEYWORD2 +setCurrentTime KEYWORD2 +setEcono KEYWORD2 +setEye KEYWORD2 +setFan KEYWORD2 +setFanSpeed KEYWORD2 +setFlap KEYWORD2 +setHealth KEYWORD2 +setHeatMode KEYWORD2 +setIonFilter KEYWORD2 +setLight KEYWORD2 +setMax KEYWORD2 +setMode KEYWORD2 +setModel KEYWORD2 +setMold KEYWORD2 +setNight KEYWORD2 +setOffTimer KEYWORD2 +setOnTimer KEYWORD2 +setPower KEYWORD2 +setPowerful KEYWORD2 +setQuiet KEYWORD2 +setRaw KEYWORD2 +setRoomTemp KEYWORD2 +setSensor KEYWORD2 +setSleep KEYWORD2 +setSpeed KEYWORD2 +setSwing KEYWORD2 +setSwingHorizontal KEYWORD2 +setSwingVertical KEYWORD2 +setTemp KEYWORD2 +setTime KEYWORD2 +setTimer KEYWORD2 +setTurbo KEYWORD2 +setUnknownThreshold KEYWORD2 +setVane KEYWORD2 +setXFan KEYWORD2 +setiFeel KEYWORD2 space KEYWORD2 -reset KEYWORD2 -elapsed KEYWORD2 -calcLGChecksum KEYWORD2 -reverseBits KEYWORD2 +stateReset KEYWORD2 +stepHoriz KEYWORD2 +stepVert KEYWORD2 +sumBytes KEYWORD2 +ticksHigh KEYWORD2 +ticksLow KEYWORD2 +timeToString KEYWORD2 +toString KEYWORD2 +toggleRC5 KEYWORD2 +toggleRC6 KEYWORD2 +typeToString KEYWORD2 +uint64ToString KEYWORD2 +validChecksum KEYWORD2 ####################################### # Constants (LITERAL1) ####################################### -NEC LITERAL1 -SONY LITERAL1 -SANYO LITERAL1 +AIWA_RC_T501 LITERAL1 +AIWA_RC_T501_BITS LITERAL1 +AIWA_RC_T501_MIN_REPEAT LITERAL1 +AIWA_RC_T501_POST_BITS LITERAL1 +AIWA_RC_T501_POST_DATA LITERAL1 +AIWA_RC_T501_PRE_BITS LITERAL1 +AIWA_RC_T501_PRE_DATA LITERAL1 +ALLOW_DELAY_CALLS LITERAL1 +ARDB1 LITERAL1 +ARGO LITERAL1 +ARGO_BIT_MARK LITERAL1 +ARGO_COMMAND_LENGTH LITERAL1 +ARGO_COOL_AUTO 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 +ARGO_FAN_AUTO LITERAL1 +ARGO_FLAP_1 LITERAL1 +ARGO_FLAP_2 LITERAL1 +ARGO_FLAP_3 LITERAL1 +ARGO_FLAP_4 LITERAL1 +ARGO_FLAP_5 LITERAL1 +ARGO_FLAP_6 LITERAL1 +ARGO_FLAP_AUTO LITERAL1 +ARGO_FLAP_FULL LITERAL1 +ARGO_HDR_MARK LITERAL1 +ARGO_HDR_SPACE LITERAL1 +ARGO_HEAT_AUTO LITERAL1 +ARGO_HEAT_BLINK LITERAL1 +ARGO_HEAT_ON LITERAL1 +ARGO_MAX_TEMP LITERAL1 +ARGO_MIN_TEMP LITERAL1 +ARGO_ONE_SPACE LITERAL1 +ARGO_ZERO_SPACE LITERAL1 +ARRAH2E LITERAL1 +CARRIER_AC LITERAL1 +CARRIER_AC_BITS LITERAL1 +CARRIER_AC_BIT_MARK LITERAL1 +CARRIER_AC_GAP LITERAL1 +CARRIER_AC_HDR_MARK LITERAL1 +CARRIER_AC_HDR_SPACE LITERAL1 +CARRIER_AC_MIN_REPEAT LITERAL1 +CARRIER_AC_ONE_SPACE LITERAL1 +CARRIER_AC_ZERO_SPACE LITERAL1 +COOLIX LITERAL1 +COOLIX_BITS LITERAL1 +COOLIX_BIT_MARK LITERAL1 +COOLIX_BIT_MARK_TICKS LITERAL1 +COOLIX_HDR_MARK LITERAL1 +COOLIX_HDR_MARK_TICKS LITERAL1 +COOLIX_HDR_SPACE LITERAL1 +COOLIX_HDR_SPACE_TICKS LITERAL1 +COOLIX_MIN_GAP_TICKS LITERAL1 +COOLIX_ONE_SPACE LITERAL1 +COOLIX_ONE_SPACE_TICKS LITERAL1 +COOLIX_TICK LITERAL1 +COOLIX_ZERO_SPACE LITERAL1 +COOLIX_ZERO_SPACE_TICKS LITERAL1 +DAIKIN LITERAL1 +DAIKIN_AUTO LITERAL1 +DAIKIN_BITS LITERAL1 +DAIKIN_BIT_ECONO LITERAL1 +DAIKIN_BIT_EYE LITERAL1 +DAIKIN_BIT_MARK LITERAL1 +DAIKIN_BIT_MOLD LITERAL1 +DAIKIN_BIT_OFF_TIMER LITERAL1 +DAIKIN_BIT_ON_TIMER LITERAL1 +DAIKIN_BIT_POWER LITERAL1 +DAIKIN_BIT_POWERFUL LITERAL1 +DAIKIN_BIT_SENSOR LITERAL1 +DAIKIN_BIT_SILENT LITERAL1 +DAIKIN_BYTE_ECONO LITERAL1 +DAIKIN_BYTE_EYE LITERAL1 +DAIKIN_BYTE_MOLD LITERAL1 +DAIKIN_BYTE_OFF_TIMER LITERAL1 +DAIKIN_BYTE_ON_TIMER LITERAL1 +DAIKIN_BYTE_POWER LITERAL1 +DAIKIN_BYTE_POWERFUL LITERAL1 +DAIKIN_BYTE_SENSOR LITERAL1 +DAIKIN_BYTE_SILENT LITERAL1 +DAIKIN_COMMAND_LENGTH LITERAL1 +DAIKIN_COOL LITERAL1 +DAIKIN_CURBIT LITERAL1 +DAIKIN_CURINDEX LITERAL1 +DAIKIN_DEBUG LITERAL1 +DAIKIN_DRY LITERAL1 +DAIKIN_FAN LITERAL1 +DAIKIN_FAN_AUTO LITERAL1 +DAIKIN_FAN_MAX LITERAL1 +DAIKIN_FAN_MIN LITERAL1 +DAIKIN_FAN_QUIET LITERAL1 +DAIKIN_FIRST_HEADER64 LITERAL1 +DAIKIN_GAP LITERAL1 +DAIKIN_HDR_MARK LITERAL1 +DAIKIN_HDR_SPACE LITERAL1 +DAIKIN_HEAT LITERAL1 +DAIKIN_MARK_EXCESS LITERAL1 +DAIKIN_MAX_TEMP LITERAL1 +DAIKIN_MIN_TEMP LITERAL1 +DAIKIN_ONE_SPACE LITERAL1 +DAIKIN_RAW_BITS LITERAL1 +DAIKIN_TOLERANCE LITERAL1 +DAIKIN_ZERO_SPACE LITERAL1 +DECODE_AC LITERAL1 +DECODE_AIWA_RC_T501 LITERAL1 +DECODE_ARGO LITERAL1 +DECODE_CARRIER_AC LITERAL1 +DECODE_COOLIX LITERAL1 +DECODE_DAIKIN LITERAL1 +DECODE_DENON LITERAL1 +DECODE_DISH LITERAL1 +DECODE_FUJITSU_AC LITERAL1 +DECODE_GICABLE LITERAL1 +DECODE_GLOBALCACHE LITERAL1 +DECODE_GREE LITERAL1 +DECODE_HAIER_AC LITERAL1 +DECODE_HASH LITERAL1 +DECODE_HITACHI_AC LITERAL1 +DECODE_HITACHI_AC1 LITERAL1 +DECODE_HITACHI_AC2 LITERAL1 +DECODE_JVC LITERAL1 +DECODE_KELVINATOR LITERAL1 +DECODE_LASERTAG LITERAL1 +DECODE_LG LITERAL1 +DECODE_MAGIQUEST LITERAL1 +DECODE_MIDEA LITERAL1 +DECODE_MITSUBISHI LITERAL1 +DECODE_MITSUBISHI2 LITERAL1 +DECODE_MITSUBISHI_AC LITERAL1 +DECODE_NEC LITERAL1 +DECODE_NIKAI LITERAL1 +DECODE_PANASONIC LITERAL1 +DECODE_PRONTO LITERAL1 +DECODE_RC5 LITERAL1 +DECODE_RC6 LITERAL1 +DECODE_RCMM LITERAL1 +DECODE_SAMSUNG LITERAL1 +DECODE_SANYO LITERAL1 +DECODE_SHARP LITERAL1 +DECODE_SHERWOOD LITERAL1 +DECODE_SONY LITERAL1 +DECODE_TOSHIBA_AC LITERAL1 +DECODE_TROTEC LITERAL1 +DECODE_WHYNTER LITERAL1 +DENON LITERAL1 +DENON_48_BITS LITERAL1 +DENON_BITS LITERAL1 +DENON_BIT_MARK LITERAL1 +DENON_BIT_MARK_TICKS LITERAL1 +DENON_HDR_MARK LITERAL1 +DENON_HDR_MARK_TICKS LITERAL1 +DENON_HDR_SPACE LITERAL1 +DENON_HDR_SPACE_TICKS LITERAL1 +DENON_LEGACY_BITS LITERAL1 +DENON_MANUFACTURER LITERAL1 +DENON_MIN_COMMAND_LENGTH LITERAL1 +DENON_MIN_COMMAND_LENGTH_TICKS LITERAL1 +DENON_MIN_GAP_TICKS LITERAL1 +DENON_ONE_SPACE LITERAL1 +DENON_ONE_SPACE_TICKS LITERAL1 +DENON_TICK LITERAL1 +DENON_ZERO_SPACE LITERAL1 +DENON_ZERO_SPACE_TICKS LITERAL1 +DISH LITERAL1 +DISH_BITS LITERAL1 +DISH_BIT_MARK LITERAL1 +DISH_BIT_MARK_TICKS LITERAL1 +DISH_HDR_MARK LITERAL1 +DISH_HDR_MARK_TICKS LITERAL1 +DISH_HDR_SPACE LITERAL1 +DISH_HDR_SPACE_TICKS LITERAL1 +DISH_MIN_REPEAT LITERAL1 +DISH_ONE_SPACE LITERAL1 +DISH_ONE_SPACE_TICKS LITERAL1 +DISH_RPT_SPACE LITERAL1 +DISH_RPT_SPACE_TICKS LITERAL1 +DISH_TICK LITERAL1 +DISH_ZERO_SPACE LITERAL1 +DISH_ZERO_SPACE_TICKS LITERAL1 +DUTY_DEFAULT LITERAL1 +DUTY_MAX LITERAL1 +FNV_BASIS_32 LITERAL1 +FNV_PRIME_32 LITERAL1 +FOOTER LITERAL1 +FUJITSU_AC LITERAL1 +FUJITSU_AC_BITS LITERAL1 +FUJITSU_AC_BIT_MARK LITERAL1 +FUJITSU_AC_CMD_STAY_ON LITERAL1 +FUJITSU_AC_CMD_STEP_HORIZ LITERAL1 +FUJITSU_AC_CMD_STEP_VERT LITERAL1 +FUJITSU_AC_CMD_TURN_OFF LITERAL1 +FUJITSU_AC_CMD_TURN_ON LITERAL1 +FUJITSU_AC_FAN_AUTO LITERAL1 +FUJITSU_AC_FAN_HIGH LITERAL1 +FUJITSU_AC_FAN_LOW LITERAL1 +FUJITSU_AC_FAN_MED LITERAL1 +FUJITSU_AC_FAN_QUIET LITERAL1 +FUJITSU_AC_HDR_MARK LITERAL1 +FUJITSU_AC_HDR_SPACE LITERAL1 +FUJITSU_AC_MAX_TEMP LITERAL1 +FUJITSU_AC_MIN_BITS LITERAL1 +FUJITSU_AC_MIN_GAP LITERAL1 +FUJITSU_AC_MIN_REPEAT LITERAL1 +FUJITSU_AC_MIN_TEMP LITERAL1 +FUJITSU_AC_MODE_AUTO LITERAL1 +FUJITSU_AC_MODE_COOL LITERAL1 +FUJITSU_AC_MODE_DRY LITERAL1 +FUJITSU_AC_MODE_FAN LITERAL1 +FUJITSU_AC_MODE_HEAT LITERAL1 +FUJITSU_AC_ONE_SPACE LITERAL1 +FUJITSU_AC_STATE_LENGTH LITERAL1 +FUJITSU_AC_STATE_LENGTH_SHORT LITERAL1 +FUJITSU_AC_SWING_BOTH LITERAL1 +FUJITSU_AC_SWING_HORIZ LITERAL1 +FUJITSU_AC_SWING_OFF LITERAL1 +FUJITSU_AC_SWING_VERT LITERAL1 +FUJITSU_AC_ZERO_SPACE LITERAL1 +GICABLE LITERAL1 +GICABLE_BITS LITERAL1 +GICABLE_BIT_MARK LITERAL1 +GICABLE_HDR_MARK LITERAL1 +GICABLE_HDR_SPACE LITERAL1 +GICABLE_MIN_COMMAND_LENGTH LITERAL1 +GICABLE_MIN_GAP LITERAL1 +GICABLE_MIN_REPEAT LITERAL1 +GICABLE_ONE_SPACE LITERAL1 +GICABLE_RPT_SPACE LITERAL1 +GICABLE_ZERO_SPACE LITERAL1 +GLOBALCACHE LITERAL1 +GLOBALCACHE_FREQ_INDEX LITERAL1 +GLOBALCACHE_MIN_USEC LITERAL1 +GLOBALCACHE_RPT_INDEX LITERAL1 +GLOBALCACHE_RPT_START_INDEX LITERAL1 +GLOBALCACHE_START_INDEX LITERAL1 +GREE LITERAL1 +GREE_AUTO LITERAL1 +GREE_BITS LITERAL1 +GREE_BIT_MARK LITERAL1 +GREE_BLOCK_FOOTER LITERAL1 +GREE_BLOCK_FOOTER_BITS LITERAL1 +GREE_COOL LITERAL1 +GREE_DRY LITERAL1 +GREE_FAN LITERAL1 +GREE_FAN_MASK LITERAL1 +GREE_FAN_MAX LITERAL1 +GREE_HDR_MARK LITERAL1 +GREE_HDR_SPACE LITERAL1 +GREE_HEAT LITERAL1 +GREE_LIGHT_MASK LITERAL1 +GREE_MAX_TEMP LITERAL1 +GREE_MIN_TEMP LITERAL1 +GREE_MODE_MASK LITERAL1 +GREE_MSG_SPACE LITERAL1 +GREE_ONE_SPACE LITERAL1 +GREE_POWER1_MASK LITERAL1 +GREE_POWER2_MASK LITERAL1 +GREE_SLEEP_MASK LITERAL1 +GREE_STATE_LENGTH LITERAL1 +GREE_SWING_AUTO LITERAL1 +GREE_SWING_AUTO_MASK LITERAL1 +GREE_SWING_DOWN LITERAL1 +GREE_SWING_DOWN_AUTO LITERAL1 +GREE_SWING_LAST_POS LITERAL1 +GREE_SWING_MIDDLE LITERAL1 +GREE_SWING_MIDDLE_AUTO LITERAL1 +GREE_SWING_MIDDLE_DOWN LITERAL1 +GREE_SWING_MIDDLE_UP LITERAL1 +GREE_SWING_POS_MASK LITERAL1 +GREE_SWING_UP LITERAL1 +GREE_SWING_UP_AUTO LITERAL1 +GREE_TURBO_MASK LITERAL1 +GREE_XFAN_MASK LITERAL1 +GREE_ZERO_SPACE LITERAL1 +HAIER_AC LITERAL1 +HAIER_AC_AUTO LITERAL1 +HAIER_AC_BITS LITERAL1 +HAIER_AC_BIT_MARK LITERAL1 +HAIER_AC_CMD_FAN LITERAL1 +HAIER_AC_CMD_HEALTH LITERAL1 +HAIER_AC_CMD_MODE LITERAL1 +HAIER_AC_CMD_OFF LITERAL1 +HAIER_AC_CMD_ON LITERAL1 +HAIER_AC_CMD_SLEEP LITERAL1 +HAIER_AC_CMD_SWING LITERAL1 +HAIER_AC_CMD_TEMP_DOWN LITERAL1 +HAIER_AC_CMD_TEMP_UP LITERAL1 +HAIER_AC_CMD_TIMER_CANCEL LITERAL1 +HAIER_AC_CMD_TIMER_SET LITERAL1 +HAIER_AC_COOL LITERAL1 +HAIER_AC_DEF_TEMP LITERAL1 +HAIER_AC_DRY LITERAL1 +HAIER_AC_FAN LITERAL1 +HAIER_AC_FAN_AUTO LITERAL1 +HAIER_AC_FAN_HIGH LITERAL1 +HAIER_AC_FAN_LOW LITERAL1 +HAIER_AC_FAN_MED LITERAL1 +HAIER_AC_HDR LITERAL1 +HAIER_AC_HDR_GAP LITERAL1 +HAIER_AC_HEAT LITERAL1 +HAIER_AC_MAX_TEMP LITERAL1 +HAIER_AC_MAX_TIME LITERAL1 +HAIER_AC_MIN_GAP LITERAL1 +HAIER_AC_MIN_TEMP LITERAL1 +HAIER_AC_ONE_SPACE LITERAL1 +HAIER_AC_PREFIX LITERAL1 +HAIER_AC_STATE_LENGTH LITERAL1 +HAIER_AC_SWING_CHG LITERAL1 +HAIER_AC_SWING_DOWN LITERAL1 +HAIER_AC_SWING_OFF LITERAL1 +HAIER_AC_SWING_UP LITERAL1 +HAIER_AC_ZERO_SPACE LITERAL1 +HEADER LITERAL1 +HIGH LITERAL1 +HITACHI_AC LITERAL1 +HITACHI_AC1 LITERAL1 +HITACHI_AC1_BITS LITERAL1 +HITACHI_AC1_HDR_MARK LITERAL1 +HITACHI_AC1_HDR_SPACE LITERAL1 +HITACHI_AC1_STATE_LENGTH LITERAL1 +HITACHI_AC2 LITERAL1 +HITACHI_AC2_BITS LITERAL1 +HITACHI_AC2_STATE_LENGTH LITERAL1 +HITACHI_AC_BITS LITERAL1 +HITACHI_AC_BIT_MARK LITERAL1 +HITACHI_AC_HDR_MARK LITERAL1 +HITACHI_AC_HDR_SPACE LITERAL1 +HITACHI_AC_MIN_GAP LITERAL1 +HITACHI_AC_ONE_SPACE LITERAL1 +HITACHI_AC_STATE_LENGTH LITERAL1 +HITACHI_AC_ZERO_SPACE LITERAL1 +ICACHE_RAM_ATTR LITERAL1 +JVC LITERAL1 +JVC_BITS LITERAL1 +JVC_BIT_MARK LITERAL1 +JVC_BIT_MARK_TICKS LITERAL1 +JVC_HDR_MARK LITERAL1 +JVC_HDR_MARK_TICKS LITERAL1 +JVC_HDR_SPACE LITERAL1 +JVC_HDR_SPACE_TICKS LITERAL1 +JVC_MIN_GAP_TICKS LITERAL1 +JVC_ONE_SPACE LITERAL1 +JVC_ONE_SPACE_TICKS LITERAL1 +JVC_RPT_LENGTH LITERAL1 +JVC_RPT_LENGTH_TICKS LITERAL1 +JVC_TICK LITERAL1 +JVC_ZERO_SPACE LITERAL1 +JVC_ZERO_SPACE_TICKS LITERAL1 +KELVINATOR LITERAL1 +KELVINATOR_AUTO LITERAL1 +KELVINATOR_AUTO_TEMP LITERAL1 +KELVINATOR_BASIC_FAN_MASK LITERAL1 +KELVINATOR_BASIC_FAN_MAX LITERAL1 +KELVINATOR_BITS LITERAL1 +KELVINATOR_BIT_MARK LITERAL1 +KELVINATOR_BIT_MARK_TICKS LITERAL1 +KELVINATOR_CHECKSUM_START LITERAL1 +KELVINATOR_CMD_FOOTER LITERAL1 +KELVINATOR_CMD_FOOTER_BITS LITERAL1 +KELVINATOR_COOL LITERAL1 +KELVINATOR_DRY LITERAL1 +KELVINATOR_FAN LITERAL1 +KELVINATOR_FAN_AUTO LITERAL1 +KELVINATOR_FAN_MASK LITERAL1 +KELVINATOR_FAN_MAX LITERAL1 +KELVINATOR_FAN_OFFSET LITERAL1 +KELVINATOR_GAP_SPACE LITERAL1 +KELVINATOR_GAP_SPACE_TICKS LITERAL1 +KELVINATOR_HDR_MARK LITERAL1 +KELVINATOR_HDR_MARK_TICKS LITERAL1 +KELVINATOR_HDR_SPACE LITERAL1 +KELVINATOR_HDR_SPACE_TICKS LITERAL1 +KELVINATOR_HEAT LITERAL1 +KELVINATOR_ION_FILTER LITERAL1 +KELVINATOR_ION_FILTER_OFFSET LITERAL1 +KELVINATOR_LIGHT LITERAL1 +KELVINATOR_LIGHT_OFFSET LITERAL1 +KELVINATOR_MAX_TEMP LITERAL1 +KELVINATOR_MIN_TEMP LITERAL1 +KELVINATOR_MODE_MASK LITERAL1 +KELVINATOR_ONE_SPACE LITERAL1 +KELVINATOR_ONE_SPACE_TICKS LITERAL1 +KELVINATOR_POWER LITERAL1 +KELVINATOR_QUIET LITERAL1 +KELVINATOR_QUIET_OFFSET LITERAL1 +KELVINATOR_SLEEP_1_AND_3 LITERAL1 +KELVINATOR_STATE_LENGTH LITERAL1 +KELVINATOR_TICK LITERAL1 +KELVINATOR_TURBO LITERAL1 +KELVINATOR_TURBO_OFFSET LITERAL1 +KELVINATOR_VENT_SWING LITERAL1 +KELVINATOR_VENT_SWING_H LITERAL1 +KELVINATOR_VENT_SWING_OFFSET LITERAL1 +KELVINATOR_VENT_SWING_V LITERAL1 +KELVINATOR_XFAN LITERAL1 +KELVINATOR_XFAN_OFFSET LITERAL1 +KELVINATOR_ZERO_SPACE LITERAL1 +KELVINATOR_ZERO_SPACE_TICKS LITERAL1 +LASERTAG LITERAL1 +LASERTAG_BITS LITERAL1 +LASERTAG_DELTA LITERAL1 +LASERTAG_EXCESS LITERAL1 +LASERTAG_MIN_GAP LITERAL1 +LASERTAG_MIN_REPEAT LITERAL1 +LASERTAG_TICK LITERAL1 +LASERTAG_TOLERANCE LITERAL1 +LG LITERAL1 +LG32_BITS LITERAL1 +LG32_HDR_MARK LITERAL1 +LG32_HDR_MARK_TICKS LITERAL1 +LG32_HDR_SPACE LITERAL1 +LG32_HDR_SPACE_TICKS LITERAL1 +LG32_RPT_HDR_MARK LITERAL1 +LG32_RPT_HDR_MARK_TICKS LITERAL1 +LG_BITS LITERAL1 +LG_BIT_MARK LITERAL1 +LG_BIT_MARK_TICKS LITERAL1 +LG_HDR_MARK LITERAL1 +LG_HDR_MARK_TICKS LITERAL1 +LG_HDR_SPACE LITERAL1 +LG_HDR_SPACE_TICKS LITERAL1 +LG_MIN_GAP LITERAL1 +LG_MIN_GAP_TICKS LITERAL1 +LG_MIN_MESSAGE_LENGTH LITERAL1 +LG_MIN_MESSAGE_LENGTH_TICKS LITERAL1 +LG_ONE_SPACE LITERAL1 +LG_ONE_SPACE_TICKS LITERAL1 +LG_RPT_SPACE LITERAL1 +LG_RPT_SPACE_TICKS LITERAL1 +LG_TICK LITERAL1 +LG_ZERO_SPACE LITERAL1 +LG_ZERO_SPACE_TICKS LITERAL1 +LOW LITERAL1 +MAGIQUEST LITERAL1 +MAGIQUEST_BITS LITERAL1 +MAGIQUEST_GAP LITERAL1 +MAGIQUEST_MARK_ONE LITERAL1 +MAGIQUEST_MARK_ZERO LITERAL1 +MAGIQUEST_ONE_RATIO LITERAL1 +MAGIQUEST_PERIOD LITERAL1 +MAGIQUEST_SPACE_ONE LITERAL1 +MAGIQUEST_SPACE_ZERO LITERAL1 +MAGIQUEST_TOTAL_USEC LITERAL1 +MAGIQUEST_ZERO_RATIO LITERAL1 +MARK_EXCESS LITERAL1 +MAX_ACCURATE_USEC_DELAY LITERAL1 +MAX_TIMEOUT_MS LITERAL1 +MIDEA LITERAL1 +MIDEA_AC_AUTO LITERAL1 +MIDEA_AC_CHECKSUM_MASK LITERAL1 +MIDEA_AC_COOL LITERAL1 +MIDEA_AC_DRY LITERAL1 +MIDEA_AC_FAN LITERAL1 +MIDEA_AC_FAN_AUTO LITERAL1 +MIDEA_AC_FAN_HI LITERAL1 +MIDEA_AC_FAN_LOW LITERAL1 +MIDEA_AC_FAN_MASK LITERAL1 +MIDEA_AC_FAN_MED LITERAL1 +MIDEA_AC_HEAT LITERAL1 +MIDEA_AC_MAX_TEMP_C LITERAL1 +MIDEA_AC_MAX_TEMP_F LITERAL1 +MIDEA_AC_MIN_TEMP_C LITERAL1 +MIDEA_AC_MIN_TEMP_F LITERAL1 +MIDEA_AC_MODE_MASK LITERAL1 +MIDEA_AC_POWER LITERAL1 +MIDEA_AC_SLEEP LITERAL1 +MIDEA_AC_STATE_MASK LITERAL1 +MIDEA_AC_TEMP_MASK LITERAL1 +MIDEA_BITS LITERAL1 +MIDEA_BIT_MARK LITERAL1 +MIDEA_BIT_MARK_TICKS LITERAL1 +MIDEA_HDR_MARK LITERAL1 +MIDEA_HDR_MARK_TICKS LITERAL1 +MIDEA_HDR_SPACE LITERAL1 +MIDEA_HDR_SPACE_TICKS LITERAL1 +MIDEA_MIN_GAP_TICKS LITERAL1 +MIDEA_MIN_REPEAT LITERAL1 +MIDEA_ONE_SPACE LITERAL1 +MIDEA_ONE_SPACE_TICKS LITERAL1 +MIDEA_TICK LITERAL1 +MIDEA_TOLERANCE LITERAL1 +MIDEA_ZERO_SPACE LITERAL1 +MIDEA_ZERO_SPACE_TICKS LITERAL1 +MIN_LASERTAG_SAMPLES LITERAL1 +MIN_RC5_SAMPLES LITERAL1 +MIN_RC6_SAMPLES LITERAL1 MITSUBISHI LITERAL1 +MITSUBISHI2 LITERAL1 +MITSUBISHI2_BIT_MARK LITERAL1 +MITSUBISHI2_HDR_SPACE LITERAL1 +MITSUBISHI2_MIN_GAP LITERAL1 +MITSUBISHI2_ONE_SPACE LITERAL1 +MITSUBISHI2_ZERO_SPACE LITERAL1 +MITSUBISHI_AC LITERAL1 +MITSUBISHI_AC_AUTO LITERAL1 +MITSUBISHI_AC_BIT_MARK LITERAL1 +MITSUBISHI_AC_COOL LITERAL1 +MITSUBISHI_AC_DRY LITERAL1 +MITSUBISHI_AC_FAN_AUTO LITERAL1 +MITSUBISHI_AC_FAN_MAX LITERAL1 +MITSUBISHI_AC_FAN_REAL_MAX LITERAL1 +MITSUBISHI_AC_FAN_SILENT LITERAL1 +MITSUBISHI_AC_HDR_MARK LITERAL1 +MITSUBISHI_AC_HDR_SPACE LITERAL1 +MITSUBISHI_AC_HEAT LITERAL1 +MITSUBISHI_AC_MAX_TEMP LITERAL1 +MITSUBISHI_AC_MIN_REPEAT LITERAL1 +MITSUBISHI_AC_MIN_TEMP LITERAL1 +MITSUBISHI_AC_ONE_SPACE LITERAL1 +MITSUBISHI_AC_POWER LITERAL1 +MITSUBISHI_AC_RPT_MARK LITERAL1 +MITSUBISHI_AC_RPT_SPACE LITERAL1 +MITSUBISHI_AC_STATE_LENGTH LITERAL1 +MITSUBISHI_AC_VANE_AUTO LITERAL1 +MITSUBISHI_AC_VANE_AUTO_MOVE LITERAL1 +MITSUBISHI_AC_ZERO_SPACE LITERAL1 +MITSUBISHI_BITS LITERAL1 +MITSUBISHI_BIT_MARK LITERAL1 +MITSUBISHI_BIT_MARK_TICKS LITERAL1 +MITSUBISHI_MIN_COMMAND_LENGTH LITERAL1 +MITSUBISHI_MIN_GAP LITERAL1 +MITSUBISHI_MIN_REPEAT LITERAL1 +MITSUBISHI_ONE_SPACE LITERAL1 +MITSUBISHI_TICK LITERAL1 +MITSUBISHI_ZERO_SPACE LITERAL1 +NEC LITERAL1 +NEC_BITS LITERAL1 +NEC_BIT_MARK LITERAL1 +NEC_BIT_MARK_TICKS LITERAL1 +NEC_HDR_MARK LITERAL1 +NEC_HDR_MARK_TICKS LITERAL1 +NEC_HDR_SPACE LITERAL1 +NEC_HDR_SPACE_TICKS LITERAL1 +NEC_LIKE LITERAL1 +NEC_MIN_COMMAND_LENGTH LITERAL1 +NEC_MIN_COMMAND_LENGTH_TICKS LITERAL1 +NEC_MIN_GAP LITERAL1 +NEC_ONE_SPACE LITERAL1 +NEC_ONE_SPACE_TICKS LITERAL1 +NEC_RPT_LENGTH LITERAL1 +NEC_RPT_SPACE LITERAL1 +NEC_RPT_SPACE_TICKS LITERAL1 +NEC_TICK LITERAL1 +NEC_ZERO_SPACE LITERAL1 +NEC_ZERO_SPACE_TICKS LITERAL1 +NIKAI LITERAL1 +NIKAI_BITS LITERAL1 +NIKAI_BIT_MARK LITERAL1 +NIKAI_BIT_MARK_TICKS LITERAL1 +NIKAI_HDR_MARK LITERAL1 +NIKAI_HDR_MARK_TICKS LITERAL1 +NIKAI_HDR_SPACE LITERAL1 +NIKAI_HDR_SPACE_TICKS LITERAL1 +NIKAI_MIN_GAP LITERAL1 +NIKAI_MIN_GAP_TICKS LITERAL1 +NIKAI_ONE_SPACE LITERAL1 +NIKAI_ONE_SPACE_TICKS LITERAL1 +NIKAI_ZERO_SPACE LITERAL1 +NIKAI_ZERO_SPACE_TICKS LITERAL1 +OFFSET_ERR LITERAL1 +OFFSET_START LITERAL1 +PANASONIC LITERAL1 +PANASONIC_BITS LITERAL1 +PANASONIC_BIT_MARK LITERAL1 +PANASONIC_BIT_MARK_TICKS LITERAL1 +PANASONIC_HDR_MARK LITERAL1 +PANASONIC_HDR_MARK_TICKS LITERAL1 +PANASONIC_HDR_SPACE LITERAL1 +PANASONIC_HDR_SPACE_TICKS LITERAL1 +PANASONIC_MANUFACTURER LITERAL1 +PANASONIC_MIN_COMMAND_LENGTH LITERAL1 +PANASONIC_MIN_COMMAND_LENGTH_TICKS LITERAL1 +PANASONIC_MIN_GAP_TICKS LITERAL1 +PANASONIC_ONE_SPACE LITERAL1 +PANASONIC_ONE_SPACE_TICKS LITERAL1 +PANASONIC_TICK LITERAL1 +PANASONIC_ZERO_SPACE LITERAL1 +PANASONIC_ZERO_SPACE_TICKS LITERAL1 +PERIOD_OFFSET LITERAL1 +PRONTO LITERAL1 +PRONTO_DATA_OFFSET LITERAL1 +PRONTO_FREQ_FACTOR LITERAL1 +PRONTO_FREQ_OFFSET LITERAL1 +PRONTO_MIN_LENGTH LITERAL1 +PRONTO_SEQ_1_LEN_OFFSET LITERAL1 +PRONTO_SEQ_2_LEN_OFFSET LITERAL1 +PRONTO_TYPE_OFFSET LITERAL1 +RAW LITERAL1 +RAWBUF LITERAL1 +RAWTICK LITERAL1 RC5 LITERAL1 RC5X LITERAL1 +RC5X_BITS LITERAL1 +RC5_BITS LITERAL1 +RC5_MIN_COMMAND_LENGTH LITERAL1 +RC5_MIN_GAP LITERAL1 +RC5_RAW_BITS LITERAL1 +RC5_T1 LITERAL1 +RC5_TOGGLE_MASK LITERAL1 RC6 LITERAL1 -DISH LITERAL1 -SHARP LITERAL1 -PANASONIC LITERAL1 -JVC LITERAL1 -LG LITERAL1 -SAMSUNG LITERAL1 -WHYNTER LITERAL1 -AIWA_RC_T501 LITERAL1 -COOLIX LITERAL1 -UNKNOWN LITERAL1 -REPEAT LITERAL1 -DENON LITERAL1 -DAIKIN LITERAL1 -KELVINATOR LITERAL1 -SHERWOOD LITERAL1 -MITSUBISHIAC LITERAL1 +RC6_36_BITS LITERAL1 +RC6_36_TOGGLE_MASK LITERAL1 +RC6_HDR_MARK LITERAL1 +RC6_HDR_MARK_TICKS LITERAL1 +RC6_HDR_SPACE LITERAL1 +RC6_HDR_SPACE_TICKS LITERAL1 +RC6_MODE0_BITS LITERAL1 +RC6_RPT_LENGTH LITERAL1 +RC6_RPT_LENGTH_TICKS LITERAL1 +RC6_TICK LITERAL1 +RC6_TOGGLE_MASK LITERAL1 RCMM LITERAL1 +RCMM_BITS LITERAL1 +RCMM_BIT_MARK LITERAL1 +RCMM_BIT_MARK_TICKS LITERAL1 +RCMM_BIT_SPACE_0 LITERAL1 +RCMM_BIT_SPACE_0_TICKS LITERAL1 +RCMM_BIT_SPACE_1 LITERAL1 +RCMM_BIT_SPACE_1_TICKS LITERAL1 +RCMM_BIT_SPACE_2 LITERAL1 +RCMM_BIT_SPACE_2_TICKS LITERAL1 +RCMM_BIT_SPACE_3 LITERAL1 +RCMM_BIT_SPACE_3_TICKS LITERAL1 +RCMM_EXCESS LITERAL1 +RCMM_HDR_MARK LITERAL1 +RCMM_HDR_MARK_TICKS LITERAL1 +RCMM_HDR_SPACE LITERAL1 +RCMM_HDR_SPACE_TICKS LITERAL1 +RCMM_MIN_GAP LITERAL1 +RCMM_MIN_GAP_TICKS LITERAL1 +RCMM_RPT_LENGTH LITERAL1 +RCMM_RPT_LENGTH_TICKS LITERAL1 +RCMM_TICK LITERAL1 +RCMM_TOLERANCE LITERAL1 +REPEAT LITERAL1 +SAMSUNG LITERAL1 +SAMSUNG_BITS LITERAL1 +SAMSUNG_BIT_MARK LITERAL1 +SAMSUNG_BIT_MARK_TICKS LITERAL1 +SAMSUNG_HDR_MARK LITERAL1 +SAMSUNG_HDR_MARK_TICKS LITERAL1 +SAMSUNG_HDR_SPACE LITERAL1 +SAMSUNG_HDR_SPACE_TICKS LITERAL1 +SAMSUNG_MIN_MESSAGE_LENGTH LITERAL1 +SAMSUNG_MIN_MESSAGE_LENGTH_TICKS LITERAL1 +SAMSUNG_ONE_SPACE LITERAL1 +SAMSUNG_ONE_SPACE_TICKS LITERAL1 +SAMSUNG_RPT_SPACE LITERAL1 +SAMSUNG_RPT_SPACE_TICKS LITERAL1 +SAMSUNG_TICK LITERAL1 +SAMSUNG_ZERO_SPACE LITERAL1 +SAMSUNG_ZERO_SPACE_TICKS LITERAL1 +SANYO LITERAL1 SANYO_LC7461 LITERAL1 -NEC_BITS LITERAL1 +SANYO_LC7461_ADDRESS_BITS LITERAL1 +SANYO_LC7461_ADDRESS_MASK LITERAL1 +SANYO_LC7461_BITS LITERAL1 +SANYO_LC7461_BIT_MARK LITERAL1 +SANYO_LC7461_COMMAND_BITS LITERAL1 +SANYO_LC7461_COMMAND_MASK LITERAL1 +SANYO_LC7461_HDR_MARK LITERAL1 +SANYO_LC7461_HDR_SPACE LITERAL1 +SANYO_LC7461_MIN_COMMAND_LENGTH LITERAL1 +SANYO_LC7461_MIN_GAP LITERAL1 +SANYO_LC7461_ONE_SPACE LITERAL1 +SANYO_LC7461_ZERO_SPACE LITERAL1 +SANYO_SA8650B_BITS LITERAL1 +SANYO_SA8650B_DOUBLE_SPACE_USECS LITERAL1 +SANYO_SA8650B_HDR_MARK LITERAL1 +SANYO_SA8650B_HDR_SPACE LITERAL1 +SANYO_SA8650B_ONE_MARK LITERAL1 +SANYO_SA8650B_RPT_LENGTH LITERAL1 +SANYO_SA8650B_ZERO_MARK LITERAL1 +SEND_AIWA_RC_T501 LITERAL1 +SEND_ARGO LITERAL1 +SEND_CARRIER_AC LITERAL1 +SEND_COOLIX LITERAL1 +SEND_DAIKIN LITERAL1 +SEND_DENON LITERAL1 +SEND_DISH LITERAL1 +SEND_FUJITSU_AC LITERAL1 +SEND_GICABLE LITERAL1 +SEND_GLOBALCACHE LITERAL1 +SEND_GREE LITERAL1 +SEND_HAIER_AC LITERAL1 +SEND_HITACHI_AC LITERAL1 +SEND_HITACHI_AC1 LITERAL1 +SEND_HITACHI_AC2 LITERAL1 +SEND_JVC LITERAL1 +SEND_KELVINATOR LITERAL1 +SEND_LASERTAG LITERAL1 +SEND_LG LITERAL1 +SEND_MAGIQUEST LITERAL1 +SEND_MIDEA LITERAL1 +SEND_MITSUBISHI LITERAL1 +SEND_MITSUBISHI2 LITERAL1 +SEND_MITSUBISHI_AC LITERAL1 +SEND_NEC LITERAL1 +SEND_NIKAI LITERAL1 +SEND_PANASONIC LITERAL1 +SEND_PRONTO LITERAL1 +SEND_RAW LITERAL1 +SEND_RC5 LITERAL1 +SEND_RC6 LITERAL1 +SEND_RCMM LITERAL1 +SEND_SAMSUNG LITERAL1 +SEND_SANYO LITERAL1 +SEND_SHARP LITERAL1 +SEND_SHERWOOD LITERAL1 +SEND_SONY LITERAL1 +SEND_TOSHIBA_AC LITERAL1 +SEND_TROTEC LITERAL1 +SEND_WHYNTER LITERAL1 +SHARP LITERAL1 +SHARP_ADDRESS_MASK LITERAL1 +SHARP_BITS LITERAL1 +SHARP_BIT_MARK LITERAL1 +SHARP_BIT_MARK_TICKS LITERAL1 +SHARP_COMMAND_BITS LITERAL1 +SHARP_COMMAND_MASK LITERAL1 +SHARP_GAP LITERAL1 +SHARP_GAP_TICKS LITERAL1 +SHARP_ONE_SPACE LITERAL1 +SHARP_ONE_SPACE_TICKS LITERAL1 +SHARP_TOGGLE_MASK LITERAL1 +SHARP_ZERO_SPACE LITERAL1 +SHARP_ZERO_SPACE_TICKS LITERAL1 +SHERWOOD LITERAL1 SHERWOOD_BITS LITERAL1 -SONY_MIN_BITS LITERAL1 +SHERWOOD_MIN_REPEAT LITERAL1 +SONY LITERAL1 SONY_12_BITS LITERAL1 SONY_15_BITS LITERAL1 SONY_20_BITS LITERAL1 -SANYO_SA8650B_BITS LITERAL1 -MITSUBISHI_BITS LITERAL1 -PANASONIC_BITS LITERAL1 -JVC_BITS LITERAL1 -LG_BITS LITERAL1 -SAMSUNG_BITS LITERAL1 -COOLIX_BITS LITERAL1 -DAIKIN_BITS LITERAL1 -RC5X_BITS LITERAL1 -RC6_36_BITS LITERAL1 -RC6_MODE0_BITS LITERAL1 -RCMM_BITS LITERAL1 -WHYNTER_BITS LITERAL1 -SANYO_LC7461_BITS LITERAL1 -SHARP_BITS LITERAL1 -DISH_BITS LITERAL1 -DENON_BITS LITERAL1 +SONY_HDR_MARK LITERAL1 +SONY_HDR_MARK_TICKS LITERAL1 +SONY_MIN_BITS LITERAL1 +SONY_MIN_GAP LITERAL1 +SONY_MIN_GAP_TICKS LITERAL1 SONY_MIN_REPEAT LITERAL1 -MITSUBISHI_MIN_REPEAT LITERAL1 -DISH_MIN_REPEAT LITERAL1 -SHERWOOD_MIN_REPEAT LITERAL1 +SONY_ONE_MARK LITERAL1 +SONY_ONE_MARK_TICKS LITERAL1 +SONY_RPT_LENGTH LITERAL1 +SONY_RPT_LENGTH_TICKS LITERAL1 +SONY_SPACE LITERAL1 +SONY_SPACE_TICKS LITERAL1 +SONY_TICK LITERAL1 +SONY_ZERO_MARK LITERAL1 +SONY_ZERO_MARK_TICKS LITERAL1 +STATE_IDLE LITERAL1 +STATE_MARK LITERAL1 +STATE_SIZE_MAX LITERAL1 +STATE_SPACE LITERAL1 +STATE_STOP LITERAL1 +TIMEOUT_MS LITERAL1 +TOLERANCE LITERAL1 +TOSHIBA_AC LITERAL1 +TOSHIBA_AC_AUTO LITERAL1 +TOSHIBA_AC_BITS LITERAL1 +TOSHIBA_AC_BIT_MARK LITERAL1 +TOSHIBA_AC_COOL LITERAL1 +TOSHIBA_AC_DRY LITERAL1 +TOSHIBA_AC_FAN_AUTO LITERAL1 +TOSHIBA_AC_FAN_MAX LITERAL1 +TOSHIBA_AC_HDR_MARK LITERAL1 +TOSHIBA_AC_HDR_SPACE LITERAL1 +TOSHIBA_AC_HEAT LITERAL1 +TOSHIBA_AC_MAX_TEMP LITERAL1 +TOSHIBA_AC_MIN_GAP LITERAL1 +TOSHIBA_AC_MIN_REPEAT LITERAL1 +TOSHIBA_AC_MIN_TEMP LITERAL1 +TOSHIBA_AC_ONE_SPACE LITERAL1 +TOSHIBA_AC_POWER LITERAL1 +TOSHIBA_AC_STATE_LENGTH LITERAL1 +TOSHIBA_AC_ZERO_SPACE LITERAL1 +TROTEC LITERAL1 +TROTEC_AUTO LITERAL1 +TROTEC_COMMAND_LENGTH LITERAL1 +TROTEC_COOL LITERAL1 +TROTEC_DEF_TEMP LITERAL1 +TROTEC_DRY LITERAL1 +TROTEC_FAN LITERAL1 +TROTEC_FAN_HIGH LITERAL1 +TROTEC_FAN_LOW LITERAL1 +TROTEC_FAN_MED LITERAL1 +TROTEC_GAP LITERAL1 +TROTEC_GAP_END LITERAL1 +TROTEC_HDR_MARK LITERAL1 +TROTEC_HDR_SPACE LITERAL1 +TROTEC_INTRO1 LITERAL1 +TROTEC_INTRO2 LITERAL1 +TROTEC_MAX_TEMP LITERAL1 +TROTEC_MAX_TIMER LITERAL1 +TROTEC_MIN_TEMP LITERAL1 +TROTEC_MIN_TIMER LITERAL1 +TROTEC_OFF LITERAL1 +TROTEC_ON LITERAL1 +TROTEC_ONE_MARK LITERAL1 +TROTEC_ONE_SPACE LITERAL1 +TROTEC_SLEEP_ON LITERAL1 +TROTEC_TIMER_ON LITERAL1 +TROTEC_ZERO_MARK LITERAL1 +TROTEC_ZERO_SPACE LITERAL1 +UNKNOWN LITERAL1 +UNKNOWN_THRESHOLD LITERAL1 +UNUSED LITERAL1 +WHYNTER LITERAL1 +WHYNTER_BITS LITERAL1 +WHYNTER_BIT_MARK LITERAL1 +WHYNTER_HDR_MARK LITERAL1 +WHYNTER_HDR_MARK_TICKS LITERAL1 +WHYNTER_HDR_SPACE LITERAL1 +WHYNTER_HDR_SPACE_TICKS LITERAL1 +WHYNTER_MIN_COMMAND_LENGTH LITERAL1 +WHYNTER_ONE_SPACE LITERAL1 +WHYNTER_ONE_SPACE_TICKS LITERAL1 +WHYNTER_TICK LITERAL1 +WHYNTER_ZERO_SPACE LITERAL1 diff --git a/lib/IRremoteESP8266/library.json b/lib/IRremoteESP8266/library.json index 082a59e..dac47af 100644 --- a/lib/IRremoteESP8266/library.json +++ b/lib/IRremoteESP8266/library.json @@ -1,6 +1,6 @@ { "name": "IRremoteESP8266", - "version": "2.2.0", + "version": "2.4.2", "keywords": "infrared, ir, remote, esp8266", "description": "Send and receive infrared signals with multiple protocols (ESP8266)", "repository": diff --git a/lib/IRremoteESP8266/library.properties b/lib/IRremoteESP8266/library.properties index 69bc07b..f972c06 100644 --- a/lib/IRremoteESP8266/library.properties +++ b/lib/IRremoteESP8266/library.properties @@ -1,5 +1,5 @@ name=IRremoteESP8266 -version=2.2.0 +version=2.4.2 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/pylintrc b/lib/IRremoteESP8266/pylintrc new file mode 100644 index 0000000..987c6ab --- /dev/null +++ b/lib/IRremoteESP8266/pylintrc @@ -0,0 +1,12 @@ +[REPORTS] + +# Tells whether to display a full report or only the messages +reports=no + +[FORMAT] + +# Maximum number of characters on a single line. +max-line-length=80 + +# String used as indentation unit. +indent-string=' ' diff --git a/lib/IRremoteESP8266/src/IRrecv.cpp b/lib/IRremoteESP8266/src/IRrecv.cpp index 5b1df32..3dc4364 100644 --- a/lib/IRremoteESP8266/src/IRrecv.cpp +++ b/lib/IRremoteESP8266/src/IRrecv.cpp @@ -90,7 +90,7 @@ static void ICACHE_RAM_ATTR gpio_intr() { // (Default: TIMEOUT_MS) // save_buffer: Use a second (save) buffer to decode from. (Def: false) // Returns: -// A IRrecv class object. +// An IRrecv class object. IRrecv::IRrecv(uint16_t recvpin, uint16_t bufsize, uint8_t timeout, bool save_buffer) { irparams.recvpin = recvpin; @@ -121,6 +121,9 @@ IRrecv::IRrecv(uint16_t recvpin, uint16_t bufsize, uint8_t timeout, } else { irparams_save = NULL; } +#if DECODE_HASH + unknown_threshold = UNKNOWN_THRESHOLD; +#endif // DECODE_HASH } // Class destructor @@ -198,6 +201,13 @@ uint16_t IRrecv::getBufSize() { return irparams.bufsize; } +#if DECODE_HASH +// Set the minimum length we will consider for reporting UNKNOWN message types. +void IRrecv::setUnknownThreshold(uint16_t length) { + unknown_threshold = length; +} +#endif // DECODE_HASH + // Decodes the received IR message. // If the interrupt state is saved, we will immediately resume waiting // for the next IR message to avoid missing messages. @@ -275,6 +285,15 @@ bool IRrecv::decode(decode_results *results, irparams_t *save) { if (decodeSanyoLC7461(results)) return true; #endif +#if DECODE_CARRIER_AC + DPRINTLN("Attempting Carrier AC decode"); + // Try decodeCarrierAC() before decodeNEC() because the protocols are + // similar in timings & structure, but the Carrier one is much longer than the + // NEC protocol (3x32 bits vs 1x32 bits) so this one should be tried first to + // try to reduce false detection as a NEC packet. + if (decodeCarrierAC(results)) + return true; +#endif #if DECODE_NEC DPRINTLN("Attempting NEC decode"); if (decodeNEC(results)) @@ -290,6 +309,11 @@ bool IRrecv::decode(decode_results *results, irparams_t *save) { if (decodeMitsubishi(results)) return true; #endif +#if DECODE_MITSUBISHI2 + DPRINTLN("Attempting Mitsubishi2 decode"); + if (decodeMitsubishi2(results)) + return true; +#endif #if DECODE_RC5 DPRINTLN("Attempting RC5 decode"); if (decodeRC5(results)) @@ -305,11 +329,16 @@ bool IRrecv::decode(decode_results *results, irparams_t *save) { if (decodeRCMM(results)) return true; #endif +#if DECODE_FUJITSU_AC + // Fujitsu A/C needs to precede Panasonic and Denon as it has a short + // message which looks exactly the same as a Panasonic/Denon message. + DPRINTLN("Attempting Fujitsu A/C decode"); + if (decodeFujitsuAC(results)) + return true; +#endif #if DECODE_DENON // Denon needs to precede Panasonic as it is a special case of Panasonic. -#ifdef DEBUG DPRINTLN("Attempting Denon decode"); -#endif if (decodeDenon(results, DENON_48_BITS) || decodeDenon(results, DENON_BITS) || decodeDenon(results, DENON_LEGACY_BITS)) @@ -329,6 +358,13 @@ bool IRrecv::decode(decode_results *results, irparams_t *save) { if (decodeLG(results, LG32_BITS, true)) return true; #endif +#if DECODE_GICABLE + // Note: Needs to happen before JVC decode, because it looks similar except + // with a required NEC-like repeat code. + DPRINTLN("Attempting GICable decode"); + if (decodeGICable(results)) + return true; +#endif #if DECODE_JVC DPRINTLN("Attempting JVC decode"); if (decodeJVC(results)) @@ -359,6 +395,38 @@ bool IRrecv::decode(decode_results *results, irparams_t *save) { if (decodeCOOLIX(results)) return true; #endif +#if DECODE_NIKAI + DPRINTLN("Attempting Nikai decode"); + if (decodeNikai(results)) + return true; +#endif +#if DECODE_KELVINATOR +// Kelvinator based-devices use a similar code to Gree ones, to avoid false +// matches this needs to happen before decodeGree(). + DPRINTLN("Attempting Kelvinator decode"); + if (decodeKelvinator(results)) + return true; +#endif +#if DECODE_DAIKIN + DPRINTLN("Attempting Daikin decode"); + if (decodeDaikin(results)) + return true; +#endif +#if DECODE_TOSHIBA_AC + DPRINTLN("Attempting Toshiba AC decode"); + if (decodeToshibaAC(results)) + return true; +#endif +#if DECODE_MIDEA + DPRINTLN("Attempting Midea decode"); + if (decodeMidea(results)) + return true; +#endif +#if DECODE_MAGIQUEST + DPRINTLN("Attempting Magiquest decode"); + if (decodeMagiQuest(results)) + return true; +#endif /* NOTE: Disabled due to poor quality. #if DECODE_SANYO // The Sanyo S866500B decoder is very poor quality & depricated. @@ -375,18 +443,53 @@ bool IRrecv::decode(decode_results *results, irparams_t *save) { // This needs to be done after all other codes that use strict and some // other protocols that are NEC-like as well, as turning off strict may // cause this to match other valid protocols. - DPRINTLN("Attempting NEC (non-stict) decode"); + DPRINTLN("Attempting NEC (non-strict) decode"); if (decodeNEC(results, NEC_BITS, false)) { results->decode_type = NEC_LIKE; return true; } #endif +#if DECODE_LASERTAG + DPRINTLN("Attempting Lasertag decode"); + if (decodeLasertag(results)) + return true; +#endif +#if DECODE_GREE + // Gree based-devices use a similar code to Kelvinator ones, to avoid false + // matches this needs to happen after decodeKelvinator(). + DPRINTLN("Attempting Gree decode"); + if (decodeGree(results)) + return true; +#endif +#if DECODE_HAIER_AC + DPRINTLN("Attempting Haier AC decode"); + if (decodeHaierAC(results)) + return true; +#endif +#if DECODE_HITACHI_AC2 + // HitachiAC2 should be checked before HitachiAC + DPRINTLN("Attempting Hitachi AC2 decode"); + if (decodeHitachiAC(results, HITACHI_AC2_BITS)) + return true; +#endif +#if DECODE_HITACHI_AC + DPRINTLN("Attempting Hitachi AC decode"); + if (decodeHitachiAC(results, HITACHI_AC_BITS)) + return true; +#endif +#if DECODE_HITACHI_AC1 + DPRINTLN("Attempting Hitachi AC1 decode"); + if (decodeHitachiAC(results, HITACHI_AC1_BITS)) + return true; +#endif +#if DECODE_HASH // decodeHash returns a hash on any input. // Thus, it needs to be last in the list. // If you add any decodes, add them before this. if (decodeHash(results)) { return true; } +#endif // DECODE_HASH // Throw away and start over if (!resumed) // Check if we have already resumed. resume(); @@ -398,11 +501,13 @@ bool IRrecv::decode(decode_results *results, irparams_t *save) { // Args: // usecs: Nr. of uSeconds. // tolerance: Percent as an integer. e.g. 10 is 10% +// delta: A non-scaling amount to reduce usecs by. // Returns: // Nr. of ticks. -uint32_t IRrecv::ticksLow(uint32_t usecs, uint8_t tolerance) { +uint32_t IRrecv::ticksLow(uint32_t usecs, uint8_t tolerance, uint16_t delta) { // max() used to ensure the result can't drop below 0 before the cast. - return((uint32_t) std::max((int32_t) (usecs * (1.0 - tolerance / 100.0)), 0)); + return((uint32_t) std::max( + (int32_t) (usecs * (1.0 - tolerance / 100.0) - delta), 0)); } // Calculate the upper bound of the nr. of ticks. @@ -410,48 +515,52 @@ uint32_t IRrecv::ticksLow(uint32_t usecs, uint8_t tolerance) { // Args: // usecs: Nr. of uSeconds. // tolerance: Percent as an integer. e.g. 10 is 10% +// delta: A non-scaling amount to increase usecs by. // Returns: // Nr. of ticks. -uint32_t IRrecv::ticksHigh(uint32_t usecs, uint8_t tolerance) { - return((uint32_t) (usecs * (1.0 + tolerance / 100.0)) + 1); +uint32_t IRrecv::ticksHigh(uint32_t usecs, uint8_t tolerance, uint16_t delta) { + return((uint32_t) (usecs * (1.0 + tolerance / 100.0)) + 1 + delta); } // Check if we match a pulse(measured) with the desired within -// +/-tolerance percent. +// +/-tolerance percent and/or +/- a fixed delta range. // // Args: // measured: The recorded period of the signal pulse. // desired: The expected period (in useconds) we are matching against. // tolerance: A percentage expressed as an integer. e.g. 10 is 10%. +// delta: A non-scaling (+/-) error margin (in useconds). // // Returns: // Boolean: true if it matches, false if it doesn't. bool IRrecv::match(uint32_t measured, uint32_t desired, - uint8_t tolerance) { + uint8_t tolerance, uint16_t delta) { measured *= RAWTICK; // Convert to uSecs. DPRINT("Matching: "); - DPRINT(ticksLow(desired, tolerance)); + DPRINT(ticksLow(desired, tolerance, delta)); DPRINT(" <= "); DPRINT(measured); DPRINT(" <= "); - DPRINTLN(ticksHigh(desired, tolerance)); - return (measured >= ticksLow(desired, tolerance) && - measured <= ticksHigh(desired, tolerance)); + DPRINTLN(ticksHigh(desired, tolerance, delta)); + return (measured >= ticksLow(desired, tolerance, delta) && + measured <= ticksHigh(desired, tolerance, delta)); } // Check if we match a pulse(measured) of at least desired within -// +/-tolerance percent. +// tolerance percent and/or a fixed delta margin. // // Args: // measured: The recorded period of the signal pulse. // desired: The expected period (in useconds) we are matching against. // tolerance: A percentage expressed as an integer. e.g. 10 is 10%. +// delta: A non-scaling amount to reduce usecs by. + // // Returns: // Boolean: true if it matches, false if it doesn't. bool IRrecv::matchAtLeast(uint32_t measured, uint32_t desired, - uint8_t tolerance) { + uint8_t tolerance, uint16_t delta) { measured *= RAWTICK; // Convert to uSecs. DPRINT("Matching ATLEAST "); DPRINT(measured); @@ -460,17 +569,18 @@ bool IRrecv::matchAtLeast(uint32_t measured, uint32_t desired, DPRINT(". Matching: "); DPRINT(measured); DPRINT(" >= "); - DPRINT(ticksLow(std::min(desired, MS_TO_USEC(irparams.timeout)), tolerance)); + DPRINT(ticksLow(std::min(desired, MS_TO_USEC(irparams.timeout)), tolerance, + delta)); DPRINT(" [min("); - DPRINT(ticksLow(desired, tolerance)); + DPRINT(ticksLow(desired, tolerance, delta)); DPRINT(", "); - DPRINT(ticksLow(MS_TO_USEC(irparams.timeout), tolerance)); + DPRINT(ticksLow(MS_TO_USEC(irparams.timeout), tolerance, delta)); DPRINTLN(")]"); // We really should never get a value of 0, except as the last value // in the buffer. If that is the case, then assume infinity and return true. if (measured == 0) return true; return measured >= ticksLow(std::min(desired, MS_TO_USEC(irparams.timeout)), - tolerance); + tolerance, delta); } // Check if we match a mark signal(measured) with the desired within @@ -545,13 +655,14 @@ int16_t IRrecv::compare(uint16_t oldval, uint16_t newval) { return 1; } +#if DECODE_HASH /* Converts the raw code values into a 32-bit hash code. * Hopefully this code is unique for each button. * This isn't a "real" decoding, just an arbitrary value. */ bool IRrecv::decodeHash(decode_results *results) { - // Require at least 6 samples to prevent triggering on noise - if (results->rawlen < 6) + // Require at least some samples to prevent triggering on noise + if (results->rawlen < unknown_threshold) return false; int32_t hash = FNV_BASIS_32; // 'rawlen - 2' to avoid the look ahead from going out of bounds. @@ -570,6 +681,7 @@ bool IRrecv::decodeHash(decode_results *results) { results->decode_type = UNKNOWN; return true; } +#endif // DECODE_HASH // Match & decode the typical data section of an IR message. // The data value constructed as the Most Significant Bit first. @@ -581,46 +693,34 @@ bool IRrecv::decodeHash(decode_results *results) { // onespace: Nr. of uSeconds in an expected space signal for a '1' bit. // zeromark: Nr. of uSeconds in an expected mark signal for a '0' bit. // zerospace: Nr. of uSeconds in an expected space signal for a '0' bit. +// tolerance: Percentage error margin to allow. // Returns: // A match_result_t structure containing the success (or not), the data value, // and how many buffer entries were used. -match_result_t IRrecv::matchData(volatile uint16_t *data_ptr, uint16_t nbits, - uint16_t onemark, uint32_t onespace, - uint16_t zeromark, uint32_t zerospace) { +match_result_t IRrecv::matchData(volatile uint16_t *data_ptr, + const uint16_t nbits, const uint16_t onemark, + const uint32_t onespace, + const uint16_t zeromark, + const uint32_t zerospace, + const uint8_t tolerance) { match_result_t result; - result.success = false; + result.success = false; // Fail by default. result.data = 0; - if (onemark == zeromark) { // Is this space encoded data format? - for (result.used = 0; - result.used < nbits * 2; - result.used += 2, data_ptr++) { - if (!matchMark(*data_ptr, onemark)) - return result; // Fail - data_ptr++; - if (matchSpace(*data_ptr, onespace)) - result.data = (result.data << 1) | 1; - else if (matchSpace(*data_ptr, zerospace)) - result.data <<= 1; - else - return result; // Fail - } - result.success = true; - } else if (onespace == zerospace) { // Is this mark encoded data format? - for (result.used = 0; - result.used < nbits * 2; - result.used += 2, data_ptr++) { - if (matchMark(*data_ptr, onemark)) - result.data = (result.data << 1) | 1; - else if (matchMark(*data_ptr, zeromark)) - result.data <<= 1; - else - return result; // Fail - data_ptr++; - if (!matchSpace(*data_ptr, onespace)) - return result; // Fail - } - result.success = true; + for (result.used = 0; + result.used < nbits * 2; + result.used += 2, data_ptr += 2) { + // Is the bit a '1'? + if (matchMark(*data_ptr, onemark, tolerance) && + matchSpace(*(data_ptr + 1), onespace, tolerance)) + result.data = (result.data << 1) | 1; + // or is the bit a '0'? + else if (matchMark(*data_ptr, zeromark, tolerance) && + matchSpace(*(data_ptr + 1), zerospace, tolerance)) + result.data <<= 1; + else + return result; // It's neither, so fail. } + result.success = true; return result; } diff --git a/lib/IRremoteESP8266/src/IRrecv.h b/lib/IRremoteESP8266/src/IRrecv.h index 4e74a88..af98441 100644 --- a/lib/IRremoteESP8266/src/IRrecv.h +++ b/lib/IRremoteESP8266/src/IRrecv.h @@ -24,6 +24,7 @@ #define MARK_EXCESS 50U #define RAWBUF 100U // Default length of raw capture buffer #define REPEAT UINT64_MAX +#define UNKNOWN_THRESHOLD 6U // Default min size of reported UNKNOWN messages. // receiver states #define STATE_IDLE 2U #define STATE_MARK 3U @@ -46,6 +47,9 @@ #define FNV_PRIME_32 16777619UL #define FNV_BASIS_32 2166136261UL +// Hitachi AC is the current largest state size. +#define STATE_SIZE_MAX HITACHI_AC2_STATE_LENGTH + // Types // information for the interrupt handler typedef struct { @@ -74,14 +78,24 @@ typedef struct { class decode_results { public: decode_type_t decode_type; // NEC, SONY, RC5, UNKNOWN - uint64_t value; // Decoded value + // value, address, & command are all mutually exclusive with state. + // i.e. They MUST NOT be used at the same time as state, so we can use a union + // structure to save us a handful of valuable bytes of memory. + union { + struct { + uint64_t value; // Decoded value + uint32_t address; // Decoded device address. + uint32_t command; // Decoded command. + }; +#if DECODE_AC // Only include state if we must. It's big. + uint8_t state[STATE_SIZE_MAX]; // Complex multi-byte A/C result. +#endif + }; uint16_t bits; // Number of bits in decoded value volatile uint16_t *rawbuf; // Raw intervals in .5 us ticks uint16_t rawlen; // Number of records in rawbuf. bool overflow; bool repeat; // Is the result a repeat code? - uint32_t address; // Decoded device address. - uint32_t command; // Decoded command. }; // main class for receiving IR @@ -96,28 +110,38 @@ class IRrecv { void disableIRIn(); void resume(); uint16_t getBufSize(); - +#if DECODE_HASH + void setUnknownThreshold(uint16_t length); +#endif + static bool match(uint32_t measured, uint32_t desired, + uint8_t tolerance = TOLERANCE, uint16_t delta = 0); + static bool matchMark(uint32_t measured, uint32_t desired, + uint8_t tolerance = TOLERANCE, + int16_t excess = MARK_EXCESS); + static bool matchSpace(uint32_t measured, uint32_t desired, + uint8_t tolerance = TOLERANCE, + int16_t excess = MARK_EXCESS); #ifndef UNIT_TEST private: #endif irparams_t *irparams_save; +#if DECODE_HASH + uint16_t unknown_threshold; +#endif // These are called by decode void copyIrParams(volatile irparams_t *src, irparams_t *dst); int16_t compare(uint16_t oldval, uint16_t newval); - uint32_t ticksLow(uint32_t usecs, uint8_t tolerance = TOLERANCE); - uint32_t ticksHigh(uint32_t usecs, uint8_t tolerance = TOLERANCE); - bool match(uint32_t measured, uint32_t desired, - uint8_t tolerance = TOLERANCE); + static uint32_t ticksLow(uint32_t usecs, uint8_t tolerance = TOLERANCE, + uint16_t delta = 0); + static uint32_t ticksHigh(uint32_t usecs, uint8_t tolerance = TOLERANCE, + uint16_t delta = 0); bool matchAtLeast(uint32_t measured, uint32_t desired, - uint8_t tolerance = TOLERANCE); - bool matchMark(uint32_t measured, uint32_t desired, - uint8_t tolerance = TOLERANCE, int16_t excess = MARK_EXCESS); - bool matchSpace(uint32_t measured, uint32_t desired, - uint8_t tolerance = TOLERANCE, int16_t excess = MARK_EXCESS); - match_result_t matchData(volatile uint16_t *data_ptr, uint16_t nbits, - uint16_t onemark, uint32_t onespace, - uint16_t zeromark, uint32_t zerospace); + uint8_t tolerance = TOLERANCE, uint16_t delta = 0); + match_result_t matchData(volatile uint16_t *data_ptr, const uint16_t nbits, + const uint16_t onemark, const uint32_t onespace, + const uint16_t zeromark, const uint32_t zerospace, + const uint8_t tolerance = TOLERANCE); bool decodeHash(decode_results *results); #if (DECODE_NEC || DECODE_SHERWOOD || DECODE_AIWA_RC_T501 || SEND_SANYO) bool decodeNEC(decode_results *results, uint16_t nbits = NEC_BITS, @@ -141,9 +165,15 @@ class IRrecv { uint16_t nbits = MITSUBISHI_BITS, bool strict = true); #endif -#if (DECODE_RC5 || DECODE_R6) +#if DECODE_MITSUBISHI2 + bool decodeMitsubishi2(decode_results *results, + uint16_t nbits = MITSUBISHI_BITS, + bool strict = true); +#endif +#if (DECODE_RC5 || DECODE_R6 || DECODE_LASERTAG) int16_t getRClevel(decode_results *results, uint16_t *offset, uint16_t *used, - uint16_t bitTime); + uint16_t bitTime, uint8_t tolerance = TOLERANCE, + int16_t excess = MARK_EXCESS, uint16_t delta = 0); #endif #if DECODE_RC5 bool decodeRC5(decode_results *results, uint16_t nbits = RC5X_BITS, @@ -198,6 +228,66 @@ class IRrecv { bool decodeAiwaRCT501(decode_results *results, uint16_t nbits = AIWA_RC_T501_BITS, bool strict = true); #endif +#if DECODE_NIKAI + bool decodeNikai(decode_results *results, uint16_t nbits = NIKAI_BITS, + bool strict = true); +#endif +#if DECODE_MAGIQUEST + bool decodeMagiQuest(decode_results *results, uint16_t nbits = MAGIQUEST_BITS, + bool strict = true); +#endif +#if DECODE_KELVINATOR + bool decodeKelvinator(decode_results *results, + uint16_t nbits = KELVINATOR_BITS, + bool strict = true); +#endif +#if DECODE_DAIKIN + bool decodeDaikin(decode_results *results, uint16_t nbits = DAIKIN_RAW_BITS, + bool strict = true); +#endif +#if DECODE_TOSHIBA_AC + bool decodeToshibaAC(decode_results *results, + uint16_t nbytes = TOSHIBA_AC_BITS, + bool strict = true); +#endif +#if DECODE_MIDEA + bool decodeMidea(decode_results *results, uint16_t nbits = MIDEA_BITS, + bool strict = true); +#endif +#if DECODE_FUJITSU_AC + bool decodeFujitsuAC(decode_results *results, + uint16_t nbits = FUJITSU_AC_BITS, + bool strict = false); +#endif +#if DECODE_LASERTAG + bool decodeLasertag(decode_results *results, uint16_t nbits = LASERTAG_BITS, + bool strict = true); +#endif +#if DECODE_CARRIER_AC + bool decodeCarrierAC(decode_results *results, + uint16_t nbits = CARRIER_AC_BITS, + bool strict = true); +#endif +#if DECODE_GREE + bool decodeGree(decode_results *results, + uint16_t nbits = GREE_BITS, bool strict = true); +#endif +#if DECODE_HAIER_AC + bool decodeHaierAC(decode_results *results, + uint16_t nbits = HAIER_AC_BITS, bool strict = true); +#endif +#if (DECODE_HITACHI_AC || DECODE_HITACHI_AC2) + bool decodeHitachiAC(decode_results *results, + uint16_t nbits = HITACHI_AC_BITS, bool strict = true); +#endif +#if DECODE_HITACHI_AC1 + bool decodeHitachiAC1(decode_results *results, + uint16_t nbits = HITACHI_AC1_BITS, bool strict = true); +#endif +#if DECODE_GICABLE + bool decodeGICable(decode_results *results, uint16_t nbits = GICABLE_BITS, + bool strict = true); +#endif }; #endif // IRRECV_H_ diff --git a/lib/IRremoteESP8266/src/IRremoteESP8266.h b/lib/IRremoteESP8266/src/IRremoteESP8266.h index 0864b60..2e553f2 100644 --- a/lib/IRremoteESP8266/src/IRremoteESP8266.h +++ b/lib/IRremoteESP8266/src/IRremoteESP8266.h @@ -21,7 +21,7 @@ * Kelvinator A/C and Sherwood added by crankyoldgit * Mitsubishi (TV) sending added by crankyoldgit * Pronto code sending added by crankyoldgit - * Mitsubishi A/C added by crankyoldgit + * Mitsubishi & Toshiba A/C added by crankyoldgit * (derived from https://github.com/r45635/HVAC-IR-Control) * DISH decode by marcosamarinho * Gree Heatpump sending added by Ville Skyttä (scop) @@ -29,10 +29,12 @@ * Updated by markszabo (https://github.com/markszabo/IRremoteESP8266) for sending IR code on ESP8266 * Updated by Sebastien Warin (http://sebastien.warin.fr) for receiving IR code on ESP8266 * - * Updated by sillyfrog for Daikin, adopted from + * Updated by sillyfrog for Daikin, adopted from * (https://github.com/mharizanov/Daikin-AC-remote-control-over-the-Internet/) * Fujitsu A/C code added by jonnygraham * Trotec AC code by stufisher + * Carrier AC code by crankyoldgit + * * GPL license, all text above must be included in any redistribution ****************************************************/ @@ -46,11 +48,16 @@ #endif // Library Version -#define _IRREMOTEESP8266_VERSION_ "2.2.0" +#define _IRREMOTEESP8266_VERSION_ "2.4.2" // 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! +// The Air Conditioner protocols are the most expensive memory-wise. // +#define DECODE_HASH true // Semi-unique code for unknown messages + +#define SEND_RAW true + #define DECODE_NEC true #define SEND_NEC true @@ -93,6 +100,9 @@ #define DECODE_MITSUBISHI true #define SEND_MITSUBISHI true +#define DECODE_MITSUBISHI2 true +#define SEND_MITSUBISHI2 true + #define DECODE_DISH true #define SEND_DISH true @@ -102,16 +112,16 @@ #define DECODE_DENON true #define SEND_DENON true -#define DECODE_KELVINATOR false // Not written. +#define DECODE_KELVINATOR true #define SEND_KELVINATOR true #define DECODE_MITSUBISHI_AC false // Not written. #define SEND_MITSUBISHI_AC true -#define DECODE_FUJITSU_AC false // Not written. -#define SEND_FUJITSU_AC true +#define DECODE_FUJITSU_AC true +#define SEND_FUJITSU_AC true -#define DECODE_DAIKIN false // Not finished. +#define DECODE_DAIKIN true #define SEND_DAIKIN true #define DECODE_COOLIX true @@ -120,7 +130,7 @@ #define DECODE_GLOBALCACHE false // Not written. #define SEND_GLOBALCACHE true -#define DECODE_GREE false // Not written. +#define DECODE_GREE true #define SEND_GREE true #define DECODE_PRONTO false // Not written. @@ -132,6 +142,54 @@ #define DECODE_TROTEC false // Not implemented. #define SEND_TROTEC true +#define DECODE_NIKAI true +#define SEND_NIKAI true + +#define DECODE_TOSHIBA_AC true +#define SEND_TOSHIBA_AC true + +#define DECODE_MAGIQUEST true +#define SEND_MAGIQUEST true + +#define DECODE_MIDEA true +#define SEND_MIDEA true + +#define DECODE_LASERTAG true +#define SEND_LASERTAG true + +#define DECODE_CARRIER_AC true +#define SEND_CARRIER_AC true + +#define DECODE_HAIER_AC true +#define SEND_HAIER_AC true + +#define DECODE_HITACHI_AC true +#define SEND_HITACHI_AC true + +#define DECODE_HITACHI_AC1 true +#define SEND_HITACHI_AC1 true + +#define DECODE_HITACHI_AC2 true +#define SEND_HITACHI_AC2 true + +#define DECODE_GICABLE true +#define SEND_GICABLE true + +#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) +#define DECODE_AC true // We need some common infrastructure for decoding A/Cs. +#else +#define DECODE_AC false // We don't need that infrastructure. +#endif + +// Use millisecond 'delay()' calls where we can to avoid tripping the WDT. +// Note: If you plan to send IR messages in the callbacks of the AsyncWebserver +// library, you need to set ALLOW_DELAY_CALLS to false. +// Ref: https://github.com/markszabo/IRremoteESP8266/issues/430 +#define ALLOW_DELAY_CALLS true + /* * Always add to the end of the list and should never remove entries * or change order. Projects may save the type number for later usage @@ -164,27 +222,57 @@ enum decode_type_t { SANYO_LC7461, RC5X, GREE, - PRONTO, + PRONTO, // Technically not a protocol, but an encoding. NEC_LIKE, ARGO, - TROTEC + TROTEC, + NIKAI, + RAW, // Technically not a protocol, but an encoding. + GLOBALCACHE, // Technically not a protocol, but an encoding. + TOSHIBA_AC, + FUJITSU_AC, + MIDEA, + MAGIQUEST, + LASERTAG, + CARRIER_AC, + HAIER_AC, + MITSUBISHI2, + HITACHI_AC, + HITACHI_AC1, + HITACHI_AC2, + GICABLE }; // Message lengths & required repeat values #define AIWA_RC_T501_BITS 15U #define AIWA_RC_T501_MIN_REPEAT 1U #define COOLIX_BITS 24U -#define DAIKIN_BITS 99U +#define CARRIER_AC_BITS 32U +#define CARRIER_AC_MIN_REPEAT 0U +// Daikin has a lot of static stuff that is discarded +#define DAIKIN_RAW_BITS 583U #define DAIKIN_COMMAND_LENGTH 27U +#define DAIKIN_BITS (DAIKIN_COMMAND_LENGTH * 8) #define DENON_BITS SHARP_BITS #define DENON_48_BITS PANASONIC_BITS #define DENON_LEGACY_BITS 14U #define DISH_BITS 16U #define DISH_MIN_REPEAT 3U +#define GICABLE_BITS 16U +#define GICABLE_MIN_REPEAT 1U #define GREE_STATE_LENGTH 8U #define GREE_BITS (GREE_STATE_LENGTH * 8) +#define HAIER_AC_STATE_LENGTH 9U +#define HAIER_AC_BITS (HAIER_AC_STATE_LENGTH * 8) +#define HITACHI_AC_STATE_LENGTH 28U +#define HITACHI_AC_BITS (HITACHI_AC_STATE_LENGTH * 8) +#define HITACHI_AC1_STATE_LENGTH 13U +#define HITACHI_AC1_BITS (HITACHI_AC1_STATE_LENGTH * 8) +#define HITACHI_AC2_STATE_LENGTH 53U +#define HITACHI_AC2_BITS (HITACHI_AC2_STATE_LENGTH * 8) #define JVC_BITS 16U #define KELVINATOR_STATE_LENGTH 16U +#define KELVINATOR_BITS (KELVINATOR_STATE_LENGTH * 8) #define LG_BITS 28U #define LG32_BITS 32U #define MITSUBISHI_BITS 16U @@ -193,6 +281,10 @@ enum decode_type_t { #define MITSUBISHI_AC_STATE_LENGTH 18U #define MITSUBISHI_AC_MIN_REPEAT 1U #define FUJITSU_AC_MIN_REPEAT 0U +#define FUJITSU_AC_STATE_LENGTH 16U +#define FUJITSU_AC_STATE_LENGTH_SHORT 7U +#define FUJITSU_AC_BITS (FUJITSU_AC_STATE_LENGTH * 8) +#define FUJITSU_AC_MIN_BITS ((FUJITSU_AC_STATE_LENGTH_SHORT - 1) * 8) #define NEC_BITS 32U #define PANASONIC_BITS 48U #define PANASONIC_MANUFACTURER 0x4004ULL @@ -219,9 +311,18 @@ enum decode_type_t { #define SONY_20_BITS 20U #define SONY_MIN_BITS SONY_12_BITS #define SONY_MIN_REPEAT 2U +#define TOSHIBA_AC_STATE_LENGTH 9U +#define TOSHIBA_AC_BITS (TOSHIBA_AC_STATE_LENGTH * 8) +#define TOSHIBA_AC_MIN_REPEAT 1U #define TROTEC_COMMAND_LENGTH 9U #define WHYNTER_BITS 32U #define ARGO_COMMAND_LENGTH 12U +#define NIKAI_BITS 24U +#define MAGIQUEST_BITS 56U +#define MIDEA_BITS 48U +#define MIDEA_MIN_REPEAT 0U +#define LASERTAG_BITS 13U +#define LASERTAG_MIN_REPEAT 0U // Turn on Debugging information by uncommenting the following line. // #define DEBUG 1 diff --git a/lib/IRremoteESP8266/src/IRsend.cpp b/lib/IRremoteESP8266/src/IRsend.cpp index 300b1fa..ff1efe9 100644 --- a/lib/IRremoteESP8266/src/IRsend.cpp +++ b/lib/IRremoteESP8266/src/IRsend.cpp @@ -29,10 +29,14 @@ // Setting this to something other than the default could // easily destroy your IR LED if you are overdriving it. // Unless you *REALLY* know what you are doing, don't change this. +// use_modulation: Do we do frequency modulation during transmission? +// i.e. If not, assume a 100% duty cycle. Ignore attempts +// to change the duty cycle etc. // Returns: // An IRsend object. -IRsend::IRsend(uint16_t IRsendPin, bool inverted) : IRpin(IRsendPin), - periodOffset(PERIOD_OFFSET) { +IRsend::IRsend(uint16_t IRsendPin, bool inverted, + bool use_modulation) : IRpin(IRsendPin), + periodOffset(PERIOD_OFFSET) { if (inverted) { outputOn = LOW; outputOff = HIGH; @@ -40,6 +44,11 @@ IRsend::IRsend(uint16_t IRsendPin, bool inverted) : IRpin(IRsendPin), outputOn = HIGH; outputOff = LOW; } + modulation = use_modulation; + if (modulation) + _dutycycle = DUTY_DEFAULT; + else + _dutycycle = DUTY_MAX; } // Enable the pin for output. @@ -47,6 +56,7 @@ void IRsend::begin() { #ifndef UNIT_TEST pinMode(IRpin, OUTPUT); #endif + ledOff(); // Ensure the LED is in a known safe state when we start. } // Turn off the IR LED. @@ -56,6 +66,13 @@ void IRsend::ledOff() { #endif } +// Turn on the IR LED. +void IRsend::ledOn() { +#ifndef UNIT_TEST + digitalWrite(IRpin, outputOn); +#endif +} + // Calculate the period for a given frequency. (T = 1/f) // // Args: @@ -78,23 +95,65 @@ uint32_t IRsend::calcUSecPeriod(uint32_t hz, bool use_offset) { // Args: // freq: The freq we want to modulate at. Assumes < 1000 means kHz else Hz. // duty: Percentage duty cycle of the LED. e.g. 25 = 25% = 1/4 on, 3/4 off. +// This is ignored if modulation is disabled at object instantiation. // // Note: // Integer timing functions & math mean we can't do fractions of // microseconds timing. Thus minor changes to the freq & duty values may have // limited effect. You've been warned. void IRsend::enableIROut(uint32_t freq, uint8_t duty) { - // Can't have more than 100% duty cycle. - duty = std::min(duty, (uint8_t) 100); + // Set the duty cycle to use if we want freq. modulation. + if (modulation) { + _dutycycle = std::min(duty, (uint8_t) DUTY_MAX); + } else { + _dutycycle = DUTY_MAX; + } if (freq < 1000) // Were we given kHz? Supports the old call usage. freq *= 1000; uint32_t period = calcUSecPeriod(freq); // Nr. of uSeconds the LED will be on per pulse. - onTimePeriod = (period * duty) / 100; + onTimePeriod = (period * _dutycycle) / DUTY_MAX; // Nr. of uSeconds the LED will be off per pulse. offTimePeriod = period - onTimePeriod; } +#if ALLOW_DELAY_CALLS +// An ESP8266 RTOS watch-dog timer friendly version of delayMicroseconds(). +// Args: +// usec: Nr. of uSeconds to delay for. +void IRsend::_delayMicroseconds(uint32_t usec) { + // delayMicroseconds() is only accurate to 16383us. + // Ref: https://www.arduino.cc/en/Reference/delayMicroseconds + if (usec <= MAX_ACCURATE_USEC_DELAY) { +#ifndef UNIT_TEST + delayMicroseconds(usec); +#endif + } else { +#ifndef UNIT_TEST + // Invoke a delay(), where possible, to avoid triggering the WDT. + delay(usec / 1000UL); // Delay for as many whole milliseconds as we can. + // Delay the remaining sub-millisecond. + delayMicroseconds(static_cast(usec % 1000UL)); +#endif + } +} +#else // ALLOW_DELAY_CALLS +// A version of delayMicroseconds() that handles large values and does NOT use +// the watch-dog friendly delay() calls where appropriate. +// Args: +// usec: Nr. of uSeconds to delay for. +// +// NOTE: Use this only if you know what you are doing as it may cause the WDT +// to reset the ESP8266. +void IRsend::_delayMicroseconds(uint32_t usec) { + for (; usec > MAX_ACCURATE_USEC_DELAY; usec -= MAX_ACCURATE_USEC_DELAY) +#ifndef UNIT_TEST + delayMicroseconds(MAX_ACCURATE_USEC_DELAY); + delayMicroseconds(static_cast(usec)); +#endif // UNIT_TEST +} +#endif // ALLOW_DELAY_CALLS + // Modulate the IR LED for the given period (usec) and at the duty cycle set. // // Args: @@ -112,6 +171,15 @@ void IRsend::enableIROut(uint32_t freq, uint8_t duty) { // Ref: // https://www.analysir.com/blog/2017/01/29/updated-esp8266-nodemcu-backdoor-upwm-hack-for-ir-signals/ uint16_t IRsend::mark(uint16_t usec) { + // Handle the simple case of no required frequency modulation. + if (!modulation || _dutycycle >= 100) { + ledOn(); + _delayMicroseconds(usec); + ledOff(); + return 1; + } + + // Not simple, so do it assuming frequency modulation. uint16_t counter = 0; IRtimer usecTimer = IRtimer(); // Cache the time taken so far. This saves us calling time, and we can be @@ -119,21 +187,17 @@ uint16_t IRsend::mark(uint16_t usec) { uint32_t elapsed = usecTimer.elapsed(); while (elapsed < usec) { // Loop until we've met/exceeded our required time. -#ifndef UNIT_TEST - digitalWrite(IRpin, outputOn); // Turn the LED on. + ledOn(); // Calculate how long we should pulse on for. // e.g. Are we to close to the end of our requested mark time (usec)? - delayMicroseconds(std::min((uint32_t) onTimePeriod, usec - elapsed)); - digitalWrite(IRpin, outputOff); // Turn the LED off. -#endif + _delayMicroseconds(std::min((uint32_t) onTimePeriod, usec - elapsed)); + ledOff(); counter++; if (elapsed + onTimePeriod >= usec) return counter; // LED is now off & we've passed our allotted time. // Wait for the lesser of the rest of the duty cycle, or the time remaining. -#ifndef UNIT_TEST - delayMicroseconds(std::min(usec - elapsed - onTimePeriod, - (uint32_t) offTimePeriod)); -#endif + _delayMicroseconds(std::min(usec - elapsed - onTimePeriod, + (uint32_t) offTimePeriod)); elapsed = usecTimer.elapsed(); // Update & recache the actual elapsed time. } return counter; @@ -148,18 +212,7 @@ uint16_t IRsend::mark(uint16_t usec) { void IRsend::space(uint32_t time) { ledOff(); if (time == 0) return; -#ifndef UNIT_TEST - // delayMicroseconds is only accurate to 16383us. - // Ref: https://www.arduino.cc/en/Reference/delayMicroseconds - if (time <= 16383) { - delayMicroseconds(time); - } else { - // Invoke a delay(), where possible, to avoid triggering the WDT. - delay(time / 1000UL); // Delay for as many whole milliseconds as we can. - // Delay the remaining sub-millisecond. - delayMicroseconds(static_cast(time % 1000UL)); - } -#endif + _delayMicroseconds(time); } // Calculate & set any offsets to account for execution times. @@ -167,12 +220,15 @@ void IRsend::space(uint32_t time) { // Args: // hz: The frequency to calibrate at >= 1000Hz. Default is 38000Hz. // -// Status: ALPHA / Untested. +// Returns: +// The calculated period offset (in uSeconds) which is now in use. e.g. -5. +// +// Status: Stable / Working. // // NOTE: // This will generate an 65535us mark() IR LED signal. // This only needs to be called once, if at all. -void IRsend::calibrate(uint16_t hz) { +int8_t IRsend::calibrate(uint16_t hz) { if (hz < 1000) // Were we given kHz? Supports the old call usage. hz *= 1000; periodOffset = 0; // Turn off any existing offset while we calibrate. @@ -197,6 +253,7 @@ void IRsend::calibrate(uint16_t hz) { double_t actualPeriod = (double_t) timeTaken / (double_t) pulses; // Store the difference between the actual time per period vs. calculated. periodOffset = (int8_t) ((double_t) calcPeriod - actualPeriod); + return periodOffset; } // Generic method for sending data that is common to most protocols. @@ -244,6 +301,165 @@ void IRsend::sendData(uint16_t onemark, uint32_t onespace, } } +// Generic method for sending simple protocol messages. +// Will send leading or trailing 0's if the nbits is larger than the number +// of bits in data. +// +// Args: +// headermark: Nr. of usecs for the led to be pulsed for the header mark. +// A value of 0 means no header mark. +// headerspace: Nr. of usecs for the led to be off after the header mark. +// A value of 0 means no header space. +// onemark: Nr. of usecs for the led to be pulsed for a '1' bit. +// onespace: Nr. of usecs for the led to be fully off for a '1' bit. +// zeromark: Nr. of usecs for the led to be pulsed for a '0' bit. +// zerospace: Nr. of usecs for the led to be fully off for a '0' bit. +// footermark: Nr. of usecs for the led to be pulsed for the footer mark. +// A value of 0 means no footer mark. +// gap: Nr. of usecs for the led to be off after the footer mark. +// This is effectively the gap between messages. +// A value of 0 means no gap space. +// data: The data to be transmitted. +// nbits: Nr. of bits of data to be sent. +// frequency: The frequency we want to modulate at. +// Assumes < 1000 means kHz otherwise it is in Hz. +// Most common value is 38000 or 38, for 38kHz. +// MSBfirst: Flag for bit transmission order. Defaults to MSB->LSB order. +// repeat: Nr. of extra times the message will be sent. +// e.g. 0 = 1 message sent, 1 = 1 initial + 1 repeat = 2 messages +// dutycycle: Percentage duty cycle of the LED. +// e.g. 25 = 25% = 1/4 on, 3/4 off. +// If you are not sure, try 50 percent. +void IRsend::sendGeneric(const uint16_t headermark, const uint32_t headerspace, + const uint16_t onemark, const uint32_t onespace, + const uint16_t zeromark, const uint32_t zerospace, + const uint16_t footermark, const uint32_t gap, + const uint64_t data, const uint16_t nbits, + const uint16_t frequency, const bool MSBfirst, + const uint16_t repeat, const uint8_t dutycycle) { + sendGeneric(headermark, headerspace, onemark, onespace, zeromark, zerospace, + footermark, gap, 0U, data, nbits, frequency, MSBfirst, repeat, + dutycycle); +} + +// Generic method for sending simple protocol messages. +// Will send leading or trailing 0's if the nbits is larger than the number +// of bits in data. +// +// Args: +// headermark: Nr. of usecs for the led to be pulsed for the header mark. +// A value of 0 means no header mark. +// headerspace: Nr. of usecs for the led to be off after the header mark. +// A value of 0 means no header space. +// onemark: Nr. of usecs for the led to be pulsed for a '1' bit. +// onespace: Nr. of usecs for the led to be fully off for a '1' bit. +// zeromark: Nr. of usecs for the led to be pulsed for a '0' bit. +// zerospace: Nr. of usecs for the led to be fully off for a '0' bit. +// footermark: Nr. of usecs for the led to be pulsed for the footer mark. +// A value of 0 means no footer mark. +// gap: Min. nr. of usecs for the led to be off after the footer mark. +// This is effectively the absolute minimum gap between messages. +// mesgtime: Min. nr. of usecs a single message needs to be. +// This is effectively the min. total length of a single message. +// data: The data to be transmitted. +// nbits: Nr. of bits of data to be sent. +// frequency: The frequency we want to modulate at. +// Assumes < 1000 means kHz otherwise it is in Hz. +// Most common value is 38000 or 38, for 38kHz. +// MSBfirst: Flag for bit transmission order. Defaults to MSB->LSB order. +// repeat: Nr. of extra times the message will be sent. +// e.g. 0 = 1 message sent, 1 = 1 initial + 1 repeat = 2 messages +// dutycycle: Percentage duty cycle of the LED. +// e.g. 25 = 25% = 1/4 on, 3/4 off. +// If you are not sure, try 50 percent. +void IRsend::sendGeneric(const uint16_t headermark, const uint32_t headerspace, + const uint16_t onemark, const uint32_t onespace, + const uint16_t zeromark, const uint32_t zerospace, + const uint16_t footermark, const uint32_t gap, + const uint32_t mesgtime, + const uint64_t data, const uint16_t nbits, + const uint16_t frequency, const bool MSBfirst, + const uint16_t repeat, const uint8_t dutycycle) { + // Setup + enableIROut(frequency, dutycycle); + IRtimer usecs = IRtimer(); + + // We always send a message, even for repeat=0, hence '<= repeat'. + for (uint16_t r = 0; r <= repeat; r++) { + usecs.reset(); + + // Header + if (headermark) mark(headermark); + if (headerspace) space(headerspace); + + // Data + sendData(onemark, onespace, zeromark, zerospace, data, nbits, MSBfirst); + + // Footer + if (footermark) mark(footermark); + uint32_t elapsed = usecs.elapsed(); + // Avoid potential unsigned integer underflow. e.g. when mesgtime is 0. + if (elapsed >= mesgtime) + space(gap); + else + space(std::max(gap, mesgtime - elapsed)); + } +} + +// Generic method for sending simple protocol messages. +// +// Args: +// headermark: Nr. of usecs for the led to be pulsed for the header mark. +// A value of 0 means no header mark. +// headerspace: Nr. of usecs for the led to be off after the header mark. +// A value of 0 means no header space. +// onemark: Nr. of usecs for the led to be pulsed for a '1' bit. +// onespace: Nr. of usecs for the led to be fully off for a '1' bit. +// zeromark: Nr. of usecs for the led to be pulsed for a '0' bit. +// zerospace: Nr. of usecs for the led to be fully off for a '0' bit. +// footermark: Nr. of usecs for the led to be pulsed for the footer mark. +// A value of 0 means no footer mark. +// gap: Nr. of usecs for the led to be off after the footer mark. +// This is effectively the gap between messages. +// A value of 0 means no gap space. +// dataptr: Pointer to the data to be transmitted. +// nbytes: Nr. of bytes of data to be sent. +// frequency: The frequency we want to modulate at. +// Assumes < 1000 means kHz otherwise it is in Hz. +// Most common value is 38000 or 38, for 38kHz. +// MSBfirst: Flag for bit transmission order. Defaults to MSB->LSB order. +// repeat: Nr. of extra times the message will be sent. +// e.g. 0 = 1 message sent, 1 = 1 initial + 1 repeat = 2 messages +// dutycycle: Percentage duty cycle of the LED. +// e.g. 25 = 25% = 1/4 on, 3/4 off. +// If you are not sure, try 50 percent. +void IRsend::sendGeneric(const uint16_t headermark, const uint32_t headerspace, + const uint16_t onemark, const uint32_t onespace, + const uint16_t zeromark, const uint32_t zerospace, + const uint16_t footermark, const uint32_t gap, + const uint8_t *dataptr, const uint16_t nbytes, + const uint16_t frequency, const bool MSBfirst, + const uint16_t repeat, const uint8_t dutycycle) { + // Setup + enableIROut(frequency, dutycycle); + // We always send a message, even for repeat=0, hence '<= repeat'. + for (uint16_t r = 0; r <= repeat; r++) { + // Header + if (headermark) mark(headermark); + if (headerspace) space(headerspace); + + // Data + for (uint16_t i = 0; i < nbytes; i++) + sendData(onemark, onespace, zeromark, zerospace, + *(dataptr + i), 8, MSBfirst); + + // Footer + if (footermark) mark(footermark); + space(gap); + } +} + +#if SEND_RAW // Send a raw IRremote message. // // Args: @@ -270,6 +486,7 @@ void IRsend::sendRaw(uint16_t buf[], uint16_t len, uint16_t hz) { } ledOff(); // We potentially have ended with a mark(), so turn of the LED. } +#endif // SEND_RAW #ifndef UNIT_TEST void IRsend::send(uint16_t type, uint64_t data, uint16_t nbits) { @@ -316,11 +533,20 @@ void IRsend::send(uint16_t type, uint64_t data, uint16_t nbits) { #if SEND_MITSUBISHI case MITSUBISHI: sendMitsubishi(data, nbits); break; #endif +#if SEND_MITSUBISHI2 + case MITSUBISHI2: sendMitsubishi2(data, nbits); break; +#endif #if SEND_SHARP case SHARP: sendSharpRaw(data, nbits); break; #endif #if SEND_AIWA_RC_T501 case AIWA_RC_T501: sendAiwaRCT501(data, nbits); break; +#endif +#if SEND_MIDEA + case MIDEA: sendMidea(data, nbits); break; +#endif +#if SEND_GICABLE + case GICABLE: sendGICable(data, nbits); break; #endif } } diff --git a/lib/IRremoteESP8266/src/IRsend.h b/lib/IRremoteESP8266/src/IRsend.h index 19e8fcb..bebf74a 100644 --- a/lib/IRremoteESP8266/src/IRsend.h +++ b/lib/IRremoteESP8266/src/IRsend.h @@ -21,24 +21,52 @@ // Constants // Offset (in microseconds) to use in Period time calculations to account for // code excution time in producing the software PWM signal. -// Value determined in https://github.com/markszabo/IRremoteESP8266/issues/62 -#define PERIOD_OFFSET -3 +// Value was calculated on Wemos D1 mini using v2.4.1 with v2.4.0 ESP core +#define PERIOD_OFFSET -5 #define DUTY_DEFAULT 50 +#define DUTY_MAX 100 // Percentage +// delayMicroseconds() is only accurate to 16383us. +// Ref: https://www.arduino.cc/en/Reference/delayMicroseconds +#define MAX_ACCURATE_USEC_DELAY 16383U // Classes class IRsend { public: - explicit IRsend(uint16_t IRsendPin, bool inverted = false); + explicit IRsend(uint16_t IRsendPin, bool inverted = false, + bool use_modulation = true); void begin(); void enableIROut(uint32_t freq, uint8_t duty = DUTY_DEFAULT); + VIRTUAL void _delayMicroseconds(uint32_t usec); VIRTUAL uint16_t mark(uint16_t usec); VIRTUAL void space(uint32_t usec); - void calibrate(uint16_t hz = 38000U); + int8_t calibrate(uint16_t hz = 38000U); void sendRaw(uint16_t buf[], uint16_t len, uint16_t hz); void sendData(uint16_t onemark, uint32_t onespace, uint16_t zeromark, uint32_t zerospace, uint64_t data, uint16_t nbits, bool MSBfirst = true); - void send(uint16_t type, uint64_t data, uint16_t nbits); + void sendGeneric(const uint16_t headermark, const uint32_t headerspace, + const uint16_t onemark, const uint32_t onespace, + const uint16_t zeromark, const uint32_t zerospace, + const uint16_t footermark, const uint32_t gap, + const uint64_t data, const uint16_t nbits, + const uint16_t frequency, const bool MSBfirst, + const uint16_t repeat, const uint8_t dutycycle); + void sendGeneric(const uint16_t headermark, const uint32_t headerspace, + const uint16_t onemark, const uint32_t onespace, + const uint16_t zeromark, const uint32_t zerospace, + const uint16_t footermark, const uint32_t gap, + const uint32_t mesgtime, + const uint64_t data, const uint16_t nbits, + const uint16_t frequency, const bool MSBfirst, + const uint16_t repeat, const uint8_t dutycycle); + void sendGeneric(const uint16_t headermark, const uint32_t headerspace, + const uint16_t onemark, const uint32_t onespace, + const uint16_t zeromark, const uint32_t zerospace, + const uint16_t footermark, const uint32_t gap, + const uint8_t *dataptr, const uint16_t nbytes, + const uint16_t frequency, const bool MSBfirst, + const uint16_t repeat, const uint8_t dutycycle); +void send(uint16_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 = NEC_BITS, uint16_t repeat = 0); uint32_t encodeNEC(uint16_t address, uint16_t command); @@ -135,6 +163,10 @@ class IRsend { void sendMitsubishi(uint64_t data, uint16_t nbits = MITSUBISHI_BITS, uint16_t repeat = MITSUBISHI_MIN_REPEAT); #endif +#if SEND_MITSUBISHI2 + void sendMitsubishi2(uint64_t data, uint16_t nbits = MITSUBISHI_BITS, + uint16_t repeat = MITSUBISHI_MIN_REPEAT); +#endif #if SEND_MITSUBISHI_AC void sendMitsubishiAC(unsigned char data[], uint16_t nbytes = MITSUBISHI_AC_STATE_LENGTH, @@ -157,6 +189,7 @@ class IRsend { void sendDaikin(unsigned char data[], uint16_t nbytes = DAIKIN_COMMAND_LENGTH, uint16_t repeat = 0); + void sendDaikinGapHeader(); #endif #if SEND_AIWA_RC_T501 void sendAiwaRCT501(uint64_t data, uint16_t nbits = AIWA_RC_T501_BITS, @@ -175,12 +208,61 @@ class IRsend { uint16_t nbytes = ARGO_COMMAND_LENGTH, uint16_t repeat = 0); #endif - #if SEND_TROTEC void sendTrotec(unsigned char data[], uint16_t nbytes = TROTEC_COMMAND_LENGTH, uint16_t repeat = 0); #endif +#if SEND_NIKAI + void sendNikai(uint64_t data, uint16_t nbits = NIKAI_BITS, + uint16_t repeat = 0); +#endif +#if SEND_TOSHIBA_AC + void sendToshibaAC(unsigned char data[], + uint16_t nbytes = TOSHIBA_AC_STATE_LENGTH, + uint16_t repeat = TOSHIBA_AC_MIN_REPEAT); +#endif +#if SEND_MIDEA + void sendMidea(uint64_t data, uint16_t nbits = MIDEA_BITS, + uint16_t repeat = MIDEA_MIN_REPEAT); +#endif +#if SEND_MAGIQUEST + void sendMagiQuest(uint64_t data, uint16_t nbits = MAGIQUEST_BITS, + uint16_t repeat = 0); + uint64_t encodeMagiQuest(uint32_t wand_id, uint16_t magnitude); +#endif +#if SEND_LASERTAG + void sendLasertag(uint64_t data, uint16_t nbits = LASERTAG_BITS, + uint16_t repeat = LASERTAG_MIN_REPEAT); +#endif +#if SEND_CARRIER_AC + void sendCarrierAC(uint64_t data, uint16_t nbits = CARRIER_AC_BITS, + uint16_t repeat = CARRIER_AC_MIN_REPEAT); +#endif +#if SEND_HAIER_AC + void sendHaierAC(unsigned char data[], + uint16_t nbytes = HAIER_AC_STATE_LENGTH, + uint16_t repeat = 0); +#endif +#if SEND_HITACHI_AC + void sendHitachiAC(unsigned char data[], + uint16_t nbytes = HITACHI_AC_STATE_LENGTH, + uint16_t repeat = 0); +#endif +#if SEND_HITACHI_AC1 + void sendHitachiAC1(unsigned char data[], + uint16_t nbytes = HITACHI_AC1_STATE_LENGTH, + uint16_t repeat = 0); +#endif +#if SEND_HITACHI_AC2 + void sendHitachiAC2(unsigned char data[], + uint16_t nbytes = HITACHI_AC2_STATE_LENGTH, + uint16_t repeat = 0); +#endif +#if SEND_GICABLE + void sendGICable(uint64_t data, uint16_t nbits = GICABLE_BITS, + uint16_t repeat = GICABLE_MIN_REPEAT); +#endif protected: #ifdef UNIT_TEST @@ -193,13 +275,16 @@ class IRsend { #endif // UNIT_TEST uint8_t outputOn; uint8_t outputOff; + VIRTUAL void ledOff(); + VIRTUAL void ledOn(); private: uint16_t onTimePeriod; uint16_t offTimePeriod; uint16_t IRpin; int8_t periodOffset; - void ledOff(); + uint8_t _dutycycle; + bool modulation; uint32_t calcUSecPeriod(uint32_t hz, bool use_offset = true); }; diff --git a/lib/IRremoteESP8266/src/IRtimer.cpp b/lib/IRremoteESP8266/src/IRtimer.cpp index cc5cdfa..d6c65b3 100644 --- a/lib/IRremoteESP8266/src/IRtimer.cpp +++ b/lib/IRremoteESP8266/src/IRtimer.cpp @@ -5,6 +5,11 @@ #include #endif +#ifdef UNIT_TEST +// Used to help simulate elapsed time in unit tests. +extern uint32_t _IRtimer_unittest_now; +#endif // UNIT_TEST + // This class performs a simple time in useconds since instantiated. // Handles when the system timer wraps around (once). @@ -16,7 +21,7 @@ void IRtimer::reset() { #ifndef UNIT_TEST start = micros(); #else - start = 0; + start = _IRtimer_unittest_now; #endif } @@ -24,10 +29,17 @@ uint32_t IRtimer::elapsed() { #ifndef UNIT_TEST uint32_t now = micros(); #else - uint32_t now = 0; + uint32_t now = _IRtimer_unittest_now; #endif if (start <= now) // Check if the system timer has wrapped. return now - start; // No wrap. else return UINT32_MAX - start + now; // Has wrapped. } + +// Only used in unit testing. +#ifdef UNIT_TEST +void IRtimer::add(uint32_t usecs) { + _IRtimer_unittest_now += usecs; +} +#endif // UNIT_TEST diff --git a/lib/IRremoteESP8266/src/IRtimer.h b/lib/IRremoteESP8266/src/IRtimer.h index 6cf7f7d..baca1cf 100644 --- a/lib/IRremoteESP8266/src/IRtimer.h +++ b/lib/IRremoteESP8266/src/IRtimer.h @@ -12,6 +12,9 @@ class IRtimer { IRtimer(); void reset(); uint32_t elapsed(); +#ifdef UNIT_TEST + static void add(uint32_t usecs); +#endif // UNIT_TEST private: uint32_t start; diff --git a/lib/IRremoteESP8266/src/IRutils.cpp b/lib/IRremoteESP8266/src/IRutils.cpp index ba3bb75..89c2281 100644 --- a/lib/IRremoteESP8266/src/IRutils.cpp +++ b/lib/IRremoteESP8266/src/IRutils.cpp @@ -4,10 +4,15 @@ #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: @@ -30,30 +35,306 @@ uint64_t reverseBits(uint64_t input, uint16_t nbits) { 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. -// Note: Based on Arduino's Print::printNumber() void serialPrintUint64(uint64_t input, uint8_t base) { - char buf[8 * sizeof(input) + 1]; // Assumes 8-bit chars plus zero byte. - char *str = &buf[sizeof(buf) - 1]; + 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 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 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 LASERTAG: result = "LASERTAG"; 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 NEC: result = "NEC"; break; + case NEC_LIKE: result = "NEC (non-strict)"; break; + case NIKAI: result = "NIKAI"; break; + case PANASONIC: result = "PANASONIC"; 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 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 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 FUJITSU_AC: + case GREE: + case HAIER_AC: + case HITACHI_AC: + case HITACHI_AC1: + case HITACHI_AC2: + case KELVINATOR: + case MITSUBISHI_AC: + case TOSHIBA_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] * RAWTICK; + // 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 - *str = '\0'; + // Dump data + for (uint16_t i = 1; i < results->rawlen; i++) { + uint32_t usecs; + for (usecs = results->rawbuf[i] * RAWTICK; + 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. + } - // prevent crash if called with base == 1 - if (base < 2) base = 10; + // End declaration + output +="};"; - do { - char c = input % base; - input /= base; + // 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"; - *--str = c < 10 ? c + '0' : c + 'A' - 10; - } while (input); + // 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; +} -#ifndef UNIT_TEST - Serial.print(str); +// 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] * RAWTICK); + // 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; +} + +// 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 : "; + if (hasACState(results->decode_type)) { +#if DECODE_AC + for (uint16_t i = 0; results->bits > i * 8; i++) { + if (results->state[i] < 0x10) output += "0"; // Zero pad + output += uint64ToString(results->state[i], 16); + } +#endif // DECODE_AC + } else { + output += uint64ToString(results->value, 16); + } + 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/src/IRutils.h b/lib/IRremoteESP8266/src/IRutils.h index e333fe4..3da62df 100644 --- a/lib/IRremoteESP8266/src/IRutils.h +++ b/lib/IRremoteESP8266/src/IRutils.h @@ -3,10 +3,37 @@ // Copyright 2017 David Conran +#ifndef UNIT_TEST +#include +#endif #define __STDC_LIMIT_MACROS #include +#ifndef ARDUINO +#include +#endif +#include "IRremoteESP8266.h" +#include "IRrecv.h" uint64_t reverseBits(uint64_t input, uint16_t nbits); -void serialPrintUint64(uint64_t input, uint8_t base); +#ifdef ARDUINO // Arduino's & C++'s string implementations can't co-exist. +String uint64ToString(uint64_t input, uint8_t base = 10); +String typeToString(const decode_type_t protocol, + const bool isRepeat = false); +void serialPrintUint64(uint64_t input, uint8_t base = 10); +String resultToSourceCode(const decode_results *results); +String resultToTimingInfo(const decode_results *results); +String resultToHumanReadableBasic(const decode_results *results); +#else +std::string uint64ToString(uint64_t input, uint8_t base = 10); +std::string typeToString(const decode_type_t protocol, + const bool isRepeat = false); +std::string resultToSourceCode(const decode_results *results); +std::string resultToTimingInfo(const decode_results *results); +std::string resultToHumanReadableBasic(const decode_results *results); +#endif +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); +uint64_t invertBits(const uint64_t data, const uint16_t nbits); #endif // IRUTILS_H_ diff --git a/lib/IRremoteESP8266/src/ir_Aiwa.cpp b/lib/IRremoteESP8266/src/ir_Aiwa.cpp index 99a97e1..6863caa 100644 --- a/lib/IRremoteESP8266/src/ir_Aiwa.cpp +++ b/lib/IRremoteESP8266/src/ir_Aiwa.cpp @@ -2,7 +2,6 @@ #include "IRrecv.h" #include "IRsend.h" -#include "IRtimer.h" // AAA IIIII W W AAA // A A I W W A A @@ -22,7 +21,7 @@ #define AIWA_RC_T501_POST_DATA 1ULL #if SEND_AIWA_RC_T501 -// Send a Aiwa RC T501 formatted message. +// Send an Aiwa RC T501 formatted message. // // Args: // data: The message to be sent. diff --git a/lib/IRremoteESP8266/src/ir_Argo.cpp b/lib/IRremoteESP8266/src/ir_Argo.cpp index 0129c72..eaa1c51 100644 --- a/lib/IRremoteESP8266/src/ir_Argo.cpp +++ b/lib/IRremoteESP8266/src/ir_Argo.cpp @@ -11,39 +11,31 @@ Copyright 2017 Schmolders // Constants // using SPACE modulation. MARK is always const 400u -#define ARGO_PREAMBLE_1 6400U // Mark -#define ARGO_PREAMBLE_2 3300U // Space -#define ARGO_MARK 400U +#define ARGO_HDR_MARK 6400U // Mark +#define ARGO_HDR_SPACE 3300U // Space +#define ARGO_BIT_MARK 400U #define ARGO_ONE_SPACE 2200U #define ARGO_ZERO_SPACE 900U #if SEND_ARGO -// Send a Argo A/C message. +// Send an Argo A/C message. // // Args: // data: An array of ARGO_COMMAND_LENGTH bytes containing the IR command. // // Status: ALPHA / Untested. -// -// Overloading the IRSend Function void IRsend::sendArgo(unsigned char data[], uint16_t nbytes, uint16_t repeat) { // Check if we have enough bytes to send a proper message. if (nbytes < ARGO_COMMAND_LENGTH) return; - // Set IR carrier frequency - enableIROut(38); - for (uint16_t r = 0; r <= repeat; r++) { - // Header - // TODO(kaschmo): validate - mark(ARGO_PREAMBLE_1); - space(ARGO_PREAMBLE_2); - // send data, defined in IRSend.cpp - for (uint16_t i = 0; i < nbytes; i++) - sendData(ARGO_MARK, ARGO_ONE_SPACE, ARGO_MARK, - ARGO_ZERO_SPACE, data[i], 8, false); - // send LSB first reverses the bit order in array for sending. - } + // TODO(kaschmo): validate + sendGeneric(ARGO_HDR_MARK, ARGO_HDR_SPACE, + ARGO_BIT_MARK, ARGO_ONE_SPACE, + ARGO_BIT_MARK, ARGO_ZERO_SPACE, + 0, 0, // No Footer. + data, nbytes, 38, false, repeat, 50); } +#endif // SEND_ARGO IRArgoAC::IRArgoAC(uint16_t pin) : _irsend(pin) { stateReset(); @@ -53,11 +45,12 @@ void IRArgoAC::begin() { _irsend.begin(); } +#if SEND_ARGO void IRArgoAC::send() { - // Serial.println("Sending IR code"); // Only for Debug checksum(); // Create valid checksum before sending _irsend.sendArgo(argo); } +#endif // SEND_ARGO void IRArgoAC::checksum() { uint8_t sum = 2; // Corresponds to byte 11 being constant 0b01 @@ -261,4 +254,3 @@ 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 } -#endif // SEND_ARGO diff --git a/lib/IRremoteESP8266/src/ir_Argo.h b/lib/IRremoteESP8266/src/ir_Argo.h index a251992..d59fb77 100644 --- a/lib/IRremoteESP8266/src/ir_Argo.h +++ b/lib/IRremoteESP8266/src/ir_Argo.h @@ -57,12 +57,13 @@ #define ARGO_FLAP_FULL 7U // 0b111 -#if SEND_ARGO class IRArgoAC { public: explicit IRArgoAC(uint16_t pin); +#if SEND_ARGO void send(); +#endif // SEND_ARGO void begin(); void on(); void off(); @@ -119,6 +120,5 @@ class IRArgoAC { uint8_t max_mode; // on/off uint8_t ifeel_mode; // on/off }; -#endif // SEND_ARGO #endif // IR_ARGO_H_ diff --git a/lib/IRremoteESP8266/src/ir_Carrier.cpp b/lib/IRremoteESP8266/src/ir_Carrier.cpp new file mode 100644 index 0000000..968c2f5 --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_Carrier.cpp @@ -0,0 +1,116 @@ +// Copyright 2018 David Conran + +#include "IRrecv.h" +#include "IRsend.h" +#include "IRutils.h" + +// CCCCC AAA RRRRRR RRRRRR IIIII EEEEEEE RRRRRR +// CC C AAAAA RR RR RR RR III EE RR RR +// CC AA AA RRRRRR RRRRRR III EEEEE RRRRRR +// CC C AAAAAAA RR RR RR RR III EE RR RR +// CCCCC AA AA RR RR RR RR IIIII EEEEEEE RR RR + +// Suits Carrier/Surrey HVAC models: +// 42QG5A55970 (remote) +// 619EGX0090E0 / 619EGX0120E0 / 619EGX0180E0 / 619EGX0220E0 (indoor units) +// 53NGK009/012 (inverter) + +// Constants +// Ref: +// https://github.com/markszabo/IRremoteESP8266/issues/385 +#define CARRIER_AC_HDR_MARK 8532U +#define CARRIER_AC_HDR_SPACE 4228U +#define CARRIER_AC_BIT_MARK 628U +#define CARRIER_AC_ONE_SPACE 1320U +#define CARRIER_AC_ZERO_SPACE 532U +#define CARRIER_AC_GAP 20000U + +#if SEND_CARRIER_AC +// Send a Carrier HVAC formatted message. +// +// Args: +// data: The message to be sent. +// nbits: The bit size of the message being sent. typically CARRIER_AC_BITS. +// repeat: The number of times the message is to be repeated. +// +// Status: BETA / Appears to work on real devices. +// +void IRsend::sendCarrierAC(uint64_t data, uint16_t nbits, uint16_t repeat) { + for (uint16_t r = 0; r <= repeat; r++) { + uint64_t temp_data = data; + // Carrier sends the data block three times. normal + inverted + normal. + for (uint16_t i = 0; i < 3; i++) { + sendGeneric(CARRIER_AC_HDR_MARK, CARRIER_AC_HDR_SPACE, + CARRIER_AC_BIT_MARK, CARRIER_AC_ONE_SPACE, + CARRIER_AC_BIT_MARK, CARRIER_AC_ZERO_SPACE, + CARRIER_AC_BIT_MARK, CARRIER_AC_GAP, + temp_data, nbits, 38, true, 0, 50); + temp_data = invertBits(temp_data, nbits); + } + } +} +#endif + +#if DECODE_CARRIER_AC +// Decode the supplied Carrier HVAC message. +// Carrier HVAC messages contain only 32 bits, but it is sent three(3) times. +// i.e. normal + inverted + normal +// 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 CARRIER_AC_BITS. +// 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 / Untested. +// +bool IRrecv::decodeCarrierAC(decode_results *results, uint16_t nbits, + bool strict) { + if (results->rawlen < ((2 * nbits + HEADER + FOOTER) * 3) - 1) + return false; // Can't possibly be a valid Carrier message. + if (strict && nbits != CARRIER_AC_BITS) + return false; // We expect Carrier to be 32 bits of message. + + uint64_t data = 0; + uint64_t prev_data = 0; + uint16_t offset = OFFSET_START; + + for (uint8_t i = 0; i < 3; i++) { + prev_data = data; + // Header + if (!matchMark(results->rawbuf[offset++], CARRIER_AC_HDR_MARK)) + return false; + if (!matchSpace(results->rawbuf[offset++], CARRIER_AC_HDR_SPACE)) + return false; + // Data + match_result_t data_result = matchData(&(results->rawbuf[offset]), nbits, + CARRIER_AC_BIT_MARK, + CARRIER_AC_ONE_SPACE, + CARRIER_AC_BIT_MARK, + CARRIER_AC_ZERO_SPACE); + if (data_result.success == false) return false; + data = data_result.data; + offset += data_result.used; + // Footer + if (!matchMark(results->rawbuf[offset++], CARRIER_AC_BIT_MARK)) + return false; + if (offset < results->rawlen && + !matchAtLeast(results->rawbuf[offset++], CARRIER_AC_GAP)) + return false; + // Compliance. + if (strict) { + // Check if the data is an inverted copy of the previous data. + if (i > 0 && prev_data != invertBits(data, nbits)) return false; + } + } + + // Success + results->bits = nbits; + results->value = data; + results->decode_type = CARRIER_AC; + results->address = data >> 16; + results->command = data & 0xFFFF; + return true; +} +#endif diff --git a/lib/IRremoteESP8266/src/ir_Coolix.cpp b/lib/IRremoteESP8266/src/ir_Coolix.cpp index f664d6a..82d59de 100644 --- a/lib/IRremoteESP8266/src/ir_Coolix.cpp +++ b/lib/IRremoteESP8266/src/ir_Coolix.cpp @@ -3,7 +3,6 @@ #include "IRrecv.h" #include "IRsend.h" -#include "IRtimer.h" #include "IRutils.h" // CCCCC OOOOO OOOOO LL IIIII XX XX @@ -99,7 +98,7 @@ bool IRrecv::decodeCOOLIX(decode_results *results, uint16_t nbits, if (results->rawlen < 2 * 2 * nbits + HEADER + FOOTER - 1) return false; // Can't possibly be a valid COOLIX message. if (strict && nbits != COOLIX_BITS) - return false; // Not strictly an COOLIX message. + return false; // Not strictly a COOLIX message. if (nbits % 8 != 0) // nbits has to be a multiple of nr. of bits in a byte. return false; diff --git a/lib/IRremoteESP8266/src/ir_Daikin.cpp b/lib/IRremoteESP8266/src/ir_Daikin.cpp index f57b676..caf0359 100644 --- a/lib/IRremoteESP8266/src/ir_Daikin.cpp +++ b/lib/IRremoteESP8266/src/ir_Daikin.cpp @@ -4,12 +4,18 @@ 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 "IRremoteESP8266.h" #include "IRutils.h" +#include "IRrecv.h" +#include "IRsend.h" // DDDDD AAA IIIII KK KK IIIII NN NN // DD DD AAAAA III KK KK III NNN NN @@ -20,15 +26,20 @@ Copyright 2016 sillyfrog // Constants // Ref: // https://github.com/mharizanov/Daikin-AC-remote-control-over-the-Internet/tree/master/IRremote -#define DAIKIN_HDR_MARK 3650U // DAIKIN_ZERO_MARK * 8 -#define DAIKIN_HDR_SPACE 1623U // DAIKIN_ZERO_MARK * 4 -#define DAIKIN_ONE_SPACE 1280U -#define DAIKIN_ONE_MARK 428U -#define DAIKIN_ZERO_MARK 428U -#define DAIKIN_ZERO_SPACE 428U -#define DAIKIN_GAP 29000U +// 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: @@ -43,32 +54,36 @@ void IRsend::sendDaikin(unsigned char data[], uint16_t nbytes, uint16_t repeat) { if (nbytes < DAIKIN_COMMAND_LENGTH) return; // Not enough bytes to send a proper message. - // Set IR carrier frequency - enableIROut(38); - for (uint16_t r = 0; r <= repeat; r++) { - // Header #1 - mark(DAIKIN_HDR_MARK); - space(DAIKIN_HDR_SPACE); - // Data #1 - for (uint16_t i = 0; i < 8 && i < nbytes; i++) - sendData(DAIKIN_ONE_MARK, DAIKIN_ONE_SPACE, DAIKIN_ZERO_MARK, - DAIKIN_ZERO_SPACE, data[i], 8, false); - // Footer #1 - mark(DAIKIN_ONE_MARK); - space(DAIKIN_ZERO_SPACE + DAIKIN_GAP); - // Header #2 - mark(DAIKIN_HDR_MARK); - space(DAIKIN_HDR_SPACE); + for (uint16_t r = 0; r <= repeat; r++) { + // Send the header, 0b00000 + sendGeneric(0, 0, // No header for the header + DAIKIN_BIT_MARK, DAIKIN_ONE_SPACE, + DAIKIN_BIT_MARK, DAIKIN_ZERO_SPACE, + DAIKIN_BIT_MARK, DAIKIN_ZERO_SPACE + DAIKIN_GAP, + (uint64_t) 0b00000, 5, 38, false, 0, 50); + // Leading header + // Do this as a constant to save RAM and keep in flash memory + sendGeneric(DAIKIN_HDR_MARK, DAIKIN_HDR_SPACE, + DAIKIN_BIT_MARK, DAIKIN_ONE_SPACE, + DAIKIN_BIT_MARK, DAIKIN_ZERO_SPACE, + DAIKIN_BIT_MARK, DAIKIN_ZERO_SPACE + DAIKIN_GAP, + DAIKIN_FIRST_HEADER64, 64, 38, false, 0, 50); + // Data #1 + sendGeneric(DAIKIN_HDR_MARK, DAIKIN_HDR_SPACE, + DAIKIN_BIT_MARK, DAIKIN_ONE_SPACE, + DAIKIN_BIT_MARK, DAIKIN_ZERO_SPACE, + DAIKIN_BIT_MARK, DAIKIN_ZERO_SPACE + DAIKIN_GAP, + data, 8, 38, false, 0, 50); // Data #2 - for (uint16_t i = 8; i < nbytes; i++) - sendData(DAIKIN_ONE_MARK, DAIKIN_ONE_SPACE, DAIKIN_ZERO_MARK, - DAIKIN_ZERO_SPACE, data[i], 8, false); - // Footer #2 - mark(DAIKIN_ONE_MARK); - space(DAIKIN_ZERO_SPACE + DAIKIN_GAP); + sendGeneric(DAIKIN_HDR_MARK, DAIKIN_HDR_SPACE, + DAIKIN_BIT_MARK, DAIKIN_ONE_SPACE, + DAIKIN_BIT_MARK, DAIKIN_ZERO_SPACE, + DAIKIN_BIT_MARK, DAIKIN_ZERO_SPACE + DAIKIN_GAP, + data + 8, nbytes - 8, 38, false, 0, 50); } } +#endif // SEND_DAIKIN IRDaikinESP::IRDaikinESP(uint16_t pin) : _irsend(pin) { stateReset(); @@ -78,42 +93,69 @@ void IRDaikinESP::begin() { _irsend.begin(); } +#if SEND_DAIKIN void IRDaikinESP::send() { + checksum(); _irsend.sendDaikin(daikin); } +#endif // SEND_DAIKIN -void IRDaikinESP::checksum() { +// 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; - uint8_t i; - - for (i = 0; i <= 6; i++) - sum += daikin[i]; - - daikin[7] = sum & 0xFF; - sum = 0; - for (i = 8; i <= 25; i++) - sum += daikin[i]; - daikin[26] = sum & 0xFF; + // 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 = 4; i < DAIKIN_COMMAND_LENGTH; i++) + for (uint8_t i = 0; i < DAIKIN_COMMAND_LENGTH; i++) daikin[i] = 0x0; daikin[0] = 0x11; daikin[1] = 0xDA; daikin[2] = 0x27; - daikin[3] = 0xF0; - daikin[7] = 0x20; + 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] = 0x41; + daikin[13] = 0x49; daikin[14] = 0x1E; daikin[16] = 0xB0; + daikin[19] = 0x06; + daikin[20] = 0x60; daikin[23] = 0xC0; - daikin[26] = 0xE3; + // daikin[26] is a checksum byte, it will be set by checksum(). checksum(); } @@ -122,16 +164,19 @@ uint8_t* IRDaikinESP::getRaw() { return daikin; } +void IRDaikinESP::setRaw(uint8_t new_code[]) { + for (uint8_t i = 0; i < DAIKIN_COMMAND_LENGTH; i++) + daikin[i] = new_code[i]; +} + void IRDaikinESP::on() { // state = ON; - daikin[13] |= 0x01; - checksum(); + setBit(DAIKIN_BYTE_POWER, DAIKIN_BIT_POWER); } void IRDaikinESP::off() { // state = OFF; - daikin[13] &= 0xFE; - checksum(); + clearBit(DAIKIN_BYTE_POWER, DAIKIN_BIT_POWER); } void IRDaikinESP::setPower(bool state) { @@ -141,40 +186,8 @@ void IRDaikinESP::setPower(bool state) { off(); } -uint8_t IRDaikinESP::getPower() { - return daikin[13] & 0x01; -} - -// DAIKIN_SILENT or DAIKIN_POWERFUL -void IRDaikinESP::setAux(uint8_t aux) { - daikin[21] = aux; - checksum(); -} - -uint8_t IRDaikinESP::getAux() { - return daikin[21]; -} - -void IRDaikinESP::setQuiet(bool state) { - if (state) - setAux(DAIKIN_SILENT); - else - setAux(0x0); -} - -bool IRDaikinESP::getQuiet() { - return (getAux() == DAIKIN_SILENT); -} - -void IRDaikinESP::setPowerful(bool state) { - if (state) - setAux(DAIKIN_POWERFUL); - else - setAux(0x0); -} - -bool IRDaikinESP::getPowerful() { - return (getAux() == DAIKIN_POWERFUL); +bool IRDaikinESP::getPower() { + return (getBit(DAIKIN_BYTE_POWER, DAIKIN_BIT_POWER) > 0); } // Set the temp in deg C @@ -184,32 +197,30 @@ void IRDaikinESP::setTemp(uint8_t temp) { else if (temp > DAIKIN_MAX_TEMP) temp = DAIKIN_MAX_TEMP; daikin[14] = temp * 2; - checksum(); } uint8_t IRDaikinESP::getTemp() { return daikin[14] / 2; } -// Set the speed of the fan, 0-5, 0 is auto, 1-5 is the speed +// Set the speed of the fan, 1-5 or DAIKIN_FAN_AUTO or DAIKIN_FAN_QUIET void IRDaikinESP::setFan(uint8_t fan) { // Set the fan speed bits, leave low 4 bits alone uint8_t fanset; - daikin[16] &= 0x0F; - fan = std::min(fan, DAIKIN_FAN_MAX); - if (fan == DAIKIN_FAN_AUTO) - fanset = 0xA0; + if (fan == DAIKIN_FAN_QUIET || fan == DAIKIN_FAN_AUTO) + fanset = fan; + else if (fan < DAIKIN_FAN_MIN || fan > DAIKIN_FAN_MAX) + fanset = DAIKIN_FAN_AUTO; else - fanset = 0x20 + (0x10 * fan); - daikin[16] |= fanset; - checksum(); + fanset = 2 + fan; + daikin[16] &= 0x0F; + daikin[16] |= (fanset << 4); } uint8_t IRDaikinESP::getFan() { uint8_t fan = daikin[16] >> 4; - fan -= 2; - if (fan > DAIKIN_FAN_MAX) - fan = DAIKIN_FAN_AUTO; + if (fan != DAIKIN_FAN_QUIET && fan != DAIKIN_FAN_AUTO) + fan -= 2; return fan; } @@ -234,8 +245,9 @@ void IRDaikinESP::setMode(uint8_t mode) { default: mode = DAIKIN_AUTO; } - daikin[13] = (mode << 4) | getPower(); - checksum(); + mode <<= 4; + daikin[13] &= 0b10001111; + daikin[13] |= mode; } void IRDaikinESP::setSwingVertical(bool state) { @@ -243,7 +255,6 @@ void IRDaikinESP::setSwingVertical(bool state) { daikin[16] |= 0x0F; else daikin[16] &= 0xF0; - checksum(); } bool IRDaikinESP::getSwingVertical() { @@ -255,93 +266,524 @@ void IRDaikinESP::setSwingHorizontal(bool state) { daikin[17] |= 0x0F; else daikin[17] &= 0xF0; - checksum(); } bool IRDaikinESP::getSwingHorizontal() { return daikin[17] & 0x01; } -#endif // SEND_DAIKIN + +void IRDaikinESP::setQuiet(bool state) { + if (state) { + setBit(DAIKIN_BYTE_SILENT, DAIKIN_BIT_SILENT); + // Powerful & Quiet mode being on are mutually exclusive. + setPowerful(false); + } else { + clearBit(DAIKIN_BYTE_SILENT, DAIKIN_BIT_SILENT); + } +} + +bool IRDaikinESP::getQuiet() { + return (getBit(DAIKIN_BYTE_SILENT, DAIKIN_BIT_SILENT) > 0); +} + +void IRDaikinESP::setPowerful(bool state) { + if (state) { + setBit(DAIKIN_BYTE_POWERFUL, DAIKIN_BIT_POWERFUL); + // Powerful, Quiet, & Econo mode being on are mutually exclusive. + setQuiet(false); + setEcono(false); + } else { + clearBit(DAIKIN_BYTE_POWERFUL, DAIKIN_BIT_POWERFUL); + } +} + +bool IRDaikinESP::getPowerful() { + return (getBit(DAIKIN_BYTE_POWERFUL, DAIKIN_BIT_POWERFUL) > 0); +} + +void IRDaikinESP::setSensor(bool state) { + if (state) + setBit(DAIKIN_BYTE_SENSOR, DAIKIN_BIT_SENSOR); + else + clearBit(DAIKIN_BYTE_SENSOR, DAIKIN_BIT_SENSOR); +} + +bool IRDaikinESP::getSensor() { + return (getBit(DAIKIN_BYTE_SENSOR, DAIKIN_BIT_SENSOR) > 0); +} + +void IRDaikinESP::setEcono(bool state) { + if (state) { + setBit(DAIKIN_BYTE_ECONO, DAIKIN_BIT_ECONO); + // Powerful & Econo mode being on are mutually exclusive. + setPowerful(false); + } else { + clearBit(DAIKIN_BYTE_ECONO, DAIKIN_BIT_ECONO); + } +} + +bool IRDaikinESP::getEcono() { + return (getBit(DAIKIN_BYTE_ECONO, DAIKIN_BIT_ECONO) > 0); +} + +void IRDaikinESP::setEye(bool state) { + if (state) + setBit(DAIKIN_BYTE_EYE, DAIKIN_BIT_EYE); + else + clearBit(DAIKIN_BYTE_EYE, DAIKIN_BIT_EYE); +} + +bool IRDaikinESP::getEye() { + return (getBit(DAIKIN_BYTE_EYE, DAIKIN_BIT_EYE) > 0); +} + +void IRDaikinESP::setMold(bool state) { + if (state) + setBit(DAIKIN_BYTE_MOLD, DAIKIN_BIT_MOLD); + else + clearBit(DAIKIN_BYTE_MOLD, DAIKIN_BIT_MOLD); +} + +bool IRDaikinESP::getMold() { + return (getBit(DAIKIN_BYTE_MOLD, DAIKIN_BIT_MOLD) > 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(DAIKIN_BYTE_ON_TIMER, DAIKIN_BIT_ON_TIMER); + 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(DAIKIN_BYTE_ON_TIMER, DAIKIN_BIT_ON_TIMER); +} + +uint16_t IRDaikinESP::getOnTime() { + uint16_t ret; + ret = daikin[19] & 0x0F; + ret = ret << 8; + ret += daikin[18]; + return ret; +} + +bool IRDaikinESP::getOnTimerEnabled() { + return getBit(DAIKIN_BYTE_ON_TIMER, DAIKIN_BIT_ON_TIMER); +} + +// endtime: Number of minutes after midnight, in 10 minutes increments +void IRDaikinESP::enableOffTimer(uint16_t endtime) { + setBit(DAIKIN_BYTE_OFF_TIMER, DAIKIN_BIT_OFF_TIMER); + daikin[20] = (uint8_t)((endtime >> 4) & 0xFF); + daikin[19] &= 0x0F; + daikin[19] |= (uint8_t) ((endtime & 0x000F) << 4); +} + +void IRDaikinESP::disableOffTimer() { + enableOffTimer(0x600); + clearBit(DAIKIN_BYTE_OFF_TIMER, DAIKIN_BIT_OFF_TIMER); +} + +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(DAIKIN_BYTE_OFF_TIMER, DAIKIN_BIT_OFF_TIMER); +} + +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 DAIKIN_AUTO: + result += " (AUTO)"; + break; + case DAIKIN_COOL: + result += " (COOL)"; + break; + case DAIKIN_HEAT: + result += " (HEAT)"; + break; + case DAIKIN_DRY: + result += " (DRY)"; + break; + case DAIKIN_FAN: + result += " (FAN)"; + break; + default: + result += " (UNKNOWN)"; + } + result += ", Temp: " + uint64ToString(getTemp()) + "C"; + result += ", Fan: " + uint64ToString(getFan()); + switch (getFan()) { + case DAIKIN_FAN_AUTO: + result += " (AUTO)"; + break; + case DAIKIN_FAN_QUIET: + result += " (QUIET)"; + break; + case DAIKIN_FAN_MIN: + result += " (MIN)"; + break; + case DAIKIN_FAN_MAX: + 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 < DAIKIN_COMMAND_LENGTH; 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 -// TODO(crankyoldgit): NOT WORKING. This needs to be finished. -// Decode the supplied Daikin A/C message. (NOT WORKING - DO NOT USE) + +void addbit(bool val, unsigned char data[]) { + uint8_t curbit = data[DAIKIN_CURBIT]; + uint8_t curindex = data[DAIKIN_CURINDEX]; + if (val) { + unsigned char bit = 1; + bit = bit << curbit; + data[curindex] |= bit; + } + curbit++; + if (curbit == 8) { + curbit = 0; + curindex++; + } + data[DAIKIN_CURBIT] = curbit; + data[DAIKIN_CURINDEX] = curindex; +} + +bool checkheader(decode_results *results, uint16_t* offset) { + if (!IRrecv::matchMark(results->rawbuf[(*offset)++], DAIKIN_BIT_MARK, + DAIKIN_TOLERANCE, DAIKIN_MARK_EXCESS)) + return false; + if (!IRrecv::matchSpace(results->rawbuf[(*offset)++], + DAIKIN_ZERO_SPACE + DAIKIN_GAP, + DAIKIN_TOLERANCE, DAIKIN_MARK_EXCESS)) + return false; + if (!IRrecv::matchMark(results->rawbuf[(*offset)++], DAIKIN_HDR_MARK, + DAIKIN_TOLERANCE, DAIKIN_MARK_EXCESS)) + return false; + if (!IRrecv::matchSpace(results->rawbuf[(*offset)++], DAIKIN_HDR_SPACE, + DAIKIN_TOLERANCE, DAIKIN_MARK_EXCESS)) + 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)++], DAIKIN_BIT_MARK, + DAIKIN_TOLERANCE, DAIKIN_MARK_EXCESS)) + return false; + if (IRrecv::matchSpace(results->rawbuf[*offset], DAIKIN_ONE_SPACE, + DAIKIN_TOLERANCE, DAIKIN_MARK_EXCESS)) + addbit(1, daikin_code); + else if (IRrecv::matchSpace(results->rawbuf[*offset], DAIKIN_ZERO_SPACE, + DAIKIN_TOLERANCE, DAIKIN_MARK_EXCESS)) + 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. Typically SAMSUNG_BITS. +// nbits: Nr. of bits to expect in the data portion. (DAIKIN_RAW_BITS) // 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: UNFINISHED / Completely not working, not even vaguely. +// 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 < 2 * nbits + HEADER + FOOTER) + if (results->rawlen < DAIKIN_RAW_BITS) return false; // Compliance - if (strict && nbits != DAIKIN_BITS) + if (strict && nbits != DAIKIN_RAW_BITS) return false; - uint32_t data = 0; uint16_t offset = OFFSET_START; + unsigned char daikin_code[DAIKIN_COMMAND_LENGTH + 2]; + for (uint8_t i = 0; i < DAIKIN_COMMAND_LENGTH+2; i++) + daikin_code[i] = 0; - // Header - if (!matchMark(results->rawbuf[offset++], DAIKIN_HDR_MARK)) - return false; - if (!matchSpace(results->rawbuf[offset++], DAIKIN_HDR_SPACE)) + // Header (#1) + for (uint8_t i = 0; i < 10; i++) { + if (!matchMark(results->rawbuf[offset++], DAIKIN_BIT_MARK)) return false; + } + if (!checkheader(results, &offset)) return false; // Data (#1) - for (uint8_t i = 0; i < sizeof(data) * 8; i++, offset++) { - if (!matchMark(results->rawbuf[offset++], DAIKIN_ONE_MARK)) - return false; - if (matchSpace(results->rawbuf[offset], DAIKIN_ONE_SPACE)) - data = (data << 1) | 1; // 1 - else if (matchSpace(results->rawbuf[offset], DAIKIN_ZERO_SPACE)) - data <<= 1; // 0 - else - return false; - } + if (!readbits(results, &offset, daikin_code, 8 * 8)) return false; - uint32_t number = data; // some number... - uint32_t reversed = reverseBits(number, sizeof(number) * 8) + // 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 < DAIKIN_COMMAND_LENGTH + 2; i++) + daikin_code[i] = 0; - DPRINT("Code "); - DPRINTLN(reversed, HEX); + // Header (#2) + if (!checkheader(results, &offset)) return false; // Data (#2) - for (uint8_t i = 0; i < sizeof(data) * 8; i++, offset++) { - if (!matchMark(results->rawbuf[offset++], DAIKIN_ONE_MARK)) - return false; - if (matchSpace(results->rawbuf[offset], DAIKIN_ONE_SPACE)) - data = (data << 1) | 1; // 1 - else if (matchSpace(results->rawbuf[offset], DAIKIN_ZERO_SPACE)) - data <<= 1; // 0 - else - return false; - } + if (!readbits(results, &offset, daikin_code, 8 * 8)) return false; - number = data; // some number... - reversed = reverseBits(number, sizeof(number) * 8) + // Header (#3) + if (!checkheader(results, &offset)) return false; - DPRINT("Code2 "); - DPRINTLN(reversed, HEX); - - if (!matchSpace(results->rawbuf[offset++], DAIKIN_GAP)) { - DPRINTLN("no gap"); + // Data (#3), read up everything else + if (!readbits(results, &offset, daikin_code, DAIKIN_BITS - (8 * 8))) return false; + + // Footer + if (!matchMark(results->rawbuf[offset++], DAIKIN_BIT_MARK)) + return false; + if (offset < results->rawlen && !matchAtLeast(results->rawbuf[offset], + DAIKIN_GAP)) + return false; + + // Compliance + if (strict) { + if (!IRDaikinESP::validChecksum(daikin_code)) return false; } // Success - results->bits = DAIKIN_BITS; - results->value = reversed; +#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 < DAIKIN_COMMAND_LENGTH; i++) + results->state[i] = daikin_code[i]; + results->bits = DAIKIN_COMMAND_LENGTH * 8; results->decode_type = DAIKIN; - results->address = 0; - results->command = 0; return true; } #endif // DECODE_DAIKIN diff --git a/lib/IRremoteESP8266/src/ir_Daikin.h b/lib/IRremoteESP8266/src/ir_Daikin.h index 7172a5e..9ad9323 100644 --- a/lib/IRremoteESP8266/src/ir_Daikin.h +++ b/lib/IRremoteESP8266/src/ir_Daikin.h @@ -1,10 +1,20 @@ -/* Copyright 2016 sillyfrog */ +// Copyright 2016 sillyfrog +// Copyright 2017 sillyfrog, crankyoldgit #ifndef IR_DAIKIN_H_ #define IR_DAIKIN_H_ +#ifndef UNIT_TEST +#include +#else +#include +#endif #include "IRremoteESP8266.h" +#include "IRrecv.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 @@ -13,6 +23,9 @@ /* 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 @@ -23,22 +36,22 @@ 110 = FAN (temp not shown, but 25) 000 = Fully Automatic (temp 25) 010 = DRY (temp 0xc0 = 96 degrees c) - b3 = 0 + b3 = 1 b2 = OFF timer set b1 = ON timer set b0 = Air Conditioner ON - byte 14=temp*2 (Temp should be between 18 - 32) + 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×30 = 1 bar - 0×40 = 2 bar - 0×50 = 3 bar - 0×60 = 4 bar - 0×70 = 5 bar - 0xa0 = Auto - 0xb0 = Not auto, moon + tree + 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 @@ -47,8 +60,18 @@ 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 -> Intelligent eye on (bit 7) + 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 */ @@ -58,27 +81,65 @@ #define DAIKIN_FAN 0b110 #define DAIKIN_AUTO 0b000 #define DAIKIN_DRY 0b010 -#define DAIKIN_POWERFUL 0b00000010 -#define DAIKIN_SILENT 0b00100000 -#define DAIKIN_MIN_TEMP 18U // Celsius +#define DAIKIN_MIN_TEMP 10U // Celsius #define DAIKIN_MAX_TEMP 32U // Celsius -#define DAIKIN_FAN_AUTO (uint8_t) 0U #define DAIKIN_FAN_MIN (uint8_t) 1U #define DAIKIN_FAN_MAX (uint8_t) 5U +#define DAIKIN_FAN_AUTO (uint8_t) 0b1010 +#define DAIKIN_FAN_QUIET (uint8_t) 0b1011 + +#define DAIKIN_BYTE_POWER 13 +#define DAIKIN_BIT_POWER 0b00000001 + +#define DAIKIN_BYTE_POWERFUL 21 +#define DAIKIN_BIT_POWERFUL 0b00000001 +#define DAIKIN_BYTE_SILENT 21 +#define DAIKIN_BIT_SILENT 0b00100000 + +#define DAIKIN_BYTE_SENSOR 24 +#define DAIKIN_BIT_SENSOR 0b00000010 +#define DAIKIN_BYTE_ECONO 24 +#define DAIKIN_BIT_ECONO 0b00000100 +#define DAIKIN_BYTE_EYE 24 +#define DAIKIN_BIT_EYE 0b10000000 +#define DAIKIN_BYTE_MOLD 25 +#define DAIKIN_BIT_MOLD 0b00000010 + +#define DAIKIN_BYTE_OFF_TIMER 13 +#define DAIKIN_BIT_OFF_TIMER 0b00000100 +#define DAIKIN_BYTE_ON_TIMER 13 +#define DAIKIN_BIT_ON_TIMER 0b00000010 + +#define DAIKIN_CURBIT DAIKIN_COMMAND_LENGTH +#define DAIKIN_CURINDEX (DAIKIN_COMMAND_LENGTH + 1) +#define OFFSET_ERR 65432 + +#define DAIKIN_TOLERANCE 35 +#define DAIKIN_MARK_EXCESS MARK_EXCESS + +#define DAIKIN_HDR_MARK 3650U // DAIKIN_BIT_MARK * 8 +#define DAIKIN_HDR_SPACE 1623U // DAIKIN_BIT_MARK * 4 +#define DAIKIN_BIT_MARK 428U +#define DAIKIN_ZERO_SPACE 428U +#define DAIKIN_ONE_SPACE 1280U +#define DAIKIN_GAP 29000U + +// Note bits in each octet swapped so can be sent as a single value +#define DAIKIN_FIRST_HEADER64 \ + 0b1101011100000000000000001100010100000000001001111101101000010001 -#if SEND_DAIKIN class IRDaikinESP { public: explicit IRDaikinESP(uint16_t pin); +#if SEND_DAIKIN void send(); +#endif void begin(); void on(); void off(); void setPower(bool state); - uint8_t getPower(); - void setAux(uint8_t aux); - uint8_t getAux(); + bool getPower(); void setTemp(uint8_t temp); uint8_t getTemp(); void setFan(uint8_t fan); @@ -93,15 +154,51 @@ class IRDaikinESP { 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 = DAIKIN_COMMAND_LENGTH); +#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[DAIKIN_COMMAND_LENGTH]; 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 #endif // IR_DAIKIN_H_ diff --git a/lib/IRremoteESP8266/src/ir_Denon.cpp b/lib/IRremoteESP8266/src/ir_Denon.cpp index 538e5af..a4feb55 100644 --- a/lib/IRremoteESP8266/src/ir_Denon.cpp +++ b/lib/IRremoteESP8266/src/ir_Denon.cpp @@ -4,7 +4,6 @@ #include #include "IRrecv.h" #include "IRsend.h" -#include "IRtimer.h" #include "IRutils.h" // DDDD EEEEE N N OOO N N diff --git a/lib/IRremoteESP8266/src/ir_Dish.cpp b/lib/IRremoteESP8266/src/ir_Dish.cpp index 4555db9..f5daf21 100644 --- a/lib/IRremoteESP8266/src/ir_Dish.cpp +++ b/lib/IRremoteESP8266/src/ir_Dish.cpp @@ -3,7 +3,6 @@ #include "IRrecv.h" #include "IRsend.h" -#include "IRtimer.h" #include "IRutils.h" // DDDD IIIII SSSS H H @@ -56,20 +55,16 @@ // Ref: // http://www.hifi-remote.com/wiki/index.php?title=Dish void IRsend::sendDISH(uint64_t data, uint16_t nbits, uint16_t repeat) { - // Set 57.6kHz IR carrier frequency, duty cycle is unknown. - enableIROut(57600); - // Header + enableIROut(57600); // Set modulation freq. to 57.6kHz. + // Header is only ever sent once. mark(DISH_HDR_MARK); space(DISH_HDR_SPACE); - // We always send a command, even for repeat=0, hence '<= repeat'. - for (uint16_t i = 0; i <= repeat; i++) { - // Data - sendData(DISH_BIT_MARK, DISH_ONE_SPACE, DISH_BIT_MARK, DISH_ZERO_SPACE, - data, nbits, true); - // Footer - mark(DISH_BIT_MARK); - space(DISH_RPT_SPACE); - } + + sendGeneric(0, 0, // No headers from here on in. + DISH_BIT_MARK, DISH_ONE_SPACE, + DISH_BIT_MARK, DISH_ZERO_SPACE, + DISH_BIT_MARK, DISH_RPT_SPACE, + data, nbits, 57600, true, repeat, 50); } #endif diff --git a/lib/IRremoteESP8266/src/ir_Fujitsu.cpp b/lib/IRremoteESP8266/src/ir_Fujitsu.cpp index adf753c..70e786b 100644 --- a/lib/IRremoteESP8266/src/ir_Fujitsu.cpp +++ b/lib/IRremoteESP8266/src/ir_Fujitsu.cpp @@ -1,30 +1,39 @@ -// Copyright 2017 Jonny Graham +// Copyright 2017 Jonny Graham, David Conran #include "ir_Fujitsu.h" #include +#ifndef ARDUINO +#include +#endif #include "IRsend.h" +#include "IRutils.h" -// Fujitsu A/C support added by Jonny Graham +// Fujitsu A/C support added by Jonny Graham & David Conran +// Equipment it seems compatible with: +// * Fujitsu ASYG30LFCA with remote AR-RAH2E +// * Fujitsu AST9RSGCW with remote AR-DB1 +// * -// Fujitsu A/C // Ref: // These values are based on averages of measurements -#define FUJITSU_AC_HDR_MARK 3224U +#define FUJITSU_AC_HDR_MARK 3324U #define FUJITSU_AC_HDR_SPACE 1574U #define FUJITSU_AC_BIT_MARK 448U #define FUJITSU_AC_ONE_SPACE 1182U -#define FUJITSU_AC_ZERO_SPACE 367U -#define FUJITSU_AC_TRL_MARK 448U -#define FUJITSU_AC_TRL_SPACE 8100U +#define FUJITSU_AC_ZERO_SPACE 390U +#define FUJITSU_AC_MIN_GAP 8100U #if SEND_FUJITSU_AC // Send a Fujitsu A/C message. // // Args: // data: An array of bytes containing the IR command. -// nbytes: Nr. of bytes of data in the array. (typically either -// FUJITSU_AC_STATE_LENGTH or FUJITSU_AC_STATE_LENGTH_SHORT) +// nbytes: Nr. of bytes of data in the array. Typically one of: +// FUJITSU_AC_STATE_LENGTH +// FUJITSU_AC_STATE_LENGTH - 1 +// FUJITSU_AC_STATE_LENGTH_SHORT +// FUJITSU_AC_STATE_LENGTH_SHORT - 1 // repeat: Nr. of times the message is to be repeated. // (Default = FUJITSU_AC_MIN_REPEAT). // @@ -32,35 +41,36 @@ // void IRsend::sendFujitsuAC(unsigned char data[], uint16_t nbytes, uint16_t repeat) { - // Set IR carrier frequency - enableIROut(38); - for (uint16_t r = 0; r <= repeat; ++r) { - // Header - mark(FUJITSU_AC_HDR_MARK); - space(FUJITSU_AC_HDR_SPACE); - // Data - for (uint16_t i = 0; i < nbytes; i++) - sendData(FUJITSU_AC_BIT_MARK, FUJITSU_AC_ONE_SPACE, - FUJITSU_AC_BIT_MARK, FUJITSU_AC_ZERO_SPACE, - data[i], 8, false); - // Footer - mark(FUJITSU_AC_TRL_MARK); - space(FUJITSU_AC_TRL_SPACE); - } + sendGeneric(FUJITSU_AC_HDR_MARK, FUJITSU_AC_HDR_SPACE, + FUJITSU_AC_BIT_MARK, FUJITSU_AC_ONE_SPACE, + FUJITSU_AC_BIT_MARK, FUJITSU_AC_ZERO_SPACE, + FUJITSU_AC_BIT_MARK, FUJITSU_AC_MIN_GAP, + data, nbytes, 38, false, repeat, 50); } +#endif // SEND_FUJITSU_AC // Code to emulate Fujitsu A/C IR remote control unit. -// Warning: Consider this very alpha code. Seems to work, but not validated. -// -// Equipment it seems compatible with: -// * Fujitsu ASYG30LFCA with remote AR-RAH2E -// * // Initialise the object. -IRFujitsuAC::IRFujitsuAC(uint16_t pin) : _irsend(pin) { +IRFujitsuAC::IRFujitsuAC(uint16_t pin, fujitsu_ac_remote_model_t model) + : _irsend(pin) { + setModel(model); stateReset(); } +void IRFujitsuAC::setModel(fujitsu_ac_remote_model_t model) { + _model = model; + switch (model) { + case ARDB1: + _state_length = FUJITSU_AC_STATE_LENGTH - 1; + _state_length_short = FUJITSU_AC_STATE_LENGTH_SHORT - 1; + break; + default: + _state_length = FUJITSU_AC_STATE_LENGTH; + _state_length_short = FUJITSU_AC_STATE_LENGTH_SHORT; + } +} + // Reset the state of the remote to a known good state/sequence. void IRFujitsuAC::stateReset() { _temp = 24; @@ -68,29 +78,23 @@ void IRFujitsuAC::stateReset() { _mode = FUJITSU_AC_MODE_COOL; _swingMode = FUJITSU_AC_SWING_BOTH; _cmd = FUJITSU_AC_CMD_TURN_ON; + buildState(); } // Configure the pin for output. void IRFujitsuAC::begin() { - _irsend.begin(); + _irsend.begin(); } +#if SEND_FUJITSU_AC // Send the current desired state to the IR LED. void IRFujitsuAC::send() { getRaw(); - uint8_t len = getCommandLength(); - _irsend.sendFujitsuAC(remote_state, len); + _irsend.sendFujitsuAC(remote_state, getStateLength()); } +#endif // SEND_FUJITSU_AC -uint8_t IRFujitsuAC::getCommandLength() { - if (remote_state[5] != 0xFE) - return FUJITSU_AC_STATE_LENGTH_SHORT; - else - return FUJITSU_AC_STATE_LENGTH; -} - -// Return a pointer to the internal state date of the remote. -uint8_t* IRFujitsuAC::getRaw() { +void IRFujitsuAC::buildState() { remote_state[0] = 0x14; remote_state[1] = 0x63; remote_state[2] = 0x00; @@ -108,48 +112,130 @@ uint8_t* IRFujitsuAC::getRaw() { remote_state[5] = 0x6C; break; default: - remote_state[5] = 0xFE; + switch (_model) { + case ARRAH2E: + remote_state[5] = 0xFE; + break; + case ARDB1: + remote_state[5] = 0xFC; + break; + } fullCmd = true; break; } - if (fullCmd) { - remote_state[6] = 0x09; - remote_state[7] = 0x30; + if (fullCmd) { // long codes uint8_t tempByte = _temp - FUJITSU_AC_MIN_TEMP; + // Nr. of bytes in the message after this byte. + remote_state[6] = _state_length - 7; + + remote_state[7] = 0x30; remote_state[8] = (_cmd == FUJITSU_AC_CMD_TURN_ON) | (tempByte << 4); remote_state[9] = _mode | 0 << 4; // timer off remote_state[10] = _fanSpeed | _swingMode << 4; remote_state[11] = 0; // timerOff values - remote_state[12] = 0; // timerOff/on values + remote_state[12] = 0; // timerOff/On values remote_state[13] = 0; // timerOn values - remote_state[14] = 0x20; - // Checksum is the sum of the 8th to 16th bytes (ie remote_state[7] - // thru remote_state[15]). - // The checksum itself is stored in the 16th byte (ie remote_state[15]). - // So we sum bytes 8th-15th... + if (_model == ARRAH2E) + remote_state[14] = 0x20; + else + remote_state[14] = 0x00; + uint8_t checksum = 0; - for (uint8_t i = 7 ; i < 15; ++i) { - checksum += remote_state[i]; + uint8_t checksum_complement = 0; + if (_model == ARRAH2E) { + checksum = sumBytes(remote_state + _state_length_short, + _state_length - _state_length_short - 1); + } else if (_model == ARDB1) { + checksum = sumBytes(remote_state, _state_length - 1); + checksum_complement = 0x9B; } - // and then do 0 - sum and store it in 16th. - remote_state[15] = 0 - checksum; - } else { - // For the short codes, byte 7 is the inverse of byte 6 - remote_state[6] = ~remote_state[5]; - for (uint8_t i = 7; i < FUJITSU_AC_STATE_LENGTH; ++i) { + // and negate the checksum and store it in the last byte. + remote_state[_state_length - 1] = checksum_complement - checksum; + } else { // short codes + if (_model == ARRAH2E) + // The last byte is the inverse of penultimate byte + remote_state[_state_length_short - 1] = ~remote_state[_state_length_short + - 2]; + // Zero the rest of the state. + for (uint8_t i = _state_length_short; + i < FUJITSU_AC_STATE_LENGTH; + i++) remote_state[i] = 0; - } } +} + +uint8_t IRFujitsuAC::getStateLength() { + buildState(); // Force an update of the internal state. + if ((_model == ARRAH2E && remote_state[5] != 0xFE) || + (_model == ARDB1 && remote_state[5] != 0xFC)) + return _state_length_short; + else + return _state_length; +} + +// Return a pointer to the internal state date of the remote. +uint8_t* IRFujitsuAC::getRaw() { + buildState(); return remote_state; } +void IRFujitsuAC::buildFromState(const uint16_t length) { + switch (length) { + case FUJITSU_AC_STATE_LENGTH - 1: + case FUJITSU_AC_STATE_LENGTH_SHORT - 1: + setModel(ARDB1); + break; + default: + setModel(ARRAH2E); + } + switch (remote_state[6]) { + case 8: + setModel(ARDB1); + break; + case 9: + setModel(ARRAH2E); + break; + } + setTemp((remote_state[8] >> 4) + FUJITSU_AC_MIN_TEMP); + if (remote_state[8] & 0x1) + setCmd(FUJITSU_AC_CMD_TURN_ON); + else + setCmd(FUJITSU_AC_CMD_STAY_ON); + setMode(remote_state[9] & 0b111); + setFanSpeed(remote_state[10] & 0b111); + setSwing(remote_state[10] >> 4); + switch (remote_state[5]) { + case FUJITSU_AC_CMD_TURN_OFF: + case FUJITSU_AC_CMD_STEP_HORIZ: + case FUJITSU_AC_CMD_STEP_VERT: + setCmd(remote_state[5]); + break; + } +} + +bool IRFujitsuAC::setRaw(const uint8_t newState[], const uint16_t length) { + if (length > FUJITSU_AC_STATE_LENGTH) return false; + for (uint16_t i = 0; i < FUJITSU_AC_STATE_LENGTH; i++) { + if (i < length) + remote_state[i] = newState[i]; + else + remote_state[i] = 0; + } + buildFromState(length); + return true; +} + // Set the requested power state of the A/C to off. void IRFujitsuAC::off() { _cmd = FUJITSU_AC_CMD_TURN_OFF; } void IRFujitsuAC::stepHoriz() { - _cmd = FUJITSU_AC_CMD_STEP_HORIZ; + switch (_model) { + case ARDB1: break; // This remote doesn't have a horizontal option. + default: + _cmd = FUJITSU_AC_CMD_STEP_HORIZ; + } } void IRFujitsuAC::stepVert() { @@ -162,20 +248,26 @@ void IRFujitsuAC::setCmd(uint8_t cmd) { case FUJITSU_AC_CMD_TURN_OFF: case FUJITSU_AC_CMD_TURN_ON: case FUJITSU_AC_CMD_STAY_ON: - case FUJITSU_AC_CMD_STEP_HORIZ: case FUJITSU_AC_CMD_STEP_VERT: + _cmd = cmd; break; + case FUJITSU_AC_CMD_STEP_HORIZ: + if (_model != ARDB1) // AR-DB1 remote doesn't have step horizontal. + _cmd = cmd; default: - cmd = FUJITSU_AC_CMD_STAY_ON; + _cmd = FUJITSU_AC_CMD_STAY_ON; break; } - _cmd = cmd; } uint8_t IRFujitsuAC::getCmd() { return _cmd; } +bool IRFujitsuAC::getPower() { + return _cmd != FUJITSU_AC_CMD_TURN_OFF; +} + // Set the temp. in deg C void IRFujitsuAC::setTemp(uint8_t temp) { temp = std::max((uint8_t) FUJITSU_AC_MIN_TEMP, temp); @@ -209,8 +301,18 @@ uint8_t IRFujitsuAC::getMode() { } // Set the requested swing operation mode of the a/c unit. void IRFujitsuAC::setSwing(uint8_t swingMode) { - if (swingMode > FUJITSU_AC_SWING_BOTH) - swingMode = FUJITSU_AC_SWING_BOTH; // Set the mode to max if out of range + switch (_model) { + case ARDB1: + // Set the mode to max if out of range + if (swingMode > FUJITSU_AC_SWING_VERT) + swingMode = FUJITSU_AC_SWING_VERT; + break; + case ARRAH2E: + default: + // Set the mode to max if out of range + if (swingMode > FUJITSU_AC_SWING_BOTH) + swingMode = FUJITSU_AC_SWING_BOTH; + } _swingMode = swingMode; } @@ -218,4 +320,228 @@ uint8_t IRFujitsuAC::getSwing() { return _swingMode; } -#endif +bool IRFujitsuAC::validChecksum(uint8_t state[], uint16_t length) { + uint8_t sum = 0; + uint8_t sum_complement = 0; + uint8_t checksum = 0; + switch (length) { + case FUJITSU_AC_STATE_LENGTH_SHORT: // ARRAH2E + return state[length - 1] == (uint8_t) ~state[length - 2]; + case FUJITSU_AC_STATE_LENGTH - 1: // ARDB1 + sum = sumBytes(state, length - 1); + sum_complement = 0x9B; + checksum = state[length - 1]; + break; + case FUJITSU_AC_STATE_LENGTH: // ARRAH2E + sum = sumBytes(state + FUJITSU_AC_STATE_LENGTH_SHORT, + length - 1 - FUJITSU_AC_STATE_LENGTH_SHORT); + default: // Includes ARDB1 short. + return true; // Assume the checksum is valid for other lengths. + } + return checksum == (uint8_t) (sum_complement - sum); // Does it match? +} + +// Convert the internal state into a human readable string. +#ifdef ARDUINO +String IRFujitsuAC::toString() { + String result = ""; +#else +std::string IRFujitsuAC::toString() { + std::string result = ""; +#endif // ARDUINO + result += "Power: "; + if (getPower()) + result += "On"; + else + result += "Off"; + result += ", Mode: " + uint64ToString(getMode()); + switch (getMode()) { + case FUJITSU_AC_MODE_AUTO: + result += " (AUTO)"; + break; + case FUJITSU_AC_MODE_COOL: + result += " (COOL)"; + break; + case FUJITSU_AC_MODE_HEAT: + result += " (HEAT)"; + break; + case FUJITSU_AC_MODE_DRY: + result += " (DRY)"; + break; + case FUJITSU_AC_MODE_FAN: + result += " (FAN)"; + break; + default: + result += " (UNKNOWN)"; + } + result += ", Temp: " + uint64ToString(getTemp()) + "C"; + result += ", Fan: " + uint64ToString(getFanSpeed()); + switch (getFanSpeed()) { + case FUJITSU_AC_FAN_AUTO: + result += " (AUTO)"; + break; + case FUJITSU_AC_FAN_HIGH: + result += " (HIGH)"; + break; + case FUJITSU_AC_FAN_MED: + result += " (MED)"; + break; + case FUJITSU_AC_FAN_LOW: + result += " (LOW)"; + break; + case FUJITSU_AC_FAN_QUIET: + result += " (QUIET)"; + break; + } + result += ", Swing: "; + switch (getSwing()) { + case FUJITSU_AC_SWING_OFF: + result += "Off"; + break; + case FUJITSU_AC_SWING_VERT: + result += "Vert"; + break; + case FUJITSU_AC_SWING_HORIZ: + result += "Horiz"; + break; + case FUJITSU_AC_SWING_BOTH: + result += "Vert + Horiz"; + break; + default: + result += "UNKNOWN"; + } + result += ", Command: "; + switch (getCmd()) { + case FUJITSU_AC_CMD_STEP_HORIZ: + result += "Step vane horizontally"; + break; + case FUJITSU_AC_CMD_STEP_VERT: + result += "Step vane vertically"; + break; + default: + result += "N/A"; + } + return result; +} + +#if DECODE_FUJITSU_AC +// Decode a Fujitsu AC IR message if possible. +// Places successful decode information in the results pointer. +// Args: +// results: Ptr to the data to decode and where to store the decode result. +// nbits: The number of data bits to expect. Typically FUJITSU_AC_BITS. +// 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 / Untested. +// +// Ref: +// +bool IRrecv::decodeFujitsuAC(decode_results *results, uint16_t nbits, + bool strict) { + uint16_t offset = OFFSET_START; + uint16_t dataBitsSoFar = 0; + + // Have we got enough data to successfully decode? + if (results->rawlen < (2 * FUJITSU_AC_MIN_BITS) + HEADER + FOOTER - 1) + return false; // Can't possibly be a valid message. + + + // Compliance + if (strict) { + switch (nbits) { + case FUJITSU_AC_BITS: + case FUJITSU_AC_BITS - 8: + case FUJITSU_AC_MIN_BITS: + case FUJITSU_AC_MIN_BITS + 8: + break; + default: + return false; // Must be called with the correct nr. of bits. + } + } + + // Header + if (!matchMark(results->rawbuf[offset++], FUJITSU_AC_HDR_MARK)) + return false; + if (!matchSpace(results->rawbuf[offset++], FUJITSU_AC_HDR_SPACE)) + return false; + + // Data (Fixed signature) + match_result_t data_result = matchData(&(results->rawbuf[offset]), + FUJITSU_AC_MIN_BITS - 8, + FUJITSU_AC_BIT_MARK, + FUJITSU_AC_ONE_SPACE, + FUJITSU_AC_BIT_MARK, + FUJITSU_AC_ZERO_SPACE); + if (data_result.success == false) return false; // Fail + if (reverseBits(data_result.data, FUJITSU_AC_MIN_BITS - 8) != 0x1010006314) + return false; // Signature failed. + dataBitsSoFar += FUJITSU_AC_MIN_BITS - 8; + offset += data_result.used; + results->state[0] = 0x14; + results->state[1] = 0x63; + results->state[2] = 0x00; + results->state[3] = 0x10; + results->state[4] = 0x10; + + // Keep reading bytes until we either run out of message or state to fill. + for (uint16_t i = 5; + offset <= results->rawlen - 16 && i < FUJITSU_AC_STATE_LENGTH; + i++, dataBitsSoFar += 8, offset += data_result.used) { + data_result = matchData(&(results->rawbuf[offset]), 8, + FUJITSU_AC_BIT_MARK, + FUJITSU_AC_ONE_SPACE, + FUJITSU_AC_BIT_MARK, + FUJITSU_AC_ZERO_SPACE); + if (data_result.success == false) break; // Fail + results->state[i] = (uint8_t) reverseBits(data_result.data, 8); + } + + // Footer + if (offset > results->rawlen || + !matchMark(results->rawbuf[offset++], FUJITSU_AC_BIT_MARK)) return false; + // The space is optional if we are out of capture. + if (offset < results->rawlen && + !matchAtLeast(results->rawbuf[offset], FUJITSU_AC_MIN_GAP)) return false; + + // Compliance + if (strict) { + if (dataBitsSoFar != nbits) return false; + } + + results->decode_type = FUJITSU_AC; + results->bits = dataBitsSoFar; + + // Compliance + switch (dataBitsSoFar) { + case FUJITSU_AC_MIN_BITS: + // Check if this values indicate that this should have been a long state + // message. + if (results->state[5] == 0xFC) return false; + return true; // Success + case FUJITSU_AC_MIN_BITS + 8: + // Check if this values indicate that this should have been a long state + // message. + if (results->state[5] == 0xFE) return false; + // The last byte needs to be the inverse of the penultimate byte. + if (results->state[5] != (uint8_t) ~results->state[6]) return false; + return true; // Success + case FUJITSU_AC_BITS - 8: + // Long messages of this size require this byte be correct. + if (results->state[5] != 0xFC) return false; + break; + case FUJITSU_AC_BITS: + // Long messages of this size require this byte be correct. + if (results->state[5] != 0xFE) return false; + break; + default: + return false; // Unexpected size. + } + if (!IRFujitsuAC::validChecksum(results->state, dataBitsSoFar / 8)) + return false; + + // Success + return true; // All good. +} +#endif // DECODE_FUJITSU_AC diff --git a/lib/IRremoteESP8266/src/ir_Fujitsu.h b/lib/IRremoteESP8266/src/ir_Fujitsu.h index 451a5b2..ed6317d 100644 --- a/lib/IRremoteESP8266/src/ir_Fujitsu.h +++ b/lib/IRremoteESP8266/src/ir_Fujitsu.h @@ -4,7 +4,13 @@ #define __STDC_LIMIT_MACROS #include +#ifdef ARDUINO +#include +#else +#include +#endif #include "IRremoteESP8266.h" +#include "IRrecv.h" #include "IRsend.h" @@ -38,16 +44,21 @@ #define FUJITSU_AC_SWING_HORIZ 0x02U #define FUJITSU_AC_SWING_BOTH 0x03U -#define FUJITSU_AC_STATE_LENGTH 16 -#define FUJITSU_AC_STATE_LENGTH_SHORT 7 -#if SEND_FUJITSU_AC +enum fujitsu_ac_remote_model_t { + ARRAH2E = 1, + ARDB1, +}; + class IRFujitsuAC { public: - explicit IRFujitsuAC(uint16_t pin); + explicit IRFujitsuAC(uint16_t pin, fujitsu_ac_remote_model_t model = ARRAH2E); + void setModel(fujitsu_ac_remote_model_t model); void stateReset(); +#if SEND_FUJITSU_AC void send(); +#endif // SEND_FUJITSU_AC void begin(); void off(); void stepHoriz(); @@ -63,18 +74,29 @@ class IRFujitsuAC { void setSwing(uint8_t mode); uint8_t getSwing(); uint8_t* getRaw(); + bool setRaw(const uint8_t newState[], const uint16_t length); + uint8_t getStateLength(); + static bool validChecksum(uint8_t *state, uint16_t length); + bool getPower(); + #ifdef ARDUINO + String toString(); + #else + std::string toString(); + #endif private: uint8_t remote_state[FUJITSU_AC_STATE_LENGTH]; - uint8_t getCommandLength(); IRsend _irsend; uint8_t _temp; uint8_t _fanSpeed; uint8_t _mode; uint8_t _swingMode; uint8_t _cmd; + fujitsu_ac_remote_model_t _model; + uint8_t _state_length; + uint8_t _state_length_short; + void buildState(); + void buildFromState(const uint16_t length); }; -#endif - #endif // IR_FUJITSU_H_ diff --git a/lib/IRremoteESP8266/src/ir_GICable.cpp b/lib/IRremoteESP8266/src/ir_GICable.cpp new file mode 100644 index 0000000..2359acc --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_GICable.cpp @@ -0,0 +1,119 @@ +// Copyright 2018 David Conran + +#define __STDC_LIMIT_MACROS +#include +#include +#include "IRrecv.h" +#include "IRsend.h" +#include "IRutils.h" + +// GGGG IIIII CCCCC AAA BBBBB LL EEEEEEE +// GG GG III CC C AAAAA BB B LL EE +// GG III CC AA AA BBBBBB LL EEEEE +// GG GG ... III ... CC C AAAAAAA BB BB LL EE +// GGGGGG ... IIIII ... CCCCC AA AA BBBBBB LLLLLLL EEEEEEE +// +// Ref: +// https://github.com/cyborg5/IRLib2/blob/master/IRLibProtocols/IRLib_P09_GICable.h +// https://github.com/markszabo/IRremoteESP8266/issues/447 + +// Constants +#define GICABLE_HDR_MARK 9000U +#define GICABLE_HDR_SPACE 4400U +#define GICABLE_BIT_MARK 550U +#define GICABLE_ONE_SPACE 4400U +#define GICABLE_ZERO_SPACE 2200U +#define GICABLE_RPT_SPACE 2200U +#define GICABLE_MIN_COMMAND_LENGTH 99600U +#define GICABLE_MIN_GAP (GICABLE_MIN_COMMAND_LENGTH - \ + (GICABLE_HDR_MARK + GICABLE_HDR_SPACE + \ + GICABLE_BITS * (GICABLE_BIT_MARK + GICABLE_ONE_SPACE) + GICABLE_BIT_MARK)) + + +#if SEND_GICABLE +// Send a raw G.I. Cable formatted message. +// +// Args: +// data: The message to be sent. +// nbits: The number of bits of the message to be sent. +// Typically GICABLE_BITS. +// repeat: The number of times the command is to be repeated. +// +// Status: Alpha / Untested. +// +// Ref: +void IRsend::sendGICable(uint64_t data, uint16_t nbits, uint16_t repeat) { + sendGeneric(GICABLE_HDR_MARK, GICABLE_HDR_SPACE, + GICABLE_BIT_MARK, GICABLE_ONE_SPACE, + GICABLE_BIT_MARK, GICABLE_ZERO_SPACE, + GICABLE_BIT_MARK, GICABLE_MIN_GAP, GICABLE_MIN_COMMAND_LENGTH, + data, nbits, 39, true, 0, // Repeats are handled later. + 50); + // Message repeat sequence. + if (repeat) + sendGeneric(GICABLE_HDR_MARK, GICABLE_RPT_SPACE, + 0, 0, 0, 0, // No actual data sent. + GICABLE_BIT_MARK, GICABLE_MIN_GAP, GICABLE_MIN_COMMAND_LENGTH, + 0, 0, // No data to be sent. + 39, true, repeat - 1, 50); +} +#endif // SEND_GICABLE + +#if DECODE_GICABLE +// Decode the supplied G.I. Cable message. +// +// Args: +// results: Ptr to the data to decode and where to store the decode result. +// nbits: The number of data bits to expect. Typically GICABLE_BITS. +// strict: Flag indicating if we should perform strict matching. +// Returns: +// boolean: True if it can decode it, false if it can't. +// +// Status: Alpha / Not tested against a real device. +bool IRrecv::decodeGICable(decode_results *results, uint16_t nbits, + bool strict) { + if (results->rawlen < 2 * (nbits + HEADER + FOOTER) - 1) + return false; // Can't possibly be a valid GICABLE message. + if (strict && nbits != GICABLE_BITS) + return false; // Not strictly an GICABLE message. + + uint64_t data = 0; + uint16_t offset = OFFSET_START; + + // Header + if (!matchMark(results->rawbuf[offset++], GICABLE_HDR_MARK)) return false; + if (!matchSpace(results->rawbuf[offset++], GICABLE_HDR_SPACE)) return false; + + // Data + match_result_t data_result = matchData(&(results->rawbuf[offset]), nbits, + GICABLE_BIT_MARK, + GICABLE_ONE_SPACE, + GICABLE_BIT_MARK, + GICABLE_ZERO_SPACE); + if (data_result.success == false) return false; + data = data_result.data; + offset += data_result.used; + + // Footer + if (!matchMark(results->rawbuf[offset++], GICABLE_BIT_MARK)) return false; + if (offset < results->rawlen && + !matchAtLeast(results->rawbuf[offset++], GICABLE_MIN_GAP)) + return false; + + // Compliance + if (strict) { + // We expect a repeat frame. + if (!matchMark(results->rawbuf[offset++], GICABLE_HDR_MARK)) return false; + if (!matchSpace(results->rawbuf[offset++], GICABLE_RPT_SPACE)) return false; + if (!matchMark(results->rawbuf[offset++], GICABLE_BIT_MARK)) return false; + } + + // Success + results->bits = nbits; + results->value = data; + results->decode_type = GICABLE; + results->command = 0; + results->address = 0; + return true; +} +#endif // DECODE_GICABLE diff --git a/lib/IRremoteESP8266/src/ir_GlobalCache.cpp b/lib/IRremoteESP8266/src/ir_GlobalCache.cpp index 019413b..bf0a82a 100644 --- a/lib/IRremoteESP8266/src/ir_GlobalCache.cpp +++ b/lib/IRremoteESP8266/src/ir_GlobalCache.cpp @@ -3,7 +3,6 @@ #include #include "IRsend.h" -#include "IRtimer.h" // GGG L OOOO BBBB AA L CCCC AA CCCC H H EEEEEE // G G L O O B B AAAA L C C AAAA C C H H E diff --git a/lib/IRremoteESP8266/src/ir_Gree.cpp b/lib/IRremoteESP8266/src/ir_Gree.cpp index d1bc760..885b06a 100644 --- a/lib/IRremoteESP8266/src/ir_Gree.cpp +++ b/lib/IRremoteESP8266/src/ir_Gree.cpp @@ -1,11 +1,22 @@ // Copyright 2017 Ville Skyttä (scop) -// Copyright 2017 David Conran +// Copyright 2017, 2018 David Conran // -// Gree protocol compatible heat pump carrying the "Ultimate" brand name. +// Code to emulate Gree protocol compatible HVAC devices. +// Should be compatible with: +// * Heat pumps carrying the "Ultimate" brand name. +// * EKOKAI air conditioners. // +#include "ir_Gree.h" +#include +#ifndef ARDUINO +#include +#endif #include "IRremoteESP8266.h" +#include "IRrecv.h" #include "IRsend.h" +#include "IRutils.h" +#include "ir_Kelvinator.h" // GGGG RRRRRR EEEEEEE EEEEEEE // GG GG RR RR EE EE @@ -15,12 +26,14 @@ // Constants // Ref: https://github.com/ToniA/arduino-heatpumpir/blob/master/GreeHeatpumpIR.h -#define GREE_HDR_MARK 9000U -#define GREE_HDR_SPACE 4000U -#define GREE_BIT_MARK 620U -#define GREE_ONE_SPACE 1600U -#define GREE_ZERO_SPACE 540U -#define GREE_MSG_SPACE 19000U +#define GREE_HDR_MARK 9000U +#define GREE_HDR_SPACE 4000U +#define GREE_BIT_MARK 620U +#define GREE_ONE_SPACE 1600U +#define GREE_ZERO_SPACE 540U +#define GREE_MSG_SPACE 19000U +#define GREE_BLOCK_FOOTER 0b010U +#define GREE_BLOCK_FOOTER_BITS 3U #if SEND_GREE // Send a Gree Heat Pump message. @@ -38,36 +51,26 @@ void IRsend::sendGree(unsigned char data[], uint16_t nbytes, uint16_t repeat) { if (nbytes < GREE_STATE_LENGTH) return; // Not enough bytes to send a proper message. - // Set IR carrier frequency - enableIROut(38); - for (uint16_t r = 0; r <= repeat; r++) { - // Header #1 - mark(GREE_HDR_MARK); - space(GREE_HDR_SPACE); + // Block #1 + sendGeneric(GREE_HDR_MARK, GREE_HDR_SPACE, + GREE_BIT_MARK, GREE_ONE_SPACE, + GREE_BIT_MARK, GREE_ZERO_SPACE, + 0, 0, // No Footer. + data, 4, 38, false, 0, 50); + // Footer #1 + sendGeneric(0, 0, // No Header + GREE_BIT_MARK, GREE_ONE_SPACE, + GREE_BIT_MARK, GREE_ZERO_SPACE, + GREE_BIT_MARK, GREE_MSG_SPACE, + 0b010, 3, 38, true, 0, false); - // Data #1 - uint16_t i; - for (i = 0; i < 4 && i < nbytes; i++) - sendData(GREE_BIT_MARK, GREE_ONE_SPACE, GREE_BIT_MARK, GREE_ZERO_SPACE, - data[i], 8, false); - - // Footer #1 (010) - sendData(GREE_BIT_MARK, GREE_ONE_SPACE, GREE_BIT_MARK, GREE_ZERO_SPACE, - 0b010, 3); - - // Header #2 - mark(GREE_BIT_MARK); - space(GREE_MSG_SPACE); - - // Data #2 - for (; i < nbytes; i++) - sendData(GREE_BIT_MARK, GREE_ONE_SPACE, GREE_BIT_MARK, GREE_ZERO_SPACE, - data[i], 8, false); - - // Footer #2 - mark(GREE_BIT_MARK); - space(GREE_MSG_SPACE); + // Block #2 + sendGeneric(0, 0, // No Header for Block #2 + GREE_BIT_MARK, GREE_ONE_SPACE, + GREE_BIT_MARK, GREE_ZERO_SPACE, + GREE_BIT_MARK, GREE_MSG_SPACE, + data + 4, nbytes - 4, 38, false, 0, 50); } } @@ -111,3 +114,385 @@ void IRsend::sendGree(uint64_t data, uint16_t nbits, uint16_t repeat) { } } #endif // SEND_GREE + +IRGreeAC::IRGreeAC(uint16_t pin) : _irsend(pin) { + stateReset(); +} + +void IRGreeAC::stateReset() { + // This resets to a known-good state to Power Off, Fan Auto, Mode Auto, 25C. + for (uint8_t i = 0; i < GREE_STATE_LENGTH; i++) + remote_state[i] = 0x0; + remote_state[1] = 0x09; + remote_state[2] = 0x20; + remote_state[3] = 0x50; + remote_state[5] = 0x20; + remote_state[7] = 0x50; +} + +void IRGreeAC::fixup() { + checksum(); // Calculate the checksums +} + +void IRGreeAC::begin() { + _irsend.begin(); +} + +#if SEND_GREE +void IRGreeAC::send() { + fixup(); // Ensure correct settings before sending. + _irsend.sendGree(remote_state); +} +#endif // SEND_GREE + +uint8_t* IRGreeAC::getRaw() { + fixup(); // Ensure correct settings before sending. + return remote_state; +} + +void IRGreeAC::setRaw(uint8_t new_code[]) { + for (uint8_t i = 0; i < GREE_STATE_LENGTH; i++) { + remote_state[i] = new_code[i]; + } +} + +void IRGreeAC::checksum(const uint16_t length) { + // Gree uses the same checksum alg. as Kelvinator's block checksum. + uint8_t sum = IRKelvinatorAC::calcBlockChecksum(remote_state, length); + remote_state[length - 1] = (sum << 4) | (remote_state[length - 1] & 0xFU); +} + +// 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 IRGreeAC::validChecksum(const uint8_t state[], const uint16_t length) { + // Top 4 bits of the last byte in the state is the state's checksum. + if (state[length - 1] >> 4 == IRKelvinatorAC::calcBlockChecksum(state, + length)) + return true; + else + return false; +} + +void IRGreeAC::on() { + remote_state[0] |= GREE_POWER1_MASK; + remote_state[2] |= GREE_POWER2_MASK; +} + +void IRGreeAC::off() { + remote_state[0] &= ~GREE_POWER1_MASK; + remote_state[2] &= ~GREE_POWER2_MASK; +} + +void IRGreeAC::setPower(const bool state) { + if (state) + on(); + else + off(); +} + +bool IRGreeAC::getPower() { + return (remote_state[0] & GREE_POWER1_MASK) && + (remote_state[2] & GREE_POWER2_MASK); +} + +// Set the temp. in deg C +void IRGreeAC::setTemp(const uint8_t temp) { + uint8_t new_temp = std::max((uint8_t) GREE_MIN_TEMP, temp); + new_temp = std::min((uint8_t) GREE_MAX_TEMP, new_temp); + if (getMode() == GREE_AUTO) new_temp = 25; + remote_state[1] = (remote_state[1] & 0xF0U) | (new_temp - GREE_MIN_TEMP); +} + +// Return the set temp. in deg C +uint8_t IRGreeAC::getTemp() { + return ((remote_state[1] & 0xFU) + GREE_MIN_TEMP); +} + +// Set the speed of the fan, 0-3, 0 is auto, 1-3 is the speed +void IRGreeAC::setFan(const uint8_t speed) { + uint8_t fan = std::min((uint8_t) GREE_FAN_MAX, speed); // Bounds check + + if (getMode() == GREE_DRY) fan = 1; // DRY mode is always locked to fan 1. + // Set the basic fan values. + remote_state[0] &= ~GREE_FAN_MASK; + remote_state[0] |= (fan << 4); +} + +uint8_t IRGreeAC::getFan() { + return ((remote_state[0] & GREE_FAN_MASK) >> 4); +} + +void IRGreeAC::setMode(const uint8_t new_mode) { + uint8_t mode = new_mode; + switch (mode) { + case GREE_AUTO: + // AUTO is locked to 25C + setTemp(25); + break; + case GREE_DRY: + // DRY always sets the fan to 1. + setFan(1); + break; + case GREE_COOL: + case GREE_FAN: + case GREE_HEAT: + break; + default: + // If we get an unexpected mode, default to AUTO. + mode = GREE_AUTO; + } + remote_state[0] &= ~GREE_MODE_MASK; + remote_state[0] |= mode; +} + +uint8_t IRGreeAC::getMode() { + return (remote_state[0] & GREE_MODE_MASK); +} + +void IRGreeAC::setLight(const bool state) { + remote_state[2] &= ~GREE_LIGHT_MASK; + remote_state[2] |= (state << 5); +} + +bool IRGreeAC::getLight() { + return remote_state[2] & GREE_LIGHT_MASK; +} + +void IRGreeAC::setXFan(const bool state) { + remote_state[2] &= ~GREE_XFAN_MASK; + remote_state[2] |= (state << 7); +} + +bool IRGreeAC::getXFan() { + return remote_state[2] & GREE_XFAN_MASK; +} + +void IRGreeAC::setSleep(const bool state) { + remote_state[0] &= ~GREE_SLEEP_MASK; + remote_state[0] |= (state << 7); +} + +bool IRGreeAC::getSleep() { + return remote_state[0] & GREE_SLEEP_MASK; +} + +void IRGreeAC::setTurbo(const bool state) { + remote_state[2] &= ~GREE_TURBO_MASK; + remote_state[2] |= (state << 4); +} + +bool IRGreeAC::getTurbo() { + return remote_state[2] & GREE_TURBO_MASK; +} + +void IRGreeAC::setSwingVertical(const bool automatic, const uint8_t position) { + remote_state[0] &= ~GREE_SWING_AUTO_MASK; + remote_state[0] |= (automatic << 6); + uint8_t new_position = position; + if (!automatic) { + switch (position) { + case GREE_SWING_UP: + case GREE_SWING_MIDDLE_UP: + case GREE_SWING_MIDDLE: + case GREE_SWING_MIDDLE_DOWN: + case GREE_SWING_DOWN: + break; + default: + new_position = GREE_SWING_LAST_POS; + } + } else { + switch (position) { + case GREE_SWING_AUTO: + case GREE_SWING_DOWN_AUTO: + case GREE_SWING_MIDDLE_AUTO: + case GREE_SWING_UP_AUTO: + break; + default: + new_position = GREE_SWING_AUTO; + } + } + remote_state[4] &= ~GREE_SWING_POS_MASK; + remote_state[4] |= new_position; +} + +bool IRGreeAC::getSwingVerticalAuto() { + return remote_state[0] & GREE_SWING_AUTO_MASK; +} + +uint8_t IRGreeAC::getSwingVerticalPosition() { + return remote_state[4] & GREE_SWING_POS_MASK; +} + +// Convert the internal state into a human readable string. +#ifdef ARDUINO +String IRGreeAC::toString() { + String result = ""; +#else +std::string IRGreeAC::toString() { + std::string result = ""; +#endif // ARDUINO + result += "Power: "; + if (getPower()) + result += "On"; + else + result += "Off"; + result += ", Mode: " + uint64ToString(getMode()); + switch (getMode()) { + case GREE_AUTO: + result += " (AUTO)"; + break; + case GREE_COOL: + result += " (COOL)"; + break; + case GREE_HEAT: + result += " (HEAT)"; + break; + case GREE_DRY: + result += " (DRY)"; + break; + case GREE_FAN: + result += " (FAN)"; + break; + default: + result += " (UNKNOWN)"; + } + result += ", Temp: " + uint64ToString(getTemp()) + "C"; + result += ", Fan: " + uint64ToString(getFan()); + switch (getFan()) { + case 0: + result += " (AUTO)"; + break; + case GREE_FAN_MAX: + result += " (MAX)"; + break; + } + result += ", Turbo: "; + if (getTurbo()) + result += "On"; + else + result += "Off"; + result += ", XFan: "; + if (getXFan()) + result += "On"; + else + result += "Off"; + result += ", Light: "; + if (getLight()) + result += "On"; + else + result += "Off"; + result += ", Sleep: "; + if (getSleep()) + result += "On"; + else + result += "Off"; + result += ", Swing Vertical Mode: "; + if (getSwingVerticalAuto()) + result += "Auto"; + else + result += "Manual"; + result += ", Swing Vertical Pos: " + + uint64ToString(getSwingVerticalPosition()); + switch (getSwingVerticalPosition()) { + case GREE_SWING_LAST_POS: + result += " (Last Pos)"; + break; + case GREE_SWING_AUTO: + result += " (Auto)"; + break; + } + return result; +} + +#if DECODE_GREE +// Decode the supplied Gree 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 GREE_BITS. +// 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::decodeGree(decode_results *results, uint16_t nbits, bool strict) { + if (results->rawlen < 2 * (nbits + GREE_BLOCK_FOOTER_BITS) + + (HEADER + FOOTER + 1)) + return false; // Can't possibly be a valid Gree message. + if (strict && nbits != GREE_BITS) + return false; // Not strictly a Gree message. + + uint32_t data; + uint16_t offset = OFFSET_START; + + // There are two blocks back-to-back in a full Gree IR message + // sequence. + int8_t state_pos = 0; + match_result_t data_result; + + // Header + if (!matchMark(results->rawbuf[offset++], GREE_HDR_MARK)) return false; + if (!matchSpace(results->rawbuf[offset++], GREE_HDR_SPACE)) return false; + // Data Block #1 (32 bits) + data_result = matchData(&(results->rawbuf[offset]), 32, GREE_BIT_MARK, + GREE_ONE_SPACE, GREE_BIT_MARK, GREE_ZERO_SPACE); + if (data_result.success == false) return false; + data = data_result.data; + offset += data_result.used; + + // Record Data Block #1 in the state. + for (int i = state_pos + 3; i >= state_pos; i--, data >>= 8) + results->state[i] = reverseBits(data & 0xFF, 8); + state_pos += 4; + + // Block #1 footer (3 bits, B010) + data_result = matchData(&(results->rawbuf[offset]), GREE_BLOCK_FOOTER_BITS, + GREE_BIT_MARK, GREE_ONE_SPACE, GREE_BIT_MARK, + GREE_ZERO_SPACE); + if (data_result.success == false) return false; + if (data_result.data != GREE_BLOCK_FOOTER) return false; + offset += data_result.used; + + // Inter-block gap. + if (!matchMark(results->rawbuf[offset++], GREE_BIT_MARK)) return false; + if (!matchSpace(results->rawbuf[offset++], GREE_MSG_SPACE)) return false; + + // Data Block #2 (32 bits) + data_result = matchData(&(results->rawbuf[offset]), 32, GREE_BIT_MARK, + GREE_ONE_SPACE, GREE_BIT_MARK, GREE_ZERO_SPACE); + if (data_result.success == false) return false; + data = data_result.data; + offset += data_result.used; + + // Record Data Block #2 in the state. + for (int i = state_pos + 3; i >= state_pos; i--, data >>= 8) + results->state[i] = reverseBits(data & 0xFF, 8); + state_pos += 4; + + // Footer. + if (!matchMark(results->rawbuf[offset++], GREE_BIT_MARK)) return false; + if (offset <= results->rawlen && + !matchAtLeast(results->rawbuf[offset], GREE_MSG_SPACE)) + return false; + + // Compliance + if (strict) { + // Correct size/length) + if (state_pos != GREE_STATE_LENGTH) return false; + // Verify the message's checksum is correct. + if (!IRGreeAC::validChecksum(results->state)) return false; + } + + // Success + results->decode_type = GREE; + results->bits = state_pos * 8; + // No need to record the state as we stored it as we decoded it. + // As we use result->state, we don't record value, address, or command as it + // is a union data type. + return true; +} +#endif // DECODE_GREE diff --git a/lib/IRremoteESP8266/src/ir_Gree.h b/lib/IRremoteESP8266/src/ir_Gree.h new file mode 100644 index 0000000..e44c97b --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_Gree.h @@ -0,0 +1,106 @@ +// Kelvinator A/C +// +// Copyright 2016 David Conran + +#ifndef IR_GREE_H_ +#define IR_GREE_H_ + +#define __STDC_LIMIT_MACROS +#include +#ifndef UNIT_TEST +#include +#else +#include +#endif +#include "IRremoteESP8266.h" +#include "IRsend.h" + +// GGGG RRRRRR EEEEEEE EEEEEEE +// GG GG RR RR EE EE +// GG RRRRRR EEEEE EEEEE +// GG GG RR RR EE EE +// GGGGGG RR RR EEEEEEE EEEEEEE + +// Constants +#define GREE_AUTO 0U +#define GREE_COOL 1U +#define GREE_DRY 2U +#define GREE_FAN 3U +#define GREE_HEAT 4U + +#define GREE_POWER1_MASK 0b00001000U +#define GREE_POWER2_MASK 0b01000000U +#define GREE_MIN_TEMP 16U // Celsius +#define GREE_MAX_TEMP 30U // Celsius +#define GREE_FAN_MAX 3U +#define GREE_FAN_MASK 0b00110000U +#define GREE_MODE_MASK 0b00000111U +#define GREE_TURBO_MASK 0b00010000U +#define GREE_LIGHT_MASK 0b00100000U +#define GREE_XFAN_MASK 0b10000000U +#define GREE_SLEEP_MASK 0b10000000U + +#define GREE_SWING_AUTO_MASK 0b01000000U +#define GREE_SWING_POS_MASK 0b00001111U +#define GREE_SWING_LAST_POS 0b00000000U +#define GREE_SWING_AUTO 0b00000001U +#define GREE_SWING_UP 0b00000010U +#define GREE_SWING_MIDDLE_UP 0b00000011U +#define GREE_SWING_MIDDLE 0b00000100U +#define GREE_SWING_MIDDLE_DOWN 0b00000101U +#define GREE_SWING_DOWN 0b00000110U +#define GREE_SWING_DOWN_AUTO 0b00000111U +#define GREE_SWING_MIDDLE_AUTO 0b00001001U +#define GREE_SWING_UP_AUTO 0b00001011U + +// Classes +class IRGreeAC { + public: + explicit IRGreeAC(uint16_t pin); + + void stateReset(); +#if SEND_GREE + void send(); +#endif // SEND_GREE + 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 speed); + uint8_t getFan(); + void setMode(const uint8_t new_mode); + uint8_t getMode(); + void setLight(const bool state); + bool getLight(); + void setXFan(const bool state); + bool getXFan(); + void setSleep(const bool state); + bool getSleep(); + void setTurbo(const bool state); + bool getTurbo(); + void setSwingVertical(const bool automatic, const uint8_t position); + bool getSwingVerticalAuto(); + uint8_t getSwingVerticalPosition(); + + uint8_t* getRaw(); + void setRaw(uint8_t new_code[]); + static bool validChecksum(const uint8_t state[], + const uint16_t length = GREE_STATE_LENGTH); +#ifdef ARDUINO + String toString(); +#else + std::string toString(); +#endif + + private: + // The state of the IR remote in IR code form. + uint8_t remote_state[GREE_STATE_LENGTH]; + void checksum(const uint16_t length = GREE_STATE_LENGTH); + void fixup(); + IRsend _irsend; +}; + +#endif // IR_GREE_H_ diff --git a/lib/IRremoteESP8266/src/ir_Haier.cpp b/lib/IRremoteESP8266/src/ir_Haier.cpp new file mode 100644 index 0000000..ae5a44a --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_Haier.cpp @@ -0,0 +1,497 @@ +// Copyright 2018 crankyoldgit +// The specifics of reverse engineering the protocol details by kuzin2006 + +#include "ir_Haier.h" +#ifndef UNIT_TEST +#include +#else +#include +#endif +#include "IRremoteESP8266.h" +#include "IRutils.h" + +// HH HH AAA IIIII EEEEEEE RRRRRR +// HH HH AAAAA III EE RR RR +// HHHHHHH AA AA III EEEEE RRRRRR +// HH HH AAAAAAA III EE RR RR +// HH HH AA AA IIIII EEEEEEE RR RR + +// Supported devices: +// * Haier HSU07-HEA03 Remote control. + +// Ref: +// https://github.com/markszabo/IRremoteESP8266/issues/404 +// https://www.dropbox.com/s/mecyib3lhdxc8c6/IR%20data%20reverse%20engineering.xlsx?dl=0 + +// Constants +#define HAIER_AC_HDR 3000U +#define HAIER_AC_HDR_GAP 4300U + +#define HAIER_AC_BIT_MARK 520U +#define HAIER_AC_ONE_SPACE 1650U +#define HAIER_AC_ZERO_SPACE 650U +#define HAIER_AC_MIN_GAP 150000U // Completely made up value. + +#if SEND_HAIER_AC +// Send a Haier A/C message. +// +// Args: +// data: An array of bytes containing the IR command. +// nbytes: Nr. of bytes of data in the array. (>=HAIER_AC_STATE_LENGTH) +// repeat: Nr. of times the message is to be repeated. (Default = 0). +// +// Status: Beta / Probably working. +// +void IRsend::sendHaierAC(unsigned char data[], uint16_t nbytes, + uint16_t repeat) { + if (nbytes < HAIER_AC_STATE_LENGTH) + return; + + for (uint16_t r = 0; r <= repeat; r++) { + enableIROut(38000); + mark(HAIER_AC_HDR); + space(HAIER_AC_HDR); + sendGeneric(HAIER_AC_HDR, HAIER_AC_HDR_GAP, + HAIER_AC_BIT_MARK, HAIER_AC_ONE_SPACE, + HAIER_AC_BIT_MARK, HAIER_AC_ZERO_SPACE, + HAIER_AC_BIT_MARK, HAIER_AC_MIN_GAP, + data, nbytes, 38, true, 0, // Repeats handled elsewhere + 50); + } +} +#endif // SEND_HAIER_AC + +IRHaierAC::IRHaierAC(uint16_t pin) : _irsend(pin) { + stateReset(); +} + +void IRHaierAC::begin() { + _irsend.begin(); +} + +#if SEND_HAIER_AC +void IRHaierAC::send() { + checksum(); + _irsend.sendHaierAC(remote_state); +} +#endif // SEND_HAIER_AC + +void IRHaierAC::checksum() { + remote_state[8] = sumBytes(remote_state, HAIER_AC_STATE_LENGTH - 1); +} + +bool IRHaierAC::validChecksum(uint8_t state[], const uint16_t length) { + if (length < 2) return false; // 1 byte of data can't have a checksum. + return (state[length - 1] == sumBytes(state, length - 1)); +} + +void IRHaierAC::stateReset() { + for (uint8_t i = 1; i < HAIER_AC_STATE_LENGTH; i++) + remote_state[i] = 0x0; + remote_state[0] = HAIER_AC_PREFIX; + remote_state[2] = 0b00100000; + + setTemp(HAIER_AC_DEF_TEMP); + setFan(HAIER_AC_FAN_AUTO); + setMode(HAIER_AC_AUTO); + setCommand(HAIER_AC_CMD_ON); +} + +uint8_t* IRHaierAC::getRaw() { + checksum(); + return remote_state; +} + +void IRHaierAC::setRaw(uint8_t new_code[]) { + for (uint8_t i = 0; i < HAIER_AC_STATE_LENGTH; i++) { + remote_state[i] = new_code[i]; + } +} + +void IRHaierAC::setCommand(uint8_t state) { + remote_state[1] &= 0b11110000; + switch (state) { + case HAIER_AC_CMD_OFF: + case HAIER_AC_CMD_ON: + case HAIER_AC_CMD_MODE: + case HAIER_AC_CMD_FAN: + case HAIER_AC_CMD_TEMP_UP: + case HAIER_AC_CMD_TEMP_DOWN: + case HAIER_AC_CMD_SLEEP: + case HAIER_AC_CMD_TIMER_SET: + case HAIER_AC_CMD_TIMER_CANCEL: + case HAIER_AC_CMD_HEALTH: + case HAIER_AC_CMD_SWING: + remote_state[1] |= (state & 0b00001111); + } +} + +uint8_t IRHaierAC::getCommand() { + return remote_state[1] & (0b00001111); +} + +void IRHaierAC::setFan(uint8_t speed) { + uint8_t new_speed = HAIER_AC_FAN_AUTO; + switch (speed) { + case HAIER_AC_FAN_LOW: + new_speed = 3; + break; + case HAIER_AC_FAN_MED: + new_speed = 1; + break; + case HAIER_AC_FAN_HIGH: + new_speed = 2; + break; + default: + new_speed = HAIER_AC_FAN_AUTO; // Default to auto for anything else. + } + + if (speed != getFan()) setCommand(HAIER_AC_CMD_FAN); + remote_state[5] &= 0b11111100; + remote_state[5] |= new_speed; +} + +uint8_t IRHaierAC::getFan() { + switch (remote_state[5] & 0b00000011) { + case 1: + return HAIER_AC_FAN_MED; + case 2: + return HAIER_AC_FAN_HIGH; + case 3: + return HAIER_AC_FAN_LOW; + default: + return HAIER_AC_FAN_AUTO; + } +} + +void IRHaierAC::setMode(uint8_t mode) { + uint8_t new_mode = mode; + setCommand(HAIER_AC_CMD_MODE); + if (mode > HAIER_AC_FAN) // If out of range, default to auto mode. + new_mode = HAIER_AC_AUTO; + remote_state[7] &= 0b00011111; + remote_state[7] |= (new_mode << 5); +} + +uint8_t IRHaierAC::getMode() { + return (remote_state[7] & 0b11100000) >> 5; +} + +void IRHaierAC::setTemp(const uint8_t celcius ) { + uint8_t temp = celcius; + if (temp < HAIER_AC_MIN_TEMP) + temp = HAIER_AC_MIN_TEMP; + else if (temp > HAIER_AC_MAX_TEMP) + temp = HAIER_AC_MAX_TEMP; + + uint8_t old_temp = getTemp(); + if (old_temp == temp) return; + if (old_temp > temp) + setCommand(HAIER_AC_CMD_TEMP_DOWN); + else + setCommand(HAIER_AC_CMD_TEMP_UP); + + remote_state[1] &= 0b00001111; // Clear the previous temp. + remote_state[1] |= ((temp - HAIER_AC_MIN_TEMP) << 4); +} + +uint8_t IRHaierAC::getTemp() { + return ((remote_state[1] & 0b11110000) >> 4) + HAIER_AC_MIN_TEMP; +} + +void IRHaierAC::setHealth(bool state) { + setCommand(HAIER_AC_CMD_HEALTH); + remote_state[4] &= 0b11011111; + remote_state[4] |= (state << 5); +} + +bool IRHaierAC::getHealth(void) { + return remote_state[4] & (1 << 5); +} + +void IRHaierAC::setSleep(bool state) { + setCommand(HAIER_AC_CMD_SLEEP); + remote_state[7] &= 0b10111111; + remote_state[7] |= (state << 6); +} + +bool IRHaierAC::getSleep(void) { + return remote_state[7] & 0b01000000; +} + +uint16_t IRHaierAC::getTime(const uint8_t ptr[]) { + return (ptr[0] & 0b00011111) * 60 + (ptr[1] & 0b00111111); +} + +int16_t IRHaierAC::getOnTimer() { + if (remote_state[3] & 0b10000000) // Check if the timer is turned on. + return getTime(remote_state + 6); + else + return -1; +} + +int16_t IRHaierAC::getOffTimer() { + if (remote_state[3] & 0b01000000) // Check if the timer is turned on. + return getTime(remote_state + 4); + else + return -1; +} + +uint16_t IRHaierAC::getCurrTime() { + return getTime(remote_state + 2); +} + +void IRHaierAC::setTime(uint8_t ptr[], const uint16_t nr_mins) { + uint16_t mins = nr_mins; + if (nr_mins > HAIER_AC_MAX_TIME) + mins = HAIER_AC_MAX_TIME; + + // Hours + ptr[0] &= 0b11100000; + ptr[0] |= (mins / 60); + // Minutes + ptr[1] &= 0b11000000; + ptr[1] |= (mins % 60); +} + +void IRHaierAC::setOnTimer(const uint16_t nr_mins) { + setCommand(HAIER_AC_CMD_TIMER_SET); + remote_state[3] |= 0b10000000; + setTime(remote_state + 6, nr_mins); +} + +void IRHaierAC::setOffTimer(const uint16_t nr_mins) { + setCommand(HAIER_AC_CMD_TIMER_SET); + remote_state[3] |= 0b01000000; + setTime(remote_state + 4, nr_mins); +} + +void IRHaierAC::cancelTimers() { + setCommand(HAIER_AC_CMD_TIMER_CANCEL); + remote_state[3] &= 0b00111111; +} + +void IRHaierAC::setCurrTime(const uint16_t nr_mins) { + setTime(remote_state + 2, nr_mins); +} + +uint8_t IRHaierAC::getSwing() { + return (remote_state[2] & 0b11000000) >> 6; +} + +void IRHaierAC::setSwing(const uint8_t state) { + if (state == getSwing()) return; // Nothing to do. + setCommand(HAIER_AC_CMD_SWING); + switch (state) { + case HAIER_AC_SWING_OFF: + case HAIER_AC_SWING_UP: + case HAIER_AC_SWING_DOWN: + case HAIER_AC_SWING_CHG: + remote_state[2] &= 0b00111111; + remote_state[2] |= (state << 6); + break; + } +} + +// Convert a Haier time into a human readable string. +#ifdef ARDUINO +String IRHaierAC::timeToString(const uint16_t nr_mins) { + String result = ""; +#else +std::string IRHaierAC::timeToString(const uint16_t nr_mins) { + std::string result = ""; +#endif // ARDUINO + + 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 += uint64ToString(nr_mins % 60); + return result; +} + +// Convert the internal state into a human readable string. +#ifdef ARDUINO +String IRHaierAC::toString() { + String result = ""; +#else +std::string IRHaierAC::toString() { + std::string result = ""; +#endif // ARDUINO + uint8_t cmd = getCommand(); + result += "Command: " + uint64ToString(cmd) +" ("; + switch (cmd) { + case HAIER_AC_CMD_OFF: + result += "Off"; + break; + case HAIER_AC_CMD_ON: + result += "On"; + break; + case HAIER_AC_CMD_MODE: + result += "Mode"; + break; + case HAIER_AC_CMD_FAN: + result += "Fan"; + break; + case HAIER_AC_CMD_TEMP_UP: + result += "Temp Up"; + break; + case HAIER_AC_CMD_TEMP_DOWN: + result += "Temp Down"; + break; + case HAIER_AC_CMD_SLEEP: + result += "Sleep"; + break; + case HAIER_AC_CMD_TIMER_SET: + result += "Timer Set"; + break; + case HAIER_AC_CMD_TIMER_CANCEL: + result += "Timer Cancel"; + break; + case HAIER_AC_CMD_HEALTH: + result += "Health"; + break; + case HAIER_AC_CMD_SWING: + result += "Swing"; + break; + default: + result += "Unknown"; + } + result += ")"; + result += ", Mode: " + uint64ToString(getMode()); + switch (getMode()) { + case HAIER_AC_AUTO: + result += " (AUTO)"; + break; + case HAIER_AC_COOL: + result += " (COOL)"; + break; + case HAIER_AC_HEAT: + result += " (HEAT)"; + break; + case HAIER_AC_DRY: + result += " (DRY)"; + break; + case HAIER_AC_FAN: + result += " (FAN)"; + break; + default: + result += " (UNKNOWN)"; + } + result += ", Temp: " + uint64ToString(getTemp()) + "C"; + result += ", Fan: " + uint64ToString(getFan()); + switch (getFan()) { + case HAIER_AC_FAN_AUTO: + result += " (AUTO)"; + break; + case HAIER_AC_FAN_HIGH: + result += " (MAX)"; + break; + } + result += ", Swing: " + uint64ToString(getSwing()) + " ("; + switch (getSwing()) { + case HAIER_AC_SWING_OFF: + result += "Off"; + break; + case HAIER_AC_SWING_UP: + result += "Up"; + break; + case HAIER_AC_SWING_DOWN: + result += "Down"; + break; + case HAIER_AC_SWING_CHG: + result += "Chg"; + break; + default: + result += "Unknown"; + } + result += ")"; + result += ", Sleep: "; + if (getSleep()) + result += "On"; + else + result += "Off"; + result += ", Health: "; + if (getHealth()) + result += "On"; + else + result += "Off"; + result += ", Current Time: " + timeToString(getCurrTime()); + result += ", On Timer: "; + if (getOnTimer() >= 0) + result += timeToString(getOnTimer()); + else + result += "Off"; + result += ", Off Timer: "; + if (getOffTimer() >= 0) + result += timeToString(getOffTimer()); + else + result += "Off"; + + return result; +} + +#if DECODE_HAIER_AC +// Decode the supplied Haier 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 HAIER_AC_BITS. +// 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. +// +bool IRrecv::decodeHaierAC(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 != HAIER_AC_BITS) + return false; // Not strictly a HAIER_AC message. + } + + if (results->rawlen < (2 * nbits + HEADER) + FOOTER - 1) + return false; // Can't possibly be a valid HAIER_AC message. + + uint16_t offset = OFFSET_START; + + + // Header + if (!matchMark(results->rawbuf[offset++], HAIER_AC_HDR)) return false; + if (!matchSpace(results->rawbuf[offset++], HAIER_AC_HDR)) return false; + if (!matchMark(results->rawbuf[offset++], HAIER_AC_HDR)) return false; + if (!matchSpace(results->rawbuf[offset++], HAIER_AC_HDR_GAP)) return false; + + // Data + for (uint16_t i = 0; i < nbits / 8; i++) { + match_result_t data_result = matchData(&(results->rawbuf[offset]), 8, + HAIER_AC_BIT_MARK, + HAIER_AC_ONE_SPACE, + HAIER_AC_BIT_MARK, + HAIER_AC_ZERO_SPACE); + if (data_result.success == false) return false; + offset += data_result.used; + results->state[i] = (uint8_t) data_result.data; + } + + // Footer + if (!matchMark(results->rawbuf[offset++], HAIER_AC_BIT_MARK)) return false; + if (offset < results->rawlen && + !matchAtLeast(results->rawbuf[offset++], HAIER_AC_MIN_GAP)) + return false; + + // Compliance + if (strict) { + if (results->state[0] != HAIER_AC_PREFIX) return false; + if (!IRHaierAC::validChecksum(results->state, nbits / 8)) return false; + } + + // Success + results->decode_type = HAIER_AC; + results->bits = nbits; + return true; +} +#endif // DECODE_HAIER_AC diff --git a/lib/IRremoteESP8266/src/ir_Haier.h b/lib/IRremoteESP8266/src/ir_Haier.h new file mode 100644 index 0000000..ac0f022 --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_Haier.h @@ -0,0 +1,126 @@ +// Copyright 2018 crankyoldgit +// The specifics of reverse engineering the protocol details by kuzin2006 + +#ifndef IR_HAIER_H_ +#define IR_HAIER_H_ + +#ifndef UNIT_TEST +#include +#else +#include +#endif +#include "IRremoteESP8266.h" +#include "IRsend.h" + +// HH HH AAA IIIII EEEEEEE RRRRRR +// HH HH AAAAA III EE RR RR +// HHHHHHH AA AA III EEEEE RRRRRR +// HH HH AAAAAAA III EE RR RR +// HH HH AA AA IIIII EEEEEEE RR RR + +// Ref: +// https://github.com/markszabo/IRremoteESP8266/issues/404 +// https://www.dropbox.com/s/mecyib3lhdxc8c6/IR%20data%20reverse%20engineering.xlsx?dl=0 + +// Constants +// Byte 0 +#define HAIER_AC_PREFIX 0b10100101 + +// Byte 1 +#define HAIER_AC_MIN_TEMP 16 +#define HAIER_AC_MAX_TEMP 30 +#define HAIER_AC_DEF_TEMP 25 + +#define HAIER_AC_CMD_OFF 0b00000000 +#define HAIER_AC_CMD_ON 0b00000001 +#define HAIER_AC_CMD_MODE 0b00000010 +#define HAIER_AC_CMD_FAN 0b00000011 +#define HAIER_AC_CMD_TEMP_UP 0b00000110 +#define HAIER_AC_CMD_TEMP_DOWN 0b00000111 +#define HAIER_AC_CMD_SLEEP 0b00001000 +#define HAIER_AC_CMD_TIMER_SET 0b00001001 +#define HAIER_AC_CMD_TIMER_CANCEL 0b00001010 +#define HAIER_AC_CMD_HEALTH 0b00001100 +#define HAIER_AC_CMD_SWING 0b00001101 + +// Byte 2 +#define HAIER_AC_SWING_OFF 0b00000000 +#define HAIER_AC_SWING_UP 0b00000001 +#define HAIER_AC_SWING_DOWN 0b00000010 +#define HAIER_AC_SWING_CHG 0b00000011 + +// Byte 6 +#define HAIER_AC_AUTO 0 +#define HAIER_AC_COOL 1 +#define HAIER_AC_DRY 2 +#define HAIER_AC_HEAT 3 +#define HAIER_AC_FAN 4 + +#define HAIER_AC_FAN_AUTO 0 +#define HAIER_AC_FAN_LOW 1 +#define HAIER_AC_FAN_MED 2 +#define HAIER_AC_FAN_HIGH 3 + +#define HAIER_AC_MAX_TIME (23 * 60 + 59) + + +class IRHaierAC { + public: + explicit IRHaierAC(uint16_t pin); + +#if SEND_HAIER_AC + void send(); +#endif // SEND_HAIER_AC + void begin(); + + void setCommand(const uint8_t command); + uint8_t getCommand(); + + void setTemp(const uint8_t temp); + uint8_t getTemp(); + + void setFan(const uint8_t speed); + uint8_t getFan(); + + uint8_t getMode(); + void setMode(const uint8_t mode); + + bool getSleep(); + void setSleep(const bool state); + bool getHealth(); + void setHealth(const bool state); + + int16_t getOnTimer(); + void setOnTimer(const uint16_t mins); + int16_t getOffTimer(); + void setOffTimer(const uint16_t mins); + void cancelTimers(); + + uint16_t getCurrTime(); + void setCurrTime(const uint16_t mins); + + uint8_t getSwing(); + void setSwing(const uint8_t state); + + uint8_t* getRaw(); + void setRaw(uint8_t new_code[]); + static bool validChecksum(uint8_t state[], + const uint16_t length = HAIER_AC_STATE_LENGTH); + #ifdef ARDUINO + String toString(); + static String timeToString(const uint16_t nr_mins); + #else + std::string toString(); + static std::string timeToString(const uint16_t nr_mins); + #endif + + private: + uint8_t remote_state[HAIER_AC_STATE_LENGTH]; + 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; +}; + +#endif // IR_HAIER_H_ diff --git a/lib/IRremoteESP8266/src/ir_Hitachi.cpp b/lib/IRremoteESP8266/src/ir_Hitachi.cpp new file mode 100644 index 0000000..f7bd849 --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_Hitachi.cpp @@ -0,0 +1,215 @@ +// Copyright 2018 David Conran +// +// Code to emulate Hitachi protocol compatible devices. +// Should be compatible with: +// * Hitachi RAS-35THA6 remote +// + +#include +#ifndef ARDUINO +#include +#endif +#include "IRremoteESP8266.h" +#include "IRrecv.h" +#include "IRsend.h" +#include "IRutils.h" + +// HH HH IIIII TTTTTTT AAA CCCCC HH HH IIIII +// HH HH III TTT AAAAA CC C HH HH III +// HHHHHHH III TTT AA AA CC HHHHHHH III +// HH HH III TTT AAAAAAA CC C HH HH III +// HH HH IIIII TTT AA AA CCCCC HH HH IIIII + +// Constants +// Ref: https://github.com/markszabo/IRremoteESP8266/issues/417 +#define HITACHI_AC_HDR_MARK 3300U +#define HITACHI_AC_HDR_SPACE 1700U +#define HITACHI_AC1_HDR_MARK 3400U +#define HITACHI_AC1_HDR_SPACE 3400U +#define HITACHI_AC_BIT_MARK 400U +#define HITACHI_AC_ONE_SPACE 1250U +#define HITACHI_AC_ZERO_SPACE 500U +#define HITACHI_AC_MIN_GAP 100000U // Completely made up value. + +#if (SEND_HITACHI_AC || SEND_HITACHI_AC2) +// Send a Hitachi A/C message. +// +// Args: +// data: An array of bytes containing the IR command. +// nbytes: Nr. of bytes of data in the array. (>=HITACHI_AC_STATE_LENGTH) +// repeat: Nr. of times the message is to be repeated. (Default = 0). +// +// Status: ALPHA / Untested. +// +// Ref: +// https://github.com/markszabo/IRremoteESP8266/issues/417 +void IRsend::sendHitachiAC(unsigned char data[], uint16_t nbytes, + uint16_t repeat) { + if (nbytes < HITACHI_AC_STATE_LENGTH) + return; // Not enough bytes to send a proper message. + sendGeneric(HITACHI_AC_HDR_MARK, HITACHI_AC_HDR_SPACE, + HITACHI_AC_BIT_MARK, HITACHI_AC_ONE_SPACE, + HITACHI_AC_BIT_MARK, HITACHI_AC_ZERO_SPACE, + HITACHI_AC_BIT_MARK, HITACHI_AC_MIN_GAP, + data, nbytes, 38, true, repeat, 50); +} +#endif // (SEND_HITACHI_AC || SEND_HITACHI_AC2) + +#if SEND_HITACHI_AC1 +// Send a Hitachi A/C 13-byte message. +// +// For devices: +// Hitachi A/C Series VI (Circa 2007) / Remote: LT0541-HTA +// +// Args: +// data: An array of bytes containing the IR command. +// nbytes: Nr. of bytes of data in the array. (>=HITACHI_AC1_STATE_LENGTH) +// repeat: Nr. of times the message is to be repeated. (Default = 0). +// +// Status: BETA / Appears to work. +// +// Ref: +// https://github.com/markszabo/IRremoteESP8266/issues/453 +// Basically the same as sendHitatchiAC() except different size and header. +void IRsend::sendHitachiAC1(unsigned char data[], uint16_t nbytes, + uint16_t repeat) { + if (nbytes < HITACHI_AC1_STATE_LENGTH) + return; // Not enough bytes to send a proper message. + sendGeneric(HITACHI_AC1_HDR_MARK, HITACHI_AC1_HDR_SPACE, + HITACHI_AC_BIT_MARK, HITACHI_AC_ONE_SPACE, + HITACHI_AC_BIT_MARK, HITACHI_AC_ZERO_SPACE, + HITACHI_AC_BIT_MARK, HITACHI_AC_MIN_GAP, + data, nbytes, 38, true, repeat, 50); +} +#endif // SEND_HITACHI_AC1 + +#if SEND_HITACHI_AC2 +// Send a Hitachi A/C 53-byte message. +// +// For devices: +// Hitachi A/C Series VI (Circa 2007) / Remote: LT0541-HTA +// +// Args: +// data: An array of bytes containing the IR command. +// nbytes: Nr. of bytes of data in the array. (>=HITACHI_AC2_STATE_LENGTH) +// repeat: Nr. of times the message is to be repeated. (Default = 0). +// +// Status: BETA / Appears to work. +// +// Ref: +// https://github.com/markszabo/IRremoteESP8266/issues/417 +// Basically the same as sendHitatchiAC() except different size. +void IRsend::sendHitachiAC2(unsigned char data[], uint16_t nbytes, + uint16_t repeat) { + if (nbytes < HITACHI_AC2_STATE_LENGTH) + return; // Not enough bytes to send a proper message. + sendHitachiAC(data, nbytes, repeat); +} +#endif // SEND_HITACHI_AC2 + +#if (DECODE_HITACHI_AC || DECODE_HITACHI_AC1 || DECODE_HITACHI_AC2) +// Decode the supplied Hitachi 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 HITACHI_AC_BITS, HITACHI_AC1_BITS, HITACHI_AC2_BITS +// 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. +// +// Supported devices: +// Hitachi A/C Series VI (Circa 2007) / Remote: LT0541-HTA +// +// Ref: +// https://github.com/markszabo/IRremoteESP8266/issues/417 +// https://github.com/markszabo/IRremoteESP8266/issues/453 +bool IRrecv::decodeHitachiAC(decode_results *results, uint16_t nbits, + bool strict) { + const uint8_t kTolerance = 30; + if (results->rawlen < 2 * nbits + HEADER + FOOTER - 1) + return false; // Can't possibly be a valid HitachiAC message. + if (strict) { + switch (nbits) { + case HITACHI_AC_BITS: + case HITACHI_AC1_BITS: + case HITACHI_AC2_BITS: + break; // Okay to continue. + default: + return false; // Not strictly a Hitachi message. + } + } + uint16_t offset = OFFSET_START; + uint16_t dataBitsSoFar = 0; + match_result_t data_result; + + // Header + if (nbits == HITACHI_AC1_BITS) { + if (!matchMark(results->rawbuf[offset++], HITACHI_AC1_HDR_MARK, kTolerance)) + return false; + if (!matchSpace(results->rawbuf[offset++], HITACHI_AC1_HDR_SPACE, + kTolerance)) + return false; + } else { // Everything else. + if (!matchMark(results->rawbuf[offset++], HITACHI_AC_HDR_MARK, kTolerance)) + return false; + if (!matchSpace(results->rawbuf[offset++], HITACHI_AC_HDR_SPACE, + kTolerance)) + return false; + } + // Data + // Keep reading bytes until we either run out of message 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, + HITACHI_AC_BIT_MARK, + HITACHI_AC_ONE_SPACE, + HITACHI_AC_BIT_MARK, + HITACHI_AC_ZERO_SPACE, + kTolerance); + if (data_result.success == false) break; // Fail + results->state[i] = (uint8_t) data_result.data; + } + + // Footer + if (!matchMark(results->rawbuf[offset++], HITACHI_AC_BIT_MARK, kTolerance)) + return false; + if (offset <= results->rawlen && + !matchAtLeast(results->rawbuf[offset], HITACHI_AC_MIN_GAP, kTolerance)) + return false; + + // Compliance + if (strict) { + // Re-check we got the correct size/length due to the way we read the data. + switch (dataBitsSoFar / 8) { + case HITACHI_AC_STATE_LENGTH: + case HITACHI_AC1_STATE_LENGTH: + case HITACHI_AC2_STATE_LENGTH: + break; // Continue + default: + return false; + } + } + + // Success + switch (dataBitsSoFar) { + case HITACHI_AC1_BITS: + results->decode_type = HITACHI_AC1; + break; + case HITACHI_AC2_BITS: + results->decode_type = HITACHI_AC2; + break; + case HITACHI_AC_BITS: + default: + results->decode_type = HITACHI_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 // (DECODE_HITACHI_AC || DECODE_HITACHI_AC1 || DECODE_HITACHI_AC2) diff --git a/lib/IRremoteESP8266/src/ir_JVC.cpp b/lib/IRremoteESP8266/src/ir_JVC.cpp index 194e392..5a4017e 100644 --- a/lib/IRremoteESP8266/src/ir_JVC.cpp +++ b/lib/IRremoteESP8266/src/ir_JVC.cpp @@ -62,13 +62,18 @@ void IRsend::sendJVC(uint64_t data, uint16_t nbits, uint16_t repeat) { // We always send the data & footer at least once, hence '<= repeat'. for (uint16_t i = 0; i <= repeat; i++) { - // Data - sendData(JVC_BIT_MARK, JVC_ONE_SPACE, JVC_BIT_MARK, JVC_ZERO_SPACE, - data, nbits, true); - // Footer - mark(JVC_BIT_MARK); + sendGeneric(0, 0, // No Header + JVC_BIT_MARK, JVC_ONE_SPACE, + JVC_BIT_MARK, JVC_ZERO_SPACE, + JVC_BIT_MARK, JVC_MIN_GAP, + data, nbits, 38, true, 0, // Repeats are handles elsewhere. + 33); // Wait till the end of the repeat time window before we send another code. - space(std::max(JVC_MIN_GAP, JVC_RPT_LENGTH - usecs.elapsed())); + uint32_t elapsed = usecs.elapsed(); + // Avoid potential unsigned integer underflow. + // e.g. when elapsed > JVC_RPT_LENGTH. + if (elapsed < JVC_RPT_LENGTH) + space(JVC_RPT_LENGTH - elapsed); usecs.reset(); } } diff --git a/lib/IRremoteESP8266/src/ir_Kelvinator.cpp b/lib/IRremoteESP8266/src/ir_Kelvinator.cpp index 257a32e..0a7f43d 100644 --- a/lib/IRremoteESP8266/src/ir_Kelvinator.cpp +++ b/lib/IRremoteESP8266/src/ir_Kelvinator.cpp @@ -16,6 +16,12 @@ #include "ir_Kelvinator.h" #include +#ifndef ARDUINO +#include +#endif +#include "IRrecv.h" +#include "IRsend.h" +#include "IRutils.h" // 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 @@ -24,13 +30,21 @@ // KK KK EEEEEEE LLLLLLL VVV IIIII NN NN AA AA TTT OOOO0 RR RR // Constants -#define KELVINATOR_HDR_MARK 8990U -#define KELVINATOR_HDR_SPACE 4490U -#define KELVINATOR_BIT_MARK 675U -#define KELVINATOR_ONE_SPACE 1560U -#define KELVINATOR_ZERO_SPACE 520U -#define KELVINATOR_GAP_SPACE 19950U +#define KELVINATOR_TICK 85U +#define KELVINATOR_HDR_MARK_TICKS 106U +#define KELVINATOR_HDR_MARK (KELVINATOR_HDR_MARK_TICKS * KELVINATOR_TICK) +#define KELVINATOR_HDR_SPACE_TICKS 53U +#define KELVINATOR_HDR_SPACE (KELVINATOR_HDR_SPACE_TICKS * KELVINATOR_TICK) +#define KELVINATOR_BIT_MARK_TICKS 8U +#define KELVINATOR_BIT_MARK (KELVINATOR_BIT_MARK_TICKS * KELVINATOR_TICK) +#define KELVINATOR_ONE_SPACE_TICKS 18U +#define KELVINATOR_ONE_SPACE (KELVINATOR_ONE_SPACE_TICKS * KELVINATOR_TICK) +#define KELVINATOR_ZERO_SPACE_TICKS 6U +#define KELVINATOR_ZERO_SPACE (KELVINATOR_ZERO_SPACE_TICKS * KELVINATOR_TICK) +#define KELVINATOR_GAP_SPACE_TICKS 235U +#define KELVINATOR_GAP_SPACE (KELVINATOR_GAP_SPACE_TICKS * KELVINATOR_TICK) #define KELVINATOR_CMD_FOOTER 2U +#define KELVINATOR_CMD_FOOTER_BITS 3U #define KELVINATOR_POWER 8U #define KELVINATOR_MODE_MASK 0xF8U @@ -69,59 +83,48 @@ void IRsend::sendKelvinator(unsigned char data[], uint16_t nbytes, if (nbytes < KELVINATOR_STATE_LENGTH) return; // Not enough bytes to send a proper message. - // Set IR carrier frequency - enableIROut(38); - for (uint16_t r = 0; r <= repeat; r++) { - // Header #1 - mark(KELVINATOR_HDR_MARK); - space(KELVINATOR_HDR_SPACE); - // Data (command) - // Send the first command data (4 bytes) - uint8_t i; - for (i = 0; i < 4; i++) - sendData(KELVINATOR_BIT_MARK, KELVINATOR_ONE_SPACE, KELVINATOR_BIT_MARK, - KELVINATOR_ZERO_SPACE, data[i], 8, false); - // Send Footer for the command data (3 bits (0b010)) - sendData(KELVINATOR_BIT_MARK, KELVINATOR_ONE_SPACE, KELVINATOR_BIT_MARK, - KELVINATOR_ZERO_SPACE, KELVINATOR_CMD_FOOTER, 3, false); - // Send an interdata gap. - mark(KELVINATOR_BIT_MARK); - space(KELVINATOR_GAP_SPACE); - // Data (options) - // Send the 1st option chunk of data (4 bytes). - for (; i < 8; i++) - sendData(KELVINATOR_BIT_MARK, KELVINATOR_ONE_SPACE, KELVINATOR_BIT_MARK, - KELVINATOR_ZERO_SPACE, data[i], 8, false); - // Send a double data gap to signify we are starting a new command sequence. - mark(KELVINATOR_BIT_MARK); - space(KELVINATOR_GAP_SPACE * 2); - // Header #2 - mark(KELVINATOR_HDR_MARK); - space(KELVINATOR_HDR_SPACE); - // Data (command) - // Send the 2nd command data (4 bytes). - // Basically an almost identical repeat of the earlier command data. - for (; i < 12; i++) - sendData(KELVINATOR_BIT_MARK, KELVINATOR_ONE_SPACE, KELVINATOR_BIT_MARK, - KELVINATOR_ZERO_SPACE, data[i], 8, false); - // Send Footer for the command data (3 bits (B010)) - sendData(KELVINATOR_BIT_MARK, KELVINATOR_ONE_SPACE, KELVINATOR_BIT_MARK, - KELVINATOR_ZERO_SPACE, KELVINATOR_CMD_FOOTER, 3, false); - // Send an interdata gap. - mark(KELVINATOR_BIT_MARK); - space(KELVINATOR_GAP_SPACE); - // Data (options) - // Send the 2nd option chunk of data (4 bytes). - // Unlike the commands, definitely not a repeat of the earlier option data. - for (; i < KELVINATOR_STATE_LENGTH; i++) - sendData(KELVINATOR_BIT_MARK, KELVINATOR_ONE_SPACE, KELVINATOR_BIT_MARK, - KELVINATOR_ZERO_SPACE, data[i], 8, false); - // Footer - mark(KELVINATOR_BIT_MARK); - space(KELVINATOR_GAP_SPACE * 2); + // Command Block #1 (4 bytes) + sendGeneric(KELVINATOR_HDR_MARK, KELVINATOR_HDR_SPACE, + KELVINATOR_BIT_MARK, KELVINATOR_ONE_SPACE, + KELVINATOR_BIT_MARK, KELVINATOR_ZERO_SPACE, + 0, 0, // No Footer yet. + data, 4, 38, false, 0, 50); + // Send Footer for the command block (3 bits (B010)) + sendGeneric(0, 0, // No Header + KELVINATOR_BIT_MARK, KELVINATOR_ONE_SPACE, + KELVINATOR_BIT_MARK, KELVINATOR_ZERO_SPACE, + KELVINATOR_BIT_MARK, KELVINATOR_GAP_SPACE, + KELVINATOR_CMD_FOOTER, KELVINATOR_CMD_FOOTER_BITS, + 38, false, 0, 50); + // Data Block #1 (4 bytes) + sendGeneric(0, 0, // No header + KELVINATOR_BIT_MARK, KELVINATOR_ONE_SPACE, + KELVINATOR_BIT_MARK, KELVINATOR_ZERO_SPACE, + KELVINATOR_BIT_MARK, KELVINATOR_GAP_SPACE * 2, + data + 4, 4, 38, false, 0, 50); + // Command Block #2 (4 bytes) + sendGeneric(KELVINATOR_HDR_MARK, KELVINATOR_HDR_SPACE, + KELVINATOR_BIT_MARK, KELVINATOR_ONE_SPACE, + KELVINATOR_BIT_MARK, KELVINATOR_ZERO_SPACE, + 0, 0, // No Footer yet. + data + 8, 4, 38, false, 0, 50); + // Send Footer for the command block (3 bits (B010)) + sendGeneric(0, 0, // No Header + KELVINATOR_BIT_MARK, KELVINATOR_ONE_SPACE, + KELVINATOR_BIT_MARK, KELVINATOR_ZERO_SPACE, + KELVINATOR_BIT_MARK, KELVINATOR_GAP_SPACE, + KELVINATOR_CMD_FOOTER, KELVINATOR_CMD_FOOTER_BITS, + 38, false, 0, 50); + // Data Block #2 (4 bytes) + sendGeneric(0, 0, // No header + KELVINATOR_BIT_MARK, KELVINATOR_ONE_SPACE, + KELVINATOR_BIT_MARK, KELVINATOR_ZERO_SPACE, + KELVINATOR_BIT_MARK, KELVINATOR_GAP_SPACE * 2, + data + 12, 4, 38, false, 0, 50); } } +#endif // SEND_KELVINATOR IRKelvinatorAC::IRKelvinatorAC(uint16_t pin) : _irsend(pin) { stateReset(); @@ -145,34 +148,62 @@ void IRKelvinatorAC::fixup() { checksum(); // Calculate the checksums } +#if SEND_KELVINATOR void IRKelvinatorAC::send() { fixup(); // Ensure correct settings before sending. _irsend.sendKelvinator(remote_state); } +#endif // SEND_KELVINATOR uint8_t* IRKelvinatorAC::getRaw() { fixup(); // Ensure correct settings before sending. return remote_state; } -// Many Bothans died to bring us this information. -void IRKelvinatorAC::checksum() { - // For each command + options block. - for (uint8_t offset = 0; offset < KELVINATOR_STATE_LENGTH; offset += 8) { - uint8_t sum = KELVINATOR_CHECKSUM_START; - // Sum the lower half of the first 4 bytes of this block. - for (uint8_t i = 0; i < 4; i++) - sum += (remote_state[i + offset] & 0xFU); +void IRKelvinatorAC::setRaw(uint8_t new_code[]) { + for (uint8_t i = 0; i < KELVINATOR_STATE_LENGTH; i++) { + remote_state[i] = new_code[i]; + } +} + +uint8_t IRKelvinatorAC::calcBlockChecksum(const uint8_t *block, + const uint16_t length) { + uint8_t sum = KELVINATOR_CHECKSUM_START; + // Sum the lower half of the first 4 bytes of this block. + for (uint8_t i = 0; i < 4 && i < length - 1; i++, block++) + sum += (*block & 0x0FU); // then sum the upper half of the next 3 bytes. - for (uint8_t i = 4; i < 7; i++) - sum += (remote_state[i + offset] >> 4); - // Trim it down to fit into the 4 bits allowed. i.e. Mod 16. - sum &= 0xFU; - // Place it into the IR code in the top half of the 8th & 16th byte. + for (uint8_t i = 4; i < length - 1; i++, block++) + sum += (*block >> 4); + // Trim it down to fit into the 4 bits allowed. i.e. Mod 16. + return sum & 0x0FU; +} + +// Many Bothans died to bring us this information. +void IRKelvinatorAC::checksum(const uint16_t length) { + // For each command + options block. + for (uint16_t offset = 0; offset + 7 < length; offset += 8) { + uint8_t sum = calcBlockChecksum(remote_state + offset); remote_state[7 + offset] = (sum << 4) | (remote_state[7 + offset] & 0xFU); } } +// 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 IRKelvinatorAC::validChecksum(const uint8_t state[], + const uint16_t length) { + for (uint16_t offset = 0; offset + 7 < length; offset += 8) { + // Top 4 bits of the last byte in the block is the block's checksum. + if (state[offset + 7] >> 4 != calcBlockChecksum(state + offset)) + return false; + } + return true; +} + void IRKelvinatorAC::on() { remote_state[0] |= KELVINATOR_POWER; remote_state[8] = remote_state[0]; // Duplicate to the 2nd command chunk. @@ -326,4 +357,206 @@ void IRKelvinatorAC::setTurbo(bool state) { bool IRKelvinatorAC::getTurbo() { return ((remote_state[2] & KELVINATOR_TURBO) != 0); } -#endif + +// Convert the internal state into a human readable string. +#ifdef ARDUINO +String IRKelvinatorAC::toString() { + String result = ""; +#else +std::string IRKelvinatorAC::toString() { + std::string result = ""; +#endif // ARDUINO + result += "Power: "; + if (getPower()) + result += "On"; + else + result += "Off"; + result += ", Mode: " + uint64ToString(getMode()); + switch (getMode()) { + case KELVINATOR_AUTO: + result += " (AUTO)"; + break; + case KELVINATOR_COOL: + result += " (COOL)"; + break; + case KELVINATOR_HEAT: + result += " (HEAT)"; + break; + case KELVINATOR_DRY: + result += " (DRY)"; + break; + case KELVINATOR_FAN: + result += " (FAN)"; + break; + default: + result += " (UNKNOWN)"; + } + result += ", Temp: " + uint64ToString(getTemp()) + "C"; + result += ", Fan: " + uint64ToString(getFan()); + switch (getFan()) { + case KELVINATOR_FAN_AUTO: + result += " (AUTO)"; + break; + case KELVINATOR_FAN_MAX: + result += " (MAX)"; + break; + } + result += ", Turbo: "; + if (getTurbo()) + result += "On"; + else + result += "Off"; + result += ", Quiet: "; + if (getQuiet()) + result += "On"; + else + result += "Off"; + result += ", XFan: "; + if (getXFan()) + result += "On"; + else + result += "Off"; + result += ", IonFilter: "; + if (getIonFilter()) + result += "On"; + else + result += "Off"; + result += ", Light: "; + if (getLight()) + 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"; + return result; +} + +#if DECODE_KELVINATOR +// Decode the supplied Kelvinator 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 KELVINATOR_BITS. +// 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::decodeKelvinator(decode_results *results, uint16_t nbits, + bool strict) { + if (results->rawlen < 2 * (nbits + KELVINATOR_CMD_FOOTER_BITS) + + (HEADER + FOOTER + 1) * 2 - 1) + return false; // Can't possibly be a valid Kelvinator message. + if (strict && nbits != KELVINATOR_BITS) + return false; // Not strictly a Kelvinator message. + + uint32_t data; + uint16_t offset = OFFSET_START; + + // There are two messages back-to-back in a full Kelvinator IR message + // sequence. + int8_t state_pos = 0; + for (uint8_t s = 0; s < 2; s++) { + match_result_t data_result; + + // Header + if (!matchMark(results->rawbuf[offset], KELVINATOR_HDR_MARK)) return false; + // Calculate how long the lowest tick time is based on the header mark. + uint32_t mark_tick = results->rawbuf[offset++] * RAWTICK / + KELVINATOR_HDR_MARK_TICKS; + if (!matchSpace(results->rawbuf[offset], KELVINATOR_HDR_SPACE)) + return false; + // Calculate how long the common tick time is based on the header space. + uint32_t space_tick = results->rawbuf[offset++] * RAWTICK / + KELVINATOR_HDR_SPACE_TICKS; + + // Data (Command) (32 bits) + data_result = matchData(&(results->rawbuf[offset]), 32, + KELVINATOR_BIT_MARK_TICKS * mark_tick, + KELVINATOR_ONE_SPACE_TICKS * space_tick, + KELVINATOR_BIT_MARK_TICKS * mark_tick, + KELVINATOR_ZERO_SPACE_TICKS * space_tick); + if (data_result.success == false) return false; + data = data_result.data; + offset += data_result.used; + + // Record command data in the state. + for (int i = state_pos + 3; i >= state_pos; i--, data >>= 8) + results->state[i] = reverseBits(data & 0xFF, 8); + state_pos += 4; + + // Command data footer (3 bits, B010) + data_result = matchData(&(results->rawbuf[offset]), + KELVINATOR_CMD_FOOTER_BITS, + KELVINATOR_BIT_MARK_TICKS * mark_tick, + KELVINATOR_ONE_SPACE_TICKS * space_tick, + KELVINATOR_BIT_MARK_TICKS * mark_tick, + KELVINATOR_ZERO_SPACE_TICKS * space_tick); + if (data_result.success == false) return false; + if (data_result.data != KELVINATOR_CMD_FOOTER) return false; + offset += data_result.used; + + // Interdata gap. + if (!matchMark(results->rawbuf[offset++], + KELVINATOR_BIT_MARK_TICKS * mark_tick)) + return false; + if (!matchSpace(results->rawbuf[offset++], + KELVINATOR_GAP_SPACE_TICKS * space_tick)) + return false; + + // Data (Options) (32 bits) + data_result = matchData(&(results->rawbuf[offset]), 32, + KELVINATOR_BIT_MARK_TICKS * mark_tick, + KELVINATOR_ONE_SPACE_TICKS * space_tick, + KELVINATOR_BIT_MARK_TICKS * mark_tick, + KELVINATOR_ZERO_SPACE_TICKS * space_tick); + if (data_result.success == false) return false; + data = data_result.data; + offset += data_result.used; + + // Record option data in the state. + for (int i = state_pos + 3; i >= state_pos; i--, data >>= 8) + results->state[i] = reverseBits(data & 0xFF, 8); + state_pos += 4; + + // Inter-sequence gap. (Double length gap) + if (!matchMark(results->rawbuf[offset++], + KELVINATOR_BIT_MARK_TICKS * mark_tick)) + return false; + if (s == 0) { + if (!matchSpace(results->rawbuf[offset++], + KELVINATOR_GAP_SPACE_TICKS * space_tick * 2)) + return false; + } else { + if (offset <= results->rawlen && + !matchAtLeast(results->rawbuf[offset], + KELVINATOR_GAP_SPACE_TICKS * 2 * space_tick)) + return false; + } + } + + // Compliance + if (strict) { + // Correct size/length) + if (state_pos != KELVINATOR_STATE_LENGTH) return false; + // Verify the message's checksum is correct. + if (!IRKelvinatorAC::validChecksum(results->state)) return false; + } + + // Success + results->decode_type = KELVINATOR; + results->bits = state_pos * 8; + // No need to record the state as we stored it as we decoded it. + // As we use result->state, we don't record value, address, or command as it + // is a union data type. + return true; +} +#endif // DECODE_KELVINATOR diff --git a/lib/IRremoteESP8266/src/ir_Kelvinator.h b/lib/IRremoteESP8266/src/ir_Kelvinator.h index c15d85c..6343b5a 100644 --- a/lib/IRremoteESP8266/src/ir_Kelvinator.h +++ b/lib/IRremoteESP8266/src/ir_Kelvinator.h @@ -7,6 +7,11 @@ #define __STDC_LIMIT_MACROS #include +#ifndef UNIT_TEST +#include +#else +#include +#endif #include "IRremoteESP8266.h" #include "IRsend.h" @@ -23,6 +28,7 @@ #define KELVINATOR_FAN 3U #define KELVINATOR_HEAT 4U #define KELVINATOR_BASIC_FAN_MAX 3U +#define KELVINATOR_FAN_AUTO 0U #define KELVINATOR_FAN_MAX 5U #define KELVINATOR_MIN_TEMP 16U // 16C #define KELVINATOR_MAX_TEMP 30U // 30C @@ -104,15 +110,15 @@ b7-4 = checksum of the previous bytes (8-14) */ -#if SEND_KELVINATOR - // Classes class IRKelvinatorAC { public: explicit IRKelvinatorAC(uint16_t pin); void stateReset(); +#if SEND_KELVINATOR void send(); +#endif // SEND_KELVINATOR void begin(); void on(); void off(); @@ -139,14 +145,24 @@ class IRKelvinatorAC { void setTurbo(bool state); bool getTurbo(); uint8_t* getRaw(); + void setRaw(uint8_t new_code[]); + static uint8_t calcBlockChecksum( + const uint8_t *block, + const uint16_t length = KELVINATOR_STATE_LENGTH / 2); + static bool validChecksum(const uint8_t state[], + const uint16_t length = KELVINATOR_STATE_LENGTH); +#ifdef ARDUINO + String toString(); +#else + std::string toString(); +#endif private: // The state of the IR remote in IR code form. uint8_t remote_state[KELVINATOR_STATE_LENGTH]; - void checksum(); + void checksum(const uint16_t length = KELVINATOR_STATE_LENGTH); void fixup(); IRsend _irsend; }; -#endif #endif // IR_KELVINATOR_H_ diff --git a/lib/IRremoteESP8266/src/ir_LG.cpp b/lib/IRremoteESP8266/src/ir_LG.cpp index 716ddd2..f2d3d06 100644 --- a/lib/IRremoteESP8266/src/ir_LG.cpp +++ b/lib/IRremoteESP8266/src/ir_LG.cpp @@ -6,7 +6,6 @@ #include #include "IRrecv.h" #include "IRsend.h" -#include "IRtimer.h" #include "IRutils.h" // L GGGG @@ -69,11 +68,7 @@ uint8_t calcLGChecksum(uint16_t data) { // Notes: // LG has a separate message to indicate a repeat, like NEC does. void IRsend::sendLG(uint64_t data, uint16_t nbits, uint16_t repeat) { - // Set IR carrier frequency - enableIROut(38); - uint16_t repeatHeaderMark = 0; - IRtimer usecTimer = IRtimer(); if (nbits >= LG32_BITS) { // LG 32bit protocol is near identical to Samsung except for repeats. @@ -83,29 +78,23 @@ void IRsend::sendLG(uint64_t data, uint16_t nbits, uint16_t repeat) { } else { // LG (28-bit) protocol. repeatHeaderMark = LG_HDR_MARK; - // Header - usecTimer.reset(); - mark(LG_HDR_MARK); - space(LG_HDR_SPACE); - // Data - sendData(LG_BIT_MARK, LG_ONE_SPACE, LG_BIT_MARK, LG_ZERO_SPACE, - data, nbits, true); - // Footer - mark(LG_BIT_MARK); - space(std::max((uint32_t) (LG_MIN_MESSAGE_LENGTH - usecTimer.elapsed()), - (uint32_t) LG_MIN_GAP)); + sendGeneric(LG_HDR_MARK, LG_HDR_SPACE, + LG_BIT_MARK, LG_ONE_SPACE, + LG_BIT_MARK, LG_ZERO_SPACE, + LG_BIT_MARK, + LG_MIN_GAP, LG_MIN_MESSAGE_LENGTH, + data, nbits, 38, true, 0, // Repeats are handled later. + 50); } // Repeat // Protocol has a mandatory repeat-specific code sent after every command. - for (uint16_t i = 0; i < repeat; i++) { - usecTimer.reset(); - mark(repeatHeaderMark); - space(LG_RPT_SPACE); - mark(LG_BIT_MARK); - space(std::max((uint32_t) LG_MIN_MESSAGE_LENGTH - usecTimer.elapsed(), - (uint32_t) LG_MIN_GAP)); - } + if (repeat) + sendGeneric(repeatHeaderMark, LG_RPT_SPACE, + 0, 0, 0, 0, // No data is sent. + LG_BIT_MARK, LG_MIN_GAP, LG_MIN_MESSAGE_LENGTH, + 0, 0, // No data. + 38, true, repeat - 1, 50); } // Construct a raw 28-bit LG message from the supplied address & command. @@ -150,8 +139,13 @@ uint32_t IRsend::encodeLG(uint16_t address, uint16_t command) { // Ref: // https://funembedded.wordpress.com/2014/11/08/ir-remote-control-for-lg-conditioner-using-stm32f302-mcu-on-mbed-platform/ bool IRrecv::decodeLG(decode_results *results, uint16_t nbits, bool strict) { - if (results->rawlen < 2 * nbits + HEADER + FOOTER - 1 && results->rawlen != 4) - return false; // Can't possibly be a valid LG message. + if (nbits >= LG32_BITS) { + if (results->rawlen < 2 * nbits + 2 * (HEADER + FOOTER) - 1) + return false; // Can't possibly be a valid LG32 message. + } else { + if (results->rawlen < 2 * nbits + HEADER + FOOTER - 1) + return false; // Can't possibly be a valid LG message. + } if (strict && nbits != LG_BITS && nbits != LG32_BITS) return false; // Doesn't comply with expected LG protocol. @@ -195,15 +189,6 @@ bool IRrecv::decodeLG(decode_results *results, uint16_t nbits, bool strict) { if (nbits >= LG32_BITS) { // If we are expecting the LG 32-bit protocol, there is always // a repeat message. So, check for it. -#ifndef UNIT_TEST - if (!matchSpace(results->rawbuf[offset], LG_MIN_GAP_TICKS * s_tick)) -#else - if (!(matchSpace(results->rawbuf[offset], - LG_MIN_MESSAGE_LENGTH_TICKS * s_tick) || - matchSpace(results->rawbuf[offset], 65500) || - matchSpace(results->rawbuf[offset], LG_MIN_GAP_TICKS * s_tick))) -#endif // UNIT_TEST - return false; offset++; if (!matchMark(results->rawbuf[offset++], LG32_RPT_HDR_MARK_TICKS * m_tick)) return false; diff --git a/lib/IRremoteESP8266/src/ir_Lasertag.cpp b/lib/IRremoteESP8266/src/ir_Lasertag.cpp new file mode 100644 index 0000000..2fc930b --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_Lasertag.cpp @@ -0,0 +1,124 @@ +// Copyright 2017 David Conran + +#include +#include "IRrecv.h" +#include "IRsend.h" +#include "IRutils.h" + +// LL AAA SSSSS EEEEEEE RRRRRR TTTTTTT AAA GGGG +// LL AAAAA SS EE RR RR TTT AAAAA GG GG +// LL AA AA SSSSS EEEEE RRRRRR TTT AA AA GG +// LL AAAAAAA SS EE RR RR TTT AAAAAAA GG GG +// LLLLLLL AA AA SSSSS EEEEEEE RR RR TTT AA AA GGGGGG + +// Constants +#define MIN_LASERTAG_SAMPLES 13U +#define LASERTAG_TICK 333U +#define LASERTAG_MIN_GAP 100000UL // Completely made up amount. +#define LASERTAG_TOLERANCE 0U // Percentage error margin +#define LASERTAG_EXCESS 0U // See MARK_EXCESS +#define LASERTAG_DELTA 150U // Use instead of EXCESS and TOLERANCE. +const int16_t kSPACE = 1; +const int16_t kMARK = 0; + +#if SEND_LASERTAG +// Send a Lasertag packet. +// This protocol is pretty much just raw Manchester encoding. +// +// Args: +// data: The message you wish to send. +// nbits: Bit size of the protocol you want to send. +// repeat: Nr. of extra times the data will be sent. +// +// Status: STABLE / Working. +// + +void IRsend::sendLasertag(uint64_t data, uint16_t nbits, uint16_t repeat) { + if (nbits > sizeof(data) * 8) + return; // We can't send something that big. + + // Set 36kHz IR carrier frequency & a 1/4 (25%) duty cycle. + // NOTE: duty cycle is not confirmed. Just guessing based on RC5/6 protocols. + enableIROut(36, 25); + + for (uint16_t i = 0; i <= repeat; i++) { + // Data + for (uint64_t mask = 1ULL << (nbits - 1); mask; mask >>= 1) + if (data & mask) { // 1 + space(LASERTAG_TICK); // 1 is space, then mark. + mark(LASERTAG_TICK); + } else { // 0 + mark(LASERTAG_TICK); // 0 is mark, then space. + space(LASERTAG_TICK); + } + // Footer + space(LASERTAG_MIN_GAP); + } +} +#endif // SEND_LASERTAG + +#if DECODE_LASERTAG +// Decode the supplied Lasertag message. +// This protocol is pretty much just raw Manchester encoding. +// +// Args: +// results: Ptr to the data to decode and where to store the decode result. +// nbits: The number of data bits to expect. +// 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 90% of the time. +// +// Ref: +// http://www.sbprojects.com/knowledge/ir/rc5.php +// https://en.wikipedia.org/wiki/RC-5 +// https://en.wikipedia.org/wiki/Manchester_code +bool IRrecv::decodeLasertag(decode_results *results, uint16_t nbits, + bool strict) { + if (results->rawlen < MIN_LASERTAG_SAMPLES) return false; + + // Compliance + if (strict && nbits != LASERTAG_BITS) return false; + + uint16_t offset = OFFSET_START; + uint16_t used = 0; + uint64_t data = 0; + uint16_t actual_bits = 0; + + // No Header + + // Data + for (; offset <= results->rawlen; actual_bits++) { + int16_t levelA = getRClevel(results, &offset, &used, LASERTAG_TICK, + LASERTAG_TOLERANCE, LASERTAG_EXCESS, + LASERTAG_DELTA); + int16_t levelB = getRClevel(results, &offset, &used, LASERTAG_TICK, + LASERTAG_TOLERANCE, LASERTAG_EXCESS, + LASERTAG_DELTA); + if (levelA == kSPACE && levelB == kMARK) { + data = (data << 1) | 1; // 1 + } else { + if (levelA == kMARK && levelB == kSPACE) { + data <<= 1; // 0 + } else { + break; + } + } + } + // Footer (None) + + // Compliance + if (actual_bits < nbits) return false; // Less data than we expected. + if (strict && actual_bits != LASERTAG_BITS) return false; + + // Success + results->decode_type = LASERTAG; + results->value = data; + results->address = data & 0xF; // Unit + results->command = data >> 4; // Team + results->repeat = false; + results->bits = actual_bits; + return true; +} +#endif // DECODE_LASERTAG diff --git a/lib/IRremoteESP8266/src/ir_Magiquest.cpp b/lib/IRremoteESP8266/src/ir_Magiquest.cpp new file mode 100644 index 0000000..dcf2174 --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_Magiquest.cpp @@ -0,0 +1,160 @@ +// Copyright 2013 mpflaga +// Copyright 2015 kitlaan +// Copyright 2017 Jason kendall, David Conran + +#include "ir_Magiquest.h" +#include +#include "IRrecv.h" +#include "IRsend.h" +#include "IRutils.h" + +#define IS_ZERO(m, s) (((m) * 100 / ((m) + (s))) <= MAGIQUEST_ZERO_RATIO) +#define IS_ONE(m, s) (((m) * 100 / ((m) + (s))) >= MAGIQUEST_ONE_RATIO) + +// Strips taken from: +// https://github.com/kitlaan/Arduino-IRremote/blob/master/ir_Magiquest.cpp +// and +// https://github.com/mpflaga/Arduino-IRremote + +// Source: https://github.com/mpflaga/Arduino-IRremote + +#if SEND_MAGIQUEST +// Send a MagiQuest formatted message. +// +// Args: +// data: The contents of the message you want to send. +// nbits: The bit size of the message being sent. +// Typically MAGIQUEST_BITS. +// repeat: The number of times you want the message to be repeated. +// +// Status: Alpha / Should be working. +// +void IRsend::sendMagiQuest(uint64_t data, uint16_t nbits, uint16_t repeat) { + sendGeneric(0, 0, // No Headers - Technically it's included in the data. + // i.e. 8 zeros. + MAGIQUEST_MARK_ONE, MAGIQUEST_SPACE_ONE, + MAGIQUEST_MARK_ZERO, MAGIQUEST_SPACE_ZERO, + 0, // No footer mark. + MAGIQUEST_GAP, + data, nbits, 36, true, repeat, 50); +} + +// Encode a MagiQuest wand_id, and a magnitude into a single 64bit value. +// (Only 48 bits of real data + 8 leading zero bits) +// This is suitable for calling sendMagiQuest() with. +// e.g. sendMagiQuest(encodeMagiQuest(wand_id, magnitude)); +uint64_t IRsend::encodeMagiQuest(uint32_t wand_id, uint16_t magnitude) { + uint64_t result = 0; + result = wand_id; + result <<= 16; + result |= magnitude; + // Shouldn't be needed, but ensure top 8/16 bit are zero. + result &= 0xFFFFFFFFFFFFULL; + return result; +} +#endif + +// Source: https://github.com/kitlaan/Arduino-IRremote/blob/master/ir_Magiquest.cpp + +#if DECODE_MAGIQUEST +// Decode the supplied MagiQuest message. +// MagiQuest protocol appears to be a header of 8 'zero' bits, followed +// by 32 bits of "wand ID" and finally 16 bits of "magnitude". +// Even though we describe this protocol as 56 bits, it really only has +// 48 bits of data that matter. +// +// In transmission order, 8 zeros + 32 wand_id + 16 magnitude. +// +// 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, inc. the 8 bit header. +// Typically MAGIQUEST_BITS. +// 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 / Should work. +// +// Ref: +// https://github.com/kitlaan/Arduino-IRremote/blob/master/ir_Magiquest.cpp +bool IRrecv::decodeMagiQuest(decode_results *results, uint16_t nbits, + bool strict) { + uint16_t bits = 0; + uint64_t data = 0; + uint16_t offset = OFFSET_START; + + if (results->rawlen < (2 * MAGIQUEST_BITS)) { + DPRINT("Not enough bits to be Magiquest - Rawlen: "); + DPRINT(results->rawlen); + DPRINT(" Expected: "); + DPRINTLN((2 * MAGIQUEST_BITS)); + return false; + } + + // Compliance + if (strict && nbits != MAGIQUEST_BITS) return false; + + // Of six wands as datapoints, so far they all start with 8 ZEROs. + // For example, here is the data from two wands + // 00000000 00100011 01001100 00100110 00000010 00000010 00010111 + // 00000000 00100000 10001000 00110001 00000010 00000010 10110100 + + // Decode the (MARK + SPACE) bits + while (offset + 1 < results->rawlen && bits < nbits - 1) { + uint16_t mark = results->rawbuf[offset]; + uint16_t space = results->rawbuf[offset + 1]; + if (!matchMark(mark + space, MAGIQUEST_TOTAL_USEC)) { + DPRINT("Not enough time to be Magiquest - Mark: "); + DPRINT(mark); + DPRINT(" Space: "); + DPRINT(space); + DPRINT(" Total: "); + DPRINT(mark+space); + DPRINT("Expected: "); + DPRINTLN(MAGIQUEST_TOTAL_USEC); + return false; + } + + if (IS_ZERO(mark, space)) data = (data << 1) | 0; + else if (IS_ONE( mark, space)) data = (data << 1) | 1; + else return false; + + bits++; + offset += 2; + + // Compliance + // The first 8 bits of this protocol are supposed to all be 0. + // Exit out early as it is never going to match. + if (strict && bits == 8 && data != 0) return false; + } + + // Last bit is special as the protocol ends with a SPACE, not a MARK. + // Grab the last MARK bit, assuming a good SPACE after it + if (offset < results->rawlen) { + uint16_t mark = results->rawbuf[offset]; + uint16_t space = (MAGIQUEST_TOTAL_USEC / RAWTICK) - mark; + + if (IS_ZERO(mark, space)) data = (data << 1) | 0; + else if (IS_ONE( mark, space)) data = (data << 1) | 1; + else return false; + + bits++; + } + + if (bits != nbits) return false; + + if (strict) { + // The top 8 bits of the 56 bits needs to be 0x00 to be valid. + // i.e. bits 56 to 49 are all zero. + if ((data >> (nbits - 8)) != 0) return false; + } + + // Success + results->decode_type = MAGIQUEST; + results->bits = bits; + results->value = data; + results->address = data >> 16; // Wand ID + results->command = data & 0xFFFF; // Magnitude + return true; +} +#endif diff --git a/lib/IRremoteESP8266/src/ir_Magiquest.h b/lib/IRremoteESP8266/src/ir_Magiquest.h new file mode 100644 index 0000000..94cebfc --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_Magiquest.h @@ -0,0 +1,37 @@ +// Copyright 2013 mpflaga +// Copyright 2015 kitlaan +// Copyright 2017 Jason kendall, David Conran + +#ifndef IR_MAGIQUEST_H_ +#define IR_MAGIQUEST_H_ + +#define __STDC_LIMIT_MACROS +#include +#include "IRremoteESP8266.h" +#include "IRsend.h" + +// MagiQuest packet is both Wand ID and magnitude of swish and flick +union magiquest { + uint64_t llword; + uint8_t byte[8]; +// uint16_t word[4]; + uint32_t lword[2]; + struct { + uint16_t magnitude; + uint32_t wand_id; + uint8_t padding; + uint8_t scrap; + } cmd; +}; + +#define MAGIQUEST_TOTAL_USEC 1150U +#define MAGIQUEST_ZERO_RATIO 30U // usually <= ~25% +#define MAGIQUEST_ONE_RATIO 38U // usually >= ~50% + +#define MAGIQUEST_PERIOD 1150U +#define MAGIQUEST_MARK_ZERO 280U +#define MAGIQUEST_SPACE_ZERO 850U +#define MAGIQUEST_MARK_ONE 580U +#define MAGIQUEST_SPACE_ONE 600U +#define MAGIQUEST_GAP 100000UL // A guess of the gap between messages +#endif // IR_MAGIQUEST_H_ diff --git a/lib/IRremoteESP8266/src/ir_Midea.cpp b/lib/IRremoteESP8266/src/ir_Midea.cpp new file mode 100644 index 0000000..a3dd890 --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_Midea.cpp @@ -0,0 +1,426 @@ +// Copyright 2017 bwze, crankyoldgit + +#include "ir_Midea.h" +#include +#ifndef ARDUINO +#include +#endif +#include "IRrecv.h" +#include "IRsend.h" +#include "IRutils.h" + +// MM MM IIIII DDDDD EEEEEEE AAA +// MMM MMM III DD DD EE AAAAA +// MM MM MM III DD DD EEEEE AA AA +// MM MM III DD DD EE AAAAAAA +// MM MM IIIII DDDDDD EEEEEEE AA AA + +// Midea A/C added by (send) bwze/crankyoldgit & (decode) crankyoldgit +// +// Equipment it seems compatible with: +// * Pioneer System Model RYBO12GMFILCAD (12K BTU) +// * Pioneer System Model RUBO18GMFILCAD (18K BTU) +// * + +// Ref: +// https://docs.google.com/spreadsheets/d/1TZh4jWrx4h9zzpYUI9aYXMl1fYOiqu-xVuOOMqagxrs/edit?usp=sharing + +// Constants +#define MIDEA_TICK 80U +#define MIDEA_BIT_MARK_TICKS 7U +#define MIDEA_BIT_MARK (MIDEA_BIT_MARK_TICKS * MIDEA_TICK) +#define MIDEA_ONE_SPACE_TICKS 21U +#define MIDEA_ONE_SPACE (MIDEA_ONE_SPACE_TICKS * MIDEA_TICK) +#define MIDEA_ZERO_SPACE_TICKS 7U +#define MIDEA_ZERO_SPACE (MIDEA_ZERO_SPACE_TICKS * MIDEA_TICK) +#define MIDEA_HDR_MARK_TICKS 56U +#define MIDEA_HDR_MARK (MIDEA_HDR_MARK_TICKS * MIDEA_TICK) +#define MIDEA_HDR_SPACE_TICKS 56U +#define MIDEA_HDR_SPACE (MIDEA_HDR_SPACE_TICKS * MIDEA_TICK) +#define MIDEA_MIN_GAP_TICKS (MIDEA_HDR_MARK_TICKS + MIDEA_ZERO_SPACE_TICKS \ + + MIDEA_BIT_MARK_TICKS) +#define MIDEA_MIN_GAP (MIDEA_MIN_GAP_TICKS * MIDEA_TICK) +#define MIDEA_TOLERANCE 30U // Percent + +#if SEND_MIDEA +// Send a Midea message +// +// Args: +// data: Contents of the message to be sent. +// nbits: Nr. of bits of data to be sent. Typically MIDEA_BITS. +// repeat: Nr. of additional times the message is to be sent. +// +// Status: Alpha / Needs testing against a real device. +// +void IRsend::sendMidea(uint64_t data, uint16_t nbits, uint16_t repeat) { + if (nbits % 8 != 0) + return; // nbits is required to be a multiple of 8. + + // Set IR carrier frequency + enableIROut(38); + + for (uint16_t r = 0; r <= repeat; r++) { + // The protcol sends the message, then follows up with an entirely + // inverted payload. + for (size_t inner_loop = 0; inner_loop < 2; inner_loop++) { + // Header + mark(MIDEA_HDR_MARK); + space(MIDEA_HDR_SPACE); + // Data + // Break data into byte segments, starting at the Most Significant + // Byte. Each byte then being sent normal, then followed inverted. + for (uint16_t i = 8; i <= nbits; i += 8) { + // Grab a bytes worth of data. + uint8_t segment = (data >> (nbits - i)) & 0xFF; + sendData(MIDEA_BIT_MARK, MIDEA_ONE_SPACE, + MIDEA_BIT_MARK, MIDEA_ZERO_SPACE, + segment, 8, true); + } + // Footer + mark(MIDEA_BIT_MARK); + space(MIDEA_MIN_GAP); // Pause before repeating + + // Invert the data for the 2nd phase of the message. + // As we get called twice in the inner loop, we will always revert + // to the original 'data' state. + data = ~data; + } + } +} +#endif + +// Code to emulate Midea A/C IR remote control unit. +// Warning: Consider this very alpha code. + +// Initialise the object. +IRMideaAC::IRMideaAC(uint16_t pin) : _irsend(pin) { + stateReset(); +} + +// Reset the state of the remote to a known good state/sequence. +void IRMideaAC::stateReset() { + // Power On, Mode Auto, Fan Auto, Temp = 25C/77F + remote_state = 0xA1826FFFFF62; +} + +// Configure the pin for output. +void IRMideaAC::begin() { + _irsend.begin(); +} + +#if SEND_MIDEA +// Send the current desired state to the IR LED. +void IRMideaAC::send() { + checksum(); // Ensure correct checksum before sending. + _irsend.sendMidea(remote_state); +} +#endif // SEND_MIDEA + +// Return a pointer to the internal state date of the remote. +uint64_t IRMideaAC::getRaw() { + checksum(); + return remote_state & MIDEA_AC_STATE_MASK; +} + +// Override the internal state with the new state. +void IRMideaAC::setRaw(uint64_t newState) { + remote_state = newState & MIDEA_AC_STATE_MASK; +} + +// Set the requested power state of the A/C to off. +void IRMideaAC::on() { + remote_state |= MIDEA_AC_POWER; +} + +// Set the requested power state of the A/C to off. +void IRMideaAC::off() { + remote_state &= (MIDEA_AC_STATE_MASK ^ MIDEA_AC_POWER); +} + +// Set the requested power state of the A/C. +void IRMideaAC::setPower(const bool state) { + if (state) + on(); + else + off(); +} + +// Return the requested power state of the A/C. +bool IRMideaAC::getPower() { + return (remote_state & MIDEA_AC_POWER); +} + +// Set the temperature. +// Args: +// temp: Temp. in degrees. +// useCelsius: Degree type to use. celsius (true) or fahrenheit (false) +void IRMideaAC::setTemp(const uint8_t temp, const bool useCelsius) { + uint8_t new_temp = temp; + if (useCelsius) { + new_temp = std::max((uint8_t) MIDEA_AC_MIN_TEMP_C, new_temp); + new_temp = std::min((uint8_t) MIDEA_AC_MAX_TEMP_C, new_temp); + new_temp = (uint8_t) ((new_temp * 1.8) + 32.5); // 0.5 so we rounding. + } + new_temp = std::max((uint8_t) MIDEA_AC_MIN_TEMP_F, new_temp); + new_temp = std::min((uint8_t) MIDEA_AC_MAX_TEMP_F, new_temp); + new_temp -= MIDEA_AC_MIN_TEMP_F; + remote_state &= MIDEA_AC_TEMP_MASK; + remote_state |= ((uint64_t) new_temp << 24); +} + +// Return the set temp. +// Args: +// useCelsius: Flag indicating if the results are in celsius or fahrenheit. +// Returns: +// A uint8_t containing the temperature. +uint8_t IRMideaAC::getTemp(const bool useCelsius) { + uint8_t temp = ((remote_state >> 24) & 0x1F) + MIDEA_AC_MIN_TEMP_F; + if (useCelsius) { + temp = (uint8_t) ((temp - 32) / 1.8); + } + return temp; +} + +// Set the speed of the fan, +// 1-3 set the speed, 0 or anything else set it to auto. +void IRMideaAC::setFan(const uint8_t fan) { + uint64_t new_fan; + switch (fan) { + case MIDEA_AC_FAN_LOW: + case MIDEA_AC_FAN_MED: + case MIDEA_AC_FAN_HI: + new_fan = fan; + break; + default: + new_fan = MIDEA_AC_FAN_AUTO; + } + remote_state &= MIDEA_AC_FAN_MASK; + remote_state |= (new_fan << 35); +} + +// Return the requested state of the unit's fan. +uint8_t IRMideaAC::getFan() { + return (remote_state >> 35) & 0b111; +} + +// Get the requested climate operation mode of the a/c unit. +// Returns: +// A uint8_t containing the A/C mode. +uint8_t IRMideaAC::getMode() { + return ((remote_state >> 32) & 0b111); +} + +// Set the requested climate operation mode of the a/c unit. +void IRMideaAC::setMode(const uint8_t mode) { + // If we get an unexpected mode, default to AUTO. + uint64_t new_mode; + switch (mode) { + case MIDEA_AC_AUTO: + case MIDEA_AC_COOL: + case MIDEA_AC_HEAT: + case MIDEA_AC_DRY: + case MIDEA_AC_FAN: + new_mode = mode; + break; + default: + new_mode = MIDEA_AC_AUTO; + } + remote_state &= MIDEA_AC_MODE_MASK; + remote_state |= (new_mode << 32); +} + +// Set the Sleep state of the A/C. +void IRMideaAC::setSleep(const bool state) { + if (state) + remote_state |= MIDEA_AC_SLEEP; + else + remote_state &= (MIDEA_AC_STATE_MASK ^ MIDEA_AC_SLEEP); +} + +// Return the Sleep state of the A/C. +bool IRMideaAC::getSleep() { + return (remote_state & MIDEA_AC_SLEEP); +} + +// Calculate the checksum for a given array. +// Args: +// state: The state to calculate the checksum over. +// Returns: +// The 8 bit checksum value. +uint8_t IRMideaAC::calcChecksum(const uint64_t state) { + uint8_t sum = 0; + uint64_t temp_state = state; + + for (uint8_t i = 0; i < 5; i++) { + temp_state >>= 8; + sum += reverseBits((temp_state & 0xFF), 8); + } + sum = 256 - sum; + return reverseBits(sum, 8); +} + +// Verify the checksum is valid for a given state. +// Args: +// state: The state to verify the checksum of. +// Returns: +// A boolean. +bool IRMideaAC::validChecksum(const uint64_t state) { + return ((state & 0xFF) == calcChecksum(state)); +} + +// Calculate & set the checksum for the current internal state of the remote. +void IRMideaAC::checksum() { + // Stored the checksum value in the last byte. + remote_state &= MIDEA_AC_CHECKSUM_MASK; + remote_state |= calcChecksum(remote_state); +} + +// Convert the internal state into a human readable string. +#ifdef ARDUINO +String IRMideaAC::toString() { + String result = ""; +#else +std::string IRMideaAC::toString() { + std::string result = ""; +#endif // ARDUINO + result += "Power: "; + if (getPower()) + result += "On"; + else + result += "Off"; + result += ", Mode: " + uint64ToString(getMode()); + switch (getMode()) { + case MIDEA_AC_AUTO: + result += " (AUTO)"; + break; + case MIDEA_AC_COOL: + result += " (COOL)"; + break; + case MIDEA_AC_HEAT: + result += " (HEAT)"; + break; + case MIDEA_AC_DRY: + result += " (DRY)"; + break; + case MIDEA_AC_FAN: + result += " (FAN)"; + break; + default: + result += " (UNKNOWN)"; + } + result += ", Temp: " + uint64ToString(getTemp(true)) + "C/" + + uint64ToString(getTemp(false)) + "F"; + result += ", Fan: " + uint64ToString(getFan()); + switch (getFan()) { + case MIDEA_AC_FAN_AUTO: + result += " (AUTO)"; + break; + case MIDEA_AC_FAN_LOW: + result += " (LOW)"; + break; + case MIDEA_AC_FAN_MED: + result += " (MED)"; + break; + case MIDEA_AC_FAN_HI: + result += " (HI)"; + break; + } + result += ", Sleep: "; + if (getSleep()) + result += "On"; + else + result += "Off"; + return result; +} + +#if DECODE_MIDEA +// Decode the supplied Midea 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 MIDEA_BITS. +// 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::decodeMidea(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; + + uint8_t min_nr_of_messages = 1; + if (strict) { + if (nbits != MIDEA_BITS) + return false; // Not strictly a MIDEA message. + min_nr_of_messages = 2; + } + + // The protocol sends the data normal + inverted, alternating on + // each byte. Hence twice the number of expected data bits. + if (results->rawlen < min_nr_of_messages * (2 * nbits + HEADER + FOOTER) - 1) + return false; // Can't possibly be a valid MIDEA message. + + uint64_t data = 0; + uint64_t inverted = 0; + uint16_t offset = OFFSET_START; + + if (nbits > sizeof(data) * 8) + return false; // We can't possibly capture a Midea packet that big. + + for (uint8_t i = 0; i < min_nr_of_messages; i++) { + // Header + if (!matchMark(results->rawbuf[offset], MIDEA_HDR_MARK)) return false; + // Calculate how long the common tick time is based on the header mark. + uint32_t m_tick = results->rawbuf[offset++] * RAWTICK / + MIDEA_HDR_MARK_TICKS; + if (!matchSpace(results->rawbuf[offset], MIDEA_HDR_SPACE)) return false; + // Calculate how long the common tick time is based on the header space. + uint32_t s_tick = results->rawbuf[offset++] * RAWTICK / + MIDEA_HDR_SPACE_TICKS; + + // Data (Normal) + match_result_t data_result = matchData(&(results->rawbuf[offset]), nbits, + MIDEA_BIT_MARK_TICKS * m_tick, + MIDEA_ONE_SPACE_TICKS * s_tick, + MIDEA_BIT_MARK_TICKS * m_tick, + MIDEA_ZERO_SPACE_TICKS * s_tick, + MIDEA_TOLERANCE); + if (data_result.success == false) return false; + offset += data_result.used; + if (i % 2 == 0) + data = data_result.data; + else + inverted = data_result.data; + + // Footer + if (!matchMark(results->rawbuf[offset++], MIDEA_BIT_MARK_TICKS * m_tick, + MIDEA_TOLERANCE)) + return false; + if (offset < results->rawlen && + !matchAtLeast(results->rawbuf[offset++], MIDEA_MIN_GAP_TICKS * s_tick, + MIDEA_TOLERANCE)) + return false; + } + + + // Compliance + if (strict) { + // Protocol requires a second message with all the data bits inverted. + // We should have checked we got a second message in the previous loop. + // Just need to check it's value is an inverted copy of the first message. + uint64_t mask = (1ULL << MIDEA_BITS) - 1; + if ((data & mask) != ((inverted ^ mask) & mask)) return false; + if (!IRMideaAC::validChecksum(data)) return false; + } + + // Success + results->decode_type = MIDEA; + results->bits = nbits; + results->value = data; + results->address = 0; + results->command = 0; + return true; +} +#endif // DECODE_MIDEA diff --git a/lib/IRremoteESP8266/src/ir_Midea.h b/lib/IRremoteESP8266/src/ir_Midea.h new file mode 100644 index 0000000..692cdf0 --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_Midea.h @@ -0,0 +1,87 @@ +// Copyright 2017 David Conran +#ifndef IR_MIDEA_H_ +#define IR_MIDEA_H_ + +#define __STDC_LIMIT_MACROS +#include +#ifdef ARDUINO +#include +#else +#include +#endif +#include "IRremoteESP8266.h" +#include "IRsend.h" + +// MM MM IIIII DDDDD EEEEEEE AAA +// MMM MMM III DD DD EE AAAAA +// MM MM MM III DD DD EEEEE AA AA +// MM MM III DD DD EE AAAAAAA +// MM MM IIIII DDDDDD EEEEEEE AA AA + +// Midea added by crankyoldgit & bwze +// Ref: +// https://docs.google.com/spreadsheets/d/1TZh4jWrx4h9zzpYUI9aYXMl1fYOiqu-xVuOOMqagxrs/edit?usp=sharing + +// Constants +#define MIDEA_AC_COOL 0U // 0b000 +#define MIDEA_AC_DRY 1U // 0b001 +#define MIDEA_AC_AUTO 2U // 0b010 +#define MIDEA_AC_HEAT 3U // 0b011 +#define MIDEA_AC_FAN 4U // 0b100 +#define MIDEA_AC_POWER 1ULL << 39 +#define MIDEA_AC_SLEEP 1ULL << 38 +#define MIDEA_AC_FAN_AUTO 0U // 0b000 +#define MIDEA_AC_FAN_LOW 1U // 0b001 +#define MIDEA_AC_FAN_MED 2U // 0b010 +#define MIDEA_AC_FAN_HI 3U // 0b011 +#define MIDEA_AC_MIN_TEMP_F 62U // 62F +#define MIDEA_AC_MAX_TEMP_F 86U // 86F +#define MIDEA_AC_MIN_TEMP_C 16U // 16C +#define MIDEA_AC_MAX_TEMP_C 30U // 30C +#define MIDEA_AC_STATE_MASK 0x0000FFFFFFFFFFFFULL +#define MIDEA_AC_TEMP_MASK 0x0000FFFFE0FFFFFFULL +#define MIDEA_AC_FAN_MASK 0x0000FFC7FFFFFFFFULL +#define MIDEA_AC_MODE_MASK 0x0000FFF8FFFFFFFFULL +#define MIDEA_AC_CHECKSUM_MASK 0x0000FFFFFFFFFF00ULL + +class IRMideaAC { + public: + explicit IRMideaAC(uint16_t pin); + + void stateReset(); +#if SEND_MIDEA + void send(); +#endif // SEND_MIDEA + void begin(); + void on(); + void off(); + void setPower(const bool state); + bool getPower(); + void setTemp(const uint8_t temp, const bool useCelsius = false); + uint8_t getTemp(const bool useCelsius = false); + void setFan(const uint8_t fan); + uint8_t getFan(); + void setMode(const uint8_t mode); + uint8_t getMode(); + void setRaw(uint64_t newState); + uint64_t getRaw(); + static bool validChecksum(const uint64_t state); + void setSleep(const bool state); + bool getSleep(); +#ifdef ARDUINO + String toString(); +#else + std::string toString(); +#endif +#ifndef UNIT_TEST + + private: +#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/src/ir_Mitsubishi.cpp b/lib/IRremoteESP8266/src/ir_Mitsubishi.cpp index df8b6e1..ddee663 100644 --- a/lib/IRremoteESP8266/src/ir_Mitsubishi.cpp +++ b/lib/IRremoteESP8266/src/ir_Mitsubishi.cpp @@ -5,7 +5,6 @@ #include #include "IRrecv.h" #include "IRsend.h" -#include "IRtimer.h" #include "IRutils.h" // MMMMM IIIII TTTTT SSSS U U BBBB IIIII SSSS H H IIIII @@ -40,6 +39,16 @@ #define MITSUBISHI_MIN_GAP (MITSUBISHI_MIN_GAP_TICKS * \ MITSUBISHI_TICK) +// Mitsubishi Projector (HC3000) +// Ref: +// https://github.com/markszabo/IRremoteESP8266/issues/441 +#define MITSUBISHI2_HDR_MARK 8400U +#define MITSUBISHI2_HDR_SPACE (MITSUBISHI2_HDR_MARK / 2) +#define MITSUBISHI2_BIT_MARK 560U +#define MITSUBISHI2_ZERO_SPACE 520U +#define MITSUBISHI2_ONE_SPACE (MITSUBISHI2_ZERO_SPACE * 3) +#define MITSUBISHI2_MIN_GAP 28500U + // Mitsubishi A/C // Ref: // https://github.com/r45635/HVAC-IR-Control/blob/master/HVAC_ESP8266/HVAC_ESP8266.ino#L84 @@ -67,24 +76,14 @@ // https://github.com/marcosamarinho/IRremoteESP8266/blob/master/ir_Mitsubishi.cpp // GlobalCache's Control Tower's Mitsubishi TV data. void IRsend::sendMitsubishi(uint64_t data, uint16_t nbits, uint16_t repeat) { - enableIROut(33); // Set IR carrier frequency - IRtimer usecTimer = IRtimer(); - - for (uint16_t i = 0; i <= repeat; i++) { - usecTimer.reset(); - // No header - - // Data - sendData(MITSUBISHI_BIT_MARK, MITSUBISHI_ONE_SPACE, - MITSUBISHI_BIT_MARK, MITSUBISHI_ZERO_SPACE, - data, nbits, true); - // Footer - mark(MITSUBISHI_BIT_MARK); - space(std::max(MITSUBISHI_MIN_COMMAND_LENGTH - usecTimer.elapsed(), - MITSUBISHI_MIN_GAP)); - } + sendGeneric(0, 0, // No Header + MITSUBISHI_BIT_MARK, MITSUBISHI_ONE_SPACE, + MITSUBISHI_BIT_MARK, MITSUBISHI_ZERO_SPACE, + MITSUBISHI_BIT_MARK, MITSUBISHI_MIN_GAP, + MITSUBISHI_MIN_COMMAND_LENGTH, + data, nbits, 33, true, repeat, 50); } -#endif +#endif // SEND_MITSUBISHI #if DECODE_MITSUBISHI // Decode the supplied Mitsubishi message. @@ -152,7 +151,122 @@ bool IRrecv::decodeMitsubishi(decode_results *results, uint16_t nbits, results->command = 0; return true; } -#endif +#endif // DECODE_MITSUBISHI + +#if SEND_MITSUBISHI2 +// Send a Mitsubishi2 message +// +// Args: +// data: Contents of the message to be sent. +// nbits: Nr. of bits of data to be sent. Typically MITSUBISHI_BITS. +// repeat: Nr. of additional times the message is to be sent. +// +// Status: ALPHA / untested. +// +// Notes: +// Based on a Mitsubishi HC3000 projector's remote. +// 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. +// You will need to add a suitable gap yourself. +// Ref: +// https://github.com/markszabo/IRremoteESP8266/issues/441 +void IRsend::sendMitsubishi2(uint64_t data, uint16_t nbits, uint16_t repeat) { + for (uint16_t i = 0; i <= repeat; i++) { + // First half of the data. + sendGeneric(MITSUBISHI2_HDR_MARK, MITSUBISHI2_HDR_SPACE, + MITSUBISHI2_BIT_MARK, MITSUBISHI2_ONE_SPACE, + MITSUBISHI2_BIT_MARK, MITSUBISHI2_ZERO_SPACE, + MITSUBISHI2_BIT_MARK, MITSUBISHI2_HDR_SPACE, + data >> (nbits / 2), nbits / 2, 33, true, 0, 50); + // Second half of the data. + sendGeneric(0, 0, // No header for the second data block + MITSUBISHI2_BIT_MARK, MITSUBISHI2_ONE_SPACE, + MITSUBISHI2_BIT_MARK, MITSUBISHI2_ZERO_SPACE, + MITSUBISHI2_BIT_MARK, MITSUBISHI2_MIN_GAP, + data & ((1 << (nbits / 2)) - 1), nbits / 2, 33, true, 0, 50); + } +} +#endif // SEND_MITSUBISHI2 + +#if DECODE_MITSUBISHI2 +// Decode the supplied Mitsubishi2 message. +// +// Args: +// results: Ptr to the data to decode and where to store the decode result. +// nbits: Nr. of data bits to expect. +// strict: Flag indicating if we should perform strict matching. +// Returns: +// boolean: True if it can decode it, false if it can't. +// +// Status: BETA / Works with simulated data. +// +// Notes: +// Hardware supported: +// * Mitsubishi HC3000 projector's remote. +// +// Ref: +// https://github.com/markszabo/IRremoteESP8266/issues/441 +bool IRrecv::decodeMitsubishi2(decode_results *results, uint16_t nbits, + bool strict) { + if (results->rawlen < 2 * nbits + HEADER + (FOOTER * 2) - 1) + return false; // Shorter than shortest possibly expected. + if (strict && nbits != MITSUBISHI_BITS) + return false; // Request is out of spec. + + uint16_t offset = OFFSET_START; + uint64_t data = 0; + uint16_t actualBits = 0; + + // Header + if (!matchMark(results->rawbuf[offset++], MITSUBISHI2_HDR_MARK)) + return false; + if (!matchSpace(results->rawbuf[offset++], MITSUBISHI2_HDR_SPACE)) + return false; + for (uint8_t i = 1; i <= 2; i++) { + // Data + match_result_t data_result = matchData(&(results->rawbuf[offset]), + nbits / 2, + MITSUBISHI2_BIT_MARK, + MITSUBISHI2_ONE_SPACE, + MITSUBISHI2_BIT_MARK, + MITSUBISHI2_ZERO_SPACE); + if (data_result.success == false) return false; + data <<= nbits / 2; + data += data_result.data; + offset += data_result.used; + actualBits += data_result.used / 2; + + // Footer + if (!matchMark(results->rawbuf[offset++], MITSUBISHI2_BIT_MARK)) + return false; + if (i % 2) { // Every odd data block, we expect a HDR space. + if (!matchSpace(results->rawbuf[offset++], MITSUBISHI2_HDR_SPACE)) + return false; + } else { // Every even data block, we expect Min Gap or end of the message. + if (offset < results->rawlen && + !matchAtLeast(results->rawbuf[offset++], MITSUBISHI2_MIN_GAP)) + return false; + } + } + + + // Compliance + if (actualBits < nbits) + return false; + if (strict && actualBits != nbits) + return false; // Not as we expected. + + // Success + results->decode_type = MITSUBISHI2; + results->bits = actualBits; + results->value = data; + results->address = data >> actualBits / 2; + results->command = data & ((1 << (actualBits / 2)) - 1); + return true; +} +#endif // DECODE_MITSUBISHI2 #if SEND_MITSUBISHI_AC // Send a Mitsubishi A/C message. @@ -170,23 +284,13 @@ void IRsend::sendMitsubishiAC(unsigned char data[], uint16_t nbytes, if (nbytes < MITSUBISHI_AC_STATE_LENGTH) return; // Not enough bytes to send a proper message. - // Set IR carrier frequency - enableIROut(38); - // Mitsubishi AC remote sends the packet twice. - for (uint16_t r = 0; r <= repeat; r++) { - // Header - mark(MITSUBISHI_AC_HDR_MARK); - space(MITSUBISHI_AC_HDR_SPACE); - // Data - for (uint16_t i = 0; i < nbytes; i++) - sendData(MITSUBISHI_AC_BIT_MARK, MITSUBISHI_AC_ONE_SPACE, - MITSUBISHI_AC_BIT_MARK, MITSUBISHI_AC_ZERO_SPACE, - data[i], 8, false); - // Footer - mark(MITSUBISHI_AC_RPT_MARK); - space(MITSUBISHI_AC_RPT_SPACE); - } + sendGeneric(MITSUBISHI_AC_HDR_MARK, MITSUBISHI_AC_HDR_SPACE, + MITSUBISHI_AC_BIT_MARK, MITSUBISHI_AC_ONE_SPACE, + MITSUBISHI_AC_BIT_MARK, MITSUBISHI_AC_ZERO_SPACE, + MITSUBISHI_AC_RPT_MARK, MITSUBISHI_AC_RPT_SPACE, + data, nbytes, 38, false, repeat, 50); } +#endif // SEND_MITSUBISHI_AC // Code to emulate Mitsubishi A/C IR remote control unit. // Inspired and derived from the work done at: @@ -232,11 +336,13 @@ void IRMitsubishiAC::begin() { _irsend.begin(); } +#if SEND_MITSUBISHI_AC // Send the current desired state to the IR LED. void IRMitsubishiAC::send() { checksum(); // Ensure correct checksum before sending. _irsend.sendMitsubishiAC(remote_state); } +#endif // SEND_MITSUBISHI_AC // Return a pointer to the internal state date of the remote. uint8_t* IRMitsubishiAC::getRaw() { @@ -346,4 +452,3 @@ void IRMitsubishiAC::setVane(uint8_t mode) { uint8_t IRMitsubishiAC::getVane() { return ((remote_state[9] & 0b00111000) >> 3); } -#endif diff --git a/lib/IRremoteESP8266/src/ir_Mitsubishi.h b/lib/IRremoteESP8266/src/ir_Mitsubishi.h index e9f5731..ec4c01b 100644 --- a/lib/IRremoteESP8266/src/ir_Mitsubishi.h +++ b/lib/IRremoteESP8266/src/ir_Mitsubishi.h @@ -32,13 +32,14 @@ #define MITSUBISHI_AC_VANE_AUTO 0U #define MITSUBISHI_AC_VANE_AUTO_MOVE 7U -#if SEND_MITSUBISHI_AC class IRMitsubishiAC { public: explicit IRMitsubishiAC(uint16_t pin); void stateReset(); +#if SEND_MITSUBISHI_AC void send(); +#endif // SEND_MITSUBISHI_AC void begin(); void on(); void off(); @@ -60,6 +61,5 @@ class IRMitsubishiAC { IRsend _irsend; }; -#endif #endif // IR_MITSUBISHI_H_ diff --git a/lib/IRremoteESP8266/src/ir_NEC.cpp b/lib/IRremoteESP8266/src/ir_NEC.cpp index a5febe1..8c603f2 100644 --- a/lib/IRremoteESP8266/src/ir_NEC.cpp +++ b/lib/IRremoteESP8266/src/ir_NEC.cpp @@ -6,7 +6,6 @@ #include #include "IRrecv.h" #include "IRsend.h" -#include "IRtimer.h" #include "IRutils.h" // N N EEEEE CCCC @@ -57,29 +56,20 @@ // Ref: // http://www.sbprojects.com/knowledge/ir/nec.php void IRsend::sendNEC(uint64_t data, uint16_t nbits, uint16_t repeat) { - // Set 38kHz IR carrier frequency & a 1/3 (33%) duty cycle. - enableIROut(38, 33); - IRtimer usecs = IRtimer(); - // Header - mark(NEC_HDR_MARK); - space(NEC_HDR_SPACE); - // Data - sendData(NEC_BIT_MARK, NEC_ONE_SPACE, NEC_BIT_MARK, NEC_ZERO_SPACE, - data, nbits, true); - // Footer - mark(NEC_BIT_MARK); - // Gap to next command. - space(std::max(NEC_MIN_GAP, NEC_MIN_COMMAND_LENGTH - usecs.elapsed())); - + sendGeneric(NEC_HDR_MARK, NEC_HDR_SPACE, + NEC_BIT_MARK, NEC_ONE_SPACE, + NEC_BIT_MARK, NEC_ZERO_SPACE, + NEC_BIT_MARK, NEC_MIN_GAP, NEC_MIN_COMMAND_LENGTH, + data, nbits, 38, true, 0, // Repeats are handled later. + 33); // Optional command repeat sequence. - for (uint16_t i = 0; i < repeat; i++) { - usecs.reset(); - mark(NEC_HDR_MARK); - space(NEC_RPT_SPACE); - mark(NEC_BIT_MARK); - // Gap till next command. - space(std::max(NEC_MIN_GAP, NEC_MIN_COMMAND_LENGTH - usecs.elapsed())); - } + if (repeat) + sendGeneric(NEC_HDR_MARK, NEC_RPT_SPACE, + 0, 0, 0, 0, // No actual data sent. + NEC_BIT_MARK, NEC_MIN_GAP, NEC_MIN_COMMAND_LENGTH, + 0, 0, // No data to be sent. + 38, true, repeat - 1, // We've already sent a one message. + 33); } // Calculate the raw NEC data based on address and command. @@ -176,7 +166,7 @@ bool IRrecv::decodeNEC(decode_results *results, uint16_t nbits, bool strict) { // Footer if (!matchMark(results->rawbuf[offset++], NEC_BIT_MARK_TICKS * mark_tick)) return false; - if (offset <= results->rawlen && + if (offset < results->rawlen && !matchAtLeast(results->rawbuf[offset], NEC_MIN_GAP_TICKS * space_tick)) return false; diff --git a/lib/IRremoteESP8266/src/ir_Nikai.cpp b/lib/IRremoteESP8266/src/ir_Nikai.cpp new file mode 100644 index 0000000..cbb6500 --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_Nikai.cpp @@ -0,0 +1,110 @@ +// Copyright 2009 Ken Shirriff +// Copyright 2017 David Conran + +#include +#include "IRrecv.h" +#include "IRsend.h" +#include "IRutils.h" + +// NN NN IIIII KK KK AAA IIIII +// NNN NN III KK KK AAAAA III +// NN N NN III KKKK AA AA III +// NN NNN III KK KK AAAAAAA III +// NN NN IIIII KK KK AA AA IIIII + +// Constants +// Ref: +// https://github.com/markszabo/IRremoteESP8266/issues/309 +#define NIKAI_TICK 500U +#define NIKAI_HDR_MARK_TICKS 8U +#define NIKAI_HDR_MARK (NIKAI_HDR_MARK_TICKS * NIKAI_TICK) +#define NIKAI_HDR_SPACE_TICKS 8U +#define NIKAI_HDR_SPACE (NIKAI_HDR_SPACE_TICKS * NIKAI_TICK) +#define NIKAI_BIT_MARK_TICKS 1U +#define NIKAI_BIT_MARK (NIKAI_BIT_MARK_TICKS * NIKAI_TICK) +#define NIKAI_ONE_SPACE_TICKS 2U +#define NIKAI_ONE_SPACE (NIKAI_ONE_SPACE_TICKS * NIKAI_TICK) +#define NIKAI_ZERO_SPACE_TICKS 4U +#define NIKAI_ZERO_SPACE (NIKAI_ZERO_SPACE_TICKS * NIKAI_TICK) +#define NIKAI_MIN_GAP_TICKS 17U +#define NIKAI_MIN_GAP (NIKAI_MIN_GAP_TICKS * NIKAI_TICK) + + +#if SEND_NIKAI +// Send a Nikai TV formatted message. +// +// Args: +// data: The message to be sent. +// nbits: The bit size of the message being sent. typically NIKAI_BITS. +// repeat: The number of times the message is to be repeated. +// +// Status: STABLE / Working. +// +// Ref: https://github.com/markszabo/IRremoteESP8266/issues/309 +void IRsend::sendNikai(uint64_t data, uint16_t nbits, uint16_t repeat) { + sendGeneric(NIKAI_HDR_MARK, NIKAI_HDR_SPACE, + NIKAI_BIT_MARK, NIKAI_ONE_SPACE, + NIKAI_BIT_MARK, NIKAI_ZERO_SPACE, + NIKAI_BIT_MARK, NIKAI_MIN_GAP, + data, nbits, 38, true, repeat, 33); +} +#endif + +#if DECODE_NIKAI +// Decode the supplied Nikai 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 NIKAI_BITS. +// 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: STABLE / Working. +// +bool IRrecv::decodeNikai(decode_results *results, uint16_t nbits, bool strict) { + if (results->rawlen < 2 * nbits + HEADER + FOOTER - 1) + return false; // Can't possibly be a valid Nikai message. + if (strict && nbits != NIKAI_BITS) + return false; // We expect Nikai to be a certain sized message. + + uint64_t data = 0; + uint16_t offset = OFFSET_START; + + // Header + if (!matchMark(results->rawbuf[offset], NIKAI_HDR_MARK)) return false; + // Calculate how long the common tick time is based on the header mark. + uint32_t m_tick = results->rawbuf[offset++] * RAWTICK / + NIKAI_HDR_MARK_TICKS; + if (!matchSpace(results->rawbuf[offset], NIKAI_HDR_SPACE)) return false; + // Calculate how long the common tick time is based on the header space. + uint32_t s_tick = results->rawbuf[offset++] * RAWTICK / + NIKAI_HDR_SPACE_TICKS; + // Data + match_result_t data_result = matchData(&(results->rawbuf[offset]), nbits, + NIKAI_BIT_MARK_TICKS * m_tick, + NIKAI_ONE_SPACE_TICKS * s_tick, + NIKAI_BIT_MARK_TICKS * m_tick, + NIKAI_ZERO_SPACE_TICKS * s_tick); + if (data_result.success == false) return false; + data = data_result.data; + offset += data_result.used; + // Footer + if (!matchMark(results->rawbuf[offset++], NIKAI_BIT_MARK_TICKS * m_tick)) + return false; + if (offset < results->rawlen && + !matchAtLeast(results->rawbuf[offset], NIKAI_MIN_GAP_TICKS * s_tick)) + return false; + + // Compliance + + // Success + results->bits = nbits; + results->value = data; + results->decode_type = NIKAI; + results->command = 0; + results->address = 0; + return true; +} +#endif diff --git a/lib/IRremoteESP8266/src/ir_Panasonic.cpp b/lib/IRremoteESP8266/src/ir_Panasonic.cpp index fb1f122..687f73c 100644 --- a/lib/IRremoteESP8266/src/ir_Panasonic.cpp +++ b/lib/IRremoteESP8266/src/ir_Panasonic.cpp @@ -4,7 +4,6 @@ #include #include "IRrecv.h" #include "IRsend.h" -#include "IRtimer.h" #include "IRutils.h" // PPPP AAA N N AAA SSSS OOO N N IIIII CCCC @@ -31,7 +30,7 @@ #define PANASONIC_ONE_SPACE (PANASONIC_ONE_SPACE_TICKS * PANASONIC_TICK) #define PANASONIC_ZERO_SPACE_TICKS 1U #define PANASONIC_ZERO_SPACE (PANASONIC_ZERO_SPACE_TICKS * PANASONIC_TICK) -#define PANASONIC_MIN_COMMAND_LENGTH_TICKS 300UL +#define PANASONIC_MIN_COMMAND_LENGTH_TICKS 378UL #define PANASONIC_MIN_COMMAND_LENGTH (PANASONIC_MIN_COMMAND_LENGTH_TICKS * \ PANASONIC_TICK) #define PANASONIC_END_GAP 5000U // See issue #245 @@ -53,24 +52,12 @@ // Note: // This protocol is a modified version of Kaseikyo. void IRsend::sendPanasonic64(uint64_t data, uint16_t nbits, uint16_t repeat) { - enableIROut(36700U); // Set IR carrier frequency of 36.7kHz. - IRtimer usecTimer = IRtimer(); - - for (uint16_t i = 0; i <= repeat; i++) { - usecTimer.reset(); - // Header - mark(PANASONIC_HDR_MARK); - space(PANASONIC_HDR_SPACE); - // Data - sendData(PANASONIC_BIT_MARK, PANASONIC_ONE_SPACE, - PANASONIC_BIT_MARK, PANASONIC_ZERO_SPACE, - data, nbits, true); - // Footer - mark(PANASONIC_BIT_MARK); - space(std::max((uint32_t) PANASONIC_MIN_COMMAND_LENGTH - - usecTimer.elapsed(), - PANASONIC_MIN_GAP)); - } + sendGeneric(PANASONIC_HDR_MARK, PANASONIC_HDR_SPACE, + PANASONIC_BIT_MARK, PANASONIC_ONE_SPACE, + PANASONIC_BIT_MARK, PANASONIC_ZERO_SPACE, + PANASONIC_BIT_MARK, + PANASONIC_MIN_GAP, PANASONIC_MIN_COMMAND_LENGTH, + data, nbits, 36700U, true, repeat, 50); } // Send a Panasonic formatted message. diff --git a/lib/IRremoteESP8266/src/ir_Pronto.cpp b/lib/IRremoteESP8266/src/ir_Pronto.cpp index ca76f08..28e8083 100644 --- a/lib/IRremoteESP8266/src/ir_Pronto.cpp +++ b/lib/IRremoteESP8266/src/ir_Pronto.cpp @@ -2,7 +2,6 @@ #include #include "IRsend.h" -#include "IRtimer.h" // PPPPPP tt // PP PP rr rr oooo nn nnn tt oooo diff --git a/lib/IRremoteESP8266/src/ir_RC5_RC6.cpp b/lib/IRremoteESP8266/src/ir_RC5_RC6.cpp index 65aaf88..8fcd097 100644 --- a/lib/IRremoteESP8266/src/ir_RC5_RC6.cpp +++ b/lib/IRremoteESP8266/src/ir_RC5_RC6.cpp @@ -42,9 +42,8 @@ #define RC6_36_TOGGLE_MASK 0x8000U // (The 16th bit) // Common (getRClevel()) -#define MARK 0U -#define SPACE 1U - +const int16_t kMARK = 0; +const int16_t kSPACE = 1; #if SEND_RC5 // Send a Philips RC-5/RC-5X packet. @@ -282,7 +281,7 @@ void IRsend::sendRC6(uint64_t data, uint16_t nbits, uint16_t repeat) { } #endif // SEND_RC6 -#if (DECODE_RC5 || DECODE_RC6) +#if (DECODE_RC5 || DECODE_RC6 || DECODE_LASERTAG) // Gets one undecoded level at a time from the raw buffer. // The RC5/6 decoding is easier if the data is broken into time intervals. // E.g. if the buffer has MARK for 2 time intervals and SPACE for 1, @@ -300,30 +299,39 @@ void IRsend::sendRC6(uint64_t data, uint16_t nbits, uint16_t repeat) { // Ref: // https://en.wikipedia.org/wiki/Manchester_code int16_t IRrecv::getRClevel(decode_results *results, uint16_t *offset, - uint16_t *used, uint16_t bitTime) { - if (*offset >= results->rawlen) - return SPACE; // After end of recorded buffer, assume SPACE. + uint16_t *used, uint16_t bitTime, + uint8_t tolerance, int16_t excess, uint16_t delta) { + DPRINT("DEBUG: getRClevel: offset = "); + DPRINTLN(uint64ToString(*offset)); + if (*offset >= results->rawlen) { + DPRINTLN("DEBUG: getRClevel: SPACE, past end of rawbuf"); + return kSPACE; // After end of recorded buffer, assume SPACE. + } uint16_t width = results->rawbuf[*offset]; // If the value of offset is odd, it's a MARK. Even, it's a SPACE. - uint16_t val = ((*offset) % 2) ? MARK : SPACE; + uint16_t val = ((*offset) % 2) ? kMARK : kSPACE; // Check to see if we have hit an inter-message gap (> 20ms). - if (val == SPACE && width > 20000) - return SPACE; - int16_t correction = (val == MARK) ? MARK_EXCESS : -MARK_EXCESS; + if (val == kSPACE && width > 20000 - delta) { + DPRINTLN("DEBUG: getRClevel: SPACE, hit end of mesg gap."); + return kSPACE; + } + int16_t correction = (val == kMARK) ? excess : -excess; // Calculate the look-ahead for our current position in the buffer. uint16_t avail; // Note: We want to match in greedy order as the other way leads to // mismatches due to overlaps induced by the correction and tolerance // values. - if (match(width, 3 * bitTime + correction)) + if (match(width, 3 * bitTime + correction, tolerance, delta)) { avail = 3; - else if (match(width, 2 * bitTime + correction)) + } else if (match(width, 2 * bitTime + correction, tolerance, delta)) { avail = 2; - else if (match(width, bitTime + correction)) + } else if (match(width, bitTime + correction, tolerance, delta)) { avail = 1; - else + } else { + DPRINTLN("DEBUG: getRClevel: Unexpected width. Exiting."); return -1; // The width is not what we expected. + } (*used)++; // Count another one of the avail slots as used. if (*used >= avail) { // Are we out of look-ahead/avail slots? @@ -331,10 +339,15 @@ int16_t IRrecv::getRClevel(decode_results *results, uint16_t *offset, *used = 0; (*offset)++; } + if (val == kMARK) { + DPRINTLN("DEBUG: getRClevel: MARK"); + } else { + DPRINTLN("DEBUG: getRClevel: SPACE"); + } return val; } -#endif // (DECODE_RC5 || DECODE_RC6) +#endif // (DECODE_RC5 || DECODE_RC6 || DECODE_LASERTAG) #if DECODE_RC5 // Decode the supplied RC-5/RC5X message. @@ -371,14 +384,14 @@ bool IRrecv::decodeRC5(decode_results *results, uint16_t nbits, bool strict) { // Header // Get start bit #1. - if (getRClevel(results, &offset, &used, RC5_T1) != MARK) return false; + if (getRClevel(results, &offset, &used, RC5_T1) != kMARK) return false; // Get field/start bit #2 (inverted bit-7 of the command if RC-5X protocol) uint16_t actual_bits = 1; int16_t levelA = getRClevel(results, &offset, &used, RC5_T1); int16_t levelB = getRClevel(results, &offset, &used, RC5_T1); - if (levelA == SPACE && levelB == MARK) { // Matched a 1. + if (levelA == kSPACE && levelB == kMARK) { // Matched a 1. is_rc5x = false; - } else if (levelA == MARK && levelB == SPACE) { // Matched a 0. + } else if (levelA == kMARK && levelB == kSPACE) { // Matched a 0. if (nbits <= RC5_BITS) return false; // Field bit must be '1' for RC5. is_rc5x = true; data = 1; @@ -390,9 +403,9 @@ bool IRrecv::decodeRC5(decode_results *results, uint16_t nbits, bool strict) { for (; offset < results->rawlen; actual_bits++) { int16_t levelA = getRClevel(results, &offset, &used, RC5_T1); int16_t levelB = getRClevel(results, &offset, &used, RC5_T1); - if (levelA == SPACE && levelB == MARK) + if (levelA == kSPACE && levelB == kMARK) data = (data << 1) | 1; // 1 - else if (levelA == MARK && levelB == SPACE) + else if (levelA == kMARK && levelB == kSPACE) data <<= 1; // 0 else break; @@ -472,8 +485,8 @@ bool IRrecv::decodeRC6(decode_results *results, uint16_t nbits, bool strict) { uint16_t used = 0; // Get the start bit. e.g. 1. - if (getRClevel(results, &offset, &used, tick) != MARK) return false; - if (getRClevel(results, &offset, &used, tick) != SPACE) return false; + if (getRClevel(results, &offset, &used, tick) != kMARK) return false; + if (getRClevel(results, &offset, &used, tick) != kSPACE) return false; uint16_t actual_bits; uint64_t data = 0; @@ -491,9 +504,9 @@ bool IRrecv::decodeRC6(decode_results *results, uint16_t nbits, bool strict) { if (actual_bits == 3 && levelB != getRClevel(results, &offset, &used, tick)) return false; - if (levelA == MARK && levelB == SPACE) // reversed compared to RC5 + if (levelA == kMARK && levelB == kSPACE) // reversed compared to RC5 data = (data << 1) | 1; // 1 - else if (levelA == SPACE && levelB == MARK) + else if (levelA == kSPACE && levelB == kMARK) data <<= 1; // 0 else break; diff --git a/lib/IRremoteESP8266/src/ir_RCMM.cpp b/lib/IRremoteESP8266/src/ir_RCMM.cpp index 362702b..3d8d244 100644 --- a/lib/IRremoteESP8266/src/ir_RCMM.cpp +++ b/lib/IRremoteESP8266/src/ir_RCMM.cpp @@ -107,7 +107,7 @@ bool IRrecv::decodeRCMM(decode_results *results, uint16_t nbits, bool strict) { return false; // Not enough entries to ever be RCMM. // Calc the maximum size in bits, the message can be, or that we can accept. - int16_t maxBitSize = std::min((uint16_t) results->rawlen - 4, + int16_t maxBitSize = std::min((uint16_t) results->rawlen - 5, (uint16_t) sizeof(data) * 8); // Compliance if (strict) { diff --git a/lib/IRremoteESP8266/src/ir_Samsung.cpp b/lib/IRremoteESP8266/src/ir_Samsung.cpp index dbd94bc..a7acead 100644 --- a/lib/IRremoteESP8266/src/ir_Samsung.cpp +++ b/lib/IRremoteESP8266/src/ir_Samsung.cpp @@ -4,7 +4,6 @@ #include #include "IRrecv.h" #include "IRsend.h" -#include "IRtimer.h" #include "IRutils.h" // SSSS AAA MMM SSSS U U N N GGGG @@ -57,24 +56,12 @@ // // Ref: http://elektrolab.wz.cz/katalog/samsung_protocol.pdf void IRsend::sendSAMSUNG(uint64_t data, uint16_t nbits, uint16_t repeat) { - // Set 38kHz IR carrier frequency & a 1/3 (33%) duty cycle. - enableIROut(38, 33); - IRtimer usecTimer = IRtimer(); - // We always send a message, even for repeat=0, hence '<= repeat'. - for (uint16_t i=0; i <= repeat; i++) { - usecTimer.reset(); - // Header - mark(SAMSUNG_HDR_MARK); - space(SAMSUNG_HDR_SPACE); - // Data - sendData(SAMSUNG_BIT_MARK, SAMSUNG_ONE_SPACE, SAMSUNG_BIT_MARK, - SAMSUNG_ZERO_SPACE, data, nbits, true); - // Footer - mark(SAMSUNG_BIT_MARK); - space(std::max((uint32_t) SAMSUNG_MIN_GAP, - (uint32_t) (SAMSUNG_MIN_MESSAGE_LENGTH - - usecTimer.elapsed()))); - } + sendGeneric(SAMSUNG_HDR_MARK, SAMSUNG_HDR_SPACE, + SAMSUNG_BIT_MARK, SAMSUNG_ONE_SPACE, + SAMSUNG_BIT_MARK, SAMSUNG_ZERO_SPACE, + SAMSUNG_BIT_MARK, + SAMSUNG_MIN_GAP, SAMSUNG_MIN_MESSAGE_LENGTH, + data, nbits, 38, true, repeat, 33); } // Construct a raw Samsung message from the supplied customer(address) & diff --git a/lib/IRremoteESP8266/src/ir_Sanyo.cpp b/lib/IRremoteESP8266/src/ir_Sanyo.cpp index cbb13a3..fc9d64f 100644 --- a/lib/IRremoteESP8266/src/ir_Sanyo.cpp +++ b/lib/IRremoteESP8266/src/ir_Sanyo.cpp @@ -5,7 +5,6 @@ #include #include "IRrecv.h" #include "IRsend.h" -#include "IRtimer.h" // SSSS AAA N N Y Y OOO // S A A NN N Y Y O O diff --git a/lib/IRremoteESP8266/src/ir_Sharp.cpp b/lib/IRremoteESP8266/src/ir_Sharp.cpp index 1a0bb35..982838d 100644 --- a/lib/IRremoteESP8266/src/ir_Sharp.cpp +++ b/lib/IRremoteESP8266/src/ir_Sharp.cpp @@ -4,7 +4,6 @@ #include #include "IRrecv.h" #include "IRsend.h" -#include "IRtimer.h" #include "IRutils.h" // SSSS H H AAA RRRR PPPP @@ -13,6 +12,11 @@ // S H H A A R R P // SSSS H H A A R R P +// Equipment it seems compatible with: +// * Sharp LC-52D62U +// * +// + // Constants // period time = 1/38000Hz = 26.316 microseconds. // Ref: @@ -56,25 +60,18 @@ // http://www.mwftr.com/ucF08/LEC14%20PIC%20IR.pdf // http://www.hifi-remote.com/johnsfine/DecodeIR.html#Sharp void IRsend::sendSharpRaw(uint64_t data, uint16_t nbits, uint16_t repeat) { - // Set 38kHz IR carrier frequency & a 1/3 (33%) duty cycle. - enableIROut(38, 33); - for (uint16_t i = 0; i <= repeat; i++) { // Protocol demands that the data be sent twice; once normally, // then with all but the address bits inverted. // Note: Previously this used to be performed 3 times (normal, inverted, // normal), however all data points to that being incorrect. for (uint8_t n = 0; n < 2; n++) { - // No Header - - // Data - sendData(SHARP_BIT_MARK, SHARP_ONE_SPACE, - SHARP_BIT_MARK, SHARP_ZERO_SPACE, - data, nbits, true); - // Footer - mark(SHARP_BIT_MARK); - space(SHARP_GAP); - + sendGeneric(0, 0, // No Header + SHARP_BIT_MARK, SHARP_ONE_SPACE, + SHARP_BIT_MARK, SHARP_ZERO_SPACE, + SHARP_BIT_MARK, SHARP_GAP, + data, nbits, 38, true, 0, // Repeats are handled already. + 33); // Invert the data per protocol. This is always called twice, so it's // retured to original upon exiting the inner loop. data ^= SHARP_TOGGLE_MASK; @@ -165,7 +162,7 @@ void IRsend::sendSharp(uint16_t address, uint16_t command, uint16_t nbits, // Returns: // boolean: True if it can decode it, false if it can't. // -// Status: BETA / Should work okay. +// Status: STABLE / Working fine. // // Note: // This procedure returns a value suitable for use in sendSharpRaw(). diff --git a/lib/IRremoteESP8266/src/ir_Sony.cpp b/lib/IRremoteESP8266/src/ir_Sony.cpp index fd2652d..132c18a 100644 --- a/lib/IRremoteESP8266/src/ir_Sony.cpp +++ b/lib/IRremoteESP8266/src/ir_Sony.cpp @@ -5,7 +5,6 @@ #include #include "IRrecv.h" #include "IRsend.h" -#include "IRtimer.h" #include "IRutils.h" // SSSS OOO N N Y Y @@ -51,24 +50,12 @@ // Ref: // http://www.sbprojects.com/knowledge/ir/sirc.php void IRsend::sendSony(uint64_t data, uint16_t nbits, uint16_t repeat) { - // Sony devices use a 40kHz IR carrier frequency & a 1/3 (33%) duty cycle. - enableIROut(40, 33); - IRtimer usecs = IRtimer(); - - for (uint16_t i = 0; i <= repeat; i++) { // Typically loop 3 or more times. - usecs.reset(); - // Header - mark(SONY_HDR_MARK); - space(SONY_SPACE); - // Data - sendData(SONY_ONE_MARK, SONY_SPACE, SONY_ZERO_MARK, SONY_SPACE, - data, nbits, true); - // Footer - // The Sony protocol requires us to wait 45ms from start of a code to the - // start of the next one. A 10ms minimum gap is also required. - space(std::max(SONY_MIN_GAP, SONY_RPT_LENGTH - usecs.elapsed())); - } - // A space() is always performed last, so no need to turn off the LED. + sendGeneric(SONY_HDR_MARK, SONY_SPACE, + SONY_ONE_MARK, SONY_SPACE, + SONY_ZERO_MARK, SONY_SPACE, + 0, // No Footer mark. + SONY_MIN_GAP, SONY_RPT_LENGTH, + data, nbits, 40, true, repeat, 33); } // Convert Sony/SIRC command, address, & extended bits into sendSony format. diff --git a/lib/IRremoteESP8266/src/ir_Toshiba.cpp b/lib/IRremoteESP8266/src/ir_Toshiba.cpp new file mode 100644 index 0000000..bea29ce --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_Toshiba.cpp @@ -0,0 +1,349 @@ +// Copyright 2017 David Conran + +#include "ir_Toshiba.h" +#include +#ifndef ARDUINO +#include +#endif +#include "IRrecv.h" +#include "IRsend.h" +#include "IRutils.h" + +// TTTTTTT OOOOO SSSSS HH HH IIIII BBBBB AAA +// TTT OO OO SS HH HH III BB B AAAAA +// TTT OO OO SSSSS HHHHHHH III BBBBBB AA AA +// TTT OO OO SS HH HH III BB BB AAAAAAA +// TTT OOOO0 SSSSS HH HH IIIII BBBBBB AA AA + +// Toshiba A/C support added by David Conran +// +// Equipment it seems compatible with: +// * Toshiba RAS-B13N3KV2 / Akita EVO II +// * Toshiba RAS-B13N3KVP-E, RAS 18SKP-ES +// * Toshiba WH-TA04NE, WC-L03SE +// * + +// Constants + +// Toshiba A/C +// Ref: +// https://github.com/r45635/HVAC-IR-Control/blob/master/HVAC_ESP8266/HVAC_ESP8266T.ino#L77 +#define TOSHIBA_AC_HDR_MARK 4400U +#define TOSHIBA_AC_HDR_SPACE 4300U +#define TOSHIBA_AC_BIT_MARK 543U +#define TOSHIBA_AC_ONE_SPACE 1623U +#define TOSHIBA_AC_ZERO_SPACE 472U +#define TOSHIBA_AC_MIN_GAP 7048U + +#if SEND_TOSHIBA_AC +// Send a Toshiba A/C message. +// +// Args: +// data: An array of bytes containing the IR command. +// nbytes: Nr. of bytes of data in the array. (>=TOSHIBA_AC_STATE_LENGTH) +// repeat: Nr. of times the message is to be repeated. +// (Default = TOSHIBA_AC_MIN_REPEAT). +// +// Status: StABLE / Working. +// +void IRsend::sendToshibaAC(unsigned char data[], uint16_t nbytes, + uint16_t repeat) { + if (nbytes < TOSHIBA_AC_STATE_LENGTH) + return; // Not enough bytes to send a proper message. + sendGeneric(TOSHIBA_AC_HDR_MARK, TOSHIBA_AC_HDR_SPACE, + TOSHIBA_AC_BIT_MARK, TOSHIBA_AC_ONE_SPACE, + TOSHIBA_AC_BIT_MARK, TOSHIBA_AC_ZERO_SPACE, + TOSHIBA_AC_BIT_MARK, TOSHIBA_AC_MIN_GAP, + data, nbytes, 38, true, repeat, 50); +} +#endif // SEND_TOSHIBA_AC + +// Code to emulate Toshiba A/C IR remote control unit. +// Inspired and derived from the work done at: +// https://github.com/r45635/HVAC-IR-Control +// +// Status: STABLE / Working. +// +// Initialise the object. +IRToshibaAC::IRToshibaAC(uint16_t pin) : _irsend(pin) { + stateReset(); +} + +// Reset the state of the remote to a known good state/sequence. +void IRToshibaAC::stateReset() { + // The state of the IR remote in IR code form. + // Known good state obtained from: + // https://github.com/r45635/HVAC-IR-Control/blob/master/HVAC_ESP8266/HVAC_ESP8266T.ino#L103 + // Note: Can't use the following because it requires -std=c++11 + // uint8_t remote_state[TOSHIBA_AC_STATE_LENGTH] = { + // 0xF2, 0x0D, 0x03, 0xFC, 0x01, 0x00, 0x00, 0x00, 0x00 }; + remote_state[0] = 0xF2; + remote_state[1] = 0x0D; + remote_state[2] = 0x03; + remote_state[3] = 0xFC; + remote_state[4] = 0x01; + for (uint8_t i = 5; i < TOSHIBA_AC_STATE_LENGTH; i++) + remote_state[i] = 0; + mode_state = remote_state[6] & 0b00000011; + checksum(); // Calculate the checksum +} + +// Configure the pin for output. +void IRToshibaAC::begin() { + _irsend.begin(); +} + +#if SEND_TOSHIBA_AC +// Send the current desired state to the IR LED. +void IRToshibaAC::send() { + checksum(); // Ensure correct checksum before sending. + _irsend.sendToshibaAC(remote_state); +} +#endif // SEND_TOSHIBA_AC + +// Return a pointer to the internal state date of the remote. +uint8_t* IRToshibaAC::getRaw() { + checksum(); + return remote_state; +} + +// Override the internal state with the new state. +void IRToshibaAC::setRaw(uint8_t newState[]) { + for (uint8_t i = 0; i < TOSHIBA_AC_STATE_LENGTH; i++) { + remote_state[i] = newState[i]; + } + mode_state = getMode(true); +} + +// 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 IRToshibaAC::calcChecksum(const uint8_t state[], + const uint16_t length) { + uint8_t checksum = 0; + // Only calculate it for valid lengths. + if (length > 1) { + // Checksum is simple XOR of all bytes except the last one. + for (uint8_t i = 0; i < length - 1; i++) + checksum ^= state[i]; + } + return checksum; +} + +// 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 IRToshibaAC::validChecksum(const uint8_t state[], + const uint16_t length) { + return (length > 1 && state[length - 1] == calcChecksum(state, length)); +} + +// Calculate & set the checksum for the current internal state of the remote. +void IRToshibaAC::checksum(const uint16_t length) { + // Stored the checksum value in the last byte. + if (length > 1) + remote_state[length - 1] = calcChecksum(remote_state, length); +} + +// Set the requested power state of the A/C to off. +void IRToshibaAC::on() { + // state = ON; + remote_state[6] &= ~TOSHIBA_AC_POWER; + setMode(mode_state); +} + +// Set the requested power state of the A/C to off. +void IRToshibaAC::off() { + // state = OFF; + remote_state[6] |= (TOSHIBA_AC_POWER | 0b00000011); +} + +// Set the requested power state of the A/C. +void IRToshibaAC::setPower(bool state) { + if (state) + on(); + else + off(); +} + +// Return the requested power state of the A/C. +bool IRToshibaAC::getPower() { + return((remote_state[6] & TOSHIBA_AC_POWER) == 0); +} + +// Set the temp. in deg C +void IRToshibaAC::setTemp(uint8_t temp) { + temp = std::max((uint8_t) TOSHIBA_AC_MIN_TEMP, temp); + temp = std::min((uint8_t) TOSHIBA_AC_MAX_TEMP, temp); + remote_state[5] = (temp - TOSHIBA_AC_MIN_TEMP) << 4; +} + +// Return the set temp. in deg C +uint8_t IRToshibaAC::getTemp() { + return((remote_state[5] >> 4) + TOSHIBA_AC_MIN_TEMP); +} + +// Set the speed of the fan, 0-5. +// 0 is auto, 1-5 is the speed, 5 is Max. +void IRToshibaAC::setFan(uint8_t fan) { + // Bounds check + if (fan > TOSHIBA_AC_FAN_MAX) + fan = TOSHIBA_AC_FAN_MAX; // Set the fan to maximum if out of range. + if (fan > TOSHIBA_AC_FAN_AUTO) fan++; + remote_state[6] &= 0b00011111; // Clear the previous fan state + remote_state[6] |= (fan << 5); +} + +// Return the requested state of the unit's fan. +uint8_t IRToshibaAC::getFan() { + uint8_t fan = remote_state[6] >> 5; + if (fan == TOSHIBA_AC_FAN_AUTO) return TOSHIBA_AC_FAN_AUTO; + return --fan; +} + +// Get the requested climate operation mode of the a/c unit. +// Args: +// useRaw: Indicate to get the mode from the state array. (Default: false) +// Returns: +// A uint8_t containing the A/C mode. +uint8_t IRToshibaAC::getMode(bool useRaw) { + if (useRaw) + return (remote_state[6] & 0b00000011); + else + return mode_state; +} + +// Set the requested climate operation mode of the a/c unit. +void IRToshibaAC::setMode(uint8_t mode) { + // If we get an unexpected mode, default to AUTO. + switch (mode) { + case TOSHIBA_AC_AUTO: break; + case TOSHIBA_AC_COOL: break; + case TOSHIBA_AC_DRY: break; + case TOSHIBA_AC_HEAT: break; + default: mode = TOSHIBA_AC_AUTO; + } + mode_state = mode; + // Only adjust the remote_state if we have power set to on. + if (getPower()) { + remote_state[6] &= 0b11111100; // Clear the previous mode. + remote_state[6] |= mode_state; + } +} + +// Convert the internal state into a human readable string. +#ifdef ARDUINO +String IRToshibaAC::toString() { + String result = ""; +#else +std::string IRToshibaAC::toString() { + std::string result = ""; +#endif // ARDUINO + result += "Power: "; + if (getPower()) + result += "On"; + else + result += "Off"; + result += ", Mode: " + uint64ToString(getMode()); + switch (getMode()) { + case TOSHIBA_AC_AUTO: + result += " (AUTO)"; + break; + case TOSHIBA_AC_COOL: + result += " (COOL)"; + break; + case TOSHIBA_AC_HEAT: + result += " (HEAT)"; + break; + case TOSHIBA_AC_DRY: + result += " (DRY)"; + break; + default: + result += " (UNKNOWN)"; + } + result += ", Temp: " + uint64ToString(getTemp()) + "C"; + result += ", Fan: " + uint64ToString(getFan()); + switch (getFan()) { + case TOSHIBA_AC_FAN_AUTO: + result += " (AUTO)"; + break; + case TOSHIBA_AC_FAN_MAX: + result += " (MAX)"; + break; + } + return result; +} + +#if DECODE_TOSHIBA_AC +// Decode a Toshiba AC IR message if possible. +// Places successful decode information in the results pointer. +// Args: +// results: Ptr to the data to decode and where to store the decode result. +// nbits: The number of data bits to expect. Typically TOSHIBA_AC_BITS. +// 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: STABLE / Working. +// +// Ref: +// +bool IRrecv::decodeToshibaAC(decode_results *results, uint16_t nbits, + bool strict) { + uint16_t offset = OFFSET_START; + uint16_t dataBitsSoFar = 0; + + // Have we got enough data to successfully decode? + if (results->rawlen < TOSHIBA_AC_BITS + HEADER + FOOTER - 1) + return false; // Can't possibly be a valid message. + + + // Compliance + if (strict && nbits != TOSHIBA_AC_BITS) + return false; // Must be called with the correct nr. of bytes. + + // Header + if (!matchMark(results->rawbuf[offset++], TOSHIBA_AC_HDR_MARK)) + return false; + if (!matchSpace(results->rawbuf[offset++], TOSHIBA_AC_HDR_SPACE)) + return false; + + // Data + for (uint8_t i = 0; i < TOSHIBA_AC_STATE_LENGTH; i++) { + // Read a byte's worth of data. + match_result_t data_result = matchData(&(results->rawbuf[offset]), 8, + TOSHIBA_AC_BIT_MARK, + TOSHIBA_AC_ONE_SPACE, + TOSHIBA_AC_BIT_MARK, + TOSHIBA_AC_ZERO_SPACE); + if (data_result.success == false) return false; // Fail + dataBitsSoFar += 8; + results->state[i] = (uint8_t) data_result.data; + offset += data_result.used; + } + + // Footer + if (!matchMark(results->rawbuf[offset++], TOSHIBA_AC_BIT_MARK)) return false; + if (!matchSpace(results->rawbuf[offset++], TOSHIBA_AC_MIN_GAP)) return false; + + // Compliance + if (strict) { + // Check that the checksum of the message is correct. + if (!IRToshibaAC::validChecksum(results->state)) return false; + } + + // Success + results->decode_type = TOSHIBA_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 // DECODE_TOSHIBA_AC diff --git a/lib/IRremoteESP8266/src/ir_Toshiba.h b/lib/IRremoteESP8266/src/ir_Toshiba.h new file mode 100644 index 0000000..a0c0df0 --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_Toshiba.h @@ -0,0 +1,74 @@ +// Copyright 2017 David Conran +#ifndef IR_TOSHIBA_H_ +#define IR_TOSHIBA_H_ + +#define __STDC_LIMIT_MACROS +#include +#ifdef ARDUINO +#include +#else +#include +#endif +#include "IRremoteESP8266.h" +#include "IRsend.h" + +// TTTTTTT OOOOO SSSSS HH HH IIIII BBBBB AAA +// TTT OO OO SS HH HH III BB B AAAAA +// TTT OO OO SSSSS HHHHHHH III BBBBBB AA AA +// TTT OO OO SS HH HH III BB BB AAAAAAA +// TTT OOOO0 SSSSS HH HH IIIII BBBBBB AA AA + +// Toshiba A/C support added by David Conran + +// Constants +#define TOSHIBA_AC_AUTO 0U +#define TOSHIBA_AC_COOL 1U +#define TOSHIBA_AC_DRY 2U +#define TOSHIBA_AC_HEAT 3U +#define TOSHIBA_AC_POWER 4U +#define TOSHIBA_AC_FAN_AUTO 0U +#define TOSHIBA_AC_FAN_MAX 5U +#define TOSHIBA_AC_MIN_TEMP 17U // 17C +#define TOSHIBA_AC_MAX_TEMP 30U // 30C + +class IRToshibaAC { + public: + explicit IRToshibaAC(uint16_t pin); + + void stateReset(); +#if SEND_TOSHIBA_AC + void send(); +#endif // SEND_TOSHIBA_AC + 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(); + void setMode(uint8_t mode); + uint8_t getMode(bool useRaw = false); + void setRaw(uint8_t newState[]); + uint8_t* getRaw(); + static bool validChecksum(const uint8_t state[], + const uint16_t length = TOSHIBA_AC_STATE_LENGTH); +#ifdef ARDUINO + String toString(); +#else + std::string toString(); +#endif +#ifndef UNIT_TEST + + private: +#endif + uint8_t remote_state[TOSHIBA_AC_STATE_LENGTH]; + void checksum(const uint16_t length = TOSHIBA_AC_STATE_LENGTH); + static uint8_t calcChecksum(const uint8_t state[], + const uint16_t length = TOSHIBA_AC_STATE_LENGTH); + uint8_t mode_state; + IRsend _irsend; +}; + +#endif // IR_TOSHIBA_H_ diff --git a/lib/IRremoteESP8266/src/ir_Trotec.cpp b/lib/IRremoteESP8266/src/ir_Trotec.cpp index ef8e9ef..6ecfcd8 100644 --- a/lib/IRremoteESP8266/src/ir_Trotec.cpp +++ b/lib/IRremoteESP8266/src/ir_Trotec.cpp @@ -21,25 +21,20 @@ void IRsend::sendTrotec(unsigned char data[], uint16_t nbytes, if (nbytes < TROTEC_COMMAND_LENGTH) return; - enableIROut(36); - for (uint16_t r = 0; r <= repeat; r++) { - // Header - mark(TROTEC_HDR_MARK); - space(TROTEC_HDR_SPACE); - - // Data - for (uint16_t i = 0; i < nbytes; i++) - sendData(TROTEC_ONE_MARK, TROTEC_ONE_SPACE, TROTEC_ZERO_MARK, - TROTEC_ZERO_SPACE, data[i], 8, false); - - // Footer - mark(TROTEC_ONE_MARK); - space(TROTEC_GAP); + sendGeneric(TROTEC_HDR_MARK, TROTEC_HDR_SPACE, + TROTEC_ONE_MARK, TROTEC_ONE_SPACE, + TROTEC_ZERO_MARK, TROTEC_ZERO_SPACE, + TROTEC_ONE_MARK, TROTEC_GAP, + data, nbytes, 36, false, 0, // Repeats handled elsewhere + 50); + // More footer + enableIROut(36); mark(TROTEC_ONE_MARK); space(TROTEC_GAP_END); } } +#endif // SEND_TROTEC IRTrotecESP::IRTrotecESP(uint16_t pin) : _irsend(pin) { stateReset(); @@ -49,10 +44,12 @@ 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; @@ -147,5 +144,3 @@ void IRTrotecESP::setTimer(uint8_t timer) { uint8_t IRTrotecESP::getTimer() { return trotec[6]; } - -#endif // SEND_TROTEC diff --git a/lib/IRremoteESP8266/src/ir_Trotec.h b/lib/IRremoteESP8266/src/ir_Trotec.h index 01ac13c..a02ab24 100644 --- a/lib/IRremoteESP8266/src/ir_Trotec.h +++ b/lib/IRremoteESP8266/src/ir_Trotec.h @@ -40,13 +40,14 @@ #define TROTEC_MIN_TIMER 0 #define TROTEC_MAX_TIMER 23 -#if SEND_TROTEC class IRTrotecESP { public: explicit IRTrotecESP(uint16_t pin); +#if SEND_TROTEC void send(); +#endif // SEND_TROTEC void begin(); void setPower(bool state); @@ -75,6 +76,5 @@ class IRTrotecESP { void checksum(); IRsend _irsend; }; -#endif #endif // IR_TROTEC_H_ diff --git a/lib/IRremoteESP8266/src/ir_Whynter.cpp b/lib/IRremoteESP8266/src/ir_Whynter.cpp index 4efb2c5..2306343 100644 --- a/lib/IRremoteESP8266/src/ir_Whynter.cpp +++ b/lib/IRremoteESP8266/src/ir_Whynter.cpp @@ -4,7 +4,6 @@ #include #include "IRrecv.h" #include "IRsend.h" -#include "IRtimer.h" #include "IRutils.h" // W W H H Y Y N N TTTTT EEEEE RRRRR @@ -54,22 +53,19 @@ void IRsend::sendWhynter(uint64_t data, uint16_t nbits, uint16_t repeat) { // Set IR carrier frequency enableIROut(38); - IRtimer usecTimer = IRtimer(); for (uint16_t i = 0; i <= repeat; i++) { - usecTimer.reset(); - // Header + // (Pre-)Header mark(WHYNTER_BIT_MARK); space(WHYNTER_ZERO_SPACE); - mark(WHYNTER_HDR_MARK); - space(WHYNTER_HDR_SPACE); - // Data - sendData(WHYNTER_BIT_MARK, WHYNTER_ONE_SPACE, WHYNTER_BIT_MARK, - WHYNTER_ZERO_SPACE, data, nbits, true); - // Footer - mark(WHYNTER_BIT_MARK); - space(std::max(WHYNTER_MIN_COMMAND_LENGTH - usecTimer.elapsed(), - WHYNTER_MIN_GAP)); + sendGeneric(WHYNTER_HDR_MARK, WHYNTER_HDR_SPACE, + WHYNTER_BIT_MARK, WHYNTER_ONE_SPACE, + WHYNTER_BIT_MARK, WHYNTER_ZERO_SPACE, + WHYNTER_BIT_MARK, WHYNTER_MIN_GAP, + WHYNTER_MIN_COMMAND_LENGTH - (WHYNTER_BIT_MARK + + WHYNTER_ZERO_SPACE), + data, nbits, 38, true, 0, // Repeats are already handled. + 50); } } #endif diff --git a/lib/IRremoteESP8266/test/IRrecv_test.cpp b/lib/IRremoteESP8266/test/IRrecv_test.cpp index 4b22656..b682508 100644 --- a/lib/IRremoteESP8266/test/IRrecv_test.cpp +++ b/lib/IRremoteESP8266/test/IRrecv_test.cpp @@ -1,5 +1,6 @@ // Copyright 2017 David Conran +#include "IRrecv_test.h" #include "IRremoteESP8266.h" #include "IRrecv.h" #include "IRsend.h" @@ -446,3 +447,128 @@ TEST(TestDecode, DecodeAiwa) { EXPECT_EQ(AIWA_RC_T501_BITS, irsend.capture.bits); EXPECT_EQ(0x7F, irsend.capture.value); } + +// Test matchData() on space encoded data. +TEST(TestMatchData, SpaceEncoded) { + IRsendTest irsend(0); + IRrecv irrecv(1); + irsend.begin(); + + uint16_t space_encoded_raw[11] = { + 500, 500, + 500, 1500, + 499, 499, + 501, 1501, + 499, 1490, + 500 + }; + match_result_t result; + + irsend.reset(); + irsend.sendRaw(space_encoded_raw, 11, 38000); + irsend.makeDecodeResult(); + result = irrecv.matchData(irsend.capture.rawbuf + 1, 5, 500, 1500, 500, 500); + ASSERT_TRUE(result.success); + EXPECT_EQ(0b01011, result.data); + EXPECT_EQ(10, result.used); + + irsend.reset(); + irsend.sendRaw(space_encoded_raw, 11, 38000); + irsend.makeDecodeResult(); + result = irrecv.matchData(irsend.capture.rawbuf + 1, 5, 500, 1000, 500, 500); + ASSERT_FALSE(result.success); +} + +// Test matchData() on mark encoded data. +TEST(TestMatchData, MarkEncoded) { + IRsendTest irsend(0); + IRrecv irrecv(1); + irsend.begin(); + + uint16_t mark_encoded_raw[11] = { + 500, 500, + 1500, 500, + 499, 499, + 1501, 501, + 1499, 490, + 500 + }; + match_result_t result; + + irsend.reset(); + irsend.sendRaw(mark_encoded_raw, 11, 38000); + irsend.makeDecodeResult(); + result = irrecv.matchData(irsend.capture.rawbuf + 1, 5, 1500, 500, 500, 500); + ASSERT_TRUE(result.success); + EXPECT_EQ(0b01011, result.data); + EXPECT_EQ(10, result.used); + + irsend.reset(); + irsend.sendRaw(mark_encoded_raw, 11, 38000); + irsend.makeDecodeResult(); + result = irrecv.matchData(irsend.capture.rawbuf + 1, 5, 1000, 500, 500, 500); + ASSERT_FALSE(result.success); +} + +// Test matchData() on "equal total bit time" encoded data. +TEST(TestMatchData, EqualTotalBitTimeEncoded) { + IRsendTest irsend(0); + IRrecv irrecv(1); + irsend.begin(); + + uint16_t equal_encoded_raw[11] = { + 500, 1500, + 1500, 500, + 499, 1499, + 1501, 501, + 1499, 490, + 500 + }; + match_result_t result; + + irsend.reset(); + irsend.sendRaw(equal_encoded_raw, 11, 38000); + irsend.makeDecodeResult(); + result = irrecv.matchData(irsend.capture.rawbuf + 1, 5, 1500, 500, 500, 1500); + ASSERT_TRUE(result.success); + EXPECT_EQ(0b01011, result.data); + EXPECT_EQ(10, result.used); + + irsend.reset(); + irsend.sendRaw(equal_encoded_raw, 11, 38000); + irsend.makeDecodeResult(); + result = irrecv.matchData(irsend.capture.rawbuf + 1, 5, 1000, 500, 500, 1000); + ASSERT_FALSE(result.success); +} + +// Test matchData() on arbitrary encoded data. +TEST(TestMatchData, ArbitraryEncoded) { + IRsendTest irsend(0); + IRrecv irrecv(1); + irsend.begin(); + + uint16_t arbitrary_encoded_raw[11] = { + 500, 1500, + 3000, 1000, + 499, 1499, + 3001, 1001, + 2999, 990, + 500 + }; + match_result_t result; + + irsend.reset(); + irsend.sendRaw(arbitrary_encoded_raw, 11, 38000); + irsend.makeDecodeResult(); + result = irrecv.matchData(irsend.capture.rawbuf + 1, 5, + 3000, 1000, 500, 1500); + ASSERT_TRUE(result.success); + EXPECT_EQ(0b01011, result.data); + EXPECT_EQ(10, result.used); + + irsend.reset(); + irsend.sendRaw(arbitrary_encoded_raw, 11, 38000); + irsend.makeDecodeResult(); + result = irrecv.matchData(irsend.capture.rawbuf + 1, 5, 1000, 500, 500, 1000); + ASSERT_FALSE(result.success); +} diff --git a/lib/IRremoteESP8266/test/IRrecv_test.h b/lib/IRremoteESP8266/test/IRrecv_test.h new file mode 100644 index 0000000..0ef2bab --- /dev/null +++ b/lib/IRremoteESP8266/test/IRrecv_test.h @@ -0,0 +1,14 @@ +// Copyright 2017 David Conran + +#ifndef TEST_IRRECV_TEST_H_ +#define TEST_IRRECV_TEST_H_ + +#include +#include +#include +#include "IRutils.h" + +#define EXPECT_STATE_EQ(a, b, c) for (uint8_t i = 0; i < c / 8; ++i) {\ + EXPECT_EQ(a[i], b[i]) << "Expected state "\ + "differs at i = " << uint64ToString(i); } +#endif // TEST_IRRECV_TEST_H_ diff --git a/lib/IRremoteESP8266/test/IRsend_test.cpp b/lib/IRremoteESP8266/test/IRsend_test.cpp index 3b18ae5..ecd785a 100644 --- a/lib/IRremoteESP8266/test/IRsend_test.cpp +++ b/lib/IRremoteESP8266/test/IRsend_test.cpp @@ -101,6 +101,10 @@ TEST(TestSendRaw, GeneralUse) { irsend.reset(); irsend.sendRaw(rawData, 67, 38); irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decodeNEC(&irsend.capture, NEC_BITS, false)); + EXPECT_EQ(NEC, irsend.capture.decode_type); + EXPECT_EQ(32, irsend.capture.bits); + EXPECT_EQ(0xC3E0E0E8, irsend.capture.value); EXPECT_EQ( "m8950s4500" "m550s1650m600s1650m550s550m600s500m600s550m550s550m600s1650m550s1650" @@ -108,10 +112,6 @@ TEST(TestSendRaw, GeneralUse) { "m550s1650m600s1650m600s1650m550s550m600s500m600s500m600s550m550s550" "m600s1650m550s1650m600s1650m600s500m650s1600m600s500m600s550m550s550" "m600", irsend.outputStr()); - ASSERT_TRUE(irrecv.decodeNEC(&irsend.capture, NEC_BITS, false)); - EXPECT_EQ(NEC, irsend.capture.decode_type); - EXPECT_EQ(32, irsend.capture.bits); - EXPECT_EQ(0xC3E0E0E8, irsend.capture.value); } // Incorrect handling of decodes from Raw. i.e. There is no gap recorded at @@ -137,3 +137,150 @@ TEST(TestSendRaw, NoTrailingGap) { EXPECT_EQ(NEC, irsend.capture.decode_type); EXPECT_EQ(NEC_BITS, 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/test/IRsend_test.h b/lib/IRremoteESP8266/test/IRsend_test.h index 564e8ce..2f7660e 100644 --- a/lib/IRremoteESP8266/test/IRsend_test.h +++ b/lib/IRremoteESP8266/test/IRsend_test.h @@ -10,9 +10,16 @@ #include #include "IRrecv.h" #include "IRsend.h" +#include "IRtimer.h" + +#define OUTPUT_BUF 10000U +#define RAW_BUF 10000U + +#ifdef UNIT_TEST +// Used to help simulate elapsed time in unit tests. +uint32_t _IRtimer_unittest_now = 0; +#endif // UNIT_TEST -#define OUTPUT_BUF 1000U -#define RAW_BUF 1000U class IRsendTest: public IRsend { public: uint32_t output[OUTPUT_BUF]; @@ -20,13 +27,17 @@ class IRsendTest: public IRsend { uint16_t rawbuf[RAW_BUF]; decode_results capture; - explicit IRsendTest(uint16_t x, bool i = false) : IRsend(x, i) { + explicit IRsendTest(uint16_t x, bool i = false, bool j = true) : + IRsend(x, i, j) { reset(); } void reset() { last = 0; - output[last] = 0; + for (uint16_t i = 0; i < OUTPUT_BUF; i++) + output[i] = 0; + for (uint16_t i = 0; i < RAW_BUF; i++) + rawbuf[i] = 0; } std::string outputStr() { @@ -47,7 +58,7 @@ class IRsendTest: public IRsend { void makeDecodeResult(uint16_t offset = 0) { capture.decode_type = UNKNOWN; capture.bits = 0; - capture.rawlen = last + 1 - offset; + capture.rawlen = last + 2 - offset; capture.overflow = (last - offset >= (int16_t) RAW_BUF); capture.repeat = false; capture.address = 0; @@ -64,16 +75,18 @@ class IRsendTest: public IRsend { } void dumpRawResult() { - std::cout << "uint16_t rawbuf["<< capture.rawlen << "] =\n"; - for (uint16_t i = 0; i < capture.rawlen; i++) { + std::cout << std::dec; + if (capture.rawlen == 0) return; + std::cout << "uint16_t rawbuf["<< capture.rawlen - 1 << "] = {"; + for (uint16_t i = 1; i < capture.rawlen; i++) { + if (i % 8 == 1) + std::cout << std::endl << " "; std::cout << (capture.rawbuf[i] * RAWTICK); - std::cout << "("; - std::cout << capture.rawbuf[i]; - std::cout << "), "; - if (i % 8 == 7) - std::cout << "\n"; + // std::cout << "(" << capture.rawbuf[i] << ")"; + if (i < capture.rawlen - 1) + std::cout << ", "; } - std::cout << "\n"; + std::cout << "};" << std::endl; } void addGap(uint32_t usecs) { @@ -82,6 +95,7 @@ class IRsendTest: public IRsend { protected: uint16_t mark(uint16_t usec) { + IRtimer::add(usec); if (last >= OUTPUT_BUF) return 0; if (last & 1) // Is odd? (i.e. last call was a space()) @@ -92,6 +106,7 @@ class IRsendTest: public IRsend { } void space(uint32_t time) { + IRtimer::add(time); if (last >= OUTPUT_BUF) return; if (last & 1) { // Is odd? (i.e. last call was a space()) @@ -101,4 +116,37 @@ class IRsendTest: public IRsend { } } }; + +#ifdef UNIT_TEST +class IRsendLowLevelTest: public IRsend { + public: + std::string low_level_sequence; + + explicit IRsendLowLevelTest(uint16_t x, bool i = false, bool j = true) : + IRsend(x, i, j) { + reset(); + } + + void reset() { + low_level_sequence = ""; + } + + protected: + void _delayMicroseconds(uint32_t usec) { + _IRtimer_unittest_now += usec; + std::ostringstream Convert; + Convert << usec; + low_level_sequence += Convert.str() + "usecs"; + } + + void ledOff() { + low_level_sequence += "[Off]"; + } + + void ledOn() { + low_level_sequence += "[On]"; + } +}; +#endif // UNIT_TEST + #endif // TEST_IRSEND_TEST_H_ diff --git a/lib/IRremoteESP8266/test/IRutils_test.cpp b/lib/IRremoteESP8266/test/IRutils_test.cpp index 2225022..edec177 100644 --- a/lib/IRremoteESP8266/test/IRutils_test.cpp +++ b/lib/IRremoteESP8266/test/IRutils_test.cpp @@ -1,6 +1,10 @@ // Copyright 2017 David Conran #include "IRutils.h" +#include +#include "IRrecv.h" +#include "IRsend.h" +#include "IRsend_test.h" #include "gtest/gtest.h" // Tests reverseBits(). @@ -33,3 +37,309 @@ TEST(ReverseBitsTest, LessBitsReversedThanInputHasSet) { EXPECT_EQ(0xF5, reverseBits(0xFA, 4)); EXPECT_EQ(0x12345678FFFF0000, reverseBits(0x123456780000FFFF, 32)); } + +// Tests for uint64ToString() + +TEST(TestUint64ToString, TrivialCases) { + EXPECT_EQ("0", uint64ToString(0)); // Default base (10) + EXPECT_EQ("0", uint64ToString(0, 2)); // Base-2 + EXPECT_EQ("0", uint64ToString(0, 8)); // Base-8 + EXPECT_EQ("0", uint64ToString(0, 10)); // Base-10 + EXPECT_EQ("0", uint64ToString(0, 16)); // Base-16 + + EXPECT_EQ("1", uint64ToString(1, 2)); // Base-2 + EXPECT_EQ("2", uint64ToString(2, 8)); // Base-8 + EXPECT_EQ("3", uint64ToString(3, 10)); // Base-10 + EXPECT_EQ("4", uint64ToString(4, 16)); // Base-16 +} + +TEST(TestUint64ToString, NormalUse) { + EXPECT_EQ("12345", uint64ToString(12345)); + EXPECT_EQ("100", uint64ToString(4, 2)); + EXPECT_EQ("3039", uint64ToString(12345, 16)); + EXPECT_EQ("123456", uint64ToString(123456)); + EXPECT_EQ("1E240", uint64ToString(123456, 16)); + EXPECT_EQ("FEEDDEADBEEF", uint64ToString(0xfeeddeadbeef, 16)); +} + +TEST(TestUint64ToString, Max64Bit) { + EXPECT_EQ("18446744073709551615", uint64ToString(UINT64_MAX)); // Default + EXPECT_EQ("1111111111111111111111111111111111111111111111111111111111111111", + uint64ToString(UINT64_MAX, 2)); // Base-2 + EXPECT_EQ("1777777777777777777777", uint64ToString(UINT64_MAX, 8)); // Base-8 + EXPECT_EQ("18446744073709551615", uint64ToString(UINT64_MAX, 10)); // Base-10 + EXPECT_EQ("FFFFFFFFFFFFFFFF", uint64ToString(UINT64_MAX, 16)); // Base-16 +} + +TEST(TestUint64ToString, Max32Bit) { + EXPECT_EQ("4294967295", uint64ToString(UINT32_MAX)); // Default + EXPECT_EQ("37777777777", uint64ToString(UINT32_MAX, 8)); // Base-8 + EXPECT_EQ("4294967295", uint64ToString(UINT32_MAX, 10)); // Base-10 + EXPECT_EQ("FFFFFFFF", uint64ToString(UINT32_MAX, 16)); // Base-16 +} + +TEST(TestUint64ToString, InterestingCases) { + // Previous hacky-code didn't handle leading zeros in the lower 32 bits. + EXPECT_EQ("100000000", uint64ToString(0x100000000, 16)); + EXPECT_EQ("100000001", uint64ToString(0x100000001, 16)); +} + +TEST(TestUint64ToString, SillyBases) { + // If we are given a silly base, we should defer to Base-10. + EXPECT_EQ("12345", uint64ToString(12345, 0)); // Super silly, makes no sense. + EXPECT_EQ("12345", uint64ToString(12345, 1)); // We don't do unary. + EXPECT_EQ("12345", uint64ToString(12345, 100)); // We can't print base-100. + EXPECT_EQ("12345", uint64ToString(12345, 37)); // Base-37 is one to far. + EXPECT_EQ("9IX", uint64ToString(12345, 36)); // But we *can* do base-36. +} + +TEST(TestGetCorrectedRawLength, NoLargeValues) { + IRsendTest irsend(0); + IRrecv irrecv(1); + uint16_t test_data[7] = {1, 2, 3, 4, 5, 6, 7}; + irsend.begin(); + irsend.reset(); + irsend.sendRaw(test_data, 7, 38000); + irsend.makeDecodeResult(); + irrecv.decode(&irsend.capture); + EXPECT_EQ(7, getCorrectedRawLength(&irsend.capture)); +} + +TEST(TestGetCorrectedRawLength, WithLargeValues) { + IRsendTest irsend(0); + IRrecv irrecv(1); + uint16_t test_data[7] = {10, 20, 30, 40, 50, 60, 70}; + irsend.begin(); + irsend.reset(); + irsend.sendRaw(test_data, 7, 38000); + irsend.makeDecodeResult(); + irrecv.decode(&irsend.capture); + irsend.capture.rawbuf[3] = 60000; + ASSERT_EQ(2, RAWTICK); // The following values rely on RAWTICK being 2. + EXPECT_EQ(7 + 2, getCorrectedRawLength(&irsend.capture)); + irsend.capture.rawbuf[4] = UINT16_MAX - 1; + EXPECT_EQ(7 + 2 * 2, getCorrectedRawLength(&irsend.capture)); + irsend.capture.rawbuf[4] = UINT16_MAX; + EXPECT_EQ(7 + 2 * 2, getCorrectedRawLength(&irsend.capture)); +} + +TEST(TestResultToSourceCode, SimpleTests) { + IRsendTest irsend(0); + IRrecv irrecv(1); + uint16_t test_data[7] = {10, 20, 30, 40, 50, 60, 70}; + irsend.begin(); + irsend.reset(); + irsend.sendRaw(test_data, 7, 38000); + irsend.makeDecodeResult(); + irrecv.decode(&irsend.capture); + EXPECT_EQ("uint16_t rawData[7] = {10, 20, 30, 40, 50, 60, 70};" + " // UNKNOWN A5E5F35D\n", resultToSourceCode(&irsend.capture)); + + // Stick in some large values. + irsend.capture.rawbuf[3] = 60000; + EXPECT_EQ("uint16_t rawData[9] = {10, 20, 65535, 0, 54465, 40," + " 50, 60, 70}; // UNKNOWN A5E5F35D\n", + resultToSourceCode(&irsend.capture)); + irsend.capture.rawbuf[5] = UINT16_MAX; + EXPECT_EQ("uint16_t rawData[11] = {10, 20, 65535, 0, 54465, 40," + " 65535, 0, 65535, 60, 70}; // UNKNOWN A5E5F35D\n", + resultToSourceCode(&irsend.capture)); + + // Reset and put the large value in a space location. + irsend.reset(); + irsend.sendRaw(test_data, 7, 38000); + irsend.makeDecodeResult(); + irrecv.decode(&irsend.capture); + irsend.capture.rawbuf[4] = UINT16_MAX - 1; + EXPECT_EQ("uint16_t rawData[9] = {10, 20, 30, 65535, 0, 65533," + " 50, 60, 70}; // UNKNOWN A5E5F35D\n", + resultToSourceCode(&irsend.capture)); +} + +TEST(TestResultToSourceCode, SimpleProtocols) { + IRsendTest irsend(0); + IRrecv irrecv(1); + irsend.begin(); + + // Generate a code which has address & command values. + irsend.reset(); + irsend.sendNEC(irsend.encodeNEC(0x10, 0x20)); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(NEC, irsend.capture.decode_type); + ASSERT_EQ(NEC_BITS, irsend.capture.bits); + EXPECT_EQ( + "uint16_t rawData[68] = {8960, 4480, 560, 560, 560, 560, 560, 560, " + "560, 560, 560, 1680, 560, 560, 560, 560, 560, 560, 560, 1680, " + "560, 1680, 560, 1680, 560, 1680, 560, 560, 560, 1680, 560, 1680, " + "560, 1680, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, " + "560, 1680, 560, 560, 560, 560, 560, 1680, 560, 1680, 560, 1680, " + "560, 1680, 560, 1680, 560, 560, 560, 1680, 560, 1680, 560, 40320 " + "}; // NEC 8F704FB\n" + "uint32_t address = 0x10;\n" + "uint32_t command = 0x20;\n" + "uint64_t data = 0x8F704FB;\n", resultToSourceCode(&irsend.capture)); + + // Generate a code which DOESN'T have address & command values. + irsend.reset(); + irsend.sendNikai(0xD0F2F); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(NIKAI, irsend.capture.decode_type); + ASSERT_EQ(NIKAI_BITS, irsend.capture.bits); + EXPECT_EQ( + "uint16_t rawData[52] = {4000, 4000, 500, 2000, 500, 2000, " + "500, 2000, 500, 2000, 500, 1000, 500, 1000, 500, 2000, 500, 1000, " + "500, 2000, 500, 2000, 500, 2000, 500, 2000, 500, 1000, 500, 1000, " + "500, 1000, 500, 1000, 500, 2000, 500, 2000, 500, 1000, 500, 2000, " + "500, 1000, 500, 1000, 500, 1000, 500, 1000, 500, 8500 };" + " // NIKAI D0F2F\n" + "uint64_t data = 0xD0F2F;\n", resultToSourceCode(&irsend.capture)); +} + +TEST(TestResultToSourceCode, ComplexProtocols) { + IRsendTest irsend(0); + IRrecv irrecv(1); + irsend.begin(); + + uint8_t state[TOSHIBA_AC_STATE_LENGTH] = { + 0xF2, 0x0D, 0x03, 0xFC, 0x01, 0x00, 0x00, 0x00, 0x01}; + + irsend.reset(); + irsend.sendToshibaAC(state); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(TOSHIBA_AC, irsend.capture.decode_type); + ASSERT_EQ(TOSHIBA_AC_BITS, irsend.capture.bits); + EXPECT_EQ( + "uint16_t rawData[296] = {4400, 4300, 542, 1622, 542, 1622, " + "542, 1622, 542, 1622, 542, 472, 542, 472, 542, 1622, 542, 472, " + "542, 472, 542, 472, 542, 472, 542, 472, 542, 1622, 542, 1622, " + "542, 472, 542, 1622, 542, 472, 542, 472, 542, 472, 542, 472, " + "542, 472, 542, 472, 542, 1622, 542, 1622, 542, 1622, 542, 1622, " + "542, 1622, 542, 1622, 542, 1622, 542, 1622, 542, 472, 542, 472, " + "542, 472, 542, 472, 542, 472, 542, 472, 542, 472, 542, 472, " + "542, 472, 542, 1622, 542, 472, 542, 472, 542, 472, 542, 472, " + "542, 472, 542, 472, 542, 472, 542, 472, 542, 472, 542, 472, " + "542, 472, 542, 472, 542, 472, 542, 472, 542, 472, 542, 472, " + "542, 472, 542, 472, 542, 472, 542, 472, 542, 472, 542, 472, " + "542, 472, 542, 472, 542, 472, 542, 472, 542, 472, 542, 472, " + "542, 472, 542, 472, 542, 472, 542, 1622, 542, 7048, 4400, 4300, " + "542, 1622, 542, 1622, 542, 1622, 542, 1622, 542, 472, 542, 472, " + "542, 1622, 542, 472, 542, 472, 542, 472, 542, 472, 542, 472, " + "542, 1622, 542, 1622, 542, 472, 542, 1622, 542, 472, 542, 472, " + "542, 472, 542, 472, 542, 472, 542, 472, 542, 1622, 542, 1622, " + "542, 1622, 542, 1622, 542, 1622, 542, 1622, 542, 1622, 542, 1622, " + "542, 472, 542, 472, 542, 472, 542, 472, 542, 472, 542, 472, " + "542, 472, 542, 472, 542, 472, 542, 1622, 542, 472, 542, 472, " + "542, 472, 542, 472, 542, 472, 542, 472, 542, 472, 542, 472, " + "542, 472, 542, 472, 542, 472, 542, 472, 542, 472, 542, 472, " + "542, 472, 542, 472, 542, 472, 542, 472, 542, 472, 542, 472, " + "542, 472, 542, 472, 542, 472, 542, 472, 542, 472, 542, 472, " + "542, 472, 542, 472, 542, 472, 542, 472, 542, 472, 542, 1622, " + "542, 7048 }; // TOSHIBA_AC\n" + "uint8_t state[9] = {0xF2, 0x0D, 0x03, 0xFC, 0x01, 0x00, 0x00, 0x00, " + "0x01};\n", resultToSourceCode(&irsend.capture)); +} + +TEST(TestResultToTimingInfo, General) { + IRsendTest irsend(0); + IRrecv irrecv(1); + irsend.begin(); + + irsend.reset(); + irsend.sendNEC(irsend.encodeNEC(0x10, 0x20)); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(NEC, irsend.capture.decode_type); + ASSERT_EQ(NEC_BITS, irsend.capture.bits); + EXPECT_EQ( + "Raw Timing[68]:\n" + " + 8960, - 4480, + 560, - 560, + 560, - 560," + " + 560, - 560, \n" + " + 560, - 560, + 560, - 1680, + 560, - 560," + " + 560, - 560, \n" + " + 560, - 560, + 560, - 1680, + 560, - 1680," + " + 560, - 1680, \n" + " + 560, - 1680, + 560, - 560, + 560, - 1680," + " + 560, - 1680, \n" + " + 560, - 1680, + 560, - 560, + 560, - 560," + " + 560, - 560, \n" + " + 560, - 560, + 560, - 560, + 560, - 1680," + " + 560, - 560, \n" + " + 560, - 560, + 560, - 1680, + 560, - 1680," + " + 560, - 1680, \n" + " + 560, - 1680, + 560, - 1680, + 560, - 560," + " + 560, - 1680, \n" + " + 560, - 1680, + 560, - 40320\n", + resultToTimingInfo(&irsend.capture)); + + irsend.reset(); + uint16_t rawData[9] = {10, 20, 30, 40, 50, 60, 70, 80, 90}; + irsend.sendRaw(rawData, 9, 38000); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ( + "Raw Timing[9]:\n" + " + 10, - 20, + 30, - 40, + 50, - 60," + " + 70, - 80, \n" + " + 90\n", + resultToTimingInfo(&irsend.capture)); +} + +TEST(TestResultToHumanReadableBasic, SimpleCodes) { + IRsendTest irsend(0); + IRrecv irrecv(1); + irsend.begin(); + + irsend.reset(); + irsend.sendNEC(irsend.encodeNEC(0x10, 0x20)); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(NEC, irsend.capture.decode_type); + ASSERT_EQ(NEC_BITS, irsend.capture.bits); + EXPECT_EQ( + "Encoding : NEC\n" + "Code : 8F704FB (32 bits)\n", + resultToHumanReadableBasic(&irsend.capture)); +} + +TEST(TestResultToHumanReadableBasic, ComplexCodes) { + IRsendTest irsend(0); + IRrecv irrecv(1); + irsend.begin(); + + + uint8_t state[TOSHIBA_AC_STATE_LENGTH] = { + 0xF2, 0x0D, 0x03, 0xFC, 0x01, 0x00, 0x00, 0x00, 0x01}; + + irsend.reset(); + irsend.sendToshibaAC(state); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(TOSHIBA_AC, irsend.capture.decode_type); + ASSERT_EQ(TOSHIBA_AC_BITS, irsend.capture.bits); + EXPECT_EQ( + "Encoding : TOSHIBA_AC\n" + "Code : F20D03FC0100000001 (72 bits)\n", + resultToHumanReadableBasic(&irsend.capture)); +} + +TEST(TestInvertBits, Normal) { + ASSERT_EQ(0xAAAA5555AAAA5555, invertBits(0x5555AAAA5555AAAA, 64)); + ASSERT_EQ(0xAAAA5555, invertBits(0x5555AAAA, 32)); + ASSERT_EQ(0xFFFFFFFFFFFFFFFF, invertBits(0x0, 64)); + ASSERT_EQ(0x0, invertBits(invertBits(0x0, 64), 64)); + ASSERT_EQ(0x2, invertBits(0x1, 2)); +} + +TEST(TestInvertBits, ZeroBits) { + ASSERT_EQ(0xAAAA5555AAAA5555, invertBits(0xAAAA5555AAAA5555, 0)); + ASSERT_EQ(0x0, invertBits(0x0, 0)); + ASSERT_EQ(0x1, invertBits(0x1, 0)); +} + +TEST(TestInvertBits, MoreThan64Bits) { + ASSERT_EQ(0xAAAA5555AAAA5555, invertBits(0x5555AAAA5555AAAA, 70)); + ASSERT_EQ(0xFFFFFFFFFFFFFFFF, invertBits(0x0, 128)); +} diff --git a/lib/IRremoteESP8266/test/Makefile b/lib/IRremoteESP8266/test/Makefile index 787beb5..17b8611 100644 --- a/lib/IRremoteESP8266/test/Makefile +++ b/lib/IRremoteESP8266/test/Makefile @@ -1,8 +1,10 @@ # SYNOPSIS: # -# make [all] - makes everything. -# make TARGET - makes the given target. -# make clean - removes all files generated by make. +# make [all] - makes everything. +# make TARGET - makes the given target. +# make run - makes everything and runs all the tests. +# make clean - removes all files generated by make. +# make install-googletest - install the googletest code suite # Please tweak the following variable definitions as needed by your # project, except GTEST_HEADERS, which you can use in your own targets @@ -30,7 +32,9 @@ TESTS = IRutils_test IRsend_test ir_NEC_test ir_GlobalCache_test \ ir_JVC_test ir_RCMM_test ir_LG_test ir_Mitsubishi_test ir_Sharp_test \ ir_RC5_RC6_test ir_Panasonic_test ir_Dish_test ir_Whynter_test \ ir_Aiwa_test ir_Denon_test ir_Sanyo_test ir_Daikin_test ir_Coolix_test \ - ir_Gree_test IRrecv_test ir_Pronto_test ir_Fujitsu_test + ir_Gree_test IRrecv_test ir_Pronto_test ir_Fujitsu_test ir_Nikai_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 # All Google Test headers. Usually you shouldn't change this # definition. @@ -56,6 +60,8 @@ run : all echo "PASS: \o/ \o/ All unit tests passed. \o/ \o/"; \ fi +run_tests : run + install-googletest : git clone https://github.com/google/googletest.git ../lib/googletest @@ -67,9 +73,12 @@ GTEST_SRCS_ = $(GTEST_DIR)/src/*.cc $(GTEST_DIR)/src/*.h $(GTEST_HEADERS) # All the IR protocol object files. PROTOCOLS = ir_NEC.o ir_Sony.o ir_Samsung.o ir_JVC.o ir_RCMM.o ir_RC5_RC6.o \ - ir_LG.o ir_Mitsubishi.o ir_Fujitsu.o ir_Sharp.o ir_Sanyo.o ir_Denon.o ir_Dish.o \ - ir_Panasonic.o ir_Whynter.o ir_Coolix.o ir_Aiwa.o ir_Sherwood.o \ - ir_Kelvinator.o ir_Daikin.o ir_Gree.o ir_Pronto.o + ir_LG.o ir_Mitsubishi.o ir_Fujitsu.o ir_Sharp.o ir_Sanyo.o ir_Denon.o ir_Dish.o \ + ir_Panasonic.o ir_Whynter.o ir_Coolix.o ir_Aiwa.o ir_Sherwood.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 + # Common object files COMMON_OBJ = IRutils.o IRtimer.o IRsend.o IRrecv.o ir_GlobalCache.o \ $(PROTOCOLS) gtest_main.a @@ -77,7 +86,7 @@ COMMON_OBJ = IRutils.o IRtimer.o IRsend.o IRrecv.o ir_GlobalCache.o \ COMMON_DEPS = $(USER_DIR)/IRrecv.h $(USER_DIR)/IRsend.h $(USER_DIR)/IRtimer.h \ $(USER_DIR)/IRutils.h $(USER_DIR)/IRremoteESP8266.h # Common test dependencies -COMMON_TEST_DEPS = $(COMMON_DEPS) IRsend_test.h +COMMON_TEST_DEPS = $(COMMON_DEPS) IRrecv_test.h IRsend_test.h # For simplicity and to avoid depending on Google Test's # implementation details, the dependencies specified below are @@ -97,17 +106,17 @@ gtest.a : gtest-all.o gtest_main.a : gtest-all.o gtest_main.o $(AR) $(ARFLAGS) $@ $^ -# Builds a our test. A test should link with either gtest.a or +# Builds our test. A test should link with either gtest.a or # gtest_main.a, depending on whether it defines its own main() # function. -IRutils.o : $(USER_DIR)/IRutils.cpp $(USER_DIR)/IRutils.h +IRutils.o : $(USER_DIR)/IRutils.cpp $(USER_DIR)/IRutils.h $(USER_DIR)/IRremoteESP8266.h $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/IRutils.cpp -IRutils_test.o : IRutils_test.cpp $(USER_DIR)/IRutils.h $(GTEST_HEADERS) +IRutils_test.o : IRutils_test.cpp $(COMMON_TEST_DEPS) $(GTEST_HEADERS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -I$(USER_DIR) -c IRutils_test.cpp -IRutils_test : IRutils.o IRutils_test.o gtest_main.a +IRutils_test : IRutils_test.o ir_NEC.o ir_Nikai.o ir_Toshiba.o $(COMMON_OBJ) gtest_main.a $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ IRtimer.o : $(USER_DIR)/IRtimer.cpp $(USER_DIR)/IRtimer.h @@ -326,7 +335,7 @@ ir_Gree.o : $(USER_DIR)/ir_Gree.cpp $(GTEST_HEADERS) ir_Gree_test.o : ir_Gree_test.cpp $(COMMON_TEST_DEPS) $(GTEST_HEADERS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -I$(USER_DIR) -c ir_Gree_test.cpp -ir_Gree_test : $(COMMON_OBJ) ir_Gree_test.o +ir_Gree_test : $(COMMON_OBJ) ir_Gree_test.o ir_Kelvinator.o $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ ir_Pronto.o : $(USER_DIR)/ir_Pronto.cpp $(GTEST_HEADERS) @@ -337,3 +346,84 @@ ir_Pronto_test.o : ir_Pronto_test.cpp $(COMMON_TEST_DEPS) $(GTEST_HEADERS) ir_Pronto_test : $(COMMON_OBJ) ir_Pronto_test.o $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ + +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 + +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 + +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 + +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 + +ir_Midea_test.o : ir_Midea_test.cpp $(COMMON_TEST_DEPS) $(GTEST_HEADERS) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) -I$(USER_DIR) -c ir_Midea_test.cpp + +ir_Midea_test : $(COMMON_OBJ) ir_Midea_test.o + $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ + +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 + +ir_Magiquest_test : $(COMMON_OBJ) ir_Magiquest_test.o + $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ + +ir_Lasertag.o : $(USER_DIR)/ir_Lasertag.cpp $(USER_DIR)/ir_RC5_RC6.cpp $(GTEST_HEADERS) + $(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 + +ir_Lasertag_test : $(COMMON_OBJ) ir_Lasertag_test.o ir_RC5_RC6.o + $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ + +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 + +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 + +ir_Haier_test.o : ir_Haier_test.cpp $(COMMON_TEST_DEPS) $(GTEST_HEADERS) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) -I$(USER_DIR) -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 + +ir_Hitachi_test.o : ir_Hitachi_test.cpp $(COMMON_TEST_DEPS) $(GTEST_HEADERS) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) -I$(USER_DIR) -c ir_Hitachi_test.cpp + +ir_Hitachi_test : $(COMMON_OBJ) ir_Hitachi_test.o + $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ + +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 + +ir_GICable_test : $(COMMON_OBJ) ir_GICable_test.o + $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ diff --git a/lib/IRremoteESP8266/test/ir_Aiwa_test.cpp b/lib/IRremoteESP8266/test/ir_Aiwa_test.cpp index a0bb12f..2c533e8 100644 --- a/lib/IRremoteESP8266/test/ir_Aiwa_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Aiwa_test.cpp @@ -20,8 +20,8 @@ TEST(TestSendAiwa, SendDataOnly) { "m560s560m560s1680m560s560m560s560m560s1680m560s1680m560s1680m560s1680" "m560s1680m560s1680m560s560m560s560m560s560m560s560m560s560m560s560" "m560s560m560s560m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680" - "m560s1680m560s1680m560s108080" - "m8960s2240m560s108080", irsend.outputStr()); + "m560s1680m560s1680m560s23520" + "m8960s2240m560s96320", irsend.outputStr()); } // Test sending oversized data. @@ -48,7 +48,7 @@ TEST(TestSendAiwa, SendWithRepeats) { "m560s560m560s1680m560s560m560s560m560s1680m560s1680m560s1680m560s1680" "m560s1680m560s1680m560s560m560s560m560s560m560s560m560s560m560s560" "m560s560m560s560m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680" - "m560s1680m560s1680m560s108080", irsend.outputStr()); + "m560s1680m560s1680m560s23520", irsend.outputStr()); irsend.reset(); irsend.sendAiwaRCT501(0x7F, AIWA_RC_T501_BITS, 1); // 1 repeat. EXPECT_EQ( @@ -58,8 +58,8 @@ TEST(TestSendAiwa, SendWithRepeats) { "m560s560m560s1680m560s560m560s560m560s1680m560s1680m560s1680m560s1680" "m560s1680m560s1680m560s560m560s560m560s560m560s560m560s560m560s560" "m560s560m560s560m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680" - "m560s1680m560s1680m560s108080" - "m8960s2240m560s108080", irsend.outputStr()); + "m560s1680m560s1680m560s23520" + "m8960s2240m560s96320", irsend.outputStr()); irsend.reset(); irsend.sendAiwaRCT501(0x7F, AIWA_RC_T501_BITS, 2); // 2 repeats. EXPECT_EQ( @@ -69,13 +69,13 @@ TEST(TestSendAiwa, SendWithRepeats) { "m560s560m560s1680m560s560m560s560m560s1680m560s1680m560s1680m560s1680" "m560s1680m560s1680m560s560m560s560m560s560m560s560m560s560m560s560" "m560s560m560s560m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680" - "m560s1680m560s1680m560s108080" - "m8960s2240m560s108080" - "m8960s2240m560s108080", irsend.outputStr()); + "m560s1680m560s1680m560s23520" + "m8960s2240m560s96320" + "m8960s2240m560s96320", irsend.outputStr()); } // Test sending an atypical data size. -TEST(TestSendAiwa, SendUsualSize) { +TEST(TestSendAiwa, SendUnusualSize) { IRsendTest irsend(4); irsend.begin(); @@ -87,8 +87,8 @@ TEST(TestSendAiwa, SendUsualSize) { "m560s560m560s560m560s560m560s560m560s560m560s1680m560s560m560s560" "m560s560m560s1680m560s560m560s560m560s1680m560s1680m560s1680m560s1680" "m560s1680m560s1680m560s560m560s560m560s560m560s1680m560s560m560s560" - "m560s1680m560s560m560s1680m560s108080" - "m8960s2240m560s108080", irsend.outputStr()); + "m560s1680m560s560m560s1680m560s36960" + "m8960s2240m560s96320", irsend.outputStr()); irsend.reset(); irsend.sendAiwaRCT501(0x1234567890, 37); @@ -102,8 +102,8 @@ TEST(TestSendAiwa, SendUsualSize) { "m560s1680m560s560m560s1680m560s560m560s1680m560s1680m560s560m560s560" "m560s1680m560s1680m560s1680m560s1680m560s560m560s560m560s560m560s1680" "m560s560m560s560m560s1680m560s560m560s560m560s560m560s560m560s1680" - "m560s108080" - "m8960s2240m560s108080", irsend.outputStr()); + "m560s22400" + "m8960s2240m560s96320", irsend.outputStr()); } // Tests for decodeAiwaRCT501(). diff --git a/lib/IRremoteESP8266/test/ir_Carrier_test.cpp b/lib/IRremoteESP8266/test/ir_Carrier_test.cpp new file mode 100644 index 0000000..3fe5ce1 --- /dev/null +++ b/lib/IRremoteESP8266/test/ir_Carrier_test.cpp @@ -0,0 +1,219 @@ +// Copyright 2018 David Conran + +#include "IRrecv.h" +#include "IRsend.h" +#include "IRsend_test.h" +#include "gtest/gtest.h" + +// Tests for sendCarrierAC() + +// Test sending typical data only. +TEST(TestSendCarrierAC, SendDataOnly) { + IRsendTest irsend(0); + irsend.begin(); + + irsend.reset(); + irsend.sendCarrierAC(0x0); + EXPECT_EQ( + "m8532s4228" + "m628s532m628s532m628s532m628s532m628s532m628s532m628s532m628s532" + "m628s532m628s532m628s532m628s532m628s532m628s532m628s532m628s532" + "m628s532m628s532m628s532m628s532m628s532m628s532m628s532m628s532" + "m628s532m628s532m628s532m628s532m628s532m628s532m628s532m628s532" + "m628s20000" + "m8532s4228" + "m628s1320m628s1320m628s1320m628s1320m628s1320m628s1320m628s1320m628s1320" + "m628s1320m628s1320m628s1320m628s1320m628s1320m628s1320m628s1320m628s1320" + "m628s1320m628s1320m628s1320m628s1320m628s1320m628s1320m628s1320m628s1320" + "m628s1320m628s1320m628s1320m628s1320m628s1320m628s1320m628s1320m628s1320" + "m628s20000" + "m8532s4228" + "m628s532m628s532m628s532m628s532m628s532m628s532m628s532m628s532" + "m628s532m628s532m628s532m628s532m628s532m628s532m628s532m628s532" + "m628s532m628s532m628s532m628s532m628s532m628s532m628s532m628s532" + "m628s532m628s532m628s532m628s532m628s532m628s532m628s532m628s532" + "m628s20000", irsend.outputStr()); + irsend.reset(); + irsend.sendCarrierAC(0x12345678); + EXPECT_EQ( + "m8532s4228" + "m628s532m628s532m628s532m628s1320m628s532m628s532m628s1320m628s532" + "m628s532m628s532m628s1320m628s1320m628s532m628s1320m628s532m628s532" + "m628s532m628s1320m628s532m628s1320m628s532m628s1320m628s1320m628s532" + "m628s532m628s1320m628s1320m628s1320m628s1320m628s532m628s532m628s532" + "m628s20000" + "m8532s4228" + "m628s1320m628s1320m628s1320m628s532m628s1320m628s1320m628s532m628s1320" + "m628s1320m628s1320m628s532m628s532m628s1320m628s532m628s1320m628s1320" + "m628s1320m628s532m628s1320m628s532m628s1320m628s532m628s532m628s1320" + "m628s1320m628s532m628s532m628s532m628s532m628s1320m628s1320m628s1320" + "m628s20000" + "m8532s4228" + "m628s532m628s532m628s532m628s1320m628s532m628s532m628s1320m628s532" + "m628s532m628s532m628s1320m628s1320m628s532m628s1320m628s532m628s532" + "m628s532m628s1320m628s532m628s1320m628s532m628s1320m628s1320m628s532" + "m628s532m628s1320m628s1320m628s1320m628s1320m628s532m628s532m628s532" + "m628s20000", irsend.outputStr()); + + irsend.reset(); + irsend.sendCarrierAC(0x4CCA541D); + EXPECT_EQ( + "m8532s4228" + "m628s532m628s1320m628s532m628s532m628s1320m628s1320m628s532m628s532" + "m628s1320m628s1320m628s532m628s532m628s1320m628s532m628s1320m628s532" + "m628s532m628s1320m628s532m628s1320m628s532m628s1320m628s532m628s532" + "m628s532m628s532m628s532m628s1320m628s1320m628s1320m628s532m628s1320" + "m628s20000" + "m8532s4228" + "m628s1320m628s532m628s1320m628s1320m628s532m628s532m628s1320m628s1320" + "m628s532m628s532m628s1320m628s1320m628s532m628s1320m628s532m628s1320" + "m628s1320m628s532m628s1320m628s532m628s1320m628s532m628s1320m628s1320" + "m628s1320m628s1320m628s1320m628s532m628s532m628s532m628s1320m628s532" + "m628s20000" + "m8532s4228" + "m628s532m628s1320m628s532m628s532m628s1320m628s1320m628s532m628s532" + "m628s1320m628s1320m628s532m628s532m628s1320m628s532m628s1320m628s532" + "m628s532m628s1320m628s532m628s1320m628s532m628s1320m628s532m628s532" + "m628s532m628s532m628s532m628s1320m628s1320m628s1320m628s532m628s1320" + "m628s20000", irsend.outputStr()); +} + +// Test sending typical data only. +TEST(TestSendCarrierAC, SendWithRepeats) { + IRsendTest irsend(0); + irsend.begin(); + + irsend.reset(); + irsend.sendCarrierAC(0x12345678, CARRIER_AC_BITS, 2); // two repeats. + EXPECT_EQ( + "m8532s4228" + "m628s532m628s532m628s532m628s1320m628s532m628s532m628s1320m628s532" + "m628s532m628s532m628s1320m628s1320m628s532m628s1320m628s532m628s532" + "m628s532m628s1320m628s532m628s1320m628s532m628s1320m628s1320m628s532" + "m628s532m628s1320m628s1320m628s1320m628s1320m628s532m628s532m628s532" + "m628s20000" + "m8532s4228" + "m628s1320m628s1320m628s1320m628s532m628s1320m628s1320m628s532m628s1320" + "m628s1320m628s1320m628s532m628s532m628s1320m628s532m628s1320m628s1320" + "m628s1320m628s532m628s1320m628s532m628s1320m628s532m628s532m628s1320" + "m628s1320m628s532m628s532m628s532m628s532m628s1320m628s1320m628s1320" + "m628s20000" + "m8532s4228" + "m628s532m628s532m628s532m628s1320m628s532m628s532m628s1320m628s532" + "m628s532m628s532m628s1320m628s1320m628s532m628s1320m628s532m628s532" + "m628s532m628s1320m628s532m628s1320m628s532m628s1320m628s1320m628s532" + "m628s532m628s1320m628s1320m628s1320m628s1320m628s532m628s532m628s532" + "m628s20000" + "m8532s4228" + "m628s532m628s532m628s532m628s1320m628s532m628s532m628s1320m628s532" + "m628s532m628s532m628s1320m628s1320m628s532m628s1320m628s532m628s532" + "m628s532m628s1320m628s532m628s1320m628s532m628s1320m628s1320m628s532" + "m628s532m628s1320m628s1320m628s1320m628s1320m628s532m628s532m628s532" + "m628s20000" + "m8532s4228" + "m628s1320m628s1320m628s1320m628s532m628s1320m628s1320m628s532m628s1320" + "m628s1320m628s1320m628s532m628s532m628s1320m628s532m628s1320m628s1320" + "m628s1320m628s532m628s1320m628s532m628s1320m628s532m628s532m628s1320" + "m628s1320m628s532m628s532m628s532m628s532m628s1320m628s1320m628s1320" + "m628s20000" + "m8532s4228" + "m628s532m628s532m628s532m628s1320m628s532m628s532m628s1320m628s532" + "m628s532m628s532m628s1320m628s1320m628s532m628s1320m628s532m628s532" + "m628s532m628s1320m628s532m628s1320m628s532m628s1320m628s1320m628s532" + "m628s532m628s1320m628s1320m628s1320m628s1320m628s532m628s532m628s532" + "m628s20000" + "m8532s4228" + "m628s532m628s532m628s532m628s1320m628s532m628s532m628s1320m628s532" + "m628s532m628s532m628s1320m628s1320m628s532m628s1320m628s532m628s532" + "m628s532m628s1320m628s532m628s1320m628s532m628s1320m628s1320m628s532" + "m628s532m628s1320m628s1320m628s1320m628s1320m628s532m628s532m628s532" + "m628s20000" + "m8532s4228" + "m628s1320m628s1320m628s1320m628s532m628s1320m628s1320m628s532m628s1320" + "m628s1320m628s1320m628s532m628s532m628s1320m628s532m628s1320m628s1320" + "m628s1320m628s532m628s1320m628s532m628s1320m628s532m628s532m628s1320" + "m628s1320m628s532m628s532m628s532m628s532m628s1320m628s1320m628s1320" + "m628s20000" + "m8532s4228" + "m628s532m628s532m628s532m628s1320m628s532m628s532m628s1320m628s532" + "m628s532m628s532m628s1320m628s1320m628s532m628s1320m628s532m628s532" + "m628s532m628s1320m628s532m628s1320m628s532m628s1320m628s1320m628s532" + "m628s532m628s1320m628s1320m628s1320m628s1320m628s532m628s532m628s532" + "m628s20000", irsend.outputStr()); +} + +// Tests for decodeCarrierAC(). + +// Decode normal "synthetic" messages. +TEST(TestDecodeCarrierAC, NormalDecodeWithStrict) { + IRsendTest irsend(0); + IRrecv irrecv(0); + irsend.begin(); + + irsend.reset(); + irsend.sendCarrierAC(0x0); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decodeCarrierAC(&irsend.capture, CARRIER_AC_BITS, true)); + EXPECT_EQ(CARRIER_AC, irsend.capture.decode_type); + EXPECT_EQ(CARRIER_AC_BITS, irsend.capture.bits); + EXPECT_EQ(0x0, irsend.capture.value); + EXPECT_EQ(0x0, irsend.capture.address); + EXPECT_EQ(0x0, irsend.capture.command); + EXPECT_FALSE(irsend.capture.repeat); + + irsend.reset(); + irsend.sendCarrierAC(0xB335ABE2); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decodeCarrierAC(&irsend.capture, CARRIER_AC_BITS, true)); + EXPECT_EQ(CARRIER_AC, irsend.capture.decode_type); + EXPECT_EQ(CARRIER_AC_BITS, irsend.capture.bits); + EXPECT_EQ(0xB335ABE2, irsend.capture.value); + EXPECT_EQ(0xB335, irsend.capture.address); + EXPECT_EQ(0xABE2, irsend.capture.command); + EXPECT_FALSE(irsend.capture.repeat); + + // Do the last one again, & use the full decoder, not just protocol specific. + irsend.reset(); + irsend.sendCarrierAC(0xB335ABE2); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(CARRIER_AC, irsend.capture.decode_type); + EXPECT_EQ(CARRIER_AC_BITS, irsend.capture.bits); + EXPECT_EQ(0xB335ABE2, irsend.capture.value); +} + +// Decode a "real" example message. +TEST(TestDecodeCarrierAC, RealExamples) { + IRsendTest irsend(0); + IRrecv irrecv(0); + irsend.begin(); + + irsend.reset(); + // Data from Issue #385 captured by gnkarn + uint16_t rawData[203] = { + 8532, 4216, 628, 1312, 628, 528, 628, 1312, 628, 1312, 628, 528, 628, 524, + 628, 1316, 624, 1316, 628, 524, 628, 528, 628, 1312, 628, 1316, 624, 528, + 628, 1312, 628, 528, 628, 1312, 628, 1312, 628, 528, 628, 1316, 624, 528, + 628, 1312, 628, 528, 628, 1312, 628, 1316, 624, 1316, 628, 1312, 628, + 1316, 628, 524, 628, 528, 628, 528, 624, 1316, 628, 528, 628, 20064, 8504, + 4228, 628, 528, 628, 1312, 628, 528, 628, 528, 628, 1312, 628, 1316, 624, + 532, 624, 528, 628, 1316, 628, 1312, 628, 528, 628, 528, 628, 1312, 628, + 528, 628, 1316, 628, 528, 624, 528, 628, 1316, 628, 528, 628, 1316, 624, + 528, 628, 1316, 628, 528, 624, 532, 624, 528, 628, 528, 628, 528, 628, + 1316, 624, 1316, 628, 1316, 628, 528, 624, 1316, 628, 20076, 8528, 4212, + 624, 1316, 628, 528, 628, 1316, 628, 1316, 624, 528, 628, 528, 628, 1316, + 628, 1316, 628, 528, 624, 532, 624, 1316, 628, 1316, 628, 528, 628, 1316, + 624, 528, 628, 1316, 628, 1316, 628, 528, 628, 1316, 624, 532, 624, 1316, + 628, 532, 624, 1316, 628, 1316, 624, 1320, 624, 1316, 628, 1316, 628, 528, + 628, 528, 628, 528, 628, 1316, 624, 532, 624}; + + irsend.sendRaw(rawData, 203, 38000); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(CARRIER_AC, irsend.capture.decode_type); + EXPECT_EQ(CARRIER_AC_BITS, irsend.capture.bits); + EXPECT_EQ(0xB335ABE2, irsend.capture.value); + EXPECT_EQ(0xB335, irsend.capture.address); + EXPECT_EQ(0xABE2, irsend.capture.command); + EXPECT_FALSE(irsend.capture.repeat); +} diff --git a/lib/IRremoteESP8266/test/ir_Coolix_test.cpp b/lib/IRremoteESP8266/test/ir_Coolix_test.cpp index 14eb8d6..ada05fe 100644 --- a/lib/IRremoteESP8266/test/ir_Coolix_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Coolix_test.cpp @@ -101,7 +101,7 @@ TEST(TestSendCoolix, SendWithRepeats) { } // Test sending an atypical data size. -TEST(TestSendCoolix, SendUsualSize) { +TEST(TestSendCoolix, SendUnusualSize) { IRsendTest irsend(4); irsend.begin(); diff --git a/lib/IRremoteESP8266/test/ir_Daikin_test.cpp b/lib/IRremoteESP8266/test/ir_Daikin_test.cpp index b516008..041c666 100644 --- a/lib/IRremoteESP8266/test/ir_Daikin_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Daikin_test.cpp @@ -1,5 +1,6 @@ // Copyright 2017 David Conran - +#include "IRrecv.h" +#include "IRrecv_test.h" #include "IRsend.h" #include "IRsend_test.h" #include "ir_Daikin.h" @@ -20,7 +21,17 @@ TEST(TestSendDaikin, SendDataOnly) { irsend.reset(); irsend.sendDaikin(daikin_code); EXPECT_EQ( - "m3650s1623" + "m428s428m428s428m428s428m428s428m428s428" + "m428s29428m3650s1623" + "m428s1280m428s428m428s428m428s428m428s1280m428s428m428s428m428s428" + "m428s428m428s1280m428s428m428s1280m428s1280m428s428m428s1280m428s1280" + "m428s1280m428s1280m428s1280m428s428m428s428m428s1280m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s1280m428s428m428s1280m428s428m428s428m428s428m428s1280m428s1280" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s1280m428s1280m428s1280m428s428m428s1280m428s428m428s1280m428s1280" + "m428s29428m3650s1623" "m428s1280m428s428m428s428m428s428m428s1280m428s428m428s428m428s428" "m428s428m428s1280m428s428m428s1280m428s1280m428s428m428s1280m428s1280" "m428s1280m428s1280m428s1280m428s428m428s428m428s1280m428s428m428s428" @@ -29,8 +40,7 @@ TEST(TestSendDaikin, SendDataOnly) { "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" "m428s428m428s428m428s428m428s428m428s428m428s1280m428s428m428s428" - "m428s29428" - "m3650s1623" + "m428s29428m3650s1623" "m428s1280m428s428m428s428m428s428m428s1280m428s428m428s428m428s428" "m428s428m428s1280m428s428m428s1280m428s1280m428s428m428s1280m428s1280" "m428s1280m428s1280m428s1280m428s428m428s428m428s1280m428s428m428s428" @@ -67,68 +77,86 @@ TEST(TestSendDaikin, SendWithRepeats) { irsend.sendDaikin(daikin_code, DAIKIN_COMMAND_LENGTH, 1); EXPECT_EQ( - "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" - "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()); + "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. @@ -152,7 +180,17 @@ TEST(TestSendDaikin, SendUnexpectedSizes) { irsend.reset(); irsend.sendDaikin(daikin_long_code, DAIKIN_COMMAND_LENGTH + 1); ASSERT_EQ( - "m3650s1623" + "m428s428m428s428m428s428m428s428m428s428" + "m428s29428m3650s1623" + "m428s1280m428s428m428s428m428s428m428s1280m428s428m428s428m428s428" + "m428s428m428s1280m428s428m428s1280m428s1280m428s428m428s1280m428s1280" + "m428s1280m428s1280m428s1280m428s428m428s428m428s1280m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s1280m428s428m428s1280m428s428m428s428m428s428m428s1280m428s1280" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s1280m428s1280m428s1280m428s428m428s1280m428s428m428s1280m428s1280" + "m428s29428m3650s1623" "m428s1280m428s428m428s428m428s428m428s1280m428s428m428s428m428s428" "m428s428m428s1280m428s428m428s1280m428s1280m428s428m428s1280m428s1280" "m428s1280m428s1280m428s1280m428s428m428s428m428s1280m428s428m428s428" @@ -161,8 +199,7 @@ TEST(TestSendDaikin, SendUnexpectedSizes) { "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" "m428s428m428s428m428s428m428s428m428s428m428s1280m428s428m428s428" - "m428s29428" - "m3650s1623" + "m428s29428m3650s1623" "m428s1280m428s428m428s428m428s428m428s1280m428s428m428s428m428s428" "m428s428m428s1280m428s428m428s1280m428s1280m428s428m428s1280m428s1280" "m428s1280m428s1280m428s1280m428s428m428s428m428s1280m428s428m428s428" @@ -306,6 +343,13 @@ TEST(TestDaikinClass, QuietMode) { 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()); } @@ -325,23 +369,54 @@ TEST(TestDaikinClass, PowerfulMode) { 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(0, irdaikin.getFan()); + EXPECT_EQ(DAIKIN_FAN_AUTO, irdaikin.getFan()); + // Unexpected value should default to Auto. irdaikin.setFan(255); - EXPECT_EQ(DAIKIN_FAN_MAX, irdaikin.getFan()); + EXPECT_EQ(DAIKIN_FAN_AUTO, irdaikin.getFan()); irdaikin.setFan(DAIKIN_FAN_MAX); EXPECT_EQ(DAIKIN_FAN_MAX, irdaikin.getFan()); + // Beyond Max should default to Auto. irdaikin.setFan(DAIKIN_FAN_MAX + 1); - EXPECT_EQ(DAIKIN_FAN_MAX, irdaikin.getFan()); + EXPECT_EQ(DAIKIN_FAN_AUTO, irdaikin.getFan()); irdaikin.setFan(DAIKIN_FAN_MAX - 1); EXPECT_EQ(DAIKIN_FAN_MAX - 1, irdaikin.getFan()); @@ -352,13 +427,248 @@ TEST(TestDaikinClass, FanSpeed) { irdaikin.setFan(DAIKIN_FAN_MIN + 1); EXPECT_EQ(DAIKIN_FAN_MIN + 1, irdaikin.getFan()); + // Beyond Min should default to Auto. + irdaikin.setFan(DAIKIN_FAN_MIN - 1); + EXPECT_EQ(DAIKIN_FAN_AUTO, irdaikin.getFan()); + irdaikin.setFan(3); EXPECT_EQ(3, irdaikin.getFan()); irdaikin.setFan(DAIKIN_FAN_AUTO); EXPECT_EQ(DAIKIN_FAN_AUTO, irdaikin.getFan()); + + irdaikin.setFan(DAIKIN_FAN_QUIET); + EXPECT_EQ(DAIKIN_FAN_QUIET, 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[DAIKIN_COMMAND_LENGTH] = { + 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[DAIKIN_COMMAND_LENGTH] = { + 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(), DAIKIN_BITS); + // toggle the power state. + irdaikin.setPower(!irdaikin.getPower()); + irdaikin.setTemp(21); + irdaikin.setMold(true); + EXPECT_STATE_EQ(expectedState, irdaikin.getRaw(), DAIKIN_BITS); + irdaikin.setRaw(initialState); + EXPECT_STATE_EQ(initialState, irdaikin.getRaw(), DAIKIN_BITS); +} + +TEST(TestDaikinClass, ChecksumValidation) { + uint8_t daikin_code[DAIKIN_COMMAND_LENGTH] = { + 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(DAIKIN_AUTO); + irdaikin.setTemp(25); + irdaikin.setFan(DAIKIN_FAN_AUTO); + 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); @@ -385,35 +695,138 @@ TEST(TestDaikinClass, MessageConstuction) { irsend.reset(); irsend.sendDaikin(irdaikin.getRaw()); EXPECT_EQ( - "m3650s1623" + "m428s428m428s428m428s428m428s428m428s428" + "m428s29428m3650s1623" "m428s1280m428s428m428s428m428s428m428s1280m428s428m428s428m428s428" "m428s428m428s1280m428s428m428s1280m428s1280m428s428m428s1280m428s1280" "m428s1280m428s1280m428s1280m428s428m428s428m428s1280m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s1280m428s1280m428s1280m428s1280" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s1280m428s428m428s1280m428s428m428s428m428s428m428s1280m428s1280" "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s1280m428s1280m428s1280m428s428m428s1280m428s428m428s1280m428s1280" + "m428s29428m3650s1623" + "m428s1280m428s428m428s428m428s428m428s1280m428s428m428s428m428s428" + "m428s428m428s1280m428s428m428s1280m428s1280m428s428m428s1280m428s1280" + "m428s1280m428s1280m428s1280m428s428m428s428m428s1280m428s428m428s428" "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s428m428s1280m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s29428" - "m3650s1623" + "m428s428m428s1280m428s428m428s428m428s428m428s428m428s1280m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s428m428s428m428s1280m428s428m428s1280m428s428m428s1280m428s428" + "m428s29428m3650s1623" "m428s1280m428s428m428s428m428s428m428s1280m428s428m428s428m428s428" "m428s428m428s1280m428s428m428s1280m428s1280m428s428m428s1280m428s1280" "m428s1280m428s1280m428s1280m428s428m428s428m428s1280m428s428m428s428" "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s1280m428s428m428s428m428s428m428s1280m428s1280m428s428m428s428" + "m428s1280m428s428m428s428m428s1280m428s1280m428s1280m428s428m428s428" "m428s428m428s1280m428s1280m428s428m428s1280m428s1280m428s428m428s428" "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" "m428s428m428s428m428s428m428s428m428s1280m428s1280m428s428m428s428" "m428s1280m428s1280m428s1280m428s1280m428s428m428s428m428s428m428s428" "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" + "m428s428m428s1280m428s1280m428s428m428s428m428s428m428s428m428s428" + "m428s428m428s428m428s428m428s428m428s428m428s1280m428s1280m428s428" "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" "m428s428m428s428m428s428m428s428m428s428m428s428m428s1280m428s1280" "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" - "m428s428m428s428m428s428m428s1280m428s1280m428s1280m428s1280m428s428" + "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[DAIKIN_COMMAND_LENGTH] = { + 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[DAIKIN_RAW_BITS] = { + 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, DAIKIN_RAW_BITS, 38000); + irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(DAIKIN, irsend.capture.decode_type); + ASSERT_EQ(DAIKIN_BITS, 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[DAIKIN_COMMAND_LENGTH] = { + 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(DAIKIN_BITS, irsend.capture.bits); + EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits); +} diff --git a/lib/IRremoteESP8266/test/ir_Denon_test.cpp b/lib/IRremoteESP8266/test/ir_Denon_test.cpp index 32de11b..321d91a 100644 --- a/lib/IRremoteESP8266/test/ir_Denon_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Denon_test.cpp @@ -32,7 +32,7 @@ TEST(TestSendDenon, SendDataOnly) { "m432s1296m432s432m432s432m432s432m432s1296m432s1296m432s432m432s1296" "m432s432m432s1296m432s1296m432s432m432s1296m432s1296m432s432m432s432" "m432s1296m432s1296m432s1296m432s432m432s432m432s432m432s1296m432s1296" - "m432s129600", irsend.outputStr()); + "m432s98928", irsend.outputStr()); } // Test sending with different repeats. @@ -91,7 +91,7 @@ TEST(TestSendDenon, Send48BitWithRepeats) { "m432s1296m432s432m432s432m432s432m432s1296m432s1296m432s432m432s1296" "m432s432m432s1296m432s1296m432s432m432s1296m432s1296m432s432m432s432" "m432s1296m432s1296m432s1296m432s432m432s432m432s432m432s1296m432s1296" - "m432s129600" + "m432s98928" "m3456s1728" "m432s432m432s432m432s1296m432s432m432s1296m432s432m432s1296m432s432" "m432s432m432s1296m432s432m432s432m432s1296m432s1296m432s432m432s432" @@ -99,7 +99,7 @@ TEST(TestSendDenon, Send48BitWithRepeats) { "m432s1296m432s432m432s432m432s432m432s1296m432s1296m432s432m432s1296" "m432s432m432s1296m432s1296m432s432m432s1296m432s1296m432s432m432s432" "m432s1296m432s1296m432s1296m432s432m432s432m432s432m432s1296m432s1296" - "m432s129600", irsend.outputStr()); + "m432s98928", irsend.outputStr()); irsend.sendDenon(0x2A4C028D6CE3, DENON_48_BITS, 2); // 2 repeats. EXPECT_EQ( "m3456s1728" @@ -109,7 +109,7 @@ TEST(TestSendDenon, Send48BitWithRepeats) { "m432s1296m432s432m432s432m432s432m432s1296m432s1296m432s432m432s1296" "m432s432m432s1296m432s1296m432s432m432s1296m432s1296m432s432m432s432" "m432s1296m432s1296m432s1296m432s432m432s432m432s432m432s1296m432s1296" - "m432s129600" + "m432s98928" "m3456s1728" "m432s432m432s432m432s1296m432s432m432s1296m432s432m432s1296m432s432" "m432s432m432s1296m432s432m432s432m432s1296m432s1296m432s432m432s432" @@ -117,7 +117,7 @@ TEST(TestSendDenon, Send48BitWithRepeats) { "m432s1296m432s432m432s432m432s432m432s1296m432s1296m432s432m432s1296" "m432s432m432s1296m432s1296m432s432m432s1296m432s1296m432s432m432s432" "m432s1296m432s1296m432s1296m432s432m432s432m432s432m432s1296m432s1296" - "m432s129600" + "m432s98928" "m3456s1728" "m432s432m432s432m432s1296m432s432m432s1296m432s432m432s1296m432s432" "m432s432m432s1296m432s432m432s432m432s1296m432s1296m432s432m432s432" @@ -125,7 +125,7 @@ TEST(TestSendDenon, Send48BitWithRepeats) { "m432s1296m432s432m432s432m432s432m432s1296m432s1296m432s432m432s1296" "m432s432m432s1296m432s1296m432s432m432s1296m432s1296m432s432m432s432" "m432s1296m432s1296m432s1296m432s432m432s432m432s432m432s1296m432s1296" - "m432s129600", irsend.outputStr()); + "m432s98928", irsend.outputStr()); } // Test sending an atypical data size. @@ -153,7 +153,7 @@ TEST(TestSendDenon, SendUnusualSize) { "m432s1296m432s432m432s1296m432s432m432s1296m432s432m432s1296m432s1296" "m432s1296m432s1296m432s432m432s432m432s1296m432s1296m432s432m432s1296" "m432s1296m432s1296m432s1296m432s432m432s1296m432s1296m432s1296m432s1296" - "m432s129600", irsend.outputStr()); + "m432s74736", irsend.outputStr()); } // Tests for decodeDenon(). diff --git a/lib/IRremoteESP8266/test/ir_Fujitsu_test.cpp b/lib/IRremoteESP8266/test/ir_Fujitsu_test.cpp index f1b6c04..49279a5 100644 --- a/lib/IRremoteESP8266/test/ir_Fujitsu_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Fujitsu_test.cpp @@ -1,5 +1,6 @@ -// Copyright 2017 Jonny Graham +// Copyright 2017 Jonny Graham, David Conran +#include "IRrecv_test.h" #include "IRsend.h" #include "IRsend_test.h" #include "ir_Fujitsu.h" @@ -17,156 +18,239 @@ template << "] (" << std::hex << e << std::dec << ")"; } } - return ::testing::AssertionSuccess(); } -// Tests for Mitsubishi A/C methods. +// Tests for Fujitsu A/C methods. // Test sending typical data only. -TEST(TestSendFujitsuAC, GetRawDefault) { - IRFujitsuAC fujitsuACSender = IRFujitsuAC(4); - fujitsuACSender.setCmd(FUJITSU_AC_CMD_TURN_ON); - fujitsuACSender.setSwing(FUJITSU_AC_SWING_BOTH); - fujitsuACSender.setMode(FUJITSU_AC_MODE_COOL); - fujitsuACSender.setFanSpeed(FUJITSU_AC_FAN_HIGH); - fujitsuACSender.setTemp(24); - uint8_t expected[16] = {0x14, 0x63, 0x0, 0x10, 0x10, 0xFE, 0x9, 0x30, - 0x81, 0x1, 0x31, 0x0, 0x0, 0x0, 0x20, 0xFD}; - EXPECT_TRUE(ArraysMatch(expected, fujitsuACSender.getRaw())); +TEST(TestIRFujitsuACClass, GetRawDefault) { + IRFujitsuAC fujitsu = IRFujitsuAC(4); // AR-RAH2E + fujitsu.setCmd(FUJITSU_AC_CMD_TURN_ON); + fujitsu.setSwing(FUJITSU_AC_SWING_BOTH); + fujitsu.setMode(FUJITSU_AC_MODE_COOL); + fujitsu.setFanSpeed(FUJITSU_AC_FAN_HIGH); + fujitsu.setTemp(24); + uint8_t expected_arrah2e[16] = { + 0x14, 0x63, 0x00, 0x10, 0x10, 0xFE, 0x09, 0x30, + 0x81, 0x01, 0x31, 0x00, 0x00, 0x00, 0x20, 0xFD}; + EXPECT_TRUE(ArraysMatch(expected_arrah2e, fujitsu.getRaw())); + EXPECT_EQ(FUJITSU_AC_STATE_LENGTH, fujitsu.getStateLength()); + EXPECT_EQ("Power: On, Mode: 1 (COOL), Temp: 24C, Fan: 1 (HIGH), " + "Swing: Vert + Horiz, Command: N/A", fujitsu.toString()); + + uint8_t expected_ardb1[15] = { + 0x14, 0x63, 0x00, 0x10, 0x10, 0xFC, 0x08, 0x30, + 0x81, 0x01, 0x31, 0x00, 0x00, 0x00, 0x1D}; + fujitsu.setModel(ARDB1); + EXPECT_TRUE(ArraysMatch(expected_ardb1, fujitsu.getRaw())); + EXPECT_EQ(FUJITSU_AC_STATE_LENGTH - 1, fujitsu.getStateLength()); + EXPECT_EQ("Power: On, Mode: 1 (COOL), Temp: 24C, Fan: 1 (HIGH), " + "Swing: Vert + Horiz, Command: N/A", fujitsu.toString()); } -TEST(TestSendFujitsuAC, GetRawTurnOff) { - IRFujitsuAC fujitsuACSender = IRFujitsuAC(4); - fujitsuACSender.off(); - uint8_t expected[7] = {0x14, 0x63, 0x0, 0x10, 0x10, 0x02, 0xFD}; - EXPECT_TRUE(ArraysMatch(expected, fujitsuACSender.getRaw())); +TEST(TestIRFujitsuACClass, GetRawTurnOff) { + IRFujitsuAC fujitsu = IRFujitsuAC(4); + fujitsu.setModel(ARRAH2E); + fujitsu.off(); + uint8_t expected_arrah2e[7] = {0x14, 0x63, 0x0, 0x10, 0x10, 0x02, 0xFD}; + EXPECT_TRUE(ArraysMatch(expected_arrah2e, fujitsu.getRaw())); + EXPECT_EQ(FUJITSU_AC_STATE_LENGTH_SHORT, fujitsu.getStateLength()); + EXPECT_EQ("Power: Off, Mode: 1 (COOL), Temp: 24C, Fan: 1 (HIGH), " + "Swing: Vert + Horiz, Command: N/A", fujitsu.toString()); + + fujitsu.setModel(ARDB1); + uint8_t expected_ardb1[6] = {0x14, 0x63, 0x0, 0x10, 0x10, 0x02}; + EXPECT_TRUE(ArraysMatch(expected_ardb1, fujitsu.getRaw())); + EXPECT_EQ(FUJITSU_AC_STATE_LENGTH_SHORT - 1, fujitsu.getStateLength()); + EXPECT_EQ("Power: Off, Mode: 1 (COOL), Temp: 24C, Fan: 1 (HIGH), " + "Swing: Vert + Horiz, Command: N/A", fujitsu.toString()); } -TEST(TestSendFujitsuAC, GetRawStepHoriz) { - IRFujitsuAC fujitsuACSender = IRFujitsuAC(4); - fujitsuACSender.stepHoriz(); + +TEST(TestIRFujitsuACClass, GetRawStepHoriz) { + IRFujitsuAC fujitsu = IRFujitsuAC(4); + fujitsu.stepHoriz(); uint8_t expected[7] = {0x14, 0x63, 0x0, 0x10, 0x10, 0x79, 0x86}; - EXPECT_TRUE(ArraysMatch(expected, fujitsuACSender.getRaw())); -} -TEST(TestSendFujitsuAC, GetRawStepVert) { - IRFujitsuAC fujitsuACSender = IRFujitsuAC(4); - fujitsuACSender.stepVert(); - uint8_t expected[7] = {0x14, 0x63, 0x0, 0x10, 0x10, 0x6C, 0x93}; - EXPECT_TRUE(ArraysMatch(expected, fujitsuACSender.getRaw())); + EXPECT_TRUE(ArraysMatch(expected, fujitsu.getRaw())); + EXPECT_EQ(FUJITSU_AC_STATE_LENGTH_SHORT, fujitsu.getStateLength()); + EXPECT_EQ("Power: On, Mode: 1 (COOL), Temp: 24C, Fan: 1 (HIGH), " + "Swing: Vert + Horiz, Command: Step vane horizontally", + fujitsu.toString()); } -TEST(TestSendFujitsuAC, GetRawWithSwingHoriz) { - IRFujitsuAC fujitsuACSender = IRFujitsuAC(4); - fujitsuACSender.setCmd(FUJITSU_AC_CMD_STAY_ON); - fujitsuACSender.setSwing(FUJITSU_AC_SWING_HORIZ); - fujitsuACSender.setMode(FUJITSU_AC_MODE_COOL); - fujitsuACSender.setFanSpeed(FUJITSU_AC_FAN_QUIET); - fujitsuACSender.setTemp(25); +TEST(TestIRFujitsuACClass, GetRawStepVert) { + IRFujitsuAC fujitsu = IRFujitsuAC(4); + fujitsu.setModel(ARRAH2E); + fujitsu.stepVert(); + uint8_t expected_arrah2e[7] = {0x14, 0x63, 0x0, 0x10, 0x10, 0x6C, 0x93}; + EXPECT_TRUE(ArraysMatch(expected_arrah2e, fujitsu.getRaw())); + EXPECT_EQ(FUJITSU_AC_STATE_LENGTH_SHORT, fujitsu.getStateLength()); + EXPECT_EQ("Power: On, Mode: 1 (COOL), Temp: 24C, Fan: 1 (HIGH), " + "Swing: Vert + Horiz, Command: Step vane vertically", + fujitsu.toString()); + + fujitsu.setModel(ARDB1); + fujitsu.stepVert(); + uint8_t expected_ardb1[6] = {0x14, 0x63, 0x0, 0x10, 0x10, 0x6C}; + EXPECT_TRUE(ArraysMatch(expected_ardb1, fujitsu.getRaw())); + EXPECT_EQ(FUJITSU_AC_STATE_LENGTH_SHORT - 1, + fujitsu.getStateLength()); + EXPECT_EQ("Power: On, Mode: 1 (COOL), Temp: 24C, Fan: 1 (HIGH), " + "Swing: Vert + Horiz, Command: Step vane vertically", + fujitsu.toString()); +} + +TEST(TestIRFujitsuACClass, GetRawWithSwingHoriz) { + IRFujitsuAC fujitsu = IRFujitsuAC(4); + fujitsu.setCmd(FUJITSU_AC_CMD_STAY_ON); + fujitsu.setSwing(FUJITSU_AC_SWING_HORIZ); + fujitsu.setMode(FUJITSU_AC_MODE_COOL); + fujitsu.setFanSpeed(FUJITSU_AC_FAN_QUIET); + fujitsu.setTemp(25); uint8_t expected[16] = {0x14, 0x63, 0x0, 0x10, 0x10, 0xFE, 0x9, 0x30, 0x90, 0x1, 0x24, 0x0, 0x0, 0x0, 0x20, 0xFB}; - EXPECT_TRUE(ArraysMatch(expected, fujitsuACSender.getRaw())); + EXPECT_TRUE(ArraysMatch(expected, fujitsu.getRaw())); + EXPECT_EQ("Power: On, Mode: 1 (COOL), Temp: 25C, Fan: 4 (QUIET), " + "Swing: Horiz, Command: N/A", + fujitsu.toString()); } -TEST(TestSendFujitsuAC, GetRawWithFan) { - IRFujitsuAC fujitsuACSender = IRFujitsuAC(4); - fujitsuACSender.setCmd(FUJITSU_AC_CMD_STAY_ON); - fujitsuACSender.setSwing(FUJITSU_AC_SWING_HORIZ); - fujitsuACSender.setMode(FUJITSU_AC_MODE_FAN); - fujitsuACSender.setFanSpeed(FUJITSU_AC_FAN_MED); - fujitsuACSender.setTemp(20); // temp doesn't matter for fan - // but it is sent by the RC anyway - uint8_t expected[16] = {0x14, 0x63, 0x0, 0x10, 0x10, 0xFE, 0x9, 0x30, - 0x40, 0x3, 0x22, 0x0, 0x0, 0x0, 0x20, 0x4B}; - EXPECT_TRUE(ArraysMatch(expected, fujitsuACSender.getRaw())); +TEST(TestIRFujitsuACClass, GetRawWithFan) { + IRFujitsuAC fujitsu = IRFujitsuAC(4); + fujitsu.setCmd(FUJITSU_AC_CMD_STAY_ON); + fujitsu.setSwing(FUJITSU_AC_SWING_HORIZ); + fujitsu.setMode(FUJITSU_AC_MODE_FAN); + fujitsu.setFanSpeed(FUJITSU_AC_FAN_MED); + fujitsu.setTemp(20); // temp doesn't matter for fan + // but it is sent by the RC anyway + fujitsu.setModel(ARRAH2E); + uint8_t expected_arrah2e[16] = { + 0x14, 0x63, 0x0, 0x10, 0x10, 0xFE, 0x9, 0x30, + 0x40, 0x3, 0x22, 0x0, 0x0, 0x0, 0x20, 0x4B}; + EXPECT_TRUE(ArraysMatch(expected_arrah2e, fujitsu.getRaw())); + EXPECT_EQ(FUJITSU_AC_STATE_LENGTH, fujitsu.getStateLength()); + EXPECT_EQ("Power: On, Mode: 3 (FAN), Temp: 20C, Fan: 2 (MED), Swing: Horiz, " + "Command: N/A", fujitsu.toString()); + + fujitsu.setModel(ARDB1); + uint8_t expected_ardb1[15] = { + 0x14, 0x63, 0x0, 0x10, 0x10, 0xFC, 0x8, 0x30, + 0x40, 0x3, 0x22, 0x0, 0x0, 0x0, 0x6B}; + EXPECT_TRUE(ArraysMatch(expected_ardb1, fujitsu.getRaw())); + EXPECT_EQ(FUJITSU_AC_STATE_LENGTH - 1, fujitsu.getStateLength()); + EXPECT_EQ("Power: On, Mode: 3 (FAN), Temp: 20C, Fan: 2 (MED), Swing: Horiz, " + "Command: N/A", fujitsu.toString()); +} + +TEST(TestIRFujitsuACClass, SetRaw) { + IRFujitsuAC fujitsu = IRFujitsuAC(0); + EXPECT_EQ(FUJITSU_AC_STATE_LENGTH, fujitsu.getStateLength()); + uint8_t expected_default_arrah2e[FUJITSU_AC_STATE_LENGTH] = { + 0x14, 0x63, 0x00, 0x10, 0x10, 0xFE, 0x09, 0x30, + 0x81, 0x01, 0x31, 0x00, 0x00, 0x00, 0x20, 0xFD}; + EXPECT_TRUE(ArraysMatch(expected_default_arrah2e, fujitsu.getRaw())); + EXPECT_EQ("Power: On, Mode: 1 (COOL), Temp: 24C, Fan: 1 (HIGH), " + "Swing: Vert + Horiz, Command: N/A", fujitsu.toString()); + // Now set a new state via setRaw(); + // This state is a real state from an AR-DB1 remote. + uint8_t new_state1[FUJITSU_AC_STATE_LENGTH - 1] = { + 0x14, 0x63, 0x00, 0x10, 0x10, 0xFC, 0x08, 0x30, + 0x30, 0x01, 0x00, 0x00, 0x00, 0x00, 0x9F}; + fujitsu.setRaw(new_state1, FUJITSU_AC_STATE_LENGTH - 1); + EXPECT_EQ(FUJITSU_AC_STATE_LENGTH - 1, fujitsu.getStateLength()); + EXPECT_TRUE(ArraysMatch(new_state1, fujitsu.getRaw())); + EXPECT_EQ("Power: On, Mode: 1 (COOL), Temp: 19C, Fan: 0 (AUTO), " + "Swing: Off, Command: N/A", fujitsu.toString()); } TEST(TestSendFujitsuAC, GenerateMessage) { - IRFujitsuAC fujitsuACSender = IRFujitsuAC(4); + IRFujitsuAC fujitsu = IRFujitsuAC(4); IRsendTest irsend(4); - fujitsuACSender.begin(); + fujitsu.begin(); irsend.begin(); - fujitsuACSender.setCmd(FUJITSU_AC_CMD_STAY_ON); - fujitsuACSender.setSwing(FUJITSU_AC_SWING_BOTH); - fujitsuACSender.setMode(FUJITSU_AC_MODE_COOL); - fujitsuACSender.setFanSpeed(FUJITSU_AC_FAN_HIGH); - fujitsuACSender.setTemp(24); + fujitsu.setCmd(FUJITSU_AC_CMD_STAY_ON); + fujitsu.setSwing(FUJITSU_AC_SWING_BOTH); + fujitsu.setMode(FUJITSU_AC_MODE_COOL); + fujitsu.setFanSpeed(FUJITSU_AC_FAN_HIGH); + fujitsu.setTemp(24); - EXPECT_EQ(FUJITSU_AC_FAN_HIGH, fujitsuACSender.getFanSpeed()); - EXPECT_EQ(FUJITSU_AC_MODE_COOL, fujitsuACSender.getMode()); - EXPECT_EQ(24, fujitsuACSender.getTemp()); - EXPECT_EQ(FUJITSU_AC_SWING_BOTH, fujitsuACSender.getSwing()); - EXPECT_EQ(FUJITSU_AC_CMD_STAY_ON, fujitsuACSender.getCmd()); + EXPECT_EQ(FUJITSU_AC_FAN_HIGH, fujitsu.getFanSpeed()); + EXPECT_EQ(FUJITSU_AC_MODE_COOL, fujitsu.getMode()); + EXPECT_EQ(24, fujitsu.getTemp()); + EXPECT_EQ(FUJITSU_AC_SWING_BOTH, fujitsu.getSwing()); + EXPECT_EQ(FUJITSU_AC_CMD_STAY_ON, fujitsu.getCmd()); irsend.reset(); - irsend.sendFujitsuAC(fujitsuACSender.getRaw(), FUJITSU_AC_STATE_LENGTH); + irsend.sendFujitsuAC(fujitsu.getRaw(), FUJITSU_AC_STATE_LENGTH); EXPECT_EQ( - "m3224s1574m448s367m448s367m448s1182m448s367m448s1182m448s367m448s367m448" - "s367m448s1182m448s1182m448s367m448s367m448s367m448s1182m448s1182m448s367" - "m448s367m448s367m448s367m448s367m448s367m448s367m448s367m448s367m448" - "s367m448s367m448s367m448s367m448s1182m448s367m448s367m448s367m448s367m448" - "s367m448s367m448s367m448s1182m448s367m448s367m448s367m448s367m448s1182" - "m448s1182m448s1182m448s1182m448s1182m448s1182m448s1182m448s1182m448s367" - "m448s367m448s1182m448s367m448s367m448s367m448s367m448s367m448s367m448s367" - "m448s367m448s1182m448s1182m448s367m448s367m448s367m448s367m448s367m448s367" - "m448s367m448s367m448s367m448s1182m448s1182m448s367m448s367m448s367m448" - "s367m448s367m448s367m448s367m448s1182m448s367m448s367m448s367m448s1182m448" - "s1182m448s367m448s367m448s367m448s367m448s367m448s367m448s367m448s367m448" - "s367m448s367m448s367m448s367m448s367m448s367m448s367m448s367m448s367m448" - "s367m448s367m448s367m448s367m448s367m448s367m448s367m448s367m448s367m448" - "s367m448s367m448s367m448s367m448s367m448s1182m448s367m448s367m448s367m448" + "m3324s1574m448s390m448s390m448s1182m448s390m448s1182m448s390m448s390m448" + "s390m448s1182m448s1182m448s390m448s390m448s390m448s1182m448s1182m448s390" + "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448" + "s390m448s390m448s390m448s390m448s1182m448s390m448s390m448s390m448s390m448" + "s390m448s390m448s390m448s1182m448s390m448s390m448s390m448s390m448s1182" + "m448s1182m448s1182m448s1182m448s1182m448s1182m448s1182m448s1182m448s390" + "m448s390m448s1182m448s390m448s390m448s390m448s390m448s390m448s390m448s390" + "m448s390m448s1182m448s1182m448s390m448s390m448s390m448s390m448s390m448s390" + "m448s390m448s390m448s390m448s1182m448s1182m448s390m448s390m448s390m448" + "s390m448s390m448s390m448s390m448s1182m448s390m448s390m448s390m448s1182m448" + "s1182m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448" + "s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448" + "s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448" + "s390m448s390m448s390m448s390m448s390m448s1182m448s390m448s390m448s390m448" "s1182m448s1182m448s1182m448s1182m448s1182m448s1182m448s1182m448s8100", irsend.outputStr()); } + TEST(TestSendFujitsuAC, GenerateShortMessage) { - IRFujitsuAC fujitsuACSender = IRFujitsuAC(4); + IRFujitsuAC fujitsu = IRFujitsuAC(4); IRsendTest irsend(4); - fujitsuACSender.begin(); + fujitsu.begin(); irsend.begin(); - fujitsuACSender.off(); + fujitsu.off(); - EXPECT_EQ(FUJITSU_AC_CMD_TURN_OFF, fujitsuACSender.getCmd()); + EXPECT_EQ(FUJITSU_AC_CMD_TURN_OFF, fujitsu.getCmd()); irsend.reset(); - irsend.sendFujitsuAC(fujitsuACSender.getRaw(), FUJITSU_AC_STATE_LENGTH_SHORT); + irsend.sendFujitsuAC(fujitsu.getRaw(), FUJITSU_AC_STATE_LENGTH_SHORT); EXPECT_EQ( - "m3224s1574m448s367m448s367m448s1182m448s367m448s1182m448s367m448s367m448" - "s367m448s1182m448s1182m448s367m448s367m448s367m448s1182m448s1182m448s367" - "m448s367m448s367m448s367m448s367m448s367m448s367m448s367m448s367m448s367" - "m448s367m448s367m448s367m448s1182m448s367m448s367m448s367m448s367m448s367" - "m448s367m448s367m448s1182m448s367m448s367m448s367m448s367m448s1182m448s367" - "m448s367m448s367m448s367m448s367m448s367m448s1182m448s367m448s1182m448" + "m3324s1574m448s390m448s390m448s1182m448s390m448s1182m448s390m448s390m448" + "s390m448s1182m448s1182m448s390m448s390m448s390m448s1182m448s1182m448s390" + "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" + "m448s390m448s390m448s390m448s1182m448s390m448s390m448s390m448s390m448s390" + "m448s390m448s390m448s1182m448s390m448s390m448s390m448s390m448s1182m448s390" + "m448s390m448s390m448s390m448s390m448s390m448s1182m448s390m448s1182m448" "s1182m448s1182m448s1182m448s1182m448s1182m448s8100", irsend.outputStr()); } // Issue #275 TEST(TestSendFujitsuAC, Issue275) { - IRFujitsuAC fujitsuACSender = IRFujitsuAC(4); + IRFujitsuAC fujitsu = IRFujitsuAC(4); IRsendTest irsend(4); - fujitsuACSender.begin(); + fujitsu.begin(); irsend.begin(); irsend.reset(); - fujitsuACSender.setCmd(FUJITSU_AC_CMD_TURN_OFF); - irsend.sendFujitsuAC(fujitsuACSender.getRaw(), FUJITSU_AC_STATE_LENGTH_SHORT); + fujitsu.setCmd(FUJITSU_AC_CMD_TURN_OFF); + irsend.sendFujitsuAC(fujitsu.getRaw(), FUJITSU_AC_STATE_LENGTH_SHORT); EXPECT_EQ( // Header - "m3224s1574" + "m3324s1574" // 0 0 1 0 1 0 0 0 (0x28) - "m448s367m448s367m448s1182m448s367m448s1182m448s367m448s367m448s367" + "m448s390m448s390m448s1182m448s390m448s1182m448s390m448s390m448s390" // 1 1 0 0 0 1 1 0 (0xC6) - "m448s1182m448s1182m448s367m448s367m448s367m448s1182m448s1182m448s367" + "m448s1182m448s1182m448s390m448s390m448s390m448s1182m448s1182m448s390" // 0 0 0 0 0 0 0 0 (0x00) - "m448s367m448s367m448s367m448s367m448s367m448s367m448s367m448s367" + "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" // 0 0 0 0 1 0 0 0 (0x08) - "m448s367m448s367m448s367m448s367m448s1182m448s367m448s367m448s367" + "m448s390m448s390m448s390m448s390m448s1182m448s390m448s390m448s390" // 0 0 0 0 1 0 0 0 (0x08) - "m448s367m448s367m448s367m448s367m448s1182m448s367m448s367m448s367" + "m448s390m448s390m448s390m448s390m448s1182m448s390m448s390m448s390" // 0 1 0 0 0 0 0 0 (0x40) - "m448s367m448s1182m448s367m448s367m448s367m448s367m448s367m448s367" + "m448s390m448s1182m448s390m448s390m448s390m448s390m448s390m448s390" // 1 0 1 1 1 1 1 1 (0xBF) - "m448s1182m448s367m448s1182m448s1182m448s1182m448s1182m448s1182m448s1182" + "m448s1182m448s390m448s1182m448s1182m448s1182m448s1182m448s1182m448s1182" // Footer "m448s8100", irsend.outputStr()); @@ -205,3 +289,264 @@ TEST(TestSendFujitsuAC, Issue275) { "m450", irsend.outputStr()); } + +TEST(TestDecodeFujitsuAC, SyntheticShortMessages) { + IRsendTest irsend(0); + IRFujitsuAC fujitsu = IRFujitsuAC(0); + IRrecv irrecv(0); + + irsend.begin(); + irsend.reset(); + + fujitsu.setModel(ARRAH2E); + fujitsu.setCmd(FUJITSU_AC_CMD_TURN_OFF); + irsend.sendFujitsuAC(fujitsu.getRaw(), fujitsu.getStateLength()); + irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(FUJITSU_AC, irsend.capture.decode_type); + ASSERT_EQ(FUJITSU_AC_MIN_BITS + 8, irsend.capture.bits); + uint8_t expected_arrah2e[7] = {0x14, 0x63, 0x0, 0x10, 0x10, 0x02, 0xFD}; + EXPECT_TRUE(ArraysMatch(expected_arrah2e, irsend.capture.state)); + + irsend.reset(); + + fujitsu.setModel(ARDB1); + fujitsu.setCmd(FUJITSU_AC_CMD_TURN_OFF); + irsend.sendFujitsuAC(fujitsu.getRaw(), fujitsu.getStateLength()); + irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(FUJITSU_AC, irsend.capture.decode_type); + ASSERT_EQ(FUJITSU_AC_MIN_BITS, irsend.capture.bits); + uint8_t expected_ardb1[6] = {0x14, 0x63, 0x0, 0x10, 0x10, 0x02}; + EXPECT_TRUE(ArraysMatch(expected_ardb1, irsend.capture.state)); +} + +TEST(TestDecodeFujitsuAC, SyntheticLongMessages) { + IRsendTest irsend(0); + IRFujitsuAC fujitsu = IRFujitsuAC(0); + IRrecv irrecv(0); + irsend.begin(); + + irsend.reset(); + + fujitsu.setModel(ARRAH2E); + fujitsu.setCmd(FUJITSU_AC_CMD_STAY_ON); + fujitsu.setSwing(FUJITSU_AC_SWING_VERT); + fujitsu.setMode(FUJITSU_AC_MODE_COOL); + fujitsu.setFanSpeed(FUJITSU_AC_FAN_QUIET); + fujitsu.setTemp(18); + irsend.sendFujitsuAC(fujitsu.getRaw(), fujitsu.getStateLength()); + ASSERT_EQ(FUJITSU_AC_STATE_LENGTH, fujitsu.getStateLength()); + irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decodeFujitsuAC(&irsend.capture)); + ASSERT_EQ(FUJITSU_AC, irsend.capture.decode_type); + ASSERT_EQ(FUJITSU_AC_BITS, irsend.capture.bits); + uint8_t expected_arrah2e[FUJITSU_AC_STATE_LENGTH] = { + 0x14, 0x63, 0x00, 0x10, 0x10, 0xFE, 0x09, 0x30, + 0x20, 0x01, 0x14, 0x00, 0x00, 0x00, 0x20, 0x7B}; + EXPECT_TRUE(ArraysMatch(expected_arrah2e, irsend.capture.state)); + fujitsu.setRaw(irsend.capture.state, irsend.capture.bits / 8); + EXPECT_EQ(FUJITSU_AC_STATE_LENGTH, fujitsu.getStateLength()); + EXPECT_EQ("Power: On, Mode: 1 (COOL), Temp: 18C, Fan: 4 (QUIET), " + "Swing: Vert, Command: N/A", fujitsu.toString()); + + irsend.reset(); + + fujitsu.setModel(ARDB1); + irsend.sendFujitsuAC(fujitsu.getRaw(), fujitsu.getStateLength()); + irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(FUJITSU_AC, irsend.capture.decode_type); + ASSERT_EQ(FUJITSU_AC_BITS - 8, irsend.capture.bits); + uint8_t expected_ardb1[FUJITSU_AC_STATE_LENGTH - 1] = { + 0x14, 0x63, 0x00, 0x10, 0x10, 0xFC, 0x08, 0x30, + 0x20, 0x01, 0x14, 0x00, 0x00, 0x00, 0x9B}; + EXPECT_TRUE(ArraysMatch(expected_ardb1, irsend.capture.state)); + fujitsu.setRaw(irsend.capture.state, irsend.capture.bits / 8); + EXPECT_EQ(FUJITSU_AC_STATE_LENGTH - 1, fujitsu.getStateLength()); + EXPECT_EQ("Power: On, Mode: 1 (COOL), Temp: 18C, Fan: 4 (QUIET), " + "Swing: Vert, Command: N/A", fujitsu.toString()); +} + +TEST(TestDecodeFujitsuAC, RealShortARDB1OffExample) { + IRsendTest irsend(0); + IRrecv irrecv(0); + IRFujitsuAC fujitsu = IRFujitsuAC(0); + + irsend.begin(); + + irsend.reset(); + // "Off" Message recorded from an AR-DB1 remote. + uint16_t rawData[99] = { + 3310, 1636, 440, 386, 440, 394, 442, 1210, 442, 390, 414, 1220, + 444, 390, 446, 380, 446, 380, 436, 1216, 438, 1214, 438, 388, + 438, 386, 438, 396, 410, 1222, 440, 1220, 442, 384, 442, 384, + 442, 384, 442, 382, 444, 382, 442, 382, 444, 380, 446, 380, + 446, 380, 444, 380, 436, 390, 436, 388, 436, 388, 438, 1214, + 438, 386, 438, 388, 438, 386, 440, 386, 440, 384, 442, 384, + 442, 384, 442, 1210, 444, 382, 444, 382, 444, 382, 444, 380, + 446, 1206, 436, 390, 436, 388, 436, 388, 438, 388, 438, 396, + 420, 388, 436}; + irsend.sendRaw(rawData, 99, 38000); + irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(FUJITSU_AC, irsend.capture.decode_type); + ASSERT_EQ(FUJITSU_AC_MIN_BITS, irsend.capture.bits); + uint8_t expected[6] = {0x14, 0x63, 0x0, 0x10, 0x10, 0x02}; + EXPECT_TRUE(ArraysMatch(expected, irsend.capture.state)); + fujitsu.setRaw(irsend.capture.state, irsend.capture.bits / 8); + EXPECT_EQ(FUJITSU_AC_STATE_LENGTH_SHORT - 1, fujitsu.getStateLength()); + EXPECT_EQ("Power: Off, Mode: 0 (AUTO), Temp: 16C, Fan: 0 (AUTO), " + "Swing: Off, Command: N/A", fujitsu.toString()); +} + +TEST(TestDecodeFujitsuAC, RealLongARDB1Example) { + IRsendTest irsend(0); + IRrecv irrecv(0); + IRFujitsuAC fujitsu = IRFujitsuAC(0); + + irsend.begin(); + irsend.reset(); + uint16_t rawData1[243] = { + 3316, 1632, 444, 390, 438, 388, 436, 1216, 438, 388, 438, 1214, + 438, 388, 438, 386, 440, 386, 440, 1212, 440, 1210, 442, 392, + 412, 396, 442, 392, 444, 1208, 444, 1208, 444, 380, 444, 380, + 446, 380, 436, 390, 436, 390, 436, 390, 436, 388, 438, 388, + 438, 388, 438, 388, 438, 386, 438, 386, 440, 384, 440, 1210, + 442, 384, 442, 382, 442, 384, 442, 384, 442, 382, 442, 382, + 444, 382, 444, 1208, 444, 382, 444, 380, 446, 380, 436, 390, + 436, 390, 436, 1214, 438, 1214, 438, 1212, 440, 1212, 440, 1220, + 412, 1222, 440, 394, 442, 382, 442, 382, 444, 1208, 444, 382, + 444, 380, 446, 380, 446, 380, 434, 390, 436, 388, 438, 388, + 438, 388, 438, 1214, 438, 1212, 440, 386, 440, 394, 412, 1222, + 440, 394, 442, 384, 442, 384, 442, 382, 442, 1208, 444, 390, + 414, 394, 442, 1216, 446, 380, 436, 390, 436, 390, 436, 388, + 436, 390, 436, 388, 438, 386, 440, 386, 440, 386, 438, 1212, + 440, 386, 440, 384, 440, 384, 442, 392, 412, 396, 440, 394, + 442, 382, 444, 382, 444, 382, 444, 380, 444, 380, 444, 382, + 444, 380, 446, 380, 436, 388, 436, 390, 436, 388, 438, 388, + 438, 388, 438, 388, 438, 386, 440, 386, 440, 386, 442, 384, + 440, 386, 442, 384, 440, 384, 442, 384, 442, 382, 442, 382, + 444, 1208, 444, 382, 444, 1208, 444, 380, 446, 1206, 436, 390, + 436, 1216, 436}; + irsend.sendRaw(rawData1, 243, 38000); + irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(FUJITSU_AC, irsend.capture.decode_type); + ASSERT_EQ(FUJITSU_AC_BITS - 8, irsend.capture.bits); + uint8_t expected1[FUJITSU_AC_STATE_LENGTH - 1] = { + 0x14, 0x63, 0x00, 0x10, 0x10, 0xFC, 0x08, 0x30, + 0x21, 0x01, 0x04, 0x00, 0x00, 0x00, 0xAA}; + EXPECT_TRUE(ArraysMatch(expected1, irsend.capture.state)); + fujitsu.setRaw(irsend.capture.state, irsend.capture.bits / 8); + EXPECT_EQ(FUJITSU_AC_STATE_LENGTH - 1, fujitsu.getStateLength()); + EXPECT_EQ("Power: On, Mode: 1 (COOL), Temp: 18C, Fan: 4 (QUIET), " + "Swing: Off, Command: N/A", fujitsu.toString()); + + irsend.reset(); + uint16_t rawData2[243] = { + 3316, 1630, 436, 398, 438, 386, 438, 1212, 440, 384, 440, 1212, + 442, 384, 442, 392, 414, 394, 442, 1218, 446, 1206, 436, 390, + 436, 388, 438, 388, 438, 1214, 440, 1212, 440, 384, 442, 384, + 442, 384, 442, 382, 444, 382, 444, 382, 444, 380, 446, 380, + 444, 380, 436, 390, 436, 388, 438, 396, 418, 388, 438, 1232, + 410, 396, 440, 394, 442, 384, 442, 384, 442, 382, 442, 392, + 414, 392, 444, 1216, 446, 380, 436, 390, 436, 396, 418, 390, + 436, 398, 438, 1214, 440, 1212, 440, 1210, 442, 1208, 444, 1216, + 416, 1218, 444, 388, 436, 390, 436, 388, 438, 1214, 440, 386, + 438, 386, 440, 386, 440, 384, 442, 384, 442, 384, 442, 382, + 444, 382, 444, 1206, 446, 1206, 436, 390, 436, 388, 438, 388, + 438, 386, 440, 394, 410, 396, 440, 1220, 442, 1210, 442, 392, + 414, 394, 442, 1218, 446, 406, 410, 388, 436, 390, 436, 390, + 436, 388, 438, 386, 440, 386, 440, 386, 440, 386, 440, 384, + 442, 384, 442, 384, 442, 382, 444, 382, 444, 380, 446, 380, + 446, 380, 436, 390, 436, 390, 436, 388, 438, 386, 438, 388, + 438, 386, 440, 386, 440, 384, 442, 384, 442, 384, 442, 384, + 442, 382, 444, 382, 444, 380, 446, 380, 446, 380, 436, 390, + 436, 388, 436, 388, 438, 386, 438, 386, 440, 386, 440, 1212, + 440, 1210, 442, 1210, 442, 1208, 444, 1208, 436, 390, 436, 388, + 436, 1214, 440}; + irsend.sendRaw(rawData2, 243, 38000); + irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(FUJITSU_AC, irsend.capture.decode_type); + ASSERT_EQ(FUJITSU_AC_BITS - 8, irsend.capture.bits); + uint8_t expected2[FUJITSU_AC_STATE_LENGTH - 1] = { + 0x14, 0x63, 0x00, 0x10, 0x10, 0xFC, 0x08, 0x30, + 0x30, 0x01, 0x00, 0x00, 0x00, 0x00, 0x9F}; + EXPECT_TRUE(ArraysMatch(expected2, irsend.capture.state)); + fujitsu.setRaw(irsend.capture.state, irsend.capture.bits / 8); + EXPECT_EQ(FUJITSU_AC_STATE_LENGTH - 1, fujitsu.getStateLength()); + EXPECT_EQ("Power: On, Mode: 1 (COOL), Temp: 19C, Fan: 0 (AUTO), " + "Swing: Off, Command: N/A", fujitsu.toString()); +} + +TEST(TestDecodeFujitsuAC, Issue414) { + IRsendTest irsend(0); + IRrecv irrecv(0); + IRFujitsuAC fujitsu = IRFujitsuAC(0); + + // Capture as supplied by arpmota + uint16_t rawData[259] = {3352, 1574, 480, 350, 480, 346, 480, 1190, 458, 346, + 508, 1140, 480, 346, 506, 346, 458, 346, 480, 1168, 480, 1192, 452, 374, + 458, 346, 480, 346, 508, 1168, 480, 1140, 480, 346, 506, 346, 458, 346, + 480, 346, 480, 346, 480, 346, 484, 372, 454, 374, 456, 346, 508, 318, + 480, 374, 458, 374, 480, 318, 480, 1196, 452, 346, 480, 346, 484, 342, + 484, 346, 480, 374, 458, 346, 506, 318, 508, 1170, 452, 346, 480, 374, + 458, 346, 506, 318, 480, 1196, 452, 1190, 458, 1162, 480, 1196, 452, + 1170, 480, 1190, 458, 1164, 480, 1196, 480, 318, 508, 346, 456, 1192, + 480, 346, 456, 374, 452, 346, 480, 374, 458, 342, 484, 346, 508, 346, + 456, 342, 512, 1164, 458, 1164, 508, 346, 456, 346, 480, 1190, 456, 342, + 484, 346, 506, 346, 456, 374, 452, 346, 508, 346, 458, 1164, 508, 346, + 458, 374, 452, 1168, 480, 374, 480, 318, 480, 374, 456, 346, 508, 318, + 480, 346, 484, 374, 480, 318, 484, 342, 484, 374, 480, 318, 484, 342, + 484, 346, 508, 318, 508, 346, 458, 346, 506, 318, 480, 374, 458, 346, + 506, 318, 480, 346, 484, 374, 480, 318, 482, 372, 456, 346, 508, 318, + 506, 348, 456, 342, 484, 346, 508, 318, 484, 374, 480, 318, 508, 318, + 484, 346, 508, 318, 480, 374, 456, 346, 508, 346, 480, 318, 480, 346, + 484, 374, 480, 320, 484, 1164, 508, 346, 458, 342, 512, 1164, 458, 1190, + 454, 346, 484, 1164, 508, 346, 458, 1164, 480, 350, 480, 374, 480}; + uint8_t state[16] = { + 0x14, 0x63, 0x00, 0x10, 0x10, 0xFE, 0x09, 0x30, 0x81, 0x04, 0x00, 0x00, + 0x00, 0x00, 0x20, 0x2B}; + irsend.begin(); + irsend.reset(); + irsend.sendRaw(rawData, 259, 38000); + irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(FUJITSU_AC, irsend.capture.decode_type); + ASSERT_EQ(FUJITSU_AC_BITS, irsend.capture.bits); + EXPECT_TRUE(ArraysMatch(state, irsend.capture.state)); + fujitsu.setRaw(irsend.capture.state, irsend.capture.bits / 8); + EXPECT_EQ(FUJITSU_AC_STATE_LENGTH, fujitsu.getStateLength()); + EXPECT_EQ("Power: On, Mode: 4 (HEAT), Temp: 24C, Fan: 0 (AUTO), " + "Swing: Off, Command: N/A", fujitsu.toString()); + + // Resend it using the state this time. + irsend.reset(); + irsend.sendFujitsuAC(state, 16); + irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(FUJITSU_AC, irsend.capture.decode_type); + ASSERT_EQ(FUJITSU_AC_BITS, irsend.capture.bits); + EXPECT_TRUE(ArraysMatch(state, irsend.capture.state)); + EXPECT_EQ( + "m3324s1574" + "m448s390m448s390m448s1182m448s390m448s1182m448s390m448s390m448s390" + "m448s1182m448s1182m448s390m448s390m448s390m448s1182m448s1182m448s390" + "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" + "m448s390m448s390m448s390m448s390m448s1182m448s390m448s390m448s390" + "m448s390m448s390m448s390m448s390m448s1182m448s390m448s390m448s390" + "m448s390m448s1182m448s1182m448s1182m448s1182m448s1182m448s1182m448s1182" + "m448s1182m448s390m448s390m448s1182m448s390m448s390m448s390m448s390" + "m448s390m448s390m448s390m448s390m448s1182m448s1182m448s390m448s390" + "m448s1182m448s390m448s390m448s390m448s390m448s390m448s390m448s1182" + "m448s390m448s390m448s1182m448s390m448s390m448s390m448s390m448s390" + "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" + "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" + "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" + "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" + "m448s390m448s390m448s390m448s390m448s390m448s1182m448s390m448s390" + "m448s1182m448s1182m448s390m448s1182m448s390m448s1182m448s390m448s390" + "m448s8100", irsend.outputStr()); +} diff --git a/lib/IRremoteESP8266/test/ir_GICable_test.cpp b/lib/IRremoteESP8266/test/ir_GICable_test.cpp new file mode 100644 index 0000000..3eb5b67 --- /dev/null +++ b/lib/IRremoteESP8266/test/ir_GICable_test.cpp @@ -0,0 +1,152 @@ +// Copyright 2018 David Conran + +#include "IRsend.h" +#include "IRsend_test.h" +#include "gtest/gtest.h" + +// Tests for sendGICable(). + +// Test sending typical data only. +TEST(TestSendGICable, SendDataOnly) { + IRsendTest irsend(0); + irsend.begin(); + irsend.sendGICable(0); + EXPECT_EQ( + "m9000s4400" + "m550s2200m550s2200m550s2200m550s2200m550s2200m550s2200m550s2200m550s2200" + "m550s2200m550s2200m550s2200m550s2200m550s2200m550s2200m550s2200m550s2200" + "m550s41650" + "m9000s2200m550s87850", irsend.outputStr()); + irsend.sendGICable(0x8807); + EXPECT_EQ( + "m9000s4400" + "m550s4400m550s2200m550s2200m550s2200m550s4400m550s2200m550s2200m550s2200" + "m550s2200m550s2200m550s2200m550s2200m550s2200m550s4400m550s4400m550s4400" + "m550s30650" + "m9000s2200m550s87850", irsend.outputStr()); + irsend.sendGICable(0xFFFF); + EXPECT_EQ( + "m9000s4400" + "m550s4400m550s4400m550s4400m550s4400m550s4400m550s4400m550s4400m550s4400" + "m550s4400m550s4400m550s4400m550s4400m550s4400m550s4400m550s4400m550s4400" + "m550s6450" + "m9000s2200m550s87850", irsend.outputStr()); +} + +// Test sending with repeats. +TEST(TestSendGICable, SendWithRepeats) { + IRsendTest irsend(0); + irsend.begin(); + // Send a command with 0 repeats. + irsend.sendGICable(0x8807, GICABLE_BITS, 0); + EXPECT_EQ( + "m9000s4400" + "m550s4400m550s2200m550s2200m550s2200m550s4400m550s2200m550s2200m550s2200" + "m550s2200m550s2200m550s2200m550s2200m550s2200m550s4400m550s4400m550s4400" + "m550s30650", irsend.outputStr()); + // Send a command with 1 repeat. + irsend.sendGICable(0x8807, GICABLE_BITS, 1); + EXPECT_EQ( + "m9000s4400" + "m550s4400m550s2200m550s2200m550s2200m550s4400m550s2200m550s2200m550s2200" + "m550s2200m550s2200m550s2200m550s2200m550s2200m550s4400m550s4400m550s4400" + "m550s30650" + "m9000s2200m550s87850", irsend.outputStr()); + // Send a command with 3 repeats. + irsend.sendGICable(0x8807, GICABLE_BITS, 3); + EXPECT_EQ( + "m9000s4400" + "m550s4400m550s2200m550s2200m550s2200m550s4400m550s2200m550s2200m550s2200" + "m550s2200m550s2200m550s2200m550s2200m550s2200m550s4400m550s4400m550s4400" + "m550s30650" + "m9000s2200m550s87850" + "m9000s2200m550s87850" + "m9000s2200m550s87850", irsend.outputStr()); +} + +// Tests for decodeGICable(). +// Decode normal GICable messages. +TEST(TestDecodeGICable, SyntheticDecode) { + IRsendTest irsend(0); + IRrecv irrecv(0); + irsend.begin(); + + // Synthesised Normal GICable message. + irsend.reset(); + irsend.sendGICable(0x8807); + irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(GICABLE, irsend.capture.decode_type); + EXPECT_EQ(GICABLE_BITS, irsend.capture.bits); + EXPECT_EQ(0x8807, irsend.capture.value); + EXPECT_EQ(0x0, irsend.capture.address); + EXPECT_EQ(0x0, irsend.capture.command); +} + +// Decode a recorded example +TEST(TestDecodeGICable, RealExampleDecodeOK) { + IRsendTest irsend(0); + IRrecv irrecv(0); + irsend.begin(); + + // Real GICable "OK/Select" message. + // Ref: https://github.com/markszabo/IRremoteESP8266/issues/447 + uint16_t rawData[39] = { + 9064, 4408, 580, 4408, 580, 2152, 578, 2150, 580, 2150, 580, 4408, 580, + 2150, 580, 2150, 580, 2150, 580, 2150, 580, 2150, 580, 2150, 580, 2150, + 580, 2150, 580, 4408, 580, 4408, 580, 4408, 580, 30622, 9066, 2148, 580}; + irsend.reset(); + irsend.sendRaw(rawData, 39, 39); + irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(GICABLE, irsend.capture.decode_type); + EXPECT_EQ(GICABLE_BITS, irsend.capture.bits); + EXPECT_EQ(0x8807, irsend.capture.value); + EXPECT_EQ(0x0, irsend.capture.address); + EXPECT_EQ(0x0, irsend.capture.command); +} + +TEST(TestDecodeGICable, RealExampleDecodeLEFT) { + IRsendTest irsend(0); + IRrecv irrecv(0); + irsend.begin(); + + // Real GICable "LEFT" message. + // Ref: https://github.com/markszabo/IRremoteESP8266/issues/447 + uint16_t rawData[39] = { + 9040, 4434, 554, 2176, 580, 4408, 554, 4434, 582, 2148, 554, 4434, 580, + 4408, 556, 2174, 580, 2150, 580, 2150, 582, 2148, 556, 2176, 580, 2150, + 580, 4408, 580, 4408, 580, 4408, 582, 2150, 580, 26078, 9066, 2148, 580}; + irsend.reset(); + irsend.sendRaw(rawData, 39, 39); + irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(GICABLE, irsend.capture.decode_type); + EXPECT_EQ(GICABLE_BITS, irsend.capture.bits); + EXPECT_EQ(0x6C0E, irsend.capture.value); + EXPECT_EQ(0x0, irsend.capture.address); + EXPECT_EQ(0x0, irsend.capture.command); +} + +TEST(TestDecodeGICable, RealExampleDecodeZEROKey) { + IRsendTest irsend(0); + IRrecv irrecv(0); + irsend.begin(); + + // Real GICable "Zero Key" message. + // Note: Zero key looks similar to a JVC message, hence this test. + // Ref: https://github.com/markszabo/IRremoteESP8266/issues/447 + uint16_t rawData[39] = { + 9036, 4434, 552, 2178, 552, 2178, 552, 2180, 550, 2178, 552, 2178, 550, + 2180, 552, 2178, 552, 2178, 550, 2180, 552, 2178, 526, 2204, 552, 2178, + 552, 2178, 526, 2204, 526, 2204, 526, 2204, 526, 41932, 9036, 2176, 552}; + irsend.reset(); + irsend.sendRaw(rawData, 39, 39); + irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(GICABLE, irsend.capture.decode_type); + EXPECT_EQ(GICABLE_BITS, irsend.capture.bits); + EXPECT_EQ(0x0, irsend.capture.value); + EXPECT_EQ(0x0, irsend.capture.address); + EXPECT_EQ(0x0, irsend.capture.command); +} diff --git a/lib/IRremoteESP8266/test/ir_GlobalCache_test.cpp b/lib/IRremoteESP8266/test/ir_GlobalCache_test.cpp index c2e9762..68b0062 100644 --- a/lib/IRremoteESP8266/test/ir_GlobalCache_test.cpp +++ b/lib/IRremoteESP8266/test/ir_GlobalCache_test.cpp @@ -22,18 +22,18 @@ TEST(TestSendGlobalCache, NonRepeatingCode) { 21, 22, 21, 65, 21, 1519}; irsend.sendGC(gc_test, 71); irsend.makeDecodeResult(); - EXPECT_EQ("m8892s4472m546s572m546s546m546s1690m546s546m546s572m546s572" - "m546s546m546s572m546s1690m546s1690m546s572m546s1690m546s1690" - "m546s1690m546s1690m546s1690m546s1690m546s572m546s572m546s546" - "m546s572m546s572m546s1690m546s572m546s546m546s1690m546s1690" - "m546s1690m546s1664m572s1690m546s572m546s1690m546s39494", - irsend.outputStr()); EXPECT_TRUE(irrecv.decodeNEC(&irsend.capture)); EXPECT_EQ(NEC, irsend.capture.decode_type); EXPECT_EQ(NEC_BITS, irsend.capture.bits); EXPECT_EQ(0x20DF827D, irsend.capture.value); EXPECT_EQ(0x4, irsend.capture.address); EXPECT_EQ(0x41, irsend.capture.command); + EXPECT_EQ("m8892s4472m546s572m546s546m546s1690m546s546m546s572m546s572" + "m546s546m546s572m546s1690m546s1690m546s572m546s1690m546s1690" + "m546s1690m546s1690m546s1690m546s1690m546s572m546s572m546s546" + "m546s572m546s572m546s1690m546s572m546s546m546s1690m546s1690" + "m546s1690m546s1664m572s1690m546s572m546s1690m546s39494", + irsend.outputStr()); } // Test sending typical command with repeats. @@ -52,6 +52,12 @@ TEST(TestSendGlobalCache, RepeatCode) { 64, 21, 64, 21, 64, 21, 1600, 341, 85, 21, 3647}; irsend.sendGC(gc_test, 75); irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decodeNEC(&irsend.capture)); + EXPECT_EQ(NEC, irsend.capture.decode_type); + EXPECT_EQ(NEC_BITS, irsend.capture.bits); + EXPECT_EQ(0xC1A28877, irsend.capture.value); + EXPECT_EQ(0x4583, irsend.capture.address); + EXPECT_EQ(0x11, irsend.capture.command); EXPECT_EQ("m8866s4446m546s1664m546s1664m546s546m546s546m546s546m546s546" "m546s546m546s1664m546s1664m546s546m546s1664m546s546m546s546" "m546s546m546s1664m546s546m546s1664m546s546m546s546m546s546" @@ -59,10 +65,4 @@ TEST(TestSendGlobalCache, RepeatCode) { "m546s1664m546s546m546s1664m546s1664m546s1664m546s41600" "m8866s2210m546s94822" "m8866s2210m546s94822", irsend.outputStr()); - EXPECT_TRUE(irrecv.decodeNEC(&irsend.capture)); - EXPECT_EQ(NEC, irsend.capture.decode_type); - EXPECT_EQ(NEC_BITS, irsend.capture.bits); - EXPECT_EQ(0xC1A28877, irsend.capture.value); - EXPECT_EQ(0x4583, irsend.capture.address); - EXPECT_EQ(0x11, irsend.capture.command); } diff --git a/lib/IRremoteESP8266/test/ir_Gree_test.cpp b/lib/IRremoteESP8266/test/ir_Gree_test.cpp index 80a3dec..811c9b7 100644 --- a/lib/IRremoteESP8266/test/ir_Gree_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Gree_test.cpp @@ -1,7 +1,11 @@ // Copyright 2017 David Conran +#include "IRremoteESP8266.h" +#include "IRrecv.h" +#include "IRrecv_test.h" #include "IRsend.h" #include "IRsend_test.h" +#include "ir_Gree.h" #include "gtest/gtest.h" // Tests for sendGree(). @@ -190,3 +194,323 @@ TEST(TestSendGree, CompareUint64ToCharResults) { irsend_uint64.sendGree((uint64_t) 0x0); ASSERT_EQ(irsend_chars.outputStr(), irsend_uint64.outputStr()); } + +// Tests for IRGreeAC class. + +TEST(TestGreeClass, Power) { + IRGreeAC irgree(0); + irgree.begin(); + + irgree.on(); + EXPECT_TRUE(irgree.getPower()); + + irgree.off(); + EXPECT_FALSE(irgree.getPower()); + + irgree.setPower(true); + EXPECT_TRUE(irgree.getPower()); + + irgree.setPower(false); + EXPECT_FALSE(irgree.getPower()); +} + +TEST(TestGreeClass, Temperature) { + IRGreeAC irgree(0); + irgree.begin(); + + irgree.setMode(GREE_COOL); + + irgree.setTemp(0); + EXPECT_EQ(GREE_MIN_TEMP, irgree.getTemp()); + + irgree.setTemp(255); + EXPECT_EQ(GREE_MAX_TEMP, irgree.getTemp()); + + irgree.setTemp(GREE_MIN_TEMP); + EXPECT_EQ(GREE_MIN_TEMP, irgree.getTemp()); + + irgree.setTemp(GREE_MAX_TEMP); + EXPECT_EQ(GREE_MAX_TEMP, irgree.getTemp()); + + irgree.setTemp(GREE_MIN_TEMP - 1); + EXPECT_EQ(GREE_MIN_TEMP, irgree.getTemp()); + + irgree.setTemp(GREE_MAX_TEMP + 1); + EXPECT_EQ(GREE_MAX_TEMP, irgree.getTemp()); + + irgree.setTemp(17); + EXPECT_EQ(17, irgree.getTemp()); + + irgree.setTemp(21); + EXPECT_EQ(21, irgree.getTemp()); + + irgree.setTemp(25); + EXPECT_EQ(25, irgree.getTemp()); + + irgree.setTemp(29); + EXPECT_EQ(29, irgree.getTemp()); +} + +TEST(TestGreeClass, OperatingMode) { + IRGreeAC irgree(0); + irgree.begin(); + + irgree.setTemp(17); + irgree.setMode(GREE_AUTO); // Auto should lock the temp to 25C. + EXPECT_EQ(GREE_AUTO, irgree.getMode()); + EXPECT_EQ(25, irgree.getTemp()); + irgree.setTemp(17); + EXPECT_EQ(25, irgree.getTemp()); + + irgree.setMode(GREE_COOL); + EXPECT_EQ(GREE_COOL, irgree.getMode()); + + irgree.setMode(GREE_HEAT); + EXPECT_EQ(GREE_HEAT, irgree.getMode()); + + ASSERT_NE(GREE_FAN_MAX, 1); + irgree.setFan(GREE_FAN_MAX); + irgree.setMode(GREE_DRY); // Dry should lock the fan to speed 1. + EXPECT_EQ(GREE_DRY, irgree.getMode()); + EXPECT_EQ(1, irgree.getFan()); + irgree.setFan(GREE_FAN_MAX); + EXPECT_EQ(1, irgree.getFan()); + + irgree.setMode(GREE_FAN); + EXPECT_EQ(GREE_FAN, irgree.getMode()); + + irgree.setMode(GREE_HEAT + 1); + EXPECT_EQ(GREE_AUTO, irgree.getMode()); + + irgree.setMode(255); + EXPECT_EQ(GREE_AUTO, irgree.getMode()); +} + +TEST(TestGreeClass, Light) { + IRGreeAC irgree(0); + irgree.begin(); + + irgree.setLight(true); + EXPECT_TRUE(irgree.getLight()); + + irgree.setLight(false); + EXPECT_FALSE(irgree.getLight()); + + irgree.setLight(true); + EXPECT_TRUE(irgree.getLight()); +} + +TEST(TestGreeClass, XFan) { + IRGreeAC irgree(0); + irgree.begin(); + + irgree.setXFan(true); + EXPECT_TRUE(irgree.getXFan()); + + irgree.setXFan(false); + EXPECT_FALSE(irgree.getXFan()); + + irgree.setXFan(true); + EXPECT_TRUE(irgree.getXFan()); +} + +TEST(TestGreeClass, Turbo) { + IRGreeAC irgree(0); + irgree.begin(); + + irgree.setTurbo(true); + EXPECT_TRUE(irgree.getTurbo()); + + irgree.setTurbo(false); + EXPECT_FALSE(irgree.getTurbo()); + + irgree.setTurbo(true); + EXPECT_TRUE(irgree.getTurbo()); +} + +TEST(TestGreeClass, Sleep) { + IRGreeAC irgree(0); + irgree.begin(); + + irgree.setSleep(true); + EXPECT_TRUE(irgree.getSleep()); + + irgree.setSleep(false); + EXPECT_FALSE(irgree.getSleep()); + + irgree.setSleep(true); + EXPECT_TRUE(irgree.getSleep()); +} + +TEST(TestGreeClass, FanSpeed) { + IRGreeAC irgree(0); + irgree.begin(); + + irgree.setFan(0); + EXPECT_EQ(0, irgree.getFan()); + + irgree.setFan(255); + EXPECT_EQ(GREE_FAN_MAX, irgree.getFan()); + + irgree.setFan(GREE_FAN_MAX); + EXPECT_EQ(GREE_FAN_MAX, irgree.getFan()); + + irgree.setFan(GREE_FAN_MAX + 1); + EXPECT_EQ(GREE_FAN_MAX, irgree.getFan()); + + irgree.setFan(GREE_FAN_MAX - 1); + EXPECT_EQ(GREE_FAN_MAX - 1, irgree.getFan()); + + irgree.setFan(1); + EXPECT_EQ(1, irgree.getFan()); + + irgree.setFan(1); + EXPECT_EQ(1, irgree.getFan()); + + irgree.setFan(3); + EXPECT_EQ(3, irgree.getFan()); +} + +TEST(TestGreeClass, VerticalSwing) { + IRGreeAC irgree(0); + irgree.begin(); + EXPECT_FALSE(irgree.getSwingVerticalAuto()); + EXPECT_EQ(GREE_SWING_LAST_POS, irgree.getSwingVerticalPosition()); + + irgree.setSwingVertical(true, GREE_SWING_AUTO); + EXPECT_TRUE(irgree.getSwingVerticalAuto()); + EXPECT_EQ(GREE_SWING_AUTO, irgree.getSwingVerticalPosition()); + + irgree.setSwingVertical(false, GREE_SWING_MIDDLE); + EXPECT_FALSE(irgree.getSwingVerticalAuto()); + EXPECT_EQ(GREE_SWING_MIDDLE, irgree.getSwingVerticalPosition()); + + irgree.setSwingVertical(true, GREE_SWING_DOWN_AUTO); + EXPECT_TRUE(irgree.getSwingVerticalAuto()); + EXPECT_EQ(GREE_SWING_DOWN_AUTO, irgree.getSwingVerticalPosition()); + + // Out of bounds. + irgree.setSwingVertical(false, 255); + EXPECT_FALSE(irgree.getSwingVerticalAuto()); + EXPECT_EQ(GREE_SWING_LAST_POS, irgree.getSwingVerticalPosition()); + irgree.setSwingVertical(false, GREE_SWING_AUTO); + EXPECT_FALSE(irgree.getSwingVerticalAuto()); + EXPECT_EQ(GREE_SWING_LAST_POS, irgree.getSwingVerticalPosition()); + + irgree.setSwingVertical(true, 255); + EXPECT_TRUE(irgree.getSwingVerticalAuto()); + EXPECT_EQ(GREE_SWING_AUTO, irgree.getSwingVerticalPosition()); + irgree.setSwingVertical(true, GREE_SWING_DOWN); + EXPECT_TRUE(irgree.getSwingVerticalAuto()); + EXPECT_EQ(GREE_SWING_AUTO, irgree.getSwingVerticalPosition()); +} + +TEST(TestGreeClass, SetAndGetRaw) { + IRGreeAC irgree(0); + uint8_t initialState[GREE_STATE_LENGTH] = { + 0x00, 0x09, 0x20, 0x50, 0x00, 0x20, 0x00, 0x50}; + uint8_t expectedState[GREE_STATE_LENGTH] = { + 0xA9, 0x05, 0xD0, 0x50, 0x00, 0x20, 0x00, 0xA0}; + + EXPECT_STATE_EQ(initialState, irgree.getRaw(), GREE_BITS); + + // toggle the power state. + irgree.setPower(!irgree.getPower()); + irgree.setMode(GREE_COOL); + irgree.setTemp(21); + irgree.setFan(2); + irgree.setLight(false); + irgree.setTurbo(true); + irgree.setSleep(true); + irgree.setXFan(true); + + EXPECT_EQ(GREE_COOL, irgree.getMode()); + EXPECT_EQ(21, irgree.getTemp()); + EXPECT_EQ(2, irgree.getFan()); + EXPECT_FALSE(irgree.getLight()); + EXPECT_TRUE(irgree.getTurbo()); + EXPECT_TRUE(irgree.getSleep()); + EXPECT_TRUE(irgree.getXFan()); + + EXPECT_STATE_EQ(expectedState, irgree.getRaw(), GREE_BITS); + irgree.setRaw(initialState); + EXPECT_STATE_EQ(initialState, irgree.getRaw(), GREE_BITS); +} + +TEST(TestGreeClass, HumanReadable) { + IRGreeAC irgree(0); + + EXPECT_EQ("Power: Off, Mode: 0 (AUTO), Temp: 25C, Fan: 0 (AUTO), Turbo: Off, " + "XFan: Off, Light: On, Sleep: Off, Swing Vertical Mode: Manual, " + "Swing Vertical Pos: 0 (Last Pos)", + irgree.toString()); + irgree.on(); + irgree.setMode(GREE_COOL); + irgree.setTemp(GREE_MIN_TEMP); + irgree.setFan(GREE_FAN_MAX); + irgree.setXFan(true); + irgree.setSleep(true); + irgree.setLight(false); + irgree.setTurbo(true); + irgree.setSwingVertical(true, GREE_SWING_AUTO); + EXPECT_EQ("Power: On, Mode: 1 (COOL), Temp: 16C, Fan: 3 (MAX), Turbo: On, " + "XFan: On, Light: Off, Sleep: On, Swing Vertical Mode: Auto, " + "Swing Vertical Pos: 1 (Auto)", + irgree.toString()); +} + +// Tests for decodeGree(). + +// Decode a synthetic Gree message. +TEST(TestDecodeGree, NormalSynthetic) { + IRsendTest irsend(4); + IRrecv irrecv(4); + irsend.begin(); + + uint8_t gree_code[GREE_STATE_LENGTH] = { + 0x00, 0x09, 0x20, 0x50, 0x00, 0x20, 0x00, 0x50}; + + irsend.reset(); + irsend.sendGree(gree_code); + irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(GREE, irsend.capture.decode_type); + ASSERT_EQ(GREE_BITS, irsend.capture.bits); + EXPECT_STATE_EQ(gree_code, irsend.capture.state, GREE_BITS); +} + +// Decode a synthetic Gree message. +TEST(TestDecodeGree, NormalRealExample) { + IRsendTest irsend(4); + IRrecv irrecv(4); + IRGreeAC irgree(4); + irsend.begin(); + + uint8_t gree_code[GREE_STATE_LENGTH] = { + 0x19, 0x0A, 0x60, 0x50, 0x02, 0x23, 0x00, 0xF0}; + + // Ref: https://github.com/markszabo/IRremoteESP8266/issues/386 + uint16_t rawData[139] = { + 9008, 4496, 644, 1660, 676, 530, 648, 558, 672, 1636, 646, 1660, 644, 556, + 650, 584, 626, 560, 644, 580, 628, 1680, 624, 560, 648, 1662, 644, 582, + 648, 536, 674, 530, 646, 580, 628, 560, 670, 532, 646, 562, 644, 556, 672, + 536, 648, 1662, 646, 1660, 652, 554, 644, 558, 672, 538, 644, 560, 668, + 560, 648, 1638, 668, 536, 644, 1660, 668, 532, 648, 560, 648, 1660, 674, + 554, 622, 19990, 646, 580, 624, 1660, 648, 556, 648, 558, 674, 556, 622, + 560, 644, 564, 668, 536, 646, 1662, 646, 1658, 672, 534, 648, 558, 644, + 562, 648, 1662, 644, 584, 622, 558, 648, 562, 668, 534, 670, 536, 670, + 532, 672, 536, 646, 560, 646, 558, 648, 558, 670, 534, 650, 558, 646, 560, + 646, 560, 668, 1638, 646, 1662, 646, 1660, 646, 1660, 648}; // Issue #386 + + irsend.reset(); + irsend.sendRaw(rawData, 139, 38000); + irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(GREE, irsend.capture.decode_type); + ASSERT_EQ(GREE_BITS, irsend.capture.bits); + EXPECT_STATE_EQ(gree_code, irsend.capture.state, GREE_BITS); + irgree.setRaw(irsend.capture.state); + EXPECT_EQ("Power: On, Mode: 1 (COOL), Temp: 26C, Fan: 1, Turbo: Off, " + "XFan: Off, Light: On, Sleep: Off, Swing Vertical Mode: Manual, " + "Swing Vertical Pos: 2", irgree.toString()); +} diff --git a/lib/IRremoteESP8266/test/ir_Haier_test.cpp b/lib/IRremoteESP8266/test/ir_Haier_test.cpp new file mode 100644 index 0000000..4fec483 --- /dev/null +++ b/lib/IRremoteESP8266/test/ir_Haier_test.cpp @@ -0,0 +1,560 @@ +// Copyright 2018 David Conran + +#include "IRrecv.h" +#include "IRrecv_test.h" +#include "IRsend.h" +#include "IRsend_test.h" +#include "ir_Haier.h" +#include "gtest/gtest.h" + +// Tests for sendHaierAC() + +// Test sending typical data only. +TEST(TestSendHaierAC, SendDataOnly) { + IRsendTest irsend(0); + irsend.begin(); + uint8_t haier_zero[HAIER_AC_STATE_LENGTH] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + + irsend.reset(); + irsend.sendHaierAC(haier_zero); + EXPECT_EQ( + "m3000s3000m3000s4300" + "m520s650m520s650m520s650m520s650m520s650m520s650m520s650m520s650" + "m520s650m520s650m520s650m520s650m520s650m520s650m520s650m520s650" + "m520s650m520s650m520s650m520s650m520s650m520s650m520s650m520s650" + "m520s650m520s650m520s650m520s650m520s650m520s650m520s650m520s650" + "m520s650m520s650m520s650m520s650m520s650m520s650m520s650m520s650" + "m520s650m520s650m520s650m520s650m520s650m520s650m520s650m520s650" + "m520s650m520s650m520s650m520s650m520s650m520s650m520s650m520s650" + "m520s650m520s650m520s650m520s650m520s650m520s650m520s650m520s650" + "m520s650m520s650m520s650m520s650m520s650m520s650m520s650m520s650" + "m520s150000", irsend.outputStr()); + + uint8_t haier_test[HAIER_AC_STATE_LENGTH] = { + 0xA5, 0x01, 0x20, 0x01, 0x00, 0xC0, 0x20, 0x00, 0xA7}; + irsend.reset(); + irsend.sendHaierAC(haier_test); + EXPECT_EQ( + "m3000s3000m3000s4300" + "m520s1650m520s650m520s1650m520s650m520s650m520s1650m520s650m520s1650" + "m520s650m520s650m520s650m520s650m520s650m520s650m520s650m520s1650" + "m520s650m520s650m520s1650m520s650m520s650m520s650m520s650m520s650" + "m520s650m520s650m520s650m520s650m520s650m520s650m520s650m520s1650" + "m520s650m520s650m520s650m520s650m520s650m520s650m520s650m520s650" + "m520s1650m520s1650m520s650m520s650m520s650m520s650m520s650m520s650" + "m520s650m520s650m520s1650m520s650m520s650m520s650m520s650m520s650" + "m520s650m520s650m520s650m520s650m520s650m520s650m520s650m520s650" + "m520s1650m520s650m520s1650m520s650m520s650m520s1650m520s1650m520s1650" + "m520s150000", irsend.outputStr()); +} + +// Test sending typical data with repeats. +TEST(TestSendHaierAC, SendWithRepeats) { + IRsendTest irsend(0); + irsend.begin(); + + irsend.reset(); + uint8_t haier_test[HAIER_AC_STATE_LENGTH] = { + 0xA5, 0x01, 0x20, 0x01, 0x00, 0xC0, 0x20, 0x00, 0xA7}; + irsend.reset(); + irsend.sendHaierAC(haier_test, HAIER_AC_STATE_LENGTH, 2); // two repeats. + EXPECT_EQ( + "m3000s3000m3000s4300" + "m520s1650m520s650m520s1650m520s650m520s650m520s1650m520s650m520s1650" + "m520s650m520s650m520s650m520s650m520s650m520s650m520s650m520s1650" + "m520s650m520s650m520s1650m520s650m520s650m520s650m520s650m520s650" + "m520s650m520s650m520s650m520s650m520s650m520s650m520s650m520s1650" + "m520s650m520s650m520s650m520s650m520s650m520s650m520s650m520s650" + "m520s1650m520s1650m520s650m520s650m520s650m520s650m520s650m520s650" + "m520s650m520s650m520s1650m520s650m520s650m520s650m520s650m520s650" + "m520s650m520s650m520s650m520s650m520s650m520s650m520s650m520s650" + "m520s1650m520s650m520s1650m520s650m520s650m520s1650m520s1650m520s1650" + "m520s150000" + "m3000s3000m3000s4300" + "m520s1650m520s650m520s1650m520s650m520s650m520s1650m520s650m520s1650" + "m520s650m520s650m520s650m520s650m520s650m520s650m520s650m520s1650" + "m520s650m520s650m520s1650m520s650m520s650m520s650m520s650m520s650" + "m520s650m520s650m520s650m520s650m520s650m520s650m520s650m520s1650" + "m520s650m520s650m520s650m520s650m520s650m520s650m520s650m520s650" + "m520s1650m520s1650m520s650m520s650m520s650m520s650m520s650m520s650" + "m520s650m520s650m520s1650m520s650m520s650m520s650m520s650m520s650" + "m520s650m520s650m520s650m520s650m520s650m520s650m520s650m520s650" + "m520s1650m520s650m520s1650m520s650m520s650m520s1650m520s1650m520s1650" + "m520s150000" + "m3000s3000m3000s4300" + "m520s1650m520s650m520s1650m520s650m520s650m520s1650m520s650m520s1650" + "m520s650m520s650m520s650m520s650m520s650m520s650m520s650m520s1650" + "m520s650m520s650m520s1650m520s650m520s650m520s650m520s650m520s650" + "m520s650m520s650m520s650m520s650m520s650m520s650m520s650m520s1650" + "m520s650m520s650m520s650m520s650m520s650m520s650m520s650m520s650" + "m520s1650m520s1650m520s650m520s650m520s650m520s650m520s650m520s650" + "m520s650m520s650m520s1650m520s650m520s650m520s650m520s650m520s650" + "m520s650m520s650m520s650m520s650m520s650m520s650m520s650m520s650" + "m520s1650m520s650m520s1650m520s650m520s650m520s1650m520s1650m520s1650" + "m520s150000", irsend.outputStr()); +} + +// Tests for IRHaierAC class. + +TEST(TestHaierACClass, Command) { + IRHaierAC haier(0); + haier.begin(); + + haier.setCommand(HAIER_AC_CMD_OFF); + EXPECT_EQ(HAIER_AC_CMD_OFF, haier.getCommand()); + haier.setCommand(HAIER_AC_CMD_ON); + EXPECT_EQ(HAIER_AC_CMD_ON, haier.getCommand()); + haier.setCommand(HAIER_AC_CMD_MODE); + EXPECT_EQ(HAIER_AC_CMD_MODE, haier.getCommand()); + haier.setCommand(HAIER_AC_CMD_FAN); + EXPECT_EQ(HAIER_AC_CMD_FAN, haier.getCommand()); + haier.setCommand(HAIER_AC_CMD_TEMP_UP); + EXPECT_EQ(HAIER_AC_CMD_TEMP_UP, haier.getCommand()); + haier.setCommand(HAIER_AC_CMD_TEMP_DOWN); + EXPECT_EQ(HAIER_AC_CMD_TEMP_DOWN, haier.getCommand()); + haier.setCommand(HAIER_AC_CMD_SLEEP); + EXPECT_EQ(HAIER_AC_CMD_SLEEP, haier.getCommand()); + haier.setCommand(HAIER_AC_CMD_TIMER_SET); + EXPECT_EQ(HAIER_AC_CMD_TIMER_SET, haier.getCommand()); + haier.setCommand(HAIER_AC_CMD_TIMER_CANCEL); + EXPECT_EQ(HAIER_AC_CMD_TIMER_CANCEL, haier.getCommand()); + haier.setCommand(HAIER_AC_CMD_HEALTH); + EXPECT_EQ(HAIER_AC_CMD_HEALTH, haier.getCommand()); + haier.setCommand(HAIER_AC_CMD_SWING); + EXPECT_EQ(HAIER_AC_CMD_SWING, haier.getCommand()); + haier.setCommand(HAIER_AC_CMD_ON); + EXPECT_EQ(HAIER_AC_CMD_ON, haier.getCommand()); + + // Test unexpected values. + haier.setCommand(0b00001110); + EXPECT_EQ(HAIER_AC_CMD_OFF, haier.getCommand()); + haier.setCommand(0b00001111); + EXPECT_EQ(HAIER_AC_CMD_OFF, haier.getCommand()); + haier.setCommand(0b00000100); + EXPECT_EQ(HAIER_AC_CMD_OFF, haier.getCommand()); +} + +TEST(TestHaierACClass, OperatingMode) { + IRHaierAC haier(0); + haier.begin(); + + haier.setMode(HAIER_AC_AUTO); + EXPECT_EQ(HAIER_AC_AUTO, haier.getMode()); + + haier.setMode(HAIER_AC_COOL); + EXPECT_EQ(HAIER_AC_COOL, haier.getMode()); + + haier.setMode(HAIER_AC_HEAT); + EXPECT_EQ(HAIER_AC_HEAT, haier.getMode()); + + haier.setMode(HAIER_AC_FAN); + EXPECT_EQ(HAIER_AC_FAN, haier.getMode()); + + haier.setMode(HAIER_AC_DRY); + EXPECT_EQ(HAIER_AC_DRY, haier.getMode()); + + haier.setMode(HAIER_AC_AUTO - 1); + EXPECT_EQ(HAIER_AC_AUTO, haier.getMode()); + + haier.setMode(HAIER_AC_COOL); + EXPECT_EQ(HAIER_AC_COOL, haier.getMode()); + + haier.setMode(HAIER_AC_FAN + 1); + EXPECT_EQ(HAIER_AC_AUTO, haier.getMode()); + + haier.setMode(255); + EXPECT_EQ(HAIER_AC_AUTO, haier.getMode()); +} + +TEST(TestHaierACClass, Temperature) { + IRHaierAC haier(0); + haier.begin(); + + haier.setTemp(HAIER_AC_MIN_TEMP); + EXPECT_EQ(HAIER_AC_MIN_TEMP, haier.getTemp()); + + haier.setCommand(HAIER_AC_CMD_ON); + haier.setTemp(HAIER_AC_MIN_TEMP + 1); + EXPECT_EQ(HAIER_AC_MIN_TEMP + 1, haier.getTemp()); + EXPECT_EQ(HAIER_AC_CMD_TEMP_UP, haier.getCommand()); + + haier.setTemp(HAIER_AC_MAX_TEMP); + EXPECT_EQ(HAIER_AC_MAX_TEMP, haier.getTemp()); + EXPECT_EQ(HAIER_AC_CMD_TEMP_UP, haier.getCommand()); + + haier.setTemp(HAIER_AC_MIN_TEMP - 1); + EXPECT_EQ(HAIER_AC_MIN_TEMP, haier.getTemp()); + EXPECT_EQ(HAIER_AC_CMD_TEMP_DOWN, haier.getCommand()); + + haier.setTemp(HAIER_AC_MAX_TEMP + 1); + EXPECT_EQ(HAIER_AC_MAX_TEMP, haier.getTemp()); + EXPECT_EQ(HAIER_AC_CMD_TEMP_UP, haier.getCommand()); + + haier.setTemp(23); + EXPECT_EQ(23, haier.getTemp()); + EXPECT_EQ(HAIER_AC_CMD_TEMP_DOWN, haier.getCommand()); + haier.setCommand(HAIER_AC_CMD_ON); + haier.setTemp(23); + EXPECT_EQ(23, haier.getTemp()); + EXPECT_EQ(HAIER_AC_CMD_ON, haier.getCommand()); + + haier.setTemp(0); + EXPECT_EQ(HAIER_AC_MIN_TEMP, haier.getTemp()); + EXPECT_EQ(HAIER_AC_CMD_TEMP_DOWN, haier.getCommand()); + + haier.setTemp(255); + EXPECT_EQ(HAIER_AC_MAX_TEMP, haier.getTemp()); + EXPECT_EQ(HAIER_AC_CMD_TEMP_UP, haier.getCommand()); +} + +TEST(TestHaierACClass, FanSpeed) { + IRHaierAC haier(0); + haier.begin(); + haier.setFan(HAIER_AC_FAN_LOW); + haier.setCommand(HAIER_AC_CMD_ON); + + haier.setFan(HAIER_AC_FAN_AUTO); + EXPECT_EQ(HAIER_AC_FAN_AUTO, haier.getFan()); + EXPECT_EQ(HAIER_AC_CMD_FAN, haier.getCommand()); + + haier.setFan(HAIER_AC_FAN_LOW); + EXPECT_EQ(HAIER_AC_FAN_LOW, haier.getFan()); + haier.setFan(HAIER_AC_FAN_MED); + EXPECT_EQ(HAIER_AC_FAN_MED, haier.getFan()); + haier.setFan(HAIER_AC_FAN_HIGH); + EXPECT_EQ(HAIER_AC_FAN_HIGH, haier.getFan()); + + haier.setCommand(HAIER_AC_CMD_ON); + haier.setFan(HAIER_AC_FAN_HIGH); + EXPECT_EQ(HAIER_AC_FAN_HIGH, haier.getFan()); + EXPECT_EQ(HAIER_AC_CMD_ON, haier.getCommand()); +} + +TEST(TestHaierACClass, Swing) { + IRHaierAC haier(0); + haier.begin(); + haier.setFan(HAIER_AC_FAN_LOW); + haier.setCommand(HAIER_AC_CMD_ON); + + haier.setSwing(HAIER_AC_SWING_OFF); + EXPECT_EQ(HAIER_AC_SWING_OFF, haier.getSwing()); + + haier.setSwing(HAIER_AC_SWING_UP); + EXPECT_EQ(HAIER_AC_SWING_UP, haier.getSwing()); + EXPECT_EQ(HAIER_AC_CMD_SWING, haier.getCommand()); + + haier.setSwing(HAIER_AC_SWING_DOWN); + EXPECT_EQ(HAIER_AC_SWING_DOWN, haier.getSwing()); + EXPECT_EQ(HAIER_AC_CMD_SWING, haier.getCommand()); + + haier.setSwing(HAIER_AC_SWING_CHG); + EXPECT_EQ(HAIER_AC_SWING_CHG, haier.getSwing()); + EXPECT_EQ(HAIER_AC_CMD_SWING, haier.getCommand()); +} + +TEST(TestHaierACClass, CurrentTime) { + IRHaierAC haier(0); + haier.begin(); + EXPECT_EQ(0, haier.getCurrTime()); + + haier.setCurrTime(1); + EXPECT_EQ(1, haier.getCurrTime()); + + haier.setCurrTime(60); + EXPECT_EQ(60, haier.getCurrTime()); + + haier.setCurrTime(61); + EXPECT_EQ(61, haier.getCurrTime()); + + haier.setCurrTime(18 * 60 + 34); // 18:34 + EXPECT_EQ(1114, haier.getCurrTime()); + + haier.setCurrTime(23 * 60 + 59); // 23:59 + EXPECT_EQ(HAIER_AC_MAX_TIME, haier.getCurrTime()); // 23:59 + + haier.setCurrTime(23 * 60 + 59 + 1); // 24:00 + EXPECT_EQ(HAIER_AC_MAX_TIME, haier.getCurrTime()); // 23:59 + + haier.setCurrTime(UINT16_MAX); + EXPECT_EQ(HAIER_AC_MAX_TIME, haier.getCurrTime()); // 23:59 +} + +TEST(TestHaierACClass, Timers) { + IRHaierAC haier(0); + haier.begin(); + + haier.setCommand(HAIER_AC_CMD_ON); + + // Off by default. + EXPECT_GT(0, haier.getOnTimer()); + EXPECT_GT(0, haier.getOffTimer()); + EXPECT_EQ(HAIER_AC_CMD_ON, haier.getCommand()); + + // On Timer. + haier.setOnTimer(6 * 60); // 6am + EXPECT_EQ(6 * 60, haier.getOnTimer()); // 6am + EXPECT_GT(0, haier.getOffTimer()); + EXPECT_EQ(HAIER_AC_CMD_TIMER_SET, haier.getCommand()); + + haier.setCommand(HAIER_AC_CMD_ON); + EXPECT_EQ(6 * 60, haier.getOnTimer()); // 6am + EXPECT_GT(0, haier.getOffTimer()); + EXPECT_EQ(HAIER_AC_CMD_ON, haier.getCommand()); + + haier.cancelTimers(); + EXPECT_GT(0, haier.getOnTimer()); + EXPECT_GT(0, haier.getOffTimer()); + EXPECT_EQ(HAIER_AC_CMD_TIMER_CANCEL, haier.getCommand()); + + // Off Timer. + haier.setOffTimer(18 * 60 + 30); // 6:30pm + EXPECT_GT(0, haier.getOnTimer()); + EXPECT_EQ(18 * 60 + 30, haier.getOffTimer()); // 6:30pm + EXPECT_EQ(HAIER_AC_CMD_TIMER_SET, haier.getCommand()); + + haier.setCommand(HAIER_AC_CMD_ON); + EXPECT_GT(0, haier.getOnTimer()); + EXPECT_EQ(18 * 60 + 30, haier.getOffTimer()); // 6:30pm + EXPECT_EQ(HAIER_AC_CMD_ON, haier.getCommand()); + + haier.cancelTimers(); + EXPECT_GT(0, haier.getOnTimer()); + EXPECT_GT(0, haier.getOffTimer()); + EXPECT_EQ(HAIER_AC_CMD_TIMER_CANCEL, haier.getCommand()); + + // Both Timers. + haier.setOnTimer(6 * 60); // 6am + EXPECT_EQ(HAIER_AC_CMD_TIMER_SET, haier.getCommand()); + haier.setOffTimer(18 * 60 + 30); // 6:30pm + EXPECT_EQ(HAIER_AC_CMD_TIMER_SET, haier.getCommand()); + EXPECT_EQ(6 * 60, haier.getOnTimer()); // 6am + EXPECT_EQ(18 * 60 + 30, haier.getOffTimer()); // 6:30pm + + haier.cancelTimers(); + EXPECT_GT(0, haier.getOnTimer()); + EXPECT_GT(0, haier.getOffTimer()); + EXPECT_EQ(HAIER_AC_CMD_TIMER_CANCEL, haier.getCommand()); +} + +TEST(TestHaierACClass, TimeToString) { + EXPECT_EQ("00:00", IRHaierAC::timeToString(0)); + EXPECT_EQ("00:01", IRHaierAC::timeToString(1)); + EXPECT_EQ("00:10", IRHaierAC::timeToString(10)); + EXPECT_EQ("00:59", IRHaierAC::timeToString(59)); + + EXPECT_EQ("01:00", IRHaierAC::timeToString(60)); + EXPECT_EQ("01:01", IRHaierAC::timeToString(61)); + EXPECT_EQ("01:59", IRHaierAC::timeToString(60 + 59)); + EXPECT_EQ("18:59", IRHaierAC::timeToString(18 * 60 + 59)); + EXPECT_EQ("23:59", IRHaierAC::timeToString(23 * 60 + 59)); +} + +TEST(TestHaierACClass, MessageConstuction) { + IRHaierAC haier(0); + + EXPECT_EQ("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", + haier.toString()); + haier.setMode(HAIER_AC_COOL); + haier.setTemp(21); + haier.setFan(HAIER_AC_FAN_HIGH); + EXPECT_EQ("Command: 3 (Fan), Mode: 1 (COOL), Temp: 21C, Fan: 3 (MAX), " + "Swing: 0 (Off), Sleep: Off, Health: Off, " + "Current Time: 00:00, On Timer: Off, Off Timer: Off", + haier.toString()); + haier.setSwing(HAIER_AC_SWING_CHG); + haier.setHealth(true); + haier.setSleep(true); + haier.setCurrTime(615); // 10:15am + EXPECT_EQ("Command: 8 (Sleep), Mode: 3 (HEAT), Temp: 21C, Fan: 3 (MAX), " + "Swing: 3 (Chg), Sleep: On, Health: On, " + "Current Time: 10:15, On Timer: Off, Off Timer: Off", + haier.toString()); + haier.setOnTimer(800); // 1:20pm + haier.setOffTimer(1125); // 6:45pm + haier.setCommand(HAIER_AC_CMD_ON); + + EXPECT_EQ("Command: 1 (On), Mode: 2 (DRY), Temp: 21C, Fan: 2, " + "Swing: 3 (Chg), Sleep: On, Health: On, " + "Current Time: 10:15, On Timer: 13:20, Off Timer: 18:45", + haier.toString()); + + // Now change a few already set things. + haier.setMode(HAIER_AC_HEAT); + EXPECT_EQ("Command: 2 (Mode), Mode: 3 (HEAT), Temp: 21C, Fan: 2, " + "Swing: 3 (Chg), Sleep: On, Health: On, " + "Current Time: 10:15, On Timer: 13:52, Off Timer: 18:45", + haier.toString()); + + haier.setTemp(25); + EXPECT_EQ("Command: 6 (Temp Up), Mode: 3 (HEAT), Temp: 25C, Fan: 2, " + "Swing: 3 (Chg), Sleep: On, Health: On, " + "Current Time: 10:15, On Timer: 13:52, Off Timer: 18:45", + haier.toString()); + + uint8_t expectedState[HAIER_AC_STATE_LENGTH] = { + 0xA5, 0x96, 0xEA, 0xCF, 0x32, 0x2D, 0x0D, 0x74, 0xD4}; + EXPECT_STATE_EQ(expectedState, haier.getRaw(), HAIER_AC_BITS); + + // Check that the checksum is valid. + EXPECT_TRUE(IRHaierAC::validChecksum(haier.getRaw())); + + // Now load up some random data. + uint8_t randomState[HAIER_AC_STATE_LENGTH] = { + 0x52, 0x49, 0x50, 0x20, 0x54, 0x61, 0x6C, 0x69, 0x61}; + EXPECT_FALSE(IRHaierAC::validChecksum(randomState)); + haier.setRaw(randomState); + EXPECT_EQ("Command: 9 (Timer Set), Mode: 3 (HEAT), Temp: 20C, Fan: 2, " + "Swing: 1 (Up), Sleep: On, Health: Off, " + "Current Time: 16:32, On Timer: Off, Off Timer: Off", + haier.toString()); + // getRaw() should correct the checksum. + EXPECT_TRUE(IRHaierAC::validChecksum(haier.getRaw())); +} + +// Tests for decodeHaierAC(). + +// Decode normal "synthetic" messages. +TEST(TestDecodeHaierAC, NormalDecodeWithStrict) { + IRsendTest irsend(0); + IRrecv irrecv(0); + irsend.begin(); + + uint8_t expectedState[HAIER_AC_STATE_LENGTH] = { + 0xA5, 0x01, 0x20, 0x01, 0x00, 0xC0, 0x20, 0x00, 0xA7}; + // With the specific decoder. + irsend.reset(); + irsend.sendHaierAC(expectedState); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decodeHaierAC(&irsend.capture, HAIER_AC_BITS, true)); + EXPECT_EQ(HAIER_AC, irsend.capture.decode_type); + EXPECT_EQ(HAIER_AC_BITS, irsend.capture.bits); + EXPECT_FALSE(irsend.capture.repeat); + EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits); + + // With the all the decoders. + irsend.reset(); + irsend.sendHaierAC(expectedState); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(HAIER_AC, irsend.capture.decode_type); + EXPECT_EQ(HAIER_AC_BITS, irsend.capture.bits); + EXPECT_FALSE(irsend.capture.repeat); + EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits); +} + +// Decode a "real" example message. +TEST(TestDecodeHaierAC, RealExample1) { + IRsendTest irsend(0); + IRrecv irrecv(0); + irsend.begin(); + + irsend.reset(); + // Data from Issue #404 captured by kuzin2006 + uint16_t rawData[149] = {3030, 3044, 3030, 4304, 576, 1694, 550, 582, 552, + 1704, 552, 714, 550, 582, 550, 1706, 552, 582, 550, 1836, 552, 582, 578, + 568, 550, 582, 550, 714, 550, 582, 550, 582, 552, 582, 550, 1836, 552, + 582, 552, 580, 580, 1692, 550, 712, 552, 582, 550, 582, 552, 580, 550, + 714, 552, 582, 550, 582, 552, 582, 578, 698, 552, 580, 552, 582, 552, + 582, 552, 1836, 552, 580, 552, 582, 552, 582, 550, 714, 578, 568, 550, + 582, 550, 582, 552, 714, 550, 1706, 550, 1706, 550, 582, 550, 714, 552, + 582, 580, 566, 552, 582, 550, 714, 552, 580, 552, 580, 552, 1706, 550, + 714, 550, 582, 552, 582, 578, 568, 552, 712, 552, 582, 550, 582, 550, + 582, 550, 712, 552, 582, 550, 582, 552, 582, 578, 722, 552, 1704, 550, + 582, 550, 1706, 550, 736, 550, 582, 550, 1706, 550, 1704, 552, 1704, 578}; + uint8_t expectedState[HAIER_AC_STATE_LENGTH] = { + 0xA5, 0x01, 0x20, 0x01, 0x00, 0xC0, 0x20, 0x00, 0xA7}; + + irsend.sendRaw(rawData, 149, 38000); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(HAIER_AC, irsend.capture.decode_type); + EXPECT_EQ(HAIER_AC_BITS, irsend.capture.bits); + EXPECT_FALSE(irsend.capture.repeat); + EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits); + + IRHaierAC haier(0); + haier.setRaw(irsend.capture.state); + EXPECT_EQ("Command: 1 (On), Mode: 0 (AUTO), Temp: 16C, Fan: 0 (AUTO), " + "Swing: 0 (Off), Sleep: Off, Health: Off, " + "Current Time: 00:01, On Timer: Off, Off Timer: Off", + haier.toString()); +} + +// Decode a "real" example message. +TEST(TestDecodeHaierAC, RealExample2) { + IRsendTest irsend(0); + IRrecv irrecv(0); + irsend.begin(); + + irsend.reset(); + // Data from Issue #404 captured by kuzin2006 + uint16_t rawData[149] = {3028, 3046, 3028, 4304, 576, 1694, 552, 582, 550, + 1704, 552, 714, 550, 582, 552, 1704, 550, 582, 550, 1836, 552, 582, 578, + 1690, 552, 1704, 552, 712, 550, 582, 550, 1706, 550, 1706, 552, 712, 550, + 582, 552, 582, 578, 1690, 552, 714, 552, 580, 552, 582, 552, 582, 550, + 712, 552, 582, 550, 582, 550, 582, 578, 698, 552, 582, 550, 584, 550, 582, + 552, 1836, 550, 582, 550, 582, 550, 582, 550, 712, 578, 568, 550, 582, + 550, 582, 550, 714, 552, 1706, 550, 1706, 552, 580, 550, 714, 550, 582, + 580, 568, 550, 582, 550, 714, 550, 582, 550, 582, 550, 1706, 552, 712, + 550, 582, 550, 582, 580, 568, 552, 712, 550, 584, 550, 582, 550, 584, 550, + 712, 550, 582, 550, 582, 550, 582, 578, 722, 550, 582, 552, 580, 552, 582, + 550, 738, 550, 1706, 550, 1704, 552, 582, 550, 582, 578}; + uint8_t expectedState[HAIER_AC_STATE_LENGTH] = { + 0xA5, 0x66, 0x20, 0x01, 0x00, 0xC0, 0x20, 0x00, 0x0C}; + + irsend.sendRaw(rawData, 149, 38000); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(HAIER_AC, irsend.capture.decode_type); + EXPECT_EQ(HAIER_AC_BITS, irsend.capture.bits); + EXPECT_FALSE(irsend.capture.repeat); + EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits); + + IRHaierAC haier(0); + haier.setRaw(irsend.capture.state); + EXPECT_EQ("Command: 6 (Temp Up), Mode: 0 (AUTO), Temp: 22C, Fan: 0 (AUTO), " + "Swing: 0 (Off), Sleep: Off, Health: Off, " + "Current Time: 00:01, On Timer: Off, Off Timer: Off", + haier.toString()); +} + +// Decode a "real" example message. +TEST(TestDecodeHaierAC, RealExample3) { + IRsendTest irsend(0); + IRrecv irrecv(0); + irsend.begin(); + + irsend.reset(); + // Data from Issue #404 captured by kuzin2006 + uint16_t rawData[149] = {3030, 3044, 3030, 4302, 578, 1692, 550, 582, 550, + 1706, 550, 714, 550, 582, 552, 1706, 550, 582, 550, 1836, 552, 1706, 578, + 1690, 552, 1704, 552, 714, 550, 1706, 552, 1706, 550, 582, 550, 714, 552, + 582, 550, 582, 578, 1690, 550, 714, 552, 582, 552, 582, 550, 582, 550, + 714, 550, 584, 550, 582, 550, 582, 578, 700, 552, 1706, 550, 582, 550, + 582, 552, 1836, 550, 582, 550, 582, 552, 1706, 550, 714, 578, 568, 552, + 582, 552, 582, 550, 714, 550, 1706, 550, 1706, 550, 582, 552, 712, 552, + 582, 580, 568, 550, 582, 550, 714, 550, 582, 550, 582, 550, 1706, 550, + 714, 550, 582, 550, 582, 578, 568, 552, 712, 552, 582, 550, 582, 550, 582, + 550, 712, 550, 584, 550, 582, 552, 582, 578, 722, 552, 1704, 550, 582, + 550, 1706, 550, 1862, 550, 1706, 550, 582, 550, 1704, 552, 582, 578}; + uint8_t expectedState[HAIER_AC_STATE_LENGTH] = { + 0xA5, 0xEC, 0x20, 0x09, 0x20, 0xC0, 0x20, 0x00, 0xBA}; + + irsend.sendRaw(rawData, 149, 38000); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(HAIER_AC, irsend.capture.decode_type); + EXPECT_EQ(HAIER_AC_BITS, irsend.capture.bits); + EXPECT_FALSE(irsend.capture.repeat); + EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits); + + IRHaierAC haier(0); + haier.setRaw(irsend.capture.state); + EXPECT_EQ("Command: 12 (Health), Mode: 0 (AUTO), Temp: 30C, Fan: 0 (AUTO), " + "Swing: 0 (Off), Sleep: Off, Health: On, " + "Current Time: 00:09, On Timer: Off, Off Timer: Off", + haier.toString()); +} diff --git a/lib/IRremoteESP8266/test/ir_Hitachi_test.cpp b/lib/IRremoteESP8266/test/ir_Hitachi_test.cpp new file mode 100644 index 0000000..0173061 --- /dev/null +++ b/lib/IRremoteESP8266/test/ir_Hitachi_test.cpp @@ -0,0 +1,577 @@ +// Copyright 2018 David Conran + +#include "IRremoteESP8266.h" +#include "IRrecv.h" +#include "IRrecv_test.h" +#include "IRsend.h" +#include "IRsend_test.h" +#include "gtest/gtest.h" + +// Tests for sendHitachiAC(). + +// Test sending typical data only. +TEST(TestSendHitachiAC, SendData) { + IRsendTest irsend(0); + irsend.begin(); + + uint8_t hitachi_code[HITACHI_AC_STATE_LENGTH] = { + 0x80, 0x08, 0x0C, 0x02, 0xFD, 0x80, 0x7F, 0x88, 0x48, 0x80, 0x20, 0x04, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x00, 0x00, 0xAC}; + irsend.reset(); + irsend.sendHitachiAC(hitachi_code); + EXPECT_EQ( + "m3300s1700" + "m400s1250m400s500m400s500m400s500m400s500m400s500m400s500m400s500" + "m400s500m400s500m400s500m400s500m400s1250m400s500m400s500m400s500" + "m400s500m400s500m400s500m400s500m400s1250m400s1250m400s500m400s500" + "m400s500m400s500m400s500m400s500m400s500m400s500m400s1250m400s500" + "m400s1250m400s1250m400s1250m400s1250m400s1250m400s1250m400s500m400s1250" + "m400s1250m400s500m400s500m400s500m400s500m400s500m400s500m400s500" + "m400s500m400s1250m400s1250m400s1250m400s1250m400s1250m400s1250m400s1250" + "m400s1250m400s500m400s500m400s500m400s1250m400s500m400s500m400s500" + "m400s500m400s1250m400s500m400s500m400s1250m400s500m400s500m400s500" + "m400s1250m400s500m400s500m400s500m400s500m400s500m400s500m400s500" + "m400s500m400s500m400s1250m400s500m400s500m400s500m400s500m400s500" + "m400s500m400s500m400s500m400s500m400s500m400s1250m400s500m400s500" + "m400s500m400s500m400s500m400s500m400s500m400s500m400s500m400s500" + "m400s1250m400s500m400s500m400s500m400s500m400s500m400s500m400s500" + "m400s500m400s500m400s500m400s500m400s500m400s500m400s500m400s500" + "m400s500m400s500m400s500m400s500m400s500m400s500m400s500m400s500" + "m400s500m400s500m400s500m400s500m400s500m400s500m400s500m400s500" + "m400s500m400s500m400s500m400s500m400s500m400s500m400s500m400s1250" + "m400s500m400s500m400s500m400s500m400s500m400s500m400s500m400s500" + "m400s500m400s500m400s500m400s500m400s500m400s500m400s500m400s500" + "m400s500m400s500m400s500m400s500m400s500m400s500m400s500m400s500" + "m400s500m400s500m400s500m400s500m400s500m400s500m400s500m400s500" + "m400s500m400s500m400s500m400s500m400s500m400s500m400s500m400s500" + "m400s500m400s500m400s500m400s500m400s500m400s500m400s500m400s500" + "m400s1250m400s500m400s500m400s500m400s500m400s500m400s500m400s500" + "m400s500m400s500m400s500m400s500m400s500m400s500m400s500m400s500" + "m400s500m400s500m400s500m400s500m400s500m400s500m400s500m400s500" + "m400s1250m400s500m400s1250m400s500m400s1250m400s1250m400s500m400s500" + "m400s100000", irsend.outputStr()); +} + +// Test sending with repeats. +TEST(TestSendHitachiAC, SendWithRepeats) { + IRsendTest irsend(0); + irsend.begin(); + + uint8_t hitachi_code[HITACHI_AC_STATE_LENGTH] = { + 0x80, 0x08, 0x0C, 0x02, 0xFD, 0x80, 0x7F, 0x88, 0x48, 0x80, 0x20, 0x04, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x00, 0x00, 0xAC}; + irsend.reset(); + + irsend.sendHitachiAC(hitachi_code, HITACHI_AC_STATE_LENGTH, 1); + EXPECT_EQ( + "m3300s1700" + "m400s1250m400s500m400s500m400s500m400s500m400s500m400s500m400s500" + "m400s500m400s500m400s500m400s500m400s1250m400s500m400s500m400s500" + "m400s500m400s500m400s500m400s500m400s1250m400s1250m400s500m400s500" + "m400s500m400s500m400s500m400s500m400s500m400s500m400s1250m400s500" + "m400s1250m400s1250m400s1250m400s1250m400s1250m400s1250m400s500m400s1250" + "m400s1250m400s500m400s500m400s500m400s500m400s500m400s500m400s500" + "m400s500m400s1250m400s1250m400s1250m400s1250m400s1250m400s1250m400s1250" + "m400s1250m400s500m400s500m400s500m400s1250m400s500m400s500m400s500" + "m400s500m400s1250m400s500m400s500m400s1250m400s500m400s500m400s500" + "m400s1250m400s500m400s500m400s500m400s500m400s500m400s500m400s500" + "m400s500m400s500m400s1250m400s500m400s500m400s500m400s500m400s500" + "m400s500m400s500m400s500m400s500m400s500m400s1250m400s500m400s500" + "m400s500m400s500m400s500m400s500m400s500m400s500m400s500m400s500" + "m400s1250m400s500m400s500m400s500m400s500m400s500m400s500m400s500" + "m400s500m400s500m400s500m400s500m400s500m400s500m400s500m400s500" + "m400s500m400s500m400s500m400s500m400s500m400s500m400s500m400s500" + "m400s500m400s500m400s500m400s500m400s500m400s500m400s500m400s500" + "m400s500m400s500m400s500m400s500m400s500m400s500m400s500m400s1250" + "m400s500m400s500m400s500m400s500m400s500m400s500m400s500m400s500" + "m400s500m400s500m400s500m400s500m400s500m400s500m400s500m400s500" + "m400s500m400s500m400s500m400s500m400s500m400s500m400s500m400s500" + "m400s500m400s500m400s500m400s500m400s500m400s500m400s500m400s500" + "m400s500m400s500m400s500m400s500m400s500m400s500m400s500m400s500" + "m400s500m400s500m400s500m400s500m400s500m400s500m400s500m400s500" + "m400s1250m400s500m400s500m400s500m400s500m400s500m400s500m400s500" + "m400s500m400s500m400s500m400s500m400s500m400s500m400s500m400s500" + "m400s500m400s500m400s500m400s500m400s500m400s500m400s500m400s500" + "m400s1250m400s500m400s1250m400s500m400s1250m400s1250m400s500m400s500" + "m400s100000" + "m3300s1700" + "m400s1250m400s500m400s500m400s500m400s500m400s500m400s500m400s500" + "m400s500m400s500m400s500m400s500m400s1250m400s500m400s500m400s500" + "m400s500m400s500m400s500m400s500m400s1250m400s1250m400s500m400s500" + "m400s500m400s500m400s500m400s500m400s500m400s500m400s1250m400s500" + "m400s1250m400s1250m400s1250m400s1250m400s1250m400s1250m400s500m400s1250" + "m400s1250m400s500m400s500m400s500m400s500m400s500m400s500m400s500" + "m400s500m400s1250m400s1250m400s1250m400s1250m400s1250m400s1250m400s1250" + "m400s1250m400s500m400s500m400s500m400s1250m400s500m400s500m400s500" + "m400s500m400s1250m400s500m400s500m400s1250m400s500m400s500m400s500" + "m400s1250m400s500m400s500m400s500m400s500m400s500m400s500m400s500" + "m400s500m400s500m400s1250m400s500m400s500m400s500m400s500m400s500" + "m400s500m400s500m400s500m400s500m400s500m400s1250m400s500m400s500" + "m400s500m400s500m400s500m400s500m400s500m400s500m400s500m400s500" + "m400s1250m400s500m400s500m400s500m400s500m400s500m400s500m400s500" + "m400s500m400s500m400s500m400s500m400s500m400s500m400s500m400s500" + "m400s500m400s500m400s500m400s500m400s500m400s500m400s500m400s500" + "m400s500m400s500m400s500m400s500m400s500m400s500m400s500m400s500" + "m400s500m400s500m400s500m400s500m400s500m400s500m400s500m400s1250" + "m400s500m400s500m400s500m400s500m400s500m400s500m400s500m400s500" + "m400s500m400s500m400s500m400s500m400s500m400s500m400s500m400s500" + "m400s500m400s500m400s500m400s500m400s500m400s500m400s500m400s500" + "m400s500m400s500m400s500m400s500m400s500m400s500m400s500m400s500" + "m400s500m400s500m400s500m400s500m400s500m400s500m400s500m400s500" + "m400s500m400s500m400s500m400s500m400s500m400s500m400s500m400s500" + "m400s1250m400s500m400s500m400s500m400s500m400s500m400s500m400s500" + "m400s500m400s500m400s500m400s500m400s500m400s500m400s500m400s500" + "m400s500m400s500m400s500m400s500m400s500m400s500m400s500m400s500" + "m400s1250m400s500m400s1250m400s500m400s1250m400s1250m400s500m400s500" + "m400s100000", irsend.outputStr()); +} + +// Test sending atypical sizes. +TEST(TestSendHitachiAC, SendUnexpectedSizes) { + IRsendTest irsend(0); + irsend.begin(); + + uint8_t hitachi_short_code[HITACHI_AC_STATE_LENGTH - 1] = { + 0x80, 0x08, 0x0C, 0x02, 0xFD, 0x80, 0x7F, 0x88, 0x48, 0x80, 0x20, 0x04, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x00, 0x00}; + uint8_t hitachi_long_code[HITACHI_AC_STATE_LENGTH + 1] = { + 0x80, 0x08, 0x0C, 0x02, 0xFD, 0x80, 0x7F, 0x88, 0x48, 0x80, 0x20, 0x04, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x00, 0x00, 0xAC, 0xFF}; + irsend.reset(); + irsend.sendHitachiAC(hitachi_short_code, HITACHI_AC_STATE_LENGTH - 1); + ASSERT_EQ("", irsend.outputStr()); + + irsend.reset(); + irsend.sendHitachiAC(hitachi_long_code, HITACHI_AC_STATE_LENGTH + 1); + ASSERT_EQ( + "m3300s1700" + "m400s1250m400s500m400s500m400s500m400s500m400s500m400s500m400s500" + "m400s500m400s500m400s500m400s500m400s1250m400s500m400s500m400s500" + "m400s500m400s500m400s500m400s500m400s1250m400s1250m400s500m400s500" + "m400s500m400s500m400s500m400s500m400s500m400s500m400s1250m400s500" + "m400s1250m400s1250m400s1250m400s1250m400s1250m400s1250m400s500m400s1250" + "m400s1250m400s500m400s500m400s500m400s500m400s500m400s500m400s500" + "m400s500m400s1250m400s1250m400s1250m400s1250m400s1250m400s1250m400s1250" + "m400s1250m400s500m400s500m400s500m400s1250m400s500m400s500m400s500" + "m400s500m400s1250m400s500m400s500m400s1250m400s500m400s500m400s500" + "m400s1250m400s500m400s500m400s500m400s500m400s500m400s500m400s500" + "m400s500m400s500m400s1250m400s500m400s500m400s500m400s500m400s500" + "m400s500m400s500m400s500m400s500m400s500m400s1250m400s500m400s500" + "m400s500m400s500m400s500m400s500m400s500m400s500m400s500m400s500" + "m400s1250m400s500m400s500m400s500m400s500m400s500m400s500m400s500" + "m400s500m400s500m400s500m400s500m400s500m400s500m400s500m400s500" + "m400s500m400s500m400s500m400s500m400s500m400s500m400s500m400s500" + "m400s500m400s500m400s500m400s500m400s500m400s500m400s500m400s500" + "m400s500m400s500m400s500m400s500m400s500m400s500m400s500m400s1250" + "m400s500m400s500m400s500m400s500m400s500m400s500m400s500m400s500" + "m400s500m400s500m400s500m400s500m400s500m400s500m400s500m400s500" + "m400s500m400s500m400s500m400s500m400s500m400s500m400s500m400s500" + "m400s500m400s500m400s500m400s500m400s500m400s500m400s500m400s500" + "m400s500m400s500m400s500m400s500m400s500m400s500m400s500m400s500" + "m400s500m400s500m400s500m400s500m400s500m400s500m400s500m400s500" + "m400s1250m400s500m400s500m400s500m400s500m400s500m400s500m400s500" + "m400s500m400s500m400s500m400s500m400s500m400s500m400s500m400s500" + "m400s500m400s500m400s500m400s500m400s500m400s500m400s500m400s500" + "m400s1250m400s500m400s1250m400s500m400s1250m400s1250m400s500m400s500" + "m400s1250m400s1250m400s1250m400s1250m400s1250m400s1250m400s1250m400s1250" + "m400s100000", irsend.outputStr()); +} + +// Tests for decodeHitachiAC(). + +// Decode a synthetic HitachiAC message. +TEST(TestDecodeHitachiAC, NormalSynthetic) { + IRsendTest irsend(0); + IRrecv irrecv(0); + irsend.begin(); + + uint8_t hitachi_code[HITACHI_AC_STATE_LENGTH] = { + 0x80, 0x08, 0x0C, 0x02, 0xFD, 0x80, 0x7F, 0x88, 0x48, 0x80, 0x20, 0x04, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x00, 0x00, 0xAC}; + + irsend.reset(); + irsend.sendHitachiAC(hitachi_code); + irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(HITACHI_AC, irsend.capture.decode_type); + ASSERT_EQ(HITACHI_AC_BITS, irsend.capture.bits); + EXPECT_STATE_EQ(hitachi_code, irsend.capture.state, HITACHI_AC_BITS); +} + +// Decode a 'real' HitachiAC message. +TEST(TestDecodeHitachiAC, NormalRealExample1) { + IRsendTest irsend(0); + IRrecv irrecv(0); + irsend.begin(); + + uint8_t hitachi_code[HITACHI_AC_STATE_LENGTH] = { + 0x80, 0x08, 0x0C, 0x02, 0xFD, 0x80, 0x7F, 0x88, 0x48, 0x80, 0x20, 0x04, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x00, 0x00, 0xAC}; + + // Ref: https://github.com/markszabo/IRremoteESP8266/issues/417 + // 'On' '16c' 'auto fan' 'cooling mode' + uint16_t rawData[451] = { + 3318, 1720, 400, 1276, 400, 432, 398, 434, 398, 434, 400, 432, 398, 432, + 398, 432, 398, 434, 398, 432, 398, 434, 400, 432, 398, 434, 398, 1278, + 398, 434, 398, 434, 396, 434, 398, 434, 398, 432, 398, 434, 398, 432, 398, + 1276, 426, 1252, 424, 408, 424, 406, 424, 408, 426, 406, 398, 432, 398, + 434, 398, 432, 400, 432, 398, 1276, 426, 408, 424, 1252, 426, 1252, 424, + 1250, 426, 1252, 428, 1250, 426, 1252, 424, 406, 426, 1248, 428, 1252, + 426, 406, 426, 406, 424, 408, 400, 432, 400, 430, 400, 432, 400, 430, 400, + 432, 400, 1276, 400, 1276, 402, 1276, 400, 1276, 400, 1276, 400, 1278, + 400, 1276, 402, 1276, 402, 428, 402, 430, 400, 430, 402, 1276, 400, 430, + 402, 430, 400, 432, 402, 428, 402, 1278, 400, 430, 402, 430, 402, 1276, + 402, 428, 402, 430, 402, 430, 400, 1276, 402, 430, 402, 430, 402, 430, + 402, 430, 402, 428, 402, 430, 404, 430, 402, 428, 402, 430, 402, 1276, + 402, 430, 402, 428, 402, 430, 400, 428, 402, 430, 402, 430, 402, 430, 402, + 430, 402, 428, 402, 430, 402, 1274, 402, 428, 402, 430, 402, 430, 402, + 430, 402, 430, 402, 428, 402, 428, 402, 428, 404, 428, 404, 428, 402, + 1276, 400, 430, 402, 430, 400, 432, 400, 456, 374, 432, 400, 456, 404, + 428, 404, 426, 404, 428, 402, 428, 402, 430, 402, 430, 400, 432, 398, 434, + 376, 454, 378, 454, 380, 452, 378, 452, 404, 428, 406, 424, 432, 398, 406, + 426, 430, 402, 404, 428, 428, 402, 400, 430, 400, 432, 398, 434, 398, 432, + 398, 434, 372, 460, 374, 434, 398, 432, 398, 434, 396, 434, 376, 456, 376, + 456, 376, 456, 376, 1300, 378, 454, 378, 452, 378, 454, 378, 454, 378, + 454, 378, 452, 378, 454, 400, 432, 402, 430, 402, 430, 402, 430, 402, 428, + 402, 430, 402, 430, 400, 430, 402, 430, 400, 432, 400, 430, 400, 432, 400, + 430, 402, 430, 400, 432, 398, 432, 400, 430, 400, 432, 398, 432, 398, 434, + 398, 432, 398, 432, 400, 434, 398, 432, 398, 432, 398, 434, 398, 434, 396, + 434, 398, 434, 398, 432, 398, 434, 398, 432, 398, 456, 376, 454, 376, 436, + 396, 454, 376, 454, 378, 454, 376, 454, 376, 456, 374, 458, 374, 1302, + 374, 456, 374, 458, 374, 458, 376, 456, 374, 456, 374, 456, 374, 456, 376, + 456, 376, 456, 374, 458, 374, 458, 374, 458, 372, 458, 372, 460, 374, 458, + 372, 460, 372, 460, 372, 460, 370, 462, 372, 460, 372, 460, 370, 462, 370, + 462, 356, 1320, 368, 464, 346, 1332, 356, 476, 368, 1310, 366, 1312, 366, + 464, 366, 466, 366}; + + irsend.reset(); + irsend.sendRaw(rawData, 451, 38000); + irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decodeHitachiAC(&irsend.capture)); + EXPECT_EQ(HITACHI_AC, irsend.capture.decode_type); + ASSERT_EQ(HITACHI_AC_BITS, irsend.capture.bits); + EXPECT_STATE_EQ(hitachi_code, irsend.capture.state, HITACHI_AC_BITS); +} + +// Decode another 'real' HitachiAC message. +TEST(TestDecodeHitachiAC, NormalRealExample2) { + IRsendTest irsend(0); + IRrecv irrecv(0); + irsend.begin(); + + uint8_t hitachi_code[HITACHI_AC_STATE_LENGTH] = { + 0x80, 0x08, 0x0C, 0x02, 0xFD, 0x80, 0x7F, 0x88, 0x48, 0x10, 0xC0, 0x02, + 0x00, 0xA0, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x00, 0x00, 0xD0}; + + // Ref: https://github.com/markszabo/IRremoteESP8266/issues/417 + // 'On' '32c' 'auto fan' 'heating mode' + uint16_t rawData[451] = { + 3322, 1718, 400, 1278, 398, 432, 402, 430, 400, 430, 402, 430, 402, 430, + 400, 432, 400, 432, 402, 430, 400, 430, 402, 430, 400, 430, 402, 1276, + 402, 430, 400, 430, 402, 430, 402, 430, 402, 428, 402, 430, 400, 430, 402, + 1278, 400, 1274, 402, 430, 400, 432, 400, 430, 402, 430, 402, 430, 400, + 430, 400, 432, 402, 428, 402, 1276, 404, 428, 402, 1276, 402, 1274, 402, + 1274, 404, 1272, 402, 1274, 402, 1274, 404, 430, 402, 1274, 402, 1274, + 402, 430, 402, 430, 402, 430, 400, 430, 402, 430, 404, 428, 402, 430, 400, + 430, 402, 1274, 402, 1276, 402, 1276, 402, 1276, 400, 1276, 402, 1272, + 404, 1274, 402, 1272, 404, 430, 402, 430, 402, 430, 402, 1274, 404, 428, + 402, 428, 404, 430, 402, 430, 402, 1274, 402, 430, 402, 430, 402, 1274, + 402, 430, 402, 430, 402, 428, 404, 428, 402, 430, 402, 430, 402, 1272, + 406, 430, 402, 428, 402, 430, 402, 430, 400, 1276, 400, 1276, 402, 430, + 402, 430, 402, 430, 402, 430, 402, 428, 402, 430, 402, 430, 402, 430, 402, + 430, 402, 430, 402, 430, 400, 430, 402, 1272, 404, 430, 400, 430, 402, + 430, 402, 428, 404, 430, 400, 430, 402, 430, 402, 430, 402, 428, 402, + 1276, 402, 428, 402, 1276, 402, 430, 402, 428, 402, 430, 402, 430, 402, + 430, 402, 428, 402, 430, 402, 430, 402, 430, 402, 430, 402, 428, 402, 428, + 404, 430, 402, 428, 402, 430, 402, 428, 402, 430, 402, 432, 402, 428, 402, + 430, 402, 430, 402, 430, 400, 430, 400, 430, 402, 430, 402, 430, 402, 426, + 404, 430, 402, 430, 400, 428, 404, 428, 404, 432, 398, 432, 400, 456, 376, + 456, 376, 456, 376, 1276, 402, 454, 378, 456, 376, 454, 378, 454, 402, + 430, 376, 454, 376, 456, 400, 430, 378, 454, 400, 432, 402, 430, 376, 454, + 376, 456, 376, 454, 400, 432, 374, 458, 400, 430, 398, 434, 374, 456, 374, + 456, 374, 458, 374, 458, 374, 456, 352, 482, 372, 460, 374, 458, 364, 468, + 352, 480, 372, 460, 352, 480, 352, 480, 352, 480, 352, 480, 352, 454, 400, + 432, 376, 480, 374, 456, 374, 456, 378, 430, 404, 452, 376, 430, 404, 428, + 406, 426, 406, 426, 404, 426, 406, 428, 406, 426, 430, 400, 430, 1244, + 408, 424, 434, 398, 432, 400, 406, 424, 432, 400, 406, 426, 430, 400, 432, + 400, 432, 400, 430, 402, 428, 402, 404, 428, 404, 426, 406, 426, 406, 426, + 404, 428, 428, 402, 404, 428, 404, 428, 402, 428, 404, 428, 402, 428, 404, + 428, 404, 1274, 402, 1274, 404, 428, 400, 1276, 402, 430, 402, 430, 402, + 430, 402, 430, 400}; + + irsend.reset(); + irsend.sendRaw(rawData, 451, 38000); + irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(HITACHI_AC, irsend.capture.decode_type); + ASSERT_EQ(HITACHI_AC_BITS, irsend.capture.bits); + EXPECT_STATE_EQ(hitachi_code, irsend.capture.state, HITACHI_AC_BITS); +} + +// Tests for sendHitachiAC1(). + +// Test sending typical data only. +TEST(TestSendHitachiAC1, SendData) { + IRsendTest irsend(0); + irsend.begin(); + + uint8_t hitachi_code[HITACHI_AC1_STATE_LENGTH] = { + 0xB2, 0xAE, 0x4D, 0x51, 0xF0, 0x61, 0x84, 0x00, 0x00, 0x00, 0x00, 0x30, + 0xB8}; + irsend.reset(); + irsend.sendHitachiAC1(hitachi_code); + EXPECT_EQ( + "m3400s3400" + "m400s1250m400s500m400s1250m400s1250m400s500m400s500m400s1250m400s500" + "m400s1250m400s500m400s1250m400s500m400s1250m400s1250m400s1250m400s500" + "m400s500m400s1250m400s500m400s500m400s1250m400s1250m400s500m400s1250" + "m400s500m400s1250m400s500m400s1250m400s500m400s500m400s500m400s1250" + "m400s1250m400s1250m400s1250m400s1250m400s500m400s500m400s500m400s500" + "m400s500m400s1250m400s1250m400s500m400s500m400s500m400s500m400s1250" + "m400s1250m400s500m400s500m400s500m400s500m400s1250m400s500m400s500" + "m400s500m400s500m400s500m400s500m400s500m400s500m400s500m400s500" + "m400s500m400s500m400s500m400s500m400s500m400s500m400s500m400s500" + "m400s500m400s500m400s500m400s500m400s500m400s500m400s500m400s500" + "m400s500m400s500m400s500m400s500m400s500m400s500m400s500m400s500" + "m400s500m400s500m400s1250m400s1250m400s500m400s500m400s500m400s500" + "m400s1250m400s500m400s1250m400s1250m400s1250m400s500m400s500m400s500" + "m400s100000", irsend.outputStr()); +} + +// Decode a 'real' HitachiAC1 message. +TEST(TestDecodeHitachiAC1, NormalRealExample) { + IRsendTest irsend(0); + IRrecv irrecv(0); + irsend.begin(); + + uint8_t hitachi_code[HITACHI_AC1_STATE_LENGTH] = { + 0xB2, 0xAE, 0x4D, 0x51, 0xF0, 0x61, 0x84, 0x00, 0x00, 0x00, 0x00, 0x10, + 0x98}; + + // Ref: https://github.com/markszabo/IRremoteESP8266/issues/453 + uint16_t rawData[211] = { + 3400, 3350, 450, 1250, 450, 400, 400, 1300, 400, 1300, 400, 400, 450, 400, + 400, 1300, 400, 400, 400, 1300, 400, 400, 450, 1250, 400, 450, 400, 1300, + 400, 1250, 450, 1250, 450, 400, 400, 450, 400, 1250, 450, 400, 400, 400, + 400, 1300, 400, 1300, 400, 400, 450, 1250, 450, 400, 400, 1300, 400, 400, + 450, 1250, 400, 400, 450, 400, 400, 400, 450, 1250, 400, 1300, 450, 1250, + 450, 1250, 400, 1300, 400, 400, 450, 400, 400, 450, 350, 450, 400, 400, 400, + 1300, 400, 1300, 400, 400, 450, 400, 400, 400, 450, 400, 400, 1300, 400, + 1250, 450, 400, 400, 400, 450, 400, 400, 400, 450, 1250, 450, 400, 400, 400, + 450, 400, 400, 400, 450, 400, 400, 400, 450, 400, 400, 400, 450, 400, 400, + 400, 400, 450, 400, 400, 400, 400, 450, 400, 400, 400, 450, 400, 400, 450, + 400, 400, 400, 400, 450, 400, 400, 400, 450, 400, 400, 450, 400, 400, 400, + 400, 400, 450, 400, 400, 400, 400, 450, 400, 400, 400, 450, 400, 400, 400, + 450, 400, 400, 400, 450, 400, 400, 400, 450, 400, 400, 1300, 400, 400, 450, + 400, 400, 400, 400, 400, 450, 1250, 450, 400, 400, 400, 450, 1250, 450, + 1250, 450, 400, 400, 400, 450, 400, 400}; // UNKNOWN 828A89E1 + + irsend.reset(); + irsend.sendRaw(rawData, 211, 38000); + irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(HITACHI_AC1, irsend.capture.decode_type); + ASSERT_EQ(HITACHI_AC1_BITS, irsend.capture.bits); + EXPECT_STATE_EQ(hitachi_code, irsend.capture.state, HITACHI_AC1_BITS); +} + + +// Tests for sendHitachiAC2(). + +// Test sending typical data only. +TEST(TestSendHitachiAC2, SendData) { + IRsendTest irsend(0); + irsend.begin(); + + uint8_t hitachi_code[HITACHI_AC2_STATE_LENGTH] = { + 0x80, 0x08, 0x00, 0x02, 0xFD, 0xFF, 0x00, 0x33, 0xCC, 0x49, 0xB6, 0x22, + 0xDD, 0x01, 0xFE, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, + 0xFF, 0xCA, 0x35, 0x8F, 0x70, 0x00, 0xFF, 0x00, 0xFF, 0x01, 0xFE, 0xC0, + 0x3F, 0x80, 0x7F, 0x11, 0xEE, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0x00, 0xFF, + 0x00, 0xFF, 0x00, 0xFF, 0x00}; + irsend.reset(); + irsend.sendHitachiAC2(hitachi_code); + EXPECT_EQ( + "m3300s1700" + "m400s1250m400s500m400s500m400s500m400s500m400s500m400s500m400s500" + "m400s500m400s500m400s500m400s500m400s1250m400s500m400s500m400s500" + "m400s500m400s500m400s500m400s500m400s500m400s500m400s500m400s500" + "m400s500m400s500m400s500m400s500m400s500m400s500m400s1250m400s500" + "m400s1250m400s1250m400s1250m400s1250m400s1250m400s1250m400s500m400s1250" + "m400s1250m400s1250m400s1250m400s1250m400s1250m400s1250m400s1250m400s1250" + "m400s500m400s500m400s500m400s500m400s500m400s500m400s500m400s500" + "m400s500m400s500m400s1250m400s1250m400s500m400s500m400s1250m400s1250" + "m400s1250m400s1250m400s500m400s500m400s1250m400s1250m400s500m400s500" + "m400s500m400s1250m400s500m400s500m400s1250m400s500m400s500m400s1250" + "m400s1250m400s500m400s1250m400s1250m400s500m400s1250m400s1250m400s500" + "m400s500m400s500m400s1250m400s500m400s500m400s500m400s1250m400s500" + "m400s1250m400s1250m400s500m400s1250m400s1250m400s1250m400s500m400s1250" + "m400s500m400s500m400s500m400s500m400s500m400s500m400s500m400s1250" + "m400s1250m400s1250m400s1250m400s1250m400s1250m400s1250m400s1250m400s500" + "m400s500m400s500m400s500m400s500m400s500m400s500m400s500m400s500" + "m400s1250m400s1250m400s1250m400s1250m400s1250m400s1250m400s1250m400s1250" + "m400s500m400s500m400s500m400s500m400s500m400s500m400s500m400s500" + "m400s1250m400s1250m400s1250m400s1250m400s1250m400s1250m400s1250m400s1250" + "m400s500m400s500m400s500m400s500m400s500m400s500m400s500m400s500" + "m400s1250m400s1250m400s1250m400s1250m400s1250m400s1250m400s1250m400s1250" + "m400s500m400s500m400s500m400s500m400s500m400s500m400s500m400s500" + "m400s1250m400s1250m400s1250m400s1250m400s1250m400s1250m400s1250m400s1250" + "m400s500m400s500m400s500m400s500m400s500m400s500m400s500m400s500" + "m400s1250m400s1250m400s1250m400s1250m400s1250m400s1250m400s1250m400s1250" + "m400s1250m400s1250m400s500m400s500m400s1250m400s500m400s1250m400s500" + "m400s500m400s500m400s1250m400s1250m400s500m400s1250m400s500m400s1250" + "m400s1250m400s500m400s500m400s500m400s1250m400s1250m400s1250m400s1250" + "m400s500m400s1250m400s1250m400s1250m400s500m400s500m400s500m400s500" + "m400s500m400s500m400s500m400s500m400s500m400s500m400s500m400s500" + "m400s1250m400s1250m400s1250m400s1250m400s1250m400s1250m400s1250m400s1250" + "m400s500m400s500m400s500m400s500m400s500m400s500m400s500m400s500" + "m400s1250m400s1250m400s1250m400s1250m400s1250m400s1250m400s1250m400s1250" + "m400s500m400s500m400s500m400s500m400s500m400s500m400s500m400s1250" + "m400s1250m400s1250m400s1250m400s1250m400s1250m400s1250m400s1250m400s500" + "m400s1250m400s1250m400s500m400s500m400s500m400s500m400s500m400s500" + "m400s500m400s500m400s1250m400s1250m400s1250m400s1250m400s1250m400s1250" + "m400s1250m400s500m400s500m400s500m400s500m400s500m400s500m400s500" + "m400s500m400s1250m400s1250m400s1250m400s1250m400s1250m400s1250m400s1250" + "m400s500m400s500m400s500m400s1250m400s500m400s500m400s500m400s1250" + "m400s1250m400s1250m400s1250m400s500m400s1250m400s1250m400s1250m400s500" + "m400s500m400s500m400s500m400s500m400s500m400s500m400s500m400s500" + "m400s1250m400s1250m400s1250m400s1250m400s1250m400s1250m400s1250m400s1250" + "m400s500m400s500m400s500m400s500m400s500m400s500m400s500m400s500" + "m400s1250m400s1250m400s1250m400s1250m400s1250m400s1250m400s1250m400s1250" + "m400s1250m400s1250m400s1250m400s1250m400s1250m400s1250m400s1250m400s1250" + "m400s500m400s500m400s500m400s500m400s500m400s500m400s500m400s500" + "m400s1250m400s1250m400s1250m400s1250m400s1250m400s1250m400s1250m400s1250" + "m400s500m400s500m400s500m400s500m400s500m400s500m400s500m400s500" + "m400s1250m400s1250m400s1250m400s1250m400s1250m400s1250m400s1250m400s1250" + "m400s500m400s500m400s500m400s500m400s500m400s500m400s500m400s500" + "m400s1250m400s1250m400s1250m400s1250m400s1250m400s1250m400s1250m400s1250" + "m400s500m400s500m400s500m400s500m400s500m400s500m400s500m400s500" + "m400s100000", irsend.outputStr()); +} + +// Decode a synthetic HitachiAC2 message. + +TEST(TestDecodeHitachiAC2, NormalSyntheticExample) { + IRsendTest irsend(0); + IRrecv irrecv(0); + irsend.begin(); + + // ON - 32c cool (fan auto) + uint8_t hitachi_code[HITACHI_AC2_STATE_LENGTH] = { + 0x80, 0x08, 0x00, 0x02, 0xFD, 0xFF, 0x00, 0x33, 0xCC, 0x49, 0xB6, 0x22, + 0xDD, 0x01, 0xFE, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, + 0xFF, 0xCA, 0x35, 0x8F, 0x70, 0x00, 0xFF, 0x00, 0xFF, 0x01, 0xFE, 0xC0, + 0x3F, 0x80, 0x7F, 0x11, 0xEE, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0x00, 0xFF, + 0x00, 0xFF, 0x00, 0xFF, 0x00}; + + irsend.reset(); + irsend.sendHitachiAC2(hitachi_code); + irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(HITACHI_AC2, irsend.capture.decode_type); + ASSERT_EQ(HITACHI_AC2_BITS, irsend.capture.bits); + EXPECT_STATE_EQ(hitachi_code, irsend.capture.state, HITACHI_AC2_BITS); +} + +// Decode a 'real' HitachiAC2 message. +TEST(TestDecodeHitachiAC2, NormalRealExample) { + IRsendTest irsend(0); + IRrecv irrecv(0); + irsend.begin(); + + uint8_t hitachi_code[HITACHI_AC2_STATE_LENGTH] = { + 0x80, 0x08, 0x00, 0x02, 0xFD, 0xFF, 0x00, 0x33, 0xCC, 0x49, 0xB6, 0x22, + 0xDD, 0x01, 0xFE, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, + 0xFF, 0xCA, 0x35, 0x8F, 0x70, 0x00, 0xFF, 0x00, 0xFF, 0x01, 0xFE, 0xC0, + 0x3F, 0x80, 0x7F, 0x11, 0xEE, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0x00, 0xFF, + 0x00, 0xFF, 0x00, 0xFF, 0x00}; + + // Ref: https://github.com/markszabo/IRremoteESP8266/issues/417 + uint16_t rawData[851] = { // ON - 32c cool (fan auto) + 3432, 1654, 492, 1180, 488, 360, 486, 360, 486, 360, 486, 362, + 486, 362, 460, 360, 486, 362, 486, 362, 486, 360, 486, 362, + 486, 362, 486, 1184, 488, 362, 486, 360, 486, 362, 486, 362, + 486, 362, 460, 362, 486, 362, 486, 362, 484, 362, 486, 362, + 486, 362, 486, 362, 458, 362, 484, 362, 486, 362, 484, 364, + 484, 362, 486, 1184, 486, 362, 486, 1210, 488, 1210, 488, 1184, + 486, 1210, 488, 1210, 488, 1182, 488, 360, 488, 1208, 488, 1208, + 488, 1182, 488, 1208, 488, 1208, 488, 1208, 462, 1208, 490, 1208, + 488, 1208, 490, 332, 488, 360, 488, 360, 488, 360, 486, 360, + 486, 360, 486, 362, 460, 360, 486, 362, 486, 362, 486, 1210, + 488, 1210, 460, 362, 486, 362, 486, 1210, 488, 1210, 488, 1182, + 488, 1210, 488, 362, 486, 360, 488, 1182, 488, 1210, 488, 362, + 486, 362, 486, 360, 486, 1182, 488, 362, 486, 362, 486, 1210, + 488, 360, 486, 336, 486, 1210, 488, 1210, 488, 360, 486, 1182, + 488, 1210, 488, 360, 488, 1208, 488, 1182, 488, 362, 486, 362, + 486, 360, 486, 1210, 460, 362, 486, 360, 486, 362, 486, 1210, + 486, 362, 486, 1184, 488, 1208, 488, 360, 486, 1210, 488, 1184, + 488, 1208, 488, 362, 486, 1208, 462, 360, 486, 360, 486, 362, + 486, 360, 486, 362, 486, 360, 486, 334, 486, 1210, 488, 1208, + 488, 1210, 488, 1182, 488, 1208, 488, 1210, 488, 1182, 488, 1208, + 488, 360, 488, 360, 486, 360, 486, 334, 486, 362, 486, 362, + 486, 362, 486, 362, 486, 362, 486, 1182, 488, 1210, 486, 1210, + 488, 1210, 460, 1210, 488, 1210, 488, 1210, 488, 1184, 488, 360, + 486, 360, 486, 362, 486, 362, 486, 360, 460, 360, 486, 362, + 486, 362, 484, 1210, 488, 1182, 488, 1210, 488, 1210, 488, 1210, + 488, 1182, 488, 1208, 488, 1208, 488, 360, 460, 360, 486, 360, + 486, 360, 488, 360, 486, 362, 486, 362, 486, 334, 486, 1210, + 488, 1208, 488, 1210, 460, 1210, 488, 1208, 488, 1210, 488, 1182, + 488, 1208, 488, 360, 486, 362, 486, 360, 488, 334, 486, 360, + 486, 362, 486, 362, 486, 362, 486, 1210, 460, 1210, 488, 1210, + 488, 1210, 488, 1182, 488, 1210, 488, 1208, 488, 1182, 488, 360, + 486, 360, 486, 362, 486, 362, 486, 360, 486, 362, 460, 360, + 486, 362, 486, 1210, 486, 1212, 486, 1184, 488, 1210, 488, 1208, + 488, 1182, 488, 1208, 488, 1208, 488, 1208, 462, 1208, 488, 360, + 486, 362, 486, 1208, 488, 334, 486, 1208, 488, 360, 486, 360, + 486, 362, 486, 1182, 488, 1210, 488, 360, 486, 1210, 488, 334, + 486, 1208, 488, 1208, 488, 360, 486, 360, 486, 334, 486, 1210, + 488, 1210, 488, 1210, 460, 1210, 488, 360, 486, 1208, 488, 1182, + 490, 1208, 488, 360, 486, 360, 486, 360, 486, 360, 460, 360, + 486, 362, 486, 362, 486, 362, 486, 362, 486, 362, 486, 362, + 458, 362, 486, 1210, 488, 1210, 488, 1182, 488, 1210, 488, 1208, + 488, 1208, 462, 1208, 488, 1208, 488, 360, 486, 360, 486, 334, + 486, 360, 486, 362, 486, 362, 486, 362, 486, 362, 486, 1184, + 488, 1210, 488, 1210, 488, 1208, 462, 1210, 488, 1208, 488, 1208, + 488, 1182, 488, 360, 486, 360, 486, 360, 486, 362, 486, 360, + 460, 362, 486, 360, 488, 1208, 488, 1210, 488, 1184, 488, 1208, + 488, 1210, 488, 1182, 488, 1208, 488, 1208, 488, 360, 488, 1182, + 488, 1208, 488, 360, 488, 360, 488, 360, 486, 334, 486, 360, + 486, 362, 486, 362, 486, 360, 486, 1210, 488, 1182, 488, 1210, + 488, 1210, 488, 1182, 488, 1210, 488, 1210, 488, 360, 488, 334, + 486, 360, 488, 360, 486, 362, 486, 362, 486, 362, 486, 362, + 460, 1210, 488, 1210, 488, 1210, 488, 1184, 488, 1208, 488, 1208, + 490, 1182, 488, 360, 488, 360, 486, 362, 486, 1208, 488, 360, + 460, 362, 486, 362, 486, 1210, 488, 1210, 488, 1182, 488, 1208, + 488, 360, 488, 1208, 488, 1184, 486, 1210, 488, 360, 488, 360, + 486, 360, 460, 362, 486, 362, 486, 362, 486, 362, 484, 362, + 486, 362, 486, 1184, 488, 1210, 488, 1210, 488, 1182, 488, 1210, + 488, 1208, 488, 1208, 462, 1210, 488, 360, 486, 362, 486, 360, + 486, 362, 486, 334, 486, 362, 486, 360, 486, 362, 486, 1208, + 488, 1184, 488, 1208, 488, 1210, 488, 1182, 488, 1208, 488, 1208, + 488, 1208, 488, 1182, 488, 1208, 488, 1208, 488, 1182, 488, 1208, + 488, 1208, 488, 1182, 488, 1208, 488, 360, 488, 360, 486, 360, + 486, 362, 460, 362, 486, 362, 486, 362, 486, 360, 486, 1208, + 488, 1182, 488, 1208, 488, 1210, 488, 1182, 488, 1208, 490, 1208, + 488, 1208, 462, 360, 488, 360, 486, 360, 488, 360, 486, 360, + 486, 360, 486, 336, 486, 362, 486, 1210, 488, 1208, 488, 1182, + 488, 1208, 488, 1208, 488, 1208, 462, 1208, 490, 1206, 490, 360, + 488, 360, 488, 332, 486, 360, 488, 360, 486, 360, 488, 360, + 486, 360, 486, 1182, 488, 1208, 488, 1206, 492, 1208, 462, 1208, + 488, 1208, 488, 1208, 490, 1182, 490, 360, 486, 360, 486, 360, + 488, 360, 488, 360, 460, 360, 486, 362, 486, 360, 488}; + + irsend.reset(); + irsend.sendRaw(rawData, 851, 38000); + irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(HITACHI_AC2, irsend.capture.decode_type); + ASSERT_EQ(HITACHI_AC2_BITS, irsend.capture.bits); + EXPECT_STATE_EQ(hitachi_code, irsend.capture.state, HITACHI_AC2_BITS); +} diff --git a/lib/IRremoteESP8266/test/ir_JVC_test.cpp b/lib/IRremoteESP8266/test/ir_JVC_test.cpp index 0826b22..a9caf85 100644 --- a/lib/IRremoteESP8266/test/ir_JVC_test.cpp +++ b/lib/IRremoteESP8266/test/ir_JVC_test.cpp @@ -17,7 +17,7 @@ TEST(TestSendJVC, SendDataOnly) { "m8400s4200" "m525s1725m525s1725m525s525m525s525m525s525m525s525m525s1725m525s525" "m525s1725m525s525m525s1725m525s1725m525s1725m525s525m525s525m525s525" - "m525s60000", irsend.outputStr()); + "m525s21675", irsend.outputStr()); } // Test sending with different repeats. @@ -31,26 +31,26 @@ TEST(TestSendJVC, SendWithRepeats) { "m8400s4200" "m525s1725m525s1725m525s525m525s525m525s525m525s525m525s1725m525s525" "m525s1725m525s525m525s1725m525s1725m525s1725m525s525m525s525m525s525" - "m525s60000" + "m525s21675" "m525s1725m525s1725m525s525m525s525m525s525m525s525m525s1725m525s525" "m525s1725m525s525m525s1725m525s1725m525s1725m525s525m525s525m525s525" - "m525s60000", irsend.outputStr()); + "m525s34275", irsend.outputStr()); irsend.sendJVC(0xC2B8, JVC_BITS, 2); // 2 repeats. EXPECT_EQ( "m8400s4200" "m525s1725m525s1725m525s525m525s525m525s525m525s525m525s1725m525s525" "m525s1725m525s525m525s1725m525s1725m525s1725m525s525m525s525m525s525" - "m525s60000" + "m525s21675" "m525s1725m525s1725m525s525m525s525m525s525m525s525m525s1725m525s525" "m525s1725m525s525m525s1725m525s1725m525s1725m525s525m525s525m525s525" - "m525s60000" + "m525s34275" "m525s1725m525s1725m525s525m525s525m525s525m525s525m525s1725m525s525" "m525s1725m525s525m525s1725m525s1725m525s1725m525s525m525s525m525s525" - "m525s60000", irsend.outputStr()); + "m525s34275", irsend.outputStr()); } // Test sending an atypical data size. -TEST(TestSendJVC, SendUsualSize) { +TEST(TestSendJVC, SendUnusualSize) { IRsendTest irsend(4); irsend.begin(); @@ -59,7 +59,7 @@ TEST(TestSendJVC, SendUsualSize) { EXPECT_EQ( "m8400s4200" "m525s525m525s525m525s525m525s525m525s525m525s525m525s525m525s525" - "m525s60000", irsend.outputStr()); + "m525s38475", irsend.outputStr()); irsend.reset(); irsend.sendJVC(0x1234567890ABCDEF, 64); @@ -73,7 +73,7 @@ TEST(TestSendJVC, SendUsualSize) { "m525s1725m525s525m525s1725m525s525m525s1725m525s525m525s1725m525s1725" "m525s1725m525s1725m525s525m525s525m525s1725m525s1725m525s525m525s1725" "m525s1725m525s1725m525s1725m525s525m525s1725m525s1725m525s1725m525s1725" - "m525s60000", irsend.outputStr()); + "m525s10875", irsend.outputStr()); } // Tests for encodeJVC(). diff --git a/lib/IRremoteESP8266/test/ir_Kelvinator_test.cpp b/lib/IRremoteESP8266/test/ir_Kelvinator_test.cpp index c409c20..9ece663 100644 --- a/lib/IRremoteESP8266/test/ir_Kelvinator_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Kelvinator_test.cpp @@ -1,5 +1,8 @@ // Copyright 2017 David Conran +#include "IRremoteESP8266.h" +#include "IRrecv.h" +#include "IRrecv_test.h" #include "IRsend.h" #include "IRsend_test.h" #include "ir_Kelvinator.h" @@ -18,30 +21,30 @@ TEST(TestSendKelvinator, SendDataOnly) { irsend.reset(); irsend.sendKelvinator(kelv_code); EXPECT_EQ( - "m8990s4490" - "m675s1560m675s520m675s520m675s1560m675s1560m675s520m675s520m675s520" - "m675s1560m675s1560m675s520m675s1560m675s520m675s520m675s520m675s520" - "m675s520m675s520m675s520m675s520m675s520m675s520m675s520m675s1560" - "m675s520m675s520m675s520m675s520m675s1560m675s520m675s1560m675s520" - "m675s520m675s1560m675s520" - "m675s19950" - "m675s520m675s520m675s520m675s520m675s520m675s520m675s520m675s520" - "m675s520m675s520m675s520m675s520m675s520m675s520m675s520m675s520" - "m675s520m675s520m675s520m675s520m675s520m675s520m675s520m675s520" - "m675s520m675s520m675s520m675s520m675s520m675s1560m675s1560m675s1560" - "m675s39900" - "m8990s4490" - "m675s1560m675s520m675s520m675s1560m675s1560m675s520m675s520m675s520" - "m675s1560m675s1560m675s520m675s1560m675s520m675s520m675s520m675s520" - "m675s520m675s520m675s520m675s520m675s520m675s520m675s520m675s1560" - "m675s520m675s520m675s520m675s520m675s1560m675s1560m675s1560m675s520" - "m675s520m675s1560m675s520" - "m675s19950" - "m675s520m675s520m675s520m675s520m675s520m675s520m675s520m675s520" - "m675s520m675s520m675s520m675s520m675s520m675s520m675s520m675s520" - "m675s520m675s520m675s520m675s520m675s1560m675s520m675s520m675s520" - "m675s520m675s520m675s520m675s520m675s1560m675s1560m675s1560m675s1560" - "m675s39900", irsend.outputStr()); + "m9010s4505" + "m680s1530m680s510m680s510m680s1530m680s1530m680s510m680s510m680s510" + "m680s1530m680s1530m680s510m680s1530m680s510m680s510m680s510m680s510" + "m680s510m680s510m680s510m680s510m680s510m680s510m680s510m680s1530" + "m680s510m680s510m680s510m680s510m680s1530m680s510m680s1530m680s510" + "m680s510m680s1530m680s510" + "m680s19975" + "m680s510m680s510m680s510m680s510m680s510m680s510m680s510m680s510" + "m680s510m680s510m680s510m680s510m680s510m680s510m680s510m680s510" + "m680s510m680s510m680s510m680s510m680s510m680s510m680s510m680s510" + "m680s510m680s510m680s510m680s510m680s510m680s1530m680s1530m680s1530" + "m680s39950" + "m9010s4505" + "m680s1530m680s510m680s510m680s1530m680s1530m680s510m680s510m680s510" + "m680s1530m680s1530m680s510m680s1530m680s510m680s510m680s510m680s510" + "m680s510m680s510m680s510m680s510m680s510m680s510m680s510m680s1530" + "m680s510m680s510m680s510m680s510m680s1530m680s1530m680s1530m680s510" + "m680s510m680s1530m680s510" + "m680s19975" + "m680s510m680s510m680s510m680s510m680s510m680s510m680s510m680s510" + "m680s510m680s510m680s510m680s510m680s510m680s510m680s510m680s510" + "m680s510m680s510m680s510m680s510m680s1530m680s510m680s510m680s510" + "m680s510m680s510m680s510m680s510m680s1530m680s1530m680s1530m680s1530" + "m680s39950", irsend.outputStr()); } // Test sending with repeats. @@ -57,54 +60,54 @@ TEST(TestSendKelvinator, SendWithRepeats) { irsend.sendKelvinator(kelv_code, KELVINATOR_STATE_LENGTH, 1); EXPECT_EQ( - "m8990s4490" - "m675s1560m675s520m675s520m675s1560m675s1560m675s520m675s520m675s520" - "m675s1560m675s1560m675s520m675s1560m675s520m675s520m675s520m675s520" - "m675s520m675s520m675s520m675s520m675s520m675s520m675s520m675s1560" - "m675s520m675s520m675s520m675s520m675s1560m675s520m675s1560m675s520" - "m675s520m675s1560m675s520" - "m675s19950" - "m675s520m675s520m675s520m675s520m675s520m675s520m675s520m675s520" - "m675s520m675s520m675s520m675s520m675s520m675s520m675s520m675s520" - "m675s520m675s520m675s520m675s520m675s520m675s520m675s520m675s520" - "m675s520m675s520m675s520m675s520m675s520m675s1560m675s1560m675s1560" - "m675s39900" - "m8990s4490" - "m675s1560m675s520m675s520m675s1560m675s1560m675s520m675s520m675s520" - "m675s1560m675s1560m675s520m675s1560m675s520m675s520m675s520m675s520" - "m675s520m675s520m675s520m675s520m675s520m675s520m675s520m675s1560" - "m675s520m675s520m675s520m675s520m675s1560m675s1560m675s1560m675s520" - "m675s520m675s1560m675s520" - "m675s19950" - "m675s520m675s520m675s520m675s520m675s520m675s520m675s520m675s520" - "m675s520m675s520m675s520m675s520m675s520m675s520m675s520m675s520" - "m675s520m675s520m675s520m675s520m675s1560m675s520m675s520m675s520" - "m675s520m675s520m675s520m675s520m675s1560m675s1560m675s1560m675s1560" - "m675s39900" - "m8990s4490" - "m675s1560m675s520m675s520m675s1560m675s1560m675s520m675s520m675s520" - "m675s1560m675s1560m675s520m675s1560m675s520m675s520m675s520m675s520" - "m675s520m675s520m675s520m675s520m675s520m675s520m675s520m675s1560" - "m675s520m675s520m675s520m675s520m675s1560m675s520m675s1560m675s520" - "m675s520m675s1560m675s520" - "m675s19950" - "m675s520m675s520m675s520m675s520m675s520m675s520m675s520m675s520" - "m675s520m675s520m675s520m675s520m675s520m675s520m675s520m675s520" - "m675s520m675s520m675s520m675s520m675s520m675s520m675s520m675s520" - "m675s520m675s520m675s520m675s520m675s520m675s1560m675s1560m675s1560" - "m675s39900" - "m8990s4490" - "m675s1560m675s520m675s520m675s1560m675s1560m675s520m675s520m675s520" - "m675s1560m675s1560m675s520m675s1560m675s520m675s520m675s520m675s520" - "m675s520m675s520m675s520m675s520m675s520m675s520m675s520m675s1560" - "m675s520m675s520m675s520m675s520m675s1560m675s1560m675s1560m675s520" - "m675s520m675s1560m675s520" - "m675s19950" - "m675s520m675s520m675s520m675s520m675s520m675s520m675s520m675s520" - "m675s520m675s520m675s520m675s520m675s520m675s520m675s520m675s520" - "m675s520m675s520m675s520m675s520m675s1560m675s520m675s520m675s520" - "m675s520m675s520m675s520m675s520m675s1560m675s1560m675s1560m675s1560" - "m675s39900", irsend.outputStr()); + "m9010s4505" + "m680s1530m680s510m680s510m680s1530m680s1530m680s510m680s510m680s510" + "m680s1530m680s1530m680s510m680s1530m680s510m680s510m680s510m680s510" + "m680s510m680s510m680s510m680s510m680s510m680s510m680s510m680s1530" + "m680s510m680s510m680s510m680s510m680s1530m680s510m680s1530m680s510" + "m680s510m680s1530m680s510" + "m680s19975" + "m680s510m680s510m680s510m680s510m680s510m680s510m680s510m680s510" + "m680s510m680s510m680s510m680s510m680s510m680s510m680s510m680s510" + "m680s510m680s510m680s510m680s510m680s510m680s510m680s510m680s510" + "m680s510m680s510m680s510m680s510m680s510m680s1530m680s1530m680s1530" + "m680s39950" + "m9010s4505" + "m680s1530m680s510m680s510m680s1530m680s1530m680s510m680s510m680s510" + "m680s1530m680s1530m680s510m680s1530m680s510m680s510m680s510m680s510" + "m680s510m680s510m680s510m680s510m680s510m680s510m680s510m680s1530" + "m680s510m680s510m680s510m680s510m680s1530m680s1530m680s1530m680s510" + "m680s510m680s1530m680s510" + "m680s19975" + "m680s510m680s510m680s510m680s510m680s510m680s510m680s510m680s510" + "m680s510m680s510m680s510m680s510m680s510m680s510m680s510m680s510" + "m680s510m680s510m680s510m680s510m680s1530m680s510m680s510m680s510" + "m680s510m680s510m680s510m680s510m680s1530m680s1530m680s1530m680s1530" + "m680s39950" + "m9010s4505" + "m680s1530m680s510m680s510m680s1530m680s1530m680s510m680s510m680s510" + "m680s1530m680s1530m680s510m680s1530m680s510m680s510m680s510m680s510" + "m680s510m680s510m680s510m680s510m680s510m680s510m680s510m680s1530" + "m680s510m680s510m680s510m680s510m680s1530m680s510m680s1530m680s510" + "m680s510m680s1530m680s510" + "m680s19975" + "m680s510m680s510m680s510m680s510m680s510m680s510m680s510m680s510" + "m680s510m680s510m680s510m680s510m680s510m680s510m680s510m680s510" + "m680s510m680s510m680s510m680s510m680s510m680s510m680s510m680s510" + "m680s510m680s510m680s510m680s510m680s510m680s1530m680s1530m680s1530" + "m680s39950" + "m9010s4505" + "m680s1530m680s510m680s510m680s1530m680s1530m680s510m680s510m680s510" + "m680s1530m680s1530m680s510m680s1530m680s510m680s510m680s510m680s510" + "m680s510m680s510m680s510m680s510m680s510m680s510m680s510m680s1530" + "m680s510m680s510m680s510m680s510m680s1530m680s1530m680s1530m680s510" + "m680s510m680s1530m680s510" + "m680s19975" + "m680s510m680s510m680s510m680s510m680s510m680s510m680s510m680s510" + "m680s510m680s510m680s510m680s510m680s510m680s510m680s510m680s510" + "m680s510m680s510m680s510m680s510m680s1530m680s510m680s510m680s510" + "m680s510m680s510m680s510m680s510m680s1530m680s1530m680s1530m680s1530" + "m680s39950", irsend.outputStr()); } // Test sending atypical sizes. @@ -126,30 +129,30 @@ TEST(TestSendKelvinator, SendUnexpectedSizes) { // extra data. irsend.sendKelvinator(kelv_long_code, 17); ASSERT_EQ( - "m8990s4490" - "m675s1560m675s520m675s520m675s1560m675s1560m675s520m675s520m675s520" - "m675s1560m675s1560m675s520m675s1560m675s520m675s520m675s520m675s520" - "m675s520m675s520m675s520m675s520m675s520m675s520m675s520m675s1560" - "m675s520m675s520m675s520m675s520m675s1560m675s520m675s1560m675s520" - "m675s520m675s1560m675s520" - "m675s19950" - "m675s520m675s520m675s520m675s520m675s520m675s520m675s520m675s520" - "m675s520m675s520m675s520m675s520m675s520m675s520m675s520m675s520" - "m675s520m675s520m675s520m675s520m675s520m675s520m675s520m675s520" - "m675s520m675s520m675s520m675s520m675s520m675s1560m675s1560m675s1560" - "m675s39900" - "m8990s4490" - "m675s1560m675s520m675s520m675s1560m675s1560m675s520m675s520m675s520" - "m675s1560m675s1560m675s520m675s1560m675s520m675s520m675s520m675s520" - "m675s520m675s520m675s520m675s520m675s520m675s520m675s520m675s1560" - "m675s520m675s520m675s520m675s520m675s1560m675s1560m675s1560m675s520" - "m675s520m675s1560m675s520" - "m675s19950" - "m675s520m675s520m675s520m675s520m675s520m675s520m675s520m675s520" - "m675s520m675s520m675s520m675s520m675s520m675s520m675s520m675s520" - "m675s520m675s520m675s520m675s520m675s1560m675s520m675s520m675s520" - "m675s520m675s520m675s520m675s520m675s1560m675s1560m675s1560m675s1560" - "m675s39900", irsend.outputStr()); + "m9010s4505" + "m680s1530m680s510m680s510m680s1530m680s1530m680s510m680s510m680s510" + "m680s1530m680s1530m680s510m680s1530m680s510m680s510m680s510m680s510" + "m680s510m680s510m680s510m680s510m680s510m680s510m680s510m680s1530" + "m680s510m680s510m680s510m680s510m680s1530m680s510m680s1530m680s510" + "m680s510m680s1530m680s510" + "m680s19975" + "m680s510m680s510m680s510m680s510m680s510m680s510m680s510m680s510" + "m680s510m680s510m680s510m680s510m680s510m680s510m680s510m680s510" + "m680s510m680s510m680s510m680s510m680s510m680s510m680s510m680s510" + "m680s510m680s510m680s510m680s510m680s510m680s1530m680s1530m680s1530" + "m680s39950" + "m9010s4505" + "m680s1530m680s510m680s510m680s1530m680s1530m680s510m680s510m680s510" + "m680s1530m680s1530m680s510m680s1530m680s510m680s510m680s510m680s510" + "m680s510m680s510m680s510m680s510m680s510m680s510m680s510m680s1530" + "m680s510m680s510m680s510m680s510m680s1530m680s1530m680s1530m680s510" + "m680s510m680s1530m680s510" + "m680s19975" + "m680s510m680s510m680s510m680s510m680s510m680s510m680s510m680s510" + "m680s510m680s510m680s510m680s510m680s510m680s510m680s510m680s510" + "m680s510m680s510m680s510m680s510m680s1530m680s510m680s510m680s510" + "m680s510m680s510m680s510m680s510m680s1530m680s1530m680s1530m680s1530" + "m680s39950", irsend.outputStr()); } @@ -370,6 +373,66 @@ TEST(TestKelvinatorClass, FanSpeed) { EXPECT_EQ(3, irkelv.getFan()); } +TEST(TestKelvinatorClass, Checksums) { + uint8_t kelv_code[KELVINATOR_STATE_LENGTH] = { + 0x19, 0x0B, 0x80, 0x50, 0x00, 0x00, 0x00, 0xE0, + 0x19, 0x0B, 0x80, 0x70, 0x00, 0x00, 0x10, 0xf0}; + + EXPECT_TRUE(IRKelvinatorAC::validChecksum(kelv_code)); + // Change the array so the checksum is invalid. + kelv_code[0] ^= 0xFF; + EXPECT_FALSE(IRKelvinatorAC::validChecksum(kelv_code)); + // Restore the previous change, and change another byte. + kelv_code[0] ^= 0xFF; + kelv_code[4] ^= 0xFF; + EXPECT_FALSE(IRKelvinatorAC::validChecksum(kelv_code)); + kelv_code[4] ^= 0xFF; + // Change something in the 2nd block. + kelv_code[10] ^= 0xFF; + EXPECT_FALSE(IRKelvinatorAC::validChecksum(kelv_code)); + kelv_code[10] ^= 0xFF; + EXPECT_TRUE(IRKelvinatorAC::validChecksum(kelv_code)); +} + +TEST(TestKelvinatorClass, SetAndGetRaw) { + IRKelvinatorAC irkelv(0); + uint8_t initialState[KELVINATOR_STATE_LENGTH] = { + 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0xA0, + 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0xA0}; + uint8_t expectedState[KELVINATOR_STATE_LENGTH] = { + 0x08, 0x05, 0x20, 0x50, 0x00, 0x00, 0x00, 0x70, + 0x08, 0x05, 0x20, 0x70, 0x00, 0x00, 0x00, 0x70}; + + EXPECT_STATE_EQ(initialState, irkelv.getRaw(), KELVINATOR_BITS); + // toggle the power state. + irkelv.setPower(!irkelv.getPower()); + irkelv.setTemp(21); + irkelv.setLight(true); + EXPECT_STATE_EQ(expectedState, irkelv.getRaw(), KELVINATOR_BITS); + irkelv.setRaw(initialState); + EXPECT_STATE_EQ(initialState, irkelv.getRaw(), KELVINATOR_BITS); +} + +TEST(TestKelvinatorClass, HumanReadable) { + IRKelvinatorAC irkelv(0); + + EXPECT_EQ("Power: Off, Mode: 0 (AUTO), Temp: 16C, Fan: 0 (AUTO), Turbo: Off, " + "Quiet: Off, XFan: Off, IonFilter: Off, Light: Off, " + "Swing (Horizontal): Off, Swing (Vertical): Off", + irkelv.toString()); + irkelv.on(); + irkelv.setMode(KELVINATOR_COOL); + irkelv.setTemp(25); + irkelv.setFan(KELVINATOR_FAN_MAX); + irkelv.setXFan(true); + irkelv.setIonFilter(true); + irkelv.setLight(true); + irkelv.setSwingHorizontal(true); + EXPECT_EQ("Power: On, Mode: 1 (COOL), Temp: 25C, Fan: 5 (MAX), Turbo: Off, " + "Quiet: Off, XFan: On, IonFilter: On, Light: On, " + "Swing (Horizontal): On, Swing (Vertical): Off", + irkelv.toString()); +} TEST(TestKelvinatorClass, MessageConstuction) { IRKelvinatorAC irkelv(0); @@ -405,28 +468,46 @@ TEST(TestKelvinatorClass, MessageConstuction) { irsend.reset(); irsend.sendKelvinator(irkelv.getRaw()); EXPECT_EQ( - "m8990s4490" - "m675s1560m675s520m675s520m675s1560m675s1560m675s520m675s1560m675s520" - "m675s1560m675s1560m675s520m675s1560m675s520m675s520m675s520m675s520" - "m675s520m675s520m675s520m675s520m675s520m675s520m675s1560m675s1560" - "m675s520m675s520m675s520m675s520m675s1560m675s520m675s1560m675s520" - "m675s520m675s1560m675s520" - "m675s19950" - "m675s520m675s520m675s520m675s520m675s1560m675s520m675s520m675s520" - "m675s520m675s520m675s520m675s520m675s520m675s520m675s520m675s520" - "m675s520m675s520m675s520m675s520m675s520m675s520m675s520m675s520" - "m675s520m675s520m675s520m675s520m675s1560m675s1560m675s1560m675s1560" - "m675s39900" - "m8990s4490" - "m675s1560m675s520m675s520m675s1560m675s1560m675s520m675s1560m675s520" - "m675s1560m675s1560m675s520m675s1560m675s520m675s520m675s520m675s520" - "m675s520m675s520m675s520m675s520m675s520m675s520m675s1560m675s1560" - "m675s520m675s520m675s520m675s520m675s1560m675s1560m675s1560m675s520" - "m675s520m675s1560m675s520" - "m675s19950" - "m675s520m675s520m675s520m675s520m675s520m675s520m675s520m675s520" - "m675s520m675s520m675s520m675s520m675s520m675s520m675s520m675s520" - "m675s520m675s520m675s520m675s520m675s1560m675s520m675s520m675s520" - "m675s520m675s520m675s520m675s520m675s1560m675s1560m675s1560m675s1560" - "m675s39900", irsend.outputStr()); + "m9010s4505" + "m680s1530m680s510m680s510m680s1530m680s1530m680s510m680s1530m680s510" + "m680s1530m680s1530m680s510m680s1530m680s510m680s510m680s510m680s510" + "m680s510m680s510m680s510m680s510m680s510m680s510m680s1530m680s1530" + "m680s510m680s510m680s510m680s510m680s1530m680s510m680s1530m680s510" + "m680s510m680s1530m680s510" + "m680s19975" + "m680s510m680s510m680s510m680s510m680s1530m680s510m680s510m680s510" + "m680s510m680s510m680s510m680s510m680s510m680s510m680s510m680s510" + "m680s510m680s510m680s510m680s510m680s510m680s510m680s510m680s510" + "m680s510m680s510m680s510m680s510m680s1530m680s1530m680s1530m680s1530" + "m680s39950" + "m9010s4505" + "m680s1530m680s510m680s510m680s1530m680s1530m680s510m680s1530m680s510" + "m680s1530m680s1530m680s510m680s1530m680s510m680s510m680s510m680s510" + "m680s510m680s510m680s510m680s510m680s510m680s510m680s1530m680s1530" + "m680s510m680s510m680s510m680s510m680s1530m680s1530m680s1530m680s510" + "m680s510m680s1530m680s510" + "m680s19975" + "m680s510m680s510m680s510m680s510m680s510m680s510m680s510m680s510" + "m680s510m680s510m680s510m680s510m680s510m680s510m680s510m680s510" + "m680s510m680s510m680s510m680s510m680s1530m680s510m680s510m680s510" + "m680s510m680s510m680s510m680s510m680s1530m680s1530m680s1530m680s1530" + "m680s39950", irsend.outputStr()); +} + +// Decode a synthetic Kelvinator message. +TEST(TestDecodeKelvinator, NormalSynthetic) { + IRsendTest irsend(4); + IRrecv irrecv(4); + irsend.begin(); + + uint8_t kelv_code[KELVINATOR_STATE_LENGTH] = { + 0x19, 0x0B, 0x80, 0x50, 0x00, 0x00, 0x00, 0xE0, + 0x19, 0x0B, 0x80, 0x70, 0x00, 0x00, 0x10, 0xf0}; + irsend.reset(); + irsend.sendKelvinator(kelv_code); + irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(KELVINATOR, irsend.capture.decode_type); + ASSERT_EQ(KELVINATOR_BITS, irsend.capture.bits); + EXPECT_STATE_EQ(kelv_code, irsend.capture.state, KELVINATOR_BITS); } diff --git a/lib/IRremoteESP8266/test/ir_LG_test.cpp b/lib/IRremoteESP8266/test/ir_LG_test.cpp index fbdaf71..19a93c9 100644 --- a/lib/IRremoteESP8266/test/ir_LG_test.cpp +++ b/lib/IRremoteESP8266/test/ir_LG_test.cpp @@ -35,7 +35,7 @@ TEST(TestSendLG, SendDataOnly) { "m550s1600m550s550m550s1600m550s1600m550s550m550s1600m550s550m550s550" "m550s1600m550s550m550s1600m550s550m550s1600m550s1600m550s1600m550s550" "m550s550m550s1600m550s550m550s1600m550s550m550s550m550s550m550s1600" - "m550s108050", irsend.outputStr()); + "m550s51050", irsend.outputStr()); irsend.reset(); irsend.sendLG(0xB4B4AE51, LG32_BITS); @@ -45,8 +45,8 @@ TEST(TestSendLG, SendDataOnly) { "m560s1680m560s560m560s1680m560s1680m560s560m560s1680m560s560m560s560" "m560s1680m560s560m560s1680m560s560m560s1680m560s1680m560s1680m560s560" "m560s560m560s1680m560s560m560s1680m560s560m560s560m560s560m560s1680" - "m560s108080" - "m8950s2250m550s108050", irsend.outputStr()); + "m560s44800" + "m8950s2250m550s96300", irsend.outputStr()); } // Test sending with different repeats. @@ -62,8 +62,8 @@ TEST(TestSendLG, SendWithRepeats) { "m550s1600m550s550m550s1600m550s1600m550s550m550s1600m550s550m550s550" "m550s1600m550s550m550s1600m550s550m550s1600m550s1600m550s1600m550s550" "m550s550m550s1600m550s550m550s1600m550s550m550s550m550s550m550s1600" - "m550s108050" - "m8000s2250m550s108050", irsend.outputStr()); + "m550s51050" + "m8000s2250m550s97250", irsend.outputStr()); irsend.reset(); irsend.sendLG(0xB4B4AE51, LG32_BITS, 1); @@ -73,13 +73,13 @@ TEST(TestSendLG, SendWithRepeats) { "m560s1680m560s560m560s1680m560s1680m560s560m560s1680m560s560m560s560" "m560s1680m560s560m560s1680m560s560m560s1680m560s1680m560s1680m560s560" "m560s560m560s1680m560s560m560s1680m560s560m560s560m560s560m560s1680" - "m560s108080" - "m8950s2250m550s108050" - "m8950s2250m550s108050", irsend.outputStr()); + "m560s44800" + "m8950s2250m550s96300" + "m8950s2250m550s96300", irsend.outputStr()); } // Test sending an atypical data size. -TEST(TestSendLG, SendUsualSize) { +TEST(TestSendLG, SendUnusualSize) { IRsendTest irsend(4); irsend.begin(); @@ -91,7 +91,7 @@ TEST(TestSendLG, SendUsualSize) { "m550s550m550s550m550s550m550s550m550s550m550s550m550s550m550s550" "m550s550m550s550m550s550m550s550m550s550m550s550m550s550m550s550" "m550s550m550s550m550s550m550s550m550s550m550s550m550s550" - "m550s108050", irsend.outputStr()); + "m550s61400", irsend.outputStr()); irsend.reset(); irsend.sendLG(0x0, 64); @@ -105,8 +105,8 @@ TEST(TestSendLG, SendUsualSize) { "m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560" "m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560" "m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560" - "m560s108080" - "m8950s2250m550s108050", irsend.outputStr()); + "m560s26880" + "m8950s2250m550s96300", irsend.outputStr()); } // Tests for encodeLG(). diff --git a/lib/IRremoteESP8266/test/ir_Lasertag_test.cpp b/lib/IRremoteESP8266/test/ir_Lasertag_test.cpp new file mode 100644 index 0000000..8bcb88f --- /dev/null +++ b/lib/IRremoteESP8266/test/ir_Lasertag_test.cpp @@ -0,0 +1,341 @@ +// Copyright 2017 David Conran + +#include "IRsend.h" +#include "IRsend_test.h" +#include "gtest/gtest.h" + +// LL AAA SSSSS EEEEEEE RRRRRR TTTTTTT AAA GGGG +// LL AAAAA SS EE RR RR TTT AAAAA GG GG +// LL AA AA SSSSS EEEEE RRRRRR TTT AA AA GG +// LL AAAAAAA SS EE RR RR TTT AAAAAAA GG GG +// LLLLLLL AA AA SSSSS EEEEEEE RR RR TTT AA AA GGGGGG + + +// Tests for sendLasertag(). + +// Test sending simplest case data only. +TEST(TestSendLasertag, SendDataOnly) { + IRsendTest irsend(4); + irsend.begin(); + + irsend.reset(); + irsend.sendLasertag(0x1); // Red 1 + EXPECT_EQ( + "m333s333m333s333m333s333m333s333m333s333m333s333m333s333m333s333m333" + "s333m333s333m333s333m333s666m333s100000", irsend.outputStr()); + + irsend.reset(); + irsend.sendLasertag(0x2); // Red 2 + EXPECT_EQ( + "m333s333m333s333m333s333m333s333m333s333m333s333m333s333" + "m333s333m333s333m333s333m333s666m666s100333", irsend.outputStr()); + + irsend.reset(); + irsend.sendLasertag(0x51); // Green 1 + // Raw: (21) + // m364s364m332s336m384s276m332s364m332s304m416s584m692s724m640s360m304s332m392s612m380, + EXPECT_EQ( + // m364s364m332s336m384s276m332s364m332s304m416s584 + // m692s724m640s360m304s332m392s612m380 + "m333s333m333s333m333s333m333s333m333s333m333s666" + "m666s666m666s333m333s333m333s666m333s100000", irsend.outputStr()); + + irsend.reset(); + // Raw: (19) + // m332s308m412s280m360s336m332s304m444s248m332s644m744s612m696s692m668s636m360 + irsend.sendLasertag(0x55); // Green 5 + EXPECT_EQ( + // m332s308m412s280m360s336m332s304m444s248m332s644 + // m744s612m696s692m668s636m360 + "m333s333m333s333m333s333m333s333m333s333m333s666" + "m666s666m666s666m666s666m333s100000", + irsend.outputStr()); +} + +TEST(TestSendLasertag, SendDataWithRepeat) { + IRsendTest irsend(4); + irsend.begin(); + + irsend.reset(); + irsend.sendLasertag(0x1, LASERTAG_BITS, 1); // Red 1, one repeat. + EXPECT_EQ( + "m333s333m333s333m333s333m333s333m333s333m333s333m333s333m333s333" + "m333s333m333s333m333s333m333s666m333s100000" + "m333s333m333s333m333s333m333s333m333s333m333s333m333s333m333s333" + "m333s333m333s333m333s333m333s666m333s100000", irsend.outputStr()); + + irsend.reset(); + irsend.sendLasertag(0x52, LASERTAG_BITS, 2); // Green 2, two repeats. + EXPECT_EQ( + "m333s333m333s333m333s333m333s333m333s333m333s666m666s666m666s333" + "m333s666m666s100333" + "m333s333m333s333m333s333m333s333m333s333m333s666m666s666m666s333" + "m333s666m666s100333" + "m333s333m333s333m333s333m333s333m333s333m333s666m666s666m666s333" + "m333s666m666s100333", irsend.outputStr()); +} + +TEST(TestSendLasertag, SmallestMessageSize) { + IRsendTest irsend(4); + irsend.begin(); + + 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", + irsend.outputStr()); +} + +// Tests for decodeLasertag(). + +// Decode normal Lasertag messages. +TEST(TestDecodeLasertag, NormalSyntheticDecodeWithStrict) { + IRsendTest irsend(0); + IRrecv irrecv(0); + irsend.begin(); + + // Normal Lasertag 13-bit message. + irsend.reset(); + irsend.sendLasertag(0x01); // Red 1 + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(LASERTAG, irsend.capture.decode_type); + EXPECT_EQ(LASERTAG_BITS, irsend.capture.bits); + EXPECT_EQ(0x01, irsend.capture.value); + EXPECT_EQ(0x1, irsend.capture.address); // Unit 1 + EXPECT_EQ(0x0, irsend.capture.command); // Team Red + EXPECT_FALSE(irsend.capture.repeat); + + irsend.reset(); + irsend.sendLasertag(0x02); // Red 2 + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(LASERTAG, irsend.capture.decode_type); + EXPECT_EQ(LASERTAG_BITS, irsend.capture.bits); + EXPECT_EQ(0x02, irsend.capture.value); + EXPECT_EQ(0x2, irsend.capture.address); // Unit 2 + EXPECT_EQ(0x0, irsend.capture.command); // Team Red + EXPECT_FALSE(irsend.capture.repeat); + + irsend.reset(); + irsend.sendLasertag(0x06); // Red 6 + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(LASERTAG, irsend.capture.decode_type); + EXPECT_EQ(LASERTAG_BITS, irsend.capture.bits); + EXPECT_EQ(0x06, irsend.capture.value); + EXPECT_EQ(0x6, irsend.capture.address); // Unit 6 + EXPECT_EQ(0x0, irsend.capture.command); // Team Red + EXPECT_FALSE(irsend.capture.repeat); + + irsend.reset(); + irsend.sendLasertag(0x51); // Green 1 + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(LASERTAG, irsend.capture.decode_type); + EXPECT_EQ(LASERTAG_BITS, irsend.capture.bits); + EXPECT_EQ(0x51, irsend.capture.value); + EXPECT_EQ(0x1, irsend.capture.address); // Unit 1 + EXPECT_EQ(0x5, irsend.capture.command); // Team Green + EXPECT_FALSE(irsend.capture.repeat); + + irsend.reset(); + irsend.sendLasertag(0x56); // Green 6 + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(LASERTAG, irsend.capture.decode_type); + EXPECT_EQ(LASERTAG_BITS, irsend.capture.bits); + EXPECT_EQ(0x56, irsend.capture.value); + EXPECT_EQ(0x6, irsend.capture.address); // Unit + EXPECT_EQ(0x5, irsend.capture.command); // Team + EXPECT_FALSE(irsend.capture.repeat); +} + +// Example data taken from: https://github.com/z3t0/Arduino-IRremote/issues/532 +TEST(TestDecodeLasertag, RealExamples) { + IRsendTest irsend(0); + IRrecv irrecv(0); + irsend.begin(); + + irsend.reset(); + uint16_t green3[21] = { + 360, 364, 272, 360, 420, 248, 360, 360, 332, 308, 388, 612, 692, 696, + 636, 360, 332, 700, 300, 308, 416}; + irsend.sendRaw(green3, 21, 36000); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(LASERTAG, irsend.capture.decode_type); + EXPECT_EQ(LASERTAG_BITS, irsend.capture.bits); + EXPECT_EQ(0x53, irsend.capture.value); + EXPECT_EQ(0x3, irsend.capture.address); // Unit + EXPECT_EQ(0x5, irsend.capture.command); // Team + + irsend.reset(); + uint16_t green1[21] = { + 364, 364, 332, 336, 384, 276, 332, 364, 332, 304, 416, 584, 692, 724, + 640, 360, 304, 332, 392, 612, 380}; + irsend.sendRaw(green1, 21, 36000); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(LASERTAG, irsend.capture.decode_type); + EXPECT_EQ(LASERTAG_BITS, irsend.capture.bits); + EXPECT_EQ(0x51, irsend.capture.value); + EXPECT_EQ(0x1, irsend.capture.address); // Unit + EXPECT_EQ(0x5, irsend.capture.command); // Team + + irsend.reset(); + uint16_t green4[19] = { + 336, 304, 412, 280, 360, 360, 304, 308, 420, 276, 332, 636, 744, 620, + 688, 724, 640, 360, 304}; + irsend.sendRaw(green4, 19, 36000); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(LASERTAG, irsend.capture.decode_type); + EXPECT_EQ(LASERTAG_BITS, irsend.capture.bits); + EXPECT_EQ(0x54, irsend.capture.value); + EXPECT_EQ(0x4, irsend.capture.address); // Unit + EXPECT_EQ(0x5, irsend.capture.command); // Team + + irsend.reset(); + uint16_t unit15[25] = { + 280, 360, 360, 308, 332, 388, 308, 332, 360, 308, 360, 360, 304, 304, + 412, 284, 304, 692, 364, 360, 276, 336, 416, 276, 328}; + irsend.sendRaw(unit15, 25, 36000); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(LASERTAG, irsend.capture.decode_type); + EXPECT_EQ(LASERTAG_BITS, irsend.capture.bits); + EXPECT_EQ(0x0F, irsend.capture.value); + EXPECT_EQ(0xF, irsend.capture.address); // Unit + EXPECT_EQ(0x0, irsend.capture.command); // Team + + irsend.reset(); + uint16_t red_unit2_1[23] = { + 406, 262, 384, 374, 256, 354, 306, 366, 252, 442, 256, 374, + 358, 336, 278, 438, 246, 340, 380, 292, 304, 688, 746}; + irsend.sendRaw(red_unit2_1, 23, 36000); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(LASERTAG, irsend.capture.decode_type); + EXPECT_EQ(LASERTAG_BITS, irsend.capture.bits); + EXPECT_EQ(0x02, irsend.capture.value); + EXPECT_EQ(0x2, irsend.capture.address); // Unit + EXPECT_EQ(0x0, irsend.capture.command); // Team + + irsend.reset(); + uint16_t red_unit2_2[23] = { + 302, 306, 302, 392, 196, 476, 278, 352, 304, 348, 278, 438, + 226, 382, 328, 366, 252, 458, 196, 392, 302, 688, 644}; + irsend.sendRaw(red_unit2_2, 23, 36000); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(LASERTAG, irsend.capture.decode_type); + EXPECT_EQ(LASERTAG_BITS, irsend.capture.bits); + EXPECT_EQ(0x02, irsend.capture.value); + EXPECT_EQ(0x2, irsend.capture.address); // Unit + EXPECT_EQ(0x0, irsend.capture.command); // Team + + irsend.reset(); + uint16_t red_unit2_3[23] = { + 196, 432, 304, 348, 328, 386, 304, 326, 302, 370, 252, 442, + 272, 356, 278, 374, 276, 438, 274, 352, 302, 668, 622}; + irsend.sendRaw(red_unit2_3, 23, 36000); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(LASERTAG, irsend.capture.decode_type); + EXPECT_EQ(LASERTAG_BITS, irsend.capture.bits); + EXPECT_EQ(0x02, irsend.capture.value); + EXPECT_EQ(0x2, irsend.capture.address); // Unit + EXPECT_EQ(0x0, irsend.capture.command); // Team + + irsend.reset(); + uint16_t red_unit2_4[23] = { + 304, 390, 328, 324, 324, 346, 350, 364, 300, 330, 320, 310, + 324, 388, 242, 366, 354, 318, 354, 340, 244, 726, 670}; + irsend.sendRaw(red_unit2_4, 23, 36000); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(LASERTAG, irsend.capture.decode_type); + EXPECT_EQ(LASERTAG_BITS, irsend.capture.bits); + EXPECT_EQ(0x02, irsend.capture.value); + EXPECT_EQ(0x2, irsend.capture.address); // Unit + EXPECT_EQ(0x0, irsend.capture.command); // Team + + irsend.reset(); + uint16_t red_unit1_1_issue532[25] = { + 368, 352, 336, 308, 388, 276, 364, 356, 280, 360, 332, 336, 360, 360, 308, + 300, 416, 280, 356, 360, 312, 328, 336, 636, 424}; + irsend.sendRaw(red_unit1_1_issue532, 25, 36000); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(LASERTAG, irsend.capture.decode_type); + EXPECT_EQ(LASERTAG_BITS, irsend.capture.bits); + EXPECT_EQ(0x01, irsend.capture.value); + EXPECT_EQ(0x1, irsend.capture.address); // Unit + EXPECT_EQ(0x0, irsend.capture.command); // Team + + irsend.reset(); + uint16_t red_unit1_2_issue532[25] = { + 328, 400, 272, 360, 388, 280, 360, 364, 272, 364, 332, 336, 332, 388, 304, + 308, 388, 280, 356, 364, 272, 368, 384, 612, 408}; + irsend.sendRaw(red_unit1_2_issue532, 25, 36000); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(LASERTAG, irsend.capture.decode_type); + EXPECT_EQ(LASERTAG_BITS, irsend.capture.bits); + EXPECT_EQ(0x01, irsend.capture.value); + EXPECT_EQ(0x1, irsend.capture.address); // Unit + EXPECT_EQ(0x0, irsend.capture.command); // Team + + irsend.reset(); + uint16_t red_unit1_3_issue532[25] = { + 416, 284, 356, 336, 328, 336, 384, 308, 328, 368, 304, 308, 412, 280, 328, + 368, 272, 368, 376, 312, 332, 392, 276, 700, 272}; + irsend.sendRaw(red_unit1_3_issue532, 25, 36000); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(LASERTAG, irsend.capture.decode_type); + EXPECT_EQ(LASERTAG_BITS, irsend.capture.bits); + EXPECT_EQ(0x01, irsend.capture.value); + EXPECT_EQ(0x1, irsend.capture.address); // Unit + EXPECT_EQ(0x0, irsend.capture.command); // Team + + irsend.reset(); + uint16_t red_unit2_1_issue532[23] = { + 308, 340, 408, 284, 332, 388, 276, 336, 356, 340, 332, 360, 300, 364, 360, + 304, 280, 444, 276, 336, 384, 640, 696}; + irsend.sendRaw(red_unit2_1_issue532, 23, 36000); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(LASERTAG, irsend.capture.decode_type); + EXPECT_EQ(LASERTAG_BITS, irsend.capture.bits); + EXPECT_EQ(0x02, irsend.capture.value); + EXPECT_EQ(0x2, irsend.capture.address); // Unit + EXPECT_EQ(0x0, irsend.capture.command); // Team + + irsend.reset(); + uint16_t red_unit2_2_issue532[23] = { + 332, 308, 388, 280, 328, 420, 308, 304, 384, 308, 332, 364, 272, 368, 384, + 276, 364, 360, 308, 332, 384, 612, 696}; + irsend.sendRaw(red_unit2_2_issue532, 23, 36000); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(LASERTAG, irsend.capture.decode_type); + EXPECT_EQ(LASERTAG_BITS, irsend.capture.bits); + EXPECT_EQ(0x02, irsend.capture.value); + EXPECT_EQ(0x2, irsend.capture.address); // Unit + EXPECT_EQ(0x0, irsend.capture.command); // Team + + irsend.reset(); + uint16_t red_unit2_3_issue532[23] = { + 392, 332, 340, 272, 448, 276, 364, 328, 340, 272, 396, 296, 340, 380, 312, + 296, 400, 272, 364, 352, 284, 720, 672}; + irsend.sendRaw(red_unit2_3_issue532, 23, 36000); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(LASERTAG, irsend.capture.decode_type); + EXPECT_EQ(LASERTAG_BITS, irsend.capture.bits); + EXPECT_EQ(0x02, irsend.capture.value); + EXPECT_EQ(0x2, irsend.capture.address); // Unit + EXPECT_EQ(0x0, irsend.capture.command); // Team +} diff --git a/lib/IRremoteESP8266/test/ir_Magiquest_test.cpp b/lib/IRremoteESP8266/test/ir_Magiquest_test.cpp new file mode 100644 index 0000000..ef5ede8 --- /dev/null +++ b/lib/IRremoteESP8266/test/ir_Magiquest_test.cpp @@ -0,0 +1,161 @@ +// Copyright 2017 David Conran + +#include "IRrecv.h" +#include "IRsend.h" +#include "IRsend_test.h" +#include "ir_Magiquest.h" +#include "gtest/gtest.h" + +// Tests for encodeMagiQuest() +TEST(TestEncodeMagiQuest, General) { + IRsendTest irsend(0); + EXPECT_EQ(0x0, irsend.encodeMagiQuest(0x0, 0x0)); + EXPECT_EQ(0x10001, irsend.encodeMagiQuest(0x1, 0x1)); + EXPECT_EQ(0x20003, irsend.encodeMagiQuest(0x2, 0x3)); + EXPECT_EQ(0x123456781234, irsend.encodeMagiQuest(0x12345678, 0x1234)); + EXPECT_EQ(0xFFFFFFFFFFFF, irsend.encodeMagiQuest(0xFFFFFFFF, 0xFFFF)); +} + +// Tests for sendMagiQuest() + +// Test sending typical data only. +TEST(TestSendMagiQuest, SendDataOnly) { + IRsendTest irsend(0); + irsend.begin(); + + irsend.reset(); + irsend.sendMagiQuest(0x0); + EXPECT_EQ( + "m280s850m280s850m280s850m280s850m280s850m280s850m280s850m280s850" + "m280s850m280s850m280s850m280s850m280s850m280s850m280s850m280s850" + "m280s850m280s850m280s850m280s850m280s850m280s850m280s850m280s850" + "m280s850m280s850m280s850m280s850m280s850m280s850m280s850m280s850" + "m280s850m280s850m280s850m280s850m280s850m280s850m280s850m280s850" + "m280s850m280s850m280s850m280s850m280s850m280s850m280s850m280s850" + "m280s850m280s850m280s850m280s850m280s850m280s850m280s850m280s100850", + irsend.outputStr()); + irsend.reset(); + irsend.sendMagiQuest(0x123456789ABC); + EXPECT_EQ( + "m280s850m280s850m280s850m280s850m280s850m280s850m280s850m280s850" + "m280s850m280s850m280s850m580s600m280s850m280s850m580s600m280s850" + "m280s850m280s850m580s600m580s600m280s850m580s600m280s850m280s850" + "m280s850m580s600m280s850m580s600m280s850m580s600m580s600m280s850" + "m280s850m580s600m580s600m580s600m580s600m280s850m280s850m280s850" + "m580s600m280s850m280s850m580s600m580s600m280s850m580s600m280s850" + "m580s600m280s850m580s600m580s600m580s600m580s600m280s850m280s100850", + irsend.outputStr()); +} + +// Test sending typical data only. +TEST(TestSendMagiQuest, SendWithRepeats) { + IRsendTest irsend(0); + irsend.begin(); + + irsend.reset(); + irsend.sendMagiQuest(0x12345678ABCD, MAGIQUEST_BITS, 2); // two repeats. + EXPECT_EQ( + "m280s850m280s850m280s850m280s850m280s850m280s850m280s850m280s850" + "m280s850m280s850m280s850m580s600m280s850m280s850m580s600m280s850" + "m280s850m280s850m580s600m580s600m280s850m580s600m280s850m280s850" + "m280s850m580s600m280s850m580s600m280s850m580s600m580s600m280s850" + "m280s850m580s600m580s600m580s600m580s600m280s850m280s850m280s850" + "m580s600m280s850m580s600m280s850m580s600m280s850m580s600m580s600" + "m580s600m580s600m280s850m280s850m580s600m580s600m280s850m580s100600" + "m280s850m280s850m280s850m280s850m280s850m280s850m280s850m280s850" + "m280s850m280s850m280s850m580s600m280s850m280s850m580s600m280s850" + "m280s850m280s850m580s600m580s600m280s850m580s600m280s850m280s850" + "m280s850m580s600m280s850m580s600m280s850m580s600m580s600m280s850" + "m280s850m580s600m580s600m580s600m580s600m280s850m280s850m280s850" + "m580s600m280s850m580s600m280s850m580s600m280s850m580s600m580s600" + "m580s600m580s600m280s850m280s850m580s600m580s600m280s850m580s100600" + "m280s850m280s850m280s850m280s850m280s850m280s850m280s850m280s850" + "m280s850m280s850m280s850m580s600m280s850m280s850m580s600m280s850" + "m280s850m280s850m580s600m580s600m280s850m580s600m280s850m280s850" + "m280s850m580s600m280s850m580s600m280s850m580s600m580s600m280s850" + "m280s850m580s600m580s600m580s600m580s600m280s850m280s850m280s850" + "m580s600m280s850m580s600m280s850m580s600m280s850m580s600m580s600" + "m580s600m580s600m280s850m280s850m580s600m580s600m280s850m580s100600", + irsend.outputStr()); +} + +// Tests for decodeMagiQuest(). + +// Decode normal "synthetic" messages. +TEST(TestDecodeMagiQuest, NormalDecodeWithStrict) { + IRsendTest irsend(0); + IRrecv irrecv(0); + irsend.begin(); + + irsend.reset(); + irsend.sendMagiQuest(0x0); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decodeMagiQuest(&irsend.capture, MAGIQUEST_BITS, true)); + EXPECT_EQ(MAGIQUEST, irsend.capture.decode_type); + EXPECT_EQ(MAGIQUEST_BITS, irsend.capture.bits); + EXPECT_EQ(0x0, irsend.capture.value); + EXPECT_EQ(0x0, irsend.capture.address); + EXPECT_EQ(0x0, irsend.capture.command); + EXPECT_FALSE(irsend.capture.repeat); + + irsend.reset(); + irsend.sendMagiQuest(irsend.encodeMagiQuest(0x1, 0x1)); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decodeMagiQuest(&irsend.capture, MAGIQUEST_BITS, true)); + EXPECT_EQ(MAGIQUEST, irsend.capture.decode_type); + EXPECT_EQ(MAGIQUEST_BITS, irsend.capture.bits); + EXPECT_EQ(0x10001, irsend.capture.value); + EXPECT_EQ(0x1, irsend.capture.address); + EXPECT_EQ(0x1, irsend.capture.command); + EXPECT_FALSE(irsend.capture.repeat); + + irsend.reset(); + irsend.sendMagiQuest(irsend.encodeMagiQuest(0x12345678, 0xABCD)); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decodeMagiQuest(&irsend.capture, MAGIQUEST_BITS, true)); + EXPECT_EQ(MAGIQUEST, irsend.capture.decode_type); + EXPECT_EQ(MAGIQUEST_BITS, irsend.capture.bits); + EXPECT_EQ(0x12345678ABCD, irsend.capture.value); + EXPECT_EQ(0x12345678, irsend.capture.address); + EXPECT_EQ(0xABCD, irsend.capture.command); + EXPECT_FALSE(irsend.capture.repeat); + + // Do the last one again, & use the full decoder, not just protocol specific. + irsend.reset(); + irsend.sendMagiQuest(irsend.encodeMagiQuest(0x12345678, 0xABCD)); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(MAGIQUEST, irsend.capture.decode_type); + EXPECT_EQ(MAGIQUEST_BITS, irsend.capture.bits); + EXPECT_EQ(0x12345678ABCD, irsend.capture.value); +} + +// Decode a "real" example message. +TEST(TestDecodeMagiQuest, RealExamples) { + IRsendTest irsend(0); + IRrecv irrecv(0); + irsend.begin(); + + irsend.reset(); + uint16_t rawData[111] = { + 262, 842, 298, 858, 238, 866, 240, 890, 238, 866, 240, 862, + 244, 886, 242, 860, 246, 858, 554, 604, 264, 840, 560, 622, + 266, 836, 552, 604, 556, 626, 262, 866, 240, 864, 264, 838, + 268, 862, 244, 886, 536, 620, 530, 628, 554, 628, 532, 624, + 244, 858, 552, 604, 264, 840, 268, 862, 266, 838, 268, 888, + 240, 864, 242, 860, 268, 862, 244, 860, 236, 868, 272, 832, + 264, 866, 240, 890, 532, 598, 268, 834, 294, 836, 270, 834, + 262, 866, 272, 858, 238, 866, 534, 622, 268, 836, 270, 860, + 268, 862, 526, 604, 264, 892, 530, 600, 268, 836, 586, 598, + 270, 834, 556}; // Data from PR #365 captured by coolacid + + irsend.sendRaw(rawData, 111, 36000); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(MAGIQUEST, irsend.capture.decode_type); + EXPECT_EQ(MAGIQUEST_BITS, irsend.capture.bits); + EXPECT_EQ(0x560F40020455, irsend.capture.value); + EXPECT_EQ(0x560F4002, irsend.capture.address); // Wand ID + EXPECT_EQ(0x0455, irsend.capture.command); // Magnitude + EXPECT_FALSE(irsend.capture.repeat); +} diff --git a/lib/IRremoteESP8266/test/ir_Midea_test.cpp b/lib/IRremoteESP8266/test/ir_Midea_test.cpp new file mode 100644 index 0000000..9c17967 --- /dev/null +++ b/lib/IRremoteESP8266/test/ir_Midea_test.cpp @@ -0,0 +1,640 @@ +// Copyright 2017 David Conran + +#include "IRsend.h" +#include "IRsend_test.h" +#include "ir_Midea.h" +#include "gtest/gtest.h" + +// Tests for sendMidea(). + +// Test sending typical data only. +TEST(TestSendMidea, SendDataOnly) { + IRsendTest irsend(4); + irsend.begin(); + + irsend.reset(); + irsend.sendMidea(0x0); + EXPECT_EQ( + "m4480s4480" + "m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560" + "m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560" + "m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560" + "m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560" + "m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560" + "m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560" + "m560s5600" + "m4480s4480" + "m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680" + "m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680" + "m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680" + "m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680" + "m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680" + "m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680" + "m560s5600", irsend.outputStr()); + + irsend.reset(); + irsend.sendMidea(0x55AA55AA55AA); + EXPECT_EQ( + "m4480s4480" + "m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680" + "m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560" + "m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680" + "m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560" + "m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680" + "m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560" + "m560s5600" + "m4480s4480" + "m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560" + "m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680" + "m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560" + "m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680" + "m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560" + "m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680" + "m560s5600", irsend.outputStr()); + + irsend.reset(); + irsend.sendMidea(0xFFFFFFFFFFFF); + EXPECT_EQ( + "m4480s4480" + "m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680" + "m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680" + "m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680" + "m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680" + "m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680" + "m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680" + "m560s5600" + "m4480s4480" + "m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560" + "m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560" + "m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560" + "m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560" + "m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560" + "m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560" + "m560s5600", irsend.outputStr()); +} + +// Test sending with different repeats. +TEST(TestSendMidea, SendWithRepeats) { + IRsendTest irsend(4); + irsend.begin(); + + irsend.reset(); + irsend.sendMidea(0x55AA55AA55AA, MIDEA_BITS, 1); // 1 repeat. + EXPECT_EQ( + "m4480s4480" + "m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680" + "m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560" + "m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680" + "m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560" + "m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680" + "m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560" + "m560s5600" + "m4480s4480" + "m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560" + "m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680" + "m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560" + "m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680" + "m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560" + "m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680" + "m560s5600" + "m4480s4480" + "m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680" + "m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560" + "m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680" + "m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560" + "m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680" + "m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560" + "m560s5600" + "m4480s4480" + "m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560" + "m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680" + "m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560" + "m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680" + "m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560" + "m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680" + "m560s5600", irsend.outputStr()); + irsend.sendMidea(0x55AA55AA55AA, MIDEA_BITS, 2); // 2 repeats. + EXPECT_EQ( + "m4480s4480" + "m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680" + "m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560" + "m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680" + "m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560" + "m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680" + "m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560" + "m560s5600" + "m4480s4480" + "m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560" + "m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680" + "m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560" + "m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680" + "m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560" + "m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680" + "m560s5600" + "m4480s4480" + "m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680" + "m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560" + "m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680" + "m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560" + "m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680" + "m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560" + "m560s5600" + "m4480s4480" + "m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560" + "m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680" + "m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560" + "m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680" + "m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560" + "m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680" + "m560s5600" + "m4480s4480" + "m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680" + "m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560" + "m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680" + "m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560" + "m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680" + "m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560" + "m560s5600" + "m4480s4480" + "m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560" + "m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680" + "m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560" + "m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680" + "m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560" + "m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680" + "m560s5600", irsend.outputStr()); +} + +// Test sending an atypical data size. +TEST(TestSendMidea, SendUnusualSize) { + IRsendTest irsend(4); + irsend.begin(); + + irsend.reset(); + irsend.sendMidea(0x0, 8); + EXPECT_EQ( + "m4480s4480" + "m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560" + "m560s5600" + "m4480s4480" + "m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680" + "m560s5600", irsend.outputStr()); + + irsend.reset(); + irsend.sendMidea(0x1234567890ABCDEF, 64); + EXPECT_EQ( + "m4480s4480" + "m560s560m560s560m560s560m560s1680m560s560m560s560m560s1680m560s560" + "m560s560m560s560m560s1680m560s1680m560s560m560s1680m560s560m560s560" + "m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s1680m560s560" + "m560s560m560s1680m560s1680m560s1680m560s1680m560s560m560s560m560s560" + "m560s1680m560s560m560s560m560s1680m560s560m560s560m560s560m560s560" + "m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s1680" + "m560s1680m560s1680m560s560m560s560m560s1680m560s1680m560s560m560s1680" + "m560s1680m560s1680m560s1680m560s560m560s1680m560s1680m560s1680m560s1680" + "m560s5600" + "m4480s4480" + "m560s1680m560s1680m560s1680m560s560m560s1680m560s1680m560s560m560s1680" + "m560s1680m560s1680m560s560m560s560m560s1680m560s560m560s1680m560s1680" + "m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s560m560s1680" + "m560s1680m560s560m560s560m560s560m560s560m560s1680m560s1680m560s1680" + "m560s560m560s1680m560s1680m560s560m560s1680m560s1680m560s1680m560s1680" + "m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s560" + "m560s560m560s560m560s1680m560s1680m560s560m560s560m560s1680m560s560" + "m560s560m560s560m560s560m560s1680m560s560m560s560m560s560m560s560" + "m560s5600", irsend.outputStr()); + + // Bit sizes must be a multiple of 8. + irsend.reset(); + irsend.sendMidea(0x0, 17); + EXPECT_EQ("" , irsend.outputStr()); +} + +// Tests for IRMideaAC class. + +// Tests for controlling the power state. +TEST(TestMideaACClass, Power) { + IRMideaAC midea(0); + midea.begin(); + + midea.setRaw(0xA1026FFFFFE2); // Power off. + + midea.on(); + EXPECT_TRUE(midea.getPower()); + + EXPECT_EQ(0xA1826FFFFF62, midea.getRaw()); + + midea.off(); + EXPECT_FALSE(midea.getPower()); + EXPECT_EQ(0xA1026FFFFFE2, midea.getRaw()); + + + midea.setPower(true); + EXPECT_TRUE(midea.getPower()); + EXPECT_EQ(0xA1826FFFFF62, midea.getRaw()); + + midea.setPower(false); + EXPECT_FALSE(midea.getPower()); + EXPECT_EQ(0xA1026FFFFFE2, midea.getRaw()); +} + +// Tests for the various Checksum routines. +TEST(TestMideaACClass, Checksums) { + IRMideaAC midea(0); + midea.begin(); + + // Known good states + EXPECT_EQ(0x62, IRMideaAC::calcChecksum(0xA1826FFFFF62)); + EXPECT_EQ(0x70, IRMideaAC::calcChecksum(0xA18177FFFF70)); + // Now without the checksum part. + EXPECT_EQ(0x62, IRMideaAC::calcChecksum(0xA1826FFFFF00)); + EXPECT_EQ(0x70, IRMideaAC::calcChecksum(0xA18177FFFF00)); + // Made up values. + EXPECT_EQ(0x00, IRMideaAC::calcChecksum(0x000000000000)); + EXPECT_EQ(0xDF, IRMideaAC::calcChecksum(0x1234567890AB)); + EXPECT_EQ(0xA0, IRMideaAC::calcChecksum(0xFFFFFFFFFFFF)); + // Larger than expected value (full 64bit) + EXPECT_EQ(0xDF, IRMideaAC::calcChecksum(0xFF1234567890AB)); + EXPECT_EQ(0xDF, IRMideaAC::calcChecksum(0x551234567890AB)); + + // Validity tests. + EXPECT_TRUE(IRMideaAC::validChecksum(0xA1826FFFFF62)); + EXPECT_TRUE(IRMideaAC::validChecksum(0xA18177FFFF70)); + EXPECT_FALSE(IRMideaAC::validChecksum(0x1234567890AB)); + + // Doing a setRaw() with a bad state should make a valid checksum on getRaw(). + midea.setRaw(0xA1826FFFFF00); + EXPECT_EQ(0xA1826FFFFF62, midea.getRaw()); +} + +TEST(TestMideaACClass, OperatingMode) { + IRMideaAC midea(0); + midea.begin(); + + midea.setRaw(0xA1826FFFFF62); // Auto mode already set. + midea.setMode(MIDEA_AC_AUTO); + EXPECT_EQ(MIDEA_AC_AUTO, midea.getMode()); + EXPECT_EQ(0xA1826FFFFF62, midea.getRaw()); // State shouldn't have changed. + + midea.setMode(MIDEA_AC_COOL); + EXPECT_EQ(MIDEA_AC_COOL, midea.getMode()); + EXPECT_EQ(0xA1806FFFFF61, midea.getRaw()); + + midea.setMode(MIDEA_AC_AUTO); + EXPECT_EQ(MIDEA_AC_AUTO, midea.getMode()); + EXPECT_EQ(0xA1826FFFFF62, midea.getRaw()); + + midea.setMode(MIDEA_AC_HEAT); + EXPECT_EQ(MIDEA_AC_HEAT, midea.getMode()); + EXPECT_EQ(0xA1836FFFFF63, midea.getRaw()); + + midea.setMode(MIDEA_AC_DRY); + EXPECT_EQ(MIDEA_AC_DRY, midea.getMode()); + EXPECT_EQ(0xA1816FFFFF60, midea.getRaw()); + + midea.setMode(MIDEA_AC_FAN); + EXPECT_EQ(MIDEA_AC_FAN, midea.getMode()); + EXPECT_EQ(0xA1846FFFFF66, midea.getRaw()); + + midea.setMode(255); + EXPECT_EQ(MIDEA_AC_AUTO, midea.getMode()); + EXPECT_EQ(0xA1826FFFFF62, midea.getRaw()); +} + +TEST(TestMideaACClass, FanSpeed) { + IRMideaAC midea(0); + midea.begin(); + + midea.setRaw(0xA1826FFFFF62); // Auto mode already set. + EXPECT_EQ(MIDEA_AC_FAN_AUTO, midea.getFan()); + + midea.setFan(MIDEA_AC_FAN_LOW); + EXPECT_EQ(MIDEA_AC_FAN_LOW, midea.getFan()); + EXPECT_EQ(0xA18A6FFFFF6C, midea.getRaw()); + + midea.setFan(255); // Setting an unexpected value defaults to auto. + EXPECT_EQ(MIDEA_AC_FAN_AUTO, midea.getFan()); + EXPECT_EQ(0xA1826FFFFF62, midea.getRaw()); + + midea.setFan(MIDEA_AC_FAN_MED); + EXPECT_EQ(MIDEA_AC_FAN_MED, midea.getFan()); + EXPECT_EQ(0xA1926FFFFF7C, midea.getRaw()); + + midea.setFan(MIDEA_AC_FAN_HI); + EXPECT_EQ(MIDEA_AC_FAN_HI, midea.getFan()); + EXPECT_EQ(0xA19A6FFFFF74, midea.getRaw()); + + midea.setFan(MIDEA_AC_FAN_AUTO); + EXPECT_EQ(MIDEA_AC_FAN_AUTO, midea.getFan()); + EXPECT_EQ(0xA1826FFFFF62, midea.getRaw()); +} + +TEST(TestMideaACClass, Temperature) { + IRMideaAC midea(0); + midea.begin(); + + midea.setRaw(0xA1826FFFFF62); // 77F / 25C + EXPECT_EQ(77, midea.getTemp()); // F + EXPECT_EQ(77, midea.getTemp(false)); // F + EXPECT_EQ(25, midea.getTemp(true)); // F + + midea.setTemp(0); + EXPECT_EQ(MIDEA_AC_MIN_TEMP_F, midea.getTemp()); + EXPECT_EQ(0xA18260FFFF6C, midea.getRaw()); + + midea.setTemp(255); + EXPECT_EQ(MIDEA_AC_MAX_TEMP_F, midea.getTemp()); + EXPECT_EQ(0xA18278FFFF78, midea.getRaw()); + + midea.setTemp(0, true); + EXPECT_EQ(MIDEA_AC_MIN_TEMP_F, midea.getTemp()); + EXPECT_EQ(0xA18260FFFF6C, midea.getRaw()); + + midea.setTemp(255, true); + EXPECT_EQ(MIDEA_AC_MAX_TEMP_F, midea.getTemp()); + EXPECT_EQ(0xA18278FFFF78, midea.getRaw()); + + // fahrenheit min/max etc. + midea.setTemp(MIDEA_AC_MIN_TEMP_F); + EXPECT_EQ(MIDEA_AC_MIN_TEMP_F, midea.getTemp()); + + midea.setTemp(MIDEA_AC_MAX_TEMP_F); + EXPECT_EQ(MIDEA_AC_MAX_TEMP_F, midea.getTemp()); + + midea.setTemp(MIDEA_AC_MIN_TEMP_F - 1); + EXPECT_EQ(MIDEA_AC_MIN_TEMP_F, midea.getTemp()); + + midea.setTemp(MIDEA_AC_MAX_TEMP_F + 1); + EXPECT_EQ(MIDEA_AC_MAX_TEMP_F, midea.getTemp()); + + // celsius min/max etc. + midea.setTemp(MIDEA_AC_MIN_TEMP_C, true); + EXPECT_EQ(MIDEA_AC_MIN_TEMP_C, midea.getTemp(true)); + EXPECT_EQ(MIDEA_AC_MIN_TEMP_F, midea.getTemp(false)); + + midea.setTemp(MIDEA_AC_MAX_TEMP_C, true); + EXPECT_EQ(MIDEA_AC_MAX_TEMP_C, midea.getTemp(true)); + EXPECT_EQ(MIDEA_AC_MAX_TEMP_F, midea.getTemp(false)); + + midea.setTemp(MIDEA_AC_MIN_TEMP_C - 1, true); + EXPECT_EQ(MIDEA_AC_MIN_TEMP_C, midea.getTemp(true)); + + midea.setTemp(MIDEA_AC_MAX_TEMP_C + 1, true); + EXPECT_EQ(MIDEA_AC_MAX_TEMP_C, midea.getTemp(true)); + EXPECT_EQ(MIDEA_AC_MAX_TEMP_F, midea.getTemp(false)); + + // General changes. + midea.setTemp(17, true); // C + EXPECT_EQ(17, midea.getTemp(true)); // C + EXPECT_EQ(63, midea.getTemp(false)); // F + + midea.setTemp(21, true); // C + EXPECT_EQ(21, midea.getTemp(true)); // C + EXPECT_EQ(70, midea.getTemp(false)); // F + + midea.setTemp(25, true); // C + EXPECT_EQ(25, midea.getTemp(true)); // C + EXPECT_EQ(77, midea.getTemp(false)); // F + + midea.setTemp(30, true); // C + EXPECT_EQ(30, midea.getTemp(true)); // C + EXPECT_EQ(86, midea.getTemp(false)); // F + + midea.setTemp(80, false); // F + EXPECT_EQ(26, midea.getTemp(true)); // C + EXPECT_EQ(80, midea.getTemp(false)); // F + + midea.setTemp(70); // F + EXPECT_EQ(21, midea.getTemp(true)); // C + EXPECT_EQ(70, midea.getTemp(false)); // F + EXPECT_EQ(70, midea.getTemp()); // F +} + +// Tests for controlling the sleep state. +TEST(TestMideaACClass, Sleep) { + IRMideaAC midea(0); + midea.begin(); + + midea.setRaw(0xA1826FFFFF62); // Sleep off. + + EXPECT_FALSE(midea.getSleep()); + midea.setSleep(true); + EXPECT_TRUE(midea.getSleep()); + EXPECT_EQ(0xA1C26FFFFF22, midea.getRaw()); + midea.setSleep(false); + EXPECT_FALSE(midea.getSleep()); + EXPECT_EQ(0xA1826FFFFF62, midea.getRaw()); +} + +TEST(TestMideaACClass, HumanReadableOutput) { + IRMideaAC midea(0); + midea.begin(); + + midea.setRaw(0xA1826FFFFF62); + EXPECT_EQ("Power: On, Mode: 2 (AUTO), Temp: 25C/77F, Fan: 0 (AUTO), " + "Sleep: Off", midea.toString()); + midea.off(); + midea.setTemp(25); + midea.setFan(MIDEA_AC_FAN_HI); + midea.setMode(MIDEA_AC_DRY); + midea.setSleep(true); + EXPECT_EQ("Power: Off, Mode: 1 (DRY), Temp: 16C/62F, Fan: 3 (HI), Sleep: On", + midea.toString()); + + midea.setRaw(0xA19867FFFF7E); + EXPECT_EQ("Power: On, Mode: 0 (COOL), Temp: 20C/69F, Fan: 3 (HI), Sleep: Off", + midea.toString()); +} + +// Tests for decodeMidea(). + +// Decode normal Midea messages with strict set. +TEST(TestDecodeMidea, NormalDecodeWithStrict) { + IRsendTest irsend(4); + IRrecv irrecv(4); + irsend.begin(); + + // Normal Midea 48-bit message. + irsend.reset(); + irsend.sendMidea(0x1234567890DF); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decodeMidea(&irsend.capture, MIDEA_BITS, true)); + EXPECT_EQ(MIDEA, irsend.capture.decode_type); + EXPECT_EQ(MIDEA_BITS, irsend.capture.bits); + EXPECT_EQ(0x1234567890DF, irsend.capture.value); + EXPECT_EQ(0x0, irsend.capture.address); + EXPECT_EQ(0x0, irsend.capture.command); + EXPECT_FALSE(irsend.capture.repeat); + + // Normal Midea 48-bit message. + irsend.reset(); + irsend.sendMidea(0x0); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decodeMidea(&irsend.capture, MIDEA_BITS, true)); + EXPECT_EQ(MIDEA, irsend.capture.decode_type); + EXPECT_EQ(MIDEA_BITS, irsend.capture.bits); + EXPECT_EQ(0x0, irsend.capture.value); + EXPECT_EQ(0x0, irsend.capture.address); + EXPECT_EQ(0x0, irsend.capture.command); + EXPECT_FALSE(irsend.capture.repeat); + + // Normal Midea 48-bit message. + irsend.reset(); + irsend.sendMidea(0xFFFFFFFFFFA0); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decodeMidea(&irsend.capture, MIDEA_BITS, true)); + EXPECT_EQ(MIDEA, irsend.capture.decode_type); + EXPECT_EQ(MIDEA_BITS, irsend.capture.bits); + EXPECT_EQ(0xFFFFFFFFFFA0, irsend.capture.value); + EXPECT_EQ(0x0, irsend.capture.address); + EXPECT_EQ(0x0, irsend.capture.command); + EXPECT_FALSE(irsend.capture.repeat); + + // Real Midea 48-bit message via just decode(). + // i.e. No conficts with other decoders. + irsend.reset(); + irsend.sendMidea(0xA18263FFFF6E); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(MIDEA, irsend.capture.decode_type); + EXPECT_EQ(MIDEA_BITS, irsend.capture.bits); + EXPECT_EQ(0xA18263FFFF6E, irsend.capture.value); + EXPECT_EQ(0x0, irsend.capture.address); + EXPECT_EQ(0x0, irsend.capture.command); + EXPECT_FALSE(irsend.capture.repeat); +} + +// Decode normal repeated Midea messages. +TEST(TestDecodeMidea, NormalDecodeWithRepeatAndStrict) { + IRsendTest irsend(4); + IRrecv irrecv(4); + irsend.begin(); + + // Normal Midea 48-bit message with 2 repeats. + irsend.reset(); + irsend.sendMidea(0xA18263FFFF6E, MIDEA_BITS, 2); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decodeMidea(&irsend.capture, MIDEA_BITS, true)); + EXPECT_EQ(MIDEA, irsend.capture.decode_type); + EXPECT_EQ(MIDEA_BITS, irsend.capture.bits); + EXPECT_EQ(0xA18263FFFF6E, irsend.capture.value); + EXPECT_FALSE(irsend.capture.repeat); + + irsend.makeDecodeResult(2 * (2 * MIDEA_BITS + 4)); + ASSERT_TRUE(irrecv.decodeMidea(&irsend.capture, MIDEA_BITS, true)); + EXPECT_EQ(MIDEA, irsend.capture.decode_type); + EXPECT_EQ(MIDEA_BITS, irsend.capture.bits); + EXPECT_EQ(0xA18263FFFF6E, irsend.capture.value); + + irsend.makeDecodeResult(4 * (2 * MIDEA_BITS + 4)); + ASSERT_TRUE(irrecv.decodeMidea(&irsend.capture, MIDEA_BITS, true)); + EXPECT_EQ(MIDEA, irsend.capture.decode_type); + EXPECT_EQ(MIDEA_BITS, irsend.capture.bits); + EXPECT_EQ(0xA18263FFFF6E, irsend.capture.value); +} + +// Decode unsupported Midea messages. +TEST(TestDecodeMidea, DecodeWithNonStrictSizes) { + IRsendTest irsend(4); + IRrecv irrecv(4); + irsend.begin(); + + irsend.reset(); + irsend.sendMidea(0x12, 8); // Illegal value Midea 8-bit message. + irsend.makeDecodeResult(); + // Should fail with strict on. + ASSERT_FALSE(irrecv.decodeMidea(&irsend.capture, MIDEA_BITS, true)); + // Should pass if strict off. + ASSERT_TRUE(irrecv.decodeMidea(&irsend.capture, 8, false)); + EXPECT_EQ(MIDEA, irsend.capture.decode_type); + EXPECT_EQ(8, irsend.capture.bits); + EXPECT_EQ(0x12, irsend.capture.value); + + irsend.reset(); + irsend.sendMidea(0x12345678, 32); // Illegal value Midea 32-bit message. + irsend.makeDecodeResult(); + // Shouldn't pass with strict when we ask for less bits than we got. + ASSERT_FALSE(irrecv.decodeMidea(&irsend.capture, MIDEA_BITS, true)); + + irsend.makeDecodeResult(); + // Should fail with strict when we ask for the wrong bit size. + ASSERT_FALSE(irrecv.decodeMidea(&irsend.capture, 32, true)); + // Should pass if strict off. + ASSERT_TRUE(irrecv.decodeMidea(&irsend.capture, 32, false)); + EXPECT_EQ(MIDEA, irsend.capture.decode_type); + EXPECT_EQ(32, irsend.capture.bits); + EXPECT_EQ(0x12345678, irsend.capture.value); + + // Decode should fail if asked to decode non-multiples of 8 bits. + irsend.reset(); + irsend.sendMidea(0x123456, MIDEA_BITS, 2); + irsend.makeDecodeResult(); + ASSERT_FALSE(irrecv.decodeMidea(&irsend.capture, 9, false)); +} + +// Decode (non-standard) 64-bit messages. +TEST(TestDecodeMidea, Decode64BitMessages) { + IRsendTest irsend(4); + IRrecv irrecv(4); + irsend.begin(); + + irsend.reset(); + // Illegal size Midea 64-bit message. + irsend.sendMidea(0xFFFFFFFFFFFFFFFF, 64); + irsend.makeDecodeResult(); + // Should work with a 'normal' match (not strict) + ASSERT_TRUE(irrecv.decodeMidea(&irsend.capture, 64, false)); + EXPECT_EQ(MIDEA, irsend.capture.decode_type); + EXPECT_EQ(64, irsend.capture.bits); + EXPECT_EQ(0xFFFFFFFFFFFFFFFF, irsend.capture.value); +} + +// Fail to decode a non-Midea example via GlobalCache +TEST(TestDecodeMidea, FailToDecodeNonMideaExample) { + IRsendTest irsend(4); + IRrecv irrecv(4); + irsend.begin(); + + irsend.reset(); + // Modified a few entries to unexpected values, based on previous test case. + uint16_t gc_test[39] = {38000, 1, 1, 322, 162, 20, 61, 20, 61, 20, 20, 20, 20, + 20, 20, 20, 127, 20, 61, 9, 20, 20, 61, 20, 20, 20, + 61, 20, 61, 20, 61, 20, 20, 20, 20, 20, 20, 20, 884}; + irsend.sendGC(gc_test, 39); + irsend.makeDecodeResult(); + + ASSERT_FALSE(irrecv.decodeMidea(&irsend.capture)); + ASSERT_FALSE(irrecv.decodeMidea(&irsend.capture, MIDEA_BITS, false)); +} + +// Decode against a real capture reported by a user. See issue #354 +TEST(TestDecodeMidea, DecodeRealExample) { + IRsendTest irsend(4); + IRrecv irrecv(4); + irsend.begin(); + irsend.reset(); + + uint16_t rawData[199] = { + 4366, 4470, 498, 1658, 522, 554, 498, 1658, 496, 580, 498, 580, 498, 578, + 498, 580, 498, 1658, 498, 1658, 498, 578, 498, 578, 498, 580, 496, 582, + 496, 578, 498, 1658, 498, 580, 498, 580, 498, 1656, 498, 1656, 500, 580, + 498, 578, 502, 576, 500, 1656, 498, 1656, 500, 1654, 500, 1656, 500, 1656, + 498, 1658, 498, 1656, 500, 1658, 498, 1656, 498, 1656, 500, 1656, 500, + 1654, 500, 1578, 578, 1658, 498, 1656, 500, 1658, 498, 1656, 498, 1656, + 500, 578, 498, 1638, 516, 1656, 500, 578, 500, 1656, 500, 1656, 498, 1658, + 522, 554, 500, 5258, 4366, 4472, 498, 580, 498, 1658, 498, 580, 498, 1656, + 500, 1600, 556, 1658, 500, 1656, 500, 578, 498, 578, 522, 1634, 498, 1588, + 568, 1658, 498, 1656, 500, 1654, 498, 580, 498, 1658, 498, 1658, 498, 580, + 496, 578, 500, 1654, 500, 1636, 518, 1656, 500, 578, 520, 558, 498, 578, + 498, 580, 498, 576, 500, 578, 498, 580, 498, 578, 498, 578, 498, 580, 498, + 578, 498, 580, 498, 580, 520, 556, 498, 580, 496, 580, 498, 578, 500, 578, + 498, 1658, 498, 580, 498, 578, 498, 1656, 500, 578, 498, 580, 498, 580, + 498, 1656, 522}; + irsend.sendRaw(rawData, 199, 38000); + irsend.makeDecodeResult(); + + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(MIDEA, irsend.capture.decode_type); + EXPECT_EQ(MIDEA_BITS, irsend.capture.bits); + EXPECT_EQ(0xA18263FFFF6E, irsend.capture.value); +} diff --git a/lib/IRremoteESP8266/test/ir_Mitsubishi_test.cpp b/lib/IRremoteESP8266/test/ir_Mitsubishi_test.cpp index 2f28657..861a1ce 100644 --- a/lib/IRremoteESP8266/test/ir_Mitsubishi_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Mitsubishi_test.cpp @@ -17,30 +17,30 @@ TEST(TestSendMitsubishi, SendDataOnly) { EXPECT_EQ( "m300s2100m300s2100m300s2100m300s900m300s900m300s900m300s2100m300s900" "m300s900m300s2100m300s900m300s900m300s900m300s900m300s2100m300s900" - "m300s53580" + "m300s28080" "m300s2100m300s2100m300s2100m300s900m300s900m300s900m300s2100m300s900" "m300s900m300s2100m300s900m300s900m300s900m300s900m300s2100m300s900" - "m300s53580", irsend.outputStr()); + "m300s28080", irsend.outputStr()); irsend.reset(); irsend.sendMitsubishi(0x0); EXPECT_EQ( "m300s900m300s900m300s900m300s900m300s900m300s900m300s900m300s900" "m300s900m300s900m300s900m300s900m300s900m300s900m300s900m300s900" - "m300s53580" + "m300s34080" "m300s900m300s900m300s900m300s900m300s900m300s900m300s900m300s900" "m300s900m300s900m300s900m300s900m300s900m300s900m300s900m300s900" - "m300s53580", irsend.outputStr()); + "m300s34080", irsend.outputStr()); irsend.reset(); irsend.sendMitsubishi(0x4321); EXPECT_EQ( "m300s900m300s2100m300s900m300s900m300s900m300s900m300s2100m300s2100" "m300s900m300s900m300s2100m300s900m300s900m300s900m300s900m300s2100" - "m300s53580" + "m300s28080" "m300s900m300s2100m300s900m300s900m300s900m300s900m300s2100m300s2100" "m300s900m300s900m300s2100m300s900m300s900m300s900m300s900m300s2100" - "m300s53580", irsend.outputStr()); + "m300s28080", irsend.outputStr()); } // Test sending with different repeats. @@ -53,32 +53,32 @@ TEST(TestSendMitsubishi, SendWithRepeats) { EXPECT_EQ( "m300s2100m300s2100m300s2100m300s900m300s900m300s900m300s2100m300s900" "m300s900m300s2100m300s900m300s900m300s900m300s900m300s2100m300s900" - "m300s53580", irsend.outputStr()); + "m300s28080", irsend.outputStr()); irsend.reset(); irsend.sendMitsubishi(0xE242, MITSUBISHI_BITS, 1); // 1 repeat. EXPECT_EQ( "m300s2100m300s2100m300s2100m300s900m300s900m300s900m300s2100m300s900" "m300s900m300s2100m300s900m300s900m300s900m300s900m300s2100m300s900" - "m300s53580" + "m300s28080" "m300s2100m300s2100m300s2100m300s900m300s900m300s900m300s2100m300s900" "m300s900m300s2100m300s900m300s900m300s900m300s900m300s2100m300s900" - "m300s53580", irsend.outputStr()); + "m300s28080", irsend.outputStr()); irsend.sendMitsubishi(0xE242, MITSUBISHI_BITS, 2); // 2 repeats. EXPECT_EQ( "m300s2100m300s2100m300s2100m300s900m300s900m300s900m300s2100m300s900" "m300s900m300s2100m300s900m300s900m300s900m300s900m300s2100m300s900" - "m300s53580" + "m300s28080" "m300s2100m300s2100m300s2100m300s900m300s900m300s900m300s2100m300s900" "m300s900m300s2100m300s900m300s900m300s900m300s900m300s2100m300s900" - "m300s53580" + "m300s28080" "m300s2100m300s2100m300s2100m300s900m300s900m300s900m300s2100m300s900" "m300s900m300s2100m300s900m300s900m300s900m300s900m300s2100m300s900" - "m300s53580", irsend.outputStr()); + "m300s28080", irsend.outputStr()); } // Test sending an atypical data size. -TEST(TestSendMitsubishi, SendUsualSize) { +TEST(TestSendMitsubishi, SendUnusualSize) { IRsendTest irsend(4); irsend.begin(); @@ -86,9 +86,9 @@ TEST(TestSendMitsubishi, SendUsualSize) { irsend.sendMitsubishi(0x0, 8); EXPECT_EQ( "m300s900m300s900m300s900m300s900m300s900m300s900m300s900m300s900" - "m300s53580" + "m300s43680" "m300s900m300s900m300s900m300s900m300s900m300s900m300s900m300s900" - "m300s53580", irsend.outputStr()); + "m300s43680", irsend.outputStr()); irsend.reset(); irsend.sendMitsubishi(0x1234567890ABCDEF, 64); @@ -101,7 +101,7 @@ TEST(TestSendMitsubishi, SendUsualSize) { "m300s2100m300s900m300s2100m300s900m300s2100m300s900m300s2100m300s2100" "m300s2100m300s2100m300s900m300s900m300s2100m300s2100m300s900m300s2100" "m300s2100m300s2100m300s2100m300s900m300s2100m300s2100m300s2100m300s2100" - "m300s53580" + "m300s28080" "m300s900m300s900m300s900m300s2100m300s900m300s900m300s2100m300s900" "m300s900m300s900m300s2100m300s2100m300s900m300s2100m300s900m300s900" "m300s900m300s2100m300s900m300s2100m300s900m300s2100m300s2100m300s900" @@ -110,7 +110,7 @@ TEST(TestSendMitsubishi, SendUsualSize) { "m300s2100m300s900m300s2100m300s900m300s2100m300s900m300s2100m300s2100" "m300s2100m300s2100m300s900m300s900m300s2100m300s2100m300s900m300s2100" "m300s2100m300s2100m300s2100m300s900m300s2100m300s2100m300s2100m300s2100" - "m300s53580", irsend.outputStr()); + "m300s28080", irsend.outputStr()); } // Decode normal Mitsubishi messages. @@ -692,3 +692,143 @@ TEST(TestMitsubishiACClass, MessageConstuction) { "m450s420m450s420m450s420m450s1300m450s420m450s420m450s1300m450s420" "m440s17100", irsend.outputStr()); } + +// Tests for sendMitsubishi2(). + +// Test sending typical data only. +TEST(TestSendMitsubishi2, SendDataOnly) { + IRsendTest irsend(4); + irsend.begin(); + + irsend.reset(); + irsend.sendMitsubishi2(0xF82); + EXPECT_EQ( + "m8400s4200" + "m560s520m560s520m560s520m560s520m560s1560m560s1560m560s1560m560s1560" + "m560s4200" + "m560s1560m560s520m560s520m560s520m560s520m560s520m560s1560m560s520" + "m560s28500" + "m8400s4200" + "m560s520m560s520m560s520m560s520m560s1560m560s1560m560s1560m560s1560" + "m560s4200" + "m560s1560m560s520m560s520m560s520m560s520m560s520m560s1560m560s520" + "m560s28500", irsend.outputStr()); + + irsend.reset(); + irsend.sendMitsubishi2(0x0); + EXPECT_EQ( + "m8400s4200" + "m560s520m560s520m560s520m560s520m560s520m560s520m560s520m560s520" + "m560s4200" + "m560s520m560s520m560s520m560s520m560s520m560s520m560s520m560s520" + "m560s28500" + "m8400s4200" + "m560s520m560s520m560s520m560s520m560s520m560s520m560s520m560s520" + "m560s4200" + "m560s520m560s520m560s520m560s520m560s520m560s520m560s520m560s520" + "m560s28500", irsend.outputStr()); +} + +// Test sending odd repeats. +TEST(TestSendMitsubishi2, Repeats) { + IRsendTest irsend(4); + irsend.begin(); + + irsend.reset(); + irsend.sendMitsubishi2(0xF82, MITSUBISHI_BITS, 0); + EXPECT_EQ( + "m8400s4200" + "m560s520m560s520m560s520m560s520m560s1560m560s1560m560s1560m560s1560" + "m560s4200" + "m560s1560m560s520m560s520m560s520m560s520m560s520m560s1560m560s520" + "m560s28500", irsend.outputStr()); + + irsend.reset(); + irsend.sendMitsubishi2(0xF82, MITSUBISHI_BITS, 2); + EXPECT_EQ( + "m8400s4200" + "m560s520m560s520m560s520m560s520m560s1560m560s1560m560s1560m560s1560" + "m560s4200" + "m560s1560m560s520m560s520m560s520m560s520m560s520m560s1560m560s520" + "m560s28500" + "m8400s4200" + "m560s520m560s520m560s520m560s520m560s1560m560s1560m560s1560m560s1560" + "m560s4200" + "m560s1560m560s520m560s520m560s520m560s520m560s520m560s1560m560s520" + "m560s28500" + "m8400s4200" + "m560s520m560s520m560s520m560s520m560s1560m560s1560m560s1560m560s1560" + "m560s4200" + "m560s1560m560s520m560s520m560s520m560s520m560s520m560s1560m560s520" + "m560s28500", irsend.outputStr()); +} + +// Tests for decodeMitsubishi2(). + +// Decode synthetic examples. +TEST(TestDecodeMitsubishi2, DecodeSyntheticExamples) { + IRsendTest irsend(4); + IRrecv irrecv(4); + irsend.begin(); + + irsend.reset(); + // Mitsubishi Projector "Power On" (16-bit). + irsend.sendMitsubishi2(0xF82); + irsend.makeDecodeResult(); + + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(MITSUBISHI2, irsend.capture.decode_type); + EXPECT_EQ(MITSUBISHI_BITS, irsend.capture.bits); + EXPECT_EQ(0xF82, irsend.capture.value); + EXPECT_EQ(0xF, irsend.capture.address); + EXPECT_EQ(0x82, irsend.capture.command); + + irsend.reset(); + irsend.sendMitsubishi2(0x0); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(MITSUBISHI2, irsend.capture.decode_type); + EXPECT_EQ(MITSUBISHI_BITS, irsend.capture.bits); + EXPECT_EQ(0x0, irsend.capture.value); + EXPECT_EQ(0x0, irsend.capture.address); + EXPECT_EQ(0x0, irsend.capture.command); + + irsend.reset(); + irsend.sendMitsubishi2(0x1234); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(MITSUBISHI2, irsend.capture.decode_type); + EXPECT_EQ(MITSUBISHI_BITS, irsend.capture.bits); + EXPECT_EQ(0x1234, irsend.capture.value); + EXPECT_EQ(0x12, irsend.capture.address); + EXPECT_EQ(0x34, irsend.capture.command); +} + +// Decode a 'real' example from Issue #441 +TEST(TestDecodeMitsubishi2, DecodeRealExample) { + IRsendTest irsend(4); + IRrecv irrecv(4); + irsend.begin(); + + irsend.reset(); + // Mitsubishi Projector "Power On" (16-bit). + uint16_t rawData[75] = { + 8402, 4172, 554, 490, 562, 484, 560, 514, 532, 512, 534, 1566, + 526, 1572, 526, 1542, 560, 1568, 532, 4192, + 534, 1564, 532, 484, 560, 512, 532, 512, 532, 514, 530, 514, + 526, 1570, 524, 520, 526, 28506, + 8454, 4170, 560, 514, 528, 516, 526, 520, 524, 490, 556, 1572, + 534, 1534, 560, 1568, 530, 1538, 558, 4166, + 560, 1538, 558, 490, 560, 512, 530, 514, 532, 484, 558, 514, + 532, 1566, 530, 486, 554}; // UNKNOWN 96A1512F + + irsend.sendRaw(rawData, 75, 33); + irsend.makeDecodeResult(); + + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(MITSUBISHI2, irsend.capture.decode_type); + EXPECT_EQ(MITSUBISHI_BITS, irsend.capture.bits); + EXPECT_EQ(0xF82, irsend.capture.value); + EXPECT_EQ(0xF, irsend.capture.address); + EXPECT_EQ(0x82, irsend.capture.command); +} diff --git a/lib/IRremoteESP8266/test/ir_NEC_test.cpp b/lib/IRremoteESP8266/test/ir_NEC_test.cpp index 781aa38..a3b915d 100644 --- a/lib/IRremoteESP8266/test/ir_NEC_test.cpp +++ b/lib/IRremoteESP8266/test/ir_NEC_test.cpp @@ -15,13 +15,13 @@ TEST(TestSendNEC, SendDataOnly) { "m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560" "m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560" "m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560" - "m560s560m560s108080", irsend.outputStr()); + "m560s560m560s58240", irsend.outputStr()); irsend.sendNEC(0xAA00FF55); EXPECT_EQ("m8960s4480m560s1680m560s560m560s1680m560s560m560s1680m560s560" "m560s1680m560s560m560s560m560s560m560s560m560s560m560s560m560s560" "m560s560m560s560m560s1680m560s1680m560s1680m560s1680m560s1680" "m560s1680m560s1680m560s1680m560s560m560s1680m560s560m560s1680" - "m560s560m560s1680m560s560m560s1680m560s108080", + "m560s560m560s1680m560s560m560s1680m560s40320", irsend.outputStr()); } @@ -30,11 +30,11 @@ TEST(TestSendNEC, SendSmallData) { IRsendTest irsend(4); irsend.begin(); irsend.sendNEC(0xA, 4); // Send only 4 data bits. - EXPECT_EQ("m8960s4480m560s1680m560s560m560s1680m560s560m560s108080", + EXPECT_EQ("m8960s4480m560s1680m560s560m560s1680m560s560m560s87360", irsend.outputStr()); irsend.sendNEC(0, 8); // Send only 8 data bits. EXPECT_EQ("m8960s4480m560s560m560s560m560s560m560s560m560s560m560s560m560s560" - "m560s560m560s108080", irsend.outputStr()); + "m560s560m560s85120", irsend.outputStr()); irsend.sendNEC(0x1234567890ABCDEF, 64); // Send 64 data bits. EXPECT_EQ("m8960s4480m560s560m560s560m560s560m560s1680m560s560m560s560" "m560s1680m560s560m560s560m560s560m560s1680m560s1680m560s560" @@ -45,7 +45,7 @@ TEST(TestSendNEC, SendSmallData) { "m560s1680m560s560m560s1680m560s560m560s1680m560s1680m560s1680" "m560s1680m560s560m560s560m560s1680m560s1680m560s560m560s1680" "m560s1680m560s1680m560s1680m560s560m560s1680m560s1680m560s1680" - "m560s1680m560s108080", irsend.outputStr()); + "m560s1680m560s22400", irsend.outputStr()); } // Test sending with repeats. @@ -54,18 +54,18 @@ TEST(TestSendNEC, SendWithRepeats) { irsend.begin(); irsend.sendNEC(0, 8, 0); // Send a command with 0 repeats. EXPECT_EQ("m8960s4480m560s560m560s560m560s560m560s560m560s560m560s560m560s560" - "m560s560m560s108080", irsend.outputStr()); + "m560s560m560s85120", irsend.outputStr()); irsend.sendNEC(0xAA, 8, 1); // Send a command with 1 repeat. EXPECT_EQ("m8960s4480m560s1680m560s560m560s1680m560s560m560s1680m560s560" - "m560s1680m560s560m560s108080" - "m8960s2240m560s108080", + "m560s1680m560s560m560s80640" + "m8960s2240m560s96320", irsend.outputStr()); irsend.sendNEC(0xAA, 8, 3); // Send a command with 3 repeats. EXPECT_EQ("m8960s4480m560s1680m560s560m560s1680m560s560m560s1680m560s560" - "m560s1680m560s560m560s108080" - "m8960s2240m560s108080" - "m8960s2240m560s108080" - "m8960s2240m560s108080", + "m560s1680m560s560m560s80640" + "m8960s2240m560s96320" + "m8960s2240m560s96320" + "m8960s2240m560s96320", irsend.outputStr()); } @@ -250,7 +250,7 @@ TEST(TestDecodeNEC, NoTrailingGap_Issue243) { EXPECT_EQ(0x4BB640BF, irsend.capture.value); // Add a zero length space to the message to test how it handles that as - // a end of command gap. + // an end of command gap. irsend.addGap(0); irsend.makeDecodeResult(); ASSERT_TRUE(irrecv.decode(&irsend.capture)); diff --git a/lib/IRremoteESP8266/test/ir_Nikai_test.cpp b/lib/IRremoteESP8266/test/ir_Nikai_test.cpp new file mode 100644 index 0000000..0eea2bc --- /dev/null +++ b/lib/IRremoteESP8266/test/ir_Nikai_test.cpp @@ -0,0 +1,210 @@ +// Copyright 2017 David Conran + +#include "IRsend.h" +#include "IRsend_test.h" +#include "gtest/gtest.h" + +// Tests for sendNikai(). +// Test sending typical data only. +TEST(TestSendNikai, SendDataOnly) { + IRsendTest irsend(4); + irsend.begin(); + + irsend.reset(); + irsend.sendNikai(0xD5F2A); // Nikai TV Power Off. + EXPECT_EQ("m4000s4000" + "m500s2000m500s2000m500s2000m500s2000m500s1000m500s1000m500s2000" + "m500s1000m500s2000m500s1000m500s2000m500s1000m500s1000m500s1000" + "m500s1000m500s1000m500s2000m500s2000m500s1000m500s2000m500s1000" + "m500s2000m500s1000m500s2000m500s8500", + irsend.outputStr()); + + irsend.reset(); +} + +// Test sending with different repeats. +TEST(TestSendNikai, SendWithRepeats) { + IRsendTest irsend(4); + irsend.begin(); + + irsend.reset(); + irsend.sendNikai(0xD5F2A, NIKAI_BITS, 1); // 1 repeat. + EXPECT_EQ("m4000s4000" + "m500s2000m500s2000m500s2000m500s2000m500s1000m500s1000m500s2000" + "m500s1000m500s2000m500s1000m500s2000m500s1000m500s1000m500s1000" + "m500s1000m500s1000m500s2000m500s2000m500s1000m500s2000m500s1000" + "m500s2000m500s1000m500s2000m500s8500" + "m4000s4000" + "m500s2000m500s2000m500s2000m500s2000m500s1000m500s1000m500s2000" + "m500s1000m500s2000m500s1000m500s2000m500s1000m500s1000m500s1000" + "m500s1000m500s1000m500s2000m500s2000m500s1000m500s2000m500s1000" + "m500s2000m500s1000m500s2000m500s8500", + irsend.outputStr()); + irsend.sendNikai(0xD5F2A, NIKAI_BITS, 2); // 2 repeat. + EXPECT_EQ("m4000s4000" + "m500s2000m500s2000m500s2000m500s2000m500s1000m500s1000m500s2000" + "m500s1000m500s2000m500s1000m500s2000m500s1000m500s1000m500s1000" + "m500s1000m500s1000m500s2000m500s2000m500s1000m500s2000m500s1000" + "m500s2000m500s1000m500s2000m500s8500" + "m4000s4000" + "m500s2000m500s2000m500s2000m500s2000m500s1000m500s1000m500s2000" + "m500s1000m500s2000m500s1000m500s2000m500s1000m500s1000m500s1000" + "m500s1000m500s1000m500s2000m500s2000m500s1000m500s2000m500s1000" + "m500s2000m500s1000m500s2000m500s8500" + "m4000s4000" + "m500s2000m500s2000m500s2000m500s2000m500s1000m500s1000m500s2000" + "m500s1000m500s2000m500s1000m500s2000m500s1000m500s1000m500s1000" + "m500s1000m500s1000m500s2000m500s2000m500s1000m500s2000m500s1000" + "m500s2000m500s1000m500s2000m500s8500", + irsend.outputStr()); +} + +// Tests for decodeNikai(). + +// Decode normal Nikai messages. +TEST(TestDecodeNikai, NormalDecodeWithStrict) { + IRsendTest irsend(4); + IRrecv irrecv(4); + irsend.begin(); + + // Normal Nikai 24-bit message. + irsend.reset(); + irsend.sendNikai(0x123456); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decodeNikai(&irsend.capture, NIKAI_BITS, true)); + EXPECT_EQ(NIKAI, irsend.capture.decode_type); + EXPECT_EQ(NIKAI_BITS, irsend.capture.bits); + EXPECT_EQ(0x123456, irsend.capture.value); + + irsend.reset(); + irsend.sendNikai(0x101); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decodeNikai(&irsend.capture, NIKAI_BITS, true)); + EXPECT_EQ(NIKAI, irsend.capture.decode_type); + EXPECT_EQ(NIKAI_BITS, irsend.capture.bits); + EXPECT_EQ(0x101, irsend.capture.value); +} + +// Decode normal repeated Nikai messages. +TEST(TestDecodeNikai, NormalDecodeWithRepeatAndStrict) { + IRsendTest irsend(4); + IRrecv irrecv(4); + irsend.begin(); + + // Normal Nikai 24-bit message. + irsend.reset(); + irsend.sendNikai(0xD5F2A, NIKAI_BITS, 2); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decodeNikai(&irsend.capture, NIKAI_BITS, true)); + EXPECT_EQ(NIKAI, irsend.capture.decode_type); + EXPECT_EQ(NIKAI_BITS, irsend.capture.bits); + EXPECT_EQ(0xD5F2A, irsend.capture.value); +} + +TEST(TestDecodeNikai, NormalDecodeWithNonStrict) { + IRsendTest irsend(4); + IRrecv irrecv(4); + irsend.begin(); + + // Illegal under length (16-bit) message + irsend.reset(); + irsend.sendNikai(0x0, 16); + irsend.makeDecodeResult(); + // Should fail with strict on. + ASSERT_FALSE(irrecv.decodeNikai(&irsend.capture, NIKAI_BITS, true)); + // And it should fail when we expect more bits. + ASSERT_FALSE(irrecv.decodeNikai(&irsend.capture, NIKAI_BITS, false)); + + // Should pass if strict off if we ask for correct nr. of bits sent. + ASSERT_TRUE(irrecv.decodeNikai(&irsend.capture, 16, false)); + EXPECT_EQ(NIKAI, irsend.capture.decode_type); + EXPECT_EQ(16, irsend.capture.bits); + EXPECT_EQ(0x0, irsend.capture.value); + + // Should fail as we are expecting less bits than there are. + ASSERT_FALSE(irrecv.decodeNikai(&irsend.capture, 12, false)); +} + +// Decode (non-standard) 64-bit messages. +// Decode unsupported Nikai messages. +TEST(TestDecodeNikai, Decode64BitMessages) { + IRsendTest irsend(4); + IRrecv irrecv(4); + irsend.begin(); + + irsend.reset(); + // Illegal size Nikai 64-bit message. + irsend.sendNikai(0xFFFFFFFFFFFFFFFF, 64); + irsend.makeDecodeResult(); + ASSERT_FALSE(irrecv.decodeNikai(&irsend.capture, NIKAI_BITS, true)); + // Should work with a 'normal' match (not strict) + ASSERT_TRUE(irrecv.decodeNikai(&irsend.capture, 64, false)); + EXPECT_EQ(NIKAI, irsend.capture.decode_type); + EXPECT_EQ(64, irsend.capture.bits); + EXPECT_EQ(0xFFFFFFFFFFFFFFFF, irsend.capture.value); +} + +// Decode real example via Issue #309 +TEST(TestDecodeNikai, DecodeExamples) { + IRsendTest irsend(4); + IRrecv irrecv(4); + irsend.begin(); + + irsend.reset(); + // Nikai TV Power Off from Issue #309 + uint16_t rawdata_off[100] = {4060, 3918, + 508, 2004, 508, 2002, 510, 2002, 508, 2004, 506, 1050, 508, 1048, + 510, 2004, 508, 1048, 508, 2002, 510, 1050, 508, 2004, 510, 1048, + 508, 1050, 508, 1048, 508, 1050, 508, 1050, 508, 2004, 508, 2002, + 510, 1048, 508, 2004, 508, 1050, 506, 2004, 508, 1048, 510, 2002, + 456, 8446, + 3956, 3998, + 508, 2004, 508, 2002, 508, 2004, 508, 1978, 532, 1050, 508, 1050, + 508, 2002, 508, 1050, 508, 2004, 508, 1050, 508, 2002, 510, 1050, + 508, 1050, 508, 1048, 508, 1050, 508, 1050, 508, 2002, 510, 2002, + 508, 1050, 508, 2002, 510, 1050, 508, 2002, 508}; + irsend.sendRaw(rawdata_off, 100, 38); + irsend.makeDecodeResult(); + + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(NIKAI, irsend.capture.decode_type); + EXPECT_EQ(NIKAI_BITS, irsend.capture.bits); + EXPECT_EQ(0xD5F2A, irsend.capture.value); + + // Nikai TV Volume Up from Issue #309 + uint16_t rawdata_volup[52] = {3972, 4002, + 504, 1982, 526, 2010, 502, 2010, 502, 2010, 500, 1056, 502, 1056, + 502, 2010, 500, 1056, 502, 2010, 502, 2010, 500, 2010, 502, 2010, + 502, 1056, 502, 1056, 502, 1056, 500, 1056, 502, 2010, 502, 2010, + 500, 1056, 502, 2008, 502, 1054, 504, 1054, 504, 1054, 500, 1056, + 450}; + + irsend.reset(); + irsend.sendRaw(rawdata_volup, 52, 38); + irsend.makeDecodeResult(); + + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(NIKAI, irsend.capture.decode_type); + EXPECT_EQ(NIKAI_BITS, irsend.capture.bits); + EXPECT_EQ(0xD0F2F, irsend.capture.value); +} + +// Fail to decode a non-Nikai example via GlobalCache +TEST(TestDecodeNikai, FailToDecodeNonNikaiExample) { + IRsendTest irsend(4); + IRrecv irrecv(4); + irsend.begin(); + + irsend.reset(); + uint16_t gc_test[71] = {38000, 1, 1, 172, 172, 22, 64, 22, 64, 22, 64, 22, 21, + 22, 21, 22, 21, 22, 11, 22, 21, 22, 128, 22, 64, 22, + 64, 22, 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, 64, + 22, 21, 22, 21, 22, 64, 22, 64, 22, 21, 22, 21, 22, + 64, 22, 21, 22, 64, 22, 64, 22, 21, 22, 21, 22, 64, + 22, 64, 22, 21, 22, 1820}; + irsend.sendGC(gc_test, 71); + irsend.makeDecodeResult(); + + ASSERT_FALSE(irrecv.decodeNikai(&irsend.capture)); + ASSERT_FALSE(irrecv.decodeNikai(&irsend.capture, NIKAI_BITS, false)); +} diff --git a/lib/IRremoteESP8266/test/ir_Panasonic_test.cpp b/lib/IRremoteESP8266/test/ir_Panasonic_test.cpp index 3f27b90..6e6c62d 100644 --- a/lib/IRremoteESP8266/test/ir_Panasonic_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Panasonic_test.cpp @@ -36,7 +36,7 @@ TEST(TestSendPanasonic64, SendDataOnly) { "m432s432m432s432m432s432m432s432m432s432m432s432m432s432m432s432" "m432s432m432s432m432s432m432s432m432s432m432s432m432s432m432s432" "m432s432m432s432m432s432m432s432m432s432m432s432m432s432m432s432" - "m432s129600", irsend.outputStr()); + "m432s116208", irsend.outputStr()); irsend.reset(); irsend.sendPanasonic64(0x40040190ED7C); @@ -48,7 +48,7 @@ TEST(TestSendPanasonic64, SendDataOnly) { "m432s1296m432s432m432s432m432s1296m432s432m432s432m432s432m432s432" "m432s1296m432s1296m432s1296m432s432m432s1296m432s1296m432s432m432s1296" "m432s432m432s1296m432s1296m432s1296m432s1296m432s1296m432s432m432s432" - "m432s129600", irsend.outputStr()); + "m432s102384", irsend.outputStr()); irsend.reset(); irsend.sendPanasonic64(0xFFFFFFFFFFFF); @@ -60,7 +60,7 @@ TEST(TestSendPanasonic64, SendDataOnly) { "m432s1296m432s1296m432s1296m432s1296m432s1296m432s1296m432s1296m432s1296" "m432s1296m432s1296m432s1296m432s1296m432s1296m432s1296m432s1296m432s1296" "m432s1296m432s1296m432s1296m432s1296m432s1296m432s1296m432s1296m432s1296" - "m432s129600", irsend.outputStr()); + "m432s74736", irsend.outputStr()); } // Test sending with different repeats. @@ -78,7 +78,7 @@ TEST(TestSendPanasonic64, SendWithRepeats) { "m432s1296m432s432m432s432m432s1296m432s432m432s432m432s432m432s432" "m432s1296m432s1296m432s1296m432s432m432s1296m432s1296m432s432m432s1296" "m432s432m432s1296m432s1296m432s1296m432s1296m432s1296m432s432m432s432" - "m432s129600", irsend.outputStr()); + "m432s102384", irsend.outputStr()); irsend.reset(); irsend.sendPanasonic64(0x40040190ED7C, PANASONIC_BITS, 1); // 1 repeat. @@ -90,7 +90,7 @@ TEST(TestSendPanasonic64, SendWithRepeats) { "m432s1296m432s432m432s432m432s1296m432s432m432s432m432s432m432s432" "m432s1296m432s1296m432s1296m432s432m432s1296m432s1296m432s432m432s1296" "m432s432m432s1296m432s1296m432s1296m432s1296m432s1296m432s432m432s432" - "m432s129600" + "m432s102384" "m3456s1728" "m432s432m432s1296m432s432m432s432m432s432m432s432m432s432m432s432" "m432s432m432s432m432s432m432s432m432s432m432s1296m432s432m432s432" @@ -98,7 +98,7 @@ TEST(TestSendPanasonic64, SendWithRepeats) { "m432s1296m432s432m432s432m432s1296m432s432m432s432m432s432m432s432" "m432s1296m432s1296m432s1296m432s432m432s1296m432s1296m432s432m432s1296" "m432s432m432s1296m432s1296m432s1296m432s1296m432s1296m432s432m432s432" - "m432s129600", irsend.outputStr()); + "m432s102384", irsend.outputStr()); irsend.sendPanasonic64(0x40040190ED7C, PANASONIC_BITS, 2); // 2 repeats. EXPECT_EQ( @@ -109,7 +109,7 @@ TEST(TestSendPanasonic64, SendWithRepeats) { "m432s1296m432s432m432s432m432s1296m432s432m432s432m432s432m432s432" "m432s1296m432s1296m432s1296m432s432m432s1296m432s1296m432s432m432s1296" "m432s432m432s1296m432s1296m432s1296m432s1296m432s1296m432s432m432s432" - "m432s129600" + "m432s102384" "m3456s1728" "m432s432m432s1296m432s432m432s432m432s432m432s432m432s432m432s432" "m432s432m432s432m432s432m432s432m432s432m432s1296m432s432m432s432" @@ -117,7 +117,7 @@ TEST(TestSendPanasonic64, SendWithRepeats) { "m432s1296m432s432m432s432m432s1296m432s432m432s432m432s432m432s432" "m432s1296m432s1296m432s1296m432s432m432s1296m432s1296m432s432m432s1296" "m432s432m432s1296m432s1296m432s1296m432s1296m432s1296m432s432m432s432" - "m432s129600" + "m432s102384" "m3456s1728" "m432s432m432s1296m432s432m432s432m432s432m432s432m432s432m432s432" "m432s432m432s432m432s432m432s432m432s432m432s1296m432s432m432s432" @@ -125,7 +125,7 @@ TEST(TestSendPanasonic64, SendWithRepeats) { "m432s1296m432s432m432s432m432s1296m432s432m432s432m432s432m432s432" "m432s1296m432s1296m432s1296m432s432m432s1296m432s1296m432s432m432s1296" "m432s432m432s1296m432s1296m432s1296m432s1296m432s1296m432s432m432s432" - "m432s129600", irsend.outputStr()); + "m432s102384", irsend.outputStr()); } // Test sending an atypical data size. @@ -138,7 +138,7 @@ TEST(TestSendPanasonic64, SendUnusualSize) { EXPECT_EQ( "m3456s1728" "m432s432m432s432m432s432m432s432m432s432m432s432m432s432m432s432" - "m432s129600", irsend.outputStr()); + "m432s150768", irsend.outputStr()); irsend.reset(); irsend.sendPanasonic64(0x1234567890ABCDEF, 64); @@ -152,7 +152,7 @@ TEST(TestSendPanasonic64, SendUnusualSize) { "m432s1296m432s432m432s1296m432s432m432s1296m432s432m432s1296m432s1296" "m432s1296m432s1296m432s432m432s432m432s1296m432s1296m432s432m432s1296" "m432s1296m432s1296m432s1296m432s432m432s1296m432s1296m432s1296m432s1296" - "m432s129600", irsend.outputStr()); + "m432s74736", irsend.outputStr()); } // Tests for sendPanasonic(). diff --git a/lib/IRremoteESP8266/test/ir_Pronto_test.cpp b/lib/IRremoteESP8266/test/ir_Pronto_test.cpp index 7fbe395..c8cac92 100644 --- a/lib/IRremoteESP8266/test/ir_Pronto_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Pronto_test.cpp @@ -129,6 +129,12 @@ TEST(TestSendPronto, NonRepeatingCode) { irsend.reset(); irsend.sendPronto(pronto_test, 108); irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(SONY, irsend.capture.decode_type); + EXPECT_EQ(SONY_12_BITS, irsend.capture.bits); + EXPECT_EQ(0x10, irsend.capture.value); + EXPECT_EQ(0x1, irsend.capture.address); + EXPECT_EQ(0x0, irsend.capture.command); EXPECT_EQ( "m2400s600" "m600s600m600s600m600s600m600s600m600s600m600s600m600s600m1200s600" @@ -142,18 +148,18 @@ TEST(TestSendPronto, NonRepeatingCode) { "m2400s600" "m600s600m600s600m600s600m600s600m600s600m600s600m600s600m1200s600" "m600s600m600s600m600s600m600s600", irsend.outputStr()); - EXPECT_TRUE(irrecv.decode(&irsend.capture)); - EXPECT_EQ(SONY, irsend.capture.decode_type); - EXPECT_EQ(SONY_12_BITS, irsend.capture.bits); - EXPECT_EQ(0x10, irsend.capture.value); - EXPECT_EQ(0x1, irsend.capture.address); - EXPECT_EQ(0x0, irsend.capture.command); // Now try repeating it. // As it has no repeat sequence, we shouldn't repeat it. (I think) irsend.reset(); irsend.sendPronto(pronto_test, 108, 3); irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(SONY, irsend.capture.decode_type); + EXPECT_EQ(SONY_12_BITS, irsend.capture.bits); + EXPECT_EQ(0x10, irsend.capture.value); + EXPECT_EQ(0x1, irsend.capture.address); + EXPECT_EQ(0x0, irsend.capture.command); EXPECT_EQ( "m2400s600" "m600s600m600s600m600s600m600s600m600s600m600s600m600s600m1200s600" @@ -167,12 +173,6 @@ TEST(TestSendPronto, NonRepeatingCode) { "m2400s600" "m600s600m600s600m600s600m600s600m600s600m600s600m600s600m1200s600" "m600s600m600s600m600s600m600s600", irsend.outputStr()); - EXPECT_TRUE(irrecv.decode(&irsend.capture)); - EXPECT_EQ(SONY, irsend.capture.decode_type); - EXPECT_EQ(SONY_12_BITS, irsend.capture.bits); - EXPECT_EQ(0x10, irsend.capture.value); - EXPECT_EQ(0x1, irsend.capture.address); - EXPECT_EQ(0x0, irsend.capture.command); } // Test sending a Pronto code that only has a repeat sequence (Sony). @@ -195,22 +195,28 @@ TEST(TestSendPronto, RepeatSequenceOnlyForSony) { irsend.reset(); irsend.sendPronto(pronto_test, 46); irsend.makeDecodeResult(); - EXPECT_EQ( - "m2400s600" - "m600s600m1200s600m1200s600m1200s600m600s600m1200s600m600s600m600s600" - "m1200s600m600s600m1200s600m1200s600m1200s600m600s600m600s600m1200s600" - "m600s600m600s600m1200s600m600s25350", irsend.outputStr()); EXPECT_TRUE(irrecv.decode(&irsend.capture)); EXPECT_EQ(SONY, irsend.capture.decode_type); EXPECT_EQ(SONY_20_BITS, irsend.capture.bits); EXPECT_EQ(0x74B92, irsend.capture.value); EXPECT_EQ(0x1A, irsend.capture.address); EXPECT_EQ(0x24AE, irsend.capture.command); + EXPECT_EQ( + "m2400s600" + "m600s600m1200s600m1200s600m1200s600m600s600m1200s600m600s600m600s600" + "m1200s600m600s600m1200s600m1200s600m1200s600m600s600m600s600m1200s600" + "m600s600m600s600m1200s600m600s25350", irsend.outputStr()); // Send the Pronto code with 2 repeats. irsend.reset(); irsend.sendPronto(pronto_test, 46, SONY_MIN_REPEAT); irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(SONY, irsend.capture.decode_type); + EXPECT_EQ(SONY_20_BITS, irsend.capture.bits); + EXPECT_EQ(0x74B92, irsend.capture.value); + EXPECT_EQ(0x1A, irsend.capture.address); + EXPECT_EQ(0x24AE, irsend.capture.command); EXPECT_EQ( "m2400s600" "m600s600m1200s600m1200s600m1200s600m600s600m1200s600m600s600m600s600" @@ -224,13 +230,6 @@ TEST(TestSendPronto, RepeatSequenceOnlyForSony) { "m600s600m1200s600m1200s600m1200s600m600s600m1200s600m600s600m600s600" "m1200s600m600s600m1200s600m1200s600m1200s600m600s600m600s600m1200s600" "m600s600m600s600m1200s600m600s25350", irsend.outputStr()); - - EXPECT_TRUE(irrecv.decode(&irsend.capture)); - EXPECT_EQ(SONY, irsend.capture.decode_type); - EXPECT_EQ(SONY_20_BITS, irsend.capture.bits); - EXPECT_EQ(0x74B92, irsend.capture.value); - EXPECT_EQ(0x1A, irsend.capture.address); - EXPECT_EQ(0x24AE, irsend.capture.command); } // Test sending a Pronto code that only has a repeat sequence (Panasonic). @@ -260,6 +259,12 @@ TEST(TestSendPronto, RepeatSequenceOnlyForPanasonic) { irsend.reset(); irsend.sendPronto(pronto_test, 104); irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(PANASONIC, irsend.capture.decode_type); + EXPECT_EQ(PANASONIC_BITS, irsend.capture.bits); + EXPECT_EQ(0x400401007C7D, irsend.capture.value); + EXPECT_EQ(0x4004, irsend.capture.address); + EXPECT_EQ(0x1007C7D, irsend.capture.command); EXPECT_EQ( "m3456s1701" "m432s432m432s1296m432s432m432s432m432s432m432s432m432s432m432s432" @@ -269,13 +274,6 @@ TEST(TestSendPronto, RepeatSequenceOnlyForPanasonic) { "m432s432m432s1296m432s1296m432s1296m432s1296m432s1296m432s432m432s432" "m432s432m432s1296m432s1296m432s1296m432s1296m432s1296m432s432m432s1296" "m432s73224", irsend.outputStr()); - - EXPECT_TRUE(irrecv.decode(&irsend.capture)); - EXPECT_EQ(PANASONIC, irsend.capture.decode_type); - EXPECT_EQ(PANASONIC_BITS, irsend.capture.bits); - EXPECT_EQ(0x400401007C7D, irsend.capture.value); - EXPECT_EQ(0x4004, irsend.capture.address); - EXPECT_EQ(0x1007C7D, irsend.capture.command); } @@ -303,6 +301,12 @@ TEST(TestSendPronto, NormalPlusRepeatSequence) { irsend.reset(); irsend.sendPronto(pronto_test, 76); irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(NEC, irsend.capture.decode_type); + EXPECT_EQ(NEC_BITS, irsend.capture.bits); + EXPECT_EQ(0x18E710EF, irsend.capture.value); + EXPECT_EQ(0x18, irsend.capture.address); + EXPECT_EQ(0x8, irsend.capture.command); EXPECT_EQ( "m8892s4446" "m546s546m546s546m546s546m546s1664m546s1664m546s546m546s546m546s546" @@ -310,17 +314,17 @@ TEST(TestSendPronto, NormalPlusRepeatSequence) { "m546s546m546s546m546s546m546s1664m546s546m546s546m546s546m546s546" "m546s1664m546s1664m546s1664m546s546m546s1664m546s1664m546s1664m546s1664" "m546s39858", irsend.outputStr()); + + // Send it again with a single repeat. + irsend.reset(); + irsend.sendPronto(pronto_test, 76, 1); + irsend.makeDecodeResult(); EXPECT_TRUE(irrecv.decode(&irsend.capture)); EXPECT_EQ(NEC, irsend.capture.decode_type); EXPECT_EQ(NEC_BITS, irsend.capture.bits); EXPECT_EQ(0x18E710EF, irsend.capture.value); EXPECT_EQ(0x18, irsend.capture.address); EXPECT_EQ(0x8, irsend.capture.command); - - // Send it again with a single repeat. - irsend.reset(); - irsend.sendPronto(pronto_test, 76, 1); - irsend.makeDecodeResult(); EXPECT_EQ( "m8892s4446" "m546s546m546s546m546s546m546s1664m546s1664m546s546m546s546m546s546" @@ -329,17 +333,17 @@ TEST(TestSendPronto, NormalPlusRepeatSequence) { "m546s1664m546s1664m546s1664m546s546m546s1664m546s1664m546s1664m546s1664" "m546s39858" "m8892s2210m546s95212", irsend.outputStr()); + + // Send it again with a two repeats. + irsend.reset(); + irsend.sendPronto(pronto_test, 76, 2); + irsend.makeDecodeResult(); EXPECT_TRUE(irrecv.decode(&irsend.capture)); EXPECT_EQ(NEC, irsend.capture.decode_type); EXPECT_EQ(NEC_BITS, irsend.capture.bits); EXPECT_EQ(0x18E710EF, irsend.capture.value); EXPECT_EQ(0x18, irsend.capture.address); EXPECT_EQ(0x8, irsend.capture.command); - - // Send it again with a two repeats. - irsend.reset(); - irsend.sendPronto(pronto_test, 76, 2); - irsend.makeDecodeResult(); EXPECT_EQ( "m8892s4446" "m546s546m546s546m546s546m546s1664m546s1664m546s546m546s546m546s546" @@ -349,10 +353,4 @@ TEST(TestSendPronto, NormalPlusRepeatSequence) { "m546s39858" "m8892s2210m546s95212" "m8892s2210m546s95212", irsend.outputStr()); - EXPECT_TRUE(irrecv.decode(&irsend.capture)); - EXPECT_EQ(NEC, irsend.capture.decode_type); - EXPECT_EQ(NEC_BITS, irsend.capture.bits); - EXPECT_EQ(0x18E710EF, irsend.capture.value); - EXPECT_EQ(0x18, irsend.capture.address); - EXPECT_EQ(0x8, irsend.capture.command); } diff --git a/lib/IRremoteESP8266/test/ir_RC5_RC6_test.cpp b/lib/IRremoteESP8266/test/ir_RC5_RC6_test.cpp index ef508e4..2699d33 100644 --- a/lib/IRremoteESP8266/test/ir_RC5_RC6_test.cpp +++ b/lib/IRremoteESP8266/test/ir_RC5_RC6_test.cpp @@ -80,52 +80,52 @@ TEST(TestSendRC5, SendDataOnly) { irsend.sendRC5(0x0, RC5_BITS); EXPECT_EQ( "m889s889m1778s889m889s889m889s889m889s889m889s889m889" - "s889m889s889m889s889m889s889m889s889m889s889m889s114667", + "s889m889s889m889s889m889s889m889s889m889s889m889s90664", irsend.outputStr()); irsend.reset(); irsend.sendRC5(0x1AAA, RC5_BITS); EXPECT_EQ( "m889s889m889s889m1778s1778m1778s1778m1778s1778" - "m1778s1778m1778s1778m1778s114667", irsend.outputStr()); + "m1778s1778m1778s1778m1778s90664", irsend.outputStr()); irsend.reset(); irsend.sendRC5(0x175, RC5_BITS); EXPECT_EQ( "m889s889m1778s889m889s889m889s1778m1778s1778" - "m889s889m889s889m1778s1778m1778s1778m889s113778", irsend.outputStr()); + "m889s889m889s889m1778s1778m1778s1778m889s89775", irsend.outputStr()); irsend.reset(); irsend.sendRC5(0x3FFF, RC5_BITS); EXPECT_EQ( "m889s889m889s889m889s889m889s889m889s889m889s889m889s889" - "m889s889m889s889m889s889m889s889m889s889m889s889m889s113778", + "m889s889m889s889m889s889m889s889m889s889m889s889m889s89775", irsend.outputStr()); irsend.reset(); irsend.sendRC5(0x0, RC5X_BITS); EXPECT_EQ( "m889s889m1778s889m889s889m889s889m889s889m889s889m889" - "s889m889s889m889s889m889s889m889s889m889s889m889s114667", + "s889m889s889m889s889m889s889m889s889m889s889m889s90664", irsend.outputStr()); irsend.reset(); irsend.sendRC5(0x1AAA, RC5X_BITS); EXPECT_EQ( "m1778s1778m1778s1778m1778s1778m1778" - "s1778m1778s1778m1778s1778m1778s114667", irsend.outputStr()); + "s1778m1778s1778m1778s1778m1778s90664", irsend.outputStr()); irsend.reset(); irsend.sendRC5(0x175, RC5X_BITS); EXPECT_EQ( "m889s889m1778s889m889s889m889s1778m1778s1778" - "m889s889m889s889m1778s1778m1778s1778m889s113778", irsend.outputStr()); + "m889s889m889s889m1778s1778m1778s1778m889s89775", irsend.outputStr()); irsend.reset(); irsend.sendRC5(0x3FFF, RC5X_BITS); EXPECT_EQ( "m1778s1778m889s889m889s889m889s889m889s889m889s889m889" - "s889m889s889m889s889m889s889m889s889m889s889m889s113778", + "s889m889s889m889s889m889s889m889s889m889s889m889s89775", irsend.outputStr()); } @@ -138,37 +138,37 @@ TEST(TestSendRC5, SendWithRepeats) { irsend.sendRC5(0x175, RC5_BITS, 1); EXPECT_EQ( "m889s889m1778s889m889s889m889s1778m1778s1778" - "m889s889m889s889m1778s1778m1778s1778m889s114667" + "m889s889m889s889m1778s1778m1778s1778m889s90664" "m889s889m1778s889m889s889m889s1778m1778s1778" - "m889s889m889s889m1778s1778m1778s1778m889s113778", irsend.outputStr()); + "m889s889m889s889m1778s1778m1778s1778m889s88886", irsend.outputStr()); irsend.reset(); irsend.sendRC5(0x175, RC5_BITS, 2); EXPECT_EQ( "m889s889m1778s889m889s889m889s1778m1778s1778" - "m889s889m889s889m1778s1778m1778s1778m889s114667" + "m889s889m889s889m1778s1778m1778s1778m889s90664" "m889s889m1778s889m889s889m889s1778m1778s1778" - "m889s889m889s889m1778s1778m1778s1778m889s114667" + "m889s889m889s889m1778s1778m1778s1778m889s89775" "m889s889m1778s889m889s889m889s1778m1778s1778" - "m889s889m889s889m1778s1778m1778s1778m889s113778", irsend.outputStr()); + "m889s889m889s889m1778s1778m1778s1778m889s88886", irsend.outputStr()); irsend.reset(); irsend.sendRC5(0x175, RC5X_BITS, 1); EXPECT_EQ( "m889s889m1778s889m889s889m889s1778m1778s1778" - "m889s889m889s889m1778s1778m1778s1778m889s114667" + "m889s889m889s889m1778s1778m1778s1778m889s90664" "m889s889m1778s889m889s889m889s1778m1778s1778" - "m889s889m889s889m1778s1778m1778s1778m889s113778", irsend.outputStr()); + "m889s889m889s889m1778s1778m1778s1778m889s88886", irsend.outputStr()); irsend.reset(); irsend.sendRC5(0x1175, RC5X_BITS, 2); EXPECT_EQ( "m1778s889m889s889m889s889m889s1778m1778s1778" - "m889s889m889s889m1778s1778m1778s1778m889s114667" + "m889s889m889s889m1778s1778m1778s1778m889s90664" "m1778s889m889s889m889s889m889s1778m1778s1778" - "m889s889m889s889m1778s1778m1778s1778m889s114667" + "m889s889m889s889m1778s1778m1778s1778m889s89775" "m1778s889m889s889m889s889m889s1778m1778s1778" - "m889s889m889s889m1778s1778m1778s1778m889s113778", irsend.outputStr()); + "m889s889m889s889m1778s1778m1778s1778m889s88886", irsend.outputStr()); } // Tests for decodeRC5(). diff --git a/lib/IRremoteESP8266/test/ir_RCMM_test.cpp b/lib/IRremoteESP8266/test/ir_RCMM_test.cpp index 2115dcb..ad11dd7 100644 --- a/lib/IRremoteESP8266/test/ir_RCMM_test.cpp +++ b/lib/IRremoteESP8266/test/ir_RCMM_test.cpp @@ -16,13 +16,13 @@ TEST(TestSendRCMM, SendDataOnly) { EXPECT_EQ("m416s277" "m166s777m166s611m166s277m166s277" "m166s611m166s611m166s444m166s611m166s277m166s277m166s277m166s277" - "m166s27778", irsend.outputStr()); + "m166s19600", irsend.outputStr()); irsend.reset(); irsend.sendRCMM(0x28e0a600UL, 32); EXPECT_EQ("m416s277" "m166s277m166s611m166s611m166s277m166s777m166s611m166s277m166s277" "m166s611m166s611m166s444m166s611m166s277m166s277m166s277m166s277" - "m166s27778", irsend.outputStr()); + "m166s17160", irsend.outputStr()); } // Test sending with different repeats. @@ -35,19 +35,19 @@ TEST(TestSendRCMM, SendWithRepeats) { EXPECT_EQ("m416s277" "m166s277m166s611m166s611m166s277m166s777m166s611m166s277m166s277" "m166s611m166s611m166s444m166s611m166s277m166s277m166s277m166s277" - "m166s27778" + "m166s17160" "m416s277" "m166s277m166s611m166s611m166s277m166s777m166s611m166s277m166s277" "m166s611m166s611m166s444m166s611m166s277m166s277m166s277m166s277" - "m166s27778" + "m166s17160" "m416s277" "m166s277m166s611m166s611m166s277m166s777m166s611m166s277m166s277" "m166s611m166s611m166s444m166s611m166s277m166s277m166s277m166s277" - "m166s27778", irsend.outputStr()); + "m166s17160", irsend.outputStr()); } // Test sending an atypical data size. -TEST(TestSendRCMM, SendUsualSize) { +TEST(TestSendRCMM, SendUnusualSize) { IRsendTest irsend(4); irsend.begin(); @@ -55,13 +55,13 @@ TEST(TestSendRCMM, SendUsualSize) { irsend.sendRCMM(0xE0, 8); EXPECT_EQ("m416s277" "m166s777m166s611m166s277m166s277" - "m166s27778", irsend.outputStr()); + "m166s24313", irsend.outputStr()); irsend.reset(); irsend.sendRCMM(0x28e0a60000UL, 40); EXPECT_EQ("m416s277" "m166s277m166s611m166s611m166s277m166s777m166s611m166s277m166s277" "m166s611m166s611m166s444m166s611m166s277m166s277m166s277m166s277" - "m166s277m166s277m166s277m166s277m166s27778", irsend.outputStr()); + "m166s277m166s277m166s277m166s277m166s15388", irsend.outputStr()); } // Tests for decodeRCMM(). diff --git a/lib/IRremoteESP8266/test/ir_Samsung_test.cpp b/lib/IRremoteESP8266/test/ir_Samsung_test.cpp index 8c35ebe..5361776 100644 --- a/lib/IRremoteESP8266/test/ir_Samsung_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Samsung_test.cpp @@ -18,7 +18,7 @@ TEST(TestSendSamsung, SendDataOnly) { "m560s560m560s1680m560s1680m560s1680m560s560m560s560m560s560" "m560s560m560s560m560s1680m560s560m560s560m560s1680m560s1680" "m560s560m560s560m560s1680m560s560m560s1680m560s1680m560s560" - "m560s560m560s1680m560s1680m560s560m560s108080", + "m560s560m560s1680m560s1680m560s560m560s47040", irsend.outputStr()); irsend.reset(); @@ -36,13 +36,13 @@ TEST(TestSendSamsung, SendWithRepeats) { "m560s560m560s1680m560s1680m560s1680m560s560m560s560m560s560" "m560s560m560s560m560s1680m560s560m560s560m560s1680m560s1680" "m560s560m560s560m560s1680m560s560m560s1680m560s1680m560s560" - "m560s560m560s1680m560s1680m560s560m560s108080" + "m560s560m560s1680m560s1680m560s560m560s47040" "m4480s4480" "m560s1680m560s1680m560s1680m560s560m560s560m560s560m560s560" "m560s560m560s1680m560s1680m560s1680m560s560m560s560m560s560" "m560s560m560s560m560s1680m560s560m560s560m560s1680m560s1680" "m560s560m560s560m560s1680m560s560m560s1680m560s1680m560s560" - "m560s560m560s1680m560s1680m560s560m560s108080" + "m560s560m560s1680m560s1680m560s560m560s47040" , irsend.outputStr()); irsend.sendSAMSUNG(0xE0E09966, SAMSUNG_BITS, 2); // 2 repeats. EXPECT_EQ("m4480s4480" @@ -50,19 +50,19 @@ TEST(TestSendSamsung, SendWithRepeats) { "m560s560m560s1680m560s1680m560s1680m560s560m560s560m560s560" "m560s560m560s560m560s1680m560s560m560s560m560s1680m560s1680" "m560s560m560s560m560s1680m560s560m560s1680m560s1680m560s560" - "m560s560m560s1680m560s1680m560s560m560s108080" + "m560s560m560s1680m560s1680m560s560m560s47040" "m4480s4480" "m560s1680m560s1680m560s1680m560s560m560s560m560s560m560s560" "m560s560m560s1680m560s1680m560s1680m560s560m560s560m560s560" "m560s560m560s560m560s1680m560s560m560s560m560s1680m560s1680" "m560s560m560s560m560s1680m560s560m560s1680m560s1680m560s560" - "m560s560m560s1680m560s1680m560s560m560s108080" + "m560s560m560s1680m560s1680m560s560m560s47040" "m4480s4480" "m560s1680m560s1680m560s1680m560s560m560s560m560s560m560s560" "m560s560m560s1680m560s1680m560s1680m560s560m560s560m560s560" "m560s560m560s560m560s1680m560s560m560s560m560s1680m560s1680" "m560s560m560s560m560s1680m560s560m560s1680m560s1680m560s560" - "m560s560m560s1680m560s1680m560s560m560s108080" + "m560s560m560s1680m560s1680m560s560m560s47040" , irsend.outputStr()); } diff --git a/lib/IRremoteESP8266/test/ir_Sanyo_test.cpp b/lib/IRremoteESP8266/test/ir_Sanyo_test.cpp index 9442f3d..c04dc1e 100644 --- a/lib/IRremoteESP8266/test/ir_Sanyo_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Sanyo_test.cpp @@ -34,7 +34,7 @@ TEST(TestEncodeSanyoLC7461, SendDataOnly) { "m560s560m560s1680m560s560m560s560m560s1680m560s1680m560s1680m560s1680" "m560s1680m560s1680m560s560m560s560m560s560m560s560m560s560m560s560" "m560s560m560s560m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680" - "m560s1680m560s1680m560s108080", irsend.outputStr()); + "m560s1680m560s1680m560s23520", irsend.outputStr()); } // Test sending with different repeats. @@ -51,8 +51,8 @@ TEST(TestEncodeSanyoLC7461, SendWithRepeats) { "m560s560m560s1680m560s560m560s560m560s1680m560s1680m560s1680m560s1680" "m560s1680m560s1680m560s560m560s560m560s560m560s560m560s560m560s560" "m560s560m560s560m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680" - "m560s1680m560s1680m560s108080" - "m8960s2240m560s108080", irsend.outputStr()); + "m560s1680m560s1680m560s23520" + "m8960s2240m560s96320", irsend.outputStr()); } // Tests for decodeSanyoLC7461(). diff --git a/lib/IRremoteESP8266/test/ir_Sherwood_test.cpp b/lib/IRremoteESP8266/test/ir_Sherwood_test.cpp index b5750b3..a1c622a 100644 --- a/lib/IRremoteESP8266/test/ir_Sherwood_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Sherwood_test.cpp @@ -17,8 +17,8 @@ TEST(TestSendSherwood, SendDataOnly) { "m560s560m560s1680m560s1680m560s560m560s1680m560s560m560s560" "m560s560m560s1680m560s560m560s1680m560s560m560s560m560s560" "m560s1680m560s560m560s560m560s560m560s560m560s1680m560s1680" - "m560s1680m560s560m560s1680m560s1680m560s1680m560s108080" - "m8960s2240m560s108080", irsend.outputStr()); + "m560s1680m560s560m560s1680m560s1680m560s1680m560s42560" + "m8960s2240m560s96320", irsend.outputStr()); } // Test sending typical data with extra repeats. @@ -32,9 +32,9 @@ TEST(TestSendSherwood, SendDataWithRepeats) { "m560s560m560s1680m560s1680m560s560m560s1680m560s560m560s560" "m560s560m560s1680m560s560m560s1680m560s560m560s560m560s560" "m560s1680m560s560m560s560m560s560m560s560m560s1680m560s1680" - "m560s1680m560s560m560s1680m560s1680m560s1680m560s108080" - "m8960s2240m560s108080" - "m8960s2240m560s108080", irsend.outputStr()); + "m560s1680m560s560m560s1680m560s1680m560s1680m560s42560" + "m8960s2240m560s96320" + "m8960s2240m560s96320", irsend.outputStr()); } // Test sending typical data with explicit no repeats. @@ -49,8 +49,8 @@ TEST(TestSendSherwood, SendDataWithZeroRepeats) { "m560s560m560s1680m560s1680m560s560m560s1680m560s560m560s560" "m560s560m560s1680m560s560m560s1680m560s560m560s560m560s560" "m560s1680m560s560m560s560m560s560m560s560m560s1680m560s1680" - "m560s1680m560s560m560s1680m560s1680m560s1680m560s108080" - "m8960s2240m560s108080", irsend.outputStr()); + "m560s1680m560s560m560s1680m560s1680m560s1680m560s42560" + "m8960s2240m560s96320", irsend.outputStr()); } // Test that a typical Sherwood send decodes as the appropriate NEC value. diff --git a/lib/IRremoteESP8266/test/ir_Sony_test.cpp b/lib/IRremoteESP8266/test/ir_Sony_test.cpp index 76d3514..4025eec 100644 --- a/lib/IRremoteESP8266/test/ir_Sony_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Sony_test.cpp @@ -16,49 +16,49 @@ TEST(TestSendSony, SendDataOnly) { // We expect three 20-bit commands to be sent. EXPECT_EQ("m2400s600m600s600m600s600m600s600m600s600m600s600m600s600m600s600" "m600s600m600s600m600s600m600s600m600s600m600s600m600s600m600s600" - "m600s600m600s600m600s600m600s600m600s45600" + "m600s600m600s600m600s600m600s600m600s18600" "m2400s600m600s600m600s600m600s600m600s600m600s600m600s600m600s600" "m600s600m600s600m600s600m600s600m600s600m600s600m600s600m600s600" - "m600s600m600s600m600s600m600s600m600s45600" + "m600s600m600s600m600s600m600s600m600s18600" "m2400s600m600s600m600s600m600s600m600s600m600s600m600s600m600s600" "m600s600m600s600m600s600m600s600m600s600m600s600m600s600m600s600" - "m600s600m600s600m600s600m600s600m600s45600", irsend.outputStr()); + "m600s600m600s600m600s600m600s600m600s18600", irsend.outputStr()); irsend.reset(); irsend.sendSony(0x240C, SONY_20_BITS); // We expect three 20-bit commands to be sent. EXPECT_EQ("m2400s600m600s600m600s600m600s600m600s600m600s600m600s600m1200s600" "m600s600m600s600m1200s600m600s600m600s600m600s600m600s600m600s600" - "m600s600m1200s600m1200s600m600s600m600s45600" + "m600s600m1200s600m1200s600m600s600m600s16200" "m2400s600m600s600m600s600m600s600m600s600m600s600m600s600m1200s600" "m600s600m600s600m1200s600m600s600m600s600m600s600m600s600m600s600" - "m600s600m1200s600m1200s600m600s600m600s45600" + "m600s600m1200s600m1200s600m600s600m600s16200" "m2400s600m600s600m600s600m600s600m600s600m600s600m600s600m1200s600" "m600s600m600s600m1200s600m600s600m600s600m600s600m600s600m600s600" - "m600s600m1200s600m1200s600m600s600m600s45600", irsend.outputStr()); + "m600s600m1200s600m1200s600m600s600m600s16200", irsend.outputStr()); irsend.reset(); irsend.sendSony(0x240C, SONY_15_BITS); // We expect three 15-bit commands to be sent. EXPECT_EQ("m2400s600m600s600m1200s600m600s600m600s600m1200s600m600s600" "m600s600m600s600m600s600m600s600m600s600m1200s600m1200s600m600s600" - "m600s45600" + "m600s22200" "m2400s600m600s600m1200s600m600s600m600s600m1200s600m600s600" "m600s600m600s600m600s600m600s600m600s600m1200s600m1200s600m600s600" - "m600s45600" + "m600s22200" "m2400s600m600s600m1200s600m600s600m600s600m1200s600m600s600" "m600s600m600s600m600s600m600s600m600s600m1200s600m1200s600m600s600" - "m600s45600", irsend.outputStr()); + "m600s22200", irsend.outputStr()); irsend.reset(); irsend.sendSony(0xA90, SONY_12_BITS); // We expect three 15-bit commands to be sent. EXPECT_EQ("m2400s600m1200s600m600s600m1200s600m600s600m1200s600m600s600" - "m600s600m1200s600m600s600m600s600m600s600m600s45600" + "m600s600m1200s600m600s600m600s600m600s600m600s25800" "m2400s600m1200s600m600s600m1200s600m600s600m1200s600m600s600" - "m600s600m1200s600m600s600m600s600m600s600m600s45600" + "m600s600m1200s600m600s600m600s600m600s600m600s25800" "m2400s600m1200s600m600s600m1200s600m600s600m1200s600m600s600" - "m600s600m1200s600m600s600m600s600m600s600m600s45600", + "m600s600m1200s600m600s600m600s600m600s600m600s25800", irsend.outputStr()); } @@ -71,27 +71,27 @@ TEST(TestSendSony, SendWithDiffRepeats) { irsend.sendSony(0x240C, SONY_20_BITS, 0); // Send a command with 0 repeats. EXPECT_EQ("m2400s600m600s600m600s600m600s600m600s600m600s600m600s600m1200s600" "m600s600m600s600m1200s600m600s600m600s600m600s600m600s600m600s600" - "m600s600m1200s600m1200s600m600s600m600s45600", irsend.outputStr()); + "m600s600m1200s600m1200s600m600s600m600s16200", irsend.outputStr()); irsend.sendSony(0x240C, SONY_20_BITS, 1); // Send a command with 1 repeat. EXPECT_EQ("m2400s600m600s600m600s600m600s600m600s600m600s600m600s600m1200s600" "m600s600m600s600m1200s600m600s600m600s600m600s600m600s600m600s600" - "m600s600m1200s600m1200s600m600s600m600s45600" + "m600s600m1200s600m1200s600m600s600m600s16200" "m2400s600m600s600m600s600m600s600m600s600m600s600m600s600m1200s600" "m600s600m600s600m1200s600m600s600m600s600m600s600m600s600m600s600" - "m600s600m1200s600m1200s600m600s600m600s45600", irsend.outputStr()); + "m600s600m1200s600m1200s600m600s600m600s16200", irsend.outputStr()); irsend.sendSony(0x240C, SONY_20_BITS, 3); // Send a command with 3 repeats. EXPECT_EQ("m2400s600m600s600m600s600m600s600m600s600m600s600m600s600m1200s600" "m600s600m600s600m1200s600m600s600m600s600m600s600m600s600m600s600" - "m600s600m1200s600m1200s600m600s600m600s45600" + "m600s600m1200s600m1200s600m600s600m600s16200" "m2400s600m600s600m600s600m600s600m600s600m600s600m600s600m1200s600" "m600s600m600s600m1200s600m600s600m600s600m600s600m600s600m600s600" - "m600s600m1200s600m1200s600m600s600m600s45600" + "m600s600m1200s600m1200s600m600s600m600s16200" "m2400s600m600s600m600s600m600s600m600s600m600s600m600s600m1200s600" "m600s600m600s600m1200s600m600s600m600s600m600s600m600s600m600s600" - "m600s600m1200s600m1200s600m600s600m600s45600" + "m600s600m1200s600m1200s600m600s600m600s16200" "m2400s600m600s600m600s600m600s600m600s600m600s600m600s600m1200s600" "m600s600m600s600m1200s600m600s600m600s600m600s600m600s600m600s600" - "m600s600m1200s600m1200s600m600s600m600s45600", irsend.outputStr()); + "m600s600m1200s600m1200s600m600s600m600s16200", irsend.outputStr()); } // Tests for encodeSony(). diff --git a/lib/IRremoteESP8266/test/ir_Toshiba_test.cpp b/lib/IRremoteESP8266/test/ir_Toshiba_test.cpp new file mode 100644 index 0000000..7411198 --- /dev/null +++ b/lib/IRremoteESP8266/test/ir_Toshiba_test.cpp @@ -0,0 +1,658 @@ +// Copyright 2017 David Conran +#include "IRrecv.h" +#include "IRrecv_test.h" +#include "IRsend.h" +#include "IRsend_test.h" +#include "ir_Toshiba.h" +#include "gtest/gtest.h" + +// Tests for Toshiba A/C methods. + +// Test sending typical data only. +TEST(TestSendToshibaAC, SendDataOnly) { + IRsendTest irsend(4); + irsend.begin(); + + uint8_t toshiba_code[TOSHIBA_AC_STATE_LENGTH] = { + 0xF2, 0x0D, 0x03, 0xFC, 0x01, 0x00, 0x00, 0x00, 0x00}; + irsend.reset(); + irsend.sendToshibaAC(toshiba_code); + EXPECT_EQ( + "m4400s4300" + "m543s1623m543s1623m543s1623m543s1623m543s472m543s472m543s1623m543s472" + "m543s472m543s472m543s472m543s472m543s1623m543s1623m543s472m543s1623" + "m543s472m543s472m543s472m543s472m543s472m543s472m543s1623m543s1623" + "m543s1623m543s1623m543s1623m543s1623m543s1623m543s1623m543s472m543s472" + "m543s472m543s472m543s472m543s472m543s472m543s472m543s472m543s1623" + "m543s472m543s472m543s472m543s472m543s472m543s472m543s472m543s472" + "m543s472m543s472m543s472m543s472m543s472m543s472m543s472m543s472" + "m543s472m543s472m543s472m543s472m543s472m543s472m543s472m543s472" + "m543s472m543s472m543s472m543s472m543s472m543s472m543s472m543s472" + "m543s7048" + "m4400s4300" + "m543s1623m543s1623m543s1623m543s1623m543s472m543s472m543s1623m543s472" + "m543s472m543s472m543s472m543s472m543s1623m543s1623m543s472m543s1623" + "m543s472m543s472m543s472m543s472m543s472m543s472m543s1623m543s1623" + "m543s1623m543s1623m543s1623m543s1623m543s1623m543s1623m543s472m543s472" + "m543s472m543s472m543s472m543s472m543s472m543s472m543s472m543s1623" + "m543s472m543s472m543s472m543s472m543s472m543s472m543s472m543s472" + "m543s472m543s472m543s472m543s472m543s472m543s472m543s472m543s472" + "m543s472m543s472m543s472m543s472m543s472m543s472m543s472m543s472" + "m543s472m543s472m543s472m543s472m543s472m543s472m543s472m543s472" + "m543s7048", irsend.outputStr()); +} + +// Test sending with repeats. +TEST(TestSendToshibaAC, SendWithRepeats) { + IRsendTest irsend(4); + irsend.begin(); + + irsend.reset(); + uint8_t toshiba_code[TOSHIBA_AC_STATE_LENGTH] = { + 0xF2, 0x0D, 0x03, 0xFC, 0x01, 0x00, 0x00, 0x00, 0x00}; + + irsend.sendToshibaAC(toshiba_code, TOSHIBA_AC_STATE_LENGTH, 0); + EXPECT_EQ( + "m4400s4300" + "m543s1623m543s1623m543s1623m543s1623m543s472m543s472m543s1623m543s472" + "m543s472m543s472m543s472m543s472m543s1623m543s1623m543s472m543s1623" + "m543s472m543s472m543s472m543s472m543s472m543s472m543s1623m543s1623" + "m543s1623m543s1623m543s1623m543s1623m543s1623m543s1623m543s472m543s472" + "m543s472m543s472m543s472m543s472m543s472m543s472m543s472m543s1623" + "m543s472m543s472m543s472m543s472m543s472m543s472m543s472m543s472" + "m543s472m543s472m543s472m543s472m543s472m543s472m543s472m543s472" + "m543s472m543s472m543s472m543s472m543s472m543s472m543s472m543s472" + "m543s472m543s472m543s472m543s472m543s472m543s472m543s472m543s472" + "m543s7048", irsend.outputStr()); + + irsend.reset(); + irsend.sendToshibaAC(toshiba_code, TOSHIBA_AC_STATE_LENGTH, 2); + EXPECT_EQ( + "m4400s4300" + "m543s1623m543s1623m543s1623m543s1623m543s472m543s472m543s1623m543s472" + "m543s472m543s472m543s472m543s472m543s1623m543s1623m543s472m543s1623" + "m543s472m543s472m543s472m543s472m543s472m543s472m543s1623m543s1623" + "m543s1623m543s1623m543s1623m543s1623m543s1623m543s1623m543s472m543s472" + "m543s472m543s472m543s472m543s472m543s472m543s472m543s472m543s1623" + "m543s472m543s472m543s472m543s472m543s472m543s472m543s472m543s472" + "m543s472m543s472m543s472m543s472m543s472m543s472m543s472m543s472" + "m543s472m543s472m543s472m543s472m543s472m543s472m543s472m543s472" + "m543s472m543s472m543s472m543s472m543s472m543s472m543s472m543s472" + "m543s7048" + "m4400s4300" + "m543s1623m543s1623m543s1623m543s1623m543s472m543s472m543s1623m543s472" + "m543s472m543s472m543s472m543s472m543s1623m543s1623m543s472m543s1623" + "m543s472m543s472m543s472m543s472m543s472m543s472m543s1623m543s1623" + "m543s1623m543s1623m543s1623m543s1623m543s1623m543s1623m543s472m543s472" + "m543s472m543s472m543s472m543s472m543s472m543s472m543s472m543s1623" + "m543s472m543s472m543s472m543s472m543s472m543s472m543s472m543s472" + "m543s472m543s472m543s472m543s472m543s472m543s472m543s472m543s472" + "m543s472m543s472m543s472m543s472m543s472m543s472m543s472m543s472" + "m543s472m543s472m543s472m543s472m543s472m543s472m543s472m543s472" + "m543s7048" + "m4400s4300" + "m543s1623m543s1623m543s1623m543s1623m543s472m543s472m543s1623m543s472" + "m543s472m543s472m543s472m543s472m543s1623m543s1623m543s472m543s1623" + "m543s472m543s472m543s472m543s472m543s472m543s472m543s1623m543s1623" + "m543s1623m543s1623m543s1623m543s1623m543s1623m543s1623m543s472m543s472" + "m543s472m543s472m543s472m543s472m543s472m543s472m543s472m543s1623" + "m543s472m543s472m543s472m543s472m543s472m543s472m543s472m543s472" + "m543s472m543s472m543s472m543s472m543s472m543s472m543s472m543s472" + "m543s472m543s472m543s472m543s472m543s472m543s472m543s472m543s472" + "m543s472m543s472m543s472m543s472m543s472m543s472m543s472m543s472" + "m543s7048", irsend.outputStr()); +} + +// Test sending atypical sizes. +TEST(TestSendToshibaAC, SendUnexpectedSizes) { + IRsendTest irsend(4); + irsend.begin(); + + uint8_t toshiba_short_code[8] = {0x01, 0x02, 0x03, 0x04, + 0x05, 0x06, 0x07, 0x08}; + uint8_t toshiba_long_code[10] = {0x01, 0x02, 0x03, 0x04, 0x05, + 0x06, 0x07, 0x08, 0x09, 0x0A}; + irsend.reset(); + irsend.sendToshibaAC(toshiba_short_code, TOSHIBA_AC_STATE_LENGTH - 1); + ASSERT_EQ("", irsend.outputStr()); + + irsend.reset(); + irsend.sendToshibaAC(toshiba_long_code, TOSHIBA_AC_STATE_LENGTH + 1); + ASSERT_EQ( + "m4400s4300" + "m543s472m543s472m543s472m543s472m543s472m543s472m543s472m543s1623" + "m543s472m543s472m543s472m543s472m543s472m543s472m543s1623m543s472" + "m543s472m543s472m543s472m543s472m543s472m543s472m543s1623m543s1623" + "m543s472m543s472m543s472m543s472m543s472m543s1623m543s472m543s472" + "m543s472m543s472m543s472m543s472m543s472m543s1623m543s472m543s1623" + "m543s472m543s472m543s472m543s472m543s472m543s1623m543s1623m543s472" + "m543s472m543s472m543s472m543s472m543s472m543s1623m543s1623m543s1623" + "m543s472m543s472m543s472m543s472m543s1623m543s472m543s472m543s472" + "m543s472m543s472m543s472m543s472m543s1623m543s472m543s472m543s1623" + "m543s472m543s472m543s472m543s472m543s1623m543s472m543s1623m543s472" + "m543s7048" + "m4400s4300" + "m543s472m543s472m543s472m543s472m543s472m543s472m543s472m543s1623" + "m543s472m543s472m543s472m543s472m543s472m543s472m543s1623m543s472" + "m543s472m543s472m543s472m543s472m543s472m543s472m543s1623m543s1623" + "m543s472m543s472m543s472m543s472m543s472m543s1623m543s472m543s472" + "m543s472m543s472m543s472m543s472m543s472m543s1623m543s472m543s1623" + "m543s472m543s472m543s472m543s472m543s472m543s1623m543s1623m543s472" + "m543s472m543s472m543s472m543s472m543s472m543s1623m543s1623m543s1623" + "m543s472m543s472m543s472m543s472m543s1623m543s472m543s472m543s472" + "m543s472m543s472m543s472m543s472m543s1623m543s472m543s472m543s1623" + "m543s472m543s472m543s472m543s472m543s1623m543s472m543s1623m543s472" + "m543s7048", irsend.outputStr()); +} + +// Tests for IRToshibaAC class. + +TEST(TestToshibaACClass, Power) { + IRToshibaAC toshiba(0); + toshiba.begin(); + + toshiba.on(); + EXPECT_TRUE(toshiba.getPower()); + + toshiba.off(); + EXPECT_FALSE(toshiba.getPower()); + + toshiba.setPower(true); + EXPECT_TRUE(toshiba.getPower()); + + toshiba.setPower(false); + EXPECT_FALSE(toshiba.getPower()); +} + +TEST(TestToshibaACClass, Temperature) { + IRToshibaAC toshiba(0); + toshiba.begin(); + + toshiba.setTemp(0); + EXPECT_EQ(TOSHIBA_AC_MIN_TEMP, toshiba.getTemp()); + + toshiba.setTemp(255); + EXPECT_EQ(TOSHIBA_AC_MAX_TEMP, toshiba.getTemp()); + + toshiba.setTemp(TOSHIBA_AC_MIN_TEMP); + EXPECT_EQ(TOSHIBA_AC_MIN_TEMP, toshiba.getTemp()); + + toshiba.setTemp(TOSHIBA_AC_MAX_TEMP); + EXPECT_EQ(TOSHIBA_AC_MAX_TEMP, toshiba.getTemp()); + + toshiba.setTemp(TOSHIBA_AC_MIN_TEMP - 1); + EXPECT_EQ(TOSHIBA_AC_MIN_TEMP, toshiba.getTemp()); + + toshiba.setTemp(TOSHIBA_AC_MAX_TEMP + 1); + EXPECT_EQ(TOSHIBA_AC_MAX_TEMP, toshiba.getTemp()); + + toshiba.setTemp(17); + EXPECT_EQ(17, toshiba.getTemp()); + + toshiba.setTemp(21); + EXPECT_EQ(21, toshiba.getTemp()); + + toshiba.setTemp(25); + EXPECT_EQ(25, toshiba.getTemp()); + + toshiba.setTemp(30); + EXPECT_EQ(30, toshiba.getTemp()); +} + +TEST(TestToshibaACClass, OperatingMode) { + IRToshibaAC toshiba(0); + toshiba.begin(); + + toshiba.setMode(TOSHIBA_AC_AUTO); + EXPECT_EQ(TOSHIBA_AC_AUTO, toshiba.getMode()); + + toshiba.setMode(TOSHIBA_AC_COOL); + EXPECT_EQ(TOSHIBA_AC_COOL, toshiba.getMode()); + + toshiba.setMode(TOSHIBA_AC_HEAT); + EXPECT_EQ(TOSHIBA_AC_HEAT, toshiba.getMode()); + + toshiba.setMode(TOSHIBA_AC_DRY); + EXPECT_EQ(TOSHIBA_AC_DRY, toshiba.getMode()); + + toshiba.setMode(TOSHIBA_AC_HEAT + 1); + EXPECT_EQ(TOSHIBA_AC_AUTO, toshiba.getMode()); + + toshiba.setMode(255); + EXPECT_EQ(TOSHIBA_AC_AUTO, toshiba.getMode()); + + // Setting the power off changes the underlying mode in the state to heat. + toshiba.setPower(true); + toshiba.setMode(TOSHIBA_AC_COOL); + EXPECT_EQ(TOSHIBA_AC_COOL, toshiba.getMode()); + EXPECT_EQ(TOSHIBA_AC_COOL, toshiba.getMode(true)); + toshiba.setPower(false); + EXPECT_EQ(TOSHIBA_AC_COOL, toshiba.getMode()); + EXPECT_EQ(TOSHIBA_AC_HEAT, toshiba.getMode(true)); +} + +TEST(TestToshibaACClass, FanSpeed) { + IRToshibaAC toshiba(0); + toshiba.begin(); + + toshiba.setFan(TOSHIBA_AC_FAN_AUTO); + EXPECT_EQ(TOSHIBA_AC_FAN_AUTO, toshiba.getFan()); + + toshiba.setFan(255); + EXPECT_EQ(TOSHIBA_AC_FAN_MAX, toshiba.getFan()); + + toshiba.setFan(TOSHIBA_AC_FAN_MAX); + EXPECT_EQ(TOSHIBA_AC_FAN_MAX, toshiba.getFan()); + + toshiba.setFan(TOSHIBA_AC_FAN_MAX - 1); + EXPECT_EQ(TOSHIBA_AC_FAN_MAX - 1, toshiba.getFan()); + + toshiba.setFan(1); + EXPECT_EQ(1, toshiba.getFan()); + + toshiba.setFan(2); + EXPECT_EQ(2, toshiba.getFan()); + + toshiba.setFan(3); + EXPECT_EQ(3, toshiba.getFan()); + + toshiba.setFan(4); + EXPECT_EQ(4, toshiba.getFan()); + + toshiba.setFan(TOSHIBA_AC_FAN_MAX + 1); + EXPECT_EQ(TOSHIBA_AC_FAN_MAX, toshiba.getFan()); +} + +TEST(TestToshibaACClass, RawState) { + IRToshibaAC toshiba(0); + toshiba.begin(); + + uint8_t initial_state[TOSHIBA_AC_STATE_LENGTH] = { + 0xF2, 0x0D, 0x03, 0xFC, 0x01, 0x00, 0x00, 0x00, 0x01}; + uint8_t modified_state[TOSHIBA_AC_STATE_LENGTH] = { + 0xF2, 0x0D, 0x03, 0xFC, 0x01, 0x00, 0xC1, 0x00, 0xC0}; + + // Verify the starting state. + EXPECT_STATE_EQ(initial_state, toshiba.getRaw(), TOSHIBA_AC_BITS); + EXPECT_TRUE(toshiba.getPower()); + EXPECT_EQ(TOSHIBA_AC_AUTO, toshiba.getMode()); + EXPECT_EQ(TOSHIBA_AC_FAN_AUTO, toshiba.getFan()); + + // Change some settings. + toshiba.setMode(TOSHIBA_AC_COOL); + toshiba.setFan(TOSHIBA_AC_FAN_MAX); + toshiba.setTemp(TOSHIBA_AC_MIN_TEMP); + // Verify those were set. + EXPECT_EQ(TOSHIBA_AC_COOL, toshiba.getMode()); + EXPECT_EQ(TOSHIBA_AC_FAN_MAX, toshiba.getFan()); + EXPECT_EQ(TOSHIBA_AC_MIN_TEMP, toshiba.getTemp()); + // Retrieve the modified state. + EXPECT_STATE_EQ(modified_state, toshiba.getRaw(), TOSHIBA_AC_BITS); + + // Set it back to the initial state. + toshiba.setRaw(initial_state); + + // Check the new state was set correctly. + EXPECT_TRUE(toshiba.getPower()); + EXPECT_EQ(TOSHIBA_AC_AUTO, toshiba.getMode()); + EXPECT_EQ(TOSHIBA_AC_FAN_AUTO, toshiba.getFan()); + EXPECT_STATE_EQ(initial_state, toshiba.getRaw(), TOSHIBA_AC_BITS); +} + +TEST(TestToshibaACClass, Checksums) { + IRToshibaAC toshiba(0); + toshiba.begin(); + + uint8_t initial_state[TOSHIBA_AC_STATE_LENGTH] = { + 0xF2, 0x0D, 0x03, 0xFC, 0x01, 0x00, 0x00, 0x00, 0x01}; + uint8_t modified_state[TOSHIBA_AC_STATE_LENGTH] = { + 0xF2, 0x0D, 0x03, 0xFC, 0x01, 0x00, 0xC1, 0x00, 0xC0}; + uint8_t invalid_state[TOSHIBA_AC_STATE_LENGTH] = { + 0xF2, 0x0D, 0x03, 0xFC, 0x01, 0x00, 0x00, 0x00, 0x00}; + + EXPECT_EQ(0x01, toshiba.calcChecksum(initial_state)); + EXPECT_EQ(0xC0, toshiba.calcChecksum(modified_state)); + // Check we can call it without instantiating the object. + EXPECT_EQ(0x01, IRToshibaAC::calcChecksum(initial_state)); + // Use different lengths. + EXPECT_EQ(0x01, IRToshibaAC::calcChecksum(initial_state, + TOSHIBA_AC_STATE_LENGTH - 1)); + EXPECT_EQ(0xFF, IRToshibaAC::calcChecksum(initial_state, 3)); + // Minimum length that actually means anything. + EXPECT_EQ(0xF2, IRToshibaAC::calcChecksum(initial_state, 2)); + // Technically, there is no such thing as a checksum for a length of < 2 + // But test it anyway + EXPECT_EQ(0x00, IRToshibaAC::calcChecksum(initial_state, 1)); + EXPECT_EQ(0x00, IRToshibaAC::calcChecksum(initial_state, 0)); + + // Validity tests. + EXPECT_TRUE(IRToshibaAC::validChecksum(initial_state)); + EXPECT_TRUE(IRToshibaAC::validChecksum(modified_state)); + EXPECT_FALSE(IRToshibaAC::validChecksum(invalid_state)); + EXPECT_FALSE(IRToshibaAC::validChecksum(initial_state, 0)); + EXPECT_FALSE(IRToshibaAC::validChecksum(initial_state, 1)); + EXPECT_FALSE(IRToshibaAC::validChecksum(initial_state, 2)); +} + +TEST(TestToshibaACClass, HumanReadableOutput) { + IRToshibaAC toshiba(0); + toshiba.begin(); + + uint8_t initial_state[TOSHIBA_AC_STATE_LENGTH] = { + 0xF2, 0x0D, 0x03, 0xFC, 0x01, 0x00, 0x00, 0x00, 0x01}; + uint8_t modified_state[TOSHIBA_AC_STATE_LENGTH] = { + 0xF2, 0x0D, 0x03, 0xFC, 0x01, 0x00, 0xC1, 0x00, 0xC0}; + + toshiba.setRaw(initial_state); + EXPECT_EQ("Power: On, Mode: 0 (AUTO), Temp: 17C, Fan: 0 (AUTO)", + toshiba.toString()); + toshiba.setRaw(modified_state); + EXPECT_EQ("Power: On, Mode: 1 (COOL), Temp: 17C, Fan: 5 (MAX)", + toshiba.toString()); + toshiba.off(); + toshiba.setTemp(25); + toshiba.setFan(3); + toshiba.setMode(TOSHIBA_AC_DRY); + EXPECT_EQ("Power: Off, Mode: 2 (DRY), Temp: 25C, Fan: 3", + toshiba.toString()); +} + +TEST(TestToshibaACClass, MessageConstuction) { + IRToshibaAC toshiba(0); + IRsendTest irsend(4); + toshiba.begin(); + irsend.begin(); + + toshiba.setFan(1); + toshiba.setMode(TOSHIBA_AC_COOL); + toshiba.setTemp(27); + toshiba.on(); + + // Check everything for kicks. + EXPECT_EQ(1, toshiba.getFan()); + EXPECT_EQ(TOSHIBA_AC_COOL, toshiba.getMode()); + EXPECT_EQ(27, toshiba.getTemp()); + EXPECT_TRUE(toshiba.getPower()); + + irsend.reset(); + irsend.sendToshibaAC(toshiba.getRaw()); + EXPECT_EQ( + "m4400s4300" + "m543s1623m543s1623m543s1623m543s1623m543s472m543s472m543s1623m543s472" + "m543s472m543s472m543s472m543s472m543s1623m543s1623m543s472m543s1623" + "m543s472m543s472m543s472m543s472m543s472m543s472m543s1623m543s1623" + "m543s1623m543s1623m543s1623m543s1623m543s1623m543s1623m543s472m543s472" + "m543s472m543s472m543s472m543s472m543s472m543s472m543s472m543s1623" + "m543s1623m543s472m543s1623m543s472m543s472m543s472m543s472m543s472" + "m543s472m543s1623m543s472m543s472m543s472m543s472m543s472m543s1623" + "m543s472m543s472m543s472m543s472m543s472m543s472m543s472m543s472" + "m543s1623m543s1623m543s1623m543s472m543s472m543s472m543s472m543s472" + "m543s7048" + "m4400s4300" + "m543s1623m543s1623m543s1623m543s1623m543s472m543s472m543s1623m543s472" + "m543s472m543s472m543s472m543s472m543s1623m543s1623m543s472m543s1623" + "m543s472m543s472m543s472m543s472m543s472m543s472m543s1623m543s1623" + "m543s1623m543s1623m543s1623m543s1623m543s1623m543s1623m543s472m543s472" + "m543s472m543s472m543s472m543s472m543s472m543s472m543s472m543s1623" + "m543s1623m543s472m543s1623m543s472m543s472m543s472m543s472m543s472" + "m543s472m543s1623m543s472m543s472m543s472m543s472m543s472m543s1623" + "m543s472m543s472m543s472m543s472m543s472m543s472m543s472m543s472" + "m543s1623m543s1623m543s1623m543s472m543s472m543s472m543s472m543s472" + "m543s7048", irsend.outputStr()); + + // Turn off the power and re-check. + toshiba.setPower(false); + // Check everything for kicks. + EXPECT_EQ(1, toshiba.getFan()); + EXPECT_EQ(TOSHIBA_AC_COOL, toshiba.getMode()); + EXPECT_EQ(27, toshiba.getTemp()); + EXPECT_FALSE(toshiba.getPower()); + + irsend.reset(); + irsend.sendToshibaAC(toshiba.getRaw()); + EXPECT_EQ( + "m4400s4300" + "m543s1623m543s1623m543s1623m543s1623m543s472m543s472m543s1623m543s472" + "m543s472m543s472m543s472m543s472m543s1623m543s1623m543s472m543s1623" + "m543s472m543s472m543s472m543s472m543s472m543s472m543s1623m543s1623" + "m543s1623m543s1623m543s1623m543s1623m543s1623m543s1623m543s472m543s472" + "m543s472m543s472m543s472m543s472m543s472m543s472m543s472m543s1623" + "m543s1623m543s472m543s1623m543s472m543s472m543s472m543s472m543s472" + "m543s472m543s1623m543s472m543s472m543s472m543s1623m543s1623m543s1623" + "m543s472m543s472m543s472m543s472m543s472m543s472m543s472m543s472" + "m543s1623m543s1623m543s1623m543s472m543s472m543s1623m543s1623m543s472" + "m543s7048" + "m4400s4300" + "m543s1623m543s1623m543s1623m543s1623m543s472m543s472m543s1623m543s472" + "m543s472m543s472m543s472m543s472m543s1623m543s1623m543s472m543s1623" + "m543s472m543s472m543s472m543s472m543s472m543s472m543s1623m543s1623" + "m543s1623m543s1623m543s1623m543s1623m543s1623m543s1623m543s472m543s472" + "m543s472m543s472m543s472m543s472m543s472m543s472m543s472m543s1623" + "m543s1623m543s472m543s1623m543s472m543s472m543s472m543s472m543s472" + "m543s472m543s1623m543s472m543s472m543s472m543s1623m543s1623m543s1623" + "m543s472m543s472m543s472m543s472m543s472m543s472m543s472m543s472" + "m543s1623m543s1623m543s1623m543s472m543s472m543s1623m543s1623m543s472" + "m543s7048", irsend.outputStr()); + + // Turn the power back on, and check nothing changed. + toshiba.on(); + + EXPECT_EQ(1, toshiba.getFan()); + EXPECT_EQ(TOSHIBA_AC_COOL, toshiba.getMode()); + EXPECT_EQ(27, toshiba.getTemp()); + EXPECT_TRUE(toshiba.getPower()); + + irsend.reset(); + irsend.sendToshibaAC(toshiba.getRaw()); + EXPECT_EQ( + "m4400s4300" + "m543s1623m543s1623m543s1623m543s1623m543s472m543s472m543s1623m543s472" + "m543s472m543s472m543s472m543s472m543s1623m543s1623m543s472m543s1623" + "m543s472m543s472m543s472m543s472m543s472m543s472m543s1623m543s1623" + "m543s1623m543s1623m543s1623m543s1623m543s1623m543s1623m543s472m543s472" + "m543s472m543s472m543s472m543s472m543s472m543s472m543s472m543s1623" + "m543s1623m543s472m543s1623m543s472m543s472m543s472m543s472m543s472" + "m543s472m543s1623m543s472m543s472m543s472m543s472m543s472m543s1623" + "m543s472m543s472m543s472m543s472m543s472m543s472m543s472m543s472" + "m543s1623m543s1623m543s1623m543s472m543s472m543s472m543s472m543s472" + "m543s7048" + "m4400s4300" + "m543s1623m543s1623m543s1623m543s1623m543s472m543s472m543s1623m543s472" + "m543s472m543s472m543s472m543s472m543s1623m543s1623m543s472m543s1623" + "m543s472m543s472m543s472m543s472m543s472m543s472m543s1623m543s1623" + "m543s1623m543s1623m543s1623m543s1623m543s1623m543s1623m543s472m543s472" + "m543s472m543s472m543s472m543s472m543s472m543s472m543s472m543s1623" + "m543s1623m543s472m543s1623m543s472m543s472m543s472m543s472m543s472" + "m543s472m543s1623m543s472m543s472m543s472m543s472m543s472m543s1623" + "m543s472m543s472m543s472m543s472m543s472m543s472m543s472m543s472" + "m543s1623m543s1623m543s1623m543s472m543s472m543s472m543s472m543s472" + "m543s7048", irsend.outputStr()); +} + +// Decoding a message we entirely constructed based solely on a given state. +TEST(TestDecodeToshibaAC, SyntheticExample) { + IRsendTest irsend(4); + IRrecv irrecv(4); + irsend.begin(); + + uint8_t expectedState[TOSHIBA_AC_STATE_LENGTH] = { + 0xF2, 0x0D, 0x03, 0xFC, 0x01, 0x00, 0x00, 0x00, 0x01}; + + irsend.reset(); + irsend.sendToshibaAC(expectedState); + irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(TOSHIBA_AC, irsend.capture.decode_type); + ASSERT_EQ(TOSHIBA_AC_BITS, irsend.capture.bits); + EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits); +} + +// Test decoding against captures from a real Toshiba A/C remote. +// Recorded by @mwildbolz +TEST(TestDecodeToshibaAC, RealExamples) { + IRToshibaAC toshiba(0); + IRsendTest irsend(4); + IRrecv irrecv(4); + irsend.begin(); + + uint16_t rawData1[295] = { + 4442, 4292, 612, 1544, 616, 1544, 616, 1544, 612, 1548, 610, 468, + 612, 468, 662, 1494, 640, 438, 616, 464, 614, 464, 616, 464, + 612, 468, 610, 1544, 616, 1544, 616, 468, 612, 1544, 616, 464, + 694, 386, 616, 464, 612, 468, 612, 468, 636, 444, 610, 1546, + 616, 1544, 612, 1546, 614, 1546, 616, 1546, 740, 1420, 612, 1544, + 616, 1546, 616, 464, 610, 468, 610, 470, 612, 468, 610, 468, 610, + 470, 636, 438, 616, 464, 616, 464, 616, 1546, 636, 442, 612, 1546, + 614, 1544, 616, 464, 614, 464, 610, 468, 612, 468, 612, 468, + 612, 468, 636, 440, 614, 464, 616, 464, 616, 464, 612, 468, + 636, 442, 638, 442, 662, 418, 610, 464, 616, 464, 616, 464, + 610, 468, 612, 468, 636, 444, 610, 468, 638, 438, 614, 1546, + 612, 1548, 612, 470, 610, 468, 636, 442, 612, 468, 612, 1544, + 612, 7396, 4442, 4292, 610, 1546, 616, 1544, 612, 1548, 612, 1546, + 616, 464, 616, 464, 616, 1544, 612, 468, 662, 418, 610, 468, + 638, 442, 638, 438, 616, 1546, 616, 1544, 612, 468, 610, 1546, + 616, 464, 616, 464, 642, 438, 616, 464, 612, 468, 610, 470, + 610, 1546, 616, 1544, 612, 1546, 616, 1546, 614, 1546, 612, 1550, + 610, 1544, 616, 1546, 614, 464, 642, 438, 610, 468, 612, 468, + 612, 468, 612, 468, 610, 468, 638, 438, 614, 464, 616, 1544, + 636, 444, 636, 1520, 616, 1544, 616, 464, 616, 464, 612, 468, + 612, 468, 612, 468, 612, 468, 612, 464, 612, 470, 636, 442, + 638, 442, 612, 470, 692, 384, 614, 464, 616, 464, 612, 468, + 610, 468, 612, 468, 610, 470, 610, 464, 616, 464, 616, 464, + 616, 464, 610, 1550, 610, 1546, 640, 444, 688, 386, 616, 464, + 612, 468, 612, 1544, 642}; + + irsend.reset(); + irsend.sendRaw(rawData1, 295, 38000); + irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(TOSHIBA_AC, irsend.capture.decode_type); + ASSERT_EQ(TOSHIBA_AC_BITS, irsend.capture.bits); + toshiba.setRaw(irsend.capture.state); + EXPECT_TRUE(toshiba.getPower()); + EXPECT_EQ(23, toshiba.getTemp()); + EXPECT_EQ(TOSHIBA_AC_FAN_AUTO, toshiba.getFan()); + EXPECT_EQ(TOSHIBA_AC_AUTO, toshiba.getMode()); + + uint16_t rawData2[295] = { + 4500, 4236, 636, 1520, 642, 1520, 640, 1520, 664, 1492, 642, 440, + 668, 412, 642, 1518, 638, 438, 666, 414, 640, 438, 642, 438, + 638, 442, 642, 1516, 640, 1520, 642, 438, 642, 1520, 636, 438, + 668, 412, 640, 440, 666, 412, 642, 438, 668, 412, 640, 1516, + 668, 1492, 642, 1520, 666, 1494, 638, 1520, 642, 1520, 668, 1490, + 666, 1494, 642, 438, 638, 438, 668, 412, 668, 412, 642, 438, + 642, 438, 664, 412, 642, 438, 642, 438, 642, 1518, 642, 434, + 668, 412, 642, 438, 668, 412, 692, 388, 666, 412, 642, 434, + 642, 438, 642, 1518, 668, 412, 668, 412, 640, 438, 638, 438, + 642, 438, 640, 438, 668, 1492, 642, 440, 666, 412, 640, 438, + 642, 438, 642, 434, 668, 412, 668, 412, 666, 414, 666, 1494, + 640, 438, 642, 434, 668, 412, 642, 438, 642, 438, 668, 412, + 668, 414, 640, 7362, 4474, 4262, 642, 1518, 638, 1520, 640, 1520, + 668, 1494, 640, 434, 642, 438, 640, 1520, 642, 438, 642, 438, + 642, 438, 642, 438, 642, 434, 668, 1494, 642, 1518, 638, 442, + 638, 1520, 642, 438, 642, 438, 668, 414, 664, 408, 642, 438, + 668, 412, 642, 1520, 666, 1494, 642, 1514, 642, 1518, 642, 1520, + 636, 1520, 668, 1494, 666, 1494, 638, 438, 666, 414, 640, 440, + 666, 412, 668, 412, 668, 412, 642, 434, 668, 412, 668, 412, + 668, 1494, 642, 438, 642, 434, 642, 438, 642, 438, 642, 438, + 642, 438, 642, 434, 646, 434, 642, 1518, 668, 412, 642, 438, + 642, 434, 666, 414, 640, 438, 642, 438, 642, 1518, 642, 438, + 642, 434, 668, 412, 642, 438, 642, 438, 642, 438, 642, 438, + 642, 438, 640, 1520, 636, 438, 642, 438, 642, 438, 666, 414, + 668, 412, 642, 440, 640, 438, 640}; + + irsend.reset(); + irsend.sendRaw(rawData2, 295, 38000); + irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(TOSHIBA_AC, irsend.capture.decode_type); + ASSERT_EQ(TOSHIBA_AC_BITS, irsend.capture.bits); + toshiba.setRaw(irsend.capture.state); + EXPECT_TRUE(toshiba.getPower()); + EXPECT_EQ(17, toshiba.getTemp()); + EXPECT_EQ(3, toshiba.getFan()); + EXPECT_EQ(TOSHIBA_AC_COOL, toshiba.getMode()); + + uint16_t rawData3[295] = { + 4474, 4262, 642, 1514, 642, 1520, 642, 1520, 642, 1514, + 642, 438, 642, 438, 642, 1520, 636, 438, 642, 438, 642, 438, + 642, 438, 642, 438, 640, 1520, 638, 1520, 642, 438, 640, 1520, + 642, 438, 642, 434, 642, 438, 642, 438, 642, 438, 668, 414, + 636, 1520, 642, 1520, 642, 1514, 642, 1520, 642, 1520, 640, 1518, + 638, 1520, 666, 1494, 642, 438, 642, 434, 642, 438, 640, 438, + 642, 438, 642, 440, 640, 434, 642, 438, 642, 438, 642, 1520, + 642, 438, 642, 1514, 642, 1520, 640, 1520, 636, 438, 642, 438, + 642, 438, 666, 414, 642, 1520, 636, 1520, 642, 438, 642, 438, + 640, 438, 642, 434, 642, 1518, 642, 1520, 642, 438, 642, 434, + 640, 438, 642, 438, 642, 438, 642, 440, 642, 438, 668, 408, + 642, 1520, 642, 438, 642, 1520, 638, 1518, 642, 438, 642, 438, + 640, 1520, 640, 438, 642, 7362, 4474, 4262, 642, 1518, 638, 1520, + 640, 1520, 642, 1520, 638, 438, 642, 438, 642, 1518, 642, 438, + 638, 438, 642, 438, 642, 438, 642, 438, 642, 1514, 642, 1520, + 642, 438, 666, 1490, 642, 438, 642, 438, 642, 440, 640, 438, + 642, 434, 640, 438, 642, 1520, 642, 1520, 636, 1520, 642, 1520, + 642, 1514, 642, 1518, 642, 1518, 640, 1516, 642, 438, 642, 438, + 642, 438, 640, 438, 638, 442, 642, 434, 642, 440, 640, 438, + 642, 438, 642, 1518, 642, 438, 642, 1514, 642, 1520, 642, 1518, + 642, 438, 642, 432, 642, 438, 642, 438, 642, 1520, 642, 1514, + 642, 438, 642, 438, 642, 438, 642, 438, 642, 1514, 642, 1520, + 642, 438, 642, 438, 638, 438, 642, 438, 642, 438, 640, 440, + 642, 438, 640, 434, 642, 1520, 642, 438, 640, 1520, 668, 1490, + 666, 414, 640, 438, 642, 1520, 642, 438, 636}; + + irsend.reset(); + irsend.sendRaw(rawData3, 295, 38000); + irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(TOSHIBA_AC, irsend.capture.decode_type); + ASSERT_EQ(TOSHIBA_AC_BITS, irsend.capture.bits); + toshiba.setRaw(irsend.capture.state); + EXPECT_TRUE(toshiba.getPower()); + EXPECT_EQ(24, toshiba.getTemp()); + EXPECT_EQ(TOSHIBA_AC_FAN_MAX, toshiba.getFan()); + EXPECT_EQ(TOSHIBA_AC_HEAT, toshiba.getMode()); + + uint16_t rawData4[295] = { + 4474, 4262, 636, 1520, 640, 1520, 640, 1520, 638, 1518, 642, 438, + 642, 438, 642, 1520, 636, 438, 642, 438, 642, 438, 642, 438, + 636, 444, 636, 1520, 640, 1520, 642, 438, 638, 1524, 638, 438, + 640, 438, 642, 438, 640, 438, 642, 438, 638, 438, 642, 1518, + 642, 1520, 666, 1494, 636, 1520, 640, 1520, 640, 1520, 636, 1524, + 638, 1520, 640, 440, 640, 438, 642, 438, 636, 444, 636, 438, + 642, 438, 640, 440, 640, 438, 642, 438, 642, 1518, 638, 438, + 642, 1518, 642, 438, 640, 1520, 636, 444, 636, 438, 640, 438, + 642, 438, 668, 1494, 640, 438, 642, 1518, 636, 444, 636, 438, + 640, 1520, 642, 1518, 642, 1520, 636, 444, 636, 438, 642, 438, + 642, 438, 640, 440, 640, 438, 640, 440, 640, 438, 640, 1518, + 642, 1520, 636, 1524, 636, 1518, 642, 438, 642, 1518, 642, 1518, + 640, 438, 642, 7364, 4472, 4262, 642, 1518, 642, 1518, 638, 1518, + 642, 1520, 642, 438, 642, 438, 640, 1520, 636, 440, 640, 438, + 642, 438, 640, 438, 642, 438, 642, 1518, 636, 1524, 636, 438, + 640, 1520, 642, 438, 642, 438, 640, 438, 636, 444, 636, 438, + 668, 412, 642, 1518, 642, 1520, 642, 1520, 636, 1518, 642, 1518, + 642, 1520, 636, 1520, 668, 1494, 642, 438, 636, 444, 664, 412, + 642, 438, 668, 412, 642, 438, 636, 442, 638, 442, 638, 438, + 642, 1518, 642, 438, 642, 1518, 638, 438, 642, 1518, 642, 440, + 640, 438, 636, 444, 636, 444, 636, 1520, 642, 438, 642, 1520, + 636, 444, 636, 438, 642, 1520, 640, 1520, 636, 1520, 668, 412, + 642, 438, 642, 438, 642, 438, 638, 442, 636, 438, 642, 438, + 668, 412, 640, 1520, 638, 1524, 636, 1520, 642, 1520, 636, 444, + 638, 1522, 638, 1518, 640, 438, 642}; + + irsend.reset(); + irsend.sendRaw(rawData4, 295, 38000); + irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(TOSHIBA_AC, irsend.capture.decode_type); + ASSERT_EQ(TOSHIBA_AC_BITS, irsend.capture.bits); + toshiba.setRaw(irsend.capture.state); + EXPECT_FALSE(toshiba.getPower()); + EXPECT_EQ(22, toshiba.getTemp()); + EXPECT_EQ(4, toshiba.getFan()); + + // Confirming the quirky behaviour that the 'Power OFF' signal + // sets the mode to heat. + // The previous state the remote was in was 'AUTO' just prior to + // sending the power off message. + EXPECT_EQ(TOSHIBA_AC_HEAT, toshiba.getMode()); +} diff --git a/lib/IRremoteESP8266/test/ir_Whynter_test.cpp b/lib/IRremoteESP8266/test/ir_Whynter_test.cpp index 4978bb6..ab63417 100644 --- a/lib/IRremoteESP8266/test/ir_Whynter_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Whynter_test.cpp @@ -19,7 +19,7 @@ TEST(TestSendWhynter, SendDataOnly) { "m750s750m750s750m750s750m750s750m750s750m750s750m750s750m750s750" "m750s750m750s750m750s750m750s750m750s750m750s750m750s750m750s750" "m750s750m750s750m750s750m750s750m750s750m750s750m750s750m750s750" - "m750s108000", irsend.outputStr()); + "m750s52050", irsend.outputStr()); irsend.reset(); irsend.sendWhynter(0xFFFFFFFF); @@ -29,7 +29,7 @@ TEST(TestSendWhynter, SendDataOnly) { "m750s2150m750s2150m750s2150m750s2150m750s2150m750s2150m750s2150m750s2150" "m750s2150m750s2150m750s2150m750s2150m750s2150m750s2150m750s2150m750s2150" "m750s2150m750s2150m750s2150m750s2150m750s2150m750s2150m750s2150m750s2150" - "m750s108000", irsend.outputStr()); + "m750s12200", irsend.outputStr()); irsend.reset(); irsend.sendWhynter(0x87654321); @@ -39,7 +39,7 @@ TEST(TestSendWhynter, SendDataOnly) { "m750s750m750s2150m750s2150m750s750m750s750m750s2150m750s750m750s2150" "m750s750m750s2150m750s750m750s750m750s750m750s750m750s2150m750s2150" "m750s750m750s750m750s2150m750s750m750s750m750s750m750s750m750s2150" - "m750s108000", irsend.outputStr()); + "m750s33850", irsend.outputStr()); } // Test sending with different repeats. @@ -55,7 +55,7 @@ TEST(TestSendWhynter, SendWithRepeats) { "m750s750m750s2150m750s2150m750s750m750s750m750s2150m750s750m750s2150" "m750s750m750s2150m750s750m750s750m750s750m750s750m750s2150m750s2150" "m750s750m750s750m750s2150m750s750m750s750m750s750m750s750m750s2150" - "m750s108000", irsend.outputStr()); + "m750s33850", irsend.outputStr()); irsend.reset(); irsend.sendWhynter(0x87654321, WHYNTER_BITS, 1); // 1 repeat. @@ -65,13 +65,15 @@ TEST(TestSendWhynter, SendWithRepeats) { "m750s750m750s2150m750s2150m750s750m750s750m750s2150m750s750m750s2150" "m750s750m750s2150m750s750m750s750m750s750m750s750m750s2150m750s2150" "m750s750m750s750m750s2150m750s750m750s750m750s750m750s750m750s2150" - "m750s108000" + "m750s33850" "m750s750m2850s2850" "m750s2150m750s750m750s750m750s750m750s750m750s2150m750s2150m750s2150" "m750s750m750s2150m750s2150m750s750m750s750m750s2150m750s750m750s2150" "m750s750m750s2150m750s750m750s750m750s750m750s750m750s2150m750s2150" "m750s750m750s750m750s2150m750s750m750s750m750s750m750s750m750s2150" - "m750s108000", irsend.outputStr()); + "m750s33850", irsend.outputStr()); + + irsend.reset(); irsend.sendWhynter(0x87654321, WHYNTER_BITS, 2); // 2 repeats. EXPECT_EQ( "m750s750m2850s2850" @@ -79,19 +81,19 @@ TEST(TestSendWhynter, SendWithRepeats) { "m750s750m750s2150m750s2150m750s750m750s750m750s2150m750s750m750s2150" "m750s750m750s2150m750s750m750s750m750s750m750s750m750s2150m750s2150" "m750s750m750s750m750s2150m750s750m750s750m750s750m750s750m750s2150" - "m750s108000" + "m750s33850" "m750s750m2850s2850" "m750s2150m750s750m750s750m750s750m750s750m750s2150m750s2150m750s2150" "m750s750m750s2150m750s2150m750s750m750s750m750s2150m750s750m750s2150" "m750s750m750s2150m750s750m750s750m750s750m750s750m750s2150m750s2150" "m750s750m750s750m750s2150m750s750m750s750m750s750m750s750m750s2150" - "m750s108000" + "m750s33850" "m750s750m2850s2850" "m750s2150m750s750m750s750m750s750m750s750m750s2150m750s2150m750s2150" "m750s750m750s2150m750s2150m750s750m750s750m750s2150m750s750m750s2150" "m750s750m750s2150m750s750m750s750m750s750m750s750m750s2150m750s2150" "m750s750m750s750m750s2150m750s750m750s750m750s750m750s750m750s2150" - "m750s108000", irsend.outputStr()); + "m750s33850", irsend.outputStr()); } // Test sending an atypical data size. @@ -104,7 +106,7 @@ TEST(TestSendWhynter, SendUnusualSize) { EXPECT_EQ( "m750s750m2850s2850" "m750s750m750s750m750s750m750s750m750s750m750s750m750s750m750s750" - "m750s108000", irsend.outputStr()); + "m750s88050", irsend.outputStr()); irsend.reset(); irsend.sendWhynter(0x1234567890ABCDEF, 64); @@ -118,7 +120,7 @@ TEST(TestSendWhynter, SendUnusualSize) { "m750s2150m750s750m750s2150m750s750m750s2150m750s750m750s2150m750s2150" "m750s2150m750s2150m750s750m750s750m750s2150m750s2150m750s750m750s2150" "m750s2150m750s2150m750s2150m750s750m750s2150m750s2150m750s2150m750s2150" - "m750s108000", irsend.outputStr()); + "m750s12200", irsend.outputStr()); } // Tests for decodeWhynter(). diff --git a/lib/IRremoteESP8266/tools/Makefile b/lib/IRremoteESP8266/tools/Makefile new file mode 100644 index 0000000..abf11fc --- /dev/null +++ b/lib/IRremoteESP8266/tools/Makefile @@ -0,0 +1,175 @@ +# SYNOPSIS: +# +# make [all] - makes everything. +# make clean - removes all files generated by make. + +# Please tweak the following variable definitions as needed by your +# project, except GTEST_HEADERS, which you can use in your own targets +# but shouldn't modify. + + +# Where to find user code. +USER_DIR = ../src + +# Where to find test code. +TEST_DIR = ../test + +# 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 + +all : gc_decode + +run_tests : all + failed=""; \ + for py_unittest in *_test.py; do \ + echo "RUNNING: $${py_unittest}"; \ + python ./$${py_unittest} || failed="$${failed} $${py_unittest}"; \ + done; \ + if [ -n "$${failed}" ]; then \ + echo "FAIL: :-( :-( Unit test(s)$${failed} failed! :-( :-("; exit 1; \ + else \ + echo "PASS: \o/ \o/ All unit tests passed. \o/ \o/"; \ + fi + +clean : + rm -f *.o *.pyc gc_decode + + +# All the IR protocol object files. +PROTOCOLS = ir_NEC.o ir_Sony.o ir_Samsung.o ir_JVC.o ir_RCMM.o ir_RC5_RC6.o \ + ir_LG.o ir_Mitsubishi.o ir_Fujitsu.o ir_Sharp.o ir_Sanyo.o \ + ir_Denon.o ir_Dish.o ir_Panasonic.o ir_Whynter.o ir_Coolix.o \ + ir_Aiwa.o ir_Sherwood.o ir_Kelvinator.o ir_Daikin.o ir_Gree.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 + +# 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 +# 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 + +gc_decode : $(COMMON_OBJ) gc_decode.o + $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ + + +IRutils.o : $(USER_DIR)/IRutils.cpp $(USER_DIR)/IRutils.h $(USER_DIR)/IRremoteESP8266.h + $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/IRutils.cpp + +IRtimer.o : $(USER_DIR)/IRtimer.cpp $(USER_DIR)/IRtimer.h + $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/IRtimer.cpp + +IRsend.o : $(USER_DIR)/IRsend.cpp $(USER_DIR)/IRsend.h $(USER_DIR)/IRremoteESP8266.h + $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/IRsend.cpp + +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 + +ir_GlobalCache.o : $(USER_DIR)/ir_GlobalCache.cpp $(COMMON_DEPS) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_GlobalCache.cpp + +ir_Sherwood.o : $(USER_DIR)/ir_Sherwood.cpp $(COMMON_DEPS) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Sherwood.cpp + +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 $(COMMON_DEPS) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) -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 + +ir_JVC.o : $(USER_DIR)/ir_JVC.cpp $(COMMON_DEPS) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_JVC.cpp + +ir_RCMM.o : $(USER_DIR)/ir_RCMM.cpp $(COMMON_DEPS) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_RCMM.cpp + +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 + +ir_Fujitsu.o : $(USER_DIR)/ir_Fujitsu.h $(USER_DIR)/ir_Fujitsu.cpp $(COMMON_DEPS) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) -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 + +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 + +ir_Dish.o : $(USER_DIR)/ir_Dish.cpp $(COMMON_DEPS) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Dish.cpp + +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 + +ir_Aiwa.o : $(USER_DIR)/ir_Aiwa.cpp $(COMMON_DEPS) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Aiwa.cpp + +ir_Denon.o : $(USER_DIR)/ir_Denon.cpp $(COMMON_DEPS) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Denon.cpp + +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 + +ir_Gree.o : $(USER_DIR)/ir_Gree.cpp $(GTEST_HEADERS) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) -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 + +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 + +ir_Midea.o : $(USER_DIR)/ir_Midea.cpp $(COMMON_DEPS) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) -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 + +ir_Lasertag.o : $(USER_DIR)/ir_Lasertag.cpp $(USER_DIR)/ir_RC5_RC6.cpp $(COMMON_DEPS) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Lasertag.cpp + +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 + +ir_Hitachi.o : $(USER_DIR)/ir_Hitachi.cpp $(GTEST_HEADERS) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) -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 diff --git a/lib/IRremoteESP8266/tools/RawToGlobalCache.sh b/lib/IRremoteESP8266/tools/RawToGlobalCache.sh new file mode 100644 index 0000000..e0b0ebe --- /dev/null +++ b/lib/IRremoteESP8266/tools/RawToGlobalCache.sh @@ -0,0 +1,53 @@ +#!/bin/bash +# Convert IRremoteESP8266's rawData output into Global Cache format. + +function isDigits() +{ + [[ "$1" =~ ^[0-9]+$ ]] +} + +function usage() +{ +cat << EOF +Usage: $0 Frequency_in_Hz + Reads an IRremoteESP8266 rawData declaration from STDIN and converts it to + GlobalCache format. + e.g. + uint16_t rawbuf[37] = { + 7930, 3952, 494, 1482, 520, 1482, 494, 1508, + 494, 520, 494, 1482, 494, 520, 494, 1482, + 494, 1482, 494, 3978, 494, 520, 494, 520, + 494, 520, 494, 520, 520, 520, 494, 520, + 494, 520, 494, 520, 494}; +EOF + exit 1 +} + +# We need a frequency argument. +if [[ $# -ne 1 ]]; then + usage +fi +HZ="$1" +# HZ must be a positive number +if ! isDigits "${HZ}"; then + usage +fi +# HZ must not be zero. +if [[ ${HZ} == 0 ]]; then + usage +fi + + +PERIOD_OFFSET=0 +period=$((((1000000 + (${HZ} / 2)) / ${HZ}) + ${PERIOD_OFFSET})) +result="${HZ},1,1" +while read line; do + # Quick and Dirty Removal of any array declaration syntax, and any commas. + line="$(echo ${line} | sed 's/uint.*{//i' | sed 's/,//g' | sed 's/};.*//g')" + for msecs in ${line}; do + if isDigits "${msecs}"; then + result="${result},$((${msecs} / ${period}))" + fi + done +done +echo "GlobalCache code = \"${result}\"" diff --git a/lib/IRremoteESP8266/tools/auto_analyse_raw_data.py b/lib/IRremoteESP8266/tools/auto_analyse_raw_data.py new file mode 100644 index 0000000..522586d --- /dev/null +++ b/lib/IRremoteESP8266/tools/auto_analyse_raw_data.py @@ -0,0 +1,406 @@ +#!/usr/bin/python +"""Attempt an automatic analysis of IRremoteESP8266's Raw data output. + Makes suggestions on key values and tried to break down the message + into likely chuncks.""" +# +# Copyright 2018 David Conran +import argparse +import sys + + +class RawIRMessage(object): + """Basic analyse functions & structure for raw IR messages.""" + + # pylint: disable=too-many-instance-attributes + + def __init__(self, margin, timings, output=sys.stdout, verbose=True): + self.hdr_mark = None + self.hdr_space = None + self.bit_mark = None + self.zero_space = None + self.one_space = None + self.gaps = [] + self.margin = margin + self.marks = [] + self.spaces = [] + self.output = output + self.verbose = verbose + if len(timings) <= 3: + raise ValueError("Too few message timings supplied.") + self.timings = timings + self._generate_timing_candidates() + self._calc_values() + + def _generate_timing_candidates(self): + """Determine the likely values from the given data.""" + count = 0 + for usecs in self.timings: + count = count + 1 + if count % 2: + self.marks.append(usecs) + else: + self.spaces.append(usecs) + self.marks = self._reduce_list(self.marks) + self.spaces = self._reduce_list(self.spaces) + + def _reduce_list(self, items): + """Reduce the list of numbers into buckets that are atleast margin apart.""" + result = [] + last = -1 + for item in sorted(items, reverse=True): + if last == -1 or item < last - self.margin: + result.append(item) + last = item + return result + + def _usec_compare(self, seen, expected): + """Compare two usec values and see if they match within a + subtrative margin.""" + return seen <= expected and seen > expected - self.margin + + def _usec_compares(self, usecs, expecteds): + """Compare a usec value to a list of values and return True + if they are within a subtractive margin.""" + for expected in expecteds: + if self._usec_compare(usecs, expected): + return True + return False + + def display_binary(self, binary_str): + """Display common representations of the suppied binary string.""" + num = int(binary_str, 2) + bits = len(binary_str) + rev_binary_str = binary_str[::-1] + rev_num = int(rev_binary_str, 2) + self.output.write("\n Bits: %d\n" + " Hex: %s (MSB first)\n" + " %s (LSB first)\n" + " Dec: %s (MSB first)\n" + " %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)) + + def add_data_code(self, bin_str): + """Add the common "data" sequence of code to send the bulk of a message.""" + # pylint: disable=no-self-use + code = [] + code.append(" // Data") + code.append(" // e.g. data = 0x%X, nbits = %d" % (int(bin_str, 2), + len(bin_str))) + code.append(" sendData(BIT_MARK, ONE_SPACE, BIT_MARK, ZERO_SPACE, data, " + "nbits, true);") + code.append(" // Footer") + code.append(" mark(BIT_MARK);") + return code + + def _calc_values(self): + """Calculate the values which describe the standard timings + for the protocol.""" + if self.verbose: + self.output.write("Potential Mark Candidates:\n" + "%s\n" + "Potential Space Candidates:\n" + "%s\n" % (str(self.marks), str(self.spaces))) + # Largest mark is likely the HDR_MARK + self.hdr_mark = self.marks[0] + # The bit mark is likely to be the smallest mark. + self.bit_mark = self.marks[-1] + + if self.is_space_encoded() and len(self.spaces) >= 3: + if self.verbose and len(self.marks) > 2: + self.output.write("DANGER: Unexpected and unused mark timings!") + # We should have 3 space candidates at least. + # They should be: zero_space (smallest), one_space, & hdr_space (largest) + spaces = list(self.spaces) + self.zero_space = spaces.pop() + self.one_space = spaces.pop() + self.hdr_space = spaces.pop() + # Rest are probably message gaps + self.gaps = spaces + + def is_space_encoded(self): + """Make an educated guess if the message is space encoded.""" + return len(self.spaces) > len(self.marks) + + def is_hdr_mark(self, usec): + """Is usec the header mark?""" + return self._usec_compare(usec, self.hdr_mark) + + def is_hdr_space(self, usec): + """Is usec the header space?""" + return self._usec_compare(usec, self.hdr_space) + + def is_bit_mark(self, usec): + """Is usec the bit mark?""" + return self._usec_compare(usec, self.bit_mark) + + def is_one_space(self, usec): + """Is usec the one space?""" + return self._usec_compare(usec, self.one_space) + + def is_zero_space(self, usec): + """Is usec the zero_space?""" + return self._usec_compare(usec, self.zero_space) + + def is_gap(self, usec): + """Is usec the a space gap?""" + return self._usec_compares(usec, self.gaps) + + +def add_bit(so_far, bit, output=sys.stdout): + """Add a bit to the end of the bits collected so far. """ + if bit == "reset": + return "" + output.write(str(bit)) # This effectively displays in LSB first order. + return so_far + str(bit) # Storing it in MSB first order. + + +def convert_rawdata(data_str): + """Parse a C++ rawdata declaration into a list of values.""" + start = data_str.find('{') + end = data_str.find('}') + if end == -1: + end = len(data_str) + if start > end: + raise ValueError("Raw Data not parsible due to parentheses placement.") + data_str = data_str[start + 1:end] + results = [] + for timing in [x.strip() for x in data_str.split(',')]: + try: + results.append(int(timing)) + except ValueError: + raise ValueError( + "Raw Data contains a non-numeric value of '%s'." % timing) + return results + + +def dump_constants(message, defines, output=sys.stdout): + """Dump the key constants and generate the C++ #defines.""" + output.write("Guessing key value:\n" + "HDR_MARK = %d\n" + "HDR_SPACE = %d\n" + "BIT_MARK = %d\n" + "ONE_SPACE = %d\n" + "ZERO_SPACE = %d\n" % + (message.hdr_mark, message.hdr_space, message.bit_mark, + message.one_space, message.zero_space)) + defines.append("#define HDR_MARK %dU" % message.hdr_mark) + defines.append("#define BIT_MARK %dU" % message.bit_mark) + defines.append("#define HDR_SPACE %dU" % message.hdr_space) + defines.append("#define ONE_SPACE %dU" % message.one_space) + defines.append("#define ZERO_SPACE %dU" % message.zero_space) + + if len(message.gaps) == 1: + output.write("SPACE_GAP = %d\n" % message.gaps[0]) + defines.append("#define SPACE_GAP = %dU" % message.gaps[0]) + else: + count = 0 + for gap in message.gaps: + # We probably (still) have a gap in the protocol. + count = count + 1 + output.write("SPACE_GAP%d = %d\n" % (count, gap)) + defines.append("#define SPACE_GAP%d = %dU" % (count, gap)) + + +def parse_and_report(rawdata_str, margin, gen_code=False, output=sys.stdout): + """Analyse the rawdata c++ definition of a IR message.""" + defines = [] + function_code = [] + + # Parse the input. + rawdata = convert_rawdata(rawdata_str) + + output.write("Found %d timing entries.\n" % len(rawdata)) + + message = RawIRMessage(margin, rawdata, output) + output.write("\nGuessing encoding type:\n") + if message.is_space_encoded(): + output.write("Looks like it uses space encoding. Yay!\n\n") + dump_constants(message, defines, output) + else: + output.write("Sorry, it looks like it is Mark encoded. " + "I can't do that yet. Exiting.\n") + sys.exit(1) + total_bits = decode_data(message, defines, function_code, output) + if gen_code: + generate_irsend_code(defines, function_code, total_bits, output) + + +def decode_data(message, defines, function_code, output=sys.stdout): + """Decode the data sequence with the given values in mind.""" + # pylint: disable=too-many-branches,too-many-statements + + # Now we have likely candidates for the key values, go through the original + # sequence and break it up and indicate accordingly. + + output.write("\nDecoding protocol based on analysis so far:\n\n") + state = "" + count = 1 + total_bits = "" + binary_value = add_bit("", "reset") + + function_code.extend([ + "// Function should be safe up to 64 bits.", + "void IRsend::sendXYZ(const uint64_t data, const uint16_t" + " nbits, const uint16_t repeat) {", + " enableIROut(38); // A guess. Most common frequency.", + " for (uint16_t r = 0; r <= repeat; r++) {" + ]) + + for usec in message.timings: + if (message.is_hdr_mark(usec) and count % 2 and + not message.is_bit_mark(usec)): + state = "HM" + if binary_value: + message.display_binary(binary_value) + total_bits = total_bits + binary_value + output.write(state) + binary_value = add_bit(binary_value, "reset") + output.write("HDR_MARK+") + function_code.extend([" // Header", " mark(HDR_MARK);"]) + elif (message.is_hdr_space(usec) and not message.is_one_space(usec)): + if state != "HM": + if binary_value: + message.display_binary(binary_value) + total_bits = total_bits + binary_value + function_code.extend(message.add_data_code(binary_value)) + binary_value = add_bit(binary_value, "reset") + output.write("UNEXPECTED->") + state = "HS" + output.write("HDR_SPACE+") + function_code.append(" space(HDR_SPACE);") + elif message.is_bit_mark(usec) and count % 2: + if state != "HS" and state != "BS": + output.write("BIT_MARK(UNEXPECTED)") + state = "BM" + elif message.is_zero_space(usec): + if state != "BM": + output.write("ZERO_SPACE(UNEXPECTED)") + state = "BS" + binary_value = add_bit(binary_value, 0, output) + elif message.is_one_space(usec): + if state != "BM": + output.write("ONE_SPACE(UNEXPECTED)") + state = "BS" + binary_value = add_bit(binary_value, 1, output) + elif message.is_gap(usec): + if state != "BM": + output.write("UNEXPECTED->") + state = "GS" + output.write(" GAP(%d)" % usec) + message.display_binary(binary_value) + function_code.extend(message.add_data_code(binary_value)) + function_code.append(" space(SPACE_GAP);") + total_bits = total_bits + binary_value + binary_value = add_bit(binary_value, "reset") + else: + output.write("UNKNOWN(%d)" % usec) + state = "UNK" + count = count + 1 + message.display_binary(binary_value) + function_code.extend(message.add_data_code(binary_value)) + function_code.extend([ + " space(100000); // A 100% made up guess of the gap" + " between messages.", " }", "}" + ]) + + total_bits = total_bits + binary_value + output.write("Total Nr. of suspected bits: %d\n" % len(total_bits)) + defines.append("#define XYZ_BITS %dU" % len(total_bits)) + if len(total_bits) > 64: + defines.append("#define XYZ_STATE_LENGTH %dU" % (len(total_bits) / 8)) + return total_bits + + +def generate_irsend_code(defines, normal, bits_str, output=sys.stdout): + """Output the estimated C++ code to reproduce the IR message.""" + output.write("\nGenerating a VERY rough code outline:\n\n" + "// WARNING: This probably isn't directly usable." + " It's a guide only.\n") + for line in defines: + output.write("%s\n" % line) + + if len(bits_str) > 64: # Will it fit in a uint64_t? + output.write("// DANGER: More than 64 bits detected. A uint64_t for " + "'data' won't work!\n") + # Display the "normal" version's code incase there are some + # oddities in it. + for line in normal: + output.write("%s\n" % line) + + if len(bits_str) > 64: # Will it fit in a uint64_t? + output.write("\n\n// Alternative >64 bit Function\n" + "void IRsend::sendXYZ(uint8_t data[], uint16_t nbytes," + " uint16_t repeat) {\n" + " // nbytes should typically be XYZ_STATE_LENGTH\n" + " // data should typically be:\n" + " // uint8_t data[XYZ_STATE_LENGTH] = {0x%s};\n" + " // data[] is assumed to be in MSB order for this code.\n" + " for (uint16_t r = 0; r <= repeat; r++) {\n" + " sendGeneric(HDR_MARK, HDR_SPACE,\n" + " BIT_MARK, ONE_SPACE,\n" + " BIT_MARK, ZERO_SPACE,\n" + " BIT_MARK\n" + " 100000, // 100%% made-up guess at the" + " message gap.\n" + " data, nbytes,\n" + " 38000, // Complete guess of the modulation" + " frequency.\n" + " true, 0, 50);\n" + "}\n" % ", 0x".join("%02X" % int(bits_str[i:i + 8], 2) + for i in range(0, len(bits_str), 8))) + + +def main(): + """Parse the commandline arguments and call the method.""" + arg_parser = argparse.ArgumentParser( + description="Read an IRremoteESP8266 rawData declaration and tries to " + "analyse it.", + formatter_class=argparse.ArgumentDefaultsHelpFormatter) + arg_parser.add_argument( + "-g", + "--code", + action="store_true", + default=False, + dest="gen_code", + help="Generate a C++ code outline to aid making an IRsend function.") + arg_group = arg_parser.add_mutually_exclusive_group(required=True) + arg_group.add_argument( + "rawdata", + help="A rawData line from IRrecvDumpV2. e.g. 'uint16_t rawbuf[37] = {" + "7930, 3952, 494, 1482, 520, 1482, 494, 1508, 494, 520, 494, 1482, 494, " + "520, 494, 1482, 494, 1482, 494, 3978, 494, 520, 494, 520, 494, 520, " + "494, 520, 520, 520, 494, 520, 494, 520, 494, 520, 494};'", + nargs="?") + arg_group.add_argument( + "-f", "--file", help="Read in a rawData line from the file.") + arg_parser.add_argument( + "-r", + "--range", + type=int, + help="Max number of micro-seconds difference between values to consider" + " it the same value.", + dest="margin", + default=200) + arg_group.add_argument( + "--stdin", + help="Read in a rawData line from STDIN.", + action="store_true", + default=False) + arg_options = arg_parser.parse_args() + + if arg_options.stdin: + data = sys.stdin.read() + elif arg_options.file: + with open(arg_options.file) as input_file: + data = input_file.read() + else: + data = arg_options.rawdata + parse_and_report(data, arg_options.margin, arg_options.gen_code) + + +if __name__ == '__main__': + main() diff --git a/lib/IRremoteESP8266/tools/auto_analyse_raw_data_test.py b/lib/IRremoteESP8266/tools/auto_analyse_raw_data_test.py new file mode 100644 index 0000000..ac84cab --- /dev/null +++ b/lib/IRremoteESP8266/tools/auto_analyse_raw_data_test.py @@ -0,0 +1,288 @@ +#!/usr/bin/python +"""Unit tests for auto_analyse_raw_data.py""" +import StringIO +import unittest +import auto_analyse_raw_data as analyse + + +class TestRawIRMessage(unittest.TestCase): + """Unit tests for the RawIRMessage class.""" + + def test_display_binary(self): + """Test the display_binary() method.""" + output = StringIO.StringIO() + message = analyse.RawIRMessage(100, [8000, 4000, 500, 500, 500], output, + False) + self.assertEqual(output.getvalue(), '') + message.display_binary("10101010") + message.display_binary("0000000000000000") + message.display_binary("00010010001101000101011001111000") + self.assertEqual(output.getvalue(), '\n' + ' Bits: 8\n' + ' Hex: 0xAA (MSB first)\n' + ' 0x55 (LSB first)\n' + ' Dec: 170 (MSB first)\n' + ' 85 (LSB first)\n' + ' Bin: 0b10101010 (MSB first)\n' + ' 0b01010101 (LSB first)\n' + '\n' + ' Bits: 16\n' + ' Hex: 0x0000 (MSB first)\n' + ' 0x0000 (LSB first)\n' + ' Dec: 0 (MSB first)\n' + ' 0 (LSB first)\n' + ' Bin: 0b0000000000000000 (MSB first)\n' + ' 0b0000000000000000 (LSB first)\n' + '\n' + ' Bits: 32\n' + ' Hex: 0x12345678 (MSB first)\n' + ' 0x1E6A2C48 (LSB first)\n' + ' Dec: 305419896 (MSB first)\n' + ' 510274632 (LSB first)\n' + ' Bin: 0b00010010001101000101011001111000 (MSB first)\n' + ' 0b00011110011010100010110001001000 (LSB first)\n') + + +class TestAutoAnalyseRawData(unittest.TestCase): + """Unit tests for the functions in AutoAnalyseRawData.""" + + def test_dump_constants_simple(self): + """Simple tests for the dump_constants() function.""" + ignore = StringIO.StringIO() + output = StringIO.StringIO() + defs = [] + message = analyse.RawIRMessage(200, [ + 7930, 3952, 494, 1482, 520, 1482, 494, 1508, 494, 520, 494, 1482, 494, + 520, 494, 1482, 494, 1482, 494, 3978, 494, 520, 494, 520, 494, 520, 494, + 520, 520, 520, 494, 520, 494, 520, 494, 1482, 494 + ], ignore) + analyse.dump_constants(message, defs, output) + self.assertEqual(defs, [ + '#define HDR_MARK 7930U', '#define BIT_MARK 520U', + '#define HDR_SPACE 3978U', '#define ONE_SPACE 1508U', + '#define ZERO_SPACE 520U' + ]) + self.assertEqual(output.getvalue(), 'Guessing key value:\n' + 'HDR_MARK = 7930\n' + 'HDR_SPACE = 3978\n' + 'BIT_MARK = 520\n' + 'ONE_SPACE = 1508\n' + 'ZERO_SPACE = 520\n') + + def test_dump_constants_aircon(self): + """More complex tests for the dump_constants() function.""" + ignore = StringIO.StringIO() + output = StringIO.StringIO() + defs = [] + message = analyse.RawIRMessage(200, [ + 9008, 4496, 644, 1660, 676, 530, 648, 558, 672, 1636, 646, 1660, 644, + 556, 650, 584, 626, 560, 644, 580, 628, 1680, 624, 560, 648, 1662, 644, + 582, 648, 536, 674, 530, 646, 580, 628, 560, 670, 532, 646, 562, 644, + 556, 672, 536, 648, 1662, 646, 1660, 652, 554, 644, 558, 672, 538, 644, + 560, 668, 560, 648, 1638, 668, 536, 644, 1660, 668, 532, 648, 560, 648, + 1660, 674, 554, 622, 19990, 646, 580, 624, 1660, 648, 556, 648, 558, + 674, 556, 622, 560, 644, 564, 668, 536, 646, 1662, 646, 1658, 672, 534, + 648, 558, 644, 562, 648, 1662, 644, 584, 622, 558, 648, 562, 668, 534, + 670, 536, 670, 532, 672, 536, 646, 560, 646, 558, 648, 558, 670, 534, + 650, 558, 646, 560, 646, 560, 668, 1638, 646, 1662, 646, 1660, 646, + 1660, 648 + ], ignore) + analyse.dump_constants(message, defs, output) + self.assertEqual(defs, [ + '#define HDR_MARK 9008U', '#define BIT_MARK 676U', + '#define HDR_SPACE 4496U', '#define ONE_SPACE 1680U', + '#define ZERO_SPACE 584U', '#define SPACE_GAP = 19990U' + ]) + self.assertEqual(output.getvalue(), 'Guessing key value:\n' + 'HDR_MARK = 9008\n' + 'HDR_SPACE = 4496\n' + 'BIT_MARK = 676\n' + 'ONE_SPACE = 1680\n' + 'ZERO_SPACE = 584\n' + 'SPACE_GAP = 19990\n') + + def test_convert_rawdata(self): + """Tests for the convert_rawdata() function.""" + # trivial cases + self.assertEqual(analyse.convert_rawdata("0"), [0]) + with self.assertRaises(ValueError) as context: + analyse.convert_rawdata("") + self.assertEqual(context.exception.message, + "Raw Data contains a non-numeric value of ''.") + + # Single parenthesis + self.assertEqual(analyse.convert_rawdata("foo {10"), [10]) + self.assertEqual(analyse.convert_rawdata("20} bar"), [20]) + + # No parentheses + self.assertEqual(analyse.convert_rawdata("10,20 , 30"), [10, 20, 30]) + + # Dual parentheses + self.assertEqual(analyse.convert_rawdata("{10,20 , 30}"), [10, 20, 30]) + self.assertEqual(analyse.convert_rawdata("foo{10,20}bar"), [10, 20]) + + # Many parentheses + self.assertEqual(analyse.convert_rawdata("foo{10,20}{bar}"), [10, 20]) + self.assertEqual(analyse.convert_rawdata("foo{10,20}{bar}}{"), [10, 20]) + + # Bad parentheses + with self.assertRaises(ValueError) as context: + analyse.convert_rawdata("}10{") + self.assertEqual(context.exception.message, + "Raw Data not parsible due to parentheses placement.") + + # Non base-10 values + with self.assertRaises(ValueError) as context: + analyse.convert_rawdata("10, 20, foo, bar, 30") + self.assertEqual(context.exception.message, + "Raw Data contains a non-numeric value of 'foo'.") + + # A messy usual "good" case. + input_str = """uint16_t rawbuf[6] = { + 9008, 4496, 644, + 1660, 676, + + 530} + ;""" + self.assertEqual( + analyse.convert_rawdata(input_str), [9008, 4496, 644, 1660, 676, 530]) + + def test_parse_and_report(self): + """Tests for the parse_and_report() function.""" + + # Without code generation. + output = StringIO.StringIO() + input_str = """ + uint16_t rawbuf[139] = {9008, 4496, 644, 1660, 676, 530, 648, 558, 672, + 1636, 646, 1660, 644, 556, 650, 584, 626, 560, 644, 580, 628, 1680, + 624, 560, 648, 1662, 644, 582, 648, 536, 674, 530, 646, 580, 628, + 560, 670, 532, 646, 562, 644, 556, 672, 536, 648, 1662, 646, 1660, + 652, 554, 644, 558, 672, 538, 644, 560, 668, 560, 648, 1638, 668, + 536, 644, 1660, 668, 532, 648, 560, 648, 1660, 674, 554, 622, 19990, + 646, 580, 624, 1660, 648, 556, 648, 558, 674, 556, 622, 560, 644, + 564, 668, 536, 646, 1662, 646, 1658, 672, 534, 648, 558, 644, 562, + 648, 1662, 644, 584, 622, 558, 648, 562, 668, 534, 670, 536, 670, + 532, 672, 536, 646, 560, 646, 558, 648, 558, 670, 534, 650, 558, + 646, 560, 646, 560, 668, 1638, 646, 1662, 646, 1660, 646, 1660, + 648};""" + analyse.parse_and_report(input_str, 200, False, output) + self.assertEqual( + output.getvalue(), 'Found 139 timing entries.\n' + 'Potential Mark Candidates:\n' + '[9008, 676]\n' + 'Potential Space Candidates:\n' + '[19990, 4496, 1680, 584]\n' + '\n' + 'Guessing encoding type:\n' + 'Looks like it uses space encoding. Yay!\n' + '\n' + 'Guessing key value:\n' + 'HDR_MARK = 9008\n' + 'HDR_SPACE = 4496\n' + 'BIT_MARK = 676\n' + 'ONE_SPACE = 1680\n' + 'ZERO_SPACE = 584\n' + 'SPACE_GAP = 19990\n' + '\n' + 'Decoding protocol based on analysis so far:\n' + '\n' + 'HDR_MARK+HDR_SPACE+10011000010100000000011000001010010 GAP(19990)\n' + ' Bits: 35\n' + ' Hex: 0x4C2803052 (MSB first)\n' + ' 0x250600A19 (LSB first)\n' + ' Dec: 20443050066 (MSB first)\n' + ' 9938405913 (LSB first)\n' + ' Bin: 0b10011000010100000000011000001010010 (MSB first)\n' + ' 0b01001010000011000000000101000011001 (LSB first)\n' + 'BIT_MARK(UNEXPECTED)01000000110001000000000000001111\n' + ' Bits: 32\n' + ' Hex: 0x40C4000F (MSB first)\n' + ' 0xF0002302 (LSB first)\n' + ' Dec: 1086586895 (MSB first)\n' + ' 4026540802 (LSB first)\n' + ' Bin: 0b01000000110001000000000000001111 (MSB first)\n' + ' 0b11110000000000000010001100000010 (LSB first)\n' + 'Total Nr. of suspected bits: 67\n') + + # With code generation. + output = StringIO.StringIO() + input_str = """ + uint16_t rawbuf[37] = {7930, 3952, 494, 1482, 520, 1482, 494, + 1508, 494, 520, 494, 1482, 494, 520, 494, 1482, 494, 1482, 494, + 3978, 494, 520, 494, 520, 494, 520, 494, 520, 520, 520, 494, 520, + 494, 520, 494, 1482, 494};""" + analyse.parse_and_report(input_str, 200, True, output) + self.assertEqual( + output.getvalue(), 'Found 37 timing entries.\n' + 'Potential Mark Candidates:\n' + '[7930, 520]\n' + 'Potential Space Candidates:\n' + '[3978, 1508, 520]\n' + '\n' + 'Guessing encoding type:\n' + 'Looks like it uses space encoding. Yay!\n' + '\n' + 'Guessing key value:\n' + 'HDR_MARK = 7930\n' + 'HDR_SPACE = 3978\n' + 'BIT_MARK = 520\n' + 'ONE_SPACE = 1508\n' + 'ZERO_SPACE = 520\n' + '\n' + 'Decoding protocol based on analysis so far:\n' + '\n' + 'HDR_MARK+HDR_SPACE+11101011\n' + ' Bits: 8\n' + ' Hex: 0xEB (MSB first)\n' + ' 0xD7 (LSB first)\n' + ' Dec: 235 (MSB first)\n' + ' 215 (LSB first)\n' + ' Bin: 0b11101011 (MSB first)\n' + ' 0b11010111 (LSB first)\n' + 'UNEXPECTED->HDR_SPACE+00000001\n' + ' Bits: 8\n Hex: 0x01 (MSB first)\n' + ' 0x80 (LSB first)\n' + ' Dec: 1 (MSB first)\n' + ' 128 (LSB first)\n' + ' Bin: 0b00000001 (MSB first)\n' + ' 0b10000000 (LSB first)\n' + 'Total Nr. of suspected bits: 16\n' + '\n' + 'Generating a VERY rough code outline:\n' + '\n' + "// WARNING: This probably isn't directly usable. It's a guide only.\n" + '#define HDR_MARK 7930U\n' + '#define BIT_MARK 520U\n' + '#define HDR_SPACE 3978U\n' + '#define ONE_SPACE 1508U\n' + '#define ZERO_SPACE 520U\n' + '#define XYZ_BITS 16U\n' + '// Function should be safe up to 64 bits.\n' + 'void IRsend::sendXYZ(const uint64_t data, const uint16_t nbits,' + ' const uint16_t repeat) {\n' + ' enableIROut(38); // A guess. Most common frequency.\n' + ' for (uint16_t r = 0; r <= repeat; r++) {\n' + ' // Header\n' + ' mark(HDR_MARK);\n' + ' space(HDR_SPACE);\n' + ' // Data\n' + ' // e.g. data = 0xEB, nbits = 8\n' + ' sendData(BIT_MARK, ONE_SPACE, BIT_MARK, ZERO_SPACE, data, nbits,' + ' true);\n' + ' // Footer\n' + ' mark(BIT_MARK);\n' + ' space(HDR_SPACE);\n' + ' // Data\n' + ' // e.g. data = 0x1, nbits = 8\n' + ' sendData(BIT_MARK, ONE_SPACE, BIT_MARK, ZERO_SPACE, data, nbits,' + ' true);\n' + ' // Footer\n' + ' mark(BIT_MARK);\n' + ' space(100000); // A 100% made up guess of the gap between' + ' messages.\n' + ' }\n' + '}\n') + + +if __name__ == '__main__': + unittest.main(verbosity=2) diff --git a/lib/IRremoteESP8266/tools/gc_decode.cpp b/lib/IRremoteESP8266/tools/gc_decode.cpp new file mode 100644 index 0000000..12b1365 --- /dev/null +++ b/lib/IRremoteESP8266/tools/gc_decode.cpp @@ -0,0 +1,108 @@ +// Quick and dirty tool to decode GlobalCache (GC) codes +// and ProntoHex codes +// Copyright 2017 Jorge Cisneros + +#include +#include +#include +#include +#include +#include "IRsend.h" +#include "IRsend_test.h" +#include "IRutils.h" + +#define MAX_GC_CODE_LENGTH 10000 + +void str_to_uint16(char *str, uint16_t *res, uint8_t base) { + char *end; + errno = 0; + intmax_t val = strtoimax(str, &end, base); + if (errno == ERANGE || val < 0 || val > UINT16_MAX || + end == str || *end != '\0') + return; + *res = (uint16_t) val; +} + +void usage_error(char * name) { + std::cerr << "Usage: " << name << " [-raw] " << std::endl + << "Usage: " << name << " -prontohex [-raw] " << std::endl; +} + +int main(int argc, char * argv[]) { + int argv_offset = 1; + bool dumpraw = false; + bool prontohex = false; + + // Check the invocation/calling usage. + if (argc < 2 || argc > 4) { + usage_error(argv[0]); + return 1; + } + if (strncmp("-prontohex", argv[argv_offset], 10) == 0) { + prontohex = true; + argv_offset++; + } + + if (strncmp("-raw", argv[argv_offset], 4) == 0) { + dumpraw = true; + argv_offset++; + } + if (argc - argv_offset != 1) { + usage_error(argv[0]); + return 1; + } + + uint16_t gc_test[MAX_GC_CODE_LENGTH]; + int index = 0; + char *pch; + char *saveptr1; + char *sep = const_cast(","); + int codebase = 10; + + if (prontohex) { + sep = const_cast(" "); + codebase = 16; + } + + pch = strtok_r(argv[argv_offset], sep, &saveptr1); + while (pch != NULL && index < MAX_GC_CODE_LENGTH) { + str_to_uint16(pch, &gc_test[index], codebase); + pch = strtok_r(NULL, sep, &saveptr1); + index++; + } + + IRsendTest irsend(4); + IRrecv irrecv(4); + irsend.begin(); + irsend.reset(); + + if (prontohex) { + irsend.sendPronto(gc_test, index); + } else { + irsend.sendGC(gc_test, index); + } + irsend.makeDecodeResult(); + irrecv.decode(&irsend.capture); + + std::cout << "Code length " << index << std::endl + << "Code type " << irsend.capture.decode_type + << " (" << typeToString(irsend.capture.decode_type) << ")" << std::endl + << "Code bits " << irsend.capture.bits << std::endl; + if (hasACState(irsend.capture.decode_type)) { + std::cout << "State value 0x"; + for (uint16_t i = 0; i < irsend.capture.bits / 8; i++) + printf("%02X", irsend.capture.state[i]); + std::cout << std::endl; + } else { + std::cout << "Code value 0x" << + std::hex << irsend.capture.value << std::endl << + "Code address 0x" << std::hex << irsend.capture.address << std::endl + << "Code command 0x" << std::hex << irsend.capture.command << + std::endl; + } + + if (dumpraw || irsend.capture.decode_type == UNKNOWN) + irsend.dumpRawResult(); + + return 0; +} diff --git a/lib/IRremoteESP8266/tools/mkkeywords b/lib/IRremoteESP8266/tools/mkkeywords new file mode 100644 index 0000000..f48d640 --- /dev/null +++ b/lib/IRremoteESP8266/tools/mkkeywords @@ -0,0 +1,80 @@ +#!/bin/bash +# Generate a keywords.txt for the library that is suitable for the Arduino IDE. +# Expects to run from the top directory of the library. + +# Set the locale for sorting +export LC_ALL=C + +cat << EndOfTextEndOfTextEndOfText +######################################### +# Syntax Coloring Map For IRremoteESP8266 +######################################### + +################################################ +# WARNING: Do NOT edit this file directly. +# It is generated by 'tools/mkkeywords' +# e.g. tools/mkkeywords > keywords.txt +################################################ + +####################################################### +# The Arduino IDE requires the use of a tab separator +# between the name and identifier. Without this tab the +# keyword is not highlighted. +# +# Reference: https://github.com/arduino/Arduino/wiki/Arduino-IDE-1.5:-Library-specification#keywords +####################################################### + +####################################### +# Datatypes & Classes (KEYWORD1) +####################################### + +EndOfTextEndOfTextEndOfText + +CLASSES=$(grep "^class " src/*.h | cut -d' ' -f2 | sort -u) +# Manually add typedefs as they are hard to parse out. +CLASSES="${CLASSES} ir_params_t match_result_t" +for i in ${CLASSES}; do + echo -e "${i}\tKEYWORD1" +done | sort -du + +cat << EndOfTextEndOfTextEndOfText + +####################################### +# Methods and Functions (KEYWORD2) +####################################### + +EndOfTextEndOfTextEndOfText + +METHODS=$(egrep "^(u?int|void|bool|String|static|match_result_t).*\(" src/*.cpp| + cut -d' ' -f2- | sed 's/^.*:://' | cut -d'(' -f1 | sort -u | + grep -v ICACHE_RAM_ATTR) +for i in ${METHODS}; do + echo -e "${i}\tKEYWORD2" +done | sort -u + +cat << EndOfTextEndOfTextEndOfText + +####################################### +# Constants (LITERAL1) +####################################### + +EndOfTextEndOfTextEndOfText +LITERALS=$(grep "^#define [A-Z]" src/*.cpp src/*.h | + while read ignore define ignore; do + echo ${define}; + done | sort -u | + grep -v [\(\)] | grep -v ^_ | grep -v _\$ | grep -v VIRTUAL) +ENUMS=$(cat src/*.h | while read a b; do + if [[ ${a} == "};" ]]; then + ENUM=0; + fi; + if [[ ${ENUM} -eq 1 ]]; then + echo $a | sed 's/,//g'; + fi; + if [[ ${a} == "enum" ]]; then + ENUM=1; + fi; + done) +for i in ${LITERALS} ${ENUMS}; do + echo -e "${i}\tLITERAL1" +done | sort -u