Ir esp8266 lib update and code impacts (#235)
* integrate new IRremoteESP8266 lib 2.4.2 and add new protocols IR_RC6 IR_MITSUBISHI IR_GICABLE IR_MITSUBISHI2 IR_LASERTAG IR_CARRIER_AC IR_MIDEA IR_NIKAI IR_SHERWOOD IR_DENON IR_AIWA_RC_T501 IR_JVC
This commit is contained in:
1 parent
9b258723a8
commit
b12ee19979
130 files changed
+16548
-1693
No files matched your search
@@ -0,0 +1,175 @@
|
||||
# SYNOPSIS:
|
||||
#
|
||||
# make [all] - makes everything.
|
||||
# make clean - removes all files generated by make.
|
||||
|
||||
# Please tweak the following variable definitions as needed by your
|
||||
# project, except GTEST_HEADERS, which you can use in your own targets
|
||||
# but shouldn't modify.
|
||||
|
||||
|
||||
# Where to find user code.
|
||||
USER_DIR = ../src
|
||||
|
||||
# Where to find test code.
|
||||
TEST_DIR = ../test
|
||||
|
||||
# Flags passed to the preprocessor.
|
||||
# Set Google Test's header directory as a system directory, such that
|
||||
# the compiler doesn't generate warnings in Google Test headers.
|
||||
CPPFLAGS += -DUNIT_TEST
|
||||
|
||||
# Flags passed to the C++ compiler.
|
||||
CXXFLAGS += -g -Wall -Wextra -pthread
|
||||
|
||||
all : gc_decode
|
||||
|
||||
run_tests : all
|
||||
failed=""; \
|
||||
for py_unittest in *_test.py; do \
|
||||
echo "RUNNING: $${py_unittest}"; \
|
||||
python ./$${py_unittest} || failed="$${failed} $${py_unittest}"; \
|
||||
done; \
|
||||
if [ -n "$${failed}" ]; then \
|
||||
echo "FAIL: :-( :-( Unit test(s)$${failed} failed! :-( :-("; exit 1; \
|
||||
else \
|
||||
echo "PASS: \o/ \o/ All unit tests passed. \o/ \o/"; \
|
||||
fi
|
||||
|
||||
clean :
|
||||
rm -f *.o *.pyc gc_decode
|
||||
|
||||
|
||||
# All the IR protocol object files.
|
||||
PROTOCOLS = ir_NEC.o ir_Sony.o ir_Samsung.o ir_JVC.o ir_RCMM.o ir_RC5_RC6.o \
|
||||
ir_LG.o ir_Mitsubishi.o ir_Fujitsu.o ir_Sharp.o ir_Sanyo.o \
|
||||
ir_Denon.o ir_Dish.o ir_Panasonic.o ir_Whynter.o ir_Coolix.o \
|
||||
ir_Aiwa.o ir_Sherwood.o ir_Kelvinator.o ir_Daikin.o ir_Gree.o \
|
||||
ir_Pronto.o ir_GlobalCache.o ir_Nikai.o ir_Toshiba.o ir_Midea.o \
|
||||
ir_Magiquest.o ir_Lasertag.o ir_Carrier.o ir_Haier.o ir_Hitachi.o \
|
||||
ir_GICable.o
|
||||
|
||||
# Common object files
|
||||
COMMON_OBJ = IRutils.o IRtimer.o IRsend.o IRrecv.o $(PROTOCOLS)
|
||||
|
||||
# Common dependencies
|
||||
COMMON_DEPS = $(USER_DIR)/IRrecv.h $(USER_DIR)/IRsend.h $(USER_DIR)/IRtimer.h \
|
||||
$(USER_DIR)/IRutils.h $(USER_DIR)/IRremoteESP8266.h
|
||||
# Common test dependencies
|
||||
COMMON_TEST_DEPS = $(COMMON_DEPS) $(TEST_DIR)/IRsend_test.h
|
||||
|
||||
gc_decode.o : gc_decode.cpp $(COMMON_TEST_DEPS) $(GTEST_HEADERS)
|
||||
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -I$(USER_DIR) -I$(TEST_DIR) -c gc_decode.cpp
|
||||
|
||||
gc_decode : $(COMMON_OBJ) gc_decode.o
|
||||
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@
|
||||
|
||||
|
||||
IRutils.o : $(USER_DIR)/IRutils.cpp $(USER_DIR)/IRutils.h $(USER_DIR)/IRremoteESP8266.h
|
||||
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/IRutils.cpp
|
||||
|
||||
IRtimer.o : $(USER_DIR)/IRtimer.cpp $(USER_DIR)/IRtimer.h
|
||||
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/IRtimer.cpp
|
||||
|
||||
IRsend.o : $(USER_DIR)/IRsend.cpp $(USER_DIR)/IRsend.h $(USER_DIR)/IRremoteESP8266.h
|
||||
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/IRsend.cpp
|
||||
|
||||
IRrecv.o : $(USER_DIR)/IRrecv.cpp $(USER_DIR)/IRrecv.h $(USER_DIR)/IRremoteESP8266.h $(GTEST_HEADERS)
|
||||
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/IRrecv.cpp
|
||||
|
||||
|
||||
ir_NEC.o : $(USER_DIR)/ir_NEC.cpp $(COMMON_DEPS)
|
||||
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_NEC.cpp
|
||||
|
||||
ir_GlobalCache.o : $(USER_DIR)/ir_GlobalCache.cpp $(COMMON_DEPS)
|
||||
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_GlobalCache.cpp
|
||||
|
||||
ir_Sherwood.o : $(USER_DIR)/ir_Sherwood.cpp $(COMMON_DEPS)
|
||||
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Sherwood.cpp
|
||||
|
||||
ir_Sony.o : $(USER_DIR)/ir_Sony.cpp $(COMMON_DEPS)
|
||||
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Sony.cpp
|
||||
|
||||
ir_Samsung.o : $(USER_DIR)/ir_Samsung.cpp $(COMMON_DEPS)
|
||||
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Samsung.cpp
|
||||
|
||||
ir_Kelvinator.o : $(USER_DIR)/ir_Kelvinator.cpp $(USER_DIR)/ir_Kelvinator.h $(COMMON_DEPS)
|
||||
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Kelvinator.cpp
|
||||
|
||||
ir_JVC.o : $(USER_DIR)/ir_JVC.cpp $(COMMON_DEPS)
|
||||
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_JVC.cpp
|
||||
|
||||
ir_RCMM.o : $(USER_DIR)/ir_RCMM.cpp $(COMMON_DEPS)
|
||||
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_RCMM.cpp
|
||||
|
||||
ir_LG.o : $(USER_DIR)/ir_LG.h $(USER_DIR)/ir_LG.cpp $(COMMON_DEPS)
|
||||
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_LG.cpp
|
||||
|
||||
ir_Mitsubishi.o : $(USER_DIR)/ir_Mitsubishi.h $(USER_DIR)/ir_Mitsubishi.cpp $(COMMON_DEPS)
|
||||
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Mitsubishi.cpp
|
||||
|
||||
ir_Fujitsu.o : $(USER_DIR)/ir_Fujitsu.h $(USER_DIR)/ir_Fujitsu.cpp $(COMMON_DEPS)
|
||||
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Fujitsu.cpp
|
||||
|
||||
ir_Sharp.o : $(USER_DIR)/ir_Sharp.cpp $(COMMON_DEPS)
|
||||
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Sharp.cpp
|
||||
|
||||
ir_RC5_RC6.o : $(USER_DIR)/ir_RC5_RC6.cpp $(COMMON_DEPS)
|
||||
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_RC5_RC6.cpp
|
||||
|
||||
ir_Panasonic.o : $(USER_DIR)/ir_Panasonic.cpp $(COMMON_DEPS)
|
||||
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Panasonic.cpp
|
||||
|
||||
ir_Dish.o : $(USER_DIR)/ir_Dish.cpp $(COMMON_DEPS)
|
||||
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Dish.cpp
|
||||
|
||||
ir_Whynter.o : $(USER_DIR)/ir_Whynter.cpp $(COMMON_DEPS)
|
||||
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Whynter.cpp
|
||||
|
||||
ir_Coolix.o : $(USER_DIR)/ir_Coolix.cpp $(COMMON_DEPS)
|
||||
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Coolix.cpp
|
||||
|
||||
ir_Aiwa.o : $(USER_DIR)/ir_Aiwa.cpp $(COMMON_DEPS)
|
||||
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Aiwa.cpp
|
||||
|
||||
ir_Denon.o : $(USER_DIR)/ir_Denon.cpp $(COMMON_DEPS)
|
||||
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Denon.cpp
|
||||
|
||||
ir_Sanyo.o : $(USER_DIR)/ir_Sanyo.cpp $(COMMON_DEPS)
|
||||
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Sanyo.cpp
|
||||
|
||||
ir_Daikin.o : $(USER_DIR)/ir_Daikin.cpp $(USER_DIR)/ir_Daikin.h $(COMMON_DEPS)
|
||||
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Daikin.cpp
|
||||
|
||||
ir_Gree.o : $(USER_DIR)/ir_Gree.cpp $(GTEST_HEADERS)
|
||||
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Gree.cpp
|
||||
|
||||
ir_Pronto.o : $(USER_DIR)/ir_Pronto.cpp $(GTEST_HEADERS)
|
||||
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Pronto.cpp
|
||||
|
||||
ir_Nikai.o : $(USER_DIR)/ir_Nikai.cpp $(GTEST_HEADERS)
|
||||
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Nikai.cpp
|
||||
|
||||
ir_Toshiba.o : $(USER_DIR)/ir_Toshiba.h $(USER_DIR)/ir_Toshiba.cpp $(COMMON_DEPS)
|
||||
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Toshiba.cpp
|
||||
|
||||
ir_Midea.o : $(USER_DIR)/ir_Midea.cpp $(COMMON_DEPS)
|
||||
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Midea.cpp
|
||||
|
||||
ir_Magiquest.o : $(USER_DIR)/ir_Magiquest.cpp $(GTEST_HEADERS)
|
||||
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Magiquest.cpp
|
||||
|
||||
ir_Lasertag.o : $(USER_DIR)/ir_Lasertag.cpp $(USER_DIR)/ir_RC5_RC6.cpp $(COMMON_DEPS)
|
||||
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Lasertag.cpp
|
||||
|
||||
ir_Carrier.o : $(USER_DIR)/ir_Carrier.cpp $(GTEST_HEADERS)
|
||||
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Carrier.cpp
|
||||
|
||||
ir_Haier.o : $(USER_DIR)/ir_Haier.cpp $(USER_DIR)/ir_Haier.h $(GTEST_HEADERS)
|
||||
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Haier.cpp
|
||||
|
||||
ir_Hitachi.o : $(USER_DIR)/ir_Hitachi.cpp $(GTEST_HEADERS)
|
||||
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Hitachi.cpp
|
||||
|
||||
ir_GICable.o : $(USER_DIR)/ir_GICable.cpp $(GTEST_HEADERS)
|
||||
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_GICable.cpp
|
||||
@@ -0,0 +1,53 @@
|
||||
#!/bin/bash
|
||||
# Convert IRremoteESP8266's rawData output into Global Cache format.
|
||||
|
||||
function isDigits()
|
||||
{
|
||||
[[ "$1" =~ ^[0-9]+$ ]]
|
||||
}
|
||||
|
||||
function usage()
|
||||
{
|
||||
cat << EOF
|
||||
Usage: $0 Frequency_in_Hz
|
||||
Reads an IRremoteESP8266 rawData declaration from STDIN and converts it to
|
||||
GlobalCache format.
|
||||
e.g.
|
||||
uint16_t rawbuf[37] = {
|
||||
7930, 3952, 494, 1482, 520, 1482, 494, 1508,
|
||||
494, 520, 494, 1482, 494, 520, 494, 1482,
|
||||
494, 1482, 494, 3978, 494, 520, 494, 520,
|
||||
494, 520, 494, 520, 520, 520, 494, 520,
|
||||
494, 520, 494, 520, 494};
|
||||
EOF
|
||||
exit 1
|
||||
}
|
||||
|
||||
# We need a frequency argument.
|
||||
if [[ $# -ne 1 ]]; then
|
||||
usage
|
||||
fi
|
||||
HZ="$1"
|
||||
# HZ must be a positive number
|
||||
if ! isDigits "${HZ}"; then
|
||||
usage
|
||||
fi
|
||||
# HZ must not be zero.
|
||||
if [[ ${HZ} == 0 ]]; then
|
||||
usage
|
||||
fi
|
||||
|
||||
|
||||
PERIOD_OFFSET=0
|
||||
period=$((((1000000 + (${HZ} / 2)) / ${HZ}) + ${PERIOD_OFFSET}))
|
||||
result="${HZ},1,1"
|
||||
while read line; do
|
||||
# Quick and Dirty Removal of any array declaration syntax, and any commas.
|
||||
line="$(echo ${line} | sed 's/uint.*{//i' | sed 's/,//g' | sed 's/};.*//g')"
|
||||
for msecs in ${line}; do
|
||||
if isDigits "${msecs}"; then
|
||||
result="${result},$((${msecs} / ${period}))"
|
||||
fi
|
||||
done
|
||||
done
|
||||
echo "GlobalCache code = \"${result}\""
|
||||
@@ -0,0 +1,406 @@
|
||||
#!/usr/bin/python
|
||||
"""Attempt an automatic analysis of IRremoteESP8266's Raw data output.
|
||||
Makes suggestions on key values and tried to break down the message
|
||||
into likely chuncks."""
|
||||
#
|
||||
# Copyright 2018 David Conran
|
||||
import argparse
|
||||
import sys
|
||||
|
||||
|
||||
class RawIRMessage(object):
|
||||
"""Basic analyse functions & structure for raw IR messages."""
|
||||
|
||||
# pylint: disable=too-many-instance-attributes
|
||||
|
||||
def __init__(self, margin, timings, output=sys.stdout, verbose=True):
|
||||
self.hdr_mark = None
|
||||
self.hdr_space = None
|
||||
self.bit_mark = None
|
||||
self.zero_space = None
|
||||
self.one_space = None
|
||||
self.gaps = []
|
||||
self.margin = margin
|
||||
self.marks = []
|
||||
self.spaces = []
|
||||
self.output = output
|
||||
self.verbose = verbose
|
||||
if len(timings) <= 3:
|
||||
raise ValueError("Too few message timings supplied.")
|
||||
self.timings = timings
|
||||
self._generate_timing_candidates()
|
||||
self._calc_values()
|
||||
|
||||
def _generate_timing_candidates(self):
|
||||
"""Determine the likely values from the given data."""
|
||||
count = 0
|
||||
for usecs in self.timings:
|
||||
count = count + 1
|
||||
if count % 2:
|
||||
self.marks.append(usecs)
|
||||
else:
|
||||
self.spaces.append(usecs)
|
||||
self.marks = self._reduce_list(self.marks)
|
||||
self.spaces = self._reduce_list(self.spaces)
|
||||
|
||||
def _reduce_list(self, items):
|
||||
"""Reduce the list of numbers into buckets that are atleast margin apart."""
|
||||
result = []
|
||||
last = -1
|
||||
for item in sorted(items, reverse=True):
|
||||
if last == -1 or item < last - self.margin:
|
||||
result.append(item)
|
||||
last = item
|
||||
return result
|
||||
|
||||
def _usec_compare(self, seen, expected):
|
||||
"""Compare two usec values and see if they match within a
|
||||
subtrative margin."""
|
||||
return seen <= expected and seen > expected - self.margin
|
||||
|
||||
def _usec_compares(self, usecs, expecteds):
|
||||
"""Compare a usec value to a list of values and return True
|
||||
if they are within a subtractive margin."""
|
||||
for expected in expecteds:
|
||||
if self._usec_compare(usecs, expected):
|
||||
return True
|
||||
return False
|
||||
|
||||
def display_binary(self, binary_str):
|
||||
"""Display common representations of the suppied binary string."""
|
||||
num = int(binary_str, 2)
|
||||
bits = len(binary_str)
|
||||
rev_binary_str = binary_str[::-1]
|
||||
rev_num = int(rev_binary_str, 2)
|
||||
self.output.write("\n Bits: %d\n"
|
||||
" Hex: %s (MSB first)\n"
|
||||
" %s (LSB first)\n"
|
||||
" Dec: %s (MSB first)\n"
|
||||
" %s (LSB first)\n"
|
||||
" Bin: 0b%s (MSB first)\n"
|
||||
" 0b%s (LSB first)\n" %
|
||||
(bits, "0x{0:0{1}X}".format(num, bits / 4),
|
||||
"0x{0:0{1}X}".format(rev_num, bits / 4), num, rev_num,
|
||||
binary_str, rev_binary_str))
|
||||
|
||||
def add_data_code(self, bin_str):
|
||||
"""Add the common "data" sequence of code to send the bulk of a message."""
|
||||
# pylint: disable=no-self-use
|
||||
code = []
|
||||
code.append(" // Data")
|
||||
code.append(" // e.g. data = 0x%X, nbits = %d" % (int(bin_str, 2),
|
||||
len(bin_str)))
|
||||
code.append(" sendData(BIT_MARK, ONE_SPACE, BIT_MARK, ZERO_SPACE, data, "
|
||||
"nbits, true);")
|
||||
code.append(" // Footer")
|
||||
code.append(" mark(BIT_MARK);")
|
||||
return code
|
||||
|
||||
def _calc_values(self):
|
||||
"""Calculate the values which describe the standard timings
|
||||
for the protocol."""
|
||||
if self.verbose:
|
||||
self.output.write("Potential Mark Candidates:\n"
|
||||
"%s\n"
|
||||
"Potential Space Candidates:\n"
|
||||
"%s\n" % (str(self.marks), str(self.spaces)))
|
||||
# Largest mark is likely the HDR_MARK
|
||||
self.hdr_mark = self.marks[0]
|
||||
# The bit mark is likely to be the smallest mark.
|
||||
self.bit_mark = self.marks[-1]
|
||||
|
||||
if self.is_space_encoded() and len(self.spaces) >= 3:
|
||||
if self.verbose and len(self.marks) > 2:
|
||||
self.output.write("DANGER: Unexpected and unused mark timings!")
|
||||
# We should have 3 space candidates at least.
|
||||
# They should be: zero_space (smallest), one_space, & hdr_space (largest)
|
||||
spaces = list(self.spaces)
|
||||
self.zero_space = spaces.pop()
|
||||
self.one_space = spaces.pop()
|
||||
self.hdr_space = spaces.pop()
|
||||
# Rest are probably message gaps
|
||||
self.gaps = spaces
|
||||
|
||||
def is_space_encoded(self):
|
||||
"""Make an educated guess if the message is space encoded."""
|
||||
return len(self.spaces) > len(self.marks)
|
||||
|
||||
def is_hdr_mark(self, usec):
|
||||
"""Is usec the header mark?"""
|
||||
return self._usec_compare(usec, self.hdr_mark)
|
||||
|
||||
def is_hdr_space(self, usec):
|
||||
"""Is usec the header space?"""
|
||||
return self._usec_compare(usec, self.hdr_space)
|
||||
|
||||
def is_bit_mark(self, usec):
|
||||
"""Is usec the bit mark?"""
|
||||
return self._usec_compare(usec, self.bit_mark)
|
||||
|
||||
def is_one_space(self, usec):
|
||||
"""Is usec the one space?"""
|
||||
return self._usec_compare(usec, self.one_space)
|
||||
|
||||
def is_zero_space(self, usec):
|
||||
"""Is usec the zero_space?"""
|
||||
return self._usec_compare(usec, self.zero_space)
|
||||
|
||||
def is_gap(self, usec):
|
||||
"""Is usec the a space gap?"""
|
||||
return self._usec_compares(usec, self.gaps)
|
||||
|
||||
|
||||
def add_bit(so_far, bit, output=sys.stdout):
|
||||
"""Add a bit to the end of the bits collected so far. """
|
||||
if bit == "reset":
|
||||
return ""
|
||||
output.write(str(bit)) # This effectively displays in LSB first order.
|
||||
return so_far + str(bit) # Storing it in MSB first order.
|
||||
|
||||
|
||||
def convert_rawdata(data_str):
|
||||
"""Parse a C++ rawdata declaration into a list of values."""
|
||||
start = data_str.find('{')
|
||||
end = data_str.find('}')
|
||||
if end == -1:
|
||||
end = len(data_str)
|
||||
if start > end:
|
||||
raise ValueError("Raw Data not parsible due to parentheses placement.")
|
||||
data_str = data_str[start + 1:end]
|
||||
results = []
|
||||
for timing in [x.strip() for x in data_str.split(',')]:
|
||||
try:
|
||||
results.append(int(timing))
|
||||
except ValueError:
|
||||
raise ValueError(
|
||||
"Raw Data contains a non-numeric value of '%s'." % timing)
|
||||
return results
|
||||
|
||||
|
||||
def dump_constants(message, defines, output=sys.stdout):
|
||||
"""Dump the key constants and generate the C++ #defines."""
|
||||
output.write("Guessing key value:\n"
|
||||
"HDR_MARK = %d\n"
|
||||
"HDR_SPACE = %d\n"
|
||||
"BIT_MARK = %d\n"
|
||||
"ONE_SPACE = %d\n"
|
||||
"ZERO_SPACE = %d\n" %
|
||||
(message.hdr_mark, message.hdr_space, message.bit_mark,
|
||||
message.one_space, message.zero_space))
|
||||
defines.append("#define HDR_MARK %dU" % message.hdr_mark)
|
||||
defines.append("#define BIT_MARK %dU" % message.bit_mark)
|
||||
defines.append("#define HDR_SPACE %dU" % message.hdr_space)
|
||||
defines.append("#define ONE_SPACE %dU" % message.one_space)
|
||||
defines.append("#define ZERO_SPACE %dU" % message.zero_space)
|
||||
|
||||
if len(message.gaps) == 1:
|
||||
output.write("SPACE_GAP = %d\n" % message.gaps[0])
|
||||
defines.append("#define SPACE_GAP = %dU" % message.gaps[0])
|
||||
else:
|
||||
count = 0
|
||||
for gap in message.gaps:
|
||||
# We probably (still) have a gap in the protocol.
|
||||
count = count + 1
|
||||
output.write("SPACE_GAP%d = %d\n" % (count, gap))
|
||||
defines.append("#define SPACE_GAP%d = %dU" % (count, gap))
|
||||
|
||||
|
||||
def parse_and_report(rawdata_str, margin, gen_code=False, output=sys.stdout):
|
||||
"""Analyse the rawdata c++ definition of a IR message."""
|
||||
defines = []
|
||||
function_code = []
|
||||
|
||||
# Parse the input.
|
||||
rawdata = convert_rawdata(rawdata_str)
|
||||
|
||||
output.write("Found %d timing entries.\n" % len(rawdata))
|
||||
|
||||
message = RawIRMessage(margin, rawdata, output)
|
||||
output.write("\nGuessing encoding type:\n")
|
||||
if message.is_space_encoded():
|
||||
output.write("Looks like it uses space encoding. Yay!\n\n")
|
||||
dump_constants(message, defines, output)
|
||||
else:
|
||||
output.write("Sorry, it looks like it is Mark encoded. "
|
||||
"I can't do that yet. Exiting.\n")
|
||||
sys.exit(1)
|
||||
total_bits = decode_data(message, defines, function_code, output)
|
||||
if gen_code:
|
||||
generate_irsend_code(defines, function_code, total_bits, output)
|
||||
|
||||
|
||||
def decode_data(message, defines, function_code, output=sys.stdout):
|
||||
"""Decode the data sequence with the given values in mind."""
|
||||
# pylint: disable=too-many-branches,too-many-statements
|
||||
|
||||
# Now we have likely candidates for the key values, go through the original
|
||||
# sequence and break it up and indicate accordingly.
|
||||
|
||||
output.write("\nDecoding protocol based on analysis so far:\n\n")
|
||||
state = ""
|
||||
count = 1
|
||||
total_bits = ""
|
||||
binary_value = add_bit("", "reset")
|
||||
|
||||
function_code.extend([
|
||||
"// Function should be safe up to 64 bits.",
|
||||
"void IRsend::sendXYZ(const uint64_t data, const uint16_t"
|
||||
" nbits, const uint16_t repeat) {",
|
||||
" enableIROut(38); // A guess. Most common frequency.",
|
||||
" for (uint16_t r = 0; r <= repeat; r++) {"
|
||||
])
|
||||
|
||||
for usec in message.timings:
|
||||
if (message.is_hdr_mark(usec) and count % 2 and
|
||||
not message.is_bit_mark(usec)):
|
||||
state = "HM"
|
||||
if binary_value:
|
||||
message.display_binary(binary_value)
|
||||
total_bits = total_bits + binary_value
|
||||
output.write(state)
|
||||
binary_value = add_bit(binary_value, "reset")
|
||||
output.write("HDR_MARK+")
|
||||
function_code.extend([" // Header", " mark(HDR_MARK);"])
|
||||
elif (message.is_hdr_space(usec) and not message.is_one_space(usec)):
|
||||
if state != "HM":
|
||||
if binary_value:
|
||||
message.display_binary(binary_value)
|
||||
total_bits = total_bits + binary_value
|
||||
function_code.extend(message.add_data_code(binary_value))
|
||||
binary_value = add_bit(binary_value, "reset")
|
||||
output.write("UNEXPECTED->")
|
||||
state = "HS"
|
||||
output.write("HDR_SPACE+")
|
||||
function_code.append(" space(HDR_SPACE);")
|
||||
elif message.is_bit_mark(usec) and count % 2:
|
||||
if state != "HS" and state != "BS":
|
||||
output.write("BIT_MARK(UNEXPECTED)")
|
||||
state = "BM"
|
||||
elif message.is_zero_space(usec):
|
||||
if state != "BM":
|
||||
output.write("ZERO_SPACE(UNEXPECTED)")
|
||||
state = "BS"
|
||||
binary_value = add_bit(binary_value, 0, output)
|
||||
elif message.is_one_space(usec):
|
||||
if state != "BM":
|
||||
output.write("ONE_SPACE(UNEXPECTED)")
|
||||
state = "BS"
|
||||
binary_value = add_bit(binary_value, 1, output)
|
||||
elif message.is_gap(usec):
|
||||
if state != "BM":
|
||||
output.write("UNEXPECTED->")
|
||||
state = "GS"
|
||||
output.write(" GAP(%d)" % usec)
|
||||
message.display_binary(binary_value)
|
||||
function_code.extend(message.add_data_code(binary_value))
|
||||
function_code.append(" space(SPACE_GAP);")
|
||||
total_bits = total_bits + binary_value
|
||||
binary_value = add_bit(binary_value, "reset")
|
||||
else:
|
||||
output.write("UNKNOWN(%d)" % usec)
|
||||
state = "UNK"
|
||||
count = count + 1
|
||||
message.display_binary(binary_value)
|
||||
function_code.extend(message.add_data_code(binary_value))
|
||||
function_code.extend([
|
||||
" space(100000); // A 100% made up guess of the gap"
|
||||
" between messages.", " }", "}"
|
||||
])
|
||||
|
||||
total_bits = total_bits + binary_value
|
||||
output.write("Total Nr. of suspected bits: %d\n" % len(total_bits))
|
||||
defines.append("#define XYZ_BITS %dU" % len(total_bits))
|
||||
if len(total_bits) > 64:
|
||||
defines.append("#define XYZ_STATE_LENGTH %dU" % (len(total_bits) / 8))
|
||||
return total_bits
|
||||
|
||||
|
||||
def generate_irsend_code(defines, normal, bits_str, output=sys.stdout):
|
||||
"""Output the estimated C++ code to reproduce the IR message."""
|
||||
output.write("\nGenerating a VERY rough code outline:\n\n"
|
||||
"// WARNING: This probably isn't directly usable."
|
||||
" It's a guide only.\n")
|
||||
for line in defines:
|
||||
output.write("%s\n" % line)
|
||||
|
||||
if len(bits_str) > 64: # Will it fit in a uint64_t?
|
||||
output.write("// DANGER: More than 64 bits detected. A uint64_t for "
|
||||
"'data' won't work!\n")
|
||||
# Display the "normal" version's code incase there are some
|
||||
# oddities in it.
|
||||
for line in normal:
|
||||
output.write("%s\n" % line)
|
||||
|
||||
if len(bits_str) > 64: # Will it fit in a uint64_t?
|
||||
output.write("\n\n// Alternative >64 bit Function\n"
|
||||
"void IRsend::sendXYZ(uint8_t data[], uint16_t nbytes,"
|
||||
" uint16_t repeat) {\n"
|
||||
" // nbytes should typically be XYZ_STATE_LENGTH\n"
|
||||
" // data should typically be:\n"
|
||||
" // uint8_t data[XYZ_STATE_LENGTH] = {0x%s};\n"
|
||||
" // data[] is assumed to be in MSB order for this code.\n"
|
||||
" for (uint16_t r = 0; r <= repeat; r++) {\n"
|
||||
" sendGeneric(HDR_MARK, HDR_SPACE,\n"
|
||||
" BIT_MARK, ONE_SPACE,\n"
|
||||
" BIT_MARK, ZERO_SPACE,\n"
|
||||
" BIT_MARK\n"
|
||||
" 100000, // 100%% made-up guess at the"
|
||||
" message gap.\n"
|
||||
" data, nbytes,\n"
|
||||
" 38000, // Complete guess of the modulation"
|
||||
" frequency.\n"
|
||||
" true, 0, 50);\n"
|
||||
"}\n" % ", 0x".join("%02X" % int(bits_str[i:i + 8], 2)
|
||||
for i in range(0, len(bits_str), 8)))
|
||||
|
||||
|
||||
def main():
|
||||
"""Parse the commandline arguments and call the method."""
|
||||
arg_parser = argparse.ArgumentParser(
|
||||
description="Read an IRremoteESP8266 rawData declaration and tries to "
|
||||
"analyse it.",
|
||||
formatter_class=argparse.ArgumentDefaultsHelpFormatter)
|
||||
arg_parser.add_argument(
|
||||
"-g",
|
||||
"--code",
|
||||
action="store_true",
|
||||
default=False,
|
||||
dest="gen_code",
|
||||
help="Generate a C++ code outline to aid making an IRsend function.")
|
||||
arg_group = arg_parser.add_mutually_exclusive_group(required=True)
|
||||
arg_group.add_argument(
|
||||
"rawdata",
|
||||
help="A rawData line from IRrecvDumpV2. e.g. 'uint16_t rawbuf[37] = {"
|
||||
"7930, 3952, 494, 1482, 520, 1482, 494, 1508, 494, 520, 494, 1482, 494, "
|
||||
"520, 494, 1482, 494, 1482, 494, 3978, 494, 520, 494, 520, 494, 520, "
|
||||
"494, 520, 520, 520, 494, 520, 494, 520, 494, 520, 494};'",
|
||||
nargs="?")
|
||||
arg_group.add_argument(
|
||||
"-f", "--file", help="Read in a rawData line from the file.")
|
||||
arg_parser.add_argument(
|
||||
"-r",
|
||||
"--range",
|
||||
type=int,
|
||||
help="Max number of micro-seconds difference between values to consider"
|
||||
" it the same value.",
|
||||
dest="margin",
|
||||
default=200)
|
||||
arg_group.add_argument(
|
||||
"--stdin",
|
||||
help="Read in a rawData line from STDIN.",
|
||||
action="store_true",
|
||||
default=False)
|
||||
arg_options = arg_parser.parse_args()
|
||||
|
||||
if arg_options.stdin:
|
||||
data = sys.stdin.read()
|
||||
elif arg_options.file:
|
||||
with open(arg_options.file) as input_file:
|
||||
data = input_file.read()
|
||||
else:
|
||||
data = arg_options.rawdata
|
||||
parse_and_report(data, arg_options.margin, arg_options.gen_code)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -0,0 +1,288 @@
|
||||
#!/usr/bin/python
|
||||
"""Unit tests for auto_analyse_raw_data.py"""
|
||||
import StringIO
|
||||
import unittest
|
||||
import auto_analyse_raw_data as analyse
|
||||
|
||||
|
||||
class TestRawIRMessage(unittest.TestCase):
|
||||
"""Unit tests for the RawIRMessage class."""
|
||||
|
||||
def test_display_binary(self):
|
||||
"""Test the display_binary() method."""
|
||||
output = StringIO.StringIO()
|
||||
message = analyse.RawIRMessage(100, [8000, 4000, 500, 500, 500], output,
|
||||
False)
|
||||
self.assertEqual(output.getvalue(), '')
|
||||
message.display_binary("10101010")
|
||||
message.display_binary("0000000000000000")
|
||||
message.display_binary("00010010001101000101011001111000")
|
||||
self.assertEqual(output.getvalue(), '\n'
|
||||
' Bits: 8\n'
|
||||
' Hex: 0xAA (MSB first)\n'
|
||||
' 0x55 (LSB first)\n'
|
||||
' Dec: 170 (MSB first)\n'
|
||||
' 85 (LSB first)\n'
|
||||
' Bin: 0b10101010 (MSB first)\n'
|
||||
' 0b01010101 (LSB first)\n'
|
||||
'\n'
|
||||
' Bits: 16\n'
|
||||
' Hex: 0x0000 (MSB first)\n'
|
||||
' 0x0000 (LSB first)\n'
|
||||
' Dec: 0 (MSB first)\n'
|
||||
' 0 (LSB first)\n'
|
||||
' Bin: 0b0000000000000000 (MSB first)\n'
|
||||
' 0b0000000000000000 (LSB first)\n'
|
||||
'\n'
|
||||
' Bits: 32\n'
|
||||
' Hex: 0x12345678 (MSB first)\n'
|
||||
' 0x1E6A2C48 (LSB first)\n'
|
||||
' Dec: 305419896 (MSB first)\n'
|
||||
' 510274632 (LSB first)\n'
|
||||
' Bin: 0b00010010001101000101011001111000 (MSB first)\n'
|
||||
' 0b00011110011010100010110001001000 (LSB first)\n')
|
||||
|
||||
|
||||
class TestAutoAnalyseRawData(unittest.TestCase):
|
||||
"""Unit tests for the functions in AutoAnalyseRawData."""
|
||||
|
||||
def test_dump_constants_simple(self):
|
||||
"""Simple tests for the dump_constants() function."""
|
||||
ignore = StringIO.StringIO()
|
||||
output = StringIO.StringIO()
|
||||
defs = []
|
||||
message = analyse.RawIRMessage(200, [
|
||||
7930, 3952, 494, 1482, 520, 1482, 494, 1508, 494, 520, 494, 1482, 494,
|
||||
520, 494, 1482, 494, 1482, 494, 3978, 494, 520, 494, 520, 494, 520, 494,
|
||||
520, 520, 520, 494, 520, 494, 520, 494, 1482, 494
|
||||
], ignore)
|
||||
analyse.dump_constants(message, defs, output)
|
||||
self.assertEqual(defs, [
|
||||
'#define HDR_MARK 7930U', '#define BIT_MARK 520U',
|
||||
'#define HDR_SPACE 3978U', '#define ONE_SPACE 1508U',
|
||||
'#define ZERO_SPACE 520U'
|
||||
])
|
||||
self.assertEqual(output.getvalue(), 'Guessing key value:\n'
|
||||
'HDR_MARK = 7930\n'
|
||||
'HDR_SPACE = 3978\n'
|
||||
'BIT_MARK = 520\n'
|
||||
'ONE_SPACE = 1508\n'
|
||||
'ZERO_SPACE = 520\n')
|
||||
|
||||
def test_dump_constants_aircon(self):
|
||||
"""More complex tests for the dump_constants() function."""
|
||||
ignore = StringIO.StringIO()
|
||||
output = StringIO.StringIO()
|
||||
defs = []
|
||||
message = analyse.RawIRMessage(200, [
|
||||
9008, 4496, 644, 1660, 676, 530, 648, 558, 672, 1636, 646, 1660, 644,
|
||||
556, 650, 584, 626, 560, 644, 580, 628, 1680, 624, 560, 648, 1662, 644,
|
||||
582, 648, 536, 674, 530, 646, 580, 628, 560, 670, 532, 646, 562, 644,
|
||||
556, 672, 536, 648, 1662, 646, 1660, 652, 554, 644, 558, 672, 538, 644,
|
||||
560, 668, 560, 648, 1638, 668, 536, 644, 1660, 668, 532, 648, 560, 648,
|
||||
1660, 674, 554, 622, 19990, 646, 580, 624, 1660, 648, 556, 648, 558,
|
||||
674, 556, 622, 560, 644, 564, 668, 536, 646, 1662, 646, 1658, 672, 534,
|
||||
648, 558, 644, 562, 648, 1662, 644, 584, 622, 558, 648, 562, 668, 534,
|
||||
670, 536, 670, 532, 672, 536, 646, 560, 646, 558, 648, 558, 670, 534,
|
||||
650, 558, 646, 560, 646, 560, 668, 1638, 646, 1662, 646, 1660, 646,
|
||||
1660, 648
|
||||
], ignore)
|
||||
analyse.dump_constants(message, defs, output)
|
||||
self.assertEqual(defs, [
|
||||
'#define HDR_MARK 9008U', '#define BIT_MARK 676U',
|
||||
'#define HDR_SPACE 4496U', '#define ONE_SPACE 1680U',
|
||||
'#define ZERO_SPACE 584U', '#define SPACE_GAP = 19990U'
|
||||
])
|
||||
self.assertEqual(output.getvalue(), 'Guessing key value:\n'
|
||||
'HDR_MARK = 9008\n'
|
||||
'HDR_SPACE = 4496\n'
|
||||
'BIT_MARK = 676\n'
|
||||
'ONE_SPACE = 1680\n'
|
||||
'ZERO_SPACE = 584\n'
|
||||
'SPACE_GAP = 19990\n')
|
||||
|
||||
def test_convert_rawdata(self):
|
||||
"""Tests for the convert_rawdata() function."""
|
||||
# trivial cases
|
||||
self.assertEqual(analyse.convert_rawdata("0"), [0])
|
||||
with self.assertRaises(ValueError) as context:
|
||||
analyse.convert_rawdata("")
|
||||
self.assertEqual(context.exception.message,
|
||||
"Raw Data contains a non-numeric value of ''.")
|
||||
|
||||
# Single parenthesis
|
||||
self.assertEqual(analyse.convert_rawdata("foo {10"), [10])
|
||||
self.assertEqual(analyse.convert_rawdata("20} bar"), [20])
|
||||
|
||||
# No parentheses
|
||||
self.assertEqual(analyse.convert_rawdata("10,20 , 30"), [10, 20, 30])
|
||||
|
||||
# Dual parentheses
|
||||
self.assertEqual(analyse.convert_rawdata("{10,20 , 30}"), [10, 20, 30])
|
||||
self.assertEqual(analyse.convert_rawdata("foo{10,20}bar"), [10, 20])
|
||||
|
||||
# Many parentheses
|
||||
self.assertEqual(analyse.convert_rawdata("foo{10,20}{bar}"), [10, 20])
|
||||
self.assertEqual(analyse.convert_rawdata("foo{10,20}{bar}}{"), [10, 20])
|
||||
|
||||
# Bad parentheses
|
||||
with self.assertRaises(ValueError) as context:
|
||||
analyse.convert_rawdata("}10{")
|
||||
self.assertEqual(context.exception.message,
|
||||
"Raw Data not parsible due to parentheses placement.")
|
||||
|
||||
# Non base-10 values
|
||||
with self.assertRaises(ValueError) as context:
|
||||
analyse.convert_rawdata("10, 20, foo, bar, 30")
|
||||
self.assertEqual(context.exception.message,
|
||||
"Raw Data contains a non-numeric value of 'foo'.")
|
||||
|
||||
# A messy usual "good" case.
|
||||
input_str = """uint16_t rawbuf[6] = {
|
||||
9008, 4496, 644,
|
||||
1660, 676,
|
||||
|
||||
530}
|
||||
;"""
|
||||
self.assertEqual(
|
||||
analyse.convert_rawdata(input_str), [9008, 4496, 644, 1660, 676, 530])
|
||||
|
||||
def test_parse_and_report(self):
|
||||
"""Tests for the parse_and_report() function."""
|
||||
|
||||
# Without code generation.
|
||||
output = StringIO.StringIO()
|
||||
input_str = """
|
||||
uint16_t rawbuf[139] = {9008, 4496, 644, 1660, 676, 530, 648, 558, 672,
|
||||
1636, 646, 1660, 644, 556, 650, 584, 626, 560, 644, 580, 628, 1680,
|
||||
624, 560, 648, 1662, 644, 582, 648, 536, 674, 530, 646, 580, 628,
|
||||
560, 670, 532, 646, 562, 644, 556, 672, 536, 648, 1662, 646, 1660,
|
||||
652, 554, 644, 558, 672, 538, 644, 560, 668, 560, 648, 1638, 668,
|
||||
536, 644, 1660, 668, 532, 648, 560, 648, 1660, 674, 554, 622, 19990,
|
||||
646, 580, 624, 1660, 648, 556, 648, 558, 674, 556, 622, 560, 644,
|
||||
564, 668, 536, 646, 1662, 646, 1658, 672, 534, 648, 558, 644, 562,
|
||||
648, 1662, 644, 584, 622, 558, 648, 562, 668, 534, 670, 536, 670,
|
||||
532, 672, 536, 646, 560, 646, 558, 648, 558, 670, 534, 650, 558,
|
||||
646, 560, 646, 560, 668, 1638, 646, 1662, 646, 1660, 646, 1660,
|
||||
648};"""
|
||||
analyse.parse_and_report(input_str, 200, False, output)
|
||||
self.assertEqual(
|
||||
output.getvalue(), 'Found 139 timing entries.\n'
|
||||
'Potential Mark Candidates:\n'
|
||||
'[9008, 676]\n'
|
||||
'Potential Space Candidates:\n'
|
||||
'[19990, 4496, 1680, 584]\n'
|
||||
'\n'
|
||||
'Guessing encoding type:\n'
|
||||
'Looks like it uses space encoding. Yay!\n'
|
||||
'\n'
|
||||
'Guessing key value:\n'
|
||||
'HDR_MARK = 9008\n'
|
||||
'HDR_SPACE = 4496\n'
|
||||
'BIT_MARK = 676\n'
|
||||
'ONE_SPACE = 1680\n'
|
||||
'ZERO_SPACE = 584\n'
|
||||
'SPACE_GAP = 19990\n'
|
||||
'\n'
|
||||
'Decoding protocol based on analysis so far:\n'
|
||||
'\n'
|
||||
'HDR_MARK+HDR_SPACE+10011000010100000000011000001010010 GAP(19990)\n'
|
||||
' Bits: 35\n'
|
||||
' Hex: 0x4C2803052 (MSB first)\n'
|
||||
' 0x250600A19 (LSB first)\n'
|
||||
' Dec: 20443050066 (MSB first)\n'
|
||||
' 9938405913 (LSB first)\n'
|
||||
' Bin: 0b10011000010100000000011000001010010 (MSB first)\n'
|
||||
' 0b01001010000011000000000101000011001 (LSB first)\n'
|
||||
'BIT_MARK(UNEXPECTED)01000000110001000000000000001111\n'
|
||||
' Bits: 32\n'
|
||||
' Hex: 0x40C4000F (MSB first)\n'
|
||||
' 0xF0002302 (LSB first)\n'
|
||||
' Dec: 1086586895 (MSB first)\n'
|
||||
' 4026540802 (LSB first)\n'
|
||||
' Bin: 0b01000000110001000000000000001111 (MSB first)\n'
|
||||
' 0b11110000000000000010001100000010 (LSB first)\n'
|
||||
'Total Nr. of suspected bits: 67\n')
|
||||
|
||||
# With code generation.
|
||||
output = StringIO.StringIO()
|
||||
input_str = """
|
||||
uint16_t rawbuf[37] = {7930, 3952, 494, 1482, 520, 1482, 494,
|
||||
1508, 494, 520, 494, 1482, 494, 520, 494, 1482, 494, 1482, 494,
|
||||
3978, 494, 520, 494, 520, 494, 520, 494, 520, 520, 520, 494, 520,
|
||||
494, 520, 494, 1482, 494};"""
|
||||
analyse.parse_and_report(input_str, 200, True, output)
|
||||
self.assertEqual(
|
||||
output.getvalue(), 'Found 37 timing entries.\n'
|
||||
'Potential Mark Candidates:\n'
|
||||
'[7930, 520]\n'
|
||||
'Potential Space Candidates:\n'
|
||||
'[3978, 1508, 520]\n'
|
||||
'\n'
|
||||
'Guessing encoding type:\n'
|
||||
'Looks like it uses space encoding. Yay!\n'
|
||||
'\n'
|
||||
'Guessing key value:\n'
|
||||
'HDR_MARK = 7930\n'
|
||||
'HDR_SPACE = 3978\n'
|
||||
'BIT_MARK = 520\n'
|
||||
'ONE_SPACE = 1508\n'
|
||||
'ZERO_SPACE = 520\n'
|
||||
'\n'
|
||||
'Decoding protocol based on analysis so far:\n'
|
||||
'\n'
|
||||
'HDR_MARK+HDR_SPACE+11101011\n'
|
||||
' Bits: 8\n'
|
||||
' Hex: 0xEB (MSB first)\n'
|
||||
' 0xD7 (LSB first)\n'
|
||||
' Dec: 235 (MSB first)\n'
|
||||
' 215 (LSB first)\n'
|
||||
' Bin: 0b11101011 (MSB first)\n'
|
||||
' 0b11010111 (LSB first)\n'
|
||||
'UNEXPECTED->HDR_SPACE+00000001\n'
|
||||
' Bits: 8\n Hex: 0x01 (MSB first)\n'
|
||||
' 0x80 (LSB first)\n'
|
||||
' Dec: 1 (MSB first)\n'
|
||||
' 128 (LSB first)\n'
|
||||
' Bin: 0b00000001 (MSB first)\n'
|
||||
' 0b10000000 (LSB first)\n'
|
||||
'Total Nr. of suspected bits: 16\n'
|
||||
'\n'
|
||||
'Generating a VERY rough code outline:\n'
|
||||
'\n'
|
||||
"// WARNING: This probably isn't directly usable. It's a guide only.\n"
|
||||
'#define HDR_MARK 7930U\n'
|
||||
'#define BIT_MARK 520U\n'
|
||||
'#define HDR_SPACE 3978U\n'
|
||||
'#define ONE_SPACE 1508U\n'
|
||||
'#define ZERO_SPACE 520U\n'
|
||||
'#define XYZ_BITS 16U\n'
|
||||
'// Function should be safe up to 64 bits.\n'
|
||||
'void IRsend::sendXYZ(const uint64_t data, const uint16_t nbits,'
|
||||
' const uint16_t repeat) {\n'
|
||||
' enableIROut(38); // A guess. Most common frequency.\n'
|
||||
' for (uint16_t r = 0; r <= repeat; r++) {\n'
|
||||
' // Header\n'
|
||||
' mark(HDR_MARK);\n'
|
||||
' space(HDR_SPACE);\n'
|
||||
' // Data\n'
|
||||
' // e.g. data = 0xEB, nbits = 8\n'
|
||||
' sendData(BIT_MARK, ONE_SPACE, BIT_MARK, ZERO_SPACE, data, nbits,'
|
||||
' true);\n'
|
||||
' // Footer\n'
|
||||
' mark(BIT_MARK);\n'
|
||||
' space(HDR_SPACE);\n'
|
||||
' // Data\n'
|
||||
' // e.g. data = 0x1, nbits = 8\n'
|
||||
' sendData(BIT_MARK, ONE_SPACE, BIT_MARK, ZERO_SPACE, data, nbits,'
|
||||
' true);\n'
|
||||
' // Footer\n'
|
||||
' mark(BIT_MARK);\n'
|
||||
' space(100000); // A 100% made up guess of the gap between'
|
||||
' messages.\n'
|
||||
' }\n'
|
||||
'}\n')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main(verbosity=2)
|
||||
@@ -0,0 +1,108 @@
|
||||
// Quick and dirty tool to decode GlobalCache (GC) codes
|
||||
// and ProntoHex codes
|
||||
// Copyright 2017 Jorge Cisneros
|
||||
|
||||
#include <errno.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <string>
|
||||
#include "IRsend.h"
|
||||
#include "IRsend_test.h"
|
||||
#include "IRutils.h"
|
||||
|
||||
#define MAX_GC_CODE_LENGTH 10000
|
||||
|
||||
void str_to_uint16(char *str, uint16_t *res, uint8_t base) {
|
||||
char *end;
|
||||
errno = 0;
|
||||
intmax_t val = strtoimax(str, &end, base);
|
||||
if (errno == ERANGE || val < 0 || val > UINT16_MAX ||
|
||||
end == str || *end != '\0')
|
||||
return;
|
||||
*res = (uint16_t) val;
|
||||
}
|
||||
|
||||
void usage_error(char * name) {
|
||||
std::cerr << "Usage: " << name << " [-raw] <global_code>" << std::endl
|
||||
<< "Usage: " << name << " -prontohex [-raw] <prontohex_code>" << std::endl;
|
||||
}
|
||||
|
||||
int main(int argc, char * argv[]) {
|
||||
int argv_offset = 1;
|
||||
bool dumpraw = false;
|
||||
bool prontohex = false;
|
||||
|
||||
// Check the invocation/calling usage.
|
||||
if (argc < 2 || argc > 4) {
|
||||
usage_error(argv[0]);
|
||||
return 1;
|
||||
}
|
||||
if (strncmp("-prontohex", argv[argv_offset], 10) == 0) {
|
||||
prontohex = true;
|
||||
argv_offset++;
|
||||
}
|
||||
|
||||
if (strncmp("-raw", argv[argv_offset], 4) == 0) {
|
||||
dumpraw = true;
|
||||
argv_offset++;
|
||||
}
|
||||
if (argc - argv_offset != 1) {
|
||||
usage_error(argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
uint16_t gc_test[MAX_GC_CODE_LENGTH];
|
||||
int index = 0;
|
||||
char *pch;
|
||||
char *saveptr1;
|
||||
char *sep = const_cast<char *>(",");
|
||||
int codebase = 10;
|
||||
|
||||
if (prontohex) {
|
||||
sep = const_cast<char *>(" ");
|
||||
codebase = 16;
|
||||
}
|
||||
|
||||
pch = strtok_r(argv[argv_offset], sep, &saveptr1);
|
||||
while (pch != NULL && index < MAX_GC_CODE_LENGTH) {
|
||||
str_to_uint16(pch, &gc_test[index], codebase);
|
||||
pch = strtok_r(NULL, sep, &saveptr1);
|
||||
index++;
|
||||
}
|
||||
|
||||
IRsendTest irsend(4);
|
||||
IRrecv irrecv(4);
|
||||
irsend.begin();
|
||||
irsend.reset();
|
||||
|
||||
if (prontohex) {
|
||||
irsend.sendPronto(gc_test, index);
|
||||
} else {
|
||||
irsend.sendGC(gc_test, index);
|
||||
}
|
||||
irsend.makeDecodeResult();
|
||||
irrecv.decode(&irsend.capture);
|
||||
|
||||
std::cout << "Code length " << index << std::endl
|
||||
<< "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)) {
|
||||
std::cout << "State value 0x";
|
||||
for (uint16_t i = 0; i < irsend.capture.bits / 8; i++)
|
||||
printf("%02X", irsend.capture.state[i]);
|
||||
std::cout << std::endl;
|
||||
} else {
|
||||
std::cout << "Code value 0x" <<
|
||||
std::hex << irsend.capture.value << std::endl <<
|
||||
"Code address 0x" << std::hex << irsend.capture.address << std::endl
|
||||
<< "Code command 0x" << std::hex << irsend.capture.command <<
|
||||
std::endl;
|
||||
}
|
||||
|
||||
if (dumpraw || irsend.capture.decode_type == UNKNOWN)
|
||||
irsend.dumpRawResult();
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
#!/bin/bash
|
||||
# Generate a keywords.txt for the library that is suitable for the Arduino IDE.
|
||||
# Expects to run from the top directory of the library.
|
||||
|
||||
# Set the locale for sorting
|
||||
export LC_ALL=C
|
||||
|
||||
cat << EndOfTextEndOfTextEndOfText
|
||||
#########################################
|
||||
# Syntax Coloring Map For IRremoteESP8266
|
||||
#########################################
|
||||
|
||||
################################################
|
||||
# WARNING: Do NOT edit this file directly.
|
||||
# It is generated by 'tools/mkkeywords'
|
||||
# e.g. tools/mkkeywords > keywords.txt
|
||||
################################################
|
||||
|
||||
#######################################################
|
||||
# The Arduino IDE requires the use of a tab separator
|
||||
# between the name and identifier. Without this tab the
|
||||
# keyword is not highlighted.
|
||||
#
|
||||
# Reference: https://github.com/arduino/Arduino/wiki/Arduino-IDE-1.5:-Library-specification#keywords
|
||||
#######################################################
|
||||
|
||||
#######################################
|
||||
# Datatypes & Classes (KEYWORD1)
|
||||
#######################################
|
||||
|
||||
EndOfTextEndOfTextEndOfText
|
||||
|
||||
CLASSES=$(grep "^class " src/*.h | cut -d' ' -f2 | sort -u)
|
||||
# Manually add typedefs as they are hard to parse out.
|
||||
CLASSES="${CLASSES} ir_params_t match_result_t"
|
||||
for i in ${CLASSES}; do
|
||||
echo -e "${i}\tKEYWORD1"
|
||||
done | sort -du
|
||||
|
||||
cat << EndOfTextEndOfTextEndOfText
|
||||
|
||||
#######################################
|
||||
# Methods and Functions (KEYWORD2)
|
||||
#######################################
|
||||
|
||||
EndOfTextEndOfTextEndOfText
|
||||
|
||||
METHODS=$(egrep "^(u?int|void|bool|String|static|match_result_t).*\(" src/*.cpp|
|
||||
cut -d' ' -f2- | sed 's/^.*:://' | cut -d'(' -f1 | sort -u |
|
||||
grep -v ICACHE_RAM_ATTR)
|
||||
for i in ${METHODS}; do
|
||||
echo -e "${i}\tKEYWORD2"
|
||||
done | sort -u
|
||||
|
||||
cat << EndOfTextEndOfTextEndOfText
|
||||
|
||||
#######################################
|
||||
# Constants (LITERAL1)
|
||||
#######################################
|
||||
|
||||
EndOfTextEndOfTextEndOfText
|
||||
LITERALS=$(grep "^#define [A-Z]" src/*.cpp src/*.h |
|
||||
while read ignore define ignore; do
|
||||
echo ${define};
|
||||
done | sort -u |
|
||||
grep -v [\(\)] | grep -v ^_ | grep -v _\$ | grep -v VIRTUAL)
|
||||
ENUMS=$(cat src/*.h | while read a b; do
|
||||
if [[ ${a} == "};" ]]; then
|
||||
ENUM=0;
|
||||
fi;
|
||||
if [[ ${ENUM} -eq 1 ]]; then
|
||||
echo $a | sed 's/,//g';
|
||||
fi;
|
||||
if [[ ${a} == "enum" ]]; then
|
||||
ENUM=1;
|
||||
fi;
|
||||
done)
|
||||
for i in ${LITERALS} ${ENUMS}; do
|
||||
echo -e "${i}\tLITERAL1"
|
||||
done | sort -u
|
||||
Reference in new issue
Block a user