Skip to content

led_strip

What an addressable LED strip promises, and code written against it.

A lib/ crate: no registers, no part numbers, no pins. It defines the contract that LED drivers keep, and the effects worth writing once rather than once per chip.

One fat trait forces all-or-nothing. A chip either implements everything — faking whatever it lacks — or it implements nothing and looks unsupported. Both are lies.

Split up, a driver states exactly what its hardware does:

Trait Meaning WS2812 APA102
[LedStrip] set a pixel, push the frame yes yes
[Dimmable] a global brightness the hardware applies no yes, 5 bits

WS2812 has no brightness register — dimming it means scaling the colour before sending, which costs colour depth. APA102 has one. If both implemented a single LedStrip with set_brightness on it, one of them would have to pretend, and a caller could not tell which.

Not implementing a trait is therefore a real statement, and make device-matrix prints who implements what so a gap is visible rather than merely absent.

struct Rgb {
pub r: u8,
pub g: u8,
pub b: u8,
}

A colour.

Lives here rather than in a driver because a colour is not a property of any part number — it was in ws2812, which meant using an APA102 would have meant depending on a WS2812 driver to name a colour.

Implements: Clone, Copy, Debug, Default, Eq, PartialEq, StructuralPartialEq

enum StripError {
OutOfRange,
Transport,
}

Something went wrong talking to the strip.

Implements: Clone, Copy, Debug, Eq, PartialEq, StructuralPartialEq

trait Dimmable {
fn set_brightness(&mut self, level: u8) -> Result<(), StripError>
}

A strip whose hardware applies a global brightness.

Separate from [LedStrip] because WS2812 has no such register. A chip that does not implement this is saying so, rather than silently scaling colours and losing depth — the caller can then choose [Rgb::dim] knowingly.

trait LedStrip {
fn len(&self) -> usize
fn is_empty(&self) -> bool
fn set(&mut self, index: usize, colour: Rgb) -> Result<(), StripError>
fn show(&mut self) -> Result<(), StripError>
fn fill(&mut self, colour: Rgb) -> Result<(), StripError>
fn clear(&mut self) -> Result<(), StripError>
}

The minimum an addressable strip must do.

Deliberately does not include brightness, colour order, or timing — those are either the chip’s business or a separate promise.

fn gradient<S>(strip: &mut S, start_hue: u8) -> Result<(), StripError>

Paint a hue gradient across the whole strip. Does not show.

Written once here rather than once per chip, which is the entire reason [LedStrip] exists.

References: StripError