NodeMCU-ESP8266连接阿里云Iot平台进行数据监测
2021/12/3 22:06:39
本文主要是介绍NodeMCU-ESP8266连接阿里云Iot平台进行数据监测,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
简介
本教程中主要讲解使用阿里云Iot监测控制NodeMCU的方法。
设备从MQTT数据上传、监测、控制的全流程如下图所示,本教程仅仅讲解从设备与Aliyun平台之间的交互,业务服务器部分(App开发)会在后面的教程中进行讲解,敬请期待。
友情提醒:多看官方文档,官方文档什么都有。
开发环境
- Arduino IDE
- NodeMCU1.0芯片包
- ArduinoJson库
- PubSubClient库
PubSubClient
作者名:Nick O’Leary
官网地址:https://pubsubclient.knolleary.net/
GitHub:https://github.com/knolleary/pubsubclient/
百度网盘下载: https://pan.baidu.com/s/12MHGbdfiOdwOGip5RMSSEQ 提取码: sizy
如果不知道怎么安装芯片包和导入第三方库,自行research
上一节讲述了NoduMCU通过软串口控制Arduino的例程,这一次笔者将NodeMCU接入AliyunIot平台,通过MQTT来远程监测控制NodeMCU,再通过NodeMCU来控制Arduino。
前述知识
MQTT协议
这里不做过多介绍,笔者默认您已经掌握了该知识,如果对MQTT不够了解,可以参考零基础入门学用物联网 – MQTT基础篇
物模型
物模型是产品数字化的描述,定义了产品的功能,物模型将不同品牌不同品类的产品功能抽象归纳,形成“标准物模型”,便于各方用统一的语言描述、控制、理解产品功能。
物模型由若干条“参数”组成,参数按描述的功能类型不同,又分为属性、方法和事件。
更为详细的物模型解析
为什么要有物模型?当下的云平台为了兼容不同的通信协议,在通信层之间构建了一个物模型,这才是最重要的事,为了让不同通信协议的设备都可以整合在一个平台内进行运行,因此需要一个云平台们非常需要一个中间层做整合,物模型由此诞生。本例程中采用MQTT模型,其订阅发布的逻辑也因为加了物模型层而有所不同。
虽然在本例程中的原理还是基于订阅发布的物模型,但是订阅发布的主题和要处理的数据已经与传统MQTT意义上的数据有所不同,下面将做简单的讲解。
基于物模型的MQTT协议
MQTT协议的使用方式在物模型的产生后所迭代,下面我举两个例子就可以看出传统MQTT和基于MQTT的物模型之间的差别了。
传统MQTT
在以上图示中一共有三个MQTT客户端。它们分别是汽车,手机和电脑。MQTT服务端在管理MQTT通讯时使用了“主题”来对信息进行管理的。比如上图所示,假设我们需要利用手机和电脑获取汽车的速度,那么我们首先要利用电脑和手机向MQTT服务器订阅主题“汽车速度”。接下来,当汽车客户端向服务端的“汽车速度”主题发布信息后,服务端就会首先检查以下都有哪些客户端订阅了“汽车速度”这一主题的信息。当它发现订阅了该主题的客户端有一个手机和一个电脑,于是服务端就会将刚刚收到的“汽车速度”信息转发给订阅了该主题的手机和电脑客户端。
我们现在做一个假设,汽车发布了多少数据手机端就像看到多少数据。而现在汽车只发布了一个速度的属性,如果这个时候汽车还想把自己车内温度上传上去,那么就要向一个名为“CarTemperture”的主题发布数据,这样一来,一个循环内,小车就要向两个主题发布两次消息,而手机也因此需要多订阅一个主题。在小车向n个主题发布数据的状态下,如果手机端想要全部接收到,难道就要一次一次把所有的主题都订阅了吗?这样来看就有些臃肿了。
而云平台的定位就和我们刚才所述的手机端一样,云平台想要看到设备发布的所有数据,因此不可能让云平台一个一个主题的去订阅,能不能把所有数据全部发送给云平台让云平台自己去解析呢?由此就产生了物模型。
基于物模型的MQTT
物模型的作用就相当于定义了主题名叫“post”,汽车只要把所有的数据放在一起发送给“post”就可以了。
如果您想要将汽车的速度和温度的属性进行上传,那么您可以先告诉云平台你的汽车要上传“速度”和“温度”两个属性,让云平台先了解你将要上传的数据。然后这时汽车就可以对云平台的“post”主题发送特定格式的字符串,这个字符串是云平台规定好的。比如发送如下字符串:
publish topic=/sys/xxx/yyy/thing/event/property/post, payload={"id":123321,"params":{"carTemperture":12312341,"speed":50},"version":"1.0","method":"thing.event.property.post"}
快速上手
下面开始讲解开发步骤,大致流程如下。
注册Aliyun帐号
进入Aliyun平台注册一个帐号,自己操作
在物联网平台创建实例
首先打开控制台;
打开物联网平台;
自行创建一个实例,这里的实例在本例程中你可以当做它是一个MQTT服务器。
创建产品
创建完实例后,进入实例,新建一个产品
创建一个设备,如下图所示。
添加物模型
在产品中,选择功能定义,编辑功能;
选择自定义功能;
创建一个温度属性,如下;
创建设备
切换页面,点击添加设备。
填写设备信息,如下图,这里的NodeMCU-1是笔者创建的产品;
Tip:产品和设备的管理你可以类比以下例子:Iphone 11 PRO是一个产品,我的11和你的11是两个设备。从这里可以看出,同一个产品的设备具有一样的属性,但是同一个产品不同设备属性的值可能不一样,比如说我的11还有100G内存,你的11只有10G内存的,这里的内存就可以当做一个属性。
创建完设备就可以进行代码的开发了。
Arduino IDE代码开发
下面讲解代码部分,代码部分主要分为几个部分:
- wifi连接
- mqtt初始化
- ntp网络时间获取
- 数据上报
- 数据接收
注意事项:
- 这里的数据上报就是对"post"主题发布信息
- 这里的数据接收功能当前只能接收到云平台发送了什么内容,这个内容是需要解析的,在后面的教程中会讲解如何解析数据。
在定义物模型的时候,笔者定义了两个属性,分别是开门时间和开门指令,这两个数据分别演示数据上报和数据下发。在NodeMCU运行的时候,会上传当前时间;而在另外一边,云平台会下发开门指令给NoduMCU。
例程
/* 目的:该例程为NodeMCU连接阿里云Iot平台的例程 * 作者:Zeeland * 最后修改时间:2021年12月2日 18:33:56 * https://gitee.com/zeeland/projects */ #include <ESP8266WiFi.h> #include <PubSubClient.h> #include <ArduinoJson.h> #include <ESP8266WiFiMulti.h> #include <NTPClient.h> #include <WiFiUdp.h> WiFiUDP ntpUDP; NTPClient timeClient(ntpUDP, "ntp1.aliyun.com",60*60*8, 30*60*1000); String currentTime; /* 设备证书信息*/ #define PRODUCT_KEY "PRODUCT_KEY" #define DEVICE_NAME "DEVICE_NAME" #define DEVICE_SECRET "DEVICE_SECRET" #define REGION_ID "cn-shanghai" /* Aliyun线上环境域名和端口号,不需要改 */ #define MQTT_SERVER PRODUCT_KEY ".iot-as-mqtt." REGION_ID ".aliyuncs.com" #define MQTT_PORT 1883 #define MQTT_USRNAME DEVICE_NAME "&" PRODUCT_KEY #define CLIENT_ID "gmvzwtDHC6.ntance1|securemode=2,signmethod=hmacsha256,timestamp=254604000000|" // MQTT连接报文参数,请参见MQTT-TCP连接通信文档,文档地址:https://help.aliyun.com/document_detail/73742.html // 加密明文是参数和对应的值(clientIdesp8266deviceName${deviceName}productKey${productKey}timestamp1234567890)按字典顺序拼接 // 密钥是设备的DeviceSecret //要使用加密工具,输入以上证书信息加密(时间戳可以省略) #define MQTT_PASSWD "a29f727768b161f9073e199ab6e37ee0e3e75f1320d0219a0a204b5bbb1420" // 发送报文的json格式 #define ALINK_BODY_FORMAT "{\"id\":\"123\",\"version\":\"1.0\",\"method\":\"thing.event.property.post\",\"params\":%s}" // 上报报文主题 #define ALINK_TOPIC_PROP_POST "/sys/" PRODUCT_KEY "/" DEVICE_NAME "/thing/event/property/post" unsigned long lastMs = 0; WiFiClient espClient; PubSubClient client(espClient); ESP8266WiFiMulti wifiMulti; unsigned int pwm_r=0,pwm_g=0,pwm_b=0; int openFlag = 0; void setup() { Serial.begin(9600); Serial.println("[info] Demo Start"); Serial.print("[info] CLIENT_ID:");Serial.println(CLIENT_ID); Serial.print("[info] MQTT_USRNAME:");Serial.println(MQTT_USRNAME); Serial.print("[info] MQTT_PASSWD:");Serial.println(MQTT_PASSWD); // 连接WIFI wifiInit(); // 初始化Mqtt服务 mqttServeInit(); // 初始化NTP时间服务 timeClient.begin(); } void loop() { //millis()是系统启动到目前的总时间,以下为5s上传一次数据 if (millis() - lastMs >= 3000) { // 获取当前时间 lastMs = millis(); // 检查连接状态 mqttCheckConnect(); timeClient.update(); // 获取当前时间 currentTime = timeClient.getFormattedTime(); Serial.print("[info] now time is :"); Serial.println(currentTime); // 上报消息 mqttIntervalPost(); // 根据下发的数据进行反馈 work(); } client.loop(); } void work() { if(openFlag ==1){ Serial.println("[info] this is the truly answer!!!"); } } // 初始化Mqtt服务 void mqttServeInit() { // 设置MQTT服务器和端口号 client.setServer(MQTT_SERVER, MQTT_PORT); // 设置MQTT订阅回调函数 client.setCallback(callback); } // 收到信息后的回调函数 void callback(char *topic, byte *payload, unsigned int length) { Serial.print("[info]Message arrived,the topic is ["); Serial.print(topic); Serial.println("] "); payload[length] = '\0'; const char* json = (char *)payload; Serial.println("收到的json:"); Serial.println(json); DynamicJsonDocument doc(1024); deserializeJson(doc, json); JsonObject root = doc.as<JsonObject>(); //云端下发的数据只有一个数据点,因此要判断是哪一个数据点下发了数据 if( root["params"].containsKey("openDoor") ) //containsKey方法为判断json对象是否包含指定字段 { openFlag = root["params"]["openDoor"]; } } // 连接wifi void wifiInit() { wifiMulti.addAP("LAPTOP-RIH1JO89 5592", "12345678"); // 将需要连接的一系列WiFi ID和密码输入这里 wifiMulti.addAP("MI 9", "12345678"); // ESP8266-NodeMCU再启动后会扫描当前网络 wifiMulti.addAP("LAPTOP9#337", "xy1229033519"); // 环境查找是否有这里列出的WiFi ID。如果有 Serial.println("[info] Connecting ..."); // 则尝试使用此处存储的密码进行连接。 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.print("[info] Connected to "); Serial.println(WiFi.SSID()); // 通过串口监视器输出连接的WiFi名称 Serial.print("[info] IP address:\t"); Serial.println(WiFi.localIP()); // 通过串口监视器输出ESP8266-NodeMCU的IP } // 检查设备与MQTT服务器连接情况 void mqttCheckConnect() { while (!client.connected()) { Serial.println("Connecting to MQTT Server ..."); if (client.connect(CLIENT_ID, MQTT_USRNAME, MQTT_PASSWD)) { Serial.println("MQTT Connected!"); } else { Serial.print("MQTT Connect err:"); Serial.println(client.state()); delay(5000); } } if (client.connected()){ Serial.println("[info] keeping alive"); } } /* 上报消息 */ void mqttIntervalPost() { char param[128]; char jsonBuf[128]; // 将current转换为字符数组 const char * temp = currentTime.c_str(); //上传的数据在这里编辑,该例程将上报的数据为当前时间 sprintf(param, "{\"openTime\":\"%s\"}",temp); sprintf(jsonBuf, ALINK_BODY_FORMAT, param); Serial.println("[info] 上传的json:"); Serial.println(jsonBuf); // 上传数据 boolean d = client.publish( ALINK_TOPIC_PROP_POST, jsonBuf); if(d==1){ Serial.println("[info] 发送成功"); }else{ Serial.println("[info] 发送失败"); } }
运行之后,NodeMCU首先会连接Wifi,然后连接aliyunIot平台的服务的对应设备,连接成功了之后,将会把当前的时间的数据上报至云服务器;而在NodeMCU接收到Iot平台发送的开门指令之后,Serial也会做出对应的反应。
注意事项:当出现MQTT连不上时,错误返回值2表示客户端标识符不正确, -4表示用户名或者密码错误。 请做以下检查:
- 先检查一下库文件PubSubClient.h文件中定义的 MQTT_MAX_PACKET_SIZE的值, 最好要大于1024, MQTT_KEEPALIVE 大于60;
- 检查一下你的签名和接入参数的设置,可以参考文档 https://help.aliyun.com/document_detail/73742.html?spm=a2c4g.11186623.6.650.3820619bBWPshh 。
官方文档:CONNECT指令中需包含Keep Alive(保活时间)。保活心跳时间取值范围为30至1200秒。如果心跳时间不在此区间内,物联网平台会拒绝连接。建议取值300秒以上。如果网络不稳定,将心跳时间设置高一些。
参考资料
nodemcu+阿里云(ArduinoIDE)
【NodeMCU_LUA系列】NodeMCU连接阿里云
(五)air800订阅云端数据并进行解析
string、char *、char[] 相互转换转换
这篇关于NodeMCU-ESP8266连接阿里云Iot平台进行数据监测的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-11-23增量更新怎么做?-icode9专业技术文章分享
- 2024-11-23压缩包加密方案有哪些?-icode9专业技术文章分享
- 2024-11-23用shell怎么写一个开机时自动同步远程仓库的代码?-icode9专业技术文章分享
- 2024-11-23webman可以同步自己的仓库吗?-icode9专业技术文章分享
- 2024-11-23在 Webman 中怎么判断是否有某命令进程正在运行?-icode9专业技术文章分享
- 2024-11-23如何重置new Swiper?-icode9专业技术文章分享
- 2024-11-23oss直传有什么好处?-icode9专业技术文章分享
- 2024-11-23如何将oss直传封装成一个组件在其他页面调用时都可以使用?-icode9专业技术文章分享
- 2024-11-23怎么使用laravel 11在代码里获取路由列表?-icode9专业技术文章分享
- 2024-11-22怎么实现ansible playbook 备份代码中命名包含时间戳功能?-icode9专业技术文章分享