PlatformIO package of the Teensy core framework compatible with GCC 10 & C++20
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

irPronto.cpp 15KB

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