Kaynağa Gözat

SubGhz: Сreating and delivering Security+ 2.0 (#1273)

* SubGhz: Security+ 2.0 "Add manually" option
* SubGhz: fix message error
* Unit_test: add Security+ 2.0 encoder
* Applications: remove obsolete code
* SubGhz: save menu position in "Add Manually" menu

Co-authored-by: Aleksandr Kutuzov <alleteam@gmail.com>
Skorpionm 3 yıl önce
ebeveyn
işleme
67fbefbe63

+ 0 - 8
applications/applications.c

@@ -420,14 +420,6 @@ const FlipperApplication FLIPPER_DEBUG_APPS[] = {
      .flags = FlipperApplicationFlagDefault},
 #endif
 
-#ifdef APP_SCENED
-    {.app = scened_app,
-     .name = "Templated Scene",
-     .stack_size = 1024,
-     .icon = NULL,
-     .flags = FlipperApplicationFlagDefault},
-#endif
-
 #ifdef APP_LF_RFID
     {.app = lfrfid_debug_app,
      .name = "LF-RFID Debug",

+ 0 - 35
applications/scened_app_example/scene/scened_app_scene_byte_input.cpp

@@ -1,35 +0,0 @@
-#include "scened_app_scene_byte_input.h"
-
-void ScenedAppSceneByteInput::on_enter(ScenedApp* app, bool /* need_restore */) {
-    ByteInputVM* byte_input = app->view_controller;
-    auto callback = cbc::obtain_connector(this, &ScenedAppSceneByteInput::result_callback);
-
-    byte_input->set_result_callback(callback, NULL, app, data, 4);
-    byte_input->set_header_text("Enter the key");
-
-    app->view_controller.switch_to<ByteInputVM>();
-}
-
-bool ScenedAppSceneByteInput::on_event(ScenedApp* app, ScenedApp::Event* event) {
-    bool consumed = false;
-
-    if(event->type == ScenedApp::EventType::ByteEditResult) {
-        app->scene_controller.switch_to_previous_scene();
-        consumed = true;
-    }
-
-    return consumed;
-}
-
-void ScenedAppSceneByteInput::on_exit(ScenedApp* app) {
-    app->view_controller.get<ByteInputVM>()->clean();
-}
-
-void ScenedAppSceneByteInput::result_callback(void* context) {
-    ScenedApp* app = static_cast<ScenedApp*>(context);
-    ScenedApp::Event event;
-
-    event.type = ScenedApp::EventType::ByteEditResult;
-
-    app->view_controller.send_event(&event);
-}

+ 0 - 19
applications/scened_app_example/scene/scened_app_scene_byte_input.h

@@ -1,19 +0,0 @@
-#pragma once
-#include "../scened_app.h"
-
-class ScenedAppSceneByteInput : public GenericScene<ScenedApp> {
-public:
-    void on_enter(ScenedApp* app, bool need_restore) final;
-    bool on_event(ScenedApp* app, ScenedApp::Event* event) final;
-    void on_exit(ScenedApp* app) final;
-
-private:
-    void result_callback(void* context);
-
-    uint8_t data[4] = {
-        0x01,
-        0xA2,
-        0xF4,
-        0xD3,
-    };
-};

+ 0 - 47
applications/scened_app_example/scene/scened_app_scene_start.cpp

@@ -1,47 +0,0 @@
-#include "scened_app_scene_start.h"
-
-typedef enum {
-    SubmenuByteInput,
-} SubmenuIndex;
-
-void ScenedAppSceneStart::on_enter(ScenedApp* app, bool need_restore) {
-    auto submenu = app->view_controller.get<SubmenuVM>();
-    auto callback = cbc::obtain_connector(this, &ScenedAppSceneStart::submenu_callback);
-
-    submenu->add_item("Byte Input", SubmenuByteInput, callback, app);
-
-    if(need_restore) {
-        submenu->set_selected_item(submenu_item_selected);
-    }
-    app->view_controller.switch_to<SubmenuVM>();
-}
-
-bool ScenedAppSceneStart::on_event(ScenedApp* app, ScenedApp::Event* event) {
-    bool consumed = false;
-
-    if(event->type == ScenedApp::EventType::MenuSelected) {
-        submenu_item_selected = event->payload.menu_index;
-        switch(event->payload.menu_index) {
-        case SubmenuByteInput:
-            app->scene_controller.switch_to_next_scene(ScenedApp::SceneType::ByteInputScene);
-            break;
-        }
-        consumed = true;
-    }
-
-    return consumed;
-}
-
-void ScenedAppSceneStart::on_exit(ScenedApp* app) {
-    app->view_controller.get<SubmenuVM>()->clean();
-}
-
-void ScenedAppSceneStart::submenu_callback(void* context, uint32_t index) {
-    ScenedApp* app = static_cast<ScenedApp*>(context);
-    ScenedApp::Event event;
-
-    event.type = ScenedApp::EventType::MenuSelected;
-    event.payload.menu_index = index;
-
-    app->view_controller.send_event(&event);
-}

+ 0 - 13
applications/scened_app_example/scene/scened_app_scene_start.h

@@ -1,13 +0,0 @@
-#pragma once
-#include "../scened_app.h"
-
-class ScenedAppSceneStart : public GenericScene<ScenedApp> {
-public:
-    void on_enter(ScenedApp* app, bool need_restore) final;
-    bool on_event(ScenedApp* app, ScenedApp::Event* event) final;
-    void on_exit(ScenedApp* app) final;
-
-private:
-    void submenu_callback(void* context, uint32_t index);
-    uint32_t submenu_item_selected = 0;
-};

+ 0 - 20
applications/scened_app_example/scened_app.cpp

@@ -1,20 +0,0 @@
-#include "scened_app.h"
-#include "scene/scened_app_scene_start.h"
-#include "scene/scened_app_scene_byte_input.h"
-
-ScenedApp::ScenedApp()
-    : scene_controller{this}
-    , text_store{128}
-    , notification{"notification"} {
-}
-
-ScenedApp::~ScenedApp() {
-}
-
-void ScenedApp::run() {
-    scene_controller.add_scene(SceneType::Start, new ScenedAppSceneStart());
-    scene_controller.add_scene(SceneType::ByteInputScene, new ScenedAppSceneByteInput());
-
-    notification_message(notification, &sequence_blink_green_10);
-    scene_controller.process(100);
-}

+ 0 - 47
applications/scened_app_example/scened_app.h

@@ -1,47 +0,0 @@
-#pragma once
-#include <furi.h>
-#include <furi_hal.h>
-
-#include <generic_scene.hpp>
-#include <scene_controller.hpp>
-#include <view_controller.hpp>
-#include <record_controller.hpp>
-#include <text_store.h>
-
-#include <view_modules/submenu_vm.h>
-#include <view_modules/byte_input_vm.h>
-
-#include <notification/notification_messages.h>
-
-class ScenedApp {
-public:
-    enum class EventType : uint8_t {
-        GENERIC_EVENT_ENUM_VALUES,
-        MenuSelected,
-        ByteEditResult,
-    };
-
-    enum class SceneType : uint8_t {
-        GENERIC_SCENE_ENUM_VALUES,
-        ByteInputScene,
-    };
-
-    class Event {
-    public:
-        union {
-            int32_t menu_index;
-        } payload;
-
-        EventType type;
-    };
-
-    SceneController<GenericScene<ScenedApp>, ScenedApp> scene_controller;
-    TextStore text_store;
-    ViewController<ScenedApp, SubmenuVM, ByteInputVM> view_controller;
-    RecordController<NotificationApp> notification;
-
-    ~ScenedApp();
-    ScenedApp();
-
-    void run();
-};

+ 0 - 11
applications/scened_app_example/scened_app_launcher.cpp

@@ -1,11 +0,0 @@
-#include "scened_app.h"
-
-// app enter function
-extern "C" int32_t scened_app(void* p) {
-    UNUSED(p);
-    ScenedApp* app = new ScenedApp();
-    app->run();
-    delete app;
-
-    return 0;
-}

+ 78 - 5
applications/subghz/scenes/subghz_scene_set_type.c

@@ -1,6 +1,7 @@
 #include "../subghz_i.h"
 #include <lib/subghz/protocols/keeloq.h>
 #include <lib/subghz/protocols/secplus_v1.h>
+#include <lib/subghz/protocols/secplus_v2.h>
 #include <lib/subghz/blocks/math.h>
 #include <dolphin/dolphin.h>
 #include <flipper_format/flipper_format_i.h>
@@ -24,6 +25,9 @@ enum SubmenuIndex {
     SubmenuIndexFirefly_300_00,
     SubmenuIndexLiftMaster_315_00,
     SubmenuIndexLiftMaster_390_00,
+    SubmenuIndexSecPlus_v2_310_00,
+    SubmenuIndexSecPlus_v2_315_00,
+    SubmenuIndexSecPlus_v2_390_00,
 };
 
 bool subghz_scene_set_type_submenu_gen_data_protocol(
@@ -157,6 +161,24 @@ void subghz_scene_set_type_on_enter(void* context) {
         SubmenuIndexLiftMaster_390_00,
         subghz_scene_set_type_submenu_callback,
         subghz);
+    submenu_add_item(
+        subghz->submenu,
+        "Security+2.0_310",
+        SubmenuIndexSecPlus_v2_310_00,
+        subghz_scene_set_type_submenu_callback,
+        subghz);
+    submenu_add_item(
+        subghz->submenu,
+        "Security+2.0_315",
+        SubmenuIndexSecPlus_v2_315_00,
+        subghz_scene_set_type_submenu_callback,
+        subghz);
+    submenu_add_item(
+        subghz->submenu,
+        "Security+2.0_390",
+        SubmenuIndexSecPlus_v2_390_00,
+        subghz_scene_set_type_submenu_callback,
+        subghz);
 
     submenu_set_selected_item(
         subghz->submenu, scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneSetType));
@@ -330,7 +352,6 @@ bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) {
             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,
@@ -345,7 +366,6 @@ bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) {
             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,
@@ -356,17 +376,70 @@ bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) {
                 generated_protocol = true;
             }
             break;
-
+        case SubmenuIndexSecPlus_v2_310_00:
+            subghz->txrx->transmitter = subghz_transmitter_alloc_init(
+                subghz->txrx->environment, SUBGHZ_PROTOCOL_SECPLUS_V2_NAME);
+            if(subghz->txrx->transmitter) {
+                subghz_protocol_secplus_v2_create_data(
+                    subghz_transmitter_get_protocol_instance(subghz->txrx->transmitter),
+                    subghz->txrx->fff_data,
+                    key,
+                    0x68,
+                    0xE500000,
+                    310000000,
+                    FuriHalSubGhzPresetOok650Async);
+                generated_protocol = true;
+            } else {
+                generated_protocol = false;
+            }
+            subghz_transmitter_free(subghz->txrx->transmitter);
+            break;
+        case SubmenuIndexSecPlus_v2_315_00:
+            subghz->txrx->transmitter = subghz_transmitter_alloc_init(
+                subghz->txrx->environment, SUBGHZ_PROTOCOL_SECPLUS_V2_NAME);
+            if(subghz->txrx->transmitter) {
+                subghz_protocol_secplus_v2_create_data(
+                    subghz_transmitter_get_protocol_instance(subghz->txrx->transmitter),
+                    subghz->txrx->fff_data,
+                    key,
+                    0x68,
+                    0xE500000,
+                    315000000,
+                    FuriHalSubGhzPresetOok650Async);
+                generated_protocol = true;
+            } else {
+                generated_protocol = false;
+            }
+            subghz_transmitter_free(subghz->txrx->transmitter);
+            break;
+        case SubmenuIndexSecPlus_v2_390_00:
+            subghz->txrx->transmitter = subghz_transmitter_alloc_init(
+                subghz->txrx->environment, SUBGHZ_PROTOCOL_SECPLUS_V2_NAME);
+            if(subghz->txrx->transmitter) {
+                subghz_protocol_secplus_v2_create_data(
+                    subghz_transmitter_get_protocol_instance(subghz->txrx->transmitter),
+                    subghz->txrx->fff_data,
+                    key,
+                    0x68,
+                    0xE500000,
+                    390000000,
+                    FuriHalSubGhzPresetOok650Async);
+                generated_protocol = true;
+            } else {
+                generated_protocol = false;
+            }
+            subghz_transmitter_free(subghz->txrx->transmitter);
+            break;
         default:
             return false;
             break;
         }
 
+        scene_manager_set_scene_state(subghz->scene_manager, SubGhzSceneSetType, event.event);
+
         if(generated_protocol) {
             subghz_file_name_clear(subghz);
             DOLPHIN_DEED(DolphinDeedSubGhzAddManually);
-            scene_manager_set_scene_state(
-                subghz->scene_manager, SubGhzSceneSetType, SubGhzCustomEventManagerSet);
             scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSaveName);
             return true;
         }

+ 7 - 0
applications/unit_tests/subghz/subghz_test.c

@@ -422,6 +422,12 @@ MU_TEST(subghz_encoder_secplus_v1_test) {
         "Test encoder " SUBGHZ_PROTOCOL_SECPLUS_V1_NAME " error\r\n");
 }
 
+MU_TEST(subghz_encoder_secplus_v2_test) {
+    mu_assert(
+        subghz_encoder_test("/ext/unit_tests/subghz/security_pls_2_0.sub"),
+        "Test encoder " SUBGHZ_PROTOCOL_SECPLUS_V2_NAME " error\r\n");
+}
+
 MU_TEST(subghz_random_test) {
     mu_assert(subghz_decode_random_test(TEST_RANDOM_DIR_NAME), "Random test error\r\n");
 }
@@ -464,6 +470,7 @@ MU_TEST_SUITE(subghz) {
     MU_RUN_TEST(subghz_encoder_megacode_test);
     MU_RUN_TEST(subghz_encoder_holtek_test);
     MU_RUN_TEST(subghz_encoder_secplus_v1_test);
+    MU_RUN_TEST(subghz_encoder_secplus_v2_test);
 
     MU_RUN_TEST(subghz_random_test);
     subghz_test_deinit();

+ 8 - 0
assets/unit_tests/subghz/security_pls_2_0.sub

@@ -0,0 +1,8 @@
+Filetype: Flipper SubGhz Key File
+Version: 1
+Frequency: 315000000
+Preset: FuriHalSubGhzPresetOok650Async
+Protocol: Security+ 2.0
+Bit: 62
+Key: 00 00 3D 29 0F B9 BE EE
+Secplus_packet_1: 00 00 3C 01 6B 19 DD 60

+ 1 - 2
lib/subghz/protocols/came_twee.c

@@ -145,8 +145,7 @@ static LevelDuration
         break;
 
     default:
-        FURI_LOG_E(TAG, "DO CRASH HERE.");
-        furi_crash(NULL);
+        furi_crash("SugGhz: ManchesterEncoderResult is incorrect.");
         break;
     }
     return level_duration_make(data.level, data.duration);

+ 502 - 148
lib/subghz/protocols/secplus_v2.c

@@ -1,5 +1,6 @@
 #include "secplus_v2.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"
@@ -42,6 +43,7 @@ struct SubGhzProtocolEncoderSecPlus_v2 {
 
     SubGhzProtocolBlockEncoder encoder;
     SubGhzBlockGeneric generic;
+    uint64_t secplus_packet_1;
 };
 
 typedef enum {
@@ -63,176 +65,46 @@ const SubGhzProtocolDecoder subghz_protocol_secplus_v2_decoder = {
 };
 
 const SubGhzProtocolEncoder subghz_protocol_secplus_v2_encoder = {
-    .alloc = NULL,
-    .free = NULL,
+    .alloc = subghz_protocol_encoder_secplus_v2_alloc,
+    .free = subghz_protocol_encoder_secplus_v2_free,
 
-    .deserialize = NULL,
-    .stop = NULL,
-    .yield = NULL,
+    .deserialize = subghz_protocol_encoder_secplus_v2_deserialize,
+    .stop = subghz_protocol_encoder_secplus_v2_stop,
+    .yield = subghz_protocol_encoder_secplus_v2_yield,
 };
 
 const SubGhzProtocol subghz_protocol_secplus_v2 = {
     .name = SUBGHZ_PROTOCOL_SECPLUS_V2_NAME,
     .type = SubGhzProtocolTypeDynamic,
-    .flag = SubGhzProtocolFlag_315 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable,
+    .flag = SubGhzProtocolFlag_315 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable |
+            SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Send,
 
     .decoder = &subghz_protocol_secplus_v2_decoder,
     .encoder = &subghz_protocol_secplus_v2_encoder,
 };
 
-void* subghz_protocol_decoder_secplus_v2_alloc(SubGhzEnvironment* environment) {
+void* subghz_protocol_encoder_secplus_v2_alloc(SubGhzEnvironment* environment) {
     UNUSED(environment);
-    SubGhzProtocolDecoderSecPlus_v2* instance = malloc(sizeof(SubGhzProtocolDecoderSecPlus_v2));
+    SubGhzProtocolEncoderSecPlus_v2* instance = malloc(sizeof(SubGhzProtocolEncoderSecPlus_v2));
+
     instance->base.protocol = &subghz_protocol_secplus_v2;
     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_runing = false;
     return instance;
 }
 
-void subghz_protocol_decoder_secplus_v2_free(void* context) {
+void subghz_protocol_encoder_secplus_v2_free(void* context) {
     furi_assert(context);
-    SubGhzProtocolDecoderSecPlus_v2* instance = context;
+    SubGhzProtocolEncoderSecPlus_v2* instance = context;
+    free(instance->encoder.upload);
     free(instance);
 }
 
-void subghz_protocol_decoder_secplus_v2_reset(void* context) {
-    furi_assert(context);
-    // SubGhzProtocolDecoderSecPlus_v2* instance = context;
-    // does not reset the decoder because you need to get 2 parts of the package
-}
-
-static bool subghz_protocol_secplus_v2_check_packet(SubGhzProtocolDecoderSecPlus_v2* instance) {
-    if((instance->decoder.decode_data & SECPLUS_V2_HEADER_MASK) == SECPLUS_V2_HEADER) {
-        if((instance->decoder.decode_data & SECPLUS_V2_PACKET_MASK) == SECPLUS_V2_PACKET_1) {
-            instance->secplus_packet_1 = instance->decoder.decode_data;
-        } else if(
-            ((instance->decoder.decode_data & SECPLUS_V2_PACKET_MASK) == SECPLUS_V2_PACKET_2) &&
-            (instance->secplus_packet_1)) {
-            return true;
-        }
-    }
-    return false;
-}
-
-void subghz_protocol_decoder_secplus_v2_feed(void* context, bool level, uint32_t duration) {
-    furi_assert(context);
-    SubGhzProtocolDecoderSecPlus_v2* instance = context;
-
-    ManchesterEvent event = ManchesterEventReset;
-    switch(instance->decoder.parser_step) {
-    case SecPlus_v2DecoderStepReset:
-        if((!level) && (DURATION_DIFF(duration, subghz_protocol_secplus_v2_const.te_long * 130) <
-                        subghz_protocol_secplus_v2_const.te_delta * 100)) {
-            //Found header Security+ 2.0
-            instance->decoder.parser_step = SecPlus_v2DecoderStepDecoderData;
-            instance->decoder.decode_data = 0;
-            instance->decoder.decode_count_bit = 0;
-            instance->secplus_packet_1 = 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);
-            manchester_advance(
-                instance->manchester_saved_state,
-                ManchesterEventShortLow,
-                &instance->manchester_saved_state,
-                NULL);
-        }
-        break;
-    case SecPlus_v2DecoderStepDecoderData:
-        if(!level) {
-            if(DURATION_DIFF(duration, subghz_protocol_secplus_v2_const.te_short) <
-               subghz_protocol_secplus_v2_const.te_delta) {
-                event = ManchesterEventShortLow;
-            } else if(
-                DURATION_DIFF(duration, subghz_protocol_secplus_v2_const.te_long) <
-                subghz_protocol_secplus_v2_const.te_delta) {
-                event = ManchesterEventLongLow;
-            } else if(
-                duration >= (uint32_t)(subghz_protocol_secplus_v2_const.te_long * 2 +
-                             subghz_protocol_secplus_v2_const.te_delta)) {
-                if(instance->decoder.decode_count_bit >=
-                   subghz_protocol_secplus_v2_const.min_count_bit_for_found) {
-                    instance->generic.data = instance->decoder.decode_data;
-                    instance->generic.data_count_bit = instance->decoder.decode_count_bit;
-                    if(subghz_protocol_secplus_v2_check_packet(instance)) {
-                        if(instance->base.callback)
-                            instance->base.callback(&instance->base, instance->base.context);
-                        instance->decoder.parser_step = SecPlus_v2DecoderStepReset;
-                    }
-                }
-                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);
-                manchester_advance(
-                    instance->manchester_saved_state,
-                    ManchesterEventShortLow,
-                    &instance->manchester_saved_state,
-                    NULL);
-            } else {
-                instance->decoder.parser_step = SecPlus_v2DecoderStepReset;
-            }
-        } else {
-            if(DURATION_DIFF(duration, subghz_protocol_secplus_v2_const.te_short) <
-               subghz_protocol_secplus_v2_const.te_delta) {
-                event = ManchesterEventShortHigh;
-            } else if(
-                DURATION_DIFF(duration, subghz_protocol_secplus_v2_const.te_long) <
-                subghz_protocol_secplus_v2_const.te_delta) {
-                event = ManchesterEventLongHigh;
-            } else {
-                instance->decoder.parser_step = SecPlus_v2DecoderStepReset;
-            }
-        }
-        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;
-    }
-}
-
-/** 
- * Security+ 2.0 half-message decoding
- * @param data data 
- * @param roll_array[] return roll_array part
- * @param fixed[] return fixed part
- * @return true On success
- */
-
-static bool
-    subghz_protocol_secplus_v2_decode_half(uint64_t data, uint8_t roll_array[], uint32_t* fixed) {
-    uint8_t order = (data >> 34) & 0x0f;
-    uint8_t invert = (data >> 30) & 0x0f;
-    uint16_t p[3] = {0};
-
-    for(int i = 29; i >= 0; i -= 3) {
-        p[0] = p[0] << 1 | bit_read(data, i);
-        p[1] = p[1] << 1 | bit_read(data, i - 1);
-        p[2] = p[2] << 1 | bit_read(data, i - 2);
-    }
-
+static bool subghz_protocol_secplus_v2_mix_invet(uint8_t invert, uint16_t p[]) {
     // selectively invert buffers
     switch(invert) {
     case 0x00: // 0b0000 (True, True, False),
@@ -268,7 +140,10 @@ static bool
         FURI_LOG_E(TAG, "Invert FAIL");
         return false;
     }
+    return true;
+}
 
+static bool subghz_protocol_secplus_v2_mix_order_decode(uint8_t order, uint16_t p[]) {
     uint16_t a = p[0], b = p[1], c = p[2];
 
     // selectively reorder buffers
@@ -310,6 +185,80 @@ static bool
         FURI_LOG_E(TAG, "Order FAIL");
         return false;
     }
+    return true;
+}
+
+static bool subghz_protocol_secplus_v2_mix_order_encode(uint8_t order, uint16_t p[]) {
+    uint16_t a, b, c;
+
+    // selectively reorder buffers
+    switch(order) {
+    case 0x06: // 0b0110  2, 1, 0],
+    case 0x09: // 0b1001  2, 1, 0],
+        a = p[2];
+        b = p[1];
+        c = p[0];
+        break;
+    case 0x08: // 0b1000  1, 2, 0],
+    case 0x04: // 0b0100  1, 2, 0],
+        a = p[1];
+        b = p[2];
+        c = p[0];
+        break;
+    case 0x01: // 0b0001 2, 0, 1],
+        a = p[2];
+        b = p[0];
+        c = p[1];
+        break;
+    case 0x00: // 0b0000  0, 2, 1],
+        a = p[0];
+        b = p[2];
+        c = p[1];
+        break;
+    case 0x05: // 0b0101 1, 0, 2],
+        a = p[1];
+        b = p[0];
+        c = p[2];
+        break;
+    case 0x02: // 0b0010 0, 1, 2],
+    case 0x0A: // 0b1010 0, 1, 2],
+        a = p[0];
+        b = p[1];
+        c = p[2];
+        break;
+    default:
+        FURI_LOG_E(TAG, "Order FAIL");
+        return false;
+    }
+
+    p[0] = a;
+    p[1] = b;
+    p[2] = c;
+    return true;
+}
+
+/** 
+ * Security+ 2.0 half-message decoding
+ * @param data data 
+ * @param roll_array[] return roll_array part
+ * @param fixed[] return fixed part
+ * @return true On success
+ */
+
+static bool
+    subghz_protocol_secplus_v2_decode_half(uint64_t data, uint8_t roll_array[], uint32_t* fixed) {
+    uint8_t order = (data >> 34) & 0x0f;
+    uint8_t invert = (data >> 30) & 0x0f;
+    uint16_t p[3] = {0};
+
+    for(int i = 29; i >= 0; i -= 3) {
+        p[0] = p[0] << 1 | bit_read(data, i);
+        p[1] = p[1] << 1 | bit_read(data, i - 1);
+        p[2] = p[2] << 1 | bit_read(data, i - 2);
+    }
+
+    if(!subghz_protocol_secplus_v2_mix_invet(invert, p)) return false;
+    if(!subghz_protocol_secplus_v2_mix_order_decode(order, p)) return false;
 
     data = order << 4 | invert;
     int k = 0;
@@ -393,6 +342,411 @@ static void
     }
 }
 
+/** 
+ * Security+ 2.0 half-message encoding
+ * @param roll_array[] roll_array part
+ * @param fixed[] fixed part
+ * @return return data 
+ */
+
+static uint64_t subghz_protocol_secplus_v2_encode_half(uint8_t roll_array[], uint32_t fixed) {
+    uint64_t data = 0;
+    uint16_t p[3] = {(fixed >> 10) & 0x3FF, fixed & 0x3FF, 0};
+    uint8_t order = roll_array[0] << 2 | roll_array[1];
+    uint8_t invert = roll_array[2] << 2 | roll_array[3];
+    p[2] = (uint16_t)roll_array[4] << 8 | roll_array[5] << 6 | roll_array[6] << 4 |
+           roll_array[7] << 2 | roll_array[8];
+
+    if(!subghz_protocol_secplus_v2_mix_order_encode(order, p)) return 0;
+    if(!subghz_protocol_secplus_v2_mix_invet(invert, p)) return 0;
+
+    for(int i = 0; i < 10; i++) {
+        data <<= 3;
+        data |= bit_read(p[0], 9 - i) << 2 | bit_read(p[1], 9 - i) << 1 | bit_read(p[2], 9 - i);
+    }
+    data |= ((uint64_t)order) << 34 | ((uint64_t)invert) << 30;
+
+    return data;
+}
+
+/** 
+ * Security+ 2.0 message encoding
+ * @param instance SubGhzProtocolEncoderSecPlus_v2* 
+ */
+
+static void subghz_protocol_secplus_v2_encode(SubGhzProtocolEncoderSecPlus_v2* instance) {
+    uint32_t fixed_1[1] = {instance->generic.btn << 12 | instance->generic.serial >> 20};
+    uint32_t fixed_2[1] = {instance->generic.serial & 0xFFFFF};
+    uint8_t rolling_digits[18] = {0};
+    uint8_t roll_1[9] = {0};
+    uint8_t roll_2[9] = {0};
+
+    instance->generic.cnt++;
+    //ToDo it is not known what value the counter starts
+    if(instance->generic.cnt > 0xFFFFFFF) instance->generic.cnt = 0xE500000;
+    uint32_t rolling = subghz_protocol_blocks_reverse_key(instance->generic.cnt, 28);
+
+    for(int8_t i = 17; i > -1; i--) {
+        rolling_digits[i] = rolling % 3;
+        rolling /= 3;
+    }
+
+    roll_2[8] = rolling_digits[0];
+    roll_1[8] = rolling_digits[1];
+
+    roll_2[4] = rolling_digits[2];
+    roll_2[5] = rolling_digits[3];
+    roll_2[6] = rolling_digits[4];
+    roll_2[7] = rolling_digits[5];
+
+    roll_1[4] = rolling_digits[6];
+    roll_1[5] = rolling_digits[7];
+    roll_1[6] = rolling_digits[8];
+    roll_1[7] = rolling_digits[9];
+
+    roll_2[0] = rolling_digits[10];
+    roll_2[1] = rolling_digits[11];
+    roll_2[2] = rolling_digits[12];
+    roll_2[3] = rolling_digits[13];
+
+    roll_1[0] = rolling_digits[14];
+    roll_1[1] = rolling_digits[15];
+    roll_1[2] = rolling_digits[16];
+    roll_1[3] = rolling_digits[17];
+
+    instance->secplus_packet_1 = SECPLUS_V2_HEADER | SECPLUS_V2_PACKET_1 |
+                                 subghz_protocol_secplus_v2_encode_half(roll_1, fixed_1[0]);
+    instance->generic.data = SECPLUS_V2_HEADER | SECPLUS_V2_PACKET_2 |
+                             subghz_protocol_secplus_v2_encode_half(roll_2, fixed_2[0]);
+}
+
+static LevelDuration
+    subghz_protocol_encoder_secplus_v2_add_duration_to_upload(ManchesterEncoderResult result) {
+    LevelDuration data = {.duration = 0, .level = 0};
+    switch(result) {
+    case ManchesterEncoderResultShortLow:
+        data.duration = subghz_protocol_secplus_v2_const.te_short;
+        data.level = false;
+        break;
+    case ManchesterEncoderResultLongLow:
+        data.duration = subghz_protocol_secplus_v2_const.te_long;
+        data.level = false;
+        break;
+    case ManchesterEncoderResultLongHigh:
+        data.duration = subghz_protocol_secplus_v2_const.te_long;
+        data.level = true;
+        break;
+    case ManchesterEncoderResultShortHigh:
+        data.duration = subghz_protocol_secplus_v2_const.te_short;
+        data.level = true;
+        break;
+
+    default:
+        furi_crash("SugGhz: ManchesterEncoderResult is incorrect.");
+        break;
+    }
+    return level_duration_make(data.level, data.duration);
+}
+
+/**
+ * Generating an upload from data.
+ * @param instance Pointer to a SubGhzProtocolEncoderSecPlus_v2 instance
+ */
+static void
+    subghz_protocol_encoder_secplus_v2_get_upload(SubGhzProtocolEncoderSecPlus_v2* instance) {
+    furi_assert(instance);
+    size_t index = 0;
+
+    ManchesterEncoderState enc_state;
+    manchester_encoder_reset(&enc_state);
+    ManchesterEncoderResult result;
+
+    //Send data packet 1
+    for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) {
+        if(!manchester_encoder_advance(
+               &enc_state, bit_read(instance->secplus_packet_1, i - 1), &result)) {
+            instance->encoder.upload[index++] =
+                subghz_protocol_encoder_secplus_v2_add_duration_to_upload(result);
+            manchester_encoder_advance(
+                &enc_state, bit_read(instance->secplus_packet_1, i - 1), &result);
+        }
+        instance->encoder.upload[index++] =
+            subghz_protocol_encoder_secplus_v2_add_duration_to_upload(result);
+    }
+    instance->encoder.upload[index] = subghz_protocol_encoder_secplus_v2_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_secplus_v2_const.te_long * 136);
+
+    //Send data packet 2
+    manchester_encoder_reset(&enc_state);
+    for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) {
+        if(!manchester_encoder_advance(
+               &enc_state, bit_read(instance->generic.data, i - 1), &result)) {
+            instance->encoder.upload[index++] =
+                subghz_protocol_encoder_secplus_v2_add_duration_to_upload(result);
+            manchester_encoder_advance(
+                &enc_state, bit_read(instance->generic.data, i - 1), &result);
+        }
+        instance->encoder.upload[index++] =
+            subghz_protocol_encoder_secplus_v2_add_duration_to_upload(result);
+    }
+    instance->encoder.upload[index] = subghz_protocol_encoder_secplus_v2_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_secplus_v2_const.te_long * 136);
+
+    instance->encoder.size_upload = index;
+}
+
+bool subghz_protocol_encoder_secplus_v2_deserialize(void* context, FlipperFormat* flipper_format) {
+    furi_assert(context);
+    SubGhzProtocolEncoderSecPlus_v2* instance = context;
+    bool res = false;
+    do {
+        if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) {
+            FURI_LOG_E(TAG, "Deserialize error");
+            break;
+        }
+
+        uint8_t key_data[sizeof(uint64_t)] = {0};
+        if(!flipper_format_read_hex(
+               flipper_format, "Secplus_packet_1", key_data, sizeof(uint64_t))) {
+            FURI_LOG_E(TAG, "Secplus_packet_1");
+            break;
+        }
+        for(uint8_t i = 0; i < sizeof(uint64_t); i++) {
+            instance->secplus_packet_1 = instance->secplus_packet_1 << 8 | key_data[i];
+        }
+
+        subghz_protocol_secplus_v2_remote_controller(
+            &instance->generic, instance->secplus_packet_1);
+        subghz_protocol_secplus_v2_encode(instance);
+        //optional parameter parameter
+        flipper_format_read_uint32(
+            flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1);
+        subghz_protocol_encoder_secplus_v2_get_upload(instance);
+
+        //update data
+        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;
+        }
+
+        for(size_t i = 0; i < sizeof(uint64_t); i++) {
+            key_data[sizeof(uint64_t) - i - 1] = (instance->secplus_packet_1 >> i * 8) & 0xFF;
+        }
+        if(!flipper_format_update_hex(
+               flipper_format, "Secplus_packet_1", key_data, sizeof(uint64_t))) {
+            FURI_LOG_E(TAG, "Unable to add Secplus_packet_1");
+            break;
+        }
+
+        instance->encoder.is_runing = true;
+
+        res = true;
+    } while(false);
+
+    return res;
+}
+
+void subghz_protocol_encoder_secplus_v2_stop(void* context) {
+    SubGhzProtocolEncoderSecPlus_v2* instance = context;
+    instance->encoder.is_runing = false;
+}
+
+LevelDuration subghz_protocol_encoder_secplus_v2_yield(void* context) {
+    SubGhzProtocolEncoderSecPlus_v2* 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;
+}
+
+bool subghz_protocol_secplus_v2_create_data(
+    void* context,
+    FlipperFormat* flipper_format,
+    uint32_t serial,
+    uint8_t btn,
+    uint32_t cnt,
+    uint32_t frequency,
+    FuriHalSubGhzPreset preset) {
+    furi_assert(context);
+    SubGhzProtocolEncoderSecPlus_v2* instance = context;
+    instance->generic.serial = serial;
+    instance->generic.cnt = cnt;
+    instance->generic.btn = btn;
+    instance->generic.data_count_bit =
+        (uint8_t)subghz_protocol_secplus_v2_const.min_count_bit_for_found;
+    subghz_protocol_secplus_v2_encode(instance);
+    bool res =
+        subghz_block_generic_serialize(&instance->generic, flipper_format, frequency, preset);
+
+    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->secplus_packet_1 >> i * 8) & 0xFF;
+    }
+
+    if(res &&
+       !flipper_format_write_hex(flipper_format, "Secplus_packet_1", key_data, sizeof(uint64_t))) {
+        FURI_LOG_E(TAG, "Unable to add Secplus_packet_1");
+        res = false;
+    }
+    return res;
+}
+
+void* subghz_protocol_decoder_secplus_v2_alloc(SubGhzEnvironment* environment) {
+    UNUSED(environment);
+    SubGhzProtocolDecoderSecPlus_v2* instance = malloc(sizeof(SubGhzProtocolDecoderSecPlus_v2));
+    instance->base.protocol = &subghz_protocol_secplus_v2;
+    instance->generic.protocol_name = instance->base.protocol->name;
+
+    return instance;
+}
+
+void subghz_protocol_decoder_secplus_v2_free(void* context) {
+    furi_assert(context);
+    SubGhzProtocolDecoderSecPlus_v2* instance = context;
+    free(instance);
+}
+
+void subghz_protocol_decoder_secplus_v2_reset(void* context) {
+    furi_assert(context);
+    // SubGhzProtocolDecoderSecPlus_v2* instance = context;
+    // does not reset the decoder because you need to get 2 parts of the package
+}
+
+static bool subghz_protocol_secplus_v2_check_packet(SubGhzProtocolDecoderSecPlus_v2* instance) {
+    if((instance->decoder.decode_data & SECPLUS_V2_HEADER_MASK) == SECPLUS_V2_HEADER) {
+        if((instance->decoder.decode_data & SECPLUS_V2_PACKET_MASK) == SECPLUS_V2_PACKET_1) {
+            instance->secplus_packet_1 = instance->decoder.decode_data;
+        } else if(
+            ((instance->decoder.decode_data & SECPLUS_V2_PACKET_MASK) == SECPLUS_V2_PACKET_2) &&
+            (instance->secplus_packet_1)) {
+            return true;
+        }
+    }
+    return false;
+}
+
+void subghz_protocol_decoder_secplus_v2_feed(void* context, bool level, uint32_t duration) {
+    furi_assert(context);
+    SubGhzProtocolDecoderSecPlus_v2* instance = context;
+
+    ManchesterEvent event = ManchesterEventReset;
+    switch(instance->decoder.parser_step) {
+    case SecPlus_v2DecoderStepReset:
+        if((!level) && (DURATION_DIFF(duration, subghz_protocol_secplus_v2_const.te_long * 130) <
+                        subghz_protocol_secplus_v2_const.te_delta * 100)) {
+            //Found header Security+ 2.0
+            instance->decoder.parser_step = SecPlus_v2DecoderStepDecoderData;
+            instance->decoder.decode_data = 0;
+            instance->decoder.decode_count_bit = 0;
+            instance->secplus_packet_1 = 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);
+            manchester_advance(
+                instance->manchester_saved_state,
+                ManchesterEventShortLow,
+                &instance->manchester_saved_state,
+                NULL);
+        }
+        break;
+    case SecPlus_v2DecoderStepDecoderData:
+        if(!level) {
+            if(DURATION_DIFF(duration, subghz_protocol_secplus_v2_const.te_short) <
+               subghz_protocol_secplus_v2_const.te_delta) {
+                event = ManchesterEventShortLow;
+            } else if(
+                DURATION_DIFF(duration, subghz_protocol_secplus_v2_const.te_long) <
+                subghz_protocol_secplus_v2_const.te_delta) {
+                event = ManchesterEventLongLow;
+            } else if(
+                duration >= (uint32_t)(subghz_protocol_secplus_v2_const.te_long * 2 +
+                             subghz_protocol_secplus_v2_const.te_delta)) {
+                if(instance->decoder.decode_count_bit >=
+                   subghz_protocol_secplus_v2_const.min_count_bit_for_found) {
+                    instance->generic.data = instance->decoder.decode_data;
+                    instance->generic.data_count_bit = instance->decoder.decode_count_bit;
+                    if(subghz_protocol_secplus_v2_check_packet(instance)) {
+                        if(instance->base.callback)
+                            instance->base.callback(&instance->base, instance->base.context);
+                        instance->decoder.parser_step = SecPlus_v2DecoderStepReset;
+                    }
+                }
+                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);
+                manchester_advance(
+                    instance->manchester_saved_state,
+                    ManchesterEventShortLow,
+                    &instance->manchester_saved_state,
+                    NULL);
+            } else {
+                instance->decoder.parser_step = SecPlus_v2DecoderStepReset;
+            }
+        } else {
+            if(DURATION_DIFF(duration, subghz_protocol_secplus_v2_const.te_short) <
+               subghz_protocol_secplus_v2_const.te_delta) {
+                event = ManchesterEventShortHigh;
+            } else if(
+                DURATION_DIFF(duration, subghz_protocol_secplus_v2_const.te_long) <
+                subghz_protocol_secplus_v2_const.te_delta) {
+                event = ManchesterEventLongHigh;
+            } else {
+                instance->decoder.parser_step = SecPlus_v2DecoderStepReset;
+            }
+        }
+        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_secplus_v2_get_hash_data(void* context) {
     furi_assert(context);
     SubGhzProtocolDecoderSecPlus_v2* instance = context;

+ 55 - 0
lib/subghz/protocols/secplus_v2.h

@@ -10,6 +10,61 @@ extern const SubGhzProtocolDecoder subghz_protocol_secplus_v2_decoder;
 extern const SubGhzProtocolEncoder subghz_protocol_secplus_v2_encoder;
 extern const SubGhzProtocol subghz_protocol_secplus_v2;
 
+/**
+ * Allocate SubGhzProtocolEncoderSecPlus_v2.
+ * @param environment Pointer to a SubGhzEnvironment instance
+ * @return SubGhzProtocolEncoderSecPlus_v2* pointer to a SubGhzProtocolEncoderSecPlus_v2 instance
+ */
+void* subghz_protocol_encoder_secplus_v2_alloc(SubGhzEnvironment* environment);
+
+/**
+ * Free SubGhzProtocolEncoderSecPlus_v2.
+ * @param context Pointer to a SubGhzProtocolEncoderSecPlus_v2 instance
+ */
+void subghz_protocol_encoder_secplus_v2_free(void* context);
+
+/**
+ * Deserialize and generating an upload to send.
+ * @param context Pointer to a SubGhzProtocolEncoderSecPlus_v2 instance
+ * @param flipper_format Pointer to a FlipperFormat instance
+ * @return true On success
+ */
+bool subghz_protocol_encoder_secplus_v2_deserialize(void* context, FlipperFormat* flipper_format);
+
+/**
+ * Forced transmission stop.
+ * @param context Pointer to a SubGhzProtocolEncoderSecPlus_v2 instance
+ */
+void subghz_protocol_encoder_secplus_v2_stop(void* context);
+
+/**
+ * Getting the level and duration of the upload to be loaded into DMA.
+ * @param context Pointer to a SubGhzProtocolEncoderSecPlus_v2 instance
+ * @return LevelDuration 
+ */
+LevelDuration subghz_protocol_encoder_secplus_v2_yield(void* context);
+
+/**
+ * Key generation from simple data.
+ * @param context Pointer to a SubGhzProtocolEncoderSecPlus_v2 instance
+ * @param flipper_format Pointer to a FlipperFormat instance
+ * @param serial Serial number, 32 bit
+ * @param btn Button number, 8 bit
+ * @param cnt Container value, 28 bit
+ * @param manufacture_name Name of manufacturer's key
+ * @param frequency Transmission frequency, Hz
+ * @param preset Modulation, FuriHalSubGhzPreset
+ * @return true On success
+ */
+bool subghz_protocol_secplus_v2_create_data(
+    void* context,
+    FlipperFormat* flipper_format,
+    uint32_t serial,
+    uint8_t btn,
+    uint32_t cnt,
+    uint32_t frequency,
+    FuriHalSubGhzPreset preset);
+
 /**
  * Allocate SubGhzProtocolDecoderSecPlus_v2.
  * @param environment Pointer to a SubGhzEnvironment instance