位置导航: 首页 / 物联网教程 / 第3章 – 用C/C++开发物联网 / 3-4 网络客户端 / 本页
由于易于解析且量级很轻,JSON成为了常用的物联网信息传输格式之一。在这一节里,我们将一起学习以下几个主要知识点:
1. 使用ESP8266来建立物联网服务器,该服务器可以向客户端发送JSON格式响应信息从而实现物联网信息通讯。
2. 使用ESP8266来通过网络向物联网服务器请求JSON信息
3. 使用ESP8266来通过ArduinoJson库解析JSON信息
在接下来的讲解中,我们将需要两块ESP8266-NodeMCU开发板。其中一块作为服务器,另一块作为客户端。如下图所示,客户端将会向服务器发送请求信息。服务器端在接收到客户端请求后,会将JSON信息加入服务器响应信息中发送给客户端。
注意,以下示例中的服务器端和客户端ESP8266必须连接同一WiFi网络,方可实现数据通讯。
示例一 ESP8266客户端请求单一JSON数据信息
本示例分为两部分,一部分为服务器程序,另一部分为客户端程序。
服务器端程序
服务器端程序主要功能:
1. 实时读取A0、 D1、D2以及D3引脚的读数。
2. 当有客户端请求时,通过响应信息将引脚读数和测试数据信息发送给客户端。
信息发送格式为json格式。以下为该json信息的示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
{ "info": { "name": "taichimaker", "url": "www.taichi-maker.com", "email": "taichimaker@163.com" }, "digital_pin": { "d1": "1", "d2": "0", "d3": "1" }, "analog_pin": { "a0": "500" } } |
以上JSON信息包含有三个数据,第一个数据”info”对应的值是一个包含有三个数据的对象。这三个数据值都是字符串格式。他们在整个程序运行中是保持不变的。第二个数据”digital_pin”所对应的值是一个含有三个数据的对象,这三个数据是ESP8266开发板的D1、D2、D3引脚的实时电平状态。其中D3引脚的状态正是NodeMCU开发板上按键的引脚状态。我们通过按下该按键,可以改变D3引脚电平状态。第三个数据”analog_pin”对应的值是一个含有一个数据的对象。该数据是ESP8266的模拟输入引脚实时读数。换句话说, “digital_pin”和”analog_pin”所对应的数据值都是ESP8266引脚的实时状态,这些信息是会改变的。
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 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 |
/********************************************************************** 项目名称/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; } |
以上程序的重点是函数rootJson。该函数作用是实时获取ESP8266开发板引脚信息并且建立JSON信息。该信息将会通过服务器响应信息发送给请求这一信息的客户端。此方法相对简单,但是缺点是不够灵活。假如我们需要修改响应的JSON信息,那么就要重新构建JSON字符串。另外,假如我们的响应JSON信息比较复杂,那么构建这个字符串的工作还是很麻烦的。接下来我们再以上程序的基础上做一下修改。我们将使用ArduinoJson官网的在线工具来自动生成代码,搭建我们想要的JSON信息并应用到程序的rootJson函数中。
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 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 |
/********************************************************************** 项目名称/Project : 零基础入门学用物联网 程序名称/Program name : cgj_server_1_serialize 团队/Team : 太极创客团队 / Taichi-Maker (www.taichi-maker.com) 作者/Author : CYNO朔 日期/Date(YYYYMMDD) : 2020517 程序目的/Purpose : 本实例用于演示esp8266的JSON数据通讯。 操作测试本程序需要使用两台8266开发板。其中一台为服务器端,一台为客户端。 本程序为服务器程序,功能如下: 1. 实时读取A0、 D1、D2以及D3引脚的读数。 2. 当有客户端请求信息时,将会通过http响应将引脚读数等信息发送给客户端。 信息发送格式为JSON格式。 3. 使用ArduinoJson库的Serialize方式建立响应JSON信息 ----------------------------------------------------------------------- 修订历史/Revision History 日期/Date 作者/Author 参考号/Ref 修订说明/Revision Description ***********************************************************************/ #include <ESP8266WiFi.h> // 本程序使用 ESP8266WiFi库 #include <ESP8266WiFiMulti.h> // ESP8266WiFiMulti库 #include <ESP8266WebServer.h> // ESP8266WebServer库 #include <ArduinoJson.h> // ArduinoJson库 #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.begin(); esp8266_server.on("/", handleRoot); 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(){ // 开始ArduinoJson Assistant的serialize代码 const size_t capacity = JSON_OBJECT_SIZE(1) + 3*JSON_OBJECT_SIZE(3)+140; DynamicJsonDocument doc(capacity); 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)); JsonObject analog_pin = doc.createNestedObject("analog_pin"); analog_pin["a0"] = String(analogRead(A0)); // 结束assistant的serialize代码 String jsonCode; serializeJson(doc, jsonCode); Serial.print("Root Json Code: ");Serial.println(jsonCode); return jsonCode; } |
那么接下来我们看一下ESP8266客户端的程序内容,
客户端程序
客户端程序的主要功能:
1. 向服务器端请求json数据信息
2. 解析服务器端响应的json信息内容。
3. 将解析后的数据信息显示于串口监视器
4. 利用服务器端D3引脚(按键引脚)读数来控制客户端开发板上LED的点亮和熄灭
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 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 |
/********************************************************************** 项目名称/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数据信息
在以上示例程序中,服务器响应的信息形式只有一种,也就是将所有JSON信息全部响应给客户端。然而在我们实际开发物联网项目过程中,可能客户端只需要服务器JSON信息中的的某一个或某几个信息。这种情况下,如果服务器总是把所有信息都发送给客户端,这一操作会产生网络资源和系统运算资源的浪费。
接下来的示例程序中,客户端可以有选择性地向服务器请求信息内容。服务器端也会在接收到客户端请求后,根据客户端的需求来选择性的发送服务器信息。当然了,这些信息同样使用了JSON格式来传输。
服务器端程序
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 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 |
/********************************************************************** 项目名称/Project : 零基础入门学用物联网 程序名称/Program name : cgj_server_2 团队/Team : 太极创客团队 / Taichi-Maker (www.taichi-maker.com) 作者/Author : CYNO朔 日期/Date(YYYYMMDD) : 2020311 程序目的/Purpose : 本实例用于演示esp8266的json数据通讯。 操作测试本程序需要使用两台8266开发板。其中一台为服务器端,一台为客户端。 本程序为服务器程序,功能如下: 1. 实时读取A0,D1,D2以及D3引脚的读数。 2. 当有客户端请求信息时,将会通过响应将引脚读数等信息发送给客户端。 3. 服务器在发送响应信息时,会根据客户端的需求而有选择性地发送响应信息。 4. 信息发送格式为json格式。建立json过程使用arduinojson serialize ----------------------------------------------------------------------- 修订历史/Revision History 日期/Date 作者/Author 参考号/Ref 修订说明/Revision Description 20200511 CYNO朔 001 移除handleNotFound以精简示例代码 ***********************************************************************/ #include <ESP8266WiFi.h> // 本程序使用 ESP8266WiFi库 #include <ESP8266WiFiMulti.h> // ESP8266WiFiMulti库 #include <ESP8266WebServer.h> // ESP8266WebServer库 #include <ArduinoJson.h> // ArduinoJson库 #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.begin(); esp8266_server.on("/", handleRoot); esp8266_server.on("/info", handleInfo); esp8266_server.on("/digital_pin", handleDigitalPin); Serial.println("HTTP esp8266_server started");// 告知用户ESP8266网络服务功能已经启动 } void loop(){ // 处理http服务器访问 esp8266_server.handleClient(); } void handleRoot() { //处理网站目录“/”的访问请求 esp8266_server.send(200, "application/json", rootJson()); } void handleInfo() { //处理网站目录“/info”的访问请求 esp8266_server.send(200, "application/json", infoJson()); } void handleDigitalPin() { //处理网站目录“/digital_pin”的访问请求 esp8266_server.send(200, "application/json", digitalPinJson()); } // 实时获取ESP8266开发板引脚信息并且建立JSON信息 // 以便ESP8266服务器通过响应信息发送给客户端 String rootJson(){ // 开始ArduinoJson Assistant的serialize代码 const size_t capacity = JSON_OBJECT_SIZE(1) + 3*JSON_OBJECT_SIZE(3); DynamicJsonDocument doc(capacity); 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)); JsonObject analog_pin = doc.createNestedObject("analog_pin"); analog_pin["a0"] = String(analogRead(A0)); // 结束assistant的serialize代码 String jsonCode; serializeJson(doc, jsonCode); Serial.print("Root Json Code: ");Serial.println(jsonCode); return jsonCode; } //建立infoJson信息 String infoJson(){ // 开始ArduinoJson Assistant的serialize代码 const size_t capacity = JSON_OBJECT_SIZE(1) + 3*JSON_OBJECT_SIZE(3)+140; DynamicJsonDocument doc(capacity); JsonObject info = doc.createNestedObject("info"); info["name"] = "taichimaker"; info["url"] = "www.taichi-maker.com"; info["email"] = "taichimaker@163.com"; // 结束assistant的serialize代码 String jsonCode; serializeJson(doc, jsonCode); Serial.print("info Json Code: ");Serial.println(jsonCode); return jsonCode; } //建立digitalPinJson信息 String digitalPinJson(){ // 开始ArduinoJson Assistant的serialize代码 const size_t capacity = JSON_OBJECT_SIZE(1) + 3*JSON_OBJECT_SIZE(3)+140; DynamicJsonDocument doc(capacity); 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)); String jsonCode; serializeJson(doc, jsonCode); Serial.print("root json Code: ");Serial.println(jsonCode); return jsonCode; } |
以上示例程序中,我们建立了一系列回调函数handleRoot、handleInfo、handleDigitalPin。这些回调函数会针对客户端的请求来发送不同的JSON响应信息。这一点与之前的服务器端示例程序有所区别。另外请留意,我们在建立JSON响应信息时,使用了ArduinoJson库的createNestedObject函数以及serializeJson函数来实现。
客户端程序
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 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 |
/********************************************************************** 项目名称/Project : 零基础入门学用物联网 程序名称/Program name : cgj_client_2 团队/Team : 太极创客团队 / Taichi-Maker (www.taichi-maker.com) 作者/Author : CYNO朔 日期/Date(YYYYMMDD) : 20200228 程序目的/Purpose : 本实例用于演示esp8266的json数据通讯。 操作测试本程序需要使用两台8266开发板。其中一台为服务器端,一台为客户端。 本程序为客户端程序,功能如下: 1. 通过http协议向服务器端请求json数据信息 2. 解析服务器端响应的json信息内容。 3. 将解析后的数据信息显示于串口监视器 4. 检查服务器响应JSON否存在某指定key ----------------------------------------------------------------------- 修订历史/Revision History 日期/Date 作者/Author 参考号/Ref 修订说明/Revision Description 20200302 CYNO朔 001 添加arduinojson解析错误识别 20200511 CYNO朔 002 调整变量名称,调整httpRequest函数 使其更加精简 20200519 CYNO朔 003 根据client_1程序调整,使示例具有连贯性 ***********************************************************************/ #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(2000); httpRequest("/info"); delay(2000); httpRequest("/digital_pin"); delay(2000); } // 向服务器请求信息并对信息进行解析 void httpRequest(String url){ WiFiClient client; String httpRequest = String("GET ") + url + " 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){ String info_name_str; bool d3_bool; const size_t capacity = JSON_OBJECT_SIZE(1) + 3*JSON_OBJECT_SIZE(3) + 140; DynamicJsonDocument doc(capacity); deserializeJson(doc, client); JsonObject info = doc["info"]; if(info){ Serial.println("Server Json has info: true"); 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" info_name_str = info["name"].as<String>(); 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"); 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" d3_bool = digital_pin["d3"].as<int>(); 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"); } else { Serial.println("Server Json has analog_pin_a0: false"); } d3_bool == 0 ? digitalWrite (LED_BUILTIN, LOW) : digitalWrite(LED_BUILTIN, HIGH); } |
我们通过使用不同参数来调用以上示例程序中的httpRequest函数,从而实现客户端向服务器发送不同JSON请求。在解析服务器JSON响应信息时,我们使用逻辑判断语句来检查服务器响应JSON信息中是否包含有所需的信息内容。如果包含所需信息,则进一步操作来获取信息内容。否则,ESP8266将会通过串口监视器告诉我用户,服务器的响应JSON中,没有指定的内容。
关于此程序的详细讲解,同样还是欢迎您收看我们制作的《零基础入门学用物联网》教程的相关部分。