Skip to content

Instantly share code, notes, and snippets.

@mihai-dinculescu
Last active September 4, 2024 17:48
Show Gist options
  • Save mihai-dinculescu/42847de6e48ab55b0a522a509d23ad2d to your computer and use it in GitHub Desktop.
Save mihai-dinculescu/42847de6e48ab55b0a522a509d23ad2d to your computer and use it in GitHub Desktop.
ESP32-MQTT-Rust
// build.rs
use embuild::build::LinkArgs;
fn main() -> anyhow::Result<()> {
// Necessary because of this issue: https://github.com/rust-lang/cargo/issues/9641
LinkArgs::output_propagated("ESP_IDF")?;
Ok(())
}
# .cargo/config.toml
[build]
target = "xtensa-esp32-espidf"
[target.xtensa-esp32-espidf]
linker = "ldproxy"
[unstable]
build-std = ["std", "panic_abort"]
build-std-features = ["panic_immediate_abort"]
# Cargo.toml
[package]
name = "esp32_mqtt_publish"
version = "0.1.0"
edition = "2021"
[profile.release]
opt-level = "s" # Optimize for size
[profile.dev]
debug = true # Symbols are nice and they don't increase the size on Flash
opt-level = "z"
[dependencies]
# rust crates
anyhow = { version = "1.0", features = ["backtrace"] }
log = "0.4"
# ESP32 crates
esp-idf-svc = "0.36"
esp-idf-sys = { version = "0.30", features = ["binstart", "native"] } # `native` uses ESP-IDF instead of PlatformIO
[build-dependencies]
anyhow = "1.0"
embuild = "0.28"
// main.rs
use log::info;
use esp_idf_svc::log::EspLogger;
static LOGGER: EspLogger = EspLogger;
fn main() -> anyhow::Result<()> {
esp_idf_sys::link_patches();
log::set_logger(&LOGGER).map(|()| LOGGER.initialize())?;
LOGGER.set_target_level("", log::LevelFilter::Info);
info!("Hello World! I'm a Rustacean!");
Ok(())
}
# Cargo.toml
# ...
# ESP32 crates
embedded-svc = "0.16"
# ...
// main.rs
use anyhow::bail;
use log::info;
use std::sync::Arc;
use embedded_svc::wifi::*;
use esp_idf_svc::log::EspLogger;
use esp_idf_svc::netif::EspNetifStack;
use esp_idf_svc::nvs::EspDefaultNvs;
use esp_idf_svc::sysloop::EspSysLoopStack;
use esp_idf_svc::wifi::EspWifi;
static LOGGER: EspLogger = EspLogger;
// !!! SET THIS !!!
const WIFI_SSID: &str = "";
const WIFI_PASS: &str = "";
fn main() -> anyhow::Result<()> {
// ...
// main.rs
// ...
fn setup_wifi(
netif_stack: Arc<EspNetifStack>,
sys_loop_stack: Arc<EspSysLoopStack>,
default_nvs: Arc<EspDefaultNvs>,
ssid: &str,
password: &str,
) -> anyhow::Result<Box<EspWifi>> {
let mut wifi = Box::new(EspWifi::new(netif_stack, sys_loop_stack, default_nvs)?);
wifi.set_configuration(&Configuration::Client(ClientConfiguration {
ssid: ssid.into(),
password: password.into(),
..Default::default()
}))?;
info!("Wifi configuration set, about to get status");
let status = wifi.get_status();
if let Status(
ClientStatus::Started(ClientConnectionStatus::Connected(ClientIpStatus::Done(_))),
_,
) = status
{
info!("Wifi connected");
} else {
bail!("Unexpected Wifi status: {:?}", status);
}
Ok(wifi)
}
// main.rs
// ...
fn main() -> anyhow::Result<()> {
// ...
let netif_stack = Arc::new(EspNetifStack::new()?);
let sys_loop_stack = Arc::new(EspSysLoopStack::new()?);
let default_nvs = Arc::new(EspDefaultNvs::new()?);
let _wifi = setup_wifi(
netif_stack,
sys_loop_stack,
default_nvs,
WIFI_SSID,
WIFI_PASS,
)?;
Ok(())
}
// ...
# Cargo.toml
# ...
# rust crates
mqtt-protocol = "0.11"
# ...
# ESP32 crates
embedded-hal = "0.2"
esp-idf-hal = "0.32"
# ...
// main.rs
use std::io::Write;
use std::net::TcpStream;
// ...
use mqtt::control::ConnectReturnCode;
use mqtt::packet::{ConnackPacket, ConnectPacket, PublishPacketRef, QoSWithPacketIdentifier};
use mqtt::{Decodable, Encodable, TopicName};
// ...
use embedded_hal::blocking::delay::DelayMs;
use esp_idf_hal::delay;
// ...
// !!! SET THIS !!!
const MQTT_ADDR: &str = ""; // host:port
const MQTT_CLIENT_ID: &str = "test_publish";
const MQTT_TOPIC_NAME: &str = "test_publish";
fn main() -> anyhow::Result<()> {
// ...
// main.rs
// ...
fn mqtt_connect(_: &EspWifi, mqtt_addr: &str, client_id: &str) -> anyhow::Result<TcpStream> {
let mut stream = TcpStream::connect(mqtt_addr)?;
let mut conn = ConnectPacket::new(client_id);
conn.set_clean_session(true);
let mut buf = Vec::new();
conn.encode(&mut buf)?;
stream.write_all(&buf[..])?;
let conn_ack = ConnackPacket::decode(&mut stream)?;
if conn_ack.connect_return_code() != ConnectReturnCode::ConnectionAccepted {
bail!("MQTT failed to receive the connection accepted ack");
}
info!("MQTT connected");
Ok(stream)
}
// main.rs
// ...
fn mqtt_publish(
_: &EspWifi,
stream: &mut TcpStream,
topic_name: &str,
message: &str,
qos: QoSWithPacketIdentifier,
) -> anyhow::Result<()> {
let topic = unsafe { TopicName::new_unchecked(topic_name.to_string()) };
let bytes = message.as_bytes();
let publish_packet = PublishPacketRef::new(&topic, qos, bytes);
let mut buf = Vec::new();
publish_packet.encode(&mut buf)?;
stream.write_all(&buf[..])?;
info!("MQTT published message {} to topic {}", message, topic_name);
Ok(())
}
// main.rs
use std::io::Write;
use std::net::TcpStream;
use std::sync::Arc;
use anyhow::bail;
use log::info;
use mqtt::control::ConnectReturnCode;
use mqtt::packet::{ConnackPacket, ConnectPacket, PublishPacketRef, QoSWithPacketIdentifier};
use mqtt::{Decodable, Encodable, TopicName};
use embedded_hal::blocking::delay::DelayMs;
use embedded_svc::wifi::*;
use esp_idf_hal::delay;
// ...
// !!! SET THIS !!!
const MQTT_ADDR: &str = ""; // host:port
const MQTT_CLIENT_ID: &str = "test_publish";
const MQTT_TOPIC_NAME: &str = "test_publish";
fn main() -> anyhow::Result<()> {
// ...
let wifi = setup_wifi(
netif_stack,
sys_loop_stack,
default_nvs,
WIFI_SSID,
WIFI_PASS,
)?;
let mut mqtt_stream = mqtt_connect(&wifi, MQTT_ADDR, MQTT_CLIENT_ID)?;
loop {
let mut delay = delay::FreeRtos;
// mock a measurement
let value = 21;
let message = format!(r#"{{"measurement":{}}}"#, value);
mqtt_publish(
&wifi,
&mut mqtt_stream,
MQTT_TOPIC_NAME,
&message,
QoSWithPacketIdentifier::Level0,
)?;
delay.delay_ms(10 * 1000_u32);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment