diff --git a/hardware/rpi/pico/Clock.zig b/hardware/rpi/pico/Clock.zig new file mode 100644 index 0000000..9acd6b6 --- /dev/null +++ b/hardware/rpi/pico/Clock.zig @@ -0,0 +1,17 @@ +const rt = @import("reticulum"); +const microzig = @import("microzig"); +const rp2 = microzig.hal; + +const Self = @This(); + +pub fn monotonicMicros(ptr: *anyopaque) u64 { + _ = ptr; + return rp2.time.get_time_since_boot().to_us(); +} + +pub fn clock(self: *Self) rt.System.Clock { + return .{ + .ptr = self, + .monotonicMicrosFn = monotonicMicros, + }; +} diff --git a/hardware/rpi/pico/Led.zig b/hardware/rpi/pico/Led.zig new file mode 100644 index 0000000..88a4d99 --- /dev/null +++ b/hardware/rpi/pico/Led.zig @@ -0,0 +1,23 @@ +const std = @import("std"); +const microzig = @import("microzig"); +const rp2 = microzig.hal; + +const Self = @This(); + +pin: rp2.gpio.Pin, + +pub fn init(index: u6) Self { + return Self{ + .pin = rp2.gpio.num(index), + }; +} + +pub fn setup(self: *Self) void { + self.pin.set_function(.sio); + self.pin.set_direction(.out); + self.pin.put(1); +} + +pub fn toggle(self: *Self) void { + self.pin.toggle(); +} diff --git a/hardware/rpi/pico/Uart.zig b/hardware/rpi/pico/Uart.zig new file mode 100644 index 0000000..82424b9 --- /dev/null +++ b/hardware/rpi/pico/Uart.zig @@ -0,0 +1,36 @@ +const std = @import("std"); +const microzig = @import("microzig"); +const rp2 = microzig.hal; + +const Self = @This(); + +const uart = rp2.uart.instance.num(0); +const baud_rate = 115200; + +tx_pin: rp2.gpio.Pin, +rx_pin: rp2.gpio.Pin, + +pub fn init() Self { + return Self{ + .tx_pin = rp2.gpio.num(0), + .rx_pin = rp2.gpio.num(1), + }; +} + +pub fn setup(self: *Self) void { + switch (rp2.compatibility.cpu) { + .RP2040 => inline for (&.{ self.tx_pin, self.rx_pin }) |pin| { + pin.set_function(.uart); + }, + .RP2350 => inline for (&.{ self.tx_pin, self.rx_pin }) |pin| { + pin.set_function(.uart_second); + }, + } + + uart.apply(.{ + .baud_rate = baud_rate, + .clock_config = rp2.clock_config, + }); + + rp2.uart.init_logger(uart); +} diff --git a/hardware/rpi/pico/main.zig b/hardware/rpi/pico/main.zig new file mode 100644 index 0000000..d30570a --- /dev/null +++ b/hardware/rpi/pico/main.zig @@ -0,0 +1,80 @@ +// Adapted from the microzig usb_cdc example. + +const std = @import("std"); +const rt = @import("reticulum"); +const microzig = @import("microzig"); +const rp2 = microzig.hal; + +const Clock = @import("Clock.zig"); +const Led = @import("Led.zig"); +const Uart = @import("Uart.zig"); +const UsbSerial = @import("serial.zig").Usb; + +pub fn panic(message: []const u8, _: ?*std.builtin.StackTrace, _: ?usize) noreturn { + std.log.err("panic: {s}", .{message}); + @breakpoint(); + while (true) {} +} + +pub const microzig_options = .{ + .log_level = .debug, + .logFn = rp2.uart.logFn, +}; + +var heap: [16_000]u8 = .{0} ** 16_000; + +pub fn main() !void { + var led = Led.init(25); + var uart = Uart.init(); + var serial = UsbSerial.init(); + + led.setup(); + uart.setup(); + serial.setup(); + + var fba = std.heap.FixedBufferAllocator.init(&heap); + var pico_clock: Clock = .{}; + var ascon = rp2.rand.Ascon.init(); + + const ally = fba.allocator(); + var clock = pico_clock.clock(); + var rng = ascon.random(); + + const announce = try make_announce(ally, clock, &rng); + const half_a_second = 500000; + var timestamp: u64 = clock.monotonicMicros(); + + while (true) { + serial.task(); + + const now = clock.monotonicMicros(); + if (now - timestamp > half_a_second) { + timestamp = now; + led.toggle(); + + serial.writePacket(&announce); + } + + const message = serial.read(); + if (message.len > 0) { + const hash = rt.crypto.Hash.hash_data(message); + serial.writeFmt("Your message: {s} => hash {s}\n", .{ message, hash.hex() }); + } + } +} + +fn make_announce(ally: std.mem.Allocator, clock: rt.System.Clock, rng: *rt.System.Rng) !rt.Packet { + const identity = try rt.crypto.Identity.random(rng); + + var builder = rt.endpoint.Builder.init(ally); + _ = try builder + .set_identity(identity) + .set_direction(.in) + .set_method(.single) + .set_application_name("reticulum-pico"); + _ = try builder.append_aspect("test"); + const endpoint = try builder.build(); + + var packet_factory = rt.packet.Factory.init(ally, clock, rng.*, .{}); + return try packet_factory.make_announce(&endpoint, "some application data"); +} diff --git a/hardware/rpi/pico/serial.zig b/hardware/rpi/pico/serial.zig new file mode 100644 index 0000000..6b330f4 --- /dev/null +++ b/hardware/rpi/pico/serial.zig @@ -0,0 +1 @@ +pub const Usb = @import("serial/Usb.zig"); diff --git a/hardware/rpi/pico/serial/Usb.zig b/hardware/rpi/pico/serial/Usb.zig new file mode 100644 index 0000000..b504fde --- /dev/null +++ b/hardware/rpi/pico/serial/Usb.zig @@ -0,0 +1,102 @@ +const std = @import("std"); +const rt = @import("reticulum"); +const microzig = @import("microzig"); +const rp2 = microzig.hal; + +const Self = @This(); + +const UsbDevice = rp2.usb.Usb(.{}); +const UsbClassDriver = rp2.usb.types.UsbClassDriver; +const CdcDriver = rp2.usb.cdc.CdcClassDriver(UsbDevice); +const DeviceConfiguration = rp2.usb.DeviceConfiguration; +const Config = struct { + const endpoint_in_1 = rp2.usb.Endpoint.to_address(1, .In); + const endpoint_in_2 = rp2.usb.Endpoint.to_address(2, .In); + const endpoint_out_2 = rp2.usb.Endpoint.to_address(2, .Out); + const config_descriptor = rp2.usb.templates.config_descriptor(1, 2, 0, descriptors_length, 0xc0, 100); + const cdc_descriptor = rp2.usb.templates.cdc_descriptor(0, 4, endpoint_in_1, 8, endpoint_out_2, endpoint_in_2, 64); + const descriptors = config_descriptor ++ cdc_descriptor; + const descriptors_length = rp2.usb.templates.config_descriptor_len + rp2.usb.templates.cdc_descriptor_len; +}; +var cdc_driver: CdcDriver = .{}; +var drivers = [_]UsbClassDriver{cdc_driver.driver()}; +var device_configuration = DeviceConfiguration{ + .device_descriptor = &.{ + .descriptor_type = rp2.usb.DescType.Device, + .bcd_usb = 0x0200, + .device_class = 0xEF, + .device_subclass = 2, + .device_protocol = 1, + .max_packet_size0 = 64, + .vendor = 0x2E8A, + .product = 0x000a, + .bcd_device = 0x0100, + .manufacturer_s = 1, + .product_s = 2, + .serial_s = 0, + .num_configurations = 1, + }, + .config_descriptor = &Config.descriptors, + .lang_descriptor = "\x04\x03\x09\x04", // length || string descriptor (0x03) || Engl (0x0409) + .descriptor_strings = &.{ + &rp2.usb.utils.utf8ToUtf16Le("Raspberry Pi"), + &rp2.usb.utils.utf8ToUtf16Le("Pico Test Device"), + &rp2.usb.utils.utf8ToUtf16Le("someserial"), + &rp2.usb.utils.utf8ToUtf16Le("Board CDC"), + }, + .drivers = &drivers, +}; + +pub const buffer_size = 1024; + +tx_buffer: [buffer_size]u8, +rx_buffer: [buffer_size]u8, + +pub fn init() Self { + return Self{ + .tx_buffer = .{0} ** buffer_size, + .rx_buffer = .{0} ** buffer_size, + }; +} + +pub fn setup(self: *Self) void { + _ = self; + UsbDevice.init_clk(); + UsbDevice.init_device(&device_configuration) catch unreachable; +} + +pub fn task(self: *Self) void { + _ = self; + const no_debug_over_uart = false; + UsbDevice.task(no_debug_over_uart) catch unreachable; +} + +pub fn writePacket(self: *Self, packet: *const rt.packet.Packet) void { + var write_buff: []const u8 = packet.write(&self.tx_buffer); + while (write_buff.len > 0) { + write_buff = cdc_driver.write(write_buff); + } +} + +pub fn writeFmt(self: *Self, comptime fmt: []const u8, args: anytype) void { + const text = std.fmt.bufPrint(&self.tx_buffer, fmt, args) catch &.{}; + + var write_buff = text; + while (write_buff.len > 0) { + write_buff = cdc_driver.write(write_buff); + } +} + +pub fn read(self: *Self) []const u8 { + var total_read: usize = 0; + var read_buff: []u8 = self.rx_buffer[0..]; + + while (true) { + const len = cdc_driver.read(read_buff); + read_buff = read_buff[len..]; + total_read += len; + if (len == 0) break; + } + + return self.rx_buffer[0..total_read]; +}