Taguchi Yuuki / IRremote

Dependents:   Lilnija_29012017 NucleoF042K6_IRReceiver

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers irPronto.cpp Source File

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