在 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>
评论 (0)