Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Dependents: Lilnija_29012017 NucleoF042K6_IRReceiver
irPronto.cpp
00001 #define TEST 0 00002 00003 #if TEST 00004 # define SEND_PRONTO 1 00005 # define PRONTO_ONCE false 00006 # define PRONTO_REPEAT true 00007 # define PRONTO_FALLBACK true 00008 # define PRONTO_NOFALLBACK false 00009 #endif 00010 00011 #if SEND_PRONTO 00012 00013 //****************************************************************************** 00014 #if TEST 00015 # include <stdio.h> 00016 void enableIROut (int freq) { printf("\nFreq = %d KHz\n", freq); } 00017 void mark (int t) { printf("+%d," , t); } 00018 void space (int t) { printf("-%d, ", t); } 00019 #else 00020 # include "IRremote.h" 00021 #endif // TEST 00022 00023 //+============================================================================= 00024 // Check for a valid hex digit 00025 // 00026 bool ishex (char ch) 00027 { 00028 return ( ((ch >= '0') && (ch <= '9')) || 00029 ((ch >= 'A') && (ch <= 'F')) || 00030 ((ch >= 'a') && (ch <= 'f')) ) ? true : false ; 00031 } 00032 00033 //+============================================================================= 00034 // Check for a valid "blank" ... '\0' is a valid "blank" 00035 // 00036 bool isblank (char ch) 00037 { 00038 return ((ch == ' ') || (ch == '\t') || (ch == '\0')) ? true : false ; 00039 } 00040 00041 //+============================================================================= 00042 // Bypass spaces 00043 // 00044 bool byp (char** pcp) 00045 { 00046 while (isblank(**pcp)) (*pcp)++ ; 00047 } 00048 00049 //+============================================================================= 00050 // Hex-to-Byte : Decode a hex digit 00051 // We assume the character has already been validated 00052 // 00053 uint8_t htob (char ch) 00054 { 00055 if ((ch >= '0') && (ch <= '9')) return ch - '0' ; 00056 if ((ch >= 'A') && (ch <= 'F')) return ch - 'A' + 10 ; 00057 if ((ch >= 'a') && (ch <= 'f')) return ch - 'a' + 10 ; 00058 } 00059 00060 //+============================================================================= 00061 // Hex-to-Word : Decode a block of 4 hex digits 00062 // We assume the string has already been validated 00063 // and the pointer being passed points at the start of a block of 4 hex digits 00064 // 00065 uint16_t htow (char* cp) 00066 { 00067 return ( (htob(cp[0]) << 12) | (htob(cp[1]) << 8) | 00068 (htob(cp[2]) << 4) | (htob(cp[3]) ) ) ; 00069 } 00070 00071 //+============================================================================= 00072 // 00073 bool sendPronto (char* s, bool repeat, bool fallback) 00074 { 00075 int i; 00076 int len; 00077 int skip; 00078 char* cp; 00079 uint16_t freq; // Frequency in KHz 00080 uint8_t usec; // pronto uSec/tick 00081 uint8_t once; 00082 uint8_t rpt; 00083 00084 // Validate the string 00085 for (cp = s; *cp; cp += 4) { 00086 byp(&cp); 00087 if ( !ishex(cp[0]) || !ishex(cp[1]) || 00088 !ishex(cp[2]) || !ishex(cp[3]) || !isblank(cp[4]) ) return false ; 00089 } 00090 00091 // We will use cp to traverse the string 00092 cp = s; 00093 00094 // Check mode = Oscillated/Learned 00095 byp(&cp); 00096 if (htow(cp) != 0000) return false; 00097 cp += 4; 00098 00099 // Extract & set frequency 00100 byp(&cp); 00101 freq = (int)(1000000 / (htow(cp) * 0.241246)); // Rounding errors will occur, tolerance is +/- 10% 00102 usec = (int)(((1.0 / freq) * 1000000) + 0.5); // Another rounding error, thank Cod for analogue electronics 00103 freq /= 1000; // This will introduce a(nother) rounding error which we do not want in the usec calcualtion 00104 cp += 4; 00105 00106 // Get length of "once" code 00107 byp(&cp); 00108 once = htow(cp); 00109 cp += 4; 00110 00111 // Get length of "repeat" code 00112 byp(&cp); 00113 rpt = htow(cp); 00114 cp += 4; 00115 00116 // Which code are we sending? 00117 if (fallback) { // fallback on the "other" code if "this" code is not present 00118 if (!repeat) { // requested 'once' 00119 if (once) len = once * 2, skip = 0 ; // if once exists send it 00120 else len = rpt * 2, skip = 0 ; // else send repeat code 00121 } else { // requested 'repeat' 00122 if (rpt) len = rpt * 2, skip = 0 ; // if rpt exists send it 00123 else len = once * 2, skip = 0 ; // else send once code 00124 } 00125 } else { // Send what we asked for, do not fallback if the code is empty! 00126 if (!repeat) len = once * 2, skip = 0 ; // 'once' starts at 0 00127 else len = rpt * 2, skip = once ; // 'repeat' starts where 'once' ends 00128 } 00129 00130 // Skip to start of code 00131 for (i = 0; i < skip; i++, cp += 4) byp(&cp) ; 00132 00133 // Send code 00134 enableIROut(freq); 00135 for (i = 0; i < len; i++) { 00136 byp(&cp); 00137 if (i & 1) space(htow(cp) * usec); 00138 else mark (htow(cp) * usec); 00139 cp += 4; 00140 } 00141 } 00142 00143 //+============================================================================= 00144 #if TEST 00145 00146 int main ( ) 00147 { 00148 char prontoTest[] = 00149 "0000 0070 0000 0032 0080 0040 0010 0010 0010 0030 " // 10 00150 "0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 " // 20 00151 "0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 " // 30 00152 "0010 0010 0010 0030 0010 0010 0010 0010 0010 0010 " // 40 00153 "0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 " // 50 00154 "0010 0010 0010 0030 0010 0010 0010 0010 0010 0010 " // 60 00155 "0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 " // 70 00156 "0010 0010 0010 0030 0010 0010 0010 0030 0010 0010 " // 80 00157 "0010 0010 0010 0030 0010 0010 0010 0010 0010 0030 " // 90 00158 "0010 0010 0010 0030 0010 0010 0010 0010 0010 0030 " // 100 00159 "0010 0030 0010 0aa6"; // 104 00160 00161 sendPronto(prontoTest, PRONTO_ONCE, PRONTO_FALLBACK); // once code 00162 sendPronto(prontoTest, PRONTO_REPEAT, PRONTO_FALLBACK); // repeat code 00163 sendPronto(prontoTest, PRONTO_ONCE, PRONTO_NOFALLBACK); // once code 00164 sendPronto(prontoTest, PRONTO_REPEAT, PRONTO_NOFALLBACK); // repeat code 00165 00166 return 0; 00167 } 00168 00169 #endif // TEST 00170 00171 #endif // SEND_PRONTO 00172 00173 00174 00175 00176 00177 00178 00179 00180 00181 00182 00183 00184 00185 00186 00187 00188 00189 00190 00191 00192 00193 00194 00195 00196 00197 00198 00199 00200 00201 00202 00203 00204 00205 00206 00207 00208 00209 00210 00211 00212 00213 00214 00215 00216 00217 00218 00219 00220 00221 00222 00223 00224 00225 00226 00227 00228 00229 00230 #if 0 00231 //****************************************************************************** 00232 // Sources: 00233 // http://www.remotecentral.com/features/irdisp2.htm 00234 // http://www.hifi-remote.com/wiki/index.php?title=Working_With_Pronto_Hex 00235 //****************************************************************************** 00236 00237 #include <stdint.h> 00238 #include <stdio.h> 00239 00240 #define IRPRONTO 00241 #include "IRremoteInt.h" // The Arduino IRremote library defines USECPERTICK 00242 00243 //------------------------------------------------------------------------------ 00244 // Source: https://www.google.co.uk/search?q=DENON+MASTER+IR+Hex+Command+Sheet 00245 // -> http://assets.denon.com/documentmaster/us/denon%20master%20ir%20hex.xls 00246 // 00247 char prontoTest[] = 00248 "0000 0070 0000 0032 0080 0040 0010 0010 0010 0030 " // 10 00249 "0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 " // 20 00250 "0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 " // 30 00251 "0010 0010 0010 0030 0010 0010 0010 0010 0010 0010 " // 40 00252 "0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 " // 50 00253 "0010 0010 0010 0030 0010 0010 0010 0010 0010 0010 " // 60 00254 "0010 0010 0010 0010 0010 0010 0010 0010 0010 0010 " // 70 00255 "0010 0010 0010 0030 0010 0010 0010 0030 0010 0010 " // 80 00256 "0010 0010 0010 0030 0010 0010 0010 0010 0010 0030 " // 90 00257 "0010 0010 0010 0030 0010 0010 0010 0010 0010 0030 " // 100 00258 "0010 0030 0010 0aa6"; // 104 00259 00260 //------------------------------------------------------------------------------ 00261 // This is the longest code we can support 00262 #define CODEMAX 200 00263 00264 //------------------------------------------------------------------------------ 00265 // This is the data we pull out of the pronto code 00266 typedef 00267 struct { 00268 int freq; // Carrier frequency (in Hz) 00269 int usec; // uSec per tick (based on freq) 00270 00271 int codeLen; // Length of code 00272 uint16_t code[CODEMAX]; // Code in hex 00273 00274 int onceLen; // Length of "once" transmit 00275 uint16_t* once; // Pointer to start within 'code' 00276 00277 int rptLen; // Length of "repeat" transmit 00278 uint16_t* rpt; // Pointer to start within 'code' 00279 } 00280 pronto_t; 00281 00282 //------------------------------------------------------------------------------ 00283 // From what I have seen, the only time we go over 8-bits is the 'space' 00284 // on the end which creates the lead-out/inter-code gap. Assuming I'm right, 00285 // we can code this up as a special case and otherwise halve the size of our 00286 // data! 00287 // Ignoring the first four values (the config data) and the last value 00288 // (the lead-out), if you find a protocol that uses values greater than 00fe 00289 // we are going to have to revisit this code! 00290 // 00291 // 00292 // So, the 0th byte will be the carrier frequency in Khz (NOT Hz) 00293 // " 1st " " " " length of the "once" code 00294 // " 2nd " " " " length of the "repeat" code 00295 // 00296 // Thereafter, odd bytes will be Mark lengths as a multiple of USECPERTICK uS 00297 // even " " " Space " " " " " " " 00298 // 00299 // Any occurence of "FF" in either a Mark or a Space will indicate 00300 // "Use the 16-bit FF value" which will also be a multiple of USECPERTICK uS 00301 // 00302 // 00303 // As a point of comparison, the test code (prontoTest[]) is 520 bytes 00304 // (yes, more than 0.5KB of our Arduino's precious 32KB) ... after conversion 00305 // to pronto hex that goes down to ((520/5)*2) = 208 bytes ... once converted to 00306 // our format we are down to ((208/2) -1 -1 +2) = 104 bytes 00307 // 00308 // In fariness this is still very memory-hungry 00309 // ...As a rough guide: 00310 // 10 codes cost 1K of memory (this will vary depending on the protocol). 00311 // 00312 // So if you're building a complex remote control, you will probably need to 00313 // keep the codes on an external memory device (not in the Arduino sketch) and 00314 // load them as you need them. Hmmm. 00315 // 00316 // This dictates that "Oscillated Pronto Codes" are probably NOT the way forward 00317 // 00318 // For example, prontoTest[] happens to be: A 48-bit IR code in Denon format 00319 // So we know it starts with 80/40 (Denon header) 00320 // and ends with 10/aa6 (Denon leadout) 00321 // and all (48) bits in between are either 10/10 (Denon 0) 00322 // or 10/30 (Denon 1) 00323 // So we could easily store this data in 1-byte ("Denon") 00324 // + 1-byte (Length=48) 00325 // + 6-bytes (IR code) 00326 // At 8-bytes per code, we can store 128 codes in 1KB or memory - that's a lot 00327 // better than the 2 (two) we started off with! 00328 // 00329 // And serendipitously, by reducing the amount of data, our program will run 00330 // a LOT faster! 00331 // 00332 // Again, I repeat, even after you have spent time converting the "Oscillated 00333 // Pronto Codes" in to IRremote format, it will be a LOT more memory-hungry 00334 // than using sendDenon() (or whichever) ...BUT these codes are easily 00335 // available on the internet, so we'll support them! 00336 // 00337 typedef 00338 struct { 00339 uint16_t FF; 00340 uint8_t code[CODEMAX]; 00341 } 00342 irCode_t; 00343 00344 //------------------------------------------------------------------------------ 00345 #define DEBUGF(...) printf(__VA_ARGS__) 00346 00347 //+============================================================================= 00348 // String must be block of 4 hex digits separated with blanks 00349 // 00350 bool validate (char* cp, int* len) 00351 { 00352 for (*len = 0; *cp; (*len)++, cp += 4) { 00353 byp(&cp); 00354 if ( !ishex(cp[0]) || !ishex(cp[1]) || 00355 !ishex(cp[2]) || !ishex(cp[3]) || !isblank(cp[4]) ) return false ; 00356 } 00357 00358 return true; 00359 } 00360 00361 //+============================================================================= 00362 // Hex-to-Byte : Decode a hex digit 00363 // We assume the character has already been validated 00364 // 00365 uint8_t htob (char ch) 00366 { 00367 if ((ch >= '0') && (ch <= '9')) return ch - '0' ; 00368 if ((ch >= 'A') && (ch <= 'F')) return ch - 'A' + 10 ; 00369 if ((ch >= 'a') && (ch <= 'f')) return ch - 'a' + 10 ; 00370 } 00371 00372 //+============================================================================= 00373 // Hex-to-Word : Decode a block of 4 hex digits 00374 // We assume the string has already been validated 00375 // and the pointer being passed points at the start of a block of 4 hex digits 00376 // 00377 uint16_t htow (char* cp) 00378 { 00379 return ( (htob(cp[0]) << 12) | (htob(cp[1]) << 8) | 00380 (htob(cp[2]) << 4) | (htob(cp[3]) ) ) ; 00381 } 00382 00383 //+============================================================================= 00384 // Convert the pronto string in to data 00385 // 00386 bool decode (char* s, pronto_t* p, irCode_t* ir) 00387 { 00388 int i, len; 00389 char* cp; 00390 00391 // Validate the Pronto string 00392 if (!validate(s, &p->codeLen)) { 00393 DEBUGF("Invalid pronto string\n"); 00394 return false ; 00395 } 00396 DEBUGF("Found %d hex codes\n", p->codeLen); 00397 00398 // Allocate memory to store the decoded string 00399 //if (!(p->code = malloc(p->len))) { 00400 // DEBUGF("Memory allocation failed\n"); 00401 // return false ; 00402 //} 00403 00404 // Check in case our code is too long 00405 if (p->codeLen > CODEMAX) { 00406 DEBUGF("Code too long, edit CODEMAX and recompile\n"); 00407 return false ; 00408 } 00409 00410 // Decode the string 00411 cp = s; 00412 for (i = 0; i < p->codeLen; i++, cp += 4) { 00413 byp(&cp); 00414 p->code[i] = htow(cp); 00415 } 00416 00417 // Announce our findings 00418 DEBUGF("Input: |%s|\n", s); 00419 DEBUGF("Found: |"); 00420 for (i = 0; i < p->codeLen; i++) DEBUGF("%04x ", p->code[i]) ; 00421 DEBUGF("|\n"); 00422 00423 DEBUGF("Form [%04X] : ", p->code[0]); 00424 if (p->code[0] == 0x0000) DEBUGF("Oscillated (Learned)\n"); 00425 else if (p->code[0] == 0x0100) DEBUGF("Unmodulated\n"); 00426 else DEBUGF("Unknown\n"); 00427 if (p->code[0] != 0x0000) return false ; // Can only handle Oscillated 00428 00429 // Calculate the carrier frequency (+/- 10%) & uSecs per pulse 00430 // Pronto uses a crystal which generates a timeabse of 0.241246 00431 p->freq = (int)(1000000 / (p->code[1] * 0.241246)); 00432 p->usec = (int)(((1.0 / p->freq) * 1000000) + 0.5); 00433 ir->code[0] = p->freq / 1000; 00434 DEBUGF("Freq [%04X] : %d Hz (%d uS/pluse) -> %d KHz\n", 00435 p->code[1], p->freq, p->usec, ir->code[0]); 00436 00437 // Set the length & start pointer for the "once" code 00438 p->onceLen = p->code[2]; 00439 p->once = &p->code[4]; 00440 ir->code[1] = p->onceLen; 00441 DEBUGF("Once [%04X] : %d\n", p->code[2], p->onceLen); 00442 00443 // Set the length & start pointer for the "repeat" code 00444 p->rptLen = p->code[3]; 00445 p->rpt = &p->code[4 + p->onceLen]; 00446 ir->code[2] = p->rptLen; 00447 DEBUGF("Rpt [%04X] : %d\n", p->code[3], p->rptLen); 00448 00449 // Check everything tallies 00450 if (1 + 1 + 1 + 1 + (p->onceLen * 2) + (p->rptLen * 2) != p->codeLen) { 00451 DEBUGF("Bad code length\n"); 00452 return false; 00453 } 00454 00455 // Convert the IR data to our new format 00456 ir->FF = p->code[p->codeLen - 1]; 00457 00458 len = (p->onceLen * 2) + (p->rptLen * 2); 00459 DEBUGF("Encoded: |"); 00460 for (i = 0; i < len; i++) { 00461 if (p->code[i+4] == ir->FF) { 00462 ir->code[i+3] = 0xFF; 00463 } else if (p->code[i+4] > 0xFE) { 00464 DEBUGF("\n%04X : Mark/Space overflow\n", p->code[i+4]); 00465 return false; 00466 } else { 00467 ir->code[i+3] = (p->code[i+4] * p->usec) / USECPERTICK; 00468 } 00469 DEBUGF("%s%d", !i ? "" : (i&1 ? "," : ", "), ir->code[i+3]); 00470 } 00471 DEBUGF("|\n"); 00472 00473 ir->FF = (ir->FF * p->usec) / USECPERTICK; 00474 DEBUGF("FF -> %d\n", ir->FF); 00475 00476 return true; 00477 } 00478 00479 //+============================================================================= 00480 // 00481 void irDump (irCode_t* ir) 00482 { 00483 int i, len; 00484 00485 printf("uint8_t buttonName[%d] = {", len); 00486 00487 printf("%d,%d, ", (ir->FF >> 8), ir->FF & 0xFF); 00488 printf("%d,%d,%d, ", ir->code[0], ir->code[1], ir->code[2]); 00489 00490 len = (ir->code[1] * 2) + (ir->code[2] * 2); 00491 for (i = 0; i < len; i++) { 00492 printf("%s%d", !i ? "" : (i&1 ? "," : ", "), ir->code[i+3]); 00493 } 00494 00495 printf("};\n"); 00496 00497 } 00498 00499 //+============================================================================= 00500 // 00501 int main ( ) 00502 { 00503 pronto_t pCode; 00504 irCode_t irCode; 00505 00506 decode(prontoTest, &pCode, &irCode); 00507 00508 irDump(&irCode); 00509 00510 return 0; 00511 } 00512 00513 #endif //0
Generated on Tue Jul 12 2022 18:56:50 by
