keeloq.c 2.9 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283
  1. /* Microchip HCS200/HCS300/HSC301 KeeLoq, rolling code remotes.
  2. *
  3. * Usually 443.92 Mhz OOK, ~200us or ~400us pulse len, depending
  4. * on the configuration.
  5. *
  6. * Preamble: 12 pairs of alternating pulse/gap.
  7. * Sync: long gap of around 10 times the duration of the short-pulse.
  8. * Data: pulse width encoded data. Each bit takes three cycles:
  9. *
  10. * 0 = 110
  11. * 1 = 100
  12. *
  13. * There are a total of 66 bits transmitted.
  14. * 0..31: 32 bits of encrypted rolling code.
  15. * 32..59: Remote ID, 28 bits
  16. * 60..63: Buttons pressed
  17. * 64..64: Low battery if set
  18. * 65..65: Always set to 1
  19. *
  20. * Bits in bytes are inverted: least significant bit is first.
  21. * For some reason there is no checksum whatsoever, so we only decode
  22. * if we find everything well formed.
  23. */
  24. #include "../app.h"
  25. static bool decode(uint8_t *bits, uint32_t numbytes, uint32_t numbits, ProtoViewMsgInfo *info) {
  26. /* In the sync pattern, we require the 12 high/low pulses and at least
  27. * half the gap we expect (5 pulses times, one is the final zero in the
  28. * 24 symbols high/low sequence, then other 4). */
  29. const char *sync_pattern = "101010101010101010101010" "0000";
  30. uint8_t sync_len = 24+4;
  31. if (numbits-sync_len+sync_len < 3*66) return false;
  32. uint32_t off = bitmap_seek_bits(bits,numbytes,0,numbits,sync_pattern);
  33. if (off == BITMAP_SEEK_NOT_FOUND) return false;
  34. info->start_off = off;
  35. off += sync_len; // Seek start of message.
  36. /* Now there is half the gap left, but we allow from 3 to 7, instead of 5
  37. * symbols of gap, to avoid missing the signal for a matter of wrong
  38. * timing. */
  39. uint8_t gap_len = 0;
  40. while(gap_len <= 7 && bitmap_get(bits,numbytes,off+gap_len) == 0)
  41. gap_len++;
  42. if (gap_len < 3 || gap_len > 7) return false;
  43. off += gap_len;
  44. FURI_LOG_E(TAG, "Keeloq preamble+sync found");
  45. uint8_t raw[9] = {0};
  46. uint32_t decoded =
  47. convert_from_line_code(raw,sizeof(raw),bits,numbytes,off,
  48. "110","100"); /* Pulse width modulation. */
  49. FURI_LOG_E(TAG, "Keeloq decoded bits: %lu", decoded);
  50. if (decoded < 66) return false; /* Require the full 66 bits. */
  51. info->pulses_count = (off+66*3) - info->start_off;
  52. bitmap_reverse_bytes(raw,sizeof(raw)); /* Keeloq is LSB first. */
  53. int buttons = raw[7]>>4;
  54. int remote_id = ((raw[7]&0x0f) << 24) |
  55. (raw[6] << 16) |
  56. (raw[5] << 8) |
  57. (raw[4] << 0);
  58. int lowbat = (raw[8]&0x80) != 0;
  59. fieldset_add_bytes(info->fieldset,"raw",raw,9*2);
  60. fieldset_add_bytes(info->fieldset,"encr",raw,4*2);
  61. fieldset_add_hex(info->fieldset,"id",remote_id,28);
  62. fieldset_add_bin(info->fieldset,"s2 s1 s0 s3",buttons,4);
  63. fieldset_add_bin(info->fieldset,"low battery",lowbat,1);
  64. return true;
  65. }
  66. ProtoViewDecoder KeeloqDecoder = {
  67. .name = "Keeloq",
  68. .decode = decode,
  69. .get_fields = NULL,
  70. .build_message = NULL
  71. };