# wireband-edge
Lightweight [Wire.Band](https://wire.band) client for IoT gateway hardware — Raspberry Pi, NVIDIA Jetson, industrial PCs.
Collects telemetry from MQTT, serial ports, BLE, Modbus, and CoAP, frames it with a 2-byte theta-symbol prefix, and flushes batches to the Wire.Band backend over HTTPS (rustls — no OpenSSL dependency).
## Quick start
```bash
# MQTT gateway (default feature)
wireband-edge \
--broker mqtt://localhost:1883 \
--topics "sensors/#" \
--backend https://ingest.wire.band \
--api-key YOUR_API_KEY \
--device-id factory-rpi4
```
## Installation
```bash
cargo add wireband-edge
# All protocol connectors + agent
cargo add wireband-edge --features "mqtt,serial,ble,coap,modbus,agent"
```
## Features
| `mqtt` (default) | MQTT connector via `rumqttc` |
| `serial` | UART/RS-232/RS-485 via `tokio-serial` |
| `ble` | Bluetooth LE GATT notifications via `btleplug` |
| `modbus` | Modbus TCP register poller via `tokio-modbus` |
| `coap` | CoAP UDP server for constrained sensors via `coap-lite` |
| `agent` | Device twin, OTA updates, hardware watchdog |
| `infer-onnx` | Edge inference via ONNX Runtime |
| `infer-tflite` | Edge inference via TensorFlow Lite |
| `python` | PyO3 bindings (built via maturin) |
## Library usage
```rust
use wireband_edge::{WireBandClient, ClientConfig};
use wireband_edge::mqtt::MqttConnector;
#[tokio::main]
async fn main() -> wireband_edge::Result<()> {
let client = WireBandClient::new(ClientConfig {
backend_url: "https://ingest.wire.band".into(),
api_key: Some("YOUR_API_KEY".into()),
device_id: "factory-rpi4".into(),
..Default::default()
});
client.start();
MqttConnector::new("mqtt://localhost:1883")
.run(client.clone(), &["sensors/#"])
.await
}
```
## Protocol connectors
### BLE (Bluetooth Low Energy)
```rust
use wireband_edge::ble::BleConnector;
use std::time::Duration;
BleConnector::new("temp-sensor", "6e400003-b5a3-f393-e0a9-e50e24dcca9e")?
.scan_duration(Duration::from_secs(10))
.topic_prefix("factory/ble")
.delta_threshold(0.01)
.run(client)
.await?;
```
### Serial (UART / RS-232 / RS-485)
```rust
use wireband_edge::serial::SerialConnector;
SerialConnector::new("/dev/ttyUSB0", 115_200)
.topic_prefix("factory/line1")
.delta_threshold(0.01)
.run(client)
.await?;
```
### CoAP (constrained sensors)
```rust
use wireband_edge::coap::CoapServer;
// Sensors PUT to coap://gateway-ip/sensors/zone-a/temp
CoapServer::new("0.0.0.0:5683")
.topic_prefix("factory/coap")
.run(client)
.await?;
```
### Modbus TCP
```rust
use wireband_edge::modbus::{ModbusPoller, RegisterDef};
use std::time::Duration;
ModbusPoller::new("192.168.1.100:502")
.slave(1)
.poll_interval(Duration::from_secs(5))
.topic_prefix("factory/plc")
.registers(vec![
RegisterDef::new(0x0100, 1, "motor_rpm", 1.0),
RegisterDef::new(0x0101, 1, "motor_temp", 0.1), // 0.1 °C per LSB
RegisterDef::new(0x0102, 2, "energy_kwh", 0.001),
])
.run(client)
.await?;
```
## Agent (device twin, OTA, watchdog)
```rust
use wireband_edge::agent::{DeviceTwin, OtaManager, OtaUpdate, Watchdog};
use std::time::Duration;
// Device twin — sync reported/desired state
let twin = DeviceTwin::new("factory-rpi4");
twin.update_reported(serde_json::json!({"firmware": "1.2.3"})
.as_object().unwrap().clone()).await;
twin.sync(&client).await;
// OTA firmware update
OtaManager::new("/tmp/ota-staging")
.run(OtaUpdate {
url: "https://releases.example.com/fw-1.3.0.bin".into(),
target_path: "/usr/local/bin/sensor-daemon".into(),
expected_sha256: Some("abc123...".into()),
version: "1.3.0".into(),
}, &client)
.await?;
// Watchdog — kicks /dev/watchdog + emits heartbeat
tokio::spawn(Watchdog::new(Duration::from_secs(30)).run(client.clone()));
```
## Edge inference
### ONNX Runtime
```rust
use wireband_edge::infer_onnx::OnnxInference;
let model = OnnxInference::from_file("anomaly_detector.onnx", "input", "output")?;
// In your sensor loop:
let scores = model.run(&sensor_readings, &[1, 16], "zone-a/anomaly", &client).await?;
if scores[0] > 0.85 {
tracing::warn!("Anomaly detected: score={:.2}", scores[0]);
}
```
### TensorFlow Lite
```rust
use wireband_edge::infer_tflite::TfliteInference;
let model = TfliteInference::from_file("anomaly.tflite")?;
let scores = model.run(&sensor_readings, "zone-a/anomaly", &client).await?;
```
## Python extension
Install the Rust-backed Python extension via maturin:
```bash
pip install maturin
cd edge-rs
maturin develop --features python
```
```python
from wireband_edge_rs import WireBandClient
client = WireBandClient(
backend_url="https://ingest.wire.band",
device_id="factory-rpi4",
api_key="YOUR_API_KEY",
)
client.start()
client.buffer_event("sensors/temp", 0xFC62, '{"value": 23.5}')
print(client.stats())
```
## Cross-compilation
Pre-built binaries for common targets are available on the [GitHub releases page](https://github.com/maco144/wireband/releases).
To cross-compile yourself:
```bash
cargo install cross
cargo build-rpi4 # aarch64 (RPi 4, Jetson)
cargo build-rpi3 # armv7 (RPi 3, Zero 2)
cargo build-rpi0 # arm (RPi Zero, RPi 1)
cargo build-x86 # x86_64 Linux
```
## License
MIT