diff --git a/Cargo.lock b/Cargo.lock index 212dbff..3810bc6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,6 +17,12 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c583acf993cf4245c4acb0a2cc2ab1f9cc097de73411bb6d3647ff6af2b1013d" +[[package]] +name = "arrayvec" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" + [[package]] name = "autocfg" version = "1.5.0" @@ -92,6 +98,15 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" +[[package]] +name = "chrono" +version = "0.4.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c673075a2e0e5f4a1dde27ce9dee1ea4558c7ffe648f576438a20ca1d2acc4b0" +dependencies = [ + "num-traits", +] + [[package]] name = "const-default" version = "1.0.0" @@ -425,6 +440,21 @@ dependencies = [ "heapless 0.9.2", ] +[[package]] +name = "embassy_gps" +version = "0.1.0" +source = "git+https://github.com/jbeyerstedt/embassy_gps.git?branch=feat%2Frework-gpsfix#75f1a67a69372234330c0c9618a953c415380b99" +dependencies = [ + "chrono", + "embassy-futures", + "embassy-time", + "embedded-io-async 0.7.0", + "esp-hal", + "heapless 0.9.2", + "log", + "nmea", +] + [[package]] name = "embedded-can" version = "0.4.1" @@ -903,9 +933,11 @@ dependencies = [ name = "esp32-c_its-companion" version = "0.1.0" dependencies = [ + "chrono", "critical-section", "embassy-executor", "embassy-time", + "embassy_gps", "embedded-io 0.7.1", "embedded-io-async 0.7.0", "esp-alloc", @@ -1183,6 +1215,12 @@ version = "0.2.186" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" +[[package]] +name = "libm" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" + [[package]] name = "linked_list_allocator" version = "0.10.6" @@ -1229,6 +1267,12 @@ version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + [[package]] name = "nb" version = "0.1.3" @@ -1244,6 +1288,30 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8d5439c4ad607c3c23abf66de8c8bf57ba8adcd1f129e699851a6e43935d339d" +[[package]] +name = "nmea" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2086c773d18da556c05ca235596d163c00379027189bafe2209ae40ebd19717c" +dependencies = [ + "arrayvec", + "cfg-if", + "chrono", + "heapless 0.8.0", + "nom", + "num-traits", +] + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + [[package]] name = "nu-ansi-term" version = "0.50.3" @@ -1271,6 +1339,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", + "libm", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index ba3f56e..0f12b56 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -43,6 +43,8 @@ esp-radio = { version = "0.18.0", features = [ "sniffer", ] } +embassy_gps = { version = "0.1.0", default-features = false, features = ["esp", "log-04"], git = "https://github.com/jbeyerstedt/embassy_gps.git", branch = "feat/rework-gpsfix" } +chrono = { version = "0.4.44", default-features = false, features = ["alloc"] } # For fine tuning these settings, please refer to https://doc.rust-lang.org/cargo/reference/profiles.html [profile.dev] diff --git a/src/main.rs b/src/main.rs index 6c740ad..fe4d827 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,10 +5,13 @@ reason = "mem::forget is generally not safe to do with esp_hal types, especially those \ holding buffers for the duration of a data transfer." )] -#![deny(clippy::large_stack_frames)] +// #![deny(clippy::large_stack_frames)] +use core::cell::RefCell; + +use critical_section::Mutex; use embassy_executor::Spawner; -use embassy_time::{Duration, Timer}; +use embassy_gps::gps::l76k; use esp_backtrace as _; use esp_hal::clock::CpuClock; use esp_hal::timer::timg::TimerGroup; @@ -21,10 +24,68 @@ mod radio; const WIFI_CHANNEL: radio::Channel = 180; +#[derive(Debug, Default)] +struct State { + initialized: bool, + time: chrono::NaiveDateTime, + + lat_deg: f32, + lon_deg: f32, + heading_deg: Option, + speed_mps: Option, +} + +impl State { + fn update_with_gpsfix(&mut self, fix: &embassy_gps::types::GpsFix) { + #[allow(clippy::cast_precision_loss, clippy::cast_possible_truncation)] + let lat_deg = fix.latitude_deg as f32; + #[allow(clippy::cast_precision_loss, clippy::cast_possible_truncation)] + let lon_deg = fix.longitude_deg as f32; + + self.lat_deg = lat_deg; + self.lon_deg = lon_deg; + + if let Some(heading) = fix.true_course_deg { + self.heading_deg = Some(heading); + } else { + warn!("State: Course missing from GpsFix"); + } + + if let Some(speed) = fix.speed_over_ground { + self.speed_mps = Some(speed); + } else { + warn!("State: Speed missing from GpsFix"); + } + + if let Some(time) = fix.get_timestamp() { + self.time = time; + + if !self.initialized { + info!("GNSS fix: {}", self.print_fix()); + } + self.initialized = true; + } + } + + fn print_fix(&self) -> alloc::string::String { + alloc::format!( + "{:?}, {:.4} N, {:.4} E, {:.0}°, {:.2} m/s", + self.time, + self.lat_deg, + self.lon_deg, + self.heading_deg.unwrap_or_default(), + self.speed_mps.unwrap_or_default(), + ) + } +} + // This creates a default app-descriptor required by the esp-idf bootloader. // For more information see: esp_bootloader_esp_idf::esp_app_desc!(); +static GNSS_UPDATE: Mutex>> = + Mutex::new(RefCell::new(None)); + #[allow( clippy::large_stack_frames, reason = "it's not unusual to allocate larger buffers etc. in main" @@ -70,13 +131,39 @@ async fn main(spawner: Spawner) -> ! { info!("WiFi promiscuous mode on channel {WIFI_CHANNEL} running"); - // TODO: Spawn some tasks - let _ = spawner; + // Seeed XIAO L67K: + // RX: D7/ GPIO12 + // TX: D6/ GPIO11 + // WAKEUP: D0/ GPIO1 -> HIGH for active, LOW for Sleep + // RESET: D2/ GPIO25 -> HIGH for normal, LOW for Reset + spawner.spawn( + gnss_task( + peripherals.GPIO1, + peripherals.GPIO25, + peripherals.UART0, + peripherals.GPIO12, + peripherals.GPIO11, + ) + .expect("Failed to spawn GNSS task"), + ); + info!("GNSS task started, waiting for GNSS fix..."); + let mut state = State::default(); loop { - // nothing to do here, all data is handled in RX callback for now - // but use a timer so that this is yields to other tasks - Timer::after(Duration::from_secs(1)).await; + embassy_time::Timer::after(embassy_time::Duration::from_millis(100)).await; + + critical_section::with(|cs| { + let gnss_update_ref = GNSS_UPDATE.borrow(cs); + + if let Some(fix) = gnss_update_ref.replace(None) { + state.update_with_gpsfix(&fix); + + if let Some(time) = fix.get_timestamp() { + state.time = time; + info!("{}", state.print_fix()); // TODO: degrate to debug later? + } + } + }); } } @@ -99,3 +186,42 @@ fn handle_frame(frame: wifi::sniffer::PromiscuousPkt<'_>) { // TODO: handle 802.11 frame info!("Received frame with {} bytes", frame.len); } + +#[allow(clippy::large_stack_frames, reason = "GNSS driver needs some space")] +#[embassy_executor::task] +async fn gnss_task( + gps_wakeup: esp_hal::peripherals::GPIO1<'static>, + gps_reset: esp_hal::peripherals::GPIO25<'static>, + uart: esp_hal::peripherals::UART0<'static>, + uart_rx: esp_hal::peripherals::GPIO12<'static>, + uart_tx: esp_hal::peripherals::GPIO11<'static>, +) { + use embassy_gps::gps::GpsFsm; + + let mut gps = l76k::esp::L76kFsm::new_sep( + l76k::esp::GpsHw { + reinit: gps_reset, + standby: gps_wakeup, + }, + || { + esp_hal::uart::Uart::new(uart, esp_hal::uart::Config::default().with_baudrate(9600)) + .expect("Failed to create UART for GNSS") + .with_rx(uart_rx) + .with_tx(uart_tx) + .into_async() + }, + ) + .await; + + loop { + if let Ok(Some(embassy_gps::types::GpsEvent::Fix(fix))) = gps.step().await { + // send to main thread + critical_section::with(|cs| { + let gnss_update_ref = GNSS_UPDATE.borrow(cs); + gnss_update_ref.replace(Some(fix)); + }); + } else { + // FSM will recover automatically from errors and ignore other event types + } + } +}