[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 | #endif | ||||||
| 
 | 
 | ||||||
| #ifdef APP_SUBGHZ | #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 | #endif | ||||||
| 
 | 
 | ||||||
| #ifdef APP_LF_RFID | #ifdef APP_LF_RFID | ||||||
|  | |||||||
| @ -1,83 +1,33 @@ | |||||||
| #include "subghz_i.h" | #include "subghz_i.h" | ||||||
| 
 | 
 | ||||||
| const SubGhzFrequency subghz_frequencies[] = { | const uint32_t subghz_frequencies[] = { | ||||||
|     /* 301 */ |     /* 300 - 348 */ | ||||||
|     { |     300000000, | ||||||
|         .frequency = 301000000, |     315000000, | ||||||
|         .path = ApiHalSubGhzPath315, |     348000000, | ||||||
|     }, |     /* 387 - 464 */ | ||||||
|     /* 315 */ |     387000000, | ||||||
|     { |     433075000, /* LPD433 first */ | ||||||
|         .frequency = 315000000, |     433920000, /* LPD433 mid */ | ||||||
|         .path = ApiHalSubGhzPath315, |     434775000, /* LPD433 last channels */ | ||||||
|     }, |     438900000, | ||||||
|     /* 346 - 385 */ |     464000000, | ||||||
|     { |     /* 779 - 928 */ | ||||||
|         .frequency = 346000000, |     779000000, | ||||||
|         .path = ApiHalSubGhzPath315, |     868350000, | ||||||
|     }, |     915000000, | ||||||
|     { |     925000000, | ||||||
|         .frequency = 385000000, |     928000000, | ||||||
|         .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_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; | const uint32_t subghz_frequencies_433_92 = 5; | ||||||
| 
 | 
 | ||||||
| void subghz_menu_callback(void* context, uint32_t index) { | void subghz_menu_callback(void* context, uint32_t index) { | ||||||
|     furi_assert(context); |     furi_assert(context); | ||||||
| 
 |  | ||||||
|     SubGhz* subghz = context; |     SubGhz* subghz = context; | ||||||
| 
 | 
 | ||||||
|     if(index == 0) { |     view_dispatcher_switch_to_view(subghz->view_dispatcher, index); | ||||||
|         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); |  | ||||||
|     } |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| uint32_t subghz_exit(void* context) { | uint32_t subghz_exit(void* context) { | ||||||
| @ -98,14 +48,25 @@ SubGhz* subghz_alloc() { | |||||||
| 
 | 
 | ||||||
|     // Menu
 |     // Menu
 | ||||||
|     subghz->submenu = submenu_alloc(); |     subghz->submenu = submenu_alloc(); | ||||||
|     submenu_add_item(subghz->submenu, "Basic Test", 0, subghz_menu_callback, subghz); |     submenu_add_item(subghz->submenu, "Capture", SubGhzViewCapture, subghz_menu_callback, subghz); | ||||||
|     submenu_add_item(subghz->submenu, "Packet Test", 1, subghz_menu_callback, subghz); |     submenu_add_item( | ||||||
|     submenu_add_item(subghz->submenu, "Static Code", 2, subghz_menu_callback, subghz); |         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* submenu_view = submenu_get_view(subghz->submenu); | ||||||
|     view_set_previous_callback(submenu_view, subghz_exit); |     view_set_previous_callback(submenu_view, subghz_exit); | ||||||
|     view_dispatcher_add_view(subghz->view_dispatcher, SubGhzViewMenu, submenu_view); |     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
 |     // Basic Test Module
 | ||||||
|     subghz->subghz_test_basic = subghz_test_basic_alloc(); |     subghz->subghz_test_basic = subghz_test_basic_alloc(); | ||||||
|     view_dispatcher_add_view( |     view_dispatcher_add_view( | ||||||
| @ -150,6 +111,10 @@ void subghz_free(SubGhz* subghz) { | |||||||
|     view_dispatcher_remove_view(subghz->view_dispatcher, SubGhzViewMenu); |     view_dispatcher_remove_view(subghz->view_dispatcher, SubGhzViewMenu); | ||||||
|     submenu_free(subghz->submenu); |     submenu_free(subghz->submenu); | ||||||
| 
 | 
 | ||||||
|  |     // Capture
 | ||||||
|  |     view_dispatcher_remove_view(subghz->view_dispatcher, SubGhzViewCapture); | ||||||
|  |     subghz_capture_free(subghz->subghz_capture); | ||||||
|  | 
 | ||||||
|     // View Dispatcher
 |     // View Dispatcher
 | ||||||
|     view_dispatcher_free(subghz->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 "subghz_cli.h" | ||||||
|  | 
 | ||||||
| #include <furi.h> | #include <furi.h> | ||||||
| #include <api-hal.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[] = { | static const uint8_t subghz_test_packet_data[] = { | ||||||
|     0x30, // 48bytes to transmit
 |     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, |     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() { | void subghz_cli_init() { | ||||||
|     Cli* cli = furi_record_open("cli"); |     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_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_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_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"); |     furi_record_close("cli"); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void subghz_cli_command_tx_carrier(Cli* cli, string_t args, void* context) { | 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); |     int ret = sscanf(string_get_cstr(args), "%lu", &frequency); | ||||||
|     if(ret != 1) { |     if(ret != 1) { | ||||||
|         printf("sscanf returned %d, frequency: %lu\r\n", ret, frequency); |         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; |         return; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     if(frequency < 300000000 || frequency > 925000000) { |     if(!subghz_check_frequency_range(frequency)) { | ||||||
|         printf("Frequency must be in 300000000...925000000 range, not %lu\r\n", frequency); |         printf( | ||||||
|  |             "Frequency must be in " CC1101_FREQUENCY_RANGE_STR " range, not %lu\r\n", frequency); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     api_hal_subghz_reset(); |     api_hal_subghz_reset(); | ||||||
|     api_hal_subghz_load_preset(ApiHalSubGhzPresetOokAsync); |     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("Transmitting at frequency %lu Hz\r\n", frequency); | ||||||
|     printf("Press CTRL+C to stop\r\n"); |     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_init(&gpio_cc1101_g0, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); | ||||||
|     hal_gpio_write(&gpio_cc1101_g0, false); |     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) { | 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); |     int ret = sscanf(string_get_cstr(args), "%lu", &frequency); | ||||||
|     if(ret != 1) { |     if(ret != 1) { | ||||||
|         printf("sscanf returned %d, frequency: %lu\r\n", ret, frequency); |         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; |         return; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     if(frequency < 300000000 || frequency > 925000000) { |     if(!subghz_check_frequency_range(frequency)) { | ||||||
|         printf("Frequency must be in 300000000...925000000 range, not %lu\r\n", frequency); |         printf( | ||||||
|  |             "Frequency must be in " CC1101_FREQUENCY_RANGE_STR " range, not %lu\r\n", frequency); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     api_hal_subghz_reset(); |     api_hal_subghz_reset(); | ||||||
|     api_hal_subghz_load_preset(ApiHalSubGhzPresetOokAsync); |     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("Receiving at frequency %lu Hz\r\n", frequency); | ||||||
|     printf("Press CTRL+C to stop\r\n"); |     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); |     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) { | 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 pattern; | ||||||
|     uint32_t count; |     uint32_t count; | ||||||
| 
 | 
 | ||||||
| @ -120,8 +124,9 @@ void subghz_cli_command_tx_pt(Cli* cli, string_t args, void* context) { | |||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     if(frequency < 300000000 || frequency > 925000000) { |     if(!subghz_check_frequency_range(frequency)) { | ||||||
|         printf("Frequency must be in 300000000...925000000 range, not %lu\r\n", frequency); |         printf( | ||||||
|  |             "Frequency must be in " CC1101_FREQUENCY_RANGE_STR " range, not %lu\r\n", frequency); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|     if(pattern > 1) { |     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); |     api_hal_subghz_load_preset(ApiHalSubGhzPreset2FskPacket); | ||||||
| 
 | 
 | ||||||
|     frequency = api_hal_subghz_set_frequency(frequency); |     frequency = api_hal_subghz_set_frequency_and_path(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); |  | ||||||
|     } |  | ||||||
|     hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullNo, GpioSpeedLow); |     hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullNo, GpioSpeedLow); | ||||||
| 
 | 
 | ||||||
|     uint8_t status = api_hal_subghz_get_status(); |     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) { | 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); |     int ret = sscanf(string_get_cstr(args), "%lu", &frequency); | ||||||
|     if(ret != 1) { |     if(ret != 1) { | ||||||
| @ -172,8 +170,9 @@ void subghz_cli_command_rx_pt(Cli* cli, string_t args, void* context) { | |||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     if(frequency < 300000000 || frequency > 925000000) { |     if(!subghz_check_frequency_range(frequency)) { | ||||||
|         printf("Frequency must be in 300000000...925000000 range, not %lu\r\n", frequency); |         printf( | ||||||
|  |             "Frequency must be in " CC1101_FREQUENCY_RANGE_STR " range, not %lu\r\n", frequency); | ||||||
|         return; |         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_idle(); | ||||||
|     api_hal_subghz_load_preset(ApiHalSubGhzPreset2FskPacket); |     api_hal_subghz_load_preset(ApiHalSubGhzPreset2FskPacket); | ||||||
| 
 | 
 | ||||||
|     frequency = api_hal_subghz_set_frequency(frequency); |     frequency = api_hal_subghz_set_frequency_and_path(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); |  | ||||||
|     } |  | ||||||
|     hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullNo, GpioSpeedLow); |     hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullNo, GpioSpeedLow); | ||||||
| 
 | 
 | ||||||
|     uint8_t status = api_hal_subghz_get_status(); |     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); |     api_hal_subghz_set_path(ApiHalSubGhzPathIsolate); | ||||||
|     hal_gpio_init(&gpio_cc1101_g0, GpioModeAnalog, GpioPullNo, GpioSpeedLow); |     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_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_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 | #pragma once | ||||||
| 
 | 
 | ||||||
| #include "subghz.h" | #include "subghz.h" | ||||||
| #include "subghz_test_basic.h" | #include "views/subghz_capture.h" | ||||||
| #include "subghz_test_packet.h" | #include "views/subghz_test_basic.h" | ||||||
| #include "subghz_static.h" | #include "views/subghz_test_packet.h" | ||||||
|  | #include "views/subghz_static.h" | ||||||
| 
 | 
 | ||||||
| #include <furi.h> | #include <furi.h> | ||||||
| #include <api-hal.h> | #include <api-hal.h> | ||||||
| @ -11,12 +12,7 @@ | |||||||
| #include <gui/view_dispatcher.h> | #include <gui/view_dispatcher.h> | ||||||
| #include <gui/modules/submenu.h> | #include <gui/modules/submenu.h> | ||||||
| 
 | 
 | ||||||
| typedef struct { | extern const uint32_t subghz_frequencies[]; | ||||||
|     uint32_t frequency; |  | ||||||
|     uint8_t path; |  | ||||||
| } SubGhzFrequency; |  | ||||||
| 
 |  | ||||||
| extern const SubGhzFrequency subghz_frequencies[]; |  | ||||||
| extern const uint32_t subghz_frequencies_count; | extern const uint32_t subghz_frequencies_count; | ||||||
| extern const uint32_t subghz_frequencies_433_92; | extern const uint32_t subghz_frequencies_433_92; | ||||||
| 
 | 
 | ||||||
| @ -27,6 +23,8 @@ struct SubGhz { | |||||||
| 
 | 
 | ||||||
|     Submenu* submenu; |     Submenu* submenu; | ||||||
| 
 | 
 | ||||||
|  |     SubghzCapture* subghz_capture; | ||||||
|  | 
 | ||||||
|     SubghzTestBasic* subghz_test_basic; |     SubghzTestBasic* subghz_test_basic; | ||||||
| 
 | 
 | ||||||
|     SubghzTestPacket* subghz_test_packet; |     SubghzTestPacket* subghz_test_packet; | ||||||
| @ -36,6 +34,7 @@ struct SubGhz { | |||||||
| 
 | 
 | ||||||
| typedef enum { | typedef enum { | ||||||
|     SubGhzViewMenu, |     SubGhzViewMenu, | ||||||
|  |     SubGhzViewCapture, | ||||||
|     SubGhzViewTestBasic, |     SubGhzViewTestBasic, | ||||||
|     SubGhzViewTestPacket, |     SubGhzViewTestPacket, | ||||||
|     SubGhzViewStatic, |     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_static.h" | ||||||
| #include "subghz_i.h" | #include "../subghz_i.h" | ||||||
| 
 | 
 | ||||||
| #include <math.h> | #include <math.h> | ||||||
| #include <furi.h> | #include <furi.h> | ||||||
| @ -30,7 +30,6 @@ typedef enum { | |||||||
| typedef struct { | typedef struct { | ||||||
|     uint8_t frequency; |     uint8_t frequency; | ||||||
|     uint32_t real_frequency; |     uint32_t real_frequency; | ||||||
|     ApiHalSubGhzPath path; |  | ||||||
|     uint8_t button; |     uint8_t button; | ||||||
| } SubghzStaticModel; | } SubghzStaticModel; | ||||||
| 
 | 
 | ||||||
| @ -51,21 +50,8 @@ void subghz_static_draw(Canvas* canvas, SubghzStaticModel* model) { | |||||||
|         model->real_frequency / 1000 % 1000, |         model->real_frequency / 1000 % 1000, | ||||||
|         model->real_frequency % 1000); |         model->real_frequency % 1000); | ||||||
|     canvas_draw_str(canvas, 2, 24, buffer); |     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); |     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) { | 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) { |                 } else if(event->key == InputKeyUp) { | ||||||
|                     if(model->button < 3) model->button++; |                     if(model->button < 3) model->button++; | ||||||
|                 } |                 } | ||||||
|                 model->path = subghz_frequencies[model->frequency].path; |  | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             if(reconfigure) { |             if(reconfigure) { | ||||||
|                 api_hal_subghz_idle(); |                 api_hal_subghz_idle(); | ||||||
|                 model->real_frequency = |                 model->real_frequency = | ||||||
|                     api_hal_subghz_set_frequency(subghz_frequencies[model->frequency].frequency); |                     api_hal_subghz_set_frequency_and_path(subghz_frequencies[model->frequency]); | ||||||
|                 api_hal_subghz_set_path(model->path); |  | ||||||
|                 api_hal_subghz_tx(); |                 api_hal_subghz_tx(); | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
| @ -154,10 +138,8 @@ void subghz_static_enter(void* context) { | |||||||
|         subghz_static->view, (SubghzStaticModel * model) { |         subghz_static->view, (SubghzStaticModel * model) { | ||||||
|             model->frequency = subghz_frequencies_433_92; |             model->frequency = subghz_frequencies_433_92; | ||||||
|             model->real_frequency = |             model->real_frequency = | ||||||
|                 api_hal_subghz_set_frequency(subghz_frequencies[model->frequency].frequency); |                 api_hal_subghz_set_frequency_and_path(subghz_frequencies[model->frequency]); | ||||||
|             model->path = subghz_frequencies[model->frequency].path; |  | ||||||
|             model->button = 0; |             model->button = 0; | ||||||
|             api_hal_subghz_set_path(model->path); |  | ||||||
|             return true; |             return true; | ||||||
|         }); |         }); | ||||||
| 
 | 
 | ||||||
| @ -1,5 +1,5 @@ | |||||||
| #include "subghz_test_basic.h" | #include "subghz_test_basic.h" | ||||||
| #include "subghz_i.h" | #include "../subghz_i.h" | ||||||
| 
 | 
 | ||||||
| #include <math.h> | #include <math.h> | ||||||
| #include <furi.h> | #include <furi.h> | ||||||
| @ -98,7 +98,7 @@ bool subghz_test_basic_input(InputEvent* event, void* context) { | |||||||
|                 } |                 } | ||||||
| 
 | 
 | ||||||
|                 model->real_frequency = |                 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); |                 api_hal_subghz_set_path(model->path); | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
| @ -131,7 +131,7 @@ void subghz_test_basic_enter(void* context) { | |||||||
|         subghz_test_basic->view, (SubghzTestBasicModel * model) { |         subghz_test_basic->view, (SubghzTestBasicModel * model) { | ||||||
|             model->frequency = subghz_frequencies_433_92; // 433
 |             model->frequency = subghz_frequencies_433_92; // 433
 | ||||||
|             model->real_frequency = |             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->path = ApiHalSubGhzPathIsolate; // isolate
 | ||||||
|             model->rssi = 0.0f; |             model->rssi = 0.0f; | ||||||
|             model->status = SubghzTestBasicModelStatusRx; |             model->status = SubghzTestBasicModelStatusRx; | ||||||
| @ -1,5 +1,5 @@ | |||||||
| #include "subghz_test_packet.h" | #include "subghz_test_packet.h" | ||||||
| #include "subghz_i.h" | #include "../subghz_i.h" | ||||||
| 
 | 
 | ||||||
| #include <math.h> | #include <math.h> | ||||||
| #include <furi.h> | #include <furi.h> | ||||||
| @ -106,7 +106,7 @@ bool subghz_test_packet_input(InputEvent* event, void* context) { | |||||||
|                 } |                 } | ||||||
| 
 | 
 | ||||||
|                 model->real_frequency = |                 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); |                 api_hal_subghz_set_path(model->path); | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
| @ -138,7 +138,7 @@ void subghz_test_packet_enter(void* context) { | |||||||
|         subghz_test_packet->view, (SubghzTestPacketModel * model) { |         subghz_test_packet->view, (SubghzTestPacketModel * model) { | ||||||
|             model->frequency = subghz_frequencies_433_92; |             model->frequency = subghz_frequencies_433_92; | ||||||
|             model->real_frequency = |             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->path = ApiHalSubGhzPathIsolate; // isolate
 | ||||||
|             model->rssi = 0.0f; |             model->rssi = 0.0f; | ||||||
|             model->status = SubghzTestPacketModelStatusRx; |             model->status = SubghzTestPacketModelStatusRx; | ||||||
| @ -136,7 +136,10 @@ osStatus_t furi_thread_terminate(FuriThread* thread) { | |||||||
| 
 | 
 | ||||||
| osStatus_t furi_thread_join(FuriThread* thread) { | osStatus_t furi_thread_join(FuriThread* thread) { | ||||||
|     furi_assert(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) { | osThreadId_t furi_thread_get_thread_id(FuriThread* thread) { | ||||||
|  | |||||||
| @ -11,6 +11,7 @@ extern "C" { | |||||||
| typedef enum { | typedef enum { | ||||||
|     ApiHalSubGhzPresetOokAsync,     /** OOK, asynchronous */ |     ApiHalSubGhzPresetOokAsync,     /** OOK, asynchronous */ | ||||||
|     ApiHalSubGhzPreset2FskPacket,   /** 2FSK, 115kBaud, variable packet length */ |     ApiHalSubGhzPreset2FskPacket,   /** 2FSK, 115kBaud, variable packet length */ | ||||||
|  |     ApiHalSubGhzPresetMP,      /** MP OOK, asynchronous */ | ||||||
| } ApiHalSubGhzPreset; | } ApiHalSubGhzPreset; | ||||||
| 
 | 
 | ||||||
| /**  Switchable Radio Paths */ | /**  Switchable Radio Paths */ | ||||||
| @ -88,6 +89,13 @@ void api_hal_subghz_tx(); | |||||||
| /** Get RSSI value in dBm */ | /** Get RSSI value in dBm */ | ||||||
| float api_hal_subghz_get_rssi(); | 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
 | /** Set frequency
 | ||||||
|  * @param frequency in herz |  * @param frequency in herz | ||||||
|  * @return real 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); | 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 | #ifdef __cplusplus | ||||||
| } | } | ||||||
|  | |||||||
| @ -13,7 +13,6 @@ template <unsigned int N> struct STOP_EXTERNING_ME {}; | |||||||
| #include "api-hal-delay.h" | #include "api-hal-delay.h" | ||||||
| #include "api-hal-pwm.h" | #include "api-hal-pwm.h" | ||||||
| #include "api-hal-task.h" | #include "api-hal-task.h" | ||||||
| #include "api-hal-tim.h" |  | ||||||
| #include "api-hal-power.h" | #include "api-hal-power.h" | ||||||
| #include "api-hal-vcp.h" | #include "api-hal-vcp.h" | ||||||
| #include "api-hal-version.h" | #include "api-hal-version.h" | ||||||
|  | |||||||
| @ -10,12 +10,13 @@ | |||||||
| static const uint8_t api_hal_subghz_preset_ook_async_regs[][2] = { | static const uint8_t api_hal_subghz_preset_ook_async_regs[][2] = { | ||||||
|     /* Base setting */ |     /* Base setting */ | ||||||
|     { CC1101_IOCFG0,    0x0D }, // GD0 as async serial data output/input
 |     { 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
 |     { CC1101_MCSM0,     0x18 }, // Autocalibrate on idle to TRX, ~150us OSC guard time
 | ||||||
|  | 
 | ||||||
|     /* Async OOK Specific things */ |     /* Async OOK Specific things */ | ||||||
|     { CC1101_MDMCFG2,   0x30 }, // ASK/OOK, No preamble/sync
 |     { CC1101_MDMCFG2,   0x30 }, // ASK/OOK, No preamble/sync
 | ||||||
|     { CC1101_PKTCTRL0,  0x32 }, // Async, no CRC, Infinite
 |     { CC1101_PKTCTRL0,  0x32 }, // Async, no CRC, Infinite
 | ||||||
|     { CC1101_FREND0,    0x01 }, // OOK/ASK PATABLE
 |     { CC1101_FREND0,    0x01 }, // OOK/ASK PATABLE
 | ||||||
|  | 
 | ||||||
|     /* End  */ |     /* End  */ | ||||||
|     { 0, 0 }, |     { 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 |     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] = { | static const uint8_t api_hal_subghz_preset_2fsk_packet_regs[][2] = { | ||||||
|     /* Base setting */ |     /* Base setting */ | ||||||
|     { CC1101_IOCFG0,    0x06 }, // GD0 as async serial data output/input
 |     { CC1101_IOCFG0,    0x06 }, // GD0 as async serial data output/input
 | ||||||
|     { CC1101_FSCTRL1,   0x06 }, // Set IF 26m/2^10*2=2.2MHz
 |  | ||||||
|     { CC1101_MCSM0,     0x18 }, // Autocalibrate on idle to TRX, ~150us OSC guard time
 |     { CC1101_MCSM0,     0x18 }, // Autocalibrate on idle to TRX, ~150us OSC guard time
 | ||||||
| 
 | 
 | ||||||
|  |     /* Magic */ | ||||||
|     { CC1101_TEST2,     0x81}, |     { CC1101_TEST2,     0x81}, | ||||||
|     { CC1101_TEST1,     0x35}, |     { CC1101_TEST1,     0x35}, | ||||||
|     { CC1101_TEST0,     0x09}, |     { CC1101_TEST0,     0x09}, | ||||||
| @ -82,6 +122,9 @@ void api_hal_subghz_load_preset(ApiHalSubGhzPreset preset) { | |||||||
|     } else if(preset == ApiHalSubGhzPreset2FskPacket) { |     } else if(preset == ApiHalSubGhzPreset2FskPacket) { | ||||||
|         api_hal_subghz_load_registers(api_hal_subghz_preset_2fsk_packet_regs); |         api_hal_subghz_load_registers(api_hal_subghz_preset_2fsk_packet_regs); | ||||||
|         api_hal_subghz_load_patable(api_hal_subghz_preset_2fsk_packet_patable); |         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; |     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) { | uint32_t api_hal_subghz_set_frequency(uint32_t value) { | ||||||
|     const ApiHalSpiDevice* device = api_hal_spi_device_get(ApiHalSpiDeviceIdSubGhz); |     const ApiHalSpiDevice* device = api_hal_spi_device_get(ApiHalSpiDeviceIdSubGhz); | ||||||
| 
 | 
 | ||||||
| @ -208,3 +265,9 @@ void api_hal_subghz_set_path(ApiHalSubGhzPath path) { | |||||||
|         furi_check(0); |         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-interrupt.h" | ||||||
| #include "api-hal-tim_i.h" |  | ||||||
| #include "api-hal-irda.h" | #include "api-hal-irda.h" | ||||||
|  | 
 | ||||||
| #include <stm32wbxx_ll_tim.h> | #include <stm32wbxx_ll_tim.h> | ||||||
| #include <stm32wbxx_ll_gpio.h> | #include <stm32wbxx_ll_gpio.h> | ||||||
|  | 
 | ||||||
| #include <stdio.h> | #include <stdio.h> | ||||||
| #include <furi.h> | #include <furi.h> | ||||||
| #include "main.h" | #include "main.h" | ||||||
| #include "api-hal-pwm.h" | #include "api-hal-pwm.h" | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| static struct{ | static struct{ | ||||||
|     TimerISRCallback callback; |     TimerISRCallback callback; | ||||||
|     void *ctx; |     void *ctx; | ||||||
| } timer_irda; | } 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; |     uint32_t duration = 0; | ||||||
|     bool level = 0; |     bool level = 0; | ||||||
| @ -39,6 +43,33 @@ void api_hal_irda_tim_isr(TimerIRQSource source) | |||||||
|         timer_irda.callback(timer_irda.ctx, level, duration); |         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) | void api_hal_irda_rx_irq_init(void) | ||||||
| { | { | ||||||
|     LL_TIM_InitTypeDef TIM_InitStruct = {0}; |     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_SetCounter(TIM2, 0); | ||||||
|     LL_TIM_EnableCounter(TIM2); |     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_SetPriority(TIM2_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(),5, 0)); | ||||||
|     NVIC_EnableIRQ(TIM2_IRQn); |     NVIC_EnableIRQ(TIM2_IRQn); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void api_hal_irda_rx_irq_deinit(void) { | void api_hal_irda_rx_irq_deinit(void) { | ||||||
|     LL_TIM_DisableIT_CC1(TIM2); |     LL_TIM_DeInit(TIM2); | ||||||
|     LL_TIM_DisableIT_CC2(TIM2); |     api_hal_interrupt_set_timer_isr(TIM2, NULL); | ||||||
|     LL_TIM_CC_DisableChannel(TIM2, LL_TIM_CHANNEL_CH1); |  | ||||||
|     LL_TIM_CC_DisableChannel(TIM2, LL_TIM_CHANNEL_CH2); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool api_hal_irda_rx_irq_is_busy(void) { | 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() { | void api_hal_irda_pwm_stop() { | ||||||
|     hal_pwmn_stop(&IRDA_TX_TIM, IRDA_TX_CH); |     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-gpio.h> | ||||||
| #include <api-hal-spi.h> | #include <api-hal-spi.h> | ||||||
|  | #include <api-hal-interrupt.h> | ||||||
| #include <api-hal-resources.h> | #include <api-hal-resources.h> | ||||||
|  | 
 | ||||||
| #include <furi.h> | #include <furi.h> | ||||||
| #include <cc1101.h> | #include <cc1101.h> | ||||||
| #include <stdio.h> | #include <stdio.h> | ||||||
| @ -10,12 +12,13 @@ | |||||||
| static const uint8_t api_hal_subghz_preset_ook_async_regs[][2] = { | static const uint8_t api_hal_subghz_preset_ook_async_regs[][2] = { | ||||||
|     /* Base setting */ |     /* Base setting */ | ||||||
|     { CC1101_IOCFG0,    0x0D }, // GD0 as async serial data output/input
 |     { 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
 |     { CC1101_MCSM0,     0x18 }, // Autocalibrate on idle to TRX, ~150us OSC guard time
 | ||||||
|  | 
 | ||||||
|     /* Async OOK Specific things */ |     /* Async OOK Specific things */ | ||||||
|     { CC1101_MDMCFG2,   0x30 }, // ASK/OOK, No preamble/sync
 |     { CC1101_MDMCFG2,   0x30 }, // ASK/OOK, No preamble/sync
 | ||||||
|     { CC1101_PKTCTRL0,  0x32 }, // Async, no CRC, Infinite
 |     { CC1101_PKTCTRL0,  0x32 }, // Async, no CRC, Infinite
 | ||||||
|     { CC1101_FREND0,    0x01 }, // OOK/ASK PATABLE
 |     { CC1101_FREND0,    0x01 }, // OOK/ASK PATABLE
 | ||||||
|  | 
 | ||||||
|     /* End  */ |     /* End  */ | ||||||
|     { 0, 0 }, |     { 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 |     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] = { | static const uint8_t api_hal_subghz_preset_2fsk_packet_regs[][2] = { | ||||||
|     /* Base setting */ |     /* Base setting */ | ||||||
|     { CC1101_IOCFG0,    0x06 }, // GD0 as async serial data output/input
 |     { CC1101_IOCFG0,    0x06 }, // GD0 as async serial data output/input
 | ||||||
|     { CC1101_FSCTRL1,   0x06 }, // Set IF 26m/2^10*2=2.2MHz
 |  | ||||||
|     { CC1101_MCSM0,     0x18 }, // Autocalibrate on idle to TRX, ~150us OSC guard time
 |     { CC1101_MCSM0,     0x18 }, // Autocalibrate on idle to TRX, ~150us OSC guard time
 | ||||||
| 
 | 
 | ||||||
|  |     /* Magic */ | ||||||
|     { CC1101_TEST2,     0x81}, |     { CC1101_TEST2,     0x81}, | ||||||
|     { CC1101_TEST1,     0x35}, |     { CC1101_TEST1,     0x35}, | ||||||
|     { CC1101_TEST0,     0x09}, |     { CC1101_TEST0,     0x09}, | ||||||
| @ -46,20 +88,26 @@ void api_hal_subghz_init() { | |||||||
|     const ApiHalSpiDevice* device = api_hal_spi_device_get(ApiHalSpiDeviceIdSubGhz); |     const ApiHalSpiDevice* device = api_hal_spi_device_get(ApiHalSpiDeviceIdSubGhz); | ||||||
|     // Reset and shutdown
 |     // Reset and shutdown
 | ||||||
|     cc1101_reset(device); |     cc1101_reset(device); | ||||||
|  | 
 | ||||||
|     // Prepare GD0 for power on self test
 |     // Prepare GD0 for power on self test
 | ||||||
|     hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullNo, GpioSpeedLow); |     hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullNo, GpioSpeedLow); | ||||||
|  | 
 | ||||||
|     // GD0 low
 |     // GD0 low
 | ||||||
|     cc1101_write_reg(device, CC1101_IOCFG0, CC1101IocfgHW); |     cc1101_write_reg(device, CC1101_IOCFG0, CC1101IocfgHW); | ||||||
|     while(hal_gpio_read(&gpio_cc1101_g0) != false); |     while(hal_gpio_read(&gpio_cc1101_g0) != false); | ||||||
|  | 
 | ||||||
|     // GD0 high
 |     // GD0 high
 | ||||||
|     cc1101_write_reg(device, CC1101_IOCFG0, CC1101IocfgHW | CC1101_IOCFG_INV); |     cc1101_write_reg(device, CC1101_IOCFG0, CC1101IocfgHW | CC1101_IOCFG_INV); | ||||||
|     while(hal_gpio_read(&gpio_cc1101_g0) != true); |     while(hal_gpio_read(&gpio_cc1101_g0) != true); | ||||||
|  | 
 | ||||||
|     // Reset GD0 to floating state
 |     // Reset GD0 to floating state
 | ||||||
|     cc1101_write_reg(device, CC1101_IOCFG0, CC1101IocfgHighImpedance); |     cc1101_write_reg(device, CC1101_IOCFG0, CC1101IocfgHighImpedance); | ||||||
|     hal_gpio_init(&gpio_cc1101_g0, GpioModeAnalog, GpioPullNo, GpioSpeedLow); |     hal_gpio_init(&gpio_cc1101_g0, GpioModeAnalog, GpioPullNo, GpioSpeedLow); | ||||||
|  | 
 | ||||||
|     // RF switches
 |     // RF switches
 | ||||||
|     hal_gpio_init(&gpio_rf_sw_0, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); |     hal_gpio_init(&gpio_rf_sw_0, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); | ||||||
|     cc1101_write_reg(device, CC1101_IOCFG2, CC1101IocfgHW); |     cc1101_write_reg(device, CC1101_IOCFG2, CC1101IocfgHW); | ||||||
|  | 
 | ||||||
|     // Turn off oscillator
 |     // Turn off oscillator
 | ||||||
|     cc1101_shutdown(device); |     cc1101_shutdown(device); | ||||||
|     api_hal_spi_device_return(device); |     api_hal_spi_device_return(device); | ||||||
| @ -82,6 +130,9 @@ void api_hal_subghz_load_preset(ApiHalSubGhzPreset preset) { | |||||||
|     } else if(preset == ApiHalSubGhzPreset2FskPacket) { |     } else if(preset == ApiHalSubGhzPreset2FskPacket) { | ||||||
|         api_hal_subghz_load_registers(api_hal_subghz_preset_2fsk_packet_regs); |         api_hal_subghz_load_registers(api_hal_subghz_preset_2fsk_packet_regs); | ||||||
|         api_hal_subghz_load_patable(api_hal_subghz_preset_2fsk_packet_patable); |         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; |     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) { | uint32_t api_hal_subghz_set_frequency(uint32_t value) { | ||||||
|     const ApiHalSpiDevice* device = api_hal_spi_device_get(ApiHalSpiDeviceIdSubGhz); |     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); |     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) { | 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
 |     // 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_FREQ2, (real_value >> 16) & 0xFF); | ||||||
|     cc1101_write_reg(device, CC1101_FREQ1, (real_value >> 8 ) & 0xFF); |     cc1101_write_reg(device, CC1101_FREQ1, (real_value >> 8 ) & 0xFF); | ||||||
|     cc1101_write_reg(device, CC1101_FREQ0, (real_value >> 0 ) & 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; |     return (uint32_t)real_frequency; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| uint32_t cc1101_get_frequency_step(const ApiHalSpiDevice* device) { | 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) { | 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); |     assert((real_value & 0xFF) == real_value); | ||||||
| 
 | 
 | ||||||
|     cc1101_write_reg(device, CC1101_FSCTRL0, (real_value >> 0 ) & 0xFF); |     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; |     return (uint32_t)real_frequency; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| uint32_t cc1101_get_frequency_offset_step(const ApiHalSpiDevice* device) { | 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]) { | void cc1101_set_pa_table(const ApiHalSpiDevice* device, const uint8_t value[8]) { | ||||||
|  | |||||||
| @ -7,10 +7,16 @@ | |||||||
| extern "C" { | extern "C" { | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
|  | /* Frequency Synthesizer constants */ | ||||||
| #define CC1101_QUARTZ                   26000000 | #define CC1101_QUARTZ                   26000000 | ||||||
|  | #define CC1101_FMASK                    0xFFFFFF | ||||||
|  | #define CC1101_FDIV                     0x10000 | ||||||
|  | #define CC1101_IFDIV                    0x400 | ||||||
| 
 | 
 | ||||||
|  | /* IO Bus constants */ | ||||||
| #define CC1101_TIMEOUT                  500 | #define CC1101_TIMEOUT                  500 | ||||||
| 
 | 
 | ||||||
|  | /* Bits and pieces */ | ||||||
| #define CC1101_READ                     (1<<7)  /** Read Bit */ | #define CC1101_READ                     (1<<7)  /** Read Bit */ | ||||||
| #define CC1101_BURST                    (1<<6)  /** Burst 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)/diskio.c | ||||||
| C_SOURCES		+= $(FATFS_DIR)/option/unicode.c | C_SOURCES		+= $(FATFS_DIR)/option/unicode.c | ||||||
| 
 | 
 | ||||||
| ifeq ($(SRV_INTERNAL_STORAGE), 1) | # Little FS
 | ||||||
| LITTLEFS_DIR	= $(LIB_DIR)/littlefs | LITTLEFS_DIR	= $(LIB_DIR)/littlefs | ||||||
| CFLAGS			+= -I$(LITTLEFS_DIR) | CFLAGS			+= -I$(LITTLEFS_DIR) | ||||||
| C_SOURCES		+= $(LITTLEFS_DIR)/lfs.c | C_SOURCES		+= $(LITTLEFS_DIR)/lfs.c | ||||||
| C_SOURCES		+= $(LITTLEFS_DIR)/lfs_util.c | C_SOURCES		+= $(LITTLEFS_DIR)/lfs_util.c | ||||||
| endif |  | ||||||
| 
 | 
 | ||||||
| ifeq ($(APP_NFC), 1) | ifeq ($(APP_NFC), 1) | ||||||
| ST25RFAL002_DIR	= $(LIB_DIR)/ST25RFAL002 | ST25RFAL002_DIR	= $(LIB_DIR)/ST25RFAL002 | ||||||
| @ -100,6 +99,10 @@ C_SOURCES		+= $(wildcard $(LIB_DIR)/irda/*/*.c) | |||||||
| CFLAGS			+= -I$(LIB_DIR)/args | CFLAGS			+= -I$(LIB_DIR)/args | ||||||
| C_SOURCES		+= $(wildcard $(LIB_DIR)/args/*.c) | 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
 | #scened app template lib
 | ||||||
| CFLAGS			+= -I$(LIB_DIR)/app-scened-template | CFLAGS			+= -I$(LIB_DIR)/app-scened-template | ||||||
| CPP_SOURCES		+= $(wildcard $(LIB_DIR)/app-scened-template/*.cpp) | CPP_SOURCES		+= $(wildcard $(LIB_DIR)/app-scened-template/*.cpp) | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 あく
						あく