generated from luke-else/esp32-std-template
	Compare commits
	
		
			2 Commits
		
	
	
		
			278444b620
			...
			a2a9abc4b3
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| a2a9abc4b3 | |||
| 6b1a461e11 | 
| @@ -1,25 +1,21 @@ | ||||
| use crate::error::Error; | ||||
|  | ||||
| use display_interface_i2c::I2CInterface; | ||||
|  | ||||
| use embedded_graphics::{ | ||||
|     prelude::*, | ||||
|     mono_font::{ascii::FONT_7X13, ascii::FONT_10X20, MonoTextStyle}, | ||||
|     mono_font::{ascii::FONT_10X20, MonoTextStyle}, | ||||
|     pixelcolor::BinaryColor, | ||||
|     text::{Text, Alignment}, | ||||
|     prelude::*, | ||||
|     text::{Alignment, Text}, | ||||
| }; | ||||
|  | ||||
| use esp_idf_hal::{ | ||||
|     self, | ||||
|     prelude::Peripherals,  | ||||
|     units::Hertz, | ||||
|     i2c::{I2cConfig, I2cDriver, I2c}  | ||||
| use esp_idf_hal::i2c::I2cDriver; | ||||
|  | ||||
| use ssd1306::{ | ||||
|     prelude::*, rotation::DisplayRotation, size::DisplaySize, I2CDisplayInterface, Ssd1306, | ||||
| }; | ||||
|  | ||||
| use ssd1306::{prelude::*, I2CDisplayInterface, Ssd1306}; | ||||
|  | ||||
| use display_interface_i2c; | ||||
|  | ||||
|  | ||||
| /// Enum to make clear the stage of failure of the display | ||||
| // clear the stage of failure of the display | ||||
| #[derive(Debug)] | ||||
| pub enum DisplayError { | ||||
|     SetupError(display_interface::DisplayError), | ||||
| @@ -28,45 +24,46 @@ pub enum DisplayError { | ||||
| } | ||||
|  | ||||
| pub struct Display<'a, SIZE: DisplaySize> { | ||||
|     display: Ssd1306<I2CInterface<I2cDriver<'a>>, SIZE, ssd1306::mode::BufferedGraphicsMode<SIZE>> | ||||
|     display: Ssd1306<I2CInterface<I2cDriver<'a>>, SIZE, ssd1306::mode::BufferedGraphicsMode<SIZE>>, | ||||
| } | ||||
|  | ||||
| impl<'a, SIZE: DisplaySize> Display<'a, SIZE> | ||||
| { | ||||
| impl<'a, SIZE: DisplaySize> Display<'a, SIZE> { | ||||
|     /// Function to create a new display interface abstraction | ||||
|     pub fn new(i2c: I2cDriver<'a>, size: SIZE, rotation: DisplayRotation) -> Result<Display<'a, SIZE>, Error> { | ||||
|     pub fn new( | ||||
|         i2c: I2cDriver<'a>, | ||||
|         size: SIZE, | ||||
|         rotation: DisplayRotation, | ||||
|     ) -> Result<Display<'a, SIZE>, Error> { | ||||
|         // Construct the I2C driver into a display interface | ||||
|         let interface = I2CDisplayInterface::new(i2c); | ||||
|  | ||||
|         // Construct display with new anew driver | ||||
|         let mut display = Ssd1306::new(interface, size, rotation) | ||||
|             .into_buffered_graphics_mode(); | ||||
|         display.init().map_err(|err| Error::DisplayError(DisplayError::SetupError(err)))?; | ||||
|         let mut display = Ssd1306::new(interface, size, rotation).into_buffered_graphics_mode(); | ||||
|         display | ||||
|             .init() | ||||
|             .map_err(|err| Error::DisplayError(DisplayError::SetupError(err)))?; | ||||
|  | ||||
|         Ok(Display{display}) | ||||
|         Ok(Display { display }) | ||||
|     } | ||||
|  | ||||
|     /// Function to draw a given set of text to a display | ||||
|     pub fn draw(&mut self, text: &str) -> Result<(), Error> { | ||||
|         // Clear display ready for next write | ||||
|         let style = MonoTextStyle::new(&FONT_10X20, BinaryColor::On); | ||||
|         self.display.clear(BinaryColor::Off) | ||||
|         .map_err(|_| Error::DisplayError(DisplayError::DrawingError))?; | ||||
|         self.display | ||||
|             .clear(BinaryColor::Off) | ||||
|             .map_err(|_| Error::DisplayError(DisplayError::DrawingError))?; | ||||
|  | ||||
|         // Draw text to Display | ||||
|         Text::with_alignment( | ||||
|             text, | ||||
|             Point::new(64, 20), | ||||
|             style, | ||||
|             Alignment::Center, | ||||
|         ) | ||||
|         .draw(&mut self.display) | ||||
|         .map_err(|_| Error::DisplayError(DisplayError::DrawingError))?; | ||||
|         Text::with_alignment(text, Point::new(64, 20), style, Alignment::Center) | ||||
|             .draw(&mut self.display) | ||||
|             .map_err(|_| Error::DisplayError(DisplayError::DrawingError))?; | ||||
|  | ||||
|         //Flush data to the display | ||||
|         self.display.flush() | ||||
|         .map_err(|_| Error::DisplayError(DisplayError::FlushError))?; | ||||
|         self.display | ||||
|             .flush() | ||||
|             .map_err(|_| Error::DisplayError(DisplayError::FlushError))?; | ||||
|  | ||||
|         Ok(()) | ||||
|     } | ||||
| } | ||||
| } | ||||
|   | ||||
| @@ -16,12 +16,13 @@ pub struct GpsData { | ||||
|     /// Altitude reported from GPS location | ||||
|     altitude: Option<f64>, | ||||
|     /// Timestamp of the last report | ||||
|     timestamp: Option<DateTime<Utc>> | ||||
|     timestamp: Option<DateTime<Utc>>, | ||||
| } | ||||
|  | ||||
| impl ToString for GpsData { | ||||
|     fn to_string(&self) -> String { | ||||
|         format!("Latitude: {:.4?} \nLongitude: {:.4?} \nSpeed: {:.1?}mph \nAltitude: {:.1?}m",  | ||||
|         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(), | ||||
| @@ -40,7 +41,8 @@ impl GpsData { | ||||
|  | ||||
|         // Format the nmea buffer into a usable string | ||||
|         let nmea_raw = String::from_utf8(buf.to_owned()) | ||||
|             .map_err(|_| GpsError::ReadError()).unwrap_or_default(); | ||||
|             .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 | ||||
| @@ -49,17 +51,19 @@ impl GpsData { | ||||
|             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; } | ||||
|                 Err(_) => { | ||||
|                     continue; | ||||
|                 } | ||||
|             }; | ||||
|              | ||||
|  | ||||
|             // print decoded gps data to serial | ||||
|             match nmea { | ||||
|                 // TODO: Investigate why the GGA Packets seem to come in mangled | ||||
| @@ -67,7 +71,7 @@ impl GpsData { | ||||
|                     // Update Altitude above ground level | ||||
|                     self.altitude = match gga.altitude { | ||||
|                         Some(_) => gga.altitude, | ||||
|                         None => self.altitude  | ||||
|                         None => self.altitude, | ||||
|                     }; | ||||
|                 } | ||||
|                 nmea_parser::ParsedMessage::Gll(gll) => { | ||||
| @@ -88,14 +92,16 @@ impl GpsData { | ||||
|                             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())), | ||||
|                         Some(_) => { | ||||
|                             self.speed = Some(knots_to_mph(rms.sog_knots.unwrap_or_default())) | ||||
|                         } | ||||
|                         None => {} | ||||
|                     } | ||||
|                 }, | ||||
|                 } | ||||
|                 _ => {} | ||||
|             } | ||||
|         } | ||||
| @@ -104,15 +110,12 @@ impl GpsData { | ||||
|     } | ||||
|  | ||||
|     /// Function to get a &str of the current speed reported by the GPS module | ||||
|     pub fn get_speed(&self) -> String { | ||||
|         match self.speed {   | ||||
|             Some(speed) => format!("{:.2?}mph\n", speed), | ||||
|             None => format!("AWAITING\nGPS\nDATA") | ||||
|         } | ||||
|     pub fn get_speed(&self) -> Option<f64> { | ||||
|         self.speed | ||||
|     } | ||||
| } | ||||
|  | ||||
| /// Function to simply convert knots to mph | ||||
| fn knots_to_mph(knots: f64) -> f64{ | ||||
| fn knots_to_mph(knots: f64) -> f64 { | ||||
|     knots * 1.150779 | ||||
| } | ||||
|   | ||||
							
								
								
									
										24
									
								
								src/main.rs
									
									
									
									
									
								
							
							
						
						
									
										24
									
								
								src/main.rs
									
									
									
									
									
								
							| @@ -14,10 +14,10 @@ use error::Error; | ||||
| mod appstate; | ||||
|  | ||||
| mod gps; | ||||
| use gps::{gpsdata, GPS}; | ||||
| use gps::{gpsdata::GpsData, GPS}; | ||||
|  | ||||
| mod display; | ||||
| use display::{Display, DisplayError}; | ||||
| use display::Display; | ||||
| use ssd1306::prelude::*; | ||||
|  | ||||
| fn main() -> Result<(), Error> { | ||||
| @@ -42,20 +42,26 @@ fn main() -> Result<(), Error> { | ||||
|     let i2c = | ||||
|         I2cDriver::new(peripherals.i2c0, sda, scl, &config).map_err(|err| Error::EspError(err))?; | ||||
|  | ||||
|     // Prepare components for processing | ||||
|     let mut gps: GPS = GPS::new(peripherals.uart0, gps_rx)?; | ||||
|     let mut display: Display<DisplaySize128x64> = | ||||
|         Display::new(i2c, DisplaySize128x64, DisplayRotation::Rotate0)?; | ||||
|     let mut app_state = appstate::AppState::default(); | ||||
|     let mut _app_state = appstate::AppState::default(); | ||||
|     const NO_DATA: &str = "NO\nGPS\nDATA"; | ||||
|  | ||||
|     loop { | ||||
|         let text: &str; | ||||
|  | ||||
|         let text: String; | ||||
|         // Get the latest data from GPS module and flush to Display | ||||
|         match gps.poll() { | ||||
|             Ok(res) => display.draw(&res.get_speed().as_str())?, | ||||
|         text = match gps.poll() { | ||||
|             Ok(res) => match res.get_speed() { | ||||
|                 Some(s) => format!("{:.2} MPH", s), | ||||
|                 None => NO_DATA.to_string(), | ||||
|             }, | ||||
|             Err(e) => { | ||||
|                 display.draw("NO\nGPS\nDATA"); | ||||
|                 eprintln!("{:?}", e); | ||||
|                 NO_DATA.to_string() | ||||
|             } | ||||
|         } | ||||
|         }; | ||||
|         display.draw(text.as_str())? | ||||
|     } | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user