# DongLoRa Rust Client
[](https://crates.io/crates/donglora-client)
[](https://docs.rs/donglora-client)
Async Rust client library for talking to a DongLoRa device, either
directly over USB serial or through a mux daemon. Speaks DongLoRa
Protocol v2 via the [`donglora-protocol`][dp] crate.
[dp]: https://crates.io/crates/donglora-protocol
## Install
```toml
[dependencies]
donglora-client = "1"
tokio = { version = "1", features = ["rt-multi-thread", "macros"] }
```
## Quick Start
```rust
use donglora_client::connect;
use donglora_protocol::{LoRaBandwidth, LoRaCodingRate, LoRaConfig, Modulation};
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let mut dongle = connect(None, std::time::Duration::from_secs(5)).await?;
dongle.set_config(&Modulation::LoRa(LoRaConfig {
freq_hz: 910_525_000,
bw: LoRaBandwidth::Khz62,
sf: 7,
cr: LoRaCodingRate::Cr4_5,
sync_word: 0x1424,
tx_power_dbm: 20,
..Default::default()
})).await?;
dongle.start_rx().await?;
while let Some(rx) = dongle.next_rx().await? {
println!("RX rssi={} snr={} len={}", rx.rssi_tenths_dbm, rx.snr_tenths_db, rx.data.len());
}
Ok(())
}
```
All `connect` functions automatically validate the device by pinging
it, so you get a clear error immediately if the port is not a real
DongLoRa.
## What's in Here
- `src/dongle.rs`: high-level `Dongle` API (connect, set_config,
transmit, start_rx, next_rx, tx_with_retry).
- `src/session.rs`: reader task, tag-allocation, per-command response
routing, async event fan-out.
- `src/retry.rs`: `RetryPolicy` + randomized backoff for
`CHANNEL_BUSY` / `EBUSY` per `PROTOCOL.md §6.10`.
- `src/connect.rs`: auto-detection pipeline (TCP mux env var, unix
socket mux, direct USB serial).
- `src/transport.rs`: tokio transports (`tokio-serial` for USB,
`tokio::net::{UnixStream, TcpStream}` for mux).
- `src/discovery.rs`: USB VID:PID discovery + ping validation.
- `src/errors.rs`: `ClientError` / `ClientResult<T>` (thiserror-based
taxonomy mirroring the Python client's `DongloraError`).
## Connection Priority
`connect(None, timeout)` tries these in order:
1. **TCP mux**: via `DONGLORA_MUX_TCP` env var (host:port).
2. **Unix socket mux**: `$XDG_RUNTIME_DIR/donglora/mux.sock` or
`/tmp/donglora-mux.sock`.
3. **Direct USB serial**: auto-detects by VID:PID (CP210x, CH9102,
CH340, FT232R bridge chips too).
Once a mux path wins, subsequent reconnects commit to that path and
will not fall back to USB, preventing port-steal races during mux
restart.
`try_connect(timeout)` returns an error immediately if no device is
found; `connect` blocks until one appears.
## TX with Retry
```rust
use donglora_client::retry::RetryPolicy;
let policy = RetryPolicy::default(); // 3 attempts, 20-100 ms backoff, 500 ms cap
let outcome = dongle.tx_with_retry(payload, &policy).await?;
println!("sent in {} attempt(s)", outcome.attempts.len());
```
## Keepalive
A background task pings the device every 500 ms when the host has
been idle, tracking the 1 s inactivity window from `PROTOCOL.md §3.4`
with a 2x safety margin. Disable via `ConnectOptions::keepalive(false)`
if you want to manage liveness yourself.
## License
MIT, see [`LICENSE`](LICENSE).