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

+54
View File
@@ -0,0 +1,54 @@
// SPDX-License-Identifier: Apache-2.0 OR MIT
#[allow(clippy::used_underscore_binding)]
#[inline(never)]
#[cfg_attr(not(test), panic_handler)]
fn _panic(_info: &core::panic::PanicInfo<'_>) -> ! {
#[cfg(feature = "stdio")]
eprintln!("{_info}");
#[cfg(feature = "panic-unwind")]
{
use crate::atomic::Ordering;
// TODO(panic-unwind): PANICKED is global, so if panics occur concurrently on thread 1 and
// thread 2 this could be mistakenly thought to be a double panic. However, I'm not sure if
// there is a way to handle that well without thread local.
if crate::experimental::panic::PANICKED.fetch_add(1, Ordering::AcqRel) != 0 {
#[cfg(feature = "stdio")]
eprintln!("panic during panic, aborting");
crate::process::abort()
}
}
#[cfg(feature = "backtrace")]
{
use core::{ffi::c_void, ptr};
use unwinding::abi::{_Unwind_Backtrace, _Unwind_GetIP, UnwindContext, UnwindReasonCode};
extern "C" fn callback(
unwind_ctx: &UnwindContext<'_>,
_arg: *mut c_void,
) -> UnwindReasonCode {
let ip = _Unwind_GetIP(unwind_ctx);
if ip == 0 {
UnwindReasonCode::NORMAL_STOP
} else {
eprintln!(" {ip:#x}");
UnwindReasonCode::NO_REASON
}
}
eprintln!("stack backtrace:");
_Unwind_Backtrace(callback, ptr::null_mut());
}
#[cfg(feature = "panic-unwind")]
{
let _code = unwinding::panic::begin_panic(alloc::boxed::Box::new(""));
#[cfg(feature = "stdio")]
eprintln!("failed to begin panic (unwind error code {})", _code.0);
}
crate::process::exit(101)
}