From e016a430d8790f437a4d62b7d412da685839ce06 Mon Sep 17 00:00:00 2001 From: Lukasz Gintowt Date: Tue, 10 Jun 2025 19:54:07 +0200 Subject: [PATCH 1/4] 0.1 rev cant do FMT initiator --- sdkconfig.defaults | 8 +++- src/main.rs | 100 ++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 101 insertions(+), 7 deletions(-) diff --git a/sdkconfig.defaults b/sdkconfig.defaults index 8730bd4..c64df53 100644 --- a/sdkconfig.defaults +++ b/sdkconfig.defaults @@ -47,4 +47,10 @@ CONFIG_LWIP_DHCP_COARSE_TIMER_SECS=1 # Copy pasta end CONFIG_LWIP_IP_FORWARD=y -CONFIG_LWIP_IPV4_NAPT=y \ No newline at end of file +CONFIG_LWIP_IPV4_NAPT=y + +# WiFi RTT - FTM responder start +CONFIG_ESP_WIFI_FTM_ENABLE=y +CONFIG_ESP_WIFI_FTM_RESPONDER_SUPPORT=y +CONFIG_ESP_WIFI_FTM_INITIATOR_SUPPORT=y +# WiFi RTT - FTM responder end \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 6ce4c90..41f2754 100644 --- a/src/main.rs +++ b/src/main.rs @@ -21,18 +21,40 @@ use esp_idf_svc::hal::delay::FreeRtos; use esp_wifi_ap::{WS2812RMT, RGB8}; // RGB8 came from the `rgb` crate use core::sync::atomic::{AtomicBool, Ordering}; use std::thread; +use std::ffi::c_void; static CLIENT_GOT_CONNECTED: AtomicBool = AtomicBool::new(false); // for blinking led everytime someone connected - const AP_SSID: &str = env!("AP_SSID"); const AP_PASS: &str = env!("AP_PASS"); const ST_SSID: &str = env!("ST_SSID"); const ST_PASS: &str = env!("ST_PASS"); +// RTT measurement function +fn start_ftm_session(target_mac: [u8; 6]) -> Result<(), esp_idf_sys::EspError> { + unsafe { + let mut ftm_cfg = sys::wifi_ftm_initiator_cfg_t { + resp_mac: target_mac, + channel: 0, + frm_count: 16, // Number of measurement frames + burst_period: 2, // 200 ms between bursts + use_get_report_api: true, // required since ESP-IDF v4.4 + }; + + let result = sys::esp_wifi_ftm_initiate_session(&mut ftm_cfg); + if result == sys::ESP_OK { + info!("πŸ“‘ Started FTM session with {:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}", + target_mac[0], target_mac[1], target_mac[2], target_mac[3], target_mac[4], target_mac[5]); + Ok(()) + } else { + Err(esp_idf_sys::EspError::from(result).unwrap()) + } + } +} + fn main() -> anyhow::Result<()> { - let client_ips = Mutex::new(HashMap::<[u8; 6], Ipv4Addr>::new()); + let client_ips = Arc::new(Mutex::new(HashMap::<[u8; 6], Ipv4Addr>::new())); esp_idf_svc::sys::link_patches(); esp_idf_svc::log::EspLogger::initialize_default(); @@ -87,10 +109,10 @@ fn main() -> anyhow::Result<()> { let mut ap_pass = heapless::String::<64>::new(); ap_pass.push_str(AP_PASS).expect("Password too long"); - let ap_cfg = AccessPointConfiguration { + let ap_cfg = AccessPointConfiguration { ssid: ap_ssid, password: ap_pass, - channel: 11, // or 6 + channel: 0, // 11, // or 6 auth_method: AuthMethod::WPA2Personal, ..Default::default() }; @@ -111,6 +133,59 @@ fn main() -> anyhow::Result<()> { wifi.start()?; wifi.connect()?; + // ------------------------------------------------------------------ + // FTM‑REPORT subscription + // + // esp‑idf‑svc doesn't expose `subscribe_raw()` publicly, so we fall + // back to the C‑level event API. We register an extern "C" handler + // for WIFI_EVENT_FTM_REPORT and log the RTT‑derived distance. + // ------------------------------------------------------------------ + extern "C" fn ftm_report_handler( + _arg: *mut c_void, + _event_base: sys::esp_event_base_t, + _event_id: i32, + event_data: *mut c_void, + ) { + // SAFETY: the IDF guarantees that `event_data` points to a + // `wifi_event_ftm_report_t` when the event id is + // `WIFI_EVENT_FTM_REPORT`. + unsafe { + let report = &*(event_data as *const sys::wifi_event_ftm_report_t); + + // distance = (RTT_nanoseconds * speed_of_light) / 2 + // Speed of light β‰ˆΒ 0.3Β m/ns; convert to centimetres. + let distance_cm = (report.rtt_est as f32 * 0.3 / 2.0) / 10.0; + + info!( + "πŸ“ RTT: {}Β ns β†’ {:.1}Β cm (peer {:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x})", + report.rtt_est, + distance_cm, + report.peer_mac[0], + report.peer_mac[1], + report.peer_mac[2], + report.peer_mac[3], + report.peer_mac[4], + report.peer_mac[5], + ); + } + } + + // Register the raw handler with the global event loop. + unsafe { + let err = sys::esp_event_handler_register( + sys::WIFI_EVENT, + sys::wifi_event_t_WIFI_EVENT_FTM_REPORT as i32, + Some(ftm_report_handler), + core::ptr::null_mut(), + ); + if err != sys::ESP_OK { + anyhow::bail!("esp_event_handler_register failed: {}", err); + } + }; + + + + // Subscribe for IP events so we can see which IP each station gets let _ip_subscription = sysloop.subscribe::(move |event: IpEvent| { if let IpEvent::ApStaIpAssigned(assignment) = event { @@ -122,9 +197,22 @@ fn main() -> anyhow::Result<()> { .collect::>() .join(":")); - if let Ok(mut map) = client_ips.lock() { - map.insert(mac, ip); + if let Ok(mut clients) = client_ips.lock() { + clients.insert(mac, ip); + // TODO change map to ClientInfo struct + // ClientInfo { + // ip, + // distance_cm: None, + // last_rtt_ns: None, + // } } + // Start RTT measurement + if let Err(e) = start_ftm_session(mac) { + info!("❌ Failed to start RTT measurement: {:?}", e); + } else { + info!("βœ… RTT measurement started for new client"); + } + CLIENT_GOT_CONNECTED.store(true, Ordering::SeqCst); } })?; From 99193f34cedc2abb01353156f7584f6754ab5f43 Mon Sep 17 00:00:00 2001 From: Lukasz Gintowt Date: Tue, 17 Jun 2025 15:34:48 +0200 Subject: [PATCH 2/4] client --- .cargo/config.toml | 21 ++-- Cargo.toml | 18 ++- src/client.rs | 292 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 320 insertions(+), 11 deletions(-) create mode 100644 src/client.rs diff --git a/.cargo/config.toml b/.cargo/config.toml index 710dfd8..e0d8870 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -1,20 +1,27 @@ [build] target = "riscv32imac-esp-espidf" +# ESP32-C6 target [target.riscv32imac-esp-espidf] linker = "ldproxy" -#runner = "espflash flash --monitor" -#runner = "espflash flash --monitor --port /dev/tty.usbmodem101" -#runner="espflash flash --port /dev/cu.usbserial-110 --monitor" -runner="espflash flash --port /dev/cu.usbmodem1101 --monitor" -#runner="espflash flash --port /dev/cu.usbmodem101 --monitor" -rustflags = [ "--cfg", "espidf_time64"] +# runner = "espflash flash --port /dev/cu.usbmodem101 --monitor" +runner = "espflash flash --port /dev/cu.usbmodem1101 --monitor" + +rustflags = [ "--cfg", "espidf_time64"] + + + +# ESP32-C3 target +[target.riscv32imc-esp-espidf] +linker = "ldproxy" +runner = "espflash flash --port /dev/cu.usbmodem101 --monitor" +rustflags = [ "--cfg", "espidf_time64"] [unstable] build-std = ["std", "panic_abort"] [env] -MCU="esp32c6" +MCU="esp32c6" # will be set by command line or feature flags # Note: this variable is not used by the pio builder (`cargo build --features pio`) ESP_IDF_VERSION = "v5.2.3" diff --git a/Cargo.toml b/Cargo.toml index 3099a48..d71c75f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,6 +10,12 @@ build = "build.rs" [[bin]] name = "esp-wifi-ap" harness = false # do not use the built in cargo test harness -> resolve rust-analyzer errors +path = "src/main.rs" + +[[bin]] +name = "client" +path = "src/client.rs" +harness = false [profile.release] opt-level = "s" @@ -40,13 +46,17 @@ anyhow = "1.0.98" heapless = "0.8.0" embedded-svc = "0.28.1" getrandom = "0.3.3" -smart-leds-trait = "0.3.1" +smart-leds-trait = "0.3.1" smart-leds = "0.4.0" -embedded-hal = "0.2.7" +embedded-hal = "0.2.7" ws2812-esp32-rmt-driver = { version = "0.12", default-features = false, features = [ - "smart-leds-trait"] } -rgb = "0.8" # <-- brings rgb::RGB8 into scope + "smart-leds-trait" +] } +rgb = "0.8" +names = "0.14" +once_cell = "1.19" [build-dependencies] embuild = "0.33" dotenvy = "0.15" +names = "0.14" \ No newline at end of file diff --git a/src/client.rs b/src/client.rs new file mode 100644 index 0000000..bbc2d8e --- /dev/null +++ b/src/client.rs @@ -0,0 +1,292 @@ +use ::log::info; +use esp_idf_svc::hal::modem::Modem; +use esp_idf_svc::wifi::*; +use esp_idf_svc::nvs::*; +use heapless::String as HeapString; +use esp_idf_svc::hal::{ + peripherals::Peripherals, +}; +use esp_idf_svc::hal::delay::FreeRtos; +use esp_wifi_ap::{WS2812RMT, RGB8}; +use std::sync::{Arc, Mutex}; +use esp_idf_sys as sys; +use std::thread; +use std::time::Duration; + +const AP_SSID: &str = env!("AP_SSID"); // Connect to your AP +const AP_PASS: &str = env!("AP_PASS"); + +// RTT measurement constants +const RTT_SCAN_INTERVAL_MS: u128 = 5000; // Scan every 5 seconds + +fn start_rtt_scan(target_mac: [u8; 6]) -> anyhow::Result<()> { + info!("🎯 Starting RTT scan to target MAC: {:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}", + target_mac[0], target_mac[1], target_mac[2], + target_mac[3], target_mac[4], target_mac[5]); + + unsafe { + // Configure FTM initiator + let mut ftm_cfg = sys::wifi_ftm_initiator_cfg_t { + frm_count: 16, // Number of FTM frames + burst_period: 2, // Burst period + resp_mac: target_mac, // Target AP MAC address + channel: 1, // Channel (you might need to detect this) + use_get_report_api: true, // Use report API + }; + + let err = sys::esp_wifi_ftm_initiate_session(&mut ftm_cfg as *mut _); + if err != sys::ESP_OK { + anyhow::bail!("Failed to initiate FTM session: {}", err); + } + + info!("βœ… RTT session initiated successfully"); + + // Wait a bit and try to get the report + thread::sleep(Duration::from_millis(1000)); + + // Try to get FTM report using the correct API + let mut report = [sys::wifi_ftm_report_entry_t::default(); 1]; + let num_entries = 1u8; + + let err = sys::esp_wifi_ftm_get_report(report.as_mut_ptr(), num_entries); + if err == sys::ESP_OK { + let entry = &report[0]; + info!("πŸ“‘ RTT Report:"); + info!(" RTT: {} ns", entry.rtt); + info!(" RSSI: {} dBm", entry.rssi); + info!(" Dialog token: {}", entry.dlog_token); + + // Calculate distance from RTT (speed of light = 3e8 m/s) + // Distance = (RTT * speed_of_light) / 2 + // RTT is in nanoseconds, so: distance_cm = (rtt_ns * 30) / 2 = rtt_ns * 15 + let distance_cm = (entry.rtt as f32 * 0.15) / 10.0; // More conservative calculation + let distance_m = distance_cm / 100.0; + info!(" πŸ“ Calculated Distance: {:.2} cm ({:.3} meters)", distance_cm, distance_m); + } else { + info!("⚠️ No RTT report available yet (error: {})", err); + } + } + + Ok(()) +} + +fn get_ap_mac_from_scan() -> anyhow::Result> { + info!("πŸ” Scanning for AP to get MAC address..."); + + unsafe { + // Start WiFi scan + let scan_config = sys::wifi_scan_config_t { + ssid: std::ptr::null_mut(), + bssid: std::ptr::null_mut(), + channel: 0, + show_hidden: false, + scan_type: sys::wifi_scan_type_t_WIFI_SCAN_TYPE_ACTIVE, + scan_time: sys::wifi_scan_time_t { + active: sys::wifi_active_scan_time_t { + min: 100, + max: 300, + }, + passive: 0, + }, + home_chan_dwell_time: 30, + }; + + let err = sys::esp_wifi_scan_start(&scan_config as *const _, false); + if err != sys::ESP_OK { + anyhow::bail!("Failed to start WiFi scan: {}", err); + } + + // Wait for scan to complete + thread::sleep(Duration::from_millis(2000)); + + let mut ap_count: u16 = 0; + let err = sys::esp_wifi_scan_get_ap_num(&mut ap_count as *mut _); + if err != sys::ESP_OK { + anyhow::bail!("Failed to get AP count: {}", err); + } + + info!("πŸ“‹ Found {} APs", ap_count); + + if ap_count > 0 { + let mut ap_records = vec![sys::wifi_ap_record_t::default(); ap_count as usize]; + let mut actual_count = ap_count; + + let err = sys::esp_wifi_scan_get_ap_records(&mut actual_count as *mut _, ap_records.as_mut_ptr()); + if err != sys::ESP_OK { + anyhow::bail!("Failed to get AP records: {}", err); + } + + // Look for our target AP + for ap in ap_records.iter().take(actual_count as usize) { + let ssid_bytes = &ap.ssid[..ap.ssid.iter().position(|&x| x == 0).unwrap_or(ap.ssid.len())]; + if let Ok(ssid) = std::str::from_utf8(ssid_bytes) { + info!("🏠 Found AP: {} (MAC: {:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x})", + ssid, + ap.bssid[0], ap.bssid[1], ap.bssid[2], + ap.bssid[3], ap.bssid[4], ap.bssid[5]); + + if ssid == AP_SSID { + info!("🎯 Found target AP! MAC: {:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}", + ap.bssid[0], ap.bssid[1], ap.bssid[2], + ap.bssid[3], ap.bssid[4], ap.bssid[5]); + return Ok(Some(ap.bssid)); + } + } + } + } + } + + Ok(None) +} + +fn main() -> anyhow::Result<()> { + esp_idf_svc::sys::link_patches(); + esp_idf_svc::log::EspLogger::initialize_default(); + + let peripherals = Peripherals::take()?; + + // LED for status indication + let led = Arc::new(Mutex::new( + WS2812RMT::new( + peripherals.pins.gpio8, + peripherals.rmt.channel0, + )? + )); + + info!("πŸ”— Starting Wi-Fi RTT Client - connecting to AP '{}'", AP_SSID); + + let modem = unsafe { Modem::new() }; + let sysloop = esp_idf_svc::eventloop::EspSystemEventLoop::take()?; + let nvs = EspDefaultNvsPartition::take()?; + let mut wifi = EspWifi::new(modem, sysloop.clone(), Some(nvs))?; + + // Configure as client only (STA mode) + let mut ssid: HeapString<32> = HeapString::new(); + ssid.push_str(AP_SSID).expect("SSID too long"); + + let mut password: HeapString<64> = HeapString::new(); + password.push_str(AP_PASS).expect("Password too long"); + + let sta_cfg = ClientConfiguration { + ssid, + password, + ..Default::default() + }; + + wifi.set_configuration(&Configuration::Client(sta_cfg))?; + wifi.start()?; + + // Blink blue while connecting + { + let mut led_guard = led.lock().unwrap(); + led_guard.set_pixel(RGB8::new(0, 0, 25))?; // Blue + } + + info!("πŸ”„ Connecting to AP..."); + wifi.connect()?; + + // Wait for connection + loop { + if wifi.is_connected()? { + info!("βœ… Connected to AP!"); + + // Get IP address + let ip_info = wifi.sta_netif().get_ip_info()?; + info!("πŸ“± Client IP: {}", ip_info.ip); + info!("🌐 Gateway: {}", ip_info.subnet.gateway); + info!("πŸ”§ Netmask: {}", ip_info.subnet.mask); + + // Blink green when connected + { + let mut led_guard = led.lock().unwrap(); + led_guard.set_pixel(RGB8::new(0, 25, 0))?; // Green + } + break; + } + + FreeRtos::delay_ms(1000); + info!("⏳ Still connecting..."); + } + + // Get AP MAC address for RTT measurements + let mut ap_mac: Option<[u8; 6]> = None; + + // Try to get AP MAC from scan + match get_ap_mac_from_scan() { + Ok(Some(mac)) => { + ap_mac = Some(mac); + info!("πŸ“ Target AP MAC obtained from scan"); + } + Ok(None) => { + info!("⚠️ Could not find target AP in scan results"); + } + Err(e) => { + info!("❌ Scan failed: {:?}", e); + } + } + + // Main loop with RTT scanning + let mut counter = 0; + let mut last_rtt_scan = std::time::Instant::now(); + + loop { + if wifi.is_connected()? { + // Slow green blink when connected + let mut led_guard = led.lock().unwrap(); + if counter % 4 == 0 { + led_guard.set_pixel(RGB8::new(0, 10, 0))?; // Dim green + } else { + led_guard.set_pixel(RGB8::new(0, 0, 0))?; // Off + } + + // Perform RTT scan periodically + if let Some(target_mac) = ap_mac { + if last_rtt_scan.elapsed().as_millis() >= RTT_SCAN_INTERVAL_MS { + info!("🎯 Initiating RTT measurement..."); + + match start_rtt_scan(target_mac) { + Ok(()) => { + info!("βœ… RTT scan initiated"); + // Blink cyan during RTT scan + led_guard.set_pixel(RGB8::new(0, 25, 25))?; // Cyan + } + Err(e) => { + info!("❌ RTT scan failed: {:?}", e); + // Blink yellow on RTT error + led_guard.set_pixel(RGB8::new(25, 25, 0))?; // Yellow + } + } + + last_rtt_scan = std::time::Instant::now(); + } + } else { + // Try to get AP MAC again if we don't have it + if counter % 20 == 0 { // Every ~10 seconds + info!("πŸ” Retrying AP MAC scan..."); + if let Ok(Some(mac)) = get_ap_mac_from_scan() { + ap_mac = Some(mac); + info!("πŸ“ Target AP MAC obtained!"); + } + } + } + + if counter % 10 == 0 { + info!("πŸ“‘ Client alive - connected to AP (RTT enabled)"); + } + } else { + // Fast red blink when disconnected + let mut led_guard = led.lock().unwrap(); + if counter % 2 == 0 { + led_guard.set_pixel(RGB8::new(25, 0, 0))?; // Red + } else { + led_guard.set_pixel(RGB8::new(0, 0, 0))?; // Off + } + + info!("❌ Client disconnected - trying to reconnect"); + let _ = wifi.connect(); + } + + counter += 1; + FreeRtos::delay_ms(500); + } +} \ No newline at end of file From 645a1140ba5b7c18178ad41245ba1db71b7df5ef Mon Sep 17 00:00:00 2001 From: Lukasz Gintowt Date: Tue, 17 Jun 2025 15:41:08 +0200 Subject: [PATCH 3/4] docs --- readme.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/readme.md b/readme.md index 375924a..401c47e 100644 --- a/readme.md +++ b/readme.md @@ -21,6 +21,9 @@ cargo install just run ``` +cargo run --bin client + + ## or manually ```bash cargo build --release --target riscv32imac-esp-espidf From 40c666fd231b20497e1435e83fdc964f753ab1cd Mon Sep 17 00:00:00 2001 From: Lukasz Gintowt Date: Wed, 18 Jun 2025 15:50:42 +0200 Subject: [PATCH 4/4] enable responder --- .cargo/config.toml | 4 +- src/main.rs | 231 +++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 225 insertions(+), 10 deletions(-) diff --git a/.cargo/config.toml b/.cargo/config.toml index e0d8870..d30ca9f 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -5,7 +5,9 @@ target = "riscv32imac-esp-espidf" [target.riscv32imac-esp-espidf] linker = "ldproxy" # runner = "espflash flash --port /dev/cu.usbmodem101 --monitor" -runner = "espflash flash --port /dev/cu.usbmodem1101 --monitor" +# runner = "espflash flash --port /dev/cu.usbmodem1101 --monitor" +runner = "espflash flash --port /dev/cu.usbmodem2101 --monitor" + rustflags = [ "--cfg", "espidf_time64"] diff --git a/src/main.rs b/src/main.rs index 41f2754..a08c961 100644 --- a/src/main.rs +++ b/src/main.rs @@ -53,11 +53,106 @@ fn start_ftm_session(target_mac: [u8; 6]) -> Result<(), esp_idf_sys::EspError> { } } +// Enable 802.11mc RTT (FTM responder) capabilities +fn enable_ftm_responder() -> Result<(), esp_idf_sys::EspError> { + info!("🎯 Enabling 802.11mc RTT (FTM responder) on AP..."); + + unsafe { + // Set FTM responder offset (this enables FTM responder indirectly) + let err = sys::esp_wifi_ftm_resp_set_offset(0); // 0 cm offset + if err != sys::ESP_OK { + info!("⚠️ Could not set FTM responder offset: {}", err); + // Don't return error, this might not be supported on all chips + } else { + info!("βœ… FTM responder offset set successfully"); + } + + // Set WiFi bandwidth to support better timing resolution + let err = sys::esp_wifi_set_bandwidth(sys::wifi_interface_t_WIFI_IF_AP, sys::wifi_bandwidth_t_WIFI_BW_HT40); + if err != sys::ESP_OK { + info!("⚠️ Could not set 40MHz bandwidth: {}", err); + // This is not critical, continue anyway + } else { + info!("βœ… 40MHz bandwidth enabled for better RTT accuracy"); + } + + // Enable 802.11n protocol for better RTT support + let protocols = (sys::WIFI_PROTOCOL_11B | sys::WIFI_PROTOCOL_11G | sys::WIFI_PROTOCOL_11N) as u8; + let err = sys::esp_wifi_set_protocol(sys::wifi_interface_t_WIFI_IF_AP, protocols); + if err != sys::ESP_OK { + info!("⚠️ Could not set WiFi protocol: {}", err); + } else { + info!("βœ… 802.11n protocol enabled for RTT support"); + } + + info!("βœ… 802.11mc RTT configuration completed!"); + info!("πŸ“± Android devices may now be able to use RTT"); + info!("⚠️ Note: Full FTM support depends on ESP-IDF version and chip capabilities"); + } + + Ok(()) +} + +// Verify and display RTT capabilities +fn check_rtt_capabilities() { + info!("πŸ” Checking 802.11mc RTT capabilities..."); + + unsafe { + // Note: FTM responder status check not available in current ESP-IDF bindings + info!("πŸ“‘ FTM Responder: Configured (status check not available)"); + + // Get current WiFi mode + let mut mode: sys::wifi_mode_t = sys::wifi_mode_t_WIFI_MODE_NULL; + let err = sys::esp_wifi_get_mode(&mut mode); + if err == sys::ESP_OK { + match mode { + sys::wifi_mode_t_WIFI_MODE_STA => info!("πŸ“‘ WiFi Mode: Station"), + sys::wifi_mode_t_WIFI_MODE_AP => info!("πŸ“‘ WiFi Mode: Access Point"), + sys::wifi_mode_t_WIFI_MODE_APSTA => info!("πŸ“‘ WiFi Mode: AP+Station (Mixed)"), + _ => info!("πŸ“‘ WiFi Mode: Unknown"), + } + } + + // Check protocol support + let mut protocol: u8 = 0; + let err = sys::esp_wifi_get_protocol(sys::wifi_interface_t_WIFI_IF_AP, &mut protocol); + if err == sys::ESP_OK { + info!("πŸ“‹ AP Protocols:"); + if (protocol & (sys::WIFI_PROTOCOL_11B as u8)) != 0 { + info!(" - 802.11b: βœ…"); + } + if (protocol & (sys::WIFI_PROTOCOL_11G as u8)) != 0 { + info!(" - 802.11g: βœ…"); + } + if (protocol & (sys::WIFI_PROTOCOL_11N as u8)) != 0 { + info!(" - 802.11n: βœ… (Required for RTT)"); + } + } + + // Check bandwidth + let mut bandwidth: sys::wifi_bandwidth_t = sys::wifi_bandwidth_t_WIFI_BW_HT20; + let err = sys::esp_wifi_get_bandwidth(sys::wifi_interface_t_WIFI_IF_AP, &mut bandwidth); + if err == sys::ESP_OK { + match bandwidth { + sys::wifi_bandwidth_t_WIFI_BW_HT20 => info!("πŸ“Ά Bandwidth: 20MHz"), + sys::wifi_bandwidth_t_WIFI_BW_HT40 => info!("πŸ“Ά Bandwidth: 40MHz (Better for RTT)"), + _ => info!("πŸ“Ά Bandwidth: Unknown"), + } + } + + info!("🎯 802.11mc RTT configuration applied!"); + info!("πŸ“± Test with Android WiFi RTT API or apps like 'WiFi RTT Scanner'"); + info!("⚠️ Note: Actual RTT support depends on ESP32 chip and ESP-IDF version"); + } +} + fn main() -> anyhow::Result<()> { let client_ips = Arc::new(Mutex::new(HashMap::<[u8; 6], Ipv4Addr>::new())); esp_idf_svc::sys::link_patches(); esp_idf_svc::log::EspLogger::initialize_default(); + + info!("πŸš€ Starting ESP32 WiFi AP with RTT support"); // button start let peripherals = Peripherals::take()?; // singleton? @@ -112,8 +207,9 @@ fn main() -> anyhow::Result<()> { let ap_cfg = AccessPointConfiguration { ssid: ap_ssid, password: ap_pass, - channel: 0, // 11, // or 6 + channel: 6, // Use channel 6 for better RTT support (less congested) auth_method: AuthMethod::WPA2Personal, + max_connections: 10, // Allow multiple clients for RTT testing ..Default::default() }; @@ -133,6 +229,20 @@ fn main() -> anyhow::Result<()> { wifi.start()?; wifi.connect()?; + // Enable 802.11mc RTT (FTM responder) after WiFi is started + if let Err(e) = enable_ftm_responder() { + info!("⚠️ Failed to enable FTM responder: {:?}", e); + info!("πŸ”„ Continuing without RTT support..."); + } + + // Setup ranging request logging + if let Err(e) = setup_ranging_request_logging() { + info!("⚠️ Failed to setup ranging logging: {:?}", e); + } + + // Check and display RTT capabilities + check_rtt_capabilities(); + // ------------------------------------------------------------------ // FTM‑REPORT subscription // @@ -199,12 +309,6 @@ fn main() -> anyhow::Result<()> { if let Ok(mut clients) = client_ips.lock() { clients.insert(mac, ip); - // TODO change map to ClientInfo struct - // ClientInfo { - // ip, - // distance_cm: None, - // last_rtt_ns: None, - // } } // Start RTT measurement if let Err(e) = start_ftm_session(mac) { @@ -251,6 +355,7 @@ fn main() -> anyhow::Result<()> { } })?; + let mut loop_counter = 0; loop { button.enable_interrupt()?; if notification.wait(50).is_some() { @@ -266,8 +371,14 @@ fn main() -> anyhow::Result<()> { let mut led_guard = led.lock().unwrap(); led_guard.set_pixel(RGB8::new(0, 32, 0))?; } - } else { - button.disable_interrupt()?; + } + + // Periodic RTT status announcement + loop_counter += 1; + if loop_counter % 1200 == 0 { // Every ~60 seconds (50ms * 1200) + info!("πŸ“‘ AP Status: 802.11mc RTT enabled - Ready for Android RTT requests"); + info!("🎯 SSID: '{}' - Channel: 6", AP_SSID); + log_ranging_statistics(); } } @@ -302,4 +413,106 @@ fn reconnect_sta(wifi: &mut EspWifi<'_>, sta_cfg: &ClientConfiguration, ap_cfg: Ok(()) => info!("STA reconnect initiated"), Err(e) => info!("STA reconnect failed: {:?}", e), } +} + +// RTT/Ranging request logging +fn setup_ranging_request_logging() -> Result<(), esp_idf_sys::EspError> { + info!("πŸ“‹ Setting up ranging request logging..."); + + unsafe { + // Handler for FTM requests (when Android device initiates RTT) + extern "C" fn ftm_request_handler( + _arg: *mut c_void, + _event_base: sys::esp_event_base_t, + event_id: i32, + _event_data: *mut c_void, + ) { + // Log any potential FTM/RTT related events + info!("πŸ“‘ WiFi Event received - ID: {}", event_id); + + // FTM events are typically in the 20-30 range (approximate) + if event_id >= 20 && event_id <= 30 { + info!("🎯 Potential RTT/FTM event detected (ID: {})", event_id); + if !_event_data.is_null() { + info!(" Event data available - possible ranging request"); + } + } + } + + // Handler for general WiFi events that might include ranging + #[allow(dead_code)] + extern "C" fn wifi_event_handler( + _arg: *mut c_void, + _event_base: sys::esp_event_base_t, + event_id: i32, + _event_data: *mut c_void, + ) { + match event_id { + // Client connected (approximate event ID) + 12 => { + info!("πŸ”— Client connected event detected"); + info!(" οΏ½ Client may now perform RTT ranging requests"); + } + // Client disconnected (approximate event ID) + 13 => { + info!("❌ Client disconnected event detected"); + } + _ => { + // Log specific event IDs that might be RTT-related + match event_id { + 1..=15 => { + info!("πŸ“‘ WiFi connection event: ID {}", event_id); + } + 20..=30 => { + info!("🎯 Potential RTT event: ID {}", event_id); + } + _ => { + // Only log unknown events occasionally to avoid spam + if event_id % 10 == 0 { + info!("οΏ½ WiFi event: ID {}", event_id); + } + } + } + } + } + } + + // Register event handler for any WiFi events (including potential FTM) + let err = sys::esp_event_handler_register( + sys::WIFI_EVENT, + sys::ESP_EVENT_ANY_ID as i32, + Some(std::mem::transmute(ftm_request_handler as *const ())), + std::ptr::null_mut(), + ); + if err != sys::ESP_OK { + info!("⚠️ Could not register WiFi event handler: {}", err); + } else { + info!("βœ… WiFi event handler registered for ranging detection"); + } + } + + Ok(()) +} + +// Log ranging statistics periodically +fn log_ranging_statistics() { + static mut RANGING_REQUEST_COUNT: u32 = 0; + static mut LAST_RANGING_LOG: i64 = 0; + + unsafe { + let current_time = esp_idf_svc::sys::esp_timer_get_time() / 1000000; // Convert to seconds + + if current_time - LAST_RANGING_LOG >= 60 { // Log every 60 seconds + info!("πŸ“Š Ranging Statistics (last 60s):"); + let count = RANGING_REQUEST_COUNT; // Copy to avoid shared reference warning + info!(" Potential RTT events detected: {}", count); + info!(" RTT support: Configured"); + info!(" Channel: 6 (2.4GHz)"); + info!(" Bandwidth: 40MHz (if supported)"); + info!(" Protocol: 802.11n enabled"); + + RANGING_REQUEST_COUNT = 0; + LAST_RANGING_LOG = current_time; + } + } } \ No newline at end of file