在这个项目中,您将创建一个带有 ESP32 的独立 Web 服务器,该服务器使用 Arduino IDE 编程环境控制输出(两个 LED)。Web 服务器具有移动响应能力,可以使用任何设备作为本地网络上的浏览器进行访问。我们将向您展示如何创建 Web 服务器以及代码如何逐步运行。
目录
项目概况
在直接进入项目之前,重要的是概述我们的 Web 服务器将做什么,以便以后更容易地遵循这些步骤。
- 您将构建的 Web 服务器控制连接到 ESP32 的两个 LED 通用输入输出口 26 和 通用输入输出口 27;
- 您可以通过在本地网络的浏览器中输入 ESP32 IP 地址来访问 ESP32 Web 服务器;
- 通过单击 Web 服务器上的按钮,您可以立即更改每个 LED 的状态。
这只是一个简单的例子来说明如何构建一个控制输出的网络服务器,想法是用继电器或任何其它你想要的电子元件替换这些 LED 。
所需零件
对于本教程,您将需要以下部分:
- ESP32 开发板
- 2x 5mm LED
- 2x 330 欧姆电阻
- 面包板
- 连接线
示意图
从构建电路开始。如下图所示,将两个 LED 连接到 ESP32 – 一个 LED 连接到通用输入输出口 26,另一个到 通用输入输出接口 27.
注意:我们使用的是 36 针的 ESP32 DEVKIT DOIT 板。在组装电路之前,请确保检查您正在使用的电路板的引脚排列。
ESP32 网络服务器代码
这里我们提供了创建 ESP32 Web 服务器的代码。将以下代码复制到您的 Arduino IDE,但先不要上传。您需要进行一些更改才能使其适合您。
完整代码:
设置您的网络凭据
您需要使用网络凭据修改以下行:SSID 和密码。该代码对您应该在哪里进行更改进行了很好的注释。
// Replace with your network credentials
const char* ssid = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";
上传代码
现在,您可以上传代码,Web 服务器将立即运行。按照以下步骤将代码上传到 ESP32:
1)将 ESP32 板插入电脑;
2) 在 Arduino IDE 中,在Tools > Board 中选择您的开发板 (在我们的例子中,我们使用的是 ESP32 DEVKIT DOIT 开发板);
3) 在Tools > Port 中选择 COM 端口 。
4) 按下Arduino IDE 中的上传按钮,等待几秒钟代码编译并上传到您的开发板。
5) 等待“上传完成”消息。
查找 ESP IP 地址
上传代码后,以115200的波特率打开串口监视器。
按下 ESP32 EN 按钮(重置)。ESP32 连接到 Wi-Fi,并在串行监视器上输出 ESP IP 地址。复制该 IP 地址,因为您需要它来访问 ESP32 Web 服务器。
访问网络服务器
要访问网络服务器,请打开浏览器,粘贴 ESP32 IP 地址,您将看到以下页面。在我们的例子中是192.168.1.135。
如果您查看串行监视器,您可以看到后台发生了什么。ESP 接收来自新客户端(在本例中为您的浏览器)的 HTTP 请求。
您还可以查看有关 HTTP 请求的其它信息。
测试 Web 服务器
现在您可以测试您的 Web 服务器是否正常工作。单击按钮以控制 LED。
同时,您可以查看串行监视器以了解后台发生了什么。例如,当您单击按钮时通用输入输出口 26ON,ESP32 接收到/26/on URL上的请求。
当 ESP32 收到该请求时,它会将连接到的 LED 点亮 通用输入输出口 26 ON 并在网页上更新其状态。
按钮为 通用输入输出口 27以类似的方式工作。测试它是否正常工作。
代码的工作原理
在本节中将仔细查看代码以了解它是如何工作的。
您需要做的第一件事是包含 WiFi 库。
#include <WiFi.h>
如前所述,您需要在双引号内的以下行中插入您的 ssid 和密码。
const char* ssid = "";
const char* password = "";
然后,您将 Web 服务器设置为端口 80。
WiFiServer server(80);
以下行创建一个变量来存储 HTTP 请求的标头:
String header;
接下来,您创建辅助变量来存储输出的当前状态。如果要添加更多输出并保存其状态,则需要创建更多变量。
String output26State = "off";
String output27State = "off";
您还需要为每个输出分配一个 GPIO。这里我们使用通用输入输出口 26 和 通用输入输出口 27. 您可以使用任何其它合适的 GPIO。
const int output26 = 26;
const int output27 = 27;
setup()
现在,让我们进入初始化函数 setup() . 首先,为了调试目的,我们以 115200 的波特率启动串行通信。
Serial.begin(115200);
您还将 GPIO 定义为 OUTPUT 并将它们设置为低电平。
// Initialize the output variables as outputs
pinMode(output26, OUTPUT);
pinMode(output27, OUTPUT);
// Set outputs to LOW
digitalWrite(output26, LOW);
digitalWrite(output27, LOW);
以下几行开始 Wi-Fi 连接 WiFi.begin(ssid, 密码), 等待连接成功并在串行监视器中打印 ESP IP 地址。
// Connect to Wi-Fi network with SSID and password
Serial.print("Connecting to ");
Serial.println(ssid);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
// Print local IP address and start web server
Serial.println("");
Serial.println("WiFi connected.");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
server.begin();
loop()
在loop() 函数里面 我们对新客户端与 Web 服务器建立连接时发生的情况进行编程。
ESP32 始终使用以下行监听传入的客户端:
WiFiClient client = server.available(); // Listen for incoming clients
当收到来自客户端的请求时,我们将保存传入的数据。只要客户端保持连接,接下来的 while 循环就会运行。我们不建议更改代码的以下部分,除非您确切地知道自己在做什么。
if (client) { // If a new client connects,
Serial.println("New Client."); // print a message out in the serial port
String currentLine = ""; // make a String to hold incoming data from the client
while (client.connected()) { // loop while the client's connected
if (client.available()) { // if there's bytes to read from the client,
char c = client.read(); // read a byte, then
Serial.write(c); // print it out the serial monitor
header += c;
if (c == 'n') { // if the byte is a newline character
// if the current line is blank, you got two newline characters in a row.
/ that's the end of the client HTTP request, so send a response:
if (currentLine.length() == 0) {
// HTTP headers always start with a response code (e.g. HTTP/1.1 200 OK)
// and a content-type so the client knows what's coming, then a blank line:
client.println("HTTP/1.1 200 OK");
client.println("Content-type:text/html");
client.println("Connection: close");
client.println();
if 和 else 语句的下一部分检查在您的网页中按下了哪个按钮,并相应地控制输出。正如我们之前看到的,我们根据按下的按钮对不同的 URL 发出请求。
// turns the GPIOs on and off
if (header.indexOf("GET /26/on") >= 0) {
Serial.println("GPIO 26 on");
output26State = "on";
digitalWrite(output26, HIGH);
} else if (header.indexOf("GET /26/off") >= 0) {
Serial.println("GPIO 26 off");
output26State = "off";
digitalWrite(output26, LOW);
} else if (header.indexOf("GET /27/on") >= 0) {
Serial.println("GPIO 27 on");
output27State = "on";
digitalWrite(output27, HIGH);
} else if (header.indexOf("GET /27/off") >= 0) {
Serial.println("GPIO 27 off");
output27State = "off";
digitalWrite(output27, LOW);
}
例如,如果您按下 GPIO 26 ON 按钮,ESP32 会收到/26/ON URL上的请求(我们可以在串行监视器的 HTTP 标头上看到该信息)。因此,我们可以检查标头是否包含表达式GET /26/on。如果它包含,我们改变输出26状态 变量为 ON,ESP32 打开 LED。
这对于其它按钮的工作方式类似。所以,如果你想添加更多的输出,你应该修改这部分代码以包含它们。
显示 HTML 网页
您需要做的下一件事是创建网页。ESP32 将使用一些 HTML 代码向您的浏览器发送响应以构建网页。
使用此表达式将网页发送给客户端 客户端.println(). 您应该输入要发送给客户端的内容作为参数。
我们应该发送的第一件事始终是以下行,这表明我们正在发送 HTML。
<!DOCTYPE HTML><html>
然后,以下行使网页在任何 Web 浏览器中都具有响应性。
client.println("<head><meta name="viewport" content="width=device-width, initial-scale=1">");
以下内容用于阻止对网站图标的请求。– 您无需担心这条线路。
client.println("<link rel="icon" href="data:,">");
网页样式
接下来,我们有一些 CSS 文本来设置按钮和网页外观的样式。我们选择 Helvetica 字体,定义要显示为块并居中对齐的内容。
client.println("<style>html { font-family: Helvetica; display: inline-block; margin: 0px auto; text-align: center;}");
我们使用#4CAF50 颜色设置按钮样式,无边框,文本为白色,内边距为:16px 40px。我们还将 text-decoration 设置为 none,将字体大小、边距和光标定义为指针。
client.println(".button { background-color: #4CAF50; border: none; color: white; padding: 16px 40px;");
client.println("text-decoration: none; font-size: 30px; margin: 2px; cursor: pointer;}");
我们还定义了第二个按钮的样式,具有我们之前定义的按钮的所有属性,但颜色不同。这将是关闭按钮的样式。
client.println(".button2 {background-color: #555555;}</style></head>");
设置网页第一标题
在下一行中,您可以设置网页的第一个标题。这里我们有“ ESP32 Web Server ”,但您可以将此文本更改为您喜欢的任何内容。
// Web Page Heading
client.println("<h1>ESP32 Web Server</h1>");
显示按钮和对应状态
然后,你写一个段落来显示 通用输入输出口 26当前状态。如您所见,我们使用输出26状态 变量,以便在此变量更改时状态立即更新。
client.println("<p>GPIO 26 - State " + output26State + "</p>");
然后,我们根据 GPIO 的当前状态显示开或关按钮。如果 GPIO 的当前状态为关闭,则显示 ON 按钮,如果不是,则显示 OFF 按钮。
if (output26State=="off") {
client.println("<p><a href="/26/on"><button class="button">ON</button></a></p>");
} else {
client.println("<p><a href="/26/off"><button class="button button2">OFF</button></a></p>");
}
我们使用相同的程序 通用输入输出口 27.
关闭连接
最后,当响应结束时,我们清除 标题 变量,并停止与客户端的连接 客户端停止().
// Clear the header variable
header = "";
// Close the connection
client.stop();