esp-idf-hal/examples/rmt_neopixel.rs
Sergio Gasquez Arcos 061a234ba9
Update ledc examples names (#214)
* style: 🎨 Rename ledc examples to be consistent

* docs: 📝 Update rust-board name
2023-03-08 07:43:02 +02:00

130 lines
3.6 KiB
Rust

//! A simple example to change colours of a WS2812/NeoPixel compatible LED.
//!
//! This example demonstrates the use of [`FixedLengthSignal`][crate::rmt::FixedLengthSignal] which
//! lives on the stack and requires a known length before creating it.
//!
//! There is a similar implementation in the esp-idf project:
//! https://github.com/espressif/esp-idf/tree/20847eeb96/examples/peripherals/rmt/led_strip
//!
//! Datasheet (PDF) for a WS2812, which explains how the pulses are to be sent:
//! https://cdn-shop.adafruit.com/datasheets/WS2812.pdf
use anyhow::{bail, Result};
use core::time::Duration;
use esp_idf_hal::delay::FreeRtos;
use esp_idf_hal::peripherals::Peripherals;
use esp_idf_hal::rmt::config::TransmitConfig;
use esp_idf_hal::rmt::*;
fn main() -> Result<()> {
esp_idf_sys::link_patches();
let peripherals = Peripherals::take().unwrap();
// Onboard RGB LED pin
// ESP32-C3-DevKitC-02 gpio8, ESP32-C3-DevKit-RUST-1 gpio2
let led = peripherals.pins.gpio2;
let channel = peripherals.rmt.channel0;
let config = TransmitConfig::new().clock_divider(1);
let mut tx = TxRmtDriver::new(channel, led, &config)?;
// 3 seconds white at 10% brightness
neopixel(
RGB {
r: 25,
g: 25,
b: 25,
},
&mut tx,
)?;
FreeRtos::delay_ms(3000);
// rainbow loop at 20% brightness
let mut i: u32 = 0;
loop {
let rgb = hsv2rgb(i, 100, 20)?;
neopixel(rgb, &mut tx)?;
if i == 360 {
i = 0;
}
i += 1;
FreeRtos::delay_ms(10);
}
}
struct RGB {
r: u8,
g: u8,
b: u8,
}
fn ns(nanos: u64) -> Duration {
Duration::from_nanos(nanos)
}
fn neopixel(rgb: RGB, tx: &mut TxRmtDriver) -> Result<()> {
// e.g. rgb: (1,2,4)
// G R B
// 7 0 7 0 7 0
// 00000010 00000001 00000100
let color: u32 = ((rgb.g as u32) << 16) | ((rgb.r as u32) << 8) | rgb.b as u32;
let ticks_hz = tx.counter_clock()?;
let t0h = Pulse::new_with_duration(ticks_hz, PinState::High, &ns(350))?;
let t0l = Pulse::new_with_duration(ticks_hz, PinState::Low, &ns(800))?;
let t1h = Pulse::new_with_duration(ticks_hz, PinState::High, &ns(700))?;
let t1l = Pulse::new_with_duration(ticks_hz, PinState::Low, &ns(600))?;
let mut signal = FixedLengthSignal::<24>::new();
for i in (0..24).rev() {
let p = 2_u32.pow(i);
let bit = p & color != 0;
let (high_pulse, low_pulse) = if bit { (t1h, t1l) } else { (t0h, t0l) };
signal.set(23 - i as usize, &(high_pulse, low_pulse))?;
}
tx.start_blocking(&signal)?;
Ok(())
}
/// Converts hue, saturation, value to RGB
fn hsv2rgb(h: u32, s: u32, v: u32) -> Result<RGB> {
if h > 360 || s > 100 || v > 100 {
bail!("The given HSV values are not in valid range");
}
let s = s as f64 / 100.0;
let v = v as f64 / 100.0;
let c = s * v;
let x = c * (1.0 - (((h as f64 / 60.0) % 2.0) - 1.0).abs());
let m = v - c;
let (r, g, b);
if h < 60 {
r = c;
g = x;
b = 0.0;
} else if h >= 60 && h < 120 {
r = x;
g = c;
b = 0.0;
} else if h >= 120 && h < 180 {
r = 0.0;
g = c;
b = x;
} else if h >= 180 && h < 240 {
r = 0.0;
g = x;
b = c;
} else if h >= 240 && h < 300 {
r = x;
g = 0.0;
b = c;
} else {
r = c;
g = 0.0;
b = x;
}
Ok(RGB {
r: ((r + m) * 255.0) as u8,
g: ((g + m) * 255.0) as u8,
b: ((b + m) * 255.0) as u8,
})
}