mirror of
https://github.com/DarkFlippers/unleashed-firmware.git
synced 2025-12-12 12:42:30 +04:00
* Fix Typos * Tune decoders * Better parsing, show more data in existing protocols * Add new protocols * Update keeloqs * Add unit tests & raws * Add honeywell unittest * Comment until better solution is found Adding GAPs to be sent first to make signal better suitable for decoder (decoding from only one signal sample) does nothing, needs something else TODO: Fix encoders? * suppressed missing issue warning * subghz: re-enabled failing encoder tests * Fix two? 3 left * properly do gangqi and marantec for unit test and real use * fix unit tests now * fix possible memory leak * reset decoder step too * subghz: extra encoder safety; report random signal test results on failure * unit_tests: subghz: renamed test file for consistency * subghz: more explicit buffer position resets * Fix gangqi samples --------- Co-authored-by: hedger <hedger@users.noreply.github.com> Co-authored-by: hedger <hedger@nanode.su>
460 lines
16 KiB
C
460 lines
16 KiB
C
#include "came_twee.h"
|
|
#include <lib/toolbox/manchester_decoder.h>
|
|
#include <lib/toolbox/manchester_encoder.h>
|
|
#include "../blocks/const.h"
|
|
#include "../blocks/decoder.h"
|
|
#include "../blocks/encoder.h"
|
|
#include "../blocks/generic.h"
|
|
#include "../blocks/math.h"
|
|
|
|
/*
|
|
* Help
|
|
* https://phreakerclub.com/forum/showthread.php?t=635&highlight=came+twin
|
|
*
|
|
*/
|
|
|
|
#define TAG "SubGhzProtocolCameTwee"
|
|
|
|
#define DIP_PATTERN "%c%c%c%c%c%c%c%c%c%c"
|
|
#define CNT_TO_DIP(dip) \
|
|
(dip & 0x0200 ? '1' : '0'), (dip & 0x0100 ? '1' : '0'), (dip & 0x0080 ? '1' : '0'), \
|
|
(dip & 0x0040 ? '1' : '0'), (dip & 0x0020 ? '1' : '0'), (dip & 0x0010 ? '1' : '0'), \
|
|
(dip & 0x0008 ? '1' : '0'), (dip & 0x0004 ? '1' : '0'), (dip & 0x0002 ? '1' : '0'), \
|
|
(dip & 0x0001 ? '1' : '0')
|
|
|
|
/**
|
|
* Rainbow table Came Twee.
|
|
*/
|
|
static const uint32_t came_twee_magic_numbers_xor[15] = {
|
|
0x0E0E0E00,
|
|
0x1D1D1D11,
|
|
0x2C2C2C22,
|
|
0x3B3B3B33,
|
|
0x4A4A4A44,
|
|
0x59595955,
|
|
0x68686866,
|
|
0x77777777,
|
|
0x86868688,
|
|
0x95959599,
|
|
0xA4A4A4AA,
|
|
0xB3B3B3BB,
|
|
0xC2C2C2CC,
|
|
0xD1D1D1DD,
|
|
0xE0E0E0EE,
|
|
};
|
|
|
|
static const SubGhzBlockConst subghz_protocol_came_twee_const = {
|
|
.te_short = 500,
|
|
.te_long = 1000,
|
|
.te_delta = 250,
|
|
.min_count_bit_for_found = 54,
|
|
};
|
|
|
|
struct SubGhzProtocolDecoderCameTwee {
|
|
SubGhzProtocolDecoderBase base;
|
|
|
|
SubGhzBlockDecoder decoder;
|
|
SubGhzBlockGeneric generic;
|
|
ManchesterState manchester_saved_state;
|
|
};
|
|
|
|
struct SubGhzProtocolEncoderCameTwee {
|
|
SubGhzProtocolEncoderBase base;
|
|
|
|
SubGhzProtocolBlockEncoder encoder;
|
|
SubGhzBlockGeneric generic;
|
|
};
|
|
|
|
typedef enum {
|
|
CameTweeDecoderStepReset = 0,
|
|
CameTweeDecoderStepDecoderData,
|
|
} CameTweeDecoderStep;
|
|
|
|
const SubGhzProtocolDecoder subghz_protocol_came_twee_decoder = {
|
|
.alloc = subghz_protocol_decoder_came_twee_alloc,
|
|
.free = subghz_protocol_decoder_came_twee_free,
|
|
|
|
.feed = subghz_protocol_decoder_came_twee_feed,
|
|
.reset = subghz_protocol_decoder_came_twee_reset,
|
|
|
|
.get_hash_data = subghz_protocol_decoder_came_twee_get_hash_data,
|
|
.serialize = subghz_protocol_decoder_came_twee_serialize,
|
|
.deserialize = subghz_protocol_decoder_came_twee_deserialize,
|
|
.get_string = subghz_protocol_decoder_came_twee_get_string,
|
|
};
|
|
|
|
const SubGhzProtocolEncoder subghz_protocol_came_twee_encoder = {
|
|
.alloc = subghz_protocol_encoder_came_twee_alloc,
|
|
.free = subghz_protocol_encoder_came_twee_free,
|
|
|
|
.deserialize = subghz_protocol_encoder_came_twee_deserialize,
|
|
.stop = subghz_protocol_encoder_came_twee_stop,
|
|
.yield = subghz_protocol_encoder_came_twee_yield,
|
|
};
|
|
|
|
const SubGhzProtocol subghz_protocol_came_twee = {
|
|
.name = SUBGHZ_PROTOCOL_CAME_TWEE_NAME,
|
|
.type = SubGhzProtocolTypeStatic,
|
|
.flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable |
|
|
SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send,
|
|
|
|
.decoder = &subghz_protocol_came_twee_decoder,
|
|
.encoder = &subghz_protocol_came_twee_encoder,
|
|
};
|
|
|
|
void* subghz_protocol_encoder_came_twee_alloc(SubGhzEnvironment* environment) {
|
|
UNUSED(environment);
|
|
SubGhzProtocolEncoderCameTwee* instance = malloc(sizeof(SubGhzProtocolEncoderCameTwee));
|
|
|
|
instance->base.protocol = &subghz_protocol_came_twee;
|
|
instance->generic.protocol_name = instance->base.protocol->name;
|
|
|
|
instance->encoder.repeat = 10;
|
|
instance->encoder.size_upload = 1536; //max upload 92*14 = 1288 !!!!
|
|
instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration));
|
|
instance->encoder.is_running = false;
|
|
return instance;
|
|
}
|
|
|
|
void subghz_protocol_encoder_came_twee_free(void* context) {
|
|
furi_assert(context);
|
|
SubGhzProtocolEncoderCameTwee* instance = context;
|
|
free(instance->encoder.upload);
|
|
free(instance);
|
|
}
|
|
|
|
static LevelDuration
|
|
subghz_protocol_encoder_came_twee_add_duration_to_upload(ManchesterEncoderResult result) {
|
|
LevelDuration data = {.duration = 0, .level = 0};
|
|
switch(result) {
|
|
case ManchesterEncoderResultShortLow:
|
|
data.duration = subghz_protocol_came_twee_const.te_short;
|
|
data.level = false;
|
|
break;
|
|
case ManchesterEncoderResultLongLow:
|
|
data.duration = subghz_protocol_came_twee_const.te_long;
|
|
data.level = false;
|
|
break;
|
|
case ManchesterEncoderResultLongHigh:
|
|
data.duration = subghz_protocol_came_twee_const.te_long;
|
|
data.level = true;
|
|
break;
|
|
case ManchesterEncoderResultShortHigh:
|
|
data.duration = subghz_protocol_came_twee_const.te_short;
|
|
data.level = true;
|
|
break;
|
|
|
|
default:
|
|
furi_crash("SubGhz: ManchesterEncoderResult is incorrect.");
|
|
break;
|
|
}
|
|
return level_duration_make(data.level, data.duration);
|
|
}
|
|
|
|
/**
|
|
* Generating an upload from data.
|
|
* @param instance Pointer to a SubGhzProtocolEncoderCameTwee instance
|
|
*/
|
|
static void subghz_protocol_encoder_came_twee_get_upload(SubGhzProtocolEncoderCameTwee* instance) {
|
|
furi_assert(instance);
|
|
size_t index = 0;
|
|
|
|
ManchesterEncoderState enc_state;
|
|
manchester_encoder_reset(&enc_state);
|
|
ManchesterEncoderResult result;
|
|
|
|
uint64_t temp_parcel = 0x003FFF7200000000; //parcel mask
|
|
|
|
for(int i = 14; i >= 0; i--) {
|
|
temp_parcel = (temp_parcel & 0xFFFFFFFF00000000) |
|
|
(instance->generic.serial ^ came_twee_magic_numbers_xor[i]);
|
|
|
|
for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) {
|
|
if(!manchester_encoder_advance(&enc_state, !bit_read(temp_parcel, i - 1), &result)) {
|
|
instance->encoder.upload[index++] =
|
|
subghz_protocol_encoder_came_twee_add_duration_to_upload(result);
|
|
manchester_encoder_advance(&enc_state, !bit_read(temp_parcel, i - 1), &result);
|
|
}
|
|
instance->encoder.upload[index++] =
|
|
subghz_protocol_encoder_came_twee_add_duration_to_upload(result);
|
|
}
|
|
instance->encoder.upload[index] = subghz_protocol_encoder_came_twee_add_duration_to_upload(
|
|
manchester_encoder_finish(&enc_state));
|
|
if(level_duration_get_level(instance->encoder.upload[index])) {
|
|
index++;
|
|
}
|
|
instance->encoder.upload[index++] =
|
|
level_duration_make(false, (uint32_t)subghz_protocol_came_twee_const.te_long * 51);
|
|
}
|
|
instance->encoder.size_upload = index;
|
|
}
|
|
|
|
/**
|
|
* Analysis of received data
|
|
* @param instance Pointer to a SubGhzBlockGeneric* instance
|
|
*/
|
|
static void subghz_protocol_came_twee_remote_controller(SubGhzBlockGeneric* instance) {
|
|
/* Came Twee 54 bit, rolling code 15 parcels with
|
|
* a decreasing counter from 0xE to 0x0
|
|
* with originally coded dip switches on the console 10 bit code
|
|
*
|
|
* 0x003FFF72E04A6FEE
|
|
* 0x003FFF72D17B5EDD
|
|
* 0x003FFF72C2684DCC
|
|
* 0x003FFF72B3193CBB
|
|
* 0x003FFF72A40E2BAA
|
|
* 0x003FFF72953F1A99
|
|
* 0x003FFF72862C0988
|
|
* 0x003FFF7277DDF877
|
|
* 0x003FFF7268C2E766
|
|
* 0x003FFF7259F3D655
|
|
* 0x003FFF724AE0C544
|
|
* 0x003FFF723B91B433
|
|
* 0x003FFF722C86A322
|
|
* 0x003FFF721DB79211
|
|
* 0x003FFF720EA48100
|
|
*
|
|
* decryption
|
|
* the last 32 bits, do XOR by the desired number, divide the result by 4,
|
|
* convert the first 16 bits of the resulting 32-bit number to bin and do
|
|
* bit-by-bit mirroring, adding up to 10 bits
|
|
*
|
|
* Example
|
|
* Step 1. 0x003FFF721DB79211 => 0x1DB79211
|
|
* Step 4. 0x1DB79211 xor 0x1D1D1D11 => 0x00AA8F00
|
|
* Step 4. 0x00AA8F00 / 4 => 0x002AA3C0
|
|
* Step 5. 0x002AA3C0 => 0x002A
|
|
* Step 6. 0x002A bin => b101010
|
|
* Step 7. b101010 => b0101010000
|
|
* Step 8. b0101010000 => (Dip) Off ON Off ON Off ON Off Off Off Off
|
|
*/
|
|
|
|
uint8_t cnt_parcel = (uint8_t)(instance->data & 0xF);
|
|
uint32_t data = (uint32_t)(instance->data & 0x0FFFFFFFF);
|
|
|
|
data = (data ^ came_twee_magic_numbers_xor[cnt_parcel]);
|
|
instance->serial = data;
|
|
data /= 4;
|
|
instance->btn = (data >> 4) & 0x0F;
|
|
data >>= 16;
|
|
data = (uint16_t)subghz_protocol_blocks_reverse_key(data, 16);
|
|
instance->cnt = data >> 6;
|
|
}
|
|
|
|
SubGhzProtocolStatus
|
|
subghz_protocol_encoder_came_twee_deserialize(void* context, FlipperFormat* flipper_format) {
|
|
furi_assert(context);
|
|
SubGhzProtocolEncoderCameTwee* instance = context;
|
|
SubGhzProtocolStatus res = SubGhzProtocolStatusError;
|
|
do {
|
|
res = subghz_block_generic_deserialize_check_count_bit(
|
|
&instance->generic,
|
|
flipper_format,
|
|
subghz_protocol_came_twee_const.min_count_bit_for_found);
|
|
if(res != SubGhzProtocolStatusOk) {
|
|
break;
|
|
}
|
|
//optional parameter parameter
|
|
flipper_format_read_uint32(
|
|
flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1);
|
|
|
|
subghz_protocol_came_twee_remote_controller(&instance->generic);
|
|
subghz_protocol_encoder_came_twee_get_upload(instance);
|
|
instance->encoder.front = 0; // reset position before start
|
|
instance->encoder.is_running = true;
|
|
} while(false);
|
|
|
|
return res;
|
|
}
|
|
|
|
void subghz_protocol_encoder_came_twee_stop(void* context) {
|
|
SubGhzProtocolEncoderCameTwee* instance = context;
|
|
instance->encoder.is_running = false;
|
|
instance->encoder.front = 0; // reset position
|
|
}
|
|
|
|
LevelDuration subghz_protocol_encoder_came_twee_yield(void* context) {
|
|
SubGhzProtocolEncoderCameTwee* instance = context;
|
|
|
|
if(instance->encoder.repeat == 0 || !instance->encoder.is_running) {
|
|
instance->encoder.is_running = false;
|
|
return level_duration_reset();
|
|
}
|
|
|
|
LevelDuration ret = instance->encoder.upload[instance->encoder.front];
|
|
|
|
if(++instance->encoder.front == instance->encoder.size_upload) {
|
|
instance->encoder.repeat--;
|
|
instance->encoder.front = 0;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
void* subghz_protocol_decoder_came_twee_alloc(SubGhzEnvironment* environment) {
|
|
UNUSED(environment);
|
|
SubGhzProtocolDecoderCameTwee* instance = malloc(sizeof(SubGhzProtocolDecoderCameTwee));
|
|
instance->base.protocol = &subghz_protocol_came_twee;
|
|
instance->generic.protocol_name = instance->base.protocol->name;
|
|
return instance;
|
|
}
|
|
|
|
void subghz_protocol_decoder_came_twee_free(void* context) {
|
|
furi_assert(context);
|
|
SubGhzProtocolDecoderCameTwee* instance = context;
|
|
free(instance);
|
|
}
|
|
|
|
void subghz_protocol_decoder_came_twee_reset(void* context) {
|
|
furi_assert(context);
|
|
SubGhzProtocolDecoderCameTwee* instance = context;
|
|
instance->decoder.parser_step = CameTweeDecoderStepReset;
|
|
manchester_advance(
|
|
instance->manchester_saved_state,
|
|
ManchesterEventReset,
|
|
&instance->manchester_saved_state,
|
|
NULL);
|
|
}
|
|
|
|
void subghz_protocol_decoder_came_twee_feed(void* context, bool level, uint32_t duration) {
|
|
furi_assert(context);
|
|
SubGhzProtocolDecoderCameTwee* instance = context;
|
|
ManchesterEvent event = ManchesterEventReset;
|
|
switch(instance->decoder.parser_step) {
|
|
case CameTweeDecoderStepReset:
|
|
if((!level) && (DURATION_DIFF(duration, subghz_protocol_came_twee_const.te_long * 51) <
|
|
subghz_protocol_came_twee_const.te_delta * 20)) {
|
|
//Found header CAME
|
|
instance->decoder.parser_step = CameTweeDecoderStepDecoderData;
|
|
instance->decoder.decode_data = 0;
|
|
instance->decoder.decode_count_bit = 0;
|
|
manchester_advance(
|
|
instance->manchester_saved_state,
|
|
ManchesterEventLongLow,
|
|
&instance->manchester_saved_state,
|
|
NULL);
|
|
manchester_advance(
|
|
instance->manchester_saved_state,
|
|
ManchesterEventLongHigh,
|
|
&instance->manchester_saved_state,
|
|
NULL);
|
|
manchester_advance(
|
|
instance->manchester_saved_state,
|
|
ManchesterEventShortLow,
|
|
&instance->manchester_saved_state,
|
|
NULL);
|
|
}
|
|
break;
|
|
case CameTweeDecoderStepDecoderData:
|
|
if(!level) {
|
|
if(DURATION_DIFF(duration, subghz_protocol_came_twee_const.te_short) <
|
|
subghz_protocol_came_twee_const.te_delta) {
|
|
event = ManchesterEventShortLow;
|
|
} else if(
|
|
DURATION_DIFF(duration, subghz_protocol_came_twee_const.te_long) <
|
|
subghz_protocol_came_twee_const.te_delta) {
|
|
event = ManchesterEventLongLow;
|
|
} else if(
|
|
duration >= ((uint32_t)subghz_protocol_came_twee_const.te_long * 2 +
|
|
subghz_protocol_came_twee_const.te_delta)) {
|
|
if(instance->decoder.decode_count_bit ==
|
|
subghz_protocol_came_twee_const.min_count_bit_for_found) {
|
|
instance->generic.data = instance->decoder.decode_data;
|
|
instance->generic.data_count_bit = instance->decoder.decode_count_bit;
|
|
|
|
if(instance->base.callback)
|
|
instance->base.callback(&instance->base, instance->base.context);
|
|
}
|
|
instance->decoder.decode_data = 0;
|
|
instance->decoder.decode_count_bit = 0;
|
|
manchester_advance(
|
|
instance->manchester_saved_state,
|
|
ManchesterEventLongLow,
|
|
&instance->manchester_saved_state,
|
|
NULL);
|
|
manchester_advance(
|
|
instance->manchester_saved_state,
|
|
ManchesterEventLongHigh,
|
|
&instance->manchester_saved_state,
|
|
NULL);
|
|
manchester_advance(
|
|
instance->manchester_saved_state,
|
|
ManchesterEventShortLow,
|
|
&instance->manchester_saved_state,
|
|
NULL);
|
|
} else {
|
|
instance->decoder.parser_step = CameTweeDecoderStepReset;
|
|
}
|
|
} else {
|
|
if(DURATION_DIFF(duration, subghz_protocol_came_twee_const.te_short) <
|
|
subghz_protocol_came_twee_const.te_delta) {
|
|
event = ManchesterEventShortHigh;
|
|
} else if(
|
|
DURATION_DIFF(duration, subghz_protocol_came_twee_const.te_long) <
|
|
subghz_protocol_came_twee_const.te_delta) {
|
|
event = ManchesterEventLongHigh;
|
|
} else {
|
|
instance->decoder.parser_step = CameTweeDecoderStepReset;
|
|
}
|
|
}
|
|
if(event != ManchesterEventReset) {
|
|
bool data;
|
|
bool data_ok = manchester_advance(
|
|
instance->manchester_saved_state, event, &instance->manchester_saved_state, &data);
|
|
|
|
if(data_ok) {
|
|
instance->decoder.decode_data = (instance->decoder.decode_data << 1) | !data;
|
|
instance->decoder.decode_count_bit++;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
uint8_t subghz_protocol_decoder_came_twee_get_hash_data(void* context) {
|
|
furi_assert(context);
|
|
SubGhzProtocolDecoderCameTwee* instance = context;
|
|
return subghz_protocol_blocks_get_hash_data(
|
|
&instance->decoder, (instance->decoder.decode_count_bit / 8) + 1);
|
|
}
|
|
|
|
SubGhzProtocolStatus subghz_protocol_decoder_came_twee_serialize(
|
|
void* context,
|
|
FlipperFormat* flipper_format,
|
|
SubGhzRadioPreset* preset) {
|
|
furi_assert(context);
|
|
SubGhzProtocolDecoderCameTwee* instance = context;
|
|
return subghz_block_generic_serialize(&instance->generic, flipper_format, preset);
|
|
}
|
|
|
|
SubGhzProtocolStatus
|
|
subghz_protocol_decoder_came_twee_deserialize(void* context, FlipperFormat* flipper_format) {
|
|
furi_assert(context);
|
|
SubGhzProtocolDecoderCameTwee* instance = context;
|
|
return subghz_block_generic_deserialize_check_count_bit(
|
|
&instance->generic,
|
|
flipper_format,
|
|
subghz_protocol_came_twee_const.min_count_bit_for_found);
|
|
}
|
|
|
|
void subghz_protocol_decoder_came_twee_get_string(void* context, FuriString* output) {
|
|
furi_assert(context);
|
|
SubGhzProtocolDecoderCameTwee* instance = context;
|
|
subghz_protocol_came_twee_remote_controller(&instance->generic);
|
|
uint32_t code_found_hi = instance->generic.data >> 32;
|
|
uint32_t code_found_lo = instance->generic.data & 0x00000000ffffffff;
|
|
|
|
furi_string_cat_printf(
|
|
output,
|
|
"%s %db\r\n"
|
|
"Key:0x%lX%08lX\r\n"
|
|
"Btn:%X\r\n"
|
|
"DIP:" DIP_PATTERN "\r\n",
|
|
instance->generic.protocol_name,
|
|
instance->generic.data_count_bit,
|
|
code_found_hi,
|
|
code_found_lo,
|
|
instance->generic.btn,
|
|
CNT_TO_DIP(instance->generic.cnt));
|
|
}
|