JSON( JSON基础,ESP8266 JSON解析)ESP8266通过JSON实现物联网数据通讯( ESP8266客户端请求JSON信息, ESP8266客户端发送JSON信息)

JSON

JSON基础

JSON(JavaScript Object Notation) 是一种通用的轻量级数据交换文本格式。它很容易让人阅读和编写,也便于机器进行解析和生成。它使用JavaScript语法来存储和描述数据对象,但是JSON完全独立于JavaScript。JSON可适用于多种流行编程语言。这些特性使JSON成为理想的数据交换格式。

  • 数据以“名”“值”对呈现
  • 数据“名”和“值”之间由冒号分隔
  • 大括号{}用于标注对象内容
  • 中括号[]用于标注数组内容
  • 逗号用于分隔数据、对象、数组
  • JSON数据

    JSON数据以“名”“值”对呈现。数据“名”“值”由冒号分隔。JSON数据的书写格式是:

    “JSON数据名”:JSON数据值

    JSON数据举例:

    “Year”: 2016
    “URL”:”www.taichi-maker.com”

    JSON数组数据示例
    "info": [
        {
            "name" : "taichi-maker",
            "website" : "www.taichi-maker.com"
        },
        {
            "year": 2020,
            "month": 12,
            "day": 30
        }
    ]
    JSON对象数据示例
    "info": {
        "name" : "taichi-maker",
        "website" : "www.taichi-maker.com"
    }

    JSON 对象

    JSON对象在大括号{}中书写,对象可以包含单个或者多个JSON数据。
    对象(object) 是一个无序的数据集合(“‘名/值’对”集合)。一个对象以“{”(左括号)开始,“}”(右括号)结束。每个“名称”后跟一个“:”(冒号);“‘名/值’ 对”之间使用“,”(逗号)分隔。

    以下是含有单个数据的JSON对象示例:

    {
        "name" : "taichi-maker"
    }

    以下是含有多个数据的JSON对象示例。该对象由两个JSON数据组成。

    {
        "name" : "taichi-maker",
        "website" : "www.taichi-maker.com"
    }

    多个数据的JSON对象示例该对象包含两个JSON数据。而每一个JSON数据又包含一个JSON对象。

    {
      "info": {
    	"name": "taichi-maker",
    	"website": "www.taichi-maker.com"
      },
      "date": {
    	"year": 2020,
    	"month": 12,
    	"day": 30
      }
    }
    问题注意 

    注意:对象不能直接存放对象,以下示例是错误的。

    {
      {
    	"name": "taichi-maker",
    	"website": "www.taichi-maker.com"
      },
      {
    	"year": 2020,
    	"month": 12,
    	"day": 30
      }
    }

    注意:对象也不能直接存放数组,以下示例是错误的。

    {
      "info": {
    	"name": "taichi-maker",
    	"website": "www.taichi-maker.com"
      },
      [
    	{
    	 "temperature" : 15
    	}
      ]
    }

     JSON 数组

    数组可包含一个或者多个对象。以下是包含单个对象的数组示例:

    [
        {
            "name" : "taichi-maker",
            "website" : "www.taichi-maker.com"
        }
    ]

    以下是包含多个对象的数组示例:

    
    [
        {
          "name" : "taichi-maker",
          "website" : "www.taichi-maker.com"
       },
       {
          "year": 2020,
          "month": 12,
          "day": 30
       }
    ]

    数组也可以包含单个或多个数组,如下所示:

    [
        [
            {
                "name" : "taichi-maker",
                "website" : "www.taichi-maker.com"
            },
            {
                "year": 2020,
                "month": 12,
                "day": 30
            }
        ],
        [
            {
                "temperature" : 15,
            }
        ]		
    ]
    问题注意 

    注意:数组不能直接存放JSON数据。以下示例是错误的。

    [
    	"date": "2020-02-02",
    	"weekday": "THU"
    ]

    JSON 对象与数组混合存放示例

    {
      "results": [
        {
          "location": {
            "name": "Beijing",
            "country": "CN"
          },
          "now": {
            "text": "Clear",
            "code": "1",
            "temperature": "3"
          },
          "last_update": "2020-03-01T20:10:00+08:00"
        }
      ]
    }

    总结

    JSON文件乍一看很复杂,但只要注意以下几点就可以分析出JSON数据内容。

    首先注意以下符号的含义:
    :用于分隔数据的“名”和“值”
    {} 标注对象内容
    []标注数组内容
    ,分隔数据、对象和数组

    另外注意:
    对象用于存放数据(名值对)。
    对象不能直接存放对象或数组。
    数组存放元素有序号(序号起始值0)。
    数组不能直接存放数据(名值对)。

    ESP8266 JSON解析

    官网:https://arduinojson.org/

    JSON解析示例-1:单一对象JSON解析

    {
      "name": "taichi-maker",
      "number": 1
    }
    
    
    
    
    /**********************************************************************
    项目名称/Project          : 零基础入门学用物联网
    程序名称/Program name     : arduinojosn_1_object
    团队/Team                : 太极创客团队 / Taichi-Maker (www.taichi-maker.com)
    作者/Author              : CYNO朔
    日期/Date(YYYYMMDD)     : 20200424
    程序目的/Purpose          : 
    此程序用于演示如何使用arduinojson库解析以下json信息。该json包含一个对象,
    对象中有一个数据。
    {
      "name": "taichi-maker",
      "number": 1
    }
    -----------------------------------------------------------------------
    本示例程序为太极创客团队制作的《零基础入门学用物联网》中示例程序。
    该教程为对物联网开发感兴趣的朋友所设计和制作。如需了解更多该教程的信息,请参考以下网页:
    http://www.taichi-maker.com/homepage/esp8266-nodemcu-iot/
    ***********************************************************************/
    #include <ArduinoJson.h>
     
    void setup() {
      Serial.begin(9600);
      Serial.println("");
     
      // 重点1:DynamicJsonDocument对象
      const size_t capacity = JSON_OBJECT_SIZE(2) + 30;
      DynamicJsonDocument doc(capacity);
     
      // 重点2:即将解析的json文件
      String json = "{\"name\":\"taichi-maker\",\"number\":1}";
      
      // 重点3:反序列化数据
      deserializeJson(doc, json);
     
      // 重点4:获取解析后的数据信息
      String nameStr = doc["name"].as<String>();
      int numberInt = doc["number"].as<int>();
     
      // 通过串口监视器输出解析后的数据信息
      Serial.print("nameStr = ");Serial.println(nameStr);
      Serial.print("numberInt = ");Serial.println(numberInt);
    }
     
    void loop() {}

    语句讲解

    重点1:
    const size_t capacity = JSON_OBJECT_SIZE(2) + 30;
    DynamicJsonDocument doc(capacity);

    这里我们建立了DynamicJsonDocument对象,该对象名称为doc。在建立该对象时需要提供一个参数,也就是括号中的参数capacity。这个capacity参数的作用是告诉ESP8266我们所建立的DynamicJsonDocument对象将要占用多大的内存空间。这个空间大小是由语句const size_t capacity = JSON_OBJECT_SIZE(2) + 30;计算出来的。在这里我们回顾一下需要解析的JSON信息内容如下所示:

    {
      "name": "taichi-maker",
      "number": 1
    }

    我们可以看到,以上JSON信息中包含一个对象,该对象含有两个数据。因此在计算DynamicJsonDocument对象占用空间大小时,使用了JSON_OBJECT_SIZE(2)这条指令。其中指令括号中的2即代表对象包含有两个数据。

    我们再看一个例子,假设我们即将解析的JSON如下:

    {
      "name": "taichi-maker",
      "url": "www.taichi-maker.com",
      "number": 1
    }

    以上JSON对象中包含有3个数据。在计算解析它所需要占用的内存大小时,我们将要使用语句:const size_t capacity = JSON_OBJECT_SIZE(3) + 60;

    讲到这里可能细心的朋友已经发现了,在以上语句中除了JSON_OBJECT_SIZE指令以外还使用+ 60来额外增加数值。这些额外增加的数值是由于ArduinoJson库在解析信息时,需要额外的空间来复制JSON信息。但是具体这个额外增加的数值是多少呢,请您先把这个问题留在心里,后面我们会给您做讲解。

    重点2:
    String json = "{\"name\":\"taichi-maker\",\"number\":1}";
    这条语句的作用是建立字符串变量,改变里用于存储需要解析的JSON信息。

    重点3:
    deserializeJson(doc, json);
    这部分语句的作用是使用deserializeJson来对JSON文件进行解析。其中第一个参数是我们重点1讲解的DynamicJsonDocument对象,第二个参数是重点2讲解的json字符串。

    重点4:
    String nameStr = doc["name"].as();
    int numberInt = doc["number"].as();

    这两条语句用于获取解析后的JSON信息,其中doc["name"].as将会返回“name”的值。这条语句中.as将会让“name”的值以字符串的形式返回。

    另一条语句中doc["number"].as()自然就是以整数形式来返回”number”的数据值。

     JSON解析示例-2:JSON数组解析

    以下示例演示了如何使用ArduinoJson库解析一个JSON数组信息。该信息如下:

    [
      {
        "name": "taichi-maker"
      },
      {
        "website": "www.taichi-maker.com"
      }
    ]

    以下是示例程序内容 

    /**********************************************************************
    项目名称/Project          : 零基础入门学用物联网
    程序名称/Program name     : arduinojosn_2_array
    团队/Team                : 太极创客团队 / Taichi-Maker (www.taichi-maker.com)
    作者/Author              : CYNO朔
    日期/Date(YYYYMMDD)     : 20200424
    程序目的/Purpose          : 
    此程序用于演示如何使用arduinojson库解析以下json信息,该json包含一个数组,
    数组有两个元素,每个元素都是一个对象,每一个对象都有一个数据。
    [
      {
        "name": "taichi-maker"
      },
      {
        "website": "www.taichi-maker.com"
      }
    ]
    -----------------------------------------------------------------------
    本示例程序为太极创客团队制作的《零基础入门学用物联网》中示例程序。
    该教程为对物联网开发感兴趣的朋友所设计和制作。如需了解更多该教程的信息,请参考以下网页:
    http://www.taichi-maker.com/homepage/esp8266-nodemcu-iot/
    ***********************************************************************/
    #include <ArduinoJson.h>
     
    void setup() {
      Serial.begin(9600);
     
      // 重点1:DynamicJsonDocument对象
      const size_t capacity = JSON_ARRAY_SIZE(2) + 2*JSON_OBJECT_SIZE(1) + 60;
      DynamicJsonDocument doc(capacity);
      
      // 重点2:即将解析的json文件
      String json = "[{\"name\":\"taichi-maker\"},{\"website\":\"www.taichi-maker.com\"}]";
     
      // 重点3:反序列化数据
      deserializeJson(doc, json);
     
     
      String nameStr = doc[0]["name"].as<String>();
      String websiteStr = doc[1]["website"].as<String>();
     
      // 通过串口监视器输出解析后的数据信息
      Serial.print("nameStr = ");Serial.println(nameStr);
      Serial.print("websiteStr = ");Serial.println(websiteStr);
    }
     
    void loop() {}

    语句讲解

    重点1:
    DynamicJsonDocument doc(capacity);
    与以上示例相同,这里我们建立了DynamicJsonDocument对象,该对象名称为doc。doc对象的capacity参数用于设置解析JSON所需要的内存大小。这个空间大小是由语句const size_t capacity = JSON_ARRAY_SIZE(2) + 2*JSON_OBJECT_SIZE(1) + 60;计算出来的。在这里我们同样回顾一下需要解析的JSON信息内容:

    [
      {
        "name": "taichi-maker"
      },
      {
        "website": "www.taichi-maker.com"
      }
    ]

    我们可以看到,以上JSON信息是一个数组,该数组含有两个元素。因此,我们在计算capacity时首先使用了语句JSON_ARRAY_SIZE(2)来获得含有两个元素的数组所占用内存的大小。

    另外,这两个数组元素都是含有一个数据的对象。因此,我们在计算capacity时使用了语句2*JSON_OBJECT_SIZE(1)。其中JSON_OBJECT_SIZE(1)可以获得含有一个数据的对象大小,我们将它乘以2是因为这里有两个含有一个数据的对象。

    在计算capacity的时候,我们还在计算的最后增加60。这么做是由于ArduinoJson库在解析信息时,需要额外的空间来复制JSON信息。

    计算capacity可以使用ArduinoJson官网的在线工具。您可以点击这里打开该工具页面。如您需要了解该工具的具体使用方法,欢迎您收看太极创客团队制作的《零基础入门学用物联网》教程,您可以点击这里打开具体介绍该工具使用方法的教程页面。

    重点2
    String json = "[{\"name\":\"taichi-maker\"},{\"website\":\"www.taichi-maker.com\"}]";
    这条语句的作用是建立字符串变量,改变里用于存储需要解析的JSON信息。

    重点3:
    deserializeJson(doc, json);
    这部分语句的作用是使用deserializeJson来对JSON文件进行解析。其中第一个参数是我们重点1讲解的DynamicJsonDocument对象,第二个参数是重点2讲解的json字符串。

     ESP8266通过JSON实现物联网数据通讯

    ESP8266客户端请求JSON信息

     ESP8266客户端请求JSON信息

    示例一 ESP8266客户端请求单一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     : cgj_server_1
    团队/Team                : 太极创客团队 / Taichi-Maker (www.taichi-maker.com)
    作者/Author              : CYNO朔
    日期/Date(YYYYMMDD)     : 2020305
    程序目的/Purpose          : 
    本实例用于演示esp8266的json数据通讯。
    操作测试本程序需要使用两台8266开发板。其中一台为服务器端,一台为客户端。
    本程序为服务器程序,功能如下:
     
    1. 实时读取A0、 D1、D2以及D3引脚的读数。
    2. 当有客户端请求信息时,将会通过http响应将引脚读数等信息发送给客户端。
       信息发送格式为json格式。
    3. 本程序使用了wifi.config对开发板的IP进行了配置。
    -----------------------------------------------------------------------
    修订历史/Revision History  
    日期/Date    作者/Author      参考号/Ref    修订说明/Revision Description
    20200511      CYNO朔           001        1 移除handleNotFound使教程代码更加精简
                                              2 改请求路径为update
    ***********************************************************************/
    #include <ESP8266WiFi.h>        // 本程序使用 ESP8266WiFi库
    #include <ESP8266WiFiMulti.h>   //  ESP8266WiFiMulti库
    #include <ESP8266WebServer.h>   //  ESP8266WebServer库
     
    #define buttonPin D3            // 按钮引脚D3
     
    ESP8266WiFiMulti wifiMulti;     // 建立ESP8266WiFiMulti对象,对象名称是'wifiMulti'
     
    ESP8266WebServer esp8266_server(80);// 建立网络服务器对象,该对象用于响应HTTP请求。监听端口(80)
     
    IPAddress local_IP(192, 168, 0, 123); // 设置ESP8266-NodeMCU联网后的IP
    IPAddress gateway(192, 168, 0, 1);    // 设置网关IP(通常网关IP是WiFI路由IP)
    IPAddress subnet(255, 255, 255, 0);   // 设置子网掩码
    IPAddress dns(192,168,0,1);           // 设置局域网DNS的IP(通常局域网DNS的IP是WiFI路由IP)
             
    void setup(){
      Serial.begin(9600);          // 启动串口通讯
      Serial.println("");
     
      // 将引脚设置为输入上拉模式
      pinMode(D1, INPUT_PULLUP);
      pinMode(D2, INPUT_PULLUP);
      pinMode(buttonPin, INPUT_PULLUP);   // NodeMCU开发板按键连接在D3引脚上
       
      // 设置开发板网络环境
      if (!WiFi.config(local_IP, gateway, subnet)) {
        Serial.println("Failed to Config ESP8266 IP"); 
      } 
     
      //通过addAp函数存储  WiFi名称       WiFi密码
      wifiMulti.addAP("taichi-maker1", "12345678"); // 这三条语句通过调用函数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地址
                     
      esp8266_server.on("/", handleRoot);          
      esp8266_server.begin();  
      
      Serial.println("HTTP esp8266_server started");//  告知用户ESP8266网络服务功能已经启动
    }
     
    void loop(){
      // 处理http服务器访问
      esp8266_server.handleClient(); 
    }                                                                   
     
    void handleRoot() {   //处理网站目录“/”的访问请求 
      esp8266_server.send(200, "application/json", rootJson());  
    }
     
    // 实时获取ESP8266开发板引脚信息并且建立JSON信息
    // 以便ESP8266服务器通过响应信息发送给客户端
    String rootJson(){
     
      String jsonCode = "{\"info\": {\"name\": \"taichimaker\",\"url\": \"www.taichi-maker.com\",\"email\": \"taichimaker@163.com\"},\"digital_pin\": {\"d1\": \"";
      jsonCode += String(digitalRead(D1));  
      jsonCode += "\",\"d2\": \""; 
      jsonCode += String(digitalRead(D2));  
      jsonCode += "\",\"d3\": \""; 
      jsonCode += String(digitalRead(D3));  
      jsonCode += "\"},\"analog_pin\": {\"a0\": \"";
      jsonCode += String(analogRead(A0));
      jsonCode += "\"}}";  
      
      Serial.print("jsonCode: ");Serial.println(jsonCode);
      
      return jsonCode;
    }
    客户端程序

    客户端程序的主要功能:

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

    /**********************************************************************
    项目名称/Project          : 零基础入门学用物联网
    程序名称/Program name     : cgj_client_1
    团队/Team                : 太极创客团队 / Taichi-Maker (www.taichi-maker.com)
    作者/Author              : CYNO朔
    日期/Date(YYYYMMDD)     : 20200228
    程序目的/Purpose          : 
    本实例用于演示esp8266的json数据通讯。
    操作测试本程序需要使用两台8266开发板。其中一台为服务器端,一台为客户端。
    本程序为客户端程序,功能如下:
     
    1. 通过http协议向服务器端请求json数据信息
    2. 解析服务器端响应的json信息内容。
    3. 将解析后的数据信息显示于串口监视器
    4. 利用服务器端D3引脚(按键引脚)读数来控制客户端开发板上LED的点亮和熄灭
    -----------------------------------------------------------------------
    修订历史/Revision History  
    日期/Date    作者/Author      参考号/Ref    修订说明/Revision Description
    20200302      CYNO朔           001        添加arduinojson解析错误识别
    20200511      CYNO朔           002        改请求路径为update
    20200511      CYNO朔           003        parse过程使用函数完成
    ***********************************************************************/
    #include <ArduinoJson.h>
    #include <ESP8266WiFi.h>
    #include <ESP8266WiFiMulti.h>
     
    ESP8266WiFiMulti wifiMulti;           // 建立ESP8266WiFiMulti对象
     
    const char* host = "192.168.0.123";   // 将要连接的服务器地址  
    const int httpPort = 80;              // 将要连接的服务器端口      
     
    void setup(){
      Serial.begin(9600);          
      Serial.println("");
      
      // 设置开发板LED引脚
      pinMode(LED_BUILTIN, OUTPUT);
      digitalWrite(LED_BUILTIN, HIGH);
     
      wifiMulti.addAP("ssid_from_AP_1", "your_password_for_AP_1"); // 将需要连接的一系列WiFi ID和密码输入这里
      wifiMulti.addAP("ssid_from_AP_2", "your_password_for_AP_2"); // ESP8266-NodeMCU再启动后会扫描当前网络
      wifiMulti.addAP("ssid_from_AP_3", "your_password_for_AP_3"); // 环境查找是否有这里列出的WiFi ID。如果有
      Serial.println("Connecting ..."); 
     
      int i = 0;  
      while (wifiMulti.run() != WL_CONNECTED) { // 尝试进行wifi连接。
        delay(1000);
        Serial.print(i++); Serial.print(' ');
      }
      
      // WiFi连接成功后将通过串口监视器输出连接成功信息 
      Serial.println("");
      Serial.print("Connected to ");
      Serial.println(WiFi.SSID());              // WiFi名称
      Serial.print("IP address:\t");
      Serial.println(WiFi.localIP());           // IP
    }
     
    void loop(){
      httpRequest();
      
      delay(3000);
    }
     
    // 向服务器请求信息并对信息进行解析
    void httpRequest(){
      WiFiClient client;
     
      String httpRequest = String("GET /") + " HTTP/1.1\r\n" + 
                                  "Host: " + host + "\r\n" + 
                                  "Connection: close\r\n\r\n";
       
      Serial.print("Connecting to "); Serial.print(host);
      
      if (client.connect(host, 80)){
        Serial.println(" Success!");
     
        // 向服务器发送http请求信息
        client.print(httpRequest);
        Serial.println("Sending request: ");
        Serial.println(httpRequest);  
     
        // 获取并显示服务器响应状态行 
        String status_response = client.readStringUntil('\n');
        Serial.print("status_response: ");
        Serial.println(status_response);
     
        // 使用find跳过HTTP响应头
        if (client.find("\r\n\r\n")) {
          Serial.println("Found Header End. Start Parsing.");
        }
     
        parseInfo(client); 
      }
      else {
        Serial.println(" connection failed!");
      }   
      //断开客户端与服务器连接工作
      client.stop(); 
    }
     
    void parseInfo(WiFiClient client){
      const size_t capacity = JSON_OBJECT_SIZE(1) + 3*JSON_OBJECT_SIZE(3) + 140;
      DynamicJsonDocument doc(capacity);
       
      deserializeJson(doc, client);
      
      JsonObject info = doc["info"];
      const char* info_name = info["name"]; // "taichimaker"
      const char* info_url = info["url"]; // "www.taichi-maker.com"
      const char* info_email = info["email"]; // "taichimaker@163.com"
      
      JsonObject digital_pin = doc["digital_pin"];
      const char* digital_pin_d1 = digital_pin["d1"]; // "1"
      const char* digital_pin_d2 = digital_pin["d2"]; // "0"
      const char* digital_pin_d3 = digital_pin["d3"]; // "1"
      
      const char* analog_pin_a0 = doc["analog_pin"]["a0"]; // "500"
     
      String info_name_str = info["name"].as<String>();
      bool d3_bool = digital_pin["d3"].as<int>();
     
      Serial.print("info_name_str = ");Serial.println(info_name_str);
      Serial.print("d3_bool = ");Serial.println(d3_bool);
     
      d3_bool == 0 ? digitalWrite (LED_BUILTIN, LOW) : digitalWrite(LED_BUILTIN, HIGH);
    }

    以上程序中最重点的部分是函数httpRequest。该函数向服务器发送HTTP请求,并且对服务器相应的JSON信息进行了解析。解析后的数据信息将通过串口监视器显示,其中服务器按键引脚的状态信息还被用于控制客户端板上的LED点亮和熄灭。

    ESP8266客户端发送JSON信息

    示例一 ESP8266客户端请求单一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     : cgj_server_1
    团队/Team                : 太极创客团队 / Taichi-Maker (www.taichi-maker.com)
    作者/Author              : CYNO朔
    日期/Date(YYYYMMDD)     : 2020305
    程序目的/Purpose          : 
    本实例用于演示esp8266的json数据通讯。
    操作测试本程序需要使用两台8266开发板。其中一台为服务器端,一台为客户端。
    本程序为服务器程序,功能如下:
     
    1. 实时读取A0、 D1、D2以及D3引脚的读数。
    2. 当有客户端请求信息时,将会通过http响应将引脚读数等信息发送给客户端。
       信息发送格式为json格式。
    3. 本程序使用了wifi.config对开发板的IP进行了配置。
    -----------------------------------------------------------------------
    修订历史/Revision History  
    日期/Date    作者/Author      参考号/Ref    修订说明/Revision Description
    20200511      CYNO朔           001        1 移除handleNotFound使教程代码更加精简
                                              2 改请求路径为update
    ***********************************************************************/
    #include <ESP8266WiFi.h>        // 本程序使用 ESP8266WiFi库
    #include <ESP8266WiFiMulti.h>   //  ESP8266WiFiMulti库
    #include <ESP8266WebServer.h>   //  ESP8266WebServer库
     
    #define buttonPin D3            // 按钮引脚D3
     
    ESP8266WiFiMulti wifiMulti;     // 建立ESP8266WiFiMulti对象,对象名称是'wifiMulti'
     
    ESP8266WebServer esp8266_server(80);// 建立网络服务器对象,该对象用于响应HTTP请求。监听端口(80)
     
    IPAddress local_IP(192, 168, 0, 123); // 设置ESP8266-NodeMCU联网后的IP
    IPAddress gateway(192, 168, 0, 1);    // 设置网关IP(通常网关IP是WiFI路由IP)
    IPAddress subnet(255, 255, 255, 0);   // 设置子网掩码
    IPAddress dns(192,168,0,1);           // 设置局域网DNS的IP(通常局域网DNS的IP是WiFI路由IP)
             
    void setup(){
      Serial.begin(9600);          // 启动串口通讯
      Serial.println("");
     
      // 将引脚设置为输入上拉模式
      pinMode(D1, INPUT_PULLUP);
      pinMode(D2, INPUT_PULLUP);
      pinMode(buttonPin, INPUT_PULLUP);   // NodeMCU开发板按键连接在D3引脚上
       
      // 设置开发板网络环境
      if (!WiFi.config(local_IP, gateway, subnet)) {
        Serial.println("Failed to Config ESP8266 IP"); 
      } 
     
      //通过addAp函数存储  WiFi名称       WiFi密码
      wifiMulti.addAP("taichi-maker1", "12345678"); // 这三条语句通过调用函数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地址
                     
      esp8266_server.on("/", handleRoot);          
      esp8266_server.begin();  
      
      Serial.println("HTTP esp8266_server started");//  告知用户ESP8266网络服务功能已经启动
    }
     
    void loop(){
      // 处理http服务器访问
      esp8266_server.handleClient(); 
    }                                                                   
     
    void handleRoot() {   //处理网站目录“/”的访问请求 
      esp8266_server.send(200, "application/json", rootJson());  
    }
     
    // 实时获取ESP8266开发板引脚信息并且建立JSON信息
    // 以便ESP8266服务器通过响应信息发送给客户端
    String rootJson(){
     
      String jsonCode = "{\"info\": {\"name\": \"taichimaker\",\"url\": \"www.taichi-maker.com\",\"email\": \"taichimaker@163.com\"},\"digital_pin\": {\"d1\": \"";
      jsonCode += String(digitalRead(D1));  
      jsonCode += "\",\"d2\": \""; 
      jsonCode += String(digitalRead(D2));  
      jsonCode += "\",\"d3\": \""; 
      jsonCode += String(digitalRead(D3));  
      jsonCode += "\"},\"analog_pin\": {\"a0\": \"";
      jsonCode += String(analogRead(A0));
      jsonCode += "\"}}";  
      
      Serial.print("jsonCode: ");Serial.println(jsonCode);
      
      return jsonCode;
    }
    客户端程序

    客户端程序的主要功能:

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

    /**********************************************************************
    项目名称/Project          : 零基础入门学用物联网
    程序名称/Program name     : cgj_client_1
    团队/Team                : 太极创客团队 / Taichi-Maker (www.taichi-maker.com)
    作者/Author              : CYNO朔
    日期/Date(YYYYMMDD)     : 20200228
    程序目的/Purpose          : 
    本实例用于演示esp8266的json数据通讯。
    操作测试本程序需要使用两台8266开发板。其中一台为服务器端,一台为客户端。
    本程序为客户端程序,功能如下:
     
    1. 通过http协议向服务器端请求json数据信息
    2. 解析服务器端响应的json信息内容。
    3. 将解析后的数据信息显示于串口监视器
    4. 利用服务器端D3引脚(按键引脚)读数来控制客户端开发板上LED的点亮和熄灭
    -----------------------------------------------------------------------
    修订历史/Revision History  
    日期/Date    作者/Author      参考号/Ref    修订说明/Revision Description
    20200302      CYNO朔           001        添加arduinojson解析错误识别
    20200511      CYNO朔           002        改请求路径为update
    20200511      CYNO朔           003        parse过程使用函数完成
    ***********************************************************************/
    #include <ArduinoJson.h>
    #include <ESP8266WiFi.h>
    #include <ESP8266WiFiMulti.h>
     
    ESP8266WiFiMulti wifiMulti;           // 建立ESP8266WiFiMulti对象
     
    const char* host = "192.168.0.123";   // 将要连接的服务器地址  
    const int httpPort = 80;              // 将要连接的服务器端口      
     
    void setup(){
      Serial.begin(9600);          
      Serial.println("");
      
      // 设置开发板LED引脚
      pinMode(LED_BUILTIN, OUTPUT);
      digitalWrite(LED_BUILTIN, HIGH);
     
      wifiMulti.addAP("ssid_from_AP_1", "your_password_for_AP_1"); // 将需要连接的一系列WiFi ID和密码输入这里
      wifiMulti.addAP("ssid_from_AP_2", "your_password_for_AP_2"); // ESP8266-NodeMCU再启动后会扫描当前网络
      wifiMulti.addAP("ssid_from_AP_3", "your_password_for_AP_3"); // 环境查找是否有这里列出的WiFi ID。如果有
      Serial.println("Connecting ..."); 
     
      int i = 0;  
      while (wifiMulti.run() != WL_CONNECTED) { // 尝试进行wifi连接。
        delay(1000);
        Serial.print(i++); Serial.print(' ');
      }
      
      // WiFi连接成功后将通过串口监视器输出连接成功信息 
      Serial.println("");
      Serial.print("Connected to ");
      Serial.println(WiFi.SSID());              // WiFi名称
      Serial.print("IP address:\t");
      Serial.println(WiFi.localIP());           // IP
    }
     
    void loop(){
      httpRequest();
      
      delay(3000);
    }
     
    // 向服务器请求信息并对信息进行解析
    void httpRequest(){
      WiFiClient client;
     
      String httpRequest = String("GET /") + " HTTP/1.1\r\n" + 
                                  "Host: " + host + "\r\n" + 
                                  "Connection: close\r\n\r\n";
       
      Serial.print("Connecting to "); Serial.print(host);
      
      if (client.connect(host, 80)){
        Serial.println(" Success!");
     
        // 向服务器发送http请求信息
        client.print(httpRequest);
        Serial.println("Sending request: ");
        Serial.println(httpRequest);  
     
        // 获取并显示服务器响应状态行 
        String status_response = client.readStringUntil('\n');
        Serial.print("status_response: ");
        Serial.println(status_response);
     
        // 使用find跳过HTTP响应头
        if (client.find("\r\n\r\n")) {
          Serial.println("Found Header End. Start Parsing.");
        }
     
        parseInfo(client); 
      }
      else {
        Serial.println(" connection failed!");
      }   
      //断开客户端与服务器连接工作
      client.stop(); 
    }
     
    void parseInfo(WiFiClient client){
      const size_t capacity = JSON_OBJECT_SIZE(1) + 3*JSON_OBJECT_SIZE(3) + 140;
      DynamicJsonDocument doc(capacity);
       
      deserializeJson(doc, client);
      
      JsonObject info = doc["info"];
      const char* info_name = info["name"]; // "taichimaker"
      const char* info_url = info["url"]; // "www.taichi-maker.com"
      const char* info_email = info["email"]; // "taichimaker@163.com"
      
      JsonObject digital_pin = doc["digital_pin"];
      const char* digital_pin_d1 = digital_pin["d1"]; // "1"
      const char* digital_pin_d2 = digital_pin["d2"]; // "0"
      const char* digital_pin_d3 = digital_pin["d3"]; // "1"
      
      const char* analog_pin_a0 = doc["analog_pin"]["a0"]; // "500"
     
      String info_name_str = info["name"].as<String>();
      bool d3_bool = digital_pin["d3"].as<int>();
     
      Serial.print("info_name_str = ");Serial.println(info_name_str);
      Serial.print("d3_bool = ");Serial.println(d3_bool);
     
      d3_bool == 0 ? digitalWrite (LED_BUILTIN, LOW) : digitalWrite(LED_BUILTIN, HIGH);
    }

    作者:@@庆

    物联沃分享整理
    物联沃-IOTWORD物联网 » JSON( JSON基础,ESP8266 JSON解析)ESP8266通过JSON实现物联网数据通讯( ESP8266客户端请求JSON信息, ESP8266客户端发送JSON信息)

    发表回复