在esp32上运行WebServer

WebServer是非常常用的一个功能,在设备上使用该功能可以直接通过浏览器访问和操作设备。

基本使用

WebServer简单点理解就是网页服务器,主要干的活就是用户访问链接的时候执行相应的动作,对于开发来说主要处理的就是注册链接并编写用户访问该链接时需要执行的操作。
使用步骤如下:
1、引入相应库#include <WebServer.h>;
2、声明webserver并设置端口号,一般WebServer端口号使用80;
3、使用on()方法注册链接与回调函数;
4、使用begin()方法启动服务器进行请求监听;
5、使用handleClient()处理来自客户端的请求;
可以使用下面代码进行测试(替换掉wifi名和密码):

# include <WiFi.h>
# include <WebServer.h> //引入相应库

const char *ssid = "********";
const char *password = "********";

WebServer server(80); //声明WebServer对象

void handleRoot() //回调函数
{
  String message = "客户端信息:";
  message += "\nClient IP: ";
  IPAddress addr = server.client().remoteIP(); //客户端ip
  message += String(addr[0]) + "." + String(addr[1]) + "." + String(addr[2]) + "." + String(addr[3]);
  message += "\nURI: ";
  message += server.uri(); //打印当前url
  message += "\nMethod: ";
  message += (server.method() == HTTP_GET) ? "GET" : "POST"; //判断http请求方法
  message += "\nArguments: ";
  message += server.args();
  message += "\n";
  for (uint8_t i = 0; i < server.args(); i++)
  {
    message += " " + server.argName(i) + ": " + server.arg(i) + "\n";
  }
  server.send(200, "text/plain", message);
}

void setup()
{
  Serial.begin(115200);
  Serial.println();

  WiFi.mode(WIFI_STA);
  WiFi.setSleep(false);
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED)
  {
    delay(500);
    Serial.print(".");
  }
  Serial.println("Connected");
  Serial.print("IP Address:");
  Serial.println(WiFi.localIP());

  server.on("/msg", HTTP_GET, handleRoot); //注册链接和回调函数

  server.begin(); //启动服务器
  Serial.println("Web server started");
}

void loop()
{
  server.handleClient(); //处理来自客户端的请求
}

写esp32后在浏览器中输入串口监视器的服务器ip地址即可访问,输入ip/p1显示页面一,输入ip/p2显示页面二。注意浏览器的设备用的网络一定要和esp32连接的wifi一致才能访问。

使用路径参数

如果你有很多相似的链接比如/user/1、/user/2、/user/3、/user/4……,使用上面的方法时就需要每个链接都需要进行声明注册,比较不方便,这里可以使用路径参数来处理这些相似的或是动态的链接,可以用下面的代码进行测试(替换掉wifi名和密码):

# include <WiFi.h>
# include <WebServer.h> //引入相应库

const char *ssid = "********";
const char *password = "********";

WebServer server(80); //声明WebServer对象

void handleArg1() //回调函数
{
  String arg = server.pathArg(0);
  server.send(200, "text/plain", "这是链接/{},参数是: " + arg);
}

void handleArg2() //回调函数
{
  String arg0 = server.pathArg(0);
  String arg1 = server.pathArg(1);
  server.send(200, "text/plain", "这是链接/p/{}/d/{},参数是: " + arg0 + " 、 " + arg1);
}

void setup()
{
  Serial.begin(115200);
  Serial.println();

  WiFi.mode(WIFI_STA);
  WiFi.setSleep(false);
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED)
  {
    delay(500);
    Serial.print(".");
  }
  Serial.println("Connected");
  Serial.print("IP Address:");
  Serial.println(WiFi.localIP());

  server.on("/{}", handleArg1);        //注册链接与回调函数
  server.on("/p/{}/d/{}", handleArg2); //注册链接与回调函数

  server.begin(); //启动服务器
  Serial.println("Web server started");
}

void loop()
{
  server.handleClient(); //处理来自客户端的请求
}


设置未注册页面响应

如果用户访问了未注册的的链接时我们最好能给个提示,比如我们在上网时经常能见到的“网页不存在”、“404 Not Found”等。在这里我们可以用onNotFound()方法来给出这样的提示,用户在访问不存在的链接时会跳转到该方法所绑定的回调函数上,可以用下面代码进行测试(替换掉wifi名和密码):

# include <WiFi.h>
# include <WebServer.h> //引入相应库

const char *ssid = "********";
const char *password = "********";

WebServer server(80); //声明WebServer对象

void handleNotFound() //未注册链接回调函数
{
  server.send(404, "text/plain", "访问的页面不存在哦");
}

void setup()
{
  Serial.begin(115200);
  Serial.println();

  WiFi.mode(WIFI_STA);
  WiFi.setSleep(false);
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED)
  {
    delay(500);
    Serial.print(".");
  }
  Serial.println("Connected");
  Serial.print("IP Address:");
  Serial.println(WiFi.localIP());

  server.onNotFound(handleNotFound); //未注册链接回调函数注册

  server.begin(); //启动服务器
  Serial.println("Web server started");
}

void loop()
{
  server.handleClient(); //处理来自客户端的请求
}


用手机自带浏览器访问时直接提示页面不存在。但实际上测试成功了。

用户认证

用户认证可以提供一定的安全性,这里提供了BASIC_AUTH和DIGEST_AUTH两种方式,一般来说DIGEST_AUTH方式安全性稍高些,下面代码进行了基本的测试:

# include <WiFi.h>
# include <WebServer.h> //引入相应库

const char *ssid = "********";
const char *password = "********";

WebServer server(80); //声明WebServer对象

const char *username = "user";     //用户名
const char *userpassword = "1234"; //用户密码

void handleRoot() //回调函数
{
  if (!server.authenticate(username, userpassword)) //校验用户是否登录
  {
    return server.requestAuthentication(); //请求进行用户登录认证
  }
  server.send(200, "text/plain", "登录成功!"); //登录成功则显示真正的内容
}

void setup()
{
  Serial.begin(115200);
  Serial.println();

  WiFi.mode(WIFI_STA);
  WiFi.setSleep(false);
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED)
  {
    delay(500);
    Serial.print(".");
  }
  Serial.println("Connected");
  Serial.print("IP Address:");
  Serial.println(WiFi.localIP());

  server.on("/", handleRoot); //注册链接和回调函数

  server.begin(); //启动服务器
  Serial.println("Web server started");
}

void loop()
{
  server.handleClient(); //处理来自客户端的请求
}


请求方信息

客户端请求链接,我们也能够知道客户端请求的一些信息,可以用下面代码进行测试:

# include <WiFi.h>
# include <WebServer.h> //引入相应库

const char *ssid = "********";
const char *password = "********";

WebServer server(80); //声明WebServer对象

void handleRoot() //回调函数
{
  String message = "客户端信息:";
  message += "\nClient IP: ";
  IPAddress addr = server.client().remoteIP(); //客户端ip
  message += String(addr[0]) + "." + String(addr[1]) + "." + String(addr[2]) + "." + String(addr[3]);
  message += "\nURI: ";
  message += server.uri(); //打印当前url
  message += "\nMethod: ";
  message += (server.method() == HTTP_GET) ? "GET" : "POST"; //判断http请求方法
  message += "\nArguments: ";
  message += server.args();
  message += "\n";
  for (uint8_t i = 0; i < server.args(); i++)
  {
    message += " " + server.argName(i) + ": " + server.arg(i) + "\n";
  }
  server.send(200, "text/plain", message);
}

void setup()
{
  Serial.begin(115200);
  Serial.println();

  WiFi.mode(WIFI_STA);
  WiFi.setSleep(false);
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED)
  {
    delay(500);
    Serial.print(".");
  }
  Serial.println("Connected");
  Serial.print("IP Address:");
  Serial.println(WiFi.localIP());

  server.on("/msg", HTTP_GET, handleRoot); //注册链接和回调函数

  server.begin(); //启动服务器
  Serial.println("Web server started");
}

void loop()
{
  server.handleClient(); //处理来自客户端的请求
}


网页与后台数据交互

网页与后台数据交互需要用到网页和html和ajax知识
我们首先准备一个带ajax脚本的网页,但是网页代码测试有错误,点击后无法显示随机数:

<html>
<head>
    <title>ESP32 WebServer Test</title>
    <script>
        function getData() {
            var xmlhttp;
            if (window.XMLHttpRequest) {
                xmlhttp = new XMLHttpRequest();
            }
            else {
                xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
            }
            xmlhttp.onreadystatechange = function () {
                if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
                    //将获取到的内容写到txtRandomData中
                    document.getElementById("txtRandomData").innerHTML = xmlhttp.responseText;
                }
            },
                xmlhttp.open("GET", "getRandomData", true); //以GET方法打开getRandomData
            xmlhttp.send();
        }
    </script>
</head>
<body>
    <div id="txtRandomData">Unkwon</div>
    <input type="button" value="random" onclick="getData()">
</body>
</html>

然后通过工具将它转换成字符串嵌入到代码中,工具可以参考:
http://tools.jb51.net/transcoding/html2js
也可以先压缩网页然后再转换成字符串,这样可以减小网页大小,提高效率,可以参考:
https://tool.lu/html/
下面就是带有 网页与后台数据交互功能 的完整代码:

# include <WiFi.h>
# include <WebServer.h> //引入相应库

//网页
String myhtmlPage =
    String("") +
    "<html>" +
    "<head>" +
    "    <title>ESP32 WebServer Test</title>" +
    "    <script>" +
    "        function getData() {" +
    "            var xmlhttp;" +
    "            if (window.XMLHttpRequest) {" +
    "                xmlhttp = new XMLHttpRequest();" +
    "            }" +
    "            else {" +
    "                xmlhttp = new ActiveXObject(\"Microsoft.XMLHTTP\");" +
    "            }" +
    "            xmlhttp.onreadystatechange = function () {" +
    "                if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {" +
    "                    document.getElementById(\"txtRandomData\").innerHTML = xmlhttp.responseText;" +
    "                }" +
    "            }," +
    "                xmlhttp.open(\"GET\", \"getRandomData\", true); " +
    "            xmlhttp.send();" +
    "        }" +
    "    </script>" +
    "</head>" +
    "<body>" +
    "    <div id=\"txtRandomData\">Unkwon</div>" +
    "    <input type=\"button\" value=\"random\" οnclick=\"getData()\">" +
    "</body>" +
    "</html>";

const char *ssid = "********";
const char *password = "********";

WebServer server(80); //声明WebServer对象

void handleRoot() //回调函数
{
  server.send(200, "text/html", myhtmlPage); //!!!注意返回网页需要用"text/html" !!!
}

void handleAjax() //回调函数
{
  String message = "随机数据:";
  message += String(random(10000)); //取得随机数
  server.send(200, "text/plain", message); //将消息发送回页面
}

void setup()
{
  Serial.begin(115200);
  Serial.println();

  WiFi.mode(WIFI_STA);
  WiFi.setSleep(false);
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED)
  {
    delay(500);
    Serial.print(".");
  }
  Serial.println("Connected");
  Serial.print("IP Address:");
  Serial.println(WiFi.localIP());

  server.on("/", handleRoot);                        //注册链接和回调函数
  server.on("/getRandomData", HTTP_GET, handleAjax); //注册网页中ajax发送的get方法的请求和回调函数

  server.begin(); //启动服务器
  Serial.println("Web server started");
}

void loop()
{
  server.handleClient(); //处理来自客户端的请求
}

因为网页代码测试失败,所以需要更正网页代码错误后才能实现。

可以看到当点击网页上random按钮时触发了getData()方法,该方法向服务器请求getRandomData链接,服务器在收到该请求后进行了响应,把数据返回给客户页面。上面只是简单演示,你也可以使用上面的方法来控制设备(比如点亮个灯、开合继电器等)。

常用方法

WebServer(int port = 80)
构造方法;
void begin()
void begin(uint16_t port)
服务器启动监听;
void handleClient();
处理来自客户端的请求;
void close()
void stop()
停止当前监听;
bool authenticate(const char * username, const char * password)
校验用户是否登录;
void requestAuthentication(HTTPAuthMethod mode = BASIC_AUTH, const char* realm = NULL, const String& authFailMsg = String(""))
请求进行用户登录认证(浏览器端会打开登录窗口);
void on(const String &uri, THandlerFunction handler)
void on(const String &uri, HTTPMethod method, THandlerFunction fn)
void on(const String &uri, HTTPMethod method, THandlerFunction fn, THandlerFunction ufn)
注册链接与回调函数;
void onNotFound(THandlerFunction fn)
注册未注册链接回调函数
void onFileUpload(THandlerFunction fn)
注册文件上传回调函数;
void send(int code, const char* content_type = NULL, const String& content = String(""))
void send(int code, char* content_type, const String& content)
void send(int code, const String& content_type, const String& content)
向客户端(浏览器)发送数据;
void sendHeader(const String& name, const String& value, bool first = false)
发送响应头;
void sendContent(const String& content)
发送响应正文内容;
String uri()
返回客户端请求的url;
HTTPMethod method()
返回客户端请求方法
String pathArg(unsigned int i)
通过序号获取路径参数;
template<typename T>
size_t streamFile(T &file, const String& contentType)
发送文件,返回发送的字节数;

其它说明

HTTP状态码说明

上面的200、404等是HTTP状态码。用户在请求访问某个地址的时候,WebServer需要进行响应,发送响应头,响应头中第一行一般像是这样的HTTP/1.1 200 OK,其中200就是状态码。常见的状态码如下:

200服务器成功返回网页;
404请求的网页不存在;
301本网页被永久性转移到另一个URL;
503服务器目前不可用;
401请求未经授权,需要登录认证;
……

Content-Type说明

上面的text/plain、text/html这些是Content-Type,具体说明如下:
Content-Type(MediaType),即是Internet Media Type,互联网媒体类型,也叫做MIME类型。在互联网中有成百上千中不同的数据类型,HTTP在传输数据对象时会为他们打上称为MIME的数据格式标签,用于区分数据类型。最初MIME是用于电子邮件系统的,后来HTTP也采用了这一方案。
在HTTP协议消息头中,使用Content-Type来表示请求和响应中的媒体类型信息。它用来告诉服务端如何处理请求的数据,以及告诉客户端(一般是浏览器)如何解析响应的数据,比如显示图片,解析并展示html等等。
Content-Type举例:

  • text/plain纯文本文件;
  • text/htmlhtml文件;
  • application/jsonjson形式数据文件;
  • application/xmlxml形式数据文件;
  • image/pngpng格式图片;
  • ……

总结

WebServer的使用主要就是上面这些了,其它的一些相功能(DNS服务器、将网页数据存储在SD卡、通过网页更新设备固件等)会在后面单独写文章进行介绍。

- 本文内容来自网络,如有侵权,请联系本站处理。

11-28   阅读(3)   评论(0)
 标签: 创客 ESP32 WebServer

涨知识
RISC-V

RISC-V(发音为“risk-five”)是一个基于精简指令集(RISC)原则的开源指令集架构(ISA)。RISC-V指令集可以自由地用于任何目的,允许任何人设计、制造和销售RISC-V芯片和软件。

评论:
相关文章
ESP32 WebServer库处理表单请求

本文主要讲解WebServer库如何来处理表单请求。



Arduino-ESP32与ESP-IDF的版本对应表

Arduino-ESP32与ESP-IDF的版本对应表。


Arduino-ESP32文件系统全解析:SPIFFS、LittleFS、SD卡操作

Arduino-ESP32提供了多种文件系统解决方案,本文将深入解析SPIFFS、LittleFS和SD卡三种主流存储方案,帮助你做出最佳选择。


ESP32-P4-WIFI6开发板

ESP32-P4-WIFI6-DEV-KIT是一款微雪(Waveshare)设计的基于 ESP32-P4 的多媒体开发板,并集成 ESP32-C6,支持 Wi-Fi 6 和 BLE 5 无线连接。它提供丰富的人机交互接口,包括 MIPI-CSI (集成图像信号处理器 ISP)、MIPI-DSI、SPI、I2S、I2C、LED PWM、MCPWM、RMT、ADC、UART 和 TWAI 等。


ESP-Hosted 入门介绍 &使用指南

ESP-Hosted 解决方案提供了将 ESP 板用作 Wi-Fi 和 Bluetooth/BLE 连接的通信处理器的方法。


设备上云太麻烦?ESP-Hosted一站触达!

ESP-Hosted 提供了一种将ESP芯片和模组用作通信协处理器的解决方案,该解决方案为主机微处理器或微控制器提供无线连接,使主机能够与其他设备通信。简单来说为网卡方案。


ESP32 + Arduino使用TFT_eSPI库

Arduino+ESP32上使用TFT_eSPI库快速点亮这个屏幕,驱动芯片ST7789


ESP32 利用 SPI 连通 TFT 彩屏

本文给出了一个ESP32与SPI 接口TFT显示屏接线的详细说明,供大家参考。


在Micropython下使用ESPNow功能进行数据传输

本文讲解如何在Micropython环境下使用ESP32的ESPNow功能进行数据传输。