Update IRRemote ESP8266 to 2.6.2 and add ESP32 support

This commit is contained in:
Florian committed 2019-07-08 22:21:02 +02:00
1 parent 2ee18320be
commit 24bcdbc97c
195 files changed
+22963 -4737

No files matched your search

+21 -21
View File
@@ -27,7 +27,7 @@
*/
#ifdef ZgatewayIR
#ifdef ESP8266
#if defined(ESP8266) || defined(ESP32)
#include <IRremoteESP8266.h>
#include <IRsend.h> // Needed if you want to send IR commands.
#include <IRrecv.h> // Needed if you want to receive IR commands.
@@ -76,8 +76,8 @@
void setupIR()
{
//IR init parameters
#ifdef ESP8266
//IR init parameters
#if defined(ESP8266) || defined(ESP32)
irsend.begin();
#endif
@@ -107,14 +107,14 @@ void IRtoMQTT(){
IRdata.set("value", (unsigned long)(results.value));
IRdata.set("protocol", (int)(results.decode_type));
IRdata.set("bits",(int)(results.bits));
#ifdef ESP8266 //resultToHexidecimal is only available with IRremoteESP8266
#if defined(ESP8266) || defined(ESP32) //resultToHexidecimal is only available with IRremoteESP8266
String hex = resultToHexidecimal(&results);
IRdata.set("hex", (char *)hex.c_str());
#endif
String rawCode = "";
// Dump data
for (uint16_t i = 1; i < results.rawlen; i++) {
#ifdef ESP8266
#if defined(ESP8266) || defined(ESP32)
if (i % 100 == 0) yield(); // Preemptive yield every 100th entry to feed the WDT.
rawCode = rawCode + (results.rawbuf[i] * RAWTICK);
#else
@@ -126,7 +126,7 @@ void IRtoMQTT(){
IRdata.set("raw", rawCode);
// if needed we directly resend the raw code
if (RawDirectForward){
#ifdef ESP8266
#if defined(ESP8266) || defined(ESP32)
uint16_t rawsend[results.rawlen];
for (uint16_t i = 1; i < results.rawlen; i++) {
if (i % 100 == 0) yield(); // Preemptive yield every 100th entry to feed the WDT.
@@ -204,7 +204,7 @@ void IRtoMQTT(){
else if(strstr(topicOri, "IR_Raw") != NULL){ // sending Raw data
trc(F("IR_Raw"));
//buffer allocation from char datacallback
#ifdef ESP8266
#if defined(ESP8266) || defined(ESP32)
uint16_t Raw[count+1];
#else
unsigned int Raw[count+1];
@@ -253,7 +253,7 @@ void IRtoMQTT(){
}else{
trc(F("Using NEC protocol"));
if (valueBITS == 0) valueBITS = NEC_BITS;
#ifdef ESP8266
#if defined(ESP8266) || defined(ESP32)
irsend.sendNEC(data, valueBITS, valueRPT);
#else
for (int i=0; i <= valueRPT; i++) irsend.sendNEC(data, valueBITS);
@@ -326,7 +326,7 @@ void IRtoMQTT(){
if(strstr(protocol_name, "IR_Raw") != NULL){ // sending Raw data
trc(F("IR_Raw"));
//buffer allocation from char datacallback
#ifdef ESP8266
#if defined(ESP8266) || defined(ESP32)
uint16_t Raw[count+1];
#else
unsigned int Raw[count+1];
@@ -354,7 +354,7 @@ void IRtoMQTT(){
}else{
trc(F("Using NEC protocol"));
if (valueBITS == 0) valueBITS = NEC_BITS;
#ifdef ESP8266
#if defined(ESP8266) || defined(ESP32)
irsend.sendNEC(data, valueBITS, valueRPT);
#else
for (int i=0; i <= valueRPT; i++) irsend.sendNEC(data, valueBITS);
@@ -391,7 +391,7 @@ boolean sendIdentifiedProtocol(const char * protocol_name, unsigned long data, c
#ifdef IR_Whynter
if (strstr(protocol_name, "IR_Whynter") != NULL){
if (valueBITS == 0) valueBITS = WHYNTER_BITS;
#ifdef ESP8266
#if defined(ESP8266) || defined(ESP32)
irsend.sendWhynter(data, valueBITS, valueRPT);
#else
for (int i=0; i <= valueRPT; i++) irsend.sendWhynter(data, valueBITS);
@@ -402,7 +402,7 @@ boolean sendIdentifiedProtocol(const char * protocol_name, unsigned long data, c
#ifdef IR_LG
if (strstr(protocol_name, "IR_LG") != NULL){
if (valueBITS == 0) valueBITS = LG_BITS;
#ifdef ESP8266
#if defined(ESP8266) || defined(ESP32)
irsend.sendLG(data, valueBITS, valueRPT);
#else
for (int i=0; i <= valueRPT; i++) irsend.sendLG(data, valueBITS);
@@ -413,7 +413,7 @@ boolean sendIdentifiedProtocol(const char * protocol_name, unsigned long data, c
#ifdef IR_Sony
if (strstr(protocol_name, "IR_Sony") != NULL){
if (valueBITS == 0) valueBITS = SONY_12_BITS;
#ifdef ESP8266
#if defined(ESP8266) || defined(ESP32)
irsend.sendSony(data, valueBITS, valueRPT);
#else
for (int i=0; i <= valueRPT; i++) irsend.sendSony(data, valueBITS);
@@ -424,7 +424,7 @@ boolean sendIdentifiedProtocol(const char * protocol_name, unsigned long data, c
#ifdef IR_DISH
if (strstr(protocol_name, "IR_DISH") != NULL){
if (valueBITS == 0) valueBITS = DISH_BITS;
#ifdef ESP8266
#if defined(ESP8266) || defined(ESP32)
irsend.sendDISH(data, valueBITS, valueRPT);
#else
for (int i=0; i <= valueRPT; i++) irsend.sendDISH(data, valueBITS);
@@ -435,7 +435,7 @@ boolean sendIdentifiedProtocol(const char * protocol_name, unsigned long data, c
#ifdef IR_RC5
if (strstr(protocol_name, "IR_RC5") != NULL){
if (valueBITS == 0) valueBITS = RC5_BITS;
#ifdef ESP8266
#if defined(ESP8266) || defined(ESP32)
irsend.sendRC5(data, valueBITS, valueRPT);
#else
for (int i=0; i <= valueRPT; i++) irsend.sendRC5(data, valueBITS);
@@ -446,7 +446,7 @@ boolean sendIdentifiedProtocol(const char * protocol_name, unsigned long data, c
#ifdef IR_RC6
if (strstr(protocol_name, "IR_RC6") != NULL){
if (valueBITS == 0) valueBITS = RC6_MODE0_BITS;
#ifdef ESP8266
#if defined(ESP8266) || defined(ESP32)
irsend.sendRC6(data, valueBITS, valueRPT);
#else
for (int i=0; i <= valueRPT; i++) irsend.sendRC6(data, valueBITS);
@@ -457,7 +457,7 @@ boolean sendIdentifiedProtocol(const char * protocol_name, unsigned long data, c
#ifdef IR_Sharp
if (strstr(protocol_name, "IR_Sharp") != NULL){
if (valueBITS == 0) valueBITS = SHARP_BITS;
#ifdef ESP8266
#if defined(ESP8266) || defined(ESP32)
irsend.sendSharpRaw(data, valueBITS, valueRPT);
#else
for (int i=0; i <= valueRPT; i++) irsend.sendSharpRaw(data, valueBITS);
@@ -468,7 +468,7 @@ boolean sendIdentifiedProtocol(const char * protocol_name, unsigned long data, c
#ifdef IR_SAMSUNG
if (strstr(protocol_name, "IR_SAMSUNG") != NULL){
if (valueBITS == 0) valueBITS = SAMSUNG_BITS;
#ifdef ESP8266
#if defined(ESP8266) || defined(ESP32)
irsend.sendSAMSUNG(data, valueBITS, valueRPT);
#else
for (int i=0; i <= valueRPT; i++) irsend.sendSAMSUNG(data, valueBITS);
@@ -479,7 +479,7 @@ boolean sendIdentifiedProtocol(const char * protocol_name, unsigned long data, c
#ifdef IR_JVC
if (strstr(protocol_name, "IR_JVC") != NULL){
if (valueBITS == 0) valueBITS = JVC_BITS;
#ifdef ESP8266
#if defined(ESP8266) || defined(ESP32)
irsend.sendJVC(data, valueBITS, valueRPT);
#else
for (int i=0; i <= valueRPT; i++) irsend.sendJVC(data, valueBITS);
@@ -489,7 +489,7 @@ boolean sendIdentifiedProtocol(const char * protocol_name, unsigned long data, c
#endif
#ifdef IR_PANASONIC
if (strstr(protocol_name, "IR_PANASONIC") != NULL){
#ifdef ESP8266
#if defined(ESP8266) || defined(ESP32)
if (valueBITS == 0) valueBITS = PANASONIC_BITS;
irsend.sendPanasonic(PanasonicAddress, data, valueBITS, valueRPT);
#else
@@ -499,7 +499,7 @@ boolean sendIdentifiedProtocol(const char * protocol_name, unsigned long data, c
}
#endif
#ifdef ESP8266 // sendings not available on arduino
#if defined(ESP8266) || defined(ESP32) // sendings not available on arduino
#ifdef IR_COOLIX
if (strstr(protocol_name, "IR_COOLIX") != NULL){
if (valueBITS == 0) valueBITS = kCoolixBits;
+3 -14
View File
@@ -43,7 +43,7 @@
#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)
#ifdef ESP8266 //IR supported protocols on ESP8266, all supported per default
#if defined(ESP8266) || defined(ESP32) //IR supported protocols on ESP8266, all supported per default
#define IR_GC
#define IR_Raw
#define IR_COOLIX
@@ -98,17 +98,6 @@
#define IR_SAMSUNG36
#define IR_TCL112AC
#define IR_TECO
#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
#elif __AVR_ATmega2560__
#define IR_COOLIX
#define IR_Whynter
@@ -141,9 +130,9 @@
#define IR_RECEIVER_PIN 27
#define IR_EMITTER_PIN 14
#elif __AVR_ATmega2560__
#define IR_RECEIVER_PIN 2 // 2 = D2 on mega
#define IR_RECEIVER_PIN 2 // 2 = D2 on arduino mega
#define IR_EMITTER_PIN 9
#else
#define IR_RECEIVER_PIN 0 // 0 = D2 on arduino
#define IR_RECEIVER_PIN 0 // 0 = D2 on arduino UNO
#define IR_EMITTER_PIN 9
#endif
+2 -1
View File
@@ -2,7 +2,7 @@
### 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/)
- [David Conran](https://github.com/crankyoldgit/) : ESP32 support and pretty much everything else.
- [Roi Dayan](https://github.com/roidayan/)
- [Marcos de Alcântara Marinho](https://github.com/marcosamarinho/)
- [Massimiliano Pinto](https://github.com/pintomax/)
@@ -13,6 +13,7 @@
- [Denes Varga](https://github.com/denxhun/)
- [Brett T. Warden](https://github.com/bwarden/)
- [Fabien Valthier](https://github.com/hcoohb)
- [Ajay Pala](https://github.com/ajaypala/)
All contributors can be found on the [contributors site](https://github.com/markszabo/IRremoteESP8266/graphs/contributors).
+2 -2
View File
@@ -1,6 +1,6 @@
_(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
### Version/revision 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._
@@ -30,7 +30,7 @@ _What can we do to (pref. reliably) repeat what is happening?_
_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._
_Link to an image of the circuit diagram used. Part number of the IR receiver module etc. ESP8266 or ESP32 board type._
### 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._
+4
View File
@@ -8,8 +8,12 @@
# vi/vim
**/*.swp
# vscode
.vscode
## Build environments
# Platformio
**/.pio/
**/.pioenvs/
**/.piolibdeps/
**/.clang_complete
+9 -2
View File
@@ -15,12 +15,14 @@ install:
- ln -s $PWD /usr/local/share/arduino/libraries/
- git clone https://github.com/tzapu/WiFiManager.git /usr/local/share/arduino/libraries/WiFiManager
- git clone https://github.com/knolleary/pubsubclient.git /usr/local/share/arduino/libraries/PubSubClient
- git clone https://github.com/bblanchon/ArduinoJson.git --branch 5.x /usr/local/share/arduino/libraries/ArduinoJson
- arduino --pref "boardsmanager.additional.urls=http://arduino.esp8266.com/stable/package_esp8266com_index.json" --save-prefs
- arduino --install-boards esp8266:esp8266
- arduino --board $BD --save-prefs
- arduino --pref "compiler.warning_level=all" --save-prefs
- sudo apt-get install jq
- sudo pip install pylint
- sudo apt-get purge python-enum34
- sudo apt-get install pylint3
script:
# Check that everything compiles.
- arduino --verify --board $BD $PWD/examples/IRrecvDemo/IRrecvDemo.ino
@@ -41,12 +43,17 @@ script:
- arduino --verify --board $BD $PWD/examples/IRMQTTServer/IRMQTTServer.ino
- arduino --verify --board $BD $PWD/examples/TurnOnToshibaAC/TurnOnToshibaAC.ino
- arduino --verify --board $BD $PWD/examples/ControlSamsungAC/ControlSamsungAC.ino
- arduino --verify --board $BD $PWD/examples/TurnOnPanasonicAC/TurnOnPanasonicAC.ino
- arduino --verify --board $BD $PWD/examples/TurnOnMitsubishiHeavyAc/TurnOnMitsubishiHeavyAc.ino
- arduino --verify --board $BD $PWD/examples/DumbIRRepeater/DumbIRRepeater.ino
- arduino --verify --board $BD $PWD/examples/SmartIRRepeater/SmartIRRepeater.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
- pylint3 -d F0001 {src,test,tools}/*.py
- shopt -u nullglob
# Build and run the unit tests.
- (cd test; make run)
+8 -3
View File
@@ -6,10 +6,11 @@
[![Percentage of issues still open](http://isitmaintained.com/badge/open/markszabo/IRremoteESP8266.svg)](http://isitmaintained.com/project/markszabo/IRremoteESP8266 "Percentage of issues still open")
[![GitLicense](https://gitlicense.com/badge/markszabo/IRremoteESP8266)](https://gitlicense.com/license/markszabo/IRremoteESP8266)
This library enables you to **send _and_ receive** infra-red signals on an [ESP8266 using the Arduino framework](https://github.com/esp8266/Arduino) using common 940nm IR LEDs and common IR receiver modules. e.g. TSOP{17,22,24,36,38,44,48}* etc.
This library enables you to **send _and_ receive** infra-red signals on an [ESP8266](https://github.com/esp8266/Arduino) or an
[ESP32](https://github.com/espressif/arduino-esp32) using the [Arduino framework](https://www.arduino.cc/) using common 940nm IR LEDs and common IR receiver modules. e.g. TSOP{17,22,24,36,38,44,48}* demodulators etc.
## v2.5.6 Now Available
Version 2.5.6 of the library is now [available](https://github.com/markszabo/IRremoteESP8266/releases/latest). You can view the [Release Notes](ReleaseNotes.md) for all the significant changes.
## v2.6.2 Now Available
Version 2.6.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 pre-v2.0
Usage of the library has been slightly changed in v2.0. You will need to change your usage to work with v2.0 and beyond. You can read more about the changes required on our [Upgrade to v2.0](https://github.com/markszabo/IRremoteESP8266/wiki/Upgrading-to-v2.0) page.
@@ -29,6 +30,10 @@ something you likely should not have. You should be able to quickly determine
the new name from the old. e.g. `CONSTANT_NAME` to `kConstantName`.
Use common sense or examining the library's code if this does affect code.
## Supported protocols
You can find the details of which protocols & devices are supported
[here](https://github.com/markszabo/IRremoteESP8266/blob/master/SupportedProtocols.md).
## 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.
+102
View File
@@ -1,5 +1,107 @@
# Release Notes
## _v2.6.2 (20190616)_
**[Features]**
- Initial support for the ESP32 architecture & boards. (#742)
- Add changable GPIO settings to IRMQTTServer. (#730)
- IRMQTTServer: Enforce a repeat for all Coolix calls (#752)
- Basic DAIKIN 160bit send and decode. (#754)
- Add example code for a Smart(er) IR Repeater. (#740)
- Enforce Samsung A/C Quiet & Powerful mutual exclusivity.
**[Misc]**
- IRMQTTServer: Add some memory alloc safety checks. (#749)
- Move some ToString() functions to IRac.cpp (#748)
- Increase tolerance value for TCL112AC protocol. (#745)
- Fix compiler warning in IRutils_test.cpp (#756)
- Scrape Supported Protocols and generate SupportedProtocols.md (#755)
- Make supported device info more organised. (#753)
## _v2.6.1 (20190609)_
**[Breaking Changes]**
- Major rework/breaking changes to Argo A/C support. (#705)
**[Bug Fixes]**
- Correct `set/getQuiet` for Samsung A/C (#736)
- Add missing `on/off()` to IRCoolixAC class. (#725)
- Daikin `set/getEye()` uses wrong bit. (#711)
- IRMQTTServer: Continue to use same Temperature units. (#710)
- Fixed a bug with `setMode()`/`getMode()` for HAIER_AC. (#705)
**[Features]**
- Add set/getPowerful for Samsung A/C (#736)
- Add `calibrate()` to all the A/C classes. (#735)
- IRMQTTServer: Add sequencing for sending MQTT IR commands. (#723)
- Add support for Fujitsu AR-REB1E & AR-JW2 remotes. (#718)
- Add Beta `decodeTrotec()` support. (#719)
- Add experimental `decodeArgo()` support. (#717)
- Support for Goodweather A/Cs. (#715)
- Add `DISABLE_CAPTURE_WHILE_TRANSMITTING` feature to IRMQTTServer. (#713)
- Support for Lixil Inax Toilet protocol. (#712)
- Add `set/getWeeklyTimerEnable()` to Daikin (#711)
- IRMQTTServer: Update Common A/C settings based on received IR messages. (#705)
- Add day of week to DAIKIN protocol (#699)
- Add limited support for Sharp A/C (#696)
- SAMSUNG_AC: Make sure special power mode messages are sent. (#695)
- Add `set/getPowerful()` (turbo) to DAIKIN216 (#693)
**[Misc]**
- Add kPeriodOffset for CPU Freq of 160MHz. (#729)
- Example code for a Dumb IR repeater. (#737)
- Update swing handling for Fujitsu A/Cs. (#724)
- Add function to convert `decode_results` to `sendRaw()` array. (#721)
- Attempt to reduce heap fragmentation from strings. (#707)
- Update Fujitsu A/C example code to safer settings (#716)
- Enforce better `const` usage in IRUtils. (#708)
- Attempt to reduce heap fragmentation by A/C `toString()`s. (#694)
- Minor changes to DAIKIN216 timings and features. (#693)
## _v2.6.0 (20190430)_
**[Bug Fixes]**
- Fixed problem where LG protocol used wrong duty cycle for repeat. (#687)
- Fix checksum calculation for Daikin protocols. (#678)
- Fix the byte array version of sendGree() (#684, #685)
- Fix artificial vs. real state creation on HaierAC. (#668, #671)
- Fix issues caused by having `MQTT_ENABLE` set to false. (#677)
- Fix compile problem when DEBUG is defined. (#673, #674)
- Fix Minor bug with MQTT_ENABLE False condition (#654)
**[Features]**
- Experimental support for DAIKIN216 (ARC433B69) (#690)
- Experimental support for Mitsubishi Heavy Industries A/Cs. (#660, #665, #667)
- Support more features of TCL A/C (#656)
- Add LEGO(TM) Power Functions IR protocol. (#655)
- Add Panasonic AC RKR model & Example (#649)
- DAIKIN/IRDaikinESP overhaul and add Comfort mode support. (#678)
**WARNING**: Previous `sendDaikin()` calls may not work.
Please recapture codes or use `kDaikinStateLengthShort` for
`nbytes` in those calls.
- IRMQTTServer: Move MQTT server and other parameters to WifiManager. (#680)
**WARNING**: Previous users may need to fully wipe/reset the
SPIFFS/WifiManager settings by visiting
`http://<your_esp8266's_ip_address>/reset` prior to or
after update.
- Add Wifi filtering options to IRMQTTServer. (#679)
- Add advanced aircon/climate functionality to IRMQTTServer (#677)
- Initial prototype of a common interface for all A/Cs. (#664)
- Improve MQTT topic usage for feedback messages. (#663)
- Add multiple independent GPIO sending support via MQTT. (#661)
**[Misc]**
- Adjust kGreeHdrSpace to 4500 (#684, #686)
- Add Home Assistant mqtt climate instructions. (#682)
- Implement htmlEscape() to prevent XSS etc. (#681)
- Add F() Macros (#670)
- Update Daikin2's Cool mode min temp to 18C (#658)
- Change per byte bit-order in Electra protocol. (#648)
- Improve Daikin2 power on/off. (#647)
## _v2.5.6 (20190316)_
**[Bug Fixes]**
+129
View File
@@ -0,0 +1,129 @@
<!--- WARNING: Do NOT edit this file directly.
It is generated by './tools/scrape_supported_devices.py'.
Last generated: Sun Jun 16 19:01:48 2019 --->
# IR Protocols supported by this library
| Protocol | Brand | Model | A/C Model | Detailed A/C Support |
| --- | --- | --- | --- | --- |
| [Aiwa](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_Aiwa.cpp) | **Aiwa** | RC-T501 RCU | | - |
| [Argo](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_Argo.cpp) | **[Argo](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_Argo.h)** | Ulisse 13 DCI Mobile Split A/C | | Yes |
| [Carrier](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_Carrier.cpp) | **Carrier/Surrey** | 42QG5A55970 remote<BR>53NGK009/012 Inverter<BR>619EGX0090E0 A/C<BR>619EGX0120E0 A/C<BR>619EGX0180E0 A/C<BR>619EGX0220E0 A/C | | - |
| [Coolix](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_Coolix.cpp) | **[Beko](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_Coolix.h)** | BINR 070/071 split-type A/C<BR>BINR 070/071 split-type A/C<BR>RG57K7(B)/BGEF Remote<BR>RG57K7(B)/BGEF Remote | | Yes |
| [Coolix](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_Coolix.cpp) | **[Midea](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_Coolix.h)** | MS12FU-10HRDN1-QRD0GW(B) A/C<BR>MS12FU-10HRDN1-QRD0GW(B) A/C<BR>MSABAU-07HRFN1-QRD0GW A/C (circa 2016)<BR>MSABAU-07HRFN1-QRD0GW A/C (circa 2016)<BR>RG52D/BGE Remote<BR>RG52D/BGE Remote | | Yes |
| [Daikin](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_Daikin.cpp) | **[Daikin](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_Daikin.h)** | ARC423A5 remote<BR>ARC433** remote<BR>ARC433B69 remote<BR>ARC477A1 remote<BR>FTXZ25NV1B A/C<BR>FTXZ35NV1B A/C<BR>FTXZ50NV1B A/C | | Yes |
| [Denon](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_Denon.cpp) | **Unknown** | | | - |
| [Dish](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_Dish.cpp) | **DISH NETWORK** | echostar 301 | | - |
| [Electra](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_Electra.cpp) | **Unknown** | | | - |
| [Fujitsu](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_Fujitsu.cpp) | **[Fujitsu](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_Fujitsu.h)** | AR-DB1 remote<BR>AR-RAE1E remote<BR>AR-RAH2E remote<BR>AR-REB1E remote<BR>AST9RSGCW A/C<BR>ASYG30LFCA A/C<BR>ASYG7LMCA A/C | ARDB1<BR>ARJW2<BR>ARRAH2E<BR>ARREB1E | Yes |
| [Fujitsu](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_Fujitsu.cpp) | **[Fujitsu General](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_Fujitsu.h)** | AR-JW2 remote | ARDB1<BR>ARJW2<BR>ARRAH2E<BR>ARREB1E | Yes |
| [GICable](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_GICable.cpp) | **Unknown** | | | - |
| [GlobalCache](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_GlobalCache.cpp) | **Unknown** | | | - |
| [Goodweather](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_Goodweather.cpp) | **[Goodweather](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_Goodweather.h)** | ZH/JT-03 remote | | Yes |
| [Gree](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_Gree.cpp) | **[EKOKAI](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_Gree.h)** | A/C | | Yes |
| [Gree](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_Gree.cpp) | **[Ultimate](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_Gree.h)** | Heat Pump | | Yes |
| [Haier](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_Haier.cpp) | **[Haier](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_Haier.h)** | HSU-09HMC203 A/C<BR>HSU07-HEA03 remote<BR>YR-W02 remote | | Yes |
| [Hitachi](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_Hitachi.cpp) | **[Hitachi](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_Hitachi.h)** | LT0541-HTA remote<BR>RAS-35THA6 remote<BR>Series VI A/C (Circa 2007) | | Yes |
| [Inax](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_Inax.cpp) | **Lixil** | Inax DT-BA283 Toilet | | - |
| [JVC](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_JVC.cpp) | **Unknown** | | | - |
| [Kelvinator](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_Kelvinator.cpp) | **[Kelvinator](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_Kelvinator.h)** | KSV26CRC A/C<BR>KSV26HRC A/C<BR>KSV35CRC A/C<BR>KSV35HRC A/C<BR>KSV53HRC A/C<BR>KSV62HRC A/C<BR>KSV70CRC A/C<BR>KSV70HRC A/C<BR>KSV80HRC A/C<BR>YALIF Remote | | Yes |
| [LG](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_LG.cpp) | **[LG](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_LG.h)** | 6711A20083V remote<BR>6711A20083V remote<BR>AKB74395308 remote<BR>AKB74395308 remote | | Yes |
| [Lasertag](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_Lasertag.cpp) | **Unknown** | | | - |
| [Lego](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_Lego.cpp) | **LEGO Power Functions** | IR Receiver | | - |
| [Lutron](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_Lutron.cpp) | **Unknown** | | | - |
| [MWM](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_MWM.cpp) | **Unknown** | | | - |
| [Magiquest](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_Magiquest.cpp) | **[Unknown](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_Magiquest.h)** | | | Yes |
| [Midea](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_Midea.cpp) | **[Pioneer System](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_Midea.h)** | RUBO18GMFILCAD A/C (18K BTU)<BR>RYBO12GMFILCAD A/C (12K BTU) | | Yes |
| [Mitsubishi](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_Mitsubishi.cpp) | **[Mitsubishi](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_Mitsubishi.h)** | HC3000 Projector<BR>TV | | Yes |
| [MitsubishiHeavy](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_MitsubishiHeavy.cpp) | **[Mitsubishi Heavy Industries](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_MitsubishiHeavy.h)** | RKX502A001C remote<BR>RLA502A700B remote<BR>SRKxxZJ-S A/C<BR>SRKxxZM-S A/C<BR>SRKxxZMXA-S A/C | | Yes |
| [NEC](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_NEC.cpp) | **[Unknown](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_NEC.h)** | | | Yes |
| [Nikai](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_Nikai.cpp) | **Unknown** | | | - |
| [Panasonic](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_Panasonic.cpp) | **[Panasonic](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_Panasonic.h)** | A75C2311 remote (CKP)<BR>A75C3704 remote<BR>A75C3747 remote<BR>A75C3747 remote<BR>A75C3747 remote<BR>A75C3747 remote<BR>CKP series A/C<BR>CS-ME10CKPG A/C<BR>CS-ME12CKPG A/C<BR>CS-ME14CKPG A/C<BR>CS-YW9MKD A/C<BR>CS-Z9RKR A/C<BR>DKE series A/C<BR>JKE series A/C<BR>NKE series A/C<BR>RKR series A/C<BR>TV | CKP<BR>DKE<BR>JKE<BR>LKE<BR>NKE<BR>RKR | Yes |
| [Pioneer](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_Pioneer.cpp) | **Unknown** | | | - |
| [Pronto](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_Pronto.cpp) | **Unknown** | | | - |
| [RC5_RC6](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_RC5_RC6.cpp) | **Unknown** | | | - |
| [RCMM](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_RCMM.cpp) | **Microsoft** | XBOX 360 | | - |
| [Samsung](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_Samsung.cpp) | **[Samsung](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_Samsung.h)** | AR12KSFPEWQNET A/C<BR>IEC-R03 remote<BR>UA55H6300 TV | | Yes |
| [Sanyo](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_Sanyo.cpp) | **Unknown** | | | - |
| [Sharp](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_Sharp.cpp) | **[Sharp](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_Sharp.h)** | AY-ZP40KR A/C<BR>LC-52D62U TV | | Yes |
| [Sherwood](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_Sherwood.cpp) | **Sherwood** | RC-138 remote<BR>RD6505(B) Receiver | | - |
| [Sony](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_Sony.cpp) | **Unknown** | | | - |
| [Tcl](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_Tcl.cpp) | **[Unknown](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_Tcl.h)** | | | Yes |
| [Teco](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_Teco.cpp) | **[Unknown](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_Teco.h)** | | | Yes |
| [Toshiba](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_Toshiba.cpp) | **[Toshiba](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_Toshiba.h)** | Akita EVO II<BR>RAS 18SKP-ES<BR>RAS-B13N3KV2<BR>RAS-B13N3KVP-E<BR>WC-L03SE<BR>WH-TA04NE | | Yes |
| [Trotec](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_Trotec.cpp) | **[Unknown](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_Trotec.h)** | | | Yes |
| [Vestel](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_Vestel.cpp) | **[Vestel](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_Vestel.h)** | BIOX CXP-9 A/C (9K BTU) | | Yes |
| [Whirlpool](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_Whirlpool.cpp) | **[Whirlpool](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_Whirlpool.h)** | DG11J1-04 remote<BR>DG11J1-3A remote<BR>DG11J1-91 remote<BR>SPIS409L A/C<BR>SPIS412L A/C<BR>SPIW409L A/C<BR>SPIW412L A/C<BR>SPIW418L A/C | DG11J13A<BR>DG11J191 | Yes |
| [Whynter](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_Whynter.cpp) | **Whynter** | ARC-110WD A/C | | - |
## Send only protocols:
- GLOBALCACHE
- PRONTO
- RAW
- SHERWOOD
## Send & decodable protocols:
- AIWA_RC_T501
- ARGO
- CARRIER_AC
- COOLIX
- DAIKIN
- DAIKIN160
- DAIKIN2
- DAIKIN216
- DENON
- DISH
- ELECTRA_AC
- FUJITSU_AC
- GICABLE
- GOODWEATHER
- GREE
- HAIER_AC
- HAIER_AC_YRW02
- HITACHI_AC
- HITACHI_AC1
- HITACHI_AC2
- INAX
- JVC
- KELVINATOR
- LASERTAG
- LEGOPF
- LG
- LG2
- LUTRON
- MAGIQUEST
- MIDEA
- MITSUBISHI
- MITSUBISHI2
- MITSUBISHI_AC
- MITSUBISHI_HEAVY_152
- MITSUBISHI_HEAVY_88
- MWM
- NEC
- NEC_LIKE
- NIKAI
- PANASONIC
- PANASONIC_AC
- PIONEER
- RC5
- RC5X
- RC6
- RCMM
- SAMSUNG
- SAMSUNG36
- SAMSUNG_AC
- SANYO
- SANYO_LC7461
- SHARP
- SHARP_AC
- SONY
- TCL112AC
- TECO
- TOSHIBA_AC
- TROTEC
- VESTEL_AC
- WHIRLPOOL_AC
- WHYNTER
@@ -23,9 +23,7 @@
* * 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 <Arduino.h>
#endif
#include <IRremoteESP8266.h>
#include <IRsend.h>
#include <ir_Samsung.h>
@@ -6,11 +6,23 @@ src_dir=.
build_flags =
lib_deps_builtin =
lib_deps_external =
lib_ldf_mode = chain+
[env:nodemcuv2]
platform = espressif8266
framework = arduino
board = nodemcuv2
lib_ldf_mode = ${common.lib_ldf_mode}
build_flags = ${common.build_flags}
lib_deps =
${common.lib_deps_builtin}
${common.lib_deps_external}
[env:esp32dev]
platform = espressif32
framework = arduino
board = esp32dev
lib_ldf_mode = ${common.lib_ldf_mode}
build_flags = ${common.build_flags}
lib_deps =
${common.lib_deps_builtin}
@@ -0,0 +1,130 @@
/*
* IRremoteESP8266: DumbIRRepeater.ino - Record and playback IR codes.
* Copyright 2019 David Conran (crankyoldgit)
*
* This program will try to capture incoming IR messages and replay them back.
* It doesn't use any of the advanced detection features, thus it will just
* replay the messages at fixed modulated frequency (kFrequency) and a 50% duty
* cycle.
*
* Note:
* This might NOT be the frequency of the incoming message, so some replayed
* messages may not work. The frequency of incoming messages & duty cycle is
* lost at the point of the Hardware IR demodulator. The ESP can't see it.
*
* W A R N I N G
* This code is just for educational/example use only. No help will be given
* to you to make it do something else, or to make it work with some
* weird device or circuit, or to make it more usable or practical.
* If it works for you. Great. If not, Congratulations on changing/fixing it.
*
* An IR detector/demodulator must be connected to the input, kRecvPin.
* An IR LED circuit must be connected to the output, kIrLedPin.
*
* Example circuit diagrams (both are needed):
* https://github.com/markszabo/IRremoteESP8266/wiki#ir-receiving
* 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
* * Some 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 ESP will interfere.
* * Pin 3/RX/RXD0: Any serial transmissions to the ESP 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.
*
* Changes:
* Version 1.0: June, 2019
* - Initial version.
*/
#include <Arduino.h>
#include <IRsend.h>
#include <IRrecv.h>
#include <IRremoteESP8266.h>
#include <IRutils.h>
// ==================== start of TUNEABLE PARAMETERS ====================
// The GPIO an IR detector/demodulator is connected to. Recommended: 14 (D5)
const uint16_t kRecvPin = 14;
// GPIO to use to control the IR LED circuit. Recommended: 4 (D2).
const uint16_t kIrLedPin = 4;
// The Serial connection baud rate.
// NOTE: Make sure you set your Serial Monitor to the same speed.
const uint32_t kBaudRate = 115200;
// As this program is a special purpose capture/resender, let's use a larger
// than expected buffer so we can handle very large IR messages.
// i.e. Up to 512 bits.
const uint16_t kCaptureBufferSize = 1024;
// kTimeout is the Nr. of milli-Seconds of no-more-data before we consider a
// message ended.
const uint8_t kTimeout = 50; // Milli-Seconds
// kFrequency is the modulation frequency all messages will be replayed at.
const uint16_t kFrequency = 38000; // in Hz. e.g. 38kHz.
// ==================== end of TUNEABLE PARAMETERS ====================
// The IR transmitter.
IRsend irsend(kIrLedPin);
// The IR receiver.
IRrecv irrecv(kRecvPin, kCaptureBufferSize, kTimeout, false);
// Somewhere to store the captured message.
decode_results results;
// This section of code runs only once at start-up.
void setup() {
irrecv.enableIRIn(); // Start up the IR receiver.
irsend.begin(); // Start up the IR sender.
Serial.begin(kBaudRate, SERIAL_8N1);
while (!Serial) // Wait for the serial connection to be establised.
delay(50);
Serial.println();
Serial.print("DumbIRRepeater is now running and waiting for IR input "
"on Pin ");
Serial.println(kRecvPin);
Serial.print("and will retransmit it on Pin ");
Serial.println(kIrLedPin);
}
// The repeating section of the code
void loop() {
// Check if an IR message has been received.
if (irrecv.decode(&results)) { // We have captured something.
// The capture has stopped at this point.
// Convert the results into an array suitable for sendRaw().
// resultToRawArray() allocates the memory we need for the array.
uint16_t *raw_array = resultToRawArray(&results);
// Find out how many elements are in the array.
uint16_t length = getCorrectedRawLength(&results);
// Send it out via the IR LED circuit.
irsend.sendRaw(raw_array, length, kFrequency);
// Resume capturing IR messages. It was not restarted until after we sent
// the message so we didn't capture our own message.
irrecv.resume();
// Deallocate the memory allocated by resultToRawArray().
delete [] raw_array;
// Display a crude timestamp & notification.
uint32_t now = millis();
Serial.printf(
"%06u.%03u: A message that was %d entries long was retransmitted.\n",
now / 1000, now % 1000, length);
}
yield(); // Or delay(milliseconds); This ensures the ESP doesn't WDT reset.
}
@@ -0,0 +1,29 @@
[platformio]
lib_extra_dirs = ../../
src_dir=.
[common]
build_flags =
lib_deps_builtin =
lib_deps_external =
lib_ldf_mode = chain+
[env:nodemcuv2]
platform = espressif8266
framework = arduino
board = nodemcuv2
lib_ldf_mode = ${common.lib_ldf_mode}
build_flags = ${common.build_flags}
lib_deps =
${common.lib_deps_builtin}
${common.lib_deps_external}
[env:esp32dev]
platform = espressif32
framework = arduino
board = esp32dev
lib_ldf_mode = ${common.lib_ldf_mode}
build_flags = ${common.build_flags}
lib_deps =
${common.lib_deps_builtin}
${common.lib_deps_external}
@@ -36,9 +36,7 @@
* for your first time. e.g. ESP-12 etc.
*/
#ifndef UNIT_TEST
#include <Arduino.h>
#endif
#include <IRremoteESP8266.h>
#include <IRsend.h>
@@ -6,11 +6,23 @@ src_dir=.
build_flags =
lib_deps_builtin =
lib_deps_external =
lib_ldf_mode = chain+
[env:nodemcuv2]
platform = espressif8266
framework = arduino
board = nodemcuv2
lib_ldf_mode = ${common.lib_ldf_mode}
build_flags = ${common.build_flags}
lib_deps =
${common.lib_deps_builtin}
${common.lib_deps_external}
[env:esp32dev]
platform = espressif32
framework = arduino
board = esp32dev
lib_ldf_mode = ${common.lib_ldf_mode}
build_flags = ${common.build_flags}
lib_deps =
${common.lib_deps_builtin}
@@ -37,10 +37,13 @@
* can check your wifi router for it's address.
*/
#ifndef UNIT_TEST
#include <Arduino.h>
#endif
#if defined(ESP8266)
#include <ESP8266WiFi.h>
#endif // ESP8266
#if defined(ESP32)
#include <WiFi.h>
#endif // ESP32
#include <IRremoteESP8266.h>
#include <IRsend.h>
#include <WiFiClient.h>
@@ -6,11 +6,23 @@ src_dir=.
build_flags =
lib_deps_builtin =
lib_deps_external =
lib_ldf_mode = chain+
[env:nodemcuv2]
platform = espressif8266
framework = arduino
board = nodemcuv2
lib_ldf_mode = ${common.lib_ldf_mode}
build_flags = ${common.build_flags}
lib_deps =
${common.lib_deps_builtin}
${common.lib_deps_external}
[env:esp32dev]
platform = espressif32
framework = arduino
board = esp32dev
lib_ldf_mode = ${common.lib_ldf_mode}
build_flags = ${common.build_flags}
lib_deps =
${common.lib_deps_builtin}
@@ -0,0 +1,334 @@
/*
* Send & receive arbitrary IR codes via a web server or MQTT.
* Copyright David Conran 2016, 2017, 2018, 2019
*/
#ifndef EXAMPLES_IRMQTTSERVER_IRMQTTSERVER_H_
#define EXAMPLES_IRMQTTSERVER_IRMQTTSERVER_H_
#include <IRremoteESP8266.h>
#include <IRrecv.h>
#include <IRsend.h>
#include <IRtimer.h>
#include <IRutils.h>
#include <IRac.h>
// ---------------- Start of User Configuration Section ------------------------
#ifndef MQTT_ENABLE
#define MQTT_ENABLE true // Whether or not MQTT is used at all.
#endif // MQTT_ENABLE
// ---------------------- Board Related Settings -------------------------------
// NOTE: Make sure you set your Serial Monitor to the same speed.
#define BAUD_RATE 115200 // Serial port Baud rate.
// Change if you need multiple independent send gpios & topics. (MQTT only)
const uint8_t kNrOfIrTxGpios = 1;
// Default GPIO the IR LED is connected to/controlled by. GPIO 4 = D2.
// For an ESP-01 we suggest you use RX/GPIO3/Pin 7. i.e. kDefaultIrLed = 3
// Note: A value of -1 means unused.
const int8_t kDefaultIrLed = 4; // <=- CHANGE_ME (optional)
// Default GPIO the IR demodulator is connected to/controlled by. GPIO 14 = D5.
const int8_t kDefaultIrRx = 14; // <=- CHANGE_ME (optional)
// Enable/disable receiving/decoding IR messages entirely.
// Note: IR_RX costs about 40k+ of program memory.
#define IR_RX true
// Should we use PULLUP on the IR Rx gpio?
#define IR_RX_PULLUP false
// --------------------- Network Related Settings ------------------------------
const uint16_t kHttpPort = 80; // The TCP port the HTTP server is listening on.
// Change to 'true'/'false' if you do/don't want these features or functions.
#define USE_STATIC_IP false // Change to 'true' if you don't want to use DHCP.
// We obtain our network config via DHCP by default but allow an easy way to
// use a static IP config.
#if USE_STATIC_IP
const IPAddress kIPAddress = IPAddress(10, 0, 1, 78);
const IPAddress kGateway = IPAddress(10, 0, 1, 1);
const IPAddress kSubnetMask = IPAddress(255, 255, 255, 0);
#endif // USE_STATIC_IP
// See: https://github.com/tzapu/WiFiManager#filter-networks for these settings.
#define HIDE_DUPLIATE_NETWORKS false // Should WifiManager hide duplicate SSIDs
// #define MIN_SIGNAL_STRENGTH 20 // Minimum WiFi signal stength (percentage)
// before we will connect.
// The unset default is 8%.
// (Uncomment to enable)
// Do you want/need mdns enabled? (https://en.wikipedia.org/wiki/Multicast_DNS)
#define MDNS_ENABLE true // `false` to disable and save ~21k of program space.
// ----------------------- HTTP Related Settings -------------------------------
#define FIRMWARE_OTA true // Allow remote update of the firmware via http.
// Less secure if enabled.
// Note: Firmware OTA is also disabled until
// a password is set.
#define HTML_PASSWORD_ENABLE false // Protect access to the HTML interface.
// Note: OTA & GPIO updates are always
// passworded.
// If you do not set a password, Firmware OTA & GPIO updates will be blocked.
// ----------------------- MQTT Related Settings -------------------------------
#if MQTT_ENABLE
const uint32_t kMqttReconnectTime = 5000; // Delay(ms) between reconnect tries.
#define MQTT_ACK "sent" // Sub-topic we send back acknowledgements on.
#define MQTT_SEND "send" // Sub-topic we get new commands from.
#define MQTT_RECV "received" // Topic we send received IRs to.
#define MQTT_LOG "log" // Topic we send log messages to.
#define MQTT_LWT "status" // Topic for the Last Will & Testament.
#define MQTT_CLIMATE "ac" // Sub-topic for the climate topics.
#define MQTT_CLIMATE_CMND "cmnd" // Sub-topic for the climate command topics.
#define MQTT_CLIMATE_STAT "stat" // Sub-topic for the climate stat topics.
#define MQTTbroadcastInterval 10 * 60 // Seconds between rebroadcasts
#define QOS 1 // MQTT broker should queue up any unreceived messages for us
// #define QOS 0 // MQTT broker WON'T queue up messages for us. Fire & Forget.
#endif // MQTT_ENABLE
// ------------------------ IR Capture Settings --------------------------------
// Should we stop listening for IR messages when we send a message via IR?
// Set this to `true` if your IR demodulator is picking up self transmissions.
// Use `false` if it isn't or can't see the self-sent transmissions
// Using `true` may mean some incoming IR messages are lost or garbled.
// i.e. `false` is better if you can get away with it.
#define DISABLE_CAPTURE_WHILE_TRANSMITTING true
// Let's use a larger than normal buffer so we can handle AirCon remote codes.
const uint16_t kCaptureBufferSize = 1024;
#if DECODE_AC
// Some A/C units have gaps in their protocols of ~40ms. e.g. Kelvinator
// A value this large may swallow repeats of some protocols
const uint8_t kCaptureTimeout = 50; // Milliseconds
#else // DECODE_AC
// Suits most messages, while not swallowing many repeats.
const uint8_t kCaptureTimeout = 15; // Milliseconds
#endif // DECODE_AC
// Ignore unknown messages with <10 pulses (see also REPORT_UNKNOWNS)
const uint16_t kMinUnknownSize = 2 * 10;
#define REPORT_UNKNOWNS false // Report inbound IR messages that we don't know.
#define REPORT_RAW_UNKNOWNS false // Report the whole buffer, recommended:
// MQTT_MAX_PACKET_SIZE of 1024 or more
// Should we use and report individual A/C settings we capture via IR if we
// can understand the individual settings of the remote.
// e.g. Aquire the A/C settings from an actual A/C IR remote and override
// any local settings set via MQTT/HTTP etc.
#define USE_DECODED_AC_SETTINGS true // `false` to disable. `true` to enable.
// Should we allow or ignore an A/C IR remote to override the A/C protocol/model
// as set via MQTT or HTTP?
// e.g. If `true`, you can use any fully supported A/C remote to control
// another brand's or model's A/C unit. `false` means change to the new
// protocol/model if we support it via `USE_DECODED_AC_SETTINGS`.
#define IGNORE_DECODED_AC_PROTOCOL true
// Do we (re-)send the captured & decoded A/C message via the IR_LED?
// `false` if you don't want to repeat the captured message.
// e.g. Useful if the IR demodulator is located in the path between the remote
// and the A/C unit so the command isn't sent twice.
// `true` if want it sent anyway.
// e.g. The IR demodulator is in a completely different location than than the
// actual a/c unit.
#define REPLAY_DECODED_AC_MESSAGE false
// ------------------------ Advanced Usage Only --------------------------------
#define KEY_PROTOCOL "protocol"
#define KEY_MODEL "model"
#define KEY_POWER "power"
#define KEY_MODE "mode"
#define KEY_TEMP "temp"
#define KEY_FANSPEED "fanspeed"
#define KEY_SWINGV "swingv"
#define KEY_SWINGH "swingh"
#define KEY_QUIET "quiet"
#define KEY_TURBO "turbo"
#define KEY_LIGHT "light"
#define KEY_BEEP "beep"
#define KEY_ECONO "econo"
#define KEY_SLEEP "sleep"
#define KEY_FILTER "filter"
#define KEY_CLEAN "clean"
#define KEY_CELSIUS "use_celsius"
// HTML arguments we will parse for IR code information.
#define KEY_TYPE "type" // KEY_PROTOCOL is also checked too.
#define KEY_CODE "code"
#define KEY_BITS "bits"
#define KEY_REPEAT "repeats"
// GPIO html/config keys
#define KEY_TX_GPIO "tx"
#define KEY_RX_GPIO "rx"
// Text for Last Will & Testament status messages.
const char* kLwtOnline = "Online";
const char* kLwtOffline = "Offline";
const uint8_t kHostnameLength = 30;
const uint8_t kPortLength = 5; // Largest value of uint16_t is "65535".
const uint8_t kUsernameLength = 15;
const uint8_t kPasswordLength = 20;
// -------------------------- Debug Settings -----------------------------------
// Debug output is disabled if any of the IR pins are on the TX (D1) pin.
// See `isSerialGpioUsedByIr()`.
// Note: Debug costs ~6k of program space.
#ifndef DEBUG
#define DEBUG false // Change to 'true' for serial debug output.
#endif // DEBUG
// ----------------- End of User Configuration Section -------------------------
// Constants
#define _MY_VERSION_ "v1.2.0"
const uint8_t kRebootTime = 15; // Seconds
const uint8_t kQuickDisplayTime = 2; // Seconds
// Gpio related
const int8_t kGpioUnused = -1;
#if defined(ESP8266)
const int8_t kTxGpios[] = {-1, 0, 1, 2, 3, 4, 5, 12, 13, 14, 15, 16};
const int8_t kRxGpios[] = {-1, 0, 1, 2, 3, 4, 5, 12, 13, 14, 15};
#endif // ESP8266
#if defined(ESP32)
const int8_t kTxGpios[] = {
-1, 0, 1, 2, 3, 4, 5, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
25, 26, 27, 28, 29, 30, 31, 32, 33};
const int8_t kRxGpios[] = {
-1, 1, 2, 3, 4, 5, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39};
#endif // ESP32
// JSON stuff
// Name of the json config file in SPIFFS.
const char* kConfigFile = "/config.json";
const char* kMqttServerKey = "mqtt_server";
const char* kMqttPortKey = "mqtt_port";
const char* kMqttUserKey = "mqtt_user";
const char* kMqttPassKey = "mqtt_pass";
const char* kMqttPrefixKey = "mqtt_prefix";
const char* kHostnameKey = "hostname";
const char* kHttpUserKey = "http_user";
const char* kHttpPassKey = "http_pass";
const char* kCommandDelimiter = ",";
// URLs
const char* kUrlRoot = "/";
const char* kUrlAdmin = "/admin";
const char* kUrlAircon = "/aircon";
const char* kUrlSendDiscovery = "/send_discovery";
const char* kUrlExamples = "/examples";
const char* kUrlGpio = "/gpio";
const char* kUrlGpioSet = "/gpio/set";
const char* kUrlInfo = "/info";
const char* kUrlReboot = "/quitquitquit";
const char* kUrlWipe = "/reset";
#if MQTT_ENABLE
const uint32_t kBroadcastPeriodMs = MQTTbroadcastInterval * 1000; // mSeconds.
const uint32_t kStatListenPeriodMs = 5 * 1000; // mSeconds
const int32_t kMaxPauseMs = 10000; // 10 Seconds.
const char* kSequenceDelimiter = ";";
const char kPauseChar = 'P';
#if defined(ESP8266)
const uint32_t kChipId = ESP.getChipId();
#endif // ESP8266
#if defined(ESP32)
const uint32_t kChipId = ESP.getEfuseMac(); // Discard the top 16 bits.
#endif // ESP32
const char* kClimateTopics =
"(" KEY_PROTOCOL "|" KEY_MODEL "|" KEY_POWER "|" KEY_MODE "|" KEY_TEMP "|"
KEY_FANSPEED "|" KEY_SWINGV "|" KEY_SWINGH "|" KEY_QUIET "|"
KEY_TURBO "|" KEY_LIGHT "|" KEY_BEEP "|" KEY_ECONO "|" KEY_SLEEP "|"
KEY_FILTER "|" KEY_CLEAN "|" KEY_CELSIUS ")<br>";
void mqttCallback(char* topic, byte* payload, unsigned int length);
String listOfCommandTopics(void);
void handleSendMqttDiscovery(void);
void subscribing(const String topic_name);
void unsubscribing(const String topic_name);
void mqttLog(const String mesg);
bool mountSpiffs(void);
bool reconnect(void);
void receivingMQTT(String const topic_name, String const callback_str);
void callback(char* topic, byte* payload, unsigned int length);
void sendMQTTDiscovery(const char *topic);
void doBroadcast(TimerMs *timer, const uint32_t interval,
const stdAc::state_t state, const bool retain,
const bool force);
#endif // MQTT_ENABLE
bool isSerialGpioUsedByIr(void);
void debug(const char *str);
void saveWifiConfigCallback(void);
void saveWifiConfig(void);
void loadWifiConfigFile(void);
String msToHumanString(uint32_t const msecs);
String timeElapsed(uint32_t const msec);
String timeSince(uint32_t const start);
String gpioToString(const int16_t gpio);
uint8_t getDefaultIrSendIdx(void);
IRsend* getDefaultIrSendPtr(void);
int8_t getDefaultTxGpio(void);
String listOfTxGpios(void);
bool hasUnsafeHTMLChars(String input);
String htmlHeader(const String title, const String h1_text = "");
String htmlEnd(void);
String htmlButton(const String url, const String button,
const String text = "");
String htmlMenu(void);
void handleRoot(void);
String addJsReloadUrl(const String url, const uint16_t timeout_s,
const bool notify);
void handleExamples(void);
String htmlSelectBool(const String name, const bool def);
String htmlSelectProtocol(const String name, const decode_type_t def);
String htmlSelectModel(const String name, const int16_t def);
String htmlSelectMode(const String name, const stdAc::opmode_t def);
String htmlSelectFanspeed(const String name, const stdAc::fanspeed_t def);
String htmlSelectSwingv(const String name, const stdAc::swingv_t def);
String htmlSelectSwingh(const String name, const stdAc::swingh_t def);
void handleAirCon(void);
void handleAirConSet(void);
void handleAdmin(void);
void handleInfo(void);
void handleReset(void);
void handleReboot(void);
bool parseStringAndSendAirCon(IRsend *irsend, const uint16_t irType,
const String str);
uint16_t countValuesInStr(const String str, char sep);
uint16_t * newCodeArray(const uint16_t size);
#if SEND_GLOBALCACHE
bool parseStringAndSendGC(IRsend *irsend, const String str);
#endif // SEND_GLOBALCACHE
#if SEND_PRONTO
bool parseStringAndSendPronto(IRsend *irsend, const String str,
uint16_t repeats);
#endif // SEND_PRONTO
#if SEND_RAW
bool parseStringAndSendRaw(IRsend *irsend, const String str);
#endif // SEND_RAW
void handleIr(void);
void handleNotFound(void);
void setup_wifi(void);
void init_vars(void);
void setup(void);
void loop(void);
uint64_t getUInt64fromHex(char const *str);
bool sendIRCode(IRsend *irsend, int const ir_type,
uint64_t const code, char const * code_str, uint16_t bits,
uint16_t repeat);
bool sendInt(const String topic, const int32_t num, const bool retain);
bool sendBool(const String topic, const bool on, const bool retain);
bool sendString(const String topic, const String str, const bool retain);
bool sendFloat(const String topic, const float_t temp, const bool retain);
stdAc::state_t updateClimate(stdAc::state_t current, const String str,
const String prefix, const String payload);
bool cmpClimate(const stdAc::state_t a, const stdAc::state_t b);
bool sendClimate(const stdAc::state_t prev, const stdAc::state_t next,
const String topic_prefix, const bool retain,
const bool forceMQTT, const bool forceIR,
const bool enableIR = true);
bool decodeCommonAc(const decode_results *decode);
#endif // EXAMPLES_IRMQTTSERVER_IRMQTTSERVER_H_
File diff suppressed because it is too large. Load diff
@@ -3,26 +3,57 @@ lib_extra_dirs = ../../
src_dir=.
[common]
build_flags = -DMQTT_MAX_PACKET_SIZE=512
build_flags = -DMQTT_MAX_PACKET_SIZE=768
lib_ldf_mode = chain+
lib_deps_builtin =
lib_deps_external =
PubSubClient
WifiManager@0.14
ArduinoJson@<6.0
[common_esp8266]
lib_deps_external =
${common.lib_deps_builtin}
${common.lib_deps_external}
WifiManager@>=0.14
[common_esp32]
lib_deps_external =
${common.lib_deps_builtin}
${common.lib_deps_external}
https://github.com/tzapu/WiFiManager.git#development
[env:nodemcuv2]
platform = espressif8266
framework = arduino
board = nodemcuv2
lib_ldf_mode = ${common.lib_ldf_mode}
build_flags = ${common.build_flags}
lib_deps =
${common.lib_deps_builtin}
${common.lib_deps_external}
${common_esp8266.lib_deps_external}
[env:d1_mini]
platform=espressif8266
framework=arduino
board=d1_mini
lib_ldf_mode = ${common.lib_ldf_mode}
build_flags = ${common.build_flags}
lib_deps =
${common.lib_deps_builtin}
${common.lib_deps_external}
${common_esp8266.lib_deps_external}
[env:d1_mini_no_mqtt]
platform=espressif8266
framework=arduino
board=d1_mini
lib_ldf_mode = ${common.lib_ldf_mode}
build_flags = ${common.build_flags} -DMQTT_ENABLE=false
lib_deps =
${common_esp8266.lib_deps_external}
[env:esp32dev]
platform = espressif32
framework = arduino
board = esp32dev
lib_ldf_mode = ${common.lib_ldf_mode}
build_flags = ${common.build_flags}
lib_deps =
${common_esp32.lib_deps_external}
@@ -1,9 +1,11 @@
/*
* IRremoteESP8266: IRServer - demonstrates sending IR codes controlled from a webserver
* Version 0.3 May, 2019
* Version 0.2 June, 2017
* Copyright 2015 Mark Szabo
* Copyright 2019 David Conran
*
* An IR LED circuit *MUST* be connected to the ESP8266 on a pin
* An IR LED circuit *MUST* be connected to the ESP on a pin
* as specified by kIrLed below.
*
* TL;DR: The IR LED needs to be driven by a transistor for a good result.
@@ -26,12 +28,17 @@
* * 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 <Arduino.h>
#endif
#if defined(ESP8266)
#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>
#include <ESP8266mDNS.h>
#endif // ESP8266
#if defined(ESP32)
#include <WiFi.h>
#include <WebServer.h>
#include <ESPmDNS.h>
#endif // ESP32
#include <IRremoteESP8266.h>
#include <IRsend.h>
#include <WiFiClient.h>
@@ -40,18 +47,27 @@ const char* kSsid = ".....";
const char* kPassword = ".....";
MDNSResponder mdns;
#if defined(ESP8266)
ESP8266WebServer server(80);
#undef HOSTNAME
#define HOSTNAME "esp8266"
#endif // ESP8266
#if defined(ESP32)
WebServer server(80);
#undef HOSTNAME
#define HOSTNAME "esp32"
#endif // ESP32
const uint16_t kIrLed = 4; // ESP8266 GPIO pin to use. Recommended: 4 (D2).
const uint16_t kIrLed = 4; // ESP GPIO pin to use. Recommended: 4 (D2).
IRsend irsend(kIrLed); // Set the GPIO to be used to sending the message.
void handleRoot() {
server.send(200, "text/html",
"<html>" \
"<head><title>ESP8266 Demo</title></head>" \
"<head><title>" HOSTNAME " Demo</title></head>" \
"<body>" \
"<h1>Hello from ESP8266, you can send NEC encoded IR" \
"<h1>Hello from " HOSTNAME ", you can send NEC encoded IR" \
"signals from here!</h1>" \
"<p><a href=\"ir?code=16769055\">Send 0xFFE01F</a></p>" \
"<p><a href=\"ir?code=16429347\">Send 0xFAB123</a></p>" \
@@ -104,7 +120,11 @@ void setup(void) {
Serial.print("IP address: ");
Serial.println(WiFi.localIP().toString());
if (mdns.begin("esp8266", WiFi.localIP())) {
#if defined(ESP8266)
if (mdns.begin(HOSTNAME, WiFi.localIP())) {
#else // ESP8266
if (mdns.begin(HOSTNAME)) {
#endif // ESP8266
Serial.println("MDNS responder started");
}
@@ -6,11 +6,23 @@ src_dir=.
build_flags =
lib_deps_builtin =
lib_deps_external =
lib_ldf_mode = chain+
[env:nodemcuv2]
platform = espressif8266
framework = arduino
board = nodemcuv2
lib_ldf_mode = ${common.lib_ldf_mode}
build_flags = ${common.build_flags}
lib_deps =
${common.lib_deps_builtin}
${common.lib_deps_external}
[env:esp32dev]
platform = espressif32
framework = arduino
board = esp32dev
lib_ldf_mode = ${common.lib_ldf_mode}
build_flags = ${common.build_flags}
lib_deps =
${common.lib_deps_builtin}
@@ -16,9 +16,7 @@
* Based on Ken Shirriff's IrsendDemo Version 0.1 July, 2009
*/
#ifndef UNIT_TEST
#include <Arduino.h>
#endif
#include <IRremoteESP8266.h>
#include <IRrecv.h>
#include <IRutils.h>
@@ -6,11 +6,23 @@ src_dir=.
build_flags =
lib_deps_builtin =
lib_deps_external =
lib_ldf_mode = chain+
[env:nodemcuv2]
platform = espressif8266
framework = arduino
board = nodemcuv2
lib_ldf_mode = ${common.lib_ldf_mode}
build_flags = ${common.build_flags}
lib_deps =
${common.lib_deps_builtin}
${common.lib_deps_external}
[env:esp32dev]
platform = espressif32
framework = arduino
board = esp32dev
lib_ldf_mode = ${common.lib_ldf_mode}
build_flags = ${common.build_flags}
lib_deps =
${common.lib_deps_builtin}
@@ -22,9 +22,7 @@
* LG added by Darryl Smith (based on the JVC protocol)
*/
#ifndef UNIT_TEST
#include <Arduino.h>
#endif
#include <IRremoteESP8266.h>
#include <IRrecv.h>
#include <IRutils.h>
@@ -6,11 +6,23 @@ src_dir=.
build_flags =
lib_deps_builtin =
lib_deps_external =
lib_ldf_mode = chain+
[env:nodemcuv2]
platform = espressif8266
framework = arduino
board = nodemcuv2
lib_ldf_mode = ${common.lib_ldf_mode}
build_flags = ${common.build_flags}
lib_deps =
${common.lib_deps_builtin}
${common.lib_deps_external}
[env:esp32dev]
platform = espressif32
framework = arduino
board = esp32dev
lib_ldf_mode = ${common.lib_ldf_mode}
build_flags = ${common.build_flags}
lib_deps =
${common.lib_deps_builtin}
@@ -19,27 +19,30 @@
* Based on Ken Shirriff's IrsendDemo Version 0.1 July, 2009,
*/
#ifndef UNIT_TEST
#include <Arduino.h>
#endif
#include <IRrecv.h>
#include <IRremoteESP8266.h>
#include <IRutils.h>
// The following are only needed for extended decoding of A/C Messages
#include <ir_Argo.h>
#include <ir_Coolix.h>
#include <ir_Daikin.h>
#include <ir_Fujitsu.h>
#include <ir_Goodweather.h>
#include <ir_Gree.h>
#include <ir_Haier.h>
#include <ir_Hitachi.h>
#include <ir_Kelvinator.h>
#include <ir_Midea.h>
#include <ir_Mitsubishi.h>
#include <ir_MitsubishiHeavy.h>
#include <ir_Panasonic.h>
#include <ir_Samsung.h>
#include <ir_Sharp.h>
#include <ir_Tcl.h>
#include <ir_Teco.h>
#include <ir_Toshiba.h>
#include <ir_Trotec.h>
#include <ir_Vestel.h>
#include <ir_Whirlpool.h>
@@ -117,8 +120,15 @@ IRrecv irrecv(kRecvPin, kCaptureBufferSize, kTimeout, 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) {
void dumpACInfo(const decode_results * const results) {
String description = "";
#if DECODE_ARGO
if (results->decode_type == ARGO) {
IRArgoAC ac(0);
ac.setRaw(results->state);
description = ac.toString();
}
#endif // DECODE_ARGO
#if DECODE_DAIKIN
if (results->decode_type == DAIKIN) {
IRDaikinESP ac(0);
@@ -133,6 +143,13 @@ void dumpACInfo(decode_results *results) {
description = ac.toString();
}
#endif // DECODE_DAIKIN2
#if DECODE_DAIKIN216
if (results->decode_type == DAIKIN216) {
IRDaikin216 ac(0);
ac.setRaw(results->state);
description = ac.toString();
}
#endif // DECODE_DAIKIN216
#if DECODE_FUJITSU_AC
if (results->decode_type == FUJITSU_AC) {
IRFujitsuAC ac(0);
@@ -154,6 +171,18 @@ void dumpACInfo(decode_results *results) {
description = ac.toString();
}
#endif // DECODE_MITSUBISHI_AC
#if DECODE_MITSUBISHIHEAVY
if (results->decode_type == MITSUBISHI_HEAVY_88) {
IRMitsubishiHeavy88Ac ac(0);
ac.setRaw(results->state);
description = ac.toString();
}
if (results->decode_type == MITSUBISHI_HEAVY_152) {
IRMitsubishiHeavy152Ac ac(0);
ac.setRaw(results->state);
description = ac.toString();
}
#endif // DECODE_MITSUBISHIHEAVY
#if DECODE_TOSHIBA_AC
if (results->decode_type == TOSHIBA_AC) {
IRToshibaAC ac(0);
@@ -161,6 +190,20 @@ void dumpACInfo(decode_results *results) {
description = ac.toString();
}
#endif // DECODE_TOSHIBA_AC
#if DECODE_TROTEC
if (results->decode_type == TROTEC) {
IRTrotecESP ac(0);
ac.setRaw(results->state);
description = ac.toString();
}
#endif // DECODE_TROTEC
#if DECODE_GOODWEATHER
if (results->decode_type == GOODWEATHER) {
IRGoodweatherAc ac(0);
ac.setRaw(results->value); // Goodweather uses value instead of state.
description = ac.toString();
}
#endif // DECODE_GOODWEATHER
#if DECODE_GREE
if (results->decode_type == GREE) {
IRGreeAC ac(0);
@@ -196,6 +239,13 @@ void dumpACInfo(decode_results *results) {
description = ac.toString();
}
#endif // DECODE_SAMSUNG_AC
#if DECODE_SHARP_AC
if (results->decode_type == SHARP_AC) {
IRSharpAc ac(0);
ac.setRaw(results->state);
description = ac.toString();
}
#endif // DECODE_SHARP_AC
#if DECODE_COOLIX
if (results->decode_type == COOLIX) {
IRCoolixAC ac(0);
@@ -250,9 +300,13 @@ void dumpACInfo(decode_results *results) {
if (description != "") Serial.println("Mesg Desc.: " + description);
}
// The section of code run only once at start-up.
// This section of code runs only once at start-up.
void setup() {
#if defined(ESP8266)
Serial.begin(kBaudRate, SERIAL_8N1, SERIAL_TX_ONLY);
#else // ESP8266
Serial.begin(kBaudRate, SERIAL_8N1);
#endif // ESP8266
while (!Serial) // Wait for the serial connection to be establised.
delay(50);
Serial.println();
@@ -6,11 +6,23 @@ src_dir=.
build_flags =
lib_deps_builtin =
lib_deps_external =
lib_ldf_mode = chain+
[env:nodemcuv2]
platform = espressif8266
framework = arduino
board = nodemcuv2
lib_ldf_mode = ${common.lib_ldf_mode}
build_flags = ${common.build_flags}
lib_deps =
${common.lib_deps_builtin}
${common.lib_deps_external}
[env:esp32dev]
platform = espressif32
framework = arduino
board = esp32dev
lib_ldf_mode = ${common.lib_ldf_mode}
build_flags = ${common.build_flags}
lib_deps =
${common.lib_deps_builtin}
@@ -28,9 +28,7 @@
* for your first time. e.g. ESP-12 etc.
*/
#ifndef UNIT_TEST
#include <Arduino.h>
#endif
#include <IRremoteESP8266.h>
#include <IRsend.h>
@@ -53,7 +51,11 @@ uint8_t samsungState[kSamsungAcStateLength] = {
void setup() {
irsend.begin();
#if ESP8266
Serial.begin(115200, SERIAL_8N1, SERIAL_TX_ONLY);
#else // ESP8266
Serial.begin(115200, SERIAL_8N1);
#endif // ESP8266
}
void loop() {
@@ -6,11 +6,23 @@ src_dir=.
build_flags =
lib_deps_builtin =
lib_deps_external =
lib_ldf_mode = chain+
[env:nodemcuv2]
platform = espressif8266
framework = arduino
board = nodemcuv2
lib_ldf_mode = ${common.lib_ldf_mode}
build_flags = ${common.build_flags}
lib_deps =
${common.lib_deps_builtin}
${common.lib_deps_external}
[env:esp32dev]
platform = espressif32
framework = arduino
board = esp32dev
lib_ldf_mode = ${common.lib_ldf_mode}
build_flags = ${common.build_flags}
lib_deps =
${common.lib_deps_builtin}
@@ -29,9 +29,7 @@
* for your first time. e.g. ESP-12 etc.
*/
#ifndef UNIT_TEST
#include <Arduino.h>
#endif
#include <IRremoteESP8266.h>
#include <IRsend.h>
@@ -6,11 +6,23 @@ src_dir=.
build_flags =
lib_deps_builtin =
lib_deps_external =
lib_ldf_mode = chain+
[env:nodemcuv2]
platform = espressif8266
framework = arduino
board = nodemcuv2
lib_ldf_mode = ${common.lib_ldf_mode}
build_flags = ${common.build_flags}
lib_deps =
${common.lib_deps_builtin}
${common.lib_deps_external}
[env:esp32dev]
platform = espressif32
framework = arduino
board = esp32dev
lib_ldf_mode = ${common.lib_ldf_mode}
build_flags = ${common.build_flags}
lib_deps =
${common.lib_deps_builtin}
@@ -28,9 +28,7 @@
* for your first time. e.g. ESP-12 etc.
*/
#ifndef UNIT_TEST
#include <Arduino.h>
#endif
#include <IRremoteESP8266.h>
#include <IRsend.h>
@@ -6,11 +6,23 @@ src_dir=.
build_flags =
lib_deps_builtin =
lib_deps_external =
lib_ldf_mode = chain+
[env:nodemcuv2]
platform = espressif8266
framework = arduino
board = nodemcuv2
lib_ldf_mode = ${common.lib_ldf_mode}
build_flags = ${common.build_flags}
lib_deps =
${common.lib_deps_builtin}
${common.lib_deps_external}
[env:esp32dev]
platform = espressif32
framework = arduino
board = esp32dev
lib_ldf_mode = ${common.lib_ldf_mode}
build_flags = ${common.build_flags}
lib_deps =
${common.lib_deps_builtin}
@@ -6,11 +6,23 @@ src_dir=.
build_flags =
lib_deps_builtin =
lib_deps_external =
lib_ldf_mode = chain+
[env:nodemcuv2]
platform = espressif8266
framework = arduino
board = nodemcuv2
lib_ldf_mode = ${common.lib_ldf_mode}
build_flags = ${common.build_flags}
lib_deps =
${common.lib_deps_builtin}
${common.lib_deps_external}
[env:esp32dev]
platform = espressif32
framework = arduino
board = esp32dev
lib_ldf_mode = ${common.lib_ldf_mode}
build_flags = ${common.build_flags}
lib_deps =
${common.lib_deps_builtin}
@@ -0,0 +1,143 @@
/*
* IRremoteESP8266: SmartIRRepeater.ino - Record and playback IR codes.
* Copyright 2019 David Conran (crankyoldgit)
*
* This program will try to capture incoming IR messages and tries to
* intelligently replay them back.
* It uses the advanced detection features of the library, and the custom
* sending routines. Thus it will try to use the correct frequencies,
* duty cycles, and repeats as it thinks is required.
* Anything it doesn't understand, it will try to replay back as best it can,
* but at 38kHz.
* Note:
* That might NOT be the frequency of the incoming message, so some not
* recogised messages that are replayed may not work. The frequency & duty
* cycle of unknown incoming messages is lost at the point of the Hardware IR
* demodulator. The ESP can't see it.
*
* W A R N I N G
* This code is just for educational/example use only. No help will be given
* to you to make it do something else, or to make it work with some
* weird device or circuit, or to make it more usable or practical.
* If it works for you. Great. If not, Congratulations on changing/fixing it.
*
* An IR detector/demodulator must be connected to the input, kRecvPin.
* An IR LED circuit must be connected to the output, kIrLedPin.
*
* Example circuit diagrams (both are needed):
* https://github.com/markszabo/IRremoteESP8266/wiki#ir-receiving
* 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
* * Some 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 ESP will interfere.
* * Pin 3/RX/RXD0: Any serial transmissions to the ESP 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.
*
* Changes:
* Version 1.0: June, 2019
* - Initial version.
*/
#include <Arduino.h>
#include <IRsend.h>
#include <IRrecv.h>
#include <IRremoteESP8266.h>
#include <IRutils.h>
// ==================== start of TUNEABLE PARAMETERS ====================
// The GPIO an IR detector/demodulator is connected to. Recommended: 14 (D5)
const uint16_t kRecvPin = 14;
// GPIO to use to control the IR LED circuit. Recommended: 4 (D2).
const uint16_t kIrLedPin = 4;
// The Serial connection baud rate.
// NOTE: Make sure you set your Serial Monitor to the same speed.
const uint32_t kBaudRate = 115200;
// As this program is a special purpose capture/resender, let's use a larger
// than expected buffer so we can handle very large IR messages.
const uint16_t kCaptureBufferSize = 1024; // 1024 == ~511 bits
// kTimeout is the Nr. of milli-Seconds of no-more-data before we consider a
// message ended.
const uint8_t kTimeout = 50; // Milli-Seconds
// kFrequency is the modulation frequency all UNKNOWN messages will be sent at.
const uint16_t kFrequency = 38000; // in Hz. e.g. 38kHz.
// ==================== end of TUNEABLE PARAMETERS ====================
// The IR transmitter.
IRsend irsend(kIrLedPin);
// The IR receiver.
IRrecv irrecv(kRecvPin, kCaptureBufferSize, kTimeout, false);
// Somewhere to store the captured message.
decode_results results;
// This section of code runs only once at start-up.
void setup() {
irrecv.enableIRIn(); // Start up the IR receiver.
irsend.begin(); // Start up the IR sender.
Serial.begin(kBaudRate, SERIAL_8N1);
while (!Serial) // Wait for the serial connection to be establised.
delay(50);
Serial.println();
Serial.print("SmartIRRepeater is now running and waiting for IR input "
"on Pin ");
Serial.println(kRecvPin);
Serial.print("and will retransmit it on Pin ");
Serial.println(kIrLedPin);
}
// The repeating section of the code
void loop() {
// Check if an IR message has been received.
if (irrecv.decode(&results)) { // We have captured something.
// The capture has stopped at this point.
decode_type_t protocol = results.decode_type;
uint16_t size = results.bits;
bool success = true;
// Is it a protocol we don't understand?
if (protocol == decode_type_t::UNKNOWN) { // Yes.
// Convert the results into an array suitable for sendRaw().
// resultToRawArray() allocates the memory we need for the array.
uint16_t *raw_array = resultToRawArray(&results);
// Find out how many elements are in the array.
size = getCorrectedRawLength(&results);
// Send it out via the IR LED circuit.
irsend.sendRaw(raw_array, size, kFrequency);
// Deallocate the memory allocated by resultToRawArray().
delete [] raw_array;
} else if (hasACState(protocol)) { // Does the message require a state[]?
// It does, so send with bytes instead.
success = irsend.send(protocol, results.state, size / 8);
} else { // Anything else must be a simple message protocol. ie. <= 64 bits
success = irsend.send(protocol, results.value, size);
}
// Resume capturing IR messages. It was not restarted until after we sent
// the message so we didn't capture our own message.
irrecv.resume();
// Display a crude timestamp & notification.
uint32_t now = millis();
Serial.printf(
"%06u.%03u: A %d-bit %s message was %ssuccessfully retransmitted.\n",
now / 1000, now % 1000, size, typeToString(protocol).c_str(),
success ? "" : "un");
}
yield(); // Or delay(milliseconds); This ensures the ESP doesn't WDT reset.
}
@@ -0,0 +1,29 @@
[platformio]
lib_extra_dirs = ../../
src_dir=.
[common]
build_flags =
lib_deps_builtin =
lib_deps_external =
lib_ldf_mode = chain+
[env:nodemcuv2]
platform = espressif8266
framework = arduino
board = nodemcuv2
lib_ldf_mode = ${common.lib_ldf_mode}
build_flags = ${common.build_flags}
lib_deps =
${common.lib_deps_builtin}
${common.lib_deps_external}
[env:esp32dev]
platform = espressif32
framework = arduino
board = esp32dev
lib_ldf_mode = ${common.lib_ldf_mode}
build_flags = ${common.build_flags}
lib_deps =
${common.lib_deps_builtin}
${common.lib_deps_external}
@@ -23,9 +23,7 @@
* for your first time. e.g. ESP-12 etc.
*/
#ifndef UNIT_TEST
#include <Arduino.h>
#endif
#include <IRremoteESP8266.h>
#include <IRsend.h>
#include <ir_Argo.h>
@@ -44,7 +42,7 @@ void loop() {
// Set up what we want to send. See ir_Argo.cpp for all the options.
ac.setPower(true);
ac.setFan(kArgoFan1);
ac.setCoolMode(kArgoCoolAuto);
ac.setMode(kArgoAuto);
ac.setTemp(25);
#if SEND_ARGO
@@ -6,11 +6,23 @@ src_dir=.
build_flags =
lib_deps_builtin =
lib_deps_external =
lib_ldf_mode = chain+
[env:nodemcuv2]
platform = espressif8266
framework = arduino
board = nodemcuv2
lib_ldf_mode = ${common.lib_ldf_mode}
build_flags = ${common.build_flags}
lib_deps =
${common.lib_deps_builtin}
${common.lib_deps_external}
[env:esp32dev]
platform = espressif32
framework = arduino
board = esp32dev
lib_ldf_mode = ${common.lib_ldf_mode}
build_flags = ${common.build_flags}
lib_deps =
${common.lib_deps_builtin}
@@ -24,9 +24,7 @@
* for your first time. e.g. ESP-12 etc.
*/
#ifndef UNIT_TEST
#include <Arduino.h>
#endif
#include <IRremoteESP8266.h>
#include <IRsend.h>
#include <ir_Daikin.h>
@@ -6,11 +6,23 @@ src_dir=.
build_flags =
lib_deps_builtin =
lib_deps_external =
lib_ldf_mode = chain+
[env:nodemcuv2]
platform = espressif8266
framework = arduino
board = nodemcuv2
lib_ldf_mode = ${common.lib_ldf_mode}
build_flags = ${common.build_flags}
lib_deps =
${common.lib_deps_builtin}
${common.lib_deps_external}
[env:esp32dev]
platform = espressif32
framework = arduino
board = esp32dev
lib_ldf_mode = ${common.lib_ldf_mode}
build_flags = ${common.build_flags}
lib_deps =
${common.lib_deps_builtin}
@@ -1,7 +1,5 @@
// Copyright 2017 Jonny Graham, 2018 David Conran
#ifndef UNIT_TEST
#include <Arduino.h>
#endif
#include <IRremoteESP8266.h>
#include <IRsend.h>
#include <ir_Fujitsu.h>
@@ -30,11 +28,13 @@ void setup() {
Serial.println("Default state of the remote.");
printState();
Serial.println("Setting desired state for A/C.");
ac.setCmd(kFujitsuAcCmdTurnOn);
ac.setSwing(kFujitsuAcSwingBoth);
// See `fujitsu_ac_remote_model_t` in `ir_Fujitsu.h` for a list of models.
ac.setModel(ARRAH2E);
ac.setSwing(kFujitsuAcSwingOff);
ac.setMode(kFujitsuAcModeCool);
ac.setFanSpeed(kFujitsuAcFanHigh);
ac.setTemp(24); // 24C
ac.setCmd(kFujitsuAcCmdTurnOn);
}
void loop() {
@@ -6,11 +6,23 @@ src_dir=.
build_flags =
lib_deps_builtin =
lib_deps_external =
lib_ldf_mode = chain+
[env:nodemcuv2]
platform = espressif8266
framework = arduino
board = nodemcuv2
lib_ldf_mode = ${common.lib_ldf_mode}
build_flags = ${common.build_flags}
lib_deps =
${common.lib_deps_builtin}
${common.lib_deps_external}
[env:esp32dev]
platform = espressif32
framework = arduino
board = esp32dev
lib_ldf_mode = ${common.lib_ldf_mode}
build_flags = ${common.build_flags}
lib_deps =
${common.lib_deps_builtin}
@@ -23,9 +23,7 @@
* * 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 <Arduino.h>
#endif
#include <IRremoteESP8266.h>
#include <IRsend.h>
#include <ir_Kelvinator.h>
@@ -6,11 +6,23 @@ src_dir=.
build_flags =
lib_deps_builtin =
lib_deps_external =
lib_ldf_mode = chain+
[env:nodemcuv2]
platform = espressif8266
framework = arduino
board = nodemcuv2
lib_ldf_mode = ${common.lib_ldf_mode}
build_flags = ${common.build_flags}
lib_deps =
${common.lib_deps_builtin}
${common.lib_deps_external}
[env:esp32dev]
platform = espressif32
framework = arduino
board = esp32dev
lib_ldf_mode = ${common.lib_ldf_mode}
build_flags = ${common.build_flags}
lib_deps =
${common.lib_deps_builtin}
@@ -23,9 +23,7 @@
* * 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 <Arduino.h>
#endif
#include <IRremoteESP8266.h>
#include <IRsend.h>
#include <ir_Mitsubishi.h>
@@ -6,11 +6,23 @@ src_dir=.
build_flags =
lib_deps_builtin =
lib_deps_external =
lib_ldf_mode = chain+
[env:nodemcuv2]
platform = espressif8266
framework = arduino
board = nodemcuv2
lib_ldf_mode = ${common.lib_ldf_mode}
build_flags = ${common.build_flags}
lib_deps =
${common.lib_deps_builtin}
${common.lib_deps_external}
[env:esp32dev]
platform = espressif32
framework = arduino
board = esp32dev
lib_ldf_mode = ${common.lib_ldf_mode}
build_flags = ${common.build_flags}
lib_deps =
${common.lib_deps_builtin}
@@ -0,0 +1,70 @@
/* Copyright 2019 David Conran
*
* An IR LED circuit *MUST* be connected to the ESP8266 on a pin
* as specified by kIrLed below.
*
* TL;DR: The IR LED needs to be driven by a transistor for a good result.
*
* Suggested circuit:
* https://github.com/markszabo/IRremoteESP8266/wiki#ir-sending
*
* Common mistakes & tips:
* * Don't just connect the IR LED directly to the pin, it won't
* have enough current to drive the IR LED effectively.
* * Make sure you have the IR LED polarity correct.
* See: https://learn.sparkfun.com/tutorials/polarity/diode-and-led-polarity
* * Typical digital camera/phones can be used to see if the IR LED is flashed.
* Replace the IR LED with a normal LED if you don't have a digital camera
* when debugging.
* * Avoid using the following pins unless you really know what you are doing:
* * Pin 0/D3: Can interfere with the boot/program mode & support circuits.
* * Pin 1/TX/TXD0: Any serial transmissions from the ESP8266 will interfere.
* * Pin 3/RX/RXD0: Any serial transmissions to the ESP8266 will interfere.
* * ESP-01 modules are tricky. We suggest you use a module with more GPIOs
* for your first time. e.g. ESP-12 etc.
*/
#include <Arduino.h>
#include <IRremoteESP8266.h>
#include <IRsend.h>
#include <ir_MitsubishiHeavy.h>
const uint16_t kIrLed = 4; // ESP8266 GPIO pin to use. Recommended: 4 (D2).
IRMitsubishiHeavy152Ac ac(kIrLed); // Set the GPIO used for sending messages.
void printState() {
// Display the settings.
Serial.println("Mitsubishi Heavy A/C remote is in the following state:");
Serial.printf(" %s\n", ac.toString().c_str());
// Display the encoded IR sequence.
unsigned char* ir_code = ac.getRaw();
Serial.print("IR Code: 0x");
for (uint8_t i = 0; i < kMitsubishiHeavy152StateLength; i++)
Serial.printf("%02X", ir_code[i]);
Serial.println();
}
void setup() {
ac.begin();
Serial.begin(115200);
delay(200);
// Set up what we want to send. See ir_MitsubishiHeavy.(cpp|h) for all the
// options.
Serial.println("Default state of the remote.");
printState();
Serial.println("Setting desired state for A/C.");
ac.setPower(true); // Turn it on.
ac.setFan(kMitsubishiHeavy152FanMed); // Medium Fan
ac.setMode(kMitsubishiHeavyCool); // Cool mode
ac.setTemp(26); // Celsius
ac.setSwingVertical(kMitsubishiHeavy152SwingVAuto); // Swing vertically
ac.setSwingHorizontal(kMitsubishiHeavy152SwingHMiddle); // Swing Horizontally
}
void loop() {
// Now send the IR signal.
Serial.println("Sending IR command to A/C ...");
ac.send();
printState();
delay(5000);
}
@@ -0,0 +1,29 @@
[platformio]
lib_extra_dirs = ../../
src_dir=.
[common]
build_flags =
lib_deps_builtin =
lib_deps_external =
lib_ldf_mode = chain+
[env:nodemcuv2]
platform = espressif8266
framework = arduino
board = nodemcuv2
lib_ldf_mode = ${common.lib_ldf_mode}
build_flags = ${common.build_flags}
lib_deps =
${common.lib_deps_builtin}
${common.lib_deps_external}
[env:esp32dev]
platform = espressif32
framework = arduino
board = esp32dev
lib_ldf_mode = ${common.lib_ldf_mode}
build_flags = ${common.build_flags}
lib_deps =
${common.lib_deps_builtin}
${common.lib_deps_external}
@@ -0,0 +1,72 @@
/* Copyright 2017, 2018 David Conran
*
* An IR LED circuit *MUST* be connected to the ESP8266 on a pin
* as specified by kIrLed below.
*
* TL;DR: The IR LED needs to be driven by a transistor for a good result.
*
* Suggested circuit:
* https://github.com/markszabo/IRremoteESP8266/wiki#ir-sending
*
* Common mistakes & tips:
* * Don't just connect the IR LED directly to the pin, it won't
* have enough current to drive the IR LED effectively.
* * Make sure you have the IR LED polarity correct.
* See: https://learn.sparkfun.com/tutorials/polarity/diode-and-led-polarity
* * Typical digital camera/phones can be used to see if the IR LED is flashed.
* Replace the IR LED with a normal LED if you don't have a digital camera
* when debugging.
* * Avoid using the following pins unless you really know what you are doing:
* * Pin 0/D3: Can interfere with the boot/program mode & support circuits.
* * Pin 1/TX/TXD0: Any serial transmissions from the ESP8266 will interfere.
* * Pin 3/RX/RXD0: Any serial transmissions to the ESP8266 will interfere.
* * ESP-01 modules are tricky. We suggest you use a module with more GPIOs
* for your first time. e.g. ESP-12 etc.
*/
#include <Arduino.h>
#include <IRremoteESP8266.h>
#include <IRsend.h>
#include <ir_Panasonic.h>
const uint16_t kIrLed = 4; // ESP8266 GPIO pin to use. Recommended: 4 (D2).
IRPanasonicAc ac(kIrLed); // Set the GPIO used for sending messages.
void printState() {
// Display the settings.
Serial.println("Panasonic A/C remote is in the following state:");
Serial.printf(" %s\n", ac.toString().c_str());
// Display the encoded IR sequence.
unsigned char* ir_code = ac.getRaw();
Serial.print("IR Code: 0x");
for (uint8_t i = 0; i < kPanasonicAcStateLength; i++)
Serial.printf("%02X", ir_code[i]);
Serial.println();
}
void setup() {
ac.begin();
Serial.begin(115200);
delay(200);
// Set up what we want to send. See ir_Panasonic.cpp for all the options.
Serial.println("Default state of the remote.");
printState();
Serial.println("Setting desired state for A/C.");
ac.setModel(kPanasonicRkr);
ac.on();
ac.setFan(kPanasonicAcFanAuto);
ac.setMode(kPanasonicAcCool);
ac.setTemp(26);
ac.setSwingVertical(kPanasonicAcSwingVAuto);
ac.setSwingHorizontal(kPanasonicAcSwingHAuto);
}
void loop() {
// Now send the IR signal.
#if SEND_PANASONIC_AC
Serial.println("Sending IR command to A/C ...");
ac.send();
#endif // SEND_PANASONIC_AC
printState();
delay(5000);
}
@@ -0,0 +1,29 @@
[platformio]
lib_extra_dirs = ../../
src_dir=.
[common]
build_flags =
lib_deps_builtin =
lib_deps_external =
lib_ldf_mode = chain+
[env:nodemcuv2]
platform = espressif8266
framework = arduino
board = nodemcuv2
lib_ldf_mode = ${common.lib_ldf_mode}
build_flags = ${common.build_flags}
lib_deps =
${common.lib_deps_builtin}
${common.lib_deps_external}
[env:esp32dev]
platform = espressif32
framework = arduino
board = esp32dev
lib_ldf_mode = ${common.lib_ldf_mode}
build_flags = ${common.build_flags}
lib_deps =
${common.lib_deps_builtin}
${common.lib_deps_external}
@@ -23,9 +23,7 @@
* * 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 <Arduino.h>
#endif
#include <IRremoteESP8266.h>
#include <IRsend.h>
#include <ir_Toshiba.h>
@@ -6,11 +6,23 @@ src_dir=.
build_flags =
lib_deps_builtin =
lib_deps_external =
lib_ldf_mode = chain+
[env:nodemcuv2]
platform = espressif8266
framework = arduino
board = nodemcuv2
lib_ldf_mode = ${common.lib_ldf_mode}
build_flags = ${common.build_flags}
lib_deps =
${common.lib_deps_builtin}
${common.lib_deps_external}
[env:esp32dev]
platform = espressif32
framework = arduino
board = esp32dev
lib_ldf_mode = ${common.lib_ldf_mode}
build_flags = ${common.build_flags}
lib_deps =
${common.lib_deps_builtin}
@@ -23,9 +23,7 @@
* for your first time. e.g. ESP-12 etc.
*/
#ifndef UNIT_TEST
#include <Arduino.h>
#endif
#include <IRremoteESP8266.h>
#include <IRsend.h>
#include <ir_Trotec.h>
@@ -6,11 +6,23 @@ src_dir=.
build_flags =
lib_deps_builtin =
lib_deps_external =
lib_ldf_mode = chain+
[env:nodemcuv2]
platform = espressif8266
framework = arduino
board = nodemcuv2
lib_ldf_mode = ${common.lib_ldf_mode}
build_flags = ${common.build_flags}
lib_deps =
${common.lib_deps_builtin}
${common.lib_deps_external}
[env:esp32dev]
platform = espressif32
framework = arduino
board = esp32dev
lib_ldf_mode = ${common.lib_ldf_mode}
build_flags = ${common.build_flags}
lib_deps =
${common.lib_deps_builtin}
File diff suppressed because it is too large. Load diff
+3 -3
View File
@@ -1,7 +1,7 @@
{
"name": "IRremoteESP8266",
"version": "2.5.6",
"keywords": "infrared, ir, remote, esp8266",
"version": "2.6.2",
"keywords": "infrared, ir, remote, esp8266, esp32",
"description": "Send and receive infrared signals with multiple protocols (ESP8266)",
"repository":
{
@@ -40,5 +40,5 @@
}
],
"frameworks": "arduino",
"platforms": "espressif8266"
"platforms": ["espressif8266", "espressif32"]
}
+4 -4
View File
@@ -1,9 +1,9 @@
name=IRremoteESP8266
version=2.5.6
version=2.6.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)
paragraph=This library enables you to send and receive infra-red signals on an ESP8266.
sentence=Send and receive infrared signals with multiple protocols (ESP8266/ESP32)
paragraph=This library enables you to send and receive infra-red signals on an ESP8266 or an ESP32.
category=Device Control
url=https://github.com/markszabo/IRremoteESP8266
architectures=esp8266
architectures=esp8266,esp32
+13
View File
@@ -6,11 +6,13 @@ src_dir = examples/IRrecvDumpV2
build_flags =
lib_deps_builtin =
lib_deps_external =
lib_ldf_mode = chain+
[env:nodemcuv2]
platform = espressif8266
framework = arduino
board = nodemcuv2
lib_ldf_mode = ${common.lib_ldf_mode}
build_flags = ${common.build_flags}
lib_deps =
${common.lib_deps_builtin}
@@ -20,6 +22,17 @@ lib_deps =
platform = espressif8266
framework = arduino
board = d1_mini
lib_ldf_mode = ${common.lib_ldf_mode}
build_flags = ${common.build_flags}
lib_deps =
${common.lib_deps_builtin}
${common.lib_deps_external}
[env:esp32dev]
platform = espressif32
framework = arduino
board = esp32dev
lib_ldf_mode = ${common.lib_ldf_mode}
build_flags = ${common.build_flags}
lib_deps =
${common.lib_deps_builtin}
File diff suppressed because it is too large. Load diff
+244
View File
@@ -0,0 +1,244 @@
#ifndef IRAC_H_
#define IRAC_H_
// Copyright 2019 David Conran
#ifndef UNIT_TEST
#include <Arduino.h>
#endif
#include "IRremoteESP8266.h"
#include "ir_Argo.h"
#include "ir_Coolix.h"
#include "ir_Daikin.h"
#include "ir_Fujitsu.h"
#include "ir_Goodweather.h"
#include "ir_Gree.h"
#include "ir_Haier.h"
#include "ir_Hitachi.h"
#include "ir_Kelvinator.h"
#include "ir_Midea.h"
#include "ir_Mitsubishi.h"
#include "ir_MitsubishiHeavy.h"
#include "ir_Panasonic.h"
#include "ir_Samsung.h"
#include "ir_Sharp.h"
#include "ir_Tcl.h"
#include "ir_Teco.h"
#include "ir_Toshiba.h"
#include "ir_Trotec.h"
#include "ir_Vestel.h"
#include "ir_Whirlpool.h"
class IRac {
public:
explicit IRac(uint8_t pin);
static bool isProtocolSupported(const decode_type_t protocol);
bool sendAc(const decode_type_t vendor, const int16_t model,
const bool power, const stdAc::opmode_t mode, const float degrees,
const bool celsius, const stdAc::fanspeed_t fan,
const stdAc::swingv_t swingv, const stdAc::swingh_t swingh,
const bool quiet, const bool turbo, const bool econo,
const bool light, const bool filter, const bool clean,
const bool beep, const int16_t sleep = -1,
const int16_t clock = -1);
static bool strToBool(const char *str, const bool def = false);
static int16_t strToModel(const char *str, const int16_t def = -1);
static stdAc::opmode_t strToOpmode(
const char *str, const stdAc::opmode_t def = stdAc::opmode_t::kAuto);
static stdAc::fanspeed_t strToFanspeed(
const char *str,
const stdAc::fanspeed_t def = stdAc::fanspeed_t::kAuto);
static stdAc::swingv_t strToSwingV(
const char *str, const stdAc::swingv_t def = stdAc::swingv_t::kOff);
static stdAc::swingh_t strToSwingH(
const char *str, const stdAc::swingh_t def = stdAc::swingh_t::kOff);
static String boolToString(const bool value);
static String opmodeToString(const stdAc::opmode_t mode);
static String fanspeedToString(const stdAc::fanspeed_t speed);
static String swingvToString(const stdAc::swingv_t swingv);
static String swinghToString(const stdAc::swingh_t swingh);
#ifndef UNIT_TEST
private:
#endif
uint8_t _pin;
#if SEND_ARGO
void argo(IRArgoAC *ac,
const bool on, const stdAc::opmode_t mode, const float degrees,
const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv,
const bool turbo, const int16_t sleep = -1);
#endif // SEND_ARGO
#if SEND_COOLIX
void coolix(IRCoolixAC *ac,
const bool on, const stdAc::opmode_t mode, const float degrees,
const stdAc::fanspeed_t fan,
const stdAc::swingv_t swingv, const stdAc::swingh_t swingh,
const bool turbo, const bool light, const bool clean,
const int16_t sleep = -1);
#endif // SEND_COOLIX
#if SEND_DAIKIN
void daikin(IRDaikinESP *ac,
const bool on, const stdAc::opmode_t mode, const float degrees,
const stdAc::fanspeed_t fan,
const stdAc::swingv_t swingv, const stdAc::swingh_t swingh,
const bool quiet, const bool turbo, const bool econo,
const bool clean);
#endif // SEND_DAIKIN
#if SEND_DAIKIN2
void daikin2(IRDaikin2 *ac,
const bool on, const stdAc::opmode_t mode,
const float degrees, const stdAc::fanspeed_t fan,
const stdAc::swingv_t swingv, const stdAc::swingh_t swingh,
const bool quiet, const bool turbo, const bool light,
const bool econo, const bool filter, const bool clean,
const bool beep, const int16_t sleep = -1,
const int16_t clock = -1);
#endif // SEND_DAIKIN2
#if SEND_DAIKIN216
void daikin216(IRDaikin216 *ac,
const bool on, const stdAc::opmode_t mode,
const float degrees, const stdAc::fanspeed_t fan,
const stdAc::swingv_t swingv, const stdAc::swingh_t swingh,
const bool quiet, const bool turbo);
#endif // SEND_DAIKIN216
#if SEND_FUJITSU_AC
void fujitsu(IRFujitsuAC *ac, const fujitsu_ac_remote_model_t model,
const bool on, const stdAc::opmode_t mode, const float degrees,
const stdAc::fanspeed_t fan,
const stdAc::swingv_t swingv, const stdAc::swingh_t swingh,
const bool quiet, const bool turbo, const bool econo);
#endif // SEND_FUJITSU_AC
#if SEND_GOODWEATHER
void goodweather(IRGoodweatherAc *ac,
const bool on, const stdAc::opmode_t mode,
const float degrees,
const stdAc::fanspeed_t fan,
const stdAc::swingv_t swingv,
const bool turbo, const bool light,
const int16_t sleep = -1);
#endif // SEND_GOODWEATHER
#if SEND_GREE
void gree(IRGreeAC *ac,
const bool on, const stdAc::opmode_t mode, const float degrees,
const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv,
const bool turbo, const bool light, const bool clean,
const int16_t sleep = -1);
#endif // SEND_GREE
#if SEND_HAIER_AC
void haier(IRHaierAC *ac,
const bool on, const stdAc::opmode_t mode, const float degrees,
const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv,
const bool filter, const int16_t sleep = -1,
const int16_t clock = -1);
#endif // SEND_HAIER_AC
#if SEND_HAIER_AC_YRW02
void haierYrwo2(IRHaierACYRW02 *ac,
const bool on, const stdAc::opmode_t mode,
const float degrees, const stdAc::fanspeed_t fan,
const stdAc::swingv_t swingv,
const bool turbo, const bool filter,
const int16_t sleep = -1);
#endif // SEND_HAIER_AC_YRW02
#if SEND_HITACHI_AC
void hitachi(IRHitachiAc *ac,
const bool on, const stdAc::opmode_t mode,
const float degrees, const stdAc::fanspeed_t fan,
const stdAc::swingv_t swingv, const stdAc::swingh_t swingh);
#endif // SEND_HITACHI_AC
#if SEND_KELVINATOR
void kelvinator(IRKelvinatorAC *ac,
const bool on, const stdAc::opmode_t mode,
const float degrees, const stdAc::fanspeed_t fan,
const stdAc::swingv_t swingv, const stdAc::swingh_t swingh,
const bool quiet, const bool turbo, const bool light,
const bool filter, const bool clean);
#endif // SEND_KELVINATOR
#if SEND_MIDEA
void midea(IRMideaAC *ac,
const bool on, const stdAc::opmode_t mode, const float degrees,
const stdAc::fanspeed_t fan, const int16_t sleep = -1);
#endif // SEND_MIDEA
#if SEND_MITSUBISHI_AC
void mitsubishi(IRMitsubishiAC *ac,
const bool on, const stdAc::opmode_t mode,
const float degrees,
const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv,
const bool quiet, const int16_t clock = -1);
#endif // SEND_MITSUBISHI_AC
#if SEND_MITSUBISHIHEAVY
void mitsubishiHeavy88(IRMitsubishiHeavy88Ac *ac,
const bool on, const stdAc::opmode_t mode,
const float degrees, const stdAc::fanspeed_t fan,
const stdAc::swingv_t swingv,
const stdAc::swingh_t swingh,
const bool turbo, const bool econo, const bool clean);
void mitsubishiHeavy152(IRMitsubishiHeavy152Ac *ac,
const bool on, const stdAc::opmode_t mode,
const float degrees, const stdAc::fanspeed_t fan,
const stdAc::swingv_t swingv,
const stdAc::swingh_t swingh,
const bool quiet, const bool turbo, const bool econo,
const bool filter, const bool clean,
const int16_t sleep = -1);
#endif // SEND_MITSUBISHIHEAVY
#if SEND_PANASONIC_AC
void panasonic(IRPanasonicAc *ac, const panasonic_ac_remote_model_t model,
const bool on, const stdAc::opmode_t mode, const float degrees,
const stdAc::fanspeed_t fan,
const stdAc::swingv_t swingv, const stdAc::swingh_t swingh,
const bool quiet, const bool turbo, const int16_t clock = -1);
#endif // SEND_PANASONIC_AC
#if SEND_SAMSUNG_AC
void samsung(IRSamsungAc *ac,
const bool on, const stdAc::opmode_t mode, const float degrees,
const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv,
const bool quiet, const bool turbo, const bool clean,
const bool beep, const bool dopower = true);
#endif // SEND_SAMSUNG_AC
#if SEND_SHARP_AC
void sharp(IRSharpAc *ac,
const bool on, const stdAc::opmode_t mode,
const float degrees, const stdAc::fanspeed_t fan);
#endif // SEND_SHARP_AC
#if SEND_TCL112AC
void tcl112(IRTcl112Ac *ac,
const bool on, const stdAc::opmode_t mode, const float degrees,
const stdAc::fanspeed_t fan,
const stdAc::swingv_t swingv, const stdAc::swingh_t swingh,
const bool turbo, const bool light, const bool econo,
const bool filter);
#endif // SEND_TCL112AC
#if SEND_TECO
void teco(IRTecoAc *ac,
const bool on, const stdAc::opmode_t mode, const float degrees,
const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv,
const int16_t sleep = -1);
#endif // SEND_TECO
#if SEND_TOSHIBA_AC
void toshiba(IRToshibaAC *ac,
const bool on, const stdAc::opmode_t mode, const float degrees,
const stdAc::fanspeed_t fan);
#endif // SEND_TOSHIBA_AC
#if SEND_TROTEC
void trotec(IRTrotecESP *ac,
const bool on, const stdAc::opmode_t mode, const float degrees,
const stdAc::fanspeed_t fan, const int16_t sleep = -1);
#endif // SEND_TROTEC
#if SEND_VESTEL_AC
void vestel(IRVestelAc *ac,
const bool on, const stdAc::opmode_t mode, const float degrees,
const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv,
const bool turbo, const bool filter,
const int16_t sleep = -1, const int16_t clock = -1,
const bool sendNormal = true);
#endif // SEND_VESTEL_AC
#if SEND_WHIRLPOOL_AC
void whirlpool(IRWhirlpoolAc *ac, const whirlpool_ac_remote_model_t model,
const bool on, const stdAc::opmode_t mode, const float degrees,
const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv,
const bool turbo, const bool light,
const int16_t sleep = -1, const int16_t clock = -1);
#endif // SEND_WHIRLPOOL_AC
}; // IRac class
#endif // IRAC_H_
+138 -24
View File
@@ -1,15 +1,17 @@
// Copyright 2009 Ken Shirriff
// Copyright 2015 Mark Szabo
// Copyright 2015 Sebastien Warin
// Copyright 2017 David Conran
// Copyright 2017, 2019 David Conran
#include "IRrecv.h"
#include <stddef.h>
#ifndef UNIT_TEST
#if defined(ESP8266)
extern "C" {
#include <gpio.h>
#include <user_interface.h>
}
#endif // ESP8266
#include <Arduino.h>
#endif
#include <algorithm>
@@ -20,6 +22,20 @@ extern "C" {
#undef ICACHE_RAM_ATTR
#define ICACHE_RAM_ATTR
#endif
#ifndef USE_IRAM_ATTR
#if defined(ESP8266)
#define USE_IRAM_ATTR ICACHE_RAM_ATTR
#endif // ESP8266
#if defined(ESP32)
#define USE_IRAM_ATTR IRAM_ATTR
#endif // ESP32
#endif // USE_IRAM_ATTR
#define ONCE 0
// Updated by David Conran (https://github.com/crankyoldgit) for receiving IR
// code on ESP32
// Updated by Sebastien Warin (http://sebastien.warin.fr) for receiving IR code
// on ESP8266
// Updated by markszabo (https://github.com/markszabo/IRremoteESP8266) for
@@ -27,25 +43,47 @@ extern "C" {
// Globals
#ifndef UNIT_TEST
#if defined(ESP8266)
static ETSTimer timer;
#endif
#endif // ESP8266
#if defined(ESP32)
static hw_timer_t * timer = NULL;
#endif // ESP32
#endif // UNIT_TEST
#if defined(ESP32)
portMUX_TYPE irremote_mux = portMUX_INITIALIZER_UNLOCKED;
#endif // ESP32
volatile irparams_t irparams;
irparams_t *irparams_save; // A copy of the interrupt state while decoding.
#ifndef UNIT_TEST
static void ICACHE_RAM_ATTR read_timeout(void *arg __attribute__((unused))) {
#if defined(ESP8266)
static void USE_IRAM_ATTR read_timeout(void *arg __attribute__((unused))) {
os_intr_lock();
#endif // ESP8266
#if defined(ESP32)
static void USE_IRAM_ATTR read_timeout(void) {
portENTER_CRITICAL(&irremote_mux);
#endif // ESP32
if (irparams.rawlen) irparams.rcvstate = kStopState;
#if defined(ESP8266)
os_intr_unlock();
#endif // ESP8266
#if defined(ESP32)
portEXIT_CRITICAL(&irremote_mux);
#endif // ESP32
}
static void ICACHE_RAM_ATTR gpio_intr() {
uint32_t now = system_get_time();
uint32_t gpio_status = GPIO_REG_READ(GPIO_STATUS_ADDRESS);
static void USE_IRAM_ATTR gpio_intr() {
uint32_t now = micros();
static uint32_t start = 0;
#if defined(ESP8266)
uint32_t gpio_status = GPIO_REG_READ(GPIO_STATUS_ADDRESS);
os_timer_disarm(&timer);
GPIO_REG_WRITE(GPIO_STATUS_W1TC_ADDRESS, gpio_status);
#endif // ESP8266
// Grab a local copy of rawlen to reduce instructions used in IRAM.
// This is an ugly premature optimisation code-wise, but we do everything we
@@ -74,8 +112,14 @@ static void ICACHE_RAM_ATTR gpio_intr() {
irparams.rawlen++;
start = now;
#define ONCE 0
#if defined(ESP8266)
os_timer_arm(&timer, irparams.timeout, ONCE);
#endif // ESP8266
#if defined(ESP32)
timerWrite(timer, 0); // Reset the timeout.
timerAlarmEnable(timer);
#endif // ESP32
}
#endif // UNIT_TEST
@@ -87,11 +131,21 @@ static void ICACHE_RAM_ATTR gpio_intr() {
// bufsize: Nr. of entries to have in the capture buffer. (Default: kRawBuf)
// timeout: Nr. of milli-Seconds of no signal before we stop capturing data.
// (Default: kTimeoutMs)
// save_buffer: Use a second (save) buffer to decode from. (Def: false)
// save_buffer: Use a second (save) buffer to decode from. (Default: false)
// timer_num: Which ESP32 timer number to use? ESP32 only, otherwise unused.
// (Range: 0-3. Default: kDefaultESP32Timer)
// Returns:
// An IRrecv class object.
IRrecv::IRrecv(uint16_t recvpin, uint16_t bufsize, uint8_t timeout,
bool save_buffer) {
#if defined(ESP32)
IRrecv::IRrecv(const uint16_t recvpin, const uint16_t bufsize,
const uint8_t timeout, const bool save_buffer,
const uint8_t timer_num) {
// There are only 4 timers. 0 to 3.
_timer_num = std::min(timer_num, (uint8_t)3);
#else // ESP32
IRrecv::IRrecv(const uint16_t recvpin, const uint16_t bufsize,
const uint8_t timeout, const bool save_buffer) {
#endif // ESP32
irparams.recvpin = recvpin;
irparams.bufsize = bufsize;
// Ensure we are going to be able to store all possible values in the
@@ -123,7 +177,7 @@ IRrecv::IRrecv(uint16_t recvpin, uint16_t bufsize, uint8_t timeout,
irparams_save = NULL;
}
#if DECODE_HASH
unknown_threshold = kUnknownThreshold;
_unknown_threshold = kUnknownThreshold;
#endif // DECODE_HASH
}
@@ -134,35 +188,57 @@ IRrecv::~IRrecv(void) {
delete[] irparams_save->rawbuf;
delete irparams_save;
}
disableIRIn();
#if defined(ESP32)
if (timer != NULL) timerEnd(timer); // Cleanup the ESP32 timeout timer.
#endif // ESP32
}
// initialization
void IRrecv::enableIRIn() {
// initialize state machine variables
void IRrecv::enableIRIn(void) {
#if defined(ESP32)
// Initialize the ESP32 timer.
timer = timerBegin(_timer_num, 80, true); // 80MHz / 80 = 1 uSec granularity.
// Set the timer so it only fires once, and set it's trigger in uSeconds.
timerAlarmWrite(timer, MS_TO_USEC(irparams.timeout), ONCE);
// Note: Interrupt needs to be attached before it can be enabled or disabled.
timerAttachInterrupt(timer, &read_timeout, true);
#endif // ESP32
// Initialize state machine variables
resume();
#ifndef UNIT_TEST
// Initialize timer
#if defined(ESP8266)
// Initialize ESP8266 timer.
os_timer_disarm(&timer);
os_timer_setfn(&timer, reinterpret_cast<os_timer_func_t *>(read_timeout),
NULL);
#endif // ESP8266
// Attach Interrupt
attachInterrupt(irparams.recvpin, gpio_intr, CHANGE);
#endif
#endif // UNIT_TEST
}
void IRrecv::disableIRIn() {
void IRrecv::disableIRIn(void) {
#ifndef UNIT_TEST
#if defined(ESP8266)
os_timer_disarm(&timer);
#endif // ESP8266
#if defined(ESP32)
timerAlarmDisable(timer);
#endif // ESP32
detachInterrupt(irparams.recvpin);
#endif
#endif // UNIT_TEST
}
void IRrecv::resume() {
void IRrecv::resume(void) {
irparams.rcvstate = kIdleState;
irparams.rawlen = 0;
irparams.overflow = false;
#if defined(ESP32)
timerAlarmDisable(timer);
#endif // ESP32
}
// Make a copy of the interrupt state & buffer data.
@@ -196,12 +272,12 @@ void IRrecv::copyIrParams(volatile irparams_t *src, irparams_t *dst) {
// Obtain the maximum number of entries possible in the capture buffer.
// i.e. It's size.
uint16_t IRrecv::getBufSize() { return irparams.bufsize; }
uint16_t IRrecv::getBufSize(void) { 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;
void IRrecv::setUnknownThreshold(const uint16_t length) {
_unknown_threshold = length;
}
#endif // DECODE_HASH
@@ -335,7 +411,7 @@ bool IRrecv::decode(decode_results *results, irparams_t *save) {
#if DECODE_DENON
// Denon needs to precede Panasonic as it is a special case of Panasonic.
DPRINTLN("Attempting Denon decode");
if (decodeDenon(results, DENON_48_BITS) || decodeDenon(results, DENON_BITS) ||
if (decodeDenon(results, kDenon48Bits) || decodeDenon(results, kDenonBits) ||
decodeDenon(results, kDenonLegacyBits))
return true;
#endif
@@ -402,6 +478,10 @@ bool IRrecv::decode(decode_results *results, irparams_t *save) {
DPRINTLN("Attempting Daikin2 decode");
if (decodeDaikin2(results)) return true;
#endif
#if DECODE_DAIKIN216
DPRINTLN("Attempting Daikin216 decode");
if (decodeDaikin216(results)) return true;
#endif
#if DECODE_TOSHIBA_AC
DPRINTLN("Attempting Toshiba AC decode");
if (decodeToshibaAC(results)) return true;
@@ -509,6 +589,40 @@ bool IRrecv::decode(decode_results *results, irparams_t *save) {
DPRINTLN("Attempting Teco decode");
if (decodeTeco(results)) return true;
#endif
#if DECODE_LEGOPF
DPRINTLN("Attempting LEGOPF decode");
if (decodeLegoPf(results)) return true;
#endif
#if DECODE_MITSUBISHIHEAVY
DPRINTLN("Attempting MITSUBISHIHEAVY (152 bit) decode");
if (decodeMitsubishiHeavy(results, kMitsubishiHeavy152Bits)) return true;
DPRINTLN("Attempting MITSUBISHIHEAVY (88 bit) decode");
if (decodeMitsubishiHeavy(results, kMitsubishiHeavy88Bits)) return true;
#endif
#if DECODE_ARGO
DPRINTLN("Attempting Argo decode");
if (decodeArgo(results)) return true;
#endif // DECODE_ARGO
#if DECODE_SHARP_AC
DPRINTLN("Attempting SHARP_AC decode");
if (decodeSharpAc(results)) return true;
#endif
#if DECODE_GOODWEATHER
DPRINTLN("Attempting GOODWEATHER decode");
if (decodeGoodweather(results)) return true;
#endif // DECODE_GOODWEATHER
#if DECODE_INAX
DPRINTLN("Attempting Inax decode");
if (decodeInax(results)) return true;
#endif // DECODE_INAX
#if DECODE_TROTEC
DPRINTLN("Attempting Trotec decode");
if (decodeTrotec(results)) return true;
#endif // DECODE_TROTEC
#if DECODE_DAIKIN160
DPRINTLN("Attempting Daikin160 decode");
if (decodeDaikin160(results)) return true;
#endif // DECODE_DAIKIN160
#if DECODE_HASH
// decodeHash returns a hash on any input.
// Thus, it needs to be last in the list.
@@ -688,7 +802,7 @@ int16_t IRrecv::compare(uint16_t oldval, uint16_t newval) {
*/
bool IRrecv::decodeHash(decode_results *results) {
// Require at least some samples to prevent triggering on noise
if (results->rawlen < unknown_threshold) return false;
if (results->rawlen < _unknown_threshold) return false;
int32_t hash = kFnvBasis32;
// 'rawlen - 2' to avoid the look ahead from going out of bounds.
// Should probably be -3 to avoid comparing the trailing space entry,
+95 -34
View File
@@ -51,6 +51,9 @@ const uint16_t kMaxTimeoutMs = kRawTick * (UINT16_MAX / MS_TO_USEC(1));
const uint32_t kFnvPrime32 = 16777619UL;
const uint32_t kFnvBasis32 = 2166136261UL;
// Which of the ESP32 timers to use by default. (0-3)
const uint8_t kDefaultESP32Timer = 3;
#if DECODE_AC
// Hitachi AC is the current largest state size.
const uint16_t kStateSizeMax = kHitachiAc2StateLength;
@@ -108,17 +111,24 @@ class decode_results {
// main class for receiving IR
class IRrecv {
public:
explicit IRrecv(uint16_t recvpin, uint16_t bufsize = kRawBuf,
uint8_t timeout = kTimeoutMs,
bool save_buffer = false); // Constructor
~IRrecv(); // Destructor
#if defined(ESP32)
explicit IRrecv(const uint16_t recvpin, const uint16_t bufsize = kRawBuf,
const uint8_t timeout = kTimeoutMs,
const bool save_buffer = false,
const uint8_t timer_num = kDefaultESP32Timer); // Constructor
#else // ESP32
explicit IRrecv(const uint16_t recvpin, const uint16_t bufsize = kRawBuf,
const uint8_t timeout = kTimeoutMs,
const bool save_buffer = false); // Constructor
#endif // ESP32
~IRrecv(void); // Destructor
bool decode(decode_results *results, irparams_t *save = NULL);
void enableIRIn();
void disableIRIn();
void resume();
uint16_t getBufSize();
void enableIRIn(void);
void disableIRIn(void);
void resume(void);
uint16_t getBufSize(void);
#if DECODE_HASH
void setUnknownThreshold(uint16_t length);
void setUnknownThreshold(const uint16_t length);
#endif
static bool match(uint32_t measured, uint32_t desired,
uint8_t tolerance = kTolerance, uint16_t delta = 0);
@@ -133,8 +143,9 @@ class IRrecv {
private:
#endif
irparams_t *irparams_save;
uint8_t _timer_num;
#if DECODE_HASH
uint16_t unknown_threshold;
uint16_t _unknown_threshold;
#endif
// These are called by decode
void copyIrParams(volatile irparams_t *src, irparams_t *dst);
@@ -156,6 +167,10 @@ class IRrecv {
bool decodeNEC(decode_results *results, uint16_t nbits = kNECBits,
bool strict = true);
#endif
#if DECODE_ARGO
bool decodeArgo(decode_results *results, const uint16_t nbits = kArgoBits,
const bool strict = true);
#endif // DECODE_ARGO
#if DECODE_SONY
bool decodeSony(decode_results *results, uint16_t nbits = kSonyMinBits,
bool strict = false);
@@ -181,6 +196,10 @@ class IRrecv {
uint16_t nbits = kMitsubishiACBits,
bool strict = false);
#endif
#if DECODE_MITSUBISHIHEAVY
bool decodeMitsubishiHeavy(decode_results *results, const uint16_t nbits,
const bool strict = true);
#endif
#if (DECODE_RC5 || DECODE_R6 || DECODE_LASERTAG || DECODE_MWM)
int16_t getRClevel(decode_results *results, uint16_t *offset, uint16_t *used,
uint16_t bitTime, uint8_t tolerance = kTolerance,
@@ -200,21 +219,27 @@ class IRrecv {
bool strict = false);
#endif
#if (DECODE_PANASONIC || DECODE_DENON)
bool decodePanasonic(decode_results *results, uint16_t nbits = kPanasonicBits,
bool strict = false,
uint32_t manufacturer = kPanasonicManufacturer);
bool decodePanasonic(decode_results *results,
const uint16_t nbits = kPanasonicBits,
const bool strict = false,
const uint32_t manufacturer = kPanasonicManufacturer);
#endif
#if DECODE_LG
bool decodeLG(decode_results *results, uint16_t nbits = kLgBits,
bool strict = false);
#endif
#if DECODE_INAX
bool decodeInax(decode_results *results, const uint16_t nbits = kInaxBits,
const bool strict = true);
#endif // DECODE_INAX
#if DECODE_JVC
bool decodeJVC(decode_results *results, uint16_t nbits = kJvcBits,
bool strict = true);
#endif
#if DECODE_SAMSUNG
bool decodeSAMSUNG(decode_results *results, uint16_t nbits = kSamsungBits,
bool strict = true);
bool decodeSAMSUNG(decode_results *results,
const uint16_t nbits = kSamsungBits,
const bool strict = true);
#endif
#if DECODE_SAMSUNG
bool decodeSamsung36(decode_results *results,
@@ -222,8 +247,9 @@ class IRrecv {
const bool strict = true);
#endif
#if DECODE_SAMSUNG_AC
bool decodeSamsungAC(decode_results *results, uint16_t nbits = kSamsungAcBits,
bool strict = true);
bool decodeSamsungAC(decode_results *results,
const uint16_t nbits = kSamsungAcBits,
const bool strict = true);
#endif
#if DECODE_WHYNTER
bool decodeWhynter(decode_results *results, uint16_t nbits = kWhynterBits,
@@ -234,7 +260,7 @@ class IRrecv {
bool strict = true);
#endif
#if DECODE_DENON
bool decodeDenon(decode_results *results, uint16_t nbits = DENON_BITS,
bool decodeDenon(decode_results *results, uint16_t nbits = kDenonBits,
bool strict = true);
#endif
#if DECODE_DISH
@@ -242,8 +268,13 @@ class IRrecv {
bool strict = true);
#endif
#if (DECODE_SHARP || DECODE_DENON)
bool decodeSharp(decode_results *results, uint16_t nbits = kSharpBits,
bool strict = true, bool expansion = true);
bool decodeSharp(decode_results *results, const uint16_t nbits = kSharpBits,
const bool strict = true, const bool expansion = true);
#endif
#if DECODE_SHARP_AC
bool decodeSharpAc(decode_results *results,
const uint16_t nbits = kSharpAcBits,
const bool strict = true);
#endif
#if DECODE_AIWA_RC_T501
bool decodeAiwaRCT501(decode_results *results,
@@ -262,17 +293,32 @@ class IRrecv {
uint16_t nbits = kKelvinatorBits, bool strict = true);
#endif
#if DECODE_DAIKIN
bool decodeDaikin(decode_results *results, uint16_t nbits = kDaikinRawBits,
bool strict = true);
bool decodeDaikin(decode_results *results, const uint16_t nbits = kDaikinBits,
const bool strict = true);
#endif
#if DECODE_DAIKIN160
bool decodeDaikin160(decode_results *results,
const uint16_t nbits = kDaikin160Bits,
const bool strict = true);
#endif // DECODE_DAIKIN160
#if DECODE_DAIKIN2
bool decodeDaikin2(decode_results *results, uint16_t nbits = kDaikin2Bits,
bool strict = true);
#endif
#if DECODE_DAIKIN216
bool decodeDaikin216(decode_results *results,
const uint16_t nbits = kDaikin216Bits,
const bool strict = true);
#endif
#if DECODE_TOSHIBA_AC
bool decodeToshibaAC(decode_results *results,
uint16_t nbytes = kToshibaACBits, bool strict = true);
const uint16_t nbytes = kToshibaACBits,
const bool strict = true);
#endif
#if DECODE_TROTEC
bool decodeTrotec(decode_results *results, const uint16_t nbits = kTrotecBits,
const bool strict = true);
#endif // DECODE_TROTEC
#if DECODE_MIDEA
bool decodeMidea(decode_results *results, uint16_t nbits = kMideaBits,
bool strict = true);
@@ -289,6 +335,11 @@ class IRrecv {
bool decodeCarrierAC(decode_results *results, uint16_t nbits = kCarrierAcBits,
bool strict = true);
#endif
#if DECODE_GOODWEATHER
bool decodeGoodweather(decode_results *results,
const uint16_t nbits = kGoodweatherBits,
const bool strict = true);
#endif // DECODE_GOODWEATHER
#if DECODE_GREE
bool decodeGree(decode_results *results, uint16_t nbits = kGreeBits,
bool strict = true);
@@ -303,12 +354,14 @@ class IRrecv {
bool strict = true);
#endif
#if (DECODE_HITACHI_AC || DECODE_HITACHI_AC2)
bool decodeHitachiAC(decode_results *results, uint16_t nbits = kHitachiAcBits,
bool strict = true);
bool decodeHitachiAC(decode_results *results,
const uint16_t nbits = kHitachiAcBits,
const bool strict = true);
#endif
#if DECODE_HITACHI_AC1
bool decodeHitachiAC1(decode_results *results,
uint16_t nbits = kHitachiAc1Bits, bool strict = true);
const uint16_t nbits = kHitachiAc1Bits,
const bool strict = true);
#endif
#if DECODE_GICABLE
bool decodeGICable(decode_results *results, uint16_t nbits = kGicableBits,
@@ -316,7 +369,8 @@ class IRrecv {
#endif
#if DECODE_WHIRLPOOL_AC
bool decodeWhirlpoolAC(decode_results *results,
uint16_t nbits = kWhirlpoolAcBits, bool strict = true);
const uint16_t nbits = kWhirlpoolAcBits,
const bool strict = true);
#endif
#if DECODE_LUTRON
bool decodeLutron(decode_results *results, uint16_t nbits = kLutronBits,
@@ -328,7 +382,8 @@ class IRrecv {
#endif
#if DECODE_PANASONIC_AC
bool decodePanasonicAC(decode_results *results,
uint16_t nbits = kPanasonicAcBits, bool strict = true);
const uint16_t nbits = kPanasonicAcBits,
const bool strict = true);
#endif
#if DECODE_PIONEER
bool decodePioneer(decode_results *results,
@@ -340,16 +395,22 @@ class IRrecv {
bool strict = true);
#endif
#if DECODE_VESTEL_AC
bool decodeVestelAc(decode_results *results, uint16_t nbits = kVestelAcBits,
bool strict = true);
bool decodeVestelAc(decode_results *results,
const uint16_t nbits = kVestelAcBits,
const bool strict = true);
#endif
#if DECODE_TCL112AC
bool decodeTcl112Ac(decode_results *results, uint16_t nbits = kTcl112AcBits,
bool strict = true);
bool decodeTcl112Ac(decode_results *results,
const uint16_t nbits = kTcl112AcBits,
const bool strict = true);
#endif
#if DECODE_TECO
bool decodeTeco(decode_results *results, uint16_t nbits = kTecoBits,
bool strict = false);
bool decodeTeco(decode_results *results, const uint16_t nbits = kTecoBits,
const bool strict = false);
#endif
#if DECODE_LEGOPF
bool decodeLegoPf(decode_results *results, const uint16_t nbits = kLegoPfBits,
const bool strict = true);
#endif
};
+81 -13
View File
@@ -47,10 +47,11 @@
#include <stdint.h>
#ifdef UNIT_TEST
#include <iostream>
#endif
#include <string>
#endif // UNIT_TEST
// Library Version
#define _IRREMOTEESP8266_VERSION_ "2.5.6"
#define _IRREMOTEESP8266_VERSION_ "2.6.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!
@@ -117,6 +118,9 @@
#define DECODE_SHARP true
#define SEND_SHARP true
#define DECODE_SHARP_AC true
#define SEND_SHARP_AC true
#define DECODE_DENON true
#define SEND_DENON true
@@ -129,6 +133,9 @@
#define DECODE_FUJITSU_AC true
#define SEND_FUJITSU_AC true
#define DECODE_INAX true
#define SEND_INAX true
#define DECODE_DAIKIN true
#define SEND_DAIKIN true
@@ -138,16 +145,19 @@
#define DECODE_GLOBALCACHE false // Not written.
#define SEND_GLOBALCACHE true
#define DECODE_GOODWEATHER true
#define SEND_GOODWEATHER true
#define DECODE_GREE true
#define SEND_GREE true
#define DECODE_PRONTO false // Not written.
#define SEND_PRONTO true
#define DECODE_ARGO false // Not written.
#define DECODE_ARGO true // Experimental
#define SEND_ARGO true
#define DECODE_TROTEC false // Not implemented.
#define DECODE_TROTEC true
#define SEND_TROTEC true
#define DECODE_NIKAI true
@@ -213,8 +223,20 @@
#define DECODE_TECO true
#define SEND_TECO true
#define DECODE_TCL112AC true
#define SEND_TCL112AC true
#define DECODE_TCL112AC true
#define SEND_TCL112AC true
#define DECODE_LEGOPF true
#define SEND_LEGOPF true
#define DECODE_MITSUBISHIHEAVY true
#define SEND_MITSUBISHIHEAVY true
#define DECODE_DAIKIN216 true
#define SEND_DAIKIN216 true
#define DECODE_DAIKIN160 true
#define SEND_DAIKIN160 true
#if (DECODE_ARGO || DECODE_DAIKIN || DECODE_FUJITSU_AC || DECODE_GREE || \
DECODE_KELVINATOR || DECODE_MITSUBISHI_AC || DECODE_TOSHIBA_AC || \
@@ -222,7 +244,8 @@
DECODE_HITACHI_AC1 || DECODE_HITACHI_AC2 || DECODE_HAIER_AC_YRW02 || \
DECODE_WHIRLPOOL_AC || DECODE_SAMSUNG_AC || DECODE_ELECTRA_AC || \
DECODE_PANASONIC_AC || DECODE_MWM || DECODE_DAIKIN2 || \
DECODE_VESTEL_AC || DECODE_TCL112AC)
DECODE_VESTEL_AC || DECODE_TCL112AC || DECODE_MITSUBISHIHEAVY || \
DECODE_DAIKIN216 || DECODE_SHARP_AC || DECODE_DAIKIN160)
#define DECODE_AC true // We need some common infrastructure for decoding A/Cs.
#else
#define DECODE_AC false // We don't need that infrastructure.
@@ -299,6 +322,16 @@ enum decode_type_t {
TECO, // (55)
SAMSUNG36,
TCL112AC,
LEGOPF,
MITSUBISHI_HEAVY_88,
MITSUBISHI_HEAVY_152, // 60
DAIKIN216,
SHARP_AC,
GOODWEATHER,
INAX,
DAIKIN160, // 65
// Add new entries before this one, and update it to point to the last entry.
kLastDecodeType = DAIKIN160,
};
// Message lengths & required repeat values
@@ -308,20 +341,28 @@ const uint16_t kSingleRepeat = 1;
const uint16_t kAiwaRcT501Bits = 15;
const uint16_t kAiwaRcT501MinRepeats = kSingleRepeat;
const uint16_t kArgoStateLength = 12;
const uint16_t kArgoBits = kArgoStateLength * 8;
const uint16_t kArgoDefaultRepeat = kNoRepeat;
const uint16_t kCoolixBits = 24;
const uint16_t kCoolixDefaultRepeat = 1;
const uint16_t kCoolixDefaultRepeat = kSingleRepeat;
const uint16_t kCarrierAcBits = 32;
const uint16_t kCarrierAcMinRepeat = kNoRepeat;
// Daikin has a lot of static stuff that is discarded
const uint16_t kDaikinRawBits = 583;
const uint16_t kDaikinStateLength = 27;
const uint16_t kDaikinStateLength = 35;
const uint16_t kDaikinBits = kDaikinStateLength * 8;
const uint16_t kDaikinStateLengthShort = kDaikinStateLength - 8;
const uint16_t kDaikinBitsShort = kDaikinStateLengthShort * 8;
const uint16_t kDaikinDefaultRepeat = kNoRepeat;
const uint16_t kDaikin2StateLength = 39;
const uint16_t kDaikin2Bits = kDaikin2StateLength * 8;
const uint16_t kDaikin2DefaultRepeat = kNoRepeat;
const uint16_t kDaikin160StateLength = 20;
const uint16_t kDaikin160Bits = kDaikin160StateLength * 8;
const uint16_t kDaikin160DefaultRepeat = kNoRepeat;
const uint16_t kDaikin216StateLength = 27;
const uint16_t kDaikin216Bits = kDaikin216StateLength * 8;
const uint16_t kDaikin216DefaultRepeat = kNoRepeat;
const uint16_t kDenonBits = 15;
const uint16_t kDenon48Bits = 48;
const uint16_t kDenonLegacyBits = 14;
const uint16_t kDishBits = 16;
const uint16_t kDishMinRepeat = 3;
@@ -334,6 +375,8 @@ const uint16_t kFujitsuAcBits = kFujitsuAcStateLength * 8;
const uint16_t kFujitsuAcMinBits = (kFujitsuAcStateLengthShort - 1) * 8;
const uint16_t kGicableBits = 16;
const uint16_t kGicableMinRepeat = kSingleRepeat;
const uint16_t kGoodweatherBits = 48;
const uint16_t kGoodweatherMinRepeat = kNoRepeat;
const uint16_t kGreeStateLength = 8;
const uint16_t kGreeBits = kGreeStateLength * 8;
const uint16_t kGreeDefaultRepeat = kNoRepeat;
@@ -350,12 +393,16 @@ const uint16_t kHitachiAc1StateLength = 13;
const uint16_t kHitachiAc1Bits = kHitachiAc1StateLength * 8;
const uint16_t kHitachiAc2StateLength = 53;
const uint16_t kHitachiAc2Bits = kHitachiAc2StateLength * 8;
const uint16_t kInaxBits = 24;
const uint16_t kInaxMinRepeat = kSingleRepeat;
const uint16_t kJvcBits = 16;
const uint16_t kKelvinatorStateLength = 16;
const uint16_t kKelvinatorBits = kKelvinatorStateLength * 8;
const uint16_t kKelvinatorDefaultRepeat = kNoRepeat;
const uint16_t kLasertagBits = 13;
const uint16_t kLasertagMinRepeat = kNoRepeat;
const uint16_t kLegoPfBits = 16;
const uint16_t kLegoPfMinRepeat = kNoRepeat;
const uint16_t kLgBits = 28;
const uint16_t kLg32Bits = 32;
const uint16_t kLutronBits = 35;
@@ -369,6 +416,12 @@ const uint16_t kMitsubishiMinRepeat = kSingleRepeat;
const uint16_t kMitsubishiACStateLength = 18;
const uint16_t kMitsubishiACBits = kMitsubishiACStateLength * 8;
const uint16_t kMitsubishiACMinRepeat = kSingleRepeat;
const uint16_t kMitsubishiHeavy88StateLength = 11;
const uint16_t kMitsubishiHeavy88Bits = kMitsubishiHeavy88StateLength * 8;
const uint16_t kMitsubishiHeavy88MinRepeat = kNoRepeat;
const uint16_t kMitsubishiHeavy152StateLength = 19;
const uint16_t kMitsubishiHeavy152Bits = kMitsubishiHeavy152StateLength * 8;
const uint16_t kMitsubishiHeavy152MinRepeat = kNoRepeat;
const uint16_t kNikaiBits = 24;
const uint16_t kNECBits = 32;
const uint16_t kPanasonicBits = 48;
@@ -401,6 +454,9 @@ const uint16_t kSanyoLC7461Bits = (kSanyoLC7461AddressBits +
const uint8_t kSharpAddressBits = 5;
const uint8_t kSharpCommandBits = 8;
const uint16_t kSharpBits = kSharpAddressBits + kSharpCommandBits + 2; // 15
const uint16_t kSharpAcStateLength = 13;
const uint16_t kSharpAcBits = kSharpAcStateLength * 8; // 104
const uint16_t kSharpAcDefaultRepeat = kNoRepeat;
const uint8_t kSherwoodBits = kNECBits;
const uint16_t kSherwoodMinRepeat = kSingleRepeat;
const uint16_t kSony12Bits = 12;
@@ -417,12 +473,13 @@ const uint16_t kToshibaACStateLength = 9;
const uint16_t kToshibaACBits = kToshibaACStateLength * 8;
const uint16_t kToshibaACMinRepeat = kSingleRepeat;
const uint16_t kTrotecStateLength = 9;
const uint16_t kTrotecBits = kTrotecStateLength * 8;
const uint16_t kTrotecDefaultRepeat = kNoRepeat;
const uint16_t kWhirlpoolAcStateLength = 21;
const uint16_t kWhirlpoolAcBits = kWhirlpoolAcStateLength * 8;
const uint16_t kWhirlpoolAcDefaultRepeat = kNoRepeat;
const uint16_t kWhynterBits = 32;
const uint8_t kVestelAcBits = 56;
const uint8_t kVestelAcBits = 56;
// Legacy defines. (Deprecated)
@@ -432,7 +489,7 @@ const uint8_t kVestelAcBits = 56;
#define CARRIER_AC_BITS kCarrierAcBits
#define DAIKIN_COMMAND_LENGTH kDaikinStateLength
#define DENON_BITS kDenonBits
#define DENON_48_BITS kPanasonicBits
#define DENON_48_BITS kDenon48Bits
#define DENON_LEGACY_BITS kDenonLegacyBits
#define DISH_BITS kDishBits
#define FUJITSU_AC_MIN_REPEAT kFujitsuAcMinRepeat
@@ -496,4 +553,15 @@ const uint8_t kVestelAcBits = 56;
#define DPRINTLN(x)
#endif // DEBUG
#ifdef UNIT_TEST
#ifndef F
// Create a no-op F() macro so the code base still compiles outside of the
// Arduino framework. Thus we can safely use the Arduino 'F()' macro through-out
// the code base. That macro stores constants in Flash (PROGMEM) memory.
// See: https://github.com/markszabo/IRremoteESP8266/issues/667
#define F(x) x
#endif // F
typedef std::string String;
#endif // UNIT_TEST
#endif // IRREMOTEESP8266_H_
+155 -1
View File
@@ -110,6 +110,9 @@ void IRsend::enableIROut(uint32_t freq, uint8_t duty) {
}
if (freq < 1000) // Were we given kHz? Supports the old call usage.
freq *= 1000;
#ifdef UNIT_TEST
_freq_unittest = freq;
#endif // UNIT_TEST
uint32_t period = calcUSecPeriod(freq);
// Nr. of uSeconds the LED will be on per pulse.
onTimePeriod = (period * _dutycycle) / kDutyMax;
@@ -496,7 +499,8 @@ void IRsend::sendRaw(uint16_t buf[], uint16_t len, uint16_t hz) {
// nbits: How many bits long the message is to be.
// Returns:
// bool: True if it is a type we can attempt to send, false if not.
bool IRsend::send(decode_type_t type, uint64_t data, uint16_t nbits) {
bool IRsend::send(const decode_type_t type, const uint64_t data,
const uint16_t nbits) {
switch (type) {
#if SEND_AIWA_RC_T501
case AIWA_RC_T501:
@@ -533,6 +537,11 @@ bool IRsend::send(decode_type_t type, uint64_t data, uint16_t nbits) {
sendGree(data, nbits);
break;
#endif
#if SEND_INAX
case INAX:
sendInax(data, nbits);
break;
#endif // SEND_INAX
#if SEND_JVC
case JVC:
sendJVC(data, nbits);
@@ -543,6 +552,11 @@ bool IRsend::send(decode_type_t type, uint64_t data, uint16_t nbits) {
sendLasertag(data, nbits);
break;
#endif
#if SEND_LEGOPF
case LEGOPF:
sendLegoPf(data, nbits);
break;
#endif
#if SEND_LG
case LG:
sendLG(data, nbits);
@@ -662,3 +676,143 @@ bool IRsend::send(decode_type_t type, uint64_t data, uint16_t nbits) {
}
return true;
}
// Send a complex (>= 64 bits) IR message of a given type.
// An unknown/unsupported type will do nothing.
// Args:
// type: Protocol number/type of the message you want to send.
// state: A pointer to the array of bytes that make up the state[].
// nbytes: How many bytes are in the state.
// Returns:
// bool: True if it is a type we can attempt to send, false if not.
bool IRsend::send(const decode_type_t type, const unsigned char *state,
const uint16_t nbytes) {
switch (type) {
#if SEND_ARGO
case ARGO:
sendArgo(state, nbytes);
break;
#endif // SEND_ARGO
#if SEND_DAIKIN
case DAIKIN:
sendDaikin(state, nbytes);
break;
#endif // SEND_DAIKIN
#if SEND_DAIKIN160
case DAIKIN160:
sendDaikin160(state, nbytes);
break;
#endif // SEND_DAIKIN160
#if SEND_DAIKIN2
case DAIKIN2:
sendDaikin2(state, nbytes);
break;
#endif // SEND_DAIKIN2
#if SEND_DAIKIN216
case DAIKIN216:
sendDaikin216(state, nbytes);
break;
#endif // SEND_DAIKIN216
#if SEND_ELECTRA_AC
case ELECTRA_AC:
sendElectraAC(state, nbytes);
break;
#endif // SEND_ELECTRA_AC
#if SEND_FUJITSU_AC
case FUJITSU_AC:
sendFujitsuAC(state, nbytes);
break;
#endif // SEND_FUJITSU_AC
#if SEND_GREE
case GREE:
sendGree(state, nbytes);
break;
#endif // SEND_GREE
#if SEND_HAIER_AC
case HAIER_AC:
sendHaierAC(state, nbytes);
break;
#endif // SEND_HAIER_AC
#if SEND_HAIER_AC_YRW02
case HAIER_AC_YRW02:
sendHaierACYRW02(state, nbytes);
break;
#endif // SEND_HAIER_AC_YRW02
#if SEND_HITACHI_AC
case HITACHI_AC:
sendHitachiAC(state, nbytes);
break;
#endif // SEND_HITACHI_AC
#if SEND_HITACHI_AC1
case HITACHI_AC1:
sendHitachiAC1(state, nbytes);
break;
#endif // SEND_HITACHI_AC1
#if SEND_HITACHI_AC2
case HITACHI_AC2:
sendHitachiAC2(state, nbytes);
break;
#endif // SEND_HITACHI_AC2
#if SEND_KELVINATOR
case KELVINATOR:
sendKelvinator(state, nbytes);
break;
#endif // SEND_KELVINATOR
#if SEND_MITSUBISHI_AC
case MITSUBISHI_AC:
sendMitsubishiAC(state, nbytes);
break;
#endif // SEND_MITSUBISHI_AC
#if SEND_MITSUBISHIHEAVY
case MITSUBISHI_HEAVY_88:
sendMitsubishiHeavy88(state, nbytes);
break;
case MITSUBISHI_HEAVY_152:
sendMitsubishiHeavy152(state, nbytes);
break;
#endif // SEND_MITSUBISHIHEAVY
#if SEND_MWM
case MWM:
sendMWM(state, nbytes);
break;
#endif // SEND_MWM
#if SEND_PANASONIC_AC
case PANASONIC_AC:
sendPanasonicAC(state, nbytes);
break;
#endif // SEND_PANASONIC_AC
#if SEND_SAMSUNG_AC
case SAMSUNG_AC:
sendSamsungAC(state, nbytes);
break;
#endif // SEND_SAMSUNG_AC
#if SEND_SHARP_AC
case SHARP_AC:
sendSharpAc(state, nbytes);
break;
#endif // SEND_SHARP_AC
#if SEND_TCL112AC
case TCL112AC:
sendTcl112Ac(state, nbytes);
break;
#endif // SEND_TCL112AC
#if SEND_TOSHIBA_AC
case TOSHIBA_AC:
sendToshibaAC(state, nbytes);
break;
#endif // SEND_TOSHIBA_AC
#if SEND_TROTEC
case TROTEC:
sendTrotec(state, nbytes);
break;
#endif // SEND_TROTEC
#if SEND_WHIRLPOOL_AC
case WHIRLPOOL_AC:
sendWhirlpoolAC(state, nbytes);
break;
#endif // SEND_WHIRLPOOL_AC
default:
return false;
}
return true;
}
+198 -74
View File
@@ -21,15 +21,87 @@
// Constants
// Offset (in microseconds) to use in Period time calculations to account for
// code excution time in producing the software PWM signal.
// Value was calculated on Wemos D1 mini using v2.4.1 with v2.4.0 ESP core
#if defined(ESP32)
// Calculated on a generic ESP-WROOM-32 board with v3.2-18 SDK @ 240MHz
const int8_t kPeriodOffset = -2;
#elif (defined(ESP8266) && F_CPU == 160000000L) // NOLINT(whitespace/parens)
// Calculated on an ESP8266 NodeMCU v2 board using:
// v2.6.0 with v2.5.2 ESP core @ 160MHz
const int8_t kPeriodOffset = -2;
#else // (defined(ESP8266) && F_CPU == 160000000L)
// Calculated on ESP8266 Wemos D1 mini using v2.4.1 with v2.4.0 ESP core @ 40MHz
const int8_t kPeriodOffset = -5;
#endif // (defined(ESP8266) && F_CPU == 160000000L)
const uint8_t kDutyDefault = 50; // Percentage
const uint8_t kDutyMax = 100; // Percentage
// delayMicroseconds() is only accurate to 16383us.
// Ref: https://www.arduino.cc/en/Reference/delayMicroseconds
const uint16_t kMaxAccurateUsecDelay = 16383;
// Usecs to wait between messages we don't know the proper gap time.
const uint32_t kDefaultMessageGap = 1000000;
const uint32_t kDefaultMessageGap = 100000;
namespace stdAc {
enum class opmode_t {
kOff = -1,
kAuto = 0,
kCool = 1,
kHeat = 2,
kDry = 3,
kFan = 4,
};
enum class fanspeed_t {
kAuto = 0,
kMin = 1,
kLow = 2,
kMedium = 3,
kHigh = 4,
kMax = 5,
};
enum class swingv_t {
kOff = -1,
kAuto = 0,
kHighest = 1,
kHigh = 2,
kMiddle = 3,
kLow = 4,
kLowest = 5,
};
enum class swingh_t {
kOff = -1,
kAuto = 0, // a.k.a. On.
kLeftMax = 1,
kLeft = 2,
kMiddle = 3,
kRight = 4,
kRightMax = 5,
};
// Structure to hold a common A/C state.
typedef struct {
decode_type_t protocol;
int16_t model;
bool power;
stdAc::opmode_t mode;
float degrees;
bool celsius;
stdAc::fanspeed_t fanspeed;
stdAc::swingv_t swingv;
stdAc::swingh_t swingh;
bool quiet;
bool turbo;
bool econo;
bool light;
bool filter;
bool clean;
bool beep;
int16_t sleep;
int16_t clock;
} state_t;
}; // namespace stdAc
// Classes
class IRsend {
@@ -68,7 +140,10 @@ class IRsend {
const uint8_t *dataptr, const uint16_t nbytes,
const uint16_t frequency, const bool MSBfirst,
const uint16_t repeat, const uint8_t dutycycle);
bool send(decode_type_t type, uint64_t data, uint16_t nbits);
bool send(const decode_type_t type, const uint64_t data,
const uint16_t nbits);
bool send(const decode_type_t type, const uint8_t state[],
const uint16_t nbytes);
#if (SEND_NEC || SEND_SHERWOOD || SEND_AIWA_RC_T501 || SEND_SANYO)
void sendNEC(uint64_t data, uint16_t nbits = kNECBits,
uint16_t repeat = kNoRepeat);
@@ -90,18 +165,18 @@ class IRsend {
uint16_t repeat = kSherwoodMinRepeat);
#endif
#if SEND_SAMSUNG
void sendSAMSUNG(uint64_t data, uint16_t nbits = kSamsungBits,
uint16_t repeat = kNoRepeat);
uint32_t encodeSAMSUNG(uint8_t customer, uint8_t command);
void sendSAMSUNG(const uint64_t data, const uint16_t nbits = kSamsungBits,
const uint16_t repeat = kNoRepeat);
uint32_t encodeSAMSUNG(const uint8_t customer, const uint8_t command);
#endif
#if SEND_SAMSUNG36
void sendSamsung36(const uint64_t data, const uint16_t nbits = kSamsung36Bits,
const uint16_t repeat = kNoRepeat);
#endif
#if SEND_SAMSUNG_AC
void sendSamsungAC(unsigned char data[],
uint16_t nbytes = kSamsungAcStateLength,
uint16_t repeat = kSamsungAcDefaultRepeat);
void sendSamsungAC(const unsigned char data[],
const uint16_t nbytes = kSamsungAcStateLength,
const uint16_t repeat = kSamsungAcDefaultRepeat);
#endif
#if SEND_LG
void sendLG(uint64_t data, uint16_t nbits = kLgBits,
@@ -111,21 +186,27 @@ class IRsend {
uint32_t encodeLG(uint16_t address, uint16_t command);
#endif
#if (SEND_SHARP || SEND_DENON)
uint32_t encodeSharp(uint16_t address, uint16_t command,
uint16_t expansion = 1, uint16_t check = 0,
bool MSBfirst = false);
void sendSharp(uint16_t address, uint16_t command,
uint16_t nbits = kSharpBits, uint16_t repeat = kNoRepeat);
void sendSharpRaw(uint64_t data, uint16_t nbits = kSharpBits,
uint16_t repeat = kNoRepeat);
uint32_t encodeSharp(const uint16_t address, const uint16_t command,
const uint16_t expansion = 1, const uint16_t check = 0,
const bool MSBfirst = false);
void sendSharp(const uint16_t address, const uint16_t command,
const uint16_t nbits = kSharpBits,
const uint16_t repeat = kNoRepeat);
void sendSharpRaw(const uint64_t data, const uint16_t nbits = kSharpBits,
const uint16_t repeat = kNoRepeat);
#endif
#if SEND_SHARP_AC
void sendSharpAc(const unsigned char data[],
const uint16_t nbytes = kSharpAcStateLength,
const uint16_t repeat = kSharpAcDefaultRepeat);
#endif // SEND_SHARP_AC
#if SEND_JVC
void sendJVC(uint64_t data, uint16_t nbits = kJvcBits,
uint16_t repeat = kNoRepeat);
uint16_t encodeJVC(uint8_t address, uint8_t command);
#endif
#if SEND_DENON
void sendDenon(uint64_t data, uint16_t nbits = DENON_BITS,
void sendDenon(uint64_t data, uint16_t nbits = kDenonBits,
uint16_t repeat = kNoRepeat);
#endif
#if SEND_SANYO
@@ -142,13 +223,14 @@ class IRsend {
uint16_t repeat = kDishMinRepeat);
#endif
#if (SEND_PANASONIC || SEND_DENON)
void sendPanasonic64(uint64_t data, uint16_t nbits = kPanasonicBits,
uint16_t repeat = kNoRepeat);
void sendPanasonic(uint16_t address, uint32_t data,
uint16_t nbits = kPanasonicBits,
uint16_t repeat = kNoRepeat);
uint64_t encodePanasonic(uint16_t manufacturer, uint8_t device,
uint8_t subdevice, uint8_t function);
void sendPanasonic64(const uint64_t data,
const uint16_t nbits = kPanasonicBits,
const uint16_t repeat = kNoRepeat);
void sendPanasonic(const uint16_t address, const uint32_t data,
const uint16_t nbits = kPanasonicBits,
const uint16_t repeat = kNoRepeat);
uint64_t encodePanasonic(const uint16_t manufacturer, const uint8_t device,
const uint8_t subdevice, const uint8_t function);
#endif
#if SEND_RC5
void sendRC5(uint64_t data, uint16_t nbits = kRC5XBits,
@@ -187,59 +269,92 @@ class IRsend {
uint16_t repeat = kMitsubishiMinRepeat);
#endif
#if SEND_MITSUBISHI_AC
void sendMitsubishiAC(unsigned char data[],
uint16_t nbytes = kMitsubishiACStateLength,
uint16_t repeat = kMitsubishiACMinRepeat);
void sendMitsubishiAC(const unsigned char data[],
const uint16_t nbytes = kMitsubishiACStateLength,
const uint16_t repeat = kMitsubishiACMinRepeat);
#endif
#if SEND_MITSUBISHIHEAVY
void sendMitsubishiHeavy88(
const unsigned char data[],
const uint16_t nbytes = kMitsubishiHeavy88StateLength,
const uint16_t repeat = kMitsubishiHeavy88MinRepeat);
void sendMitsubishiHeavy152(
const unsigned char data[],
const uint16_t nbytes = kMitsubishiHeavy152StateLength,
const uint16_t repeat = kMitsubishiHeavy152MinRepeat);
#endif
#if SEND_FUJITSU_AC
void sendFujitsuAC(unsigned char data[], uint16_t nbytes,
uint16_t repeat = kFujitsuAcMinRepeat);
void sendFujitsuAC(const unsigned char data[], const uint16_t nbytes,
const uint16_t repeat = kFujitsuAcMinRepeat);
#endif
#if SEND_INAX
void sendInax(const uint64_t data, const uint16_t nbits = kInaxBits,
const uint16_t repeat = kInaxMinRepeat);
#endif // SEND_INAX
#if SEND_GLOBALCACHE
void sendGC(uint16_t buf[], uint16_t len);
#endif
#if SEND_KELVINATOR
void sendKelvinator(unsigned char data[],
uint16_t nbytes = kKelvinatorStateLength,
uint16_t repeat = kKelvinatorDefaultRepeat);
void sendKelvinator(const unsigned char data[],
const uint16_t nbytes = kKelvinatorStateLength,
const uint16_t repeat = kKelvinatorDefaultRepeat);
#endif
#if SEND_DAIKIN
void sendDaikin(unsigned char data[], uint16_t nbytes = kDaikinStateLength,
uint16_t repeat = kDaikinDefaultRepeat);
void sendDaikin(const unsigned char data[],
const uint16_t nbytes = kDaikinStateLength,
const uint16_t repeat = kDaikinDefaultRepeat);
#endif
#if SEND_DAIKIN160
void sendDaikin160(const unsigned char data[],
const uint16_t nbytes = kDaikin160StateLength,
const uint16_t repeat = kDaikin160DefaultRepeat);
#endif // SEND_DAIKIN160
#if SEND_DAIKIN2
void sendDaikin2(unsigned char data[], uint16_t nbytes = kDaikin2StateLength,
uint16_t repeat = kDaikin2DefaultRepeat);
void sendDaikin2(const unsigned char data[],
const uint16_t nbytes = kDaikin2StateLength,
const uint16_t repeat = kDaikin2DefaultRepeat);
#endif
#if SEND_DAIKIN216
void sendDaikin216(const unsigned char data[],
const uint16_t nbytes = kDaikin216StateLength,
const uint16_t repeat = kDaikin216DefaultRepeat);
#endif
#if SEND_AIWA_RC_T501
void sendAiwaRCT501(uint64_t data, uint16_t nbits = kAiwaRcT501Bits,
uint16_t repeat = kAiwaRcT501MinRepeats);
#endif
#if SEND_GREE
void sendGree(uint64_t data, uint16_t nbits = kGreeBits,
uint16_t repeat = kGreeDefaultRepeat);
void sendGree(uint8_t data[], uint16_t nbytes = kGreeStateLength,
uint16_t repeat = kGreeDefaultRepeat);
void sendGree(const uint64_t data, const uint16_t nbits = kGreeBits,
const uint16_t repeat = kGreeDefaultRepeat);
void sendGree(const uint8_t data[], const uint16_t nbytes = kGreeStateLength,
const uint16_t repeat = kGreeDefaultRepeat);
#endif
#if SEND_GOODWEATHER
void sendGoodweather(const uint64_t data,
const uint16_t nbits = kGoodweatherBits,
const uint16_t repeat = kGoodweatherMinRepeat);
#endif // SEND_GOODWEATHER
#if SEND_PRONTO
void sendPronto(uint16_t data[], uint16_t len, uint16_t repeat = kNoRepeat);
#endif
#if SEND_ARGO
void sendArgo(unsigned char data[], uint16_t nbytes = kArgoStateLength,
uint16_t repeat = kArgoDefaultRepeat);
void sendArgo(const unsigned char data[],
const uint16_t nbytes = kArgoStateLength,
const uint16_t repeat = kArgoDefaultRepeat);
#endif
#if SEND_TROTEC
void sendTrotec(unsigned char data[], uint16_t nbytes = kTrotecStateLength,
uint16_t repeat = kTrotecDefaultRepeat);
void sendTrotec(const unsigned char data[],
const uint16_t nbytes = kTrotecStateLength,
const uint16_t repeat = kTrotecDefaultRepeat);
#endif
#if SEND_NIKAI
void sendNikai(uint64_t data, uint16_t nbits = kNikaiBits,
uint16_t repeat = kNoRepeat);
#endif
#if SEND_TOSHIBA_AC
void sendToshibaAC(unsigned char data[],
uint16_t nbytes = kToshibaACStateLength,
uint16_t repeat = kToshibaACMinRepeat);
void sendToshibaAC(const unsigned char data[],
const uint16_t nbytes = kToshibaACStateLength,
const uint16_t repeat = kToshibaACMinRepeat);
#endif
#if SEND_MIDEA
void sendMidea(uint64_t data, uint16_t nbits = kMideaBits,
@@ -259,51 +374,52 @@ class IRsend {
uint16_t repeat = kCarrierAcMinRepeat);
#endif
#if (SEND_HAIER_AC || SEND_HAIER_AC_YRW02)
void sendHaierAC(unsigned char data[], uint16_t nbytes = kHaierACStateLength,
uint16_t repeat = kHaierAcDefaultRepeat);
void sendHaierAC(const unsigned char data[],
const uint16_t nbytes = kHaierACStateLength,
const uint16_t repeat = kHaierAcDefaultRepeat);
#endif
#if SEND_HAIER_AC_YRW02
void sendHaierACYRW02(unsigned char data[],
uint16_t nbytes = kHaierACYRW02StateLength,
uint16_t repeat = kHaierAcYrw02DefaultRepeat);
void sendHaierACYRW02(const unsigned char data[],
const uint16_t nbytes = kHaierACYRW02StateLength,
const uint16_t repeat = kHaierAcYrw02DefaultRepeat);
#endif
#if SEND_HITACHI_AC
void sendHitachiAC(unsigned char data[],
uint16_t nbytes = kHitachiAcStateLength,
uint16_t repeat = kHitachiAcDefaultRepeat);
void sendHitachiAC(const unsigned char data[],
const uint16_t nbytes = kHitachiAcStateLength,
const uint16_t repeat = kHitachiAcDefaultRepeat);
#endif
#if SEND_HITACHI_AC1
void sendHitachiAC1(unsigned char data[],
uint16_t nbytes = kHitachiAc1StateLength,
uint16_t repeat = kNoRepeat);
void sendHitachiAC1(const unsigned char data[],
const uint16_t nbytes = kHitachiAc1StateLength,
const uint16_t repeat = kNoRepeat);
#endif
#if SEND_HITACHI_AC2
void sendHitachiAC2(unsigned char data[],
uint16_t nbytes = kHitachiAc2StateLength,
uint16_t repeat = kNoRepeat);
void sendHitachiAC2(const unsigned char data[],
const uint16_t nbytes = kHitachiAc2StateLength,
const uint16_t repeat = kNoRepeat);
#endif
#if SEND_GICABLE
void sendGICable(uint64_t data, uint16_t nbits = kGicableBits,
uint16_t repeat = kGicableMinRepeat);
#endif
#if SEND_WHIRLPOOL_AC
void sendWhirlpoolAC(unsigned char data[],
uint16_t nbytes = kWhirlpoolAcStateLength,
uint16_t repeat = kWhirlpoolAcDefaultRepeat);
void sendWhirlpoolAC(const unsigned char data[],
const uint16_t nbytes = kWhirlpoolAcStateLength,
const uint16_t repeat = kWhirlpoolAcDefaultRepeat);
#endif
#if SEND_LUTRON
void sendLutron(uint64_t data, uint16_t nbits = kLutronBits,
uint16_t repeat = kNoRepeat);
#endif
#if SEND_ELECTRA_AC
void sendElectraAC(unsigned char data[],
uint16_t nbytes = kElectraAcStateLength,
uint16_t repeat = kNoRepeat);
void sendElectraAC(const unsigned char data[],
const uint16_t nbytes = kElectraAcStateLength,
const uint16_t repeat = kNoRepeat);
#endif
#if SEND_PANASONIC_AC
void sendPanasonicAC(unsigned char data[],
uint16_t nbytes = kPanasonicAcStateLength,
uint16_t repeat = kPanasonicAcDefaultRepeat);
void sendPanasonicAC(const unsigned char data[],
const uint16_t nbytes = kPanasonicAcStateLength,
const uint16_t repeat = kPanasonicAcDefaultRepeat);
#endif
#if SEND_PIONEER
void sendPioneer(const uint64_t data, const uint16_t nbits = kPioneerBits,
@@ -311,8 +427,8 @@ class IRsend {
uint64_t encodePioneer(uint16_t address, uint16_t command);
#endif
#if SEND_MWM
void sendMWM(unsigned char data[], uint16_t nbytes,
uint16_t repeat = kNoRepeat);
void sendMWM(const unsigned char data[], const uint16_t nbytes,
const uint16_t repeat = kNoRepeat);
#endif
#if SEND_VESTEL_AC
void sendVestelAc(const uint64_t data, const uint16_t nbits = kVestelAcBits,
@@ -324,8 +440,12 @@ class IRsend {
const uint16_t repeat = kTcl112AcDefaultRepeat);
#endif
#if SEND_TECO
void sendTeco(uint64_t data, uint16_t nbits = kTecoBits,
uint16_t repeat = kNoRepeat);
void sendTeco(const uint64_t data, const uint16_t nbits = kTecoBits,
const uint16_t repeat = kNoRepeat);
#endif
#if SEND_LEGOPF
void sendLegoPf(const uint64_t data, const uint16_t nbits = kLegoPfBits,
const uint16_t repeat = kLegoPfMinRepeat);
#endif
@@ -342,8 +462,12 @@ class IRsend {
uint8_t outputOff;
VIRTUAL void ledOff();
VIRTUAL void ledOn();
#ifndef UNIT_TEST
private:
#else
uint32_t _freq_unittest;
#endif // UNIT_TEST
uint16_t onTimePeriod;
uint16_t offTimePeriod;
uint16_t IRpin;
+31 -2
View File
@@ -7,12 +7,12 @@
#ifdef UNIT_TEST
// Used to help simulate elapsed time in unit tests.
extern uint32_t _IRtimer_unittest_now;
uint32_t _IRtimer_unittest_now = 0;
uint32_t _TimerMs_unittest_now = 0;
#endif // UNIT_TEST
// This class performs a simple time in useconds since instantiated.
// Handles when the system timer wraps around (once).
IRtimer::IRtimer() { reset(); }
void IRtimer::reset() {
@@ -39,3 +39,32 @@ uint32_t IRtimer::elapsed() {
#ifdef UNIT_TEST
void IRtimer::add(uint32_t usecs) { _IRtimer_unittest_now += usecs; }
#endif // UNIT_TEST
// This class performs a simple time in milli-seoncds since instantiated.
// Handles when the system timer wraps around (once).
TimerMs::TimerMs() { reset(); }
void TimerMs::reset() {
#ifndef UNIT_TEST
start = millis();
#else
start = _TimerMs_unittest_now;
#endif
}
uint32_t TimerMs::elapsed() {
#ifndef UNIT_TEST
uint32_t now = millis();
#else
uint32_t now = _TimerMs_unittest_now;
#endif
if (start <= now) // Check if the system timer has wrapped.
return now - start; // No wrap.
else
return UINT32_MAX - start + now; // Has wrapped.
}
// Only used in unit testing.
#ifdef UNIT_TEST
void TimerMs::add(uint32_t msecs) { _IRtimer_unittest_now += msecs; }
#endif // UNIT_TEST
+12
View File
@@ -20,4 +20,16 @@ class IRtimer {
uint32_t start;
};
class TimerMs {
public:
TimerMs();
void reset();
uint32_t elapsed();
#ifdef UNIT_TEST
static void add(uint32_t msecs);
#endif // UNIT_TEST
private:
uint32_t start;
};
#endif // IRTIMER_H_
+404 -140
View File
@@ -7,6 +7,7 @@
#define __STDC_LIMIT_MACROS
#include <stdint.h>
#include <string.h>
#include <algorithm>
#ifndef ARDUINO
#include <string>
@@ -43,19 +44,19 @@ uint64_t reverseBits(uint64_t input, uint16_t nbits) {
// 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;
// Reserve some string space to reduce fragmentation.
// 16 bytes should store a uint64 in hex text which is the likely worst case.
// 64 bytes would be the worst case (base 2).
result.reserve(16);
do {
char c = input % base;
input /= base;
@@ -81,208 +82,437 @@ void serialPrintUint64(uint64_t input, uint8_t base) {
}
#endif
// Convert a c-style str to a decode_type_t
// Note: Assumes str is upper case.
//
// Args:
// str: An upper-case C-style string.
// Returns:
// A decode_type_t enum.
decode_type_t strToDecodeType(const char * const str) {
if (!strcmp(str, "UNKNOWN"))
return decode_type_t::UNKNOWN;
else if (!strcmp(str, "UNUSED"))
return decode_type_t::UNUSED;
else if (!strcmp(str, "AIWA_RC_T501"))
return decode_type_t::AIWA_RC_T501;
else if (!strcmp(str, "ARGO"))
return decode_type_t::ARGO;
else if (!strcmp(str, "CARRIER_AC"))
return decode_type_t::CARRIER_AC;
else if (!strcmp(str, "COOLIX"))
return decode_type_t::COOLIX;
else if (!strcmp(str, "DAIKIN"))
return decode_type_t::DAIKIN;
else if (!strcmp(str, "DAIKIN160"))
return decode_type_t::DAIKIN160;
else if (!strcmp(str, "DAIKIN2"))
return decode_type_t::DAIKIN2;
else if (!strcmp(str, "DAIKIN216"))
return decode_type_t::DAIKIN216;
else if (!strcmp(str, "DENON"))
return decode_type_t::DENON;
else if (!strcmp(str, "DISH"))
return decode_type_t::DISH;
else if (!strcmp(str, "ELECTRA_AC"))
return decode_type_t::ELECTRA_AC;
else if (!strcmp(str, "FUJITSU_AC"))
return decode_type_t::FUJITSU_AC;
else if (!strcmp(str, "GICABLE"))
return decode_type_t::GICABLE;
else if (!strcmp(str, "GLOBALCACHE"))
return decode_type_t::GLOBALCACHE;
else if (!strcmp(str, "GOODWEATHER"))
return decode_type_t::GOODWEATHER;
else if (!strcmp(str, "GREE"))
return decode_type_t::GREE;
else if (!strcmp(str, "HAIER_AC"))
return decode_type_t::HAIER_AC;
else if (!strcmp(str, "HAIER_AC_YRW02"))
return decode_type_t::HAIER_AC_YRW02;
else if (!strcmp(str, "HITACHI_AC"))
return decode_type_t::HITACHI_AC;
else if (!strcmp(str, "HITACHI_AC1"))
return decode_type_t::HITACHI_AC1;
else if (!strcmp(str, "HITACHI_AC2"))
return decode_type_t::HITACHI_AC2;
else if (!strcmp(str, "INAX"))
return decode_type_t::INAX;
else if (!strcmp(str, "JVC"))
return decode_type_t::JVC;
else if (!strcmp(str, "KELVINATOR"))
return decode_type_t::KELVINATOR;
else if (!strcmp(str, "LEGOPF"))
return decode_type_t::LEGOPF;
else if (!strcmp(str, "LG"))
return decode_type_t::LG;
else if (!strcmp(str, "LG2"))
return decode_type_t::LG2;
else if (!strcmp(str, "LASERTAG"))
return decode_type_t::LASERTAG;
else if (!strcmp(str, "LUTRON"))
return decode_type_t::LUTRON;
else if (!strcmp(str, "MAGIQUEST"))
return decode_type_t::MAGIQUEST;
else if (!strcmp(str, "MIDEA"))
return decode_type_t::MIDEA;
else if (!strcmp(str, "MITSUBISHI"))
return decode_type_t::MITSUBISHI;
else if (!strcmp(str, "MITSUBISHI2"))
return decode_type_t::MITSUBISHI2;
else if (!strcmp(str, "MITSUBISHI_AC"))
return decode_type_t::MITSUBISHI_AC;
else if (!strcmp(str, "MWM"))
return decode_type_t::MWM;
else if (!strcmp(str, "NEC") || !strcmp(str, "NEC (NON-STRICT"))
return decode_type_t::NEC;
else if (!strcmp(str, "NIKAI"))
return decode_type_t::NIKAI;
else if (!strcmp(str, "PANASONIC"))
return decode_type_t::PANASONIC;
else if (!strcmp(str, "PANASONIC_AC"))
return decode_type_t::PANASONIC_AC;
else if (!strcmp(str, "PIONEER"))
return decode_type_t::PIONEER;
else if (!strcmp(str, "PRONTO"))
return decode_type_t::PRONTO;
else if (!strcmp(str, "RAW"))
return decode_type_t::RAW;
else if (!strcmp(str, "RC5"))
return decode_type_t::RC5;
else if (!strcmp(str, "RC5X"))
return decode_type_t::RC5X;
else if (!strcmp(str, "RC6"))
return decode_type_t::RC6;
else if (!strcmp(str, "RCMM"))
return decode_type_t::RCMM;
else if (!strcmp(str, "SAMSUNG"))
return decode_type_t::SAMSUNG;
else if (!strcmp(str, "SAMSUNG36"))
return decode_type_t::SAMSUNG36;
else if (!strcmp(str, "SAMSUNG_AC"))
return decode_type_t::SAMSUNG_AC;
else if (!strcmp(str, "SANYO"))
return decode_type_t::SANYO;
else if (!strcmp(str, "SANYO_LC7461"))
return decode_type_t::SANYO_LC7461;
else if (!strcmp(str, "SHARP"))
return decode_type_t::SHARP;
else if (!strcmp(str, "SHARP_AC"))
return decode_type_t::SHARP_AC;
else if (!strcmp(str, "SHERWOOD"))
return decode_type_t::SHERWOOD;
else if (!strcmp(str, "SONY"))
return decode_type_t::SONY;
else if (!strcmp(str, "TCL112AC"))
return decode_type_t::TCL112AC;
else if (!strcmp(str, "TECO"))
return decode_type_t::TECO;
else if (!strcmp(str, "TOSHIBA_AC"))
return decode_type_t::TOSHIBA_AC;
else if (!strcmp(str, "TROTEC"))
return decode_type_t::TROTEC;
else if (!strcmp(str, "VESTEL_AC"))
return decode_type_t::VESTEL_AC;
else if (!strcmp(str, "WHIRLPOOL_AC"))
return decode_type_t::WHIRLPOOL_AC;
else if (!strcmp(str, "WHYNTER"))
return decode_type_t::WHYNTER;
// Handle integer values of the type by converting to a string and back again.
decode_type_t result = strToDecodeType(
typeToString((decode_type_t)atoi(str)).c_str());
if (result > 0)
return result;
else
return decode_type_t::UNKNOWN;
}
// Escape any special HTML (unsafe) characters in a string. e.g. anti-XSS.
// Args:
// unescaped: A string containing text to make HTML safe.
// Returns:
// A string that is HTML safe.
String htmlEscape(const String unescaped) {
String result = "";
uint16_t ulen = unescaped.length();
result.reserve(ulen); // The result will be at least the size of input.
for (size_t i = 0; i < ulen; i++) {
char c = unescaped[i];
switch (c) {
// ';!-"<>=&#{}() are all unsafe.
case '\'':
result += F("&apos;");
break;
case ';':
result += F("&semi;");
break;
case '!':
result += F("&excl;");
break;
case '-':
result += F("&dash;");
break;
case '\"':
result += F("&quot;");
break;
case '<':
result += F("&lt;");
break;
case '>':
result += F("&gt;");
break;
case '=':
result += F("&#equals;");
break;
case '&':
result += F("&amp;");
break;
case '#':
result += F("&num;");
break;
case '{':
result += F("&lcub;");
break;
case '}':
result += F("&rcub;");
break;
case '(':
result += F("&lpar;");
break;
case ')':
result += F("&rpar;");
break;
default:
result += c;
}
}
return result;
}
// Convert a protocol type (enum etc) to a human readable string.
// Args:
// protocol: Nr. (enum) of the protocol.
// isRepeat: A flag indicating if it is a repeat message of the protocol.
// Returns:
// A string containing the protocol name.
#ifdef ARDUINO // Arduino's & C++'s string implementations can't co-exist.
String typeToString(const decode_type_t protocol, const bool isRepeat) {
String result = "";
#else
std::string typeToString(const decode_type_t protocol, const bool isRepeat) {
std::string result = "";
#endif
switch (protocol) {
default:
case UNKNOWN:
result = "UNKNOWN";
break;
case UNUSED:
result = "UNUSED";
result = F("UNUSED");
break;
case AIWA_RC_T501:
result = "AIWA_RC_T501";
result = F("AIWA_RC_T501");
break;
case ARGO:
result = "ARGO";
result = F("ARGO");
break;
case CARRIER_AC:
result = "CARRIER_AC";
result = F("CARRIER_AC");
break;
case COOLIX:
result = "COOLIX";
result = F("COOLIX");
break;
case DAIKIN:
result = "DAIKIN";
result = F("DAIKIN");
break;
case DAIKIN160:
result = F("DAIKIN160");
break;
case DAIKIN2:
result = "DAIKIN2";
result = F("DAIKIN2");
break;
case DAIKIN216:
result = F("DAIKIN216");
break;
case DENON:
result = "DENON";
result = F("DENON");
break;
case DISH:
result = "DISH";
result = F("DISH");
break;
case ELECTRA_AC:
result = "ELECTRA_AC";
result = F("ELECTRA_AC");
break;
case FUJITSU_AC:
result = "FUJITSU_AC";
result = F("FUJITSU_AC");
break;
case GICABLE:
result = "GICABLE";
result = F("GICABLE");
break;
case GLOBALCACHE:
result = "GLOBALCACHE";
result = F("GLOBALCACHE");
break;
case GOODWEATHER:
result = F("GOODWEATHER");
break;
case GREE:
result = "GREE";
result = F("GREE");
break;
case HAIER_AC:
result = "HAIER_AC";
result = F("HAIER_AC");
break;
case HAIER_AC_YRW02:
result = "HAIER_AC_YRW02";
result = F("HAIER_AC_YRW02");
break;
case HITACHI_AC:
result = "HITACHI_AC";
result = F("HITACHI_AC");
break;
case HITACHI_AC1:
result = "HITACHI_AC1";
result = F("HITACHI_AC1");
break;
case HITACHI_AC2:
result = "HITACHI_AC2";
result = F("HITACHI_AC2");
break;
case INAX:
result = F("INAX");
break;
case JVC:
result = "JVC";
result = F("JVC");
break;
case KELVINATOR:
result = "KELVINATOR";
result = F("KELVINATOR");
break;
case LEGOPF:
result = F("LEGOPF");
break;
case LG:
result = "LG";
result = F("LG");
break;
case LG2:
result = "LG2";
result = F("LG2");
break;
case LASERTAG:
result = "LASERTAG";
result = F("LASERTAG");
break;
case LUTRON:
result = "LUTRON";
result = F("LUTRON");
break;
case MAGIQUEST:
result = "MAGIQUEST";
result = F("MAGIQUEST");
break;
case MIDEA:
result = "MIDEA";
result = F("MIDEA");
break;
case MITSUBISHI:
result = "MITSUBISHI";
result = F("MITSUBISHI");
break;
case MITSUBISHI2:
result = "MITSUBISHI2";
result = F("MITSUBISHI2");
break;
case MITSUBISHI_AC:
result = "MITSUBISHI_AC";
result = F("MITSUBISHI_AC");
break;
case MITSUBISHI_HEAVY_88:
result = F("MITSUBISHI_HEAVY_88");
break;
case MITSUBISHI_HEAVY_152:
result = F("MITSUBISHI_HEAVY_152");
break;
case MWM:
result = "MWM";
result = F("MWM");
break;
case NEC:
result = "NEC";
result = F("NEC");
break;
case NEC_LIKE:
result = "NEC (non-strict)";
result = F("NEC (non-strict)");
break;
case NIKAI:
result = "NIKAI";
result = F("NIKAI");
break;
case PANASONIC:
result = "PANASONIC";
result = F("PANASONIC");
break;
case PANASONIC_AC:
result = "PANASONIC_AC";
result = F("PANASONIC_AC");
break;
case PIONEER:
result = "PIONEER";
result = F("PIONEER");
break;
case PRONTO:
result = "PRONTO";
result = F("PRONTO");
break;
case RAW:
result = "RAW";
result = F("RAW");
break;
case RC5:
result = "RC5";
result = F("RC5");
break;
case RC5X:
result = "RC5X";
result = F("RC5X");
break;
case RC6:
result = "RC6";
result = F("RC6");
break;
case RCMM:
result = "RCMM";
result = F("RCMM");
break;
case SAMSUNG:
result = "SAMSUNG";
result = F("SAMSUNG");
break;
case SAMSUNG36:
result = "SAMSUNG36";
result = F("SAMSUNG36");
break;
case SAMSUNG_AC:
result = "SAMSUNG_AC";
result = F("SAMSUNG_AC");
break;
case SANYO:
result = "SANYO";
result = F("SANYO");
break;
case SANYO_LC7461:
result = "SANYO_LC7461";
result = F("SANYO_LC7461");
break;
case SHARP:
result = "SHARP";
result = F("SHARP");
break;
case SHARP_AC:
result = F("SHARP_AC");
break;
case SHERWOOD:
result = "SHERWOOD";
result = F("SHERWOOD");
break;
case SONY:
result = "SONY";
result = F("SONY");
break;
case TCL112AC:
result = "TCL112AC";
result = F("TCL112AC");
break;
case TECO:
result = "TECO";
result = F("TECO");
break;
case TOSHIBA_AC:
result = "TOSHIBA_AC";
result = F("TOSHIBA_AC");
break;
case TROTEC:
result = "TROTEC";
result = F("TROTEC");
break;
case VESTEL_AC:
result = "VESTEL_AC";
result = F("VESTEL_AC");
break;
case WHIRLPOOL_AC:
result = "WHIRLPOOL_AC";
result = F("WHIRLPOOL_AC");
break;
case WHYNTER:
result = "WHYNTER";
result = F("WHYNTER");
break;
case UNKNOWN:
default:
result = F("UNKNOWN");
break;
}
if (isRepeat) result += " (Repeat)";
if (isRepeat) result += F(" (Repeat)");
return result;
}
// Does the given protocol use a complex state as part of the decode?
bool hasACState(const decode_type_t protocol) {
switch (protocol) {
case ARGO:
case DAIKIN:
case DAIKIN160:
case DAIKIN2:
case DAIKIN216:
case ELECTRA_AC:
case FUJITSU_AC:
case GREE:
@@ -293,11 +523,15 @@ bool hasACState(const decode_type_t protocol) {
case HITACHI_AC2:
case KELVINATOR:
case MITSUBISHI_AC:
case MITSUBISHI_HEAVY_88:
case MITSUBISHI_HEAVY_152:
case MWM:
case PANASONIC_AC:
case SAMSUNG_AC:
case SHARP_AC:
case TCL112AC:
case TOSHIBA_AC:
case TROTEC:
case WHIRLPOOL_AC:
return true;
default:
@@ -311,7 +545,7 @@ bool hasACState(const decode_type_t protocol) {
// results: A ptr to a decode result.
// Returns:
// A uint16_t containing the length.
uint16_t getCorrectedRawLength(const decode_results *results) {
uint16_t getCorrectedRawLength(const decode_results * const results) {
uint16_t extended_length = results->rawlen - 1;
for (uint16_t i = 0; i < results->rawlen - 1; i++) {
uint32_t usecs = results->rawbuf[i] * kRawTick;
@@ -323,19 +557,16 @@ uint16_t getCorrectedRawLength(const decode_results *results) {
// 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 resultToSourceCode(const decode_results * const results) {
String output = "";
#else
std::string resultToSourceCode(const decode_results *results) {
std::string output = "";
#endif
// Reserve some space for the string to reduce heap fragmentation.
output.reserve(1536); // 1.5KB should cover most cases.
// Start declaration
output += "uint16_t "; // variable type
output += "rawData["; // array name
output += F("uint16_t "); // variable type
output += F("rawData["); // array name
output += uint64ToString(getCorrectedRawLength(results), 10);
// array size
output += "] = {"; // Start declaration
output += F("] = {"); // Start declaration
// Dump data
for (uint16_t i = 1; i < results->rawlen; i++) {
@@ -344,39 +575,42 @@ std::string resultToSourceCode(const decode_results *results) {
usecs -= UINT16_MAX) {
output += uint64ToString(UINT16_MAX);
if (i % 2)
output += ", 0, ";
output += F(", 0, ");
else
output += ", 0, ";
output += F(", 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.
output += F(", "); // ',' not needed on the last one
if (i % 2 == 0) output += ' '; // Extra if it was even.
}
// End declaration
output += "};";
output += F("};");
// Comment
output += " // " + typeToString(results->decode_type, results->repeat);
output += F(" // ");
output += typeToString(results->decode_type, results->repeat);
// Only display the value if the decode type doesn't have an A/C state.
if (!hasACState(results->decode_type))
output += " " + uint64ToString(results->value, 16);
output += "\n";
output += ' ' + uint64ToString(results->value, 16);
output += F("\n");
// Now dump "known" codes
if (results->decode_type != UNKNOWN) {
if (hasACState(results->decode_type)) {
#if DECODE_AC
uint16_t nbytes = results->bits / 8;
output += "uint8_t state[" + uint64ToString(nbytes) + "] = {";
output += F("uint8_t state[");
output += uint64ToString(nbytes);
output += F("] = {");
for (uint16_t i = 0; i < nbytes; i++) {
output += "0x";
if (results->state[i] < 0x10) output += "0";
output += F("0x");
if (results->state[i] < 0x10) output += '0';
output += uint64ToString(results->state[i], 16);
if (i < nbytes - 1) output += ", ";
if (i < nbytes - 1) output += F(", ");
}
output += "};\n";
output += F("};\n");
#endif // DECODE_AC
} else {
// Simple protocols
@@ -384,14 +618,17 @@ std::string resultToSourceCode(const decode_results *results) {
// 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";
output += F("uint32_t address = 0x");
output += uint64ToString(results->address, 16);
output += F(";\n");
output += F("uint32_t command = 0x");
output += uint64ToString(results->command, 16);
output += F(";\n");
}
// Most protocols have data
output +=
"uint64_t data = 0x" + uint64ToString(results->value, 16) + ";\n";
output += F("uint64_t data = 0x");
output += uint64ToString(results->value, 16);
output += F(";\n");
}
}
return output;
@@ -399,46 +636,43 @@ std::string resultToSourceCode(const decode_results *results) {
// Dump out the decode_results structure.
//
#ifdef ARDUINO
String resultToTimingInfo(const decode_results *results) {
String resultToTimingInfo(const decode_results * const 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";
// Reserve some space for the string to reduce heap fragmentation.
output.reserve(2048); // 2KB should cover most cases.
value.reserve(6); // Max value should be 2^17 = 131072
output += F("Raw Timing[");
output += uint64ToString(results->rawlen - 1, 10);
output += F("]:\n");
for (uint16_t i = 1; i < results->rawlen; i++) {
if (i % 2 == 0)
output += "-"; // even
output += '-'; // even
else
output += " +"; // odd
output += F(" +"); // odd
value = uint64ToString(results->rawbuf[i] * kRawTick);
// Space pad the value till it is at least 6 chars long.
while (value.length() < 6) value = " " + value;
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.
if (i < results->rawlen - 1)
output += F(", "); // ',' not needed for last one
if (!(i % 8)) output += '\n'; // Newline every 8 entries.
}
output += "\n";
output += '\n';
return output;
}
// Convert the decode_results structure's value/state to simple hexadecimal.
//
#ifdef ARDUINO
String resultToHexidecimal(const decode_results *result) {
String resultToHexidecimal(const decode_results * const result) {
String output = "";
#else
std::string resultToHexidecimal(const decode_results *result) {
std::string output = "";
#endif
// Reserve some space for the string to reduce heap fragmentation.
output.reserve(2 * kStateSizeMax); // Should cover worst cases.
if (hasACState(result->decode_type)) {
#if DECODE_AC
for (uint16_t i = 0; result->bits > i * 8; i++) {
if (result->state[i] < 0x10) output += "0"; // Zero pad
if (result->state[i] < 0x10) output += '0'; // Zero pad
output += uint64ToString(result->state[i], 16);
}
#endif // DECODE_AC
@@ -450,35 +684,61 @@ std::string resultToHexidecimal(const decode_results *result) {
// Dump out the decode_results structure.
//
#ifdef ARDUINO
String resultToHumanReadableBasic(const decode_results *results) {
String resultToHumanReadableBasic(const decode_results * const results) {
String output = "";
#else
std::string resultToHumanReadableBasic(const decode_results *results) {
std::string output = "";
#endif
// Reserve some space for the string to reduce heap fragmentation.
output.reserve(2 * kStateSizeMax + 50); // Should cover most cases.
// Show Encoding standard
output +=
"Encoding : " + typeToString(results->decode_type, results->repeat) +
"\n";
output += F("Encoding : ");
output += typeToString(results->decode_type, results->repeat);
output += '\n';
// Show Code & length
output += "Code : ";
output += F("Code : ");
output += resultToHexidecimal(results);
output += " (" + uint64ToString(results->bits) + " bits)\n";
output += F(" (");
output += uint64ToString(results->bits);
output += F(" bits)\n");
return output;
}
uint8_t sumBytes(uint8_t *start, const uint16_t length, const uint8_t init) {
// Convert a decode_results into an array suitable for `sendRaw()`.
// Args:
// decode: A pointer to an IR decode_results structure that contains a mesg.
// Returns:
// A pointer to a dynamically allocated uint16_t sendRaw compatible array.
// Note:
// Result needs to be delete[]'ed/free()'ed (deallocated) after use by caller.
uint16_t * resultToRawArray(const decode_results * const decode) {
uint16_t *result = new uint16_t[getCorrectedRawLength(decode)];
if (result != NULL) { // The memory was allocated successfully.
// Convert the decode data.
uint16_t pos = 0;
for (uint16_t i = 1; i < decode->rawlen; i++) {
uint32_t usecs = decode->rawbuf[i] * kRawTick;
while (usecs > UINT16_MAX) { // Keep truncating till it fits.
result[pos++] = UINT16_MAX;
result[pos++] = 0; // A 0 in a sendRaw() array basically means skip.
usecs -= UINT16_MAX;
}
result[pos++] = usecs;
}
}
return result;
}
uint8_t sumBytes(const uint8_t * const start, const uint16_t length,
const uint8_t init) {
uint8_t checksum = init;
uint8_t *ptr;
const uint8_t *ptr;
for (ptr = start; ptr - start < length; ptr++) checksum += *ptr;
return checksum;
}
uint8_t xorBytes(uint8_t *start, const uint16_t length, const uint8_t init) {
uint8_t xorBytes(const uint8_t * const start, const uint16_t length,
const uint8_t init) {
uint8_t checksum = init;
uint8_t *ptr;
const uint8_t *ptr;
for (ptr = start; ptr - start < length; ptr++) checksum ^= *ptr;
return checksum;
}
@@ -491,8 +751,8 @@ uint8_t xorBytes(uint8_t *start, const uint16_t length, const uint8_t init) {
// init: Start the counting from this value.
// Returns:
// Nr. of bits found.
uint16_t countBits(const uint8_t *start, const uint16_t length, const bool ones,
const uint16_t init) {
uint16_t countBits(const uint8_t * const start, const uint16_t length,
const bool ones, const uint16_t init) {
uint16_t count = init;
for (uint16_t offset = 0; offset < length; offset++)
for (uint8_t currentbyte = *(start + offset);
@@ -535,3 +795,7 @@ uint64_t invertBits(const uint64_t data, const uint16_t nbits) {
// Mask off any unwanted bits and return the result.
return (result & ((1ULL << nbits) - 1));
}
float celsiusToFahrenheit(const float deg) { return (deg * 9.0) / 5.0 + 32.0; }
float fahrenheitToCelsius(const float deg) { return (deg - 32.0) * 5.0 / 9.0; }
+15 -19
View File
@@ -15,32 +15,28 @@
#include "IRrecv.h"
uint64_t reverseBits(uint64_t input, uint16_t nbits);
#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);
String resultToHexidecimal(const decode_results *result);
#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);
std::string resultToHexidecimal(const decode_results *result);
#endif
String resultToSourceCode(const decode_results * const results);
String resultToTimingInfo(const decode_results * const results);
String resultToHumanReadableBasic(const decode_results * const results);
String resultToHexidecimal(const decode_results * const result);
String htmlEscape(const String unescaped);
bool hasACState(const decode_type_t protocol);
uint16_t getCorrectedRawLength(const decode_results *results);
uint8_t sumBytes(uint8_t *start, const uint16_t length, const uint8_t init = 0);
uint8_t xorBytes(uint8_t *start, const uint16_t length, const uint8_t init = 0);
uint16_t countBits(const uint8_t *start, const uint16_t length,
uint16_t getCorrectedRawLength(const decode_results * const results);
uint16_t * resultToRawArray(const decode_results * const decode);
uint8_t sumBytes(const uint8_t * const start, const uint16_t length,
const uint8_t init = 0);
uint8_t xorBytes(const uint8_t * const start, const uint16_t length,
const uint8_t init = 0);
uint16_t countBits(const uint8_t * const start, const uint16_t length,
const bool ones = true, const uint16_t init = 0);
uint16_t countBits(const uint64_t data, const uint8_t length,
const bool ones = true, const uint16_t init = 0);
uint64_t invertBits(const uint64_t data, const uint16_t nbits);
decode_type_t strToDecodeType(const char *str);
float celsiusToFahrenheit(const float deg);
float fahrenheitToCelsius(const float deg);
#endif // IRUTILS_H_
+2
View File
@@ -12,6 +12,8 @@
// Based off the RC-T501 RCU
// Added by David Conran. (Inspired by IRremoteESP8266's implementation:
// https://github.com/z3t0/Arduino-IRremote)
// Supports:
// Brand: Aiwa, Model: RC-T501 RCU
const uint16_t kAiwaRcT501PreBits = 26;
const uint16_t kAiwaRcT501PostBits = 1;
+327 -97
View File
@@ -2,10 +2,14 @@
Node MCU/ESP8266 Sketch to emulate Argo Ulisse 13 DCI remote
Controls Argo Ulisse 13 DCI A/C
Copyright 2017 Schmolders
Copyright 2019 crankyoldgit
*/
#include "ir_Argo.h"
#include <algorithm>
#ifndef UNIT_TEST
#include <Arduino.h>
#endif // UNIT_TEST
#include "IRremoteESP8266.h"
#include "IRutils.h"
@@ -16,6 +20,7 @@ const uint16_t kArgoHdrSpace = 3300;
const uint16_t kArgoBitMark = 400;
const uint16_t kArgoOneSpace = 2200;
const uint16_t kArgoZeroSpace = 900;
const uint32_t kArgoGap = kDefaultMessageGap; // Made up value. Complete guess.
#if SEND_ARGO
// Send an Argo A/C message.
@@ -25,7 +30,8 @@ const uint16_t kArgoZeroSpace = 900;
//
// Status: ALPHA / Untested.
void IRsend::sendArgo(unsigned char data[], uint16_t nbytes, uint16_t repeat) {
void IRsend::sendArgo(const unsigned char data[], const uint16_t nbytes,
const uint16_t repeat) {
// Check if we have enough bytes to send a proper message.
if (nbytes < kArgoStateLength) return;
// TODO(kaschmo): validate
@@ -35,26 +41,31 @@ void IRsend::sendArgo(unsigned char data[], uint16_t nbytes, uint16_t repeat) {
}
#endif // SEND_ARGO
IRArgoAC::IRArgoAC(uint16_t pin) : _irsend(pin) { stateReset(); }
IRArgoAC::IRArgoAC(const uint16_t pin) : _irsend(pin) { this->stateReset(); }
void IRArgoAC::begin() { _irsend.begin(); }
void IRArgoAC::begin(void) { _irsend.begin(); }
#if SEND_ARGO
void IRArgoAC::send(const uint16_t repeat) {
checksum(); // Create valid checksum before sending
this->checksum(); // Create valid checksum before sending
_irsend.sendArgo(argo, kArgoStateLength, repeat);
}
#endif // SEND_ARGO
void IRArgoAC::checksum() {
uint8_t sum = 2; // Corresponds to byte 11 being constant 0b01
uint8_t i;
uint8_t IRArgoAC::calcChecksum(const uint8_t state[], const uint16_t length) {
// Corresponds to byte 11 being constant 0b01
// Only add up bytes to 9. byte 10 is 0b01 constant anyway.
// Assume that argo array is MSB first (left)
for (i = 0; i < 10; i++) sum += argo[i];
return sumBytes(state, length - 2, 2);
}
sum = sum % 256; // modulo 256
bool IRArgoAC::validChecksum(const uint8_t state[], const uint16_t length) {
return ((state[length - 2] >> 2) + (state[length - 1] << 6)) ==
IRArgoAC::calcChecksum(state, length);
}
void IRArgoAC::checksum(void) {
uint8_t sum = IRArgoAC::calcChecksum(argo, kArgoStateLength);
// Append sum to end of array
// Set const part of checksum bit 10
argo[10] = 0b00000010;
@@ -62,7 +73,7 @@ void IRArgoAC::checksum() {
argo[11] = sum >> 6; // Shift down 6 bits and add in two LSBs of bit 11
}
void IRArgoAC::stateReset() {
void IRArgoAC::stateReset(void) {
for (uint8_t i = 0; i < kArgoStateLength; i++) argo[i] = 0x0;
// Argo Message. Store MSB left.
@@ -76,155 +87,374 @@ void IRArgoAC::stateReset() {
this->off();
this->setTemp(20);
this->setRoomTemp(25);
this->setCoolMode(kArgoCoolAuto);
this->setMode(kArgoAuto);
this->setFan(kArgoFanAuto);
}
uint8_t* IRArgoAC::getRaw() {
checksum(); // Ensure correct bit array before returning
uint8_t* IRArgoAC::getRaw(void) {
this->checksum(); // Ensure correct bit array before returning
return argo;
}
void IRArgoAC::on() {
// state = ON;
ac_state = 1;
void IRArgoAC::setRaw(const uint8_t state[]) {
for (uint8_t i = 0; i < kArgoStateLength; i++) argo[i] = state[i];
}
void IRArgoAC::on(void) {
// Bit 5 of byte 9 is on/off
// in MSB first
argo[9] = argo[9] | 0b00100000; // Set ON/OFF bit to 1
argo[9]|= kArgoPowerBit; // Set ON/OFF bit to 1
}
void IRArgoAC::off() {
// state = OFF;
ac_state = 0;
void IRArgoAC::off(void) {
// in MSB first
// bit 5 of byte 9 to off
argo[9] = argo[9] & 0b11011111; // Set on/off bit to 0
argo[9] &= ~kArgoPowerBit; // Set on/off bit to 0
}
void IRArgoAC::setPower(bool state) {
if (state)
on();
void IRArgoAC::setPower(const bool on) {
if (on)
this->on();
else
off();
this->off();
}
uint8_t IRArgoAC::getPower() { return ac_state; }
bool IRArgoAC::getPower(void) { return argo[9] & kArgoPowerBit; }
void IRArgoAC::setMax(bool state) {
max_mode = state;
if (max_mode)
argo[9] |= 0b00001000;
void IRArgoAC::setMax(const bool on) {
if (on)
argo[9] |= kArgoMaxBit;
else
argo[9] &= 0b11110111;
argo[9] &= ~kArgoMaxBit;
}
bool IRArgoAC::getMax() { return max_mode; }
bool IRArgoAC::getMax(void) { return argo[9] & kArgoMaxBit; }
// Set the temp in deg C
// Sending 0 equals +4
void IRArgoAC::setTemp(uint8_t temp) {
if (temp < kArgoMinTemp)
temp = kArgoMinTemp;
else if (temp > kArgoMaxTemp)
temp = kArgoMaxTemp;
void IRArgoAC::setTemp(const uint8_t degrees) {
uint8_t temp = std::max(kArgoMinTemp, degrees);
temp = std::min(kArgoMaxTemp, temp);
// Store in attributes
set_temp = temp;
// offset 4 degrees. "If I want 12 degrees, I need to send 8"
temp -= 4;
temp -= kArgoTempOffset;
// Settemp = Bit 6,7 of byte 2, and bit 0-2 of byte 3
// mask out bits
// argo[13] & 0x00000100; // mask out ON/OFF Bit
argo[2] &= 0b00111111;
argo[3] &= 0b11111000;
argo[2] &= ~kArgoTempLowMask;
argo[3] &= ~kArgoTempHighMask;
argo[2] += temp << 6; // append to bit 6,7
argo[3] += temp >> 2; // remove lowest to bits and append in 0-2
// append to bit 6,7
argo[2] += temp << 6;
// remove lowest to bits and append in 0-2
argo[3] += ((temp >> 2) & kArgoTempHighMask);
}
uint8_t IRArgoAC::getTemp() { return set_temp; }
uint8_t IRArgoAC::getTemp(void) {
return (((argo[3] & kArgoTempHighMask) << 2 ) | (argo[2] >> 6)) +
kArgoTempOffset;
}
// Set the speed of the fan
void IRArgoAC::setFan(uint8_t fan) {
// Set the fan speed bits, leave low 4 bits alone
fan_mode = fan;
void IRArgoAC::setFan(const uint8_t fan) {
// Mask out bits
argo[3] &= 0b11100111;
argo[3] &= ~kArgoFanMask;
// Set fan mode at bit positions
argo[3] += fan << 3;
argo[3] |= (std::min(fan, kArgoFan3) << 3);
}
uint8_t IRArgoAC::getFan() { return fan_mode; }
uint8_t IRArgoAC::getFan(void) { return (argo[3] & kArgoFanMask) >> 3; }
void IRArgoAC::setFlap(uint8_t flap) {
void IRArgoAC::setFlap(const uint8_t flap) {
flap_mode = flap;
// TODO(kaschmo): set correct bits for flap mode
}
uint8_t IRArgoAC::getFlap() { return flap_mode; }
uint8_t IRArgoAC::getFlap(void) { return flap_mode; }
uint8_t IRArgoAC::getMode() {
// return cooling 0, heating 1
return ac_mode;
uint8_t IRArgoAC::getMode(void) {
return (argo[2] & kArgoModeMask) >> 3;
}
void IRArgoAC::setCoolMode(uint8_t mode) {
ac_mode = 0; // Set ac mode to cooling
cool_mode = mode;
// Mask out bits, also leave bit 5 on 0 for cooling
argo[2] &= 0b11000111;
// Set cool mode at bit positions
argo[2] += mode << 3;
void IRArgoAC::setMode(const uint8_t mode) {
switch (mode) {
case kArgoCool:
case kArgoDry:
case kArgoAuto:
case kArgoOff:
case kArgoHeat:
case kArgoHeatAuto:
// Mask out bits
argo[2] &= ~kArgoModeMask;
// Set the mode at bit positions
argo[2] |= ((mode << 3) & kArgoModeMask);
return;
default:
this->setMode(kArgoAuto);
}
}
uint8_t IRArgoAC::getCoolMode() { return cool_mode; }
void IRArgoAC::setHeatMode(uint8_t mode) {
ac_mode = 1; // Set ac mode to heating
heat_mode = mode;
// Mask out bits
argo[2] &= 0b11000111;
// Set heating bit
argo[2] |= 0b00100000;
// Set cool mode at bit positions
argo[2] += mode << 3;
}
uint8_t IRArgoAC::getHeatMode() { return heat_mode; }
void IRArgoAC::setNight(bool state) {
night_mode = state;
if (night_mode)
void IRArgoAC::setNight(const bool on) {
if (on)
// Set bit at night position: bit 2
argo[9] |= 0b00000100;
argo[9] |= kArgoNightBit;
else
argo[9] &= 0b11111011;
argo[9] &= ~kArgoNightBit;
}
bool IRArgoAC::getNight() { return night_mode; }
bool IRArgoAC::getNight(void) { return argo[9] & kArgoNightBit; }
void IRArgoAC::setiFeel(bool state) {
ifeel_mode = state;
if (ifeel_mode)
void IRArgoAC::setiFeel(const bool on) {
if (on)
// Set bit at iFeel position: bit 7
argo[9] |= 0b10000000;
argo[9] |= kArgoIFeelBit;
else
argo[9] &= 0b01111111;
argo[9] &= ~kArgoIFeelBit;
}
bool IRArgoAC::getiFeel() { return ifeel_mode; }
bool IRArgoAC::getiFeel(void) { return argo[9] & kArgoIFeelBit; }
void IRArgoAC::setTime() {
void IRArgoAC::setTime(void) {
// TODO(kaschmo): use function call from checksum to set time first
}
void IRArgoAC::setRoomTemp(uint8_t temp) {
temp -= 4;
void IRArgoAC::setRoomTemp(const uint8_t degrees) {
uint8_t temp = std::min(degrees, kArgoMaxRoomTemp);
temp = std::max(temp, kArgoTempOffset);
temp -= kArgoTempOffset;;
// Mask out bits
argo[3] &= 0b00011111;
argo[4] &= 0b11111100;
argo[3] &= ~kArgoRoomTempLowMask;
argo[4] &= ~kArgoRoomTempHighMask;
argo[3] += temp << 5; // Append to bit 5,6,7
argo[4] += temp >> 3; // Remove lowest 3 bits and append in 0,1
}
uint8_t IRArgoAC::getRoomTemp(void) {
return ((argo[4] & kArgoRoomTempHighMask) << 3 | (argo[3] >> 5)) +
kArgoTempOffset;
}
// Convert a standard A/C mode into its native mode.
uint8_t IRArgoAC::convertMode(const stdAc::opmode_t mode) {
switch (mode) {
case stdAc::opmode_t::kCool:
return kArgoCool;
case stdAc::opmode_t::kHeat:
return kArgoHeat;
case stdAc::opmode_t::kDry:
return kArgoDry;
case stdAc::opmode_t::kOff:
return kArgoOff;
// No fan mode.
default:
return kArgoAuto;
}
}
// Convert a standard A/C Fan speed into its native fan speed.
uint8_t IRArgoAC::convertFan(const stdAc::fanspeed_t speed) {
switch (speed) {
case stdAc::fanspeed_t::kMin:
case stdAc::fanspeed_t::kLow:
return kArgoFan1;
case stdAc::fanspeed_t::kMedium:
return kArgoFan2;
case stdAc::fanspeed_t::kHigh:
case stdAc::fanspeed_t::kMax:
return kArgoFan3;
default:
return kArgoFanAuto;
}
}
// Convert a standard A/C Fan speed into its native fan speed.
uint8_t IRArgoAC::convertSwingV(const stdAc::swingv_t position) {
switch (position) {
case stdAc::swingv_t::kHighest:
return kArgoFlapFull;
case stdAc::swingv_t::kHigh:
return kArgoFlap5;
case stdAc::swingv_t::kMiddle:
return kArgoFlap4;
case stdAc::swingv_t::kLow:
return kArgoFlap3;
case stdAc::swingv_t::kLowest:
return kArgoFlap1;
default:
return kArgoFlapAuto;
}
}
// Convert a native mode to it's common equivalent.
stdAc::opmode_t IRArgoAC::toCommonMode(const uint8_t mode) {
switch (mode) {
case kArgoCool: return stdAc::opmode_t::kCool;
case kArgoHeat: return stdAc::opmode_t::kHeat;
case kArgoDry: return stdAc::opmode_t::kDry;
// No fan mode.
default: return stdAc::opmode_t::kAuto;
}
}
// Convert a native fan speed to it's common equivalent.
stdAc::fanspeed_t IRArgoAC::toCommonFanSpeed(const uint8_t speed) {
switch (speed) {
case kArgoFan3: return stdAc::fanspeed_t::kMax;
case kArgoFan2: return stdAc::fanspeed_t::kMedium;
case kArgoFan1: return stdAc::fanspeed_t::kMin;
default: return stdAc::fanspeed_t::kAuto;
}
}
// Convert the A/C state to it's common equivalent.
stdAc::state_t IRArgoAC::toCommon(void) {
stdAc::state_t result;
result.protocol = decode_type_t::ARGO;
result.power = this->getPower();
result.mode = this->toCommonMode(this->getMode());
result.celsius = true;
result.degrees = this->getTemp();
result.fanspeed = this->toCommonFanSpeed(this->getFan());
result.turbo = this->getMax();
result.sleep = this->getNight() ? 0 : -1;
// Not supported.
result.model = -1; // Not supported.
result.swingv = stdAc::swingv_t::kOff;
result.swingh = stdAc::swingh_t::kOff;
result.light = false;
result.filter = false;
result.econo = false;
result.quiet = false;
result.clean = false;
result.beep = false;
result.clock = -1;
return result;
}
// Convert the internal state into a human readable string.
String IRArgoAC::toString() {
String result = "";
result.reserve(100); // Reserve some heap for the string to reduce fragging.
result += F("Power: ");
result += getPower() ? F("On") : F("Off");
result += F(", Mode: ");
result += uint64ToString(getMode());
switch (getMode()) {
case kArgoAuto:
result += F(" (AUTO)");
break;
case kArgoCool:
result += F(" (COOL)");
break;
case kArgoHeat:
result += F(" (HEAT)");
break;
case kArgoDry:
result += F(" (DRY)");
break;
case kArgoHeatAuto:
result += F(" (HEATAUTO)");
break;
case kArgoOff:
result += F(" (OFF)");
break;
default:
result += F(" (UNKNOWN)");
}
result += F(", Fan: ");
result += uint64ToString(getFan());
switch (getFan()) {
case kArgoFanAuto:
result += F(" (AUTO)");
break;
case kArgoFan3:
result += F(" (MAX)");
break;
case kArgoFan1:
result += F(" (MIN)");
break;
case kArgoFan2:
result += F(" (MED)");
break;
default:
result += F(" (UNKNOWN)");
}
result += F(", Temp: ");
result += uint64ToString(getTemp());
result += 'C';
result += F(", Room Temp: ");
result += uint64ToString(getRoomTemp());
result += 'C';
result += F(", Max: ");
result += getMax() ? F("On") : F("Off");
result += F(", iFeel: ");
result += getiFeel() ? F("On") : F("Off");
result += F(", Night: ");
result += getNight() ? F("On") : F("Off");
return result;
}
#if DECODE_ARGO
// Decode the supplied Argo 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 kArgoBits.
// strict: Flag indicating if we should perform strict matching.
// Returns:
// boolean: True if it can decode it, false if it can't.
//
// Status: ALPHA / Probably doesn't work.
//
// Note:
// This decoder is based soley off sendArgo(). We have no actual captures
// to test this against. If you have one of these units, please let us know.
bool IRrecv::decodeArgo(decode_results *results, const uint16_t nbits,
const bool strict) {
if (results->rawlen < 2 * nbits + kHeader + kFooter - 1)
return false; // Can't possibly be a valid Samsung A/C message.
if (strict && nbits != kArgoBits) return false;
uint16_t offset = kStartOffset;
uint16_t dataBitsSoFar = 0;
match_result_t data_result;
// Message Header
if (!matchMark(results->rawbuf[offset++], kArgoHdrMark)) return false;
if (!matchSpace(results->rawbuf[offset++], kArgoHdrSpace)) return false;
// Data
// Keep reading bytes until we either run out of data 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, kArgoBitMark,
kArgoOneSpace, kArgoBitMark,
kArgoZeroSpace, kTolerance, 0, false);
if (data_result.success == false) {
DPRINT("DEBUG: offset = ");
DPRINTLN(offset + data_result.used);
return false; // Fail
}
results->state[i] = data_result.data;
DPRINT("DEBUG: Matched state[");
DPRINT(i);
DPRINTLN("]");
}
// Footer (None, allegedly. This seems very wrong.)
// Compliance
// Re-check we got the correct size/length due to the way we read the data.
if (dataBitsSoFar != nbits) return false;
// Verify we got a valid checksum.
if (strict && !IRArgoAC::validChecksum(results->state)) return false;
// Success
results->decode_type = decode_type_t::ARGO;
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_ARGO
+89 -49
View File
@@ -1,11 +1,21 @@
/* Copyright 2017 Schmolders
// Copyright 2017 Schmolders
// Adds support for Argo Ulisse 13 DCI Mobile Split ACs.
*/
// Supports:
// Brand: Argo, Model: Ulisse 13 DCI Mobile Split A/C
#ifndef IR_ARGO_H_
#define IR_ARGO_H_
#ifndef UNIT_TEST
#include <Arduino.h>
#endif
#include "IRremoteESP8266.h"
#include "IRsend.h"
#ifdef UNIT_TEST
#include "IRsend_test.h"
#endif
// ARGO Ulisse DCI
@@ -29,19 +39,42 @@
// Constants. Store MSB left.
const uint8_t kArgoCoolOn = 0; // 0b000
const uint8_t kArgoCoolOff = 3; // 0b110
const uint8_t kArgoCoolAuto = 2; // 0b010
const uint8_t kArgoCoolHum = 1; // 0b100
const uint8_t kArgoHeatOn = 0; // 0b001
const uint8_t kArgoHeatAuto = 1; // 0b101
const uint8_t kArgoHeatBlink = 2; // 0b011 // ??no idea what mode that is
const uint8_t kArgoMinTemp = 10; // Celsius offset +4
const uint8_t kArgoMaxTemp = 32; // Celsius
// byte[2]
const uint8_t kArgoHeatBit = 0b00100000;
const uint8_t kArgoModeMask = 0b00111000;
const uint8_t kArgoTempLowMask = 0b11000000;
const uint8_t kArgoCool = 0b000; // 0b000 (LSB) 0
const uint8_t kArgoDry = 0b001; // 0b100 (LSB) 1
const uint8_t kArgoAuto = 0b010; // 0b010 (LSB) 2
const uint8_t kArgoOff = 0b011; // 0b110 (LSB) 3
const uint8_t kArgoHeat = 0b100; // 0b001 (LSB) 4
const uint8_t kArgoHeatAuto = 0b101; // 0b101 (LSB) 5
// ?no idea what mode that is
const uint8_t kArgoHeatBlink = 0b110; // 0b011 (LSB) 6
// byte[3]
const uint8_t kArgoTempHighMask = 0b00000111;
const uint8_t kArgoFanMask = 0b00011000;
const uint8_t kArgoRoomTempLowMask = 0b11100000;
const uint8_t kArgoFanAuto = 0; // 0b00
const uint8_t kArgoFan3 = 3; // 0b11
const uint8_t kArgoFan2 = 2; // 0b01
const uint8_t kArgoFan1 = 1; // 0b10
// byte[4]
const uint8_t kArgoRoomTempHighMask = 0b00000011;
const uint8_t kArgoTempOffset = 4;
const uint8_t kArgoMaxRoomTemp = ((1 << 5) - 1) + kArgoTempOffset; // 35C
// byte[9]
const uint8_t kArgoPowerBit = 0b00100000;
const uint8_t kArgoMaxBit = 0b00001000;
const uint8_t kArgoNightBit = 0b00000100;
const uint8_t kArgoIFeelBit = 0b10000000;
const uint8_t kArgoMinTemp = 10; // Celsius offset +4
const uint8_t kArgoMaxTemp = 32; // Celsius
const uint8_t kArgoFlapAuto = 0; // 0b000
const uint8_t kArgoFlap1 = 1; // 0b100
const uint8_t kArgoFlap2 = 2; // 0b010
@@ -55,7 +88,7 @@ const uint8_t kArgoFlapFull = 7; // 0b111
#define ARGO_COOL_ON kArgoCoolOn
#define ARGO_COOL_OFF kArgoCoolOff
#define ARGO_COOL_AUTO kArgoCoolAuto
#define ARGO_COOl_HUM kArgoCoolHum
#define ARGO_COOL_HUM kArgoCoolHum
#define ARGO_HEAT_ON kArgoHeatOn
#define ARGO_HEAT_AUTO kArgoHeatAuto
#define ARGO_HEAT_BLINK kArgoHeatBlink
@@ -77,66 +110,73 @@ const uint8_t kArgoFlapFull = 7; // 0b111
class IRArgoAC {
public:
explicit IRArgoAC(uint16_t pin);
explicit IRArgoAC(const uint16_t pin);
#if SEND_ARGO
void send(const uint16_t repeat = kArgoDefaultRepeat);
uint8_t calibrate(void) { return _irsend.calibrate(); }
#endif // SEND_ARGO
void begin();
void on();
void off();
void begin(void);
void on(void);
void off(void);
void setPower(bool state);
uint8_t getPower();
void setPower(const bool on);
bool getPower(void);
void setTemp(uint8_t temp);
uint8_t getTemp();
void setTemp(const uint8_t degrees);
uint8_t getTemp(void);
void setFan(uint8_t fan);
uint8_t getFan();
void setFan(const uint8_t fan);
uint8_t getFan(void);
void setFlap(uint8_t flap);
uint8_t getFlap();
void setFlap(const uint8_t flap);
uint8_t getFlap(void);
void setCoolMode(uint8_t mode);
uint8_t getCoolMode();
void setMode(const uint8_t mode);
uint8_t getMode(void);
void setHeatMode(uint8_t mode);
uint8_t getHeatMode();
uint8_t getMode();
void setMax(const bool on);
bool getMax(void);
void setMax(bool state);
bool getMax();
void setNight(const bool on);
bool getNight(void);
void setNight(bool state);
bool getNight();
void setiFeel(const bool on);
bool getiFeel(void);
void setiFeel(bool state);
bool getiFeel();
void setTime(void);
void setRoomTemp(const uint8_t degrees);
uint8_t getRoomTemp(void);
void setTime();
void setRoomTemp(uint8_t temp);
uint8_t* getRaw();
uint8_t* getRaw(void);
void setRaw(const uint8_t state[]);
static uint8_t calcChecksum(const uint8_t state[],
const uint16_t length = kArgoStateLength);
static bool validChecksum(const uint8_t state[],
const uint16_t length = kArgoStateLength);
static uint8_t convertMode(const stdAc::opmode_t mode);
static uint8_t convertFan(const stdAc::fanspeed_t speed);
static uint8_t convertSwingV(const stdAc::swingv_t position);
static stdAc::opmode_t toCommonMode(const uint8_t mode);
static stdAc::fanspeed_t toCommonFanSpeed(const uint8_t speed);
stdAc::state_t toCommon(void);
String toString();
#ifndef UNIT_TEST
private:
IRsend _irsend; // instance of the IR send class
#else
IRsendTest _irsend; // instance of the testing IR send class
#endif
// # of bytes per command
uint8_t argo[kArgoStateLength]; // Defined in IRremoteESP8266.h
void stateReset();
void checksum();
IRsend _irsend; // instance of the IR send class
void stateReset(void);
void checksum(void);
// Attributes
uint8_t set_temp;
uint8_t fan_mode;
uint8_t flap_mode;
uint8_t ac_state;
uint8_t ac_mode; // heat 1, cool 0
uint8_t heat_mode;
uint8_t cool_mode;
uint8_t night_mode; // on/off
uint8_t max_mode; // on/off
uint8_t ifeel_mode; // on/off
};
#endif // IR_ARGO_H_
+8 -11
View File
@@ -1,20 +1,17 @@
// Copyright 2018 David Conran
// Supports:
// Brand: Carrier/Surrey, Model: 42QG5A55970 remote
// Brand: Carrier/Surrey, Model: 619EGX0090E0 A/C
// Brand: Carrier/Surrey, Model: 619EGX0120E0 A/C
// Brand: Carrier/Surrey, Model: 619EGX0180E0 A/C
// Brand: Carrier/Surrey, Model: 619EGX0220E0 A/C
// Brand: Carrier/Surrey, Model: 53NGK009/012 Inverter
#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
+175 -52
View File
@@ -1,5 +1,5 @@
// Copyright bakrus
// Copyright 2017 David Conran
// Copyright 2017,2019 David Conran
#include "ir_Coolix.h"
#include <algorithm>
@@ -19,7 +19,11 @@
// Coolix A/C / heatpump added by (send) bakrus & (decode) crankyoldgit
//
// Supports:
// RG57K7(B)/BGEF remote control for Beko BINR 070/071 split-type aircon.
// Brand: Beko, Model: RG57K7(B)/BGEF Remote
// Brand: Beko, Model: BINR 070/071 split-type A/C
// Brand: Midea, Model: RG52D/BGE Remote
// Brand: Midea, Model: MS12FU-10HRDN1-QRD0GW(B) A/C
// Brand: Midea, Model: MSABAU-07HRFN1-QRD0GW A/C (circa 2016)
// Ref:
// https://github.com/markszabo/IRremoteESP8266/issues/484
@@ -200,6 +204,14 @@ void IRCoolixAC::setPower(const bool power) {
}
}
void IRCoolixAC::on(void) {
this->setPower(true);
}
void IRCoolixAC::off(void) {
this->setPower(false);
}
bool IRCoolixAC::getSwing() { return remote_state == kCoolixSwing; }
void IRCoolixAC::setSwing() {
@@ -260,18 +272,30 @@ void IRCoolixAC::clearSensorTemp() {
void IRCoolixAC::setMode(const uint8_t mode) {
uint32_t actualmode = mode;
switch (actualmode) {
case kCoolixAuto:
case kCoolixDry:
if (this->getFan() == kCoolixFanAuto)
// No kCoolixFanAuto in Dry/Auto mode.
this->setFan(kCoolixFanAuto0, false);
break;
case kCoolixCool:
case kCoolixHeat:
case kCoolixFan:
if (this->getFan() == kCoolixFanAuto0)
// kCoolixFanAuto0 only in Dry/Auto mode.
this->setFan(kCoolixFanAuto, false);
break;
default: // Anything else, go with Auto mode.
this->setMode(kCoolixAuto);
return;
}
// Fan mode is a special case of Dry.
if (mode == kCoolixFan) actualmode = kCoolixDry;
switch (actualmode) {
case kCoolixCool:
case kCoolixAuto:
case kCoolixHeat:
case kCoolixDry:
recoverSavedState();
remote_state = (remote_state & ~kCoolixModeMask) | (actualmode << 2);
// Force the temp into a known-good state.
setTemp(getTemp());
}
recoverSavedState();
remote_state = (remote_state & ~kCoolixModeMask) | (actualmode << 2);
// Force the temp into a known-good state.
setTemp(getTemp());
if (mode == kCoolixFan) setTempRaw(kCoolixFanTempCode);
}
@@ -286,15 +310,33 @@ uint8_t IRCoolixAC::getFan() {
return (getNormalState() & kCoolixFanMask) >> 13;
}
void IRCoolixAC::setFan(const uint8_t speed) {
void IRCoolixAC::setFan(const uint8_t speed, const bool modecheck) {
recoverSavedState();
uint8_t newspeed = speed;
switch (speed) {
case kCoolixFanAuto: // Dry & Auto mode can't have this speed.
if (modecheck) {
switch (this->getMode()) {
case kCoolixAuto:
case kCoolixDry:
newspeed = kCoolixFanAuto0;
}
}
break;
case kCoolixFanAuto0: // Only Dry & Auto mode can have this speed.
if (modecheck) {
switch (this->getMode()) {
case kCoolixAuto:
case kCoolixDry:
break;
default:
newspeed = kCoolixFanAuto;
}
}
break;
case kCoolixFanMin:
case kCoolixFanMed:
case kCoolixFanMax:
case kCoolixFanAuto:
case kCoolixFanAuto0:
case kCoolixFanZoneFollow:
case kCoolixFanFixed:
break;
@@ -305,100 +347,181 @@ void IRCoolixAC::setFan(const uint8_t speed) {
remote_state |= ((newspeed << 13) & kCoolixFanMask);
}
// Convert a standard A/C mode into its native mode.
uint8_t IRCoolixAC::convertMode(const stdAc::opmode_t mode) {
switch (mode) {
case stdAc::opmode_t::kCool:
return kCoolixCool;
case stdAc::opmode_t::kHeat:
return kCoolixHeat;
case stdAc::opmode_t::kDry:
return kCoolixDry;
case stdAc::opmode_t::kFan:
return kCoolixFan;
default:
return kCoolixAuto;
}
}
// Convert a standard A/C Fan speed into its native fan speed.
uint8_t IRCoolixAC::convertFan(const stdAc::fanspeed_t speed) {
switch (speed) {
case stdAc::fanspeed_t::kMin:
case stdAc::fanspeed_t::kLow:
return kCoolixFanMin;
case stdAc::fanspeed_t::kMedium:
return kCoolixFanMed;
case stdAc::fanspeed_t::kHigh:
case stdAc::fanspeed_t::kMax:
return kCoolixFanMax;
default:
return kCoolixFanAuto;
}
}
// Convert a native mode to it's common equivalent.
stdAc::opmode_t IRCoolixAC::toCommonMode(const uint8_t mode) {
switch (mode) {
case kCoolixCool: return stdAc::opmode_t::kCool;
case kCoolixHeat: return stdAc::opmode_t::kHeat;
case kCoolixDry: return stdAc::opmode_t::kDry;
case kCoolixFan: return stdAc::opmode_t::kFan;
default: return stdAc::opmode_t::kAuto;
}
}
// Convert a native fan speed to it's common equivalent.
stdAc::fanspeed_t IRCoolixAC::toCommonFanSpeed(const uint8_t speed) {
switch (speed) {
case kCoolixFanMax: return stdAc::fanspeed_t::kMax;
case kCoolixFanMed: return stdAc::fanspeed_t::kMedium;
case kCoolixFanMin: return stdAc::fanspeed_t::kMin;
default: return stdAc::fanspeed_t::kAuto;
}
}
// Convert the A/C state to it's common equivalent.
stdAc::state_t IRCoolixAC::toCommon(void) {
stdAc::state_t result;
result.protocol = decode_type_t::COOLIX;
result.model = -1; // No models used.
result.power = this->getPower();
result.mode = this->toCommonMode(this->getMode());
result.celsius = true;
result.degrees = this->getTemp();
result.fanspeed = this->toCommonFanSpeed(this->getFan());
result.swingv = this->getSwing() ? stdAc::swingv_t::kAuto :
stdAc::swingv_t::kOff;
result.swingh = this->getSwing() ? stdAc::swingh_t::kAuto :
stdAc::swingh_t::kOff;
result.turbo = this->getTurbo();
result.sleep = this->getSleep() ? 0 : -1;
result.light = this->getLed();
result.clean = this->getClean();
// Not supported.
result.quiet = false;
result.econo = false;
result.filter = false;
result.beep = false;
result.clock = -1;
return result;
}
// Convert the internal state into a human readable string.
#ifdef ARDUINO
String IRCoolixAC::toString() {
String result = "";
#else
std::string IRCoolixAC::toString() {
std::string result = "";
#endif // ARDUINO
result += "Power: ";
result.reserve(100); // Reserve some heap for the string to reduce fragging.
result += F("Power: ");
if (getPower()) {
result += "On";
result += F("On");
} else {
result += "Off";
result += F("Off");
return result; // If it's off, there is no other info.
}
// Special modes.
if (getSwing()) {
result += ", Swing: Toggle";
result += F(", Swing: Toggle");
return result;
}
if (getSleep()) {
result += ", Sleep: Toggle";
result += F(", Sleep: Toggle");
return result;
}
if (getTurbo()) {
result += ", Turbo: Toggle";
result += F(", Turbo: Toggle");
return result;
}
if (getLed()) {
result += ", Led: Toggle";
result += F(", Led: Toggle");
return result;
}
if (getClean()) {
result += ", Clean: Toggle";
result += F(", Clean: Toggle");
return result;
}
result += ", Mode: " + uint64ToString(getMode());
result += F(", Mode: ");
result += uint64ToString(getMode());
switch (getMode()) {
case kCoolixAuto:
result += " (AUTO)";
result += F(" (AUTO)");
break;
case kCoolixCool:
result += " (COOL)";
result += F(" (COOL)");
break;
case kCoolixHeat:
result += " (HEAT)";
result += F(" (HEAT)");
break;
case kCoolixDry:
result += " (DRY)";
result += F(" (DRY)");
break;
case kCoolixFan:
result += " (FAN)";
result += F(" (FAN)");
break;
default:
result += " (UNKNOWN)";
result += F(" (UNKNOWN)");
}
result += ", Fan: " + uint64ToString(getFan());
result += F(", Fan: ");
result += uint64ToString(getFan());
switch (getFan()) {
case kCoolixFanAuto:
result += " (AUTO)";
result += F(" (AUTO)");
break;
case kCoolixFanAuto0:
result += " (AUTO0)";
result += F(" (AUTO0)");
break;
case kCoolixFanMax:
result += " (MAX)";
result += F(" (MAX)");
break;
case kCoolixFanMin:
result += " (MIN)";
result += F(" (MIN)");
break;
case kCoolixFanMed:
result += " (MED)";
result += F(" (MED)");
break;
case kCoolixFanZoneFollow:
result += " (ZONEFOLLOW)";
result += F(" (ZONEFOLLOW)");
break;
case kCoolixFanFixed:
result += " (FIXED)";
result += F(" (FIXED)");
break;
default:
result += " (UNKNOWN)";
result += F(" (UNKNOWN)");
}
if (getMode() != kCoolixFan) // Fan mode doesn't have a temperature.
result += ", Temp: " + uint64ToString(getTemp()) + "C";
result += ", Zone Follow: ";
if (getMode() != kCoolixFan) { // Fan mode doesn't have a temperature.
result += F(", Temp: ");
result += uint64ToString(getTemp());
result += 'C';
}
result += F(", Zone Follow: ");
if (getZoneFollow())
result += "On";
result += F("On");
else
result += "Off";
result += ", Sensor Temp: ";
result += F("Off");
result += F(", Sensor Temp: ");
if (getSensorTemp() > kCoolixSensorTempMax)
result += "Ignored";
result += F("Ignored");
else
result += uint64ToString(getSensorTemp()) + "C";
result += uint64ToString(getSensorTemp()) + F("C");
return result;
}
+25 -15
View File
@@ -9,11 +9,12 @@
#include <stdint.h>
#ifndef UNIT_TEST
#include <Arduino.h>
#else
#include <string>
#endif
#include "IRremoteESP8266.h"
#include "IRsend.h"
#ifdef UNIT_TEST
#include "IRsend_test.h"
#endif
// CCCCC OOOOO OOOOO LL IIIII XX XX
// CC C OO OO OO OO LL III XX XX
@@ -22,7 +23,11 @@
// CCCCC OOOO0 OOOO0 LLLLLLL IIIII XX XX
// Supports:
// RG57K7(B)/BGEF remote control for Beko BINR 070/071 split-type aircon.
// Brand: Beko, Model: RG57K7(B)/BGEF Remote
// Brand: Beko, Model: BINR 070/071 split-type A/C
// Brand: Midea, Model: RG52D/BGE Remote
// Brand: Midea, Model: MS12FU-10HRDN1-QRD0GW(B) A/C
// Brand: Midea, Model: MSABAU-07HRFN1-QRD0GW A/C (circa 2016)
// Ref:
// https://github.com/markszabo/IRremoteESP8266/issues/484
// Kudos:
@@ -30,11 +35,11 @@
// Constants
// Modes
const uint8_t kCoolixCool = 0b00;
const uint8_t kCoolixDry = 0b01;
const uint8_t kCoolixAuto = 0b10;
const uint8_t kCoolixHeat = 0b11;
const uint8_t kCoolixFan = 4; // Synthetic.
const uint8_t kCoolixCool = 0b000;
const uint8_t kCoolixDry = 0b001;
const uint8_t kCoolixAuto = 0b010;
const uint8_t kCoolixHeat = 0b011;
const uint8_t kCoolixFan = 0b100; // Synthetic.
const uint32_t kCoolixModeMask = 0b000000000000000000001100; // 0xC
const uint32_t kCoolixZoneFollowMask = 0b000010000000000000000000; // 0x80000
// Fan Control
@@ -92,6 +97,7 @@ class IRCoolixAC {
void stateReset();
#if SEND_COOLIX
void send(const uint16_t repeat = kCoolixDefaultRepeat);
uint8_t calibrate(void) { return _irsend.calibrate(); }
#endif // SEND_COOLIX
void begin();
void on();
@@ -103,7 +109,7 @@ class IRCoolixAC {
void setSensorTemp(const uint8_t desired);
uint8_t getSensorTemp();
void clearSensorTemp();
void setFan(const uint8_t fan);
void setFan(const uint8_t speed, const bool modecheck = true);
uint8_t getFan();
void setMode(const uint8_t mode);
uint8_t getMode();
@@ -120,17 +126,21 @@ class IRCoolixAC {
bool getZoneFollow();
uint32_t getRaw();
void setRaw(const uint32_t new_code);
#ifdef ARDUINO
uint8_t convertMode(const stdAc::opmode_t mode);
uint8_t convertFan(const stdAc::fanspeed_t speed);
static stdAc::opmode_t toCommonMode(const uint8_t mode);
static stdAc::fanspeed_t toCommonFanSpeed(const uint8_t speed);
stdAc::state_t toCommon(void);
String toString();
#else
std::string toString();
#endif
#ifndef UNIT_TEST
private:
IRsend _irsend;
#else
IRsendTest _irsend;
#endif
uint32_t remote_state; // The state of the IR remote in IR code form.
uint32_t saved_state; // Copy of the state if we required a special mode.
IRsend _irsend;
void setTempRaw(const uint8_t code);
uint8_t getTempRaw();
void setSensorTempRaw(const uint8_t code);
File diff suppressed because it is too large. Load diff
+246 -95
View File
@@ -1,34 +1,40 @@
// Copyright 2016 sillyfrog
// Copyright 2017 sillyfrog, crankyoldgit
// Copyright 2018-2019 crankyoldgit
// Supports:
// Brand: Daikin, Model: ARC433** remote
// Brand: Daikin, Model: ARC477A1 remote
// Brand: Daikin, Model: FTXZ25NV1B A/C
// Brand: Daikin, Model: FTXZ35NV1B A/C
// Brand: Daikin, Model: FTXZ50NV1B A/C
// Brand: Daikin, Model: ARC433B69 remote
// Brand: Daikin, Model: ARC423A5 remote
#ifndef IR_DAIKIN_H_
#define IR_DAIKIN_H_
#ifndef UNIT_TEST
#include <Arduino.h>
#else
#include <string>
#endif
#include "IRrecv.h"
#include "IRremoteESP8266.h"
#include "IRsend.h"
// Option to disable the additional Daikin debug info to conserve memory
#define DAIKIN_DEBUG false
// DDDDD AAA IIIII KK KK IIIII NN NN
// DD DD AAAAA III KK KK III NNN NN
// DD DD AA AA III KKKK III NN N NN
// DD DD AAAAAAA III KK KK III NN NNN
// DDDDDD AA AA IIIII KK KK IIIII NN NN
#ifdef UNIT_TEST
#include "IRsend_test.h"
#endif
/*
Daikin AC map
byte 5=Current time, mins past midnight, low bits
byte 6
b0-b3=Current time, mins past midnight, high bits
byte 6=
b4:Comfort
byte 7= checksum of the first part (and last byte before a 29ms pause)
byte 13=mode
byte 13=Current time, mins past midnight, low bits
byte 14
b5-b3=Day of the week (SUN=1, MON=2, ..., SAT=7)
b2-b0=Current time, mins past midnight, high bits
byte 15= checksum of the second part (and last byte before a 29ms pause)
byte 21=mode
b7 = 0
b6+b5+b4 = Mode
Modes: b6+b5+b4
@@ -41,8 +47,8 @@
b2 = OFF timer set
b1 = ON timer set
b0 = Air Conditioner ON
byte 14=temp*2 (Temp should be between 10 - 32)
byte 16=Fan
byte 22=temp*2 (Temp should be between 10 - 32)
byte 24=Fan
FAN control
b7+b6+b5+b4 = Fan speed
Fan: b7+b6+b5+b4
@@ -57,23 +63,23 @@
Swing control up/down:
0000 = Swing up/down off
1111 = Swing up/down on
byte 17
byte 25
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
byte 26=On timer mins past midnight, low bits
byte 27
b0-b3=On timer mins past midnight, high bits
b4-b7=Off timer mins past midnight, low bits
byte 20=Off timer mins past midnight, high bits
byte 21=Aux -> Powerful (bit 1), Silent (bit 5)
byte 24=Aux2
byte 28=Off timer mins past midnight, high bits
byte 29=Aux -> Powerful (bit 1), Silent (bit 5)
byte 32=Aux2
b1: Sensor
b2: Econo mode
b7: Intelligent eye on
byte 25=Aux3
byte 33=Aux3
b1: Mold Proof
byte 26= checksum of the second part
byte 34= checksum of the third part
*/
// Constants
@@ -88,24 +94,46 @@ const uint8_t kDaikinFanMin = 1;
const uint8_t kDaikinFanMax = 5;
const uint8_t kDaikinFanAuto = 0b1010;
const uint8_t kDaikinFanQuiet = 0b1011;
const uint8_t kDaikinBytePower = 13;
const uint16_t kDaikinHeaderLength = 5;
const uint8_t kDaikinSections = 3;
const uint8_t kDaikinSection1Length = 8;
const uint8_t kDaikinSection2Length = 8;
const uint8_t kDaikinSection3Length =
kDaikinStateLength - kDaikinSection1Length - kDaikinSection2Length;
const uint8_t kDaikinByteComfort = 6;
const uint8_t kDaikinByteChecksum1 = 7;
const uint8_t kDaikinBitComfort = 0b00010000;
const uint8_t kDaikinByteClockMinsLow = 13;
const uint8_t kDaikinByteClockMinsHigh = 14;
const uint8_t kDaikinByteChecksum2 = 15;
const uint8_t kDaikinBytePower = 21;
const uint8_t kDaikinBitPower = 0b00000001;
const uint8_t kDaikinBytePowerful = 21;
const uint8_t kDaikinByteTemp = 22;
const uint8_t kDaikinByteFan = 24;
const uint8_t kDaikinByteSwingH = 25;
const uint8_t kDaikinByteOnTimerMinsLow = 26;
const uint8_t kDaikinByteOnTimerMinsHigh = 27;
const uint8_t kDaikinByteOffTimerMinsLow = kDaikinByteOnTimerMinsHigh;
const uint8_t kDaikinByteOffTimerMinsHigh = 28;
const uint8_t kDaikinBytePowerful = 29;
const uint8_t kDaikinBitPowerful = 0b00000001;
const uint8_t kDaikinByteSilent = 21;
const uint8_t kDaikinByteSilent = kDaikinBytePowerful;
const uint8_t kDaikinBitSilent = 0b00100000;
const uint8_t kDaikinByteSensor = 24;
const uint8_t kDaikinByteSensor = 32;
const uint8_t kDaikinBitSensor = 0b00000010;
const uint8_t kDaikinByteEcono = 24;
const uint8_t kDaikinByteEcono = kDaikinByteSensor;
const uint8_t kDaikinBitEcono = 0b00000100;
const uint8_t kDaikinByteEye = 24;
const uint8_t kDaikinByteEye = kDaikinByteSensor;
const uint8_t kDaikinBitEye = 0b10000000;
const uint8_t kDaikinByteMold = 25;
const uint8_t kDaikinByteWeeklyTimer = kDaikinByteSensor;
const uint8_t kDaikinBitWeeklyTimer = 0b10000000;
const uint8_t kDaikinByteMold = 33;
const uint8_t kDaikinBitMold = 0b00000010;
const uint8_t kDaikinByteOffTimer = 13;
const uint8_t kDaikinByteOffTimer = kDaikinBytePower;
const uint8_t kDaikinBitOffTimer = 0b00000100;
const uint8_t kDaikinByteOnTimer = 13;
const uint8_t kDaikinByteOnTimer = kDaikinByteOffTimer;
const uint8_t kDaikinBitOnTimer = 0b00000010;
const uint8_t kDaikinByteChecksum3 = kDaikinStateLength - 1;
const uint16_t kDaikinUnusedTime = 0x600;
const uint8_t kDaikinBeepQuiet = 1;
const uint8_t kDaikinBeepLoud = 2;
@@ -150,6 +178,7 @@ const uint8_t kDaikin2BitMold = 0b00001000;
const uint8_t kDaikin2BitClean = 0b00100000;
const uint8_t kDaikin2BitFreshAir = 0b00000001;
const uint8_t kDaikin2BitFreshAirHigh = 0b10000000;
const uint8_t kDaikin2BitPower = 0b10000000;
const uint8_t kDaikin2LightMask = 0b00110000;
const uint8_t kDaikin2BeepMask = 0b11000000;
const uint8_t kDaikin2SwingVHigh = 0x1;
@@ -159,8 +188,45 @@ const uint8_t kDaikin2SwingVCirculate = 0xD;
const uint8_t kDaikin2SwingVAuto = 0xE;
const uint8_t kDaikin2SwingHAuto = 0xBE;
const uint8_t kDaikin2SwingHSwing = 0xBF;
const uint8_t kDaikin2MinCoolTemp = 18; // Min temp (in C) when in Cool mode.
// Another variant of the protocol for the Daikin ARC433B69 remote.
const uint16_t kDaikin216Freq = 38000; // Modulation Frequency in Hz.
const uint16_t kDaikin216HdrMark = 3440;
const uint16_t kDaikin216HdrSpace = 1750;
const uint16_t kDaikin216BitMark = 420;
const uint16_t kDaikin216OneSpace = 1300;
const uint16_t kDaikin216ZeroSpace = 450;
const uint16_t kDaikin216Gap = 29650;
const uint16_t kDaikin216Sections = 2;
const uint16_t kDaikin216Section1Length = 8;
const uint16_t kDaikin216Section2Length = kDaikin216StateLength -
kDaikin216Section1Length;
const uint8_t kDaikin216BytePower = 13;
const uint8_t kDaikin216ByteMode = kDaikin216BytePower;
const uint8_t kDaikin216MaskMode = 0b01110000;
const uint8_t kDaikin216ByteTemp = 14;
const uint8_t kDaikin216MaskTemp = 0b01111110;
const uint8_t kDaikin216ByteFan = 16;
const uint8_t kDaikin216MaskFan = 0b11110000;
const uint8_t kDaikin216ByteSwingV = 16;
const uint8_t kDaikin216MaskSwingV = 0b00001111;
const uint8_t kDaikin216ByteSwingH = 17;
const uint8_t kDaikin216MaskSwingH = kDaikin216MaskSwingV;
const uint8_t kDaikin216BytePowerful = 21;
// Another variant of the protocol for the Daikin ARC423A5 remote.
const uint16_t kDaikin160Freq = 38000; // Modulation Frequency in Hz.
const uint16_t kDaikin160HdrMark = 5000;
const uint16_t kDaikin160HdrSpace = 2145;
const uint16_t kDaikin160BitMark = 342;
const uint16_t kDaikin160OneSpace = 1786;
const uint16_t kDaikin160ZeroSpace = 700;
const uint16_t kDaikin160Gap = 29650;
const uint16_t kDaikin160Sections = 2;
const uint16_t kDaikin160Section1Length = 7;
const uint16_t kDaikin160Section2Length = kDaikin160StateLength -
kDaikin160Section1Length;
// Legacy defines.
#define DAIKIN_COOL kDaikinCool
#define DAIKIN_HEAT kDaikinHeat
@@ -180,70 +246,72 @@ class IRDaikinESP {
#if SEND_DAIKIN
void send(const uint16_t repeat = kDaikinDefaultRepeat);
uint8_t calibrate(void) { return _irsend.calibrate(); }
#endif
void begin();
void on();
void off();
void setPower(bool state);
bool getPower();
void setTemp(uint8_t temp);
void begin(void);
void on(void);
void off(void);
void setPower(const bool on);
bool getPower(void);
void setTemp(const uint8_t temp);
uint8_t getTemp();
void setFan(uint8_t fan);
uint8_t getFan();
uint8_t getMode();
void setMode(uint8_t mode);
void setSwingVertical(bool state);
bool getSwingVertical();
void setSwingHorizontal(bool state);
bool getSwingHorizontal();
bool getQuiet();
void setQuiet(bool state);
bool getPowerful();
void setPowerful(bool state);
void setSensor(bool state);
bool getSensor();
void setEcono(bool state);
bool getEcono();
void setEye(bool state);
bool getEye();
void setMold(bool state);
bool getMold();
void enableOnTimer(uint16_t starttime);
void disableOnTimer();
uint16_t getOnTime();
void setFan(const uint8_t fan);
uint8_t getFan(void);
void setMode(const uint8_t mode);
uint8_t getMode(void);
void setSwingVertical(const bool on);
bool getSwingVertical(void);
void setSwingHorizontal(const bool on);
bool getSwingHorizontal(void);
bool getQuiet(void);
void setQuiet(const bool on);
bool getPowerful(void);
void setPowerful(const bool on);
void setSensor(const bool on);
bool getSensor(void);
void setEcono(const bool on);
bool getEcono(void);
void setMold(const bool on);
bool getMold(void);
void setComfort(const bool on);
bool getComfort(void);
void enableOnTimer(const uint16_t starttime);
void disableOnTimer(void);
uint16_t getOnTime(void);
bool getOnTimerEnabled();
void enableOffTimer(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);
void enableOffTimer(const uint16_t endtime);
void disableOffTimer(void);
uint16_t getOffTime(void);
bool getOffTimerEnabled(void);
void setCurrentTime(const uint16_t mins_since_midnight);
uint16_t getCurrentTime(void);
void setCurrentDay(const uint8_t day_of_week);
uint8_t getCurrentDay(void);
void setWeeklyTimerEnable(const bool on);
bool getWeeklyTimerEnable(void);
uint8_t* getRaw(void);
void setRaw(const uint8_t new_code[],
const uint16_t length = kDaikinStateLength);
static bool validChecksum(uint8_t state[],
const uint16_t length = kDaikinStateLength);
#ifdef ARDUINO
String toString();
static String renderTime(uint16_t timemins);
#else
std::string toString();
static std::string renderTime(uint16_t timemins);
#endif
static uint8_t convertMode(const stdAc::opmode_t mode);
static uint8_t convertFan(const stdAc::fanspeed_t speed);
static stdAc::opmode_t toCommonMode(const uint8_t mode);
static stdAc::fanspeed_t toCommonFanSpeed(const uint8_t speed);
stdAc::state_t toCommon(void);
String toString(void);
static String renderTime(const uint16_t timemins);
#ifndef UNIT_TEST
private:
// # of bytes per command
uint8_t daikin[kDaikinStateLength];
void stateReset();
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;
#else
IRsendTest _irsend;
#endif
// # of bytes per command
uint8_t remote[kDaikinStateLength];
void stateReset(void);
void checksum(void);
};
// Class to emulate a Daikin ARC477A1 remote.
@@ -253,6 +321,7 @@ class IRDaikin2 {
#if SEND_DAIKIN2
void send(const uint16_t repeat = kDaikin2DefaultRepeat);
uint8_t calibrate(void) { return _irsend.calibrate(); }
#endif
void begin();
void on();
@@ -315,22 +384,104 @@ class IRDaikin2 {
void setCommand(uint32_t value);
static bool validChecksum(uint8_t state[],
const uint16_t length = kDaikin2StateLength);
#ifdef ARDUINO
static uint8_t convertMode(const stdAc::opmode_t mode);
static uint8_t convertFan(const stdAc::fanspeed_t speed);
uint8_t convertSwingV(const stdAc::swingv_t position);
static stdAc::swingv_t toCommonSwingV(const uint8_t setting);
static stdAc::swingh_t toCommonSwingH(const uint8_t setting);
stdAc::state_t toCommon(void);
String toString();
static String renderTime(uint16_t timemins);
#else
std::string toString();
static std::string renderTime(uint16_t timemins);
#endif
#ifndef UNIT_TEST
private:
IRsend _irsend;
#else
IRsendTest _irsend;
#endif
// # of bytes per command
uint8_t remote_state[kDaikin2StateLength];
void stateReset();
void checksum();
void clearOnTimerFlag();
void clearSleepTimerFlag();
};
// Class to emulate a Daikin ARC433B69 remote.
class IRDaikin216 {
public:
explicit IRDaikin216(uint16_t pin);
#if SEND_DAIKIN216
void send(const uint16_t repeat = kDaikin216DefaultRepeat);
uint8_t calibrate(void) { return _irsend.calibrate(); }
#endif
void begin();
uint8_t* getRaw();
void setRaw(const uint8_t new_code[]);
static bool validChecksum(uint8_t state[],
const uint16_t length = kDaikin216StateLength);
void on(void);
void off(void);
void setPower(const bool on);
bool getPower(void);
void setTemp(const uint8_t temp);
uint8_t getTemp();
void setMode(const uint8_t mode);
uint8_t getMode(void);
static uint8_t convertMode(const stdAc::opmode_t mode);
void setFan(const uint8_t fan);
uint8_t getFan(void);
static uint8_t convertFan(const stdAc::fanspeed_t speed);
void setSwingVertical(const bool on);
bool getSwingVertical(void);
void setSwingHorizontal(const bool on);
bool getSwingHorizontal(void);
void setQuiet(const bool on);
bool getQuiet(void);
void setPowerful(const bool on);
bool getPowerful(void);
stdAc::state_t toCommon(void);
String toString(void);
static String renderTime(const uint16_t timemins);
#ifndef UNIT_TEST
private:
IRsend _irsend;
#else
IRsendTest _irsend;
#endif
// # of bytes per command
uint8_t remote_state[kDaikin216StateLength];
void stateReset();
void checksum();
};
// Class to emulate a Daikin ARC423A5 remote.
class IRDaikin160 {
public:
explicit IRDaikin160(uint16_t pin);
#if SEND_DAIKIN160
void send(const uint16_t repeat = kDaikin160DefaultRepeat);
uint8_t calibrate(void) { return _irsend.calibrate(); }
#endif
void begin();
uint8_t* getRaw();
void setRaw(const uint8_t new_code[]);
static bool validChecksum(uint8_t state[],
const uint16_t length = kDaikin160StateLength);
#ifndef UNIT_TEST
private:
IRsend _irsend;
#else
IRsendTest _irsend;
#endif
// # of bytes per command
uint8_t remote_state[kDaikin160StateLength];
void stateReset();
void checksum();
};
#endif // IR_DAIKIN_H_
+4 -10
View File
@@ -6,12 +6,6 @@
#include "IRsend.h"
#include "IRutils.h"
// DDDD EEEEE N N OOO N N
// D D E NN N O O NN N
// D D EEE N N N O O N N N
// D D E N NN O O N NN
// DDDD EEEEE N N OOO N N
// Original Denon support added by https://github.com/csBlueChip
// Ported over by Massimiliano Pinto
@@ -43,7 +37,7 @@ const uint64_t kDenonManufacturer = 0x2A4CULL;
//
// Args:
// data: Contents of the message to be sent.
// nbits: Nr. of bits of data to be sent. Typically DENON_BITS.
// nbits: Nr. of bits of data to be sent. Typically kDenonBits.
// repeat: Nr. of additional times the message is to be sent.
//
// Status: BETA / Should be working.
@@ -70,7 +64,7 @@ void IRsend::sendDenon(uint64_t data, uint16_t nbits, uint16_t repeat) {
//
// Args:
// results: Ptr to the data to decode and where to store the decode result.
// nbits: Expected nr. of data bits. (Typically DENON_BITS)
// nbits: Expected nr. of data bits. (Typically kDenonBits)
// Returns:
// boolean: True if it can decode it, false if it can't.
//
@@ -82,8 +76,8 @@ bool IRrecv::decodeDenon(decode_results *results, uint16_t nbits, bool strict) {
// Compliance
if (strict) {
switch (nbits) {
case DENON_BITS:
case DENON_48_BITS:
case kDenonBits:
case kDenon48Bits:
case kDenonLegacyBits:
break;
default:
+3 -6
View File
@@ -5,15 +5,12 @@
#include "IRsend.h"
#include "IRutils.h"
// DDDD IIIII SSSS H H
// D D I S H H
// D D I SSS HHHHH
// D D I S H H
// DDDD IIIII SSSS H H
// DISH support originally by Todd Treece
// http://unionbridge.org/design/ircommand
// Supports:
// Brand: DISH NETWORK, Model: echostar 301
// Constants
// Ref:
// https://github.com/marcosamarinho/IRremoteESP8266/blob/master/ir_Dish.cpp
+15 -15
View File
@@ -1,15 +1,9 @@
// Copyright 2018 David Conran
// Copyright 2018, 2019 David Conran
#include "IRrecv.h"
#include "IRsend.h"
#include "IRutils.h"
// EEEEEEE LL EEEEEEE CCCCC TTTTTTT RRRRRR AAA
// EE LL EE CC C TTT RR RR AAAAA
// EEEEE LL EEEEE CC TTT RRRRRR AA AA
// EE LL EE CC C TTT RR RR AAAAAAA
// EEEEEEE LLLLLLL EEEEEEE CCCCC TTT RR RR AA AA
// Electra A/C added by crankyoldgit
//
// Equipment it seems compatible with:
@@ -17,6 +11,7 @@
// Ref:
// https://github.com/markszabo/IRremoteESP8266/issues/527
// https://github.com/markszabo/IRremoteESP8266/issues/642
// Constants
const uint16_t kElectraAcHdrMark = 9166;
@@ -24,7 +19,7 @@ const uint16_t kElectraAcBitMark = 646;
const uint16_t kElectraAcHdrSpace = 4470;
const uint16_t kElectraAcOneSpace = 1647;
const uint16_t kElectraAcZeroSpace = 547;
const uint32_t kElectraAcMessageGap = 100000; // Completely made-up guess.
const uint32_t kElectraAcMessageGap = kDefaultMessageGap; // Just a guess.
#if SEND_ELECTRA_AC
// Send a Electra message
@@ -36,13 +31,15 @@ const uint32_t kElectraAcMessageGap = 100000; // Completely made-up guess.
//
// Status: Alpha / Needs testing against a real device.
//
void IRsend::sendElectraAC(uint8_t data[], uint16_t nbytes, uint16_t repeat) {
void IRsend::sendElectraAC(const uint8_t data[], const uint16_t nbytes,
const uint16_t repeat) {
for (uint16_t r = 0; r <= repeat; r++)
sendGeneric(kElectraAcHdrMark, kElectraAcHdrSpace, kElectraAcBitMark,
kElectraAcOneSpace, kElectraAcBitMark, kElectraAcZeroSpace,
kElectraAcBitMark, kElectraAcMessageGap, data, nbytes,
38000, // Complete guess of the modulation frequency.
true, 0, 50);
false, // Send data in LSB order per byte
0, 50);
}
#endif
@@ -56,7 +53,7 @@ void IRsend::sendElectraAC(uint8_t data[], uint16_t nbytes, uint16_t repeat) {
// Returns:
// boolean: True if it can decode it, false if it can't.
//
// Status: Alpha / Needs testing against a real device.
// Status: Beta / Probably works.
//
bool IRrecv::decodeElectraAC(decode_results *results, uint16_t nbits,
bool strict) {
@@ -68,8 +65,6 @@ bool IRrecv::decodeElectraAC(decode_results *results, uint16_t nbits,
return false; // Not strictly a ELECTRA_AC message.
}
// The protocol sends the data normal + inverted, alternating on
// each byte. Hence twice the number of expected data bits.
if (results->rawlen < 2 * nbits + kHeader + kFooter - 1)
return false; // Can't possibly be a valid ELECTRA_AC message.
@@ -87,7 +82,7 @@ bool IRrecv::decodeElectraAC(decode_results *results, uint16_t nbits,
i++, dataBitsSoFar += 8, offset += data_result.used) {
data_result = matchData(&(results->rawbuf[offset]), 8, kElectraAcBitMark,
kElectraAcOneSpace, kElectraAcBitMark,
kElectraAcZeroSpace, kTolerance, 0, true);
kElectraAcZeroSpace, kTolerance, 0, false);
if (data_result.success == false) return false; // Fail
results->state[i] = data_result.data;
}
@@ -99,7 +94,12 @@ bool IRrecv::decodeElectraAC(decode_results *results, uint16_t nbits,
return false;
// Compliance
if (strict && dataBitsSoFar != nbits) return false;
if (strict) {
if (dataBitsSoFar != nbits) return false;
// Verify the checksum.
if (sumBytes(results->state, (dataBitsSoFar / 8) - 1) !=
results->state[(dataBitsSoFar / 8) - 1]) return false;
}
// Success
results->decode_type = ELECTRA_AC;
+365 -131
View File
@@ -1,4 +1,5 @@
// Copyright 2017 Jonny Graham, David Conran
// Copyright 2017 Jonny Graham
// Copyright 2017-2019 David Conran
#include "ir_Fujitsu.h"
#include <algorithm>
#ifndef ARDUINO
@@ -12,6 +13,9 @@
// Equipment it seems compatible with:
// * Fujitsu ASYG30LFCA with remote AR-RAH2E
// * Fujitsu AST9RSGCW with remote AR-DB1
// * Fujitsu ASYG7LMCA with remote AR-REB1E
// * Fujitsu AR-RAE1E remote.
// * Fujitsu General with remote AR-JW2
// * <Add models (A/C & remotes) you've gotten it working with here>
// Ref:
@@ -36,10 +40,10 @@ const uint16_t kFujitsuAcMinGap = 8100;
// repeat: Nr. of times the message is to be repeated.
// (Default = kFujitsuAcMinRepeat).
//
// Status: BETA / Appears to be working.
// Status: STABLE / Known Good.
//
void IRsend::sendFujitsuAC(unsigned char data[], uint16_t nbytes,
uint16_t repeat) {
void IRsend::sendFujitsuAC(const unsigned char data[], const uint16_t nbytes,
const uint16_t repeat) {
sendGeneric(kFujitsuAcHdrMark, kFujitsuAcHdrSpace, kFujitsuAcBitMark,
kFujitsuAcOneSpace, kFujitsuAcBitMark, kFujitsuAcZeroSpace,
kFujitsuAcBitMark, kFujitsuAcMinGap, data, nbytes, 38, false,
@@ -50,47 +54,53 @@ void IRsend::sendFujitsuAC(unsigned char data[], uint16_t nbytes,
// Code to emulate Fujitsu A/C IR remote control unit.
// Initialise the object.
IRFujitsuAC::IRFujitsuAC(uint16_t pin, fujitsu_ac_remote_model_t model)
IRFujitsuAC::IRFujitsuAC(const uint16_t pin,
const fujitsu_ac_remote_model_t model)
: _irsend(pin) {
setModel(model);
stateReset();
this->setModel(model);
this->stateReset();
}
void IRFujitsuAC::setModel(fujitsu_ac_remote_model_t model) {
void IRFujitsuAC::setModel(const fujitsu_ac_remote_model_t model) {
_model = model;
switch (model) {
case ARDB1:
case ARJW2:
_state_length = kFujitsuAcStateLength - 1;
_state_length_short = kFujitsuAcStateLengthShort - 1;
break;
case ARRAH2E:
case ARREB1E:
default:
_state_length = kFujitsuAcStateLength;
_state_length_short = kFujitsuAcStateLengthShort;
}
}
fujitsu_ac_remote_model_t IRFujitsuAC::getModel(void) { return _model; }
// Reset the state of the remote to a known good state/sequence.
void IRFujitsuAC::stateReset() {
void IRFujitsuAC::stateReset(void) {
_temp = 24;
_fanSpeed = kFujitsuAcFanHigh;
_mode = kFujitsuAcModeCool;
_swingMode = kFujitsuAcSwingBoth;
_cmd = kFujitsuAcCmdTurnOn;
buildState();
this->buildState();
}
// Configure the pin for output.
void IRFujitsuAC::begin() { _irsend.begin(); }
void IRFujitsuAC::begin(void) { _irsend.begin(); }
#if SEND_FUJITSU_AC
// Send the current desired state to the IR LED.
void IRFujitsuAC::send(const uint16_t repeat) {
getRaw();
this->buildState();
_irsend.sendFujitsuAC(remote_state, getStateLength(), repeat);
}
#endif // SEND_FUJITSU_AC
void IRFujitsuAC::buildState() {
void IRFujitsuAC::buildState(void) {
remote_state[0] = 0x14;
remote_state[1] = 0x63;
remote_state[2] = 0x00;
@@ -98,21 +108,23 @@ void IRFujitsuAC::buildState() {
remote_state[4] = 0x10;
bool fullCmd = false;
switch (_cmd) {
case kFujitsuAcCmdTurnOff:
remote_state[5] = 0x02;
break;
case kFujitsuAcCmdStepHoriz:
remote_state[5] = 0x79;
break;
case kFujitsuAcCmdStepVert:
remote_state[5] = 0x6C;
case kFujitsuAcCmdTurnOff: // 0x02
case kFujitsuAcCmdEcono: // 0x09
case kFujitsuAcCmdPowerful: // 0x39
case kFujitsuAcCmdStepVert: // 0x6C
case kFujitsuAcCmdToggleSwingVert: // 0x6D
case kFujitsuAcCmdStepHoriz: // 0x79
case kFujitsuAcCmdToggleSwingHoriz: // 0x7A
remote_state[5] = _cmd;
break;
default:
switch (_model) {
case ARRAH2E:
case ARREB1E:
remote_state[5] = 0xFE;
break;
case ARDB1:
case ARJW2:
remote_state[5] = 0xFC;
break;
}
@@ -127,49 +139,61 @@ void IRFujitsuAC::buildState() {
remote_state[7] = 0x30;
remote_state[8] = (_cmd == kFujitsuAcCmdTurnOn) | (tempByte << 4);
remote_state[9] = _mode | 0 << 4; // timer off
remote_state[10] = _fanSpeed | _swingMode << 4;
remote_state[10] = _fanSpeed;
remote_state[11] = 0; // timerOff values
remote_state[12] = 0; // timerOff/On values
remote_state[13] = 0; // timerOn values
if (_model == ARRAH2E)
remote_state[14] = 0x20;
else
remote_state[14] = 0x00;
remote_state[14] = 0;
uint8_t checksum = 0;
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;
switch (_model) {
case ARDB1:
case ARJW2:
checksum = sumBytes(remote_state, _state_length - 1);
checksum_complement = 0x9B;
break;
case ARREB1E:
remote_state[14] |= (_outsideQuiet << 7);
// FALL THRU
case ARRAH2E:
remote_state[14] |= 0x20;
remote_state[10] |= _swingMode << 4;
// FALL THRU
default:
checksum = sumBytes(remote_state + _state_length_short,
_state_length - _state_length_short - 1);
}
// 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];
switch (_model) {
case ARRAH2E:
case ARREB1E:
// The last byte is the inverse of penultimate byte
remote_state[_state_length_short - 1] =
~remote_state[_state_length_short - 2];
break;
default:
{}; // We don't need to do anything for the others.
}
// Zero the rest of the state.
for (uint8_t i = _state_length_short; i < kFujitsuAcStateLength; 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))
uint8_t IRFujitsuAC::getStateLength(void) {
this->buildState(); // Force an update of the internal state.
if (((_model == ARRAH2E || _model == ARREB1E) && remote_state[5] != 0xFE) ||
((_model == ARDB1 || _model == ARJW2) && 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();
uint8_t* IRFujitsuAC::getRaw(void) {
this->buildState();
return remote_state;
}
@@ -177,17 +201,28 @@ void IRFujitsuAC::buildFromState(const uint16_t length) {
switch (length) {
case kFujitsuAcStateLength - 1:
case kFujitsuAcStateLengthShort - 1:
setModel(ARDB1);
this->setModel(ARDB1);
// ARJW2 has horizontal swing.
if (this->getSwing(true) > kFujitsuAcSwingVert) this->setModel(ARJW2);
break;
default:
setModel(ARRAH2E);
switch (this->getCmd(true)) {
case kFujitsuAcCmdEcono:
case kFujitsuAcCmdPowerful:
this->setModel(fujitsu_ac_remote_model_t::ARREB1E);
break;
default:
this->setModel(fujitsu_ac_remote_model_t::ARRAH2E);
}
}
switch (remote_state[6]) {
case 8:
setModel(ARDB1);
if (this->getModel() != fujitsu_ac_remote_model_t::ARJW2)
this->setModel(ARDB1);
break;
case 9:
setModel(ARRAH2E);
if (this->getModel() != fujitsu_ac_remote_model_t::ARREB1E)
this->setModel(ARRAH2E);
break;
}
setTemp((remote_state[8] >> 4) + kFujitsuAcMinTemp);
@@ -201,10 +236,15 @@ void IRFujitsuAC::buildFromState(const uint16_t length) {
switch (remote_state[5]) {
case kFujitsuAcCmdTurnOff:
case kFujitsuAcCmdStepHoriz:
case kFujitsuAcCmdToggleSwingHoriz:
case kFujitsuAcCmdStepVert:
case kFujitsuAcCmdToggleSwingVert:
case kFujitsuAcCmdEcono:
case kFujitsuAcCmdPowerful:
setCmd(remote_state[5]);
break;
}
_outsideQuiet = this->getOutsideQuiet(true);
}
bool IRFujitsuAC::setRaw(const uint8_t newState[], const uint16_t length) {
@@ -220,184 +260,378 @@ bool IRFujitsuAC::setRaw(const uint8_t newState[], const uint16_t length) {
}
// Set the requested power state of the A/C to off.
void IRFujitsuAC::off() { _cmd = kFujitsuAcCmdTurnOff; }
void IRFujitsuAC::off(void) { this->setCmd(kFujitsuAcCmdTurnOff); }
void IRFujitsuAC::stepHoriz() {
switch (_model) {
case ARDB1:
break; // This remote doesn't have a horizontal option.
default:
_cmd = kFujitsuAcCmdStepHoriz;
}
void IRFujitsuAC::stepHoriz(void) { this->setCmd(kFujitsuAcCmdStepHoriz); }
void IRFujitsuAC::toggleSwingHoriz(const bool update) {
// Toggle the current setting.
if (update) this->setSwing(this->getSwing() ^ kFujitsuAcSwingHoriz);
// and set the appropriate special command.
this->setCmd(kFujitsuAcCmdToggleSwingHoriz);
}
void IRFujitsuAC::stepVert() { _cmd = kFujitsuAcCmdStepVert; }
void IRFujitsuAC::stepVert(void) { this->setCmd(kFujitsuAcCmdStepVert); }
void IRFujitsuAC::toggleSwingVert(const bool update) {
// Toggle the current setting.
if (update) this->setSwing(this->getSwing() ^ kFujitsuAcSwingVert);
// and set the appropriate special command.
this->setCmd(kFujitsuAcCmdToggleSwingVert);
}
// Set the requested command of the A/C.
void IRFujitsuAC::setCmd(uint8_t cmd) {
void IRFujitsuAC::setCmd(const uint8_t cmd) {
switch (cmd) {
case kFujitsuAcCmdTurnOff:
case kFujitsuAcCmdTurnOn:
case kFujitsuAcCmdStayOn:
case kFujitsuAcCmdStepVert:
case kFujitsuAcCmdToggleSwingVert:
_cmd = cmd;
break;
case kFujitsuAcCmdStepHoriz:
if (_model != ARDB1) // AR-DB1 remote doesn't have step horizontal.
case kFujitsuAcCmdToggleSwingHoriz:
switch (_model) {
// Only these remotes have step horizontal.
case ARRAH2E:
case ARJW2:
_cmd = cmd;
break;
default:
_cmd = kFujitsuAcCmdStayOn;
}
break;
case kFujitsuAcCmdEcono:
case kFujitsuAcCmdPowerful:
switch (_model) {
// Only these remotes have these commands.
case ARREB1E:
_cmd = cmd;
// FALLTHRU
break;
default:
_cmd = kFujitsuAcCmdStayOn;
}
break;
default:
_cmd = kFujitsuAcCmdStayOn;
break;
}
}
uint8_t IRFujitsuAC::getCmd() { return _cmd; }
// Get the special command part of the message.
// Args:
// raw: Do we need to get it from first principles from the raw data?
// Returns:
// A uint8_t containing the contents of the special command byte.
uint8_t IRFujitsuAC::getCmd(const bool raw) {
if (raw) return remote_state[5];
return _cmd;
}
bool IRFujitsuAC::getPower() { return _cmd != kFujitsuAcCmdTurnOff; }
bool IRFujitsuAC::getPower(void) { return _cmd != kFujitsuAcCmdTurnOff; }
void IRFujitsuAC::setOutsideQuiet(const bool on) {
_outsideQuiet = on;
this->setCmd(kFujitsuAcCmdStayOn); // No special command involved.
}
// Get the status of the Outside Quiet setting.
// Args:
// raw: Do we get the result from base data?
// Returns:
// A boolean for if it is set or not.
bool IRFujitsuAC::getOutsideQuiet(const bool raw) {
if (_state_length == kFujitsuAcStateLength && raw) {
_outsideQuiet = remote_state[14] & 0b10000000;
// Only ARREB1E seems to have this mode.
if (_outsideQuiet) this->setModel(fujitsu_ac_remote_model_t::ARREB1E);
}
return _outsideQuiet;
}
// Set the temp. in deg C
void IRFujitsuAC::setTemp(uint8_t temp) {
temp = std::max((uint8_t)kFujitsuAcMinTemp, temp);
temp = std::min((uint8_t)kFujitsuAcMaxTemp, temp);
_temp = temp;
void IRFujitsuAC::setTemp(const uint8_t temp) {
_temp = std::max((uint8_t)kFujitsuAcMinTemp, temp);
_temp = std::min((uint8_t)kFujitsuAcMaxTemp, _temp);
this->setCmd(kFujitsuAcCmdStayOn); // No special command involved.
}
uint8_t IRFujitsuAC::getTemp() { return _temp; }
uint8_t IRFujitsuAC::getTemp(void) { return _temp; }
// Set the speed of the fan
void IRFujitsuAC::setFanSpeed(uint8_t fanSpeed) {
void IRFujitsuAC::setFanSpeed(const uint8_t fanSpeed) {
if (fanSpeed > kFujitsuAcFanQuiet)
fanSpeed = kFujitsuAcFanHigh; // Set the fan to maximum if out of range.
_fanSpeed = fanSpeed;
_fanSpeed = kFujitsuAcFanHigh; // Set the fan to maximum if out of range.
else
_fanSpeed = fanSpeed;
this->setCmd(kFujitsuAcCmdStayOn); // No special command involved.
}
uint8_t IRFujitsuAC::getFanSpeed() { return _fanSpeed; }
uint8_t IRFujitsuAC::getFanSpeed(void) { return _fanSpeed; }
// Set the requested climate operation mode of the a/c unit.
void IRFujitsuAC::setMode(uint8_t mode) {
void IRFujitsuAC::setMode(const uint8_t mode) {
if (mode > kFujitsuAcModeHeat)
mode = kFujitsuAcModeHeat; // Set the mode to maximum if out of range.
_mode = mode;
_mode = kFujitsuAcModeHeat; // Set the mode to maximum if out of range.
else
_mode = mode;
this->setCmd(kFujitsuAcCmdStayOn); // No special command involved.
}
uint8_t IRFujitsuAC::getMode() { return _mode; }
uint8_t IRFujitsuAC::getMode(void) { return _mode; }
// Set the requested swing operation mode of the a/c unit.
void IRFujitsuAC::setSwing(uint8_t swingMode) {
void IRFujitsuAC::setSwing(const uint8_t swingMode) {
_swingMode = swingMode;
switch (_model) {
// No Horizontal support.
case ARDB1:
case ARREB1E:
// Set the mode to max if out of range
if (swingMode > kFujitsuAcSwingVert) swingMode = kFujitsuAcSwingVert;
if (swingMode > kFujitsuAcSwingVert) _swingMode = kFujitsuAcSwingVert;
break;
// Has Horizontal support.
case ARRAH2E:
case ARJW2:
default:
// Set the mode to max if out of range
if (swingMode > kFujitsuAcSwingBoth) swingMode = kFujitsuAcSwingBoth;
if (swingMode > kFujitsuAcSwingBoth) _swingMode = kFujitsuAcSwingBoth;
}
_swingMode = swingMode;
this->setCmd(kFujitsuAcCmdStayOn); // No special command involved.
}
uint8_t IRFujitsuAC::getSwing() { return _swingMode; }
// Get what the swing part of the message should be.
// Args:
// raw: Do we need to get it from first principles from the raw data?
// Returns:
// A uint8_t containing the contents of the swing state.
uint8_t IRFujitsuAC::getSwing(const bool raw) {
if (raw) _swingMode = remote_state[10] >> 4;
return _swingMode;
}
bool IRFujitsuAC::validChecksum(uint8_t state[], uint16_t length) {
bool IRFujitsuAC::validChecksum(uint8_t state[], const uint16_t length) {
uint8_t sum = 0;
uint8_t sum_complement = 0;
uint8_t checksum = state[length - 1];
switch (length) {
case kFujitsuAcStateLengthShort: // ARRAH2E
case kFujitsuAcStateLengthShort: // ARRAH2E & ARREB1E
return state[length - 1] == (uint8_t)~state[length - 2];
case kFujitsuAcStateLength - 1: // ARDB1
case kFujitsuAcStateLength - 1: // ARDB1 & ARJW2
sum = sumBytes(state, length - 1);
sum_complement = 0x9B;
break;
case kFujitsuAcStateLength: // ARRAH2E
case kFujitsuAcStateLength: // ARRAH2E & ARREB1E
sum = sumBytes(state + kFujitsuAcStateLengthShort,
length - 1 - kFujitsuAcStateLengthShort);
break;
default: // Includes ARDB1 short.
default: // Includes ARDB1 & ARJW2 short.
return true; // Assume the checksum is valid for other lengths.
}
return checksum == (uint8_t)(sum_complement - sum); // Does it match?
}
// Convert a standard A/C mode into its native mode.
uint8_t IRFujitsuAC::convertMode(const stdAc::opmode_t mode) {
switch (mode) {
case stdAc::opmode_t::kCool:
return kFujitsuAcModeCool;
case stdAc::opmode_t::kHeat:
return kFujitsuAcModeHeat;
case stdAc::opmode_t::kDry:
return kFujitsuAcModeDry;
case stdAc::opmode_t::kFan:
return kFujitsuAcModeFan;
default:
return kFujitsuAcModeAuto;
}
}
// Convert a standard A/C Fan speed into its native fan speed.
uint8_t IRFujitsuAC::convertFan(stdAc::fanspeed_t speed) {
switch (speed) {
case stdAc::fanspeed_t::kMin:
return kFujitsuAcFanQuiet;
case stdAc::fanspeed_t::kLow:
return kFujitsuAcFanLow;
case stdAc::fanspeed_t::kMedium:
return kFujitsuAcFanMed;
case stdAc::fanspeed_t::kHigh:
case stdAc::fanspeed_t::kMax:
return kFujitsuAcFanHigh;
default:
return kFujitsuAcFanAuto;
}
}
// Convert a native mode to it's common equivalent.
stdAc::opmode_t IRFujitsuAC::toCommonMode(const uint8_t mode) {
switch (mode) {
case kFujitsuAcModeCool: return stdAc::opmode_t::kCool;
case kFujitsuAcModeHeat: return stdAc::opmode_t::kHeat;
case kFujitsuAcModeDry: return stdAc::opmode_t::kDry;
case kFujitsuAcModeFan: return stdAc::opmode_t::kFan;
default: return stdAc::opmode_t::kAuto;
}
}
// Convert a native fan speed to it's common equivalent.
stdAc::fanspeed_t IRFujitsuAC::toCommonFanSpeed(const uint8_t speed) {
switch (speed) {
case kFujitsuAcFanHigh: return stdAc::fanspeed_t::kMax;
case kFujitsuAcFanMed: return stdAc::fanspeed_t::kMedium;
case kFujitsuAcFanLow: return stdAc::fanspeed_t::kLow;
case kFujitsuAcFanQuiet: return stdAc::fanspeed_t::kMin;
default: return stdAc::fanspeed_t::kAuto;
}
}
// Convert the A/C state to it's common equivalent.
stdAc::state_t IRFujitsuAC::toCommon(void) {
stdAc::state_t result;
result.protocol = decode_type_t::FUJITSU_AC;
result.model = this->getModel();
result.power = this->getPower();
result.mode = this->toCommonMode(this->getMode());
result.celsius = true;
result.degrees = this->getTemp();
result.fanspeed = this->toCommonFanSpeed(this->getFanSpeed());
uint8_t swing = this->getSwing();
switch (result.model) {
case fujitsu_ac_remote_model_t::ARREB1E:
case fujitsu_ac_remote_model_t::ARRAH2E:
result.swingv = (swing & kFujitsuAcSwingVert) ? stdAc::swingv_t::kAuto :
stdAc::swingv_t::kOff;
result.swingh = (swing & kFujitsuAcSwingHoriz) ? stdAc::swingh_t::kAuto :
stdAc::swingh_t::kOff;
break;
case fujitsu_ac_remote_model_t::ARDB1:
case fujitsu_ac_remote_model_t::ARJW2:
default:
result.swingv = stdAc::swingv_t::kOff;
result.swingh = stdAc::swingh_t::kOff;
}
result.quiet = (this->getFanSpeed() == kFujitsuAcFanQuiet);
result.turbo = this->getCmd() == kFujitsuAcCmdPowerful;
result.econo = this->getCmd() == kFujitsuAcCmdEcono;
// Not supported.
result.light = false;
result.filter = false;
result.clean = false;
result.beep = false;
result.sleep = -1;
result.clock = -1;
return result;
}
// Convert the internal state into a human readable string.
#ifdef ARDUINO
String IRFujitsuAC::toString() {
String IRFujitsuAC::toString(void) {
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()) {
result.reserve(100); // Reserve some heap for the string to reduce fragging.
result += F("Model: ");
fujitsu_ac_remote_model_t model = this->getModel();
result += uint64ToString(model);
switch (model) {
case fujitsu_ac_remote_model_t::ARRAH2E: result += F(" (ARRAH2E)"); break;
case fujitsu_ac_remote_model_t::ARDB1: result += F(" (ARDB1)"); break;
case fujitsu_ac_remote_model_t::ARREB1E: result += F(" (ARREB1E)"); break;
case fujitsu_ac_remote_model_t::ARJW2: result += F(" (ARJW2)"); break;
default: result += F(" (UNKNOWN)");
}
result += F(", Power: ");
result += this->getPower() ? F("On") : F("Off");
result += F(", Mode: ");
result += uint64ToString(this->getMode());
switch (this->getMode()) {
case kFujitsuAcModeAuto:
result += " (AUTO)";
result += F(" (AUTO)");
break;
case kFujitsuAcModeCool:
result += " (COOL)";
result += F(" (COOL)");
break;
case kFujitsuAcModeHeat:
result += " (HEAT)";
result += F(" (HEAT)");
break;
case kFujitsuAcModeDry:
result += " (DRY)";
result += F(" (DRY)");
break;
case kFujitsuAcModeFan:
result += " (FAN)";
result += F(" (FAN)");
break;
default:
result += " (UNKNOWN)";
result += F(" (UNKNOWN)");
}
result += ", Temp: " + uint64ToString(getTemp()) + "C";
result += ", Fan: " + uint64ToString(getFanSpeed());
result += F(", Temp: ");
result += uint64ToString(this->getTemp());
result += F("C, Fan: ");
result += uint64ToString(this->getFanSpeed());
switch (getFanSpeed()) {
case kFujitsuAcFanAuto:
result += " (AUTO)";
result += F(" (AUTO)");
break;
case kFujitsuAcFanHigh:
result += " (HIGH)";
result += F(" (HIGH)");
break;
case kFujitsuAcFanMed:
result += " (MED)";
result += F(" (MED)");
break;
case kFujitsuAcFanLow:
result += " (LOW)";
result += F(" (LOW)");
break;
case kFujitsuAcFanQuiet:
result += " (QUIET)";
result += F(" (QUIET)");
break;
}
result += ", Swing: ";
switch (getSwing()) {
case kFujitsuAcSwingOff:
result += "Off";
switch (model) {
// These models have no internal swing state.
case fujitsu_ac_remote_model_t::ARDB1:
case fujitsu_ac_remote_model_t::ARJW2:
break;
case kFujitsuAcSwingVert:
result += "Vert";
break;
case kFujitsuAcSwingHoriz:
result += "Horiz";
break;
case kFujitsuAcSwingBoth:
result += "Vert + Horiz";
break;
default:
result += "UNKNOWN";
default: // Assume everything else does.
result += F(", Swing: ");
switch (this->getSwing()) {
case kFujitsuAcSwingOff:
result += F("Off");
break;
case kFujitsuAcSwingVert:
result += F("Vert");
break;
case kFujitsuAcSwingHoriz:
result += F("Horiz");
break;
case kFujitsuAcSwingBoth:
result += F("Vert + Horiz");
break;
default:
result += F("UNKNOWN");
}
}
result += ", Command: ";
switch (getCmd()) {
result += F(", Command: ");
switch (this->getCmd()) {
case kFujitsuAcCmdStepHoriz:
result += "Step vane horizontally";
result += F("Step vane horizontally");
break;
case kFujitsuAcCmdStepVert:
result += "Step vane vertically";
result += F("Step vane vertically");
break;
case kFujitsuAcCmdToggleSwingHoriz:
result += F("Toggle horizontal swing");
break;
case kFujitsuAcCmdToggleSwingVert:
result += F("Toggle vertically swing");
break;
case kFujitsuAcCmdEcono:
result += F("Economy");
break;
case kFujitsuAcCmdPowerful:
result += F("Powerful");
break;
default:
result += "N/A";
result += F("N/A");
}
if (this->getModel() == fujitsu_ac_remote_model_t::ARREB1E) {
result += F(", Outside Quiet: ");
result += this->getOutsideQuiet() ? F("On") : F("Off");
}
return result;
}
+71 -38
View File
@@ -1,5 +1,16 @@
// Copyright 2017 Jonny Graham
// Copyright 2018 David Conran
// Copyright 2018-2019 David Conran
// Supports:
// Brand: Fujitsu, Model: AR-RAH2E remote
// Brand: Fujitsu, Model: ASYG30LFCA A/C
// Brand: Fujitsu, Model: AR-DB1 remote
// Brand: Fujitsu, Model: AST9RSGCW A/C
// Brand: Fujitsu, Model: AR-REB1E remote
// Brand: Fujitsu, Model: ASYG7LMCA A/C
// Brand: Fujitsu, Model: AR-RAE1E remote
// Brand: Fujitsu General, Model: AR-JW2 remote
#ifndef IR_FUJITSU_H_
#define IR_FUJITSU_H_
@@ -7,12 +18,13 @@
#include <stdint.h>
#ifdef ARDUINO
#include <Arduino.h>
#else
#include <string>
#endif
#include "IRrecv.h"
#include "IRremoteESP8266.h"
#include "IRsend.h"
#ifdef UNIT_TEST
#include "IRsend_test.h"
#endif
// FUJITSU A/C support added by Jonny Graham
@@ -23,11 +35,15 @@ const uint8_t kFujitsuAcModeDry = 0x02;
const uint8_t kFujitsuAcModeFan = 0x03;
const uint8_t kFujitsuAcModeHeat = 0x04;
const uint8_t kFujitsuAcCmdStayOn = 0x00;
const uint8_t kFujitsuAcCmdTurnOn = 0x01;
const uint8_t kFujitsuAcCmdTurnOff = 0x02;
const uint8_t kFujitsuAcCmdStepHoriz = 0x79;
const uint8_t kFujitsuAcCmdStepVert = 0x6C;
const uint8_t kFujitsuAcCmdStayOn = 0x00; // b00000000
const uint8_t kFujitsuAcCmdTurnOn = 0x01; // b00000001
const uint8_t kFujitsuAcCmdTurnOff = 0x02; // b00000010
const uint8_t kFujitsuAcCmdEcono = 0x09; // b00001001
const uint8_t kFujitsuAcCmdPowerful = 0x39; // b00111001
const uint8_t kFujitsuAcCmdStepVert = 0x6C; // b01101100
const uint8_t kFujitsuAcCmdToggleSwingVert = 0x6D; // b01101101
const uint8_t kFujitsuAcCmdStepHoriz = 0x79; // b01111001
const uint8_t kFujitsuAcCmdToggleSwingHoriz = 0x7A; // b01111010
const uint8_t kFujitsuAcFanAuto = 0x00;
const uint8_t kFujitsuAcFanHigh = 0x01;
@@ -67,47 +83,63 @@ const uint8_t kFujitsuAcSwingBoth = 0x03;
#define FUJITSU_AC_SWING_BOTH kFujitsuAcSwingBoth
enum fujitsu_ac_remote_model_t {
ARRAH2E = 1,
ARDB1,
ARRAH2E = 1, // (1) AR-RAH2E, AR-RAE1E (Default)
ARDB1, // (2) AR-DB1
ARREB1E, // (3) AR-REB1E
ARJW2, // (4) AR-JW2 (Same as ARDB1 but with horiz control)
};
class IRFujitsuAC {
public:
explicit IRFujitsuAC(uint16_t pin, fujitsu_ac_remote_model_t model = ARRAH2E);
explicit IRFujitsuAC(const uint16_t pin,
const fujitsu_ac_remote_model_t model = ARRAH2E);
void setModel(fujitsu_ac_remote_model_t model);
void stateReset();
void setModel(const fujitsu_ac_remote_model_t model);
fujitsu_ac_remote_model_t getModel(void);
void stateReset(void);
#if SEND_FUJITSU_AC
void send(const uint16_t repeat = kFujitsuAcMinRepeat);
uint8_t calibrate(void) { return _irsend.calibrate(); }
#endif // SEND_FUJITSU_AC
void begin();
void off();
void stepHoriz();
void stepVert();
void setCmd(uint8_t cmd);
uint8_t getCmd();
void setTemp(uint8_t temp);
uint8_t getTemp();
void setFanSpeed(uint8_t fan);
uint8_t getFanSpeed();
void setMode(uint8_t mode);
uint8_t getMode();
void setSwing(uint8_t mode);
uint8_t getSwing();
uint8_t* getRaw();
void begin(void);
void off(void);
void stepHoriz(void);
void toggleSwingHoriz(const bool update = true);
void stepVert(void);
void toggleSwingVert(const bool update = true);
void setCmd(const uint8_t cmd);
uint8_t getCmd(const bool raw = false);
void setTemp(const uint8_t temp);
uint8_t getTemp(void);
void setFanSpeed(const uint8_t fan);
uint8_t getFanSpeed(void);
void setMode(const uint8_t mode);
uint8_t getMode(void);
void setSwing(const uint8_t mode);
uint8_t getSwing(const bool raw = false);
uint8_t* getRaw(void);
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
uint8_t getStateLength(void);
static bool validChecksum(uint8_t* state, const uint16_t length);
bool getPower(void);
void setOutsideQuiet(const bool on);
bool getOutsideQuiet(const bool raw = false);
uint8_t convertMode(const stdAc::opmode_t mode);
uint8_t convertFan(stdAc::fanspeed_t speed);
static stdAc::opmode_t toCommonMode(const uint8_t mode);
static stdAc::fanspeed_t toCommonFanSpeed(const uint8_t speed);
stdAc::state_t toCommon(void);
String toString(void);
#ifndef UNIT_TEST
private:
uint8_t remote_state[kFujitsuAcStateLength];
IRsend _irsend;
#else
IRsendTest _irsend;
#endif
uint8_t remote_state[kFujitsuAcStateLength];
uint8_t _temp;
uint8_t _fanSpeed;
uint8_t _mode;
@@ -116,7 +148,8 @@ class IRFujitsuAC {
fujitsu_ac_remote_model_t _model;
uint8_t _state_length;
uint8_t _state_length_short;
void buildState();
bool _outsideQuiet;
void buildState(void);
void buildFromState(const uint16_t length);
};
+1 -6
View File
@@ -1,4 +1,5 @@
// Copyright 2018 David Conran
// G.I. Cable
#define __STDC_LIMIT_MACROS
#include <stdint.h>
@@ -7,12 +8,6 @@
#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
+3 -9
View File
@@ -1,18 +1,12 @@
// Copyright 2016 Hisham Khalifa
// Copyright 2017 David Conran
#include <algorithm>
#include "IRsend.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
// G L O O BBBBB A A L C A A C HHHHHH EEEE
// G GG L O O B BB AAAAAA L C C AAAAAA C C H H E
// GGGGG LLLLLL OOOO BBBBB A A LLLLLL CCCC A A CCCC H H EEEEEE
// Global Cache IR format sender originally added by Hisham Khalifa
// (http://www.hishamkhalifa.com)
#include <algorithm>
#include "IRsend.h"
// Constants
const uint16_t kGlobalCacheMaxRepeat = 50;
const uint32_t kGlobalCacheMinUsec = 80;
+494
View File
@@ -0,0 +1,494 @@
// Copyright 2019 ribeirodanielf
// Copyright 2019 David Conran
//
// Code to emulate Goodweather protocol compatible HVAC devices.
// Should be compatible with:
// * ZH/JT-03 remote control
//
#include "ir_Goodweather.h"
#include <algorithm>
#ifndef ARDUINO
#include <string>
#endif
#include "IRrecv.h"
#include "IRremoteESP8266.h"
#include "IRsend.h"
#include "IRutils.h"
#if SEND_GOODWEATHER
// Send a Goodweather message.
//
// Args:
// data: The raw message to be sent.
// nbits: Nr. of bits of data in the message. (Default is kGoodweatherBits)
// repeat: Nr. of times the message is to be repeated. (Default = 0).
//
// Status: ALPHA / Untested.
//
// Ref:
// https://github.com/markszabo/IRremoteESP8266/issues/697
void IRsend::sendGoodweather(const uint64_t data, const uint16_t nbits,
const uint16_t repeat) {
if (nbits != kGoodweatherBits)
return; // Wrong nr. of bits to send a proper message.
// Set IR carrier frequency
enableIROut(38);
for (uint16_t r = 0; r <= repeat; r++) {
// Header
mark(kGoodweatherHdrMark);
space(kGoodweatherHdrSpace);
// Data
for (int16_t i = 0; i < nbits; i += 8) {
uint16_t chunk = (data >> i) & 0xFF; // Grab a byte at a time.
chunk = (~chunk) << 8 | chunk; // Prepend a inverted copy of the byte.
sendData(kGoodweatherBitMark, kGoodweatherOneSpace,
kGoodweatherBitMark, kGoodweatherZeroSpace,
chunk, 16, false);
}
// Footer
mark(kGoodweatherBitMark);
space(kGoodweatherHdrSpace);
mark(kGoodweatherBitMark);
space(kDefaultMessageGap);
}
}
#endif // SEND_GOODWEATHER
IRGoodweatherAc::IRGoodweatherAc(uint16_t pin) : _irsend(pin) { stateReset(); }
void IRGoodweatherAc::stateReset(void) {
}
void IRGoodweatherAc::begin(void) { _irsend.begin(); }
#if SEND_GOODWEATHER
void IRGoodweatherAc::send(const uint16_t repeat) {
_irsend.sendGoodweather(remote, kGoodweatherBits, repeat);
}
#endif // SEND_GOODWEATHER
uint64_t IRGoodweatherAc::getRaw(void) { return remote; }
void IRGoodweatherAc::setRaw(const uint64_t state) { remote = state; }
void IRGoodweatherAc::on(void) { this->setPower(true); }
void IRGoodweatherAc::off(void) { this->setPower(false); }
void IRGoodweatherAc::setPower(const bool on) {
this->setCommand(kGoodweatherCmdPower);
if (on)
remote |= kGoodweatherPowerMask;
else
remote &= ~kGoodweatherPowerMask;
}
bool IRGoodweatherAc::getPower(void) { return remote & kGoodweatherPowerMask; }
// Set the temp. in deg C
void IRGoodweatherAc::setTemp(const uint8_t temp) {
uint8_t new_temp = std::max(kGoodweatherTempMin, temp);
new_temp = std::min(kGoodweatherTempMax, new_temp);
if (new_temp > this->getTemp()) this->setCommand(kGoodweatherCmdUpTemp);
if (new_temp < this->getTemp()) this->setCommand(kGoodweatherCmdDownTemp);
remote &= ~kGoodweatherTempMask;
remote |= (uint64_t)(new_temp - kGoodweatherTempMin) << kGoodweatherBitTemp;
}
// Return the set temp. in deg C
uint8_t IRGoodweatherAc::getTemp(void) {
return ((remote & kGoodweatherTempMask) >> kGoodweatherBitTemp) +
kGoodweatherTempMin;
}
// Set the speed of the fan
void IRGoodweatherAc::setFan(const uint8_t speed) {
switch (speed) {
case kGoodweatherFanAuto:
case kGoodweatherFanLow:
case kGoodweatherFanMed:
case kGoodweatherFanHigh:
this->setCommand(kGoodweatherCmdFan);
remote &= ~kGoodweatherFanMask;
remote |= ((uint64_t)speed << kGoodweatherBitFan);
break;
default:
this->setFan(kGoodweatherFanAuto);
}
}
uint8_t IRGoodweatherAc::getFan() {
return (remote & kGoodweatherFanMask) >> kGoodweatherBitFan;
}
void IRGoodweatherAc::setMode(const uint8_t mode) {
switch (mode) {
case kGoodweatherAuto:
case kGoodweatherDry:
case kGoodweatherCool:
case kGoodweatherFan:
case kGoodweatherHeat:
this->setCommand(kGoodweatherCmdMode);
remote &= ~kGoodweatherModeMask;
remote |= (uint64_t)mode << kGoodweatherBitMode;
break;
default:
// If we get an unexpected mode, default to AUTO.
this->setMode(kGoodweatherAuto);
}
}
uint8_t IRGoodweatherAc::getMode() {
return (remote & kGoodweatherModeMask) >> kGoodweatherBitMode;
}
void IRGoodweatherAc::setLight(const bool toggle) {
this->setCommand(kGoodweatherCmdLight);
if (toggle)
remote |= kGoodweatherLightMask;
else
remote &= ~kGoodweatherLightMask;
}
bool IRGoodweatherAc::getLight() { return remote & kGoodweatherLightMask; }
void IRGoodweatherAc::setSleep(const bool toggle) {
this->setCommand(kGoodweatherCmdSleep);
if (toggle)
remote |= kGoodweatherSleepMask;
else
remote &= ~kGoodweatherSleepMask;
}
bool IRGoodweatherAc::getSleep() { return remote & kGoodweatherSleepMask; }
void IRGoodweatherAc::setTurbo(const bool toggle) {
this->setCommand(kGoodweatherCmdTurbo);
if (toggle)
remote |= kGoodweatherTurboMask;
else
remote &= ~kGoodweatherTurboMask;
}
bool IRGoodweatherAc::getTurbo() { return remote & kGoodweatherTurboMask; }
void IRGoodweatherAc::setSwing(const uint8_t speed) {
switch (speed) {
case kGoodweatherSwingOff:
case kGoodweatherSwingSlow:
case kGoodweatherSwingFast:
this->setCommand(kGoodweatherCmdSwing);
remote &= ~kGoodweatherSwingMask;
remote |= ((uint64_t)speed << kGoodweatherBitSwing);
break;
default:
this->setSwing(kGoodweatherSwingOff);
}
}
uint8_t IRGoodweatherAc::getSwing() {
return (remote & kGoodweatherSwingMask) >> kGoodweatherBitSwing;
}
void IRGoodweatherAc::setCommand(const uint8_t cmd) {
if (cmd <= kGoodweatherCmdLight) {
remote &= ~kGoodweatherCommandMask;
remote |= (cmd << kGoodweatherBitCommand);
}
}
uint8_t IRGoodweatherAc::getCommand() {
return (remote & kGoodweatherCommandMask) >> kGoodweatherBitCommand;
}
// Convert a standard A/C mode into its native mode.
uint8_t IRGoodweatherAc::convertMode(const stdAc::opmode_t mode) {
switch (mode) {
case stdAc::opmode_t::kCool:
return kGoodweatherCool;
case stdAc::opmode_t::kHeat:
return kGoodweatherHeat;
case stdAc::opmode_t::kDry:
return kGoodweatherDry;
case stdAc::opmode_t::kFan:
return kGoodweatherFan;
default:
return kGoodweatherAuto;
}
}
// Convert a standard A/C Fan speed into its native fan speed.
uint8_t IRGoodweatherAc::convertFan(const stdAc::fanspeed_t speed) {
switch (speed) {
case stdAc::fanspeed_t::kMin:
case stdAc::fanspeed_t::kLow:
return kGoodweatherFanLow;
case stdAc::fanspeed_t::kMedium:
return kGoodweatherFanMed;
case stdAc::fanspeed_t::kHigh:
case stdAc::fanspeed_t::kMax:
return kGoodweatherFanHigh;
default:
return kGoodweatherFanAuto;
}
}
// Convert a standard A/C Vertical Swing into its native version.
uint8_t IRGoodweatherAc::convertSwingV(const stdAc::swingv_t swingv) {
switch (swingv) {
case stdAc::swingv_t::kHighest:
case stdAc::swingv_t::kHigh:
case stdAc::swingv_t::kMiddle:
return kGoodweatherSwingFast;
case stdAc::swingv_t::kLow:
case stdAc::swingv_t::kLowest:
case stdAc::swingv_t::kAuto:
return kGoodweatherSwingSlow;
default:
return kGoodweatherSwingOff;
}
}
// Convert a native mode to it's common equivalent.
stdAc::opmode_t IRGoodweatherAc::toCommonMode(const uint8_t mode) {
switch (mode) {
case kGoodweatherCool: return stdAc::opmode_t::kCool;
case kGoodweatherHeat: return stdAc::opmode_t::kHeat;
case kGoodweatherDry: return stdAc::opmode_t::kDry;
case kGoodweatherFan: return stdAc::opmode_t::kFan;
default: return stdAc::opmode_t::kAuto;
}
}
// Convert a native fan speed to it's common equivalent.
stdAc::fanspeed_t IRGoodweatherAc::toCommonFanSpeed(const uint8_t speed) {
switch (speed) {
case kGoodweatherFanHigh: return stdAc::fanspeed_t::kMax;
case kGoodweatherFanMed: return stdAc::fanspeed_t::kMedium;
case kGoodweatherFanLow: return stdAc::fanspeed_t::kMin;
default: return stdAc::fanspeed_t::kAuto;
}
}
// Convert the A/C state to it's common equivalent.
stdAc::state_t IRGoodweatherAc::toCommon(void) {
stdAc::state_t result;
result.protocol = decode_type_t::GOODWEATHER;
result.power = this->getPower();
result.mode = this->toCommonMode(this->getMode());
result.celsius = true;
result.degrees = this->getTemp();
result.fanspeed = this->toCommonFanSpeed(this->getFan());
result.swingv = this->getSwing() == kGoodweatherSwingOff ?
stdAc::swingv_t::kOff : stdAc::swingv_t::kAuto;
result.turbo = this->getTurbo();
result.light = this->getLight();
result.sleep = this->getSleep() ? 0: -1;
// Not supported.
result.model = -1;
result.swingh = stdAc::swingh_t::kOff;
result.quiet = false;
result.econo = false;
result.filter = false;
result.clean = false;
result.beep = false;
result.clock = -1;
return result;
}
// Convert the internal state into a human readable string.
String IRGoodweatherAc::toString() {
String result = "";
result.reserve(150); // Reserve some heap for the string to reduce fragging.
result += F("Power: ");
result += this->getPower() ? F("On") : F("Off");
result += F(", Mode: ");
result += uint64ToString(this->getMode());
switch (this->getMode()) {
case kGoodweatherAuto:
result += F(" (AUTO)");
break;
case kGoodweatherCool:
result += F(" (COOL)");
break;
case kGoodweatherHeat:
result += F(" (HEAT)");
break;
case kGoodweatherDry:
result += F(" (DRY)");
break;
case kGoodweatherFan:
result += F(" (FAN)");
break;
default:
result += F(" (UNKNOWN)");
}
result += F(", Temp: ");
result += uint64ToString(this->getTemp());
result += F("C, Fan: ");
result += uint64ToString(this->getFan());
switch (this->getFan()) {
case kGoodweatherFanAuto:
result += F(" (AUTO)");
break;
case kGoodweatherFanHigh:
result += F(" (HIGH)");
break;
case kGoodweatherFanMed:
result += F(" (MED)");
break;
case kGoodweatherFanLow:
result += F(" (LOW)");
break;
}
result += F(", Turbo: ");
result += this->getTurbo() ? F("Toggle") : F("-");
result += F(", Light: ");
result += this->getLight() ? F("Toggle") : F("-");
result += F(", Sleep: ");
result += this->getSleep() ? F("Toggle") : F("-");
result += F(", Swing: ");
result += uint64ToString(this->getSwing());
switch (this->getSwing()) {
case kGoodweatherSwingFast:
result += F(" (Fast)");
break;
case kGoodweatherSwingSlow:
result += F(" (Slow)");
break;
case kGoodweatherSwingOff:
result += F(" (Off)");
break;
default:
result += F(" (UNKNOWN)");
}
result += F(", Command: ");
result += uint64ToString(this->getCommand());
switch (this->getCommand()) {
case kGoodweatherCmdPower:
result += F(" (Power)");
break;
case kGoodweatherCmdMode:
result += F(" (Mode)");
break;
case kGoodweatherCmdUpTemp:
result += F(" (Temp Up)");
break;
case kGoodweatherCmdDownTemp:
result += F(" (Temp Down)");
break;
case kGoodweatherCmdSwing:
result += F(" (Swing)");
break;
case kGoodweatherCmdFan:
result += F(" (Fan)");
break;
case kGoodweatherCmdTimer:
result += F(" (Timer)");
break;
case kGoodweatherCmdAirFlow:
result += F(" (Air Flow)");
break;
case kGoodweatherCmdHold:
result += F(" (Hold)");
break;
case kGoodweatherCmdSleep:
result += F(" (Sleep)");
break;
case kGoodweatherCmdTurbo:
result += F(" (Turbo)");
break;
case kGoodweatherCmdLight:
result += F(" (Light)");
break;
default:
result += F(" (UNKNOWN)");
}
return result;
}
#if DECODE_GOODWEATHER
// Decode the supplied Goodweather 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 kGoodweatherBits.
// 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::decodeGoodweather(decode_results* results,
const uint16_t nbits,
const bool strict) {
if (results->rawlen < 2 * (2 * nbits) + kHeader + 2 * kFooter - 1)
return false; // Can't possibly be a valid Goodweather message.
if (strict && nbits != kGoodweatherBits)
return false; // Not strictly a Goodweather message.
uint64_t dataSoFar = 0;
uint16_t dataBitsSoFar = 0;
uint16_t offset = kStartOffset;
match_result_t data_result;
// Header
if (!matchMark(results->rawbuf[offset++], kGoodweatherHdrMark)) return false;
if (!matchSpace(results->rawbuf[offset++], kGoodweatherHdrSpace))
return false;
// Data
for (; offset <= results->rawlen - 32 && dataBitsSoFar < nbits;
dataBitsSoFar += 8) {
DPRINT("DEBUG: Attempting Byte #");
DPRINTLN(dataBitsSoFar / 8);
// Read in a byte at a time.
// Normal first.
data_result = matchData(&(results->rawbuf[offset]), 8,
kGoodweatherBitMark, kGoodweatherOneSpace,
kGoodweatherBitMark, kGoodweatherZeroSpace,
kTolerance, kMarkExcess, false);
if (data_result.success == false) return false;
DPRINTLN("DEBUG: Normal byte read okay.");
offset += data_result.used;
uint8_t data = (uint8_t)data_result.data;
// Then inverted.
data_result = matchData(&(results->rawbuf[offset]), 8,
kGoodweatherBitMark, kGoodweatherOneSpace,
kGoodweatherBitMark, kGoodweatherZeroSpace,
kTolerance, kMarkExcess, false);
if (data_result.success == false) return false;
DPRINTLN("DEBUG: Inverted byte read okay.");
offset += data_result.used;
uint8_t inverted = (uint8_t)data_result.data;
DPRINT("DEBUG: data = ");
DPRINTLN((uint16_t)data);
DPRINT("DEBUG: inverted = ");
DPRINTLN((uint16_t)inverted);
if (data != (inverted ^ 0xFF)) return false; // Data integrity failed.
dataSoFar |= (uint64_t)data << dataBitsSoFar;
}
// Footer.
if (!matchMark(results->rawbuf[offset++], kGoodweatherBitMark)) return false;
if (!matchSpace(results->rawbuf[offset++], kGoodweatherHdrSpace))
return false;
if (!matchMark(results->rawbuf[offset++], kGoodweatherBitMark)) return false;
if (offset <= results->rawlen &&
!matchAtLeast(results->rawbuf[offset], kGoodweatherHdrSpace))
return false;
// Compliance
if (strict && (dataBitsSoFar != kGoodweatherBits)) return false;
// Success
results->decode_type = decode_type_t::GOODWEATHER;
results->bits = dataBitsSoFar;
results->value = dataSoFar;
results->address = 0;
results->command = 0;
return true;
}
#endif // DECODE_GOODWEATHER
+136
View File
@@ -0,0 +1,136 @@
// Goodweather A/C
//
// Copyright 2019 ribeirodanielf
// Copyright 2019 David Conran
// Supports:
// Brand: Goodweather, Model: ZH/JT-03 remote
#ifndef IR_GOODWEATHER_H_
#define IR_GOODWEATHER_H_
#define __STDC_LIMIT_MACROS
#include <stdint.h>
#ifndef UNIT_TEST
#include <Arduino.h>
#endif
#include "IRremoteESP8266.h"
#include "IRsend.h"
#ifdef UNIT_TEST
#include "IRsend_test.h"
#endif
// Ref:
// https://github.com/markszabo/IRremoteESP8266/issues/697
// Constants
// Timing
const uint16_t kGoodweatherBitMark = 640;
const uint16_t kGoodweatherOneSpace = 580;
const uint16_t kGoodweatherZeroSpace = 1600;
const uint16_t kGoodweatherHdrMark = 6800;
const uint16_t kGoodweatherHdrSpace = 6800;
// Masks
const uint8_t kGoodweatherBitLight = 8;
const uint64_t kGoodweatherLightMask = 0x1ULL << kGoodweatherBitLight;
const uint8_t kGoodweatherBitTurbo = kGoodweatherBitLight + 3; // 11
const uint64_t kGoodweatherTurboMask = 0x1ULL << kGoodweatherBitTurbo;
const uint8_t kGoodweatherBitCommand = kGoodweatherBitTurbo + 5; // 16
const uint64_t kGoodweatherCommandMask = 0xFULL << kGoodweatherBitCommand;
const uint8_t kGoodweatherBitSleep = kGoodweatherBitCommand + 8; // 24
const uint64_t kGoodweatherSleepMask = 0x1ULL << kGoodweatherBitSleep;
const uint8_t kGoodweatherBitPower = kGoodweatherBitSleep + 1; // 25
const uint64_t kGoodweatherPowerMask = 0x1ULL << kGoodweatherBitPower;
const uint8_t kGoodweatherBitSwing = kGoodweatherBitPower + 1; // 26
const uint64_t kGoodweatherSwingMask = 0x3ULL << kGoodweatherBitSwing;
const uint8_t kGoodweatherBitFan = kGoodweatherBitSwing + 3; // 29
const uint64_t kGoodweatherFanMask = 0x3ULL << kGoodweatherBitFan;
const uint8_t kGoodweatherBitTemp = kGoodweatherBitFan + 3; // 32
const uint64_t kGoodweatherTempMask = 0xFULL << kGoodweatherBitTemp;
const uint8_t kGoodweatherBitMode = kGoodweatherBitTemp + 5; // 37
const uint64_t kGoodweatherModeMask = 0x7ULL << kGoodweatherBitMode;
// Modes
const uint8_t kGoodweatherAuto = 0b000;
const uint8_t kGoodweatherCool = 0b001;
const uint8_t kGoodweatherDry = 0b010;
const uint8_t kGoodweatherFan = 0b011;
const uint8_t kGoodweatherHeat = 0b100;
const uint8_t kGoodweatherSwingFast = 0b00;
const uint8_t kGoodweatherSwingSlow = 0b01;
const uint8_t kGoodweatherSwingOff = 0b10;
// Fan Control
const uint8_t kGoodweatherFanAuto = 0b00;
const uint8_t kGoodweatherFanHigh = 0b01;
const uint8_t kGoodweatherFanMed = 0b10;
const uint8_t kGoodweatherFanLow = 0b11;
// Temperature
const uint8_t kGoodweatherTempMin = 16; // Celsius
const uint8_t kGoodweatherTempMax = 31; // Celsius
// Commands
const uint8_t kGoodweatherCmdPower = 0x00;
const uint8_t kGoodweatherCmdMode = 0x01;
const uint8_t kGoodweatherCmdUpTemp = 0x02;
const uint8_t kGoodweatherCmdDownTemp = 0x03;
const uint8_t kGoodweatherCmdSwing = 0x04;
const uint8_t kGoodweatherCmdFan = 0x05;
const uint8_t kGoodweatherCmdTimer = 0x06;
const uint8_t kGoodweatherCmdAirFlow = 0x07;
const uint8_t kGoodweatherCmdHold = 0x08;
const uint8_t kGoodweatherCmdSleep = 0x09;
const uint8_t kGoodweatherCmdTurbo = 0x0A;
const uint8_t kGoodweatherCmdLight = 0x0B;
// Classes
class IRGoodweatherAc {
public:
explicit IRGoodweatherAc(uint16_t pin);
void stateReset(void);
#if SEND_GOODWEATHER
void send(const uint16_t repeat = kGoodweatherMinRepeat);
uint8_t calibrate(void) { return _irsend.calibrate(); }
#endif // SEND_GOODWEATHER
void begin(void);
void on(void);
void off(void);
void setPower(const bool on);
bool getPower(void);
void setTemp(const uint8_t temp);
uint8_t getTemp(void);
void setFan(const uint8_t speed);
uint8_t getFan(void);
void setMode(const uint8_t mode);
uint8_t getMode();
void setSwing(const uint8_t speed);
uint8_t getSwing(void);
void setSleep(const bool toggle);
bool getSleep(void);
void setTurbo(const bool toggle);
bool getTurbo(void);
void setLight(const bool toggle);
bool getLight(void);
void setCommand(const uint8_t cmd);
uint8_t getCommand(void);
uint64_t getRaw(void);
void setRaw(const uint64_t state);
uint8_t convertMode(const stdAc::opmode_t mode);
uint8_t convertFan(const stdAc::fanspeed_t speed);
uint8_t convertSwingV(const stdAc::swingv_t swingv);
static stdAc::opmode_t toCommonMode(const uint8_t mode);
static stdAc::fanspeed_t toCommonFanSpeed(const uint8_t speed);
stdAc::state_t toCommon(void);
String toString();
#ifndef UNIT_TEST
private:
IRsend _irsend;
#else
IRsendTest _irsend;
#endif
uint64_t remote; // The state of the IR remote in IR code form.
};
#endif // IR_GOODWEATHER_H_
+188 -81
View File
@@ -18,16 +18,10 @@
#include "IRutils.h"
#include "ir_Kelvinator.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
// Ref: https://github.com/ToniA/arduino-heatpumpir/blob/master/GreeHeatpumpIR.h
const uint16_t kGreeHdrMark = 9000;
const uint16_t kGreeHdrSpace = 4000;
const uint16_t kGreeHdrSpace = 4500; // See #684 and real example in unit tests
const uint16_t kGreeBitMark = 620;
const uint16_t kGreeOneSpace = 1600;
const uint16_t kGreeZeroSpace = 540;
@@ -47,7 +41,8 @@ const uint8_t kGreeBlockFooterBits = 3;
//
// Ref:
// https://github.com/ToniA/arduino-heatpumpir/blob/master/GreeHeatpumpIR.cpp
void IRsend::sendGree(unsigned char data[], uint16_t nbytes, uint16_t repeat) {
void IRsend::sendGree(const unsigned char data[], const uint16_t nbytes,
const uint16_t repeat) {
if (nbytes < kGreeStateLength)
return; // Not enough bytes to send a proper message.
@@ -59,7 +54,7 @@ void IRsend::sendGree(unsigned char data[], uint16_t nbytes, uint16_t repeat) {
// Footer #1
sendGeneric(0, 0, // No Header
kGreeBitMark, kGreeOneSpace, kGreeBitMark, kGreeZeroSpace,
kGreeBitMark, kGreeMsgSpace, 0b010, 3, 38, true, 0, false);
kGreeBitMark, kGreeMsgSpace, 0b010, 3, 38, false, 0, 50);
// Block #2
sendGeneric(0, 0, // No Header for Block #2
@@ -80,7 +75,8 @@ void IRsend::sendGree(unsigned char data[], uint16_t nbytes, uint16_t repeat) {
//
// Ref:
// https://github.com/ToniA/arduino-heatpumpir/blob/master/GreeHeatpumpIR.cpp
void IRsend::sendGree(uint64_t data, uint16_t nbits, uint16_t repeat) {
void IRsend::sendGree(const uint64_t data, const uint16_t nbits,
const uint16_t repeat) {
if (nbits != kGreeBits)
return; // Wrong nr. of bits to send a proper message.
// Set IR carrier frequency
@@ -112,7 +108,7 @@ void IRsend::sendGree(uint64_t data, uint16_t nbits, uint16_t repeat) {
IRGreeAC::IRGreeAC(uint16_t pin) : _irsend(pin) { stateReset(); }
void IRGreeAC::stateReset() {
void IRGreeAC::stateReset(void) {
// This resets to a known-good state to Power Off, Fan Auto, Mode Auto, 25C.
for (uint8_t i = 0; i < kGreeStateLength; i++) remote_state[i] = 0x0;
remote_state[1] = 0x09;
@@ -122,11 +118,11 @@ void IRGreeAC::stateReset() {
remote_state[7] = 0x50;
}
void IRGreeAC::fixup() {
void IRGreeAC::fixup(void) {
checksum(); // Calculate the checksums
}
void IRGreeAC::begin() { _irsend.begin(); }
void IRGreeAC::begin(void) { _irsend.begin(); }
#if SEND_GREE
void IRGreeAC::send(const uint16_t repeat) {
@@ -135,12 +131,12 @@ void IRGreeAC::send(const uint16_t repeat) {
}
#endif // SEND_GREE
uint8_t* IRGreeAC::getRaw() {
uint8_t* IRGreeAC::getRaw(void) {
fixup(); // Ensure correct settings before sending.
return remote_state;
}
void IRGreeAC::setRaw(uint8_t new_code[]) {
void IRGreeAC::setRaw(const uint8_t new_code[]) {
for (uint8_t i = 0; i < kGreeStateLength; i++) {
remote_state[i] = new_code[i];
}
@@ -167,24 +163,24 @@ bool IRGreeAC::validChecksum(const uint8_t state[], const uint16_t length) {
return false;
}
void IRGreeAC::on() {
void IRGreeAC::on(void) {
remote_state[0] |= kGreePower1Mask;
remote_state[2] |= kGreePower2Mask;
}
void IRGreeAC::off() {
void IRGreeAC::off(void) {
remote_state[0] &= ~kGreePower1Mask;
remote_state[2] &= ~kGreePower2Mask;
}
void IRGreeAC::setPower(const bool state) {
if (state)
on();
void IRGreeAC::setPower(const bool on) {
if (on)
this->on();
else
off();
this->off();
}
bool IRGreeAC::getPower() {
bool IRGreeAC::getPower(void) {
return (remote_state[0] & kGreePower1Mask) &&
(remote_state[2] & kGreePower2Mask);
}
@@ -198,7 +194,7 @@ void IRGreeAC::setTemp(const uint8_t temp) {
}
// Return the set temp. in deg C
uint8_t IRGreeAC::getTemp() {
uint8_t IRGreeAC::getTemp(void) {
return ((remote_state[1] & 0xFU) + kGreeMinTemp);
}
@@ -212,7 +208,7 @@ void IRGreeAC::setFan(const uint8_t speed) {
remote_state[0] |= (fan << 4);
}
uint8_t IRGreeAC::getFan() { return ((remote_state[0] & kGreeFanMask) >> 4); }
uint8_t IRGreeAC::getFan(void) { return (remote_state[0] & kGreeFanMask) >> 4; }
void IRGreeAC::setMode(const uint8_t new_mode) {
uint8_t mode = new_mode;
@@ -237,35 +233,35 @@ void IRGreeAC::setMode(const uint8_t new_mode) {
remote_state[0] |= mode;
}
uint8_t IRGreeAC::getMode() { return (remote_state[0] & kGreeModeMask); }
uint8_t IRGreeAC::getMode(void) { return (remote_state[0] & kGreeModeMask); }
void IRGreeAC::setLight(const bool state) {
void IRGreeAC::setLight(const bool on) {
remote_state[2] &= ~kGreeLightMask;
remote_state[2] |= (state << 5);
remote_state[2] |= (on << 5);
}
bool IRGreeAC::getLight() { return remote_state[2] & kGreeLightMask; }
bool IRGreeAC::getLight(void) { return remote_state[2] & kGreeLightMask; }
void IRGreeAC::setXFan(const bool state) {
void IRGreeAC::setXFan(const bool on) {
remote_state[2] &= ~kGreeXfanMask;
remote_state[2] |= (state << 7);
remote_state[2] |= (on << 7);
}
bool IRGreeAC::getXFan() { return remote_state[2] & kGreeXfanMask; }
bool IRGreeAC::getXFan(void) { return remote_state[2] & kGreeXfanMask; }
void IRGreeAC::setSleep(const bool state) {
void IRGreeAC::setSleep(const bool on) {
remote_state[0] &= ~kGreeSleepMask;
remote_state[0] |= (state << 7);
remote_state[0] |= (on << 7);
}
bool IRGreeAC::getSleep() { return remote_state[0] & kGreeSleepMask; }
bool IRGreeAC::getSleep(void) { return remote_state[0] & kGreeSleepMask; }
void IRGreeAC::setTurbo(const bool state) {
void IRGreeAC::setTurbo(const bool on) {
remote_state[2] &= ~kGreeTurboMask;
remote_state[2] |= (state << 4);
remote_state[2] |= (on << 4);
}
bool IRGreeAC::getTurbo() { return remote_state[2] & kGreeTurboMask; }
bool IRGreeAC::getTurbo(void) { return remote_state[2] & kGreeTurboMask; }
void IRGreeAC::setSwingVertical(const bool automatic, const uint8_t position) {
remote_state[0] &= ~kGreeSwingAutoMask;
@@ -297,90 +293,201 @@ void IRGreeAC::setSwingVertical(const bool automatic, const uint8_t position) {
remote_state[4] |= new_position;
}
bool IRGreeAC::getSwingVerticalAuto() {
bool IRGreeAC::getSwingVerticalAuto(void) {
return remote_state[0] & kGreeSwingAutoMask;
}
uint8_t IRGreeAC::getSwingVerticalPosition() {
uint8_t IRGreeAC::getSwingVerticalPosition(void) {
return remote_state[4] & kGreeSwingPosMask;
}
// 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";
// Convert a standard A/C mode into its native mode.
uint8_t IRGreeAC::convertMode(const stdAc::opmode_t mode) {
switch (mode) {
case stdAc::opmode_t::kCool:
return kGreeCool;
case stdAc::opmode_t::kHeat:
return kGreeHeat;
case stdAc::opmode_t::kDry:
return kGreeDry;
case stdAc::opmode_t::kFan:
return kGreeFan;
default:
return kGreeAuto;
}
}
// Convert a standard A/C Fan speed into its native fan speed.
uint8_t IRGreeAC::convertFan(const stdAc::fanspeed_t speed) {
switch (speed) {
case stdAc::fanspeed_t::kMin:
return kGreeFanMin;
case stdAc::fanspeed_t::kLow:
case stdAc::fanspeed_t::kMedium:
return kGreeFanMax - 1;
case stdAc::fanspeed_t::kHigh:
case stdAc::fanspeed_t::kMax:
return kGreeFanMax;
default:
return kGreeFanAuto;
}
}
// Convert a standard A/C Vertical Swing into its native version.
uint8_t IRGreeAC::convertSwingV(const stdAc::swingv_t swingv) {
switch (swingv) {
case stdAc::swingv_t::kHighest:
return kGreeSwingUp;
case stdAc::swingv_t::kHigh:
return kGreeSwingMiddleUp;
case stdAc::swingv_t::kMiddle:
return kGreeSwingMiddle;
case stdAc::swingv_t::kLow:
return kGreeSwingMiddleDown;
case stdAc::swingv_t::kLowest:
return kGreeSwingDown;
default:
return kGreeSwingAuto;
}
}
// Convert a native mode to it's common equivalent.
stdAc::opmode_t IRGreeAC::toCommonMode(const uint8_t mode) {
switch (mode) {
case kGreeCool: return stdAc::opmode_t::kCool;
case kGreeHeat: return stdAc::opmode_t::kHeat;
case kGreeDry: return stdAc::opmode_t::kDry;
case kGreeFan: return stdAc::opmode_t::kFan;
default: return stdAc::opmode_t::kAuto;
}
}
// Convert a native fan speed to it's common equivalent.
stdAc::fanspeed_t IRGreeAC::toCommonFanSpeed(const uint8_t speed) {
switch (speed) {
case kGreeFanMax: return stdAc::fanspeed_t::kMax;
case kGreeFanMax - 1: return stdAc::fanspeed_t::kMedium;
case kGreeFanMin: return stdAc::fanspeed_t::kMin;
default: return stdAc::fanspeed_t::kAuto;
}
}
// Convert a native vertical swing to it's common equivalent.
stdAc::swingv_t IRGreeAC::toCommonSwingV(const uint8_t pos) {
switch (pos) {
case kGreeSwingUp: return stdAc::swingv_t::kHighest;
case kGreeSwingMiddleUp: return stdAc::swingv_t::kHigh;
case kGreeSwingMiddle: return stdAc::swingv_t::kMiddle;
case kGreeSwingMiddleDown: return stdAc::swingv_t::kLow;
case kGreeSwingDown: return stdAc::swingv_t::kLowest;
default: return stdAc::swingv_t::kAuto;
}
}
// Convert the A/C state to it's common equivalent.
stdAc::state_t IRGreeAC::toCommon(void) {
stdAc::state_t result;
result.protocol = decode_type_t::GREE;
result.model = -1; // No models used.
result.power = this->getPower();
result.mode = this->toCommonMode(this->getMode());
result.celsius = true;
result.degrees = this->getTemp();
result.fanspeed = this->toCommonFanSpeed(this->getFan());
if (this->getSwingVerticalAuto())
result.swingv = stdAc::swingv_t::kAuto;
else
result += "Off";
result += ", Mode: " + uint64ToString(getMode());
result.swingv = this->toCommonSwingV(this->getSwingVerticalPosition());
result.turbo = this->getTurbo();
result.light = this->getLight();
result.clean = this->getXFan();
result.sleep = this->getSleep() ? 0 : -1;
// Not supported.
result.swingh = stdAc::swingh_t::kOff;
result.quiet = false;
result.econo = false;
result.filter = false;
result.beep = false;
result.clock = -1;
return result;
}
// Convert the internal state into a human readable string.
String IRGreeAC::toString(void) {
String result = "";
result.reserve(150); // Reserve some heap for the string to reduce fragging.
result += F("Power: ");
if (getPower())
result += F("On");
else
result += F("Off");
result += F(", Mode: ");
result += uint64ToString(getMode());
switch (getMode()) {
case kGreeAuto:
result += " (AUTO)";
result += F(" (AUTO)");
break;
case kGreeCool:
result += " (COOL)";
result += F(" (COOL)");
break;
case kGreeHeat:
result += " (HEAT)";
result += F(" (HEAT)");
break;
case kGreeDry:
result += " (DRY)";
result += F(" (DRY)");
break;
case kGreeFan:
result += " (FAN)";
result += F(" (FAN)");
break;
default:
result += " (UNKNOWN)";
result += F(" (UNKNOWN)");
}
result += ", Temp: " + uint64ToString(getTemp()) + "C";
result += ", Fan: " + uint64ToString(getFan());
result += F(", Temp: ");
result += uint64ToString(getTemp());
result += F("C, Fan: ");
result += uint64ToString(getFan());
switch (getFan()) {
case 0:
result += " (AUTO)";
result += F(" (AUTO)");
break;
case kGreeFanMax:
result += " (MAX)";
result += F(" (MAX)");
break;
}
result += ", Turbo: ";
result += F(", Turbo: ");
if (getTurbo())
result += "On";
result += F("On");
else
result += "Off";
result += ", XFan: ";
result += F("Off");
result += F(", XFan: ");
if (getXFan())
result += "On";
result += F("On");
else
result += "Off";
result += ", Light: ";
result += F("Off");
result += F(", Light: ");
if (getLight())
result += "On";
result += F("On");
else
result += "Off";
result += ", Sleep: ";
result += F("Off");
result += F(", Sleep: ");
if (getSleep())
result += "On";
result += F("On");
else
result += "Off";
result += ", Swing Vertical Mode: ";
result += F("Off");
result += F(", Swing Vertical Mode: ");
if (getSwingVerticalAuto())
result += "Auto";
result += F("Auto");
else
result += "Manual";
result +=
", Swing Vertical Pos: " + uint64ToString(getSwingVerticalPosition());
result += F("Manual");
result += F(", Swing Vertical Pos: ");
result += uint64ToString(getSwingVerticalPosition());
switch (getSwingVerticalPosition()) {
case kGreeSwingLastPos:
result += " (Last Pos)";
result += F(" (Last Pos)");
break;
case kGreeSwingAuto:
result += " (Auto)";
result += F(" (Auto)");
break;
}
return result;
+46 -39
View File
@@ -1,6 +1,9 @@
// Kelvinator A/C
//
// Copyright 2016 David Conran
// Gree A/C
//
// Supports:
// Brand: Ultimate, Model: Heat Pump
// Brand: EKOKAI, Model: A/C
#ifndef IR_GREE_H_
#define IR_GREE_H_
@@ -9,17 +12,12 @@
#include <stdint.h>
#ifndef UNIT_TEST
#include <Arduino.h>
#else
#include <string>
#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
#ifdef UNIT_TEST
#include "IRsend_test.h"
#endif
// Constants
const uint8_t kGreeAuto = 0;
@@ -44,6 +42,8 @@ const uint8_t kGreeSwingPosMask = 0b00001111;
const uint8_t kGreeMinTemp = 16; // Celsius
const uint8_t kGreeMaxTemp = 30; // Celsius
const uint8_t kGreeFanAuto = 0;
const uint8_t kGreeFanMin = 1;
const uint8_t kGreeFanMax = 3;
const uint8_t kGreeSwingLastPos = 0b00000000;
@@ -82,49 +82,56 @@ class IRGreeAC {
public:
explicit IRGreeAC(uint16_t pin);
void stateReset();
void stateReset(void);
#if SEND_GREE
void send(const uint16_t repeat = kGreeDefaultRepeat);
uint8_t calibrate(void) { return _irsend.calibrate(); }
#endif // SEND_GREE
void begin();
void on();
void off();
void setPower(const bool state);
bool getPower();
void begin(void);
void on(void);
void off(void);
void setPower(const bool on);
bool getPower(void);
void setTemp(const uint8_t temp);
uint8_t getTemp();
uint8_t getTemp(void);
void setFan(const uint8_t speed);
uint8_t getFan();
uint8_t getFan(void);
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();
uint8_t getMode(void);
void setLight(const bool on);
bool getLight(void);
void setXFan(const bool on);
bool getXFan(void);
void setSleep(const bool on);
bool getSleep(void);
void setTurbo(const bool on);
bool getTurbo(void);
void setSwingVertical(const bool automatic, const uint8_t position);
bool getSwingVerticalAuto();
uint8_t getSwingVerticalPosition();
uint8_t* getRaw();
void setRaw(uint8_t new_code[]);
bool getSwingVerticalAuto(void);
uint8_t getSwingVerticalPosition(void);
uint8_t convertMode(const stdAc::opmode_t mode);
uint8_t convertFan(const stdAc::fanspeed_t speed);
uint8_t convertSwingV(const stdAc::swingv_t swingv);
static stdAc::opmode_t toCommonMode(const uint8_t mode);
static stdAc::fanspeed_t toCommonFanSpeed(const uint8_t speed);
static stdAc::swingv_t toCommonSwingV(const uint8_t pos);
stdAc::state_t toCommon(void);
uint8_t* getRaw(void);
void setRaw(const uint8_t new_code[]);
static bool validChecksum(const uint8_t state[],
const uint16_t length = kGreeStateLength);
#ifdef ARDUINO
String toString();
#else
std::string toString();
#endif
String toString(void);
#ifndef UNIT_TEST
private:
IRsend _irsend;
#else // UNIT_TEST
IRsendTest _irsend;
#endif // UNIT_TEST
// The state of the IR remote in IR code form.
uint8_t remote_state[kGreeStateLength];
void checksum(const uint16_t length = kGreeStateLength);
void fixup();
IRsend _irsend;
void fixup(void);
};
#endif // IR_GREE_H_
+401 -176
View File
@@ -1,4 +1,5 @@
// Copyright 2018 crankyoldgit
// Code to emulate Haier protocol compatible devices.
// The specifics of reverse engineering the protocols details:
// * HSU07-HEA03 by kuzin2006.
// * YR-W02/HSU-09HMC203 by non7top.
@@ -6,18 +7,10 @@
#include "ir_Haier.h"
#ifndef UNIT_TEST
#include <Arduino.h>
#else
#include <string>
#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.
// * Haier YR-W02 Remote control
@@ -45,10 +38,10 @@ const uint32_t kHaierAcMinGap = 150000; // Completely made up value.
// nbytes: Nr. of bytes of data in the array. (>=kHaierACStateLength)
// repeat: Nr. of times the message is to be repeated. (Default = 0).
//
// Status: Beta / Probably working.
// Status: STABLE / Known to be working.
//
void IRsend::sendHaierAC(unsigned char data[], uint16_t nbytes,
uint16_t repeat) {
void IRsend::sendHaierAC(const unsigned char data[], const uint16_t nbytes,
const uint16_t repeat) {
if (nbytes < kHaierACStateLength) return;
for (uint16_t r = 0; r <= repeat; r++) {
@@ -74,16 +67,16 @@ void IRsend::sendHaierAC(unsigned char data[], uint16_t nbytes,
//
// Status: Alpha / Untested on a real device.
//
void IRsend::sendHaierACYRW02(unsigned char data[], uint16_t nbytes,
uint16_t repeat) {
void IRsend::sendHaierACYRW02(const unsigned char data[], const uint16_t nbytes,
const uint16_t repeat) {
if (nbytes >= kHaierACYRW02StateLength) sendHaierAC(data, nbytes, repeat);
}
#endif // SEND_HAIER_AC_YRW02
// Class for emulating a Haier HSU07-HEA03 remote
IRHaierAC::IRHaierAC(uint16_t pin) : _irsend(pin) { stateReset(); }
IRHaierAC::IRHaierAC(const uint16_t pin) : _irsend(pin) { stateReset(); }
void IRHaierAC::begin() { _irsend.begin(); }
void IRHaierAC::begin(void) { _irsend.begin(); }
#if SEND_HAIER_AC
void IRHaierAC::send(const uint16_t repeat) {
@@ -92,7 +85,7 @@ void IRHaierAC::send(const uint16_t repeat) {
}
#endif // SEND_HAIER_AC
void IRHaierAC::checksum() {
void IRHaierAC::checksum(void) {
remote_state[8] = sumBytes(remote_state, kHaierACStateLength - 1);
}
@@ -101,10 +94,12 @@ bool IRHaierAC::validChecksum(uint8_t state[], const uint16_t length) {
return (state[length - 1] == sumBytes(state, length - 1));
}
void IRHaierAC::stateReset() {
void IRHaierAC::stateReset(void) {
for (uint8_t i = 1; i < kHaierACStateLength; i++) remote_state[i] = 0x0;
remote_state[0] = kHaierAcPrefix;
remote_state[2] = 0b00100000;
remote_state[2] = 0x20;
remote_state[4] = 0x0C;
remote_state[5] = 0xC0;
setTemp(kHaierAcDefTemp);
setFan(kHaierAcFanAuto);
@@ -112,18 +107,18 @@ void IRHaierAC::stateReset() {
setCommand(kHaierAcCmdOn);
}
uint8_t* IRHaierAC::getRaw() {
uint8_t* IRHaierAC::getRaw(void) {
checksum();
return remote_state;
}
void IRHaierAC::setRaw(uint8_t new_code[]) {
void IRHaierAC::setRaw(const uint8_t new_code[]) {
for (uint8_t i = 0; i < kHaierACStateLength; i++) {
remote_state[i] = new_code[i];
}
}
void IRHaierAC::setCommand(uint8_t state) {
void IRHaierAC::setCommand(const uint8_t state) {
remote_state[1] &= 0b11110000;
switch (state) {
case kHaierAcCmdOff:
@@ -141,9 +136,9 @@ void IRHaierAC::setCommand(uint8_t state) {
}
}
uint8_t IRHaierAC::getCommand() { return remote_state[1] & (0b00001111); }
uint8_t IRHaierAC::getCommand(void) { return remote_state[1] & (0b00001111); }
void IRHaierAC::setFan(uint8_t speed) {
void IRHaierAC::setFan(const uint8_t speed) {
uint8_t new_speed = kHaierAcFanAuto;
switch (speed) {
case kHaierAcFanLow:
@@ -164,7 +159,7 @@ void IRHaierAC::setFan(uint8_t speed) {
remote_state[5] |= new_speed;
}
uint8_t IRHaierAC::getFan() {
uint8_t IRHaierAC::getFan(void) {
switch (remote_state[5] & 0b00000011) {
case 1:
return kHaierAcFanMed;
@@ -182,11 +177,13 @@ void IRHaierAC::setMode(uint8_t mode) {
setCommand(kHaierAcCmdMode);
if (mode > kHaierAcFan) // If out of range, default to auto mode.
new_mode = kHaierAcAuto;
remote_state[7] &= 0b00011111;
remote_state[7] |= (new_mode << 5);
remote_state[6] &= ~kHaierAcModeMask;
remote_state[6] |= (new_mode << 5);
}
uint8_t IRHaierAC::getMode() { return (remote_state[7] & 0b11100000) >> 5; }
uint8_t IRHaierAC::getMode(void) {
return (remote_state[6] & kHaierAcModeMask) >> 5;
}
void IRHaierAC::setTemp(const uint8_t celsius) {
uint8_t temp = celsius;
@@ -206,45 +203,47 @@ void IRHaierAC::setTemp(const uint8_t celsius) {
remote_state[1] |= ((temp - kHaierAcMinTemp) << 4);
}
uint8_t IRHaierAC::getTemp() {
uint8_t IRHaierAC::getTemp(void) {
return ((remote_state[1] & 0b11110000) >> 4) + kHaierAcMinTemp;
}
void IRHaierAC::setHealth(bool state) {
void IRHaierAC::setHealth(const bool on) {
setCommand(kHaierAcCmdHealth);
remote_state[4] &= 0b11011111;
remote_state[4] |= (state << 5);
remote_state[4] |= (on << 5);
}
bool IRHaierAC::getHealth(void) { return remote_state[4] & (1 << 5); }
void IRHaierAC::setSleep(bool state) {
void IRHaierAC::setSleep(const bool on) {
setCommand(kHaierAcCmdSleep);
remote_state[7] &= 0b10111111;
remote_state[7] |= (state << 6);
if (on)
remote_state[7] |= kHaierAcSleepBit;
else
remote_state[7] &= ~kHaierAcSleepBit;
}
bool IRHaierAC::getSleep(void) { return remote_state[7] & 0b01000000; }
bool IRHaierAC::getSleep(void) { return remote_state[7] & kHaierAcSleepBit; }
uint16_t IRHaierAC::getTime(const uint8_t ptr[]) {
return (ptr[0] & 0b00011111) * 60 + (ptr[1] & 0b00111111);
}
int16_t IRHaierAC::getOnTimer() {
int16_t IRHaierAC::getOnTimer(void) {
if (remote_state[3] & 0b10000000) // Check if the timer is turned on.
return getTime(remote_state + 6);
else
return -1;
}
int16_t IRHaierAC::getOffTimer() {
int16_t IRHaierAC::getOffTimer(void) {
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); }
uint16_t IRHaierAC::getCurrTime(void) { return getTime(remote_state + 2); }
void IRHaierAC::setTime(uint8_t ptr[], const uint16_t nr_mins) {
uint16_t mins = nr_mins;
@@ -270,7 +269,7 @@ void IRHaierAC::setOffTimer(const uint16_t nr_mins) {
setTime(remote_state + 4, nr_mins);
}
void IRHaierAC::cancelTimers() {
void IRHaierAC::cancelTimers(void) {
setCommand(kHaierAcCmdTimerCancel);
remote_state[3] &= 0b00111111;
}
@@ -279,7 +278,9 @@ void IRHaierAC::setCurrTime(const uint16_t nr_mins) {
setTime(remote_state + 2, nr_mins);
}
uint8_t IRHaierAC::getSwing() { return (remote_state[2] & 0b11000000) >> 6; }
uint8_t IRHaierAC::getSwing(void) {
return (remote_state[2] & 0b11000000) >> 6;
}
void IRHaierAC::setSwing(const uint8_t state) {
if (state == getSwing()) return; // Nothing to do.
@@ -296,139 +297,243 @@ void IRHaierAC::setSwing(const uint8_t state) {
}
// 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.
if (nr_mins / 24 < 10) result += '0'; // Zero pad.
result += uint64ToString(nr_mins / 60);
result += ":";
if (nr_mins % 60 < 10) result += "0"; // Zero pad.
result += ':';
if (nr_mins % 60 < 10) result += '0'; // Zero pad.
result += uint64ToString(nr_mins % 60);
return result;
}
// Convert a standard A/C mode into its native mode.
uint8_t IRHaierAC::convertMode(const stdAc::opmode_t mode) {
switch (mode) {
case stdAc::opmode_t::kCool:
return kHaierAcCool;
case stdAc::opmode_t::kHeat:
return kHaierAcHeat;
case stdAc::opmode_t::kDry:
return kHaierAcDry;
case stdAc::opmode_t::kFan:
return kHaierAcFan;
default:
return kHaierAcAuto;
}
}
// Convert a standard A/C Fan speed into its native fan speed.
uint8_t IRHaierAC::convertFan(const stdAc::fanspeed_t speed) {
switch (speed) {
case stdAc::fanspeed_t::kMin:
case stdAc::fanspeed_t::kLow:
return kHaierAcFanLow;
case stdAc::fanspeed_t::kMedium:
return kHaierAcFanMed;
case stdAc::fanspeed_t::kHigh:
case stdAc::fanspeed_t::kMax:
return kHaierAcFanHigh;
default:
return kHaierAcFanAuto;
}
}
// Convert a standard A/C vertical swing into its native setting.
uint8_t IRHaierAC::convertSwingV(const stdAc::swingv_t position) {
switch (position) {
case stdAc::swingv_t::kHighest:
case stdAc::swingv_t::kHigh:
case stdAc::swingv_t::kMiddle:
return kHaierAcSwingUp;
case stdAc::swingv_t::kLow:
case stdAc::swingv_t::kLowest:
return kHaierAcSwingDown;
case stdAc::swingv_t::kOff:
return kHaierAcSwingOff;
default:
return kHaierAcSwingChg;
}
}
// Convert a native mode to it's common equivalent.
stdAc::opmode_t IRHaierAC::toCommonMode(const uint8_t mode) {
switch (mode) {
case kHaierAcCool: return stdAc::opmode_t::kCool;
case kHaierAcHeat: return stdAc::opmode_t::kHeat;
case kHaierAcDry: return stdAc::opmode_t::kDry;
case kHaierAcFan: return stdAc::opmode_t::kFan;
default: return stdAc::opmode_t::kAuto;
}
}
// Convert a native fan speed to it's common equivalent.
stdAc::fanspeed_t IRHaierAC::toCommonFanSpeed(const uint8_t speed) {
switch (speed) {
case kHaierAcFanHigh: return stdAc::fanspeed_t::kMax;
case kHaierAcFanMed: return stdAc::fanspeed_t::kMedium;
case kHaierAcFanLow: return stdAc::fanspeed_t::kMin;
default: return stdAc::fanspeed_t::kAuto;
}
}
// Convert a native vertical swing to it's common equivalent.
stdAc::swingv_t IRHaierAC::toCommonSwingV(const uint8_t pos) {
switch (pos) {
case kHaierAcSwingUp: return stdAc::swingv_t::kHighest;
case kHaierAcSwingDown: return stdAc::swingv_t::kLowest;
case kHaierAcSwingOff: return stdAc::swingv_t::kOff;
default: return stdAc::swingv_t::kAuto;
}
}
// Convert the A/C state to it's common equivalent.
stdAc::state_t IRHaierAC::toCommon(void) {
stdAc::state_t result;
result.protocol = decode_type_t::HAIER_AC;
result.model = -1; // No models used.
result.power = true;
if (this->getCommand() == kHaierAcCmdOff) result.power = false;
result.mode = this->toCommonMode(this->getMode());
result.celsius = true;
result.degrees = this->getTemp();
result.fanspeed = this->toCommonFanSpeed(this->getFan());
result.swingv = this->toCommonSwingV(this->getSwing());
result.filter = this->getHealth();
result.sleep = this->getSleep() ? 0 : -1;
// Not supported.
result.swingh = stdAc::swingh_t::kOff;
result.quiet = false;
result.turbo = false;
result.econo = false;
result.light = false;
result.clean = false;
result.beep = false;
result.clock = -1;
return result;
}
// Convert the internal state into a human readable string.
#ifdef ARDUINO
String IRHaierAC::toString() {
String IRHaierAC::toString(void) {
String result = "";
#else
std::string IRHaierAC::toString() {
std::string result = "";
#endif // ARDUINO
result.reserve(150); // Reserve some heap for the string to reduce fragging.
uint8_t cmd = getCommand();
result += "Command: " + uint64ToString(cmd) + " (";
result += F("Command: ");
result += uint64ToString(cmd);
result += F(" (");
switch (cmd) {
case kHaierAcCmdOff:
result += "Off";
result += F("Off");
break;
case kHaierAcCmdOn:
result += "On";
result += F("On");
break;
case kHaierAcCmdMode:
result += "Mode";
result += F("Mode");
break;
case kHaierAcCmdFan:
result += "Fan";
result += F("Fan");
break;
case kHaierAcCmdTempUp:
result += "Temp Up";
result += F("Temp Up");
break;
case kHaierAcCmdTempDown:
result += "Temp Down";
result += F("Temp Down");
break;
case kHaierAcCmdSleep:
result += "Sleep";
result += F("Sleep");
break;
case kHaierAcCmdTimerSet:
result += "Timer Set";
result += F("Timer Set");
break;
case kHaierAcCmdTimerCancel:
result += "Timer Cancel";
result += F("Timer Cancel");
break;
case kHaierAcCmdHealth:
result += "Health";
result += F("Health");
break;
case kHaierAcCmdSwing:
result += "Swing";
result += F("Swing");
break;
default:
result += "Unknown";
result += F("Unknown");
}
result += ")";
result += ", Mode: " + uint64ToString(getMode());
result += ')';
result += F(", Mode: ");
result += uint64ToString(getMode());
switch (getMode()) {
case kHaierAcAuto:
result += " (AUTO)";
result += F(" (AUTO)");
break;
case kHaierAcCool:
result += " (COOL)";
result += F(" (COOL)");
break;
case kHaierAcHeat:
result += " (HEAT)";
result += F(" (HEAT)");
break;
case kHaierAcDry:
result += " (DRY)";
result += F(" (DRY)");
break;
case kHaierAcFan:
result += " (FAN)";
result += F(" (FAN)");
break;
default:
result += " (UNKNOWN)";
result += F(" (UNKNOWN)");
}
result += ", Temp: " + uint64ToString(getTemp()) + "C";
result += ", Fan: " + uint64ToString(getFan());
result += F(", Temp: ");
result += uint64ToString(getTemp());
result += F("C, Fan: ");
result += uint64ToString(getFan());
switch (getFan()) {
case kHaierAcFanAuto:
result += " (AUTO)";
result += F(" (AUTO)");
break;
case kHaierAcFanHigh:
result += " (MAX)";
result += F(" (MAX)");
break;
}
result += ", Swing: " + uint64ToString(getSwing()) + " (";
result += F(", Swing: ");
result += uint64ToString(getSwing());
result += F(" (");
switch (getSwing()) {
case kHaierAcSwingOff:
result += "Off";
result += F("Off");
break;
case kHaierAcSwingUp:
result += "Up";
result += F("Up");
break;
case kHaierAcSwingDown:
result += "Down";
result += F("Down");
break;
case kHaierAcSwingChg:
result += "Chg";
result += F("Chg");
break;
default:
result += "Unknown";
result += F("Unknown");
}
result += ")";
result += ", Sleep: ";
result += ')';
result += F(", Sleep: ");
if (getSleep())
result += "On";
result += F("On");
else
result += "Off";
result += ", Health: ";
result += F("Off");
result += F(", Health: ");
if (getHealth())
result += "On";
result += F("On");
else
result += "Off";
result += ", Current Time: " + timeToString(getCurrTime());
result += ", On Timer: ";
result += F("Off");
result += F(", Current Time: ");
result += timeToString(getCurrTime());
result += F(", On Timer: ");
if (getOnTimer() >= 0)
result += timeToString(getOnTimer());
else
result += "Off";
result += ", Off Timer: ";
result += F("Off");
result += F(", Off Timer: ");
if (getOffTimer() >= 0)
result += timeToString(getOffTimer());
else
result += "Off";
result += F("Off");
return result;
}
@@ -437,7 +542,7 @@ std::string IRHaierAC::toString() {
// Class for emulating a Haier YRW02 remote
IRHaierACYRW02::IRHaierACYRW02(uint16_t pin) : _irsend(pin) { stateReset(); }
void IRHaierACYRW02::begin() { _irsend.begin(); }
void IRHaierACYRW02::begin(void) { _irsend.begin(); }
#if SEND_HAIER_AC_YRW02
void IRHaierACYRW02::send(const uint16_t repeat) {
@@ -446,7 +551,7 @@ void IRHaierACYRW02::send(const uint16_t repeat) {
}
#endif // SEND_HAIER_AC_YRW02
void IRHaierACYRW02::checksum() {
void IRHaierACYRW02::checksum(void) {
remote_state[kHaierACYRW02StateLength - 1] =
sumBytes(remote_state, kHaierACYRW02StateLength - 1);
}
@@ -456,7 +561,7 @@ bool IRHaierACYRW02::validChecksum(uint8_t state[], const uint16_t length) {
return (state[length - 1] == sumBytes(state, length - 1));
}
void IRHaierACYRW02::stateReset() {
void IRHaierACYRW02::stateReset(void) {
for (uint8_t i = 1; i < kHaierACYRW02StateLength; i++) remote_state[i] = 0x0;
remote_state[0] = kHaierAcYrw02Prefix;
@@ -470,12 +575,12 @@ void IRHaierACYRW02::stateReset() {
setPower(true);
}
uint8_t* IRHaierACYRW02::getRaw() {
uint8_t* IRHaierACYRW02::getRaw(void) {
checksum();
return remote_state;
}
void IRHaierACYRW02::setRaw(uint8_t new_code[]) {
void IRHaierACYRW02::setRaw(const uint8_t new_code[]) {
for (uint8_t i = 0; i < kHaierACYRW02StateLength; i++) {
remote_state[i] = new_code[i];
}
@@ -497,7 +602,9 @@ void IRHaierACYRW02::setButton(uint8_t button) {
}
}
uint8_t IRHaierACYRW02::getButton() { return remote_state[12] & (0b00001111); }
uint8_t IRHaierACYRW02::getButton(void) {
return remote_state[12] & 0b00001111;
}
void IRHaierACYRW02::setMode(uint8_t mode) {
uint8_t new_mode = mode;
@@ -516,10 +623,10 @@ void IRHaierACYRW02::setMode(uint8_t mode) {
remote_state[7] |= (new_mode << 4);
}
uint8_t IRHaierACYRW02::getMode() { return remote_state[7] >> 4; }
uint8_t IRHaierACYRW02::getMode(void) { return remote_state[7] >> 4; }
void IRHaierACYRW02::setTemp(const uint8_t celcius) {
uint8_t temp = celcius;
void IRHaierACYRW02::setTemp(const uint8_t celsius) {
uint8_t temp = celsius;
if (temp < kHaierAcMinTemp)
temp = kHaierAcMinTemp;
else if (temp > kHaierAcMaxTemp)
@@ -536,43 +643,47 @@ void IRHaierACYRW02::setTemp(const uint8_t celcius) {
remote_state[1] |= ((temp - kHaierAcMinTemp) << 4);
}
uint8_t IRHaierACYRW02::getTemp() {
uint8_t IRHaierACYRW02::getTemp(void) {
return ((remote_state[1] & 0b11110000) >> 4) + kHaierAcMinTemp;
}
void IRHaierACYRW02::setHealth(bool state) {
void IRHaierACYRW02::setHealth(const bool on) {
setButton(kHaierAcYrw02ButtonHealth);
remote_state[3] &= 0b11111101;
remote_state[3] |= (state << 1);
remote_state[3] |= (on << 1);
}
bool IRHaierACYRW02::getHealth(void) { return remote_state[3] & 0b00000010; }
bool IRHaierACYRW02::getPower() { return remote_state[4] & kHaierAcYrw02Power; }
bool IRHaierACYRW02::getPower(void) {
return remote_state[4] & kHaierAcYrw02Power;
}
void IRHaierACYRW02::setPower(bool state) {
void IRHaierACYRW02::setPower(const bool on) {
setButton(kHaierAcYrw02ButtonPower);
if (state)
if (on)
remote_state[4] |= kHaierAcYrw02Power;
else
remote_state[4] &= ~kHaierAcYrw02Power;
}
void IRHaierACYRW02::on() { setPower(true); }
void IRHaierACYRW02::on(void) { setPower(true); }
void IRHaierACYRW02::off() { setPower(false); }
void IRHaierACYRW02::off(void) { setPower(false); }
bool IRHaierACYRW02::getSleep() { return remote_state[8] & kHaierAcYrw02Sleep; }
bool IRHaierACYRW02::getSleep(void) {
return remote_state[8] & kHaierAcYrw02Sleep;
}
void IRHaierACYRW02::setSleep(bool state) {
void IRHaierACYRW02::setSleep(const bool on) {
setButton(kHaierAcYrw02ButtonSleep);
if (state)
if (on)
remote_state[8] |= kHaierAcYrw02Sleep;
else
remote_state[8] &= ~kHaierAcYrw02Sleep;
}
uint8_t IRHaierACYRW02::getTurbo() { return remote_state[6] >> 6; }
uint8_t IRHaierACYRW02::getTurbo(void) { return remote_state[6] >> 6; }
void IRHaierACYRW02::setTurbo(uint8_t speed) {
switch (speed) {
@@ -585,7 +696,7 @@ void IRHaierACYRW02::setTurbo(uint8_t speed) {
}
}
uint8_t IRHaierACYRW02::getFan() { return remote_state[5] >> 4; }
uint8_t IRHaierACYRW02::getFan(void) { return remote_state[5] >> 4; }
void IRHaierACYRW02::setFan(uint8_t speed) {
switch (speed) {
@@ -599,7 +710,7 @@ void IRHaierACYRW02::setFan(uint8_t speed) {
}
}
uint8_t IRHaierACYRW02::getSwing() { return remote_state[1] & 0b00001111; }
uint8_t IRHaierACYRW02::getSwing(void) { return remote_state[1] & 0b00001111; }
void IRHaierACYRW02::setSwing(uint8_t state) {
uint8_t newstate = state;
@@ -628,140 +739,254 @@ void IRHaierACYRW02::setSwing(uint8_t state) {
remote_state[1] |= newstate;
}
// Convert a standard A/C mode into its native mode.
uint8_t IRHaierACYRW02::convertMode(const stdAc::opmode_t mode) {
switch (mode) {
case stdAc::opmode_t::kCool:
return kHaierAcYrw02Cool;
case stdAc::opmode_t::kHeat:
return kHaierAcYrw02Heat;
case stdAc::opmode_t::kDry:
return kHaierAcYrw02Dry;
case stdAc::opmode_t::kFan:
return kHaierAcYrw02Fan;
default:
return kHaierAcYrw02Auto;
}
}
// Convert a standard A/C Fan speed into its native fan speed.
uint8_t IRHaierACYRW02::convertFan(const stdAc::fanspeed_t speed) {
switch (speed) {
case stdAc::fanspeed_t::kMin:
case stdAc::fanspeed_t::kLow:
return kHaierAcYrw02FanLow;
case stdAc::fanspeed_t::kMedium:
return kHaierAcYrw02FanMed;
case stdAc::fanspeed_t::kHigh:
case stdAc::fanspeed_t::kMax:
return kHaierAcYrw02FanHigh;
default:
return kHaierAcYrw02FanAuto;
}
}
// Convert a standard A/C vertical swing into its native setting.
uint8_t IRHaierACYRW02::convertSwingV(const stdAc::swingv_t position) {
switch (position) {
case stdAc::swingv_t::kHighest:
case stdAc::swingv_t::kHigh:
return kHaierAcYrw02SwingTop;
case stdAc::swingv_t::kMiddle:
return kHaierAcYrw02SwingMiddle;
case stdAc::swingv_t::kLow:
return kHaierAcYrw02SwingDown;
case stdAc::swingv_t::kLowest:
return kHaierAcYrw02SwingBottom;
case stdAc::swingv_t::kOff:
return kHaierAcYrw02SwingOff;
default:
return kHaierAcYrw02SwingAuto;
}
}
// Convert a native mode to it's common equivalent.
stdAc::opmode_t IRHaierACYRW02::toCommonMode(const uint8_t mode) {
switch (mode) {
case kHaierAcYrw02Cool: return stdAc::opmode_t::kCool;
case kHaierAcYrw02Heat: return stdAc::opmode_t::kHeat;
case kHaierAcYrw02Dry: return stdAc::opmode_t::kDry;
case kHaierAcYrw02Fan: return stdAc::opmode_t::kFan;
default: return stdAc::opmode_t::kAuto;
}
}
// Convert a native fan speed to it's common equivalent.
stdAc::fanspeed_t IRHaierACYRW02::toCommonFanSpeed(const uint8_t speed) {
switch (speed) {
case kHaierAcYrw02FanHigh: return stdAc::fanspeed_t::kMax;
case kHaierAcYrw02FanMed: return stdAc::fanspeed_t::kMedium;
case kHaierAcYrw02FanLow: return stdAc::fanspeed_t::kMin;
default: return stdAc::fanspeed_t::kAuto;
}
}
// Convert a native vertical swing to it's common equivalent.
stdAc::swingv_t IRHaierACYRW02::toCommonSwingV(const uint8_t pos) {
switch (pos) {
case kHaierAcYrw02SwingTop: return stdAc::swingv_t::kHighest;
case kHaierAcYrw02SwingMiddle: return stdAc::swingv_t::kMiddle;
case kHaierAcYrw02SwingDown: return stdAc::swingv_t::kLow;
case kHaierAcYrw02SwingBottom: return stdAc::swingv_t::kLowest;
case kHaierAcYrw02SwingOff: return stdAc::swingv_t::kOff;
default: return stdAc::swingv_t::kAuto;
}
}
// Convert the A/C state to it's common equivalent.
stdAc::state_t IRHaierACYRW02::toCommon(void) {
stdAc::state_t result;
result.protocol = decode_type_t::HAIER_AC_YRW02;
result.model = -1; // No models used.
result.power = this->getPower();
result.mode = this->toCommonMode(this->getMode());
result.celsius = true;
result.degrees = this->getTemp();
result.fanspeed = this->toCommonFanSpeed(this->getFan());
result.swingv = this->toCommonSwingV(this->getSwing());
result.filter = this->getHealth();
result.sleep = this->getSleep() ? 0 : -1;
// Not supported.
result.swingh = stdAc::swingh_t::kOff;
result.quiet = false;
result.turbo = false;
result.econo = false;
result.light = false;
result.clean = false;
result.beep = false;
result.clock = -1;
return result;
}
// Convert the internal state into a human readable string.
#ifdef ARDUINO
String IRHaierACYRW02::toString() {
String IRHaierACYRW02::toString(void) {
String result = "";
#else
std::string IRHaierACYRW02::toString() {
std::string result = "";
#endif // ARDUINO
result += "Power: ";
result.reserve(130); // Reserve some heap for the string to reduce fragging.
result += F("Power: ");
if (getPower())
result += "On";
result += F("On");
else
result += "Off";
result += F("Off");
uint8_t cmd = getButton();
result += ", Button: " + uint64ToString(cmd) + " (";
result += F(", Button: ");
result += uint64ToString(cmd);
result += F(" (");
switch (cmd) {
case kHaierAcYrw02ButtonPower:
result += "Power";
result += F("Power");
break;
case kHaierAcYrw02ButtonMode:
result += "Mode";
result += F("Mode");
break;
case kHaierAcYrw02ButtonFan:
result += "Fan";
result += F("Fan");
break;
case kHaierAcYrw02ButtonTempUp:
result += "Temp Up";
result += F("Temp Up");
break;
case kHaierAcYrw02ButtonTempDown:
result += "Temp Down";
result += F("Temp Down");
break;
case kHaierAcYrw02ButtonSleep:
result += "Sleep";
result += F("Sleep");
break;
case kHaierAcYrw02ButtonHealth:
result += "Health";
break;
case kHaierAcYrw02ButtonSwing:
result += "Swing";
result += F("Swing");
break;
case kHaierAcYrw02ButtonTurbo:
result += "Turbo";
result += F("Turbo");
break;
default:
result += "Unknown";
result += F("Unknown");
}
result += ")";
result += ", Mode: " + uint64ToString(getMode());
result += ')';
result += F(", Mode: ");
result += uint64ToString(getMode());
switch (getMode()) {
case kHaierAcYrw02Auto:
result += " (Auto)";
result += F(" (Auto)");
break;
case kHaierAcYrw02Cool:
result += " (Cool)";
result += F(" (Cool)");
break;
case kHaierAcYrw02Heat:
result += " (Heat)";
result += F(" (Heat)");
break;
case kHaierAcYrw02Dry:
result += " (Dry)";
result += F(" (Dry)");
break;
case kHaierAcYrw02Fan:
result += " (Fan)";
result += F(" (Fan)");
break;
default:
result += " (UNKNOWN)";
result += F(" (UNKNOWN)");
}
result += ", Temp: " + uint64ToString(getTemp()) + "C";
result += ", Fan: " + uint64ToString(getFan());
result += F(", Temp: ");
result += uint64ToString(getTemp());
result += F("C, Fan: ");
result += uint64ToString(getFan());
switch (getFan()) {
case kHaierAcYrw02FanAuto:
result += " (Auto)";
result += F(" (Auto)");
break;
case kHaierAcYrw02FanHigh:
result += " (High)";
result += F(" (High)");
break;
case kHaierAcYrw02FanLow:
result += " (Low)";
result += F(" (Low)");
break;
case kHaierAcYrw02FanMed:
result += " (Med)";
result += F(" (Med)");
break;
default:
result += " (Unknown)";
result += F(" (Unknown)");
}
result += ", Turbo: " + uint64ToString(getTurbo()) + " (";
result += F(", Turbo: ");
result += uint64ToString(getTurbo());
result += F(" (");
switch (getTurbo()) {
case kHaierAcYrw02TurboOff:
result += "Off";
result += F("Off");
break;
case kHaierAcYrw02TurboLow:
result += "Low";
result += F("Low");
break;
case kHaierAcYrw02TurboHigh:
result += "High";
result += F("High");
break;
default:
result += "Unknown";
result += F("Unknown");
}
result += ")";
result += ", Swing: " + uint64ToString(getSwing()) + " (";
result += ')';
result += F(", Swing: ");
result += uint64ToString(getSwing());
result += F(" (");
switch (getSwing()) {
case kHaierAcYrw02SwingOff:
result += "Off";
result += F("Off");
break;
case kHaierAcYrw02SwingAuto:
result += "Auto";
result += F("Auto");
break;
case kHaierAcYrw02SwingBottom:
result += "Bottom";
result += F("Bottom");
break;
case kHaierAcYrw02SwingDown:
result += "Down";
result += F("Down");
break;
case kHaierAcYrw02SwingTop:
result += "Top";
result += F("Top");
break;
case kHaierAcYrw02SwingMiddle:
result += "Middle";
result += F("Middle");
break;
default:
result += "Unknown";
result += F("Unknown");
}
result += ")";
result += ", Sleep: ";
result += ')';
result += F(", Sleep: ");
if (getSleep())
result += "On";
result += F("On");
else
result += "Off";
result += ", Health: ";
result += F("Off");
result += F(", Health: ");
if (getHealth())
result += "On";
result += F("On");
else
result += "Off";
result += F("Off");
return result;
}
@@ -777,7 +1002,7 @@ std::string IRHaierACYRW02::toString() {
// Returns:
// boolean: True if it can decode it, false if it can't.
//
// Status: BETA / Appears to be working.
// Status: STABLE / Known to be working.
//
bool IRrecv::decodeHaierAC(decode_results* results, uint16_t nbits,
bool strict) {
+77 -59
View File
@@ -1,22 +1,22 @@
// Copyright 2018 crankyoldgit
// The specifics of reverse engineering the protocol details by kuzin2006
// Supports:
// Brand: Haier, Model: HSU07-HEA03 remote
// Brand: Haier, Model: YR-W02 remote
// Brand: Haier, Model: HSU-09HMC203 A/C
#ifndef IR_HAIER_H_
#define IR_HAIER_H_
#ifndef UNIT_TEST
#include <Arduino.h>
#else
#include <string>
#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
#ifdef UNIT_TEST
#include "IRsend_test.h"
#endif
// Ref:
// https://github.com/markszabo/IRremoteESP8266/issues/404
@@ -53,6 +53,7 @@ const uint8_t kHaierAcSwingDown = 0b00000010;
const uint8_t kHaierAcSwingChg = 0b00000011;
// Byte 6
const uint8_t kHaierAcModeMask = 0b11100000;
const uint8_t kHaierAcAuto = 0;
const uint8_t kHaierAcCool = 1;
const uint8_t kHaierAcDry = 2;
@@ -66,6 +67,9 @@ const uint8_t kHaierAcFanHigh = 3;
const uint16_t kHaierAcMaxTime = (23 * 60) + 59;
// Byte 7
const uint8_t kHaierAcSleepBit = 0b01000000;
// Legacy Haier AC defines.
#define HAIER_AC_MIN_TEMP kHaierAcMinTemp
#define HAIER_AC_DEF_TEMP kHaierAcDefTemp
@@ -183,61 +187,68 @@ const uint8_t kHaierAcYrw02ButtonSleep = 0xB;
class IRHaierAC {
public:
explicit IRHaierAC(uint16_t pin);
explicit IRHaierAC(const uint16_t pin);
#if SEND_HAIER_AC
void send(const uint16_t repeat = kHaierAcDefaultRepeat);
uint8_t calibrate(void) { return _irsend.calibrate(); }
#endif // SEND_HAIER_AC
void begin();
void begin(void);
void setCommand(const uint8_t command);
uint8_t getCommand();
uint8_t getCommand(void);
void setTemp(const uint8_t temp);
uint8_t getTemp();
uint8_t getTemp(void);
void setFan(const uint8_t speed);
uint8_t getFan();
uint8_t getFan(void);
uint8_t getMode();
uint8_t getMode(void);
void setMode(const uint8_t mode);
bool getSleep();
void setSleep(const bool state);
bool getHealth();
void setHealth(const bool state);
bool getSleep(void);
void setSleep(const bool on);
bool getHealth(void);
void setHealth(const bool on);
int16_t getOnTimer();
int16_t getOnTimer(void);
void setOnTimer(const uint16_t mins);
int16_t getOffTimer();
int16_t getOffTimer(void);
void setOffTimer(const uint16_t mins);
void cancelTimers();
void cancelTimers(void);
uint16_t getCurrTime();
uint16_t getCurrTime(void);
void setCurrTime(const uint16_t mins);
uint8_t getSwing();
uint8_t getSwing(void);
void setSwing(const uint8_t state);
uint8_t* getRaw();
void setRaw(uint8_t new_code[]);
uint8_t* getRaw(void);
void setRaw(const uint8_t new_code[]);
static bool validChecksum(uint8_t state[],
const uint16_t length = kHaierACStateLength);
#ifdef ARDUINO
String toString();
uint8_t convertMode(const stdAc::opmode_t mode);
uint8_t convertFan(const stdAc::fanspeed_t speed);
uint8_t convertSwingV(const stdAc::swingv_t position);
static stdAc::opmode_t toCommonMode(const uint8_t mode);
static stdAc::fanspeed_t toCommonFanSpeed(const uint8_t speed);
static stdAc::swingv_t toCommonSwingV(const uint8_t pos);
stdAc::state_t toCommon(void);
String toString(void);
static String timeToString(const uint16_t nr_mins);
#else
std::string toString();
static std::string timeToString(const uint16_t nr_mins);
#endif
#ifndef UNIT_TEST
private:
IRsend _irsend;
#else
IRsendTest _irsend;
#endif
uint8_t remote_state[kHaierACStateLength];
void stateReset();
void checksum();
void stateReset(void);
void checksum(void);
static uint16_t getTime(const uint8_t ptr[]);
static void setTime(uint8_t ptr[], const uint16_t nr_mins);
IRsend _irsend;
};
class IRHaierACYRW02 {
@@ -247,51 +258,58 @@ class IRHaierACYRW02 {
#if SEND_HAIER_AC_YRW02
void send(const uint16_t repeat = kHaierAcYrw02DefaultRepeat);
#endif // SEND_HAIER_AC_YRW02
void begin();
void begin(void);
void setButton(const uint8_t button);
uint8_t getButton();
uint8_t getButton(void);
void setTemp(const uint8_t temp);
uint8_t getTemp();
uint8_t getTemp(void);
void setFan(const uint8_t speed);
uint8_t getFan();
uint8_t getFan(void);
uint8_t getMode();
uint8_t getMode(void);
void setMode(const uint8_t mode);
bool getPower();
void setPower(const bool state);
void on();
void off();
bool getPower(void);
void setPower(const bool on);
void on(void);
void off(void);
bool getSleep();
void setSleep(const bool state);
bool getHealth();
void setHealth(const bool state);
bool getSleep(void);
void setSleep(const bool on);
bool getHealth(void);
void setHealth(const bool on);
uint8_t getTurbo();
uint8_t getTurbo(void);
void setTurbo(const uint8_t speed);
uint8_t getSwing();
uint8_t getSwing(void);
void setSwing(const uint8_t state);
uint8_t* getRaw();
void setRaw(uint8_t new_code[]);
uint8_t* getRaw(void);
void setRaw(const uint8_t new_code[]);
static bool validChecksum(uint8_t state[],
const uint16_t length = kHaierACYRW02StateLength);
#ifdef ARDUINO
String toString();
#else
std::string toString();
#endif
uint8_t convertMode(const stdAc::opmode_t mode);
uint8_t convertFan(const stdAc::fanspeed_t speed);
uint8_t convertSwingV(const stdAc::swingv_t position);
static stdAc::opmode_t toCommonMode(const uint8_t mode);
static stdAc::fanspeed_t toCommonFanSpeed(const uint8_t speed);
static stdAc::swingv_t toCommonSwingV(const uint8_t pos);
stdAc::state_t toCommon(void);
String toString(void);
#ifndef UNIT_TEST
private:
uint8_t remote_state[kHaierACYRW02StateLength];
void stateReset();
void checksum();
IRsend _irsend;
#else
IRsendTest _irsend;
#endif
uint8_t remote_state[kHaierACYRW02StateLength];
void stateReset(void);
void checksum(void);
};
#endif // IR_HAIER_H_
+134 -56
View File
@@ -15,12 +15,6 @@
#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
const uint16_t kHitachiAcHdrMark = 3300;
@@ -30,7 +24,7 @@ const uint16_t kHitachiAc1HdrSpace = 3400;
const uint16_t kHitachiAcBitMark = 400;
const uint16_t kHitachiAcOneSpace = 1250;
const uint16_t kHitachiAcZeroSpace = 500;
const uint32_t kHitachiAcMinGap = 100000; // Completely made up value.
const uint32_t kHitachiAcMinGap = kDefaultMessageGap; // Just a guess.
#if (SEND_HITACHI_AC || SEND_HITACHI_AC2)
// Send a Hitachi A/C message.
@@ -44,8 +38,8 @@ const uint32_t kHitachiAcMinGap = 100000; // Completely made up value.
//
// Ref:
// https://github.com/markszabo/IRremoteESP8266/issues/417
void IRsend::sendHitachiAC(unsigned char data[], uint16_t nbytes,
uint16_t repeat) {
void IRsend::sendHitachiAC(const unsigned char data[], const uint16_t nbytes,
const uint16_t repeat) {
if (nbytes < kHitachiAcStateLength)
return; // Not enough bytes to send a proper message.
sendGeneric(kHitachiAcHdrMark, kHitachiAcHdrSpace, kHitachiAcBitMark,
@@ -71,8 +65,8 @@ void IRsend::sendHitachiAC(unsigned char data[], uint16_t nbytes,
// 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) {
void IRsend::sendHitachiAC1(const unsigned char data[], const uint16_t nbytes,
const uint16_t repeat) {
if (nbytes < kHitachiAc1StateLength)
return; // Not enough bytes to send a proper message.
sendGeneric(kHitachiAc1HdrMark, kHitachiAc1HdrSpace, kHitachiAcBitMark,
@@ -98,21 +92,21 @@ void IRsend::sendHitachiAC1(unsigned char data[], uint16_t nbytes,
// 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) {
void IRsend::sendHitachiAC2(const unsigned char data[], const uint16_t nbytes,
const uint16_t repeat) {
if (nbytes < kHitachiAc2StateLength)
return; // Not enough bytes to send a proper message.
sendHitachiAC(data, nbytes, repeat);
}
#endif // SEND_HITACHI_AC2
// Class for handling the remote control oh a Hitachi 28 byte A/C message.
// Class for handling the remote control on a Hitachi 28 byte A/C message.
// Inspired by:
// https://github.com/ToniA/arduino-heatpumpir/blob/master/HitachiHeatpumpIR.cpp
IRHitachiAc::IRHitachiAc(uint16_t pin) : _irsend(pin) { stateReset(); }
IRHitachiAc::IRHitachiAc(const uint16_t pin) : _irsend(pin) { stateReset(); }
void IRHitachiAc::stateReset() {
void IRHitachiAc::stateReset(void) {
remote_state[0] = 0x80;
remote_state[1] = 0x08;
remote_state[2] = 0x0C;
@@ -130,7 +124,7 @@ void IRHitachiAc::stateReset() {
setTemp(23);
}
void IRHitachiAc::begin() { _irsend.begin(); }
void IRHitachiAc::begin(void) { _irsend.begin(); }
uint8_t IRHitachiAc::calcChecksum(const uint8_t state[],
const uint16_t length) {
@@ -148,7 +142,7 @@ bool IRHitachiAc::validChecksum(const uint8_t state[], const uint16_t length) {
return (state[length - 1] == calcChecksum(state, length));
}
uint8_t *IRHitachiAc::getRaw() {
uint8_t *IRHitachiAc::getRaw(void) {
checksum();
return remote_state;
}
@@ -165,7 +159,7 @@ void IRHitachiAc::send(const uint16_t repeat) {
}
#endif // SEND_HITACHI_AC
bool IRHitachiAc::getPower() { return (remote_state[17] & 0x01); }
bool IRHitachiAc::getPower(void) { return (remote_state[17] & 0x01); }
void IRHitachiAc::setPower(const bool on) {
if (on)
@@ -174,11 +168,11 @@ void IRHitachiAc::setPower(const bool on) {
remote_state[17] &= 0xFE;
}
void IRHitachiAc::on() { setPower(true); }
void IRHitachiAc::on(void) { setPower(true); }
void IRHitachiAc::off() { setPower(false); }
void IRHitachiAc::off(void) { setPower(false); }
uint8_t IRHitachiAc::getMode() { return reverseBits(remote_state[10], 8); }
uint8_t IRHitachiAc::getMode(void) { return reverseBits(remote_state[10], 8); }
void IRHitachiAc::setMode(const uint8_t mode) {
uint8_t newmode = mode;
@@ -200,7 +194,9 @@ void IRHitachiAc::setMode(const uint8_t mode) {
setFan(getFan()); // Reset the fan speed after the mode change.
}
uint8_t IRHitachiAc::getTemp() { return reverseBits(remote_state[11], 8) >> 1; }
uint8_t IRHitachiAc::getTemp(void) {
return reverseBits(remote_state[11], 8) >> 1;
}
void IRHitachiAc::setTemp(const uint8_t celsius) {
uint8_t temp;
@@ -220,7 +216,7 @@ void IRHitachiAc::setTemp(const uint8_t celsius) {
remote_state[9] = 0x10;
}
uint8_t IRHitachiAc::getFan() { return reverseBits(remote_state[13], 8); }
uint8_t IRHitachiAc::getFan(void) { return reverseBits(remote_state[13], 8); }
void IRHitachiAc::setFan(const uint8_t speed) {
uint8_t fanmin = kHitachiAcFanAuto;
@@ -239,7 +235,7 @@ void IRHitachiAc::setFan(const uint8_t speed) {
remote_state[13] = reverseBits(newspeed, 8);
}
bool IRHitachiAc::getSwingVertical() { return remote_state[14] & 0x80; }
bool IRHitachiAc::getSwingVertical(void) { return remote_state[14] & 0x80; }
void IRHitachiAc::setSwingVertical(const bool on) {
if (on)
@@ -248,7 +244,7 @@ void IRHitachiAc::setSwingVertical(const bool on) {
remote_state[14] &= 0x7F;
}
bool IRHitachiAc::getSwingHorizontal() { return remote_state[15] & 0x80; }
bool IRHitachiAc::getSwingHorizontal(void) { return remote_state[15] & 0x80; }
void IRHitachiAc::setSwingHorizontal(const bool on) {
if (on)
@@ -257,65 +253,147 @@ void IRHitachiAc::setSwingHorizontal(const bool on) {
remote_state[15] &= 0x7F;
}
// Convert a standard A/C mode into its native mode.
uint8_t IRHitachiAc::convertMode(const stdAc::opmode_t mode) {
switch (mode) {
case stdAc::opmode_t::kCool:
return kHitachiAcCool;
case stdAc::opmode_t::kHeat:
return kHitachiAcHeat;
case stdAc::opmode_t::kDry:
return kHitachiAcDry;
case stdAc::opmode_t::kFan:
return kHitachiAcFan;
default:
return kHitachiAcAuto;
}
}
// Convert a standard A/C Fan speed into its native fan speed.
uint8_t IRHitachiAc::convertFan(const stdAc::fanspeed_t speed) {
switch (speed) {
case stdAc::fanspeed_t::kMin:
case stdAc::fanspeed_t::kLow:
return kHitachiAcFanLow;
case stdAc::fanspeed_t::kMedium:
return kHitachiAcFanLow + 1;
case stdAc::fanspeed_t::kHigh:
return kHitachiAcFanHigh - 1;
case stdAc::fanspeed_t::kMax:
return kHitachiAcFanHigh;
default:
return kHitachiAcFanAuto;
}
}
// Convert a native mode to it's common equivalent.
stdAc::opmode_t IRHitachiAc::toCommonMode(const uint8_t mode) {
switch (mode) {
case kHitachiAcCool: return stdAc::opmode_t::kCool;
case kHitachiAcHeat: return stdAc::opmode_t::kHeat;
case kHitachiAcDry: return stdAc::opmode_t::kDry;
case kHitachiAcFan: return stdAc::opmode_t::kFan;
default: return stdAc::opmode_t::kAuto;
}
}
// Convert a native fan speed to it's common equivalent.
stdAc::fanspeed_t IRHitachiAc::toCommonFanSpeed(const uint8_t speed) {
switch (speed) {
case kHitachiAcFanHigh: return stdAc::fanspeed_t::kMax;
case kHitachiAcFanHigh - 1: return stdAc::fanspeed_t::kHigh;
case kHitachiAcFanLow + 1: return stdAc::fanspeed_t::kMedium;
case kHitachiAcFanLow: return stdAc::fanspeed_t::kLow;
default: return stdAc::fanspeed_t::kAuto;
}
}
// Convert the A/C state to it's common equivalent.
stdAc::state_t IRHitachiAc::toCommon(void) {
stdAc::state_t result;
result.protocol = decode_type_t::HITACHI_AC;
result.model = -1; // No models used.
result.power = this->getPower();
result.mode = this->toCommonMode(this->getMode());
result.celsius = true;
result.degrees = this->getTemp();
result.fanspeed = this->toCommonFanSpeed(this->getFan());
result.swingv = this->getSwingVertical() ? stdAc::swingv_t::kAuto :
stdAc::swingv_t::kOff;
result.swingh = this->getSwingHorizontal() ? stdAc::swingh_t::kAuto :
stdAc::swingh_t::kOff;
// Not supported.
result.quiet = false;
result.turbo = false;
result.clean = false;
result.econo = false;
result.filter = false;
result.light = false;
result.beep = false;
result.sleep = -1;
result.clock = -1;
return result;
}
// Convert the internal state into a human readable string.
#ifdef ARDUINO
String IRHitachiAc::toString() {
String IRHitachiAc::toString(void) {
String result = "";
#else
std::string IRHitachiAc::toString() {
std::string result = "";
#endif // ARDUINO
result += "Power: ";
result.reserve(110); // Reserve some heap for the string to reduce fragging.
result += F("Power: ");
if (getPower())
result += "On";
result += F("On");
else
result += "Off";
result += ", Mode: " + uint64ToString(getMode());
result += F("Off");
result += F(", Mode: ");
result += uint64ToString(getMode());
switch (getMode()) {
case kHitachiAcAuto:
result += " (AUTO)";
result += F(" (AUTO)");
break;
case kHitachiAcCool:
result += " (COOL)";
result += F(" (COOL)");
break;
case kHitachiAcHeat:
result += " (HEAT)";
result += F(" (HEAT)");
break;
case kHitachiAcDry:
result += " (DRY)";
result += F(" (DRY)");
break;
case kHitachiAcFan:
result += " (FAN)";
result += F(" (FAN)");
break;
default:
result += " (UNKNOWN)";
result += F(" (UNKNOWN)");
}
result += ", Temp: " + uint64ToString(getTemp()) + "C";
result += ", Fan: " + uint64ToString(getFan());
result += F(", Temp: ");
result += uint64ToString(getTemp());
result += F("C, Fan: ");
result += uint64ToString(getFan());
switch (getFan()) {
case kHitachiAcFanAuto:
result += " (AUTO)";
result += F(" (AUTO)");
break;
case kHitachiAcFanLow:
result += " (LOW)";
result += F(" (LOW)");
break;
case kHitachiAcFanHigh:
result += " (HIGH)";
result += F(" (HIGH)");
break;
default:
result += " (UNKNOWN)";
result += F(" (UNKNOWN)");
break;
}
result += ", Swing (Vertical): ";
result += F(", Swing (Vertical): ");
if (getSwingVertical())
result += "On";
result += F("On");
else
result += "Off";
result += ", Swing (Horizontal): ";
result += F("Off");
result += F(", Swing (Horizontal): ");
if (getSwingHorizontal())
result += "On";
result += F("On");
else
result += "Off";
result += F("Off");
return result;
}
@@ -338,8 +416,8 @@ std::string IRHitachiAc::toString() {
// 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) {
bool IRrecv::decodeHitachiAC(decode_results *results, const uint16_t nbits,
const bool strict) {
const uint8_t kTolerance = 30;
if (results->rawlen < 2 * nbits + kHeader + kFooter - 1)
return false; // Can't possibly be a valid HitachiAC message.
+32 -20
View File
@@ -2,6 +2,11 @@
//
// Copyright 2018 David Conran
// Supports:
// Brand: Hitachi, Model: RAS-35THA6 remote
// Brand: Hitachi, Model: LT0541-HTA remote
// Brand: Hitachi, Model: Series VI A/C (Circa 2007)
#ifndef IR_HITACHI_H_
#define IR_HITACHI_H_
@@ -9,11 +14,12 @@
#include <stdint.h>
#ifndef UNIT_TEST
#include <Arduino.h>
#else
#include <string>
#endif
#include "IRremoteESP8266.h"
#include "IRsend.h"
#ifdef UNIT_TEST
#include "IRsend_test.h"
#endif
// Constants
const uint8_t kHitachiAcAuto = 2;
@@ -31,45 +37,51 @@ const uint8_t kHitachiAcAutoTemp = 23; // 23C
// Classes
class IRHitachiAc {
public:
explicit IRHitachiAc(uint16_t pin);
explicit IRHitachiAc(const uint16_t pin);
void stateReset();
void stateReset(void);
#if SEND_HITACHI_AC
void send(const uint16_t repeat = kHitachiAcDefaultRepeat);
uint8_t calibrate(void) { return _irsend.calibrate(); }
#endif // SEND_HITACHI_AC
void begin();
void on();
void off();
void begin(void);
void on(void);
void off(void);
void setPower(const bool on);
bool getPower();
bool getPower(void);
void setTemp(const uint8_t temp);
uint8_t getTemp();
uint8_t getTemp(void);
void setFan(const uint8_t speed);
uint8_t getFan();
uint8_t getFan(void);
void setMode(const uint8_t mode);
uint8_t getMode();
uint8_t getMode(void);
void setSwingVertical(const bool on);
bool getSwingVertical();
bool getSwingVertical(void);
void setSwingHorizontal(const bool on);
bool getSwingHorizontal();
uint8_t* getRaw();
bool getSwingHorizontal(void);
uint8_t* getRaw(void);
void setRaw(const uint8_t new_code[],
const uint16_t length = kHitachiAcStateLength);
static bool validChecksum(const uint8_t state[],
const uint16_t length = kHitachiAcStateLength);
static uint8_t calcChecksum(const uint8_t state[],
const uint16_t length = kHitachiAcStateLength);
#ifdef ARDUINO
String toString();
#else
std::string toString();
#endif
uint8_t convertMode(const stdAc::opmode_t mode);
uint8_t convertFan(const stdAc::fanspeed_t speed);
static stdAc::opmode_t toCommonMode(const uint8_t mode);
static stdAc::fanspeed_t toCommonFanSpeed(const uint8_t speed);
stdAc::state_t toCommon(void);
String toString(void);
#ifndef UNIT_TEST
private:
IRsend _irsend;
#else
IRsendTest _irsend;
#endif
// The state of the IR remote in IR code form.
uint8_t remote_state[kHitachiAcStateLength];
void checksum(const uint16_t length = kHitachiAcStateLength);
IRsend _irsend;
uint8_t _previoustemp;
};
+96
View File
@@ -0,0 +1,96 @@
// Copyright 2019 David Conran (crankyoldgit)
// Support for an IR controlled Robot Toilet
#include <algorithm>
#include "IRrecv.h"
#include "IRsend.h"
#include "IRutils.h"
// Supports:
// Brand: Lixil, Model: Inax DT-BA283 Toilet
// Documentation:
// https://www.lixil-manual.com/GCW-1365-16050/GCW-1365-16050.pdf
// Constants
// Ref:
// https://github.com/markszabo/IRremoteESP8266/issues/706
const uint16_t kInaxTick = 500;
const uint16_t kInaxHdrMark = 9000;
const uint16_t kInaxHdrSpace = 4500;
const uint16_t kInaxBitMark = 560;
const uint16_t kInaxOneSpace = 1675;
const uint16_t kInaxZeroSpace = kInaxBitMark;
const uint16_t kInaxMinGap = 40000;
#if SEND_INAX
// Send a Inax Toilet formatted message.
//
// Args:
// data: The message to be sent.
// nbits: The bit size of the message being sent. typically kInaxBits.
// repeat: The number of times the message is to be repeated.
//
// Status: BETA / Should be working.
//
// Ref: https://github.com/markszabo/IRremoteESP8266/issues/706
void IRsend::sendInax(const uint64_t data, const uint16_t nbits,
const uint16_t repeat) {
sendGeneric(kInaxHdrMark, kInaxHdrSpace,
kInaxBitMark, kInaxOneSpace,
kInaxBitMark, kInaxZeroSpace,
kInaxBitMark, kInaxMinGap,
data, nbits, 38, true, repeat, kDutyDefault);
}
#endif
#if DECODE_INAX
// Decode the supplied Inax Toilet 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 kInaxBits.
// strict: Flag to indicate if we strictly adhere to the specification.
// Returns:
// boolean: True if it can decode it, false if it can't.
//
// Status: BETA / Should be Working.
//
bool IRrecv::decodeInax(decode_results *results, const uint16_t nbits,
const bool strict) {
if (results->rawlen < 2 * nbits + kHeader + kFooter - 1)
return false; // Can't possibly be a valid Inax message.
if (strict && nbits != kInaxBits)
return false; // We expect Inax to be a certain sized message.
uint64_t data = 0;
uint16_t offset = kStartOffset;
// Header
if (!matchMark(results->rawbuf[offset++], kInaxHdrMark)) return false;
if (!matchSpace(results->rawbuf[offset++], kInaxHdrSpace)) return false;
// Data
match_result_t data_result =
matchData(&(results->rawbuf[offset]), nbits, kInaxBitMark,
kInaxOneSpace, kInaxBitMark, kInaxZeroSpace);
if (data_result.success == false) return false;
data = data_result.data;
offset += data_result.used;
// Footer
if (!matchMark(results->rawbuf[offset++], kInaxBitMark)) return false;
if (offset < results->rawlen &&
!matchAtLeast(results->rawbuf[offset], kInaxMinGap))
return false;
// Compliance
// Success
results->bits = nbits;
results->value = data;
results->decode_type = INAX;
results->command = 0;
results->address = 0;
return true;
}
#endif
-6
View File
@@ -7,12 +7,6 @@
#include "IRtimer.h"
#include "IRutils.h"
// JJJJJ V V CCCC
// J V V C
// J V V C
// J J V V C
// J V CCCC
// JVC originally added by Kristian Lauszus
// (Thanks to zenwheel and other people at the original blog post)
+174 -134
View File
@@ -19,16 +19,11 @@
#ifndef ARDUINO
#include <string>
#endif
#include "IRac.h"
#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
// KKKK EEEEE LL VV VV III NN N NN AA AA TTT OO OO RRRRRR
// KK KK EE LL VV VV III NN NNN AAAAAAA TTT OO OO RR RR
// KK KK EEEEEEE LLLLLLL VVV IIIII NN NN AA AA TTT OOOO0 RR RR
// Constants
const uint16_t kKelvinatorTick = 85;
@@ -81,8 +76,8 @@ const uint8_t kKelvinatorTurbo = 1 << kKelvinatorTurboOffset;
//
// Status: STABLE / Known working.
//
void IRsend::sendKelvinator(unsigned char data[], uint16_t nbytes,
uint16_t repeat) {
void IRsend::sendKelvinator(const unsigned char data[], const uint16_t nbytes,
const uint16_t repeat) {
if (nbytes < kKelvinatorStateLength)
return; // Not enough bytes to send a proper message.
@@ -123,36 +118,38 @@ void IRsend::sendKelvinator(unsigned char data[], uint16_t nbytes,
}
#endif // SEND_KELVINATOR
IRKelvinatorAC::IRKelvinatorAC(uint16_t pin) : _irsend(pin) { stateReset(); }
IRKelvinatorAC::IRKelvinatorAC(uint16_t pin) : _irsend(pin) {
this->stateReset();
}
void IRKelvinatorAC::stateReset() {
void IRKelvinatorAC::stateReset(void) {
for (uint8_t i = 0; i < kKelvinatorStateLength; i++) remote_state[i] = 0x0;
remote_state[3] = 0x50;
remote_state[11] = 0x70;
}
void IRKelvinatorAC::begin() { _irsend.begin(); }
void IRKelvinatorAC::begin(void) { _irsend.begin(); }
void IRKelvinatorAC::fixup() {
void IRKelvinatorAC::fixup(void) {
// X-Fan mode is only valid in COOL or DRY modes.
if (getMode() != kKelvinatorCool && getMode() != kKelvinatorDry)
setXFan(false);
checksum(); // Calculate the checksums
if (this->getMode() != kKelvinatorCool && this->getMode() != kKelvinatorDry)
this->setXFan(false);
this->checksum(); // Calculate the checksums
}
#if SEND_KELVINATOR
void IRKelvinatorAC::send(const uint16_t repeat) {
fixup(); // Ensure correct settings before sending.
this->fixup(); // Ensure correct settings before sending.
_irsend.sendKelvinator(remote_state, kKelvinatorStateLength, repeat);
}
#endif // SEND_KELVINATOR
uint8_t *IRKelvinatorAC::getRaw() {
fixup(); // Ensure correct settings before sending.
uint8_t *IRKelvinatorAC::getRaw(void) {
this->fixup(); // Ensure correct settings before sending.
return remote_state;
}
void IRKelvinatorAC::setRaw(uint8_t new_code[]) {
void IRKelvinatorAC::setRaw(const uint8_t new_code[]) {
for (uint8_t i = 0; i < kKelvinatorStateLength; i++) {
remote_state[i] = new_code[i];
}
@@ -195,46 +192,46 @@ bool IRKelvinatorAC::validChecksum(const uint8_t state[],
return true;
}
void IRKelvinatorAC::on() {
void IRKelvinatorAC::on(void) {
remote_state[0] |= kKelvinatorPower;
remote_state[8] = remote_state[0]; // Duplicate to the 2nd command chunk.
}
void IRKelvinatorAC::off() {
void IRKelvinatorAC::off(void) {
remote_state[0] &= ~kKelvinatorPower;
remote_state[8] = remote_state[0]; // Duplicate to the 2nd command chunk.
}
void IRKelvinatorAC::setPower(bool state) {
if (state)
on();
void IRKelvinatorAC::setPower(const bool on) {
if (on)
this->on();
else
off();
this->off();
}
bool IRKelvinatorAC::getPower() {
return ((remote_state[0] & kKelvinatorPower) != 0);
bool IRKelvinatorAC::getPower(void) {
return remote_state[0] & kKelvinatorPower;
}
// Set the temp. in deg C
void IRKelvinatorAC::setTemp(uint8_t temp) {
temp = std::max(kKelvinatorMinTemp, temp);
void IRKelvinatorAC::setTemp(const uint8_t degrees) {
uint8_t temp = std::max(kKelvinatorMinTemp, degrees);
temp = std::min(kKelvinatorMaxTemp, temp);
remote_state[1] = (remote_state[1] & 0xF0U) | (temp - kKelvinatorMinTemp);
remote_state[9] = remote_state[1]; // Duplicate to the 2nd command chunk.
}
// Return the set temp. in deg C
uint8_t IRKelvinatorAC::getTemp() {
uint8_t IRKelvinatorAC::getTemp(void) {
return ((remote_state[1] & 0xFU) + kKelvinatorMinTemp);
}
// Set the speed of the fan, 0-5, 0 is auto, 1-5 is the speed
void IRKelvinatorAC::setFan(uint8_t fan) {
fan = std::min(kKelvinatorFanMax, fan); // Bounds check
void IRKelvinatorAC::setFan(const uint8_t speed) {
uint8_t fan = std::min(kKelvinatorFanMax, speed); // Bounds check
// Only change things if we need to.
if (fan != getFan()) {
if (fan != this->getFan()) {
// Set the basic fan values.
uint8_t fan_basic = std::min(kKelvinatorBasicFanMax, fan);
remote_state[0] = (remote_state[0] & kKelvinatorBasicFanMask) |
@@ -243,188 +240,231 @@ void IRKelvinatorAC::setFan(uint8_t fan) {
// Set the advanced(?) fan value.
remote_state[14] =
(remote_state[14] & kKelvinatorFanMask) | (fan << kKelvinatorFanOffset);
setTurbo(false); // Turbo mode is turned off if we change the fan settings.
// Turbo mode is turned off if we change the fan settings.
this->setTurbo(false);
}
}
uint8_t IRKelvinatorAC::getFan() {
uint8_t IRKelvinatorAC::getFan(void) {
return ((remote_state[14] & ~kKelvinatorFanMask) >> kKelvinatorFanOffset);
}
uint8_t IRKelvinatorAC::getMode() {
uint8_t IRKelvinatorAC::getMode(void) {
return (remote_state[0] & ~kKelvinatorModeMask);
}
void IRKelvinatorAC::setMode(uint8_t mode) {
// If we get an unexpected mode, default to AUTO.
if (mode > kKelvinatorHeat) mode = kKelvinatorAuto;
remote_state[0] = (remote_state[0] & kKelvinatorModeMask) | mode;
remote_state[8] = remote_state[0]; // Duplicate to the 2nd command chunk.
if (mode == kKelvinatorAuto || kKelvinatorDry)
// When the remote is set to Auto or Dry, it defaults to 25C and doesn't
// show it.
setTemp(kKelvinatorAutoTemp);
void IRKelvinatorAC::setMode(const uint8_t mode) {
switch (mode) {
case kKelvinatorAuto:
case kKelvinatorDry:
// When the remote is set to Auto or Dry, it defaults to 25C and doesn't
// show it.
this->setTemp(kKelvinatorAutoTemp);
// FALL-THRU
case kKelvinatorHeat:
case kKelvinatorCool:
case kKelvinatorFan:
remote_state[0] = (remote_state[0] & kKelvinatorModeMask) | mode;
remote_state[8] = remote_state[0]; // Duplicate to the 2nd command chunk.
break;
default: // If we get an unexpected mode, default to AUTO.
this->setMode(kKelvinatorAuto);
}
}
void IRKelvinatorAC::setSwingVertical(bool state) {
if (state) {
void IRKelvinatorAC::setSwingVertical(const bool on) {
if (on) {
remote_state[0] |= kKelvinatorVentSwing;
remote_state[4] |= kKelvinatorVentSwingV;
} else {
remote_state[4] &= ~kKelvinatorVentSwingV;
if (!getSwingHorizontal()) remote_state[0] &= ~kKelvinatorVentSwing;
if (!this->getSwingHorizontal()) remote_state[0] &= ~kKelvinatorVentSwing;
}
remote_state[8] = remote_state[0]; // Duplicate to the 2nd command chunk.
}
bool IRKelvinatorAC::getSwingVertical() {
return ((remote_state[4] & kKelvinatorVentSwingV) != 0);
bool IRKelvinatorAC::getSwingVertical(void) {
return remote_state[4] & kKelvinatorVentSwingV;
}
void IRKelvinatorAC::setSwingHorizontal(bool state) {
if (state) {
void IRKelvinatorAC::setSwingHorizontal(const bool on) {
if (on) {
remote_state[0] |= kKelvinatorVentSwing;
remote_state[4] |= kKelvinatorVentSwingH;
} else {
remote_state[4] &= ~kKelvinatorVentSwingH;
if (!getSwingVertical()) remote_state[0] &= ~kKelvinatorVentSwing;
if (!this->getSwingVertical()) remote_state[0] &= ~kKelvinatorVentSwing;
}
remote_state[8] = remote_state[0]; // Duplicate to the 2nd command chunk.
}
bool IRKelvinatorAC::getSwingHorizontal() {
return ((remote_state[4] & kKelvinatorVentSwingH) != 0);
bool IRKelvinatorAC::getSwingHorizontal(void) {
return remote_state[4] & kKelvinatorVentSwingH;
}
void IRKelvinatorAC::setQuiet(bool state) {
void IRKelvinatorAC::setQuiet(const bool on) {
remote_state[12] &= ~kKelvinatorQuiet;
remote_state[12] |= (state << kKelvinatorQuietOffset);
remote_state[12] |= (on << kKelvinatorQuietOffset);
}
bool IRKelvinatorAC::getQuiet() {
return ((remote_state[12] & kKelvinatorQuiet) != 0);
bool IRKelvinatorAC::getQuiet(void) {
return remote_state[12] & kKelvinatorQuiet;
}
void IRKelvinatorAC::setIonFilter(bool state) {
void IRKelvinatorAC::setIonFilter(const bool on) {
remote_state[2] &= ~kKelvinatorIonFilter;
remote_state[2] |= (state << kKelvinatorIonFilterOffset);
remote_state[2] |= (on << kKelvinatorIonFilterOffset);
remote_state[10] = remote_state[2]; // Duplicate to the 2nd command chunk.
}
bool IRKelvinatorAC::getIonFilter() {
return ((remote_state[2] & kKelvinatorIonFilter) != 0);
bool IRKelvinatorAC::getIonFilter(void) {
return remote_state[2] & kKelvinatorIonFilter;
}
void IRKelvinatorAC::setLight(bool state) {
void IRKelvinatorAC::setLight(const bool on) {
remote_state[2] &= ~kKelvinatorLight;
remote_state[2] |= (state << kKelvinatorLightOffset);
remote_state[2] |= (on << kKelvinatorLightOffset);
remote_state[10] = remote_state[2]; // Duplicate to the 2nd command chunk.
}
bool IRKelvinatorAC::getLight() {
return ((remote_state[2] & kKelvinatorLight) != 0);
bool IRKelvinatorAC::getLight(void) {
return remote_state[2] & kKelvinatorLight;
}
// Note: XFan mode is only valid in Cool or Dry mode.
void IRKelvinatorAC::setXFan(bool state) {
void IRKelvinatorAC::setXFan(const bool on) {
remote_state[2] &= ~kKelvinatorXfan;
remote_state[2] |= (state << kKelvinatorXfanOffset);
remote_state[2] |= (on << kKelvinatorXfanOffset);
remote_state[10] = remote_state[2]; // Duplicate to the 2nd command chunk.
}
bool IRKelvinatorAC::getXFan() {
return ((remote_state[2] & kKelvinatorXfan) != 0);
bool IRKelvinatorAC::getXFan(void) {
return remote_state[2] & kKelvinatorXfan;
}
// Note: Turbo mode is turned off if the fan speed is changed.
void IRKelvinatorAC::setTurbo(bool state) {
void IRKelvinatorAC::setTurbo(const bool on) {
remote_state[2] &= ~kKelvinatorTurbo;
remote_state[2] |= (state << kKelvinatorTurboOffset);
remote_state[2] |= (on << kKelvinatorTurboOffset);
remote_state[10] = remote_state[2]; // Duplicate to the 2nd command chunk.
}
bool IRKelvinatorAC::getTurbo() {
return ((remote_state[2] & kKelvinatorTurbo) != 0);
bool IRKelvinatorAC::getTurbo(void) {
return remote_state[2] & kKelvinatorTurbo;
}
// Convert a standard A/C mode into its native mode.
uint8_t IRKelvinatorAC::convertMode(const stdAc::opmode_t mode) {
switch (mode) {
case stdAc::opmode_t::kCool:
return kKelvinatorCool;
case stdAc::opmode_t::kHeat:
return kKelvinatorHeat;
case stdAc::opmode_t::kDry:
return kKelvinatorDry;
case stdAc::opmode_t::kFan:
return kKelvinatorFan;
default:
return kKelvinatorAuto;
}
}
// Convert a native mode to it's common equivalent.
stdAc::opmode_t IRKelvinatorAC::toCommonMode(const uint8_t mode) {
switch (mode) {
case kKelvinatorCool: return stdAc::opmode_t::kCool;
case kKelvinatorHeat: return stdAc::opmode_t::kHeat;
case kKelvinatorDry: return stdAc::opmode_t::kDry;
case kKelvinatorFan: return stdAc::opmode_t::kFan;
default: return stdAc::opmode_t::kAuto;
}
}
// Convert a native fan speed to it's common equivalent.
stdAc::fanspeed_t IRKelvinatorAC::toCommonFanSpeed(const uint8_t speed) {
return (stdAc::fanspeed_t)speed;
}
// Convert the A/C state to it's common equivalent.
stdAc::state_t IRKelvinatorAC::toCommon(void) {
stdAc::state_t result;
result.protocol = decode_type_t::KELVINATOR;
result.model = -1; // Unused.
result.power = this->getPower();
result.mode = this->toCommonMode(this->getMode());
result.celsius = true;
result.degrees = this->getTemp();
result.fanspeed = this->toCommonFanSpeed(this->getFan());
result.swingv = this->getSwingVertical() ? stdAc::swingv_t::kAuto :
stdAc::swingv_t::kOff;
result.swingh = this->getSwingHorizontal() ? stdAc::swingh_t::kAuto :
stdAc::swingh_t::kOff;
result.quiet = this->getQuiet();
result.turbo = this->getTurbo();
result.light = this->getLight();
result.filter = this->getIonFilter();
result.clean = this->getXFan();
// Not supported.
result.econo = false;
result.beep = false;
result.sleep = -1;
result.clock = -1;
return result;
}
// Convert the internal state into a human readable string.
#ifdef ARDUINO
String IRKelvinatorAC::toString() {
String IRKelvinatorAC::toString(void) {
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());
result.reserve(160); // Reserve some heap for the string to reduce fragging.
result += F("Power: ");
result += getPower() ? F("On") : F("Off");
result += F(", Mode: ");
result += uint64ToString(getMode());
switch (getMode()) {
case kKelvinatorAuto:
result += " (AUTO)";
result += F(" (AUTO)");
break;
case kKelvinatorCool:
result += " (COOL)";
result += F(" (COOL)");
break;
case kKelvinatorHeat:
result += " (HEAT)";
result += F(" (HEAT)");
break;
case kKelvinatorDry:
result += " (DRY)";
result += F(" (DRY)");
break;
case kKelvinatorFan:
result += " (FAN)";
result += F(" (FAN)");
break;
default:
result += " (UNKNOWN)";
result += F(" (UNKNOWN)");
}
result += ", Temp: " + uint64ToString(getTemp()) + "C";
result += ", Fan: " + uint64ToString(getFan());
result += F(", Temp: ");
result += uint64ToString(getTemp());
result += F("C, Fan: ");
result += uint64ToString(getFan());
switch (getFan()) {
case kKelvinatorFanAuto:
result += " (AUTO)";
result += F(" (AUTO)");
break;
case kKelvinatorFanMax:
result += " (MAX)";
result += F(" (MAX)");
break;
}
result += ", Turbo: ";
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";
result += F(", Turbo: ");
result += getTurbo() ? F("On") : F("Off");
result += F(", Quiet: ");
result += getQuiet() ? F("On") : F("Off");
result += F(", XFan: ");
result += getXFan() ? F("On") : F("Off");
result += F(", IonFilter: ");
result += getIonFilter() ? F("On") : F("Off");
result += F(", Light: ");
result += getLight() ? F("On") : F("Off");
result += F(", Swing (Horizontal): ");
result += getSwingHorizontal() ? F("On") : F("Off");
result += F(", Swing (Vertical): ");
result += getSwingVertical() ? F("On") : F("Off");
return result;
}
@@ -438,7 +478,7 @@ std::string IRKelvinatorAC::toString() {
// Returns:
// boolean: True if it can decode it, false if it can't.
//
// Status: ALPHA / Untested.
// Status: STABLE / Known working.
bool IRrecv::decodeKelvinator(decode_results *results, uint16_t nbits,
bool strict) {
if (results->rawlen <
@@ -540,7 +580,7 @@ bool IRrecv::decodeKelvinator(decode_results *results, uint16_t nbits,
}
// Success
results->decode_type = KELVINATOR;
results->decode_type = decode_type_t::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
+55 -43
View File
@@ -2,6 +2,18 @@
//
// Copyright 2016 David Conran
// Supports:
// Brand: Kelvinator, Model: YALIF Remote
// Brand: Kelvinator, Model: KSV26CRC A/C
// Brand: Kelvinator, Model: KSV26HRC A/C
// Brand: Kelvinator, Model: KSV35CRC A/C
// Brand: Kelvinator, Model: KSV35HRC A/C
// Brand: Kelvinator, Model: KSV53HRC A/C
// Brand: Kelvinator, Model: KSV62HRC A/C
// Brand: Kelvinator, Model: KSV70CRC A/C
// Brand: Kelvinator, Model: KSV70HRC A/C
// Brand: Kelvinator, Model: KSV80HRC A/C
#ifndef IR_KELVINATOR_H_
#define IR_KELVINATOR_H_
@@ -9,17 +21,12 @@
#include <stdint.h>
#ifndef UNIT_TEST
#include <Arduino.h>
#else
#include <string>
#endif
#include "IRremoteESP8266.h"
#include "IRsend.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
// KKKK EEEEE LL VV VV III NN N NN AA AA TTT OO OO RRRRRR
// KK KK EE LL VV VV III NN NNN AAAAAAA TTT OO OO RR RR
// KK KK EEEEEEE LLLLLLL VVV IIIII NN NN AA AA TTT OOOO0 RR RR
#ifdef UNIT_TEST
#include "IRsend_test.h"
#endif
// Constants
const uint8_t kKelvinatorAuto = 0;
@@ -128,53 +135,58 @@ class IRKelvinatorAC {
public:
explicit IRKelvinatorAC(uint16_t pin);
void stateReset();
void stateReset(void);
#if SEND_KELVINATOR
void send(const uint16_t repeat = kKelvinatorDefaultRepeat);
uint8_t calibrate(void) { return _irsend.calibrate(); }
#endif // SEND_KELVINATOR
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();
void setSwingVertical(bool state);
bool getSwingVertical();
void setSwingHorizontal(bool state);
bool getSwingHorizontal();
void setQuiet(bool state);
bool getQuiet();
void setIonFilter(bool state);
bool getIonFilter();
void setLight(bool state);
bool getLight();
void setXFan(bool state);
bool getXFan();
void setTurbo(bool state);
bool getTurbo();
uint8_t* getRaw();
void setRaw(uint8_t new_code[]);
void begin(void);
void on(void);
void off(void);
void setPower(const bool on);
bool getPower(void);
void setTemp(const uint8_t degrees);
uint8_t getTemp(void);
void setFan(const uint8_t speed);
uint8_t getFan(void);
void setMode(const uint8_t mode);
uint8_t getMode(void);
void setSwingVertical(const bool on);
bool getSwingVertical(void);
void setSwingHorizontal(const bool on);
bool getSwingHorizontal(void);
void setQuiet(const bool on);
bool getQuiet(void);
void setIonFilter(const bool on);
bool getIonFilter(void);
void setLight(const bool on);
bool getLight(void);
void setXFan(const bool on);
bool getXFan(void);
void setTurbo(const bool on);
bool getTurbo(void);
uint8_t* getRaw(void);
void setRaw(const uint8_t new_code[]);
static uint8_t calcBlockChecksum(
const uint8_t* block, const uint16_t length = kKelvinatorStateLength / 2);
static bool validChecksum(const uint8_t state[],
const uint16_t length = kKelvinatorStateLength);
#ifdef ARDUINO
String toString();
#else
std::string toString();
#endif
uint8_t convertMode(const stdAc::opmode_t mode);
static stdAc::opmode_t toCommonMode(const uint8_t mode);
static stdAc::fanspeed_t toCommonFanSpeed(const uint8_t speed);
stdAc::state_t toCommon(void);
String toString(void);
#ifndef UNIT_TEST
private:
IRsend _irsend;
#else
IRsendTest _irsend;
#endif
// The state of the IR remote in IR code form.
uint8_t remote_state[kKelvinatorStateLength];
void checksum(const uint16_t length = kKelvinatorStateLength);
void fixup();
IRsend _irsend;
void fixup(void);
};
#endif // IR_KELVINATOR_H_
+8 -13
View File
@@ -2,25 +2,18 @@
// Copyright 2015 cheaplin
// Copyright 2017, 2018 David Conran
// Supports:
// Brand: LG, Model: 6711A20083V remote
// Brand: LG, Model: AKB74395308 remote
#include "ir_LG.h"
#include <algorithm>
#include "IRrecv.h"
#include "IRsend.h"
#include "IRutils.h"
// L GGGG
// L G
// L G GG
// L G G
// LLLLL GGG
// LG decode originally added by Darryl Smith (based on the JVC protocol)
// LG send originally added by https://github.com/chaeplin
//
// Known supported devices:
// IR Remotes:
// 6711A20083V
// AKB74395308
// Constants
const uint16_t kLgTick = 50;
@@ -85,11 +78,13 @@ uint8_t calcLGChecksum(uint16_t data) {
// IR Remote models: 6711A20083V
void IRsend::sendLG(uint64_t data, uint16_t nbits, uint16_t repeat) {
uint16_t repeatHeaderMark = 0;
uint8_t duty = kDutyDefault;
if (nbits >= kLg32Bits) {
// LG 32bit protocol is near identical to Samsung except for repeats.
sendSAMSUNG(data, nbits, 0); // Send it as a single Samsung message.
repeatHeaderMark = kLg32RptHdrMark;
duty = 33;
repeat++;
} else {
// LG (28-bit) protocol.
@@ -97,7 +92,7 @@ void IRsend::sendLG(uint64_t data, uint16_t nbits, uint16_t repeat) {
sendGeneric(kLgHdrMark, kLgHdrSpace, kLgBitMark, kLgOneSpace, kLgBitMark,
kLgZeroSpace, kLgBitMark, kLgMinGap, kLgMinMessageLength, data,
nbits, 38, true, 0, // Repeats are handled later.
50);
duty);
}
// Repeat
@@ -105,7 +100,7 @@ void IRsend::sendLG(uint64_t data, uint16_t nbits, uint16_t repeat) {
if (repeat)
sendGeneric(repeatHeaderMark, kLgRptSpace, 0, 0, 0, 0, // No data is sent.
kLgBitMark, kLgMinGap, kLgMinMessageLength, 0, 0, // No data.
38, true, repeat - 1, 50);
38, true, repeat - 1, duty);
}
// Send an LG Variant-2 formatted message.
+4 -6
View File
@@ -1,14 +1,12 @@
// Copyright 2017 David Conran
// Supports:
// Brand: LG, Model: 6711A20083V remote
// Brand: LG, Model: AKB74395308 remote
#ifndef IR_LG_H_
#define IR_LG_H_
// L GGGG
// L G
// L G GG
// L G G
// LLLLL GGG
#define __STDC_LIMIT_MACROS
#include <stdint.h>
+2 -7
View File
@@ -1,20 +1,15 @@
// Copyright 2017 David Conran
// Lasertag
#include <algorithm>
#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
const uint16_t kLasertagMinSamples = 13;
const uint16_t kLasertagTick = 333;
const uint32_t kLasertagMinGap = 100000; // Completely made up amount.
const uint32_t kLasertagMinGap = kDefaultMessageGap; // Just a guess.
const uint8_t kLasertagTolerance = 0; // Percentage error margin.
const uint16_t kLasertagExcess = 0; // See kMarkExcess.
const uint16_t kLasertagDelta = 150; // Use instead of Excess and Tolerance.
Loaded 100 of 195 files, more files were not shown because too many files have changed in this diff. Show more