From e8211226f3a4f7609143ef02d6ef684b9fb41f00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=82=E3=81=8F?= Date: Wed, 30 Jun 2021 00:19:20 +0300 Subject: [PATCH] [FL-1496] SubGhz: Library, Cli, Application (#543) * ApiHal: set frequency and path in one go. Drivers: proper frequency registers calculation for CC1101. Update subghz cli to match new api. * SubGhz: preparation for parsers porting, tim2 sharing * ApiHal: add interrupts API, move all TIM2 related things there. * SubGhz: refactor protocol lib and add keeloq. * SubGhz: proper init_set for keeloq manafacture key * SubGhz: port more protocols to lib * SubGhz: load keeloq manufacture keys from sd card (if any). * SubGhz: format output from protocols. * SubGhz: use default frequency in subghz_rx cli command. * SubGhz: keeloq key types * Fix compillation error when internal storage disabled * SubGhz: minor cleanup * SubGhz: properly handle timeout and reset signal in subghz_rx * SubGhz: Worker, Capture View. Furi: emulate thread join. * SubGhz: free strings on keeloq key load end * SubGhz: update protocols reporting API, app refactoring and capture view, update API HAL usage. * SubGhz: update dump formatting * ApiHal: backport subghz preset to F5 * ApiHal: backport subghz frequency range to F5 --- applications/applications.c | 3 +- applications/subghz/subghz.c | 111 ++--- applications/subghz/subghz_cli.c | 161 ++++++-- applications/subghz/subghz_cli.h | 4 + applications/subghz/subghz_i.h | 17 +- applications/subghz/views/subghz_capture.c | 188 +++++++++ applications/subghz/views/subghz_capture.h | 11 + .../subghz/{ => views}/subghz_static.c | 26 +- .../subghz/{ => views}/subghz_static.h | 0 .../subghz/{ => views}/subghz_test_basic.c | 6 +- .../subghz/{ => views}/subghz_test_basic.h | 0 .../subghz/{ => views}/subghz_test_packet.c | 6 +- .../subghz/{ => views}/subghz_test_packet.h | 0 core/furi/thread.c | 5 +- .../targets/api-hal-include/api-hal-subghz.h | 38 ++ firmware/targets/api-hal-include/api-hal.h | 1 - firmware/targets/f5/api-hal/api-hal-subghz.c | 75 +++- .../targets/f6/api-hal/api-hal-interrupt.c | 42 ++ .../targets/f6/api-hal/api-hal-interrupt.h | 23 ++ firmware/targets/f6/api-hal/api-hal-irda.c | 47 ++- firmware/targets/f6/api-hal/api-hal-irda_i.h | 10 - firmware/targets/f6/api-hal/api-hal-subghz.c | 168 +++++++- firmware/targets/f6/api-hal/api-hal-tim.c | 45 --- firmware/targets/f6/api-hal/api-hal-tim_i.h | 7 - firmware/targets/f6/api-hal/api-interrupts.c | 16 - lib/drivers/cc1101.c | 14 +- lib/drivers/cc1101_regs.h | 6 + lib/fl_subghz/protocols/subghz_protocol.c | 130 ++++++ lib/fl_subghz/protocols/subghz_protocol.h | 19 + .../protocols/subghz_protocol_came.c | 119 ++++++ .../protocols/subghz_protocol_came.h | 13 + lib/fl_subghz/protocols/subghz_protocol_cfm.c | 4 + .../fl_subghz/protocols/subghz_protocol_cfm.h | 0 .../protocols/subghz_protocol_common.c | 70 ++++ .../protocols/subghz_protocol_common.h | 53 +++ .../protocols/subghz_protocol_keeloq.c | 378 ++++++++++++++++++ .../protocols/subghz_protocol_keeloq.h | 17 + .../protocols/subghz_protocol_nice_flo.c | 117 ++++++ .../protocols/subghz_protocol_nice_flo.h | 15 + .../protocols/subghz_protocol_nice_flor_s.c | 133 ++++++ .../protocols/subghz_protocol_nice_flor_s.h | 15 + .../protocols/subghz_protocol_princeton.c | 109 +++++ .../protocols/subghz_protocol_princeton.h | 15 + lib/fl_subghz/subghz_worker.c | 110 +++++ lib/fl_subghz/subghz_worker.h | 25 ++ lib/lib.mk | 7 +- 46 files changed, 2114 insertions(+), 265 deletions(-) mode change 100755 => 100644 applications/subghz/subghz_cli.c create mode 100644 applications/subghz/views/subghz_capture.c create mode 100644 applications/subghz/views/subghz_capture.h rename applications/subghz/{ => views}/subghz_static.c (85%) rename applications/subghz/{ => views}/subghz_static.h (100%) rename applications/subghz/{ => views}/subghz_test_basic.c (98%) rename applications/subghz/{ => views}/subghz_test_basic.h (100%) rename applications/subghz/{ => views}/subghz_test_packet.c (98%) rename applications/subghz/{ => views}/subghz_test_packet.h (100%) create mode 100644 firmware/targets/f6/api-hal/api-hal-interrupt.c create mode 100644 firmware/targets/f6/api-hal/api-hal-interrupt.h delete mode 100644 firmware/targets/f6/api-hal/api-hal-irda_i.h delete mode 100644 firmware/targets/f6/api-hal/api-hal-tim.c delete mode 100644 firmware/targets/f6/api-hal/api-hal-tim_i.h delete mode 100644 firmware/targets/f6/api-hal/api-interrupts.c create mode 100644 lib/fl_subghz/protocols/subghz_protocol.c create mode 100644 lib/fl_subghz/protocols/subghz_protocol.h create mode 100644 lib/fl_subghz/protocols/subghz_protocol_came.c create mode 100644 lib/fl_subghz/protocols/subghz_protocol_came.h create mode 100644 lib/fl_subghz/protocols/subghz_protocol_cfm.c rename firmware/targets/f6/api-hal/api-hal-tim.h => lib/fl_subghz/protocols/subghz_protocol_cfm.h (100%) create mode 100644 lib/fl_subghz/protocols/subghz_protocol_common.c create mode 100644 lib/fl_subghz/protocols/subghz_protocol_common.h create mode 100644 lib/fl_subghz/protocols/subghz_protocol_keeloq.c create mode 100644 lib/fl_subghz/protocols/subghz_protocol_keeloq.h create mode 100644 lib/fl_subghz/protocols/subghz_protocol_nice_flo.c create mode 100644 lib/fl_subghz/protocols/subghz_protocol_nice_flo.h create mode 100644 lib/fl_subghz/protocols/subghz_protocol_nice_flor_s.c create mode 100644 lib/fl_subghz/protocols/subghz_protocol_nice_flor_s.h create mode 100644 lib/fl_subghz/protocols/subghz_protocol_princeton.c create mode 100644 lib/fl_subghz/protocols/subghz_protocol_princeton.h create mode 100644 lib/fl_subghz/subghz_worker.c create mode 100644 lib/fl_subghz/subghz_worker.h diff --git a/applications/applications.c b/applications/applications.c index 694e136e..977c4805 100644 --- a/applications/applications.c +++ b/applications/applications.c @@ -184,7 +184,8 @@ const FlipperApplication FLIPPER_APPS[] = { #endif #ifdef APP_SUBGHZ - {.app = subghz_app, .name = "Sub-1 GHz", .stack_size = 1024, .icon = A_Sub1ghz_14}, + // TODO: decrease stack after SD API refactoring + {.app = subghz_app, .name = "Sub-1 GHz", .stack_size = 4096, .icon = A_Sub1ghz_14}, #endif #ifdef APP_LF_RFID diff --git a/applications/subghz/subghz.c b/applications/subghz/subghz.c index 607cda76..6190a72b 100644 --- a/applications/subghz/subghz.c +++ b/applications/subghz/subghz.c @@ -1,83 +1,33 @@ #include "subghz_i.h" -const SubGhzFrequency subghz_frequencies[] = { - /* 301 */ - { - .frequency = 301000000, - .path = ApiHalSubGhzPath315, - }, - /* 315 */ - { - .frequency = 315000000, - .path = ApiHalSubGhzPath315, - }, - /* 346 - 385 */ - { - .frequency = 346000000, - .path = ApiHalSubGhzPath315, - }, - { - .frequency = 385000000, - .path = ApiHalSubGhzPath315, - }, - /* LPD433 first, mid, last channels */ - { - .frequency = 433075000, - .path = ApiHalSubGhzPath433, - }, - { - .frequency = 433920000, - .path = ApiHalSubGhzPath433, - }, - { - .frequency = 434775000, - .path = ApiHalSubGhzPath433, - }, - /* 438.9 - 781 */ - { - .frequency = 438900000, - .path = ApiHalSubGhzPath433, - }, - { - .frequency = 463000000, - .path = ApiHalSubGhzPath433, - }, - { - .frequency = 781000000, - .path = ApiHalSubGhzPath868, - }, - /* 868.35 */ - { - .frequency = 868350000, - .path = ApiHalSubGhzPath868, - }, - /* 915 */ - { - .frequency = 915000000, - .path = ApiHalSubGhzPath868, - }, - /* 925 */ - { - .frequency = 925000000, - .path = ApiHalSubGhzPath868, - }, +const uint32_t subghz_frequencies[] = { + /* 300 - 348 */ + 300000000, + 315000000, + 348000000, + /* 387 - 464 */ + 387000000, + 433075000, /* LPD433 first */ + 433920000, /* LPD433 mid */ + 434775000, /* LPD433 last channels */ + 438900000, + 464000000, + /* 779 - 928 */ + 779000000, + 868350000, + 915000000, + 925000000, + 928000000, }; -const uint32_t subghz_frequencies_count = sizeof(subghz_frequencies) / sizeof(SubGhzFrequency); +const uint32_t subghz_frequencies_count = sizeof(subghz_frequencies) / sizeof(uint32_t); const uint32_t subghz_frequencies_433_92 = 5; void subghz_menu_callback(void* context, uint32_t index) { furi_assert(context); - SubGhz* subghz = context; - if(index == 0) { - view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewTestBasic); - } else if(index == 1) { - view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewTestPacket); - } else if(index == 2) { - view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewStatic); - } + view_dispatcher_switch_to_view(subghz->view_dispatcher, index); } uint32_t subghz_exit(void* context) { @@ -98,14 +48,25 @@ SubGhz* subghz_alloc() { // Menu subghz->submenu = submenu_alloc(); - submenu_add_item(subghz->submenu, "Basic Test", 0, subghz_menu_callback, subghz); - submenu_add_item(subghz->submenu, "Packet Test", 1, subghz_menu_callback, subghz); - submenu_add_item(subghz->submenu, "Static Code", 2, subghz_menu_callback, subghz); + submenu_add_item(subghz->submenu, "Capture", SubGhzViewCapture, subghz_menu_callback, subghz); + submenu_add_item( + subghz->submenu, "Basic Test", SubGhzViewTestBasic, subghz_menu_callback, subghz); + submenu_add_item( + subghz->submenu, "Packet Test", SubGhzViewTestPacket, subghz_menu_callback, subghz); + submenu_add_item( + subghz->submenu, "Static Code", SubGhzViewStatic, subghz_menu_callback, subghz); View* submenu_view = submenu_get_view(subghz->submenu); view_set_previous_callback(submenu_view, subghz_exit); view_dispatcher_add_view(subghz->view_dispatcher, SubGhzViewMenu, submenu_view); + // Capture + subghz->subghz_capture = subghz_capture_alloc(); + view_dispatcher_add_view( + subghz->view_dispatcher, + SubGhzViewCapture, + subghz_capture_get_view(subghz->subghz_capture)); + // Basic Test Module subghz->subghz_test_basic = subghz_test_basic_alloc(); view_dispatcher_add_view( @@ -150,6 +111,10 @@ void subghz_free(SubGhz* subghz) { view_dispatcher_remove_view(subghz->view_dispatcher, SubGhzViewMenu); submenu_free(subghz->submenu); + // Capture + view_dispatcher_remove_view(subghz->view_dispatcher, SubGhzViewCapture); + subghz_capture_free(subghz->subghz_capture); + // View Dispatcher view_dispatcher_free(subghz->view_dispatcher); diff --git a/applications/subghz/subghz_cli.c b/applications/subghz/subghz_cli.c old mode 100755 new mode 100644 index 419da1b6..173e1b42 --- a/applications/subghz/subghz_cli.c +++ b/applications/subghz/subghz_cli.c @@ -1,6 +1,11 @@ #include "subghz_cli.h" + #include #include +#include + +#define CC1101_FREQUENCY_RANGE_STR \ + "300000000...348000000 or 387000000...464000000 or 779000000...928000000" static const uint8_t subghz_test_packet_data[] = { 0x30, // 48bytes to transmit @@ -9,6 +14,15 @@ static const uint8_t subghz_test_packet_data[] = { 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, }; +bool subghz_check_frequency_range(uint32_t frequency) { + if(!(frequency >= 300000000 && frequency <= 348000000) && + !(frequency >= 387000000 && frequency <= 464000000) && + !(frequency >= 779000000 && frequency <= 928000000)) { + return false; + } + return true; +} + void subghz_cli_init() { Cli* cli = furi_record_open("cli"); @@ -16,12 +30,14 @@ void subghz_cli_init() { cli_add_command(cli, "subghz_rx_carrier", subghz_cli_command_rx_carrier, NULL); cli_add_command(cli, "subghz_tx_pt", subghz_cli_command_tx_pt, NULL); cli_add_command(cli, "subghz_rx_pt", subghz_cli_command_rx_pt, NULL); + cli_add_command(cli, "subghz_tx", subghz_cli_command_tx, NULL); + cli_add_command(cli, "subghz_rx", subghz_cli_command_rx, NULL); furi_record_close("cli"); } void subghz_cli_command_tx_carrier(Cli* cli, string_t args, void* context) { - uint32_t frequency; + uint32_t frequency = 0; int ret = sscanf(string_get_cstr(args), "%lu", &frequency); if(ret != 1) { printf("sscanf returned %d, frequency: %lu\r\n", ret, frequency); @@ -29,23 +45,17 @@ void subghz_cli_command_tx_carrier(Cli* cli, string_t args, void* context) { return; } - if(frequency < 300000000 || frequency > 925000000) { - printf("Frequency must be in 300000000...925000000 range, not %lu\r\n", frequency); + if(!subghz_check_frequency_range(frequency)) { + printf( + "Frequency must be in " CC1101_FREQUENCY_RANGE_STR " range, not %lu\r\n", frequency); return; } api_hal_subghz_reset(); api_hal_subghz_load_preset(ApiHalSubGhzPresetOokAsync); - frequency = api_hal_subghz_set_frequency(frequency); + frequency = api_hal_subghz_set_frequency_and_path(frequency); printf("Transmitting at frequency %lu Hz\r\n", frequency); printf("Press CTRL+C to stop\r\n"); - if(frequency < 400000000) { - api_hal_subghz_set_path(ApiHalSubGhzPath315); - } else if(frequency < 500000000) { - api_hal_subghz_set_path(ApiHalSubGhzPath433); - } else { - api_hal_subghz_set_path(ApiHalSubGhzPath868); - } hal_gpio_init(&gpio_cc1101_g0, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); hal_gpio_write(&gpio_cc1101_g0, false); @@ -62,7 +72,7 @@ void subghz_cli_command_tx_carrier(Cli* cli, string_t args, void* context) { } void subghz_cli_command_rx_carrier(Cli* cli, string_t args, void* context) { - uint32_t frequency; + uint32_t frequency = 0; int ret = sscanf(string_get_cstr(args), "%lu", &frequency); if(ret != 1) { printf("sscanf returned %d, frequency: %lu\r\n", ret, frequency); @@ -70,23 +80,17 @@ void subghz_cli_command_rx_carrier(Cli* cli, string_t args, void* context) { return; } - if(frequency < 300000000 || frequency > 925000000) { - printf("Frequency must be in 300000000...925000000 range, not %lu\r\n", frequency); + if(!subghz_check_frequency_range(frequency)) { + printf( + "Frequency must be in " CC1101_FREQUENCY_RANGE_STR " range, not %lu\r\n", frequency); return; } api_hal_subghz_reset(); api_hal_subghz_load_preset(ApiHalSubGhzPresetOokAsync); - frequency = api_hal_subghz_set_frequency(frequency); + frequency = api_hal_subghz_set_frequency_and_path(frequency); printf("Receiving at frequency %lu Hz\r\n", frequency); printf("Press CTRL+C to stop\r\n"); - if(frequency < 400000000) { - api_hal_subghz_set_path(ApiHalSubGhzPath315); - } else if(frequency < 500000000) { - api_hal_subghz_set_path(ApiHalSubGhzPath433); - } else { - api_hal_subghz_set_path(ApiHalSubGhzPath868); - } hal_gpio_init(&gpio_cc1101_g0, GpioModeAnalog, GpioPullNo, GpioSpeedLow); @@ -103,7 +107,7 @@ void subghz_cli_command_rx_carrier(Cli* cli, string_t args, void* context) { } void subghz_cli_command_tx_pt(Cli* cli, string_t args, void* context) { - uint32_t frequency; + uint32_t frequency = 0; uint32_t pattern; uint32_t count; @@ -120,8 +124,9 @@ void subghz_cli_command_tx_pt(Cli* cli, string_t args, void* context) { return; } - if(frequency < 300000000 || frequency > 925000000) { - printf("Frequency must be in 300000000...925000000 range, not %lu\r\n", frequency); + if(!subghz_check_frequency_range(frequency)) { + printf( + "Frequency must be in " CC1101_FREQUENCY_RANGE_STR " range, not %lu\r\n", frequency); return; } if(pattern > 1) { @@ -133,14 +138,7 @@ void subghz_cli_command_tx_pt(Cli* cli, string_t args, void* context) { api_hal_subghz_load_preset(ApiHalSubGhzPreset2FskPacket); - frequency = api_hal_subghz_set_frequency(frequency); - if(frequency < 400000000) { - api_hal_subghz_set_path(ApiHalSubGhzPath315); - } else if(frequency < 500000000) { - api_hal_subghz_set_path(ApiHalSubGhzPath433); - } else { - api_hal_subghz_set_path(ApiHalSubGhzPath868); - } + frequency = api_hal_subghz_set_frequency_and_path(frequency); hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullNo, GpioSpeedLow); uint8_t status = api_hal_subghz_get_status(); @@ -163,7 +161,7 @@ void subghz_cli_command_tx_pt(Cli* cli, string_t args, void* context) { } void subghz_cli_command_rx_pt(Cli* cli, string_t args, void* context) { - uint32_t frequency; + uint32_t frequency = 0; int ret = sscanf(string_get_cstr(args), "%lu", &frequency); if(ret != 1) { @@ -172,8 +170,9 @@ void subghz_cli_command_rx_pt(Cli* cli, string_t args, void* context) { return; } - if(frequency < 300000000 || frequency > 925000000) { - printf("Frequency must be in 300000000...925000000 range, not %lu\r\n", frequency); + if(!subghz_check_frequency_range(frequency)) { + printf( + "Frequency must be in " CC1101_FREQUENCY_RANGE_STR " range, not %lu\r\n", frequency); return; } @@ -181,14 +180,7 @@ void subghz_cli_command_rx_pt(Cli* cli, string_t args, void* context) { api_hal_subghz_idle(); api_hal_subghz_load_preset(ApiHalSubGhzPreset2FskPacket); - frequency = api_hal_subghz_set_frequency(frequency); - if(frequency < 400000000) { - api_hal_subghz_set_path(ApiHalSubGhzPath315); - } else if(frequency < 500000000) { - api_hal_subghz_set_path(ApiHalSubGhzPath433); - } else { - api_hal_subghz_set_path(ApiHalSubGhzPath868); - } + frequency = api_hal_subghz_set_frequency_and_path(frequency); hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullNo, GpioSpeedLow); uint8_t status = api_hal_subghz_get_status(); @@ -216,3 +208,84 @@ void subghz_cli_command_rx_pt(Cli* cli, string_t args, void* context) { api_hal_subghz_set_path(ApiHalSubGhzPathIsolate); hal_gpio_init(&gpio_cc1101_g0, GpioModeAnalog, GpioPullNo, GpioSpeedLow); } + +void subghz_cli_command_tx(Cli* cli, string_t args, void* context) { +} + +#include + +volatile bool subghz_cli_overrun = false; + +void subghz_cli_command_rx_callback( + ApiHalSubGhzCaptureLevel level, + uint32_t duration, + void* context) { + BaseType_t xHigherPriorityTaskWoken = pdFALSE; + LevelPair pair = {.level = level, .duration = duration}; + if(subghz_cli_overrun) { + subghz_cli_overrun = false; + pair.level = ApiHalSubGhzCaptureLevelOverrun; + } + size_t ret = + xStreamBufferSendFromISR(context, &pair, sizeof(LevelPair), &xHigherPriorityTaskWoken); + if(sizeof(LevelPair) != ret) subghz_cli_overrun = true; + portYIELD_FROM_ISR(xHigherPriorityTaskWoken); +} + +void subghz_cli_command_rx(Cli* cli, string_t args, void* context) { + uint32_t frequency = 433920000; + if(string_size(args)) { + int ret = sscanf(string_get_cstr(args), "%lu", &frequency); + if(ret != 1) { + printf("sscanf returned %d, frequency: %lu\r\n", ret, frequency); + cli_print_usage("subghz_rx", "", string_get_cstr(args)); + return; + } + + if(!subghz_check_frequency_range(frequency)) { + printf( + "Frequency must be in " CC1101_FREQUENCY_RANGE_STR " range, not %lu\r\n", + frequency); + return; + } + } + + api_hal_subghz_reset(); + api_hal_subghz_idle(); + api_hal_subghz_load_preset(ApiHalSubGhzPresetMP); + + SubGhzProtocol* protocol = subghz_protocol_alloc(); + subghz_protocol_load_keeloq_file(protocol, "/assets/subghz/keeloq_mfcodes"); + subghz_protocol_enable_dump(protocol, NULL, NULL); + + frequency = api_hal_subghz_set_frequency_and_path(frequency); + hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullNo, GpioSpeedLow); + + StreamBufferHandle_t rx_stream = + xStreamBufferCreate(sizeof(LevelPair) * 1024, sizeof(LevelPair)); + + api_hal_subghz_set_capture_callback(subghz_cli_command_rx_callback, rx_stream); + api_hal_subghz_enable_capture(); + + api_hal_subghz_flush_rx(); + api_hal_subghz_rx(); + + printf("Listening at %lu. Press CTRL+C to stop\r\n", frequency); + LevelPair pair; + while(!cli_cmd_interrupt_received(cli)) { + int ret = xStreamBufferReceive(rx_stream, &pair, sizeof(LevelPair), 10); + if(ret == sizeof(LevelPair)) { + if(pair.level == ApiHalSubGhzCaptureLevelOverrun) { + printf("."); + subghz_protocol_reset(protocol); + } else { + subghz_protocol_parse(protocol, pair); + } + } + } + + subghz_protocol_free(protocol); + vStreamBufferDelete(rx_stream); + api_hal_subghz_disable_capture(); + api_hal_subghz_init(); +} diff --git a/applications/subghz/subghz_cli.h b/applications/subghz/subghz_cli.h index 59b41b08..e6dc16a5 100644 --- a/applications/subghz/subghz_cli.h +++ b/applications/subghz/subghz_cli.h @@ -11,3 +11,7 @@ void subghz_cli_command_rx_carrier(Cli* cli, string_t args, void* context); void subghz_cli_command_tx_pt(Cli* cli, string_t args, void* context); void subghz_cli_command_rx_pt(Cli* cli, string_t args, void* context); + +void subghz_cli_command_tx(Cli* cli, string_t args, void* context); + +void subghz_cli_command_rx(Cli* cli, string_t args, void* context); diff --git a/applications/subghz/subghz_i.h b/applications/subghz/subghz_i.h index e8039e12..ccf41b71 100644 --- a/applications/subghz/subghz_i.h +++ b/applications/subghz/subghz_i.h @@ -1,9 +1,10 @@ #pragma once #include "subghz.h" -#include "subghz_test_basic.h" -#include "subghz_test_packet.h" -#include "subghz_static.h" +#include "views/subghz_capture.h" +#include "views/subghz_test_basic.h" +#include "views/subghz_test_packet.h" +#include "views/subghz_static.h" #include #include @@ -11,12 +12,7 @@ #include #include -typedef struct { - uint32_t frequency; - uint8_t path; -} SubGhzFrequency; - -extern const SubGhzFrequency subghz_frequencies[]; +extern const uint32_t subghz_frequencies[]; extern const uint32_t subghz_frequencies_count; extern const uint32_t subghz_frequencies_433_92; @@ -27,6 +23,8 @@ struct SubGhz { Submenu* submenu; + SubghzCapture* subghz_capture; + SubghzTestBasic* subghz_test_basic; SubghzTestPacket* subghz_test_packet; @@ -36,6 +34,7 @@ struct SubGhz { typedef enum { SubGhzViewMenu, + SubGhzViewCapture, SubGhzViewTestBasic, SubGhzViewTestPacket, SubGhzViewStatic, diff --git a/applications/subghz/views/subghz_capture.c b/applications/subghz/views/subghz_capture.c new file mode 100644 index 00000000..075468a5 --- /dev/null +++ b/applications/subghz/views/subghz_capture.c @@ -0,0 +1,188 @@ +#include "subghz_capture.h" +#include "../subghz_i.h" + +#include +#include +#include +#include +#include +#include + +#include +#include + +struct SubghzCapture { + View* view; + SubGhzWorker* worker; + SubGhzProtocol* protocol; +}; + +typedef struct { + uint8_t frequency; + uint32_t real_frequency; + uint32_t counter; + string_t text; +} SubghzCaptureModel; + +static const char subghz_symbols[] = {'-', '\\', '|', '/'}; + +void subghz_capture_draw(Canvas* canvas, SubghzCaptureModel* model) { + char buffer[64]; + + canvas_set_color(canvas, ColorBlack); + canvas_set_font(canvas, FontPrimary); + + snprintf( + buffer, + sizeof(buffer), + "Capture: %03ld.%03ldMHz %c", + model->real_frequency / 1000000 % 1000, + model->real_frequency / 1000 % 1000, + subghz_symbols[model->counter % 4]); + canvas_draw_str(canvas, 2, 12, buffer); + + canvas_set_font(canvas, FontSecondary); + elements_multiline_text(canvas, 0, 24, string_get_cstr(model->text)); +} + +bool subghz_capture_input(InputEvent* event, void* context) { + furi_assert(context); + SubghzCapture* subghz_capture = context; + + if(event->key == InputKeyBack) { + return false; + } + + with_view_model( + subghz_capture->view, (SubghzCaptureModel * model) { + bool reconfigure = false; + if(event->type == InputTypeShort) { + if(event->key == InputKeyLeft) { + if(model->frequency > 0) model->frequency--; + reconfigure = true; + } else if(event->key == InputKeyRight) { + if(model->frequency < subghz_frequencies_count - 1) model->frequency++; + reconfigure = true; + } + } + + if(reconfigure) { + api_hal_subghz_idle(); + model->real_frequency = + api_hal_subghz_set_frequency_and_path(subghz_frequencies[model->frequency]); + api_hal_subghz_rx(); + } + + return reconfigure; + }); + + return true; +} + +void subghz_capture_text_callback(string_t text, void* context) { + furi_assert(context); + SubghzCapture* subghz_capture = context; + + with_view_model( + subghz_capture->view, (SubghzCaptureModel * model) { + model->counter++; + string_set(model->text, text); + return true; + }); +} + +void subghz_capture_enter(void* context) { + furi_assert(context); + SubghzCapture* subghz_capture = context; + + api_hal_subghz_reset(); + api_hal_subghz_idle(); + api_hal_subghz_load_preset(ApiHalSubGhzPresetMP); + + with_view_model( + subghz_capture->view, (SubghzCaptureModel * model) { + model->frequency = subghz_frequencies_433_92; + model->real_frequency = + api_hal_subghz_set_frequency_and_path(subghz_frequencies[model->frequency]); + return true; + }); + + hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullNo, GpioSpeedLow); + + api_hal_subghz_set_capture_callback(subghz_worker_rx_callback, subghz_capture->worker); + api_hal_subghz_enable_capture(); + + subghz_worker_start(subghz_capture->worker); + + api_hal_subghz_flush_rx(); + api_hal_subghz_rx(); +} + +void subghz_capture_exit(void* context) { + furi_assert(context); + SubghzCapture* subghz_capture = context; + + subghz_worker_stop(subghz_capture->worker); + + api_hal_subghz_disable_capture(); + api_hal_subghz_init(); +} + +uint32_t subghz_capture_back(void* context) { + return SubGhzViewMenu; +} + +SubghzCapture* subghz_capture_alloc() { + SubghzCapture* subghz_capture = furi_alloc(sizeof(SubghzCapture)); + + // View allocation and configuration + subghz_capture->view = view_alloc(); + view_allocate_model(subghz_capture->view, ViewModelTypeLocking, sizeof(SubghzCaptureModel)); + view_set_context(subghz_capture->view, subghz_capture); + view_set_draw_callback(subghz_capture->view, (ViewDrawCallback)subghz_capture_draw); + view_set_input_callback(subghz_capture->view, subghz_capture_input); + view_set_enter_callback(subghz_capture->view, subghz_capture_enter); + view_set_exit_callback(subghz_capture->view, subghz_capture_exit); + view_set_previous_callback(subghz_capture->view, subghz_capture_back); + + with_view_model( + subghz_capture->view, (SubghzCaptureModel * model) { + string_init(model->text); + return true; + }); + + subghz_capture->worker = subghz_worker_alloc(); + subghz_capture->protocol = subghz_protocol_alloc(); + + subghz_worker_set_overrun_callback( + subghz_capture->worker, (SubGhzWorkerOverrunCallback)subghz_protocol_reset); + subghz_worker_set_pair_callback( + subghz_capture->worker, (SubGhzWorkerPairCallback)subghz_protocol_parse); + subghz_worker_set_context(subghz_capture->worker, subghz_capture->protocol); + + subghz_protocol_load_keeloq_file(subghz_capture->protocol, "/assets/subghz/keeloq_mfcodes"); + subghz_protocol_enable_dump( + subghz_capture->protocol, subghz_capture_text_callback, subghz_capture); + + return subghz_capture; +} + +void subghz_capture_free(SubghzCapture* subghz_capture) { + furi_assert(subghz_capture); + + subghz_protocol_free(subghz_capture->protocol); + subghz_worker_free(subghz_capture->worker); + + with_view_model( + subghz_capture->view, (SubghzCaptureModel * model) { + string_clear(model->text); + return true; + }); + view_free(subghz_capture->view); + free(subghz_capture); +} + +View* subghz_capture_get_view(SubghzCapture* subghz_capture) { + furi_assert(subghz_capture); + return subghz_capture->view; +} diff --git a/applications/subghz/views/subghz_capture.h b/applications/subghz/views/subghz_capture.h new file mode 100644 index 00000000..84159cf5 --- /dev/null +++ b/applications/subghz/views/subghz_capture.h @@ -0,0 +1,11 @@ +#pragma once + +#include + +typedef struct SubghzCapture SubghzCapture; + +SubghzCapture* subghz_capture_alloc(); + +void subghz_capture_free(SubghzCapture* subghz_capture); + +View* subghz_capture_get_view(SubghzCapture* subghz_capture); diff --git a/applications/subghz/subghz_static.c b/applications/subghz/views/subghz_static.c similarity index 85% rename from applications/subghz/subghz_static.c rename to applications/subghz/views/subghz_static.c index 561c094d..18da91f6 100644 --- a/applications/subghz/subghz_static.c +++ b/applications/subghz/views/subghz_static.c @@ -1,5 +1,5 @@ #include "subghz_static.h" -#include "subghz_i.h" +#include "../subghz_i.h" #include #include @@ -30,7 +30,6 @@ typedef enum { typedef struct { uint8_t frequency; uint32_t real_frequency; - ApiHalSubGhzPath path; uint8_t button; } SubghzStaticModel; @@ -51,21 +50,8 @@ void subghz_static_draw(Canvas* canvas, SubghzStaticModel* model) { model->real_frequency / 1000 % 1000, model->real_frequency % 1000); canvas_draw_str(canvas, 2, 24, buffer); - // Path - char* path_name = "Unknown"; - if(model->path == ApiHalSubGhzPathIsolate) { - path_name = "isolate"; - } else if(model->path == ApiHalSubGhzPath433) { - path_name = "433MHz"; - } else if(model->path == ApiHalSubGhzPath315) { - path_name = "315MHz"; - } else if(model->path == ApiHalSubGhzPath868) { - path_name = "868MHz"; - } - snprintf(buffer, sizeof(buffer), "Path: %d - %s", model->path, path_name); - canvas_draw_str(canvas, 2, 36, buffer); snprintf(buffer, sizeof(buffer), "Key: %d", model->button); - canvas_draw_str(canvas, 2, 48, buffer); + canvas_draw_str(canvas, 2, 36, buffer); } bool subghz_static_input(InputEvent* event, void* context) { @@ -91,14 +77,12 @@ bool subghz_static_input(InputEvent* event, void* context) { } else if(event->key == InputKeyUp) { if(model->button < 3) model->button++; } - model->path = subghz_frequencies[model->frequency].path; } if(reconfigure) { api_hal_subghz_idle(); model->real_frequency = - api_hal_subghz_set_frequency(subghz_frequencies[model->frequency].frequency); - api_hal_subghz_set_path(model->path); + api_hal_subghz_set_frequency_and_path(subghz_frequencies[model->frequency]); api_hal_subghz_tx(); } @@ -154,10 +138,8 @@ void subghz_static_enter(void* context) { subghz_static->view, (SubghzStaticModel * model) { model->frequency = subghz_frequencies_433_92; model->real_frequency = - api_hal_subghz_set_frequency(subghz_frequencies[model->frequency].frequency); - model->path = subghz_frequencies[model->frequency].path; + api_hal_subghz_set_frequency_and_path(subghz_frequencies[model->frequency]); model->button = 0; - api_hal_subghz_set_path(model->path); return true; }); diff --git a/applications/subghz/subghz_static.h b/applications/subghz/views/subghz_static.h similarity index 100% rename from applications/subghz/subghz_static.h rename to applications/subghz/views/subghz_static.h diff --git a/applications/subghz/subghz_test_basic.c b/applications/subghz/views/subghz_test_basic.c similarity index 98% rename from applications/subghz/subghz_test_basic.c rename to applications/subghz/views/subghz_test_basic.c index f2eafba2..26c74e3c 100644 --- a/applications/subghz/subghz_test_basic.c +++ b/applications/subghz/views/subghz_test_basic.c @@ -1,5 +1,5 @@ #include "subghz_test_basic.h" -#include "subghz_i.h" +#include "../subghz_i.h" #include #include @@ -98,7 +98,7 @@ bool subghz_test_basic_input(InputEvent* event, void* context) { } model->real_frequency = - api_hal_subghz_set_frequency(subghz_frequencies[model->frequency].frequency); + api_hal_subghz_set_frequency(subghz_frequencies[model->frequency]); api_hal_subghz_set_path(model->path); } @@ -131,7 +131,7 @@ void subghz_test_basic_enter(void* context) { subghz_test_basic->view, (SubghzTestBasicModel * model) { model->frequency = subghz_frequencies_433_92; // 433 model->real_frequency = - api_hal_subghz_set_frequency(subghz_frequencies[model->frequency].frequency); + api_hal_subghz_set_frequency(subghz_frequencies[model->frequency]); model->path = ApiHalSubGhzPathIsolate; // isolate model->rssi = 0.0f; model->status = SubghzTestBasicModelStatusRx; diff --git a/applications/subghz/subghz_test_basic.h b/applications/subghz/views/subghz_test_basic.h similarity index 100% rename from applications/subghz/subghz_test_basic.h rename to applications/subghz/views/subghz_test_basic.h diff --git a/applications/subghz/subghz_test_packet.c b/applications/subghz/views/subghz_test_packet.c similarity index 98% rename from applications/subghz/subghz_test_packet.c rename to applications/subghz/views/subghz_test_packet.c index eea260ec..28c35c72 100644 --- a/applications/subghz/subghz_test_packet.c +++ b/applications/subghz/views/subghz_test_packet.c @@ -1,5 +1,5 @@ #include "subghz_test_packet.h" -#include "subghz_i.h" +#include "../subghz_i.h" #include #include @@ -106,7 +106,7 @@ bool subghz_test_packet_input(InputEvent* event, void* context) { } model->real_frequency = - api_hal_subghz_set_frequency(subghz_frequencies[model->frequency].frequency); + api_hal_subghz_set_frequency(subghz_frequencies[model->frequency]); api_hal_subghz_set_path(model->path); } @@ -138,7 +138,7 @@ void subghz_test_packet_enter(void* context) { subghz_test_packet->view, (SubghzTestPacketModel * model) { model->frequency = subghz_frequencies_433_92; model->real_frequency = - api_hal_subghz_set_frequency(subghz_frequencies[model->frequency].frequency); + api_hal_subghz_set_frequency(subghz_frequencies[model->frequency]); model->path = ApiHalSubGhzPathIsolate; // isolate model->rssi = 0.0f; model->status = SubghzTestPacketModelStatusRx; diff --git a/applications/subghz/subghz_test_packet.h b/applications/subghz/views/subghz_test_packet.h similarity index 100% rename from applications/subghz/subghz_test_packet.h rename to applications/subghz/views/subghz_test_packet.h diff --git a/core/furi/thread.c b/core/furi/thread.c index 5dfd6ad2..344c9511 100644 --- a/core/furi/thread.c +++ b/core/furi/thread.c @@ -136,7 +136,10 @@ osStatus_t furi_thread_terminate(FuriThread* thread) { osStatus_t furi_thread_join(FuriThread* thread) { furi_assert(thread); - return osThreadJoin(thread->id); + while(thread->state != FuriThreadStateStopped) { + osDelay(10); + } + return osOK; } osThreadId_t furi_thread_get_thread_id(FuriThread* thread) { diff --git a/firmware/targets/api-hal-include/api-hal-subghz.h b/firmware/targets/api-hal-include/api-hal-subghz.h index 6b5af436..1c26c2d4 100644 --- a/firmware/targets/api-hal-include/api-hal-subghz.h +++ b/firmware/targets/api-hal-include/api-hal-subghz.h @@ -11,6 +11,7 @@ extern "C" { typedef enum { ApiHalSubGhzPresetOokAsync, /** OOK, asynchronous */ ApiHalSubGhzPreset2FskPacket, /** 2FSK, 115kBaud, variable packet length */ + ApiHalSubGhzPresetMP, /** MP OOK, asynchronous */ } ApiHalSubGhzPreset; /** Switchable Radio Paths */ @@ -88,6 +89,13 @@ void api_hal_subghz_tx(); /** Get RSSI value in dBm */ float api_hal_subghz_get_rssi(); +/** Set frequency and path + * This function automatically selects antenna matching network + * @param frequency in herz + * @return real frequency in herz + */ +uint32_t api_hal_subghz_set_frequency_and_path(uint32_t value); + /** Set frequency * @param frequency in herz * @return real frequency in herz @@ -99,6 +107,36 @@ uint32_t api_hal_subghz_set_frequency(uint32_t value); */ void api_hal_subghz_set_path(ApiHalSubGhzPath path); +/** Front Definition for capture callback */ +typedef enum { + ApiHalSubGhzCaptureLevelHigh, + ApiHalSubGhzCaptureLevelLow, + ApiHalSubGhzCaptureLevelOverrun, + ApiHalSubGhzCaptureLevelUnderrun, +} ApiHalSubGhzCaptureLevel; + +typedef struct { + ApiHalSubGhzCaptureLevel level; + uint32_t duration; +} LevelPair; + +/** Signal Timings Capture callback */ +typedef void (*ApiHalSubGhzCaptureCallback)(ApiHalSubGhzCaptureLevel level, uint32_t time, void* context); + +/** Set signal timings capture callback + * @param callback - your callback for front capture + */ +void api_hal_subghz_set_capture_callback(ApiHalSubGhzCaptureCallback callback, void* context); + +/** Enable signal timings capture + * Initializes GPIO and TIM2 for timings capture + */ +void api_hal_subghz_enable_capture(); + +/** Disable signal timings capture + * Resets GPIO and TIM2 + */ +void api_hal_subghz_disable_capture(); #ifdef __cplusplus } diff --git a/firmware/targets/api-hal-include/api-hal.h b/firmware/targets/api-hal-include/api-hal.h index 7bb2ed82..6f79a67a 100644 --- a/firmware/targets/api-hal-include/api-hal.h +++ b/firmware/targets/api-hal-include/api-hal.h @@ -13,7 +13,6 @@ template struct STOP_EXTERNING_ME {}; #include "api-hal-delay.h" #include "api-hal-pwm.h" #include "api-hal-task.h" -#include "api-hal-tim.h" #include "api-hal-power.h" #include "api-hal-vcp.h" #include "api-hal-version.h" diff --git a/firmware/targets/f5/api-hal/api-hal-subghz.c b/firmware/targets/f5/api-hal/api-hal-subghz.c index 7bbc5917..73091dc8 100644 --- a/firmware/targets/f5/api-hal/api-hal-subghz.c +++ b/firmware/targets/f5/api-hal/api-hal-subghz.c @@ -10,12 +10,13 @@ static const uint8_t api_hal_subghz_preset_ook_async_regs[][2] = { /* Base setting */ { CC1101_IOCFG0, 0x0D }, // GD0 as async serial data output/input - { CC1101_FSCTRL1, 0x06 }, // Set IF 26m/2^10*2=2.2MHz { CC1101_MCSM0, 0x18 }, // Autocalibrate on idle to TRX, ~150us OSC guard time - /* Async OOK Specific things */ + + /* Async OOK Specific things */ { CC1101_MDMCFG2, 0x30 }, // ASK/OOK, No preamble/sync { CC1101_PKTCTRL0, 0x32 }, // Async, no CRC, Infinite { CC1101_FREND0, 0x01 }, // OOK/ASK PATABLE + /* End */ { 0, 0 }, }; @@ -24,15 +25,54 @@ static const uint8_t api_hal_subghz_preset_ook_async_patable[8] = { 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; +static const uint8_t api_hal_subghz_preset_mp_regs[][2] = { + { CC1101_IOCFG0, 0x0D }, + { CC1101_FIFOTHR, 0x07 }, + { CC1101_PKTCTRL0, 0x32 }, + //{ CC1101_FSCTRL1, 0x0E }, + { CC1101_FSCTRL1, 0x06 }, + { CC1101_FREQ2, 0x10 }, + { CC1101_FREQ1, 0xB0 }, + { CC1101_FREQ0, 0x7F }, + { CC1101_MDMCFG4, 0x17 }, + { CC1101_MDMCFG3, 0x32 }, + { CC1101_MDMCFG2, 0x30 }, //<---OOK/ASK + { CC1101_MDMCFG1, 0x23 }, + { CC1101_MDMCFG0, 0xF8 }, + { CC1101_MCSM0, 0x18 }, + { CC1101_FOCCFG, 0x18 }, + { CC1101_AGCTRL2, 0x07 }, + { CC1101_AGCTRL1, 0x00 }, + { CC1101_AGCTRL0, 0x91 }, + { CC1101_WORCTRL, 0xFB }, + { CC1101_FREND1, 0xB6 }, + //{ CC1101_FREND0, 0x11 }, + { CC1101_FREND0, 0x01 }, + { CC1101_FSCAL3, 0xE9 }, + { CC1101_FSCAL2, 0x2A }, + { CC1101_FSCAL1, 0x00 }, + { CC1101_FSCAL0, 0x1F }, + { CC1101_TEST2, 0x88 }, + { CC1101_TEST1, 0x31 }, + { CC1101_TEST0, 0x09 }, + + /* End */ + { 0, 0 }, +}; + +static const uint8_t api_hal_subghz_preset_mp_patable[8] = { + 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + static const uint8_t api_hal_subghz_preset_2fsk_packet_regs[][2] = { /* Base setting */ { CC1101_IOCFG0, 0x06 }, // GD0 as async serial data output/input - { CC1101_FSCTRL1, 0x06 }, // Set IF 26m/2^10*2=2.2MHz { CC1101_MCSM0, 0x18 }, // Autocalibrate on idle to TRX, ~150us OSC guard time - { CC1101_TEST2, 0x81}, - { CC1101_TEST1, 0x35}, - { CC1101_TEST0, 0x09}, + /* Magic */ + { CC1101_TEST2, 0x81}, + { CC1101_TEST1, 0x35}, + { CC1101_TEST0, 0x09}, /* End */ { 0, 0 }, @@ -82,6 +122,9 @@ void api_hal_subghz_load_preset(ApiHalSubGhzPreset preset) { } else if(preset == ApiHalSubGhzPreset2FskPacket) { api_hal_subghz_load_registers(api_hal_subghz_preset_2fsk_packet_regs); api_hal_subghz_load_patable(api_hal_subghz_preset_2fsk_packet_patable); + } else if(preset == ApiHalSubGhzPresetMP) { + api_hal_subghz_load_registers(api_hal_subghz_preset_mp_regs); + api_hal_subghz_load_patable(api_hal_subghz_preset_mp_patable); } } @@ -175,6 +218,20 @@ float api_hal_subghz_get_rssi() { return rssi; } +uint32_t api_hal_subghz_set_frequency_and_path(uint32_t value) { + value = api_hal_subghz_set_frequency(value); + if(value >= 300000000 && value <= 348000335) { + api_hal_subghz_set_path(ApiHalSubGhzPath315); + } else if(value >= 387000000 && value <= 464000000) { + api_hal_subghz_set_path(ApiHalSubGhzPath433); + } else if(value >= 779000000 && value <= 928000000) { + api_hal_subghz_set_path(ApiHalSubGhzPath868); + } else { + furi_check(0); + } + return value; +} + uint32_t api_hal_subghz_set_frequency(uint32_t value) { const ApiHalSpiDevice* device = api_hal_spi_device_get(ApiHalSpiDeviceIdSubGhz); @@ -208,3 +265,9 @@ void api_hal_subghz_set_path(ApiHalSubGhzPath path) { furi_check(0); } } + +void api_hal_subghz_set_capture_callback(ApiHalSubGhzCaptureCallback callback, void* context) {} + +void api_hal_subghz_enable_capture() {} + +void api_hal_subghz_disable_capture() {} diff --git a/firmware/targets/f6/api-hal/api-hal-interrupt.c b/firmware/targets/f6/api-hal/api-hal-interrupt.c new file mode 100644 index 00000000..00f45228 --- /dev/null +++ b/firmware/targets/f6/api-hal/api-hal-interrupt.c @@ -0,0 +1,42 @@ +#include "api-hal-interrupt.h" + +#include +#include +#include + +volatile ApiHalInterruptISR api_hal_tim_tim2_isr = NULL; + +void TIM2_IRQHandler(void) { + if (api_hal_tim_tim2_isr) { + api_hal_tim_tim2_isr(); + } else { + HAL_TIM_IRQHandler(&htim2); + } +} + +void api_hal_interrupt_set_timer_isr(TIM_TypeDef *timer, ApiHalInterruptISR isr) { + if (timer == TIM2) { + if (isr) { + furi_assert(api_hal_tim_tim2_isr == NULL); + } else { + furi_assert(api_hal_tim_tim2_isr != NULL); + } + api_hal_tim_tim2_isr = isr; + } else { + furi_check(0); + } +} + +extern void api_interrupt_call(InterruptType type, void* hw); + +/* ST HAL symbols */ + +/* Comparator trigger event */ +void HAL_COMP_TriggerCallback(COMP_HandleTypeDef* hcomp) { + api_interrupt_call(InterruptTypeComparatorTrigger, hcomp); +} + +/* Timer update event */ +void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef* htim) { + api_interrupt_call(InterruptTypeTimerUpdate, htim); +} diff --git a/firmware/targets/f6/api-hal/api-hal-interrupt.h b/firmware/targets/f6/api-hal/api-hal-interrupt.h new file mode 100644 index 00000000..5efb2ba3 --- /dev/null +++ b/firmware/targets/f6/api-hal/api-hal-interrupt.h @@ -0,0 +1,23 @@ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** Timer ISR */ +typedef void (*ApiHalInterruptISR)(); + +/** Set Timer ISR + * By default ISR is serviced by ST HAL. Use this function to override it. + * We don't clear interrupt flags for you, do it by your self. + * @timer - timer instance + * @isr - your interrupt service routine or use NULL to clear + */ +void api_hal_interrupt_set_timer_isr(TIM_TypeDef *timer, ApiHalInterruptISR isr); + + +#ifdef __cplusplus +} +#endif diff --git a/firmware/targets/f6/api-hal/api-hal-irda.c b/firmware/targets/f6/api-hal/api-hal-irda.c index 434c7037..37c23432 100644 --- a/firmware/targets/f6/api-hal/api-hal-irda.c +++ b/firmware/targets/f6/api-hal/api-hal-irda.c @@ -1,21 +1,25 @@ -#include "cmsis_os.h" -#include "api-hal-tim_i.h" +#include "api-hal-interrupt.h" #include "api-hal-irda.h" + #include #include + #include #include #include "main.h" #include "api-hal-pwm.h" - static struct{ TimerISRCallback callback; void *ctx; } timer_irda; +typedef enum{ + TimerIRQSourceCCI1, + TimerIRQSourceCCI2, +} TimerIRQSource; -void api_hal_irda_tim_isr(TimerIRQSource source) +static void api_hal_irda_handle_capture(TimerIRQSource source) { uint32_t duration = 0; bool level = 0; @@ -39,6 +43,33 @@ void api_hal_irda_tim_isr(TimerIRQSource source) timer_irda.callback(timer_irda.ctx, level, duration); } +static void api_hal_irda_isr() { + if(LL_TIM_IsActiveFlag_CC1(TIM2) == 1) { + LL_TIM_ClearFlag_CC1(TIM2); + + if(READ_BIT(TIM2->CCMR1, TIM_CCMR1_CC1S)) { + // input capture + api_hal_irda_handle_capture(TimerIRQSourceCCI1); + } else { + // output compare + // HAL_TIM_OC_DelayElapsedCallback(htim); + // HAL_TIM_PWM_PulseFinishedCallback(htim); + } + } + if(LL_TIM_IsActiveFlag_CC2(TIM2) == 1) { + LL_TIM_ClearFlag_CC2(TIM2); + + if(READ_BIT(TIM2->CCMR1, TIM_CCMR1_CC2S)) { + // input capture + api_hal_irda_handle_capture(TimerIRQSourceCCI2); + } else { + // output compare + // HAL_TIM_OC_DelayElapsedCallback(htim); + // HAL_TIM_PWM_PulseFinishedCallback(htim); + } + } +} + void api_hal_irda_rx_irq_init(void) { LL_TIM_InitTypeDef TIM_InitStruct = {0}; @@ -86,15 +117,14 @@ void api_hal_irda_rx_irq_init(void) LL_TIM_SetCounter(TIM2, 0); LL_TIM_EnableCounter(TIM2); + api_hal_interrupt_set_timer_isr(TIM2, api_hal_irda_isr); NVIC_SetPriority(TIM2_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(),5, 0)); NVIC_EnableIRQ(TIM2_IRQn); } void api_hal_irda_rx_irq_deinit(void) { - LL_TIM_DisableIT_CC1(TIM2); - LL_TIM_DisableIT_CC2(TIM2); - LL_TIM_CC_DisableChannel(TIM2, LL_TIM_CHANNEL_CH1); - LL_TIM_CC_DisableChannel(TIM2, LL_TIM_CHANNEL_CH2); + LL_TIM_DeInit(TIM2); + api_hal_interrupt_set_timer_isr(TIM2, NULL); } bool api_hal_irda_rx_irq_is_busy(void) { @@ -115,4 +145,3 @@ void api_hal_irda_pwm_set(float value, float freq) { void api_hal_irda_pwm_stop() { hal_pwmn_stop(&IRDA_TX_TIM, IRDA_TX_CH); } - diff --git a/firmware/targets/f6/api-hal/api-hal-irda_i.h b/firmware/targets/f6/api-hal/api-hal-irda_i.h deleted file mode 100644 index 5863a00f..00000000 --- a/firmware/targets/f6/api-hal/api-hal-irda_i.h +++ /dev/null @@ -1,10 +0,0 @@ -#pragma once -#include "api-hal-tim_i.h" - -/** - * Function to handle IRDA timer ISR. - * - * @param source - reason for interrupt request. - */ -void api_hal_irda_tim_isr(TimerIRQSource source); - diff --git a/firmware/targets/f6/api-hal/api-hal-subghz.c b/firmware/targets/f6/api-hal/api-hal-subghz.c index 25be8be2..c9ba3353 100644 --- a/firmware/targets/f6/api-hal/api-hal-subghz.c +++ b/firmware/targets/f6/api-hal/api-hal-subghz.c @@ -2,7 +2,9 @@ #include #include +#include #include + #include #include #include @@ -10,12 +12,13 @@ static const uint8_t api_hal_subghz_preset_ook_async_regs[][2] = { /* Base setting */ { CC1101_IOCFG0, 0x0D }, // GD0 as async serial data output/input - { CC1101_FSCTRL1, 0x06 }, // Set IF 26m/2^10*2=2.2MHz { CC1101_MCSM0, 0x18 }, // Autocalibrate on idle to TRX, ~150us OSC guard time - /* Async OOK Specific things */ + + /* Async OOK Specific things */ { CC1101_MDMCFG2, 0x30 }, // ASK/OOK, No preamble/sync { CC1101_PKTCTRL0, 0x32 }, // Async, no CRC, Infinite { CC1101_FREND0, 0x01 }, // OOK/ASK PATABLE + /* End */ { 0, 0 }, }; @@ -24,15 +27,54 @@ static const uint8_t api_hal_subghz_preset_ook_async_patable[8] = { 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; +static const uint8_t api_hal_subghz_preset_mp_regs[][2] = { + { CC1101_IOCFG0, 0x0D }, + { CC1101_FIFOTHR, 0x07 }, + { CC1101_PKTCTRL0, 0x32 }, + //{ CC1101_FSCTRL1, 0x0E }, + { CC1101_FSCTRL1, 0x06 }, + { CC1101_FREQ2, 0x10 }, + { CC1101_FREQ1, 0xB0 }, + { CC1101_FREQ0, 0x7F }, + { CC1101_MDMCFG4, 0x17 }, + { CC1101_MDMCFG3, 0x32 }, + { CC1101_MDMCFG2, 0x30 }, //<---OOK/ASK + { CC1101_MDMCFG1, 0x23 }, + { CC1101_MDMCFG0, 0xF8 }, + { CC1101_MCSM0, 0x18 }, + { CC1101_FOCCFG, 0x18 }, + { CC1101_AGCTRL2, 0x07 }, + { CC1101_AGCTRL1, 0x00 }, + { CC1101_AGCTRL0, 0x91 }, + { CC1101_WORCTRL, 0xFB }, + { CC1101_FREND1, 0xB6 }, + //{ CC1101_FREND0, 0x11 }, + { CC1101_FREND0, 0x01 }, + { CC1101_FSCAL3, 0xE9 }, + { CC1101_FSCAL2, 0x2A }, + { CC1101_FSCAL1, 0x00 }, + { CC1101_FSCAL0, 0x1F }, + { CC1101_TEST2, 0x88 }, + { CC1101_TEST1, 0x31 }, + { CC1101_TEST0, 0x09 }, + + /* End */ + { 0, 0 }, +}; + +static const uint8_t api_hal_subghz_preset_mp_patable[8] = { + 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + static const uint8_t api_hal_subghz_preset_2fsk_packet_regs[][2] = { /* Base setting */ { CC1101_IOCFG0, 0x06 }, // GD0 as async serial data output/input - { CC1101_FSCTRL1, 0x06 }, // Set IF 26m/2^10*2=2.2MHz { CC1101_MCSM0, 0x18 }, // Autocalibrate on idle to TRX, ~150us OSC guard time - { CC1101_TEST2, 0x81}, - { CC1101_TEST1, 0x35}, - { CC1101_TEST0, 0x09}, + /* Magic */ + { CC1101_TEST2, 0x81}, + { CC1101_TEST1, 0x35}, + { CC1101_TEST0, 0x09}, /* End */ { 0, 0 }, @@ -46,20 +88,26 @@ void api_hal_subghz_init() { const ApiHalSpiDevice* device = api_hal_spi_device_get(ApiHalSpiDeviceIdSubGhz); // Reset and shutdown cc1101_reset(device); + // Prepare GD0 for power on self test hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullNo, GpioSpeedLow); + // GD0 low cc1101_write_reg(device, CC1101_IOCFG0, CC1101IocfgHW); while(hal_gpio_read(&gpio_cc1101_g0) != false); + // GD0 high cc1101_write_reg(device, CC1101_IOCFG0, CC1101IocfgHW | CC1101_IOCFG_INV); while(hal_gpio_read(&gpio_cc1101_g0) != true); + // Reset GD0 to floating state cc1101_write_reg(device, CC1101_IOCFG0, CC1101IocfgHighImpedance); hal_gpio_init(&gpio_cc1101_g0, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + // RF switches hal_gpio_init(&gpio_rf_sw_0, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); cc1101_write_reg(device, CC1101_IOCFG2, CC1101IocfgHW); + // Turn off oscillator cc1101_shutdown(device); api_hal_spi_device_return(device); @@ -82,6 +130,9 @@ void api_hal_subghz_load_preset(ApiHalSubGhzPreset preset) { } else if(preset == ApiHalSubGhzPreset2FskPacket) { api_hal_subghz_load_registers(api_hal_subghz_preset_2fsk_packet_regs); api_hal_subghz_load_patable(api_hal_subghz_preset_2fsk_packet_patable); + } else if(preset == ApiHalSubGhzPresetMP) { + api_hal_subghz_load_registers(api_hal_subghz_preset_mp_regs); + api_hal_subghz_load_patable(api_hal_subghz_preset_mp_patable); } } @@ -176,6 +227,20 @@ float api_hal_subghz_get_rssi() { return rssi; } +uint32_t api_hal_subghz_set_frequency_and_path(uint32_t value) { + value = api_hal_subghz_set_frequency(value); + if(value >= 300000000 && value <= 348000335) { + api_hal_subghz_set_path(ApiHalSubGhzPath315); + } else if(value >= 387000000 && value <= 464000000) { + api_hal_subghz_set_path(ApiHalSubGhzPath433); + } else if(value >= 779000000 && value <= 928000000) { + api_hal_subghz_set_path(ApiHalSubGhzPath868); + } else { + furi_check(0); + } + return value; +} + uint32_t api_hal_subghz_set_frequency(uint32_t value) { const ApiHalSpiDevice* device = api_hal_spi_device_get(ApiHalSpiDeviceIdSubGhz); @@ -211,3 +276,94 @@ void api_hal_subghz_set_path(ApiHalSubGhzPath path) { } api_hal_spi_device_return(device); } + +volatile uint32_t api_hal_subghz_capture_delta_duration = 0; +volatile ApiHalSubGhzCaptureCallback api_hal_subghz_capture_callback = NULL; +volatile void* api_hal_subghz_capture_callback_context = NULL; + +void api_hal_subghz_set_capture_callback(ApiHalSubGhzCaptureCallback callback, void* context) { + api_hal_subghz_capture_callback = callback; + api_hal_subghz_capture_callback_context = context; +} + +static void api_hal_subghz_capture_ISR() { + // Channel 1 + if(LL_TIM_IsActiveFlag_CC1(TIM2)) { + LL_TIM_ClearFlag_CC1(TIM2); + api_hal_subghz_capture_delta_duration = LL_TIM_IC_GetCaptureCH1(TIM2); + if (api_hal_subghz_capture_callback) { + api_hal_subghz_capture_callback( + ApiHalSubGhzCaptureLevelHigh, + api_hal_subghz_capture_delta_duration, + (void*)api_hal_subghz_capture_callback_context + ); + } + } + // Channel 2 + if(LL_TIM_IsActiveFlag_CC2(TIM2)) { + LL_TIM_ClearFlag_CC2(TIM2); + if (api_hal_subghz_capture_callback) { + api_hal_subghz_capture_callback( + ApiHalSubGhzCaptureLevelLow, + LL_TIM_IC_GetCaptureCH2(TIM2) - api_hal_subghz_capture_delta_duration, + (void*)api_hal_subghz_capture_callback_context + ); + } + } +} + +void api_hal_subghz_enable_capture() { + /* Peripheral clock enable */ + LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_TIM2); + LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOA); + + hal_gpio_init_ex(&gpio_cc1101_g0, GpioModeAltFunctionPushPull, GpioPullNo, GpioSpeedLow, GpioAltFn1TIM2); + + // Timer: base + LL_TIM_InitTypeDef TIM_InitStruct = {0}; + TIM_InitStruct.Prescaler = 64-1; + TIM_InitStruct.CounterMode = LL_TIM_COUNTERMODE_UP; + TIM_InitStruct.Autoreload = 0xFFFFFFFF; + TIM_InitStruct.ClockDivision = LL_TIM_CLOCKDIVISION_DIV1; + LL_TIM_Init(TIM2, &TIM_InitStruct); + + // Timer: advanced and channel + LL_TIM_SetClockSource(TIM2, LL_TIM_CLOCKSOURCE_INTERNAL); + LL_TIM_DisableARRPreload(TIM2); + LL_TIM_SetTriggerInput(TIM2, LL_TIM_TS_TI2FP2); + LL_TIM_SetSlaveMode(TIM2, LL_TIM_SLAVEMODE_RESET); + LL_TIM_CC_DisableChannel(TIM2, LL_TIM_CHANNEL_CH2); + LL_TIM_IC_SetFilter(TIM2, LL_TIM_CHANNEL_CH2, LL_TIM_IC_FILTER_FDIV1); + LL_TIM_IC_SetPolarity(TIM2, LL_TIM_CHANNEL_CH2, LL_TIM_IC_POLARITY_RISING); + LL_TIM_DisableIT_TRIG(TIM2); + LL_TIM_DisableDMAReq_TRIG(TIM2); + LL_TIM_SetTriggerOutput(TIM2, LL_TIM_TRGO_RESET); + LL_TIM_EnableMasterSlaveMode(TIM2); + LL_TIM_IC_SetActiveInput(TIM2, LL_TIM_CHANNEL_CH1, LL_TIM_ACTIVEINPUT_INDIRECTTI); + LL_TIM_IC_SetPrescaler(TIM2, LL_TIM_CHANNEL_CH1, LL_TIM_ICPSC_DIV1); + LL_TIM_IC_SetFilter(TIM2, LL_TIM_CHANNEL_CH1, LL_TIM_IC_FILTER_FDIV1); + LL_TIM_IC_SetPolarity(TIM2, LL_TIM_CHANNEL_CH1, LL_TIM_IC_POLARITY_FALLING); + LL_TIM_IC_SetActiveInput(TIM2, LL_TIM_CHANNEL_CH2, LL_TIM_ACTIVEINPUT_DIRECTTI); + LL_TIM_IC_SetPrescaler(TIM2, LL_TIM_CHANNEL_CH2, LL_TIM_ICPSC_DIV1); + + // ISR setup + api_hal_interrupt_set_timer_isr(TIM2, api_hal_subghz_capture_ISR); + NVIC_SetPriority(TIM2_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(),5, 0)); + NVIC_EnableIRQ(TIM2_IRQn); + + // Interrupts and channels + LL_TIM_EnableIT_CC1(TIM2); + LL_TIM_EnableIT_CC2(TIM2); + LL_TIM_CC_EnableChannel(TIM2, LL_TIM_CHANNEL_CH1); + LL_TIM_CC_EnableChannel(TIM2, LL_TIM_CHANNEL_CH2); + + // Start timer + LL_TIM_SetCounter(TIM2, 0); + LL_TIM_EnableCounter(TIM2); +} + +void api_hal_subghz_disable_capture() { + LL_TIM_DeInit(TIM2); + api_hal_interrupt_set_timer_isr(TIM2, NULL); + hal_gpio_init(&gpio_cc1101_g0, GpioModeAnalog, GpioPullNo, GpioSpeedLow); +} diff --git a/firmware/targets/f6/api-hal/api-hal-tim.c b/firmware/targets/f6/api-hal/api-hal-tim.c deleted file mode 100644 index 42f0f4eb..00000000 --- a/firmware/targets/f6/api-hal/api-hal-tim.c +++ /dev/null @@ -1,45 +0,0 @@ -#include "api-hal-tim_i.h" -#include "api-hal-irda_i.h" -#include -#include - -void TIM2_IRQHandler(void) { - bool consumed = false; - - if(LL_TIM_IsActiveFlag_CC1(TIM2) == 1) { - if(LL_TIM_IsEnabledIT_CC1(TIM2)) { - LL_TIM_ClearFlag_CC1(TIM2); - - if(READ_BIT(TIM2->CCMR1, TIM_CCMR1_CC1S)) { - // input capture - api_hal_irda_tim_isr(TimerIRQSourceCCI1); - consumed = true; - } else { - // output compare - // HAL_TIM_OC_DelayElapsedCallback(htim); - // HAL_TIM_PWM_PulseFinishedCallback(htim); - } - } - } - if(LL_TIM_IsActiveFlag_CC2(TIM2) == 1) { - if(LL_TIM_IsEnabledIT_CC2(TIM2)) { - LL_TIM_ClearFlag_CC2(TIM2); - - if(READ_BIT(TIM2->CCMR1, TIM_CCMR1_CC2S)) { - // input capture - api_hal_irda_tim_isr(TimerIRQSourceCCI2); - consumed = true; - } else { - // output compare - // HAL_TIM_OC_DelayElapsedCallback(htim); - // HAL_TIM_PWM_PulseFinishedCallback(htim); - } - } - } - - // TODO move all timers on LL hal - if(!consumed) { - // currently backed up with a crutch, we need more bicycles - HAL_TIM_IRQHandler(&htim2); - } -} diff --git a/firmware/targets/f6/api-hal/api-hal-tim_i.h b/firmware/targets/f6/api-hal/api-hal-tim_i.h deleted file mode 100644 index 9975e268..00000000 --- a/firmware/targets/f6/api-hal/api-hal-tim_i.h +++ /dev/null @@ -1,7 +0,0 @@ -#pragma once - -typedef enum{ - TimerIRQSourceCCI1, - TimerIRQSourceCCI2, -} TimerIRQSource; - diff --git a/firmware/targets/f6/api-hal/api-interrupts.c b/firmware/targets/f6/api-hal/api-interrupts.c deleted file mode 100644 index 4fd90716..00000000 --- a/firmware/targets/f6/api-hal/api-interrupts.c +++ /dev/null @@ -1,16 +0,0 @@ -#include "api-hal/api-interrupt-mgr.h" -#include - -extern void api_interrupt_call(InterruptType type, void* hw); - -/* interrupts */ - -/* Comparator trigger event */ -void HAL_COMP_TriggerCallback(COMP_HandleTypeDef* hcomp) { - api_interrupt_call(InterruptTypeComparatorTrigger, hcomp); -} - -/* Timer update event */ -void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef* htim) { - api_interrupt_call(InterruptTypeTimerUpdate, htim); -} \ No newline at end of file diff --git a/lib/drivers/cc1101.c b/lib/drivers/cc1101.c index d8c4dc40..5acde04d 100644 --- a/lib/drivers/cc1101.c +++ b/lib/drivers/cc1101.c @@ -104,37 +104,37 @@ void cc1101_flush_tx(const ApiHalSpiDevice* device) { } uint32_t cc1101_set_frequency(const ApiHalSpiDevice* device, uint32_t value) { - uint64_t real_value = (uint64_t)value * 0xFFFF / CC1101_QUARTZ; + uint64_t real_value = (uint64_t)value * CC1101_FDIV / CC1101_QUARTZ; // Sanity check - assert((real_value & 0xFFFFFF) == real_value); + assert((real_value & CC1101_FMASK) == real_value); cc1101_write_reg(device, CC1101_FREQ2, (real_value >> 16) & 0xFF); cc1101_write_reg(device, CC1101_FREQ1, (real_value >> 8 ) & 0xFF); cc1101_write_reg(device, CC1101_FREQ0, (real_value >> 0 ) & 0xFF); - uint64_t real_frequency = real_value * CC1101_QUARTZ / 0xFFFF; + uint64_t real_frequency = real_value * CC1101_QUARTZ / CC1101_FDIV; return (uint32_t)real_frequency; } uint32_t cc1101_get_frequency_step(const ApiHalSpiDevice* device) { - return CC1101_QUARTZ / 0xFFFF; + return CC1101_QUARTZ / CC1101_FDIV; } uint32_t cc1101_set_frequency_offset(const ApiHalSpiDevice* device, uint32_t value) { - uint64_t real_value = value * 0x4000 / CC1101_QUARTZ; + uint64_t real_value = value * CC1101_IFDIV / CC1101_QUARTZ; assert((real_value & 0xFF) == real_value); cc1101_write_reg(device, CC1101_FSCTRL0, (real_value >> 0 ) & 0xFF); - uint64_t real_frequency = real_value * CC1101_QUARTZ / 0x4000; + uint64_t real_frequency = real_value * CC1101_QUARTZ / CC1101_IFDIV; return (uint32_t)real_frequency; } uint32_t cc1101_get_frequency_offset_step(const ApiHalSpiDevice* device) { - return CC1101_QUARTZ / 0x4000; + return CC1101_QUARTZ / CC1101_IFDIV; } void cc1101_set_pa_table(const ApiHalSpiDevice* device, const uint8_t value[8]) { diff --git a/lib/drivers/cc1101_regs.h b/lib/drivers/cc1101_regs.h index c3c8366e..8305584d 100644 --- a/lib/drivers/cc1101_regs.h +++ b/lib/drivers/cc1101_regs.h @@ -7,10 +7,16 @@ extern "C" { #endif +/* Frequency Synthesizer constants */ #define CC1101_QUARTZ 26000000 +#define CC1101_FMASK 0xFFFFFF +#define CC1101_FDIV 0x10000 +#define CC1101_IFDIV 0x400 +/* IO Bus constants */ #define CC1101_TIMEOUT 500 +/* Bits and pieces */ #define CC1101_READ (1<<7) /** Read Bit */ #define CC1101_BURST (1<<6) /** Burst Bit */ diff --git a/lib/fl_subghz/protocols/subghz_protocol.c b/lib/fl_subghz/protocols/subghz_protocol.c new file mode 100644 index 00000000..39172307 --- /dev/null +++ b/lib/fl_subghz/protocols/subghz_protocol.c @@ -0,0 +1,130 @@ +#include "subghz_protocol.h" + +#include "subghz_protocol_came.h" +#include "subghz_protocol_cfm.h" +#include "subghz_protocol_keeloq.h" +#include "subghz_protocol_nice_flo.h" +#include "subghz_protocol_nice_flor_s.h" +#include "subghz_protocol_princeton.h" + +#include +#include +#include + +#define FILE_BUFFER_SIZE 64 + +struct SubGhzProtocol { + SubGhzProtocolCame* came; + SubGhzProtocolKeeloq* keeloq; + SubGhzProtocolNiceFlo* nice_flo; + SubGhzProtocolNiceFlorS* nice_flor_s; + SubGhzProtocolPrinceton* princeton; + + SubGhzProtocolTextCallback text_callback; + void* text_callback_context; +}; + +static void subghz_protocol_came_rx_callback(SubGhzProtocolCommon* parser, void* context) { + SubGhzProtocol* instance = context; + + string_t output; + string_init(output); + subghz_protocol_common_to_str((SubGhzProtocolCommon*)parser, output); + if (instance->text_callback) { + instance->text_callback(output, instance->text_callback_context); + } else { + printf(string_get_cstr(output)); + } + string_clear(output); +} + +SubGhzProtocol* subghz_protocol_alloc() { + SubGhzProtocol* instance = furi_alloc(sizeof(SubGhzProtocol)); + + instance->came = subghz_protocol_came_alloc(); + instance->keeloq = subghz_protocol_keeloq_alloc(); + instance->princeton = subghz_protocol_princeton_alloc(); + instance->nice_flo = subghz_protocol_nice_flo_alloc(); + instance->nice_flor_s = subghz_protocol_nice_flor_s_alloc(); + + return instance; +} + +void subghz_protocol_free(SubGhzProtocol* instance) { + furi_assert(instance); + + subghz_protocol_came_free(instance->came); + subghz_protocol_keeloq_free(instance->keeloq); + subghz_protocol_princeton_free(instance->princeton); + subghz_protocol_nice_flo_free(instance->nice_flo); + subghz_protocol_nice_flor_s_free(instance->nice_flor_s); + + free(instance); +} + +void subghz_protocol_enable_dump(SubGhzProtocol* instance, SubGhzProtocolTextCallback callback, void* context) { + furi_assert(instance); + + subghz_protocol_common_set_callback((SubGhzProtocolCommon*)instance->came, subghz_protocol_came_rx_callback, instance); + subghz_protocol_common_set_callback((SubGhzProtocolCommon*)instance->keeloq, subghz_protocol_came_rx_callback, instance); + subghz_protocol_common_set_callback((SubGhzProtocolCommon*)instance->princeton, subghz_protocol_came_rx_callback, instance); + subghz_protocol_common_set_callback((SubGhzProtocolCommon*)instance->nice_flo, subghz_protocol_came_rx_callback, instance); + subghz_protocol_common_set_callback((SubGhzProtocolCommon*)instance->nice_flor_s, subghz_protocol_came_rx_callback, instance); + + instance->text_callback = callback; + instance->text_callback_context = context; +} + +static void subghz_protocol_load_keeloq_file_process_line(SubGhzProtocol* instance, string_t line) { + uint64_t key = 0; + uint16_t type = 0; + char skey[17] = {0}; + char name[65] = {0}; + int ret = sscanf(string_get_cstr(line), "%16s:%hu:%64s", skey, &type, name); + key = strtoull(skey, NULL, 16); + if (ret == 3) { + subghz_protocol_keeloq_add_manafacture_key(instance->keeloq, name, key, type); + } else { + printf("Failed to load line: %s\r\n", string_get_cstr(line)); + } +} + +void subghz_protocol_load_keeloq_file(SubGhzProtocol* instance, const char* file_name) { + File manufacture_keys_file; + FS_Api* fs_api = furi_record_open("sdcard"); + fs_api->file.open(&manufacture_keys_file, file_name, FSAM_READ, FSOM_OPEN_EXISTING); + string_t line; + string_init(line); + if(manufacture_keys_file.error_id == FSE_OK) { + printf("Loading manufacture keys file %s\r\n", file_name); + char buffer[FILE_BUFFER_SIZE]; + uint16_t ret; + do { + ret = fs_api->file.read(&manufacture_keys_file, buffer, FILE_BUFFER_SIZE); + for (uint16_t i=0; i < ret; i++) { + if (buffer[i] == '\n' && string_size(line) > 0) { + subghz_protocol_load_keeloq_file_process_line(instance, line); + string_clean(line); + } else { + string_push_back(line, buffer[i]); + } + } + } while(ret > 0); + } else { + printf("Manufacture keys file is not found: %s\r\n", file_name); + } + string_clear(line); + fs_api->file.close(&manufacture_keys_file); + furi_record_close("sdcard"); +} + +void subghz_protocol_reset(SubGhzProtocol* instance) { +} + +void subghz_protocol_parse(SubGhzProtocol* instance, LevelPair data) { + subghz_protocol_came_parse(instance->came, data); + subghz_protocol_keeloq_parse(instance->keeloq, data); + subghz_protocol_princeton_parse(instance->princeton, data); + subghz_protocol_nice_flo_parse(instance->nice_flo, data); + subghz_protocol_nice_flor_s_parse(instance->nice_flor_s, data); +} diff --git a/lib/fl_subghz/protocols/subghz_protocol.h b/lib/fl_subghz/protocols/subghz_protocol.h new file mode 100644 index 00000000..c39c0cfe --- /dev/null +++ b/lib/fl_subghz/protocols/subghz_protocol.h @@ -0,0 +1,19 @@ +#pragma once + +#include "subghz_protocol_common.h" + +typedef void (*SubGhzProtocolTextCallback)(string_t text, void* context); + +typedef struct SubGhzProtocol SubGhzProtocol; + +SubGhzProtocol* subghz_protocol_alloc(); + +void subghz_protocol_free(SubGhzProtocol* instance); + +void subghz_protocol_enable_dump(SubGhzProtocol* instance, SubGhzProtocolTextCallback callback, void* context); + +void subghz_protocol_load_keeloq_file(SubGhzProtocol* instance, const char* file_name); + +void subghz_protocol_reset(SubGhzProtocol* instance); + +void subghz_protocol_parse(SubGhzProtocol* instance, LevelPair data); diff --git a/lib/fl_subghz/protocols/subghz_protocol_came.c b/lib/fl_subghz/protocols/subghz_protocol_came.c new file mode 100644 index 00000000..8a016b8a --- /dev/null +++ b/lib/fl_subghz/protocols/subghz_protocol_came.c @@ -0,0 +1,119 @@ +#include "subghz_protocol_came.h" +#include "subghz_protocol_common.h" + +/* + * Help + * https://phreakerclub.com/447 + * + */ + +struct SubGhzProtocolCame { + SubGhzProtocolCommon common; +}; + +SubGhzProtocolCame* subghz_protocol_came_alloc() { + SubGhzProtocolCame* instance = furi_alloc(sizeof(SubGhzProtocolCame)); + + instance->common.name = "Came"; + instance->common.code_min_count_bit_for_found = 12; + instance->common.te_shot = 320; + instance->common.te_long = 640; + instance->common.te_delta = 150; + + return instance; +} + +void subghz_protocol_came_free(SubGhzProtocolCame* instance) { + furi_assert(instance); + free(instance); +} + +void subghz_protocol_came_send_bit(SubGhzProtocolCame* instance, uint8_t bit) { + if (bit) { + //send bit 1 + SUBGHZ_TX_PIN_LOW(); + delay_us(instance->common.te_long); + SUBGHZ_TX_PIN_HIGTH(); + delay_us(instance->common.te_shot); + } else { + //send bit 0 + SUBGHZ_TX_PIN_LOW(); + delay_us(instance->common.te_shot); + SUBGHZ_TX_PIN_HIGTH(); + delay_us(instance->common.te_long); + } +} + +void subghz_protocol_came_send_key(SubGhzProtocolCame* instance, uint64_t key, uint8_t bit, uint8_t repeat) { + while (repeat--) { + //Send header + SUBGHZ_TX_PIN_LOW(); + delay_us(instance->common.te_shot * 34); //+2 interval v bit 1 + //Send start bit + subghz_protocol_came_send_bit(instance, 1); + //Send key data + for (uint8_t i = bit; i > 0; i--) { + subghz_protocol_came_send_bit(instance, bit_read(key, i - 1)); + } + } +} + +void subghz_protocol_came_parse(SubGhzProtocolCame* instance, LevelPair data) { + switch (instance->common.parser_step) { + case 0: + if ((data.level == ApiHalSubGhzCaptureLevelLow) + && (DURATION_DIFF(data.duration,instance->common.te_shot * 51)< instance->common.te_delta * 51)) { //Need protocol 36 te_shot + //Found header CAME + instance->common.parser_step = 1; + } else { + instance->common.parser_step = 0; + } + break; + case 1: + if (data.level == ApiHalSubGhzCaptureLevelLow) { + break; + } else if (DURATION_DIFF(data.duration,instance->common.te_shot)< instance->common.te_delta) { + //Found start bit CAME + instance->common.parser_step = 2; + instance->common.code_found = 0; + instance->common.code_count_bit = 0; + } else { + instance->common.parser_step = 0; + } + break; + case 2: + if (data.level == ApiHalSubGhzCaptureLevelLow) { //save interval + if (data.duration >= (instance->common.te_shot * 4)) { + instance->common.parser_step = 1; + if (instance->common.code_count_bit>= instance->common.code_min_count_bit_for_found) { + + //ToDo out data display + if (instance->common.callback) + instance->common.callback((SubGhzProtocolCommon*)instance, instance->common.context); + } + break; + } + instance->common.te_last = data.duration; + instance->common.parser_step = 3; + } else { + instance->common.parser_step = 0; + } + break; + case 3: + if (data.level == ApiHalSubGhzCaptureLevelHigh) { + if ((DURATION_DIFF(instance->common.te_last,instance->common.te_shot) < instance->common.te_delta) + && (DURATION_DIFF(data.duration,instance->common.te_long)< instance->common.te_delta)) { + subghz_protocol_common_add_bit(&instance->common, 0); + instance->common.parser_step = 2; + } else if ((DURATION_DIFF(instance->common.te_last,instance->common.te_long)< instance->common.te_delta) + && (DURATION_DIFF(data.duration,instance->common.te_shot)< instance->common.te_delta)) { + subghz_protocol_common_add_bit(&instance->common, 1); + instance->common.parser_step = 2; + } else + instance->common.parser_step = 0; + } else { + instance->common.parser_step = 0; + } + break; + } +} diff --git a/lib/fl_subghz/protocols/subghz_protocol_came.h b/lib/fl_subghz/protocols/subghz_protocol_came.h new file mode 100644 index 00000000..78557513 --- /dev/null +++ b/lib/fl_subghz/protocols/subghz_protocol_came.h @@ -0,0 +1,13 @@ +#pragma once + +#include "subghz_protocol_common.h" + +typedef struct SubGhzProtocolCame SubGhzProtocolCame; + +SubGhzProtocolCame* subghz_protocol_came_alloc(); + +void subghz_protocol_came_free(SubGhzProtocolCame* instance); + +void subghz_protocol_came_send_key(SubGhzProtocolCame* instance, uint64_t key, uint8_t bit, uint8_t repeat); + +void subghz_protocol_came_parse(SubGhzProtocolCame* instance, LevelPair data); diff --git a/lib/fl_subghz/protocols/subghz_protocol_cfm.c b/lib/fl_subghz/protocols/subghz_protocol_cfm.c new file mode 100644 index 00000000..705d38b3 --- /dev/null +++ b/lib/fl_subghz/protocols/subghz_protocol_cfm.c @@ -0,0 +1,4 @@ + +/* + * https://phreakerclub.com/616 + */ diff --git a/firmware/targets/f6/api-hal/api-hal-tim.h b/lib/fl_subghz/protocols/subghz_protocol_cfm.h similarity index 100% rename from firmware/targets/f6/api-hal/api-hal-tim.h rename to lib/fl_subghz/protocols/subghz_protocol_cfm.h diff --git a/lib/fl_subghz/protocols/subghz_protocol_common.c b/lib/fl_subghz/protocols/subghz_protocol_common.c new file mode 100644 index 00000000..b3fd7e29 --- /dev/null +++ b/lib/fl_subghz/protocols/subghz_protocol_common.c @@ -0,0 +1,70 @@ +#include "subghz_protocol_common.h" +#include + +void subghz_protocol_common_add_bit(SubGhzProtocolCommon *common, uint8_t bit){ + common->code_found = common->code_found <<1 | bit; + common->code_count_bit++; +} + +uint8_t subghz_protocol_common_check_interval (SubGhzProtocolCommon *common, uint32_t interval, uint16_t interval_check) { + if ((interval_check >= (interval - common->te_delta))&&(interval_check <= (interval + common->te_delta))){ + return 1; + } else { + return 0; + } +} + +uint64_t subghz_protocol_common_reverse_key(uint64_t key, uint8_t count_bit){ + uint64_t key_reverse=0; + for(uint8_t i=0; icallback = callback; + common->context = context; +} + +void subghz_protocol_common_to_str(SubGhzProtocolCommon* instance, string_t output) { + if (instance->to_string) { + instance->to_string(instance, output); + } else { + uint32_t code_found_hi = instance->code_found >> 32; + uint32_t code_found_lo = instance->code_found & 0x00000000ffffffff; + + uint64_t code_found_reverse = subghz_protocol_common_reverse_key(instance->code_found, instance->code_count_bit); + + uint32_t code_found_reverse_hi = code_found_reverse>>32; + uint32_t code_found_reverse_lo = code_found_reverse&0x00000000ffffffff; + + if (code_found_hi>0) { + string_cat_printf( + output, + "Protocol %s, %d Bit\r\n" + " KEY:0x%lX%08lX\r\n" + " YEK:0x%lX%08lX\r\n", + instance->name, + instance->code_count_bit, + code_found_hi, + code_found_lo, + code_found_reverse_hi, + code_found_reverse_lo + ); + } else { + string_cat_printf( + output, + "Protocol %s, %d Bit\r\n" + " KEY:0x%lX%lX\r\n" + " YEK:0x%lX%lX\r\n", + instance->name, + instance->code_count_bit, + code_found_hi, + code_found_lo, + code_found_reverse_hi, + code_found_reverse_lo + ); + } + } +} diff --git a/lib/fl_subghz/protocols/subghz_protocol_common.h b/lib/fl_subghz/protocols/subghz_protocol_common.h new file mode 100644 index 00000000..d8e029d6 --- /dev/null +++ b/lib/fl_subghz/protocols/subghz_protocol_common.h @@ -0,0 +1,53 @@ +#pragma once + +#include +#include +#include + +#define bit_read(value, bit) (((value) >> (bit)) & 0x01) +#define bit_set(value, bit) ((value) |= (1UL << (bit))) +#define bit_clear(value, bit) ((value) &= ~(1UL << (bit))) +#define bit_write(value, bit, bitvalue) (bitvalue ? bit_set(value, bit) : bit_clear(value, bit)) + +#define SUBGHZ_TX_PIN_HIGTH() +#define SUBGHZ_TX_PIN_LOW() +#define DURATION_DIFF(x,y) ((x < y) ? (y - x) : (x - y)) + +typedef struct SubGhzProtocolCommon SubGhzProtocolCommon; + +typedef void (*SubGhzProtocolCommonCallback)(SubGhzProtocolCommon* parser, void* context); + +typedef void (*SubGhzProtocolCommonToStr)(SubGhzProtocolCommon* instance, string_t output); + +struct SubGhzProtocolCommon { + const char* name; + uint16_t te_long; + uint16_t te_shot; + uint16_t te_delta; + uint64_t code_found; + uint64_t code_last_found; + uint8_t code_count_bit; + uint8_t code_min_count_bit_for_found; + uint8_t parser_step; + uint16_t te_last; + uint8_t header_count; + uint16_t cnt; + + /* Standard Callback for on rx complete event */ + SubGhzProtocolCommonCallback callback; + void* context; + + /* Dump To String */ + SubGhzProtocolCommonToStr to_string; +}; + +void subghz_protocol_common_add_bit(SubGhzProtocolCommon *common, uint8_t bit); + +uint8_t subghz_protocol_common_check_interval(SubGhzProtocolCommon *common, uint32_t interval, uint16_t interval_check); + +uint64_t subghz_protocol_common_reverse_key(uint64_t key, uint8_t count_bit); + +void subghz_protocol_common_set_callback(SubGhzProtocolCommon* instance, SubGhzProtocolCommonCallback callback, void* context); + +void subghz_protocol_common_to_str(SubGhzProtocolCommon* instance, string_t output); + diff --git a/lib/fl_subghz/protocols/subghz_protocol_keeloq.c b/lib/fl_subghz/protocols/subghz_protocol_keeloq.c new file mode 100644 index 00000000..4ac01e75 --- /dev/null +++ b/lib/fl_subghz/protocols/subghz_protocol_keeloq.c @@ -0,0 +1,378 @@ +#include "subghz_protocol_keeloq.h" + +#include + +#include +#include + +/* + * Keeloq + * https://ru.wikipedia.org/wiki/KeeLoq + * https://phreakerclub.com/forum/showthread.php?t=1094 + * + */ + +#define KEELOQ_NLF 0x3A5C742E +#define bit(x,n) (((x)>>(n))&1) +#define g5(x,a,b,c,d,e) (bit(x,a)+bit(x,b)*2+bit(x,c)*4+bit(x,d)*8+bit(x,e)*16) + +/* + * KeeLoq learning types + * https://phreakerclub.com/forum/showthread.php?t=67 + */ +#define KEELOQ_LEARNING_UNKNOWN 0u +#define KEELOQ_LEARNING_SIMPLE 1u +#define KEELOQ_LEARNING_NORMAL 2u +#define KEELOQ_LEARNING_SECURE 3u + +typedef struct { + string_t name; + uint64_t key; + uint16_t type; +} KeeLoqManufactureCode; + +ARRAY_DEF(KeeLoqManufactureCodeArray, KeeLoqManufactureCode, M_POD_OPLIST) +#define M_OPL_KeeLoqManufactureCodeArray_t() ARRAY_OPLIST(KeeLoqManufactureCodeArray, M_POD_OPLIST) + +struct SubGhzProtocolKeeloq { + SubGhzProtocolCommon common; + KeeLoqManufactureCodeArray_t manufacture_codes; + const char* manufacture_name; +}; + +/** Simple Learning Encrypt + * @param data - serial number (28bit) + * @param key - manufacture (64bit) + * @return ? + */ +inline uint32_t subghz_protocol_keeloq_encrypt(const uint32_t data, const uint64_t key) { + uint32_t x = data, r; + for (r = 0; r < 528; r++) + x = (x>>1)^((bit(x,0)^bit(x,16)^(uint32_t)bit(key,r&63)^bit(KEELOQ_NLF,g5(x,1,9,20,26,31)))<<31); + return x; +} + +/** Simple Learning Decrypt + * @param data - serial number (28bit) + * @param key - manufacture (64bit) + * @return ? + */ +inline uint32_t subghz_protocol_keeloq_decrypt(const uint32_t data, const uint64_t key) { + uint32_t x = data, r; + for (r = 0; r < 528; r++) + x = (x<<1)^bit(x,31)^bit(x,15)^(uint32_t)bit(key,(15-r)&63)^bit(KEELOQ_NLF,g5(x,0,8,19,25,30)); + return x; +} + +/** Normal Learning + * @param data - serial number (28bit) + * @param key - manufacture (64bit) + * @return ? + */ +inline uint64_t subghz_protocol_keeloq_normal_learning(uint32_t data, const uint64_t key){ + uint32_t k1,k2; + + data&=0x0FFFFFFF; + data|=0x20000000; + k1=subghz_protocol_keeloq_decrypt(data, key); + + data&=0x0FFFFFFF; + data|=0x60000000; + k2=subghz_protocol_keeloq_decrypt(data, key); + + return ((uint64_t)k2<<32)| k1; // key - shifrovanoya +} + +SubGhzProtocolKeeloq* subghz_protocol_keeloq_alloc() { + SubGhzProtocolKeeloq* instance = furi_alloc(sizeof(SubGhzProtocolKeeloq)); + + instance->common.name = "KeeLoq"; + instance->common.code_min_count_bit_for_found = 64; + instance->common.te_shot = 400; + instance->common.te_long = 800; + instance->common.te_delta = 140; + instance->common.to_string = (SubGhzProtocolCommonToStr)subghz_protocol_keeloq_to_str; + + KeeLoqManufactureCodeArray_init(instance->manufacture_codes); + + return instance; +} + +void subghz_protocol_keeloq_free(SubGhzProtocolKeeloq* instance) { + furi_assert(instance); + for + M_EACH(manufacture_code, instance->manufacture_codes, KeeLoqManufactureCodeArray_t) { + string_clear(manufacture_code->name); + manufacture_code->key = 0; + } + KeeLoqManufactureCodeArray_clear(instance->manufacture_codes); + free(instance); +} + +void subghz_protocol_keeloq_add_manafacture_key(SubGhzProtocolKeeloq* instance, const char* name, uint64_t key, uint16_t type) { + KeeLoqManufactureCode* manufacture_code = KeeLoqManufactureCodeArray_push_raw(instance->manufacture_codes); + string_init_set_str(manufacture_code->name, name); + manufacture_code->key = key; + manufacture_code->type = type; +} + +uint8_t subghz_protocol_keeloq_check_remote_controller_selector(SubGhzProtocolKeeloq* instance, uint32_t fix , uint32_t hop) { + uint16_t end_serial = (uint16_t)(fix&0x3FF); + uint8_t btn = (uint8_t)(fix>>28); + uint32_t decrypt = 0; + uint64_t man_normal_learning; + + for + M_EACH(manufacture_code, instance->manufacture_codes, KeeLoqManufactureCodeArray_t) { + switch (manufacture_code->type){ + case KEELOQ_LEARNING_SIMPLE: + //Simple Learning + decrypt = subghz_protocol_keeloq_decrypt(hop, manufacture_code->key); + if((decrypt>>28 == btn) && ((((uint16_t)(decrypt>>16)) & 0x3FF) == end_serial)){ + instance->manufacture_name = string_get_cstr(manufacture_code->name); + instance->common.cnt = decrypt & 0x0000FFFF; + return 1; + } + break; + case KEELOQ_LEARNING_NORMAL: + // Normal_Learning + // https://phreakerclub.com/forum/showpost.php?p=43557&postcount=37 + man_normal_learning = subghz_protocol_keeloq_normal_learning(fix, manufacture_code->key); + decrypt=subghz_protocol_keeloq_decrypt(hop, man_normal_learning); + if( (decrypt>>28 ==btn)&& ((((uint16_t)(decrypt>>16))&0x3FF)==end_serial)){ + instance->manufacture_name = string_get_cstr(manufacture_code->name); + instance->common.cnt = decrypt & 0x0000FFFF; + return 1; + } + break; + case KEELOQ_LEARNING_UNKNOWN: + // Simple Learning + decrypt=subghz_protocol_keeloq_decrypt(hop, manufacture_code->key); + if( (decrypt>>28 ==btn) && ((((uint16_t)(decrypt>>16))&0x3FF)==end_serial)){ + instance->manufacture_name = string_get_cstr(manufacture_code->name); + instance->common.cnt = decrypt & 0x0000FFFF; + return 1; + } + // Check for mirrored man + uint64_t man_rev=0; + uint64_t man_rev_byte=0; + for(uint8_t i=0; i<64; i+=8){ + man_rev_byte=(uint8_t)(manufacture_code->key >> i); + man_rev = man_rev | man_rev_byte << (56-i); + } + decrypt=subghz_protocol_keeloq_decrypt(hop, man_rev); + if( (decrypt>>28 ==btn) && ((((uint16_t)(decrypt>>16))&0x3FF)==end_serial)){ + instance->manufacture_name = string_get_cstr(manufacture_code->name); + instance->common.cnt= decrypt&0x0000FFFF; + return 1; + } + //########################### + // Normal_Learning + // https://phreakerclub.com/forum/showpost.php?p=43557&postcount=37 + man_normal_learning = subghz_protocol_keeloq_normal_learning(fix, manufacture_code->key); + decrypt=subghz_protocol_keeloq_decrypt(hop, man_normal_learning); + if( (decrypt>>28 ==btn)&& ((((uint16_t)(decrypt>>16))&0x3FF)==end_serial)){ + instance->manufacture_name = string_get_cstr(manufacture_code->name); + instance->common.cnt= decrypt&0x0000FFFF; + return 1; + } + // Check for mirrored man + man_rev=0; + man_rev_byte=0; + for(uint8_t i=0; i<64; i+=8){ + man_rev_byte = (uint8_t)(manufacture_code->key >> i); + man_rev = man_rev | man_rev_byte << (56-i); + } + man_normal_learning = subghz_protocol_keeloq_normal_learning(fix, man_rev); + decrypt=subghz_protocol_keeloq_decrypt(hop, man_normal_learning); + if( (decrypt>>28 ==btn) && ((((uint16_t)(decrypt>>16))&0x3FF)==end_serial)){ + instance->manufacture_name = string_get_cstr(manufacture_code->name); + instance->common.cnt= decrypt&0x0000FFFF; + return 1; + } + break; + } + } + + instance->manufacture_name = "Unknown"; + instance->common.cnt=0; + + return 0; +} + +void subghz_protocol_keeloq_check_remote_controller(SubGhzProtocolKeeloq* instance) { + uint64_t key = subghz_protocol_common_reverse_key(instance->common.code_found, instance->common.code_count_bit); + uint32_t key_fix = key >> 32; + uint32_t key_hop = key & 0x00000000ffffffff; + // Check key AN-Motors + if((key_hop >> 24) == ((key_hop>>16)&0x00ff) && (key_fix>>28) ==((key_hop>>12)&0x0f) ){ + instance->manufacture_name = "AN-Motors"; + instance->common.cnt = key_hop>>16; + } else { + subghz_protocol_keeloq_check_remote_controller_selector(instance, key_fix, key_hop); + } + if (instance->common.callback) instance->common.callback((SubGhzProtocolCommon*)instance, instance->common.context); +} + +void subghz_protocol_keeloq_send_bit(SubGhzProtocolKeeloq* instance, uint8_t bit) { + if (bit) { + // send bit 1 + SUBGHZ_TX_PIN_HIGTH(); + delay_us(instance->common.te_shot); + SUBGHZ_TX_PIN_LOW(); + delay_us(instance->common.te_long); + } else { + // send bit 0 + SUBGHZ_TX_PIN_HIGTH(); + delay_us(instance->common.te_long); + SUBGHZ_TX_PIN_LOW(); + delay_us(instance->common.te_shot); + } +} + +void subghz_protocol_keeloq_send_key(SubGhzProtocolKeeloq* instance, uint64_t key, uint8_t bit, uint8_t repeat) { + while (repeat--) { + // Send header + for (uint8_t i = 11; i > 0; i--) { + SUBGHZ_TX_PIN_HIGTH(); + delay_us(instance->common.te_shot); + SUBGHZ_TX_PIN_LOW(); + delay_us(instance->common.te_shot); + } + delay_us(instance->common.te_shot * 9); //+1 up Send header + + for (uint8_t i = bit; i > 0; i--) { + subghz_protocol_keeloq_send_bit(instance, bit_read(key, i - 1)); + } + // +send 2 status bit + subghz_protocol_keeloq_send_bit(instance, 0); + subghz_protocol_keeloq_send_bit(instance, 0); + // send end + subghz_protocol_keeloq_send_bit(instance, 0); + delay_us(instance->common.te_shot * 2); //+2 interval END SEND + } +} + +void subghz_protocol_keeloq_parse(SubGhzProtocolKeeloq* instance, LevelPair data) { + switch (instance->common.parser_step) { + case 0: + if ((data.level == ApiHalSubGhzCaptureLevelHigh) && DURATION_DIFF(data.duration, instance->common.te_shot)< instance->common.te_delta) { + instance->common.parser_step = 1; + instance->common.header_count++; + } else { + instance->common.parser_step = 0; + } + + break; + case 1: + if ((data.level == ApiHalSubGhzCaptureLevelLow) && (DURATION_DIFF(data.duration, instance->common.te_shot ) < instance->common.te_delta)) { + instance->common.parser_step = 0; + break; + } + if ((instance->common.header_count > 2) && ( DURATION_DIFF(data.duration, instance->common.te_shot * 10)< instance->common.te_delta * 10)) { + // Found header + instance->common.parser_step = 2; + instance->common.code_found = 0; + instance->common.code_count_bit = 0; + } else { + instance->common.parser_step = 0; + instance->common.header_count = 0; + } + break; + case 2: + if (data.level == ApiHalSubGhzCaptureLevelHigh) { + instance->common.te_last = data.duration; + instance->common.parser_step = 3; + } + break; + case 3: + if (data.level == ApiHalSubGhzCaptureLevelLow) { + if (data.duration >= (instance->common.te_shot * 2 + instance->common.te_delta)) { + // Found end TX + instance->common.parser_step = 0; + if (instance->common.code_count_bit >= instance->common.code_min_count_bit_for_found) { + //&& (instance->common.code_last_found != instance->common.code_found )) { + instance->common.code_last_found = instance->common.code_found; + + //ToDo out data display + subghz_protocol_keeloq_check_remote_controller(instance); + + //Print_Code(&KEELOQ); + //Reverse_Code(KEELOQ.Code); + instance->common.code_found = 0; + instance->common.code_count_bit = 0; + instance->common.header_count = 0; + } + break; + } else if ((DURATION_DIFF(instance->common.te_last, instance->common.te_shot) < instance->common.te_delta) + && (DURATION_DIFF(data.duration, instance->common.te_long) < instance->common.te_delta)) { + if (instance->common.code_count_bit < instance->common.code_min_count_bit_for_found) { + subghz_protocol_common_add_bit(&instance->common, 1); + } + instance->common.parser_step = 2; + } else if ((DURATION_DIFF(instance->common.te_last, instance->common.te_long) < instance->common.te_delta) + && (DURATION_DIFF(data.duration, instance->common.te_shot) < instance->common.te_delta)) { + if (instance->common.code_count_bit < instance->common.code_min_count_bit_for_found) { + subghz_protocol_common_add_bit(&instance->common, 0); + } + instance->common.parser_step = 2; + } else { + instance->common.parser_step = 0; + instance->common.header_count = 0; + } + } else { + instance->common.parser_step = 0; + instance->common.header_count = 0; + } + break; + } +} + +void subghz_protocol_keeloq_to_str(SubGhzProtocolKeeloq* instance, string_t output) { + //snprintf(BufTX, sizeof(BufTX),"Protocol %s: %d Bit | KEY:0x%llX HEX \n\r", common->Name_Protocol, common->Count_BIT, common->Code); + uint32_t code_found_hi = instance->common.code_found >> 32; + uint32_t code_found_lo = instance->common.code_found & 0x00000000ffffffff; + + uint64_t code_found_reverse = subghz_protocol_common_reverse_key(instance->common.code_found, instance->common.code_count_bit); + + uint32_t code_found_reverse_hi = code_found_reverse>>32; + uint32_t code_found_reverse_lo = code_found_reverse&0x00000000ffffffff; + + if (code_found_hi>0) { + string_cat_printf( + output, + "Protocol %s, %d Bit\r\n" + " KEY:0x%lX%08lX\r\n" + " YEK:0x%lX%08lX\r\n", + instance->common.name, + instance->common.code_count_bit, + code_found_hi, + code_found_lo, + code_found_reverse_hi, + code_found_reverse_lo + ); + } else { + string_cat_printf( + output, + "Protocol %s, %d Bit\r\n" + " KEY:0x%lX%lX\r\n" + " YEK:0x%lX%lX\r\n", + instance->common.name, + instance->common.code_count_bit, + code_found_hi, + code_found_lo, + code_found_reverse_hi, + code_found_reverse_lo + ); + } + string_cat_printf( + output, + " MF:%s FIX:%lX\r\n" + " HOP:%lX CNT:%04X BTN:%02lX\r\n", + instance->manufacture_name, + code_found_reverse_hi, + code_found_reverse_lo, + instance->common.cnt, //need manufacture code + code_found_reverse_hi >> 28 + ); +} \ No newline at end of file diff --git a/lib/fl_subghz/protocols/subghz_protocol_keeloq.h b/lib/fl_subghz/protocols/subghz_protocol_keeloq.h new file mode 100644 index 00000000..363835cf --- /dev/null +++ b/lib/fl_subghz/protocols/subghz_protocol_keeloq.h @@ -0,0 +1,17 @@ +#pragma once + +#include "subghz_protocol_common.h" + +typedef struct SubGhzProtocolKeeloq SubGhzProtocolKeeloq; + +SubGhzProtocolKeeloq* subghz_protocol_keeloq_alloc(); + +void subghz_protocol_keeloq_free(SubGhzProtocolKeeloq* instance); + +void subghz_protocol_keeloq_add_manafacture_key(SubGhzProtocolKeeloq* instance, const char* name, uint64_t key, uint16_t type); + +void subghz_protocol_keeloq_send_key(SubGhzProtocolKeeloq* instance, uint64_t key, uint8_t bit, uint8_t repeat); + +void subghz_protocol_keeloq_parse(SubGhzProtocolKeeloq* instance, LevelPair data); + +void subghz_protocol_keeloq_to_str(SubGhzProtocolKeeloq* instance, string_t output); diff --git a/lib/fl_subghz/protocols/subghz_protocol_nice_flo.c b/lib/fl_subghz/protocols/subghz_protocol_nice_flo.c new file mode 100644 index 00000000..d8ba71e9 --- /dev/null +++ b/lib/fl_subghz/protocols/subghz_protocol_nice_flo.c @@ -0,0 +1,117 @@ +#include "subghz_protocol_nice_flo.h" + +/* + * Help + * https://phreakerclub.com/447 + * + */ + +struct SubGhzProtocolNiceFlo { + SubGhzProtocolCommon common; +}; + +SubGhzProtocolNiceFlo* subghz_protocol_nice_flo_alloc() { + SubGhzProtocolNiceFlo* instance = furi_alloc(sizeof(SubGhzProtocolNiceFlo)); + + instance->common.name = "Nice FLO"; + instance->common.code_min_count_bit_for_found = 12; + instance->common.te_shot = 700; + instance->common.te_long = 1400; + instance->common.te_delta = 200; + + return instance; +} + +void subghz_protocol_nice_flo_free(SubGhzProtocolNiceFlo* instance) { + furi_assert(instance); + free(instance); +} + +void subghz_protocol_nice_flo_send_bit(SubGhzProtocolNiceFlo* instance, uint8_t bit) { + if (bit) { + //send bit 1 + SUBGHZ_TX_PIN_LOW(); + delay_us(instance->common.te_long); + SUBGHZ_TX_PIN_HIGTH(); + delay_us(instance->common.te_shot); + } else { + //send bit 0 + SUBGHZ_TX_PIN_LOW(); + delay_us(instance->common.te_shot); + SUBGHZ_TX_PIN_HIGTH(); + delay_us(instance->common.te_long); + } +} + +void subghz_protocol_nice_flo_send_key(SubGhzProtocolNiceFlo* instance, uint64_t key, uint8_t bit, uint8_t repeat) { + while (repeat--) { + //Send header + SUBGHZ_TX_PIN_LOW(); + delay_us(instance->common.te_shot * 34); //+2 interval v bit 1 + //Send start bit + subghz_protocol_nice_flo_send_bit(instance, 1); + //Send key data + for (uint8_t i = bit; i > 0; i--) { + subghz_protocol_nice_flo_send_bit(instance, bit_read(key, i - 1)); + } + } +} + +void subghz_protocol_nice_flo_parse(SubGhzProtocolNiceFlo* instance, LevelPair data) { + switch (instance->common.parser_step) { + case 0: + if ((data.level == ApiHalSubGhzCaptureLevelLow) + && (DURATION_DIFF(data.duration,instance->common.te_shot * 36)< instance->common.te_delta * 36)) { + //Found header Nice Flo + instance->common.parser_step = 1; + } else { + instance->common.parser_step = 0; + } + break; + case 1: + if (data.level == ApiHalSubGhzCaptureLevelLow) { + break; + } else if (DURATION_DIFF(data.duration,instance->common.te_shot)< instance->common.te_delta) { + //Found start bit Nice Flo + instance->common.parser_step = 2; + instance->common.code_found = 0; + instance->common.code_count_bit = 0; + } else { + instance->common.parser_step = 0; + } + break; + case 2: + if (data.level == ApiHalSubGhzCaptureLevelLow) { //save interval + if (data.duration >= (instance->common.te_shot * 4)) { + instance->common.parser_step = 1; + if (instance->common.code_count_bit>= instance->common.code_min_count_bit_for_found) { + + //ToDo out data display + if (instance->common.callback) instance->common.callback((SubGhzProtocolCommon*)instance, instance->common.context); + } + break; + } + instance->common.te_last = data.duration; + instance->common.parser_step = 3; + } else { + instance->common.parser_step = 0; + } + break; + case 3: + if (data.level == ApiHalSubGhzCaptureLevelHigh) { + if ((DURATION_DIFF(instance->common.te_last,instance->common.te_shot) < instance->common.te_delta) + && (DURATION_DIFF(data.duration,instance->common.te_long)< instance->common.te_delta)) { + subghz_protocol_common_add_bit(&instance->common, 0); + instance->common.parser_step = 2; + } else if ((DURATION_DIFF(instance->common.te_last,instance->common.te_long)< instance->common.te_delta) + && (DURATION_DIFF(data.duration,instance->common.te_shot)< instance->common.te_delta)) { + subghz_protocol_common_add_bit(&instance->common, 1); + instance->common.parser_step = 2; + } else + instance->common.parser_step = 0; + } else { + instance->common.parser_step = 0; + } + break; + } +} diff --git a/lib/fl_subghz/protocols/subghz_protocol_nice_flo.h b/lib/fl_subghz/protocols/subghz_protocol_nice_flo.h new file mode 100644 index 00000000..d9938ef7 --- /dev/null +++ b/lib/fl_subghz/protocols/subghz_protocol_nice_flo.h @@ -0,0 +1,15 @@ +#pragma once + +#include "subghz_protocol_common.h" + +typedef struct SubGhzProtocolNiceFlo SubGhzProtocolNiceFlo; + +SubGhzProtocolNiceFlo* subghz_protocol_nice_flo_alloc(); + +void subghz_protocol_nice_flo_free(SubGhzProtocolNiceFlo* instance); + +void subghz_protocol_nice_flo_set_callback(SubGhzProtocolNiceFlo* instance, SubGhzProtocolCommonCallback callback, void* context); + +void subghz_protocol_nice_flo_send_key(SubGhzProtocolNiceFlo* instance, uint64_t key, uint8_t bit, uint8_t repeat); + +void subghz_protocol_nice_flo_parse(SubGhzProtocolNiceFlo* instance, LevelPair data); diff --git a/lib/fl_subghz/protocols/subghz_protocol_nice_flor_s.c b/lib/fl_subghz/protocols/subghz_protocol_nice_flor_s.c new file mode 100644 index 00000000..306aa6c5 --- /dev/null +++ b/lib/fl_subghz/protocols/subghz_protocol_nice_flor_s.c @@ -0,0 +1,133 @@ +#include "subghz_protocol_nice_flor_s.h" +/* + * https://phreakerclub.com/1615 + * https://phreakerclub.com/forum/showthread.php?t=2360 + * https://vrtp.ru/index.php?showtopic=27867 + */ + +struct SubGhzProtocolNiceFlorS { + SubGhzProtocolCommon common; +}; + +SubGhzProtocolNiceFlorS* subghz_protocol_nice_flor_s_alloc() { + SubGhzProtocolNiceFlorS* instance = furi_alloc(sizeof(SubGhzProtocolNiceFlorS)); + + instance->common.name = "Nice FloR S"; + instance->common.code_min_count_bit_for_found = 52; + instance->common.te_shot = 500; + instance->common.te_long = 1000; + instance->common.te_delta = 300; + + return instance; +} + +void subghz_protocol_nice_flor_s_free(SubGhzProtocolNiceFlorS* instance) { + furi_assert(instance); + free(instance); +} + +void subghz_protocol_nice_flor_s_send_bit(SubGhzProtocolNiceFlorS* instance, uint8_t bit) { + if (bit) { + //send bit 1 + SUBGHZ_TX_PIN_HIGTH(); + delay_us(instance->common.te_long); + SUBGHZ_TX_PIN_LOW(); + delay_us(instance->common.te_shot); + } else { + //send bit 0 + SUBGHZ_TX_PIN_HIGTH(); + delay_us(instance->common.te_shot); + SUBGHZ_TX_PIN_LOW(); + delay_us(instance->common.te_long); + } +} + +void subghz_protocol_nice_flor_s_send_key(SubGhzProtocolNiceFlorS* instance, uint64_t key, uint8_t bit, uint8_t repeat) { + while (repeat--) { + //Send header + SUBGHZ_TX_PIN_LOW(); + delay_us(instance->common.te_shot * 34); + //Send Start Bit + SUBGHZ_TX_PIN_HIGTH(); + delay_us(instance->common.te_shot*3); + SUBGHZ_TX_PIN_LOW(); + delay_us(instance->common.te_shot*3); + //Send key data + for (uint8_t i = bit; i > 0; i--) { + subghz_protocol_nice_flor_s_send_bit(instance, bit_read(key, i - 1)); + } + //Send Stop Bit + SUBGHZ_TX_PIN_HIGTH(); + delay_us(instance->common.te_shot*3); + SUBGHZ_TX_PIN_LOW(); + delay_us(instance->common.te_shot*3); + } +} + +void subghz_protocol_nice_flor_s_parse(SubGhzProtocolNiceFlorS* instance, LevelPair data) { + switch (instance->common.parser_step) { + case 0: + if ((data.level == ApiHalSubGhzCaptureLevelLow) + && (DURATION_DIFF(data.duration,instance->common.te_shot * 38)< instance->common.te_delta * 38)) { + //Found start header Nice Flor-S + instance->common.parser_step = 1; + } else { + instance->common.parser_step = 0; + } + break; + case 1: + if ((data.level == ApiHalSubGhzCaptureLevelHigh) + && (DURATION_DIFF(data.duration,instance->common.te_shot * 3)< instance->common.te_delta * 3)) { + //Found next header Nice Flor-S + instance->common.parser_step = 2; + } else { + instance->common.parser_step = 0; + } + break; + case 2: + if ((data.level == ApiHalSubGhzCaptureLevelLow) + && (DURATION_DIFF(data.duration,instance->common.te_shot * 3)< instance->common.te_delta * 3)) { + //Found header Nice Flor-S + instance->common.parser_step = 3; + instance->common.code_found = 0; + instance->common.code_count_bit = 0; + } else { + instance->common.parser_step = 0; + } + break; + case 3: + if (data.level == ApiHalSubGhzCaptureLevelHigh) { + if(DURATION_DIFF(data.duration,instance->common.te_shot*3) < instance->common.te_delta){ + //Found STOP bit + instance->common.parser_step = 0; + if (instance->common.code_count_bit>= instance->common.code_min_count_bit_for_found) { + + //ToDo out data display + if (instance->common.callback) instance->common.callback((SubGhzProtocolCommon*)instance, instance->common.context); + } + break; + } else { + //save interval + instance->common.te_last = data.duration; + instance->common.parser_step = 4; + } + } + break; + case 4: + if (data.level == ApiHalSubGhzCaptureLevelLow) { + if ((DURATION_DIFF(instance->common.te_last,instance->common.te_shot) < instance->common.te_delta) + && (DURATION_DIFF(data.duration,instance->common.te_long)< instance->common.te_delta)) { + subghz_protocol_common_add_bit(&instance->common, 0); + instance->common.parser_step = 3; + } else if ((DURATION_DIFF(instance->common.te_last,instance->common.te_long)< instance->common.te_delta) + && (DURATION_DIFF(data.duration,instance->common.te_shot)< instance->common.te_delta)) { + subghz_protocol_common_add_bit(&instance->common, 1); + instance->common.parser_step = 3; + } else + instance->common.parser_step = 0; + } else { + instance->common.parser_step = 0; + } + break; + } +} diff --git a/lib/fl_subghz/protocols/subghz_protocol_nice_flor_s.h b/lib/fl_subghz/protocols/subghz_protocol_nice_flor_s.h new file mode 100644 index 00000000..34f8fd11 --- /dev/null +++ b/lib/fl_subghz/protocols/subghz_protocol_nice_flor_s.h @@ -0,0 +1,15 @@ +#pragma once + +#include "subghz_protocol_common.h" + +typedef struct SubGhzProtocolNiceFlorS SubGhzProtocolNiceFlorS; + +SubGhzProtocolNiceFlorS* subghz_protocol_nice_flor_s_alloc(); + +void subghz_protocol_nice_flor_s_free(SubGhzProtocolNiceFlorS* instance); + +void subghz_protocol_nice_flor_s_set_callback(SubGhzProtocolNiceFlorS* instance, SubGhzProtocolCommonCallback callback, void* context); + +void subghz_protocol_nice_flor_s_send_key(SubGhzProtocolNiceFlorS* instance, uint64_t key, uint8_t bit, uint8_t repeat); + +void subghz_protocol_nice_flor_s_parse(SubGhzProtocolNiceFlorS* instance, LevelPair data); diff --git a/lib/fl_subghz/protocols/subghz_protocol_princeton.c b/lib/fl_subghz/protocols/subghz_protocol_princeton.c new file mode 100644 index 00000000..66b92ab9 --- /dev/null +++ b/lib/fl_subghz/protocols/subghz_protocol_princeton.c @@ -0,0 +1,109 @@ +#include "subghz_protocol_princeton.h" + +/* + * Help + * https://phreakerclub.com/447 + * + */ + +struct SubGhzProtocolPrinceton { + SubGhzProtocolCommon common; +}; + +SubGhzProtocolPrinceton* subghz_protocol_princeton_alloc(void) { + SubGhzProtocolPrinceton* instance = furi_alloc(sizeof(SubGhzProtocolPrinceton)); + + instance->common.name = "Princeton"; + instance->common.code_min_count_bit_for_found = 24; + instance->common.te_shot = 450;//150; + instance->common.te_long = 1350;//450; + instance->common.te_delta = 200;//50; + + return instance; +} + +void subghz_protocol_princeton_free(SubGhzProtocolPrinceton* instance) { + furi_assert(instance); + free(instance); +} + +void subghz_protocol_princeton_send_bit(SubGhzProtocolPrinceton* instance, uint8_t bit) { + if (bit) { + //send bit 1 + SUBGHZ_TX_PIN_LOW(); + delay_us(instance->common.te_long); + SUBGHZ_TX_PIN_HIGTH(); + delay_us(instance->common.te_shot); + } else { + //send bit 0 + SUBGHZ_TX_PIN_LOW(); + delay_us(instance->common.te_shot); + SUBGHZ_TX_PIN_HIGTH(); + delay_us(instance->common.te_long); + } +} + +void subghz_protocol_princeton_send_key(SubGhzProtocolPrinceton* instance, uint64_t key, uint8_t bit,uint8_t repeat) { + while (repeat--) { + SUBGHZ_TX_PIN_LOW(); + //Send start bit + subghz_protocol_princeton_send_bit(instance, 1); + //Send header + delay_us(instance->common.te_shot * 33); //+2 interval v bit 1 + //Send key data + for (uint8_t i = bit; i > 0; i--) { + subghz_protocol_princeton_send_bit(instance, bit_read(key, i - 1)); + } + } +} + +void subghz_protocol_princeton_parse(SubGhzProtocolPrinceton* instance, LevelPair data) { + switch (instance->common.parser_step) { + case 0: + if ((data.level == ApiHalSubGhzCaptureLevelLow) + && (DURATION_DIFF(data.duration,instance->common.te_shot * 36)< instance->common.te_delta * 36)) { + //Found Preambula + instance->common.parser_step = 1; + instance->common.code_found = 0; + instance->common.code_count_bit = 0; + } else { + instance->common.parser_step = 0; + } + break; + case 1: + //save duration + if (data.level == ApiHalSubGhzCaptureLevelHigh) { + instance->common.te_last = data.duration; + instance->common.parser_step = 2; + } + break; + case 2: + if (data.level == ApiHalSubGhzCaptureLevelLow) { + if (data.duration>= (instance->common.te_shot * 10+ instance->common.te_delta)) { + instance->common.parser_step = 1; + if (instance->common.code_count_bit>= instance->common.code_min_count_bit_for_found) { + //ToDo out data display + if (instance->common.callback) instance->common.callback((SubGhzProtocolCommon*)instance, instance->common.context); + } + instance->common.code_found = 0; + instance->common.code_count_bit = 0; + break; + } + + if ((DURATION_DIFF(instance->common.te_last,instance->common.te_shot)< instance->common.te_delta) + && (DURATION_DIFF(data.duration,instance->common.te_long)< instance->common.te_delta*3)) { + subghz_protocol_common_add_bit(&instance->common, 0); + instance->common.parser_step = 1; + } else if ((DURATION_DIFF(instance->common.te_last,instance->common.te_long)< instance->common.te_delta*3) + && (DURATION_DIFF(data.duration,instance->common.te_shot)< instance->common.te_delta)) { + subghz_protocol_common_add_bit(&instance->common, 1); + instance->common.parser_step = 1; + } else { + instance->common.parser_step = 0; + } + } else { + instance->common.parser_step = 0; + } + break; + } +} diff --git a/lib/fl_subghz/protocols/subghz_protocol_princeton.h b/lib/fl_subghz/protocols/subghz_protocol_princeton.h new file mode 100644 index 00000000..89f3b341 --- /dev/null +++ b/lib/fl_subghz/protocols/subghz_protocol_princeton.h @@ -0,0 +1,15 @@ +#pragma once + +#include "subghz_protocol_common.h" + +typedef struct SubGhzProtocolPrinceton SubGhzProtocolPrinceton; + +SubGhzProtocolPrinceton* subghz_protocol_princeton_alloc(); + +void subghz_protocol_princeton_free(SubGhzProtocolPrinceton* instance); + +void subghz_protocol_princeton_send_key(SubGhzProtocolPrinceton* instance, uint64_t key, uint8_t bit, uint8_t repeat); + +void subghz_protocol_princeton_parse(SubGhzProtocolPrinceton* instance, LevelPair data); + +void subghz_protocol_princeton_to_str(SubGhzProtocolPrinceton* instance, string_t output); diff --git a/lib/fl_subghz/subghz_worker.c b/lib/fl_subghz/subghz_worker.c new file mode 100644 index 00000000..afc1f21e --- /dev/null +++ b/lib/fl_subghz/subghz_worker.c @@ -0,0 +1,110 @@ +#include "subghz_worker.h" + +#include +#include + +struct SubGhzWorker { + FuriThread* thread; + StreamBufferHandle_t stream; + + volatile bool running; + volatile bool overrun; + + SubGhzWorkerOverrunCallback overrun_callback; + SubGhzWorkerPairCallback pair_callback; + void* context; +}; + +void subghz_worker_rx_callback( + ApiHalSubGhzCaptureLevel level, + uint32_t duration, + void* context) { + + SubGhzWorker* instance = context; + + BaseType_t xHigherPriorityTaskWoken = pdFALSE; + LevelPair pair = {.level = level, .duration = duration}; + if(instance->overrun) { + instance->overrun = false; + pair.level = ApiHalSubGhzCaptureLevelOverrun; + } + size_t ret = + xStreamBufferSendFromISR(instance->stream, &pair, sizeof(LevelPair), &xHigherPriorityTaskWoken); + if(sizeof(LevelPair) != ret) instance->overrun = true; + portYIELD_FROM_ISR(xHigherPriorityTaskWoken); +} + +static int32_t subghz_worker_thread_callback(void* context) { + SubGhzWorker* instance = context; + + LevelPair pair; + while(instance->running) { + int ret = xStreamBufferReceive(instance->stream, &pair, sizeof(LevelPair), 10); + if(ret == sizeof(LevelPair)) { + if(pair.level == ApiHalSubGhzCaptureLevelOverrun) { + printf("."); + if (instance->overrun_callback) instance->overrun_callback(instance->context); + } else { + if (instance->pair_callback) instance->pair_callback(instance->context, pair); + } + } + } + + return 0; +} + +SubGhzWorker* subghz_worker_alloc() { + SubGhzWorker* instance = furi_alloc(sizeof(SubGhzWorker)); + + instance->thread = furi_thread_alloc(); + furi_thread_set_name(instance->thread, "subghz_worker"); + furi_thread_set_stack_size(instance->thread, 2048); + furi_thread_set_context(instance->thread, instance); + furi_thread_set_callback(instance->thread, subghz_worker_thread_callback); + + instance->stream = xStreamBufferCreate(sizeof(LevelPair) * 1024, sizeof(LevelPair)); + + return instance; +} + +void subghz_worker_free(SubGhzWorker* instance) { + furi_assert(instance); + + vStreamBufferDelete(instance->stream); + furi_thread_free(instance->thread); + + free(instance); +} + +void subghz_worker_set_overrun_callback(SubGhzWorker* instance, SubGhzWorkerOverrunCallback callback) { + furi_assert(instance); + instance->overrun_callback = callback; +} + +void subghz_worker_set_pair_callback(SubGhzWorker* instance, SubGhzWorkerPairCallback callback) { + furi_assert(instance); + instance->pair_callback = callback; +} + +void subghz_worker_set_context(SubGhzWorker* instance, void* context) { + furi_assert(instance); + instance->context = context; +} + +void subghz_worker_start(SubGhzWorker* instance) { + furi_assert(instance); + furi_assert(!instance->running); + + instance->running = true; + + furi_thread_start(instance->thread); +} + +void subghz_worker_stop(SubGhzWorker* instance) { + furi_assert(instance); + furi_assert(instance->running); + + instance->running = false; + + furi_thread_join(instance->thread); +} diff --git a/lib/fl_subghz/subghz_worker.h b/lib/fl_subghz/subghz_worker.h new file mode 100644 index 00000000..d9b6d0ca --- /dev/null +++ b/lib/fl_subghz/subghz_worker.h @@ -0,0 +1,25 @@ +#pragma once + +#include + +typedef struct SubGhzWorker SubGhzWorker; + +typedef void (*SubGhzWorkerOverrunCallback)(void* context); + +typedef void (*SubGhzWorkerPairCallback)(void* context, LevelPair pair); + +void subghz_worker_rx_callback(ApiHalSubGhzCaptureLevel level, uint32_t duration, void* context); + +SubGhzWorker* subghz_worker_alloc(); + +void subghz_worker_free(SubGhzWorker* instance); + +void subghz_worker_set_overrun_callback(SubGhzWorker* instance, SubGhzWorkerOverrunCallback callback); + +void subghz_worker_set_pair_callback(SubGhzWorker* instance, SubGhzWorkerPairCallback callback); + +void subghz_worker_set_context(SubGhzWorker* instance, void* context); + +void subghz_worker_start(SubGhzWorker* instance); + +void subghz_worker_stop(SubGhzWorker* instance); diff --git a/lib/lib.mk b/lib/lib.mk index 4c467786..58cb1741 100644 --- a/lib/lib.mk +++ b/lib/lib.mk @@ -36,12 +36,11 @@ C_SOURCES += $(FATFS_DIR)/ff_gen_drv.c C_SOURCES += $(FATFS_DIR)/diskio.c C_SOURCES += $(FATFS_DIR)/option/unicode.c -ifeq ($(SRV_INTERNAL_STORAGE), 1) +# Little FS LITTLEFS_DIR = $(LIB_DIR)/littlefs CFLAGS += -I$(LITTLEFS_DIR) C_SOURCES += $(LITTLEFS_DIR)/lfs.c C_SOURCES += $(LITTLEFS_DIR)/lfs_util.c -endif ifeq ($(APP_NFC), 1) ST25RFAL002_DIR = $(LIB_DIR)/ST25RFAL002 @@ -100,6 +99,10 @@ C_SOURCES += $(wildcard $(LIB_DIR)/irda/*/*.c) CFLAGS += -I$(LIB_DIR)/args C_SOURCES += $(wildcard $(LIB_DIR)/args/*.c) +# SubGhz +C_SOURCES += $(wildcard $(LIB_DIR)/fl_subghz/*.c) +C_SOURCES += $(wildcard $(LIB_DIR)/fl_subghz/*/*.c) + #scened app template lib CFLAGS += -I$(LIB_DIR)/app-scened-template CPP_SOURCES += $(wildcard $(LIB_DIR)/app-scened-template/*.cpp)