rsiot/components/cmp_raspberrypi_gpio/
fn_process.rsuse std::time::Duration;
use rppal::gpio::{Gpio, InputPin, Level, OutputPin};
use tokio::{task::JoinSet, time::sleep};
use crate::{
executor::CmpInOut,
message::{Message, MsgDataBound, ServiceBound},
};
use super::{Config, Error, PullMode};
const INPUT_READ_DELAY: Duration = Duration::from_millis(100);
pub async fn fn_process<TMsg, TService>(
config: Config<TMsg>,
in_out: CmpInOut<TMsg, TService>,
) -> super::Result<()>
where
TMsg: MsgDataBound + 'static,
TService: ServiceBound + 'static,
{
let gpio = Gpio::new()?;
let mut task_set: JoinSet<super::Result<()>> = JoinSet::new();
for input_config in config.inputs {
let pin = gpio.get(input_config.pin_number)?;
let pin = match input_config.pull_mode {
PullMode::Floating => pin.into_input(),
PullMode::Up => pin.into_input_pullup(),
PullMode::Down => pin.into_input_pulldown(),
};
task_set.spawn(input_pin(pin, input_config.fn_output, in_out.clone()));
}
for output_config in config.outputs {
let pin = gpio.get(output_config.pin_number)?.into_output();
task_set.spawn(output_pin(
pin,
output_config.fn_input,
in_out.clone(),
output_config.is_low_triggered,
));
}
while let Some(res) = task_set.join_next().await {
res??
}
Ok(())
}
async fn input_pin<TMsg, TService>(
pin: InputPin,
fn_output: fn(bool) -> Message<TMsg>,
in_out: CmpInOut<TMsg, TService>,
) -> super::Result<()>
where
TMsg: MsgDataBound,
TService: ServiceBound,
{
let mut prev_level: Option<bool> = None;
loop {
let level_read = pin.read();
let level = match level_read {
Level::High => true,
Level::Low => false,
};
match prev_level {
None => {
prev_level = Some(level);
}
Some(prev_level_value) => {
if prev_level_value == level {
sleep(INPUT_READ_DELAY).await;
continue;
} else {
prev_level = Some(level)
}
}
}
let msg = (fn_output)(level);
in_out.send_output(msg).await.map_err(Error::CmpOutput)?;
sleep(INPUT_READ_DELAY).await;
}
}
async fn output_pin<TMsg, TService>(
mut pin: OutputPin,
fn_input: fn(Message<TMsg>) -> Option<bool>,
mut in_out: CmpInOut<TMsg, TService>,
is_low_triggered: bool,
) -> super::Result<()>
where
TMsg: MsgDataBound,
TService: ServiceBound,
{
if is_low_triggered {
pin.set_high();
} else {
pin.set_low();
}
while let Ok(msg) = in_out.recv_input().await {
let Some(control) = (fn_input)(msg) else {
continue;
};
if is_low_triggered ^ control {
pin.set_high();
} else {
pin.set_low();
}
}
Ok(())
}