mqtt-ir-remote/IRremoteESP8266/src/IRutils.cpp

332 lines
11 KiB
C++

// Copyright 2017 David Conran
#include "IRutils.h"
#ifndef UNIT_TEST
#include <Arduino.h>
#endif
#define __STDC_LIMIT_MACROS
#include <stdint.h>
#include <algorithm>
#ifndef ARDUINO
#include <string>
#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));
}