Firmware for an Arduino Uno (ATmega328P) robot arm, written in Zig with Arduino core libraries handling hardware peripherals.
Application logic lives in Zig. Arduino's C++ core handles hardware
initialization, interrupts, and peripheral drivers. A thin C++ bridge
(arduino_bridge.cpp) exposes Arduino APIs to Zig via extern "C" functions.
flowchart TD
subgraph compile [Compile]
mainZig["main.zig"] -->|"Zig compiler (AVR target)"| robotO["robot.o"]
coreC["Arduino Core .c"] -->|avr-gcc| coreO["core .o files"]
coreCpp["Arduino Core .cpp"] -->|avr-g++| coreO
servoLib["Servo lib .cpp"] -->|avr-g++| servoO["Servo.o"]
bridgeCpp["arduino_bridge.cpp"] -->|avr-g++| bridgeO["bridge.o"]
end
subgraph link [Link]
robotO --> linker["avr-gcc -mmcu=atmega328p"]
coreO --> linker
servoO --> linker
bridgeO --> linker
libs["libm + libgcc + CRT startup"] --> linker
linker --> elf["robot.elf"]
end
subgraph output [Output]
elf -->|avr-objcopy| hex["robot.hex"]
hex -->|arduino-cli upload| mcu["ATmega328P"]
end
Arduino's main.cpp (from ArduinoCore-avr) is the real entry point:
flowchart TD
crt["CRT startup (stack, .data/.bss)"] --> main["main() -- Arduino main.cpp"]
main --> init["init() -- timers, ADC, sei()"]
init --> setup["setup() -- your Zig code"]
setup --> loop["loop() -- your Zig code, called forever"]
loop -->|"servo.write()"| bridge["arduino_bridge.cpp"]
loop -->|"Serial.println()"| bridge
loop -->|"arduino.delay()"| bridge
bridge --> servoLib["Arduino Servo lib"]
bridge --> hwSerial["Arduino HardwareSerial"]
bridge --> wiring["Arduino wiring.c"]
loop -->|repeats| loop
zig/
build.zig -- build pipeline: Zig compiler + avr-gcc + avr-objcopy
build.zig.zon -- dependencies (ArduinoCore-avr, Servo library)
src/
main.zig -- application: setup() and loop() entry points
lib/
arduino.zig -- Zig bindings for Arduino core (Serial, GPIO, delay)
servo.zig -- Zig Servo struct wrapping the Arduino Servo library
arduino_bridge.cpp -- C++ bridge: exposes Arduino APIs as extern "C"
| Dependency | Version | Purpose |
|---|---|---|
| ArduinoCore-avr | 1.8.7 | Arduino core (wiring, serial, timers, main.cpp) |
| Servo | 1.2.2 | Servo motor control via Timer1 |
Managed via build.zig.zon -- Zig fetches them automatically.
- Zig >= 0.15.2
- avr-gcc 9.x (
brew install avr-gcc@9) - avr-binutils (
brew install avr-binutils) - arduino-cli (for uploading and serial monitoring)
# Build only (produces zig-out/firmware/robot.hex)
cd zig && zig build
# Or from the repo root using make:
make deploy_zig # build + upload
make monit # open serial monitor (9600 baud)- Add the dependency to
build.zig.zon(usezig fetch --save <url>) - In
build.zig: addb.dependency(...), append its include path toinclude_paths, compile its.cppsource withcompileWithAvrGpp - In
arduino_bridge.cpp: addextern "C"wrapper functions - In
src/lib/: create a.zigmodule with@externbindings