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

+8
View File
@@ -0,0 +1,8 @@
extern crate trybuild;
#[cfg_attr(skip_ui_tests, ignore)]
#[test]
fn ui() {
let t = trybuild::TestCases::new();
t.compile_fail("tests/ui/*.rs");
}
+10
View File
@@ -0,0 +1,10 @@
extern crate test_crate;
use test_crate::*;
ok!(it_works);
#[test]
fn check_it_works() {
it_works();
}
+13
View File
@@ -0,0 +1,13 @@
use proc_macro_error::*;
#[test]
#[should_panic = "proc-macro-error API cannot be used outside of"]
fn missing_attr_emit() {
emit_call_site_error!("You won't see me");
}
#[test]
#[should_panic = "proc-macro-error API cannot be used outside of"]
fn missing_attr_abort() {
abort_call_site!("You won't see me");
}
+11
View File
@@ -0,0 +1,11 @@
extern crate test_crate;
use test_crate::*;
abort_from!(one, two);
abort_to_string!(one, two);
abort_format!(one, two);
direct_abort!(one, two);
abort_notes!(one, two);
abort_call_site_test!(one, two);
fn main() {}
+48
View File
@@ -0,0 +1,48 @@
error: abort!(span, from) test
--> $DIR/abort.rs:4:13
|
4 | abort_from!(one, two);
| ^^^
error: abort!(span, single_expr) test
--> $DIR/abort.rs:5:18
|
5 | abort_to_string!(one, two);
| ^^^
error: abort!(span, expr1, expr2) test
--> $DIR/abort.rs:6:15
|
6 | abort_format!(one, two);
| ^^^
error: Diagnostic::abort() test
--> $DIR/abort.rs:7:15
|
7 | direct_abort!(one, two);
| ^^^
error: This is an error
= note: simple note
= help: simple help
= help: simple hint
= note: simple yay
= note: format note
= note: Some note
= note: spanned simple note
= note: spanned format note
= note: Some note
--> $DIR/abort.rs:8:14
|
8 | abort_notes!(one, two);
| ^^^
error: abort_call_site! test
--> $DIR/abort.rs:9:1
|
9 | abort_call_site_test!(one, two);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
+13
View File
@@ -0,0 +1,13 @@
extern crate test_crate;
use test_crate::*;
enum NeedDefault {
A,
B
}
append_dummy!(need_default);
fn main() {
let _ = NeedDefault::default();
}
+5
View File
@@ -0,0 +1,5 @@
error: append_dummy test
--> $DIR/append_dummy.rs:9:15
|
9 | append_dummy!(need_default);
| ^^^^^^^^^^^^
+6
View File
@@ -0,0 +1,6 @@
extern crate test_crate;
use test_crate::*;
children_messages!(one, two, three, four);
fn main() {}
@@ -0,0 +1,23 @@
error: main macro message
--> $DIR/children_messages.rs:4:20
|
4 | children_messages!(one, two, three, four);
| ^^^
error: child message
--> $DIR/children_messages.rs:4:25
|
4 | children_messages!(one, two, three, four);
| ^^^
error: main syn::Error
--> $DIR/children_messages.rs:4:30
|
4 | children_messages!(one, two, three, four);
| ^^^^^
error: child syn::Error
--> $DIR/children_messages.rs:4:37
|
4 | children_messages!(one, two, three, four);
| ^^^^
+13
View File
@@ -0,0 +1,13 @@
extern crate test_crate;
use test_crate::*;
enum NeedDefault {
A,
B
}
dummy!(need_default);
fn main() {
let _ = NeedDefault::default();
}
+5
View File
@@ -0,0 +1,5 @@
error: set_dummy test
--> $DIR/dummy.rs:9:8
|
9 | dummy!(need_default);
| ^^^^^^^^^^^^
+7
View File
@@ -0,0 +1,7 @@
extern crate test_crate;
use test_crate::*;
emit!(one, two, three, four, five);
emit_notes!(one, two);
fn main() {}
+48
View File
@@ -0,0 +1,48 @@
error: emit!(span, from) test
--> $DIR/emit.rs:4:7
|
4 | emit!(one, two, three, four, five);
| ^^^
error: emit!(span, expr1, expr2) test
--> $DIR/emit.rs:4:12
|
4 | emit!(one, two, three, four, five);
| ^^^
error: emit!(span, single_expr) test
--> $DIR/emit.rs:4:17
|
4 | emit!(one, two, three, four, five);
| ^^^^^
error: Diagnostic::emit() test
--> $DIR/emit.rs:4:24
|
4 | emit!(one, two, three, four, five);
| ^^^^
error: emit_call_site_error!(expr) test
--> $DIR/emit.rs:4:1
|
4 | emit!(one, two, three, four, five);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
error: This is an error
= note: simple note
= help: simple help
= help: simple hint
= note: simple yay
= note: format note
= note: Some note
= note: spanned simple note
= note: spanned format note
= note: Some note
--> $DIR/emit.rs:5:13
|
5 | emit_notes!(one, two);
| ^^^
@@ -0,0 +1,6 @@
extern crate test_crate;
use test_crate::*;
explicit_span_range!(one, two, three, four);
fn main() {}
@@ -0,0 +1,5 @@
error: explicit SpanRange
--> $DIR/explicit_span_range.rs:4:22
|
4 | explicit_span_range!(one, two, three, four);
| ^^^^^^^^^^^^^^^
+11
View File
@@ -0,0 +1,11 @@
extern crate proc_macro_error;
use proc_macro_error::abort;
struct Foo;
#[allow(unused)]
fn foo() {
abort!(Foo, "BOOM");
}
fn main() {}
+13
View File
@@ -0,0 +1,13 @@
error[E0599]: no method named `FIRST_ARG_MUST_EITHER_BE_Span_OR_IMPLEMENT_ToTokens_OR_BE_SpanRange` found for reference `&Foo` in the current scope
--> $DIR/misuse.rs:8:5
|
4 | struct Foo;
| ----------- doesn't satisfy `Foo: quote::to_tokens::ToTokens`
...
8 | abort!(Foo, "BOOM");
| ^^^^^^^^^^^^^^^^^^^^ method not found in `&Foo`
|
= note: the method `FIRST_ARG_MUST_EITHER_BE_Span_OR_IMPLEMENT_ToTokens_OR_BE_SpanRange` exists but the following trait bounds were not satisfied:
`Foo: quote::to_tokens::ToTokens`
which is required by `&Foo: proc_macro_error::__export::ToTokensAsSpanRange`
= note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
+6
View File
@@ -0,0 +1,6 @@
extern crate test_crate;
#[test_crate::multiple_tokens]
type T = ();
fn main() {}
@@ -0,0 +1,5 @@
error: ...
--> $DIR/multiple_tokens.rs:4:1
|
4 | type T = ();
| ^^^^^^^^^^^^
+4
View File
@@ -0,0 +1,4 @@
use proc_macro_error::proc_macro_error;
#[proc_macro_error]
fn main() {}
+10
View File
@@ -0,0 +1,10 @@
error: #[proc_macro_error] attribute can be used only with procedural macros
= hint: if you are really sure that #[proc_macro_error] should be applied to this exact function, use #[proc_macro_error(allow_not_macro)]
--> $DIR/not_proc_macro.rs:3:1
|
3 | #[proc_macro_error]
| ^^^^^^^^^^^^^^^^^^^
|
= note: this error originates in an attribute macro (in Nightly builds, run with -Z macro-backtrace for more info)
+6
View File
@@ -0,0 +1,6 @@
extern crate test_crate;
use test_crate::*;
option_ext!(one, two);
fn main() {}
+7
View File
@@ -0,0 +1,7 @@
error: Option::expect_or_abort() test
--> $DIR/option_ext.rs:4:1
|
4 | option_ext!(one, two);
| ^^^^^^^^^^^^^^^^^^^^^^
|
= note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
+10
View File
@@ -0,0 +1,10 @@
// Adapted from https://github.com/dtolnay/proc-macro-hack/blob/master/example/src/main.rs
// Licensed under either of Apache License, Version 2.0 or MIT license at your option.
use proc_macro_hack_test::add_one;
fn main() {
let two = 2;
let nine = add_one!(two) + add_one!(2 + 3);
println!("nine = {}", nine);
}
+26
View File
@@ -0,0 +1,26 @@
error: BOOM
--> $DIR/proc_macro_hack.rs:8:25
|
8 | let nine = add_one!(two) + add_one!(2 + 3);
| ^^^
|
= note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
error: BOOM
--> $DIR/proc_macro_hack.rs:8:41
|
8 | let nine = add_one!(two) + add_one!(2 + 3);
| ^^^^^
|
= note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
warning: unreachable expression
--> $DIR/proc_macro_hack.rs:8:32
|
8 | let nine = add_one!(two) + add_one!(2 + 3);
| ------------- ^^^^^^^^^^^^^^^ unreachable expression
| |
| any code following this expression is unreachable
|
= note: `#[warn(unreachable_code)]` on by default
= note: this warning originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
+7
View File
@@ -0,0 +1,7 @@
extern crate test_crate;
use test_crate::*;
result_unwrap_or_abort!(one, two);
result_expect_or_abort!(one, two);
fn main() {}
+11
View File
@@ -0,0 +1,11 @@
error: Result::unwrap_or_abort() test
--> $DIR/result_ext.rs:4:25
|
4 | result_unwrap_or_abort!(one, two);
| ^^^
error: BOOM: Result::expect_or_abort() test
--> $DIR/result_ext.rs:5:25
|
5 | result_expect_or_abort!(one, two);
| ^^^
+6
View File
@@ -0,0 +1,6 @@
extern crate test_crate;
use test_crate::*;
to_tokens_span!(std::option::Option);
fn main() {}
+11
View File
@@ -0,0 +1,11 @@
error: whole type
--> $DIR/to_tokens_span.rs:4:17
|
4 | to_tokens_span!(std::option::Option);
| ^^^^^^^^^^^^^^^^^^^
error: explicit .span()
--> $DIR/to_tokens_span.rs:4:17
|
4 | to_tokens_span!(std::option::Option);
| ^^^
+4
View File
@@ -0,0 +1,4 @@
use proc_macro_error::proc_macro_error;
#[proc_macro_error(allow_not_macro, assert_unwind_safe, trololo)]
fn main() {}
@@ -0,0 +1,5 @@
error: unknown setting `trololo`, expected one of `assert_unwind_safe`, `allow_not_macro`, `proc_macro_hack`
--> $DIR/unknown_setting.rs:3:57
|
3 | #[proc_macro_error(allow_not_macro, assert_unwind_safe, trololo)]
| ^^^^^^^
+6
View File
@@ -0,0 +1,6 @@
extern crate test_crate;
use test_crate::*;
unrelated_panic!();
fn main() {}
@@ -0,0 +1,7 @@
error: proc macro panicked
--> $DIR/unrelated_panic.rs:4:1
|
4 | unrelated_panic!();
| ^^^^^^^^^^^^^^^^^^^
|
= help: message: unrelated panic test