学习太极创客 — ESP8226 (九)JSON 数据通讯 三

视频链接:https://www.bilibili.com/video/BV1L7411c7jw?p=18&vd_source=b91967c499b23106586d7aa35af46413

程序链接:http://www.taichi-maker.com/homepage/esp8266-nodemcu-iot/iot-c/esp8266-nodemcu-web-client/esp8266-client-sends-json/

在这里插入图片描述
在这里插入图片描述

注意,以下示例中的服务器端和客户端ESP8266必须连接同一WiFi网络,方可实现数据通讯。

在学习本节课的内容之前,需要先学习 WiFiServer 库的基本操作。

WiFiServer 库的基本操作

该库的介绍链接: http://www.taichi-maker.com/homepage/iot-development/iot-dev-reference/esp8266-c-plus-plus-reference/wifiserver/

从名称看,这个库将是控制 ESP8266 以服务器的形式来工作的。而在之前的学习中,当让 ESP8266 以服务器的形式来工作时,使用的是 ESP8266WebServer 库。

WiFiServer 库 和 ESP8266WebServer 库之间有什么区别吗?

这两个库从功能上来说,是非常相似的,假如我们让 ESP8266 以服务器的模式来工作,再让服务器获取客户端的请求信息中的详细内容(请求体)时,就必须要使用 WiFiServer 库了。

在这里插入图片描述
下面来演示下如何使用程序对 WiFiServer 库进行基本操作,

使用 WiFiServer 库,首先要添加头文件 #include <ESP8266WiFi.h>。

/**********************************************************************
项目名称/Project          : 零基础入门学用物联网
程序名称/Program name     : wifiServer_example
团队/Team                : 太极创客团队 / Taichi-Maker (www.taichi-maker.com)
作者/Author              : CYNO朔
日期/Date(YYYYMMDD)     : 20200521
程序目的/Purpose          : 
本实例用于演示如何使用WiFiServer库利用ESP8266开发板建立基本网络服务器。
-----------------------------------------------------------------------
修订历史/Revision History  
日期/Date    作者/Author      参考号/Ref    修订说明/Revision Description
-----------------------------------------------------------------------
本示例程序为太极创客团队制作的《零基础入门学用物联网》中示例程序。
该教程为对物联网开发感兴趣的朋友所设计和制作。如需了解更多该教程的信息,请参考以下网页:
http://www.taichi-maker.com/homepage/esp8266-nodemcu-iot/
***********************************************************************/
#include <ESP8266WiFi.h>
 
const char* ssid = "taichimaker";
const char* password = "12345678";
 
WiFiServer server(80);
 
void setup() {
  Serial.begin(9600);
  Serial.println();
  
  WiFi.begin(ssid, password);
 
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(F("."));
  }
 
  // WiFi连接成功后将通过串口监视器输出连接成功信息 
  Serial.println('\n');                     // WiFi连接成功后
  Serial.print("Connected to ");            // NodeMCU将通过串口监视器输出。
  Serial.println(WiFi.SSID());              // 连接的WiFI名称
  Serial.print("IP address:\t");            // 以及
  Serial.println(WiFi.localIP());           // NodeMCU的IP地址
 
  // 启动服务器
  server.begin();
}
 
void loop() {
  runServer();  // 运行服务器
}
 
void runServer(){
  // 建立WiFiClient对象用于处理客户端请求信息
  WiFiClient incomingClient = server.available();
 
  // 如果没有客户端请求信息,则“跳过”函数中后续程序内容
  if (!incomingClient) {
    return;
  }
  
  Serial.println("====Client  Connected===");
 
  // 通过串口监视器输出客户端请求信息
  String clientRequest = incomingClient.readString();
  Serial.print(clientRequest);
 
// 建立服务器响应信息
  String httpResponse =
        "HTTP/1.0 200 OK\r\n"
        "Connection: close\r\n"
        "Content-Type: text/plain;\r\n"
        "\r\n"
        "client_request_received";
 
  // 向客户端发送以上服务器响应信息
  incomingClient.print(httpResponse); 
 
  incomingClient.stop();  
  Serial.println("incomingClient stop");   
}

在上述程序中,重点是要说明下 runServer(); 这个函数,这个函数的作用是运行服务器。在 runServer 函数中,首先是

 // 建立WiFiClient对象用于处理客户端请求信息
  WiFiClient incomingClient = server.available();

可能会有疑问,这里不是服务器端的程序吗,为什么要建立一个客户端对象呢?这是因为这段程序虽然是在服务器上运行的,但其主要作用是用来处理客户端的请求信息的。既然是处理客户端的请求信息的,因此就必须建立一个客户端的对象。

此外,在建立对象的右边还有 server.available() ,这部分的操作呢是一旦有客户端连接到服务器上,并且向服务器发送请求信息,我们将利用建立的 incomingClient 对象来处理向服务器发送的请求信息。

接着是

  // 如果没有客户端请求信息,则“跳过”函数中后续程序内容
  if (!incomingClient) {
    return;
  }

这里,为什么可以使用 incomingClient 这个对象来作为判断条件,通常都是使用 bool 类型的变量来作为判断条件的,那这是为什么呢?

对于 ESP8266 平台来说,当有客户端向服务器发送请求信息,对于该信息的处理是以 Stream 数据来处理的,而这个 Stream 数据,它总是要有一个载体的。而这个载体正是 incomingClient 这个对象。当然,incomingClient 作为 WiFiClient 对象,它不光只是一个载体,还会有其他的功能。

这个程序虽然说的是 WiFiServer 库,但是实际上,大量利用的是 WiFiClient 对象,因为 WiFiServer 库和WiFiClient 对象是密不可分的,我们在进行 WiFiServer 库的操作时,会用到很多 WiFiClient 对象中的内容来处理客户端请求信息,并且发送服务器响应信息。

下面来测试下程序,打开串口监视器
在这里插入图片描述
再打开浏览器,输入网址,
在这里插入图片描述
可见,服务器收到浏览器(客户端)的请求信息(串口中打印的数据)后,做出了响应(client_request_received)。

关于的浏览器图标文件 favicon.ico 的说明

浏览器调用 favicon 的原理是首先在网页所在目录(服务器端)寻找 favicon.ico 文件(这也是上图浏览器出现太极图标的原因,之前上传过文件系统。),如果没有找到就去浏览器的根目录下寻找(一般采用浏览器默认图标)。所以最简单的方法就是将制作好的 favicon 文件命名为favicon.ico然后上传到网站的根目录下。

客户端程序

主要功能:

  1. 实时读取 A0、 D1、D2 以及 D3 引脚的读数。
  2. 向服务器发送 Json 信息。发送的信息中包含有 D3 引脚状态从而控制服务器开发板上的LED点亮或熄灭。

以下为该json信息的示例:

{
  "info": {
    "name": "taichimaker",
    "url": "www.taichi-maker.com",
    "email": "taichimaker@163.com"
  },
  "digital_pin": {
    "d1": "1",
    "d2": "0",
    "d3": "1"
  },
  "analog_pin": {
    "a0": "500"
  }
}

具体客户端代码如下,

/**********************************************************************
项目名称/Project          : 零基础入门学用物联网
程序名称/Program name     : csj_client_one_json
团队/Team                : 太极创客团队 / Taichi-Maker (www.taichi-maker.com)
作者/Author              : Dapenson
日期/Date(YYYYMMDD)     : 20200425
程序目的/Purpose          : 
本实例用于演示esp8266的json数据通讯。
操作测试本程序需要使用两台8266开发板。其中一台为服务器端,一台为客户端
 
本程序为客户端程序,功能如下:
1. 实时读取A0、 D1、D2以及D3引脚的读数。
2. 向服务器发送Json信息。发送的信息中包含有D3引脚状态从而控制服务器开发板上
   的LED点亮或熄灭
-----------------------------------------------------------------------
修订历史/Revision History  
日期/Date    作者/Author      参考号/Ref    修订说明/Revision Description
20200520      CYNO朔           001        修改httpRequest将两个“/r/n”合并
-----------------------------------------------------------------------
本示例程序为太极创客团队制作的《零基础入门学用物联网》中示例程序。
该教程为对物联网开发感兴趣的朋友所设计和制作。如需了解更多该教程的信息,请参考以下网页:
http://www.taichi-maker.com/homepage/esp8266-nodemcu-iot/iot-c/esp8266-nodemcu-web-client/http-request/
***********************************************************************/
#include <ArduinoJson.h>
#include <ESP8266WiFi.h>
#include <ESP8266WiFiMulti.h>
 
ESP8266WiFiMulti wifiMulti;           // 建立ESP8266WiFiMulti对象
 
const char* host = "192.168.2.44"; // 网络服务器地址
const int httpPort = 80;            // http端口80
 
void setup(){
  Serial.begin(9600);          
  Serial.println("");

  // 将引脚设置为输入上拉模式
  pinMode(D1, INPUT_PULLUP);
  pinMode(D2, INPUT_PULLUP);
  pinMode(D3, INPUT_PULLUP);   // NodeMCU开发板按键连接在D3(GPIO0)引脚上
  
   //通过addAp函数存储  WiFi名称       WiFi密码
  wifiMulti.addAP("FAST_153C80", "123456798");  // 这三条语句通过调用函数addAP来记录3个不同的WiFi网络信息。
  wifiMulti.addAP("taichi-maker2", "87654321"); // 这3个WiFi网络名称分别是taichi-maker, taichi-maker2, taichi-maker3。
  wifiMulti.addAP("taichi-maker3", "13572468"); // 这3个网络的密码分别是123456789,87654321,13572468。
                                                // 此处WiFi信息只是示例,请在使用时将需要连接的WiFi信息填入相应位置。
                                                // 另外这里只存储了3个WiFi信息,您可以存储更多的WiFi信息在此处。
 
  int i = 0;                                 
  while (wifiMulti.run() != WL_CONNECTED) {  // 此处的wifiMulti.run()是重点。通过wifiMulti.run(),NodeMCU将会在当前
    delay(1000);                             // 环境中搜索addAP函数所存储的WiFi。如果搜到多个存储的WiFi那么NodeMCU
    Serial.print(i++); Serial.print(' ');    // 将会连接信号最强的那一个WiFi信号。
  }                                          // 一旦连接WiFI成功,wifiMulti.run()将会返回“WL_CONNECTED”。这也是
                                             // 此处while循环判断是否跳出循环的条件。
  // WiFi连接成功后将通过串口监视器输出连接成功信息 
  Serial.println('\n');                     // WiFi连接成功后
  Serial.print("Connected to ");            // NodeMCU将通过串口监视器输出。
  Serial.println(WiFi.SSID());              // 连接的WiFI名称
  Serial.print("IP address:\t");            // 以及
  Serial.println(WiFi.localIP());           // NodeMCU的IP地址
}
 
void loop(){
  // 发送HTTP请求
  httpRequest();    
  
  delay(3000);
}
 
// 向服务器发送HTTP请求,请求信息中包含json信息
void httpRequest(){
  // 建立WiFi客户端对象,对象名称client
  WiFiClient client;    
 
  // 重点1: 建立JSON,此JSON包含需要发送的信息
  StaticJsonDocument<384> doc;

  JsonObject info = doc.createNestedObject("info");
  info["name"] = "taichimaker";
  info["url"] = "www.taichi-maker.com";
  info["email"] = "taichimaker@163.com";

  JsonObject digital_pin = doc.createNestedObject("digital_pin");
  digital_pin["d1"] = String(digitalRead(D1));
  digital_pin["d2"] = String(digitalRead(D2));
  digital_pin["d3"] = String(digitalRead(D3));
  doc["analog_pin"]["a0"] = String(analogRead(A0));

  String payloadJson;
  serializeJson(doc, payloadJson);
  
  // 建立字符串,用于HTTP请求
  String httpRequest =  String("GET /") + " HTTP/1.1\r\n" +
                        "Host: " + host + "\r\n" +
                        "Connection: close\r\n\r\n" + payloadJson;
  
  // 通过串口输出连接服务器名称以便查阅连接服务器的网址                      
  Serial.print("Connecting to "); 
  Serial.print(host); 
 
  if (client.connect(host, httpPort)){ 
    Serial.println(" Success!");            // 连接成功后串口输出“Success”信息
    
    client.print(httpRequest);              // 向服务器发送请求
    Serial.println("Sending request: ");    // 通过串口输出HTTP请求信息内容以便查阅
    Serial.println(httpRequest);     
    
    Serial.println("Web Server Response:"); // 通过串口监视输出服务器响应信息        
    while (client.connected() || client.available()){ 
      if (client.available()){
        String line = client.readStringUntil('\n');
        Serial.println(line);
      }
    } 
  } else{    // 如果连接不成功则通过串口输出“连接失败”信息
    Serial.println(" failed!");
  } 
  
  client.stop();                      // 断开与服务器的连接
  Serial.print("Disconnected from "); // 并且通过串口输出断开连接信息
  Serial.println(host);   
}

原网页代码中没有加上LED的初始化设置,pinMode(D3, INPUT_PULLUP); 所以运行结果有问题。

这里还需要注意的是用于 HTTP 请求的信息,

 // 重点1: 建立JSON,此JSON包含需要发送的信息
  String payloadJson = "{\"info\": {\"name\": \"taichimaker\",\"url\": \"www.taichi-maker.com\",\"email\": \"taichimaker@163.com\"},\"digital_pin\": {\"d1\": \"";
  payloadJson += String(digitalRead(D1));  
  payloadJson += "\",\"d2\": \""; 
  payloadJson += String(digitalRead(D2));  
  payloadJson += "\",\"d3\": \""; 
  payloadJson += String(digitalRead(D3));  
  payloadJson += "\"},\"analog_pin\": {\"a0\": \"";
  payloadJson += String(analogRead(A0));
  payloadJson += "\"}}";  

上述代码可以使用前面介绍的自动生成 JSON 代码的网站自动生成。

// 建立字符串,用于HTTP请求
  String httpRequest =  String("GET /") + " HTTP/1.1\r\n" +
                        "Host: " + host + "\r\n" +
                        "Connection: close\r\n\r\n" + payloadJson;

需要注意,通常 http 请求包含三个部分,分别是请求行、请求头和请求体。这里的

String("GET /") + " HTTP/1.1\r\n" +

是请求行,

"Host: " + host + "\r\n" +
"Connection: close\r\n\r\n" +

是请求头,之后间隔一个空行,所以建立的 JSON 信息 payloadJson 是位于请求体中的内容。

服务器端程序

在掌握了如何使用 WiFiServer 库来获取客户端请求信息后,接下来我们开始学习服务器端示例程序。该程序将会实现以下功能。

  1. 获取客户端请求信息中的json
  2. 解析 json 信息内容
  3. 将解析后的数据信息显示于串口监视器
  4. 利用 json 中客户端 D3 引脚(按键引脚)读数来控制服务器端开发板上 LED 的点亮和熄灭

具体服务器端代码如下,

/**********************************************************************
项目名称/Project          : 零基础入门学用物联网
程序名称/Program name     : csj_server
团队/Team                : 太极创客团队 / Taichi-Maker (www.taichi-maker.com)
作者/Author              : CYNO朔
日期/Date(YYYYMMDD)     : 20200425
程序目的/Purpose          : 
本实例用于演示esp8266的json数据通讯。
操作测试本程序需要使用两台8266开发板。其中一台为服务器端,一台为客户端。
本程序为服务器程序,功能如下:
 
1. 获取客户端请求信息中的json。
2. 解析json信息内容。
3. 将解析后的数据信息显示于串口监视器
4. 利用json中客户端D3引脚(按键引脚)读数来控制服务器端开发板上LED的点亮和熄灭
-----------------------------------------------------------------------
修订历史/Revision History  
日期/Date    作者/Author      参考号/Ref    修订说明/Revision Description
20200520      CYNO朔           001        修改parseInfo部分,使其与cgj_client_2
                                          更加一致
-----------------------------------------------------------------------
本示例程序为太极创客团队制作的《零基础入门学用物联网》中示例程序。
该教程为对物联网开发感兴趣的朋友所设计和制作。如需了解更多该教程的信息,请参考以下网页:
http://www.taichi-maker.com/homepage/esp8266-nodemcu-iot/
***********************************************************************/
#include <ESP8266WiFi.h>
#include <ArduinoJson.h>
#include <ESP8266WiFiMulti.h>   
 
ESP8266WiFiMulti wifiMulti;     // 建立ESP8266WiFiMulti对象,对象名称是'wifiMulti'
 
IPAddress local_IP(192, 168, 2, 44); // 设置ESP8266-NodeMCU联网后的IP
IPAddress gateway(192, 168, 2, 1);    // 设置网关IP(通常网关IP是WiFI路由IP)
IPAddress subnet(255, 255, 255, 0);   // 设置子网掩码
IPAddress dns(192,168,2,1);           // 设置局域网DNS的IP(通常局域网DNS的IP是WiFI路由IP)
 
// 建立WiFiServerSecure对象
WiFiServer server(80);
 
void setup() {
  Serial.begin(9600);
  Serial.println();
  
  pinMode(LED_BUILTIN, OUTPUT);
  digitalWrite(LED_BUILTIN, HIGH);
  
  // 设置开发板网络环境
  if (!WiFi.config(local_IP, gateway, subnet)) {
    Serial.println("Failed to Config ESP8266 IP"); 
  } 
  
  //通过addAp函数存储  WiFi名称       WiFi密码
  wifiMulti.addAP("FAST_153C80", "123456798"); // 这三条语句通过调用函数addAP来记录3个不同的WiFi网络信息。
  wifiMulti.addAP("taichi-maker2", "87654321"); // 这3个WiFi网络名称分别是taichi-maker, taichi-maker2, taichi-maker3。
  wifiMulti.addAP("taichi-maker3", "13572468"); // 这3个网络的密码分别是123456789,87654321,13572468。
                                                // 此处WiFi信息只是示例,请在使用时将需要连接的WiFi信息填入相应位置。
                                                // 另外这里只存储了3个WiFi信息,您可以存储更多的WiFi信息在此处。
 
  int i = 0;                                 
  while (wifiMulti.run() != WL_CONNECTED) {  // 此处的wifiMulti.run()是重点。通过wifiMulti.run(),NodeMCU将会在当前
    delay(1000);                             // 环境中搜索addAP函数所存储的WiFi。如果搜到多个存储的WiFi那么NodeMCU
    Serial.print(i++); Serial.print(' ');    // 将会连接信号最强的那一个WiFi信号。
  }                                          // 一旦连接WiFI成功,wifiMulti.run()将会返回“WL_CONNECTED”。这也是
                                             // 此处while循环判断是否跳出循环的条件。
  // WiFi连接成功后将通过串口监视器输出连接成功信息 
  Serial.println('\n');                     // WiFi连接成功后
  Serial.print("Connected to ");            // NodeMCU将通过串口监视器输出。
  Serial.println(WiFi.SSID());              // 连接的WiFI名称
  Serial.print("IP address:\t");            // 以及
  Serial.println(WiFi.localIP());           // NodeMCU的IP地址
 
  // 启动服务器
  server.begin();
}
 
void loop() {
  runServer();  // 运行服务器
}
 
// 运行服务器
void runServer(){
  // 重点1:建立WiFiClient对象用于处理客户端请求信息
  WiFiClient incomingClient = server.available();
 
  // 如果没有客户端连接服务器,则“跳过”本函数中后续程序内容
  if (!incomingClient) {
    return;
  }
  
  Serial.println("====Client  Connected===");
  
  // 重点2:如果有客户端连接服务器,则尝试使用find跳过HTTP请求头
  if (incomingClient.find("\r\n\r\n")) {
    Serial.println("Found Header End. Start Parsing.");
  }
  
  // 解析请求体中的json信息 
  parseInfo(incomingClient);
 
  // 建立服务器响应信息
  String httpResponse =
        "HTTP/1.0 200 OK\r\n"
        "Connection: close\r\n"
        "Content-Type: text/plain;\r\n"
        "\r\n"
        "client_message_received";
 
  // 向客户端发送以上服务器响应信息
  incomingClient.print(httpResponse); 
 
  incomingClient.stop();  
  Serial.println("incomingClient stop");     
}
 
// 重点3:解析请求体中的json信息
void parseInfo(WiFiClient client){
  bool d3_bool;          // 建立变量存储客户端开发板按键信息
  String info_name_str;  // 建立变量存储字符串信息
  int a0_int; // 建立变量存储客户端开发板模拟输入引脚读数
  
  StaticJsonDocument<384> doc;
  
  DeserializationError error = deserializeJson(doc, client);
  
  if (error) {
    Serial.print(F("deserializeJson() failed: "));
    Serial.println(error.f_str());
    return;
  }
  
  JsonObject info = doc["info"];
  if(info){
    Serial.println("Server Json has info: true");
    info_name_str = info["name"].as<String>(); // "taichimaker"
    Serial.print("info_name_str = ");Serial.println(info_name_str);
  }else{
    Serial.println("Server Json has info: false");
  }
  
  JsonObject digital_pin = doc["digital_pin"];
  if(digital_pin){
    Serial.println("Server Json has digital_pin: true");
    d3_bool = digital_pin["d3"].as<int>(); // "1"
    d3_bool == 0 ? digitalWrite (LED_BUILTIN, LOW) : digitalWrite(LED_BUILTIN, HIGH);
    Serial.print("d3_bool = ");Serial.println(d3_bool);
  }else{
    Serial.println("Server Json has digital_pin: false");
  }
  
  const char* analog_pin_a0 = doc["analog_pin"]["a0"]; // "500"
  if(analog_pin_a0){
    Serial.println("Server Json has analog_pin_a0: true");
    a0_int = doc["analog_pin"]["a0"].as<int>(); 
    Serial.print("a0_int = ");Serial.println(a0_int);
  }else{
    Serial.println("Server Json has analog_pin_a0: false");
  }
}

运行代码后,服务器端运行结果如下,
在这里插入图片描述
客户端程序运行结果如下,
在这里插入图片描述

后面还有一部分是讲解使用 ESP8266 客户端发送多种 JSON 数据信息的例子,但是大同小异,需要的话再来仔细研究下。

目前,我们使用的是都是局域网实现的,还没有使用互联网来实现,后面的学习将使用互联网实现。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

xuechanba

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

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

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

打赏作者

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

抵扣说明:

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

余额充值