Forge clean, testable Arduino projects from a single command.
Anvil generates self-contained embedded projects with hardware abstraction, mock/simulator infrastructure, a Google Test suite, and cross-platform build scripts. Once the project is forged, Anvil walks away.
git clone https://nxgit.dev/nexus-workshops/anvil.git
The classic Arduino project is a single .ino file that talks directly to the hardware. It works fine for blinking an LED. It falls apart when you have sensors, actuators, state machines, and a student asking "why did it stop working at 3am before the competition?"
The sketch calls digitalRead() directly. There is no seam to inject test behavior. You upload, watch the serial monitor, guess, change, repeat. That loop is slow and it teaches bad habits.
When sensor reads and application decisions live in the same function, the only way to test the decision is to have the sensor. Hardware abstraction exists to break this coupling. Anvil builds it in from the start.
Professional embedded teams use mocks for unit tests and simulators for behavioral tests. Most student projects use neither. The result: bugs that only appear at runtime, often at the worst possible moment.
Setting up CMake, Google Test, Arduino CLI, and cross-platform build scripts is a weekend project. Anvil does it in one command so you can spend the weekend on the actual problem.
HAL-based architecture with dependency injection is not an Arduino trick. It is how embedded systems are built at every level of industry. Learning it on an Uno is the right place to start.
Anvil generates a project where your application code depends only on a Hal interface. Tests create a mock HAL. The sketch creates the real one. This is constructor injection — the simplest form of dependency inversion.
Your application code in lib/app/ depends only on the Hal interface, never on Arduino.h. The sketch creates the real HAL and passes it in. Tests create a mock instead. Clear seam. No magic.
Every library ships two test doubles. The mock returns exact values you set — for verifying logic. The simulator returns realistic values with noise, bounce, and drift — for verifying behavior. The difference between these is one of the most important things in testing.
CMakeLists.txt fetches Google Test and auto-discovers driver directories. ./test.sh runs your entire test suite on your laptop, no board required, in under a second. You find bugs at the desk, not at the competition.
Assign pins by name, not by magic number. anvil pin --assign tmp36_data A0. Generate a pins.h header. Audit your wiring against library requirements. Catch wiring errors before you discover them at 11pm.
When Anvil ships improvements to build scripts or library drivers, anvil refresh updates the managed files without touching your application code, your tests, or your config. .anvilignore protects everything you own.
One project, multiple boards. Add a Mega, a Nano, a custom board. Each gets its own FQBN, baud rate, and pin assignments in .anvil.toml. Scripts auto-detect connected boards by USB VID:PID, not just port name.
Anvil sets up your whole project in one shot, then walks away. Everything after that runs without Anvil installed at all — just your code, the build scripts, and the board.
Templates are pure data. Each is a directory with a template.toml that declares its base, required libraries, and per-board pin defaults. Pick the one that matches where you are.
--template basicA blank canvas with the right structure. HAL interface, real and mock implementations, CMakeLists.txt, cross-platform scripts, and a starter test file. Nothing you don't need yet, everything you'll need when you do.
--template weatherA complete application with a TMP36 temperature sensor, state management, managed example tests, and student test starters. The example tests show you the mock pattern and the simulator pattern. Start here if you are learning.
The --template button adds a ButtonApp with proper edge detection — it prints "Button pressed!" exactly once per press, no repeated messages from holding it down. The debounce simulator makes the behavioral test actually test debounce behavior.
Application code in lib/app/ depends only on the Hal interface, never on Arduino.h. The sketch creates the real HAL and passes it in. Tests create a mock or simulator HAL instead.
This is constructor injection. It is the simplest form of dependency inversion. Learning it here means you will recognize it in every professional codebase you encounter for the rest of your career.
Each library ships four files: an abstract interface, a hardware implementation, a test mock, and a deterministic simulator. CMakeLists.txt auto-discovers driver directories.
anvil add tmp36 --pin A0Analog temperature sensor with averaging. Simulator produces realistic thermal noise and gradual drift. Mock returns exact values for verifying averaging logic.
anvil add button --pin 2Edge-detection with debounce. Simulator models realistic contact bounce timing so your debounce logic actually gets tested, not just called with clean transitions.
anvil lib --availableView the full registry of available drivers. New libraries follow the same four-file pattern. Adding a new template requires zero Rust code changes.
Anvil is free, open source, and MIT licensed. Download the binary, run anvil setup, and have a complete project with 12 passing tests in under five minutes.