基于ESP32搭建物联网服务器十(用LittleFS保存设置和从LittleFS读取设置[下])

前文中已经详细地介绍了用LittleFS保存设置和从LittleFS读取设置所用到的函数和方法:

https://blog.csdn.net/m0_50114967/article/details/126976629

因为这个WEB服务器功能越来越多,所有代码都在同一个文件,可读性也会变得越来越差,这里先介绍一下arduino IDE的多文件管理功能。

arduino IDE保留了和C语言一样的引入.h头文件的功能,但也有自已独特的多文件管理功能。

引入头文件的方法

我们先新建一个用于测试的.h头文件,test.h

 文件的内容为

void hardware_begin(){
  Serial.begin(9600);
}

里面包含一个hardware_begin函数,下面在主文件中引用

#include "test.h"
void setup() {
  // put your setup code here, to run once:
  hardware_begin();
  Serial.print("abcd");
}

void loop() {
  // put your main code here, to run repeatedly:

}

无需引入头文件的方法

这是arduino IDE特有的多文件管理,用相同的方法,新建一个文件,无需输入扩展名或输入.ino扩展名。

 

内容和之前的test.h文件一样:

void hardware_begin(){
  Serial.begin(9600);
}

 在引用时,无需引入该头件就可以直接调用test.ino文件里的函数

//#include "test.h" 无需引入头文件
void setup() {
  // put your setup code here, to run once:
  hardware_begin();
  Serial.print("abcd");
}

void loop() {
  // put your main code here, to run repeatedly:

}

其原理就是在编译时会把项目文夹下的所有ino文件合并编译,基本上不用担心文件名。

在测试中,把setup和loop分开放在不同的文件中也是可以的。对比引入头文件,显然后面的方法比较方便。

现在我们可以把之前所实现的功能做一些分类整理,把各种类型的函数分别放到不同的文件中。

第一个文件用来保存配网所需要的函数:

/*WIFI连接*/
#include <WiFi.h>
#include <DNSServer.h>


DNSServer dnsserver;


/***********************************************************************************
 * 函数:连接WIFI
 * ssid:        WIFI名称
 * password:    WIFI密码
 * return:      连接成功返回true
***********************************************************************************/
void connect_WIFI(String ssid, String password){
  WiFi.begin(ssid.c_str(), password.c_str());               //连接WIFI
  Serial.print("连接WIFI");
  //循环,10秒后连接不上跳出循环
  int i = 0;
  while(WiFi.status() != WL_CONNECTED){
    Serial.print(".");
    delay(500);
    i++;
    if(i>20){
      Serial.println();
      Serial.println("WIFI连接失败");
      return;
    }
  }
  Serial.println();
  IPAddress local_IP = WiFi.localIP();
  Serial.print("WIFI连接成功,本地IP地址:"); //连接成功提示
  Serial.println(local_IP); 
}


/***********************************************************************************
 * 设置AP和STA共存模式,设置DNS服务器
***********************************************************************************/
void connect_NET(){
  const byte DNS_PORT = 53;                     //DNS端口
  const String url = "ESPAP.com";               //域名
  IPAddress APIp(10,0,10,1);                    //AP IP
  IPAddress APGateway(10,0,10,1);               //AP网关
  IPAddress APSubnetMask(255,255,255,0);        //AP子网掩码
  const char* APSsid = "esp32_AP";              //AP SSID
  const char* APPassword = "12345678";          //AP wifi密码
  wifi_connect();                               //连接WIFI
  WiFi.mode(WIFI_AP_STA);                             //打开AP和STA共存模式
  WiFi.softAPConfig(APIp, APGateway, APSubnetMask);   //设置AP的IP地址,网关和子网掩码
  WiFi.softAP(APSsid, APPassword, 6);                 //设置AP模式的登陆名和密码
  dnsserver.start(DNS_PORT, url, APIp);               //设置DNS的端口、网址、和IP
  Serial.print("AP模式IP地址为:");
  Serial.println(WiFi.softAPIP());
}


/***********************************************************************************
 * DNS处理请求的循环
***********************************************************************************/
void DNS_request_loop(){
	dnsserver.processNextRequest();
}

第二个文件用来保存WEB所需要的函数,包含了读写文件函数,JSON格式解析或创建函数

#include "ESPAsyncWebServer.h"
#include <ArduinoJson.h>
#include <LittleFS.h> 

//AsyncWebServer server(80);										//创建一个服务器对象,WEB服务器端口:80


/**************************************************************************************
 * 函数:字符串写入文件,文件如果存在,将被清零并新建,文件不存在,将新建该文件
 * path:    文件的绝对路径
 * str:     要写入的字符串
 *************************************************************************************/
void str_write(String path, String str){
    Serial.println("写入文件");
    File wf = LittleFS.open(path,"w");                           //以写入模式打开文件
    if(!wf){                                                     //如果无法打开文件
      Serial.println("打开文件写入时错误");  //显示错误信息
      return;                                                    //无法打开文件直接返回
    }
    wf.print(str);                                               //字符串写入文件
    wf.close();                                                  //关闭文件
    File rf = LittleFS.open(path,"r");                           //以读取模式打开文件
    Serial.print("FILE:");Serial.println(rf.readString());       //读取文件
    rf.close();                                                  //关闭文件 
}


 /**********************************************************************************
  * 函数:响应网站/setwifi目录的POST请求,收到请求后,运行get_WIFI_set_CALLback回调函数
  * 获取并格式化收到的POST数据
  *********************************************************************************/
void get_WIFI_set_CALLback(AsyncWebServerRequest *request){
   Serial.println("收到设置WIFI按钮");
  if(request->hasParam("wifiname",true)){
    AsyncWebParameter* wifiname = request->getParam("wifiname",true);					    //获取POST数据
    AsyncWebParameter* wifipassword = request->getParam("wifipassword",true);			//获取POST数据
    String wn  = wifiname->name().c_str();
    String wnv = wifiname->value().c_str();
    String wp  = wifipassword->name().c_str();
    String wpv = wifipassword->value().c_str();
    //把SSID和password写成一个JSON格式
    StaticJsonDocument<200> wifi_json;                                            //创建一个JSON对象,wifi_json
    wifi_json[wn] = wnv;                                                          //写入一个建和值
    wifi_json[wp] = wpv;                                                          //写入一个键和值
    String wifi_json_str;                                                         //定义一个字符串变量
    serializeJson(wifi_json, wifi_json_str);                                      //生成JOSN的字符串
    str_write("/WIFIConfig.conf",wifi_json_str);                                  //字符串写入
  }
}


/**********************************************************************************
 * 函数:从文件path中读取字符串
 * path:      文件的绝对路径
 * return:    返回读取的字符串
 *********************************************************************************/
String str_read(String path){
    Serial.println("读取文件");
    File rf = LittleFS.open(path,"r");                 //以读取模式打开文件
    if(!rf){                                           //如果无法打开文件
      Serial.println("打开文件读取时错误");               //显示错误信息
      return "";                                       //无法打开文件直接返回
    }
    String str = rf.readString();                      //读取字符串
    rf.close();                                        //关闭文件 
    return str;
}


/***************************************************************************************
 * 函数:解析JSON字符串,从JSON字符串名称得到该值
 * str:   JSON字符串
 * Name:  JSON集合的名称
 * return: 返回值的字符串
 ***************************************************************************************/
String analysis_json(String str, String Name){
  DynamicJsonDocument doc(str.length()*2);    //定义一个JSON对象
  deserializeJson(doc, str);                  //反序列数据
  String value = doc[Name].as<String>();      //从Name中读取对应的值
  return value;
}


/***********************************************************************************
 * 函数:/WIFIConfig.conf文件中读取设置数据并连接WIFI
***********************************************************************************/
void wifi_connect(){
  Serial.println("在conf文件中读取数据并连接WIFI");
  String str = str_read("/WIFIConfig.conf");                  //读取文件内容
  String wifiname = analysis_json(str, "wifiname");           //解析WIFI名称
  String wifipassword = analysis_json(str, "wifipassword");   //解析WIFI密码
  connect_WIFI(wifiname, wifipassword);                       //连接WIFI
}


/***********************************************************************************
 * web服务器初始化
***********************************************************************************/
void web_server(){
  Serial.println("初始化WEB服务器");
  server.serveStatic("/", LittleFS, "/").setDefaultFile("index.html");			//响应网站根目录的GET请求,返回文件index.html
  server.on("/setwifi"  ,HTTP_POST , get_WIFI_set_CALLback);                //响应设置WIFI按钮的请求
  server.begin();                       										                //初始化
}


/********************************************************************************
 * LittleFS文件系统初始化
 *********************************************************************************/
void LittleFS_begin(){
  Serial.println();
  Serial.println("初始化文件系统");
  if(!LittleFS.begin(true)){
    Serial.println("An Error has occurred while mounting LittleFS");
    return;
  }
}

第三个文件就是主文件,包括了ESPAsyncWebServer库的全局服务器变量和setup和loop两个接口

#include "ESPAsyncWebServer.h"
AsyncWebServer server(80);      //创建一个服务器对象,WEB服务器端口:80
void setup() {
  Serial.begin(9600); //串口波特率初始化
  LittleFS_begin();   //LittleFS文件系统初始化
  connect_NET();       //网络初始化
  web_server();       //WEB服务器初始化
}

void loop() {
  DNS_request_loop();   //DNS服务请求处理
}

最后是网页的html代码:

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<link rel="stylesheet" type="text/css" href="mystyle.css">
<title>EPS32教程</title>
</head>
<body>
	<div id="pal">
		<form name="wifiset" onsubmit="return validateForm()" action="\setwifi" method="post" target="myframe">
			<label for="wifiname">WIFI SSID</label>
			<input name="wifiname" type="text" value="ESP32">
			<label for="wifipassward">WIFI PASSWARD</label>
			<input name="wifipassword" type="text" value="ESP32">
			<input type='submit' value='设置WIFI'>
		</form>
		<iframe src="" width="200" height="200" frameborder="0" name="myframe" style="display:NONE" ></iframe>
	</div>
</body>

<script>
/******************************
表单验证,WIFI名称输入框为空时提示
******************************/
function validateForm(){
var wifiname=document.forms["wifiset"]["wifiname"].value;	//得到name输入框的文字
if (wifiname==null || wifiname==""){					//如果输入框为空
  alert("WIFI SSID必需输入");							//显示提示
  return false;
  }
}
</script>
</html>

运行流程:

        1、初始化LittleFS文件系统

        2、连接WIFI,如果超时将跳过

        3、设置AP模式和STA模式(如果WIFI已连接)

        4、初始化服务器

        5、如果未能连接WIFI,只能用手机或电脑通过AP模式WIFI直接连接ESP32

                可以通过IP地址:10.0.10.1或域名espap.com用浏览器进入ESP32的WEB页面

                设置WIFI名称和WIFI密码,点击设置,如果设置正确,下次启动就可以直接连接WIFI了

至此,一个可以通过WEB页面配置WIFI的WEB服务器就完成了,在下一章中,将会介绍ESP32的远程互动,介绍通过WEB页面控制ESP32和通过MQTT协议控制ESP32。

猜你喜欢

转载自blog.csdn.net/m0_50114967/article/details/127009686