// online mqtt client http://www.emqx.io/online-mqtt-client
use esp_idf_sys::{ EspError }; // If using the `binstart` feature of `esp-idf-sys`, always keep this module imported
use std::fs::{File, read_dir};
use std::io::prelude::*;
use std::path::Path;
use log::{info, warn, error, debug};
fn init_first() -> Result<(), anyhow::Error> {
use log::{set_max_level, LevelFilter};
set_max_level(LevelFilter::Trace);
esp_idf_svc::log::EspLogger::initialize_default();
// println!("{}", include_str!("../Cargo.toml"));
info!("init first");
Ok(())
}
fn fat_init<'a>(path: &'a str, volume: &'a str) -> Result<(), EspError> {
use esp_idf_sys::{ esp,
esp_vfs_fat_mount_config_t, WL_INVALID_HANDLE,
esp_vfs_fat_spiflash_mount
};
use std::ffi::{CString, CStr};
info!("VFS init");
let mount_config = esp_vfs_fat_mount_config_t {
format_if_mount_failed: true,
max_files: 1,
allocation_unit_size: 0,
};
let mut wl_handle = WL_INVALID_HANDLE;
let path = CString::new(path).expect("CString::new failed");
let volume = CString::new(volume).expect("CString::new failed");
unsafe {
esp!( esp_vfs_fat_spiflash_mount(
path.as_ptr(),
volume.as_ptr(),
&mount_config,
&mut wl_handle as *mut i32,
))?;
}
info!("VFS init done");
Ok(())
}
fn connect_wifi<'a>(modem: esp_idf_hal::modem::Modem, ssid: &'a str, psk: &'a str) -> anyhow::Result<esp_idf_svc::wifi::EspWifi<'a>> {
use std::sync::Arc;
use anyhow::bail;
use embedded_svc::ipv4::Ipv4Addr;
use embedded_svc::wifi::{
self, AuthMethod, ClientConfiguration,
Wifi as _,
};
use esp_idf_svc::{
nvs::EspDefaultNvs, eventloop::EspSystemEventLoop, wifi::EspWifi, netif::{EspNetif, EspNetifWait}
};
use esp_idf_sys::esp;
use log::{info, warn};
use std::time::Duration;
esp!(unsafe{ esp_idf_sys::nvs_flash_init() })?;
let mut auth_method = AuthMethod::WPA2Personal; // Todo: add this setting - router dependent
if ssid.is_empty() {
anyhow::bail!("missing WiFi name")
}
if psk.is_empty() {
auth_method = AuthMethod::None;
warn!("Wifi password is empty");
}
let sysloop = EspSystemEventLoop::take()?;
let mut wifi = EspWifi::new(
modem,
sysloop.clone(),
None,
)?;
info!("Searching for Wifi network {}", ssid);
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
};
info!("setting Wifi configuration");
wifi.set_configuration(&wifi::Configuration::Client(ClientConfiguration {
ssid: ssid.into(),
password: psk.into(),
channel,
auth_method,
..Default::default()
}))?;
warn!("Starting wifi...");
wifi.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()?;
debug!("Wifi DHCP info: {:?}", ip_info);
// ping(Ipv4Addr::new(8, 8, 8, 8))?;
Ok(wifi)
}
use embedded_svc::ipv4;
fn ping(ip: ipv4::Ipv4Addr) -> Result<(), anyhow::Error> {
use log::info;
use anyhow::bail;
use esp_idf_svc::ping;
info!("About to do some pings for {:?}", ip);
let ping_summary = ping::EspPing::default().ping(ip, &Default::default())?;
if ping_summary.transmitted != ping_summary.received {
bail!("Pinging IP {} resulted in timeouts, trans: {}, recvs: {}", ip, ping_summary.transmitted, ping_summary.received);
} else {
debug!("Pinging IP {}, trans: {}, recvs: {}", ip, ping_summary.transmitted, ping_summary.received);
}
info!("Pinging done");
Ok(())
}
fn main() {
use esp_idf_hal::peripherals::Peripherals;
esp_idf_sys::link_patches();
init_first().unwrap();
let peripherals = Peripherals::take().unwrap();
fat_init( "/etc", "storage");
let (ssid, psk) = {
if !std::path::Path::new("/etc/wifi").exists() {
let mut file = File::create("/etc/wifi").unwrap();
writeln!(&mut file, "Wokwi-GUEST").unwrap();
writeln!(&mut file, "").unwrap();
}
let mut file = File::open("/etc/wifi").unwrap();
let mut buffer = String::new();
file.read_to_string(&mut buffer).unwrap();
let parts: Vec<&str> = buffer.split("\n").collect();
(String::from(parts[0]), String::from(parts[1]))
};
let _wifi = connect_wifi(peripherals.modem, &ssid, &psk).unwrap();
println!("after");
{
use esp_idf_hal::gpio::PinDriver;
let mut led = PinDriver::output(peripherals.pins.gpio7).unwrap();
use esp_idf_hal::gpio::{Gpio0, Gpio1, Gpio2};
use esp_idf_hal::adc::{
self,
AdcDriver,
Atten11dB,
AdcChannelDriver,
};
let mut adc = AdcDriver::new(peripherals.adc1, &adc::config::Config::new()).unwrap();
let mut adc_pin0: esp_idf_hal::adc::AdcChannelDriver<'_, Gpio0, Atten11dB<_>> =
AdcChannelDriver::new(peripherals.pins.gpio0).unwrap();
let mut adc_pin1: esp_idf_hal::adc::AdcChannelDriver<'_, Gpio1, Atten11dB<_>> =
AdcChannelDriver::new(peripherals.pins.gpio1).unwrap();
let mut adc_pin2: esp_idf_hal::adc::AdcChannelDriver<'_, Gpio2, Atten11dB<_>> =
AdcChannelDriver::new(peripherals.pins.gpio2).unwrap();
led.set_low().unwrap();
use esp_idf_svc::mqtt::client::{EspMqttClient, MqttClientConfiguration};
use embedded_svc::mqtt::client::{QoS, Event::Received};
let mut mqtt_config = MqttClientConfiguration::default();
let mut mqtt_rp_config = MqttClientConfiguration::default();
mqtt_config.client_id = Some("esp32c3");
mqtt_rp_config.client_id = Some("esp32c3-rp");
let mut rp_client = EspMqttClient::new(
"mqtt://broker-cn.emqx.io:1883/mqtt",
&mqtt_rp_config,
|msg| { warn!("rp mqtt {:?}", msg); }
).unwrap();
let mut client = EspMqttClient::new(
"mqtt://broker-cn.emqx.io:1883/mqtt",
&mqtt_config,
move |message_event| match message_event {
Ok(Received(msg)) => {
info!("Message: {:?}", msg);
if msg.topic() == Some("relay/0") {
if let Ok(state) = std::str::from_utf8(msg.data()) {
info!("Change relay: {}", state);
if state.eq("on") {
info!("relay on");
led.set_high().unwrap();
rp_client.publish("relay/0/state", QoS::AtLeastOnce, false, "on".as_bytes()).unwrap();
} else {
info!("relay off");
led.set_low().unwrap();
rp_client.publish("relay/0/state", QoS::AtLeastOnce, false, "off".as_bytes()).unwrap();
}
} else {
warn!("Unable parse message: {:?}", msg);
}
} else {
warn!("Unknown message: {:?}", msg);
}
},
_ => warn!("Received from MQTT: {:?}", message_event),
}
).unwrap();
client.publish("boot", QoS::AtLeastOnce, false, "booted".as_bytes()).unwrap();
info!("Sent boot info on mqtt");
client.subscribe("relay/0", QoS::AtLeastOnce).unwrap();
info!("Subscribe relay/0");
client.publish("relay/0/state", QoS::AtLeastOnce, false, "off".as_bytes()).unwrap();
loop {
use embedded_svc::mqtt::client::QoS;
std::thread::sleep(std::time::Duration::from_secs(10));
client.publish("boot", QoS::AtLeastOnce, false, "booted".as_bytes()).unwrap();
client.publish("adc/0", QoS::AtLeastOnce, false, format!("{}", adc.read(&mut adc_pin0).unwrap()).as_bytes()).unwrap();
}
}
}