diff --git a/.gitignore b/.gitignore index ad6654a70..b58efd41d 100644 --- a/.gitignore +++ b/.gitignore @@ -40,6 +40,7 @@ tools/*.o tools/*.a tools/gc_decode tools/mode2_decode +tools/code_to_raw .pioenvs .piolibdeps diff --git a/tools/Makefile b/tools/Makefile index 15f192efd..bf65a996d 100644 --- a/tools/Makefile +++ b/tools/Makefile @@ -27,7 +27,9 @@ CPPFLAGS += -DUNIT_TEST -D_IR_LOCALE_=en-AU # Flags passed to the C++ compiler. CXXFLAGS += -g -Wall -Wextra -pthread -std=gnu++11 -all : gc_decode mode2_decode +objects = $(patsubst %.cpp,%,$(wildcard *.cpp)) + +all : $(objects) run_tests : all failed=""; \ @@ -35,6 +37,10 @@ run_tests : all echo "RUNNING: $${py_unittest}"; \ python3 ./$${py_unittest} || failed="$${failed} $${py_unittest}"; \ done; \ + for shell_unittest in *_test.sh; do \ + echo "RUNNING: $${shell_unittest}"; \ + bash ./$${shell_unittest} || failed="$${failed} $${shell_unittest}"; \ + done; \ if [ -n "$${failed}" ]; then \ echo "FAIL: :-( :-( Unit test(s)$${failed} failed! :-( :-("; exit 1; \ else \ @@ -46,7 +52,7 @@ run-% : all python3 ./$*.py; clean : - rm -f *.o *.pyc gc_decode mode2_decode + rm -f *.o *.pyc $(objects) # Keep all intermediate files. @@ -80,7 +86,7 @@ IRrecv.o : $(USER_DIR)/IRrecv.cpp $(USER_DIR)/IRrecv.h $(USER_DIR)/IRremoteESP82 # new specific targets goes above this line -%_decode : $(COMMON_OBJ) %_decode.o +$(objects) : %: $(COMMON_OBJ) %.o $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ ir_%.o : $(USER_DIR)/ir_%.h $(USER_DIR)/ir_%.cpp $(COMMON_DEPS) $(GTEST_HEADERS) diff --git a/tools/code_to_raw.cpp b/tools/code_to_raw.cpp new file mode 100644 index 000000000..7358f16da --- /dev/null +++ b/tools/code_to_raw.cpp @@ -0,0 +1,148 @@ +// Quick and dirty tool to convert a protocol's (hex) codes to raw timings. +// Copyright 2021 David Conran + +#include +#include +#include +#include +#include +#include "IRac.h" +#include "IRsend.h" +#include "IRsend_test.h" +#include "IRutils.h" + + +void usage_error(char *name) { + std::cerr << "Usage: " << name + << " --protocol PROTOCOL_NAME" + << " --code " + << " [--bits 1-" << kStateSizeMax * 8 << "]" + << " [--timinginfo]" + << std::endl; +} + +int main(int argc, char *argv[]) { + int argv_offset = 1; + int repeats = 0; + uint64_t code = 0; + uint8_t state[kStateSizeMax] = {0}; // All array elements are set to 0. + decode_type_t input_type = decode_type_t::UNKNOWN; + bool timinginfo = false; + + // Check the invocation/calling usage. + if (argc < 5 || argc > 8) { + usage_error(argv[0]); + return 1; + } + + if (strncmp("--protocol", argv[argv_offset], 11) == 0) { + argv_offset++; + input_type = strToDecodeType(argv[argv_offset]); + switch (input_type) { + // Unsupported types + case decode_type_t::UNUSED: + case decode_type_t::UNKNOWN: + case decode_type_t::GLOBALCACHE: + case decode_type_t::PRONTO: + case decode_type_t::RAW: + std::cerr << "The protocol specified is not supported by this program." + << std::endl; + return 1; + default: + break; + } + argv_offset++; + } + + uint16_t nbits = IRsend::defaultBits(input_type); + uint16_t stateSize = nbits / 8; + if (strncmp("--code", argv[argv_offset], 7) == 0) { + argv_offset++; + String hexstr = String(argv[argv_offset]); + uint64_t strOffset = 0; + if (hexstr.rfind("0x", 0) || hexstr.rfind("0X", 0)) strOffset = 2; + + // Calculate how many hexadecimal characters there are. + uint64_t hexstrlength = hexstr.length() - strOffset; + + // Ptr to the least significant byte of the resulting state for this + // protocol. + uint8_t *statePtr = &state[stateSize - 1]; + + // Convert the string into a state array of the correct length. + for (uint16_t i = 0; i < hexstrlength; i++) { + // Grab the next least sigificant hexadecimal digit from the string. + uint8_t c = tolower(hexstr[hexstrlength + strOffset - i - 1]); + if (isxdigit(c)) { + if (isdigit(c)) + c -= '0'; + else + c = c - 'a' + 10; + } else { + std::cerr << "Code " << argv[argv_offset] + << " contains non-hexidecimal characters." << std::endl; + return 3; + } + if (i % 2 == 1) { // Odd: Upper half of the byte. + *statePtr += (c << 4); + statePtr--; // Advance up to the next least significant byte of state. + } else { // Even: Lower half of the byte. + *statePtr = c; + } + } + if (!hasACState(input_type)) + code = std::stoull(argv[argv_offset], nullptr, 16); + argv_offset++; + } + + if (argc - argv_offset > 0 && strncmp("--bits", argv[argv_offset], 7) == 0) { + argv_offset++; + nbits = std::stoul(argv[argv_offset], nullptr, 10); + if (nbits == 0 && (nbits <= kStateSizeMax * 8)) { + std::cerr << "Nr. of bits " << argv[argv_offset] + << " is invalid." << std::endl; + return 1; + } + stateSize = nbits / 8; + argv_offset++; + } + + if (argc - argv_offset > 0 && + strncmp("--timinginfo", argv[argv_offset], 13) == 0) { + argv_offset++; + timinginfo = true; + } + + if (argc - argv_offset != 0) { + usage_error(argv[0]); + return 1; + } + + IRsendTest irsend(kGpioUnused); + IRrecv irrecv(kGpioUnused); + irsend.begin(); + irsend.reset(); + + if (hasACState(input_type)) // Is it larger than 64 bits? + irsend.send(input_type, state, stateSize); + else + irsend.send(input_type, code, nbits, repeats); + + irsend.makeDecodeResult(); + irrecv.decode(&irsend.capture); + + std::cout << "Code type: " << irsend.capture.decode_type << " (" + << typeToString(irsend.capture.decode_type) << ")" << std::endl + << "Code bits: " << irsend.capture.bits << std::endl; + if (hasACState(irsend.capture.decode_type)) { + String description = IRAcUtils::resultAcToString(&irsend.capture); + if (description.length()) { + std::cout << "Description: " << description.c_str() << std::endl; + } + } + + std::cout << std::endl << resultToSourceCode(&irsend.capture) << std::endl; + if (timinginfo) std::cout << resultToTimingInfo(&irsend.capture); + + return 0; +} diff --git a/tools/code_to_raw_test.sh b/tools/code_to_raw_test.sh new file mode 100755 index 000000000..fd587f2ad --- /dev/null +++ b/tools/code_to_raw_test.sh @@ -0,0 +1,65 @@ +#! /bin/bash +CODE_TO_RAW=./code_to_raw +if [[ ! -x ${CODE_TO_RAW} ]]; then + echo "'raw_to_code' failed to compile and produce an executable." + exit 1 +fi + +function unittest_success() +{ + COMMAND=$1 + EXPECTED="$2" + echo -n "Testing: \"${COMMAND}\" ..." + OUTPUT="$(${COMMAND})" + STATUS=$? + FAILURE="" + if [[ ${STATUS} -ne 0 ]]; then + FAILURE="Non-Zero Exit status: ${STATUS}. " + fi + if [[ "${OUTPUT}" != "${EXPECTED}" ]]; then + FAILURE="${FAILURE} Unexpected Output: \"${OUTPUT}\" != \"${EXPECTED}\"" + fi + if [[ -z ${FAILURE} ]]; then + echo " ok!" + return 0 + else + echo + echo "FAILED: ${FAILURE}" + return 1 + fi +} + +read -r -d '' OUT << EOM +Code type: 4 (SONY) +Code bits: 12 + +uint16_t rawData[78] = {2400, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 600, 600, 600, 600, 600, 24600, 2400, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 600, 600, 600, 600, 600, 24600, 2400, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 600, 600, 600, 600, 600, 24600 }; // SONY F50 +uint32_t address = 0x1; +uint32_t command = 0x2F; +uint64_t data = 0xF50; +EOM + +unittest_success "${CODE_TO_RAW} --protocol Sony --code 0xf50 --bits 12" "${OUT}" + +read -r -d '' OUT << EOM +Code type: 7 (SAMSUNG) +Code bits: 32 + +uint16_t rawData[68] = {4480, 4480, 560, 1680, 560, 1680, 560, 1680, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 1680, 560, 1680, 560, 1680, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 1680, 560, 560, 560, 560, 560, 1680, 560, 1680, 560, 560, 560, 560, 560, 1680, 560, 560, 560, 1680, 560, 1680, 560, 560, 560, 560, 560, 1680, 560, 1680, 560, 560, 560, 47040 }; // SAMSUNG E0E09966 +uint32_t address = 0x7; +uint32_t command = 0x99; +uint64_t data = 0xE0E09966; +EOM + +unittest_success "${CODE_TO_RAW} --protocol SAMSUNG --code 0xE0E09966" "${OUT}" + +read -r -d '' OUT << xEOMx +Code type: 18 (KELVINATOR) +Code bits: 128 +Description: Power: On, Mode: 1 (Cool), Temp: 27C, Fan: 1 (Low), Turbo: Off, Quiet: Off, XFan: On, Ion: Off, Light: Off, Swing(H): Off, Swing(V): Off + +uint16_t rawData[280] = {9010, 4504, 680, 1530, 680, 510, 680, 510, 680, 1530, 680, 1530, 680, 510, 680, 510, 680, 510, 680, 1530, 680, 1530, 680, 510, 680, 1530, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 1530, 680, 510, 680, 510, 680, 510, 680, 510, 680, 1530, 680, 510, 680, 1530, 680, 510, 680, 510, 680, 1530, 680, 510, 680, 19974, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 1530, 680, 1530, 680, 1530, 680, 39950, 9010, 4504, 680, 1530, 680, 510, 680, 510, 680, 1530, 680, 1530, 680, 510, 680, 510, 680, 510, 680, 1530, 680, 1530, 680, 510, 680, 1530, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 1530, 680, 510, 680, 510, 680, 510, 680, 510, 680, 1530, 680, 1530, 680, 1530, 680, 510, 680, 510, 680, 1530, 680, 510, 680, 19974, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 1530, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 1530, 680, 1530, 680, 1530, 680, 1530, 680, 39950 }; // KELVINATOR +uint8_t state[16] = {0x19, 0x0B, 0x80, 0x50, 0x00, 0x00, 0x00, 0xE0, 0x19, 0x0B, 0x80, 0x70, 0x00, 0x00, 0x10, 0xF0}; +xEOMx + +unittest_success "${CODE_TO_RAW} --protocol KELVINATOR --code 0x190B8050000000E0190B8070000010F0" "${OUT}"