#![allow(unknown_lints)]
#![allow(unexpected_cfgs)]
#[cfg(any(
feature = "rmt-legacy",
not(esp_idf_version_at_least_5_3_0),
not(esp_idf_soc_rmt_supported),
))]
fn main() -> anyhow::Result<()> {
println!("This example requires feature `rmt-legacy` disabled, using ESP-IDF >= v5.1.2, or is not supported on this MCU");
loop {
std::thread::sleep(std::time::Duration::from_millis(1000));
}
}
#[cfg(all(
esp_idf_soc_rmt_supported,
esp_idf_version_at_least_5_3_0,
not(feature = "rmt-legacy")
))]
mod example {
use esp_idf_hal::peripherals::Peripherals;
use esp_idf_hal::rmt::config::{MemoryAccess, TxChannelConfig};
use esp_idf_hal::rmt::encoder::simple_encoder::{
EncoderCallback, NotEnoughSpace, SimpleEncoder, SymbolBuffer,
};
use esp_idf_hal::rmt::TxChannelDriver;
use esp_idf_hal::rmt::{PinState, PulseTicks, Symbol};
use esp_idf_hal::units::Hertz;
use core::f64::consts::PI;
use core::time::Duration;
const RMT_LED_STRIP_RESOLUTION_HZ: Hertz = Hertz(10_000_000); const NUMBER_OF_LEDS: usize = 1;
const ANGLE_INC_FRAME: f64 = 0.02;
const ANGLE_INC_LED: f64 = 0.3;
const FRAME_DURATION: Duration = Duration::from_millis(20);
const BRIGHTNESS: f64 = 0.5;
const T0H: Duration = Duration::from_nanos(350); const T0L: Duration = Duration::from_nanos(800); const T1H: Duration = Duration::from_nanos(700); const T1L: Duration = Duration::from_nanos(600); const TRESET: Duration = Duration::from_micros(281);
#[derive(Debug, Clone, Copy, Default)]
#[repr(C)]
pub struct Color {
pub red: u8,
pub green: u8,
pub blue: u8,
}
impl Color {
#[must_use]
pub fn apply_brightness(self, brightness: f64) -> Self {
let factor = brightness.clamp(0.0, 1.0);
Self {
red: (self.red as f64 * factor).round() as u8,
green: (self.green as f64 * factor).round() as u8,
blue: (self.blue as f64 * factor).round() as u8,
}
}
}
fn byte_to_symbols(byte: u8) -> impl IntoIterator<Item = Symbol> {
let ws2812_zero = Symbol::new_with(
RMT_LED_STRIP_RESOLUTION_HZ,
PinState::High,
T0H,
PinState::Low,
T0L,
)
.unwrap();
let ws2812_one = Symbol::new_with(
RMT_LED_STRIP_RESOLUTION_HZ,
PinState::High,
T1H,
PinState::Low,
T1L,
)
.unwrap();
(0..8)
.map(|i| 0x80 >> i)
.map(move |bitmask| (byte & bitmask) != 0)
.map(move |is_one| if is_one { ws2812_one } else { ws2812_zero })
}
impl Color {
#[must_use]
pub fn to_vec(self) -> Vec<Symbol> {
let mut result = Vec::with_capacity(24);
for byte in [self.green, self.red, self.blue] {
result.extend(byte_to_symbols(byte));
}
result
}
}
pub struct LedEncoder {
input_position: usize,
}
impl LedEncoder {
pub fn new() -> Self {
Self { input_position: 0 }
}
}
impl EncoderCallback for LedEncoder {
type Item = Color;
fn encode(
&mut self,
input_data: &[Self::Item],
buffer: &mut SymbolBuffer<'_>,
) -> Result<(), NotEnoughSpace> {
let ws2812_reset = Symbol::new_half_split(
RMT_LED_STRIP_RESOLUTION_HZ,
PinState::Low,
PinState::Low,
TRESET,
)
.unwrap();
if buffer.position() == 0 {
self.input_position = 0;
}
for &next_color in &input_data[self.input_position..] {
let mut symbols = vec![ws2812_reset];
symbols.extend(next_color.apply_brightness(BRIGHTNESS).to_vec());
buffer.write_all(&symbols)?;
self.input_position += 1;
}
let max_duration = PulseTicks::max().duration(RMT_LED_STRIP_RESOLUTION_HZ) * 2;
let vec = Symbol::new_half_split(
RMT_LED_STRIP_RESOLUTION_HZ,
PinState::Low,
PinState::Low,
max_duration,
)
.unwrap()
.repeat_for(RMT_LED_STRIP_RESOLUTION_HZ, FRAME_DURATION)
.collect::<Vec<Symbol>>();
buffer.write_all(&vec)?;
Ok(())
}
}
pub fn run() -> anyhow::Result<()> {
println!("Create RMT TX channel");
let peripherals = Peripherals::take()?;
let mut channel = TxChannelDriver::new(
peripherals.pins.gpio21, &TxChannelConfig {
clock_source: Default::default(),
memory_access: MemoryAccess::Indirect {
memory_block_symbols: 64,
}, resolution: RMT_LED_STRIP_RESOLUTION_HZ,
transaction_queue_depth: 4, ..Default::default()
},
)?;
println!("Start LED rainbow chase");
let mut offset = 0.0;
let iterator = core::iter::from_fn(|| {
let mut signal = [Color::default(); NUMBER_OF_LEDS];
for (i, color) in signal.iter_mut().enumerate() {
let angle = offset + (i as f64 * ANGLE_INC_LED);
let color_offset = (PI * 2.0) / 3.0;
*color = Color {
red: ((angle + color_offset * 1.0).sin() * 127.0 + 128.0) as u8,
green: ((angle + color_offset * 0.0).sin() * 127.0 + 128.0) as u8,
blue: ((angle + color_offset * 2.0).sin() * 117.0 + 128.0) as u8,
};
}
offset += ANGLE_INC_FRAME;
if offset > 2.0 * PI {
offset -= 2.0 * PI;
}
Some(signal)
});
let mut encoders = [(); 10]
.map(|_| SimpleEncoder::with_config(LedEncoder::new(), &Default::default()).unwrap());
channel.send_iter(encoders.each_mut(), iterator, &Default::default())?;
Ok(())
}
}
#[cfg(all(
esp_idf_soc_rmt_supported,
esp_idf_version_at_least_5_3_0,
not(feature = "rmt-legacy")
))]
fn main() -> anyhow::Result<()> {
example::run()
}