unleashed-firmware/lib/subghz/protocols/somfy_keytis.c
2023-02-25 23:25:32 +03:00

801 lines
31 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "somfy_keytis.h"
#include <lib/toolbox/manchester_decoder.h>
#include "../blocks/const.h"
#include "../blocks/decoder.h"
#include "../blocks/encoder.h"
#include "../blocks/generic.h"
#include "../blocks/math.h"
#define TAG "SubGhzProtocolSomfyKeytis"
static const SubGhzBlockConst subghz_protocol_somfy_keytis_const = {
.te_short = 640,
.te_long = 1280,
.te_delta = 250,
.min_count_bit_for_found = 80,
};
struct SubGhzProtocolDecoderSomfyKeytis {
SubGhzProtocolDecoderBase base;
SubGhzBlockDecoder decoder;
SubGhzBlockGeneric generic;
uint16_t header_count;
ManchesterState manchester_saved_state;
uint32_t press_duration_counter;
};
struct SubGhzProtocolEncoderSomfyKeytis {
SubGhzProtocolEncoderBase base;
SubGhzProtocolBlockEncoder encoder;
SubGhzBlockGeneric generic;
};
typedef enum {
SomfyKeytisDecoderStepReset = 0,
SomfyKeytisDecoderStepCheckPreambula,
SomfyKeytisDecoderStepFoundPreambula,
SomfyKeytisDecoderStepStartDecode,
SomfyKeytisDecoderStepDecoderData,
} SomfyKeytisDecoderStep;
const SubGhzProtocolDecoder subghz_protocol_somfy_keytis_decoder = {
.alloc = subghz_protocol_decoder_somfy_keytis_alloc,
.free = subghz_protocol_decoder_somfy_keytis_free,
.feed = subghz_protocol_decoder_somfy_keytis_feed,
.reset = subghz_protocol_decoder_somfy_keytis_reset,
.get_hash_data = subghz_protocol_decoder_somfy_keytis_get_hash_data,
.serialize = subghz_protocol_decoder_somfy_keytis_serialize,
.deserialize = subghz_protocol_decoder_somfy_keytis_deserialize,
.get_string = subghz_protocol_decoder_somfy_keytis_get_string,
};
const SubGhzProtocol subghz_protocol_somfy_keytis = {
.name = SUBGHZ_PROTOCOL_SOMFY_KEYTIS_NAME,
.type = SubGhzProtocolTypeDynamic,
.flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_868 | SubGhzProtocolFlag_AM |
SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send,
.decoder = &subghz_protocol_somfy_keytis_decoder,
.encoder = &subghz_protocol_somfy_keytis_encoder,
};
const SubGhzProtocolEncoder subghz_protocol_somfy_keytis_encoder = {
.alloc = subghz_protocol_encoder_somfy_keytis_alloc,
.free = subghz_protocol_encoder_somfy_keytis_free,
.deserialize = subghz_protocol_encoder_somfy_keytis_deserialize,
.stop = subghz_protocol_encoder_somfy_keytis_stop,
.yield = subghz_protocol_encoder_somfy_keytis_yield,
};
void* subghz_protocol_encoder_somfy_keytis_alloc(SubGhzEnvironment* environment) {
UNUSED(environment);
SubGhzProtocolEncoderSomfyKeytis* instance = malloc(sizeof(SubGhzProtocolEncoderSomfyKeytis));
instance->base.protocol = &subghz_protocol_somfy_keytis;
instance->generic.protocol_name = instance->base.protocol->name;
instance->encoder.repeat = 10;
instance->encoder.size_upload = 512;
instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration));
instance->encoder.is_running = false;
return instance;
}
void* subghz_protocol_decoder_somfy_keytis_alloc(SubGhzEnvironment* environment) {
UNUSED(environment);
SubGhzProtocolDecoderSomfyKeytis* instance = malloc(sizeof(SubGhzProtocolDecoderSomfyKeytis));
instance->base.protocol = &subghz_protocol_somfy_keytis;
instance->generic.protocol_name = instance->base.protocol->name;
return instance;
}
void subghz_protocol_encoder_somfy_keytis_free(void* context) {
furi_assert(context);
SubGhzProtocolEncoderSomfyKeytis* instance = context;
free(instance->encoder.upload);
free(instance);
}
void subghz_protocol_decoder_somfy_keytis_free(void* context) {
furi_assert(context);
SubGhzProtocolDecoderSomfyKeytis* instance = context;
free(instance);
}
void subghz_protocol_decoder_somfy_keytis_reset(void* context) {
furi_assert(context);
SubGhzProtocolDecoderSomfyKeytis* instance = context;
instance->decoder.parser_step = SomfyKeytisDecoderStepReset;
manchester_advance(
instance->manchester_saved_state,
ManchesterEventReset,
&instance->manchester_saved_state,
NULL);
}
static bool
subghz_protocol_somfy_keytis_gen_data(SubGhzProtocolEncoderSomfyKeytis* instance, uint8_t btn) {
UNUSED(btn);
uint64_t data = instance->generic.data ^ (instance->generic.data >> 8);
instance->generic.btn = (data >> 48) & 0xF;
instance->generic.cnt = (data >> 24) & 0xFFFF;
instance->generic.serial = data & 0xFFFFFF;
if(instance->generic.cnt < 0xFFFF) {
if((instance->generic.cnt + furi_hal_subghz_get_rolling_counter_mult()) >= 0xFFFF) {
instance->generic.cnt = 0;
} else {
instance->generic.cnt += furi_hal_subghz_get_rolling_counter_mult();
}
} else if(instance->generic.cnt >= 0xFFFF) {
instance->generic.cnt = 0;
}
uint8_t frame[10];
frame[0] = (0xA << 4) | instance->generic.btn;
frame[1] = 0xF << 4;
frame[2] = instance->generic.cnt >> 8;
frame[3] = instance->generic.cnt;
frame[4] = instance->generic.serial >> 16;
frame[5] = instance->generic.serial >> 8;
frame[6] = instance->generic.serial;
frame[7] = 0xC4;
frame[8] = 0x00;
frame[9] = 0x19;
uint8_t checksum = 0;
for(uint8_t i = 0; i < 7; i++) {
checksum = checksum ^ frame[i] ^ (frame[i] >> 4);
}
checksum &= 0xF;
frame[1] |= checksum;
for(uint8_t i = 1; i < 7; i++) {
frame[i] ^= frame[i - 1];
}
data = 0;
for(uint8_t i = 0; i < 7; ++i) {
data <<= 8;
data |= frame[i];
}
instance->generic.data = data;
data = 0;
for(uint8_t i = 7; i < 10; ++i) {
data <<= 8;
data |= frame[i];
}
instance->generic.data_2 = data;
return true;
}
bool subghz_protocol_somfy_keytis_create_data(
void* context,
FlipperFormat* flipper_format,
uint32_t serial,
uint8_t btn,
uint16_t cnt,
SubGhzRadioPreset* preset) {
furi_assert(context);
SubGhzProtocolEncoderSomfyKeytis* instance = context;
instance->generic.serial = serial;
instance->generic.cnt = cnt;
instance->generic.data_count_bit = 80;
bool res = subghz_protocol_somfy_keytis_gen_data(instance, btn);
if(res) {
res = subghz_block_generic_serialize(&instance->generic, flipper_format, preset);
}
return res;
}
/**
* Generating an upload from data.
* @param instance Pointer to a SubGhzProtocolEncoderKeeloq instance
* @return true On success
*/
static bool subghz_protocol_encoder_somfy_keytis_get_upload(
SubGhzProtocolEncoderSomfyKeytis* instance,
uint8_t btn) {
furi_assert(instance);
//gen new key
if(subghz_protocol_somfy_keytis_gen_data(instance, btn)) {
//ToDo if you need to add a callback to automatically update the data on the display
} else {
return false;
}
size_t index = 0;
//Send header
//Wake up
instance->encoder.upload[index++] = level_duration_make(true, (uint32_t)9415); // 1
instance->encoder.upload[index++] = level_duration_make(false, (uint32_t)89565); // 0
//Hardware sync
for(uint8_t i = 0; i < 12; ++i) {
instance->encoder.upload[index++] = level_duration_make(
true, (uint32_t)subghz_protocol_somfy_keytis_const.te_short * 4); // 1
instance->encoder.upload[index++] = level_duration_make(
false, (uint32_t)subghz_protocol_somfy_keytis_const.te_short * 4); // 0
}
//Software sync
instance->encoder.upload[index++] = level_duration_make(true, (uint32_t)4550); // 1
instance->encoder.upload[index++] =
level_duration_make(false, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 0
//Send key data MSB manchester
for(uint8_t i = instance->generic.data_count_bit - 24; i > 0; i--) {
if(bit_read(instance->generic.data, i - 1)) {
if(instance->encoder.upload[index - 1].level == LEVEL_DURATION_LEVEL_LOW) {
instance->encoder.upload[index - 1].duration *= 2; // 00
instance->encoder.upload[index++] = level_duration_make(
true, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 1
} else {
instance->encoder.upload[index++] = level_duration_make(
false, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 0
instance->encoder.upload[index++] = level_duration_make(
true, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 1
}
} else {
if(instance->encoder.upload[index - 1].level == LEVEL_DURATION_LEVEL_HIGH) {
instance->encoder.upload[index - 1].duration *= 2; // 11
instance->encoder.upload[index++] = level_duration_make(
false, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 0
} else {
instance->encoder.upload[index++] = level_duration_make(
true, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 1
instance->encoder.upload[index++] = level_duration_make(
false, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 0
}
}
}
for(uint8_t i = 24; i > 0; i--) {
if(bit_read(instance->generic.data_2, i - 1)) {
if(instance->encoder.upload[index - 1].level == LEVEL_DURATION_LEVEL_LOW) {
instance->encoder.upload[index - 1].duration *= 2; // 00
instance->encoder.upload[index++] = level_duration_make(
true, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 1
} else {
instance->encoder.upload[index++] = level_duration_make(
false, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 0
instance->encoder.upload[index++] = level_duration_make(
true, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 1
}
} else {
if(instance->encoder.upload[index - 1].level == LEVEL_DURATION_LEVEL_HIGH) {
instance->encoder.upload[index - 1].duration *= 2; // 11
instance->encoder.upload[index++] = level_duration_make(
false, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 0
} else {
instance->encoder.upload[index++] = level_duration_make(
true, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 1
instance->encoder.upload[index++] = level_duration_make(
false, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 0
}
}
}
//Inter-frame silence
if(instance->encoder.upload[index - 1].level == LEVEL_DURATION_LEVEL_LOW) {
instance->encoder.upload[index - 1].duration +=
(uint32_t)subghz_protocol_somfy_keytis_const.te_short * 3;
} else {
instance->encoder.upload[index++] =
level_duration_make(false, (uint32_t)subghz_protocol_somfy_keytis_const.te_short * 3);
}
for(uint8_t i = 0; i < 2; ++i) {
//Hardware sync
for(uint8_t i = 0; i < 6; ++i) {
instance->encoder.upload[index++] = level_duration_make(
true, (uint32_t)subghz_protocol_somfy_keytis_const.te_short * 4); // 1
instance->encoder.upload[index++] = level_duration_make(
false, (uint32_t)subghz_protocol_somfy_keytis_const.te_short * 4); // 0
}
//Software sync
instance->encoder.upload[index++] = level_duration_make(true, (uint32_t)4550); // 1
instance->encoder.upload[index++] =
level_duration_make(false, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 0
//Send key data MSB manchester
for(uint8_t i = instance->generic.data_count_bit - 24; i > 0; i--) {
if(bit_read(instance->generic.data, i - 1)) {
if(instance->encoder.upload[index - 1].level == LEVEL_DURATION_LEVEL_LOW) {
instance->encoder.upload[index - 1].duration *= 2; // 00
instance->encoder.upload[index++] = level_duration_make(
true, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 1
} else {
instance->encoder.upload[index++] = level_duration_make(
false, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 0
instance->encoder.upload[index++] = level_duration_make(
true, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 1
}
} else {
if(instance->encoder.upload[index - 1].level == LEVEL_DURATION_LEVEL_HIGH) {
instance->encoder.upload[index - 1].duration *= 2; // 11
instance->encoder.upload[index++] = level_duration_make(
false, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 0
} else {
instance->encoder.upload[index++] = level_duration_make(
true, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 1
instance->encoder.upload[index++] = level_duration_make(
false, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 0
}
}
}
for(uint8_t i = 24; i > 0; i--) {
if(bit_read(instance->generic.data_2, i - 1)) {
if(instance->encoder.upload[index - 1].level == LEVEL_DURATION_LEVEL_LOW) {
instance->encoder.upload[index - 1].duration *= 2; // 00
instance->encoder.upload[index++] = level_duration_make(
true, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 1
} else {
instance->encoder.upload[index++] = level_duration_make(
false, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 0
instance->encoder.upload[index++] = level_duration_make(
true, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 1
}
} else {
if(instance->encoder.upload[index - 1].level == LEVEL_DURATION_LEVEL_HIGH) {
instance->encoder.upload[index - 1].duration *= 2; // 11
instance->encoder.upload[index++] = level_duration_make(
false, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 0
} else {
instance->encoder.upload[index++] = level_duration_make(
true, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 1
instance->encoder.upload[index++] = level_duration_make(
false, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 0
}
}
}
//Inter-frame silence
if(instance->encoder.upload[index - 1].level == LEVEL_DURATION_LEVEL_LOW) {
instance->encoder.upload[index - 1].duration +=
(uint32_t)subghz_protocol_somfy_keytis_const.te_short * 3;
} else {
instance->encoder.upload[index++] = level_duration_make(
false, (uint32_t)subghz_protocol_somfy_keytis_const.te_short * 3);
}
}
//Inter-frame silence
instance->encoder.upload[index - 1].duration +=
(uint32_t)30415 - (uint32_t)subghz_protocol_somfy_keytis_const.te_short * 3;
size_t size_upload = index;
if(size_upload > instance->encoder.size_upload) {
FURI_LOG_E(TAG, "Size upload exceeds allocated encoder buffer.");
return false;
} else {
instance->encoder.size_upload = size_upload;
}
return true;
}
bool subghz_protocol_encoder_somfy_keytis_deserialize(void* context, FlipperFormat* flipper_format) {
furi_assert(context);
SubGhzProtocolEncoderSomfyKeytis* instance = context;
bool res = false;
do {
if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) {
FURI_LOG_E(TAG, "Deserialize error");
break;
}
//optional parameter parameter
flipper_format_read_uint32(
flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1);
subghz_protocol_encoder_somfy_keytis_get_upload(instance, instance->generic.btn);
if(!flipper_format_rewind(flipper_format)) {
FURI_LOG_E(TAG, "Rewind error");
break;
}
uint8_t key_data[sizeof(uint64_t)] = {0};
for(size_t i = 0; i < sizeof(uint64_t); i++) {
key_data[sizeof(uint64_t) - i - 1] = (instance->generic.data >> i * 8) & 0xFF;
}
if(!flipper_format_update_hex(flipper_format, "Key", key_data, sizeof(uint64_t))) {
FURI_LOG_E(TAG, "Unable to add Key");
break;
}
instance->encoder.is_running = true;
res = true;
} while(false);
return res;
}
void subghz_protocol_encoder_somfy_keytis_stop(void* context) {
SubGhzProtocolEncoderSomfyKeytis* instance = context;
instance->encoder.is_running = false;
}
LevelDuration subghz_protocol_encoder_somfy_keytis_yield(void* context) {
SubGhzProtocolEncoderSomfyKeytis* 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;
}
/**
* Сhecksum calculation.
* @param data Вata for checksum calculation
* @return CRC
*/
static uint8_t subghz_protocol_somfy_keytis_crc(uint64_t data) {
uint8_t crc = 0;
data &= 0xFFF0FFFFFFFFFF;
for(uint8_t i = 0; i < 56; i += 8) {
crc = crc ^ data >> i ^ (data >> (i + 4));
}
return crc & 0xf;
}
void subghz_protocol_decoder_somfy_keytis_feed(void* context, bool level, uint32_t duration) {
furi_assert(context);
SubGhzProtocolDecoderSomfyKeytis* instance = context;
ManchesterEvent event = ManchesterEventReset;
switch(instance->decoder.parser_step) {
case SomfyKeytisDecoderStepReset:
if((level) && DURATION_DIFF(duration, subghz_protocol_somfy_keytis_const.te_short * 4) <
subghz_protocol_somfy_keytis_const.te_delta * 4) {
instance->decoder.parser_step = SomfyKeytisDecoderStepFoundPreambula;
instance->header_count++;
}
break;
case SomfyKeytisDecoderStepFoundPreambula:
if((!level) && (DURATION_DIFF(duration, subghz_protocol_somfy_keytis_const.te_short * 4) <
subghz_protocol_somfy_keytis_const.te_delta * 4)) {
instance->decoder.parser_step = SomfyKeytisDecoderStepCheckPreambula;
} else {
instance->header_count = 0;
instance->decoder.parser_step = SomfyKeytisDecoderStepReset;
}
break;
case SomfyKeytisDecoderStepCheckPreambula:
if(level) {
if(DURATION_DIFF(duration, subghz_protocol_somfy_keytis_const.te_short * 4) <
subghz_protocol_somfy_keytis_const.te_delta * 4) {
instance->decoder.parser_step = SomfyKeytisDecoderStepFoundPreambula;
instance->header_count++;
} else if(
(instance->header_count > 1) &&
(DURATION_DIFF(duration, subghz_protocol_somfy_keytis_const.te_short * 7) <
subghz_protocol_somfy_keytis_const.te_delta * 4)) {
instance->decoder.parser_step = SomfyKeytisDecoderStepDecoderData;
instance->decoder.decode_data = 0;
instance->decoder.decode_count_bit = 0;
instance->press_duration_counter = 0;
manchester_advance(
instance->manchester_saved_state,
ManchesterEventReset,
&instance->manchester_saved_state,
NULL);
manchester_advance(
instance->manchester_saved_state,
ManchesterEventLongHigh,
&instance->manchester_saved_state,
NULL);
}
}
break;
case SomfyKeytisDecoderStepDecoderData:
if(!level) {
if(DURATION_DIFF(duration, subghz_protocol_somfy_keytis_const.te_short) <
subghz_protocol_somfy_keytis_const.te_delta) {
event = ManchesterEventShortLow;
} else if(
DURATION_DIFF(duration, subghz_protocol_somfy_keytis_const.te_long) <
subghz_protocol_somfy_keytis_const.te_delta) {
event = ManchesterEventLongLow;
} else if(
duration >= (subghz_protocol_somfy_keytis_const.te_long +
subghz_protocol_somfy_keytis_const.te_delta)) {
if(instance->decoder.decode_count_bit ==
subghz_protocol_somfy_keytis_const.min_count_bit_for_found) {
//check crc
uint64_t data_tmp = instance->generic.data ^ (instance->generic.data >> 8);
if(((data_tmp >> 40) & 0xF) == subghz_protocol_somfy_keytis_crc(data_tmp)) {
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,
ManchesterEventReset,
&instance->manchester_saved_state,
NULL);
manchester_advance(
instance->manchester_saved_state,
ManchesterEventLongHigh,
&instance->manchester_saved_state,
NULL);
instance->decoder.parser_step = SomfyKeytisDecoderStepReset;
} else {
instance->decoder.parser_step = SomfyKeytisDecoderStepReset;
}
} else {
if(DURATION_DIFF(duration, subghz_protocol_somfy_keytis_const.te_short) <
subghz_protocol_somfy_keytis_const.te_delta) {
event = ManchesterEventShortHigh;
} else if(
DURATION_DIFF(duration, subghz_protocol_somfy_keytis_const.te_long) <
subghz_protocol_somfy_keytis_const.te_delta) {
event = ManchesterEventLongHigh;
} else {
instance->decoder.parser_step = SomfyKeytisDecoderStepReset;
}
}
if(event != ManchesterEventReset) {
bool data;
bool data_ok = manchester_advance(
instance->manchester_saved_state, event, &instance->manchester_saved_state, &data);
if(data_ok) {
if(instance->decoder.decode_count_bit < 56) {
instance->decoder.decode_data = (instance->decoder.decode_data << 1) | data;
} else {
instance->press_duration_counter = (instance->press_duration_counter << 1) |
data;
}
instance->decoder.decode_count_bit++;
}
}
break;
}
}
/**
* Analysis of received data
* @param instance Pointer to a SubGhzBlockGeneric* instance
*/
static void subghz_protocol_somfy_keytis_check_remote_controller(SubGhzBlockGeneric* instance) {
//https://pushstack.wordpress.com/somfy-rts-protocol/
/*
* 604 us
* /
* | 2416us | 2416us | 2416us | 2416us | 4550 us | |
*
* +--------+ +--------+ +---...---+
* + +--------+ +--------+ +--+XXXX...XXX+
*
* | hw. sync. | soft. | |
* | | sync. | data |
*
*
* encrypt | decrypt
*
* package 80 bit pdc key btn crc cnt serial
*
* 0xA453537C4B9855 C40019 => 0xA 4 F 7 002F 37D3CD
* 0xA453537C4B9855 C80026 => 0xA 4 F 7 002F 37D3CD
* 0xA453537C4B9855 CC0033 => 0xA 4 F 7 002F 37D3CD
* 0xA453537C4B9855 D00049 => 0xA 4 F 7 002F 37D3CD
* 0xA453537C4B9855 D4005C => 0xA 4 F 7 002F 37D3CD
* 0xA453537C4B9855 D80063 => 0xA 4 F 7 002F 37D3CD
* 0xA453537C4B9855 DC0076 => 0xA 4 F 7 002F 37D3CD
* 0xA453537C4B9855 E00086 => 0xA 4 F 7 002F 37D3CD
* 0xA453537C4B9855 E40093 => 0xA 4 F 7 002F 37D3CD
* 0xA453537C4B9855 E800AC => 0xA 4 F 7 002F 37D3CD
* 0xA453537C4B9855 EC00B9 => 0xA 4 F 7 002F 37D3CD
* 0xA453537C4B9855 F000C3 => 0xA 4 F 7 002F 37D3CD
* 0xA453537C4B9855 F400D6 => 0xA 4 F 7 002F 37D3CD
* 0xA453537C4B9855 F800E9 => 0xA 4 F 7 002F 37D3CD
* 0xA453537C4B9855 FC00FC => 0xA 4 F 7 002F 37D3CD
* 0xA453537C4B9855 FC0102 => 0xA 4 F 7 002F 37D3CD
* 0xA453537C4B9855 FC0113 => 0xA 4 F 7 002F 37D3CD
* 0xA453537C4B9855 FC0120 => 0xA 4 F 7 002F 37D3CD
* ..........
* 0xA453537C4B9855 FC048F => 0xA 4 F 7 002F 37D3CD
*
* Pdc: "Press Duration Counter" the total delay of the button is sent 72 parcels,
* pdc cnt4b cnt8b pdc_crc
* C40019 => 11 0001 00 0000 00000001 1001
* C80026 => 11 0010 00 0000 00000010 0110
* CC0033 => 11 0011 00 0000 00000011 0011
* D00049 => 11 0100 00 0000 00000100 1001
* D4005C => 11 0101 00 0000 00000101 1100
* D80063 => 11 0110 00 0000 00000110 0011
* DC0076 => 11 0111 00 0000 00000111 0110
* E00086 => 11 1000 00 0000 00001000 0110
* E40093 => 11 1001 00 0000 00001001 0011
* E800AC => 11 1010 00 0000 00001010 1100
* EC00B9 => 11 1011 00 0000 00001011 1001
* F000C3 => 11 1100 00 0000 00001100 0011
* F400D6 => 11 1101 00 0000 00001101 0110
* F800E9 => 11 1110 00 0000 00001110 1001
* FC00FC => 11 1111 00 0000 00001111 1100
* FC0102 => 11 1111 00 0000 00010000 0010
* FC0113 => 11 1111 00 0000 00010001 0011
* FC0120 => 11 1111 00 0000 00010010 0000
*
* Cnt4b: 4-bit counter changes from 1 to 15 then always equals 15
* Cnt8b: 8-bit counter changes from 1 to 72 (0x48)
* Ppdc_crc:
* uint8_t crc=0;
* for(i=4; i<24; i+=4){
* crc ^=(pdc>>i);
* }
* return crc;
* example: crc = 1^0^0^4^C = 9
* 11, 00, 0000: const
*
* Key: “Encryption Key”, Most significant 4-bit are always 0xA, Least Significant bits is
* a linear counter. In the Smoove Origin this counter is increased together with the
* rolling code. But leaving this on a constant value also works. Gerardwr notes that
* for some other types of remotes the MSB is not constant.
* Btn: 4-bit Control codes, this indicates the button that is pressed
* CRC: 4-bit Checksum.
* Ctn: 16-bit rolling code (big-endian) increased with every button press.
* Serial: 24-bit identifier of sending device (little-endian)
*
*
* Decrypt
*
* uint8_t frame[7];
* for (i=1; i < 7; i++) {
* frame[i] = frame[i] ^ frame[i-1];
* }
* or
* uint64 Decrypt = frame ^ (frame>>8);
*
* CRC
*
* uint8_t frame[7];
* for (i=0; i < 7; i++) {
* crc = crc ^ frame[i] ^ (frame[i] >> 4);
* }
* crc = crc & 0xf;
*
*/
uint64_t data = instance->data ^ (instance->data >> 8);
instance->btn = (data >> 48) & 0xF;
instance->cnt = (data >> 24) & 0xFFFF;
instance->serial = data & 0xFFFFFF;
}
/**
* Get button name.
* @param btn Button number, 4 bit
*/
static const char* subghz_protocol_somfy_keytis_get_name_button(uint8_t btn) {
const char* name_btn[0x10] = {
"Unknown",
"0x01",
"0x02",
"Prog",
"Key_1",
"0x05",
"0x06",
"0x07",
"0x08",
"0x09",
"0x0A",
"0x0B",
"0x0C",
"0x0D",
"0x0E",
"0x0F"};
return btn <= 0xf ? name_btn[btn] : name_btn[0];
}
uint8_t subghz_protocol_decoder_somfy_keytis_get_hash_data(void* context) {
furi_assert(context);
SubGhzProtocolDecoderSomfyKeytis* instance = context;
return subghz_protocol_blocks_get_hash_data(
&instance->decoder, (instance->decoder.decode_count_bit / 8) + 1);
}
bool subghz_protocol_decoder_somfy_keytis_serialize(
void* context,
FlipperFormat* flipper_format,
SubGhzRadioPreset* preset) {
furi_assert(context);
SubGhzProtocolDecoderSomfyKeytis* instance = context;
bool res = subghz_block_generic_serialize(&instance->generic, flipper_format, preset);
if(res && !flipper_format_write_uint32(
flipper_format, "Duration_Counter", &instance->press_duration_counter, 1)) {
FURI_LOG_E(TAG, "Unable to add Duration_Counter");
res = false;
}
return res;
}
bool subghz_protocol_decoder_somfy_keytis_deserialize(void* context, FlipperFormat* flipper_format) {
furi_assert(context);
SubGhzProtocolDecoderSomfyKeytis* instance = context;
bool res = false;
do {
if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) {
FURI_LOG_E(TAG, "Deserialize error");
break;
}
if(instance->generic.data_count_bit !=
subghz_protocol_somfy_keytis_const.min_count_bit_for_found) {
FURI_LOG_E(TAG, "Wrong number of bits in key");
break;
}
if(!flipper_format_rewind(flipper_format)) {
FURI_LOG_E(TAG, "Rewind error");
break;
}
if(!flipper_format_read_uint32(
flipper_format,
"Duration_Counter",
(uint32_t*)&instance->press_duration_counter,
1)) {
FURI_LOG_E(TAG, "Missing Duration_Counter");
break;
}
res = true;
} while(false);
return res;
}
void subghz_protocol_decoder_somfy_keytis_get_string(void* context, FuriString* output) {
furi_assert(context);
SubGhzProtocolDecoderSomfyKeytis* instance = context;
subghz_protocol_somfy_keytis_check_remote_controller(&instance->generic);
furi_string_cat_printf(
output,
"%s %db\r\n"
"%lX%08lX%06lX\r\n"
"Sn:0x%06lX \r\n"
"Cnt:0x%04lX\r\n"
"Btn:%s\r\n",
instance->generic.protocol_name,
instance->generic.data_count_bit,
(uint32_t)(instance->generic.data >> 32),
(uint32_t)instance->generic.data,
instance->press_duration_counter,
instance->generic.serial,
instance->generic.cnt,
subghz_protocol_somfy_keytis_get_name_button(instance->generic.btn));
}