relay.rs
1 use anyhow::Result; 2 use esp_idf_hal::gpio::{AnyOutputPin, Output, PinDriver}; 3 use log::{info, warn}; 4 use serde::{Deserialize, Serialize}; 5 use std::sync::atomic::{AtomicBool, Ordering}; 6 use std::sync::{Arc, Mutex}; 7 8 /// Types of devices that can be controlled 9 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] 10 pub enum DeviceType { 11 ExhaustFan, // 220V exhaust fan 12 CirculationFan, // 5V air circulation fan 13 Heater, // Electric heater 14 AirPurifier, // Air purifier/filter 15 } 16 17 impl DeviceType { 18 pub fn name(&self) -> &'static str { 19 match self { 20 DeviceType::ExhaustFan => "Exhaust Fan", 21 DeviceType::CirculationFan => "Circulation Fan", 22 DeviceType::Heater => "Heater", 23 DeviceType::AirPurifier => "Air Purifier", 24 } 25 } 26 } 27 28 /// Single relay channel controller with lock-free state reads 29 pub struct RelayChannel { 30 pin: Mutex<PinDriver<'static, AnyOutputPin, Output>>, 31 active_high: bool, 32 state: AtomicBool, // Lock-free atomic state for fast reads 33 device_type: DeviceType, 34 name: String, 35 } 36 37 impl RelayChannel { 38 /// Create a new relay channel 39 pub fn new( 40 pin: AnyOutputPin, 41 active_high: bool, 42 device_type: DeviceType, 43 name: String, 44 ) -> Result<Self> { 45 let mut driver = PinDriver::output(pin)?; 46 47 // Initialize to OFF state 48 if active_high { 49 driver.set_low()?; 50 } else { 51 driver.set_high()?; 52 } 53 54 info!( 55 "Relay channel '{}' ({}) initialized on GPIO (active_{})", 56 name, 57 device_type.name(), 58 if active_high { "high" } else { "low" } 59 ); 60 61 Ok(Self { 62 pin: Mutex::new(driver), 63 active_high, 64 state: AtomicBool::new(false), 65 device_type, 66 name, 67 }) 68 } 69 70 /// Turn the relay ON 71 pub fn turn_on(&self) -> Result<()> { 72 // Fast check without locking 73 if self.state.load(Ordering::Acquire) { 74 return Ok(()); // Already on 75 } 76 77 let mut pin = self.pin.lock().unwrap(); 78 79 if self.active_high { 80 pin.set_high()?; 81 } else { 82 pin.set_low()?; 83 } 84 85 self.state.store(true, Ordering::Release); 86 info!("{} ({}) turned ON", self.name, self.device_type.name()); 87 Ok(()) 88 } 89 90 /// Turn the relay OFF 91 pub fn turn_off(&self) -> Result<()> { 92 // Fast check without locking 93 if !self.state.load(Ordering::Acquire) { 94 return Ok(()); // Already off 95 } 96 97 let mut pin = self.pin.lock().unwrap(); 98 99 if self.active_high { 100 pin.set_low()?; 101 } else { 102 pin.set_high()?; 103 } 104 105 self.state.store(false, Ordering::Release); 106 info!("{} ({}) turned OFF", self.name, self.device_type.name()); 107 Ok(()) 108 } 109 110 /// Get current state (lock-free, very fast) 111 pub fn is_on(&self) -> bool { 112 self.state.load(Ordering::Relaxed) 113 } 114 115 /// Get device type 116 pub fn device_type(&self) -> DeviceType { 117 self.device_type 118 } 119 120 /// Get device name 121 pub fn name(&self) -> &str { 122 &self.name 123 } 124 } 125 126 /// Multi-channel relay controller managing multiple devices 127 pub struct MultiRelayController { 128 channels: Vec<Arc<RelayChannel>>, 129 } 130 131 impl MultiRelayController { 132 pub fn new() -> Self { 133 Self { 134 channels: Vec::new(), 135 } 136 } 137 138 /// Add a relay channel 139 pub fn add_channel(&mut self, channel: RelayChannel) -> Arc<RelayChannel> { 140 let arc_channel = Arc::new(channel); 141 self.channels.push(arc_channel.clone()); 142 arc_channel 143 } 144 145 /// Get channel by device type 146 pub fn get_channel(&self, device_type: DeviceType) -> Option<&Arc<RelayChannel>> { 147 self.channels 148 .iter() 149 .find(|ch| ch.device_type() == device_type) 150 } 151 152 /// Turn on a specific device 153 pub fn turn_on(&self, device_type: DeviceType) -> Result<()> { 154 if let Some(channel) = self.get_channel(device_type) { 155 channel.turn_on() 156 } else { 157 Err(anyhow::anyhow!("Device type {:?} not found", device_type)) 158 } 159 } 160 161 /// Turn off a specific device 162 pub fn turn_off(&self, device_type: DeviceType) -> Result<()> { 163 if let Some(channel) = self.get_channel(device_type) { 164 channel.turn_off() 165 } else { 166 Err(anyhow::anyhow!("Device type {:?} not found", device_type)) 167 } 168 } 169 170 /// Get all channels 171 pub fn channels(&self) -> &[Arc<RelayChannel>] { 172 &self.channels 173 } 174 175 /// Turn off all channels (safety) 176 pub fn all_off(&self) -> Result<()> { 177 for channel in &self.channels { 178 channel.turn_off()?; 179 } 180 info!("All relay channels turned OFF"); 181 Ok(()) 182 } 183 } 184 185 impl Drop for MultiRelayController { 186 fn drop(&mut self) { 187 if let Err(e) = self.all_off() { 188 warn!("Failed to turn off all relays on drop: {:?}", e); 189 } 190 } 191 }