Arduino ESP32Web配网(二)

Arduino ESP32Web配网(二)


相对于之前的那篇文章,这篇的配网有不同的地方就是,html页面修饰了一下。
在这里插入图片描述
在这里插入图片描述

配网操作

1.如果是第一次配网,就可以省略第一步,如果是清空WIFI信息请接下来看这一步。

还是采用的GPIO 0引脚下拉作为清除nvs数据的引脚。我是采用一根导线串联1K的电阻进行与GND短接,如果是串联的是10K的电阻,短接到GND是没有反应的。为什么串电阻短接到地,直接短接到GND不行吗,也可以,为了保护一下引脚。(注意上面的那种板子的GPIO 0引脚是没有引出来的,需要条线进行短接,当然你可以跟换引脚定义也可以,根据个人需求)

  1. 连接配网wifi
    在这里插入图片描述

点击连接,即可自动跳转到打浏览器并进入到配网界面

在这里插入图片描述

点击保存并连接后,串口打印信息。

在这里插入图片描述
3. 配网成功后,只要不主动去清除wifi信息,nvs数据中将永远保存该信息。
4. 主动清空nvs数据操作,非必须项。

从下图可以看出,清空前,nvs容量是415,清空后的容量变大了,变成了421,清空数据后会重启,自动进入到配网模式。

在这里插入图片描述

自动配网源码(采用的VSCode Platform IO进行编译的)

#include <Arduino.h>
#include <WiFi.h>
//#include <WiFiUdp.h>
#include <HTTPClient.h>
#include <Preferences.h>
#include "SetWiFi.h" //Web配网
#include <esp_wifi.h>     //用于esp_wifi_restore() 删除保存的wifi信息
String PrefSSID, PrefPassword, cityCode;
//在VSCode Platform IO里面,板子led灯需要注释掉,不然报警告,在ArduinoIDE中需要启用这个宏定义,不然找不到该定义的错误信息产生。
#define LED_BUILTIN  (2)    //板载led灯

#define NTP1 "ntp1.aliyun.com"
#define NTP2 "ntp2.aliyun.com"
#define NTP3 "ntp3.aliyun.com"
unsigned long wifiTimes = 0;
bool ledState = LOW;
const unsigned long interval = 6000UL; //设置延时时间间隔
unsigned long previousMillis = 0;
//const char* ssid = "MERCURY_D268G";
//const char* password = "pba5ayzk";
//强制门户Web配网
const int resetPin = 0;                    //设置重置按键引脚,用于删除WiFi信息
bool setWiFi_Flag = false;

String NowTime();
void setWiFi()
{
  initSoftAP();
  initWebServer();
  initDNS();
  while (setWiFi_Flag == false)
  {
    server.handleClient();
    dnsServer.processNextRequest();
    if (WiFi.status() == WL_CONNECTED)
    {
      server.stop();
      setWiFi_Flag = true;
    }
  }
}
//删除保存的wifi信息
void DeleteWiFi(){
  Preferences prefs;
    prefs.begin("wifi",false);//为false才能删除键值
    Serial.println(prefs.freeEntries());//查询清除前的剩余空间
    prefs.remove("ssid"); // 删除当前命名空间中键名为"ssid"的元素
    prefs.remove("password"); // 删除当前命名空间中键名为"ssid"的元素
    prefs.clear();
       delay(500);
       Serial.println(prefs.freeEntries());//查询清除后的剩余空间
       prefs.end();
       esp_wifi_restore();  //删除保存的wifi信息
       Serial.println("连接信息已清空,准备重启设备..");
  }
  
void setup()
{
  Serial.begin(115200);
  pinMode(LED_BUILTIN, OUTPUT); //板载led灯作为指示
 pinMode(resetPin, INPUT_PULLUP);      //按键上拉输入模式(默认高电平输入,按下时下拉接到低电平)
  //首次使用自动进入配网模式,读取NVS存储空间内的ssid、password和citycode
  Preferences prefs;
  prefs.begin("wifi");
  if (prefs.isKey("ssid"),"nano")
    PrefSSID = prefs.getString("ssid","nano");//如果键值为空,返回0
  
  if (prefs.isKey("password"))
    PrefPassword = prefs.getString("password");

  if (prefs.isKey("citycode"))
    cityCode = prefs.getString("citycode");

  prefs.end();//从nvs获取到wifi信息后,关闭Preferences

  if (PrefSSID == "nano")
  {
    setWiFi();
  }else{
    Serial.println(PrefSSID);
  Serial.println(PrefPassword);
    Serial.println(cityCode);
  WiFi.mode(WIFI_STA);//切换为STA模式,进行入网
  WiFi.begin(PrefSSID.c_str(), PrefPassword.c_str());
  Serial.println("正在连接" + PrefSSID + "...Connecting to WiFi...");
  Serial.println("-------------");
  }
  byte i = 0;
  while (WiFi.status() != WL_CONNECTED)
  {   
    i++;
    Serial.print('.');
    delay(500);

    if (i > 10)
    {
      setWiFi();
    }
     digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN)); //板载led灯闪烁
  }
  Serial.printf("SSID:%s\r\n", WiFi.SSID().c_str());
  Serial.printf("PSW:%s\r\n", WiFi.psk().c_str());
  Serial.println(WiFi.localIP());
  configTime(8 * 3600, 0, NTP1, NTP2, NTP3);
}

void loop()
{

  unsigned long currentMillis = millis();
  if (currentMillis - previousMillis >= interval)
  {
    ledState = !ledState; //状态翻转
    digitalWrite(LED_BUILTIN, ledState);

    struct tm timeInfo; //声明一个结构体
    if (!getLocalTime(&timeInfo))
    { //一定要加这个条件判断,否则内存溢出
      Serial.println("Failed to obtain time");
    }
    Serial.println(&timeInfo, "%F %T %A");
    previousMillis = currentMillis;
  }
  if(!digitalRead(resetPin)){
        delay(3000);
        if(!digitalRead(resetPin)){ //1Kde 下来电阻,10K的拉不动  
           Serial.println("\n按键已长按3秒,正在清空NVS保存的信息.");  
           DeleteWiFi();    //删除保存的wifi信息 
           ESP.restart();    //重启复位esp32
           Serial.println("已重启设备.");
        }      
     }
}

  • SetWiFi.h代码
#include <WiFi.h>
#include <DNSServer.h>
#include <WebServer.h>
#include <Preferences.h>
const char* AP_NAME = "ESP32WiFiAP";//Web配网模式下的AP-wifi名字
extern String PrefSSID, PrefPassword,cityCode; 

//暂时存储wifi账号密码
char sta_ssid[32] = {0};
char sta_password[64] = {0};
char sta_citycode[32] = {0};

//配网页面代码 
String page_html = R"(
<!DOCTYPE html>
<html lang='en'>
  <head>
    <meta charset='UTF-8'>
   
    <meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,minimum-scale=1.0,user-scalable=no">
    <title>ESP32参数配置页面</title>
    <style type="text/css">
      * { margin: 0; padding: 0; }
       html { height: 100%; }
       h2 {text-align: center;color: #fff;line-height: 2.2;}
       body { height: 100%; background-color: #1F6F4A; 50% 50% no-repeat; background-size: cover;}
       .dowebok { position: absolute; left: 50%; top: 30%; width: 380px; height: 500px; margin: -200px 0 0 -200px; border: 3px solid #fff; border-radius: 10px; overflow: hidden;}
       
       .form-item { position: relative; width: 360px; margin: 0 auto; padding-bottom: 20px;}
       .form-item input { width: 288px; height: 48px; padding-left: 10px; border: 1px solid #fff; border-radius: 25px; font-size: 18px; color: #fff; background-color: transparent; outline: none;}
       .send_button { width: 360px; height: 50px; border: 0; border-radius: 25px; font-size: 18px; color: #1f6f4a; outline: none; cursor: pointer; background-color: #fff; }
       
       .tip { display: none; position: absolute; left: 20px; top: 52px; font-size: 14px; color: #f50; }
       .reg-bar { width: 360px; margin: 20px auto 0; font-size: 14px; overflow: hidden;}
       .reg-bar a { color: #fff; text-decoration: none; }
       .reg-bar a:hover { text-decoration: underline; }
       .reg-bar .reg { float: left; }
       .reg-bar .forget { float: right; }
       .dowebok ::-webkit-input-placeholder { font-size: 18px; line-height: 1.4; color: #fff;}
       .dowebok :-moz-placeholder { font-size: 18px; line-height: 1.4; color: #fff;}
       .dowebok ::-moz-placeholder { font-size: 18px; line-height: 1.4; color: #fff;}
       .dowebok :-ms-input-placeholder { font-size: 18px; line-height: 1.4; color: #fff;}
        
       @media screen and (max-width: 500px) {
       * { box-sizing: border-box; }
       .dowebok { position: static; width: auto; height: auto; margin: 0 30px; border: 0; border-radius: 0; }
       .logo { margin: 50px auto; }
       .form-item { width: auto; }
       .form-item input, .form-item button, .reg-bar { width: 100%; }
       }
       
    </style>
  </head>
  
  <body>
    <div class="dowebok">
      <h2>参 数 配 置</h2>
      <form style='text-align: center;padding-top: 20px' name='input' action='/' method='POST'>  
         <div class="form-item">
          <input id="username" type="text" name='ssid' autocomplete="off" placeholder="WiFi名称">
         </div>
         <div class="form-item">
          <input id="password" type="password" name='password' autocomplete="off" placeholder="WiFi密码">
         </div>
         <div class="form-item">
          <input id="citycode" type="citycode" name='citycode' autocomplete="off" placeholder="城市代码,留空则自动定位获取">
         </div>
         <div class="form-item">
           <div id="">
            <input id="send_button" type='submit' value='保存并连接'>
           </div>
        </div>
        <div class="form-item">
          <div class="user_text">
            <br>
            <p><h3>如何获取cityCode:</h3></p>
              <h5>
                1、城市代码由9位阿拉伯数字组成,超过位数会导致获取不到数据而无限重启
              </h5>
            </p>
          </div>
         </div>
        
      </form> 
     </div>
  </body>
</html>
)";

const byte DNS_PORT = 53;//DNS端口号
IPAddress apIP(192, 168, 4, 1);//esp32-AP-IP地址
DNSServer dnsServer;//创建dnsServer实例
WebServer server(80);//创建WebServer

void handleRoot() {//访问主页回调函数
  server.send(200, "text/html", page_html);
}
void initSoftAP(void){//初始化AP模式
  WiFi.mode(WIFI_AP);
  WiFi.softAPConfig(apIP, apIP, IPAddress(255, 255, 255, 0));
  Serial.println(WiFi.softAPIP());
//    Serial.print("本地IP: ");
//  Serial.println(WiFi.localIP());
  if(WiFi.softAP(AP_NAME)){
    Serial.println("ESP32 SoftAP is right");
  }
}

void initDNS(void){//初始化DNS服务器
  if(dnsServer.start(DNS_PORT, "*", apIP)){//判断将所有地址映射到esp8266的ip上是否成功
    Serial.println("start dnsserver success.");
  }
  else Serial.println("start dnsserver failed.");
}
void handleRootPost() {//Post回调函数
String wifiid="",wifipass="",cityid="";
  Serial.println("handleRootPost");
  if (server.hasArg("ssid")) {//判断是否有账号参数
    Serial.print("got ssid:");
    strcpy(sta_ssid, server.arg("ssid").c_str());//将账号参数拷贝到sta_ssid中
    Serial.println(sta_ssid);
  } else {//没有参数
    Serial.println("error, not found ssid");
    server.send(200, "text/html", "<meta charset='UTF-8'>提示:请输入WiFi名称");//返回错误页面
    return;
  }
  //密码与账号同理
  if (server.hasArg("password")) {
    Serial.print("got password:");
    strcpy(sta_password, server.arg("password").c_str());
    Serial.println(sta_password);
  } else {
    Serial.println("error, not found password");
    server.send(200, "text/html", "<meta charset='UTF-8'>提示:请输入WiFi密码");
    return;
  }


  if (server.hasArg("citycode")) {
    Serial.print("got citycode:");
    strcpy(sta_citycode, server.arg("citycode").c_str());
    Serial.println(sta_citycode);
  } else {
    Serial.println("error, not found citycode");
    server.send(200, "text/html", "<meta charset='UTF-8'>提示:请输入城市代码");
    return;
  }
    Preferences prefs; 
  prefs.begin("wifi");
  wifiid=sta_ssid;wifipass=sta_password;cityid=sta_citycode;
  prefs.putString( "ssid" ,wifiid);
  prefs.putString( "password", wifipass);
  prefs.putString( "citycode", cityid);
  prefs.end();

  server.send(200, "text/html", "<meta charset='UTF-8'><h1>保存成功,ESP32重启中...</h1>");//返回保存成功页面
  delay(2000);
  //连接wifi
  //connectNewWifi();

  ESP.restart(); //重启ESP32
}
void initWebServer(void){//初始化WebServer
  //server.on("/",handleRoot);
  //上面那行必须以下面这种格式去写否则无法强制门户
  server.on("/", HTTP_GET, handleRoot);//设置主页回调函数
  server.onNotFound(handleRoot);//设置无法响应的http请求的回调函数
  server.on("/", HTTP_POST, handleRootPost);//设置Post请求回调函数
  server.begin();//启动WebServer
  Serial.println("WebServer started!");
}
void connectNewWifi(void){
Preferences prefs; 
  prefs.begin("wifi");
    if(prefs.isKey("ssid")) 
  PrefSSID =  prefs.getString("ssid");
   Serial.println(PrefSSID);
    if(prefs.isKey("password")) 
  PrefPassword =  prefs.getString("password");
    Serial.println(PrefPassword);
       if(prefs.isKey("citycode")) 
   cityCode = prefs.getString("citycode");
    Serial.println(cityCode);
  prefs.end();
 // WiFi.mode(WIFI_STA);
 WiFi.begin(PrefSSID.c_str(), PrefPassword.c_str());
   
  WiFi.mode(WIFI_STA);//切换为STA模式
  WiFi.setAutoConnect(true);//设置自动连接
  //  WiFi.begin(PrefSSID.c_str(), PrefPassword.c_str());//连接上一次连接成功的wifi
  Serial.println("");
  Serial.print("Connect to wifi");
  int count = 0;
   while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    count++;
    if(count > 20){//如果10秒内没有连上,就开启Web配网 可适当调整这个时间
      initSoftAP();
      initWebServer();
      initDNS();
      break;//跳出 防止无限初始化
    }
    Serial.print(".");
  }
  Serial.println("");
  if(WiFi.status() == WL_CONNECTED){
    Serial.println("WIFI Connected!");
    Serial.print("IP address: ");
    Serial.println(WiFi.localIP());
    server.stop();
  }
}

代码还可以优化,只是演示功能实现,网页配网页面可以添加其他参数配置选项,这里只是做演示,代码是从其他项目里面移植出来的功能。优化后,可以作为其他项目的功能模块来使用。

  • 6
    点赞
  • 50
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
在上一期:高性价比WIFI图传方案快速入门教程 我们可以看到确实这个图传模块可以实现高性价比的图传方案,但是问题又来了,上一期的wifi摄像头连上的是局域网的热点,并且局域网的热点和密码是写死在程序里面的,所以让这个图传模块连上网络似乎比较头疼。 有一篇CSDN的博客论述了WIFI模块各种常见的配网方式说明及其比较,证明了最好的配网方式是WEB配网,反正个人觉得微信配网很麻烦,因为要使用微信,没有微信就没有办法了。在这一期项目中:将要实现一个WEB配网方式,通过ESP32-CAM先创建一个知道账号和密码的AP热点“daodanjishui”,然后用手机WIFI通过输入密码asdfghjkl连上这个热点,再用该手机打开浏览器,在浏览器网址输入栏输入192.168.4.1(热点的IP是固定的,如果想在局域网实现DNS域名解析,请看我:ESP32-CAM、ESP8266、WIFI、蓝牙、单片机、热点创建嵌入式DNS服务器(姐妹篇)),输入网址之后就可以看到保存在ESP32配网主页(如果想编写监控主页的话,请看我:ESP32-CAM、ESP8266、WIFI、蓝牙、摄像头设备实现嵌入式服务器点灯(原理图+源码)),在主页输入要链接的无线WIFI账号和密码 ,点击连接即可。连上Internet之后,自然可以轻松地发送get请求,最后请求"心知天气"的服务器(如果想自己搭建私人服务器的话,请看我:ESP8266作为客户端发送get请求和post请求的电路方案设计(原理图+程序))返回北京的天气预报(如果想实现私人天气报警的话,请看我:基于stm32、Android和Html的温湿度监控系统的设计(原理图+源码);如果想实现上传天气参数给私人服务器的话,请看我:ESP8266上传DHT11数据给私人javaweb服务器实现网页查询数据的电路方案(电路图+源码))给ESP32模块,最后ESP32-CAMM模块通过串口打印出来天气情况(如果想把串口打印的消息输出到网页,请看我:ESP32-CAM高性价比温湿度监控系统 和 ESP32-CAM无线监控智能网关的设计与实现)。 下面是实际演示过程: (1)模块上电,通过ESP32-CAM先创建一个知道账号和密码的AP热点“daodanjishui”,再输入密码连上去。 (2)打开该手机的随便一个浏览器,在浏览器输入IP:192.168.4.1(这个热点IP是不会变的,所以不用看串口打印的IP地址就可以写对),确定就跳转到一个配网主页。 (3)在配网主页输入需要连接路由器wifi和密码,点击连接 (4)如果密码正确,ESP32会在串口打印出有关网络信息(箭头1),并且利用该网络发送get请求(箭头2)。用户可以修改为LED指示灯也是没有问题的。 (5)互联网上的“心知天气”服务器收到该ESP32-CAM发送的get请求,就会把json格式的天气预报信息返回给ESP32-CAM,ESP32-CAM再将该信息打印到串口上(不想让程序复杂化,这里我没有做json字符串解析) 项目真源码截图: 总结:这套代码可以学到图传摄像头ESP32-CAM WEB配网、AP模式发布配网主页到手机浏览器、嵌入式服务器获取表单数据、STA模式连上Internet、发送GET请求给远程服务器获取天气预报。免费版的代码是没有注释的,收费版是有详细注释的,随君选择。本项目涉及到的有关项目都是原创为主,不是什么51单片机点灯之类程序,挑选的是比较新的前沿的设备进行创作。程序源码都经过严格测试,教程也详细到位,从而保证了含金,也欢迎广大电子爱好者和同学们采用相关的方案做电子设计、毕业设计或diy创作。 在本项目中放入的快捷链接,同样是精心准备的电路设计方案,都是软件和硬件结合联合调试的结晶,欢迎买家免费观看和了解。 后期将加入一些娱乐、玩耍和科幻创作的元素,敬请期待。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值