chore: release 1.0.0

This commit is contained in:
swaits committed 2026-04-22 09:45:43 -06:00
1 parent 6937ad47ba
commit c43a78f8bb
4 files changed
+84 -35

No files matched your search

+1 -1
View File
@@ -1,6 +1,6 @@
# Changelog
## 1.0.0-alpha.1 — 2026-04-21
## 1.0.0 — 2026-04-22
### Breaking
Generated
+4 -2
View File
@@ -71,7 +71,7 @@ checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
[[package]]
name = "donglora-client"
version = "1.0.0-alpha.1"
version = "1.0.0"
dependencies = [
"anyhow",
"donglora-protocol",
@@ -87,7 +87,9 @@ dependencies = [
[[package]]
name = "donglora-protocol"
version = "0.1.0"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c8f2743ac7668845500e3686890917cdf5b0a09d17ec099bde2db4d3fae2a4a4"
dependencies = [
"heapless",
"ucobs",
+3 -3
View File
@@ -1,6 +1,6 @@
[package]
name = "donglora-client"
version = "1.0.0-alpha.1"
version = "1.0.0"
edition = "2024"
rust-version = "1.85"
authors = ["Stephen Waits <steve@waits.net>"]
@@ -13,7 +13,7 @@ categories = ["hardware-support"]
exclude = ["CLAUDE_TODO.md", "deny.toml", "justfile", "mise.toml", "rustfmt.toml", ".jj"]
[dependencies]
donglora-protocol = { path = "../protocol", default-features = false, features = ["std"] }
donglora-protocol = { version = "1.0.0", default-features = false, features = ["std"] }
thiserror = "2"
tokio = { version = "1", features = ["rt", "rt-multi-thread", "sync", "time", "macros", "io-util", "net"] }
tokio-serial = "5"
@@ -27,7 +27,7 @@ anyhow = "1"
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
# Direct access to raw wire primitives for tests (frame encoder/decoder
# helpers that are not part of the donglora-client public API).
donglora-protocol = { path = "../protocol", default-features = false, features = ["std"] }
donglora-protocol = { version = "1.0.0", default-features = false, features = ["std"] }
heapless = "0.9"
[[example]]
+76 -29
View File
@@ -1,57 +1,104 @@
# DongLoRa Rust Client
Rust client library for talking to a DongLoRa device — either directly
over USB or through a mux daemon.
[![Crates.io](https://img.shields.io/crates/v/donglora-client.svg)](https://crates.io/crates/donglora-client)
[![Docs.rs](https://docs.rs/donglora-client/badge.svg)](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 = "0.2"
donglora-client = "1"
tokio = { version = "1", features = ["rt-multi-thread", "macros"] }
```
## Quick Start
```rust
use donglora_client::*;
use donglora_client::connect;
use donglora_protocol::{LoRaBandwidth, LoRaCodingRate, LoRaConfig, Modulation};
let mut client = connect_default()?;
client.set_config(RadioConfig::default())?;
client.start_rx()?;
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let mut dongle = connect(None, std::time::Duration::from_secs(5)).await?;
loop {
if let Some(Response::RxPacket { rssi, snr, payload }) = client.recv()? {
println!("RX rssi={rssi} snr={snr} len={}", payload.len());
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'll get a clear error immediately if the port isn't a real DongLoRa.
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/protocol.rs` — wire protocol types (`RadioConfig`, `Command`, `Response`, `ErrorCode`)
- `src/codec.rs` — COBS framing, frame accumulator
- `src/discovery.rs` — USB VID:PID device discovery
- `src/transport.rs` — serial and mux socket transports
- `src/client.rs` — high-level `Client<T>` with send/recv
- `src/connect.rs` — auto-detection (mux socket, TCP, direct USB)
- `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`] and [`try_connect`] try these in order:
`connect(None, timeout)` tries these in order:
1. **TCP mux** — via `DONGLORA_MUX_TCP` env var
2. **Unix socket mux** — checks `$XDG_RUNTIME_DIR/donglora/mux.sock` or `/tmp/donglora-mux.sock`
3. **Direct USB serial** — auto-detects by VID:PID
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).
`try_connect` returns an error immediately if no device is found.
`connect` blocks until a USB device appears.
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.
## Dependencies
`try_connect(timeout)` returns an error immediately if no device is
found; `connect` blocks until one appears.
- `ucobs` — COBS framing (same implementation as the firmware)
- `serialport` — USB serial communication
- `anyhow` — error handling
- `tracing` — logging
## 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).