mirror of
https://github.com/crankyoldgit/IRremoteESP8266.git
synced 2026-01-12 00:05:10 +08:00
Add tool to convert protocol & code to raw timing info. (#1708)
* Add tool to convert protocol & code to raw timing info. Usage: tools/code_to_raw --protocol PROTOCOL_NAME --code <hexidecimal> [--bits 1-424] [--timinginfo] e.g. ``` # Convert an A/C code to rawData[] & Timinginfo tool/code_to_raw --protocol KELVINATOR --code 0x190B8050000000E0190B8070000010F0 --timinginfo # Convert a Samsung TV code to rawData[]. tools/code_to_raw --protocol SAMSUNG --code 0xE0E09966 # Convert a Sony 12 bit message to rawData[]. tools/code_to_raw --protocol Sony --code 0xf50 --bits 12 ``` * Add some tests for `code_to_raw`. * Update tools Makefile to be more dynamic For #1707 For #1703 Co-authored-by: Christian Nilsson <nikize@gmail.com>
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -40,6 +40,7 @@ tools/*.o
|
|||||||
tools/*.a
|
tools/*.a
|
||||||
tools/gc_decode
|
tools/gc_decode
|
||||||
tools/mode2_decode
|
tools/mode2_decode
|
||||||
|
tools/code_to_raw
|
||||||
|
|
||||||
.pioenvs
|
.pioenvs
|
||||||
.piolibdeps
|
.piolibdeps
|
||||||
|
|||||||
@@ -27,7 +27,9 @@ CPPFLAGS += -DUNIT_TEST -D_IR_LOCALE_=en-AU
|
|||||||
# Flags passed to the C++ compiler.
|
# Flags passed to the C++ compiler.
|
||||||
CXXFLAGS += -g -Wall -Wextra -pthread -std=gnu++11
|
CXXFLAGS += -g -Wall -Wextra -pthread -std=gnu++11
|
||||||
|
|
||||||
all : gc_decode mode2_decode
|
objects = $(patsubst %.cpp,%,$(wildcard *.cpp))
|
||||||
|
|
||||||
|
all : $(objects)
|
||||||
|
|
||||||
run_tests : all
|
run_tests : all
|
||||||
failed=""; \
|
failed=""; \
|
||||||
@@ -35,6 +37,10 @@ run_tests : all
|
|||||||
echo "RUNNING: $${py_unittest}"; \
|
echo "RUNNING: $${py_unittest}"; \
|
||||||
python3 ./$${py_unittest} || failed="$${failed} $${py_unittest}"; \
|
python3 ./$${py_unittest} || failed="$${failed} $${py_unittest}"; \
|
||||||
done; \
|
done; \
|
||||||
|
for shell_unittest in *_test.sh; do \
|
||||||
|
echo "RUNNING: $${shell_unittest}"; \
|
||||||
|
bash ./$${shell_unittest} || failed="$${failed} $${shell_unittest}"; \
|
||||||
|
done; \
|
||||||
if [ -n "$${failed}" ]; then \
|
if [ -n "$${failed}" ]; then \
|
||||||
echo "FAIL: :-( :-( Unit test(s)$${failed} failed! :-( :-("; exit 1; \
|
echo "FAIL: :-( :-( Unit test(s)$${failed} failed! :-( :-("; exit 1; \
|
||||||
else \
|
else \
|
||||||
@@ -46,7 +52,7 @@ run-% : all
|
|||||||
python3 ./$*.py;
|
python3 ./$*.py;
|
||||||
|
|
||||||
clean :
|
clean :
|
||||||
rm -f *.o *.pyc gc_decode mode2_decode
|
rm -f *.o *.pyc $(objects)
|
||||||
|
|
||||||
|
|
||||||
# Keep all intermediate files.
|
# 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
|
# new specific targets goes above this line
|
||||||
|
|
||||||
%_decode : $(COMMON_OBJ) %_decode.o
|
$(objects) : %: $(COMMON_OBJ) %.o
|
||||||
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@
|
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@
|
||||||
|
|
||||||
ir_%.o : $(USER_DIR)/ir_%.h $(USER_DIR)/ir_%.cpp $(COMMON_DEPS) $(GTEST_HEADERS)
|
ir_%.o : $(USER_DIR)/ir_%.h $(USER_DIR)/ir_%.cpp $(COMMON_DEPS) $(GTEST_HEADERS)
|
||||||
|
|||||||
148
tools/code_to_raw.cpp
Normal file
148
tools/code_to_raw.cpp
Normal file
@@ -0,0 +1,148 @@
|
|||||||
|
// Quick and dirty tool to convert a protocol's (hex) codes to raw timings.
|
||||||
|
// Copyright 2021 David Conran
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <string>
|
||||||
|
#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 <hexidecimal>"
|
||||||
|
<< " [--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;
|
||||||
|
}
|
||||||
65
tools/code_to_raw_test.sh
Executable file
65
tools/code_to_raw_test.sh
Executable file
@@ -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}"
|
||||||
Reference in New Issue
Block a user