rtl_433/src/devices/secplus_v2.c
2021-02-05 06:42:21 +01:00

435 lines
12 KiB
C

/** @file
Security+ 2.0 rolling code.
Copyright (C) 2020 Peter Shipley <peter.shipley@gmail.com>
Based on code by Clayton Smith https://github.com/argilo/secplus
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.
*/
/**
Freq 310, 315 and 390 MHz.
Security+ 2.0 is described in [US patent application US20110317835A1](https://patents.google.com/patent/US20110317835A1/)
*/
#include "decoder.h"
/**
Security+ 2.0 rolling code.
Data comes in two bursts/packets.
Layout:
bits = `AA BB IIII OOOO X*30`
- AA = payload type ( 2 bits 00 or 01 )
- BB = FrameID ( 2 bits always 00)
- IIII = inversion indicator ( 4 bits )
- OOOO = Order indicator ( 4 bits ).
- XXXX.... = data ( 30 bits )
---
data is broken up into 3 parts ( p0 p1 p2 )
eg:
data = `ABCABCABCABCABCABCABCABCABCABC`
becomes:
`p0 = AAAAAAAAAA`
`p1 = BBBBBBBBBB`
`p2 = CCCCCCCCCC`
these three parts are then inverted and reordered based on the 4bit Order and Inversion indicators
fixed generated from concatenate p0 + p1
roll_array is generated from the 8 bit used for Order and Inversion indicators + p3
by reading the buffer in binary bit pairs forming trinary values
EG:
`1 0 0 1 1 0 1 0 0 1 1 0=> [1 0] [0 1] [1 0] [1 0] [0 1] [1 0] => 2 1 2 2 1 2`
Returns data in :
* roll_array as an array of trinary values ( 0, 1, 2) the value 3 is invalid
* fixed_p as an bitbuffer_t with 20 bits of data
Once the above has been run twice the two are merged
---
*/
int _decode_v2_half(bitbuffer_t *bits, uint8_t roll_array[], bitbuffer_t *fixed_p, int verbose)
{
uint8_t invert = 0;
uint8_t order = 0;
uint32_t x = 0;
unsigned int start_pos = 2; //
uint8_t buffy[10];
uint8_t part_id = (bits->bb[0][0] >> 6);
// fprintf(stderr, "%s: part %d\n", __func__, part_id);
if (verbose) {
fprintf(stderr, "%s: bits_per_row = %d\n", __func__, bits->bits_per_row[0]);
bitrow_debugf(bits->bb[0], bits->bits_per_row[0], "%s : ", __func__);
}
bitbuffer_extract_bytes(bits, 0, start_pos, buffy, 2);
start_pos += 2;
bitbuffer_extract_bytes(bits, 0, start_pos, buffy, 8);
start_pos += 8;
order = buffy[0] >> 4;
invert = buffy[0] & 0x0f;
// bitrow_debug(&invert, 8);
bitbuffer_extract_bytes(bits, 0, start_pos, buffy, 30);
start_pos += 30;
// copy 30 bits of data into 32bit int then shift >> 2
// memcpy(&dat, buffy, 4);
x = ((uint32_t)buffy[0] << 24) | (buffy[1] << 16) | (buffy[2] << 8) | (buffy[3]);
x >>= 2;
// using short to store 10bit values
uint16_t p0 = 0, p1 = 0, p2 = 0;
// sort 30 bits of interleaved data into three 10 bit buffers
for (int i = 0; i < 10; i++) {
p2 ^= (x & 0x00000001) << i; // 9-
x >>= 1;
p1 ^= (x & 0x00000001) << i;
x >>= 1;
p0 ^= (x & 0x00000001) << i;
x >>= 1;
}
// fprintf(stderr, "f1 (%d) %d %d %d\n", part_id, p0, p1, p2);
// selectively invert buffers
switch (invert) {
case 0x00: // 0b0000 (True, True, False),
p0 = ~p0 & 0x03FF;
p1 = ~p1 & 0x03FF;
break;
case 0x01: // 0b0001 (False, True, False),
p1 = ~p1 & 0x03FF;
break;
case 0x02: // 0b0010 (False, False, True),
p2 = ~p2 & 0x03FF;
break;
case 0x04: // 0b0100 (True, True, True),
p0 = ~p0 & 0x03FF;
p1 = ~p1 & 0x03FF;
p2 = ~p2 & 0x03FF;
break;
case 0x05: // 0b0101 (True, False, True),
case 0x0a: // 0b1010 (True, False, True),
p0 = ~p0 & 0x03FF;
p2 = ~p2 & 0x03FF;
break;
case 0x06: // 0b0110 (False, True, True),
p1 = ~p1 & 0x03FF;
p2 = ~p2 & 0x03FF;
break;
case 0x08: // 0b1000 (True, False, False),
p0 = ~p0 & 0x03FF;
break;
case 0x09: // 0b1001 (False, False, False),
break;
default:
if (verbose)
fprintf(stderr, "Invert FAIL\n");
return 1;
}
uint16_t a = p0, b = p1, c = p2;
// selectively reorder buffers
switch (order) {
case 0x06: // 0b0110 2, 1, 0],
case 0x09: // 0b1001 2, 1, 0],
p2 = a;
p1 = b;
p0 = c;
break;
case 0x08: // 0b1000 1, 2, 0],
case 0x04: // 0b0100 1, 2, 0],
p1 = a;
p2 = b;
p0 = c;
break;
case 0x01: // 0b0001 2, 0, 1],
p2 = a;
p0 = b;
p1 = c;
break;
case 0x00: // 0b0000 0, 2, 1],
p0 = a;
p2 = b;
p1 = c;
break;
case 0x05: // 0b0101 1, 0, 2],
p1 = a;
p0 = b;
p2 = c;
break;
case 0x02: // 0b0010 0, 1, 2],
case 0x0A: // 0b1010 0, 1, 2],
p0 = a;
p1 = b;
p2 = c;
break;
default:
if (verbose)
fprintf(stderr, "Order FAIL");
return 2;
}
bitbuffer_extract_bytes(bits, 0, 4, buffy, 8);
x = buffy[0];
int k = 0;
for (int i = 6; i >= 0; i -= 2) {
roll_array[k++] = (x >> i) & 0x03;
}
// bitrow_printf(buffy, 8, "%s ; buffy bits ", __func__);
// assemble binary bits into trinary
x = p2;
for (int i = 8; i >= 0; i -= 2) {
roll_array[k++] = (x >> i) & 0x03;
}
if (verbose) {
fprintf(stderr, "%s : roll_array : (%d) ", __func__, part_id);
for (int i = 0; i < 9; i++) {
fprintf(stderr, "%d ", roll_array[i]);
}
fprintf(stderr, "\n");
}
// SANITY check trinary valuse, 00/01/10 are valid, 11 is not
for (int i = 0; i < 9; i++) {
if (roll_array[i] == 3) {
fprintf(stderr, "roll_array val FAIL\n");
return 1; // DECODE_FAIL_SANITY;
}
}
// fixed_p = p0 + p1
for (int i = 9; i >= 0; i--) {
bitbuffer_add_bit(fixed_p, (p0 >> i) & 0x01);
}
for (int i = 9; i >= 0; i--) {
bitbuffer_add_bit(fixed_p, (p1 >> i) & 0x01);
}
return 0;
}
static const uint8_t _preamble[] = {0xaa, 0xaa, 0x95, 0x60};
unsigned _preamble_len = 28;
static int secplus_v2_callback(r_device *decoder, bitbuffer_t *bitbuffer)
{
unsigned search_index = 0;
bitbuffer_t bits = {0};
// int i = 0;
//bitbuffer_t bits_1 = {0};
bitbuffer_t fixed_1 = {0};
uint8_t rolling_1[16] = {0};
//bitbuffer_t bits_2 = {0};
bitbuffer_t fixed_2 = {0};
uint8_t rolling_2[16] = {0};
for (uint16_t row = 0; row < bitbuffer->num_rows; ++row) {
if (bitbuffer->bits_per_row[row] < 110) {
continue;
}
search_index = bitbuffer_search(bitbuffer, row, 0, _preamble, _preamble_len);
if (search_index >= bitbuffer->bits_per_row[row]) {
break;
}
bitbuffer_clear(&bits);
bitbuffer_manchester_decode(bitbuffer, row, search_index + 26, &bits, 80);
search_index += 20;
if (bits.bits_per_row[0] < 42) {
continue; // DECODE_ABORT_LENGTH;
}
if (decoder->verbose) {
(void)fprintf(stderr, "%s: manchester_decode %d len = %u\n", __func__,
0, bits.bits_per_row[0]);
bitrow_debugf(bits.bb[0], bits.bits_per_row[0], "%s: manchester_decoded %d", __func__, 0);
}
// valid = 0X00XXXX
// 1st 3rs and 4th bits should always be 0
if (bits.bb[0][0] & 0xB0) {
if (decoder->verbose)
fprintf(stderr, "%s: DECODE_FAIL_SANITY\n", __func__);
continue;
}
// 2nd bit indicates with half of the data
if (bits.bb[0][0] & 0xC0) {
if (decoder->verbose)
(void)fprintf(stderr, "%s: Set 2\n", __func__);
_decode_v2_half(&bits, rolling_2, &fixed_2, decoder->verbose);
}
else {
if (decoder->verbose)
(void)fprintf(stderr, "%s: Set 1\n", __func__);
_decode_v2_half(&bits, rolling_1, &fixed_1, decoder->verbose);
}
// break if we've received both halfs
if (fixed_1.bits_per_row[0] > 1 && fixed_2.bits_per_row[0] > 1) {
break;
}
}
// Do was have what we need ??
if (fixed_1.bits_per_row[0] == 0 || fixed_2.bits_per_row[0] == 0) {
// No? Awww F'ck it then
if (decoder->verbose)
fprintf(stderr, "%s: DECODE_FAIL_SANITY\n", __func__);
return DECODE_FAIL_SANITY;
}
// Assemble rolling_1[] and rolling_2[] into rolling_digits[]
uint8_t rolling_digits[24] = {0};
uint8_t *r;
r = rolling_digits;
*r++ = rolling_2[8];
*r++ = rolling_1[8];
for (int i = 4; i < 8; i++) {
*r++ = rolling_2[i];
}
for (int i = 4; i < 8; i++) {
*r++ = rolling_1[i];
}
for (int i = 0; i < 4; i++) {
*r++ = rolling_2[i];
}
for (int i = 0; i < 4; i++) {
*r++ = rolling_1[i];
}
// compute rolling_total from rolling_digits[]
uint32_t rolling_total = 0;
uint32_t rolling_temp = 0;
for (int i = 0; i < 18; i++) {
rolling_temp = (rolling_temp * 3) + rolling_digits[i];
// fprintf(stderr, ">> %12d\t%d\n", rolling_temp, rolling_digits[i]);
}
// Max value = 2^28 (268435456)
if ( rolling_temp >= 0x10000000 ) {
return DECODE_FAIL_SANITY;
}
// value is 28 bits thus need to shift over 4 bit
rolling_total = reverse32(rolling_temp);
rolling_total = rolling_total >> 4;
// Assemble "fixed" data part
uint64_t fixed_total = 0;
uint8_t *bb;
bb = fixed_1.bb[0];
fixed_total ^= ((uint64_t)bb[0]) << 32;
fixed_total ^= ((uint64_t)bb[1]) << 24;
fixed_total ^= ((uint64_t)bb[2]) << 16;
bb = fixed_2.bb[0];
fixed_total ^= ((uint64_t)bb[0]) << 12;
fixed_total ^= ((uint64_t)bb[1]) << 4;
fixed_total ^= (bb[2] >> 4) & 0x0f;
// fprintf(stderr, "fixed_total = %lu\n", fixed_total);
// int button = fixed_total >> 32;
// int remote_id = fixed_total & 0xffffffff;
char fixed_str[16];
char rolling_str[16];
// rolling_total is a 28 bit unsigned number
// fixed_totals is 40 bit in a uint64_t
snprintf(fixed_str, sizeof(fixed_str), "%llu", (long long unsigned)fixed_total);
snprintf(rolling_str, sizeof(rolling_str), "%u", rolling_total);
/* clang-format off */
data_t *data;
data = data_make(
"model", "Model", DATA_STRING, "Secplus-v2",
"id", "", DATA_INT, (fixed_total & 0xffffffff),
"button_id", "Button-ID", DATA_INT, (fixed_total >> 32),
"remote_id", "Remote-ID", DATA_INT, (fixed_total & 0xffffffff),
// "fixed", "", DATA_INT, fixed_total,
"fixed", "Fixed_Code", DATA_STRING, fixed_str,
"rolling", "Rolling_Code", DATA_STRING, rolling_str,
NULL);
/* clang-format on */
decoder_output_data(decoder, data);
return 1;
}
static char *output_fields[] = {
// Common fields
"model",
"id",
"rolling"
"fixed",
"button_id",
"remote_id",
NULL,
};
// Freq 310.01M
// -X "n=vI3,m=OOK_PCM,s=230,l=230,t=40,r=10000,g=7400,match={24}0xaaaa9560"
r_device secplus_v2 = {
.name = "Security+ 2.0 (Keyfob)",
.modulation = OOK_PULSE_PCM_RZ,
.short_width = 250,
.long_width = 250,
.tolerance = 50,
.gap_limit = 1500,
.reset_limit = 9000,
.decode_fn = &secplus_v2_callback,
.disabled = 0,
.fields = output_fields,
};