# Anvil **Arduino project generator and build tool -- forges clean embedded projects.** A single binary that scaffolds self-contained Arduino projects with hardware abstraction, Google Mock infrastructure, and a streamlined build/upload/monitor workflow. Works on Linux and Windows. Generated projects are fully standalone -- they only need `arduino-cli` in PATH. The Anvil binary is a scaffolding and diagnostic tool, not a runtime dependency. Anvil is a [Nexus Workshops](https://nxlearn.net) project. ## Install Download the latest release binary for your platform: ```bash # Linux chmod +x anvil sudo mv anvil /usr/local/bin/ # Windows -- add anvil.exe to a directory in your PATH ``` Then run first-time setup: ```bash anvil setup ``` This checks for `arduino-cli`, installs the `arduino:avr` core, and verifies your system is ready. If something is missing, Anvil tells you exactly what to do. ## Your First Project Create a project, plug in your board, and upload: ```bash anvil new blink cd blink ``` On **Linux/macOS**: ```bash ./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 anvil devices ``` This lists every serial port with its board name, protocol, and -- for USB devices -- its VID:PID identifier. The VID:PID is a pair of hex codes that uniquely identifies the USB chip on your board (e.g. `0403:6001` for an FTDI, `1a86:7523` for a CH340). ### Save a default device ```bash 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 stores the port name *and* the VID:PID of the device on that port. It looks like this: ```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 the project keeps its own copy with its own port assignment. ### Why VID:PID matters 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 anvil devices --get ``` This shows the saved port and VID:PID for the current project, whether the device is currently connected, and whether it has moved to a different port. ### Port resolution priority When the scripts need a port, they check in this order: 1. **`-p` flag** -- `upload.bat -p COM3` always wins 2. **VID:PID** from `.anvil.local` -- finds the device regardless of port number 3. **Saved port** from `.anvil.local` -- fallback if VID:PID lookup fails 4. **Auto-detect** -- prefers USB serial over legacy COM ports ### Specifying a project directory Both `--set` and `--get` default to the current directory, but you can point them at another project: ```bash anvil devices --set COM3 -d ../other-project anvil devices --get -d ../other-project ``` ## Updating Project Scripts 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 Every generated project follows the same layout: ``` your-project/ your-project/your-project.ino Entry point (setup + loop) lib/ hal/ hal.h Hardware abstraction interface hal_arduino.h Real hardware implementation app/ your-project_app.h Application logic (testable) test/ mocks/ mock_hal.h Google Mock HAL sim_hal.h Stateful simulator HAL test_unit.cpp Unit tests CMakeLists.txt Test build system run_tests.sh / .bat Test runner build.sh / build.bat Compile sketch upload.sh / upload.bat Compile + upload to board monitor.sh / monitor.bat Serial monitor _detect_port.ps1 Port detection helper (Windows) .anvil.toml Project config (tracked by git) .anvil.local Machine-specific config (gitignored) ``` All hardware access goes through the `Hal` interface in `lib/hal/hal.h`. Application code in `lib/app/` depends only on `Hal`, never on `Arduino.h` directly. This means the app logic compiles and runs on the host for testing with Google Mock -- no board required. ## Configuration ### .anvil.toml (tracked by git) Shared project settings. Edit this to change the board, baud rate, or compiler flags: ```toml [project] name = "blink" anvil_version = "1.0.0" [build] fqbn = "arduino:avr:uno" warnings = "more" include_dirs = ["lib/hal", "lib/app"] extra_flags = ["-Werror"] [monitor] baud = 115200 ``` ### .anvil.local (not tracked by git) Machine-specific settings, created by `anvil devices --set`: ```toml port = "COM3" vid_pid = "0403:6001" ``` ## Building from Source ```bash cargo build --release ``` The release binary is at `target/release/anvil` (Linux) or `target\release\anvil.exe` (Windows). ## License MIT -- see [LICENSE](LICENSE).