Update Irremote lib to latest master commit ca474a6 (#24226)

This commit is contained in:
Jason2866 2025-12-15 16:54:39 +01:00 committed by GitHub
parent a922d6609f
commit df1aa6dc98
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
81 changed files with 4157 additions and 2186 deletions

View File

@ -25,7 +25,7 @@ jobs:
JSONI=${JSONI//$'\n'}
echo $JSONI
# Set output
echo "::set-output name=matrix::[${JSONI}]"
echo "matrix=[${JSONI}]" >> $GITHUB_OUTPUT
Build_Example:
needs: Gen_Matrix
runs-on: ubuntu-latest
@ -36,19 +36,19 @@ jobs:
steps:
- uses: actions/checkout@v2
- name: Cache pip
uses: actions/cache@v2
uses: actions/cache@v4
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
restore-keys: |
${{ runner.os }}-pip-
- name: Cache PlatformIO
uses: actions/cache@v2
uses: actions/cache@v4
with:
path: ~/.platformio
key: ${{ runner.os }}-${{ hashFiles('**/platformio.ini') }}
- name: Cache PlatformIO build
uses: actions/cache@v2
uses: actions/cache@v4
with:
path: .pio
key: pio-${{ runner.os }}-${{ matrix.project }}
@ -56,10 +56,13 @@ jobs:
pio-${{ runner.os }}-
pio-
- name: Set up Python
uses: actions/setup-python@v2
uses: actions/setup-python@v5
with:
python-version: '3.13'
- name: Install PlatformIO
run: |
python -m pip install --upgrade pip
pip install --upgrade intelhex
pip install --upgrade platformio
- name: Build example
env:

View File

@ -42,7 +42,7 @@ jobs:
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v2
uses: github/codeql-action/init@v3
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
@ -56,7 +56,7 @@ jobs:
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v2
uses: github/codeql-action/autobuild@v3
# Command-line programs to run using the OS shell.
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
@ -69,4 +69,4 @@ jobs:
# ./location_of_script_within_repo/buildscript.sh
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2
uses: github/codeql-action/analyze@v3

View File

@ -1,3 +1,4 @@
set noparent
root=src
linelength=80
filter=-whitespace/indent_namespace

View File

@ -77,6 +77,8 @@ If you want to [contribute](.github/CONTRIBUTING.md#how-can-i-contribute) to thi
- Improve our documentation
- [Creating issues](.github/CONTRIBUTING.md#reporting-bugs) and [pull requests](.github/CONTRIBUTING.md#pull-requests)
- Tell other people about this library
- Updated documentation formatting and clarified installation steps (Hacktoberfest contribution by Prerna Utage)
## Contributors
Available [here](.github/Contributors.md)
@ -87,3 +89,6 @@ This library was originally based on Ken Shirriff's work (https://github.com/shi
[Mark Szabo](https://github.com/crankyoldgit/IRremoteESP8266) has updated the IRsend class to work on ESP8266 and [Sebastien Warin](https://github.com/sebastienwarin/IRremoteESP8266) the receiving & decoding part (IRrecv class).
As of v2.0, the library was almost entirely re-written with the ESP8266's resources in mind.
## About This Project
This project allows decoding and encoding of IR signals for controlling Air Conditioners and other devices using ESP8266 or ESP32 boards.

View File

@ -593,7 +593,7 @@ bool mountSpiffs(void) {
bool saveConfig(void) {
debug("Saving the config.");
bool success = false;
DynamicJsonDocument json(kJsonConfigMaxSize);
JsonDocument json;
#if MQTT_ENABLE
json[kMqttServerKey] = MqttServer;
json[kMqttPortKey] = MqttPort;
@ -611,6 +611,8 @@ bool saveConfig(void) {
const String key = KEY_TX_GPIO + String(i);
json[key] = static_cast<int>(txGpioTable[i]);
}
if (json.overflowed())
debug("ERROR: no enough memory to store the entire json document");
if (mountSpiffs()) {
File configFile = FILESYSTEM.open(kConfigFile, "w");
@ -642,7 +644,7 @@ bool loadConfigFile(void) {
std::unique_ptr<char[]> buf(new char[size]);
configFile.readBytes(buf.get(), size);
DynamicJsonDocument json(kJsonConfigMaxSize);
JsonDocument json;
if (!deserializeJson(json, buf.get(), kJsonConfigMaxSize)) {
debug("Json config file parsed ok.");
#if MQTT_ENABLE
@ -1659,11 +1661,13 @@ bool parseStringAndSendAirCon(IRsend *irsend, const decode_type_t irType,
stateSize = inputLength / 2; // Every two hex chars is a byte.
// Use at least the minimum size.
stateSize = std::max(stateSize,
(uint16_t) (kFujitsuAcStateLengthShort - 1));
static_cast<uint16_t>(kFujitsuAcStateLengthShort -
1));
// If we think it isn't a "short" message.
if (stateSize > kFujitsuAcStateLengthShort)
// Then it has to be at least the smaller version of the "normal" size.
stateSize = std::max(stateSize, (uint16_t) (kFujitsuAcStateLength - 1));
stateSize = std::max(stateSize,
static_cast<uint16_t>(kFujitsuAcStateLength - 1));
// Lastly, it should never exceed the maximum "normal" size.
stateSize = std::min(stateSize, kFujitsuAcStateLength);
break;
@ -1675,12 +1679,12 @@ bool parseStringAndSendAirCon(IRsend *irsend, const decode_type_t irType,
stateSize = inputLength / 2; // Every two hex chars is a byte.
// Use at least the minimum size.
stateSize = std::max(stateSize,
(uint16_t) (kHitachiAc3MinStateLength));
static_cast<uint16_t>(kHitachiAc3MinStateLength));
// If we think it isn't a "short" message.
if (stateSize > kHitachiAc3MinStateLength)
// Then it probably the "normal" size.
stateSize = std::max(stateSize,
(uint16_t) (kHitachiAc3StateLength));
static_cast<uint16_t>(kHitachiAc3StateLength));
// Lastly, it should never exceed the maximum "normal" size.
stateSize = std::min(stateSize, kHitachiAc3StateLength);
break;
@ -1691,7 +1695,7 @@ bool parseStringAndSendAirCon(IRsend *irsend, const decode_type_t irType,
// the correct length/byte size.
stateSize = inputLength / 2; // Every two hex chars is a byte.
// Use at least the minimum size.
stateSize = std::max(stateSize, (uint16_t) 3);
stateSize = std::max(stateSize, static_cast<uint16_t>(3));
// Cap the maximum size.
stateSize = std::min(stateSize, kStateSizeMax);
break;
@ -1702,12 +1706,13 @@ bool parseStringAndSendAirCon(IRsend *irsend, const decode_type_t irType,
// the correct length/byte size.
stateSize = inputLength / 2; // Every two hex chars is a byte.
// Use at least the minimum size.
stateSize = std::max(stateSize, (uint16_t) (kSamsungAcStateLength));
stateSize = std::max(stateSize,
static_cast<uint16_t>(kSamsungAcStateLength));
// If we think it isn't a "normal" message.
if (stateSize > kSamsungAcStateLength)
// Then it probably the extended size.
stateSize = std::max(stateSize,
(uint16_t) (kSamsungAcExtendedStateLength));
stateSize = std::max(
stateSize, static_cast<uint16_t>(kSamsungAcExtendedStateLength));
// Lastly, it should never exceed the maximum "extended" size.
stateSize = std::min(stateSize, kSamsungAcExtendedStateLength);
break;
@ -2671,7 +2676,8 @@ void receivingMQTT(String const topic_name, String const callback_str) {
switch (ircommand[0]) {
case kPauseChar:
{ // It's a pause. Everything after the 'P' should be a number.
int32_t msecs = std::min((int32_t) strtoul(ircommand + 1, NULL, 10),
int32_t msecs = std::min(static_cast<int32_t>(strtoul(ircommand + 1,
NULL, 10)),
kMaxPauseMs);
delay(msecs);
mqtt_client.publish(MqttAck.c_str(),
@ -3142,7 +3148,7 @@ bool sendFloat(const String topic, const float_t temp, const bool retain) {
#if MQTT_CLIMATE_JSON
void sendJsonState(const stdAc::state_t state, const String topic,
const bool retain, const bool ha_mode) {
DynamicJsonDocument json(kJsonAcStateMaxSize);
JsonDocument json;
json[KEY_PROTOCOL] = typeToString(state.protocol);
json[KEY_MODEL] = state.model;
json[KEY_COMMAND] = IRac::commandToString(state.command);
@ -3168,6 +3174,8 @@ void sendJsonState(const stdAc::state_t state, const String topic,
json[KEY_CLEAN] = IRac::boolToString(state.clean);
json[KEY_BEEP] = IRac::boolToString(state.beep);
json[KEY_SLEEP] = state.sleep;
if (json.overflowed())
debug("ERROR: no enough memory to store the entire json document");
String payload = "";
payload.reserve(200);
@ -3175,16 +3183,16 @@ void sendJsonState(const stdAc::state_t state, const String topic,
sendString(topic, payload, retain);
}
bool validJsonStr(DynamicJsonDocument doc, const char* key) {
bool validJsonStr(JsonDocument doc, const char* key) {
return doc.containsKey(key) && doc[key].is<char*>();
}
bool validJsonInt(DynamicJsonDocument doc, const char* key) {
bool validJsonInt(JsonDocument doc, const char* key) {
return doc.containsKey(key) && doc[key].is<signed int>();
}
stdAc::state_t jsonToState(const stdAc::state_t current, const char *str) {
DynamicJsonDocument json(kJsonAcStateMaxSize);
JsonDocument json;
if (deserializeJson(json, str, kJsonAcStateMaxSize)) {
debug("json MQTT message did not parse. Skipping!");
return current;

View File

@ -14,7 +14,7 @@ monitor_speed = 115200
lib_deps_builtin =
lib_deps_external =
PubSubClient@>=2.8.0
ArduinoJson@>=6.0
ArduinoJson@>=7.0
# Uncomment the following to enable SHT-3x support.
# https://github.com/wemos/WEMOS_SHT3x_Arduino_Library.git

View File

@ -87,7 +87,7 @@ void dump(decode_results *results) {
Serial.print(results->rawbuf[i] * kRawTick, DEC);
} else {
Serial.print(", ");
Serial.print((uint32_t) results->rawbuf[i] * kRawTick, DEC);
Serial.print(static_cast<uint32_t>(results->rawbuf[i] * kRawTick), DEC);
}
}
Serial.println("};");

View File

@ -130,6 +130,8 @@ decode_results results; // Somewhere to store the results
void setup() {
#if defined(ESP8266)
Serial.begin(kBaudRate, SERIAL_8N1, SERIAL_TX_ONLY);
#elif ARDUINO_USB_CDC_ON_BOOT
Serial.begin(kBaudRate);
#else // ESP8266
Serial.begin(kBaudRate, SERIAL_8N1);
#endif // ESP8266

View File

@ -138,6 +138,8 @@ void setup() {
OTAwifi(); // start default wifi (previously saved on the ESP) for OTA
#if defined(ESP8266)
Serial.begin(kBaudRate, SERIAL_8N1, SERIAL_TX_ONLY);
#elif ARDUINO_USB_CDC_ON_BOOT
Serial.begin(kBaudRate);
#else // ESP8266
Serial.begin(kBaudRate, SERIAL_8N1);
#endif // ESP8266

View File

@ -46,5 +46,6 @@
],
"exclude": [".github", "extras", "docs", "assets"],
"frameworks": "arduino",
"libCompatMode": "strict",
"platforms": ["espressif8266", "espressif32"]
}

View File

@ -13,6 +13,7 @@
#include <string>
#endif
#include <cmath>
#include <memory>
#if __cplusplus >= 201103L && defined(_GLIBCXX_USE_C99_MATH_TR1)
using std::roundf;
#else
@ -33,6 +34,7 @@
#include "ir_Daikin.h"
#include "ir_Ecoclim.h"
#include "ir_Electra.h"
#include "ir_Eurom.h"
#include "ir_Fujitsu.h"
#include "ir_Haier.h"
#include "ir_Hitachi.h"
@ -243,6 +245,9 @@ bool IRac::isProtocolSupported(const decode_type_t protocol) {
#if SEND_ELECTRA_AC
case decode_type_t::ELECTRA_AC:
#endif
#if SEND_EUROM
case decode_type_t::EUROM:
#endif
#if SEND_FUJITSU_AC
case decode_type_t::FUJITSU_AC:
#endif
@ -402,10 +407,8 @@ void IRac::airton(IRAirtonAc *ac,
const int16_t sleep) {
ac->begin();
ac->setPower(on);
ac->setTemp(degrees);
// Mode needs to be set after temp as Fan-only uses a special temp.
ac->setMode(ac->convertMode(mode));
// Fan needs to be set after mode, as setMode can change the fan speed.
ac->setTemp(degrees);
ac->setFan(ac->convertFan(fan));
ac->setSwingV(swingv != stdAc::swingv_t::kOff);
// No Quiet setting available.
@ -592,13 +595,19 @@ void IRac::argoWrem3_ConfigSet(IRArgoAC_WREM3 *ac, const uint8_t param,
if (safe) {
switch (param) {
case 5: // temp. scale (note this is likely excess as not transmitted)
if (value > 1) { return; /* invalid */ }
if (value > 1) {
return; /* invalid */
}
break;
case 6: // channel (note this is likely excess as not transmitted)
if (value > 3) { return; /* invalid */ }
if (value > 3) {
return; /* invalid */
}
break;
case 12: // eco power limit
if (value < 30 || value > 99) { return; /* invalid */ }
if (value < 30 || value > 99) {
return; /* invalid */
}
break;
default:
return; /* invalid */
@ -738,8 +747,10 @@ void IRac::coolix(IRCoolixAC *ac,
ac->send();
return;
}
ac->setMode(ac->convertMode(mode));
ac->setTemp(degrees);
// Mode needs to be set after temp as Fan-only uses a special temp.
ac->setMode(ac->convertMode(mode));
// Fan needs to be set after mode, as setMode can change the fan speed.
ac->setFan(ac->convertFan(fan));
// No Filter setting available.
// No Beep setting available.
@ -1169,6 +1180,7 @@ void IRac::ecoclim(IREcoclimAc *ac,
/// @param[in] swingv The vertical swing setting.
/// @param[in] swingh The horizontal swing setting.
/// @param[in] iFeel Whether to enable iFeel (remote temp) mode on the A/C unit.
/// @param[in] quiet Run the device in quiet/silent mode.
/// @param[in] turbo Run the device in turbo/powerful mode.
/// @param[in] lighttoggle Should we toggle the LED/Display?
/// @param[in] clean Turn on the self-cleaning mode. e.g. Mould, dry filters etc
@ -1177,7 +1189,8 @@ void IRac::electra(IRElectraAc *ac,
const float degrees, const float sensorTemp,
const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv,
const stdAc::swingh_t swingh, const bool iFeel,
const bool turbo, const bool lighttoggle, const bool clean) {
const bool quiet, const bool turbo, const bool lighttoggle,
const bool clean) {
ac->begin();
ac->setPower(on);
ac->setMode(ac->convertMode(mode));
@ -1188,7 +1201,7 @@ void IRac::electra(IRElectraAc *ac,
ac->setFan(ac->convertFan(fan));
ac->setSwingV(swingv != stdAc::swingv_t::kOff);
ac->setSwingH(swingh != stdAc::swingh_t::kOff);
// No Quiet setting available.
ac->setQuiet(quiet);
ac->setTurbo(turbo);
ac->setLightToggle(lighttoggle);
// No Econo setting available.
@ -1202,6 +1215,31 @@ void IRac::electra(IRElectraAc *ac,
}
#endif // SEND_ELECTRA_AC
#if SEND_EUROM
/// Send an Eurom A/C message with the supplied settings.
/// @param[in, out] ac A Ptr to an IREuromAc object to use.
/// @param[in] power The power setting.
/// @param[in] mode The operation mode setting.
/// @param[in] degrees The temperature setting in degrees, normally Celsius.
/// @param[in] fahrenheit If the given temperature is in Fahrenheit instead.
/// @param[in] fan The speed setting for the fan.
/// @param[in] swingv The swing setting.
/// @param[in] sleep The sleep mode setting.
void IRac::eurom(IREuromAc *ac, const bool power, const stdAc::opmode_t mode,
const float degrees, const bool fahrenheit,
const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv,
const bool sleep) {
ac->begin();
ac->setPower(power);
ac->setMode(ac->convertMode(mode));
ac->setTemp(degrees, fahrenheit);
ac->setFan(ac->convertFan(fan));
ac->setSwing(ac->convertSwing(swingv));
ac->setSleep(sleep);
ac->send();
}
#endif // SEND_EUROM
#if SEND_FUJITSU_AC
/// Send a Fujitsu A/C message with the supplied settings.
/// @param[in, out] ac A Ptr to an IRFujitsuAC object to use.
@ -3225,11 +3263,20 @@ bool IRac::sendAc(const stdAc::state_t desired, const stdAc::state_t *prev) {
{
IRElectraAc ac(_pin, _inverted, _modulation);
electra(&ac, send.power, send.mode, degC, sensorTempC, send.fanspeed,
send.swingv, send.swingh, send.iFeel, send.turbo, send.light,
send.clean);
send.swingv, send.swingh, send.iFeel, send.quiet, send.turbo,
send.light, send.clean);
break;
}
#endif // SEND_ELECTRA_AC
#if SEND_EUROM
case EUROM:
{
IREuromAc ac(_pin, _inverted, _modulation);
eurom(&ac, send.power, send.mode, send.degrees, !send.celsius,
send.fanspeed, send.swingv, send.sleep);
break;
}
#endif // SEND_EUROM
#if SEND_FUJITSU_AC
case FUJITSU_AC:
{
@ -3277,8 +3324,8 @@ bool IRac::sendAc(const stdAc::state_t desired, const stdAc::state_t *prev) {
{
IRHaierAC160 ac(_pin, _inverted, _modulation);
haier160(&ac, send.power, send.mode, send.celsius, send.degrees,
send.fanspeed, send.swingv, send.turbo, send.filter, send.clean,
send.light, prev_light, send.sleep);
send.fanspeed, send.swingv, send.turbo, send.quiet,
send.filter, send.clean, send.light, prev_light, send.sleep);
break;
}
#endif // SEND_HAIER_AC160
@ -3288,7 +3335,8 @@ bool IRac::sendAc(const stdAc::state_t desired, const stdAc::state_t *prev) {
IRHaierAC176 ac(_pin, _inverted, _modulation);
haier176(&ac, (haier_ac176_remote_model_t)send.model, send.power,
send.mode, send.celsius, send.degrees, send.fanspeed,
send.swingv, send.swingh, send.turbo, send.filter, send.sleep);
send.swingv, send.swingh, send.turbo, send.quiet, send.filter,
send.sleep);
break;
}
#endif // SEND_HAIER_AC176
@ -3298,7 +3346,7 @@ bool IRac::sendAc(const stdAc::state_t desired, const stdAc::state_t *prev) {
IRHaierACYRW02 ac(_pin, _inverted, _modulation);
haierYrwo2(&ac, send.power, send.mode, send.celsius, send.degrees,
send.fanspeed, send.swingv, send.swingh, send.turbo,
send.filter, send.sleep);
send.quiet, send.filter, send.sleep);
break;
}
#endif // SEND_HAIER_AC_YRW02
@ -4118,6 +4166,15 @@ namespace IRAcUtils {
return ac.toString();
}
#endif // DECODE_COOLIX
#if DECODE_COOLIX48
case decode_type_t::COOLIX48: {
IRCoolixAC ac(kGpioUnused);
ac.on();
// Coolix uses value instead of state.
ac.setRawFromCoolix48(result->value);
return ac.toString();
}
#endif // DECODE_COOLIX
#if DECODE_CORONA_AC
case decode_type_t::CORONA_AC: {
IRCoronaAc ac(kGpioUnused);
@ -4205,6 +4262,13 @@ namespace IRAcUtils {
return ac.toString();
}
#endif // DECODE_ELECTRA_AC
#if DECODE_EUROM
case decode_type_t::EUROM: {
IREuromAc ac(kGpioUnused);
ac.setRaw(result->state);
return ac.toString();
}
#endif // DECODE_EUROM
#if DECODE_FUJITSU_AC
case decode_type_t::FUJITSU_AC: {
IRFujitsuAC ac(kGpioUnused);
@ -4705,6 +4769,14 @@ namespace IRAcUtils {
break;
}
#endif // DECODE_ELECTRA_AC
#if DECODE_EUROM
case decode_type_t::EUROM: {
IREuromAc ac(kGpioUnused);
ac.setRaw(decode->state);
*result = ac.toCommon();
break;
}
#endif // DECODE_EUROM
#if DECODE_FUJITSU_AC
case decode_type_t::FUJITSU_AC: {
IRFujitsuAC ac(kGpioUnused);

View File

@ -22,6 +22,7 @@
#include "ir_Fujitsu.h"
#include "ir_Ecoclim.h"
#include "ir_Electra.h"
#include "ir_Eurom.h"
#include "ir_Goodweather.h"
#include "ir_Gree.h"
#include "ir_Haier.h"
@ -264,9 +265,15 @@ void electra(IRElectraAc *ac,
const bool on, const stdAc::opmode_t mode,
const float degrees, const float sensorTemp,
const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv,
const stdAc::swingh_t swingh, const bool iFeel, const bool turbo,
const bool lighttoggle, const bool clean);
const stdAc::swingh_t swingh, const bool iFeel, const bool quiet,
const bool turbo, const bool lighttoggle, const bool clean);
#endif // SEND_ELECTRA_AC
#if SEND_EUROM
void eurom(IREuromAc *ac, const bool power, const stdAc::opmode_t mode,
const float degrees, const bool fahrenheit,
const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv,
const bool sleep);
#endif // SEND_EUROM
#if SEND_FUJITSU_AC
void fujitsu(IRFujitsuAC *ac, const fujitsu_ac_remote_model_t model,
const bool on, const stdAc::opmode_t mode,

View File

@ -13,11 +13,6 @@ extern "C" {
}
#endif // ESP8266
#include <Arduino.h>
#if defined(ESP32)
#if ( defined(ESP_ARDUINO_VERSION_MAJOR) && (ESP_ARDUINO_VERSION_MAJOR >= 3) )
#include <driver/gpio.h>
#endif // ESP_ARDUINO_VERSION_MAJOR >= 3
#endif
#endif // UNIT_TEST
#include <algorithm>
#ifdef UNIT_TEST
@ -61,20 +56,24 @@ static ETSTimer timer;
} // namespace _IRrecv
#endif // ESP8266
#if defined(ESP32)
#if ( defined(ESP_ARDUINO_VERSION) && \
(ESP_ARDUINO_VERSION >= ESP_ARDUINO_VERSION_VAL(3, 0, 0)) )
#define _ESP32_ARDUINO_CORE_V3PLUS
#endif // ESP_ARDUINO_VERSION >= 3
// We need a horrible timer hack for ESP32 Arduino framework < v2.0.0
#if !defined(_ESP32_IRRECV_TIMER_HACK)
#if !defined(_ESP32_ARDUINO_CORE_V2PLUS)
// 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
#define _ESP32_ARDUINO_CORE_V2PLUS 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
#define _ESP32_ARDUINO_CORE_V2PLUS true
#endif // Version check
#endif // !defined(_ESP32_IRRECV_TIMER_HACK)
#endif // !defined(_ESP32_ARDUINO_CORE_V2PLUS)
#if _ESP32_IRRECV_TIMER_HACK
#if _ESP32_ARDUINO_CORE_V2PLUS
// 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
@ -136,7 +135,7 @@ typedef struct hw_timer_s {
uint8_t timer;
portMUX_TYPE lock;
} hw_timer_t;
#endif // _ESP32_IRRECV_TIMER_HACK / End of Horrible Hack.
#endif // _ESP32_ARDUINO_CORE_V2PLUS / End of Horrible Hack.
namespace _IRrecv {
static hw_timer_t *timer = NULL;
@ -230,31 +229,31 @@ 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:
#if _ESP32_ARDUINO_CORE_V2PLUS
// The following three lines of code are the equivalent of:
// `timerWrite(timer, 0);`
// We can't call that routine safely from inside an ISR as that procedure
// is not stored in IRAM. Hence, we do it manually so that it's covered by
// USE_IRAM_ATTR in this ISR.
// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1350
// @see https://github.com/espressif/arduino-esp32/blob/6b0114366baf986c155e8173ab7c22bc0c5fcedc/cores/esp32/esp32-hal-timer.c#L106-L110
timer->dev->load_high = (uint32_t) 0;
timer->dev->load_low = (uint32_t) 0;
timer->dev->load_high = static_cast<uint32_t>(0);
timer->dev->load_low = static_cast<uint32_t>(0);
timer->dev->reload = 1;
// The next line is the same, but instead replaces:
// `timerAlarmEnable(timer);`
// @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
#if ( defined(ESP_ARDUINO_VERSION_MAJOR) && (ESP_ARDUINO_VERSION_MAJOR >= 3) )
#elif defined(_ESP32_ARDUINO_CORE_V3PLUS)
// For ESP32 core version 3.x, replace `timerAlarmEnable`
timerWrite(timer, 0);
timerStart(timer);
#else // ESP_ARDUINO_VERSION_MAJOR >= 3
uint64_t alarm_value = 50000; // Example value (50ms)
timerAlarm(timer, alarm_value, false, 0);
#else // !_ESP32_ARDUINO_CORE_V3PLUS
timerWrite(timer, 0);
timerAlarmEnable(timer);
#endif // ESP_ARDUINO_VERSION_MAJOR >= 3
#endif // _ESP32_IRRECV_TIMER_HACK
#endif // _ESP32_ARDUINO_CORE_V2PLUS
#endif // ESP32
}
#endif // UNIT_TEST
@ -344,10 +343,7 @@ IRrecv::IRrecv(const uint16_t recvpin, const uint16_t bufsize,
IRrecv::~IRrecv(void) {
disableIRIn();
#if defined(ESP32)
if (timer != NULL) {
timerEnd(timer); // Cleanup the ESP32 timeout timer.
timer = NULL;
}
if (timer != NULL) timerEnd(timer); // Cleanup the ESP32 timeout timer.
#endif // ESP32
delete[] params.rawbuf;
if (params_save != NULL) {
@ -371,12 +367,15 @@ void IRrecv::enableIRIn(const bool pullup) {
}
#if defined(ESP32)
// Initialise the ESP32 timer.
#if defined(_ESP32_ARDUINO_CORE_V3PLUS)
// Use newer timerBegin signature for ESP32 core version 3.x
timer = timerBegin(1000000); // Initialize with 1MHz (1us per tick)
#else // _ESP32_ARDUINO_CORE_V3PLUS
// 80MHz / 80 = 1 uSec granularity.
#if ( defined(ESP_ARDUINO_VERSION_MAJOR) && (ESP_ARDUINO_VERSION_MAJOR >= 3) )
timer = timerBegin(1000000); // 1 MHz
#else // ESP_ARDUINO_VERSION_MAJOR >= 3
timer = timerBegin(_timer_num, 80, true); // 1 MHz : 80 MHz with divider 80
#endif // ESP_ARDUINO_VERSION_MAJOR >= 3
timer = timerBegin(_timer_num, 80, true);
#endif // _ESP32_ARDUINO_CORE_V3PLUS
// Ensure the timer is successfully initialized
#ifdef DEBUG
if (timer == NULL) {
DPRINT("FATAL: Unable enable system timer: ");
@ -384,17 +383,17 @@ void IRrecv::enableIRIn(const bool pullup) {
}
#endif // DEBUG
assert(timer != NULL); // Check we actually got the timer.
#if ( defined(ESP_ARDUINO_VERSION_MAJOR) && (ESP_ARDUINO_VERSION_MAJOR >= 3) )
// Set the timer so it only fires once, and set its trigger in microseconds.
#if defined(_ESP32_ARDUINO_CORE_V3PLUS)
timerWrite(timer, 0); // Reset the timer for ESP32 core version 3.x
timerAttachInterrupt(timer, &read_timeout);
timerAlarm(timer, MS_TO_USEC(params.timeout), ONCE, 0);
#else // ESP_ARDUINO_VERSION_MAJOR >= 3
// Set the timer so it only fires once, and set it's trigger in uSeconds.
#else // _ESP32_ARDUINO_CORE_V3PLUS
timerAlarmWrite(timer, MS_TO_USEC(params.timeout), ONCE);
// Note: Interrupt needs to be attached before it can be enabled or disabled.
// 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 // ESP_ARDUINO_VERSION_MAJOR >= 3
#endif // _ESP32_ARDUINO_CORE_V3PLUS
#endif // ESP32
// Initialise state machine variables
@ -418,16 +417,14 @@ void IRrecv::disableIRIn(void) {
#ifndef UNIT_TEST
#if defined(ESP8266)
os_timer_disarm(&timer);
#endif // ESP8266
#if defined(ESP32)
#if ( defined(ESP_ARDUINO_VERSION_MAJOR) && (ESP_ARDUINO_VERSION_MAJOR >= 3) )
#elif defined(_ESP32_ARDUINO_CORE_V3PLUS)
timerWrite(timer, 0); // Reset the timer
timerDetachInterrupt(timer);
timerEnd(timer);
#else // ESP_ARDUINO_VERSION_MAJOR >= 3
#elif defined(ESP32)
timerAlarmDisable(timer);
timerDetachInterrupt(timer);
timerEnd(timer);
#endif // ESP_ARDUINO_VERSION_MAJOR >= 3
#endif // ESP32
detachInterrupt(params.recvpin);
#endif // UNIT_TEST
@ -453,11 +450,13 @@ void IRrecv::resume(void) {
params.rawlen = 0;
params.overflow = false;
#if defined(ESP32)
#if ( defined(ESP_ARDUINO_VERSION_MAJOR) && (ESP_ARDUINO_VERSION_MAJOR >= 3) )
timerStop(timer);
#else // ESP_ARDUINO_VERSION_MAJOR >= 3
// Check for ESP32 core version and handle timer functions differently
#if defined(_ESP32_ARDUINO_CORE_V3PLUS)
timerWrite(timer, 0); // Reset the timer (no need for timerAlarmDisable)
#else // _ESP32_ARDUINO_CORE_V3PLUS
timerAlarmDisable(timer);
#endif // ESP_ARDUINO_VERSION_MAJOR >= 3
#endif // _ESP32_ARDUINO_CORE_V3PLUS
// Re-enable GPIO interrupt in both versions
gpio_intr_enable((gpio_num_t)params.recvpin);
#endif // ESP32
}
@ -1216,6 +1215,14 @@ bool IRrecv::decode(decode_results *results, irparams_t *save,
DPRINTLN("Attempting York decode");
if (decodeYork(results, offset, kYorkBits)) return true;
#endif // DECODE_YORK
#if DECODE_BLUESTARHEAVY
DPRINTLN("Attempting BluestarHeavy decode");
if (decodeBluestarHeavy(results, offset, kBluestarHeavyBits)) return true;
#endif // DECODE_BLUESTARHEAVY
#if DECODE_EUROM
DPRINTLN("Attempting Eurom decode");
if (decodeEurom(results, offset, kEuromBits)) return true;
#endif // DECODE_EUROM
// Typically new protocols are added above this line.
}
#if DECODE_HASH
@ -1246,9 +1253,10 @@ uint8_t IRrecv::_validTolerance(const uint8_t percentage) {
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 - _validTolerance(tolerance) / 100.0) - delta),
(int32_t)0));
return (static_cast<uint32_t>(std::max(
static_cast<int32_t>(usecs * (1.0 - _validTolerance(tolerance) / 100.0) -
delta),
static_cast<int32_t>(0))));
}
/// Calculate the upper bound of the nr. of ticks.
@ -1258,8 +1266,8 @@ uint32_t IRrecv::ticksLow(const uint32_t usecs, const uint8_t tolerance,
/// @return Nr. of ticks.
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);
return (static_cast<uint32_t>(usecs * (1.0 + _validTolerance(tolerance) /
100.0)) + 1 + delta);
}
/// Check if we match a pulse(measured) with the desired within
@ -1310,7 +1318,8 @@ bool IRrecv::matchAtLeast(uint32_t measured, uint32_t desired,
DPRINT(". Matching: ");
DPRINT(measured);
DPRINT(" >= ");
DPRINT(ticksLow(std::min(desired, (uint32_t)MS_TO_USEC(params.timeout)),
DPRINT(ticksLow(std::min(desired,
static_cast<uint32_t>(MS_TO_USEC(params.timeout))),
tolerance, delta));
DPRINT(" [min(");
DPRINT(ticksLow(desired, tolerance, delta));
@ -1331,9 +1340,9 @@ 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,
(uint32_t)MS_TO_USEC(params.timeout)),
tolerance, delta);
return measured >= ticksLow(std::min(
desired, static_cast<uint32_t>(MS_TO_USEC(params.timeout))), tolerance,
delta);
}
/// Check if we match a mark signal(measured) with the desired within

View File

@ -879,10 +879,22 @@ class IRrecv {
#endif // DECODE_WOWWEE
#if DECODE_YORK
bool decodeYork(decode_results *results,
uint16_t kStartOffset,
const uint16_t kYorkBits,
uint16_t offset = kStartOffset,
const uint16_t nbits = kYorkBits,
const bool strict = true);
#endif // DECODE_YORK
#if DECODE_BLUESTARHEAVY
bool decodeBluestarHeavy(decode_results *results,
uint16_t offset = kStartOffset,
const uint16_t nbits = kBluestarHeavyBits,
const bool strict = true);
#endif // DECODE_BLUESTARHEAVY
#if DECODE_EUROM
bool decodeEurom(decode_results *results,
uint16_t offset = kStartOffset,
const uint16_t nbits = kEuromBits,
const bool strict = true);
#endif // DECODE_EUROM
};
#endif // IRRECV_H_

View File

@ -952,6 +952,20 @@
#define SEND_YORK _IR_ENABLE_DEFAULT_
#endif // SEND_YORK
#ifndef DECODE_BLUESTARHEAVY
#define DECODE_BLUESTARHEAVY _IR_ENABLE_DEFAULT_
#endif // DECODE_BLUESTARHEAVY
#ifndef SEND_BLUESTARHEAVY
#define SEND_BLUESTARHEAVY _IR_ENABLE_DEFAULT_
#endif // SEND_BLUESTARHEAVY
#ifndef DECODE_EUROM
#define DECODE_EUROM _IR_ENABLE_DEFAULT_
#endif // DECODE_EUROM
#ifndef SEND_EUROM
#define SEND_EUROM _IR_ENABLE_DEFAULT_
#endif // SEND_EUROM
#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 || \
@ -970,7 +984,8 @@
DECODE_KELON168 || DECODE_HITACHI_AC296 || DECODE_CARRIER_AC128 || \
DECODE_DAIKIN200 || DECODE_HAIER_AC160 || DECODE_TCL96AC || \
DECODE_BOSCH144 || DECODE_SANYO_AC152 || DECODE_DAIKIN312 || \
DECODE_CARRIER_AC84 || DECODE_YORK || \
DECODE_CARRIER_AC84 || DECODE_YORK || DECODE_BLUESTARHEAVY || \
DECODE_EUROM || \
false)
// Add any DECODE to the above if it uses result->state (see kStateSizeMax)
// you might also want to add the protocol to hasACState function
@ -1137,8 +1152,10 @@ enum decode_type_t {
WOWWEE,
CARRIER_AC84, // 125
YORK,
BLUESTARHEAVY,
EUROM,
// Add new entries before this one, and update it to point to the last entry.
kLastDecodeType = YORK,
kLastDecodeType = EUROM,
};
// Message lengths & required repeat values
@ -1165,6 +1182,8 @@ const uint16_t kArgo3TimerStateLength = 9; // Bytes
const uint16_t kArgo3ConfigStateLength = 4; // Bytes
const uint16_t kArgoDefaultRepeat = kNoRepeat;
const uint16_t kArrisBits = 32;
const uint16_t kBluestarHeavyStateLength = 13;
const uint16_t kBluestarHeavyBits = kBluestarHeavyStateLength * 8;
const uint16_t kBosch144StateLength = 18;
const uint16_t kBosch144Bits = kBosch144StateLength * 8;
const uint16_t kCoolixBits = 24;
@ -1435,7 +1454,8 @@ const uint16_t kRhossDefaultRepeat = 0;
const uint16_t kClimaButlerBits = 52;
const uint16_t kYorkBits = 136;
const uint16_t kYorkStateLength = 17;
const uint16_t kEuromStateLength = 12;
const uint16_t kEuromBits = kEuromStateLength * 8;
// Legacy defines. (Deprecated)
#define AIWA_RC_T501_BITS kAiwaRcT501Bits
@ -1498,12 +1518,24 @@ const uint16_t kYorkStateLength = 17;
#ifdef DEBUG
#ifdef UNIT_TEST
#define DPRINT(x) do { std::cout << x; } while (0)
#define DPRINTLN(x) do { std::cout << x << std::endl; } while (0)
#define DPRINT(x) do { \
std::cout << x; \
} \
while (0)
#define DPRINTLN(x) do { \
std::cout << x << std::endl; \
} \
while (0)
#endif // UNIT_TEST
#ifdef ARDUINO
#define DPRINT(x) do { Serial.print(x); } while (0)
#define DPRINTLN(x) do { Serial.println(x); } while (0)
#define DPRINT(x) do { \
Serial.print(x); \
} \
while (0)
#define DPRINTLN(x) do { \
Serial.println(x); \
} \
while (0)
#endif // ARDUINO
#else // DEBUG
#define DPRINT(x)

View File

@ -74,9 +74,9 @@ uint32_t IRsend::calcUSecPeriod(uint32_t hz, bool use_offset) {
(1000000UL + hz / 2) / hz; // The equiv of round(1000000/hz).
// Apply the offset and ensure we don't result in a <= 0 value.
if (use_offset)
return std::max((uint32_t)1, period + periodOffset);
return std::max(static_cast<uint32_t>(1), period + periodOffset);
else
return std::max((uint32_t)1, period);
return std::max(static_cast<uint32_t>(1), period);
}
/// Set the output frequency modulation and duty cycle.
@ -174,14 +174,16 @@ uint16_t IRsend::mark(uint16_t usec) {
ledOn();
// Calculate how long we should pulse on for.
// e.g. Are we to close to the end of our requested mark time (usec)?
_delayMicroseconds(std::min((uint32_t)onTimePeriod, usec - elapsed));
_delayMicroseconds(std::min(static_cast<uint32_t>(onTimePeriod),
usec - elapsed));
ledOff();
counter++;
if (elapsed + onTimePeriod >= usec)
return counter; // LED is now off & we've passed our allotted time.
// Wait for the lesser of the rest of the duty cycle, or the time remaining.
_delayMicroseconds(
std::min(usec - elapsed - onTimePeriod, (uint32_t)offTimePeriod));
std::min(usec - elapsed - onTimePeriod,
static_cast<uint32_t>(offTimePeriod)));
elapsed = usecTimer.elapsed(); // Update & recache the actual elapsed time.
}
return counter;
@ -214,7 +216,7 @@ int8_t IRsend::calibrate(uint16_t hz) {
uint32_t timeTaken = usecTimer.elapsed(); // Record the time it took.
// While it shouldn't be necessary, assume at least 1 pulse, to avoid a
// divide by 0 situation.
pulses = std::max(pulses, (uint16_t)1U);
pulses = std::max(pulses, static_cast<uint16_t>(1U));
uint32_t calcPeriod = calcUSecPeriod(hz); // e.g. @38kHz it should be 26us.
// Assuming 38kHz for the example calculations:
// In a 65535us pulse, we should have 2520.5769 pulses @ 26us periods.
@ -719,6 +721,8 @@ uint16_t IRsend::defaultBits(const decode_type_t protocol) {
return kDaikin64Bits;
case ELECTRA_AC:
return kElectraAcBits;
case EUROM:
return kEuromBits;
case GREE:
return kGreeBits;
case HAIER_AC:
@ -798,6 +802,8 @@ uint16_t IRsend::defaultBits(const decode_type_t protocol) {
return kXmpBits;
case YORK:
return kYorkBits;
case BLUESTARHEAVY:
return kBluestarHeavyBits;
// No default amount of bits.
case FUJITSU_AC:
case MWM:
@ -1241,6 +1247,11 @@ bool IRsend::send(const decode_type_t type, const uint8_t *state,
sendElectraAC(state, nbytes);
break;
#endif // SEND_ELECTRA_AC
#if SEND_EUROM
case EUROM:
sendEurom(state, nbytes);
break;
#endif // SEND_EUROM
#if SEND_FUJITSU_AC
case FUJITSU_AC:
sendFujitsuAC(state, nbytes);
@ -1434,6 +1445,11 @@ bool IRsend::send(const decode_type_t type, const uint8_t *state,
sendYork(state, nbytes);
break;
#endif // SEND_YORK
#if SEND_BLUESTARHEAVY
case BLUESTARHEAVY:
sendBluestarHeavy(state, nbytes);
break;
#endif // SEND_BLUESTARHEAVY
default:
return false;
}

View File

@ -228,6 +228,13 @@ enum argo_ac_remote_model_t {
SAC_WREM3 // (2) ARGO WREM3 remote (touch buttons), bit-len vary by cmd
};
/// Toshiba A/C model numbers
enum toshiba_ac_remote_model_t {
kToshibaGenericRemote_A = 0, // Default from existing codebase
kToshibaGenericRemote_B = 1, // Newly discovered remote control b, applies to
// many remote models such as WA-TH03A, WA-TH04A etc.
};
// Classes
/// Class for sending all basic IR protocols.
@ -619,11 +626,13 @@ class IRsend {
uint16_t nbytes = kCarrierAc128StateLength,
uint16_t repeat = kCarrierAc128MinRepeat);
#endif // SEND_CARRIER_AC128
#if (SEND_HAIER_AC || SEND_HAIER_AC_YRW02 || SEND_HAIER_AC176)
#if (SEND_HAIER_AC || SEND_HAIER_AC_YRW02 || SEND_HAIER_AC160 || \
SEND_HAIER_AC176)
void sendHaierAC(const unsigned char data[],
const uint16_t nbytes = kHaierACStateLength,
const uint16_t repeat = kHaierAcDefaultRepeat);
#endif // (SEND_HAIER_AC || SEND_HAIER_AC_YRW02 || SEND_HAIER_AC176)
#endif // (SEND_HAIER_AC || SEND_HAIER_AC_YRW02 || SEND_HAIER_AC160 ||
// SEND_HAIER_AC176)
#if SEND_HAIER_AC_YRW02
void sendHaierACYRW02(const unsigned char data[],
const uint16_t nbytes = kHaierACYRW02StateLength,
@ -885,6 +894,16 @@ class IRsend {
const uint16_t nbytes = kYorkStateLength,
const uint16_t repeat = kNoRepeat);
#endif // SEND_YORK
#if SEND_BLUESTARHEAVY
void sendBluestarHeavy(const unsigned char data[],
const uint16_t nbytes = kBluestarHeavyStateLength,
const uint16_t repeat = kNoRepeat);
#endif // SEND_BLUESTARHEAVY
#if SEND_EUROM
void sendEurom(const uint8_t data[],
const uint16_t nbytes = kEuromStateLength,
const uint16_t repeat = kNoRepeat);
#endif // SEND_EUROM
protected:
#ifdef UNIT_TEST

View File

@ -295,6 +295,10 @@ IRTEXT_CONST_STRING(kDg11j104Str, D_STR_DG11J104); ///< "DG11J104"
IRTEXT_CONST_STRING(kDg11j191Str, D_STR_DG11J191); ///< "DG11J191"
IRTEXT_CONST_STRING(kArgoWrem2Str, D_STR_ARGO_WREM2); ///< "WREM3"
IRTEXT_CONST_STRING(kArgoWrem3Str, D_STR_ARGO_WREM3); ///< "WREM3"
IRTEXT_CONST_STRING(kToshibaGenericRemoteAStr, D_STR_TOSHIBAGENERICREMOTEA);
// "TOSHIBA REMOTE A"
IRTEXT_CONST_STRING(kToshibaGenericRemoteBStr, D_STR_TOSHIBAGENERICREMOTEB);
// "TOSHIBA REMOTE B"
#define D_STR_UNSUPPORTED "?" // Unsupported protocols will be showing as
// a question mark, check for length > 1
@ -555,6 +559,10 @@ IRTEXT_CONST_BLOB_DECL(kAllProtocolNamesStr) {
D_STR_CARRIER_AC84, D_STR_UNSUPPORTED) "\x0"
COND(DECODE_YORK || SEND_YORK,
D_STR_YORK, D_STR_UNSUPPORTED) "\x0"
COND(DECODE_BLUESTARHEAVY || SEND_BLUESTARHEAVY,
D_STR_BLUESTARHEAVY, D_STR_UNSUPPORTED) "\x0"
COND(DECODE_EUROM || SEND_EUROM,
D_STR_EUROM, D_STR_UNSUPPORTED) "\x0"
///< New protocol (macro) strings should be added just above this line.
"\x0" ///< This string requires double null termination.
};

View File

@ -229,6 +229,8 @@ extern IRTEXT_CONST_PTR(kSetTimerCommandStr);
extern IRTEXT_CONST_PTR(kTimerStr);
extern IRTEXT_CONST_PTR(kToggleStr);
extern IRTEXT_CONST_PTR(kTopStr);
extern IRTEXT_CONST_PTR(kToshibaGenericRemoteAStr);
extern IRTEXT_CONST_PTR(kToshibaGenericRemoteBStr);
extern IRTEXT_CONST_PTR(kTrueStr);
extern IRTEXT_CONST_PTR(kTurboStr);
extern IRTEXT_CONST_PTR(kTurboToggleStr);

View File

@ -46,7 +46,7 @@
uint64_t reverseBits(uint64_t input, uint16_t nbits) {
if (nbits <= 1) return input; // Reversing <= 1 bits makes no change at all.
// Cap the nr. of bits to rotate to the max nr. of bits in the input.
nbits = std::min(nbits, (uint16_t)(sizeof(input) * 8));
nbits = std::min(nbits, static_cast<uint16_t>((sizeof(input) * 8)));
uint64_t output = 0;
for (uint16_t i = 0; i < nbits; i++) {
output <<= 1;
@ -169,6 +169,7 @@ bool hasACState(const decode_type_t protocol) {
// This is kept sorted by name
case AMCOR:
case ARGO:
case BLUESTARHEAVY:
case BOSCH144:
case CARRIER_AC84:
case CARRIER_AC128:
@ -183,6 +184,7 @@ bool hasACState(const decode_type_t protocol) {
case DAIKIN216:
case DAIKIN312:
case ELECTRA_AC:
case EUROM:
case FUJITSU_AC:
case GREE:
case HAIER_AC:
@ -701,6 +703,15 @@ namespace irutils {
default: return kUnknownStr;
}
break;
case decode_type_t::TOSHIBA_AC:
switch (model) {
case toshiba_ac_remote_model_t::kToshibaGenericRemote_A:
return kToshibaGenericRemoteAStr;
case toshiba_ac_remote_model_t::kToshibaGenericRemote_B:
return kToshibaGenericRemoteBStr;
default:
return kUnknownStr;
}
default: return kUnknownStr;
}
}
@ -753,7 +764,8 @@ namespace irutils {
result += addIntToString(degrees, (isSensorTemp)?
kSensorTempStr : kTempStr, precomma);
// Is it a half degree?
if (((uint16_t)(2 * degrees)) & 1) result += F(".5");
if (static_cast<uint16_t>(2 * degrees) & 1)
result += F(".5");
result += celsius ? 'C' : 'F';
return result;
}
@ -1282,7 +1294,7 @@ namespace irutils {
/// @param[in] position Nr. of the bit to be changed. `0` is the LSB.
/// @param[in] on Value to set the position'th bit to.
void setBit(uint32_t * const data, const uint8_t position, const bool on) {
uint32_t mask = (uint32_t)1 << position;
uint32_t mask = static_cast<uint32_t>(1) << position;
if (on)
*data |= mask;
else
@ -1294,7 +1306,7 @@ namespace irutils {
/// @param[in] position Nr. of the bit to be changed. `0` is the LSB.
/// @param[in] on Value to set the position'th bit to.
void setBit(uint64_t * const data, const uint8_t position, const bool on) {
uint64_t mask = (uint64_t)1 << position;
uint64_t mask = static_cast<uint64_t>(1) << position;
if (on)
*data |= mask;
else

View File

@ -117,21 +117,22 @@ namespace irutils {
bool getBit(const uint64_t data, const uint8_t position,
const uint8_t size = 64);
bool getBit(const uint8_t data, const uint8_t position);
#define GETBIT8(a, b) ((a) & ((uint8_t)1 << (b)))
#define GETBIT16(a, b) ((a) & ((uint16_t)1 << (b)))
#define GETBIT32(a, b) ((a) & ((uint32_t)1 << (b)))
#define GETBIT64(a, b) ((a) & ((uint64_t)1 << (b)))
#define GETBIT8(a, b) ((a) & (static_cast<uint8_t>(1) << (b)))
#define GETBIT16(a, b) ((a) & (static_cast<uint16_t>(1) << (b)))
#define GETBIT32(a, b) ((a) & (static_cast<uint32_t>(1) << (b)))
#define GETBIT64(a, b) ((a) & (static_cast<uint64_t>(1) << (b)))
#define GETBITS8(data, offset, size) \
(((data) & (((uint8_t)UINT8_MAX >> (8 - (size))) << (offset))) >> (offset))
(((data) & ((static_cast<uint8_t>(UINT8_MAX) >> (8 - (size))) << \
(offset))) >> (offset))
#define GETBITS16(data, offset, size) \
(((data) & (((uint16_t)UINT16_MAX >> (16 - (size))) << (offset))) >> \
(offset))
(((data) & ((static_cast<uint16_t>(UINT16_MAX) >> (16 - (size))) << \
(offset))) >> (offset))
#define GETBITS32(data, offset, size) \
(((data) & (((uint32_t)UINT32_MAX >> (32 - (size))) << (offset))) >> \
(offset))
(((data) & ((static_cast<uint32_t>(UINT32_MAX) >> (32 - (size))) << \
(offset))) >> (offset))
#define GETBITS64(data, offset, size) \
(((data) & (((uint64_t)UINT64_MAX >> (64 - (size))) << (offset))) >> \
(offset))
(((data) & ((static_cast<uint64_t>(UINT64_MAX) >> (64 - (size))) << \
(offset))) >> (offset))
uint64_t setBit(const uint64_t data, const uint8_t position,
const bool on = true, const uint8_t size = 64);
uint8_t setBit(const uint8_t data, const uint8_t position,

View File

@ -68,7 +68,7 @@ const uint8_t kAirtonFanHigh = 0b100; // 4
const uint8_t kAirtonFanMax = 0b101; // 5
const uint8_t kAirtonMinTemp = 16; // 16C
const uint8_t kAirtonMaxTemp = 25; // 25C
const uint8_t kAirtonMaxTemp = 31; // 31C
/// Class for handling detailed Airton 56-bit A/C messages.

View File

@ -12,6 +12,8 @@
#include <algorithm>
#include <cmath>
#include <cstring>
#include <set>
#include <utility>
#ifndef UNIT_TEST
#include <Arduino.h>
#endif // UNIT_TEST
@ -1791,16 +1793,20 @@ bool IRArgoAC_WREM3::isValidWrem3Message(const uint8_t state[],
switch (messageType) {
case argoIrMessageType_t::AC_CONTROL :
if (stateLengthBytes != kArgo3AcControlStateLength) { return false; }
if (stateLengthBytes != kArgo3AcControlStateLength)
return false;
break;
case argoIrMessageType_t::CONFIG_PARAM_SET:
if (stateLengthBytes != kArgo3ConfigStateLength) { return false; }
if (stateLengthBytes != kArgo3ConfigStateLength)
return false;
break;
case argoIrMessageType_t::TIMER_COMMAND:
if (stateLengthBytes != kArgo3TimerStateLength) { return false; }
if (stateLengthBytes != kArgo3TimerStateLength)
return false;
break;
case argoIrMessageType_t::IFEEL_TEMP_REPORT:
if (stateLengthBytes != kArgo3iFeelReportStateLength) { return false; }
if (stateLengthBytes != kArgo3iFeelReportStateLength)
return false;
break;
default:
return false;

View File

@ -0,0 +1,72 @@
// Copyright 2024 Harsh Bhosale (harshbhosale01)
/// @file
/// @brief Support for BluestarHeavy protocol
// Supports:
// Brand: Bluestar, Model: D716LXM0535A2400313 (Remote)
#include "IRrecv.h"
#include "IRsend.h"
#include "IRutils.h"
const uint16_t kBluestarHeavyHdrMark = 4912;
const uint16_t kBluestarHeavyBitMark = 465;
const uint16_t kBluestarHeavyHdrSpace = 5058;
const uint16_t kBluestarHeavyOneSpace = 572;
const uint16_t kBluestarHeavyZeroSpace = 1548;
const uint16_t kBluestarHeavyFreq = 38000;
const uint16_t kBluestarHeavyOverhead = 3;
#if SEND_BLUESTARHEAVY
/// Send a BluestarHeavy formatted message.
/// Status: BETA / Tested.
/// @param[in] data An array of bytes containing the IR command.
/// It is assumed to be in MSB order for this code.
/// e.g.
/// @code
/// uint8_t data[kBluestarHeavyStateLength] =
/// {0x2A,0x00,0x20,0xD0,0x05,0xA0,0x05,0xA0,0x00,0x80,0xBA,0x02,0x23};
/// @endcode
/// @param[in] nbytes Nr. of bytes of data in the array.
/// @param[in] repeat Nr. of times the message is to be repeated.
void IRsend::sendBluestarHeavy(const uint8_t data[], const uint16_t nbytes,
const uint16_t repeat) {
sendGeneric(kBluestarHeavyHdrMark, kBluestarHeavyHdrSpace,
kBluestarHeavyBitMark, kBluestarHeavyOneSpace,
kBluestarHeavyBitMark, kBluestarHeavyZeroSpace,
kBluestarHeavyHdrMark, kDefaultMessageGap,
data, nbytes, // Bytes
kBluestarHeavyFreq, true, repeat, kDutyDefault);
}
#endif // SEND_BLUESTARHEAVY
#if DECODE_BLUESTARHEAVY
/// Decode the supplied BluestarHeavy message.
/// Status: BETA / Tested.
/// @param[in,out] results Ptr to the data to decode & where to store the decode
/// @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.
bool IRrecv::decodeBluestarHeavy(decode_results *results, uint16_t offset,
const uint16_t nbits, const bool strict) {
if (strict && nbits != kBluestarHeavyBits)
return false;
uint16_t used = 0;
used = matchGeneric(results->rawbuf + offset, results->state,
results->rawlen - offset, nbits,
kBluestarHeavyHdrMark, kBluestarHeavyHdrSpace,
kBluestarHeavyBitMark, kBluestarHeavyOneSpace,
kBluestarHeavyBitMark, kBluestarHeavyZeroSpace,
kBluestarHeavyHdrMark, kDefaultMessageGap, true);
if (used == 0) return false; // We failed to find any data.
// Success
results->decode_type = decode_type_t::BLUESTARHEAVY;
results->bits = nbits;
return true;
}
#endif // DECODE_BLUESTARHEAVY

View File

@ -5,6 +5,7 @@
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1787
#include "ir_Bosch.h"
#include <algorithm>
#if SEND_BOSCH144
/// Send a Bosch 144-bit / 18-byte message (96-bit message are also possible)

View File

@ -99,6 +99,8 @@ void IRCoolixAC::stateReset(void) {
cleanFlag = false;
sleepFlag = false;
swingFlag = false;
tempLowF = false;
tempHighF = false;
}
/// Set up hardware to be able to send a message.
@ -112,8 +114,34 @@ void IRCoolixAC::send(const uint16_t repeat) {
// Typically repeat is `kCoolixDefaultRepeat` which is `1`, so this allows
// it to be 0 normally for this command, and allows additional repeats if
// requested rather always 0 for that command.
_irsend.sendCOOLIX(getRaw(), kCoolixBits, repeat - (getSwingVStep() &&
repeat > 0) ? 1 : 0);
const uint16_t repeat_override =
repeat - (getSwingVStep() && repeat > 0) ? 1 : 0;
if (tempHighF || tempLowF) {
uint64_t coolix24_raw = getRaw();
// We need to sneak our temperature range into a parity bit.
uint64_t coolix48_raw = 0;
// First create our parity.
uint64_t parity = getRaw() ^ UINT32_MAX;
// Interleave raw data and parity into output, in order.
coolix48_raw |= ((coolix24_raw >> 16) & 0xFF) << 40;
coolix48_raw |= ((parity >> 16) & 0xFF) << 32;
coolix48_raw |= ((coolix24_raw >> 8) & 0xFF) << 24;
coolix48_raw |= ((parity >> 8) & 0xFF) << 16;
coolix48_raw |= (coolix24_raw & 0xFF) << 8;
coolix48_raw |= parity & 0xFF;
// Mangle parity as necessary to represent Fahrenheit range.
coolix48_raw |= static_cast<uint64_t>(tempLowF ? 1 : 3) << (36);
// Send as Coolix48.
_irsend.sendCoolix48(coolix48_raw, kCoolix48Bits, repeat_override);
} else {
_irsend.sendCOOLIX(getRaw(), kCoolixBits, repeat_override);
}
// make sure to remove special state from the internal state
// after command has being transmitted.
recoverSavedState();
@ -140,6 +168,27 @@ void IRCoolixAC::setRaw(const uint32_t new_code) {
_.raw = new_code;
}
// Set internal state from a COOLIX48 read, handling F data hidden in parity.
void IRCoolixAC::setRawFromCoolix48(const uint64_t data) {
// Assume parity bits as in coolix24, let's strip them.
CoolixProtocol coolix24;
coolix24.raw = 0;
coolix24.raw |= ((data >> 8) & 0xff);
coolix24.raw |= ((data >> 24) & 0xff) << 8;
coolix24.raw |= ((data >> 40) & 0xff) << 16;
// Delegate to coolix24 code.
setRaw(coolix24.raw);
// Handle Fahrenheit range.
uint8_t fRange = (data >> 36) & 3;
if (fRange == 1) {
setTempFRange(false);
} else if (fRange == 3) {
setTempFRange(true);
}
}
/// Is the current state is a special state?
/// @return true, if it is. false if it isn't.
bool IRCoolixAC::isSpecialState(void) const {
@ -217,19 +266,53 @@ void IRCoolixAC::setTempRaw(const uint8_t code) { _.Temp = code; }
/// @return The native temperature value.
uint8_t IRCoolixAC::getTempRaw(void) const { return _.Temp; }
/// Set the fahrenheit temperature range.
/// @param[in] high True if setting high F range, false if low F range.
void IRCoolixAC::setTempFRange(const bool high) {
tempLowF = !high;
tempHighF = high;
}
// Clear the fahrenheit temperature range bits.
void IRCoolixAC::clearTempFRange() {
tempLowF = false;
tempHighF = false;
}
/// Set the temperature.
/// @param[in] desired The temperature in degrees celsius.
void IRCoolixAC::setTemp(const uint8_t desired) {
// Range check.
if (desired >= kCoolixTempLowFMin &&
desired <= kCoolixTempLowFMax) {
setTempRaw(kCoolixTempMapLowF[desired - kCoolixTempLowFMin]);
setTempFRange(false);
return;
}
if (desired >= kCoolixTempHighFMin &&
desired <= kCoolixTempHighFMax) {
setTempRaw(kCoolixTempMapHighF[desired - kCoolixTempHighFMin]);
setTempFRange(true);
return;
}
uint8_t temp = std::min(desired, kCoolixTempMax);
temp = std::max(temp, kCoolixTempMin);
setTempRaw(kCoolixTempMap[temp - kCoolixTempMin]);
clearTempFRange();
}
/// Get the current temperature setting.
/// @return The current setting for temp. in degrees celsius.
uint8_t IRCoolixAC::getTemp(void) const {
const uint8_t code = getTempRaw();
if (tempLowF) {
for (uint8_t i = 0; i < kCoolixTempLowFRange; i++)
if (kCoolixTempMapLowF[i] == code) return kCoolixTempLowFMin + i;
}
if (tempHighF) {
for (uint8_t i = 0; i < kCoolixTempHighFRange; i++)
if (kCoolixTempMapHighF[i] == code) return kCoolixTempHighFMin + i;
}
for (uint8_t i = 0; i < kCoolixTempRange; i++)
if (kCoolixTempMap[i] == code) return kCoolixTempMin + i;
return kCoolixTempMax; // Not a temp we expected.
@ -613,7 +696,9 @@ String IRCoolixAC::toString(void) const {
}
result += ')';
// Fan mode doesn't have a temperature.
if (getMode() != kCoolixFan) result += addTempToString(getTemp());
bool celcius = true;
if (tempHighF || tempLowF) celcius = false;
if (getMode() != kCoolixFan) result += addTempToString(getTemp(), celcius);
result += addBoolToString(getZoneFollow(), kZoneFollowStr);
result += addLabeledString(
(getSensorTemp() == kCoolixSensorTempIgnoreCode)

View File

@ -15,6 +15,7 @@
// Brand: Midea, Model: RG52D/BGE Remote
// Brand: Midea, Model: MS12FU-10HRDN1-QRD0GW(B) A/C
// Brand: Midea, Model: MSABAU-07HRFN1-QRD0GW A/C (circa 2016)
// Brand: Comfee (Midea), Model: Breezy Cool Pro 2.6
// Brand: Tokio, Model: AATOEMF17-12CHR1SW split-type RG51|50/BGE Remote
// Brand: Airwell, Model: RC08B remote
// Brand: Kastron, Model: RG57A7/BGEF Inverter remote
@ -25,6 +26,7 @@
// Brand: Toshiba, Model: WH-E1YE remote
// Brand: Bosch, Model: RG36B4/BGE remote
// Brand: Bosch, Model: B1ZAI2441W/B1ZAO2441W A/C
// Brand: Fisher, Model: R51L1/BGE remote
#ifndef IR_COOLIX_H_
#define IR_COOLIX_H_
@ -78,6 +80,42 @@ const uint8_t kCoolixTempMap[kCoolixTempRange] = {
0b1010, // 29C
0b1011 // 30C
};
const uint8_t kCoolixTempLowFMin = 63;
const uint8_t kCoolixTempLowFMax = 75;
const uint8_t kCoolixTempLowFRange =
kCoolixTempLowFMax - kCoolixTempLowFMin + 1;
const uint8_t kCoolixTempHighFMin = kCoolixTempLowFMax + 1;
const uint8_t kCoolixTempHighFMax = 86;
const uint8_t kCoolixTempHighFRange =
kCoolixTempHighFMax - kCoolixTempHighFMin + 1;
const uint8_t kCoolixTempMapLowF[kCoolixTempLowFRange] = {
0b1100, // 63F
0b0010, // 64F
0b1010, // 65F
0b0110, // 66F
0b1110, // 67F
0b0001, // 68F
0b1001, // 69F
0b0101, // 70F
0b1101, // 71F
0b0011, // 72F
0b1011, // 73F
0b0111, // 74F
0b1111 // 75F
};
const uint8_t kCoolixTempMapHighF[kCoolixTempHighFRange] = {
0b0000, // 76F
0b1000, // 77F
0b0100, // 78F
0b1100, // 79F
0b0010, // 80F
0b1010, // 81F
0b0110, // 82F
0b1110, // 83F
0b0001, // 84F
0b1001, // 85F
0b0101 // 86F
};
const uint8_t kCoolixSensorTempMax = 30; // Celsius
const uint8_t kCoolixSensorTempIgnoreCode = 0b11111; // 0x1F / 31 (DEC)
// kCoolixSensorTempMask = 0b000000000000111100000000; // 0xF00
@ -135,6 +173,8 @@ class IRCoolixAC {
void off(void);
void setPower(const bool on);
bool getPower(void) const;
void setTempFRange(const bool high = false);
void clearTempFRange();
void setTemp(const uint8_t temp);
uint8_t getTemp(void) const;
void setSensorTemp(const uint8_t temp);
@ -159,6 +199,8 @@ class IRCoolixAC {
bool getZoneFollow(void) const;
uint32_t getRaw(void) const;
void setRaw(const uint32_t new_code);
// Convert from Coolix48 with Fahrenheit handling.
void setRawFromCoolix48(const uint64_t new_code);
static uint8_t convertMode(const stdAc::opmode_t mode);
static uint8_t convertFan(const stdAc::fanspeed_t speed);
static stdAc::opmode_t toCommonMode(const uint8_t mode);
@ -185,6 +227,8 @@ class IRCoolixAC {
bool cleanFlag;
bool sleepFlag;
bool swingFlag;
bool tempLowF; // Indicates low-range F temperatures.
bool tempHighF; // Indicates high-range F temperatures.
uint8_t savedFan;
void setTempRaw(const uint8_t code);

View File

@ -46,6 +46,7 @@ using irutils::addModeToString;
using irutils::addSwingHToString;
using irutils::addSwingVToString;
using irutils::addTempToString;
using irutils::addTempFloatToString;
using irutils::addFanToString;
using irutils::bcdToUint8;
using irutils::minsToString;
@ -73,7 +74,8 @@ void IRsend::sendDaikin(const unsigned char data[], const uint16_t nbytes,
sendGeneric(0, 0, // No header for the header
kDaikinBitMark, kDaikinOneSpace, kDaikinBitMark,
kDaikinZeroSpace, kDaikinBitMark, kDaikinZeroSpace + kDaikinGap,
(uint64_t)0b00000, kDaikinHeaderLength, 38, false, 0, 50);
static_cast<uint64_t>(0b00000), kDaikinHeaderLength, 38, false,
0, 50);
// Data #1
if (nbytes < kDaikinStateLength) { // Are we using the legacy size?
// Do this as a constant to save RAM and keep in flash memory
@ -221,15 +223,15 @@ bool IRDaikinESP::getPower(void) const {
/// Set the temperature.
/// @param[in] temp The temperature in degrees celsius.
void IRDaikinESP::setTemp(const uint8_t temp) {
uint8_t degrees = std::max(temp, kDaikinMinTemp);
degrees = std::min(degrees, kDaikinMaxTemp);
_.Temp = degrees;
void IRDaikinESP::setTemp(const float temp) {
float degrees = std::max(temp, static_cast<float>(kDaikinMinTemp));
degrees = std::min(degrees, static_cast<float>(kDaikinMaxTemp));
_.Temp = degrees * 2.0f;
}
/// Get the current temperature setting.
/// @return The current setting for temp. in degrees celsius.
uint8_t IRDaikinESP::getTemp(void) const { return _.Temp; }
float IRDaikinESP::getTemp(void) const { return _.Temp / 2.0f; }
/// Set the speed of the fan.
/// @param[in] fan The desired setting.
@ -536,7 +538,7 @@ stdAc::state_t IRDaikinESP::toCommon(void) const {
result.power = _.Power;
result.mode = toCommonMode(_.Mode);
result.celsius = true;
result.degrees = _.Temp;
result.degrees = getTemp();
result.fanspeed = toCommonFanSpeed(getFan());
result.swingv = _.SwingV ? stdAc::swingv_t::kAuto :
stdAc::swingv_t::kOff;
@ -563,7 +565,7 @@ String IRDaikinESP::toString(void) const {
result += addBoolToString(_.Power, kPowerStr, false);
result += addModeToString(_.Mode, kDaikinAuto, kDaikinCool, kDaikinHeat,
kDaikinDry, kDaikinFan);
result += addTempToString(_.Temp);
result += addTempFloatToString(getTemp());
result += addFanToString(getFan(), kDaikinFanMax, kDaikinFanMin,
kDaikinFanAuto, kDaikinFanQuiet, kDaikinFanMed);
result += addBoolToString(_.Powerful, kPowerfulStr);
@ -674,7 +676,7 @@ void IRsend::sendDaikin2(const unsigned char data[], const uint16_t nbytes,
for (uint16_t r = 0; r <= repeat; r++) {
// Leader
sendGeneric(kDaikin2LeaderMark, kDaikin2LeaderSpace,
0, 0, 0, 0, 0, 0, (uint64_t) 0, // No data payload.
0, 0, 0, 0, 0, 0, static_cast<uint64_t>(0), // No data payload.
0, kDaikin2Freq, false, 0, 50);
// Section #1
sendGeneric(kDaikin2HdrMark, kDaikin2HdrSpace, kDaikin2BitMark,
@ -2936,7 +2938,8 @@ bool IRrecv::decodeDaikin128(decode_results *results, uint16_t offset,
kDaikinTolerance, kDaikinMarkExcess)) return false;
}
const uint16_t ksectionSize[kDaikin128Sections] = {
kDaikin128SectionLength, (uint16_t)(nbits / 8 - kDaikin128SectionLength)};
kDaikin128SectionLength,
static_cast<uint16_t>(nbits / 8 - kDaikin128SectionLength)};
// Data Sections
uint16_t pos = 0;
for (uint8_t section = 0; section < kDaikin128Sections; section++) {
@ -2986,7 +2989,7 @@ void IRsend::sendDaikin152(const unsigned char data[], const uint16_t nbytes,
sendGeneric(0, 0, kDaikin152BitMark, kDaikin152OneSpace,
kDaikin152BitMark, kDaikin152ZeroSpace,
kDaikin152BitMark, kDaikin152Gap,
(uint64_t)0, kDaikin152LeaderBits,
static_cast<uint64_t>(0), kDaikin152LeaderBits,
kDaikin152Freq, false, 0, kDutyDefault);
// Header + Data + Footer
sendGeneric(kDaikin152HdrMark, kDaikin152HdrSpace, kDaikin152BitMark,
@ -3838,7 +3841,7 @@ void IRsend::sendDaikin312(const unsigned char data[], const uint16_t nbytes,
kDaikin312BitMark, kDaikin312OneSpace,
kDaikin312BitMark, kDaikin312ZeroSpace,
kDaikin312BitMark, kDaikin312HdrGap,
(uint64_t)0b00000, kDaikinHeaderLength,
static_cast<uint64_t>(0b00000), kDaikinHeaderLength,
kDaikin2Freq, false, 0, kDutyDefault);
// Section #1
sendGeneric(kDaikin312HdrMark, kDaikin312HdrSpace, kDaikin312BitMark,

View File

@ -53,6 +53,7 @@
// Brand: Daikin, Model: BRC4M150W16 remote (DAIKIN200)
// Brand: Daikin, Model: FTXM20R5V1B A/C (DAIKIN312)
// Brand: Daikin, Model: ARC466A67 remote (DAIKIN312)
// Brand: Daikin, Model: ARC443A5 remote (DAIKIN)
#ifndef IR_DAIKIN_H_
#define IR_DAIKIN_H_
@ -99,8 +100,7 @@ union DaikinESPProtocol{
uint64_t Mode :3;
uint64_t :1;
// Byte 22
uint64_t :1;
uint64_t Temp :7; // Temp should be between 10 - 32
uint64_t Temp :8; // Temp should be between 20 - 64 (10 C - 32 C)
// Byte 23
uint64_t :8;
@ -738,8 +738,8 @@ class IRDaikinESP {
void off(void);
void setPower(const bool on);
bool getPower(void) const;
void setTemp(const uint8_t temp);
uint8_t getTemp(void) const;
void setTemp(const float temp);
float getTemp(void) const;
void setFan(const uint8_t fan);
uint8_t getFan(void) const;
void setMode(const uint8_t mode);

View File

@ -282,7 +282,7 @@ uint16_t IREcoclimAc::getClock(void) const { return _.Clock; }
/// Set the clock time on the A/C unit.
/// @param[in] nr_of_mins Nr. of minutes past midnight.
void IREcoclimAc::setClock(const uint16_t nr_of_mins) {
_.Clock = std::min(nr_of_mins, (uint16_t)(24 * 60 - 1));
_.Clock = std::min(nr_of_mins, static_cast<uint16_t>(24 * 60 - 1));
}
/// Get the Unit type/DIP switch settings of the remote.

View File

@ -310,6 +310,18 @@ bool IRElectraAc::getTurbo(void) const {
return _.Turbo;
}
/// Set the Quiet/Silent'mode of the A/C.
/// @param[in] on true, the setting is on. false, the setting is off.
void IRElectraAc::setQuiet(const bool on) {
_.Quiet = on;
}
/// Get the Quiet/Silent mode of the A/C.
/// @return true, the setting is on. false, the setting is off.
bool IRElectraAc::getQuiet(void) const {
return _.Quiet;
}
/// Get the IFeel mode of the A/C.
/// @return true, the setting is on. false, the setting is off.
bool IRElectraAc::getIFeel(void) const { return _.IFeel; }
@ -373,11 +385,11 @@ stdAc::state_t IRElectraAc::toCommon(void) const {
: stdAc::swingh_t::kOff;
result.light = getLightToggle();
result.turbo = _.Turbo;
result.quiet = _.Quiet;
result.clean = _.Clean;
result.iFeel = getIFeel();
// Not supported.
result.model = -1; // No models used.
result.quiet = false;
result.econo = false;
result.filter = false;
result.beep = false;
@ -404,6 +416,7 @@ String IRElectraAc::toString(void) const {
result += addToggleToString(getLightToggle(), kLightStr);
result += addBoolToString(_.Clean, kCleanStr);
result += addBoolToString(_.Turbo, kTurboStr);
result += addBoolToString(_.Quiet, kQuietStr);
result += addBoolToString(_.IFeel, kIFeelStr);
}
if (_.IFeel || _.SensorUpdate) {

View File

@ -15,6 +15,7 @@
// Brand: Centek, Model: YKR-P/002E remote
// Brand: AEG, Model: Chillflex Pro AXP26U338CW A/C
// Brand: Electrolux, Model: YKR-H/531E A/C
// Brand: Delonghi, Modell: PAC EM90
#ifndef IR_ELECTRA_H_
#define IR_ELECTRA_H_
@ -52,7 +53,7 @@ union ElectraProtocol {
// Byte 5
uint8_t :6;
uint8_t Turbo :1;
uint8_t :1;
uint8_t Quiet :1;
// Byte 6
uint8_t :3;
uint8_t IFeel :1;
@ -145,6 +146,8 @@ class IRElectraAc {
bool getLightToggle(void) const;
void setTurbo(const bool on);
bool getTurbo(void) const;
void setQuiet(const bool on);
bool getQuiet(void) const;
void setIFeel(const bool on);
bool getIFeel(void) const;
void setSensorUpdate(const bool on);

View File

@ -0,0 +1,489 @@
// Copyright 2025 GottemHams
/// @file
/// @brief Support for Eurom A/C protocols.
/// @see https://eurom.nl/wp-content/uploads/2022/04/Polar-12C-16CH-v1.0.pdf
#include "ir_Eurom.h"
#include <algorithm>
#include <cmath>
#include <cstring>
#include "IRrecv.h"
#include "IRsend.h"
#include "IRtext.h"
#include "IRutils.h"
#if __cplusplus >= 201103L && defined(_GLIBCXX_USE_C99_MATH_TR1)
using std::roundf;
#else
using ::roundf;
#endif
using irutils::uint8ToBcd;
using irutils::bcdToUint8;
using irutils::addBoolToString;
using irutils::addFanToString;
using irutils::addLabeledString;
using irutils::addModeToString;
using irutils::addTempToString;
using irutils::minsToString;
#if SEND_EUROM
/// Send a Eurom formatted message.
/// Status: STABLE / Confirmed Working.
/// @param[in] data An array of bytes containing the IR command.
/// It is assumed to be in MSB order for this code.
/// e.g.
/// @code
/// unsigned char data[kEuromStateLength] =
/// {0x18,0x27,0x31,0x80,0x00,0x00,0x00,0x80,0x00,0x80,0x10,0x1D};
/// @endcode
/// @param[in] nbytes The number of bytes of data in the array.
/// @param[in] repeat The number of times the command is to be repeated.
void IRsend::sendEurom(const uint8_t data[], const uint16_t nbytes,
const uint16_t repeat) {
// Check if we have enough bytes to send a proper message
if (nbytes < kEuromStateLength)
return;
sendGeneric(kEuromHdrMark, kEuromHdrSpace,
kEuromBitMark, kEuromOneSpace,
kEuromBitMark, kEuromZeroSpace,
kEuromBitMark, kEuromSpaceGap,
data, nbytes, kEuromFreq, true, repeat, kDutyDefault);
}
#endif // SEND_EUROM
#if DECODE_EUROM
/// Decode the supplied Eurom message.
/// Status: STABLE / Confirmed Working.
/// @param[in,out] results PTR to the data to decode & where to store the 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 True if it can decode it, false if it can't.
bool IRrecv::decodeEurom(decode_results *results, uint16_t offset,
const uint16_t nbits, const bool strict) {
if (results->rawlen < nbits)
return false; // Too short a message to match
if (strict && nbits != kEuromBits)
return false;
if (!matchGeneric(results->rawbuf + offset, results->state,
results->rawlen - offset, nbits,
kEuromHdrMark, kEuromHdrSpace,
kEuromBitMark, kEuromOneSpace,
kEuromBitMark, kEuromZeroSpace,
kEuromBitMark, kEuromSpaceGap, true)) return false;
// Success
results->bits = nbits;
results->decode_type = EUROM;
return true;
}
#endif // DECODE_EUROM
/// 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?
IREuromAc::IREuromAc(const uint16_t pin, const bool inverted,
const bool use_modulation)
: _irsend(pin, inverted, use_modulation) {
stateReset();
}
/// Combine a mode flag and temperature into a single byte for the AC.
/// Note that validity is not checked again.
/// @param[in] mode A valid mode flag.
/// @param[in] celsius A valid temperature, i.e. within the proper range.
uint8_t IREuromAc::getModeCelsiusByte(const uint8_t mode,
const uint8_t celsius) const {
if (celsius >= kEuromMaxTempC)
return mode | kEuromMaxTempFlag;
return mode | ((celsius - kEuromMinTempC) << 4);
}
/// Combine sleep mode and a timer duration into a single byte for the AC.
/// Note that validity is not checked again.
/// @param[in] sleep Whether sleep mode should be enabled.
/// @param[in] hours A valid duration, i.e. within the proper range.
uint8_t IREuromAc::getSleepOnTimerByte(const bool sleep,
const uint8_t hours) const {
uint8_t base = sleep ? kEuromSleepEnabled : kEuromSleepOnTimerDisabled;
return base + uint8ToBcd(hours);
}
/// Reset the internals of the object to a known good state.
void IREuromAc::stateReset(void) {
_.Sum1 = 0x18;
_.Sum2 = 0x27;
// No need to call setMode() separately, is handled by setTemp()
setTemp(state_celsius_); // 23 C
_.Power_Swing = kEuromPowerSwingDisabled;
setSleep(state_sleep_); // false
// No need to call setOnTimer() separately, is handled by setSleep()
_.Sum3 = 0x00;
setOffTimer(0);
_.Sum4 = 0x80;
setFan(kEuromFanLow);
}
#if SEND_EUROM
/// Send the current internal state as an IR message.
/// @param[in] repeat Number of times the message will be repeated. Note that
/// the original remote sends the same signal twice, but the
/// actual A/C works just fine if you send it once.
void IREuromAc::send(const uint16_t repeat) {
_irsend.sendEurom(getRaw(), kEuromStateLength, repeat);
}
#endif // SEND_EUROM
/// Set up hardware to be able to send a message.
void IREuromAc::begin(void) {
_irsend.begin();
}
/// Calculate the checksum for the supplied state.
/// @param[in] state The source state to generate the checksum from.
/// @param[in] length Length of the supplied state to checksum.
/// @return The checksum value.
uint8_t IREuromAc::calcChecksum(const uint8_t state[], const uint16_t length) {
uint8_t checksum = irutils::sumNibbles(state + 1, length - 2);
checksum -= irutils::sumNibbles(state, 1);
return checksum;
}
/// Verify if the checksum is valid for a given state.
/// @param[in] state The source state to verify the checksum of.
/// @param[in] length The size of the supplied state.
/// @return A boolean indicating if its checksum is valid.
bool IREuromAc::validChecksum(const uint8_t state[], const uint16_t length) {
return state[length - 1] == IREuromAc::calcChecksum(state, length);
}
/// Update the checksum value for the current internal state.
void IREuromAc::checksum(void) {
_.Checksum = IREuromAc::calcChecksum(_.raw, kEuromStateLength);
}
/// Set the raw state of the remote.
/// @param[in] state The raw state from the native IR message.
void IREuromAc::setRaw(const uint8_t state[]) {
std::memcpy(_.raw, state, kEuromStateLength);
}
/// Get the raw state of the remote, suitable to be sent with the appropriate
/// IRsend object method.
/// @return A PTR to the internal state.
uint8_t *IREuromAc::getRaw(void) {
checksum(); // Let's ensure this is updated before returning
return _.raw;
}
/// Set the internal state to powered on.
void IREuromAc::on(void) {
setPower(true);
}
/// Set the internal state to powered off.
void IREuromAc::off(void) {
setPower(false);
}
/// Set the internal state to use the desired power setting.
/// @param[in] state The desired power setting.
void IREuromAc::setPower(const bool state) {
// We'll also have to preserve the swing state
if (state)
_.Power_Swing |= kEuromPowerOn;
else
_.Power_Swing &= kEuromSwingOn;
}
/// Get the current power setting from the internal state.
/// @return A boolean indicating the current power setting.
bool IREuromAc::getPower(void) const {
return (_.Power_Swing & kEuromPowerOn) == kEuromPowerOn;
}
/// Set the internal state to use the desired operation mode.
/// @param[in] mode The desired operation mode.
void IREuromAc::setMode(const uint8_t mode) {
switch (mode) {
case kEuromCool:
case kEuromHeat:
state_mode_ = mode;
_.Mode_Celsius = getModeCelsiusByte(mode, state_celsius_);
break;
case kEuromDehumidify:
case kEuromVentilate:
state_mode_ = mode;
_.Mode_Celsius = mode;
break;
default:
break;
}
}
/// Get the current operation mode setting from the internal state.
/// @return The current operation mode.
uint8_t IREuromAc::getMode(void) const {
return state_mode_;
}
/// Set the internal state to use the desired temperature.
/// @param[in] degrees The desired temperature in degrees, normally Celsius.
/// @param[in] fahrenheit If the given temperature is in Fahrenheit instead.
void IREuromAc::setTemp(const uint8_t degrees, const bool fahrenheit) {
if (state_mode_ != kEuromCool && state_mode_ != kEuromHeat)
return;
uint8_t temp_c, temp_f;
if (fahrenheit) {
temp_f = std::max(kEuromMinTempF, degrees);
temp_f = std::min(kEuromMaxTempF, temp_f);
temp_c = static_cast<uint8_t>(roundf(fahrenheitToCelsius(temp_f)));
_.Fahrenheit = kEuromFahrenheitEnabled + temp_f;
} else {
temp_c = degrees;
_.Fahrenheit = kEuromFahrenheitDisabled;
}
temp_c = std::max(kEuromMinTempC, temp_c);
temp_c = std::min(kEuromMaxTempC, temp_c);
state_celsius_ = temp_c;
_.Mode_Celsius = getModeCelsiusByte(state_mode_, temp_c);
}
/// Get the current temperature from the internal state.
/// @return The current temperature, which can be either Celsius or Fahrenheit,
/// depending on what was used with setTemp(). See also: getTempIsFahrenheit().
uint8_t IREuromAc::getTemp(void) const {
if (state_mode_ != kEuromCool && state_mode_ != kEuromHeat)
return 0; // Not supported in other modes
if (getTempIsFahrenheit())
return _.Fahrenheit - kEuromFahrenheitEnabled;
return state_celsius_;
}
/// Check if Fahrenheit is currently being used by the internal state.
/// @return A boolean indicating if the current temperature is in Fahrenheit.
bool IREuromAc::getTempIsFahrenheit(void) const {
return _.Fahrenheit != kEuromFahrenheitDisabled;
}
/// Set the internal state to use the desired fan speed.
/// @param[in] speed The desired fan speed.
void IREuromAc::setFan(const uint8_t speed) {
switch (speed) {
case kEuromFanLow:
case kEuromFanMed:
case kEuromFanHigh:
_.Fan = speed;
break;
default:
break;
}
}
/// Get the current fan speed from the internal state.
/// @return The current fan speed.
uint8_t IREuromAc::getFan(void) const {
return _.Fan;
}
/// Set the internal state to use the desired swing setting.
/// @param[in] state The desired swing setting.
void IREuromAc::setSwing(const bool state) {
if (state)
_.Power_Swing |= kEuromSwingOn;
else
_.Power_Swing &= kEuromPowerOn;
}
/// Get the current swing setting from the internal state.
/// @return A boolean indicating the current swing setting.
bool IREuromAc::getSwing(void) const {
return (_.Power_Swing & kEuromSwingOn) == kEuromSwingOn;
}
/// Set the internal state to use the desired sleep setting.
/// @param[in] state The desired sleep setting.
void IREuromAc::setSleep(const bool state) {
state_sleep_ = state;
_.Sleep_OnTimer = getSleepOnTimerByte(state, state_on_timer_);
}
/// Get the current sleep setting from the internal state.
/// @return A boolean indicating the current sleep setting.
bool IREuromAc::getSleep(void) const {
return state_sleep_;
}
/// Set the internal state to use the desired "off timer" duration.
/// @param[in] duration The desired duration, in hours.
void IREuromAc::setOffTimer(const uint8_t duration) {
uint8_t hours = std::max(kEuromTimerMin, duration);
hours = std::min(kEuromTimerMax, hours);
_.OffTimer = kEuromOffTimer + uint8ToBcd(hours);
_.OffTimerEnabled = hours ? kEuromOffTimer : kEuromOffTimerDisabled;
}
/// Get the current "off timer" duration from the internal state.
/// @return The current duration, in hours.
uint8_t IREuromAc::getOffTimer(void) const {
return bcdToUint8(_.OffTimer - kEuromOffTimer);
}
/// Set the internal state to use the desired "on timer" duration.
/// @param[in] duration The desired duration, in hours.
void IREuromAc::setOnTimer(const uint8_t duration) {
uint8_t hours = std::max(kEuromTimerMin, duration);
hours = std::min(kEuromTimerMax, hours);
state_on_timer_ = hours;
_.Sleep_OnTimer = getSleepOnTimerByte(state_sleep_, hours);
}
/// Get the current "on timer" duration from the internal state.
/// @return The current duration, in hours.
uint8_t IREuromAc::getOnTimer(void) const {
return state_on_timer_;
}
/// Convert a stdAc::opmode_t enum into its native operation mode.
/// @param[in] mode The enum to be converted.
/// @return The native equivalent of the enum.
uint8_t IREuromAc::convertMode(const stdAc::opmode_t mode) {
// This particular A/C doesn't actually have an 'Auto' mode, so we'll just use
// the normal fan mode instead
switch (mode) {
case stdAc::opmode_t::kCool:
return kEuromCool;
case stdAc::opmode_t::kHeat:
return kEuromHeat;
case stdAc::opmode_t::kDry:
return kEuromDehumidify;
default:
return kEuromVentilate;
}
}
/// Convert a stdAc::fanspeed_t enum into its native speed.
/// @param[in] speed The enum to be converted.
/// @return The native equivalent of the enum.
uint8_t IREuromAc::convertFan(const stdAc::fanspeed_t speed) {
// This particular A/C doesn't actually have an 'Auto' mode, so we'll just use
// the lowest fan speed instead
switch (speed) {
case stdAc::fanspeed_t::kHigh:
case stdAc::fanspeed_t::kMax:
return kEuromFanHigh;
case stdAc::fanspeed_t::kMedium:
return kEuromFanMed;
default:
return kEuromFanLow;
}
}
/// Convert a stdAc::swingv_t enum into its native swing.
/// @param[in] swing The enum to be converted.
/// @return The native equivalent of the enum.
bool IREuromAc::convertSwing(const stdAc::swingv_t swing) {
// The only choice is on or off, so let's just treat the former as auto mode
switch (swing) {
case stdAc::swingv_t::kAuto:
return true;
default:
return false;
}
}
/// Convert a native operation mode into its stdAc enum equivalent.
/// @param[in] mode The native operation mode setting to be converted.
/// @return The stdAc enum equivalent of the native setting.
stdAc::opmode_t IREuromAc::toCommonMode(const uint8_t mode) {
// This particular A/C doesn't actually have an 'Auto' mode, so we'll just use
// the normal fan mode instead. To make this more clear, 'kEuromVentilate' is
// explicitly included in the switch (instead of being omitted and implicitly
// handled via the default case).
switch (mode) {
case kEuromCool:
return stdAc::opmode_t::kCool;
case kEuromHeat:
return stdAc::opmode_t::kHeat;
case kEuromDehumidify:
return stdAc::opmode_t::kDry;
case kEuromVentilate:
default:
return stdAc::opmode_t::kFan;
}
}
/// Convert a native fan speed into its stdAc enum equivalent.
/// @param[in] speed The native speed setting to be converted.
/// @return The stdAc enum equivalent of the native setting.
stdAc::fanspeed_t IREuromAc::toCommonFanSpeed(const uint8_t speed) {
// This particular A/C doesn't actually have an 'Auto' mode, so we'll just use
// the lowest speed instead. To make this more clear, 'kEuromFanLow' is
// explicitly included in the switch (instead of being omitted and implicitly
// handled via the default case).
switch (speed) {
case kEuromFanHigh:
return stdAc::fanspeed_t::kMax;
case kEuromFanMed:
return stdAc::fanspeed_t::kMedium;
case kEuromFanLow:
default:
return stdAc::fanspeed_t::kMin;
}
}
/// Convert a native swing setting into its stdAc enum equivalent.
/// @param[in] swing The native swing setting to be converted.
/// @return The stdAc enum equivalent of the native setting.
stdAc::swingv_t IREuromAc::toCommonSwing(const bool swing) {
// The only choice is on or off, so let's just treat the former as auto mode
return swing ? stdAc::swingv_t::kAuto : stdAc::swingv_t::kOff;
}
/// Convert the current internal state into its stdAc::state_t equivalent.
/// @return The stdAc struct equivalent of the native settings.
stdAc::state_t IREuromAc::toCommon(void) const {
stdAc::state_t result{};
result.protocol = EUROM;
result.power = getPower();
result.mode = toCommonMode(getMode());
result.degrees = getTemp();
result.celsius = !getTempIsFahrenheit();
result.fanspeed = toCommonFanSpeed(getFan());
result.swingv = toCommonSwing(getSwing());
result.sleep = getSleep();
return result;
}
/// Convert the current internal state into a human-readable string.
/// @return A human-readable string.
String IREuromAc::toString(void) const {
String result = "";
result.reserve(70); // Reserve some heap for the string to reduce fragging
result += addBoolToString(getPower(), kPowerStr, false);
result += addModeToString(getMode(), 0xFF, kEuromCool,
kEuromHeat, kEuromDehumidify, kEuromVentilate);
result += addTempToString(getTemp(), !getTempIsFahrenheit());
result += addFanToString(getFan(), kEuromFanHigh, kEuromFanLow,
0xFF, 0xFF,
kEuromFanMed);
result += addBoolToString(getSwing(), kSwingVStr);
result += addBoolToString(getSleep(), kSleepStr);
uint8_t off_timer_min = getOffTimer() * 60;
uint8_t on_timer_min = getOnTimer() * 60;
String off_timer_str = off_timer_min ? minsToString(off_timer_min) : kOffStr;
String on_timer_str = on_timer_min ? minsToString(on_timer_min) : kOffStr;
result += addLabeledString(off_timer_str, kOffTimerStr);
result += addLabeledString(on_timer_str, kOnTimerStr);
return result;
}

View File

@ -0,0 +1,244 @@
// Copyright 2025 GottemHams
/// @file
/// @brief Support for Eurom A/C protocols.
/// @see https://eurom.nl/wp-content/uploads/2022/04/Polar-12C-16CH-v1.0.pdf
// Supports:
// Brand: Eurom, Model: Polar 16CH
#ifndef IR_EUROM_H_
#define IR_EUROM_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 Eurom message.
union EuromProtocol {
uint8_t raw[kEuromStateLength]; // The state of the IR remote
struct {
// Byte 0 is used as a negative offset for the checksum and is always 0x18
uint8_t Sum1 :8;
// Byte 1 is used as part of the checksum only and is always 0x27
uint8_t Sum2 :8;
// Byte 2 combines 2 functions and has some considerations:
// 1. Cooling mode almost always has the lower nibble set to 0x1,
// e.g. 0x01 = 16 C, 0x11 = 17 C, 0xF1 = 31 C.
// Exception: 0x09 means 32 C (max temperature).
// 2. Dehumidification doesn't support temperatures, so this is always 0x72.
// 3. Same goes for fan mode, which is always 0x73.
// 4. Heating mode almost always has the lower nibble set to 0x4,
// e.g. 0x04 = 16 C, 0x14 = 17 C, 0xF4 = 31 C.
// Exception: 0x0C means 32 C (max temperature).
uint8_t Mode_Celsius :8;
// Byte 3 also combines 2 functions, with the values being OR'ed together:
// 1. 0x00 means power off, swing off
// 2. 0x40 means power off, swing on
// 3. 0x80 means power on, swing off
// 3. 0xC0 means power on, swing on
uint8_t Power_Swing :8;
// Byte 4 is to track Fahrenheit separately, but note that it will always
// reset to 0x00 if Celsius is used. On the other hand, Celsius moves along
// with this, i.e. a change of +1/-1 C for roughly every 3 F. The base value
// is 0x41 which corresponds to 61 F and increases by 0x01 for every degree.
// This gives it a range of 0x41 - 0x5E (inclusive).
uint8_t Fahrenheit :8;
// Byte 5 yet again combines functions:
// 1. 0x00 for sleep mode disabled, 0x40 for enabled
// 2. The timer duration is simply encoded as BCD and added to this, with a
// maximum of 24 hours
uint8_t Sleep_OnTimer :8;
// Byte 6 seems to be truly unused, since it's always 0x00. We'll still
// always use it in checksums though.
uint8_t Sum3 :8;
// Byte 7 is always at least 0x80, with the hours also being added as BCD,
// e.g. 0x80 = 0 hours, 0x81 = 1 h, 0xA4 = 24 h.
uint8_t OffTimer :8;
// Byte 8 doesn't really seem to matter, but it should be 0x00 or 0x80 for
// off and on respectively. Apparently setting the **duration** alone is
// already enough to set the timer?
uint8_t OffTimerEnabled :8;
// Byte 9 is used as part of the checksum only and is slways 0x80
uint8_t Sum4 :8;
// Byte 10 is simple: 0x10, 0x20, 0x40 for low, medium and high respectively
uint8_t Fan :8;
// Byte 11 holds a funny checksum. =]
// Add all nibbles beyond the first byte (excluding the checksum of course),
// then subtract the first byte. The second byte should always be larger, so
// this never results in sudden signedness (i.e. underflowing). It might be
// pure coincidence that the first byte is always 0x18 and they could have
// hardcoded that value elsewhere/otherwise.
uint8_t Checksum :8;
};
};
// Constants
// IR signal information
const uint16_t kEuromHdrMark = 3257;
const uint16_t kEuromBitMark = 454;
const uint16_t kEuromHdrSpace = 3187;
const uint16_t kEuromOneSpace = 1162;
const uint16_t kEuromZeroSpace = 355;
const uint16_t kEuromSpaceGap = 50058;
const uint16_t kEuromFreq = 38000;
// Modes
const uint8_t kEuromCool = 0x01; // Lowest possible value, 16 C
const uint8_t kEuromDehumidify = 0x72;
const uint8_t kEuromVentilate = 0x73;
const uint8_t kEuromHeat = 0x04; // Also 16 C
// Reaching the highest temperature breaks the formula that is used otherwise,
// because we should basically just OR this flag to the above mode byte. It
// seems more like it indicates "max temp" instead of "32 C".
const uint8_t kEuromMaxTempFlag = 0x08;
// Temperatures
const uint8_t kEuromMinTempC = 16;
const uint8_t kEuromMaxTempC = 32;
const uint8_t kEuromMinTempF = 61;
const uint8_t kEuromMaxTempF = 90;
// The enabled flag will simply be added to chosen temperature
const uint8_t kEuromFahrenheitDisabled = 0x00;
const uint8_t kEuromFahrenheitEnabled = 0x04;
// Power and swing
const uint8_t kEuromPowerSwingDisabled = 0x00;
const uint8_t kEuromPowerOn = 0x80;
const uint8_t kEuromSwingOn = 0x40;
// Sleep mode and the "on timer"
const uint8_t kEuromSleepOnTimerDisabled = 0x00;
const uint8_t kEuromSleepEnabled = 0x40;
// The "off timer"
const uint8_t kEuromOffTimerDisabled = 0x00;
const uint8_t kEuromOffTimerEnabled = 0x80;
const uint8_t kEuromOffTimer = kEuromOffTimerEnabled; // Corresponds to 0 hours
// Stuff for all timers
const uint8_t kEuromTimerMin = 0;
const uint8_t kEuromTimerMax = 24;
// Fan speeds
const uint8_t kEuromFanLow = 0x10;
const uint8_t kEuromFanMed = 0x20;
const uint8_t kEuromFanHigh = 0x40;
// Classes
/// Class for handling detailed Eurom A/C messages.
class IREuromAc {
public:
explicit IREuromAc(const uint16_t pin, const bool inverted = false,
const bool use_modulation = true);
void stateReset();
#if SEND_EUROM
void send(const uint16_t repeat = kNoRepeat);
/// 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 65 ms IR signal pulse at 38 kHz.
/// Only ever needs to be run once per object instantiation, if at all.
int8_t calibrate(void) {
return _irsend.calibrate();
}
#endif // SEND_EUROM
void begin(void);
static uint8_t calcChecksum(const uint8_t state[],
const uint16_t length = kEuromStateLength);
static bool validChecksum(const uint8_t state[],
const uint16_t length = kEuromStateLength);
void setRaw(const uint8_t state[]);
uint8_t *getRaw(void);
void on(void);
void off(void);
void setPower(const bool state);
bool getPower(void) const;
void setMode(const uint8_t mode);
uint8_t getMode(void) const;
void setTemp(const uint8_t degrees, const bool fahrenheit = false);
uint8_t getTemp(void) const;
bool getTempIsFahrenheit(void) const;
void setFan(const uint8_t speed);
uint8_t getFan(void) const;
void setSwing(const bool state);
bool getSwing(void) const;
void setSleep(const bool state);
bool getSleep(void) const;
void setOffTimer(const uint8_t duration);
uint8_t getOffTimer(void) const;
void setOnTimer(const uint8_t duration);
uint8_t getOnTimer(void) const;
static uint8_t convertMode(const stdAc::opmode_t mode);
static uint8_t convertFan(const stdAc::fanspeed_t speed);
static bool convertSwing(const stdAc::swingv_t swing);
static stdAc::opmode_t toCommonMode(const uint8_t mode);
static stdAc::fanspeed_t toCommonFanSpeed(const uint8_t speed);
static stdAc::swingv_t toCommonSwing(const bool swing);
stdAc::state_t toCommon(void) const;
String toString(void) const;
#ifndef UNIT_TEST
private:
IRsend _irsend;
#else
/// @cond IGNORE
IRsendTest _irsend;
/// @endcond
#endif
EuromProtocol _;
// Due to some bytes combining multiple functions, we'll need to keep track of
// some of the original values ourselves. Otherwise we wouldn't really be able
// to e.g. return the current mode or temperature, or changing the sleep mode
// without also messing with the timer hours.
uint8_t state_mode_ = kEuromCool;
uint8_t state_celsius_ = 23;
bool state_sleep_ = false;
uint8_t state_on_timer_ = kEuromTimerMin;
// Some helper functions for reusing the above state variables depending on
// context and returning the byte expected by the AC.
uint8_t getModeCelsiusByte(const uint8_t mode, const uint8_t celsius) const;
uint8_t getSleepOnTimerByte(const bool sleep, const uint8_t hours) const;
void checksum(void);
};
#endif // IR_EUROM_H_

View File

@ -41,6 +41,7 @@
// Brand: Fujitsu, Model: AR-REG1U remote (ARRAH2E)
// Brand: OGeneral, Model: AR-RCL1E remote (ARRAH2E)
// Brand: Fujitsu General, Model: AR-JW17 remote (ARDB1)
// Brand: Fujitsu General, Model: AR-JW19 remote (ARDB1)
#ifndef IR_FUJITSU_H_
#define IR_FUJITSU_H_

View File

@ -37,7 +37,8 @@ void IRsend::sendGC(uint16_t buf[], uint16_t len) {
enableIROut(hz);
uint32_t periodic_time = calcUSecPeriod(hz, false);
uint8_t emits =
std::min(buf[kGlobalCacheRptIndex], (uint16_t)kGlobalCacheMaxRepeat);
std::min(buf[kGlobalCacheRptIndex],
static_cast<uint16_t>(kGlobalCacheMaxRepeat));
// Repeat
for (uint8_t repeat = 0; repeat < emits; repeat++) {
// First time through, start at the beginning (kGlobalCacheStartIndex),

View File

@ -471,7 +471,7 @@ bool IRrecv::decodeGoodweather(decode_results* results, uint16_t offset,
DPRINT("DEBUG: inverted = ");
DPRINTLN((uint16_t)inverted);
if (data != (inverted ^ 0xFF)) return false; // Data integrity failed.
dataSoFar |= (uint64_t)data << dataBitsSoFar;
dataSoFar |= static_cast<uint64_t>(data) << dataBitsSoFar;
}
// Footer.

View File

@ -375,6 +375,7 @@ void IRGreeAC::setSwingVertical(const bool automatic, const uint8_t position) {
uint8_t new_position = position;
if (!automatic) {
switch (position) {
case kGreeSwingLastPos:
case kGreeSwingUp:
case kGreeSwingMiddleUp:
case kGreeSwingMiddle:
@ -503,6 +504,7 @@ uint8_t IRGreeAC::convertFan(const stdAc::fanspeed_t speed) {
/// @return The native equivalent of the enum.
uint8_t IRGreeAC::convertSwingV(const stdAc::swingv_t swingv) {
switch (swingv) {
case stdAc::swingv_t::kOff: return kGreeSwingLastPos;
case stdAc::swingv_t::kHighest: return kGreeSwingUp;
case stdAc::swingv_t::kHigh: return kGreeSwingMiddleUp;
case stdAc::swingv_t::kMiddle: return kGreeSwingMiddle;
@ -562,7 +564,7 @@ stdAc::swingv_t IRGreeAC::toCommonSwingV(const uint8_t pos) {
case kGreeSwingMiddle: return stdAc::swingv_t::kMiddle;
case kGreeSwingMiddleDown: return stdAc::swingv_t::kLow;
case kGreeSwingDown: return stdAc::swingv_t::kLowest;
default: return stdAc::swingv_t::kAuto;
default: return stdAc::swingv_t::kOff;
}
}

View File

@ -723,8 +723,10 @@ void IRHaierAC176::setTemp(const uint8_t degree, const bool fahrenheit) {
temp = kHaierAcYrw02MinTempF;
else if (temp > kHaierAcYrw02MaxTempF)
temp = kHaierAcYrw02MaxTempF;
if (degree >= 77) { temp++; }
if (degree >= 79) { temp++; }
if (degree >= 77)
temp++;
if (degree >= 79)
temp++;
// See at IRHaierAC176::getTemp() comments for clarification
_.ExtraDegreeF = temp % 2;
_.Temp = (temp - kHaierAcYrw02MinTempF -_.ExtraDegreeF) >> 1;
@ -741,7 +743,8 @@ void IRHaierAC176::setTemp(const uint8_t degree, const bool fahrenheit) {
/// The unit of temperature is specified by UseFahrenheit value.
/// @return The current setting for temperature.
uint8_t IRHaierAC176::getTemp(void) const {
if (!_.UseFahrenheit) { return _.Temp + kHaierAcYrw02MinTempC; }
if (!_.UseFahrenheit)
return _.Temp + kHaierAcYrw02MinTempC;
uint8_t degree = _.Temp*2 + kHaierAcYrw02MinTempF + _.ExtraDegreeF;
// The way of coding the temperature in degree Fahrenheit is
// kHaierAcYrw02MinTempF + Temp*2 + ExtraDegreeF, for example
@ -778,8 +781,10 @@ uint8_t IRHaierAC176::getTemp(void) const {
// | 84F | 0b1101 | 0b0 |
// | 86F | 0b1110 | 0b0 |
// | 85F | 0b1101 | 0b1 |
if (degree >= 77) { degree--; }
if (degree >= 79) { degree--; }
if (degree >= 77)
degree--;
if (degree >= 79)
degree--;
return degree;
}
@ -958,7 +963,7 @@ uint8_t IRHaierAC176::getTimerMode(void) const { return _.TimerMode; }
/// Set the number of minutes of the On Timer setting.
/// @param[in] mins Nr. of Minutes for the Timer. `0` means disable the timer.
void IRHaierAC176::setOnTimer(const uint16_t mins) {
const uint16_t nr_mins = std::min((uint16_t)(23 * 60 + 59), mins);
const uint16_t nr_mins = std::min(static_cast<uint16_t>(23 * 60 + 59), mins);
_.OnTimerHrs = nr_mins / 60;
_.OnTimerMins = nr_mins % 60;
@ -988,7 +993,7 @@ uint16_t IRHaierAC176::getOnTimer(void) const {
/// Set the number of minutes of the Off Timer setting.
/// @param[in] mins Nr. of Minutes for the Timer. `0` means disable the timer.
void IRHaierAC176::setOffTimer(const uint16_t mins) {
const uint16_t nr_mins = std::min((uint16_t)(23 * 60 + 59), mins);
const uint16_t nr_mins = std::min(static_cast<uint16_t>(23 * 60 + 59), mins);
_.OffTimerHrs = nr_mins / 60;
_.OffTimerMins = nr_mins % 60;
@ -1617,8 +1622,10 @@ void IRHaierAC160::setTemp(const uint8_t degree, const bool fahrenheit) {
temp = kHaierAcYrw02MinTempF;
else if (temp > kHaierAcYrw02MaxTempF)
temp = kHaierAcYrw02MaxTempF;
if (degree >= 77) { temp++; }
if (degree >= 79) { temp++; }
if (degree >= 77)
temp++;
if (degree >= 79)
temp++;
// See at IRHaierAC160::getTemp() comments for clarification
_.ExtraDegreeF = temp % 2;
_.Temp = (temp - kHaierAcYrw02MinTempF -_.ExtraDegreeF) >> 1;
@ -1635,7 +1642,8 @@ void IRHaierAC160::setTemp(const uint8_t degree, const bool fahrenheit) {
/// The unit of temperature is specified by UseFahrenheit value.
/// @return The current setting for temperature.
uint8_t IRHaierAC160::getTemp(void) const {
if (!_.UseFahrenheit) { return _.Temp + kHaierAcYrw02MinTempC; }
if (!_.UseFahrenheit)
return _.Temp + kHaierAcYrw02MinTempC;
uint8_t degree = _.Temp*2 + kHaierAcYrw02MinTempF + _.ExtraDegreeF;
// The way of coding the temperature in degree Fahrenheit is
// kHaierAcYrw02MinTempF + Temp*2 + ExtraDegreeF, for example
@ -1672,8 +1680,10 @@ uint8_t IRHaierAC160::getTemp(void) const {
// | 84F | 0b1101 | 0b0 |
// | 86F | 0b1110 | 0b0 |
// | 85F | 0b1101 | 0b1 |
if (degree >= 77) { degree--; }
if (degree >= 79) { degree--; }
if (degree >= 77)
degree--;
if (degree >= 79)
degree--;
return degree;
}
@ -1854,7 +1864,7 @@ uint8_t IRHaierAC160::getTimerMode(void) const { return _.TimerMode; }
/// Set the number of minutes of the On Timer setting.
/// @param[in] mins Nr. of Minutes for the Timer. `0` means disable the timer.
void IRHaierAC160::setOnTimer(const uint16_t mins) {
const uint16_t nr_mins = std::min((uint16_t)(23 * 60 + 59), mins);
const uint16_t nr_mins = std::min(static_cast<uint16_t>(23 * 60 + 59), mins);
_.OnTimerHrs = nr_mins / 60;
_.OnTimerMins = nr_mins % 60;
@ -1884,7 +1894,7 @@ uint16_t IRHaierAC160::getOnTimer(void) const {
/// Set the number of minutes of the Off Timer setting.
/// @param[in] mins Nr. of Minutes for the Timer. `0` means disable the timer.
void IRHaierAC160::setOffTimer(const uint16_t mins) {
const uint16_t nr_mins = std::min((uint16_t)(23 * 60 + 59), mins);
const uint16_t nr_mins = std::min(static_cast<uint16_t>(23 * 60 + 59), mins);
_.OffTimerHrs = nr_mins / 60;
_.OffTimerMins = nr_mins % 60;

View File

@ -318,8 +318,9 @@ void IRKelonAc::setTimer(uint16_t mins) {
/// @return The timer set minutes
uint16_t IRKelonAc::getTimer() const {
if (_.TimerHours >= 10)
return ((uint16_t) ((_.TimerHours << 1) | _.TimerHalfHour) - 10) * 60;
return (((uint16_t) _.TimerHours) * 60) + (_.TimerHalfHour ? 30 : 0);
return (static_cast<uint16_t>((_.TimerHours << 1) | _.TimerHalfHour) -
10) * 60;
return static_cast<uint16_t>(_.TimerHours) * 60 + (_.TimerHalfHour ? 30 : 0);
}
/// Enable or disable the timer. Note that in order to enable the timer the

View File

@ -38,7 +38,7 @@ void IRsend::sendLegoPf(const uint64_t data, const uint16_t nbits,
// Spec says a pause before transmittion.
if (channelid < 4) space((4 - channelid) * kLegoPfMinCommandLength);
// Spec says there are a minimum of 5 message repeats.
for (uint16_t r = 0; r < std::max(repeat, (uint16_t)5); r++) {
for (uint16_t r = 0; r < std::max(repeat, static_cast<uint16_t>(5)); r++) {
// Lego has a special repeat mode which repeats a message with varying
// start to start times.
sendGeneric(kLegoPfBitMark, kLegoPfHdrSpace,

View File

@ -96,7 +96,7 @@ bool IRrecv::decodeLutron(decode_results *results, uint16_t offset,
break; // We've likely reached the end of a message.
}
// Remove a bit length from the current entry.
entry = std::max(entry, (uint16_t)(kLutronTick / kRawTick)) -
entry = std::max(entry, static_cast<uint16_t>(kLutronTick / kRawTick)) -
kLutronTick / kRawTick;
}
if (offset % 2 && !match(entry, kLutronDelta, 0, kLutronDelta)) {

View File

@ -566,7 +566,7 @@ uint16_t IRMideaAC::getOnTimer(void) const {
/// Setting it will disable that mode/settings.
void IRMideaAC::setOnTimer(const uint16_t mins) {
setEnableSensorTemp(false);
uint8_t halfhours = std::min((uint16_t)(24 * 60), mins) / 30;
uint8_t halfhours = std::min(static_cast<uint16_t>(24 * 60), mins) / 30;
if (halfhours)
_.SensorTemp = ((halfhours - 1) << 1) | 1;
else
@ -589,7 +589,7 @@ uint16_t IRMideaAC::getOffTimer(void) const { return _.OffTimer * 30 + 30; }
/// of the actual device/protocol.
/// @note A value of less than 30 will disable the Timer.
void IRMideaAC::setOffTimer(const uint16_t mins) {
uint8_t halfhours = std::min((uint16_t)(24 * 60), mins) / 30;
uint8_t halfhours = std::min(static_cast<uint16_t>(24 * 60), mins) / 30;
if (halfhours)
_.OffTimer = halfhours - 1;
else

View File

@ -389,8 +389,9 @@ void IRMirageAc::setClock(const uint32_t nr_of_seconds) {
_.Minutes = _.Seconds = 0; // No clock setting. Clear it just in case.
break;
default:
// Limit to 23:59:59
uint32_t remaining = std::min(
nr_of_seconds, (uint32_t)(24 * 60 * 60 - 1)); // Limit to 23:59:59.
nr_of_seconds, static_cast<uint32_t>(24 * 60 * 60 - 1));
_.Seconds = uint8ToBcd(remaining % 60);
remaining /= 60;
_.Minutes = uint8ToBcd(remaining % 60);
@ -586,7 +587,7 @@ uint16_t IRMirageAc::getOnTimer(void) const {
/// Set the number of minutes for the On Timer.
/// @param[in] nr_of_mins How long to set the timer for. 0 disables the timer.
void IRMirageAc::setOnTimer(const uint16_t nr_of_mins) {
uint16_t mins = std::min(nr_of_mins, (uint16_t)(24 * 60));
uint16_t mins = std::min(nr_of_mins, static_cast<uint16_t>(24 * 60));
switch (_model) {
case mirage_ac_remote_model_t::KKG29AC1:
_.OnTimerEnable = (mins > 0);
@ -612,7 +613,7 @@ uint16_t IRMirageAc::getOffTimer(void) const {
/// Set the number of minutes for the Off Timer.
/// @param[in] nr_of_mins How long to set the timer for. 0 disables the timer.
void IRMirageAc::setOffTimer(const uint16_t nr_of_mins) {
uint16_t mins = std::min(nr_of_mins, (uint16_t)(24 * 60));
uint16_t mins = std::min(nr_of_mins, static_cast<uint16_t>(24 * 60));
switch (_model) {
case mirage_ac_remote_model_t::KKG29AC1:
_.OffTimerEnable = (mins > 0);

View File

@ -88,7 +88,8 @@ void IRsend::sendPanasonic64(const uint64_t data, const uint16_t nbits,
/// @note This protocol is a modified version of Kaseikyo.
void IRsend::sendPanasonic(const uint16_t address, const uint32_t data,
const uint16_t nbits, const uint16_t repeat) {
sendPanasonic64(((uint64_t)address << 32) | (uint64_t)data, nbits, repeat);
sendPanasonic64(static_cast<uint64_t>(address) << 32 |
static_cast<uint64_t>(data), nbits, repeat);
}
/// Calculate the raw Panasonic data based on device, subdevice, & function.
@ -105,8 +106,10 @@ uint64_t IRsend::encodePanasonic(const uint16_t manufacturer,
const uint8_t subdevice,
const uint8_t function) {
uint8_t checksum = device ^ subdevice ^ function;
return (((uint64_t)manufacturer << 32) | ((uint64_t)device << 24) |
((uint64_t)subdevice << 16) | ((uint64_t)function << 8) | checksum);
return ((static_cast<uint64_t>(manufacturer) << 32) |
(static_cast<uint64_t>(device) << 24) |
(static_cast<uint64_t>(subdevice) << 16) |
(static_cast<uint64_t>(function) << 8) | checksum);
}
#endif // (SEND_PANASONIC || SEND_DENON)

View File

@ -20,9 +20,11 @@
// Brand: Panasonic, Model: CS-Z9RKR A/C (PANASONIC_AC RKR/6)
// Brand: Panasonic, Model: CS-Z24RKR A/C (PANASONIC_AC RKR/6)
// Brand: Panasonic, Model: CS-YW9MKD A/C (PANASONIC_AC JKE/4)
// Brand: Panasonic, Model: CS-E12QKEW A/C (PANASONIC_AC DKE/3)
// Brand: Panasonic, Model: A75C2311 remote (PANASONIC_AC CKP/5)
// Brand: Panasonic, Model: A75C2616-1 remote (PANASONIC_AC DKE/3)
// Brand: Panasonic, Model: A75C3704 remote (PANASONIC_AC DKE/3)
// Brand: Panasonic, Model: PN1122V remote (PANASONIC_AC DKE/3)
// Brand: Panasonic, Model: A75C3747 remote (PANASONIC_AC JKE/4)
// Brand: Panasonic, Model: CS-E9CKP series A/C (PANASONIC_AC32)
// Brand: Panasonic, Model: A75C2295 remote (PANASONIC_AC32)

View File

@ -80,7 +80,7 @@ void IRsend::sendPioneer(const uint64_t data, const uint16_t nbits,
/// `irsend.sendPioneer(irsend.encodePioneer(0xAA1C, 0xAA1C), 64, 0);`
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1749#issuecomment-1028122645
uint64_t IRsend::encodePioneer(const uint16_t address, const uint16_t command) {
return (((uint64_t)encodeNEC(address >> 8, address & 0xFF)) << 32) |
return static_cast<uint64_t>(encodeNEC(address >> 8, address & 0xFF)) << 32 |
encodeNEC(command >> 8, command & 0xFF);
}
#endif // SEND_PIONEER

View File

@ -62,7 +62,8 @@ void IRsend::sendPronto(uint16_t data[], uint16_t len, uint16_t repeat) {
// Pronto frequency is in Hz.
uint16_t hz =
(uint16_t)(1000000U / (data[kProntoFreqOffset] * kProntoFreqFactor));
static_cast<uint16_t>(1000000U / (data[kProntoFreqOffset] *
kProntoFreqFactor));
enableIROut(hz);
// Grab the length of the two sequences.

View File

@ -129,7 +129,7 @@ uint16_t IRsend::encodeRC5X(const uint8_t address, const uint8_t command,
// The 2nd start/field bit (MSB of the return value) is the value of the 7th
// command bit.
bool s2 = (command >> 6) & 1;
return ((uint16_t)s2 << (kRC5XBits - 1)) |
return (static_cast<uint16_t>(s2) << (kRC5XBits - 1)) |
encodeRC5(address, command, key_released);
}
@ -174,7 +174,8 @@ uint64_t IRsend::encodeRC6(const uint32_t address, const uint8_t command,
case kRC6Mode0Bits:
return ((address & 0xFFF) << 8) | (command & 0xFF);
case kRC6_36Bits:
return ((uint64_t)(address & 0xFFFFFFF) << 8) | (command & 0xFF);
return (static_cast<uint64_t>(address & 0xFFFFFFF) << 8) |
(command & 0xFF);
default:
return 0;
}
@ -360,7 +361,7 @@ bool IRrecv::decodeRC5(decode_results *results, uint16_t offset,
results->repeat = false;
if (is_rc5x) {
results->decode_type = RC5X;
results->command |= ((uint32_t)is_rc5x) << 6;
results->command |= (static_cast<uint32_t>(is_rc5x)) << 6;
} else {
results->decode_type = RC5;
actual_bits--; // RC5 doesn't count the field bit as data.

View File

@ -35,7 +35,6 @@ const uint16_t kRcmmMinGapTicks = 120;
const uint32_t kRcmmMinGap = 3360;
// Use a tolerance of +/-10% when matching some data spaces.
const uint8_t kRcmmTolerance = 10;
const uint16_t kRcmmExcess = 50;
#if SEND_RCMM
/// Send a Philips RC-MM packet.
@ -102,13 +101,15 @@ bool IRrecv::decodeRCMM(decode_results *results, uint16_t offset,
// Calc the maximum size in bits, the message can be, or that we can accept.
int16_t maxBitSize =
std::min((uint16_t)results->rawlen - 5, (uint16_t)sizeof(data) * 8);
std::min(static_cast<uint16_t>(results->rawlen) - 5,
static_cast<uint16_t>(sizeof(data)) * 8);
// Compliance
if (strict) {
// Technically the spec says bit sizes should be 12 xor 24. however
// 32 bits has been seen from a device. We are going to assume
// 12 <= bits <= 32 is the 'required' bit length for the spec.
if (maxBitSize < 12 || maxBitSize > 32) return false;
if (maxBitSize < 12 || maxBitSize > 32)
return false;
if (maxBitSize < nbits)
return false; // Short cut, we can never reach the expected nr. of bits.
}

View File

@ -775,7 +775,7 @@ uint16_t IRSamsungAc::getSleepTimer(void) const {
}
#define TIMER_RESOLUTION(mins) \
(((std::min((mins), (uint16_t)(24 * 60))) / 10) * 10)
(((std::min((mins), static_cast<uint16_t>(24 * 60))) / 10) * 10)
/// Set the On Timer value of the A/C.
/// @param[in] nr_of_mins The number of minutes the timer should be.

View File

@ -892,7 +892,8 @@ uint16_t IRSanyoAc88::getClock(void) const {
/// Set the current clock time.
/// @param[in] mins_since_midnight The time as nr. of minutes past midnight.
void IRSanyoAc88::setClock(const uint16_t mins_since_midnight) {
uint16_t mins = std::min(mins_since_midnight, (uint16_t)(23 * 60 + 59));
uint16_t mins = std::min(mins_since_midnight,
static_cast<uint16_t>(23 * 60 + 59));
_.ClockMins = mins % 60;
_.ClockHrs = mins / 60;
_.ClockSecs = 0;

View File

@ -35,9 +35,11 @@ const uint16_t kSharpGapTicks = 1677;
const uint16_t kSharpGap = kSharpGapTicks * kSharpTick;
// Address(5) + Command(8) + Expansion(1) + Check(1)
const uint64_t kSharpToggleMask =
((uint64_t)1 << (kSharpBits - kSharpAddressBits)) - 1;
const uint64_t kSharpAddressMask = ((uint64_t)1 << kSharpAddressBits) - 1;
const uint64_t kSharpCommandMask = ((uint64_t)1 << kSharpCommandBits) - 1;
(static_cast<uint64_t>(1) << (kSharpBits - kSharpAddressBits)) - 1;
const uint64_t kSharpAddressMask = (static_cast<uint64_t>(1) <<
kSharpAddressBits) - 1;
const uint64_t kSharpCommandMask = (static_cast<uint64_t>(1) <<
kSharpCommandBits) - 1;
using irutils::addBoolToString;
using irutils::addFanToString;

View File

@ -19,6 +19,7 @@
/// @note Sherwood remote codes appear to be NEC codes with a mandatory repeat
/// code. i.e. repeat should be >= kSherwoodMinRepeat (1).
void IRsend::sendSherwood(uint64_t data, uint16_t nbits, uint16_t repeat) {
sendNEC(data, nbits, std::max((uint16_t)kSherwoodMinRepeat, repeat));
sendNEC(data, nbits,
std::max(static_cast<uint16_t>(kSherwoodMinRepeat), repeat));
}
#endif // SEND_SHERWOOD

View File

@ -11,6 +11,7 @@
// Brand: Daewoo, Model: DSB-F0934ELH-V A/C
// Brand: Daewoo, Model: GYKQ-52E remote
// Brand: TCL, Model: GYKQ-58(XM) remote (TCL96AC)
// Brand: Electrolux, Model: EACM CL/N3 series remote
#ifndef IR_TCL_H_
#define IR_TCL_H_

View File

@ -231,7 +231,8 @@ uint16_t IRTecoAc::getTimer(void) const {
/// `0` will clear the timer. Max is 24 hrs.
/// @note Time is stored internally in increments of 30 mins.
void IRTecoAc::setTimer(const uint16_t nr_mins) {
uint16_t mins = std::min(nr_mins, (uint16_t)(24 * 60)); // Limit to 24 hrs.
// Limit to 24 hrs
uint16_t mins = std::min(nr_mins, static_cast<uint16_t>(24 * 60));
uint8_t hours = mins / 60;
_.TimerOn = mins > 0; // Set the timer flag.
_.HalfHour = (mins % 60) >= 30;

View File

@ -40,6 +40,7 @@ using irutils::addModeToString;
using irutils::addTempToString;
using irutils::checkInvertedBytePairs;
using irutils::invertBytePairs;
using irutils::addModelToString;
#if SEND_TOSHIBA_AC
/// Send a Toshiba A/C message.
@ -104,8 +105,9 @@ void IRToshibaAC::send(const uint16_t repeat) {
uint16_t IRToshibaAC::getInternalStateLength(const uint8_t state[],
const uint16_t size) {
if (size < kToshibaAcLengthByte) return 0;
return std::min((uint16_t)(state[kToshibaAcLengthByte] + kToshibaAcMinLength),
kToshibaACStateLengthLong);
// Fix: Extract the last 4 bits instead
return std::min(static_cast<uint16_t>((state[kToshibaAcLengthByte] & 0xF)
+ kToshibaAcMinLength), kToshibaACStateLengthLong);
}
/// Get the length of the current internal state per the protocol structure.
@ -462,7 +464,8 @@ stdAc::state_t IRToshibaAC::toCommon(const stdAc::state_t *prev) const {
String IRToshibaAC::toString(void) const {
String result = "";
result.reserve(95);
result += addTempToString(getTemp(), true, false);
result += addModelToString(decode_type_t::TOSHIBA_AC, getModel(), false);
result += addTempToString(getTemp(), true);
switch (getStateLength()) {
case kToshibaACStateLengthShort:
result += addIntToString(getSwing(true), kSwingVStr);
@ -493,6 +496,30 @@ String IRToshibaAC::toString(void) const {
return result;
}
/// Get the model information currently known.
/// @return The known model number.
toshiba_ac_remote_model_t IRToshibaAC::getModel(void) const {
switch (_.Model) {
case kToshibaAcRemoteB:
return toshiba_ac_remote_model_t::kToshibaGenericRemote_B;
default:
return toshiba_ac_remote_model_t::kToshibaGenericRemote_A;
}
}
/// Set the current model for the remote.
/// @param[in] model The model number.
void IRToshibaAC::setModel(const toshiba_ac_remote_model_t model) {
switch (model) {
case toshiba_ac_remote_model_t::kToshibaGenericRemote_B:
_.Model = kToshibaAcRemoteB;
break;
default:
_.Model = kToshibaAcRemoteA;
break;
}
}
#if DECODE_TOSHIBA_AC
/// Decode the supplied Toshiba A/C message.
/// Status: STABLE / Working.

View File

@ -53,7 +53,11 @@ union ToshibaProtocol{
///< 1 (56 bit message)
///< 3 (72 bit message)
///< 4 (80 bit message)
uint8_t Length :8;
uint8_t Length :4;
// Toshiba remote type
// 0 - Remote control A
// 1 - Remote control B
uint8_t Model :4;
// Byte[3] - The bit-inverted value of the "length" byte.
uint8_t :8;
// Byte[4]
@ -111,6 +115,9 @@ const uint8_t kToshibaAcFanMax = 5; // 0b101
const uint8_t kToshibaAcTurboOn = 1; // 0b01
const uint8_t kToshibaAcEconoOn = 3; // 0b11
const uint8_t kToshibaAcRemoteA = 0; // 0b0000
const uint8_t kToshibaAcRemoteB = 1; // 0b0001
// Legacy defines. (Deprecated)
#define TOSHIBA_AC_AUTO kToshibaAcAuto
#define TOSHIBA_AC_COOL kToshibaAcCool
@ -140,6 +147,8 @@ class IRToshibaAC {
void begin(void);
void on(void);
void off(void);
void setModel(const toshiba_ac_remote_model_t model);
toshiba_ac_remote_model_t getModel() const;
void setPower(const bool on);
bool getPower(void) const;
void setTemp(const uint8_t degrees);

View File

@ -422,7 +422,7 @@ uint16_t IRVoltas::getOnTime(void) const {
/// 0 disables the timer. Max is 23 hrs & 59 mins (1439 mins)
void IRVoltas::setOnTime(const uint16_t nr_of_mins) {
// Cap the total number of mins.
uint16_t mins = std::min(nr_of_mins, (uint16_t)(23 * 60 + 59));
uint16_t mins = std::min(nr_of_mins, static_cast<uint16_t>(23 * 60 + 59));
uint16_t hrs = (mins / 60) + 1;
_.OnTimerMins = mins % 60;
_.OnTimer12Hr = hrs / 12;
@ -442,7 +442,7 @@ uint16_t IRVoltas::getOffTime(void) const {
/// 0 disables the timer. Max is 23 hrs & 59 mins (1439 mins)
void IRVoltas::setOffTime(const uint16_t nr_of_mins) {
// Cap the total number of mins.
uint16_t mins = std::min(nr_of_mins, (uint16_t)(23 * 60 + 59));
uint16_t mins = std::min(nr_of_mins, static_cast<uint16_t>(23 * 60 + 59));
uint16_t hrs = (mins / 60) + 1;
_.OffTimerMins = mins % 60;
_.OffTimer12Hr = hrs / 12;

View File

@ -126,7 +126,8 @@ void IRsend::sendXmp(const uint64_t data, const uint16_t nbits,
uint64_t send_data = data;
for (uint16_t r = 0; r <= repeat; r++) {
uint16_t bits_so_far = kXmpWordSize;
for (uint64_t mask = ((uint64_t)kXmpMaxWordValue) << (nbits - kXmpWordSize);
for (uint64_t mask = static_cast<uint64_t>(kXmpMaxWordValue) <<
(nbits - kXmpWordSize);
mask;
mask >>= kXmpWordSize) {
uint8_t word = (send_data & mask) >> (nbits - bits_so_far);

View File

@ -751,6 +751,9 @@ D_STR_INDIRECT " " D_STR_MODE
#ifndef D_STR_ARRIS
#define D_STR_ARRIS "ARRIS"
#endif // D_STR_ARRIS
#ifndef D_STR_BLUESTARHEACY
#define D_STR_BLUESTARHEAVY "BLUESTARHEAVY"
#endif // D_STR_TESTEXAMPLE
#ifndef D_STR_BOSCH
#define D_STR_BOSCH "BOSCH"
#endif // D_STR_BOSCH
@ -841,6 +844,9 @@ D_STR_INDIRECT " " D_STR_MODE
#ifndef D_STR_EPSON
#define D_STR_EPSON "EPSON"
#endif // D_STR_EPSON
#ifndef D_STR_EUROM
#define D_STR_EUROM "EUROM"
#endif // D_STR_EUROM
#ifndef D_STR_FUJITSU_AC
#define D_STR_FUJITSU_AC "FUJITSU_AC"
#endif // D_STR_FUJITSU_AC
@ -1120,6 +1126,12 @@ D_STR_INDIRECT " " D_STR_MODE
#ifndef D_STR_ZEPEAL
#define D_STR_ZEPEAL "ZEPEAL"
#endif // D_STR_ZEPEAL
#ifndef D_STR_TOSHIBAGENERICREMOTEA
#define D_STR_TOSHIBAGENERICREMOTEA "TOSHIBA REMOTE A"
#endif // D_STR_TOSHIBAGENERICREMOTEA
#ifndef D_STR_TOSHIBAGENERICREMOTEB
#define D_STR_TOSHIBAGENERICREMOTEB "TOSHIBA REMOTE B"
#endif // D_STR_TOSHIBAGENERICREMOTEB
// IRrecvDumpV2+
#ifndef D_STR_TIMESTAMP

View File

@ -143,7 +143,7 @@
#define D_STR_MESGDESC "Описание Сообщения."
#define D_STR_TOLERANCE "Допуск"
#define D_STR_IRRECVDUMP_STARTUP \
"IRrecvDump запущен и ождает ИК команды на Входе %d"
"IRrecvDump запущен и ожидает ИК команды на Входе %d"
#define D_WARN_BUFFERFULL \
"ПРЕДУПРЕЖДЕНИЕ: ИК код слишком велик для буфера (>= %d). " \
"Этому результату не следует доверять, пока это не будет исправлено. " \

View File

@ -0,0 +1,190 @@
// Copyright 2024 - @misa1515
// Locale/language file for Slovakia.
// This file will override the default values located in `defaults.h`.
#ifndef LOCALE_SK_SK_H_
#define LOCALE_SK_SK_H_
#define D_STR_UNKNOWN "NEZNÁME"
#define D_STR_PROTOCOL "Protokol"
#define D_STR_POWER "Výkon"
#define D_STR_PREVIOUS "Predchádzajúce"
#define D_STR_ON "Zap"
#define D_STR_OFF "Vyp"
#define D_STR_MODE "Režim"
#define D_STR_TOGGLE "Prepnúť"
#define D_STR_SLEEP "Spať"
#define D_STR_LIGHT "Svetlo"
#define D_STR_POWERFUL "Výkonný"
#define D_STR_QUIET "Tichý"
#define D_STR_ECONO "Econo"
#define D_STR_SWING "Kývanie"
#define D_STR_SWINGH D_STR_SWING"(H)" // Set `D_STR_SWING` prvé!
#define D_STR_SWINGV D_STR_SWING"(V)" // Set `D_STR_SWING` prvé!
#define D_STR_BEEP "Pípnutie"
#define D_STR_MOULD "Pleseň"
#define D_STR_CLEAN "Čistiť"
#define D_STR_PURIFY "Čistota"
#define D_STR_TIMER "Časovač"
#define D_STR_ONTIMER D_STR_ON " " D_STR_TIMER // Set `D_STR_ON` prvé!
#define D_STR_OFFTIMER D_STR_OFF " " D_STR_TIMER // Set `D_STR_OFF` prvé!
#define D_STR_TIMERMODE D_STR_TIMER " " D_STR_MODE // Set `D_STR_MODE` prvé!
#define D_STR_CLOCK "Hodiny"
#define D_STR_COMMAND "Príkaz"
#define D_STR_XFAN "XVentilátor"
#define D_STR_HEALTH "Zdravie"
#define D_STR_TEMP "Teplota"
#define D_STR_IFEEL "IPocitovo"
#define D_STR_ISEE "IVidieť"
#define D_STR_HUMID "Vlhkosť"
#define D_STR_SAVE "Uložiť"
#define D_STR_EYE "Oči"
#define D_STR_FOLLOW "Nasledovať"
#define D_STR_FRESH "Čerstvé"
#define D_STR_HOLD "Podržať"
#define D_STR_8C_HEAT "8C " D_STR_HEAT // Set `D_STR_HEAT` prvé!
#define D_STR_10C_HEAT "10C " D_STR_HEAT // Set `D_STR_HEAT` prvé!
#define D_STR_BUTTON "Tlačidlo"
#define D_STR_NIGHT "Noc"
#define D_STR_SILENT "Tiché"
#define D_STR_CELSIUS "Celzia"
#define D_STR_FAHRENHEIT "Fahrenheit"
#define D_STR_CELSIUS_FAHRENHEIT D_STR_CELSIUS "/" D_STR_FAHRENHEIT
#define D_STR_UP "Hore"
#define D_STR_TEMPUP D_STR_TEMP " " D_STR_UP // Set `D_STR_TEMP` prvé!
#define D_STR_DOWN "Dole"
#define D_STR_TEMPDOWN D_STR_TEMP " " D_STR_DOWN // Set `D_STR_TEMP` prvé!
#define D_STR_CHANGE "Zmeniť"
#define D_STR_START "Štart"
#define D_STR_MOVE "Posun"
#define D_STR_SET "Nastaviť"
#define D_STR_CANCEL "Zrušiť"
#define D_STR_COMFORT "Komfort"
#define D_STR_SENSOR "Snímač"
#define D_STR_ABSENSEDETECT "Detekcia neprítomnosti"
#define D_STR_DIRECT "Priamo"
#define D_STR_INDIRECT "Nepriamo"
#define D_STR_DIRECTINDIRECTMODE D_STR_DIRECT " / " \
D_STR_INDIRECT " " D_STR_MODE
#define D_STR_DISPLAY "Displej"
#define D_STR_WEEKLY "Týždenne"
#define D_STR_WEEKLYTIMER D_STR_WEEKLY " " D_STR_TIMER // Needs `D_STR_WEEKLY`!
#define D_STR_LAST "Posledné"
#define D_STR_FAST "Rýchlo"
#define D_STR_SLOW "Pomaly"
#define D_STR_AIRFLOW "Prúd vzduchu"
#define D_STR_STEP "Krok"
#define D_STR_INSIDE "Vnútri"
#define D_STR_OUTSIDE "Vonku"
#define D_STR_LOUD "Hlasno"
#define D_STR_UPPER "Horná"
#define D_STR_LOWER "Dolná"
#define D_STR_BREEZE "Vánok"
#define D_STR_CIRCULATE "Cirkulácia"
#define D_STR_CEILING "Strop"
#define D_STR_WALL "Stena"
#define D_STR_ROOM "Izba"
#define D_STR_6THSENSE "6ty zmysel"
#define D_STR_ZONEFOLLOW "Sledovanie zóny"
#define D_STR_FIXED "Fixované"
#define D_STR_TYPE "Typ"
#define D_STR_SPECIAL "Špeciálne"
#define D_STR_RECYCLE "Recyklovať"
#define D_STR_LOCK "Zamknuté"
#define D_STR_AUTOMATIC "Automatické"
#define D_STR_MANUAL "Manuálne"
#define D_STR_COOL "Chladiť"
#define D_STR_COOLING "Chladenie"
#define D_STR_HEAT "Ohrev"
#define D_STR_HEATING "Kúrenie"
#define D_STR_FAN "Ventilátor"
#define D_STR_FANONLY "len ventilátor"
#define D_STR_FAN_ONLY "len_ventilátor"
#define D_STR_ONLY "Len"
#define D_STR_FANSPACEONLY D_STR_FAN " " D_STR_ONLY
#define D_STR_FANONLYNOSPACE D_STR_FAN D_STR_ONLY
#define D_STR_DRY "Sušiť"
#define D_STR_DRYING "Sušenie"
#define D_STR_DEHUMIDIFY "Odvlhčiť"
#define D_STR_MAX "Max"
#define D_STR_MIN "Min"
#define D_STR_MED "Str"
#define D_STR_MEDIUM "Stredné"
#define D_STR_MED_HIGH D_STR_MED "-" D_STR_HIGH
#define D_STR_HIGHEST "Najvyššie"
#define D_STR_HIGH "Vysoké"
#define D_STR_HI "Vys"
#define D_STR_MID "Str"
#define D_STR_MIDDLE "Stredné"
#define D_STR_LOW "Nózke"
#define D_STR_LO "Níz"
#define D_STR_LOWEST "Najnižšie"
#define D_STR_RIGHT "Pravé"
#define D_STR_MAXRIGHT D_STR_MAX " " D_STR_RIGHT // Set `D_STR_MAX` prvé!
#define D_STR_MAXRIGHT_NOSPACE D_STR_MAX D_STR_RIGHT // Set `D_STR_MAX` prvé!
#define D_STR_RIGHTMAX D_STR_RIGHT " " D_STR_MAX // Set `D_STR_MAX` prvé!
#define D_STR_RIGHTMAX_NOSPACE D_STR_RIGHT D_STR_MAX // Set `D_STR_MAX` prvé!
#define D_STR_LEFT "Vľavo"
#define D_STR_MAXLEFT D_STR_MAX " " D_STR_LEFT // Set `D_STR_MAX` prvé!
#define D_STR_MAXLEFT_NOSPACE D_STR_MAX D_STR_LEFT // Set `D_STR_MAX` prvé!
#define D_STR_LEFTMAX D_STR_LEFT " " D_STR_MAX // Set `D_STR_MAX` prvé!
#define D_STR_LEFTMAX_NOSPACE D_STR_LEFT D_STR_MAX // Set `D_STR_MAX` prvé!
#define D_STR_WIDE "Široké"
#define D_STR_CENTRE "Stredné"
#define D_STR_TOP "Horné"
#define D_STR_BOTTOM "Dolné"
#define D_STR_UPPER_MIDDLE D_STR_UPPER "-" D_STR_MIDDLE
#define D_STR_CONFIG "Konfigurácia"
#define D_STR_CONTROL "Riadenie"
#define D_STR_SET_TIMER D_STR_SET " " D_STR_TIMER
#define D_STR_SCHEDULE "Rozvrh"
#define D_STR_CH "CH#"
#define D_STR_TIMER_ACTIVE_DAYS "TimerActiveDays"
#define D_STR_KEY "Kľúč"
#define D_STR_VALUE "Hodnota"
// Compound words/phrases/descriptions from pre-defined words.
// Note: Obviously these need to be defined *after* their component words.
#define D_STR_ECONOTOGGLE D_STR_ECONO " " D_STR_TOGGLE
#define D_STR_EYEAUTO D_STR_EYE " " D_STR_AUTO
#define D_STR_LIGHTTOGGLE D_STR_LIGHT " " D_STR_TOGGLE
#define D_STR_OUTSIDEQUIET D_STR_OUTSIDE " " D_STR_QUIET
#define D_STR_POWERTOGGLE D_STR_POWER " " D_STR_TOGGLE
#define D_STR_POWERBUTTON D_STR_POWER " " D_STR_BUTTON
#define D_STR_PREVIOUSPOWER D_STR_PREVIOUS " " D_STR_POWER
#define D_STR_DISPLAYTEMP D_STR_DISPLAY " " D_STR_TEMP
#define D_STR_IFEELREPORT D_STR_IFEEL " " D_STR_REPORT
#define D_STR_SENSORTEMP D_STR_SENSOR " " D_STR_TEMP
#define D_STR_SLEEP_TIMER D_STR_SLEEP " " D_STR_TIMER
#define D_STR_SWINGVMODE D_STR_SWINGV " " D_STR_MODE
#define D_STR_SWINGVTOGGLE D_STR_SWINGV " " D_STR_TOGGLE
#define D_STR_TURBOTOGGLE D_STR_TURBO " " D_STR_TOGGLE
// Separators
#define D_STR_DAY "Deň"
#define D_STR_DAYS D_STR_DAY "s"
#define D_STR_HOUR "Hodina"
#define D_STR_HOURS D_STR_HOUR "s"
#define D_STR_MINUTE "Minúta"
#define D_STR_MINUTES D_STR_MINUTE "s"
#define D_STR_SECOND "Sekunda"
#define D_STR_SECONDS D_STR_SECOND "s"
#define D_STR_NOW "Teraz"
#define D_STR_YES "Áno"
#define D_STR_NO "Nie"
#define D_STR_REPEAT "Opakovať"
#define D_STR_CODE "Kód"
#define D_STR_BITS "Bity"
// IRrecvDumpV2+
#define D_STR_TIMESTAMP "Časová pečiatka"
#define D_STR_LIBRARY "Knižnica"
#define D_STR_MESGDESC "Mesg Desc."
#define D_STR_TOLERANCE "Tolerancia"
#define D_STR_IRRECVDUMP_STARTUP \
"IRrecvDump teraz beží a čaká na IR vstup na pine %d"
#define D_WARN_BUFFERFULL \
"UPOZORNENIE: IR kód je príliš veľký pre vyrovnávaciu pamäť (>= %d). " \
"Tomuto výsledku by sa nemalo dôverovať, kým sa to nevyrieši. " \
"Upraviť a zväčšiť `kCaptureBufferSize`."
#endif // LOCALE_SK_SK_H_

View File

@ -247,6 +247,36 @@ TEST(TestIRac, Coolix) {
ASSERT_EQ(stdAc::ac_command_t::kControlCommand, r.command);
}
TEST(TestIRac, Coolix_Issue2103) {
IRCoolixAC ac(kGpioUnused);
IRac irac(kGpioUnused);
IRrecv capture(kGpioUnused);
char expected[] =
"Power: On, Mode: 4 (Fan), Fan: 5 (Auto), Zone Follow: Off, "
"Sensor Temp: Off";
ac.begin();
irac.coolix(&ac,
true, // Power
stdAc::opmode_t::kFan, // Mode
21, // Celsius
kNoTempValue, // Sensor Temp
stdAc::fanspeed_t::kAuto, // Fan speed
stdAc::swingv_t::kOff, // Vertical swing
stdAc::swingh_t::kOff, // Horizontal swing
false, // iFeel
false, // Turbo
false, // Light
false, // Clean
-1); // Sleep
ASSERT_EQ(expected, ac.toString());
ac._irsend.makeDecodeResult();
EXPECT_TRUE(capture.decode(&ac._irsend.capture));
ASSERT_EQ(COOLIX, ac._irsend.capture.decode_type);
ASSERT_EQ(kCoolixBits, ac._irsend.capture.bits);
ASSERT_EQ(expected, IRAcUtils::resultAcToString(&ac._irsend.capture));
}
TEST(TestIRac, Corona) {
IRCoronaAc ac(kGpioUnused);
IRac irac(kGpioUnused);
@ -613,7 +643,7 @@ TEST(TestIRac, Electra) {
char expected[] =
"Power: On, Mode: 6 (Fan), Temp: 26C, Fan: 1 (High), "
"Swing(V): On, Swing(H): On, Light: Toggle, Clean: On, Turbo: On, "
"IFeel: Off";
"Quiet: On, IFeel: Off";
ac.begin();
irac.electra(&ac,
@ -625,6 +655,7 @@ TEST(TestIRac, Electra) {
stdAc::swingv_t::kAuto, // Vertical swing
stdAc::swingh_t::kLeft, // Horizontal swing
false, // iFeel
true, // Quiet
true, // Turbo
true, // Light (toggle)
true); // Clean
@ -2157,8 +2188,8 @@ TEST(TestIRac, Toshiba) {
IRac irac(kGpioUnused);
IRrecv capture(kGpioUnused);
char expected[] =
"Temp: 29C, Power: On, Mode: 2 (Dry), Fan: 2 (UNKNOWN), "
"Turbo: Off, Econo: On, Filter: Off";
"Model: 0 (TOSHIBA REMOTE A), Temp: 29C, Power: On, Mode: 2 (Dry), "
"Fan: 2 (UNKNOWN), Turbo: Off, Econo: On, Filter: Off";
ac.begin();
irac.toshiba(&ac,
@ -3144,8 +3175,8 @@ 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, Filter: Off";
"Model: 0 (TOSHIBA REMOTE A), Temp: 19C, Power: On, Mode: 4 (Fan), "
"Fan: 0 (Auto), Turbo: Off, Econo: Off, Filter: Off";
ac._irsend.reset();
irac.toshiba(&ac,
irac.next.power, // Power
@ -3171,8 +3202,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, "
"Filter: Off";
"Model: 0 (TOSHIBA REMOTE A), Temp: 19C, Power: Off, Fan: 0 (Auto), "
"Turbo: Off, Econo: Off, Filter: Off";
ac._irsend.reset();
irac.toshiba(&ac,
irac.next.power, // Power

View File

@ -674,16 +674,16 @@ TEST(TestSend, GenericSimpleSendMethodFailure) {
for (int i = 0; i <= kLastDecodeType; i++) {
if (hasACState((decode_type_t)i) && i != GREE) { // Gree is an exception.
// Check it fails.
ASSERT_FALSE(irsend.send((decode_type_t)i, (uint64_t)0, 0));
ASSERT_FALSE(irsend.send((decode_type_t)i, static_cast<uint64_t>(0), 0));
}
}
// Test some other special cases.
ASSERT_FALSE(irsend.send(UNKNOWN, (uint64_t)0, 0));
ASSERT_FALSE(irsend.send(UNUSED, (uint64_t)0, 0));
ASSERT_FALSE(irsend.send(RAW, (uint64_t)0, 0));
ASSERT_FALSE(irsend.send(PRONTO, (uint64_t)0, 0));
ASSERT_FALSE(irsend.send(GLOBALCACHE, (uint64_t)0, 0));
ASSERT_FALSE(irsend.send(UNKNOWN, static_cast<uint64_t>(0), 0));
ASSERT_FALSE(irsend.send(UNUSED, static_cast<uint64_t>(0), 0));
ASSERT_FALSE(irsend.send(RAW, static_cast<uint64_t>(0), 0));
ASSERT_FALSE(irsend.send(PRONTO, static_cast<uint64_t>(0), 0));
ASSERT_FALSE(irsend.send(GLOBALCACHE, static_cast<uint64_t>(0), 0));
}
// Test expected to work/produce a message for complex irsend:send()

View File

@ -72,7 +72,7 @@ class IRsendTest : public IRsend {
capture.decode_type = UNKNOWN;
capture.bits = 0;
capture.rawlen = last + 2 - offset;
capture.overflow = (last - offset >= (int16_t)RAW_BUF);
capture.overflow = (last - offset >= static_cast<uint16_t>(RAW_BUF));
capture.repeat = false;
capture.address = 0;
capture.command = 0;

View File

@ -582,12 +582,12 @@ TEST(TestUtils, getBit) {
EXPECT_FALSE(GETBIT8((uint8_t)0b01111111, 7));
EXPECT_TRUE(GETBIT8((uint8_t)0b10000000, 7));
// uint64_t method.
EXPECT_FALSE(irutils::getBit((uint64_t)0, 0));
EXPECT_TRUE(irutils::getBit((uint64_t)1, 0));
EXPECT_FALSE(irutils::getBit((uint64_t)0b01, 1));
EXPECT_TRUE(irutils::getBit((uint64_t)0b10, 1));
EXPECT_FALSE(irutils::getBit((uint64_t)0b01111111, 7));
EXPECT_TRUE(irutils::getBit((uint64_t)0b10000000, 7));
EXPECT_FALSE(irutils::getBit(static_cast<uint64_t>(0), 0));
EXPECT_TRUE(irutils::getBit(static_cast<uint64_t>(1), 0));
EXPECT_FALSE(irutils::getBit(static_cast<uint64_t>(0b01), 1));
EXPECT_TRUE(irutils::getBit(static_cast<uint64_t>(0b10), 1));
EXPECT_FALSE(irutils::getBit(static_cast<uint64_t>(0b01111111), 7));
EXPECT_TRUE(irutils::getBit(static_cast<uint64_t>(0b10000000), 7));
}
TEST(TestUtils, setBit) {
@ -603,16 +603,17 @@ TEST(TestUtils, setBit) {
EXPECT_EQ(0b11111111, irutils::setBit((uint8_t)0b01111111, 7, true));
EXPECT_EQ(0, irutils::setBit((uint8_t)0b10000000, 7, false));
// uint64_t method.
EXPECT_EQ(0, irutils::setBit((uint64_t)0, 0, false));
EXPECT_EQ(0, irutils::setBit((uint64_t)1, 0, false));
EXPECT_EQ(1, irutils::setBit((uint64_t)0, 0, true));
EXPECT_EQ(1, irutils::setBit((uint64_t)1, 0, true));
EXPECT_EQ(0b101, irutils::setBit((uint64_t)0b101, 1, false));
EXPECT_EQ(0b100, irutils::setBit((uint64_t)0b110, 1, false));
EXPECT_EQ(0b111, irutils::setBit((uint64_t)0b101, 1, true));
EXPECT_EQ(0b110, irutils::setBit((uint64_t)0b110, 1, true));
EXPECT_EQ(0b11111111, irutils::setBit((uint64_t)0b01111111, 7, true));
EXPECT_EQ(0, irutils::setBit((uint64_t)0b10000000, 7, false));
EXPECT_EQ(0, irutils::setBit(static_cast<uint64_t>(0), 0, false));
EXPECT_EQ(0, irutils::setBit(static_cast<uint64_t>(1), 0, false));
EXPECT_EQ(1, irutils::setBit(static_cast<uint64_t>(0), 0, true));
EXPECT_EQ(1, irutils::setBit(static_cast<uint64_t>(1), 0, true));
EXPECT_EQ(0b101, irutils::setBit(static_cast<uint64_t>(0b101), 1, false));
EXPECT_EQ(0b100, irutils::setBit(static_cast<uint64_t>(0b110), 1, false));
EXPECT_EQ(0b111, irutils::setBit(static_cast<uint64_t>(0b101), 1, true));
EXPECT_EQ(0b110, irutils::setBit(static_cast<uint64_t>(0b110), 1, true));
EXPECT_EQ(0b11111111,
irutils::setBit(static_cast<uint64_t>(0b01111111), 7, true));
EXPECT_EQ(0, irutils::setBit(static_cast<uint64_t>(0b10000000), 7, false));
// uint8_t Pointer method.
uint8_t data = 0;
irutils::setBit(&data, 0, false);

View File

@ -2,7 +2,13 @@
// Copyright 2022 Mateusz Bronk (mbronk)
#include <gtest/gtest.h>
#include <gmock/gmock.h>
#include <algorithm>
#include <memory>
#include <set>
#include <string>
#include <tuple>
#include <utility>
#include <vector>
#include "ir_Argo.h"
#include "IRac.h"
#include "IRrecv.h"

View File

@ -393,6 +393,7 @@ TEST(TestCoolixACClass, SetAndGetRaw) {
TEST(TestCoolixACClass, SetAndGetTemp) {
IRCoolixAC ac(kGpioUnused);
// Celcius
ac.setTemp(25);
EXPECT_EQ(25, ac.getTemp());
ac.setTemp(kCoolixTempMin);
@ -403,6 +404,26 @@ TEST(TestCoolixACClass, SetAndGetTemp) {
EXPECT_EQ(kCoolixTempMin, ac.getTemp());
ac.setTemp(kCoolixTempMax + 1);
EXPECT_EQ(kCoolixTempMax, ac.getTemp());
// Fahrenheit low
ac.setTemp(70);
EXPECT_EQ(70, ac.getTemp());
ac.setTemp(kCoolixTempLowFMin);
EXPECT_EQ(kCoolixTempLowFMin, ac.getTemp());
ac.setTemp(kCoolixTempLowFMax);
EXPECT_EQ(kCoolixTempLowFMax, ac.getTemp());
ac.setTemp(kCoolixTempLowFMin - 1);
EXPECT_EQ(kCoolixTempMax, ac.getTemp());
// Fahrenheit high
ac.setTemp(80);
EXPECT_EQ(80, ac.getTemp());
ac.setTemp(kCoolixTempHighFMin);
EXPECT_EQ(kCoolixTempHighFMin, ac.getTemp());
ac.setTemp(kCoolixTempHighFMax);
EXPECT_EQ(kCoolixTempHighFMax, ac.getTemp());
ac.setTemp(kCoolixTempHighFMax + 1);
EXPECT_EQ(kCoolixTempMax, ac.getTemp());
}
TEST(TestCoolixACClass, SetAndGetMode) {
@ -1104,3 +1125,55 @@ TEST(TestCoolixACClass, Issue2012) {
ac.toString());
EXPECT_EQ(kNoTempValue, ac.toCommon().sensorTemperature);
}
// Test decoding Fahrenheit from Coolix48.
TEST(TestCoolixACClass, SetRawFahrenheit) {
IRsendTest irsend(kGpioUnused);
IRrecv irrecv(kGpioUnused);
IRCoolixAC ac(kGpioUnused);
ac.stateReset();
ac.setRawFromCoolix48(0xB25DFF00C03F);
EXPECT_EQ(
"Power: On, Mode: 0 (Cool), Fan: 7 (Fixed), Temp: 63F, "
"Zone Follow: Off, Sensor Temp: Off",
ac.toString());
}
// Test sending and decoding of Fahrenheit values.
TEST(TestCoolixACClass, SendFahrenheit) {
IRrecv irrecv(kGpioUnused);
IRCoolixAC ac(kGpioUnused);
const uint32_t on_cool_63f_fan_fixed_noparity = 0xB2FFC0;
ac.begin();
ac.on();
ac.setPower(true);
ac.setMode(kCoolixCool);
ac.setFan(kCoolixFanFixed);
ac.setTemp(63);
EXPECT_EQ(on_cool_63f_fan_fixed_noparity, ac.getRaw());
ac.send();
ac._irsend.makeDecodeResult();
ASSERT_TRUE(irrecv.decode(&ac._irsend.capture));
EXPECT_EQ(COOLIX48, ac._irsend.capture.decode_type);
EXPECT_EQ(kCoolix48Bits, ac._irsend.capture.bits);
EXPECT_EQ(0xB25DFF00C03F, ac._irsend.capture.value);
EXPECT_EQ(0x0, ac._irsend.capture.address);
EXPECT_EQ(0x0, ac._irsend.capture.command);
const uint32_t on_cool_80f_fan_fixed_noparity = 0xB2FF20;
ac.setTemp(80);
EXPECT_EQ(on_cool_80f_fan_fixed_noparity, ac.getRaw());
ac._irsend.reset();
ac.send();
ac._irsend.makeDecodeResult();
ASSERT_TRUE(irrecv.decode(&ac._irsend.capture));
EXPECT_EQ(COOLIX48, ac._irsend.capture.decode_type);
EXPECT_EQ(kCoolix48Bits, ac._irsend.capture.bits);
EXPECT_EQ(0xB27DFF0020DF, ac._irsend.capture.value);
EXPECT_EQ(0x0, ac._irsend.capture.address);
EXPECT_EQ(0x0, ac._irsend.capture.command);
}

View File

@ -102,7 +102,7 @@ TEST(TestDecodeElectraAC, RealExampleDecode) {
EXPECT_EQ(
"Power: On, Mode: 1 (Cool), Temp: 24C, Fan: 3 (Low), "
"Swing(V): Off, Swing(H): Off, Light: -, Clean: Off, Turbo: Off, "
"IFeel: Off",
"Quiet: Off, IFeel: Off",
IRAcUtils::resultAcToString(&irsend.capture));
stdAc::state_t r, p;
ASSERT_TRUE(IRAcUtils::decodeToState(&irsend.capture, &r, &p));
@ -237,7 +237,7 @@ TEST(TestIRElectraAcClass, HumanReadable) {
EXPECT_EQ(
"Power: On, Mode: 1 (Cool), Temp: 32C, Fan: 5 (Auto), "
"Swing(V): Off, Swing(H): Off, Light: -, Clean: Off, Turbo: Off, "
"IFeel: Off",
"Quiet: Off, IFeel: Off",
ac.toString());
uint8_t on_cool_16C_auto_voff[13] = {
0xC3, 0x47, 0xE0, 0x00, 0xA0, 0x00, 0x20,
@ -246,7 +246,7 @@ TEST(TestIRElectraAcClass, HumanReadable) {
EXPECT_EQ(
"Power: On, Mode: 1 (Cool), Temp: 16C, Fan: 5 (Auto), "
"Swing(V): Off, Swing(H): Off, Light: -, Clean: Off, Turbo: Off, "
"IFeel: Off",
"Quiet: Off, IFeel: Off",
ac.toString());
uint8_t on_cool_16C_low_voff[13] = {
0xC3, 0x47, 0xE0, 0x00, 0x60, 0x00, 0x20,
@ -255,7 +255,7 @@ TEST(TestIRElectraAcClass, HumanReadable) {
EXPECT_EQ(
"Power: On, Mode: 1 (Cool), Temp: 16C, Fan: 3 (Low), "
"Swing(V): Off, Swing(H): Off, Light: -, Clean: Off, Turbo: Off, "
"IFeel: Off",
"Quiet: Off, IFeel: Off",
ac.toString());
}
@ -280,7 +280,7 @@ TEST(TestIRElectraAcClass, Clean) {
EXPECT_EQ(
"Power: On, Mode: 1 (Cool), Temp: 24C, Fan: 3 (Low), "
"Swing(V): Off, Swing(H): Off, Light: Toggle, Clean: On, Turbo: Off, "
"IFeel: Off",
"Quiet: Off, IFeel: Off",
ac.toString());
}
@ -307,7 +307,7 @@ TEST(TestIRElectraAcClass, Turbo) {
EXPECT_EQ(
"Power: On, Mode: 1 (Cool), Temp: 24C, Fan: 3 (Low), "
"Swing(V): Off, Swing(H): Off, Light: -, Clean: Off, Turbo: On, "
"IFeel: Off",
"Quiet: Off, IFeel: Off",
ac.toString());
}
@ -332,7 +332,7 @@ TEST(TestIRElectraAcClass, LightToggle) {
EXPECT_EQ(
"Power: On, Mode: 1 (Cool), Temp: 24C, Fan: 3 (Low), "
"Swing(V): Off, Swing(H): Off, Light: Toggle, Clean: On, Turbo: Off, "
"IFeel: Off",
"Quiet: Off, IFeel: Off",
ac.toString());
}
@ -360,7 +360,7 @@ TEST(TestIRElectraAcClass, ConstructKnownState) {
EXPECT_EQ(
"Power: On, Mode: 1 (Cool), Temp: 24C, Fan: 3 (Low), "
"Swing(V): Off, Swing(H): Off, Light: Toggle, Clean: On, Turbo: Off, "
"IFeel: Off",
"Quiet: Off, IFeel: Off",
ac.toString());
EXPECT_STATE_EQ(on_cool_24C_fan1_swing_off_turbo_off_clean_on,
ac.getRaw(), kElectraAcBits);
@ -379,7 +379,7 @@ TEST(TestIRElectraAcClass, IFeelAndSensor) {
EXPECT_EQ(
"Power: On, Mode: 1 (Cool), Temp: 21C, Fan: 5 (Auto), "
"Swing(V): Off, Swing(H): Off, Light: -, Clean: Off, Turbo: Off, "
"IFeel: On, Sensor Temp: 26C",
"Quiet: Off, IFeel: On, Sensor Temp: 26C",
ac.toString());
ac.stateReset();
@ -429,6 +429,6 @@ TEST(TestIRElectraAcClass, IFeelAndSensor) {
EXPECT_EQ(
"Power: On, Mode: 4 (Heat), Temp: 27C, Fan: 5 (Auto), "
"Swing(V): Off, Swing(H): Off, Light: -, Clean: Off, Turbo: Off, "
"IFeel: On, Sensor Temp: 28C",
"Quiet: Off, IFeel: On, Sensor Temp: 28C",
ac.toString());
}

View File

@ -0,0 +1,377 @@
// Copyright 2025 GottemHams
#include "ir_Eurom.h"
#include "gtest/gtest.h"
#include "IRac.h"
#include "IRrecv.h"
#include "IRrecv_test.h"
#include "IRsend.h"
#include "IRsend_test.h"
TEST(TestEurom, Housekeeping) {
ASSERT_EQ("EUROM", typeToString(decode_type_t::EUROM));
ASSERT_EQ(decode_type_t::EUROM, strToDecodeType("EUROM"));
ASSERT_TRUE(hasACState(decode_type_t::EUROM));
ASSERT_TRUE(IRac::isProtocolSupported(decode_type_t::EUROM));
ASSERT_EQ(kEuromBits, IRsend::defaultBits(decode_type_t::EUROM));
}
/// Tests for sendEurom().
/// Test sending typical data only.
TEST(TestSendEurom, SendDataOnly) {
IRsendTest irsend(kGpioUnused);
const uint8_t state[kEuromStateLength] = {
0x18, 0x27,
0x71, // Cooling mode, 23 C
0x80, // Power on, swing off
0x00, // No Fahrenheit
0x00, // Sleep disabled, no "on timer"
0x00,
0x80, // No "off timer"
0x00, // "Off timer" disabled
0x80,
0x10, // Low fan
0x21, // Checksum
};
irsend.begin();
irsend.reset();
irsend.sendEurom(state);
EXPECT_EQ(
"f38000d50"
"m3257s3187"
"m454s355m454s355m454s355m454s1162m454s1162m454s355m454s355m454s355m454s355"
"m454s355m454s1162m454s355m454s355m454s1162m454s1162m454s1162m454s355m454"
"s1162m454s1162m454s1162m454s355m454s355m454s355m454s1162m454s1162m454s355"
"m454s355m454s355m454s355m454s355m454s355m454s355m454s355m454s355m454s355"
"m454s355m454s355m454s355m454s355m454s355m454s355m454s355m454s355m454s355"
"m454s355m454s355m454s355m454s355m454s355m454s355m454s355m454s355m454s355"
"m454s355m454s355m454s355m454s1162m454s355m454s355m454s355m454s355m454s355"
"m454s355m454s355m454s355m454s355m454s355m454s355m454s355m454s355m454s355"
"m454s355m454s1162m454s355m454s355m454s355m454s355m454s355m454s355m454s355"
"m454s355m454s355m454s355m454s1162m454s355m454s355m454s355m454s355m454s355"
"m454s355m454s1162m454s355m454s355m454s355m454s355m454s1162m454"
"s50058",
irsend.outputStr());
irsend.reset();
}
/// Tests for decodeEurom().
/// Decode a normal Eurom message.
TEST(TestDecodeEurom, SyntheticExample) {
IRsendTest irsend(kGpioUnused);
IRrecv irrecv(kGpioUnused);
// This is the same state as used in SendDataOnly
const uint8_t state[kEuromStateLength] = {
0x18, 0x27,
0x71, // Cooling mode, 23 C
0x80, // Power on, swing off
0x00, // No Fahrenheit
0x00, // Sleep disabled, no "on timer"
0x00,
0x80, // No "off timer"
0x00, // "Off timer" disabled
0x80,
0x10, // Low fan
0x21, // Checksum
};
irsend.begin();
irsend.reset();
irsend.sendEurom(state);
irsend.makeDecodeResult();
ASSERT_TRUE(irrecv.decode(&irsend.capture));
EXPECT_EQ(decode_type_t::EUROM, irsend.capture.decode_type);
EXPECT_EQ(kEuromBits, irsend.capture.bits);
EXPECT_FALSE(irsend.capture.repeat);
EXPECT_STATE_EQ(state, irsend.capture.state, irsend.capture.bits);
EXPECT_EQ(
"Power: On, Mode: 1 (Cool), Temp: 23C, Fan: 16 (Low), Swing(V): Off"
", Sleep: Off, Off Timer: Off, On Timer: Off",
IRAcUtils::resultAcToString(&irsend.capture));
stdAc::state_t r, p;
ASSERT_TRUE(IRAcUtils::decodeToState(&irsend.capture, &r, &p));
irsend.reset();
}
/// Decode a real example.
TEST(TestDecodeEurom, RealExample) {
IRsendTest irsend(kGpioUnused);
IRrecv irrecv(kGpioUnused);
// UNKNOWN 1C60B17
const uint16_t raw_data[391] = {
3318, 3136,
490, 318, 490, 322, 486, 324, 488, 1126, 490, 1118, 490, 322, 490, 318, 490,
326, 490, 318, 488, 322, 486, 1124, 490, 328, 488, 318, 490, 1124, 490,
1122, 486, 1130, 484, 324, 490, 1122, 486, 1122, 490, 1128, 490, 322, 486,
322, 490, 322, 486, 1132, 484, 1124, 488, 1124, 486, 322, 490, 328, 488,
318, 490, 322, 486, 322, 490, 328, 486, 322, 490, 322, 486, 322, 486, 330,
486, 322, 486, 322, 490, 322, 486, 332, 486, 322, 486, 322, 490, 322, 484,
328, 490, 322, 486, 322, 486, 322, 490, 326, 484, 324, 488, 324, 484, 324,
484, 332, 486, 322, 486, 326, 486, 322, 486, 330, 486, 1122, 490, 324, 486,
322, 484, 332, 486, 322, 486, 328, 484, 322, 486, 332, 484, 322, 490, 324,
484, 322, 486, 332, 486, 326, 486, 322, 486, 322, 484, 336, 486, 1124, 486,
348, 458, 328, 486, 330, 486, 350, 458, 326, 486, 324, 484, 332, 484, 326,
486, 322, 486, 322, 486, 1136, 486, 322, 484, 348, 438, 348, 484, 332, 484,
348, 460, 326, 486, 1122, 486, 336, 486, 344, 464, 1148, 438, 348, 486,
1130, 486,
50010,
3308, 3140,
490, 322, 486, 322, 486, 322, 486, 1132, 490, 1122, 486, 324, 484, 328, 484,
328, 490, 322, 486, 322, 486, 1126, 486, 328, 488, 324, 484, 1124, 490,
1122, 486, 1132, 486, 322, 490, 1122, 486, 1128, 484, 1132, 486, 322, 486,
322, 490, 322, 486, 1132, 486, 1122, 490, 1122, 486, 322, 490, 326, 486,
328, 484, 324, 486, 322, 484, 332, 486, 322, 486, 328, 484, 322, 486, 330,
486, 324, 484, 322, 486, 326, 486, 328, 484, 328, 486, 324, 484, 322, 486,
330, 486, 322, 486, 326, 486, 322, 486, 332, 486, 322, 484, 348, 460, 328,
486, 330, 482, 326, 486, 322, 486, 322, 486, 330, 486, 1128, 484, 324, 484,
322, 486, 332, 484, 348, 460, 328, 484, 324, 484, 358, 460, 326, 486, 322,
486, 348, 460, 336, 480, 328, 484, 348, 460, 328, 480, 336, 486, 1148, 460,
328, 484, 324, 484, 358, 460, 326, 482, 326, 486, 348, 458, 336, 480, 348,
464, 348, 460, 328, 480, 1136, 484, 348, 460, 328, 480, 328, 484, 358, 460,
326, 482, 348, 460, 1152, 460, 356, 460, 354, 458, 1148, 460, 328, 484,
1162, 460,
};
// Note that this is a different state than before
const uint8_t expected_state[kEuromStateLength] = {
0x18, 0x27, 0x71, 0xC0, 0x00, 0x00, 0x00, 0x80, 0x00, 0x80, 0x10, 0x25,
};
irsend.begin();
irsend.reset();
irsend.sendRaw(raw_data, 391, kEuromFreq);
irsend.makeDecodeResult();
ASSERT_TRUE(irrecv.decode(&irsend.capture));
EXPECT_EQ(decode_type_t::EUROM, irsend.capture.decode_type);
EXPECT_EQ(kEuromBits, irsend.capture.bits);
EXPECT_FALSE(irsend.capture.repeat);
EXPECT_STATE_EQ(expected_state, irsend.capture.state, irsend.capture.bits);
EXPECT_EQ(
"Power: On, Mode: 1 (Cool), Temp: 23C, Fan: 16 (Low), Swing(V): On"
", Sleep: Off, Off Timer: Off, On Timer: Off",
IRAcUtils::resultAcToString(&irsend.capture));
stdAc::state_t r, p;
ASSERT_TRUE(IRAcUtils::decodeToState(&irsend.capture, &r, &p));
irsend.reset();
}
/// Decode a real example without repeat.
TEST(TestDecodeEurom, RealExampleNoRepeat) {
IRsendTest irsend(kGpioUnused);
IRrecv irrecv(kGpioUnused);
// UNKNOWN 14601C1D
const uint16_t raw_data[195] = {
3260, 3186,
468, 344, 438, 374, 438, 370, 438, 1178, 438, 1174, 438, 374, 438, 370, 438,
378, 438, 370, 438, 374, 438, 1174, 438, 378, 438, 370, 438, 1174, 438,
1170, 438, 1184, 438, 370, 438, 1174, 438, 370, 438, 1182, 434, 374, 438,
374, 434, 374, 438, 1178, 438, 374, 438, 370, 438, 374, 434, 382, 434, 374,
438, 374, 434, 374, 438, 378, 438, 370, 438, 374, 434, 374, 438, 378, 438,
370, 438, 374, 434, 374, 438, 378, 438, 370, 438, 374, 438, 370, 438, 378,
438, 374, 434, 374, 438, 370, 438, 378, 438, 374, 434, 374, 438, 374, 434,
378, 438, 374, 438, 370, 438, 374, 434, 382, 434, 1174, 438, 374, 438, 370,
438, 378, 438, 370, 438, 374, 438, 370, 438, 382, 434, 374, 438, 370, 438,
374, 438, 378, 438, 374, 434, 374, 438, 370, 438, 382, 434, 1174, 464, 348,
434, 374, 438, 382, 434, 374, 438, 370, 438, 374, 438, 378, 438, 374, 434,
374, 438, 370, 438, 1182, 438, 374, 434, 374, 438, 370, 438, 382, 434, 374,
438, 374, 434, 374, 438, 1182, 434, 374, 438, 1174, 434, 1174, 438, 1182,
434,
};
// This is also another state than all the earlier tests
const uint8_t expected_state[kEuromStateLength] = {
0x18, 0x27, 0x51, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x80, 0x10, 0x17,
};
irsend.begin();
irsend.reset();
irsend.sendRaw(raw_data, 195, kEuromFreq);
irsend.makeDecodeResult();
ASSERT_TRUE(irrecv.decode(&irsend.capture));
EXPECT_EQ(decode_type_t::EUROM, irsend.capture.decode_type);
EXPECT_EQ(kEuromBits, irsend.capture.bits);
EXPECT_FALSE(irsend.capture.repeat);
EXPECT_STATE_EQ(expected_state, irsend.capture.state, irsend.capture.bits);
EXPECT_EQ(
"Power: Off, Mode: 1 (Cool), Temp: 23C, Fan: 16 (Low), Swing(V): Off"
", Sleep: Off, Off Timer: Off, On Timer: Off",
IRAcUtils::resultAcToString(&irsend.capture));
stdAc::state_t r, p;
ASSERT_TRUE(IRAcUtils::decodeToState(&irsend.capture, &r, &p));
irsend.reset();
}
/// Tests for the IREuromAc class.
/// Test power setting and getting.
TEST(TestEuromAc, SetAndGetPower) {
IREuromAc ac(kGpioUnused);
// The initial state is powered off
ASSERT_FALSE(ac.getPower());
ac.setPower(true);
EXPECT_TRUE(ac.getPower());
}
/// Test operation mode setting and getting.
TEST(TestEuromAc, SetAndGetMode) {
IREuromAc ac(kGpioUnused);
// The initial state is cooling mode
ASSERT_EQ(kEuromCool, ac.getMode());
// Temperature is not used in dehumidification/ventilation modes
ac.setMode(kEuromDehumidify);
EXPECT_EQ(kEuromDehumidify, ac.getMode());
ac.setMode(kEuromVentilate);
EXPECT_EQ(kEuromVentilate, ac.getMode());
ac.setMode(kEuromHeat);
EXPECT_EQ(kEuromHeat, ac.getMode());
}
/// Test temperature setting and getting.
TEST(TestEuromAc, SetAndGetTemperature) {
IREuromAc ac(kGpioUnused);
// The initial state is 23 C
ASSERT_FALSE(ac.getTempIsFahrenheit());
ASSERT_EQ(23, ac.getTemp());
ac.setTemp(22);
ASSERT_FALSE(ac.getTempIsFahrenheit());
EXPECT_EQ(22, ac.getTemp());
}
/// Test temperature setting and getting with Fahrenheit.
TEST(TestEuromAc, SetAndGetTemperatureFahrenheit) {
IREuromAc ac(kGpioUnused);
// The initial state is not using Fahrenheit
ASSERT_FALSE(ac.getTempIsFahrenheit());
// This corresponds to 16 C
ac.setTemp(70, true);
ASSERT_TRUE(ac.getTempIsFahrenheit());
EXPECT_EQ(70, ac.getTemp());
}
/// Test fan speed setting and getting.
TEST(TestEuromAc, SetAndGetFan) {
IREuromAc ac(kGpioUnused);
// The initial state is low fan
ASSERT_EQ(kEuromFanLow, ac.getFan());
ac.setFan(kEuromFanHigh);
EXPECT_EQ(kEuromFanHigh, ac.getFan());
}
/// Test swing setting and getting.
TEST(TestEuromAc, SetAndGetSwing) {
IREuromAc ac(kGpioUnused);
// The initial state is swing disabled
ASSERT_FALSE(ac.getSwing());
ac.setSwing(true);
EXPECT_TRUE(ac.getSwing());
}
/// Test sleep mode setting and getting.
TEST(TestEuromAc, SetAndGetSleep) {
IREuromAc ac(kGpioUnused);
// The initial state is sleep disabled
ASSERT_FALSE(ac.getSleep());
ac.setSleep(true);
EXPECT_TRUE(ac.getSleep());
}
/// Test "off timer" setting and getting.
TEST(TestEuromAc, SetAndGetOffTimer) {
IREuromAc ac(kGpioUnused);
// The initial state is no timer
ASSERT_EQ(kEuromTimerMin, ac.getOffTimer());
ac.setOffTimer(kEuromTimerMax);
EXPECT_EQ(kEuromTimerMax, ac.getOffTimer());
}
/// Test "on timer" setting and getting.
TEST(TestEuromAc, SetAndGetOnTimer) {
IREuromAc ac(kGpioUnused);
// The initial state is no timer
ASSERT_EQ(kEuromTimerMin, ac.getOnTimer());
ac.setOnTimer(kEuromTimerMax);
EXPECT_EQ(kEuromTimerMax, ac.getOnTimer());
}
/// Test checksumming for the initial state.
TEST(TestEuromAc, ChecksumInitial) {
IREuromAc ac(kGpioUnused);
// The initial state is powered off, cooling mode, 23 C, low fan, swing and
// sleep disabled, no timers
const uint8_t *raw_state = ac.getRaw();
ASSERT_EQ(0x19, raw_state[kEuromStateLength - 1]);
}
/// Test checksumming with every "feature" set to the "highest" value, which
/// also includes the special case of using the max temperature.
TEST(TestEuromAc, ChecksumHigh) {
IREuromAc ac(kGpioUnused);
// The initial state is powered off, cooling mode, 23 C, low fan, swing and
// sleep disabled, no timers
ac.setPower(true);
ac.setMode(kEuromHeat);
ac.setTemp(kEuromMaxTempF, true);
ac.setFan(kEuromFanHigh);
ac.setSwing(true);
ac.setSleep(true);
ac.setOffTimer(kEuromTimerMax);
ac.setOnTimer(kEuromTimerMax);
const uint8_t expected_state[kEuromStateLength] = {
0x18, 0x27,
0x0C, // Heating mode, 32 C (changing Fahrenheit updates this too)
0xC0, // Power on, swing on
0x5E, // 90 F
0x64, // Sleep enabled, "on timer" set to 24 hours
0x00,
0xA4, // "Off timer" set to 24 hours
0x80, // "Off timer" enabled
0x80,
0x40, // High fan
0x57, // Checksum
};
EXPECT_STATE_EQ(expected_state, ac.getRaw(), kEuromBits);
}

View File

@ -1,5 +1,7 @@
// Copyright 2022 Mateusz Bronk (mbronk)
#include <tuple>
#include <vector>
#include "IRac.h"
#include "IRsend.h"
#include "IRsend_test.h"

View File

@ -202,7 +202,7 @@ TEST(TestSendGree, CompareUint64ToCharResults) {
irsend_chars.reset();
irsend_uint64.reset();
irsend_chars.sendGree(gree_zero_code);
irsend_uint64.sendGree((uint64_t)0x0);
irsend_uint64.sendGree(static_cast<uint64_t>(0x0));
ASSERT_EQ(irsend_chars.outputStr(), irsend_uint64.outputStr());
}
@ -701,6 +701,11 @@ TEST(TestGreeClass, toCommon) {
ASSERT_FALSE(ac.toCommon().filter);
ASSERT_FALSE(ac.toCommon().beep);
ASSERT_EQ(-1, ac.toCommon().clock);
// Test kGreeSwingLastPos following the pattern in IRac::gree().
ASSERT_EQ(kGreeSwingLastPos, ac.convertSwingV(stdAc::swingv_t::kOff));
ac.setSwingVertical(false, kGreeSwingLastPos);
ASSERT_EQ(stdAc::swingv_t::kOff, ac.toCommon().swingv);
}
TEST(TestGreeClass, Issue814Power) {

View File

@ -311,22 +311,25 @@ TEST(TestToshibaACClass, HumanReadableOutput) {
0x00, 0xC1, 0x00, 0xC0};
ac.setRaw(initial_state);
EXPECT_EQ("Temp: 17C, Power: On, Mode: 0 (Auto), Fan: 0 (Auto), "
"Turbo: Off, Econo: Off, Filter: Off",
EXPECT_EQ("Model: 0 (TOSHIBA REMOTE A), Temp: 17C, Power: On, "
"Mode: 0 (Auto), Fan: 0 (Auto), 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, Filter: Off",
EXPECT_EQ("Model: 0 (TOSHIBA REMOTE A), Temp: 17C, Power: On, "
"Mode: 1 (Cool), Fan: 5 (High), 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, Filter: Off",
EXPECT_EQ("Model: 0 (TOSHIBA REMOTE A), Temp: 25C, Power: On, "
"Mode: 2 (Dry), Fan: 3 (Medium), Turbo: Off, Econo: Off, "
"Filter: Off",
ac.toString());
ac.off();
EXPECT_EQ("Temp: 25C, Power: Off, Fan: 3 (Medium), Turbo: Off, Econo: Off, "
"Filter: Off",
EXPECT_EQ("Model: 0 (TOSHIBA REMOTE A), Temp: 25C, Power: Off, "
"Fan: 3 (Medium), Turbo: Off, Econo: Off, Filter: Off",
ac.toString());
}
@ -379,8 +382,8 @@ TEST(TestDecodeToshibaAC, SyntheticExample) {
ASSERT_EQ(kToshibaACBits, irsend.capture.bits);
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, Filter: Off",
"Model: 0 (TOSHIBA REMOTE A), Temp: 17C, Power: On, Mode: 0 (Auto), "
"Fan: 0 (Auto), Turbo: Off, Econo: Off, Filter: Off",
IRAcUtils::resultAcToString(&irsend.capture));
stdAc::state_t r, p;
ASSERT_TRUE(IRAcUtils::decodeToState(&irsend.capture, &r, &p));
@ -627,8 +630,8 @@ TEST(TestDecodeToshibaAC, RealLongExample) {
EXPECT_EQ(kToshibaACBitsLong, irsend.capture.bits);
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, Filter: Off",
"Model: 0 (TOSHIBA REMOTE A), Temp: 22C, Power: On, Mode: 0 (Auto), "
"Fan: 0 (Auto), Turbo: On, Econo: Off, Filter: Off",
IRAcUtils::resultAcToString(&irsend.capture));
}
@ -718,7 +721,7 @@ TEST(TestDecodeToshibaAC, RealShortExample) {
EXPECT_EQ(kToshibaACBitsShort, irsend.capture.bits);
EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits);
EXPECT_EQ(
"Temp: 17C, Swing(V): 0 (Step)",
"Model: 0 (TOSHIBA REMOTE A), Temp: 17C, Swing(V): 0 (Step)",
IRAcUtils::resultAcToString(&irsend.capture));
}
@ -732,8 +735,8 @@ TEST(TestToshibaACClass, ConstructLongState) {
ac.setTurbo(false);
ac.setEcono(true);
EXPECT_EQ(
"Temp: 29C, Power: On, Mode: 2 (Dry), Fan: 2 (UNKNOWN), "
"Turbo: Off, Econo: On, Filter: Off",
"Model: 0 (TOSHIBA REMOTE A), Temp: 29C, Power: On, Mode: 2 (Dry), "
"Fan: 2 (UNKNOWN), Turbo: Off, Econo: On, Filter: Off",
ac.toString());
EXPECT_EQ(kToshibaACStateLengthLong, ac.getStateLength());
const uint8_t expectedState[kToshibaACStateLengthLong] = {
@ -783,8 +786,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, "
"Filter: Off",
"Model: 0 (TOSHIBA REMOTE A), Temp: 20C, Power: Off, Fan: 0 (Auto), "
"Turbo: Off, Econo: Off, Filter: Off",
IRAcUtils::resultAcToString(&irsend.capture));
}
@ -805,7 +808,7 @@ TEST(TestToshibaACClass, SwingCodes) {
ac.setSwing(kToshibaAcSwingOn);
EXPECT_EQ(
"Temp: 17C, Swing(V): 1 (On)",
"Model: 0 (TOSHIBA REMOTE A), Temp: 17C, Swing(V): 1 (On)",
ac.toString());
EXPECT_EQ(kToshibaACStateLengthShort, ac.getStateLength());
const uint8_t swingOnState[kToshibaACStateLengthShort] = {
@ -815,7 +818,7 @@ TEST(TestToshibaACClass, SwingCodes) {
ac.setSwing(kToshibaAcSwingOff);
EXPECT_EQ(
"Temp: 17C, Swing(V): 2 (Off)",
"Model: 0 (TOSHIBA REMOTE A), Temp: 17C, Swing(V): 2 (Off)",
ac.toString());
EXPECT_EQ(kToshibaACStateLengthShort, ac.getStateLength());
const uint8_t swingOffState[kToshibaACStateLengthShort] = {
@ -828,7 +831,7 @@ TEST(TestToshibaACClass, SwingCodes) {
ac.setRaw(swingToggleState, kToshibaACStateLengthShort);
EXPECT_EQ(kToshibaAcSwingToggle, ac.getSwing());
EXPECT_EQ(
"Temp: 17C, Swing(V): 4 (Toggle)",
"Model: 0 (TOSHIBA REMOTE A), Temp: 17C, Swing(V): 4 (Toggle)",
ac.toString());
}

View File

@ -105,7 +105,7 @@ class RawIRMessage():
code.append(f" // Data Section #{self.section_count}")
code.append(f" // e.g. data = 0x{int(bin_str, 2):X}, nbits = {nbits}")
code.append(f" sendData(k{name}BitMark, k{name}OneSpace, k{name}BitMark,"
f" k{name}ZeroSpace, send_data, {nbits}, true);")
f" k{name}ZeroSpace, send_data, {nbits}, k{name}MsbFirst);")
code.append(f" send_data >>= {nbits};")
if footer:
code.append(" // Footer")
@ -122,7 +122,8 @@ class RawIRMessage():
f" // e.g. data_result.data = 0x{int(bin_str, 2):X}, nbits = {nbits}",
f" data_result = matchData(&(results->rawbuf[offset]), {nbits},",
f" k{name}BitMark, k{name}OneSpace,",
f" k{name}BitMark, k{name}ZeroSpace);",
f" k{name}BitMark, k{name}ZeroSpace,",
f" kUseDefTol, kMarkExcess, k{name}MsbFirst);",
" offset += data_result.used;",
" if (data_result.success == false) return false; // Fail",
f" data <<= {nbits}; // Make room for the new bits of data.",
@ -163,7 +164,8 @@ class RawIRMessage():
f" k{name}BitMark, k{name}ZeroSpace,",
f" {lastmark}, {lastspace},",
f" data + pos, {int(nbytes)}, // Bytes",
f" k{name}Freq, true, kNoRepeat, kDutyDefault);",
f" k{name}Freq, k{name}MsbFirst, kNoRepeat,"
" kDutyDefault);",
f" pos += {int(nbytes)};"
f" // Adjust by how many bytes of data we sent"])
return code
@ -198,7 +200,7 @@ class RawIRMessage():
f" {firstmark}, {firstspace},",
f" k{name}BitMark, k{name}OneSpace,",
f" k{name}BitMark, k{name}ZeroSpace,",
f" {lastmark}, {lastspace}, true);",
f" {lastmark}, {lastspace}, k{name}MsbFirst);",
" if (used == 0) return false; // We failed to find any data.",
" offset += used; // Adjust for how much of the message we read.",
f" pos += {int(nbytes)};"
@ -351,6 +353,7 @@ def dump_constants(message, defines, name="", output=sys.stdout):
defines.append(f"const uint16_t k{name}SpaceGap{count} = {gap};")
defines.append(f"const uint16_t k{name}Freq = 38000; "
"// Hz. (Guessing the most common frequency.)")
defines.append(f"const bool k{name}MsbFirst = true; // default assumption")
def parse_and_report(rawdata_str, margin, gen_code=False, name="",

View File

@ -76,7 +76,8 @@ class TestAutoAnalyseRawData(unittest.TestCase):
'const uint16_t kBAROneSpace = 1485;',
'const uint16_t kBARZeroSpace = 520;',
'const uint16_t kBARFreq = 38000; // Hz. (Guessing the most common '
'frequency.)'
'frequency.)',
'const bool kBARMsbFirst = true; // default assumption'
])
self.assertEqual(
output.getvalue(), 'Guessing key value:\n'
@ -113,7 +114,8 @@ class TestAutoAnalyseRawData(unittest.TestCase):
'const uint16_t kTESTZeroSpace = 554;',
'const uint16_t kTESTSpaceGap = 19990;',
'const uint16_t kTESTFreq = 38000; // Hz. (Guessing the most common '
'frequency.)'
'frequency.)',
'const bool kTESTMsbFirst = true; // default assumption'
])
self.assertEqual(
output.getvalue(), 'Guessing key value:\n'
@ -302,6 +304,7 @@ class TestAutoAnalyseRawData(unittest.TestCase):
'const uint16_t kFOOZeroSpace = 520;\n'
'const uint16_t kFOOFreq = 38000; // Hz. (Guessing the most common'
' frequency.)\n'
'const bool kFOOMsbFirst = true; // default assumption\n'
'const uint16_t kFOOBits = 16; // Move to IRremoteESP8266.h\n'
'const uint16_t kFOOOverhead = 5;\n'
'\n'
@ -323,7 +326,7 @@ class TestAutoAnalyseRawData(unittest.TestCase):
' // Data Section #1\n'
' // e.g. data = 0xEB, nbits = 8\n'
' sendData(kFOOBitMark, kFOOOneSpace, kFOOBitMark, kFOOZeroSpace,'
' send_data, 8, true);\n'
' send_data, 8, kFOOMsbFirst);\n'
' send_data >>= 8;\n'
' // Footer\n'
' mark(kFOOBitMark);\n'
@ -331,7 +334,7 @@ class TestAutoAnalyseRawData(unittest.TestCase):
' // Data Section #2\n'
' // e.g. data = 0x1, nbits = 8\n'
' sendData(kFOOBitMark, kFOOOneSpace, kFOOBitMark, kFOOZeroSpace,'
' send_data, 8, true);\n'
' send_data, 8, kFOOMsbFirst);\n'
' send_data >>= 8;\n'
' // Footer\n'
' mark(kFOOBitMark);\n'
@ -374,7 +377,8 @@ class TestAutoAnalyseRawData(unittest.TestCase):
' // e.g. data_result.data = 0xEB, nbits = 8\n'
' data_result = matchData(&(results->rawbuf[offset]), 8,\n'
' kFOOBitMark, kFOOOneSpace,\n'
' kFOOBitMark, kFOOZeroSpace);\n'
' kFOOBitMark, kFOOZeroSpace,\n'
' kUseDefTol, kMarkExcess, kFOOMsbFirst);\n'
' offset += data_result.used;\n'
' if (data_result.success == false) return false; // Fail\n'
' data <<= 8; // Make room for the new bits of data.\n'
@ -390,7 +394,8 @@ class TestAutoAnalyseRawData(unittest.TestCase):
' // e.g. data_result.data = 0x1, nbits = 8\n'
' data_result = matchData(&(results->rawbuf[offset]), 8,\n'
' kFOOBitMark, kFOOOneSpace,\n'
' kFOOBitMark, kFOOZeroSpace);\n'
' kFOOBitMark, kFOOZeroSpace,\n'
' kUseDefTol, kMarkExcess, kFOOMsbFirst);\n'
' offset += data_result.used;\n'
' if (data_result.success == false) return false; // Fail\n'
' data <<= 8; // Make room for the new bits of data.\n'
@ -585,6 +590,7 @@ class TestAutoAnalyseRawData(unittest.TestCase):
'const uint16_t kHitachiSpaceGap = 49290;\n'
'const uint16_t kHitachiFreq = 38000; // Hz. (Guessing the most'
' common frequency.)\n'
'const bool kHitachiMsbFirst = true; // default assumption\n'
'const uint16_t kHitachiBits = 424; // Move to IRremoteESP8266.h\n'
'const uint16_t kHitachiStateLength = 53; // Move to IRremoteESP8266.h'
'\n'
@ -616,7 +622,7 @@ class TestAutoAnalyseRawData(unittest.TestCase):
'FFCA358F7000FF00FF01FEC03F807F11EE00FF00FFFF00FF00FF00FF00,'
' nbits = 424\n'
' sendData(kHitachiBitMark, kHitachiOneSpace, kHitachiBitMark,'
' kHitachiZeroSpace, send_data, 424, true);\n'
' kHitachiZeroSpace, send_data, 424, kHitachiMsbFirst);\n'
' send_data >>= 424;\n'
' // Footer\n'
' mark(kHitachiBitMark);\n'
@ -663,7 +669,8 @@ class TestAutoAnalyseRawData(unittest.TestCase):
' kHitachiBitMark, kHitachiZeroSpace,\n'
' kHitachiBitMark, kHitachiSpaceGap,\n'
' data + pos, 53, // Bytes\n'
' kHitachiFreq, true, kNoRepeat, kDutyDefault);\n'
' kHitachiFreq, kHitachiMsbFirst, kNoRepeat,'
' kDutyDefault);\n'
' pos += 53; // Adjust by how many bytes of data we sent\n'
' }\n'
'}\n'
@ -714,7 +721,9 @@ class TestAutoAnalyseRawData(unittest.TestCase):
' nbits = 424\n'
' data_result = matchData(&(results->rawbuf[offset]), 424,\n'
' kHitachiBitMark, kHitachiOneSpace,\n'
' kHitachiBitMark, kHitachiZeroSpace);\n'
' kHitachiBitMark, kHitachiZeroSpace,\n'
' kUseDefTol, kMarkExcess,'
' kHitachiMsbFirst);\n'
' offset += data_result.used;\n'
' if (data_result.success == false) return false; // Fail\n'
' data <<= 424; // Make room for the new bits of data.\n'
@ -772,7 +781,8 @@ class TestAutoAnalyseRawData(unittest.TestCase):
' kHitachiHdrMark, kHitachiHdrSpace,\n'
' kHitachiBitMark, kHitachiOneSpace,\n'
' kHitachiBitMark, kHitachiZeroSpace,\n'
' kHitachiBitMark, kHitachiSpaceGap, true);\n'
' kHitachiBitMark, kHitachiSpaceGap,'
' kHitachiMsbFirst);\n'
' if (used == 0) return false; // We failed to find any data.\n'
' offset += used; // Adjust for how much of the message we read.\n'
' pos += 53; // Adjust by how many bytes of data we read\n'
@ -898,6 +908,7 @@ class TestAutoAnalyseRawData(unittest.TestCase):
'const uint16_t kFOOSpaceGap = 13996;\n'
'const uint16_t kFOOFreq = 38000; // Hz. (Guessing the most common'
' frequency.)\n'
'const bool kFOOMsbFirst = true; // default assumption\n'
'const uint16_t kFOOBits = 128; // Move to IRremoteESP8266.h\n'
'const uint16_t kFOOStateLength = 16; // Move to IRremoteESP8266.h\n'
'const uint16_t kFOOOverhead = 16;\n'
@ -922,7 +933,7 @@ class TestAutoAnalyseRawData(unittest.TestCase):
' // Data Section #1\n'
' // e.g. data = 0x5F5F4040, nbits = 32\n'
' sendData(kFOOBitMark, kFOOOneSpace, kFOOBitMark, kFOOZeroSpace,'
' send_data, 32, true);\n'
' send_data, 32, kFOOMsbFirst);\n'
' send_data >>= 32;\n'
' // Header\n'
' mark(kFOOHdrMark);\n'
@ -930,7 +941,7 @@ class TestAutoAnalyseRawData(unittest.TestCase):
' // Data Section #2\n'
' // e.g. data = 0x5F5F4040, nbits = 32\n'
' sendData(kFOOBitMark, kFOOOneSpace, kFOOBitMark, kFOOZeroSpace,'
' send_data, 32, true);\n'
' send_data, 32, kFOOMsbFirst);\n'
' send_data >>= 32;\n'
' // Header\n'
' mark(kFOOHdrMark);\n'
@ -944,7 +955,7 @@ class TestAutoAnalyseRawData(unittest.TestCase):
' // Data Section #3\n'
' // e.g. data = 0x2F2F6C6C, nbits = 32\n'
' sendData(kFOOBitMark, kFOOOneSpace, kFOOBitMark, kFOOZeroSpace,'
' send_data, 32, true);\n'
' send_data, 32, kFOOMsbFirst);\n'
' send_data >>= 32;\n'
' // Header\n'
' mark(kFOOHdrMark);\n'
@ -952,7 +963,7 @@ class TestAutoAnalyseRawData(unittest.TestCase):
' // Data Section #4\n'
' // e.g. data = 0x2F2F6C6C, nbits = 32\n'
' sendData(kFOOBitMark, kFOOOneSpace, kFOOBitMark, kFOOZeroSpace,'
' send_data, 32, true);\n'
' send_data, 32, kFOOMsbFirst);\n'
' send_data >>= 32;\n'
' // Header\n'
' mark(kFOOHdrMark);\n'
@ -994,7 +1005,7 @@ class TestAutoAnalyseRawData(unittest.TestCase):
' kFOOBitMark, kFOOZeroSpace,\n'
' kFOOHdrMark, kFOOHdrSpace,\n'
' data + pos, 4, // Bytes\n'
' kFOOFreq, true, kNoRepeat, kDutyDefault);\n'
' kFOOFreq, kFOOMsbFirst, kNoRepeat, kDutyDefault);\n'
' pos += 4; // Adjust by how many bytes of data we sent\n'
' // Data Section #2\n'
' // e.g.\n'
@ -1005,7 +1016,7 @@ class TestAutoAnalyseRawData(unittest.TestCase):
' kFOOBitMark, kFOOZeroSpace,\n'
' kFOOHdrMark, kFOOHdrSpace,\n'
' data + pos, 4, // Bytes\n'
' kFOOFreq, true, kNoRepeat, kDutyDefault);\n'
' kFOOFreq, kFOOMsbFirst, kNoRepeat, kDutyDefault);\n'
' pos += 4; // Adjust by how many bytes of data we sent\n'
' // Data Section #3\n'
' // e.g.\n'
@ -1016,7 +1027,7 @@ class TestAutoAnalyseRawData(unittest.TestCase):
' kFOOBitMark, kFOOZeroSpace,\n'
' kFOOHdrMark, kFOOHdrSpace,\n'
' data + pos, 4, // Bytes\n'
' kFOOFreq, true, kNoRepeat, kDutyDefault);\n'
' kFOOFreq, kFOOMsbFirst, kNoRepeat, kDutyDefault);\n'
' pos += 4; // Adjust by how many bytes of data we sent\n'
' // Data Section #4\n'
' // e.g.\n'
@ -1027,7 +1038,7 @@ class TestAutoAnalyseRawData(unittest.TestCase):
' kFOOBitMark, kFOOZeroSpace,\n'
' kFOOHdrMark, kFOOHdrSpace,\n'
' data + pos, 4, // Bytes\n'
' kFOOFreq, true, kNoRepeat, kDutyDefault);\n'
' kFOOFreq, kFOOMsbFirst, kNoRepeat, kDutyDefault);\n'
' pos += 4; // Adjust by how many bytes of data we sent\n'
' }\n'
'}\n'
@ -1068,7 +1079,8 @@ class TestAutoAnalyseRawData(unittest.TestCase):
' // e.g. data_result.data = 0x5F5F4040, nbits = 32\n'
' data_result = matchData(&(results->rawbuf[offset]), 32,\n'
' kFOOBitMark, kFOOOneSpace,\n'
' kFOOBitMark, kFOOZeroSpace);\n'
' kFOOBitMark, kFOOZeroSpace,\n'
' kUseDefTol, kMarkExcess, kFOOMsbFirst);\n'
' offset += data_result.used;\n'
' if (data_result.success == false) return false; // Fail\n'
' data <<= 32; // Make room for the new bits of data.\n'
@ -1084,7 +1096,8 @@ class TestAutoAnalyseRawData(unittest.TestCase):
' // e.g. data_result.data = 0x5F5F4040, nbits = 32\n'
' data_result = matchData(&(results->rawbuf[offset]), 32,\n'
' kFOOBitMark, kFOOOneSpace,\n'
' kFOOBitMark, kFOOZeroSpace);\n'
' kFOOBitMark, kFOOZeroSpace,\n'
' kUseDefTol, kMarkExcess, kFOOMsbFirst);\n'
' offset += data_result.used;\n'
' if (data_result.success == false) return false; // Fail\n'
' data <<= 32; // Make room for the new bits of data.\n'
@ -1112,7 +1125,8 @@ class TestAutoAnalyseRawData(unittest.TestCase):
' // e.g. data_result.data = 0x2F2F6C6C, nbits = 32\n'
' data_result = matchData(&(results->rawbuf[offset]), 32,\n'
' kFOOBitMark, kFOOOneSpace,\n'
' kFOOBitMark, kFOOZeroSpace);\n'
' kFOOBitMark, kFOOZeroSpace,\n'
' kUseDefTol, kMarkExcess, kFOOMsbFirst);\n'
' offset += data_result.used;\n'
' if (data_result.success == false) return false; // Fail\n'
' data <<= 32; // Make room for the new bits of data.\n'
@ -1128,7 +1142,8 @@ class TestAutoAnalyseRawData(unittest.TestCase):
' // e.g. data_result.data = 0x2F2F6C6C, nbits = 32\n'
' data_result = matchData(&(results->rawbuf[offset]), 32,\n'
' kFOOBitMark, kFOOOneSpace,\n'
' kFOOBitMark, kFOOZeroSpace);\n'
' kFOOBitMark, kFOOZeroSpace,\n'
' kUseDefTol, kMarkExcess, kFOOMsbFirst);\n'
' offset += data_result.used;\n'
' if (data_result.success == false) return false; // Fail\n'
' data <<= 32; // Make room for the new bits of data.\n'
@ -1189,7 +1204,7 @@ class TestAutoAnalyseRawData(unittest.TestCase):
' kFOOHdrMark, kFOOHdrSpace,\n'
' kFOOBitMark, kFOOOneSpace,\n'
' kFOOBitMark, kFOOZeroSpace,\n'
' kFOOHdrMark, kFOOHdrSpace, true);\n'
' kFOOHdrMark, kFOOHdrSpace, kFOOMsbFirst);\n'
' if (used == 0) return false; // We failed to find any data.\n'
' offset += used; // Adjust for how much of the message we read.\n'
' pos += 4; // Adjust by how many bytes of data we read\n'
@ -1204,7 +1219,7 @@ class TestAutoAnalyseRawData(unittest.TestCase):
' kFOOHdrMark, kFOOHdrSpace,\n'
' kFOOBitMark, kFOOOneSpace,\n'
' kFOOBitMark, kFOOZeroSpace,\n'
' kFOOHdrMark, kFOOHdrSpace, true);\n'
' kFOOHdrMark, kFOOHdrSpace, kFOOMsbFirst);\n'
' if (used == 0) return false; // We failed to find any data.\n'
' offset += used; // Adjust for how much of the message we read.\n'
' pos += 4; // Adjust by how many bytes of data we read\n'
@ -1219,7 +1234,7 @@ class TestAutoAnalyseRawData(unittest.TestCase):
' kFOOHdrMark, kFOOHdrSpace,\n'
' kFOOBitMark, kFOOOneSpace,\n'
' kFOOBitMark, kFOOZeroSpace,\n'
' kFOOHdrMark, kFOOHdrSpace, true);\n'
' kFOOHdrMark, kFOOHdrSpace, kFOOMsbFirst);\n'
' if (used == 0) return false; // We failed to find any data.\n'
' offset += used; // Adjust for how much of the message we read.\n'
' pos += 4; // Adjust by how many bytes of data we read\n'
@ -1234,7 +1249,7 @@ class TestAutoAnalyseRawData(unittest.TestCase):
' kFOOHdrMark, kFOOHdrSpace,\n'
' kFOOBitMark, kFOOOneSpace,\n'
' kFOOBitMark, kFOOZeroSpace,\n'
' kFOOHdrMark, kFOOHdrSpace, true);\n'
' kFOOHdrMark, kFOOHdrSpace, kFOOMsbFirst);\n'
' if (used == 0) return false; // We failed to find any data.\n'
' offset += used; // Adjust for how much of the message we read.\n'
' pos += 4; // Adjust by how many bytes of data we read\n'
@ -1333,6 +1348,7 @@ class TestAutoAnalyseRawData(unittest.TestCase):
'const uint16_t kZeroSpace = 547;\n'
'const uint16_t kFreq = 38000; // Hz. (Guessing the most common'
' frequency.)\n'
'const bool kMsbFirst = true; // default assumption\n'
'const uint16_t kBits = 128; // Move to IRremoteESP8266.h\n'
'const uint16_t kStateLength = 16; // Move to IRremoteESP8266.h\n'
'const uint16_t kOverhead = 1;\n'
@ -1354,7 +1370,7 @@ class TestAutoAnalyseRawData(unittest.TestCase):
' // Data Section #1\n'
' // e.g. data = 0xA55A0000400000000000000000000080, nbits = 128\n'
' sendData(kBitMark, kOneSpace, kBitMark, kZeroSpace, send_data,'
' 128, true);\n'
' 128, kMsbFirst);\n'
' send_data >>= 128;\n'
' // Footer\n'
' mark(kBitMark);\n'
@ -1393,7 +1409,7 @@ class TestAutoAnalyseRawData(unittest.TestCase):
' kBitMark, kZeroSpace,\n'
' kBitMark, kDefaultMessageGap,\n'
' data + pos, 16, // Bytes\n'
' kFreq, true, kNoRepeat, kDutyDefault);\n'
' kFreq, kMsbFirst, kNoRepeat, kDutyDefault);\n'
' pos += 16; // Adjust by how many bytes of data we sent\n'
' }\n'
'}\n'
@ -1429,7 +1445,8 @@ class TestAutoAnalyseRawData(unittest.TestCase):
' = 128\n'
' data_result = matchData(&(results->rawbuf[offset]), 128,\n'
' kBitMark, kOneSpace,\n'
' kBitMark, kZeroSpace);\n'
' kBitMark, kZeroSpace,\n'
' kUseDefTol, kMarkExcess, kMsbFirst);\n'
' offset += data_result.used;\n'
' if (data_result.success == false) return false; // Fail\n'
' data <<= 128; // Make room for the new bits of data.\n'
@ -1483,7 +1500,7 @@ class TestAutoAnalyseRawData(unittest.TestCase):
' 0, 0,\n'
' kBitMark, kOneSpace,\n'
' kBitMark, kZeroSpace,\n'
' kBitMark, kDefaultMessageGap, true);\n'
' kBitMark, kDefaultMessageGap, kMsbFirst);\n'
' if (used == 0) return false; // We failed to find any data.\n'
' offset += used; // Adjust for how much of the message we read.\n'
' pos += 16; // Adjust by how many bytes of data we read\n'

View File

@ -5,6 +5,7 @@
#include <inttypes.h>
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <string>
#include "IRac.h"
#include "IRsend.h"

View File

@ -6,6 +6,8 @@
#include <inttypes.h>
#include <stdio.h>
#include <string.h>
#include <cstdio>
#include <iostream>
#include <string>
#include "IRac.h"
#include "IRsend.h"
@ -21,7 +23,7 @@ void str_to_uint16(char *str, uint16_t *res, uint8_t base) {
if (errno == ERANGE || val < 0 || val > UINT16_MAX || end == str ||
*end != '\0')
return;
*res = (uint16_t)val;
*res = static_cast<uint16_t>(val);
}
void usage_error(char *name) {

View File

@ -21,6 +21,7 @@ space 500000
#include <inttypes.h>
#include <stdio.h>
#include <string.h>
#include <cstdio>
#include <iostream>
#include <sstream>
#include <string>
@ -37,7 +38,7 @@ void str_to_uint16(char *str, uint16_t *res, uint8_t base) {
if (errno == ERANGE || val < 0 || val > UINT16_MAX || end == str ||
*end != '\0')
return;
*res = (uint16_t)val;
*res = static_cast<uint16_t>(val);
}
void usage_error(char *name) {

View File

@ -8,7 +8,7 @@ from auto_analyse_raw_data import convert_rawdata, add_rawdata_args, get_rawdata
# pylint: disable=too-many-arguments
def parse_and_report(rawdata_str, hertz=38000, end_usecs=100000,
def parse_and_report(rawdata_str, hertz=38000, *, end_usecs=100000,
use_initial=False, generate_code=False, verbose=False,
output=sys.stdout):
"""Analyse the rawdata c++ definition of a IR message."""
@ -94,9 +94,11 @@ def main():
default=False)
add_rawdata_args(arg_parser)
arg_options = arg_parser.parse_args()
parse_and_report(get_rawdata(arg_options), arg_options.hertz,
arg_options.usecs, arg_options.use_initial,
arg_options.generate_code, arg_options.verbose)
parse_and_report(get_rawdata(arg_options), hertz=arg_options.hertz,
end_usecs=arg_options.usecs,
use_initial=arg_options.use_initial,
generate_code=arg_options.generate_code,
verbose=arg_options.verbose)
if __name__ == '__main__':

View File

@ -14,8 +14,9 @@ class TestRawToPronto(unittest.TestCase):
input_str = """
uint16_t rawData[7] = {
20100, 20472, 15092, 30704, 20102, 20472, 15086};"""
pronto.parse_and_report(input_str, 38000, 100000, True, False, False,
output)
pronto.parse_and_report(input_str, hertz=38000, end_usecs=100000,
use_initial=True, generate_code=False,
verbose=False, output=output)
self.assertEqual(
output.getvalue(),
"Pronto code = "
@ -28,8 +29,9 @@ class TestRawToPronto(unittest.TestCase):
input_str = """
uint16_t rawData[7] = {
20100, 20472, 15092, 30704, 20102, 20472, 15086};"""
pronto.parse_and_report(input_str, 36000, 100000, True, False, False,
output)
pronto.parse_and_report(input_str, hertz=36000, end_usecs=100000,
use_initial=True, generate_code=False,
verbose=False, output=output)
self.assertEqual(
output.getvalue(),
"Pronto code = "
@ -42,8 +44,9 @@ class TestRawToPronto(unittest.TestCase):
input_str = """
uint16_t rawData[7] = {
20100, 20472, 15092, 30704, 20102, 20472, 15086};"""
pronto.parse_and_report(input_str, 57600, 100000, True, False, False,
output)
pronto.parse_and_report(input_str, hertz=57600, end_usecs=100000,
use_initial=True, generate_code=False,
verbose=False, output=output)
self.assertEqual(
output.getvalue(),
"Pronto code = "
@ -56,8 +59,9 @@ class TestRawToPronto(unittest.TestCase):
input_str = """
uint16_t rawData[7] = {
20100, 20472, 15092, 30704, 20102, 20472, 15086};"""
pronto.parse_and_report(input_str, 38000, 30000, False, False, False,
output)
pronto.parse_and_report(input_str, hertz=38000, end_usecs=30000,
use_initial=False, generate_code=False,
verbose=False, output=output)
self.assertEqual(
output.getvalue(),
"Pronto code = "
@ -70,7 +74,9 @@ class TestRawToPronto(unittest.TestCase):
input_str = """
uint16_t rawData[7] = {
20100, 20472, 15092, 30704, 20102, 20472, 15086};"""
pronto.parse_and_report(input_str, 38000, 30000, True, True, False, output)
pronto.parse_and_report(input_str, 38000, end_usecs=30000,
use_initial=True, generate_code=True,
verbose=False, output=output)
self.assertEqual(
output.getvalue(),
"uint16_t pronto[12] = {0x0000, 0x006D, 0x0004, 0x0000, 0x02fb, "