资讯动态

ESP32免重刷改WiFi配置:NVS存储与浏览器配置页面实战

发布时间:2026/10/27 18:47:08 来源:尧图企业网站定制
1. 从一个让人抓狂的场景说起如果你玩过 ESP32大概率经历过这个场景设备已经焊好、装进壳子、挂在墙上突然要换个 WiFi 密码。按照传统流程你得打开电脑、连上开发板、改代码里的ssid和password、重新编译、重新烧录运气不好还得按住 BOOT 键手动进下载模式。整个过程少说五分钟多则半小时如果设备装在吊顶或者户外那更是灾难。我手上有一批基于 ESP32 的温湿度采集节点分散在几个房间里最早那批固件把 WiFi 凭据硬编码在代码里。后来路由器换了一次密码我花了整整一个下午挨个拆下来重刷。那次之后我就下定决心一定要找一个不用重刷固件就能改 WiFi 配置的方案。后来我摸索出一套基于NVSNon-Volatile Storage加浏览器工具的做法把 WiFi 凭据存在 NVS 分区里设备启动时从 NVS 读取同时设备跑一个轻量 HTTP 服务提供一个网页界面你在浏览器里填新密码提交设备写入 NVS 然后重启完事。整个过程不需要数据线、不需要编译环境手机连上设备热点就能操作。这套方案解决的核心问题就一个把改配置和刷固件这两件事彻底解耦。固件负责逻辑NVS 负责数据浏览器负责交互。适合所有做 ESP32 物联网项目、需要现场调试或远程维护的开发者哪怕你刚入门照着做也能跑通。2. 为什么是 NVS 加浏览器工具这套组合2.1 硬编码、SPIFFS 文件、NVS 三种方案对比在决定用 NVS 之前我把常见的几种配置存储方案都试了一遍这里直接给结论。方案修改方式是否需要重刷掉电保存磨损寿命适合场景代码硬编码改源码重编译是是不涉及一次性量产、配置永不变SPIFFS/LittleFS 文件串口或网页上传文件否是一般存大块配置、网页资源NVS 键值对API 读写否是高带磨损均衡小配置项、频繁读写EEPROM 模拟API 读写否是较低老项目兼容硬编码的问题不用多说改一次哭一次。SPIFFS 文件方案其实也能用但它的定位是存文件系统用来存几个字符串有点杀鸡用牛刀而且文件系统挂载本身有失败风险。NVS 是 ESP-IDF 原生提供的键值存储底层跑在 flash 的一个独立分区上自带磨损均衡和掉电保护专门为存配置参数设计读写几个字符串又快又稳。提示NVS 分区大小在分区表里定义默认nvs分区通常是 0x600024KB存 WiFi 凭据、设备 ID、校准参数这类小数据绰绰有余。如果你要存几百条记录那得考虑加大分区或者换文件系统。2.2 为什么交互层选浏览器而不是串口或蓝牙串口改配置需要电脑和线蓝牙需要装 App 或者用通用 BLE 调试工具体验都不如浏览器。浏览器的优势在于任何有屏幕和浏览器的设备都能用手机、平板、电脑通吃不需要装任何东西。ESP32 本身就能跑 HTTP 服务器用WebServer库或者 ESP-IDF 的httpd组件几十行代码就能起一个配置页面。具体流程是这样的设备上电后先尝试用 NVS 里的凭据连 WiFi如果连不上或者 NVS 里没有凭据就切换成 AP 模式自己发一个热点。你用手机连上这个热点浏览器打开192.168.4.1就能看到配置页面。填好新的 SSID 和密码提交设备写入 NVS重启用新凭据连网。整个过程设备不用拆、不用接线。2.3 这套方案能解决哪些真实痛点我总结下来它主要解决四类问题。第一类是现场部署设备装好了才发现密码填错不用拆机。第二类是批量维护路由器换密码后设备自动进 AP 模式等你改不用挨个找。第三类是多环境切换同一块板子拿到公司、家里、客户现场连不同 WiFi 不用改代码。第四类是给非技术人员用你把设备给客户客户自己就能配网不用你远程指导烧录。这里有个设计上的取舍值得说设备启动时是先连再退还是先判断再连。我的做法是启动后先读 NVS如果有凭据就尝试连接超时 10 秒连不上就切 AP 模式如果 NVS 里压根没凭据直接进 AP 模式。这样正常使用时启动快异常时又能自动兜底。3. NVS 读写的核心细节与避坑要点3.1 NVS 的命名空间、键名和数据类型NVS 的结构是命名空间namespace 键key 值value。命名空间类似文件夹键类似文件名值就是数据。命名空间和键名都有长度限制命名空间最长 15 个字符键名最长 15 个字符超了会报错。这个坑我踩过当时用wifi_configuration当命名空间编译能过但运行时报ESP_ERR_NVS_KEY_TOO_LONG查了半天才发现是长度问题。数据类型方面NVS 支持整数i8/u8/i16/u16/i32/u32/i64/u64、字符串、二进制大对象blob。WiFi 的 SSID 和密码用字符串存就行。注意字符串是以 null 结尾的实际占用空间是长度加一。// ESP-IDF 下写入 WiFi 凭据的典型代码 #include nvs_flash.h #include nvs.h void save_wifi_credentials(const char *ssid, const char *password) { nvs_handle_t handle; esp_err_t err nvs_open(wifi_cfg, NVS_READWRITE, handle); if (err ! ESP_OK) { printf(nvs_open failed: %s\n, esp_err_to_name(err)); return; } nvs_set_str(handle, ssid, ssid); nvs_set_str(handle, pass, password); nvs_commit(handle); // 必须 commit 才真正落盘 nvs_close(handle); }注意nvs_set_str之后一定要调用nvs_commit否则数据可能还在缓存里掉电就丢了。这个和数据库的事务提交是一个道理很多人第一次用会漏掉。3.2 读取时的容错处理读取比写入更需要小心因为首次上电时 NVS 里是空的直接读会返回ESP_ERR_NVS_NOT_FOUND。如果不处理这个错误程序可能崩溃或者读到乱码。正确的做法是判断返回值读不到就用默认值或者进 AP 模式。bool load_wifi_credentials(char *ssid, size_t ssid_len, char *pass, size_t pass_len) { nvs_handle_t handle; if (nvs_open(wifi_cfg, NVS_READONLY, handle) ! ESP_OK) { return false; } esp_err_t e1 nvs_get_str(handle, ssid, ssid, ssid_len); esp_err_t e2 nvs_get_str(handle, pass, pass, pass_len); nvs_close(handle); return (e1 ESP_OK e2 ESP_OK); }这里有个细节nvs_get_str的第三个参数是缓冲区大小传入的是指针函数会把它更新为实际读取的长度。如果你传的缓冲区太小会返回ESP_ERR_NVS_INVALID_LENGTH。所以缓冲区至少给 33 字节WiFi SSID 最大 32 字节加结尾符密码给 64 字节比较稳妥。3.3 NVS 分区被写满或损坏怎么办NVS 虽然带磨损均衡但如果你频繁写入比如每秒写一次分区迟早会满。满了之后nvs_set_str会返回ESP_ERR_NVS_NOT_ENOUGH_SPACE。解决办法有两个一是调用nvs_erase_key删掉旧键再写二是用nvs_erase_all清空整个命名空间。我的做法是写入前先 erase 对应的键避免旧数据占空间。还有一种情况是 NVS 分区损坏通常发生在固件升级或者 flash 操作异常之后。这时候nvs_flash_init会返回ESP_ERR_NVS_NO_FREE_PAGES或ESP_ERR_NVS_NEW_VERSION_FOUND。标准处理方式是擦除后重新初始化esp_err_t ret nvs_flash_init(); if (ret ESP_ERR_NVS_NO_FREE_PAGES || ret ESP_ERR_NVS_NEW_VERSION_FOUND) { nvs_flash_erase(); ret nvs_flash_init(); }提示nvs_flash_erase会清掉所有命名空间的数据包括你可能存的其他配置。所以如果你的项目里 NVS 还存了别的关键数据擦除前要三思或者把 WiFi 配置单独放一个分区。4. 浏览器配置页面的完整实现4.1 HTTP 服务器的搭建与路由设计在 ESP32 上起 HTTP 服务Arduino 环境用WebServer库最省事ESP-IDF 环境用httpd组件。我这里以 Arduino 为例因为它对新手更友好。核心思路是注册两个路由GET /返回配置页面 HTMLPOST /save接收表单数据并写入 NVS。#include WiFi.h #include WebServer.h #include Preferences.h WebServer server(80); Preferences prefs; void handleRoot() { String html !DOCTYPE htmlhtmlbody; html h2WiFi Config/h2; html form action/save methodPOST; html SSID:brinput namessidbr; html Password:brinput namepass typepasswordbrbr; html input typesubmit valueSave; html /form/body/html; server.send(200, text/html, html); } void handleSave() { String ssid server.arg(ssid); String pass server.arg(pass); prefs.begin(wifi_cfg, false); prefs.putString(ssid, ssid); prefs.putString(pass, pass); prefs.end(); server.send(200, text/html, h3Saved. Rebooting.../h3); delay(1000); ESP.restart(); }Arduino 的Preferences库其实就是对 NVS 的封装begin对应nvs_openputString对应nvs_set_strend会自动 commit。用起来比原生 API 简洁不少适合快速开发。4.2 AP 模式与 STA 模式的自动切换逻辑设备启动后的状态机是这样的先读 NVS有凭据就进 STA 模式尝试连接连上就正常跑业务连不上或者没凭据就进 AP 模式起热点和 HTTP 服务。这里的关键是连接超时要设合理太短了网络慢的时候连不上太长了用户等得着急。我实测 10 到 15 秒比较合适。void setup() { Serial.begin(115200); prefs.begin(wifi_cfg, true); String ssid prefs.getString(ssid, ); String pass prefs.getString(pass, ); prefs.end(); if (ssid.length() 0) { WiFi.mode(WIFI_STA); WiFi.begin(ssid.c_str(), pass.c_str()); unsigned long start millis(); while (WiFi.status() ! WL_CONNECTED millis() - start 15000) { delay(500); } } if (WiFi.status() ! WL_CONNECTED) { WiFi.mode(WIFI_AP); WiFi.softAP(ESP32_Config, 12345678); server.on(/, handleRoot); server.on(/save, HTTP_POST, handleSave); server.begin(); } }注意AP 模式的热点密码别设太简单也别不设不设密码的话任何人都能连上来改你的配置。设个12345678这种临时密码就行反正配完就重启了。4.3 配置页面的安全与体验优化基础版本能用但有几个地方可以优化。第一是表单校验SSID 不能为空密码长度至少 8 位WPA2 要求前端用required和minlength属性就能做。第二是提交后反馈别让用户点了按钮没反应返回一个保存成功设备重启中的页面。第三是防止误操作可以加一个确认弹窗。还有一个容易被忽略的点AP 模式下 DNS 劫持。默认情况下用户得手动输入192.168.4.1体验不好。可以起一个 DNS 服务器把所有域名解析到192.168.4.1这样用户连上热点后随便打开一个网页都会跳到配置页。Arduino 环境下用DNSServer库几行就能搞定。#include DNSServer.h DNSServer dnsServer; dnsServer.start(53, *, WiFi.softAPIP());这个技巧在商业产品里很常见用户连上热点自动弹配置页不用记 IP。不过要注意 DNS 服务器会占用资源配置完成后记得关掉。5. 实操全流程与关键环节记录5.1 从零搭建的完整步骤我把整个流程拆成可复现的步骤你照着做就行。准备开发环境。Arduino IDE 装好 ESP32 支持包或者用 PlatformIO。我推荐 PlatformIO依赖管理更清晰编译也快。开发板选ESP32 Dev Module如果是 S3 核心板就选对应的 S3 型号。确认分区表。默认分区表里已经有nvs分区一般不用改。如果你用的是自定义分区表确保nvs分区存在且不小于 0x5000。写代码。把上面的 NVS 读写、HTTP 服务、模式切换逻辑整合到一个.ino或.cpp文件里。代码量大概 150 行左右。烧录测试。第一次烧录后设备因为没有 NVS 凭据会直接进 AP 模式。手机连上ESP32_Config热点浏览器打开192.168.4.1填 WiFi 信息提交。验证。设备重启后应该连上你填的 WiFi串口打印出获取到的 IP 地址。再改一次密码确认不用重刷就能生效。5.2 关键参数的计算与选择有几个参数需要根据实际情况调整。连接超时我设 15 秒因为有些企业级 AP 的认证流程比较慢。AP 热点信道默认是 1如果周围干扰严重可以改成 6 或 11。HTTP 服务器端口用 80方便用户直接输 IP 不用带端口号。NVS 分区大小方面存两个字符串大概占用不到 100 字节加上 NVS 自身的元数据开销24KB 的分区能存几百条记录。如果你还要存其他配置算一下总量别超就行。计算公式很简单每条记录占用约键名长度 值长度 32 字节元数据。5.3 实测记录与性能数据我在 ESP32-WROOM-32 上实测了一组数据。从提交表单到设备重启完成并连上新 WiFi整个过程约 8 到 12 秒其中 NVS 写入耗时不到 10 毫秒重启约 3 秒WiFi 连接约 5 到 8 秒。HTTP 页面加载时间在 AP 模式下约 200 毫秒完全可接受。功耗方面AP 模式比 STA 模式费电实测 AP 模式电流约 120mASTA 模式约 80mA。所以配置完成后一定要切回 STA 模式别一直挂着 AP。我的代码里是重启后自然切回 STA如果你不想重启也可以动态切换模式但要注意先关 HTTP 服务和 DNS 服务再切。6. 常见问题与排查技巧实录6.1 连不上 WiFi 的排查顺序设备连不上 WiFi 是最常见的问题我整理了一个排查顺序。先看串口日志确认是 NVS 读取失败还是连接超时。如果是 NVS 读取失败检查命名空间和键名有没有写错。如果是连接超时检查 SSID 和密码有没有多余空格特别是从网页表单复制粘贴的时候容易带空格。还有一个隐蔽的坑WiFi 密码里的特殊字符。如果密码里有、、这些字符在 HTTP POST 表单里需要 URL 编码否则server.arg拿到的数据是错的。Arduino 的WebServer库会自动解码但如果你自己拼 HTML 表单要注意enctype设置。现象可能原因解决办法串口报 NVS_NOT_FOUND首次上电NVS 为空正常进 AP 模式配置即可串口报 NVS_KEY_TOO_LONG命名空间或键名超 15 字符改短命名空间和键名连 WiFi 一直超时SSID/密码错误或信号弱检查凭据靠近路由器测试提交表单后无反应HTTP 路由未注册或方法不对确认server.on注册了 POST重启后还是 AP 模式NVS 写入未 commit检查prefs.end()是否调用6.2 NVS 数据读出来是乱码怎么办乱码通常有两个原因。一是读取缓冲区没初始化nvs_get_str只写入实际长度的数据如果你缓冲区里是脏数据超出部分就是乱码。解决办法是读取前memset清零。二是数据类型不匹配比如你用nvs_set_i32写进去却用nvs_get_str读那肯定乱。写入和读取的类型必须一致。提示调试 NVS 的时候可以用nvs_get_stats查看分区使用情况包括已用条目数、剩余空间等。这个 API 在排查分区满问题时特别有用。6.3 浏览器页面打不开的几种情况页面打不开先确认手机是不是真的连上了 ESP32 的热点。有些手机会自动切换到移动数据导致访问192.168.4.1走的是蜂窝网络。解决办法是在手机 WiFi 设置里关掉自动切换网络或者智能网络切换。如果热点连上了但页面还是打不开检查 HTTP 服务器有没有server.begin()以及端口是不是 80。还有一种情况是浏览器缓存了旧页面强制刷新或者换个浏览器试试。我遇到过 Chrome 缓存导致改了代码但页面没变的情况折腾了半小时才发现是缓存问题。6.4 几个我踩过的坑和独家技巧第一个坑AP 模式和 STA 模式不能同时用同一个 WiFi 库实例。如果你先WiFi.softAP再WiFi.begin行为是不确定的。正确做法是先WiFi.mode(WIFI_AP_STA)再分别配置或者干脆分时复用配置完重启。第二个技巧在配置页面加一个恢复出厂设置按钮。调用nvs_erase_all清空配置然后重启设备就回到 AP 模式。这个功能在设备换环境时特别有用不用重新烧录。第三个技巧把设备 IP 显示在串口和 OLED 上。如果你的设备带屏幕连上 WiFi 后把 IP 显示出来方便你找到它。没有屏幕的话串口打印也行至少调试的时候不用猜。第四个坑NVS 写入频率。有人在循环里不停写 NVS结果几个月就把分区写坏了。NVS 虽然有磨损均衡但也是有寿命的。配置类数据只在用户提交时写一次千万别在循环里写。7. 方案扩展与进阶玩法7.1 加上 mDNS 让设备有个好记的名字每次输 IP 太麻烦可以加 mDNS让设备有个域名。ESP32 的 Arduino 环境用ESPmDNS库两行代码就能注册一个esp32.local这样的名字。这样你在浏览器里输http://esp32.local就能访问配置页不用记 IP。不过 mDNS 在部分安卓手机上支持不好iOS 和电脑上没问题。#include ESPmDNS.h MDNS.begin(esp32); MDNS.addService(http, tcp, 80);7.2 配置项扩展不止 WiFiNVS 这套机制不只用来存 WiFi你可以把设备 ID、服务器地址、上报间隔、校准系数都存进去。配置页面相应扩展成多个输入框提交时一起写入。这样设备的所有可调参数都能通过浏览器改真正做到零重刷维护。我现在的项目里NVS 存了 WiFi 凭据、MQTT 服务器地址、设备别名、采样间隔四个配置项配置页面用一个大表单搞定。用户拿到设备后自己配一次之后除非换环境否则不用再动。7.3 远程配置的思路AP 模式配置需要人到设备旁边如果设备装在够不着的地方怎么办可以在 STA 模式下也开一个 HTTP 服务连上 WiFi 后通过局域网 IP 访问配置页。这样只要设备和你在同一个网络里就能远程改配置。更进一步可以接一个 MQTT 主题收到配置消息后写入 NVS 并重启实现真正的远程配置。不过远程配置要注意安全至少加个简单的 token 校验别让局域网里任何人都能改你的设备。我的做法是在 NVS 里存一个随机生成的 token配置请求必须带上正确的 token 才处理。7.4 和 OTA 升级的配合NVS 配置和 OTA 升级是绝配。OTA 升级固件不会动 NVS 分区所以升级后 WiFi 配置还在设备自动连网。这样你就能实现远程推固件、配置不丢失的完整闭环。ESP32 的 OTA 用ArduinoOTA库或者 ESP-IDF 的esp_https_ota组件都行升级前记得检查固件签名别把设备刷成砖。我现在的维护流程是设备出厂时烧一个带 AP 配置和 OTA 的基础固件客户自己配网。后续固件更新通过 OTA 推送配置存在 NVS 里不受影响。客户全程不用碰设备体验很好。这套 NVS 加浏览器配置的方案我从最早的硬编码一路迭代过来现在已经是所有 ESP32 项目的标配。它不复杂但确实解决了实际维护中的大麻烦。如果你还在用改代码重刷的方式改 WiFi真的建议花半天时间把这套机制加上后面省下的时间远超这点投入。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑