diff --git a/applications/subghz/scenes/subghz_scene_set_type.c b/applications/subghz/scenes/subghz_scene_set_type.c index 0d6d48b40..198cd0fd0 100644 --- a/applications/subghz/scenes/subghz_scene_set_type.c +++ b/applications/subghz/scenes/subghz_scene_set_type.c @@ -1,6 +1,7 @@ #include "../subghz_i.h" #include #include +#include #include #include #include @@ -23,6 +24,8 @@ enum SubmenuIndex { SubmenuIndexDoorHan_315_00, SubmenuIndexDoorHan_433_92, SubmenuIndexFirefly_300_00, + SubmenuIndexLiftMaster_315_00, + SubmenuIndexLiftMaster_390_00, }; bool subghz_scene_set_type_submenu_gen_data_protocol( @@ -150,6 +153,18 @@ void subghz_scene_set_type_on_enter(void* context) { SubmenuIndexDoorHan_433_92, subghz_scene_set_type_submenu_callback, subghz); + submenu_add_item( + subghz->submenu, + "LiftMaster_315", + SubmenuIndexLiftMaster_315_00, + subghz_scene_set_type_submenu_callback, + subghz); + submenu_add_item( + subghz->submenu, + "LiftMaster_390", + SubmenuIndexLiftMaster_390_00, + subghz_scene_set_type_submenu_callback, + subghz); submenu_set_selected_item( subghz->submenu, scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneSetType)); @@ -322,6 +337,37 @@ bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) { scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowError); } break; + case SubmenuIndexLiftMaster_315_00: + while(!subghz_protocol_secplus_v1_check_fixed(key)) { + key = subghz_random_serial(); + } + + if(subghz_scene_set_type_submenu_gen_data_protocol( + subghz, + SUBGHZ_PROTOCOL_SECPLUS_V1_NAME, + (uint64_t)key << 32 | 32, + 42, + 315000000, + FuriHalSubGhzPresetOok650Async)) { + generated_protocol = true; + } + break; + case SubmenuIndexLiftMaster_390_00: + while(!subghz_protocol_secplus_v1_check_fixed(key)) { + key = subghz_random_serial(); + } + + if(subghz_scene_set_type_submenu_gen_data_protocol( + subghz, + SUBGHZ_PROTOCOL_SECPLUS_V1_NAME, + (uint64_t)key << 32 | 32, + 42, + 390000000, + FuriHalSubGhzPresetOok650Async)) { + generated_protocol = true; + } + break; + default: return false; break; diff --git a/applications/subghz/scenes/subghz_scene_transmitter.c b/applications/subghz/scenes/subghz_scene_transmitter.c index a72444243..38ab84994 100644 --- a/applications/subghz/scenes/subghz_scene_transmitter.c +++ b/applications/subghz/scenes/subghz_scene_transmitter.c @@ -108,8 +108,5 @@ bool subghz_scene_transmitter_on_event(void* context, SceneManagerEvent event) { void subghz_scene_transmitter_on_exit(void* context) { SubGhz* subghz = context; - //Restore default setting - subghz->txrx->frequency = subghz_setting_get_default_frequency(subghz->setting); - subghz->txrx->preset = FuriHalSubGhzPresetOok650Async; subghz->state_notifications = SubGhzNotificationStateIDLE; } diff --git a/applications/subghz/subghz_i.c b/applications/subghz/subghz_i.c index ff22b4299..690e85f74 100644 --- a/applications/subghz/subghz_i.c +++ b/applications/subghz/subghz_i.c @@ -171,7 +171,9 @@ bool subghz_tx_start(SubGhz* subghz, FlipperFormat* flipper_format) { } if(!ret) { subghz_transmitter_free(subghz->txrx->transmitter); - subghz_idle(subghz); + if(subghz->txrx->txrx_state != SubGhzTxRxStateSleep) { + subghz_idle(subghz); + } } } while(false); diff --git a/lib/subghz/protocols/secplus_v1.c b/lib/subghz/protocols/secplus_v1.c index ff43e2a4c..d16975de0 100644 --- a/lib/subghz/protocols/secplus_v1.c +++ b/lib/subghz/protocols/secplus_v1.c @@ -48,6 +48,8 @@ struct SubGhzProtocolEncoderSecPlus_v1 { SubGhzProtocolBlockEncoder encoder; SubGhzBlockGeneric generic; + + uint8_t data_array[44]; }; typedef enum { @@ -71,23 +73,259 @@ const SubGhzProtocolDecoder subghz_protocol_secplus_v1_decoder = { }; const SubGhzProtocolEncoder subghz_protocol_secplus_v1_encoder = { - .alloc = NULL, - .free = NULL, + .alloc = subghz_protocol_encoder_secplus_v1_alloc, + .free = subghz_protocol_encoder_secplus_v1_free, - .deserialize = NULL, - .stop = NULL, - .yield = NULL, + .deserialize = subghz_protocol_encoder_secplus_v1_deserialize, + .stop = subghz_protocol_encoder_secplus_v1_stop, + .yield = subghz_protocol_encoder_secplus_v1_yield, }; const SubGhzProtocol subghz_protocol_secplus_v1 = { .name = SUBGHZ_PROTOCOL_SECPLUS_V1_NAME, .type = SubGhzProtocolTypeDynamic, - .flag = SubGhzProtocolFlag_315 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable, + .flag = SubGhzProtocolFlag_315 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | + SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Send | SubGhzProtocolFlag_Save, .decoder = &subghz_protocol_secplus_v1_decoder, .encoder = &subghz_protocol_secplus_v1_encoder, }; +void* subghz_protocol_encoder_secplus_v1_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolEncoderSecPlus_v1* instance = malloc(sizeof(SubGhzProtocolEncoderSecPlus_v1)); + + instance->base.protocol = &subghz_protocol_secplus_v1; + instance->generic.protocol_name = instance->base.protocol->name; + + instance->encoder.repeat = 10; + instance->encoder.size_upload = 128; + instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); + instance->encoder.is_runing = false; + return instance; +} + +void subghz_protocol_encoder_secplus_v1_free(void* context) { + furi_assert(context); + SubGhzProtocolEncoderSecPlus_v1* instance = context; + free(instance->encoder.upload); + free(instance); +} + +/** + * Generating an upload from data. + * @param instance Pointer to a SubGhzProtocolEncoderSecPlus_v1 instance + * @return true On success + */ +static bool + subghz_protocol_encoder_secplus_v1_get_upload(SubGhzProtocolEncoderSecPlus_v1* instance) { + furi_assert(instance); + size_t index = 0; + size_t size_upload = (instance->generic.data_count_bit * 2); + if(size_upload > instance->encoder.size_upload) { + FURI_LOG_E(TAG, "Encoder size upload exceeds allocated encoder buffer."); + return false; + } else { + instance->encoder.size_upload = size_upload; + } + + //Send header packet 1 + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_secplus_v1_const.te_short * (116 + 3)); + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_secplus_v1_const.te_short); + + //Send data packet 1 + for(uint8_t i = SECPLUS_V1_PACKET_1_INDEX_BASE + 1; i < SECPLUS_V1_PACKET_1_INDEX_BASE + 21; + i++) { + switch(instance->data_array[i]) { + case SECPLUS_V1_BIT_0: + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_secplus_v1_const.te_short * 3); + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_secplus_v1_const.te_short); + break; + case SECPLUS_V1_BIT_1: + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_secplus_v1_const.te_short * 2); + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_secplus_v1_const.te_short * 2); + break; + case SECPLUS_V1_BIT_2: + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_secplus_v1_const.te_short); + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_secplus_v1_const.te_short * 3); + break; + + default: + FURI_LOG_E(TAG, "Encoder error, wrong bit type"); + return false; + break; + } + } + + //Send header packet 2 + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_secplus_v1_const.te_short * (116)); + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_secplus_v1_const.te_short * 3); + + //Send data packet 2 + for(uint8_t i = SECPLUS_V1_PACKET_2_INDEX_BASE + 1; i < SECPLUS_V1_PACKET_2_INDEX_BASE + 21; + i++) { + switch(instance->data_array[i]) { + case SECPLUS_V1_BIT_0: + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_secplus_v1_const.te_short * 3); + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_secplus_v1_const.te_short); + break; + case SECPLUS_V1_BIT_1: + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_secplus_v1_const.te_short * 2); + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_secplus_v1_const.te_short * 2); + break; + case SECPLUS_V1_BIT_2: + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_secplus_v1_const.te_short); + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_secplus_v1_const.te_short * 3); + break; + + default: + FURI_LOG_E(TAG, "Encoder error, wrong bit type."); + return false; + break; + } + } + + return true; +} + +/** + * Security+ 1.0 message encoding + * @param instance SubGhzProtocolEncoderSecPlus_v1* + */ + +static bool subghz_protocol_secplus_v1_encode(SubGhzProtocolEncoderSecPlus_v1* instance) { + uint32_t fixed = (instance->generic.data >> 32) & 0xFFFFFFFF; + uint32_t rolling = instance->generic.data & 0xFFFFFFFF; + + uint8_t rolling_array[20] = {0}; + uint8_t fixed_array[20] = {0}; + uint32_t acc = 0; + + //increment the counter + rolling += 2; + + //update data + instance->generic.data &= 0xFFFFFFFF00000000; + instance->generic.data |= rolling; + + if(rolling > 0xFFFFFFFF) { + FURI_LOG_E("TAG", "Encode counter overrun"); + return false; + } + if(fixed > 0xCFD41B90) { + FURI_LOG_E("TAG", "Encode wrong fixed data"); + return false; + } + + rolling = subghz_protocol_blocks_reverse_key(rolling, 32); + + for(int i = 19; i > -1; i--) { + rolling_array[i] = rolling % 3; + rolling /= 3; + fixed_array[i] = fixed % 3; + fixed /= 3; + } + + instance->data_array[SECPLUS_V1_PACKET_1_INDEX_BASE] = SECPLUS_V1_PACKET_1_HEADER; + instance->data_array[SECPLUS_V1_PACKET_2_INDEX_BASE] = SECPLUS_V1_PACKET_2_HEADER; + + //encode packet 1 + for(uint8_t i = 1; i < 11; i++) { + acc += rolling_array[i - 1]; + instance->data_array[i * 2 - 1] = rolling_array[i - 1]; + acc += fixed_array[i - 1]; + instance->data_array[i * 2] = acc % 3; + } + + acc = 0; + //encode packet 2 + for(uint8_t i = 11; i < 21; i++) { + acc += rolling_array[i - 1]; + instance->data_array[i * 2] = rolling_array[i - 1]; + acc += fixed_array[i - 1]; + instance->data_array[i * 2 + 1] = acc % 3; + } + + return true; +} + +bool subghz_protocol_encoder_secplus_v1_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolEncoderSecPlus_v1* 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); + + if(!subghz_protocol_secplus_v1_encode(instance)){ + break; + } + if(!subghz_protocol_encoder_secplus_v1_get_upload(instance)){ + 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_runing = true; + + res = true; + } while(false); + + return res; +} + +void subghz_protocol_encoder_secplus_v1_stop(void* context) { + SubGhzProtocolEncoderSecPlus_v1* instance = context; + instance->encoder.is_runing = false; +} + +LevelDuration subghz_protocol_encoder_secplus_v1_yield(void* context) { + SubGhzProtocolEncoderSecPlus_v1* instance = context; + + if(instance->encoder.repeat == 0 || !instance->encoder.is_runing) { + instance->encoder.is_runing = 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_secplus_v1_alloc(SubGhzEnvironment* environment) { UNUSED(environment); SubGhzProtocolDecoderSecPlus_v1* instance = malloc(sizeof(SubGhzProtocolDecoderSecPlus_v1)); @@ -110,7 +348,7 @@ void subghz_protocol_decoder_secplus_v1_reset(void* context) { } /** - * Security+ 1.0 half-message decoding + * Security+ 1.0 message decoding * @param instance SubGhzProtocolDecoderSecPlus_v1* */ @@ -291,6 +529,18 @@ bool subghz_protocol_decoder_secplus_v1_deserialize(void* context, FlipperFormat return subghz_block_generic_deserialize(&instance->generic, flipper_format); } +bool subghz_protocol_secplus_v1_check_fixed (uint32_t fixed) { + //uint8_t id0 = (fixed / 3) % 3; + uint8_t id1 = (fixed / 9) % 3; + uint8_t btn = fixed % 3; + + do{ + if(id1 == 0) return false; + if(!(btn == 0 || btn == 1 || btn == 2)) return false; + } while(false); + return true; +} + void subghz_protocol_decoder_secplus_v1_get_string(void* context, string_t output) { furi_assert(context); SubGhzProtocolDecoderSecPlus_v1* instance = context; diff --git a/lib/subghz/protocols/secplus_v1.h b/lib/subghz/protocols/secplus_v1.h index 891f751cd..1c752df76 100644 --- a/lib/subghz/protocols/secplus_v1.h +++ b/lib/subghz/protocols/secplus_v1.h @@ -10,6 +10,40 @@ extern const SubGhzProtocolDecoder subghz_protocol_secplus_v1_decoder; extern const SubGhzProtocolEncoder subghz_protocol_secplus_v1_encoder; extern const SubGhzProtocol subghz_protocol_secplus_v1; +/** + * Allocate SubGhzProtocolEncoderSecPlus_v1. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolEncoderSecPlus_v1* pointer to a SubGhzProtocolEncoderSecPlus_v1 instance + */ +void* subghz_protocol_encoder_secplus_v1_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolEncoderSecPlus_v1. + * @param context Pointer to a SubGhzProtocolEncoderSecPlus_v1 instance + */ +void subghz_protocol_encoder_secplus_v1_free(void* context); + +/** + * Deserialize and generating an upload to send. + * @param context Pointer to a SubGhzProtocolEncoderSecPlus_v1 instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_encoder_secplus_v1_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Forced transmission stop. + * @param context Pointer to a SubGhzProtocolEncoderSecPlus_v1 instance + */ +void subghz_protocol_encoder_secplus_v1_stop(void* context); + +/** + * Getting the level and duration of the upload to be loaded into DMA. + * @param context Pointer to a SubGhzProtocolEncoderSecPlus_v1 instance + * @return LevelDuration + */ +LevelDuration subghz_protocol_encoder_secplus_v1_yield(void* context); + /** * Allocate SubGhzProtocolDecoderSecPlus_v1. * @param environment Pointer to a SubGhzEnvironment instance @@ -66,6 +100,13 @@ bool subghz_protocol_decoder_secplus_v1_serialize( */ bool subghz_protocol_decoder_secplus_v1_deserialize(void* context, FlipperFormat* flipper_format); +/** + * Validation of fixed parts SubGhzProtocolDecoderSecPlus_v1. + * @param fixed fixed parts + * @return true On success + */ +bool subghz_protocol_secplus_v1_check_fixed(uint32_t fixed); + /** * Getting a textual representation of the received data. * @param context Pointer to a SubGhzProtocolDecoderSecPlus_v1 instance