### In The Wild with 129 Active Installs
FREE RAG Converter Online -- <a href="https://RAGconverter.com">RAGconverter.com</a>
# rusty_esp_signal
[](https://github.com/remade-with-rust) [](https://www.mata.network) [](https://crates.io/crates/rusty_esp_signal) [](https://docs.rs/rusty_esp_signal) [](https://github.com/Remade-With-Rust/rusty_esp_signal/blob/main/LICENSE-MIT)
Radios and sensing for the **Janus** ESP32 family: Wi-Fi station policy,
provisioning a device that has no network yet, a peer-to-peer link for chips
with no access point, long-range radio, presence from channel state, and a
millimetre-wave radar. Pure Rust, no C, no FFI, `no_std` by default.
* **A device with no network can still be given one.** A browser hands an
ESP32 its network over Bluetooth, on a page with no app and no server,
inside an encrypted session that a setup code unlocks (SPAKE2+, the device
proving its `did:mata`): the passphrase never crosses the air in the clear
and never reaches a server, a log, or a command line. **Verified from Chrome
on an ESP32-S3 and an ESP32-CAM**: a whole session costs the device **0.27 s
on the S3 and 0.82 s on the original ESP32**, and the board joins in about a
second.
* **Presence from the Wi-Fi channel itself**, judged against a labelled public
capture rather than our own recording: an empty room reads 17.4% occupancy
and a walking person 86.1%, with a held-out pair at 60.8%.
* **A link where there is no access point.** Peer-to-peer framing sized to fit:
227 bytes of payload inside a 250-byte datagram, with hello, accept and
confirm at 84, 100 and 18 bytes.
* **The radar's own documentation is wrong, and the tests say where.** Field
offsets in the vendor datasheet do not match the vendor's tool; ours follow
the wire.
## What has run on hardware
| what | measured |
|---|---|
| the setup session over Bluetooth, from Chrome (XIAO ESP32-S3, trouble-host) | the device's time **0.27 s** a session (Start 260 ms); joined in **995 ms**, restarted without Bluetooth, the camera page on its token |
| the same on the ESP32-CAM (Bluedroid) | **0.82 s** a session (Start 695-790 ms, the portable P-256 path); a mistyped passphrase offers setup again; the page 200 on its token, 403 without |
| refusals, from an independent central (`tools/setup_central.py`) | a wrong code, another device, a replayed session and a second writer refused; five wrong codes lock the window, and on the next power-on one more guess |
| provisioning over Bluetooth (the unencrypted version, now retired) | **joined in 7.5 s**; the reboot joins alone in **9.5 s** with no Bluetooth |
| the identity behind it | the same `did:mata` held across six reflashes |
| the advertisement budget | 31 bytes, which is what forced the name and service layout |
| a failed join | must not spend the modem — a device that cannot join has to stay askable |
| a station on the open MAC (`rusty_esp_signal-open`, XIAO ESP32-S3) | a WPA2 join, DHCP, 100/100 pings, the camera page and 1,500 stream frames; heap at radio start 53 KB on the blob, 0.1 KB here |
| an access point on the open MAC | a laptop joined as 802.11n (65 Mbit/s), through power save, group-key rotation and re-association; the camera cell hosting it took a signed 538 KB update over its own network |
| the link on raw frames on the open MAC | 1,000 of 1,000 round trips, three runs, against esp-radio's ESP-NOW on an ESP32-CAM; a signed 540 KB update through a relay in 35–41 s; a laptop streaming at 14.92 fps from the same radio meanwhile |
**Known gaps, stated plainly:** channel-state presence is verified against
captures and against the host, not on a chip; S1 as written (C6 to C6) has
not run, for want of C6 boards. The ledger says so rather than implying
otherwise.
## The open MAC
`rusty_esp_signal-open` (off by default) is a station, an access point and
the link on raw frames for the ESP32-S3, with no esp-radio. **Images built
on it carry no C except the PHY**: Espressif's `libphy`, 18 members,
33.5 KB, which brings the radio up, calibrates it and tunes it. Our code
calls ten of its functions and none that sets a transmit power or a
regulatory table. The transmit power is capped where esp-radio caps it,
**5 dBm**, through the PHY's own init data (the vendored esp-phy's
`phy_max_tx_power`, quarter dBm; `ESP_PHY_CONFIG_PHY_MAX_TX_POWER = "80"`
for ESP-IDF's 20 dBm), so a firmware moving from esp-radio transmits as it
did. The umbrella's `tools/e6-phy.py` checks all three for every open-MAC
image. **Not Wi-Fi certified.**
Every number, with the run that produced it:
[`docs/LEDGER.md`](https://github.com/Remade-With-Rust/rusty_esp_signal/blob/main/docs/LEDGER.md).
## Using it
```rust
use rusty_esp_signal::prelude::*;
use rusty_esp_signal::provision::Env;
use rusty_esp_signal::setup::Reset;
use rusty_esp_signal::wifi::{Action, Event};
// The owner's settings, the identity store, randomness and the device key.
let env = Env { settings, identity, rng, signer };
let mut provisioner = Provisioner::new(StationPolicy::default(), devpub, Reset::PowerOn, now, env)?;
// A stored network is joined at once; without one, the setup window is open
// and the BLE backend routes the `setup` and `discover` characteristics here.
// A setup code unlocks the session; only then does a network go in.
if provisioner.boot(now).action == Action::Connect {
let event = match wifi.join(provisioner.credentials().unwrap()) {
Ok(()) => Event::Connected,
// A refusal has to leave the device askable, not spent.
Err(_) => Event::Disconnected,
};
provisioner.on_event(event, now);
}
```
## Two tracks
| track | what it is | this crate |
|---|---|---|
| **A** | `std` on ESP-IDF — Wi-Fi, Bluetooth provisioning, the radar's serial port | `rusty_esp_signal-esp --features esp-idf` |
| **B** | `no_std` on `esp-hal` — the protocols, the detector, the framing | `rusty_esp_signal-core`, default |
## 0.3.1
Additions only; nothing 0.3.0 offered has changed.
- `rusty_esp_signal-esp`: a station and an access point at once on esp-radio
(`hal::netstack::access_point_station_config`, one radio, both halves built
where WPA2 is decided), and a DHCP server that hands its clients the
upstream lease's DNS servers (`dhcp_server_with_dns_task`), for a board
relaying for its clients.
- `rusty_esp_signal-esp`: BLE setup fixes found on a C6 -- `ble::setup_address`
(a fresh random address per boot, so a central's stale per-address state
cannot hide services) and `ble::ANSWER_GRACE`.
- `rusty_esp_signal-core`: `setup::page`, the setup session over a device's
own open network with no Bluetooth (feature `setup-page`, ~76 KB of flash).
- The family siblings are taken by published version (core 0.1.2, dsp 0.1.1,
mid-core 0.1.2), not by git.
## 0.3.0 is a breaking release
The provisioning service carries the setup session (`setup` and `discover`);
the plaintext `credentials` write and the public `scan` are gone, and
`Provisioner` is the session's GATT router over the stores a platform lends it.
```rust
- let mut p: Provisioner = Provisioner::new(StationPolicy::default());
- p.restore(credentials, now);
+ let env = Env { settings, identity, rng, signer };
+ let mut p = Provisioner::new(StationPolicy::default(), devpub, Reset::PowerOn, now, env)?;
+ p.boot(now);
```
A device takes a session once a setup code's verifier (`setup.v`) is in its
settings, written at flash time (espino's `provision --setup-code-out`, or the
portal). The page is `docs/provision.html`, its wasm `rusty_esp_signal-web`.
0.3.0 also carries the CSI work (breathing and a heart band, the presence
record at version 2, the CSI stream, a fall as a shape in the wander) and the
link's HMAC on the S3's SHA unit (`sha-accel`).
## 0.2.0 was a breaking release
`EspNowLink` no longer takes a lifetime parameter. `esp-radio` 1.0.0-beta.1
dropped it from `EspNowSender` and `EspNowReceiver`, and carrying one here
would have claimed a borrow the type does not hold — it owns the halves.
```rust
- let link: EspNowLink<'_> = EspNowLink::new(sender, receiver, peer);
+ let link: EspNowLink = EspNowLink::new(sender, receiver, peer);
```
The whole signal set moves to 0.2.0 together, the way it has always
published, even though only `-esp` broke.
The chip backends also move to the esp-hal 1.2 companion set, and the
library's dependency pins are **ranges** now rather than `=`: a firmware
must not mix companion sets, but it is the firmware that picks which, and an
`=` pin in a library took that choice away from every consumer at once.
## S1, as a firmware you can run
`firmware/c6-s1-link` is the C6 ↔ C6 kill test, self-driving: one source,
two images, 1,000 authenticated ESP-NOW frames each way, loss / replay /
bad-tag counters straight from `Session::counters()`, and an unadopted
identity that must be refused.
It needs two boards, not three — what has to be turned away is an unadopted
**identity**, not a third piece of silicon, so the initiator mints a second
key and tries again. Same radio, same distance: a refusal cannot be blamed
on range.
## Part of Janus
**Janus** rebuilds the Espressif ESP32 and Arduino application portfolio as
independent, memory-safe Rust packages — so a hardware maker can ship a device
that the [MATA](https://www.mata.network) home computer discovers, catalogs honestly, adopts
under its own identity, and pays for. Ten packages, three layers, and the
dependency direction never reverses.
| layer | packages |
|---|---|
| **0 — the vocabulary** | [`rusty_esp_core`](https://crates.io/crates/rusty_esp_core) · [`rusty_esp_dsp`](https://crates.io/crates/rusty_esp_dsp) |
| **1 — the functions** | [`rusty_esp_image`](https://crates.io/crates/rusty_esp_image) · [`rusty_esp_video`](https://crates.io/crates/rusty_esp_video) · [`rusty_esp_audio`](https://crates.io/crates/rusty_esp_audio) · [`rusty_esp_signal`](https://crates.io/crates/rusty_esp_signal) · [`rusty_esp_mid`](https://crates.io/crates/rusty_esp_mid) · [`rusty_esp_iroh`](https://crates.io/crates/rusty_esp_iroh) |
| **2 — the surfaces** | [`rusty_esp_arduino`](https://crates.io/crates/rusty_esp_arduino) — the sketch facade · `espino` — the maker's CLI (not published) |
Every package is host-verified against an external oracle and keeps a ledger
in which no number appears without the run that produced it. **Five of seven
device profiles have now run their kill tests on real silicon**, three of them
over a Wi-Fi network the board hosts itself.
Also check out the rest of [Remade With Rust](https://github.com/remade-with-rust) — including
[`rusty_alloc`](https://crates.io/crates/rusty_alloc), the pure-Rust rebuild of
mimalloc that these firmwares run on, and
[`rusty_jpeg`](https://crates.io/crates/rusty_jpeg), the JPEG engine behind the
camera path — and our sister project
[remade_ffmpeg_rs](https://github.com/Remade-With-Rust/remade_ffmpeg_rs), a ground-up Rust rebuild of FFmpeg.
## About Mata Network
[Mata Network](https://www.mata.network) builds sovereign, self-hostable infrastructure.
**Remade With Rust** is our open-source home for the permissively-licensed
building blocks that work depends on.
## License
MIT OR Apache-2.0, at your option. See [LICENSE-MIT](https://github.com/Remade-With-Rust/rusty_esp_signal/blob/main/LICENSE-MIT)
and [LICENSE-APACHE](https://github.com/Remade-With-Rust/rusty_esp_signal/blob/main/LICENSE-APACHE).