summaryrefslogtreecommitdiff
path: root/src/lib.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib.rs')
-rw-r--r--src/lib.rs211
1 files changed, 211 insertions, 0 deletions
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<SerialError> for Error {
+ fn from(value: SerialError) -> Self {
+ Self::Serial(value)
+ }
+}
+
+impl From<std::io::Error> for Error {
+ fn from(value: std::io::Error) -> Self {
+ Self::Serial(value.into())
+ }
+}
+
+impl From<std::num::ParseFloatError> for Error {
+ fn from(_: std::num::ParseFloatError) -> Self {
+ Self::Parse
+ }
+}
+
+impl From<std::str::Utf8Error> for Error {
+ fn from(_: std::str::Utf8Error) -> Self {
+ Self::Parse
+ }
+}
+
+pub type Result<T> = std::result::Result<T, Error>;
+
+/// An interface to the power supply.
+pub struct PowerSupply {
+ port: Box<dyn SerialPort>,
+}
+
+impl PowerSupply {
+ pub fn open(path: impl AsRef<str>) -> Result<Self> {
+ 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<f64> {
+ 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<f64> {
+ 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<bool> 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)
+ }
+}