Skip to content

Commit

Permalink
TOTO: An experimental _(s)wipe_ at support for Toto Toilets. (#1811)
Browse files Browse the repository at this point in the history
* 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 #1806
  • Loading branch information
crankyoldgit committed May 24, 2022
1 parent c41ce9d commit 0f94857
Show file tree
Hide file tree
Showing 9 changed files with 329 additions and 2 deletions.
7 changes: 6 additions & 1 deletion src/IRrecv.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1114,6 +1114,11 @@ bool IRrecv::decode(decode_results *results, irparams_t *save,
DPRINTLN("Attempting Carrier AC 128-bit decode");
if (decodeCarrierAC128(results, offset)) return true;
#endif // DECODE_CARRIER_AC128
#if DECODE_TOTO
DPRINTLN("Attempting Toto 48/24-bit decode");
if (decodeToto(results, offset, kTotoLongBits) || // Long needs to be first
decodeToto(results, offset, kTotoShortBits)) return true;
#endif // DECODE_TOTO
// Typically new protocols are added above this line.
}
#if DECODE_HASH
Expand All @@ -1128,7 +1133,7 @@ bool IRrecv::decode(decode_results *results, irparams_t *save,
if (!resumed) // Check if we have already resumed.
resume();
return false;
}
} // NOLINT(readability/fn_size)

/// Convert the tolerance percentage into something valid.
/// @param[in] percentage An integer percentage.
Expand Down
5 changes: 5 additions & 0 deletions src/IRrecv.h
Original file line number Diff line number Diff line change
Expand Up @@ -822,6 +822,11 @@ class IRrecv {
const uint16_t nbits = kAirtonBits,
const bool strict = true);
#endif // DECODE_AIRTON
#if DECODE_TOTO
bool decodeToto(decode_results *results, uint16_t offset = kStartOffset,
const uint16_t nbits = kTotoBits,
const bool strict = true);
#endif // DECODE_TOTO
};

#endif // IRRECV_H_
14 changes: 13 additions & 1 deletion src/IRremoteESP8266.h
Original file line number Diff line number Diff line change
Expand Up @@ -882,6 +882,13 @@
#define SEND_HAIER_AC160 _IR_ENABLE_DEFAULT_
#endif // SEND_HAIER_AC160

#ifndef DECODE_TOTO
#define DECODE_TOTO _IR_ENABLE_DEFAULT_
#endif // DECODE_TOTO
#ifndef SEND_TOTO
#define SEND_TOTO _IR_ENABLE_DEFAULT_
#endif // SEND_TOTO

#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 || \
Expand Down Expand Up @@ -1055,8 +1062,9 @@ enum decode_type_t {
DAIKIN200,
HAIER_AC160, // 115
CARRIER_AC128,
TOTO,
// Add new entries before this one, and update it to point to the last entry.
kLastDecodeType = CARRIER_AC128,
kLastDecodeType = TOTO,
};

// Message lengths & required repeat values
Expand Down Expand Up @@ -1298,6 +1306,10 @@ const uint16_t kToshibaACStateLengthShort = kToshibaACStateLength - 2;
const uint16_t kToshibaACBitsShort = kToshibaACStateLengthShort * 8;
const uint16_t kToshibaACStateLengthLong = kToshibaACStateLength + 1;
const uint16_t kToshibaACBitsLong = kToshibaACStateLengthLong * 8;
const uint16_t kTotoBits = 24;
const uint16_t kTotoShortBits = kTotoBits;
const uint16_t kTotoLongBits = kTotoShortBits * 2;
const uint16_t kTotoDefaultRepeat = kSingleRepeat;
const uint16_t kTranscoldBits = 24;
const uint16_t kTranscoldDefaultRepeat = kNoRepeat;
const uint16_t kTrotecStateLength = 9;
Expand Down
7 changes: 7 additions & 0 deletions src/IRsend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -571,6 +571,7 @@ uint16_t IRsend::minRepeats(const decode_type_t protocol) {
case MULTIBRACKETS:
case SHERWOOD:
case TOSHIBA_AC:
case TOTO:
return kSingleRepeat;
// Special
case AIRWELL:
Expand Down Expand Up @@ -633,6 +634,7 @@ uint16_t IRsend::defaultBits(const decode_type_t protocol) {
case MIDEA24:
case NIKAI:
case RCMM:
case TOTO:
case TRANSCOLD:
return 24;
case LG:
Expand Down Expand Up @@ -1072,6 +1074,11 @@ bool IRsend::send(const decode_type_t type, const uint64_t data,
sendTeco(data, nbits, min_repeat);
break;
#endif // SEND_TECO
#if SEND_TOTO
case TOTO:
sendToto(data, nbits, min_repeat);
break;
#endif // SEND_TOTO
#if SEND_TRANSCOLD
case TRANSCOLD:
sendTranscold(data, nbits, min_repeat);
Expand Down
4 changes: 4 additions & 0 deletions src/IRsend.h
Original file line number Diff line number Diff line change
Expand Up @@ -806,6 +806,10 @@ class IRsend {
void sendAirton(const uint64_t data, const uint16_t nbits = kAirtonBits,
const uint16_t repeat = kAirtonDefaultRepeat);
#endif // SEND_AIRTON
#if SEND_TOTO
void sendToto(const uint64_t data, const uint16_t nbits = kTotoBits,
const uint16_t repeat = kTotoDefaultRepeat);
#endif // SEND_TOTO

protected:
#ifdef UNIT_TEST
Expand Down
1 change: 1 addition & 0 deletions src/IRtext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -399,6 +399,7 @@ IRTEXT_CONST_BLOB_DECL(kAllProtocolNamesStr) {
D_STR_DAIKIN200 "\x0"
D_STR_HAIER_AC160 "\x0"
D_STR_CARRIER_AC128 "\x0"
D_STR_TOTO "\x0"
///< New protocol strings should be added just above this line.
"\x0" ///< This string requires double null termination.
};
Expand Down
131 changes: 131 additions & 0 deletions src/ir_Toto.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
// Copyright 2022 David Conran (crankyoldgit)
/// @file
/// @brief Support for the Toto Toilet IR protocols.
/// @see https://www.d-resi.jp/dt/nishi-shinjuku/limited/imgs/pdf/book6.pdf
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1806

// Supports:
// Brand: Toto, Model: Washlet Toilet NJ

#include <algorithm>
#include "IRrecv.h"
#include "IRsend.h"
#include "IRutils.h"

// Constants
const uint16_t kTotoHdrMark = 6197;
const uint16_t kTotoHdrSpace = 2754;
const uint16_t kTotoBitMark = 600;
const uint16_t kTotoOneSpace = 1634;
const uint16_t kTotoZeroSpace = 516;
const uint16_t kTotoGap = 38000;
const uint16_t kTotoSpecialGap = 42482;
const uint64_t kTotoPrefix = 0x0802;
const uint16_t kTotoPrefixBits = 15;

#if SEND_TOTO
/// Send a Toto Toilet formatted message.
/// Status: BETA / Seems to work.
/// @param[in] data The message to be sent.
/// @param[in] nbits The number of bits of message to be sent.
/// @param[in] repeat The number of times the command is to be repeated.
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1806
void IRsend::sendToto(const uint64_t data, const uint16_t nbits,
const uint16_t repeat) {
if (nbits <= kTotoShortBits) { // Short code message.
sendGeneric(kTotoHdrMark, kTotoHdrSpace,
kTotoBitMark, kTotoOneSpace,
kTotoBitMark, kTotoZeroSpace,
kTotoBitMark, kTotoGap,
(data << kTotoPrefixBits) | kTotoPrefix,
nbits + kTotoPrefixBits,
38, false, repeat, kDutyDefault);
} else { // Long code message
// This is really two messages sent at least one extra time each.
sendToto(data >> kTotoShortBits, nbits - kTotoShortBits, repeat + 1);
sendToto(GETBITS64(data, 0, kTotoShortBits), kTotoShortBits, repeat + 1);
}
}
#endif // SEND_TOTO

#if DECODE_TOTO
/// Decode the supplied Toto Toilet message.
/// Status: ALPHA / Untested.
/// @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.
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1806
bool IRrecv::decodeToto(decode_results *results, uint16_t offset,
const uint16_t nbits, const bool strict) {
if (strict && nbits != kTotoShortBits && nbits != kTotoLongBits)
return false; // We expect Toto to be a certain sized messages.

const uint16_t repeats = (nbits > kTotoShortBits) ? kTotoDefaultRepeat + 1
: kTotoDefaultRepeat;
const uint16_t ksections = (nbits > kTotoShortBits) ? 2 : 1;
const uint16_t ksection_bits = nbits / ksections;

if (results->rawlen < (2 * (nbits + kTotoPrefixBits) + kHeader + kFooter) *
(repeats + 1) - 1 + offset)
return false; // We don't have enough entries to possibly match.

uint16_t used = 0;

// Long messages have two sections, short have only one.
for (uint16_t section = 1; section <= ksections; section++) {
results->value <<= ksection_bits;
uint64_t data = 0;
uint64_t repeat_data = 0;
for (uint16_t r = 0; r <= repeats; r++) { // We expect a repeat.
uint64_t prefix = 0;
// Header + Prefix
used = matchGeneric(results->rawbuf + offset, &prefix,
results->rawlen - offset, kTotoPrefixBits,
kTotoHdrMark, kTotoHdrSpace,
kTotoBitMark, kTotoOneSpace,
kTotoBitMark, kTotoZeroSpace,
0, 0, false, // No Footer
kUseDefTol, kMarkExcess, false);
if (!used) return false; // Didn't match, so fail.
offset += used;
if (strict && (prefix != kTotoPrefix)) return false;
// Data + Footer + Gap
used = matchGeneric(results->rawbuf + offset, &data,
results->rawlen - offset, ksection_bits,
0, 0, // No Header
kTotoBitMark, kTotoOneSpace,
kTotoBitMark, kTotoZeroSpace,
kTotoBitMark, kTotoGap, true,
kUseDefTol, kMarkExcess, false);
if (!used) return false; // Didn't match, so fail.
offset += used;
if (strict) {
if (r && data != repeat_data) return false; // Repeat didn't match.
// Integrity check.
uint8_t result = 0;
uint64_t check = data;
uint16_t bits = 0;
// Loop over the data 8 bits at a time.
while (bits < ksection_bits) {
result ^= (check & 0b111111111); // Xor with the last 8 bits.
bits += 8;
check >>= 8;
}
if (result) return false; // Intergrity check failed.
}
repeat_data = data;
}
results->value |= data;
}
// Success
results->bits = nbits;
results->decode_type = decode_type_t::TOTO;
results->command = GETBITS64(results->value, 0, ksection_bits - 8);
results->address = GETBITS64(results->value, ksection_bits,
ksection_bits - 8);
return true;
}
#endif // DECODE_TOTO
3 changes: 3 additions & 0 deletions src/locale/defaults.h
Original file line number Diff line number Diff line change
Expand Up @@ -1009,6 +1009,9 @@ D_STR_INDIRECT " " D_STR_MODE
#ifndef D_STR_TOSHIBA_AC
#define D_STR_TOSHIBA_AC "TOSHIBA_AC"
#endif // D_STR_TOSHIBA_AC
#ifndef D_STR_TOTO
#define D_STR_TOTO "TOTO"
#endif // D_STR_TOTO
#ifndef D_STR_TRANSCOLD
#define D_STR_TRANSCOLD "TRANSCOLD"
#endif // D_STR_TRANSCOLD
Expand Down
Loading

0 comments on commit 0f94857

Please sign in to comment.