Skip to main content

Crate hick_smoltcp

Crate hick_smoltcp 

Source
Expand description

hick-smoltcp

Runtime-agnostic mDNS / DNS-SD engine for bare metal — responder and querier over smoltcp UDP, no_std + alloc, no OS and no async runtime required.

github LoC Build codecov

docs.rs crates.io crates.io license

§Introduction

hick-smoltcp drives the Sans-I/O core (mdns-proto) over a smoltcp UDP socket through a small UdpIo transport seam. It owns no executor and performs no blocking: you pump it from your own poll loop alongside the smoltcp Interface, and it tells you when to wake next. This is the shared engine the hick-embassy driver builds on — but it is usable standalone with any smoltcp stack.

no_std + alloc: the protocol state lives in slab-backed pools, so there is no per-packet heap traffic on the hot path.

§Installation

[dependencies]
hick-smoltcp = "0.1"

§Example

use hick_smoltcp::{DualStack, Engine};
use mdns_proto::{EndpointConfig, Name, ServiceRecords, ServiceSpec};

// The engine wires mdns-proto's alloc-tier state machines to any `UdpIo`. For
// smoltcp, use the family-aware `DualStack` over your v4 and/or v6 socket handles.
let mut engine = Engine::new(EndpointConfig::new(), rng);

let mut records = ServiceRecords::new(
    Name::try_from_str("_http._tcp.local.")?,
    Name::try_from_str("My Device._http._tcp.local.")?,
    Name::try_from_str("mydevice.local.")?,
    80,
    120,
);
records.add_a(my_ipv4);
engine.register_service(ServiceSpec::new(records), now)?;

// Pump from your own loop, handing it a `DualStack` view of your sockets and a
// scratch buffer. The engine fans each multicast to BOTH groups, so `DualStack`
// routes by family (leave a family `None` for a single-stack node). `pump` returns
// the next instant the engine wants to be polled.
loop {
    let mut io = DualStack::new(&mut sockets, Some(v4_handle), Some(v6_handle));
    let next = engine.pump(now, &mut io, &mut scratch);
    // ...advance the smoltcp interface (iface.poll(.., &mut sockets)), then sleep
    // until `next` or RX-ready...
}

§Feature flags

FeatureDescription
atomic (default)The native-atomic alloc tier: SmolStr names + Bytes rdata. Needs pointer-width atomics.
no-atomicFor cores without native atomic CAS (Cortex-M0+ / thumbv6m / RP2040): swaps the refcounted Name / rdata onto portable-atomic’s Arc (clone via a critical-section impl your binary provides), keeping the alloc-backed pools. Build with --no-default-features --features no-atomic.
tracingForward structured tracing events from mdns-proto (requires a std subscriber; a no-op on bare-metal without one).
statsEnable no_std-safe atomic counters; read a snapshot via engine.stats(). Implies atomic.
defmtEmit defmt log events — the idiomatic choice for bare-metal targets.

§Observability

For bare-metal targets the recommended logging path is defmt combined with stats:

hick-smoltcp = { version = "0.1", features = ["defmt", "stats"] }

Enable defmt to have the engine emit structured log events through the defmt framework at the level your firmware configures.

Enable stats and call engine.stats() to obtain a hick_trace::stats::StatsSnapshot:

let snap = engine.stats();
// snap.packets_rx, snap.cache_size, etc.

The tracing feature is available but is a no-op in bare-metal builds that do not set up a tracing subscriber.

The metrics feature (bridging counters to the metrics facade) requires std and is not available on bare-metal targets. Use stats + defmt instead for observability on embedded.

§Design

  • no_std + alloc, panic-free hot path: slab-backed protocol pools, no per-packet allocation.
  • UdpIo transport seam — a two-method, non-blocking trait (try_recv / try_send). The family-aware DualStack implements it over smoltcp UDP sockets (routing each multicast to the socket of its own family); bring your own and the engine is transport-agnostic.
  • RFC 6762 §11 on-link gate — uses the received hop-limit (== 255) when the transport surfaces it, else falls back to a source-subnet membership check.
  • RFC 6762 §10.1 goodbye — unregistering an announced service emits a TTL=0 goodbye, bursted a few times by the pump.

Pinned to a single smoltcp 0.13, unified with the version hick-embassy’s embassy-net pulls in.

§The hick family

hick (facade) · mdns-proto (Sans-I/O core) · hick-udp (UDP) · hick-reactor (tokio / smol driver) · hick-compio (compio driver) · hick-smoltcp (this crate) · hick-embassy (embassy driver).

§License

hick-smoltcp is under the terms of both the MIT license and the Apache License (Version 2.0).

See LICENSE-APACHE, LICENSE-MIT for details.

Copyright (c) 2025 Al Liu.

Re-exports§

pub use engine::Engine;
pub use time::SmoltcpInstant;
pub use udpio::RecvMeta;
pub use udpio::SendError;
pub use udpio::UdpIo;

Modules§

constants
mDNS multicast endpoints (RFC 6762 §3).
engine
The runtime-agnostic mDNS engine: a synchronous pump that drives the mdns_proto::Endpoint (plus the per-service / per-query state machines it hands back) over a UdpIo transport.
time
Bridge smoltcp’s monotonic clock to the mdns_proto::Instant trait.
udpio
The UdpIo transport seam between the engine and a concrete socket layer.

Structs§

DualStack
A family-aware UdpIo over a v4 and/or v6 smoltcp UDP socket living in a shared SocketSet — the smoltcp transport (single- OR dual-stack), mirroring hick-embassy’s DualUdp.