从零开始的ESP8266探索(06)-使用Server功能搭建Web Server

目的

ESP8266可以方便的进行UDP通讯实现通过网络控制设备的功能,但是该方法对于用户来说还需要一个界面来完成数据通讯,所幸ESP系列有着不错的性能和不小的存储空间,足够用来构建简单的网页服务器(Web Server),这样用户就可以通过浏览器访问进行查看数据或控制设备。

这篇文章中更多的会涉及一些底层原理相关的内容,想要简单的使用可以参考《从零开始的ESP8266探索(09)-更加方便的ESP8266WebServer使用介绍》
如果你只是想要用TCP Server功能那也可以参考下面内容。

开启服务器

在Arduino for esp8266中启用服务功能非常简单,如下两步即可实现:

WiFiServer server(80); //建立服务器对象,设置监听端口号为80(网页默认端口号)
server.begin(); //启用服务器

只要上述两步就完成了Server的设置,接下来就是具体的处理工作了。

监听客户端

Server的具体处理事务在loop()函数中进行,首先请看下述代码:

String readString = ""; //建立一个字符串对象用来接收存放来自客户的数据

void loop()
{
    WiFiClient client = server.available(); //尝试建立客户对象
    if (client) //如果当前有客户可用
    {
        boolean currentLineIsBlank = true;
        Serial.println("[Client connected]");
        
        while (client.connected()) //如果客户端建立连接
        {
            if (client.available()) //等待有可读数据
            {
                char c = client.read(); //读取一字节数据
                readString += c; //拼接数据
                /************************************************/
                if (c == '\n' && currentLineIsBlank) //等待请求头接收完成(接收到空行)
                {
                    break;
                }

                if (c == '\n')
                {
                    currentLineIsBlank = true; //开始新行
                }
                else if (c != '\r')
                {
                    currentLineIsBlank = false; //正在接收某行中
                }
                /************************************************/
            }
        }
        delay(1); //等待客户完成接收
        client.stop(); //结束当前连接:
        Serial.println("[Client disconnected]");
        
        Serial.println(readString); //打印输出来自客户的数据
        readString = "";
    }
}

以上代码中除去/***//***/之间部分代码为监听客户端连接并接收数据功能;/***//***/之间部分代码用于对客户端的请求进行处理。将代码烧录到模块中进行测试,可以看到效果如下:
这里写图片描述
可以看到在浏览器访问Server的时候Server会接收到下面的信息:

GET / HTTP/1.1
Accept: text/html, application/xhtml+xml, image/jxr, /
Accept-Language: zh-Hans-CN,zh-Hans;q=0.8,ja;q=0.6,en-US;q=0.4,en;q=0.2
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko
Accept-Encoding: gzip, deflate
Host: 172.16.2.231
Connection: Keep-Alive

(还有一种GET /favicon.ico HTTP/1.1开头的,是请求图标的)
这个信息就是浏览器向Server发出的信息,到目前为止这只是一个标准的TCP Server,如果我们在收到浏览器发送的信息后做出对应的应答(HTTP协议),那这就变成了Web Server。

Web的请求与响应

上面的测试中浏览器发出的消息是什么,应该怎么处理,了解的这些就能完成完整的Web Server功能了,该部分内容可以参考如下:
这里写图片描述
这里写图片描述

实现Web Server功能

根据上文内容综合可知,对于Web Server来说只要接收到完整的请求头时返回相应的东西即可,更改上面部分代码我们的Web Server基本功能就实现了:
首先准备好响应头网页

//响应头
String responseHeaders =
    String("") +
    "HTTP/1.1 200 OK\r\n" +
    "Content-Type: text/html\r\n" +
    "Connection: close\r\n" +
    "\r\n";
//网页
String myhtmlPage = 
    String("") +
    "Hello World";

然后上文中代码改写如下:

if (c == '\n' && currentLineIsBlank) //等待请求头接收完成(接收到空行)
{
    //比较接收到的请求数据
    if (readString.startsWith("GET / HTTP/1.1")) //如果是网页请求
    {
        client.print(responseHeaders); //向客户端输出网页响应
        client.print(myhtmlPage); //向客户端输出网页内容
        client.print("\r\n");
    }
    else
    {
        client.print("\r\n");
    }
    break;
}

上述代码烧录运行后可以看到下面请,在浏览器访问时可以看到我们准备的网页内容:
这里写图片描述

通过网页收发数据

上面内容其实已经完成了基本的Web Server功能,现在我们再进一步,实现网页和服务器的数据交互功能,在这里需要用到AJAX知识,很简单,可以参考下面的教程:
http://www.w3school.com.cn/ajax/
http://www.runoob.com/ajax/ajax-tutorial.html
在下文中我们将要实现通过浏览器访问建立于ESP8266上的网页,网页中显示ESP8266模块上LED状态和一个按钮,通过点击按钮可以点亮/熄灭LED。

建立网页

首先我们准备一个网页:

<html>
<head>
    <title>ESP8266 Web Server Test</title>
    <script defer="defer">
        function ledSwitch() {
            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("txtState").innerHTML = xmlhttp.responseText;
                    }
                },
            xmlhttp.open("GET", "Switch", true);
            xmlhttp.send(); 
        }
    </script>
</head>
<body>
    <div id="txtState">Unkwon</div>
    <input type="button" value="Switch" onclick="ledSwitch()">
</body>
</html>

网页初始效果如下:
这里写图片描述
网页可以通过以下网站转成字符串:
http://www.css88.com/tool/html2js/

完善Web Server功能

下面是完整的需要烧录到ESP8266中的代码:

#include <ESP8266WiFi.h>

/*** 该工程可以在2.4.0版本esp8266库中运行,没在更高版本库中进行测试 ***/

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

WiFiServer server(80);

String readString = ""; //建立一个字符串对象用来接收存放来自客户的数据

//响应头
String responseHeaders =
    String("") +
    "HTTP/1.1 200 OK\r\n" +
    "Content-Type: text/html\r\n" +
    "Connection: close\r\n" +
    "\r\n";

//网页
String myhtmlPage =
    String("") +
    "<html>" +
    "<head>" +
    "    <title>ESP8266 Web Server Test</title>" +
    "    <script defer=\"defer\">" +
    "        function ledSwitch() {" +
    "            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(\"txtState\").innerHTML = xmlhttp.responseText;" +
    "                }" +
    "            }," +
    "            xmlhttp.open(\"GET\", \"Switch\", true);" +
    "            xmlhttp.send(); " +
    "        }" +
    "    </script>" +
    "</head>" +
    "<body>" +
    "    <div id=\"txtState\">Unkwon</div>" +
    "    <input type=\"button\" value=\"Switch\" οnclick=\"ledSwitch()\">" +
    "</body>" +
    "</html>";

bool isLedTurnOpen = false; // 记录LED状态

void setup()
{
    pinMode(2, OUTPUT);
    digitalWrite(2, HIGH); // 熄灭LED

    Serial.begin(115200);
    Serial.println();

    Serial.printf("Connecting to %s ", ssid);
    WiFi.begin(ssid, password);
    while (WiFi.status() != WL_CONNECTED)
    {
        delay(500);
        Serial.print(".");
    }
    Serial.println(" connected");

    server.begin();
    Serial.printf("Web server started, open %s in a web browser\n", WiFi.localIP().toString().c_str());
}

void loop()
{
    WiFiClient client = server.available(); //尝试建立客户对象
    if (client)                             //如果当前有客户可用
    {
        boolean currentLineIsBlank = true;
        Serial.println("[Client connected]");

        while (client.connected()) //如果客户端建立连接
        {
            if (client.available()) //等待有可读数据
            {
                char c = client.read(); //读取一字节数据
                readString += c;        //拼接数据
                /************************************************/
                if (c == '\n' && currentLineIsBlank) //等待请求头接收完成(接收到空行)
                {
                    //比较接收到的请求数据
                    if (readString.startsWith("GET / HTTP/1.1")) //如果是网页请求
                    {
                        client.print(responseHeaders); //向客户端输出网页响应
                        client.print(myhtmlPage);      //向客户端输出网页内容
                        client.print("\r\n");
                    }
                    else if (readString.startsWith("GET /Switch")) //如果是改变LED状态请求
                    {
                        if (isLedTurnOpen == false)
                        {
                            digitalWrite(2, LOW); // 点亮LED
                            client.print("LED has been turn on");
                            isLedTurnOpen = true;
                        }
                        else
                        {
                            digitalWrite(2, HIGH); // 熄灭LED
                            client.print("LED has been turn off");
                            isLedTurnOpen = false;
                        }
                    }
                    else
                    {
                        client.print("\r\n");
                    }
                    break;
                }

                if (c == '\n')
                {
                    currentLineIsBlank = true; //开始新行
                }
                else if (c != '\r')
                {
                    currentLineIsBlank = false; //正在接收某行中
                }
                /************************************************/
            }
        }
        delay(1);      //等待客户完成接收
        client.stop(); //结束当前连接:
        Serial.println("[Client disconnected]");

        Serial.println(readString); //打印输出来自客户的数据
        readString = "";
    }
}

上面的代码直接拷贝可能网页的字符串部分代码会出现编码问题导致无法正确运行,该部分最好删除重新手打。如果想偷懒的话可以试试只删除 onclick 这个词的 o 然后手打输入。

将上面代码烧录运行可以看到下面效果:
这里写图片描述

总结

使用Arduino for esp8266可以非常简单实现Web Server功能,更多详细内容可以参考如下:
Server功能示例:
https://arduino-esp8266.readthedocs.io/en/latest/esp8266wifi/server-examples.html
Server功能库说明:
https://arduino-esp8266.readthedocs.io/en/latest/esp8266wifi/server-class.html

这篇文章中更多的会涉及一些底层原理相关的内容,想要简单的使用可以参考《从零开始的ESP8266探索(09)-更加方便的ESP8266WebServer使用介绍》

评论 29
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Naisu Xu

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值