feat: add display UI with splash screen, dashboard, and RSSI sparkline

Implement full SSD1306 OLED display support:
- Splash screen on boot (1.5s) with project name, version, board
- Dashboard showing radio state, config, counters, RSSI/SNR
- RSSI history sparkline (64-sample ring buffer, 2px-wide bars)
- DisplayOn/DisplayOff host commands routed via dedicated channel
- Pure render functions against generic DrawTarget (no hardware coupling)
- Display on/off control bypasses radio task entirely

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
swaitsandClaude Opus 4.6 committed 2026-03-29 20:42:54 -06:00
1 parent 7064110293
commit 3102fc747a
17624 files changed
+4564928 -38

No files matched your search

+70
View File
@@ -0,0 +1,70 @@
//! Image drawable.
use crate::{
draw_target::DrawTarget,
geometry::{OriginDimensions, Point},
pixelcolor::PixelColor,
primitives::Rectangle,
};
/// Image drawable.
///
/// `ImageDrawable` is implemented for types that contains image information, which makes them
/// usable with the [`Image`] object.
///
/// The methods in this trait shouldn't be called directly by user code. Instead the object
/// that implements `ImageDrawable` should be wrapped in an [`Image`] object.
///
/// # Implementing `ImageDrawable`
///
/// All image drawables are positioned at the origin and need to implement [`OriginDimensions`], in
/// addition to this trait, to define their dimensions.
///
/// [`Image`]: https://docs.rs/embedded-graphics/latest/embedded_graphics/image/struct.Image.html
/// [`OriginDimensions`]: crate::geometry::OriginDimensions
pub trait ImageDrawable: OriginDimensions {
/// The color type.
type Color: PixelColor;
/// Draws the entire image to the target.
///
/// This method shouldn't be called directly by user code. Use an [`Image`] object instead.
///
/// # Implementation notes
///
/// The implementation of this method must draw the image inside the bounding box defined by
/// the [`OriginDimensions`] trait implementation. This means that the top left corner is at the
/// origin and no drawing operations outside the bounding box are allowed.
///
/// [`Image`]: https://docs.rs/embedded-graphics/latest/embedded_graphics/image/struct.Image.html
/// [`OriginDimensions`]: crate::geometry::OriginDimensions
fn draw<D>(&self, target: &mut D) -> Result<(), D::Error>
where
D: DrawTarget<Color = Self::Color>;
/// Draws a part of the image to the target.
///
/// This method shouldn't be called directly by user code. Use a [`SubImage`] object instead.
///
/// # Implementation notes
///
/// The implementation of this method must draw the image inside the given `area`.
/// It must be ensured that no drawing operation outside this [`Rectangle`] occur.
///
/// [`SubImage`]: https://docs.rs/embedded-graphics/latest/embedded_graphics/image/struct.SubImage.html
/// [`Rectangle`]: crate::primitives::rectangle::Rectangle
fn draw_sub_image<D>(&self, target: &mut D, area: &Rectangle) -> Result<(), D::Error>
where
D: DrawTarget<Color = Self::Color>;
}
/// Pixel getter.
pub trait GetPixel {
/// The color type.
type Color: PixelColor;
/// Gets the color of a pixel.
///
/// Returns `None` if `p` is outside the bounding box.
fn pixel(&self, p: Point) -> Option<Self::Color>;
}