Просмотр исходного кода

Microchip HCS series, Keeloq protocol.

antirez 3 лет назад
Родитель
Сommit
ea48c7f89d
3 измененных файлов с 102 добавлено и 8 удалено
  1. 1 1
      app.h
  2. 87 0
      protocols/keeloq.c
  3. 14 7
      signal.c

+ 1 - 1
app.h

@@ -162,7 +162,7 @@ void scan_for_signal(ProtoViewApp *app);
 bool bitmap_get(uint8_t *b, uint32_t blen, uint32_t bitpos);
 void bitmap_set(uint8_t *b, uint32_t blen, uint32_t bitpos, bool val);
 void bitmap_set_pattern(uint8_t *b, uint32_t blen, const char *pat);
-void bitmap_invert_bytes_bits(uint8_t *p, uint32_t len);
+void bitmap_reverse_bytes(uint8_t *p, uint32_t len);
 bool bitmap_match_bits(uint8_t *b, uint32_t blen, uint32_t bitpos, const char *bits);
 uint32_t bitmap_seek_bits(uint8_t *b, uint32_t blen, uint32_t startpos, uint32_t maxbits, const char *bits);
 uint32_t convert_from_line_code(uint8_t *buf, uint64_t buflen, uint8_t *bits, uint32_t len, uint32_t offset, const char *zero_pattern, const char *one_pattern);

+ 87 - 0
protocols/keeloq.c

@@ -0,0 +1,87 @@
+/* Microchip HCS200/HCS300/HSC301 KeeLoq, rolling code remotes.
+ *
+ * Usually 443.92 Mhz OOK, ~200us or ~400us pulse len, depending
+ * on the configuration.
+ *
+ * Preamble: 12 pairs of alternating pulse/gap.
+ * Sync: long gap of around 10 times the duration of the short-pulse.
+ * Data: pulse width encoded data. Each bit takes three cycles:
+ *
+ * 0 = 110
+ * 1 = 100
+ *
+ * There are a total of 66 bits transmitted.
+ *  0..31: 32 bits of encrypted rolling code.
+ * 32..59: Remote ID, 28 bits
+ * 60..63: Buttons pressed
+ * 64..64: Low battery if set
+ * 65..65: Always set to 1
+ *
+ * Bits in bytes are inverted: least significant bit is first.
+ * For some reason there is no checksum whatsoever, so we only decode
+ * if we find everything well formed.
+ */
+
+#include "../app.h"
+
+static bool decode(uint8_t *bits, uint32_t numbytes, uint32_t numbits, ProtoViewMsgInfo *info) {
+
+    /* In the sync pattern, we require the 12 high/low pulses and at least
+     * half the gap we expect (5 pulses times, one is the final zero in the
+     * 24 symbols high/low sequence, then other 4). */
+    const char *sync_pattern = "101010101010101010101010" "0000";
+    uint8_t sync_len = 24+4;
+    if (numbits-sync_len+sync_len < 3*66) return false;
+    uint64_t off = bitmap_seek_bits(bits,numbytes,0,numbits,sync_pattern);
+    if (off == BITMAP_SEEK_NOT_FOUND) return false;
+    off += sync_len;
+
+    /* Now there is half the gap left, but we allow from 3 to 7, instead of 5
+     * symbols of gap, to avoid missing the signal for a matter of wrong
+     * timing. */
+    uint8_t gap_len = 0;
+    while(gap_len <= 7 && bitmap_get(bits,numbytes,off+gap_len) == 0)
+        gap_len++;
+    if (gap_len < 3 || gap_len > 7) return false;
+
+    off += gap_len;
+    FURI_LOG_E(TAG, "Keeloq preamble+sync found");
+
+    uint8_t raw[9] = {0};
+    uint32_t decoded =
+        convert_from_line_code(raw,sizeof(raw),bits,numbytes,off,
+            "110","100"); /* Pulse width modulation. */
+    FURI_LOG_E(TAG, "Keeloq decoded bits: %lu", decoded);
+
+    if (decoded < 66) return false; /* Require the full 66 bits. */
+    bitmap_reverse_bytes(raw,sizeof(raw)); /* Keeloq is LSB first. */
+
+    int buttons = raw[7]>>4;
+    int s3 = (buttons&1) != 0;
+    int s0 = (buttons&2) != 0;
+    int s1 = (buttons&4) != 0;
+    int s2 = (buttons&8) != 0;
+
+    int remote_id = ((raw[7]&0x0f) << 24) |
+                     (raw[6] << 16) |
+                     (raw[5] << 8) |
+                     (raw[4] << 0);
+    int lowbat = raw[8]&0x80;
+
+    snprintf(info->name,sizeof(info->name),"%s","Keeloq remote");
+    snprintf(info->raw,sizeof(info->raw),"%02X%02X%02X%02X%02X%02X%02X%02X%02X",
+        raw[0],raw[1],raw[2],raw[3],raw[4],raw[5],
+        raw[6],raw[7],raw[8]);
+    snprintf(info->info1,sizeof(info->info1),"Encrpyted %02X%02X%02X%02X",
+        raw[3],raw[2],raw[1],raw[0]);
+    snprintf(info->info2,sizeof(info->info2),"ID %08X", remote_id);
+    snprintf(info->info3,sizeof(info->info3),"s0-s3: %d%d%d%d",
+        s0,s1,s2,s3);
+    snprintf(info->info4,sizeof(info->info4),"Low battery? %s",
+        lowbat ? "yes" : "no");
+    return true;
+}
+
+ProtoViewDecoder KeeloqDecoder = {
+    "Keeloq", decode
+};

+ 14 - 7
signal.c

@@ -215,14 +215,19 @@ bool bitmap_get(uint8_t *b, uint32_t blen, uint32_t bitpos) {
     return (b[byte] & (1<<bit)) != 0;
 }
 
-/* We decode bits assuming the first bit we receive is the LSB
- * (see bitmap_set/get functions). Many devices send data
+/* We decode bits assuming the first bit we receive is the MSB
+ * (see bitmap_set/get functions). Certain devices send data
  * encoded in the reverse way. */
-void bitmap_invert_bytes_bits(uint8_t *p, uint32_t len) {
-    for (uint32_t j = 0; j < len*8; j += 8) {
-        bool bits[8];
-        for (int i = 0; i < 8; i++) bits[i] = bitmap_get(p,len,j+i);
-        for (int i = 0; i < 8; i++) bitmap_set(p,len,j+i,bits[7-i]);
+void bitmap_reverse_bytes(uint8_t *p, uint32_t len) {
+    for (uint32_t j = 0; j < len; j++) {
+        uint32_t b = p[j];
+        /* Step 1: swap the two nibbles: 12345678 -> 56781234 */
+        b = (b&0xf0)>>4 | (b&0x0f)<<4;
+        /* Step 2: swap adjacent pairs : 56781234 -> 78563412 */
+        b = (b&0xcc)>>2 | (b&0x33)<<2;
+        /* Step 3: swap adjacent bits  : 78563412 -> 87654321 */
+        b = (b&0xaa)>>1 | (b&0x55)<<1;
+        p[j] = b;
     }
 }
 
@@ -388,6 +393,7 @@ extern ProtoViewDecoder SchraderTPMSDecoder;
 extern ProtoViewDecoder SchraderEG53MA4TPMSDecoder;
 extern ProtoViewDecoder CitroenTPMSDecoder;
 extern ProtoViewDecoder FordTPMSDecoder;
+extern ProtoViewDecoder KeeloqDecoder;
 
 ProtoViewDecoder *Decoders[] = {
     &Oregon2Decoder,                /* Oregon sensors v2.1 protocol. */
@@ -398,6 +404,7 @@ ProtoViewDecoder *Decoders[] = {
     &SchraderEG53MA4TPMSDecoder,    /* Schrader EG53MA4 TPMS. */
     &CitroenTPMSDecoder,            /* Citroen TPMS. */
     &FordTPMSDecoder,               /* Ford TPMS. */
+    &KeeloqDecoder,                 /* Keeloq remote. */
     NULL
 };