背景: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