在本教程中,我将向你展示如何在带有WiFi的Arduino设备上启动一个Web服务器,比如我的Arduino MKR WiFi 1010。
我们将连接到一个现有的WiFi网络,我们将能够通过HTTP从我们的浏览器与Arduino互动。
这对各种应用来说是非常有趣的。从简单的检查传感器的数据,到根据执行的HTTP请求执行行动。
我们将从这个在使用Arduino连接到WiFi网络的教程中定义的程序开始。
#include <SPI.h>
#include <WiFiNINA.h>
void setup() {
char ssid[] = SECRET_SSID;
char pass[] = SECRET_PASS;
Serial.begin(9600);
while (!Serial);
int status = WL_IDLE_STATUS;
while (status != WL_CONNECTED) {
Serial.print("Connecting to ");
Serial.println(ssid);
status = WiFi.begin(ssid, pass);
delay(5000);
}
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
}
void loop() {
}
在setup()之前,添加这一行。
来初始化80端口的TCP服务器,并在setup() 的最后调用
来启动该服务器。
现在这是一个TCP服务器,而不是一个HTTP服务器。但由于HTTP(一种TCP/IP应用协议)是建立在TCP(传输层)之上的,所以我们可以很容易地建立起自己的HTTP服务器。
首先,我们需要监听客户端的连接。我们在loop() 中这样做。
void loop() {
WiFiClient client = server.available();
if (client) {
}
}
server 的available()方法监听传入的客户端。
在if (client) {} 检查中,我们有一个HTTP客户端连接。我们需要做的是。
- 调用
client.connected(),检查数据是否被连接,并且有数据可以读取 - 调用
client.available(),以获得可供读取的字节数(这可确保有数据可读)。 - 调用
client.read(),从传入的数据(客户端发送的HTTP请求)中读取一个字节。 - 调用
client.println()或client.print()向客户端发送数据,建立一个适当的 HTTP 响应。 - 调用
client.stop()来结束连接
我们开始向串行接口打印客户端发送的每个字符,最后我们关闭连接。
void loop() {
WiFiClient client = server.available();
if (client) {
while (client.connected()) {
if (client.available()) {
char c = client.read();
Serial.write(c);
}
}
client.stop();
}
}
尝试在Arduino上上传这个程序。把你的浏览器指向IP地址。你会看到类似这样的东西打印到串行接口。这是由浏览器发送的内容。
GET / HTTP/1.1
Host: 192.168.1.40
Upgrade-Insecure-Requests: 1
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0.2 Safari/605.1.15
Accept-Language: en-us
Accept-Encoding: gzip, deflate
Connection: keep-alive
注意结尾的空行。这是HTTP请求的结束。
我们需要拦截这个空行。
HTTP请求中的每一行都是由CR回车符(\r)和LF换行符(\n)结束的。
因此,请求的结束可以通过2组这些序列来确定:\r\n\r\n 。
这个简单的算法会起作用,我们只需记住当前字符之前的2个字符,并检查我们是否确定了序列\n\r\n (该序列的最后3个字符足以确定最后一行)。
void loop() {
WiFiClient client = server.available();
if (client) {
char prevprev;
char prev;
while (client.connected()) {
if (client.available()) {
char c = client.read();
Serial.write(c);
if (prevprev == '\n' && prev == '\r' && c == '\n') {
//we can send the response!
}
prevprev = prev;
prev = c;
}
}
client.stop();
}
}
所以现在我们可以在if 里面发送响应,我们可以使用client.println() ,我们添加一个简单的响应,像这样。
HTTP/1.1 200 OK
Content-Type: text/html
Connection: close
<!DOCTYPE HTML>
<html>
test
</html>
以这种方式。
client.println("HTTP/1.1 200 OK");
client.println("Content-Type: text/html");
client.println("Connection: close");
client.println();
client.println("<!DOCTYPE HTML>");
client.println("<html>");
client.println("test");
client.println("</html>");
break;
break; 语句结束了while (client.connected()) {} 块。
这里是完整的程序。
#include <SPI.h>
#include <WiFiNINA.h>
WiFiServer server(80);
void setup() {
char ssid[] = SECRET_SSID;
char pass[] = SECRET_PASS;
Serial.begin(9600);
while (!Serial);
int status = WL_IDLE_STATUS;
while (status != WL_CONNECTED) {
Serial.print("Connecting to ");
Serial.println(ssid);
status = WiFi.begin(ssid, pass);
delay(5000);
}
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
server.begin();
}
void loop() {
WiFiClient client = server.available();
if (client) {
char prevprev;
char prev;
while (client.connected()) {
if (client.available()) {
char c = client.read();
Serial.write(c);
if (prevprev == '\n' && prev == '\r' && c == '\n') {
client.println("HTTP/1.1 200 OK");
client.println("Content-Type: text/html");
client.println("Connection: close");
client.println();
client.println("<!DOCTYPE HTML>");
client.println("<html>");
client.println("test");
client.println("</html>");
break;
}
prevprev = prev;
prev = c;
}
}
client.stop();
}
}
试试吧,你应该看到test 在浏览器中显示出来。

这种方法是有效的,直到你需要弄清楚客户问我们什么。
在这种情况下,你想阅读每一行,所以这种替代方法效果更好。
void loop() {
WiFiClient client = server.available();
if (client) {
String line = "";
while (client.connected()) {
if (client.available()) {
char c = client.read();
Serial.write(c);
if (c != '\n' && c != '\r') {
line += c;
}
if (c == '\n') {
if (line.length() == 0) {
client.println("HTTP/1.1 200 OK");
client.println("Content-Type: text/html");
client.println("Connection: close");
client.println();
client.println("<!DOCTYPE HTML>");
client.println("<html>");
client.println("test");
client.println("</html>");
break;
} else {
line = "";
}
}
}
}
client.stop();
}
}
在最后一个else ,我们可以检查该行,因为该行被终止了,并根据我们的需要采取相应的行动。