Skip to main content

automation_hat/
lib.rs

1//! # Automation HAT Rust Library
2//!
3//! A Rust library for controlling the [Pimoroni Automation HAT](https://shop.pimoroni.com/products/automation-hat),
4//! [Automation pHAT](https://shop.pimoroni.com/products/automation-phat), and
5//! [Automation HAT Mini](https://shop.pimoroni.com/products/automation-hat-mini).
6//!
7//! This library provides a convenient interface to control all features of the Automation HAT devices,
8//! including relays, digital outputs, digital inputs, analog inputs, and LEDs. The library also supports
9//! the display on the Automation HAT Mini.
10//!
11//! ## Features
12//!
13//! - Control relays, digital outputs, and read digital/analog inputs
14//! - Full LED control with automatic status indication
15//! - Support for all Automation HAT variants (HAT, pHAT, Mini)
16//! - Display support for Automation HAT Mini
17//!
18//! ## Example
19//!
20//! ```rust,no_run
21//! use automation_hat::{AutomationHAT, HatType};
22//!
23//! fn main() -> Result<(), Box<dyn std::error::Error>> {
24//!     // Create a new AutomationHAT instance
25//!     let mut hat = AutomationHAT::new(HatType::AutomationHAT);
26//!
27//!     // Toggle relay 3
28//!     hat.relays.three.write(true)?;
29//!
30//!     // Read digital input 1
31//!     let input_value = hat.inputs.one.read()?;
32//!     println!("Input 1: {}", input_value);
33//!
34//!     // Set digital output 2
35//!     hat.outputs.two.write(true)?;
36//!
37//!     // Read analog input 3
38//!     let analog_value = hat.analog_inputs.three.read()?;
39//!     println!("Analog 3: {}", analog_value);
40//!
41//!     Ok(())
42//! }
43//! ```
44
45mod analog_input;
46mod digital_input;
47mod digital_output;
48mod lights;
49mod relay;
50
51pub use analog_input::AnalogInput;
52pub use digital_input::DigitalInput;
53pub use digital_output::DigitalOutput;
54pub use lights::LED;
55pub use relay::Relay;
56
57use ads1x1x::{Ads1x1x, FullScaleRange, TargetAddr};
58use linux_embedded_hal::{
59    CdevPin, I2cdev, SpidevDevice,
60    gpio_cdev::{Chip, LineRequestFlags},
61};
62use sn3218_hal::SN3218;
63use st7735_lcd::ST7735;
64use std::sync::{Arc, Mutex};
65
66static RELAY_1: u32 = 13;
67static RELAY_2: u32 = 19;
68static RELAY_3: u32 = 16;
69
70static INPUT_1: u32 = 26;
71static INPUT_2: u32 = 20;
72static INPUT_3: u32 = 21;
73
74static OUTPUT_1: u32 = 5;
75static OUTPUT_2: u32 = 12;
76static OUTPUT_3: u32 = 6;
77
78/// Represents the type of Automation HAT hardware being used.
79///
80/// Different HAT types have different capabilities:
81/// - `AutomationHAT`: Full-size HAT with 3 relays, LEDs for all I/O
82/// - `AutomationPHAT`: Smaller pHAT form factor with fewer features
83/// - `AutomationHATMini`: Mini form factor with LCD display
84pub enum HatType {
85    /// Full-sized Automation HAT with 3 relays and status LEDs for all I/O
86    AutomationHAT,
87    /// Smaller pHAT form factor with fewer features than the full HAT
88    AutomationPHAT,
89    /// Compact form factor with 0.96" color LCD display
90    AutomationHATMini,
91}
92
93/// Container for relay controls on the Automation HAT.
94///
95/// Provides access to the relays on the Automation HAT:
96/// - `one` and `two` are optional as they're not present on all HAT variants
97/// - `three` is available on all HAT variants
98pub struct Relays {
99    /// Relay 1 - Only present on full HAT
100    pub one: Option<Relay>,
101    /// Relay 2 - Only present on full HAT
102    pub two: Option<Relay>,
103    /// Relay 3 - Present on all HAT variants
104    pub three: Relay,
105}
106
107impl Relays {
108    /// Creates a new Relays container with the specified relay instances.
109    ///
110    /// # Arguments
111    ///
112    /// * `one` - Optional Relay 1 instance (present on HAT)
113    /// * `two` - Optional Relay 2 instance (present on HAT)
114    /// * `three` - Relay 3 instance (present on all variants)
115    pub fn new(one: Option<Relay>, two: Option<Relay>, three: Relay) -> Self {
116        Relays { one, two, three }
117    }
118}
119
120/// Container for digital input controls on the Automation HAT.
121///
122/// Provides access to the three digital inputs available on all HAT variants.
123pub struct Inputs {
124    /// Digital Input 1
125    pub one: DigitalInput,
126    /// Digital Input 2
127    pub two: DigitalInput,
128    /// Digital Input 3
129    pub three: DigitalInput,
130}
131
132impl Inputs {
133    /// Creates a new Inputs container with the specified digital input instances.
134    ///
135    /// # Arguments
136    ///
137    /// * `one` - Digital Input 1 instance
138    /// * `two` - Digital Input 2 instance
139    /// * `three` - Digital Input 3 instance
140    pub fn new(one: DigitalInput, two: DigitalInput, three: DigitalInput) -> Self {
141        Inputs { one, two, three }
142    }
143}
144
145/// Container for digital output controls on the Automation HAT.
146///
147/// Provides access to the three digital outputs available on all HAT variants.
148pub struct Outputs {
149    /// Digital Output 1
150    pub one: DigitalOutput,
151    /// Digital Output 2
152    pub two: DigitalOutput,
153    /// Digital Output 3
154    pub three: DigitalOutput,
155}
156
157impl Outputs {
158    /// Creates a new Outputs container with the specified digital output instances.
159    ///
160    /// # Arguments
161    ///
162    /// * `one` - Digital Output 1 instance
163    /// * `two` - Digital Output 2 instance
164    /// * `three` - Digital Output 3 instance
165    pub fn new(one: DigitalOutput, two: DigitalOutput, three: DigitalOutput) -> Self {
166        Outputs { one, two, three }
167    }
168}
169
170/// Container for analog input controls on the Automation HAT.
171///
172/// Provides access to the three analog inputs available on all HAT variants.
173pub struct AnalogInputs {
174    /// Analog Input 1
175    pub one: AnalogInput,
176    /// Analog Input 2
177    pub two: AnalogInput,
178    /// Analog Input 3
179    pub three: AnalogInput,
180}
181
182impl AnalogInputs {
183    /// Creates a new AnalogInputs container with the specified analog input instances.
184    ///
185    /// # Arguments
186    ///
187    /// * `one` - Analog Input 1 instance
188    /// * `two` - Analog Input 2 instance
189    /// * `three` - Analog Input 3 instance
190    pub fn new(one: AnalogInput, two: AnalogInput, three: AnalogInput) -> Self {
191        AnalogInputs { one, two, three }
192    }
193}
194
195/// Main interface for the Automation HAT family of boards.
196///
197/// This struct provides access to all features of the Automation HAT:
198/// - Relays for high-power switching
199/// - Digital inputs for reading 5V signals
200/// - Digital outputs for 5V control signals
201/// - Analog inputs for reading variable voltage levels
202/// - Optional display (only on Automation HAT Mini)
203pub struct AutomationHAT {
204    /// The type of Automation HAT hardware being used
205    pub hat_type: HatType,
206    /// Access to relay controls
207    pub relays: Relays,
208    /// Access to digital input controls
209    pub inputs: Inputs,
210    /// Access to digital output controls
211    pub outputs: Outputs,
212    /// Access to analog input controls
213    pub analog_inputs: AnalogInputs,
214    /// Access to the ST7735 display (only available on Automation HAT Mini)
215    pub display: Option<ST7735<SpidevDevice, CdevPin, CdevPin>>,
216}
217
218impl AutomationHAT {
219    /// Creates a new AutomationHAT instance for the specified HAT type.
220    ///
221    /// This initializes all hardware connections, GPIO pins, I2C devices, and
222    /// the display (if using the Automation HAT Mini).
223    ///
224    /// # Arguments
225    ///
226    /// * `hat_type` - The type of Automation HAT to initialize
227    ///
228    /// # Returns
229    ///
230    /// A fully configured `AutomationHAT` instance ready for use
231    ///
232    /// # Examples
233    ///
234    /// ```
235    /// use automation_hat::{AutomationHAT, HatType};
236    ///
237    /// // Create a new AutomationHAT instance
238    /// let mut hat = AutomationHAT::new(HatType::AutomationHAT);
239    /// ```
240    pub fn new(hat_type: HatType) -> Self {
241        let i2c_analog = I2cdev::new("/dev/i2c-1").unwrap();
242        let mut analog_driver = Ads1x1x::new_ads1015(i2c_analog, TargetAddr::default());
243
244        analog_driver
245            .set_full_scale_range(FullScaleRange::Within2_048V)
246            .unwrap();
247
248        let analog_driver = match analog_driver.into_continuous() {
249            Ok(driver) => Arc::new(Mutex::new(driver)),
250            Err(_) => panic!("Failed to convert analog driver into continuous mode"),
251        };
252
253        let mut gpio_chip = Chip::new("/dev/gpiochip0").unwrap();
254
255        // For AutomationHATMini, disable auto-lighting since there are no LEDs
256        let auto_light = !matches!(hat_type, HatType::AutomationHATMini);
257
258        let mut relay_1 = None;
259        let mut relay_2 = None;
260
261        let mut relay_3_no_led = None;
262        let mut relay_3_nc_led = None;
263        let mut input_1_led = None;
264        let mut input_2_led = None;
265        let mut input_3_led = None;
266        let mut output_1_led = None;
267        let mut output_2_led = None;
268        let mut output_3_led = None;
269        let mut analog_input_1_led = None;
270        let mut analog_input_2_led = None;
271        let mut analog_input_3_led = None;
272        let mut display = None;
273
274        match hat_type {
275            HatType::AutomationHAT => {
276                let i2c_led = I2cdev::new("/dev/i2c-1").unwrap();
277                let driver = Arc::new(Mutex::new(SN3218::new(i2c_led)));
278
279                analog_input_1_led = Some(LED::new(driver.clone(), 0));
280                analog_input_2_led = Some(LED::new(driver.clone(), 1));
281                analog_input_3_led = Some(LED::new(driver.clone(), 2));
282
283                output_1_led = Some(LED::new(driver.clone(), 3));
284                output_2_led = Some(LED::new(driver.clone(), 4));
285                output_3_led = Some(LED::new(driver.clone(), 5));
286
287                input_1_led = Some(LED::new(driver.clone(), 14));
288                input_2_led = Some(LED::new(driver.clone(), 13));
289                input_3_led = Some(LED::new(driver.clone(), 12));
290
291                let relay_1_no_led = Some(LED::new(driver.clone(), 6));
292                let relay_1_nc_led = Some(LED::new(driver.clone(), 7));
293                let relay_2_no_led = Some(LED::new(driver.clone(), 8));
294                let relay_2_nc_led = Some(LED::new(driver.clone(), 9));
295                relay_3_no_led = Some(LED::new(driver.clone(), 10));
296                relay_3_nc_led = Some(LED::new(driver.clone(), 11));
297
298                relay_1 = Some(Relay::new_with_auto_light(
299                    gpio_chip.get_line(RELAY_1).unwrap(),
300                    relay_1_no_led,
301                    relay_1_nc_led,
302                    auto_light,
303                ));
304
305                relay_2 = Some(Relay::new_with_auto_light(
306                    gpio_chip.get_line(RELAY_2).unwrap(),
307                    relay_2_no_led,
308                    relay_2_nc_led,
309                    auto_light,
310                ));
311            }
312            HatType::AutomationPHAT => {}
313            HatType::AutomationHATMini => {
314                let dc = gpio_chip.get_line(9).unwrap();
315                let dc = dc
316                    .request(LineRequestFlags::OUTPUT, 0, "AutomationHAT Rust SDK")
317                    .unwrap();
318                let dc = CdevPin::new(dc).unwrap();
319
320                let rst = gpio_chip.get_line(22).unwrap();
321                let rst = rst
322                    .request(LineRequestFlags::OUTPUT, 0, "AutomationHAT Rust SDK")
323                    .unwrap();
324                let rst = CdevPin::new(rst).unwrap();
325                display = Some(ST7735::new(
326                    SpidevDevice::open("/dev/spidev0.1").unwrap(),
327                    dc,
328                    rst,
329                    false,
330                    true,
331                    80,
332                    160,
333                ));
334
335                if let Some(ref mut disp) = display {
336                    let mut delay = linux_embedded_hal::Delay {};
337                    disp.init(&mut delay).unwrap();
338                    disp.set_offset(26, 2);
339                }
340            }
341        }
342
343        let relay_3 = Relay::new_with_auto_light(
344            gpio_chip.get_line(RELAY_3).unwrap(),
345            relay_3_no_led,
346            relay_3_nc_led,
347            auto_light,
348        );
349
350        let input_1 = DigitalInput::new_with_auto_light(
351            gpio_chip.get_line(INPUT_1).unwrap(),
352            input_1_led,
353            auto_light,
354        );
355        let input_2 = DigitalInput::new_with_auto_light(
356            gpio_chip.get_line(INPUT_2).unwrap(),
357            input_2_led,
358            auto_light,
359        );
360        let input_3 = DigitalInput::new_with_auto_light(
361            gpio_chip.get_line(INPUT_3).unwrap(),
362            input_3_led,
363            auto_light,
364        );
365        let output_1 = DigitalOutput::new_with_auto_light(
366            gpio_chip.get_line(OUTPUT_1).unwrap(),
367            output_1_led,
368            auto_light,
369        );
370        let output_2 = DigitalOutput::new_with_auto_light(
371            gpio_chip.get_line(OUTPUT_2).unwrap(),
372            output_2_led,
373            auto_light,
374        );
375        let output_3 = DigitalOutput::new_with_auto_light(
376            gpio_chip.get_line(OUTPUT_3).unwrap(),
377            output_3_led,
378            auto_light,
379        );
380        let analog_input_1 = AnalogInput::new(analog_driver.clone(), analog_input_1_led, 0);
381        let analog_input_2 = AnalogInput::new(analog_driver.clone(), analog_input_2_led, 1);
382        let analog_input_3 = AnalogInput::new(analog_driver.clone(), analog_input_3_led, 2);
383
384        let analog_inputs = AnalogInputs::new(analog_input_1, analog_input_2, analog_input_3);
385        let inputs = Inputs::new(input_1, input_2, input_3);
386        let outputs = Outputs::new(output_1, output_2, output_3);
387        let relays = Relays::new(relay_1, relay_2, relay_3);
388
389        Self {
390            analog_inputs,
391            display,
392            hat_type,
393            inputs,
394            outputs,
395            relays,
396        }
397    }
398}