Skip to content

Commit

Permalink
Merge pull request #32 from VladLupashevskyi/slcan
Browse files Browse the repository at this point in the history
Add support for SLCAN devices
  • Loading branch information
rnd-ash authored Sep 27, 2024
2 parents e73bd74 + eaf81cd commit 3f54649
Show file tree
Hide file tree
Showing 7 changed files with 1,235 additions and 5 deletions.
6 changes: 4 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ description = "A rust crate for ECU diagnostic servers and communication APIs"
license = "MIT"
repository = "https://github.com/rnd-ash/ecu_diagnostics"
readme = "README.md"
keywords = ["socketcan", "kwp2000", "uds", "j2534", "dpdu"]
keywords = ["socketcan", "kwp2000", "uds", "j2534", "dpdu", "slcan"]
exclude = [
"examples/*",
"build.rs",
Expand All @@ -22,9 +22,10 @@ all-features = true
targets = ["x86_64-unknown-linux-gnu", "i686-pc-windows-msvc", "x86_64-apple-darwin"]

[features]
default = ["passthru", "socketcan"]
default = ["passthru", "socketcan", "slcan"]
socketcan = ["dep:socketcan-isotp", "dep:socketcan"]
passthru = ["dep:libloading", "dep:shellexpand", "dep:winreg", "dep:serde_json", "dep:j2534_rust"]
slcan = ["dep:serial-rs"]

[dependencies]
#automotive_diag = { version = "0.1", path = "../automotive_diag" }
Expand All @@ -36,6 +37,7 @@ log="0.4.16"
strum = "0.26.3"
strum_macros = "0.26.4"
thiserror="1.0.44"
serial-rs = { version = "0.2.1", optional = true }

[dev-dependencies]
env_logger = "0.11.3"
Expand Down
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,10 @@ for creating Channels for diagnostic servers using the hardware
* ISO-TP
* CAN

### SLCAN
* ISO-TP
* CAN

### D-PDU (ISO 22900-2)
TBA

Expand Down
135 changes: 135 additions & 0 deletions examples/kwp_slcan.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
use std::time::Duration;

use automotive_diag::kwp2000::{KwpSessionType, KwpSessionTypeByte};
use serial_rs::{FlowControl, SerialPortSettings};

use ecu_diagnostics::{
channel::IsoTPSettings,
dynamic_diag::{
DiagProtocol, DiagServerAdvancedOptions, DiagServerBasicOptions, DiagServerEmptyLogger, DiagSessionMode, DynamicDiagSession, TimeoutConfig
},
hardware::Hardware,
kwp2000::Kwp2000Protocol,
};

extern crate ecu_diagnostics;
extern crate serial_rs;

fn ecu_waiting_hook() {
println!("Called hook! ECU is processing our request");
}

fn tx_ok_hook(data: &[u8]) {
println!(
"This {} long array was sent to the ECU OK!: {:02X?}",
data.len(),
data
);
}

fn print_diag_mode(server: &DynamicDiagSession) {
if let Some(mode) = server.get_current_diag_mode() {
println!(
"ECU is currently in '{}' diagnostic mode (0x{:02X?}). Tester present being sent?: {}",
mode.name, mode.id, mode.tp_require
);
}
}


fn main() {
env_logger::builder()
.format_timestamp(Some(env_logger::TimestampPrecision::Millis))
.init();

let port = serial_rs::new_from_path(
"/dev/cu.usbmodem90379201".into(),
Some(
SerialPortSettings::default()
.baud(2000000)
.read_timeout(Some(5))
.write_timeout(Some(100))
.set_flow_control(FlowControl::None)
.set_blocking(true),
),
).unwrap();

let mut d = ecu_diagnostics::hardware::slcan::device::SlCanDevice::new(port, 1000);

let isotp = d.create_iso_tp_channel().unwrap();

let mut protocol = Kwp2000Protocol::default();
println!("Diagnostic server is {}!", protocol.get_protocol_name());
// Register a custom diagnostic session with the protocol (Usually OEM specific)
protocol.register_session_type(DiagSessionMode {
id: 0x93,
tp_require: true,
name: "SuperSecretDiagMode".into(),
});

let mut diag_server = ecu_diagnostics::dynamic_diag::DynamicDiagSession::new_over_iso_tp(
protocol,
isotp,
IsoTPSettings {
// ISO-TP layer settings
block_size: 8,
st_min: 20,
extended_addresses: None,
pad_frame: true,
can_speed: 500_000,
can_use_ext_addr: false,
},
DiagServerBasicOptions {
// Basic server options
send_id: 0x07E1,
recv_id: 0x07E9,
timeout_cfg: TimeoutConfig {
read_timeout_ms: 2500,
write_timeout_ms: 2500,
},
},
Some(DiagServerAdvancedOptions {
// Advanced server options
global_tp_id: 0,
tester_present_interval_ms: 2000,
tester_present_require_response: true,
global_session_control: false,
tp_ext_id: None,
command_cooldown_ms: 100,
}),
DiagServerEmptyLogger{}
)
.unwrap();

// This call would work for KWP or UDS, not OBD2 as OBD2 has no form of 'session control'
if let Some(mode) = diag_server.get_current_diag_mode() {
println!(
"ECU is currently in '{}' diagnostic mode (0x{:02X?}). Tester present being sent?: {}",
mode.name, mode.id, mode.tp_require
);
}

// Register hook for when ECU responsds with RequestCorrectlyReceivedResponsePending
diag_server.register_waiting_hook(|| ecu_waiting_hook());
// Register hook for when our requests are sent to the ECU, but we have not got a response. Usually
// this can be used to just let the program know Tx was OK!
diag_server.register_send_complete_hook(|bytes| tx_ok_hook(bytes));
// Set diag session mode
let res = diag_server.kwp_set_session(KwpSessionType::ExtendedDiagnostics.into());
println!("Into extended diag mode result: {:?}", res);
// Now check diag session mode, should be extended
print_diag_mode(&diag_server);
let res = diag_server.kwp_set_session(KwpSessionTypeByte::from(0x93)); // Same ID as what we registered at the start
println!("Into special diag mode result: {:?}", res);
print_diag_mode(&diag_server);
println!("Reset result: {:?}", diag_server.kwp_reset_ecu(automotive_diag::kwp2000::ResetType::PowerOnReset));
print_diag_mode(&diag_server); // ECU should be in standard mode now as the ECU was rebooted
std::thread::sleep(Duration::from_millis(500));
println!("Read op: {:?}", diag_server.kwp_enable_normal_message_transmission());
print_diag_mode(&diag_server); // ECU will automatically be put into 0x93 mode
// (Last requested mode as enable_normal_message_transmission cannot be ran in standard mode)
loop {
// TP will be sent in this mode forever
std::thread::sleep(Duration::from_millis(1000));
}
}
45 changes: 45 additions & 0 deletions examples/slcan_can.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
use serial_rs::{FlowControl, SerialPortSettings};
use ecu_diagnostics::{channel::CanFrame, hardware::Hardware};

extern crate ecu_diagnostics;
extern crate serial_rs;

fn main() {
env_logger::builder()
.format_timestamp(Some(env_logger::TimestampPrecision::Millis))
.init();

let port = serial_rs::new_from_path(
"/dev/cu.usbmodem90379201".into(),
Some(
SerialPortSettings::default()
.baud(2000000)
.read_timeout(Some(1))
.write_timeout(Some(100))
.set_flow_control(FlowControl::None)
.set_blocking(true),
),
).unwrap();

let mut d = ecu_diagnostics::hardware::slcan::device::SlCanDevice::new(port, 1000);

let mut can = d.create_can_channel().unwrap();

can.set_can_cfg(83_333, false).unwrap();

can.open().unwrap();

let packets = can.read_packets(100, 500).unwrap();

for p in packets {
println!("{:02X?}", p);
}

can.write_packets(vec![CanFrame::new(0x5B4, vec![2, 0x10, 0x92, 0, 0,0,0].as_ref(), false)], 100).unwrap();

let packets = can.read_packets(100, 500).unwrap();

for p in packets {
println!("{:02X?}", p);
}
}
7 changes: 4 additions & 3 deletions src/hardware/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,14 @@ mod dpdu;
#[cfg(feature = "passthru")]
pub mod passthru; // Not finished at all yet, hide from the crate

#[cfg(feature = "passthru")]
use std::sync::Arc;
use std::{any::{Any, TypeId}, fmt::Debug, sync::{Mutex, PoisonError, RwLock}};
use std::{any::{Any, TypeId}, fmt::Debug, sync::{Arc, PoisonError, RwLock}};

#[cfg(all(feature="socketcan", target_os="linux"))]
pub mod socketcan;

#[cfg(feature = "slcan")]
pub mod slcan;

use crate::channel::{CanChannel, IsoTPChannel};

/// Hardware API result
Expand Down
Loading

0 comments on commit 3f54649

Please sign in to comment.