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 void IRsend::sendPronto(const __FlashStringHelper *str, int_fast8_t aNumberOfRepeats) {
191  size_t len = strlen_P(reinterpret_cast<const char*>(str));
192  char work[len + 1];
193  strcpy_P(work, reinterpret_cast<const char*>(str));
194  return sendPronto(work, aNumberOfRepeats);
195 }
196 
197 static uint16_t effectiveFrequency(uint16_t frequency) {
198  return frequency > 0 ? frequency : fallbackFrequency;
199 }
200 
201 static uint16_t toTimebase(uint16_t frequency) {
202  return microsecondsInSeconds / effectiveFrequency(frequency);
203 }
204 
205 static uint16_t toFrequencyCode(uint16_t frequency) {
206  return referenceFrequency / effectiveFrequency(frequency);
207 }
208 
209 static char hexDigit(uint16_t x) {
210  return (char) (x <= 9 ? ('0' + x) : ('A' + (x - 10)));
211 }
212 
213 static void dumpDigit(Print *aSerial, uint16_t number) {
214  aSerial->print(hexDigit(number));
215 }
216 
217 static void dumpNumber(Print *aSerial, uint16_t number) {
218  for (uint16_t i = 0; i < digitsInProntoNumber; i++) {
219  uint16_t shifts = bitsInHexadecimal * (digitsInProntoNumber - 1 - i);
220  dumpDigit(aSerial, (number >> shifts) & hexMask);
221  }
222  aSerial->print(' ');
223 }
224 
225 static void dumpDuration(Print *aSerial, uint32_t duration, uint16_t timebase) {
226  dumpNumber(aSerial, (duration + timebase / 2) / timebase);
227 }
228 
229 /*
230  * Compensate received values by MARK_EXCESS_MICROS, like it is done for decoding!
231  */
232 static void compensateAndDumpSequence(Print *aSerial, const volatile uint16_t *data, size_t length, uint16_t timebase) {
233  for (size_t i = 0; i < length; i++) {
234  uint32_t tDuration = data[i] * MICROS_PER_TICK;
235  if (i & 1) {
236  // Mark
237  tDuration -= getMarkExcessMicros();
238  } else {
239  tDuration += getMarkExcessMicros();
240  }
241  dumpDuration(aSerial, tDuration, timebase);
242  }
243 
244  // append a gap
245  dumpDuration(aSerial, PRONTO_DEFAULT_GAP, timebase);
246 }
247 
254 void IRrecv::compensateAndPrintIRResultAsPronto(Print *aSerial, uint16_t aFrequencyHertz) {
255  aSerial->println(F("Pronto Hex as string"));
256  aSerial->print(F("char prontoData[] = \""));
257  dumpNumber(aSerial, aFrequencyHertz > 0 ? learnedToken : learnedNonModulatedToken);
258  dumpNumber(aSerial, toFrequencyCode(aFrequencyHertz));
259  dumpNumber(aSerial, (decodedIRData.rawlen + 1) / 2);
260  dumpNumber(aSerial, 0);
261  uint16_t timebase = toTimebase(aFrequencyHertz);
262  compensateAndDumpSequence(aSerial, &decodedIRData.rawDataPtr->rawbuf[1], decodedIRData.rawlen - 1, timebase); // skip leading space
263  aSerial->println("\";");
264 }
265 
266 /*
267  * Functions for dumping Pronto to a String. This is not very time and space efficient
268  * and can lead to resource problems especially on small processors like AVR's
269  */
270 
271 static bool dumpDigit(String *aString, uint16_t number) {
272  aString->concat(hexDigit(number));
273  return number;
274 }
275 
276 static size_t dumpNumber(String *aString, uint16_t number) {
277 
278  size_t size = 0;
279 
280  for (uint16_t i = 0; i < digitsInProntoNumber; i++) {
281  uint16_t shifts = bitsInHexadecimal * (digitsInProntoNumber - 1 - i);
282  size += dumpDigit(aString, (number >> shifts) & hexMask);
283  }
284  aString->concat(' ');
285  size++;
286 
287  return size;
288 }
289 
290 /*
291  * Compensate received values by MARK_EXCESS_MICROS, like it is done for decoding!
292  */
293 static size_t dumpDuration(String *aString, uint32_t duration, uint16_t timebase) {
294  return dumpNumber(aString, (duration + timebase / 2) / timebase);
295 }
296 
297 static size_t compensateAndDumpSequence(String *aString, const volatile uint16_t *data, size_t length, uint16_t timebase) {
298 
299  size_t size = 0;
300 
301  for (size_t i = 0; i < length; i++) {
302  uint32_t tDuration = data[i] * MICROS_PER_TICK;
303  if (i & 1) {
304  // Mark
305  tDuration -= getMarkExcessMicros();
306  } else {
307  tDuration += getMarkExcessMicros();
308  }
309  size += dumpDuration(aString, tDuration, timebase);
310  }
311 
312  // append minimum gap
313  size += dumpDuration(aString, PRONTO_DEFAULT_GAP, timebase);
314 
315  return size;
316 }
317 
318 /*
319  * Writes Pronto HEX to a String object.
320  * Returns the amount of characters added to the string.(360 characters for a NEC code!)
321  */
322 size_t IRrecv::compensateAndStorePronto(String *aString, uint16_t frequency) {
323 
324  size_t size = 0;
325  uint16_t timebase = toTimebase(frequency);
326 
327  size += dumpNumber(aString, frequency > 0 ? learnedToken : learnedNonModulatedToken);
328  size += dumpNumber(aString, toFrequencyCode(frequency));
329  size += dumpNumber(aString, (decodedIRData.rawlen + 1) / 2);
330  size += dumpNumber(aString, 0);
332  timebase); // skip leading space
333 
334  return size;
335 }
336 
338 #if defined(LOCAL_DEBUG)
339 #undef LOCAL_DEBUG
340 #endif
341 #endif // _IR_PRONTO_HPP
MICROS_PER_TICK
#define MICROS_PER_TICK
microseconds per clock interrupt tick
Definition: IRremote.hpp:249
compensateAndDumpSequence
static void compensateAndDumpSequence(Print *aSerial, const volatile uint16_t *data, size_t length, uint16_t timebase)
Definition: ir_Pronto.hpp:232
IRsend::aNumberOfRepeats
void int_fast8_t aNumberOfRepeats
Definition: IRremoteInt.h:520
MICROS_IN_ONE_MILLI
#define MICROS_IN_ONE_MILLI
Definition: IRremote.hpp:254
IRrecv::compensateAndStorePronto
size_t compensateAndStorePronto(String *aString, uint16_t frequency=38000U)
Definition: ir_Pronto.hpp:322
IRData::rawlen
uint_fast8_t rawlen
counter of entries in rawbuf
Definition: IRProtocol.h:123
irparams_struct::rawbuf
uint16_t rawbuf[RAW_BUFFER_LENGTH]
raw data / tick counts per mark/space, first entry is the length of the gap between previous and curr...
Definition: IRremoteInt.h:113
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:129
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:254
dumpDuration
static void dumpDuration(Print *aSerial, uint32_t duration, uint16_t timebase)
Definition: ir_Pronto.hpp:225
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:213
IRrecv::decodedIRData
IRData decodedIRData
Definition: IRremoteInt.h:321
hexDigit
static char hexDigit(uint16_t x)
Definition: ir_Pronto.hpp:209
dumpNumber
static void dumpNumber(Print *aSerial, uint16_t number)
Definition: ir_Pronto.hpp:217
getMarkExcessMicros
int getMarkExcessMicros()
Getter function for MARK_EXCESS_MICROS.
Definition: IRReceive.hpp:1149
toTimebase
static uint16_t toTimebase(uint16_t frequency)
Definition: ir_Pronto.hpp:201
IRsend::sendPronto
void sendPronto(const __FlashStringHelper *str, int_fast8_t aNumberOfRepeats=NO_REPEATS)
Definition: ir_Pronto.hpp:190
effectiveFrequency
static uint16_t effectiveFrequency(uint16_t frequency)
Definition: ir_Pronto.hpp:197
toFrequencyCode
static uint16_t toFrequencyCode(uint16_t frequency)
Definition: ir_Pronto.hpp:205