feat: Layer 2 device library system with TMP36 reference driver
Add embedded device library registry with full lifecycle management, automated test integration, and pin assignment workflow. Library system: - Library registry embedded in binary via include_dir! (same as templates) - library.toml metadata format: name, version, bus type, pins, provided files - anvil add <name> extracts headers to lib/drivers/<name>/, test files to test/ - anvil remove <name> cleans up all files, config entries, and include paths - anvil lib lists installed libraries with status - anvil lib --available shows registry with student-friendly wiring summary - [libraries] section in .anvil.toml tracks installed libraries and versions - CMake auto-discovers lib/drivers/*/ for include paths at configure time TMP36 analog temperature sensor (reference implementation): - TempSensor abstract interface (readCelsius/readFahrenheit/readRaw) - Tmp36Analog: real hardware impl via Hal::analogRead with configurable Vref - Tmp36Mock: programmable values, read counting, no hardware needed - Tmp36Sim: deterministic noise via seeded LCG for repeatable system tests - test_tmp36.cpp: 21 Google Test cases covering mock, analog, sim, polymorphism - TMP36 formula: voltage_mV = raw * Vref_mV / 1024, temp_C = (mV - 500) / 10 Automated test integration: - Library test files (test_*.cpp) route to test/ during extraction - CMakeLists.txt auto-discovers test_*.cpp via GLOB, builds each as own target - anvil remove cleans up test files alongside driver headers - Zero manual CMake editing: add library, run test --clean, tests appear Pin assignment integration: - anvil add <name> --pin A0 does extract + pin assignment in one step - Without --pin, prints step-by-step wiring guidance with copy-paste commands - anvil pin --audit flags installed libraries with unassigned pins - Audit works even with zero existing pin assignments (fixed early-return bug) - LibraryMeta helpers: wiring_summary(), pin_roles(), default_mode() - Bus-aware guidance: analog pins, I2C bus registration, SPI with CS selection UX improvements: - anvil lib --available shows "Needs: 1 analog pin (e.g. A0)" not raw metadata - anvil add prints app code example, test code example, and next step - anvil pin --audit prints exact commands to resolve missing library pins - anvil remove shows test file deletion in output Files added: - libraries/tmp36/library.toml - libraries/tmp36/src/tmp36.h, tmp36_analog.h, tmp36_mock.h, tmp36_sim.h - libraries/tmp36/src/test_tmp36.cpp - src/library/mod.rs Files modified: - src/lib.rs, src/main.rs, src/commands/mod.rs - src/commands/lib.rs (add/remove/list/list_available with --pin support) - src/commands/pin.rs (audit library pin warnings, print_library_pin_warnings) - src/project/config.rs (libraries HashMap field) - templates/basic/test/CMakeLists.txt.tmpl (driver + test auto-discovery) Tests: 254 total (89 unit + 165 integration) - 12 library/mod.rs unit tests (registry, extraction, helpers) - 2 commands/lib.rs unit tests (class name derivation) - 30+ new integration tests covering library lifecycle, pin integration, audit flows, file routing, CMake discovery, config roundtrips, ASCII compliance, polymorphism contracts, and idempotent add/remove cycles
This commit is contained in:
523
src/commands/lib.rs
Normal file
523
src/commands/lib.rs
Normal file
@@ -0,0 +1,523 @@
|
||||
use anyhow::{Result, bail};
|
||||
use colored::*;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use crate::library;
|
||||
use crate::project::config::ProjectConfig;
|
||||
|
||||
/// Add a library to the current project.
|
||||
pub fn add_library(name: &str, pin: Option<&str>, project_dir: Option<&str>) -> Result<()> {
|
||||
let meta = library::find_library(name)
|
||||
.ok_or_else(|| {
|
||||
let available: Vec<String> = library::list_available()
|
||||
.iter()
|
||||
.map(|l| l.name.clone())
|
||||
.collect();
|
||||
anyhow::anyhow!(
|
||||
"Unknown library: '{}'\n Available: {}",
|
||||
name,
|
||||
if available.is_empty() {
|
||||
"(none)".to_string()
|
||||
} else {
|
||||
available.join(", ")
|
||||
}
|
||||
)
|
||||
})?;
|
||||
|
||||
let project_path = match project_dir {
|
||||
Some(dir) => PathBuf::from(dir),
|
||||
None => std::env::current_dir()?,
|
||||
};
|
||||
let project_root = ProjectConfig::find_project_root(&project_path)?;
|
||||
let mut config = ProjectConfig::load(&project_root)?;
|
||||
|
||||
// Check if already installed
|
||||
if config.libraries.contains_key(name) {
|
||||
println!(
|
||||
"{} {} is already installed (version {}).",
|
||||
"ok".green(),
|
||||
name.bright_white().bold(),
|
||||
config.libraries[name]
|
||||
);
|
||||
println!(
|
||||
" To reinstall, remove it first: {}",
|
||||
format!("anvil remove {}", name).bright_cyan()
|
||||
);
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// Extract files
|
||||
println!(
|
||||
"Adding {} {}",
|
||||
meta.name.bright_white().bold(),
|
||||
format!("({})", meta.description).bright_black()
|
||||
);
|
||||
|
||||
let written = library::extract_library(name, &project_root)?;
|
||||
for f in &written {
|
||||
println!(" {} {}", "+".bright_green(), f.bright_white());
|
||||
}
|
||||
|
||||
// Add to include_dirs if not present
|
||||
let driver_include = format!("lib/drivers/{}", name);
|
||||
if !config.build.include_dirs.contains(&driver_include) {
|
||||
config.build.include_dirs.push(driver_include);
|
||||
}
|
||||
|
||||
// Track in config
|
||||
config.libraries.insert(name.to_string(), meta.version.clone());
|
||||
config.save(&project_root)?;
|
||||
|
||||
// If --pin was given and this is a simple analog/digital library, assign it now
|
||||
if let Some(pin_str) = pin {
|
||||
if meta.bus == "i2c" || meta.bus == "spi" {
|
||||
println!();
|
||||
println!(
|
||||
" {} --pin is not used for {} libraries (bus pins are fixed).",
|
||||
"note:".bright_yellow(),
|
||||
meta.bus.bright_cyan()
|
||||
);
|
||||
if meta.bus == "i2c" {
|
||||
println!(
|
||||
" Assign the bus: {}",
|
||||
"anvil pin --assign i2c".bright_cyan()
|
||||
);
|
||||
} else {
|
||||
println!(
|
||||
" Assign the bus: {}",
|
||||
"anvil pin --assign spi --cs 10".bright_cyan()
|
||||
);
|
||||
}
|
||||
} else {
|
||||
// Assign each pin role to the given pin
|
||||
// For single-pin libraries this is straightforward
|
||||
// For multi-pin, the user would need to run multiple commands
|
||||
if meta.pins.len() == 1 {
|
||||
let assign_name = meta.pin_assignment_name(&meta.pins[0]);
|
||||
println!();
|
||||
println!("{}", "Assigning pin:".bright_yellow().bold());
|
||||
// Call pin assignment through the commands module
|
||||
match crate::commands::pin::assign_pin(
|
||||
&assign_name, pin_str,
|
||||
Some(meta.default_mode()),
|
||||
None, project_dir,
|
||||
) {
|
||||
Ok(()) => {}
|
||||
Err(e) => {
|
||||
println!(
|
||||
" {} Pin assignment failed: {}",
|
||||
"!!".red(),
|
||||
e
|
||||
);
|
||||
println!(
|
||||
" You can assign it manually: {}",
|
||||
format!(
|
||||
"anvil pin --assign {} {} --mode {}",
|
||||
assign_name, pin_str, meta.default_mode()
|
||||
).bright_cyan()
|
||||
);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
println!();
|
||||
println!(
|
||||
" {} This library needs {} pins. Assign them individually:",
|
||||
"note:".bright_yellow(),
|
||||
meta.pins.len()
|
||||
);
|
||||
for (_role, assign_name) in meta.pin_roles() {
|
||||
println!(
|
||||
" {}",
|
||||
format!(
|
||||
"anvil pin --assign {} <PIN> --mode {}",
|
||||
assign_name, meta.default_mode()
|
||||
).bright_cyan()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Print usage help
|
||||
println!();
|
||||
println!("{}", "In your app code:".bright_yellow().bold());
|
||||
println!(
|
||||
" #include \"{}\"",
|
||||
meta.interface.bright_white()
|
||||
);
|
||||
println!(
|
||||
" #include \"{}\"",
|
||||
meta.implementation.bright_white()
|
||||
);
|
||||
match meta.bus.as_str() {
|
||||
"analog" => {
|
||||
let pin_example = pin.unwrap_or("A0");
|
||||
println!(
|
||||
" {} sensor(&hal, {});",
|
||||
impl_class_name(&meta.implementation).bright_cyan(),
|
||||
pin_example
|
||||
);
|
||||
println!(" float temp = sensor.readCelsius();");
|
||||
}
|
||||
_ => {
|
||||
println!(
|
||||
" {} sensor(&hal);",
|
||||
impl_class_name(&meta.implementation).bright_cyan()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
println!();
|
||||
println!("{}", "In your tests:".bright_yellow().bold());
|
||||
println!(
|
||||
" #include \"{}\"",
|
||||
meta.mock.bright_white()
|
||||
);
|
||||
println!(
|
||||
" {} sensor;",
|
||||
mock_class_name(&meta.mock).bright_cyan()
|
||||
);
|
||||
println!(" sensor.setTemperature(25.0f);");
|
||||
println!(
|
||||
" // Your app sees 25 C -- no hardware needed"
|
||||
);
|
||||
|
||||
// Next steps
|
||||
println!();
|
||||
if pin.is_none() && meta.bus != "i2c" && meta.bus != "spi" {
|
||||
println!("{}", "Next step:".bright_yellow().bold());
|
||||
print_wiring_guide(&meta, None);
|
||||
} else if pin.is_none() && (meta.bus == "i2c" || meta.bus == "spi") {
|
||||
println!("{}", "Next step:".bright_yellow().bold());
|
||||
print_bus_guide(&meta);
|
||||
}
|
||||
|
||||
println!(
|
||||
"{} {} {} added. Run {} to rebuild tests.",
|
||||
"ok".green(),
|
||||
meta.name.bright_white().bold(),
|
||||
meta.version.bright_black(),
|
||||
"./test.sh --clean".bright_cyan()
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Print wiring guidance for analog/digital libraries.
|
||||
fn print_wiring_guide(meta: &library::LibraryMeta, board: Option<&str>) {
|
||||
let board_flag = board.map(|b| format!(" --board {}", b)).unwrap_or_default();
|
||||
match meta.bus.as_str() {
|
||||
"analog" => {
|
||||
println!(
|
||||
" Wire the {} sensor signal pin to an analog input (e.g. {}).",
|
||||
meta.name.bright_white(),
|
||||
"A0".bright_cyan()
|
||||
);
|
||||
println!(
|
||||
" Then tell Anvil which pin you chose:"
|
||||
);
|
||||
println!();
|
||||
for (_role, assign_name) in meta.pin_roles() {
|
||||
println!(
|
||||
" {}",
|
||||
format!(
|
||||
"anvil pin --assign {} A0 --mode analog{}",
|
||||
assign_name, board_flag
|
||||
).bright_cyan()
|
||||
);
|
||||
}
|
||||
println!();
|
||||
println!(
|
||||
" {}",
|
||||
"Pick any analog pin (A0-A5 on Uno). The pin you choose here"
|
||||
.bright_black()
|
||||
);
|
||||
println!(
|
||||
" {}",
|
||||
"must match the physical wire AND the pin in your code."
|
||||
.bright_black()
|
||||
);
|
||||
}
|
||||
"digital" => {
|
||||
println!(
|
||||
" Wire the {} sensor to a digital pin.",
|
||||
meta.name.bright_white()
|
||||
);
|
||||
println!(
|
||||
" Then tell Anvil which pin you chose:"
|
||||
);
|
||||
println!();
|
||||
for (_role, assign_name) in meta.pin_roles() {
|
||||
println!(
|
||||
" {}",
|
||||
format!(
|
||||
"anvil pin --assign {} <PIN> --mode input{}",
|
||||
assign_name, board_flag
|
||||
).bright_cyan()
|
||||
);
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
for (role, assign_name) in meta.pin_roles() {
|
||||
println!(
|
||||
" Assign {} pin: {}",
|
||||
role.bright_white(),
|
||||
format!(
|
||||
"anvil pin --assign {} <PIN>{}",
|
||||
assign_name, board_flag
|
||||
).bright_cyan()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
println!();
|
||||
}
|
||||
|
||||
/// Print wiring guidance for I2C/SPI bus libraries.
|
||||
fn print_bus_guide(meta: &library::LibraryMeta) {
|
||||
match meta.bus.as_str() {
|
||||
"i2c" => {
|
||||
println!(
|
||||
" Wire the {} sensor to the I2C bus (SDA + SCL pins).",
|
||||
meta.name.bright_white()
|
||||
);
|
||||
println!(
|
||||
" Then register the bus:"
|
||||
);
|
||||
println!();
|
||||
println!(
|
||||
" {}",
|
||||
"anvil pin --assign i2c".bright_cyan()
|
||||
);
|
||||
println!();
|
||||
println!(
|
||||
" {}",
|
||||
"SDA and SCL are fixed pins on your board (A4/A5 on Uno)."
|
||||
.bright_black()
|
||||
);
|
||||
println!(
|
||||
" {}",
|
||||
"Anvil knows which pins they are -- just register the bus."
|
||||
.bright_black()
|
||||
);
|
||||
}
|
||||
"spi" => {
|
||||
println!(
|
||||
" Wire the {} sensor to the SPI bus (MOSI/MISO/SCK) with a CS pin.",
|
||||
meta.name.bright_white()
|
||||
);
|
||||
println!(
|
||||
" Then register the bus with your chosen CS pin:"
|
||||
);
|
||||
println!();
|
||||
println!(
|
||||
" {}",
|
||||
"anvil pin --assign spi --cs 10".bright_cyan()
|
||||
);
|
||||
println!();
|
||||
println!(
|
||||
" {}",
|
||||
"MOSI/MISO/SCK are fixed on your board. You pick the CS pin."
|
||||
.bright_black()
|
||||
);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
println!();
|
||||
}
|
||||
|
||||
/// Remove a library from the current project.
|
||||
pub fn remove_library(name: &str, project_dir: Option<&str>) -> Result<()> {
|
||||
let project_path = match project_dir {
|
||||
Some(dir) => PathBuf::from(dir),
|
||||
None => std::env::current_dir()?,
|
||||
};
|
||||
let project_root = ProjectConfig::find_project_root(&project_path)?;
|
||||
let mut config = ProjectConfig::load(&project_root)?;
|
||||
|
||||
if !config.libraries.contains_key(name)
|
||||
&& !library::is_installed_on_disk(name, &project_root)
|
||||
{
|
||||
bail!(
|
||||
"Library '{}' is not installed.\n See installed: anvil lib\n See available: anvil lib --available",
|
||||
name
|
||||
);
|
||||
}
|
||||
|
||||
// Remove files
|
||||
let test_file = project_root.join("test").join(format!("test_{}.cpp", name));
|
||||
let had_test = test_file.exists();
|
||||
library::remove_library_files(name, &project_root)?;
|
||||
println!(
|
||||
" {} lib/drivers/{}/",
|
||||
"-".bright_red(),
|
||||
name.bright_white()
|
||||
);
|
||||
if had_test {
|
||||
println!(
|
||||
" {} test/test_{}.cpp",
|
||||
"-".bright_red(),
|
||||
name.bright_white()
|
||||
);
|
||||
}
|
||||
|
||||
// Remove from include_dirs
|
||||
let driver_include = format!("lib/drivers/{}", name);
|
||||
config.build.include_dirs.retain(|d| d != &driver_include);
|
||||
|
||||
// Remove from tracked libraries
|
||||
config.libraries.remove(name);
|
||||
config.save(&project_root)?;
|
||||
|
||||
println!();
|
||||
println!(
|
||||
"{} {} removed.",
|
||||
"ok".green(),
|
||||
name.bright_white().bold()
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// List installed libraries in the current project.
|
||||
pub fn list_libraries(project_dir: Option<&str>) -> Result<()> {
|
||||
let project_path = match project_dir {
|
||||
Some(dir) => PathBuf::from(dir),
|
||||
None => std::env::current_dir()?,
|
||||
};
|
||||
let project_root = ProjectConfig::find_project_root(&project_path)?;
|
||||
let config = ProjectConfig::load(&project_root)?;
|
||||
|
||||
if config.libraries.is_empty() {
|
||||
println!("No libraries installed.");
|
||||
println!();
|
||||
println!(
|
||||
" Browse available: {}",
|
||||
"anvil lib --available".bright_cyan()
|
||||
);
|
||||
println!(
|
||||
" Add one: {}",
|
||||
"anvil add tmp36".bright_cyan()
|
||||
);
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
println!("{}", "Installed libraries:".bright_yellow().bold());
|
||||
println!();
|
||||
|
||||
for (name, version) in &config.libraries {
|
||||
let on_disk = library::is_installed_on_disk(name, &project_root);
|
||||
let meta = library::find_library(name);
|
||||
|
||||
let desc = meta
|
||||
.as_ref()
|
||||
.map(|m| m.description.as_str())
|
||||
.unwrap_or("(unknown)");
|
||||
|
||||
if on_disk {
|
||||
println!(
|
||||
" {} {} {} {}",
|
||||
"ok".green(),
|
||||
name.bright_white().bold(),
|
||||
version.bright_black(),
|
||||
desc.bright_black()
|
||||
);
|
||||
} else {
|
||||
println!(
|
||||
" {} {} {} {}",
|
||||
"!!".red(),
|
||||
name.bright_white().bold(),
|
||||
version.bright_black(),
|
||||
"files missing -- run: anvil add ".red()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// List all available libraries in the Anvil registry.
|
||||
pub fn list_available() -> Result<()> {
|
||||
let libs = library::list_available();
|
||||
|
||||
if libs.is_empty() {
|
||||
println!("No libraries available.");
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
println!("{}", "Available libraries:".bright_yellow().bold());
|
||||
println!();
|
||||
|
||||
for lib in &libs {
|
||||
println!(
|
||||
" {} {} {}",
|
||||
lib.name.bright_white().bold(),
|
||||
lib.version.bright_black(),
|
||||
lib.description
|
||||
);
|
||||
println!(
|
||||
" Needs: {}",
|
||||
lib.wiring_summary().bright_cyan()
|
||||
);
|
||||
// Show inline pin example for simple cases
|
||||
match lib.bus.as_str() {
|
||||
"analog" if lib.pins.len() == 1 => {
|
||||
println!(
|
||||
" Add: {}",
|
||||
format!("anvil add {} --pin A0", lib.name).bright_cyan()
|
||||
);
|
||||
}
|
||||
_ => {
|
||||
println!(
|
||||
" Add: {}",
|
||||
format!("anvil add {}", lib.name).bright_cyan()
|
||||
);
|
||||
}
|
||||
}
|
||||
println!();
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Derive class name from implementation filename.
|
||||
/// "tmp36_analog.h" -> "Tmp36Analog"
|
||||
fn impl_class_name(filename: &str) -> String {
|
||||
let stem = filename.trim_end_matches(".h");
|
||||
stem.split('_')
|
||||
.map(|word| {
|
||||
let mut chars = word.chars();
|
||||
match chars.next() {
|
||||
Some(c) => {
|
||||
let mut s = c.to_uppercase().to_string();
|
||||
s.push_str(&chars.collect::<String>());
|
||||
s
|
||||
}
|
||||
None => String::new(),
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Derive mock class name from mock filename.
|
||||
/// "tmp36_mock.h" -> "Tmp36Mock"
|
||||
fn mock_class_name(filename: &str) -> String {
|
||||
impl_class_name(filename)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_impl_class_name() {
|
||||
assert_eq!(impl_class_name("tmp36_analog.h"), "Tmp36Analog");
|
||||
assert_eq!(impl_class_name("bmp280_i2c.h"), "Bmp280I2c");
|
||||
assert_eq!(impl_class_name("servo.h"), "Servo");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_mock_class_name() {
|
||||
assert_eq!(mock_class_name("tmp36_mock.h"), "Tmp36Mock");
|
||||
}
|
||||
}
|
||||
@@ -4,4 +4,5 @@ pub mod setup;
|
||||
pub mod devices;
|
||||
pub mod refresh;
|
||||
pub mod board;
|
||||
pub mod pin;
|
||||
pub mod pin;
|
||||
pub mod lib;
|
||||
@@ -7,6 +7,7 @@ use std::path::PathBuf;
|
||||
use crate::board::pinmap::{
|
||||
self, BoardPinMap, ALL_CAPABILITIES, ALL_MODES,
|
||||
};
|
||||
use crate::library;
|
||||
use crate::project::config::{
|
||||
ProjectConfig, BoardPinConfig, PinAssignment, BusConfig, CONFIG_FILENAME,
|
||||
};
|
||||
@@ -393,16 +394,22 @@ pub fn audit_pins(
|
||||
if pc.assignments.is_empty() && pc.buses.is_empty() {
|
||||
println!(" {}", "No pin assignments configured.".bright_black());
|
||||
println!();
|
||||
println!(" Get started:");
|
||||
println!(
|
||||
" {}",
|
||||
format!("anvil pin --assign led 13 --board {}", board).bright_black()
|
||||
);
|
||||
println!(
|
||||
" {}",
|
||||
format!("anvil pin --assign i2c --board {}", board).bright_black()
|
||||
);
|
||||
println!();
|
||||
|
||||
// Even with no manual assignments, libraries may need pins
|
||||
let has_library_warnings = print_library_pin_warnings(&config, pc);
|
||||
|
||||
if !has_library_warnings {
|
||||
println!(" Get started:");
|
||||
println!(
|
||||
" {}",
|
||||
format!("anvil pin --assign led 13 --board {}", board).bright_black()
|
||||
);
|
||||
println!(
|
||||
" {}",
|
||||
format!("anvil pin --assign i2c --board {}", board).bright_black()
|
||||
);
|
||||
println!();
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
@@ -512,6 +519,9 @@ pub fn audit_pins(
|
||||
}
|
||||
}
|
||||
|
||||
// Library pin check
|
||||
print_library_pin_warnings(&config, pc);
|
||||
|
||||
// Wiring checklist
|
||||
println!(" {}", "Wiring Checklist:".bold());
|
||||
let mut all_wiring: Vec<(u8, String, String)> = Vec::new();
|
||||
@@ -554,6 +564,126 @@ pub fn audit_pins(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Check installed libraries for unassigned pins and print warnings.
|
||||
/// Returns true if any warnings were printed.
|
||||
fn print_library_pin_warnings(config: &ProjectConfig, pc: &BoardPinConfig) -> bool {
|
||||
if config.libraries.is_empty() {
|
||||
return false;
|
||||
}
|
||||
|
||||
let assigned_names: Vec<String> = pc.assignments.keys().cloned().collect();
|
||||
let mut missing_any = false;
|
||||
|
||||
for (lib_name, _version) in &config.libraries {
|
||||
if let Some(meta) = library::find_library(lib_name) {
|
||||
// Bus-based libraries (i2c/spi) use bus assignments
|
||||
if meta.bus == "i2c" || meta.bus == "spi" {
|
||||
if !pc.buses.contains_key(&meta.bus) {
|
||||
if !missing_any {
|
||||
println!(" {}", "Library Wiring:".bright_yellow().bold());
|
||||
missing_any = true;
|
||||
}
|
||||
println!(
|
||||
" {} {} needs the {} bus -- not yet assigned",
|
||||
"!!".red().bold(),
|
||||
lib_name.bright_white(),
|
||||
meta.bus.bright_cyan()
|
||||
);
|
||||
match meta.bus.as_str() {
|
||||
"i2c" => {
|
||||
println!(
|
||||
" Wire SDA + SCL, then: {}",
|
||||
"anvil pin --assign i2c".bright_cyan()
|
||||
);
|
||||
println!(
|
||||
" {}",
|
||||
"(SDA/SCL are fixed pins on your board -- A4/A5 on Uno)"
|
||||
.bright_black()
|
||||
);
|
||||
}
|
||||
"spi" => {
|
||||
println!(
|
||||
" Wire MOSI/MISO/SCK + CS, then: {}",
|
||||
"anvil pin --assign spi --cs 10".bright_cyan()
|
||||
);
|
||||
println!(
|
||||
" {}",
|
||||
"(MOSI/MISO/SCK are fixed. You pick the CS pin.)"
|
||||
.bright_black()
|
||||
);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
let unassigned = library::unassigned_pins(&meta, &assigned_names);
|
||||
if !unassigned.is_empty() {
|
||||
if !missing_any {
|
||||
println!(" {}", "Library Wiring:".bright_yellow().bold());
|
||||
missing_any = true;
|
||||
}
|
||||
for assign_name in &unassigned {
|
||||
println!(
|
||||
" {} {} needs {} pin \"{}\" -- not yet assigned",
|
||||
"!!".red().bold(),
|
||||
lib_name.bright_white(),
|
||||
meta.bus.bright_cyan(),
|
||||
assign_name.bright_white()
|
||||
);
|
||||
match meta.bus.as_str() {
|
||||
"analog" => {
|
||||
println!(
|
||||
" Wire the sensor to an analog pin (e.g. A0), then:",
|
||||
);
|
||||
println!(
|
||||
" {}",
|
||||
format!(
|
||||
"anvil pin --assign {} A0 --mode analog",
|
||||
assign_name
|
||||
).bright_cyan()
|
||||
);
|
||||
println!(
|
||||
" {}",
|
||||
"Any analog pin works (A0-A5 on Uno). Match wire to code."
|
||||
.bright_black()
|
||||
);
|
||||
}
|
||||
"digital" => {
|
||||
println!(
|
||||
" Wire the sensor to a digital pin, then:",
|
||||
);
|
||||
println!(
|
||||
" {}",
|
||||
format!(
|
||||
"anvil pin --assign {} <PIN> --mode input",
|
||||
assign_name
|
||||
).bright_cyan()
|
||||
);
|
||||
}
|
||||
_ => {
|
||||
println!(
|
||||
" {}",
|
||||
format!(
|
||||
"anvil pin --assign {} <PIN>",
|
||||
assign_name
|
||||
).bright_cyan()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if missing_any {
|
||||
println!();
|
||||
}
|
||||
|
||||
missing_any
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// Init pins from another board
|
||||
// =========================================================================
|
||||
|
||||
Reference in New Issue
Block a user