阿里云IoT套件测试5:管理多个设备(单个ESP32)
本帖最后由 szjuliet 于 2019-5-30 10:55 编辑管理多个设备(单个ESP32)
阿里云IoT套件测试1:开箱、软件安装及示例测试
阿里云IoT套件测试2:IoT平台注册、创建产品及设备
阿里云IoT套件测试3:创建物联网移动应用开发
阿里云IoT套件测试4:智能LED灯
阿里云IoT套件测试5:管理多个设备(单个ESP32)
依据Starter Kit for Aliyun IoT 教程测试:链接地址
感谢@绿水无痕的技术指导!!
演示视频
http://v.qq.com/x/page/y08751cig30.html
测试预期目标:
[*]搭建一个模拟智能家居环境,实现以下功能:
[*]FireBeetle连接了多个设备:灯、风扇和继电器
[*]在一个APP中可以同时管理多个设备
所需元件:
[*]DFR0478 FireBettle Board-ESP32(焊接排母)x1
[*]DFR0483 FireBeetle Covers-Gravity Adapter Board(焊接排针)x1
[*]DFR0031-R 数字食人鱼红色LED发光模块x1
[*]DFR0017 继电器模块 x1
[*]FIT0011 数字传感器连接线 x3
[*]小风扇x1
[*]智能手机 x1
说明:如果将继电器接上插座,就可以实现控制现实生活中的设备。智能插座的项目我在2016年指导学生小课题做过类似的项目,只是通信方式是BLE,使用的是Arduino101,移动端开发使用的是App Inventor。阿里云这个项目的继电器也可以这样来控制,因为时间关系,这一步我就省略了,做法是一样的。下面是Arduino101通过BLE控制电器开关的演示视频。
http://v.qq.com/x/page/y0867xf5yc4.html
解决的主要问题
这个项目虽然看上去只是完成了一个简单的功能(实际上我刚开始也觉得这个目标应该很容易实现),但是做了以后才发现不容易。这个项目很是折腾了几天。教程给出的项目都是一个产品一个设备,每次只能控制一个设备。我的设想是控制多个设备。
1. 解决控制多个设备的控制问题:
我采用了一个产品下挂多个设备的方式。在云平台上使用在线模拟虽然可以控制所有设备的开关,但是在手机上却只能控制一个设备,观察串口监视器显示的publish的信息发现虽然控制了不同的设备,但是回调函数返回的topic都是第1个设备(LED)的。向技术人员@绿水无痕 请教后对设置进行了修改,一个产品下只有一个设备,产品自定义功能全部归属到这个设备中,这样就保证subTopic和pubTopic都是唯一的,手机可以控制所有设备。
2. 解决控制的逻辑问题:
虽然可以控制多个设备,但是奇怪的是开启某个设备会把前面开的设备关掉,每次只能有一个设备处于开启的状态,这个和现实是有冲突的。和@绿水无痕多次沟通,在他的帮助下解决了问题。后面会详细说明。
以下是项目测试的详细步骤
一、阿里云IoT设置
实现单ESP32下控制多个设备按下面的流程来进行:
新建项目-->新建产品-->自定义功能(灯,风扇,插座)-->新增设备(一个)-->移动应用开发-->每个控制对应产品的一个自定义功能。
1. 新建项目:
[*]登录阿里云并进入物联网平台控制台,在左侧导航栏选择开发服务->IoT Studio,单击新建项目,根据提示,填写信息。项目名称为“智能家居管理”
2. 新增(或导入已有)产品:
[*]在左侧导航栏选择设备管理->产品,单击新增一个产品(或导入产品,如已有),根据提示,填写信息。产品名称为“智能家居”
3. 为产品添加自定义功能:
[*]点击产品智能家居-->功能定义-->自定义功能-->添加功能,为智能家居添加属性:灯的状态,标识符为LightStatus,数据类型为bool(布尔型),0为关闭,1为打开,读写类型为“读写”
[*]同样的方法添加三个自定义功能:风扇状态、插座状态、红外状态,标识符分别为LightFanStatus、RelayStatus、PIRStatus,数据类型为bool(布尔型),0为关(无),1为开(有),读写类型为“读写”。添加完后显示如下:
说明:这里添加了红外,是准备后面做智能防盗用的。
4. 新增设备:
[*]在左侧导航栏选择设备管理->设备,单击新增设备,会自动生成一个设备。
[*]设备生成后会出现在设备列表中
[*]点击上图中右下角的“激活凭证”,可以查看凭证,将凭证复制并保存到安全的位置,我们在后面的程序中会用到这些凭证
5. 新增可视化应用:
[*]在左侧导航栏选择推荐->移动应用开发,点击右方新增可视化应用,根据实际填写信息:
[*]APP用户界面设置
http://v.qq.com/x/page/d0872he1aaz.html
[*]为设备配置数据
http://v.qq.com/x/page/s0874u8nzvj.html
[*]在编辑中,点击首页模块-->列表页-->新增页面入口-->配置跳转链接,选择跳转到“智能家居管理”页面,修改标题和描述,上传自定义图片作为图标。
[*]点击右上角构建-->Android构建,将APP打包并安装到手机上并运行。第一次运行需要输入用户名和密码,这个是在账号中设置:
二、线路连接
LED灯接D2,风扇接D4,继电器接D6
三、程序编写
选择样例文件中的任何一个进行改写。
改写的部分代码如下:
1. 定义3个设备的引脚
#define LIGHTD2
#define FAN D4
#define RELAY D6
2. 定义标识符对应每种物理设备
/*需要操作的产品标识符*/
String Identifier_Light = "LightStatus";
String Identifier_Fan = "FanStatus";
String Identifier_Relay = "RelayStatus";
3. 三个设备的开关代码
static void openLight(){
digitalWrite(LIGHT, HIGH);
}
static void closeLight(){
digitalWrite(LIGHT, LOW);
}
static void openFan(){
digitalWrite(FAN, HIGH);
}
static void closeFan(){
digitalWrite(FAN, LOW);
}
static void openRelay(){
digitalWrite(RELAY, HIGH);
}
static void closeRelay(){
digitalWrite(RELAY, LOW);
}
4. 修改回调函数
void callback(char * topic, byte * payload, unsigned int len){
Serial.print("Recevice [");
Serial.print(topic);
Serial.print("] ");
for (int i = 0; i < len; i++){
Serial.print((char)payload);
}
Serial.println();
StaticJsonBuffer<300> jsonBuffer;
JsonObject& root = jsonBuffer.parseObject((const char *)payload);
if(!root.success()){
Serial.println("parseObject() failed");
return;
}
const uint16_t LightStatus = root["params"];
if(LightStatus == 1){
openLight();
}else{
closeLight();
}
Serial.println("LightStatus:");
Serial.println(LightStatus);
const uint16_t FanStatus = root["params"];
if(FanStatus == 1){
openFan();
}else{
closeFan();
}
Serial.println("FanStatus:");
Serial.println(FanStatus);
const uint16_t RelayStatus = root["params"];
if(RelayStatus == 1){
openRelay();
}else{
closeRelay();
}
Serial.println("RelayStatus:");
Serial.println(RelayStatus);
String tempMseg_Light = "{\"id\":"+ClientId+",\"params\":{\""+Identifier_Light+"\":"+(String)LightStatus+"},\"method\":\"thing.event.property.post\"}";
char sendMseg_Light;
strcpy(sendMseg_Light,tempMseg_Light.c_str());
client.publish(pubTopic,sendMseg_Light);
String tempMseg_Fan = "{\"id\":"+ClientId+",\"params\":{\""+Identifier_Fan+"\":"+(String)FanStatus+"},\"method\":\"thing.event.property.post\"}";
char sendMseg_Fan;
strcpy(sendMseg_Fan,tempMseg_Fan.c_str());
client.publish(pubTopic,sendMseg_Fan);
String tempMseg_Relay = "{\"id\":"+ClientId+",\"params\":{\""+Identifier_Relay+"\":"+(String)RelayStatus+"},\"method\":\"thing.event.property.post\"}";
char sendMseg_Relay;
strcpy(sendMseg_Relay,tempMseg_Relay.c_str());
client.publish(pubTopic,sendMseg_Relay);
5. 定义引脚输出模式
pinMode(LIGHT,OUTPUT);
pinMode(FAN,OUTPUT);
pinMode(RELAY,OUTPUT);
四、调试程序
在调试中会遇到很多错误,如编译错误,程序运行错误。编译错误比较好解决,程序运行错误就要仔细分析串口监视器的返回内容,查找错误原因。在DF给的KIT中有许多.h头文件,根据需要打开相应的头文件了解程序运行的机制,还可以根据错误返回信息查找头文件来找出错误原因。
这个错误可以看到是MQTT连接失败,rc=4,返回的错误代码是4,我们找到相应的头文件PubSubClient.h可以查看到返回代码4是凭证错误,也就是三联码:ProductKey,DeviceName和DeviceSecret中的一个或几个有问题,检查后发现确实有一个凭证是错的,修改后问题解决。从下图中可以看到返回代码所代表的含义:
MQTT连接超时 -4
MQTT连接丢失 -3
MQTT连接失败 -2
MQTT断开连接 -1
MQTT连接成功 0
MQTT连接协议错误 1
MQTT连接ClientId错误 2
MQTT连接不可用 3
MQTT连接凭证错误 4
MQTT连接未授权 5
进行在线模拟的时候发现虽然在平台上可以控制设备的开关,但是手机上只能控制灯的开关,其他两种设备无法控制,查看串口监视器发现回调函数返回的Topic是有问题的。这是因为我们在一个产品下有多个设备,这样造成Topic不唯一,解决方法是在一个产品下只增加一个设备,产品下面添加自定义功能,每个功能对应设备的一种属性。
按照上面的想法修改程序后,在线模拟以及手机都可以控制所有设备,但是奇怪的是在打开某个设备时也会关闭其他开着的设备,如下面的视频演示:
http://v.qq.com/x/page/r08759lcco5.html
与@绿水无痕进行探讨,他分析原因应该是“LightStatus是ArduinoJson中的子对象,没法用containSkey判断,只能用root["params"]["LightStatus"]获取,如果没有的话自动返回NULL,会与Bool类型的0冲突,导致无法判断”,即每次发送命令时只有一个设备的状态被发送,另外两个没有发送,而没有发送系统返回null,即0。所以应付对其他两个设备采取关闭的操作,导致上面所演示的错误发生。
解决方法是引入3个变量来读取返回的topic里是否包含了对应的标识符,如果没有包含就不执行任何操作(保持设备原有的状态);如果包含再判断返回来的值是1还是0,1就执行开启操作,0执行关闭操作。
修改回调函数:
void callback(char * topic, byte * payload, unsigned int len){
Serial.print("Recevice [");
Serial.print(topic);
Serial.print("] ");
for (int i = 0; i < len; i++){
Serial.print((char)payload);
}
Serial.println();
StaticJsonBuffer<300> jsonBuffer;
JsonObject& root = jsonBuffer.parseObject((const char *)payload);
if(!root.success()){
Serial.println("parseObject() failed");
return;
}
const char* val1=root["params"];
const char* val2=root["params"];
const char* val3=root ["params"];
if(val1!=NULL)
{
Serial.println("++++++++++");
const uint16_t LightStatus = root["params"];
if (LightStatus == 1){
openLight();
}else{
closeLight();
}
String tempMseg = "{\"id\":"+ClientId+",\"params\":{\""+Identifier_Light+"\":"+(String)LightStatus+"},\"method\":\"thing.event.property.post\"}";
char sendMseg;
strcpy(sendMseg,tempMseg.c_str());
client.publish(pubTopic,sendMseg);
}
if(val2!=NULL)
{
Serial. println("----------");
const uint16_t FanStatus = root["params"];
if (FanStatus == 1){
openFan();
}else{
closeFan();
}
String tempMseg = "{\"id\":"+ClientId+",\"params\":{\""+Identifier_Fan+"\":"+(String)FanStatus+"},\"method\":\"thing.event.property.post\"}";
char sendMseg;
strcpy(sendMseg,tempMseg.c_str());
client.publish(pubTopic,sendMseg);
}
if(val3!=NULL)
{
Serial. println("**********");
const uint16_t RelayStatus = root["params"];
if (RelayStatus == 1){
openRelay();
}else{
closeRelay();
}
String tempMseg = "{\"id\":"+ClientId+",\"params\":{\""+Identifier_Relay+"\":"+(String)RelayStatus+"},\"method\":\"thing.event.property.post\"}";
char sendMseg;
strcpy(sendMseg,tempMseg.c_str());
client.publish(pubTopic,sendMseg);
}
}
程序修改完后运行,执行开启设备命令后串口监视器如下:
提升
在现实家居生活中有多个设备,单用一个ESP32肯定是不够也不方便,可以用多个ESP32,每个ESP32下可以挂多个设备。每个ESP32程序结构大致一样,不需要做过多的修改。只需要在阿里云的项目中增加产品,每个产品对应一个ESP32即可。手头有FireBeetle,掌控板,Linkit7697,还有Obloq,ArduinoUNO WiFi,等有机会试验一下。
无力吐槽一下阿里云。这个平台目前还是不太稳定,总会有奇奇怪怪的问题出现:删除的设备在组件属性中还是会出现,即使更新并保存,过一会儿又恢复已删除的东西;打包的app运行后点进页面出现“访问发现错误,当前版本没有打包”,重新开发一个新的app才运行正常;23号更坑,打包的app一运行就闪退,换电脑,换手机,换网络,重新构建...试了各种方法仍然不行,24号早上才得知是阿里云本身出了问题,最近更新出了bug,这是得多大的bug,太不应该了啊!另外已经在线模拟或使用的app不能删除也是一个大槽点,太不方便了。
倒是DF的器材性能很稳定,尤其主控板连接很迅速,响应也很快。赞一个!!
赞!很认真、好详细,收藏 安卓机器人 发表于 2019-5-25 11:35
赞!很认真、好详细,收藏
{:5_197:} 好棒!解决了我这几天的困惑 好棒!学习中! 楼主,有完整的样例代码吗? 楼主,感谢真的香! 楼主,上报的topic要修改吗?我在做一个土壤湿度传感器和继电器的东西,传感器topic只要上报就行了,但继电器要上报和订阅,这俩个怎么改啊?能教教吗?
我也被这个问题折磨了很久,我一直以为是我云端创建产品的时候有问题才导致下发的时候params里只有一个状态 版主您好,如果是土壤湿度、温湿度检测加灯光、喇叭实现报警检测,如何实现共联 本帖最后由 大叔1 于 2021-3-3 20:13 编辑
我按代码里填了三元组,编译成功后,上传到 ESP32 CAM 主板, 但是一直连不上MQTT:
到底是什么原因?
本帖最后由 大叔1 于 2021-3-3 23:07 编辑
阿里云IOT studio 做的界面里还有个很奇怪的问题,就是无论什么开关,点一下,会发送两次消息,一次开,一次关,同时发送了,而且开关会弹回原位
我今年才买了一套阿里云套件,教材里几个例子,单个设备调试都正常,也遇到楼主的问题,但想把多个设备一起上云,程序不好修改。楼主的思路值得借鉴。已经过了二三年了,不知道有没有新的比较简洁的办法出现? 大叔1 发表于 2021-3-3 22:14
阿里云IOT studio 做的界面里还有个很奇怪的问题,就是无论什么开关,点一下,会发送两次消息,一次开,一 ...
我也出现了这个现象,而不是开关二次,而是重复不停的开关。不清楚原因. 赞!很认真、好详细,收藏 大叔1 发表于 2021-3-3 22:14
阿里云IOT studio 做的界面里还有个很奇怪的问题,就是无论什么开关,点一下,会发送两次消息,一次开,一 ...
我也遇到了和你同样的问题。今天和阿里云IOT的一个工程师沟通了半天,结果发现是,当阿里云平台发送指令(类似于 /sys/hrkchWpcCEv/LED/thing/service/property/set)后,原本我们的esp32应该发送一个且仅有一个reply给云平台的。再DFRobot提供的程序里面,是通过callback()函数的。
但是不知道什么原因,这个callback函数被不停的调用,多次上报给云平台,而因为设备端短时间上报次数过多导致了云平台的限流,所以没有上报成功,从而web项目报错, 为保护设备,自动恢复到开始的状态。
阿里云IoT的工程建议我,修改下设备端上报逻辑。
我自己通过调看串口,的确发现短时间内,callback函数被调用了50多次。
但是我们这边如果需要修改callback()函数的化,工作量不是一般的大。只能请DFRobot的工程师@绿水无痕来修改的。
狮山闲人 发表于 2022-4-27 11:35
我也出现了这个现象,而不是开关二次,而是重复不停的开关。不清楚原因. ...
我也遇到了和你同样的问题。今天和阿里云IOT的一个工程师沟通了半天,结果发现是,当阿里云平台发送指令(类似于 /sys/hrkchWpcCEv/LED/thing/service/property/set)后,原本我们的esp32应该发送一个且仅有一个reply给云平台的。再DFRobot提供的程序里面,是通过callback()函数的。
但是不知道什么原因,这个callback函数被不停的调用,多次上报给云平台,而因为设备端短时间上报次数过多导致了云平台的限流,所以没有上报成功,从而web项目报错, 为保护设备,自动恢复到开始的状态。
阿里云IoT的工程建议我,修改下设备端上报逻辑。
我自己通过调看串口,的确发现短时间内,callback函数被调用了50多次。
但是我们这边如果需要修改callback()函数的化,工作量不是一般的大。只能请DFRobot的工程师@绿水无痕来修改的。
大叔1 发表于 2021-3-3 22:14
阿里云IOT studio 做的界面里还有个很奇怪的问题,就是无论什么开关,点一下,会发送两次消息,一次开,一 ...
这个是代码callback函数问题的。 helloworld_dfro 发表于 2022-10-18 00:07
我也遇到了和你同样的问题。今天和阿里云IOT的一个工程师沟通了半天,结果发现是,当阿里云平台发送指令 ...
和DFRobot的工程师沟通了半天,并没有解决。后面找了一个计算机的高手。修改了回调函数,解决了问题。不得不吐槽一下下DFRobot的工程师,这么大的bug放在那里几年了,还没有解决,一直说是他们的套件没有问题,验证了无数次。
页:
[1]