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
+1
@@ -0,0 +1 @@
|
||||
{"files":{".cargo_vcs_info.json":"0e6b992e514296b611341a199f76d70f8796fd447a47de504601bd5a233dbe7d","Cargo.toml":"c40a61fe9acf33383c437cd9bcf4c92fe72b5ae35c0b51349df0c84d4bed828d","Cargo.toml.orig":"47cdc66d69a3ee0573ff08420bfab4c59055dd92cc0c1deca9331707f2adec9b","README.md":"2ba51541849f8935a3f65c14bccb1d6aec0b1dcf54ac1dd951eccdb434804611","src/lib.rs":"bc7661aa9017889a8e85363509d92de46730226faa5a30bd5c4f84dc20528422"},"package":"b94a4b8d74e7cc7baabcca5b2277b41877e039ad9cd49959d48ef94dac7eab4b"}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"git": {
|
||||
"sha1": "111dcda1032562503bfe2de87401683c680e505f"
|
||||
},
|
||||
"path_in_vcs": "esp-build"
|
||||
}
|
||||
Vendored
+36
@@ -0,0 +1,36 @@
|
||||
# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
|
||||
#
|
||||
# When uploading crates to the registry Cargo will automatically
|
||||
# "normalize" Cargo.toml files for maximal compatibility
|
||||
# with all versions of Cargo and also rewrite `path` dependencies
|
||||
# to registry (e.g., crates.io) dependencies.
|
||||
#
|
||||
# If you are reading this file be aware that the original Cargo.toml
|
||||
# will likely look very different (and much more reasonable).
|
||||
# See Cargo.toml.orig for the original contents.
|
||||
|
||||
[package]
|
||||
edition = "2021"
|
||||
rust-version = "1.60.0"
|
||||
name = "esp-build"
|
||||
version = "0.1.0"
|
||||
description = "Build utilities for esp-hal"
|
||||
readme = "README.md"
|
||||
license = "MIT OR Apache-2.0"
|
||||
repository = "https://github.com/esp-rs/esp-hal"
|
||||
|
||||
[lib]
|
||||
proc-macro = true
|
||||
|
||||
[dependencies.quote]
|
||||
version = "1.0.35"
|
||||
|
||||
[dependencies.syn]
|
||||
version = "2.0.52"
|
||||
features = [
|
||||
"fold",
|
||||
"full",
|
||||
]
|
||||
|
||||
[dependencies.termcolor]
|
||||
version = "1.4.1"
|
||||
Vendored
+33
@@ -0,0 +1,33 @@
|
||||
# esp-build
|
||||
|
||||
[](https://crates.io/crates/esp-build)
|
||||
[](https://docs.rs/esp-build)
|
||||

|
||||

|
||||
[](https://matrix.to/#/#esp-rs:matrix.org)
|
||||
|
||||
Build utilities for use with `esp-hal` and other related packages, intended for use in [build scripts]. This package is still quite minimal, but provides:
|
||||
|
||||
[build scripts]: https://doc.rust-lang.org/cargo/reference/build-scripts.html
|
||||
|
||||
## [Documentation](https://docs.rs/crate/esp-build)
|
||||
|
||||
## Minimum Supported Rust Version (MSRV)
|
||||
|
||||
This crate is guaranteed to compile on stable Rust 1.60 and up. It _might_
|
||||
compile with older versions but that may change in any new patch release.
|
||||
|
||||
## License
|
||||
|
||||
Licensed under either of:
|
||||
|
||||
- Apache License, Version 2.0 ([LICENSE-APACHE](../LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
|
||||
- MIT license ([LICENSE-MIT](../LICENSE-MIT) or http://opensource.org/licenses/MIT)
|
||||
|
||||
at your option.
|
||||
|
||||
### Contribution
|
||||
|
||||
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in
|
||||
the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without
|
||||
any additional terms or conditions.
|
||||
Vendored
+222
@@ -0,0 +1,222 @@
|
||||
//! Build utilities for esp-hal.
|
||||
|
||||
#![doc(html_logo_url = "https://avatars.githubusercontent.com/u/46717278")]
|
||||
|
||||
use std::{io::Write as _, process};
|
||||
|
||||
use proc_macro::TokenStream;
|
||||
use syn::{parse_macro_input, punctuated::Punctuated, LitStr, Token};
|
||||
use termcolor::{Color, ColorChoice, ColorSpec, StandardStream, WriteColor};
|
||||
|
||||
/// Print a build error and terminate the process.
|
||||
///
|
||||
/// It should be noted that the error will be printed BEFORE the main function
|
||||
/// is called, and as such this should NOT be thought analogous to `println!` or
|
||||
/// similar utilities.
|
||||
///
|
||||
/// ## Example
|
||||
///
|
||||
/// ```rust
|
||||
/// esp_build::error! {"
|
||||
/// ERROR: something really bad has happened!
|
||||
/// "}
|
||||
/// // Process exits with exit code 1
|
||||
/// ```
|
||||
#[proc_macro]
|
||||
pub fn error(input: TokenStream) -> TokenStream {
|
||||
do_alert(Color::Red, input);
|
||||
process::exit(1);
|
||||
}
|
||||
|
||||
/// Print a build warning.
|
||||
///
|
||||
/// It should be noted that the warning will be printed BEFORE the main function
|
||||
/// is called, and as such this should NOT be thought analogous to `println!` or
|
||||
/// similar utilities.
|
||||
///
|
||||
/// ## Example
|
||||
///
|
||||
/// ```rust
|
||||
/// esp_build::warning! {"
|
||||
/// WARNING: something unpleasant has happened!
|
||||
/// "};
|
||||
/// ```
|
||||
#[proc_macro]
|
||||
pub fn warning(input: TokenStream) -> TokenStream {
|
||||
do_alert(Color::Yellow, input)
|
||||
}
|
||||
|
||||
/// Given some features, assert that **at most** one of the features is enabled.
|
||||
///
|
||||
/// ## Example
|
||||
/// ```rust
|
||||
/// assert_unique_features!("foo", "bar", "baz");
|
||||
/// ```
|
||||
#[proc_macro]
|
||||
pub fn assert_unique_features(input: TokenStream) -> TokenStream {
|
||||
let features = parse_macro_input!(input with Punctuated<LitStr, Token![,]>::parse_terminated)
|
||||
.into_iter()
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let pairs = unique_pairs(&features);
|
||||
let unique_cfgs = pairs
|
||||
.iter()
|
||||
.map(|(a, b)| quote::quote! { all(feature = #a, feature = #b) });
|
||||
|
||||
let message = format!(
|
||||
r#"
|
||||
ERROR: expected exactly zero or one enabled feature from feature group:
|
||||
{:?}
|
||||
"#,
|
||||
features.iter().map(|lit| lit.value()).collect::<Vec<_>>(),
|
||||
);
|
||||
|
||||
quote::quote! {
|
||||
#[cfg(any(#(#unique_cfgs),*))]
|
||||
::esp_build::error! { #message }
|
||||
}
|
||||
.into()
|
||||
}
|
||||
|
||||
/// Given some features, assert that **at least** one of the features is
|
||||
/// enabled.
|
||||
///
|
||||
/// ## Example
|
||||
/// ```rust
|
||||
/// assert_used_features!("foo", "bar", "baz");
|
||||
/// ```
|
||||
#[proc_macro]
|
||||
pub fn assert_used_features(input: TokenStream) -> TokenStream {
|
||||
let features = parse_macro_input!(input with Punctuated<LitStr, Token![,]>::parse_terminated)
|
||||
.into_iter()
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let message = format!(
|
||||
r#"
|
||||
ERROR: expected at least one enabled feature from feature group:
|
||||
{:?}
|
||||
"#,
|
||||
features.iter().map(|lit| lit.value()).collect::<Vec<_>>()
|
||||
);
|
||||
|
||||
quote::quote! {
|
||||
#[cfg(not(any(#(feature = #features),*)))]
|
||||
::esp_build::error! { #message }
|
||||
}
|
||||
.into()
|
||||
}
|
||||
|
||||
/// Given some features, assert that **exactly** one of the features is enabled.
|
||||
///
|
||||
/// ## Example
|
||||
/// ```rust
|
||||
/// assert_unique_used_features!("foo", "bar", "baz");
|
||||
/// ```
|
||||
#[proc_macro]
|
||||
pub fn assert_unique_used_features(input: TokenStream) -> TokenStream {
|
||||
let features = parse_macro_input!(input with Punctuated<LitStr, Token![,]>::parse_terminated)
|
||||
.into_iter()
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let pairs = unique_pairs(&features);
|
||||
let unique_cfgs = pairs
|
||||
.iter()
|
||||
.map(|(a, b)| quote::quote! { all(feature = #a, feature = #b) });
|
||||
|
||||
let message = format!(
|
||||
r#"
|
||||
ERROR: expected exactly one enabled feature from feature group:
|
||||
{:?}
|
||||
"#,
|
||||
features.iter().map(|lit| lit.value()).collect::<Vec<_>>()
|
||||
);
|
||||
|
||||
quote::quote! {
|
||||
#[cfg(any(any(#(#unique_cfgs),*), not(any(#(feature = #features),*))))]
|
||||
::esp_build::error! { #message }
|
||||
}
|
||||
.into()
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Helper Functions
|
||||
|
||||
// Adapted from:
|
||||
// https://github.com/dtolnay/build-alert/blob/49d060e/src/lib.rs#L54-L93
|
||||
fn do_alert(color: Color, input: TokenStream) -> TokenStream {
|
||||
let message = parse_macro_input!(input as LitStr).value();
|
||||
|
||||
let ref mut stderr = StandardStream::stderr(ColorChoice::Auto);
|
||||
let color_spec = ColorSpec::new().set_fg(Some(color)).clone();
|
||||
|
||||
let mut has_nonspace = false;
|
||||
|
||||
for mut line in message.lines() {
|
||||
if !has_nonspace {
|
||||
let (maybe_heading, rest) = split_heading(line);
|
||||
|
||||
if let Some(heading) = maybe_heading {
|
||||
stderr.set_color(color_spec.clone().set_bold(true)).ok();
|
||||
write!(stderr, "\n{}", heading).ok();
|
||||
has_nonspace = true;
|
||||
}
|
||||
|
||||
line = rest;
|
||||
}
|
||||
|
||||
if line.is_empty() {
|
||||
writeln!(stderr).ok();
|
||||
} else {
|
||||
stderr.set_color(&color_spec).ok();
|
||||
writeln!(stderr, "{}", line).ok();
|
||||
|
||||
has_nonspace = has_nonspace || line.contains(|ch: char| ch != ' ');
|
||||
}
|
||||
}
|
||||
|
||||
stderr.reset().ok();
|
||||
writeln!(stderr).ok();
|
||||
|
||||
TokenStream::new()
|
||||
}
|
||||
|
||||
// Adapted from:
|
||||
// https://github.com/dtolnay/build-alert/blob/49d060e/src/lib.rs#L95-L114
|
||||
fn split_heading(s: &str) -> (Option<&str>, &str) {
|
||||
let mut end = 0;
|
||||
while end < s.len() && s[end..].starts_with(|ch: char| ch.is_ascii_uppercase()) {
|
||||
end += 1;
|
||||
}
|
||||
|
||||
if end >= 3 && (end == s.len() || s[end..].starts_with(':')) {
|
||||
let (heading, rest) = s.split_at(end);
|
||||
(Some(heading), rest)
|
||||
} else {
|
||||
(None, s)
|
||||
}
|
||||
}
|
||||
|
||||
fn unique_pairs(features: &Vec<LitStr>) -> Vec<(&LitStr, &LitStr)> {
|
||||
let mut pairs = Vec::new();
|
||||
|
||||
let mut i = 0;
|
||||
let mut j = 0;
|
||||
|
||||
while i < features.len() {
|
||||
let a = &features[i];
|
||||
let b = &features[j];
|
||||
|
||||
if a.value() != b.value() {
|
||||
pairs.push((a, b));
|
||||
}
|
||||
|
||||
j += 1;
|
||||
|
||||
if j >= features.len() {
|
||||
i += 1;
|
||||
j = i;
|
||||
}
|
||||
}
|
||||
|
||||
pairs
|
||||
}
|
||||
Reference in new issue
Block a user