Skip to content

Commit 3ee05e0

Browse files
authored
Merge pull request #6 from jaylikesbunda/main
Small Updates
2 parents 4be6698 + 3dd4dd1 commit 3ee05e0

13 files changed

+927
-148
lines changed

README.md

+5-6
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
# Ghost ESP 👻
2-
A Flipper Zero application for interfacing with the [Ghost ESP32 Firmware]([https://github.com/your-username/ghost_esp_firmware_repo](https://github.com/Spooks4576/Ghost_ESP)).
2+
A Flipper Zero application for interfacing with the Ghost ESP32 firmware
33

44
## Preview
5-
![Ghost ESP Preview](https://github.com/user-attachments/assets/dbff6546-24ed-4d20-af6e-0e01e1643385)
5+
![image](https://github.com/user-attachments/assets/dbff6546-24ed-4d20-af6e-0e01e1643385)
6+
67

78
## Features
89

@@ -34,15 +35,13 @@ A Flipper Zero application for interfacing with the [Ghost ESP32 Firmware]([http
3435
- **ESP Reboot**: Reboot the ESP with a single command
3536
- **NVS Clearing**: Clear NVS data with a confirmation prompt
3637

37-
## Downloads
38-
- **Prebuilt App:** [Download](https://cdn.spookytools.com/assets/ghost_esp.fap)
39-
- **Firmware:** [Ghost ESP32 Firmware Repository](https://github.com/Spooks4576/Ghost_ESP)
4038

4139
## Credits 🙏
4240
- Made by [Spooky](https://github.com/Spooks4576)
4341
- Additional contributions by [Jay Candel](https://github.com/jaylikesbunda)
4442

4543
## Support
46-
For support, please open an [issue](https://github.com/Spooks4576/ghost_esp_app/issues) on the repository or contact [Jay](https://github.com/jaylikesbunda) or [Spooky](https://github.com/Spooks4576).
44+
For support, please open an [issue](https://github.com/Spooks4576/ghost_esp_app/issues) on the repository or contact [Jay](https://github.com/jaylikesbunda) (@fuckyoudeki on Discord) or [Spooky](https://github.com/Spooks4576).
45+
4746

4847

app_state.h

+10
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,15 @@
1111
#include "app_types.h"
1212
#include "settings_ui_types.h"
1313

14+
typedef struct {
15+
bool enabled; // Master switch for filtering
16+
bool show_ble_status;
17+
bool show_wifi_status;
18+
bool show_flipper_devices;
19+
bool show_wifi_networks;
20+
bool strip_ansi_codes;
21+
bool add_prefixes; // Whether to add [BLE], [WIFI] etc prefixes
22+
} FilterConfig;
1423

1524
struct AppState {
1625
// Views
@@ -26,6 +35,7 @@ struct AppState {
2635

2736
// UART Context
2837
UartContext* uart_context;
38+
FilterConfig* filter_config;
2939

3040
// Settings
3141
Settings settings;

callbacks.c

+77
Original file line numberDiff line numberDiff line change
@@ -195,4 +195,81 @@ void nvs_clear_cancelled_callback(void* context) {
195195
view_dispatcher_switch_to_view(app_state->view_dispatcher, prev_view);
196196
app_state->current_view = prev_view; // Add this line
197197
}
198+
}
199+
void show_app_info(void* context) {
200+
SettingsUIContext* settings_context = (SettingsUIContext*)context;
201+
AppState* app = (AppState*)settings_context->context;
202+
203+
FURI_LOG_D("AppInfo", "Show app info called, context: %p", app);
204+
205+
const char* info_text =
206+
"Ghost ESP v1.0.5\n\n"
207+
"Created by: Spooky\n"
208+
"Special Thanks:\n"
209+
"- Jay Candel\n"
210+
"Built with <3";
211+
212+
if(app && app->confirmation_view) {
213+
// Create a new context for the confirmation dialog
214+
SettingsConfirmContext* confirm_ctx = malloc(sizeof(SettingsConfirmContext));
215+
if(!confirm_ctx) {
216+
FURI_LOG_E("AppInfo", "Failed to allocate confirmation context");
217+
return;
218+
}
219+
confirm_ctx->state = app;
220+
221+
// Save current view before switching
222+
app->previous_view = app->current_view;
223+
FURI_LOG_D("AppInfo", "Saved previous view: %d", app->previous_view);
224+
225+
confirmation_view_set_header(app->confirmation_view, "App Info");
226+
confirmation_view_set_text(app->confirmation_view, info_text);
227+
228+
// Set up callbacks with proper context
229+
confirmation_view_set_ok_callback(
230+
app->confirmation_view,
231+
app_info_ok_callback,
232+
confirm_ctx);
233+
confirmation_view_set_cancel_callback(
234+
app->confirmation_view,
235+
app_info_cancel_callback,
236+
confirm_ctx);
237+
238+
// Switch to confirmation view
239+
FURI_LOG_D("AppInfo", "Switching to confirmation view");
240+
view_dispatcher_switch_to_view(app->view_dispatcher, 7); // 7 is confirmation view
241+
app->current_view = 7;
242+
} else {
243+
FURI_LOG_E("AppInfo", "Invalid app state or confirmation view");
244+
}
245+
}
246+
247+
// Update callback functions to use proper context
248+
void app_info_ok_callback(void* context) {
249+
SettingsConfirmContext* ctx = (SettingsConfirmContext*)context;
250+
if(!ctx || !ctx->state) {
251+
FURI_LOG_E("AppInfo", "Invalid callback context");
252+
return;
253+
}
254+
255+
AppState* app_state = ctx->state;
256+
uint32_t prev_view = app_state->previous_view;
257+
258+
FURI_LOG_D("AppInfo", "OK callback, returning to view: %lu", prev_view);
259+
260+
// Reset callbacks
261+
confirmation_view_set_ok_callback(app_state->confirmation_view, NULL, NULL);
262+
confirmation_view_set_cancel_callback(app_state->confirmation_view, NULL, NULL);
263+
264+
// Free the context
265+
free(ctx);
266+
267+
// Return to previous view
268+
view_dispatcher_switch_to_view(app_state->view_dispatcher, prev_view);
269+
app_state->current_view = prev_view;
270+
}
271+
272+
void app_info_cancel_callback(void* context) {
273+
// Use same callback as OK since both just return to previous view
274+
app_info_ok_callback(context);
198275
}

callbacks.h

+4-1
Original file line numberDiff line numberDiff line change
@@ -22,4 +22,7 @@ void on_clear_nvs_changed(VariableItem* item);
2222
void logs_clear_confirmed_callback(void* context);
2323
void logs_clear_cancelled_callback(void* context);
2424
void nvs_clear_confirmed_callback(void* context);
25-
void nvs_clear_cancelled_callback(void* context);
25+
void nvs_clear_cancelled_callback(void* context);
26+
void show_app_info(void* context);
27+
void app_info_ok_callback(void* context);
28+
void app_info_cancel_callback(void* context);

docs/changelog.md

+19-4
Original file line numberDiff line numberDiff line change
@@ -69,11 +69,26 @@
6969
- Added state tracking
7070

7171

72+
## v1.0.4
73+
74+
- Refined confirmation view line breaks for readability
75+
- Improved ESP Connectity check to decrease false negatives
76+
- Added optional filtering to UART output to improve readability (BETA)
77+
- Added 'App Info' Button in Settings
78+
- Misc Changes (mostly to UI)
79+
80+
81+
## v1.0.5
82+
- Commands will silently fail if UART isn't working rather than crashing
83+
- Fixed double-free memory issue by removing stream buffer cleanup from the worker thread
84+
- Reorganized initialization order
85+
- UART initialization happens in background
86+
- Serial operations don't block app startup
87+
- Optimized storage initialization by deferring file operations until needed
88+
- Improved directory creation efficiency in storage handling
7289

7390
## TODO
7491
- Add Help menu
75-
- Add optional filtering to UART output to improve readability
76-
- Improve ESP Connectity check to decrease false negatives
7792
- Add view log from start/end configuration setting
78-
- Double check settings get sent to board and get pulled from
79-
- Improve directory organisation
93+
- Settings get sent to board and get pulled from on ESP Check or Init
94+
- Improve directory organisation

main.c

+79-22
Original file line numberDiff line numberDiff line change
@@ -26,23 +26,44 @@
2626
// Include the header where settings_custom_event_callback is declared
2727
#include "settings_ui.h"
2828

29+
#define UART_INIT_STACK_SIZE 2048
30+
31+
static int32_t init_uart_task(void* context) {
32+
AppState* state = context;
33+
34+
// Add some delay to let system stabilize
35+
furi_delay_ms(50);
36+
37+
state->uart_context = uart_init(state);
38+
if (state->uart_context) {
39+
FURI_LOG_I("Ghost_ESP", "UART initialized successfully");
40+
} else {
41+
FURI_LOG_E("Ghost_ESP", "UART initialization failed");
42+
}
43+
return 0;
44+
}
45+
46+
2947
int32_t ghost_esp_app(void* p) {
3048
UNUSED(p);
3149

32-
uint8_t attempts = 0;
50+
// Quick power check and initialization
3351
bool otg_was_enabled = furi_hal_power_is_otg_enabled();
34-
while(!furi_hal_power_is_otg_enabled() && attempts++ < 5) {
35-
furi_hal_power_enable_otg();
36-
furi_delay_ms(10);
52+
if(!otg_was_enabled) {
53+
uint8_t attempts = 0;
54+
while(!furi_hal_power_is_otg_enabled() && attempts++ < 3) {
55+
furi_hal_power_enable_otg();
56+
furi_delay_ms(10);
57+
}
58+
furi_delay_ms(50);
3759
}
38-
furi_delay_ms(200);
3960

40-
// Set up UI
61+
// Set up bare minimum UI state
4162
AppState* state = malloc(sizeof(AppState));
4263
if (!state) return -1;
4364
memset(state, 0, sizeof(AppState)); // Zero all memory first
4465

45-
// Initialize text buffers
66+
// Initialize essential text buffers with minimal size
4667
state->textBoxBuffer = malloc(1);
4768
if (state->textBoxBuffer) {
4869
state->textBoxBuffer[0] = '\0';
@@ -53,50 +74,75 @@ int32_t ghost_esp_app(void* p) {
5374
memset(state->input_buffer, 0, 32);
5475
}
5576

56-
// Initialize UI components
77+
// Initialize UI components - core components first
5778
state->view_dispatcher = view_dispatcher_alloc();
5879
state->main_menu = main_menu_alloc();
80+
if (!state->view_dispatcher || !state->main_menu) {
81+
// Clean up and exit if core components fail
82+
if (state->view_dispatcher) view_dispatcher_free(state->view_dispatcher);
83+
if (state->main_menu) main_menu_free(state->main_menu);
84+
free(state->textBoxBuffer);
85+
free(state->input_buffer);
86+
free(state);
87+
return -1;
88+
}
89+
90+
// Allocate remaining UI components
5991
state->wifi_menu = submenu_alloc();
6092
state->ble_menu = submenu_alloc();
6193
state->gps_menu = submenu_alloc();
6294
state->text_box = text_box_alloc();
6395
state->settings_menu = variable_item_list_alloc();
6496
state->text_input = text_input_alloc();
6597
state->confirmation_view = confirmation_view_alloc();
98+
state->settings_actions_menu = submenu_alloc();
6699

67-
// Set headers after allocation
100+
// Set headers - only for successfully allocated components
68101
if(state->main_menu) main_menu_set_header(state->main_menu, "Select a Utility");
69102
if(state->wifi_menu) submenu_set_header(state->wifi_menu, "Select a Wifi Utility");
70103
if(state->ble_menu) submenu_set_header(state->ble_menu, "Select a Bluetooth Utility");
71104
if(state->gps_menu) submenu_set_header(state->gps_menu, "Select a GPS Utility");
72105
if(state->text_input) text_input_set_header_text(state->text_input, "Enter Your Text");
106+
if(state->settings_actions_menu) submenu_set_header(state->settings_actions_menu, "Settings");
73107

74-
// Initialize storage and load settings before UART
108+
// Initialize settings and configuration early
75109
settings_storage_init();
76110
if(settings_storage_load(&state->settings, GHOST_ESP_APP_SETTINGS_FILE) != SETTINGS_OK) {
77111
memset(&state->settings, 0, sizeof(Settings));
78112
state->settings.stop_on_back_index = 1;
79113
settings_storage_save(&state->settings, GHOST_ESP_APP_SETTINGS_FILE);
80114
}
81115

116+
// Initialize filter config
117+
state->filter_config = malloc(sizeof(FilterConfig));
118+
if(state->filter_config) {
119+
state->filter_config->enabled = state->settings.enable_filtering_index;
120+
state->filter_config->show_ble_status = true;
121+
state->filter_config->show_wifi_status = true;
122+
state->filter_config->show_flipper_devices = true;
123+
state->filter_config->show_wifi_networks = true;
124+
state->filter_config->strip_ansi_codes = true;
125+
state->filter_config->add_prefixes = true;
126+
}
127+
82128
// Set up settings UI context
83129
state->settings_ui_context.settings = &state->settings;
84130
state->settings_ui_context.send_uart_command = send_uart_command;
85131
state->settings_ui_context.switch_to_view = NULL;
86132
state->settings_ui_context.show_confirmation_view = show_confirmation_view_wrapper;
87133
state->settings_ui_context.context = state;
88-
state->settings_actions_menu = submenu_alloc();
89-
if(state->settings_actions_menu) {
90-
submenu_set_header(state->settings_actions_menu, "Settings Actions");
91-
}
92134

93135
// Initialize settings menu
94136
settings_setup_gui(state->settings_menu, &state->settings_ui_context);
95137

96-
// Initialize UART after settings are ready
97-
state->uart_context = uart_init(state);
138+
// Start UART init in background thread
139+
FuriThread* uart_init_thread = furi_thread_alloc_ex(
140+
"UartInit",
141+
UART_INIT_STACK_SIZE, // Increased stack size
142+
init_uart_task,
143+
state);
98144

99-
// Add views if view_dispatcher exists
145+
// Add views to dispatcher - check each component before adding
100146
if(state->view_dispatcher) {
101147
if(state->main_menu) view_dispatcher_add_view(state->view_dispatcher, 0, main_menu_get_view(state->main_menu));
102148
if(state->wifi_menu) view_dispatcher_add_view(state->view_dispatcher, 1, submenu_get_view(state->wifi_menu));
@@ -108,14 +154,16 @@ int32_t ghost_esp_app(void* p) {
108154
if(state->confirmation_view) view_dispatcher_add_view(state->view_dispatcher, 7, confirmation_view_get_view(state->confirmation_view));
109155
if(state->settings_actions_menu) view_dispatcher_add_view(state->view_dispatcher, 8, submenu_get_view(state->settings_actions_menu));
110156

111-
view_dispatcher_set_custom_event_callback(state->view_dispatcher, settings_custom_event_callback);
112-
157+
view_dispatcher_set_custom_event_callback(state->view_dispatcher, settings_custom_event_callback);
113158
}
114159

115-
// Show main menu
160+
// Show main menu immediately
116161
show_main_menu(state);
117162

118-
// Set up GUI
163+
// Initialize UART in background
164+
state->uart_context = uart_init(state);
165+
166+
// Set up and run GUI
119167
Gui* gui = furi_record_open("gui");
120168
if(gui && state->view_dispatcher) {
121169
view_dispatcher_attach_to_gui(state->view_dispatcher, gui, ViewDispatcherTypeFullscreen);
@@ -125,13 +173,18 @@ int32_t ghost_esp_app(void* p) {
125173
}
126174
furi_record_close("gui");
127175

176+
// Wait for UART initialization to complete
177+
furi_thread_join(uart_init_thread);
178+
furi_thread_free(uart_init_thread);
179+
128180
// Start cleanup - first remove views
129181
if(state->view_dispatcher) {
130182
for(size_t i = 0; i <= 8; i++) {
131183
view_dispatcher_remove_view(state->view_dispatcher, i);
132184
}
133185
}
134186

187+
135188
// Clear callbacks before cleanup
136189
if(state->confirmation_view) {
137190
confirmation_view_set_ok_callback(state->confirmation_view, NULL, NULL);
@@ -197,7 +250,11 @@ int32_t ghost_esp_app(void* p) {
197250
free(state->textBoxBuffer);
198251
state->textBoxBuffer = NULL;
199252
}
200-
253+
// Add filter config cleanup
254+
if(state->filter_config) {
255+
free(state->filter_config);
256+
state->filter_config = NULL;
257+
}
201258
// Final state cleanup
202259
free(state);
203260

menu.c

+3-3
Original file line numberDiff line numberDiff line change
@@ -61,12 +61,12 @@ static const MenuCommand wifi_commands[] = {
6161

6262
// Portal & Network Operations
6363
{"Evil Portal", "startportal\n", NULL, NULL, NULL, false, NULL, true,
64-
"Evil Portal", "You need to configure settings\n in the WebUI for this command.\n\n"},
64+
"Evil Portal", "You need to configure\n settings in the WebUI\n for this command.\n\n"},
6565
{"Stop Portal", "stopportal\n", NULL, NULL, NULL, false, NULL, false, NULL, NULL},
6666
{"Connect To WiFi", "connect", NULL, NULL, NULL, true, "SSID,Password", false, NULL, NULL},
67-
{"Dial Random Video", "dialconnect\n", NULL, NULL, NULL, false, NULL, false, NULL, NULL},
67+
{"Cast Random Video", "dialconnect\n", NULL, NULL, NULL, false, NULL, false, NULL, NULL},
6868
{"Printer Power", "powerprinter\n", NULL, NULL, NULL, false, NULL, true,
69-
"Printer Power", "You need to configure settings\n n the WebUI for this command.\n"},
69+
"Printer Power", "You need to configure\n settings in the WebUI\n for this command.\n"},
7070
};
7171

7272
// BLE menu command definitions

0 commit comments

Comments
 (0)