Skip to content

Commit

Permalink
chore: add tokio support
Browse files Browse the repository at this point in the history
  • Loading branch information
acpiccolo committed Mar 11, 2024
1 parent 68583fa commit a1dd097
Show file tree
Hide file tree
Showing 6 changed files with 281 additions and 23 deletions.
7 changes: 6 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,10 @@ path = "src/main.rs"
required-features = ["bin-dependencies"]

[features]
serde = ["dep:serde"]
default = ["bin-dependencies"]
serialport = ["dep:serialport", "dep:anyhow"]
tokio-async = ["dep:tokio-serial", "tokio/time", "tokio/io-util", "dep:anyhow"]
bin-dependencies = [
"dep:anyhow",
"serialport",
Expand All @@ -26,10 +28,13 @@ bin-dependencies = [
]

[dependencies]
serde = { version = "1", features = ["derive"], optional = true }
serialport = { version = "4", optional = true }
tokio-serial = { version = "5", optional = true }
tokio = { version = "1", default-features = false, optional = true }
# Requirements for bin
log = { version = "0.4" }
anyhow = { version = "1", optional = true }
serialport = { version = "4", optional = true }
clap = { version = "4", optional = true }
clap-verbosity-flag = { version = "2", optional = true }
clap-num = { version = "1", optional = true }
Expand Down
3 changes: 3 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,6 @@ pub use error::Error;

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

#[cfg(feature = "tokio-async")]
pub mod tokio_serial;
19 changes: 3 additions & 16 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -114,11 +114,9 @@ fn main() -> Result<()> {

let _log_handle = logging_init(args.verbose.log_level_filter());

// https://minimalmodbus.readthedocs.io/en/stable/serialcommunication.html#timing-of-the-serial-communications
// minimum delay 4ms by baud rate 9600
let delay = Duration::max(args.delay, Duration::from_millis(4));

let mut bms = dalybms_lib::serialport::DalyBMS::new(&args.device, args.timeout)?;
let mut bms = dalybms_lib::serialport::DalyBMS::new(&args.device)?;
bms.set_timeout(args.timeout)?;
bms.set_delay(args.delay);

match args.command {
CliCommands::Status => {
Expand All @@ -132,39 +130,28 @@ fn main() -> Result<()> {
}
CliCommands::CellVoltages => {
let _ = bms.get_status()?;
std::thread::sleep(delay);
println!("CellVoltages: {:?}", bms.get_cell_voltages()?);
}
CliCommands::Temperatures => {
let _ = bms.get_status()?;
std::thread::sleep(delay);
println!("Temperatures: {:?}", bms.get_cell_temperatures()?);
}
CliCommands::Balancing => {
let _ = bms.get_status()?;
std::thread::sleep(delay);
println!("Balancing: {:?}", bms.get_balancing_status()?);
}
CliCommands::Errors => {
println!("Errors: {:?}", bms.get_errors()?);
}
CliCommands::All => {
println!("Status: {:?}", bms.get_status()?);
std::thread::sleep(delay);
println!("SOC: {:?}", bms.get_soc()?);
std::thread::sleep(delay);
println!("CellVoltageRange: {:?}", bms.get_cell_voltage_range()?);
std::thread::sleep(delay);
println!("TemperatureRange: {:?}", bms.get_temperature_range()?);
std::thread::sleep(delay);
println!("Mosfet: {:?}", bms.get_mosfet_status()?);
std::thread::sleep(delay);
println!("CellVoltages: {:?}", bms.get_cell_voltages()?);
std::thread::sleep(delay);
println!("CellTemperatures: {:?}", bms.get_cell_temperatures()?);
std::thread::sleep(delay);
println!("Balancing: {:?}", bms.get_balancing_status()?);
std::thread::sleep(delay);
println!("Errors: {:?}", bms.get_errors()?);
}
CliCommands::SetSoc { soc_percent } => bms.set_soc(soc_percent)?,
Expand Down
15 changes: 15 additions & 0 deletions src/protocol.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,19 @@
use crate::Error;
use std::fmt;

#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};

#[derive(Debug)]
#[repr(u8)]
pub enum Address {
Host = 0x40,
}

// https://minimalmodbus.readthedocs.io/en/stable/serialcommunication.html#timing-of-the-serial-communications
// minimum delay 4ms by baud rate 9600
pub const MINIMUM_DELAY: std::time::Duration = std::time::Duration::from_millis(4);

const TX_BUFFER_LENGTH: usize = 13;
const RX_BUFFER_LENGTH: usize = 13;
const START_BYTE: u8 = 0xa5;
Expand Down Expand Up @@ -68,6 +75,7 @@ fn validate_checksum(buffer: &[u8]) -> std::result::Result<(), Error> {
}

#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Soc {
pub total_voltage: f32,
pub current: f32, // negative=charging, positive=discharging
Expand Down Expand Up @@ -99,6 +107,7 @@ impl Soc {
}

#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct CellVoltageRange {
pub highest_voltage: f32,
pub highest_cell: u8,
Expand Down Expand Up @@ -130,6 +139,7 @@ impl CellVoltageRange {
}

#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct TemperatureRange {
pub highest_temperature: i8,
pub highest_sensor: u8,
Expand Down Expand Up @@ -162,13 +172,15 @@ impl TemperatureRange {
}

#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum MosfetMode {
Stationary,
Charging,
Discharging,
}

#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct MosfetStatus {
pub mode: MosfetMode,
pub charging_mosfet: bool,
Expand Down Expand Up @@ -214,6 +226,7 @@ impl MosfetStatus {
}

#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct IOState {
pub di1: bool,
pub di2: bool,
Expand All @@ -226,6 +239,7 @@ pub struct IOState {
}

#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Status {
pub cells: u8,
pub temperature_sensors: u8,
Expand Down Expand Up @@ -399,6 +413,7 @@ impl CellBalanceState {
}

#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum ErrorCode {
CellVoltHighLevel1,
CellVoltHighLevel2,
Expand Down
40 changes: 34 additions & 6 deletions src/serialport.rs
Original file line number Diff line number Diff line change
@@ -1,27 +1,38 @@
use crate::protocol::*;
use anyhow::{bail, Context, Result};
use std::time::Duration;
use std::time::{Duration, Instant};

#[derive(Debug)]
pub struct DalyBMS {
serial: Box<dyn serialport::SerialPort>,
last_execution: Instant,
delay: Duration,
status: Option<Status>,
}

impl DalyBMS {
pub fn new(port: &str, timeout: Duration) -> Result<Self> {
pub fn new(port: &str) -> Result<Self> {
Ok(Self {
serial: serialport::new(port, 9600)
.data_bits(serialport::DataBits::Eight)
.parity(serialport::Parity::None)
.stop_bits(serialport::StopBits::One)
.flow_control(serialport::FlowControl::None)
.timeout(timeout)
.open()
.with_context(|| format!("Cannot open serial port '{}'", port))?,
last_execution: Instant::now(),
delay: MINIMUM_DELAY,
status: None,
})
}

fn serial_await_delay(&self) {
let last_exec_diff = Instant::now().duration_since(self.last_execution);
if let Some(time_until_delay_reached) = self.delay.checked_sub(last_exec_diff) {
std::thread::sleep(time_until_delay_reached);
}
}

fn send_bytes(&mut self, tx_buffer: &[u8]) -> Result<()> {
// clear all incoming serial to avoid data collision
loop {
Expand All @@ -41,13 +52,18 @@ impl DalyBMS {
break;
}
}
self.serial_await_delay();

self.serial
.write_all(tx_buffer)
.with_context(|| "Cannot write to serial")?;
self.serial
.flush()
.with_context(|| "Cannot flush serial connection")

if false {
self.serial
.flush()
.with_context(|| "Cannot flush serial connection")?;
}
Ok(())
}

fn receive_bytes(&mut self, size: usize) -> Result<Vec<u8>> {
Expand All @@ -59,10 +75,22 @@ impl DalyBMS {
.read_exact(&mut rx_buffer)
.with_context(|| "Cannot receive response")?;

self.last_execution = Instant::now();

log::trace!("receive_bytes: {:02X?}", rx_buffer);
Ok(rx_buffer)
}

pub fn set_timeout(&mut self, timeout: Duration) -> Result<()> {
self.serial
.set_timeout(timeout)
.map_err(|err| anyhow::Error::from(err))
}

pub fn set_delay(&mut self, delay: Duration) {
self.delay = Duration::max(delay, MINIMUM_DELAY);
}

pub fn get_soc(&mut self) -> Result<Soc> {
self.send_bytes(&Soc::request(Address::Host))?;
Soc::decode(&self.receive_bytes(Soc::reply_size())?).with_context(|| "Cannot get SOC")
Expand Down
Loading

0 comments on commit a1dd097

Please sign in to comment.