Open Source · MIT License

Anvil

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 Problem

Arduino Projects Grow Into
a Mess. They Don't Have To.

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?"

01

You Can't Test Without the Board

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.

02

Logic Tangled With Hardware

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.

03

No Mock, No Simulator

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.

04

Infrastructure From Scratch Every Time

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.

05

The Same Pattern Scales

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.

The Approach

One Command. A Complete
Engineering Foundation.

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.

Hardware Abstraction Layer

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.

Mock & Simulator Split

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.

Google Test, Ready to Run

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.

Pin Management & Validation

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.

Refresh Without Clobbering

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.

Multi-Board Projects

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.

~/projects/workshop
$anvil new weather_station --template weather --board uno
okCreated project 'weather_station' (weather / uno)
okHAL + mock + simulator generated
okTMP36 driver installed
--12 example tests, 2 student test starters
 
$cd weather_station && ./test.sh
ok14 tests passed -- 0.4s
--No board required.
 
$anvil add button --pin 2
okInstalled driver: button (pin 2)
okCMakeLists.txt updated automatically
 
$./upload.sh
okCompiled, uploaded to /dev/ttyUSB0
--Monitoring. Press Ctrl+C to stop.
Workflow

From Scaffold to Upload in Minutes

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.

  • One command creates the full project
  • Tests run on your laptop — no board needed
  • Add sensor libraries with pin validation
  • Upload with auto-detected serial port
  • Works on Linux, macOS, and Windows
  • Refresh infrastructure without losing your code
Templates

Start From a Working Project,
Not a Blank Sketch

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 basic

Clean Foundation

A 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.

~12Files
1Test
  • HAL interface + Arduino implementation
  • Mock HAL for unit tests
  • Sim HAL for behavioral tests
  • Cross-platform build, upload, monitor, test scripts
--template weather

Weather Station

A 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.

~20Files
14Tests
  • TMP36 driver with mock and simulator
  • WeatherApp with temperature averaging
  • Unit tests: mock returns, exact values
  • System tests: simulator with noise and drift
  • Student test starters protected from refresh
Also: Button Template

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.

Architecture

One Architectural Rule, Consistently Applied

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.

weather_station/
weather_station/.ino thin shell
lib/
hal/
hal.h pure virtual interface
hal_arduino.h real board
app/
weather_app.h your logic
drivers/
tmp36/ interface
hardware impl
mock
simulator
test/
mocks/ Arduino shims
test_unit.cpp your tests
test_weather.cpp examples
CMakeLists.txt fetches GTest
build.sh / build.bat
.anvil.toml project config
.anvilignore your files, protected
Libraries

Add Sensors With One Command.
Tests Come With Them.

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 A0

TMP36 Temperature Sensor

Analog temperature sensor with averaging. Simulator produces realistic thermal noise and gradual drift. Mock returns exact values for verifying averaging logic.

Mock Simulator
anvil add button --pin 2

Digital Pushbutton

Edge-detection with debounce. Simulator models realistic contact bounce timing so your debounce logic actually gets tested, not just called with clean transitions.

Mock Simulator
anvil lib --available

More Libraries

View the full registry of available drivers. New libraries follow the same four-file pattern. Adding a new template requires zero Rust code changes.

Mock Simulator
Why Anvil

Every Professional Embedded Team
Already Does This. Now You Can Too.

Hardware abstraction layers, mock objects, and host-side test suites are not exotic practices. They are the baseline in defense, medical devices, automotive, and industrial controls. The barrier was always the setup. Anvil removes it.

< 1s
full test suite runtime
on your laptop, no board, no USB cable, no waiting
1
command to scaffold
anvil new my_project --template weather and you have a complete, tested project
0
runtime dependency
Anvil generates the project and walks away. No lock-in.
Without Anvil
  • Upload to board to test any change
  • Stare at serial monitor, guess what went wrong
  • digitalRead() called directly in logic code
  • Can't test without physical hardware
  • CMake setup: a weekend project
  • Testing is manual and happens at runtime
  • Bug discovered at the competition
With Anvil
  • Run full test suite in under a second on your laptop
  • Tests give you exact failure messages
  • Application code depends only on a Hal interface
  • Mock HAL runs tests anywhere, anytime
  • CMake setup: zero minutes, it's already done
  • Testing is automated and happens at the desk
  • Bug found before you ever touched the board

"The mock/simulator split is one of the most important distinctions in embedded testing. A mock verifies that your code calls the hardware correctly. A simulator verifies that your code handles realistic hardware behavior. Most student projects have neither. Anvil gives you both, with examples showing the difference."

From the Anvil documentation

Why "Anvil"?

Philosophy

An anvil does not stay in the finished sword. It shapes the project, establishes the structure, and then steps aside. What remains is entirely yours. That is the point.

Built with Rust. Open source. By an engineer who teaches embedded systems and believes professional tools belong in the classroom, not just the workplace.

01

Scaffold, Don't Depend

02

Test Everything

03

Abstract the Hardware

04

Mock vs Sim

05

No Magic

06

Same Patterns, Any Scale

07

Learn by Building

Ready to Start Building
Better Embedded Projects?

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.

Questions? eric@nxlearn.net