├── meal_pager.h ├── icons ├── KeySave_24x11.png ├── meal_pager_10px.png ├── KeyBackspace_16x9.png ├── KeySaveSelected_24x11.png └── KeyBackspaceSelected_16x9.png ├── helpers ├── retekess │ ├── meal_pager_retekess_td174.h │ ├── meal_pager_retekess_t119.h │ ├── meal_pager_retekess_td157.h │ ├── meal_pager_retekess_td165.h │ ├── meal_pager_retekess_td157.c │ ├── meal_pager_retekess_td165.c │ ├── meal_pager_retekess_td174.c │ └── meal_pager_retekess_t119.c ├── subghz │ ├── subghz.h │ ├── subghz_error_type.h │ ├── subghz_txrx_i.h │ ├── subghz_types.h │ ├── subghz.c │ ├── subghz_i.h │ ├── subghz_i.c │ ├── subghz_txrx.h │ └── subghz_txrx.c ├── meal_pager_speaker.h ├── meal_pager_haptic.h ├── meal_pager_calc.h ├── meal_pager_led.h ├── meal_pager_speaker.c ├── meal_pager_haptic.c ├── meal_pager_storage.h ├── gui │ ├── int_input.h │ └── int_input.c ├── meal_pager_calc.c ├── meal_pager_led.c ├── meal_pager_custom_event.h └── meal_pager_storage.c ├── docs ├── README.md └── changelog.md ├── scenes ├── meal_pager_scene_config.h ├── meal_pager_scene.h ├── meal_pager_scene.c ├── meal_pager_scene_startscreen.c ├── meal_pager_scene_set_first_pager.c ├── meal_pager_scene_set_first_station.c ├── meal_pager_scene_set_last_pager.c ├── meal_pager_scene_set_last_station.c ├── meal_pager_scene_transmit.c ├── meal_pager_scene_menu.c └── meal_pager_scene_settings.c ├── application.fam ├── views ├── meal_pager_startscreen.h ├── meal_pager_transmit.h ├── meal_pager_startscreen.c └── meal_pager_transmit.c ├── meal_pager_i.h ├── README.md ├── .clang-format └── meal_pager.c /meal_pager.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "meal_pager_i.h" 3 | -------------------------------------------------------------------------------- /icons/KeySave_24x11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leedave/flipper-zero-meal-pager/HEAD/icons/KeySave_24x11.png -------------------------------------------------------------------------------- /icons/meal_pager_10px.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leedave/flipper-zero-meal-pager/HEAD/icons/meal_pager_10px.png -------------------------------------------------------------------------------- /icons/KeyBackspace_16x9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leedave/flipper-zero-meal-pager/HEAD/icons/KeyBackspace_16x9.png -------------------------------------------------------------------------------- /icons/KeySaveSelected_24x11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leedave/flipper-zero-meal-pager/HEAD/icons/KeySaveSelected_24x11.png -------------------------------------------------------------------------------- /icons/KeyBackspaceSelected_16x9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leedave/flipper-zero-meal-pager/HEAD/icons/KeyBackspaceSelected_16x9.png -------------------------------------------------------------------------------- /helpers/retekess/meal_pager_retekess_td174.h: -------------------------------------------------------------------------------- 1 | 2 | #pragma once 3 | 4 | #include "../../meal_pager_i.h" 5 | #include "../meal_pager_calc.h" 6 | 7 | bool meal_pager_retekess_td174_generate_all(void* context); -------------------------------------------------------------------------------- /helpers/subghz/subghz.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "subghz_i.h" 4 | 5 | typedef struct SubGhz SubGhz; 6 | 7 | SubGhz* subghz_alloc(); 8 | void subghz_free(SubGhz* subghz); 9 | void subghz_send(void* context); -------------------------------------------------------------------------------- /helpers/meal_pager_speaker.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../meal_pager_i.h" 4 | 5 | #define NOTE_INPUT 587.33f 6 | 7 | void meal_pager_play_input_sound(void* context); 8 | void meal_pager_stop_all_sound(void* context); 9 | -------------------------------------------------------------------------------- /helpers/retekess/meal_pager_retekess_t119.h: -------------------------------------------------------------------------------- 1 | 2 | #pragma once 3 | 4 | #include "../../meal_pager_i.h" 5 | #include "../meal_pager_calc.h" 6 | #include "../meal_pager_storage.h" 7 | 8 | bool meal_pager_retekess_t119_generate_all(void* context); -------------------------------------------------------------------------------- /helpers/retekess/meal_pager_retekess_td157.h: -------------------------------------------------------------------------------- 1 | 2 | #pragma once 3 | 4 | #include "../../meal_pager_i.h" 5 | #include "../meal_pager_calc.h" 6 | #include "../meal_pager_storage.h" 7 | 8 | bool meal_pager_retekess_td157_generate_all(void* context); -------------------------------------------------------------------------------- /helpers/retekess/meal_pager_retekess_td165.h: -------------------------------------------------------------------------------- 1 | 2 | #pragma once 3 | 4 | #include "../../meal_pager_i.h" 5 | #include "../meal_pager_calc.h" 6 | #include "../meal_pager_storage.h" 7 | 8 | bool meal_pager_retekess_td165_generate_all(void* context); -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | ## Meal Pager Trigger App 2 | 3 | Can make a range of Restaurant Pagers ring. 4 | 5 | ## Features 6 | - Support for Retekess T119, TD157, TD165, TD174 7 | - Select Range of Station Ids 8 | - Select Range of Pager Ids 9 | 10 | -------------------------------------------------------------------------------- /helpers/meal_pager_haptic.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "../meal_pager_i.h" 5 | 6 | void meal_pager_play_happy_bump(void* context); 7 | 8 | void meal_pager_play_bad_bump(void* context); 9 | 10 | void meal_pager_play_long_bump(void* context); 11 | -------------------------------------------------------------------------------- /helpers/meal_pager_calc.h: -------------------------------------------------------------------------------- 1 | 2 | #pragma once 3 | 4 | #include "../meal_pager_i.h" 5 | 6 | char* encManchester(const char* bits, int mode); 7 | 8 | void uint32ToBinaray(uint32_t number, char* str, int8_t length); 9 | 10 | void reverse(char* str); 11 | 12 | void customConcat(char* dest, const char* src); -------------------------------------------------------------------------------- /helpers/meal_pager_led.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../meal_pager_i.h" 4 | 5 | void meal_pager_blink_start_subghz(Meal_Pager* app); 6 | 7 | void meal_pager_blink_start_compile(Meal_Pager* app); 8 | 9 | void meal_pager_blink_stop(Meal_Pager* app); 10 | 11 | void meal_pager_led_set_rgb(void* context, int red, int green, int blue); 12 | 13 | void meal_pager_led_reset(void* context); 14 | -------------------------------------------------------------------------------- /scenes/meal_pager_scene_config.h: -------------------------------------------------------------------------------- 1 | ADD_SCENE(meal_pager, startscreen, Startscreen) 2 | ADD_SCENE(meal_pager, menu, Menu) 3 | ADD_SCENE(meal_pager, transmit, Transmit) 4 | ADD_SCENE(meal_pager, settings, Settings) 5 | ADD_SCENE(meal_pager, set_first_station, SetFirstStation) 6 | ADD_SCENE(meal_pager, set_last_station, SetLastStation) 7 | ADD_SCENE(meal_pager, set_first_pager, SetFirstPager) 8 | ADD_SCENE(meal_pager, set_last_pager, SetLastPager) -------------------------------------------------------------------------------- /application.fam: -------------------------------------------------------------------------------- 1 | App( 2 | appid="meal_pager", 3 | name="Restaurant Pager Trigger", 4 | apptype=FlipperAppType.EXTERNAL, 5 | entry_point="meal_pager_app", 6 | stack_size=2 * 1024, 7 | fap_icon="icons/meal_pager_10px.png", 8 | fap_icon_assets="icons", 9 | fap_category="Sub-GHz", 10 | fap_version="1.7", 11 | fap_author="leedave", 12 | fap_weburl="https://github.com/leedave/flipper-zero-meal-pager", 13 | fap_description="This app triggers restaurant pagers in a brute force manner, useful to test if devices are still functional.", 14 | ) -------------------------------------------------------------------------------- /helpers/subghz/subghz_error_type.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | /** SubGhzErrorType */ 7 | typedef enum { 8 | SubGhzErrorTypeNoError = 0, /** There are no errors */ 9 | SubGhzErrorTypeParseFile = 10 | 1, /** File parsing error, or wrong file structure, or missing required parameters. more accurate data can be obtained through the debug port */ 11 | SubGhzErrorTypeOnlyRX = 12 | 2, /** Transmission on this frequency is blocked by regional settings */ 13 | SubGhzErrorTypeParserOthers = 3, /** Error in protocol parameters description */ 14 | } SubGhzErrorType; 15 | -------------------------------------------------------------------------------- /views/meal_pager_startscreen.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "../helpers/meal_pager_custom_event.h" 5 | 6 | typedef struct Meal_PagerStartscreen Meal_PagerStartscreen; 7 | 8 | typedef void (*Meal_PagerStartscreenCallback)(Meal_PagerCustomEvent event, void* context); 9 | 10 | void meal_pager_startscreen_set_callback( 11 | Meal_PagerStartscreen* meal_pager_startscreen, 12 | Meal_PagerStartscreenCallback callback, 13 | void* context); 14 | 15 | View* meal_pager_startscreen_get_view(Meal_PagerStartscreen* meal_pager_static); 16 | 17 | Meal_PagerStartscreen* meal_pager_startscreen_alloc(); 18 | 19 | void meal_pager_startscreen_free(Meal_PagerStartscreen* meal_pager_static); -------------------------------------------------------------------------------- /helpers/meal_pager_speaker.c: -------------------------------------------------------------------------------- 1 | #include "meal_pager_speaker.h" 2 | 3 | #define NOTE_INPUT 587.33f 4 | 5 | void meal_pager_play_input_sound(void* context) { 6 | Meal_Pager* app = context; 7 | if(app->speaker != 1) { 8 | return; 9 | } 10 | float volume = 1.0f; 11 | if(furi_hal_speaker_is_mine() || furi_hal_speaker_acquire(30)) { 12 | furi_hal_speaker_start(NOTE_INPUT, volume); 13 | } 14 | } 15 | 16 | void meal_pager_stop_all_sound(void* context) { 17 | Meal_Pager* app = context; 18 | if(app->speaker != 1) { 19 | return; 20 | } 21 | if(furi_hal_speaker_is_mine()) { 22 | furi_hal_speaker_stop(); 23 | furi_hal_speaker_release(); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /helpers/subghz/subghz_txrx_i.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "subghz_txrx.h" 4 | 5 | struct SubGhzTxRx { 6 | SubGhzWorker* worker; 7 | 8 | SubGhzEnvironment* environment; 9 | SubGhzReceiver* receiver; 10 | SubGhzTransmitter* transmitter; 11 | SubGhzProtocolDecoderBase* decoder_result; 12 | FlipperFormat* fff_data; 13 | 14 | SubGhzRadioPreset* preset; 15 | SubGhzSetting* setting; 16 | 17 | uint8_t hopper_timeout; 18 | uint8_t hopper_idx_frequency; 19 | bool is_database_loaded; 20 | SubGhzHopperState hopper_state; 21 | 22 | SubGhzTxRxState txrx_state; 23 | SubGhzSpeakerState speaker_state; 24 | const SubGhzDevice* radio_device; 25 | SubGhzRadioDeviceType radio_device_type; 26 | 27 | SubGhzTxRxNeedSaveCallback need_save_callback; 28 | void* need_save_context; 29 | }; 30 | -------------------------------------------------------------------------------- /views/meal_pager_transmit.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "../helpers/meal_pager_custom_event.h" 5 | 6 | typedef struct Meal_PagerTransmit Meal_PagerTransmit; 7 | 8 | typedef void (*Meal_PagerTransmitCallback)(Meal_PagerCustomEvent event, void* context); 9 | 10 | void meal_pager_transmit_set_callback( 11 | Meal_PagerTransmit* meal_pager_transmit, 12 | Meal_PagerTransmitCallback callback, 13 | void* context); 14 | 15 | void meal_pager_transmit_model_set_sending(Meal_PagerTransmit* instance, uint32_t value); 16 | void meal_pager_transmit_model_set_type(Meal_PagerTransmit* instance, uint32_t type); 17 | void meal_pager_transmit_model_set_station(Meal_PagerTransmit* instance, uint32_t station); 18 | void meal_pager_transmit_model_set_pager(Meal_PagerTransmit* instance, uint32_t pager); 19 | 20 | View* meal_pager_transmit_get_view(Meal_PagerTransmit* meal_pager_static); 21 | 22 | Meal_PagerTransmit* meal_pager_transmit_alloc(); 23 | 24 | void meal_pager_transmit_free(Meal_PagerTransmit* meal_pager_static); -------------------------------------------------------------------------------- /scenes/meal_pager_scene.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | // Generate scene id and total number 6 | #define ADD_SCENE(prefix, name, id) Meal_PagerScene##id, 7 | typedef enum { 8 | #include "meal_pager_scene_config.h" 9 | Meal_PagerSceneNum, 10 | } Meal_PagerScene; 11 | #undef ADD_SCENE 12 | 13 | extern const SceneManagerHandlers meal_pager_scene_handlers; 14 | 15 | // Generate scene on_enter handlers declaration 16 | #define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*); 17 | #include "meal_pager_scene_config.h" 18 | #undef ADD_SCENE 19 | 20 | // Generate scene on_event handlers declaration 21 | #define ADD_SCENE(prefix, name, id) \ 22 | bool prefix##_scene_##name##_on_event(void* context, SceneManagerEvent event); 23 | #include "meal_pager_scene_config.h" 24 | #undef ADD_SCENE 25 | 26 | // Generate scene on_exit handlers declaration 27 | #define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_exit(void* context); 28 | #include "meal_pager_scene_config.h" 29 | #undef ADD_SCENE 30 | -------------------------------------------------------------------------------- /scenes/meal_pager_scene.c: -------------------------------------------------------------------------------- 1 | #include "meal_pager_scene.h" 2 | 3 | // Generate scene on_enter handlers array 4 | #define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter, 5 | void (*const meal_pager_on_enter_handlers[])(void*) = { 6 | #include "meal_pager_scene_config.h" 7 | }; 8 | #undef ADD_SCENE 9 | 10 | // Generate scene on_event handlers array 11 | #define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_event, 12 | bool (*const meal_pager_on_event_handlers[])(void* context, SceneManagerEvent event) = { 13 | #include "meal_pager_scene_config.h" 14 | }; 15 | #undef ADD_SCENE 16 | 17 | // Generate scene on_exit handlers array 18 | #define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_exit, 19 | void (*const meal_pager_on_exit_handlers[])(void* context) = { 20 | #include "meal_pager_scene_config.h" 21 | }; 22 | #undef ADD_SCENE 23 | 24 | // Initialize scene handlers configuration structure 25 | const SceneManagerHandlers meal_pager_scene_handlers = { 26 | .on_enter_handlers = meal_pager_on_enter_handlers, 27 | .on_event_handlers = meal_pager_on_event_handlers, 28 | .on_exit_handlers = meal_pager_on_exit_handlers, 29 | .scene_num = Meal_PagerSceneNum, 30 | }; 31 | -------------------------------------------------------------------------------- /helpers/meal_pager_haptic.c: -------------------------------------------------------------------------------- 1 | #include "meal_pager_haptic.h" 2 | 3 | void meal_pager_play_happy_bump(void* context) { 4 | Meal_Pager* app = context; 5 | if(app->haptic != 1) { 6 | return; 7 | } 8 | notification_message(app->notification, &sequence_set_vibro_on); 9 | furi_thread_flags_wait(0, FuriFlagWaitAny, 20); 10 | notification_message(app->notification, &sequence_reset_vibro); 11 | } 12 | 13 | void meal_pager_play_bad_bump(void* context) { 14 | Meal_Pager* app = context; 15 | if(app->haptic != 1) { 16 | return; 17 | } 18 | notification_message(app->notification, &sequence_set_vibro_on); 19 | furi_thread_flags_wait(0, FuriFlagWaitAny, 100); 20 | notification_message(app->notification, &sequence_reset_vibro); 21 | } 22 | 23 | void meal_pager_play_long_bump(void* context) { 24 | Meal_Pager* app = context; 25 | if(app->haptic != 1) { 26 | return; 27 | } 28 | for(int i = 0; i < 4; i++) { 29 | notification_message(app->notification, &sequence_set_vibro_on); 30 | furi_thread_flags_wait(0, FuriFlagWaitAny, 50); 31 | notification_message(app->notification, &sequence_reset_vibro); 32 | furi_thread_flags_wait(0, FuriFlagWaitAny, 100); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /helpers/meal_pager_storage.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "../meal_pager_i.h" 8 | 9 | #define MEAL_PAGER_SETTINGS_FILE_VERSION 2 10 | #define CONFIG_FILE_DIRECTORY_PATH EXT_PATH("apps_data/meal_pager") 11 | #define MEAL_PAGER_SETTINGS_SAVE_PATH CONFIG_FILE_DIRECTORY_PATH "/meal_pager.conf" 12 | #define MEAL_PAGER_SETTINGS_SAVE_PATH_TMP MEAL_PAGER_SETTINGS_SAVE_PATH ".tmp" 13 | #define MEAL_PAGER_SETTINGS_HEADER "Meal_Pager Config File" 14 | #define MEAL_PAGER_SETTINGS_KEY_PAGER_TYPE "Pager Type" 15 | #define MEAL_PAGER_SETTINGS_KEY_FIRST_STATION "First Station" 16 | #define MEAL_PAGER_SETTINGS_KEY_LAST_STATION "Last Station" 17 | #define MEAL_PAGER_SETTINGS_KEY_FIRST_PAGER "First Pager" 18 | #define MEAL_PAGER_SETTINGS_KEY_LAST_PAGER "Last Pager" 19 | #define MEAL_PAGER_SETTINGS_KEY_REPEATS "Repeats" 20 | #define MEAL_PAGER_SETTINGS_KEY_HAPTIC "Haptic" 21 | #define MEAL_PAGER_SETTINGS_KEY_LED "Led" 22 | #define MEAL_PAGER_SETTINGS_KEY_SPEAKER "Speaker" 23 | #define MEAL_PAGER_SETTINGS_KEY_SAVE_SETTINGS "SaveSettings" 24 | #define MEAL_PAGER_TMP_FILE CONFIG_FILE_DIRECTORY_PATH "/tmp.sub" 25 | #define MEAL_PAGER_SUBGHZ_FILE_TYPE "Flipper SubGhz RAW File" 26 | #define MEAL_PAGER_SUBGHZ_FILE_VERSION 1 27 | #define MEAL_PAGER_SUBGHZ_FILE_FREQUENCY "433920000" 28 | #define MEAL_PAGER_SUBGHZ_FILE_ALT_FREQUENCY "433889000" 29 | #define MEAL_PAGER_SUBGHZ_FILE_PRESET "FuriHalSubGhzPresetOok650Async" 30 | #define MEAL_PAGER_SUBGHZ_FILE_Protocol "RAW" 31 | 32 | bool meal_pager_save_subghz_buffer_file_start( 33 | void* context, 34 | FlipperFormat* ff, 35 | Storage* storage, 36 | char* frequency); 37 | 38 | void meal_pager_save_subghz_buffer_stop(void* context, FlipperFormat* ff); 39 | 40 | void meal_pager_save_settings(void* context); 41 | void meal_pager_read_settings(void* context); 42 | void meal_pager_set_max_values(void* context); -------------------------------------------------------------------------------- /helpers/gui/int_input.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file int_input.h 3 | * GUI: Integer string keyboard view module API 4 | */ 5 | 6 | #pragma once 7 | 8 | #include 9 | 10 | #ifdef __cplusplus 11 | extern "C" { 12 | #endif 13 | 14 | /** Int input anonymous structure */ 15 | typedef struct IntInput IntInput; 16 | 17 | /** callback that is executed on save button press */ 18 | typedef void (*IntInputCallback)(void* context); 19 | 20 | /** callback that is executed when byte buffer is changed */ 21 | typedef void (*IntChangedCallback)(void* context); 22 | 23 | /** Allocate and initialize Int input. This Int input is used to enter Ints. 24 | * 25 | * @return IntInput instance pointer 26 | */ 27 | IntInput* int_input_alloc(); 28 | 29 | /** Deinitialize and free byte input 30 | * 31 | * @param int_input Int input instance 32 | */ 33 | void int_input_free(IntInput* int_input); 34 | 35 | /** Get byte input view 36 | * 37 | * @param int_input byte input instance 38 | * 39 | * @return View instance that can be used for embedding 40 | */ 41 | View* int_input_get_view(IntInput* int_input); 42 | 43 | /** Set byte input result callback 44 | * 45 | * @param int_input byte input instance 46 | * @param input_callback input callback fn 47 | * @param changed_callback changed callback fn 48 | * @param callback_context callback context 49 | * @param text_buffer buffer to use 50 | * @param text_buffer_size buffer length 51 | * @param clear_default_text clear previous entry 52 | */ 53 | 54 | void int_input_set_result_callback( 55 | IntInput* int_input, 56 | IntInputCallback input_callback, 57 | void* callback_context, 58 | char* text_buffer, 59 | size_t text_buffer_size, 60 | bool clear_default_text); 61 | 62 | /** Set byte input header text 63 | * 64 | * @param int_input byte input instance 65 | * @param text text to be shown 66 | */ 67 | void int_input_set_header_text(IntInput* int_input, const char* text); 68 | 69 | #ifdef __cplusplus 70 | } 71 | #endif 72 | -------------------------------------------------------------------------------- /helpers/subghz/subghz_types.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | /** SubGhzNotification state */ 7 | typedef enum { 8 | SubGhzNotificationStateStarting, 9 | SubGhzNotificationStateIDLE, 10 | SubGhzNotificationStateTx, 11 | SubGhzNotificationStateRx, 12 | SubGhzNotificationStateRxDone, 13 | } SubGhzNotificationState; 14 | 15 | /** SubGhzTxRx state */ 16 | typedef enum { 17 | SubGhzTxRxStateIDLE, 18 | SubGhzTxRxStateRx, 19 | SubGhzTxRxStateTx, 20 | SubGhzTxRxStateSleep, 21 | } SubGhzTxRxState; 22 | 23 | /** SubGhzHopperState state */ 24 | typedef enum { 25 | SubGhzHopperStateOFF, 26 | SubGhzHopperStateRunnig, 27 | SubGhzHopperStatePause, 28 | SubGhzHopperStateRSSITimeOut, 29 | } SubGhzHopperState; 30 | 31 | /** SubGhzSpeakerState state */ 32 | typedef enum { 33 | SubGhzSpeakerStateDisable, 34 | SubGhzSpeakerStateShutdown, 35 | SubGhzSpeakerStateEnable, 36 | } SubGhzSpeakerState; 37 | 38 | /** SubGhzRadioDeviceType */ 39 | typedef enum { 40 | SubGhzRadioDeviceTypeAuto, 41 | SubGhzRadioDeviceTypeInternal, 42 | SubGhzRadioDeviceTypeExternalCC1101, 43 | } SubGhzRadioDeviceType; 44 | 45 | /** SubGhzRxKeyState state */ 46 | typedef enum { 47 | SubGhzRxKeyStateIDLE, 48 | SubGhzRxKeyStateNoSave, 49 | SubGhzRxKeyStateNeedSave, 50 | SubGhzRxKeyStateBack, 51 | SubGhzRxKeyStateStart, 52 | SubGhzRxKeyStateAddKey, 53 | SubGhzRxKeyStateExit, 54 | SubGhzRxKeyStateRAWLoad, 55 | SubGhzRxKeyStateRAWSave, 56 | } SubGhzRxKeyState; 57 | 58 | /** SubGhzLoadKeyState state */ 59 | typedef enum { 60 | SubGhzLoadKeyStateUnknown, 61 | SubGhzLoadKeyStateOK, 62 | SubGhzLoadKeyStateParseErr, 63 | SubGhzLoadKeyStateProtocolDescriptionErr, 64 | } SubGhzLoadKeyState; 65 | 66 | /** SubGhzLock */ 67 | typedef enum { 68 | SubGhzLockOff, 69 | SubGhzLockOn, 70 | } SubGhzLock; 71 | 72 | /** SubGhz load type file */ 73 | typedef enum { 74 | SubGhzLoadTypeFileNoLoad, 75 | SubGhzLoadTypeFileKey, 76 | SubGhzLoadTypeFileRaw, 77 | } SubGhzLoadTypeFile; 78 | -------------------------------------------------------------------------------- /docs/changelog.md: -------------------------------------------------------------------------------- 1 | ## v1.8 2 | - Compatibility Update for OFW 0.103.1-rc and above 3 | 4 | ## v1.7 5 | - Added code refactors from xMasterX and WillyJL 6 | - Minor update of Readme 7 | 8 | ## v1.6 9 | - Fix crashes on exit on fw 0.100.3 10 | 11 | ## v1.5 12 | - Support for ufbt compilation (assets fixed) 13 | 14 | ## v1.4 15 | - Fixed some restrictions that prevented build under 0.99.1 16 | 17 | ## v1.3 18 | - Fixed some memory leaks 19 | - Changed Subghz Protocol Registry to Fipper API conform variant 20 | - Version number in Start Screen 21 | - Code formatting 22 | 23 | ## v1.2 24 | - Fixed Memory bug in Last Station UI 25 | - Added auto-correction when entries in First/Last station/pager are out of range 26 | 27 | ## v1.1 28 | - Created a new UI Input View as FW does not supply one for numbers 29 | - New UI to Set First Station 30 | - New UI to Set Last Station 31 | - New UI to Set First Page 32 | - New UI to Set Last Pager 33 | - Removed Vibro/Sound settings as not used 34 | 35 | Known issues 36 | - After setting last station, the settings view is weird. Some kindo of memory bug. But data remains correct. 37 | - Extensive use can cause crashes, must be some memory leak left 38 | 39 | 40 | ## v1.0 41 | - Added support for TD174 42 | 43 | ## v0.9 44 | - Added support for TD165 45 | 46 | ## v0.8 47 | - Added Repeats feature 48 | - Repeats configuration in settings 49 | - Usage of repeats in T119 50 | - Usage of repeats in TD157 51 | 52 | ## v0.7 53 | 54 | - Added support for TD157 55 | - Some log & comment cleaning 56 | - Moved common code to new location 57 | - Removed testing assets 58 | - Fixed bad menu label (from "scene1" to "send data") 59 | 60 | ## v0.6 61 | 62 | - Display when data is being generated and when it is being sent 63 | - Fixed issue where callbacks were sent infitiv when leaving the transmission page 64 | - Fixed blinking remaining when leaving transmission page prematurely 65 | - Set default last station Number lower to prevent crashes on first try 66 | 67 | 68 | ## v0.5 69 | 70 | Compiled .sub data is read out and sent via SubGhz. Currently only support for T119. 71 | 72 | ## v0.1 73 | 74 | Can now generate a temporary .sub file for Retekess T119 Pager triggers. Must still be run via SubGhz App -------------------------------------------------------------------------------- /scenes/meal_pager_scene_startscreen.c: -------------------------------------------------------------------------------- 1 | #include "../meal_pager_i.h" 2 | #include "../helpers/meal_pager_custom_event.h" 3 | #include "../views/meal_pager_startscreen.h" 4 | 5 | void meal_pager_scene_startscreen_callback(Meal_PagerCustomEvent event, void* context) { 6 | furi_assert(context); 7 | Meal_Pager* app = context; 8 | view_dispatcher_send_custom_event(app->view_dispatcher, event); 9 | } 10 | 11 | void meal_pager_scene_startscreen_on_enter(void* context) { 12 | furi_assert(context); 13 | Meal_Pager* app = context; 14 | meal_pager_startscreen_set_callback( 15 | app->meal_pager_startscreen, meal_pager_scene_startscreen_callback, app); 16 | view_dispatcher_switch_to_view(app->view_dispatcher, Meal_PagerViewIdStartscreen); 17 | } 18 | 19 | bool meal_pager_scene_startscreen_on_event(void* context, SceneManagerEvent event) { 20 | Meal_Pager* app = context; 21 | bool consumed = false; 22 | 23 | if(event.type == SceneManagerEventTypeCustom) { 24 | switch(event.event) { 25 | case Meal_PagerCustomEventStartscreenLeft: 26 | case Meal_PagerCustomEventStartscreenRight: 27 | break; 28 | case Meal_PagerCustomEventStartscreenUp: 29 | case Meal_PagerCustomEventStartscreenDown: 30 | break; 31 | case Meal_PagerCustomEventStartscreenOk: 32 | scene_manager_next_scene(app->scene_manager, Meal_PagerSceneMenu); 33 | consumed = true; 34 | break; 35 | case Meal_PagerCustomEventStartscreenBack: 36 | notification_message(app->notification, &sequence_reset_red); 37 | notification_message(app->notification, &sequence_reset_green); 38 | notification_message(app->notification, &sequence_reset_blue); 39 | if(!scene_manager_search_and_switch_to_previous_scene( 40 | app->scene_manager, Meal_PagerSceneStartscreen)) { 41 | scene_manager_stop(app->scene_manager); 42 | view_dispatcher_stop(app->view_dispatcher); 43 | } 44 | consumed = true; 45 | break; 46 | } 47 | } 48 | 49 | return consumed; 50 | } 51 | 52 | void meal_pager_scene_startscreen_on_exit(void* context) { 53 | Meal_Pager* app = context; 54 | UNUSED(app); 55 | } -------------------------------------------------------------------------------- /helpers/meal_pager_calc.c: -------------------------------------------------------------------------------- 1 | #include "meal_pager_calc.h" 2 | 3 | void customConcat(char* dest, const char* src) { 4 | // Find the end of the destination string 5 | while(*dest != '\0') { 6 | dest++; 7 | } 8 | 9 | // Copy characters from src to dest 10 | while(*src != '\0') { 11 | *dest = *src; 12 | dest++; 13 | src++; 14 | } 15 | 16 | // Null-terminate the concatenated string 17 | *dest = '\0'; 18 | } 19 | 20 | char* encManchester(const char* bits, int mode) { 21 | // Allocate memory for the result string 22 | char* res = (char*)malloc((strlen(bits) * 2 + 1) * sizeof(char)); 23 | 24 | int index = 0; 25 | for(int i = 0; bits[i] != '\0'; i++) { 26 | char c = bits[i]; 27 | if(c == '0') { 28 | if(mode) { 29 | res[index++] = '1'; 30 | res[index++] = '0'; 31 | } else { 32 | res[index++] = '0'; 33 | res[index++] = '1'; 34 | } 35 | } else if(c == '1') { 36 | if(mode) { 37 | res[index++] = '0'; 38 | res[index++] = '1'; 39 | } else { 40 | res[index++] = '1'; 41 | res[index++] = '0'; 42 | } 43 | } else { 44 | // Handle 'EE' case (error) 45 | res[index++] = 'E'; 46 | res[index++] = 'E'; 47 | } 48 | } 49 | 50 | // Null-terminate the result string 51 | res[index] = '\0'; 52 | 53 | return res; 54 | } 55 | 56 | void uint32ToBinaray(uint32_t number, char* str, int8_t length) { 57 | int i = 0; 58 | length--; // count length without 0 59 | for(i = length; i >= 0; i--) { 60 | // Bitwise AND extration of the i-th bit 61 | int bit = (number >> i) & 1; 62 | // convert the bit to a character of 1 or 0 63 | str[length - i] = bit + '0'; 64 | } 65 | // Terminate the string 66 | str[length + 1] = '\0'; 67 | } 68 | 69 | void reverse(char* str) { 70 | int length = strlen(str); 71 | int start = 0; 72 | int end = length - 1; 73 | while(start < end) { 74 | char temp = str[start]; 75 | str[start] = str[end]; 76 | str[end] = temp; 77 | start++; 78 | end--; 79 | } 80 | } -------------------------------------------------------------------------------- /helpers/meal_pager_led.c: -------------------------------------------------------------------------------- 1 | #include "meal_pager_led.h" 2 | 3 | void meal_pager_blink_start_subghz(Meal_Pager* app) { 4 | furi_assert(app); 5 | if(app->led == 1) { 6 | notification_message(app->notification, &sequence_blink_stop); 7 | notification_message(app->notification, &sequence_blink_start_magenta); 8 | } 9 | } 10 | 11 | void meal_pager_blink_start_compile(Meal_Pager* app) { 12 | furi_assert(app); 13 | if(app->led == 1) { 14 | notification_message(app->notification, &sequence_blink_stop); 15 | notification_message(app->notification, &sequence_blink_start_yellow); 16 | } 17 | } 18 | 19 | void meal_pager_blink_stop(Meal_Pager* app) { 20 | furi_assert(app); 21 | notification_message(app->notification, &sequence_blink_stop); 22 | } 23 | 24 | void meal_pager_led_set_rgb(void* context, int red, int green, int blue) { 25 | Meal_Pager* app = context; 26 | if(app->led != 1) { 27 | return; 28 | } 29 | NotificationMessage notification_led_message_1; 30 | notification_led_message_1.type = NotificationMessageTypeLedRed; 31 | NotificationMessage notification_led_message_2; 32 | notification_led_message_2.type = NotificationMessageTypeLedGreen; 33 | NotificationMessage notification_led_message_3; 34 | notification_led_message_3.type = NotificationMessageTypeLedBlue; 35 | 36 | notification_led_message_1.data.led.value = red; 37 | notification_led_message_2.data.led.value = green; 38 | notification_led_message_3.data.led.value = blue; 39 | const NotificationSequence notification_sequence = { 40 | ¬ification_led_message_1, 41 | ¬ification_led_message_2, 42 | ¬ification_led_message_3, 43 | &message_do_not_reset, 44 | NULL, 45 | }; 46 | notification_message(app->notification, ¬ification_sequence); 47 | furi_thread_flags_wait( 48 | 0, FuriFlagWaitAny, 10); //Delay, prevent removal from RAM before LED value set 49 | } 50 | 51 | void meal_pager_led_reset(void* context) { 52 | Meal_Pager* app = context; 53 | notification_message(app->notification, &sequence_reset_red); 54 | notification_message(app->notification, &sequence_reset_green); 55 | notification_message(app->notification, &sequence_reset_blue); 56 | 57 | furi_thread_flags_wait( 58 | 0, FuriFlagWaitAny, 300); //Delay, prevent removal from RAM before LED value set 59 | } 60 | -------------------------------------------------------------------------------- /helpers/subghz/subghz.c: -------------------------------------------------------------------------------- 1 | /* Reduced variant of the Flipper Zero SubGhz Class */ 2 | 3 | #include "subghz_i.h" 4 | #include "../../helpers/meal_pager_custom_event.h" 5 | #include "../../helpers/meal_pager_led.h" 6 | //#include "../meal_pager_storage.h" 7 | 8 | SubGhz* subghz_alloc() { 9 | SubGhz* subghz = malloc(sizeof(SubGhz)); 10 | 11 | subghz->file_path = furi_string_alloc(); 12 | 13 | subghz->txrx = subghz_txrx_alloc(); 14 | 15 | return subghz; 16 | } 17 | 18 | void subghz_free(SubGhz* subghz) { 19 | //TxRx 20 | subghz_txrx_free(subghz->txrx); 21 | 22 | // Furi strings 23 | furi_string_free(subghz->file_path); 24 | 25 | // The rest 26 | free(subghz); 27 | } 28 | 29 | void subghz_scene_transmit_callback_end_tx(void* context) { 30 | furi_assert(context); 31 | //UNUSED(context); 32 | FURI_LOG_D(TAG, "callback end"); 33 | Meal_Pager* app = context; 34 | view_dispatcher_send_custom_event( 35 | app->view_dispatcher, Meal_PagerCustomEventViewTransmitterSendStop); 36 | } 37 | 38 | void subghz_send(void* context) { 39 | //UNUSED(context); 40 | Meal_Pager* app = context; 41 | //SubGhz* subghz = subghz_alloc(); 42 | 43 | FURI_LOG_D(TAG, "loading protocol from file"); 44 | subghz_load_protocol_from_file(app->subghz); 45 | 46 | /*Storage* storage = furi_record_open(RECORD_STORAGE); 47 | FlipperFormat* ff = flipper_format_file_alloc(storage); 48 | 49 | if(!flipper_format_file_open_existing(ff, MEAL_PAGER_TMP_FILE)) { 50 | //totp_close_config_file(fff_file); 51 | FURI_LOG_E(TAG, "Error reading Temp File %s", MEAL_PAGER_TMP_FILE); 52 | furi_record_close(RECORD_STORAGE); 53 | return; 54 | }*/ 55 | 56 | //subghz_txrx_tx_start(subghz->txrx, ff); 57 | 58 | FURI_LOG_D(TAG, "Starting Transmission"); 59 | subghz_txrx_tx_start( 60 | app->subghz->txrx, 61 | subghz_txrx_get_fff_data(app->subghz->txrx)); //Seems like it must be done this way 62 | 63 | FURI_LOG_D(TAG, "setting sugbhz raw file encoder worker callback"); 64 | subghz_txrx_set_raw_file_encoder_worker_callback_end( 65 | app->subghz->txrx, subghz_scene_transmit_callback_end_tx, app); 66 | app->state_notifications = SubGhzNotificationStateTx; 67 | 68 | /*flipper_format_rewind(ff); 69 | flipper_format_file_close(ff); 70 | flipper_format_free(ff); 71 | 72 | furi_record_close(RECORD_STORAGE);*/ 73 | 74 | //subghz_free(subghz); 75 | FURI_LOG_D(TAG, "Finished Transmitting"); 76 | //meal_pager_blink_stop(app); 77 | } -------------------------------------------------------------------------------- /helpers/meal_pager_custom_event.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | typedef enum { 4 | Meal_PagerCustomEventStartscreenUp, 5 | Meal_PagerCustomEventStartscreenDown, 6 | Meal_PagerCustomEventStartscreenLeft, 7 | Meal_PagerCustomEventStartscreenRight, 8 | Meal_PagerCustomEventStartscreenOk, 9 | Meal_PagerCustomEventStartscreenBack, 10 | Meal_PagerCustomEventTransmitUp, 11 | Meal_PagerCustomEventTransmitDown, 12 | Meal_PagerCustomEventTransmitLeft, 13 | Meal_PagerCustomEventTransmitRight, 14 | Meal_PagerCustomEventTransmitOk, 15 | Meal_PagerCustomEventTransmitBack, 16 | Meal_PagerCustomEventScene2Up, 17 | Meal_PagerCustomEventScene2Down, 18 | Meal_PagerCustomEventScene2Left, 19 | Meal_PagerCustomEventScene2Right, 20 | Meal_PagerCustomEventScene2Ok, 21 | Meal_PagerCustomEventScene2Back, 22 | Meal_PagerCustomEventViewTransmitterBack, 23 | Meal_PagerCustomEventViewTransmitterSendStart, 24 | Meal_PagerCustomEventViewTransmitterSendStop, 25 | Meal_PagerCustomEventViewTransmitterError, 26 | Meal_PagerCustomerEventIntInput, 27 | Meal_PagerCustomEventViewIntInputOk, 28 | } Meal_PagerCustomEvent; 29 | 30 | enum Meal_PagerCustomEventType { 31 | // Reserve first 100 events for button types and indexes, starting from 0 32 | Meal_PagerCustomEventMenuVoid, 33 | Meal_PagerCustomEventMenuSelected, 34 | }; 35 | 36 | #pragma pack(push, 1) 37 | typedef union { 38 | uint32_t packed_value; 39 | struct { 40 | uint16_t type; 41 | int16_t value; 42 | } content; 43 | } Meal_PagerCustomEventMenu; 44 | #pragma pack(pop) 45 | 46 | static inline uint32_t meal_pager_custom_menu_event_pack(uint16_t type, int16_t value) { 47 | Meal_PagerCustomEventMenu event = {.content = {.type = type, .value = value}}; 48 | return event.packed_value; 49 | } 50 | static inline void 51 | meal_pager_custom_menu_event_unpack(uint32_t packed_value, uint16_t* type, int16_t* value) { 52 | Meal_PagerCustomEventMenu event = {.packed_value = packed_value}; 53 | if(type) *type = event.content.type; 54 | if(value) *value = event.content.value; 55 | } 56 | 57 | static inline uint16_t meal_pager_custom_menu_event_get_type(uint32_t packed_value) { 58 | uint16_t type; 59 | meal_pager_custom_menu_event_unpack(packed_value, &type, NULL); 60 | return type; 61 | } 62 | 63 | static inline int16_t meal_pager_custom_menu_event_get_value(uint32_t packed_value) { 64 | int16_t value; 65 | meal_pager_custom_menu_event_unpack(packed_value, NULL, &value); 66 | return value; 67 | } -------------------------------------------------------------------------------- /scenes/meal_pager_scene_set_first_pager.c: -------------------------------------------------------------------------------- 1 | #include "../meal_pager_i.h" 2 | #include "../helpers/meal_pager_custom_event.h" 3 | #include "../helpers/meal_pager_led.h" 4 | #include 5 | 6 | void meal_pager_set_first_pager_callback(void* context) { 7 | furi_assert(context); 8 | Meal_Pager* app = context; 9 | view_dispatcher_send_custom_event(app->view_dispatcher, Meal_PagerCustomerEventIntInput); 10 | } 11 | 12 | void meal_pager_scene_set_first_pager_on_enter(void* context) { 13 | furi_assert(context); 14 | Meal_Pager* app = context; 15 | IntInput* int_input = app->int_input; 16 | size_t enter_name_length = 5; 17 | meal_pager_set_max_values(app); 18 | char* str = "Set First Pager (0 - 999)"; 19 | const char* constStr = str; 20 | snprintf(str, 36, "Set First Pager (0 - %lu)", app->max_pager); 21 | 22 | int_input_set_header_text(int_input, constStr); 23 | 24 | int_input_set_result_callback( 25 | int_input, 26 | meal_pager_set_first_pager_callback, 27 | context, 28 | app->text_store[2], 29 | enter_name_length, 30 | false); 31 | 32 | view_dispatcher_switch_to_view(app->view_dispatcher, Meal_PagerViewIdIntInput); 33 | } 34 | 35 | bool meal_pager_scene_set_first_pager_on_event(void* context, SceneManagerEvent event) { 36 | Meal_Pager* app = context; 37 | bool consumed = false; 38 | 39 | if(event.type == SceneManagerEventTypeBack) { 40 | scene_manager_previous_scene(app->scene_manager); 41 | return true; 42 | } else if(event.type == SceneManagerEventTypeCustom) { 43 | app->first_pager = atoi(app->text_store[2]); 44 | if(app->first_pager > app->max_pager) { 45 | app->first_pager = app->max_pager; 46 | snprintf(app->text_store[2], sizeof(app->text_store[2]), "%lu", app->first_pager); 47 | } 48 | app->first_pager_char = app->text_store[2]; 49 | scene_manager_previous_scene(app->scene_manager); 50 | return true; 51 | } else if(event.type == SceneManagerEventTypeTick) { 52 | if(app->state_notifications == SubGhzNotificationStateTx) { 53 | app->state_notifications = SubGhzNotificationStateIDLE; 54 | subghz_txrx_stop(app->subghz->txrx); 55 | meal_pager_blink_stop(app); 56 | meal_pager_transmit_model_set_sending(app->meal_pager_transmit, 0); 57 | } 58 | return true; 59 | } 60 | 61 | return consumed; 62 | } 63 | 64 | void meal_pager_scene_set_first_pager_on_exit(void* context) { 65 | Meal_Pager* app = context; 66 | UNUSED(app); 67 | } -------------------------------------------------------------------------------- /scenes/meal_pager_scene_set_first_station.c: -------------------------------------------------------------------------------- 1 | #include "../meal_pager_i.h" 2 | #include "../helpers/meal_pager_custom_event.h" 3 | #include "../helpers/meal_pager_led.h" 4 | #include 5 | 6 | void meal_pager_set_first_station_callback(void* context) { 7 | furi_assert(context); 8 | Meal_Pager* app = context; 9 | view_dispatcher_send_custom_event(app->view_dispatcher, Meal_PagerCustomerEventIntInput); 10 | } 11 | 12 | void meal_pager_scene_set_first_station_on_enter(void* context) { 13 | furi_assert(context); 14 | Meal_Pager* app = context; 15 | IntInput* int_input = app->int_input; 16 | size_t enter_name_length = 5; 17 | meal_pager_set_max_values(app); 18 | char* str = "Set First Station (0 - 9999)"; 19 | const char* constStr = str; 20 | snprintf(str, 36, "Set First Station (0 - %lu)", app->max_station); 21 | 22 | int_input_set_header_text(int_input, constStr); 23 | 24 | int_input_set_result_callback( 25 | int_input, 26 | meal_pager_set_first_station_callback, 27 | context, 28 | app->text_store[0], 29 | enter_name_length, 30 | false); 31 | 32 | view_dispatcher_switch_to_view(app->view_dispatcher, Meal_PagerViewIdIntInput); 33 | } 34 | 35 | bool meal_pager_scene_set_first_station_on_event(void* context, SceneManagerEvent event) { 36 | Meal_Pager* app = context; 37 | bool consumed = false; 38 | 39 | if(event.type == SceneManagerEventTypeBack) { 40 | scene_manager_previous_scene(app->scene_manager); 41 | return true; 42 | } else if(event.type == SceneManagerEventTypeCustom) { 43 | app->first_station = atoi(app->text_store[0]); 44 | if(app->first_station > app->max_station) { 45 | app->first_station = app->max_station; 46 | snprintf(app->text_store[0], sizeof(app->text_store[0]), "%lu", app->first_station); 47 | } 48 | app->first_station_char = app->text_store[0]; 49 | scene_manager_previous_scene(app->scene_manager); 50 | return true; 51 | } else if(event.type == SceneManagerEventTypeTick) { 52 | if(app->state_notifications == SubGhzNotificationStateTx) { 53 | app->state_notifications = SubGhzNotificationStateIDLE; 54 | subghz_txrx_stop(app->subghz->txrx); 55 | meal_pager_blink_stop(app); 56 | meal_pager_transmit_model_set_sending(app->meal_pager_transmit, 0); 57 | } 58 | return true; 59 | } 60 | 61 | return consumed; 62 | } 63 | 64 | void meal_pager_scene_set_first_station_on_exit(void* context) { 65 | Meal_Pager* app = context; 66 | UNUSED(app); 67 | } -------------------------------------------------------------------------------- /helpers/subghz/subghz_i.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "subghz_types.h" 4 | #include "subghz_error_type.h" 5 | #include 6 | #include "subghz.h" 7 | #include "../meal_pager_storage.h" 8 | 9 | /*#include "views/receiver.h" 10 | #include "views/transmitter.h" 11 | #include "views/subghz_frequency_analyzer.h" 12 | #include "views/subghz_read_raw.h" 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | #include 25 | 26 | #include "subghz_history.h" 27 | 28 | #include 29 | #include 30 | 31 | #include "rpc/rpc_app.h" 32 | 33 | #include "helpers/subghz_threshold_rssi.h" 34 | 35 | 36 | */ 37 | #include "subghz_txrx.h" 38 | 39 | #define SUBGHZ_MAX_LEN_NAME 64 40 | 41 | typedef struct SubGhz SubGhz; 42 | 43 | struct SubGhz { 44 | SubGhzTxRx* txrx; 45 | FuriString* file_path; 46 | //FuriString* file_path_tmp; 47 | //char file_name_tmp[SUBGHZ_MAX_LEN_NAME]; // just left it in to make the object not empty 48 | //SubGhzNotificationState state_notifications; 49 | 50 | /*SubGhzViewReceiver* subghz_receiver; 51 | SubGhzViewTransmitter* subghz_transmitter; 52 | 53 | SubGhzFrequencyAnalyzer* subghz_frequency_analyzer; 54 | SubGhzReadRAW* subghz_read_raw;*/ 55 | 56 | //SubGhzProtocolFlag filter; 57 | //FuriString* error_str; 58 | //SubGhzLock lock; 59 | //SubGhzThresholdRssi* threshold_rssi; 60 | //SubGhzRxKeyState rx_key_state; 61 | //SubGhzHistory* history; 62 | SubGhzLoadTypeFile load_type_file; 63 | //void* rpc_ctx; 64 | }; 65 | 66 | //void subghz_set_default_preset(SubGhz* subghz); 67 | //void subghz_blink_start(SubGhz* subghz); 68 | //void subghz_blink_stop(SubGhz* subghz); 69 | 70 | //bool subghz_tx_start(SubGhz* subghz, FlipperFormat* flipper_format); 71 | //void subghz_dialog_message_show_only_rx(SubGhz* subghz); 72 | 73 | bool subghz_key_load(SubGhz* subghz, const char* file_path); //, bool show_dialog); 74 | bool subghz_load_protocol_from_file(SubGhz* subghz); 75 | //bool subghz_file_available(SubGhz* subghz); 76 | //SubGhzLoadTypeFile subghz_get_load_type_file(SubGhz* subghz); 77 | 78 | //void subghz_lock(SubGhz* subghz); 79 | //void subghz_unlock(SubGhz* subghz); 80 | //bool subghz_is_locked(SubGhz* subghz); 81 | 82 | //void subghz_rx_key_state_set(SubGhz* subghz, SubGhzRxKeyState state); 83 | //SubGhzRxKeyState subghz_rx_key_state_get(SubGhz* subghz); 84 | -------------------------------------------------------------------------------- /scenes/meal_pager_scene_set_last_pager.c: -------------------------------------------------------------------------------- 1 | #include "../meal_pager_i.h" 2 | #include "../helpers/meal_pager_custom_event.h" 3 | #include "../helpers/meal_pager_led.h" 4 | #include 5 | 6 | void meal_pager_set_last_pager_callback(void* context) { 7 | furi_assert(context); 8 | Meal_Pager* app = context; 9 | view_dispatcher_send_custom_event(app->view_dispatcher, Meal_PagerCustomerEventIntInput); 10 | } 11 | 12 | void meal_pager_scene_set_last_pager_on_enter(void* context) { 13 | furi_assert(context); 14 | Meal_Pager* app = context; 15 | IntInput* int_input = app->int_input; 16 | size_t enter_name_length = 5; 17 | meal_pager_set_max_values(app); 18 | char* str = "Set Last Pager (0 - 999)"; 19 | const char* constStr = str; 20 | snprintf(str, 36, "Set Last Pager (%lu - %lu)", app->first_pager, app->max_pager); 21 | 22 | int_input_set_header_text(int_input, constStr); 23 | 24 | int_input_set_result_callback( 25 | int_input, 26 | meal_pager_set_last_pager_callback, 27 | context, 28 | app->text_store[3], 29 | enter_name_length, 30 | false); 31 | 32 | view_dispatcher_switch_to_view(app->view_dispatcher, Meal_PagerViewIdIntInput); 33 | } 34 | 35 | bool meal_pager_scene_set_last_pager_on_event(void* context, SceneManagerEvent event) { 36 | Meal_Pager* app = context; 37 | bool consumed = false; 38 | 39 | if(event.type == SceneManagerEventTypeBack) { 40 | scene_manager_previous_scene(app->scene_manager); 41 | return true; 42 | } else if(event.type == SceneManagerEventTypeCustom) { 43 | app->last_pager = atoi(app->text_store[3]); 44 | if(app->last_pager > app->max_pager) { 45 | app->last_pager = app->max_pager; 46 | snprintf(app->text_store[3], sizeof(app->text_store[3]), "%lu", app->last_pager); 47 | } 48 | if(app->last_pager < app->first_pager) { 49 | app->last_pager = app->first_pager; 50 | snprintf(app->text_store[3], sizeof(app->text_store[3]), "%lu", app->last_pager); 51 | } 52 | app->last_pager_char = app->text_store[3]; 53 | scene_manager_previous_scene(app->scene_manager); 54 | return true; 55 | } else if(event.type == SceneManagerEventTypeTick) { 56 | if(app->state_notifications == SubGhzNotificationStateTx) { 57 | app->state_notifications = SubGhzNotificationStateIDLE; 58 | subghz_txrx_stop(app->subghz->txrx); 59 | meal_pager_blink_stop(app); 60 | meal_pager_transmit_model_set_sending(app->meal_pager_transmit, 0); 61 | } 62 | return true; 63 | } 64 | 65 | return consumed; 66 | } 67 | 68 | void meal_pager_scene_set_last_pager_on_exit(void* context) { 69 | Meal_Pager* app = context; 70 | UNUSED(app); 71 | } -------------------------------------------------------------------------------- /scenes/meal_pager_scene_set_last_station.c: -------------------------------------------------------------------------------- 1 | #include "../meal_pager_i.h" 2 | #include "../helpers/meal_pager_custom_event.h" 3 | #include "../helpers/meal_pager_led.h" 4 | #include 5 | 6 | void meal_pager_set_last_station_callback(void* context) { 7 | furi_assert(context); 8 | Meal_Pager* app = context; 9 | view_dispatcher_send_custom_event(app->view_dispatcher, Meal_PagerCustomerEventIntInput); 10 | } 11 | 12 | void meal_pager_scene_set_last_station_on_enter(void* context) { 13 | furi_assert(context); 14 | Meal_Pager* app = context; 15 | IntInput* int_input = app->int_input; 16 | size_t enter_name_length = 5; 17 | meal_pager_set_max_values(app); 18 | char* str = "Set Last Station (0 - 9999)"; 19 | const char* constStr = str; 20 | snprintf(str, 36, "Set Last Station (%lu - %lu)", app->first_station, app->max_station); 21 | 22 | int_input_set_header_text(int_input, constStr); 23 | 24 | int_input_set_result_callback( 25 | int_input, 26 | meal_pager_set_last_station_callback, 27 | context, 28 | app->text_store[1], 29 | enter_name_length, 30 | false); 31 | 32 | view_dispatcher_switch_to_view(app->view_dispatcher, Meal_PagerViewIdIntInput); 33 | } 34 | 35 | bool meal_pager_scene_set_last_station_on_event(void* context, SceneManagerEvent event) { 36 | Meal_Pager* app = context; 37 | bool consumed = false; 38 | 39 | if(event.type == SceneManagerEventTypeBack) { 40 | scene_manager_previous_scene(app->scene_manager); 41 | return true; 42 | } else if(event.type == SceneManagerEventTypeCustom) { 43 | app->last_station = atoi(app->text_store[1]); 44 | if(app->last_station > app->max_station) { 45 | app->last_station = app->max_station; 46 | snprintf(app->text_store[1], sizeof(app->text_store[1]), "%lu", app->last_station); 47 | } 48 | if(app->last_station < app->first_station) { 49 | app->last_station = app->first_station; 50 | snprintf(app->text_store[1], sizeof(app->text_store[1]), "%lu", app->last_station); 51 | } 52 | app->last_station_char = app->text_store[1]; 53 | scene_manager_previous_scene(app->scene_manager); 54 | return true; 55 | } else if(event.type == SceneManagerEventTypeTick) { 56 | if(app->state_notifications == SubGhzNotificationStateTx) { 57 | app->state_notifications = SubGhzNotificationStateIDLE; 58 | subghz_txrx_stop(app->subghz->txrx); 59 | meal_pager_blink_stop(app); 60 | meal_pager_transmit_model_set_sending(app->meal_pager_transmit, 0); 61 | } 62 | return true; 63 | } 64 | 65 | return consumed; 66 | } 67 | 68 | void meal_pager_scene_set_last_station_on_exit(void* context) { 69 | Meal_Pager* app = context; 70 | UNUSED(app); 71 | } -------------------------------------------------------------------------------- /meal_pager_i.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include "scenes/meal_pager_scene.h" 17 | #include "views/meal_pager_startscreen.h" 18 | #include "views/meal_pager_transmit.h" 19 | #include "helpers/meal_pager_storage.h" 20 | #include "helpers/subghz/subghz_types.h" 21 | #include "helpers/subghz/subghz.h" 22 | #include "helpers/gui/int_input.h" 23 | 24 | #define TAG "Meal_Pager" 25 | 26 | #define SUBGHZ_APP_EXTENSION ".sub" 27 | #define MEAL_PAGER_VERSION "1.8" 28 | 29 | typedef struct Meal_PagerTransmit Meal_PagerTransmit; 30 | typedef struct SubGhz SubGhz; 31 | 32 | typedef struct { 33 | Gui* gui; 34 | NotificationApp* notification; 35 | SubGhzNotificationState state_notifications; 36 | ViewDispatcher* view_dispatcher; 37 | Submenu* submenu; 38 | SubGhz* subghz; 39 | SceneManager* scene_manager; 40 | VariableItemList* variable_item_list; 41 | Meal_PagerStartscreen* meal_pager_startscreen; 42 | Meal_PagerTransmit* meal_pager_transmit; 43 | DialogsApp* dialogs; // File Browser 44 | FuriString* file_path; // File Browser 45 | uint32_t haptic; 46 | uint32_t speaker; 47 | uint32_t led; 48 | uint32_t save_settings; 49 | uint32_t pager_type; 50 | uint32_t first_station; 51 | char* first_station_char; 52 | uint32_t last_station; 53 | char* last_station_char; 54 | uint32_t first_pager; 55 | char* first_pager_char; 56 | uint32_t last_pager; 57 | char* last_pager_char; 58 | uint32_t current_station; 59 | uint32_t current_pager; 60 | bool stop_transmit; 61 | uint32_t repeats; 62 | char* repeats_char; 63 | IntInput* int_input; 64 | char* text_buffer; 65 | uint32_t max_station; 66 | uint32_t max_pager; 67 | char text_store[6][36]; 68 | } Meal_Pager; 69 | 70 | typedef enum { 71 | Meal_PagerViewIdStartscreen, 72 | Meal_PagerViewIdMenu, 73 | Meal_PagerViewIdTransmit, 74 | Meal_PagerViewIdSettings, 75 | Meal_PagerViewIdIntInput, 76 | } Meal_PagerViewId; 77 | 78 | typedef enum { 79 | Meal_PagerPagerTypeT119, 80 | Meal_PagerPagerTypeTD157, 81 | Meal_PagerPagerTypeTD165, 82 | Meal_PagerPagerTypeTD174, 83 | } Meal_PagerPagerType; 84 | 85 | typedef enum { 86 | Meal_PagerHapticOff, 87 | Meal_PagerHapticOn, 88 | } Meal_PagerHapticState; 89 | 90 | typedef enum { 91 | Meal_PagerSpeakerOff, 92 | Meal_PagerSpeakerOn, 93 | } Meal_PagerSpeakerState; 94 | 95 | typedef enum { 96 | Meal_PagerLedOff, 97 | Meal_PagerLedOn, 98 | } Meal_PagerLedState; 99 | 100 | typedef enum { 101 | Meal_PagerSettingsOff, 102 | Meal_PagerSettingsOn, 103 | } Meal_PagerSettingsStoreState; 104 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Flipper Zero Meal Pager Tool 2 | 3 | ## What this is? 4 | This app triggers restaurant pagers in a brute force manner, useful to test if devices are still functional. 5 |

6 | 7 | ## Supported Pagers 8 | - Retekess T119 9 | - Retekess TD157 10 | - Retekess TD165 11 | - Retekess TD174 12 | 13 | ### Features 14 | - Send a Range of Signals 15 | - Select range of stations 16 | - Select range of pagers 17 | 18 | ## How to install on Flipper Zero 19 | - If you do not have one, download a firmware onto your PC via git
20 | - Plug your Flipper Zero in via USB.
21 | - Copy the contents of this folder into the applications_user folder of your firmware.
22 | 23 | ## What does the blinking mean 24 | 25 | ### Yellow Blinking LED 26 | Means that the SubGhz Code is being generated for the configured range 27 | 28 | ### Purple Blinking LED 29 | Means that SubGhz Signals are being sent 30 | 31 | ## Settings Explanation 32 | 33 | ### Pager Type 34 | Sets the current Pager type your targeting. Each model uses a different encoding, you cannot trigger multiple models at once 35 | 36 | ### First Station / Last Station 37 | Each Station has a unique id. This is important so many stations can run in one location without conflicting each other. Use these settings to define a range of stations to trigger 38 | 39 | I do not recommend a lager range than 10 40 | 41 | ### Fist Pager / Last Pager 42 | The range of numbers on the pagers to be triggered. Most stations don't have many pagers, so a range of 0 - 31 probably is enough for all. 43 | 44 | ### Signal Repeat 45 | How many times a single pager trigger is sent. Usually a signal is sent multiple times to combat radio noise that can cause a signal not to be recognised. 46 | This is the total number of signals, so a setting of 0 will not send anything. More signals take longer, less have a higher risk of no effect. Set a number between 1 and 10. 47 | 48 | 49 | ## Can this Brute-Force Attacks 50 | This is a Proof-of-Concept. In Theory it could, but I wouldn't recommend trying it. Its annoying for people targeted and it could get you into trouble. Seriously, don't be that person, nobody will like your for it. 51 | Appart from that, most pagers support 8191 Stations. Triggering ~30 Pagers per station would easily take a long time. That when only sending each trigger once. It is recommended is to repeat the signal approx 10x, so that would already take all day. 52 | Also your Flipper Zero will crash in that time, as the generated signals use RAM which is limited in the device. There may be ways to work around this, but I think its better to have such a limitation. 53 | 54 | ## Does this even work 55 | I don't know. It's based on intel collected from other people. The Flipper sends data, I checked that with a second flipper. I've also heard rumors that it works in Australia. I'm sure it doesn't work in the US, as they use different frequencies. 56 | 57 | Then run the command: 58 | ``` 59 | .\fbt launch APPSRC=applications_user/meal_pager 60 | ``` 61 | The application will be compiled and copied onto your device. 62 | 63 | ## Thank you notes 64 | - [Moeker](https://github.com/moeker) and his awesome [pagger tool](https://github.com/meoker/pagger), couldn't have done this without 65 | - [xb8](https://github.com/xb8/t119bruteforcer) for the useful data collected 66 | - [Xenobyte, ShotokanZH](https://twitter.com/xenobyte_/status/1558123251276070912) for their super interesting research 67 | - [WillyJL](https://github.com/Willy-JL) and [xMasterX](https://github.com/xMasterX) for code contributions/corrections -------------------------------------------------------------------------------- /helpers/retekess/meal_pager_retekess_td157.c: -------------------------------------------------------------------------------- 1 | 2 | #include "meal_pager_retekess_td157.h" 3 | 4 | static char* genRawDataTD157(int zero, int one, const char* bits) { 5 | int bitsLen = strlen(bits); 6 | int lineLen = 256; // Adjust the line length as needed 7 | char* line = (char*)malloc(lineLen * sizeof(char)); 8 | 9 | // Initialize the line with the first part 10 | char* res = (char*)malloc(lineLen * sizeof(char)); 11 | res[0] = '\0'; // Null-terminate the result string 12 | 13 | customConcat(res, "-6000"); 14 | 15 | // Append bits and create the line 16 | for(int i = 0; i < bitsLen; i++) { 17 | char c = bits[i]; 18 | int t = (c == '0') ? zero : one; 19 | 20 | if(i % 2 == 0) { 21 | snprintf(line, lineLen, " %d", t); 22 | } else { 23 | snprintf(line, lineLen, " -%d", t); 24 | } 25 | 26 | // Concatenate the line to the result string 27 | customConcat(res, line); 28 | } 29 | 30 | // Append the closing part to the line 31 | customConcat(res, " 200 -6000"); 32 | 33 | free(line); // Free memory allocated for the line 34 | 35 | return res; 36 | } 37 | 38 | static void meal_pager_retekess_td157_generate_pager( 39 | void* context, 40 | char* stationId, 41 | uint32_t pager, 42 | FlipperFormat* ff) { 43 | Meal_Pager* app = context; 44 | char pagerId[11]; 45 | char* fullId = (char*)malloc(25 * sizeof(char)); 46 | uint32_t action = 2; 47 | char actionId[4]; 48 | app->current_pager = pager; 49 | meal_pager_transmit_model_set_pager(app->meal_pager_transmit, app->current_pager); 50 | FURI_LOG_D(TAG, "Generating TD157 Data for Pager %lu", pager); 51 | uint32ToBinaray(pager, pagerId, 10); 52 | customConcat(fullId, stationId); 53 | customConcat(fullId, pagerId); 54 | uint32ToBinaray(action, actionId, 4); 55 | customConcat(fullId, actionId); 56 | char* manchester = encManchester(fullId, 0); 57 | char* rawSignal = genRawDataTD157(200, 600, manchester); 58 | for(uint32_t i = 1; app->repeats >= i; i++) { 59 | flipper_format_write_string_cstr(ff, "RAW_Data", rawSignal); 60 | } 61 | free(manchester); 62 | free(rawSignal); 63 | } 64 | 65 | static void 66 | meal_pager_retekess_td157_generate_station(void* context, uint32_t station, FlipperFormat* ff) { 67 | Meal_Pager* app = context; 68 | FURI_LOG_D( 69 | TAG, 70 | "Generating TD157 Data for Station %lu. Pagers From %lu to %lu", 71 | station, 72 | app->first_pager, 73 | app->last_pager); 74 | app->current_station = station; 75 | app->current_pager = app->first_pager; 76 | char stationId[14]; 77 | uint32ToBinaray(station, stationId, 10); 78 | //reverse(stationId); 79 | meal_pager_transmit_model_set_station(app->meal_pager_transmit, app->current_station); 80 | for(uint32_t i = app->current_pager; i <= app->last_pager; i++) { 81 | meal_pager_retekess_td157_generate_pager(app, stationId, i, ff); 82 | if(app->stop_transmit) { 83 | break; 84 | } 85 | } 86 | } 87 | 88 | bool meal_pager_retekess_td157_generate_all(void* context) { 89 | Meal_Pager* app = context; 90 | 91 | app->current_pager = 1; 92 | app->current_station = app->first_station; 93 | 94 | Storage* storage = furi_record_open(RECORD_STORAGE); 95 | FlipperFormat* ff = flipper_format_file_alloc(storage); 96 | bool success = meal_pager_save_subghz_buffer_file_start( 97 | app, ff, storage, MEAL_PAGER_SUBGHZ_FILE_FREQUENCY); 98 | 99 | if(!success) { 100 | FURI_LOG_D(TAG, "failed to save to buffer"); 101 | meal_pager_save_subghz_buffer_stop(app, ff); 102 | furi_record_close(RECORD_STORAGE); 103 | return success; 104 | } 105 | 106 | for(uint32_t i = app->current_station; i <= app->last_station; i++) { 107 | meal_pager_retekess_td157_generate_station(app, i, ff); 108 | if(app->stop_transmit) { 109 | break; 110 | } 111 | } 112 | 113 | meal_pager_save_subghz_buffer_stop(app, ff); 114 | furi_record_close(RECORD_STORAGE); 115 | return success; 116 | } 117 | -------------------------------------------------------------------------------- /helpers/retekess/meal_pager_retekess_td165.c: -------------------------------------------------------------------------------- 1 | 2 | #include "meal_pager_retekess_td165.h" 3 | 4 | static char* genRawDataTD165(int zero, int one, const char* bits) { 5 | int bitsLen = strlen(bits); 6 | int lineLen = 256; // Adjust the line length as needed 7 | char* line = (char*)malloc(lineLen * sizeof(char)); 8 | 9 | // Initialize the line with the first part 10 | char* res = (char*)malloc(lineLen * sizeof(char)); 11 | res[0] = '\0'; // Null-terminate the result string 12 | 13 | customConcat(res, "-6000"); 14 | 15 | // Append bits and create the line 16 | for(int i = 0; i < bitsLen; i++) { 17 | char c = bits[i]; 18 | int t = (c == '0') ? zero : one; 19 | 20 | if(i % 2 == 0) { 21 | snprintf(line, lineLen, " %d", t); 22 | } else { 23 | snprintf(line, lineLen, " -%d", t); 24 | } 25 | 26 | // Concatenate the line to the result string 27 | customConcat(res, line); 28 | } 29 | 30 | // Append the closing part to the line 31 | customConcat(res, " 200 -6000"); 32 | 33 | free(line); // Free memory allocated for the line 34 | 35 | return res; 36 | } 37 | 38 | static void meal_pager_retekess_td165_generate_pager( 39 | void* context, 40 | char* stationId, 41 | uint32_t pager, 42 | FlipperFormat* ff) { 43 | Meal_Pager* app = context; 44 | char pagerId[11]; 45 | char* fullId = (char*)malloc(25 * sizeof(char)); 46 | uint32_t action = 0; 47 | char actionId[2]; 48 | app->current_pager = pager; 49 | meal_pager_transmit_model_set_pager(app->meal_pager_transmit, app->current_pager); 50 | FURI_LOG_D(TAG, "Generating TD165 Data for Pager %lu", pager); 51 | uint32ToBinaray(pager, pagerId, 10); 52 | reverse(pagerId); 53 | customConcat(fullId, stationId); 54 | customConcat(fullId, pagerId); 55 | uint32ToBinaray(action, actionId, 1); 56 | reverse(actionId); 57 | customConcat(fullId, actionId); 58 | char* manchester = encManchester(fullId, 0); 59 | char* rawSignal = genRawDataTD165(200, 600, manchester); 60 | for(uint32_t i = 1; app->repeats >= i; i++) { 61 | flipper_format_write_string_cstr(ff, "RAW_Data", rawSignal); 62 | } 63 | free(manchester); 64 | free(rawSignal); 65 | } 66 | 67 | static void 68 | meal_pager_retekess_td165_generate_station(void* context, uint32_t station, FlipperFormat* ff) { 69 | Meal_Pager* app = context; 70 | FURI_LOG_D( 71 | TAG, 72 | "Generating TD165 Data for Station %lu. Pagers From %lu to %lu", 73 | station, 74 | app->first_pager, 75 | app->last_pager); 76 | app->current_station = station; 77 | app->current_pager = app->first_pager; 78 | char stationId[14]; 79 | uint32ToBinaray(station, stationId, 13); 80 | reverse(stationId); 81 | meal_pager_transmit_model_set_station(app->meal_pager_transmit, app->current_station); 82 | for(uint32_t i = app->current_pager; i <= app->last_pager; i++) { 83 | meal_pager_retekess_td165_generate_pager(app, stationId, i, ff); 84 | if(app->stop_transmit) { 85 | break; 86 | } 87 | } 88 | } 89 | 90 | bool meal_pager_retekess_td165_generate_all(void* context) { 91 | Meal_Pager* app = context; 92 | 93 | app->current_pager = 1; 94 | app->current_station = app->first_station; 95 | 96 | Storage* storage = furi_record_open(RECORD_STORAGE); 97 | FlipperFormat* ff = flipper_format_file_alloc(storage); 98 | bool success = meal_pager_save_subghz_buffer_file_start( 99 | app, ff, storage, MEAL_PAGER_SUBGHZ_FILE_FREQUENCY); 100 | 101 | if(!success) { 102 | FURI_LOG_D(TAG, "failed to save to buffer"); 103 | meal_pager_save_subghz_buffer_stop(app, ff); 104 | furi_record_close(RECORD_STORAGE); 105 | return success; 106 | } 107 | 108 | for(uint32_t i = app->current_station; i <= app->last_station; i++) { 109 | meal_pager_retekess_td165_generate_station(app, i, ff); 110 | if(app->stop_transmit) { 111 | break; 112 | } 113 | } 114 | 115 | meal_pager_save_subghz_buffer_stop(app, ff); 116 | furi_record_close(RECORD_STORAGE); 117 | return success; 118 | } 119 | -------------------------------------------------------------------------------- /helpers/retekess/meal_pager_retekess_td174.c: -------------------------------------------------------------------------------- 1 | 2 | #include "meal_pager_retekess_td174.h" 3 | 4 | static char* genRawDataTd174(int zero, int one, const char* bits) { 5 | int bitsLen = strlen(bits); 6 | int lineLen = 256; // Adjust the line length as needed 7 | char* line = (char*)malloc(lineLen * sizeof(char)); 8 | 9 | // Initialize the line with the first part 10 | char* res = (char*)malloc(lineLen * sizeof(char)); 11 | res[0] = '\0'; // Null-terminate the result string 12 | 13 | customConcat(res, "-6000 300 -900"); // Always starts with 01 14 | 15 | // Append bits and create the line 16 | for(int i = 0; i < bitsLen; i++) { 17 | char c = bits[i]; 18 | int t = (c == '0') ? zero : one; 19 | 20 | if(i % 2 == 0) { 21 | snprintf(line, lineLen, " %d", t); 22 | } else { 23 | snprintf(line, lineLen, " -%d", t); 24 | } 25 | 26 | // Concatenate the line to the result string 27 | customConcat(res, line); 28 | } 29 | 30 | // Append the closing part to the line 31 | customConcat(res, " 300 -6000"); 32 | 33 | free(line); // Free memory allocated for the line 34 | 35 | return res; 36 | } 37 | 38 | static void meal_pager_retekess_td174_generate_pager( 39 | void* context, 40 | char* stationId, 41 | uint32_t pager, 42 | FlipperFormat* ff) { 43 | Meal_Pager* app = context; 44 | char pagerId[11]; 45 | char* fullId = (char*)malloc(25 * sizeof(char)); 46 | uint32_t action = 0; // 0 = ring, 1 = mute 47 | char actionId[2]; 48 | //FURI_LOG_D(TAG, "Generating TD174 Data for Pager %lu", pager); 49 | app->current_pager = pager; 50 | meal_pager_transmit_model_set_pager(app->meal_pager_transmit, app->current_pager); 51 | uint32ToBinaray(pager, pagerId, 8); 52 | uint32ToBinaray(action, actionId, 2); 53 | reverse(pagerId); 54 | reverse(actionId); 55 | //FURI_LOG_D(TAG, "Station Bin: %s", stationId); 56 | //FURI_LOG_D(TAG, "Pager Bin: %s", pagerId); 57 | //FURI_LOG_D(TAG, "Action Bin: %s", actionId); 58 | customConcat(fullId, stationId); 59 | customConcat(fullId, actionId); 60 | customConcat(fullId, pagerId); 61 | char* manchester = encManchester(fullId, 0); 62 | char* rawSignal = genRawDataTd174(300, 900, manchester); 63 | for(uint32_t i = 1; app->repeats >= i; i++) { 64 | flipper_format_write_string_cstr(ff, "RAW_Data", rawSignal); 65 | } 66 | free(manchester); 67 | free(rawSignal); 68 | } 69 | 70 | static void 71 | meal_pager_retekess_td174_generate_station(void* context, uint32_t station, FlipperFormat* ff) { 72 | Meal_Pager* app = context; 73 | FURI_LOG_D( 74 | TAG, 75 | "Generating TD174 Data for Station %lu. Pagers From %lu to %lu", 76 | station, 77 | app->first_pager, 78 | app->last_pager); 79 | app->current_station = station; 80 | app->current_pager = app->first_pager; 81 | char stationId[14]; 82 | uint32ToBinaray(station, stationId, 13); 83 | reverse(stationId); 84 | meal_pager_transmit_model_set_station(app->meal_pager_transmit, app->current_station); 85 | for(uint32_t i = app->current_pager; i <= app->last_pager; i++) { 86 | meal_pager_retekess_td174_generate_pager(app, stationId, i, ff); 87 | if(app->stop_transmit) { 88 | break; 89 | } 90 | } 91 | } 92 | 93 | bool meal_pager_retekess_td174_generate_all(void* context) { 94 | Meal_Pager* app = context; 95 | 96 | app->current_pager = 1; 97 | app->current_station = app->first_station; 98 | 99 | Storage* storage = furi_record_open(RECORD_STORAGE); 100 | FlipperFormat* ff = flipper_format_file_alloc(storage); 101 | bool success = meal_pager_save_subghz_buffer_file_start( 102 | app, ff, storage, MEAL_PAGER_SUBGHZ_FILE_ALT_FREQUENCY); 103 | 104 | if(!success) { 105 | FURI_LOG_D(TAG, "failed to save to buffer"); 106 | meal_pager_save_subghz_buffer_stop(app, ff); 107 | furi_record_close(RECORD_STORAGE); 108 | return success; 109 | } 110 | 111 | for(uint32_t i = app->current_station; i <= app->last_station; i++) { 112 | meal_pager_retekess_td174_generate_station(app, i, ff); 113 | //furi_thread_flags_wait(0, FuriFlagWaitAny, 100); 114 | if(app->stop_transmit) { 115 | break; 116 | } 117 | } 118 | 119 | meal_pager_save_subghz_buffer_stop(app, ff); 120 | furi_record_close(RECORD_STORAGE); 121 | return success; 122 | } 123 | -------------------------------------------------------------------------------- /scenes/meal_pager_scene_transmit.c: -------------------------------------------------------------------------------- 1 | #include "../meal_pager_i.h" 2 | #include "../helpers/meal_pager_custom_event.h" 3 | #include "../helpers/retekess/meal_pager_retekess_t119.h" 4 | #include "../helpers/retekess/meal_pager_retekess_td157.h" 5 | #include "../helpers/retekess/meal_pager_retekess_td165.h" 6 | #include "../helpers/retekess/meal_pager_retekess_td174.h" 7 | #include "../views/meal_pager_transmit.h" 8 | #include "../helpers/meal_pager_led.h" 9 | #include "../helpers/subghz/subghz.h" 10 | #include "../views/meal_pager_transmit.h" 11 | #include 12 | 13 | void meal_pager_transmit_callback(Meal_PagerCustomEvent event, void* context) { 14 | furi_assert(context); 15 | Meal_Pager* app = context; 16 | view_dispatcher_send_custom_event(app->view_dispatcher, event); 17 | } 18 | 19 | void meal_pager_scene_transmit_on_enter(void* context) { 20 | furi_assert(context); 21 | Meal_Pager* app = context; 22 | FURI_LOG_D(TAG, "Type is %lu", app->pager_type); 23 | 24 | app->stop_transmit = false; 25 | meal_pager_blink_start_compile(app); 26 | meal_pager_transmit_model_set_type(app->meal_pager_transmit, app->pager_type); 27 | meal_pager_transmit_model_set_station(app->meal_pager_transmit, app->current_station); 28 | meal_pager_transmit_model_set_pager(app->meal_pager_transmit, app->current_pager); 29 | meal_pager_transmit_set_callback(app->meal_pager_transmit, meal_pager_transmit_callback, app); 30 | view_dispatcher_switch_to_view(app->view_dispatcher, Meal_PagerViewIdTransmit); 31 | bool generated = false; 32 | switch(app->pager_type) { 33 | case Meal_PagerPagerTypeT119: 34 | generated = meal_pager_retekess_t119_generate_all(app); 35 | break; 36 | case Meal_PagerPagerTypeTD157: 37 | generated = meal_pager_retekess_td157_generate_all(app); 38 | break; 39 | case Meal_PagerPagerTypeTD165: 40 | generated = meal_pager_retekess_td165_generate_all(app); 41 | break; 42 | case Meal_PagerPagerTypeTD174: 43 | generated = meal_pager_retekess_td174_generate_all(app); 44 | break; 45 | default: 46 | generated = false; 47 | break; 48 | } 49 | if(!generated) { 50 | FURI_LOG_D(TAG, "Could not generate temp file"); 51 | meal_pager_blink_stop(app); 52 | return; 53 | } 54 | FURI_LOG_D(TAG, "Generated tmp.sub"); 55 | FURI_LOG_D(TAG, "Start Transmitting"); 56 | if(!app->stop_transmit) { 57 | meal_pager_transmit_model_set_sending(app->meal_pager_transmit, 1); 58 | subghz_send(app); 59 | } 60 | dolphin_deed(DolphinDeedSubGhzSend); 61 | } 62 | 63 | bool meal_pager_scene_transmit_on_event(void* context, SceneManagerEvent event) { 64 | Meal_Pager* app = context; 65 | bool consumed = false; 66 | 67 | if(event.type == SceneManagerEventTypeCustom) { 68 | switch(event.event) { 69 | case Meal_PagerCustomEventTransmitLeft: 70 | case Meal_PagerCustomEventTransmitRight: 71 | break; 72 | case Meal_PagerCustomEventTransmitUp: 73 | case Meal_PagerCustomEventTransmitDown: 74 | break; 75 | case Meal_PagerCustomEventTransmitBack: 76 | app->stop_transmit = true; 77 | app->state_notifications = SubGhzNotificationStateIDLE; 78 | meal_pager_blink_stop(app); 79 | if(!scene_manager_search_and_switch_to_previous_scene( 80 | app->scene_manager, Meal_PagerSceneMenu)) { 81 | scene_manager_stop(app->scene_manager); 82 | view_dispatcher_stop(app->view_dispatcher); 83 | } 84 | consumed = true; 85 | break; 86 | case Meal_PagerCustomEventViewTransmitterSendStop: 87 | app->state_notifications = SubGhzNotificationStateIDLE; 88 | subghz_txrx_stop(app->subghz->txrx); 89 | meal_pager_blink_stop(app); 90 | meal_pager_transmit_model_set_sending(app->meal_pager_transmit, 0); 91 | scene_manager_next_scene(app->scene_manager, Meal_PagerSceneMenu); 92 | FURI_LOG_D(TAG, "Stop Event"); 93 | break; 94 | } 95 | } else if(event.type == SceneManagerEventTypeTick) { 96 | if(app->state_notifications == SubGhzNotificationStateTx && app->led == 1) { 97 | notification_message(app->notification, &sequence_blink_magenta_10); 98 | } 99 | return true; 100 | } 101 | 102 | return consumed; 103 | } 104 | 105 | void meal_pager_scene_transmit_on_exit(void* context) { 106 | Meal_Pager* app = context; 107 | UNUSED(app); 108 | } -------------------------------------------------------------------------------- /views/meal_pager_startscreen.c: -------------------------------------------------------------------------------- 1 | #include "meal_pager_startscreen.h" 2 | 3 | #include "../meal_pager_i.h" 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | struct Meal_PagerStartscreen { 10 | View* view; 11 | Meal_PagerStartscreenCallback callback; 12 | void* context; 13 | }; 14 | 15 | typedef struct { 16 | int some_value; 17 | } Meal_PagerStartscreenModel; 18 | 19 | void meal_pager_startscreen_set_callback( 20 | Meal_PagerStartscreen* instance, 21 | Meal_PagerStartscreenCallback callback, 22 | void* context) { 23 | furi_assert(instance); 24 | furi_assert(callback); 25 | instance->callback = callback; 26 | instance->context = context; 27 | } 28 | 29 | void meal_pager_startscreen_draw(Canvas* canvas, Meal_PagerStartscreenModel* model) { 30 | UNUSED(model); 31 | char buffer[64]; 32 | canvas_clear(canvas); 33 | canvas_set_color(canvas, ColorBlack); 34 | canvas_set_font(canvas, FontPrimary); 35 | canvas_draw_str_aligned(canvas, 64, 10, AlignCenter, AlignTop, "Restaurant Pager"); 36 | canvas_set_font(canvas, FontSecondary); 37 | canvas_draw_str_aligned(canvas, 64, 22, AlignCenter, AlignTop, "Trigger Tool"); 38 | snprintf(buffer, sizeof(buffer), "Version: %s", MEAL_PAGER_VERSION); 39 | canvas_draw_str_aligned(canvas, 64, 42, AlignCenter, AlignTop, buffer); 40 | elements_button_center(canvas, "Start"); 41 | } 42 | 43 | static void meal_pager_startscreen_model_init(Meal_PagerStartscreenModel* const model) { 44 | model->some_value = 1; 45 | } 46 | 47 | bool meal_pager_startscreen_input(InputEvent* event, void* context) { 48 | furi_assert(context); 49 | Meal_PagerStartscreen* instance = context; 50 | if(event->type == InputTypeRelease) { 51 | switch(event->key) { 52 | case InputKeyBack: 53 | with_view_model( 54 | instance->view, 55 | Meal_PagerStartscreenModel * model, 56 | { 57 | UNUSED(model); 58 | instance->callback(Meal_PagerCustomEventStartscreenBack, instance->context); 59 | }, 60 | true); 61 | break; 62 | case InputKeyLeft: 63 | case InputKeyRight: 64 | case InputKeyUp: 65 | case InputKeyDown: 66 | case InputKeyOk: 67 | with_view_model( 68 | instance->view, 69 | Meal_PagerStartscreenModel * model, 70 | { 71 | UNUSED(model); 72 | instance->callback(Meal_PagerCustomEventStartscreenOk, instance->context); 73 | }, 74 | true); 75 | break; 76 | case InputKeyMAX: 77 | break; 78 | } 79 | } 80 | return true; 81 | } 82 | 83 | void meal_pager_startscreen_exit(void* context) { 84 | furi_assert(context); 85 | } 86 | 87 | void meal_pager_startscreen_enter(void* context) { 88 | furi_assert(context); 89 | Meal_PagerStartscreen* instance = (Meal_PagerStartscreen*)context; 90 | with_view_model( 91 | instance->view, 92 | Meal_PagerStartscreenModel * model, 93 | { meal_pager_startscreen_model_init(model); }, 94 | true); 95 | } 96 | 97 | Meal_PagerStartscreen* meal_pager_startscreen_alloc() { 98 | Meal_PagerStartscreen* instance = malloc(sizeof(Meal_PagerStartscreen)); 99 | instance->view = view_alloc(); 100 | view_allocate_model(instance->view, ViewModelTypeLocking, sizeof(Meal_PagerStartscreenModel)); 101 | view_set_context(instance->view, instance); // furi_assert crashes in events without this 102 | view_set_draw_callback(instance->view, (ViewDrawCallback)meal_pager_startscreen_draw); 103 | view_set_input_callback(instance->view, meal_pager_startscreen_input); 104 | //view_set_enter_callback(instance->view, meal_pager_startscreen_enter); 105 | //view_set_exit_callback(instance->view, meal_pager_startscreen_exit); 106 | 107 | with_view_model( 108 | instance->view, 109 | Meal_PagerStartscreenModel * model, 110 | { meal_pager_startscreen_model_init(model); }, 111 | true); 112 | 113 | return instance; 114 | } 115 | 116 | void meal_pager_startscreen_free(Meal_PagerStartscreen* instance) { 117 | furi_assert(instance); 118 | 119 | with_view_model( 120 | instance->view, Meal_PagerStartscreenModel * model, { UNUSED(model); }, true); 121 | view_free(instance->view); 122 | free(instance); 123 | } 124 | 125 | View* meal_pager_startscreen_get_view(Meal_PagerStartscreen* instance) { 126 | furi_assert(instance); 127 | return instance->view; 128 | } 129 | -------------------------------------------------------------------------------- /helpers/retekess/meal_pager_retekess_t119.c: -------------------------------------------------------------------------------- 1 | 2 | #include "meal_pager_retekess_t119.h" 3 | 4 | static char* genRawDataT119(int zero, int one, const char* bits) { 5 | int bitsLen = strlen(bits); 6 | int lineLen = 256; // Adjust the line length as needed 7 | char* line = (char*)malloc(lineLen * sizeof(char)); 8 | 9 | // Initialize the line with the first part 10 | char* res = (char*)malloc(lineLen * sizeof(char)); 11 | res[0] = '\0'; // Null-terminate the result string 12 | 13 | customConcat(res, "-6000"); 14 | 15 | // Append bits and create the line 16 | for(int i = 0; i < bitsLen; i++) { 17 | char c = bits[i]; 18 | int t = (c == '0') ? zero : one; 19 | 20 | if(i % 2 == 0) { 21 | snprintf(line, lineLen, " %d", t); 22 | } else { 23 | snprintf(line, lineLen, " -%d", t); 24 | } 25 | 26 | // Concatenate the line to the result string 27 | customConcat(res, line); 28 | } 29 | 30 | // Append the closing part to the line 31 | customConcat(res, " 200 -6000"); 32 | 33 | free(line); // Free memory allocated for the line 34 | 35 | return res; 36 | } 37 | 38 | static void meal_pager_retekess_t119_generate_pager( 39 | void* context, 40 | char* stationId, 41 | uint32_t pager, 42 | FlipperFormat* ff) { 43 | Meal_Pager* app = context; 44 | char pagerId[11]; 45 | char* fullId = (char*)malloc(25 * sizeof(char)); 46 | uint32_t action = 0; // 0 = ring, 1 = mute 47 | char actionId[2]; 48 | //FURI_LOG_D(TAG, "Generating T119 Data for Pager %lu", pager); 49 | app->current_pager = pager; 50 | meal_pager_transmit_model_set_pager(app->meal_pager_transmit, app->current_pager); 51 | uint32ToBinaray(pager, pagerId, 10); 52 | uint32ToBinaray(action, actionId, 1); 53 | reverse(pagerId); 54 | reverse(actionId); 55 | //FURI_LOG_D(TAG, "Station Bin: %s", stationId); 56 | //FURI_LOG_D(TAG, "Pager Bin: %s", pagerId); 57 | //FURI_LOG_D(TAG, "Action Bin: %s", actionId); 58 | customConcat(fullId, stationId); 59 | customConcat(fullId, pagerId); 60 | //FURI_LOG_D(TAG, "Result %s", fullId); 61 | //FURI_LOG_D(TAG, "Station & Pager: %s", stationPagerId); 62 | //FURI_LOG_D(TAG, "Station & Pager: %s", stationPagerId); 63 | customConcat(fullId, actionId); 64 | //FURI_LOG_D(TAG, "CustomConcat: %s", fullId); 65 | //FURI_LOG_D(TAG, "Station & Pager & Action: %s", fullId); 66 | char* manchester = encManchester(fullId, 0); 67 | //FURI_LOG_D(TAG, "Manchester: %s", manchester); 68 | char* rawSignal = genRawDataT119(200, 600, manchester); 69 | //FURI_LOG_D(TAG, "RAW_Data: %s", rawSignal); 70 | for(uint32_t i = 1; app->repeats >= i; i++) { 71 | flipper_format_write_string_cstr(ff, "RAW_Data", rawSignal); 72 | } 73 | //flipper_format_write_string_cstr(ff, "RAW_Data", rawSignal); 74 | free(manchester); 75 | free(rawSignal); 76 | } 77 | 78 | static void 79 | meal_pager_retekess_t119_generate_station(void* context, uint32_t station, FlipperFormat* ff) { 80 | Meal_Pager* app = context; 81 | FURI_LOG_D( 82 | TAG, 83 | "Generating T119 Data for Station %lu. Pagers From %lu to %lu", 84 | station, 85 | app->first_pager, 86 | app->last_pager); 87 | app->current_station = station; 88 | app->current_pager = app->first_pager; 89 | char stationId[14]; 90 | uint32ToBinaray(station, stationId, 13); 91 | reverse(stationId); 92 | meal_pager_transmit_model_set_station(app->meal_pager_transmit, app->current_station); 93 | for(uint32_t i = app->current_pager; i <= app->last_pager; i++) { 94 | meal_pager_retekess_t119_generate_pager(app, stationId, i, ff); 95 | //furi_thread_flags_wait(0, FuriFlagWaitAny, 1); 96 | if(app->stop_transmit) { 97 | break; 98 | } 99 | } 100 | } 101 | 102 | bool meal_pager_retekess_t119_generate_all(void* context) { 103 | Meal_Pager* app = context; 104 | 105 | app->current_pager = 1; 106 | app->current_station = app->first_station; 107 | 108 | Storage* storage = furi_record_open(RECORD_STORAGE); 109 | FlipperFormat* ff = flipper_format_file_alloc(storage); 110 | bool success = meal_pager_save_subghz_buffer_file_start( 111 | app, ff, storage, MEAL_PAGER_SUBGHZ_FILE_FREQUENCY); 112 | 113 | if(!success) { 114 | FURI_LOG_D(TAG, "failed to save to buffer"); 115 | meal_pager_save_subghz_buffer_stop(app, ff); 116 | furi_record_close(RECORD_STORAGE); 117 | return success; 118 | } 119 | 120 | for(uint32_t i = app->current_station; i <= app->last_station; i++) { 121 | meal_pager_retekess_t119_generate_station(app, i, ff); 122 | //furi_thread_flags_wait(0, FuriFlagWaitAny, 100); 123 | if(app->stop_transmit) { 124 | break; 125 | } 126 | } 127 | 128 | meal_pager_save_subghz_buffer_stop(app, ff); 129 | furi_record_close(RECORD_STORAGE); 130 | return success; 131 | } 132 | -------------------------------------------------------------------------------- /scenes/meal_pager_scene_menu.c: -------------------------------------------------------------------------------- 1 | #include "../meal_pager_i.h" 2 | #include "../helpers/meal_pager_led.h" 3 | #include "../helpers/meal_pager_storage.h" 4 | #include "../views/meal_pager_transmit.h" 5 | 6 | enum SubmenuIndex { 7 | SubmenuIndexTransmit = 10, 8 | SubmenuIndexSetFirstStation, 9 | SubmenuIndexSetLastStation, 10 | SubmenuIndexSetFirstPager, 11 | SubmenuIndexSetLastPager, 12 | SubmenuIndexScene3, 13 | SubmenuIndexScene4, 14 | SubmenuIndexScene5, 15 | SubmenuIndexSettings, 16 | }; 17 | 18 | void meal_pager_scene_menu_submenu_callback(void* context, uint32_t index) { 19 | Meal_Pager* app = context; 20 | view_dispatcher_send_custom_event(app->view_dispatcher, index); 21 | } 22 | 23 | void meal_pager_scene_menu_on_enter(void* context) { 24 | Meal_Pager* app = context; 25 | 26 | meal_pager_set_max_values(app); 27 | 28 | submenu_add_item( 29 | app->submenu, 30 | "Send Data", 31 | SubmenuIndexTransmit, 32 | meal_pager_scene_menu_submenu_callback, 33 | app); 34 | submenu_add_item( 35 | app->submenu, 36 | "Set First Station", 37 | SubmenuIndexSetFirstStation, 38 | meal_pager_scene_menu_submenu_callback, 39 | app); 40 | submenu_add_item( 41 | app->submenu, 42 | "Set Last Station", 43 | SubmenuIndexSetLastStation, 44 | meal_pager_scene_menu_submenu_callback, 45 | app); 46 | submenu_add_item( 47 | app->submenu, 48 | "Set First Pager", 49 | SubmenuIndexSetFirstPager, 50 | meal_pager_scene_menu_submenu_callback, 51 | app); 52 | submenu_add_item( 53 | app->submenu, 54 | "Set Last Pager", 55 | SubmenuIndexSetLastPager, 56 | meal_pager_scene_menu_submenu_callback, 57 | app); 58 | submenu_add_item( 59 | app->submenu, 60 | "Settings", 61 | SubmenuIndexSettings, 62 | meal_pager_scene_menu_submenu_callback, 63 | app); 64 | 65 | submenu_set_selected_item( 66 | app->submenu, scene_manager_get_scene_state(app->scene_manager, Meal_PagerSceneMenu)); 67 | 68 | view_dispatcher_switch_to_view(app->view_dispatcher, Meal_PagerViewIdMenu); 69 | } 70 | 71 | bool meal_pager_scene_menu_on_event(void* context, SceneManagerEvent event) { 72 | Meal_Pager* app = context; 73 | UNUSED(app); 74 | if(event.type == SceneManagerEventTypeBack) { 75 | //exit app 76 | scene_manager_stop(app->scene_manager); 77 | view_dispatcher_stop(app->view_dispatcher); 78 | return true; 79 | } else if(event.type == SceneManagerEventTypeCustom) { 80 | if(event.event == SubmenuIndexTransmit) { 81 | scene_manager_set_scene_state( 82 | app->scene_manager, Meal_PagerSceneMenu, SubmenuIndexTransmit); 83 | scene_manager_next_scene(app->scene_manager, Meal_PagerSceneTransmit); 84 | return true; 85 | } else if(event.event == SubmenuIndexSettings) { 86 | scene_manager_set_scene_state( 87 | app->scene_manager, Meal_PagerSceneMenu, SubmenuIndexSettings); 88 | scene_manager_next_scene(app->scene_manager, Meal_PagerSceneSettings); 89 | return true; 90 | } else if(event.event == Meal_PagerCustomEventViewTransmitterSendStop) { 91 | app->state_notifications = SubGhzNotificationStateIDLE; 92 | subghz_txrx_stop(app->subghz->txrx); 93 | FURI_LOG_D(TAG, "Stop Event from Menu"); 94 | return true; 95 | } else if(event.event == SubmenuIndexSetFirstStation) { 96 | scene_manager_set_scene_state( 97 | app->scene_manager, Meal_PagerSceneSetFirstStation, SubmenuIndexSetFirstStation); 98 | scene_manager_next_scene(app->scene_manager, Meal_PagerSceneSetFirstStation); 99 | return true; 100 | } else if(event.event == SubmenuIndexSetLastStation) { 101 | scene_manager_set_scene_state( 102 | app->scene_manager, Meal_PagerSceneSetLastStation, SubmenuIndexSetLastStation); 103 | scene_manager_next_scene(app->scene_manager, Meal_PagerSceneSetLastStation); 104 | return true; 105 | } else if(event.event == SubmenuIndexSetFirstPager) { 106 | scene_manager_set_scene_state( 107 | app->scene_manager, Meal_PagerSceneSetFirstPager, SubmenuIndexSetFirstPager); 108 | scene_manager_next_scene(app->scene_manager, Meal_PagerSceneSetFirstPager); 109 | return true; 110 | } else if(event.event == SubmenuIndexSetLastPager) { 111 | scene_manager_set_scene_state( 112 | app->scene_manager, Meal_PagerSceneSetLastPager, SubmenuIndexSetLastPager); 113 | scene_manager_next_scene(app->scene_manager, Meal_PagerSceneSetLastPager); 114 | return true; 115 | } 116 | } else if(event.type == SceneManagerEventTypeTick) { 117 | if(app->state_notifications == SubGhzNotificationStateTx) { 118 | app->state_notifications = SubGhzNotificationStateIDLE; 119 | subghz_txrx_stop(app->subghz->txrx); 120 | meal_pager_blink_stop(app); 121 | meal_pager_transmit_model_set_sending(app->meal_pager_transmit, 0); 122 | } 123 | return true; 124 | } 125 | return false; 126 | } 127 | 128 | void meal_pager_scene_menu_on_exit(void* context) { 129 | Meal_Pager* app = context; 130 | submenu_reset(app->submenu); 131 | } -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | Language: Cpp 3 | AccessModifierOffset: -4 4 | AlignAfterOpenBracket: AlwaysBreak 5 | AlignArrayOfStructures: None 6 | AlignConsecutiveMacros: None 7 | AlignConsecutiveAssignments: None 8 | AlignConsecutiveBitFields: None 9 | AlignConsecutiveDeclarations: None 10 | AlignEscapedNewlines: Left 11 | AlignOperands: Align 12 | AlignTrailingComments: false 13 | AllowAllArgumentsOnNextLine: true 14 | AllowAllParametersOfDeclarationOnNextLine: false 15 | AllowShortEnumsOnASingleLine: true 16 | AllowShortBlocksOnASingleLine: Never 17 | AllowShortCaseLabelsOnASingleLine: false 18 | AllowShortFunctionsOnASingleLine: None 19 | AllowShortLambdasOnASingleLine: All 20 | AllowShortIfStatementsOnASingleLine: WithoutElse 21 | AllowShortLoopsOnASingleLine: true 22 | AlwaysBreakAfterDefinitionReturnType: None 23 | AlwaysBreakAfterReturnType: None 24 | AlwaysBreakBeforeMultilineStrings: false 25 | AlwaysBreakTemplateDeclarations: Yes 26 | AttributeMacros: 27 | - __capability 28 | BinPackArguments: false 29 | BinPackParameters: false 30 | BraceWrapping: 31 | AfterCaseLabel: false 32 | AfterClass: false 33 | AfterControlStatement: Never 34 | AfterEnum: false 35 | AfterFunction: false 36 | AfterNamespace: false 37 | AfterObjCDeclaration: false 38 | AfterStruct: false 39 | AfterUnion: false 40 | AfterExternBlock: false 41 | BeforeCatch: false 42 | BeforeElse: false 43 | BeforeLambdaBody: false 44 | BeforeWhile: false 45 | IndentBraces: false 46 | SplitEmptyFunction: true 47 | SplitEmptyRecord: true 48 | SplitEmptyNamespace: true 49 | BreakBeforeBinaryOperators: None 50 | BreakBeforeConceptDeclarations: true 51 | BreakBeforeBraces: Attach 52 | BreakBeforeInheritanceComma: false 53 | BreakInheritanceList: BeforeColon 54 | BreakBeforeTernaryOperators: false 55 | BreakConstructorInitializersBeforeComma: false 56 | BreakConstructorInitializers: BeforeComma 57 | BreakAfterJavaFieldAnnotations: false 58 | BreakStringLiterals: false 59 | ColumnLimit: 99 60 | CommentPragmas: '^ IWYU pragma:' 61 | QualifierAlignment: Leave 62 | CompactNamespaces: false 63 | ConstructorInitializerIndentWidth: 4 64 | ContinuationIndentWidth: 4 65 | Cpp11BracedListStyle: true 66 | DeriveLineEnding: true 67 | DerivePointerAlignment: false 68 | DisableFormat: false 69 | EmptyLineAfterAccessModifier: Never 70 | EmptyLineBeforeAccessModifier: LogicalBlock 71 | ExperimentalAutoDetectBinPacking: false 72 | PackConstructorInitializers: BinPack 73 | BasedOnStyle: '' 74 | ConstructorInitializerAllOnOneLineOrOnePerLine: false 75 | AllowAllConstructorInitializersOnNextLine: true 76 | FixNamespaceComments: false 77 | ForEachMacros: 78 | - foreach 79 | - Q_FOREACH 80 | - BOOST_FOREACH 81 | IfMacros: 82 | - KJ_IF_MAYBE 83 | IncludeBlocks: Preserve 84 | IncludeCategories: 85 | - Regex: '.*' 86 | Priority: 1 87 | SortPriority: 0 88 | CaseSensitive: false 89 | - Regex: '^(<|"(gtest|gmock|isl|json)/)' 90 | Priority: 3 91 | SortPriority: 0 92 | CaseSensitive: false 93 | - Regex: '.*' 94 | Priority: 1 95 | SortPriority: 0 96 | CaseSensitive: false 97 | IncludeIsMainRegex: '(Test)?$' 98 | IncludeIsMainSourceRegex: '' 99 | IndentAccessModifiers: false 100 | IndentCaseLabels: false 101 | IndentCaseBlocks: false 102 | IndentGotoLabels: true 103 | IndentPPDirectives: None 104 | IndentExternBlock: AfterExternBlock 105 | IndentRequires: false 106 | IndentWidth: 4 107 | IndentWrappedFunctionNames: true 108 | InsertTrailingCommas: None 109 | JavaScriptQuotes: Leave 110 | JavaScriptWrapImports: true 111 | KeepEmptyLinesAtTheStartOfBlocks: false 112 | LambdaBodyIndentation: Signature 113 | MacroBlockBegin: '' 114 | MacroBlockEnd: '' 115 | MaxEmptyLinesToKeep: 1 116 | NamespaceIndentation: None 117 | ObjCBinPackProtocolList: Auto 118 | ObjCBlockIndentWidth: 4 119 | ObjCBreakBeforeNestedBlockParam: true 120 | ObjCSpaceAfterProperty: true 121 | ObjCSpaceBeforeProtocolList: true 122 | PenaltyBreakAssignment: 10 123 | PenaltyBreakBeforeFirstCallParameter: 30 124 | PenaltyBreakComment: 10 125 | PenaltyBreakFirstLessLess: 0 126 | PenaltyBreakOpenParenthesis: 0 127 | PenaltyBreakString: 10 128 | PenaltyBreakTemplateDeclaration: 10 129 | PenaltyExcessCharacter: 100 130 | PenaltyReturnTypeOnItsOwnLine: 60 131 | PenaltyIndentedWhitespace: 0 132 | PointerAlignment: Left 133 | PPIndentWidth: -1 134 | ReferenceAlignment: Pointer 135 | ReflowComments: false 136 | RemoveBracesLLVM: false 137 | SeparateDefinitionBlocks: Leave 138 | ShortNamespaceLines: 1 139 | SortIncludes: Never 140 | SortJavaStaticImport: Before 141 | SortUsingDeclarations: false 142 | SpaceAfterCStyleCast: false 143 | SpaceAfterLogicalNot: false 144 | SpaceAfterTemplateKeyword: true 145 | SpaceBeforeAssignmentOperators: true 146 | SpaceBeforeCaseColon: false 147 | SpaceBeforeCpp11BracedList: false 148 | SpaceBeforeCtorInitializerColon: true 149 | SpaceBeforeInheritanceColon: true 150 | SpaceBeforeParens: Never 151 | SpaceBeforeParensOptions: 152 | AfterControlStatements: false 153 | AfterForeachMacros: false 154 | AfterFunctionDefinitionName: false 155 | AfterFunctionDeclarationName: false 156 | AfterIfMacros: false 157 | AfterOverloadedOperator: false 158 | BeforeNonEmptyParentheses: false 159 | SpaceAroundPointerQualifiers: Default 160 | SpaceBeforeRangeBasedForLoopColon: true 161 | SpaceInEmptyBlock: false 162 | SpaceInEmptyParentheses: false 163 | SpacesBeforeTrailingComments: 1 164 | SpacesInAngles: Never 165 | SpacesInConditionalStatement: false 166 | SpacesInContainerLiterals: false 167 | SpacesInCStyleCastParentheses: false 168 | SpacesInLineCommentPrefix: 169 | Minimum: 1 170 | Maximum: -1 171 | SpacesInParentheses: false 172 | SpacesInSquareBrackets: false 173 | SpaceBeforeSquareBrackets: false 174 | BitFieldColonSpacing: Both 175 | Standard: c++03 176 | StatementAttributeLikeMacros: 177 | - Q_EMIT 178 | StatementMacros: 179 | - Q_UNUSED 180 | - QT_REQUIRE_VERSION 181 | TabWidth: 4 182 | UseCRLF: false 183 | UseTab: Never 184 | WhitespaceSensitiveMacros: 185 | - STRINGIZE 186 | - PP_STRINGIZE 187 | - BOOST_PP_STRINGIZE 188 | - NS_SWIFT_NAME 189 | - CF_SWIFT_NAME 190 | ... 191 | 192 | -------------------------------------------------------------------------------- /meal_pager.c: -------------------------------------------------------------------------------- 1 | #include "meal_pager.h" 2 | 3 | bool meal_pager_custom_event_callback(void* context, uint32_t event) { 4 | furi_assert(context); 5 | Meal_Pager* app = context; 6 | return scene_manager_handle_custom_event(app->scene_manager, event); 7 | } 8 | 9 | void meal_pager_tick_event_callback(void* context) { 10 | furi_assert(context); 11 | Meal_Pager* app = context; 12 | scene_manager_handle_tick_event(app->scene_manager); 13 | } 14 | 15 | //leave app if back button pressed 16 | bool meal_pager_navigation_event_callback(void* context) { 17 | furi_assert(context); 18 | Meal_Pager* app = context; 19 | return scene_manager_handle_back_event(app->scene_manager); 20 | } 21 | 22 | Meal_Pager* meal_pager_app_alloc() { 23 | Meal_Pager* app = malloc(sizeof(Meal_Pager)); 24 | app->gui = furi_record_open(RECORD_GUI); 25 | app->notification = furi_record_open(RECORD_NOTIFICATION); 26 | 27 | //Turn backlight on, believe me this makes testing your app easier 28 | notification_message(app->notification, &sequence_display_backlight_on); 29 | 30 | //Scene additions 31 | app->view_dispatcher = view_dispatcher_alloc(); 32 | view_dispatcher_enable_queue(app->view_dispatcher); 33 | 34 | app->scene_manager = scene_manager_alloc(&meal_pager_scene_handlers, app); 35 | view_dispatcher_set_event_callback_context(app->view_dispatcher, app); 36 | view_dispatcher_set_navigation_event_callback( 37 | app->view_dispatcher, meal_pager_navigation_event_callback); 38 | view_dispatcher_set_tick_event_callback( 39 | app->view_dispatcher, meal_pager_tick_event_callback, 100); 40 | view_dispatcher_set_custom_event_callback( 41 | app->view_dispatcher, meal_pager_custom_event_callback); 42 | app->submenu = submenu_alloc(); 43 | 44 | // Set defaults, in case no config loaded 45 | app->haptic = 1; 46 | app->speaker = 1; 47 | app->led = 1; 48 | app->save_settings = 1; 49 | app->pager_type = 0; 50 | app->first_station = 0; 51 | app->first_station_char = "0"; 52 | app->last_station = 10; 53 | app->last_station_char = "10"; 54 | app->first_pager = 0; 55 | app->first_pager_char = "0"; 56 | app->last_pager = 31; 57 | app->last_pager_char = "31"; 58 | app->stop_transmit = false; 59 | app->repeats = 1; 60 | app->repeats_char = "1"; 61 | app->max_station = 8191; 62 | app->max_pager = 999; 63 | 64 | snprintf(app->text_store[0], 32, "%lu", app->first_station); 65 | 66 | // Used for File Browser 67 | app->dialogs = furi_record_open(RECORD_DIALOGS); 68 | app->file_path = furi_string_alloc(); 69 | 70 | app->subghz = subghz_alloc(); 71 | 72 | // Load configs 73 | meal_pager_read_settings(app); 74 | 75 | view_dispatcher_add_view( 76 | app->view_dispatcher, Meal_PagerViewIdMenu, submenu_get_view(app->submenu)); 77 | app->meal_pager_startscreen = meal_pager_startscreen_alloc(); 78 | view_dispatcher_add_view( 79 | app->view_dispatcher, 80 | Meal_PagerViewIdStartscreen, 81 | meal_pager_startscreen_get_view(app->meal_pager_startscreen)); 82 | app->meal_pager_transmit = meal_pager_transmit_alloc(app); 83 | view_dispatcher_add_view( 84 | app->view_dispatcher, 85 | Meal_PagerViewIdTransmit, 86 | meal_pager_transmit_get_view(app->meal_pager_transmit)); 87 | 88 | app->variable_item_list = variable_item_list_alloc(); 89 | view_dispatcher_add_view( 90 | app->view_dispatcher, 91 | Meal_PagerViewIdSettings, 92 | variable_item_list_get_view(app->variable_item_list)); 93 | 94 | app->int_input = int_input_alloc(); 95 | view_dispatcher_add_view( 96 | app->view_dispatcher, Meal_PagerViewIdIntInput, int_input_get_view(app->int_input)); 97 | 98 | //End Scene Additions 99 | 100 | snprintf(app->text_store[0], 20, "%lu", app->first_station); 101 | snprintf(app->text_store[1], 20, "%lu", app->last_station); 102 | snprintf(app->text_store[2], 20, "%lu", app->first_pager); 103 | snprintf(app->text_store[3], 20, "%lu", app->last_pager); 104 | 105 | return app; 106 | } 107 | 108 | void meal_pager_app_free(Meal_Pager* app) { 109 | furi_assert(app); 110 | 111 | // Scene manager 112 | scene_manager_free(app->scene_manager); 113 | 114 | // View Dispatcher 115 | view_dispatcher_remove_view(app->view_dispatcher, Meal_PagerViewIdMenu); 116 | view_dispatcher_remove_view(app->view_dispatcher, Meal_PagerViewIdTransmit); 117 | view_dispatcher_remove_view(app->view_dispatcher, Meal_PagerViewIdSettings); 118 | view_dispatcher_remove_view(app->view_dispatcher, Meal_PagerViewIdIntInput); 119 | view_dispatcher_remove_view(app->view_dispatcher, Meal_PagerViewIdStartscreen); 120 | submenu_free(app->submenu); 121 | int_input_free(app->int_input); 122 | 123 | view_dispatcher_free(app->view_dispatcher); 124 | variable_item_list_free(app->variable_item_list); 125 | meal_pager_transmit_free(app->meal_pager_transmit); 126 | meal_pager_startscreen_free(app->meal_pager_startscreen); 127 | furi_record_close(RECORD_GUI); 128 | 129 | // Close File Browser 130 | furi_record_close(RECORD_DIALOGS); 131 | furi_record_close(RECORD_NOTIFICATION); 132 | furi_string_free(app->file_path); 133 | 134 | app->gui = NULL; 135 | app->notification = NULL; 136 | 137 | subghz_free(app->subghz); 138 | 139 | //Remove whatever is left 140 | free(app); 141 | } 142 | 143 | int32_t meal_pager_app(void* p) { 144 | UNUSED(p); 145 | FURI_LOG_D(TAG, "Started Meal Pager"); 146 | 147 | Meal_Pager* app = meal_pager_app_alloc(); 148 | 149 | view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); 150 | 151 | scene_manager_next_scene( 152 | app->scene_manager, Meal_PagerSceneStartscreen); //Start with start screen 153 | //scene_manager_next_scene(app->scene_manager, Meal_PagerSceneMenu); //if you want to directly start with Menu 154 | 155 | furi_hal_power_suppress_charge_enter(); 156 | 157 | view_dispatcher_run(app->view_dispatcher); 158 | 159 | meal_pager_save_settings(app); 160 | 161 | furi_hal_power_suppress_charge_exit(); 162 | meal_pager_app_free(app); 163 | 164 | return 0; 165 | } 166 | -------------------------------------------------------------------------------- /views/meal_pager_transmit.c: -------------------------------------------------------------------------------- 1 | #include "../meal_pager_i.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | const char* const pager_type_text_long[4] = { 9 | "Retekess T119", 10 | "Retekess TD157", 11 | "Retekess TD165", 12 | "Retekess TD174", 13 | }; 14 | 15 | struct Meal_PagerTransmit { 16 | View* view; 17 | Meal_PagerTransmitCallback callback; 18 | void* context; 19 | }; 20 | 21 | typedef struct { 22 | uint32_t pager_type; 23 | uint32_t station; 24 | uint32_t pager; 25 | uint32_t sending; 26 | } Meal_PagerTransmitModel; 27 | 28 | void meal_pager_transmit_set_callback( 29 | Meal_PagerTransmit* instance, 30 | Meal_PagerTransmitCallback callback, 31 | void* context) { 32 | furi_assert(instance); 33 | furi_assert(callback); 34 | instance->callback = callback; 35 | instance->context = context; 36 | } 37 | 38 | void meal_pager_transmit_draw(Canvas* canvas, Meal_PagerTransmitModel* model) { 39 | canvas_clear(canvas); 40 | canvas_set_color(canvas, ColorBlack); 41 | canvas_set_font(canvas, FontPrimary); 42 | //char* test = ""; 43 | //snprintf(test, 20, "%lu", model->pager_type); 44 | char stationText[20] = ""; 45 | char pagerText[20] = ""; 46 | snprintf(stationText, 20, "Station: %lu", model->station); 47 | snprintf(pagerText, 20, "Pager: %lu", model->pager); 48 | canvas_draw_str_aligned( 49 | canvas, 0, 10, AlignLeft, AlignTop, pager_type_text_long[model->pager_type]); 50 | canvas_set_font(canvas, FontSecondary); 51 | if(model->sending == 0) { 52 | canvas_draw_str_aligned(canvas, 0, 22, AlignLeft, AlignTop, "Generating Data"); 53 | canvas_draw_str_aligned(canvas, 0, 32, AlignLeft, AlignTop, stationText); 54 | canvas_draw_str_aligned(canvas, 0, 42, AlignLeft, AlignTop, pagerText); 55 | } else { 56 | canvas_draw_str_aligned(canvas, 0, 22, AlignLeft, AlignTop, "Sending Data"); 57 | } 58 | } 59 | 60 | static void meal_pager_transmit_model_init(Meal_PagerTransmitModel* const model) { 61 | FURI_LOG_D(TAG, "Scene 1 Model Init"); 62 | model->pager_type = 0; 63 | model->station = 0; 64 | model->pager = 0; 65 | model->sending = 0; 66 | } 67 | 68 | void meal_pager_transmit_model_set_type(Meal_PagerTransmit* instance, uint32_t type) { 69 | furi_assert(instance); 70 | Meal_PagerTransmitModel* model = view_get_model(instance->view); 71 | model->pager_type = type; 72 | view_commit_model(instance->view, false); 73 | } 74 | 75 | void meal_pager_transmit_model_set_station(Meal_PagerTransmit* instance, uint32_t station) { 76 | furi_assert(instance); 77 | Meal_PagerTransmitModel* model = view_get_model(instance->view); 78 | model->station = station; 79 | view_commit_model(instance->view, false); 80 | with_view_model( 81 | instance->view, Meal_PagerTransmitModel * model, { UNUSED(model); }, true); 82 | } 83 | 84 | void meal_pager_transmit_model_set_sending(Meal_PagerTransmit* instance, uint32_t value) { 85 | furi_assert(instance); 86 | Meal_PagerTransmitModel* model = view_get_model(instance->view); 87 | model->sending = value; 88 | view_commit_model(instance->view, false); 89 | with_view_model( 90 | instance->view, Meal_PagerTransmitModel * model, { UNUSED(model); }, true); 91 | } 92 | 93 | void meal_pager_transmit_model_set_pager(Meal_PagerTransmit* instance, uint32_t pager) { 94 | furi_assert(instance); 95 | Meal_PagerTransmitModel* model = view_get_model(instance->view); 96 | model->pager = pager; 97 | view_commit_model(instance->view, false); 98 | with_view_model( 99 | instance->view, Meal_PagerTransmitModel * model, { UNUSED(model); }, true); 100 | } 101 | 102 | bool meal_pager_transmit_input(InputEvent* event, void* context) { 103 | furi_assert(context); 104 | Meal_PagerTransmit* instance = context; 105 | if(event->type == InputTypeRelease) { 106 | switch(event->key) { 107 | case InputKeyBack: 108 | with_view_model( 109 | instance->view, 110 | Meal_PagerTransmitModel * model, 111 | { 112 | if(model->sending != 0) { 113 | //instance->callback(Meal_PagerCustomEventTransmitBack, instance->context); 114 | } 115 | }, 116 | true); 117 | break; 118 | case InputKeyLeft: 119 | case InputKeyRight: 120 | case InputKeyUp: 121 | case InputKeyDown: 122 | case InputKeyOk: 123 | with_view_model( 124 | instance->view, Meal_PagerTransmitModel * model, { UNUSED(model); }, true); 125 | break; 126 | case InputKeyMAX: 127 | break; 128 | } 129 | } 130 | return true; 131 | } 132 | 133 | void meal_pager_transmit_exit(void* context) { 134 | furi_assert(context); 135 | FURI_LOG_D(TAG, "Scene 1 Exit"); 136 | } 137 | 138 | void meal_pager_transmit_enter(void* context) { 139 | FURI_LOG_D(TAG, "Scene 1 Enter"); 140 | furi_assert(context); 141 | Meal_PagerTransmit* instance = (Meal_PagerTransmit*)context; 142 | with_view_model( 143 | instance->view, Meal_PagerTransmitModel * model, { UNUSED(model); }, true); 144 | } 145 | 146 | Meal_PagerTransmit* meal_pager_transmit_alloc(void* context) { 147 | FURI_LOG_D(TAG, "Scene 1 Alloc"); 148 | furi_assert(context); 149 | Meal_PagerTransmit* instance = malloc(sizeof(Meal_PagerTransmit)); 150 | instance->view = view_alloc(); 151 | view_allocate_model(instance->view, ViewModelTypeLocking, sizeof(Meal_PagerTransmitModel)); 152 | view_set_context(instance->view, instance); // furi_assert crashes in events without this 153 | view_set_draw_callback(instance->view, (ViewDrawCallback)meal_pager_transmit_draw); 154 | view_set_input_callback(instance->view, meal_pager_transmit_input); 155 | view_set_enter_callback(instance->view, meal_pager_transmit_enter); 156 | view_set_exit_callback(instance->view, meal_pager_transmit_exit); 157 | 158 | with_view_model( 159 | instance->view, 160 | Meal_PagerTransmitModel * model, 161 | { 162 | meal_pager_transmit_model_init(model); 163 | //meal_pager_transmit_model_set_type(instance, 0); 164 | }, 165 | true); 166 | 167 | return instance; 168 | } 169 | 170 | void meal_pager_transmit_free(Meal_PagerTransmit* instance) { 171 | FURI_LOG_D(TAG, "Transmit Free"); 172 | furi_assert(instance); 173 | 174 | with_view_model( 175 | instance->view, 176 | Meal_PagerTransmitModel * model, 177 | { 178 | model->pager_type = 0; 179 | model->station = 0; 180 | model->pager = 0; 181 | model->sending = 0; 182 | }, 183 | true); 184 | view_free(instance->view); 185 | free(instance); 186 | } 187 | 188 | View* meal_pager_transmit_get_view(Meal_PagerTransmit* instance) { 189 | furi_assert(instance); 190 | return instance->view; 191 | } 192 | -------------------------------------------------------------------------------- /scenes/meal_pager_scene_settings.c: -------------------------------------------------------------------------------- 1 | #include "../meal_pager_i.h" 2 | #include 3 | 4 | enum SettingsIndex { 5 | SettingsIndexHaptic = 10, 6 | SettingsIndexValue1, 7 | SettingsIndexValue2, 8 | }; 9 | 10 | const char* const haptic_text[2] = { 11 | "OFF", 12 | "ON", 13 | }; 14 | const uint32_t haptic_value[2] = { 15 | Meal_PagerHapticOff, 16 | Meal_PagerHapticOn, 17 | }; 18 | 19 | const char* const pager_type_text[4] = { 20 | "T119", 21 | "TD157", 22 | "TD165", 23 | "TD174", 24 | }; 25 | 26 | const uint32_t pager_type_value[4] = { 27 | Meal_PagerPagerTypeT119, 28 | Meal_PagerPagerTypeTD157, 29 | Meal_PagerPagerTypeTD165, 30 | Meal_PagerPagerTypeTD174, 31 | }; 32 | 33 | const char* const speaker_text[2] = { 34 | "OFF", 35 | "ON", 36 | }; 37 | const uint32_t speaker_value[2] = { 38 | Meal_PagerSpeakerOff, 39 | Meal_PagerSpeakerOn, 40 | }; 41 | 42 | const char* const led_text[2] = { 43 | "OFF", 44 | "ON", 45 | }; 46 | const uint32_t led_value[2] = { 47 | Meal_PagerLedOff, 48 | Meal_PagerLedOn, 49 | }; 50 | 51 | const char* const settings_text[2] = { 52 | "OFF", 53 | "ON", 54 | }; 55 | const uint32_t settings_value[2] = { 56 | Meal_PagerSettingsOff, 57 | Meal_PagerSettingsOn, 58 | }; 59 | 60 | static void meal_pager_scene_settings_set_pager_type(VariableItem* item) { 61 | Meal_Pager* app = variable_item_get_context(item); 62 | uint8_t index = variable_item_get_current_value_index(item); 63 | 64 | variable_item_set_current_value_text(item, pager_type_text[index]); 65 | app->pager_type = pager_type_value[index]; 66 | } 67 | 68 | static void meal_pager_scene_settings_set_first_station(VariableItem* item) { 69 | UNUSED(item); 70 | } 71 | 72 | static void meal_pager_scene_settings_set_last_station(VariableItem* item) { 73 | UNUSED(item); 74 | } 75 | 76 | static void meal_pager_scene_settings_set_first_pager(VariableItem* item) { 77 | Meal_Pager* app = variable_item_get_context(item); 78 | uint32_t index = variable_item_get_current_value_index(item); 79 | 80 | snprintf(app->first_pager_char, 20, "%lu", index); 81 | variable_item_set_current_value_text(item, app->first_pager_char); 82 | app->first_pager = index; 83 | } 84 | 85 | static void meal_pager_scene_settings_set_last_pager(VariableItem* item) { 86 | Meal_Pager* app = variable_item_get_context(item); 87 | uint32_t index = variable_item_get_current_value_index(item); 88 | 89 | snprintf(app->last_pager_char, 20, "%lu", index); 90 | variable_item_set_current_value_text(item, app->last_pager_char); 91 | app->last_pager = index; 92 | } 93 | 94 | static void meal_pager_scene_settings_set_repeats(VariableItem* item) { 95 | Meal_Pager* app = variable_item_get_context(item); 96 | uint32_t index = variable_item_get_current_value_index(item); 97 | 98 | snprintf(app->repeats_char, 20, "%lu", index); 99 | variable_item_set_current_value_text(item, app->repeats_char); 100 | app->repeats = index; 101 | } 102 | 103 | /*static void meal_pager_scene_settings_set_haptic(VariableItem* item) { 104 | Meal_Pager* app = variable_item_get_context(item); 105 | uint8_t index = variable_item_get_current_value_index(item); 106 | 107 | variable_item_set_current_value_text(item, haptic_text[index]); 108 | app->haptic = haptic_value[index]; 109 | } 110 | 111 | static void meal_pager_scene_settings_set_speaker(VariableItem* item) { 112 | Meal_Pager* app = variable_item_get_context(item); 113 | uint8_t index = variable_item_get_current_value_index(item); 114 | variable_item_set_current_value_text(item, speaker_text[index]); 115 | app->speaker = speaker_value[index]; 116 | }*/ 117 | 118 | static void meal_pager_scene_settings_set_led(VariableItem* item) { 119 | Meal_Pager* app = variable_item_get_context(item); 120 | uint8_t index = variable_item_get_current_value_index(item); 121 | variable_item_set_current_value_text(item, led_text[index]); 122 | app->led = led_value[index]; 123 | } 124 | 125 | static void meal_pager_scene_settings_set_save_settings(VariableItem* item) { 126 | Meal_Pager* app = variable_item_get_context(item); 127 | uint8_t index = variable_item_get_current_value_index(item); 128 | variable_item_set_current_value_text(item, settings_text[index]); 129 | app->save_settings = settings_value[index]; 130 | } 131 | 132 | void meal_pager_scene_settings_submenu_callback(void* context, uint32_t index) { 133 | Meal_Pager* app = context; 134 | view_dispatcher_send_custom_event(app->view_dispatcher, index); 135 | } 136 | 137 | void meal_pager_scene_settings_on_enter(void* context) { 138 | Meal_Pager* app = context; 139 | VariableItem* item; 140 | uint8_t value_index; 141 | 142 | // Pager Type 143 | item = variable_item_list_add( 144 | app->variable_item_list, "Pager Type:", 4, meal_pager_scene_settings_set_pager_type, app); 145 | value_index = value_index_uint32(app->pager_type, pager_type_value, 4); 146 | variable_item_set_current_value_index(item, value_index); 147 | variable_item_set_current_value_text(item, pager_type_text[value_index]); 148 | 149 | // First Station 150 | item = variable_item_list_add( 151 | app->variable_item_list, 152 | "First Station", 153 | 1, 154 | meal_pager_scene_settings_set_first_station, 155 | app); 156 | snprintf(app->first_pager_char, 20, "%lu", app->first_station); 157 | variable_item_set_current_value_text(item, app->first_station_char); 158 | 159 | // Last Station 160 | item = variable_item_list_add( 161 | app->variable_item_list, 162 | "Last Station", 163 | 1, 164 | meal_pager_scene_settings_set_last_station, 165 | app); 166 | snprintf(app->last_station_char, 20, "%lu", app->last_station); 167 | variable_item_set_current_value_text(item, app->last_station_char); 168 | 169 | // First Pager 170 | item = variable_item_list_add( 171 | app->variable_item_list, "First Pager", 1, meal_pager_scene_settings_set_first_pager, app); 172 | snprintf(app->first_pager_char, 20, "%lu", app->first_pager); 173 | variable_item_set_current_value_text(item, app->first_pager_char); 174 | 175 | // Last Pager 176 | item = variable_item_list_add( 177 | app->variable_item_list, "Last Pager", 1, meal_pager_scene_settings_set_last_pager, app); 178 | snprintf(app->last_pager_char, 20, "%lu", app->last_pager); 179 | variable_item_set_current_value_text(item, app->last_pager_char); 180 | 181 | // Repeat Attacks 182 | item = variable_item_list_add( 183 | app->variable_item_list, "Signal Repeat", 11, meal_pager_scene_settings_set_repeats, app); 184 | variable_item_set_current_value_index(item, app->repeats); 185 | snprintf(app->repeats_char, 20, "%lu", app->repeats); 186 | variable_item_set_current_value_text(item, app->repeats_char); 187 | 188 | // Vibro on/off Disabled until used 189 | /*item = variable_item_list_add( 190 | app->variable_item_list, "Vibro/Haptic:", 2, meal_pager_scene_settings_set_haptic, app); 191 | value_index = value_index_uint32(app->haptic, haptic_value, 2); 192 | variable_item_set_current_value_index(item, value_index); 193 | variable_item_set_current_value_text(item, haptic_text[value_index]);*/ 194 | 195 | // Sound on/off Disabled until used 196 | /*item = variable_item_list_add( 197 | app->variable_item_list, "Sound:", 2, meal_pager_scene_settings_set_speaker, app); 198 | value_index = value_index_uint32(app->speaker, speaker_value, 2); 199 | variable_item_set_current_value_index(item, value_index); 200 | variable_item_set_current_value_text(item, speaker_text[value_index]);*/ 201 | 202 | // LED Effects on/off 203 | item = variable_item_list_add( 204 | app->variable_item_list, "LED FX:", 2, meal_pager_scene_settings_set_led, app); 205 | value_index = value_index_uint32(app->led, led_value, 2); 206 | variable_item_set_current_value_index(item, value_index); 207 | variable_item_set_current_value_text(item, led_text[value_index]); 208 | 209 | // Save Settings to File 210 | item = variable_item_list_add( 211 | app->variable_item_list, 212 | "Save Settings", 213 | 2, 214 | meal_pager_scene_settings_set_save_settings, 215 | app); 216 | value_index = value_index_uint32(app->save_settings, settings_value, 2); 217 | variable_item_set_current_value_index(item, value_index); 218 | variable_item_set_current_value_text(item, settings_text[value_index]); 219 | 220 | view_dispatcher_switch_to_view(app->view_dispatcher, Meal_PagerViewIdSettings); 221 | } 222 | 223 | bool meal_pager_scene_settings_on_event(void* context, SceneManagerEvent event) { 224 | Meal_Pager* app = context; 225 | UNUSED(app); 226 | bool consumed = false; 227 | if(event.type == SceneManagerEventTypeCustom) { 228 | } 229 | return consumed; 230 | } 231 | 232 | void meal_pager_scene_settings_on_exit(void* context) { 233 | Meal_Pager* app = context; 234 | variable_item_list_set_selected_item(app->variable_item_list, 0); 235 | variable_item_list_reset(app->variable_item_list); 236 | meal_pager_set_max_values(app); 237 | } -------------------------------------------------------------------------------- /helpers/subghz/subghz_i.c: -------------------------------------------------------------------------------- 1 | #include "subghz_i.h" 2 | 3 | #include "subghz/types.h" 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include 14 | #include 15 | #include 16 | 17 | //#define TAG "SubGhz" 18 | /* 19 | void subghz_set_default_preset(SubGhz* subghz) { 20 | furi_assert(subghz); 21 | subghz_txrx_set_preset( 22 | subghz->txrx, 23 | "AM650", 24 | subghz_setting_get_default_frequency(subghz_txrx_get_setting(subghz->txrx)), 25 | NULL, 26 | 0); 27 | }*/ 28 | 29 | /*bool subghz_tx_start(SubGhz* subghz, FlipperFormat* flipper_format) { 30 | switch(subghz_txrx_tx_start(subghz->txrx, flipper_format)) { 31 | case SubGhzTxRxStartTxStateErrorParserOthers: 32 | dialog_message_show_storage_error( 33 | subghz->dialogs, "Error in protocol\nparameters\ndescription"); 34 | break; 35 | case SubGhzTxRxStartTxStateErrorOnlyRx: 36 | FURI_LOG_D(TAG, 'Cannot send, only RX possible'); 37 | break; 38 | 39 | default: 40 | return true; 41 | break; 42 | } 43 | return false; 44 | }*/ 45 | 46 | bool subghz_key_load(SubGhz* subghz, const char* file_path) { //, bool show_dialog) { 47 | furi_assert(subghz); 48 | furi_assert(file_path); 49 | 50 | Storage* storage = furi_record_open(RECORD_STORAGE); 51 | FlipperFormat* fff_data_file = flipper_format_file_alloc(storage); 52 | Stream* fff_data_stream = 53 | flipper_format_get_raw_stream(subghz_txrx_get_fff_data(subghz->txrx)); 54 | 55 | SubGhzLoadKeyState load_key_state = SubGhzLoadKeyStateParseErr; 56 | FuriString* temp_str = furi_string_alloc(); 57 | uint32_t temp_data32; 58 | 59 | do { 60 | stream_clean(fff_data_stream); 61 | if(!flipper_format_file_open_existing(fff_data_file, file_path)) { 62 | FURI_LOG_E(TAG, "Error open file %s", file_path); 63 | break; 64 | } 65 | 66 | if(!flipper_format_read_header(fff_data_file, temp_str, &temp_data32)) { 67 | FURI_LOG_E(TAG, "Missing or incorrect header"); 68 | break; 69 | } 70 | 71 | if(((!strcmp(furi_string_get_cstr(temp_str), SUBGHZ_KEY_FILE_TYPE)) || 72 | (!strcmp(furi_string_get_cstr(temp_str), SUBGHZ_RAW_FILE_TYPE))) && 73 | temp_data32 == SUBGHZ_KEY_FILE_VERSION) { 74 | } else { 75 | FURI_LOG_E(TAG, "Type or version mismatch"); 76 | break; 77 | } 78 | 79 | //Load frequency 80 | if(!flipper_format_read_uint32(fff_data_file, "Frequency", &temp_data32, 1)) { 81 | FURI_LOG_E(TAG, "Missing Frequency"); 82 | break; 83 | } 84 | 85 | if(!subghz_txrx_radio_device_is_frequecy_valid(subghz->txrx, temp_data32)) { 86 | FURI_LOG_E(TAG, "Frequency not supported"); 87 | break; 88 | } 89 | 90 | //Load preset 91 | if(!flipper_format_read_string(fff_data_file, "Preset", temp_str)) { 92 | FURI_LOG_E(TAG, "Missing Preset"); 93 | break; 94 | } 95 | 96 | furi_string_set_str( 97 | temp_str, subghz_txrx_get_preset_name(subghz->txrx, furi_string_get_cstr(temp_str))); 98 | if(!strcmp(furi_string_get_cstr(temp_str), "")) { 99 | break; 100 | } 101 | SubGhzSetting* setting = subghz_txrx_get_setting(subghz->txrx); 102 | 103 | if(!strcmp(furi_string_get_cstr(temp_str), "CUSTOM")) { 104 | //TODO FL-3551: add Custom_preset_module 105 | //delete preset if it already exists 106 | subghz_setting_delete_custom_preset(setting, furi_string_get_cstr(temp_str)); 107 | //load custom preset from file 108 | if(!subghz_setting_load_custom_preset( 109 | setting, furi_string_get_cstr(temp_str), fff_data_file)) { 110 | FURI_LOG_E(TAG, "Missing Custom preset"); 111 | break; 112 | } 113 | } 114 | size_t preset_index = 115 | subghz_setting_get_inx_preset_by_name(setting, furi_string_get_cstr(temp_str)); 116 | subghz_txrx_set_preset( 117 | subghz->txrx, 118 | furi_string_get_cstr(temp_str), 119 | temp_data32, 120 | subghz_setting_get_preset_data(setting, preset_index), 121 | subghz_setting_get_preset_data_size(setting, preset_index)); 122 | 123 | //Load protocol 124 | if(!flipper_format_read_string(fff_data_file, "Protocol", temp_str)) { 125 | FURI_LOG_E(TAG, "Missing Protocol"); 126 | break; 127 | } 128 | 129 | FlipperFormat* fff_data = subghz_txrx_get_fff_data(subghz->txrx); 130 | if(!strcmp(furi_string_get_cstr(temp_str), "RAW")) { 131 | //if RAW 132 | subghz->load_type_file = SubGhzLoadTypeFileRaw; 133 | subghz_protocol_raw_gen_fff_data( 134 | fff_data, file_path, subghz_txrx_radio_device_get_name(subghz->txrx)); 135 | } else { 136 | subghz->load_type_file = SubGhzLoadTypeFileKey; 137 | stream_copy_full( 138 | flipper_format_get_raw_stream(fff_data_file), 139 | flipper_format_get_raw_stream(fff_data)); 140 | } 141 | 142 | if(subghz_txrx_load_decoder_by_name_protocol( 143 | subghz->txrx, furi_string_get_cstr(temp_str))) { 144 | SubGhzProtocolStatus status = subghz_protocol_decoder_base_deserialize( 145 | subghz_txrx_get_decoder(subghz->txrx), fff_data); 146 | if(status != SubGhzProtocolStatusOk) { 147 | load_key_state = SubGhzLoadKeyStateProtocolDescriptionErr; 148 | break; 149 | } 150 | } else { 151 | FURI_LOG_E(TAG, "Protocol not found"); 152 | break; 153 | } 154 | 155 | load_key_state = SubGhzLoadKeyStateOK; 156 | } while(0); 157 | 158 | furi_string_free(temp_str); 159 | flipper_format_free(fff_data_file); 160 | furi_record_close(RECORD_STORAGE); 161 | 162 | switch(load_key_state) { 163 | case SubGhzLoadKeyStateParseErr: 164 | //if(show_dialog) { 165 | // dialog_message_show_storage_error(subghz->dialogs, "Cannot parse\nfile"); 166 | //} 167 | return false; 168 | case SubGhzLoadKeyStateProtocolDescriptionErr: 169 | //if(show_dialog) { 170 | // dialog_message_show_storage_error( 171 | // subghz->dialogs, "Error in protocol\nparameters\ndescription"); 172 | //} 173 | return false; 174 | 175 | case SubGhzLoadKeyStateOK: 176 | return true; 177 | 178 | default: 179 | furi_crash("SubGhz: Unknown load_key_state."); 180 | return false; 181 | } 182 | return false; 183 | } 184 | 185 | /*SubGhzLoadTypeFile subghz_get_load_type_file(SubGhz* subghz) { 186 | furi_assert(subghz); 187 | return subghz->load_type_file; 188 | }*/ 189 | 190 | bool subghz_load_protocol_from_file(SubGhz* subghz) { 191 | furi_assert(subghz); 192 | 193 | //FuriString* file_path = furi_string_alloc(); 194 | 195 | bool res = false; 196 | 197 | /*DialogsFileBrowserOptions browser_options; 198 | dialog_file_browser_set_basic_options( 199 | &browser_options, SUBGHZ_APP_FILENAME_EXTENSION, &I_sub1_10px); 200 | browser_options.base_path = SUBGHZ_APP_FOLDER; 201 | 202 | // Input events and views are managed by file_select 203 | bool res = dialog_file_browser_show( 204 | subghz->dialogs, subghz->file_path, subghz->file_path, &browser_options); 205 | 206 | if(res) {*/ 207 | //res = subghz_key_load(subghz, furi_string_get_cstr(subghz->file_path), true); 208 | res = subghz_key_load(subghz, MEAL_PAGER_TMP_FILE); //, true); 209 | //} 210 | 211 | //furi_string_free(file_path); 212 | 213 | return res; 214 | } 215 | 216 | /*bool subghz_file_available(SubGhz* subghz) { 217 | furi_assert(subghz); 218 | bool ret = true; 219 | Storage* storage = furi_record_open(RECORD_STORAGE); 220 | 221 | FS_Error fs_result = 222 | storage_common_stat(storage, furi_string_get_cstr(subghz->file_path), NULL); 223 | 224 | if(fs_result != FSE_OK) { 225 | dialog_message_show_storage_error(subghz->dialogs, "File not available\n file/directory"); 226 | ret = false; 227 | } 228 | 229 | furi_record_close(RECORD_STORAGE); 230 | return ret; 231 | }*/ 232 | 233 | /*bool subghz_path_is_file(FuriString* path) { 234 | return furi_string_end_with(path, SUBGHZ_APP_FILENAME_EXTENSION); 235 | }*/ 236 | 237 | /*void subghz_lock(SubGhz* subghz) { 238 | furi_assert(subghz); 239 | subghz->lock = SubGhzLockOn; 240 | }*/ 241 | 242 | /*void subghz_unlock(SubGhz* subghz) { 243 | furi_assert(subghz); 244 | subghz->lock = SubGhzLockOff; 245 | }*/ 246 | 247 | /*bool subghz_is_locked(SubGhz* subghz) { 248 | furi_assert(subghz); 249 | return (subghz->lock == SubGhzLockOn); 250 | }*/ 251 | 252 | /*void subghz_rx_key_state_set(SubGhz* subghz, SubGhzRxKeyState state) { 253 | furi_assert(subghz); 254 | subghz->rx_key_state = state; 255 | }*/ 256 | 257 | /*SubGhzRxKeyState subghz_rx_key_state_get(SubGhz* subghz) { 258 | furi_assert(subghz); 259 | return subghz->rx_key_state; 260 | }*/ 261 | -------------------------------------------------------------------------------- /helpers/subghz/subghz_txrx.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "subghz_types.h" 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | typedef struct SubGhzTxRx SubGhzTxRx; 13 | 14 | typedef void (*SubGhzTxRxNeedSaveCallback)(void* context); 15 | 16 | typedef enum { 17 | SubGhzTxRxStartTxStateOk, 18 | SubGhzTxRxStartTxStateErrorOnlyRx, 19 | SubGhzTxRxStartTxStateErrorParserOthers, 20 | } SubGhzTxRxStartTxState; 21 | 22 | /** 23 | * Allocate SubGhzTxRx 24 | * 25 | * @return SubGhzTxRx* pointer to SubGhzTxRx 26 | */ 27 | SubGhzTxRx* subghz_txrx_alloc(); 28 | 29 | /** 30 | * Free SubGhzTxRx 31 | * 32 | * @param instance Pointer to a SubGhzTxRx 33 | */ 34 | void subghz_txrx_free(SubGhzTxRx* instance); 35 | 36 | /** 37 | * Check if the database is loaded 38 | * 39 | * @param instance Pointer to a SubGhzTxRx 40 | * @return bool True if the database is loaded 41 | */ 42 | //bool subghz_txrx_is_database_loaded(SubGhzTxRx* instance); 43 | 44 | /** 45 | * Set preset 46 | * 47 | * @param instance Pointer to a SubGhzTxRx 48 | * @param preset_name Name of preset 49 | * @param frequency Frequency in Hz 50 | * @param preset_data Data of preset 51 | * @param preset_data_size Size of preset data 52 | */ 53 | void subghz_txrx_set_preset( 54 | SubGhzTxRx* instance, 55 | const char* preset_name, 56 | uint32_t frequency, 57 | uint8_t* preset_data, 58 | size_t preset_data_size); 59 | 60 | /** 61 | * Get name of preset 62 | * 63 | * @param instance Pointer to a SubGhzTxRx 64 | * @param preset String of preset 65 | * @return const char* Name of preset 66 | */ 67 | const char* subghz_txrx_get_preset_name(SubGhzTxRx* instance, const char* preset); 68 | 69 | /** 70 | * Get of preset 71 | * 72 | * @param instance Pointer to a SubGhzTxRx 73 | * @return SubGhzRadioPreset Preset 74 | */ 75 | //SubGhzRadioPreset subghz_txrx_get_preset(SubGhzTxRx* instance); 76 | 77 | /** 78 | * Get string frequency and modulation 79 | * 80 | * @param instance Pointer to a SubGhzTxRx 81 | * @param frequency Pointer to a string frequency 82 | * @param modulation Pointer to a string modulation 83 | */ 84 | /*void subghz_txrx_get_frequency_and_modulation( 85 | SubGhzTxRx* instance, 86 | FuriString* frequency, 87 | FuriString* modulation);*/ 88 | 89 | /** 90 | * Start TX CC1101 91 | * 92 | * @param instance Pointer to a SubGhzTxRx 93 | * @param flipper_format Pointer to a FlipperFormat 94 | * @return SubGhzTxRxStartTxState 95 | */ 96 | SubGhzTxRxStartTxState subghz_txrx_tx_start(SubGhzTxRx* instance, FlipperFormat* flipper_format); 97 | 98 | /** 99 | * Start RX CC1101 100 | * 101 | * @param instance Pointer to a SubGhzTxRx 102 | */ 103 | //void subghz_txrx_rx_start(SubGhzTxRx* instance); 104 | 105 | /** 106 | * Stop TX/RX CC1101 107 | * 108 | * @param instance Pointer to a SubGhzTxRx 109 | */ 110 | void subghz_txrx_stop(SubGhzTxRx* instance); 111 | 112 | /** 113 | * Set sleep mode CC1101 114 | * 115 | * @param instance Pointer to a SubGhzTxRx 116 | */ 117 | //void subghz_txrx_sleep(SubGhzTxRx* instance); 118 | 119 | /** 120 | * Update frequency CC1101 in automatic mode (hopper) 121 | * 122 | * @param instance Pointer to a SubGhzTxRx 123 | */ 124 | //void subghz_txrx_hopper_update(SubGhzTxRx* instance); 125 | 126 | /** 127 | * Get state hopper 128 | * 129 | * @param instance Pointer to a SubGhzTxRx 130 | * @return SubGhzHopperState 131 | */ 132 | //SubGhzHopperState subghz_txrx_hopper_get_state(SubGhzTxRx* instance); 133 | 134 | /** 135 | * Set state hopper 136 | * 137 | * @param instance Pointer to a SubGhzTxRx 138 | * @param state State hopper 139 | */ 140 | void subghz_txrx_hopper_set_state(SubGhzTxRx* instance, SubGhzHopperState state); 141 | 142 | /** 143 | * Unpause hopper 144 | * 145 | * @param instance Pointer to a SubGhzTxRx 146 | */ 147 | //void subghz_txrx_hopper_unpause(SubGhzTxRx* instance); 148 | 149 | /** 150 | * Set pause hopper 151 | * 152 | * @param instance Pointer to a SubGhzTxRx 153 | */ 154 | //void subghz_txrx_hopper_pause(SubGhzTxRx* instance); 155 | 156 | /** 157 | * Speaker on 158 | * 159 | * @param instance Pointer to a SubGhzTxRx 160 | */ 161 | void subghz_txrx_speaker_on(SubGhzTxRx* instance); 162 | 163 | /** 164 | * Speaker off 165 | * 166 | * @param instance Pointer to a SubGhzTxRx 167 | */ 168 | void subghz_txrx_speaker_off(SubGhzTxRx* instance); 169 | 170 | /** 171 | * Speaker mute 172 | * 173 | * @param instance Pointer to a SubGhzTxRx 174 | */ 175 | //void subghz_txrx_speaker_mute(SubGhzTxRx* instance); 176 | 177 | /** 178 | * Speaker unmute 179 | * 180 | * @param instance Pointer to a SubGhzTxRx 181 | */ 182 | void subghz_txrx_speaker_unmute(SubGhzTxRx* instance); 183 | 184 | /** 185 | * Set state speaker 186 | * 187 | * @param instance Pointer to a SubGhzTxRx 188 | * @param state State speaker 189 | */ 190 | void subghz_txrx_speaker_set_state(SubGhzTxRx* instance, SubGhzSpeakerState state); 191 | 192 | /** 193 | * Get state speaker 194 | * 195 | * @param instance Pointer to a SubGhzTxRx 196 | * @return SubGhzSpeakerState 197 | */ 198 | //SubGhzSpeakerState subghz_txrx_speaker_get_state(SubGhzTxRx* instance); 199 | 200 | /** 201 | * load decoder by name protocol 202 | * 203 | * @param instance Pointer to a SubGhzTxRx 204 | * @param name_protocol Name protocol 205 | * @return bool True if the decoder is loaded 206 | */ 207 | bool subghz_txrx_load_decoder_by_name_protocol(SubGhzTxRx* instance, const char* name_protocol); 208 | 209 | /** 210 | * Get decoder 211 | * 212 | * @param instance Pointer to a SubGhzTxRx 213 | * @return SubGhzProtocolDecoderBase* Pointer to a SubGhzProtocolDecoderBase 214 | */ 215 | SubGhzProtocolDecoderBase* subghz_txrx_get_decoder(SubGhzTxRx* instance); 216 | 217 | /** 218 | * Set callback for save data 219 | * 220 | * @param instance Pointer to a SubGhzTxRx 221 | * @param callback Callback for save data 222 | * @param context Context for callback 223 | */ 224 | /*void subghz_txrx_set_need_save_callback( 225 | SubGhzTxRx* instance, 226 | SubGhzTxRxNeedSaveCallback callback, 227 | void* context);*/ 228 | 229 | /** 230 | * Get pointer to a load data key 231 | * 232 | * @param instance Pointer to a SubGhzTxRx 233 | * @return FlipperFormat* 234 | */ 235 | FlipperFormat* subghz_txrx_get_fff_data(SubGhzTxRx* instance); 236 | 237 | /** 238 | * Get pointer to a SugGhzSetting 239 | * 240 | * @param instance Pointer to a SubGhzTxRx 241 | * @return SubGhzSetting* 242 | */ 243 | SubGhzSetting* subghz_txrx_get_setting(SubGhzTxRx* instance); 244 | 245 | /** 246 | * Is it possible to save this protocol 247 | * 248 | * @param instance Pointer to a SubGhzTxRx 249 | * @return bool True if it is possible to save this protocol 250 | */ 251 | //bool subghz_txrx_protocol_is_serializable(SubGhzTxRx* instance); 252 | 253 | /** 254 | * Is it possible to send this protocol 255 | * 256 | * @param instance Pointer to a SubGhzTxRx 257 | * @return bool True if it is possible to send this protocol 258 | */ 259 | //bool subghz_txrx_protocol_is_transmittable(SubGhzTxRx* instance, bool check_type); 260 | 261 | /** 262 | * Set filter, what types of decoder to use 263 | * 264 | * @param instance Pointer to a SubGhzTxRx 265 | * @param filter Filter 266 | */ 267 | //void subghz_txrx_receiver_set_filter(SubGhzTxRx* instance, SubGhzProtocolFlag filter); 268 | 269 | /** 270 | * Set callback for receive data 271 | * 272 | * @param instance Pointer to a SubGhzTxRx 273 | * @param callback Callback for receive data 274 | * @param context Context for callback 275 | */ 276 | /*void subghz_txrx_set_rx_calback( 277 | SubGhzTxRx* instance, 278 | SubGhzReceiverCallback callback, 279 | void* context);*/ 280 | 281 | /** 282 | * Set callback for Raw decoder, end of data transfer 283 | * 284 | * @param instance Pointer to a SubGhzTxRx 285 | * @param callback Callback for Raw decoder, end of data transfer 286 | * @param context Context for callback 287 | */ 288 | void subghz_txrx_set_raw_file_encoder_worker_callback_end( 289 | SubGhzTxRx* instance, 290 | SubGhzProtocolEncoderRAWCallbackEnd callback, 291 | void* context); 292 | 293 | /* Checking if an external radio device is connected 294 | * 295 | * @param instance Pointer to a SubGhzTxRx 296 | * @param name Name of external radio device 297 | * @return bool True if is connected to the external radio device 298 | */ 299 | bool subghz_txrx_radio_device_is_external_connected(SubGhzTxRx* instance, const char* name); 300 | 301 | /* Set the selected radio device to use 302 | * 303 | * @param instance Pointer to a SubGhzTxRx 304 | * @param radio_device_type Radio device type 305 | * @return SubGhzRadioDeviceType Type of installed radio device 306 | */ 307 | SubGhzRadioDeviceType 308 | subghz_txrx_radio_device_set(SubGhzTxRx* instance, SubGhzRadioDeviceType radio_device_type); 309 | 310 | /* Get the selected radio device to use 311 | * 312 | * @param instance Pointer to a SubGhzTxRx 313 | * @return SubGhzRadioDeviceType Type of installed radio device 314 | */ 315 | //SubGhzRadioDeviceType subghz_txrx_radio_device_get(SubGhzTxRx* instance); 316 | 317 | /* Get RSSI the selected radio device to use 318 | * 319 | * @param instance Pointer to a SubGhzTxRx 320 | * @return float RSSI 321 | */ 322 | //float subghz_txrx_radio_device_get_rssi(SubGhzTxRx* instance); 323 | 324 | /* Get name the selected radio device to use 325 | * 326 | * @param instance Pointer to a SubGhzTxRx 327 | * @return const char* Name of installed radio device 328 | */ 329 | const char* subghz_txrx_radio_device_get_name(SubGhzTxRx* instance); 330 | 331 | /* Get get intelligence whether frequency the selected radio device to use 332 | * 333 | * @param instance Pointer to a SubGhzTxRx 334 | * @return bool True if the frequency is valid 335 | */ 336 | bool subghz_txrx_radio_device_is_frequecy_valid(SubGhzTxRx* instance, uint32_t frequency); -------------------------------------------------------------------------------- /helpers/meal_pager_storage.c: -------------------------------------------------------------------------------- 1 | #include "meal_pager_storage.h" 2 | 3 | static Storage* meal_pager_open_storage() { 4 | return furi_record_open(RECORD_STORAGE); 5 | } 6 | 7 | static void meal_pager_close_storage() { 8 | furi_record_close(RECORD_STORAGE); 9 | } 10 | 11 | static void meal_pager_close_config_file(FlipperFormat* file) { 12 | if(file == NULL) return; 13 | flipper_format_rewind(file); 14 | flipper_format_file_close(file); 15 | flipper_format_free(file); 16 | } 17 | 18 | bool meal_pager_save_subghz_buffer_file_start( 19 | void* context, 20 | FlipperFormat* ff, 21 | Storage* storage, 22 | char* frequency) { 23 | // SubGhz TXRX can only be loaded with files, makes sense as to save RAM 24 | Meal_Pager* app = context; 25 | UNUSED(app); 26 | bool success = false; 27 | FURI_LOG_D(TAG, "Creating Temp File"); 28 | //Storage* storage = furi_record_open(RECORD_STORAGE); 29 | //FlipperFormat* ff = flipper_format_file_alloc(storage); 30 | 31 | // Overwrite wont work, so delete first 32 | if(storage_file_exists(storage, MEAL_PAGER_TMP_FILE)) { 33 | bool stored = storage_simply_remove(storage, MEAL_PAGER_TMP_FILE); 34 | if(!stored) { 35 | FURI_LOG_D(TAG, "Cannot remove file, seems to be open"); 36 | return success; 37 | } 38 | } 39 | 40 | // Open File, create if not exists 41 | if(!storage_common_stat(storage, MEAL_PAGER_TMP_FILE, NULL) == FSE_OK) { 42 | FURI_LOG_D( 43 | TAG, "Config file %s is not found. Will create new.", MEAL_PAGER_SETTINGS_SAVE_PATH); 44 | if(storage_common_stat(storage, CONFIG_FILE_DIRECTORY_PATH, NULL) == FSE_NOT_EXIST) { 45 | FURI_LOG_D( 46 | TAG, "Directory %s doesn't exist. Will create new.", CONFIG_FILE_DIRECTORY_PATH); 47 | if(!storage_simply_mkdir(storage, CONFIG_FILE_DIRECTORY_PATH)) { 48 | FURI_LOG_D(TAG, "Error creating directory %s", CONFIG_FILE_DIRECTORY_PATH); 49 | } 50 | } 51 | } 52 | 53 | if(!flipper_format_file_open_new(ff, MEAL_PAGER_TMP_FILE)) { 54 | //totp_close_config_file(fff_file); 55 | FURI_LOG_D(TAG, "Error creating new file %s", MEAL_PAGER_TMP_FILE); 56 | meal_pager_close_storage(); 57 | return success; 58 | } 59 | 60 | success = flipper_format_write_header_cstr( 61 | ff, MEAL_PAGER_SUBGHZ_FILE_TYPE, MEAL_PAGER_SUBGHZ_FILE_VERSION) && 62 | flipper_format_write_string_cstr(ff, "Frequency", frequency) && 63 | flipper_format_write_string_cstr(ff, "Preset", MEAL_PAGER_SUBGHZ_FILE_PRESET) && 64 | flipper_format_write_string_cstr(ff, "Protocol", MEAL_PAGER_SUBGHZ_FILE_Protocol); 65 | return success; 66 | } 67 | 68 | void meal_pager_save_subghz_buffer_stop(void* context, FlipperFormat* ff) { 69 | Meal_Pager* app = context; 70 | UNUSED(app); 71 | FURI_LOG_D(TAG, "Closing Temp File"); 72 | if(!flipper_format_rewind(ff)) { 73 | meal_pager_close_config_file(ff); 74 | FURI_LOG_E(TAG, "Rewind error"); 75 | meal_pager_close_storage(); 76 | return; 77 | } 78 | 79 | meal_pager_close_config_file(ff); 80 | meal_pager_close_storage(); 81 | } 82 | 83 | void meal_pager_save_settings(void* context) { 84 | Meal_Pager* app = context; 85 | if(app->save_settings == 0) { 86 | FURI_LOG_D(TAG, "Skipping Save because Disabled"); 87 | return; 88 | } 89 | 90 | FURI_LOG_D(TAG, "Saving Settings to File"); 91 | Storage* storage = meal_pager_open_storage(); 92 | FlipperFormat* fff_file = flipper_format_file_alloc(storage); 93 | 94 | // Overwrite wont work, so delete first 95 | if(storage_file_exists(storage, MEAL_PAGER_SETTINGS_SAVE_PATH)) { 96 | storage_simply_remove(storage, MEAL_PAGER_SETTINGS_SAVE_PATH); 97 | } 98 | 99 | // Open File, create if not exists 100 | if(!storage_common_stat(storage, MEAL_PAGER_SETTINGS_SAVE_PATH, NULL) == FSE_OK) { 101 | FURI_LOG_D( 102 | TAG, "Config file %s is not found. Will create new.", MEAL_PAGER_SETTINGS_SAVE_PATH); 103 | if(storage_common_stat(storage, CONFIG_FILE_DIRECTORY_PATH, NULL) == FSE_NOT_EXIST) { 104 | FURI_LOG_D( 105 | TAG, "Directory %s doesn't exist. Will create new.", CONFIG_FILE_DIRECTORY_PATH); 106 | if(!storage_simply_mkdir(storage, CONFIG_FILE_DIRECTORY_PATH)) { 107 | FURI_LOG_E(TAG, "Error creating directory %s", CONFIG_FILE_DIRECTORY_PATH); 108 | } 109 | } 110 | } 111 | 112 | if(!flipper_format_file_open_new(fff_file, MEAL_PAGER_SETTINGS_SAVE_PATH)) { 113 | //totp_close_config_file(fff_file); 114 | FURI_LOG_E(TAG, "Error creating new file %s", MEAL_PAGER_SETTINGS_SAVE_PATH); 115 | meal_pager_close_storage(); 116 | return; 117 | } 118 | 119 | // Store Settings 120 | flipper_format_write_header_cstr( 121 | fff_file, MEAL_PAGER_SETTINGS_HEADER, MEAL_PAGER_SETTINGS_FILE_VERSION); 122 | flipper_format_write_uint32(fff_file, MEAL_PAGER_SETTINGS_KEY_PAGER_TYPE, &app->pager_type, 1); 123 | flipper_format_write_uint32( 124 | fff_file, MEAL_PAGER_SETTINGS_KEY_FIRST_STATION, &app->first_station, 1); 125 | flipper_format_write_uint32( 126 | fff_file, MEAL_PAGER_SETTINGS_KEY_LAST_STATION, &app->last_station, 1); 127 | flipper_format_write_uint32( 128 | fff_file, MEAL_PAGER_SETTINGS_KEY_FIRST_PAGER, &app->first_pager, 1); 129 | flipper_format_write_uint32(fff_file, MEAL_PAGER_SETTINGS_KEY_LAST_PAGER, &app->last_pager, 1); 130 | flipper_format_write_uint32(fff_file, MEAL_PAGER_SETTINGS_KEY_LAST_PAGER, &app->last_pager, 1); 131 | flipper_format_write_uint32(fff_file, MEAL_PAGER_SETTINGS_KEY_REPEATS, &app->repeats, 1); 132 | flipper_format_write_uint32(fff_file, MEAL_PAGER_SETTINGS_KEY_HAPTIC, &app->haptic, 1); 133 | flipper_format_write_uint32(fff_file, MEAL_PAGER_SETTINGS_KEY_SPEAKER, &app->speaker, 1); 134 | flipper_format_write_uint32(fff_file, MEAL_PAGER_SETTINGS_KEY_LED, &app->led, 1); 135 | flipper_format_write_uint32( 136 | fff_file, MEAL_PAGER_SETTINGS_KEY_SAVE_SETTINGS, &app->save_settings, 1); 137 | 138 | if(!flipper_format_rewind(fff_file)) { 139 | meal_pager_close_config_file(fff_file); 140 | FURI_LOG_E(TAG, "Rewind error"); 141 | meal_pager_close_storage(); 142 | return; 143 | } 144 | 145 | meal_pager_close_config_file(fff_file); 146 | meal_pager_close_storage(); 147 | } 148 | 149 | void meal_pager_read_settings(void* context) { 150 | Meal_Pager* app = context; 151 | Storage* storage = meal_pager_open_storage(); 152 | FlipperFormat* fff_file = flipper_format_file_alloc(storage); 153 | 154 | if(storage_common_stat(storage, MEAL_PAGER_SETTINGS_SAVE_PATH, NULL) != FSE_OK) { 155 | meal_pager_close_config_file(fff_file); 156 | meal_pager_close_storage(); 157 | return; 158 | } 159 | uint32_t file_version; 160 | FuriString* temp_str = furi_string_alloc(); 161 | 162 | if(!flipper_format_file_open_existing(fff_file, MEAL_PAGER_SETTINGS_SAVE_PATH)) { 163 | FURI_LOG_E(TAG, "Cannot open file %s", MEAL_PAGER_SETTINGS_SAVE_PATH); 164 | meal_pager_close_config_file(fff_file); 165 | meal_pager_close_storage(); 166 | furi_string_free(temp_str); 167 | return; 168 | } 169 | 170 | if(!flipper_format_read_header(fff_file, temp_str, &file_version)) { 171 | FURI_LOG_E(TAG, "Missing Header Data"); 172 | meal_pager_close_config_file(fff_file); 173 | meal_pager_close_storage(); 174 | furi_string_free(temp_str); 175 | return; 176 | } 177 | 178 | if(file_version < MEAL_PAGER_SETTINGS_FILE_VERSION) { 179 | FURI_LOG_I(TAG, "old config version, will be removed."); 180 | meal_pager_close_config_file(fff_file); 181 | meal_pager_close_storage(); 182 | furi_string_free(temp_str); 183 | return; 184 | } 185 | 186 | flipper_format_read_uint32(fff_file, MEAL_PAGER_SETTINGS_KEY_PAGER_TYPE, &app->pager_type, 1); 187 | meal_pager_set_max_values(app); 188 | flipper_format_read_uint32( 189 | fff_file, MEAL_PAGER_SETTINGS_KEY_FIRST_STATION, &app->first_station, 1); 190 | flipper_format_read_uint32( 191 | fff_file, MEAL_PAGER_SETTINGS_KEY_LAST_STATION, &app->last_station, 1); 192 | flipper_format_read_uint32( 193 | fff_file, MEAL_PAGER_SETTINGS_KEY_FIRST_PAGER, &app->first_pager, 1); 194 | flipper_format_read_uint32(fff_file, MEAL_PAGER_SETTINGS_KEY_LAST_PAGER, &app->last_pager, 1); 195 | flipper_format_read_uint32(fff_file, MEAL_PAGER_SETTINGS_KEY_REPEATS, &app->repeats, 1); 196 | flipper_format_read_uint32(fff_file, MEAL_PAGER_SETTINGS_KEY_HAPTIC, &app->haptic, 1); 197 | flipper_format_read_uint32(fff_file, MEAL_PAGER_SETTINGS_KEY_SPEAKER, &app->speaker, 1); 198 | flipper_format_read_uint32(fff_file, MEAL_PAGER_SETTINGS_KEY_LED, &app->led, 1); 199 | flipper_format_read_uint32( 200 | fff_file, MEAL_PAGER_SETTINGS_KEY_SAVE_SETTINGS, &app->save_settings, 1); 201 | 202 | flipper_format_rewind(fff_file); 203 | 204 | furi_string_free(temp_str); 205 | 206 | meal_pager_close_config_file(fff_file); 207 | meal_pager_close_storage(); 208 | } 209 | 210 | void meal_pager_set_max_values(void* context) { 211 | Meal_Pager* app = context; 212 | switch(app->pager_type) { 213 | case Meal_PagerPagerTypeT119: 214 | case Meal_PagerPagerTypeTD165: 215 | app->max_station = 8191; 216 | app->max_pager = 999; 217 | break; 218 | case Meal_PagerPagerTypeTD174: 219 | app->max_station = 8191; 220 | app->max_pager = 10; 221 | break; 222 | case Meal_PagerPagerTypeTD157: 223 | app->max_station = 1023; 224 | app->max_pager = 999; 225 | break; 226 | } 227 | if(app->first_station > app->max_station) { 228 | app->first_station = app->max_station; 229 | snprintf(app->text_store[0], sizeof(app->text_store[0]), "%lu", app->first_station); 230 | } 231 | if(app->last_station > app->max_station) { 232 | app->last_station = app->max_station; 233 | snprintf(app->text_store[1], sizeof(app->text_store[1]), "%lu", app->last_station); 234 | } 235 | if(app->last_station < app->first_station) { 236 | app->last_station = app->first_station; 237 | snprintf(app->text_store[1], sizeof(app->text_store[1]), "%lu", app->last_station); 238 | } 239 | if(app->first_pager > app->max_pager) { 240 | app->first_pager = app->max_pager; 241 | snprintf(app->text_store[2], sizeof(app->text_store[2]), "%lu", app->first_pager); 242 | } 243 | if(app->last_pager > app->max_pager) { 244 | app->last_pager = app->max_pager; 245 | snprintf(app->text_store[3], sizeof(app->text_store[3]), "%lu", app->last_pager); 246 | } 247 | if(app->last_pager < app->first_pager) { 248 | app->last_pager = app->first_pager; 249 | snprintf(app->text_store[3], sizeof(app->text_store[3]), "%lu", app->last_pager); 250 | } 251 | } -------------------------------------------------------------------------------- /helpers/gui/int_input.c: -------------------------------------------------------------------------------- 1 | #include "int_input.h" 2 | 3 | #include 4 | #include 5 | #include "meal_pager_icons.h" 6 | 7 | /** IntInput type */ 8 | struct IntInput { 9 | View* view; 10 | }; 11 | 12 | typedef struct { 13 | const char text; 14 | const uint8_t x; 15 | const uint8_t y; 16 | } IntInputKey; 17 | 18 | typedef struct { 19 | const char* header; 20 | char* text_buffer; 21 | size_t text_buffer_size; 22 | bool clear_default_text; 23 | 24 | IntInputCallback callback; 25 | void* callback_context; 26 | 27 | int8_t selected_row; 28 | uint8_t selected_column; 29 | } IntInputModel; 30 | 31 | static const uint8_t keyboard_origin_x = 7; 32 | static const uint8_t keyboard_origin_y = 31; 33 | static const uint8_t keyboard_row_count = 2; 34 | static const uint8_t enter_symbol = '\r'; 35 | static const uint8_t backspace_symbol = '\b'; 36 | 37 | static const IntInputKey keyboard_keys_row_1[] = { 38 | {'0', 0, 12}, 39 | {'1', 11, 12}, 40 | {'2', 22, 12}, 41 | {'3', 33, 12}, 42 | {'4', 44, 12}, 43 | {backspace_symbol, 103, 4}, 44 | }; 45 | 46 | static const IntInputKey keyboard_keys_row_2[] = { 47 | {'5', 0, 26}, 48 | {'6', 11, 26}, 49 | {'7', 22, 26}, 50 | {'8', 33, 26}, 51 | {'9', 44, 26}, 52 | {enter_symbol, 95, 17}, 53 | }; 54 | 55 | /** Get row size 56 | * 57 | * @param row_index Index of row 58 | * 59 | * @return uint8_t Row size 60 | */ 61 | static uint8_t int_input_get_row_size(uint8_t row_index) { 62 | uint8_t row_size = 0; 63 | 64 | switch(row_index + 1) { 65 | case 1: 66 | row_size = COUNT_OF(keyboard_keys_row_1); 67 | break; 68 | case 2: 69 | row_size = COUNT_OF(keyboard_keys_row_2); 70 | break; 71 | default: 72 | furi_crash(); 73 | } 74 | 75 | return row_size; 76 | } 77 | 78 | /** Get row pointer 79 | * 80 | * @param row_index Index of row 81 | * 82 | * @return const IntInputKey* Row pointer 83 | */ 84 | static const IntInputKey* int_input_get_row(uint8_t row_index) { 85 | const IntInputKey* row = NULL; 86 | 87 | switch(row_index + 1) { 88 | case 1: 89 | row = keyboard_keys_row_1; 90 | break; 91 | case 2: 92 | row = keyboard_keys_row_2; 93 | break; 94 | default: 95 | furi_crash(); 96 | } 97 | 98 | return row; 99 | } 100 | 101 | /** Draw input box (common view) 102 | * 103 | * @param canvas The canvas 104 | * @param model The model 105 | */ 106 | static void int_input_draw_input(Canvas* canvas, IntInputModel* model) { 107 | const uint8_t text_x = 8; 108 | const uint8_t text_y = 25; 109 | 110 | elements_slightly_rounded_frame(canvas, 6, 14, 116, 15); 111 | 112 | const char* text = model->text_buffer; 113 | canvas_draw_str(canvas, text_x, text_y, text); 114 | } 115 | 116 | static void int_input_backspace_cb(IntInputModel* model) { 117 | uint8_t text_length = model->clear_default_text ? 1 : strlen(model->text_buffer); 118 | if(text_length > 0) { 119 | model->text_buffer[text_length - 1] = 0; 120 | } 121 | } 122 | 123 | /** Handle up button 124 | * 125 | * @param model The model 126 | */ 127 | static void int_input_handle_up(IntInputModel* model) { 128 | if(model->selected_row > 0) { 129 | model->selected_row--; 130 | } 131 | } 132 | 133 | /** Handle down button 134 | * 135 | * @param model The model 136 | */ 137 | static void int_input_handle_down(IntInputModel* model) { 138 | if(model->selected_row < keyboard_row_count - 1) { 139 | model->selected_row += 1; 140 | } 141 | } 142 | 143 | /** Handle left button 144 | * 145 | * @param model The model 146 | */ 147 | static void int_input_handle_left(IntInputModel* model) { 148 | if(model->selected_column > 0) { 149 | model->selected_column--; 150 | } else { 151 | model->selected_column = int_input_get_row_size(model->selected_row) - 1; 152 | } 153 | } 154 | 155 | /** Handle right button 156 | * 157 | * @param model The model 158 | */ 159 | static void int_input_handle_right(IntInputModel* model) { 160 | if(model->selected_column < int_input_get_row_size(model->selected_row) - 1) { 161 | model->selected_column++; 162 | } else { 163 | model->selected_column = 0; 164 | } 165 | } 166 | 167 | /** Handle OK button 168 | * 169 | * @param model The model 170 | */ 171 | static void int_input_handle_ok(IntInputModel* model) { 172 | char selected = int_input_get_row(model->selected_row)[model->selected_column].text; 173 | size_t text_length = strlen(model->text_buffer); 174 | if(selected == enter_symbol) { 175 | model->callback(model->callback_context); 176 | } else if(selected == backspace_symbol) { 177 | int_input_backspace_cb(model); 178 | } else { 179 | if(model->clear_default_text) { 180 | text_length = 0; 181 | } 182 | if(text_length < (model->text_buffer_size - 1)) { 183 | model->text_buffer[text_length] = selected; 184 | model->text_buffer[text_length + 1] = 0; 185 | } 186 | } 187 | model->clear_default_text = false; 188 | } 189 | 190 | /** Draw callback 191 | * 192 | * @param canvas The canvas 193 | * @param _model The model 194 | */ 195 | static void int_input_view_draw_callback(Canvas* canvas, void* _model) { 196 | IntInputModel* model = _model; 197 | uint8_t text_length = model->text_buffer ? strlen(model->text_buffer) : 0; 198 | UNUSED(text_length); 199 | 200 | canvas_clear(canvas); 201 | canvas_set_color(canvas, ColorBlack); 202 | 203 | int_input_draw_input(canvas, model); 204 | 205 | canvas_set_font(canvas, FontSecondary); 206 | canvas_draw_str(canvas, 2, 9, model->header); 207 | canvas_set_font(canvas, FontKeyboard); 208 | // Draw keyboard 209 | for(uint8_t row = 0; row < keyboard_row_count; row++) { 210 | const uint8_t column_count = int_input_get_row_size(row); 211 | const IntInputKey* keys = int_input_get_row(row); 212 | 213 | for(size_t column = 0; column < column_count; column++) { 214 | if(keys[column].text == enter_symbol) { 215 | canvas_set_color(canvas, ColorBlack); 216 | if(model->selected_row == row && model->selected_column == column) { 217 | canvas_draw_icon( 218 | canvas, 219 | keyboard_origin_x + keys[column].x, 220 | keyboard_origin_y + keys[column].y, 221 | &I_KeySaveSelected_24x11); 222 | } else { 223 | canvas_draw_icon( 224 | canvas, 225 | keyboard_origin_x + keys[column].x, 226 | keyboard_origin_y + keys[column].y, 227 | &I_KeySave_24x11); 228 | } 229 | } else if(keys[column].text == backspace_symbol) { 230 | canvas_set_color(canvas, ColorBlack); 231 | if(model->selected_row == row && model->selected_column == column) { 232 | canvas_draw_icon( 233 | canvas, 234 | keyboard_origin_x + keys[column].x, 235 | keyboard_origin_y + keys[column].y, 236 | &I_KeyBackspaceSelected_16x9); 237 | } else { 238 | canvas_draw_icon( 239 | canvas, 240 | keyboard_origin_x + keys[column].x, 241 | keyboard_origin_y + keys[column].y, 242 | &I_KeyBackspace_16x9); 243 | } 244 | } else { 245 | if(model->selected_row == row && model->selected_column == column) { 246 | canvas_set_color(canvas, ColorBlack); 247 | canvas_draw_box( 248 | canvas, 249 | keyboard_origin_x + keys[column].x - 3, 250 | keyboard_origin_y + keys[column].y - 10, 251 | 11, 252 | 13); 253 | canvas_set_color(canvas, ColorWhite); 254 | } else if(model->selected_row == -1 && row == 0 && model->selected_column == column) { 255 | canvas_set_color(canvas, ColorBlack); 256 | canvas_draw_frame( 257 | canvas, 258 | keyboard_origin_x + keys[column].x - 3, 259 | keyboard_origin_y + keys[column].y - 10, 260 | 11, 261 | 13); 262 | } else { 263 | canvas_set_color(canvas, ColorBlack); 264 | } 265 | 266 | canvas_draw_glyph( 267 | canvas, 268 | keyboard_origin_x + keys[column].x, 269 | keyboard_origin_y + keys[column].y, 270 | keys[column].text); 271 | } 272 | } 273 | } 274 | } 275 | 276 | /** Input callback 277 | * 278 | * @param event The event 279 | * @param context The context 280 | * 281 | * @return true 282 | * @return false 283 | */ 284 | static bool int_input_view_input_callback(InputEvent* event, void* context) { 285 | IntInput* int_input = context; 286 | furi_assert(int_input); 287 | 288 | bool consumed = false; 289 | 290 | // Fetch the model 291 | IntInputModel* model = view_get_model(int_input->view); 292 | 293 | if(event->type == InputTypeShort || event->type == InputTypeLong || 294 | event->type == InputTypeRepeat) { 295 | consumed = true; 296 | switch(event->key) { 297 | case InputKeyLeft: 298 | int_input_handle_left(model); 299 | break; 300 | case InputKeyRight: 301 | int_input_handle_right(model); 302 | break; 303 | case InputKeyUp: 304 | int_input_handle_up(model); 305 | break; 306 | case InputKeyDown: 307 | int_input_handle_down(model); 308 | break; 309 | case InputKeyOk: 310 | int_input_handle_ok(model); 311 | break; 312 | default: 313 | consumed = false; 314 | break; 315 | } 316 | } 317 | 318 | // commit view 319 | view_commit_model(int_input->view, consumed); 320 | 321 | return consumed; 322 | } 323 | 324 | void int_input_reset(IntInput* int_input) { 325 | FURI_LOG_D("INT_INPUT", "Resetting Model"); 326 | furi_assert(int_input); 327 | with_view_model( 328 | int_input->view, 329 | IntInputModel * model, 330 | { 331 | model->header = ""; 332 | model->selected_row = 0; 333 | model->selected_column = 0; 334 | model->clear_default_text = false; 335 | model->text_buffer = ""; 336 | model->text_buffer_size = 0; 337 | model->callback = NULL; 338 | model->callback_context = NULL; 339 | }, 340 | true); 341 | } 342 | 343 | IntInput* int_input_alloc() { 344 | IntInput* int_input = malloc(sizeof(IntInput)); 345 | int_input->view = view_alloc(); 346 | view_set_context(int_input->view, int_input); 347 | view_allocate_model(int_input->view, ViewModelTypeLocking, sizeof(IntInputModel)); 348 | view_set_draw_callback(int_input->view, int_input_view_draw_callback); 349 | view_set_input_callback(int_input->view, int_input_view_input_callback); 350 | 351 | int_input_reset(int_input); 352 | 353 | return int_input; 354 | } 355 | 356 | void int_input_free(IntInput* int_input) { 357 | furi_assert(int_input); 358 | view_free(int_input->view); 359 | free(int_input); 360 | } 361 | 362 | View* int_input_get_view(IntInput* int_input) { 363 | furi_assert(int_input); 364 | return int_input->view; 365 | } 366 | 367 | void int_input_set_result_callback( 368 | IntInput* int_input, 369 | IntInputCallback callback, 370 | void* callback_context, 371 | char* text_buffer, 372 | size_t text_buffer_size, 373 | bool clear_default_text) { 374 | with_view_model( 375 | int_input->view, 376 | IntInputModel * model, 377 | { 378 | model->callback = callback; 379 | model->callback_context = callback_context; 380 | model->text_buffer = text_buffer; 381 | model->text_buffer_size = text_buffer_size; 382 | model->clear_default_text = clear_default_text; 383 | }, 384 | true); 385 | } 386 | 387 | void int_input_set_header_text(IntInput* int_input, const char* text) { 388 | with_view_model( 389 | int_input->view, IntInputModel * model, { model->header = text; }, true); 390 | } 391 | -------------------------------------------------------------------------------- /helpers/subghz/subghz_txrx.c: -------------------------------------------------------------------------------- 1 | #include "subghz_txrx_i.h" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #define TAG "SubGhz" 8 | 9 | static void subghz_txrx_radio_device_power_on(SubGhzTxRx* instance) { 10 | UNUSED(instance); 11 | uint8_t attempts = 5; 12 | while(--attempts > 0) { 13 | if(furi_hal_power_enable_otg()) break; 14 | } 15 | if(attempts == 0) { 16 | if(furi_hal_power_get_usb_voltage() < 4.5f) { 17 | FURI_LOG_E( 18 | TAG, 19 | "Error power otg enable. BQ2589 check otg fault = %d", 20 | furi_hal_power_check_otg_fault() ? 1 : 0); 21 | } 22 | } 23 | } 24 | 25 | static void subghz_txrx_radio_device_power_off(SubGhzTxRx* instance) { 26 | UNUSED(instance); 27 | if(furi_hal_power_is_otg_enabled()) furi_hal_power_disable_otg(); 28 | } 29 | 30 | SubGhzTxRx* subghz_txrx_alloc() { 31 | SubGhzTxRx* instance = malloc(sizeof(SubGhzTxRx)); 32 | instance->setting = subghz_setting_alloc(); 33 | subghz_setting_load(instance->setting, EXT_PATH("subghz/assets/setting_user")); 34 | 35 | instance->preset = malloc(sizeof(SubGhzRadioPreset)); 36 | instance->preset->name = furi_string_alloc(); 37 | subghz_txrx_set_preset( 38 | instance, "AM650", subghz_setting_get_default_frequency(instance->setting), NULL, 0); 39 | 40 | instance->txrx_state = SubGhzTxRxStateSleep; 41 | 42 | subghz_txrx_hopper_set_state(instance, SubGhzHopperStateOFF); 43 | subghz_txrx_speaker_set_state(instance, SubGhzSpeakerStateDisable); 44 | 45 | instance->worker = subghz_worker_alloc(); 46 | instance->fff_data = flipper_format_string_alloc(); 47 | 48 | instance->environment = subghz_environment_alloc(); 49 | instance->is_database_loaded = 50 | subghz_environment_load_keystore(instance->environment, SUBGHZ_KEYSTORE_DIR_NAME); 51 | subghz_environment_load_keystore(instance->environment, SUBGHZ_KEYSTORE_DIR_USER_NAME); 52 | subghz_environment_set_came_atomo_rainbow_table_file_name( 53 | instance->environment, SUBGHZ_CAME_ATOMO_DIR_NAME); 54 | subghz_environment_set_alutech_at_4n_rainbow_table_file_name( 55 | instance->environment, SUBGHZ_ALUTECH_AT_4N_DIR_NAME); 56 | subghz_environment_set_nice_flor_s_rainbow_table_file_name( 57 | instance->environment, SUBGHZ_NICE_FLOR_S_DIR_NAME); 58 | subghz_environment_set_protocol_registry( 59 | instance->environment, (void*)&subghz_protocol_registry); 60 | instance->receiver = subghz_receiver_alloc_init(instance->environment); 61 | 62 | subghz_worker_set_overrun_callback( 63 | instance->worker, (SubGhzWorkerOverrunCallback)subghz_receiver_reset); 64 | subghz_worker_set_pair_callback( 65 | instance->worker, (SubGhzWorkerPairCallback)subghz_receiver_decode); 66 | subghz_worker_set_context(instance->worker, instance->receiver); 67 | 68 | //set default device External 69 | subghz_devices_init(); 70 | instance->radio_device_type = SubGhzRadioDeviceTypeInternal; 71 | instance->radio_device_type = 72 | subghz_txrx_radio_device_set(instance, SubGhzRadioDeviceTypeExternalCC1101); 73 | 74 | FURI_LOG_D(TAG, "completed TXRX alloc"); 75 | 76 | return instance; 77 | } 78 | 79 | void subghz_txrx_free(SubGhzTxRx* instance) { 80 | furi_assert(instance); 81 | FURI_LOG_D(TAG, "freeing TXRX"); 82 | 83 | if(instance->radio_device_type != SubGhzRadioDeviceTypeInternal) { 84 | subghz_txrx_radio_device_power_off(instance); 85 | subghz_devices_end(instance->radio_device); 86 | } 87 | 88 | subghz_devices_deinit(); 89 | 90 | subghz_worker_free(instance->worker); 91 | subghz_receiver_free(instance->receiver); 92 | subghz_environment_free(instance->environment); 93 | flipper_format_free(instance->fff_data); 94 | furi_string_free(instance->preset->name); 95 | subghz_setting_free(instance->setting); 96 | 97 | free(instance->preset); 98 | free(instance); 99 | } 100 | 101 | /*bool subghz_txrx_is_database_loaded(SubGhzTxRx* instance) { 102 | furi_assert(instance); 103 | return instance->is_database_loaded; 104 | }*/ 105 | 106 | void subghz_txrx_set_preset( 107 | SubGhzTxRx* instance, 108 | const char* preset_name, 109 | uint32_t frequency, 110 | uint8_t* preset_data, 111 | size_t preset_data_size) { 112 | furi_assert(instance); 113 | furi_string_set(instance->preset->name, preset_name); 114 | SubGhzRadioPreset* preset = instance->preset; 115 | preset->frequency = frequency; 116 | preset->data = preset_data; 117 | preset->data_size = preset_data_size; 118 | } 119 | 120 | const char* subghz_txrx_get_preset_name(SubGhzTxRx* instance, const char* preset) { 121 | UNUSED(instance); 122 | const char* preset_name = ""; 123 | if(!strcmp(preset, "FuriHalSubGhzPresetOok270Async")) { 124 | preset_name = "AM270"; 125 | } else if(!strcmp(preset, "FuriHalSubGhzPresetOok650Async")) { 126 | preset_name = "AM650"; 127 | } else if(!strcmp(preset, "FuriHalSubGhzPreset2FSKDev238Async")) { 128 | preset_name = "FM238"; 129 | } else if(!strcmp(preset, "FuriHalSubGhzPreset2FSKDev476Async")) { 130 | preset_name = "FM476"; 131 | } else if(!strcmp(preset, "FuriHalSubGhzPresetCustom")) { 132 | preset_name = "CUSTOM"; 133 | } else { 134 | FURI_LOG_E(TAG, "Unknown preset"); 135 | } 136 | return preset_name; 137 | } 138 | 139 | /*SubGhzRadioPreset subghz_txrx_get_preset(SubGhzTxRx* instance) { 140 | furi_assert(instance); 141 | return *instance->preset; 142 | }*/ 143 | 144 | /*void subghz_txrx_get_frequency_and_modulation( 145 | SubGhzTxRx* instance, 146 | FuriString* frequency, 147 | FuriString* modulation) { 148 | furi_assert(instance); 149 | SubGhzRadioPreset* preset = instance->preset; 150 | if(frequency != NULL) { 151 | furi_string_printf( 152 | frequency, 153 | "%03ld.%02ld", 154 | preset->frequency / 1000000 % 1000, 155 | preset->frequency / 10000 % 100); 156 | } 157 | if(modulation != NULL) { 158 | furi_string_printf(modulation, "%.2s", furi_string_get_cstr(preset->name)); 159 | } 160 | }*/ 161 | 162 | static void subghz_txrx_begin(SubGhzTxRx* instance, uint8_t* preset_data) { 163 | furi_assert(instance); 164 | subghz_devices_reset(instance->radio_device); 165 | subghz_devices_idle(instance->radio_device); 166 | subghz_devices_load_preset(instance->radio_device, FuriHalSubGhzPresetCustom, preset_data); 167 | instance->txrx_state = SubGhzTxRxStateIDLE; 168 | FURI_LOG_D(TAG, "completed subghz_txrx_begin"); 169 | } 170 | 171 | /*static uint32_t subghz_txrx_rx(SubGhzTxRx* instance, uint32_t frequency) { 172 | furi_assert(instance); 173 | 174 | furi_assert( 175 | instance->txrx_state != SubGhzTxRxStateRx && instance->txrx_state != SubGhzTxRxStateSleep); 176 | 177 | subghz_devices_idle(instance->radio_device); 178 | 179 | uint32_t value = subghz_devices_set_frequency(instance->radio_device, frequency); 180 | subghz_devices_flush_rx(instance->radio_device); 181 | subghz_txrx_speaker_on(instance); 182 | 183 | subghz_devices_start_async_rx( 184 | instance->radio_device, subghz_worker_rx_callback, instance->worker); 185 | subghz_worker_start(instance->worker); 186 | instance->txrx_state = SubGhzTxRxStateRx; 187 | return value; 188 | }*/ 189 | 190 | static void subghz_txrx_idle(SubGhzTxRx* instance) { 191 | furi_assert(instance); 192 | if(instance->txrx_state != SubGhzTxRxStateSleep) { 193 | subghz_devices_idle(instance->radio_device); 194 | subghz_txrx_speaker_off(instance); 195 | instance->txrx_state = SubGhzTxRxStateIDLE; 196 | } 197 | FURI_LOG_D(TAG, "completed subghz_txrx_idle"); 198 | } 199 | 200 | /*static void subghz_txrx_rx_end(SubGhzTxRx* instance) { 201 | furi_assert(instance); 202 | furi_assert(instance->txrx_state == SubGhzTxRxStateRx); 203 | 204 | if(subghz_worker_is_running(instance->worker)) { 205 | subghz_worker_stop(instance->worker); 206 | subghz_devices_stop_async_rx(instance->radio_device); 207 | } 208 | subghz_devices_idle(instance->radio_device); 209 | subghz_txrx_speaker_off(instance); 210 | instance->txrx_state = SubGhzTxRxStateIDLE; 211 | }*/ 212 | 213 | /*void subghz_txrx_sleep(SubGhzTxRx* instance) { 214 | furi_assert(instance); 215 | subghz_devices_sleep(instance->radio_device); 216 | instance->txrx_state = SubGhzTxRxStateSleep; 217 | }*/ 218 | 219 | static bool subghz_txrx_tx(SubGhzTxRx* instance, uint32_t frequency) { 220 | furi_assert(instance); 221 | furi_assert(instance->txrx_state != SubGhzTxRxStateSleep); 222 | subghz_devices_idle(instance->radio_device); 223 | subghz_devices_set_frequency(instance->radio_device, frequency); 224 | 225 | bool ret = subghz_devices_set_tx(instance->radio_device); 226 | if(ret) { 227 | subghz_txrx_speaker_on(instance); 228 | instance->txrx_state = SubGhzTxRxStateTx; 229 | } 230 | 231 | FURI_LOG_D(TAG, "completed subghz_txrx_tx"); 232 | return ret; 233 | } 234 | 235 | SubGhzTxRxStartTxState subghz_txrx_tx_start(SubGhzTxRx* instance, FlipperFormat* flipper_format) { 236 | furi_assert(instance); 237 | furi_assert(flipper_format); 238 | 239 | subghz_txrx_stop(instance); 240 | 241 | SubGhzTxRxStartTxState ret = SubGhzTxRxStartTxStateErrorParserOthers; 242 | FuriString* temp_str = furi_string_alloc(); 243 | uint32_t repeat = 200; 244 | 245 | FURI_LOG_D(TAG, "starting loop in subghz_txrx_tx_start"); 246 | do { 247 | FURI_LOG_D(TAG, "looping"); 248 | if(!flipper_format_rewind(flipper_format)) { 249 | FURI_LOG_E(TAG, "Rewind error"); 250 | break; 251 | } 252 | if(!flipper_format_read_string(flipper_format, "Protocol", temp_str)) { 253 | FURI_LOG_E(TAG, "Missing Protocol"); 254 | break; 255 | } 256 | if(!flipper_format_insert_or_update_uint32(flipper_format, "Repeat", &repeat, 1)) { 257 | FURI_LOG_E(TAG, "Unable Repeat"); 258 | break; 259 | } 260 | //FURI_LOG_D(TAG, "File loaded"); 261 | ret = SubGhzTxRxStartTxStateOk; 262 | 263 | SubGhzRadioPreset* preset = instance->preset; 264 | instance->transmitter = 265 | subghz_transmitter_alloc_init(instance->environment, furi_string_get_cstr(temp_str)); 266 | 267 | if(instance->transmitter) { 268 | FURI_LOG_D(TAG, "transmitter found"); 269 | if(subghz_transmitter_deserialize(instance->transmitter, flipper_format) == 270 | SubGhzProtocolStatusOk) { 271 | //if (false) { 272 | FURI_LOG_D(TAG, "deserialization"); 273 | if(strcmp(furi_string_get_cstr(preset->name), "") != 0) { 274 | FURI_LOG_D(TAG, "got preset name"); 275 | subghz_txrx_begin( 276 | instance, 277 | subghz_setting_get_preset_data_by_name( 278 | instance->setting, furi_string_get_cstr(preset->name))); 279 | FURI_LOG_D(TAG, "loaded preset by name"); 280 | if(preset->frequency) { 281 | if(!subghz_txrx_tx(instance, preset->frequency)) { 282 | FURI_LOG_E(TAG, "Only Rx"); 283 | ret = SubGhzTxRxStartTxStateErrorOnlyRx; 284 | } 285 | FURI_LOG_D(TAG, "got frequency"); 286 | } else { 287 | ret = SubGhzTxRxStartTxStateErrorParserOthers; 288 | } 289 | 290 | } else { 291 | FURI_LOG_E( 292 | TAG, "Unknown name preset \" %s \"", furi_string_get_cstr(preset->name)); 293 | ret = SubGhzTxRxStartTxStateErrorParserOthers; 294 | } 295 | 296 | if(ret == SubGhzTxRxStartTxStateOk) { 297 | //Start TX 298 | FURI_LOG_D(TAG, "starting Async TX"); 299 | subghz_devices_start_async_tx( 300 | instance->radio_device, subghz_transmitter_yield, instance->transmitter); 301 | } 302 | } else { 303 | FURI_LOG_D(TAG, "no deserialization"); 304 | ret = SubGhzTxRxStartTxStateErrorParserOthers; 305 | } 306 | } else { 307 | ret = SubGhzTxRxStartTxStateErrorParserOthers; 308 | } 309 | if(ret != SubGhzTxRxStartTxStateOk) { 310 | FURI_LOG_D(TAG, "state not ok"); 311 | subghz_transmitter_free(instance->transmitter); // Crashes here 312 | if(instance->txrx_state != SubGhzTxRxStateIDLE) { 313 | subghz_txrx_idle(instance); 314 | } 315 | } 316 | } while(false); 317 | furi_string_free(temp_str); 318 | return ret; 319 | } 320 | 321 | /*void subghz_txrx_rx_start(SubGhzTxRx* instance) { 322 | furi_assert(instance); 323 | subghz_txrx_stop(instance); 324 | subghz_txrx_begin( 325 | instance, 326 | subghz_setting_get_preset_data_by_name( 327 | subghz_txrx_get_setting(instance), furi_string_get_cstr(instance->preset->name))); 328 | subghz_txrx_rx(instance, instance->preset->frequency); 329 | }*/ 330 | 331 | /*void subghz_txrx_set_need_save_callback( 332 | SubGhzTxRx* instance, 333 | SubGhzTxRxNeedSaveCallback callback, 334 | void* context) { 335 | furi_assert(instance); 336 | instance->need_save_callback = callback; 337 | instance->need_save_context = context; 338 | }*/ 339 | 340 | static void subghz_txrx_tx_stop(SubGhzTxRx* instance) { 341 | furi_assert(instance); 342 | furi_assert(instance->txrx_state == SubGhzTxRxStateTx); 343 | //Stop TX 344 | subghz_devices_stop_async_tx(instance->radio_device); 345 | subghz_transmitter_stop(instance->transmitter); 346 | subghz_transmitter_free(instance->transmitter); 347 | 348 | //if protocol dynamic then we save the last upload 349 | /*if(instance->decoder_result->protocol->type == SubGhzProtocolTypeDynamic) { 350 | if(instance->need_save_callback) { 351 | instance->need_save_callback(instance->need_save_context); 352 | } 353 | }*/ 354 | subghz_txrx_idle(instance); 355 | subghz_txrx_speaker_off(instance); 356 | } 357 | 358 | FlipperFormat* subghz_txrx_get_fff_data(SubGhzTxRx* instance) { 359 | furi_assert(instance); 360 | return instance->fff_data; 361 | } 362 | 363 | SubGhzSetting* subghz_txrx_get_setting(SubGhzTxRx* instance) { 364 | furi_assert(instance); 365 | return instance->setting; 366 | } 367 | 368 | void subghz_txrx_stop(SubGhzTxRx* instance) { 369 | furi_assert(instance); 370 | 371 | switch(instance->txrx_state) { 372 | case SubGhzTxRxStateTx: 373 | subghz_txrx_tx_stop(instance); 374 | subghz_txrx_speaker_unmute(instance); 375 | break; 376 | case SubGhzTxRxStateRx: 377 | //subghz_txrx_rx_end(instance); 378 | //subghz_txrx_speaker_mute(instance); 379 | break; 380 | 381 | default: 382 | break; 383 | } 384 | } 385 | 386 | /*void subghz_txrx_hopper_update(SubGhzTxRx* instance) { 387 | furi_assert(instance); 388 | 389 | switch(instance->hopper_state) { 390 | case SubGhzHopperStateOFF: 391 | case SubGhzHopperStatePause: 392 | return; 393 | case SubGhzHopperStateRSSITimeOut: 394 | if(instance->hopper_timeout != 0) { 395 | instance->hopper_timeout--; 396 | return; 397 | } 398 | break; 399 | default: 400 | break; 401 | } 402 | float rssi = -127.0f; 403 | if(instance->hopper_state != SubGhzHopperStateRSSITimeOut) { 404 | // See RSSI Calculation timings in CC1101 17.3 RSSI 405 | rssi = subghz_devices_get_rssi(instance->radio_device); 406 | 407 | // Stay if RSSI is high enough 408 | if(rssi > -90.0f) { 409 | instance->hopper_timeout = 10; 410 | instance->hopper_state = SubGhzHopperStateRSSITimeOut; 411 | return; 412 | } 413 | } else { 414 | instance->hopper_state = SubGhzHopperStateRunnig; 415 | } 416 | // Select next frequency 417 | if(instance->hopper_idx_frequency < 418 | subghz_setting_get_hopper_frequency_count(instance->setting) - 1) { 419 | instance->hopper_idx_frequency++; 420 | } else { 421 | instance->hopper_idx_frequency = 0; 422 | } 423 | 424 | if(instance->txrx_state == SubGhzTxRxStateRx) { 425 | subghz_txrx_rx_end(instance); 426 | }; 427 | if(instance->txrx_state == SubGhzTxRxStateIDLE) { 428 | subghz_receiver_reset(instance->receiver); 429 | instance->preset->frequency = 430 | subghz_setting_get_hopper_frequency(instance->setting, instance->hopper_idx_frequency); 431 | subghz_txrx_rx(instance, instance->preset->frequency); 432 | } 433 | }*/ 434 | 435 | /*SubGhzHopperState subghz_txrx_hopper_get_state(SubGhzTxRx* instance) { 436 | furi_assert(instance); 437 | return instance->hopper_state; 438 | }*/ 439 | 440 | void subghz_txrx_hopper_set_state(SubGhzTxRx* instance, SubGhzHopperState state) { 441 | furi_assert(instance); 442 | instance->hopper_state = state; 443 | } 444 | 445 | /*void subghz_txrx_hopper_unpause(SubGhzTxRx* instance) { 446 | furi_assert(instance); 447 | if(instance->hopper_state == SubGhzHopperStatePause) { 448 | instance->hopper_state = SubGhzHopperStateRunnig; 449 | } 450 | }*/ 451 | 452 | /*void subghz_txrx_hopper_pause(SubGhzTxRx* instance) { 453 | furi_assert(instance); 454 | if(instance->hopper_state == SubGhzHopperStateRunnig) { 455 | instance->hopper_state = SubGhzHopperStatePause; 456 | } 457 | }*/ 458 | 459 | void subghz_txrx_speaker_on(SubGhzTxRx* instance) { 460 | furi_assert(instance); 461 | if(instance->speaker_state == SubGhzSpeakerStateEnable) { 462 | if(furi_hal_speaker_acquire(30)) { 463 | subghz_devices_set_async_mirror_pin(instance->radio_device, &gpio_speaker); 464 | } else { 465 | instance->speaker_state = SubGhzSpeakerStateDisable; 466 | } 467 | } 468 | } 469 | 470 | void subghz_txrx_speaker_off(SubGhzTxRx* instance) { 471 | furi_assert(instance); 472 | if(instance->speaker_state != SubGhzSpeakerStateDisable) { 473 | if(furi_hal_speaker_is_mine()) { 474 | subghz_devices_set_async_mirror_pin(instance->radio_device, NULL); 475 | furi_hal_speaker_release(); 476 | if(instance->speaker_state == SubGhzSpeakerStateShutdown) 477 | instance->speaker_state = SubGhzSpeakerStateDisable; 478 | } 479 | } 480 | } 481 | 482 | /*void subghz_txrx_speaker_mute(SubGhzTxRx* instance) { 483 | furi_assert(instance); 484 | if(instance->speaker_state == SubGhzSpeakerStateEnable) { 485 | if(furi_hal_speaker_is_mine()) { 486 | subghz_devices_set_async_mirror_pin(instance->radio_device, NULL); 487 | } 488 | } 489 | }*/ 490 | 491 | void subghz_txrx_speaker_unmute(SubGhzTxRx* instance) { 492 | furi_assert(instance); 493 | if(instance->speaker_state == SubGhzSpeakerStateEnable) { 494 | if(furi_hal_speaker_is_mine()) { 495 | subghz_devices_set_async_mirror_pin(instance->radio_device, &gpio_speaker); 496 | } 497 | } 498 | } 499 | 500 | void subghz_txrx_speaker_set_state(SubGhzTxRx* instance, SubGhzSpeakerState state) { 501 | furi_assert(instance); 502 | instance->speaker_state = state; 503 | } 504 | 505 | /*SubGhzSpeakerState subghz_txrx_speaker_get_state(SubGhzTxRx* instance) { 506 | furi_assert(instance); 507 | return instance->speaker_state; 508 | }*/ 509 | 510 | bool subghz_txrx_load_decoder_by_name_protocol(SubGhzTxRx* instance, const char* name_protocol) { 511 | furi_assert(instance); 512 | furi_assert(name_protocol); 513 | bool res = false; 514 | instance->decoder_result = 515 | subghz_receiver_search_decoder_base_by_name(instance->receiver, name_protocol); 516 | if(instance->decoder_result) { 517 | res = true; 518 | } 519 | return res; 520 | } 521 | 522 | SubGhzProtocolDecoderBase* subghz_txrx_get_decoder(SubGhzTxRx* instance) { 523 | furi_assert(instance); 524 | return instance->decoder_result; 525 | } 526 | 527 | /*bool subghz_txrx_protocol_is_serializable(SubGhzTxRx* instance) { 528 | furi_assert(instance); 529 | return ( 530 | (instance->decoder_result->protocol->flag & SubGhzProtocolFlag_Save) == 531 | SubGhzProtocolFlag_Save); 532 | }*/ 533 | 534 | /*bool subghz_txrx_protocol_is_transmittable(SubGhzTxRx* instance, bool check_type) { 535 | furi_assert(instance); 536 | const SubGhzProtocol* protocol = instance->decoder_result->protocol; 537 | if(check_type) { 538 | return ( 539 | ((protocol->flag & SubGhzProtocolFlag_Send) == SubGhzProtocolFlag_Send) && 540 | protocol->encoder->deserialize && protocol->type == SubGhzProtocolTypeStatic); 541 | } 542 | return ( 543 | ((protocol->flag & SubGhzProtocolFlag_Send) == SubGhzProtocolFlag_Send) && 544 | protocol->encoder->deserialize); 545 | }*/ 546 | 547 | /*void subghz_txrx_receiver_set_filter(SubGhzTxRx* instance, SubGhzProtocolFlag filter) { 548 | furi_assert(instance); 549 | subghz_receiver_set_filter(instance->receiver, filter); 550 | }*/ 551 | 552 | /*void subghz_txrx_set_rx_calback( 553 | SubGhzTxRx* instance, 554 | SubGhzReceiverCallback callback, 555 | void* context) { 556 | subghz_receiver_set_rx_callback(instance->receiver, callback, context); 557 | }*/ 558 | 559 | void subghz_txrx_set_raw_file_encoder_worker_callback_end( 560 | SubGhzTxRx* instance, 561 | SubGhzProtocolEncoderRAWCallbackEnd callback, 562 | void* context) { 563 | subghz_protocol_raw_file_encoder_worker_set_callback_end( 564 | (SubGhzProtocolEncoderRAW*)subghz_transmitter_get_protocol_instance(instance->transmitter), 565 | callback, 566 | context); 567 | } 568 | 569 | bool subghz_txrx_radio_device_is_external_connected(SubGhzTxRx* instance, const char* name) { 570 | furi_assert(instance); 571 | 572 | bool is_connect = false; 573 | bool is_otg_enabled = furi_hal_power_is_otg_enabled(); 574 | 575 | if(!is_otg_enabled) { 576 | subghz_txrx_radio_device_power_on(instance); 577 | } 578 | 579 | const SubGhzDevice* device = subghz_devices_get_by_name(name); 580 | if(device) { 581 | is_connect = subghz_devices_is_connect(device); 582 | } 583 | 584 | if(!is_otg_enabled) { 585 | subghz_txrx_radio_device_power_off(instance); 586 | } 587 | return is_connect; 588 | } 589 | 590 | SubGhzRadioDeviceType 591 | subghz_txrx_radio_device_set(SubGhzTxRx* instance, SubGhzRadioDeviceType radio_device_type) { 592 | furi_assert(instance); 593 | 594 | if(radio_device_type == SubGhzRadioDeviceTypeExternalCC1101 && 595 | subghz_txrx_radio_device_is_external_connected(instance, SUBGHZ_DEVICE_CC1101_EXT_NAME)) { 596 | subghz_txrx_radio_device_power_on(instance); 597 | instance->radio_device = subghz_devices_get_by_name(SUBGHZ_DEVICE_CC1101_EXT_NAME); 598 | subghz_devices_begin(instance->radio_device); 599 | instance->radio_device_type = SubGhzRadioDeviceTypeExternalCC1101; 600 | } else { 601 | subghz_txrx_radio_device_power_off(instance); 602 | if(instance->radio_device_type != SubGhzRadioDeviceTypeInternal) { 603 | subghz_devices_end(instance->radio_device); 604 | } 605 | instance->radio_device = subghz_devices_get_by_name(SUBGHZ_DEVICE_CC1101_INT_NAME); 606 | instance->radio_device_type = SubGhzRadioDeviceTypeInternal; 607 | } 608 | 609 | return instance->radio_device_type; 610 | } 611 | 612 | /*SubGhzRadioDeviceType subghz_txrx_radio_device_get(SubGhzTxRx* instance) { 613 | furi_assert(instance); 614 | return instance->radio_device_type; 615 | }*/ 616 | 617 | /*float subghz_txrx_radio_device_get_rssi(SubGhzTxRx* instance) { 618 | furi_assert(instance); 619 | return subghz_devices_get_rssi(instance->radio_device); 620 | }*/ 621 | 622 | const char* subghz_txrx_radio_device_get_name(SubGhzTxRx* instance) { 623 | furi_assert(instance); 624 | return subghz_devices_get_name(instance->radio_device); 625 | } 626 | 627 | bool subghz_txrx_radio_device_is_frequecy_valid(SubGhzTxRx* instance, uint32_t frequency) { 628 | furi_assert(instance); 629 | return subghz_devices_is_frequency_valid(instance->radio_device, frequency); 630 | } --------------------------------------------------------------------------------