1
+ #include "wardriver.h"
2
+ #include "wardriver_uart.h"
3
+
4
+ void save_file (Context * ctx ) {
5
+ Storage * storage = furi_record_open (RECORD_STORAGE );
6
+ FuriHalRtcDateTime datetime ;
7
+ furi_hal_rtc_get_datetime (& datetime );
8
+
9
+ FuriString * filename = furi_string_alloc ();
10
+ furi_string_printf (
11
+ filename ,
12
+ "%s/%s_%d_%d_%d_%d_%d_%d.txt" ,
13
+ FILE_PATH ,
14
+ "wigle" ,
15
+ datetime .year ,
16
+ datetime .month ,
17
+ datetime .day ,
18
+ datetime .hour ,
19
+ datetime .minute ,
20
+ datetime .second );
21
+
22
+ File * file = storage_file_alloc (storage );
23
+
24
+ if (!storage_common_exists (storage , FILE_PATH )) {
25
+ storage_common_mkdir (storage , FILE_PATH );
26
+ }
27
+
28
+ if (!storage_file_open (file , furi_string_get_cstr (filename ), FSAM_WRITE , FSOM_OPEN_ALWAYS )) {
29
+ FURI_LOG_I (appname , "Failed to open file" );
30
+ storage_file_close (file );
31
+ storage_file_free (file );
32
+ furi_string_free (filename );
33
+ furi_record_close (RECORD_STORAGE );
34
+
35
+ furi_hal_light_blink_start (LightRed , 100 , 100 , 5000 );
36
+
37
+ return ;
38
+ }
39
+
40
+ furi_string_reset (ctx -> buffer );
41
+
42
+ // WIGLE HEADERS DONT CHANGE THIS ITS IMPORTANT!
43
+ furi_string_printf (
44
+ ctx -> buffer ,
45
+ "%s,%s,%s,%s,%s,%s,%s,%s\r\n" ,
46
+ "WigleWifi-1.4" ,
47
+ "appRelease=v2.0" ,
48
+ "model=S33" ,
49
+ "release=XtremeFW" ,
50
+ "Flipper Zero" ,
51
+ "" ,
52
+ "Wardriver" ,
53
+ "S33" );
54
+
55
+ furi_string_cat_printf (
56
+ ctx -> buffer ,
57
+ "%s,%s,%s,%s,%s,%s,%s\r\n" ,
58
+ "MAC" ,
59
+ "SSID" ,
60
+ "FirstSeen" ,
61
+ "Channel" ,
62
+ "RSSI" ,
63
+ "CurrentLatitude" ,
64
+ "CurrentLongitude" );
65
+ if (!storage_file_write (
66
+ file , furi_string_get_cstr (ctx -> buffer ), strlen (furi_string_get_cstr (ctx -> buffer )))) {
67
+ FURI_LOG_I (appname , "Failed to write header to file" );
68
+ }
69
+
70
+ for (int i = 0 ; i < ctx -> access_points_count ; i ++ ) {
71
+ AccessPoint ap = ctx -> access_points [i ];
72
+ furi_string_printf (
73
+ ctx -> buffer ,
74
+ "%s,%s,%04d-%02d-%02d %02d:%02d:%02d,%d,%d,%f,%f\r\n" ,
75
+ ap .bssid ,
76
+ ap .ssid ,
77
+ ap .datetime .year ,
78
+ ap .datetime .month ,
79
+ ap .datetime .day ,
80
+ ap .datetime .hour ,
81
+ ap .datetime .minute ,
82
+ ap .datetime .second ,
83
+ ap .rssi ,
84
+ ap .channel ,
85
+ (double )ap .latitude ,
86
+ (double )ap .longitude );
87
+
88
+ if (!storage_file_write (
89
+ file ,
90
+ furi_string_get_cstr (ctx -> buffer ),
91
+ strlen (furi_string_get_cstr (ctx -> buffer )))) {
92
+ FURI_LOG_I (appname , "Failed to write AP to file" );
93
+ }
94
+
95
+ free (ap .ssid );
96
+ free (ap .bssid );
97
+ }
98
+
99
+ storage_file_close (file );
100
+ storage_file_free (file );
101
+ furi_string_free (filename );
102
+ furi_record_close (RECORD_STORAGE );
103
+
104
+ return ;
105
+ }
106
+
107
+ static void tick_callback (void * ctx_q ) {
108
+ furi_assert (ctx_q );
109
+ FuriMessageQueue * queue = ctx_q ;
110
+ Event event = {.type = EventTypeTick };
111
+ furi_message_queue_put (queue , & event , 0 );
112
+ }
113
+
114
+ static void input_callback (InputEvent * input_event , FuriMessageQueue * queue ) {
115
+ furi_assert (queue );
116
+ Event event = {.type = EventTypeKey , .input = * input_event };
117
+ furi_message_queue_put (queue , & event , FuriWaitForever );
118
+ }
119
+
120
+ static void draw_access_point (Canvas * canvas , Context * context ) {
121
+ Context * ctx = context ;
122
+
123
+ AccessPoint ap = ctx -> active_access_point ;
124
+
125
+ canvas_draw_str_aligned (canvas , 62 , 25 , AlignCenter , AlignBottom , ap .ssid );
126
+
127
+ canvas_set_font (canvas , FontSecondary );
128
+
129
+ canvas_draw_str_aligned (canvas , 38 , 12 , AlignLeft , AlignBottom , ap .bssid );
130
+
131
+ furi_string_printf (ctx -> buffer , "Signal strength: %ddBm" , ap .rssi );
132
+ canvas_draw_str_aligned (
133
+ canvas , 3 , 35 , AlignLeft , AlignBottom , furi_string_get_cstr (ctx -> buffer ));
134
+
135
+ furi_string_printf (ctx -> buffer , "CH: %d" , ap .channel );
136
+ canvas_draw_str_aligned (
137
+ canvas , 3 , 47 , AlignLeft , AlignBottom , furi_string_get_cstr (ctx -> buffer ));
138
+
139
+ furi_string_printf (ctx -> buffer , "%d" , ap .packetRxCount );
140
+ canvas_draw_icon (canvas , 35 , 39 , & I_down );
141
+ canvas_draw_str_aligned (
142
+ canvas , 45 , 47 , AlignLeft , AlignBottom , furi_string_get_cstr (ctx -> buffer ));
143
+
144
+ furi_string_printf (ctx -> buffer , "%d" , ap .packetTxCount );
145
+ canvas_draw_icon (canvas , 85 , 38 , & I_up );
146
+ canvas_draw_str_aligned (
147
+ canvas , 95 , 47 , AlignLeft , AlignBottom , furi_string_get_cstr (ctx -> buffer ));
148
+
149
+ furi_string_printf (
150
+ ctx -> buffer ,
151
+ "Seen: %02d:%02d:%02d (%lds ago)" ,
152
+ ap .datetime .hour ,
153
+ ap .datetime .minute ,
154
+ ap .datetime .second ,
155
+ furi_hal_rtc_get_timestamp () - furi_hal_rtc_datetime_to_timestamp (& ap .datetime ));
156
+ canvas_draw_str_aligned (
157
+ canvas , 3 , 59 , AlignLeft , AlignBottom , furi_string_get_cstr (ctx -> buffer ));
158
+ }
159
+
160
+ static void render_callback (Canvas * canvas , void * context ) {
161
+ Context * ctx = context ;
162
+
163
+ canvas_set_font (canvas , FontPrimary );
164
+
165
+ if (ctx -> access_points_count >= MAX_ACCESS_POINTS ) {
166
+ canvas_draw_str (canvas , 118 , 10 , "!" );
167
+ }
168
+
169
+ switch (ctx -> view_state ) {
170
+ case SHOW_NMEA :
171
+
172
+ if (UART_CH_ESP == UART_CH_GPS ) {
173
+ canvas_draw_str (canvas , 0 , 10 , "GPS channel invalid!" );
174
+ canvas_draw_str (canvas , 0 , 20 , "Change UART" );
175
+ canvas_draw_str (canvas , 0 , 30 , "channel" );
176
+ canvas_draw_str (canvas , 0 , 40 , "in the Xtreme" );
177
+ canvas_draw_str (canvas , 0 , 50 , "app" );
178
+ } else {
179
+ furi_string_printf (
180
+ ctx -> buffer ,
181
+ "%f" ,
182
+ isnan (ctx -> gps_data .latitude ) ? 0 : (double )ctx -> gps_data .latitude );
183
+ canvas_draw_str (canvas , 0 , 10 , furi_string_get_cstr (ctx -> buffer ));
184
+
185
+ furi_string_printf (
186
+ ctx -> buffer ,
187
+ "%f" ,
188
+ isnan (ctx -> gps_data .longitude ) ? 0 : (double )ctx -> gps_data .longitude );
189
+ canvas_draw_str (canvas , 0 , 20 , furi_string_get_cstr (ctx -> buffer ));
190
+
191
+ furi_string_printf (ctx -> buffer , "%d sats" , ctx -> gps_data .satelites );
192
+ canvas_draw_str (canvas , 0 , 30 , furi_string_get_cstr (ctx -> buffer ));
193
+
194
+ furi_string_printf (
195
+ ctx -> buffer ,
196
+ "%02d:%02d:%02dZ" ,
197
+ ctx -> gps_data .hour ,
198
+ ctx -> gps_data .minute ,
199
+ ctx -> gps_data .second );
200
+ canvas_draw_str (canvas , 0 , 40 , furi_string_get_cstr (ctx -> buffer ));
201
+ canvas_draw_str (canvas , 70 , 10 , "GPS DATA" );
202
+ }
203
+
204
+ elements_button_left (canvas , "Back" );
205
+
206
+ canvas_draw_icon (canvas , 71 , 15 , & I_DolphinCommon_56x48 );
207
+ break ;
208
+ case NO_APS :
209
+ canvas_draw_str (canvas , 80 , 30 , "No AP's" );
210
+ canvas_draw_str (canvas , 80 , 40 , "Found!" );
211
+ canvas_draw_icon (canvas , 1 , 4 , & I_DolphinWait_61x59 );
212
+ break ;
213
+ case NORMAL :
214
+ default :
215
+ canvas_draw_frame (canvas , 0 , 0 , 128 , 64 );
216
+
217
+ furi_string_printf (
218
+ ctx -> buffer , "%d/%d" , ctx -> access_points_index + 1 , ctx -> access_points_count );
219
+
220
+ canvas_draw_str (canvas , 3 , 12 , furi_string_get_cstr (ctx -> buffer ));
221
+
222
+ draw_access_point (canvas , ctx );
223
+ break ;
224
+ }
225
+ furi_mutex_release (ctx -> mutex );
226
+ }
227
+
228
+ int32_t wardriver_app () {
229
+ // turn off 5v, so it gets reset on startup
230
+ if (furi_hal_power_is_otg_enabled ()) {
231
+ furi_hal_power_disable_otg ();
232
+ }
233
+
234
+ // Enable 5v on startup
235
+ uint8_t attempts = 0 ;
236
+ while (!furi_hal_power_is_otg_enabled () && attempts ++ < 5 ) {
237
+ furi_hal_power_enable_otg ();
238
+ furi_delay_ms (10 );
239
+ }
240
+ furi_delay_ms (200 );
241
+
242
+ Context * ctx = malloc (sizeof (Context ));
243
+ ctx -> queue = furi_message_queue_alloc (8 , sizeof (Event ));
244
+ ctx -> mutex = furi_mutex_alloc (FuriMutexTypeNormal );
245
+ ctx -> buffer = furi_string_alloc ();
246
+
247
+ ctx -> access_points_count = 0 ;
248
+ ctx -> access_points_index = 0 ;
249
+ ctx -> pressedButton = false;
250
+ ctx -> view_state = NO_APS ;
251
+
252
+ wardriver_uart_init (ctx );
253
+
254
+ ViewPort * view_port = view_port_alloc ();
255
+ view_port_draw_callback_set (view_port , render_callback , ctx );
256
+ view_port_input_callback_set (view_port , input_callback , ctx -> queue );
257
+
258
+ Gui * gui = furi_record_open (RECORD_GUI );
259
+ gui_add_view_port (gui , view_port , GuiLayerFullscreen );
260
+
261
+ FuriTimer * timer = furi_timer_alloc (tick_callback , FuriTimerTypePeriodic , ctx -> queue );
262
+ furi_timer_start (timer , 100 );
263
+
264
+ // application loop
265
+ Event event ;
266
+ bool processing = true;
267
+ do {
268
+ if (furi_message_queue_get (ctx -> queue , & event , FuriWaitForever ) == FuriStatusOk ) {
269
+ furi_mutex_acquire (ctx -> mutex , FuriWaitForever );
270
+ switch (event .type ) {
271
+ case EventTypeKey :
272
+ if (event .input .type == InputTypeShort && event .input .key == InputKeyBack ) {
273
+ if (ctx -> view_state == SHOW_NMEA ) {
274
+ ctx -> view_state = NORMAL ;
275
+ } else {
276
+ processing = false;
277
+ }
278
+ } else if (event .input .type == InputTypeLong && event .input .key == InputKeyBack ) {
279
+ if (ctx -> view_state == SHOW_NMEA ) {
280
+ ctx -> view_state = NORMAL ;
281
+ } else {
282
+ processing = false;
283
+ }
284
+ } else if (event .input .type == InputTypeLong && event .input .key == InputKeyOk ) {
285
+ } else if (event .input .type == InputTypePress && event .input .key == InputKeyDown ) {
286
+ ctx -> access_points_index -- ;
287
+ if (ctx -> access_points_index < 0 ) {
288
+ ctx -> access_points_index = ctx -> access_points_count - 1 ;
289
+ }
290
+ ctx -> active_access_point = ctx -> access_points [ctx -> access_points_index ];
291
+ ctx -> pressedButton = true;
292
+ } else if (event .input .type == InputTypePress && event .input .key == InputKeyUp ) {
293
+ ctx -> access_points_index ++ ;
294
+ if (ctx -> access_points_index >= ctx -> access_points_count ) {
295
+ ctx -> access_points_index = 0 ;
296
+ }
297
+ ctx -> active_access_point = ctx -> access_points [ctx -> access_points_index ];
298
+ ctx -> pressedButton = true;
299
+ } else if (event .input .type == InputTypePress && event .input .key == InputKeyLeft ) {
300
+ if (ctx -> view_state == NORMAL ) {
301
+ ctx -> view_state = SHOW_NMEA ;
302
+ } else if (ctx -> view_state == SHOW_NMEA ) {
303
+ ctx -> view_state = NORMAL ;
304
+ }
305
+ } else if (event .input .type == InputTypePress && event .input .key == InputKeyRight ) {
306
+ }
307
+ break ;
308
+ case EventTypeTick :
309
+ // fix for the empty active access point when there was no interaction
310
+ if (!ctx -> pressedButton ) {
311
+ ctx -> access_points_index = 0 ;
312
+ ctx -> active_access_point = ctx -> access_points [ctx -> access_points_index ];
313
+ }
314
+
315
+ break ;
316
+ default :
317
+ break ;
318
+ }
319
+
320
+ view_port_update (view_port );
321
+ } else {
322
+ processing = false;
323
+ }
324
+ } while (processing );
325
+
326
+ furi_timer_free (timer );
327
+ view_port_enabled_set (view_port , false);
328
+ gui_remove_view_port (gui , view_port );
329
+ view_port_free (view_port );
330
+ furi_record_close (RECORD_GUI );
331
+
332
+ furi_message_queue_free (ctx -> queue );
333
+ furi_mutex_free (ctx -> mutex );
334
+ furi_string_free (ctx -> buffer );
335
+
336
+ wardriver_uart_deinit (ctx );
337
+
338
+ save_file (ctx );
339
+
340
+ free (ctx );
341
+
342
+ furi_hal_light_set (LightBlue , 0 );
343
+ furi_hal_light_set (LightGreen , 0 );
344
+
345
+ if (furi_hal_power_is_otg_enabled ()) {
346
+ furi_hal_power_disable_otg ();
347
+ }
348
+
349
+ return 0 ;
350
+ }
0 commit comments