Boards · Rust firmware · Open source

Tools for people who build hardware

Boards, Rust firmware and the open-source software that ties them together, from Norway.

draft Copy not yet read by Marketing and PM. Board renders follow.

FIG. 1B153 Donguru

01Products

Hardware for the bench

Three tools for bring-up, debugging and test, each with a Rust driver, a CLI and a GUI.

FIG. 2B153 Donguru

Donguru Coming Q1–Q2 2027

A USB-C dongle with a USB hub, a serial port, a power switch and a power meter.

Hub
USB, on board
Serial
TX/RX flip
Power
switch + meter
FIG. 3B200 rePort

rePort In development

Digital and analog I/O over USB. No programming needed.

Link
USB
Digital
USART, SPI, I2C, 1-Wire
Levels
selectable
FIG. 4B135 USB to CAN FD

CAN line In development

USB and Ethernet bridges to isolated CAN FD, with SD card logging, and the adapters around them.

Bus
CAN FD, isolated
Host
USB or Ethernet
Connector
DB-9

02Donguru · B153

One cable to your board

Donguru sits between your computer and a board with a USB-C port. One cable carries USB, a serial console and power you can switch and measure.

  1. Hub and serial in one plug

    A USB hub and a UART on the same cable. TX and RX are flipped for you.

  2. Power under control

    Electronic switches on VBUS and the USB data lines, a power meter, and a mechanical switch for the boot mode.

  3. Scriptable

    A fast CLI, donguru or dg, with a Rust driver and Python behind it.

For development, debugging and production flashing of devices with a USB-C port.

Donguru block diagram The host's USB-C cable goes into a USB hub. The hub feeds the on-board MCU and, through a data line switch, the USB-C plug to your board. VBUS passes a power switch with a power meter on its way to the plug. The MCU's UART reaches the plug with TX and RX flipped as needed, and the MCU drives both switches. HOST USB-C USB HUB ON BOARD DATA SW VBUS SW + METER PLUG USB-C TO BOARD MCU FIRMWARE USB VBUS UART · TX/RX FLIP CTRL
FIG. 5Donguru block diagram draft

03Software

See what your device says

IOWeaver plots what your device says. RockFace debugs the firmware that says it. WireWeaver keeps both sides speaking the same API.

FIG. 6 · Software

GUI

IOWeaver: protocol analyzer and grapher

Debugger

RockFace: firmware debugger

Code

// The API, once, for both sides (ww_uart):
#[ww_trait]
#[claim(exclusive)]
pub trait Uart {
    #[counted]
    stream!(rx: RxChunk<'i>);

    #[credit]
    sink!(tx: MaxLen<RefVec<'i, u8>, UART_TX_MAX>);

    fn capabilities() -> Capabilities<'i>;
}

// A device with several ports lists them as one array:
ww_impl!(uart[]: ww_uart::Uart)

IOWeaver

Protocol analyzer and grapher. Record, decode, plot and send. It ships with rePort and Donguru.

RockFace draft

Firmware debugger: logs, stack and CPU use, zero-cost profiling, logs from a fleet. First paid release planned for January 2027.

WireWeaver

One API definition for device and host: RPC, streams and properties, backwards and forwards compatible. Open source.

04From the lab

Work I am proud of

Selected boards and machines from my own work: optics, power, motor control and precision analog.

Assembled laser scanner machine on a workbench
FIG. 7

3D laser scanner

Optical inspection of solder paste and placed parts with a scanning green laser line.

Resolution
15 µm
Optics
F-Theta, telecentric
Stage
2× CoreXY
6-step and FOC motor controller board, top view
FIG. 8

Motor controller

6-step and FOC control for brushless motors.

MCU
STM32F4
Bus
CAN
6-10S BMS board, 50A nominal / 100A peak, top view with copper bus bars
FIG. 9

BMS

Battery management for 6 to 10 cells in series.

Current
50 A, 100 A peak
AFE
BQ76930
Gauge
BQ34Z100
Round torque sensor AFE board, held to show its small size
FIG. 10

Torque sensor AFE

Analog front end that rides on the shaft and talks through a slip ring.

Mount
shaft
Link
slip ring

More from the lab

05Open source

From register to GUI

The products stand on open crates, all Rust. Each layer works on its own, and together they reach from the M_CAN registers to the table on your screen.

  1. ProductsDonguru · rePort · CAN line · IOWeaver · RockFace
  2. GUI

    The desktop side: IOWeaver and RockFace are built from these.

    vhrd_egui

    not on crates.io yet

    egui building blocks: the widget frame every panel sits in, a dock that moves widgets between windows, an HDR phosphor scope.

    let hints = SizeHints::new([90.0, 30.0], [200.0, 80.0], [360.0, 80.0]).tall(200.0);
    WidgetFrame::new("log")
        .sizes(hints)
        .show_area(ui, &mut state, |ui, area| {
            let rows = match area.height {
                Height::Min => 1,
                Height::Nom => 5,
                Height::Tall => 15,
            };
        });

    egui_tabular

    v0.2.0 · 41 downloads

    Fast table viewer and editor that only draws the visible rows. Data from vectors, files, databases or live bus traces.

    let mut table = VariantTable::new([ColumnDef::new("Name", VariantTy::Str)]);
    let mut view = TableView::new(TableViewOptions::default());
    let mut config = TableViewConfig::default();
    
    // Every frame:
    let output = view.show(ui, &mut table, &mut VariantCellUi, &mut config);
  3. Test bench

    Runs the jobs that use the boards.

    choreograph

    not on crates.io yet

    Plans and runs jobs over shared instruments and resources, with priorities and deadlines. Says why each waiting job waits.

    [[resource]]
    name = "supply"
    kind = "supply"
    states = ["off", "3V0", "3V3", "3V6"]
    safe = "off"
    
    [[job]]
    name = "a-3V3"
    needs = [{ kind = "probe", state = "fw-a" }, { kind = "supply", state = "3V3" }]
    run = "10s"
  4. API

    One definition for the device and the host, and the bytes between them.

    wire_weaver

    v0.4.0 · 866 downloads

    API description language and code generator: RPC, streams and properties, backwards and forwards compatible, no_std and no alloc.

    #[ww_trait]
    #[claim(exclusive)]
    pub trait Uart {
        #[counted]
        stream!(rx: RxChunk<'i>);
    
        #[credit]
        sink!(tx: MaxLen<RefVec<'i, u8>, UART_TX_MAX>);
    
        fn capabilities() -> Capabilities<'i>;
    }

    shrink_wrap

    v0.1.2 · 1,699 downloads

    Compact zero-copy wire format for microcontrollers. No allocator, dynamic types, packed down to the bit.

    let mut buf = [0u8; 256];
    let mut wr = BufWriter::new(&mut buf);
    wr.write_bool(true).unwrap();
    wr.write_u8(0xaa).unwrap();
    let bytes = wr.finish().unwrap();
    assert_eq!(bytes, &[0x80, 0xaa]); // the bool takes one bit

    exact_units

    not on crates.io yet

    Units of measure checked at compile time, with exact scales. The same bytes as the plain integer, no_std and no alloc.

    // exact or refused, never rounded through a float
    let t: Micros<u32> = Fixed::new(1500);
    assert_eq!(t.to::<Milli, u16>(), None); // 1500 µs is not a whole number of ms
    
    // zero cost: the type is the integer
    assert_eq!(size_of::<Micros<u32>>(), size_of::<u32>());
  5. Firmware

    Bare metal, next to the registers.

    embedded_bedrock

    not on crates.io yet

    Bare-metal firmware template with ruggedizing, debugging and logging built in. Optional embassy, RTIC and HAL support.

    use bedrock_led::code::{blink, Code};
    
    // A firmware that won't start says why on its status LED,
    // then waits in the ROM bootloader to be flashed.
    if !guard.runs_board() {
        blink(Code::UnknownRework, |on| led_pin_set(on), |ms| cortex_m::asm::delay(ms * BOOT_CLOCK_KHZ));
        reboot_into_rom_bootloader();
    }

    canzone

    renamed from mcan, not on crates.io yet

    Stand-alone Rust driver for the Bosch M_CAN controller on STM32 G0, G4, H5, H7 and L5. No panics, no blocking waits without a timeout.

    let mut can = unwrap!(can.into_config_mode());
    can.set_nominal_bit_timing(unwrap!(NominalBitTiming::new(1, 55, 8, 1)));
    unwrap!(can.set_layout(layout_fdcan1));
    can.set_automatic_bus_off_recovery(true);
    let mut can = unwrap!(can.into_normal());
    
    can.write_tx_buffer_pend(
        tx_buffers.idx1,
        TxFrameHeader::fd_brs(Id::Standard(unwrap!(StandardId::new(0x123)))),
        &[0xAA, 0xBB, 0xCC],
    );

All open-source projects

06Stay in the loop

Hear when Donguru launches

Donguru launches on Crowd Supply. Its sign-up list opens with the pre-launch page; until then, send a line and you hear first. draft

Contact

Questions about the boards, the software or a project of your own: hello@vhrd.tech.

Engineering services are rare right now. What I do and how I work