Skip to content

Commit 9630be3

Browse files
committed
Enhanced raw conversion tool for UCR2
The uccode_to_raw tool is based on the code_to_raw tool. It outputs additional information about the IR code and protocol in JSON format. The input parameter has been changed to a combined format as it is stored in UCR2: `<PROTOCOL>;<CODE>;<BITS>;<REPEAT>`
1 parent 534cab5 commit 9630be3

File tree

2 files changed

+294
-0
lines changed

2 files changed

+294
-0
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ tools/*.a
4141
tools/gc_decode
4242
tools/mode2_decode
4343
tools/code_to_raw
44+
tools/uccode_to_raw
4445

4546
.pioenvs
4647
.piolibdeps

tools/uccode_to_raw.cpp

Lines changed: 293 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,293 @@
1+
// Custom version of the code_to_raw tool for Unfolded Circle Remote Two.
2+
// Convert an IR hex code to raw timing and additional IR information to
3+
// json output.
4+
//
5+
// Original file header of code_to_raw:
6+
// Quick and dirty tool to convert a protocol's (hex) codes to raw timings.
7+
// Copyright 2021 David Conran
8+
9+
#include <errno.h>
10+
#include <inttypes.h>
11+
#include <math.h>
12+
#include <stdio.h>
13+
#include <string.h>
14+
#include <string>
15+
#include "IRac.h"
16+
#include "IRsend.h"
17+
#include "IRsend_test.h"
18+
#include "IRutils.h"
19+
20+
21+
String resultToRaw(const decode_results * const results,
22+
const uint16_t repeat);
23+
24+
void usage_error(char *name) {
25+
std::cerr << "Usage: " << name << " UC_CODE" << std::endl;
26+
std::cerr << std::endl;
27+
std::cerr << " UC_CODE: <PROTOCOL>;<CODE>;<BITS>;<REPEAT>" << std::endl;
28+
std::cerr << std::endl;
29+
std::cerr << " Example: " << name << " \"12;0xE242;16;2\"" << std::endl;
30+
}
31+
32+
int main(int argc, char *argv[]) {
33+
int argv_offset = 1;
34+
uint64_t code = 0;
35+
uint8_t state[kStateSizeMax] = {0}; // All array elements are set to 0.
36+
decode_type_t input_type = decode_type_t::UNKNOWN;
37+
38+
// Check the invocation/calling usage.
39+
if (argc != 2) {
40+
usage_error(argv[0]);
41+
return 1;
42+
}
43+
44+
// Split the UC_CODE parameter into protocol / code / bits / repeats
45+
char* parts[4];
46+
int partcount = 0;
47+
48+
parts[partcount++] = argv[argv_offset];
49+
50+
char* ptr = argv[argv_offset];
51+
while (*ptr && partcount < 4) {
52+
if (*ptr == ';') {
53+
*ptr = 0;
54+
parts[partcount++] = ptr + 1;
55+
}
56+
ptr++;
57+
}
58+
59+
if (partcount != 4) {
60+
std::cerr << "Invalid UC_CODE specified" << std::endl;
61+
return 1;
62+
}
63+
argv_offset++;
64+
65+
input_type = strToDecodeType(parts[0]);
66+
switch (input_type) {
67+
// Unsupported types
68+
case decode_type_t::UNUSED:
69+
case decode_type_t::UNKNOWN:
70+
case decode_type_t::GLOBALCACHE:
71+
case decode_type_t::PRONTO:
72+
case decode_type_t::RAW:
73+
std::cerr << "The protocol specified is not supported by this program."
74+
<< std::endl;
75+
return 1;
76+
default:
77+
break;
78+
}
79+
80+
uint16_t nbits = static_cast<uint16_t>(std::stoul(parts[2]));
81+
if (nbits == 0 && (nbits <= kStateSizeMax * 8)) {
82+
std::cerr << "Nr. of bits " << parts[2]
83+
<< " is invalid." << std::endl;
84+
return 1;
85+
}
86+
uint16_t stateSize = nbits / 8;
87+
88+
uint16_t repeats = static_cast<uint16_t>(std::stoul(parts[3]));
89+
if (repeats > 20) {
90+
std::cerr << "Repeat count is too large: " << repeats
91+
<< ". Maximum is 20." << std::endl;
92+
return 1;
93+
}
94+
95+
String hexstr = String(parts[1]);
96+
uint64_t strOffset = 0;
97+
if (hexstr.rfind("0x", 0) || hexstr.rfind("0X", 0)) {
98+
strOffset = 2;
99+
}
100+
101+
// Calculate how many hexadecimal characters there are.
102+
uint64_t hexstrlength = hexstr.length() - strOffset;
103+
104+
// Ptr to the least significant byte of the resulting state for this
105+
// protocol.
106+
uint8_t *statePtr = &state[stateSize - 1];
107+
108+
// Convert the string into a state array of the correct length.
109+
for (uint16_t i = 0; i < hexstrlength; i++) {
110+
// Grab the next least sigificant hexadecimal digit from the string.
111+
uint8_t c = tolower(hexstr[hexstrlength + strOffset - i - 1]);
112+
if (isxdigit(c)) {
113+
if (isdigit(c))
114+
c -= '0';
115+
else
116+
c = c - 'a' + 10;
117+
} else {
118+
std::cerr << "Code " << parts[1]
119+
<< " contains non-hexidecimal characters." << std::endl;
120+
return 3;
121+
}
122+
if (i % 2 == 1) { // Odd: Upper half of the byte.
123+
*statePtr += (c << 4);
124+
statePtr--; // Advance up to the next least significant byte of state.
125+
} else { // Even: Lower half of the byte.
126+
*statePtr = c;
127+
}
128+
}
129+
if (!hasACState(input_type)) {
130+
code = std::stoull(parts[1], nullptr, 16);
131+
}
132+
133+
IRsendTest irsend(kGpioUnused);
134+
IRrecv irrecv(kGpioUnused);
135+
irsend.begin();
136+
irsend.reset();
137+
138+
bool result;
139+
if (hasACState(input_type)) { // Is it larger than 64 bits?
140+
result = irsend.send(input_type, state, stateSize);
141+
} else {
142+
result = irsend.send(input_type, code, nbits, repeats);
143+
}
144+
145+
if (!result) {
146+
std::cerr << "Failed to decode IR code!" << std::endl;
147+
return 4;
148+
}
149+
150+
irsend.makeDecodeResult();
151+
irrecv.decode(&irsend.capture);
152+
153+
std::cout << resultToRaw(&irsend.capture, repeats) << std::endl;
154+
155+
return 0;
156+
}
157+
158+
/// Return the frequency of a given protocol.
159+
/// Values have been pulled out of the individual IR decoders.
160+
uint32_t frequency(const decode_results * const results) {
161+
switch (results->decode_type) {
162+
case decode_type_t::DAIKIN2:
163+
case decode_type_t::PANASONIC:
164+
return kPanasonicFreq;
165+
case decode_type_t::DENON:
166+
return results->bits >= kPanasonicBits ? kPanasonicFreq : 38000;
167+
case decode_type_t::RC5:
168+
case decode_type_t::RC5X:
169+
case decode_type_t::RC6:
170+
case decode_type_t::RCMM:
171+
case decode_type_t::TROTEC:
172+
return 36000;
173+
case decode_type_t::PIONEER:
174+
case decode_type_t::SONY:
175+
return 40000;
176+
case decode_type_t::DISH:
177+
return 57600;
178+
case decode_type_t::LUTRON:
179+
return 40000;
180+
default:
181+
return 38000;
182+
}
183+
}
184+
185+
/// Return the duty cyle of a given protocol.
186+
/// Values have been pulled out of the individual IR decoders.
187+
uint8_t duty_cycle(const decode_results * const results) {
188+
switch (results->decode_type) {
189+
case decode_type_t::RC5:
190+
case decode_type_t::RC5X:
191+
case decode_type_t::LASERTAG:
192+
case decode_type_t::MWM:
193+
return 25;
194+
case decode_type_t::JVC:
195+
case decode_type_t::RC6:
196+
case decode_type_t::RCMM:
197+
return 33;
198+
case decode_type_t::LUTRON:
199+
return 40;
200+
default:
201+
return 50;
202+
}
203+
}
204+
205+
/// Return a String containing the key values of a decode_results structure
206+
/// in a JSON style format.
207+
/// @param[in] results A ptr to a decode_results structure.
208+
/// @return A String containing the code-ified result.
209+
String resultToRaw(const decode_results * const results,
210+
const uint16_t repeat) {
211+
String output = "";
212+
const uint16_t length = getCorrectedRawLength(results);
213+
const bool hasState = hasACState(results->decode_type);
214+
// Reserve some space for the string to reduce heap fragmentation.
215+
// "uint16_t rawData[9999] = {}; // LONGEST_PROTOCOL\n" = ~55 chars.
216+
// "NNNN, " = ~7 chars on average per raw entry
217+
// Protocols with a `state`:
218+
// "uint8_t state[NN] = {};\n" = ~25 chars
219+
// "0xNN, " = 6 chars per byte.
220+
// Protocols without a `state`:
221+
// " DEADBEEFDEADBEEF\n"
222+
// "uint32_t address = 0xDEADBEEF;\n"
223+
// "uint32_t command = 0xDEADBEEF;\n"
224+
// "uint64_t data = 0xDEADBEEFDEADBEEF;" = ~116 chars max.
225+
output.reserve(55 + (length * 7) + hasState ? 25 + (results->bits / 8) * 6
226+
: 116);
227+
// Start declaration
228+
output += F("{\n\"raw\": ["); // Start declaration
229+
230+
// Dump data
231+
for (uint16_t i = 1; i < results->rawlen; i++) {
232+
uint32_t usecs = results->rawbuf[i] * kRawTick;
233+
output += uint64ToString(usecs, 10);
234+
if (i < results->rawlen - 1)
235+
output += kCommaSpaceStr; // ',' not needed on the last one
236+
}
237+
238+
output += F("],\n");
239+
240+
output += F("\"protocol\": \"");
241+
output += typeToString(results->decode_type);
242+
output += F("\",\n\"bits\": ");
243+
output += uint64ToString(results->bits, 10);
244+
output += F(",\n\"frequency\": ");
245+
output += uint64ToString(frequency(results), 10);
246+
output += F(",\n\"duty_cycle\": ");
247+
output += uint64ToString(duty_cycle(results), 10);
248+
output += F(",\n\"repeat\": ");
249+
output += uint64ToString(repeat);
250+
output += F(",\n\"min_repeat\": ");
251+
output += uint64ToString(IRsend::minRepeats(results->decode_type));
252+
output += F(",\n");
253+
254+
// Now dump "known" codes
255+
if (results->decode_type != UNKNOWN) {
256+
if (hasState) {
257+
// Untested, UCR2 doesn't support AC protocols
258+
#if DECODE_AC
259+
uint16_t nbytes = ceil(static_cast<float>(results->bits) / 8.0);
260+
output += F("\"states\": [");
261+
for (uint16_t i = 0; i < nbytes; i++) {
262+
output += F("\"0x");
263+
if (results->state[i] < 0x10) output += '0';
264+
output += uint64ToString(results->state[i], 16);
265+
output += F("\"");
266+
if (i < nbytes - 1) output += kCommaSpaceStr;
267+
}
268+
output += F("],\n");
269+
#endif // DECODE_AC
270+
} else {
271+
// Simple protocols
272+
// Some protocols have an address &/or command.
273+
// NOTE: It will ignore the atypical case when a message has been
274+
// decoded but the address & the command are both 0.
275+
if (results->address > 0 || results->command > 0) {
276+
output += F("\"address\": \"0x");
277+
output += uint64ToString(results->address, 16);
278+
output += F("\",\n");
279+
output += F("\"command\": \"0x");
280+
output += uint64ToString(results->command, 16);
281+
output += F("\",\n");
282+
}
283+
// Most protocols have data
284+
output += F("\"data\": \"0x");
285+
output += uint64ToString(results->value, 16);
286+
output += F("\"\n");
287+
}
288+
}
289+
290+
output += F("}\n");
291+
292+
return output;
293+
}

0 commit comments

Comments
 (0)