diff --git a/README.md b/README.md index 23f5101e7..5552bae64 100644 --- a/README.md +++ b/README.md @@ -356,6 +356,8 @@ See [CONTRIBUTING.md](./docs/CONTRIBUTING.md). [268] Bresser Thermo-/Hygro-Sensor Explore Scientific ST1005H [269] DeltaDore X3D devices [270]* Quinetic + [271] Microchip HCS362 KeeLoq Hopping Encoder based remotes (mode 0) + [272] Microchip HCS362 KeeLoq Hopping Encoder based remotes (mode 1) * Disabled by default, use -R n or a conf file to enable diff --git a/conf/rtl_433.example.conf b/conf/rtl_433.example.conf index 4579e174f..c4bda957e 100644 --- a/conf/rtl_433.example.conf +++ b/conf/rtl_433.example.conf @@ -496,7 +496,9 @@ convert si protocol 267 # ThermoPro Meat Thermometers, TP828B 2 probes with Temp, BBQ Target LO and HI protocol 268 # Bresser Thermo-/Hygro-Sensor Explore Scientific ST1005H protocol 269 # DeltaDore X3D devices -# protocol 268 # Quinetic +# protocol 270 # Quinetic + protocol 271 # Microchip HCS362 KeeLoq Hopping Encoder based remotes (mode 0) + protocol 272 # Microchip HCS362 KeeLoq Hopping Encoder based remotes (mode 1) ## Flex devices (command line option "-X") diff --git a/include/rtl_433_devices.h b/include/rtl_433_devices.h index f872c2521..b2f4a13ca 100644 --- a/include/rtl_433_devices.h +++ b/include/rtl_433_devices.h @@ -278,6 +278,8 @@ DECL(bresser_st1005h) \ DECL(deltadore_x3d) \ DECL(quinetic) \ + DECL(hcs362_pwm) \ + DECL(hcs362_mc) \ /* Add new decoders here. */ diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index ae152f343..dbe4dcef7 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -138,6 +138,7 @@ add_library(r_433 STATIC devices/gt_wt_02.c devices/gt_wt_03.c devices/hcs200.c + devices/hcs362.c devices/hideki.c devices/holman_ws5029.c devices/hondaremote.c diff --git a/src/devices/hcs362.c b/src/devices/hcs362.c new file mode 100644 index 000000000..4ca985e59 --- /dev/null +++ b/src/devices/hcs362.c @@ -0,0 +1,132 @@ +/** @file + Microchip HCS362 KeeLoq Code Hopping Encoder based remotes. + + Copyright (C) 2024 Christian W. Zuckschwerdt + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. +*/ + +#include "decoder.h" + +/** +Microchip HCS362 KeeLoq Code Hopping Encoder based remotes. + +There are two transmissions modes: PWM (mode 0) and MC (mode 1). + +72 bits transmitted, LSB first. + +69-bit transmission code length +- 32-bit hopping code +- 37-bit fixed code (28/32-bit serial number, 4/0-bit function code, 1-bit status, 2-bit CRC/time, 2-bit queue) +- Stop bit + +| 0-31 | 32 bit Encrypted Portion +| 32-59 | 28 bit Serial Number +| 60-63 | 4 bit Function Code (S3, S0, S1, S2) +| 64 | 1 bit Battery Low (Low Voltage Detector Status) +| 65-66 | 2 bit CRC +| 67-68 | 2 bit Button Queue Information + +Note that the button bits are (MSB/first sent to LSB) S3, S0, S1, S2. +Hardware buttons might map to combinations of these bits. + +- Datasheet HCS362: https://ww1.microchip.com/downloads/aemDocuments/documents/MCU08/ProductDocuments/DataSheets/40189E.pdf + +The preamble of 12 short pulses is followed by a long 850 us gap. + +Raw data capture: + + rtl_433 -R 0 -X 'n=HCS362,m=OOK_PCM,s=214,l=214,g=600,r=900' +*/ + +static int hcs362_decode(r_device *decoder, bitbuffer_t *bitbuffer) +{ + // Reject codes of wrong length + if (bitbuffer->bits_per_row[0] != 12 || bitbuffer->bits_per_row[1] != 72) + return DECODE_ABORT_LENGTH; + + uint8_t *b = bitbuffer->bb[0]; + // Reject codes with an incorrect preamble (expected 0xfff) + // Manchester decoding might read this as 0x000 + if ((b[0] != 0xff || (b[1] & 0xf0) != 0xf0) && (b[0] != 0x00 || (b[1] & 0xf0) != 0x00)) { + decoder_log(decoder, 2, __func__, "Preamble not found"); + return DECODE_ABORT_EARLY; + } + + // Second row is data + b = bitbuffer->bb[1]; + + // No need to decode/extract values for simple test + if (b[1] == 0xff && b[2] == 0xff && b[3] == 0xff && b[4] == 0xff + && b[5] == 0xff && b[6] == 0xff && b[7] == 0xff) { + decoder_log(decoder, 2, __func__, "DECODE_FAIL_SANITY data all 0xff"); + return DECODE_FAIL_SANITY; + } + + // The transmission is LSB first, big endian. + uint32_t encrypted = ((unsigned)reverse8(b[3]) << 24) | (reverse8(b[2]) << 16) | (reverse8(b[1]) << 8) | (reverse8(b[0])); + int serial = (reverse8(b[7] & 0xf0) << 24) | (reverse8(b[6]) << 16) | (reverse8(b[5]) << 8) | (reverse8(b[4])); + int btn = (b[7] & 0x0f); + int btn_num = (btn & 0x08) | ((btn & 0x01) << 2) | (btn & 0x02) | ((btn & 0x04) >> 2); // S3, S0, S1, S2 + int learn = (b[7] & 0x0f) == 0x0f; + int battery_low = (b[8] & 0x80) == 0x80; + int repeat = (b[8] & 0x40) == 0x40; + + char encrypted_str[9]; + snprintf(encrypted_str, sizeof(encrypted_str), "%08X", encrypted); + char serial_str[9]; + snprintf(serial_str, sizeof(serial_str), "%07X", serial); + + /* clang-format off */ + data_t *data = data_make( + "model", "", DATA_STRING, "Microchip-HCS362", + "id", "", DATA_STRING, serial_str, + "battery_ok", "Battery", DATA_INT, !battery_low, + "button", "Button", DATA_INT, btn_num, + "learn", "Learn mode", DATA_INT, learn, + "repeat", "Repeat", DATA_INT, repeat, + "encrypted", "", DATA_STRING, encrypted_str, + NULL); + /* clang-format on */ + + decoder_output_data(decoder, data); + return 1; +} + +static char const *const output_fields[] = { + "model", + "id", + "battery_ok", + "button", + "learn", + "repeat", + "encrypted", + NULL, +}; + +r_device const hcs362_pwm = { + .name = "Microchip HCS362 KeeLoq Hopping Encoder based remotes (mode 0)", + .modulation = OOK_PULSE_PWM, + .short_width = 214, + .long_width = 430, + .gap_limit = 600, + .reset_limit = 900, + .tolerance = 50, // us + .decode_fn = &hcs362_decode, + .fields = output_fields, +}; + +r_device const hcs362_mc = { + .name = "Microchip HCS362 KeeLoq Hopping Encoder based remotes (mode 1)", + .modulation = OOK_PULSE_MANCHESTER_ZEROBIT, + .short_width = 214, + .long_width = 214, + .gap_limit = 600, + .reset_limit = 900, + .tolerance = 50, // us + .decode_fn = &hcs362_decode, + .fields = output_fields, +};