Wi-Fi, MQTT, LoRa, ESP-NOW, and OTA support libraries for ESP32 projects.
Note: Large parts of this library (and documentation) were developed with the assistance of AI tools. All generated code has been reviewed and curated by the maintainer.
Any ESP32-IDF project can add Wi-Fi and MQTT in minutes, with confidence.
We are building this for: ESP32-IDF Rust firmware developers — internal projects first, clean enough for anyone to adopt.
Long-term goals:
- Reliable, thin hardware wrappers that stay focused on connectivity — nothing more
- A growing platform-independent layer (
rustyfarian-network-pure) that can be unit-tested on the host - Minimal friction: a few lines of
Cargo.tomland no surprises
Out of scope: General-purpose application-layer clients (HTTP, CoAP, WebSocket) and provisioning/SoftAP flows.
The OTA crates (rustyfarian-esp-idf-ota, rustyfarian-esp-hal-ota) carry their own internal HTTP/1.1 GET clients for firmware download, but these are implementation details and not published as reusable workspace HTTP APIs.
Full vision, success signals, and open questions: VISION.md
This library embodies the principle of extracting testable pure logic from hardware-specific code — a pattern common in application development but rare in embedded Rust.
- Pure functions belong in
rustyfarian-network-pure— a platform-independent crate with no ESP-IDF dependency - Examples: SSID and password validation, connection timeout arithmetic, backoff calculations
- If you can unit-test it without hardware, it should be in
rustyfarian-network-pure - The ESP-IDF wrappers (
rustyfarian-esp-idf-wifi,rustyfarian-esp-idf-mqtt) are thin layers that delegate logic downward and handle the hardware lifecycle
rustyfarian-network-pure can be fully unit-tested on your laptop without an ESP32 or ESP toolchain.
| Crate | Description |
|---|---|
rustyfarian-network-pure |
Platform-independent primitives — validation, timing math; unit-testable on the host |
wifi-pure |
Platform-independent Wi-Fi types, traits, and validation; no_std; unit-testable on host |
rustyfarian-esp-idf-wifi |
Wi-Fi connection manager with LED status feedback |
rustyfarian-esp-hal-wifi |
Wi-Fi driver stub for bare-metal esp-hal targets; full implementation in progress |
rustyfarian-esp-idf-mqtt |
MQTT client with automatic reconnection and graceful shutdown |
lora-pure |
Platform-independent LoRa/LoRaWAN types and traits; no_std; unit-testable on host |
rustyfarian-esp-idf-lora |
LoRa radio driver (SX1262) and LoRaWAN adapter for ESP-IDF targets |
rustyfarian-esp-hal-lora |
LoRa radio stub for bare-metal esp-hal targets; hardware driver in progress |
espnow-pure |
Platform-independent ESP-NOW types, traits, and validation; no_std; unit-testable on host |
rustyfarian-esp-idf-espnow |
ESP-NOW driver for ESP-IDF projects, implementing the EspNowDriver trait |
ota-pure |
Platform-independent OTA primitives — Version, streaming SHA-256, sidecar metadata |
rustyfarian-esp-idf-ota |
ESP-IDF OTA driver — blocking; streaming download, SHA-256 verify, partition swap, rollback |
rustyfarian-esp-hal-ota |
Bare-metal OTA driver — async-only; strict HTTP/1.1 over embassy-net + OtaUpdater (MVP) |
Add to your Cargo.toml:
[dependencies]
rustyfarian-esp-idf-wifi = { git = "https://github.com/datenkollektiv/rustyfarian-network" }
rustyfarian-esp-idf-mqtt = { git = "https://github.com/datenkollektiv/rustyfarian-network" }use rustyfarian_esp_idf_wifi::{WiFiManager, WiFiConfig};
use rustyfarian_esp_idf_mqtt::{MqttBuilder, MqttConfig};
use esp_idf_svc::mqtt::client::QoS;
let wifi_config = WiFiConfig::new("MyNetwork", "password123");
let wifi = WiFiManager::new(modem, sys_loop, Some(nvs), wifi_config, None::<&mut MyLed>)?;
if let Some(ip) = wifi.get_ip(10000)? {
println!("Connected with IP: {}", ip);
}
let mqtt_config = MqttConfig::new("192.168.1.100", 1883, "my-device");
let mqtt = MqttBuilder::new(mqtt_config)
.on_connect(|client, _clean_session| {
client.subscribe("commands", QoS::AtMostOnce)?;
Ok(())
})
.on_message(|topic, data| {
println!("Received on {}: {:?}", topic, data);
})
.build()?;
mqtt.publish_with("status", b"online", QoS::AtMostOnce, false)?;use rustyfarian_esp_idf_mqtt::{MqttBuilder, MqttConfig, LwtConfig};
use esp_idf_svc::mqtt::client::QoS;
let lwt = LwtConfig::new("device/status", b"offline", QoS::AtLeastOnce, true);
let mqtt_config = MqttConfig::new("192.168.1.100", 1883, "my-device")
.with_lwt(lwt);
let mqtt = MqttBuilder::new(mqtt_config)
.on_connect(|client, _clean_session| {
client.subscribe("commands", QoS::AtMostOnce)?;
Ok(())
})
.on_message(|topic, data| {
println!("Received on {}: {:?}", topic, data);
})
.build()?;
mqtt.publish_with("device/status", b"online", QoS::AtLeastOnce, true)?;The Wi-Fi manager supports optional LED status feedback during connection.
For boards with a simple on/off LED (not RGB), use SimpleLed:
use rustyfarian_esp_idf_wifi::{WiFiManager, WiFiConfig, SimpleLed};
use esp_idf_hal::gpio::PinDriver;
let pin = PinDriver::output(peripherals.pins.gpio8)?;
let mut led = SimpleLed::new(pin);
let wifi_config = WiFiConfig::new("MyNetwork", "password123");
let wifi = WiFiManager::new(modem, sys_loop, Some(nvs), wifi_config, Some(&mut led))?;For RGB LEDs, implement the StatusLed trait from pennant.
Each crate includes runnable examples for specific ESP32 targets.
List all examples with just and build one with:
just build-example idf_c3_connectTo flash to a connected board:
just flash idf_c3_connectSee crates/*/examples/ for the full set, including Wi-Fi, MQTT, and LoRaWAN OTAA join demos.
After cloning, run the one-time setup before building or running examples:
just setup-toolchain
just setup-cargo-configsetup-cargo-config copies .cargo/config.toml.dist to .cargo/config.toml, which
configures linker settings and target-specific flags for ESP32, ESP32-S3, and bare-metal
Xtensa targets.
Without this step, builds for those targets will fail with linker or toolchain errors.
MIT or Apache-2.0