目录
1、JSON
2、JSON语法格式
基本概念:
语法规则:
数据类型:
示例:
3、JSON解析
单一对象JSON解析(无嵌套)
JSON数组解析
使用ArduinoJson官网在线工具解析JSON信息
ESP8266闪存存储的JSON解析
通过api获取天气预报json格式并解析打印输出
4、发送json格式
1、JSON
JSON是一种简洁、易于理解和使用的数据格式,主要用于存储和传输数据。它基于JavaScript语言的标准,但与任何编程语言无关,因此广泛用于不同的编程环境中。
2、JSON语法格式
基本概念:
-数据对象:由一系列“名/值对”组成,用于表示复杂的数据结构。
- 数组:由一系列值组成,这些值是有序的。
语法规则:
- 数据以“名/值”对的形式呈现,其中“名”和“值”由冒号分隔。
- 对象用大括号`{}`表示,对象中的“名/值”对由逗号分隔。
- 数组用中括号`[]`表示,数组中的值也由逗号分隔。
数据类型:
-名称:必须用双引号`""`括起来。
- 值:可以是数字、字符串、布尔值(true/false)、数组、对象或null。
示例:
- 数字:"value": 25
- 字符串:"name": "taichi-maker"
- 布尔值:"bool_value": true
-数组:
"info": [{"name": "taichi-maker", "website": "www.taichi-maker.com"}, {"year": 2020, "month": 12, "day": 30}]
- 对象:
{
"info":
{
"name": "taichi-maker",
"website": "www.taichi-maker.com"
}
}
- 注意:
- 对象和数组:
对象可以包含多个数据,包括其他对象和数组,但不能直接存放对象或数组。
数组可以包含多个对象或值,但不能直接存放“名/值”对。
- 混合使用:
- 对象和数组可以相互嵌套,一个对象的值可以是数组,反之亦然
- 对象是用来存储数据的,而数组是用来存储有序集合的。
3、JSON解析
解析JSON格式信息是一个较为繁琐的工作,因此我们将借助解析Arduino – ESP8266平台中解析JSON格式信息的第三方库——ArduionJson库,该库是目前最受好评的解析JSON信息第三方库。
解析json格式的过程就是反序列号过程
注意版本号,不同版本可能会有区别,当前使用版本为7.0.4。该版本提供了两个工具,一个在线解析json格式代码工具,一个故障排除工具。
ArduinoJson Assistant 6
ArduinoJson Troubleshooter
单一对象JSON解析(无嵌套)
以下方json信息解析示例
{
"gatew": "admin5555",
"time": 153034324,
"tag1": 15.32,
"tag2": 0,
"tag3": 43,
"tag4": "33"
}
代码实现
#include <ArduinoJson.h>
void setup() {
Serial.begin(9600);
}
void loop() {
Serial.println("");
//1、JsonDocument doc 对象
JsonDocument doc;
// 2、即将解析的json文件
String json="{\"gatew\": \"admin5555\",\"time\": 153034324,\"tag1\": 15.32,\"tag2\": 0,\"tag3\": 43,\"tag4\": \"33\"}";
//3、反序列化数据
DeserializationError error = deserializeJson(doc, json);
if (error) {
Serial.print("deserializeJson() failed: ");
Serial.println(error.c_str());
return;
}
// 4:获取解析后的数据信息
const char* gatew = doc["gatew"]; // "admin5555"
long time = doc["time"]; // 153034324
float tag1 = doc["tag1"]; // 15.32
int tag2 = doc["tag2"]; // 0
int tag3 = doc["tag3"]; // 43
const char* tag4 = doc["tag4"]; // "33"
// 通过串口监视器输出解析后的数据信息
Serial.print("gatew = ");Serial.println(gatew);
Serial.print("tag1 = ");Serial.println(tag1);
Serial.print("tag2 = ");Serial.println(tag2);
Serial.print("tag3 = ");Serial.println(tag3);
Serial.print("tag4 = ");Serial.println(tag4);
Serial.print("time = ");Serial.println(time);
delay(1000);
}
测试ok,间隔1s输出解析后的数据信息。
JSON数组解析
[
{
"name": "taichi-maker"
},
{
"website": "www.taichi-maker.com"
}
]
示例程序内容
#include <ArduinoJson.h>
void setup() {
Serial.begin(9600);
}
void loop() {
Serial.println("");
//1、JsonDocument doc 对象
JsonDocument doc;
// 2、即将解析的json文件
String json="[{\"name\":\"taichi-maker\"},{\"website\": \"www.taichi-maker.com\"}]";
//3、反序列化数据
DeserializationError error = deserializeJson(doc, json);
if (error) {
Serial.print("deserializeJson() failed: ");
Serial.println(error.c_str());
return;
}
// 4:获取解析后的数据信息
const char* name = doc[0]["name"]; // "taichi-maker"
const char* website = doc[1]["website"]; // www.taichi-maker.com
// 通过串口监视器输出解析后的数据信息
Serial.print("name = ");Serial.println(name);
Serial.print("website = ");Serial.println(website);
delay(1000);
}
使用ArduinoJson官网在线工具解析JSON信息
#include <ArduinoJson.h>
JsonDocument doc;
DeserializationError error = deserializeJson(doc, input);
if (error) {
Serial.print("deserializeJson() failed: ");
Serial.println(error.c_str());
return;
}
const char* gatew = doc["gatew"]; // "admin5555"
long time = doc["time"]; // 153034324
float tag1 = doc["tag1"]; // 15.32
int tag2 = doc["tag2"]; // 0
int tag3 = doc["tag3"]; // 43
const char* tag4 = doc["tag4"]; // "33"
ESP8266闪存存储的JSON解析
在开发物联网项目时,大型JSON信息存储再程序中,将会占用大量系统动态内存,严重的甚至会出现系统控制程序空间不足问题。因此,我们需要将大型JSON文件存储在ESP8266的闪存系统中。暂未研究。
通过api获取天气预报json格式并解析打印输出
api格式
天气实况 | 心知天气文档
{
"results": [
{
"location": {
"id": "C23NB62W20TF",
"name": "西雅图",
"country": "US",
"path": "西雅图,华盛顿州,美国",
"timezone": "America/Los_Angeles",
"timezone_offset": "-07:00"
},
"now": {
"text": "多云", //天气现象文字
"code": "4", //天气现象代码
"temperature": "14", //温度,单位为c摄氏度或f华氏度
"feels_like": "14", //体感温度,单位为c摄氏度或f华氏度
"pressure": "1018", //气压,单位为mb百帕或in英寸
"humidity": "76", //相对湿度,0~100,单位为百分比
"visibility": "16.09", //能见度,单位为km公里或mi英里
"wind_direction": "西北", //风向文字
"wind_direction_degree": "340", //风向角度,范围0~360,0为正北,90为正东,180为正南,270为正西
"wind_speed": "8.05", //风速,单位为km/h公里每小时或mph英里每小时
"wind_scale": "2", //风力等级,请参考:http://baike.baidu.com/view/465076.htm
"clouds": "90", //云量,单位%,范围0~100,天空被云覆盖的百分比 #目前不支持中国城市#
"dew_point": "-12" //露点温度,请参考:http://baike.baidu.com/view/118348.htm #目前不支持中国城市#
},
"last_update": "2015-09-25T22:45:00-07:00" //数据更新时间(该城市的本地时间)
}
]
}
#include <ArduinoJson.h>
#include <ESP8266WiFi.h>
const char *ssid = "Xiaomi Civi 3"; // WiFi账号
const char *password = "12345678c"; // WiFi密码
const char* host = "api.seniverse.com"; // 将要连接的服务器地址
const int httpPort = 80; // 将要连接的服务器端口
// 心知天气HTTP请求所需信息
String reqUserKey = "Smlv4QbicqLpVYlXH"; // 私钥
String reqLocation = "Wuhan"; // 城市
String reqUnit = "c"; // 摄氏/华氏
void setup(){
Serial.begin(9600);
Serial.println("");
// 连接WiFi
connectWiFi();
}
void loop(){
// 建立心知天气API当前天气请求资源地址
String reqRes = "/v3/weather/now.json?key=" + reqUserKey +
+ "&location=" + reqLocation +
"&language=en&unit=" +reqUnit;
// 向心知天气服务器服务器请求信息并对信息进行解析
httpRequest(reqRes);
delay(3000);
}
// 向心知天气服务器服务器请求信息并对信息进行解析
void httpRequest(String reqRes){
WiFiClient client;
// 建立http请求信息
String httpRequest = String("GET ") + reqRes + " HTTP/1.1\r\n" +
"Host: " + host + "\r\n" +
"Connection: close\r\n\r\n";
Serial.println("");
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.");
}
// 利用ArduinoJson库解析心知天气响应信息
parseInfo(client);
} else {
Serial.println(" connection failed!");
}
//断开客户端与服务器连接工作
client.stop();
}
// 连接WiFi
void connectWiFi(){
WiFi.begin(ssid, password); // 启动网络连接
Serial.print("Connecting to "); // 串口监视器输出网络连接信息
Serial.print(ssid); Serial.println(" ..."); // 告知用户NodeMCU正在尝试WiFi连接
int i = 0; // 这一段程序语句用于检查WiFi是否连接成功
while (WiFi.status() != WL_CONNECTED) { // WiFi.status()函数的返回值是由NodeMCU的WiFi连接状态所决定的。
delay(1000); // 如果WiFi连接成功则返回值为WL_CONNECTED
Serial.print(i++); Serial.print(' '); // 此处通过While循环让NodeMCU每隔一秒钟检查一次WiFi.status()函数返回值
} // 同时NodeMCU将通过串口监视器输出连接时长读秒。
// 这个读秒是通过变量i每隔一秒自加1来实现的。
Serial.println(""); // WiFi连接成功后
Serial.println("Connection established!"); // NodeMCU将通过串口监视器输出"连接成功"信息。
Serial.print("IP address: "); // 同时还将输出NodeMCU的IP地址。这一功能是通过调用
Serial.println(WiFi.localIP()); // WiFi.localIP()函数来实现的。该函数的返回值即NodeMCU的IP地址。
}
// 利用ArduinoJson库解析心知天气响应信息
void parseInfo(WiFiClient client){
// String input;
StaticJsonDocument<768> doc;
DeserializationError error = deserializeJson(doc, client);
if (error) {
Serial.print(F("deserializeJson() failed: "));
Serial.println(error.f_str());
return;
}
JsonObject results_0 = doc["results"][0];
JsonObject results_0_location = results_0["location"];
const char* results_0_location_id = results_0_location["id"]; // "C23NB62W20TF"
const char* results_0_location_name = results_0_location["name"]; // "西雅图"
const char* results_0_location_country = results_0_location["country"]; // "US"
const char* results_0_location_path = results_0_location["path"]; // "西雅图,华盛顿州,美国"
const char* results_0_location_timezone = results_0_location["timezone"]; // "America/Los_Angeles"
const char* results_0_location_timezone_offset = results_0_location["timezone_offset"]; // "-07:00"
JsonObject results_0_now = results_0["now"];
const char* results_0_now_text = results_0_now["text"]; // "多云"
const char* results_0_now_code = results_0_now["code"]; // "4"
const char* results_0_now_temperature = results_0_now["temperature"]; // "14"
const char* results_0_now_feels_like = results_0_now["feels_like"]; // "14"
const char* results_0_now_pressure = results_0_now["pressure"]; // "1018"
const char* results_0_now_humidity = results_0_now["humidity"]; // "76"
const char* results_0_now_visibility = results_0_now["visibility"]; // "16.09"
const char* results_0_now_wind_direction = results_0_now["wind_direction"]; // "西北"
const char* results_0_now_wind_direction_degree = results_0_now["wind_direction_degree"]; // "340"
const char* results_0_now_wind_speed = results_0_now["wind_speed"]; // "8.05"
const char* results_0_now_wind_scale = results_0_now["wind_scale"]; // "2"
const char* results_0_now_clouds = results_0_now["clouds"]; // "90"
const char* results_0_now_dew_point = results_0_now["dew_point"]; // "-12"
const char* results_0_last_update = results_0["last_update"]; // "2015-09-25T22:45:00-07:00"
// 通过串口监视器显示以上信息
int results_0_now_code_int = results_0_now["code"].as<int>();
int results_0_now_temperature_int = results_0_now["temperature"].as<int>();
String results_0_now_text_Str = results_0_now["text"].as<String>();
Serial.println(F("======Weahter Now======="));
Serial.print(F("Weather Now: "));
Serial.println(results_0_now_code_int);
Serial.print(F("Temperature: "));
Serial.println(results_0_now_temperature_int);
Serial.print(F("天气: "));
Serial.println(results_0_now_text_Str);
Serial.println(F("========================"));
}
串口打印结果
4、发送json格式
esp8266以json的格式往mqtt服务发数据,数据格式如下:
上行topic:esp8266/tsest
{
"gatew": "admin5555",
"time": 153034324,
"tag1": 15.32,
"tag2": 0,
"tag3": 43,
"tag4": "33"
}
1、发送固定格式
#include <ESP8266WiFi.h>
#include <PubSubClient.h>
// WiFi
const char *ssid = "999"; // WiFi账号
const char *password = "sp888888"; // WiFi密码
// MQTT Broker
const char *mqtt_broker = "39.98.87.103";//服务器Ip
const char *topic = "esp8266/io_test";//主题topic。下行topic,根据这个进行控制
const char *mqtt_username = "PubSubClien1"; //用户名
const char *mqtt_password = "";//密码
const int mqtt_port = 1883;//服务端口号
WiFiClient espClient;
PubSubClient client(espClient);
unsigned long previousMillis = 0;// 将保存上次触发的时间点,默认为0
// 定义常量:触发间隔,单位是毫秒,1000 = 1秒
const long interval = 3000;
void setup() {
// 串口波特率: 115200;
Serial.begin(115200);
delay(1000);
// 连接WIFI网络
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.println("连接WiFi网络中...");
}
Serial.println("连接到WIFI网络");
// 连接MQTT服务
client.setServer(mqtt_broker, mqtt_port);
// client.setCallback(callback);
while (!client.connected()) {
String client_id = "esp8266-client-";
client_id += String(WiFi.macAddress());
Serial.printf("客户端%s连接到MQTT 服务\n", client_id.c_str());
if (client.connect(client_id.c_str(), mqtt_username, mqtt_password)) {
Serial.println("已连接MQTT服务");//已连接公共EMQX MQTT代理
} else {
Serial.print("失败,状态为 ");
Serial.print(client.state());
delay(2000);
}
}
// 发布和订阅
//client.publish(topic, "hello emqx");//发布
client.subscribe(topic);//订阅
}
char* payloadJson="{\"gatew\": \"admin5555\",\"time\": 153034324,\"tag1\": 15.32,\"tag2\": 0,\"tag3\": 43,\"tag4\": \"33\"}";
void loop() {
client.loop();
unsigned long currentMillis = millis();//获取当前时间
//如果(当前时间 - 上次触发的时间 >) 说明该进行下一次触发了。
if (currentMillis - previousMillis >= interval)
{
// 将上次触发事件更新为 "当前时间"
previousMillis = currentMillis;
client.publish(topic, payloadJson);
Serial.println(payloadJson);
}
}
2、获取io状态以及传感器数据,通过json格式发送给mqtt。
测试代码
#include <ESP8266WiFi.h>
#include <PubSubClient.h>
#include <DHT.h>
#include <ArduinoJson.h>
// WiFi
const char *ssid = "999"; // WiFi账号
const char *password = "sp888888"; // WiFi密码
// MQTT Broker
const char *mqtt_broker = "39.98.87.103"; // 服务器Ip
const char *topic1 = "esp8266/io_test/w"; // 主题topic。下行topic,根据这个进行控制
const char *topic2 = "esp8266/io_test/r"; // 主题topic。下行topic,根据这个进行控制
const char *mqtt_username = "PubSubClien1"; // 用户名
const char *mqtt_password = ""; // 密码
const int mqtt_port = 1883; // 服务端口号
WiFiClient espClient;
PubSubClient client(espClient);
unsigned long previousMillis = 0; // 将保存上次触发的时间点,默认为0
//传感器
#define DHTPIN 2 // 连接到DHT传感器的数字引脚
#define DHTTYPE DHT11 // DHT 11
DHT dht(DHTPIN, DHTTYPE);
//定义4的标志位,存放4个io的状态。
int key1=0;
int key2=0;
int key3=0;
int key4=0;
void setup() {
// 串口波特率: 9600;
Serial.begin(9600);
// pinMode(0,OUTPUT);
//pinMode(5,OUTPUT);
//pinMode(6,OUTPUT);
// pinMode(7,OUTPUT);
//digitalWrite(0,HIGH);
// digitalWrite(5,HIGH);
//digitalWrite(6,HIGH);
//digitalWrite(7,HIGH);
delay(1000);
// 连接WIFI网络
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.println("连接WiFi网络中...");
}
Serial.println("连接到WIFI网络");
// 连接MQTT服务
client.setServer(mqtt_broker, mqtt_port);
// client.setCallback(callback);
while (!client.connected()) {
String client_id = "esp8266-client-";
client_id += String(WiFi.macAddress());
Serial.printf("客户端%s连接到MQTT 服务\n", client_id.c_str());
if (client.connect(client_id.c_str(), mqtt_username, mqtt_password)) {
Serial.println("已连接MQTT服务");//已连接公共EMQX MQTT代理
} else {
Serial.print("失败,状态为 ");
Serial.print(client.state());
delay(2000);
}
}
// 发布和订阅
// client.publish(topic, "hello emqx");//发布
client.subscribe(topic1);//订阅
client.subscribe(topic2);//订阅
dht.begin();//初始化温湿度传感器
}
void loop() {
client.loop();
unsigned long currentMillis = millis(); // 获取当前时间
// 如果(当前时间 - 上次触发的时间 >) 说明该进行下一次触发了。
if (currentMillis - previousMillis >= 5000) {
// 将上次触发事件更新为 "当前时间"
previousMillis = currentMillis;
// 温湿度传感器
float h = dht.readHumidity();
// 读取湿度度(%RH)
float t = dht.readTemperature();
// 读取湿度(℃)
// 检查传感器是否有任何读取失败,并提前退出(重试)。
if (isnan(h) || isnan(t)) {
Serial.println(F("从DHT传感器读取失败!"));
//return; // 如果传感器读取失败,直接退出此次循环
}
// 序列化 JSON 数据
DynamicJsonDocument doc(256); // 定义一个大小为 256 字节的 JSON 文档
doc["temp"] = t;
doc["hum"] = h;
doc["key1"] = key1;
doc["key2"] = key2;
doc["key3"] = key3;
doc["key4"] = key4;
// 分配足够的内存
char payloadJson[256];
//将 JSON 文档 doc 序列化为 JSON 字符串,并将其存储在 payloadJson 字符数组中,
//然后将序列化后的 JSON 字符串的长度存储在 length 变量中
size_t length = serializeJson(doc, payloadJson, sizeof(payloadJson));
// 发布 MQTT 消息
client.publish(topic2, payloadJson);
// 打印到串口
Serial.print(F("湿度: "));
Serial.print(h);
Serial.print(F(",温度: "));
Serial.println(t);
}
}