// Copyright 2017 David Conran #include "IRutils.h" #ifndef UNIT_TEST #include #endif #define __STDC_LIMIT_MACROS #include #include #ifndef ARDUINO #include #endif #include "IRrecv.h" #include "IRremoteESP8266.h" // Reverse the order of the requested least significant nr. of bits. // Args: // input: Bit pattern/integer to reverse. // nbits: Nr. of bits to reverse. // Returns: // The reversed bit pattern. uint64_t reverseBits(uint64_t input, uint16_t nbits) { if (nbits <= 1) return input; // Reversing <= 1 bits makes no change at all. // Cap the nr. of bits to rotate to the max nr. of bits in the input. nbits = std::min(nbits, (uint16_t) (sizeof(input) * 8)); uint64_t output = 0; for (uint16_t i = 0; i < nbits; i++) { output <<= 1; output |= (input & 1); input >>= 1; } // Merge any remaining unreversed bits back to the top of the reversed bits. return (input << nbits) | output; } // Convert a uint64_t (unsigned long long) to a string. // Arduino String/toInt/Serial.print() can't handle printing 64 bit values. // // Args: // input: The value to print // base: The output base. // Returns: // A string representation of the integer. // Note: Based on Arduino's Print::printNumber() #ifdef ARDUINO // Arduino's & C++'s string implementations can't co-exist. String uint64ToString(uint64_t input, uint8_t base) { String result = ""; #else std::string uint64ToString(uint64_t input, uint8_t base) { std::string result = ""; #endif // prevent issues if called with base <= 1 if (base < 2) base = 10; // Check we have a base that we can actually print. // i.e. [0-9A-Z] == 36 if (base > 36) base = 10; do { char c = input % base; input /= base; if (c < 10) c +='0'; else c += 'A' - 10; result = c + result; } while (input); return result; } #ifdef ARDUINO // Print a uint64_t/unsigned long long to the Serial port // Serial.print() can't handle printing long longs. (uint64_t) // // Args: // input: The value to print // base: The output base. void serialPrintUint64(uint64_t input, uint8_t base) { Serial.print(uint64ToString(input, base)); } #endif // Convert a protocol type (enum etc) to a human readable string. // Args: // protocol: Nr. (enum) of the protocol. // isRepeat: A flag indicating if it is a repeat message of the protocol. // Returns: // A string containing the protocol name. #ifdef ARDUINO // Arduino's & C++'s string implementations can't co-exist. String typeToString(const decode_type_t protocol, const bool isRepeat) { String result = ""; #else std::string typeToString(const decode_type_t protocol, const bool isRepeat) { std::string result = ""; #endif switch (protocol) { default: case UNKNOWN: result = "UNKNOWN"; break; case UNUSED: result = "UNUSED"; break; case AIWA_RC_T501: result = "AIWA_RC_T501"; break; case ARGO: result = "ARGO"; break; case CARRIER_AC: result = "CARRIER_AC"; break; case COOLIX: result = "COOLIX"; break; case DAIKIN: result = "DAIKIN"; break; case DENON: result = "DENON"; break; case DISH: result = "DISH"; break; case FUJITSU_AC: result = "FUJITSU_AC"; break; case GLOBALCACHE: result = "GLOBALCACHE"; break; case GREE: result = "GREE"; break; case HAIER_AC: result = "HAIER_AC"; break; case JVC: result = "JVC"; break; case KELVINATOR: result = "KELVINATOR"; break; case LG: result = "LG"; break; case LASERTAG: result = "LASERTAG"; break; case MAGIQUEST: result = "MAGIQUEST"; break; case MIDEA: result = "MIDEA"; break; case MITSUBISHI: result = "MITSUBISHI"; break; case MITSUBISHI_AC: result = "MITSUBISHI_AC"; break; case NEC: result = "NEC"; break; case NEC_LIKE: result = "NEC (non-strict)"; break; case NIKAI: result = "NIKAI"; break; case PANASONIC: result = "PANASONIC"; break; case PRONTO: result = "PRONTO"; break; case RAW: result = "RAW"; break; case RC5: result = "RC5"; break; case RC5X: result = "RC5X"; break; case RC6: result = "RC6"; break; case RCMM: result = "RCMM"; break; case SAMSUNG: result = "SAMSUNG"; break; case SANYO: result = "SANYO"; break; case SANYO_LC7461: result = "SANYO_LC7461"; break; case SHARP: result = "SHARP"; break; case SHERWOOD: result = "SHERWOOD"; break; case SONY: result = "SONY"; break; case TOSHIBA_AC: result = "TOSHIBA_AC"; break; case TROTEC: result = "TROTEC"; break; case WHYNTER: result = "WHYNTER"; break; } if (isRepeat) result += " (Repeat)"; return result; } // Does the given protocol use a complex state as part of the decode? bool hasACState(const decode_type_t protocol) { switch (protocol) { case DAIKIN: case FUJITSU_AC: case GREE: case HAIER_AC: case KELVINATOR: case TOSHIBA_AC: return true; default: return false; } } // Return the corrected length of a 'raw' format array structure // after over-large values are converted into multiple entries. // Args: // results: A ptr to a decode result. // Returns: // A uint16_t containing the length. uint16_t getCorrectedRawLength(const decode_results *results) { uint16_t extended_length = results->rawlen - 1; for (uint16_t i = 0; i < results->rawlen - 1; i++) { uint32_t usecs = results->rawbuf[i] * RAWTICK; // Add two extra entries for multiple larger than UINT16_MAX it is. extended_length += (usecs / (UINT16_MAX + 1)) * 2; } return extended_length; } // Return a string containing the key values of a decode_results structure // in a C/C++ code style format. #ifdef ARDUINO String resultToSourceCode(const decode_results *results) { String output = ""; #else std::string resultToSourceCode(const decode_results *results) { std::string output = ""; #endif // Start declaration output += "uint16_t "; // variable type output += "rawData["; // array name output += uint64ToString(getCorrectedRawLength(results), 10); // array size output += "] = {"; // Start declaration // Dump data for (uint16_t i = 1; i < results->rawlen; i++) { uint32_t usecs; for (usecs = results->rawbuf[i] * RAWTICK; usecs > UINT16_MAX; usecs -= UINT16_MAX) { output += uint64ToString(UINT16_MAX); if (i % 2) output += ", 0, "; else output += ", 0, "; } output += uint64ToString(usecs, 10); if (i < results->rawlen - 1) output += ", "; // ',' not needed on the last one if (i % 2 == 0) output += " "; // Extra if it was even. } // End declaration output +="};"; // Comment output += " // " + typeToString(results->decode_type, results->repeat); // Only display the value if the decode type doesn't have an A/C state. if (!hasACState(results->decode_type)) output += " " + uint64ToString(results->value, 16); output += "\n"; // Now dump "known" codes if (results->decode_type != UNKNOWN) { if (hasACState(results->decode_type)) { #if DECODE_AC uint16_t nbytes = results->bits / 8; output += "uint8_t state[" + uint64ToString(nbytes) + "] = {"; for (uint16_t i = 0; i < nbytes; i++) { output += "0x"; if (results->state[i] < 0x10) output += "0"; output += uint64ToString(results->state[i], 16); if (i < nbytes - 1) output += ", "; } output += "};\n"; #endif // DECODE_AC } else { // Simple protocols // Some protocols have an address &/or command. // NOTE: It will ignore the atypical case when a message has been // decoded but the address & the command are both 0. if (results->address > 0 || results->command > 0) { output += "uint32_t address = 0x" + uint64ToString(results->address, 16) + ";\n"; output += "uint32_t command = 0x" + uint64ToString(results->command, 16) + ";\n"; } // Most protocols have data output += "uint64_t data = 0x" + uint64ToString(results->value, 16) + ";\n"; } } return output; } // Dump out the decode_results structure. // #ifdef ARDUINO String resultToTimingInfo(const decode_results *results) { String output = ""; String value = ""; #else std::string resultToTimingInfo(const decode_results *results) { std::string output = ""; std::string value = ""; #endif output += "Raw Timing[" + uint64ToString(results->rawlen - 1, 10) + "]:\n"; for (uint16_t i = 1; i < results->rawlen; i++) { if (i % 2 == 0) output += "-"; // even else output += " +"; // odd value = uint64ToString(results->rawbuf[i] * RAWTICK); // Space pad the value till it is at least 6 chars long. while (value.length() < 6) value = " " + value; output += value; if (i < results->rawlen - 1) output += ", "; // ',' not needed for last one if (!(i % 8)) output += "\n"; // Newline every 8 entries. } output += "\n"; return output; } // Dump out the decode_results structure. // #ifdef ARDUINO String resultToHumanReadableBasic(const decode_results *results) { String output = ""; #else std::string resultToHumanReadableBasic(const decode_results *results) { std::string output = ""; #endif // Show Encoding standard output += "Encoding : " + typeToString(results->decode_type, results->repeat) + "\n"; // Show Code & length output += "Code : "; if (hasACState(results->decode_type)) { #if DECODE_AC for (uint16_t i = 0; results->bits > i * 8; i++) { if (results->state[i] < 0x10) output += "0"; // Zero pad output += uint64ToString(results->state[i], 16); } #endif // DECODE_AC } else { output += uint64ToString(results->value, 16); } output += " (" + uint64ToString(results->bits) + " bits)\n"; return output; } uint8_t sumBytes(uint8_t *start, const uint16_t length, const uint8_t init) { uint8_t checksum = init; uint8_t *ptr; for (ptr = start; ptr - start < length; ptr++) checksum += *ptr; return checksum; } uint64_t invertBits(const uint64_t data, const uint16_t nbits) { // No change if we are asked to invert no bits. if (nbits == 0) return data; uint64_t result = ~data; // If we are asked to invert all the bits or more than we have, it's simple. if (nbits >= sizeof(data) * 8) return result; // Mask off any unwanted bits and return the result. return (result & ((1ULL << nbits) - 1)); }