Sitelet https://github.com/angeldeejay/IRremoteESP8266/commit/0f9485700f1847bdc536fe032000388fc7f2c950
Skip to content

Commit 0f94857

Browse files
authored
TOTO: An experimental _(s)wipe_ at support for Toto Toilets. (crankyoldgit#1811)
* Add `sendToto()` and `decodeToto()` routines. - Supports two (2) bit sizes, 24 (short) & 48 (long) bit codes. - Includes/assumes a standard bit prefix to ensure it fits into a `uint64_t`. - `Xor` integrity check. - LSBF order based on user analysis. * Update supported devices. * Add unit tests to cover new changes. _Legs crossed_, this should do it for the _Number Two_ toilet protocol supported by this library. _Roll_ on the next one. Fixes crankyoldgit#1806
1 parent c41ce9d commit 0f94857

9 files changed

Lines changed: 329 additions & 2 deletions

File tree

‎src/IRrecv.cpp‎

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1114,6 +1114,11 @@ bool IRrecv::decode(decode_results *results, irparams_t *save,
11141114
DPRINTLN("Attempting Carrier AC 128-bit decode");
11151115
if (decodeCarrierAC128(results, offset)) return true;
11161116
#endif // DECODE_CARRIER_AC128
1117+
#if DECODE_TOTO
1118+
DPRINTLN("Attempting Toto 48/24-bit decode");
1119+
if (decodeToto(results, offset, kTotoLongBits) || // Long needs to be first
1120+
decodeToto(results, offset, kTotoShortBits)) return true;
1121+
#endif // DECODE_TOTO
11171122
// Typically new protocols are added above this line.
11181123
}
11191124
#if DECODE_HASH
@@ -1128,7 +1133,7 @@ bool IRrecv::decode(decode_results *results, irparams_t *save,
11281133
if (!resumed) // Check if we have already resumed.
11291134
resume();
11301135
return false;
1131-
}
1136+
} // NOLINT(readability/fn_size)
11321137

11331138
/// Convert the tolerance percentage into something valid.
11341139
/// @param[in] percentage An integer percentage.

‎src/IRrecv.h‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -822,6 +822,11 @@ class IRrecv {
822822
const uint16_t nbits = kAirtonBits,
823823
const bool strict = true);
824824
#endif // DECODE_AIRTON
825+
#if DECODE_TOTO
826+
bool decodeToto(decode_results *results, uint16_t offset = kStartOffset,
827+
const uint16_t nbits = kTotoBits,
828+
const bool strict = true);
829+
#endif // DECODE_TOTO
825830
};
826831

827832
#endif // IRRECV_H_

‎src/IRremoteESP8266.h‎

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -882,6 +882,13 @@
882882
#define SEND_HAIER_AC160 _IR_ENABLE_DEFAULT_
883883
#endif // SEND_HAIER_AC160
884884

885+
#ifndef DECODE_TOTO
886+
#define DECODE_TOTO _IR_ENABLE_DEFAULT_
887+
#endif // DECODE_TOTO
888+
#ifndef SEND_TOTO
889+
#define SEND_TOTO _IR_ENABLE_DEFAULT_
890+
#endif // SEND_TOTO
891+
885892
#if (DECODE_ARGO || DECODE_DAIKIN || DECODE_FUJITSU_AC || DECODE_GREE || \
886893
DECODE_KELVINATOR || DECODE_MITSUBISHI_AC || DECODE_TOSHIBA_AC || \
887894
DECODE_TROTEC || DECODE_HAIER_AC || DECODE_HITACHI_AC || \
@@ -1055,8 +1062,9 @@ enum decode_type_t {
10551062
DAIKIN200,
10561063
HAIER_AC160, // 115
10571064
CARRIER_AC128,
1065+
TOTO,
10581066
// Add new entries before this one, and update it to point to the last entry.
1059-
kLastDecodeType = CARRIER_AC128,
1067+
kLastDecodeType = TOTO,
10601068
};
10611069

10621070
// Message lengths & required repeat values
@@ -1298,6 +1306,10 @@ const uint16_t kToshibaACStateLengthShort = kToshibaACStateLength - 2;
12981306
const uint16_t kToshibaACBitsShort = kToshibaACStateLengthShort * 8;
12991307
const uint16_t kToshibaACStateLengthLong = kToshibaACStateLength + 1;
13001308
const uint16_t kToshibaACBitsLong = kToshibaACStateLengthLong * 8;
1309+
const uint16_t kTotoBits = 24;
1310+
const uint16_t kTotoShortBits = kTotoBits;
1311+
const uint16_t kTotoLongBits = kTotoShortBits * 2;
1312+
const uint16_t kTotoDefaultRepeat = kSingleRepeat;
13011313
const uint16_t kTranscoldBits = 24;
13021314
const uint16_t kTranscoldDefaultRepeat = kNoRepeat;
13031315
const uint16_t kTrotecStateLength = 9;

‎src/IRsend.cpp‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -571,6 +571,7 @@ uint16_t IRsend::minRepeats(const decode_type_t protocol) {
571571
case MULTIBRACKETS:
572572
case SHERWOOD:
573573
case TOSHIBA_AC:
574+
case TOTO:
574575
return kSingleRepeat;
575576
// Special
576577
case AIRWELL:
@@ -633,6 +634,7 @@ uint16_t IRsend::defaultBits(const decode_type_t protocol) {
633634
case MIDEA24:
634635
case NIKAI:
635636
case RCMM:
637+
case TOTO:
636638
case TRANSCOLD:
637639
return 24;
638640
case LG:
@@ -1072,6 +1074,11 @@ bool IRsend::send(const decode_type_t type, const uint64_t data,
10721074
sendTeco(data, nbits, min_repeat);
10731075
break;
10741076
#endif // SEND_TECO
1077+
#if SEND_TOTO
1078+
case TOTO:
1079+
sendToto(data, nbits, min_repeat);
1080+
break;
1081+
#endif // SEND_TOTO
10751082
#if SEND_TRANSCOLD
10761083
case TRANSCOLD:
10771084
sendTranscold(data, nbits, min_repeat);

‎src/IRsend.h‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -806,6 +806,10 @@ class IRsend {
806806
void sendAirton(const uint64_t data, const uint16_t nbits = kAirtonBits,
807807
const uint16_t repeat = kAirtonDefaultRepeat);
808808
#endif // SEND_AIRTON
809+
#if SEND_TOTO
810+
void sendToto(const uint64_t data, const uint16_t nbits = kTotoBits,
811+
const uint16_t repeat = kTotoDefaultRepeat);
812+
#endif // SEND_TOTO
809813

810814
protected:
811815
#ifdef UNIT_TEST

‎src/IRtext.cpp‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -399,6 +399,7 @@ IRTEXT_CONST_BLOB_DECL(kAllProtocolNamesStr) {
399399
D_STR_DAIKIN200 "\x0"
400400
D_STR_HAIER_AC160 "\x0"
401401
D_STR_CARRIER_AC128 "\x0"
402+
D_STR_TOTO "\x0"
402403
///< New protocol strings should be added just above this line.
403404
"\x0" ///< This string requires double null termination.
404405
};

‎src/ir_Toto.cpp‎

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
// Copyright 2022 David Conran (crankyoldgit)
2+
/// @file
3+
/// @brief Support for the Toto Toilet IR protocols.
4+
/// @see https://www.d-resi.jp/dt/nishi-shinjuku/limited/imgs/pdf/book6.pdf
5+
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1806
6+
7+
// Supports:
8+
// Brand: Toto, Model: Washlet Toilet NJ
9+
10+
#include <algorithm>
11+
#include "IRrecv.h"
12+
#include "IRsend.h"
13+
#include "IRutils.h"
14+
15+
// Constants
16+
const uint16_t kTotoHdrMark = 6197;
17+
const uint16_t kTotoHdrSpace = 2754;
18+
const uint16_t kTotoBitMark = 600;
19+
const uint16_t kTotoOneSpace = 1634;
20+
const uint16_t kTotoZeroSpace = 516;
21+
const uint16_t kTotoGap = 38000;
22+
const uint16_t kTotoSpecialGap = 42482;
23+
const uint64_t kTotoPrefix = 0x0802;
24+
const uint16_t kTotoPrefixBits = 15;
25+
26+
#if SEND_TOTO
27+
/// Send a Toto Toilet formatted message.
28+
/// Status: BETA / Seems to work.
29+
/// @param[in] data The message to be sent.
30+
/// @param[in] nbits The number of bits of message to be sent.
31+
/// @param[in] repeat The number of times the command is to be repeated.
32+
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1806
33+
void IRsend::sendToto(const uint64_t data, const uint16_t nbits,
34+
const uint16_t repeat) {
35+
if (nbits <= kTotoShortBits) { // Short code message.
36+
sendGeneric(kTotoHdrMark, kTotoHdrSpace,
37+
kTotoBitMark, kTotoOneSpace,
38+
kTotoBitMark, kTotoZeroSpace,
39+
kTotoBitMark, kTotoGap,
40+
(data << kTotoPrefixBits) | kTotoPrefix,
41+
nbits + kTotoPrefixBits,
42+
38, false, repeat, kDutyDefault);
43+
} else { // Long code message
44+
// This is really two messages sent at least one extra time each.
45+
sendToto(data >> kTotoShortBits, nbits - kTotoShortBits, repeat + 1);
46+
sendToto(GETBITS64(data, 0, kTotoShortBits), kTotoShortBits, repeat + 1);
47+
}
48+
}
49+
#endif // SEND_TOTO
50+
51+
#if DECODE_TOTO
52+
/// Decode the supplied Toto Toilet message.
53+
/// Status: ALPHA / Untested.
54+
/// @param[in,out] results Ptr to the data to decode & where to store the result
55+
/// @param[in] offset The starting index to use when attempting to decode the
56+
/// raw data. Typically/Defaults to kStartOffset.
57+
/// @param[in] nbits The number of data bits to expect.
58+
/// @param[in] strict Flag indicating if we should perform strict matching.
59+
/// @return True if it can decode it, false if it can't.
60+
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1806
61+
bool IRrecv::decodeToto(decode_results *results, uint16_t offset,
62+
const uint16_t nbits, const bool strict) {
63+
if (strict && nbits != kTotoShortBits && nbits != kTotoLongBits)
64+
return false; // We expect Toto to be a certain sized messages.
65+
66+
const uint16_t repeats = (nbits > kTotoShortBits) ? kTotoDefaultRepeat + 1
67+
: kTotoDefaultRepeat;
68+
const uint16_t ksections = (nbits > kTotoShortBits) ? 2 : 1;
69+
const uint16_t ksection_bits = nbits / ksections;
70+
71+
if (results->rawlen < (2 * (nbits + kTotoPrefixBits) + kHeader + kFooter) *
72+
(repeats + 1) - 1 + offset)
73+
return false; // We don't have enough entries to possibly match.
74+
75+
uint16_t used = 0;
76+
77+
// Long messages have two sections, short have only one.
78+
for (uint16_t section = 1; section <= ksections; section++) {
79+
results->value <<= ksection_bits;
80+
uint64_t data = 0;
81+
uint64_t repeat_data = 0;
82+
for (uint16_t r = 0; r <= repeats; r++) { // We expect a repeat.
83+
uint64_t prefix = 0;
84+
// Header + Prefix
85+
used = matchGeneric(results->rawbuf + offset, &prefix,
86+
results->rawlen - offset, kTotoPrefixBits,
87+
kTotoHdrMark, kTotoHdrSpace,
88+
kTotoBitMark, kTotoOneSpace,
89+
kTotoBitMark, kTotoZeroSpace,
90+
0, 0, false, // No Footer
91+
kUseDefTol, kMarkExcess, false);
92+
if (!used) return false; // Didn't match, so fail.
93+
offset += used;
94+
if (strict && (prefix != kTotoPrefix)) return false;
95+
// Data + Footer + Gap
96+
used = matchGeneric(results->rawbuf + offset, &data,
97+
results->rawlen - offset, ksection_bits,
98+
0, 0, // No Header
99+
kTotoBitMark, kTotoOneSpace,
100+
kTotoBitMark, kTotoZeroSpace,
101+
kTotoBitMark, kTotoGap, true,
102+
kUseDefTol, kMarkExcess, false);
103+
if (!used) return false; // Didn't match, so fail.
104+
offset += used;
105+
if (strict) {
106+
if (r && data != repeat_data) return false; // Repeat didn't match.
107+
// Integrity check.
108+
uint8_t result = 0;
109+
uint64_t check = data;
110+
uint16_t bits = 0;
111+
// Loop over the data 8 bits at a time.
112+
while (bits < ksection_bits) {
113+
result ^= (check & 0b111111111); // Xor with the last 8 bits.
114+
bits += 8;
115+
check >>= 8;
116+
}
117+
if (result) return false; // Intergrity check failed.
118+
}
119+
repeat_data = data;
120+
}
121+
results->value |= data;
122+
}
123+
// Success
124+
results->bits = nbits;
125+
results->decode_type = decode_type_t::TOTO;
126+
results->command = GETBITS64(results->value, 0, ksection_bits - 8);
127+
results->address = GETBITS64(results->value, ksection_bits,
128+
ksection_bits - 8);
129+
return true;
130+
}
131+
#endif // DECODE_TOTO

‎src/locale/defaults.h‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1009,6 +1009,9 @@ D_STR_INDIRECT " " D_STR_MODE
10091009
#ifndef D_STR_TOSHIBA_AC
10101010
#define D_STR_TOSHIBA_AC "TOSHIBA_AC"
10111011
#endif // D_STR_TOSHIBA_AC
1012+
#ifndef D_STR_TOTO
1013+
#define D_STR_TOTO "TOTO"
1014+
#endif // D_STR_TOTO
10121015
#ifndef D_STR_TRANSCOLD
10131016
#define D_STR_TRANSCOLD "TRANSCOLD"
10141017
#endif // D_STR_TRANSCOLD

0 commit comments

Comments
 (0)