phidget/devices/
digital_output.rs

1// phidget-rs/src/digital_output.rs
2//
3// Copyright (c) 2023-2024, Frank Pagliughi
4//
5// This file is part of the 'phidget-rs' library.
6//
7// Licensed under the MIT license:
8//   <LICENSE or http://opensource.org/licenses/MIT>
9// This file may not be copied, modified, or distributed except according
10// to those terms.
11//
12
13use crate::{Phidget, Result, ReturnCode};
14use phidget_sys::{self as ffi, PhidgetDigitalOutputHandle, PhidgetHandle};
15use std::{
16    ffi::{c_int, c_void},
17    mem, ptr,
18};
19
20/// The function type for the safe Rust temperature sensor attach callback.
21pub type AttachCallback = dyn Fn(&mut DigitalOutput) + Send + 'static;
22
23/// The function type for the safe Rust temperature sensor detach callback.
24pub type DetachCallback = dyn Fn(&mut DigitalOutput) + Send + 'static;
25
26/// Phidget digital output
27pub struct DigitalOutput {
28    // Handle to the digital output in the phidget22 library
29    chan: PhidgetDigitalOutputHandle,
30    // Double-boxed attach callback, if registered
31    attach_cb: Option<*mut c_void>,
32    // Double-boxed detach callback, if registered
33    detach_cb: Option<*mut c_void>,
34}
35
36impl DigitalOutput {
37    /// Create a new digital input.
38    pub fn new() -> Self {
39        let mut chan: PhidgetDigitalOutputHandle = ptr::null_mut();
40        unsafe {
41            ffi::PhidgetDigitalOutput_create(&mut chan);
42        }
43        Self::from(chan)
44    }
45
46    // Low-level, unsafe callback for device attach events
47    unsafe extern "C" fn on_attach(phid: PhidgetHandle, ctx: *mut c_void) {
48        if !ctx.is_null() {
49            let cb: &mut Box<AttachCallback> = &mut *(ctx as *mut _);
50            let mut sensor = Self::from(phid as PhidgetDigitalOutputHandle);
51            cb(&mut sensor);
52            mem::forget(sensor);
53        }
54    }
55
56    // Low-level, unsafe callback for device detach events
57    unsafe extern "C" fn on_detach(phid: PhidgetHandle, ctx: *mut c_void) {
58        if !ctx.is_null() {
59            let cb: &mut Box<DetachCallback> = &mut *(ctx as *mut _);
60            let mut sensor = Self::from(phid as PhidgetDigitalOutputHandle);
61            cb(&mut sensor);
62            mem::forget(sensor);
63        }
64    }
65
66    /// Get a reference to the underlying sensor handle
67    pub fn as_channel(&self) -> &PhidgetDigitalOutputHandle {
68        &self.chan
69    }
70
71    /// Set enable failsafe
72    pub fn set_enable_failsafe(&self, failsafe_time: u32) -> Result<()> {
73        ReturnCode::result(unsafe {
74            ffi::PhidgetDigitalOutput_enableFailsafe(self.chan, failsafe_time)
75        })?;
76        Ok(())
77    }
78    /// Set reset failsafe
79    pub fn set_reset_failsafe(&self) -> Result<()> {
80        ReturnCode::result(unsafe { ffi::PhidgetDigitalOutput_resetFailsafe(self.chan) })?;
81        Ok(())
82    }
83
84    /// Set the duty cycle of the digital output
85    /// This is the fraction of the time the output is high. A value of 1.0
86    /// means constantly high; 0.0 means constantly low
87    pub fn set_duty_cycle(&self, duty_cycle: f64) -> Result<()> {
88        ReturnCode::result(unsafe {
89            ffi::PhidgetDigitalOutput_setDutyCycle(self.chan, duty_cycle)
90        })?;
91        Ok(())
92    }
93
94    /// Get duty cycle
95    pub fn duty_cycle(&self) -> Result<f64> {
96        let mut value = 0.0;
97        ReturnCode::result(unsafe {
98            ffi::PhidgetDigitalOutput_getDutyCycle(self.chan, &mut value)
99        })?;
100        Ok(value)
101    }
102
103    /// Get minimum duty cycle
104    pub fn min_duty_cycle(&self) -> Result<f64> {
105        let mut value = 0.0;
106        ReturnCode::result(unsafe {
107            ffi::PhidgetDigitalOutput_getMinDutyCycle(self.chan, &mut value)
108        })?;
109        Ok(value)
110    }
111
112    /// Get maximum duty cycle
113    pub fn max_duty_cycle(&self) -> Result<f64> {
114        let mut value = 0.0;
115        ReturnCode::result(unsafe {
116            ffi::PhidgetDigitalOutput_getMaxDutyCycle(self.chan, &mut value)
117        })?;
118        Ok(value)
119    }
120
121    /// Get minimum failsafe time
122    pub fn min_failsafe_time(&self) -> Result<u32> {
123        let mut value = 0;
124        ReturnCode::result(unsafe {
125            ffi::PhidgetDigitalOutput_getMinFailsafeTime(self.chan, &mut value)
126        })?;
127        Ok(value)
128    }
129
130    /// Get maximum failsafe time
131    pub fn max_failsafe_time(&self) -> Result<u32> {
132        let mut value = 0;
133        ReturnCode::result(unsafe {
134            ffi::PhidgetDigitalOutput_getMaxFailsafeTime(self.chan, &mut value)
135        })?;
136        Ok(value)
137    }
138
139    /// Set frequency for PWM output
140    pub fn set_frequency(&self, frequency: f64) -> Result<()> {
141        ReturnCode::result(unsafe {
142            ffi::PhidgetDigitalOutput_setFrequency(self.chan, frequency)
143        })?;
144        Ok(())
145    }
146
147    /// Get frequency for PWM output
148    pub fn frequency(&self) -> Result<f64> {
149        let mut value = 0.0;
150        ReturnCode::result(unsafe {
151            ffi::PhidgetDigitalOutput_getFrequency(self.chan, &mut value)
152        })?;
153        Ok(value)
154    }
155
156    /// Get minimum frequency
157    pub fn min_frequency(&self) -> Result<f64> {
158        let mut value = 0.0;
159        ReturnCode::result(unsafe {
160            ffi::PhidgetDigitalOutput_getMinFrequency(self.chan, &mut value)
161        })?;
162        Ok(value)
163    }
164
165    /// Get maximum frequency
166    pub fn max_frequency(&self) -> Result<f64> {
167        let mut value = 0.0;
168        ReturnCode::result(unsafe {
169            ffi::PhidgetDigitalOutput_getMaxFrequency(self.chan, &mut value)
170        })?;
171        Ok(value)
172    }
173
174    /// Set led current limit
175    pub fn set_led_current_limit(&self, led_current_limit: f64) -> Result<()> {
176        ReturnCode::result(unsafe {
177            ffi::PhidgetDigitalOutput_setLEDCurrentLimit(self.chan, led_current_limit)
178        })?;
179        Ok(())
180    }
181
182    /// Get led current limit
183    pub fn led_current_limit(&self) -> Result<f64> {
184        let mut value = 0.0;
185        ReturnCode::result(unsafe {
186            ffi::PhidgetDigitalOutput_getLEDCurrentLimit(self.chan, &mut value)
187        })?;
188        Ok(value)
189    }
190
191    /// Get minimum led current limit
192    pub fn min_led_current_limit(&self) -> Result<f64> {
193        let mut value = 0.0;
194        ReturnCode::result(unsafe {
195            ffi::PhidgetDigitalOutput_getMinLEDCurrentLimit(self.chan, &mut value)
196        })?;
197        Ok(value)
198    }
199
200    /// Get maximum led current limit
201    pub fn max_led_current_limit(&self) -> Result<f64> {
202        let mut value = 0.0;
203        ReturnCode::result(unsafe {
204            ffi::PhidgetDigitalOutput_getMaxLEDCurrentLimit(self.chan, &mut value)
205        })?;
206        Ok(value)
207    }
208
209    /// Get led forward voltage
210    pub fn led_forward_voltage(&self) -> Result<u32> {
211        let mut value: ffi::PhidgetDigitalOutput_LEDForwardVoltage = 0;
212        ReturnCode::result(unsafe {
213            ffi::PhidgetDigitalOutput_getLEDForwardVoltage(self.chan, &mut value)
214        })?;
215        Ok(value)
216    }
217
218    /// Set the state of the digital output
219    /// This overrides any duty cycle that was previously set.
220    pub fn set_state(&self, state: u8) -> Result<()> {
221        ReturnCode::result(unsafe { ffi::PhidgetDigitalOutput_setState(self.chan, state as c_int) })
222    }
223
224    /// Get the state of the digital output channel
225    pub fn state(&self) -> Result<u8> {
226        let mut value = 0;
227        ReturnCode::result(unsafe { ffi::PhidgetDigitalOutput_getState(self.chan, &mut value) })?;
228        Ok(value as u8)
229    }
230
231    /// Sets a handler to receive attach callbacks
232    pub fn set_on_attach_handler<F>(&mut self, cb: F) -> Result<()>
233    where
234        F: Fn(&mut DigitalOutput) + Send + 'static,
235    {
236        // 1st box is fat ptr, 2nd is regular pointer.
237        let cb: Box<Box<AttachCallback>> = Box::new(Box::new(cb));
238        let ctx = Box::into_raw(cb) as *mut c_void;
239
240        ReturnCode::result(unsafe {
241            ffi::Phidget_setOnAttachHandler(self.as_mut_handle(), Some(Self::on_attach), ctx)
242        })?;
243        self.attach_cb = Some(ctx);
244        Ok(())
245    }
246
247    /// Sets a handler to receive detach callbacks
248    pub fn set_on_detach_handler<F>(&mut self, cb: F) -> Result<()>
249    where
250        F: Fn(&mut DigitalOutput) + Send + 'static,
251    {
252        // 1st box is fat ptr, 2nd is regular pointer.
253        let cb: Box<Box<DetachCallback>> = Box::new(Box::new(cb));
254        let ctx = Box::into_raw(cb) as *mut c_void;
255
256        ReturnCode::result(unsafe {
257            ffi::Phidget_setOnDetachHandler(self.as_mut_handle(), Some(Self::on_detach), ctx)
258        })?;
259        self.detach_cb = Some(ctx);
260        Ok(())
261    }
262}
263
264impl Phidget for DigitalOutput {
265    fn as_mut_handle(&mut self) -> PhidgetHandle {
266        self.chan as PhidgetHandle
267    }
268    fn as_handle(&self) -> PhidgetHandle {
269        self.chan as PhidgetHandle
270    }
271}
272
273unsafe impl Send for DigitalOutput {}
274
275impl Default for DigitalOutput {
276    fn default() -> Self {
277        Self::new()
278    }
279}
280
281impl From<PhidgetDigitalOutputHandle> for DigitalOutput {
282    fn from(chan: PhidgetDigitalOutputHandle) -> Self {
283        Self {
284            chan,
285            attach_cb: None,
286            detach_cb: None,
287        }
288    }
289}
290
291impl Drop for DigitalOutput {
292    fn drop(&mut self) {
293        if let Ok(true) = self.is_open() {
294            let _ = self.close();
295        }
296        unsafe {
297            ffi::PhidgetDigitalOutput_delete(&mut self.chan);
298            crate::drop_cb::<AttachCallback>(self.attach_cb.take());
299            crate::drop_cb::<DetachCallback>(self.detach_cb.take());
300        }
301    }
302}