mirror of
				https://github.com/Djeeberjr/fw-anwesenheit.git
				synced 2025-11-04 07:34:10 +00:00 
			
		
		
		
	implemented mocking of rpi hardware
buzzer,led & hotspot got traits and a mock version of it. Based on the flag the real or mock version is used.
This commit is contained in:
		
							parent
							
								
									31f65261df
								
							
						
					
					
						commit
						64a50d434b
					
				@ -3,6 +3,10 @@ name = "fw-anwesenheit"
 | 
			
		||||
version = "0.1.0"
 | 
			
		||||
edition = "2024"
 | 
			
		||||
 | 
			
		||||
[features]
 | 
			
		||||
default = []
 | 
			
		||||
mock_pi = [] # Enable mocking of the rpi hardware 
 | 
			
		||||
 | 
			
		||||
[dependencies]
 | 
			
		||||
chrono = { version = "0.4.40", features = ["serde"] }
 | 
			
		||||
gpio = "0.4.1"
 | 
			
		||||
 | 
			
		||||
@ -1,6 +1,12 @@
 | 
			
		||||
use rppal::pwm::{Channel, Error, Polarity, Pwm};
 | 
			
		||||
use std::{future::Future, time::Duration};
 | 
			
		||||
use tokio::time::sleep;
 | 
			
		||||
use std::time::Duration;
 | 
			
		||||
 | 
			
		||||
pub trait Buzzer {
 | 
			
		||||
    fn beep_ack(&mut self) -> impl Future<Output = Result<(), Error>> + std::marker::Send;
 | 
			
		||||
 | 
			
		||||
    fn beep_nak(&mut self) -> impl Future<Output = Result<(), Error>> + std::marker::Send;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub struct GPIOBuzzer {
 | 
			
		||||
    pwm: Pwm,
 | 
			
		||||
@ -8,19 +14,19 @@ pub struct GPIOBuzzer {
 | 
			
		||||
 | 
			
		||||
impl GPIOBuzzer {
 | 
			
		||||
    /// Create a new GPIOBuzzer instance.
 | 
			
		||||
    /// 0.5 duty cyle 
 | 
			
		||||
    /// 0.5 duty cyle
 | 
			
		||||
    /// # Arguments
 | 
			
		||||
    /// * "channel" - PWM channel for buzzer PWM0 = GPIO 12 / PWM1 = GPIO 13
 | 
			
		||||
    pub fn new(channel: Channel) -> Result<Self, Error> {
 | 
			
		||||
        // Enable with dummy values; we'll set frequency/duty in the tone method
 | 
			
		||||
        let duty_cycle:f64 = 0.5;
 | 
			
		||||
        let duty_cycle: f64 = 0.5;
 | 
			
		||||
        let pwm = Pwm::with_frequency(channel, 1000.0, duty_cycle, Polarity::Normal, true)?;
 | 
			
		||||
        pwm.disable()?; // Start disabled
 | 
			
		||||
 | 
			
		||||
        Ok(GPIOBuzzer { pwm })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
        /// Play a tone using hardware PWM on supported GPIO pins.
 | 
			
		||||
    /// Play a tone using hardware PWM on supported GPIO pins.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Arguments
 | 
			
		||||
    /// * `frequency` - Frequency in Hz.
 | 
			
		||||
@ -32,33 +38,24 @@ impl GPIOBuzzer {
 | 
			
		||||
        self.pwm.disable()?;
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
    pub async fn beep_ack(&mut self) -> Result<(), Error>{
 | 
			
		||||
impl Buzzer for GPIOBuzzer {
 | 
			
		||||
    async fn beep_ack(&mut self) -> Result<(), Error> {
 | 
			
		||||
        let sleep_ms: u64 = 10;
 | 
			
		||||
 | 
			
		||||
        self.modulated_tone(1200.0, 100).await?;
 | 
			
		||||
        sleep(Duration::from_millis(sleep_ms)).await;
 | 
			
		||||
        self.modulated_tone(2000.0,50).await?;
 | 
			
		||||
        self.modulated_tone(2000.0, 50).await?;
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub async fn beep_nak(&mut self) -> Result<(), Error>{
 | 
			
		||||
    async fn beep_nak(&mut self) -> Result<(), Error> {
 | 
			
		||||
        let sleep_ms: u64 = 100;
 | 
			
		||||
 | 
			
		||||
        self.modulated_tone(600.0, 150).await?;
 | 
			
		||||
        sleep(Duration::from_millis(sleep_ms)).await;
 | 
			
		||||
        self.modulated_tone(600.0,150).await?;
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub async fn beep_unnkown(&mut self) -> Result<(), Error>{
 | 
			
		||||
        let sleep_ms: u64 = 100;
 | 
			
		||||
 | 
			
		||||
        self.modulated_tone(750.0, 100).await?;
 | 
			
		||||
        sleep(Duration::from_millis(sleep_ms)).await;
 | 
			
		||||
        self.modulated_tone(1200.0,100).await?;
 | 
			
		||||
        sleep(Duration::from_millis(sleep_ms)).await;
 | 
			
		||||
        self.modulated_tone(2300.0,100).await?;
 | 
			
		||||
        self.modulated_tone(600.0, 150).await?;
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										199
									
								
								src/hotspot.rs
									
									
									
									
									
								
							
							
						
						
									
										199
									
								
								src/hotspot.rs
									
									
									
									
									
								
							@ -1,5 +1,6 @@
 | 
			
		||||
use log::trace;
 | 
			
		||||
use log::{error, trace, warn};
 | 
			
		||||
use std::{
 | 
			
		||||
    env,
 | 
			
		||||
    fmt::{self},
 | 
			
		||||
    process::Output,
 | 
			
		||||
};
 | 
			
		||||
@ -14,6 +15,7 @@ const IPV4_ADDRES: &str = "192.168.4.1/24";
 | 
			
		||||
pub enum HotspotError {
 | 
			
		||||
    IoError(std::io::Error),
 | 
			
		||||
    NonZeroExit(Output),
 | 
			
		||||
    PasswordToShort,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl fmt::Display for HotspotError {
 | 
			
		||||
@ -30,104 +32,145 @@ impl fmt::Display for HotspotError {
 | 
			
		||||
                    "Failed to run hotspot command.\nStdout: {stdout}\nStderr: {stderr}",
 | 
			
		||||
                )
 | 
			
		||||
            }
 | 
			
		||||
            HotspotError::PasswordToShort => {
 | 
			
		||||
                write!(f, "The password must be at leat 8 characters long")
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl std::error::Error for HotspotError {}
 | 
			
		||||
 | 
			
		||||
/// Create the connection in NM
 | 
			
		||||
/// Will fail if already exists
 | 
			
		||||
async fn create_hotspot() -> Result<(), HotspotError> {
 | 
			
		||||
    let cmd = Command::new("nmcli")
 | 
			
		||||
        .args(["device", "wifi", "hotspot"])
 | 
			
		||||
        .arg("con-name")
 | 
			
		||||
        .arg(CON_NAME)
 | 
			
		||||
        .arg("ssid")
 | 
			
		||||
        .arg(SSID)
 | 
			
		||||
        .arg("password")
 | 
			
		||||
        .arg(PASSWORD)
 | 
			
		||||
        .output()
 | 
			
		||||
        .await
 | 
			
		||||
        .map_err(HotspotError::IoError)?;
 | 
			
		||||
pub trait Hotspot {
 | 
			
		||||
    fn enable_hotspot(
 | 
			
		||||
        &self,
 | 
			
		||||
    ) -> impl std::future::Future<Output = Result<(), HotspotError>> + std::marker::Send;
 | 
			
		||||
 | 
			
		||||
    trace!("nmcli (std): {}", String::from_utf8_lossy(&cmd.stdout));
 | 
			
		||||
    trace!("nmcli (err): {}", String::from_utf8_lossy(&cmd.stderr));
 | 
			
		||||
 | 
			
		||||
    if !cmd.status.success() {
 | 
			
		||||
        return Err(HotspotError::NonZeroExit(cmd));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    let cmd = Command::new("nmcli")
 | 
			
		||||
        .arg("connection")
 | 
			
		||||
        .arg("modify")
 | 
			
		||||
        .arg(CON_NAME)
 | 
			
		||||
        .arg("ipv4.method")
 | 
			
		||||
        .arg("shared")
 | 
			
		||||
        .arg("ipv4.addresses")
 | 
			
		||||
        .arg(IPV4_ADDRES)
 | 
			
		||||
        .output()
 | 
			
		||||
        .await
 | 
			
		||||
        .map_err(HotspotError::IoError)?;
 | 
			
		||||
 | 
			
		||||
    if !cmd.status.success() {
 | 
			
		||||
        return Err(HotspotError::NonZeroExit(cmd));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    Ok(())
 | 
			
		||||
    async fn disable_hotspot(&self) -> Result<(), HotspotError>;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Checks if the connection already exists
 | 
			
		||||
async fn exists() -> Result<bool, HotspotError> {
 | 
			
		||||
    let cmd = Command::new("nmcli")
 | 
			
		||||
        .args(["connection", "show"])
 | 
			
		||||
        .arg(CON_NAME)
 | 
			
		||||
        .output()
 | 
			
		||||
        .await
 | 
			
		||||
        .map_err(HotspotError::IoError)?;
 | 
			
		||||
 | 
			
		||||
    trace!("nmcli (std): {}", String::from_utf8_lossy(&cmd.stdout));
 | 
			
		||||
    trace!("nmcli (err): {}", String::from_utf8_lossy(&cmd.stderr));
 | 
			
		||||
 | 
			
		||||
    Ok(cmd.status.success())
 | 
			
		||||
/// NetworkManager Hotspot
 | 
			
		||||
pub struct NMHotspot {
 | 
			
		||||
    ssid: String,
 | 
			
		||||
    con_name: String,
 | 
			
		||||
    password: String,
 | 
			
		||||
    ipv4: String,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub async fn enable_hotspot() -> Result<(), HotspotError> {
 | 
			
		||||
    if !exists().await? {
 | 
			
		||||
        create_hotspot().await?;
 | 
			
		||||
impl NMHotspot {
 | 
			
		||||
    pub fn new_from_env() -> Result<Self, HotspotError> {
 | 
			
		||||
        let ssid = env::var("HOTSPOT_SSID").unwrap_or(SSID.to_owned());
 | 
			
		||||
        let password = env::var("HOTSPOT_PW").unwrap_or_else(|_| {
 | 
			
		||||
            warn!("HOTSPOT_PW not set. Using default password");
 | 
			
		||||
            PASSWORD.to_owned()
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        if password.len() < 8 {
 | 
			
		||||
            error!("Hotspot PW is to short");
 | 
			
		||||
            return Err(HotspotError::PasswordToShort);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        Ok(NMHotspot {
 | 
			
		||||
            ssid,
 | 
			
		||||
            con_name: CON_NAME.to_owned(),
 | 
			
		||||
            password,
 | 
			
		||||
            ipv4: IPV4_ADDRES.to_owned(),
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    let cmd = Command::new("nmcli")
 | 
			
		||||
        .args(["connection", "up"])
 | 
			
		||||
        .arg(CON_NAME)
 | 
			
		||||
        .output()
 | 
			
		||||
        .await
 | 
			
		||||
        .map_err(HotspotError::IoError)?;
 | 
			
		||||
    async fn create_hotspot(&self) -> Result<(), HotspotError> {
 | 
			
		||||
        let cmd = Command::new("nmcli")
 | 
			
		||||
            .args(["device", "wifi", "hotspot"])
 | 
			
		||||
            .arg("con-name")
 | 
			
		||||
            .arg(&self.con_name)
 | 
			
		||||
            .arg("ssid")
 | 
			
		||||
            .arg(&self.ssid)
 | 
			
		||||
            .arg("password")
 | 
			
		||||
            .arg(&self.password)
 | 
			
		||||
            .output()
 | 
			
		||||
            .await
 | 
			
		||||
            .map_err(HotspotError::IoError)?;
 | 
			
		||||
 | 
			
		||||
    trace!("nmcli (std): {}", String::from_utf8_lossy(&cmd.stdout));
 | 
			
		||||
    trace!("nmcli (err): {}", String::from_utf8_lossy(&cmd.stderr));
 | 
			
		||||
        trace!("nmcli (std): {}", String::from_utf8_lossy(&cmd.stdout));
 | 
			
		||||
        trace!("nmcli (err): {}", String::from_utf8_lossy(&cmd.stderr));
 | 
			
		||||
 | 
			
		||||
    if !cmd.status.success() {
 | 
			
		||||
        return Err(HotspotError::NonZeroExit(cmd));
 | 
			
		||||
        if !cmd.status.success() {
 | 
			
		||||
            return Err(HotspotError::NonZeroExit(cmd));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let cmd = Command::new("nmcli")
 | 
			
		||||
            .arg("connection")
 | 
			
		||||
            .arg("modify")
 | 
			
		||||
            .arg(&self.con_name)
 | 
			
		||||
            .arg("ipv4.method")
 | 
			
		||||
            .arg("shared")
 | 
			
		||||
            .arg("ipv4.addresses")
 | 
			
		||||
            .arg(&self.ipv4)
 | 
			
		||||
            .output()
 | 
			
		||||
            .await
 | 
			
		||||
            .map_err(HotspotError::IoError)?;
 | 
			
		||||
 | 
			
		||||
        if !cmd.status.success() {
 | 
			
		||||
            return Err(HotspotError::NonZeroExit(cmd));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    Ok(())
 | 
			
		||||
    /// Checks if the connection already exists
 | 
			
		||||
    async fn exists(&self) -> Result<bool, HotspotError> {
 | 
			
		||||
        let cmd = Command::new("nmcli")
 | 
			
		||||
            .args(["connection", "show"])
 | 
			
		||||
            .arg(&self.con_name)
 | 
			
		||||
            .output()
 | 
			
		||||
            .await
 | 
			
		||||
            .map_err(HotspotError::IoError)?;
 | 
			
		||||
 | 
			
		||||
        trace!("nmcli (std): {}", String::from_utf8_lossy(&cmd.stdout));
 | 
			
		||||
        trace!("nmcli (err): {}", String::from_utf8_lossy(&cmd.stderr));
 | 
			
		||||
 | 
			
		||||
        Ok(cmd.status.success())
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub async fn disable_hotspot() -> Result<(), HotspotError> {
 | 
			
		||||
    let cmd = Command::new("nmcli")
 | 
			
		||||
        .args(["connection", "down"])
 | 
			
		||||
        .arg(CON_NAME)
 | 
			
		||||
        .output()
 | 
			
		||||
        .await
 | 
			
		||||
        .map_err(HotspotError::IoError)?;
 | 
			
		||||
impl Hotspot for NMHotspot {
 | 
			
		||||
    async fn enable_hotspot(&self) -> Result<(), HotspotError> {
 | 
			
		||||
        if !self.exists().await? {
 | 
			
		||||
            self.create_hotspot().await?;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    trace!("nmcli (std): {}", String::from_utf8_lossy(&cmd.stdout));
 | 
			
		||||
    trace!("nmcli (err): {}", String::from_utf8_lossy(&cmd.stderr));
 | 
			
		||||
        let cmd = Command::new("nmcli")
 | 
			
		||||
            .args(["connection", "up"])
 | 
			
		||||
            .arg(&self.con_name)
 | 
			
		||||
            .output()
 | 
			
		||||
            .await
 | 
			
		||||
            .map_err(HotspotError::IoError)?;
 | 
			
		||||
 | 
			
		||||
    if !cmd.status.success() {
 | 
			
		||||
        return Err(HotspotError::NonZeroExit(cmd));
 | 
			
		||||
        trace!("nmcli (std): {}", String::from_utf8_lossy(&cmd.stdout));
 | 
			
		||||
        trace!("nmcli (err): {}", String::from_utf8_lossy(&cmd.stderr));
 | 
			
		||||
 | 
			
		||||
        if !cmd.status.success() {
 | 
			
		||||
            return Err(HotspotError::NonZeroExit(cmd));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    Ok(())
 | 
			
		||||
    async fn disable_hotspot(&self) -> Result<(), HotspotError> {
 | 
			
		||||
        let cmd = Command::new("nmcli")
 | 
			
		||||
            .args(["connection", "down"])
 | 
			
		||||
            .arg(&self.con_name)
 | 
			
		||||
            .output()
 | 
			
		||||
            .await
 | 
			
		||||
            .map_err(HotspotError::IoError)?;
 | 
			
		||||
 | 
			
		||||
        trace!("nmcli (std): {}", String::from_utf8_lossy(&cmd.stdout));
 | 
			
		||||
        trace!("nmcli (err): {}", String::from_utf8_lossy(&cmd.stderr));
 | 
			
		||||
 | 
			
		||||
        if !cmd.status.success() {
 | 
			
		||||
            return Err(HotspotError::NonZeroExit(cmd));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										36
									
								
								src/led.rs
									
									
									
									
									
								
							
							
						
						
									
										36
									
								
								src/led.rs
									
									
									
									
									
								
							@ -7,35 +7,47 @@ use ws2812_spi::Ws2812;
 | 
			
		||||
 | 
			
		||||
use crate::color::NamedColor;
 | 
			
		||||
 | 
			
		||||
pub struct Led {
 | 
			
		||||
const STATUS_DURATION: Duration = Duration::from_secs(1); // 1s sleep for all status led signals
 | 
			
		||||
 | 
			
		||||
pub trait StatusLed {
 | 
			
		||||
    fn turn_green_on_1s(
 | 
			
		||||
        &mut self,
 | 
			
		||||
    ) -> impl std::future::Future<Output = Result<(), Error>> + std::marker::Send;
 | 
			
		||||
 | 
			
		||||
    fn turn_red_on_1s(
 | 
			
		||||
        &mut self,
 | 
			
		||||
    ) -> impl std::future::Future<Output = Result<(), Error>> + std::marker::Send;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub struct SpiLed {
 | 
			
		||||
    controller: Ws2812<Spi>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const STATUS_DURATION: Duration = Duration::from_secs(1); // 1s sleep for all status led signals
 | 
			
		||||
 | 
			
		||||
impl Led {
 | 
			
		||||
impl SpiLed {
 | 
			
		||||
    pub fn new() -> Result<Self, Error> {
 | 
			
		||||
        let spi = Spi::new(Bus::Spi0, SlaveSelect::Ss0, 3_800_000, Mode::Mode0)?;
 | 
			
		||||
        let controller = Ws2812::new(spi);
 | 
			
		||||
        Ok(Led { controller })
 | 
			
		||||
        Ok(SpiLed { controller })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub async fn turn_green_on_1s(&mut self) -> Result<(), Error> {
 | 
			
		||||
    fn turn_off(&mut self) -> Result<(), Error> {
 | 
			
		||||
        self.controller.write(NamedColor::Off.into_iter())?;
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl StatusLed for SpiLed {
 | 
			
		||||
    async fn turn_green_on_1s(&mut self) -> Result<(), Error> {
 | 
			
		||||
        self.controller.write(NamedColor::Green.into_iter())?;
 | 
			
		||||
        sleep(STATUS_DURATION).await;
 | 
			
		||||
        self.turn_off()?;
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub async fn turn_red_on_1s(&mut self) -> Result<(), Error> {
 | 
			
		||||
    async fn turn_red_on_1s(&mut self) -> Result<(), Error> {
 | 
			
		||||
        self.controller.write(NamedColor::Red.into_iter())?;
 | 
			
		||||
        sleep(STATUS_DURATION).await;
 | 
			
		||||
        self.turn_off()?;
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn turn_off(&mut self) -> Result<(), Error> {
 | 
			
		||||
        self.controller.write(NamedColor::Off.into_iter())?;
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										69
									
								
								src/main.rs
									
									
									
									
									
								
							
							
						
						
									
										69
									
								
								src/main.rs
									
									
									
									
									
								
							@ -1,6 +1,7 @@
 | 
			
		||||
use buzzer::GPIOBuzzer;
 | 
			
		||||
use buzzer::{Buzzer, GPIOBuzzer};
 | 
			
		||||
use hotspot::{Hotspot, HotspotError, NMHotspot};
 | 
			
		||||
use id_store::IDStore;
 | 
			
		||||
use led::Led;
 | 
			
		||||
use led::{SpiLed, StatusLed};
 | 
			
		||||
use log::{LevelFilter, debug, error, info, warn};
 | 
			
		||||
use pm3::run_pm3;
 | 
			
		||||
use rppal::pwm::Channel;
 | 
			
		||||
@ -13,11 +14,15 @@ use tokio::{
 | 
			
		||||
};
 | 
			
		||||
use webserver::start_webserver;
 | 
			
		||||
 | 
			
		||||
#[cfg(feature = "mock_pi")]
 | 
			
		||||
use mock::{MockBuzzer, MockHotspot, MockLed};
 | 
			
		||||
 | 
			
		||||
mod buzzer;
 | 
			
		||||
mod color;
 | 
			
		||||
mod hotspot;
 | 
			
		||||
mod id_store;
 | 
			
		||||
mod led;
 | 
			
		||||
mod mock;
 | 
			
		||||
mod parser;
 | 
			
		||||
mod pm3;
 | 
			
		||||
mod tally_id;
 | 
			
		||||
@ -48,7 +53,10 @@ fn setup_logger() {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Signal the user success via buzzer and led
 | 
			
		||||
async fn feedback_success(gpio_buzzer: &Arc<Mutex<GPIOBuzzer>>, status_led: &Arc<Mutex<Led>>) {
 | 
			
		||||
async fn feedback_success<T: Buzzer, I: StatusLed>(
 | 
			
		||||
    gpio_buzzer: &Arc<Mutex<T>>,
 | 
			
		||||
    status_led: &Arc<Mutex<I>>,
 | 
			
		||||
) {
 | 
			
		||||
    let mut buzzer_guard = gpio_buzzer.lock().await;
 | 
			
		||||
    let mut led_guard = status_led.lock().await;
 | 
			
		||||
 | 
			
		||||
@ -64,7 +72,10 @@ async fn feedback_success(gpio_buzzer: &Arc<Mutex<GPIOBuzzer>>, status_led: &Arc
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Signal the user failure via buzzer and led
 | 
			
		||||
async fn feedback_failure(gpio_buzzer: &Arc<Mutex<GPIOBuzzer>>, status_led: &Arc<Mutex<Led>>) {
 | 
			
		||||
async fn feedback_failure<T: Buzzer, I: StatusLed>(
 | 
			
		||||
    gpio_buzzer: &Arc<Mutex<T>>,
 | 
			
		||||
    status_led: &Arc<Mutex<I>>,
 | 
			
		||||
) {
 | 
			
		||||
    let mut buzzer_guard = gpio_buzzer.lock().await;
 | 
			
		||||
    let mut led_guard = status_led.lock().await;
 | 
			
		||||
 | 
			
		||||
@ -79,6 +90,48 @@ async fn feedback_failure(gpio_buzzer: &Arc<Mutex<GPIOBuzzer>>, status_led: &Arc
 | 
			
		||||
    });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Create a buzzer
 | 
			
		||||
/// Respects the `mock_pi` flag.
 | 
			
		||||
fn create_buzzer() -> Result<Arc<Mutex<impl Buzzer>>, rppal::pwm::Error> {
 | 
			
		||||
    #[cfg(feature = "mock_pi")]
 | 
			
		||||
    {
 | 
			
		||||
        Ok(Arc::new(Mutex::new(MockBuzzer {})))
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[cfg(not(feature = "mock_pi"))]
 | 
			
		||||
    {
 | 
			
		||||
        Ok(Arc::new(Mutex::new(GPIOBuzzer::new(PWM_CHANNEL_BUZZER)?)))
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Creates a status led.
 | 
			
		||||
/// Respects the `mock_pi` flag.
 | 
			
		||||
fn create_status_led() -> Result<Arc<Mutex<impl StatusLed>>, rppal::spi::Error> {
 | 
			
		||||
    #[cfg(feature = "mock_pi")]
 | 
			
		||||
    {
 | 
			
		||||
        Ok(Arc::new(Mutex::new(MockLed {})))
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[cfg(not(feature = "mock_pi"))]
 | 
			
		||||
    {
 | 
			
		||||
        Ok(Arc::new(Mutex::new(SpiLed::new()?)))
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Create a struct to manage the hotspot
 | 
			
		||||
/// Respects the `mock_pi` flag.
 | 
			
		||||
fn create_hotspot() -> Result<impl Hotspot, HotspotError> {
 | 
			
		||||
    #[cfg(feature = "mock_pi")]
 | 
			
		||||
    {
 | 
			
		||||
        Ok(MockHotspot {})
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[cfg(not(feature = "mock_pi"))]
 | 
			
		||||
    {
 | 
			
		||||
        NMHotspot::new_from_env()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[tokio::main]
 | 
			
		||||
async fn main() -> Result<(), Box<dyn Error>> {
 | 
			
		||||
    setup_logger();
 | 
			
		||||
@ -109,9 +162,9 @@ async fn main() -> Result<(), Box<dyn Error>> {
 | 
			
		||||
    debug!("created store sucessfully");
 | 
			
		||||
 | 
			
		||||
    let store: Arc<Mutex<IDStore>> = Arc::new(Mutex::new(raw_store));
 | 
			
		||||
    let gpio_buzzer: Arc<Mutex<GPIOBuzzer>> =
 | 
			
		||||
        Arc::new(Mutex::new(GPIOBuzzer::new(PWM_CHANNEL_BUZZER)?));
 | 
			
		||||
    let status_led: Arc<Mutex<Led>> = Arc::new(Mutex::new(Led::new()?));
 | 
			
		||||
    let gpio_buzzer = create_buzzer()?;
 | 
			
		||||
    let status_led = create_status_led()?;
 | 
			
		||||
    let hotspot = create_hotspot()?;
 | 
			
		||||
 | 
			
		||||
    let hotspot_ids: Vec<TallyID> = env::var("HOTSPOT_IDS")
 | 
			
		||||
        .map(|ids| ids.split(";").map(|id| TallyID(id.to_owned())).collect())
 | 
			
		||||
@ -130,7 +183,7 @@ async fn main() -> Result<(), Box<dyn Error>> {
 | 
			
		||||
 | 
			
		||||
            if hotspot_ids.contains(&tally_id) {
 | 
			
		||||
                info!("Enableing hotspot");
 | 
			
		||||
                hotspot::enable_hotspot().await.unwrap_or_else(|err| {
 | 
			
		||||
                hotspot.enable_hotspot().await.unwrap_or_else(|err| {
 | 
			
		||||
                    error!("Hotspot: {err}");
 | 
			
		||||
                });
 | 
			
		||||
                // TODO: Should the ID be added anyway or ignored ?
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										45
									
								
								src/mock.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								src/mock.rs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,45 @@
 | 
			
		||||
use log::debug;
 | 
			
		||||
 | 
			
		||||
use crate::{buzzer::Buzzer, hotspot::Hotspot, led::StatusLed};
 | 
			
		||||
 | 
			
		||||
pub struct MockBuzzer {}
 | 
			
		||||
 | 
			
		||||
impl Buzzer for MockBuzzer {
 | 
			
		||||
    async fn beep_ack(&mut self) -> Result<(), rppal::pwm::Error> {
 | 
			
		||||
        debug!("Mockbuzzer: ACK");
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async fn beep_nak(&mut self) -> Result<(), rppal::pwm::Error> {
 | 
			
		||||
        debug!("Mockbuzzer: NAK");
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub struct MockLed {}
 | 
			
		||||
 | 
			
		||||
impl StatusLed for MockLed {
 | 
			
		||||
    async fn turn_green_on_1s(&mut self) -> Result<(), rppal::spi::Error> {
 | 
			
		||||
        debug!("Mockled: Turn LED green for 1 sec");
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async fn turn_red_on_1s(&mut self) -> Result<(), rppal::spi::Error> {
 | 
			
		||||
        debug!("Mockled: Turn LED red for 1 sec");
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub struct MockHotspot {}
 | 
			
		||||
 | 
			
		||||
impl Hotspot for MockHotspot {
 | 
			
		||||
    async fn enable_hotspot(&self) -> Result<(), crate::hotspot::HotspotError> {
 | 
			
		||||
        debug!("Mockhotspot: Enable hotspot");
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async fn disable_hotspot(&self) -> Result<(), crate::hotspot::HotspotError> {
 | 
			
		||||
        debug!("Mockhotspot: Disable hotspot");
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user