mirror of
https://github.com/crankyoldgit/IRremoteESP8266.git
synced 2026-01-12 00:05:10 +08:00
Support for Lixil Inax Toilet protocol. (#712)
* Add send/decode routines & unit tests. * Update example code. Fixes #706
This commit is contained in:
@@ -591,6 +591,7 @@ void handleRoot(void) {
|
||||
"<option value='17'>Denon</option>"
|
||||
"<option value='13'>Dish</option>"
|
||||
"<option value='43'>GICable</option>"
|
||||
"<option value='64'>Inax</option>"
|
||||
"<option value='6'>JVC</option>"
|
||||
"<option value='36'>Lasertag</option>"
|
||||
"<option value='58'>LEGOPF</option>"
|
||||
@@ -2571,6 +2572,14 @@ bool sendIRCode(IRsend *irsend, int const ir_type,
|
||||
irsend->sendPanasonic64(code, bits, repeat);
|
||||
break;
|
||||
#endif
|
||||
#if SEND_INAX
|
||||
case INAX: // 64
|
||||
if (bits == 0)
|
||||
bits = kInaxBits;
|
||||
repeat = std::max(repeat, kInaxMinRepeat);
|
||||
irsend->sendInax(code, bits, repeat);
|
||||
break;
|
||||
#endif
|
||||
#if SEND_JVC
|
||||
case JVC: // 6
|
||||
if (bits == 0)
|
||||
|
||||
@@ -527,6 +527,10 @@ bool IRrecv::decode(decode_results *results, irparams_t *save) {
|
||||
DPRINTLN("Attempting SHARP_AC decode");
|
||||
if (decodeSharpAc(results)) return true;
|
||||
#endif
|
||||
#if DECODE_INAX
|
||||
DPRINTLN("Attempting Inax decode");
|
||||
if (decodeInax(results)) return true;
|
||||
#endif // DECODE_INAX
|
||||
#if DECODE_HASH
|
||||
// decodeHash returns a hash on any input.
|
||||
// Thus, it needs to be last in the list.
|
||||
|
||||
@@ -213,6 +213,10 @@ class IRrecv {
|
||||
bool decodeLG(decode_results *results, uint16_t nbits = kLgBits,
|
||||
bool strict = false);
|
||||
#endif
|
||||
#if DECODE_INAX
|
||||
bool decodeInax(decode_results *results, const uint16_t nbits = kInaxBits,
|
||||
const bool strict = true);
|
||||
#endif // DECODE_INAX
|
||||
#if DECODE_JVC
|
||||
bool decodeJVC(decode_results *results, uint16_t nbits = kJvcBits,
|
||||
bool strict = true);
|
||||
|
||||
@@ -132,6 +132,9 @@
|
||||
#define DECODE_FUJITSU_AC true
|
||||
#define SEND_FUJITSU_AC true
|
||||
|
||||
#define DECODE_INAX true
|
||||
#define SEND_INAX true
|
||||
|
||||
#define DECODE_DAIKIN true
|
||||
#define SEND_DAIKIN true
|
||||
|
||||
@@ -317,8 +320,10 @@ enum decode_type_t {
|
||||
MITSUBISHI_HEAVY_152, // 60
|
||||
DAIKIN216,
|
||||
SHARP_AC,
|
||||
GOODWEATHER,
|
||||
INAX,
|
||||
// Add new entries before this one, and update it to point to the last entry.
|
||||
kLastDecodeType = SHARP_AC,
|
||||
kLastDecodeType = INAX,
|
||||
};
|
||||
|
||||
// Message lengths & required repeat values
|
||||
@@ -374,6 +379,8 @@ const uint16_t kHitachiAc1StateLength = 13;
|
||||
const uint16_t kHitachiAc1Bits = kHitachiAc1StateLength * 8;
|
||||
const uint16_t kHitachiAc2StateLength = 53;
|
||||
const uint16_t kHitachiAc2Bits = kHitachiAc2StateLength * 8;
|
||||
const uint16_t kInaxBits = 24;
|
||||
const uint16_t kInaxMinRepeat = kSingleRepeat;
|
||||
const uint16_t kJvcBits = 16;
|
||||
const uint16_t kKelvinatorStateLength = 16;
|
||||
const uint16_t kKelvinatorBits = kKelvinatorStateLength * 8;
|
||||
|
||||
@@ -536,6 +536,11 @@ bool IRsend::send(decode_type_t type, uint64_t data, uint16_t nbits) {
|
||||
sendGree(data, nbits);
|
||||
break;
|
||||
#endif
|
||||
#if SEND_INAX
|
||||
case INAX:
|
||||
sendInax(data, nbits);
|
||||
break;
|
||||
#endif // SEND_INAX
|
||||
#if SEND_JVC
|
||||
case JVC:
|
||||
sendJVC(data, nbits);
|
||||
|
||||
@@ -275,6 +275,10 @@ class IRsend {
|
||||
void sendFujitsuAC(unsigned char data[], uint16_t nbytes,
|
||||
uint16_t repeat = kFujitsuAcMinRepeat);
|
||||
#endif
|
||||
#if SEND_INAX
|
||||
void sendInax(const uint64_t data, const uint16_t nbits = kInaxBits,
|
||||
const uint16_t repeat = kInaxMinRepeat);
|
||||
#endif // SEND_INAX
|
||||
#if SEND_GLOBALCACHE
|
||||
void sendGC(uint16_t buf[], uint16_t len);
|
||||
#endif
|
||||
|
||||
@@ -137,6 +137,8 @@ decode_type_t strToDecodeType(const char * const str) {
|
||||
return decode_type_t::HITACHI_AC1;
|
||||
else if (!strcmp(str, "HITACHI_AC2"))
|
||||
return decode_type_t::HITACHI_AC2;
|
||||
else if (!strcmp(str, "INAX"))
|
||||
return decode_type_t::INAX;
|
||||
else if (!strcmp(str, "JVC"))
|
||||
return decode_type_t::JVC;
|
||||
else if (!strcmp(str, "KELVINATOR"))
|
||||
@@ -367,6 +369,9 @@ std::string typeToString(const decode_type_t protocol, const bool isRepeat) {
|
||||
case HITACHI_AC2:
|
||||
result = F("HITACHI_AC2");
|
||||
break;
|
||||
case INAX:
|
||||
result = F("INAX");
|
||||
break;
|
||||
case JVC:
|
||||
result = F("JVC");
|
||||
break;
|
||||
|
||||
98
src/ir_Inax.cpp
Normal file
98
src/ir_Inax.cpp
Normal file
@@ -0,0 +1,98 @@
|
||||
// Copyright 2019 David Conran (crankyoldgit)
|
||||
|
||||
#include <algorithm>
|
||||
#include "IRrecv.h"
|
||||
#include "IRsend.h"
|
||||
#include "IRutils.h"
|
||||
|
||||
// Support for an IR controlled Robot Toilet
|
||||
//
|
||||
// Brand: Lixil
|
||||
// Model: Inax
|
||||
// Type: DT-BA283
|
||||
|
||||
// Documentation:
|
||||
// https://www.lixil-manual.com/GCW-1365-16050/GCW-1365-16050.pdf
|
||||
|
||||
// Constants
|
||||
// Ref:
|
||||
// https://github.com/markszabo/IRremoteESP8266/issues/706
|
||||
const uint16_t kInaxTick = 500;
|
||||
const uint16_t kInaxHdrMark = 9000;
|
||||
const uint16_t kInaxHdrSpace = 4500;
|
||||
const uint16_t kInaxBitMark = 560;
|
||||
const uint16_t kInaxOneSpace = 1675;
|
||||
const uint16_t kInaxZeroSpace = kInaxBitMark;
|
||||
const uint16_t kInaxMinGap = 40000;
|
||||
|
||||
#if SEND_INAX
|
||||
// Send a Inax Toilet formatted message.
|
||||
//
|
||||
// Args:
|
||||
// data: The message to be sent.
|
||||
// nbits: The bit size of the message being sent. typically kInaxBits.
|
||||
// repeat: The number of times the message is to be repeated.
|
||||
//
|
||||
// Status: BETA / Should be working.
|
||||
//
|
||||
// Ref: https://github.com/markszabo/IRremoteESP8266/issues/706
|
||||
void IRsend::sendInax(const uint64_t data, const uint16_t nbits,
|
||||
const uint16_t repeat) {
|
||||
sendGeneric(kInaxHdrMark, kInaxHdrSpace,
|
||||
kInaxBitMark, kInaxOneSpace,
|
||||
kInaxBitMark, kInaxZeroSpace,
|
||||
kInaxBitMark, kInaxMinGap,
|
||||
data, nbits, 38, true, repeat, kDutyDefault);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if DECODE_INAX
|
||||
// Decode the supplied Inax Toilet message.
|
||||
//
|
||||
// Args:
|
||||
// results: Ptr to the data to decode and where to store the decode result.
|
||||
// nbits: Nr. of bits to expect in the data portion.
|
||||
// Typically kInaxBits.
|
||||
// strict: Flag to indicate if we strictly adhere to the specification.
|
||||
// Returns:
|
||||
// boolean: True if it can decode it, false if it can't.
|
||||
//
|
||||
// Status: BETA / Should be Working.
|
||||
//
|
||||
bool IRrecv::decodeInax(decode_results *results, const uint16_t nbits,
|
||||
const bool strict) {
|
||||
if (results->rawlen < 2 * nbits + kHeader + kFooter - 1)
|
||||
return false; // Can't possibly be a valid Inax message.
|
||||
if (strict && nbits != kInaxBits)
|
||||
return false; // We expect Inax to be a certain sized message.
|
||||
|
||||
uint64_t data = 0;
|
||||
uint16_t offset = kStartOffset;
|
||||
|
||||
// Header
|
||||
if (!matchMark(results->rawbuf[offset++], kInaxHdrMark)) return false;
|
||||
if (!matchSpace(results->rawbuf[offset++], kInaxHdrSpace)) return false;
|
||||
// Data
|
||||
match_result_t data_result =
|
||||
matchData(&(results->rawbuf[offset]), nbits, kInaxBitMark,
|
||||
kInaxOneSpace, kInaxBitMark, kInaxZeroSpace);
|
||||
if (data_result.success == false) return false;
|
||||
data = data_result.data;
|
||||
offset += data_result.used;
|
||||
// Footer
|
||||
if (!matchMark(results->rawbuf[offset++], kInaxBitMark)) return false;
|
||||
if (offset < results->rawlen &&
|
||||
!matchAtLeast(results->rawbuf[offset], kInaxMinGap))
|
||||
return false;
|
||||
|
||||
// Compliance
|
||||
|
||||
// Success
|
||||
results->bits = nbits;
|
||||
results->value = data;
|
||||
results->decode_type = INAX;
|
||||
results->command = 0;
|
||||
results->address = 0;
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
@@ -38,7 +38,7 @@ TESTS = IRutils_test IRsend_test ir_NEC_test ir_GlobalCache_test \
|
||||
ir_Carrier_test ir_Haier_test ir_Hitachi_test ir_GICable_test \
|
||||
ir_Whirlpool_test ir_Lutron_test ir_Electra_test ir_Pioneer_test \
|
||||
ir_MWM_test ir_Vestel_test ir_Teco_test ir_Tcl_test ir_Lego_test IRac_test \
|
||||
ir_MitsubishiHeavy_test ir_Trotec_test ir_Argo_test
|
||||
ir_MitsubishiHeavy_test ir_Trotec_test ir_Argo_test ir_Inax_test
|
||||
|
||||
# All Google Test headers. Usually you shouldn't change this
|
||||
# definition.
|
||||
@@ -83,7 +83,7 @@ PROTOCOLS = ir_NEC.o ir_Sony.o ir_Samsung.o ir_JVC.o ir_RCMM.o ir_RC5_RC6.o \
|
||||
ir_Midea.o ir_Magiquest.o ir_Lasertag.o ir_Carrier.o ir_Haier.o \
|
||||
ir_Hitachi.o ir_GICable.o ir_Whirlpool.o ir_Lutron.o ir_Electra.o \
|
||||
ir_Pioneer.o ir_MWM.o ir_Vestel.o ir_Teco.o ir_Tcl.o ir_Lego.o ir_Argo.o \
|
||||
ir_Trotec.o ir_MitsubishiHeavy.o
|
||||
ir_Trotec.o ir_MitsubishiHeavy.o ir_Inax.o
|
||||
|
||||
# All the IR Protocol header files.
|
||||
PROTOCOLS_H = $(USER_DIR)/ir_Argo.h \
|
||||
@@ -576,3 +576,12 @@ ir_Trotec_test.o : ir_Trotec_test.cpp $(COMMON_TEST_DEPS) $(GTEST_HEADERS)
|
||||
|
||||
ir_Trotec_test : $(COMMON_OBJ) ir_Trotec_test.o
|
||||
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@
|
||||
|
||||
ir_Inax.o : $(USER_DIR)/ir_Inax.cpp $(COMMON_DEPS)
|
||||
$(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/ir_Inax.cpp
|
||||
|
||||
ir_Inax_test.o : ir_Inax_test.cpp $(COMMON_TEST_DEPS) $(GTEST_HEADERS)
|
||||
$(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c ir_Inax_test.cpp
|
||||
|
||||
ir_Inax_test : $(COMMON_OBJ) ir_Inax_test.o
|
||||
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@
|
||||
|
||||
119
test/ir_Inax_test.cpp
Normal file
119
test/ir_Inax_test.cpp
Normal file
@@ -0,0 +1,119 @@
|
||||
// Copyright 2019 crankyoldgit (David Conran)
|
||||
|
||||
#include "IRsend.h"
|
||||
#include "IRsend_test.h"
|
||||
#include "IRutils.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
|
||||
// General housekeeping
|
||||
TEST(TestInax, Housekeeping) {
|
||||
ASSERT_EQ("INAX", typeToString(INAX));
|
||||
ASSERT_FALSE(hasACState(INAX));
|
||||
}
|
||||
|
||||
// Tests for sendInax().
|
||||
// Test sending typical data only.
|
||||
TEST(TestSendInax, SendDataOnly) {
|
||||
IRsendTest irsend(0);
|
||||
irsend.begin();
|
||||
|
||||
irsend.reset();
|
||||
irsend.sendInax(0x5C32CD); // Small flush.
|
||||
EXPECT_EQ(
|
||||
"f38000d50"
|
||||
"m9000s4500"
|
||||
"m560s560m560s1675m560s560m560s1675m560s1675m560s1675m560s560m560s560"
|
||||
"m560s560m560s560m560s1675m560s1675m560s560m560s560m560s1675m560s560"
|
||||
"m560s1675m560s1675m560s560m560s560m560s1675m560s1675m560s560m560s1675"
|
||||
"m560s40000"
|
||||
"m9000s4500"
|
||||
"m560s560m560s1675m560s560m560s1675m560s1675m560s1675m560s560m560s560"
|
||||
"m560s560m560s560m560s1675m560s1675m560s560m560s560m560s1675m560s560"
|
||||
"m560s1675m560s1675m560s560m560s560m560s1675m560s1675m560s560m560s1675"
|
||||
"m560s40000",
|
||||
irsend.outputStr());
|
||||
|
||||
irsend.reset();
|
||||
}
|
||||
|
||||
// Test sending with different repeats.
|
||||
TEST(TestSendInax, SendWithRepeats) {
|
||||
IRsendTest irsend(0);
|
||||
irsend.begin();
|
||||
|
||||
irsend.reset();
|
||||
irsend.sendInax(0x5C32CD, kInaxBits, 0); // 0 repeats.
|
||||
EXPECT_EQ(
|
||||
"f38000d50"
|
||||
"m9000s4500"
|
||||
"m560s560m560s1675m560s560m560s1675m560s1675m560s1675m560s560m560s560"
|
||||
"m560s560m560s560m560s1675m560s1675m560s560m560s560m560s1675m560s560"
|
||||
"m560s1675m560s1675m560s560m560s560m560s1675m560s1675m560s560m560s1675"
|
||||
"m560s40000",
|
||||
irsend.outputStr());
|
||||
irsend.sendInax(0x5C32CD, kInaxBits, 2); // 2 repeats.
|
||||
EXPECT_EQ(
|
||||
"f38000d50"
|
||||
"m9000s4500"
|
||||
"m560s560m560s1675m560s560m560s1675m560s1675m560s1675m560s560m560s560"
|
||||
"m560s560m560s560m560s1675m560s1675m560s560m560s560m560s1675m560s560"
|
||||
"m560s1675m560s1675m560s560m560s560m560s1675m560s1675m560s560m560s1675"
|
||||
"m560s40000"
|
||||
"m9000s4500"
|
||||
"m560s560m560s1675m560s560m560s1675m560s1675m560s1675m560s560m560s560"
|
||||
"m560s560m560s560m560s1675m560s1675m560s560m560s560m560s1675m560s560"
|
||||
"m560s1675m560s1675m560s560m560s560m560s1675m560s1675m560s560m560s1675"
|
||||
"m560s40000"
|
||||
"m9000s4500"
|
||||
"m560s560m560s1675m560s560m560s1675m560s1675m560s1675m560s560m560s560"
|
||||
"m560s560m560s560m560s1675m560s1675m560s560m560s560m560s1675m560s560"
|
||||
"m560s1675m560s1675m560s560m560s560m560s1675m560s1675m560s560m560s1675"
|
||||
"m560s40000",
|
||||
irsend.outputStr());
|
||||
}
|
||||
|
||||
// Tests for decodeInax().
|
||||
|
||||
// Decode normal Inax messages.
|
||||
TEST(TestDecodeInax, SyntheticDecode) {
|
||||
IRsendTest irsend(0);
|
||||
IRrecv irrecv(0);
|
||||
irsend.begin();
|
||||
|
||||
// Normal Inax 24-bit message.
|
||||
irsend.reset();
|
||||
irsend.sendInax(0x5C32CD);
|
||||
irsend.makeDecodeResult();
|
||||
ASSERT_TRUE(irrecv.decode(&irsend.capture));
|
||||
EXPECT_EQ(INAX, irsend.capture.decode_type);
|
||||
EXPECT_EQ(kInaxBits, irsend.capture.bits);
|
||||
EXPECT_EQ(0x5C32CD, irsend.capture.value);
|
||||
EXPECT_EQ(0, irsend.capture.address);
|
||||
EXPECT_EQ(0, irsend.capture.command);
|
||||
}
|
||||
|
||||
// Decode real example via Issue #704
|
||||
TEST(TestDecodeInax, DecodeExamples) {
|
||||
IRsendTest irsend(0);
|
||||
IRrecv irrecv(0);
|
||||
irsend.begin();
|
||||
|
||||
irsend.reset();
|
||||
// Inax Small Flush from Issue #309
|
||||
uint16_t smallFlushRawData[51] = {
|
||||
8996, 4474, 568, 556, 560, 1676, 568, 556, 562, 1676, 562, 1678, 566,
|
||||
1674, 566, 558, 560, 560, 566, 556, 566, 556, 560, 1678, 562, 1676, 566,
|
||||
556, 562, 560, 564, 1672, 566, 556, 562, 1676, 562, 1678, 562, 560, 564,
|
||||
558, 564, 1674, 560, 1678, 564, 560, 566, 1670, 562};
|
||||
|
||||
irsend.sendRaw(smallFlushRawData, 51, 38);
|
||||
irsend.makeDecodeResult();
|
||||
|
||||
ASSERT_TRUE(irrecv.decode(&irsend.capture));
|
||||
EXPECT_EQ(INAX, irsend.capture.decode_type);
|
||||
EXPECT_EQ(kInaxBits, irsend.capture.bits);
|
||||
EXPECT_EQ(0x5C32CD, irsend.capture.value);
|
||||
EXPECT_EQ(0, irsend.capture.address);
|
||||
EXPECT_EQ(0, irsend.capture.command);
|
||||
}
|
||||
@@ -50,7 +50,7 @@ PROTOCOLS = ir_NEC.o ir_Sony.o ir_Samsung.o ir_JVC.o ir_RCMM.o ir_RC5_RC6.o \
|
||||
ir_Magiquest.o ir_Lasertag.o ir_Carrier.o ir_Haier.o ir_Hitachi.o \
|
||||
ir_GICable.o ir_Whirlpool.o ir_Lutron.o ir_Electra.o ir_Pioneer.o \
|
||||
ir_MWM.o ir_Vestel.o ir_Teco.o ir_Tcl.o ir_Lego.o \
|
||||
ir_MitsubishiHeavy.o
|
||||
ir_MitsubishiHeavy.o ir_Inax.o
|
||||
|
||||
# Common object files
|
||||
COMMON_OBJ = IRutils.o IRtimer.o IRsend.o IRrecv.o $(PROTOCOLS)
|
||||
@@ -104,6 +104,9 @@ ir_Samsung.o : $(USER_DIR)/ir_Samsung.cpp $(USER_DIR)/ir_Samsung.h $(COMMON_DEPS
|
||||
ir_Kelvinator.o : $(USER_DIR)/ir_Kelvinator.cpp $(USER_DIR)/ir_Kelvinator.h $(COMMON_DEPS)
|
||||
$(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/ir_Kelvinator.cpp
|
||||
|
||||
ir_Inax.o : $(USER_DIR)/ir_Inax.cpp $(COMMON_DEPS)
|
||||
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Inax.cpp
|
||||
|
||||
ir_JVC.o : $(USER_DIR)/ir_JVC.cpp $(COMMON_DEPS)
|
||||
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_JVC.cpp
|
||||
|
||||
|
||||
Reference in New Issue
Block a user