esp32c3自动配网

尽意
2024-11-06 / 0 评论 / 37 阅读 / 正在检测是否收录...
温馨提示:
本文最后更新于2024年11月06日,已超过77天没有更新,若内容或图片失效,请留言反馈。

esp32-c3 rust使用docker编译 新建项目的基础上,引入必要的依赖

Cargo.toml

[dependencies]
log = { version = "0.4", default-features = false }
esp-idf-svc = { version = "0.49", default-features = false }

# 添加http的依赖
anyhow       = "=1.0.86"
embedded-svc = "=0.28.0"

# json处理
serde = { version = "1.0", features = ["derive"] } # 序列化,支持多种,xml,json等
serde_json = "1.0" # 专门处理json格式

新建一个跟main.rs同级的配网文件,auto_set_wifi.rs

然后在main.rs文件中引用

mod auto_set_wifi;

编写自动配网rust代码


// 引入配网页面的html文件
static INDEX_HTML: &str = include_str!("../index.html");
// 设置nvs存储的参数
static WIFI_INFO_KEY: &str = "WIFI_INFO";
static NAME_SPACE: &str = "ESP_WIFI";

// 默认ap模式下的配置信息
static AP_WIFO_INFO: WifiInfo<'static> = WifiInfo {
    ssid: "esp32_c3_wifi",
    password: "00000000",
};

#[derive(Debug, Deserialize, Serialize)]
struct WifiInfo<'a> {
    ssid: &'a str,
    password: &'a str,
}


pub fn start_auto_wifi() -> anyhow::Result<()> {
    // 自动配网,需要先开启ap模式,让手机连上esp32的热点
    let peripherals = Peripherals::take()?;
    let sysloop = EspEventLoop::take()?;
    let nvs = EspDefaultNvsPartition::take()?;


    let mut wifi = BlockingWifi::wrap(
        EspWifi::new(peripherals.modem, sysloop.clone(), Some(nvs.clone()))?,
        sysloop.clone(),
    )?;
    wifi.set_configuration(&esp_idf_svc::wifi::Configuration::AccessPoint(
        AccessPointConfiguration {
            ssid: AP_WIFO_INFO.ssid.try_into().unwrap(),
            password: AP_WIFO_INFO.password.try_into().unwrap(),
            auth_method: esp_idf_svc::wifi::AuthMethod::WPA2Personal,
            ..Default::default()
        },
    ))?;

     // 由于wifi,跟nvs在后面的函数也需要被调用,所有权不能丢弃,需要用安全的智能指针进行接管
     let wifi = Arc::new(Mutex::new(wifi));
     let nvs = Arc::new(Mutex::new(EspNvs::new(nvs, NAME_SPACE, true)?));
     
     let temp_wifi = wifi.clone();
     let temp_nvs = nvs.clone();
     let mut buf = [0;128];
     log::warn!("正在查找是否存在配网信息...");
     if let Err(_) = nvs.lock().unwrap().get_raw(WIFI_INFO_KEY, &mut buf) {
        log::info!("不存在配网信息,开启ap模式");
        let mut temp_wifi = temp_wifi.lock().unwrap();
        temp_wifi.start()?;
        log::info!("开启wifi");
        temp_wifi.wait_netif_up()?;
        log::info!("等待底层网络分配");
        log::info!("WiFi信息为:{:?}", temp_wifi.wifi().ap_netif().get_ip_info());
     } else {
        log::info!("存在配网信息,开启混合模式");
        let _ = std::thread::spawn(|| start_muxt_mode(temp_wifi, temp_nvs));
     }


    // 配网页面路由
    let temp_nvs = nvs.clone();
    let mut server = EspHttpServer::new(&ServerConfig::default())?;
    server.fn_handler("/", esp_idf_svc::http::Method::Get, |request| {
        request
            .into_ok_response()
            .unwrap()
            .write_all(INDEX_HTML.as_bytes())
    })?;
    // 提交配网的表单信息
    server.fn_handler(
        "/wifi",
        esp_idf_svc::http::Method::Post,
        move |mut request| {
            let len = request.content_len().unwrap() as usize;
            let mut buf = vec![0; len];
            request.read_exact(&mut buf).unwrap();
            let temp_nvs = temp_nvs.clone();

            let mut response = request.into_ok_response().unwrap();
            let raw = temp_nvs.lock().unwrap().set_raw(WIFI_INFO_KEY, &buf);
            if let true = raw.unwrap_or(false) {
                response.write_all(b"{\"msg\":\"success\"}").unwrap();
            } else {
                response.write_all(b"{\"msg\":\"faiture\"}").unwrap();
            }

            response.flush().unwrap();
            Ok(())
        },
    )?;

    // 查看nvs里保存的配网信息
    let temp_nvs = nvs.clone();
    server.fn_handler("/nvs", esp_idf_svc::http::Method::Get, move |request| {
        let temp_nvs = temp_nvs.clone();
        let mut buf = [0; 128];

        let mut response = request.into_ok_response().unwrap();
        if let Option::None = temp_nvs
            .lock()
            .unwrap()
            .get_raw(WIFI_INFO_KEY, &mut buf)
            .unwrap_or(Option::None)
        {
            response.write_all(b"{\"msg\":\"faiure\"}").unwrap();
        } else {
            let end = buf.iter().position(|&x| x == 0).unwrap_or(buf.len());
            let res = String::from_utf8_lossy(&buf[..end]);
            let wifi_info = serde_json::from_slice::<WifiInfo>(&buf[..end]).unwrap();
            log::error!("WiFi信息为:{:?}", wifi_info);
            response.write_all(res.as_bytes()).unwrap();
        }

        response.flush()
    })?;

    // 开启混合模式路由
    let temp_wifi = wifi.clone();
    let temp_nvs = nvs.clone();
    server.fn_handler("/muxt", esp_idf_svc::http::Method::Get, move |request| {
        request.into_ok_response().unwrap().write(b"okk").unwrap();
        let (temp_wifi, temp_nvs) = (temp_wifi.clone(), temp_nvs.clone());
        let _ = std::thread::spawn(|| start_muxt_mode(temp_wifi, temp_nvs));
        Ok(())
    })?;

    // 删除nvs的配置信息
    let temp_nvs = nvs.clone();
    server.fn_handler("/del", esp_idf_svc::http::Method::Get, move |request| {
        let mut response = request.into_ok_response().unwrap();
        let mut temp_nvs = temp_nvs.lock().unwrap();
        if let Err(e) = temp_nvs.remove(WIFI_INFO_KEY) {
            response.write_all(format!("delete faiure,{e}").as_bytes())
        }else{
            response.write_all(b"delete success")
        }
    })?;


    forget(server);
    forget(wifi);

    Ok(())
}

pub fn start_muxt_mode(
    wifi: Arc<Mutex<BlockingWifi<EspWifi>>>,
    nvs: Arc<Mutex<EspNvs<NvsDefault>>>
)-> anyhow::Result<()> {
    // 启动混合模式
    log::info!("延迟500毫秒执行");
    FreeRtos::delay_ms(500);

    log::info!("开始配置");
    let mut wifi = wifi.lock().unwrap();
    let nvs = nvs.lock().unwrap();

    // 重试连接的次数
    let mut retry = 5;
    loop {
        if retry < 0 {
            log::error!("尝试剩余{retry}次后,连接失败");
            break;
        }

        if wifi.is_started()? {
            if let Err(_) = wifi.stop() {
                log::error!("断开wifi连接失败,剩余{}次重试",retry);
                retry-=1;
                continue;
            }
            log::info!("断开wifi");
        }

        let mut buf = [0; 128];
        if let Option::None = nvs.get_raw(WIFI_INFO_KEY, &mut buf).unwrap_or(Option::None) {
            log::error!("获取WiFi信息失败,剩余{}次重试",retry);
        }
        let end = buf.iter().position(|&x| x == 0).unwrap_or(buf.len());
        let wifi_info = serde_json::from_slice::<WifiInfo>(&buf[..end]).unwrap();
        log::warn!("WiFi信息为:{:?}", wifi_info);

        let client_config = ClientConfiguration {
            ssid: wifi_info.ssid.try_into().unwrap(),
            password: wifi_info.password.try_into().unwrap(),
            ..Default::default()
        };
        let ap_config = AccessPointConfiguration {
            ssid: AP_WIFO_INFO.ssid.try_into().unwrap(),
            password: AP_WIFO_INFO.password.try_into().unwrap(),
            auth_method: esp_idf_svc::wifi::AuthMethod::WPA2Personal,
            ..Default::default()
        };
        wifi.set_configuration(&esp_idf_svc::wifi::Configuration::Mixed(
            client_config,
            ap_config,
        ))?;

        log::info!("启动wifi");
        wifi.start()?;
        
        log::info!("等待网络连接");
        if let Err(e) = wifi.connect() {
            log::error!("连接失败:{e},剩余{}次连接尝试", retry);
            retry -= 1;
            continue;
        }
        
        log::info!("等待底层网络驱动处理");
        if let Err(e) = wifi.wait_netif_up() {
            log::error!("等待底层网络驱动处理失败,{e},剩余{}次重试",retry);
        }

        break;
    }

    log::info!("Ap模式信息为:{:?}", wifi.wifi().ap_netif().get_ip_info());
    log::info!("Ip信息为:{:?}", wifi.wifi().sta_netif().get_ip_info());

    Ok(())
}

在main.rs中调用

fn main() {
    // It is necessary to call this function once. Otherwise some patches to the runtime
    // implemented by esp-idf-sys might not link properly. See https://github.com/esp-rs/esp-idf-template/issues/71
    esp_idf_svc::sys::link_patches();

    // Bind the log crate to the ESP Logging facilities
    esp_idf_svc::log::EspLogger::initialize_default();

    start_auto_wifi().unwrap();

}

配网的html页面代码

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>esp32配网页面</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            display: flex;
            justify-content: center;
            align-items: center;
            height: 100vh;
            margin: 0;
            background-color: #f4f4f9;
        }
        .login-container {
            width: 300px;
            padding: 20px;
            background-color: #fff;
            box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
            border-radius: 8px;
        }
        .login-container h2 {
            text-align: center;
            margin-bottom: 20px;
            color: #333;
        }
        .login-container input {
            width: 100%;
            padding: 10px;
            margin: 10px 0;
            border: 1px solid #ccc;
            border-radius: 4px;
            box-sizing: border-box;
        }
        .login-container button {
            width: 100%;
            padding: 10px;
            background-color: #4CAF50;
            border: none;
            color: #fff;
            border-radius: 4px;
            cursor: pointer;
            font-size: 16px;
        }
        .login-container button:hover {
            background-color: #45a049;
        }
    </style>
</head>
<body>

<div class="login-container">
    <h2>配网页面</h2>
    <form id="loginForm">
        <input type="text" id="username" placeholder="ssid" required>
        <input type="password" id="password" placeholder="密码" required>
        <button type="button" onclick="submitForm()">提交</button>
    </form>
</div>

<script>
    function submitForm() {
        const ssid = document.getElementById('username').value;
        const password = document.getElementById('password').value;

        fetch(window.location.origin + '/wifi', {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json'
            },
            body: JSON.stringify({ ssid, password })
        })
        .then(response => response.json())
        .then(data => {
            // 在此处理响应数据
            alert(data.msg);
            if (data.msg === "success"){
                fetch(window.location.origin + '/muxt');
            }
            console.log('成功:', data);
        })
        .catch(error => {
            console.error('错误:', error);
        });
    }
</script>

</body>
</html>
3

评论 (0)

取消