diff --git a/Cargo.toml b/Cargo.toml index b05ba2e..ec43f5f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,4 +11,3 @@ env_logger = "0.10" ftdi = "0.1.3" log = "0.4.8" thiserror = "1.0.15" -serialport="=4.6.1" diff --git a/examples/basic/main.rs b/examples/basic/main.rs index fb83144..f396bb2 100644 --- a/examples/basic/main.rs +++ b/examples/basic/main.rs @@ -2,19 +2,15 @@ use obd2::commands::Obd2DataRetrieval; use std::time; -fn main() -> Result<(), obd2::Error> { +fn main() { env_logger::init(); - let mut device: obd2::Obd2> = obd2::Obd2::new( - obd2::device::Elm327::new( - obd2::device::FTDIDevice::new()? - )? - )?; + let mut device: obd2::Obd2 = obd2::Obd2::default(); println!("VIN: {:?}", device.get_vin()); - for s in device.get_service_1_pid_support_1()?.iter() { + for s in device.get_service_1_pid_support_1().unwrap().iter() { println!("PID support ($01-$20): {:08X}", s); } - for s in device.get_service_1_pid_support_2()?.iter() { + for s in device.get_service_1_pid_support_2().unwrap().iter() { println!("PID support ($21-$40): {:08X}", s); } @@ -51,6 +47,4 @@ fn main() -> Result<(), obd2::Error> { device.get_throttle_position() ); } - - Ok(()) } diff --git a/src/commands/mod.rs b/src/commands/mod.rs index 443d8f0..1265eb9 100644 --- a/src/commands/mod.rs +++ b/src/commands/mod.rs @@ -220,7 +220,4 @@ 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/device/elm327.rs b/src/device/elm327.rs index 8dd3513..83543a6 100644 --- a/src/device/elm327.rs +++ b/src/device/elm327.rs @@ -1,27 +1,34 @@ use log::{debug, info, trace}; use std::{ collections::VecDeque, + io::{Read, Write}, thread, time, }; -use super::{Error, Obd2BaseDevice, Obd2Reader, Result, serial_comm::SerialComm}; +use super::{Error, Obd2BaseDevice, Obd2Reader, Result}; /// An ELM327 OBD-II adapter /// -/// It communicates with the computer or UART using an FTDI FT232R USB-to-UART converter. +/// It communicates with the computer over UART using an FTDI FT232R USB-to-UART converter. /// Commands to the device itself are indicated by sending "AT" followed by the command, while /// plain strings of hex data indicate OBD-II requests to be sent to the vehicle. The responses of /// the vehicle are echoed back as hex characters. Capitalization and spaces are always ignored. /// /// [Datasheet for v1.4b](https://github.com/rsammelson/obd2/blob/master/docs/ELM327DSH.pdf), and /// the [source](https://www.elmelectronics.com/products/dsheets/). -pub struct Elm327 { - device: T, +pub struct Elm327 { + device: ftdi::Device, buffer: VecDeque, baud_rate: u32, } -impl Obd2BaseDevice for Elm327 { +impl Default for Elm327 { + fn default() -> Self { + Elm327::new().unwrap() + } +} + +impl Obd2BaseDevice for Elm327 { fn reset(&mut self) -> Result<()> { self.flush_buffers()?; self.reset_ic()?; @@ -41,7 +48,7 @@ impl Obd2BaseDevice for Elm327 { } } -impl Obd2Reader for Elm327 { +impl Obd2Reader for Elm327 { fn get_line(&mut self) -> Result>> { self.get_until(b'\n', false) } @@ -57,14 +64,22 @@ impl Obd2Reader for Elm327 { } } -impl Elm327 { - /// Creates a new Elm327 adapter with the given - /// unserlying Serial Communication device - pub fn new(serial_device: T) -> Result { +impl Elm327 { + fn new() -> Result { + let mut ftdi_device = ftdi::find_by_vid_pid(0x0403, 0x6001) + .interface(ftdi::Interface::A) + .open()?; + + ftdi_device.set_baud_rate(38400)?; + ftdi_device.configure(ftdi::Bits::Eight, ftdi::StopBits::One, ftdi::Parity::None)?; + // device.set_latency_timer(2).unwrap(); + + ftdi_device.usb_reset()?; + let mut device = Elm327 { - device: serial_device, + device: ftdi_device, buffer: VecDeque::new(), - baud_rate: 38_400, + baud_rate: 38400, }; device.connect(false)?; @@ -83,7 +98,7 @@ impl Elm327 { } fn flush_buffers(&mut self) -> Result<()> { - self.device.purge_buffers()?; + self.device.usb_purge_buffers()?; Ok(()) } @@ -108,10 +123,9 @@ impl Elm327 { fn reset_ic(&mut self) -> Result<()> { info!("Performing IC reset"); self.send_serial_str("ATZ")?; - let response = self.get_response()?; debug!( "reset_ic: got response {:?}", - response + self.get_response()? .as_ref() .map(|l| std::str::from_utf8(l.as_slice())) ); @@ -120,18 +134,14 @@ impl Elm327 { fn reset_protocol(&mut self) -> Result<()> { info!("Performing protocol reset"); - let elm_response = self.serial_cmd("ATSP0")?; debug!( "reset_protocol: got response {:?}", - elm_response + self.serial_cmd("ATSP0")? ); - - let obd_response = self.cmd(&[0x01, 0x00])?; debug!( "reset_protocol: got OBD response {:?}", - obd_response + self.cmd(&[0x01, 0x00])? ); - self.flush_buffers()?; Ok(()) } @@ -248,18 +258,13 @@ impl Elm327 { fn read_into_queue(&mut self) -> Result<()> { let mut buf = [0u8; 16]; loop { - let len_res = self.device.read(&mut buf); - if let Ok(len) = len_res { - if len > 0 { - self.buffer.extend(&buf[0..len]); - trace!( - "read_into_queue: values {:?}", - std::str::from_utf8(&buf[0..len]) - ); - } else { - trace!("read_into_queue: no values left to read"); - break; - } + let len = self.device.read(&mut buf)?; + if len > 0 { + self.buffer.extend(&buf[0..len]); + trace!( + "read_into_queue: values {:?}", + std::str::from_utf8(&buf[0..len]) + ); } else { trace!("read_into_queue: no values left to read"); break; diff --git a/src/device/mod.rs b/src/device/mod.rs index 96fca1c..1f0f561 100644 --- a/src/device/mod.rs +++ b/src/device/mod.rs @@ -3,9 +3,6 @@ mod elm327; pub use elm327::Elm327; -mod serial_comm; -pub use serial_comm::{SerialPort, FTDIDevice}; - type Result = std::result::Result; /// A lower-level API for using an OBD-II device @@ -56,10 +53,6 @@ pub enum Error { /// An error with the underlying [FTDI device](ftdi::Device) #[error("FTDI error: `{0:?}`")] Ftdi(ftdi::Error), - - /// An error with the underlying [serialport device](serialport::SerialPort) - #[error("Serialport error: `{0:?}`")] - Serialport(serialport::Error), /// An I/O error in a low-level [std::io] stream operation #[error("IO error: `{0:?}`")] @@ -76,12 +69,6 @@ impl From for Error { } } -impl From for Error { - fn from(e: serialport::Error) -> Self { - Error::Serialport(e) - } -} - impl From for Error { fn from(e: std::io::Error) -> Self { Error::IO(e) diff --git a/src/device/serial_comm.rs b/src/device/serial_comm.rs deleted file mode 100644 index 0ee1dd1..0000000 --- a/src/device/serial_comm.rs +++ /dev/null @@ -1,95 +0,0 @@ -use std::time::Duration; -use super::Result; -use std::io::{Read, Write}; - -const DEFAULT_BAUD_RATE: u32 = 38_400; - -/// An API to communicate with a serial device -pub trait SerialComm { - fn write_all(&mut self, data: &[u8]) -> Result<()>; - fn read(&mut self, data: &mut [u8]) -> Result; - 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/error.rs b/src/error.rs index 3498595..6feac68 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,6 +1,3 @@ -//! Error types for OBD-II related errors - -/// Result type defaulted with this library's error type pub type Result = std::result::Result; /// An error with OBD-II communication @@ -19,9 +16,8 @@ pub enum Error { Other(String), } -/// An error with the ELM327 device #[derive(Debug)] -pub struct DeviceError(pub crate::device::Error); +pub struct DeviceError(crate::device::Error); impl From for Error { fn from(e: super::device::Error) -> Self { @@ -31,12 +27,12 @@ impl From for Error { impl From for Error { fn from(e: std::num::ParseIntError) -> Self { - Error::Other(format!("invalid data received: {:?}", e)) + Error::Other(format!("invalid data recieved: {:?}", e)) } } impl From for Error { fn from(e: std::string::FromUtf8Error) -> Self { - Error::Other(format!("invalid string received: {:?}", e)) + Error::Other(format!("invalid string recieved: {:?}", e)) } } diff --git a/src/interface.rs b/src/interface.rs index ae728fa..5d49c0b 100644 --- a/src/interface.rs +++ b/src/interface.rs @@ -6,6 +6,7 @@ use super::{device::Obd2BaseDevice, Error, Obd2Device, Result}; /// /// Wraps an implementer of [Obd2BaseDevice] to allow for higher-level usage of the OBD-II /// interface. +#[derive(Default)] pub struct Obd2 { device: T, } @@ -40,20 +41,6 @@ impl Obd2Device for Obd2 { } impl Obd2 { - /// Creates a new instance of an Obd device - pub fn new(dev: T) -> Result { - let device = Obd2 { - device: dev - }; - - Ok(device) - } - - /// Resets the device - pub fn reset(&mut self) -> Result<()> { - Ok(self.device.reset()?) - } - fn command(&mut self, command: &[u8]) -> Result>> { let response = self .device diff --git a/src/lib.rs b/src/lib.rs index 10bc301..b0b6261 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,20 +6,14 @@ //! //! # Usage //! ``` -//! use obd2::{commands::Obd2DataRetrieval, device::{Elm327, FTDIDevice}, Obd2}; +//! use obd2::{commands::Obd2DataRetrieval, device::Elm327, Obd2}; //! //! fn main() -> Result<(), obd2::Error> { -//! let mut device = Obd2::>::new(Elm327::new(FTDIDevice::new()?)?)?; +//! let mut device = Obd2::::default(); //! println!("VIN: {}", device.get_vin()?); //! Ok(()) //! } //! ``` -//! -//! alternatively, you could use a serial port provided by your operating system such as -//! /dev/ttyUSB0 on unix-like systems -//! ``` -//! let mut device = Obd2::>::new(Elm327::new(SerialPort::new("/dev/ttyUSB0")?)?)?; -//! ``` #![forbid(unsafe_code)] #![warn(missing_docs)] @@ -28,7 +22,7 @@ pub mod commands; pub mod device; -pub mod error; +mod error; pub use error::Error; use error::Result;