Merge pull request #14406 from s-hadinger/ir_281

IRremoteESP8266 library from v2.8.0 to v2.8.1
This commit is contained in:
s-hadinger 2022-01-12 21:45:10 +01:00 committed by GitHub
commit 955d807cec
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
46 changed files with 2199 additions and 186 deletions

View File

@ -8,6 +8,7 @@ All notable changes to this project will be documented in this file.
- Tasmota favicon to webbrowser tab (#14322)
### Changed
- IRremoteESP8266 library from v2.8.0 to v2.8.1
### Fixed

View File

@ -10,8 +10,8 @@
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.8.0 Now Available
Version 2.8.0 of the library is now [available](https://github.com/crankyoldgit/IRremoteESP8266/releases/latest). You can view the [Release Notes](ReleaseNotes.md) for all the significant changes.
## v2.8.1 Now Available
Version 2.8.1 of the library is now [available](https://github.com/crankyoldgit/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/crankyoldgit/IRremoteESP8266/wiki/Upgrading-to-v2.0) page.

View File

@ -11,8 +11,8 @@
Diese Programmbibliothek ermöglicht das **Senden _und_ Empfangen** von Infrarotsignalen mit [ESP8266](https://github.com/esp8266/Arduino)- und
[ESP32](https://github.com/espressif/arduino-esp32)-Mikrocontrollern mithilfe des [Arduino-Frameworks](https://www.arduino.cc/) und handelsüblichen 940nm Infrarot-LEDs undIR-Empfängermodulen, wie zum Beispiel TSOP{17,22,24,36,38,44,48}*-Demodulatoren.
## v2.8.0 jetzt verfügbar
Version 2.8.0 der Bibliothek ist nun [verfügbar](https://github.com/crankyoldgit/IRremoteESP8266/releases/latest). Die [Versionshinweise](ReleaseNotes.md) enthalten alle wichtigen Neuerungen.
## v2.8.1 jetzt verfügbar
Version 2.8.1 der Bibliothek ist nun [verfügbar](https://github.com/crankyoldgit/IRremoteESP8266/releases/latest). Die [Versionshinweise](ReleaseNotes.md) enthalten alle wichtigen Neuerungen.
#### Hinweis für Nutzer von Versionen vor v2.0
Die Benutzung der Bibliothek hat sich mit Version 2.0 leicht geändert. Einige Anpassungen im aufrufenden Code werden nötig sein, um mit Version ab 2.0 korrekt zu funktionieren. Mehr zu den Anpassungen finden sich auf unserer [Upgrade to v2.0](https://github.com/crankyoldgit/IRremoteESP8266/wiki/Upgrading-to-v2.0)-Seite.

View File

@ -10,8 +10,8 @@
Cette librairie vous permetra de **recevoir et d'envoyer des signaux** infrarouge sur le protocole [ESP8266](https://github.com/esp8266/Arduino) ou sur le protocole
[ESP32](https://github.com/espressif/arduino-esp32) en utilisant le [Arduino framework](https://www.arduino.cc/) qui utilise la norme 940nm IR LEDs et le module basique de reception d'onde IR. Exemple : TSOP{17,22,24,36,38,44,48}* modules etc.
## v2.8.0 disponible
Version 2.8.0 de la libraire est maintenant [disponible](https://github.com/crankyoldgit/IRremoteESP8266/releases/latest). Vous pouvez voir le [Release Notes](ReleaseNotes.md) pour tous les changements importants.
## v2.8.1 disponible
Version 2.8.1 de la libraire est maintenant [disponible](https://github.com/crankyoldgit/IRremoteESP8266/releases/latest). Vous pouvez voir le [Release Notes](ReleaseNotes.md) pour tous les changements importants.
#### mise à jour depuis pre-v2.0
L'utilisation de la librairie à un peu changer depuis la version in v2.0. Si vous voulez l'utiliser vous devrez changer votre utilisation aussi. Vous pouvez vous renseigner sur les précondition d'utilisation ici : [Upgrade to v2.0](https://github.com/crankyoldgit/IRremoteESP8266/wiki/Upgrading-to-v2.0) page.

View File

@ -1,5 +1,29 @@
# Release Notes
## _v2.8.1 (20220101)_
**[Bug Fixes]**
- Arduino ESP32 Core v2.0.2+ crashes due to our timer hack. (#1715 #1713)
- SONY: Fix old Sony CD-Player Remote (12 Bit) (#1714)
**[Features]**
- Add tool to convert protocol & code to raw timing info. (#1708 #1707 #1703)
- Add basic support for COOLIX48 protocol. (#1697 #1694)
- MITSUBISHI_AC: Added support for i-SAVE mode. (#1666)
- TOSHIBA_AC: Add Filter setting support. aka. Pure. (#1693 #1692)
- Airton: Add detailed A/C support. (#1688 #1670)
**[Misc]**
- Add a structured library version number. (#1717)
- Workflows Split UnitTests (#1712)
- Reduce time for workflow/Build (#1709)
- Fix some compiler & linter warnings (#1699 #1700)
- Fujitsu: Update supported A/C models (#1690 #1689 #1702 #1701)
- Remove extra `const` qualifier for char pointer (#1704)
- TCL: Update supported devices. (#1698)
- ESP32-C3: Work around for some C3 specific compiler issues. (#1696 #1695)
## _v2.8.0 (20211119)_
**[Bug Fixes]**

View File

@ -1,11 +1,11 @@
<!--- WARNING: Do NOT edit this file directly.
It is generated by './tools/scrape_supported_devices.py'.
Last generated: Fri 19 Nov 2021 00:35:37 +0000 --->
Last generated: Fri 31 Dec 2021 21:49:00 +0000 --->
# IR Protocols supported by this library
| Protocol | Brand | Model | A/C Model | Detailed A/C Support |
| --- | --- | --- | --- | --- |
| [Airton](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Airton.cpp) | **Airton** | RD1A1 remote<BR>SMVH09B-2A2A3NH ref. 409730 A/C | | - |
| [Airton](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Airton.cpp) | **[Airton](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Airton.h)** | RD1A1 remote<BR>SMVH09B-2A2A3NH ref. 409730 A/C | | Yes |
| [Airwell](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Airwell.cpp) | **[Airwell](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Airwell.h)** | DLS 21 DCI R410 AW A/C<BR>RC04 remote<BR>RC08W remote | | Yes |
| [Aiwa](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Aiwa.cpp) | **Aiwa** | RC-T501 RCU | | - |
| [Amcor](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Amcor.cpp) | **[Amcor](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Amcor.h)** | ADR-853H A/C<BR>TAC-444 remote<BR>TAC-495 remote | | Yes |
@ -15,6 +15,7 @@
| [Carrier](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Carrier.cpp) | **[Carrier/Surrey](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Carrier.h)** | 42QG5A55970 remote<BR>53NGK009/012 Inverter<BR>619EGX0090E0 A/C<BR>619EGX0120E0 A/C<BR>619EGX0180E0 A/C<BR>619EGX0220E0 A/C | | Yes |
| [Coolix](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Coolix.cpp) | **[Airwell](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Coolix.h)** | RC08B remote | | Yes |
| [Coolix](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Coolix.cpp) | **[Beko](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Coolix.h)** | BINR 070/071 split-type A/C<BR>RG57K7(B)/BGEF Remote | | Yes |
| [Coolix](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Coolix.cpp) | **[Bosch](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Coolix.h)** | B1ZAI2441W/B1ZAO2441W A/C<BR>RG36B4/BGE remote | | Yes |
| [Coolix](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Coolix.cpp) | **[Kastron](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Coolix.h)** | RG57A7/BGEF Inverter remote | | Yes |
| [Coolix](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Coolix.cpp) | **[Kaysun](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Coolix.h)** | Casual CF A/C | | Yes |
| [Coolix](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Coolix.cpp) | **[Midea](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Coolix.h)** | MS12FU-10HRDN1-QRD0GW(B) A/C<BR>MSABAU-07HRFN1-QRD0GW A/C (circa 2016)<BR>RG52D/BGE Remote | | Yes |
@ -35,7 +36,7 @@
| [EliteScreens](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_EliteScreens.cpp) | **Elite Screens** | CineTension2 / CineTension3 series<BR>Home2 / Home3 series<BR>Spectrum series<BR>VMAX Plus4 series<BR>VMAX2 / VMAX2 Plus series<BR>ZSP-IR-B / ZSP-IR-W remote | | - |
| [EliteScreens](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_EliteScreens.cpp) | **Lumene Screens** | Embassy | | - |
| [Epson](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Epson.cpp) | **Epson** | EN-TW9100W Projector<BR>EX3220 Projector<BR>EX5220 Projector<BR>EX5230 Projector<BR>EX6220 Projector<BR>EX7220 Projector<BR>VS230 Projector<BR>VS330 Projector | | - |
| [Fujitsu](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Fujitsu.cpp) | **[Fujitsu](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Fujitsu.h)** | AGTV14LAC A/C (ARRAH2E)<BR>AR-DB1 remote (ARDB1)<BR>AR-DL10 remote (ARDB1)<BR>AR-RAC1E remote (ARRAH2E)<BR>AR-RAE1E remote (ARRAH2E)<BR>AR-RAH1U remote (ARREB1E)<BR>AR-RAH2E remote (ARRAH2E)<BR>AR-REB1E remote (ARREB1E)<BR>AR-REW4E remote (ARREW4E)<BR>AR-RY4 remote (ARRY4)<BR>AST9RSGCW A/C (ARDB1)<BR>ASTB09LBC A/C (ARRY4)<BR>ASU12RLF A/C (ARREB1E)<BR>ASU30C1 A/C (ARDB1)<BR>ASYG09KETA-B A/C (ARREW4E)<BR>ASYG30LFCA A/C (ARRAH2E)<BR>ASYG7LMCA A/C (ARREB1E) | ARDB1<BR>ARJW2<BR>ARRAH2E<BR>ARREB1E<BR>ARREW4E<BR>ARRY4 | Yes |
| [Fujitsu](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Fujitsu.cpp) | **[Fujitsu](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Fujitsu.h)** | AGTV14LAC A/C (ARRAH2E)<BR>AR-DB1 remote (ARDB1)<BR>AR-DL10 remote (ARDB1)<BR>AR-RAC1E remote (ARRAH2E)<BR>AR-RAE1E remote (ARRAH2E)<BR>AR-RAH1U remote (ARREB1E)<BR>AR-RAH2E remote (ARRAH2E)<BR>AR-REB1E remote (ARREB1E)<BR>AR-REB4E remote (ARREB1E)<BR>AR-REW1E remote (ARREW4E)<BR>AR-REW4E remote (ARREW4E)<BR>AR-RY4 remote (ARRY4)<BR>AST9RSGCW A/C (ARDB1)<BR>ASTB09LBC A/C (ARRY4)<BR>ASTG09K A/C (ARREW4E)<BR>ASTG18K A/C (ARREW4E)<BR>ASU12RLF A/C (ARREB1E)<BR>ASU30C1 A/C (ARDB1)<BR>ASYG09KETA-B A/C (ARREW4E)<BR>ASYG30LFCA A/C (ARRAH2E)<BR>ASYG7LMCA A/C (ARREB1E) | ARDB1<BR>ARJW2<BR>ARRAH2E<BR>ARREB1E<BR>ARREW4E<BR>ARRY4 | Yes |
| [Fujitsu](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Fujitsu.cpp) | **[Fujitsu General](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Fujitsu.h)** | AOHG09LLC A/C (ARRAH2E)<BR>AR-JW2 remote (ARJW2)<BR>AR-RCE1E remote (ARRAH2E)<BR>ASHG09LLCA A/C (ARRAH2E) | ARDB1<BR>ARJW2<BR>ARRAH2E<BR>ARREB1E<BR>ARREW4E<BR>ARRY4 | Yes |
| [GICable](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_GICable.cpp) | **G.I. Cable** | XRC-200 remote | | - |
| [GlobalCache](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_GlobalCache.cpp) | **Global Cache** | Control Tower IR DB | | - |
@ -79,7 +80,7 @@
| [Mirage](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Mirage.cpp) | **[Mirage](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Mirage.h)** | VLU series A/C | KKG29AC1<BR>KKG9AC1 | Yes |
| [Mirage](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Mirage.cpp) | **[Tronitechnik](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Mirage.h)** | KKG29A-C1 remote<BR>Reykir 9000 A/C | KKG29AC1<BR>KKG9AC1 | Yes |
| [Mitsubishi](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Mitsubishi.cpp) | **[Mitsubishi](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Mitsubishi.h)** | HC3000 Projector (MITSUBISHI2)<BR>KM14A 0179213 remote<BR>MS-GK24VA A/C<BR>TV (MITSUBISHI) | | Yes |
| [Mitsubishi](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Mitsubishi.cpp) | **[Mitsubishi Electric](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Mitsubishi.h)** | 001CP T7WE10714 remote (MITSUBISHI136)<BR>KPOA remote (MITSUBISHI112)<BR>MLZ-RX5017AS A/C (MITSUBISHI_AC)<BR>MSH-A24WV A/C (MITSUBISHI112)<BR>MSZ-GV2519 A/C (MITSUBISHI_AC)<BR>MSZ-SF25VE3 A/C (MITSUBISHI_AC)<BR>MSZ-ZW4017S A/C (MITSUBISHI_AC)<BR>MUH-A24WV A/C (MITSUBISHI112)<BR>PEAD-RP71JAA Ducted A/C (MITSUBISHI136)<BR>RH151/M21ED6426 remote (MITSUBISHI_AC)<BR>SG153/M21EDF426 remote (MITSUBISHI_AC)<BR>SG15D remote (MITSUBISHI_AC) | | Yes |
| [Mitsubishi](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Mitsubishi.cpp) | **[Mitsubishi Electric](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Mitsubishi.h)** | 001CP T7WE10714 remote (MITSUBISHI136)<BR>KPOA remote (MITSUBISHI112)<BR>MLZ-RX5017AS A/C (MITSUBISHI_AC)<BR>MSH-A24WV A/C (MITSUBISHI112)<BR>MSZ-FHnnVE A/C (MITSUBISHI_AC)<BR>MSZ-GV2519 A/C (MITSUBISHI_AC)<BR>MSZ-SF25VE3 A/C (MITSUBISHI_AC)<BR>MSZ-ZW4017S A/C (MITSUBISHI_AC)<BR>MUH-A24WV A/C (MITSUBISHI112)<BR>PEAD-RP71JAA Ducted A/C (MITSUBISHI136)<BR>RH151/M21ED6426 remote (MITSUBISHI_AC)<BR>SG153/M21EDF426 remote (MITSUBISHI_AC)<BR>SG15D remote (MITSUBISHI_AC) | | Yes |
| [MitsubishiHeavy](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_MitsubishiHeavy.cpp) | **[Mitsubishi Heavy Industries](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_MitsubishiHeavy.h)** | RKX502A001C remote (88 bit)<BR>RLA502A700B remote (152 bit)<BR>SRKxxZJ-S A/C (88 bit)<BR>SRKxxZM-S A/C (152 bit)<BR>SRKxxZMXA-S A/C (152 bit) | | Yes |
| [Multibrackets](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Multibrackets.cpp) | **Multibrackets** | Motorized Swing mount large - 4500 | | - |
| [NEC](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_NEC.cpp) | **[Aloka](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_NEC.h)** | SleepyLights LED Lamp | | - |
@ -107,6 +108,7 @@
| [Symphony](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Symphony.cpp) | **SilverCrest** | SSVS 85 A1 Fan | | - |
| [Symphony](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Symphony.cpp) | **Symphony** | Air Cooler 3Di | | - |
| [Symphony](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Symphony.cpp) | **Westinghouse** | 78095 Remote<BR>Ceiling fan | | - |
| [Tcl](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Tcl.cpp) | **[Daewoo](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Tcl.h)** | DSB-F0934ELH-V A/C<BR>GYKQ-52E remote | GZ055BE1<BR>TAC09CHSD | Yes |
| [Tcl](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Tcl.cpp) | **[Leberg](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Tcl.h)** | LBS-TOR07 A/C (TAC09CHSD) | GZ055BE1<BR>TAC09CHSD | Yes |
| [Tcl](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Tcl.cpp) | **[TCL](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Tcl.h)** | TAC-09CHSD/XA31I A/C (TAC09CHSD) | GZ055BE1<BR>TAC09CHSD | Yes |
| [Tcl](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Tcl.cpp) | **[Teknopoint](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Tcl.h)** | Allegro SSA-09H A/C (GZ055BE1)<BR>GZ-055B-E1 remote (GZ055BE1) | GZ055BE1<BR>TAC09CHSD | Yes |
@ -114,7 +116,7 @@
| [Teco](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Teco.cpp) | **[Alaska](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Teco.h)** | SAC9010QC A/C<BR>SAC9010QC remote | | Yes |
| [Teknopoint](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Teknopoint.cpp) | **Teknopoint** | Allegro SSA-09H A/C<BR>GZ-055B-E1 remote | | - |
| [Toshiba](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Toshiba.cpp) | **[Carrier](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Toshiba.h)** | 42NQV025M2 / 38NYV025M2 A/C<BR>42NQV035M2 / 38NYV035M2 A/C<BR>42NQV050M2 / 38NYV050M2 A/C<BR>42NQV060M2 / 38NYV060M2 A/C | | Yes |
| [Toshiba](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Toshiba.cpp) | **[Toshiba](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Toshiba.h)** | Akita EVO II<BR>RAS 18SKP-ES<BR>RAS-2558V A/C<BR>RAS-B13N3KV2<BR>RAS-B13N3KVP-E<BR>WC-L03SE<BR>WH-TA04NE<BR>WH-UB03NJ remote | | Yes |
| [Toshiba](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Toshiba.cpp) | **[Toshiba](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Toshiba.h)** | Akita EVO II<BR>RAS 18SKP-ES<BR>RAS-2558V A/C<BR>RAS-25SKVP2-ND A/C<BR>RAS-B13N3KV2<BR>RAS-B13N3KVP-E<BR>WC-L03SE<BR>WH-TA01JE remote<BR>WH-TA04NE<BR>WH-UB03NJ remote | | Yes |
| [Transcold](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Transcold.cpp) | **[Transcold](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Transcold.h)** | M1-F-NO-6 A/C | | Yes |
| [Trotec](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Trotec.cpp) | **[Duux](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Trotec.h)** | Blizzard Smart 10K / DXMA04 A/C (TROTEC) | | Yes |
| [Trotec](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Trotec.cpp) | **[Trotec](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Trotec.h)** | PAC 3200 A/C (TROTEC)<BR>PAC 3550 Pro A/C (TROTEC_3550) | | Yes |
@ -149,6 +151,7 @@
- CARRIER_AC40
- CARRIER_AC64
- COOLIX
- COOLIX48
- CORONA_AC
- DAIKIN
- DAIKIN128

View File

@ -1249,7 +1249,7 @@ void handleInfo(void) {
"Built: " __DATE__
" " __TIME__ "<br>"
"Period Offset: ") + String(offset) + F("us<br>"
"IR Lib Version: " _IRREMOTEESP8266_VERSION_ "<br>"
"IR Lib Version: " _IRREMOTEESP8266_VERSION_STR "<br>"
#if defined(ESP8266)
"ESP8266 Core Version: ") + ESP.getCoreVersion() + F("<br>"
"Free Sketch Space: ") + String(maxSketchSpace() >> 10) + F("k<br>"

View File

@ -154,7 +154,7 @@ void loop() {
if (results.overflow)
Serial.printf(D_WARN_BUFFERFULL "\n", kCaptureBufferSize);
// Display the library version the message was captured with.
Serial.println(D_STR_LIBRARY " : v" _IRREMOTEESP8266_VERSION_ "\n");
Serial.println(D_STR_LIBRARY " : v" _IRREMOTEESP8266_VERSION_STR "\n");
// Display the tolerance percentage if it has been change from the default.
if (kTolerancePercentage != kTolerance)
Serial.printf(D_STR_TOLERANCE " : %d%%\n", kTolerancePercentage);

View File

@ -163,7 +163,7 @@ void loop() {
if (results.overflow)
Serial.printf(D_WARN_BUFFERFULL "\n", kCaptureBufferSize);
// Display the library version the message was captured with.
Serial.println(D_STR_LIBRARY " : v" _IRREMOTEESP8266_VERSION_ "\n");
Serial.println(D_STR_LIBRARY " : v" _IRREMOTEESP8266_VERSION_STR "\n");
// Display the tolerance percentage if it has been change from the default.
if (kTolerancePercentage != kTolerance)
Serial.printf(D_STR_TOLERANCE " : %d%%\n", kTolerancePercentage);

View File

@ -20,6 +20,8 @@
# Datatypes & Classes (KEYWORD1)
#######################################
CoronaSection KEYWORD1
IRAirtonAc KEYWORD1
IRAirwellAc KEYWORD1
IRAmcorAc KEYWORD1
IRArgoAC KEYWORD1
@ -147,6 +149,7 @@ addTempFloatToString KEYWORD2
addTempToString KEYWORD2
addToggleToString KEYWORD2
adjustRepeat KEYWORD2
airton KEYWORD2
airwell KEYWORD2
amcor KEYWORD2
argo KEYWORD2
@ -174,6 +177,7 @@ checkSum KEYWORD2
checkZjsSig KEYWORD2
checkZmsSig KEYWORD2
checksum KEYWORD2
cleanState KEYWORD2
clearOnTimerFlag KEYWORD2
clearPowerSpecial KEYWORD2
clearSensorTemp KEYWORD2
@ -211,6 +215,7 @@ decodeCOOLIX KEYWORD2
decodeCarrierAC KEYWORD2
decodeCarrierAC40 KEYWORD2
decodeCarrierAC64 KEYWORD2
decodeCoolix48 KEYWORD2
decodeCoronaAc KEYWORD2
decodeDISH KEYWORD2
decodeDaikin KEYWORD2
@ -339,6 +344,7 @@ fujitsu KEYWORD2
get10CHeat KEYWORD2
get3D KEYWORD2
get8CHeat KEYWORD2
getAbsenseDetect KEYWORD2
getBeep KEYWORD2
getBit KEYWORD2
getBoost KEYWORD2
@ -356,9 +362,11 @@ getCorrectedRawLength KEYWORD2
getCurrTime KEYWORD2
getCurrentDay KEYWORD2
getCurrentTime KEYWORD2
getDirectIndirect KEYWORD2
getDisplay KEYWORD2
getDisplayTempSource KEYWORD2
getDryGrade KEYWORD2
getEcocool KEYWORD2
getEcono KEYWORD2
getEconoToggle KEYWORD2
getEnableSensorTemp KEYWORD2
@ -377,6 +385,8 @@ getHold KEYWORD2
getHumid KEYWORD2
getHumidity KEYWORD2
getIFeel KEYWORD2
getISave10C KEYWORD2
getISee KEYWORD2
getId KEYWORD2
getInternalStateLength KEYWORD2
getIon KEYWORD2
@ -388,6 +398,7 @@ getLock KEYWORD2
getMax KEYWORD2
getMode KEYWORD2
getMold KEYWORD2
getNaturalFlow KEYWORD2
getNight KEYWORD2
getOffSleepTimer KEYWORD2
getOffTime KEYWORD2
@ -423,7 +434,9 @@ getSleepTimerEnabled KEYWORD2
getSpecial KEYWORD2
getSpeed KEYWORD2
getStartClock KEYWORD2
getState KEYWORD2
getStateLength KEYWORD2
getStatePrev KEYWORD2
getStopClock KEYWORD2
getSupercool KEYWORD2
getSwing KEYWORD2
@ -474,6 +487,7 @@ haier KEYWORD2
haier176 KEYWORD2
haierYrwo2 KEYWORD2
handleSpecialState KEYWORD2
handleToggles KEYWORD2
hasACState KEYWORD2
hasInvertedStates KEYWORD2
hasStateChanged KEYWORD2
@ -571,6 +585,7 @@ sendCOOLIX KEYWORD2
sendCarrierAC KEYWORD2
sendCarrierAC40 KEYWORD2
sendCarrierAC64 KEYWORD2
sendCoolix48 KEYWORD2
sendCoronaAc KEYWORD2
sendDISH KEYWORD2
sendDaikin KEYWORD2
@ -679,6 +694,7 @@ serialPrintUint64 KEYWORD2
set10CHeat KEYWORD2
set3D KEYWORD2
set8CHeat KEYWORD2
setAbsenseDetect KEYWORD2
setAuto KEYWORD2
setBeep KEYWORD2
setBit KEYWORD2
@ -696,9 +712,11 @@ setCommand KEYWORD2
setCurrTime KEYWORD2
setCurrentDay KEYWORD2
setCurrentTime KEYWORD2
setDirectIndirect KEYWORD2
setDisplay KEYWORD2
setDisplayTempSource KEYWORD2
setDryGrade KEYWORD2
setEcocool KEYWORD2
setEcono KEYWORD2
setEconoToggle KEYWORD2
setEnableSensorTemp KEYWORD2
@ -717,6 +735,8 @@ setHold KEYWORD2
setHumid KEYWORD2
setHumidity KEYWORD2
setIFeel KEYWORD2
setISave10C KEYWORD2
setISee KEYWORD2
setId KEYWORD2
setInvertedStates KEYWORD2
setIon KEYWORD2
@ -729,6 +749,7 @@ setMax KEYWORD2
setMode KEYWORD2
setModel KEYWORD2
setMold KEYWORD2
setNaturalFlow KEYWORD2
setNight KEYWORD2
setOffTime KEYWORD2
setOffTimeEnabled KEYWORD2
@ -819,6 +840,7 @@ technibel KEYWORD2
teco KEYWORD2
ticksHigh KEYWORD2
ticksLow KEYWORD2
toCommon KEYWORD2
toString KEYWORD2
toggleArrisRelease KEYWORD2
toggleRC5 KEYWORD2
@ -896,6 +918,7 @@ CARRIER_AC40 LITERAL1
CARRIER_AC64 LITERAL1
CARRIER_AC_BITS LITERAL1
COOLIX LITERAL1
COOLIX48 LITERAL1
COOLIX_BITS LITERAL1
CORONA_AC LITERAL1
DAIKIN LITERAL1
@ -930,6 +953,7 @@ DECODE_CARRIER_AC LITERAL1
DECODE_CARRIER_AC40 LITERAL1
DECODE_CARRIER_AC64 LITERAL1
DECODE_COOLIX LITERAL1
DECODE_COOLIX48 LITERAL1
DECODE_CORONA_AC LITERAL1
DECODE_DAIKIN LITERAL1
DECODE_DAIKIN128 LITERAL1
@ -1284,6 +1308,7 @@ SEND_CARRIER_AC LITERAL1
SEND_CARRIER_AC40 LITERAL1
SEND_CARRIER_AC64 LITERAL1
SEND_COOLIX LITERAL1
SEND_COOLIX48 LITERAL1
SEND_CORONA_AC LITERAL1
SEND_DAIKIN LITERAL1
SEND_DAIKIN128 LITERAL1
@ -1443,13 +1468,27 @@ k8CHeatStr LITERAL1
kA705Str LITERAL1
kA903Str LITERAL1
kA907Str LITERAL1
kAbsenseDetectStr LITERAL1
kAirFlowStr LITERAL1
kAirtonAuto LITERAL1
kAirtonBitMark LITERAL1
kAirtonBits LITERAL1
kAirtonCool LITERAL1
kAirtonDefaultRepeat LITERAL1
kAirtonDry LITERAL1
kAirtonFan LITERAL1
kAirtonFanAuto LITERAL1
kAirtonFanHigh LITERAL1
kAirtonFanLow LITERAL1
kAirtonFanMax LITERAL1
kAirtonFanMed LITERAL1
kAirtonFanMin LITERAL1
kAirtonFreq LITERAL1
kAirtonHdrMark LITERAL1
kAirtonHdrSpace LITERAL1
kAirtonHeat LITERAL1
kAirtonMaxTemp LITERAL1
kAirtonMinTemp LITERAL1
kAirtonOneSpace LITERAL1
kAirtonZeroSpace LITERAL1
kAirwellAuto LITERAL1
@ -1645,6 +1684,8 @@ kCommandStr LITERAL1
kCool LITERAL1
kCoolStr LITERAL1
kCoolingStr LITERAL1
kCoolix48Bits LITERAL1
kCoolix48ExtraTolerance LITERAL1
kCoolixAuto LITERAL1
kCoolixBitMark LITERAL1
kCoolixBitMarkTicks LITERAL1
@ -1997,6 +2038,8 @@ kDenonZeroSpaceTicks LITERAL1
kDg11j104Str LITERAL1
kDg11j13aStr LITERAL1
kDg11j191Str LITERAL1
kDirectIndirectModeStr LITERAL1
kDirectStr LITERAL1
kDishBitMark LITERAL1
kDishBitMarkTicks LITERAL1
kDishBits LITERAL1
@ -2478,6 +2521,7 @@ kHourStr LITERAL1
kHoursStr LITERAL1
kHumidStr LITERAL1
kIFeelStr LITERAL1
kISeeStr LITERAL1
kIdStr LITERAL1
kIdleState LITERAL1
kInaxBitMark LITERAL1
@ -2489,6 +2533,7 @@ kInaxMinRepeat LITERAL1
kInaxOneSpace LITERAL1
kInaxTick LITERAL1
kInaxZeroSpace LITERAL1
kIndirectStr LITERAL1
kInsideStr LITERAL1
kIonStr LITERAL1
kJkeStr LITERAL1
@ -2894,6 +2939,8 @@ kMitsubishiACStateLength LITERAL1
kMitsubishiAcAuto LITERAL1
kMitsubishiAcBitMark LITERAL1
kMitsubishiAcCool LITERAL1
kMitsubishiAcDirect LITERAL1
kMitsubishiAcDirectOff LITERAL1
kMitsubishiAcDry LITERAL1
kMitsubishiAcExtraTolerance LITERAL1
kMitsubishiAcFan LITERAL1
@ -2905,6 +2952,7 @@ kMitsubishiAcFanSilent LITERAL1
kMitsubishiAcHdrMark LITERAL1
kMitsubishiAcHdrSpace LITERAL1
kMitsubishiAcHeat LITERAL1
kMitsubishiAcIndirect LITERAL1
kMitsubishiAcMaxTemp LITERAL1
kMitsubishiAcMinTemp LITERAL1
kMitsubishiAcNoTimer LITERAL1

View File

@ -1,6 +1,6 @@
{
"name": "IRremoteESP8266",
"version": "2.8.0",
"version": "2.8.1",
"keywords": "infrared, ir, remote, esp8266, esp32",
"description": "Send and receive infrared signals with multiple protocols (ESP8266/ESP32)",
"repository":

View File

@ -1,5 +1,5 @@
name=IRremoteESP8266
version=2.8.0
version=2.8.1
author=David Conran, Sebastien Warin, Mark Szabo, Ken Shirriff
maintainer=David Conran, Mark Szabo, Sebastien Warin, Roi Dayan, Massimiliano Pinto, Christian Nilsson
sentence=Send and receive infrared signals with multiple protocols (ESP8266/ESP32)

View File

@ -16,6 +16,7 @@
#include "IRremoteESP8266.h"
#include "IRtext.h"
#include "IRutils.h"
#include "ir_Airton.h"
#include "ir_Airwell.h"
#include "ir_Amcor.h"
#include "ir_Argo.h"
@ -56,7 +57,7 @@
#ifndef STRCASECMP
#if defined(ESP8266)
#define STRCASECMP(LHS, RHS) \
strcasecmp_P(LHS, reinterpret_cast<const char* const>(RHS))
strcasecmp_P(LHS, reinterpret_cast<const char*>(RHS))
#else // ESP8266
#define STRCASECMP(LHS, RHS) strcasecmp(LHS, RHS)
#endif // ESP8266
@ -152,9 +153,12 @@ stdAc::state_t IRac::getStatePrev(void) { return _prev; }
/// @return true if the protocol is supported by this class, otherwise false.
bool IRac::isProtocolSupported(const decode_type_t protocol) {
switch (protocol) {
#if SEND_AIRTON
case decode_type_t::AIRTON:
#endif // SEND_AIRTON
#if SEND_AIRWELL
case decode_type_t::AIRWELL:
#endif
#endif // SEND_AIRWELL
#if SEND_AMCOR
case decode_type_t::AMCOR:
#endif
@ -326,6 +330,44 @@ bool IRac::isProtocolSupported(const decode_type_t protocol) {
}
}
#if SEND_AIRTON
/// Send an Airton 56-bit A/C message with the supplied settings.
/// @param[in, out] ac A Ptr to an IRAirtonAc object to use.
/// @param[in] on The power setting.
/// @param[in] mode The operation mode setting.
/// @param[in] degrees The temperature setting in degrees.
/// @param[in] fan The speed setting for the fan.
/// @param[in] swingv The vertical swing setting.
/// @param[in] turbo Run the device in turbo/powerful mode.
/// @param[in] light Turn on the LED/Display mode.
/// @param[in] econo Run the device in economical mode.
/// @param[in] filter Turn on the (ion/pollen/health/etc) filter mode.
/// @param[in] sleep Nr. of minutes for sleep mode.
/// @note -1 is Off, >= 0 is on.
void IRac::airton(IRAirtonAc *ac,
const bool on, const stdAc::opmode_t mode,
const float degrees, const stdAc::fanspeed_t fan,
const stdAc::swingv_t swingv, const bool turbo,
const bool light, const bool econo, const bool filter,
const int16_t sleep) {
ac->begin();
ac->setPower(on);
ac->setMode(ac->convertMode(mode));
ac->setTemp(degrees);
ac->setFan(ac->convertFan(fan));
ac->setSwingV(swingv != stdAc::swingv_t::kOff);
// No Quiet setting available.
ac->setLight(light);
ac->setHealth(filter);
ac->setTurbo(turbo);
ac->setEcono(econo);
// No Clean setting available.
// No Beep setting available.
ac->setSleep(sleep >= 0); // Convert to a boolean.
ac->send();
}
#endif // SEND_AIRTON
#if SEND_AIRWELL
/// Send an Airwell A/C message with the supplied settings.
/// @param[in, out] ac A Ptr to an IRAirwellAc object to use.
@ -1530,6 +1572,7 @@ void IRac::mitsubishi(IRMitsubishiAC *ac,
ac->setVane(ac->convertSwingV(swingv));
ac->setWideVane(ac->convertSwingH(swingh));
if (quiet) ac->setFan(kMitsubishiAcFanSilent);
ac->setISave10C(false);
// No Turbo setting available.
// No Light setting available.
// No Filter setting available.
@ -2116,11 +2159,12 @@ void IRac::teco(IRTecoAc *ac,
/// @param[in] swingv The vertical swing setting.
/// @param[in] turbo Run the device in turbo/powerful mode.
/// @param[in] econo Run the device in economical mode.
/// @param[in] filter Turn on the (Pure/ion/pollen/etc) filter mode.
void IRac::toshiba(IRToshibaAC *ac,
const bool on, const stdAc::opmode_t mode,
const float degrees, const stdAc::fanspeed_t fan,
const stdAc::swingv_t swingv,
const bool turbo, const bool econo) {
const bool turbo, const bool econo, const bool filter) {
ac->begin();
ac->setMode(ac->convertMode(mode));
ac->setTemp(degrees);
@ -2133,7 +2177,7 @@ void IRac::toshiba(IRToshibaAC *ac,
ac->setTurbo(turbo);
ac->setEcono(econo);
// No Light setting available.
// No Filter setting available.
ac->setFilter(filter);
// No Clean setting available.
// No Beep setting available.
// No Sleep setting available.
@ -2603,6 +2647,16 @@ bool IRac::sendAc(const stdAc::state_t desired, const stdAc::state_t *prev) {
#endif // (SEND_LG || SEND_SHARP_AC)
// Per vendor settings & setup.
switch (send.protocol) {
#if SEND_AIRTON
case AIRTON:
{
IRAirtonAc ac(_pin, _inverted, _modulation);
airton(&ac, send.power, send.mode, degC, send.fanspeed,
send.swingv, send.turbo, send.light, send.econo, send.filter,
send.sleep);
break;
}
#endif // SEND_AIRTON
#if SEND_AIRWELL
case AIRWELL:
{
@ -3062,7 +3116,7 @@ bool IRac::sendAc(const stdAc::state_t desired, const stdAc::state_t *prev) {
{
IRToshibaAC ac(_pin, _inverted, _modulation);
toshiba(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv,
send.turbo, send.econo);
send.turbo, send.econo, send.filter);
break;
}
#endif // SEND_TOSHIBA_AC
@ -3507,6 +3561,13 @@ namespace IRAcUtils {
/// An empty string if we can't.
String resultAcToString(const decode_results * const result) {
switch (result->decode_type) {
#if DECODE_AIRTON
case decode_type_t::AIRTON: {
IRAirtonAc ac(kGpioUnused);
ac.setRaw(result->value); // AIRTON uses value instead of state.
return ac.toString();
}
#endif // DECODE_AIRTON
#if DECODE_AIRWELL
case decode_type_t::AIRWELL: {
IRAirwellAc ac(kGpioUnused);
@ -3931,6 +3992,14 @@ namespace IRAcUtils {
) {
if (decode == NULL || result == NULL) return false; // Safety check.
switch (decode->decode_type) {
#if DECODE_AIRTON
case decode_type_t::AIRTON: {
IRAirtonAc ac(kGpioUnused);
ac.setRaw(decode->value); // Uses value instead of state.
*result = ac.toCommon();
break;
}
#endif // DECODE_AIRTON
#if DECODE_AIRWELL
case decode_type_t::AIRWELL: {
IRAirwellAc ac(kGpioUnused);

View File

@ -7,6 +7,7 @@
#include <Arduino.h>
#endif
#include "IRremoteESP8266.h"
#include "ir_Airton.h"
#include "ir_Airwell.h"
#include "ir_Amcor.h"
#include "ir_Argo.h"
@ -109,6 +110,14 @@ class IRac {
bool _inverted; ///< IR LED is lit when GPIO is LOW (true) or HIGH (false)?
bool _modulation; ///< Is frequency modulation to be used?
stdAc::state_t _prev; ///< The state we expect the device to currently be in.
#if SEND_AIRTON
void airton(IRAirtonAc *ac,
const bool on, const stdAc::opmode_t mode,
const float degrees, const stdAc::fanspeed_t fan,
const stdAc::swingv_t swingv, const bool turbo,
const bool light, const bool econo, const bool filter,
const int16_t sleep = -1);
#endif // SEND_AIRTON
#if SEND_AIRWELL
void airwell(IRAirwellAc *ac,
const bool on, const stdAc::opmode_t mode, const float degrees,
@ -457,7 +466,7 @@ void electra(IRElectraAc *ac,
void toshiba(IRToshibaAC *ac,
const bool on, const stdAc::opmode_t mode, const float degrees,
const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv,
const bool turbo, const bool econo);
const bool turbo, const bool econo, const bool filter);
#endif // SEND_TOSHIBA_AC
#if SEND_TROTEC
void trotec(IRTrotecESP *ac,

View File

@ -13,7 +13,7 @@ extern "C" {
}
#endif // ESP8266
#include <Arduino.h>
#endif
#endif // UNIT_TEST
#include <algorithm>
#ifdef UNIT_TEST
#include <cassert>
@ -56,6 +56,20 @@ static ETSTimer timer;
} // namespace _IRrecv
#endif // ESP8266
#if defined(ESP32)
// We need a horrible timer hack for ESP32 Arduino framework < v2.0.0
#if !defined(_ESP32_IRRECV_TIMER_HACK)
// Version check
#if ( defined(ESP_ARDUINO_VERSION_MAJOR) && (ESP_ARDUINO_VERSION_MAJOR >= 2) )
// No need for the hack if we are running version >= 2.0.0
#define _ESP32_IRRECV_TIMER_HACK false
#else // Version check
// If no ESP_ARDUINO_VERSION_MAJOR is defined, or less than 2, then we are
// using an old ESP32 core, so we need the hack.
#define _ESP32_IRRECV_TIMER_HACK true
#endif // Version check
#endif // !defined(_ESP32_IRRECV_TIMER_HACK)
#if _ESP32_IRRECV_TIMER_HACK
// Required structs/types from:
// https://github.com/espressif/arduino-esp32/blob/6b0114366baf986c155e8173ab7c22bc0c5fcedc/cores/esp32/esp32-hal-timer.c#L28-L58
// These are needed to be able to directly manipulate the timer registers from
@ -117,7 +131,7 @@ typedef struct hw_timer_s {
uint8_t timer;
portMUX_TYPE lock;
} hw_timer_t;
// End of Horrible Hack.
#endif // _ESP32_IRRECV_TIMER_HACK / End of Horrible Hack.
namespace _IRrecv {
static hw_timer_t * timer = NULL;
@ -211,6 +225,7 @@ static void USE_IRAM_ATTR gpio_intr() {
#if defined(ESP32)
// Reset the timeout.
//
#if _ESP32_IRRECV_TIMER_HACK
// The following three lines of code are the equiv of:
// `timerWrite(timer, 0);`
// We can't call that routine safely from inside an ISR as that procedure
@ -226,6 +241,10 @@ static void USE_IRAM_ATTR gpio_intr() {
// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1350
// @see https://github.com/espressif/arduino-esp32/blob/6b0114366baf986c155e8173ab7c22bc0c5fcedc/cores/esp32/esp32-hal-timer.c#L176-L178
timer->dev->config.alarm_en = 1;
#else // _ESP32_IRRECV_TIMER_HACK
timerWrite(timer, 0);
timerAlarmEnable(timer);
#endif // _ESP32_IRRECV_TIMER_HACK
#endif // ESP32
}
#endif // UNIT_TEST
@ -337,7 +356,9 @@ void IRrecv::enableIRIn(const bool pullup) {
// Set the timer so it only fires once, and set it's trigger in uSeconds.
timerAlarmWrite(timer, MS_TO_USEC(params.timeout), ONCE);
// Note: Interrupt needs to be attached before it can be enabled or disabled.
timerAttachInterrupt(timer, &read_timeout, true);
// Note: EDGE (true) is not supported, use LEVEL (false). Ref: #1713
// See: https://github.com/espressif/arduino-esp32/blob/caef4006af491130136b219c1205bdcf8f08bf2b/cores/esp32/esp32-hal-timer.c#L224-L227
timerAttachInterrupt(timer, &read_timeout, false);
#endif // ESP32
// Initialise state machine variables
@ -695,9 +716,9 @@ bool IRrecv::decode(decode_results *results, irparams_t *save,
if (decodeSharp(results, offset)) return true;
#endif
#if DECODE_COOLIX
DPRINTLN("Attempting Coolix decode");
DPRINTLN("Attempting Coolix 24-bit decode");
if (decodeCOOLIX(results, offset)) return true;
#endif
#endif // DECODE_COOLIX
#if DECODE_NIKAI
DPRINTLN("Attempting Nikai decode");
if (decodeNikai(results, offset)) return true;
@ -1047,6 +1068,10 @@ bool IRrecv::decode(decode_results *results, irparams_t *save,
DPRINTLN("Attempting Airton decode");
if (decodeAirton(results, offset)) return true;
#endif // DECODE_AIRTON
#if DECODE_COOLIX48
DPRINTLN("Attempting Coolix 48-bit decode");
if (decodeCoolix48(results, offset)) return true;
#endif // DECODE_COOLIX48
// Typically new protocols are added above this line.
}
#if DECODE_HASH
@ -1079,7 +1104,7 @@ uint32_t IRrecv::ticksLow(const uint32_t usecs, const uint8_t tolerance,
// max() used to ensure the result can't drop below 0 before the cast.
return ((uint32_t)std::max(
(int32_t)(usecs * (1.0 - _validTolerance(tolerance) / 100.0) - delta),
0));
(int32_t)0));
}
/// Calculate the upper bound of the nr. of ticks.
@ -1162,7 +1187,8 @@ bool IRrecv::matchAtLeast(uint32_t measured, uint32_t desired,
// We really should never get a value of 0, except as the last value
// in the buffer. If that is the case, then assume infinity and return true.
if (measured == 0) return true;
return measured >= ticksLow(std::min(desired, MS_TO_USEC(params.timeout)),
return measured >= ticksLow(std::min(desired,
(uint32_t)MS_TO_USEC(params.timeout)),
tolerance, delta);
}

View File

@ -419,7 +419,12 @@ class IRrecv {
bool decodeCOOLIX(decode_results *results, uint16_t offset = kStartOffset,
const uint16_t nbits = kCoolixBits,
const bool strict = true);
#endif
#endif // DECODE_COOLIX
#if DECODE_COOLIX48
bool decodeCoolix48(decode_results *results, uint16_t offset = kStartOffset,
const uint16_t nbits = kCoolix48Bits,
const bool strict = true);
#endif // DECODE_COOLIX48
#if DECODE_DENON
bool decodeDenon(decode_results *results, uint16_t offset = kStartOffset,
const uint16_t nbits = kDenonBits,

View File

@ -52,8 +52,29 @@
#include <string>
#endif // UNIT_TEST
// Library Version
#define _IRREMOTEESP8266_VERSION_ "2.8.0"
// Library Version Information
// Major version number (X.x.x)
#define _IRREMOTEESP8266_VERSION_MAJOR 2
// Minor version number (x.X.x)
#define _IRREMOTEESP8266_VERSION_MINOR 8
// Patch version number (x.x.X)
#define _IRREMOTEESP8266_VERSION_PATCH 1
// Macro to convert version info into an integer
#define _IRREMOTEESP8266_VERSION_VAL(major, minor, patch) \
((major << 16) | (minor << 8) | (patch))
// Macro to convert literal into a string
#define MKSTR(x) #x
// Integer version
#define _IRREMOTEESP8266_VERSION _IRREMOTEESP8266_VERSION_VAL(\
_IRREMOTEESP8266_VERSION_MAJOR, \
_IRREMOTEESP8266_VERSION_MINOR, \
_IRREMOTEESP8266_VERSION_PATCH)
// String version
#define _IRREMOTEESP8266_VERSION_STR MKSTR(_IRREMOTEESP8266_VERSION_MAJOR) "." \
MKSTR(_IRREMOTEESP8266_VERSION_MINOR) "." \
MKSTR(_IRREMOTEESP8266_VERSION_PATCH)
// String version (DEPRECATED)
#define _IRREMOTEESP8266_VERSION_ _IRREMOTEESP8266_VERSION_STR
// Set the language & locale for the library. See the `locale` dir for options.
#ifndef _IR_LOCALE_
@ -314,6 +335,13 @@
#define SEND_COOLIX _IR_ENABLE_DEFAULT_
#endif // SEND_COOLIX
#ifndef DECODE_COOLIX48
#define DECODE_COOLIX48 _IR_ENABLE_DEFAULT_
#endif // DECODE_COOLIX48
#ifndef SEND_COOLIX48
#define SEND_COOLIX48 _IR_ENABLE_DEFAULT_
#endif // SEND_COOLIX48
#ifndef DECODE_GLOBALCACHE
#define DECODE_GLOBALCACHE false // Not applicable.
#endif // DECODE_GLOBALCACHE
@ -975,8 +1003,9 @@ enum decode_type_t {
ARRIS,
RHOSS,
AIRTON,
COOLIX48, // 110
// Add new entries before this one, and update it to point to the last entry.
kLastDecodeType = AIRTON,
kLastDecodeType = COOLIX48,
};
// Message lengths & required repeat values
@ -998,6 +1027,7 @@ const uint16_t kArgoBits = kArgoStateLength * 8;
const uint16_t kArgoDefaultRepeat = kNoRepeat;
const uint16_t kArrisBits = 32;
const uint16_t kCoolixBits = 24;
const uint16_t kCoolix48Bits = kCoolixBits * 2;
const uint16_t kCoolixDefaultRepeat = kSingleRepeat;
const uint16_t kCarrierAcBits = 32;
const uint16_t kCarrierAcMinRepeat = kNoRepeat;

View File

@ -560,6 +560,7 @@ uint16_t IRsend::minRepeats(const decode_type_t protocol) {
case AIWA_RC_T501:
case AMCOR:
case COOLIX:
case COOLIX48:
case ELITESCREENS:
case GICABLE:
case INAX:
@ -661,6 +662,7 @@ uint16_t IRsend::defaultBits(const decode_type_t protocol) {
return kDoshishaBits; // 40
case SANYO_LC7461:
return kSanyoLC7461Bits; // 42
case COOLIX48:
case GOODWEATHER:
case KELON:
case MIDEA:
@ -829,7 +831,12 @@ bool IRsend::send(const decode_type_t type, const uint64_t data,
case COOLIX:
sendCOOLIX(data, nbits, min_repeat);
break;
#endif
#endif // SEND_COOLIX
#if SEND_COOLIX48
case COOLIX48:
sendCoolix48(data, nbits, min_repeat);
break;
#endif // SEND_COOLIX48
#if SEND_DAIKIN64
case DAIKIN64:
sendDaikin64(data, nbits, min_repeat);

View File

@ -42,78 +42,78 @@ const uint32_t kDefaultMessageGap = 100000;
/// Enumerators and Structures for the Common A/C API.
namespace stdAc {
/// Common A/C settings for A/C operating modes.
enum class opmode_t {
kOff = -1,
kAuto = 0,
kCool = 1,
kHeat = 2,
kDry = 3,
kFan = 4,
// Add new entries before this one, and update it to point to the last entry
kLastOpmodeEnum = kFan,
};
/// Common A/C settings for A/C operating modes.
enum class opmode_t {
kOff = -1,
kAuto = 0,
kCool = 1,
kHeat = 2,
kDry = 3,
kFan = 4,
// Add new entries before this one, and update it to point to the last entry
kLastOpmodeEnum = kFan,
};
/// Common A/C settings for Fan Speeds.
enum class fanspeed_t {
kAuto = 0,
kMin = 1,
kLow = 2,
kMedium = 3,
kHigh = 4,
kMax = 5,
// Add new entries before this one, and update it to point to the last entry
kLastFanspeedEnum = kMax,
};
/// Common A/C settings for Fan Speeds.
enum class fanspeed_t {
kAuto = 0,
kMin = 1,
kLow = 2,
kMedium = 3,
kHigh = 4,
kMax = 5,
// Add new entries before this one, and update it to point to the last entry
kLastFanspeedEnum = kMax,
};
/// Common A/C settings for Vertical Swing.
enum class swingv_t {
kOff = -1,
kAuto = 0,
kHighest = 1,
kHigh = 2,
kMiddle = 3,
kLow = 4,
kLowest = 5,
// Add new entries before this one, and update it to point to the last entry
kLastSwingvEnum = kLowest,
};
/// Common A/C settings for Vertical Swing.
enum class swingv_t {
kOff = -1,
kAuto = 0,
kHighest = 1,
kHigh = 2,
kMiddle = 3,
kLow = 4,
kLowest = 5,
// Add new entries before this one, and update it to point to the last entry
kLastSwingvEnum = kLowest,
};
/// Common A/C settings for Horizontal Swing.
enum class swingh_t {
kOff = -1,
kAuto = 0, // a.k.a. On.
kLeftMax = 1,
kLeft = 2,
kMiddle = 3,
kRight = 4,
kRightMax = 5,
kWide = 6, // a.k.a. left & right at the same time.
// Add new entries before this one, and update it to point to the last entry
kLastSwinghEnum = kWide,
};
/// Common A/C settings for Horizontal Swing.
enum class swingh_t {
kOff = -1,
kAuto = 0, // a.k.a. On.
kLeftMax = 1,
kLeft = 2,
kMiddle = 3,
kRight = 4,
kRightMax = 5,
kWide = 6, // a.k.a. left & right at the same time.
// Add new entries before this one, and update it to point to the last entry
kLastSwinghEnum = kWide,
};
/// Structure to hold a common A/C state.
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;
/// Structure to hold a common A/C state.
struct state_t {
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;
};
}; // namespace stdAc
/// Fujitsu A/C model numbers
@ -381,9 +381,13 @@ class IRsend {
uint16_t repeat = kNoRepeat);
#endif
#if SEND_COOLIX
void sendCOOLIX(uint64_t data, uint16_t nbits = kCoolixBits,
uint16_t repeat = kCoolixDefaultRepeat);
#endif
void sendCOOLIX(const uint64_t data, const uint16_t nbits = kCoolixBits,
const uint16_t repeat = kCoolixDefaultRepeat);
#endif // SEND_COOLIX
#if SEND_COOLIX48
void sendCoolix48(const uint64_t data, const uint16_t nbits = kCoolix48Bits,
const uint16_t repeat = kCoolixDefaultRepeat);
#endif // SEND_COOLIX48
#if SEND_WHYNTER
void sendWhynter(const uint64_t data, const uint16_t nbits = kWhynterBits,
const uint16_t repeat = kNoRepeat);

View File

@ -81,6 +81,14 @@ IRTEXT_CONST_STRING(kHoldStr, D_STR_HOLD); ///< "Hold"
IRTEXT_CONST_STRING(kButtonStr, D_STR_BUTTON); ///< "Button"
IRTEXT_CONST_STRING(k8CHeatStr, D_STR_8C_HEAT); ///< "8C Heat"
IRTEXT_CONST_STRING(k10CHeatStr, D_STR_10C_HEAT); ///< "10C Heat"
IRTEXT_CONST_STRING(kISeeStr, D_STR_ISEE); ///< "ISee"
IRTEXT_CONST_STRING(kAbsenseDetectStr, D_STR_ABSENSEDETECT);
///< "AbsenseDetect"
IRTEXT_CONST_STRING(kDirectIndirectModeStr, D_STR_DIRECTINDIRECTMODE);
///< "Direct/Indirect mode"
IRTEXT_CONST_STRING(kDirectStr, D_STR_DIRECT); ///< "Direct"
IRTEXT_CONST_STRING(kIndirectStr, D_STR_INDIRECT); ///< "Indirect"
IRTEXT_CONST_STRING(kNightStr, D_STR_NIGHT); ///< "Night"
IRTEXT_CONST_STRING(kSilentStr, D_STR_SILENT); ///< "Silent"
IRTEXT_CONST_STRING(kFilterStr, D_STR_FILTER); ///< "Filter"
@ -382,6 +390,7 @@ IRTEXT_CONST_BLOB_DECL(kAllProtocolNamesStr) {
D_STR_ARRIS "\x0"
D_STR_RHOSS "\x0"
D_STR_AIRTON "\x0"
D_STR_COOLIX48 "\x0"
///< New protocol strings should be added just above this line.
"\x0" ///< This string requires double null termination.
};

View File

@ -231,5 +231,11 @@ extern IRTEXT_CONST_PTR(kYbofbStr);
extern IRTEXT_CONST_PTR(kYesStr);
extern IRTEXT_CONST_PTR(kZoneFollowStr);
extern IRTEXT_CONST_PTR(kAllProtocolNamesStr);
extern IRTEXT_CONST_PTR(kISeeStr);
extern IRTEXT_CONST_PTR(kEcocoolStr);
extern IRTEXT_CONST_PTR(kAbsenseDetectStr);
extern IRTEXT_CONST_PTR(kDirectIndirectModeStr);
extern IRTEXT_CONST_PTR(kDirectStr);
extern IRTEXT_CONST_PTR(kIndirectStr);
#endif // IRTEXT_H_

View File

@ -1049,6 +1049,21 @@ namespace irutils {
return nibbleonly ? sum & 0xF : sum;
}
/// Sum all the bytes together in an integer.
/// @param[in] data The integer to be summed.
/// @param[in] count The number of bytes to sum. Starts from LSB. Max of 8.
/// @param[in] init Starting value of the calculation to use. (Default is 0)
/// @param[in] byteonly true, the result is 8 bits. false, it's 16 bits.
/// @return The 8/16-bit calculated result of all the bytes and init value.
uint16_t sumBytes(const uint64_t data, const uint8_t count,
const uint8_t init, const bool byteonly) {
uint16_t sum = init;
uint64_t copy = data;
const uint8_t nrofbytes = (count < 8) ? count : (64 / 8);
for (uint8_t i = 0; i < nrofbytes; i++, copy >>= 8) sum += (copy & 0xFF);
return byteonly ? sum & 0xFF : sum;
}
/// Convert a byte of Binary Coded Decimal(BCD) into an Integer.
/// @param[in] bcd The BCD value.
/// @return A normal Integer value.

View File

@ -94,6 +94,8 @@ namespace irutils {
const uint8_t init = 0);
uint8_t sumNibbles(const uint64_t data, const uint8_t count = 16,
const uint8_t init = 0, const bool nibbleonly = true);
uint16_t sumBytes(const uint64_t data, const uint8_t count = 8,
const uint8_t init = 0, const bool byteonly = true);
uint8_t bcdToUint8(const uint8_t bcd);
uint8_t uint8ToBcd(const uint8_t integer);
bool getBit(const uint64_t data, const uint8_t position,

View File

@ -3,12 +3,11 @@
/// @brief Support for Airton protocol
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1670
// Supports:
// Brand: Airton, Model: SMVH09B-2A2A3NH ref. 409730 A/C
// Brand: Airton, Model: RD1A1 remote
#include "ir_Airton.h"
#include <algorithm>
#include "IRrecv.h"
#include "IRsend.h"
#include "IRtext.h"
#include "IRutils.h"
const uint16_t kAirtonHdrMark = 6630;
@ -18,6 +17,12 @@ const uint16_t kAirtonOneSpace = 1260;
const uint16_t kAirtonZeroSpace = 430;
const uint16_t kAirtonFreq = 38000; // Hz. (Just a guess)
using irutils::addBoolToString;
using irutils::addModeToString;
using irutils::addFanToString;
using irutils::addTempToString;
using irutils::sumBytes;
#if SEND_AIRTON
// Function should be safe up to 64 bits.
/// Send a Airton formatted message.
@ -59,7 +64,8 @@ bool IRrecv::decodeAirton(decode_results *results, uint16_t offset,
kAirtonBitMark, kAirtonZeroSpace,
kAirtonBitMark, kDefaultMessageGap,
true, kUseDefTol, kMarkExcess, false)) return false;
// Compliance
if (strict && !IRAirtonAc::validChecksum(results->value)) return false;
// Success
results->decode_type = decode_type_t::AIRTON;
results->bits = nbits;
@ -68,3 +74,289 @@ bool IRrecv::decodeAirton(decode_results *results, uint16_t offset,
return true;
}
#endif // DECODE_AIRTON
/// Class constructor
/// @param[in] pin GPIO to be used when sending.
/// @param[in] inverted Is the output signal to be inverted?
/// @param[in] use_modulation Is frequency modulation to be used?
IRAirtonAc::IRAirtonAc(const uint16_t pin, const bool inverted,
const bool use_modulation)
: _irsend(pin, inverted, use_modulation) { stateReset(); }
/// Set up hardware to be able to send a message.
void IRAirtonAc::begin(void) { _irsend.begin(); }
#if SEND_AIRTON
/// Send the current internal state as an IR message.
/// @param[in] repeat Nr. of times the message will be repeated.
void IRAirtonAc::send(const uint16_t repeat) {
_irsend.sendAirton(getRaw(), kAirtonBits, repeat);
}
#endif // SEND_AIRTON
/// Calculate the checksum for the supplied state.
/// @param[in] state The source state to generate the checksum from.
/// @return The checksum value.
uint8_t IRAirtonAc::calcChecksum(const uint64_t state) {
return (uint8_t)(0x7F - sumBytes(state, 6)) ^ 0x2C;
}
/// Verify the checksum is valid for a given state.
/// @param[in] state The value to verify the checksum of.
/// @return A boolean indicating if it's checksum is valid.
bool IRAirtonAc::validChecksum(const uint64_t state) {
AirtonProtocol p;
p.raw = state;
return p.Sum == IRAirtonAc::calcChecksum(state);
}
/// Update the checksum value for the internal state.
void IRAirtonAc::checksum(void) { _.Sum = IRAirtonAc::calcChecksum(_.raw); }
/// Reset the internals of the object to a known good state.
void IRAirtonAc::stateReset(void) { setRaw(0x11D3); }
/// Get the raw state of the object, suitable to be sent with the appropriate
/// IRsend object method.
/// @return A copy to the internal state.
uint64_t IRAirtonAc::getRaw(void) {
checksum(); // Ensure correct bit array before returning
return _.raw;
}
/// Set the raw state of the object.
/// @param[in] state The raw state from the native IR message.
void IRAirtonAc::setRaw(const uint64_t state) { _.raw = state; }
/// Set the internal state to have the power on.
void IRAirtonAc::on(void) { setPower(true); }
/// Set the internal state to have the power off.
void IRAirtonAc::off(void) { setPower(false); }
/// Set the internal state to have the desired power.
/// @param[in] on The desired power state.
void IRAirtonAc::setPower(const bool on) {
_.Power = on;
setMode(getMode()); // Re-do the mode incase we need to do something special.
}
/// Get the power setting from the internal state.
/// @return A boolean indicating the power setting.
bool IRAirtonAc::getPower(void) const { return _.Power; }
/// Get the current operation mode setting.
/// @return The current operation mode.
uint8_t IRAirtonAc::getMode(void) const { return _.Mode; }
/// Set the desired operation mode.
/// @param[in] mode The desired operation mode.
void IRAirtonAc::setMode(const uint8_t mode) {
// Changing the mode always removes the sleep setting.
if (mode != _.Mode) setSleep(false);
// Set the actual mode.
_.Mode = (mode > kAirtonHeat) ? kAirtonAuto : mode;
// Handle special settings for each mode.
switch (_.Mode) {
case kAirtonAuto:
setTemp(25); // Auto has a fixed temp.
_.NotAutoOn = !getPower();
break;
case kAirtonHeat:
// When powered on and in Heat mode, set a special bit.
_.HeatOn = getPower();
// FALL-THRU
default:
_.NotAutoOn = true;
}
// Reset the economy setting if we need to.
setEcono(getEcono());
}
/// Convert a stdAc::opmode_t enum into its native mode.
/// @param[in] mode The enum to be converted.
/// @return The native equivalent of the enum.
uint8_t IRAirtonAc::convertMode(const stdAc::opmode_t mode) {
switch (mode) {
case stdAc::opmode_t::kCool: return kAirtonCool;
case stdAc::opmode_t::kHeat: return kAirtonHeat;
case stdAc::opmode_t::kDry: return kAirtonDry;
case stdAc::opmode_t::kFan: return kAirtonFan;
default: return kAirtonAuto;
}
}
/// Convert a native mode into its stdAc equivalent.
/// @param[in] mode The native setting to be converted.
/// @return The stdAc equivalent of the native setting.
stdAc::opmode_t IRAirtonAc::toCommonMode(const uint8_t mode) {
switch (mode) {
case kAirtonCool: return stdAc::opmode_t::kCool;
case kAirtonHeat: return stdAc::opmode_t::kHeat;
case kAirtonDry: return stdAc::opmode_t::kDry;
case kAirtonFan: return stdAc::opmode_t::kFan;
default: return stdAc::opmode_t::kAuto;
}
}
/// Set the temperature.
/// @param[in] degrees The temperature in degrees celsius.
void IRAirtonAc::setTemp(const uint8_t degrees) {
uint8_t temp = std::max(kAirtonMinTemp, degrees);
temp = std::min(kAirtonMaxTemp, temp);
if (_.Mode == kAirtonAuto) temp = kAirtonMaxTemp; // Auto has a fixed temp.
_.Temp = temp - kAirtonMinTemp;
}
/// Get the current temperature setting.
/// @return Get current setting for temp. in degrees celsius.
uint8_t IRAirtonAc::getTemp(void) const { return _.Temp + kAirtonMinTemp; }
/// Set the speed of the fan.
/// @param[in] speed The desired setting.
void IRAirtonAc::setFan(const uint8_t speed) {
_.Fan = (speed > kAirtonFanMax) ? kAirtonFanAuto : speed;
}
/// Get the current fan speed setting.
/// @return The current fan speed.
uint8_t IRAirtonAc::getFan(void) const { return _.Fan; }
/// Convert a stdAc::fanspeed_t enum into it's native speed.
/// @param[in] speed The enum to be converted.
/// @return The native equivalent of the enum.
uint8_t IRAirtonAc::convertFan(const stdAc::fanspeed_t speed) {
switch (speed) {
case stdAc::fanspeed_t::kMin: return kAirtonFanMin;
case stdAc::fanspeed_t::kLow: return kAirtonFanLow;
case stdAc::fanspeed_t::kMedium: return kAirtonFanMed;
case stdAc::fanspeed_t::kHigh: return kAirtonFanHigh;
case stdAc::fanspeed_t::kMax: return kAirtonFanMax;
default: return kAirtonFanAuto;
}
}
/// Convert a native fan speed into its stdAc equivalent.
/// @param[in] speed The native setting to be converted.
/// @return The stdAc equivalent of the native setting.
stdAc::fanspeed_t IRAirtonAc::toCommonFanSpeed(const uint8_t speed) {
switch (speed) {
case kAirtonFanMax: return stdAc::fanspeed_t::kMax;
case kAirtonFanHigh: return stdAc::fanspeed_t::kHigh;
case kAirtonFanMed: return stdAc::fanspeed_t::kMedium;
case kAirtonFanLow: return stdAc::fanspeed_t::kLow;
case kAirtonFanMin: return stdAc::fanspeed_t::kMin;
default: return stdAc::fanspeed_t::kAuto;
}
}
/// Set the Vertical Swing setting of the A/C.
/// @param[in] on true, the setting is on. false, the setting is off.
void IRAirtonAc::setSwingV(const bool on) { _.SwingV = on; }
/// Get the Vertical Swing setting of the A/C.
/// @return true, the setting is on. false, the setting is off.
bool IRAirtonAc::getSwingV(void) const { return _.SwingV; }
/// Set the Light/LED/Display setting of the A/C.
/// @param[in] on true, the setting is on. false, the setting is off.
void IRAirtonAc::setLight(const bool on) { _.Light = on; }
/// Get the Light/LED/Display setting of the A/C.
/// @return true, the setting is on. false, the setting is off.
bool IRAirtonAc::getLight(void) const { return _.Light; }
/// Set the Economy setting of the A/C.
/// @param[in] on true, the setting is on. false, the setting is off.
/// @note Only available in Cool mode.
void IRAirtonAc::setEcono(const bool on) {
_.Econo = on && (getMode() == kAirtonCool);
}
/// Get the Economy setting of the A/C.
/// @return true, the setting is on. false, the setting is off.
bool IRAirtonAc::getEcono(void) const { return _.Econo; }
/// Set the Turbo setting of the A/C.
/// @param[in] on true, the setting is on. false, the setting is off.
void IRAirtonAc::setTurbo(const bool on) {
_.Turbo = on;
// Pressing the turbo button sets the fan to max as well.
if (on) setFan(kAirtonFanMax);
}
/// Get the Turbo setting of the A/C.
/// @return true, the setting is on. false, the setting is off.
bool IRAirtonAc::getTurbo(void) const { return _.Turbo; }
/// Set the Sleep setting of the A/C.
/// @param[in] on true, the setting is on. false, the setting is off.
/// @note Sleep not available in fan or auto mode.
void IRAirtonAc::setSleep(const bool on) {
switch (getMode()) {
case kAirtonAuto:
case kAirtonFan: _.Sleep = false; break;
default: _.Sleep = on;
}
}
/// Get the Sleep setting of the A/C.
/// @return true, the setting is on. false, the setting is off.
bool IRAirtonAc::getSleep(void) const { return _.Sleep; }
/// Set the Health/Filter setting of the A/C.
/// @param[in] on true, the setting is on. false, the setting is off.
void IRAirtonAc::setHealth(const bool on) { _.Health = on; }
/// Get the Health/Filter setting of the A/C.
/// @return true, the setting is on. false, the setting is off.
bool IRAirtonAc::getHealth(void) const { return _.Health; }
/// Convert the current internal state into its stdAc::state_t equivalent.
/// @return The stdAc equivalent of the native settings.
stdAc::state_t IRAirtonAc::toCommon(void) const {
stdAc::state_t result;
result.protocol = decode_type_t::AIRTON;
result.power = getPower();
result.mode = toCommonMode(getMode());
result.celsius = true;
result.degrees = getTemp();
result.fanspeed = toCommonFanSpeed(getFan());
result.swingv = getSwingV() ? stdAc::swingv_t::kAuto : stdAc::swingv_t::kOff;
result.econo = getEcono();
result.turbo = getTurbo();
result.filter = getHealth();
result.light = getLight();
result.sleep = getSleep() ? 0 : -1;
// Not supported.
result.model = -1;
result.swingh = stdAc::swingh_t::kOff;
result.quiet = false;
result.clean = false;
result.beep = false;
result.clock = -1;
return result;
}
/// Convert the current internal state into a human readable string.
/// @return A human readable string.
String IRAirtonAc::toString(void) const {
String result = "";
result.reserve(135); // Reserve some heap for the string to reduce fragging.
result += addBoolToString(getPower(), kPowerStr, false);
result += addModeToString(_.Mode, kAirtonAuto, kAirtonCool,
kAirtonHeat, kAirtonDry, kAirtonFan);
result += addFanToString(_.Fan, kAirtonFanHigh, kAirtonFanLow,
kAirtonFanAuto, kAirtonFanMin, kAirtonFanMed,
kAirtonFanMax);
result += addTempToString(getTemp());
result += addBoolToString(getSwingV(), kSwingVStr);
result += addBoolToString(getEcono(), kEconoStr);
result += addBoolToString(getTurbo(), kTurboStr);
result += addBoolToString(getLight(), kLightStr);
result += addBoolToString(getHealth(), kHealthStr);
result += addBoolToString(getSleep(), kSleepStr);
return result;
}

View File

@ -0,0 +1,134 @@
// Copyright 2021 David Conran (crankyoldgit)
/// @file
/// @brief Support for Airton protocol
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1670
// Supports:
// Brand: Airton, Model: SMVH09B-2A2A3NH ref. 409730 A/C
// Brand: Airton, Model: RD1A1 remote
#ifndef IR_AIRTON_H_
#define IR_AIRTON_H_
#define __STDC_LIMIT_MACROS
#include <stdint.h>
#ifndef UNIT_TEST
#include <Arduino.h>
#endif
#include "IRremoteESP8266.h"
#include "IRsend.h"
#ifdef UNIT_TEST
#include "IRsend_test.h"
#endif
/// Native representation of a Airton 56 A/C message.
/// @see https://docs.google.com/spreadsheets/d/1Kpq7WCkh85heLnTQGlwUfCR6eeu_vfBHvhii8wtP4LU/edit?usp=sharing
union AirtonProtocol{
uint64_t raw; ///< The state in code form.
struct { // Common
// Byte 1 & 0 (LSB)
uint16_t Header :16; // Header. (0x11D3)
// Byte 2
uint8_t Mode :3; // Operating Mode
uint8_t Power :1; // Power Control
uint8_t Fan :3;
uint8_t Turbo :1;
// Byte 3
uint8_t Temp :4; // Degrees Celsius (+16 offset)
uint8_t :4; // Unknown / Unused.
// Byte 4
uint8_t SwingV :1;
uint8_t :7; // Unknown / Unused.
// Byte 5
uint8_t Econo :1;
uint8_t Sleep :1;
uint8_t NotAutoOn :1;
uint8_t :1; // Unknown / Unused.
uint8_t HeatOn :1;
uint8_t :1; // Unknown / Unused.
uint8_t Health :1;
uint8_t Light :1;
// Byte 6
uint8_t Sum :8; // Sepecial checksum value
};
};
// Constants
const uint8_t kAirtonAuto = 0b000; // 0
const uint8_t kAirtonCool = 0b001; // 1
const uint8_t kAirtonDry = 0b010; // 2
const uint8_t kAirtonFan = 0b011; // 3
const uint8_t kAirtonHeat = 0b100; // 4
const uint8_t kAirtonFanAuto = 0b000; // 0
const uint8_t kAirtonFanMin = 0b001; // 1
const uint8_t kAirtonFanLow = 0b010; // 2
const uint8_t kAirtonFanMed = 0b011; // 3
const uint8_t kAirtonFanHigh = 0b100; // 4
const uint8_t kAirtonFanMax = 0b101; // 5
const uint8_t kAirtonMinTemp = 16; // 16C
const uint8_t kAirtonMaxTemp = 25; // 25C
/// Class for handling detailed Airton 56-bit A/C messages.
class IRAirtonAc {
public:
explicit IRAirtonAc(const uint16_t pin, const bool inverted = false,
const bool use_modulation = true);
void stateReset(void);
#if SEND_AIRTON
void send(const uint16_t repeat = kAirtonDefaultRepeat);
/// Run the calibration to calculate uSec timing offsets for this platform.
/// @return The uSec timing offset needed per modulation of the IR Led.
/// @note This will produce a 65ms IR signal pulse at 38kHz.
/// Only ever needs to be run once per object instantiation, if at all.
int8_t calibrate(void) { return _irsend.calibrate(); }
#endif // SEND_AIRTON
void begin(void);
void on(void);
void off(void);
void setPower(const bool on);
bool getPower(void) const;
void setTemp(const uint8_t degrees);
uint8_t getTemp(void) const;
void setFan(const uint8_t speed);
uint8_t getFan(void) const;
void setMode(const uint8_t mode);
uint8_t getMode(void) const;
uint64_t getRaw(void);
void setRaw(const uint64_t data);
void setLight(const bool on);
bool getLight(void) const;
void setEcono(const bool on);
bool getEcono(void) const;
void setTurbo(const bool on);
bool getTurbo(void) const;
void setHealth(const bool on);
bool getHealth(void) const;
void setSleep(const bool on);
bool getSleep(void) const;
void setSwingV(const bool on);
bool getSwingV(void) const;
static bool validChecksum(const uint64_t data);
static uint8_t calcChecksum(const uint64_t data);
static uint8_t convertMode(const stdAc::opmode_t mode);
static uint8_t convertFan(const stdAc::fanspeed_t speed);
static uint8_t convertSwingV(const stdAc::swingv_t position);
static stdAc::opmode_t toCommonMode(const uint8_t mode);
static stdAc::fanspeed_t toCommonFanSpeed(const uint8_t speed);
stdAc::state_t toCommon(void) const;
String toString(void) const;
#ifndef UNIT_TEST
private:
IRsend _irsend; ///< Instance of the IR send class
#else // UNIT_TEST
/// @cond IGNORE
IRsendTest _irsend; ///< Instance of the testing IR send class
/// @endcond
#endif // UNIT_TEST
AirtonProtocol _;
void checksum(void);
};
#endif // IR_AIRTON_H_

View File

@ -21,17 +21,18 @@
// pulse parameters in usec
const uint16_t kCoolixTick = 276; // Approximately 10.5 cycles at 38kHz
const uint16_t kCoolixBitMarkTicks = 2;
const uint16_t kCoolixBitMark = kCoolixBitMarkTicks * kCoolixTick;
const uint16_t kCoolixBitMark = kCoolixBitMarkTicks * kCoolixTick; // 552us
const uint16_t kCoolixOneSpaceTicks = 6;
const uint16_t kCoolixOneSpace = kCoolixOneSpaceTicks * kCoolixTick;
const uint16_t kCoolixOneSpace = kCoolixOneSpaceTicks * kCoolixTick; // 1656us
const uint16_t kCoolixZeroSpaceTicks = 2;
const uint16_t kCoolixZeroSpace = kCoolixZeroSpaceTicks * kCoolixTick;
const uint16_t kCoolixZeroSpace = kCoolixZeroSpaceTicks * kCoolixTick; // 552us
const uint16_t kCoolixHdrMarkTicks = 17;
const uint16_t kCoolixHdrMark = kCoolixHdrMarkTicks * kCoolixTick;
const uint16_t kCoolixHdrMark = kCoolixHdrMarkTicks * kCoolixTick; // 4692us
const uint16_t kCoolixHdrSpaceTicks = 16;
const uint16_t kCoolixHdrSpace = kCoolixHdrSpaceTicks * kCoolixTick;
const uint16_t kCoolixHdrSpace = kCoolixHdrSpaceTicks * kCoolixTick; // 4416us
const uint16_t kCoolixMinGapTicks = kCoolixHdrMarkTicks + kCoolixZeroSpaceTicks;
const uint16_t kCoolixMinGap = kCoolixMinGapTicks * kCoolixTick;
const uint16_t kCoolixMinGap = kCoolixMinGapTicks * kCoolixTick; // 5244us
const uint8_t kCoolix48ExtraTolerance = 5; // Percent
using irutils::addBoolToString;
using irutils::addIntToString;
@ -40,7 +41,7 @@ using irutils::addModeToString;
using irutils::addTempToString;
#if SEND_COOLIX
/// Send a Coolix message
/// Send a Coolix 24-bit message
/// Status: STABLE / Confirmed Working.
/// @param[in] data The message to be sent.
/// @param[in] nbits The number of bits of message to be sent.
@ -620,7 +621,7 @@ String IRCoolixAC::toString(void) const {
}
#if DECODE_COOLIX
/// Decode the supplied Coolix A/C message.
/// Decode the supplied Coolix 24-bit A/C message.
/// Status: STABLE / Known Working.
/// @param[in,out] results Ptr to the data to decode & where to store the decode
/// result.
@ -699,3 +700,58 @@ bool IRrecv::decodeCOOLIX(decode_results *results, uint16_t offset,
return true;
}
#endif // DECODE_COOLIX
#if SEND_COOLIX48
/// Send a Coolix 48-bit message.
/// Status: ALPHA / Untested.
/// @param[in] data The message to be sent.
/// @param[in] nbits The number of bits of message to be sent.
/// @param[in] repeat The number of times the command is to be repeated.
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1694
/// @note This is effectively the same as `sendCOOLIX()` except requiring the
/// bit flipping be done prior to the call.
void IRsend::sendCoolix48(const uint64_t data, const uint16_t nbits,
const uint16_t repeat) {
// Header + Data + Footer
sendGeneric(kCoolixHdrMark, kCoolixHdrSpace,
kCoolixBitMark, kCoolixOneSpace,
kCoolixBitMark, kCoolixZeroSpace,
kCoolixBitMark, kCoolixMinGap,
data, nbits, 38000, true, repeat, 33);
}
#endif // SEND_COOLIX48
#if DECODE_COOLIX
/// Decode the supplied Coolix 48-bit A/C message.
/// Status: BETA / Probably Working.
/// @param[in,out] results Ptr to the data to decode & where to store the decode
/// result.
/// @param[in] offset The starting index to use when attempting to decode the
/// raw data. Typically/Defaults to kStartOffset.
/// @param[in] nbits The number of data bits to expect.
/// @param[in] strict Flag indicating if we should perform strict matching.
/// @return A boolean. True if it can decode it, false if it can't.
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1694
bool IRrecv::decodeCoolix48(decode_results *results, uint16_t offset,
const uint16_t nbits, const bool strict) {
if (strict && nbits != kCoolix48Bits)
return false; // Not strictly a COOLIX48 message.
// Header + Data + Footer
if (!matchGeneric(results->rawbuf + offset, &(results->value),
results->rawlen - offset, nbits,
kCoolixHdrMark, kCoolixHdrSpace,
kCoolixBitMark, kCoolixOneSpace,
kCoolixBitMark, kCoolixZeroSpace,
kCoolixBitMark, kCoolixMinGap,
true, _tolerance + kCoolix48ExtraTolerance, 0, true))
return false;
// Success
results->decode_type = COOLIX48;
results->bits = nbits;
results->address = 0;
results->command = 0;
return true;
}
#endif // DECODE_COOLIX48

View File

@ -6,6 +6,8 @@
/// @note Kudos:
/// Hamper: For the breakdown and mapping of the bit values.
/// fraschizzato: For additional ZoneFollow & SwingVStep analysis.
/// @note Timers seem to use the `COOLIX48` protocol.
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1694
// Supports:
// Brand: Beko, Model: RG57K7(B)/BGEF Remote
@ -21,6 +23,8 @@
// Brand: Toshiba, Model: RAS-M13YKV-E A/C
// Brand: Toshiba, Model: RAS-4M27YAV-E A/C
// Brand: Toshiba, Model: WH-E1YE remote
// Brand: Bosch, Model: RG36B4/BGE remote
// Brand: Bosch, Model: B1ZAI2441W/B1ZAO2441W A/C
#ifndef IR_COOLIX_H_
#define IR_COOLIX_H_

View File

@ -33,6 +33,10 @@
// Brand: Fujitsu, Model: ASU12RLF A/C (ARREB1E)
// Brand: Fujitsu, Model: AR-REW4E remote (ARREW4E)
// Brand: Fujitsu, Model: ASYG09KETA-B A/C (ARREW4E)
// Brand: Fujitsu, Model: AR-REB4E remote (ARREB1E)
// Brand: Fujitsu, Model: ASTG09K A/C (ARREW4E)
// Brand: Fujitsu, Model: ASTG18K A/C (ARREW4E)
// Brand: Fujitsu, Model: AR-REW1E remote (ARREW4E)
#ifndef IR_FUJITSU_H_
#define IR_FUJITSU_H_

View File

@ -399,6 +399,8 @@ void IRMitsubishiAC::setTemp(const float degrees) {
// Do we have a half degree celsius?
_.HalfDegree = nrHalfDegrees & 1;
_.Temp = static_cast<uint8_t>(nrHalfDegrees / 2 - kMitsubishiAcMinTemp);
// If temp is modified, iSave10C cannot be ON (because temp is then > 10C)
setISave10C(false);
}
/// Get the current temperature setting.
@ -452,8 +454,104 @@ void IRMitsubishiAC::setMode(const uint8_t mode) {
return;
}
_.Mode = mode;
// iSave10C can only be on in Heat mode.
if (mode != kMitsubishiAcHeat) {
setISave10C(false);
}
}
/// Set the iSave10C (i-SAVE) mode of the A/C.
/// @param[in] state true, the setting is on. false, the setting is off.
/// @note Normal minimum temp is 16C; i-SAVE mode works as gate to enable AC
/// to use 10C as setting. However, when Remote control shows 10C, it still
/// emits 16C on the "Temp" bits, and instead it uses other bits to indicate
/// a target temp of 10C.
/// Slightly strange, but I guess it's to keep compatibility to systems
/// without i-SAVE.
/// i-SAVE only has this 10C functionality when the AC is already in Heat mode.
/// In all other modes, minimum temp is 16C.
/// I have found no other difference between normal Heat mode and i-SAVE
/// other than the ability to go to 10C.
/// In this implementation, i-SAVE mode is ONLY used to enable the AC
/// temperature setting to 10C. Therefore "Temp" is set to 16 disregarding
/// what the remote shows, and mode is set to Heat.
void IRMitsubishiAC::setISave10C(const bool state) {
if (state) setMode(kMitsubishiAcHeat);
if (state) setTemp(kMitsubishiAcMinTemp);
_.iSave10C = state;
}
/// Get the iSave10C (i-SAVE) mode of the A/C.
/// @return true, the setting is on. false, the setting is off.
bool IRMitsubishiAC::getISave10C(void) const {
return _.iSave10C;
}
/// Set the requested iSee mode.
/// @param[in] state requested iSee mode.
void IRMitsubishiAC::setISee(const bool state) {
_.ISee = state;
}
/// Get the iSee mode of the A/C.
/// @return The iSee mode setting.
bool IRMitsubishiAC::getISee(void) const {
return _.ISee;
}
/// Set the requested Ecocool mode.
/// @param[in] state requested Ecocool mode.
void IRMitsubishiAC::setEcocool(const bool state) {
_.Ecocool = state;
}
/// Get the Ecocool mode of the A/C.
/// @return The Ecocool mode setting.
bool IRMitsubishiAC::getEcocool(void) const {
return _.Ecocool;
}
/// Set the requested Absense Detect mode.
/// @param[in] state requested Absense Detect mode.
void IRMitsubishiAC::setAbsenseDetect(const bool state) {
_.AbsenseDetect = state;
}
/// Get the Absense Detect mode of the A/C.
/// @return The Absense Detect mode setting.
bool IRMitsubishiAC::getAbsenseDetect(void) const {
return _.AbsenseDetect;
}
/// Set the requested Direct/Indirect mode. Only works if I-See mode is ON.
/// @param[in] mode requested Direct/Indirect mode.
void IRMitsubishiAC::setDirectIndirect(const uint8_t mode) {
if (_.ISee) {
_.DirectIndirect = std::min(mode, kMitsubishiAcDirect); // bounds check
} else {
_.DirectIndirect = 0;
}
}
/// Get the Direct/Indirect mode of the A/C.
/// @return The native mode setting.
uint8_t IRMitsubishiAC::getDirectIndirect(void) const {
return _.DirectIndirect;
}
/// Set the requested Natural Flow mode.
/// @param[in] state requested Natural Flow mode.
void IRMitsubishiAC::setNaturalFlow(const bool state) {
_.NaturalFlow = state;
}
/// Get the Natural Flow mode of the A/C.
/// @return The Natural Flow mode setting.
bool IRMitsubishiAC::getNaturalFlow(void) const {
return _.NaturalFlow;
}
/// Set the requested vane (Vertical Swing) operation mode of the a/c unit.
/// @note On some models, this represents the Right vertical vane.
/// @param[in] position The position/mode to set the vane to.
@ -463,12 +561,6 @@ void IRMitsubishiAC::setVane(const uint8_t position) {
_.Vane = pos;
}
/// Set the requested wide-vane (Horizontal Swing) operation mode of the a/c.
/// @param[in] position The position/mode to set the wide vane to.
void IRMitsubishiAC::setWideVane(const uint8_t position) {
_.WideVane = std::min(position, kMitsubishiAcWideVaneAuto);
}
/// Get the Vane (Vertical Swing) mode of the A/C.
/// @note On some models, this represents the Right vertical vane.
/// @return The native position/mode setting.
@ -476,12 +568,6 @@ uint8_t IRMitsubishiAC::getVane(void) const {
return _.Vane;
}
/// Get the Wide Vane (Horizontal Swing) mode of the A/C.
/// @return The native position/mode setting.
uint8_t IRMitsubishiAC::getWideVane(void) const {
return _.WideVane;
}
/// Set the requested Left Vane (Vertical Swing) operation mode of the a/c unit.
/// @param[in] position The position/mode to set the vane to.
void IRMitsubishiAC::setVaneLeft(const uint8_t position) {
@ -492,6 +578,18 @@ void IRMitsubishiAC::setVaneLeft(const uint8_t position) {
/// @return The native position/mode setting.
uint8_t IRMitsubishiAC::getVaneLeft(void) const { return _.VaneLeft; }
/// Set the requested wide-vane (Horizontal Swing) operation mode of the a/c.
/// @param[in] position The position/mode to set the wide vane to.
void IRMitsubishiAC::setWideVane(const uint8_t position) {
_.WideVane = std::min(position, kMitsubishiAcWideVaneAuto);
}
/// Get the Wide Vane (Horizontal Swing) mode of the A/C.
/// @return The native position/mode setting.
uint8_t IRMitsubishiAC::getWideVane(void) const {
return _.WideVane;
}
/// Get the clock time of the A/C unit.
/// @return Nr. of 10 minute increments past midnight.
/// @note 1 = 1/6 hour (10 minutes). e.g. 4pm = 48.
@ -777,6 +875,12 @@ String IRMitsubishiAC::toString(void) const {
result += ')';
}
result += addBoolToString(_.WeeklyTimer, kWeeklyTimerStr);
result += addBoolToString(_.iSave10C, k10CHeatStr);
result += addBoolToString(_.ISee, kISeeStr);
result += addBoolToString(_.Ecocool, kEconoStr);
result += addBoolToString(_.AbsenseDetect, kAbsenseDetectStr);
result += addIntToString(_.DirectIndirect, kDirectIndirectModeStr);
result += addBoolToString(_.NaturalFlow, kFreshStr);
return result;
}

View File

@ -34,6 +34,7 @@
// Brand: Mitsubishi Electric, Model: MSZ-SF25VE3 A/C (MITSUBISHI_AC)
// Brand: Mitsubishi Electric, Model: SG15D remote (MITSUBISHI_AC)
// Brand: Mitsubishi Electric, Model: MSZ-ZW4017S A/C (MITSUBISHI_AC)
// Brand: Mitsubishi Electric, Model: MSZ-FHnnVE A/C (MITSUBISHI_AC)
#ifndef IR_MITSUBISHI_H_
#define IR_MITSUBISHI_H_
@ -62,7 +63,8 @@ union Mitsubishi144Protocol{
// Byte 6
uint8_t :3;
uint8_t Mode :3;
uint8_t :2;
uint8_t ISee : 1;
uint8_t :1;
// Byte 7
uint8_t Temp :4;
uint8_t HalfDegree :1;
@ -72,7 +74,7 @@ union Mitsubishi144Protocol{
uint8_t WideVane:4; // SwingH
// Byte 9
uint8_t Fan :3;
uint8_t Vane :3; // SwingV
uint8_t Vane :3; // SwingV or VaneRight
uint8_t VaneBit :1;
uint8_t FanAuto :1;
// Byte 10
@ -86,13 +88,21 @@ union Mitsubishi144Protocol{
uint8_t WeeklyTimer :1;
uint8_t :4;
// Byte 14
uint8_t :8;
uint8_t :5;
uint8_t Ecocool :1;
uint8_t :2;
// Byte 15
uint8_t :8;
uint8_t DirectIndirect:2;
uint8_t AbsenseDetect :1;
uint8_t :2;
uint8_t iSave10C :1; // i-SAVE:mode=Heat & iSave=on AND 10C on remote
uint8_t :2;
// Byte 16
uint8_t :3;
uint8_t VaneLeft :3; // SwingV(Left)
uint8_t :2;
uint8_t :1;
uint8_t NaturalFlow :1;
uint8_t :1;
uint8_t VaneLeft :3; // SwingV(Left)
uint8_t :2;
// Byte 17
uint8_t Sum :8;
};
@ -126,6 +136,9 @@ const uint8_t kMitsubishiAcWideVaneRight = 0b0100; // 4
const uint8_t kMitsubishiAcWideVaneRightMax = 0b0101; // 5
const uint8_t kMitsubishiAcWideVaneWide = 0b0110; // 6
const uint8_t kMitsubishiAcWideVaneAuto = 0b1000; // 8
const uint8_t kMitsubishiAcDirectOff = 0b00; // Vanes move when AC wants to.
const uint8_t kMitsubishiAcIndirect = 0b01;
const uint8_t kMitsubishiAcDirect = 0b11;
const uint8_t kMitsubishiAcNoTimer = 0;
const uint8_t kMitsubishiAcStartTimer = 5;
const uint8_t kMitsubishiAcStopTimer = 3;
@ -274,12 +287,24 @@ class IRMitsubishiAC {
uint8_t getFan(void) const;
void setMode(const uint8_t mode);
uint8_t getMode(void) const;
void setVane(const uint8_t position);
void setWideVane(const uint8_t position);
void setISave10C(const bool state);
bool getISave10C(void) const;
void setISee(const bool state);
bool getISee(void) const;
void setDirectIndirect(const uint8_t position);
uint8_t getDirectIndirect(void) const;
void setEcocool(const bool state);
bool getEcocool(void) const;
void setAbsenseDetect(const bool state);
bool getAbsenseDetect(void) const;
void setNaturalFlow(const bool state);
bool getNaturalFlow(void) const;
void setVane(const uint8_t position); // controls RIGHT vane on some models
uint8_t getVane(void) const;
uint8_t getWideVane(void) const;
void setVaneLeft(const uint8_t position);
uint8_t getVaneLeft(void) const;
void setWideVane(const uint8_t position);
uint8_t getWideVane(void) const;
uint8_t* getRaw(void);
void setRaw(const uint8_t* data);
uint8_t getClock(void) const;

View File

@ -120,7 +120,7 @@ uint32_t IRsend::encodeSony(const uint16_t nbits, const uint16_t command,
/// bits long.
bool IRrecv::decodeSony(decode_results *results, uint16_t offset,
const uint16_t nbits, const bool strict) {
if (results->rawlen <= 2 * nbits + kHeader - 1 + offset)
if (results->rawlen < 2 * nbits + kHeader - 1 + offset)
return false; // Message is smaller than we expected.
// Compliance

View File

@ -8,6 +8,8 @@
// Brand: TCL, Model: TAC-09CHSD/XA31I A/C (TAC09CHSD)
// Brand: Teknopoint, Model: Allegro SSA-09H A/C (GZ055BE1)
// Brand: Teknopoint, Model: GZ-055B-E1 remote (GZ055BE1)
// Brand: Daewoo, Model: DSB-F0934ELH-V A/C
// Brand: Daewoo, Model: GYKQ-52E remote
#ifndef IR_TCL_H_
#define IR_TCL_H_

View File

@ -216,9 +216,7 @@ void IRToshibaAC::setTemp(const uint8_t degrees) {
/// Get the current temperature setting.
/// @return The current setting for temp. in degrees celsius.
uint8_t IRToshibaAC::getTemp(void) const {
return _.Temp + kToshibaAcMinTemp;
}
uint8_t IRToshibaAC::getTemp(void) const { return _.Temp + kToshibaAcMinTemp; }
/// Set the speed of the fan.
/// @param[in] speed The desired setting (0 is Auto, 1-5 is the speed, 5 is Max)
@ -339,6 +337,19 @@ void IRToshibaAC::setEcono(const bool on) {
}
}
/// Get the filter (Pure/Ion Filter) setting of the A/C.
/// @return true, if the current setting is on. Otherwise, false.
bool IRToshibaAC::getFilter(void) const {
return (getStateLength() >= kToshibaACStateLength) ? _.Filter : false;
}
/// Set the filter (Pure/Ion Filter) setting of the A/C.
/// @param[in] on true, the setting is on. false, the setting is off.
void IRToshibaAC::setFilter(const bool on) {
_.Filter = on;
if (on) setStateLength(std::min(kToshibaACStateLength, getStateLength()));
}
/// Convert a stdAc::opmode_t enum into its native mode.
/// @param[in] mode The enum to be converted.
/// @return The native equivalent of the enum.
@ -421,6 +432,7 @@ stdAc::state_t IRToshibaAC::toCommon(const stdAc::state_t *prev) const {
result.fanspeed = toCommonFanSpeed(getFan());
result.turbo = getTurbo();
result.econo = getEcono();
result.filter = getFilter();
}
switch (getSwing()) {
case kToshibaAcSwingOn:
@ -436,7 +448,6 @@ stdAc::state_t IRToshibaAC::toCommon(const stdAc::state_t *prev) const {
}
// Not supported.
result.light = false;
result.filter = false;
result.swingh = stdAc::swingh_t::kOff;
result.quiet = false;
result.clean = false;
@ -450,7 +461,7 @@ stdAc::state_t IRToshibaAC::toCommon(const stdAc::state_t *prev) const {
/// @return A human readable string.
String IRToshibaAC::toString(void) const {
String result = "";
result.reserve(80);
result.reserve(95);
result += addTempToString(getTemp(), true, false);
switch (getStateLength()) {
case kToshibaACStateLengthShort:
@ -477,6 +488,7 @@ String IRToshibaAC::toString(void) const {
kToshibaAcFanMed);
result += addBoolToString(getTurbo(), kTurboStr);
result += addBoolToString(getEcono(), kEconoStr);
result += addBoolToString(getFilter(), kFilterStr);
}
return result;
}

View File

@ -8,6 +8,7 @@
/// @see https://docs.google.com/spreadsheets/d/1yidE2fvaO9kpCHfKafIdH31q4uaskYR1OwwrkyOxbp0/edit?usp=drivesdk
/// @see https://www.toshiba-carrier.co.jp/global/about/index.htm
/// @see http://www.toshiba-carrier.co.th/AboutUs/Pages/CompanyProfile.aspx
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1692
// Supports:
// Brand: Toshiba, Model: RAS-B13N3KV2
@ -18,6 +19,8 @@
// Brand: Toshiba, Model: WC-L03SE
// Brand: Toshiba, Model: WH-UB03NJ remote
// Brand: Toshiba, Model: RAS-2558V A/C
// Brand: Toshiba, Model: WH-TA01JE remote
// Brand: Toshiba, Model: RAS-25SKVP2-ND A/C
// Brand: Carrier, Model: 42NQV060M2 / 38NYV060M2 A/C
// Brand: Carrier, Model: 42NQV050M2 / 38NYV050M2 A/C
// Brand: Carrier, Model: 42NQV035M2 / 38NYV035M2 A/C
@ -50,28 +53,31 @@ union ToshibaProtocol{
///< 1 (56 bit message)
///< 3 (72 bit message)
///< 4 (80 bit message)
uint8_t Length :8;
uint8_t Length :8;
// Byte[3] - The bit-inverted value of the "length" byte.
uint8_t :8;
uint8_t :8;
// Byte[4]
uint8_t :3;
uint8_t LongMsg :1;
uint8_t :1;
uint8_t ShortMsg:1;
uint8_t :2;
uint8_t :3;
uint8_t LongMsg :1;
uint8_t :1;
uint8_t ShortMsg :1;
uint8_t :2;
// Byte[5]
uint8_t Swing :3;
uint8_t :1;
uint8_t Temp :4;
uint8_t Swing :3;
uint8_t :1;
uint8_t Temp :4;
// Byte[6]
uint8_t Mode :3;
uint8_t :2;
uint8_t Fan :3;
uint8_t Mode :3;
uint8_t :2;
uint8_t Fan :3;
// Byte[7]
uint8_t :8;
uint8_t :4;
uint8_t Filter :1;
uint8_t :3;
// Byte[8]
// (Checksum for 72 bit messages, Eco/Turbo for long 80 bit messages)
uint8_t EcoTurbo :8;
uint8_t EcoTurbo :8;
};
};
@ -144,6 +150,8 @@ class IRToshibaAC {
bool getTurbo(void) const;
void setEcono(const bool on);
bool getEcono(void) const;
void setFilter(const bool on);
bool getFilter(void) const;
void setMode(const uint8_t mode);
uint8_t getMode(const bool raw = false) const;
void setRaw(const uint8_t newState[],

View File

@ -120,6 +120,9 @@
#ifndef D_STR_IFEEL
#define D_STR_IFEEL "IFeel"
#endif // D_STR_IFEEL
#ifndef D_STR_ISEE
#define D_STR_ISEE "ISee"
#endif // D_STR_ISEE
#ifndef D_STR_HUMID
#define D_STR_HUMID "Humid"
#endif // D_STR_HUMID
@ -207,6 +210,19 @@
#ifndef D_STR_SENSOR
#define D_STR_SENSOR "Sensor"
#endif // D_STR_SENSOR
#ifndef D_STR_ABSENSEDETECT
#define D_STR_ABSENSEDETECT "Absense detect"
#endif // D_STR_ABSENSEDETECT
#ifndef D_STR_DIRECT
#define D_STR_DIRECT "Direct"
#endif // D_STR_DIRECT
#ifndef D_STR_INDIRECT
#define D_STR_INDIRECT "Indirect"
#endif // D_STR_INDIRECT
#ifndef D_STR_DIRECTINDIRECTMODE
#define D_STR_DIRECTINDIRECTMODE D_STR_DIRECT " / " \
D_STR_INDIRECT " " D_STR_MODE
#endif // D_STR_DIRECTINDIRECTMODE
#ifndef D_STR_DISPLAY
#define D_STR_DISPLAY "Display"
#endif // D_STR_DISPLAY
@ -702,6 +718,9 @@
#ifndef D_STR_COOLIX
#define D_STR_COOLIX "COOLIX"
#endif // D_STR_COOLIX
#ifndef D_STR_COOLIX48
#define D_STR_COOLIX48 D_STR_COOLIX "48"
#endif // D_STR_COOLIX48
#ifndef D_STR_CORONA_AC
#define D_STR_CORONA_AC "CORONA_AC"
#endif // D_STR_CORONA_AC

View File

@ -1,6 +1,7 @@
// Copyright 2019-2021 David Conran
#include <string>
#include "ir_Airton.h"
#include "ir_Airwell.h"
#include "ir_Amcor.h"
#include "ir_Argo.h"
@ -44,6 +45,36 @@
// Tests for IRac class.
TEST(TestIRac, Airton) {
IRAirtonAc ac(kGpioUnused);
IRac irac(kGpioUnused);
IRrecv capture(kGpioUnused);
const char expected[] =
"Power: On, Mode: 1 (Cool), Fan: 5 (Maximum), Temp: 18C, "
"Swing(V): On, Econo: On, Turbo: On, Light: On, Health: On, Sleep: On";
ac.begin();
irac.airton(&ac,
true, // Power
stdAc::opmode_t::kCool, // Mode
18, // Celsius
stdAc::fanspeed_t::kMax, // Fan speed
stdAc::swingv_t::kAuto, // Vertical Swing
true, // Turbo
true, // Light/Display/LED
true, // Econo (Eco)
true, // Filter (Health)
9 * 60 + 12); // Sleep (09:12)
ASSERT_EQ(expected, ac.toString());
ac._irsend.makeDecodeResult();
EXPECT_TRUE(capture.decode(&ac._irsend.capture));
ASSERT_EQ(AIRTON, ac._irsend.capture.decode_type);
ASSERT_EQ(kAirtonBits, ac._irsend.capture.bits);
ASSERT_EQ(expected, IRAcUtils::resultAcToString(&ac._irsend.capture));
stdAc::state_t r, p;
ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &r, &p));
}
TEST(TestIRac, Airwell) {
IRAirwellAc ac(kGpioUnused);
IRac irac(kGpioUnused);
@ -1418,7 +1449,9 @@ TEST(TestIRac, Mitsubishi) {
"Power: On, Mode: 3 (Cool), Temp: 20C, Fan: 2 (Medium), "
"Swing(V): 0 (Auto), Swing(H): 3 (Middle), "
"Clock: 14:30, On Timer: 00:00, Off Timer: 00:00, Timer: -, "
"Weekly Timer: Off";
"Weekly Timer: Off"
", 10C Heat: Off, ISee: Off, Econo: Off, Absense detect: Off, "
"Direct / Indirect Mode: 0, Fresh: Off";
ac.begin();
irac.mitsubishi(&ac,
@ -1958,7 +1991,7 @@ TEST(TestIRac, Toshiba) {
IRrecv capture(kGpioUnused);
char expected[] =
"Temp: 29C, Power: On, Mode: 2 (Dry), Fan: 2 (UNKNOWN), "
"Turbo: Off, Econo: On";
"Turbo: Off, Econo: On, Filter: Off";
ac.begin();
irac.toshiba(&ac,
@ -1968,7 +2001,8 @@ TEST(TestIRac, Toshiba) {
stdAc::fanspeed_t::kLow, // Fan speed
stdAc::swingv_t::kOff, // Vertical Swing
false, // Turbo
true); // Econo
true, // Econo
false); // Filter
ASSERT_EQ(expected, ac.toString());
ASSERT_EQ(kToshibaACStateLengthLong, ac.getStateLength());
ac._irsend.makeDecodeResult();
@ -2873,7 +2907,7 @@ TEST(TestIRac, Issue1250) {
// Now send the state so we can actually decode/capture what we sent.
char expected_on[] =
"Temp: 19C, Power: On, Mode: 4 (Fan), Fan: 0 (Auto), "
"Turbo: Off, Econo: Off";
"Turbo: Off, Econo: Off, Filter: Off";
ac._irsend.reset();
irac.toshiba(&ac,
irac.next.power, // Power
@ -2882,7 +2916,8 @@ TEST(TestIRac, Issue1250) {
irac.next.fanspeed, // Fan speed
irac.next.swingv, // Vertical Swing
irac.next.turbo, // Turbo
irac.next.econo); // Econo
irac.next.econo, // Econo
irac.next.filter); // Filter
ASSERT_EQ(expected_on, ac.toString());
ASSERT_EQ(kToshibaACStateLength, ac.getStateLength());
ac._irsend.makeDecodeResult();
@ -2898,7 +2933,8 @@ TEST(TestIRac, Issue1250) {
irac.sendAc();
// Now send the state so we can actually decode/capture what we sent.
char expected_off[] =
"Temp: 19C, Power: Off, Fan: 0 (Auto), Turbo: Off, Econo: Off";
"Temp: 19C, Power: Off, Fan: 0 (Auto), Turbo: Off, Econo: Off, "
"Filter: Off";
ac._irsend.reset();
irac.toshiba(&ac,
irac.next.power, // Power
@ -2907,7 +2943,8 @@ TEST(TestIRac, Issue1250) {
irac.next.fanspeed, // Fan speed
irac.next.swingv, // Vertical Swing
irac.next.turbo, // Turbo
irac.next.econo); // Econo
irac.next.econo, // Econo
irac.next.filter); // Filter
ASSERT_EQ(expected_off, ac.toString());
ASSERT_EQ(kToshibaACStateLength, ac.getStateLength());
ac._irsend.makeDecodeResult();

View File

@ -1,5 +1,6 @@
// Copyright 2021 crankyoldgit
#include "ir_Airton.h"
#include "IRac.h"
#include "IRrecv.h"
#include "IRrecv_test.h"
@ -34,6 +35,13 @@ TEST(TestDecodeAirton, RealExample) {
EXPECT_EQ(0x5E1400090C11D3, irsend.capture.value);
EXPECT_EQ(0x0, irsend.capture.address);
EXPECT_EQ(0x0, irsend.capture.command);
EXPECT_EQ(
"Power: On, Mode: 4 (Heat), Fan: 0 (Auto), Temp: 25C, "
"Swing(V): Off, Econo: Off, Turbo: Off, Light: Off, "
"Health: Off, Sleep: Off",
IRAcUtils::resultAcToString(&irsend.capture));
stdAc::state_t r, p;
ASSERT_TRUE(IRAcUtils::decodeToState(&irsend.capture, &r, &p));
}
TEST(TestDecodeAirton, SyntheticExample) {
@ -56,7 +64,265 @@ TEST(TestUtils, Housekeeping) {
ASSERT_EQ("AIRTON", typeToString(decode_type_t::AIRTON));
ASSERT_EQ(decode_type_t::AIRTON, strToDecodeType("AIRTON"));
ASSERT_FALSE(hasACState(decode_type_t::AIRTON));
ASSERT_FALSE(IRac::isProtocolSupported(decode_type_t::AIRTON));
ASSERT_TRUE(IRac::isProtocolSupported(decode_type_t::AIRTON));
ASSERT_EQ(kAirtonBits, IRsend::defaultBits(decode_type_t::AIRTON));
ASSERT_EQ(kAirtonDefaultRepeat, IRsend::minRepeats(decode_type_t::AIRTON));
}
// Tests for IRAirtonAc class.
TEST(TestIRAirtonAcClass, Power) {
IRAirtonAc ac(kGpioUnused);
ac.begin();
ac.on();
EXPECT_TRUE(ac.getPower());
ac.off();
EXPECT_FALSE(ac.getPower());
ac.setPower(true);
EXPECT_TRUE(ac.getPower());
ac.setPower(false);
EXPECT_FALSE(ac.getPower());
}
TEST(TestIRAirtonAcClass, Checksums) {
ASSERT_TRUE(IRAirtonAc::validChecksum(0x5E1400090C11D3));
ASSERT_EQ(0x5E, IRAirtonAc::calcChecksum(0x5E1400090C11D3));
ASSERT_FALSE(IRAirtonAc::validChecksum(0x551400090C11D3));
ASSERT_TRUE(IRAirtonAc::validChecksum(0x2F8801060911D3));
ASSERT_TRUE(IRAirtonAc::validChecksum(0xDB8800021A11D3));
}
TEST(TestIRAirtonAcClass, Temperature) {
IRAirtonAc ac(kGpioUnused);
ac.begin();
ac.setMode(kAirtonCool); // Cool mode allows the entire temp range.
ac.setTemp(0);
EXPECT_EQ(kAirtonMinTemp, ac.getTemp());
ac.setTemp(255);
EXPECT_EQ(kAirtonMaxTemp, ac.getTemp());
ac.setTemp(kAirtonMinTemp);
EXPECT_EQ(kAirtonMinTemp, ac.getTemp());
ac.setTemp(kAirtonMaxTemp);
EXPECT_EQ(kAirtonMaxTemp, ac.getTemp());
ac.setTemp(kAirtonMinTemp - 1);
EXPECT_EQ(kAirtonMinTemp, ac.getTemp());
ac.setTemp(kAirtonMaxTemp + 1);
EXPECT_EQ(kAirtonMaxTemp, ac.getTemp());
ac.setTemp(17);
EXPECT_EQ(17, ac.getTemp());
ac.setTemp(21);
EXPECT_EQ(21, ac.getTemp());
ac.setTemp(20);
EXPECT_EQ(20, ac.getTemp());
}
TEST(TestIRAirtonAcClass, OperatingMode) {
IRAirtonAc ac(kGpioUnused);
ac.begin();
ac.setMode(kAirtonCool);
EXPECT_EQ(kAirtonCool, ac.getMode());
ac.setMode(kAirtonDry);
EXPECT_EQ(kAirtonDry, ac.getMode());
ac.setMode(kAirtonFan);
EXPECT_EQ(kAirtonFan, ac.getMode());
EXPECT_NE(kAirtonMaxTemp, ac.getTemp());
ac.setMode(kAirtonAuto);
EXPECT_EQ(kAirtonAuto, ac.getMode());
EXPECT_EQ(kAirtonMaxTemp, ac.getTemp());
ac.setMode(kAirtonHeat);
EXPECT_EQ(kAirtonHeat, ac.getMode());
ac.setMode(kAirtonHeat + 1);
EXPECT_EQ(kAirtonAuto, ac.getMode());
ac.setMode(255);
EXPECT_EQ(kAirtonAuto, ac.getMode());
}
TEST(TestIRAirtonAcClass, FanSpeed) {
IRAirtonAc ac(kGpioUnused);
ac.begin();
ac.setMode(kAirtonCool); // All fan speeds available in this mode.
ac.setFan(0);
EXPECT_EQ(kAirtonFanAuto, ac.getFan());
ac.setFan(255);
EXPECT_EQ(kAirtonFanAuto, ac.getFan());
ac.setFan(kAirtonFanHigh);
EXPECT_EQ(kAirtonFanHigh, ac.getFan());
ac.setFan(kAirtonFanLow);
EXPECT_EQ(kAirtonFanLow, ac.getFan());
ac.setFan(kAirtonFanMax);
EXPECT_EQ(kAirtonFanMax, ac.getFan());
ac.setFan(kAirtonFanMin);
EXPECT_EQ(kAirtonFanMin, ac.getFan());
ac.setFan(kAirtonFanMed);
EXPECT_EQ(kAirtonFanMed, ac.getFan());
ac.setFan(kAirtonFanMax + 1);
EXPECT_EQ(kAirtonFanAuto, ac.getFan());
}
TEST(TestIRAirtonAcClass, SwingV) {
IRAirtonAc ac(kGpioUnused);
ac.begin();
ac.setSwingV(false);
EXPECT_FALSE(ac.getSwingV());
ac.setSwingV(true);
EXPECT_TRUE(ac.getSwingV());
ac.setSwingV(false);
EXPECT_FALSE(ac.getSwingV());
// Known swingv on state
ac.setRaw(0xBC0401050111D3);
EXPECT_TRUE(ac.getSwingV());
}
TEST(TestIRAirtonAcClass, Light) {
IRAirtonAc ac(kGpioUnused);
ac.begin();
ac.setLight(false);
EXPECT_FALSE(ac.getLight());
ac.setLight(true);
EXPECT_TRUE(ac.getLight());
ac.setLight(false);
EXPECT_FALSE(ac.getLight());
// Known light on state
ac.setRaw(0x298801040911D3);
EXPECT_TRUE(ac.getLight());
}
TEST(TestIRAirtonAcClass, ConstructKnownExamples) {
IRAirtonAc ac(kGpioUnused);
ac.begin();
ac.stateReset();
ac.on();
ac.setMode(kAirtonHeat);
ac.setFan(kAirtonFanAuto);
ac.setTemp(25);
ac.setSwingV(false);
ac.setLight(false);
ac.setTurbo(false);
ac.setSleep(false);
ac.setEcono(false);
ac.setHealth(false);
EXPECT_EQ(
"Power: On, Mode: 4 (Heat), Fan: 0 (Auto), Temp: 25C, "
"Swing(V): Off, Econo: Off, Turbo: Off, Light: Off, "
"Health: Off, Sleep: Off",
ac.toString());
EXPECT_EQ(0x5E1400090C11D3, ac.getRaw());
}
TEST(TestIRAirtonAcClass, Turbo) {
IRAirtonAc ac(kGpioUnused);
ac.begin();
ac.setTurbo(false);
EXPECT_FALSE(ac.getTurbo());
EXPECT_NE(kAirtonFanMax, ac.getFan());
ac.setTurbo(true);
EXPECT_TRUE(ac.getTurbo());
EXPECT_EQ(kAirtonFanMax, ac.getFan());
ac.setTurbo(false);
EXPECT_FALSE(ac.getTurbo());
// Known Turbo on state
ac.setRaw(0x92040000D911D3);
EXPECT_TRUE(ac.getTurbo());
}
TEST(TestIRAirtonAcClass, Sleep) {
IRAirtonAc ac(kGpioUnused);
ac.begin();
ac.setMode(kAirtonCool); // Sleep is available in Cool mode.
ac.setSleep(false);
EXPECT_FALSE(ac.getSleep());
ac.setSleep(true);
EXPECT_TRUE(ac.getSleep());
ac.setSleep(false);
EXPECT_FALSE(ac.getSleep());
ac.setSleep(true);
// Sleep is available in Heat mode, but changing modes resets it.
ac.setMode(kAirtonHeat);
EXPECT_FALSE(ac.getSleep());
ac.setSleep(true);
EXPECT_TRUE(ac.getSleep());
ac.setMode(kAirtonAuto); // Sleep is NOT available in Auto mode.
EXPECT_FALSE(ac.getSleep());
ac.setSleep(true);
EXPECT_FALSE(ac.getSleep());
ac.setMode(kAirtonFan); // Sleep is NOT available in Fan mode.
EXPECT_FALSE(ac.getSleep());
ac.setSleep(true);
EXPECT_FALSE(ac.getSleep());
// Known Sleep on state
ac.setRaw(0xA00600000911D3);
EXPECT_TRUE(ac.getSleep());
EXPECT_NE(kAirtonAuto, ac.getMode());
EXPECT_NE(kAirtonFan, ac.getMode());
}
TEST(TestIRAirtonAcClass, Health) {
IRAirtonAc ac(kGpioUnused);
ac.begin();
ac.setHealth(false);
EXPECT_FALSE(ac.getHealth());
ac.setHealth(true);
EXPECT_TRUE(ac.getHealth());
ac.setHealth(false);
EXPECT_FALSE(ac.getHealth());
// Known Health on state
ac.setRaw(0xE5C900000911D3);
EXPECT_TRUE(ac.getHealth());
}
TEST(TestIRAirtonAcClass, Econo) {
IRAirtonAc ac(kGpioUnused);
ac.begin();
ac.setMode(kAirtonCool); // Econo is only available in Cool.
ac.setEcono(false);
EXPECT_FALSE(ac.getEcono());
ac.setEcono(true);
EXPECT_TRUE(ac.getEcono());
ac.setEcono(false);
EXPECT_FALSE(ac.getEcono());
ac.setEcono(true);
ac.setMode(kAirtonHeat); // Econo is only available in Cool, not Heat!
EXPECT_FALSE(ac.getEcono());
ac.setEcono(true);
EXPECT_FALSE(ac.getEcono());
// Known Econo on state
ac.setRaw(0xE5C900000911D3);
EXPECT_TRUE(ac.getEcono());
}

View File

@ -6,6 +6,25 @@
#include "IRsend_test.h"
#include "gtest/gtest.h"
TEST(TestUtils, Housekeeping) {
// COOLIX
ASSERT_EQ("COOLIX", typeToString(decode_type_t::COOLIX));
ASSERT_EQ(decode_type_t::COOLIX, strToDecodeType("COOLIX"));
ASSERT_FALSE(hasACState(decode_type_t::COOLIX));
ASSERT_TRUE(IRac::isProtocolSupported(decode_type_t::COOLIX));
ASSERT_EQ(kCoolixBits, IRsend::defaultBits(decode_type_t::COOLIX));
ASSERT_EQ(kSingleRepeat, IRsend::minRepeats(decode_type_t::COOLIX));
// COOLIX48
ASSERT_EQ("COOLIX48", typeToString(decode_type_t::COOLIX48));
ASSERT_EQ(decode_type_t::COOLIX48, strToDecodeType("COOLIX48"));
ASSERT_FALSE(hasACState(decode_type_t::COOLIX48));
ASSERT_FALSE(IRac::isProtocolSupported(decode_type_t::COOLIX48));
ASSERT_EQ(kCoolix48Bits, IRsend::defaultBits(decode_type_t::COOLIX48));
ASSERT_EQ(kSingleRepeat, IRsend::minRepeats(decode_type_t::COOLIX48));
}
// Tests for sendCOOLIX().
// Test sending typical data only.
@ -941,3 +960,76 @@ TEST(TestCoolixACClass, VerifyZoneFollowFan) {
"Zone Follow: On, Sensor Temp: 19C",
ac.toString());
}
TEST(TestDecodeCoolix48, RealExample) {
IRsendTest irsend(kGpioUnused);
IRrecv irrecv(kGpioUnused);
// Ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/1694#issue-1068786691
// Off Timer: 1 hour
const uint16_t rawData[199] = {
4342, 4454, 486, 1724, 436, 658, 438, 1748, 464, 1718, 462, 634, 440, 656,
462, 1696, 488, 634, 462, 634, 436, 1722, 516, 608, 462, 660, 436, 1694,
488, 1720, 440, 630, 488, 1700, 488, 1704, 458, 660, 462, 1698, 490, 632,
462, 634, 436, 684, 436, 1700, 464, 1748, 462, 634, 462, 1720, 436, 658,
462, 1700, 488, 1692, 512, 1696, 438, 684, 410, 686, 434, 688, 408, 1696,
488, 1694, 464, 682, 414, 1748, 436, 1722, 488, 632, 438, 686, 408, 662,
462, 1696, 488, 1722, 462, 1696, 462, 1746, 436, 1798, 386, 1694, 490,
1720, 516, 5234, 4370, 4446, 490, 1690, 492, 658, 434, 1726, 436, 1746,
464, 604, 488, 658, 412, 1718, 490, 636, 460, 660, 438, 1698, 460, 662,
458, 632, 436, 1718, 490, 1720, 488, 608, 436, 1754, 462, 1726, 438, 682,
414, 1748, 464, 632, 460, 660, 410, 658, 438, 1748, 464, 1694, 464, 660,
436, 1720, 488, 634, 460, 1726, 462, 1724, 462, 1692, 490, 606, 462, 714,
384, 660, 460, 1722, 460, 1722, 490, 606, 464, 1718, 490, 1670, 486, 634,
462, 662, 410, 660, 460, 1722, 464, 1718, 460, 1696, 464, 1720, 462, 1720,
462, 1722, 486, 1700, 462}; // UNKNOWN 1F691B97
irsend.begin();
irsend.reset();
irsend.sendRaw(rawData, 199, 38000);
irsend.makeDecodeResult();
ASSERT_TRUE(irrecv.decode(&irsend.capture));
EXPECT_EQ(COOLIX48, irsend.capture.decode_type);
EXPECT_EQ(kCoolix48Bits, irsend.capture.bits);
EXPECT_EQ(0xB24DA35C6C7F, irsend.capture.value);
EXPECT_EQ(0x0, irsend.capture.address);
EXPECT_EQ(0x0, irsend.capture.command);
}
TEST(TestDecodeCoolix48, SyntheticSelfDecode) {
IRsendTest irsend(kGpioUnused);
IRrecv irrecv(kGpioUnused);
irsend.begin();
irsend.reset();
irsend.sendCoolix48(0xB24DA35C6C7F);
irsend.makeDecodeResult();
ASSERT_TRUE(irrecv.decode(&irsend.capture));
EXPECT_EQ(COOLIX48, irsend.capture.decode_type);
EXPECT_EQ(kCoolix48Bits, irsend.capture.bits);
EXPECT_EQ(0xB24DA35C6C7F, irsend.capture.value);
EXPECT_EQ(0x0, irsend.capture.address);
EXPECT_EQ(0x0, irsend.capture.command);
EXPECT_EQ(
"f38000d33"
"m4692s4416" // Message.
"m552s1656m552s552m552s1656m552s1656m552s552m552s552m552s1656m552s552"
"m552s552m552s1656m552s552m552s552m552s1656m552s1656m552s552m552s1656"
"m552s1656m552s552m552s1656m552s552m552s552m552s552m552s1656m552s1656"
"m552s552m552s1656m552s552m552s1656m552s1656m552s1656m552s552m552s552"
"m552s552m552s1656m552s1656m552s552m552s1656m552s1656m552s552m552s552"
"m552s552m552s1656m552s1656m552s1656m552s1656m552s1656m552s1656m552s1656"
"m552s5244"
"m4692s4416" // Repeat
"m552s1656m552s552m552s1656m552s1656m552s552m552s552m552s1656m552s552"
"m552s552m552s1656m552s552m552s552m552s1656m552s1656m552s552m552s1656"
"m552s1656m552s552m552s1656m552s552m552s552m552s552m552s1656m552s1656"
"m552s552m552s1656m552s552m552s1656m552s1656m552s1656m552s552m552s552"
"m552s552m552s1656m552s1656m552s552m552s1656m552s1656m552s552m552s552"
"m552s552m552s1656m552s1656m552s1656m552s1656m552s1656m552s1656m552s1656"
"m552s5244",
irsend.outputStr());
}

View File

@ -913,7 +913,9 @@ TEST(TestMitsubishiACClass, HumanReadable) {
"Power: On, Mode: 1 (Heat), Temp: 22C, Fan: 6 (Quiet), "
"Swing(V): 0 (Auto), Swing(H): 3 (Middle), "
"Clock: 17:10, On Timer: 00:00, Off Timer: 00:00, Timer: -, "
"Weekly Timer: Off",
"Weekly Timer: Off"
", 10C Heat: Off, ISee: Off, Econo: Off, Absense detect: Off, "
"Direct / Indirect Mode: 0, Fresh: Off",
ac.toString());
ac.setTemp(21.5);
ac.setWeeklyTimerEnabled(true);
@ -921,7 +923,9 @@ TEST(TestMitsubishiACClass, HumanReadable) {
"Power: On, Mode: 1 (Heat), Temp: 21.5C, Fan: 6 (Quiet), "
"Swing(V): 0 (Auto), Swing(H): 3 (Middle), "
"Clock: 17:10, On Timer: 00:00, Off Timer: 00:00, Timer: -, "
"Weekly Timer: On",
"Weekly Timer: On"
", 10C Heat: Off, ISee: Off, Econo: Off, Absense detect: Off, "
"Direct / Indirect Mode: 0, Fresh: Off",
ac.toString());
}
@ -1434,7 +1438,9 @@ TEST(TestDecodeMitsubishiAC, Issue891) {
"Power: Off, Mode: 3 (Cool), Temp: 24C, Fan: 0 (Auto), "
"Swing(V): 0 (Auto), Swing(H): 3 (Middle), "
"Clock: 00:00, On Timer: 00:00, Off Timer: 00:00, Timer: -, "
"Weekly Timer: Off",
"Weekly Timer: Off"
", 10C Heat: Off, ISee: Off, Econo: Off, Absense detect: Off, "
"Direct / Indirect Mode: 0, Fresh: Off",
ac.toString());
}

View File

@ -312,20 +312,21 @@ TEST(TestToshibaACClass, HumanReadableOutput) {
ac.setRaw(initial_state);
EXPECT_EQ("Temp: 17C, Power: On, Mode: 0 (Auto), Fan: 0 (Auto), "
"Turbo: Off, Econo: Off",
"Turbo: Off, Econo: Off, Filter: Off",
ac.toString());
ac.setRaw(modified_state);
EXPECT_EQ("Temp: 17C, Power: On, Mode: 1 (Cool), Fan: 5 (High), "
"Turbo: Off, Econo: Off",
"Turbo: Off, Econo: Off, Filter: Off",
ac.toString());
ac.setTemp(25);
ac.setFan(3);
ac.setMode(kToshibaAcDry);
EXPECT_EQ("Temp: 25C, Power: On, Mode: 2 (Dry), Fan: 3 (Medium), "
"Turbo: Off, Econo: Off",
"Turbo: Off, Econo: Off, Filter: Off",
ac.toString());
ac.off();
EXPECT_EQ("Temp: 25C, Power: Off, Fan: 3 (Medium), Turbo: Off, Econo: Off",
EXPECT_EQ("Temp: 25C, Power: Off, Fan: 3 (Medium), Turbo: Off, Econo: Off, "
"Filter: Off",
ac.toString());
}
@ -379,7 +380,7 @@ TEST(TestDecodeToshibaAC, SyntheticExample) {
EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits);
EXPECT_EQ(
"Temp: 17C, Power: On, Mode: 0 (Auto), Fan: 0 (Auto), Turbo: Off, "
"Econo: Off",
"Econo: Off, Filter: Off",
IRAcUtils::resultAcToString(&irsend.capture));
stdAc::state_t r, p;
ASSERT_TRUE(IRAcUtils::decodeToState(&irsend.capture, &r, &p));
@ -555,6 +556,7 @@ TEST(TestToshibaACClass, toCommon) {
ac.setMode(kToshibaAcCool);
ac.setTemp(20);
ac.setFan(kToshibaAcFanMax);
ac.setFilter(true);
// Now test it.
ASSERT_EQ(decode_type_t::TOSHIBA_AC, ac.toCommon().protocol);
ASSERT_EQ(-1, ac.toCommon().model);
@ -563,13 +565,13 @@ TEST(TestToshibaACClass, toCommon) {
ASSERT_EQ(20, ac.toCommon().degrees);
ASSERT_EQ(stdAc::opmode_t::kCool, ac.toCommon().mode);
ASSERT_EQ(stdAc::fanspeed_t::kMax, ac.toCommon().fanspeed);
ASSERT_TRUE(ac.toCommon().filter);
// Unsupported.
ASSERT_EQ(stdAc::swingv_t::kOff, ac.toCommon().swingv);
ASSERT_EQ(stdAc::swingh_t::kOff, ac.toCommon().swingh);
ASSERT_FALSE(ac.toCommon().turbo);
ASSERT_FALSE(ac.toCommon().econo);
ASSERT_FALSE(ac.toCommon().light);
ASSERT_FALSE(ac.toCommon().filter);
ASSERT_FALSE(ac.toCommon().clean);
ASSERT_FALSE(ac.toCommon().beep);
ASSERT_FALSE(ac.toCommon().quiet);
@ -626,7 +628,7 @@ TEST(TestDecodeToshibaAC, RealLongExample) {
EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits);
EXPECT_EQ(
"Temp: 22C, Power: On, Mode: 0 (Auto), Fan: 0 (Auto), Turbo: On, "
"Econo: Off",
"Econo: Off, Filter: Off",
IRAcUtils::resultAcToString(&irsend.capture));
}
@ -731,7 +733,7 @@ TEST(TestToshibaACClass, ConstructLongState) {
ac.setEcono(true);
EXPECT_EQ(
"Temp: 29C, Power: On, Mode: 2 (Dry), Fan: 2 (UNKNOWN), "
"Turbo: Off, Econo: On",
"Turbo: Off, Econo: On, Filter: Off",
ac.toString());
EXPECT_EQ(kToshibaACStateLengthLong, ac.getStateLength());
const uint8_t expectedState[kToshibaACStateLengthLong] = {
@ -781,7 +783,8 @@ TEST(TestDecodeToshibaAC, RealExample_WHUB03NJ) {
EXPECT_EQ(kToshibaACBits, irsend.capture.bits);
EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits);
EXPECT_EQ(
"Temp: 20C, Power: Off, Fan: 0 (Auto), Turbo: Off, Econo: Off",
"Temp: 20C, Power: Off, Fan: 0 (Auto), Turbo: Off, Econo: Off, "
"Filter: Off",
IRAcUtils::resultAcToString(&irsend.capture));
}
@ -828,3 +831,34 @@ TEST(TestToshibaACClass, SwingCodes) {
"Temp: 17C, Swing(V): 4 (Toggle)",
ac.toString());
}
// For https://github.com/crankyoldgit/IRremoteESP8266/issues/1692
TEST(TestToshibaACClass, Filter) {
IRToshibaAC ac(kGpioUnused);
ac.begin();
EXPECT_FALSE(ac.getFilter());
ac.setFilter(true);
EXPECT_TRUE(ac.getFilter());
ac.setFilter(false);
EXPECT_FALSE(ac.getFilter());
ac.setFilter(true);
EXPECT_TRUE(ac.getFilter());
const uint8_t pure_off[kToshibaACStateLength] = {
0xF2, 0x0D, 0x03, 0xFC, 0x01, 0x40, 0x03, 0x00, 0x42};
ac.setRaw(pure_off);
EXPECT_FALSE(ac.getFilter());
const uint8_t pure_on[kToshibaACStateLength] = {
0xF2, 0x0D, 0x03, 0xFC, 0x01, 0x40, 0x03, 0x10, 0x52};
ac.setRaw(pure_on);
EXPECT_TRUE(ac.getFilter());
// Convert a known filter/pure on state to a known off filter/pure state.
ac.setFilter(false);
EXPECT_STATE_EQ(pure_off, ac.getRaw(), ac.getStateLength() * 8);
}

View File

@ -27,7 +27,9 @@ CPPFLAGS += -DUNIT_TEST -D_IR_LOCALE_=en-AU
# Flags passed to the C++ compiler.
CXXFLAGS += -g -Wall -Wextra -pthread -std=gnu++11
all : gc_decode mode2_decode
objects = $(patsubst %.cpp,%,$(wildcard *.cpp))
all : $(objects)
run_tests : all
failed=""; \
@ -35,6 +37,10 @@ run_tests : all
echo "RUNNING: $${py_unittest}"; \
python3 ./$${py_unittest} || failed="$${failed} $${py_unittest}"; \
done; \
for shell_unittest in *_test.sh; do \
echo "RUNNING: $${shell_unittest}"; \
bash ./$${shell_unittest} || failed="$${failed} $${shell_unittest}"; \
done; \
if [ -n "$${failed}" ]; then \
echo "FAIL: :-( :-( Unit test(s)$${failed} failed! :-( :-("; exit 1; \
else \
@ -46,7 +52,7 @@ run-% : all
python3 ./$*.py;
clean :
rm -f *.o *.pyc gc_decode mode2_decode
rm -f *.o *.pyc $(objects)
# Keep all intermediate files.
@ -80,7 +86,7 @@ IRrecv.o : $(USER_DIR)/IRrecv.cpp $(USER_DIR)/IRrecv.h $(USER_DIR)/IRremoteESP82
# new specific targets goes above this line
%_decode : $(COMMON_OBJ) %_decode.o
$(objects) : %: $(COMMON_OBJ) %.o
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@
ir_%.o : $(USER_DIR)/ir_%.h $(USER_DIR)/ir_%.cpp $(COMMON_DEPS) $(GTEST_HEADERS)

View File

@ -0,0 +1,148 @@
// Quick and dirty tool to convert a protocol's (hex) codes to raw timings.
// Copyright 2021 David Conran
#include <errno.h>
#include <inttypes.h>
#include <stdio.h>
#include <string.h>
#include <string>
#include "IRac.h"
#include "IRsend.h"
#include "IRsend_test.h"
#include "IRutils.h"
void usage_error(char *name) {
std::cerr << "Usage: " << name
<< " --protocol PROTOCOL_NAME"
<< " --code <hexidecimal>"
<< " [--bits 1-" << kStateSizeMax * 8 << "]"
<< " [--timinginfo]"
<< std::endl;
}
int main(int argc, char *argv[]) {
int argv_offset = 1;
int repeats = 0;
uint64_t code = 0;
uint8_t state[kStateSizeMax] = {0}; // All array elements are set to 0.
decode_type_t input_type = decode_type_t::UNKNOWN;
bool timinginfo = false;
// Check the invocation/calling usage.
if (argc < 5 || argc > 8) {
usage_error(argv[0]);
return 1;
}
if (strncmp("--protocol", argv[argv_offset], 11) == 0) {
argv_offset++;
input_type = strToDecodeType(argv[argv_offset]);
switch (input_type) {
// Unsupported types
case decode_type_t::UNUSED:
case decode_type_t::UNKNOWN:
case decode_type_t::GLOBALCACHE:
case decode_type_t::PRONTO:
case decode_type_t::RAW:
std::cerr << "The protocol specified is not supported by this program."
<< std::endl;
return 1;
default:
break;
}
argv_offset++;
}
uint16_t nbits = IRsend::defaultBits(input_type);
uint16_t stateSize = nbits / 8;
if (strncmp("--code", argv[argv_offset], 7) == 0) {
argv_offset++;
String hexstr = String(argv[argv_offset]);
uint64_t strOffset = 0;
if (hexstr.rfind("0x", 0) || hexstr.rfind("0X", 0)) strOffset = 2;
// Calculate how many hexadecimal characters there are.
uint64_t hexstrlength = hexstr.length() - strOffset;
// Ptr to the least significant byte of the resulting state for this
// protocol.
uint8_t *statePtr = &state[stateSize - 1];
// Convert the string into a state array of the correct length.
for (uint16_t i = 0; i < hexstrlength; i++) {
// Grab the next least sigificant hexadecimal digit from the string.
uint8_t c = tolower(hexstr[hexstrlength + strOffset - i - 1]);
if (isxdigit(c)) {
if (isdigit(c))
c -= '0';
else
c = c - 'a' + 10;
} else {
std::cerr << "Code " << argv[argv_offset]
<< " contains non-hexidecimal characters." << std::endl;
return 3;
}
if (i % 2 == 1) { // Odd: Upper half of the byte.
*statePtr += (c << 4);
statePtr--; // Advance up to the next least significant byte of state.
} else { // Even: Lower half of the byte.
*statePtr = c;
}
}
if (!hasACState(input_type))
code = std::stoull(argv[argv_offset], nullptr, 16);
argv_offset++;
}
if (argc - argv_offset > 0 && strncmp("--bits", argv[argv_offset], 7) == 0) {
argv_offset++;
nbits = std::stoul(argv[argv_offset], nullptr, 10);
if (nbits == 0 && (nbits <= kStateSizeMax * 8)) {
std::cerr << "Nr. of bits " << argv[argv_offset]
<< " is invalid." << std::endl;
return 1;
}
stateSize = nbits / 8;
argv_offset++;
}
if (argc - argv_offset > 0 &&
strncmp("--timinginfo", argv[argv_offset], 13) == 0) {
argv_offset++;
timinginfo = true;
}
if (argc - argv_offset != 0) {
usage_error(argv[0]);
return 1;
}
IRsendTest irsend(kGpioUnused);
IRrecv irrecv(kGpioUnused);
irsend.begin();
irsend.reset();
if (hasACState(input_type)) // Is it larger than 64 bits?
irsend.send(input_type, state, stateSize);
else
irsend.send(input_type, code, nbits, repeats);
irsend.makeDecodeResult();
irrecv.decode(&irsend.capture);
std::cout << "Code type: " << irsend.capture.decode_type << " ("
<< typeToString(irsend.capture.decode_type) << ")" << std::endl
<< "Code bits: " << irsend.capture.bits << std::endl;
if (hasACState(irsend.capture.decode_type)) {
String description = IRAcUtils::resultAcToString(&irsend.capture);
if (description.length()) {
std::cout << "Description: " << description.c_str() << std::endl;
}
}
std::cout << std::endl << resultToSourceCode(&irsend.capture) << std::endl;
if (timinginfo) std::cout << resultToTimingInfo(&irsend.capture);
return 0;
}

View File

@ -0,0 +1,65 @@
#! /bin/bash
CODE_TO_RAW=./code_to_raw
if [[ ! -x ${CODE_TO_RAW} ]]; then
echo "'raw_to_code' failed to compile and produce an executable."
exit 1
fi
function unittest_success()
{
COMMAND=$1
EXPECTED="$2"
echo -n "Testing: \"${COMMAND}\" ..."
OUTPUT="$(${COMMAND})"
STATUS=$?
FAILURE=""
if [[ ${STATUS} -ne 0 ]]; then
FAILURE="Non-Zero Exit status: ${STATUS}. "
fi
if [[ "${OUTPUT}" != "${EXPECTED}" ]]; then
FAILURE="${FAILURE} Unexpected Output: \"${OUTPUT}\" != \"${EXPECTED}\""
fi
if [[ -z ${FAILURE} ]]; then
echo " ok!"
return 0
else
echo
echo "FAILED: ${FAILURE}"
return 1
fi
}
read -r -d '' OUT << EOM
Code type: 4 (SONY)
Code bits: 12
uint16_t rawData[78] = {2400, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 600, 600, 600, 600, 600, 24600, 2400, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 600, 600, 600, 600, 600, 24600, 2400, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 600, 600, 600, 600, 600, 24600 }; // SONY F50
uint32_t address = 0x1;
uint32_t command = 0x2F;
uint64_t data = 0xF50;
EOM
unittest_success "${CODE_TO_RAW} --protocol Sony --code 0xf50 --bits 12" "${OUT}"
read -r -d '' OUT << EOM
Code type: 7 (SAMSUNG)
Code bits: 32
uint16_t rawData[68] = {4480, 4480, 560, 1680, 560, 1680, 560, 1680, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 1680, 560, 1680, 560, 1680, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 1680, 560, 560, 560, 560, 560, 1680, 560, 1680, 560, 560, 560, 560, 560, 1680, 560, 560, 560, 1680, 560, 1680, 560, 560, 560, 560, 560, 1680, 560, 1680, 560, 560, 560, 47040 }; // SAMSUNG E0E09966
uint32_t address = 0x7;
uint32_t command = 0x99;
uint64_t data = 0xE0E09966;
EOM
unittest_success "${CODE_TO_RAW} --protocol SAMSUNG --code 0xE0E09966" "${OUT}"
read -r -d '' OUT << xEOMx
Code type: 18 (KELVINATOR)
Code bits: 128
Description: Power: On, Mode: 1 (Cool), Temp: 27C, Fan: 1 (Low), Turbo: Off, Quiet: Off, XFan: On, Ion: Off, Light: Off, Swing(H): Off, Swing(V): Off
uint16_t rawData[280] = {9010, 4504, 680, 1530, 680, 510, 680, 510, 680, 1530, 680, 1530, 680, 510, 680, 510, 680, 510, 680, 1530, 680, 1530, 680, 510, 680, 1530, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 1530, 680, 510, 680, 510, 680, 510, 680, 510, 680, 1530, 680, 510, 680, 1530, 680, 510, 680, 510, 680, 1530, 680, 510, 680, 19974, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 1530, 680, 1530, 680, 1530, 680, 39950, 9010, 4504, 680, 1530, 680, 510, 680, 510, 680, 1530, 680, 1530, 680, 510, 680, 510, 680, 510, 680, 1530, 680, 1530, 680, 510, 680, 1530, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 1530, 680, 510, 680, 510, 680, 510, 680, 510, 680, 1530, 680, 1530, 680, 1530, 680, 510, 680, 510, 680, 1530, 680, 510, 680, 19974, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 1530, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 1530, 680, 1530, 680, 1530, 680, 1530, 680, 39950 }; // KELVINATOR
uint8_t state[16] = {0x19, 0x0B, 0x80, 0x50, 0x00, 0x00, 0x00, 0xE0, 0x19, 0x0B, 0x80, 0x70, 0x00, 0x00, 0x10, 0xF0};
xEOMx
unittest_success "${CODE_TO_RAW} --protocol KELVINATOR --code 0x190B8050000000E0190B8070000010F0" "${OUT}"

View File

@ -0,0 +1,14 @@
#!/bin/bash -e
# Copyright 2021 crankyoldgit
# Extract and constuct the string version of the IRremoteESP8266 Library Version
function getVerNum()
{
echo $(egrep "^#define\s+_IRREMOTEESP8266_VERSION_$1\s+" \
src/IRremoteESP8266.h | awk '{print $3;}')
}
MAJOR=$(getVerNum "MAJOR")
MINOR=$(getVerNum "MINOR")
PATCH=$(getVerNum "PATCH")
LIB_VERSION="${MAJOR}.${MINOR}.${PATCH}"
echo $LIB_VERSION

View File

@ -30,7 +30,8 @@ cat << EndOfTextEndOfTextEndOfText
EndOfTextEndOfTextEndOfText
CLASSES=$(egrep -h "^ *((enum|class) |} [a-zA-Z0-9_]+_t;$)" src/*.h |
CLASSES=$(egrep -h \
"^ *((enum|class|struct) [a-zA-Z0-9_]+|} [a-zA-Z0-9_]+_t;$)" src/*.h |
sed 's/^ *//;s/enum class//;s/\;$//' | cut -d' ' -f2 | sort -u |
grep -v "^__")
for i in ${CLASSES}; do
@ -45,7 +46,7 @@ cat << EndOfTextEndOfTextEndOfText
EndOfTextEndOfTextEndOfText
CTYPES="u?int(8|16|32|64)?(_t)?|void|bool|char|float|long|double|String|static"
OURTYPES="match_result_t|state_t|decode_type_t"
OURTYPES="match_result_t|stdAc::state_t|decode_type_t"
METHODS=$(egrep -h "^[ ]{0,2}(${CTYPES}|${OURTYPES})\*? [^ ]*\(" src/*.cpp |
sed 's/^ //' | cut -d' ' -f2 | sed 's/^\([^:]*::\| *\* *\)//' |
cut -d'(' -f1 | sort -u | grep -v RAM_ATTR)

View File

@ -0,0 +1,426 @@
#!/usr/bin/env python3
"""Generate SupportedProtocols.md by scraping source code files"""
import pathlib
import argparse
import subprocess
from io import StringIO
import sys
import re
import time
CODE_URL = "https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_"
BRAND_MODEL = re.compile(r"""
Brand:\s{1,20} # "Brand:" label followd by between 1 and 20 whitespace chars.
\b(?P<brand>.{1,40})\b # The actual brand of the device, max 40 chars.
\s{0,10}, # Followed by at most 10 whitespace chars, then a comma.
\s{1,20} # The between 1 and 20 whitespace chars.
Model:\s{1,20} # "Model:" label followd by between 1 and 20 whitespace chars.
\b(?P<model>.{1,80}) # The model info of the device, max 80 chars.
\s{0,5}$ # Followed by at most 5 whitespaces before the end of line.
""", re.VERBOSE)
ENUMS = re.compile(r"enum (\w{1,60}) {(.{1,5000}?)};", re.DOTALL)
ENUM_ENTRY = re.compile(r"^\s{1,80}(\w{1,80})", re.MULTILINE)
DECODED_PROTOCOLS = re.compile(r"""
.{0,80} # Ignore upto an 80 char line of whitespace/code etc.
# Now look for code that looks like we are assigning the Protocol type.
# There are two typical styles used:
(?:results->decode_type # The first style.
| # Or
typeguess) # The second style
\s{0,5}=\s{0,5} # The assignment operator and potential whitespace
(?:decode_type_t::)? # The protocol could have an optional type prefix.
(\w{1,40}); # Finally, the last word of code should be the Protocol.
""", re.VERBOSE)
AC_FN = re.compile(r"ir_(.{1,80})\.h")
AC_MODEL_ENUM_RE = re.compile(r"(.{1,40})_ac_remote_model_t")
IRSEND_FN_RE = re.compile(r"IRsend\.h")
ALL_FN = re.compile(r"ir_(.{1,80})\.(h|cpp)")
EXCLUDED_PROTOCOLS = ["UNKNOWN", "UNUSED", "kLastDecodeType", "typeguess"]
EXCLUDED_ACS = ["Magiquest", "NEC"]
def getgitcommittime():
"""Call git to get time of last commit
"""
try:
label = subprocess.check_output(\
["git", "show", "-s", "--format=%ct", "HEAD"]).strip()
return int(label)
except FileNotFoundError as err:
print("Git failed, which is ok, no git binary found?:", err)
return None
except subprocess.SubprocessError as err:
print("Git failed, which is ok, see output, maybe no git checkout?:", err)
return None
def getmarkdownheader():
"""Get the generated header
"""
srctime = getgitcommittime()
# pylint: disable=C0209
return """<!--- WARNING: Do NOT edit this file directly.
It is generated by './tools/scrape_supported_devices.py'.
Last generated: {} --->""".format(
time.strftime("%a %d %b %Y %H:%M:%S +0000", time.gmtime(srctime)))
# pylint: enable=C0209
def getallprotocols():
"""Return all protocls configured in IRremoteESP8266.h
"""
irremote = ARGS.directory / "IRremoteESP8266.h"
enums = getenums(irremote)["decode_type_t"]
if not enums:
errorexit("Error getting ENUMS from IRremoteESP8266.h")
return enums
def getdecodedprotocols():
"""All protocols that include decoding support"""
ret = set()
for path in ARGS.directory.iterdir():
if path.suffix != ".cpp":
continue
matches = DECODED_PROTOCOLS.finditer(path.open(encoding="utf-8").read())
for match in matches:
protocol = match.group(1)
if protocol not in EXCLUDED_PROTOCOLS:
ret.add(protocol)
return ret
def getallacs():
"""All supported A/C codes"""
ret = {}
for path in ARGS.directory.iterdir():
match = AC_FN.match(path.name)
if match:
acprotocol = match.group(1)
rawmodels = getenums(path)
models = set()
for model in rawmodels:
model = model.upper()
model = model.replace(f"K{acprotocol.upper()}", "")
if model and model not in EXCLUDED_PROTOCOLS:
models.add(model)
if acprotocol in ret:
ret[acprotocol].update(models)
else:
ret[acprotocol] = models
# Parse IRsend.h's enums
match = IRSEND_FN_RE.match(path.name)
if match:
rawmodels = getenums(path)
for acprotocol, acmodels in rawmodels.items():
models = set()
for model in acmodels:
model = model.upper()
model = model.replace(f"K{acprotocol.upper()}", "")
if model and model not in EXCLUDED_PROTOCOLS:
models.add(model)
if acprotocol in ret:
ret[acprotocol].update(models)
else:
ret[acprotocol] = models
return ret
class FnSets():
"""Container for getalldevices"""
def __init__(self):
self.allcodes = {}
self.fnnomatch = set()
self.allhfileprotos = set()
self.fnhmatch = set()
self.fncppmatch = set()
def add(self, supports, path):
"""add the path to correct set based on supports"""
if path.suffix == ".h":
self.allhfileprotos.add(path.stem)
if supports:
if path.suffix == ".h":
self.fnhmatch.add(path.stem)
elif path.suffix == ".cpp":
self.fncppmatch.add(path.stem)
else:
self.fnnomatch.add(path.stem)
def printwarnings(self):
"""print warnings"""
# all protos with support in .cpp file, when there is a .h file
# meaning that the documentation should probably be moved to .h
# in the future, with doxygen, that might change
protosincppwithh = list(self.fncppmatch & self.allhfileprotos)
if protosincppwithh:
protosincppwithh.sort()
print("The following files has supports section in .cpp, expected in .h")
for path in protosincppwithh:
print(f"\t{path}")
protosincppandh = list(self.fncppmatch & self.fnhmatch)
if protosincppandh:
protosincppandh.sort()
print("The following files has supports section in both .h and .cpp")
for path in protosincppandh:
print(f"\t{path}")
nosupports = self.getnosupports()
if nosupports:
nosupports.sort()
print("The following files had no supports section:")
for path in nosupports:
print(f"\t{path}")
return protosincppwithh or protosincppandh or nosupports
def getnosupports(self):
"""get protos without supports sections"""
return list(self.fnnomatch - self.fnhmatch - self.fncppmatch)
def getalldevices():
"""All devices and associated branding and model information (if available)
"""
sets = FnSets()
for path in ARGS.directory.iterdir():
match = ALL_FN.match(path.name)
if not match:
continue
supports = extractsupports(path)
sets.add(supports, path)
protocol = match.group(1)
for brand, model in supports:
protocolbrand = (protocol, brand)
pbset = sets.allcodes.get(protocolbrand, [])
if model in pbset:
print(f"Model {model} is duplicated for {protocol}, {brand}")
sets.allcodes[protocolbrand] = pbset + [model]
for fnprotocol in sets.getnosupports():
sets.allcodes[(fnprotocol[3:], "Unknown")] = []
return sets
def getenums(path):
"""Returns the keys for the first enum type in path
"""
ret = {}
for enums in ENUMS.finditer(path.open(encoding="utf-8").read()):
if enums:
enum_name = AC_MODEL_ENUM_RE.search(enums.group(1))
if enum_name:
enum_name = enum_name.group(1).capitalize()
else:
enum_name = enums.group(1)
ret[enum_name] = set()
for enum in ENUM_ENTRY.finditer(enums.group(2)):
enum = enum.group(1)
if enum in EXCLUDED_PROTOCOLS:
continue
ret[enum_name].add(enum)
return ret
ARGS = None
def initargs():
"""Init the command line arguments"""
global ARGS # pylint: disable=global-statement
parser = argparse.ArgumentParser()
parser.add_argument(
"-n",
"--noout",
help="generate no output data, combine with --alert to only check",
action="store_true",
)
parser.add_argument(
"-s",
"--stdout",
help="output to stdout rather than SupportedProtocols.md",
action="store_true",
)
parser.add_argument("-v",
"--verbose",
help="increase output verbosity",
action="store_true")
parser.add_argument(
"-a",
"--alert",
help="alert if a file does not have a supports section, "
"non zero exit code if issues where found",
action="store_true",
)
parser.add_argument(
"directory",
nargs="?",
help="directory of the source git checkout",
default=None,
)
ARGS = parser.parse_args()
if ARGS.directory is None:
src = pathlib.Path("../src")
if not src.is_dir():
src = pathlib.Path("./src")
else:
src = pathlib.Path(ARGS.directory) / "src"
if not src.is_dir():
errorexit(f"Directory not valid: {src!s}")
ARGS.directory = src
return ARGS
def getmdfile():
"""Resolves SupportedProtocols.md path"""
foutpath = ARGS.directory / "../SupportedProtocols.md"
return foutpath.resolve()
def errorexit(msg):
"""Print an error and exit on critical error"""
sys.stderr.write(f"{msg}\n")
sys.exit(1)
def extractsupports(path):
"""Extract all of the Supports: sections and associated brands and models
"""
supports = []
insupports = False
for line in path.open(encoding="utf-8"):
if not line.startswith("//"):
continue
line = line[2:].strip()
if line == "Supports:":
insupports = True
continue
if insupports:
match = BRAND_MODEL.match(line)
if match:
supports.append((match.group("brand"), match.group("model")))
else:
insupports = False
continue
# search and inform about any legacy formated supports data
elif any(x in line for x in [ \
"seems compatible with",
"be compatible with",
"it working with here"]):
print(f"\t{path.name} Legacy supports format found\n\t\t{line}")
return supports
def makeurl(txt, path):
"""Make a Markup URL from given filename"""
return f"[{txt}]({CODE_URL + path})"
def outputprotocols(fout, protocols):
"""For a given protocol set, sort and output the markdown"""
protocols = list(protocols)
protocols.sort()
for protocol in protocols:
fout.write(f"- {protocol}\n")
def generate(fout):
"""Generate data to fout
return True on any issues (when alert is active)"""
decodedprotocols = getdecodedprotocols()
sendonly = getallprotocols() - decodedprotocols
allacs = getallacs()
sets = getalldevices()
allcodes = sets.allcodes
allbrands = list(allcodes.keys())
allbrands.sort()
fout.write("\n# IR Protocols supported by this library\n\n")
fout.write(
"| Protocol | Brand | Model | A/C Model | Detailed A/C Support |\n")
fout.write("| --- | --- | --- | --- | --- |\n")
for protocolbrand in allbrands:
protocol, brand = protocolbrand
codes = allcodes[protocolbrand]
codes.sort()
acmodels = []
acsupport = "-"
if protocol in allacs:
acmodels = list(allacs[protocol])
acmodels.sort()
brand = makeurl(brand, protocol + ".h")
if protocol not in EXCLUDED_ACS:
acsupport = "Yes"
# pylint: disable=C0209
fout.write("| {} | **{}** | {} | {} | {} |\n".format(
makeurl(protocol, protocol + ".cpp"),
brand,
"<BR>".join(codes).replace("|", "\\|"),
"<BR>".join(acmodels),
acsupport,
))
# pylint: enable=C0209
fout.write("\n\n## Send only protocols:\n\n")
outputprotocols(fout, sendonly)
fout.write("\n\n## Send & decodable protocols:\n\n")
outputprotocols(fout, decodedprotocols)
return ARGS.alert and sets.printwarnings()
def generatenone():
"""No out write
return True on any issues"""
return generate(StringIO())
def generatestdout():
"""Standard out write
return True on any issues"""
fout = sys.stdout
fout.write(getmarkdownheader())
return generate(fout)
def generatefile():
"""File write, extra detection of changes in existing file
return True on any issues, but only if there is changes"""
# get file path
foutpath = getmdfile()
if ARGS.verbose:
print(f"Output path: {foutpath!s}")
# write data to temp memorystream
ftemp = StringIO()
ret = generate(ftemp)
# get old filedata, skipping header
with getmdfile().open("r", encoding="utf-8") as forg:
olddata = forg.readlines()[3:]
# get new data, skip first empty line
ftemp.seek(0)
newdata = ftemp.readlines()[1:]
# if new data is same as old we don't need to write anything
if newdata == olddata:
print("No changes, exit without write")
return False
# write output
with foutpath.open("w", encoding="utf-8") as fout:
fout.write(getmarkdownheader())
fout.write(ftemp.getvalue())
return ret
def main():
"""Default main function
return True on any issues"""
initargs()
if ARGS.verbose:
print(f"Looking for files in: {ARGS.directory.resolve()!s}")
if ARGS.noout:
return generatenone()
if ARGS.stdout:
return generatestdout()
# default file
return generatefile()
if __name__ == "__main__":
sys.exit(1 if main() else 0)