ESP32 Async Web Server – 使用 Arduino IDE 控制输出(ESPAsyncWebServer 库)

释放双眼,带上耳机,听听看~!

在本教程中,您将学习如何使用 ESP32 开发板构建异步 Web 服务器来控制其输出。该板将使用 Arduino IDE 进行编程,我们将使用 ESPAsyncWebServer 库。

ESP32 Async Web Server – 使用 Arduino IDE 控制输出(ESPAsyncWebServer 库)

 

异步网络服务器

为了构建 Web 服务器,我们将使用 ESPAsyncWebServer 库 ,它提供了一种构建异步 Web 服务器的简单方法。如库 GitHub 页面中所述,构建异步 Web 服务器有几个优点,例如:

  • “同时处理多个连接”;
  • “当您发送响应时,您可以立即准备好处理其它连接,而服务器正在后台处理发送响应”;
  • “处理模板的简单模板处理引擎”;
  • 以及更多。

所需零件

在本教程中,我们将控制三个输出。例如,我们将控制 LED。因此,您需要以下部分:

  • ESP32(阅读最佳 ESP32 开发板)
  • 3x LED
  • 3x 220 欧姆电阻
  • 面包板
  • 连接线

示意图

在继续执行代码之前,将 3 个 LED 连接到 ESP32。我们将 LED 连接到 GPIO 2、4 和 33,但您可以使用任何其它 GPIO(阅读ESP32 GPIO 参考指南)。

ESP32 Async Web Server – 使用 Arduino IDE 控制输出(ESPAsyncWebServer 库)

安装库 – ESP Async Web 服务器

要构建 Web 服务器,您需要安装以下库。单击下面的链接下载库。

这些库无法通过Arduino库管理器安装,因此您需要将库文件复制到Arduino安装库文件夹。或者,在Arduino IDE中,您可以转到Sketch>Include Library>Add.zip Library并选择刚刚下载的库。

项目概述

为了更好地理解代码,我们来看看Web服务器是如何工作的。

ESP32 Async Web Server – 使用 Arduino IDE 控制输出(ESPAsyncWebServer 库)
  • web服务器包含一个标题“ESP web服务器”和三个按钮(切换开关),用于控制三个输出。每个滑块按钮都有一个指示GPIO输出引脚的标签。您可以轻松删除/添加更多输出。
  • 滑块为红色时,表示输出为开(其状态为高)。如果切换滑块,它将关闭输出(将状态更改为低)。
  • 当滑块为灰色时,表示输出关闭(其状态为 LOW)。如果切换滑块,它会打开输出(将状态更改为 HIGH)。

这个怎么运作?

ESP32 Async Web Server – 使用 Arduino IDE 控制输出(ESPAsyncWebServer 库)

让我们看看切换按钮时会发生什么。我们将看到 GPIO 2 的示例。其它按钮的工作方式类似。

1.在第一种情况下,您切换按钮以打开 GPIO 2。发生这种情况时,浏览器会在/update?output=2&state=1。基于该 URL,ESP 将 GPIO 2 的状态更改为 1 ( HIGH ) 并打开 LED。

2.在第二个示例中,您切换按钮以关闭 GPIO 2。发生这种情况时,浏览器会在/update?output=2&state=0。基于该 URL,我们将 GPIO 2 的状态更改为 0 ( LOW ) 并关闭 LED。

ESP 异步 Web 服务器的代码

将以下代码复制到您的 Arduino IDE。

// Import required libraries
#include <WiFi.h>
#include <AsyncTCP.h>
#include <ESPAsyncWebServer.h>

// Replace with your network credentials
const char* ssid = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";

const char* PARAM_INPUT_1 = "output";
const char* PARAM_INPUT_2 = "state";

// Create AsyncWebServer object on port 80
AsyncWebServer server(80);

const char index_html[] PROGMEM = R"rawliteral(
<!DOCTYPE HTML><html>
<head>
  <title>ESP Web Server</title>
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="icon" href="data:,">
  <style>
    html {font-family: Arial; display: inline-block; text-align: center;}
    h2 {font-size: 3.0rem;}
    p {font-size: 3.0rem;}
    body {max-width: 600px; margin:0px auto; padding-bottom: 25px;}
    .switch {position: relative; display: inline-block; width: 120px; height: 68px} 
    .switch input {display: none}
    .slider {position: absolute; top: 0; left: 0; right: 0; bottom: 0; background-color: #ccc; border-radius: 6px}
    .slider:before {position: absolute; content: ""; height: 52px; width: 52px; left: 8px; bottom: 8px; background-color: #fff; -webkit-transition: .4s; transition: .4s; border-radius: 3px}
    input:checked+.slider {background-color: #b30000}
    input:checked+.slider:before {-webkit-transform: translateX(52px); -ms-transform: translateX(52px); transform: translateX(52px)}
  </style>
</head>
<body>
  <h2>ESP Web Server</h2>
  %BUTTONPLACEHOLDER%
<script>function toggleCheckbox(element) {
  var xhr = new XMLHttpRequest();
  if(element.checked){ xhr.open("GET", "/update?output="+element.id+"&state=1", true); }
  else { xhr.open("GET", "/update?output="+element.id+"&state=0", true); }
  xhr.send();
}
</script>
</body>
</html>
)rawliteral";

// Replaces placeholder with button section in your web page
String processor(const String& var){
  //Serial.println(var);
  if(var == "BUTTONPLACEHOLDER"){
    String buttons = "";
    buttons += "<h4>Output - GPIO 2</h4><label class="switch"><input type="checkbox" onchange="toggleCheckbox(this)" id="2" " + outputState(2) + "><span class="slider"></span></label>";
    buttons += "<h4>Output - GPIO 4</h4><label class="switch"><input type="checkbox" onchange="toggleCheckbox(this)" id="4" " + outputState(4) + "><span class="slider"></span></label>";
    buttons += "<h4>Output - GPIO 33</h4><label class="switch"><input type="checkbox" onchange="toggleCheckbox(this)" id="33" " + outputState(33) + "><span class="slider"></span></label>";
    return buttons;
  }
  return String();
}

String outputState(int output){
  if(digitalRead(output)){
    return "checked";
  }
  else {
    return "";
  }
}

void setup(){
  // Serial port for debugging purposes
  Serial.begin(115200);

  pinMode(2, OUTPUT);
  digitalWrite(2, LOW);
  pinMode(4, OUTPUT);
  digitalWrite(4, LOW);
  pinMode(33, OUTPUT);
  digitalWrite(33, LOW);
  
  // Connect to Wi-Fi
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
    Serial.println("Connecting to WiFi..");
  }

  // Print ESP Local IP Address
  Serial.println(WiFi.localIP());

  // Route for root / web page
  server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send_P(200, "text/html", index_html, processor);
  });

  // Send a GET request to <ESP_IP>/update?output=<inputMessage1>&state=<inputMessage2>
  server.on("/update", HTTP_GET, [] (AsyncWebServerRequest *request) {
    String inputMessage1;
    String inputMessage2;
    // GET input1 value on <ESP_IP>/update?output=<inputMessage1>&state=<inputMessage2>
    if (request->hasParam(PARAM_INPUT_1) && request->hasParam(PARAM_INPUT_2)) {
      inputMessage1 = request->getParam(PARAM_INPUT_1)->value();
      inputMessage2 = request->getParam(PARAM_INPUT_2)->value();
      digitalWrite(inputMessage1.toInt(), inputMessage2.toInt());
    }
    else {
      inputMessage1 = "No message sent";
      inputMessage2 = "No message sent";
    }
    Serial.print("GPIO: ");
    Serial.print(inputMessage1);
    Serial.print(" - Set to: ");
    Serial.println(inputMessage2);
    request->send(200, "text/plain", "OK");
  });

  // Start server
  server.begin();
}

void loop() {

}

 

代码如何运作

在本节中,我们将解释代码是如何工作的。

导入库

首先,导入所需的库。您需要包括无线上网,ESPAsync网络服务器和异步TCP库。

#include <WiFi.h>
#include <AsyncTCP.h>
#include <ESPAsyncWebServer.h>

设置您的网络凭据

在以下变量中插入您的网络凭据,以便 ESP32 可以连接到您的本地网络。

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

输入参数

为了检查 URL 上传递的参数(GPIO 编号及其状态),我们创建了两个变量,一个用于输出,另一个用于状态。

const char* PARAM_INPUT_1 = "output";
const char* PARAM_INPUT_2 = "state";

请记住,ESP32 接收如下请求:/update?output=2&state=0

AsyncWebServer 对象

创建一个异步网络服务器端口 80 上的对象。

AsyncWebServer server(80);

构建网页

所有带有样式和 JavaScript 的 HTML 文本都存储在 index_html variable。现在我们将浏览 HTML 文本并查看每个部分的作用。

标题位于<title>和</tile>标签内。标题正是它听起来的样子:文档的标题,显示在 Web 浏览器的标题栏中。在这种情况下,它是“ESP Web 服务器”。

<title>ESP Web Server</title>
ESP32 Async Web Server – 使用 Arduino IDE 控制输出(ESPAsyncWebServer 库)

以下<meta>标记使您的网页在任何浏览器(笔记本电脑、平板电脑或智能手机)中都能响应。

<meta name="viewport" content="width=device-width, initial-scale=1">

下一行阻止对 favicon 的请求。在这种情况下,我们没有网站图标。favicon 是显示在 Web 浏览器选项卡中标题旁边的网站图标。如果我们不添加以下行,ESP32 将在我们每次访问 Web 服务器时收到对 favicon 的请求。

<link rel="icon" href="data:,">

在<style></style> 标签之间,我们添加了一些 CSS 来设置网页的样式。我们不会详细介绍这种 CSS 样式是如何工作的。

<style>
    html {font-family: Arial; display: inline-block; text-align: center;}
    h2 {font-size: 3.0rem;}
    p {font-size: 3.0rem;}
    body {max-width: 600px; margin:0px auto; padding-bottom: 25px;}
    .switch {position: relative; display: inline-block; width: 120px; height: 68px} 
    .switch input {display: none}
    .slider {position: absolute; top: 0; left: 0; right: 0; bottom: 0; background-color: #ccc; border-radius: 6px}
    .slider:before {position: absolute; content: ""; height: 52px; width: 52px; left: 8px; bottom: 8px; background-color: #fff; -webkit-transition: .4s; transition: .4s; border-radius: 3px}
    input:checked+.slider {background-color: #b30000}
    input:checked+.slider:before {-webkit-transform: translateX(52px); -ms-transform: translateX(52px); transform: translateX(52px)}
</style>

HTML 正文

<body></body>标签内是我们添加网页内容的地方。

<  h2></h2> 标签将标题添加到网页。在本例中为“ESP Web Server”文本,但您可以添加任何其它文本。

<h2>ESP Web Server</h2>

在标题之后,我们有按钮。按钮在网页上的显示方式(红色:如果 GPIO 开启;或灰色:如果 GPIO 关闭)取决于当前的 GPIO 状态。

当您访问 Web 服务器页面时,您希望它显示正确的当前 GPIO 状态。因此,我们将添加一个占位符,而不是添加 HTML 文本来构建按钮%按钮占位符%. 然后,当加载网页时,该占位符将被实际的 HTML 文本替换,以构建具有正确状态的按钮。

%BUTTONPLACEHOLDER%

JavaScript

然后,正如我们之前解释的那样,当您切换按钮时,有一些 JavaScript 负责发出HTTP GET 请求。

<script>function toggleCheckbox(element) {
  var xhr = new XMLHttpRequest();
  if(element.checked){ xhr.open("GET", "/update?output="+element.id+"&state=1", true); }
  else { xhr.open("GET", "/update?output="+element.id+"&state=0", true); }
  xhr.send();
}
</script>

这是发出请求的行:

if(element.checked){ xhr.open("GET", "/update?output="+element.id+"&state=1", true); }

元素.id返回 HTML 元素的 id。每个按钮的 id 将是 GPIO 控制的,我们将在下一节中看到:

  • GPIO 2 按钮 » element.id = 2
  • GPIO 4 按钮 » element.id = 4
  • GPIO 33 按钮 » element.id = 33

processor

现在,我们需要创建 processor() 函数,它将 HTML 文本中的占位符替换为我们定义的内容。

当请求网页时,检查 HTML 是否有任何占位符。如果它发现%BUTTONPLACEHOLDER%占位符,它返回 HTML 文本以创建按钮。

String processor(const String& var){
  //Serial.println(var);
  if(var == "BUTTONPLACEHOLDER"){
    String buttons = "";
    buttons += "<h4>Output - GPIO 2</h4><label class="switch"><input type="checkbox" onchange="toggleCheckbox(this)" id="2" " + outputState(2) + "><span class="slider"></span></label>";
    buttons += "<h4>Output - GPIO 4</h4><label class="switch"><input type="checkbox" onchange="toggleCheckbox(this)" id="4" " + outputState(4) + "><span class="slider"></span></label>";
    buttons += "<h4>Output - GPIO 33</h4><label class="switch"><input type="checkbox" onchange="toggleCheckbox(this)" id="33" " + outputState(33) + "><span class="slider"></span></label>";
    return buttons;
  }
  return String();
}

您可以轻松删除或添加更多行以创建更多按钮。

让我们看看按钮是如何创建的。我们创建一个名为的 String 变量纽扣包含用于构建按钮的 HTML 文本。我们将 HTML 文本与当前输出状态连接起来,以便切换按钮为灰色或红色。当前的输出状态由返回outputState(<GPIO>)函数(它接受 GPIO 编号作为参数)。见下文:

buttons += "<h4>Output - GPIO 2</h4><label class="switch"><input type="checkbox" onchange="toggleCheckbox(this)" id="2" " + outputState(2) + "><span class="slider"></span></label>";

使用 以便我们可以在字符串中传递“”。

这outputState()函数返回“检查”如果 GPIO 处于打开状态或为空字段“”如果 GPIO 关闭。

String outputState(int output){
  if(digitalRead(output)){
    return "checked";
  }
  else {
    return "";
  }
}

因此,GPIO 2 打开时的 HTML 文本将是:

<h4>Output - GPIO 2</h4>
<label class="switch">
<input type="checkbox" onchange="toggleCheckbox(this)" id="2" checked><span class="slider"></span>
</label>

让我们把它分解成更小的部分来了解它是如何工作的。

在 HTML 中,切换开关是一种输入类型。<input>标签指定用户可以输入数据的输入字段。拨动开关是类型的输入字段复选框. 还有许多其它输入字段类型。

<input type="checkbox">

该复选框可以选中或不选中。检查时,您有以下内容:

<input type="checkbox" checked>

这改变是当我们更改元素(复选框)的值时发生的事件属性。每当您选中或取消选中切换开关时,它都会调用切换复选框()该特定元素 ID 的 JavaScript 函数 (这个)。

这ID指定该 HTML 元素的唯一 ID。id 允许我们使用 JavaScript 或 CSS 操作元素。

<input type="checkbox" onchange="toggleCheckbox(this)" id="2" checked>

setup()

在里面 setup() 初始化串行监视器以进行调试。

Serial.begin(115200);

使用 pinMode() 函数将要控制的 GPIO 设置为输出,并在 ESP32 首次启动时将其设置为 LOW。如果您添加了更多 GPIO,请执行相同的步骤。

pinMode(2, OUTPUT);
digitalWrite(2, LOW);
pinMode(4, OUTPUT);
digitalWrite(4, LOW);
pinMode(33, OUTPUT);
digitalWrite(33, LOW);

连接到本地网络并打印 ESP32 IP 地址。

WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
  delay(1000);
  Serial.println("Connecting to WiFi..");
}

// Print ESP Local IP Address
Serial.println(WiFi.localIP());

在setup() 里面,您需要处理 ESP32 收到请求时发生的情况。正如我们之前看到的,您会收到这种类型的请求:

<ESP_IP>/update?output=<inputMessage1>&state=<inputMessage2>

因此,我们检查请求是否包含PARAM_INPUT1变量值 (output) 和PARAM_INPUT2(state) 并将相应的值保存在输入1消息和输入2消息变量。

if (request->hasParam(PARAM_INPUT_1) && request->hasParam(PARAM_INPUT_2)) {
  inputMessage1 = request->getParam(PARAM_INPUT_1)->value();
  inputMessage2 = request->getParam(PARAM_INPUT_2)->value();

然后,我们用相应的状态控制相应的GPIO(输入消息1变量保存 GPIO 编号和输入消息2保存状态 – 0 或 1)

digitalWrite(inputMessage1.toInt(), inputMessage2.toInt());

以下是处理 HTTP GET /update请求的完整代码:

server.on("/update", HTTP_GET, [] (AsyncWebServerRequest *request) {
  String inputMessage1;
  String inputMessage2;
  // GET input1 value on <ESP_IP>/update?output=<inputMessage1>&state=<inputMessage2>
  if (request->hasParam(PARAM_INPUT_1) && request->hasParam(PARAM_INPUT_2)) {
    inputMessage1 = request->getParam(PARAM_INPUT_1)->value();
    inputMessage2 = request->getParam(PARAM_INPUT_2)->value();
    digitalWrite(inputMessage1.toInt(), inputMessage2.toInt());
  }
  else {
    inputMessage1 = "No message sent";
    inputMessage2 = "No message sent";
  }
  Serial.print("GPIO: ");
  Serial.print(inputMessage1);
  Serial.print(" - Set to: ");
  Serial.println(inputMessage2);
  request->send(200, "text/plain", "OK");
});

最后,启动服务器:

server.begin();

示范

将代码上传到 ESP32 后,以 115200 的波特率打开串行监视器。按下板载 RST/EN 按钮。你应该得到它的IP地址。

打开浏览器并输入 ESP IP 地址。您将可以访问类似的网页。

ESP32 Async Web Server – 使用 Arduino IDE 控制输出(ESPAsyncWebServer 库)

按下切换按钮来控制 ESP32 GPIO。同时,您应该在串行监视器中获得以下消息,以帮助您调试代码。

ESP32 Async Web Server – 使用 Arduino IDE 控制输出(ESPAsyncWebServer 库)

您还可以从智能手机中的浏览器访问 Web 服务器。每当您打开 Web 服务器时,它都会显示当前的 GPIO 状态。红色表示 GPIO 开启,灰色表示 GPIO 关闭。

ESP32 Async Web Server – 使用 Arduino IDE 控制输出(ESPAsyncWebServer 库)

总结

在本教程中,您学习了如何使用 ESP32 创建异步 Web 服务器,以使用拨动开关控制其输出。每当您打开网页时,它都会显示更新后的 GPIO 状态。

给TA打赏
共{{data.count}}人
人已打赏
ESP32-进阶免费项目

带滑块的 ESP32 Web 服务器:网页控制 LED 亮度 (PWM)

2022-8-28 1:51:43

ESP32-进阶免费项目

在 Arduino IDE 中安装 ESP32 文件系统上传器插件

2022-8-28 15:10:56

0 条回复 A文章作者 M管理员
    暂无讨论,说说你的看法吧
个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索
'); })();