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

File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+127
View File
@@ -0,0 +1,127 @@
#[doc = "Register `DISABLEINDEBUG` reader"]
pub struct R(crate::R<DISABLEINDEBUG_SPEC>);
impl core::ops::Deref for R {
type Target = crate::R<DISABLEINDEBUG_SPEC>;
#[inline(always)]
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl From<crate::R<DISABLEINDEBUG_SPEC>> for R {
#[inline(always)]
fn from(reader: crate::R<DISABLEINDEBUG_SPEC>) -> Self {
R(reader)
}
}
#[doc = "Register `DISABLEINDEBUG` writer"]
pub struct W(crate::W<DISABLEINDEBUG_SPEC>);
impl core::ops::Deref for W {
type Target = crate::W<DISABLEINDEBUG_SPEC>;
#[inline(always)]
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl core::ops::DerefMut for W {
#[inline(always)]
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl From<crate::W<DISABLEINDEBUG_SPEC>> for W {
#[inline(always)]
fn from(writer: crate::W<DISABLEINDEBUG_SPEC>) -> Self {
W(writer)
}
}
#[doc = "Field `DISABLEINDEBUG` reader - Disable the protection mechanism for NVM regions while in debug interface mode. This register will only disable the protection mechanism if the device is in debug interface mode."]
pub type DISABLEINDEBUG_R = crate::BitReader<DISABLEINDEBUG_A>;
#[doc = "Disable the protection mechanism for NVM regions while in debug interface mode. This register will only disable the protection mechanism if the device is in debug interface mode.\n\nValue on reset: 1"]
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum DISABLEINDEBUG_A {
#[doc = "1: Disable in debug"]
DISABLED = 1,
#[doc = "0: Enable in debug"]
ENABLED = 0,
}
impl From<DISABLEINDEBUG_A> for bool {
#[inline(always)]
fn from(variant: DISABLEINDEBUG_A) -> Self {
variant as u8 != 0
}
}
impl DISABLEINDEBUG_R {
#[doc = "Get enumerated values variant"]
#[inline(always)]
pub fn variant(&self) -> DISABLEINDEBUG_A {
match self.bits {
true => DISABLEINDEBUG_A::DISABLED,
false => DISABLEINDEBUG_A::ENABLED,
}
}
#[doc = "Checks if the value of the field is `DISABLED`"]
#[inline(always)]
pub fn is_disabled(&self) -> bool {
*self == DISABLEINDEBUG_A::DISABLED
}
#[doc = "Checks if the value of the field is `ENABLED`"]
#[inline(always)]
pub fn is_enabled(&self) -> bool {
*self == DISABLEINDEBUG_A::ENABLED
}
}
#[doc = "Field `DISABLEINDEBUG` writer - Disable the protection mechanism for NVM regions while in debug interface mode. This register will only disable the protection mechanism if the device is in debug interface mode."]
pub type DISABLEINDEBUG_W<'a, const O: u8> =
crate::BitWriter<'a, u32, DISABLEINDEBUG_SPEC, DISABLEINDEBUG_A, O>;
impl<'a, const O: u8> DISABLEINDEBUG_W<'a, O> {
#[doc = "Disable in debug"]
#[inline(always)]
pub fn disabled(self) -> &'a mut W {
self.variant(DISABLEINDEBUG_A::DISABLED)
}
#[doc = "Enable in debug"]
#[inline(always)]
pub fn enabled(self) -> &'a mut W {
self.variant(DISABLEINDEBUG_A::ENABLED)
}
}
impl R {
#[doc = "Bit 0 - Disable the protection mechanism for NVM regions while in debug interface mode. This register will only disable the protection mechanism if the device is in debug interface mode."]
#[inline(always)]
pub fn disableindebug(&self) -> DISABLEINDEBUG_R {
DISABLEINDEBUG_R::new((self.bits & 1) != 0)
}
}
impl W {
#[doc = "Bit 0 - Disable the protection mechanism for NVM regions while in debug interface mode. This register will only disable the protection mechanism if the device is in debug interface mode."]
#[inline(always)]
pub fn disableindebug(&mut self) -> DISABLEINDEBUG_W<0> {
DISABLEINDEBUG_W::new(self)
}
#[doc = "Writes raw bits to the register."]
#[inline(always)]
pub unsafe fn bits(&mut self, bits: u32) -> &mut Self {
self.0.bits(bits);
self
}
}
#[doc = "Disable protection mechanism in debug interface mode\n\nThis register you can [`read`](crate::generic::Reg::read), [`write_with_zero`](crate::generic::Reg::write_with_zero), [`reset`](crate::generic::Reg::reset), [`write`](crate::generic::Reg::write), [`modify`](crate::generic::Reg::modify). See [API](https://docs.rs/svd2rust/#read--modify--write-api).\n\nFor information about available fields see [disableindebug](index.html) module"]
pub struct DISABLEINDEBUG_SPEC;
impl crate::RegisterSpec for DISABLEINDEBUG_SPEC {
type Ux = u32;
}
#[doc = "`read()` method returns [disableindebug::R](R) reader structure"]
impl crate::Readable for DISABLEINDEBUG_SPEC {
type Reader = R;
}
#[doc = "`write(|w| ..)` method takes [disableindebug::W](W) writer structure"]
impl crate::Writable for DISABLEINDEBUG_SPEC {
type Writer = W;
}
#[doc = "`reset()` method sets DISABLEINDEBUG to value 0x01"]
impl crate::Resettable for DISABLEINDEBUG_SPEC {
#[inline(always)]
fn reset_value() -> Self::Ux {
0x01
}
}
+64
View File
@@ -0,0 +1,64 @@
#[doc = "Register `UNUSED0` reader"]
pub struct R(crate::R<UNUSED0_SPEC>);
impl core::ops::Deref for R {
type Target = crate::R<UNUSED0_SPEC>;
#[inline(always)]
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl From<crate::R<UNUSED0_SPEC>> for R {
#[inline(always)]
fn from(reader: crate::R<UNUSED0_SPEC>) -> Self {
R(reader)
}
}
#[doc = "Register `UNUSED0` writer"]
pub struct W(crate::W<UNUSED0_SPEC>);
impl core::ops::Deref for W {
type Target = crate::W<UNUSED0_SPEC>;
#[inline(always)]
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl core::ops::DerefMut for W {
#[inline(always)]
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl From<crate::W<UNUSED0_SPEC>> for W {
#[inline(always)]
fn from(writer: crate::W<UNUSED0_SPEC>) -> Self {
W(writer)
}
}
impl W {
#[doc = "Writes raw bits to the register."]
#[inline(always)]
pub unsafe fn bits(&mut self, bits: u32) -> &mut Self {
self.0.bits(bits);
self
}
}
#[doc = "Unspecified\n\nThis register you can [`read`](crate::generic::Reg::read), [`write_with_zero`](crate::generic::Reg::write_with_zero), [`reset`](crate::generic::Reg::reset), [`write`](crate::generic::Reg::write), [`modify`](crate::generic::Reg::modify). See [API](https://docs.rs/svd2rust/#read--modify--write-api).\n\nFor information about available fields see [unused0](index.html) module"]
pub struct UNUSED0_SPEC;
impl crate::RegisterSpec for UNUSED0_SPEC {
type Ux = u32;
}
#[doc = "`read()` method returns [unused0::R](R) reader structure"]
impl crate::Readable for UNUSED0_SPEC {
type Reader = R;
}
#[doc = "`write(|w| ..)` method takes [unused0::W](W) writer structure"]
impl crate::Writable for UNUSED0_SPEC {
type Writer = W;
}
#[doc = "`reset()` method sets UNUSED0 to value 0"]
impl crate::Resettable for UNUSED0_SPEC {
#[inline(always)]
fn reset_value() -> Self::Ux {
0
}
}