Updated readme
This commit is contained in:
439
README.md
439
README.md
@@ -1,265 +1,356 @@
|
|||||||
# Anvil
|
# Anvil
|
||||||
|
|
||||||
**Arduino project generator and build tool -- forges clean embedded projects.**
|
**Forge clean, testable Arduino projects from a single command.**
|
||||||
|
|
||||||
A single binary that scaffolds self-contained Arduino projects with hardware
|
Anvil generates self-contained Arduino projects with hardware abstraction,
|
||||||
abstraction, Google Mock infrastructure, and a streamlined build/upload/monitor
|
test infrastructure, sensor libraries, and a complete build/upload/test
|
||||||
workflow. Works on Linux and Windows.
|
workflow. Once generated, the project stands alone -- Anvil is a scaffolding
|
||||||
|
tool, not a runtime dependency.
|
||||||
|
|
||||||
Generated projects are fully standalone -- they only need `arduino-cli` in
|
<p align="center">
|
||||||
PATH. The Anvil binary is a scaffolding and diagnostic tool, not a runtime
|
<img src="docs/terminal-demo.svg" alt="Anvil terminal demo" width="720"/>
|
||||||
dependency.
|
</p>
|
||||||
|
|
||||||
Anvil is a [Nexus Workshops](https://nxlearn.net) project.
|
Anvil is a [Nexus Workshops](https://nxlearn.net) project, built for FTC
|
||||||
|
robotics teams and embedded systems students.
|
||||||
|
|
||||||
## Install
|
---
|
||||||
|
|
||||||
Download the latest release binary for your platform:
|
## Getting Started
|
||||||
|
|
||||||
```bash
|
### Install
|
||||||
# Linux
|
|
||||||
chmod +x anvil
|
|
||||||
sudo mv anvil /usr/local/bin/
|
|
||||||
|
|
||||||
# Windows -- add anvil.exe to a directory in your PATH
|
|
||||||
```
|
|
||||||
|
|
||||||
|
Download the release binary for your platform and add it to your PATH.
|
||||||
Then run first-time setup:
|
Then run first-time setup:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
anvil setup
|
anvil setup
|
||||||
```
|
```
|
||||||
|
|
||||||
This checks for `arduino-cli`, installs the `arduino:avr` core, and verifies
|
This installs `arduino-cli` and the `arduino:avr` core. If something is
|
||||||
your system is ready. If something is missing, Anvil tells you exactly what
|
already installed, Anvil skips it. Run `anvil doctor` at any time to
|
||||||
to do.
|
check your environment.
|
||||||
|
|
||||||
## Your First Project
|
### Create a project
|
||||||
|
|
||||||
Create a project, plug in your board, and upload:
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
anvil new blink
|
anvil new blink
|
||||||
cd blink
|
cd blink
|
||||||
```
|
```
|
||||||
|
|
||||||
On **Linux/macOS**:
|
That's it. You have a complete project with build scripts, a HAL interface,
|
||||||
```bash
|
mock infrastructure, and a starter test file. Plug in your board and run:
|
||||||
./build.sh # compile only (verify)
|
|
||||||
./upload.sh # compile + upload to board
|
|
||||||
./upload.sh --monitor # compile, upload, open serial monitor
|
|
||||||
./monitor.sh # serial monitor (no compile)
|
|
||||||
./test/run_tests.sh # host-side unit tests (no board needed)
|
|
||||||
```
|
|
||||||
|
|
||||||
On **Windows**:
|
|
||||||
```bat
|
|
||||||
build REM compile only
|
|
||||||
upload REM compile + upload
|
|
||||||
upload --monitor REM compile, upload, open serial monitor
|
|
||||||
monitor REM serial monitor
|
|
||||||
test\run_tests REM host-side unit tests
|
|
||||||
```
|
|
||||||
|
|
||||||
Every script reads its settings from `.anvil.toml` in the project root.
|
|
||||||
You edit that file to change the board, baud rate, include paths, or
|
|
||||||
compiler flags. No Anvil binary required for any of this -- students clone
|
|
||||||
the repo, plug in a board, and run `upload`.
|
|
||||||
|
|
||||||
## Telling Anvil Which Board to Use
|
|
||||||
|
|
||||||
When you run `upload` without specifying a port, Anvil's scripts auto-detect
|
|
||||||
the board. They prefer USB serial ports over legacy motherboard COM ports,
|
|
||||||
which is usually the right thing. But if you have multiple boards plugged in,
|
|
||||||
or you want a consistent setup, you can pin a specific device.
|
|
||||||
|
|
||||||
### See what's connected
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
anvil devices
|
./build.sh # compile (verify it builds)
|
||||||
|
./upload.sh # compile + upload to board
|
||||||
|
./monitor.sh # serial monitor
|
||||||
|
./test.sh # host-side tests (no board needed)
|
||||||
```
|
```
|
||||||
|
|
||||||
This lists every serial port with its board name, protocol, and -- for USB
|
On Windows, use `build.bat`, `upload.bat`, `monitor.bat`, `test.bat`.
|
||||||
devices -- its VID:PID identifier. The VID:PID is a pair of hex codes that
|
Every script reads settings from `.anvil.toml` -- no Anvil binary required.
|
||||||
uniquely identifies the USB chip on your board (e.g. `0403:6001` for an FTDI,
|
|
||||||
`1a86:7523` for a CH340).
|
|
||||||
|
|
||||||
### Save a default device
|
---
|
||||||
|
|
||||||
|
## Templates
|
||||||
|
|
||||||
|
The default `basic` template gives you a blank canvas. For a richer starting
|
||||||
|
point, use a composed template:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
anvil new weather_station --template weather --board uno
|
||||||
|
```
|
||||||
|
|
||||||
|
The weather template builds on basic, adding a `WeatherApp` with a TMP36
|
||||||
|
temperature sensor driver, managed example tests demonstrating both mock and
|
||||||
|
simulator patterns, and student test starters. To see available templates:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
anvil new --list-templates
|
||||||
|
```
|
||||||
|
|
||||||
|
Templates are pure data -- each is a directory with a `template.toml`
|
||||||
|
declaring its base, required libraries, and per-board pin defaults. Adding
|
||||||
|
a new template requires zero Rust code changes.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Libraries
|
||||||
|
|
||||||
|
Anvil ships sensor and actuator libraries, each with four files: an abstract
|
||||||
|
interface, a hardware implementation, a test mock, and a deterministic
|
||||||
|
simulator.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
anvil add tmp36 --pin A0 # analog temperature sensor
|
||||||
|
anvil add button --pin 2 # digital pushbutton with debounce sim
|
||||||
|
```
|
||||||
|
|
||||||
|
See what's available and what's installed:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
anvil lib --available # all libraries in the registry
|
||||||
|
anvil lib # libraries installed in this project
|
||||||
|
```
|
||||||
|
|
||||||
|
Remove a library:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
anvil remove tmp36
|
||||||
|
```
|
||||||
|
|
||||||
|
Each library installs to `lib/drivers/<name>/` with its test file in `test/`.
|
||||||
|
The `CMakeLists.txt` auto-discovers driver directories, so adding a library
|
||||||
|
immediately makes it available to your tests.
|
||||||
|
|
||||||
|
### The mock/sim split
|
||||||
|
|
||||||
|
Every library provides two test doubles:
|
||||||
|
|
||||||
|
- **Mock** -- Returns exact values you set. Use in unit tests to verify your
|
||||||
|
application logic calls the sensor correctly and responds to specific values.
|
||||||
|
- **Simulator** -- Returns realistic values with configurable noise, bounce,
|
||||||
|
or drift. Use in system tests to verify your code handles real-world
|
||||||
|
sensor behavior (jitter, debounce timing, averaging).
|
||||||
|
|
||||||
|
This split teaches the difference between interaction testing and behavioral
|
||||||
|
testing -- a concept that transfers directly to professional software
|
||||||
|
development.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Pin Management
|
||||||
|
|
||||||
|
Anvil knows the pinout of every supported board. Assignments are validated
|
||||||
|
at the command line, not when you discover a wiring bug at 9 PM.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
anvil pin --assign led 13 --mode output
|
||||||
|
anvil pin --assign tmp36_data A0 --mode analog
|
||||||
|
anvil pin --assign spi --cs 10 # SPI bus with chip-select
|
||||||
|
anvil pin --assign i2c # I2C (pins auto-resolved)
|
||||||
|
```
|
||||||
|
|
||||||
|
Generate a `pins.h` header with `#define` constants:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
anvil pin --generate
|
||||||
|
```
|
||||||
|
|
||||||
|
Audit your wiring against library requirements:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
anvil pin --audit
|
||||||
|
```
|
||||||
|
|
||||||
|
Pin assignments are stored per-board in `.anvil.toml`, so switching between
|
||||||
|
an Uno and a Mega doesn't lose your wiring for either.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Board Profiles
|
||||||
|
|
||||||
|
A single project can target multiple boards:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
anvil board --add mega
|
||||||
|
anvil board --add nano --baud 57600
|
||||||
|
anvil board --default mega
|
||||||
|
```
|
||||||
|
|
||||||
|
Each board gets its own `[boards.<name>]` section in `.anvil.toml` with FQBN,
|
||||||
|
baud rate, and independent pin assignments. Scripts use the default board
|
||||||
|
unless you pass `--board`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./upload.sh --board nano
|
||||||
|
```
|
||||||
|
|
||||||
|
List available presets:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
anvil new --list-boards
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Device Detection
|
||||||
|
|
||||||
|
Anvil's scripts auto-detect your board, but you can pin a specific device:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
anvil devices # list connected boards
|
||||||
|
anvil devices --set # auto-detect and save to .anvil.local
|
||||||
anvil devices --set COM3 # save a specific port
|
anvil devices --set COM3 # save a specific port
|
||||||
anvil devices --set # auto-detect the best port and save it
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Both forms write a `.anvil.local` file in your project directory. This file
|
The `.anvil.local` file stores both the port name and the USB VID:PID. If
|
||||||
stores the port name *and* the VID:PID of the device on that port. It looks
|
your board moves to a different port (common on Windows), the scripts find
|
||||||
like this:
|
it by VID:PID automatically.
|
||||||
|
|
||||||
```toml
|
---
|
||||||
# Machine-specific Anvil config (not tracked by git)
|
|
||||||
port = "COM3"
|
|
||||||
vid_pid = "0403:6001"
|
|
||||||
```
|
|
||||||
|
|
||||||
The `.anvil.local` file is gitignored by default. Each machine that works on
|
## Refresh and .anvilignore
|
||||||
the project keeps its own copy with its own port assignment.
|
|
||||||
|
|
||||||
### Why VID:PID matters
|
When you upgrade Anvil, existing projects still have old infrastructure.
|
||||||
|
Refresh updates managed files without touching your code:
|
||||||
On Windows, if you unplug a USB device and plug it back in to a different
|
|
||||||
port, it often gets a new COM number (COM3 becomes COM5). The VID:PID
|
|
||||||
doesn't change -- it's baked into the USB chip. When the scripts see a
|
|
||||||
VID:PID in `.anvil.local`, they search for that device on whatever port
|
|
||||||
it's currently on. If it moved, you'll see:
|
|
||||||
|
|
||||||
```
|
|
||||||
info Device 0403:6001 found on COM5 (moved from COM3)
|
|
||||||
```
|
|
||||||
|
|
||||||
No manual intervention needed.
|
|
||||||
|
|
||||||
### Check what's saved
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
anvil devices --get
|
anvil refresh # dry run -- shows what would change
|
||||||
|
anvil refresh --force # update managed files
|
||||||
```
|
```
|
||||||
|
|
||||||
This shows the saved port and VID:PID for the current project, whether the
|
### What's protected
|
||||||
device is currently connected, and whether it has moved to a different port.
|
|
||||||
|
|
||||||
### Port resolution priority
|
An `.anvilignore` file (generated automatically) protects student-authored
|
||||||
|
files from refresh. The defaults protect:
|
||||||
|
|
||||||
When the scripts need a port, they check in this order:
|
- Your test files (`test/test_unit.cpp`, `test/test_system.cpp`)
|
||||||
|
- Your application code (`lib/app/*`)
|
||||||
|
- Your sketch (`*/*.ino`)
|
||||||
|
- Your config (`.anvil.toml`)
|
||||||
|
- Your project files (`.gitignore`, `README.md`, `.editorconfig`, etc.)
|
||||||
|
|
||||||
1. **`-p` flag** -- `upload.bat -p COM3` always wins
|
Managed infrastructure (build scripts, mock headers, CMakeLists.txt, library
|
||||||
2. **VID:PID** from `.anvil.local` -- finds the device regardless of port number
|
drivers, template example tests) gets updated. Missing files are always
|
||||||
3. **Saved port** from `.anvil.local` -- fallback if VID:PID lookup fails
|
recreated, even without `--force`.
|
||||||
4. **Auto-detect** -- prefers USB serial over legacy COM ports
|
|
||||||
|
|
||||||
### Specifying a project directory
|
### Fine-grained control
|
||||||
|
|
||||||
Both `--set` and `--get` default to the current directory, but you can point
|
|
||||||
them at another project:
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
anvil devices --set COM3 -d ../other-project
|
anvil refresh --ignore "test/my_helper.h" # protect a custom file
|
||||||
anvil devices --get -d ../other-project
|
anvil refresh --unignore "test/test_unit.cpp" # allow refresh to update it
|
||||||
|
anvil refresh --force --file test/test_unit.cpp # one-time override
|
||||||
```
|
```
|
||||||
|
|
||||||
## Updating Project Scripts
|
Patterns support globs: `test/*.cpp`, `lib/app/*`, `*.h`.
|
||||||
|
|
||||||
When you upgrade Anvil, your existing projects still have the old build
|
---
|
||||||
scripts. The `refresh` command updates them to the latest versions without
|
|
||||||
touching your source code, configuration, or test files:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
anvil refresh # check current project (dry run)
|
|
||||||
anvil refresh --force # overwrite differing scripts
|
|
||||||
anvil refresh ../other-project # check a different project
|
|
||||||
```
|
|
||||||
|
|
||||||
Refresh only replaces build infrastructure: `build.sh`, `build.bat`,
|
|
||||||
`upload.sh`, `upload.bat`, `monitor.sh`, `monitor.bat`, `_detect_port.ps1`,
|
|
||||||
and `test/run_tests.sh`, `test/run_tests.bat`. It never touches `.anvil.toml`,
|
|
||||||
`.anvil.local`, your `.ino` files, HAL headers, app code, mocks, or test
|
|
||||||
sources.
|
|
||||||
|
|
||||||
## What Anvil Does vs. What the Project Does
|
|
||||||
|
|
||||||
| Need Anvil for | Don't need Anvil for |
|
|
||||||
|---------------------------------|-------------------------------|
|
|
||||||
| `anvil new` (create project) | `./build.sh` (compile) |
|
|
||||||
| `anvil setup` (install core) | `./upload.sh` (flash) |
|
|
||||||
| `anvil doctor` (diagnose) | `./monitor.sh` (serial) |
|
|
||||||
| `anvil devices` (port scan) | `./test/run_tests.sh` (test) |
|
|
||||||
| `anvil refresh` (update scripts)| |
|
|
||||||
|
|
||||||
Once a project is created, Anvil is optional for daily work.
|
|
||||||
|
|
||||||
## Commands
|
|
||||||
|
|
||||||
| Command | Description |
|
|
||||||
|----------------------------------|------------------------------------------------|
|
|
||||||
| `anvil new NAME` | Create a new project with HAL and test scaffold |
|
|
||||||
| `anvil setup` | Install arduino-cli and AVR core |
|
|
||||||
| `anvil doctor` | Check system prerequisites |
|
|
||||||
| `anvil devices` | List connected boards and serial ports |
|
|
||||||
| `anvil devices --set [PORT]` | Save default port + VID:PID to .anvil.local |
|
|
||||||
| `anvil devices --get` | Show saved port for this project |
|
|
||||||
| `anvil refresh [DIR] [--force]` | Update project scripts to latest templates |
|
|
||||||
|
|
||||||
## Project Structure
|
## Project Structure
|
||||||
|
|
||||||
Every generated project follows the same layout:
|
|
||||||
|
|
||||||
```
|
```
|
||||||
your-project/
|
your-project/
|
||||||
your-project/your-project.ino Entry point (setup + loop)
|
your-project/your-project.ino Sketch (thin shell, no logic)
|
||||||
lib/
|
lib/
|
||||||
hal/
|
hal/
|
||||||
hal.h Hardware abstraction interface
|
hal.h Hardware abstraction (pure virtual)
|
||||||
hal_arduino.h Real hardware implementation
|
hal_arduino.h Real Arduino implementation
|
||||||
app/
|
app/
|
||||||
your-project_app.h Application logic (testable)
|
your-project_app.h Your application logic (testable)
|
||||||
|
drivers/
|
||||||
|
tmp36/ Sensor driver (interface/impl/mock/sim)
|
||||||
|
button/ Actuator driver (same pattern)
|
||||||
test/
|
test/
|
||||||
mocks/
|
mocks/
|
||||||
mock_hal.h Google Mock HAL
|
mock_arduino.h Arduino API shims for host compile
|
||||||
sim_hal.h Stateful simulator HAL
|
mock_hal.h Google Mock HAL
|
||||||
test_unit.cpp Unit tests
|
sim_hal.h Stateful simulator HAL
|
||||||
CMakeLists.txt Test build system
|
test_weather.cpp Managed example tests (refreshable)
|
||||||
run_tests.sh / .bat Test runner
|
test_unit.cpp Your unit tests (protected)
|
||||||
build.sh / build.bat Compile sketch
|
test_system.cpp Your system tests (protected)
|
||||||
upload.sh / upload.bat Compile + upload to board
|
test_tmp36.cpp Library driver tests
|
||||||
monitor.sh / monitor.bat Serial monitor
|
CMakeLists.txt Fetches Google Test, compiles tests
|
||||||
_detect_port.ps1 Port detection helper (Windows)
|
build.sh / build.bat Compile sketch
|
||||||
.anvil.toml Project config (tracked by git)
|
upload.sh / upload.bat Compile + upload to board
|
||||||
.anvil.local Machine-specific config (gitignored)
|
monitor.sh / monitor.bat Serial monitor
|
||||||
|
test.sh / test.bat Run host-side tests
|
||||||
|
.anvil.toml Project config (tracked by git)
|
||||||
|
.anvil.local Machine-specific port (gitignored)
|
||||||
|
.anvilignore File protection rules for refresh
|
||||||
```
|
```
|
||||||
|
|
||||||
All hardware access goes through the `Hal` interface in `lib/hal/hal.h`.
|
The key architectural rule: application code in `lib/app/` depends only on
|
||||||
Application code in `lib/app/` depends only on `Hal`, never on `Arduino.h`
|
the `Hal` interface, never on `Arduino.h`. The sketch creates the real HAL
|
||||||
directly. This means the app logic compiles and runs on the host for testing
|
and passes it in. Tests create a mock or simulator HAL instead. This is
|
||||||
with Google Mock -- no board required.
|
constructor injection -- the simplest form of dependency inversion.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Commands
|
||||||
|
|
||||||
|
| Command | Description |
|
||||||
|
|---|---|
|
||||||
|
| `anvil new NAME [--template T] [--board B]` | Create a new project |
|
||||||
|
| `anvil new --list-templates` | Show available templates |
|
||||||
|
| `anvil new --list-boards` | Show available board presets |
|
||||||
|
| `anvil setup` | Install arduino-cli and AVR core |
|
||||||
|
| `anvil doctor [--fix]` | Check system prerequisites |
|
||||||
|
| `anvil devices [--set] [--get] [--clear]` | Manage serial port assignment |
|
||||||
|
| `anvil add NAME [--pin P]` | Install a device library |
|
||||||
|
| `anvil remove NAME` | Remove a device library |
|
||||||
|
| `anvil lib [--available]` | List installed or available libraries |
|
||||||
|
| `anvil pin --assign NAME PIN [--mode M]` | Assign a pin |
|
||||||
|
| `anvil pin --generate` | Generate pins.h header |
|
||||||
|
| `anvil pin --audit [--brief]` | Check wiring against library requirements |
|
||||||
|
| `anvil pin --capabilities` | Show board pin capabilities |
|
||||||
|
| `anvil pin --init-from BOARD` | Copy pin assignments from another board |
|
||||||
|
| `anvil board --add NAME [--id FQBN] [--baud N]` | Add a board profile |
|
||||||
|
| `anvil board --remove NAME` | Remove a board profile |
|
||||||
|
| `anvil board --default NAME` | Set the default board |
|
||||||
|
| `anvil refresh [--force] [--file P] [--ignore P] [--unignore P]` | Update project infrastructure |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Configuration
|
## Configuration
|
||||||
|
|
||||||
### .anvil.toml (tracked by git)
|
### .anvil.toml (tracked by git)
|
||||||
|
|
||||||
Shared project settings. Edit this to change the board, baud rate, or
|
|
||||||
compiler flags:
|
|
||||||
|
|
||||||
```toml
|
```toml
|
||||||
[project]
|
[project]
|
||||||
name = "blink"
|
name = "weather_station"
|
||||||
anvil_version = "1.0.0"
|
anvil_version = "1.0.0"
|
||||||
|
template = "weather"
|
||||||
|
|
||||||
[build]
|
[build]
|
||||||
fqbn = "arduino:avr:uno"
|
default = "uno"
|
||||||
warnings = "more"
|
warnings = "more"
|
||||||
include_dirs = ["lib/hal", "lib/app"]
|
include_dirs = ["lib/hal", "lib/app", "lib/drivers/tmp36"]
|
||||||
extra_flags = ["-Werror"]
|
extra_flags = ["-Werror"]
|
||||||
|
|
||||||
[monitor]
|
[boards.uno]
|
||||||
|
fqbn = "arduino:avr:uno"
|
||||||
baud = 115200
|
baud = 115200
|
||||||
|
|
||||||
|
[boards.mega]
|
||||||
|
fqbn = "arduino:avr:mega:cpu=atmega2560"
|
||||||
|
baud = 115200
|
||||||
|
|
||||||
|
[libraries]
|
||||||
|
tmp36 = "0.1.0"
|
||||||
|
|
||||||
|
[pins.uno]
|
||||||
|
tmp36_data = { pin = 14, mode = "analog" }
|
||||||
```
|
```
|
||||||
|
|
||||||
### .anvil.local (not tracked by git)
|
### .anvil.local (gitignored)
|
||||||
|
|
||||||
Machine-specific settings, created by `anvil devices --set`:
|
|
||||||
|
|
||||||
```toml
|
```toml
|
||||||
port = "COM3"
|
port = "COM3"
|
||||||
vid_pid = "0403:6001"
|
vid_pid = "0403:6001"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Building from Source
|
## Building from Source
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cargo build --release
|
cargo build --release
|
||||||
```
|
```
|
||||||
|
|
||||||
The release binary is at `target/release/anvil` (Linux) or
|
Binary at `target/release/anvil` (Linux) or `target\release\anvil.exe`
|
||||||
`target\release\anvil.exe` (Windows).
|
(Windows). Requires Rust 2021 edition.
|
||||||
|
|
||||||
|
The test suite:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cargo test
|
||||||
|
```
|
||||||
|
|
||||||
|
615 tests (137 unit + 478 integration), ~4 seconds, zero warnings.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
|
|||||||
90
docs/terminal-demo.svg
Normal file
90
docs/terminal-demo.svg
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="720" height="440" viewBox="0 0 720 440">
|
||||||
|
<style>
|
||||||
|
@keyframes t1 { 0%, 4% { opacity: 0 } 5% { opacity: 1 } }
|
||||||
|
@keyframes t2 { 0%, 9% { opacity: 0 } 10% { opacity: 1 } }
|
||||||
|
@keyframes t3 { 0%, 11% { opacity: 0 } 12% { opacity: 1 } }
|
||||||
|
@keyframes t4 { 0%, 13% { opacity: 0 } 14% { opacity: 1 } }
|
||||||
|
@keyframes t5 { 0%, 15% { opacity: 0 } 16% { opacity: 1 } }
|
||||||
|
@keyframes t6 { 0%, 17% { opacity: 0 } 18% { opacity: 1 } }
|
||||||
|
@keyframes t7 { 0%, 19% { opacity: 0 } 20% { opacity: 1 } }
|
||||||
|
@keyframes t8 { 0%, 21% { opacity: 0 } 22% { opacity: 1 } }
|
||||||
|
@keyframes t9 { 0%, 23% { opacity: 0 } 24% { opacity: 1 } }
|
||||||
|
@keyframes t10 { 0%, 25% { opacity: 0 } 26% { opacity: 1 } }
|
||||||
|
@keyframes t11 { 0%, 27% { opacity: 0 } 28% { opacity: 1 } }
|
||||||
|
@keyframes t12 { 0%, 29% { opacity: 0 } 30% { opacity: 1 } }
|
||||||
|
@keyframes t13 { 0%, 34% { opacity: 0 } 35% { opacity: 1 } }
|
||||||
|
@keyframes t14 { 0%, 39% { opacity: 0 } 40% { opacity: 1 } }
|
||||||
|
@keyframes t15 { 0%, 44% { opacity: 0 } 45% { opacity: 1 } }
|
||||||
|
@keyframes t16 { 0%, 49% { opacity: 0 } 50% { opacity: 1 } }
|
||||||
|
@keyframes t17 { 0%, 51% { opacity: 0 } 52% { opacity: 1 } }
|
||||||
|
@keyframes t18 { 0%, 53% { opacity: 0 } 54% { opacity: 1 } }
|
||||||
|
@keyframes t19 { 0%, 58% { opacity: 0 } 59% { opacity: 1 } }
|
||||||
|
@keyframes t20 { 0%, 63% { opacity: 0 } 64% { opacity: 1 } }
|
||||||
|
@keyframes t21 { 0%, 65% { opacity: 0 } 66% { opacity: 1 } }
|
||||||
|
@keyframes t22 { 0%, 67% { opacity: 0 } 68% { opacity: 1 } }
|
||||||
|
@keyframes t23 { 0%, 72% { opacity: 0 } 73% { opacity: 1 } }
|
||||||
|
@keyframes t24 { 0%, 77% { opacity: 0 } 78% { opacity: 1 } }
|
||||||
|
@keyframes cursor { 0%, 49% { opacity: 1 } 50%, 100% { opacity: 0 } }
|
||||||
|
.l { font-family: 'Cascadia Code', 'Fira Code', 'SF Mono', 'Consolas', monospace; font-size: 13px; fill: #c0c0c0; }
|
||||||
|
.pr { fill: #6ec06e; }
|
||||||
|
.cm { fill: #58d0f0; }
|
||||||
|
.fl { fill: #c0c0c0; }
|
||||||
|
.ok { fill: #6ec06e; }
|
||||||
|
.nm { fill: #f0c060; }
|
||||||
|
.dm { fill: #888888; }
|
||||||
|
</style>
|
||||||
|
<!-- Background -->
|
||||||
|
<rect width="720" height="440" rx="8" fill="#1e1e2e"/>
|
||||||
|
<!-- Title bar -->
|
||||||
|
<rect width="720" height="32" rx="8" fill="#2a2a3c"/>
|
||||||
|
<rect y="24" width="720" height="8" fill="#2a2a3c"/>
|
||||||
|
<circle cx="18" cy="16" r="6" fill="#f65f57"/>
|
||||||
|
<circle cx="38" cy="16" r="6" fill="#f9bf2f"/>
|
||||||
|
<circle cx="58" cy="16" r="6" fill="#30c840"/>
|
||||||
|
<text x="360" y="20" text-anchor="middle" font-family="Arial" font-size="13" fill="#888">anvil -- terminal</text>
|
||||||
|
|
||||||
|
<!-- Line 1: prompt + command -->
|
||||||
|
<text y="60" class="l" opacity="0" style="animation: t1 24s infinite">
|
||||||
|
<tspan x="16" class="pr">$</tspan>
|
||||||
|
<tspan class="cm"> anvil new weather_station --template weather --board uno</tspan>
|
||||||
|
</text>
|
||||||
|
|
||||||
|
<!-- Lines 2-11: output -->
|
||||||
|
<text y="78" class="l" opacity="0" style="animation: t2 24s infinite"><tspan x="16" class="ok"> create</tspan><tspan class="fl"> weather_station/weather_station/weather_station.ino</tspan></text>
|
||||||
|
<text y="94" class="l" opacity="0" style="animation: t3 24s infinite"><tspan x="16" class="ok"> create</tspan><tspan class="fl"> weather_station/lib/app/weather_app.h</tspan></text>
|
||||||
|
<text y="110" class="l" opacity="0" style="animation: t4 24s infinite"><tspan x="16" class="ok"> create</tspan><tspan class="fl"> weather_station/lib/hal/hal.h</tspan></text>
|
||||||
|
<text y="126" class="l" opacity="0" style="animation: t5 24s infinite"><tspan x="16" class="ok"> create</tspan><tspan class="fl"> weather_station/lib/drivers/tmp36/tmp36.h</tspan></text>
|
||||||
|
<text y="142" class="l" opacity="0" style="animation: t6 24s infinite"><tspan x="16" class="ok"> create</tspan><tspan class="fl"> weather_station/test/test_weather.cpp</tspan></text>
|
||||||
|
<text y="158" class="l" opacity="0" style="animation: t7 24s infinite"><tspan x="16" class="ok"> create</tspan><tspan class="fl"> weather_station/test/test_unit.cpp</tspan></text>
|
||||||
|
<text y="174" class="l" opacity="0" style="animation: t8 24s infinite"><tspan x="16" class="ok"> create</tspan><tspan class="fl"> weather_station/build.sh, build.bat</tspan></text>
|
||||||
|
<text y="190" class="l" opacity="0" style="animation: t9 24s infinite"><tspan x="16" class="ok"> create</tspan><tspan class="fl"> weather_station/.anvil.toml</tspan></text>
|
||||||
|
<text y="206" class="l" opacity="0" style="animation: t10 24s infinite"><tspan x="16" class="ok"> create</tspan><tspan class="fl"> weather_station/.anvilignore</tspan></text>
|
||||||
|
<text y="222" class="l dm" opacity="0" style="animation: t11 24s infinite"><tspan x="16"> ... 28 more files</tspan></text>
|
||||||
|
<text y="240" class="l" opacity="0" style="animation: t12 24s infinite"><tspan x="16" class="ok"> done</tspan><tspan> Project ready. cd weather_station && ./test.sh</tspan></text>
|
||||||
|
|
||||||
|
<!-- Line 13: second command -->
|
||||||
|
<text y="270" class="l" opacity="0" style="animation: t13 24s infinite">
|
||||||
|
<tspan x="16" class="pr">$</tspan>
|
||||||
|
<tspan class="cm"> cd weather_station && ./test.sh</tspan>
|
||||||
|
</text>
|
||||||
|
|
||||||
|
<!-- Lines 14-18: test output -->
|
||||||
|
<text y="288" class="l dm" opacity="0" style="animation: t14 24s infinite"><tspan x="16">[==========] Running 14 tests from 4 test suites.</tspan></text>
|
||||||
|
<text y="304" class="l" opacity="0" style="animation: t15 24s infinite"><tspan x="16" class="ok">[ PASSED ]</tspan><tspan> WeatherMockTest.ReadsTemperature</tspan></text>
|
||||||
|
<text y="320" class="l" opacity="0" style="animation: t16 24s infinite"><tspan x="16" class="ok">[ PASSED ]</tspan><tspan> WeatherSimTest.NoisyReadingsInRange</tspan></text>
|
||||||
|
<text y="336" class="l dm" opacity="0" style="animation: t17 24s infinite"><tspan x="16"> ... 10 more tests</tspan></text>
|
||||||
|
<text y="354" class="l" opacity="0" style="animation: t18 24s infinite"><tspan x="16" class="ok">[ PASSED ]</tspan><tspan> Tmp36PolymorphismTest.AllImplsWorkThroughBasePointer</tspan></text>
|
||||||
|
|
||||||
|
<!-- Lines 19-20: summary -->
|
||||||
|
<text y="374" class="l" opacity="0" style="animation: t19 24s infinite"><tspan x="16" class="ok">[==========]</tspan><tspan> 14 tests ran. </tspan><tspan class="nm">(12 ms total)</tspan></text>
|
||||||
|
<text y="390" class="l" opacity="0" style="animation: t20 24s infinite"><tspan x="16" class="ok">[ PASSED ] 14 tests.</tspan></text>
|
||||||
|
|
||||||
|
<!-- Line 21: third command -->
|
||||||
|
<text y="414" class="l" opacity="0" style="animation: t23 24s infinite">
|
||||||
|
<tspan x="16" class="pr">$</tspan>
|
||||||
|
<tspan class="cm"> ./upload.sh --monitor</tspan>
|
||||||
|
</text>
|
||||||
|
|
||||||
|
<!-- Blinking cursor -->
|
||||||
|
<rect x="210" y="404" width="8" height="15" fill="#58d0f0" opacity="0" style="animation: t23 24s infinite, cursor 1s step-end infinite"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 6.5 KiB |
Reference in New Issue
Block a user