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  * Pronto is the standard for the professional audio and video hardware market.
6  *
7  * See http://www.harctoolbox.org/Glossary.html#ProntoSemantics
8  * Pronto database http://www.remotecentral.com/search.htm
9  *
10  * This file is part of Arduino-IRremote https://github.com/Arduino-IRremote/Arduino-IRremote.
11  *
12  ************************************************************************************
13  * MIT License
14  *
15  * Copyright (c) 2020-2025 Bengt Martensson, Armin Joachimsmeyer
16  *
17  * Permission is hereby granted, free of charge, to any person obtaining a copy
18  * of this software and associated documentation files (the "Software"), to deal
19  * in the Software without restriction, including without limitation the rights
20  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
21  * copies of the Software, and to permit persons to whom the Software is furnished
22  * to do so, subject to the following conditions:
23  *
24  * The above copyright notice and this permission notice shall be included in all
25  * copies or substantial portions of the Software.
26  *
27  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
28  * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
29  * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
30  * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
31  * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
32  * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
33  *
34  ************************************************************************************
35  */
36 #ifndef _IR_PRONTO_HPP
37 #define _IR_PRONTO_HPP
38 
39 #include "LocalDebugLevelStart.h"
40 
45 // DO NOT EXPORT from this file
47 static const uint16_t learnedToken = 0x0000U;
48 static const uint16_t learnedNonModulatedToken = 0x0100U;
49 static const uint16_t bitsInHexadecimal = 4U;
50 static const uint16_t digitsInProntoNumber = 4U;
51 static const uint16_t numbersInPreamble = 4U;
52 static const uint16_t hexMask = 0xFU;
53 static const uint32_t referenceFrequency = 4145146UL;
54 static const uint16_t fallbackFrequency = 64767U; // To use with frequency = 0;
55 static const uint32_t microsecondsInSeconds = 1000000UL;
56 static const uint16_t PRONTO_DEFAULT_GAP = 45000;
58 
59 static uint16_t toFrequencyKHz(uint16_t code) {
60  return ((referenceFrequency / code) + 500) / 1000;
61 }
62 
63 /*
64  * Parse the string given as Pronto Hex, and send it a number of times given as argument.
65  * The first number denotes the type of the signal. 0000 denotes a raw IR signal with modulation,
66  // The second number denotes a frequency code
67  */
68 void IRsend::sendPronto(const uint16_t *data, uint16_t length, int_fast8_t aNumberOfRepeats) {
69  uint16_t timebase = (microsecondsInSeconds * data[1] + referenceFrequency / 2) / referenceFrequency;
70  uint16_t khz;
71  switch (data[0]) {
72  case learnedToken: // normal, "learned"
73  khz = toFrequencyKHz(data[1]);
74  break;
75  case learnedNonModulatedToken: // non-demodulated, "learned"
76  khz = 0U;
77  break;
78  default:
79  return; // There are other types, but they are not handled yet.
80  }
81  uint16_t intros = 2 * data[2];
82  uint16_t repeats = 2 * data[3];
83 
84  DEBUG_PRINT(F("sendPronto intros="));
85  DEBUG_PRINT(intros);
86  DEBUG_PRINT(F(" repeats="));
87  DEBUG_PRINTLN(repeats);
88 
89  if (numbersInPreamble + intros + repeats != length) { // inconsistent sizes
90  return;
91  }
92 
93  /*
94  * Generate a new microseconds timing array for sendRaw.
95  * If recorded by IRremote, intro contains the whole IR data and repeat is empty
96  */
97  uint16_t durations[intros + repeats];
98  for (uint16_t i = 0; i < intros + repeats; i++) {
99  uint32_t duration = ((uint32_t) data[i + numbersInPreamble]) * timebase;
100  durations[i] = (uint16_t)((duration <= UINT16_MAX) ? duration : UINT16_MAX);
101  }
102 
103  /*
104  * Send the intro. intros is even.
105  * Do not send the trailing space here, send it if repeats are requested
106  */
107  if (intros >= 2) {
108  sendRaw(durations, intros - 1, khz);
109  }
110 
111  if (repeats == 0 || aNumberOfRepeats == 0) {
112  // only send intro once
113  return;
114  }
115 
116  /*
117  * Now send the trailing space/gap of the intro and all the repeats
118  */
119  if (intros >= 2) {
120  delay(durations[intros - 1] / MICROS_IN_ONE_MILLI); // equivalent to space(durations[intros - 1]); but allow bigger values for the gap
121  }
122  for (int i = 0; i < aNumberOfRepeats; i++) {
123  sendRaw(durations + intros, repeats - 1, khz);
124  if ((i + 1) < aNumberOfRepeats) { // skip last trailing space/gap, see above
125  delay(durations[intros + repeats - 1] / MICROS_IN_ONE_MILLI);
126  }
127  }
128 }
129 
147 void IRsend::sendPronto(const char *str, int_fast8_t aNumberOfRepeats) {
148  size_t len = strlen(str) / (digitsInProntoNumber + 1) + 1;
149  uint16_t data[len];
150  const char *p = str;
151  char *endptr[1];
152  for (uint16_t i = 0; i < len; i++) {
153  long x = strtol(p, endptr, 16);
154  if (x == 0 && i >= numbersInPreamble) {
155  // Alignment error?, bail immediately (often right result).
156  len = i;
157  break;
158  }
159  data[i] = static_cast<uint16_t>(x); // If input is conforming, there can be no overflow!
160  p = *endptr;
161  }
162  sendPronto(data, len, aNumberOfRepeats);
163 }
164 
165 #if defined(__AVR__)
166 
171 //far pointer (? for ATMega2560 etc.)
172 void IRsend::sendPronto_PF(uint_farptr_t str, int_fast8_t aNumberOfRepeats) {
173  size_t len = strlen_PF(str);
174  char work[len + 1];
175  strcpy_PF(work, str); // We know that string including terminating character fits in work
177 }
178 
179 //standard pointer
180 void IRsend::sendPronto_P(const char *str, int_fast8_t aNumberOfRepeats) {
181  size_t len = strlen_P(str);
182  char work[len + 1];
183  strcpy_P(work, str);
185 }
186 #endif
187 
188 /*
189  * Copy flash data to ram buffer in stack
190  */
191 void IRsend::sendPronto(const __FlashStringHelper *str, int_fast8_t aNumberOfRepeats) {
192  size_t len = strlen_P(reinterpret_cast<const char*>(str));
193  char work[len + 1];
194  strcpy_P(work, reinterpret_cast<const char*>(str));
195  return sendPronto(work, aNumberOfRepeats);
196 }
197 
198 static uint16_t effectiveFrequency(uint16_t frequency) {
199  return frequency > 0 ? frequency : fallbackFrequency;
200 }
201 
202 static uint16_t toTimebase(uint16_t frequency) {
203  return microsecondsInSeconds / effectiveFrequency(frequency);
204 }
205 
206 static uint16_t toFrequencyCode(uint16_t frequency) {
207  return referenceFrequency / effectiveFrequency(frequency);
208 }
209 
210 static char DigitToHex(uint8_t x) {
211  return (char) (x <= 9 ? ('0' + x) : ('A' + (x - 10)));
212 }
213 
214 static void dumpDigitHex(Print *aSerial, uint8_t number) {
215  aSerial->print(DigitToHex(number));
216 }
217 
218 static void dumpNumberHex(Print *aSerial, uint16_t number) {
219  // Loop through all 4 nibbles
220  for (uint16_t i = 0; i < digitsInProntoNumber; i++) {
221  uint16_t shifts = bitsInHexadecimal * (digitsInProntoNumber - 1 - i);
222  dumpDigitHex(aSerial, (number >> shifts) & hexMask);
223  }
224  aSerial->print(' ');
225 }
226 
227 static void dumpDurationHex(Print *aSerial, uint32_t duration, uint16_t timebase) {
228  dumpNumberHex(aSerial, (duration + timebase / 2) / timebase);
229 }
230 
231 /*
232  * Compensate received values by MARK_EXCESS_MICROS, like it is done for decoding!
233  */
234 static void compensateAndDumpSequence(Print *aSerial, const volatile IRRawbufType *data, size_t length, uint16_t timebase) {
235  for (size_t i = 0; i < length; i++) {
236  uint32_t tDuration = data[i] * MICROS_PER_TICK;
237  if (i & 1) {
238  // Mark
239  tDuration -= getMarkExcessMicros();
240  } else {
241  tDuration += getMarkExcessMicros();
242  }
243  dumpDurationHex(aSerial, tDuration, timebase);
244  }
245 
246  // append a gap
247  dumpDurationHex(aSerial, PRONTO_DEFAULT_GAP, timebase);
248 }
249 
257 void IRrecv::compensateAndPrintIRResultAsPronto(Print *aSerial, uint16_t aFrequencyHertz) {
258  aSerial->println(F("Pronto Hex as string without repeat sequence"));
259  aSerial->print(F("char prontoData[] = \""));
260  dumpNumberHex(aSerial, aFrequencyHertz > 0 ? learnedToken : learnedNonModulatedToken);
261  dumpNumberHex(aSerial, toFrequencyCode(aFrequencyHertz));
262  dumpNumberHex(aSerial, (decodedIRData.rawlen + 1) / 2);
263  dumpNumberHex(aSerial, 0); // no repeat data
264  uint16_t timebase = toTimebase(aFrequencyHertz);
265  compensateAndDumpSequence(aSerial, &irparams.rawbuf[1], decodedIRData.rawlen - 1, timebase); // skip leading space
266  aSerial->println(F("\";"));
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 dumpDigitHex(String *aString, uint8_t number) {
275  aString->concat(DigitToHex(number));
276  return number;
277 }
278 
279 static size_t dumpNumberHex(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 += dumpDigitHex(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 dumpDurationHex(String *aString, uint32_t duration, uint16_t timebase) {
297  return dumpNumberHex(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 += dumpDurationHex(aString, tDuration, timebase);
313  }
314 
315  // append minimum gap
316  size += dumpDurationHex(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 += dumpNumberHex(aString, frequency > 0 ? learnedToken : learnedNonModulatedToken);
331  size += dumpNumberHex(aString, toFrequencyCode(frequency));
332  size += dumpNumberHex(aString, (decodedIRData.rawlen + 1) / 2);
333  size += dumpNumberHex(aString, 0);
334  size += compensateAndDumpSequence(aString, &irparams.rawbuf[1], decodedIRData.rawlen - 1, timebase); // skip leading space
335 
336  return size;
337 }
338 
340 #include "LocalDebugLevelEnd.h"
341 
342 #endif // _IR_PRONTO_HPP
MICROS_PER_TICK
#define MICROS_PER_TICK
microseconds per clock interrupt tick
Definition: IRremote.hpp:133
DEBUG_PRINT
#define DEBUG_PRINT(...)
Definition: LocalDebugLevelStart.h:79
IRsend::aNumberOfRepeats
void int_fast8_t aNumberOfRepeats
Definition: IRremoteInt.h:528
MICROS_IN_ONE_MILLI
#define MICROS_IN_ONE_MILLI
Definition: IRremote.hpp:217
IRrecv::compensateAndStorePronto
size_t compensateAndStorePronto(String *aString, uint16_t frequency=38000U)
Definition: ir_Pronto.hpp:325
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
LocalDebugLevelStart.h
IRsend::sendRaw
void sendRaw(const uint8_t aBufferWithTicks[], uint_fast16_t aLengthOfBuffer, uint_fast8_t aIRFrequencyKilohertz)
Sends an 8 byte tick timing array to save program memory.
Definition: IRSend.hpp:453
toFrequencyKHz
static uint16_t toFrequencyKHz(uint16_t code)
Definition: ir_Pronto.hpp:59
IRrecv::decodedIRData
IRData decodedIRData
Definition: IRremoteInt.h:401
compensateAndDumpSequence
static void compensateAndDumpSequence(Print *aSerial, const volatile IRRawbufType *data, size_t length, uint16_t timebase)
Definition: ir_Pronto.hpp:234
dumpDigitHex
static void dumpDigitHex(Print *aSerial, uint8_t number)
Definition: ir_Pronto.hpp:214
dumpDurationHex
static void dumpDurationHex(Print *aSerial, uint32_t duration, uint16_t timebase)
Definition: ir_Pronto.hpp:227
getMarkExcessMicros
int getMarkExcessMicros()
Getter function for MARK_EXCESS_MICROS.
Definition: IRReceive.hpp:1420
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:147
toTimebase
static uint16_t toTimebase(uint16_t frequency)
Definition: ir_Pronto.hpp:202
IRrecv::irparams
irparams_struct irparams
Definition: IRremoteInt.h:400
IRsend::sendPronto
void sendPronto(const __FlashStringHelper *str, int_fast8_t aNumberOfRepeats=NO_REPEATS)
Definition: ir_Pronto.hpp:191
DEBUG_PRINTLN
#define DEBUG_PRINTLN(...)
Definition: LocalDebugLevelStart.h:80
dumpNumberHex
static void dumpNumberHex(Print *aSerial, uint16_t number)
Definition: ir_Pronto.hpp:218
IRData::rawlen
IRRawlenType rawlen
Counter of entries in rawbuf of last received frame.
Definition: IRremoteInt.h:182
IRRawbufType
uint8_t IRRawbufType
Definition: IRremoteInt.h:107
effectiveFrequency
static uint16_t effectiveFrequency(uint16_t frequency)
Definition: ir_Pronto.hpp:198
toFrequencyCode
static uint16_t toFrequencyCode(uint16_t frequency)
Definition: ir_Pronto.hpp:206
DigitToHex
static char DigitToHex(uint8_t x)
Definition: ir_Pronto.hpp:210
LocalDebugLevelEnd.h