Skip to content

Commit

Permalink
Support for Lixil Inax Toilet protocol.
Browse files Browse the repository at this point in the history
* Add send/decode routines & unit tests.
* Update example code.

Fixes #706
  • Loading branch information
crankyoldgit committed May 20, 2019
1 parent 7275d78 commit 8009d19
Show file tree
Hide file tree
Showing 11 changed files with 271 additions and 4 deletions.
9 changes: 9 additions & 0 deletions examples/IRMQTTServer/IRMQTTServer.ino
Original file line number Diff line number Diff line change
Expand Up @@ -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>"
Expand Down Expand Up @@ -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)
Expand Down
4 changes: 4 additions & 0 deletions src/IRrecv.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
4 changes: 4 additions & 0 deletions src/IRrecv.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
9 changes: 8 additions & 1 deletion src/IRremoteESP8266.h
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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;
Expand Down
5 changes: 5 additions & 0 deletions src/IRsend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
4 changes: 4 additions & 0 deletions src/IRsend.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
5 changes: 5 additions & 0 deletions src/IRutils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,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"))
Expand Down Expand Up @@ -362,6 +364,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;
Expand Down
98 changes: 98 additions & 0 deletions src/ir_Inax.cpp
Original file line number Diff line number Diff line change
@@ -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
13 changes: 11 additions & 2 deletions test/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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 \
Expand Down Expand Up @@ -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 changes: 119 additions & 0 deletions test/ir_Inax_test.cpp
Original file line number Diff line number Diff line change
@@ -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);
}
5 changes: 4 additions & 1 deletion tools/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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

Expand Down

0 comments on commit 8009d19

Please sign in to comment.