|
| 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 |
0 commit comments