Command line tool¶
ww is the WireWeaver command line tool. It finds connected devices and lists them with their API, prints a
device's API as a resource tree using its introspection data (or an API crate's tree, from source), observes
properties, asks any device what it is and what it counted, and tests the link.
Installation¶
Install from git with cargo (the binary ends up in ~/.cargo/bin):
From a local checkout of the repository, just install builds ww with the deploy profile and installs
it. It works anywhere: with tpm it goes into the kept-releases stack in ~/.local/bin (the only ww on PATH),
otherwise through cargo install --locked --path wire_weaver_cli. just deploy pulls main first and checks
ww --version.
Inside the repository it can also be run without installing, with cargo ww <args> (alias from
.cargo/config.toml). just install (also just install-cli) installs the current checkout as above, which is handy when testing changes to the tool itself.
Linux permissions
Listing devices works without any setup, but opening one (introspect, info, ...) needs read/write access
to /dev/bus/usb/..., usually granted to your user with a udev rule. See USB.
Shell completions¶
Completions are produced by ww itself, so they always match the installed version. Values of the device
selection flags (--serial, --label, --api, --usb-path, ...) are completed from the devices connected right
now, with the product and label of each shown as a hint. Register them once in the shell's startup file:
echo 'source <(COMPLETE=bash ww)' >> ~/.bashrc # bash
echo 'source <(COMPLETE=zsh ww)' >> ~/.zshrc # zsh
echo 'COMPLETE=fish ww | source' >> ~/.config/fish/config.fish # fish
Elvish and PowerShell are supported as well (COMPLETE=elvish, COMPLETE=powershell). In bash, values containing
spaces or quotes are inserted quoted, and values after @ or : complete too (--api blinky_api@<TAB>,
--vid-pid c0de:<TAB>).
First steps¶
Plug in a device and look at what is there:
$ ww list
LOCATION PRODUCT SERIAL API HASH LABEL
usb 003-1 c0de:cafe WireWeaver Generic 09000F000251313438333634 all_gpio_api@0.1.0 5ff989a95162d4d7
ww list reads the USB descriptors only, without opening the device. API is the API crate the firmware
implements. With one device connected, every other command uses it; with several, pick one with a
device selection flag such as --serial 0900.
Next, the device's API as a resource tree, from the introspection data it carries (-d hides doc comments):
$ ww introspect -d
trait AllGpioApi all_gpio_api@0.1.0
└─ 0 impl port[]: ww_gpio::Bank
├─ 0 impl pin[]: ww_gpio::Pin
│ ├─ 0 fn set_output_level(level: Level)
│ ├─ 1 fn toggle()
...
From there:
ww info: what the device is (firmware, board, ids, label, last reset), on any device implementing the device contract.ww observe <path>: watch a trait level's observed properties change, no generated code needed. For calls and streams from a script, use Python.ww usb-loopback: check the link itself, round trips and throughput, whatever the API.ww api tree <path>andww api scaffold <path>: the same tree from an API crate's source, and the server handlers to implement for it.
Commands¶
| Command | Needs a device | What it does |
|---|---|---|
ww list |
no (not opened) | Lists connected devices matching the device selection |
ww introspect |
yes | Prints the device's resource tree from its introspection data |
ww api |
no | From an API crate's source: resource tree, AST, server scaffold, snapshots (save, check, diff) |
ww observe |
yes | Observes a trait level's +observe properties: values, rate, link bytes/s |
ww usb-loopback |
yes, any API | Runs link loopback and throughput tests |
ww dfu |
yes, any API | Firmware updates through ww_dfu traits: reboot, status, confirm, revert; flash a chunky device |
ww dfu rp |
a boot ROM | RP2040 / RP2350 in BOOTSEL mode: list, flash, reboot, follow back |
ww dfu stm32 |
a boot ROM | STM32 system bootloader in DFU mode: list, layout, flash, read, erase, follow back |
ww counters |
yes, any API | The device's cnt counters, decoded with its ELF: read, watch, TUI, reset |
ww info |
yes, any API | What the device is: firmware, board, ids, label, last reset |
ww report |
yes, any API | A bug report in one file: info, counters, stored log, crash, decoded with the ELF |
ww pins |
yes, with PinDiag |
Pin tests: shorts to GND, the supply and each other, pulls; plan files |
ww elf |
no | The ELF directory: ww elf add a release archive, ww elf id |
ww settings |
no | Decodes a ww_settings::Store flash dump |
ww config |
no | Shows or saves the device selection in a project ww.toml |
ww help <command> or ww <command> --help prints all options of a command; the
command reference explains each one with examples.
Device selection¶
Commands working with a device (introspect, observe, info, ...) connect to the only device matching the selection,
and ww list shows all the devices it matches. With no selection, the only connected device reporting a WireWeaver
API id is used; if several match, they are all printed and nothing is opened, run ww list to see them and narrow
the selection.
| Flag | Env variable | ww.toml key |
Matches |
|---|---|---|---|
-s, --serial |
WW_SERIAL |
serial |
Serial number containing the value |
-l, --label |
WW_LABEL |
label |
User label, whole |
-p, --product |
WW_PRODUCT |
product |
Product description containing the value |
--manufacturer |
WW_MANUFACTURER |
manufacturer |
Manufacturer containing the value |
--api |
WW_API |
api |
Implemented API: name[@req], e.g. blinky_api@^0.1 |
--vid-pid |
WW_VID_PID |
vid_pid |
USB VID:PID in hex, e.g. c0de:cafe |
--usb-path |
WW_USB_PATH |
usb_path |
USB bus and port chain as shown by ww list, e.g. 003-1.2 |
--rtt |
WW_RTT |
rtt |
Connect over RTT through a debug probe: the probe-rs chip name, e.g. STM32G0B1RETx, see below |
--probe |
WW_PROBE |
probe |
With --rtt, the probe: serial containing the value, VID:PID in hex or VID:PID:SERIAL |
--elf |
WW_ELF |
elf |
The firmware ELF file: with --rtt the RTT control block address is taken from it instead of scanning RAM; counters and report decode with it when given on the command line |
--probe-speed-khz |
WW_PROBE_SPEED_KHZ |
probe_speed_khz |
With --rtt, the SWD clock in kHz, not a filter (the probe's default without it) |
--timeout-ms |
WW_TIMEOUT_MS |
timeout_ms |
Request timeout in milliseconds, not a filter |
--link-setup-timeout-ms |
WW_LINK_SETUP_TIMEOUT_MS |
link_setup_timeout_ms |
How long to wait for the device to answer when connecting, in milliseconds, not a filter. Default: USB 1000, RTT 10000 |
- All text matching ignores case.
- The version requirement in
--apiuses Cargo's SemVer syntax; without it, any version matches. --usb-pathpins a physical port, so it keeps selecting the same device across re-enumeration, and tells apart identical devices without serial numbers.- The flags are global: they can be given before or after the subcommand (
ww --serial 2100 listandww list --serial 2100are the same).
Settings of different kinds are combined, so for example api from ww.toml and --serial on the command line
narrow the selection together. Each setting is taken from the first place that has it:
command line flag > environment variable > ww.toml.
Through a debug probe (RTT)¶
A board with no USB of its own (a Nucleo-64 has only its ST-LINK) is reached over RTT through the debug probe, with
probe-rs: --rtt CHIP connects every command (info, counters, sync, pins, dfu reboot,
observe, report, ...) through it instead of USB. The firmware serves WireWeaver on the RTT channels ww_up and
ww_down (see RTT); a running firmware is attached to as it is, without a reset.
ww --rtt STM32G0B1RETx info # the only probe
ww --rtt STM32G0B1RETx --probe 066D counters read # one of several, by a part of its serial
ww --rtt STM32G0B1RETx --elf target/thumbv6m-none-eabi/debug/fw sync # control block from the ELF, no RAM scan
ww list --probes # the probes, and who holds each
- Without
--elfthe RAM is scanned for the RTT control block (about 1.5 s for 144 KiB through an ST-LINK); with it the address is read from the_SEGGER_RTTsymbol. A firmware that doesn't match the file is still found by a scan, with a warning. - The USB filters (
--serial,--label,--product,--manufacturer,--vid-pid,--usb-path) describe a USB device and are refused next to--rttfrom the same or a stronger source; fromww.tomlunder a--rttflag they are left out (wwnames them in its messages).--apiis checked on the device once connected. --probeand--probe-speed-khzwithout--rttare an error.- A probe is held by one program at a time. When another WireWeaver program has it, the error names it (
held by g0b1re, pid 4242, ...from its lock file); a program that writes no lock file (probe-rs run, OpenOCD) is named on Linux from/proc. With a ww hub holding it (ww hub take rtt:<probe serial>:<chip>), several programs share the probe. - Shell completion offers the chip names (from what is typed so far) and the serials of the connected probes.
A board project keeps it in ww.toml; a relative elf path there is relative to the file:
Project config: ww.toml¶
Device selection that stays the same for a project goes into the [device] table of a ww.toml, using the keys
from the table above:
ww.toml is looked up in the current directory and then its parents, so one file at the project root covers the
whole project. --config <path> (or WW_CONFIG) uses another file, --no-config ignores it. Unknown keys are
reported as errors rather than ignored. ww config show prints the config file in use, then every setting with its
resolved value and where it came from (flag, env variable or file), so it answers "why is this device (not)
selected".
See also¶
- Command reference: every command and option
- USB: how devices report their API id and user label, and how the host finds them
- ww_self: introspection data format