diff --git a/applications/subghz/scenes/subghz_scene_config.h b/applications/subghz/scenes/subghz_scene_config.h index 070097e7a24..7958764df2b 100644 --- a/applications/subghz/scenes/subghz_scene_config.h +++ b/applications/subghz/scenes/subghz_scene_config.h @@ -7,6 +7,10 @@ ADD_SCENE(subghz, save_success, SaveSuccess) ADD_SCENE(subghz, saved, Saved) ADD_SCENE(subghz, transmitter, Transmitter) ADD_SCENE(subghz, show_error, ShowError) +ADD_SCENE(subghz, show_only_rx, ShowOnlyRx) +ADD_SCENE(subghz, saved_menu, SavedMenu) +ADD_SCENE(subghz, delete, Delete) +ADD_SCENE(subghz, delete_success, DeleteSuccess) ADD_SCENE(subghz, test, Test) ADD_SCENE(subghz, test_static, TestStatic) ADD_SCENE(subghz, test_carrier, TestCarrier) diff --git a/applications/subghz/scenes/subghz_scene_delete.c b/applications/subghz/scenes/subghz_scene_delete.c new file mode 100644 index 00000000000..e97fd989544 --- /dev/null +++ b/applications/subghz/scenes/subghz_scene_delete.c @@ -0,0 +1,71 @@ +#include "../subghz_i.h" + +typedef enum { + SubGhzSceneDeleteInfoCustomEventDelete, +} SubGhzSceneDeleteInfoCustomEvent; + +void subghz_scene_delete_callback(GuiButtonType result, InputType type, void* context) { + furi_assert(context); + SubGhz* subghz = context; + if((result == GuiButtonTypeRight) && (type == InputTypeShort)) { + view_dispatcher_send_custom_event( + subghz->view_dispatcher, SubGhzSceneDeleteInfoCustomEventDelete); + } +} + +void subghz_scene_delete_on_enter(void* context) { + SubGhz* subghz = context; + + char buffer_str[16]; + snprintf( + buffer_str, + sizeof(buffer_str), + "%03ld.%02ld", + subghz->txrx->frequency / 1000000 % 1000, + subghz->txrx->frequency / 10000 % 100); + widget_add_string_element( + subghz->widget, 78, 0, AlignLeft, AlignTop, FontSecondary, buffer_str); + if(subghz->txrx->preset == FuriHalSubGhzPresetOok650Async || + subghz->txrx->preset == FuriHalSubGhzPresetOok270Async) { + snprintf(buffer_str, sizeof(buffer_str), "AM"); + } else if(subghz->txrx->preset == FuriHalSubGhzPreset2FSKAsync) { + snprintf(buffer_str, sizeof(buffer_str), "FM"); + } else { + furi_crash(NULL); + } + widget_add_string_element( + subghz->widget, 113, 0, AlignLeft, AlignTop, FontSecondary, buffer_str); + string_t text; + string_init(text); + subghz->txrx->protocol_result->to_string(subghz->txrx->protocol_result, text); + widget_add_string_multiline_element( + subghz->widget, 0, 0, AlignLeft, AlignTop, FontSecondary, string_get_cstr(text)); + string_clear(text); + + widget_add_button_element( + subghz->widget, GuiButtonTypeRight, "Delete", subghz_scene_delete_callback, subghz); + + view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewWidget); +} + +bool subghz_scene_delete_on_event(void* context, SceneManagerEvent event) { + SubGhz* subghz = context; + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == SubGhzSceneDeleteInfoCustomEventDelete) { + memcpy(subghz->file_name_tmp, subghz->file_name, strlen(subghz->file_name)); + if(subghz_delete_file(subghz)) { + scene_manager_next_scene(subghz->scene_manager, SubGhzSceneDeleteSuccess); + } else { + scene_manager_search_and_switch_to_previous_scene( + subghz->scene_manager, SubGhzSceneStart); + } + return true; + } + } + return false; +} + +void subghz_scene_delete_on_exit(void* context) { + SubGhz* subghz = context; + widget_clear(subghz->widget); +} diff --git a/applications/subghz/scenes/subghz_scene_delete_success.c b/applications/subghz/scenes/subghz_scene_delete_success.c new file mode 100644 index 00000000000..bfafb7e5719 --- /dev/null +++ b/applications/subghz/scenes/subghz_scene_delete_success.c @@ -0,0 +1,48 @@ +#include "../subghz_i.h" + +#define SCENE_DELETE_SUCCESS_CUSTOM_EVENT (0UL) + +void subghz_scene_delete_success_popup_callback(void* context) { + SubGhz* subghz = context; + view_dispatcher_send_custom_event(subghz->view_dispatcher, SCENE_DELETE_SUCCESS_CUSTOM_EVENT); +} + +void subghz_scene_delete_success_on_enter(void* context) { + SubGhz* subghz = context; + + // Setup view + Popup* popup = subghz->popup; + popup_set_icon(popup, 0, 2, &I_DolphinMafia_115x62); + popup_set_header(popup, "Deleted", 83, 19, AlignLeft, AlignBottom); + popup_set_timeout(popup, 1500); + popup_set_context(popup, subghz); + popup_set_callback(popup, subghz_scene_delete_success_popup_callback); + popup_enable_timeout(popup); + view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewPopup); +} + +bool subghz_scene_delete_success_on_event(void* context, SceneManagerEvent event) { + SubGhz* subghz = context; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == SCENE_DELETE_SUCCESS_CUSTOM_EVENT) { + return scene_manager_search_and_switch_to_previous_scene( + subghz->scene_manager, SubGhzSceneStart); + } + } + return false; +} + +void subghz_scene_delete_success_on_exit(void* context) { + SubGhz* subghz = context; + + // Clear view + Popup* popup = subghz->popup; + popup_set_header(popup, NULL, 0, 0, AlignCenter, AlignBottom); + popup_set_text(popup, NULL, 0, 0, AlignCenter, AlignTop); + popup_set_icon(popup, 0, 0, NULL); + popup_set_callback(popup, NULL); + popup_set_context(popup, NULL); + popup_set_timeout(popup, 0); + popup_disable_timeout(popup); +} diff --git a/applications/subghz/scenes/subghz_scene_receiver_info.c b/applications/subghz/scenes/subghz_scene_receiver_info.c index 7cea22b135b..f3a75e546e4 100644 --- a/applications/subghz/scenes/subghz_scene_receiver_info.c +++ b/applications/subghz/scenes/subghz_scene_receiver_info.c @@ -101,7 +101,6 @@ bool subghz_scene_receiver_info_on_event(void* context, SceneManagerEvent event) if(event.type == SceneManagerEventTypeCustom) { if(event.event == SubGhzSceneReceiverInfoCustomEventTxStart) { //CC1101 Stop RX -> Start TX - subghz->state_notifications = NOTIFICATION_TX_STATE; if(subghz->txrx->hopper_state != SubGhzHopperStateOFF) { subghz->txrx->hopper_state = SubGhzHopperStatePause; } @@ -112,7 +111,11 @@ bool subghz_scene_receiver_info_on_event(void* context, SceneManagerEvent event) return false; } if(subghz->txrx->txrx_state == SubGhzTxRxStateIdle) { - subghz_tx_start(subghz); + if(!subghz_tx_start(subghz)) { + scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowOnlyRx); + } else { + subghz->state_notifications = NOTIFICATION_TX_STATE; + } } return true; } else if(event.event == SubGhzSceneReceiverInfoCustomEventTxStop) { @@ -145,6 +148,7 @@ bool subghz_scene_receiver_info_on_event(void* context, SceneManagerEvent event) } if(subghz->txrx->protocol_result && subghz->txrx->protocol_result->to_save_string && strcmp(subghz->txrx->protocol_result->name, "KeeLoq")) { + subghz_file_name_clear(subghz); scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSaveName); } return true; diff --git a/applications/subghz/scenes/subghz_scene_save_name.c b/applications/subghz/scenes/subghz_scene_save_name.c index 1ebb5d3b54c..81255723a24 100644 --- a/applications/subghz/scenes/subghz_scene_save_name.c +++ b/applications/subghz/scenes/subghz_scene_save_name.c @@ -16,15 +16,19 @@ void subghz_scene_save_name_on_enter(void* context) { TextInput* text_input = subghz->text_input; bool dev_name_empty = false; - set_random_name(subghz->text_store, sizeof(subghz->text_store)); - dev_name_empty = true; + if(!strcmp(subghz->file_name, "")) { + set_random_name(subghz->file_name, sizeof(subghz->file_name)); + dev_name_empty = true; + } else { + memcpy(subghz->file_name_tmp, subghz->file_name, strlen(subghz->file_name)); + } text_input_set_header_text(text_input, "Name signal"); text_input_set_result_callback( text_input, subghz_scene_save_name_text_input_callback, subghz, - subghz->text_store, + subghz->file_name, 22, //Max len name dev_name_empty); view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewTextInput); @@ -35,8 +39,12 @@ bool subghz_scene_save_name_on_event(void* context, SceneManagerEvent event) { if(event.type == SceneManagerEventTypeCustom) { if(event.event == SCENE_SAVE_NAME_CUSTOM_EVENT) { - if(strcmp(subghz->text_store, "") && - subghz_save_protocol_to_file(subghz, subghz->text_store)) { + if(strcmp(subghz->file_name, "") && + subghz_save_protocol_to_file(subghz, subghz->file_name)) { + if(strcmp(subghz->file_name_tmp, "")) { + subghz_delete_file(subghz); + } + subghz_file_name_clear(subghz); scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSaveSuccess); return true; } else { diff --git a/applications/subghz/scenes/subghz_scene_saved.c b/applications/subghz/scenes/subghz_scene_saved.c index 4ec75b5a05a..68231ba6e94 100644 --- a/applications/subghz/scenes/subghz_scene_saved.c +++ b/applications/subghz/scenes/subghz_scene_saved.c @@ -4,7 +4,7 @@ void subghz_scene_saved_on_enter(void* context) { SubGhz* subghz = context; if(subghz_load_protocol_from_file(subghz)) { - scene_manager_next_scene(subghz->scene_manager, SubGhzSceneTransmitter); + scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSavedMenu); } else { scene_manager_search_and_switch_to_previous_scene(subghz->scene_manager, SubGhzSceneStart); } diff --git a/applications/subghz/scenes/subghz_scene_saved_menu.c b/applications/subghz/scenes/subghz_scene_saved_menu.c new file mode 100644 index 00000000000..59d7e3e8542 --- /dev/null +++ b/applications/subghz/scenes/subghz_scene_saved_menu.c @@ -0,0 +1,69 @@ +#include "../subghz_i.h" + +enum SubmenuIndex { + SubmenuIndexEmulate, + SubmenuIndexEdit, + SubmenuIndexDelete, +}; + +void subghz_scene_saved_menu_submenu_callback(void* context, uint32_t index) { + SubGhz* subghz = context; + view_dispatcher_send_custom_event(subghz->view_dispatcher, index); +} + +void subghz_scene_saved_menu_on_enter(void* context) { + SubGhz* subghz = context; + submenu_add_item( + subghz->submenu, + "Emulate", + SubmenuIndexEmulate, + subghz_scene_saved_menu_submenu_callback, + subghz); + submenu_add_item( + subghz->submenu, + "Edit name", + SubmenuIndexEdit, + subghz_scene_saved_menu_submenu_callback, + subghz); + submenu_add_item( + subghz->submenu, + "Delete", + SubmenuIndexDelete, + subghz_scene_saved_menu_submenu_callback, + subghz); + + submenu_set_selected_item( + subghz->submenu, + scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneSavedMenu)); + + view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewMenu); +} + +bool subghz_scene_saved_menu_on_event(void* context, SceneManagerEvent event) { + SubGhz* subghz = context; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == SubmenuIndexEmulate) { + scene_manager_set_scene_state( + subghz->scene_manager, SubGhzSceneSavedMenu, SubmenuIndexEmulate); + scene_manager_next_scene(subghz->scene_manager, SubGhzSceneTransmitter); + return true; + } else if(event.event == SubmenuIndexDelete) { + scene_manager_set_scene_state( + subghz->scene_manager, SubGhzSceneSavedMenu, SubmenuIndexDelete); + scene_manager_next_scene(subghz->scene_manager, SubGhzSceneDelete); + return true; + } else if(event.event == SubmenuIndexEdit) { + scene_manager_set_scene_state( + subghz->scene_manager, SubGhzSceneSavedMenu, SubmenuIndexEdit); + scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSaveName); + return true; + } + } + return false; +} + +void subghz_scene_saved_menu_on_exit(void* context) { + SubGhz* subghz = context; + submenu_clean(subghz->submenu); +} diff --git a/applications/subghz/scenes/subghz_scene_set_type.c b/applications/subghz/scenes/subghz_scene_set_type.c index 7beb8a27f94..4ec34e23eaf 100644 --- a/applications/subghz/scenes/subghz_scene_set_type.c +++ b/applications/subghz/scenes/subghz_scene_set_type.c @@ -174,6 +174,7 @@ bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) { if(generated_protocol) { subghz->txrx->frequency = subghz_frequencies[subghz_frequencies_433_92]; subghz->txrx->preset = FuriHalSubGhzPresetOok650Async; + subghz_file_name_clear(subghz); scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSaveName); return true; } diff --git a/applications/subghz/scenes/subghz_scene_show_only_rx.c b/applications/subghz/scenes/subghz_scene_show_only_rx.c new file mode 100644 index 00000000000..f808b0fb195 --- /dev/null +++ b/applications/subghz/scenes/subghz_scene_show_only_rx.c @@ -0,0 +1,53 @@ +#include "../subghz_i.h" + +#define SCENE_NO_MAN_CUSTOM_EVENT (11UL) + +void subghz_scene_show_only_rx_popup_callback(void* context) { + SubGhz* subghz = context; + view_dispatcher_send_custom_event(subghz->view_dispatcher, SCENE_NO_MAN_CUSTOM_EVENT); +} + +const void subghz_scene_show_only_rx_on_enter(void* context) { + SubGhz* subghz = context; + + // Setup view + Popup* popup = subghz->popup; + popup_set_icon(popup, 67, 12, &I_DolphinFirstStart7_61x51); + popup_set_text( + popup, + "This frequency can\nonly be used for RX\nin your region", + 38, + 40, + AlignCenter, + AlignBottom); + popup_set_timeout(popup, 1500); + popup_set_context(popup, subghz); + popup_set_callback(popup, subghz_scene_show_only_rx_popup_callback); + popup_enable_timeout(popup); + view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewPopup); +} + +const bool subghz_scene_show_only_rx_on_event(void* context, SceneManagerEvent event) { + SubGhz* subghz = context; + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == SCENE_NO_MAN_CUSTOM_EVENT) { + scene_manager_previous_scene(subghz->scene_manager); + return true; + } + } + return false; +} + +const void subghz_scene_show_only_rx_on_exit(void* context) { + SubGhz* subghz = context; + + // Clear view + Popup* popup = subghz->popup; + popup_set_header(popup, NULL, 0, 0, AlignCenter, AlignBottom); + popup_set_text(popup, NULL, 0, 0, AlignCenter, AlignTop); + popup_set_icon(popup, 0, 0, NULL); + popup_set_callback(popup, NULL); + popup_set_context(popup, NULL); + popup_set_timeout(popup, 0); + popup_disable_timeout(popup); +} diff --git a/applications/subghz/scenes/subghz_scene_test_carrier.c b/applications/subghz/scenes/subghz_scene_test_carrier.c index ecc42260e96..e2fa6634ecf 100644 --- a/applications/subghz/scenes/subghz_scene_test_carrier.c +++ b/applications/subghz/scenes/subghz_scene_test_carrier.c @@ -1,12 +1,27 @@ #include "../subghz_i.h" +#include "../views/subghz_test_carrier.h" + +void subghz_scene_test_carrier_callback(SubghzTestCarrierEvent event, void* context) { + furi_assert(context); + SubGhz* subghz = context; + view_dispatcher_send_custom_event(subghz->view_dispatcher, event); +} void subghz_scene_test_carrier_on_enter(void* context) { SubGhz* subghz = context; + subghz_test_carrier_set_callback( + subghz->subghz_test_carrier, subghz_scene_test_carrier_callback, subghz); view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewTestCarrier); } bool subghz_scene_test_carrier_on_event(void* context, SceneManagerEvent event) { - // SubGhz* subghz = context; + SubGhz* subghz = context; + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == SubghzTestCarrierEventOnlyRx) { + scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowOnlyRx); + return true; + } + } return false; } diff --git a/applications/subghz/scenes/subghz_scene_test_packet.c b/applications/subghz/scenes/subghz_scene_test_packet.c index e4933babef6..a49ec480420 100644 --- a/applications/subghz/scenes/subghz_scene_test_packet.c +++ b/applications/subghz/scenes/subghz_scene_test_packet.c @@ -1,12 +1,27 @@ #include "../subghz_i.h" +#include "../views/subghz_test_packet.h" + +void subghz_scene_test_packet_callback(SubghzTestPacketEvent event, void* context) { + furi_assert(context); + SubGhz* subghz = context; + view_dispatcher_send_custom_event(subghz->view_dispatcher, event); +} void subghz_scene_test_packet_on_enter(void* context) { SubGhz* subghz = context; + subghz_test_packet_set_callback( + subghz->subghz_test_packet, subghz_scene_test_packet_callback, subghz); view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewTestPacket); } bool subghz_scene_test_packet_on_event(void* context, SceneManagerEvent event) { - // SubGhz* subghz = context; + SubGhz* subghz = context; + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == SubghzTestPacketEventOnlyRx) { + scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowOnlyRx); + return true; + } + } return false; } diff --git a/applications/subghz/scenes/subghz_scene_test_static.c b/applications/subghz/scenes/subghz_scene_test_static.c index f663c7fd370..6c6099ec960 100644 --- a/applications/subghz/scenes/subghz_scene_test_static.c +++ b/applications/subghz/scenes/subghz_scene_test_static.c @@ -1,12 +1,27 @@ #include "../subghz_i.h" +#include "../views/subghz_test_static.h" + +void subghz_scene_test_static_callback(SubghzTestStaticEvent event, void* context) { + furi_assert(context); + SubGhz* subghz = context; + view_dispatcher_send_custom_event(subghz->view_dispatcher, event); +} void subghz_scene_test_static_on_enter(void* context) { SubGhz* subghz = context; + subghz_test_static_set_callback( + subghz->subghz_test_static, subghz_scene_test_static_callback, subghz); view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewStatic); } bool subghz_scene_test_static_on_event(void* context, SceneManagerEvent event) { - // SubGhz* subghz = context; + SubGhz* subghz = context; + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == SubghzTestStaticEventOnlyRx) { + scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowOnlyRx); + return true; + } + } return false; } diff --git a/applications/subghz/scenes/subghz_scene_transmitter.c b/applications/subghz/scenes/subghz_scene_transmitter.c index dd1f30305ff..85c61240e87 100644 --- a/applications/subghz/scenes/subghz_scene_transmitter.c +++ b/applications/subghz/scenes/subghz_scene_transmitter.c @@ -68,14 +68,18 @@ bool subghz_scene_transmitter_on_event(void* context, SceneManagerEvent event) { SubGhz* subghz = context; if(event.type == SceneManagerEventTypeCustom) { if(event.event == SubghzTransmitterEventSendStart) { - subghz->state_notifications = NOTIFICATION_TX_STATE; + subghz->state_notifications = NOTIFICATION_IDLE_STATE; if(subghz->txrx->txrx_state == SubGhzTxRxStateRx) { subghz_rx_end(subghz); } if((subghz->txrx->txrx_state == SubGhzTxRxStateIdle) || (subghz->txrx->txrx_state == SubGhzTxRxStateSleep)) { - subghz_tx_start(subghz); - subghz_scene_transmitter_update_data_show(subghz); + if(!subghz_tx_start(subghz)) { + scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowOnlyRx); + } else { + subghz->state_notifications = NOTIFICATION_TX_STATE; + subghz_scene_transmitter_update_data_show(subghz); + } } return true; } else if(event.event == SubghzTransmitterEventSendStop) { diff --git a/applications/subghz/subghz_cli.c b/applications/subghz/subghz_cli.c index 52e6b6d3814..1a33beab8c8 100644 --- a/applications/subghz/subghz_cli.c +++ b/applications/subghz/subghz_cli.c @@ -48,12 +48,14 @@ void subghz_cli_command_tx_carrier(Cli* cli, string_t args, void* context) { hal_gpio_init(&gpio_cc1101_g0, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); hal_gpio_write(&gpio_cc1101_g0, true); - furi_hal_subghz_tx(); - - printf("Transmitting at frequency %lu Hz\r\n", frequency); - printf("Press CTRL+C to stop\r\n"); - while(!cli_cmd_interrupt_received(cli)) { - osDelay(250); + if(furi_hal_subghz_tx()) { + printf("Transmitting at frequency %lu Hz\r\n", frequency); + printf("Press CTRL+C to stop\r\n"); + while(!cli_cmd_interrupt_received(cli)) { + osDelay(250); + } + } else { + printf("This frequency can only be used for RX in your region\r\n"); } furi_hal_subghz_set_path(FuriHalSubGhzPathIsolate); diff --git a/applications/subghz/subghz_i.c b/applications/subghz/subghz_i.c index 9a52586a4ed..2f849e41017 100644 --- a/applications/subghz/subghz_i.c +++ b/applications/subghz/subghz_i.c @@ -40,19 +40,19 @@ uint32_t subghz_rx(SubGhz* subghz, uint32_t frequency) { return value; } -uint32_t subghz_tx(SubGhz* subghz, uint32_t frequency) { +static bool subghz_tx(SubGhz* subghz, uint32_t frequency) { furi_assert(subghz); if(!furi_hal_subghz_is_frequency_valid(frequency)) { furi_crash(NULL); } furi_assert(subghz->txrx->txrx_state != SubGhzTxRxStateSleep); furi_hal_subghz_idle(); - uint32_t value = furi_hal_subghz_set_frequency_and_path(frequency); + furi_hal_subghz_set_frequency_and_path(frequency); hal_gpio_init(&gpio_cc1101_g0, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); hal_gpio_write(&gpio_cc1101_g0, true); - furi_hal_subghz_tx(); + bool ret = furi_hal_subghz_tx(); subghz->txrx->txrx_state = SubGhzTxRxStateTx; - return value; + return ret; } void subghz_idle(SubGhz* subghz) { @@ -90,9 +90,10 @@ static void subghz_frequency_preset_to_str(SubGhz* subghz, string_t output) { (int)subghz->txrx->preset); } -void subghz_tx_start(SubGhz* subghz) { +bool subghz_tx_start(SubGhz* subghz) { furi_assert(subghz); + bool ret = false; subghz->txrx->encoder = subghz_protocol_encoder_common_alloc(); subghz->txrx->encoder->repeat = 200; //max repeat with the button held down //get upload @@ -105,16 +106,23 @@ void subghz_tx_start(SubGhz* subghz) { subghz_begin(subghz, FuriHalSubGhzPresetOok270Async); } if(subghz->txrx->frequency) { - subghz_tx(subghz, subghz->txrx->frequency); + ret = subghz_tx(subghz, subghz->txrx->frequency); } else { - subghz_tx(subghz, 433920000); + ret = subghz_tx(subghz, 433920000); } - //Start TX - furi_hal_subghz_start_async_tx( - subghz_protocol_encoder_common_yield, subghz->txrx->encoder); + if(ret) { + //Start TX + furi_hal_subghz_start_async_tx( + subghz_protocol_encoder_common_yield, subghz->txrx->encoder); + } } } + if(!ret) { + subghz_protocol_encoder_common_free(subghz->txrx->encoder); + subghz_idle(subghz); + } + return ret; } void subghz_tx_stop(SubGhz* subghz) { @@ -125,8 +133,9 @@ void subghz_tx_stop(SubGhz* subghz) { subghz_protocol_encoder_common_free(subghz->txrx->encoder); subghz_idle(subghz); //if protocol dynamic then we save the last upload - if(subghz->txrx->protocol_result->type_protocol == SubGhzProtocolCommonTypeDynamic) { - subghz_save_protocol_to_file(subghz, subghz->text_store); + if((subghz->txrx->protocol_result->type_protocol == SubGhzProtocolCommonTypeDynamic) && + (strcmp(subghz->file_name, ""))) { + subghz_save_protocol_to_file(subghz, subghz->file_name); } notification_message(subghz->notifications, &sequence_reset_red); } @@ -268,8 +277,8 @@ bool subghz_load_protocol_from_file(SubGhz* subghz) { file_worker, SUBGHZ_APP_PATH_FOLDER, SUBGHZ_APP_EXTENSION, - subghz->text_store, - sizeof(subghz->text_store), + subghz->file_name, + sizeof(subghz->file_name), NULL); if(res) { @@ -278,7 +287,7 @@ bool subghz_load_protocol_from_file(SubGhz* subghz) { protocol_file_name, "%s/%s%s", SUBGHZ_APP_PATH_FOLDER, - subghz->text_store, + subghz->file_name, SUBGHZ_APP_EXTENSION); } else { string_clear(temp_str); @@ -292,7 +301,7 @@ bool subghz_load_protocol_from_file(SubGhz* subghz) { do { if(!file_worker_open( file_worker, string_get_cstr(protocol_file_name), FSAM_READ, FSOM_OPEN_EXISTING)) { - break; + return res; } // Read and parse frequency from 1st line if(!file_worker_read_until(file_worker, temp_str, '\n')) { @@ -345,6 +354,40 @@ bool subghz_load_protocol_from_file(SubGhz* subghz) { return res; } +bool subghz_delete_file(SubGhz* subghz) { + furi_assert(subghz); + + bool result = true; + FileWorker* file_worker = file_worker_alloc(false); + string_t file_path; + + do { + // Get key file path + string_init_printf( + file_path, + "%s/%s%s", + SUBGHZ_APP_PATH_FOLDER, + subghz->file_name_tmp, + SUBGHZ_APP_EXTENSION); + // Delete original file + if(!file_worker_remove(file_worker, string_get_cstr(file_path))) { + result = false; + break; + } + } while(0); + + string_clear(file_path); + file_worker_close(file_worker); + file_worker_free(file_worker); + return result; +} + +void subghz_file_name_clear(SubGhz* subghz) { + furi_assert(subghz); + memset(subghz->file_name, 0, sizeof(subghz->file_name)); + memset(subghz->file_name_tmp, 0, sizeof(subghz->file_name_tmp)); +} + uint32_t subghz_random_serial(void) { static bool rand_generator_inited = false; diff --git a/applications/subghz/subghz_i.h b/applications/subghz/subghz_i.h index eecf98d3bff..dc1e5a3f92b 100644 --- a/applications/subghz/subghz_i.h +++ b/applications/subghz/subghz_i.h @@ -29,7 +29,7 @@ #include -#define SUBGHZ_TEXT_STORE_SIZE 128 +#define SUBGHZ_TEXT_STORE_SIZE 40 #define NOTIFICATION_STARTING_STATE 0u #define NOTIFICATION_IDLE_STATE 1u @@ -90,7 +90,8 @@ struct SubGhz { Popup* popup; TextInput* text_input; Widget* widget; - char text_store[SUBGHZ_TEXT_STORE_SIZE + 1]; + char file_name[SUBGHZ_TEXT_STORE_SIZE + 1]; + char file_name_tmp[SUBGHZ_TEXT_STORE_SIZE + 1]; uint8_t state_notifications; SubghzReceiver* subghz_receiver; @@ -121,10 +122,12 @@ void subghz_begin(SubGhz* subghz, FuriHalSubGhzPreset preset); uint32_t subghz_rx(SubGhz* subghz, uint32_t frequency); void subghz_rx_end(SubGhz* subghz); void subghz_sleep(SubGhz* subghz); -void subghz_tx_start(SubGhz* subghz); +bool subghz_tx_start(SubGhz* subghz); void subghz_tx_stop(SubGhz* subghz); bool subghz_key_load(SubGhz* subghz, const char* file_path); bool subghz_save_protocol_to_file(SubGhz* subghz, const char* dev_name); bool subghz_load_protocol_from_file(SubGhz* subghz); +bool subghz_delete_file(SubGhz* subghz); +void subghz_file_name_clear(SubGhz* subghz); uint32_t subghz_random_serial(void); void subghz_hopper_update(SubGhz* subghz); diff --git a/applications/subghz/views/subghz_test_carrier.c b/applications/subghz/views/subghz_test_carrier.c index 82d11650bdb..1217240ebb6 100644 --- a/applications/subghz/views/subghz_test_carrier.c +++ b/applications/subghz/views/subghz_test_carrier.c @@ -9,6 +9,8 @@ struct SubghzTestCarrier { View* view; osTimerId timer; + SubghzTestCarrierCallback callback; + void* context; }; typedef enum { @@ -24,6 +26,16 @@ typedef struct { SubghzTestCarrierModelStatus status; } SubghzTestCarrierModel; +void subghz_test_carrier_set_callback( + SubghzTestCarrier* subghz_test_carrier, + SubghzTestCarrierCallback callback, + void* context) { + furi_assert(subghz_test_carrier); + furi_assert(callback); + subghz_test_carrier->callback = callback; + subghz_test_carrier->context = context; +} + void subghz_test_carrier_draw(Canvas* canvas, SubghzTestCarrierModel* model) { char buffer[64]; @@ -105,7 +117,11 @@ bool subghz_test_carrier_input(InputEvent* event, void* context) { } else { hal_gpio_init(&gpio_cc1101_g0, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); hal_gpio_write(&gpio_cc1101_g0, true); - furi_hal_subghz_tx(); + if(!furi_hal_subghz_tx()) { + hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullNo, GpioSpeedLow); + subghz_test_carrier->callback( + SubghzTestCarrierEventOnlyRx, subghz_test_carrier->context); + } } return true; diff --git a/applications/subghz/views/subghz_test_carrier.h b/applications/subghz/views/subghz_test_carrier.h index f191f7af8e7..b452234581b 100644 --- a/applications/subghz/views/subghz_test_carrier.h +++ b/applications/subghz/views/subghz_test_carrier.h @@ -2,8 +2,19 @@ #include +typedef enum { + SubghzTestCarrierEventOnlyRx, +} SubghzTestCarrierEvent; + typedef struct SubghzTestCarrier SubghzTestCarrier; +typedef void (*SubghzTestCarrierCallback)(SubghzTestCarrierEvent event, void* context); + +void subghz_test_carrier_set_callback( + SubghzTestCarrier* subghz_test_carrier, + SubghzTestCarrierCallback callback, + void* context); + SubghzTestCarrier* subghz_test_carrier_alloc(); void subghz_test_carrier_free(SubghzTestCarrier* subghz_test_carrier); diff --git a/applications/subghz/views/subghz_test_packet.c b/applications/subghz/views/subghz_test_packet.c index f1c481795a3..2d4b4aea768 100644 --- a/applications/subghz/views/subghz_test_packet.c +++ b/applications/subghz/views/subghz_test_packet.c @@ -16,12 +16,14 @@ struct SubghzTestPacket { SubGhzDecoderPrinceton* decoder; SubGhzEncoderPrinceton* encoder; - volatile size_t packet_rx; + SubghzTestPacketCallback callback; + void* context; }; typedef enum { SubghzTestPacketModelStatusRx, + SubghzTestPacketModelStatusOnlyRx, SubghzTestPacketModelStatusTx, } SubghzTestPacketModelStatus; @@ -36,6 +38,16 @@ typedef struct { volatile bool subghz_test_packet_overrun = false; +void subghz_test_packet_set_callback( + SubghzTestPacket* subghz_test_packet, + SubghzTestPacketCallback callback, + void* context) { + furi_assert(subghz_test_packet); + furi_assert(callback); + subghz_test_packet->callback = callback; + subghz_test_packet->context = context; +} + static void subghz_test_packet_rx_callback(bool level, uint32_t duration, void* context) { furi_assert(context); SubghzTestPacket* instance = context; @@ -57,7 +69,7 @@ static void subghz_test_packet_rssi_timer_callback(void* context) { if(model->status == SubghzTestPacketModelStatusRx) { model->rssi = furi_hal_subghz_get_rssi(); model->packets = instance->packet_rx; - } else { + } else if(model->status == SubghzTestPacketModelStatusTx) { model->packets = SUBGHZ_TEST_PACKET_COUNT - subghz_encoder_princeton_get_repeat_left(instance->encoder); } @@ -124,7 +136,7 @@ static bool subghz_test_packet_input(InputEvent* event, void* context) { instance->view, (SubghzTestPacketModel * model) { if(model->status == SubghzTestPacketModelStatusRx) { furi_hal_subghz_stop_async_rx(); - } else { + } else if(model->status == SubghzTestPacketModelStatusTx) { furi_hal_subghz_stop_async_tx(); } @@ -137,10 +149,10 @@ static bool subghz_test_packet_input(InputEvent* event, void* context) { } else if(event->key == InputKeyUp) { if(model->path < FuriHalSubGhzPath868) model->path++; } else if(event->key == InputKeyOk) { - if(model->status == SubghzTestPacketModelStatusTx) { - model->status = SubghzTestPacketModelStatusRx; - } else { + if(model->status == SubghzTestPacketModelStatusRx) { model->status = SubghzTestPacketModelStatusTx; + } else { + model->status = SubghzTestPacketModelStatusRx; } } @@ -151,8 +163,13 @@ static bool subghz_test_packet_input(InputEvent* event, void* context) { if(model->status == SubghzTestPacketModelStatusRx) { furi_hal_subghz_start_async_rx(subghz_test_packet_rx_callback, instance); } else { - subghz_encoder_princeton_set(instance->encoder, 0x00AABBCC, 1000); - furi_hal_subghz_start_async_tx(subghz_encoder_princeton_yield, instance->encoder); + subghz_encoder_princeton_set( + instance->encoder, 0x00AABBCC, SUBGHZ_TEST_PACKET_COUNT); + if(!furi_hal_subghz_start_async_tx( + subghz_encoder_princeton_yield, instance->encoder)) { + model->status = SubghzTestPacketModelStatusOnlyRx; + instance->callback(SubghzTestPacketEventOnlyRx, instance->context); + } } return true; @@ -195,7 +212,7 @@ void subghz_test_packet_exit(void* context) { instance->view, (SubghzTestPacketModel * model) { if(model->status == SubghzTestPacketModelStatusRx) { furi_hal_subghz_stop_async_rx(); - } else { + } else if(model->status == SubghzTestPacketModelStatusTx) { furi_hal_subghz_stop_async_tx(); } return true; @@ -240,4 +257,4 @@ void subghz_test_packet_free(SubghzTestPacket* instance) { View* subghz_test_packet_get_view(SubghzTestPacket* instance) { furi_assert(instance); return instance->view; -} +} \ No newline at end of file diff --git a/applications/subghz/views/subghz_test_packet.h b/applications/subghz/views/subghz_test_packet.h index 00fae10ee73..bd861ee9979 100644 --- a/applications/subghz/views/subghz_test_packet.h +++ b/applications/subghz/views/subghz_test_packet.h @@ -2,8 +2,19 @@ #include +typedef enum { + SubghzTestPacketEventOnlyRx, +} SubghzTestPacketEvent; + typedef struct SubghzTestPacket SubghzTestPacket; +typedef void (*SubghzTestPacketCallback)(SubghzTestPacketEvent event, void* context); + +void subghz_test_packet_set_callback( + SubghzTestPacket* subghz_test_packet, + SubghzTestPacketCallback callback, + void* context); + SubghzTestPacket* subghz_test_packet_alloc(); void subghz_test_packet_free(SubghzTestPacket* subghz_test_packet); diff --git a/applications/subghz/views/subghz_test_static.c b/applications/subghz/views/subghz_test_static.c index 240dc215be6..aa701464910 100644 --- a/applications/subghz/views/subghz_test_static.c +++ b/applications/subghz/views/subghz_test_static.c @@ -8,6 +8,11 @@ #include #include +typedef enum { + SubghzTestStaticStatusIDLE, + SubghzTestStaticStatusTX, +} SubghzTestStaticStatus; + static const uint32_t subghz_test_static_keys[] = { 0x0074BADE, 0x0074BADD, @@ -17,20 +22,28 @@ static const uint32_t subghz_test_static_keys[] = { struct SubghzTestStatic { View* view; + SubghzTestStaticStatus satus_tx; SubGhzEncoderPrinceton* encoder; + SubghzTestStaticCallback callback; + void* context; }; -typedef enum { - SubghzTestStaticStatusRx, - SubghzTestStaticStatusTx, -} SubghzTestStaticStatus; - typedef struct { uint8_t frequency; uint32_t real_frequency; uint8_t button; } SubghzTestStaticModel; +void subghz_test_static_set_callback( + SubghzTestStatic* subghz_test_static, + SubghzTestStaticCallback callback, + void* context) { + furi_assert(subghz_test_static); + furi_assert(callback); + subghz_test_static->callback = callback; + subghz_test_static->context = context; +} + void subghz_test_static_draw(Canvas* canvas, SubghzTestStaticModel* model) { char buffer[64]; @@ -79,22 +92,30 @@ bool subghz_test_static_input(InputEvent* event, void* context) { if(event->key == InputKeyOk) { NotificationApp* notification = furi_record_open("notification"); if(event->type == InputTypePress) { - notification_message_block(notification, &sequence_set_red_255); - - FURI_LOG_I("SubghzTestStatic", "TX Start"); furi_hal_subghz_idle(); furi_hal_subghz_set_frequency_and_path(subghz_frequencies[model->frequency]); + if(!furi_hal_subghz_tx()) { + instance->callback(SubghzTestStaticEventOnlyRx, instance->context); + } else { + notification_message_block(notification, &sequence_set_red_255); - subghz_encoder_princeton_set( - instance->encoder, subghz_test_static_keys[model->button], 10000); + FURI_LOG_I("SubghzTestStatic", "TX Start"); - furi_hal_subghz_start_async_tx( - subghz_encoder_princeton_yield, instance->encoder); - } else if(event->type == InputTypeRelease) { - FURI_LOG_I("SubghzTestStatic", "TX Stop"); - furi_hal_subghz_stop_async_tx(); + subghz_encoder_princeton_set( + instance->encoder, subghz_test_static_keys[model->button], 10000); - notification_message(notification, &sequence_reset_red); + furi_hal_subghz_start_async_tx( + subghz_encoder_princeton_yield, instance->encoder); + instance->satus_tx = SubghzTestStaticStatusTX; + } + } else if(event->type == InputTypeRelease) { + if(instance->satus_tx == SubghzTestStaticStatusTX) { + FURI_LOG_I("SubghzTestStatic", "TX Stop"); + subghz_encoder_princeton_print_log(instance->encoder); + furi_hal_subghz_stop_async_tx(); + notification_message(notification, &sequence_reset_red); + } + instance->satus_tx = SubghzTestStaticStatusIDLE; } furi_record_close("notification"); } @@ -114,12 +135,14 @@ void subghz_test_static_enter(void* context) { hal_gpio_init(&gpio_cc1101_g0, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); hal_gpio_write(&gpio_cc1101_g0, false); + instance->satus_tx = SubghzTestStaticStatusIDLE; with_view_model( instance->view, (SubghzTestStaticModel * model) { model->frequency = subghz_frequencies_433_92; model->real_frequency = subghz_frequencies[model->frequency]; model->button = 0; + return true; }); } @@ -156,4 +179,4 @@ void subghz_test_static_free(SubghzTestStatic* instance) { View* subghz_test_static_get_view(SubghzTestStatic* instance) { furi_assert(instance); return instance->view; -} +} \ No newline at end of file diff --git a/applications/subghz/views/subghz_test_static.h b/applications/subghz/views/subghz_test_static.h index ddcd83157c7..48346d8a737 100644 --- a/applications/subghz/views/subghz_test_static.h +++ b/applications/subghz/views/subghz_test_static.h @@ -2,8 +2,19 @@ #include +typedef enum { + SubghzTestStaticEventOnlyRx, +} SubghzTestStaticEvent; + typedef struct SubghzTestStatic SubghzTestStatic; +typedef void (*SubghzTestStaticCallback)(SubghzTestStaticEvent event, void* context); + +void subghz_test_static_set_callback( + SubghzTestStatic* subghz_test_static, + SubghzTestStaticCallback callback, + void* context); + SubghzTestStatic* subghz_test_static_alloc(); void subghz_test_static_free(SubghzTestStatic* subghz_static); diff --git a/firmware/targets/f6/furi-hal/furi-hal-subghz.c b/firmware/targets/f6/furi-hal/furi-hal-subghz.c index 4d1f0cbf799..34446d8da84 100644 --- a/firmware/targets/f6/furi-hal/furi-hal-subghz.c +++ b/firmware/targets/f6/furi-hal/furi-hal-subghz.c @@ -1,4 +1,5 @@ #include "furi-hal-subghz.h" +#include "furi-hal-version.h" #include #include @@ -10,6 +11,7 @@ #include static volatile SubGhzState furi_hal_subghz_state = SubGhzStateInit; +static volatile SubGhzRegulation furi_hal_subghz_regulation = SubGhzRegulationTxRx; static const uint8_t furi_hal_subghz_preset_ook_270khz_async_regs[][2] = { // https://e2e.ti.com/support/wireless-connectivity/sub-1-ghz-group/sub-1-ghz/f/sub-1-ghz-forum/382066/cc1101---don-t-know-the-correct-registers-configuration @@ -41,11 +43,11 @@ static const uint8_t furi_hal_subghz_preset_ook_270khz_async_regs[][2] = { 0x18}, // no frequency offset compensation, POST_K same as PRE_K, PRE_K is 4K, GATE is off /* Automatic Gain Control */ - {CC1101_AGCTRL0, + {CC1101_AGCCTRL0, 0x40}, // 01 - Low hysteresis, small asymmetric dead zone, medium gain; 00 - 8 samples agc; 00 - Normal AGC, 00 - 4dB boundary - {CC1101_AGCTRL1, + {CC1101_AGCCTRL1, 0x00}, // 0; 0 - LNA 2 gain is decreased to minimum before decreasing LNA gain; 00 - Relative carrier sense threshold disabled; 0000 - RSSI to MAIN_TARGET - {CC1101_AGCTRL2, 0x03}, // 00 - DVGA all; 000 - MAX LNA+LNA2; 011 - MAIN_TARGET 24 dB + {CC1101_AGCCTRL2, 0x03}, // 00 - DVGA all; 000 - MAX LNA+LNA2; 011 - MAIN_TARGET 24 dB /* Wake on radio and timeouts control */ {CC1101_WORCTRL, 0xFB}, // WOR_RES is 2^15 periods (0.91 - 0.94 s) 16.5 - 17.2 hours @@ -99,11 +101,15 @@ static const uint8_t furi_hal_subghz_preset_ook_650khz_async_regs[][2] = { 0x18}, // no frequency offset compensation, POST_K same as PRE_K, PRE_K is 4K, GATE is off /* Automatic Gain Control */ - {CC1101_AGCTRL0, - 0x40}, // 01 - Low hysteresis, small asymmetric dead zone, medium gain; 00 - 8 samples agc; 00 - Normal AGC, 00 - 4dB boundary - {CC1101_AGCTRL1, - 0x00}, // 0; 0 - LNA 2 gain is decreased to minimum before decreasing LNA gain; 00 - Relative carrier sense threshold disabled; 0000 - RSSI to MAIN_TARGET - {CC1101_AGCTRL2, 0x03}, // 00 - DVGA all; 000 - MAX LNA+LNA2; 011 - MAIN_TARGET 24 dB + // {CC1101_AGCTRL0,0x40}, // 01 - Low hysteresis, small asymmetric dead zone, medium gain; 00 - 8 samples agc; 00 - Normal AGC, 00 - 4dB boundary + // {CC1101_AGCTRL1,0x00}, // 0; 0 - LNA 2 gain is decreased to minimum before decreasing LNA gain; 00 - Relative carrier sense threshold disabled; 0000 - RSSI to MAIN_TARGET + // {CC1101_AGCCTRL2, 0x03}, // 00 - DVGA all; 000 - MAX LNA+LNA2; 011 - MAIN_TARGET 24 dB + //MAGN_TARGET for RX filter BW =< 100 kHz is 0x3. For higher RX filter BW's MAGN_TARGET is 0x7. + {CC1101_AGCCTRL0, + 0x91}, // 10 - Medium hysteresis, medium asymmetric dead zone, medium gain ; 01 - 16 samples agc; 00 - Normal AGC, 01 - 8dB boundary + {CC1101_AGCCTRL1, + 0x0}, // 0; 0 - LNA 2 gain is decreased to minimum before decreasing LNA gain; 00 - Relative carrier sense threshold disabled; 0000 - RSSI to MAIN_TARGET + {CC1101_AGCCTRL2, 0x07}, // 00 - DVGA all; 000 - MAX LNA+LNA2; 111 - MAIN_TARGET 42 dB /* Wake on radio and timeouts control */ {CC1101_WORCTRL, 0xFB}, // WOR_RES is 2^15 periods (0.91 - 0.94 s) 16.5 - 17.2 hours @@ -131,23 +137,21 @@ static const uint8_t furi_hal_subghz_preset_2fsk_async_regs[][2] = { /* GPIO GD0 */ {CC1101_IOCFG0, 0x0D}, // GD0 as async serial data output/input - /* FIFO and internals */ - {CC1101_FIFOTHR, 0x47}, // The only important bit is ADC_RETENTION + /* Frequency Synthesizer Control */ + {CC1101_FSCTRL1, 0x06}, // IF = (26*10^6) / (2^10) * 0x06 = 152343.75Hz /* Packet engine */ {CC1101_PKTCTRL0, 0x32}, // Async, continious, no whitening + {CC1101_PKTCTRL1, 0x04}, - /* Frequency Synthesizer Control */ - {CC1101_FSCTRL1, 0x06}, // IF = (26*10^6) / (2^10) * 0x06 = 152343.75Hz - - // Modem Configuration + // // Modem Configuration {CC1101_MDMCFG0, 0x00}, - {CC1101_MDMCFG1, 0x02}, - {CC1101_MDMCFG2, 0x04}, // Format 2-FSK/FM, No preamble/sync, Disable (current optimized) - {CC1101_MDMCFG3, 0x8B}, // Data rate is 19.5885 kBaud - {CC1101_MDMCFG4, 0x69}, // Rx BW filter is 270.833333 kHz - - {CC1101_DEVIATN, 0x47}, //Deviation 47.607422 khz + {CC1101_MDMCFG1, 0x2}, + {CC1101_MDMCFG2, 0x4}, // Format 2-FSK/FM, No preamble/sync, Disable (current optimized) + {CC1101_MDMCFG3, 0x83}, // Data rate is 4.79794 kBaud + {CC1101_MDMCFG4, 0x67}, //Rx BW filter is 270.833333 kHz + //{ CC1101_DEVIATN, 0x14 }, //Deviation 4.760742 kHz + {CC1101_DEVIATN, 0x04}, //Deviation 2.380371 kHz /* Main Radio Control State Machine */ {CC1101_MCSM0, 0x18}, // Autocalibrate on idle-to-rx/tx, PO_TIMEOUT is 64 cycles(149-155us) @@ -157,18 +161,18 @@ static const uint8_t furi_hal_subghz_preset_2fsk_async_regs[][2] = { 0x16}, // no frequency offset compensation, POST_K same as PRE_K, PRE_K is 4K, GATE is off /* Automatic Gain Control */ - {CC1101_AGCTRL0, - 0x40}, // 01 - Low hysteresis, small asymmetric dead zone, medium gain; 00 - 8 samples agc; 00 - Normal AGC, 00 - 4dB boundary - {CC1101_AGCTRL1, + {CC1101_AGCCTRL0, + 0x91}, //10 - Medium hysteresis, medium asymmetric dead zone, medium gain ; 01 - 16 samples agc; 00 - Normal AGC, 01 - 8dB boundary + {CC1101_AGCCTRL1, 0x00}, // 0; 0 - LNA 2 gain is decreased to minimum before decreasing LNA gain; 00 - Relative carrier sense threshold disabled; 0000 - RSSI to MAIN_TARGET - {CC1101_AGCTRL2, 0x03}, // 00 - DVGA all; 000 - MAX LNA+LNA2; 011 - MAIN_TARGET 24 dB + {CC1101_AGCCTRL2, 0x07}, // 00 - DVGA all; 000 - MAX LNA+LNA2; 111 - MAIN_TARGET 42 dB /* Wake on radio and timeouts control */ {CC1101_WORCTRL, 0xFB}, // WOR_RES is 2^15 periods (0.91 - 0.94 s) 16.5 - 17.2 hours /* Frontend configuration */ {CC1101_FREND0, 0x10}, // Adjusts current TX LO buffer - {CC1101_FREND1, 0xB6}, // + {CC1101_FREND1, 0x56}, /* Frequency Synthesizer Calibration, valid for 433.92 */ {CC1101_FSCAL3, 0xE9}, @@ -281,7 +285,7 @@ void furi_hal_subghz_load_preset(FuriHalSubGhzPreset preset) { } else if(preset == FuriHalSubGhzPreset2FSKAsync) { furi_hal_subghz_load_registers(furi_hal_subghz_preset_2fsk_async_regs); furi_hal_subghz_load_patable(furi_hal_subghz_preset_2fsk_async_patable); - }else { + } else { furi_crash(NULL); } } @@ -358,10 +362,12 @@ void furi_hal_subghz_rx() { furi_hal_spi_device_return(device); } -void furi_hal_subghz_tx() { +bool furi_hal_subghz_tx() { + if(furi_hal_subghz_regulation != SubGhzRegulationTxRx) return false; const FuriHalSpiDevice* device = furi_hal_spi_device_get(FuriHalSpiDeviceIdSubGhz); cc1101_switch_to_tx(device); furi_hal_spi_device_return(device); + return true; } float furi_hal_subghz_get_rssi() { @@ -385,6 +391,7 @@ bool furi_hal_subghz_is_frequency_valid(uint32_t value) { !(value >= 778999847 && value <= 928000000)) { return false; } + return true; } @@ -405,6 +412,46 @@ uint32_t furi_hal_subghz_set_frequency_and_path(uint32_t value) { uint32_t furi_hal_subghz_set_frequency(uint32_t value) { const FuriHalSpiDevice* device = furi_hal_spi_device_get(FuriHalSpiDeviceIdSubGhz); + //checking regional settings + bool txrx = false; + switch(furi_hal_version_get_hw_region()) { + case FuriHalVersionRegionEuRu: + //433,05..434,79; 868,15..868,55 + if(!(value >= 433050000 && value <= 434790000) && + !(value >= 868150000 && value <= 8680550000)) { + } else { + txrx = true; + } + break; + case FuriHalVersionRegionUsCaAu: + //304,10..315,25; 433,05..434,79; 915,00..928,00 + if(!(value >= 304100000 && value <= 315250000) && + !(value >= 433050000 && value <= 434790000) && + !(value >= 915000000 && value <= 928000000)) { + } else { + txrx = true; + } + break; + case FuriHalVersionRegionJp: + //312,00..315,25; 920,50..923,50 + if(!(value >= 312000000 && value <= 315250000) && + !(value >= 920500000 && value <= 923500000)) { + } else { + txrx = true; + } + break; + + default: + txrx = true; + break; + } + + if(txrx) { + furi_hal_subghz_regulation = SubGhzRegulationTxRx; + } else { + furi_hal_subghz_regulation = SubGhzRegulationOnlyRx; + } + uint32_t real_frequency = cc1101_set_frequency(device, value); cc1101_calibrate(device); @@ -482,7 +529,7 @@ void furi_hal_subghz_start_async_rx(FuriHalSubGhzCaptureCallback callback, void* TIM_InitStruct.Prescaler = 64 - 1; TIM_InitStruct.CounterMode = LL_TIM_COUNTERMODE_UP; TIM_InitStruct.Autoreload = 0x7FFFFFFE; - TIM_InitStruct.ClockDivision = LL_TIM_CLOCKDIVISION_DIV1; + TIM_InitStruct.ClockDivision = LL_TIM_CLOCKDIVISION_DIV4; LL_TIM_Init(TIM2, &TIM_InitStruct); // Timer: advanced @@ -505,7 +552,7 @@ void furi_hal_subghz_start_async_rx(FuriHalSubGhzCaptureCallback callback, void* 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); LL_TIM_IC_SetPolarity(TIM2, LL_TIM_CHANNEL_CH2, LL_TIM_IC_POLARITY_RISING); - LL_TIM_IC_SetFilter(TIM2, LL_TIM_CHANNEL_CH2, LL_TIM_IC_FILTER_FDIV1); + LL_TIM_IC_SetFilter(TIM2, LL_TIM_CHANNEL_CH2, LL_TIM_IC_FILTER_FDIV32_N8); // ISR setup furi_hal_interrupt_set_timer_isr(TIM2, furi_hal_subghz_capture_ISR); @@ -610,6 +657,8 @@ static void furi_hal_subghz_async_tx_timer_isr() { if(LL_TIM_GetAutoReload(TIM2) == 0) { if(furi_hal_subghz_state == SubGhzStateAsyncTx) { furi_hal_subghz_state = SubGhzStateAsyncTxLast; + //forcibly pulls the pin to the ground so that there is no carrier + hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullDown, GpioSpeedLow); } else { furi_hal_subghz_state = SubGhzStateAsyncTxEnd; LL_TIM_DisableCounter(TIM2); @@ -618,10 +667,13 @@ static void furi_hal_subghz_async_tx_timer_isr() { } } -void furi_hal_subghz_start_async_tx(FuriHalSubGhzAsyncTxCallback callback, void* context) { +bool furi_hal_subghz_start_async_tx(FuriHalSubGhzAsyncTxCallback callback, void* context) { furi_assert(furi_hal_subghz_state == SubGhzStateIdle); furi_assert(callback); + //If transmission is prohibited by regional settings + if(furi_hal_subghz_regulation != SubGhzRegulationTxRx) return false; + furi_hal_subghz_async_tx.callback = callback; furi_hal_subghz_async_tx.callback_context = context; @@ -696,6 +748,7 @@ void furi_hal_subghz_start_async_tx(FuriHalSubGhzAsyncTxCallback callback, void* LL_TIM_SetCounter(TIM2, 0); LL_TIM_EnableCounter(TIM2); + return true; } bool furi_hal_subghz_is_async_tx_complete() { diff --git a/firmware/targets/f7/furi-hal/furi-hal-subghz.c b/firmware/targets/f7/furi-hal/furi-hal-subghz.c index 7e8c12cdc40..34446d8da84 100644 --- a/firmware/targets/f7/furi-hal/furi-hal-subghz.c +++ b/firmware/targets/f7/furi-hal/furi-hal-subghz.c @@ -1,4 +1,5 @@ #include "furi-hal-subghz.h" +#include "furi-hal-version.h" #include #include @@ -10,6 +11,7 @@ #include static volatile SubGhzState furi_hal_subghz_state = SubGhzStateInit; +static volatile SubGhzRegulation furi_hal_subghz_regulation = SubGhzRegulationTxRx; static const uint8_t furi_hal_subghz_preset_ook_270khz_async_regs[][2] = { // https://e2e.ti.com/support/wireless-connectivity/sub-1-ghz-group/sub-1-ghz/f/sub-1-ghz-forum/382066/cc1101---don-t-know-the-correct-registers-configuration @@ -41,11 +43,11 @@ static const uint8_t furi_hal_subghz_preset_ook_270khz_async_regs[][2] = { 0x18}, // no frequency offset compensation, POST_K same as PRE_K, PRE_K is 4K, GATE is off /* Automatic Gain Control */ - {CC1101_AGCTRL0, + {CC1101_AGCCTRL0, 0x40}, // 01 - Low hysteresis, small asymmetric dead zone, medium gain; 00 - 8 samples agc; 00 - Normal AGC, 00 - 4dB boundary - {CC1101_AGCTRL1, + {CC1101_AGCCTRL1, 0x00}, // 0; 0 - LNA 2 gain is decreased to minimum before decreasing LNA gain; 00 - Relative carrier sense threshold disabled; 0000 - RSSI to MAIN_TARGET - {CC1101_AGCTRL2, 0x03}, // 00 - DVGA all; 000 - MAX LNA+LNA2; 011 - MAIN_TARGET 24 dB + {CC1101_AGCCTRL2, 0x03}, // 00 - DVGA all; 000 - MAX LNA+LNA2; 011 - MAIN_TARGET 24 dB /* Wake on radio and timeouts control */ {CC1101_WORCTRL, 0xFB}, // WOR_RES is 2^15 periods (0.91 - 0.94 s) 16.5 - 17.2 hours @@ -99,11 +101,15 @@ static const uint8_t furi_hal_subghz_preset_ook_650khz_async_regs[][2] = { 0x18}, // no frequency offset compensation, POST_K same as PRE_K, PRE_K is 4K, GATE is off /* Automatic Gain Control */ - {CC1101_AGCTRL0, - 0x40}, // 01 - Low hysteresis, small asymmetric dead zone, medium gain; 00 - 8 samples agc; 00 - Normal AGC, 00 - 4dB boundary - {CC1101_AGCTRL1, - 0x00}, // 0; 0 - LNA 2 gain is decreased to minimum before decreasing LNA gain; 00 - Relative carrier sense threshold disabled; 0000 - RSSI to MAIN_TARGET - {CC1101_AGCTRL2, 0x03}, // 00 - DVGA all; 000 - MAX LNA+LNA2; 011 - MAIN_TARGET 24 dB + // {CC1101_AGCTRL0,0x40}, // 01 - Low hysteresis, small asymmetric dead zone, medium gain; 00 - 8 samples agc; 00 - Normal AGC, 00 - 4dB boundary + // {CC1101_AGCTRL1,0x00}, // 0; 0 - LNA 2 gain is decreased to minimum before decreasing LNA gain; 00 - Relative carrier sense threshold disabled; 0000 - RSSI to MAIN_TARGET + // {CC1101_AGCCTRL2, 0x03}, // 00 - DVGA all; 000 - MAX LNA+LNA2; 011 - MAIN_TARGET 24 dB + //MAGN_TARGET for RX filter BW =< 100 kHz is 0x3. For higher RX filter BW's MAGN_TARGET is 0x7. + {CC1101_AGCCTRL0, + 0x91}, // 10 - Medium hysteresis, medium asymmetric dead zone, medium gain ; 01 - 16 samples agc; 00 - Normal AGC, 01 - 8dB boundary + {CC1101_AGCCTRL1, + 0x0}, // 0; 0 - LNA 2 gain is decreased to minimum before decreasing LNA gain; 00 - Relative carrier sense threshold disabled; 0000 - RSSI to MAIN_TARGET + {CC1101_AGCCTRL2, 0x07}, // 00 - DVGA all; 000 - MAX LNA+LNA2; 111 - MAIN_TARGET 42 dB /* Wake on radio and timeouts control */ {CC1101_WORCTRL, 0xFB}, // WOR_RES is 2^15 periods (0.91 - 0.94 s) 16.5 - 17.2 hours @@ -131,23 +137,21 @@ static const uint8_t furi_hal_subghz_preset_2fsk_async_regs[][2] = { /* GPIO GD0 */ {CC1101_IOCFG0, 0x0D}, // GD0 as async serial data output/input - /* FIFO and internals */ - {CC1101_FIFOTHR, 0x47}, // The only important bit is ADC_RETENTION + /* Frequency Synthesizer Control */ + {CC1101_FSCTRL1, 0x06}, // IF = (26*10^6) / (2^10) * 0x06 = 152343.75Hz /* Packet engine */ {CC1101_PKTCTRL0, 0x32}, // Async, continious, no whitening + {CC1101_PKTCTRL1, 0x04}, - /* Frequency Synthesizer Control */ - {CC1101_FSCTRL1, 0x06}, // IF = (26*10^6) / (2^10) * 0x06 = 152343.75Hz - - // Modem Configuration + // // Modem Configuration {CC1101_MDMCFG0, 0x00}, - {CC1101_MDMCFG1, 0x02}, - {CC1101_MDMCFG2, 0x04}, // Format 2-FSK/FM, No preamble/sync, Disable (current optimized) - {CC1101_MDMCFG3, 0x8B}, // Data rate is 19.5885 kBaud - {CC1101_MDMCFG4, 0x69}, // Rx BW filter is 270.833333 kHz - - {CC1101_DEVIATN, 0x47}, //Deviation 47.607422 khz + {CC1101_MDMCFG1, 0x2}, + {CC1101_MDMCFG2, 0x4}, // Format 2-FSK/FM, No preamble/sync, Disable (current optimized) + {CC1101_MDMCFG3, 0x83}, // Data rate is 4.79794 kBaud + {CC1101_MDMCFG4, 0x67}, //Rx BW filter is 270.833333 kHz + //{ CC1101_DEVIATN, 0x14 }, //Deviation 4.760742 kHz + {CC1101_DEVIATN, 0x04}, //Deviation 2.380371 kHz /* Main Radio Control State Machine */ {CC1101_MCSM0, 0x18}, // Autocalibrate on idle-to-rx/tx, PO_TIMEOUT is 64 cycles(149-155us) @@ -157,18 +161,18 @@ static const uint8_t furi_hal_subghz_preset_2fsk_async_regs[][2] = { 0x16}, // no frequency offset compensation, POST_K same as PRE_K, PRE_K is 4K, GATE is off /* Automatic Gain Control */ - {CC1101_AGCTRL0, - 0x40}, // 01 - Low hysteresis, small asymmetric dead zone, medium gain; 00 - 8 samples agc; 00 - Normal AGC, 00 - 4dB boundary - {CC1101_AGCTRL1, + {CC1101_AGCCTRL0, + 0x91}, //10 - Medium hysteresis, medium asymmetric dead zone, medium gain ; 01 - 16 samples agc; 00 - Normal AGC, 01 - 8dB boundary + {CC1101_AGCCTRL1, 0x00}, // 0; 0 - LNA 2 gain is decreased to minimum before decreasing LNA gain; 00 - Relative carrier sense threshold disabled; 0000 - RSSI to MAIN_TARGET - {CC1101_AGCTRL2, 0x03}, // 00 - DVGA all; 000 - MAX LNA+LNA2; 011 - MAIN_TARGET 24 dB + {CC1101_AGCCTRL2, 0x07}, // 00 - DVGA all; 000 - MAX LNA+LNA2; 111 - MAIN_TARGET 42 dB /* Wake on radio and timeouts control */ {CC1101_WORCTRL, 0xFB}, // WOR_RES is 2^15 periods (0.91 - 0.94 s) 16.5 - 17.2 hours /* Frontend configuration */ {CC1101_FREND0, 0x10}, // Adjusts current TX LO buffer - {CC1101_FREND1, 0xB6}, // + {CC1101_FREND1, 0x56}, /* Frequency Synthesizer Calibration, valid for 433.92 */ {CC1101_FSCAL3, 0xE9}, @@ -201,7 +205,9 @@ static const uint8_t furi_hal_subghz_preset_2fsk_async_patable[8] = { 0x00, 0x00, 0x00, - 0x00}; + 0x00 + +}; void furi_hal_subghz_init() { furi_assert(furi_hal_subghz_state == SubGhzStateInit); @@ -279,7 +285,7 @@ void furi_hal_subghz_load_preset(FuriHalSubGhzPreset preset) { } else if(preset == FuriHalSubGhzPreset2FSKAsync) { furi_hal_subghz_load_registers(furi_hal_subghz_preset_2fsk_async_regs); furi_hal_subghz_load_patable(furi_hal_subghz_preset_2fsk_async_patable); - }else { + } else { furi_crash(NULL); } } @@ -356,10 +362,12 @@ void furi_hal_subghz_rx() { furi_hal_spi_device_return(device); } -void furi_hal_subghz_tx() { +bool furi_hal_subghz_tx() { + if(furi_hal_subghz_regulation != SubGhzRegulationTxRx) return false; const FuriHalSpiDevice* device = furi_hal_spi_device_get(FuriHalSpiDeviceIdSubGhz); cc1101_switch_to_tx(device); furi_hal_spi_device_return(device); + return true; } float furi_hal_subghz_get_rssi() { @@ -383,6 +391,7 @@ bool furi_hal_subghz_is_frequency_valid(uint32_t value) { !(value >= 778999847 && value <= 928000000)) { return false; } + return true; } @@ -403,6 +412,46 @@ uint32_t furi_hal_subghz_set_frequency_and_path(uint32_t value) { uint32_t furi_hal_subghz_set_frequency(uint32_t value) { const FuriHalSpiDevice* device = furi_hal_spi_device_get(FuriHalSpiDeviceIdSubGhz); + //checking regional settings + bool txrx = false; + switch(furi_hal_version_get_hw_region()) { + case FuriHalVersionRegionEuRu: + //433,05..434,79; 868,15..868,55 + if(!(value >= 433050000 && value <= 434790000) && + !(value >= 868150000 && value <= 8680550000)) { + } else { + txrx = true; + } + break; + case FuriHalVersionRegionUsCaAu: + //304,10..315,25; 433,05..434,79; 915,00..928,00 + if(!(value >= 304100000 && value <= 315250000) && + !(value >= 433050000 && value <= 434790000) && + !(value >= 915000000 && value <= 928000000)) { + } else { + txrx = true; + } + break; + case FuriHalVersionRegionJp: + //312,00..315,25; 920,50..923,50 + if(!(value >= 312000000 && value <= 315250000) && + !(value >= 920500000 && value <= 923500000)) { + } else { + txrx = true; + } + break; + + default: + txrx = true; + break; + } + + if(txrx) { + furi_hal_subghz_regulation = SubGhzRegulationTxRx; + } else { + furi_hal_subghz_regulation = SubGhzRegulationOnlyRx; + } + uint32_t real_frequency = cc1101_set_frequency(device, value); cc1101_calibrate(device); @@ -480,7 +529,7 @@ void furi_hal_subghz_start_async_rx(FuriHalSubGhzCaptureCallback callback, void* TIM_InitStruct.Prescaler = 64 - 1; TIM_InitStruct.CounterMode = LL_TIM_COUNTERMODE_UP; TIM_InitStruct.Autoreload = 0x7FFFFFFE; - TIM_InitStruct.ClockDivision = LL_TIM_CLOCKDIVISION_DIV1; + TIM_InitStruct.ClockDivision = LL_TIM_CLOCKDIVISION_DIV4; LL_TIM_Init(TIM2, &TIM_InitStruct); // Timer: advanced @@ -503,7 +552,7 @@ void furi_hal_subghz_start_async_rx(FuriHalSubGhzCaptureCallback callback, void* 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); LL_TIM_IC_SetPolarity(TIM2, LL_TIM_CHANNEL_CH2, LL_TIM_IC_POLARITY_RISING); - LL_TIM_IC_SetFilter(TIM2, LL_TIM_CHANNEL_CH2, LL_TIM_IC_FILTER_FDIV1); + LL_TIM_IC_SetFilter(TIM2, LL_TIM_CHANNEL_CH2, LL_TIM_IC_FILTER_FDIV32_N8); // ISR setup furi_hal_interrupt_set_timer_isr(TIM2, furi_hal_subghz_capture_ISR); @@ -608,6 +657,8 @@ static void furi_hal_subghz_async_tx_timer_isr() { if(LL_TIM_GetAutoReload(TIM2) == 0) { if(furi_hal_subghz_state == SubGhzStateAsyncTx) { furi_hal_subghz_state = SubGhzStateAsyncTxLast; + //forcibly pulls the pin to the ground so that there is no carrier + hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullDown, GpioSpeedLow); } else { furi_hal_subghz_state = SubGhzStateAsyncTxEnd; LL_TIM_DisableCounter(TIM2); @@ -616,10 +667,13 @@ static void furi_hal_subghz_async_tx_timer_isr() { } } -void furi_hal_subghz_start_async_tx(FuriHalSubGhzAsyncTxCallback callback, void* context) { +bool furi_hal_subghz_start_async_tx(FuriHalSubGhzAsyncTxCallback callback, void* context) { furi_assert(furi_hal_subghz_state == SubGhzStateIdle); furi_assert(callback); + //If transmission is prohibited by regional settings + if(furi_hal_subghz_regulation != SubGhzRegulationTxRx) return false; + furi_hal_subghz_async_tx.callback = callback; furi_hal_subghz_async_tx.callback_context = context; @@ -694,6 +748,7 @@ void furi_hal_subghz_start_async_tx(FuriHalSubGhzAsyncTxCallback callback, void* LL_TIM_SetCounter(TIM2, 0); LL_TIM_EnableCounter(TIM2); + return true; } bool furi_hal_subghz_is_async_tx_complete() { diff --git a/firmware/targets/furi-hal-include/furi-hal-subghz.h b/firmware/targets/furi-hal-include/furi-hal-subghz.h index 653a343c940..38aa65da873 100644 --- a/firmware/targets/furi-hal-include/furi-hal-subghz.h +++ b/firmware/targets/furi-hal-include/furi-hal-subghz.h @@ -26,17 +26,24 @@ typedef enum { /** SubGhz state */ typedef enum { - SubGhzStateInit, /** Init pending */ + SubGhzStateInit, /** Init pending */ - SubGhzStateIdle, /** Idle, energy save mode */ + SubGhzStateIdle, /** Idle, energy save mode */ - SubGhzStateAsyncRx, /** Async RX started */ + SubGhzStateAsyncRx, /** Async RX started */ + + SubGhzStateAsyncTx, /** Async TX started, DMA and timer is on */ + SubGhzStateAsyncTxLast, /** Async TX continue, DMA completed and timer got last value to go */ + SubGhzStateAsyncTxEnd, /** Async TX complete, cleanup needed */ - SubGhzStateAsyncTx, /** Async TX started, DMA and timer is on */ - SubGhzStateAsyncTxLast, /** Async TX continue, DMA completed and timer got last value to go */ - SubGhzStateAsyncTxEnd, /** Async TX complete, cleanup needed */ } SubGhzState; +/** SubGhz regulation, receive transmission on the current frequency for the region */ +typedef enum { + SubGhzRegulationOnlyRx, /**only Rx*/ + SubGhzRegulationTxRx, /**TxRx*/ +} SubGhzRegulation; + /** Initialize and switch to power save mode * Used by internal API-HAL initalization routine * Can be used to reinitialize device to safe state and send it to sleep @@ -100,8 +107,10 @@ void furi_hal_subghz_idle(); /** Switch to Recieve */ void furi_hal_subghz_rx(); -/** Switch to Transmit */ -void furi_hal_subghz_tx(); +/** Switch to Transmit +* @return true if the transfer is allowed by belonging to the region +*/ +bool furi_hal_subghz_tx(); /** Get RSSI value in dBm */ float furi_hal_subghz_get_rssi(); @@ -152,8 +161,9 @@ typedef LevelDuration (*FuriHalSubGhzAsyncTxCallback)(void* context); /** Start async TX * Initializes GPIO, TIM2 and DMA1 for signal output + * @return true if the transfer is allowed by belonging to the region */ -void furi_hal_subghz_start_async_tx(FuriHalSubGhzAsyncTxCallback callback, void* context); +bool furi_hal_subghz_start_async_tx(FuriHalSubGhzAsyncTxCallback callback, void* context); /** Wait for async transmission to complete */ bool furi_hal_subghz_is_async_tx_complete(); diff --git a/lib/drivers/cc1101_regs.h b/lib/drivers/cc1101_regs.h index 8305584d31c..6e71cc37b3d 100644 --- a/lib/drivers/cc1101_regs.h +++ b/lib/drivers/cc1101_regs.h @@ -48,9 +48,9 @@ extern "C" { #define CC1101_MCSM0 0x18 /** Main Radio Control State Machine configuration */ #define CC1101_FOCCFG 0x19 /** Frequency Offset Compensation configuration */ #define CC1101_BSCFG 0x1A /** Bit Synchronization configuration */ -#define CC1101_AGCTRL2 0x1B /** AGC control */ -#define CC1101_AGCTRL1 0x1C /** AGC control */ -#define CC1101_AGCTRL0 0x1D /** AGC control */ +#define CC1101_AGCCTRL2 0x1B /** AGC control */ +#define CC1101_AGCCTRL1 0x1C /** AGC control */ +#define CC1101_AGCCTRL0 0x1D /** AGC control */ #define CC1101_WOREVT1 0x1E /** High byte Event 0 timeout */ #define CC1101_WOREVT0 0x1F /** Low byte Event 0 timeout */ #define CC1101_WORCTRL 0x20 /** Wake On Radio control */ diff --git a/lib/subghz/protocols/subghz_protocol_princeton.c b/lib/subghz/protocols/subghz_protocol_princeton.c index 700d221f920..7819d0dd076 100644 --- a/lib/subghz/protocols/subghz_protocol_princeton.c +++ b/lib/subghz/protocols/subghz_protocol_princeton.c @@ -8,12 +8,17 @@ #define SUBGHZ_PT_SHORT 400 #define SUBGHZ_PT_LONG (SUBGHZ_PT_SHORT * 3) #define SUBGHZ_PT_GUARD (SUBGHZ_PT_SHORT * 30) +#define SUBGHZ_PT_COUNT_KEY 5 +#define SUBGHZ_PT_TIMEOUT 320 struct SubGhzEncoderPrinceton { uint32_t key; uint16_t te; size_t repeat; size_t front; + size_t count_key; + uint32_t time_high; + uint32_t time_low; }; typedef enum { @@ -45,8 +50,11 @@ void subghz_encoder_princeton_set(SubGhzEncoderPrinceton* instance, uint32_t key furi_assert(instance); instance->te = SUBGHZ_PT_SHORT; instance->key = key; - instance->repeat = repeat; + instance->repeat = repeat + 1; instance->front = 48; + instance->count_key = SUBGHZ_PT_COUNT_KEY + 7; + instance->time_high = 0; + instance->time_low = 0; } size_t subghz_encoder_princeton_get_repeat_left(SubGhzEncoderPrinceton* instance) { @@ -54,9 +62,25 @@ size_t subghz_encoder_princeton_get_repeat_left(SubGhzEncoderPrinceton* instance return instance->repeat; } +void subghz_encoder_princeton_print_log(void* context) { + SubGhzEncoderPrinceton* instance = context; + float duty_cycle = + ((float)instance->time_high / (instance->time_high + instance->time_low)) * 100; + FURI_LOG_I( + "EncoderPrinceton", + "Radio ON=%dus, OFF=%dus, DutyCycle=%d,%d%%", + instance->time_high, + instance->time_low, + (uint32_t)duty_cycle, + (uint32_t)((duty_cycle - (uint32_t)duty_cycle) * 100)); +} + LevelDuration subghz_encoder_princeton_yield(void* context) { SubGhzEncoderPrinceton* instance = context; - if(instance->repeat == 0) return level_duration_reset(); + if(instance->repeat == 0) { + subghz_encoder_princeton_print_log(instance); + return level_duration_reset(); + } size_t bit = instance->front / 2; bool level = !(instance->front % 2); @@ -68,11 +92,33 @@ LevelDuration subghz_encoder_princeton_yield(void* context) { bool value = (((uint8_t*)&instance->key)[2 - byte] >> (7 - bit_in_byte)) & 1; if(value) { ret = level_duration_make(level, level ? instance->te * 3 : instance->te); + if(level) + instance->time_high += instance->te * 3; + else + instance->time_low += instance->te; } else { ret = level_duration_make(level, level ? instance->te : instance->te * 3); + if(level) + instance->time_high += instance->te; + else + instance->time_low += instance->te * 3; } } else { - ret = level_duration_make(level, level ? instance->te : instance->te * 30); + if(--instance->count_key != 0) { + ret = level_duration_make(level, level ? instance->te : instance->te * 30); + if(level) + instance->time_high += instance->te; + else + instance->time_low += instance->te * 30; + } else { + instance->count_key = SUBGHZ_PT_COUNT_KEY + 6; + instance->front = 48; + ret = level_duration_make(level, level ? instance->te : SUBGHZ_PT_TIMEOUT * 1000); + if(level) + instance->time_high += instance->te; + else + instance->time_low += SUBGHZ_PT_TIMEOUT * 1000; + } } instance->front++; diff --git a/lib/subghz/protocols/subghz_protocol_princeton.h b/lib/subghz/protocols/subghz_protocol_princeton.h index fdc37a43c76..09a9f4821f4 100644 --- a/lib/subghz/protocols/subghz_protocol_princeton.h +++ b/lib/subghz/protocols/subghz_protocol_princeton.h @@ -33,6 +33,11 @@ void subghz_encoder_princeton_set(SubGhzEncoderPrinceton* instance, uint32_t key */ size_t subghz_encoder_princeton_get_repeat_left(SubGhzEncoderPrinceton* instance); +/** Print encoder log + * @param instance - SubGhzEncoderPrinceton instance + */ +void subghz_encoder_princeton_print_log(void* context); + /** Get level duration * @param instance - SubGhzEncoderPrinceton instance * @return level duration diff --git a/lib/subghz/protocols/subghz_protocol_scher_khan.c b/lib/subghz/protocols/subghz_protocol_scher_khan.c new file mode 100644 index 00000000000..b5124f93bcb --- /dev/null +++ b/lib/subghz/protocols/subghz_protocol_scher_khan.c @@ -0,0 +1,246 @@ +#include "subghz_protocol_scher_khan.h" + +//https://phreakerclub.com/72 +//https://phreakerclub.com/forum/showthread.php?t=7&page=2 +//https://phreakerclub.com/forum/showthread.php?t=274&highlight=magicar +//!!! https://phreakerclub.com/forum/showthread.php?t=489&highlight=magicar&page=5 + +struct SubGhzProtocolScherKhan { + SubGhzProtocolCommon common; + const char* protocol_name; +}; + +typedef enum { + ScherKhanDecoderStepReset = 0, + ScherKhanDecoderStepCheckPreambula, + ScherKhanDecoderStepSaveDuration, + ScherKhanDecoderStepCheckDuration, +} ScherKhanDecoderStep; + +SubGhzProtocolScherKhan* subghz_protocol_scher_khan_alloc(void) { + SubGhzProtocolScherKhan* instance = furi_alloc(sizeof(SubGhzProtocolScherKhan)); + + instance->common.name = "Scher-Khan"; + instance->common.code_min_count_bit_for_found = 35; + instance->common.te_short = 750; + instance->common.te_long = 1100; + instance->common.te_delta = 150; + instance->common.type_protocol = SubGhzProtocolCommonTypeDynamic; + instance->common.to_string = (SubGhzProtocolCommonToStr)subghz_protocol_scher_khan_to_str; + instance->common.to_load_protocol = + (SubGhzProtocolCommonLoadFromRAW)subghz_decoder_scher_khan_to_load_protocol; + + return instance; +} + +void subghz_protocol_scher_khan_free(SubGhzProtocolScherKhan* instance) { + furi_assert(instance); + free(instance); +} + +/** Send bit + * + * @param instance - SubGhzProtocolScherKhan instance + * @param bit - bit + */ +// void subghz_protocol_scher_khan_send_bit(SubGhzProtocolScherKhan* instance, uint8_t bit) { +// if(bit) { +// //send bit 1 +// SUBGHZ_TX_PIN_HIGH(); +// delay_us(instance->common.te_long); +// SUBGHZ_TX_PIN_LOW(); +// delay_us(instance->common.te_short); +// } else { +// //send bit 0 +// SUBGHZ_TX_PIN_HIGH(); +// delay_us(instance->common.te_short); +// SUBGHZ_TX_PIN_LOW(); +// delay_us(instance->common.te_long); +// } +// } + +// void subghz_protocol_scher_khan_send_key( +// SubGhzProtocolScherKhan* instance, +// uint64_t key, +// uint8_t bit, +// uint8_t repeat) { +// while(repeat--) { +// SUBGHZ_TX_PIN_HIGH(); +// //Send header +// delay_us(instance->common.te_long * 2); +// SUBGHZ_TX_PIN_LOW(); +// delay_us(instance->common.te_long * 2); +// //Send key data +// for(uint8_t i = bit; i > 0; i--) { +// subghz_protocol_scher_khan_send_bit(instance, bit_read(key, i - 1)); +// } +// } +// } + +void subghz_protocol_scher_khan_reset(SubGhzProtocolScherKhan* instance) { + instance->common.parser_step = ScherKhanDecoderStepReset; +} + +/** Analysis of received data + * + * @param instance SubGhzProtocolScherKhan instance + */ +void subghz_protocol_scher_khan_check_remote_controller(SubGhzProtocolScherKhan* instance) { + /* + * MAGICAR 51 bit 00000001A99121DE83C3 MAGIC CODE, Dinamic + * 0E8C1619E830C -> 000011101000110000010110 0001 1001 1110 1000001100001100 + * 0E8C1629D830D -> 000011101000110000010110 0010 1001 1101 1000001100001101 + * 0E8C1649B830E -> 000011101000110000010110 0100 1001 1011 1000001100001110 + * 0E8C16897830F -> 000011101000110000010110 1000 1001 0111 1000001100001111 + * Serial Key Ser ~Key CNT + */ + + switch(instance->common.code_last_count_bit) { + // case 35: //MAGIC CODE, Static + // instance->protocol_name = "MAGIC CODE, Static"; + // break; + case 51: //MAGIC CODE, Dinamic + instance->protocol_name = "MAGIC CODE, Dinamic"; + instance->common.serial = ((instance->common.code_last_found >> 24) & 0xFFFFFF0) | + ((instance->common.code_last_found >> 20) & 0x0F); + instance->common.btn = (instance->common.code_last_found >> 24) & 0x0F; + instance->common.cnt = instance->common.code_last_found & 0xFFFF; + break; + // case 57: //MAGIC CODE PRO / PRO2 + // instance->protocol_name = "MAGIC CODE PRO / PRO2"; + // break; + + default: + instance->protocol_name = "Unknown"; + instance->common.serial = 0; + instance->common.btn = 0; + instance->common.cnt = 0; + break; + } +} + +void subghz_protocol_scher_khan_parse( + SubGhzProtocolScherKhan* instance, + bool level, + uint32_t duration) { + switch(instance->common.parser_step) { + case ScherKhanDecoderStepReset: + if((level) && + (DURATION_DIFF(duration, instance->common.te_short * 2) < instance->common.te_delta)) { + instance->common.parser_step = ScherKhanDecoderStepCheckPreambula; + instance->common.te_last = duration; + instance->common.header_count = 0; + } else { + instance->common.parser_step = ScherKhanDecoderStepReset; + } + break; + case ScherKhanDecoderStepCheckPreambula: + if(level) { + if((DURATION_DIFF(duration, instance->common.te_short * 2) < + instance->common.te_delta) || + (DURATION_DIFF(duration, instance->common.te_short) < instance->common.te_delta)) { + instance->common.te_last = duration; + } else { + instance->common.parser_step = ScherKhanDecoderStepReset; + } + } else if( + (DURATION_DIFF(duration, instance->common.te_short * 2) < instance->common.te_delta) || + (DURATION_DIFF(duration, instance->common.te_short) < instance->common.te_delta)) { + if(DURATION_DIFF(instance->common.te_last, instance->common.te_short * 2) < + instance->common.te_delta) { + // Found header + instance->common.header_count++; + break; + } else if( + DURATION_DIFF(instance->common.te_last, instance->common.te_short) < + instance->common.te_delta) { + // Found start bit + if(instance->common.header_count >= 2) { + instance->common.parser_step = ScherKhanDecoderStepSaveDuration; + instance->common.code_found = 0; + instance->common.code_count_bit = 1; + } else { + instance->common.parser_step = ScherKhanDecoderStepReset; + } + } else { + instance->common.parser_step = ScherKhanDecoderStepReset; + } + } else { + instance->common.parser_step = ScherKhanDecoderStepReset; + } + break; + case ScherKhanDecoderStepSaveDuration: + if(level) { + if(duration >= (instance->common.te_long + instance->common.te_delta * 2)) { + //Found stop bit + instance->common.parser_step = ScherKhanDecoderStepReset; + 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_count_bit = instance->common.code_count_bit; + if(instance->common.callback) + instance->common.callback( + (SubGhzProtocolCommon*)instance, instance->common.context); + } + instance->common.code_found = 0; + instance->common.code_count_bit = 0; + break; + } else { + instance->common.te_last = duration; + instance->common.parser_step = ScherKhanDecoderStepCheckDuration; + } + + } else { + instance->common.parser_step = ScherKhanDecoderStepReset; + } + break; + case ScherKhanDecoderStepCheckDuration: + if(!level) { + if((DURATION_DIFF(instance->common.te_last, instance->common.te_short) < + instance->common.te_delta) && + (DURATION_DIFF(duration, instance->common.te_short) < instance->common.te_delta)) { + subghz_protocol_common_add_bit(&instance->common, 0); + instance->common.parser_step = ScherKhanDecoderStepSaveDuration; + } else if( + (DURATION_DIFF(instance->common.te_last, instance->common.te_long) < + instance->common.te_delta) && + (DURATION_DIFF(duration, instance->common.te_long) < instance->common.te_delta)) { + subghz_protocol_common_add_bit(&instance->common, 1); + instance->common.parser_step = ScherKhanDecoderStepSaveDuration; + } else { + instance->common.parser_step = ScherKhanDecoderStepReset; + } + } else { + instance->common.parser_step = ScherKhanDecoderStepReset; + } + break; + } +} + +void subghz_protocol_scher_khan_to_str(SubGhzProtocolScherKhan* instance, string_t output) { + subghz_protocol_scher_khan_check_remote_controller(instance); + + string_cat_printf( + output, + "%s %dbit\r\n" + "Key:0x%lX%08lX\r\n" + "Sn:%07lX Btn:%lX Cnt:%04X\r\n" + "Pt: %s\r\n", + instance->common.name, + instance->common.code_last_count_bit, + (uint32_t)(instance->common.code_last_found >> 32), + (uint32_t)instance->common.code_last_found, + instance->common.serial, + instance->common.btn, + instance->common.cnt, + instance->protocol_name); +} + +void subghz_decoder_scher_khan_to_load_protocol(SubGhzProtocolScherKhan* instance, void* context) { + furi_assert(context); + furi_assert(instance); + SubGhzProtocolCommonLoad* data = context; + instance->common.code_last_found = data->code_found; + instance->common.code_last_count_bit = data->code_count_bit; + subghz_protocol_scher_khan_check_remote_controller(instance); +} \ No newline at end of file diff --git a/lib/subghz/protocols/subghz_protocol_scher_khan.h b/lib/subghz/protocols/subghz_protocol_scher_khan.h new file mode 100644 index 00000000000..3aa0670118d --- /dev/null +++ b/lib/subghz/protocols/subghz_protocol_scher_khan.h @@ -0,0 +1,58 @@ +#pragma once + +#include "subghz_protocol_common.h" + +typedef struct SubGhzProtocolScherKhan SubGhzProtocolScherKhan; + +/** Allocate SubGhzProtocolScherKhan + * + * @return SubGhzProtocolScherKhan* + */ +SubGhzProtocolScherKhan* subghz_protocol_scher_khan_alloc(); + +/** Free SubGhzProtocolScherKhan + * + * @param instance + */ +void subghz_protocol_scher_khan_free(SubGhzProtocolScherKhan* instance); + +/** Sends the key on the air + * + * @param instance - SubGhzProtocolScherKhan instance + * @param key - key send + * @param bit - count bit key + * @param repeat - repeat send key + */ +void subghz_protocol_scher_khan_send_key(SubGhzProtocolScherKhan* instance, uint64_t key, uint8_t bit, uint8_t repeat); + +/** Reset internal state + * @param instance - SubGhzProtocolScherKhan instance + */ +void subghz_protocol_scher_khan_reset(SubGhzProtocolScherKhan* instance); + +/** Analysis of received data + * + * @param instance SubGhzProtocolScherKhan instance + */ +void subghz_protocol_scher_khan_check_remote_controller(SubGhzProtocolScherKhan* instance); + +/** Parse accepted duration + * + * @param instance - SubGhzProtocolScherKhan instance + * @param data - LevelDuration level_duration + */ +void subghz_protocol_scher_khan_parse(SubGhzProtocolScherKhan* instance, bool level, uint32_t duration); + +/** Outputting information from the parser + * + * @param instance - SubGhzProtocolScherKhan* instance + * @param output - output string + */ +void subghz_protocol_scher_khan_to_str(SubGhzProtocolScherKhan* instance, string_t output); + +/** Loading protocol from bin data + * + * @param instance - SubGhzProtocolScherKhan instance + * @param context - SubGhzProtocolCommonLoad context + */ +void subghz_decoder_scher_khan_to_load_protocol(SubGhzProtocolScherKhan* instance, void* context); diff --git a/lib/subghz/subghz_parser.c b/lib/subghz/subghz_parser.c index 490d728e113..fed09fc5a89 100644 --- a/lib/subghz/subghz_parser.c +++ b/lib/subghz/subghz_parser.c @@ -12,6 +12,7 @@ #include "protocols/subghz_protocol_nero_sketch.h" #include "protocols/subghz_protocol_star_line.h" #include "protocols/subghz_protocol_nero_radio.h" +#include "protocols/subghz_protocol_scher_khan.h" #include "subghz_keystore.h" @@ -30,6 +31,7 @@ typedef enum { SubGhzProtocolTypeNeroSketch, SubGhzProtocolTypeStarLine, SubGhzProtocolTypeNeroRadio, + SubGhzProtocolTypeScherKhan, SubGhzProtocolTypeMax, } SubGhzProtocolType; @@ -93,6 +95,8 @@ SubGhzParser* subghz_parser_alloc() { (SubGhzProtocolCommon*)subghz_protocol_star_line_alloc(instance->keystore); instance->protocols[SubGhzProtocolTypeNeroRadio] = (SubGhzProtocolCommon*)subghz_protocol_nero_radio_alloc(); + instance->protocols[SubGhzProtocolTypeScherKhan] = + (SubGhzProtocolCommon*)subghz_protocol_scher_khan_alloc(); return instance; } @@ -120,6 +124,8 @@ void subghz_parser_free(SubGhzParser* instance) { (SubGhzProtocolStarLine*)instance->protocols[SubGhzProtocolTypeStarLine]); subghz_protocol_nero_radio_free( (SubGhzProtocolNeroRadio*)instance->protocols[SubGhzProtocolTypeNeroRadio]); + subghz_protocol_scher_khan_free( + (SubGhzProtocolScherKhan*)instance->protocols[SubGhzProtocolTypeScherKhan]); subghz_keystore_free(instance->keystore); @@ -199,6 +205,8 @@ void subghz_parser_reset(SubGhzParser* instance) { (SubGhzProtocolStarLine*)instance->protocols[SubGhzProtocolTypeStarLine]); subghz_protocol_nero_radio_reset( (SubGhzProtocolNeroRadio*)instance->protocols[SubGhzProtocolTypeNeroRadio]); + subghz_protocol_scher_khan_reset( + (SubGhzProtocolScherKhan*)instance->protocols[SubGhzProtocolTypeScherKhan]); } void subghz_parser_parse(SubGhzParser* instance, bool level, uint32_t duration) { @@ -232,4 +240,8 @@ void subghz_parser_parse(SubGhzParser* instance, bool level, uint32_t duration) (SubGhzProtocolNeroRadio*)instance->protocols[SubGhzProtocolTypeNeroRadio], level, duration); + subghz_protocol_scher_khan_parse( + (SubGhzProtocolScherKhan*)instance->protocols[SubGhzProtocolTypeScherKhan], + level, + duration); } diff --git a/lib/subghz/subghz_worker.c b/lib/subghz/subghz_worker.c index 626ec77f8cd..8697b394116 100644 --- a/lib/subghz/subghz_worker.c +++ b/lib/subghz/subghz_worker.c @@ -10,6 +10,10 @@ struct SubGhzWorker { volatile bool running; volatile bool overrun; + LevelDuration filter_level_duration; + bool filter_running; + uint16_t filter_duration; + SubGhzWorkerOverrunCallback overrun_callback; SubGhzWorkerPairCallback pair_callback; void* context; @@ -30,8 +34,8 @@ void subghz_worker_rx_callback(bool level, uint32_t duration, void* context) { instance->overrun = false; level_duration = level_duration_reset(); } - size_t ret = - xStreamBufferSendFromISR(instance->stream, &level_duration, sizeof(LevelDuration), &xHigherPriorityTaskWoken); + size_t ret = xStreamBufferSendFromISR( + instance->stream, &level_duration, sizeof(LevelDuration), &xHigherPriorityTaskWoken); if(sizeof(LevelDuration) != ret) instance->overrun = true; portYIELD_FROM_ISR(xHigherPriorityTaskWoken); } @@ -46,15 +50,35 @@ static int32_t subghz_worker_thread_callback(void* context) { LevelDuration level_duration; while(instance->running) { - int ret = xStreamBufferReceive(instance->stream, &level_duration, sizeof(LevelDuration), 10); + int ret = + xStreamBufferReceive(instance->stream, &level_duration, sizeof(LevelDuration), 10); if(ret == sizeof(LevelDuration)) { if(level_duration_is_reset(level_duration)) { printf("."); - if (instance->overrun_callback) instance->overrun_callback(instance->context); + if(instance->overrun_callback) instance->overrun_callback(instance->context); } else { bool level = level_duration_get_level(level_duration); uint32_t duration = level_duration_get_duration(level_duration); - if (instance->pair_callback) instance->pair_callback(instance->context, level, duration); + + if(instance->filter_running) { + if((duration < instance->filter_duration) || + (instance->filter_level_duration.level == level)) { + instance->filter_level_duration.duration += duration; + + } else if(instance->filter_level_duration.level != level) { + if(instance->pair_callback) + instance->pair_callback( + instance->context, + instance->filter_level_duration.level, + instance->filter_level_duration.duration); + + instance->filter_level_duration.duration = duration; + instance->filter_level_duration.level = level; + } + } else { + if(instance->pair_callback) + instance->pair_callback(instance->context, level, duration); + } } } } @@ -70,9 +94,13 @@ SubGhzWorker* subghz_worker_alloc() { 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(LevelDuration) * 1024, sizeof(LevelDuration)); + //setting filter + instance->filter_running = true; + instance->filter_duration = 20; + return instance; } @@ -85,7 +113,9 @@ void subghz_worker_free(SubGhzWorker* instance) { free(instance); } -void subghz_worker_set_overrun_callback(SubGhzWorker* instance, SubGhzWorkerOverrunCallback callback) { +void subghz_worker_set_overrun_callback( + SubGhzWorker* instance, + SubGhzWorkerOverrunCallback callback) { furi_assert(instance); instance->overrun_callback = callback; }