文章目录
通过JSON实现物联网数据通讯
1 ESP8266客户端请求JSON信息
由于易于解析且量级很轻,JSON成为了常用的物联网信息传输格式之一。在这一节里,我们将一起学习以下几个主要知识点:
- 使用ESP8266来建立物联网服务器,该服务器可以向客户端发送JSON格式响应信息从而实现物联网信息通讯。
- 使用ESP8266来通过网络向物联网服务器请求JSON信息
- 使用ESP8266来通过ArduinoJson库解析JSON信息
在接下来的讲解中,我们将需要两块ESP8266-NodeMCU开发板。其中一块作为服务器,另一块作为客户端。如下图所示,客户端将会向服务器发送请求信息。服务器端在接收到客户端请求后,会将JSON信息加入服务器响应信息中发送给客户端。
注意,以下示例中的服务器端和客户端ESP8266必须连接同一WiFi网络,方可实现数据通讯。
1.1 ESP8266客户端请求单一JSON数据信息
本示例分为两部分,一部分为
服务器程序
,另一部分为客户端程序
。
1.1.1 服务器端程序
服务器端程序主要功能:
- 实时读取A0、 D1、D2以及D3引脚的读数。
- 当有客户端请求时,通过响应信息将引脚读数和测试数据信息发送给客户端。
信息发送格式为json格式。以下为该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"
}
}
以上JSON信息包含有三个数据
,第一个数据”info
”对应的值是一个包含有三个数据的对象。这三个数据值都是字符串格式。他们在整个程序运行中是保持不变的。第二个数据”digital_pin
”所对应的值是一个含有三个数据的对象,这三个数据是ESP8266开发板的D1、D2、D3引脚的实时电平状态。其中D3引脚的状态正是NodeMCU开发板上按键的引脚状态。我们通过按下该按键,可以改变D3引脚电平状态。第三个数据”analog_pin
”对应的值是一个含有一个数据的对象。该数据是ESP8266的模拟输入引脚实时读数。换句话说, “digital_pin”和”analog_pin”所对应的数据值都是ESP8266引脚的实时状态,这些信息是会改变的。
/**********************************************************************
项目名称/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函数中。
/**********************************************************************
项目名称/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;
}
1.1.2 客户端程序
客户端程序的主要功能:
- 向服务器端请求json数据信息
- 解析服务器端响应的json信息内容。
- 将解析后的数据信息显示于串口监视器
- 利用服务器端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点亮和熄灭。
2 ESP8266客户端发送JSON信息
客户端将会向服务器发送请求信息。在客户端的请求信息中将会包含JSON信息。服务器接收到请求信息后,会从请求信息中获取JSON信息,并且解析该JSON中的内容。
注意,以下示例中的服务器端和客户端ESP8266必须连接同一WiFi网络,方可实现数据通讯。
2.1 ESP8266客户端发送单一JSON数据信息
本示例程序分为两部分,一部分为服务器程序,另一部分为客户端程序。
2.1.1 客户端
客户端程序主要功能:
- 实时读取A0、 D1、D2以及D3引脚的读数。
- 向服务器发送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 <ESP8266WiFi.h>
#include <ESP8266WiFiMulti.h>
ESP8266WiFiMulti wifiMulti; // 建立ESP8266WiFiMulti对象
const char* host = "192.168.0.123"; // 网络服务器地址
const int httpPort = 80; // http端口80
void setup(){
Serial.begin(9600);
Serial.println("");
//通过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地址
}
void loop(){
// 发送HTTP请求
httpRequest();
delay(3000);
}
// 向服务器发送HTTP请求,请求信息中包含json信息
void httpRequest(){
// 建立WiFi客户端对象,对象名称client
WiFiClient client;
// 重点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 += "\"}}";
// 建立字符串,用于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);
}
重点部分讲解:
请留意以上程序重点1部分。这里我们实时读取开发板各个引脚状态并且将这些引脚读数放在payloadJson
字符串中,然后将其放入httpRequest字符串中发送给服务器。
2.1.2 服务器程序
以下示例程序将会利用
WiFiServer库
建立ESP8266网络服务器。我们可以通过网页浏览器客户端访问该服务器。客户端的请求信息将会通过ESP8266的串口输出以便我们观察程序 运行状况。
了解WiFiServer库的基本操作
/**********************************************************************
项目名称/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");
}
掌握了如何使用WiFiServer库来获取客户端请求信息后,接下来我们开始学习服务器端示例程序。该程序将会实现以下功能。
- 获取客户端请求信息中的json
- 解析json信息内容
- 将解析后的数据信息显示于串口监视器
- 利用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, 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)
// 建立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("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地址
// 启动服务器
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 analog_pin_a0_int; // 建立变量存储客户端开发板模拟输入引脚读数
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"];
const char* info_url = info["url"];
const char* info_email = info["email"];
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"];
const char* digital_pin_d2 = digital_pin["d2"];
const char* digital_pin_d3 = digital_pin["d3"];
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"];
if (analog_pin_a0){
Serial.println("Server Json has analog_pin_a0: true");
analog_pin_a0_int = doc["analog_pin"]["a0"].as<int>();
Serial.print("analog_pin_a0 = ");Serial.println(analog_pin_a0);
} else {
Serial.println("Server Json has analog_pin_a0: false");
}
// 利用d3引脚读数控制板上LED点亮和熄灭
d3_bool == 0 ? digitalWrite(LED_BUILTIN, LOW) : digitalWrite(LED_BUILTIN, HIGH);
}
程序重点部分讲解:
重点1:建立WiFiClient对象用于处理客户端请求信息
当有客户端连接到服务器后,我们可以使用此处建立的WiFiClient对象来处理客户端所发送的请求信息。
重点2:如果有客户端向服务器发送请求信息,则尝试跳过客户端请求头信息。此处我们使用了incomingClient.find(“\r\n\r\n”)来查找请求头结尾。
一旦查找到请求头结尾部分,后续的信息就是客户端请求体了。在请求体中的正是客户端发来的json信息。
重点3:解析请求体中的json信息
parseJson函数的作用是解析客户端发送的JSON信息。这一操作没有过多需要讲解的,因为我们在以前的示例中已经多次出现这一操作。需要特别提出的是在解析JSON信息时,我们使用了一系列if判断语句来判断JSON信息中是否包含有相应的键。比如如下语句:
Arduino
if (info){
Serial.println("Server Response JSON contains key info: true");
info_name_str = info["name"].as<String>();
Serial.print("info_name_str = ");Serial.println(info_name_str);
} else {
Serial.println("Server Response JSON contains key info: false");
}
这里使用了if(info)来判断客户端JSON信息中是否有info这一键。如果有,我们就会对它实施进一步操作。如果没有则通过串口监视器告知用户,客户端发送的JSON中没有info键。
2.2 ESP8266客户端发送多种JSON数据信息
以上这一操作利用了字符串来建立客户端请求。这么做很简单直接,但是缺点是不够灵活。试想我们建立的物联网项目中,客户端连接有大量传感器,并且每当任何一个传感器发生变化都要将所有传感器信息上传服务器。这么做会浪费大量网络资源和运算资源。接下来我们看如何操作让客户端只将一部分信息上传服务器。
/**********************************************************************
项目名称/Project : 零基础入门学用物联网
程序名称/Program name : client_send_multi_json
团队/Team : 太极创客团队 / Taichi-Maker (www.taichi-maker.com)
作者/Author : Dapenson
日期/Date(YYYYMMDD) : 20200425
程序目的/Purpose :
本实例用于演示esp8266的json数据通讯。
操作测试本程序需要使用两台8266开发板。其中一台为服务器端,一台为客户端
本程序为客户端程序,功能如下:
1. 实时读取A0、 D1、D2以及D3引脚的读数。
2. 向服务器发送多种Json信息。
3. 在有些发送的信息中包含有D3引脚状态从而控制服务器开发板上
的LED点亮或熄灭
-----------------------------------------------------------------------
修订历史/Revision History
日期/Date 作者/Author 参考号/Ref 修订说明/Revision Description
20200521 CYNO朔 001 调整以使程序与csj_client一致
-----------------------------------------------------------------------
本示例程序为太极创客团队制作的《零基础入门学用物联网》中示例程序。
该教程为对物联网开发感兴趣的朋友所设计和制作。如需了解更多该教程的信息,请参考以下网页:
http://www.taichi-maker.com/homepage/esp8266-nodemcu-iot/iot-c/esp8266-nodemcu-web-client/http-request/
***********************************************************************/
#include <ESP8266WiFi.h>
#include <ESP8266WiFiMulti.h>
#include <ArduinoJson.h>
ESP8266WiFiMulti wifiMulti; // 建立ESP8266WiFiMulti对象
const char* host = "192.168.0.123"; // 网络服务器地址
const int httpPort = 80; // http端口80
void setup(){
Serial.begin(9600);
Serial.println("");
//通过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地址
}
void loop(){
// 发送HTTP请求,使用参数控制函数发送不同类型JSON
httpRequest(1);
delay(2000);
httpRequest(2);
delay(2000);
httpRequest(3);
delay(2000);
}
// 向服务器发送HTTP请求,请求信息中包含json信息
void httpRequest(int jsonType){
// 建立WiFi客户端对象,对象名称client
WiFiClient client;
// 根据jsonType参数建立不同类型JSON
String payloadJson = buildJson(jsonType);
// 建立字符串,用于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);
}
//建立json信息
String buildJson(int type){
// 开始ArduinoJson Assistant的serialize代码
const size_t capacity = JSON_OBJECT_SIZE(1) + 3*JSON_OBJECT_SIZE(3)+140;
DynamicJsonDocument doc(capacity);
if (type == 1){
JsonObject info = doc.createNestedObject("info");
info["name"] = "taichimaker";
info["url"] = "www.taichi-maker.com";
info["email"] = "taichimaker@163.com";
}
if (type == 2){
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));
}
if (type == 3){
JsonObject analog_pin = doc.createNestedObject("analog_pin");
analog_pin["a0"] = String(analogRead(A0));
}
// 结束assistant的serialize代码
String jsonCode;
serializeJson(doc, jsonCode);
Serial.print("json Code: ");Serial.println(jsonCode);
return jsonCode;
}
以上示例中的buildJson函数中,我们针对3个不同情况构建了3种不同的JSON信息。这里我们使用了ArduinoJson库的createNestedObject函数以及serializeJson函数来实现。
示例二中的服务器程序与示例一服务器程序可以通用。所以这里就不再重复这一程序内容了。
内容来自 -----> 太极创客
详情可见太极创客官网,内有Arduino、ESP32等详细教程
http://www.taichi-maker.com