From 97109ce45bf13e412b498064d5d49223b458d529 Mon Sep 17 00:00:00 2001 From: Luke Else Date: Tue, 28 Nov 2023 23:09:51 +0000 Subject: [PATCH] Integrated new NMEA package --- .cargo/config.toml | 2 +- Cargo.toml | 2 +- build.rs | 3 +- src/gps/gpsdata.rs | 98 +++++++++------------------------------------- src/gps/mod.rs | 41 ++++++++++--------- src/main.rs | 17 +++++--- 6 files changed, 54 insertions(+), 109 deletions(-) diff --git a/.cargo/config.toml b/.cargo/config.toml index 703b870..3d889d9 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -12,6 +12,6 @@ build-std = ["std", "panic_abort"] [env] # Note: these variables are not used when using pio builder (`cargo build --features pio`) -ESP_IDF_VERSION = "release/v5.1" +ESP_IDF_VERSION = "v5.1.1" ESP_IDF_PATH_ISSUES = "warn" # or "ignore" diff --git a/Cargo.toml b/Cargo.toml index 37f1514..ddee508 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,11 +28,11 @@ embassy = ["esp-idf-svc/embassy-sync", "esp-idf-svc/critical-section", "esp-idf- log = { version = "0.4.17", default-features = false } esp-idf-svc = { version = "0.47.3", default-features = false } embedded-svc = { version = "0.25", optional = true, default-features = false } -nmea-parser = "0.10.0" embedded-graphics = "0.8.0" ssd1306 = "0.8.0" display-interface = "0.4.1" display-interface-i2c = "0.4.0" +nmea = { version = "0.6.0", default-features = false } [build-dependencies] embuild = "0.31.2" diff --git a/build.rs b/build.rs index c59899a..44dda0b 100644 --- a/build.rs +++ b/build.rs @@ -1,5 +1,4 @@ // Necessary because of this issue: https://github.com/rust-lang/cargo/issues/9641 -fn main() -> Result<(), Box> { +fn main() { embuild::espidf::sysenv::output(); - Ok(()) } diff --git a/src/gps/gpsdata.rs b/src/gps/gpsdata.rs index 4db974a..0ddd8ca 100644 --- a/src/gps/gpsdata.rs +++ b/src/gps/gpsdata.rs @@ -1,32 +1,21 @@ use super::GpsError; -use nmea_parser; -use nmea_parser::chrono::{DateTime, Utc}; -use nmea_parser::gnss::FaaMode; +use nmea::Nmea; /// Data structure to store all relevant data collected from the gps #[derive(Default)] pub struct GpsData { - /// Latitude of reported GPS location - latitude: Option, - /// Longitude of reported GPS location - longitude: Option, - /// Calculated speed from GPS reciever - speed: Option, - /// Altitude reported from GPS location - altitude: Option, - /// Timestamp of the last report - timestamp: Option>, + pub nmea: Nmea, } impl ToString for GpsData { fn to_string(&self) -> String { format!( "Latitude: {:.4?} \nLongitude: {:.4?} \nSpeed: {:.1?}mph \nAltitude: {:.1?}m", - self.latitude.unwrap_or_default(), - self.longitude.unwrap_or_default(), - self.speed.unwrap_or_default(), - self.altitude.unwrap_or_default(), + self.nmea.latitude.unwrap_or_default(), + self.nmea.longitude.unwrap_or_default(), + knots_to_mph(self.nmea.speed_over_ground.unwrap_or_default()), + self.nmea.altitude.unwrap_or_default(), ) } } @@ -37,85 +26,34 @@ impl GpsData { /// Takes in a reference to a buffer of raw serial data and then /// tries to read it in as NMEA Data pub fn update(&mut self, buf: &Vec) -> Result<(), GpsError> { - let mut nmea_parser = nmea_parser::NmeaParser::new(); - - // Format the nmea buffer into a usable string let nmea_raw = String::from_utf8(buf.to_owned()) .map_err(|_| GpsError::ReadError()) .unwrap_or_default(); - let nmea_vec: Vec<&str> = nmea_raw.split('$').collect(); - // Loop through each sentence and use the information to update GPS data - for nmea_line in nmea_vec { - // Don't try and process / parse if the string is empty - if nmea_line.is_empty() { + // Split at $ and then prepend the '$' back to each string + let nmea_vec: Vec = nmea_raw.split('$').map(|str| format!("${}", str)).collect(); + + for nmea_string in nmea_vec { + if nmea_string.is_empty() { continue; } - // Construct string that is in the correct format for parsing - let mut sentence = "$".to_string(); - sentence.push_str(nmea_line); + match self.nmea.parse(&nmea_string) { + Ok(sentence) => { + println!("Recieved GPS Packet: {}", sentence) + } - let nmea = match nmea_parser.parse_sentence(sentence.as_str()) { - Ok(nmea) => nmea, - // Don't continue processing a sentence if we know that it isn't supported - Err(_) => { - continue; + Err(e) => { + eprintln!("Couldn't parse Packet: {}", e) } - }; - - // print decoded gps data to serial - match nmea { - // TODO: Investigate why the GGA Packets seem to come in mangled - nmea_parser::ParsedMessage::Gga(gga) => { - // Update Altitude above ground level - self.altitude = match gga.altitude { - Some(_) => gga.altitude, - None => self.altitude, - }; - } - nmea_parser::ParsedMessage::Gll(gll) => { - // FAA mode is used to determine the validity of the data in this case - if gll.faa_mode.unwrap_or(FaaMode::NotValid) == FaaMode::Autonomous { - // Only make updates to position if we have new data, do not overwrite - // stale data - match gll.latitude { - Some(_) => self.latitude = gll.latitude, - None => {} - } - match gll.longitude { - Some(_) => self.longitude = gll.longitude, - None => {} - } - match gll.timestamp { - Some(_) => self.timestamp = gll.timestamp, - None => {} - } - } - } - nmea_parser::ParsedMessage::Rmc(rms) => { - // Update Ground Speed - match rms.sog_knots { - Some(_) => { - self.speed = Some(knots_to_mph(rms.sog_knots.unwrap_or_default())) - } - None => {} - } - } - _ => {} } } Ok(()) } - - /// Function to get a &str of the current speed reported by the GPS module - pub fn get_speed(&self) -> Option { - self.speed - } } /// Function to simply convert knots to mph -fn knots_to_mph(knots: f64) -> f64 { +pub fn knots_to_mph(knots: f32) -> f32 { knots * 1.150779 } diff --git a/src/gps/mod.rs b/src/gps/mod.rs index 1f4b85c..e42ecf3 100644 --- a/src/gps/mod.rs +++ b/src/gps/mod.rs @@ -1,10 +1,10 @@ use esp_idf_svc::hal::{ self, - peripheral::Peripheral, - gpio::{AnyInputPin, AnyOutputPin, InputPin}, - units::Hertz, delay::BLOCK, + gpio::{AnyInputPin, AnyOutputPin, InputPin}, + peripheral::Peripheral, uart::{self, Uart}, + units::Hertz, }; use crate::error::Error; @@ -14,7 +14,7 @@ pub mod gpsdata; #[derive(Debug)] #[allow(unused)] pub enum GpsError { - ParseError(nmea_parser::ParseError), + ParseError(), UpdateError(), ReadError(), NoData(), @@ -23,7 +23,7 @@ pub enum GpsError { pub struct GPS<'a> { uart: uart::UartRxDriver<'a>, latest: gpsdata::GpsData, - current: gpsdata::GpsData + current: gpsdata::GpsData, } impl<'a> GPS<'a> { @@ -33,36 +33,39 @@ impl<'a> GPS<'a> { uart: impl Peripheral

+ 'a, gps_rx: impl Peripheral

+ 'a, ) -> Result, Error> { - // Setup UART to read serial data from GPS + // Setup UART to read serial data from GPS let config = uart::config::Config::default().baudrate(Hertz(9_600)); let uart: uart::UartRxDriver = uart::UartRxDriver::new( uart, gps_rx, None::, None::, - &config - ).map_err(|err| Error::EspError(err))?; + &config, + ) + .map_err(|err| Error::EspError(err))?; // Create GPS struct - Ok(GPS { - uart, - latest: gpsdata::GpsData::default(), - current: gpsdata::GpsData::default() + Ok(GPS { + uart, + latest: gpsdata::GpsData::default(), + current: gpsdata::GpsData::default(), }) } pub fn poll(self: &mut GPS<'a>) -> Result<&gpsdata::GpsData, Error> { //Read buffer from UART - let mut buf: Vec = ( - 0..self.uart.count().map_err(|err| Error::EspError(err))? as u8 - ).collect(); - self.uart.read(&mut buf[..], BLOCK).map_err(|err| Error::EspError(err))?; + let mut buf: Vec = + (0..self.uart.count().map_err(|err| Error::EspError(err))? as u8).collect(); + self.uart + .read(&mut buf[..], BLOCK) + .map_err(|err| Error::EspError(err))?; //Update GPS Data Struct with the latest data fetched from UART buffer - self.latest.update(&buf) - .map_err(|err| Error::GpsError(err))?; + self.latest + .update(&buf) + .map_err(|err| Error::GpsError(err))?; // Return the latest stored data Ok(&self.latest) } -} \ No newline at end of file +} diff --git a/src/main.rs b/src/main.rs index 691ac3b..9aed2b7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -16,7 +16,10 @@ use error::Error; mod appstate; mod gps; -use gps::{gpsdata::GpsData, GPS}; +use gps::{ + gpsdata::{knots_to_mph, GpsData}, + GPS, +}; mod display; use display::Display; @@ -26,8 +29,10 @@ fn main() -> Result<(), Error> { // It is necessary to call this function once. Otherwise some patches to the runtime // implemented by esp-idf-sys might not link properly. See https://github.com/esp-rs/esp-idf-template/issues/71 esp_idf_svc::sys::link_patches(); + // Bind the log crate to the ESP Logging facilities - //esp_idf_svc::log::EspLogger::initialize_default(); + esp_idf_svc::log::EspLogger::initialize_default(); + log::info!("hello"); // Get Peripherals and sysloop ready let peripherals = Peripherals::take().unwrap(); @@ -46,7 +51,7 @@ fn main() -> Result<(), Error> { // Setup GPIO interrupts static FLAG: AtomicBool = AtomicBool::new(false); - let mut interrupt_button_handle = PinDriver::input(pins.gpio9).unwrap(); + let mut interrupt_button_handle = PinDriver::input(pins.gpio4).unwrap(); interrupt_button_handle.set_pull(Pull::Up); interrupt_button_handle.set_interrupt_type(InterruptType::NegEdge); @@ -56,7 +61,7 @@ fn main() -> Result<(), Error> { interrupt_button_handle.enable_interrupt(); // Prepare components for processing - let mut gps: GPS = GPS::new(peripherals.uart0, gps_rx)?; + let mut gps: GPS = GPS::new(peripherals.uart1, gps_rx)?; let mut display: Display = Display::new(i2c, DisplaySize128x64, DisplayRotation::Rotate0)?; let mut _app_state = appstate::AppState::default(); @@ -66,8 +71,8 @@ fn main() -> Result<(), Error> { let text: String; // Get the latest data from GPS module and flush to Display text = match gps.poll() { - Ok(res) => match res.get_speed() { - Some(s) => format!("{:.2} MPH", s), + Ok(res) => match res.nmea.speed_over_ground { + Some(s) => format!("{:.2} MPH", knots_to_mph(s)), None => NO_DATA.to_string(), }, Err(e) => {