pub use device_envoy_core::led::{Led, LedLevel, OnLevel};
pub mod led_generated;
#[cfg(target_os = "none")]
#[doc(hidden)]
pub use paste;
#[cfg(target_os = "none")]
use embassy_sync::{blocking_mutex::raw::CriticalSectionRawMutex, signal::Signal};
#[cfg(target_os = "none")]
use embassy_time::{Duration, Timer};
#[cfg(target_os = "none")]
use esp_hal::gpio::{Level, Output};
#[cfg(target_os = "none")]
use heapless::Vec;
#[cfg(target_os = "none")]
#[doc(hidden)] pub const DEFAULT_MAX_STEPS: usize = 32;
#[cfg(target_os = "none")]
#[derive(Clone)]
#[doc(hidden)] pub enum LedCommand<const MAX_STEPS: usize> {
Set(LedLevel),
Animate(Vec<(LedLevel, Duration), MAX_STEPS>),
}
#[cfg(target_os = "none")]
#[doc(hidden)] pub type LedOuterStatic<const MAX_STEPS: usize> =
Signal<CriticalSectionRawMutex, LedCommand<MAX_STEPS>>;
#[cfg(target_os = "none")]
#[doc(hidden)] pub struct LedStatic<const MAX_STEPS: usize> {
outer: LedOuterStatic<MAX_STEPS>,
}
#[cfg(target_os = "none")]
impl<const MAX_STEPS: usize> LedStatic<MAX_STEPS> {
#[doc(hidden)] pub const fn new() -> Self {
Self {
outer: Signal::new(),
}
}
#[doc(hidden)] pub fn outer(&self) -> &LedOuterStatic<MAX_STEPS> {
&self.outer
}
}
#[cfg(target_os = "none")]
#[doc(hidden)] pub fn set_pin_for_led_level(led_level: LedLevel, pin: &mut Output<'_>, on_level: OnLevel) {
let pin_level = match (led_level, on_level) {
(LedLevel::On, OnLevel::High) | (LedLevel::Off, OnLevel::Low) => Level::High,
(LedLevel::Off, OnLevel::High) | (LedLevel::On, OnLevel::Low) => Level::Low,
};
pin.set_level(pin_level);
}
#[cfg(target_os = "none")]
#[doc(hidden)] pub async fn run_set_level_loop<const MAX_STEPS: usize>(
led_level: LedLevel,
outer_static: &'static LedOuterStatic<MAX_STEPS>,
pin: &mut Output<'_>,
on_level: OnLevel,
) -> LedCommand<MAX_STEPS> {
set_pin_for_led_level(led_level, pin, on_level);
loop {
match outer_static.wait().await {
LedCommand::Set(new_led_level) => {
if new_led_level == led_level {
continue;
}
return LedCommand::Set(new_led_level);
}
other => return other,
}
}
}
#[cfg(target_os = "none")]
#[doc(hidden)] pub async fn run_animation_loop<const MAX_STEPS: usize>(
animation: Vec<(LedLevel, Duration), MAX_STEPS>,
outer_static: &'static LedOuterStatic<MAX_STEPS>,
pin: &mut Output<'_>,
on_level: OnLevel,
) -> LedCommand<MAX_STEPS> {
if animation.is_empty() {
return LedCommand::Animate(animation);
}
let mut frame_index = 0;
loop {
let (led_level, duration) = animation[frame_index];
set_pin_for_led_level(led_level, pin, on_level);
frame_index = (frame_index + 1) % animation.len();
match embassy_futures::select::select(Timer::after(duration), outer_static.wait()).await {
embassy_futures::select::Either::First(_) => {}
embassy_futures::select::Either::Second(command) => return command,
}
}
}
#[cfg(target_os = "none")]
#[doc(hidden)]
#[macro_export]
macro_rules! led {
($($tt:tt)*) => { $crate::__led_impl! { $($tt)* } };
}
#[cfg(target_os = "none")]
#[doc(hidden)]
#[macro_export]
macro_rules! __led_impl {
(
$vis:vis $name:ident {
$($fields:tt)*
}
) => {
$crate::__led_impl! {
@__parse
vis: $vis,
name: $name,
pin: [],
max_steps: [],
fields: [ $($fields)* ]
}
};
(@__parse
vis: $vis:vis,
name: $name:ident,
pin: [],
max_steps: [$($max_steps:expr)?],
fields: [ pin: $pin:ident $(, $($rest:tt)*)? ]
) => {
$crate::__led_impl! {
@__parse
vis: $vis,
name: $name,
pin: [$pin],
max_steps: [$($max_steps)?],
fields: [ $($($rest)*)? ]
}
};
(@__parse
vis: $vis:vis,
name: $name:ident,
pin: [$_pin_seen:ident],
max_steps: [$($max_steps:expr)?],
fields: [ pin: $pin:ident $(, $($rest:tt)*)? ]
) => {
compile_error!("led! duplicate `pin` field");
};
(@__parse
vis: $vis:vis,
name: $name:ident,
pin: [$($pin:ident)?],
max_steps: [],
fields: [ max_steps: $max_steps:expr $(, $($rest:tt)*)? ]
) => {
$crate::__led_impl! {
@__parse
vis: $vis,
name: $name,
pin: [$($pin)?],
max_steps: [$max_steps],
fields: [ $($($rest)*)? ]
}
};
(@__parse
vis: $vis:vis,
name: $name:ident,
pin: [$($pin:ident)?],
max_steps: [$_max_steps_seen:expr],
fields: [ max_steps: $max_steps:expr $(, $($rest:tt)*)? ]
) => {
compile_error!("led! duplicate `max_steps` field");
};
(@__parse
vis: $vis:vis,
name: $name:ident,
pin: [$($pin:ident)?],
max_steps: [$($max_steps:expr)?],
fields: [ ]
) => {
$crate::__led_impl! {
@__finish
vis: $vis,
name: $name,
pin: [$($pin)?],
max_steps: [$($max_steps)?]
}
};
(@__parse
vis: $vis:vis,
name: $name:ident,
pin: [$($pin:ident)?],
max_steps: [$($max_steps:expr)?],
fields: [ $field:ident : $value:expr $(, $($rest:tt)*)? ]
) => {
compile_error!("led! unknown field; expected `pin` or `max_steps`");
};
(@__finish
vis: $vis:vis,
name: $name:ident,
pin: [],
max_steps: [$($max_steps:expr)?]
) => {
compile_error!("led! missing required `pin` field");
};
(@__finish
vis: $vis:vis,
name: $name:ident,
pin: [$pin:ident],
max_steps: []
) => {
$crate::__led_impl!(@__emit vis: $vis, name: $name, pin: $pin, max_steps: $crate::led::DEFAULT_MAX_STEPS);
};
(@__finish
vis: $vis:vis,
name: $name:ident,
pin: [$pin:ident],
max_steps: [$max_steps:expr]
) => {
$crate::__led_impl!(@__emit vis: $vis, name: $name, pin: $pin, max_steps: $max_steps);
};
(
@__emit
vis: $vis:vis,
name: $name:ident,
pin: $pin:ident,
max_steps: $max_steps:expr
) => {
$crate::led::paste::paste! {
#[cfg(target_os = "none")]
const [<$name:upper _MAX_STEPS>]: usize = $max_steps;
#[cfg(target_os = "none")]
#[allow(non_upper_case_globals)]
static [<$name:upper _STATIC>]: $crate::led::LedStatic<{ [<$name:upper _MAX_STEPS>] }> =
$crate::led::LedStatic::new();
#[cfg(target_os = "none")]
#[allow(non_camel_case_types)]
$vis struct $name(&'static $crate::led::LedOuterStatic<{ [<$name:upper _MAX_STEPS>] }>);
#[cfg(target_os = "none")]
impl $name {
$vis const MAX_STEPS: usize = [<$name:upper _MAX_STEPS>];
pub fn new(
pin: $crate::esp_hal::peripherals::$pin<'static>,
on_level: $crate::led::OnLevel,
spawner: embassy_executor::Spawner,
) -> $crate::Result<Self> {
let pin_output = $crate::esp_hal::gpio::Output::new(
pin,
$crate::esp_hal::gpio::Level::Low,
$crate::esp_hal::gpio::OutputConfig::default(),
);
let token = [<__led_task_ $name:snake>](
[<$name:upper _STATIC>].outer(),
pin_output,
on_level,
);
spawner.spawn(token.map_err($crate::Error::TaskSpawn)?);
Ok(Self([<$name:upper _STATIC>].outer()))
}
}
#[cfg(target_os = "none")]
impl $crate::led::Led for $name {
fn set_level(&self, led_level: $crate::led::LedLevel) {
self.0.signal($crate::led::LedCommand::Set(led_level));
}
fn animate<I>(&self, frames: I)
where
I: IntoIterator,
I::Item: ::core::borrow::Borrow<(
$crate::led::LedLevel,
embassy_time::Duration,
)>,
{
let mut animation: heapless::Vec<
($crate::led::LedLevel, embassy_time::Duration),
{ [<$name:upper _MAX_STEPS>] },
> = heapless::Vec::new();
for frame in frames {
let frame = *::core::borrow::Borrow::borrow(&frame);
animation
.push(frame)
.expect("LED animation fits within MAX_STEPS");
}
self.0.signal($crate::led::LedCommand::Animate(animation));
}
}
#[cfg(target_os = "none")]
#[embassy_executor::task]
async fn [<__led_task_ $name:snake>](
outer_static: &'static $crate::led::LedOuterStatic<{ [<$name:upper _MAX_STEPS>] }>,
mut pin: $crate::esp_hal::gpio::Output<'static>,
on_level: $crate::led::OnLevel,
) -> ! {
let mut command = $crate::led::LedCommand::Set($crate::led::LedLevel::Off);
$crate::led::set_pin_for_led_level($crate::led::LedLevel::Off, &mut pin, on_level);
loop {
command = match command {
$crate::led::LedCommand::Set(led_level) => {
$crate::led::run_set_level_loop(led_level, outer_static, &mut pin, on_level).await
}
$crate::led::LedCommand::Animate(animation) => {
$crate::led::run_animation_loop(animation, outer_static, &mut pin, on_level).await
}
};
}
}
}
};
}
#[cfg(target_os = "none")]
#[doc(inline)]
pub use led;