#![no_std]
#![no_main]
#![feature(iter_collect_into, iter_array_chunks, array_chunks, generic_const_exprs)]
#![allow(incomplete_features)]
use core::iter::once;
use esp_backtrace as _;
use esp_hal::{
clock::ClockControl,
delay::Delay,
gpio::{OutputPin, IO},
peripheral::Peripheral,
peripherals::Peripherals,
prelude::*,
rmt::{PulseCode, Rmt, TxChannel, TxChannelConfig, TxChannelCreator},
};
use log::{error, info};
const PULSES_PER_LED: usize = 24;
const CLOCK_MHZ: u32 = 80;
const T0H_NS: u32 = 350;
const T0L_NS: u32 = 800;
const T1H_NS: u32 = 700;
const T1L_NS: u32 = 600;
const T0: PulseCode = PulseCode {
level1: true,
length1: (T0H_NS * CLOCK_MHZ / 1000) as u16,
level2: false,
length2: (T0L_NS * CLOCK_MHZ / 1000) as u16,
};
const T1: PulseCode = PulseCode {
level1: true,
length1: (T1H_NS * CLOCK_MHZ / 1000) as u16,
level2: false,
length2: (T1L_NS * CLOCK_MHZ / 1000) as u16,
};
struct NeoPixelDriver<const LED_COUNT: usize, TX: TxChannel>
where
[(); LED_COUNT * PULSES_PER_LED]:,
{
channel: Option<TX>,
buffer: [u32; LED_COUNT * PULSES_PER_LED],
}
impl<'pin, const LED_COUNT: usize, TX: TxChannel> NeoPixelDriver<LED_COUNT, TX>
where
[(); LED_COUNT * PULSES_PER_LED]:,
{
pub fn new<C, O>(channel: C, pin: impl Peripheral<P = O> + 'pin) -> Self
where
O: OutputPin + 'pin,
C: TxChannelCreator<'pin, TX, O>,
{
let tx_config = TxChannelConfig {
clk_divider: 1,
idle_output_level: false,
idle_output: true,
..Default::default()
};
let channel = channel.configure(pin, tx_config).unwrap();
Self { channel: Some(channel), buffer: [0; LED_COUNT * PULSES_PER_LED] }
}
pub fn write<I>(&mut self, iterator: I) -> Result<(), esp_hal::rmt::Error>
where
I: IntoIterator<Item = Color>,
{
let mut channel = self.channel.take().unwrap();
let mut chunks = iterator.into_iter().array_chunks::<LED_COUNT>();
for chunk in chunks.by_ref() {
for (code, color) in
self.buffer.array_chunks_mut::<PULSES_PER_LED>().zip(chunk.into_iter())
{
color.write_pulses(code);
}
// info!("Sending chunk");
match channel.transmit(&self.buffer).wait() {
Ok(ch) => channel = ch,
Err((err, ch)) => {
self.channel = Some(ch);
error!("Error: {:?}", err);
return Err(err);
},
};
}
if let Some(color) = chunks.into_remainder() {
self.buffer.fill(PulseCode::default().into());
for (code, color) in
self.buffer.array_chunks_mut::<PULSES_PER_LED>().zip(color.into_iter())
{
color.write_pulses(code);
}
// info!("Sending remainder");
channel = channel.transmit(&self.buffer).wait().map_err(|error| error.0)?;
}
self.channel = Some(channel);
Ok(())
}
}
#[derive(Clone, Default, Debug)]
struct Color {
r: u8,
g: u8,
b: u8,
}
impl Color {
pub const fn rgb(r: u8, g: u8, b: u8) -> Self {
Self { r, g, b }
}
pub fn hsv(h: u32, s: u32, v: u32) -> Self {
if h > 360 || s > 100 || v > 100 {
error!("Invalid HSV values");
panic!("Invalid HSV values");
}
let s = s as f64 / 100.0;
let v = v as f64 / 100.0;
let c = s * v;
let mut x = ((h as f64 / 60.0) % 2.0) - 1.0;
if x < 0.0 {
x = -x
};
let x = c * (1.0 - x);
let m = v - c;
let (r, g, b) = match h {
0..=59 => (c, x, 0.0),
60..=119 => (x, c, 0.0),
120..=179 => (0.0, c, x),
180..=239 => (0.0, x, c),
240..=299 => (x, 0.0, c),
_ => (c, 0.0, x),
};
Self { r: ((r + m) * 255.0) as u8, g: ((g + m) * 255.0) as u8, b: ((b + m) * 255.0) as u8 }
}
pub fn write_pulses(&self, buf: &mut [u32; PULSES_PER_LED]) {
const POSITIONS: [u8; 8] = [128, 64, 32, 16, 8, 4, 2, 1];
let channels = [self.g, self.r, self.b];
for (idx_channel, channel) in channels.iter().enumerate() {
for (idx_position, position) in POSITIONS.iter().enumerate() {
buf[POSITIONS.len() * idx_channel + idx_position] =
(if channel & position == 0 { T0 } else { T1 }).into();
}
}
}
}
#[entry]
fn main() -> ! {
let peripherals = Peripherals::take();
let io = IO::new(peripherals.GPIO, peripherals.IO_MUX);
let system = peripherals.SYSTEM.split();
let clocks = ClockControl::max(system.clock_control).freeze();
let delay = Delay::new(&clocks);
let rmt = Rmt::new(peripherals.RMT, CLOCK_MHZ.MHz(), &clocks, None).unwrap();
esp_println::logger::init_logger_from_env();
info!("logger initialized");
const INIT_COLORS: [Color; 6] = [
Color::rgb(0, 32, 0),
Color::rgb(32, 32, 0),
Color::rgb(32, 0, 0),
Color::rgb(32, 0, 32),
Color::rgb(0, 0, 32),
Color::rgb(0, 32, 32),
];
info!("Starting");
let mut leds = NeoPixelDriver::<1, _>::new(rmt.channel0, io.pins.gpio8);
for color in INIT_COLORS.into_iter() {
leds.write(once(color)).unwrap();
delay.delay_nanos(100_000_000);
}
const MAX_LED: u32 = 16;
fn colors(hue: u32, length: u32) -> impl IntoIterator<Item = Color> {
(0..length)
.map(move |led| Color::hsv((hue + led * (360 / MAX_LED)) % 360, 100, 100))
.chain((length..MAX_LED).map(|_| Color::default()))
}
for hue in (0..=360).cycle().step_by(60) {
info!("Hue: {}", hue);
for length in 0..MAX_LED {
leds.write(colors(hue, length)).unwrap();
delay.delay_nanos(100_000_000);
}
for length in (0..MAX_LED).rev() {
leds.write(colors(hue, length)).unwrap();
delay.delay_nanos(100_000_000);
}
}
unreachable!();
}
fn map_int(value: u32, from_low: u32, from_high: u32, to_low: u32, to_high: u32) -> u32 {
let from_range = from_high - from_low;
let to_range = to_high - to_low;
to_low + (value - from_low) * to_range / from_range
}