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
Vendored
+55
@@ -0,0 +1,55 @@
|
||||
use std::fmt;
|
||||
|
||||
use crate::{ParseError, err::{perr, ParseErrorKind::*}};
|
||||
|
||||
|
||||
/// A bool literal: `true` or `false`. Also see [the reference][ref].
|
||||
///
|
||||
/// Notice that, strictly speaking, from Rust point of view "boolean literals" are not
|
||||
/// actual literals but [keywords].
|
||||
///
|
||||
/// [ref]: https://doc.rust-lang.org/reference/expressions/literal-expr.html#boolean-literal-expressions
|
||||
/// [keywords]: https://doc.rust-lang.org/reference/keywords.html#strict-keywords
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum BoolLit {
|
||||
False,
|
||||
True,
|
||||
}
|
||||
|
||||
impl BoolLit {
|
||||
/// Parses the input as a bool literal. Returns an error if the input is
|
||||
/// invalid or represents a different kind of literal.
|
||||
pub fn parse(s: &str) -> Result<Self, ParseError> {
|
||||
match s {
|
||||
"false" => Ok(Self::False),
|
||||
"true" => Ok(Self::True),
|
||||
_ => Err(perr(None, InvalidLiteral)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the actual Boolean value of this literal.
|
||||
pub fn value(self) -> bool {
|
||||
match self {
|
||||
Self::False => false,
|
||||
Self::True => true,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the literal as string.
|
||||
pub fn as_str(&self) -> &'static str {
|
||||
match self {
|
||||
Self::False => "false",
|
||||
Self::True => "true",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for BoolLit {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.pad(self.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
Vendored
+48
@@ -0,0 +1,48 @@
|
||||
use crate::{
|
||||
Literal, BoolLit,
|
||||
test_util::assert_parse_ok_eq,
|
||||
};
|
||||
|
||||
macro_rules! assert_bool_parse {
|
||||
($input:literal, $expected:expr) => {
|
||||
assert_parse_ok_eq(
|
||||
$input, Literal::parse($input), Literal::Bool($expected), "Literal::parse");
|
||||
assert_parse_ok_eq($input, BoolLit::parse($input), $expected, "BoolLit::parse");
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
#[test]
|
||||
fn parse_ok() {
|
||||
assert_bool_parse!("false", BoolLit::False);
|
||||
assert_bool_parse!("true", BoolLit::True);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_err() {
|
||||
assert!(Literal::parse("fa").is_err());
|
||||
assert!(Literal::parse("fal").is_err());
|
||||
assert!(Literal::parse("fals").is_err());
|
||||
assert!(Literal::parse(" false").is_err());
|
||||
assert!(Literal::parse("false ").is_err());
|
||||
assert!(Literal::parse("False").is_err());
|
||||
|
||||
assert!(Literal::parse("tr").is_err());
|
||||
assert!(Literal::parse("tru").is_err());
|
||||
assert!(Literal::parse(" true").is_err());
|
||||
assert!(Literal::parse("true ").is_err());
|
||||
assert!(Literal::parse("True").is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn value() {
|
||||
assert!(!BoolLit::False.value());
|
||||
assert!(BoolLit::True.value());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn as_str() {
|
||||
assert_eq!(BoolLit::False.as_str(), "false");
|
||||
assert_eq!(BoolLit::True.as_str(), "true");
|
||||
}
|
||||
Reference in new issue
Block a user