RadioClient

The main entry point to the Meshtastic SDK.

A RadioClient manages a single radio device over a chosen transport (BLE, TCP, serial, etc.) and exposes a clean async/reactive API:

Drop-in philosophy: configure once with Builder, then observe and call:

val client = RadioClient.Builder()
.transport(TcpTransport(host = "192.168.1.42", port = 4403))
.storage(MyStorageProvider())
.build()

client.connect() // suspends until Connected or throws
client.sendText("Hello mesh!") // non-suspending; returns a MessageHandle

client.packets.collect { packet -> // reactive inbound stream
println(packet.decoded?.portnum)
}

client.disconnect() // graceful shutdown

Multi-radio: instantiate one RadioClient per physical radio. They share nothing.

No host plumbing: foreground services, permissions, notifications, and WorkManager are the app's responsibility. The SDK owns only the engine.

Resource management: implements AutoCloseable, so client.use { … } guarantees cleanup even on exception. Prefer disconnect() (suspend) over close() (blocking) when inside a coroutine.

Since

0.1.0

Types

Link copied to clipboard
class Builder

DSL builder for RadioClient.

Link copied to clipboard
object Companion

Properties

Link copied to clipboard

Admin API for device configuration and control. Available while connected.

Link copied to clipboard
val configBundle: StateFlow<ConfigBundle?>

Most recently committed ConfigBundle for the active session.

Link copied to clipboard
val connection: StateFlow<ConnectionState>

The current connection state.

Link copied to clipboard
val events: Flow<MeshEvent>

Side-channel advisory events: queue status, transport errors, key-verification prompts, drop notifications, and identity-rebind signals (MeshEvent.IdentityRebound — emitted before the engine clears storage when the device reports a different NodeNum than the one previously persisted).

Link copied to clipboard
val nodes: Flow<NodeChange>

Node-change deltas. Late subscribers receive a NodeChange.Snapshot immediately (single-replay), then live NodeChange.Added / NodeChange.Updated / NodeChange.Removed in causal order.

Link copied to clipboard
val ownNode: StateFlow<NodeInfo?>

The local node's NodeInfo, populated after handshake completes.

Link copied to clipboard
val packets: Flow<MeshPacket>

Inbound decoded packets.

Link copied to clipboard

Routing API for traceRoute and neighbor enumeration. Available while connected.

Link copied to clipboard

Telemetry API for device and environment metrics. Available while connected.

Functions

Link copied to clipboard
suspend fun RadioClient.awaitInitialNodeDb(): Map<NodeId, NodeInfo>

Suspend until the device finishes streaming its NodeDB during handshake, then return the current node-snapshot map.

Link copied to clipboard
open override fun close()

Blocking close for AutoCloseable conformance.

Link copied to clipboard
suspend fun connect()

Initiate connection to the device.

Link copied to clipboard
suspend fun RadioClient.connectAndAwaitReady(timeout: Duration = 30.seconds): ConfigBundle

Connect and suspend until the handshake settles, returning the resolved ConfigBundle (HLP-34).

Link copied to clipboard
suspend fun disconnect()

Gracefully disconnect from the device.

Link copied to clipboard
suspend fun nodeSnapshot(): Map<NodeId, NodeInfo>

Pull a snapshot of all known nodes on demand.

Link copied to clipboard
fun RadioClient.requestPosition(from: NodeId, channel: ChannelIndex = ChannelIndex(0)): MessageHandle

Request the current Position from node from.

Link copied to clipboard
fun send(packet: MeshPacket): MessageHandle

Enqueue an outbound packet for transmission.

suspend fun send(portnum: PortNum, payload: ByteArray, to: NodeId = NodeId.BROADCAST, channel: ChannelIndex = ChannelIndex(0), wantAck: Boolean = false, hopLimit: Int? = null): MessageHandle

Convenience: build a MeshPacket for the given portnum with payload and enqueue it.

suspend fun send(portnum: PortNum, payload: Buffer, to: NodeId = NodeId.BROADCAST, channel: ChannelIndex = ChannelIndex(0), wantAck: Boolean = false, hopLimit: Int? = null): MessageHandle

Convenience: build a MeshPacket for the given portnum from a kotlinx.io.Buffer payload.

Link copied to clipboard
suspend fun RadioClient.send(block: SendBuilder.() -> Unit): MessageHandle

DSL form of RadioClient.send. Builds a MeshPacket via SendBuilder and enqueues it.

Link copied to clipboard
suspend fun RadioClient.sendDirectMessage(to: NodeId, text: String, channel: ChannelIndex = ChannelIndex(0), wantAck: Boolean = true): MessageHandle

Send a unicast text message to to. Defaults wantAck to true.

Link copied to clipboard

Send a PKI-encrypted unicast text message to to.

Link copied to clipboard
suspend fun RadioClient.sendPosition(latLng: LatLng, to: NodeId = NodeId.BROADCAST, channel: ChannelIndex = ChannelIndex(0), wantAck: Boolean = false): MessageHandle

Send a Position payload to to (defaults to broadcast).

Link copied to clipboard
fun sendText(text: String, channel: ChannelIndex = ChannelIndex(0), to: NodeId = NodeId.BROADCAST): MessageHandle

Convenience: send a text message.