forked from Arduino-IRremote/Arduino-IRremote
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathir_Pronto.hpp
344 lines (304 loc) · 12.2 KB
/
ir_Pronto.hpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
/*
* @file ir_Pronto.hpp
* @brief In this file, the functions IRrecv::compensateAndPrintPronto and IRsend::sendPronto are defined.
*
* See http://www.harctoolbox.org/Glossary.html#ProntoSemantics
* Pronto database http://www.remotecentral.com/search.htm
*
* This file is part of Arduino-IRremote https://github.com/Arduino-IRremote/Arduino-IRremote.
*
************************************************************************************
* MIT License
*
* Copyright (c) 2020 Bengt Martensson
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is furnished
* to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
************************************************************************************
*/
#ifndef _IR_PRONTO_HPP
#define _IR_PRONTO_HPP
#if defined(DEBUG) && !defined(LOCAL_DEBUG)
#define LOCAL_DEBUG
#else
//#define LOCAL_DEBUG // This enables debug output only for this file
#endif
/** \addtogroup Decoder Decoders and encoders for different protocols
* @{
*/
//! @cond
// DO NOT EXPORT from this file
static const uint16_t learnedToken = 0x0000U;
static const uint16_t learnedNonModulatedToken = 0x0100U;
static const uint16_t bitsInHexadecimal = 4U;
static const uint16_t digitsInProntoNumber = 4U;
static const uint16_t numbersInPreamble = 4U;
static const uint16_t hexMask = 0xFU;
static const uint32_t referenceFrequency = 4145146UL;
static const uint16_t fallbackFrequency = 64767U; // To use with frequency = 0;
static const uint32_t microsecondsInSeconds = 1000000UL;
static const uint16_t PRONTO_DEFAULT_GAP = 45000;
//! @endcond
static uint16_t toFrequencyKHz(uint16_t code) {
return ((referenceFrequency / code) + 500) / 1000;
}
/*
* Parse the string given as Pronto Hex, and send it a number of times given as argument.
* The first number denotes the type of the signal. 0000 denotes a raw IR signal with modulation,
// The second number denotes a frequency code
*/
void IRsend::sendPronto(const uint16_t *data, uint16_t length, int_fast8_t aNumberOfRepeats) {
uint16_t timebase = (microsecondsInSeconds * data[1] + referenceFrequency / 2) / referenceFrequency;
uint16_t khz;
switch (data[0]) {
case learnedToken: // normal, "learned"
khz = toFrequencyKHz(data[1]);
break;
case learnedNonModulatedToken: // non-demodulated, "learned"
khz = 0U;
break;
default:
return; // There are other types, but they are not handled yet.
}
uint16_t intros = 2 * data[2];
uint16_t repeats = 2 * data[3];
#if defined(LOCAL_DEBUG)
Serial.print(F("sendPronto intros="));
Serial.print(intros);
Serial.print(F(" repeats="));
Serial.println(repeats);
#endif
if (numbersInPreamble + intros + repeats != length) { // inconsistent sizes
return;
}
/*
* Generate a new microseconds timing array for sendRaw.
* If recorded by IRremote, intro contains the whole IR data and repeat is empty
*/
uint16_t durations[intros + repeats];
for (uint16_t i = 0; i < intros + repeats; i++) {
uint32_t duration = ((uint32_t) data[i + numbersInPreamble]) * timebase;
durations[i] = (uint16_t) ((duration <= UINT16_MAX) ? duration : UINT16_MAX);
}
/*
* Send the intro. intros is even.
* Do not send the trailing space here, send it if repeats are requested
*/
if (intros >= 2) {
sendRaw(durations, intros - 1, khz);
}
if (repeats == 0 || aNumberOfRepeats == 0) {
// only send intro once
return;
}
/*
* Now send the trailing space/gap of the intro and all the repeats
*/
if (intros >= 2) {
delay(durations[intros - 1] / MICROS_IN_ONE_MILLI); // equivalent to space(durations[intros - 1]); but allow bigger values for the gap
}
for (int i = 0; i < aNumberOfRepeats; i++) {
sendRaw(durations + intros, repeats - 1, khz);
if ((i + 1) < aNumberOfRepeats) { // skip last trailing space/gap, see above
delay(durations[intros + repeats - 1] / MICROS_IN_ONE_MILLI);
}
}
}
/**
* Parse the string given as Pronto Hex, and send it a number of times given
* as the second argument. Thereby the division of the Pronto Hex into
* an intro-sequence and a repeat sequence is taken into account:
* First the intro sequence is sent, then the repeat sequence is sent times-1 times.
* However, if the intro sequence is empty, the repeat sequence is sent times times.
* <a href="http://www.harctoolbox.org/Glossary.html#ProntoSemantics">Reference</a>.
*
* Note: Using this function is very wasteful for the memory consumption on
* a small board.
* Normally it is a much better idea to use a tool like e.g. IrScrutinizer
* to transform Pronto type signals offline
* to a more memory efficient format.
*
* @param str C type string (null terminated) containing a Pronto Hex representation.
* @param aNumberOfRepeats Number of times to send the signal.
*/
void IRsend::sendPronto(const char *str, int_fast8_t aNumberOfRepeats) {
size_t len = strlen(str) / (digitsInProntoNumber + 1) + 1;
uint16_t data[len];
const char *p = str;
char *endptr[1];
for (uint16_t i = 0; i < len; i++) {
long x = strtol(p, endptr, 16);
if (x == 0 && i >= numbersInPreamble) {
// Alignment error?, bail immediately (often right result).
len = i;
break;
}
data[i] = static_cast<uint16_t>(x); // If input is conforming, there can be no overflow!
p = *endptr;
}
sendPronto(data, len, aNumberOfRepeats);
}
#if defined(__AVR__)
/**
* Version of sendPronto that reads from PROGMEM, saving RAM memory.
* @param str pronto C type string (null terminated) containing a Pronto Hex representation.
* @param aNumberOfRepeats Number of times to send the signal.
*/
//far pointer (? for ATMega2560 etc.)
void IRsend::sendPronto_PF(uint_farptr_t str, int_fast8_t aNumberOfRepeats) {
size_t len = strlen_PF(str);
char work[len + 1];
strcpy_PF(work, str); // We know that string including terminating character fits in work
sendPronto(work, aNumberOfRepeats);
}
//standard pointer
void IRsend::sendPronto_P(const char *str, int_fast8_t aNumberOfRepeats) {
size_t len = strlen_P(str);
char work[len + 1];
strcpy_P(work, str);
sendPronto(work, aNumberOfRepeats);
}
#endif
/*
* Copy flash data to ram buffer in stack
*/
void IRsend::sendPronto(const __FlashStringHelper *str, int_fast8_t aNumberOfRepeats) {
size_t len = strlen_P(reinterpret_cast<const char*>(str));
char work[len + 1];
strcpy_P(work, reinterpret_cast<const char*>(str));
return sendPronto(work, aNumberOfRepeats);
}
static uint16_t effectiveFrequency(uint16_t frequency) {
return frequency > 0 ? frequency : fallbackFrequency;
}
static uint16_t toTimebase(uint16_t frequency) {
return microsecondsInSeconds / effectiveFrequency(frequency);
}
static uint16_t toFrequencyCode(uint16_t frequency) {
return referenceFrequency / effectiveFrequency(frequency);
}
static char hexDigit(uint16_t x) {
return (char) (x <= 9 ? ('0' + x) : ('A' + (x - 10)));
}
static void dumpDigit(Print *aSerial, uint16_t number) {
aSerial->print(hexDigit(number));
}
static void dumpNumber(Print *aSerial, uint16_t number) {
for (uint16_t i = 0; i < digitsInProntoNumber; i++) {
uint16_t shifts = bitsInHexadecimal * (digitsInProntoNumber - 1 - i);
dumpDigit(aSerial, (number >> shifts) & hexMask);
}
aSerial->print(' ');
}
static void dumpDuration(Print *aSerial, uint32_t duration, uint16_t timebase) {
dumpNumber(aSerial, (duration + timebase / 2) / timebase);
}
/*
* Compensate received values by MARK_EXCESS_MICROS, like it is done for decoding!
*/
static void compensateAndDumpSequence(Print *aSerial, const volatile uint16_t *data, size_t length, uint16_t timebase) {
for (size_t i = 0; i < length; i++) {
uint32_t tDuration = data[i] * MICROS_PER_TICK;
if (i & 1) {
// Mark
tDuration -= getMarkExcessMicros();
} else {
tDuration += getMarkExcessMicros();
}
dumpDuration(aSerial, tDuration, timebase);
}
// append a gap
dumpDuration(aSerial, PRONTO_DEFAULT_GAP, timebase);
}
/**
* Print the result (second argument) as Pronto Hex on the Print supplied as argument.
* Used in the ReceiveDump example.
* @param aSerial The Print object on which to write, for Arduino you can use &Serial.
* @param aFrequencyHertz Modulation frequency in Hz. Often 38000Hz.
*/
void IRrecv::compensateAndPrintIRResultAsPronto(Print *aSerial, uint16_t aFrequencyHertz) {
aSerial->println(F("Pronto Hex as string"));
aSerial->print(F("char prontoData[] = \""));
dumpNumber(aSerial, aFrequencyHertz > 0 ? learnedToken : learnedNonModulatedToken);
dumpNumber(aSerial, toFrequencyCode(aFrequencyHertz));
dumpNumber(aSerial, (decodedIRData.rawlen + 1) / 2);
dumpNumber(aSerial, 0);
uint16_t timebase = toTimebase(aFrequencyHertz);
compensateAndDumpSequence(aSerial, &decodedIRData.rawDataPtr->rawbuf[1], decodedIRData.rawlen - 1, timebase); // skip leading space
aSerial->println("\";");
}
/*
* Functions for dumping Pronto to a String. This is not very time and space efficient
* and can lead to resource problems especially on small processors like AVR's
*/
static bool dumpDigit(String *aString, uint16_t number) {
aString->concat(hexDigit(number));
return number;
}
static size_t dumpNumber(String *aString, uint16_t number) {
size_t size = 0;
for (uint16_t i = 0; i < digitsInProntoNumber; i++) {
uint16_t shifts = bitsInHexadecimal * (digitsInProntoNumber - 1 - i);
size += dumpDigit(aString, (number >> shifts) & hexMask);
}
aString->concat(' ');
size++;
return size;
}
/*
* Compensate received values by MARK_EXCESS_MICROS, like it is done for decoding!
*/
static size_t dumpDuration(String *aString, uint32_t duration, uint16_t timebase) {
return dumpNumber(aString, (duration + timebase / 2) / timebase);
}
static size_t compensateAndDumpSequence(String *aString, const volatile uint16_t *data, size_t length, uint16_t timebase) {
size_t size = 0;
for (size_t i = 0; i < length; i++) {
uint32_t tDuration = data[i] * MICROS_PER_TICK;
if (i & 1) {
// Mark
tDuration -= getMarkExcessMicros();
} else {
tDuration += getMarkExcessMicros();
}
size += dumpDuration(aString, tDuration, timebase);
}
// append minimum gap
size += dumpDuration(aString, PRONTO_DEFAULT_GAP, timebase);
return size;
}
/*
* Writes Pronto HEX to a String object.
* Returns the amount of characters added to the string.(360 characters for a NEC code!)
*/
size_t IRrecv::compensateAndStorePronto(String *aString, uint16_t frequency) {
size_t size = 0;
uint16_t timebase = toTimebase(frequency);
size += dumpNumber(aString, frequency > 0 ? learnedToken : learnedNonModulatedToken);
size += dumpNumber(aString, toFrequencyCode(frequency));
size += dumpNumber(aString, (decodedIRData.rawlen + 1) / 2);
size += dumpNumber(aString, 0);
size += compensateAndDumpSequence(aString, &decodedIRData.rawDataPtr->rawbuf[1], decodedIRData.rawlen - 1,
timebase); // skip leading space
return size;
}
/** @}*/
#if defined(LOCAL_DEBUG)
#undef LOCAL_DEBUG
#endif
#endif // _IR_PRONTO_HPP