Skip to main content

Device discovery over Serial, USB, and TCP/IP

Project description

Dafydd

CI PyPI License Python Rust

Find your device, even when it isn't where it's supposed to be.

Dafydd is a Rust library with Python bindings (via PyO3) for two questions every embedded / instrument-control codebase asks:

  1. "I have a device of type X with serial number Y — where is it?" Sweep the relevant bus (USB / Serial / TCP), probe each endpoint, and return the one whose response matches your identifier.

  2. "My device was supposed to be at COM3 / VID:PID 1234:5678 / 192.168.1.50:5025. It's not. Find it anywhere else on the same bus." Try the expected location first with a short timeout. On failure, fall back to a probe-and-identify sweep — and return the actual location.

Same DeviceMatch shape comes back regardless of transport, so the calling code that consumes it (e.g. python-bus) stays uniform across USB, Serial, and TCP devices.

Why this exists

Most Python projects re-write this loop by hand: enumerate ports, open each one, send *IDN?, match the response substring, give up. dafydd does it in Rust with proper concurrency, pure-Rust ARP cache reads, and a unified result shape — typically ~100× faster than a hand-rolled asyncio sweep for the "find this device on the LAN" case (see benches/).

Library Serial probe-and-identify USB filter TCP /24 sweep
dafydd ✅ unified API ✅ unified API ✅ ~100 ms (priority + concurrency)
pyserial + manual loop hand-rolled n/a n/a
pyusb / nusb n/a ✅ enumerate only n/a
python-nmap n/a n/a needs nmap binary + root
scapy SYN scan n/a n/a ~30 s, pure Python

Installation

pip install dafydd

Build from source (requires Rust 1.95+):

uv sync
uv run maturin develop --release

Use case 1 — "find this device, I don't know where it is"

You know the type (USB VID/PID, or how it responds to *IDN?). dafydd sweeps and returns the match.

Serial

import dafydd

# Find a device that responds to *IDN? with "MyDevice" anywhere on the bus.
matches = dafydd.SerialDiscovery(
    probe_command=b"*IDN?\r\n",
    baud_rates=[9600, 115200],
    timeout_ms=500,
    response_filter=b"MyDevice",
    response_terminator=b"\r\n",
).discover()

for m in matches:
    print(f"{m.address} @ {m.baud_rate} baud: {m.response}")

USB

# Filter by vendor/product ID — descriptor enumeration only, no probing.
matches = dafydd.UsbDiscovery(vid=0x04D8, pid=0x000A).discover()
for m in matches:
    print(f"{m.address}  {m.info.get('product', '')}")

TCP

# Sweep the LAN for a device that answers to your probe with the right ID.
matches = dafydd.TcpDiscovery(
    port=5025,
    probe_command=b"*IDN?\n",
    response_filter=b"SN:ES123DFD3",
).discover()

for m in matches:
    print(m.address, m.info.get("mac"))   # info["mac"] populated when from ARP

Use case 2 — "it should be at X but it isn't; find it"

You have an expected location. Try it first; on failure, fall back to a full bus sweep filtered by probe response.

Serial — "should be COM3, but COM3 is empty"

m = dafydd.SerialDiscovery(
    probe_command=b"C0AMSF\r\n",
    response_filter=b"sn:ES123DFD3",
    preferred_port="COM3",          # try this first with a fast timeout
    preferred_retry=2,              # retry a couple of times
    baud_rates=[9600],
    timeout_ms=200,
).discover()
# m[0].address now points to wherever the device actually is — COM4, /dev/ttyUSB1, …

TCP — "should be 192.168.1.50:5025, but it's not"

m = dafydd.TcpDiscovery(
    port=5025,
    preferred_host="192.168.1.50",  # try first; if no response, sweep
    probe_command=b"*IDN?\n",
    response_filter=b"SN:ES123DFD3",
    subnet_prefix=24,               # how broad the fallback sweep goes
).discover()

Passing the result to your transport library

Every DeviceMatch produces kwargs ready for python-bus (or any serial/socket library) via .bus_params():

import bus  # python-bus

match = matches[0]
device = bus.Device(**match.bus_params())
device.write(b"some-command")

Streaming and Watch

import dafydd

token = dafydd.CancellationToken()

# Streaming — callback fires as each device is found.
dafydd.SerialDiscovery(
    probe_command=b"*IDN?\r\n",
    baud_rates=[9600],
    timeout_ms=500,
    cancellation_token=token,
).discover_streaming(lambda d: print("found", d.address))

# Watch — fires on plug/unplug. USB uses real OS hotplug; Serial and TCP poll.
import threading
threading.Thread(
    target=dafydd.UsbDiscovery(vid=0x04D8).watch,
    kwargs={
        "on_added":   lambda d: print("connected",    d.address),
        "on_removed": lambda d: print("disconnected", d.address),
    },
    daemon=True,
).start()

# Stop from any thread.
token.cancel()

Configuration knobs

TcpDiscovery

Knob Default Purpose
subnet_prefix 24 Broadest auto-detected subnet to sweep. Must be in [16, 32]. 16 allows /16 sweeps (65 k hosts, slow); 24 is the polite default.
tcp_linger_seconds None TCP SO_LINGER. None = OS default (graceful FIN close). 0 = RST close, no TIME_WAIT — fast but antisocial; use only on networks you own. n>0 = block for n seconds.
use_arp_cache True Probe IPs from the kernel ARP cache before the linear sweep. Pure-Rust on all platforms (no arp subprocess). When a match comes from ARP, its info["mac"] is populated.
use_mdns / use_ssdp False Active DNS-SD or SSDP M-SEARCH before the sweep. Useful for routers, cameras, printers.
preferred_host, preferred_retry None / 0 "Try here first" fast-path.

SerialDiscovery

Key knobs: preferred_port, probe_command, response_filter, response_terminator, baud_rates, port_filter (callback to exclude ports), include_bluetooth (Windows). See the docstring on the class for the full list.

UsbDiscovery

Filters: vid, pid, manufacturer, product, serial, class. All optional. Real OS hotplug via nusb::watch_devices().

Utilities

# Auto-detect local subnets — same list TcpDiscovery uses when `subnets=[]`.
print(dafydd.local_subnets())              # default /24 ceiling
print(dafydd.local_subnets(max_prefix=20)) # allow up to /20 (4096 hosts)

How it works

  1. Preferred-address path (when configured): try the named port / host / VID:PID with a short timeout. On success, return immediately.
  2. TCP only — priority probes: ARP cache (with MAC stamped on the match), then common last-octet heuristics (.1, .100, .254, …), then optional mDNS/SSDP responders.
  3. Full sweep: enumerate endpoints, probe concurrently via Tokio JoinSet + semaphore. With raw-socket privilege on Linux, a single-RTT SYN scan further narrows TCP targets to open ports.
  4. macOS tty dedup (Serial): the blocking /dev/tty.* variant of each port is filtered out so probes use /dev/cu.*.

Building from source

# Editable install
uv sync
uv run maturin develop

# Release build
uv run maturin develop --release

# Rust-only tests + benches
cargo test
cargo bench --bench tcp_scan       # see benches/README.md

Contributing

See CONTRIBUTING.md.

License

MIT — see LICENSE.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

dafydd-0.1.1.tar.gz (79.9 kB view details)

Uploaded Source

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

dafydd-0.1.1-cp313-cp313-win_amd64.whl (657.0 kB view details)

Uploaded CPython 3.13Windows x86-64

dafydd-0.1.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (1.1 MB view details)

Uploaded CPython 3.13manylinux: glibc 2.17+ x86-64

dafydd-0.1.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (1.1 MB view details)

Uploaded CPython 3.13manylinux: glibc 2.17+ ARM64

dafydd-0.1.1-cp313-cp313-macosx_11_0_arm64.whl (610.0 kB view details)

Uploaded CPython 3.13macOS 11.0+ ARM64

dafydd-0.1.1-cp312-cp312-win_amd64.whl (657.0 kB view details)

Uploaded CPython 3.12Windows x86-64

dafydd-0.1.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (1.1 MB view details)

Uploaded CPython 3.12manylinux: glibc 2.17+ x86-64

dafydd-0.1.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (1.1 MB view details)

Uploaded CPython 3.12manylinux: glibc 2.17+ ARM64

dafydd-0.1.1-cp312-cp312-macosx_11_0_arm64.whl (609.9 kB view details)

Uploaded CPython 3.12macOS 11.0+ ARM64

dafydd-0.1.1-cp311-cp311-win_amd64.whl (659.1 kB view details)

Uploaded CPython 3.11Windows x86-64

dafydd-0.1.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (1.1 MB view details)

Uploaded CPython 3.11manylinux: glibc 2.17+ x86-64

dafydd-0.1.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (1.1 MB view details)

Uploaded CPython 3.11manylinux: glibc 2.17+ ARM64

dafydd-0.1.1-cp311-cp311-macosx_11_0_arm64.whl (610.6 kB view details)

Uploaded CPython 3.11macOS 11.0+ ARM64

File details

Details for the file dafydd-0.1.1.tar.gz.

File metadata

  • Download URL: dafydd-0.1.1.tar.gz
  • Upload date:
  • Size: 79.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for dafydd-0.1.1.tar.gz
Algorithm Hash digest
SHA256 d5d58729ba2200ec002ad23b0ea2f70a7b378f32b76702dc77ca20393a5afa28
MD5 5d7334ca81df88e30b4e7a82039102b1
BLAKE2b-256 877750b272624a5e12293dc2271674e96fd8d605ad8dde089bfa4a8444b5de80

See more details on using hashes here.

Provenance

The following attestation bundles were made for dafydd-0.1.1.tar.gz:

Publisher: release.yml on tstenvold/dafydd

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file dafydd-0.1.1-cp313-cp313-win_amd64.whl.

File metadata

  • Download URL: dafydd-0.1.1-cp313-cp313-win_amd64.whl
  • Upload date:
  • Size: 657.0 kB
  • Tags: CPython 3.13, Windows x86-64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for dafydd-0.1.1-cp313-cp313-win_amd64.whl
Algorithm Hash digest
SHA256 2b059f0d23f1234f515f749202873325f2cd41858fd51d8344cbbfdaa6ab0294
MD5 78c8c2419aec02177ce970c32d8999e9
BLAKE2b-256 846784d3283d85589b84a5d1118b52bc65c80572a8d20fa3e44699e4678c0ec6

See more details on using hashes here.

Provenance

The following attestation bundles were made for dafydd-0.1.1-cp313-cp313-win_amd64.whl:

Publisher: release.yml on tstenvold/dafydd

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file dafydd-0.1.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for dafydd-0.1.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 8b777c766ca778c452817bb0e01318c7f274e9bb4854f3d1f568ec56fcb56f6a
MD5 aaabe01aab3e90fb6f07047939759956
BLAKE2b-256 6ac1dc4b46d67273335ff5a6c0a8f6d5235b78ad69466629f4b60802ca2a1f6f

See more details on using hashes here.

Provenance

The following attestation bundles were made for dafydd-0.1.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl:

Publisher: release.yml on tstenvold/dafydd

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file dafydd-0.1.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.

File metadata

File hashes

Hashes for dafydd-0.1.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 27363656e923b99577076c43a3e2a4fdb979364dc5fdfa5bf1bbf0b54dbd10f4
MD5 bd7bb338ec74c404d1029766eb6ed8ea
BLAKE2b-256 580a61028b15d0f5b33389131e1a64514e6fef78138b4f1925f1d05f3f64eaeb

See more details on using hashes here.

Provenance

The following attestation bundles were made for dafydd-0.1.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl:

Publisher: release.yml on tstenvold/dafydd

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file dafydd-0.1.1-cp313-cp313-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for dafydd-0.1.1-cp313-cp313-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 c274276494a4c50677b91bfb02fbd27b4824e52b718f975df2c2383de02026f7
MD5 43800ea5dee3d40738234ed21ba51342
BLAKE2b-256 326200ac7ec0f6c5ac93814d4e654f5d75cc1669ddd4fc4d20bb0d49b55b58cb

See more details on using hashes here.

Provenance

The following attestation bundles were made for dafydd-0.1.1-cp313-cp313-macosx_11_0_arm64.whl:

Publisher: release.yml on tstenvold/dafydd

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file dafydd-0.1.1-cp312-cp312-win_amd64.whl.

File metadata

  • Download URL: dafydd-0.1.1-cp312-cp312-win_amd64.whl
  • Upload date:
  • Size: 657.0 kB
  • Tags: CPython 3.12, Windows x86-64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for dafydd-0.1.1-cp312-cp312-win_amd64.whl
Algorithm Hash digest
SHA256 cd80f07af815be230be1f3445854c4ac277587e74ba79a842eecfd1e001ee3d7
MD5 3ed86a08e676bd4d066d34abe4d0f3f4
BLAKE2b-256 b6106808030b955e7c1cf6ed91711b8f3010f208e90f279ca8227d5c391a56f7

See more details on using hashes here.

Provenance

The following attestation bundles were made for dafydd-0.1.1-cp312-cp312-win_amd64.whl:

Publisher: release.yml on tstenvold/dafydd

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file dafydd-0.1.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for dafydd-0.1.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 454312c9a5f56f2d7f593793954fbb0914c81a07c63413eb94e62668c67309db
MD5 f69e451e2e95b7a78c9038aa9811a7fa
BLAKE2b-256 fdc046c5b789a4e719893ec664290f60eae853334e4a2de6a210f939e868a841

See more details on using hashes here.

Provenance

The following attestation bundles were made for dafydd-0.1.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl:

Publisher: release.yml on tstenvold/dafydd

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file dafydd-0.1.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.

File metadata

File hashes

Hashes for dafydd-0.1.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 c4a5b71fa9b716f319be7d79e75198bcc89249fac879f3288f4370086fde951c
MD5 c8b568bc74a40437246d5c92efe983d2
BLAKE2b-256 f1d71be0898811a657df68cca20511b0c2a8a9396dd51bb5fd06c7e125a30051

See more details on using hashes here.

Provenance

The following attestation bundles were made for dafydd-0.1.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl:

Publisher: release.yml on tstenvold/dafydd

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file dafydd-0.1.1-cp312-cp312-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for dafydd-0.1.1-cp312-cp312-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 908494bdf26d29ba21c8c0c5c472aa7eb4c0b46ef63be5b6eef231c6d64f02f7
MD5 e57e2c4ed18a27ed4fc79b5f5fde85f4
BLAKE2b-256 b4e2f4084c0fa2b33185b11b7c1a32a1daffed427da5ea7be11d177a9fd8d6b3

See more details on using hashes here.

Provenance

The following attestation bundles were made for dafydd-0.1.1-cp312-cp312-macosx_11_0_arm64.whl:

Publisher: release.yml on tstenvold/dafydd

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file dafydd-0.1.1-cp311-cp311-win_amd64.whl.

File metadata

  • Download URL: dafydd-0.1.1-cp311-cp311-win_amd64.whl
  • Upload date:
  • Size: 659.1 kB
  • Tags: CPython 3.11, Windows x86-64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for dafydd-0.1.1-cp311-cp311-win_amd64.whl
Algorithm Hash digest
SHA256 60bcdefca354a0c83652fe8799b392d913db0c5817272bbc517e46b03caf229f
MD5 4f87b9cebd4a8441e340f9e5940fae7a
BLAKE2b-256 75866e3d3378fed39943d9b0dede8fdc6377f1684f19340868310e2174ba82d7

See more details on using hashes here.

Provenance

The following attestation bundles were made for dafydd-0.1.1-cp311-cp311-win_amd64.whl:

Publisher: release.yml on tstenvold/dafydd

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file dafydd-0.1.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for dafydd-0.1.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 c620ba4ecc2e919416453fb19545dca1f5fbef00a814e881bcb2d4a7313c080c
MD5 106769850708253188961a4e46e04c5e
BLAKE2b-256 5d49f15a6bbbd417e3967e11edc723a53412b14c7d2f8dba285dfbc9fab92930

See more details on using hashes here.

Provenance

The following attestation bundles were made for dafydd-0.1.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl:

Publisher: release.yml on tstenvold/dafydd

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file dafydd-0.1.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.

File metadata

File hashes

Hashes for dafydd-0.1.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 2b26b9a17363c46ce5e6f27d7b0fa9e1bf00a4d47e6090340277b0c7eb8d6ea1
MD5 51e857f5c667f50666181991e0b43f3d
BLAKE2b-256 465dd99713c7e03767006d7b68f6b0ae4360fcb7ae71ad0b240b8f9eac016988

See more details on using hashes here.

Provenance

The following attestation bundles were made for dafydd-0.1.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl:

Publisher: release.yml on tstenvold/dafydd

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file dafydd-0.1.1-cp311-cp311-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for dafydd-0.1.1-cp311-cp311-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 4826107f4a341ecb90721c5442a42cead6e29b8910d44cae01c10f4ae37a1fc6
MD5 60e59062aeea58fb97875a3c6b4070b3
BLAKE2b-256 e302e38caababe7e2468defeeda55c2ecf6226bf92327458c8a8a1ed71ca8298

See more details on using hashes here.

Provenance

The following attestation bundles were made for dafydd-0.1.1-cp311-cp311-macosx_11_0_arm64.whl:

Publisher: release.yml on tstenvold/dafydd

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page