[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
This commit is contained in:
		
							parent
							
								
									dce3665f63
								
							
						
					
					
						commit
						e8211226f3
					
				| @ -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 | ||||
|  | ||||
| @ -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); | ||||
| 
 | ||||
|  | ||||
							
								
								
									
										161
									
								
								applications/subghz/subghz_cli.c
									
									
									
									
									
										
										
										Executable file → Normal file
									
								
							
							
						
						
									
										161
									
								
								applications/subghz/subghz_cli.c
									
									
									
									
									
										
										
										Executable file → Normal file
									
								
							| @ -1,6 +1,11 @@ | ||||
| #include "subghz_cli.h" | ||||
| 
 | ||||
| #include <furi.h> | ||||
| #include <api-hal.h> | ||||
| #include <stream_buffer.h> | ||||
| 
 | ||||
| #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 <fl_subghz/protocols/subghz_protocol.h> | ||||
| 
 | ||||
| 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", "<Frequency in HZ>", 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(); | ||||
| } | ||||
|  | ||||
| @ -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); | ||||
|  | ||||
| @ -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 <furi.h> | ||||
| #include <api-hal.h> | ||||
| @ -11,12 +12,7 @@ | ||||
| #include <gui/view_dispatcher.h> | ||||
| #include <gui/modules/submenu.h> | ||||
| 
 | ||||
| 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, | ||||
|  | ||||
							
								
								
									
										188
									
								
								applications/subghz/views/subghz_capture.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										188
									
								
								applications/subghz/views/subghz_capture.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,188 @@ | ||||
| #include "subghz_capture.h" | ||||
| #include "../subghz_i.h" | ||||
| 
 | ||||
| #include <math.h> | ||||
| #include <furi.h> | ||||
| #include <api-hal.h> | ||||
| #include <input/input.h> | ||||
| #include <gui/elements.h> | ||||
| #include <notification/notification-messages.h> | ||||
| 
 | ||||
| #include <fl_subghz/subghz_worker.h> | ||||
| #include <fl_subghz/protocols/subghz_protocol.h> | ||||
| 
 | ||||
| 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; | ||||
| } | ||||
							
								
								
									
										11
									
								
								applications/subghz/views/subghz_capture.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								applications/subghz/views/subghz_capture.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,11 @@ | ||||
| #pragma once | ||||
| 
 | ||||
| #include <gui/view.h> | ||||
| 
 | ||||
| typedef struct SubghzCapture SubghzCapture; | ||||
| 
 | ||||
| SubghzCapture* subghz_capture_alloc(); | ||||
| 
 | ||||
| void subghz_capture_free(SubghzCapture* subghz_capture); | ||||
| 
 | ||||
| View* subghz_capture_get_view(SubghzCapture* subghz_capture); | ||||
| @ -1,5 +1,5 @@ | ||||
| #include "subghz_static.h" | ||||
| #include "subghz_i.h" | ||||
| #include "../subghz_i.h" | ||||
| 
 | ||||
| #include <math.h> | ||||
| #include <furi.h> | ||||
| @ -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; | ||||
|         }); | ||||
| 
 | ||||
| @ -1,5 +1,5 @@ | ||||
| #include "subghz_test_basic.h" | ||||
| #include "subghz_i.h" | ||||
| #include "../subghz_i.h" | ||||
| 
 | ||||
| #include <math.h> | ||||
| #include <furi.h> | ||||
| @ -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; | ||||
| @ -1,5 +1,5 @@ | ||||
| #include "subghz_test_packet.h" | ||||
| #include "subghz_i.h" | ||||
| #include "../subghz_i.h" | ||||
| 
 | ||||
| #include <math.h> | ||||
| #include <furi.h> | ||||
| @ -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; | ||||
| @ -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) { | ||||
|  | ||||
| @ -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 | ||||
| } | ||||
|  | ||||
| @ -13,7 +13,6 @@ template <unsigned int N> 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" | ||||
|  | ||||
| @ -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 */ | ||||
|     { 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,12 +25,51 @@ 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
 | ||||
| 
 | ||||
|     /* Magic */ | ||||
|     { CC1101_TEST2,     0x81}, | ||||
|     { CC1101_TEST1,     0x35}, | ||||
|     { CC1101_TEST0,     0x09}, | ||||
| @ -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() {} | ||||
|  | ||||
							
								
								
									
										42
									
								
								firmware/targets/f6/api-hal/api-hal-interrupt.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								firmware/targets/f6/api-hal/api-hal-interrupt.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,42 @@ | ||||
| #include "api-hal-interrupt.h" | ||||
| 
 | ||||
| #include <furi.h> | ||||
| #include <main.h> | ||||
| #include <stm32wbxx_ll_tim.h> | ||||
| 
 | ||||
| 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); | ||||
| } | ||||
							
								
								
									
										23
									
								
								firmware/targets/f6/api-hal/api-hal-interrupt.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								firmware/targets/f6/api-hal/api-hal-interrupt.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,23 @@ | ||||
| #pragma once | ||||
| 
 | ||||
| #include <stm32wbxx_ll_tim.h> | ||||
| 
 | ||||
| #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 | ||||
| @ -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 <stm32wbxx_ll_tim.h> | ||||
| #include <stm32wbxx_ll_gpio.h> | ||||
| 
 | ||||
| #include <stdio.h> | ||||
| #include <furi.h> | ||||
| #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); | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -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); | ||||
| 
 | ||||
| @ -2,7 +2,9 @@ | ||||
| 
 | ||||
| #include <api-hal-gpio.h> | ||||
| #include <api-hal-spi.h> | ||||
| #include <api-hal-interrupt.h> | ||||
| #include <api-hal-resources.h> | ||||
| 
 | ||||
| #include <furi.h> | ||||
| #include <cc1101.h> | ||||
| #include <stdio.h> | ||||
| @ -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 */ | ||||
|     { 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,12 +27,51 @@ 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
 | ||||
| 
 | ||||
|     /* Magic */ | ||||
|     { CC1101_TEST2,     0x81}, | ||||
|     { CC1101_TEST1,     0x35}, | ||||
|     { CC1101_TEST0,     0x09}, | ||||
| @ -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); | ||||
| } | ||||
|  | ||||
| @ -1,45 +0,0 @@ | ||||
| #include "api-hal-tim_i.h" | ||||
| #include "api-hal-irda_i.h" | ||||
| #include <stm32wbxx_ll_tim.h> | ||||
| #include <furi.h> | ||||
| 
 | ||||
| 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); | ||||
|     } | ||||
| } | ||||
| @ -1,7 +0,0 @@ | ||||
| #pragma once | ||||
| 
 | ||||
| typedef enum{ | ||||
|     TimerIRQSourceCCI1, | ||||
|     TimerIRQSourceCCI2, | ||||
| } TimerIRQSource; | ||||
| 
 | ||||
| @ -1,16 +0,0 @@ | ||||
| #include "api-hal/api-interrupt-mgr.h" | ||||
| #include <main.h> | ||||
| 
 | ||||
| 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); | ||||
| } | ||||
| @ -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]) { | ||||
|  | ||||
| @ -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 */ | ||||
| 
 | ||||
|  | ||||
							
								
								
									
										130
									
								
								lib/fl_subghz/protocols/subghz_protocol.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										130
									
								
								lib/fl_subghz/protocols/subghz_protocol.c
									
									
									
									
									
										Normal file
									
								
							| @ -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 <furi.h> | ||||
| #include <m-string.h> | ||||
| #include <filesystem-api.h> | ||||
| 
 | ||||
| #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); | ||||
| } | ||||
							
								
								
									
										19
									
								
								lib/fl_subghz/protocols/subghz_protocol.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								lib/fl_subghz/protocols/subghz_protocol.h
									
									
									
									
									
										Normal file
									
								
							| @ -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); | ||||
							
								
								
									
										119
									
								
								lib/fl_subghz/protocols/subghz_protocol_came.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										119
									
								
								lib/fl_subghz/protocols/subghz_protocol_came.c
									
									
									
									
									
										Normal file
									
								
							| @ -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; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										13
									
								
								lib/fl_subghz/protocols/subghz_protocol_came.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								lib/fl_subghz/protocols/subghz_protocol_came.h
									
									
									
									
									
										Normal file
									
								
							| @ -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); | ||||
							
								
								
									
										4
									
								
								lib/fl_subghz/protocols/subghz_protocol_cfm.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								lib/fl_subghz/protocols/subghz_protocol_cfm.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,4 @@ | ||||
| 
 | ||||
| /*
 | ||||
|  * https://phreakerclub.com/616
 | ||||
|  */ | ||||
							
								
								
									
										70
									
								
								lib/fl_subghz/protocols/subghz_protocol_common.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										70
									
								
								lib/fl_subghz/protocols/subghz_protocol_common.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,70 @@ | ||||
| #include "subghz_protocol_common.h" | ||||
| #include <stdio.h> | ||||
| 
 | ||||
| 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; i<count_bit; i++) { | ||||
|         key_reverse=key_reverse<<1|bit_read(key,i); | ||||
|     } | ||||
|     return key_reverse; | ||||
| } | ||||
| 
 | ||||
| void subghz_protocol_common_set_callback(SubGhzProtocolCommon* common, SubGhzProtocolCommonCallback callback, void* context) { | ||||
|     common->callback = 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 | ||||
|             ); | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										53
									
								
								lib/fl_subghz/protocols/subghz_protocol_common.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								lib/fl_subghz/protocols/subghz_protocol_common.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,53 @@ | ||||
| #pragma once | ||||
| 
 | ||||
| #include <m-string.h> | ||||
| #include <api-hal.h> | ||||
| #include <stdint.h> | ||||
| 
 | ||||
| #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); | ||||
| 
 | ||||
							
								
								
									
										378
									
								
								lib/fl_subghz/protocols/subghz_protocol_keeloq.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										378
									
								
								lib/fl_subghz/protocols/subghz_protocol_keeloq.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,378 @@ | ||||
| #include "subghz_protocol_keeloq.h" | ||||
| 
 | ||||
| #include <furi.h> | ||||
| 
 | ||||
| #include <m-string.h> | ||||
| #include <m-array.h> | ||||
| 
 | ||||
| /*
 | ||||
|  * 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 | ||||
|     ); | ||||
| } | ||||
							
								
								
									
										17
									
								
								lib/fl_subghz/protocols/subghz_protocol_keeloq.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								lib/fl_subghz/protocols/subghz_protocol_keeloq.h
									
									
									
									
									
										Normal file
									
								
							| @ -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); | ||||
							
								
								
									
										117
									
								
								lib/fl_subghz/protocols/subghz_protocol_nice_flo.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										117
									
								
								lib/fl_subghz/protocols/subghz_protocol_nice_flo.c
									
									
									
									
									
										Normal file
									
								
							| @ -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; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										15
									
								
								lib/fl_subghz/protocols/subghz_protocol_nice_flo.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								lib/fl_subghz/protocols/subghz_protocol_nice_flo.h
									
									
									
									
									
										Normal file
									
								
							| @ -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); | ||||
							
								
								
									
										133
									
								
								lib/fl_subghz/protocols/subghz_protocol_nice_flor_s.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										133
									
								
								lib/fl_subghz/protocols/subghz_protocol_nice_flor_s.c
									
									
									
									
									
										Normal file
									
								
							| @ -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; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										15
									
								
								lib/fl_subghz/protocols/subghz_protocol_nice_flor_s.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								lib/fl_subghz/protocols/subghz_protocol_nice_flor_s.h
									
									
									
									
									
										Normal file
									
								
							| @ -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); | ||||
							
								
								
									
										109
									
								
								lib/fl_subghz/protocols/subghz_protocol_princeton.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										109
									
								
								lib/fl_subghz/protocols/subghz_protocol_princeton.c
									
									
									
									
									
										Normal file
									
								
							| @ -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; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										15
									
								
								lib/fl_subghz/protocols/subghz_protocol_princeton.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								lib/fl_subghz/protocols/subghz_protocol_princeton.h
									
									
									
									
									
										Normal file
									
								
							| @ -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); | ||||
							
								
								
									
										110
									
								
								lib/fl_subghz/subghz_worker.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										110
									
								
								lib/fl_subghz/subghz_worker.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,110 @@ | ||||
| #include "subghz_worker.h" | ||||
| 
 | ||||
| #include <stream_buffer.h> | ||||
| #include <furi.h> | ||||
| 
 | ||||
| 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); | ||||
| } | ||||
							
								
								
									
										25
									
								
								lib/fl_subghz/subghz_worker.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								lib/fl_subghz/subghz_worker.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,25 @@ | ||||
| #pragma once | ||||
| 
 | ||||
| #include <api-hal.h> | ||||
| 
 | ||||
| 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); | ||||
| @ -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) | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 あく
						あく