From 620ff4b2072f9df94f262505d7ac22e2bf16c6ae Mon Sep 17 00:00:00 2001 From: Luke Else Date: Fri, 21 Jul 2023 10:23:23 +0100 Subject: [PATCH] Reverted back to pre async attempt --- Cargo.toml | 1 + src/display/mod.rs | 7 +++ src/error.rs | 10 ++++ src/gps/gpsdata.rs | 86 +++++++++++++++++++++++++++++++++ src/gps/mod.rs | 10 ++++ src/main.rs | 116 +++++++++++++-------------------------------- 6 files changed, 147 insertions(+), 83 deletions(-) create mode 100644 src/display/mod.rs create mode 100644 src/error.rs create mode 100644 src/gps/gpsdata.rs create mode 100644 src/gps/mod.rs diff --git a/Cargo.toml b/Cargo.toml index f39f41e..7084e5f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -36,6 +36,7 @@ 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" [build-dependencies] embuild = "0.31.2" diff --git a/src/display/mod.rs b/src/display/mod.rs new file mode 100644 index 0000000..36be10b --- /dev/null +++ b/src/display/mod.rs @@ -0,0 +1,7 @@ +/// Enum to make clear the stage of failure of the display +#[derive(Debug)] +pub enum DisplayError { + SetupError(display_interface::DisplayError), + DrawingError, + FlushError, +} \ No newline at end of file diff --git a/src/error.rs b/src/error.rs new file mode 100644 index 0000000..87e2686 --- /dev/null +++ b/src/error.rs @@ -0,0 +1,10 @@ +use esp_idf_sys::EspError; +use crate::display::DisplayError; +use crate::gps::GpsError; + +#[derive(Debug)] +pub enum Error { + EspError(EspError), + DisplayError(DisplayError), + GpsError(GpsError), +} \ No newline at end of file diff --git a/src/gps/gpsdata.rs b/src/gps/gpsdata.rs new file mode 100644 index 0000000..4365491 --- /dev/null +++ b/src/gps/gpsdata.rs @@ -0,0 +1,86 @@ +use super::GpsError; + +use nmea_parser; +use nmea_parser::gnss::FaaMode; + +/// 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, +} + +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() + ) + } +} + +impl GpsData { + /// Function to go through and update GPS data from a nmea stream + 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())?; + 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() { + continue; + } + + // Construct string that is in the correct format for parsing + let mut sentence = "$".to_string(); + sentence.push_str(nmea_line); + + 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; } + }; + + // print decoded gps data to serial + match nmea { + nmea_parser::ParsedMessage::Gga(gga) => { + self.latitude = gga.latitude; + self.longitude = gga.longitude; + self.altitude = gga.altitude; + } + nmea_parser::ParsedMessage::Gll(gll) => { + if gll.faa_mode.unwrap_or(FaaMode::NotValid) == FaaMode::Autonomous { + self.latitude = gll.latitude; + self.longitude = gll.longitude; + } + }, + nmea_parser::ParsedMessage::Rmc(rms) => { + self.latitude = rms.latitude; + self.longitude = rms.longitude; + self.speed = Some(knots_to_mph(rms.sog_knots.unwrap_or_default())); + }, + _ => {} + } + } + + Ok(()) + } +} + +/// Function to simply convert knots to mph +fn knots_to_mph(knots: f64) -> f64{ + knots * 1.150779 +} \ No newline at end of file diff --git a/src/gps/mod.rs b/src/gps/mod.rs new file mode 100644 index 0000000..201e85a --- /dev/null +++ b/src/gps/mod.rs @@ -0,0 +1,10 @@ +pub mod gpsdata; + +#[derive(Debug)] +#[allow(unused)] +pub enum GpsError { + ParseError(nmea_parser::ParseError), + UpdateError(), + ReadError(), + NoData(), +} \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index eb70f7a..74871a8 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,4 @@ -use esp_idf_sys::{self as _, EspError}; // If using the `binstart` feature of `esp-idf-sys`, always keep this module imported +use esp_idf_sys::{self as _}; // If using the `binstart` feature of `esp-idf-sys`, always keep this module imported use esp_idf_hal::{ prelude::Peripherals, gpio::{AnyInputPin, AnyOutputPin}, @@ -8,8 +8,6 @@ use esp_idf_hal::{ i2c::{I2cConfig, I2cDriver} }; use esp_idf_hal; -use nmea_parser; -use nmea_parser::gnss::FaaMode; use embedded_graphics::{ prelude::*, @@ -21,7 +19,16 @@ use embedded_graphics::{ use ssd1306::{prelude::*, I2CDisplayInterface, Ssd1306}; -fn main() -> Result<(), EspError> { +mod error; +use error::Error; + +mod gps; +use gps::gpsdata; + +mod display; +use display::DisplayError; + +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_sys::link_patches(); @@ -47,7 +54,7 @@ fn main() -> Result<(), EspError> { None::, None::, &config - )?; + ).map_err(|err| Error::EspError(err))?; // Setup I2C driver let config = I2cConfig::new().baudrate(Hertz(921600)); @@ -56,7 +63,7 @@ fn main() -> Result<(), EspError> { sda, scl, &config - )?; + ).map_err(|err| Error::EspError(err))?; let interface = I2CDisplayInterface::new(i2c); let mut display = Ssd1306::new(interface, DisplaySize128x64, DisplayRotation::Rotate0) @@ -66,95 +73,38 @@ fn main() -> Result<(), EspError> { let raw: ImageRaw = ImageRaw::new(include_bytes!("./rust.raw"), 64); let im: Image<'_, _> = Image::new(&raw, Point::new(32, 0)); - im.draw(&mut display).unwrap(); + im.draw(&mut display).map_err(|err| Error::DisplayError(DisplayError::SetupError(err)))?; - - let mut nmea_parser = nmea_parser::NmeaParser::new(); - let mut latest_data = GpsData{latitude: None, longitude: None, speed: None, direction: None}; + let mut latest_data = gpsdata::GpsData::default(); loop { // Read buffer from UART - let mut buf: Vec = (0..uart.count()? as u8).collect(); - uart.read(&mut buf[..], BLOCK)?; - std::thread::sleep(std::time::Duration::from_millis(10)); - - let nmea_raw = String::from_utf8(buf).unwrap_or_default(); - let nmea_vec: Vec<&str> = nmea_raw.split('$').collect(); - for nmea_line in nmea_vec { - // Don't try and process / parse if the string is empty - if nmea_line.is_empty() { - continue; - } - - // Construct string that is in the correct format for parsing - let mut sentence = "$".to_string(); - sentence.push_str(nmea_line); - - let nmea = match nmea_parser.parse_sentence(sentence.as_str()) { - Ok(nmea) => nmea, - // Don't continue processin sentence if we know that it isn't supported - Err(_) => { continue; } - }; - - // print decoded gps data to serial - match nmea { - nmea_parser::ParsedMessage::Gll(gll) => { - println!("Latitude: {:.6?}", gll.latitude.unwrap_or_default()); - println!("Longitude: {:.6?}", gll.longitude.unwrap_or_default()); - println!("FAA Mode: {:?}m", gll.faa_mode.unwrap_or(nmea_parser::gnss::FaaMode::NotValid)); - println!("Data Valid: {:?}", gll.data_valid.unwrap_or(false)); - if gll.faa_mode.unwrap_or(FaaMode::NotValid) == FaaMode::Autonomous { - latest_data.latitude = gll.latitude; - latest_data.longitude = gll.longitude; - } - println!("\n\n"); - }, - nmea_parser::ParsedMessage::Gns(gns) => { - println!("Altitude: {:.2?}", gns.altitude.unwrap_or_default()); - println!("Num Sat: {:?}", gns.satellite_count.unwrap_or_default()); - println!("Time: {:?}", gns.timestamp.unwrap_or_default()); - }, - nmea_parser::ParsedMessage::Rmc(rms) => { - println!("Speed: {:.2?}mph", rms.sog_knots.unwrap_or_default() * 1.150779); - println!("Current Track: {:.2?}deg", rms.bearing.unwrap_or_default()); - latest_data.speed = Some(rms.sog_knots.unwrap_or_default() * 1.150779); - latest_data.direction = rms.bearing; - }, - _ => {} - } - } + let mut buf: Vec = ( + 0..uart.count().map_err(|err| Error::EspError(err))? as u8 + ).collect(); + uart.read(&mut buf[..], BLOCK).map_err(|err| Error::EspError(err))?; + //Update GPS Data Struct with the latest data fetched from UART buffer + latest_data.update(&buf) + .map_err(|err| Error::GpsError(err))?; + + // Clear display ready for next write let style = MonoTextStyle::new(&FONT_7X13, BinaryColor::On); + display.clear(BinaryColor::Off) + .map_err(|_| Error::DisplayError(DisplayError::DrawingError))?; - display.clear(BinaryColor::Off).unwrap(); - + // Draw text to Display Text::with_alignment( &latest_data.to_string().as_str(), Point::new(64, 10), style, Alignment::Center, ) - .draw(&mut display).unwrap(); + .draw(&mut display) + .map_err(|_| Error::DisplayError(DisplayError::DrawingError))?; - display.flush().unwrap(); + //Flush data to the display + display.flush() + .map_err(|_| Error::DisplayError(DisplayError::FlushError))?; } -} - -/// Data structure to store all relevant data collected from the gps -struct GpsData { - latitude: Option, - longitude: Option, - speed: Option, - direction: Option -} - -impl GpsData { - pub fn to_string(&self) -> String { - format!("Latitude: {:.4?} \nLongitude: {:.4?} \nSpeed: {:.1?}mph \nDirection: {:.4?}deg", - self.latitude.unwrap_or_default(), - self.longitude.unwrap_or_default(), - self.speed.unwrap_or_default(), - self.direction.unwrap_or_default() - ) - } -} +} \ No newline at end of file