mirror of
https://github.com/flipperdevices/flipperzero-firmware.git
synced 2025-12-12 04:41:26 +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>
342 lines
13 KiB
C
342 lines
13 KiB
C
#include "roger.h"
|
|
#include "../blocks/const.h"
|
|
#include "../blocks/decoder.h"
|
|
#include "../blocks/encoder.h"
|
|
#include "../blocks/generic.h"
|
|
#include "../blocks/math.h"
|
|
|
|
#define TAG "SubGhzProtocolRoger"
|
|
|
|
static const SubGhzBlockConst subghz_protocol_roger_const = {
|
|
.te_short = 500,
|
|
.te_long = 1000,
|
|
.te_delta = 270,
|
|
.min_count_bit_for_found = 28,
|
|
};
|
|
|
|
struct SubGhzProtocolDecoderRoger {
|
|
SubGhzProtocolDecoderBase base;
|
|
|
|
SubGhzBlockDecoder decoder;
|
|
SubGhzBlockGeneric generic;
|
|
};
|
|
|
|
struct SubGhzProtocolEncoderRoger {
|
|
SubGhzProtocolEncoderBase base;
|
|
|
|
SubGhzProtocolBlockEncoder encoder;
|
|
SubGhzBlockGeneric generic;
|
|
};
|
|
|
|
typedef enum {
|
|
RogerDecoderStepReset = 0,
|
|
RogerDecoderStepSaveDuration,
|
|
RogerDecoderStepCheckDuration,
|
|
} RogerDecoderStep;
|
|
|
|
const SubGhzProtocolDecoder subghz_protocol_roger_decoder = {
|
|
.alloc = subghz_protocol_decoder_roger_alloc,
|
|
.free = subghz_protocol_decoder_roger_free,
|
|
|
|
.feed = subghz_protocol_decoder_roger_feed,
|
|
.reset = subghz_protocol_decoder_roger_reset,
|
|
|
|
.get_hash_data = subghz_protocol_decoder_roger_get_hash_data,
|
|
.serialize = subghz_protocol_decoder_roger_serialize,
|
|
.deserialize = subghz_protocol_decoder_roger_deserialize,
|
|
.get_string = subghz_protocol_decoder_roger_get_string,
|
|
};
|
|
|
|
const SubGhzProtocolEncoder subghz_protocol_roger_encoder = {
|
|
.alloc = subghz_protocol_encoder_roger_alloc,
|
|
.free = subghz_protocol_encoder_roger_free,
|
|
|
|
.deserialize = subghz_protocol_encoder_roger_deserialize,
|
|
.stop = subghz_protocol_encoder_roger_stop,
|
|
.yield = subghz_protocol_encoder_roger_yield,
|
|
};
|
|
|
|
const SubGhzProtocol subghz_protocol_roger = {
|
|
.name = SUBGHZ_PROTOCOL_ROGER_NAME,
|
|
.type = SubGhzProtocolTypeStatic,
|
|
.flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_868 | SubGhzProtocolFlag_AM |
|
|
SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save |
|
|
SubGhzProtocolFlag_Send,
|
|
|
|
.decoder = &subghz_protocol_roger_decoder,
|
|
.encoder = &subghz_protocol_roger_encoder,
|
|
};
|
|
|
|
void* subghz_protocol_encoder_roger_alloc(SubGhzEnvironment* environment) {
|
|
UNUSED(environment);
|
|
SubGhzProtocolEncoderRoger* instance = malloc(sizeof(SubGhzProtocolEncoderRoger));
|
|
|
|
instance->base.protocol = &subghz_protocol_roger;
|
|
instance->generic.protocol_name = instance->base.protocol->name;
|
|
|
|
instance->encoder.repeat = 10;
|
|
instance->encoder.size_upload = 256;
|
|
instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration));
|
|
instance->encoder.is_running = false;
|
|
return instance;
|
|
}
|
|
|
|
void subghz_protocol_encoder_roger_free(void* context) {
|
|
furi_assert(context);
|
|
SubGhzProtocolEncoderRoger* instance = context;
|
|
free(instance->encoder.upload);
|
|
free(instance);
|
|
}
|
|
|
|
/**
|
|
* Generating an upload from data.
|
|
* @param instance Pointer to a SubGhzProtocolEncoderRoger instance
|
|
*/
|
|
static void subghz_protocol_encoder_roger_get_upload(SubGhzProtocolEncoderRoger* instance) {
|
|
furi_assert(instance);
|
|
size_t index = 0;
|
|
|
|
// Send key and GAP
|
|
for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) {
|
|
if(bit_read(instance->generic.data, i - 1)) {
|
|
// Send bit 1
|
|
instance->encoder.upload[index++] =
|
|
level_duration_make(true, (uint32_t)subghz_protocol_roger_const.te_long);
|
|
if(i == 1) {
|
|
//Send gap if bit was last
|
|
instance->encoder.upload[index++] = level_duration_make(
|
|
false, (uint32_t)subghz_protocol_roger_const.te_short * 19);
|
|
} else {
|
|
instance->encoder.upload[index++] =
|
|
level_duration_make(false, (uint32_t)subghz_protocol_roger_const.te_short);
|
|
}
|
|
} else {
|
|
// Send bit 0
|
|
instance->encoder.upload[index++] =
|
|
level_duration_make(true, (uint32_t)subghz_protocol_roger_const.te_short);
|
|
if(i == 1) {
|
|
//Send gap if bit was last
|
|
instance->encoder.upload[index++] = level_duration_make(
|
|
false, (uint32_t)subghz_protocol_roger_const.te_short * 19);
|
|
} else {
|
|
instance->encoder.upload[index++] =
|
|
level_duration_make(false, (uint32_t)subghz_protocol_roger_const.te_long);
|
|
}
|
|
}
|
|
}
|
|
|
|
instance->encoder.size_upload = index;
|
|
return;
|
|
}
|
|
|
|
/**
|
|
* Analysis of received data
|
|
* @param instance Pointer to a SubGhzBlockGeneric* instance
|
|
*/
|
|
static void subghz_protocol_roger_check_remote_controller(SubGhzBlockGeneric* instance) {
|
|
// Roger Decoder
|
|
// 2025.07 - @xMasterX (MMX)
|
|
|
|
// Key samples
|
|
// 0010001111111001 0001 00100000 // S/N: 0x23F9 Btn: 0x1 End: 0x20
|
|
// 0010001111111001 0010 00100011 // S/N: 0x23F9 Btn: 0x2 End: 0x23
|
|
// 0101011001010110 0001 00000001 // S/N: 0x5656 Btn: 0x1 End: 0x01
|
|
// 0101011001010110 0010 00000010 // S/N: 0x5656 Btn: 0x2 End: 0x02
|
|
// 0000110111111110 0001 00000001 // S/N: 0x0DFE Btn: 0x1 End: 0x01
|
|
// 0000110111111110 0100 00000100 // S/N: 0x0DFE Btn: 0x4 End: 0x04
|
|
// 0000110111111110 0010 00000010 // S/N: 0x0DFE Btn: 0x2 End: 0x02
|
|
// 0000110111111110 1000 00001000 // S/N: 0x0DFE Btn: 0x8 End: 0x08
|
|
|
|
instance->serial = instance->data >> 12;
|
|
instance->btn = (instance->data >> 8) & 0xF;
|
|
}
|
|
|
|
SubGhzProtocolStatus
|
|
subghz_protocol_encoder_roger_deserialize(void* context, FlipperFormat* flipper_format) {
|
|
furi_assert(context);
|
|
SubGhzProtocolEncoderRoger* instance = context;
|
|
SubGhzProtocolStatus ret = SubGhzProtocolStatusError;
|
|
do {
|
|
ret = subghz_block_generic_deserialize_check_count_bit(
|
|
&instance->generic,
|
|
flipper_format,
|
|
subghz_protocol_roger_const.min_count_bit_for_found);
|
|
if(ret != SubGhzProtocolStatusOk) {
|
|
break;
|
|
}
|
|
//optional parameter parameter
|
|
flipper_format_read_uint32(
|
|
flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1);
|
|
|
|
subghz_protocol_roger_check_remote_controller(&instance->generic);
|
|
subghz_protocol_encoder_roger_get_upload(instance);
|
|
instance->encoder.front = 0;
|
|
|
|
instance->encoder.is_running = true;
|
|
} while(false);
|
|
|
|
return ret;
|
|
}
|
|
|
|
void subghz_protocol_encoder_roger_stop(void* context) {
|
|
SubGhzProtocolEncoderRoger* instance = context;
|
|
instance->encoder.is_running = false;
|
|
instance->encoder.front = 0;
|
|
}
|
|
|
|
LevelDuration subghz_protocol_encoder_roger_yield(void* context) {
|
|
SubGhzProtocolEncoderRoger* 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_roger_alloc(SubGhzEnvironment* environment) {
|
|
UNUSED(environment);
|
|
SubGhzProtocolDecoderRoger* instance = malloc(sizeof(SubGhzProtocolDecoderRoger));
|
|
instance->base.protocol = &subghz_protocol_roger;
|
|
instance->generic.protocol_name = instance->base.protocol->name;
|
|
return instance;
|
|
}
|
|
|
|
void subghz_protocol_decoder_roger_free(void* context) {
|
|
furi_assert(context);
|
|
SubGhzProtocolDecoderRoger* instance = context;
|
|
free(instance);
|
|
}
|
|
|
|
void subghz_protocol_decoder_roger_reset(void* context) {
|
|
furi_assert(context);
|
|
SubGhzProtocolDecoderRoger* instance = context;
|
|
instance->decoder.parser_step = RogerDecoderStepReset;
|
|
}
|
|
|
|
void subghz_protocol_decoder_roger_feed(void* context, bool level, volatile uint32_t duration) {
|
|
furi_assert(context);
|
|
SubGhzProtocolDecoderRoger* instance = context;
|
|
|
|
switch(instance->decoder.parser_step) {
|
|
case RogerDecoderStepReset:
|
|
if((!level) && (DURATION_DIFF(duration, subghz_protocol_roger_const.te_short * 19) <
|
|
subghz_protocol_roger_const.te_delta * 5)) {
|
|
//Found GAP
|
|
instance->decoder.decode_data = 0;
|
|
instance->decoder.decode_count_bit = 0;
|
|
instance->decoder.parser_step = RogerDecoderStepSaveDuration;
|
|
}
|
|
break;
|
|
case RogerDecoderStepSaveDuration:
|
|
if(level) {
|
|
instance->decoder.te_last = duration;
|
|
instance->decoder.parser_step = RogerDecoderStepCheckDuration;
|
|
} else {
|
|
instance->decoder.parser_step = RogerDecoderStepReset;
|
|
}
|
|
break;
|
|
case RogerDecoderStepCheckDuration:
|
|
if(!level) {
|
|
// Bit 1 is long and short timing = 1000us HIGH (te_last) and 500us LOW
|
|
if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_roger_const.te_long) <
|
|
subghz_protocol_roger_const.te_delta) &&
|
|
(DURATION_DIFF(duration, subghz_protocol_roger_const.te_short) <
|
|
subghz_protocol_roger_const.te_delta)) {
|
|
subghz_protocol_blocks_add_bit(&instance->decoder, 1);
|
|
instance->decoder.parser_step = RogerDecoderStepSaveDuration;
|
|
// Bit 0 is short and long timing = 500us HIGH (te_last) and 1000us LOW
|
|
} else if(
|
|
(DURATION_DIFF(instance->decoder.te_last, subghz_protocol_roger_const.te_short) <
|
|
subghz_protocol_roger_const.te_delta) &&
|
|
(DURATION_DIFF(duration, subghz_protocol_roger_const.te_long) <
|
|
subghz_protocol_roger_const.te_delta)) {
|
|
subghz_protocol_blocks_add_bit(&instance->decoder, 0);
|
|
instance->decoder.parser_step = RogerDecoderStepSaveDuration;
|
|
} else if(
|
|
// End of the key
|
|
DURATION_DIFF(duration, subghz_protocol_roger_const.te_short * 19) <
|
|
subghz_protocol_roger_const.te_delta * 5) {
|
|
//Found next GAP and add bit 1 or 0
|
|
if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_roger_const.te_long) <
|
|
subghz_protocol_roger_const.te_delta)) {
|
|
subghz_protocol_blocks_add_bit(&instance->decoder, 1);
|
|
}
|
|
if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_roger_const.te_short) <
|
|
subghz_protocol_roger_const.te_delta)) {
|
|
subghz_protocol_blocks_add_bit(&instance->decoder, 0);
|
|
}
|
|
// If got full 28 bits key reading is finished
|
|
if(instance->decoder.decode_count_bit ==
|
|
subghz_protocol_roger_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;
|
|
instance->decoder.parser_step = RogerDecoderStepReset;
|
|
} else {
|
|
instance->decoder.parser_step = RogerDecoderStepReset;
|
|
}
|
|
} else {
|
|
instance->decoder.parser_step = RogerDecoderStepReset;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
uint8_t subghz_protocol_decoder_roger_get_hash_data(void* context) {
|
|
furi_assert(context);
|
|
SubGhzProtocolDecoderRoger* instance = context;
|
|
return subghz_protocol_blocks_get_hash_data(
|
|
&instance->decoder, (instance->decoder.decode_count_bit / 8) + 1);
|
|
}
|
|
|
|
SubGhzProtocolStatus subghz_protocol_decoder_roger_serialize(
|
|
void* context,
|
|
FlipperFormat* flipper_format,
|
|
SubGhzRadioPreset* preset) {
|
|
furi_assert(context);
|
|
SubGhzProtocolDecoderRoger* instance = context;
|
|
return subghz_block_generic_serialize(&instance->generic, flipper_format, preset);
|
|
}
|
|
|
|
SubGhzProtocolStatus
|
|
subghz_protocol_decoder_roger_deserialize(void* context, FlipperFormat* flipper_format) {
|
|
furi_assert(context);
|
|
SubGhzProtocolDecoderRoger* instance = context;
|
|
return subghz_block_generic_deserialize_check_count_bit(
|
|
&instance->generic, flipper_format, subghz_protocol_roger_const.min_count_bit_for_found);
|
|
}
|
|
|
|
void subghz_protocol_decoder_roger_get_string(void* context, FuriString* output) {
|
|
furi_assert(context);
|
|
SubGhzProtocolDecoderRoger* instance = context;
|
|
|
|
subghz_protocol_roger_check_remote_controller(&instance->generic);
|
|
|
|
furi_string_cat_printf(
|
|
output,
|
|
"%s %db\r\n"
|
|
"Key: 0x%07lX\r\n"
|
|
"Serial: 0x%04lX\r\n"
|
|
"End: 0x%02lX\r\n"
|
|
"Btn: %01X",
|
|
instance->generic.protocol_name,
|
|
instance->generic.data_count_bit,
|
|
(uint32_t)(instance->generic.data & 0xFFFFFFF),
|
|
instance->generic.serial,
|
|
(uint32_t)(instance->generic.data & 0xFF),
|
|
instance->generic.btn);
|
|
}
|