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:
17624 files changed
+4564928
-38
No files matched your search
+93
@@ -0,0 +1,93 @@
|
||||
bitflags! {
|
||||
#[derive(PartialEq)]
|
||||
struct Flags: u8 {
|
||||
const A = 1 << 0;
|
||||
const B = 1 << 1;
|
||||
const C = 1 << 2;
|
||||
const D = 1 << 3;
|
||||
}
|
||||
}
|
||||
|
||||
fn flag_to_string(flag: Flags) -> String {
|
||||
bitflags_match!(flag, {
|
||||
Flags::A => "A".to_string(),
|
||||
Flags::B => { "B".to_string() }
|
||||
Flags::C => "C".to_string(),
|
||||
Flags::D => "D".to_string(),
|
||||
Flags::A | Flags::B => "A or B".to_string(),
|
||||
Flags::A & Flags::B => { "A and B | empty".to_string() },
|
||||
Flags::A ^ Flags::B => "A xor B".to_string(),
|
||||
Flags::A | Flags::B | Flags::C => "A or B or C".to_string(),
|
||||
Flags::A & Flags::B & Flags::C => "A and B and C".to_string(),
|
||||
Flags::A ^ Flags::B ^ Flags::C => "A xor B xor C".to_string(),
|
||||
Flags::A | Flags::B | Flags::C | Flags::D => "All flags".to_string(),
|
||||
_ => "Unknown combination".to_string()
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_single_flags() {
|
||||
assert_eq!(flag_to_string(Flags::A), "A");
|
||||
assert_eq!(flag_to_string(Flags::B), "B");
|
||||
assert_eq!(flag_to_string(Flags::C), "C");
|
||||
assert_eq!(flag_to_string(Flags::D), "D");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_or_operations() {
|
||||
assert_eq!(flag_to_string(Flags::A | Flags::B), "A or B");
|
||||
assert_eq!(
|
||||
flag_to_string(Flags::A | Flags::B | Flags::C),
|
||||
"A or B or C"
|
||||
);
|
||||
assert_eq!(
|
||||
flag_to_string(Flags::A | Flags::B | Flags::C | Flags::D),
|
||||
"All flags"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_and_operations() {
|
||||
assert_eq!(flag_to_string(Flags::A & Flags::A), "A");
|
||||
assert_eq!(flag_to_string(Flags::A & Flags::B), "A and B | empty");
|
||||
assert_eq!(
|
||||
flag_to_string(Flags::A & Flags::B & Flags::C),
|
||||
"A and B | empty"
|
||||
); // Since A, B, and C are mutually exclusive, the result of A & B & C is 0 ==> A & B & C = 0000 (i.e., empty).
|
||||
// However, in the bitflags_match! statement (actually is if {..} else if {..} .. else {..}),
|
||||
// the "A & B = 0000" condition is listed first, so 0000 will match "A & B" first,
|
||||
// resulting in the output of the "A and B | empty" branch.
|
||||
assert_eq!(
|
||||
flag_to_string(Flags::A & Flags::B & Flags::C & Flags::D),
|
||||
"A and B | empty"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_xor_operations() {
|
||||
assert_eq!(flag_to_string(Flags::A ^ Flags::B), "A or B"); // A | B = A ^ B == 0011
|
||||
assert_eq!(flag_to_string(Flags::A ^ Flags::A), "A and B | empty");
|
||||
assert_eq!(
|
||||
flag_to_string(Flags::A ^ Flags::B ^ Flags::C),
|
||||
"A or B or C"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_complex_operations() {
|
||||
assert_eq!(flag_to_string(Flags::A | (Flags::B & Flags::C)), "A");
|
||||
assert_eq!(
|
||||
flag_to_string((Flags::A | Flags::B) & (Flags::B | Flags::C)),
|
||||
"B"
|
||||
);
|
||||
assert_eq!(
|
||||
flag_to_string(Flags::A ^ (Flags::B | Flags::C)),
|
||||
"A or B or C"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_empty_and_full_flags() {
|
||||
assert_eq!(flag_to_string(Flags::empty()), "A and B | empty");
|
||||
assert_eq!(flag_to_string(Flags::all()), "All flags");
|
||||
}
|
||||
Reference in new issue
Block a user