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