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

目的

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

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

开启服务器

在Arduino for esp8266中启用服务功能很是简单,以下两步便可实现:html

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

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

监听客户端

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

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会接收到下面的信息:web

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-Aliveajax

(还有一种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 = "";
    }
}

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

总结

使用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使用介绍》