diff --git a/src/main.rs b/src/main.rs index 6b04279..8a10165 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,5 +3,17 @@ mod obd2; fn main() { env_logger::init(); let mut device = obd2::Obd2::default(); + println!("VIN: {:?}", device.get_vin()); + + let dtcs = device.get_dtcs(); + println!("DTCs: {:?}", dtcs); + if let Ok(dtcs) = dtcs { + for (i, response) in dtcs.iter().enumerate() { + println!("DTCs from response {}:", i); + for dtc in response { + println!(" - {}", dtc); + } + } + } } diff --git a/src/obd2/mod.rs b/src/obd2/mod.rs index 65635e2..abb3803 100644 --- a/src/obd2/mod.rs +++ b/src/obd2/mod.rs @@ -1,3 +1,5 @@ +use core::fmt; + use log::{debug, trace}; mod device; @@ -10,20 +12,34 @@ pub struct Obd2 { } impl Obd2 { - pub fn obd_command(&mut self, mode: u8, pid: u8) -> Result> { + pub fn obd_command(&mut self, mode: u8, pid: u8) -> Result>> { let result = self.command(&format!("{:02x}{:02x}", mode, pid))?; - if result.first() != Some(&(0x40 | mode)) { - todo!() - } - if result.get(1) != Some(&pid) { - todo!() + for response in result.iter() { + if response.first() != Some(&(0x40 | mode)) { + todo!() + } + if response.get(1) != Some(&pid) { + todo!() + } } - Ok(result.split_at(2).1.to_vec()) + Ok(result.iter().map(|l| l.split_at(2).1.to_vec()).collect()) } - fn command(&mut self, command: &str) -> Result> { + pub fn obd_mode_command(&mut self, mode: u8) -> Result>> { + let result = self.command(&format!("{:02x}", mode))?; + + for response in result.iter() { + if response.first() != Some(&(0x40 | mode)) { + todo!() + } + } + + Ok(result.iter().map(|l| l.split_at(1).1.to_vec()).collect()) + } + + fn command(&mut self, command: &str) -> Result>> { let response = self .device .cmd(command)? @@ -35,18 +51,55 @@ impl Obd2 { response ); - let data = self.parse_command_multiline(response)?; + let data = if response.contains("0:") { + vec![self.parse_command_multiline(response)?] + } else { + self.parse_command(response)? + }; debug!("Sent OBD command {:?} and got data {:?}", command, data); let result = data .iter() - .map(|s| u8::from_str_radix(s, 16).map_err(|e| e.into())) + .map(|l| { + l.iter() + .map(|s| u8::from_str_radix(s, 16).map_err(|e| e.into())) + .collect() + }) .collect(); result } + fn parse_command(&mut self, response: String) -> Result>> { + let result: Vec<_> = response + .split('\n') + .filter_map(|l| { + let res: Vec<_> = l + .split(' ') + .filter_map(|s| { + if !s.is_empty() { + Some(s.to_owned()) + } else { + None + } + }) + .collect(); + if !res.is_empty() { + Some(res) + } else { + None + } + }) + .collect(); + + if !result.is_empty() { + Ok(result) + } else { + Err(Error::Other("parse_command: found no responses".to_owned())) + } + } + fn parse_command_multiline(&mut self, response: String) -> Result> { let mut n_idx = 0; Ok(response @@ -63,10 +116,64 @@ impl Obd2 { } pub fn get_vin(&mut self) -> Result { - let mut result = self.obd_command(0x09, 0x02)?; + let mut result = self.obd_command(0x09, 0x02)?.pop().unwrap(); result.remove(0); // do not know what this byte is Ok(String::from_utf8(result)?) } + + pub fn get_dtcs(&mut self) -> Result>> { + let result = self.obd_mode_command(0x03)?; + result + .iter() + .map(|response| match response.first() { + Some(0) => { + if response.len() % 2 == 1 { + let mut ret = Vec::new(); + for i in (1..response.len()).step_by(2) { + ret.push(match response[i] >> 6 { + 0 => Dtc::Powertrain(0), + 1 => Dtc::Chassis(0), + 2 => Dtc::Body(0), + 3 => Dtc::Network(0), + _ => unreachable!(), + }); + } + Ok(ret) + } else { + Err(Error::Other(format!("invalid response {:?}", response))) + } + } + Some(n) if *n <= 3 => todo!(), + Some(_) => Err(Error::Other(format!( + "invalid response {:?} when getting DTCs", + response + ))), + None => Err(Error::Other( + "no response bytes when getting DTCs".to_owned(), + )), + }) + .collect::>>>() + } +} + +#[derive(Debug)] +pub enum Dtc { + Powertrain(u16), + Chassis(u16), + Body(u16), + Network(u16), +} + +impl fmt::Display for Dtc { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let (c, n) = match self { + Self::Powertrain(n) => ('P', n), + Self::Chassis(n) => ('C', n), + Self::Body(n) => ('B', n), + Self::Network(n) => ('U', n), + }; + f.write_fmt(format_args!("{}{:03X}", c, n)) + } } #[derive(thiserror::Error, Debug)]