IRremote
irPronto.cpp
Go to the documentation of this file.
1 #define TEST 0
2 #if TEST
3 # define SEND_PRONTO 1
4 #else
5 # include "IRremote.h"
6 #endif
7 
8 #if SEND_PRONTO
9 
10 //******************************************************************************
11 #if TEST
12 # include <stdio.h>
13 void enableIROut(int freq) {
14  printf("\nFreq = %d KHz\n", freq);
15 }
16 void mark(int t) {
17  printf("+%d,", t);
18 }
19 void space(int t) {
20  printf("-%d, ", t);
21 }
22 #endif // TEST
23 
24 //+=============================================================================
25 // Check for a valid hex digit
26 //
27 bool ishex(char ch) {
28  return (((ch >= '0') && (ch <= '9')) || ((ch >= 'A') && (ch <= 'F')) || ((ch >= 'a') && (ch <= 'f'))) ? true : false;
29 }
30 
31 //+=============================================================================
32 // Check for a valid "blank" ... '\0' is a valid "blank"
33 //
34 bool isblank(char ch) {
35  return ((ch == ' ') || (ch == '\t') || (ch == '\0')) ? true : false;
36 }
37 
38 //+=============================================================================
39 // Bypass spaces
40 //
41 void byp(char** pcp) {
42  while (isblank(**pcp))
43  (*pcp)++;
44 }
45 
46 //+=============================================================================
47 // Hex-to-Byte : Decode a hex digit
48 // We assume the character has already been validated
49 //
50 uint8_t htob(char ch) {
51  if ((ch >= '0') && (ch <= '9')) {
52  return ch - '0';
53  }
54  if ((ch >= 'A') && (ch <= 'F')) {
55  return ch - 'A' + 10;
56  }
57  if ((ch >= 'a') && (ch <= 'f')) {
58  return ch - 'a' + 10;
59  }
60  return 0;
61 }
62 
63 //+=============================================================================
64 // Hex-to-Word : Decode a block of 4 hex digits
65 // We assume the string has already been validated
66 // and the pointer being passed points at the start of a block of 4 hex digits
67 //
68 uint16_t htow(char* cp) {
69  return ((htob(cp[0]) << 12) | (htob(cp[1]) << 8) | (htob(cp[2]) << 4) | (htob(cp[3])));
70 }
71 
72 //+=============================================================================
73 //
74 bool sendPronto(char* s, bool repeat, bool fallback) {
75  int i;
76  int len;
77  int skip;
78  char* cp;
79  uint16_t freq; // Frequency in KHz
80  uint8_t usec; // pronto uSec/tick
81  uint8_t once;
82  uint8_t rpt;
83 
84  // Validate the string
85  for (cp = s; *cp; cp += 4) {
86  byp(&cp);
87  if (!ishex(cp[0]) || !ishex(cp[1]) || !ishex(cp[2]) || !ishex(cp[3]) || !isblank(cp[4])) {
88  return false;
89  }
90  }
91 
92  // We will use cp to traverse the string
93  cp = s;
94 
95  // Check mode = Oscillated/Learned
96  byp(&cp);
97  if (htow(cp) != 0000) {
98  return false;
99  }
100  cp += 4;
101 
102  // Extract & set frequency
103  byp(&cp);
104  freq = (int) (1000000 / (htow(cp) * 0.241246)); // Rounding errors will occur, tolerance is +/- 10%
105  usec = (int) (((1.0 / freq) * 1000000) + 0.5); // Another rounding error, thank Cod for analogue electronics
106  freq /= 1000; // This will introduce a(nother) rounding error which we do not want in the usec calcualtion
107  cp += 4;
108 
109  // Get length of "once" code
110  byp(&cp);
111  once = htow(cp);
112  cp += 4;
113 
114  // Get length of "repeat" code
115  byp(&cp);
116  rpt = htow(cp);
117  cp += 4;
118 
119  // Which code are we sending?
120  if (fallback) { // fallback on the "other" code if "this" code is not present
121  if (!repeat) { // requested 'once'
122  if (once) {
123  len = once * 2, skip = 0;
124  } // if once exists send it
125  else {
126  len = rpt * 2, skip = 0;
127  } // else send repeat code
128  } else { // requested 'repeat'
129  if (rpt) {
130  len = rpt * 2, skip = 0;
131  } // if rpt exists send it
132  else {
133  len = once * 2, skip = 0;
134  } // else send once code
135  }
136  } else { // Send what we asked for, do not fallback if the code is empty!
137  if (!repeat) {
138  len = once * 2, skip = 0;
139  } // 'once' starts at 0
140  else {
141  len = rpt * 2, skip = once;
142  } // 'repeat' starts where 'once' ends
143  }
144 
145  // Skip to start of code
146  for (i = 0; i < skip; i++, cp += 4) {
147  byp(&cp);
148  }
149 
150  // Send code
151  enableIROut(freq);
152  for (i = 0; i < len; i++) {
153  byp(&cp);
154  if (i & 1) {
155  space(htow(cp) * usec);
156  } else {
157  mark(htow(cp) * usec);
158  }
159  cp += 4;
160  }
161  return true;
162 }
163 
164 //+=============================================================================
165 #if TEST
166 
167 #define PRONTO_ONCE false
168 #define PRONTO_REPEAT true
169 #define PRONTO_FALLBACK true
170 #define PRONTO_NOFALLBACK false
171 
172 int main() {
173  char prontoTest[] = "0000 0070 0000 0032 0080 0040 0010 0010 0010 0030 " // 10
174  "0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 "// 20
175  "0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 "// 30
176  "0010 0010 0010 0030 0010 0010 0010 0010 0010 0010 "// 40
177  "0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 "// 50
178  "0010 0010 0010 0030 0010 0010 0010 0010 0010 0010 "// 60
179  "0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 "// 70
180  "0010 0010 0010 0030 0010 0010 0010 0030 0010 0010 "// 80
181  "0010 0010 0010 0030 0010 0010 0010 0010 0010 0030 "// 90
182  "0010 0010 0010 0030 0010 0010 0010 0010 0010 0030 "// 100
183  "0010 0030 0010 0aa6";// 104
184 
185  sendPronto(prontoTest, PRONTO_ONCE, PRONTO_FALLBACK); // once code
186  sendPronto(prontoTest, PRONTO_REPEAT, PRONTO_FALLBACK); // repeat code
187  sendPronto(prontoTest, PRONTO_ONCE, PRONTO_NOFALLBACK); // once code
188  sendPronto(prontoTest, PRONTO_REPEAT, PRONTO_NOFALLBACK); // repeat code
189 
190  return 0;
191 }
192 
193 #endif // TEST
194 
195 #endif // SEND_PRONTO
196 
197 #if 0
198 //******************************************************************************
199 // Sources:
200 // http://www.remotecentral.com/features/irdisp2.htm
201 // http://www.hifi-remote.com/wiki/index.php?title=Working_With_Pronto_Hex
202 //******************************************************************************
203 
204 #include <stdint.h>
205 #include <stdio.h>
206 
207 #define IRPRONTO
208 #include "IRremoteInt.h" // The Arduino IRremote library defines USECPERTICK
209 
210 //------------------------------------------------------------------------------
211 // Source: https://www.google.co.uk/search?q=DENON+MASTER+IR+Hex+Command+Sheet
212 // -> http://assets.denon.com/documentmaster/us/denon%20master%20ir%20hex.xls
213 //
214 char prontoTest[] = "0000 0070 0000 0032 0080 0040 0010 0010 0010 0030 " // 10
215  "0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 "// 20
216  "0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 "// 30
217  "0010 0010 0010 0030 0010 0010 0010 0010 0010 0010 "// 40
218  "0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 "// 50
219  "0010 0010 0010 0030 0010 0010 0010 0010 0010 0010 "// 60
220  "0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 "// 70
221  "0010 0010 0010 0030 0010 0010 0010 0030 0010 0010 "// 80
222  "0010 0010 0010 0030 0010 0010 0010 0010 0010 0030 "// 90
223  "0010 0010 0010 0030 0010 0010 0010 0010 0010 0030 "// 100
224  "0010 0030 0010 0aa6";// 104
225 
226 //------------------------------------------------------------------------------
227 // This is the longest code we can support
228 #define CODEMAX 200
229 
230 //------------------------------------------------------------------------------
231 // This is the data we pull out of the pronto code
232 typedef struct {
233  int freq; // Carrier frequency (in Hz)
234  int usec; // uSec per tick (based on freq)
235 
236  int codeLen; // Length of code
237  uint16_t code[CODEMAX]; // Code in hex
238 
239  int onceLen; // Length of "once" transmit
240  uint16_t* once; // Pointer to start within 'code'
241 
242  int rptLen; // Length of "repeat" transmit
243  uint16_t* rpt; // Pointer to start within 'code'
244 } pronto_t;
245 
246 //------------------------------------------------------------------------------
247 // From what I have seen, the only time we go over 8-bits is the 'space'
248 // on the end which creates the lead-out/inter-code gap. Assuming I'm right,
249 // we can code this up as a special case and otherwise halve the size of our
250 // data!
251 // Ignoring the first four values (the config data) and the last value
252 // (the lead-out), if you find a protocol that uses values greater than 00fe
253 // we are going to have to revisit this code!
254 //
255 //
256 // So, the 0th byte will be the carrier frequency in Khz (NOT Hz)
257 // " 1st " " " " length of the "once" code
258 // " 2nd " " " " length of the "repeat" code
259 //
260 // Thereafter, odd bytes will be Mark lengths as a multiple of USECPERTICK uS
261 // even " " " Space " " " " " " "
262 //
263 // Any occurence of "FF" in either a Mark or a Space will indicate
264 // "Use the 16-bit FF value" which will also be a multiple of USECPERTICK uS
265 //
266 //
267 // As a point of comparison, the test code (prontoTest[]) is 520 bytes
268 // (yes, more than 0.5KB of our Arduino's precious 32KB) ... after conversion
269 // to pronto hex that goes down to ((520/5)*2) = 208 bytes ... once converted to
270 // our format we are down to ((208/2) -1 -1 +2) = 104 bytes
271 //
272 // In fariness this is still very memory-hungry
273 // ...As a rough guide:
274 // 10 codes cost 1K of memory (this will vary depending on the protocol).
275 //
276 // So if you're building a complex remote control, you will probably need to
277 // keep the codes on an external memory device (not in the Arduino sketch) and
278 // load them as you need them. Hmmm.
279 //
280 // This dictates that "Oscillated Pronto Codes" are probably NOT the way forward
281 //
282 // For example, prontoTest[] happens to be: A 48-bit IR code in Denon format
283 // So we know it starts with 80/40 (Denon header)
284 // and ends with 10/aa6 (Denon leadout)
285 // and all (48) bits in between are either 10/10 (Denon 0)
286 // or 10/30 (Denon 1)
287 // So we could easily store this data in 1-byte ("Denon")
288 // + 1-byte (Length=48)
289 // + 6-bytes (IR code)
290 // At 8-bytes per code, we can store 128 codes in 1KB or memory - that's a lot
291 // better than the 2 (two) we started off with!
292 //
293 // And serendipitously, by reducing the amount of data, our program will run
294 // a LOT faster!
295 //
296 // Again, I repeat, even after you have spent time converting the "Oscillated
297 // Pronto Codes" in to IRremote format, it will be a LOT more memory-hungry
298 // than using sendDenon() (or whichever) ...BUT these codes are easily
299 // available on the internet, so we'll support them!
300 //
301 typedef struct {
302  uint16_t FF;
303  uint8_t code[CODEMAX];
304 } irCode_t;
305 
306 //------------------------------------------------------------------------------
307 #define DEBUGF(...) printf(__VA_ARGS__)
308 
309 //+=============================================================================
310 // String must be block of 4 hex digits separated with blanks
311 //
312 bool validate(char* cp, int* len) {
313  for (*len = 0; *cp; (*len)++, cp += 4) {
314  byp(&cp);
315  if (!ishex(cp[0]) || !ishex(cp[1]) || !ishex(cp[2]) || !ishex(cp[3]) || !isblank(cp[4]))
316  return false;
317  }
318 
319  return true;
320 }
321 
322 //+=============================================================================
323 // Hex-to-Byte : Decode a hex digit
324 // We assume the character has already been validated
325 //
326 uint8_t htob(char ch) {
327  if ((ch >= '0') && (ch <= '9')) {
328  return ch - '0';
329  }
330  if ((ch >= 'A') && (ch <= 'F')) {
331  return ch - 'A' + 10;
332  }
333  if ((ch >= 'a') && (ch <= 'f')) {
334  return ch - 'a' + 10;
335  }
336 }
337 
338 //+=============================================================================
339 // Hex-to-Word : Decode a block of 4 hex digits
340 // We assume the string has already been validated
341 // and the pointer being passed points at the start of a block of 4 hex digits
342 //
343 uint16_t htow(char* cp) {
344  return ((htob(cp[0]) << 12) | (htob(cp[1]) << 8) | (htob(cp[2]) << 4) | (htob(cp[3])));
345 }
346 
347 //+=============================================================================
348 // Convert the pronto string in to data
349 //
350 bool decode(char* s, pronto_t* p, irCode_t* ir) {
351  int i, len;
352  char* cp;
353 
354  // Validate the Pronto string
355  if (!validate(s, &p->codeLen)) {
356  DEBUGF("Invalid pronto string\n");
357  return false;
358  }
359  DEBUGF("Found %d hex codes\n", p->codeLen);
360 
361  // Allocate memory to store the decoded string
362  //if (!(p->code = malloc(p->len))) {
363  // DEBUGF("Memory allocation failed\n");
364  // return false ;
365  //}
366 
367  // Check in case our code is too long
368  if (p->codeLen > CODEMAX) {
369  DEBUGF("Code too long, edit CODEMAX and recompile\n");
370  return false;
371  }
372 
373  // Decode the string
374  cp = s;
375  for (i = 0; i < p->codeLen; i++, cp += 4) {
376  byp(&cp);
377  p->code[i] = htow(cp);
378  }
379 
380  // Announce our findings
381  DEBUGF("Input: |%s|\n", s);
382  DEBUGF("Found: |");
383  for (i = 0; i < p->codeLen; i++) {
384  DEBUGF("%04x ", p->code[i]);
385  }
386  DEBUGF("|\n");
387 
388  DEBUGF("Form [%04X] : ", p->code[0]);
389  if (p->code[0] == 0x0000) {
390  DEBUGF("Oscillated (Learned)\n");
391  } else if (p->code[0] == 0x0100) {
392  DEBUGF("Unmodulated\n");
393  } else {
394  DEBUGF("Unknown\n");
395  }
396  if (p->code[0] != 0x0000) {
397  return false; // Can only handle Oscillated
398  }
399 
400  // Calculate the carrier frequency (+/- 10%) & uSecs per pulse
401  // Pronto uses a crystal which generates a timeabse of 0.241246
402  p->freq = (int) (1000000 / (p->code[1] * 0.241246));
403  p->usec = (int) (((1.0 / p->freq) * 1000000) + 0.5);
404  ir->code[0] = p->freq / 1000;
405  DEBUGF("Freq [%04X] : %d Hz (%d uS/pluse) -> %d KHz\n", p->code[1], p->freq, p->usec, ir->code[0]);
406 
407  // Set the length & start pointer for the "once" code
408  p->onceLen = p->code[2];
409  p->once = &p->code[4];
410  ir->code[1] = p->onceLen;
411  DEBUGF("Once [%04X] : %d\n", p->code[2], p->onceLen);
412 
413  // Set the length & start pointer for the "repeat" code
414  p->rptLen = p->code[3];
415  p->rpt = &p->code[4 + p->onceLen];
416  ir->code[2] = p->rptLen;
417  DEBUGF("Rpt [%04X] : %d\n", p->code[3], p->rptLen);
418 
419  // Check everything tallies
420  if (1 + 1 + 1 + 1 + (p->onceLen * 2) + (p->rptLen * 2) != p->codeLen) {
421  DEBUGF("Bad code length\n");
422  return false;
423  }
424 
425  // Convert the IR data to our new format
426  ir->FF = p->code[p->codeLen - 1];
427 
428  len = (p->onceLen * 2) + (p->rptLen * 2);
429  DEBUGF("Encoded: |");
430  for (i = 0; i < len; i++) {
431  if (p->code[i + 4] == ir->FF) {
432  ir->code[i + 3] = 0xFF;
433  } else if (p->code[i + 4] > 0xFE) {
434  DEBUGF("\n%04X : Mark/Space overflow\n", p->code[i + 4]);
435  return false;
436  } else {
437  ir->code[i + 3] = (p->code[i + 4] * p->usec) / USECPERTICK;
438  }
439  DEBUGF("%s%d", !i ? "" : (i & 1 ? "," : ", "), ir->code[i + 3]);
440  }
441  DEBUGF("|\n");
442 
443  ir->FF = (ir->FF * p->usec) / USECPERTICK;
444  DEBUGF("FF -> %d\n", ir->FF);
445 
446  return true;
447 }
448 
449 //+=============================================================================
450 //
451 void irDump(irCode_t* ir) {
452  int i, len;
453 
454  printf("uint8_t buttonName[%d] = {", len);
455 
456  printf("%d,%d, ", (ir->FF >> 8), ir->FF & 0xFF);
457  printf("%d,%d,%d, ", ir->code[0], ir->code[1], ir->code[2]);
458 
459  len = (ir->code[1] * 2) + (ir->code[2] * 2);
460  for (i = 0; i < len; i++) {
461  printf("%s%d", !i ? "" : (i & 1 ? "," : ", "), ir->code[i + 3]);
462  }
463 
464  printf("};\n");
465 
466 }
467 
468 //+=============================================================================
469 //
470 int main() {
471  pronto_t pCode;
472  irCode_t irCode;
473 
474  decode(prontoTest, &pCode, &irCode);
475 
476  irDump(&irCode);
477 
478  return 0;
479 }
480 
481 #endif //0
PRONTO_NOFALLBACK
#define PRONTO_NOFALLBACK
Definition: IRremote.h:106
PRONTO_REPEAT
#define PRONTO_REPEAT
Definition: IRremote.h:104
USECPERTICK
#define USECPERTICK
Definition: IRremoteBoardDefs.h:166
IRremote.h
Public API to the library.
PRONTO_ONCE
#define PRONTO_ONCE
Definition: IRremote.h:103
PRONTO_FALLBACK
#define PRONTO_FALLBACK
Definition: IRremote.h:105
IRremoteInt.h