Skip to content

3.8 HTTP 网页配网与本地管理 ​

这节要搞懂:设备怎么在不改代码的情况下换 WiFi,以及那个内置网页还能干什么。 需要基础:3.7

一、要解决什么问题 ​

WiFi 密码如果写死在代码里,换个地方就得重新烧录固件 —— 带去公司、拿到朋友家、路由器换了名字,全都要重来。

配网(Provisioning)指的是:让设备在不修改代码的情况下获取 WiFi 账号密码的过程。

本工程的网页地址

设备会开启一个配网热点:

项目值
热点名称ESP32-Clock-Setup
热点密码12345678
配网页面http://192.168.4.1/
SD 卡管理页面http://192.168.4.1/sd

手机或电脑连上这个热点后,打开上面的地址就能配网和管理 SD 卡。

设备已经连上路由器时,也可以让手机/电脑连同一个路由器,然后访问它的局域网 IP:

  • http://192.168.x.x/
  • http://192.168.x.x/sd

局域网 IP 从串口日志里的 got ip: xxx.xxx.xxx.xxx 查看,或者去路由器后台看。

配网有两条路

除了本节讲的网页配网,工程里还可以通过蓝牙配网(3.10 第五节)—— 手机连上蓝牙,往 WiFi 特征值写 {"ssid":"...","password":"..."} 就行。

网页配网蓝牙配网
设备要变成热点吗✅ 要❌ 不用
手机要做什么连热点 + 开浏览器连蓝牙 + 写特征值
适合普通用户开发调试更快

二、AP 模式和 STA 模式 ​

要理解配网,先分清 WiFi 的两种角色:

STA(Station,station 模式):设备作为客户端去连别人的路由器 —— 平时上网就是这个模式。

AP(Access Point,接入点模式):设备自己变成一个热点,让手机来连它。

STA 模式AP 模式
谁连谁设备 → 路由器手机 → 设备
有没有外网✅ 有❌ 没有
什么时候用正常工作配网的时候

配网的核心思路:设备还不知道 WiFi 密码时,先让自己变成热点,手机连上来把密码告诉它。

三、完整流程 ​

开机 → 从 NVS 读取已保存的 WiFi
  │
  ├── 有配置 → 尝试 STA 模式连接路由器
  │              │
  │              ├── 连接成功 → 同时开启 AP 热点 + HTTP 网页
  │              └── 连接失败 → 开启 AP 热点 + HTTP 网页
  │
  └── 没有配置 → 开启 AP 热点 + HTTP 网页

手机 / 电脑连接 ESP32-Clock-Setup
        ↓
浏览器打开 http://192.168.4.1/
        ↓
选择或手动输入 WiFi 名称和密码
        ↓
设备尝试连接路由器
        ↓
连接成功 → 保存到 NVS

注意:AP 热点是一直开着的

很多教程写的是"只有没配置时才开热点,连上就关掉"。

本工程不是这样 —— 不管有没有连上路由器,AP 热点和网页服务都保持开启。

这样做的好处:

  • 随时能改配置,不用先把 WiFi 弄坏
  • SD 卡管理页面随时可用,不依赖路由器
  • 换网络环境时不用重新触发配网模式

代价:多一点功耗,以及热点一直暴露着(所以设了密码 12345678)。

这叫 AP + STA 共存模式

ESP32 支持 WIFI_MODE_APSTA —— 同时当热点和客户端。

一边连着家里的路由器上网,一边自己还开着热点让手机连,两不耽误。

代价:两个角色共用一套射频,同时用的时候吞吐会下降一些。日常配置这点数据量完全无感。

四、HTTP Server 怎么起 ​

ESP-IDF 自带 esp_http_server,起一个服务器就几行:

c
#include "esp_http_server.h"

static httpd_handle_t s_server = NULL;

// 每个 URL 对应一个 handler
static const httpd_uri_t uri_root = {
    .uri      = "/",
    .method   = HTTP_GET,
    .handler  = root_get_handler,       // 返回配网页面的 HTML
};

static const httpd_uri_t uri_scan = {
    .uri      = "/scan",
    .method   = HTTP_GET,
    .handler  = wifi_scan_handler,      // 返回扫描到的 WiFi 列表
};

static const httpd_uri_t uri_connect = {
    .uri      = "/connect",             // ⭐ 提交 WiFi 账号密码
    .method   = HTTP_POST,
    .handler  = wifi_connect_handler,
};

void http_server_start(void)
{
    httpd_config_t config = HTTPD_DEFAULT_CONFIG();
    config.max_uri_handlers = 16;       // ⭐ SD 卡还要挂十来个,默认 8 个不够

    if (httpd_start(&s_server, &config) == ESP_OK) {
        httpd_register_uri_handler(s_server, &uri_root);
        httpd_register_uri_handler(s_server, &uri_scan);
        httpd_register_uri_handler(s_server, &uri_connect);
        // ... 还有 SD 卡相关的一堆,见第六节
    }
}

五、接收网页提交的 WiFi 配置 ​

网页把账号密码 POST 到 /connect:

c
static esp_err_t wifi_connect_handler(httpd_req_t *req)
{
    char content[256];

    int len = httpd_req_recv(req, content, sizeof(content) - 1);
    if (len <= 0) {
        return ESP_FAIL;
    }
    content[len] = '\0';

    char ssid[33] = {0};
    char password[65] = {0};

    // 从 "ssid=xxx&pass=yyy" 里取出两个字段
    httpd_query_key_value(content, "ssid", ssid, sizeof(ssid));
    httpd_query_key_value(content, "pass", password, sizeof(password));

    app_config_save_wifi(ssid, password);       // 存进 NVS(3.4)

    httpd_resp_sendstr(req, "saved");

    vTaskDelay(pdMS_TO_TICKS(1000));
    esp_restart();                              // 重启后用新配置连

    return ESP_OK;
}

一定要判断长度

httpd_req_recv() 返回的可能比缓冲区还大。不判断就 memcpy 会踩内存(和 3.6 解析天气 JSON 是同一个坑)。

六、同一个服务器还兼做 SD 卡管理 ​

工程里 app_wifi.c 起的这个 HTTP Server 不只用来配网 —— 它还提供一整套 SD 卡管理接口:

方法路径干什么
GET/sdSD 卡管理页面(文件列表)
POST/sd/upload?name=文件名上传文件
GET/sd/file?name=文件名下载文件
POST/sd/delete删除文件
POST/sd/show在屏幕上显示图片
POST/sd/show-rgb565显示 RGB565 格式的图片
POST/sd/audio-play播放音频文件
POST/sd/audio-pcm播放 PCM 裸数据
POST/sd/audio-stop停止播放
POST/sd/exit退出当前显示 / 播放

浏览器打开 http://192.168.4.1/sd 就能看到文件列表,上传音频和图片、点一下就在设备屏幕上显示或播放。

具体实现在 3.9 讲。

一个服务器挂多个 handler

不用为每个功能单开一个服务器 —— httpd_register_uri_handler() 注册多个 URL 就行,共用一个端口、一份内存。

注意 config.max_uri_handlers 默认只有 8 个 —— 光 SD 卡就要十个,加上配网的三个,必须调大,否则后面注册的会静默失败。

这个设计很实用

把"文件管理"做成网页,比通过串口命令或者拔卡拷贝方便得多:

  • 换铃声:网页上传一个音频,点播放试听
  • 换壁纸:上传图片,点一下就显示在屏幕上
  • 看日志:直接下载 CSV 到电脑

而且不需要装任何 App,有浏览器就行。

本节要点 ​

  • STA = 设备连路由器,AP = 设备自己当热点;配网时用 AP
  • ⭐ AP 热点和网页服务一直开着(APSTA 共存),不是连上就关
  • 热点 ESP32-Clock-Setup / 密码 12345678,配网页 192.168.4.1,SD 卡页 192.168.4.1/sd
  • 已连上路由器时,也能用局域网 IP 访问(串口 got ip 里看)
  • 配网接口:GET /scan 扫列表、POST /connect 提交
  • esp_http_server 起服务器,一个服务器可以挂多个 URL handler
  • 接收数据要判断长度,别踩内存
  • 同一个服务器还挂了十个 SD 卡管理接口,max_uri_handlers 要调大
  • Captive Portal 目前没实现,需要手动输 IP,是值得加的优化

操作过程 ​

目标:不改一行代码,把设备连到一个新的 WiFi 上。

第 1 步:编译烧录 ​

第 2 步:手机连上设备的热点 ​

打开手机 WiFi 列表,找到:

名称:ESP32-Clock-Setup
密码:12345678

手机会提示"无法访问互联网"

这是正常的 —— 这个热点本来就没有外网。选"仍然连接 / 保持连接"。

有的手机会自动切回移动数据,要在 WiFi 设置里关掉"自动切换网络"。

第 3 步:打开配网页面 ​

浏览器输入:

http://192.168.4.1/

一定要带 http://

直接输 192.168.4.1 有的浏览器会当成搜索词去百度。手动加上 http://。

第 4 步:填 WiFi 并提交 ​

页面上选择或手动输入你家的 WiFi 名称和密码,点提交。

第 5 步:应该看到什么 ​

串口:

I (45000) wifi: save ssid=MyHome
I (45200) wifi: connecting to AP ...
I (47800) wifi: got ip: 192.168.1.107     ← ⭐ 记下这个 IP

屏幕:显示连接成功,然后进入主界面。

第 6 步:用局域网 IP 再访问一次 ​

让电脑连上同一个路由器,浏览器打开上一步那个 IP:

http://192.168.1.107/
http://192.168.1.107/sd

这一步验证了什么

说明设备连着路由器的同时,网页服务也还开着(APSTA 共存)—— 不用再切回热点就能改配置、管文件。

动手试试 ​

  1. 手机连上 ESP32-Clock-Setup(密码 12345678),打开 http://192.168.4.1/ 完成一次配网。
  2. 打开 http://192.168.4.1/sd,看看 SD 卡里有什么文件。
  3. 设备连上路由器后,从串口日志里找到 got ip,用电脑访问那个 IP,验证局域网也能进。
  4. 在 wifi_connect_handler 里打印收到的原始内容,看看浏览器提交上来的格式长什么样。
  5. 把 config.max_uri_handlers 改成 5,观察后面注册的 handler 会怎样。