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
+116
@@ -0,0 +1,116 @@
|
||||
mod device;
|
||||
/// Runs tests against a TestClass device running on actual hardware using libusb.
|
||||
///
|
||||
/// This is implemented as an example as opposed to a test because the Rust test runner system is
|
||||
/// not well suited for running tests that must depend on outside resources such as hardware and
|
||||
/// cannot be run in parallel.
|
||||
mod tests;
|
||||
|
||||
use crate::device::open_device;
|
||||
use crate::tests::{get_tests, TestFn};
|
||||
use std::io::prelude::*;
|
||||
use std::io::stdout;
|
||||
use std::panic;
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
use usb_device::device::CONFIGURATION_VALUE;
|
||||
|
||||
fn main() {
|
||||
let tests = get_tests();
|
||||
run_tests(&tests[..]);
|
||||
}
|
||||
|
||||
fn run_tests(tests: &[(&str, TestFn)]) {
|
||||
const INTERFACE: u8 = 0;
|
||||
|
||||
println!("test_class_host starting");
|
||||
println!("looking for device...");
|
||||
|
||||
let ctx = rusb::Context::new().expect("create libusb context");
|
||||
|
||||
// Look for the device for about 5 seconds in case it hasn't finished enumerating yet
|
||||
let mut dev = Err(rusb::Error::NoDevice);
|
||||
for _ in 0..50 {
|
||||
dev = open_device(&ctx);
|
||||
if dev.is_ok() {
|
||||
break;
|
||||
}
|
||||
|
||||
thread::sleep(Duration::from_millis(100));
|
||||
}
|
||||
|
||||
let mut dev = match dev {
|
||||
Ok(d) => d,
|
||||
Err(err) => {
|
||||
println!("Did not find a TestClass device. Make sure the device is correctly programmed and plugged in. Last error: {}", err);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
println!("\nrunning {} tests", tests.len());
|
||||
|
||||
let mut success = 0;
|
||||
for (name, test) in tests {
|
||||
if let Err(err) = dev.reset() {
|
||||
println!("Failed to reset the device: {}", err);
|
||||
return;
|
||||
}
|
||||
|
||||
if let Err(err) = dev.set_active_configuration(CONFIGURATION_VALUE) {
|
||||
println!("Failed to set active configuration: {}", err);
|
||||
return;
|
||||
}
|
||||
|
||||
if let Err(err) = dev.claim_interface(INTERFACE) {
|
||||
println!("Failed to claim interface: {}", err);
|
||||
return;
|
||||
}
|
||||
|
||||
print!("test {} ... ", name);
|
||||
let _ = stdout().flush();
|
||||
|
||||
let mut out = String::new();
|
||||
|
||||
let res = {
|
||||
let hook = panic::take_hook();
|
||||
panic::set_hook(Box::new(|_| {}));
|
||||
let res = panic::catch_unwind(panic::AssertUnwindSafe(|| {
|
||||
test(&mut dev, &mut out);
|
||||
}));
|
||||
panic::set_hook(hook);
|
||||
|
||||
res
|
||||
};
|
||||
|
||||
dev.release_interface(INTERFACE)
|
||||
.expect("failed to release interface");
|
||||
|
||||
if let Err(err) = res {
|
||||
let err = if let Some(err) = err.downcast_ref::<&'static str>() {
|
||||
String::from(*err)
|
||||
} else if let Some(err) = err.downcast_ref::<String>() {
|
||||
err.clone()
|
||||
} else {
|
||||
String::from("???")
|
||||
};
|
||||
|
||||
println!("FAILED\nerror: {}\n", err);
|
||||
} else {
|
||||
println!("ok");
|
||||
|
||||
if !out.is_empty() {
|
||||
print!("{}", out);
|
||||
}
|
||||
|
||||
success += 1;
|
||||
}
|
||||
}
|
||||
|
||||
println!("{} failed, {} succeeded", tests.len() - success, success);
|
||||
|
||||
if success == tests.len() {
|
||||
println!("\nALL TESTS PASSED!");
|
||||
} else {
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user