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}