Merge pull request #6324 from s-hadinger/ir_full2

Add 'sonoff-ir' pre-packaged IR-dedicated firmware and 'sonoff-ircustom' to customize firmware with IR Full protocol support
This commit is contained in:
Theo Arends 2019-08-30 22:48:56 +02:00 committed by GitHub
commit b886bd217b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
222 changed files with 2116 additions and 280 deletions

View File

@ -13,6 +13,7 @@ env:
- ENV=sonoff-knx
- ENV=sonoff-sensors
- ENV=sonoff-display
- ENV=sonoff-ir
- ENV=sonoff-BG
- ENV=sonoff-BR
- ENV=sonoff-CN

View File

@ -48,3 +48,6 @@ tools/mode2_decode
#Cygwin builds
*.exe
# Mac extended attributes
.DS_Store

View File

View File

@ -56,6 +56,7 @@ jobs:
- arduino --verify --board $BD $PWD/examples/TurnOnMitsubishiHeavyAc/TurnOnMitsubishiHeavyAc.ino 2> /dev/null
- arduino --verify --board $BD $PWD/examples/DumbIRRepeater/DumbIRRepeater.ino 2> /dev/null
- arduino --verify --board $BD $PWD/examples/SmartIRRepeater/SmartIRRepeater.ino 2> /dev/null
- arduino --verify --board $BD $PWD/examples/CommonAcControl/CommonAcControl.ino 2> /dev/null
- script:
# Check the version numbers match.
- LIB_VERSION=$(egrep "^#define\s+_IRREMOTEESP8266_VERSION_\s+" src/IRremoteESP8266.h | cut -d\" -f2)

View File

@ -9,7 +9,7 @@
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.6.4 Now Available
## v2.6.5 Now Available
Version 2.6.4 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

View File

@ -1,5 +1,30 @@
# Release Notes
## _v2.6.5 (20190828)_
**[Bug Fixes]**
- IRMQTTServer: Remove duplicate MQTT_CLIMATE from HA discovery (#869)
- Fujitsu: Ensure `on()` is called in common a/c framework. (#862)
- Update `strToModel()` (#861)
- IRMQTTServer: Add missing header file. (#858)
- IRMQTTServer: Fix a compile error when HTML_PASSWORD_ENABLE is enabled. (#856)
**[Features]**
- IRrecv: Allow tolerance percentage to be set at run-time. (#865)
- Basic support for Daikin152 A/C protocol. (#874)
- Teco: Add light, humid, & save support. (#871)
- Detailed support for Amcor A/C protocol. (#836, #854)
- IRMQTTServer: Add ability to report Vcc at the ESP chip. (#845)
- Gree: Add timer support. (#849)
- IRac/Mitsubishi A/C: Support wide `swingh_t` mode (#844)
- IRMQTTServer: Generate protocol and bit size html selects (#838)
**[Misc]**
- New example code to show how to use the `IRac` class to control A/Cs (#839)
- Improve/fix `swingh_t::kWide` support (#846)
- Kelvinator: Optimise code a little to save space. (#843)
## _v2.6.4 (20190726)_
**[Bug Fixes]**

View File

@ -1,16 +1,17 @@
<!--- WARNING: Do NOT edit this file directly.
It is generated by './tools/scrape_supported_devices.py'.
Last generated: Fri Jul 26 17:01:16 2019 --->
Last generated: Wed Aug 28 12:37:20 2019 --->
# IR Protocols supported by this library
| Protocol | Brand | Model | A/C Model | Detailed A/C Support |
| --- | --- | --- | --- | --- |
| [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>ADR-853H A/C<BR>TAC-444 remote<BR>TAC-444 remote<BR>TAC-495 remote<BR>TAC-495 remote | | Yes |
| [Argo](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Argo.cpp) | **[Argo](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Argo.h)** | Ulisse 13 DCI Mobile Split A/C | | Yes |
| [Carrier](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Carrier.cpp) | **Carrier/Surrey** | 42QG5A55970 remote<BR>53NGK009/012 Inverter<BR>619EGX0090E0 A/C<BR>619EGX0120E0 A/C<BR>619EGX0180E0 A/C<BR>619EGX0220E0 A/C | | - |
| [Coolix](https://github.com/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>BINR 070/071 split-type A/C<BR>RG57K7(B)/BGEF Remote<BR>RG57K7(B)/BGEF Remote | | 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>MS12FU-10HRDN1-QRD0GW(B) A/C<BR>MSABAU-07HRFN1-QRD0GW A/C (circa 2016)<BR>MSABAU-07HRFN1-QRD0GW A/C (circa 2016)<BR>RG52D/BGE Remote<BR>RG52D/BGE Remote | | Yes |
| [Daikin](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Daikin.cpp) | **[Daikin](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Daikin.h)** | 17 Series A/C (DAIKIN128)<BR>ARC423A5 remote<BR>ARC433** remote<BR>ARC433B69 remote<BR>ARC477A1 remote<BR>BRC4C153 remote<BR>BRC52B63 remote (DAIKIN128)<BR>FTE12HV2S A/C<BR>FTXB09AXVJU A/C (DAIKIN128)<BR>FTXB12AXVJU A/C (DAIKIN128)<BR>FTXZ25NV1B A/C<BR>FTXZ35NV1B A/C<BR>FTXZ50NV1B A/C | | Yes |
| [Daikin](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Daikin.cpp) | **[Daikin](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Daikin.h)** | 17 Series A/C (DAIKIN128)<BR>ARC423A5 remote<BR>ARC433** remote<BR>ARC433B69 remote<BR>ARC477A1 remote<BR>ARC480A5 remote (DAIKIN152)<BR>BRC4C153 remote<BR>BRC52B63 remote (DAIKIN128)<BR>FTE12HV2S A/C<BR>FTXB09AXVJU A/C (DAIKIN128)<BR>FTXB12AXVJU A/C (DAIKIN128)<BR>FTXZ25NV1B A/C<BR>FTXZ35NV1B A/C<BR>FTXZ50NV1B A/C | | Yes |
| [Denon](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Denon.cpp) | **Unknown** | | | - |
| [Dish](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Dish.cpp) | **DISH NETWORK** | echostar 301 | | - |
| [Electra](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Electra.cpp) | **[AUX](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Electra.h)** | KFR-35GW/BpNFW=3 A/C<BR>YKR-T/011 remote | | Yes |
@ -40,7 +41,7 @@
| [Midea](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Midea.cpp) | **[Pioneer System](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Midea.h)** | RUBO18GMFILCAD A/C (18K BTU)<BR>RYBO12GMFILCAD A/C (12K BTU) | | 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<BR>TV | | 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<BR>RLA502A700B remote<BR>SRKxxZJ-S A/C<BR>SRKxxZM-S A/C<BR>SRKxxZMXA-S A/C | | Yes |
| [NEC](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_NEC.cpp) | **[Unknown](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_NEC.h)** | | | Yes |
| [NEC](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_NEC.cpp) | **[Yamaha](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_NEC.h)** | RAV561 remote<BR>RXV585B A/V Receiver | | Yes |
| [Neoclima](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Neoclima.cpp) | **[Neoclima](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Neoclima.h)** | NS-09AHTI A/C<BR>NS-09AHTI A/C<BR>ZH/TY-01 remote<BR>ZH/TY-01 remote | | Yes |
| [Nikai](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Nikai.cpp) | **Unknown** | | | - |
| [Panasonic](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Panasonic.cpp) | **[Panasonic](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Panasonic.h)** | A75C2311 remote (CKP)<BR>A75C3704 remote<BR>A75C3747 remote<BR>A75C3747 remote<BR>A75C3747 remote<BR>A75C3747 remote<BR>CKP series A/C<BR>CS-ME10CKPG A/C<BR>CS-ME12CKPG A/C<BR>CS-ME14CKPG A/C<BR>CS-YW9MKD A/C<BR>CS-Z9RKR A/C<BR>DKE series A/C<BR>JKE series A/C<BR>NKE series A/C<BR>RKR series A/C<BR>TV | CKP<BR>DKE<BR>JKE<BR>LKE<BR>NKE<BR>RKR | Yes |
@ -54,7 +55,7 @@
| [Sherwood](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Sherwood.cpp) | **Sherwood** | RC-138 remote<BR>RD6505(B) Receiver | | - |
| [Sony](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Sony.cpp) | **Unknown** | | | - |
| [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 | | Yes |
| [Teco](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Teco.cpp) | **[Unknown](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Teco.h)** | | | Yes |
| [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 |
| [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-B13N3KV2<BR>RAS-B13N3KVP-E<BR>WC-L03SE<BR>WH-TA04NE | | Yes |
| [Trotec](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Trotec.cpp) | **[Unknown](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Trotec.h)** | | | Yes |
| [Vestel](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Vestel.cpp) | **[Vestel](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Vestel.h)** | BIOX CXP-9 A/C (9K BTU) | | Yes |
@ -73,11 +74,13 @@
## Send & decodable protocols:
- AIWA_RC_T501
- AMCOR
- ARGO
- CARRIER_AC
- COOLIX
- DAIKIN
- DAIKIN128
- DAIKIN152
- DAIKIN160
- DAIKIN176
- DAIKIN2

View File

@ -0,0 +1,81 @@
/* Copyright 2019 David Conran
*
* This example code demonstrates how to use the "Common" IRac class to control
* various air conditions. The IRac class does not support all the features
* for every protocol. Some have more detailed support that what the "Common"
* interface offers, and some only have a limited subset of the "Common" options.
*
* This example code will:
* o Try to turn on, then off every fully supported A/C protocol we know of.
* o It will try to put the A/C unit into Cooling mode at 25C, with a medium
* fan speed, and no fan swinging.
* Note: Some protocols support multiple models, only the first model is tried.
*
*/
#include <Arduino.h>
#include <IRremoteESP8266.h>
#include <IRac.h>
#include <IRutils.h>
const uint16_t kIrLed = 4; // The ESP GPIO pin to use that controls the IR LED.
IRac ac(kIrLed); // Create a A/C object using GPIO to sending messages with.
stdAc::state_t state; // Where we will store the desired state of the A/C.
stdAc::state_t prev; // Where we will store the previous state of the A/C.
void setup() {
Serial.begin(115200);
delay(200);
// Set up what we want to send.
// See state_t, opmode_t, fanspeed_t, swingv_t, & swingh_t in IRsend.h for
// all the various options.
state.protocol = decode_type_t::DAIKIN; // Set a protocol to use.
state.model = 1; // Some A/C's have different models. Let's try using just 1.
state.mode = stdAc::opmode_t::kCool; // Run in cool mode initially.
state.celsius = true; // Use Celsius for units of temp. False = Fahrenheit
state.degrees = 25; // 25 degrees.
state.fanspeed = stdAc::fanspeed_t::kMedium; // Start with the fan at medium.
state.swingv = stdAc::swingv_t::kOff; // Don't swing the fan up or down.
state.swingh = stdAc::swingh_t::kOff; // Don't swing the fan left or right.
state.light = false; // Turn off any LED/Lights/Display that we can.
state.beep = false; // Turn off any beep from the A/C if we can.
state.econo = false; // Turn off any economy modes if we can.
state.filter = false; // Turn off any Ion/Mold/Health filters if we can.
state.turbo = false; // Don't use any turbo/powerful/etc modes.
state.quiet = false; // Don't use any quiet/silent/etc modes.
state.sleep = -1; // Don't set any sleep time or modes.
state.clean = false; // Turn off any Cleaning options if we can.
state.clock = -1; // Don't set any current time if we can avoid it.
state.power = false; // Initially start with the unit off.
prev = state; // Make sure we have a valid previous state.
}
void loop() {
// For every protocol the library has ...
for (int i = 1; i < kLastDecodeType; i++) {
decode_type_t protocol = (decode_type_t)i;
// If the protocol is supported by the IRac class ...
if (ac.isProtocolSupported(protocol)) {
state.protocol = protocol; // Change the protocol used.
Serial.println("Protocol " + String(protocol) + " / " +
typeToString(protocol));
state.power = true; // We want to turn on the A/C unit.
// Have the IRac class create and send a message.
// We need a `prev` state as some A/Cs use toggle messages.
// e.g. On & Off are the same message. When given the previous state,
// it will try to do the correct thing for you.
ac.sendAc(state, &prev); // Construct and send the message.
Serial.println("Sent a message to turn ON the A/C unit.");
prev = state; // Copy new state over the previous one.
delay(5000); // Wait 5 seconds.
state.power = false; // Now we want to turn the A/C off.
ac.sendAc(state, &prev); // Construct and send the message.
Serial.println("Sent a message to turn OFF the A/C unit.");
prev = state; // Copy new state over the previous one.
delay(1000); // Wait 1 second.
}
}
Serial.println("Starting from the begining again ...");
}

View File

@ -5,6 +5,9 @@
#ifndef EXAMPLES_IRMQTTSERVER_IRMQTTSERVER_H_
#define EXAMPLES_IRMQTTSERVER_IRMQTTSERVER_H_
#if defined(ESP8266)
#include <ESP8266WiFi.h>
#endif // ESP8266
#include <IRremoteESP8266.h>
#include <IRrecv.h>
#include <IRsend.h>
@ -156,6 +159,16 @@ const uint16_t kMinUnknownSize = 2 * 10;
// ------------------------ Advanced Usage Only --------------------------------
// Reports the input voltage to the ESP chip. **NOT** the input voltage
// to the development board (e.g. NodeMCU, D1 Mini etc) which are typically
// powered by USB (5V) which is then lowered to 3V via a Low Drop Out (LDO)
// Voltage regulator. Hence, this feature is turned off by default as it
// make little sense for most users as it really isn't the actual input voltage.
// E.g. For purposes of monitoring a battery etc.
// Note: Turning on the feature costs ~250 bytes of prog space.
#define REPORT_VCC false // Do we report Vcc via html info page & MQTT?
// Keywords for MQTT topics, html arguments, or config file.
#define KEY_PROTOCOL "protocol"
#define KEY_MODEL "model"
#define KEY_POWER "power"
@ -175,6 +188,7 @@ const uint16_t kMinUnknownSize = 2 * 10;
#define KEY_CELSIUS "use_celsius"
#define KEY_JSON "json"
#define KEY_RESEND "resend"
#define KEY_VCC "vcc"
// HTML arguments we will parse for IR code information.
#define KEY_TYPE "type" // KEY_PROTOCOL is also checked too.
@ -206,11 +220,14 @@ const uint8_t kPasswordLength = 20;
// ----------------- End of User Configuration Section -------------------------
// Constants
#define _MY_VERSION_ "v1.3.3"
#define _MY_VERSION_ "v1.3.4"
const uint8_t kRebootTime = 15; // Seconds
const uint8_t kQuickDisplayTime = 2; // Seconds
// Common bit sizes for the simple protocols.
const uint8_t kCommonBitSizes[] = {
12, 13, 15, 16, 20, 24, 28, 32, 35, 36, 42, 48, 56, 64};
// Gpio related
#if defined(ESP8266)
const int8_t kTxGpios[] = {-1, 0, 1, 2, 3, 4, 5, 12, 13, 14, 15, 16};
@ -294,6 +311,9 @@ void sendJsonState(const stdAc::state_t state, const String topic,
const bool retain = false, const bool ha_mode = true);
#endif // MQTT_CLIMATE_JSON
#endif // MQTT_ENABLE
#if REPORT_VCC
String vccToString(void);
#endif // REPORT_VCC
bool isSerialGpioUsedByIr(void);
void debug(const char *str);
void saveWifiConfigCallback(void);
@ -319,7 +339,9 @@ String addJsReloadUrl(const String url, const uint16_t timeout_s,
const bool notify);
void handleExamples(void);
String htmlSelectBool(const String name, const bool def);
String htmlSelectProtocol(const String name, const decode_type_t def);
String htmlSelectClimateProtocol(const String name, const decode_type_t def);
String htmlSelectAcStateProtocol(const String name, const decode_type_t def,
const bool simple);
String htmlSelectModel(const String name, const int16_t def);
String htmlSelectMode(const String name, const stdAc::opmode_t def);
String htmlSelectFanspeed(const String name, const stdAc::fanspeed_t def);

View File

@ -347,6 +347,10 @@
using irutils::msToString;
#if REPORT_VCC
ADC_MODE(ADC_VCC);
#endif // REPORT_VCC
// Globals
#if defined(ESP8266)
ESP8266WebServer server(kHttpPort);
@ -650,6 +654,26 @@ String htmlMenu(void) {
return html;
}
String htmlSelectAcStateProtocol(const String name, const decode_type_t def,
const bool simple) {
String html = "<select name='" + name + "'>";
for (uint8_t i = 1; i <= decode_type_t::kLastDecodeType; i++) {
if (simple ^ hasACState((decode_type_t)i)) {
switch (i) {
case decode_type_t::RAW:
case decode_type_t::PRONTO:
case decode_type_t::GLOBALCACHE:
break;
default:
html += htmlOptionItem(String(i), typeToString((decode_type_t)i),
i == def);
}
}
}
html += F("</select>");
return html;
}
// Root web page with example usage etc.
void handleRoot(void) {
#if HTML_PASSWORD_ENABLE
@ -664,65 +688,23 @@ void handleRoot(void) {
html += F(
"<h3>Send a simple IR message</h3><p>"
"<form method='POST' action='/ir' enctype='multipart/form-data'>"
"Type: "
"<select name='type'>"
"<option value='9'>Aiwa RC T501</option>"
"<option value='37'>Carrier AC</option>"
"<option value='15'>Coolix</option>"
"<option value='17'>Denon</option>"
"<option value='13'>Dish</option>"
"<option value='43'>GICable</option>"
"<option value='63'>Goodweather</option>"
"<option value='64'>Inax</option>"
"<option value='6'>JVC</option>"
"<option value='36'>Lasertag</option>"
"<option value='58'>LEGOPF</option>"
"<option value='10'>LG</option>"
"<option value='51'>LG2</option>"
"<option value='47'>Lutron</option>"
"<option value='35'>MagiQuest</option>"
"<option value='34'>Midea</option>"
"<option value='12'>Mitsubishi</option>"
"<option value='39'>Mitsubishi2</option>"
"<option selected='selected' value='3'>NEC</option>" // Default
"<option value='29'>Nikai</option>"
"<option value='5'>Panasonic</option>"
"<option value='50'>Pioneer</option>"
"<option value='1'>RC-5</option>"
"<option value='23'>RC-5X</option>"
"<option value='2'>RC-6</option>"
"<option value='21'>RC-MM</option>"
"<option value='7'>Samsung</option>"
"<option value='56'>Samsung36</option>"
"<option value='11'>Sanyo</option>"
"<option value='22'>Sanyo LC7461</option>"
"<option value='14'>Sharp</option>"
"<option value='19'>Sherwood</option>"
"<option value='4'>Sony</option>"
"<option value='54'>Vestel AC</option>"
"<option value='55'>Teco AC</option>"
"<option value='8'>Whynter</option>"
"</select>"
"Type: ");
html += htmlSelectAcStateProtocol(KEY_TYPE, decode_type_t::NEC, true);
html += F(
" Code: 0x<input type='text' name='code' min='0' value='0' size='16'"
" maxlength='16'>"
" Bit size: "
"<select name='bits'>"
"<option selected='selected' value='0'>Default</option>" // Default
// Common bit length options for most protocols.
"<option value='12'>12</option>"
"<option value='13'>13</option>"
"<option value='14'>14</option>"
"<option value='15'>15</option>"
"<option value='16'>16</option>"
"<option value='20'>20</option>"
"<option value='21'>21</option>"
"<option value='24'>24</option>"
"<option value='28'>28</option>"
"<option value='32'>32</option>"
"<option value='35'>35</option>"
"<option value='36'>36</option>"
"<option value='48'>48</option>"
"<option value='56'>56</option>"
"<option selected='selected' value='0'>Default</option>"); // Default
for (uint8_t i = 0; i < sizeof(kCommonBitSizes); i++) {
String num = String(kCommonBitSizes[i]);
html += F("<option value='");
html += num;
html += F("'>");
html += num;
html += F("</option>");
}
html += F(
"</select>"
" Repeats: <input type='number' name='repeats' min='0' max='99' value='0'"
"size='2' maxlength='2'>"
@ -731,36 +713,9 @@ void handleRoot(void) {
"<br><hr>"
"<h3>Send a complex (Air Conditioner) IR message</h3><p>"
"<form method='POST' action='/ir' enctype='multipart/form-data'>"
"Type: "
"<select name='type'>"
"<option value='27'>Argo</option>"
"<option value='16'>Daikin (35 bytes)</option>"
"<option value='68'>Daikin128 (16 bytes)</option>"
"<option value='65'>Daikin160 (20 bytes)</option>"
"<option value='67'>Daikin176 (22 bytes)</option>"
"<option value='53'>Daikin2 (39 bytes)</option>"
"<option value='61'>Daikin216 (27 bytes)</option>"
"<option value='48'>Electra</option>"
"<option value='33'>Fujitsu</option>"
"<option value='24'>Gree</option>"
"<option value='38'>Haier (9 bytes)</option>"
"<option value='44'>Haier (14 bytes/YR-W02)</option>"
"<option value='40'>Hitachi (28 bytes)</option>"
"<option value='41'>Hitachi1 (13 bytes)</option>"
"<option value='42'>Hitachi2 (53 bytes)</option>"
"<option selected='selected' value='18'>Kelvinator</option>" // Default
"<option value='20'>Mitsubishi</option>"
"<option value='59'>Mitsubishi Heavy (11 bytes)</option>"
"<option value='60'>Mitsubishi Heavy (19 bytes)</option>"
"<option value='52'>MWM</option>"
"<option value='66'>Neoclima</option>"
"<option value='46'>Samsung</option>"
"<option value='62'>Sharp</option>"
"<option value='57'>TCL112</option>"
"<option value='32'>Toshiba</option>"
"<option value='28'>Trotec</option>"
"<option value='45'>Whirlpool</option>"
"</select>"
"Type: ");
html += htmlSelectAcStateProtocol(KEY_TYPE, decode_type_t::KELVINATOR, false);
html += F(
" State code: 0x"
"<input type='text' name='code' size='");
html += String(kStateSizeMax * 2);
@ -918,7 +873,7 @@ String htmlSelectBool(const String name, const bool def) {
return html;
}
String htmlSelectProtocol(const String name, const decode_type_t def) {
String htmlSelectClimateProtocol(const String name, const decode_type_t def) {
String html = "<select name='" + name + "'>";
for (uint8_t i = 1; i <= decode_type_t::kLastDecodeType; i++) {
if (IRac::isProtocolSupported((decode_type_t)i)) {
@ -962,7 +917,7 @@ String htmlSelectGpio(const String name, const int16_t def,
String htmlSelectMode(const String name, const stdAc::opmode_t def) {
String html = "<select name='" + name + "'>";
for (int8_t i = -1; i <= 4; i++) {
for (int8_t i = -1; i <= (int8_t)stdAc::opmode_t::kLastOpmodeEnum; i++) {
String mode = IRac::opmodeToString((stdAc::opmode_t)i);
html += htmlOptionItem(mode, mode, (stdAc::opmode_t)i == def);
}
@ -972,7 +927,7 @@ String htmlSelectMode(const String name, const stdAc::opmode_t def) {
String htmlSelectFanspeed(const String name, const stdAc::fanspeed_t def) {
String html = "<select name='" + name + "'>";
for (int8_t i = 0; i <= 5; i++) {
for (int8_t i = 0; i <= (int8_t)stdAc::fanspeed_t::kLastFanspeedEnum; i++) {
String speed = IRac::fanspeedToString((stdAc::fanspeed_t)i);
html += htmlOptionItem(speed, speed, (stdAc::fanspeed_t)i == def);
}
@ -982,7 +937,7 @@ String htmlSelectFanspeed(const String name, const stdAc::fanspeed_t def) {
String htmlSelectSwingv(const String name, const stdAc::swingv_t def) {
String html = "<select name='" + name + "'>";
for (int8_t i = -1; i <= 5; i++) {
for (int8_t i = -1; i <= (int8_t)stdAc::swingv_t::kLastSwingvEnum; i++) {
String swing = IRac::swingvToString((stdAc::swingv_t)i);
html += htmlOptionItem(swing, swing, (stdAc::swingv_t)i == def);
}
@ -992,7 +947,7 @@ String htmlSelectSwingv(const String name, const stdAc::swingv_t def) {
String htmlSelectSwingh(const String name, const stdAc::swingh_t def) {
String html = "<select name='" + name + "'>";
for (int8_t i = -1; i <= 5; i++) {
for (int8_t i = -1; i <= (int8_t)stdAc::swingh_t::kLastSwinghEnum; i++) {
String swing = IRac::swinghToString((stdAc::swingh_t)i);
html += htmlOptionItem(swing, swing, (stdAc::swingh_t)i == def);
}
@ -1034,7 +989,8 @@ void handleAirCon(void) {
"<form method='POST' action='/aircon/set' enctype='multipart/form-data'>"
"<table style='width:33%'>"
"<tr><td>Protocol</td><td>" +
htmlSelectProtocol(KEY_PROTOCOL, climate.protocol) + "</td></tr>"
htmlSelectClimateProtocol(KEY_PROTOCOL, climate.protocol) +
"</td></tr>"
"<tr><td>Model</td><td>" + htmlSelectModel(KEY_MODEL, climate.model) +
"</td></tr>"
"<tr><td>Power</td><td>" + htmlSelectBool(KEY_POWER, climate.power) +
@ -1169,6 +1125,10 @@ uint32_t maxSketchSpace(void) {
#endif // defined(ESP8266)
}
#if REPORT_VCC
String vccToString(void) { return String(ESP.getVcc() / 1000.0); }
#endif // REPORT_VCC
// Info web page
void handleInfo(void) {
String html = htmlHeader(F("IR MQTT server info"));
@ -1223,6 +1183,11 @@ void handleInfo(void) {
"Off"
#endif // DEBUG
"<br>"
#if REPORT_VCC
"Vcc: ";
html += vccToString();
html += "V<br>"
#endif // REPORT_VCC
"</p>"
#if MQTT_ENABLE
"<h4>MQTT Information</h4>"
@ -1300,7 +1265,8 @@ void doRestart(const char* str, const bool serial_only) {
void handleReset(void) {
#if HTML_PASSWORD_ENABLE
if (!server.authenticate(HttpUsername, HttpPassword)) {
debug("Basic HTTP authentication failure for " + kUrlWipe);
debug(("Basic HTTP authentication failure for " +
String(kUrlWipe)).c_str());
return server.requestAuthentication();
}
#endif
@ -1329,7 +1295,8 @@ void handleReset(void) {
void handleReboot() {
#if HTML_PASSWORD_ENABLE
if (!server.authenticate(HttpUsername, HttpPassword)) {
debug("Basic HTTP authentication failure for " + kUrlReboot);
debug(("Basic HTTP authentication failure for " +
String(kUrlReboot)).c_str());
return server.requestAuthentication();
}
#endif
@ -2215,6 +2182,9 @@ void doBroadcast(TimerMs *timer, const uint32_t interval,
debug("Sending MQTT stat update broadcast.");
sendClimate(state, state, MqttClimateStat,
retain, true, false);
#if REPORT_VCC
sendString(MqttClimateStat + KEY_VCC, vccToString(), false);
#endif // REPORT_VCC
#if MQTT_CLIMATE_JSON
sendJsonState(state, MqttClimateStat + KEY_JSON);
#endif // MQTT_CLIMATE_JSON
@ -2370,29 +2340,22 @@ void sendMQTTDiscovery(const char *topic) {
"{"
"\"~\":\"" + MqttClimate + "\","
"\"name\":\"" + MqttHAName + "\","
"\"pow_cmd_t\":\"~" MQTT_CLIMATE "/" MQTT_CLIMATE_CMND "/" KEY_POWER "\","
"\"mode_cmd_t\":\"~" MQTT_CLIMATE "/" MQTT_CLIMATE_CMND "/" KEY_MODE "\","
"\"mode_stat_t\":\"~" MQTT_CLIMATE "/" MQTT_CLIMATE_STAT "/" KEY_MODE
"\","
"\"pow_cmd_t\":\"~/" MQTT_CLIMATE_CMND "/" KEY_POWER "\","
"\"mode_cmd_t\":\"~/" MQTT_CLIMATE_CMND "/" KEY_MODE "\","
"\"mode_stat_t\":\"~/" MQTT_CLIMATE_STAT "/" KEY_MODE "\","
"\"modes\":[\"off\",\"auto\",\"cool\",\"heat\",\"dry\",\"fan_only\"],"
"\"temp_cmd_t\":\"~" MQTT_CLIMATE "/" MQTT_CLIMATE_CMND "/" KEY_TEMP "\","
"\"temp_stat_t\":\"~" MQTT_CLIMATE "/" MQTT_CLIMATE_STAT "/" KEY_TEMP
"\","
"\"temp_cmd_t\":\"~/" MQTT_CLIMATE_CMND "/" KEY_TEMP "\","
"\"temp_stat_t\":\"~/" MQTT_CLIMATE_STAT "/" KEY_TEMP "\","
"\"min_temp\":\"16\","
"\"max_temp\":\"30\","
"\"temp_step\":\"1\","
"\"fan_mode_cmd_t\":\"~" MQTT_CLIMATE "/" MQTT_CLIMATE_CMND "/"
KEY_FANSPEED "\","
"\"fan_mode_stat_t\":\"~" MQTT_CLIMATE "/" MQTT_CLIMATE_STAT "/"
KEY_FANSPEED "\","
"\"fan_mode_cmd_t\":\"~/" MQTT_CLIMATE_CMND "/" KEY_FANSPEED "\","
"\"fan_mode_stat_t\":\"~/" MQTT_CLIMATE_STAT "/" KEY_FANSPEED "\","
"\"fan_modes\":[\"auto\",\"min\",\"low\",\"medium\",\"high\",\"max\"],"
"\"swing_mode_cmd_t\":\"~" MQTT_CLIMATE "/" MQTT_CLIMATE_CMND "/"
KEY_SWINGV "\","
"\"swing_mode_stat_t\":\"~" MQTT_CLIMATE "/" MQTT_CLIMATE_STAT "/"
KEY_SWINGV "\","
"\"swing_mode_cmd_t\":\"~/" MQTT_CLIMATE_CMND "/" KEY_SWINGV "\","
"\"swing_mode_stat_t\":\"~/" MQTT_CLIMATE_STAT "/" KEY_SWINGV "\","
"\"swing_modes\":["
"\"off\",\"auto\",\"highest\",\"high\",\"middle\",\"low\",\"lowest\""
"]"
"\"off\",\"auto\",\"highest\",\"high\",\"middle\",\"low\",\"lowest\"]"
"}").c_str())) {
mqttLog("MQTT climate discovery successful sent.");
hasDiscoveryBeenSent = true;

View File

@ -0,0 +1,18 @@
[platformio]
src_dir = .
[env]
lib_extra_dirs = ../../
lib_ldf_mode = deep+
lib_ignore = examples
build_flags =
[env:nodemcuv2]
platform = espressif8266
framework = arduino
board = nodemcuv2
[env:esp32dev]
platform = espressif32
framework = arduino
board = esp32dev

View File

@ -20,9 +20,11 @@
# Datatypes & Classes (KEYWORD1)
#######################################
IRAmcorAc KEYWORD1
IRArgoAC KEYWORD1
IRCoolixAC KEYWORD1
IRDaikin128 KEYWORD1
IRDaikin152 KEYWORD1
IRDaikin160 KEYWORD1
IRDaikin176 KEYWORD1
IRDaikin2 KEYWORD1
@ -77,6 +79,7 @@ _delayMicroseconds KEYWORD2
_matchGeneric KEYWORD2
_setMode KEYWORD2
_setTemp KEYWORD2
_validTolerance KEYWORD2
add KEYWORD2
addBoolToString KEYWORD2
addFanToString KEYWORD2
@ -84,6 +87,7 @@ addIntToString KEYWORD2
addLabeledString KEYWORD2
addModeToString KEYWORD2
addTempToString KEYWORD2
amcor KEYWORD2
argo KEYWORD2
bcdToUint8 KEYWORD2
begin KEYWORD2
@ -125,12 +129,14 @@ daikin2 KEYWORD2
daikin216 KEYWORD2
decode KEYWORD2
decodeAiwaRCT501 KEYWORD2
decodeAmcor KEYWORD2
decodeArgo KEYWORD2
decodeCOOLIX KEYWORD2
decodeCarrierAC KEYWORD2
decodeDISH KEYWORD2
decodeDaikin KEYWORD2
decodeDaikin128 KEYWORD2
decodeDaikin152 KEYWORD2
decodeDaikin160 KEYWORD2
decodeDaikin176 KEYWORD2
decodeDaikin2 KEYWORD2
@ -243,6 +249,7 @@ getFreshAir KEYWORD2
getFreshAirHigh KEYWORD2
getHealth KEYWORD2
getHold KEYWORD2
getHumid KEYWORD2
getIFeel KEYWORD2
getIon KEYWORD2
getIonFilter KEYWORD2
@ -269,6 +276,7 @@ getQuiet KEYWORD2
getRClevel KEYWORD2
getRaw KEYWORD2
getRoomTemp KEYWORD2
getSave KEYWORD2
getSensor KEYWORD2
getSensorTemp KEYWORD2
getSilent KEYWORD2
@ -293,6 +301,8 @@ getTempOffset KEYWORD2
getTempRaw KEYWORD2
getTime KEYWORD2
getTimer KEYWORD2
getTimerEnabled KEYWORD2
getTolerance KEYWORD2
getTurbo KEYWORD2
getUseCelsius KEYWORD2
getVane KEYWORD2
@ -357,12 +367,14 @@ samsung KEYWORD2
send KEYWORD2
sendAc KEYWORD2
sendAiwaRCT501 KEYWORD2
sendAmcor KEYWORD2
sendArgo KEYWORD2
sendCOOLIX KEYWORD2
sendCarrierAC KEYWORD2
sendDISH KEYWORD2
sendDaikin KEYWORD2
sendDaikin128 KEYWORD2
sendDaikin152 KEYWORD2
sendDaikin160 KEYWORD2
sendDaikin176 KEYWORD2
sendDaikin2 KEYWORD2
@ -455,6 +467,7 @@ setFreshAir KEYWORD2
setFreshAirHigh KEYWORD2
setHealth KEYWORD2
setHold KEYWORD2
setHumid KEYWORD2
setIFeel KEYWORD2
setIon KEYWORD2
setIonFilter KEYWORD2
@ -480,6 +493,7 @@ setPurify KEYWORD2
setQuiet KEYWORD2
setRaw KEYWORD2
setRoomTemp KEYWORD2
setSave KEYWORD2
setSensor KEYWORD2
setSensorTemp KEYWORD2
setSensorTempRaw KEYWORD2
@ -500,6 +514,8 @@ setTempRaw KEYWORD2
setTime KEYWORD2
setTimer KEYWORD2
setTimerActive KEYWORD2
setTimerEnabled KEYWORD2
setTolerance KEYWORD2
setTurbo KEYWORD2
setUnknownThreshold KEYWORD2
setUseCelsius KEYWORD2
@ -550,6 +566,7 @@ xorBytes KEYWORD2
AIWA_RC_T501 LITERAL1
AIWA_RC_T501_BITS LITERAL1
ALLOW_DELAY_CALLS LITERAL1
AMCOR LITERAL1
ARDB1 LITERAL1
ARGO LITERAL1
ARGO_COMMAND_LENGTH LITERAL1
@ -583,6 +600,7 @@ COOLIX LITERAL1
COOLIX_BITS LITERAL1
DAIKIN LITERAL1
DAIKIN128 LITERAL1
DAIKIN152 LITERAL1
DAIKIN160 LITERAL1
DAIKIN176 LITERAL1
DAIKIN2 LITERAL1
@ -601,11 +619,13 @@ DAIKIN_MAX_TEMP LITERAL1
DAIKIN_MIN_TEMP LITERAL1
DECODE_AC LITERAL1
DECODE_AIWA_RC_T501 LITERAL1
DECODE_AMCOR LITERAL1
DECODE_ARGO LITERAL1
DECODE_CARRIER_AC LITERAL1
DECODE_COOLIX LITERAL1
DECODE_DAIKIN LITERAL1
DECODE_DAIKIN128 LITERAL1
DECODE_DAIKIN152 LITERAL1
DECODE_DAIKIN160 LITERAL1
DECODE_DAIKIN176 LITERAL1
DECODE_DAIKIN2 LITERAL1
@ -889,11 +909,13 @@ SANYO_LC7461 LITERAL1
SANYO_LC7461_BITS LITERAL1
SANYO_SA8650B_BITS LITERAL1
SEND_AIWA_RC_T501 LITERAL1
SEND_AMCOR LITERAL1
SEND_ARGO LITERAL1
SEND_CARRIER_AC LITERAL1
SEND_COOLIX LITERAL1
SEND_DAIKIN LITERAL1
SEND_DAIKIN128 LITERAL1
SEND_DAIKIN152 LITERAL1
SEND_DAIKIN160 LITERAL1
SEND_DAIKIN176 LITERAL1
SEND_DAIKIN2 LITERAL1
@ -1001,6 +1023,42 @@ kAiwaRcT501PostBits LITERAL1
kAiwaRcT501PostData LITERAL1
kAiwaRcT501PreBits LITERAL1
kAiwaRcT501PreData LITERAL1
kAmcorAuto LITERAL1
kAmcorBits LITERAL1
kAmcorChecksumByte LITERAL1
kAmcorCool LITERAL1
kAmcorDefaultRepeat LITERAL1
kAmcorDry LITERAL1
kAmcorFan LITERAL1
kAmcorFanAuto LITERAL1
kAmcorFanMask LITERAL1
kAmcorFanMax LITERAL1
kAmcorFanMed LITERAL1
kAmcorFanMin LITERAL1
kAmcorFooterMark LITERAL1
kAmcorGap LITERAL1
kAmcorHdrMark LITERAL1
kAmcorHdrSpace LITERAL1
kAmcorHeat LITERAL1
kAmcorMaxMask LITERAL1
kAmcorMaxTemp LITERAL1
kAmcorMinTemp LITERAL1
kAmcorModeFanByte LITERAL1
kAmcorModeMask LITERAL1
kAmcorOneMark LITERAL1
kAmcorOneSpace LITERAL1
kAmcorPowerByte LITERAL1
kAmcorPowerMask LITERAL1
kAmcorPowerOff LITERAL1
kAmcorPowerOn LITERAL1
kAmcorSpecialByte LITERAL1
kAmcorStateLength LITERAL1
kAmcorTempByte LITERAL1
kAmcorTempMask LITERAL1
kAmcorTolerance LITERAL1
kAmcorVentMask LITERAL1
kAmcorZeroMark LITERAL1
kAmcorZeroSpace LITERAL1
kArgoAuto LITERAL1
kArgoBitMark LITERAL1
kArgoBits LITERAL1
@ -1151,6 +1209,17 @@ kDaikin128SectionLength LITERAL1
kDaikin128Sections LITERAL1
kDaikin128StateLength LITERAL1
kDaikin128ZeroSpace LITERAL1
kDaikin152BitMark LITERAL1
kDaikin152Bits LITERAL1
kDaikin152DefaultRepeat LITERAL1
kDaikin152Freq LITERAL1
kDaikin152Gap LITERAL1
kDaikin152HdrMark LITERAL1
kDaikin152HdrSpace LITERAL1
kDaikin152LeaderBits LITERAL1
kDaikin152OneSpace LITERAL1
kDaikin152StateLength LITERAL1
kDaikin152ZeroSpace LITERAL1
kDaikin160BitMark LITERAL1
kDaikin160Bits LITERAL1
kDaikin160ByteFan LITERAL1
@ -1555,6 +1624,13 @@ kGreeSwingMiddleUp LITERAL1
kGreeSwingPosMask LITERAL1
kGreeSwingUp LITERAL1
kGreeSwingUpAuto LITERAL1
kGreeTempMask LITERAL1
kGreeTimer1Mask LITERAL1
kGreeTimerEnabledBit LITERAL1
kGreeTimerHalfHrBit LITERAL1
kGreeTimerHoursMask LITERAL1
kGreeTimerMax LITERAL1
kGreeTimerTensHrMask LITERAL1
kGreeTurboMask LITERAL1
kGreeWiFiMask LITERAL1
kGreeXfanMask LITERAL1
@ -1749,6 +1825,10 @@ kLasertagMinSamples LITERAL1
kLasertagTick LITERAL1
kLasertagTolerance LITERAL1
kLastDecodeType LITERAL1
kLastFanspeedEnum LITERAL1
kLastOpmodeEnum LITERAL1
kLastSwinghEnum LITERAL1
kLastSwingvEnum LITERAL1
kLeft LITERAL1
kLeftMax LITERAL1
kLegoPfBitMark LITERAL1
@ -2420,12 +2500,15 @@ kTecoGap LITERAL1
kTecoHdrMark LITERAL1
kTecoHdrSpace LITERAL1
kTecoHeat LITERAL1
kTecoHumid LITERAL1
kTecoLight LITERAL1
kTecoMaxTemp LITERAL1
kTecoMinTemp LITERAL1
kTecoModeMask LITERAL1
kTecoOneSpace LITERAL1
kTecoPower LITERAL1
kTecoReset LITERAL1
kTecoSave LITERAL1
kTecoSleep LITERAL1
kTecoSwing LITERAL1
kTecoTempMask LITERAL1
@ -2483,6 +2566,7 @@ kTrotecStateLength LITERAL1
kTrotecTimerBit LITERAL1
kTrotecZeroSpace LITERAL1
kUnknownThreshold LITERAL1
kUseDefTol LITERAL1
kVestelAcAuto LITERAL1
kVestelAcBitMark LITERAL1
kVestelAcBits LITERAL1
@ -2604,3 +2688,4 @@ kWhynterOneSpaceTicks LITERAL1
kWhynterTick LITERAL1
kWhynterZeroSpace LITERAL1
kWhynterZeroSpaceTicks LITERAL1
kWide LITERAL1

View File

@ -1,6 +1,6 @@
{
"name": "IRremoteESP8266",
"version": "2.6.4",
"version": "2.6.5",
"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.6.4
version=2.6.5
author=David Conran, Sebastien Warin, Mark Szabo, Ken Shirriff
maintainer=Mark Szabo, David Conran, Sebastien Warin, Roi Dayan, Massimiliano Pinto
sentence=Send and receive infrared signals with multiple protocols (ESP8266/ESP32)

View File

@ -15,6 +15,7 @@
#include "IRsend.h"
#include "IRremoteESP8266.h"
#include "IRutils.h"
#include "ir_Amcor.h"
#include "ir_Argo.h"
#include "ir_Coolix.h"
#include "ir_Daikin.h"
@ -46,7 +47,10 @@ IRac::IRac(const uint16_t pin, const bool inverted, const bool use_modulation) {
// Is the given protocol supported by the IRac class?
bool IRac::isProtocolSupported(const decode_type_t protocol) {
switch (protocol) {
#if SEND_ARGO
#if SEND_AMCOR
case decode_type_t::AMCOR:
#endif
#if SEND_AMCOR
case decode_type_t::ARGO:
#endif
#if SEND_COOLIX
@ -140,6 +144,27 @@ bool IRac::isProtocolSupported(const decode_type_t protocol) {
}
}
#if SEND_AMCOR
void IRac::amcor(IRAmcorAc *ac,
const bool on, const stdAc::opmode_t mode, const float degrees,
const stdAc::fanspeed_t fan) {
ac->setPower(on);
ac->setMode(ac->convertMode(mode));
ac->setTemp(degrees);
ac->setFan(ac->convertFan(fan));
// No Swing setting available.
// No Quiet setting available.
// No Light setting available.
// No Filter setting available.
// No Turbo setting available.
// No Economy setting available.
// No Clean setting available.
// No Beep setting available.
// No Sleep setting available.
ac->send();
}
#endif // SEND_AMCOR
#if SEND_ARGO
void IRac::argo(IRArgoAC *ac,
const bool on, const stdAc::opmode_t mode, const float degrees,
@ -400,6 +425,7 @@ void IRac::fujitsu(IRFujitsuAC *ac, const fujitsu_ac_remote_model_t model,
// No Beep setting available.
// No Sleep setting available.
// No Clock setting available.
ac->on(); // Ref: Issue #860
} else {
// Off is special case/message. We don't need to send other messages.
ac->off();
@ -801,7 +827,7 @@ void IRac::tcl112(IRTcl112Ac *ac,
void IRac::teco(IRTecoAc *ac,
const bool on, const stdAc::opmode_t mode, const float degrees,
const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv,
const int16_t sleep) {
const bool light, const int16_t sleep) {
ac->setPower(on);
ac->setMode(ac->convertMode(mode));
ac->setTemp(degrees);
@ -810,7 +836,7 @@ void IRac::teco(IRTecoAc *ac,
// No Horizontal swing setting available.
// No Quiet setting available.
// No Turbo setting available.
// No Light setting available.
ac->setLight(light);
// No Filter setting available.
// No Clean setting available.
// No Beep setting available.
@ -1014,6 +1040,14 @@ bool IRac::sendAc(const decode_type_t vendor, const int16_t model,
if (mode == stdAc::opmode_t::kOff) on = false;
// Per vendor settings & setup.
switch (vendor) {
#if SEND_AMCOR
case AMCOR:
{
IRAmcorAc ac(_pin, _inverted, _modulation);
amcor(&ac, on, mode, degC, fan);
break;
}
#endif // SEND_AMCOR
#if SEND_ARGO
case ARGO:
{
@ -1247,7 +1281,7 @@ bool IRac::sendAc(const decode_type_t vendor, const int16_t model,
{
IRTecoAc ac(_pin, _inverted, _modulation);
ac.begin();
teco(&ac, on, mode, degC, fan, swingv, sleep);
teco(&ac, on, mode, degC, fan, swingv, light, sleep);
break;
}
#endif // SEND_TECO
@ -1413,17 +1447,28 @@ stdAc::swingh_t IRac::strToSwingH(const char *str,
!strcasecmp(str, "MAXRIGHT") || !strcasecmp(str, "MAX RIGHT") ||
!strcasecmp(str, "FARRIGHT") || !strcasecmp(str, "FAR RIGHT"))
return stdAc::swingh_t::kRightMax;
else if (!strcasecmp(str, "WIDE"))
return stdAc::swingh_t::kWide;
else
return def;
}
// Assumes str is the model or an integer >= 1.
int16_t IRac::strToModel(const char *str, const int16_t def) {
// Gree
if (!strcasecmp(str, "YAW1F")) {
return gree_ac_remote_model_t::YAW1F;
} else if (!strcasecmp(str, "YBOFB")) {
return gree_ac_remote_model_t::YBOFB;
// Fujitsu A/C models
if (!strcasecmp(str, "ARRAH2E")) {
} else if (!strcasecmp(str, "ARRAH2E")) {
return fujitsu_ac_remote_model_t::ARRAH2E;
} else if (!strcasecmp(str, "ARDB1")) {
return fujitsu_ac_remote_model_t::ARDB1;
} else if (!strcasecmp(str, "ARREB1E")) {
return fujitsu_ac_remote_model_t::ARREB1E;
} else if (!strcasecmp(str, "ARJW2")) {
return fujitsu_ac_remote_model_t::ARJW2;
// Panasonic A/C families
} else if (!strcasecmp(str, "LKE") || !strcasecmp(str, "PANASONICLKE")) {
return panasonic_ac_remote_model_t::kPanasonicLke;
@ -1542,6 +1587,8 @@ String IRac::swinghToString(const stdAc::swingh_t swingh) {
return F("right");
case stdAc::swingh_t::kRightMax:
return F("rightmax");
case stdAc::swingh_t::kWide:
return F("wide");
default:
return F("unknown");
}
@ -1555,6 +1602,13 @@ namespace IRAcUtils {
// A string with the human description of the A/C message. "" if we can't.
String resultAcToString(const decode_results * const result) {
switch (result->decode_type) {
#if DECODE_AMCOR
case decode_type_t::AMCOR: {
IRAmcorAc ac(0);
ac.setRaw(result->state);
return ac.toString();
}
#endif // DECODE_AMCOR
#if DECODE_ARGO
case decode_type_t::ARGO: {
IRArgoAC ac(0);
@ -1784,6 +1838,14 @@ namespace IRAcUtils {
const stdAc::state_t *prev) {
if (decode == NULL || result == NULL) return false; // Safety check.
switch (decode->decode_type) {
#if DECODE_AMCOR
case decode_type_t::AMCOR: {
IRAmcorAc ac(kGpioUnused);
ac.setRaw(decode->state);
*result = ac.toCommon();
break;
}
#endif // DECODE_AMCOR
#if DECODE_ARGO
case decode_type_t::ARGO: {
IRArgoAC ac(kGpioUnused);

View File

@ -7,6 +7,7 @@
#include <Arduino.h>
#endif
#include "IRremoteESP8266.h"
#include "ir_Amcor.h"
#include "ir_Argo.h"
#include "ir_Coolix.h"
#include "ir_Daikin.h"
@ -73,6 +74,11 @@ class IRac {
uint16_t _pin;
bool _inverted;
bool _modulation;
#if SEND_AMCOR
void amcor(IRAmcorAc *ac,
const bool on, const stdAc::opmode_t mode, const float degrees,
const stdAc::fanspeed_t fan);
#endif // SEND_AMCOR
#if SEND_ARGO
void argo(IRArgoAC *ac,
const bool on, const stdAc::opmode_t mode, const float degrees,
@ -260,7 +266,7 @@ void electra(IRElectraAc *ac,
void teco(IRTecoAc *ac,
const bool on, const stdAc::opmode_t mode, const float degrees,
const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv,
const int16_t sleep = -1);
const bool light, const int16_t sleep = -1);
#endif // SEND_TECO
#if SEND_TOSHIBA_AC
void toshiba(IRToshibaAC *ac,

View File

@ -182,6 +182,7 @@ IRrecv::IRrecv(const uint16_t recvpin, const uint16_t bufsize,
#if DECODE_HASH
_unknown_threshold = kUnknownThreshold;
#endif // DECODE_HASH
_tolerance = kTolerance;
}
// Class destructor
@ -297,6 +298,15 @@ void IRrecv::setUnknownThreshold(const uint16_t length) {
}
#endif // DECODE_HASH
// Set the base tolerance percentage for matching incoming IR messages.
void IRrecv::setTolerance(const uint8_t percent) {
_tolerance = std::min(percent, (uint8_t)100);
}
// Get the base tolerance percentage for matching incoming IR messages.
uint8_t IRrecv::getTolerance(void) { return _tolerance; }
// Decodes the received IR message.
// If the interrupt state is saved, we will immediately resume waiting
// for the next IR message to avoid missing messages.
@ -651,6 +661,14 @@ bool IRrecv::decode(decode_results *results, irparams_t *save) {
DPRINTLN("Attempting Daikin128 decode");
if (decodeDaikin128(results)) return true;
#endif // DECODE_DAIKIN128
#if DECODE_AMCOR
DPRINTLN("Attempting Amcor decode");
if (decodeAmcor(results)) return true;
#endif // DECODE_AMCOR
#if DECODE_DAIKIN152
DPRINTLN("Attempting Daikin152 decode");
if (decodeDaikin152(results)) return true;
#endif // DECODE_DAIKIN152
#if DECODE_HASH
// decodeHash returns a hash on any input.
// Thus, it needs to be last in the list.
@ -665,6 +683,11 @@ bool IRrecv::decode(decode_results *results, irparams_t *save) {
return false;
}
// Convert the tolerance percentage into something valid.
uint8_t IRrecv::_validTolerance(const uint8_t percentage) {
return (percentage > 100) ? _tolerance : percentage;
}
// Calculate the lower bound of the nr. of ticks.
//
// Args:
@ -673,10 +696,12 @@ bool IRrecv::decode(decode_results *results, irparams_t *save) {
// delta: A non-scaling amount to reduce usecs by.
// Returns:
// Nr. of ticks.
uint32_t IRrecv::ticksLow(uint32_t usecs, uint8_t tolerance, uint16_t delta) {
uint32_t IRrecv::ticksLow(const uint32_t usecs, const uint8_t tolerance,
const uint16_t delta) {
// max() used to ensure the result can't drop below 0 before the cast.
return ((uint32_t)std::max(
(int32_t)(usecs * (1.0 - tolerance / 100.0) - delta), 0));
(int32_t)(usecs * (1.0 - _validTolerance(tolerance) / 100.0) - delta),
0));
}
// Calculate the upper bound of the nr. of ticks.
@ -687,8 +712,10 @@ uint32_t IRrecv::ticksLow(uint32_t usecs, uint8_t tolerance, uint16_t delta) {
// delta: A non-scaling amount to increase usecs by.
// Returns:
// Nr. of ticks.
uint32_t IRrecv::ticksHigh(uint32_t usecs, uint8_t tolerance, uint16_t delta) {
return ((uint32_t)(usecs * (1.0 + tolerance / 100.0)) + 1 + delta);
uint32_t IRrecv::ticksHigh(const uint32_t usecs, const uint8_t tolerance,
const uint16_t delta) {
return ((uint32_t)(usecs * (1.0 + _validTolerance(tolerance) / 100.0)) + 1 +
delta);
}
// Check if we match a pulse(measured) with the desired within
@ -836,7 +863,7 @@ bool IRrecv::matchSpace(uint32_t measured, uint32_t desired, uint8_t tolerance,
// Compare two tick values, returning 0 if newval is shorter,
// 1 if newval is equal, and 2 if newval is longer
// Use a tolerance of 20%
int16_t IRrecv::compare(uint16_t oldval, uint16_t newval) {
uint16_t IRrecv::compare(const uint16_t oldval, const uint16_t newval) {
if (newval < oldval * 0.8)
return 0;
else if (oldval < newval * 0.8)
@ -859,7 +886,7 @@ bool IRrecv::decodeHash(decode_results *results) {
// however it is left this way for compatibility with previously captured
// values.
for (uint16_t i = 1; i < results->rawlen - 2; i++) {
int16_t value = compare(results->rawbuf[i], results->rawbuf[i + 2]);
uint16_t value = compare(results->rawbuf[i], results->rawbuf[i + 2]);
// Add value into the hash
hash = (hash * kFnvPrime32) ^ value;
}
@ -883,7 +910,7 @@ bool IRrecv::decodeHash(decode_results *results) {
// onespace: Nr. of uSeconds in an expected space signal for a '1' bit.
// zeromark: Nr. of uSeconds in an expected mark signal for a '0' bit.
// zerospace: Nr. of uSeconds in an expected space signal for a '0' bit.
// tolerance: Percentage error margin to allow. (Def: kTolerance)
// tolerance: Percentage error margin to allow. (Def: kUseDefTol)
// excess: Nr. of useconds. (Def: kMarkExcess)
// MSBfirst: Bit order to save the data in. (Def: true)
// Returns:
@ -928,7 +955,7 @@ match_result_t IRrecv::matchData(
// onespace: Nr. of uSeconds in an expected space signal for a '1' bit.
// zeromark: Nr. of uSeconds in an expected mark signal for a '0' bit.
// zerospace: Nr. of uSeconds in an expected space signal for a '0' bit.
// tolerance: Percentage error margin to allow. (Def: kTolerance)
// tolerance: Percentage error margin to allow. (Def: kUseDefTol)
// excess: Nr. of useconds. (Def: kMarkExcess)
// MSBfirst: Bit order to save the data in. (Def: true)
// Returns:
@ -975,7 +1002,7 @@ uint16_t IRrecv::matchBytes(volatile uint16_t *data_ptr, uint8_t *result_ptr,
// footermark: Nr. of uSeconds for the expected footer mark signal.
// footerspace: Nr. of uSeconds for the expected footer space/gap signal.
// atleast: Is the match on the footerspace a matchAtLeast or matchSpace?
// tolerance: Percentage error margin to allow. (Def: kTolerance)
// tolerance: Percentage error margin to allow. (Def: kUseDefTol)
// excess: Nr. of useconds. (Def: kMarkExcess)
// MSBfirst: Bit order to save the data in. (Def: true)
// Returns:
@ -1074,7 +1101,7 @@ uint16_t IRrecv::_matchGeneric(volatile uint16_t *data_ptr,
// footermark: Nr. of uSeconds for the expected footer mark signal.
// footerspace: Nr. of uSeconds for the expected footer space/gap signal.
// atleast: Is the match on the footerspace a matchAtLeast or matchSpace?
// tolerance: Percentage error margin to allow. (Def: kTolerance)
// tolerance: Percentage error margin to allow. (Def: kUseDefTol)
// excess: Nr. of useconds. (Def: kMarkExcess)
// MSBfirst: Bit order to save the data in. (Def: true)
// Returns:
@ -1121,7 +1148,7 @@ uint16_t IRrecv::matchGeneric(volatile uint16_t *data_ptr,
// footermark: Nr. of uSeconds for the expected footer mark signal.
// footerspace: Nr. of uSeconds for the expected footer space/gap signal.
// atleast: Is the match on the footerspace a matchAtLeast or matchSpace?
// tolerance: Percentage error margin to allow. (Def: kTolerance)
// tolerance: Percentage error margin to allow. (Def: kUseDefTol)
// excess: Nr. of useconds. (Def: kMarkExcess)
// MSBfirst: Bit order to save the data in. (Def: true)
// Returns:

View File

@ -33,6 +33,7 @@ const uint8_t kMarkState = 3;
const uint8_t kSpaceState = 4;
const uint8_t kStopState = 5;
const uint8_t kTolerance = 25; // default percent tolerance in measurements.
const uint8_t kUseDefTol = 255; // Indicate to use the class default tolerance.
const uint16_t kRawTick = 2; // Capture tick to uSec factor.
#define RAWTICK kRawTick // Deprecated. For legacy user code support only.
// How long (ms) before we give up wait for more data?
@ -122,6 +123,8 @@ class IRrecv {
const bool save_buffer = false); // Constructor
#endif // ESP32
~IRrecv(void); // Destructor
void setTolerance(const uint8_t percent = kTolerance);
uint8_t getTolerance(void);
bool decode(decode_results *results, irparams_t *save = NULL);
void enableIRIn(const bool pullup = false);
void disableIRIn(void);
@ -130,32 +133,40 @@ class IRrecv {
#if DECODE_HASH
void setUnknownThreshold(const uint16_t length);
#endif
static bool match(uint32_t measured, uint32_t desired,
uint8_t tolerance = kTolerance, uint16_t delta = 0);
static bool matchMark(uint32_t measured, uint32_t desired,
uint8_t tolerance = kTolerance,
int16_t excess = kMarkExcess);
static bool matchSpace(uint32_t measured, uint32_t desired,
uint8_t tolerance = kTolerance,
int16_t excess = kMarkExcess);
bool match(const uint32_t measured, const uint32_t desired,
const uint8_t tolerance = kUseDefTol,
const uint16_t delta = 0);
bool matchMark(const uint32_t measured, const uint32_t desired,
const uint8_t tolerance = kUseDefTol,
const int16_t excess = kMarkExcess);
bool matchSpace(const uint32_t measured, const uint32_t desired,
const uint8_t tolerance = kUseDefTol,
const int16_t excess = kMarkExcess);
#ifndef UNIT_TEST
private:
#endif
irparams_t *irparams_save;
uint8_t _tolerance;
#if defined(ESP32)
uint8_t _timer_num;
#endif // defined(ESP32)
#if DECODE_HASH
uint16_t _unknown_threshold;
#endif
// These are called by decode
uint8_t _validTolerance(const uint8_t percentage);
void copyIrParams(volatile irparams_t *src, irparams_t *dst);
int16_t compare(uint16_t oldval, uint16_t newval);
static uint32_t ticksLow(uint32_t usecs, uint8_t tolerance = kTolerance,
uint16_t delta = 0);
static uint32_t ticksHigh(uint32_t usecs, uint8_t tolerance = kTolerance,
uint16_t delta = 0);
bool matchAtLeast(uint32_t measured, uint32_t desired,
uint8_t tolerance = kTolerance, uint16_t delta = 0);
uint16_t compare(const uint16_t oldval, const uint16_t newval);
uint32_t ticksLow(const uint32_t usecs,
const uint8_t tolerance = kUseDefTol,
const uint16_t delta = 0);
uint32_t ticksHigh(const uint32_t usecs,
const uint8_t tolerance = kUseDefTol,
const uint16_t delta = 0);
bool matchAtLeast(const uint32_t measured, const uint32_t desired,
const uint8_t tolerance = kUseDefTol,
const uint16_t delta = 0);
uint16_t _matchGeneric(volatile uint16_t *data_ptr,
uint64_t *result_bits_ptr,
uint8_t *result_ptr,
@ -171,20 +182,20 @@ class IRrecv {
const uint16_t footermark,
const uint32_t footerspace,
const bool atleast = false,
const uint8_t tolerance = kTolerance,
const uint8_t tolerance = kUseDefTol,
const int16_t excess = kMarkExcess,
const bool MSBfirst = true);
match_result_t matchData(volatile uint16_t *data_ptr, const uint16_t nbits,
const uint16_t onemark, const uint32_t onespace,
const uint16_t zeromark, const uint32_t zerospace,
const uint8_t tolerance = kTolerance,
const uint8_t tolerance = kUseDefTol,
const int16_t excess = kMarkExcess,
const bool MSBfirst = true);
uint16_t matchBytes(volatile uint16_t *data_ptr, uint8_t *result_ptr,
const uint16_t remaining, const uint16_t nbytes,
const uint16_t onemark, const uint32_t onespace,
const uint16_t zeromark, const uint32_t zerospace,
const uint8_t tolerance = kTolerance,
const uint8_t tolerance = kUseDefTol,
const int16_t excess = kMarkExcess,
const bool MSBfirst = true);
uint16_t matchGeneric(volatile uint16_t *data_ptr,
@ -195,7 +206,7 @@ class IRrecv {
const uint16_t zeromark, const uint32_t zerospace,
const uint16_t footermark, const uint32_t footerspace,
const bool atleast = false,
const uint8_t tolerance = kTolerance,
const uint8_t tolerance = kUseDefTol,
const int16_t excess = kMarkExcess,
const bool MSBfirst = true);
uint16_t matchGeneric(volatile uint16_t *data_ptr, uint8_t *result_ptr,
@ -206,7 +217,7 @@ class IRrecv {
const uint16_t footermark,
const uint32_t footerspace,
const bool atleast = false,
const uint8_t tolerance = kTolerance,
const uint8_t tolerance = kUseDefTol,
const int16_t excess = kMarkExcess,
const bool MSBfirst = true);
bool decodeHash(decode_results *results);
@ -249,7 +260,7 @@ class IRrecv {
#endif
#if (DECODE_RC5 || DECODE_R6 || DECODE_LASERTAG || DECODE_MWM)
int16_t getRClevel(decode_results *results, uint16_t *offset, uint16_t *used,
uint16_t bitTime, uint8_t tolerance = kTolerance,
uint16_t bitTime, uint8_t tolerance = kUseDefTol,
int16_t excess = kMarkExcess, uint16_t delta = 0,
uint8_t maxwidth = 3);
#endif
@ -348,6 +359,11 @@ class IRrecv {
const uint16_t nbits = kDaikin128Bits,
const bool strict = true);
#endif // DECODE_DAIKIN128
#if DECODE_DAIKIN152
bool decodeDaikin152(decode_results *results,
const uint16_t nbits = kDaikin152Bits,
const bool strict = true);
#endif // DECODE_DAIKIN152
#if DECODE_DAIKIN160
bool decodeDaikin160(decode_results *results,
const uint16_t nbits = kDaikin160Bits,
@ -474,6 +490,11 @@ bool decodeNeoclima(decode_results *results,
const uint16_t nbits = kNeoclimaBits,
const bool strict = true);
#endif // DECODE_NEOCLIMA
#if DECODE_AMCOR
bool decodeAmcor(decode_results *results,
const uint16_t nbits = kAmcorBits,
const bool strict = true);
#endif // DECODE_AMCOR
};
#endif // IRRECV_H_

View File

@ -51,13 +51,14 @@
#endif // UNIT_TEST
// Library Version
#define _IRREMOTEESP8266_VERSION_ "2.6.4"
#define _IRREMOTEESP8266_VERSION_ "2.6.5"
// Supported IR protocols
// Each protocol you include costs memory and, during decode, costs time
// Disable (set to false) all the protocols you do not need/want!
// The Air Conditioner protocols are the most expensive memory-wise.
//
/*
#if defined(FIRMWARE_IR) || defined(FIRMWARE_IR_CUSTOM) // full IR protocols
#define DECODE_HASH true // Semi-unique code for unknown messages
#define SEND_RAW true
@ -247,7 +248,14 @@
#define DECODE_DAIKIN128 true
#define SEND_DAIKIN128 true
*/
#define DECODE_AMCOR true
#define SEND_AMCOR true
#define DECODE_DAIKIN152 true
#define SEND_DAIKIN152 true
#else // defined(FIRMWARE_IR) || defined(FIRMWARE_IR_CUSTOM) // full IR protocols
// Tasmota supported protocols (less protocols is less code size)
#define DECODE_HASH true // Semi-unique code for unknown messages
@ -440,6 +448,8 @@
#define DECODE_DAIKIN128 false
#define SEND_DAIKIN128 true
#endif // defined(FIRMWARE_IR) || defined(FIRMWARE_IR_CUSTOM) // full IR protocols
#if (DECODE_ARGO || DECODE_DAIKIN || DECODE_FUJITSU_AC || DECODE_GREE || \
DECODE_KELVINATOR || DECODE_MITSUBISHI_AC || DECODE_TOSHIBA_AC || \
DECODE_TROTEC || DECODE_HAIER_AC || DECODE_HITACHI_AC || \
@ -448,7 +458,8 @@
DECODE_PANASONIC_AC || DECODE_MWM || DECODE_DAIKIN2 || \
DECODE_VESTEL_AC || DECODE_TCL112AC || DECODE_MITSUBISHIHEAVY || \
DECODE_DAIKIN216 || DECODE_SHARP_AC || DECODE_DAIKIN160 || \
DECODE_NEOCLIMA || DECODE_DAIKIN176 || DECODE_DAIKIN128)
DECODE_NEOCLIMA || DECODE_DAIKIN176 || DECODE_DAIKIN128 || \
DECODE_AMCOR || DECODE_DAIKIN152)
#define DECODE_AC true // We need some common infrastructure for decoding A/Cs.
#else
#define DECODE_AC false // We don't need that infrastructure.
@ -536,8 +547,10 @@ enum decode_type_t {
NEOCLIMA,
DAIKIN176,
DAIKIN128,
AMCOR,
DAIKIN152, // 70
// Add new entries before this one, and update it to point to the last entry.
kLastDecodeType = DAIKIN128,
kLastDecodeType = DAIKIN152,
};
// Message lengths & required repeat values
@ -546,6 +559,9 @@ const uint16_t kSingleRepeat = 1;
const uint16_t kAiwaRcT501Bits = 15;
const uint16_t kAiwaRcT501MinRepeats = kSingleRepeat;
const uint16_t kAmcorStateLength = 8;
const uint16_t kAmcorBits = kAmcorStateLength * 8;
const uint16_t kAmcorDefaultRepeat = kSingleRepeat;
const uint16_t kArgoStateLength = 12;
const uint16_t kArgoBits = kArgoStateLength * 8;
const uint16_t kArgoDefaultRepeat = kNoRepeat;
@ -567,6 +583,9 @@ const uint16_t kDaikin160DefaultRepeat = kNoRepeat;
const uint16_t kDaikin128StateLength = 16;
const uint16_t kDaikin128Bits = kDaikin128StateLength * 8;
const uint16_t kDaikin128DefaultRepeat = kNoRepeat;
const uint16_t kDaikin152StateLength = 19;
const uint16_t kDaikin152Bits = kDaikin152StateLength * 8;
const uint16_t kDaikin152DefaultRepeat = kNoRepeat;
const uint16_t kDaikin176StateLength = 22;
const uint16_t kDaikin176Bits = kDaikin176StateLength * 8;
const uint16_t kDaikin176DefaultRepeat = kNoRepeat;

View File

@ -500,6 +500,7 @@ uint16_t IRsend::minRepeats(const decode_type_t protocol) {
switch (protocol) {
// Single repeats
case AIWA_RC_T501:
case AMCOR:
case COOLIX:
case GICABLE:
case INAX:
@ -574,6 +575,7 @@ uint16_t IRsend::defaultBits(const decode_type_t protocol) {
case MAGIQUEST:
case VESTEL_AC:
return 56;
case AMCOR:
case PIONEER:
return 64;
case ARGO:
@ -582,6 +584,8 @@ uint16_t IRsend::defaultBits(const decode_type_t protocol) {
return kDaikinBits;
case DAIKIN128:
return kDaikin128Bits;
case DAIKIN152:
return kDaikin152Bits;
case DAIKIN160:
return kDaikin160Bits;
case DAIKIN176:
@ -841,6 +845,11 @@ bool IRsend::send(const decode_type_t type, const uint64_t data,
bool IRsend::send(const decode_type_t type, const unsigned char *state,
const uint16_t nbytes) {
switch (type) {
#if SEND_AMCOR
case AMCOR:
sendAmcor(state, nbytes);
break;
#endif
#if SEND_ARGO
case ARGO:
sendArgo(state, nbytes);
@ -856,6 +865,11 @@ bool IRsend::send(const decode_type_t type, const unsigned char *state,
sendDaikin128(state, nbytes);
break;
#endif // SEND_DAIKIN128
#if SEND_DAIKIN152
case DAIKIN152:
sendDaikin152(state, nbytes);
break;
#endif // SEND_DAIKIN152
#if SEND_DAIKIN160
case DAIKIN160:
sendDaikin160(state, nbytes);

View File

@ -49,6 +49,8 @@ namespace stdAc {
kHeat = 2,
kDry = 3,
kFan = 4,
// Add new entries before this one, and update it to point to the last entry
kLastOpmodeEnum = kFan,
};
enum class fanspeed_t {
@ -58,6 +60,8 @@ namespace stdAc {
kMedium = 3,
kHigh = 4,
kMax = 5,
// Add new entries before this one, and update it to point to the last entry
kLastFanspeedEnum = kMax,
};
enum class swingv_t {
@ -68,6 +72,8 @@ namespace stdAc {
kMiddle = 3,
kLow = 4,
kLowest = 5,
// Add new entries before this one, and update it to point to the last entry
kLastSwingvEnum = kLowest,
};
enum class swingh_t {
@ -78,6 +84,9 @@ namespace stdAc {
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.
@ -311,6 +320,11 @@ class IRsend {
const uint16_t nbytes = kDaikin128StateLength,
const uint16_t repeat = kDaikin128DefaultRepeat);
#endif // SEND_DAIKIN128
#if SEND_DAIKIN152
void sendDaikin152(const unsigned char data[],
const uint16_t nbytes = kDaikin152StateLength,
const uint16_t repeat = kDaikin152DefaultRepeat);
#endif // SEND_DAIKIN152
#if SEND_DAIKIN160
void sendDaikin160(const unsigned char data[],
const uint16_t nbytes = kDaikin160StateLength,
@ -464,6 +478,11 @@ class IRsend {
const uint16_t nbytes = kNeoclimaStateLength,
const uint16_t repeat = kNeoclimaMinRepeat);
#endif // SEND_NEOCLIMA
#if SEND_AMCOR
void sendAmcor(const unsigned char data[],
const uint16_t nbytes = kAmcorStateLength,
const uint16_t repeat = kAmcorDefaultRepeat);
#endif // SEND_AMCOR
protected:

View File

@ -95,6 +95,8 @@ decode_type_t strToDecodeType(const char * const str) {
return decode_type_t::UNUSED;
else if (!strcasecmp(str, "AIWA_RC_T501"))
return decode_type_t::AIWA_RC_T501;
else if (!strcasecmp(str, "AMCOR"))
return decode_type_t::AMCOR;
else if (!strcasecmp(str, "ARGO"))
return decode_type_t::ARGO;
else if (!strcasecmp(str, "CARRIER_AC"))
@ -105,6 +107,8 @@ decode_type_t strToDecodeType(const char * const str) {
return decode_type_t::DAIKIN;
else if (!strcasecmp(str, "DAIKIN128"))
return decode_type_t::DAIKIN128;
else if (!strcasecmp(str, "DAIKIN152"))
return decode_type_t::DAIKIN152;
else if (!strcasecmp(str, "DAIKIN160"))
return decode_type_t::DAIKIN160;
else if (!strcasecmp(str, "DAIKIN176"))
@ -254,6 +258,9 @@ String typeToString(const decode_type_t protocol, const bool isRepeat) {
case AIWA_RC_T501:
result = F("AIWA_RC_T501");
break;
case AMCOR:
result = F("AMCOR");
break;
case ARGO:
result = F("ARGO");
break;
@ -269,6 +276,9 @@ String typeToString(const decode_type_t protocol, const bool isRepeat) {
case DAIKIN128:
result = F("DAIKIN128");
break;
case DAIKIN152:
result = F("DAIKIN152");
break;
case DAIKIN160:
result = F("DAIKIN160");
break;
@ -467,9 +477,11 @@ String typeToString(const decode_type_t protocol, const bool isRepeat) {
// Does the given protocol use a complex state as part of the decode?
bool hasACState(const decode_type_t protocol) {
switch (protocol) {
case AMCOR:
case ARGO:
case DAIKIN:
case DAIKIN128:
case DAIKIN152:
case DAIKIN160:
case DAIKIN176:
case DAIKIN2:

View File

@ -0,0 +1,326 @@
// Copyright 2019 David Conran
// Supports:
// Brand: Amcor, Model: ADR-853H A/C
// Brand: Amcor, Model: TAC-495 remote
// Brand: Amcor, Model: TAC-444 remote
#include "ir_Amcor.h"
#include <algorithm>
#include "IRrecv.h"
#include "IRsend.h"
#include "IRutils.h"
// Constants
// Ref:
// https://github.com/crankyoldgit/IRremoteESP8266/issues/385
const uint16_t kAmcorHdrMark = 8200;
const uint16_t kAmcorHdrSpace = 4200;
const uint16_t kAmcorOneMark = 1500;
const uint16_t kAmcorZeroMark = 600;
const uint16_t kAmcorOneSpace = kAmcorZeroMark;
const uint16_t kAmcorZeroSpace = kAmcorOneMark;
const uint16_t kAmcorFooterMark = 1900;
const uint16_t kAmcorGap = 34300;
const uint8_t kAmcorTolerance = 40;
using irutils::addBoolToString;
using irutils::addModeToString;
using irutils::addFanToString;
using irutils::addTempToString;
#if SEND_AMCOR
// Send a Amcor HVAC formatted message.
//
// Args:
// data: The message to be sent.
// nbytes: The byte size of the array being sent. typically kAmcorStateLength.
// repeat: The number of times the message is to be repeated.
//
// Status: STABLE / Reported as working.
//
void IRsend::sendAmcor(const unsigned char data[], const uint16_t nbytes,
const uint16_t repeat) {
// Check if we have enough bytes to send a proper message.
if (nbytes < kAmcorStateLength) return;
sendGeneric(kAmcorHdrMark, kAmcorHdrSpace, kAmcorOneMark, kAmcorOneSpace,
kAmcorZeroMark, kAmcorZeroSpace, kAmcorFooterMark, kAmcorGap,
data, nbytes, 38, false, repeat, kDutyDefault);
}
#endif
#if DECODE_AMCOR
// Decode the supplied Amcor HVAC message.
// Args:
// results: Ptr to the data to decode and where to store the decode result.
// nbits: Nr. of bits to expect in the data portion.
// Typically kAmcorBits.
// strict: Flag to indicate if we strictly adhere to the specification.
// Returns:
// boolean: True if it can decode it, false if it can't.
//
// Status: STABLE / Reported as working.
//
bool IRrecv::decodeAmcor(decode_results *results, uint16_t nbits,
bool strict) {
if (results->rawlen < 2 * nbits + kHeader - 1)
return false; // Can't possibly be a valid Amcor message.
if (strict && nbits != kAmcorBits)
return false; // We expect Amcor to be 64 bits of message.
uint16_t offset = kStartOffset;
uint16_t used;
// Header + Data Block (64 bits) + Footer
used = matchGeneric(results->rawbuf + offset, results->state,
results->rawlen - offset, 64,
kAmcorHdrMark, kAmcorHdrSpace,
kAmcorOneMark, kAmcorOneSpace,
kAmcorZeroMark, kAmcorZeroSpace,
kAmcorFooterMark, kAmcorGap, true,
kAmcorTolerance, 0, false);
if (!used) return false;
offset += used;
if (strict) {
if (!IRAmcorAc::validChecksum(results->state)) return false;
}
// Success
results->bits = nbits;
results->decode_type = AMCOR;
// No need to record the state as we stored it as we decoded it.
// As we use result->state, we don't record value, address, or command as it
// is a union data type.
return true;
}
#endif
IRAmcorAc::IRAmcorAc(const uint16_t pin, const bool inverted,
const bool use_modulation)
: _irsend(pin, inverted, use_modulation) { this->stateReset(); }
void IRAmcorAc::begin(void) { _irsend.begin(); }
#if SEND_AMCOR
void IRAmcorAc::send(const uint16_t repeat) {
this->checksum(); // Create valid checksum before sending
_irsend.sendAmcor(remote_state, kAmcorStateLength, repeat);
}
#endif // SEND_AMCOR
uint8_t IRAmcorAc::calcChecksum(const uint8_t state[], const uint16_t length) {
return irutils::sumNibbles(state, length - 1);
}
bool IRAmcorAc::validChecksum(const uint8_t state[], const uint16_t length) {
return (state[length - 1] == IRAmcorAc::calcChecksum(state, length));
}
void IRAmcorAc::checksum(void) {
remote_state[kAmcorChecksumByte] = IRAmcorAc::calcChecksum(remote_state,
kAmcorStateLength);
}
void IRAmcorAc::stateReset(void) {
for (uint8_t i = 1; i < kAmcorStateLength; i++) remote_state[i] = 0x0;
remote_state[0] = 0x01;
setFan(kAmcorFanAuto);
setMode(kAmcorAuto);
setTemp(25); // 25C
}
uint8_t* IRAmcorAc::getRaw(void) {
this->checksum(); // Ensure correct bit array before returning
return remote_state;
}
void IRAmcorAc::setRaw(const uint8_t state[]) {
for (uint8_t i = 0; i < kAmcorStateLength; i++) remote_state[i] = state[i];
}
void IRAmcorAc::on(void) { setPower(true); }
void IRAmcorAc::off(void) { setPower(false); }
void IRAmcorAc::setPower(const bool on) {
remote_state[kAmcorPowerByte] &= ~kAmcorPowerMask;
remote_state[kAmcorPowerByte] |= (on ? kAmcorPowerOn : kAmcorPowerOff);
}
bool IRAmcorAc::getPower(void) {
return ((remote_state[kAmcorPowerByte] & kAmcorPowerMask) == kAmcorPowerOn);
}
// Set the temp in deg C
void IRAmcorAc::setTemp(const uint8_t degrees) {
uint8_t temp = std::max(kAmcorMinTemp, degrees);
temp = std::min(kAmcorMaxTemp, temp);
temp <<= 1;
remote_state[kAmcorTempByte] &= ~kAmcorTempMask;
remote_state[kAmcorTempByte] |= temp;
}
uint8_t IRAmcorAc::getTemp(void) {
return (remote_state[kAmcorTempByte] & kAmcorTempMask) >> 1;
}
// Maximum Cooling or Hearing
void IRAmcorAc::setMax(const bool on) {
if (on) {
switch (getMode()) {
case kAmcorCool:
setTemp(kAmcorMinTemp);
break;
case kAmcorHeat:
setTemp(kAmcorMaxTemp);
break;
default: // Not allowed in all other operating modes.
return;
}
remote_state[kAmcorSpecialByte] |= kAmcorMaxMask;
} else {
remote_state[kAmcorSpecialByte] &= ~kAmcorMaxMask;
}
}
bool IRAmcorAc::getMax(void) {
return ((remote_state[kAmcorSpecialByte] & kAmcorMaxMask) == kAmcorMaxMask);
}
// Set the speed of the fan
void IRAmcorAc::setFan(const uint8_t speed) {
switch (speed) {
case kAmcorFanAuto:
case kAmcorFanMin:
case kAmcorFanMed:
case kAmcorFanMax:
remote_state[kAmcorModeFanByte] &= ~kAmcorFanMask;
remote_state[kAmcorModeFanByte] |= speed << 4;
break;
default:
setFan(kAmcorFanAuto);
}
}
uint8_t IRAmcorAc::getFan(void) {
return (remote_state[kAmcorModeFanByte] & kAmcorFanMask) >> 4;
}
uint8_t IRAmcorAc::getMode(void) {
return remote_state[kAmcorModeFanByte] & kAmcorModeMask;
}
void IRAmcorAc::setMode(const uint8_t mode) {
remote_state[kAmcorSpecialByte] &= ~kAmcorVentMask; // Clear the vent setting
switch (mode) {
case kAmcorFan:
remote_state[kAmcorSpecialByte] |= kAmcorVentMask; // Set the vent option
// FALL-THRU
case kAmcorCool:
case kAmcorHeat:
case kAmcorDry:
case kAmcorAuto:
// Mask out bits
remote_state[kAmcorModeFanByte] &= ~kAmcorModeMask;
// Set the mode at bit positions
remote_state[kAmcorModeFanByte] |= mode;
return;
default:
this->setMode(kAmcorAuto);
}
}
// Convert a standard A/C mode into its native mode.
uint8_t IRAmcorAc::convertMode(const stdAc::opmode_t mode) {
switch (mode) {
case stdAc::opmode_t::kCool:
return kAmcorCool;
case stdAc::opmode_t::kHeat:
return kAmcorHeat;
case stdAc::opmode_t::kDry:
return kAmcorDry;
case stdAc::opmode_t::kFan:
return kAmcorFan;
default:
return kAmcorAuto;
}
}
// Convert a standard A/C Fan speed into its native fan speed.
uint8_t IRAmcorAc::convertFan(const stdAc::fanspeed_t speed) {
switch (speed) {
case stdAc::fanspeed_t::kMin:
case stdAc::fanspeed_t::kLow:
return kAmcorFanMin;
case stdAc::fanspeed_t::kMedium:
return kAmcorFanMed;
case stdAc::fanspeed_t::kHigh:
case stdAc::fanspeed_t::kMax:
return kAmcorFanMax;
default:
return kAmcorFanAuto;
}
}
// Convert a native mode to it's common equivalent.
stdAc::opmode_t IRAmcorAc::toCommonMode(const uint8_t mode) {
switch (mode) {
case kAmcorCool: return stdAc::opmode_t::kCool;
case kAmcorHeat: return stdAc::opmode_t::kHeat;
case kAmcorDry: return stdAc::opmode_t::kDry;
case kAmcorFan: return stdAc::opmode_t::kFan;
default: return stdAc::opmode_t::kAuto;
}
}
// Convert a native fan speed to it's common equivalent.
stdAc::fanspeed_t IRAmcorAc::toCommonFanSpeed(const uint8_t speed) {
switch (speed) {
case kAmcorFanMax: return stdAc::fanspeed_t::kMax;
case kAmcorFanMed: return stdAc::fanspeed_t::kMedium;
case kAmcorFanMin: return stdAc::fanspeed_t::kMin;
default: return stdAc::fanspeed_t::kAuto;
}
}
// Convert the A/C state to it's common equivalent.
stdAc::state_t IRAmcorAc::toCommon(void) {
stdAc::state_t result;
result.protocol = decode_type_t::AMCOR;
result.power = this->getPower();
result.mode = this->toCommonMode(this->getMode());
result.celsius = true;
result.degrees = this->getTemp();
result.fanspeed = this->toCommonFanSpeed(this->getFan());
// Not supported.
result.model = -1;
result.turbo = false;
result.swingv = stdAc::swingv_t::kOff;
result.swingh = stdAc::swingh_t::kOff;
result.light = false;
result.filter = false;
result.econo = false;
result.quiet = false;
result.clean = false;
result.beep = false;
result.sleep = -1;
result.clock = -1;
return result;
}
// Convert the internal state into a human readable string.
String IRAmcorAc::toString() {
String result = "";
result.reserve(70); // Reserve some heap for the string to reduce fragging.
result += addBoolToString(getPower(), F("Power"), false);
result += addModeToString(getMode(), kAmcorAuto, kAmcorCool,
kAmcorHeat, kAmcorDry, kAmcorFan);
result += addFanToString(getFan(), kAmcorFanMax, kAmcorFanMin,
kAmcorFanAuto, kAmcorFanAuto,
kAmcorFanMed);
result += addTempToString(getTemp());
result += addBoolToString(getMax(), F("Max"));
return result;
}

View File

@ -0,0 +1,118 @@
// Amcor A/C
//
// Copyright 2019 David Conran
#ifndef IR_AMCOR_H_
#define IR_AMCOR_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
// Supports:
// Brand: Amcor, Model: ADR-853H A/C
// Brand: Amcor, Model: TAC-495 remote
// Brand: Amcor, Model: TAC-444 remote
// Ref:
// https://github.com/crankyoldgit/IRremoteESP8266/issues/834
// Kudos:
// ldellus: For the breakdown and mapping of the bit values.
// Constants
// state[1]
const uint8_t kAmcorModeFanByte = 1;
// Fan Control
const uint8_t kAmcorFanMin = 0b001;
const uint8_t kAmcorFanMed = 0b010;
const uint8_t kAmcorFanMax = 0b011;
const uint8_t kAmcorFanAuto = 0b100;
const uint8_t kAmcorFanMask = 0b01110000;
// Modes
const uint8_t kAmcorCool = 0b001;
const uint8_t kAmcorHeat = 0b010;
const uint8_t kAmcorFan = 0b011; // Aka "Vent"
const uint8_t kAmcorDry = 0b100;
const uint8_t kAmcorAuto = 0b101;
const uint8_t kAmcorModeMask = 0b00000111;
// state[2]
const uint8_t kAmcorTempByte = 2;
// Temperature
const uint8_t kAmcorMinTemp = 12; // Celsius
const uint8_t kAmcorMaxTemp = 32; // Celsius
const uint8_t kAmcorTempMask = 0b01111110;
// state[5]
// Power
const uint8_t kAmcorPowerByte = 5;
const uint8_t kAmcorPowerMask = 0b11110000;
const uint8_t kAmcorPowerOn = 0b00110000; // 0x30
const uint8_t kAmcorPowerOff = 0b11000000; // 0xC0
// state[6]
const uint8_t kAmcorSpecialByte = 6;
// Max Mode (aka "Lo" in Cool and "Hi" in Heat)
const uint8_t kAmcorMaxMask = 0b00000011; // 0x03
// "Vent" Mode
const uint8_t kAmcorVentMask = 0b11000000; // 0xC0
// state[7]
// Checksum byte.
const uint8_t kAmcorChecksumByte = kAmcorStateLength - 1;
// Classes
class IRAmcorAc {
public:
explicit IRAmcorAc(const uint16_t pin, const bool inverted = false,
const bool use_modulation = true);
void stateReset();
#if SEND_AMCOR
void send(const uint16_t repeat = kAmcorDefaultRepeat);
uint8_t calibrate(void) { return _irsend.calibrate(); }
#endif // SEND_AMCOR
void begin();
static uint8_t calcChecksum(const uint8_t state[],
const uint16_t length = kAmcorStateLength);
static bool validChecksum(const uint8_t state[],
const uint16_t length = kAmcorStateLength);
void setPower(const bool state);
bool getPower();
void on();
void off();
void setTemp(const uint8_t temp);
uint8_t getTemp();
void setMax(const bool on);
bool getMax(void);
void setFan(const uint8_t speed);
uint8_t getFan();
void setMode(const uint8_t mode);
uint8_t getMode();
uint8_t* getRaw();
void setRaw(const uint8_t state[]);
uint8_t convertMode(const stdAc::opmode_t mode);
uint8_t convertFan(const stdAc::fanspeed_t speed);
static stdAc::opmode_t toCommonMode(const uint8_t mode);
static stdAc::fanspeed_t toCommonFanSpeed(const uint8_t speed);
stdAc::state_t toCommon(void);
String toString();
#ifndef UNIT_TEST
private:
IRsend _irsend;
#else
IRsendTest _irsend;
#endif
uint8_t remote_state[kAmcorStateLength]; // The state of the IR remote.
void checksum(void);
};
#endif // IR_AMCOR_H_

View File

@ -422,7 +422,7 @@ bool IRrecv::decodeArgo(decode_results *results, const uint16_t nbits,
kArgoBitMark, kArgoOneSpace,
kArgoBitMark, kArgoZeroSpace,
0, 0, // Footer (None, allegedly. This seems very wrong.)
true, kTolerance, 0, false)) return false;
true, _tolerance, 0, false)) return false;
// Compliance
// Verify we got a valid checksum.

View File

@ -1275,9 +1275,9 @@ bool IRrecv::decodeDaikin2(decode_results *results, uint16_t nbits,
// Leader
if (!matchMark(results->rawbuf[offset++], kDaikin2LeaderMark,
kDaikin2Tolerance)) return false;
_tolerance + kDaikin2Tolerance)) return false;
if (!matchSpace(results->rawbuf[offset++], kDaikin2LeaderSpace,
kDaikin2Tolerance)) return false;
_tolerance + kDaikin2Tolerance)) return false;
// Sections
uint16_t pos = 0;
@ -1291,7 +1291,8 @@ bool IRrecv::decodeDaikin2(decode_results *results, uint16_t nbits,
kDaikin2BitMark, kDaikin2ZeroSpace,
kDaikin2BitMark, kDaikin2Gap,
section >= kDaikin2Sections - 1,
kDaikin2Tolerance, kDaikinMarkExcess, false);
_tolerance + kDaikin2Tolerance, kDaikinMarkExcess,
false);
if (used == 0) return false;
offset += used;
pos += ksectionSize[section];
@ -2913,3 +2914,156 @@ bool IRrecv::decodeDaikin128(decode_results *results, const uint16_t nbits,
return true;
}
#endif // DECODE_DAIKIN128
#if SEND_DAIKIN152
// Send a Daikin 152 bit A/C message.
//
// Args:
// data: An array of kDaikin152StateLength bytes containing the IR command.
//
// Supported devices:
// - Daikin ARC480A5 remote.
//
// Status: Beta / Probably working.
//
// Ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/873
void IRsend::sendDaikin152(const unsigned char data[], const uint16_t nbytes,
const uint16_t repeat) {
for (uint16_t r = 0; r <= repeat; r++) {
// Leader
sendGeneric(0, 0, kDaikin152BitMark, kDaikin152OneSpace,
kDaikin152BitMark, kDaikin152ZeroSpace,
kDaikin152BitMark, kDaikin152Gap,
(uint64_t)0, kDaikin152LeaderBits,
kDaikin152Freq, false, 0, kDutyDefault);
// Header + Data + Footer
sendGeneric(kDaikin152HdrMark, kDaikin152HdrSpace, kDaikin152BitMark,
kDaikin152OneSpace, kDaikin152BitMark, kDaikin152ZeroSpace,
kDaikin152BitMark, kDaikin152Gap, data,
nbytes, kDaikin152Freq, false, 0, kDutyDefault);
}
}
#endif // SEND_DAIKIN152
#if DECODE_DAIKIN152
// Decode the supplied Daikin 152 bit A/C message.
// Args:
// results: Ptr to the data to decode and where to store the decode result.
// nbits: Nr. of bits to expect in the data portion. (kDaikin152Bits)
// strict: Flag to indicate if we strictly adhere to the specification.
// Returns:
// boolean: True if it can decode it, false if it can't.
//
// Supported devices:
// - Daikin ARC480A5 remote.
//
// Status: Beta / Probably working.
//
// Ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/873
bool IRrecv::decodeDaikin152(decode_results *results, const uint16_t nbits,
const bool strict) {
if (results->rawlen < 2 * (5 + nbits + kFooter) + kHeader - 1)
return false;
if (nbits / 8 < kDaikin152StateLength) return false;
// Compliance
if (strict && nbits != kDaikin152Bits) return false;
uint16_t offset = kStartOffset;
uint16_t used;
// Leader
uint64_t leader = 0;
used = matchGeneric(results->rawbuf + offset, &leader,
results->rawlen - offset, kDaikin152LeaderBits,
0, 0, // No Header
kDaikin152BitMark, kDaikin152OneSpace,
kDaikin152BitMark, kDaikin152ZeroSpace,
kDaikin152BitMark, kDaikin152Gap, // Footer gap
false, _tolerance, kMarkExcess, false);
if (used == 0 || leader != 0) return false;
offset += used;
// Header + Data + Footer
used = matchGeneric(results->rawbuf + offset, results->state,
results->rawlen - offset, nbits,
kDaikin152HdrMark, kDaikin152HdrSpace,
kDaikin152BitMark, kDaikin152OneSpace,
kDaikin152BitMark, kDaikin152ZeroSpace,
kDaikin152BitMark, kDaikin152Gap,
true, _tolerance, kMarkExcess, false);
if (used == 0) return false;
// Compliance
if (strict) {
if (!IRDaikin152::validChecksum(results->state)) return false;
}
// Success
results->decode_type = decode_type_t::DAIKIN152;
results->bits = nbits;
// No need to record the state as we stored it as we decoded it.
// As we use result->state, we don't record value, address, or command as it
// is a union data type.
return true;
}
#endif // DECODE_DAIKIN152
// Class for handling Daikin 152 bit / 19 byte A/C messages.
//
// Code by crankyoldgit.
//
// Supported Remotes: Daikin ARC480A5 remote
//
// Ref:
// https://github.com/crankyoldgit/IRremoteESP8266/issues/873
IRDaikin152::IRDaikin152(const uint16_t pin, const bool inverted,
const bool use_modulation)
: _irsend(pin, inverted, use_modulation) { stateReset(); }
void IRDaikin152::begin() { _irsend.begin(); }
#if SEND_DAIKIN152
void IRDaikin152::send(const uint16_t repeat) {
checksum();
_irsend.sendDaikin152(remote_state, kDaikin152StateLength, repeat);
}
#endif // SEND_DAIKIN152
// Verify the checksum is valid for a given state.
// Args:
// state: The array to verify the checksum of.
// length: The size of the state.
// Returns:
// A boolean.
bool IRDaikin152::validChecksum(uint8_t state[], const uint16_t length) {
// Validate the checksum of the given state.
if (length <= 1 || state[length - 1] != sumBytes(state, length - 1))
return false;
else
return true;
}
// Calculate and set the checksum values for the internal state.
void IRDaikin152::checksum() {
remote_state[kDaikin152StateLength - 1] = sumBytes(
remote_state, kDaikin152StateLength - 1);
}
void IRDaikin152::stateReset() {
for (uint8_t i = 3; i < kDaikin152StateLength; i++) remote_state[i] = 0x00;
remote_state[0] = 0x11;
remote_state[1] = 0xDA;
remote_state[2] = 0x27;
// remote_state[19] is a checksum byte, it will be set by checksum().
}
uint8_t *IRDaikin152::getRaw() {
checksum(); // Ensure correct settings before sending.
return remote_state;
}
void IRDaikin152::setRaw(const uint8_t new_code[]) {
for (uint8_t i = 0; i < kDaikin152StateLength; i++)
remote_state[i] = new_code[i];
}

View File

@ -16,7 +16,7 @@
// Brand: Daikin, Model: FTXB12AXVJU A/C (DAIKIN128)
// Brand: Daikin, Model: FTXB09AXVJU A/C (DAIKIN128)
// Brand: Daikin, Model: BRC52B63 remote (DAIKIN128)
// Brand: Daikin, Model: ARC480A5 remote (DAIKIN152)
#ifndef IR_DAIKIN_H_
#define IR_DAIKIN_H_
@ -32,7 +32,7 @@
#endif
/*
Daikin AC map
Daikin AC map (i.e. DAIKIN, not the other variants)
byte 6=
b4:Comfort
byte 7= checksum of the first part (and last byte before a 29ms pause)
@ -176,7 +176,7 @@ const uint16_t kDaikin2ZeroSpace = 420;
const uint16_t kDaikin2Sections = 2;
const uint16_t kDaikin2Section1Length = 20;
const uint16_t kDaikin2Section2Length = 19;
const uint8_t kDaikin2Tolerance = kTolerance + 5;
const uint8_t kDaikin2Tolerance = 5; // Extra percentage tolerance
const uint8_t kDaikin2BitSleepTimer = 0b00100000;
const uint8_t kDaikin2BitPurify = 0b00010000;
@ -328,6 +328,17 @@ const uint8_t kDaikin128BitWall = 0b00001000;
const uint8_t kDaikin128BitCeiling = 0b00000001;
const uint8_t kDaikin128MaskLight = kDaikin128BitWall | kDaikin128BitCeiling;
// Another variant of the protocol for the Daikin ARC480A5 remote.
// Ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/873
const uint16_t kDaikin152Freq = 38000; // Modulation Frequency in Hz.
const uint8_t kDaikin152LeaderBits = 5;
const uint16_t kDaikin152HdrMark = 3492;
const uint16_t kDaikin152HdrSpace = 1718;
const uint16_t kDaikin152BitMark = 433;
const uint16_t kDaikin152OneSpace = 1529;
const uint16_t kDaikin152ZeroSpace = kDaikin152BitMark;
const uint16_t kDaikin152Gap = 25182;
// Legacy defines.
#define DAIKIN_COOL kDaikinCool
#define DAIKIN_HEAT kDaikinHeat
@ -603,6 +614,7 @@ class IRDaikin160 {
void stateReset();
void checksum();
};
// Class to emulate a Daikin BRC4C153 remote.
class IRDaikin176 {
public:
@ -721,4 +733,31 @@ class IRDaikin128 {
void clearSleepTimerFlag(void);
};
// Class to emulate a Daikin ARC480A5 remote.
class IRDaikin152 {
public:
explicit IRDaikin152(const uint16_t pin, const bool inverted = false,
const bool use_modulation = true);
#if SEND_DAIKIN152
void send(const uint16_t repeat = kDaikin152DefaultRepeat);
uint8_t calibrate(void) { return _irsend.calibrate(); }
#endif
void begin();
uint8_t* getRaw();
void setRaw(const uint8_t new_code[]);
static bool validChecksum(uint8_t state[],
const uint16_t length = kDaikin152StateLength);
#ifndef UNIT_TEST
private:
IRsend _irsend;
#else
IRsendTest _irsend;
#endif
// # of bytes per command
uint8_t remote_state[kDaikin152StateLength];
void stateReset();
void checksum();
};
#endif // IR_DAIKIN_H_

View File

@ -317,7 +317,7 @@ bool IRrecv::decodeElectraAC(decode_results *results, uint16_t nbits,
kElectraAcBitMark, kElectraAcOneSpace,
kElectraAcBitMark, kElectraAcZeroSpace,
kElectraAcBitMark, kElectraAcMessageGap, true,
kTolerance, 0, false)) return false;
_tolerance, 0, false)) return false;
// Compliance
if (strict) {

View File

@ -267,9 +267,6 @@ bool IRFujitsuAC::setRaw(const uint8_t newState[], const uint16_t length) {
return true;
}
// Set the requested power state of the A/C to off.
void IRFujitsuAC::off(void) { this->setCmd(kFujitsuAcCmdTurnOff); }
void IRFujitsuAC::stepHoriz(void) { this->setCmd(kFujitsuAcCmdStepHoriz); }
void IRFujitsuAC::toggleSwingHoriz(const bool update) {
@ -336,6 +333,17 @@ uint8_t IRFujitsuAC::getCmd(const bool raw) {
return _cmd;
}
// Set the requested power state of the A/C.
void IRFujitsuAC::setPower(const bool on) {
this->setCmd(on ? kFujitsuAcCmdTurnOn : kFujitsuAcCmdTurnOff);
}
// Set the requested power state of the A/C to off.
void IRFujitsuAC::off(void) { this->setPower(false); }
// Set the requested power state of the A/C to on.
void IRFujitsuAC::on(void) { this->setPower(true); }
bool IRFujitsuAC::getPower(void) { return _cmd != kFujitsuAcCmdTurnOff; }
void IRFujitsuAC::setOutsideQuiet(const bool on) {
@ -649,7 +657,7 @@ bool IRrecv::decodeFujitsuAC(decode_results* results, uint16_t nbits,
match_result_t data_result =
matchData(&(results->rawbuf[offset]), kFujitsuAcMinBits - 8,
kFujitsuAcBitMark, kFujitsuAcOneSpace, kFujitsuAcBitMark,
kFujitsuAcZeroSpace, kTolerance, kMarkExcess, false);
kFujitsuAcZeroSpace, _tolerance, kMarkExcess, false);
if (data_result.success == false) return false; // Fail
if (data_result.data != 0x1010006314) return false; // Signature failed.
dataBitsSoFar += kFujitsuAcMinBits - 8;
@ -666,7 +674,7 @@ bool IRrecv::decodeFujitsuAC(decode_results* results, uint16_t nbits,
i++, dataBitsSoFar += 8, offset += data_result.used) {
data_result = matchData(
&(results->rawbuf[offset]), 8, kFujitsuAcBitMark, kFujitsuAcOneSpace,
kFujitsuAcBitMark, kFujitsuAcZeroSpace, kTolerance, kMarkExcess, false);
kFujitsuAcBitMark, kFujitsuAcZeroSpace, _tolerance, kMarkExcess, false);
if (data_result.success == false) break; // Fail
results->state[i] = data_result.data;
}

View File

@ -104,7 +104,6 @@ class IRFujitsuAC {
uint8_t calibrate(void) { return _irsend.calibrate(); }
#endif // SEND_FUJITSU_AC
void begin(void);
void off(void);
void stepHoriz(void);
void toggleSwingHoriz(const bool update = true);
void stepVert(void);
@ -123,6 +122,9 @@ class IRFujitsuAC {
bool setRaw(const uint8_t newState[], const uint16_t length);
uint8_t getStateLength(void);
static bool validChecksum(uint8_t* state, const uint16_t length);
void setPower(const bool on);
void off(void);
void on(void);
bool getPower(void);
void setOutsideQuiet(const bool on);

View File

@ -419,7 +419,7 @@ bool IRrecv::decodeGoodweather(decode_results* results,
data_result = matchData(&(results->rawbuf[offset]), 8,
kGoodweatherBitMark, kGoodweatherOneSpace,
kGoodweatherBitMark, kGoodweatherZeroSpace,
kTolerance, kMarkExcess, false);
_tolerance, kMarkExcess, false);
if (data_result.success == false) return false;
DPRINTLN("DEBUG: Normal byte read okay.");
offset += data_result.used;
@ -428,7 +428,7 @@ bool IRrecv::decodeGoodweather(decode_results* results,
data_result = matchData(&(results->rawbuf[offset]), 8,
kGoodweatherBitMark, kGoodweatherOneSpace,
kGoodweatherBitMark, kGoodweatherZeroSpace,
kTolerance, kMarkExcess, false);
_tolerance, kMarkExcess, false);
if (data_result.success == false) return false;
DPRINTLN("DEBUG: Inverted byte read okay.");
offset += data_result.used;

View File

@ -35,6 +35,7 @@ using irutils::addLabeledString;
using irutils::addModeToString;
using irutils::addFanToString;
using irutils::addTempToString;
using irutils::minsToString;
#if SEND_GREE
// Send a Gree Heat Pump message.
@ -222,12 +223,13 @@ void IRGreeAC::setTemp(const uint8_t temp) {
uint8_t new_temp = std::max((uint8_t)kGreeMinTemp, temp);
new_temp = std::min((uint8_t)kGreeMaxTemp, new_temp);
if (getMode() == kGreeAuto) new_temp = 25;
remote_state[1] = (remote_state[1] & 0xF0U) | (new_temp - kGreeMinTemp);
remote_state[1] = (remote_state[1] & ~kGreeTempMask) |
(new_temp - kGreeMinTemp);
}
// Return the set temp. in deg C
uint8_t IRGreeAC::getTemp(void) {
return ((remote_state[1] & 0xFU) + kGreeMinTemp);
return ((remote_state[1] & kGreeTempMask) + kGreeMinTemp);
}
// Set the speed of the fan, 0-3, 0 is auto, 1-3 is the speed
@ -359,6 +361,44 @@ uint8_t IRGreeAC::getSwingVerticalPosition(void) {
return remote_state[4] & kGreeSwingPosMask;
}
void IRGreeAC::setTimerEnabled(const bool on) {
if (on)
remote_state[1] |= kGreeTimerEnabledBit;
else
remote_state[1] &= ~kGreeTimerEnabledBit;
}
bool IRGreeAC::getTimerEnabled(void) {
return remote_state[1] & kGreeTimerEnabledBit;
}
// Returns the number of minutes the timer is set for.
uint16_t IRGreeAC::getTimer(void) {
uint16_t hrs = irutils::bcdToUint8(
(remote_state[2] & kGreeTimerHoursMask) |
((remote_state[1] & kGreeTimerTensHrMask) >> 1));
return hrs * 60 + ((remote_state[1] & kGreeTimerHalfHrBit) ? 30 : 0);
}
// Set the A/C's timer to turn off in X many minutes.
// Stores time internally in 30 min units.
// e.g. 5 mins means 0 (& Off), 95 mins is 90 mins (& On). Max is 24 hours.
//
// Args:
// minutes: The number of minutes the timer should be set for.
void IRGreeAC::setTimer(const uint16_t minutes) {
// Clear the previous settings.
remote_state[1] &= ~kGreeTimer1Mask;
remote_state[2] &= ~kGreeTimerHoursMask;
uint16_t mins = std::min(kGreeTimerMax, minutes); // Bounds check.
setTimerEnabled(mins >= 30); // Timer is enabled when >= 30 mins.
uint8_t hours = mins / 60;
uint8_t halfhour = (mins % 60) < 30 ? 0 : 1;
// Set the "tens" digit of hours & the half hour.
remote_state[1] |= (((hours / 10) << 1) | halfhour) << 4;
// Set the "units" digit of hours.
remote_state[2] |= (hours % 10);
}
// Convert a standard A/C mode into its native mode.
uint8_t IRGreeAC::convertMode(const stdAc::opmode_t mode) {
@ -504,6 +544,11 @@ String IRGreeAC::toString(void) {
result += F(" (Auto)");
break;
}
result += F(", Timer: ");
if (getTimerEnabled())
result += minsToString(getTimer());
else
result += F("Off");
return result;
}
@ -538,7 +583,7 @@ bool IRrecv::decodeGree(decode_results* results, uint16_t nbits, bool strict) {
kGreeBitMark, kGreeOneSpace,
kGreeBitMark, kGreeZeroSpace,
0, 0, false,
kTolerance, kMarkExcess, false);
_tolerance, kMarkExcess, false);
if (used == 0) return false;
offset += used;
@ -546,7 +591,7 @@ bool IRrecv::decodeGree(decode_results* results, uint16_t nbits, bool strict) {
match_result_t data_result;
data_result = matchData(&(results->rawbuf[offset]), kGreeBlockFooterBits,
kGreeBitMark, kGreeOneSpace, kGreeBitMark,
kGreeZeroSpace, kTolerance, kMarkExcess, false);
kGreeZeroSpace, _tolerance, kMarkExcess, false);
if (data_result.success == false) return false;
if (data_result.data != kGreeBlockFooter) return false;
offset += data_result.used;
@ -558,7 +603,7 @@ bool IRrecv::decodeGree(decode_results* results, uint16_t nbits, bool strict) {
kGreeBitMark, kGreeOneSpace,
kGreeBitMark, kGreeZeroSpace,
kGreeBitMark, kGreeMsgSpace, true,
kTolerance, kMarkExcess, false)) return false;
_tolerance, kMarkExcess, false)) return false;
// Compliance
if (strict) {

View File

@ -39,12 +39,28 @@ const uint8_t kGreeHeat = 4;
const uint8_t kGreeModeMask = 0b00000111;
const uint8_t kGreePower1Mask = 0b00001000;
const uint8_t kGreeFanMask = 0b00110000;
const uint8_t kGreeFanAuto = 0;
const uint8_t kGreeFanMin = 1;
const uint8_t kGreeFanMed = 2;
const uint8_t kGreeFanMax = 3;
const uint8_t kGreeSwingAutoMask = 0b01000000;
const uint8_t kGreeSleepMask = 0b10000000;
// Byte 1
const uint8_t kGreeTempMask = 0b00001111;
const uint8_t kGreeMinTemp = 16; // Celsius
const uint8_t kGreeMaxTemp = 30; // Celsius
const uint8_t kGreeTimerEnabledBit = 0b10000000;
const uint8_t kGreeTimerHalfHrBit = 0b00010000;
const uint8_t kGreeTimerTensHrMask = 0b01100000;
const uint8_t kGreeTimer1Mask = kGreeTimerTensHrMask | kGreeTimerHalfHrBit;
const uint16_t kGreeTimerMax = 24 * 60;
// Byte 2
const uint8_t kGreeTimerHoursMask = 0b00001111;
const uint8_t kGreeTurboMask = 0b00010000;
const uint8_t kGreeLightMask = 0b00100000;
const uint8_t kGreePower2Mask = 0b01000000; // This might not be used. See #814
// This might not be used. See #814
const uint8_t kGreePower2Mask = 0b01000000;
const uint8_t kGreeXfanMask = 0b10000000;
// Byte 4
const uint8_t kGreeSwingPosMask = 0b00001111;
@ -52,14 +68,6 @@ const uint8_t kGreeSwingPosMask = 0b00001111;
const uint8_t kGreeIFeelMask = 0b00000100;
const uint8_t kGreeWiFiMask = 0b01000000;
const uint8_t kGreeMinTemp = 16; // Celsius
const uint8_t kGreeMaxTemp = 30; // Celsius
const uint8_t kGreeFanAuto = 0;
const uint8_t kGreeFanMin = 1;
const uint8_t kGreeFanMed = 2;
const uint8_t kGreeFanMax = 3;
const uint8_t kGreeSwingLastPos = 0b00000000;
const uint8_t kGreeSwingAuto = 0b00000001;
const uint8_t kGreeSwingUp = 0b00000010;
@ -132,6 +140,8 @@ class IRGreeAC {
void setSwingVertical(const bool automatic, const uint8_t position);
bool getSwingVerticalAuto(void);
uint8_t getSwingVerticalPosition(void);
uint16_t getTimer(void);
void setTimer(const uint16_t minutes);
uint8_t convertMode(const stdAc::opmode_t mode);
uint8_t convertFan(const stdAc::fanspeed_t speed);
uint8_t convertSwingV(const stdAc::swingv_t swingv);
@ -156,6 +166,8 @@ class IRGreeAC {
gree_ac_remote_model_t _model;
void checksum(const uint16_t length = kGreeStateLength);
void fixup(void);
void setTimerEnabled(const bool on);
bool getTimerEnabled(void);
};
#endif // IR_GREE_H_

Some files were not shown because too many files have changed in this diff Show More