A step towards more efficient LED output.
Now on each frame, the driver has two separate steps:
- Encode the next frame into a buffer.
- Get all the colors for the next frame from the
Pattern. - Do any processing (e.g. convert color to linear sRGB, brightness, color corrections, etc)
- Format colors into words (bytes) that will be sent to the LEDs.
- Add any other words (bytes), depending on the LED type. (e.g. a start header, an end trailer, etc)
- Send the encoded frame buffer to the LEDs.
The purpose of separating these steps is to:
- Minimize the work required to send chunks of the encoded frame buffer to the LEDs, thus minimizing the timing gap in between these chunks.
- Open up the possibility of pre-encoding the next frame into a buffer, while sending the current frame to the LEDs.
defmtis now optional across all crates, defaults to enabled ingledopto.allocis now optional ingledoptocrate.ControlBuilder::with_layoutgeneric type signature changes from<Layout> to<Layout, const PIXEL_COUNT: usize>- If your layout is
Layout, then change.with_layout<Layout>()to.with_layout::<Layout, { Layout::PIXEL_COUNT }>()
- If your layout is
- .with_layout::<Layout>()
+ .with_layout::<Layout, { Layout::PIXEL_COUNT }>()ControlBuildernow has awith_frame_buffer_sizeto provide aFRAME_BUFFER_SIZEconstant.- So for example, to build a frame buffer to drive Ws2812 LEDs:
+ .with_frame_buffer_size::<{ Ws2812::frame_buffer_size(Layout::PIXEL_COUNT) }>()If using the gledopto high-level helper macros, this should be all you need to change.
Below are changes for lower-level interfaces:
- Change
Driver(andDriverAsync) traits:
pub trait Driver {
/// The error type that may be returned by the driver.
type Error;
/// The color type accepted by the driver.
type Color;
/// The word of the frame buffer.
type Word;
/// Encodes an update frame buffer for the LED hardware.
///
/// # Type Parameters
///
/// * `PIXEL_COUNT` - Number of pixels in frame
/// * `FRAME_BUFFER_SIZE` - Length of encoded frame buffer, in words.
/// * `Pixels` - Iterator of colors for each pixel
/// * `Color` - Type of each pixel
///
/// # Arguments
///
/// * `pixels` - Iterator of colors for each pixel
/// * `brightness` - Global brightness scaling factor (0.0 to 1.0)
/// * `correction` - Color correction factors
///
/// # Returns
///
/// Result with frame buffer
fn encode<const PIXEL_COUNT: usize, const FRAME_BUFFER_SIZE: usize, Pixels, Color>(
&mut self,
pixels: Pixels,
brightness: f32,
correction: ColorCorrection,
) -> Vec<Self::Word, FRAME_BUFFER_SIZE>
where
Pixels: IntoIterator<Item = Color>,
Self::Color: FromColor<Color>;
/// Writes frame buffer to the LED hardware.
///
/// # Type Parameters
///
/// * `FRAME_BUFFER_SIZE` - Length of encoded frame buffer, in words.
///
/// # Arguments
///
/// * `frame` - Frame buffer
///
/// # Returns
///
/// Result indicating success or an error
fn write<const FRAME_BUFFER_SIZE: usize>(
&mut self,
frame: Vec<Self::Word, FRAME_BUFFER_SIZE>,
brightness: f32,
correction: ColorCorrection,
) -> Result<(), Self::Error>;
/// Shows a frame on the LED hardware.
///
/// # Type Parameters
///
/// * `PIXEL_COUNT` - Number of pixels in frame
/// * `FRAME_BUFFER_SIZE` - Length of encoded frame buffer, in words.
/// * `Pixels` - Iterator of colors for each pixel
/// * `Color` - Type of each pixel
///
/// # Arguments
///
/// * `pixels` - Iterator of colors for each pixel
/// * `brightness` - Global brightness scaling factor (0.0 to 1.0)
/// * `correction` - Color correction factors
///
/// # Returns
///
/// Result indicating success or an error
fn show<const PIXEL_COUNT: usize, const FRAME_BUFFER_SIZE: usize, I, C>(
&mut self,
pixels: I,
brightness: f32,
correction: ColorCorrection,
) -> Result<(), Self::Error>
where
I: IntoIterator<Item = C>,
Self::Color: FromColor<C>,
{
let frame_buffer =
self.encode::<PIXEL_COUNT, FRAME_BUFFER_SIZE, _, _>(pixels, brightness, correction);
self.write(frame_buffer, brightness, correction)
}
}- Until the
generic_const_exprsfeature is stable, we aren't able to use associated constants, const functions, or expressions in the Blinksy code to calculate constants at compile-time. Instead, we must receive pre-calculated constants from the user as a generic. The best we can do is make it easy as possible by providing good types, traits, and const functions for the user to use. - Built-in LED drivers have been refactored:
- There is a generic driver for each type:
ClocklessDriverandClockedDriver. - You construct the generic driver by combining an Led with a Writer, both of that type.
- All drivers and all writers are made through the builder pattern.
- So for example: the clockless RMT driver for a WS2812 LED:
- There is a generic driver for each type:
let driver = ClocklessDriver::default()
.with_led::<Ws2812>()
.with_writer(ClocklessRmt::default()
.with_led::<Ws2812>()
.with_rmt_buffer_size::<{ rmt_buffer_size::<Ws2812>(Layout::PIXEL_COUNT) }>()
.with_channel(/* rmt channel */)
.with_pin(/* rmt pin */)
.build()
);- Another example: the clocked SPI driver for an APA102 LED:
let driver = ClockedDriver::default()
.with_led::<Apa102>()
.with_writer(/* spi bus */)- Clockless and clocked writers are also constructed through the builder pattern.
- Move LED definitions to
blinksy::ledsmodule, removeLedsuffix from structs. blinksy-espRMT driver expectsRMT_BUFFER_SIZE.- Each memory block on the RMT driver has a size of 64, with a max of 8 memory blocks.
- If async, you should use
64. - If blocking and not too many LEDs, you should use
rmt_buffer_size::<Led>(Layout::PIXEL_COUNT). - If blocking and too many LEDs, you should use
64.
- #96: Gledopto clockless led macros default to using buffered RMT
- #94: Upgrade to esp-hal@1.0.0-rc.1
- #93: Make more features optional
- #92: Document and enforce generic constants in ControlBuilder
- #90: Re-architect to pre-calculate a buffer for each frame
- #85: Pin RMT driver in RAM for examples
- #82: Use pixels buffer
- Write all colors from
Patterniterator to pixel buffer, then write pixel buffer to LEDs withDriver.
- Write all colors from
- #87: Refactor clocked LED drivers
Yee haw blinksy now supports async!
- You can now add the
asyncfeature and use async drivers.- For
gledopto, add theembassyfeature and use theembassy_mainentry. - See examples for async usage.
- For
- For projects using
gledoptomacros: no known breaking changes. - For projects using
blinksy-esp:create_rmt_buffer!macro no longer needs $num_leds. - For projects using
blinksyinternals, changes:- Renamed
dimensionsmodule tomarkersdimension::Dim1d->markers::Dim1ddimension::Dim2d->markers::Dim2d
- Move
dimension::LayoutForDimtolayout::LayoutForDim - Un-pub-ify internal functions within drivers
- Renamed
- #54: Add async drivers
blinksy-desktop::driver::Desktophas a new API:
fn main() {
Desktop::new_1d::<Layout>().start(|driver| {
let mut control = ControlBuilder::new_1d()
.with_layout::<Layout>()
.with_pattern::<Pattern>(PatternParams::default())
.with_driver(driver)
.build();
loop {
// ...
}
});
}- #72: Fix desktop simulator on macOS
rust-versionhas been increased to 1.88, to followesp-hal.- You should upgrade to
espflash@4, which will also require adding bootloader metadata via a macro.- See
gledoptolibrary and examples forbootloader!()on how to easily do this. - See
esp-hal-1.0.0-rc.0release for "Special migration note" about this.
- See
- #66: Upgrade to esp-hal-1.0.0-rc.0
No known breaking changes.
- #61: Fix points step of Shape::Line
- #64: Change rainbow pattern so is not just over x, but over all available dimensions
Woo hoo blinksy now supports 3D!
No known breaking changes.
- #56: Refactor layout code into separate files
Thanks @ahdinosaur for their contributions.
No known breaking changes.
Thanks @not-jan, @nazo6, and @ahdinosaur for their contributions.