mirror of
https://github.com/Djeeberjr/fw-anwesenheit.git
synced 2025-07-02 01:04:16 +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"
|
version = "0.1.0"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = []
|
||||||
|
mock_pi = [] # Enable mocking of the rpi hardware
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
chrono = { version = "0.4.40", features = ["serde"] }
|
chrono = { version = "0.4.40", features = ["serde"] }
|
||||||
gpio = "0.4.1"
|
gpio = "0.4.1"
|
||||||
|
@ -1,6 +1,12 @@
|
|||||||
use rppal::pwm::{Channel, Error, Polarity, Pwm};
|
use rppal::pwm::{Channel, Error, Polarity, Pwm};
|
||||||
|
use std::{future::Future, time::Duration};
|
||||||
use tokio::time::sleep;
|
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 {
|
pub struct GPIOBuzzer {
|
||||||
pwm: Pwm,
|
pwm: Pwm,
|
||||||
@ -8,19 +14,19 @@ pub struct GPIOBuzzer {
|
|||||||
|
|
||||||
impl GPIOBuzzer {
|
impl GPIOBuzzer {
|
||||||
/// Create a new GPIOBuzzer instance.
|
/// Create a new GPIOBuzzer instance.
|
||||||
/// 0.5 duty cyle
|
/// 0.5 duty cyle
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
/// * "channel" - PWM channel for buzzer PWM0 = GPIO 12 / PWM1 = GPIO 13
|
/// * "channel" - PWM channel for buzzer PWM0 = GPIO 12 / PWM1 = GPIO 13
|
||||||
pub fn new(channel: Channel) -> Result<Self, Error> {
|
pub fn new(channel: Channel) -> Result<Self, Error> {
|
||||||
// Enable with dummy values; we'll set frequency/duty in the tone method
|
// 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)?;
|
let pwm = Pwm::with_frequency(channel, 1000.0, duty_cycle, Polarity::Normal, true)?;
|
||||||
pwm.disable()?; // Start disabled
|
pwm.disable()?; // Start disabled
|
||||||
|
|
||||||
Ok(GPIOBuzzer { pwm })
|
Ok(GPIOBuzzer { pwm })
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Play a tone using hardware PWM on supported GPIO pins.
|
/// Play a tone using hardware PWM on supported GPIO pins.
|
||||||
///
|
///
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
/// * `frequency` - Frequency in Hz.
|
/// * `frequency` - Frequency in Hz.
|
||||||
@ -32,33 +38,24 @@ impl GPIOBuzzer {
|
|||||||
self.pwm.disable()?;
|
self.pwm.disable()?;
|
||||||
Ok(())
|
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;
|
let sleep_ms: u64 = 10;
|
||||||
|
|
||||||
self.modulated_tone(1200.0, 100).await?;
|
self.modulated_tone(1200.0, 100).await?;
|
||||||
sleep(Duration::from_millis(sleep_ms)).await;
|
sleep(Duration::from_millis(sleep_ms)).await;
|
||||||
self.modulated_tone(2000.0,50).await?;
|
self.modulated_tone(2000.0, 50).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn beep_nak(&mut self) -> Result<(), Error>{
|
async fn beep_nak(&mut self) -> Result<(), Error> {
|
||||||
let sleep_ms: u64 = 100;
|
let sleep_ms: u64 = 100;
|
||||||
|
|
||||||
self.modulated_tone(600.0, 150).await?;
|
self.modulated_tone(600.0, 150).await?;
|
||||||
sleep(Duration::from_millis(sleep_ms)).await;
|
sleep(Duration::from_millis(sleep_ms)).await;
|
||||||
self.modulated_tone(600.0,150).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?;
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
199
src/hotspot.rs
199
src/hotspot.rs
@ -1,5 +1,6 @@
|
|||||||
use log::trace;
|
use log::{error, trace, warn};
|
||||||
use std::{
|
use std::{
|
||||||
|
env,
|
||||||
fmt::{self},
|
fmt::{self},
|
||||||
process::Output,
|
process::Output,
|
||||||
};
|
};
|
||||||
@ -14,6 +15,7 @@ const IPV4_ADDRES: &str = "192.168.4.1/24";
|
|||||||
pub enum HotspotError {
|
pub enum HotspotError {
|
||||||
IoError(std::io::Error),
|
IoError(std::io::Error),
|
||||||
NonZeroExit(Output),
|
NonZeroExit(Output),
|
||||||
|
PasswordToShort,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for HotspotError {
|
impl fmt::Display for HotspotError {
|
||||||
@ -30,104 +32,145 @@ impl fmt::Display for HotspotError {
|
|||||||
"Failed to run hotspot command.\nStdout: {stdout}\nStderr: {stderr}",
|
"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 {}
|
impl std::error::Error for HotspotError {}
|
||||||
|
|
||||||
/// Create the connection in NM
|
pub trait Hotspot {
|
||||||
/// Will fail if already exists
|
fn enable_hotspot(
|
||||||
async fn create_hotspot() -> Result<(), HotspotError> {
|
&self,
|
||||||
let cmd = Command::new("nmcli")
|
) -> impl std::future::Future<Output = Result<(), HotspotError>> + std::marker::Send;
|
||||||
.args(["device", "wifi", "hotspot"])
|
|
||||||
.arg("con-name")
|
|
||||||
.arg(CON_NAME)
|
|
||||||
.arg("ssid")
|
|
||||||
.arg(SSID)
|
|
||||||
.arg("password")
|
|
||||||
.arg(PASSWORD)
|
|
||||||
.output()
|
|
||||||
.await
|
|
||||||
.map_err(HotspotError::IoError)?;
|
|
||||||
|
|
||||||
trace!("nmcli (std): {}", String::from_utf8_lossy(&cmd.stdout));
|
async fn disable_hotspot(&self) -> Result<(), HotspotError>;
|
||||||
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(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Checks if the connection already exists
|
/// NetworkManager Hotspot
|
||||||
async fn exists() -> Result<bool, HotspotError> {
|
pub struct NMHotspot {
|
||||||
let cmd = Command::new("nmcli")
|
ssid: String,
|
||||||
.args(["connection", "show"])
|
con_name: String,
|
||||||
.arg(CON_NAME)
|
password: String,
|
||||||
.output()
|
ipv4: String,
|
||||||
.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 enable_hotspot() -> Result<(), HotspotError> {
|
impl NMHotspot {
|
||||||
if !exists().await? {
|
pub fn new_from_env() -> Result<Self, HotspotError> {
|
||||||
create_hotspot().await?;
|
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")
|
async fn create_hotspot(&self) -> Result<(), HotspotError> {
|
||||||
.args(["connection", "up"])
|
let cmd = Command::new("nmcli")
|
||||||
.arg(CON_NAME)
|
.args(["device", "wifi", "hotspot"])
|
||||||
.output()
|
.arg("con-name")
|
||||||
.await
|
.arg(&self.con_name)
|
||||||
.map_err(HotspotError::IoError)?;
|
.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 (std): {}", String::from_utf8_lossy(&cmd.stdout));
|
||||||
trace!("nmcli (err): {}", String::from_utf8_lossy(&cmd.stderr));
|
trace!("nmcli (err): {}", String::from_utf8_lossy(&cmd.stderr));
|
||||||
|
|
||||||
if !cmd.status.success() {
|
if !cmd.status.success() {
|
||||||
return Err(HotspotError::NonZeroExit(cmd));
|
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> {
|
impl Hotspot for NMHotspot {
|
||||||
let cmd = Command::new("nmcli")
|
async fn enable_hotspot(&self) -> Result<(), HotspotError> {
|
||||||
.args(["connection", "down"])
|
if !self.exists().await? {
|
||||||
.arg(CON_NAME)
|
self.create_hotspot().await?;
|
||||||
.output()
|
}
|
||||||
.await
|
|
||||||
.map_err(HotspotError::IoError)?;
|
|
||||||
|
|
||||||
trace!("nmcli (std): {}", String::from_utf8_lossy(&cmd.stdout));
|
let cmd = Command::new("nmcli")
|
||||||
trace!("nmcli (err): {}", String::from_utf8_lossy(&cmd.stderr));
|
.args(["connection", "up"])
|
||||||
|
.arg(&self.con_name)
|
||||||
|
.output()
|
||||||
|
.await
|
||||||
|
.map_err(HotspotError::IoError)?;
|
||||||
|
|
||||||
if !cmd.status.success() {
|
trace!("nmcli (std): {}", String::from_utf8_lossy(&cmd.stdout));
|
||||||
return Err(HotspotError::NonZeroExit(cmd));
|
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;
|
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>,
|
controller: Ws2812<Spi>,
|
||||||
}
|
}
|
||||||
|
|
||||||
const STATUS_DURATION: Duration = Duration::from_secs(1); // 1s sleep for all status led signals
|
impl SpiLed {
|
||||||
|
|
||||||
impl Led {
|
|
||||||
pub fn new() -> Result<Self, Error> {
|
pub fn new() -> Result<Self, Error> {
|
||||||
let spi = Spi::new(Bus::Spi0, SlaveSelect::Ss0, 3_800_000, Mode::Mode0)?;
|
let spi = Spi::new(Bus::Spi0, SlaveSelect::Ss0, 3_800_000, Mode::Mode0)?;
|
||||||
let controller = Ws2812::new(spi);
|
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())?;
|
self.controller.write(NamedColor::Green.into_iter())?;
|
||||||
sleep(STATUS_DURATION).await;
|
sleep(STATUS_DURATION).await;
|
||||||
self.turn_off()?;
|
self.turn_off()?;
|
||||||
Ok(())
|
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())?;
|
self.controller.write(NamedColor::Red.into_iter())?;
|
||||||
sleep(STATUS_DURATION).await;
|
sleep(STATUS_DURATION).await;
|
||||||
self.turn_off()?;
|
self.turn_off()?;
|
||||||
Ok(())
|
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 id_store::IDStore;
|
||||||
use led::Led;
|
use led::{SpiLed, StatusLed};
|
||||||
use log::{LevelFilter, debug, error, info, warn};
|
use log::{LevelFilter, debug, error, info, warn};
|
||||||
use pm3::run_pm3;
|
use pm3::run_pm3;
|
||||||
use rppal::pwm::Channel;
|
use rppal::pwm::Channel;
|
||||||
@ -13,11 +14,15 @@ use tokio::{
|
|||||||
};
|
};
|
||||||
use webserver::start_webserver;
|
use webserver::start_webserver;
|
||||||
|
|
||||||
|
#[cfg(feature = "mock_pi")]
|
||||||
|
use mock::{MockBuzzer, MockHotspot, MockLed};
|
||||||
|
|
||||||
mod buzzer;
|
mod buzzer;
|
||||||
mod color;
|
mod color;
|
||||||
mod hotspot;
|
mod hotspot;
|
||||||
mod id_store;
|
mod id_store;
|
||||||
mod led;
|
mod led;
|
||||||
|
mod mock;
|
||||||
mod parser;
|
mod parser;
|
||||||
mod pm3;
|
mod pm3;
|
||||||
mod tally_id;
|
mod tally_id;
|
||||||
@ -48,7 +53,10 @@ fn setup_logger() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Signal the user success via buzzer and led
|
/// 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 buzzer_guard = gpio_buzzer.lock().await;
|
||||||
let mut led_guard = status_led.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
|
/// 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 buzzer_guard = gpio_buzzer.lock().await;
|
||||||
let mut led_guard = status_led.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]
|
#[tokio::main]
|
||||||
async fn main() -> Result<(), Box<dyn Error>> {
|
async fn main() -> Result<(), Box<dyn Error>> {
|
||||||
setup_logger();
|
setup_logger();
|
||||||
@ -109,9 +162,9 @@ async fn main() -> Result<(), Box<dyn Error>> {
|
|||||||
debug!("created store sucessfully");
|
debug!("created store sucessfully");
|
||||||
|
|
||||||
let store: Arc<Mutex<IDStore>> = Arc::new(Mutex::new(raw_store));
|
let store: Arc<Mutex<IDStore>> = Arc::new(Mutex::new(raw_store));
|
||||||
let gpio_buzzer: Arc<Mutex<GPIOBuzzer>> =
|
let gpio_buzzer = create_buzzer()?;
|
||||||
Arc::new(Mutex::new(GPIOBuzzer::new(PWM_CHANNEL_BUZZER)?));
|
let status_led = create_status_led()?;
|
||||||
let status_led: Arc<Mutex<Led>> = Arc::new(Mutex::new(Led::new()?));
|
let hotspot = create_hotspot()?;
|
||||||
|
|
||||||
let hotspot_ids: Vec<TallyID> = env::var("HOTSPOT_IDS")
|
let hotspot_ids: Vec<TallyID> = env::var("HOTSPOT_IDS")
|
||||||
.map(|ids| ids.split(";").map(|id| TallyID(id.to_owned())).collect())
|
.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) {
|
if hotspot_ids.contains(&tally_id) {
|
||||||
info!("Enableing hotspot");
|
info!("Enableing hotspot");
|
||||||
hotspot::enable_hotspot().await.unwrap_or_else(|err| {
|
hotspot.enable_hotspot().await.unwrap_or_else(|err| {
|
||||||
error!("Hotspot: {err}");
|
error!("Hotspot: {err}");
|
||||||
});
|
});
|
||||||
// TODO: Should the ID be added anyway or ignored ?
|
// 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