From 30d86863a4311d486585a364e25923550b4ccbee Mon Sep 17 00:00:00 2001 From: Ian McIntyre Date: Sun, 14 Sep 2025 20:39:38 -0400 Subject: First commit --- src/lib.rs | 211 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 211 insertions(+) create mode 100644 src/lib.rs (limited to 'src/lib.rs') diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..b3ddcd9 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,211 @@ +//! Host-side control interface for B&K Precision 16xB Power Supplies. +//! +//! The library optimizes for the 1687B and 1688B models. This library +//! doe not support two decimal places for current on the 1685B model. +//! +//! This library has no affiliation with B&K Precision. + +use std::{ + fmt::{Debug, Display}, + str::from_utf8, +}; + +use serialport::SerialPort; +pub use serialport::{Error as SerialError, ErrorKind as SerialErrorKind}; + +pub enum Error { + UnexpectedResponse, + Parse, + Serial(SerialError), +} + +impl From for Error { + fn from(value: SerialError) -> Self { + Self::Serial(value) + } +} + +impl From for Error { + fn from(value: std::io::Error) -> Self { + Self::Serial(value.into()) + } +} + +impl From for Error { + fn from(_: std::num::ParseFloatError) -> Self { + Self::Parse + } +} + +impl From for Error { + fn from(_: std::str::Utf8Error) -> Self { + Self::Parse + } +} + +pub type Result = std::result::Result; + +/// An interface to the power supply. +pub struct PowerSupply { + port: Box, +} + +impl PowerSupply { + pub fn open(path: impl AsRef) -> Result { + serialport::new(path.as_ref(), 9600) + .data_bits(serialport::DataBits::Eight) + .parity(serialport::Parity::None) + .stop_bits(serialport::StopBits::One) + .flow_control(serialport::FlowControl::None) + .timeout(std::time::Duration::from_millis(100)) + .open() + .map(|port| PowerSupply { port }) + .map_err(From::from) + } + + fn parse_volts(&mut self) -> Result { + let mut buff = [0u8; 3]; + self.port.read_exact(&mut buff)?; + let volts: f64 = from_utf8(&buff)?.parse()?; + Ok(volts / 10.0) + } + + fn parse_current(&mut self) -> Result { + let mut buff = [0u8; 3]; + self.port.read_exact(&mut buff)?; + let current: f64 = from_utf8(&buff)?.parse()?; + Ok(current / 10.0) + } + + fn parse_cr(&mut self) -> Result<()> { + let mut buff = [0; 1]; + self.port.read_exact(&mut buff)?; + if buff[0] == b'\r' { + Ok(()) + } else { + Err(Error::UnexpectedResponse) + } + } + + fn parse_ok(&mut self) -> Result<()> { + let mut buff = [0; 2]; + self.port.read_exact(&mut buff)?; + if &buff == b"OK" { + Ok(()) + } else { + Err(Error::UnexpectedResponse) + } + } + + fn parse_end(&mut self) -> Result<()> { + self.parse_ok()?; + self.parse_cr()?; + Ok(()) + } + + fn write_command(&mut self, cmd: &[u8]) -> Result<()> { + self.port.write_all(cmd)?; + self.port.flush()?; + Ok(()) + } + + /// Set the supply output state. + pub fn set_output(&mut self, output: Output) -> Result<()> { + self.write_command(match output { + Output::Off => b"SOUT1\r", + Output::On => b"SOUT0\r", + })?; + self.parse_end()?; + Ok(()) + } + + /// Read the supply's maximum voltage and current. + pub fn max_voltage_current(&mut self) -> Result<(f64, f64)> { + self.write_command(b"GMAX\r")?; + + let volts = self.parse_volts()?; + let amps = self.parse_current()?; + self.parse_end()?; + + Ok((volts, amps)) + } +} + +/// The power supply output state. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum Output { + /// Supply off. + Off, + /// Supply on. + On, +} + +impl From for Output { + fn from(value: bool) -> Self { + if value { + Self::On + } else { + Self::Off + } + } +} + +/// Power supply model +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[non_exhaustive] +pub enum Model { + /// BK Precision 1687B. + /// + /// 36V - 10A. + Bkp1687B, +} + +/// A voltage setting. +/// +/// This has a 0.1V resolution across all power supply models. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct Voltage( + u32, /* Internally represented as mV; see GETD command. */ +); + +impl Display for Voltage { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {} +} + +impl Voltage { + pub const fn from_f64_v(volts: f64) -> Voltage { + Voltage((volts * 1000.0f64) as u32) + } + + pub const fn from_f32_v(volts: f32) -> Voltage { + Voltage((volts * 1000.0f32) as u32) + } + + pub const fn from_mv(millivolts: u32) -> Voltage { + Voltage(millivolts / 10 * 10) + } +} + +/// A current setting. +/// +/// On the 1687B and 1688B models, this has +/// a 0.1A resolution. On the 1685B model, this +/// has a 0.01A resolution. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct Current( + u32, /* Internally represented as mA; see GETD command. */ +); + +impl Current { + pub const fn from_f64_a(amps: f64) -> Current { + Current((amps * 1000.0f64) as u32) + } + + pub const fn from_f32_a(amps: f32) -> Current { + Current((amps * 1000.0f32) as u32) + } + + pub const fn from_ma(milliamps: u32) -> Current { + Current(milliamps) + } +} -- cgit v1.2.3