// 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, BlockingWifi}
    };
    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 = BlockingWifi::wrap(EspWifi::new(modem, sysloop.clone(), nvs)?, sysloop.clone())?;

    info!("Searching for Wifi network {}", ssid);

    info!("setting Wifi configuration");
    wifi.set_configuration(&wifi::Configuration::Client(ClientConfiguration {
        ssid: ssid.into(),
        password: psk.into(),
        auth_method,
        ..Default::default()
    }))?;
    
    warn!("Starting wifi...");
    wifi.start()?;

    info!("Connecting wifi...");
    wifi.connect()?;

    wifi.wait_netif_up()?;
    info!("Wifi netif up");

    Ok(wifi)
}


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();
        }

    }

}
Loading
esp32-c6-devkitc-1
NOCOMNCVCCGNDINLED1PWRRelay Module
NOCOMNCVCCGNDINLED1PWRRelay Module
NOCOMNCVCCGNDINLED1PWRRelay Module
NOCOMNCVCCGNDINLED1PWRRelay Module