#![cfg_attr(
feature = "doc-images",
doc = ::embed_doc_image::embed_image!(
"led_strip_simple",
"docs/assets/led_strip_simple.png"
),
doc = ::embed_doc_image::embed_image!(
"led_strip_animated",
"docs/assets/led_strip_animated.png"
)
)]
pub use device_envoy_core::led_strip::*;
pub mod led_strip_generated;
#[doc(hidden)]
pub struct LedStripEsp<const N: usize, const MAX_FRAMES: usize> {
command_signal: &'static LedStripCommandSignal<N, MAX_FRAMES>,
}
impl<const N: usize, const MAX_FRAMES: usize> LedStripEsp<N, MAX_FRAMES> {
#[doc(hidden)]
pub const fn new_static() -> LedStripStatic<N, MAX_FRAMES> {
LedStripStatic::new_static()
}
#[doc(hidden)]
pub fn new(led_strip_static: &'static LedStripStatic<N, MAX_FRAMES>) -> Self {
Self {
command_signal: led_strip_static.command_signal(),
}
}
#[doc(hidden)]
pub fn __command_signal(&self) -> &'static LedStripCommandSignal<N, MAX_FRAMES> {
self.command_signal
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum Engine {
Rmt,
Spi,
}
impl Default for Engine {
fn default() -> Self {
#[cfg(esp_has_rmt)]
{
return Self::Rmt;
}
#[cfg(not(esp_has_rmt))]
{
return Self::Spi;
}
#[allow(unreachable_code)]
Self::Rmt
}
}
#[doc(hidden)]
pub const CURRENT_DEFAULT: Current = Current::Milliamps(250);
#[cfg(all(target_os = "none", esp_has_rmt))]
use embassy_futures::select::{Either, select};
#[cfg(all(target_os = "none", esp_has_rmt))]
use embassy_time::Timer;
#[cfg(all(target_os = "none", esp_has_rmt))]
use esp_hal::gpio::Level;
#[cfg(all(target_os = "none", esp_has_rmt))]
use esp_hal::rmt::{Channel, PulseCode, Tx};
#[cfg(all(target_os = "none", esp_has_rmt))]
const BIT0: PulseCode = PulseCode::new(Level::High, 8, Level::Low, 17);
#[cfg(all(target_os = "none", esp_has_rmt))]
const BIT1: PulseCode = PulseCode::new(Level::High, 16, Level::Low, 9);
#[cfg(all(target_os = "none", esp_has_rmt))]
#[doc(hidden)]
pub struct RmtWs2812<'d, const LEDS: usize, const PULSES: usize> {
channel: Option<Channel<'d, esp_hal::Blocking, Tx>>,
pulse_buf: [PulseCode; PULSES],
}
#[cfg(all(target_os = "none", esp_has_rmt))]
impl<'d, const LEDS: usize, const PULSES: usize> RmtWs2812<'d, LEDS, PULSES> {
#[must_use]
pub fn new(channel: Channel<'d, esp_hal::Blocking, Tx>) -> Self {
assert_eq!(
PULSES,
LEDS * 24 + 1,
"PULSES must equal LEDS * 24 + 1; this is enforced by led_strip!"
);
Self {
channel: Some(channel),
pulse_buf: [PulseCode::end_marker(); PULSES],
}
}
pub fn write(&mut self, frame: &Frame1d<LEDS>) -> Result<(), WritingError> {
for (led_index, pixel) in frame.iter().enumerate() {
let grb: u32 = ((pixel.g as u32) << 16) | ((pixel.r as u32) << 8) | (pixel.b as u32);
for bit_index in 0..24 {
let bit = (grb >> (23 - bit_index)) & 1;
self.pulse_buf[led_index * 24 + bit_index] = if bit == 1 { BIT1 } else { BIT0 };
}
}
self.pulse_buf[LEDS * 24] = PulseCode::end_marker();
let channel = self.channel.take().ok_or(WritingError::ChannelMissing)?;
let transfer = channel
.transmit(&self.pulse_buf)
.map_err(|_| WritingError::TransmitStart)?;
match transfer.wait() {
Ok(channel) => {
self.channel = Some(channel);
Ok(())
}
Err((err, channel)) => {
self.channel = Some(channel);
Err(WritingError::Transmit(err))
}
}
}
}
#[cfg(all(target_os = "none", esp_has_rmt))]
#[doc(hidden)]
#[derive(Debug)]
pub enum WritingError {
ChannelMissing,
TransmitStart,
Transmit(esp_hal::rmt::Error),
}
#[doc(hidden)]
#[cfg(all(target_os = "none", esp_has_rmt))]
pub async fn led_strip_device_loop<
'd,
const LEDS: usize,
const PULSES: usize,
const MAX_FRAMES: usize,
>(
mut driver: RmtWs2812<'d, LEDS, PULSES>,
command_signal: &'static LedStripCommandSignal<LEDS, MAX_FRAMES>,
combo_table: &'static [u8; 256],
) -> ! {
let _ = driver.write(&Frame1d::new());
let mut pending: Option<Command<LEDS, MAX_FRAMES>> = None;
loop {
let command = match pending.take() {
Some(cmd) => cmd,
None => command_signal.wait().await,
};
match command {
Command::DisplayStatic(mut frame) => {
apply_correction(&mut frame, combo_table);
let _ = driver.write(&frame);
}
Command::Animate(sequence) => {
'animate: loop {
for (mut frame, duration) in sequence.iter().cloned() {
apply_correction(&mut frame, combo_table);
let _ = driver.write(&frame);
match select(Timer::after(duration), command_signal.wait()).await {
Either::First(_) => {
}
Either::Second(new_command) => {
pending = Some(new_command);
break 'animate;
}
}
}
if let Some(new_command) = command_signal.try_take() {
pending = Some(new_command);
break 'animate;
}
}
}
}
}
}
#[doc = include_str!("docs/current_limiting_and_gamma.md")]
#[doc(hidden)]
#[macro_export]
macro_rules! led_strip {
($($tt:tt)*) => { $crate::__led_strip_entry! { $($tt)* } };
}
#[doc(hidden)]
#[macro_export]
macro_rules! __led_strip_entry {
(
$name:ident {
$($before:tt)*
led2d: { $($led2d_fields:tt)* }
$($after:tt)*
}
) => {
compile_error!("led_strip! is 1D-only. Use led2d! for panel generation.");
};
(
$name:ident {
$($fields:tt)*
}
) => {
$crate::__led_strip_collect_fields!{
name = $name,
pin = [],
len = [],
max_current = [],
engine = [],
gamma = [],
max_frames = [],
reset_us = [],
fields = [$($fields)*],
}
};
(
$vis:vis $name:ident {
$($fields:tt)*
}
) => {
$crate::__paste! {
$crate::__led_strip_entry! {
[<__ $name _visibility_inner>] {
$($fields)*
}
}
$vis type $name = [<__ $name _visibility_inner>];
}
};
}
#[cfg(target_os = "none")]
#[doc(inline)]
pub use led_strip;
#[doc(hidden)]
#[macro_export]
macro_rules! __led_strip_collect_fields {
(
name = $name:ident,
pin = [$pin:ident],
len = [$len:expr],
max_current = [$($max_current:expr)?],
engine = [$($engine:tt)?],
gamma = [$($gamma:expr)?],
max_frames = [$($max_frames:expr)?],
reset_us = [$($reset_us:expr)?],
fields = [],
) => {
$crate::__led_strip_dispatch_engine!(
$name,
$pin,
$len,
$crate::__led_strip_max_current_or_default!([$($max_current)?]),
[$($engine)?],
[$($gamma)?],
[$($max_frames)?],
[$($reset_us)?],
);
};
(
name = $name:ident,
pin = [],
len = [$($len:expr)?],
max_current = [$($max_current:expr)?],
engine = [$($engine:tt)?],
gamma = [$($gamma:expr)?],
max_frames = [$($max_frames:expr)?],
reset_us = [$($reset_us:expr)?],
fields = [],
) => {
compile_error!("led_strip! missing required `pin` field");
};
(
name = $name:ident,
pin = [$pin:ident],
len = [],
max_current = [$($max_current:expr)?],
engine = [$($engine:tt)?],
gamma = [$($gamma:expr)?],
max_frames = [$($max_frames:expr)?],
reset_us = [$($reset_us:expr)?],
fields = [],
) => {
compile_error!("led_strip! missing required `len` field");
};
(
name = $name:ident,
pin = [],
len = [$($len:expr)?],
max_current = [$($max_current:expr)?],
engine = [$($engine:tt)?],
gamma = [$($gamma:expr)?],
max_frames = [$($max_frames:expr)?],
reset_us = [$($reset_us:expr)?],
fields = [pin: $pin:ident $(, $($rest:tt)*)?],
) => {
$crate::__led_strip_collect_fields!{
name = $name,
pin = [$pin],
len = [$($len)?],
max_current = [$($max_current)?],
engine = [$($engine)?],
gamma = [$($gamma)?],
max_frames = [$($max_frames)?],
reset_us = [$($reset_us)?],
fields = [$($($rest)*)?],
}
};
(
name = $name:ident,
pin = [$already_pin:ident],
len = [$($len:expr)?],
max_current = [$($max_current:expr)?],
engine = [$($engine:tt)?],
gamma = [$($gamma:expr)?],
max_frames = [$($max_frames:expr)?],
reset_us = [$($reset_us:expr)?],
fields = [pin: $pin:ident $(, $($rest:tt)*)?],
) => {
compile_error!("led_strip! duplicate `pin` field");
};
(
name = $name:ident,
pin = [$($pin:ident)?],
len = [],
max_current = [$($max_current:expr)?],
engine = [$($engine:tt)?],
gamma = [$($gamma:expr)?],
max_frames = [$($max_frames:expr)?],
reset_us = [$($reset_us:expr)?],
fields = [len: $len:expr $(, $($rest:tt)*)?],
) => {
$crate::__led_strip_collect_fields!{
name = $name,
pin = [$($pin)?],
len = [$len],
max_current = [$($max_current)?],
engine = [$($engine)?],
gamma = [$($gamma)?],
max_frames = [$($max_frames)?],
reset_us = [$($reset_us)?],
fields = [$($($rest)*)?],
}
};
(
name = $name:ident,
pin = [$($pin:ident)?],
len = [$already_len:expr],
max_current = [$($max_current:expr)?],
engine = [$($engine:tt)?],
gamma = [$($gamma:expr)?],
max_frames = [$($max_frames:expr)?],
reset_us = [$($reset_us:expr)?],
fields = [len: $len:expr $(, $($rest:tt)*)?],
) => {
compile_error!("led_strip! duplicate `len` field");
};
(
name = $name:ident,
pin = [$($pin:ident)?],
len = [$($len:expr)?],
max_current = [],
engine = [$($engine:tt)?],
gamma = [$($gamma:expr)?],
max_frames = [$($max_frames:expr)?],
reset_us = [$($reset_us:expr)?],
fields = [max_current: $max_current:expr $(, $($rest:tt)*)?],
) => {
$crate::__led_strip_collect_fields!{
name = $name,
pin = [$($pin)?],
len = [$($len)?],
max_current = [$max_current],
engine = [$($engine)?],
gamma = [$($gamma)?],
max_frames = [$($max_frames)?],
reset_us = [$($reset_us)?],
fields = [$($($rest)*)?],
}
};
(
name = $name:ident,
pin = [$($pin:ident)?],
len = [$($len:expr)?],
max_current = [$already_max_current:expr],
engine = [$($engine:tt)?],
gamma = [$($gamma:expr)?],
max_frames = [$($max_frames:expr)?],
reset_us = [$($reset_us:expr)?],
fields = [max_current: $max_current:expr $(, $($rest:tt)*)?],
) => {
compile_error!("led_strip! duplicate `max_current` field");
};
(
name = $name:ident,
pin = [$($pin:ident)?],
len = [$($len:expr)?],
max_current = [$($max_current:expr)?],
engine = [],
gamma = [$($gamma:expr)?],
max_frames = [$($max_frames:expr)?],
reset_us = [$($reset_us:expr)?],
fields = [engine: Engine::Spi $(, $($rest:tt)*)?],
) => {
$crate::__led_strip_collect_fields!{
name = $name,
pin = [$($pin)?],
len = [$($len)?],
max_current = [$($max_current)?],
engine = [Spi],
gamma = [$($gamma)?],
max_frames = [$($max_frames)?],
reset_us = [$($reset_us)?],
fields = [$($($rest)*)?],
}
};
(
name = $name:ident,
pin = [$($pin:ident)?],
len = [$($len:expr)?],
max_current = [$($max_current:expr)?],
engine = [],
gamma = [$($gamma:expr)?],
max_frames = [$($max_frames:expr)?],
reset_us = [$($reset_us:expr)?],
fields = [engine: $crate::led_strip::Engine::Spi $(, $($rest:tt)*)?],
) => {
$crate::__led_strip_collect_fields!{
name = $name,
pin = [$($pin)?],
len = [$($len)?],
max_current = [$($max_current)?],
engine = [Spi],
gamma = [$($gamma)?],
max_frames = [$($max_frames)?],
reset_us = [$($reset_us)?],
fields = [$($($rest)*)?],
}
};
(
name = $name:ident,
pin = [$($pin:ident)?],
len = [$($len:expr)?],
max_current = [$($max_current:expr)?],
engine = [],
gamma = [$($gamma:expr)?],
max_frames = [$($max_frames:expr)?],
reset_us = [$($reset_us:expr)?],
fields = [engine: device_envoy_esp::led_strip::Engine::Spi $(, $($rest:tt)*)?],
) => {
$crate::__led_strip_collect_fields!{
name = $name,
pin = [$($pin)?],
len = [$($len)?],
max_current = [$($max_current)?],
engine = [Spi],
gamma = [$($gamma)?],
max_frames = [$($max_frames)?],
reset_us = [$($reset_us)?],
fields = [$($($rest)*)?],
}
};
(
name = $name:ident,
pin = [$($pin:ident)?],
len = [$($len:expr)?],
max_current = [$($max_current:expr)?],
engine = [],
gamma = [$($gamma:expr)?],
max_frames = [$($max_frames:expr)?],
reset_us = [$($reset_us:expr)?],
fields = [engine: Engine::Rmt $(, $($rest:tt)*)?],
) => {
$crate::__led_strip_collect_fields!{
name = $name,
pin = [$($pin)?],
len = [$($len)?],
max_current = [$($max_current)?],
engine = [Rmt],
gamma = [$($gamma)?],
max_frames = [$($max_frames)?],
reset_us = [$($reset_us)?],
fields = [$($($rest)*)?],
}
};
(
name = $name:ident,
pin = [$($pin:ident)?],
len = [$($len:expr)?],
max_current = [$($max_current:expr)?],
engine = [],
gamma = [$($gamma:expr)?],
max_frames = [$($max_frames:expr)?],
reset_us = [$($reset_us:expr)?],
fields = [engine: $crate::led_strip::Engine::Rmt $(, $($rest:tt)*)?],
) => {
$crate::__led_strip_collect_fields!{
name = $name,
pin = [$($pin)?],
len = [$($len)?],
max_current = [$($max_current)?],
engine = [Rmt],
gamma = [$($gamma)?],
max_frames = [$($max_frames)?],
reset_us = [$($reset_us)?],
fields = [$($($rest)*)?],
}
};
(
name = $name:ident,
pin = [$($pin:ident)?],
len = [$($len:expr)?],
max_current = [$($max_current:expr)?],
engine = [],
gamma = [$($gamma:expr)?],
max_frames = [$($max_frames:expr)?],
reset_us = [$($reset_us:expr)?],
fields = [engine: device_envoy_esp::led_strip::Engine::Rmt $(, $($rest:tt)*)?],
) => {
$crate::__led_strip_collect_fields!{
name = $name,
pin = [$($pin)?],
len = [$($len)?],
max_current = [$($max_current)?],
engine = [Rmt],
gamma = [$($gamma)?],
max_frames = [$($max_frames)?],
reset_us = [$($reset_us)?],
fields = [$($($rest)*)?],
}
};
(
name = $name:ident,
pin = [$($pin:ident)?],
len = [$($len:expr)?],
max_current = [$($max_current:expr)?],
engine = [$already_engine:tt],
gamma = [$($gamma:expr)?],
max_frames = [$($max_frames:expr)?],
reset_us = [$($reset_us:expr)?],
fields = [engine: $ignored:path $(, $($rest:tt)*)?],
) => {
compile_error!("led_strip! duplicate `engine` field");
};
(
name = $name:ident,
pin = [$($pin:ident)?],
len = [$($len:expr)?],
max_current = [$($max_current:expr)?],
engine = [],
gamma = [$($gamma:expr)?],
max_frames = [$($max_frames:expr)?],
reset_us = [$($reset_us:expr)?],
fields = [engine: $ignored:path $(, $($rest:tt)*)?],
) => {
compile_error!("led_strip! engine must be Engine::Rmt or Engine::Spi");
};
(
name = $name:ident,
pin = [$($pin:ident)?],
len = [$($len:expr)?],
max_current = [$($max_current:expr)?],
engine = [$($engine:tt)?],
gamma = [],
max_frames = [$($max_frames:expr)?],
reset_us = [$($reset_us:expr)?],
fields = [gamma: $gamma:expr $(, $($rest:tt)*)?],
) => {
$crate::__led_strip_collect_fields!{
name = $name,
pin = [$($pin)?],
len = [$($len)?],
max_current = [$($max_current)?],
engine = [$($engine)?],
gamma = [$gamma],
max_frames = [$($max_frames)?],
reset_us = [$($reset_us)?],
fields = [$($($rest)*)?],
}
};
(
name = $name:ident,
pin = [$($pin:ident)?],
len = [$($len:expr)?],
max_current = [$($max_current:expr)?],
engine = [$($engine:tt)?],
gamma = [$already_gamma:expr],
max_frames = [$($max_frames:expr)?],
reset_us = [$($reset_us:expr)?],
fields = [gamma: $gamma:expr $(, $($rest:tt)*)?],
) => {
compile_error!("led_strip! duplicate `gamma` field");
};
(
name = $name:ident,
pin = [$($pin:ident)?],
len = [$($len:expr)?],
max_current = [$($max_current:expr)?],
engine = [$($engine:tt)?],
gamma = [$($gamma:expr)?],
max_frames = [],
reset_us = [$($reset_us:expr)?],
fields = [max_frames: $max_frames:expr $(, $($rest:tt)*)?],
) => {
$crate::__led_strip_collect_fields!{
name = $name,
pin = [$($pin)?],
len = [$($len)?],
max_current = [$($max_current)?],
engine = [$($engine)?],
gamma = [$($gamma)?],
max_frames = [$max_frames],
reset_us = [$($reset_us)?],
fields = [$($($rest)*)?],
}
};
(
name = $name:ident,
pin = [$($pin:ident)?],
len = [$($len:expr)?],
max_current = [$($max_current:expr)?],
engine = [$($engine:tt)?],
gamma = [$($gamma:expr)?],
max_frames = [$already_max_frames:expr],
reset_us = [$($reset_us:expr)?],
fields = [max_frames: $max_frames:expr $(, $($rest:tt)*)?],
) => {
compile_error!("led_strip! duplicate `max_frames` field");
};
(
name = $name:ident,
pin = [$($pin:ident)?],
len = [$($len:expr)?],
max_current = [$($max_current:expr)?],
engine = [$($engine:tt)?],
gamma = [$($gamma:expr)?],
max_frames = [$($max_frames:expr)?],
reset_us = [],
fields = [reset_us: $reset_us:expr $(, $($rest:tt)*)?],
) => {
$crate::__led_strip_collect_fields!{
name = $name,
pin = [$($pin)?],
len = [$($len)?],
max_current = [$($max_current)?],
engine = [$($engine)?],
gamma = [$($gamma)?],
max_frames = [$($max_frames)?],
reset_us = [$reset_us],
fields = [$($($rest)*)?],
}
};
(
name = $name:ident,
pin = [$($pin:ident)?],
len = [$($len:expr)?],
max_current = [$($max_current:expr)?],
engine = [$($engine:tt)?],
gamma = [$($gamma:expr)?],
max_frames = [$($max_frames:expr)?],
reset_us = [$already_reset_us:expr],
fields = [reset_us: $reset_us:expr $(, $($rest:tt)*)?],
) => {
compile_error!("led_strip! duplicate `reset_us` field");
};
(
name = $name:ident,
pin = [$($pin:ident)?],
len = [$($len:expr)?],
max_current = [$($max_current:expr)?],
engine = [$($engine:tt)?],
gamma = [$($gamma:expr)?],
max_frames = [$($max_frames:expr)?],
reset_us = [$($reset_us:expr)?],
fields = [$field:ident : $value:expr $(, $($rest:tt)*)?],
) => {
compile_error!(
"led_strip! unknown field; expected `pin`, `len`, `max_current`, `engine`, `gamma`, `max_frames`, or `reset_us`"
);
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! __led_strip_max_current_or_default {
([$max_current:expr]) => {
$max_current
};
([]) => {
$crate::led_strip::CURRENT_DEFAULT
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! __led_strip_inner {
(
$name:ident,
$pin:ident,
$len:expr,
$max_current:expr,
[$($gamma:expr)?],
[$($max_frames:expr)?],
[$($led2d_layout:expr)?],
[$($led2d_font:expr)?],
) => {
$crate::__led_strip_impl!{
name = $name,
pin = $pin,
len = $len,
max_current = $max_current,
gamma = $crate::__led_strip_first_or_default!(
[$($gamma)?],
$crate::led_strip::GAMMA_DEFAULT
),
max_frames = $crate::__led_strip_first_or_default!(
[$($max_frames)?],
$crate::led_strip::MAX_FRAMES_DEFAULT
),
led2d_layout = [$($led2d_layout)?],
led2d_font = [$($led2d_font)?],
}
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! __led_strip_parse_options {
(
name = $name:ident,
pin = $pin:ident,
len = $len:expr,
max_current = $max_current:expr,
engine = [$($engine:tt)*],
gamma = [$($gamma:expr)?],
max_frames = [$($max_frames:expr)?],
reset_us = [$($reset_us:expr)?],
) => {
$crate::__led_strip_dispatch_engine! {
$name,
$pin,
$len,
$max_current,
[$($engine)*],
[$($gamma)?],
[$($max_frames)?],
[$($reset_us)?],
}
};
(
name = $name:ident,
pin = $pin:ident,
len = $len:expr,
max_current = $max_current:expr,
engine = [],
gamma = [$($gamma:expr)?],
max_frames = [$($max_frames:expr)?],
reset_us = [$($reset_us:expr)?],
engine: Engine::Spi
$(, $($tail:tt)*)?
) => {
$crate::__led_strip_parse_options! {
name = $name,
pin = $pin,
len = $len,
max_current = $max_current,
engine = [Spi],
gamma = [$($gamma)?],
max_frames = [$($max_frames)?],
reset_us = [$($reset_us)?],
$($($tail)*)?
}
};
(
name = $name:ident,
pin = $pin:ident,
len = $len:expr,
max_current = $max_current:expr,
engine = [],
gamma = [$($gamma:expr)?],
max_frames = [$($max_frames:expr)?],
reset_us = [$($reset_us:expr)?],
engine: $crate::led_strip::Engine::Spi
$(, $($tail:tt)*)?
) => {
$crate::__led_strip_parse_options! {
name = $name,
pin = $pin,
len = $len,
max_current = $max_current,
engine = [Spi],
gamma = [$($gamma)?],
max_frames = [$($max_frames)?],
reset_us = [$($reset_us)?],
$($($tail)*)?
}
};
(
name = $name:ident,
pin = $pin:ident,
len = $len:expr,
max_current = $max_current:expr,
engine = [],
gamma = [$($gamma:expr)?],
max_frames = [$($max_frames:expr)?],
reset_us = [$($reset_us:expr)?],
engine: device_envoy_esp::led_strip::Engine::Spi
$(, $($tail:tt)*)?
) => {
$crate::__led_strip_parse_options! {
name = $name,
pin = $pin,
len = $len,
max_current = $max_current,
engine = [Spi],
gamma = [$($gamma)?],
max_frames = [$($max_frames)?],
reset_us = [$($reset_us)?],
$($($tail)*)?
}
};
(
name = $name:ident,
pin = $pin:ident,
len = $len:expr,
max_current = $max_current:expr,
engine = [],
gamma = [$($gamma:expr)?],
max_frames = [$($max_frames:expr)?],
reset_us = [$($reset_us:expr)?],
engine: Engine::Rmt
$(, $($tail:tt)*)?
) => {
$crate::__led_strip_parse_options! {
name = $name,
pin = $pin,
len = $len,
max_current = $max_current,
engine = [Rmt],
gamma = [$($gamma)?],
max_frames = [$($max_frames)?],
reset_us = [$($reset_us)?],
$($($tail)*)?
}
};
(
name = $name:ident,
pin = $pin:ident,
len = $len:expr,
max_current = $max_current:expr,
engine = [],
gamma = [$($gamma:expr)?],
max_frames = [$($max_frames:expr)?],
reset_us = [$($reset_us:expr)?],
engine: $crate::led_strip::Engine::Rmt
$(, $($tail:tt)*)?
) => {
$crate::__led_strip_parse_options! {
name = $name,
pin = $pin,
len = $len,
max_current = $max_current,
engine = [Rmt],
gamma = [$($gamma)?],
max_frames = [$($max_frames)?],
reset_us = [$($reset_us)?],
$($($tail)*)?
}
};
(
name = $name:ident,
pin = $pin:ident,
len = $len:expr,
max_current = $max_current:expr,
engine = [],
gamma = [$($gamma:expr)?],
max_frames = [$($max_frames:expr)?],
reset_us = [$($reset_us:expr)?],
engine: device_envoy_esp::led_strip::Engine::Rmt
$(, $($tail:tt)*)?
) => {
$crate::__led_strip_parse_options! {
name = $name,
pin = $pin,
len = $len,
max_current = $max_current,
engine = [Rmt],
gamma = [$($gamma)?],
max_frames = [$($max_frames)?],
reset_us = [$($reset_us)?],
$($($tail)*)?
}
};
(
name = $name:ident,
pin = $pin:ident,
len = $len:expr,
max_current = $max_current:expr,
engine = [$($engine:tt)+],
gamma = [$($gamma:expr)?],
max_frames = [$($max_frames:expr)?],
reset_us = [$($reset_us:expr)?],
engine: $ignored:path
$(, $($tail:tt)*)?
) => {
compile_error!("led_strip! duplicate `engine` field");
};
(
name = $name:ident,
pin = $pin:ident,
len = $len:expr,
max_current = $max_current:expr,
engine = [],
gamma = [$($gamma:expr)?],
max_frames = [$($max_frames:expr)?],
reset_us = [$($reset_us:expr)?],
engine: $ignored:path
$(, $($tail:tt)*)?
) => {
compile_error!("led_strip! engine must be Engine::Rmt or Engine::Spi");
};
(
name = $name:ident,
pin = $pin:ident,
len = $len:expr,
max_current = $max_current:expr,
engine = [$($engine:tt)*],
gamma = [],
max_frames = [$($max_frames:expr)?],
reset_us = [$($reset_us:expr)?],
gamma: $gamma:expr
$(, $($tail:tt)*)?
) => {
$crate::__led_strip_parse_options! {
name = $name,
pin = $pin,
len = $len,
max_current = $max_current,
engine = [$($engine)*],
gamma = [$gamma],
max_frames = [$($max_frames)?],
reset_us = [$($reset_us)?],
$($($tail)*)?
}
};
(
name = $name:ident,
pin = $pin:ident,
len = $len:expr,
max_current = $max_current:expr,
engine = [$($engine:tt)*],
gamma = [$already_gamma:expr],
max_frames = [$($max_frames:expr)?],
reset_us = [$($reset_us:expr)?],
gamma: $gamma:expr
$(, $($tail:tt)*)?
) => {
compile_error!("led_strip! duplicate `gamma` field");
};
(
name = $name:ident,
pin = $pin:ident,
len = $len:expr,
max_current = $max_current:expr,
engine = [$($engine:tt)*],
gamma = [$($gamma:expr)?],
max_frames = [],
reset_us = [$($reset_us:expr)?],
max_frames: $max_frames:expr
$(, $($tail:tt)*)?
) => {
$crate::__led_strip_parse_options! {
name = $name,
pin = $pin,
len = $len,
max_current = $max_current,
engine = [$($engine)*],
gamma = [$($gamma)?],
max_frames = [$max_frames],
reset_us = [$($reset_us)?],
$($($tail)*)?
}
};
(
name = $name:ident,
pin = $pin:ident,
len = $len:expr,
max_current = $max_current:expr,
engine = [$($engine:tt)*],
gamma = [$($gamma:expr)?],
max_frames = [$already_max_frames:expr],
reset_us = [$($reset_us:expr)?],
max_frames: $max_frames:expr
$(, $($tail:tt)*)?
) => {
compile_error!("led_strip! duplicate `max_frames` field");
};
(
name = $name:ident,
pin = $pin:ident,
len = $len:expr,
max_current = $max_current:expr,
engine = [$($engine:tt)*],
gamma = [$($gamma:expr)?],
max_frames = [$($max_frames:expr)?],
reset_us = [],
reset_us: $reset_us:expr
$(, $($tail:tt)*)?
) => {
$crate::__led_strip_parse_options! {
name = $name,
pin = $pin,
len = $len,
max_current = $max_current,
engine = [$($engine)*],
gamma = [$($gamma)?],
max_frames = [$($max_frames)?],
reset_us = [$reset_us],
$($($tail)*)?
}
};
(
name = $name:ident,
pin = $pin:ident,
len = $len:expr,
max_current = $max_current:expr,
engine = [$($engine:tt)*],
gamma = [$($gamma:expr)?],
max_frames = [$($max_frames:expr)?],
reset_us = [$already_reset_us:expr],
reset_us: $reset_us:expr
$(, $($tail:tt)*)?
) => {
compile_error!("led_strip! duplicate `reset_us` field");
};
(
name = $name:ident,
pin = $pin:ident,
len = $len:expr,
max_current = $max_current:expr,
engine = [$($engine:tt)*],
gamma = [$($gamma:expr)?],
max_frames = [$($max_frames:expr)?],
reset_us = [$($reset_us:expr)?],
$field:ident : $value:expr
$(, $($tail:tt)*)?
) => {
compile_error!("led_strip! unknown field; expected `engine`, `gamma`, `max_frames`, or `reset_us`");
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! __led_strip_dispatch_engine {
(
$name:ident,
$pin:ident,
$len:expr,
$max_current:expr,
[Spi],
[$($gamma:expr)?],
[$($max_frames:expr)?],
[$($reset_us:expr)?],
) => {
$crate::led_strip::spi::__led_strip_spi_inner!{
$name,
$pin,
$len,
$max_current,
[$($gamma)?],
[$($max_frames)?],
[$($reset_us)?],
[],
[],
}
};
(
$name:ident,
$pin:ident,
$len:expr,
$max_current:expr,
[Rmt],
[$($gamma:expr)?],
[$($max_frames:expr)?],
[$reset_us:expr],
) => {
compile_error!("led_strip! `reset_us` is only supported with `engine: Engine::Spi`");
};
(
$name:ident,
$pin:ident,
$len:expr,
$max_current:expr,
[Rmt],
[$($gamma:expr)?],
[$($max_frames:expr)?],
[],
) => {
$crate::__led_strip_dispatch_rmt_engine!{
$name,
$pin,
$len,
$max_current,
[$($gamma)?],
[$($max_frames)?],
}
};
(
$name:ident,
$pin:ident,
$len:expr,
$max_current:expr,
[],
[$($gamma:expr)?],
[$($max_frames:expr)?],
[$reset_us:expr],
) => {
compile_error!("led_strip! `reset_us` is only supported with `engine: Engine::Spi`");
};
(
$name:ident,
$pin:ident,
$len:expr,
$max_current:expr,
[],
[$($gamma:expr)?],
[$($max_frames:expr)?],
[],
) => {
$crate::__led_strip_dispatch_default_engine!{
$name,
$pin,
$len,
$max_current,
[$($gamma)?],
[$($max_frames)?],
}
};
}
#[doc(hidden)]
#[cfg(esp_has_rmt)]
#[macro_export]
macro_rules! __led_strip_dispatch_rmt_engine {
(
$name:ident,
$pin:ident,
$len:expr,
$max_current:expr,
[$($gamma:expr)?],
[$($max_frames:expr)?],
) => {
$crate::led_strip::__led_strip_inner!{
$name,
$pin,
$len,
$max_current,
[$($gamma)?],
[$($max_frames)?],
[],
[],
}
};
}
#[doc(hidden)]
#[cfg(not(esp_has_rmt))]
#[macro_export]
macro_rules! __led_strip_dispatch_rmt_engine {
(
$name:ident,
$pin:ident,
$len:expr,
$max_current:expr,
[$($gamma:expr)?],
[$($max_frames:expr)?],
) => {
compile_error!(
"led_strip! `engine: Engine::Rmt` requires an RMT-capable chip; use `engine: Engine::Spi`."
);
};
}
#[doc(hidden)]
#[cfg(esp_has_rmt)]
#[macro_export]
macro_rules! __led_strip_dispatch_default_engine {
(
$name:ident,
$pin:ident,
$len:expr,
$max_current:expr,
[$($gamma:expr)?],
[$($max_frames:expr)?],
) => {
$crate::__led_strip_dispatch_rmt_engine!{
$name,
$pin,
$len,
$max_current,
[$($gamma)?],
[$($max_frames)?],
}
};
}
#[doc(hidden)]
#[cfg(not(esp_has_rmt))]
#[macro_export]
macro_rules! __led_strip_dispatch_default_engine {
(
$name:ident,
$pin:ident,
$len:expr,
$max_current:expr,
[$($gamma:expr)?],
[$($max_frames:expr)?],
) => {
$crate::led_strip::spi::__led_strip_spi_inner!{
$name,
$pin,
$len,
$max_current,
[$($gamma)?],
[$($max_frames)?],
[],
[],
[],
}
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! __led_strip_first_or_default {
([$value:expr], $_default:expr) => {
$value
};
([], $default:expr) => {
$default
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! __led2d_strip_methods {
($_leds:expr, $max_frames:expr, [$led_layout:expr], [$font:expr]) => {
pub const FONT: $crate::led2d::Led2dFont = $font;
pub const WIDTH: usize = $led_layout.width();
pub const HEIGHT: usize = $led_layout.height();
pub const SIZE: $crate::led2d::Size =
$crate::led2d::Frame2d::<{ $led_layout.width() }, { $led_layout.height() }>::SIZE;
pub const TOP_LEFT: $crate::led2d::Point =
$crate::led2d::Frame2d::<{ $led_layout.width() }, { $led_layout.height() }>::TOP_LEFT;
pub const TOP_RIGHT: $crate::led2d::Point =
$crate::led2d::Frame2d::<{ $led_layout.width() }, { $led_layout.height() }>::TOP_RIGHT;
pub const BOTTOM_LEFT: $crate::led2d::Point = $crate::led2d::Frame2d::<
{ $led_layout.width() },
{ $led_layout.height() },
>::BOTTOM_LEFT;
pub const BOTTOM_RIGHT: $crate::led2d::Point = $crate::led2d::Frame2d::<
{ $led_layout.width() },
{ $led_layout.height() },
>::BOTTOM_RIGHT;
};
($_leds:expr, $_max_frames:expr, [], []) => {};
}
#[doc(hidden)]
#[macro_export]
macro_rules! __led2d_strip_trait_impl {
($name:ident, [$led_layout:expr], [$font:expr], $max_frames:expr) => {
impl $crate::led2d::Led2d<{ $led_layout.width() }, { $led_layout.height() }>
for &'static $name
{
const WIDTH: usize = $name::WIDTH;
const HEIGHT: usize = $name::HEIGHT;
const LEN: usize = $name::LEN;
const SIZE: $crate::led2d::Size = $name::SIZE;
const TOP_LEFT: $crate::led2d::Point = $name::TOP_LEFT;
const TOP_RIGHT: $crate::led2d::Point = $name::TOP_RIGHT;
const BOTTOM_LEFT: $crate::led2d::Point = $name::BOTTOM_LEFT;
const BOTTOM_RIGHT: $crate::led2d::Point = $name::BOTTOM_RIGHT;
const MAX_FRAMES: usize = $max_frames;
const MAX_BRIGHTNESS: u8 = $name::MAX_BRIGHTNESS;
const FONT: $crate::led2d::Led2dFont = $font;
fn write_frame(
&self,
frame2d: $crate::led2d::Frame2d<{ $led_layout.width() }, { $led_layout.height() }>,
) {
let led2d = $crate::led2d::Led2dEsp::new(*self, &$led_layout);
$crate::led2d::Led2dStripBacked::write_frame(&led2d, frame2d);
}
fn animate<I>(&self, frames: I)
where
I: IntoIterator,
I::Item: ::core::borrow::Borrow<(
$crate::led2d::Frame2d<{ $led_layout.width() }, { $led_layout.height() }>,
embassy_time::Duration,
)>,
{
let led2d = $crate::led2d::Led2dEsp::new(*self, &$led_layout);
$crate::led2d::Led2dStripBacked::animate(&led2d, frames);
}
}
};
($_name:ident, [], [], $_max_frames:expr) => {};
}
#[doc(hidden)]
#[macro_export]
macro_rules! __led_strip_impl {
(
name = $name:ident,
pin = $pin:ident,
len = $len:expr,
max_current = $max_current:expr,
gamma = $gamma:expr,
max_frames = $max_frames:expr,
led2d_layout = [$($led2d_layout:expr)?],
led2d_font = [$($led2d_font:expr)?],
) => {
::paste::paste! {
mod [<$name:snake _consts>] {
pub const LEDS: usize = $len;
pub const PULSES: usize = LEDS * 24 + 1;
pub const WORST_CASE_MA: u32 = LEDS as u32 * 60;
}
static [<$name:snake:upper _STATIC>]:
$crate::led_strip::LedStripStatic<
{ [<$name:snake _consts>]::LEDS },
{ $max_frames },
> = $crate::led_strip::LedStripEsp::new_static();
pub struct $name {
inner: $crate::led_strip::LedStripEsp<
{ [<$name:snake _consts>]::LEDS },
{ $max_frames },
>,
}
impl $name {
pub const LEN: usize = [<$name:snake _consts>]::LEDS;
pub const MAX_FRAMES: usize = $max_frames;
pub const MAX_BRIGHTNESS: u8 = <$crate::led_strip::Current>::max_brightness(
$max_current,
[<$name:snake _consts>]::WORST_CASE_MA,
);
pub const COMBO_TABLE: [u8; 256] =
$crate::led_strip::generate_combo_table($gamma, Self::MAX_BRIGHTNESS);
$crate::__led2d_strip_methods!(
{ [<$name:snake _consts>]::LEDS },
{ $max_frames },
[$($led2d_layout)?],
[$($led2d_font)?]
);
pub fn new(
pin: $crate::esp_hal::peripherals::$pin<'static>,
channel_creator: impl ::esp_hal::rmt::TxChannelCreator<
'static,
::esp_hal::Blocking,
>,
spawner: ::embassy_executor::Spawner,
) -> $crate::Result<&'static Self> {
use ::static_cell::StaticCell;
static INSTANCE: StaticCell<$name> = StaticCell::new();
static COMBO: StaticCell<[u8; 256]> = StaticCell::new();
let combo_ref: &'static [u8; 256] =
COMBO.init(<$name>::COMBO_TABLE);
let channel = channel_creator
.configure_tx(&$crate::init_and_start::rmt::ws2812_tx_config())
.map_err($crate::Error::RmtConfig)?
.with_pin(pin);
let driver =
$crate::led_strip::RmtWs2812::<
{ [<$name:snake _consts>]::LEDS },
{ [<$name:snake _consts>]::PULSES },
>::new(channel);
let strip_static: &'static _ = &[<$name:snake:upper _STATIC>];
spawner
.spawn([<$name:snake _device_task>](driver, strip_static, combo_ref).map_err($crate::Error::TaskSpawn)?);
let instance = INSTANCE.init($name {
inner: $crate::led_strip::LedStripEsp::new(strip_static),
});
Ok(instance)
}
}
impl $crate::led_strip::LedStrip<{ [<$name:snake _consts>]::LEDS }> for $name {
const MAX_FRAMES: usize = $max_frames;
const MAX_BRIGHTNESS: u8 = Self::MAX_BRIGHTNESS;
fn write_frame(
&self,
frame: $crate::led_strip::Frame1d<{ [<$name:snake _consts>]::LEDS }>,
) {
$crate::led_strip::__write_frame(self.inner.__command_signal(), frame);
}
fn animate<I>(&self, frames: I)
where
I: IntoIterator,
I::Item: ::core::borrow::Borrow<(
$crate::led_strip::Frame1d<{ [<$name:snake _consts>]::LEDS }>,
embassy_time::Duration,
)>,
{
$crate::led_strip::__animate(self.inner.__command_signal(), frames);
}
}
$crate::__led2d_strip_trait_impl!(
$name,
[$($led2d_layout)?],
[$($led2d_font)?],
$max_frames
);
#[::embassy_executor::task]
async fn [<$name:snake _device_task>](
driver: $crate::led_strip::RmtWs2812<
'static,
{ [<$name:snake _consts>]::LEDS },
{ [<$name:snake _consts>]::PULSES },
>,
strip_static: &'static $crate::led_strip::LedStripStatic<
{ [<$name:snake _consts>]::LEDS },
{ $max_frames },
>,
combo_table: &'static [u8; 256],
) {
$crate::led_strip::led_strip_device_loop(
driver,
strip_static.command_signal(),
combo_table,
)
.await;
}
}
};
}
#[cfg(target_os = "none")]
#[doc(hidden)]
pub mod spi;
pub use crate::{
__led_strip_dispatch_default_engine, __led_strip_dispatch_engine,
__led_strip_dispatch_rmt_engine, __led_strip_first_or_default, __led_strip_impl,
__led_strip_inner, __led_strip_parse_options, __led2d_strip_methods, __led2d_strip_trait_impl,
};