1 /* Wi-Fi FTM Example
2 
3    This example code is in the Public Domain (or CC0 licensed, at your option.)
4 
5    Unless required by applicable law or agreed to in writing, this
6    software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
7    CONDITIONS OF ANY KIND, either express or implied.
8 */
9 
10 #include <errno.h>
11 #include <string.h>
12 #include <stdio.h>
13 #include <string.h>
14 #include "nvs_flash.h"
15 #include "cmd_system.h"
16 #include "argtable3/argtable3.h"
17 #include "freertos/FreeRTOS.h"
18 #include "freertos/event_groups.h"
19 #include "esp_event.h"
20 #include "esp_log.h"
21 #include "esp_err.h"
22 #include "esp_wifi.h"
23 #include "esp_console.h"
24 
25 typedef struct {
26     struct arg_str *ssid;
27     struct arg_str *password;
28     struct arg_end *end;
29 } wifi_args_t;
30 
31 typedef struct {
32     struct arg_str *ssid;
33     struct arg_end *end;
34 } wifi_scan_arg_t;
35 
36 typedef struct {
37     /* FTM Initiator */
38     struct arg_lit *initiator;
39     struct arg_int *frm_count;
40     struct arg_int *burst_period;
41     struct arg_str *ssid;
42     /* FTM Responder */
43     struct arg_lit *responder;
44     struct arg_lit *enable;
45     struct arg_lit *disable;
46     struct arg_int *offset;
47     struct arg_end *end;
48 } wifi_ftm_args_t;
49 
50 static wifi_args_t sta_args;
51 static wifi_args_t ap_args;
52 static wifi_scan_arg_t scan_args;
53 static wifi_ftm_args_t ftm_args;
54 
55 wifi_config_t g_ap_config = {
56     .ap.max_connection = 4,
57     .ap.authmode = WIFI_AUTH_WPA2_PSK,
58     .ap.ftm_responder = true
59 };
60 
61 #define ETH_ALEN 6
62 #define MAX_CONNECT_RETRY_ATTEMPTS  5
63 
64 static bool s_reconnect = true;
65 static int s_retry_num = 0;
66 static const char *TAG_STA = "ftm_station";
67 static const char *TAG_AP = "ftm_ap";
68 
69 static EventGroupHandle_t wifi_event_group;
70 const int CONNECTED_BIT = BIT0;
71 const int DISCONNECTED_BIT = BIT1;
72 
73 static EventGroupHandle_t ftm_event_group;
74 const int FTM_REPORT_BIT = BIT0;
75 const int FTM_FAILURE_BIT = BIT1;
76 wifi_ftm_report_entry_t *g_ftm_report;
77 uint8_t g_ftm_report_num_entries;
78 static uint32_t g_rtt_est, g_dist_est;
79 bool g_ap_started;
80 uint8_t g_ap_channel;
81 uint8_t g_ap_bssid[ETH_ALEN];
82 
83 const int g_report_lvl =
84 #ifdef CONFIG_ESP_FTM_REPORT_SHOW_DIAG
85     BIT0 |
86 #endif
87 #ifdef CONFIG_ESP_FTM_REPORT_SHOW_RTT
88     BIT1 |
89 #endif
90 #ifdef CONFIG_ESP_FTM_REPORT_SHOW_T1T2T3T4
91     BIT2 |
92 #endif
93 #ifdef CONFIG_ESP_FTM_REPORT_SHOW_RSSI
94     BIT3 |
95 #endif
96 0;
97 
98 uint16_t g_scan_ap_num;
99 wifi_ap_record_t *g_ap_list_buffer;
100 
event_handler(void * arg,esp_event_base_t event_base,int32_t event_id,void * event_data)101 static void event_handler(void *arg, esp_event_base_t event_base,
102                           int32_t event_id, void *event_data)
103 {
104 	if (event_id == WIFI_EVENT_STA_CONNECTED) {
105         wifi_event_sta_connected_t *event = (wifi_event_sta_connected_t *)event_data;
106 
107         ESP_LOGI(TAG_STA, "Connected to %s (BSSID: "MACSTR", Channel: %d)", event->ssid,
108                  MAC2STR(event->bssid), event->channel);
109 
110         memcpy(g_ap_bssid, event->bssid, ETH_ALEN);
111         g_ap_channel = event->channel;
112         xEventGroupClearBits(wifi_event_group, DISCONNECTED_BIT);
113         xEventGroupSetBits(wifi_event_group, CONNECTED_BIT);
114     } else if (event_id == WIFI_EVENT_STA_DISCONNECTED) {
115         if (s_reconnect && ++s_retry_num < MAX_CONNECT_RETRY_ATTEMPTS) {
116             ESP_LOGI(TAG_STA, "sta disconnect, retry attempt %d...", s_retry_num);
117             esp_wifi_connect();
118         } else {
119             ESP_LOGI(TAG_STA, "sta disconnected");
120         }
121         xEventGroupClearBits(wifi_event_group, CONNECTED_BIT);
122         xEventGroupSetBits(wifi_event_group, DISCONNECTED_BIT);
123     } else if (event_id == WIFI_EVENT_FTM_REPORT) {
124         wifi_event_ftm_report_t *event = (wifi_event_ftm_report_t *) event_data;
125 
126         if (event->status == FTM_STATUS_SUCCESS) {
127             g_rtt_est = event->rtt_est;
128             g_dist_est = event->dist_est;
129             g_ftm_report = event->ftm_report_data;
130             g_ftm_report_num_entries = event->ftm_report_num_entries;
131             xEventGroupSetBits(ftm_event_group, FTM_REPORT_BIT);
132         } else {
133             ESP_LOGI(TAG_STA, "FTM procedure with Peer("MACSTR") failed! (Status - %d)",
134                      MAC2STR(event->peer_mac), event->status);
135             xEventGroupSetBits(ftm_event_group, FTM_FAILURE_BIT);
136         }
137     } else if (event_id == WIFI_EVENT_AP_START) {
138         g_ap_started = true;
139     } else if (event_id == WIFI_EVENT_AP_STOP) {
140         g_ap_started = false;
141     }
142 }
143 
ftm_process_report(void)144 static void ftm_process_report(void)
145 {
146     int i;
147     char *log = malloc(200);
148 
149     if (!g_report_lvl)
150         return;
151 
152     if (!log) {
153         ESP_LOGE(TAG_STA, "Failed to alloc buffer for FTM report");
154         return;
155     }
156 
157     bzero(log, 200);
158     sprintf(log, "%s%s%s%s", g_report_lvl & BIT0 ? " Diag |":"", g_report_lvl & BIT1 ? "   RTT   |":"",
159                  g_report_lvl & BIT2 ? "       T1       |       T2       |       T3       |       T4       |":"",
160                  g_report_lvl & BIT3 ? "  RSSI  |":"");
161     ESP_LOGI(TAG_STA, "FTM Report:");
162     ESP_LOGI(TAG_STA, "|%s", log);
163     for (i = 0; i < g_ftm_report_num_entries; i++) {
164         char *log_ptr = log;
165 
166         bzero(log, 200);
167         if (g_report_lvl & BIT0) {
168             log_ptr += sprintf(log_ptr, "%6d|", g_ftm_report[i].dlog_token);
169         }
170         if (g_report_lvl & BIT1) {
171             log_ptr += sprintf(log_ptr, "%7u  |", g_ftm_report[i].rtt);
172         }
173         if (g_report_lvl & BIT2) {
174             log_ptr += sprintf(log_ptr, "%14llu  |%14llu  |%14llu  |%14llu  |", g_ftm_report[i].t1,
175                                         g_ftm_report[i].t2, g_ftm_report[i].t3, g_ftm_report[i].t4);
176         }
177         if (g_report_lvl & BIT3) {
178             log_ptr += sprintf(log_ptr, "%6d  |", g_ftm_report[i].rssi);
179         }
180         ESP_LOGI(TAG_STA, "|%s", log);
181     }
182     free(log);
183 }
184 
initialise_wifi(void)185 void initialise_wifi(void)
186 {
187     esp_log_level_set("wifi", ESP_LOG_WARN);
188     static bool initialized = false;
189 
190     if (initialized) {
191         return;
192     }
193 
194     ESP_ERROR_CHECK(esp_netif_init());
195     wifi_event_group = xEventGroupCreate();
196     ftm_event_group = xEventGroupCreate();
197     ESP_ERROR_CHECK( esp_event_loop_create_default() );
198     wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
199     ESP_ERROR_CHECK(esp_wifi_init(&cfg));
200 
201     esp_event_handler_instance_t instance_any_id;
202     ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT,
203                                                         ESP_EVENT_ANY_ID,
204                                                         &event_handler,
205                                                         NULL,
206                                                         &instance_any_id));
207 
208     ESP_ERROR_CHECK(esp_wifi_set_storage(WIFI_STORAGE_RAM) );
209     ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_NULL) );
210     ESP_ERROR_CHECK(esp_wifi_start() );
211     initialized = true;
212 }
213 
wifi_cmd_sta_join(const char * ssid,const char * pass)214 static bool wifi_cmd_sta_join(const char *ssid, const char *pass)
215 {
216     int bits = xEventGroupWaitBits(wifi_event_group, CONNECTED_BIT, 0, 1, 0);
217 
218     wifi_config_t wifi_config = { 0 };
219 
220     strlcpy((char *) wifi_config.sta.ssid, ssid, sizeof(wifi_config.sta.ssid));
221     if (pass) {
222         strlcpy((char *) wifi_config.sta.password, pass, sizeof(wifi_config.sta.password));
223     }
224 
225     if (bits & CONNECTED_BIT) {
226         s_reconnect = false;
227         xEventGroupClearBits(wifi_event_group, CONNECTED_BIT);
228         ESP_ERROR_CHECK( esp_wifi_disconnect() );
229         xEventGroupWaitBits(wifi_event_group, DISCONNECTED_BIT, 0, 1, portTICK_RATE_MS);
230     }
231 
232     s_reconnect = true;
233     s_retry_num = 0;
234     ESP_ERROR_CHECK( esp_wifi_set_mode(WIFI_MODE_STA) );
235     ESP_ERROR_CHECK( esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config) );
236     ESP_ERROR_CHECK( esp_wifi_connect() );
237 
238     xEventGroupWaitBits(wifi_event_group, CONNECTED_BIT, 0, 1, 5000 / portTICK_RATE_MS);
239 
240     return true;
241 }
242 
wifi_cmd_sta(int argc,char ** argv)243 static int wifi_cmd_sta(int argc, char **argv)
244 {
245     int nerrors = arg_parse(argc, argv, (void **) &sta_args);
246 
247     if (nerrors != 0) {
248         arg_print_errors(stderr, sta_args.end, argv[0]);
249         return 1;
250     }
251 
252     ESP_LOGI(TAG_STA, "sta connecting to '%s'", sta_args.ssid->sval[0]);
253     wifi_cmd_sta_join(sta_args.ssid->sval[0], sta_args.password->sval[0]);
254     return 0;
255 }
256 
wifi_perform_scan(const char * ssid,bool internal)257 static bool wifi_perform_scan(const char *ssid, bool internal)
258 {
259     wifi_scan_config_t scan_config = { 0 };
260     scan_config.ssid = (uint8_t *) ssid;
261     uint8_t i;
262 
263     ESP_ERROR_CHECK( esp_wifi_set_mode(WIFI_MODE_STA) );
264     ESP_ERROR_CHECK( esp_wifi_scan_start(&scan_config, true) );
265 
266     esp_wifi_scan_get_ap_num(&g_scan_ap_num);
267     if (g_scan_ap_num == 0) {
268         ESP_LOGI(TAG_STA, "No matching AP found");
269         return false;
270     }
271 
272     if (g_ap_list_buffer) {
273         free(g_ap_list_buffer);
274     }
275     g_ap_list_buffer = malloc(g_scan_ap_num * sizeof(wifi_ap_record_t));
276     if (g_ap_list_buffer == NULL) {
277         ESP_LOGE(TAG_STA, "Failed to malloc buffer to print scan results");
278         return false;
279     }
280 
281     if (esp_wifi_scan_get_ap_records(&g_scan_ap_num, (wifi_ap_record_t *)g_ap_list_buffer) == ESP_OK) {
282         if (!internal) {
283             for (i = 0; i < g_scan_ap_num; i++) {
284                 ESP_LOGI(TAG_STA, "[%s][rssi=%d]""%s", g_ap_list_buffer[i].ssid, g_ap_list_buffer[i].rssi,
285                          g_ap_list_buffer[i].ftm_responder ? "[FTM Responder]" : "");
286             }
287         }
288     }
289 
290     ESP_LOGI(TAG_STA, "sta scan done");
291 
292     return true;
293 }
294 
wifi_cmd_scan(int argc,char ** argv)295 static int wifi_cmd_scan(int argc, char **argv)
296 {
297     int nerrors = arg_parse(argc, argv, (void **) &scan_args);
298 
299     if (nerrors != 0) {
300         arg_print_errors(stderr, scan_args.end, argv[0]);
301         return 1;
302     }
303 
304     ESP_LOGI(TAG_STA, "sta start to scan");
305     if ( scan_args.ssid->count == 1 ) {
306         wifi_perform_scan(scan_args.ssid->sval[0], false);
307     } else {
308         wifi_perform_scan(NULL, false);
309     }
310     return 0;
311 }
312 
wifi_cmd_ap_set(const char * ssid,const char * pass)313 static bool wifi_cmd_ap_set(const char* ssid, const char* pass)
314 {
315     s_reconnect = false;
316     strlcpy((char*) g_ap_config.ap.ssid, ssid, MAX_SSID_LEN);
317     if (pass) {
318         if (strlen(pass) != 0 && strlen(pass) < 8) {
319             s_reconnect = true;
320             ESP_LOGE(TAG_AP, "password cannot be less than 8 characters long");
321             return false;
322         }
323         strlcpy((char*) g_ap_config.ap.password, pass, MAX_PASSPHRASE_LEN);
324     }
325 
326     if (strlen(pass) == 0) {
327         g_ap_config.ap.authmode = WIFI_AUTH_OPEN;
328     }
329 
330     ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_AP));
331     ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_AP, &g_ap_config));
332     return true;
333 }
334 
wifi_cmd_ap(int argc,char ** argv)335 static int wifi_cmd_ap(int argc, char** argv)
336 {
337     int nerrors = arg_parse(argc, argv, (void**) &ap_args);
338 
339     if (nerrors != 0) {
340         arg_print_errors(stderr, ap_args.end, argv[0]);
341         return 1;
342     }
343 
344     if (true == wifi_cmd_ap_set(ap_args.ssid->sval[0], ap_args.password->sval[0]))
345         ESP_LOGI(TAG_AP, "Starting SoftAP with FTM Responder support, SSID - %s, Password - %s", ap_args.ssid->sval[0], ap_args.password->sval[0]);
346     else
347         ESP_LOGE(TAG_AP, "Failed to start SoftAP!");
348 
349     return 0;
350 }
351 
wifi_cmd_query(int argc,char ** argv)352 static int wifi_cmd_query(int argc, char **argv)
353 {
354     wifi_config_t cfg;
355     wifi_mode_t mode;
356 
357     esp_wifi_get_mode(&mode);
358     if (WIFI_MODE_AP == mode) {
359         esp_wifi_get_config(WIFI_IF_AP, &cfg);
360         ESP_LOGI(TAG_AP, "AP mode, %s %s", cfg.ap.ssid, cfg.ap.password);
361     } else if (WIFI_MODE_STA == mode) {
362         int bits = xEventGroupWaitBits(wifi_event_group, CONNECTED_BIT, 0, 1, 0);
363         if (bits & CONNECTED_BIT) {
364             esp_wifi_get_config(WIFI_IF_STA, &cfg);
365             ESP_LOGI(TAG_STA, "sta mode, connected %s", cfg.ap.ssid);
366         } else {
367             ESP_LOGI(TAG_STA, "sta mode, disconnected");
368         }
369     } else {
370         ESP_LOGI(TAG_STA, "NULL mode");
371         return 0;
372     }
373 
374     return 0;
375 }
376 
find_ftm_responder_ap(const char * ssid)377 wifi_ap_record_t *find_ftm_responder_ap(const char *ssid)
378 {
379     bool retry_scan = false;
380     uint8_t i;
381 
382     if (!ssid)
383         return NULL;
384 
385 retry:
386     if (!g_ap_list_buffer || (g_scan_ap_num == 0)) {
387         ESP_LOGI(TAG_STA, "Scanning for %s", ssid);
388         if (false == wifi_perform_scan(ssid, true)) {
389             return NULL;
390         }
391     }
392 
393     for (i = 0; i < g_scan_ap_num; i++) {
394         if (strcmp((const char *)g_ap_list_buffer[i].ssid, ssid) == 0)
395             return &g_ap_list_buffer[i];
396     }
397 
398     if (!retry_scan) {
399         retry_scan = true;
400         if (g_ap_list_buffer) {
401             free(g_ap_list_buffer);
402             g_ap_list_buffer = NULL;
403         }
404         goto retry;
405     }
406 
407     ESP_LOGI(TAG_STA, "No matching AP found");
408 
409     return NULL;
410 }
411 
wifi_cmd_ftm(int argc,char ** argv)412 static int wifi_cmd_ftm(int argc, char **argv)
413 {
414     int nerrors = arg_parse(argc, argv, (void **) &ftm_args);
415     wifi_ap_record_t *ap_record;
416     EventBits_t bits;
417 
418     wifi_ftm_initiator_cfg_t ftmi_cfg = {
419         .frm_count = 32,
420         .burst_period = 2,
421     };
422 
423     if (nerrors != 0) {
424         arg_print_errors(stderr, ftm_args.end, argv[0]);
425         return 0;
426     }
427 
428     if (ftm_args.initiator->count != 0 && ftm_args.responder->count != 0) {
429         ESP_LOGE(TAG_STA, "Invalid FTM cmd argument");
430         return 0;
431     }
432 
433     if (ftm_args.responder->count != 0)
434         goto ftm_responder;
435 
436     bits = xEventGroupWaitBits(wifi_event_group, CONNECTED_BIT, 0, 1, 0);
437     if (bits & CONNECTED_BIT && !ftm_args.ssid->count) {
438         memcpy(ftmi_cfg.resp_mac, g_ap_bssid, ETH_ALEN);
439         ftmi_cfg.channel = g_ap_channel;
440     } else if (ftm_args.ssid->count == 1) {
441         ap_record = find_ftm_responder_ap(ftm_args.ssid->sval[0]);
442         if (ap_record) {
443             memcpy(ftmi_cfg.resp_mac, ap_record->bssid, 6);
444             ftmi_cfg.channel = ap_record->primary;
445         } else {
446             return 0;
447         }
448     } else {
449         ESP_LOGE(TAG_STA, "Provide SSID of the AP in disconnected state!");
450         return 0;
451     }
452 
453     if (ftm_args.frm_count->count != 0) {
454         uint8_t count = ftm_args.frm_count->ival[0];
455         if (count != 0 && count != 8 && count != 16 &&
456             count != 24 && count != 32 && count != 64) {
457             ESP_LOGE(TAG_STA, "Invalid Frame Count! Valid options are 0/8/16/24/32/64");
458             return 0;
459         }
460         ftmi_cfg.frm_count = count;
461     }
462 
463     if (ftm_args.burst_period->count != 0) {
464         if (ftm_args.burst_period->ival[0] >= 2 &&
465                 ftm_args.burst_period->ival[0] < 256) {
466             ftmi_cfg.burst_period = ftm_args.burst_period->ival[0];
467         } else {
468             ESP_LOGE(TAG_STA, "Invalid Burst Period! Valid range is 2-255");
469             return 0;
470         }
471     }
472 
473     ESP_LOGI(TAG_STA, "Requesting FTM session with Frm Count - %d, Burst Period - %dmSec (0: No Preference)",
474              ftmi_cfg.frm_count, ftmi_cfg.burst_period*100);
475 
476     if (ESP_OK != esp_wifi_ftm_initiate_session(&ftmi_cfg)) {
477         ESP_LOGE(TAG_STA, "Failed to start FTM session");
478         return 0;
479     }
480 
481     bits = xEventGroupWaitBits(ftm_event_group, FTM_REPORT_BIT | FTM_FAILURE_BIT,
482                                            pdTRUE, pdFALSE, portMAX_DELAY);
483     /* Processing data from FTM session */
484     if (bits & FTM_REPORT_BIT) {
485         ftm_process_report();
486         free(g_ftm_report);
487         g_ftm_report = NULL;
488         g_ftm_report_num_entries = 0;
489         ESP_LOGI(TAG_STA, "Estimated RTT - %d nSec, Estimated Distance - %d.%02d meters",
490                           g_rtt_est, g_dist_est / 100, g_dist_est % 100);
491     } else {
492         /* Failure case */
493     }
494 
495     return 0;
496 
497 ftm_responder:
498     if (ftm_args.offset->count != 0) {
499         int16_t offset_cm = ftm_args.offset->ival[0];
500 
501         esp_wifi_ftm_resp_set_offset(offset_cm);
502     }
503 
504     if (ftm_args.enable->count != 0) {
505         if (!g_ap_started) {
506             ESP_LOGE(TAG_AP, "Start the SoftAP first with 'ap' command");
507             return 0;
508         }
509         g_ap_config.ap.ftm_responder = true;
510         ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_AP, &g_ap_config));
511         ESP_LOGI(TAG_AP, "Re-starting SoftAP with FTM Responder enabled");
512 
513         return 0;
514     }
515 
516     if (ftm_args.disable->count != 0) {
517         if (!g_ap_started) {
518             ESP_LOGE(TAG_AP, "Start the SoftAP first with 'ap' command");
519             return 0;
520         }
521         g_ap_config.ap.ftm_responder = false;
522         ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_AP, &g_ap_config));
523         ESP_LOGI(TAG_AP, "Re-starting SoftAP with FTM Responder disabled");
524     }
525 
526     return 0;
527 }
528 
register_wifi(void)529 void register_wifi(void)
530 {
531     sta_args.ssid = arg_str1(NULL, NULL, "<ssid>", "SSID of AP");
532     sta_args.password = arg_str0(NULL, NULL, "<pass>", "password of AP");
533     sta_args.end = arg_end(2);
534 
535     const esp_console_cmd_t sta_cmd = {
536         .command = "sta",
537         .help = "WiFi is station mode, join specified soft-AP",
538         .hint = NULL,
539         .func = &wifi_cmd_sta,
540         .argtable = &sta_args
541     };
542 
543     ESP_ERROR_CHECK( esp_console_cmd_register(&sta_cmd) );
544 
545     ap_args.ssid = arg_str1(NULL, NULL, "<ssid>", "SSID of AP");
546     ap_args.password = arg_str0(NULL, NULL, "<pass>", "password of AP");
547     ap_args.end = arg_end(2);
548 
549     const esp_console_cmd_t ap_cmd = {
550         .command = "ap",
551         .help = "AP mode, configure ssid and password",
552         .hint = NULL,
553         .func = &wifi_cmd_ap,
554         .argtable = &ap_args
555     };
556 
557     ESP_ERROR_CHECK( esp_console_cmd_register(&ap_cmd) );
558 
559     scan_args.ssid = arg_str0(NULL, NULL, "<ssid>", "SSID of AP want to be scanned");
560     scan_args.end = arg_end(1);
561 
562     const esp_console_cmd_t scan_cmd = {
563         .command = "scan",
564         .help = "WiFi is station mode, start scan ap",
565         .hint = NULL,
566         .func = &wifi_cmd_scan,
567         .argtable = &scan_args
568     };
569 
570     ESP_ERROR_CHECK( esp_console_cmd_register(&scan_cmd) );
571 
572     const esp_console_cmd_t query_cmd = {
573         .command = "query",
574         .help = "query WiFi info",
575         .hint = NULL,
576         .func = &wifi_cmd_query,
577     };
578     ESP_ERROR_CHECK( esp_console_cmd_register(&query_cmd) );
579 
580     /* FTM Initiator commands */
581     ftm_args.initiator = arg_lit0("I", "ftm_initiator", "FTM Initiator mode");
582     ftm_args.ssid = arg_str0("s", "ssid", "SSID", "SSID of AP");
583     ftm_args.frm_count = arg_int0("c", "frm_count", "<0/8/16/24/32/64>", "FTM frames to be exchanged (0: No preference)");
584     ftm_args.burst_period = arg_int0("p", "burst_period", "<2-255 (x 100 mSec)>", "Periodicity of FTM bursts in 100's of miliseconds (0: No preference)");
585     /* FTM Responder commands */
586     ftm_args.responder = arg_lit0("R", "ftm_responder", "FTM Responder mode");
587     ftm_args.enable = arg_lit0("e", "enable", "Restart SoftAP with FTM enabled");
588     ftm_args.disable = arg_lit0("d", "disable", "Restart SoftAP with FTM disabled");
589     ftm_args.offset = arg_int0("o", "offset", "Offset in cm", "T1 offset in cm for FTM Responder");
590     ftm_args.end = arg_end(1);
591 
592     const esp_console_cmd_t ftm_cmd = {
593         .command = "ftm",
594         .help = "FTM command",
595         .hint = NULL,
596         .func = &wifi_cmd_ftm,
597         .argtable = &ftm_args
598     };
599 
600     ESP_ERROR_CHECK( esp_console_cmd_register(&ftm_cmd) );
601 }
602 
app_main(void)603 void app_main(void)
604 {
605     esp_err_t ret = nvs_flash_init();
606     if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
607         ESP_ERROR_CHECK(nvs_flash_erase());
608         ret = nvs_flash_init();
609     }
610     ESP_ERROR_CHECK( ret );
611 
612     initialise_wifi();
613 
614     esp_console_repl_t *repl = NULL;
615     esp_console_repl_config_t repl_config = ESP_CONSOLE_REPL_CONFIG_DEFAULT();
616     esp_console_dev_uart_config_t uart_config = ESP_CONSOLE_DEV_UART_CONFIG_DEFAULT();
617     repl_config.prompt = "ftm>";
618     // init console REPL environment
619     ESP_ERROR_CHECK(esp_console_new_repl_uart(&uart_config, &repl_config, &repl));
620     /* Register commands */
621     register_system();
622     register_wifi();
623 
624     printf("\n ==========================================================\n");
625     printf(" |                      Steps to test FTM                 |\n");
626     printf(" |                                                        |\n");
627     printf(" |  1. Use 'help' for detailed information on parameters  |\n");
628     printf(" |  2. Start SoftAP with command 'ap <SSID> <password>'   |\n");
629     printf(" |                          OR                            |\n");
630     printf(" |  2. Use 'scan' command to search for external AP's     |\n");
631     printf(" |  3. On second device initiate FTM with an AP using     |\n");
632     printf(" |     command 'ftm -I -s <SSID>'                         |\n");
633     printf(" ==========================================================\n\n");
634 
635     // start console REPL
636     ESP_ERROR_CHECK(esp_console_start_repl(repl));
637 }
638