diff --git a/applications/system/findmy/application.fam b/applications/system/findmy/application.fam index 0b6006923c..9a7b2df949 100644 --- a/applications/system/findmy/application.fam +++ b/applications/system/findmy/application.fam @@ -4,11 +4,11 @@ App( apptype=FlipperAppType.EXTERNAL, entry_point="findmy_main", requires=["gui"], - stack_size=1 * 1024, - order=35, + stack_size=2 * 1024, fap_icon="location_icon.png", fap_category="Bluetooth", fap_author="@MatthewKuKanich", + fap_weburl="https://github.com/MatthewKuKanich/FindMyFlipper", fap_version="1.0", fap_description="BLE FindMy Location Beacon", ) diff --git a/applications/system/findmy/findmy.c b/applications/system/findmy/findmy.c index ed7d472f08..10019347fd 100644 --- a/applications/system/findmy/findmy.c +++ b/applications/system/findmy/findmy.c @@ -43,13 +43,12 @@ static FindMy* findmy_app_alloc() { view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); - app->beacon_active = false; - findmy_main_update_active(app->findmy_main, app->beacon_active); - app->broadcast_interval = 5; - findmy_main_update_interval(app->findmy_main, app->broadcast_interval); - app->transmit_power = 6; - app->apple = true; - findmy_main_update_apple(app->findmy_main, app->apple); + findmy_state_load(&app->state); + findmy_state_apply(&app->state); + + findmy_main_update_active(app->findmy_main, furi_hal_bt_extra_beacon_is_active()); + findmy_main_update_interval(app->findmy_main, app->state.broadcast_interval); + findmy_main_update_type(app->findmy_main, findmy_data_get_type(app->state.data)); return app; } @@ -74,47 +73,10 @@ static void findmy_app_free(FindMy* app) { free(app); } -static void findmy_start(FindMy* app) { - furi_hal_bt_extra_beacon_stop(); // Stop any running beacon - - app->config.min_adv_interval_ms = app->broadcast_interval * 1000; // Converting s to ms - app->config.max_adv_interval_ms = (app->broadcast_interval * 1000) + 150; - app->config.adv_channel_map = GapAdvChannelMapAll; - app->config.adv_power_level = GapAdvPowerLevel_0dBm + app->transmit_power; - app->config.address_type = GapAddressTypePublic; - - uint8_t mac[EXTRA_BEACON_MAC_ADDR_SIZE] = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66}; - furi_hal_bt_reverse_mac_addr(mac); - memcpy(&app->config.address, mac, sizeof(app->config.address)); - furi_check(furi_hal_bt_extra_beacon_set_config(&app->config)); - - uint8_t data[EXTRA_BEACON_MAX_DATA_SIZE]; - uint8_t* it = data; - - // For Apple AirTags - *it++ = 0x1E; // Length - *it++ = 0xFF; // Manufacturer Specific Data - *it++ = 0x4C; // Company ID (Apple, Inc.) - *it++ = 0x00; // ... - *it++ = 0x12; // Type (FindMy) - *it++ = 0x19; // Length - *it++ = 0x00; // Status - // Placeholder Empty Public Key without the MAC address - for(size_t i = 0; i < 22; ++i) { - *it++ = 0x00; - } - *it++ = 0x00; // First 2 bits are the version, the rest is the battery level - *it++ = 0x00; // Hint (0x00) - - furi_check(furi_hal_bt_extra_beacon_set_data(data, it - data)); -} - int32_t findmy_main(void* p) { UNUSED(p); FindMy* app = findmy_app_alloc(); - findmy_start(app); - scene_manager_next_scene(app->scene_manager, FindMySceneMain); view_dispatcher_run(app->view_dispatcher); @@ -127,16 +89,16 @@ void findmy_change_broadcast_interval(FindMy* app, uint8_t value) { if(value > 10 || value < 1) { return; } - app->broadcast_interval = value; - findmy_main_update_interval(app->findmy_main, app->broadcast_interval); - if(app->beacon_active) { + app->state.broadcast_interval = value; + findmy_main_update_interval(app->findmy_main, app->state.broadcast_interval); + if(furi_hal_bt_extra_beacon_is_active()) { // Always check if beacon is active before changing config furi_check(furi_hal_bt_extra_beacon_stop()); } - app->config.min_adv_interval_ms = app->broadcast_interval * 1000; - app->config.max_adv_interval_ms = app->config.min_adv_interval_ms + 150; - furi_check(furi_hal_bt_extra_beacon_set_config(&app->config)); - if(app->beacon_active) { + app->state.config.min_adv_interval_ms = app->state.broadcast_interval * 1000; + app->state.config.max_adv_interval_ms = app->state.config.min_adv_interval_ms + 150; + furi_check(furi_hal_bt_extra_beacon_set_config(&app->state.config)); + if(app->state.beacon_active) { furi_check(furi_hal_bt_extra_beacon_start()); } } @@ -145,24 +107,38 @@ void findmy_change_transmit_power(FindMy* app, uint8_t value) { if(value > 6) { return; } - app->transmit_power = value; - if(app->beacon_active) { + app->state.transmit_power = value; + if(furi_hal_bt_extra_beacon_is_active()) { furi_check(furi_hal_bt_extra_beacon_stop()); } - app->config.adv_power_level = GapAdvPowerLevel_0dBm + app->transmit_power; - furi_check(furi_hal_bt_extra_beacon_set_config(&app->config)); - if(app->beacon_active) { + app->state.config.adv_power_level = GapAdvPowerLevel_0dBm + app->state.transmit_power; + furi_check(furi_hal_bt_extra_beacon_set_config(&app->state.config)); + if(app->state.beacon_active) { furi_check(furi_hal_bt_extra_beacon_start()); } } void findmy_toggle_beacon(FindMy* app) { - app->beacon_active = !app->beacon_active; - findmy_main_update_active(app->findmy_main, app->beacon_active); - findmy_main_update_apple(app->findmy_main, app->apple); - if(app->beacon_active) { - furi_hal_bt_extra_beacon_start(); + app->state.beacon_active = !app->state.beacon_active; + if(furi_hal_bt_extra_beacon_is_active()) { + furi_check(furi_hal_bt_extra_beacon_stop()); + } + if(app->state.beacon_active) { + furi_check(furi_hal_bt_extra_beacon_start()); + } + findmy_main_update_active(app->findmy_main, furi_hal_bt_extra_beacon_is_active()); +} + +FindMyType findmy_data_get_type(uint8_t data[EXTRA_BEACON_MAX_DATA_SIZE]) { + if(data[0] == 0x1E && // Length + data[1] == 0xFF && // Manufacturer Specific Data + data[2] == 0x4C && // Company ID (Apple, Inc.) + data[3] == 0x00 && // ... + data[4] == 0x12 && // Type (FindMy) + data[5] == 0x19 // Length + ) { + return FindMyTypeApple; } else { - furi_hal_bt_extra_beacon_stop(); + return FindMyTypeSamsung; } } \ No newline at end of file diff --git a/applications/system/findmy/findmy.h b/applications/system/findmy/findmy.h index 344882ef10..a5135f2d70 100644 --- a/applications/system/findmy/findmy.h +++ b/applications/system/findmy/findmy.h @@ -1,3 +1,5 @@ #pragma once typedef struct FindMy FindMy; + +typedef enum FindMyType FindMyType; \ No newline at end of file diff --git a/applications/system/findmy/findmy_i.h b/applications/system/findmy/findmy_i.h index fd09e5324a..c5991b5f47 100644 --- a/applications/system/findmy/findmy_i.h +++ b/applications/system/findmy/findmy_i.h @@ -1,6 +1,7 @@ #pragma once #include "findmy.h" +#include "findmy_state.h" #include #include #include @@ -24,11 +25,7 @@ struct FindMy { uint8_t mac_buf[EXTRA_BEACON_MAC_ADDR_SIZE]; uint8_t packet_buf[EXTRA_BEACON_MAX_DATA_SIZE]; - GapExtraBeaconConfig config; - bool apple; - bool beacon_active; - uint8_t broadcast_interval; - uint8_t transmit_power; + FindMyState state; }; typedef enum { @@ -37,6 +34,12 @@ typedef enum { FindMyViewVarItemList, } FindMyView; +enum FindMyType { + FindMyTypeApple, + FindMyTypeSamsung, +}; + void findmy_change_broadcast_interval(FindMy* app, uint8_t value); void findmy_change_transmit_power(FindMy* app, uint8_t value); void findmy_toggle_beacon(FindMy* app); +FindMyType findmy_data_get_type(uint8_t data[EXTRA_BEACON_MAX_DATA_SIZE]); diff --git a/applications/system/findmy/findmy_state.c b/applications/system/findmy/findmy_state.c new file mode 100644 index 0000000000..34e9c80b22 --- /dev/null +++ b/applications/system/findmy/findmy_state.c @@ -0,0 +1,61 @@ +#include "findmy_state.h" + +#include +#include +#include + +bool findmy_state_load(FindMyState* out_state) { + FindMyState state; + + // Set default values + state.beacon_active = false; + state.broadcast_interval = 5; + state.transmit_power = 6; + state.config.min_adv_interval_ms = state.broadcast_interval * 1000; // Converting s to ms + state.config.max_adv_interval_ms = (state.broadcast_interval * 1000) + 150; + state.config.adv_channel_map = GapAdvChannelMapAll; + state.config.adv_power_level = GapAdvPowerLevel_0dBm + state.transmit_power; + state.config.address_type = GapAddressTypePublic; + + // Set default mac + uint8_t default_mac[EXTRA_BEACON_MAC_ADDR_SIZE] = {0x66, 0x55, 0x44, 0x33, 0x22, 0x11}; + memcpy(state.mac, default_mac, sizeof(state.mac)); + memcpy(state.config.address, default_mac, sizeof(state.config.address)); + + // Set default empty AirTag data + uint8_t* data = state.data; + *data++ = 0x1E; // Length + *data++ = 0xFF; // Manufacturer Specific Data + *data++ = 0x4C; // Company ID (Apple, Inc.) + *data++ = 0x00; // ... + *data++ = 0x12; // Type (FindMy) + *data++ = 0x19; // Length + *data++ = 0x00; // Status + // Placeholder Empty Public Key without the MAC address + for(size_t i = 0; i < 22; ++i) { + *data++ = 0x00; + } + *data++ = 0x00; // First 2 bits are the version, the rest is the battery level + *data++ = 0x00; // Hint (0x00) + + // Copy to caller state before popping stack + memcpy(out_state, &state, sizeof(state)); + + // Return if active, can be used to start after loading in an if statement + return state.beacon_active; +} + +void findmy_state_apply(FindMyState* state) { + // Stop any running beacon + if(furi_hal_bt_extra_beacon_is_active()) { + furi_check(furi_hal_bt_extra_beacon_stop()); + } + + furi_check(furi_hal_bt_extra_beacon_set_config(&state->config)); + + furi_check(furi_hal_bt_extra_beacon_set_data(state->data, sizeof(state->data))); + + if(state->beacon_active) { + furi_check(furi_hal_bt_extra_beacon_start()); + } +} diff --git a/applications/system/findmy/findmy_state.h b/applications/system/findmy/findmy_state.h new file mode 100644 index 0000000000..48a82fd2e4 --- /dev/null +++ b/applications/system/findmy/findmy_state.h @@ -0,0 +1,17 @@ +#pragma once + +#include + +typedef struct { + uint8_t mac[EXTRA_BEACON_MAC_ADDR_SIZE]; + uint8_t data[EXTRA_BEACON_MAX_DATA_SIZE]; + GapExtraBeaconConfig config; + + bool beacon_active; + uint8_t broadcast_interval; + uint8_t transmit_power; +} FindMyState; + +bool findmy_state_load(FindMyState* out_state); + +void findmy_state_apply(FindMyState* state); diff --git a/applications/system/findmy/scenes/findmy_scene_config.c b/applications/system/findmy/scenes/findmy_scene_config.c index bf961a5f72..415bda9453 100644 --- a/applications/system/findmy/scenes/findmy_scene_config.c +++ b/applications/system/findmy/scenes/findmy_scene_config.c @@ -12,9 +12,9 @@ void findmy_scene_config_broadcast_interval_changed(VariableItem* item) { uint8_t index = variable_item_get_current_value_index(item); findmy_change_broadcast_interval(app, index + 1); char str[5]; - snprintf(str, sizeof(str), "%ds", app->broadcast_interval); + snprintf(str, sizeof(str), "%ds", app->state.broadcast_interval); variable_item_set_current_value_text(item, str); - variable_item_set_current_value_index(item, app->broadcast_interval - 1); + variable_item_set_current_value_index(item, app->state.broadcast_interval - 1); } void findmy_scene_config_transmit_power_changed(VariableItem* item) { @@ -22,9 +22,9 @@ void findmy_scene_config_transmit_power_changed(VariableItem* item) { uint8_t index = variable_item_get_current_value_index(item); findmy_change_transmit_power(app, index); char str[7]; - snprintf(str, sizeof(str), "%ddBm", app->transmit_power); + snprintf(str, sizeof(str), "%ddBm", app->state.transmit_power); variable_item_set_current_value_text(item, str); - variable_item_set_current_value_index(item, app->transmit_power); + variable_item_set_current_value_index(item, app->state.transmit_power); } void findmy_scene_config_callback(void* context, uint32_t index) { @@ -45,17 +45,17 @@ void findmy_scene_config_on_enter(void* context) { findmy_scene_config_broadcast_interval_changed, app); // Broadcast Interval is 1-10, so use 0-9 and offset indexes by 1 - variable_item_set_current_value_index(item, app->broadcast_interval - 1); - char broadcast_interval_s[5]; - snprintf(broadcast_interval_s, sizeof(broadcast_interval_s), "%ds", app->broadcast_interval); - variable_item_set_current_value_text(item, broadcast_interval_s); + variable_item_set_current_value_index(item, app->state.broadcast_interval - 1); + char interval_str[5]; + snprintf(interval_str, sizeof(interval_str), "%ds", app->state.broadcast_interval); + variable_item_set_current_value_text(item, interval_str); item = variable_item_list_add( var_item_list, "Transmit Power", 7, findmy_scene_config_transmit_power_changed, app); - variable_item_set_current_value_index(item, app->transmit_power); - char transmit_power_s[7]; - snprintf(transmit_power_s, sizeof(transmit_power_s), "%ddBm", app->transmit_power); - variable_item_set_current_value_text(item, transmit_power_s); + variable_item_set_current_value_index(item, app->state.transmit_power); + char power_str[7]; + snprintf(power_str, sizeof(power_str), "%ddBm", app->state.transmit_power); + variable_item_set_current_value_text(item, power_str); item = variable_item_list_add(var_item_list, "Register Tag", 0, NULL, NULL); item = variable_item_list_add( diff --git a/applications/system/findmy/scenes/findmy_scene_config_mac.c b/applications/system/findmy/scenes/findmy_scene_config_mac.c index 0a5ed1392e..b42291a7f4 100644 --- a/applications/system/findmy/scenes/findmy_scene_config_mac.c +++ b/applications/system/findmy/scenes/findmy_scene_config_mac.c @@ -16,7 +16,7 @@ void findmy_scene_config_mac_on_enter(void* context) { byte_input_set_header_text(byte_input, "Enter Bluetooth MAC:"); - memcpy(app->mac_buf, &app->config.address, sizeof(app->mac_buf)); + memcpy(app->mac_buf, app->state.mac, sizeof(app->mac_buf)); furi_hal_bt_reverse_mac_addr(app->mac_buf); byte_input_set_result_callback( @@ -39,8 +39,15 @@ bool findmy_scene_config_mac_on_event(void* context, SceneManagerEvent event) { switch(event.event) { case ByteInputResultOk: furi_hal_bt_reverse_mac_addr(app->mac_buf); - memcpy(&app->config.address, app->mac_buf, sizeof(app->config.address)); - furi_hal_bt_extra_beacon_set_config(&app->config); + memcpy(&app->state.mac, app->mac_buf, sizeof(app->state.mac)); + memcpy(&app->state.config.address, app->mac_buf, sizeof(app->state.config.address)); + if(furi_hal_bt_extra_beacon_is_active()) { + furi_check(furi_hal_bt_extra_beacon_stop()); + } + furi_check(furi_hal_bt_extra_beacon_set_config(&app->state.config)); + if(app->state.beacon_active) { + furi_check(furi_hal_bt_extra_beacon_start()); + } scene_manager_next_scene(app->scene_manager, FindMySceneConfigPacket); break; default: diff --git a/applications/system/findmy/scenes/findmy_scene_config_packet.c b/applications/system/findmy/scenes/findmy_scene_config_packet.c index f431445d58..121bf58b58 100644 --- a/applications/system/findmy/scenes/findmy_scene_config_packet.c +++ b/applications/system/findmy/scenes/findmy_scene_config_packet.c @@ -16,8 +16,7 @@ void findmy_scene_config_packet_on_enter(void* context) { byte_input_set_header_text(byte_input, "Enter Bluetooth Payload:"); - memset(app->packet_buf, 0, sizeof(app->packet_buf)); - furi_hal_bt_extra_beacon_get_data(app->packet_buf); + memcpy(app->packet_buf, app->state.data, sizeof(app->packet_buf)); byte_input_set_result_callback( byte_input, @@ -40,14 +39,10 @@ bool findmy_scene_config_packet_on_event(void* context, SceneManagerEvent event) case ByteInputResultOk: scene_manager_search_and_switch_to_previous_scene( app->scene_manager, FindMySceneConfig); + memcpy(app->state.data, app->packet_buf, sizeof(app->state.data)); furi_check( - furi_hal_bt_extra_beacon_set_data(app->packet_buf, sizeof(app->packet_buf))); - if(app->packet_buf[0] == 0x1E && app->packet_buf[3] == 0x00) { - app->apple = true; // Checks payload data for Apple identifier - } else { - app->apple = false; - } - findmy_main_update_apple(app->findmy_main, app->apple); + furi_hal_bt_extra_beacon_set_data(app->state.data, sizeof(app->state.data))); + findmy_main_update_type(app->findmy_main, findmy_data_get_type(app->state.data)); break; default: break; diff --git a/applications/system/findmy/scenes/findmy_scene_main.c b/applications/system/findmy/scenes/findmy_scene_main.c index 40e22d9420..63932fb5c8 100644 --- a/applications/system/findmy/scenes/findmy_scene_main.c +++ b/applications/system/findmy/scenes/findmy_scene_main.c @@ -25,20 +25,24 @@ bool findmy_scene_main_on_event(void* context, SceneManagerEvent event) { findmy_toggle_beacon(app); break; case FindMyMainEventBackground: - furi_hal_bt_extra_beacon_start(); + if(!furi_hal_bt_extra_beacon_is_active()) { + furi_check(furi_hal_bt_extra_beacon_start()); + } view_dispatcher_stop(app->view_dispatcher); break; case FindMyMainEventConfig: scene_manager_next_scene(app->scene_manager, FindMySceneConfig); break; case FindMyMainEventIntervalUp: - findmy_change_broadcast_interval(app, app->broadcast_interval + 1); + findmy_change_broadcast_interval(app, app->state.broadcast_interval + 1); break; case FindMyMainEventIntervalDown: - findmy_change_broadcast_interval(app, app->broadcast_interval - 1); + findmy_change_broadcast_interval(app, app->state.broadcast_interval - 1); break; case FindMyMainEventQuit: - furi_hal_bt_extra_beacon_stop(); + if(furi_hal_bt_extra_beacon_is_active()) { + furi_check(furi_hal_bt_extra_beacon_stop()); + } break; default: consumed = false; diff --git a/applications/system/findmy/views/findmy_main.c b/applications/system/findmy/views/findmy_main.c index 6ac55d2618..b39adc2bf8 100644 --- a/applications/system/findmy/views/findmy_main.c +++ b/applications/system/findmy/views/findmy_main.c @@ -9,8 +9,8 @@ struct FindMyMain { typedef struct { bool active; - bool apple; uint8_t interval; + FindMyType type; } FindMyMainModel; static void findmy_main_draw_callback(Canvas* canvas, void* _model) { @@ -34,12 +34,17 @@ static void findmy_main_draw_callback(Canvas* canvas, void* _model) { snprintf(interval_str, sizeof(interval_str), "Ping Interval: %ds", model->interval); canvas_draw_str(canvas, 4, 62, interval_str); canvas_set_font(canvas, FontPrimary); - if(model->apple) { + switch(model->type) { + case FindMyTypeApple: canvas_draw_str(canvas, 4, 32, "Apple Network"); canvas_draw_icon(canvas, 80, 24, &I_Lock_7x8); - } else { + break; + case FindMyTypeSamsung: canvas_draw_str(canvas, 4, 32, "Samsung Network"); canvas_draw_icon(canvas, 97, 24, &I_Lock_7x8); + break; + default: + break; } canvas_set_font(canvas, FontSecondary); canvas_draw_str(canvas, 100, 61, "Config"); @@ -96,9 +101,9 @@ FindMyMain* findmy_main_alloc(FindMy* app) { findmy_main->view, FindMyMainModel * model, { - model->active = app->beacon_active; - model->apple = app->apple; - model->interval = app->broadcast_interval; + model->active = app->state.beacon_active; + model->interval = app->state.broadcast_interval; + model->type = findmy_data_get_type(app->state.data); }, false); view_set_context(findmy_main->view, findmy_main); @@ -138,8 +143,8 @@ void findmy_main_update_interval(FindMyMain* findmy_main, uint8_t interval) { findmy_main->view, FindMyMainModel * model, { model->interval = interval; }, true); } -void findmy_main_update_apple(FindMyMain* findmy_main, bool apple) { +void findmy_main_update_type(FindMyMain* findmy_main, FindMyType type) { furi_assert(findmy_main); with_view_model( - findmy_main->view, FindMyMainModel * model, { model->apple = apple; }, true); + findmy_main->view, FindMyMainModel * model, { model->type = type; }, true); } \ No newline at end of file diff --git a/applications/system/findmy/views/findmy_main.h b/applications/system/findmy/views/findmy_main.h index 977f574d71..92a9170ec2 100644 --- a/applications/system/findmy/views/findmy_main.h +++ b/applications/system/findmy/views/findmy_main.h @@ -26,4 +26,4 @@ void findmy_main_set_callback(FindMyMain* findmy_main, FindMyMainCallback callba // To redraw when info changes void findmy_main_update_active(FindMyMain* findmy_main, bool active); void findmy_main_update_interval(FindMyMain* findmy_main, uint8_t interval); -void findmy_main_update_apple(FindMyMain* findmy_main, bool apple); \ No newline at end of file +void findmy_main_update_type(FindMyMain* findmy_main, FindMyType type); \ No newline at end of file