注:本文系湛江市第十七中学星火创客团队及岭南师范学院物联网俱乐部原创部分参赛项目,转载请保留声明。
一般在做物联网相关项目的时候,物联网与软件对接的时候,需要先采集数据,将数据上传至云端后,再将数据转存到MySQL中,方便后台人员去调取数据,本文采用的方法是一种使用Wemos(ESP8266)将采集到的数据通过MQTT协议上传至EMQX服务器,再由EMQX服务器将数据转存至MySQL。这样就可以让后端人员很方便的去调取所需要的数据内容。
提示:以下是本篇文章正文内容,下面案例可供参考
硬件 | 数量 |
---|---|
Wemos | 1 |
DHT11 | 1 |
杜邦线 | 若干 |
软件 |
---|
Ubuntu 18.04 LTS |
Arduino IDE |
宝塔面板 |
EMQ X Enterprise |
MQTTX |
Navicat |
在项目开始前,要记得在云服务器的 “安全组/防火墙” 那边开放宝塔的默认端口(8888),再顺便把MQTT(1883)、WebSocket(8083)、EMQX(18083)、MySQL(3306)、phpMyAdmin(888)这些端口开放
宝塔面板安装链接:https://www.bt.cn/bbs/thread-19376-1-1.html
在云服务器上安装宝塔面板,阿里云、腾讯云、华为云等云平台均可,只需在下载的时候选对自己的云服务器所对应的系统及版本
我这里用的是阿里云的Ubuntu 18.04 LTS 云服务器
在云服务器中运行以下指令,自动安装,安装完后,在网页端打开宝塔面板(xxx.xxx.xxx.xxx:8888)默认选择LNMP
(注意,云服务器不能在安装宝塔面板之前,安装过(Apache、MySQL、PHP、Nginx)等环境,不然会导致出错)
wget -O install.sh http://download.bt.cn/install/install-ubuntu_6.0.sh && sudo bash install.sh
安装完宝塔后,即可通过外网面板地址进入登录界面,使用“username”“password”登入宝塔面板
(若是觉得默认的 “username” 和 “password” 比较难记或者麻烦,也可以在宝塔面板的“面板设置”中进行修改)
在宝塔面板中 “数据库”,点击“添加数据库”新建一个数据库
数据库编码格式中选择 “utf8mb4”,避免出现编码问题
打开**“Navicat For MySQL”**来对数据库进行管理操作,
首先建立一个新的连接
填写好内容,不保证参数正确的话,可以测试连接,确保测试连接通过,提交后,即可成功创建连接,双击即可连接
字段 | 用途 |
---|---|
ID | 用于数据的序号展示 |
Temperature | 储存温度值 |
Humidity | 储存湿度值 |
ts | 储存数据上传时的时间戳 |
Ps:“ID” 字段要勾上 “自动递增” 方便排序,在数据表中看上去更直观
并将表名命名为 “Temp_Humi_Data”
点击 “phpMyAdmin” 或 “管理” 进入phpMyAdmin进行数据库管理
“新建表” 添加以下4个字段,并将表命名为 “Temp_Humi_Data”
下载链接:https://www.emqx.cn/downloads
这里使用的是Ubuntu 18.04 LTS,我使用的是以下下载方法
若系统是Ubuntu 18.04LTS,选择的参数与上图一致,则可依次执行以下指令
wget https://www.emqx.cn/downloads/enterprise/v4.2.5/emqx-ee-ubuntu18.04-4.2.5-x86_64.zip
unzip emqx-ee-ubuntu18.04-4.2.5-x86_64.zip
cd ./emqx
./bin/emqx start
在网址栏输入“云服务器公网IP:18083”,进入 EMQ X Dashboard
在左侧选中“规则引擎”—“资源”—“创建”,按照下图填写
(注意:带有MySQL字样的参数,都是在宝塔中新建数据库的参数,要保证参数一致)
创建完资源后,接着创建规则,在 “SQL输入” 中输入以下SQL语句
SELECT
payload.Temperature AS Temperature,
payload.Humidity AS Humidity,
payload.ts AS ts
FROM
"Device_pubTopic/Temp_Humi"
这里是获取 “Device_pubTopic/Temp_Humi” payload中“Temperature”、“Humidity”、“ts”字段的值
然后下去添加响应动作
其中 “使用资源” 为我们刚才创建的资源
SQL模板:
INSERT INTO
`Temp_Humi_Data` (`Temperature`, `Humidity`, `ts`)
VALUES
(${Temperature}, ${Humidity}, ${ts});
这里的意思是,向 “Temp_Humi_Data” 数据表中写入“Temperature”、“Humidity”、“ts”的值
配置好后,本规则就可以将payload中的 “温湿度及时间戳” 数据提取出来,并转存到MySQL中相应数据库的相应数据表中
下载链接:https://mqttx.app/cn/
接下来使用MQTTX进行测试,打开MQTTX,新建一个连接,填写以下参数
在EMQ X Dashboard界面左侧“工具”—“WebSocket”填写 “”连接配置“” 的参数,并订阅设备发布的主题 “Device_pubTopic/Temp_Humi”
注意事项:Client_ID不要和MQTTX中的Client_ID相同,不然会导致MQTTX连接后自动断开
由下图可看出消息的发布与接收均正常
(红色为MQTTX发布到服务器的JSON数据,蓝色为服务器发布到MQTTX的JSON数据)
本项目获取的时间戳是使用了苏宁网站的授时网页提供的
http://quan.suning.com/getSysTime.do
对此需要对网站内容进行JSON格式解析,提取出 “sysTime2” 字段的值 “2021-04-20 20:29:03”
核心代码
http.setTimeout(5000); //设置5s超时
http.begin("http://quan.suning.com/getSysTime.do"); //HTTP初始化
int httpCode = http.GET(); //发送HTTP请求,返回状态码
if (httpCode > 0) {
Serial.printf("[HTTP] GET... code: %d\n", httpCode);
if (httpCode == HTTP_CODE_OK) {
//读取响应内容
JSON_DATA = http.getString();
//这里返回的是json格式的字符串
deserializeJson(Doc, JSON_DATA);
JsonObject Obj = Doc.as<JsonObject>();
Field_Val = Obj["sysTime2"]; //获取Json数据中的字段值Field(2020-04-20 20:29:03)
}
} else {
Serial.printf("[HTTP] GET... failed, error: %s\n", http.errorToString(httpCode).c_str());
}
http.end(); //接收完数据后,触发end事件
利用
核心代码
unsigned long now = millis();
if (now - lastMsg > 10000) {
Get_Time(); //从网站获得网页内容
Get_Temp_Humi(); //获取温湿度数据
lastMsg = now;
//String MQTT_Msg = "{\"Temperature\": \"%.1f\",\"Humidity\": \"%.1f\",\"ts\": \"%s\"}";
//将消息模板和温湿度、时间戳数据合并到一起
snprintf (Msg, Msg_LEN, MQTT_Msg, Temp, Humi, Field_Val.c_str());
Serial.print("Publish message: ");
Serial.println(Msg);
client.publish(pubTopic, Msg);
}
#include
#include
#include
#include
#include
/*连接的Wi-Fi、服务器地址*/
const char* ssid = "SSID";
const char* password = "PASSWORD";
const char* mqtt_server = "xxx.xxx.xxx.xxx";
/*设置发布和订阅的主题*/
const char* subTopic = "Cloud_pubTopic"; //subTopic
const char* pubTopic = "Device_pubTopic/Temp_Humi"; //pubTopic
#define MQTT_Msg "{\"Temperature\": \"%.1f\",\"Humidity\": \"%.1f\",\"ts\": \"%s\"}"
WiFiClient espClient;
PubSubClient client(espClient);
long lastMsg = 0;
#define Msg_LEN 100
char Msg[Msg_LEN];
HTTPClient http;
String GetUrl = "http://quan.suning.com/getSysTime.do";
String JSON_DATA;
#define JSON_LEN 100
DynamicJsonDocument Doc(JSON_LEN);
String Target = "sysTime2";
String Field_Val = "";
dht DHT;
#define DHT11_PIN D2
float Temp, Humi;
void setup_wifi() {
delay(10);
// We start by connecting to a WiFi network
Serial.println();
Serial.print("Connecting to ");
Serial.println(ssid);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
randomSeed(micros());
Serial.println("");
Serial.println("WiFi connected");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
}
void callback(char* topic, byte* payload, unsigned int length) {
Serial.print("Message arrived [");
Serial.print(topic);
Serial.print("] ");
for (int i = 0; i < length; i++) {
Serial.print((char)payload[i]);
}
Serial.println();
// Switch on the LED if an 1 was received as first character
if ((char)payload[0] == '1') {
digitalWrite(BUILTIN_LED, LOW); // Turn the LED on (Note that LOW is the voltage level
// but actually the LED is on; this is because
// it is active low on the ESP-01)
} else {
digitalWrite(BUILTIN_LED, HIGH); // Turn the LED off by making the voltage HIGH
}
}
void reconnect() {
// Loop until we're reconnected
while (!client.connected()) {
Serial.print("Attempting MQTT connection...");
// Create a random client ID
String clientId = "ESP8266Client-";
clientId += String(random(0xffff), HEX);
// Attempt to connect
if (client.connect(clientId.c_str())) {
Serial.println("connected");
// Once connected, publish an announcement...
// client.publish(pubTopic, "hello world");
// ... and resubscribe
client.subscribe(subTopic);
} else {
Serial.print("failed, rc=");
Serial.print(client.state());
Serial.println(" try again in 5 seconds");
// Wait 5 seconds before retrying
delay(5000);
}
}
}
void setup() {
pinMode(BUILTIN_LED, OUTPUT); // Initialize the BUILTIN_LED pin as an output
Serial.begin(115200);
setup_wifi();
client.setServer(mqtt_server, 1883);
client.setCallback(callback);
// 连接苏宁网站的授时网页
http.setTimeout(5000);
http.begin(GetUrl);
}
void loop() {
if (!client.connected()) {
reconnect();
}
client.loop();
unsigned long now = millis();
if (now - lastMsg > 10000) {
Get_Time(); //从网站获得网页内容
Get_Temp_Humi(); //获取温湿度数据
lastMsg = now;
//String MQTT_Msg = "{\"Temperature\": \"%.1f\",\"Humidity\": \"%.1f\",\"ts\": \"%s\"}";
snprintf (Msg, Msg_LEN, MQTT_Msg, Temp, Humi, Field_Val.c_str());
Serial.print("Publish message: ");
Serial.println(Msg);
client.publish(pubTopic, Msg);
}
}
/**********
* 读取DHT11的温湿度数据
**********/
void Get_Temp_Humi() {
int chk = DHT.read11(DHT11_PIN);
switch (chk)
{
case DHTLIB_OK:
Serial.print("OK,\t");
break;
case DHTLIB_ERROR_CHECKSUM:
Serial.print("Checksum error,\t");
break;
case DHTLIB_ERROR_TIMEOUT:
Serial.print("Time out error,\t");
break;
default:
Serial.print("Unknown error,\t");
break;
}
Temp = DHT.temperature;
Humi = DHT.humidity;
}
void Get_Time() {
int httpCode = http.GET();
if (httpCode > 0) {
Serial.printf("[HTTP] GET... code: %d\n", httpCode);
if (httpCode == HTTP_CODE_OK) {
//读取响应内容
JSON_DATA = http.getString();
//这里返回的是json格式的字符串
Field_Val = Get_JSON_Field(JSON_DATA, "sysTime2"); //获取Json数据中的字段值Field
}
} else {
Serial.printf("[HTTP] GET... failed, error: %s\n", http.errorToString(httpCode).c_str());
}
http.end();
}
String Get_JSON_Field(String JSON_FORMAT_DATA, String Target) {
// 将 JSON_FORMAT_DATA 替换成你想处理的字符串
deserializeJson(Doc, JSON_FORMAT_DATA);
JsonObject Obj = Doc.as<JsonObject>();
String Params = Obj[Target]; //将Target字段的值提取出来赋给Params
return Params;
}
以上就是使用Wemos(ESP8266)通过MQTT将数据传输到服务器并转存到MySQL中,本文仅仅简单介绍了Wemos(ESP8266)上传数据至云服务器的使用,如有写的不好的地方,欢迎大家提点宝贵的建议。