From acb1143409ae0ad80d21f9af8a23fe01728e3d1f Mon Sep 17 00:00:00 2001 From: Nachiket Kukade Date: Thu, 21 Jan 2021 19:55:39 +0530 Subject: [PATCH] Add support for FTM operation Add FTM support for below configuration - 1. Station(connected) as Initiator with AP as responder 2. SoftAP as responder with the connected Station Added Station example with runtime FTM configurations. --- components/esp_event/event_send.c | 3 + components/esp_event/event_send_compat.inc | 5 + .../esp_event/include/esp_event_legacy.h | 5 + components/esp_wifi/Kconfig | 37 ++ .../esp_wifi/include/esp_private/wifi.h | 23 + components/esp_wifi/include/esp_wifi.h | 14 + components/esp_wifi/include/esp_wifi_types.h | 34 +- components/esp_wifi/src/wifi_init.c | 25 + docs/en/api-guides/wifi.rst | 22 +- examples/wifi/README.md | 6 + examples/wifi/ftm/CMakeLists.txt | 8 + examples/wifi/ftm/README.md | 119 +++++ examples/wifi/ftm/main/CMakeLists.txt | 2 + examples/wifi/ftm/main/ftm_station_main.c | 442 ++++++++++++++++++ 14 files changed, 743 insertions(+), 2 deletions(-) create mode 100644 examples/wifi/ftm/CMakeLists.txt create mode 100644 examples/wifi/ftm/README.md create mode 100644 examples/wifi/ftm/main/CMakeLists.txt create mode 100644 examples/wifi/ftm/main/ftm_station_main.c diff --git a/components/esp_event/event_send.c b/components/esp_event/event_send.c index 334be196a3..62c0513952 100644 --- a/components/esp_event/event_send.c +++ b/components/esp_event/event_send.c @@ -91,6 +91,9 @@ static system_event_id_t esp_event_legacy_wifi_event_id(int32_t event_id) case WIFI_EVENT_ROC_DONE: return SYSTEM_EVENT_ROC_DONE; + case WIFI_EVENT_FTM_REPORT: + return SYSTEM_EVENT_FTM_REPORT; + default: ESP_LOGE(TAG, "invalid wifi event id %d", event_id); return SYSTEM_EVENT_MAX; diff --git a/components/esp_event/event_send_compat.inc b/components/esp_event/event_send_compat.inc index 78fbb14b65..d8381f36c1 100644 --- a/components/esp_event/event_send_compat.inc +++ b/components/esp_event/event_send_compat.inc @@ -70,6 +70,7 @@ esp_err_t esp_event_send_to_default_loop(system_event_t *event) HANDLE_SYS_EVENT_ARG(WIFI, SCAN_DONE, scan_done); HANDLE_SYS_EVENT(WIFI, STA_START); HANDLE_SYS_EVENT(WIFI, STA_STOP); + HANDLE_SYS_EVENT_ARG(WIFI, FTM_REPORT, ftm_report); /* STA events */ HANDLE_SYS_EVENT_ARG(WIFI, STA_CONNECTED, connected); @@ -207,6 +208,10 @@ static void esp_system_event_debug(const system_event_t* event) ESP_LOGD(TAG, "SYSTEM_EVENT_STA_AUTHMODE_CHNAGE, old_mode:%d, new_mode:%d", auth_change->old_mode, auth_change->new_mode); break; } + case SYSTEM_EVENT_FTM_REPORT: { + ESP_LOGD(TAG, "SYSTEM_EVENT_FTM_REPORT"); + break; + } case SYSTEM_EVENT_STA_GOT_IP: { const system_event_sta_got_ip_t *got_ip = &event->event_info.got_ip; ESP_LOGD(TAG, "SYSTEM_EVENT_STA_GOT_IP, ip:" IPSTR ", mask:" IPSTR ", gw:" IPSTR, diff --git a/components/esp_event/include/esp_event_legacy.h b/components/esp_event/include/esp_event_legacy.h index e307d8e60e..ddcb194a6b 100644 --- a/components/esp_event/include/esp_event_legacy.h +++ b/components/esp_event/include/esp_event_legacy.h @@ -50,6 +50,7 @@ typedef enum { SYSTEM_EVENT_AP_PROBEREQRECVED, /*!< Receive probe request packet in soft-AP interface */ SYSTEM_EVENT_ACTION_TX_STATUS, /*!< Receive status of Action frame transmitted */ SYSTEM_EVENT_ROC_DONE, /*!< Indicates the completion of Remain-on-Channel operation status */ + SYSTEM_EVENT_FTM_REPORT, /*!< Receive report of FTM procedure */ SYSTEM_EVENT_GOT_IP6, /*!< ESP32 station or ap or ethernet interface v6IP addr is preferred */ SYSTEM_EVENT_ETH_START, /*!< ESP32 ethernet start */ SYSTEM_EVENT_ETH_STOP, /*!< ESP32 ethernet stop */ @@ -95,6 +96,9 @@ typedef wifi_event_ap_stadisconnected_t system_event_ap_stadisconnected_t; /** Argument structure of event */ typedef wifi_event_ap_probe_req_rx_t system_event_ap_probe_req_rx_t; +/** Argument structure of SYSTEM_EVENT_FTM_REPORT event */ +typedef wifi_event_ftm_report_t system_event_ftm_report_t; + /** Argument structure of event */ typedef ip_event_ap_staipassigned_t system_event_ap_staipassigned_t; @@ -117,6 +121,7 @@ typedef union { system_event_ap_staconnected_t sta_connected; /*!< a station connected to ESP32 soft-AP */ system_event_ap_stadisconnected_t sta_disconnected; /*!< a station disconnected to ESP32 soft-AP */ system_event_ap_probe_req_rx_t ap_probereqrecved; /*!< ESP32 soft-AP receive probe request packet */ + system_event_ftm_report_t ftm_report; /*!< Report of FTM procedure */ system_event_ap_staipassigned_t ap_staipassigned; /**< ESP32 soft-AP assign an IP to the station*/ system_event_got_ip6_t got_ip6; /*!< ESP32 station or ap or ethernet ipv6 addr state change to preferred */ } system_event_info_t; diff --git a/components/esp_wifi/Kconfig b/components/esp_wifi/Kconfig index e72afb2e09..8cfe901fec 100644 --- a/components/esp_wifi/Kconfig +++ b/components/esp_wifi/Kconfig @@ -331,6 +331,43 @@ menu "Wi-Fi" If neither of them are enabled, the other 7.4KB IRAM memory would be taken by this option. Wi-Fi power-save mode average current would be reduced if this option is enabled. + config ESP32S2_WIFI_FTM_INITIATOR_SUPPORT + bool "FTM Initiator support" + default y + depends on IDF_TARGET_ESP32S2 + + config ESP32S2_WIFI_FTM_REPORT_LOG_ENABLE + bool "FTM Report logging" + default n + depends on ESP32S2_WIFI_FTM_INITIATOR_SUPPORT + help + Select this option to get a detailed report of FTM Procedure with raw values + + config ESP32S2_WIFI_FTM_REPORT_SHOW_RTT + depends on ESP32S2_WIFI_FTM_REPORT_LOG_ENABLE + bool "Show RTT values" + default y + + config ESP32S2_WIFI_FTM_REPORT_SHOW_DIAG + depends on ESP32S2_WIFI_FTM_REPORT_LOG_ENABLE + bool "Show dialog tokens" + default y + + config ESP32S2_WIFI_FTM_REPORT_SHOW_T1T2T3T4 + depends on ESP32S2_WIFI_FTM_REPORT_LOG_ENABLE + bool "Show T1 to T4" + default y + + config ESP32S2_WIFI_FTM_REPORT_SHOW_RSSI + depends on ESP32S2_WIFI_FTM_REPORT_LOG_ENABLE + bool "Show RSSI levels" + default y + + config ESP32S2_WIFI_FTM_RESPONDER_SUPPORT + bool "FTM Responder support" + default y + depends on IDF_TARGET_ESP32S2 + endmenu # Wi-Fi menu "PHY" diff --git a/components/esp_wifi/include/esp_private/wifi.h b/components/esp_wifi/include/esp_private/wifi.h index 5f10f9b765..c0df0d2749 100644 --- a/components/esp_wifi/include/esp_private/wifi.h +++ b/components/esp_wifi/include/esp_private/wifi.h @@ -72,6 +72,17 @@ typedef enum { WIFI_LOG_MODULE_MESH, /*logs related to Mesh*/ } wifi_log_module_t; +/** + * @brief FTM Report log levels configuration + * + */ +typedef struct { + uint8_t show_rtt:1; /**< Display all valid Round-Trip-Time readings for FTM frames */ + uint8_t show_diag:1; /**< Display dialogue tokens for all FTM frames with valid readings */ + uint8_t show_t1t2t3t4:1;/**< Display all valid T1, T2, T3, T4 readings considered while calculating RTT */ + uint8_t show_rxrssi:1; /**< Display RSSI for each FTM frame with valid readings */ +} ftm_report_log_level_t; + /** * @brief WiFi log submodule definition * @@ -558,6 +569,18 @@ esp_err_t esp_wifi_internal_set_spp_amsdu(wifi_interface_t ifidx, bool spp_cap, * */ void esp_wifi_internal_optimize_wake_ahead_time(void); + +/** + * @brief Set FTM Report log level + * + * @param log_lvl Log levels configuration + * + * @return + * - ESP_OK: succeed + * - ESP_ERR_NOT_SUPPORTED: No FTM support + */ +esp_err_t esp_wifi_set_ftm_report_log_level(ftm_report_log_level_t *log_lvl); + #ifdef __cplusplus } #endif diff --git a/components/esp_wifi/include/esp_wifi.h b/components/esp_wifi/include/esp_wifi.h index a893ce0453..2df46515a0 100644 --- a/components/esp_wifi/include/esp_wifi.h +++ b/components/esp_wifi/include/esp_wifi.h @@ -203,6 +203,8 @@ extern uint64_t g_wifi_feature_caps; #define CONFIG_FEATURE_WPA3_SAE_BIT (1<<0) #define CONFIG_FEATURE_CACHE_TX_BUF_BIT (1<<1) +#define CONFIG_FEATURE_FTM_INITIATOR_BIT (1<<2) +#define CONFIG_FEATURE_FTM_RESPONDER_BIT (1<<3) #define WIFI_INIT_CONFIG_DEFAULT() { \ .event_handler = &esp_event_send_internal, \ @@ -1153,6 +1155,18 @@ esp_err_t esp_wifi_statis_dump(uint32_t modules); */ esp_err_t esp_wifi_set_rssi_threshold(int32_t rssi); +/** + * @brief Start FTM Initiator session + * If successful, event WIFI_EVENT_FTM_REPORT is generated with the result of the FTM procedure + * + * @param cfg FTM Initiator configurations + * + * @return + * - ESP_OK: succeed + * - others: failed + */ +esp_err_t esp_wifi_ftm_start_initiator(wifi_ftm_initiator_cfg_t *cfg); + #ifdef __cplusplus } #endif diff --git a/components/esp_wifi/include/esp_wifi_types.h b/components/esp_wifi/include/esp_wifi_types.h index 238e90a8aa..4155409555 100644 --- a/components/esp_wifi/include/esp_wifi_types.h +++ b/components/esp_wifi/include/esp_wifi_types.h @@ -178,7 +178,9 @@ typedef struct { uint32_t phy_11n:1; /**< bit: 2 flag to identify if 11n mode is enabled or not */ uint32_t phy_lr:1; /**< bit: 3 flag to identify if low rate is enabled or not */ uint32_t wps:1; /**< bit: 4 flag to identify if WPS is supported or not */ - uint32_t reserved:27; /**< bit: 5..31 reserved */ + uint32_t ftm_responder:1; /**< bit: 5 flag to identify if FTM is supported in responder mode */ + uint32_t ftm_initiator:1; /**< bit: 6 flag to identify if FTM is supported in initiator mode */ + uint32_t reserved:25; /**< bit: 7..31 reserved */ wifi_country_t country; /**< country information of AP */ } wifi_ap_record_t; @@ -515,6 +517,15 @@ typedef struct { uint8_t data[0]; /**< Appended Data payload */ } wifi_action_tx_req_t; +/** + * @brief FTM Initiator configuration + * + */ +typedef struct { + uint8_t frm_count; /**< No. of FTM frames requested in terms of 4 or 8 bursts (allowed values - 0(No pref), 16, 24, 32, 64) */ + uint16_t burst_period; /**< Requested time period between consecutive FTM bursts in 100's of milliseconds (0 - No pref) */ +} wifi_ftm_initiator_cfg_t; + /** * @brief WiFi PHY rate encodings * @@ -672,6 +683,27 @@ typedef struct { int32_t rssi; /**< RSSI value of bss */ } wifi_event_bss_rssi_low_t; +/** + * @brief FTM operation status types + * + */ +typedef enum { + FTM_STATUS_SUCCESS = 0, /**< FTM exchange is successful */ + FTM_STATUS_UNSUPPORTED, /**< Peer does not support FTM */ + FTM_STATUS_CONF_REJECTED, /**< Peer rejected FTM configuration in FTM Request */ + FTM_STATUS_NO_RESPONSE, /**< Peer did not respond to FTM Requests */ + FTM_STATUS_FAIL, /**< Unknown error during FTM exchange */ +} wifi_ftm_status_t; + +/** Argument structure for WIFI_EVENT_FTM_REPORT event */ +typedef struct { + uint8_t peer_mac[6]; /**< MAC address of the FTM Peer */ + wifi_ftm_status_t status; /**< Status of the FTM operation */ + uint32_t rtt_raw; /**< Raw average Round-Trip-Time with peer in Nano-Seconds */ + uint32_t rtt_est; /**< Estimated Round-Trip-Time with peer in Nano-Seconds */ + uint32_t dist_est; /**< Estimated one-way distance in Centi-Meters */ +} wifi_event_ftm_report_t; + #define WIFI_STATIS_BUFFER (1<<0) #define WIFI_STATIS_RXTX (1<<1) #define WIFI_STATIS_HW (1<<2) diff --git a/components/esp_wifi/src/wifi_init.c b/components/esp_wifi/src/wifi_init.c index 5a5a5ffcdb..353345d406 100644 --- a/components/esp_wifi/src/wifi_init.c +++ b/components/esp_wifi/src/wifi_init.c @@ -56,6 +56,12 @@ uint64_t g_wifi_feature_caps = #if (CONFIG_ESP32_SPIRAM_SUPPORT | CONFIG_ESP32S2_SPIRAM_SUPPORT) CONFIG_FEATURE_CACHE_TX_BUF_BIT | #endif +#if CONFIG_ESP32S2_WIFI_FTM_INITIATOR_SUPPORT + CONFIG_FEATURE_FTM_INITIATOR_BIT | +#endif +#if CONFIG_ESP32S2_WIFI_FTM_RESPONDER_SUPPORT + CONFIG_FEATURE_FTM_RESPONDER_BIT | +#endif 0; static bool s_wifi_adc_xpd_flag; @@ -248,6 +254,25 @@ esp_err_t esp_wifi_init(const wifi_init_config_t *config) } } adc2_cal_include(); //This enables the ADC2 calibration constructor at start up. + +#if CONFIG_IDF_TARGET_ESP32S2 +#ifdef CONFIG_ESP32S2_WIFI_FTM_REPORT_LOG_ENABLE + ftm_report_log_level_t log_lvl = {0}; +#ifdef CONFIG_ESP32S2_WIFI_FTM_REPORT_SHOW_RTT + log_lvl.show_rtt = 1; +#endif +#ifdef CONFIG_ESP32S2_WIFI_FTM_REPORT_SHOW_DIAG + log_lvl.show_diag = 1; +#endif +#ifdef CONFIG_ESP32S2_WIFI_FTM_REPORT_SHOW_T1T2T3T4 + log_lvl.show_t1t2t3t4 = 1; +#endif +#ifdef CONFIG_ESP32S2_WIFI_FTM_REPORT_SHOW_RSSI + log_lvl.show_rxrssi = 1; +#endif + esp_wifi_set_ftm_report_log_level(&log_lvl); +#endif +#endif esp_wifi_config_info(); return result; } diff --git a/docs/en/api-guides/wifi.rst b/docs/en/api-guides/wifi.rst index 3a23ea7735..23a2954650 100644 --- a/docs/en/api-guides/wifi.rst +++ b/docs/en/api-guides/wifi.rst @@ -1459,6 +1459,26 @@ For establishing a secure connection, AP and Station negotiate and agree on the Detailed information on creating certificates and how to run wpa2_enterprise example on {IDF_TARGET_NAME} can be found in :example:`wifi/wpa2_enterprise`. +.. only:: esp32s2 + +Wi-Fi Location +------------------------------- + +Wi-Fi Location will improve the accuracy of a device's location data beyond the Access Point, which will enable creation of new, feature-rich applications and services such as geo-fencing, network management, navigation and others. One of the protocols used to determine the device location with respect to the Access Point is Fine Timing Measurement which calculates Time-of-Flight of a WiFi frame. + +Fine Timing Measurement (FTM) ++++++++++++++++++++++++++++++ + +FTM is used to measure Wi-Fi Round Trip Time (Wi-Fi RTT) which is the time a WiFi signal takes to travel from a device to another device and back again. Using WiFi RTT the distance between the devices can be calculated with a simple formula of `RTT * c / 2`, where c is the speed of light. +FTM uses timestamps given by WiFi interface hardware at the time of arrival or departure of frames exchanged between a pair of devices. One entity called FTM Initiator (mostly a Station device) discovers the FTM Responder (can be a Station or an Access Point) and negotiates to start an FTM procedure. The procedure uses multiple Action frames sent in bursts and its ACK's to gather the timestamps data. FTM Initiator gathers the data in the end to calculate an average Round-Trip-Time. +{IDF_TARGET_NAME} supports FTM in below configuration: + + - {IDF_TARGET_NAME} as FTM Initiator in Station mode with the associated AP acting as FTM Responder. + - {IDF_TARGET_NAME} as FTM Responder in SoftAP mode with an associated Station acting as FTM Initiator. + +Distance measurement using RTT is not accurate, factors such as RF interference, multi-path travel, antenna orientation and lack of calibration increase these inaccuracies. For better results it is suggested to perform FTM between two {IDF_TARGET_NAME} devices as Station and SoftAP. +Refer to IDF example :idf_file:`examples/wifi/ftm/README.md` for steps on how to setup and perform FTM. + {IDF_TARGET_NAME} Wi-Fi Power-saving Mode ----------------------------------------- @@ -2318,4 +2338,4 @@ Please refer to a separate document with :doc:`wireshark-user-guide`. .. toctree:: :hidden: - wireshark-user-guide \ No newline at end of file + wireshark-user-guide diff --git a/examples/wifi/README.md b/examples/wifi/README.md index 0aab2e2dc2..9879ea3efc 100644 --- a/examples/wifi/README.md +++ b/examples/wifi/README.md @@ -38,6 +38,12 @@ Show how to scan for all the available APs. See the [README.md](./scan/README.md) file in the project [scan](./scan/). +## FTM(Fine Timing Measurement) + +Shows how to use FTM(Fine Timing Measurement). + +See the [README.md](./ftm/README.md) file in the project [ftm](./ftm/). + # More See the [README.md](../README.md) file in the upper level [examples](../) directory for more information about examples. diff --git a/examples/wifi/ftm/CMakeLists.txt b/examples/wifi/ftm/CMakeLists.txt new file mode 100644 index 0000000000..acb3c076d2 --- /dev/null +++ b/examples/wifi/ftm/CMakeLists.txt @@ -0,0 +1,8 @@ +# The following lines of boilerplate have to be in your project's CMakeLists +# in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.5) + +set(EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/system/console/components) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(ftm) diff --git a/examples/wifi/ftm/README.md b/examples/wifi/ftm/README.md new file mode 100644 index 0000000000..c1cae5c79d --- /dev/null +++ b/examples/wifi/ftm/README.md @@ -0,0 +1,119 @@ +| Supported Targets | ESP32-S2 | +| ----------------- | -------- | + +# FTM Example + +(See the README.md file in the upper level 'examples' directory for more information about examples.) + +## Introduction +One of the ways in which WiFi enabled devices can measure their distance to the Access Point is by measuring Wi-Fi Round Trip Time (Wi-Fi RTT). Wi-Fi RTT is the time a WiFi signal takes to travel from Station to an AP. This time is proportional to the actual distance between them. Given the RTT, the distance can be calculated with below simple formula - + +> distance = RTT * c / 2 +> (Where c is the speed of light) + +Wi-Fi RTT is calculated using a procedure called Fine Timing Measurement(FTM). During FTM procedure, a burst of Action frames is transmitted by one device(FTM Responder) to another(FTM Initiator) and each of them is ACK'ed. Hardware in both the devices mark time-of-arrival (TOA) and time-of-departure (TOD) of both Action frame and its ACK. In the end, the FTM Initiator collects the data for all pairs of Action frame and ACK and calculates RTT for each pair with below formula - + +> RTT[i] = (T4[i] - T1[i]) - (T3[i] - T2[i]) Where +> T1[i] : TOD of i'th Action frame from Responder +> T2[i] : TOA of i'th Action frame at Initiator +> T3[i] : TOD of i'th ACK from Initiator +> T4[i] : TOA of i'th ACK at Responder + +Average RTT is calculated over all such pairs to get a more accurate result. +In this example, FTM procedure is supported only between a Station(ESP32S2) and an AP it is connected to. The AP can be a SoftAP(ESP32S2) or an External AP that supports FTM Responder mode. + +## How to use example + +With this example, users can scan for AP's that support FTM Responder role, connect with them and perform FTM procedure with different configurations. Below steps show how to do this using 2 ESP32-S2's in Station and SoftAP mode. +First make sure that FTM Initiator support on Station and FTM Responder support on SoftAP is enabled in the example configuration menu. For this, open project configuration menu (`idf.py menuconfig`), navigate to `Component config -> Wi-Fi` and check `FTM Initiator support` on Station build and `FTM Responder support` on SoftAP build. Furthermore for getting a per frame detailed report of the FTM procedure, enable `FTM Report logging` option. +Build and flash the example on respective ESP32-S2's to see below output - + +```bash + ========================================================== + | Steps to test FTM | + | | + | 1. Print 'help' to gain overview of commands | + | 2. Use 'scan' command for AP that support FTM | + | OR | + | 2. Start SoftAP on another ESP32S2 with 'ap' command | + | 3. Setup connection with the AP using 'sta' command | + | 4. Initiate FTM from Station using 'ftm -I' command | + | | + ========================================================== + +ftm> +``` + +Use `help` to get a list of available commands and options. Use `scan` command to scan for AP's that support FTM Responder mode. +Before testing FTM with an external AP, make sure that `FTM Responder` is visible in the respective scan result entry. + +```bash +ftm> scan +I (476765) ftm_station: sta start to scan +ftm> I (478805) ftm_station: [Abeeys Palace][rssi=84] +I (478805) ftm_station: [privateproperty][rssi=76] +I (478805) ftm_station: [C904][rssi=69] +I (478815) ftm_station: [FTM][rssi=-94][FTM Responder] +I (478815) ftm_station: [Velop][rssi=-115] +I (478825) ftm_station: sta scan done +``` + +AP's that support FTM Responder mode can be seen in the scan results. Or setup a SoftAP using another ESP32-S2 device using the `ap` command - + +```bash +ftm> ap FTM password +I (91271) ftm_ap: Starting SoftAP with FTM Responder support, SSID - FTM, Password - password +ftm> +``` + +Use command `sta []` to connect with an eligible AP. Then simply issue `ftm -I` to initiate a session with default configuration of 32 FTM frames. For more configurations below options are available - +`ftm [-I] [-c <0/16/24/32/64>] [-p <0-255 (x 100 mSec)>]` +Where - +* `-I` OR `--ftm_initiator`: FTM Initiator mode +* `-c` OR `--frm_count`: FTM frames to be exchanged (Valid values: 0=No preference, 16, 24, 32, 64, default: 32) +* `-p` OR `--burst_period`: Periodicity of FTM bursts in 100's of miliseconds (0: No preference, default: 2) + +Currently FTM is only supported in below configuration - + 1. Station(ESP32-S2) as Initiator in connected mode with SoftAP(ESP32-S2) as Responder + 2. Station(ESP32-S2) as Initiator in connected mode with external AP as Responder +The first option should be preferred since ESP32-S2 is self calibrated for high resolution measurement. Support for more configurations like STA to STA with ASAP mode will follow in future updates. + +## Example Output +Example output of an FTM Procedure - + +```bash +ftm> ftm -I +I (13796) ftm_station: Starting FTM Initiator with Frm Count 32, Burst Period - 200mSec +ftm> W (23696) wifi:FTM report: +W (23696) wifi:| Diag | RTT | RSSI | T1 | T2 | T3 | T4 | +W (23706) wifi:| 3| 24850 | -18 |13598650592600 | 3973101843750 | 3973205662500 |13598754436200 | +W (23716) wifi:| 5| 32662 | -18 |13600546592600 | 3974997826562 | 3975101662500 |13600650461200 | +W (23726) wifi:| 7| 31100 | -18 |14498977692600 | 4873420043750 | 4873523662500 |14499081342450 | +W (23736) wifi:| 9| 29412 | -18 |14500856692600 | 4875299026562 | 4875402662500 |14500960357950 | +W (23746) wifi:| 11| 24850 | -18 |15399235817600 | 5773669262500 | 5773785662500 |15399352242450 | +W (23756) wifi:| 12| 27975 | -18 |15400258817600 | 5774692253125 | 5774796662500 |15400363254950 | +W (23766) wifi:| 13| 26287 | -18 |15401215817600 | 5775649242187 | 5775753662500 |15401320264200 | +W (23776) wifi:| 14| 24725 | -18 |15402158817600 | 5776592234375 | 5776696662500 |15402263270450 | +W (23786) wifi:| 15| 24850 | -18 |16298632917600 | 6673057456250 | 6673173662500 |16298749148700 | +W (23796) wifi:| 16| 29412 | -18 |16299698917600 | 6674123445312 | 6674227662500 |16299803164200 | +W (23796) wifi:| 17| 26287 | -18 |16300637917600 | 6675062435937 | 6675166662500 |16300742170450 | +W (23816) wifi:| 18| 27975 | -18 |16301580917600 | 6676005428125 | 6676109662500 |16301685179950 | +W (23826) wifi:| 19| 27850 | -18 |17198977042600 | 862506262500 | 862622262500 |17199093070450 | +W (23836) wifi:| 20| 23162 | -18 |17200051042600 | 863580251562 | 863684262500 |17200155076700 | +W (23846) wifi:| 21| 31100 | -18 |17200991042600 | 864520243750 | 864624262500 |17201095092450 | +W (23846) wifi:| 22| 26412 | -18 |17201930042600 | 865459232812 | 865563262500 |17202034098700 | +W (23856) wifi:| 23| 31100 | -18 |18099333142600 | 1762853443750 | 1762969262500 |18099448992450 | +W (23876) wifi:| 24| 23162 | -18 |18100397142600 | 1763917432812 | 1764021262500 |18100500995450 | +W (23886) wifi:| 25| 26287 | -18 |18101336142600 | 1764856423437 | 1764960262500 |18101440007950 | +W (23896) wifi:| 26| 24850 | -18 |18102343142600 | 1765863412500 | 1765967262500 |18102447017450 | +W (23896) wifi:| 27| 31100 | -18 |19000187242600 | 2663698618750 | 2663814262500 |19000302917450 | +W (23906) wifi:| 28| 24725 | -18 |19001274242600 | 2664785609375 | 2664889262500 |19001377920450 | +W (23916) wifi:| 29| 31100 | -18 |19002225242600 | 2665736600000 | 2665840262500 |19002328936200 | +W (23936) wifi:| 31| 23287 | -19 |19004133242600 | 2667644579687 | 2667748262500 |19004236948700 | +W (23936) wifi:| 32| 26287 | -18 |19899976367600 | 3563478835937 | 3563595262500 |19900092820450 | +W (23946) wifi:FTM session ends with 25 valid readings out of 31, Avg raw RTT: 27.232 nSec, Avg RSSI: -18 +I (23956) ftm_station: Estimated RTT - 13 nSec, Estimated Distance - 1.95 meters +``` + +The final statement gives the average calculated RTT along with an estimated distance between the Station and the AP. This distance is measured by first adjusting the RTT with any physical analog delays and a calibration delta. Distances measured using RTT are not perfectly accurate, and are subjected to various errors like RF interference, multi-path, path loss, orientations etc. +The design requires line-of-sight with straightforward propagation path with no less than -70dBm RSSI for better results. diff --git a/examples/wifi/ftm/main/CMakeLists.txt b/examples/wifi/ftm/main/CMakeLists.txt new file mode 100644 index 0000000000..9713164e27 --- /dev/null +++ b/examples/wifi/ftm/main/CMakeLists.txt @@ -0,0 +1,2 @@ +idf_component_register(SRCS "ftm_station_main.c" + INCLUDE_DIRS ".") diff --git a/examples/wifi/ftm/main/ftm_station_main.c b/examples/wifi/ftm/main/ftm_station_main.c new file mode 100644 index 0000000000..69f7127538 --- /dev/null +++ b/examples/wifi/ftm/main/ftm_station_main.c @@ -0,0 +1,442 @@ +/* Wi-Fi FTM Example + + This example code is in the Public Domain (or CC0 licensed, at your option.) + + Unless required by applicable law or agreed to in writing, this + software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. +*/ + +#include +#include +#include +#include +#include "nvs_flash.h" +#include "cmd_system.h" +#include "argtable3/argtable3.h" +#include "freertos/FreeRTOS.h" +#include "freertos/event_groups.h" +#include "esp_event.h" +#include "esp_log.h" +#include "esp_err.h" +#include "esp_wifi.h" +#include "esp_console.h" + +typedef struct { + struct arg_str *ssid; + struct arg_str *password; + struct arg_end *end; +} wifi_args_t; + +typedef struct { + struct arg_str *ssid; + struct arg_end *end; +} wifi_scan_arg_t; + +typedef struct { + struct arg_lit *mode; + struct arg_int *frm_count; + struct arg_int *burst_period; + struct arg_end *end; +} wifi_ftm_args_t; + +static wifi_args_t sta_args; +static wifi_args_t ap_args; +static wifi_scan_arg_t scan_args; +static wifi_ftm_args_t ftm_args; + +static bool s_reconnect = true; +static const char *TAG_STA = "ftm_station"; +static const char *TAG_AP = "ftm_ap"; + +static EventGroupHandle_t wifi_event_group; +const int CONNECTED_BIT = BIT0; +const int DISCONNECTED_BIT = BIT1; + +static void scan_done_handler(void *arg, esp_event_base_t event_base, + int32_t event_id, void *event_data) +{ + uint16_t sta_number = 0; + uint8_t i; + wifi_ap_record_t *ap_list_buffer; + + esp_wifi_scan_get_ap_num(&sta_number); + ap_list_buffer = malloc(sta_number * sizeof(wifi_ap_record_t)); + if (ap_list_buffer == NULL) { + ESP_LOGE(TAG_STA, "Failed to malloc buffer to print scan results"); + return; + } + + if (esp_wifi_scan_get_ap_records(&sta_number, (wifi_ap_record_t *)ap_list_buffer) == ESP_OK) { + for (i = 0; i < sta_number; i++) { + ESP_LOGI(TAG_STA, "[%s][rssi=%d]""%s", ap_list_buffer[i].ssid, ap_list_buffer[i].rssi, + ap_list_buffer[i].ftm_responder ? "[FTM Responder]" : ""); + } + } + free(ap_list_buffer); + ESP_LOGI(TAG_STA, "sta scan done"); +} + +static void wifi_connected_handler(void *arg, esp_event_base_t event_base, + int32_t event_id, void *event_data) +{ + wifi_event_sta_connected_t *event = (wifi_event_sta_connected_t *)event_data; + + ESP_LOGI(TAG_STA, "Connected to %s (BSSID: "MACSTR", Channel: %d)", event->ssid, + MAC2STR(event->bssid), event->channel); + + xEventGroupClearBits(wifi_event_group, DISCONNECTED_BIT); + xEventGroupSetBits(wifi_event_group, CONNECTED_BIT); +} + +static void disconnect_handler(void *arg, esp_event_base_t event_base, + int32_t event_id, void *event_data) +{ + if (s_reconnect) { + ESP_LOGI(TAG_STA, "sta disconnect, s_reconnect..."); + esp_wifi_connect(); + } else { + ESP_LOGI(TAG_STA, "sta disconnect"); + } + xEventGroupClearBits(wifi_event_group, CONNECTED_BIT); + xEventGroupSetBits(wifi_event_group, DISCONNECTED_BIT); +} + +static void ftm_report_handler(void *arg, esp_event_base_t event_base, + int32_t event_id, void *event_data) +{ + wifi_event_ftm_report_t *event = (wifi_event_ftm_report_t *) event_data; + + ESP_LOGI(TAG_STA, "Estimated RTT - %d nSec, Estimated Distance - %d.%02d meters", event->rtt_est, + event->dist_est / 100, event->dist_est % 100); +} + +void initialise_wifi(void) +{ + esp_log_level_set("wifi", ESP_LOG_WARN); + static bool initialized = false; + + if (initialized) { + return; + } + + ESP_ERROR_CHECK(esp_netif_init()); + wifi_event_group = xEventGroupCreate(); + ESP_ERROR_CHECK( esp_event_loop_create_default() ); + wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); + ESP_ERROR_CHECK(esp_wifi_init(&cfg)); + ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT, + WIFI_EVENT_SCAN_DONE, + &scan_done_handler, + NULL, + NULL)); + ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT, + WIFI_EVENT_STA_CONNECTED, + &wifi_connected_handler, + NULL, + NULL)); + ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT, + WIFI_EVENT_STA_DISCONNECTED, + &disconnect_handler, + NULL, + NULL)); + ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT, + WIFI_EVENT_FTM_REPORT, + &ftm_report_handler, + NULL, + NULL)); + ESP_ERROR_CHECK(esp_wifi_set_storage(WIFI_STORAGE_RAM) ); + ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_NULL) ); + ESP_ERROR_CHECK(esp_wifi_start() ); + initialized = true; +} + +static bool wifi_cmd_sta_join(const char *ssid, const char *pass) +{ + int bits = xEventGroupWaitBits(wifi_event_group, CONNECTED_BIT, 0, 1, 0); + + wifi_config_t wifi_config = { 0 }; + + strlcpy((char *) wifi_config.sta.ssid, ssid, sizeof(wifi_config.sta.ssid)); + if (pass) { + strlcpy((char *) wifi_config.sta.password, pass, sizeof(wifi_config.sta.password)); + } + + if (bits & CONNECTED_BIT) { + s_reconnect = false; + xEventGroupClearBits(wifi_event_group, CONNECTED_BIT); + ESP_ERROR_CHECK( esp_wifi_disconnect() ); + xEventGroupWaitBits(wifi_event_group, DISCONNECTED_BIT, 0, 1, portTICK_RATE_MS); + } + + s_reconnect = true; + ESP_ERROR_CHECK( esp_wifi_set_mode(WIFI_MODE_STA) ); + ESP_ERROR_CHECK( esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config) ); + ESP_ERROR_CHECK( esp_wifi_connect() ); + + xEventGroupWaitBits(wifi_event_group, CONNECTED_BIT, 0, 1, 5000 / portTICK_RATE_MS); + + return true; +} + +static int wifi_cmd_sta(int argc, char **argv) +{ + int nerrors = arg_parse(argc, argv, (void **) &sta_args); + + if (nerrors != 0) { + arg_print_errors(stderr, sta_args.end, argv[0]); + return 1; + } + + ESP_LOGI(TAG_STA, "sta connecting to '%s'", sta_args.ssid->sval[0]); + wifi_cmd_sta_join(sta_args.ssid->sval[0], sta_args.password->sval[0]); + return 0; +} + +static bool wifi_cmd_sta_scan(const char *ssid) +{ + wifi_scan_config_t scan_config = { 0 }; + scan_config.ssid = (uint8_t *) ssid; + + ESP_ERROR_CHECK( esp_wifi_set_mode(WIFI_MODE_STA) ); + ESP_ERROR_CHECK( esp_wifi_scan_start(&scan_config, false) ); + + return true; +} + +static int wifi_cmd_scan(int argc, char **argv) +{ + int nerrors = arg_parse(argc, argv, (void **) &scan_args); + + if (nerrors != 0) { + arg_print_errors(stderr, scan_args.end, argv[0]); + return 1; + } + + ESP_LOGI(TAG_STA, "sta start to scan"); + if ( scan_args.ssid->count == 1 ) { + wifi_cmd_sta_scan(scan_args.ssid->sval[0]); + } else { + wifi_cmd_sta_scan(NULL); + } + return 0; +} + +static bool wifi_cmd_ap_set(const char* ssid, const char* pass) +{ + wifi_config_t wifi_config = { + .ap = { + .ssid = "", + .ssid_len = 0, + .max_connection = 4, + .password = "", + .authmode = WIFI_AUTH_WPA2_PSK + }, + }; + + s_reconnect = false; + strlcpy((char*) wifi_config.ap.ssid, ssid, sizeof(wifi_config.ap.ssid)); + if (pass) { + if (strlen(pass) != 0 && strlen(pass) < 8) { + s_reconnect = true; + ESP_LOGE(TAG_AP, "password less than 8"); + return false; + } + strlcpy((char*) wifi_config.ap.password, pass, sizeof(wifi_config.ap.password)); + } + + if (strlen(pass) == 0) { + wifi_config.ap.authmode = WIFI_AUTH_OPEN; + } + + ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_AP)); + ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_AP, &wifi_config)); + return true; +} + +static int wifi_cmd_ap(int argc, char** argv) +{ + int nerrors = arg_parse(argc, argv, (void**) &ap_args); + + if (nerrors != 0) { + arg_print_errors(stderr, ap_args.end, argv[0]); + return 1; + } + + wifi_cmd_ap_set(ap_args.ssid->sval[0], ap_args.password->sval[0]); + ESP_LOGI(TAG_AP, "Starting SoftAP with FTM Responder support, SSID - %s, Password - %s", ap_args.ssid->sval[0], ap_args.password->sval[0]); + return 0; +} + +static int wifi_cmd_query(int argc, char **argv) +{ + wifi_config_t cfg; + wifi_mode_t mode; + + esp_wifi_get_mode(&mode); + if (WIFI_MODE_AP == mode) { + esp_wifi_get_config(WIFI_IF_AP, &cfg); + ESP_LOGI(TAG_AP, "AP mode, %s %s", cfg.ap.ssid, cfg.ap.password); + } else if (WIFI_MODE_STA == mode) { + int bits = xEventGroupWaitBits(wifi_event_group, CONNECTED_BIT, 0, 1, 0); + if (bits & CONNECTED_BIT) { + esp_wifi_get_config(WIFI_IF_STA, &cfg); + ESP_LOGI(TAG_STA, "sta mode, connected %s", cfg.ap.ssid); + } else { + ESP_LOGI(TAG_STA, "sta mode, disconnected"); + } + } else { + ESP_LOGI(TAG_STA, "NULL mode"); + return 0; + } + + return 0; +} + +static int wifi_cmd_ftm(int argc, char **argv) +{ + int nerrors = arg_parse(argc, argv, (void **) &ftm_args); + + wifi_ftm_initiator_cfg_t ftmi_cfg = { + .frm_count = 32, + .burst_period = 2, + }; + + if (nerrors != 0) { + arg_print_errors(stderr, ftm_args.end, argv[0]); + return 0; + } + + if (ftm_args.mode->count == 0) { + goto ftm_start; + } + + if (ftm_args.frm_count->count != 0) { + uint8_t count = ftm_args.frm_count->ival[0]; + if (count != 0 && count != 16 && count != 24 && + count != 32 && count != 64) { + count = 0; + } + ftmi_cfg.frm_count = count; + } + + if (ftm_args.burst_period->count != 0) { + if (ftm_args.burst_period->ival[0] > 0 && + ftm_args.burst_period->ival[0] < 256) { + ftmi_cfg.burst_period = ftm_args.burst_period->ival[0]; + } else { + ftmi_cfg.burst_period = 0; + } + } + +ftm_start: + ESP_LOGI(TAG_STA, "Starting FTM Initiator with Frm Count %d, Burst Period - %dmSec", + ftmi_cfg.frm_count, ftmi_cfg.burst_period * 100); + esp_wifi_ftm_start_initiator(&ftmi_cfg); + + return 0; +} + +void register_wifi(void) +{ + sta_args.ssid = arg_str1(NULL, NULL, "", "SSID of AP"); + sta_args.password = arg_str0(NULL, NULL, "", "password of AP"); + sta_args.end = arg_end(2); + + const esp_console_cmd_t sta_cmd = { + .command = "sta", + .help = "WiFi is station mode, join specified soft-AP", + .hint = NULL, + .func = &wifi_cmd_sta, + .argtable = &sta_args + }; + + ESP_ERROR_CHECK( esp_console_cmd_register(&sta_cmd) ); + + ap_args.ssid = arg_str1(NULL, NULL, "", "SSID of AP"); + ap_args.password = arg_str0(NULL, NULL, "", "password of AP"); + ap_args.end = arg_end(2); + + const esp_console_cmd_t ap_cmd = { + .command = "ap", + .help = "AP mode, configure ssid and password", + .hint = NULL, + .func = &wifi_cmd_ap, + .argtable = &ap_args + }; + + ESP_ERROR_CHECK( esp_console_cmd_register(&ap_cmd) ); + + scan_args.ssid = arg_str0(NULL, NULL, "", "SSID of AP want to be scanned"); + scan_args.end = arg_end(1); + + const esp_console_cmd_t scan_cmd = { + .command = "scan", + .help = "WiFi is station mode, start scan ap", + .hint = NULL, + .func = &wifi_cmd_scan, + .argtable = &scan_args + }; + + ESP_ERROR_CHECK( esp_console_cmd_register(&scan_cmd) ); + + const esp_console_cmd_t query_cmd = { + .command = "query", + .help = "query WiFi info", + .hint = NULL, + .func = &wifi_cmd_query, + }; + ESP_ERROR_CHECK( esp_console_cmd_register(&query_cmd) ); + + ftm_args.mode = arg_lit1("I", "ftm_initiator", "FTM Initiator mode"); + ftm_args.frm_count = arg_int0("c", "frm_count", "<0/16/24/32/64>", "FTM frames to be exchanged (0: No preference)"); + ftm_args.burst_period = arg_int0("p", "burst_period", "<0-255 (x 100 mSec)>", "Periodicity of FTM bursts in 100's of miliseconds (0: No preference)"); + ftm_args.end = arg_end(1); + + const esp_console_cmd_t ftm_cmd = { + .command = "ftm", + .help = "FTM command", + .hint = NULL, + .func = &wifi_cmd_ftm, + .argtable = &ftm_args + }; + + ESP_ERROR_CHECK( esp_console_cmd_register(&ftm_cmd) ); +} + +void app_main(void) +{ + esp_err_t ret = nvs_flash_init(); + if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) { + ESP_ERROR_CHECK(nvs_flash_erase()); + ret = nvs_flash_init(); + } + ESP_ERROR_CHECK( ret ); + + initialise_wifi(); + + esp_console_repl_t *repl = NULL; + esp_console_repl_config_t repl_config = ESP_CONSOLE_REPL_CONFIG_DEFAULT(); + esp_console_dev_uart_config_t uart_config = ESP_CONSOLE_DEV_UART_CONFIG_DEFAULT(); + repl_config.prompt = "ftm>"; + // init console REPL environment + ESP_ERROR_CHECK(esp_console_new_repl_uart(&uart_config, &repl_config, &repl)); + /* Register commands */ + register_system(); + register_wifi(); + + printf("\n ==========================================================\n"); + printf(" | Steps to test FTM |\n"); + printf(" | |\n"); + printf(" | 1. Print 'help' to gain overview of commands |\n"); + printf(" | 2. Use 'scan' command for AP that support FTM |\n"); + printf(" | OR |\n"); + printf(" | 2. Start SoftAP on another ESP32S2 with 'ap' command |\n"); + printf(" | 3. Setup connection with the AP using 'sta' command |\n"); + printf(" | 4. Initiate FTM from Station using 'ftm -I' command |\n"); + printf(" | |\n"); + printf(" ==========================================================\n\n"); + + // start console REPL + ESP_ERROR_CHECK(esp_console_start_repl(repl)); +}