diff --git a/Cargo.toml b/Cargo.toml index bfa0625..b05ba2e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,11 +8,7 @@ edition = "2021" [dependencies] env_logger = "0.10" +ftdi = "0.1.3" log = "0.4.8" thiserror = "1.0.15" -ftdi = { version = "0.1.3", optional = true } -serialport= { version = "=4.6.1", optional = true } - -[features] -ftdi_comm = [ "dep:ftdi" ] -serialport_comm = [ "dep:serialport" ] +serialport="=4.6.1" diff --git a/src/commands/mod.rs b/src/commands/mod.rs index 1265eb9..6d60b1a 100644 --- a/src/commands/mod.rs +++ b/src/commands/mod.rs @@ -220,4 +220,7 @@ func! { /// Get service 1 PID support for $21 to $40 fn get_service_1_pid_support_2(0x01, 0x20) -> u32; + + // Get the fuel level (out of 255) + fn get_fuel_level(0x01, 0x2F) -> u8; } diff --git a/src/commands/types.rs b/src/commands/types.rs index 75f1f21..5c10d8b 100644 --- a/src/commands/types.rs +++ b/src/commands/types.rs @@ -42,7 +42,7 @@ impl From for Dtc { 1 => Dtc::Chassis(n), 2 => Dtc::Body(n), 3 => Dtc::Network(n), - _ => unreachable!(), // can't happen, only two bits + _ => unreachable!(), } } } diff --git a/src/device/elm327.rs b/src/device/elm327.rs index 2352fe3..735aab4 100644 --- a/src/device/elm327.rs +++ b/src/device/elm327.rs @@ -30,7 +30,7 @@ impl Obd2BaseDevice for Elm327 { fn send_cmd(&mut self, data: &[u8]) -> Result<()> { trace!("send_cmd: sending {:?}", std::str::from_utf8(data)); self.send_serial_str( - data.iter() + data.into_iter() .flat_map(|v| format!("{:02X}", v).chars().collect::>()) .collect::() .as_str(), @@ -115,18 +115,13 @@ impl Elm327 { fn reset_protocol(&mut self) -> Result<()> { info!("Performing protocol reset"); - - // set to use automatic protocol selection let elm_response = self.serial_cmd("ATSP0")?; debug!("reset_protocol: got response {:?}", elm_response); - // perform the search for ECUs let obd_response = self.cmd(&[0x01, 0x00])?; debug!("reset_protocol: got OBD response {:?}", obd_response); - // get rid of extra data hanging around in the buffer self.flush_buffers()?; - Ok(()) } diff --git a/src/device/ftdi_comm.rs b/src/device/ftdi_comm.rs deleted file mode 100644 index b65afdb..0000000 --- a/src/device/ftdi_comm.rs +++ /dev/null @@ -1,42 +0,0 @@ -use super::serial_comm::{SerialComm, DEFAULT_BAUD_RATE}; -use super::Result; -use std::io::{Read, Write}; - -/// Communicate with a USB to Serial FTDI device -/// with the FTDI library -pub struct FTDIDevice { - device: ftdi::Device, -} - -impl FTDIDevice { - /// Creates a new instance of an FTDIDevice - pub fn new() -> Result { - let mut device = ftdi::find_by_vid_pid(0x0404, 0x6001) - .interface(ftdi::Interface::A) - .open()?; - - device.set_baud_rate(DEFAULT_BAUD_RATE)?; - device.configure(ftdi::Bits::Eight, ftdi::StopBits::One, ftdi::Parity::None)?; - device.usb_reset()?; - - Ok(Self { device }) - } -} - -impl SerialComm for FTDIDevice { - fn write_all(&mut self, data: &[u8]) -> Result<()> { - Ok(self.device.write_all(data)?) - } - - fn read(&mut self, data: &mut [u8]) -> Result { - Ok(self.device.read(data)?) - } - - fn set_baud_rate(&mut self, baud_rate: u32) -> Result<()> { - Ok(self.device.set_baud_rate(baud_rate)?) - } - - fn purge_buffers(&mut self) -> Result<()> { - Ok(self.device.usb_purge_buffers()?) - } -} diff --git a/src/device/mod.rs b/src/device/mod.rs index 878d2aa..31f0293 100644 --- a/src/device/mod.rs +++ b/src/device/mod.rs @@ -4,16 +4,7 @@ mod elm327; pub use elm327::Elm327; mod serial_comm; - -#[cfg(feature = "ftdi_comm")] -mod ftdi_comm; -#[cfg(feature = "ftdi_comm")] -pub use ftdi_comm::FTDIDevice; - -#[cfg(feature = "serialport_comm")] -mod serialport_comm; -#[cfg(feature = "serialport_comm")] -pub use serialport_comm::SerialPort; +pub use serial_comm::{FTDIDevice, SerialPort}; type Result = std::result::Result; @@ -63,12 +54,10 @@ pub trait Obd2Reader { #[derive(thiserror::Error, Debug)] pub enum Error { /// An error with the underlying [FTDI device](ftdi::Device) - #[cfg(feature = "ftdi_comm")] #[error("FTDI error: `{0:?}`")] Ftdi(ftdi::Error), /// An error with the underlying [serialport device](serialport::SerialPort) - #[cfg(feature = "serialport_comm")] #[error("Serialport error: `{0:?}`")] Serialport(serialport::Error), @@ -81,14 +70,12 @@ pub enum Error { Communication(String), } -#[cfg(feature = "ftdi_comm")] impl From for Error { fn from(e: ftdi::Error) -> Self { Error::Ftdi(e) } } -#[cfg(feature = "serialport_comm")] impl From for Error { fn from(e: serialport::Error) -> Self { Error::Serialport(e) diff --git a/src/device/serial_comm.rs b/src/device/serial_comm.rs index 8c31b50..0ab6467 100644 --- a/src/device/serial_comm.rs +++ b/src/device/serial_comm.rs @@ -1,6 +1,8 @@ use super::Result; +use std::io::{Read, Write}; +use std::time::Duration; -pub const DEFAULT_BAUD_RATE: u32 = 38_400; +const DEFAULT_BAUD_RATE: u32 = 38_400; /// An API to communicate with a serial device pub trait SerialComm { @@ -9,3 +11,84 @@ pub trait SerialComm { fn set_baud_rate(&mut self, baud_rate: u32) -> Result<()>; fn purge_buffers(&mut self) -> Result<()>; } + +/// Communicate with a serial device using the +/// serialport library +/// +/// /dev/tty* or similar on unix-like systems +/// COM devices on Windows systems +pub struct SerialPort { + device: Box, +} + +impl SerialPort { + /// Creates a new instance of a SerialPort + pub fn new(path: &str) -> Result { + let device = serialport::new(path, DEFAULT_BAUD_RATE) + .timeout(Duration::from_millis(10)) + .parity(serialport::Parity::None) + .data_bits(serialport::DataBits::Eight) + .stop_bits(serialport::StopBits::One) + .path(path) + .open()?; + + Ok(Self { device }) + } +} + +impl SerialComm for SerialPort { + fn write_all(&mut self, data: &[u8]) -> Result<()> { + Ok(self.device.write_all(data)?) + } + + fn read(&mut self, data: &mut [u8]) -> Result { + Ok(self.device.read(data)?) + } + + fn set_baud_rate(&mut self, baud_rate: u32) -> Result<()> { + Ok(self.device.set_baud_rate(baud_rate)?) + } + + fn purge_buffers(&mut self) -> Result<()> { + Ok(self.device.clear(serialport::ClearBuffer::All)?) + } +} + +/// Communicate with a USB to Serial FTDI device +/// with the FTDI library +pub struct FTDIDevice { + device: ftdi::Device, +} + +impl FTDIDevice { + /// Creates a new instance of an FTDIDevice + pub fn new() -> Result { + let mut device = ftdi::find_by_vid_pid(0x0404, 0x6001) + .interface(ftdi::Interface::A) + .open()?; + + device.set_baud_rate(DEFAULT_BAUD_RATE)?; + device.configure(ftdi::Bits::Eight, ftdi::StopBits::One, ftdi::Parity::None)?; + device.usb_reset()?; + + Ok(Self { device }) + } +} + +impl SerialComm for FTDIDevice { + fn write_all(&mut self, data: &[u8]) -> Result<()> { + Ok(self.device.write_all(data)?) + } + + fn read(&mut self, data: &mut [u8]) -> Result { + Ok(self.device.read(data)?) + } + + fn set_baud_rate(&mut self, baud_rate: u32) -> Result<()> { + Ok(self.device.set_baud_rate(baud_rate)?) + } + + fn purge_buffers(&mut self) -> Result<()> { + Ok(self.device.usb_purge_buffers()?) + } +} diff --git a/src/device/serialport_comm.rs b/src/device/serialport_comm.rs deleted file mode 100644 index 1ff12fb..0000000 --- a/src/device/serialport_comm.rs +++ /dev/null @@ -1,46 +0,0 @@ -use super::serial_comm::{SerialComm, DEFAULT_BAUD_RATE}; -use super::Result; -use std::io::{Read, Write}; -use std::time::Duration; - -/// Communicate with a serial device using the -/// serialport library -/// -/// /dev/tty* or similar on unix-like systems -/// COM devices on Windows systems -pub struct SerialPort { - device: Box, -} - -impl SerialPort { - /// Creates a new instance of a SerialPort - pub fn new(path: &str) -> Result { - let device = serialport::new(path, DEFAULT_BAUD_RATE) - .timeout(Duration::from_millis(10)) - .parity(serialport::Parity::None) - .data_bits(serialport::DataBits::Eight) - .stop_bits(serialport::StopBits::One) - .path(path) - .open()?; - - Ok(Self { device }) - } -} - -impl SerialComm for SerialPort { - fn write_all(&mut self, data: &[u8]) -> Result<()> { - Ok(self.device.write_all(data)?) - } - - fn read(&mut self, data: &mut [u8]) -> Result { - Ok(self.device.read(data)?) - } - - fn set_baud_rate(&mut self, baud_rate: u32) -> Result<()> { - Ok(self.device.set_baud_rate(baud_rate)?) - } - - fn purge_buffers(&mut self) -> Result<()> { - Ok(self.device.clear(serialport::ClearBuffer::All)?) - } -} diff --git a/src/error.rs b/src/error.rs index 7368b97..f6afd75 100644 --- a/src/error.rs +++ b/src/error.rs @@ -21,7 +21,7 @@ pub enum Error { /// An error with the ELM327 device #[derive(Debug)] -pub struct DeviceError(crate::device::Error); +pub struct DeviceError(pub crate::device::Error); impl From for Error { fn from(e: super::device::Error) -> Self { @@ -31,12 +31,12 @@ impl From for Error { impl From for Error { fn from(e: std::num::ParseIntError) -> Self { - Error::Other(format!("invalid data recieved: {:?}", e)) + Error::Other(format!("invalid data received: {:?}", e)) } } impl From for Error { fn from(e: std::string::FromUtf8Error) -> Self { - Error::Other(format!("invalid string recieved: {:?}", e)) + Error::Other(format!("invalid string received: {:?}", e)) } } diff --git a/src/interface.rs b/src/interface.rs index a73b7df..724ae15 100644 --- a/src/interface.rs +++ b/src/interface.rs @@ -16,11 +16,9 @@ impl Obd2Device for Obd2 { for response in result.iter() { if response.first() != Some(&(0x40 | mode)) { - // mismatch of mode in response todo!() } if response.get(1) != Some(&pid) { - // mismatch of PID in response todo!() } } @@ -122,8 +120,7 @@ impl Obd2 { .filter_map(|l| l.split_once(':')) .flat_map(|(idx, data)| { if u8::from_str_radix(idx, 16) != Ok(n_idx) { - // got an invalid hex code or values were not already in the correct order - todo!("Line index: {}, should be {:X}", idx, n_idx) + todo!() } n_idx = (n_idx + 1) % 0x10; data.split_whitespace().map(|s| s.to_owned()) diff --git a/src/lib.rs b/src/lib.rs index 7b9805d..2e04c5b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -22,7 +22,7 @@ //! ``` #![forbid(unsafe_code)] -#![warn(missing_docs, clippy::panic)] +#![warn(missing_docs)] pub mod commands; diff --git a/src/obd2_device.rs b/src/obd2_device.rs index b7268fd..219855e 100644 --- a/src/obd2_device.rs +++ b/src/obd2_device.rs @@ -13,7 +13,7 @@ pub trait Obd2Device { /// /// The responses are a list with one element for each ECU that responds. The data is decoded /// into the ODB-II bytes from the vehicle and the first byte of the response---representing - /// the mode the vehicle received---is validated and removed. + /// the mode the vehicle recieved---is validated and removed. fn obd_mode_command(&mut self, mode: u8) -> Result>>; /// Send command and get list of OBD-II responses as an array