diff --git a/src/actuators.rs b/src/actuators.rs new file mode 100644 index 0000000..e10b90e --- /dev/null +++ b/src/actuators.rs @@ -0,0 +1,17 @@ +use crate::{ + config::*, + io::{Relay, RelaySwitchState}, + GenericResult, +}; + +pub fn switch_lights(relay: &mut Relay, state: RelaySwitchState) -> GenericResult<()> { + relay.switch(LIGHT_RELAY_PIN, state) +} + +pub fn switch_fan(relay: &mut Relay, state: RelaySwitchState) -> GenericResult<()> { + relay.switch(FAN_RELAY_PIN, state) +} + +pub fn switch_water_pump(relay: &mut Relay, state: RelaySwitchState) -> GenericResult<()> { + relay.switch(WATER_PUMP_RELAY_PIN, state) +} diff --git a/src/config.rs b/src/config.rs new file mode 100644 index 0000000..b9f84bb --- /dev/null +++ b/src/config.rs @@ -0,0 +1,8 @@ +pub const LOGIC_LEVEL: f32 = 3.3; +pub const THERMISTOR_ANALOG_PIN: u8 = 0; +pub const THERMISTOR_VOLTAGE_DIVIDER_RESISTANCE: f32 = 9_700.; + +pub const LIGHT_RELAY_PIN: u8 = 0; +pub const FAN_RELAY_PIN: u8 = 1; +pub const WATER_PUMP_RELAY_PIN: u8 = 2; +pub const RELAY_GPIO_PINS: [Option; 4] = [Some(17), Some(27), Some(22), None]; diff --git a/src/dht11.rs b/src/dht11.rs deleted file mode 100644 index 5e4edde..0000000 --- a/src/dht11.rs +++ /dev/null @@ -1,98 +0,0 @@ -use std::{error::Error, time::Duration}; - -use rppal::gpio::Gpio; - -pub struct Dht11Data { - pub temperature: f32, - pub humidity: f32, -} -pub struct Dht11Sensor { - pin: rppal::gpio::IoPin, -} -impl Dht11Sensor { - pub fn new(pin: u8) -> Result> { - let pin = Gpio::new()?.get(pin)?.into_io(rppal::gpio::Mode::Input); - Ok(Dht11Sensor { pin }) - } - - fn expect_pulse(&mut self, expected_level: bool) -> Result> { - let _guard = interrupts::disable(); - let mut count: u32 = 0; - let mut rtclock = linux_realtime::Clock::new()?; - loop { - if self.pin.is_high() != expected_level { - break; - } - if count >= 1000 { - return Err(std::io::Error::new( - std::io::ErrorKind::InvalidData, - "Timeout while reading pulse", - ) - .into()); - } - count += 1; - rtclock.sleep(Duration::from_micros(1))?; - } - Ok(count) - } - - pub fn read(&mut self) -> Result> { - use rppal::gpio::{Bias, Mode}; - - let mut data = [0; 5]; - let mut cycles: [u32; 80] = [0; 80]; - - let mut rtclock = linux_realtime::Clock::new()?; - - self.pin.set_mode(Mode::Input); - self.pin.set_bias(Bias::PullUp); - rtclock.sleep(Duration::from_millis(1))?; - - self.pin.set_mode(Mode::Output); - self.pin.set_low(); - rtclock.sleep(Duration::from_millis(20))?; - - // Timing Critical Code - self.pin.set_mode(Mode::Input); - self.pin.set_bias(Bias::PullUp); - rtclock.sleep(Duration::from_micros(55))?; - - self.expect_pulse(false)?; - self.expect_pulse(true)?; - for i in (0..80).step_by(2) { - cycles[i] = self.expect_pulse(false)?; - cycles[i + 1] = self.expect_pulse(true)?; - } - - for i in 0..40 { - let low_cycles = cycles[2 * i]; - let high_cycles = cycles[2 * i + 1]; - data[i / 8] <<= 1; - if high_cycles > low_cycles { - data[i / 8] |= 1; - } - } - - if data[4] != ((data[0] + data[1] + data[2] + data[3]) & 0xFF) { - return Err(std::io::Error::new( - std::io::ErrorKind::InvalidData, - "DHT Checksum Failure", - ) - .into()); - } - - let mut temperature = data[2] as f32; - if data[3] & 0x80 != 0 { - temperature = -1. - temperature; - } - temperature += ((data[3] & 0x0F) as f32) * 0.1; - - let mut humidity = data[0] as f32; - humidity += (data[1] as f32) * 0.1; - - Ok(Dht11Data { - temperature, - humidity, - }) - } -} diff --git a/src/error.rs b/src/error.rs new file mode 100644 index 0000000..7f7bc38 --- /dev/null +++ b/src/error.rs @@ -0,0 +1,3 @@ +use std::error::Error; + +type GenericResult = Result>; diff --git a/src/io.rs b/src/io.rs new file mode 100644 index 0000000..b0da009 --- /dev/null +++ b/src/io.rs @@ -0,0 +1,75 @@ +use ads1x1x::{Ads1x1x, ChannelSelection, DynamicOneShot}; +use nb::block; +use rppal::gpio::{Gpio, OutputPin}; + +use crate::{config::*, GenericResult}; + +pub fn get_input_voltage(pin: u8) -> GenericResult { + const ADS1115_DEFAULT_RANGE: f32 = 2.048; + let adc = rppal::i2c::I2c::new()?; + let mut adc = Ads1x1x::new_ads1115(adc, ads1x1x::SlaveAddr::Alternative(false, false)); + let channel: ChannelSelection = match pin { + 0 => ChannelSelection::SingleA0, + 1 => ChannelSelection::SingleA1, + 2 => ChannelSelection::SingleA2, + 3 => ChannelSelection::SingleA3, + _ => return Err(format!("Pin {} not available. Only 0-3", pin).into()), + }; + let result = block!(adc.read(channel)).map_err(|e| format!("{:?}", e))?; + let result = result as f32; + let result = result / i16::MAX as f32 * ADS1115_DEFAULT_RANGE; + Ok(result) +} + +pub struct Relay { + relay_pins: [Option; RELAY_GPIO_PINS.len()], +} +pub enum RelaySwitchState { + On, + Off, +} +impl Relay { + pub fn new() -> GenericResult { + let mut output_pins = RELAY_GPIO_PINS.map(|pin| { + pin.and_then(|pin| { + let result = + (|| -> GenericResult { Ok(Gpio::new()?.get(pin)?.into_output()) })(); + result.ok() + }) + }); + for pin in output_pins.iter_mut().flatten() { + // The relay turns ON on LOW + pin.set_high(); + } + Ok(Relay { + relay_pins: output_pins, + }) + } + pub fn toggle(&mut self, pin: u8) -> GenericResult<()> { + let pin = self.get_output_pin(pin)?; + pin.toggle(); + Ok(()) + } + + pub fn switch(&mut self, pin: u8, state: RelaySwitchState) -> GenericResult<()> { + let pin = self.get_output_pin(pin)?; + match state { + RelaySwitchState::On => pin.set_low(), + RelaySwitchState::Off => pin.set_high(), + } + Ok(()) + } + + fn get_output_pin(&mut self, pin: u8) -> GenericResult<&mut OutputPin> { + Ok(self + .relay_pins + .get_mut(pin as usize) + .ok_or(format!( + "Pin {} not within pin array with length {}", + pin, + RELAY_GPIO_PINS.len() + ))? + .as_mut() + .ok_or("Pin not configured.")?) + } +} diff --git a/src/main.rs b/src/main.rs index 0735780..3d2affe 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,11 +1,15 @@ -use std::{error::Error, io, thread, time::Duration}; +#![allow(dead_code)] +use std::{error::Error, thread, time::Duration}; use ads1x1x::{Ads1x1x, ChannelSelection, DynamicOneShot}; use nb::block; -use rppal::{ - gpio::{Gpio, OutputPin}, - i2c::I2c, -}; +use rppal::gpio::{Gpio, OutputPin}; + +mod actuators; +mod config; +mod error; +mod io; +mod sensors; type GenericResult = Result>; @@ -37,7 +41,7 @@ fn main() -> GenericResult<()> { loop { println!("Enter command: "); let mut input_string = String::new(); - io::stdin().read_line(&mut input_string)?; + std::io::stdin().read_line(&mut input_string)?; match process_command(input_string, &mut program_state) { Ok(to_exit) => { diff --git a/src/sensors.rs b/src/sensors.rs new file mode 100644 index 0000000..3ed6a8f --- /dev/null +++ b/src/sensors.rs @@ -0,0 +1,15 @@ +use crate::{config::*, io::get_input_voltage, GenericResult}; + +pub fn get_temperature() -> GenericResult { + const THERMISTOR_NOMINAL_RESISTANCE: f32 = 10_000.; + const THERMISTOR_NOMINAL_TEMPERATURE: f32 = 298.15; + const THERMISTOR_CONSTANT: f32 = 3950.; + + let voltage = get_input_voltage(THERMISTOR_ANALOG_PIN)?; + let resistance = (LOGIC_LEVEL / voltage - 1.) * THERMISTOR_VOLTAGE_DIVIDER_RESISTANCE; + let temperature = 1. + / ((1. / THERMISTOR_NOMINAL_TEMPERATURE) + + (1. / THERMISTOR_CONSTANT * f32::ln(resistance / THERMISTOR_NOMINAL_RESISTANCE))) + - 273.15; + Ok(temperature) +}