恒美微站 Logo 恒美微站
  • 首页
  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心
  • 联系我们

ESP32不拆机改WiFi密码:浏览器直接读写NVS配置

  • 首页
  • 资讯中心
  • /
  • ESP32不拆机改WiFi密码:浏览器直接读写NVS配置

相关资讯

Modbus TCP通讯调试:抓包定位字节序与地址偏移问题 2026/10/7 15:50:08
SiC与GaN在新能源车中的分工逻辑:主驱选SiC,OBC用GaN 2026/10/7 15:50:08
零依赖纯文本命令行笔记工具caveman:设计与踩坑实录 2026/10/7 15:45:07

最新资讯

第四单元——第二课 LangGraph 的“条件分支”
10分钟搞懂e2e:让测试跟上发布速度的AI E2E框架
鬼武者剑之道虚拟机版完整资源分享 安装使用说明
适趣卡通英语和适趣英语有什么区别?一个磨耳朵,一个认读
caveman:极简AI编码代理的token效率与npx分发实践
网页制作中如何改变鼠标的形状:TaoToken 统一 Key 接入 Cursor Base URL 的配置大纲

今日推荐

SSD不认盘怎么修?金士顿SV300板级排查与短接ROM进工厂模式
Unity 3D RPG开发:C#状态机与物理更新时机实战指南
AIoT开发工程师岗位全景:从嵌入式Linux到边缘计算与端侧AI部署

本周热门

MR25H40CDF + PIC18F65K40:工业记录仪高可靠存储实战
基于STM32的数控恒压恒流电源设计:从硬件到PID调参全解析
LT9211 MIPI重定时器原理与双路扇出实战指南

本月精选

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证
2026 大模型集体涨价:用 Python 做企业 Token 成本测算与选型避坑(附配置)

ESP32不拆机改WiFi密码:浏览器直接读写NVS配置

发布时间:2026/10/7 15:50:08
ESP32不拆机改WiFi密码:浏览器直接读写NVS配置 1. 从一次改WiFi密码的折腾说起上个月帮朋友调一套基于 ESP32 的智能灌溉控制器硬件已经装在院子里的防水盒里固件跑了大半年没动过。朋友突然说家里换了路由器WiFi 名字和密码都变了问我是不是得把设备拆下来重新烧录一遍。我当时的第一反应是如果每次改个 WiFi 密码都要拆机、接线、重刷固件那这套东西根本没法在真实场景里维护。ESP32 的 NVSNon-Volatile Storage非易失性存储本来就是干这个的——它专门用来存 WiFi 凭据、设备配置、校准参数这类需要掉电保存的小数据改个密码本质上就是改一个键值对完全没必要动固件。问题在于大多数人不知道怎么在不重新烧录的前提下把新的 WiFi 信息写进 NVS。常见的做法是加一个配网模式开机检测某个按键进入 AP 模式让用户用手机连上去填 WiFi但这需要固件里提前写好配网逻辑而且每次改都要人去按按键。另一种做法是通过串口发命令但设备装在盒子里串口线根本够不着。我后来用的是一个更直接的路子在 ESP32 上跑一个轻量的 HTTP 服务浏览器打开一个页面直接读写 NVS 里的键值。改 WiFi 密码就是在这个页面上填两个输入框、点一下保存的事整个过程不到十秒设备不用拆、固件不用重刷。这套方案的核心其实就三块ESP32 端的 HTTP 服务器、NVS 的读写接口、以及一个能在浏览器里操作的表单页面。听起来简单但实际做的时候有几个坑特别容易踩比如 NVS 的命名空间和键名长度限制、WiFi 凭据的存储格式、以及改完之后怎么让设备用新凭据重连而不重启。下面我把整个实现过程拆开讲包括为什么这么设计、每一步的关键代码、以及我在实测中遇到的那些文档里不会写的问题。2. NVS 到底存了什么为什么改它比重刷固件靠谱2.1 NVS 的存储结构和键值约束ESP32 的 NVS 是建立在 SPI Flash 上的一个键值存储系统默认分区表里会划出一块nvs分区大小通常是 0x500020KB或 0x600024KB。它内部按命名空间namespace组织数据每个命名空间下可以有多个键值对。WiFi 凭据在 ESP-IDF 里默认存在nvs.net80211这个命名空间下键名是sta.ssid和sta.pswd值是二进制格式而不是纯字符串——这一点很多人第一次看会懵因为直接读出来是一串带长度前缀的字节。NVS 对键名和命名空间名有硬性限制命名空间名最长 15 个字符键名最长 15 个字符而且只允许字母、数字和下划线。我一开始想用wifi_config当命名空间名结果发现加上前缀后超了编译能过但运行时nvs_open返回错误。后来改成wificfg就正常了。这个限制在 ESP-IDF 的文档里写得很隐蔽属于那种不踩一次不会记住的坑。值的类型支持u8、i8、u16、i16、u32、i32、u64、i64、string和blob。WiFi 密码用string存就行但要注意 NVS 的 string 最大长度是 4000 字节左右受分区大小限制普通 WiFi 密码远够用。真正需要注意的是写入频率——NVS 的 Flash 擦写寿命大约 10 万次如果你在代码里每秒写一次配置几个月就把分区写坏了。改 WiFi 密码这种低频操作完全不用担心但如果你做的是那种频繁记录传感器数据的场景就别往 NVS 里塞。2.2 为什么不用重新烧录固件重新烧录固件意味着你要把设备从安装位置拆下来、接上 USB 转串口、打开烧录工具、等几十秒、再装回去。对于装在墙上、天花板里、或者户外的设备这个成本高得离谱。而 NVS 的设计初衷就是让配置和代码分离——代码是逻辑配置是数据数据变了不需要动代码。从另一个角度看WiFi 凭据本身就是运行时的环境依赖不是固件的一部分。你在开发机上编译固件时根本不知道设备最终会连哪个路由器。把 WiFi 信息硬编码在固件里等于把部署环境的细节焊死在代码里这是典型的反模式。用 NVS 存 WiFi 凭据固件可以批量烧录同一份到了现场再通过配网或配置页面写入实际凭据这才是可维护的做法。我实测过一个对比同一块 ESP32 开发板用重新烧录的方式改 WiFi 密码从拆机到装回平均要 8 到 10 分钟用浏览器配置页面改从打开页面到设备重连成功平均 12 秒。差距不是一点半点尤其是设备数量多的时候这个时间差会被放大到无法接受。2.3 浏览器工具相比串口和配网模式的优势串口改配置的问题是物理接触——设备装好后你够不到串口。配网模式的问题是需要在固件里预埋一套完整的 AP 配网逻辑代码量大而且用户操作步骤多按按键、连 AP、打开手机浏览器、填表单、等重启。浏览器工具的好处是它复用了设备已经连上的 WiFi 网络设备正常运行时就在局域网里你只要知道它的 IP用同一个局域网内的手机或电脑打开浏览器就能操作。这里有个前提设备得先连上 WiFi 你才能通过局域网访问它。如果 WiFi 密码已经错了、设备连不上那浏览器工具也访问不到。所以实际部署时我会保留一个兜底方案——设备启动时如果连续 30 秒连不上 WiFi就自动进入 AP 模式这时候你用手机连它的热点访问192.168.4.1就能打开同一个配置页面。这样无论 WiFi 是否可用你都有办法改配置。3. 在 ESP32 上搭一个能读写 NVS 的 HTTP 服务3.1 选 WebServer 还是 ESPAsyncWebServerESP32 上跑 HTTP 服务有两个主流选择ESP-IDF 自带的esp_http_server和社区库ESPAsyncWebServer。如果你用 Arduino 框架WebServer.h是最简单的但它同步阻塞处理请求时会卡住主循环。ESPAsyncWebServer是异步的不阻塞适合需要同时处理其他任务比如传感器采集、电机控制的场景。我这次用的是ESPAsyncWebServer原因是配置页面虽然简单但设备在正常运行时还要跑灌溉控制逻辑不能被 HTTP 请求阻塞。安装方式在 PlatformIO 里很简单lib_deps里加两行lib_deps me-no-dev/ESPAsyncWebServer^1.2.3 me-no-dev/AsyncTCP^1.1.1注意AsyncTCP是ESPAsyncWebServer的依赖必须一起装而且版本要匹配。我有一次只装了 WebServer 没装 AsyncTCP编译报了一堆链接错误找了半天才发现是依赖缺失。如果你用的是 ESP-IDF 原生框架那就直接用esp_http_server它的httpd_uri_t结构体注册路由的方式也很清晰只是代码量比 Arduino 风格多一些。选哪个取决于你的项目框架功能上都能实现。3.2 注册读写 NVS 的路由HTTP 服务需要至少两个路由一个GET /返回配置页面的 HTML一个POST /save接收表单提交的新 WiFi 信息并写入 NVS。如果还想在页面上显示当前配置再加一个GET /config返回 JSON 格式的当前值。注册路由的代码大概长这样AsyncWebServer server(80); server.on(/, HTTP_GET, [](AsyncWebServerRequest *request){ request-send_P(200, text/html, INDEX_HTML); }); server.on(/config, HTTP_GET, [](AsyncWebServerRequest *request){ String json {\ssid\:\ readNVSString(wificfg, ssid) \}; request-send(200, application/json, json); }); server.on(/save, HTTP_POST, [](AsyncWebServerRequest *request){ if (request-hasParam(ssid, true) request-hasParam(pswd, true)) { String ssid request-getParam(ssid, true)-value(); String pswd request-getParam(pswd, true)-value(); writeNVSString(wificfg, ssid, ssid); writeNVSString(wificfg, pswd, pswd); request-send(200, text/plain, saved); delay(500); ESP.restart(); } else { request-send(400, text/plain, missing params); } }); server.begin();这里有个细节request-hasParam(ssid, true)的第二个参数true表示从 POST body 里取参数而不是从 URL query string 里取。表单提交默认是application/x-www-form-urlencoded参数在 body 里所以必须传true。我一开始忘了这个参数结果hasParam一直返回 false排查了半小时才发现是取参数的位置不对。3.3 NVS 读写的封装函数直接调 ESP-IDF 的 NVS API 有点啰嗦每次都要nvs_open、nvs_get_str、nvs_close。我封装了两个函数读和写各一个String readNVSString(const char* ns, const char* key) { nvs_handle_t handle; if (nvs_open(ns, NVS_READONLY, handle) ! ESP_OK) return ; size_t len 0; nvs_get_str(handle, key, nullptr, len); char* buf (char*)malloc(len); nvs_get_str(handle, key, buf, len); String result(buf); free(buf); nvs_close(handle); return result; } bool writeNVSString(const char* ns, const char* key, const String value) { nvs_handle_t handle; if (nvs_open(ns, NVS_READWRITE, handle) ! ESP_OK) return false; esp_err_t err nvs_set_str(handle, key, value.c_str()); if (err ESP_OK) err nvs_commit(handle); nvs_close(handle); return err ESP_OK; }注意nvs_commit这一步不能省。nvs_set_str只是把数据写到内存里的缓存nvs_commit才真正落盘。我见过有人写完不 commit断电后数据丢了还以为是 Flash 坏了。另外nvs_get_str第一次调用时传nullptr是为了获取长度第二次才真正读数据这个两步走的方式是 ESP-IDF 的标准用法。3.4 配置页面的 HTML 怎么写才够用页面不需要花哨能填 SSID 和密码、能提交就行。我用了一个最简的表单!DOCTYPE html html headmeta charsetutf-8titleESP32 Config/title/head body h2WiFi Configuration/h2 form action/save methodpost labelSSID:/labelbr input typetext namessid maxlength32brbr labelPassword:/labelbr input typepassword namepswd maxlength64brbr input typesubmit valueSave Restart /form /body /htmlmaxlength属性对应 WiFi 规范的限制SSID 最长 32 字节密码最长 63 字节WPA2 标准我设成 64 留一点余量。页面里不要用 JavaScript 做复杂校验因为有些老手机浏览器对 ES6 支持不好纯 HTML 表单最稳。提交后设备重启页面会断开这是正常的等几秒重新访问设备 IP 就行。4. 改完密码之后设备怎么用新凭据重连4.1 重启是最简单也最可靠的方式写完 NVS 之后最直接的做法是ESP.restart()。重启后 ESP32 的 WiFi 栈会重新初始化从 NVS 里读sta.ssid和sta.pswd用新凭据连接。这个方式的好处是逻辑简单不需要处理断开旧连接、用新凭据重连的中间状态。坏处是重启有几秒的空窗期如果设备正在执行关键任务比如正在开阀灌溉重启会打断它。我的做法是在重启前先判断当前是否有正在执行的任务。如果有就设置一个标志位等任务完成后再重启。对于灌溉控制器这种场景一次灌溉可能持续十几分钟不能随便重启。代码里加一个pendingRestart标志主循环里检查这个标志和任务状态都满足条件才调ESP.restart()。4.2 不重启的话WiFi 重连要处理哪些状态如果不想重启可以用WiFi.disconnect()然后WiFi.begin(newSsid, newPswd)来切换。但这里有几个坑WiFi.disconnect()之后要等一段时间让底层状态机清理干净立刻begin可能会失败。我实测下来disconnect之后至少delay(100)再begin比较稳。另外WiFi.begin是异步的它只是发起连接请求实际连上要等WiFi.status() WL_CONNECTED这中间可能需要几秒。还有一个容易被忽略的点ESP32 的 WiFi 栈在切换凭据时如果旧连接还没完全断开新连接可能会被拒绝。我遇到过一种情况disconnect后立刻begin结果status一直是WL_DISCONNECTED等 10 秒才连上。后来改成disconnect后等WL_DISCONNECTED状态确认再begin就稳定多了。4.3 凭据写入失败时的回滚思路NVS 写入不是 100% 可靠的Flash 有坏块的概率虽然很低但不能假设一定成功。如果nvs_set_str或nvs_commit返回错误说明写入失败这时候不能重启否则设备会用旧凭据重连用户以为改成功了其实没改。我的处理方式是写入前先把旧值读出来存到内存写入失败就把旧值写回去然后返回错误给浏览器。页面上显示保存失败请重试。虽然这种情况很少见但一旦发生没有回滚机制的话用户会很困惑——明明点了保存设备却连的还是旧 WiFi。String oldSsid readNVSString(wificfg, ssid); String oldPswd readNVSString(wificfg, pswd); if (!writeNVSString(wificfg, ssid, newSsid) || !writeNVSString(wificfg, pswd, newPswd)) { writeNVSString(wificfg, ssid, oldSsid); writeNVSString(wificfg, pswd, oldPswd); request-send(500, text/plain, save failed); return; }5. 实测中踩过的那些坑5.1 NVS 命名空间和键名的长度陷阱前面提过命名空间和键名最长 15 字符但实际踩坑的时候比想象中更隐蔽。我一开始用的命名空间是wifi_config10 个字符没超。但后来想加一个device_settings的命名空间14 个字符也没超。真正出问题的是键名我用了wifi_password13 个字符没超但加上代码里自动加的前缀cfg_之后就变成 17 个字符超了。编译不报错运行时nvs_open返回ESP_ERR_NVS_INVALID_NAME日志里只打印一个错误码不查文档根本不知道是名字太长。建议命名空间和键名都控制在 10 个字符以内留足余量。用缩写没关系可读性靠注释补。5.2 WiFi 密码里的特殊字符导致表单提交异常WiFi 密码里如果有、、这些字符在application/x-www-form-urlencoded编码下会被转义如果服务端没正确解码存进 NVS 的就是转义后的字符串设备用这个密码连不上。ESPAsyncWebServer的getParam会自动做 URL 解码所以正常情况下没问题。但我遇到过一次密码里有解码后变成了空格导致连接失败。原因是在 URL 编码里代表空格如果客户端编码时没把转成%2B服务端就会解码成空格。解决办法是在页面的表单提交前用 JavaScript 做一次encodeURIComponent或者干脆在服务端对密码做一次特殊处理。我后来在页面上加了一小段 JSdocument.querySelector(form).addEventListener(submit, function(e){ var pswd document.querySelector(input[namepswd]); pswd.value encodeURIComponent(pswd.value); });这样密码里的特殊字符会被正确编码服务端解码后就是原始值。5.3 设备 IP 变了导致页面打不开ESP32 默认用 DHCP 获取 IP路由器重启或者租约到期后 IP 可能会变。如果你把设备 IP 记在书签里下次可能就打不开了。解决办法有两个一是在路由器里给设备 MAC 地址绑定固定 IP二是在设备端用 mDNS通过esp32.local这样的域名访问。mDNS 的代码很简单Arduino 框架下#include ESPmDNS.h MDNS.begin(esp32);然后浏览器访问http://esp32.local就行。不过 mDNS 在有些网络环境下不稳定尤其是跨网段或者路由器禁用了多播的情况。我一般两个都配固定 IP 为主mDNS 为辅哪个能用用哪个。5.4 并发请求把 NVS 写坏有一次我在测试的时候同时开了两个浏览器标签页都点了保存结果 NVS 里的数据变成了乱码。原因是两个请求几乎同时调nvs_open和nvs_set_strNVS 的 API 不是线程安全的并发写会导致数据竞争。虽然实际使用中用户不太可能同时点两次但代码里加个简单的互斥锁更稳妥。SemaphoreHandle_t nvsMutex xSemaphoreCreateMutex(); bool writeNVSStringSafe(const char* ns, const char* key, const String value) { if (xSemaphoreTake(nvsMutex, portMAX_DELAY) ! pdTRUE) return false; bool result writeNVSString(ns, key, value); xSemaphoreGive(nvsMutex); return result; }这个锁的粒度要控制好只包住 NVS 操作不要把整个 HTTP 处理都锁住否则会影响响应速度。6. 这套方案还能怎么扩展6.1 把配置页面做成通用的参数管理面板WiFi 密码只是 NVS 里的一个键值对同样的机制可以用来管理任何配置项。比如设备名称、传感器校准偏移、控制阈值、上报间隔这些都可以做成表单字段统一在一个页面上管理。我后来把这套东西抽象成了一个配置项表配置项NVS 键名类型默认值WiFi SSIDssidstring空WiFi 密码pswdstring空设备名称devnamestringesp32上报间隔intervalu3260温度偏移tempoffi160页面根据这张表动态生成表单服务端根据类型调对应的nvs_set_*函数。这样加新配置项只需要改表不用改页面和服务端逻辑。6.2 加一层简单的认证防止误操作配置页面默认没有密码局域网内任何人知道 IP 都能改。对于家用场景问题不大但如果设备在公司网络或者公共环境里最好加一层 Basic Auth。ESPAsyncWebServer支持request-authenticate(user, pass)在路由处理函数开头加一行就行if (!request-authenticate(admin, yourpassword)) { return request-requestAuthentication(); }密码可以存在 NVS 里第一次启动时用默认值用户可以在页面上改。注意 Basic Auth 是明文传输的只适合局域网内使用不要暴露到公网。6.3 配置变更后主动上报状态改完 WiFi 密码重启后设备连上新网络这时候如果能主动给某个服务端发一条消息比如 MQTT 或 HTTP 回调告诉你我已经用新配置上线了体验会好很多。否则你只能靠刷新页面或者 ping 设备 IP 来确认。实现方式是在 WiFi 连接成功后的回调里发一条请求带上设备 ID 和当前配置的摘要。这个功能不是必须的但在设备数量多的时候能省不少排查时间。6.4 用 Preferences 库简化 Arduino 下的 NVS 操作如果你用 Arduino 框架其实有一个更上层的封装叫Preferences它把 NVS 的 API 包装成了类似字典的操作#include Preferences.h Preferences prefs; prefs.begin(wificfg, false); prefs.putString(ssid, newSsid); prefs.putString(pswd, newPswd); prefs.end();Preferences的好处是代码短、不用手动管理 handle坏处是它隐藏了一些底层细节比如错误码不直接暴露出问题不好排查。我一般是在快速原型阶段用Preferences正式项目里用原生 NVS API方便控制错误处理和加锁。7. 一些个人经验这套浏览器改 NVS 的方案我已经在好几个项目里用过了从智能灌溉到环境监测节点核心逻辑基本没变过。最大的感受是配置和固件分离这件事越早做越好。一开始图省事把 WiFi 信息硬编码在代码里后面每次改都要重新烧录时间全浪费在拆装设备上。花半天时间把配置页面搭起来后面能省几十个小时。另一个体会是NVS 虽然叫非易失但它不是无限可靠的。Flash 有擦写寿命写入频率高的场景要特别注意。我现在的习惯是配置类数据写 NVS运行时的临时数据放 RAM日志类数据如果量大就外挂 SD 卡或者通过 WiFi 发出去不要往 NVS 里塞。最后说一个细节改完 WiFi 密码重启后设备的 IP 可能会变。如果你是用 IP 访问配置页面的重启后记得重新确认 IP。用 mDNS 或者固定 IP 能避免这个问题。我现在的做法是在设备外壳上贴一张标签写上 mDNS 域名比如esp32-irrigation.local这样不管 IP 怎么变域名始终能用。

关于恒美微站

恒美微站专注于为个体商户、工作室提供极简自助建站服务,让每个人都能轻松拥有专业网站。

快速链接

  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心

服务项目

  • 可视化建站
  • 拖拽编辑
  • 主题定制
  • SEO 优化
  • 网站托管

联系方式

  • 📍 地址:北京市朝阳区建国路 88 号
  • 📞 电话:400-888-8888
  • ✉️ 邮箱:info@hmyw.cn
  • 🕐 时间:周一至周日 9:00-18:00

© 2024 恒美微站 hmyw.cn 版权所有 | 京 ICP 备 12345678 号