背景:ESP32C3にCO2センサを接続して、IoT PFにMQTTでPublishするシステムを試作する
課題:RustでMQTT Publishできるコードを作成する
取り組み:全部入りサンプルとか、事例集があるので、それらを参考にする
結果:全部入りサンプルを引用しただけだが、Publishまではできた
詳細:
MQTTサンプルがあるのでそれを動かしてみる
How to use MQTT in Rust | EMQ
cargo generate https://github.com/esp-rs/esp-idf-template cargo
全部入りのデモソースから、mqttの部分を切り取って、先のwifiサンプルに貼り付けた
細かい所はそのまま引用した状態。mqtt_clientの関数は不要なsubscribeも入っているのでもっと短くなるはず
#![allow(unused_imports)] #![allow(clippy::single_component_path_imports)] use core::ffi; use std::io::{Read, Write}; use std::net::{TcpListener, TcpStream}; use std::env; use std::time::*; use std::{cell::RefCell, sync::atomic::*, sync::Arc, thread, time::*}; use anyhow::bail; use log::*; use embedded_svc::eth; #[allow(deprecated)] use embedded_svc::httpd::*; // <<<<????? use embedded_svc::io; use embedded_svc::ipv4; use embedded_svc::mqtt::client::{Client, Connection, MessageImpl, Publish, QoS}; use embedded_svc::ping::Ping; use embedded_svc::wifi::*; use esp_idf_svc::eventloop::*; use esp_idf_svc::systime::EspSystemTime; use esp_idf_svc::timer::*; use esp_idf_svc::eventloop::*; use esp_idf_svc::netif::*; use esp_idf_svc::ping; use embedded_svc::utils::mqtt::client::ConnState; use esp_idf_svc::wifi::*; use esp_idf_svc::mqtt::client::*; use esp_idf_hal::peripheral; use esp_idf_hal::prelude::*; use esp_idf_hal::delay::FreeRtos; use esp_idf_sys; use esp_idf_sys::{esp, EspError}; const SSID: &str = env!("RUST_ESP32_STD_DEMO_WIFI_SSID"); const PASS: &str = env!("RUST_ESP32_STD_DEMO_WIFI_PASS"); fn main() -> Result<()> { esp_idf_sys::link_patches(); #[allow(unused)] let peripherals = Peripherals::take().unwrap(); let sysloop = EspSystemEventLoop::take()?; println!("**************** wifi start *****************"); #[allow(clippy::redundant_clone)] #[allow(unused_mut)] let mut _wifi = wifi(peripherals.modem, sysloop.clone())?; println!("**************** mqtt test *****************"); let mut mqtt_client = test_mqtt_client()?; for n in 1..5{ FreeRtos::delay_ms(5000); println!("**************** mqtt publish({}) *****************", n); let msg = format!("Hello from rust-esp32-std-demo!({})",n); let payload = &msg.as_bytes(); mqtt_client.publish( "rust-esp32-std-demo", QoS::AtMostOnce, false, payload)?; } println!("**************** fin *****************"); Ok(()) } fn wifi( modem: impl peripheral::Peripheral<P = esp_idf_hal::modem::Modem> + 'static, sysloop: EspSystemEventLoop,) -> Result<Box<EspWifi<'static>>> { use std::net::Ipv4Addr; use esp_idf_svc::handle::RawHandle; let mut wifi = Box::new(EspWifi::new(modem, sysloop.clone(), None)?); info!("Wifi created, about to scan"); let ap_infos = wifi.scan()?; let ours = ap_infos.into_iter().find(|a| a.ssid == SSID); let channel = if let Some(ours) = ours { info!( "Found configured access point {} on channel {}", SSID, ours.channel ); Some(ours.channel) } else { info!( "Configured access point {} not found during scanning, will go with unknown channel", SSID ); None }; wifi.set_configuration(&Configuration::Mixed( ClientConfiguration { ssid: SSID.into(), password: PASS.into(), channel, ..Default::default() }, AccessPointConfiguration { ssid: "aptest".into(), channel: channel.unwrap_or(1), ..Default::default() }, ))?; wifi.start()?; info!("Starting wifi..."); if !WifiWait::new(&sysloop)? .wait_with_timeout(Duration::from_secs(20), || wifi.is_started().unwrap()) { bail!("Wifi did not start"); } info!("Connecting wifi..."); wifi.connect()?; if !EspNetifWait::new::<EspNetif>(wifi.sta_netif(), &sysloop)?.wait_with_timeout( Duration::from_secs(20), || { wifi.is_connected().unwrap() && wifi.sta_netif().get_ip_info().unwrap().ip != Ipv4Addr::new(0, 0, 0, 0) }, ) { bail!("Wifi did not connect or did not receive a DHCP lease"); } let ip_info = wifi.sta_netif().get_ip_info()?; info!("Wifi DHCP info: {:?}", ip_info); Ok(wifi) } fn test_mqtt_client() -> Result<EspMqttClient<ConnState<MessageImpl, EspError>>> { info!("About to start MQTT client"); println!("About to start MQTT client"); let conf = MqttClientConfiguration { client_id: Some("rust-esp32-std-demo"), crt_bundle_attach: Some(esp_idf_sys::esp_crt_bundle_attach), ..Default::default() }; let (mut client, mut connection) = EspMqttClient::new_with_conn("mqtts://broker.emqx.io:8883", &conf)?; info!("MQTT client started"); println!("MQTT client started"); println!("broker: broker.emqx.io"); thread::spawn(move || { info!("MQTT Listening for messages"); while let Some(msg) = connection.next() { match msg { Err(e) => info!("MQTT Message ERROR: {}", e), Ok(msg) => info!("MQTT Message: {:?}", msg), } } info!("MQTT connection loop exit"); println!("MQTT connection loop exit"); }); client.subscribe("rust-esp32-std-demo", QoS::AtMostOnce)?; info!("Subscribed to all topics (rust-esp32-std-demo)"); client.publish( "rust-esp32-std-demo", QoS::AtMostOnce, false, "Hello from rust-esp32-std-demo!".as_bytes(), )?; info!("Published a hello message to topic \"rust-esp32-std-demo\""); println!("Published a hello message to topic \"rust-esp32-std-demo\""); Ok(client) }
PC側でMQTT Clientを走らせてsubscribeしてみる
不要なメッセージを一回受信してしまうのは、retainが働いているからだろうか・・・
$ ./mqtt_sub.py connect: broker.emqx.io Connected with result code 0 -------------------------------- topic:rust-esp32-std-demo payload:b'Hello from rust-esp32-std-demo!' -------------------------------- topic:rust-esp32-std-demo payload:b'Hello from rust-esp32-std-demo!' -------------------------------- topic:rust-esp32-std-demo payload:b'Hello from rust-esp32-std-demo!(1)' -------------------------------- topic:rust-esp32-std-demo payload:b'Hello from rust-esp32-std-demo!(2)' -------------------------------- topic:rust-esp32-std-demo payload:b'Hello from rust-esp32-std-demo!(3)' -------------------------------- topic:rust-esp32-std-demo payload:b'Hello from rust-esp32-std-demo!(4)'
■参考URL
embedded_svc::mqtt - Rust
GitHub - obabec/rust-mqtt: Rust native mqtt client for both std and no_std environmnents.
Let's take a look at MQTT and how you can use MQTT with Rust using the rumqttc crate in your next IoT project. · GitHub
client.rs - source
espressif-trainings/solution_publ.rs at main · esp-rs/espressif-trainings · GitHub
espressif-trainings/03_5_2_mqtt.md at main · esp-rs/espressif-trainings · GitHub
esp-idf-svc/client.rs at master · esp-rs/esp-idf-svc · GitHub