• ESP8266--Arduino开发(搭建HTTP网络服务器)


    一、前言

    超文本传输协议(Hyper Text Transfer Protocol,HTTP)是一个简单的请求-响应协议,它通常运行在TCP之上,它指定了客户端可能发送给服务器什么样的消息以及得到什么样的响应,,我们使用浏览器打开的网页使用的就是HTTP协议。

    接下来我们会参数在ESP8266-NodeMCU上建立一个HTTP网络服务器,然后通过浏览器来访问它。


    二、搭建HTTP网络服务器

    /*
      ESP8266-NodeMCU作为HttpServer服务器
    */
     
    #include                         // 本程序使用ESP8266WiFi库
    #include                    // web服务器通信库需要使用
    
    /* 1. 设置Wifi接入信息 */
    const char* ssid     = "LaiFu";                // 需要连接到的WiFi名
    const char* password = "wangjichuan";          // 连接的WiFi密码
    
    /* 2. 创建一个web服务器对象,使用80端口,HTTP网络服务器标准端口号即为80 */
    ESP8266WebServer esp8266_server(80);
    
    /* 3. 处理访问网站根目录“/”的访问请求 */
    void handleRoot() { 
      esp8266_server.send(200, "text/plain", "Hello from ESP8266");     // NodeMCU将调用此函数。
    }
    
    /* 4. 设置处理404情况的函数'handleNotFound' */
    void handleNotFound(){                                              // 当浏览器请求的网络资源无法在服务器找到时,
      esp8266_server.send(404, "text/plain", "404: Not found");         // NodeMCU将调用此函数。
    }
    
    void setup() {
      /* 1. 初始化串口通讯波特率为115200*/
      Serial.begin(115200);
    
      /* 2. 开启wifi连接,连接成功后打印IP地址 */
      WiFi.mode(WIFI_STA);                          // 设置Wifi工作模式为STA,默认为AP+STA模式
      WiFi.begin(ssid, password);                   // 通过wifi名和密码连接到Wifi
      Serial.print("\r\nConnecting to ");           // 串口监视器输出网络连接信息
      Serial.print(ssid); Serial.println(" ...");   // 显示NodeMCU正在尝试WiFi连接
      int i = 0;                                    // 检查WiFi是否连接成功
      while (WiFi.status() != WL_CONNECTED)         // WiFi.status()函数的返回值是由NodeMCU的WiFi连接状态所决定的。 
      {                                             // 如果WiFi连接成功则返回值为WL_CONNECTED
        delay(1000);                                // 此处通过While循环让NodeMCU每隔一秒钟检查一次WiFi.status()函数返回值
        Serial.print("waiting for ");                          
        Serial.print(i++); Serial.println("s...");       
      }                                             
      Serial.println("");                           // WiFi连接成功后
      Serial.println("WiFi connected!");            // NodeMCU将通过串口监视器输出"连接成功"信息。
      Serial.print("IP address: ");                 // 同时还将输出NodeMCU的IP地址。这一功能是通过调用
      Serial.println(WiFi.localIP());               // WiFi.localIP()函数来实现的。该函数的返回值即NodeMCU的IP地址。
    
      /* 3. 开启http网络服务器功能 */
      esp8266_server.begin();                       // 启动http网络服务器
      esp8266_server.on("/", handleRoot);           // 设置请求根目录时的处理函数函数
      esp8266_server.onNotFound(handleNotFound);    // 设置无法响应时的处理函数    
      Serial.println("HTTP esp8266_server started");// 告知用户ESP8266网络服务功能已经启动
    }
    
    void loop() {
      esp8266_server.handleClient();                // 处理http访问,需要一直运行
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55

    ESP8266连接手机热点

    请添加图片描述

    打开手机浏览器,输入ESP8266的IP

    显示Hello FROM esp8266字符串说明HTTP服务器访问正常

    在这里插入图片描述


    三、添加一个简单网页进行交互

    通过上面的步骤只是搭建好了HTTP网络服务器的框架,现在嵌入一个简单的网页来试试。

    这个网页提供两个按钮:关灯、开灯

    主要是要添加路径响应函数,在函数中设置小灯的亮灭

    在这里插入图片描述

    esp8266_server.on()中的路径对应于表单的提交按钮action属性的路径

    在这里插入图片描述

    /*
      ESP8266-NodeMCU作为HttpServer服务器
    */
     
    #include                         // 本程序使用ESP8266WiFi库
    #include                    // web服务器通信库需要使用
    
    /* 1. 设置Wifi接入信息 */
    const char* ssid     = "LaiFu";                // 需要连接到的WiFi名
    const char* password = "wangjichuan";          // 连接的WiFi密码
    
    /* 2. 创建一个web服务器对象,使用80端口,HTTP网络服务器标准端口号即为80 */
    ESP8266WebServer esp8266_server(80);
    
    /* 3. 处理访问网站根目录“/”的访问请求 */
    void handleRoot() { 
      String htmlCode = "\n"; 
      htmlCode +=       " \n";
      htmlCode +=       "   \n";
      htmlCode +=       "     \n";
      htmlCode +=       "     ESP8266 Butoon Ctrl\n";
      htmlCode +=       "   \n"; 
      htmlCode +=       "   \n";
      htmlCode +=       "     

    esp8266控制开关

    "
    ; htmlCode += "

    \n"
    ; htmlCode += "

    \n"
    ; htmlCode += " \n"; htmlCode += " \n"; esp8266_server.send(200, "text/html", htmlCode); // NodeMCU将调用此函数。 } /* 4. 设置处理404情况的函数'handleNotFound' */ void handleNotFound(){ // 当浏览器请求的网络资源无法在服务器找到时, esp8266_server.send(404, "text/plain", "404: Not found"); // NodeMCU将调用此函数。 } void handle_LED_ON() { Serial.println("handle_LED_ON"); digitalWrite(2, LOW); } void handle_LED_OFF() { Serial.println("handle_LED_OFF"); digitalWrite(2, HIGH); } void setup() { /* 1. 初始化串口通讯波特率为115200*/ Serial.begin(115200); //配置GPIO2为输出模式 pinMode(2, OUTPUT); digitalWrite(2, LOW); /* 2. 开启wifi连接,连接成功后打印IP地址 */ WiFi.mode(WIFI_STA); // 设置Wifi工作模式为STA,默认为AP+STA模式 WiFi.begin(ssid, password); // 通过wifi名和密码连接到Wifi Serial.print("\r\nConnecting to "); // 串口监视器输出网络连接信息 Serial.print(ssid); Serial.println(" ..."); // 显示NodeMCU正在尝试WiFi连接 int i = 0; // 检查WiFi是否连接成功 while (WiFi.status() != WL_CONNECTED) // WiFi.status()函数的返回值是由NodeMCU的WiFi连接状态所决定的。 { // 如果WiFi连接成功则返回值为WL_CONNECTED delay(1000); // 此处通过While循环让NodeMCU每隔一秒钟检查一次WiFi.status()函数返回值 Serial.print("waiting for "); Serial.print(i++); Serial.println("s..."); } Serial.println(""); // WiFi连接成功后 Serial.println("WiFi connected!"); // NodeMCU将通过串口监视器输出"连接成功"信息。 Serial.print("IP address: "); // 同时还将输出NodeMCU的IP地址。这一功能是通过调用 Serial.println(WiFi.localIP()); // WiFi.localIP()函数来实现的。该函数的返回值即NodeMCU的IP地址。 /* 3. 开启http网络服务器功能 */ esp8266_server.begin(); // 启动http网络服务器 esp8266_server.on("/", handleRoot); // 设置请求根目录时的处理函数函数 esp8266_server.onNotFound(handleNotFound); // 设置无法响应时的处理函数 esp8266_server.on("/LED_ON", handle_LED_ON); // 设置请求开灯目录时的处理函数函数 esp8266_server.on("/LED_OFF", handle_LED_OFF);// 设置请求关灯目录时的处理函数函数 Serial.println("HTTP esp8266_server started");// 告知用户ESP8266网络服务功能已经启动 } void loop() { esp8266_server.handleClient(); // 处理http访问,需要一直运行 }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86

    在处理根目录访问请求函数handleRoot()中添加了html字符串,所以打开ESP8266的IP就能看到解析的网页:

    • 点击开灯可以看见开发板小灯打开;
    • 点击关灯可以看见开发板小灯关闭;

    在这里插入图片描述


    四、ESP8266WebServer库

    4.1、WebServer管理方法

    1、创建Web Server

    ESP8266WebServer(IPAddress addr, int port = 80);
    ESP8266WebServer(int port = 80);
    
    //例如:创建一个web服务器对象,使用80端口
    ESP8266WebServer esp8266_server(80);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • addr:IP地址
    • port:端口号

    2、启动Web Server

    void begin();
    void begin(uint16_t port);
    
    //例如:启动Web Server
    esp8266_server.begin(); 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • port:端口号
    • begin函数要在配置好各个请求后使用

    3、关闭Web Server

    close();
    stop();
    
    //例如:关闭Web Server
    esp8266_server.close(); 
    
    • 1
    • 2
    • 3
    • 4
    • 5

    4.2、配置client请求处理方法

    1、请求响应回调

    void on(const String &url, THandlerFunction handler);
    
    • 1
    • url:路径
    • handler:对应url的处理函数

    注意:这里的handler函数是Http_ANY,不区分GET、POST等

    void on(const String &url, HTTPMethod method, THandlerFunction fn);
    
    • 1
    • url:路径
    • method:Http请求方法(HTTP_ANY, HTTP_GET, HTTP_POST, HTTP_PUT, HTTP_PATCH, TTP_DELETE, HTTP_OPTIONS)
    • fn:对应url的处理函数
    void on(const String &url, HTTPMethod method, THandlerFunction fn ThandlerFunction ufn);
    
    • 1
    • url:路径
    • method:Http请求方法(HTTP_ANY, HTTP_GET, HTTP_POST, HTTP_PUT, HTTP_PATCH, TTP_DELETE, HTTP_OPTIONS)
    • fn:对应url的处理函数
    • ufn:文件上传处理函数

    2、配置无效url的handler

    void onNotFound(THandlerFunction fn);
    
    • 1
    • fn:对应的处理函数

    注意:当找不到相对于的http请求处理函数时会调用该函数配置的fn方法

    3、配置处理文件上传的handler

    void onFileUpload(THandlerFunction fn);
    
    • 1
    • fn:对应的处理函数

    4.3、获取请求方法

    1、获取请求的url

    String url();
    
    • 1

    2、获取请求方法

    HTTPMethod method()
    
    • 1
    • 返回值: HTTP_ANY, HTTP_GET, HTTP_POST, HTTP_PUT, HTTP_PATCH, HTTP_DELETE, HTTP_OPTIONS

    3、获取请求参数的值

    String arg(String name);
    
    • 1
    • name:根据关键字name获取请求参数的值
    String arg(int i);
    
    • 1
    • i:获取第i个请求参数的值

    4、获取请求参数的名称

    String arg(int i);
    
    • 1
    • i:获取第i个请求参数的名称

    5、获取参数个数

    int args();
    
    • 1

    6、是否存在某个参数

    bool hasArg(String name);
    
    • 1
    • name:参数的名称

    7、设置需要收集的请求头

    void collectHeaders(const char* headerKeys[], const size_t headerKeysCount); 
    
    • 1
    • headerkeys[]:请求头的名字
    • headerkeysCount:请求头的个数

    8、获取请求头参数

    String header(String name);
    
    • 1
    • name:请求头名称
    String header(int i);
    
    • 1
    • i:获取第i个请求头参数

    9、获取请求头名字

    String headerName(int i);
    
    • 1
    • i:获取第i个请求头名字

    10、获取请求头个数

    int headers();
    
    • 1

    11、判断是否存在某个请求头

    bool hasHeader(String name);
    
    • 1

    12、获取请求头Host的值

    String hostHeader();
    
    • 1

    13、认证校验

    bool authenticate(const char * username, const char * password);
    
    • 1
    • username: 用户账号
    • password: 用户密码

    14、处理http请求

    void handleClient();
    
    • 1

    4.4、获取client请求方法

    1、处理文件上传

    HTTPUpload& upload();
    
    • 1
    //实例说明 非完整代码,无法直接运行,理解即可
    /**
     * 处理文件上传 HandlerFunction
     * 此方法会在文件上传过程中多次回调,我们可以判断上传状态
     */
    void handleFileUpload() {
      //判断http requestUri
      if (server.uri() != "/edit") {
        return;
      }
      //获得 Http上传文件处理对象
      HTTPUpload& upload = server.upload();
      //文件开始上传
      if (upload.status == UPLOAD_FILE_START) {
        String filename = upload.filename;
        if (!filename.startsWith("/")) {
          filename = "/" + filename;
        }
        DBG_OUTPUT_PORT.print("handleFileUpload Name: "); DBG_OUTPUT_PORT.println(filename);
        //本地文件系统创建一个文件用来保存内容
        fsUploadFile = SPIFFS.open(filename, "w");
        filename = String();
      } else if (upload.status == UPLOAD_FILE_WRITE) {
        //文件开始写入文件
        //DBG_OUTPUT_PORT.print("handleFileUpload Data: "); DBG_OUTPUT_PORT.println(upload.currentSize);
        if (fsUploadFile) {
          //写入文件
          fsUploadFile.write(upload.buf, upload.currentSize);
        }
      } else if (upload.status == UPLOAD_FILE_END) {
        //文件上传结束
        if (fsUploadFile) {
          fsUploadFile.close();
        }
        DBG_OUTPUT_PORT.print("handleFileUpload Size: "); DBG_OUTPUT_PORT.println(upload.totalSize);
      }
    }
     
    //注册文件上传处理回调
    server.on("/edit", HTTP_POST, []() {
        server.send(200, "text/plain", "");
      }, handleFileUpload);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42

    2、设置响应头

    void sendHeader(const String& name, const String& value, bool first = false);
    
    • 1
    • name: 响应头名
    • value: 响应头值
    • first: 是否需要放在第一行

    3、设置响应体长度

    void setContentLength(const size_t contentLength);
    
    • 1

    4、发送响应内容

    void sendContent(const String& content);
    void sendContent_P(PGM_P content);
    void sendContent_P(PGM_P content, size_t size);
    
    • 1
    • 2
    • 3

    5、发送响应文件流

    size_t streamFile(T &file, const String& contentType);
    
    • 1
    • file: 具体文件
    • contentType: 响应类型

    6、发送响应数据

    void send(int code, const char* content_type = NULL, const String& content = String(""));
    void send(int code, char* content_type, const String& content);
    void send(int code, const String& content_type, const String& content);
    void send_P(int code, PGM_P content_type, PGM_P content);
    void send_P(int code, PGM_P content_type, PGM_P content, size_t contentLength);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • code: 响应状态码
    • content_type: 响应内容类型
    • content:具体响应内容
  • 相关阅读:
    C语言---08自定义数据类---02共用体union与枚举enum
    [SpringBoot]基于jasypt-spring-boot-starter对配置加解密
    常见的Transforms(一)Compose & Normalize
    《TCPIP网络编程》课后练习答案第一部分1~5章 尹圣雨
    重装系统后电脑耳机插前面没有声音输出怎么办?
    【VUE项目实战】56、商品添加功能(六)-提交添加的商品
    DeepFace【部署 03】轻量级人脸识别和面部属性分析框架deepface在Linux环境下服务部署(conda虚拟环境+docker)
    Windows端ZLMediaKit编译与webrtc推拉流测试
    Redis篇---第十一篇
    展讯多语言支持列表
  • 原文地址:https://blog.csdn.net/Mr_robot_strange/article/details/127843516