Anvil v1.0.0 -- Arduino build tool with HAL and test scaffolding

Single-binary CLI that scaffolds testable Arduino projects, compiles,
uploads, and monitors serial output. Templates embed a hardware
abstraction layer, Google Mock infrastructure, and CMake-based host
tests so application logic can be verified without hardware.

Commands: new, doctor, setup, devices, build, upload, monitor
39 Rust tests (21 unit, 18 integration)
Cross-platform: Linux and Windows
This commit is contained in:
Eric Ratliff
2026-02-15 11:16:17 -06:00
commit 3298844399
41 changed files with 4866 additions and 0 deletions

View File

@@ -0,0 +1,88 @@
#ifndef APP_H
#define APP_H
#include <hal.h>
/*
* BlinkApp -- Testable blink logic, decoupled from hardware.
*
* Blinks an LED and reads a button. When the button is pressed,
* the blink rate doubles (toggles between normal and fast mode).
*
* All hardware access goes through the injected Hal pointer. This
* class has no dependency on Arduino.h and compiles on any host.
*/
class BlinkApp {
public:
static constexpr uint8_t DEFAULT_LED_PIN = LED_BUILTIN; // pin 13
static constexpr uint8_t DEFAULT_BUTTON_PIN = 2;
static constexpr unsigned long SLOW_INTERVAL_MS = 500;
static constexpr unsigned long FAST_INTERVAL_MS = 125;
BlinkApp(Hal* hal,
uint8_t led_pin = DEFAULT_LED_PIN,
uint8_t button_pin = DEFAULT_BUTTON_PIN)
: hal_(hal)
, led_pin_(led_pin)
, button_pin_(button_pin)
, led_state_(LOW)
, fast_mode_(false)
, last_toggle_ms_(0)
, last_button_state_(HIGH) // pulled up, so HIGH = not pressed
{}
// Call once from setup()
void begin() {
hal_->pinMode(led_pin_, OUTPUT);
hal_->pinMode(button_pin_, INPUT_PULLUP);
hal_->serialBegin(115200);
hal_->serialPrintln("BlinkApp started");
last_toggle_ms_ = hal_->millis();
}
// Call repeatedly from loop()
void update() {
handleButton();
handleBlink();
}
// -- Accessors for testing ----------------------------------------------
bool ledState() const { return led_state_ == HIGH; }
bool fastMode() const { return fast_mode_; }
unsigned long interval() const {
return fast_mode_ ? FAST_INTERVAL_MS : SLOW_INTERVAL_MS;
}
private:
void handleButton() {
uint8_t reading = hal_->digitalRead(button_pin_);
// Detect falling edge (HIGH -> LOW = button press with INPUT_PULLUP)
if (last_button_state_ == HIGH && reading == LOW) {
fast_mode_ = !fast_mode_;
hal_->serialPrintln(fast_mode_ ? "FAST" : "SLOW");
}
last_button_state_ = reading;
}
void handleBlink() {
unsigned long now = hal_->millis();
unsigned long target = fast_mode_ ? FAST_INTERVAL_MS : SLOW_INTERVAL_MS;
if (now - last_toggle_ms_ >= target) {
led_state_ = (led_state_ == HIGH) ? LOW : HIGH;
hal_->digitalWrite(led_pin_, led_state_);
last_toggle_ms_ = now;
}
}
Hal* hal_;
uint8_t led_pin_;
uint8_t button_pin_;
uint8_t led_state_;
bool fast_mode_;
unsigned long last_toggle_ms_;
uint8_t last_button_state_;
};
#endif // APP_H