// Copyright 2018 David Conran // Lutron #define __STDC_LIMIT_MACROS #include #include #include "IRrecv.h" #include "IRsend.h" #include "IRutils.h" // Notes: // The Lutron protocol uses a sort of Run Length encoding to encode // its data. There is no header or footer per-se. // As a mark is the first data we will notice, we always assume the First // bit of the technically 36-bit protocol is '1'. So it is assumed, and thus // we only care about the 35 bits of data. // Constants // Ref: // https://github.com/crankyoldgit/IRremoteESP8266/issues/515 const uint16_t kLutronTick = 2288; const uint32_t kLutronGap = 150000; // Completely made up value. const uint16_t kLutronDelta = 400; // +/- 300 usecs. #if SEND_LUTRON // Send a Lutron formatted message. // // Args: // data: The message to be sent. // nbits: The number of bits of the message to be sent. Typically kLutronBits // repeat: The number of times the command is to be repeated. // // Status: Stable / Appears to be working for real devices. // Notes: // Protocol is really 36 bits long, but the first bit is always a 1. // So, assume the 1 and only have a normal payload of 35 bits. // // Ref: // https://github.com/crankyoldgit/IRremoteESP8266/issues/515 void IRsend::sendLutron(uint64_t data, uint16_t nbits, uint16_t repeat) { enableIROut(40000, 40); // 40Khz & 40% dutycycle. for (uint16_t r = 0; r <= repeat; r++) { mark(kLutronTick); // 1st bit is always '1'. // Send the supplied data in MSB First order. for (uint64_t mask = 1ULL << (nbits - 1); mask; mask >>= 1) if (data & mask) mark(kLutronTick); // Send a 1 else space(kLutronTick); // Send a 0 space(kLutronGap); // Inter-message gap. } } #endif // SEND_LUTRON #if DECODE_LUTRON // Decode the supplied Lutron message. // // Args: // results: Ptr to the data to decode and where to store the decode result. // offset: The starting index to use when attempting to decode the raw data. // Typically/Defaults to kStartOffset. // nbits: The number of data bits to expect. Typically kLutronBits. // strict: Flag indicating if we should perform strict matching. // Returns: // boolean: True if it can decode it, false if it can't. // // Status: ALPHA / Untested. // // Notes: // // Ref: // https://github.com/crankyoldgit/IRremoteESP8266/issues/515 bool IRrecv::decodeLutron(decode_results *results, uint16_t offset, const uint16_t nbits, const bool strict) { // Technically the smallest number of entries for the smallest message is '1'. // i.e. All the bits set to 1, would produce a single huge mark signal. // So no minimum length check is required. if (strict && nbits != kLutronBits) return false; // Not strictly an Lutron message. uint64_t data = 0; int16_t bitsSoFar = -1; if (nbits > sizeof(data) * 8) return false; // To large to store the data. for (; bitsSoFar < nbits && offset < results->rawlen; offset++) { uint16_t entry = results->rawbuf[offset]; // It has to be large enough to qualify as a bit. if (!matchAtLeast(entry, kLutronTick, 0, kLutronDelta)) { DPRINTLN("Entry too small. Aborting."); return false; } // Keep reading bits of the same value until we run out. while (entry != 0 && matchAtLeast(entry, kLutronTick, 0, kLutronDelta)) { bitsSoFar++; DPRINT("Bit: "); DPRINT(bitsSoFar); if (offset % 2) { // Is Odd? data = (data << 1) + 1; // Append a '1'. DPRINTLN(" is a 1."); } else { // Is it Even? data <<= 1; // Append a '0'. DPRINTLN(" is a 0."); if (bitsSoFar == nbits && matchAtLeast(entry, kLutronGap)) break; // We've likely reached the end of a message. } // Remove a bit length from the current entry. entry = std::max(entry, (uint16_t)(kLutronTick / kRawTick)) - kLutronTick / kRawTick; } if (offset % 2 && !match(entry, kLutronDelta, 0, kLutronDelta)) { DPRINT("offset = "); DPRINTLN(offset); DPRINT("rawlen = "); DPRINTLN(results->rawlen); DPRINT("entry = "); DPRINTLN(entry); DPRINTLN("Odd Entry has too much left over. Aborting."); return false; // Too much left over to be a good value. Reject it. } if (offset % 2 == 0 && offset <= results->rawlen - 1 && !matchAtLeast(entry, kLutronDelta, 0, kLutronDelta)) { DPRINT("offset = "); DPRINTLN(offset); DPRINT("rawlen = "); DPRINTLN(results->rawlen); DPRINT("entry = "); DPRINTLN(entry); DPRINTLN("Entry has too much left over. Aborting."); return false; // Too much left over to be a good value. Reject it. } } // We got too many bits. if (bitsSoFar > nbits || bitsSoFar < 0) { DPRINTLN("Wrong number of bits found. Aborting."); return false; } // If we got less bits than we were expecting, we need to pad with zeros // until we get the correct number of bits. if (bitsSoFar < nbits) data <<= (nbits - bitsSoFar); // Success DPRINTLN("Lutron Success!"); results->decode_type = LUTRON; results->bits = bitsSoFar; results->value = data ^ (1ULL << nbits); // Mask off the initial '1'. results->address = 0; results->command = 0; return true; } #endif // DECODE_LUTRON