Compare commits

...

5 Commits

Author SHA1 Message Date
Nareshkumar Rao e3e7c95ec6 wip 7 months ago
Nareshkumar Rao 917f3dac86 wip 7 months ago
Nareshkumar Rao 0b8395137e wip 7 months ago
Nareshkumar Rao 817704d4c7 wip 7 months ago
Nareshkumar Rao 6804acd6e2 wip 7 months ago
  1. 294
      Cargo.lock
  2. 8
      Cargo.toml
  3. 17
      src/actuators.rs
  4. 113
      src/cli_mode.rs
  5. 8
      src/config.rs
  6. 100
      src/dht11.rs
  7. 3
      src/error.rs
  8. 75
      src/io.rs
  9. 32
      src/main.rs
  10. 15
      src/sensors.rs

294
Cargo.lock

@ -2,6 +2,16 @@
# It is not intended for manual editing.
version = 3
[[package]]
name = "ads1x1x"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6b44310e48193933db0cd788961f450a86f35fdad022e4373883fa8e4640488e"
dependencies = [
"embedded-hal 0.2.7",
"nb 1.1.0",
]
[[package]]
name = "bitflags"
version = "2.5.0"
@ -21,13 +31,80 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e"
[[package]]
name = "interrupts"
version = "0.1.5"
name = "clipboard-win"
version = "5.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "79f4473f5144e20d9aceaf2972478f06ddf687831eafeeb434fbaf0acc4144ad"
dependencies = [
"error-code",
]
[[package]]
name = "embedded-hal"
version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "35949884794ad573cf46071e41c9b60efb0cb311e3ca01f7af807af1debc66ff"
dependencies = [
"nb 0.1.3",
"void",
]
[[package]]
name = "embedded-hal"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "361a90feb7004eca4019fb28352a9465666b24f840f5c3cddf0ff13920590b89"
[[package]]
name = "embedded-hal-nb"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fba4268c14288c828995299e59b12babdbe170f6c6d73731af1b4648142e8605"
dependencies = [
"embedded-hal 1.0.0",
"nb 1.1.0",
]
[[package]]
name = "endian-type"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c34f04666d835ff5d62e058c3995147c06f42fe86ff053337632bca83e42702d"
[[package]]
name = "errno"
version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c1ccb0bdc9cefc5772ebc23d9bd94aabf408ab6753735f1936ac6ed7e714c8c6"
checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245"
dependencies = [
"libc",
"windows-sys",
]
[[package]]
name = "error-code"
version = "3.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a0474425d51df81997e2f90a21591180b38eccf27292d755f3e30750225c175b"
[[package]]
name = "fd-lock"
version = "4.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7e5768da2206272c81ef0b5e951a41862938a6070da63bcea197899942d3b947"
dependencies = [
"cfg-if",
"nix",
"rustix",
"windows-sys",
]
[[package]]
name = "home"
version = "0.5.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5"
dependencies = [
"windows-sys",
]
[[package]]
@ -36,6 +113,48 @@ version = "0.2.153"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd"
[[package]]
name = "linux-raw-sys"
version = "0.4.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c"
[[package]]
name = "log"
version = "0.4.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c"
[[package]]
name = "memchr"
version = "2.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d"
[[package]]
name = "nb"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "801d31da0513b6ec5214e9bf433a77966320625a37860f910be265be6e18d06f"
dependencies = [
"nb 1.1.0",
]
[[package]]
name = "nb"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d5439c4ad607c3c23abf66de8c8bf57ba8adcd1f129e699851a6e43935d339d"
[[package]]
name = "nibble_vec"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77a5d83df9f36fe23f0c3648c6bbb8b0298bb5f1939c8f2704431371f4b84d43"
dependencies = [
"smallvec",
]
[[package]]
name = "nix"
version = "0.28.0"
@ -48,13 +167,25 @@ dependencies = [
"libc",
]
[[package]]
name = "radix_trie"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c069c179fcdc6a2fe24d8d18305cf085fdbd4f922c041943e203685d6a1c58fd"
dependencies = [
"endian-type",
"nibble_vec",
]
[[package]]
name = "rpitest"
version = "0.1.0"
dependencies = [
"interrupts",
"ads1x1x",
"libc",
"nb 1.1.0",
"rppal",
"rustyline",
]
[[package]]
@ -63,5 +194,158 @@ version = "0.17.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1dc171bbe325b04172e18d917c58c2cf1fb5adfd9ffabb1d6b3d62ba4c1c1331"
dependencies = [
"embedded-hal 0.2.7",
"embedded-hal 1.0.0",
"embedded-hal-nb",
"libc",
"nb 0.1.3",
"spin_sleep",
"void",
]
[[package]]
name = "rustix"
version = "0.38.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f"
dependencies = [
"bitflags",
"errno",
"libc",
"linux-raw-sys",
"windows-sys",
]
[[package]]
name = "rustyline"
version = "14.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7803e8936da37efd9b6d4478277f4b2b9bb5cdb37a113e8d63222e58da647e63"
dependencies = [
"bitflags",
"cfg-if",
"clipboard-win",
"fd-lock",
"home",
"libc",
"log",
"memchr",
"nix",
"radix_trie",
"unicode-segmentation",
"unicode-width",
"utf8parse",
"windows-sys",
]
[[package]]
name = "smallvec"
version = "1.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
[[package]]
name = "spin_sleep"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "368a978649eaf70006b082e79c832bd72556ac1393eaf564d686e919dca2347f"
dependencies = [
"windows-sys",
]
[[package]]
name = "unicode-segmentation"
version = "1.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202"
[[package]]
name = "unicode-width"
version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85"
[[package]]
name = "utf8parse"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
[[package]]
name = "void"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
[[package]]
name = "windows-sys"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
dependencies = [
"windows-targets",
]
[[package]]
name = "windows-targets"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_gnullvm",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263"
[[package]]
name = "windows_aarch64_msvc"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6"
[[package]]
name = "windows_i686_gnu"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670"
[[package]]
name = "windows_i686_gnullvm"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9"
[[package]]
name = "windows_i686_msvc"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf"
[[package]]
name = "windows_x86_64_gnu"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596"
[[package]]
name = "windows_x86_64_msvc"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0"

8
Cargo.toml

@ -6,7 +6,9 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
rppal = "0.17.1"
rppal = { version = "0.17", features = ["hal"] }
#rumqttc = "0.24.0"
interrupts = "0.1.5"
libc = "0.2.153"
libc = "0.2"
ads1x1x = "0.2"
nb = "1.1.0"
rustyline = "14.0.0"

17
src/actuators.rs

@ -0,0 +1,17 @@
use crate::{
config::*,
error::GenericResult,
io::{Relay, RelaySwitchState},
};
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)
}

113
src/cli_mode.rs

@ -0,0 +1,113 @@
use rustyline::{config::Configurer, error::ReadlineError, history::FileHistory};
use crate::{
error::GenericResult,
io::{self, get_input_voltage},
};
struct LoopFlags {
exit: bool,
}
fn process_input(input: String, program_state: &mut ProgramState) -> GenericResult<LoopFlags> {
let args = input.split(' ').collect::<Vec<_>>();
let main_command = *args.first().ok_or("No main command found.")?;
match main_command {
"ana" => command_ana(&args)?,
"rel" => command_rel(&args, program_state)?,
"exit" => return Ok(LoopFlags { exit: true }),
_ => return Err("Unknown main command".into()),
};
Ok(LoopFlags { exit: false })
}
fn command_rel(args: &[&str], program_state: &mut ProgramState) -> GenericResult<()> {
let pin = args
.get(1)
.ok_or("Must specify pin number.")?
.parse::<u8>()
.map_err(|_| "Not a valid pin number")?;
let switch_state = args.get(2).map(|arg| match *arg {
"1" => Ok(io::RelaySwitchState::On),
"on" => Ok(io::RelaySwitchState::On),
"true" => Ok(io::RelaySwitchState::On),
"0" => Ok(io::RelaySwitchState::Off),
"off" => Ok(io::RelaySwitchState::Off),
"false" => Ok(io::RelaySwitchState::Off),
_ => Err("Not a valid switch state"),
});
match switch_state {
Some(state) => {
println!("Switching relay");
program_state.relay.switch(pin, state?)?
}
None => {
println!("Toggling relay");
program_state.relay.toggle(pin)?
}
};
Ok(())
}
fn command_ana(args: &[&str]) -> GenericResult<()> {
let pin = args
.get(1)
.ok_or("Must specify pin number.")?
.parse::<u8>()
.map_err(|_| "Not a valid pin number")?;
let voltage = get_input_voltage(pin)?;
println!("Voltage read: {}", voltage);
Ok(())
}
fn cli_loop(rl: &mut CLIEditor, program_state: &mut ProgramState) -> GenericResult<LoopFlags> {
let readline = rl.readline("growpi>> ");
match readline {
Ok(line) => {
rl.add_history_entry(line.as_str())?;
process_input(line, program_state)
}
Err(ReadlineError::Eof) => Ok(LoopFlags { exit: true }),
Err(_) => Err("No input".into()),
}
}
type CLIEditor = rustyline::Editor<(), FileHistory>;
fn init_readline() -> GenericResult<CLIEditor> {
let mut rl = rustyline::DefaultEditor::new()?;
rl.set_max_history_size(10)?;
Ok(rl)
}
struct ProgramState {
relay: io::Relay,
}
fn init_state() -> GenericResult<ProgramState> {
Ok(ProgramState {
relay: io::Relay::new()?,
})
}
pub fn run_cli() {
let mut rl = init_readline().unwrap();
let mut program_state = init_state().unwrap();
'cli_loop: loop {
match cli_loop(&mut rl, &mut program_state) {
Ok(loop_flags) => {
if loop_flags.exit {
println!("Leaving CLI");
break 'cli_loop;
}
}
Err(e) => println!("{}", e),
}
}
}

8
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<u8>; 4] = [Some(17), Some(27), Some(22), None];

100
src/dht11.rs

@ -1,100 +0,0 @@
use std::{
error::Error,
thread,
time::{Duration, Instant},
};
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<Dht11Sensor, Box<dyn Error>> {
let pin = Gpio::new()?.get(pin)?.into_io(rppal::gpio::Mode::Input);
Ok(Dht11Sensor { pin })
}
fn expect_pulse(&mut self, expected_level: bool) -> Result<Duration, Box<dyn Error>> {
let started = Instant::now();
loop {
if self.pin.is_high() != expected_level {
break;
}
if started.elapsed() >= Duration::from_micros(1000) {
return Err(std::io::Error::new(
std::io::ErrorKind::InvalidData,
"Timeout while reading pulse",
)
.into());
}
thread::sleep(Duration::from_micros(1));
}
Ok(started.elapsed())
}
pub fn read(&mut self) -> Result<Dht11Data, Box<dyn Error>> {
use rppal::gpio::{Bias, Mode};
let mut data = [0; 5];
let mut cycles: [Duration; 80] = [Duration::from_micros(0); 80];
let guard = interrupts::disable();
self.pin.set_mode(Mode::Input);
self.pin.set_bias(Bias::PullUp);
thread::sleep(Duration::from_millis(1));
self.pin.set_mode(Mode::Output);
self.pin.set_low();
thread::sleep(Duration::from_millis(20));
// Timing Critical Code
self.pin.set_mode(Mode::Input);
self.pin.set_bias(Bias::PullUp);
thread::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,
})
}
}

3
src/error.rs

@ -0,0 +1,3 @@
use std::error::Error;
pub type GenericResult<T> = Result<T, Box<dyn Error>>;

75
src/io.rs

@ -0,0 +1,75 @@
use ads1x1x::{Ads1x1x, ChannelSelection, DynamicOneShot};
use nb::block;
use rppal::gpio::{Gpio, OutputPin};
use crate::{config::*, error::GenericResult};
pub fn get_input_voltage(pin: u8) -> GenericResult<f32> {
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<rppal::gpio::OutputPin>; RELAY_GPIO_PINS.len()],
}
pub enum RelaySwitchState {
On,
Off,
}
impl Relay {
pub fn new() -> GenericResult<Relay> {
let mut output_pins = RELAY_GPIO_PINS.map(|pin| {
pin.and_then(|pin| {
let result =
(|| -> GenericResult<OutputPin> { 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.")?)
}
}

32
src/main.rs

@ -1,24 +1,20 @@
use std::{error::Error, thread, time::Duration};
#![allow(dead_code)]
use dht11::Dht11Sensor;
use cli_mode::run_cli;
mod dht11;
mod actuators;
mod cli_mode;
mod config;
mod error;
mod io;
mod sensors;
fn main() -> Result<(), Box<dyn Error>> {
unsafe {
libc::setpriority(libc::PRIO_PROCESS, 0, -20);
fn main() {
let args = std::env::args().collect::<Vec<_>>();
if let Some(mode) = args.first().map(|x| x.as_str()) {
match mode {
"cli" => run_cli(),
_ => run_cli(),
}
let mut sensor = Dht11Sensor::new(2)?;
loop {
match sensor.read() {
Ok(data) => println!(
"Humidity: {}; Temperature: {}",
data.humidity, data.temperature
),
Err(msg) => println!("Error: {}", msg),
}
thread::sleep(Duration::from_millis(2000));
}
Ok(())
}

15
src/sensors.rs

@ -0,0 +1,15 @@
use crate::{config::*, error::GenericResult, io::get_input_voltage};
pub fn get_temperature() -> GenericResult<f32> {
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)
}
Loading…
Cancel
Save