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
+78
@@ -0,0 +1,78 @@
|
||||
use std::fmt::Write;
|
||||
|
||||
use crate::cargo;
|
||||
|
||||
pub(crate) fn mangled(defmt_tag: &str, data: &str) -> String {
|
||||
Symbol::new(defmt_tag, data).mangle()
|
||||
}
|
||||
|
||||
struct Symbol<'a> {
|
||||
/// Name of the Cargo package in which the symbol is being instantiated. Used for avoiding
|
||||
/// symbol name collisions.
|
||||
package: String,
|
||||
|
||||
/// Unique identifier that disambiguates otherwise equivalent invocations in the same crate.
|
||||
disambiguator: u64,
|
||||
|
||||
/// Symbol categorization. Known values:
|
||||
/// * `defmt_prim` for primitive formatting strings that are placed at the start of the `.defmt`
|
||||
/// section.
|
||||
/// * `defmt_fmt`, `defmt_str` for interned format strings and string literals.
|
||||
/// * `defmt_println` for logging messages that are always displayed.
|
||||
/// * `defmt_trace`, `defmt_debug`, `defmt_info`, `defmt_warn`, `defmt_error` for logging
|
||||
/// messages used at the different log levels.
|
||||
/// * `defmt_bitflags` indicates that a format string was generated by a `defmt::bitflags!`
|
||||
/// invocation, and that the decoder should look up possible flags in the binary.
|
||||
/// The data string is of the format `NAME@REPR#NUM`, where `NAME` is the name of the bitflags
|
||||
/// struct, `REPR` is the raw integer type representing the bitflags value (and also the
|
||||
/// wire format), and `NUM` is the number of defined bitflag values.
|
||||
/// * `defmt_bitflags_value` marks a `static` that holds the value of a bitflags `const`, its
|
||||
/// data field is `STRUCT_NAME::FLAG_NAME`.
|
||||
/// * Anything starting with `defmt_` is reserved for use by defmt, other prefixes are free for
|
||||
/// use by third-party apps (but they all should use a prefix!).
|
||||
tag: String,
|
||||
|
||||
/// Symbol data for use by the host tooling. Interpretation depends on `tag`.
|
||||
data: &'a str,
|
||||
|
||||
/// Crate name obtained via CARGO_CRATE_NAME (added since a Cargo package can contain many crates).
|
||||
crate_name: String,
|
||||
}
|
||||
|
||||
impl<'a> Symbol<'a> {
|
||||
fn new(tag: &'a str, data: &'a str) -> Self {
|
||||
Self {
|
||||
// `CARGO_PKG_NAME` is set to the invoking package's name.
|
||||
package: cargo::package_name(),
|
||||
disambiguator: super::crate_local_disambiguator(),
|
||||
tag: format!("defmt_{tag}"),
|
||||
data,
|
||||
crate_name: cargo::crate_name(),
|
||||
}
|
||||
}
|
||||
|
||||
fn mangle(&self) -> String {
|
||||
format!(
|
||||
r#"{{"package":"{}","tag":"{}","data":"{}","disambiguator":"{}","crate_name":"{}"}}"#,
|
||||
json_escape(&self.package),
|
||||
json_escape(&self.tag),
|
||||
json_escape(self.data),
|
||||
self.disambiguator,
|
||||
json_escape(&self.crate_name),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fn json_escape(string: &str) -> String {
|
||||
let mut escaped = String::new();
|
||||
for c in string.chars() {
|
||||
match c {
|
||||
'\\' => escaped.push_str("\\\\"),
|
||||
'\"' => escaped.push_str("\\\""),
|
||||
'\n' => escaped.push_str("\\n"),
|
||||
c if c.is_control() || c == '@' => write!(escaped, "\\u{:04x}", c as u32).unwrap(),
|
||||
c => escaped.push(c),
|
||||
}
|
||||
}
|
||||
escaped
|
||||
}
|
||||
Reference in new issue
Block a user