Arduino zig: blink
blink is like hello world on Arduino, and here I will show how to do it in zig. First, you need to create a zig project:
mkdir zig-arduino && cd zig-arduino
zig init-exe
1. Blink(programming)
You can look the following picture from Arduino Examples to figure out how to build these handcrafts.
Now, you can start programming(in src/main.zig
). First, import
arduino
and arduino.gpio
:
const arduino = @import("arduino"); const gpio = arduino.gpio;
@import("arduino")
is special since it's not import a local package,
but a global one, this need to be setup in build.zig
, you can learn
this in setup section. A side effect code is
required:
pub const panic = arduino.start.panicHang;
Finally, came to main
function. Idea is simple:
- initialize LED(13) as output pin
- loop
- turn on LED(set LED pin to high)
- delay 500ms
- turn off(set LED pin to low)
- delay 500ms
const LED: u8 = 13; pub fn main() void { gpio.setMode(LED, .output); while (true) { gpio.setPin(LED, .high); arduino.cpu.delayMilliseconds(500); gpio.setPin(LED, .low); arduino.cpu.delayMilliseconds(500); } }
All code is here:
const arduino = @import("arduino"); const gpio = arduino.gpio; // Necessary, and has the side effect of pulling in the needed _start method pub const panic = arduino.start.panicHang; const LED: u8 = 13; pub fn main() void { gpio.setMode(LED, .output); while (true) { gpio.setPin(LED, .high); arduino.cpu.delayMilliseconds(500); gpio.setPin(LED, .low); arduino.cpu.delayMilliseconds(500); } }
2. Zig project setup
zig init-exe zigmod init
zigmod
is a package manager for zig, you might need to install it first. I recommended you just install it from source.
Now, you should have a tree
. ├── README.md ├── build.zig ├── src │ └── main.zig └── zig.mod
Add the line - src: git https://github.com/dannypsnl/avr-arduino-zig
after dev_dependencies:
in zig.mod
. Example:
id: <your id> name: <your project name> license: <your license> description: <your description> dev_dependencies: - src: git https://github.com/dannypsnl/avr-arduino-zig
Then you run zigmod fetch
, the command creates deps.zig
in the
current directory, where deps.addAllTo(exe)
will let
@import("arduino")
work. Now, you are able to modify build.zig
to
fit this project!
pub fn build(b: *std.build.Builder) !void { const uno = std.zig.CrossTarget{ .cpu_arch = .avr, .cpu_model = .{ .explicit = &std.Target.avr.cpu.atmega328p }, .os_tag = .freestanding, .abi = .none, }; const exe = b.addExecutable("blink", "main.zig"); deps.addAllTo(exe); exe.setTarget(uno); exe.setBuildMode(.ReleaseSmall); exe.bundle_compiler_rt = false; exe.setLinkerScriptPath(.{ .path = deps.dirs._ie76bs50j4tl ++ "/src/linker.ld" }); exe.install(); // ... }
This is enough to get zig-out/bin/blink
after run zig build
. You can
run command like
avrdude -carduino -patmega328p -D -P /dev/<your device> -Uflash:w:./zig-out/bin/blink:e
to upload binary and see result, so the following section is optional.
2.1. Custom command in build.zig
pub fn build(b: *std.build.Builder) !void { // ... const tty = b.option( []const u8, "tty", "Specify the port to which the Arduino is connected (defaults to /dev/ttyACM0)", ) orelse "/dev/ttyACM0"; const bin_path = b.getInstallPath(exe.install_step.?.dest_dir, exe.out_filename); const flash = blk: { var tmp = std.ArrayList(u8).init(b.allocator); try tmp.appendSlice("-Uflash:w:"); try tmp.appendSlice(bin_path); try tmp.appendSlice(":e"); break :blk tmp.toOwnedSlice(); }; const avrdude = b.addSystemCommand(&.{ "avrdude", "-carduino", "-patmega328p", "-D", "-P", tty, flash, }); const upload = b.step("upload", "Upload the code to an Arduino device using avrdude"); upload.dependOn(&avrdude.step); avrdude.step.dependOn(&exe.install_step.?.step); }
The point is addSystemCommand
, which allowed you to add arbitrary
command into build script! flash
is unfortunately, but we always need
allocator for a runtime string.