2019-5-25 10:57:43 [显示全部楼层]
22961浏览
查看: 22961|回复: 18

[讨论交流] 阿里云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 教程测试:链接地址


感谢@绿水无痕的技术指导!!

演示视频


测试预期目标:
  • 搭建一个模拟智能家居环境,实现以下功能:
  • 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控制电器开关的演示视频。


解决的主要问题

这个项目虽然看上去只是完成了一个简单的功能(实际上我刚开始也觉得这个目标应该很容易实现),但是做了以后才发现不容易。这个项目很是折腾了几天。教程给出的项目都是一个产品一个设备,每次只能控制一个设备。我的设想是控制多个设备。
1. 解决控制多个设备的控制问题:
我采用了一个产品下挂多个设备的方式。在云平台上使用在线模拟虽然可以控制所有设备的开关,但是在手机上却只能控制一个设备,观察串口监视器显示的publish的信息发现虽然控制了不同的设备,但是回调函数返回的topic都是第1个设备(LED)的。向技术人员@绿水无痕 请教后对设置进行了修改,一个产品下只有一个设备,产品自定义功能全部归属到这个设备中,这样就保证subTopic和pubTopic都是唯一的,手机可以控制所有设备。
2. 解决控制的逻辑问题:
虽然可以控制多个设备,但是奇怪的是开启某个设备会把前面开的设备关掉,每次只能有一个设备处于开启的状态,这个和现实是有冲突的。和@绿水无痕多次沟通,在他的帮助下解决了问题。后面会详细说明。

以下是项目测试的详细步骤

一、阿里云IoT设置

实现单ESP32下控制多个设备按下面的流程来进行:
新建项目-->新建产品-->自定义功能(灯,风扇,插座)-->新增设备(一个)-->移动应用开发-->每个控制对应产品的一个自定义功能。


1. 新建项目:
  • 登录阿里云并进入物联网平台控制台,在左侧导航栏选择开发服务->IoT Studio,单击新建项目,根据提示,填写信息。项目名称为“智能家居管理”
阿里云IoT套件测试5:管理多个设备(单个ESP32)图2

2. 新增(或导入已有)产品:
  • 在左侧导航栏选择设备管理->产品,单击新增一个产品(或导入产品,如已有),根据提示,填写信息。产品名称为“智能家居”
阿里云IoT套件测试5:管理多个设备(单个ESP32)图3
3. 为产品添加自定义功能:
  • 点击产品智能家居-->功能定义-->自定义功能-->添加功能,为智能家居添加属性:灯的状态,标识符为LightStatus,数据类型为bool(布尔型),0为关闭,1为打开,读写类型为“读写
阿里云IoT套件测试5:管理多个设备(单个ESP32)图4

  • 同样的方法添加三个自定义功能:风扇状态、插座状态、红外状态,标识符分别为LightFanStatus、RelayStatus、PIRStatus,数据类型为bool(布尔型),0为关(无),1为开(有),读写类型为“读写”。添加完后显示如下:
阿里云IoT套件测试5:管理多个设备(单个ESP32)图1
说明:这里添加了红外,是准备后面做智能防盗用的。

4. 新增设备:
  • 在左侧导航栏选择设备管理->设备,单击新增设备,会自动生成一个设备。
阿里云IoT套件测试5:管理多个设备(单个ESP32)图5

  • 设备生成后会出现在设备列表中

阿里云IoT套件测试5:管理多个设备(单个ESP32)图6

  • 点击上图中右下角的“激活凭证”,可以查看凭证,将凭证复制并保存到安全的位置,我们在后面的程序中会用到这些凭证
阿里云IoT套件测试5:管理多个设备(单个ESP32)图7

5. 新增可视化应用:
  • 在左侧导航栏选择推荐->移动应用开发,点击右方新增可视化应用,根据实际填写信息:
阿里云IoT套件测试5:管理多个设备(单个ESP32)图8

  • APP用户界面设置


  • 为设备配置数据


  • 在编辑中,点击首页模块-->列表页-->新增页面入口-->配置跳转链接,选择跳转到“智能家居管理”页面,修改标题和描述,上传自定义图片作为图标。
阿里云IoT套件测试5:管理多个设备(单个ESP32)图9

  • 点击右上角构建-->Android构建,将APP打包并安装到手机上并运行。第一次运行需要输入用户名和密码,这个是在账号中设置:
阿里云IoT套件测试5:管理多个设备(单个ESP32)图10

二、线路连接
阿里云IoT套件测试5:管理多个设备(单个ESP32)图11
LED灯接D2,风扇接D4,继电器接D6

三、程序编写
选择样例文件中的任何一个进行改写。
改写的部分代码如下:

1. 定义3个设备的引脚
[mw_shl_code=cpp,true]#define LIGHT  D2
#define FAN D4
#define RELAY D6[/mw_shl_code]
2. 定义标识符对应每种物理设备
[mw_shl_code=cpp,true]/*需要操作的产品标识符*/
String Identifier_Light = "LightStatus";
String Identifier_Fan = "FanStatus";
String Identifier_Relay = "RelayStatus";[/mw_shl_code]
3. 三个设备的开关代码
[mw_shl_code=cpp,true]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);
}[/mw_shl_code]
4. 修改回调函数
[mw_shl_code=cpp,true]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"][Identifier_Light];
  if(LightStatus == 1){
    openLight();
  }else{
    closeLight();
  }
  Serial.println("LightStatus:");
  Serial.println(LightStatus);
  
  const uint16_t FanStatus = root["params"][Identifier_Fan];
  if(FanStatus == 1){
    openFan();
  }else{
    closeFan();
  }
  Serial.println("FanStatus:");
  Serial.println(FanStatus);
  
  const uint16_t RelayStatus = root["params"][Identifier_Relay];
  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[tempMseg_Light.length()];
  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[tempMseg_Fan.length()];
  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[tempMseg_Relay.length()];
  strcpy(sendMseg_Relay,tempMseg_Relay.c_str());
  client.publish(pubTopic,sendMseg_Relay);[/mw_shl_code]
5. 定义引脚输出模式
[mw_shl_code=cpp,true]  pinMode(LIGHT,OUTPUT);
  pinMode(FAN,OUTPUT);
  pinMode(RELAY,OUTPUT); [/mw_shl_code]

四、调试程序

在调试中会遇到很多错误,如编译错误,程序运行错误。编译错误比较好解决,程序运行错误就要仔细分析串口监视器的返回内容,查找错误原因。在DF给的KIT中有许多.h头文件,根据需要打开相应的头文件了解程序运行的机制,还可以根据错误返回信息查找头文件来找出错误原因。
阿里云IoT套件测试5:管理多个设备(单个ESP32)图13
这个错误可以看到是MQTT连接失败,rc=4,返回的错误代码是4,我们找到相应的头文件PubSubClient.h可以查看到返回代码4是凭证错误,也就是三联码:ProductKey,DeviceName和DeviceSecret中的一个或几个有问题,检查后发现确实有一个凭证是错的,修改后问题解决。从下图中可以看到返回代码所代表的含义:
阿里云IoT套件测试5:管理多个设备(单个ESP32)图12

MQTT连接超时 -4
MQTT连接丢失 -3
MQTT连接失败 -2
MQTT断开连接 -1
MQTT连接成功 0
MQTT连接协议错误 1
MQTT连接ClientId错误 2
MQTT连接不可用 3
MQTT连接凭证错误 4
MQTT连接未授权 5

进行在线模拟的时候发现虽然在平台上可以控制设备的开关,但是手机上只能控制灯的开关,其他两种设备无法控制,查看串口监视器发现回调函数返回的Topic是有问题的。这是因为我们在一个产品下有多个设备,这样造成Topic不唯一,解决方法是在一个产品下只增加一个设备,产品下面添加自定义功能,每个功能对应设备的一种属性。
阿里云IoT套件测试5:管理多个设备(单个ESP32)图15

按照上面的想法修改程序后,在线模拟以及手机都可以控制所有设备,但是奇怪的是在打开某个设备时也会关闭其他开着的设备,如下面的视频演示:


与@绿水无痕进行探讨,他分析原因应该是“LightStatus是ArduinoJson中的子对象,没法用containSkey判断,只能用root["params"]["LightStatus"]获取,如果没有的话自动返回NULL,会与Bool类型的0冲突,导致无法判断”,即每次发送命令时只有一个设备的状态被发送,另外两个没有发送,而没有发送系统返回null,即0。所以应付对其他两个设备采取关闭的操作,导致上面所演示的错误发生。

解决方法是引入3个变量来读取返回的topic里是否包含了对应的标识符,如果没有包含就不执行任何操作(保持设备原有的状态);如果包含再判断返回来的值是1还是0,1就执行开启操作,0执行关闭操作。

修改回调函数:
[mw_shl_code=cpp,true]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"][Identifier_Light];
  const char* val2=root["params"][Identifier_Fan];
  const char* val3=root ["params"][Identifier_Relay];
  if(val1!=NULL)
  {
      Serial.println("++++++++++");
      const uint16_t LightStatus = root["params"][Identifier_Light];
      if (LightStatus == 1){
        openLight();
      }else{
        closeLight();  
        }
  String tempMseg = "{\"id\":"+ClientId+",\"params\":{\""+Identifier_Light+"\":"+(String)LightStatus+"},\"method\":\"thing.event.property.post\"}";
  char sendMseg[tempMseg.length()];
  strcpy(sendMseg,tempMseg.c_str());
  client.publish(pubTopic,sendMseg);
  }

  if(val2!=NULL)
  {
      Serial. println("----------");
      const uint16_t FanStatus = root["params"][Identifier_Fan];
      if (FanStatus == 1){
        openFan();
      }else{
        closeFan();  
        }  
  String tempMseg = "{\"id\":"+ClientId+",\"params\":{\""+Identifier_Fan+"\":"+(String)FanStatus+"},\"method\":\"thing.event.property.post\"}";
  char sendMseg[tempMseg.length()];
  strcpy(sendMseg,tempMseg.c_str());
  client.publish(pubTopic,sendMseg);           
  }

  if(val3!=NULL)
  {
      Serial. println("**********");
      const uint16_t RelayStatus = root["params"][Identifier_Relay];
      if (RelayStatus == 1){
        openRelay();
      }else{
        closeRelay();  
        }
  String tempMseg = "{\"id\":"+ClientId+",\"params\":{\""+Identifier_Relay+"\":"+(String)RelayStatus+"},\"method\":\"thing.event.property.post\"}";
  char sendMseg[tempMseg.length()];
  strcpy(sendMseg,tempMseg.c_str());
  client.publish(pubTopic,sendMseg);  
  }
}[/mw_shl_code]

程序修改完后运行,执行开启设备命令后串口监视器如下:
阿里云IoT套件测试5:管理多个设备(单个ESP32)图14

提升
在现实家居生活中有多个设备,单用一个ESP32肯定是不够也不方便,可以用多个ESP32,每个ESP32下可以挂多个设备。每个ESP32程序结构大致一样,不需要做过多的修改。只需要在阿里云的项目中增加产品,每个产品对应一个ESP32即可。手头有FireBeetle,掌控板,Linkit7697,还有Obloq,ArduinoUNO WiFi,等有机会试验一下。


无力吐槽一下阿里云。这个平台目前还是不太稳定,总会有奇奇怪怪的问题出现:删除的设备在组件属性中还是会出现,即使更新并保存,过一会儿又恢复已删除的东西;打包的app运行后点进页面出现“访问发现错误,当前版本没有打包”,重新开发一个新的app才运行正常;23号更坑,打包的app一运行就闪退,换电脑,换手机,换网络,重新构建...试了各种方法仍然不行,24号早上才得知是阿里云本身出了问题,最近更新出了bug,这是得多大的bug,太不应该了啊!另外已经在线模拟或使用的app不能删除也是一个大槽点,太不方便了。
倒是DF的器材性能很稳定,尤其主控板连接很迅速,响应也很快。赞一个!!

Ali_MultiDevices.zip

2.09 KB, 下载次数: 26

售价: 10 创造力  [记录]

INO文件

安卓机器人  中级技神

发表于 2019-5-25 11:35:51

赞!很认真、好详细,收藏
回复

使用道具 举报

szjuliet  版主
 楼主|

发表于 2019-5-25 13:50:31

安卓机器人 发表于 2019-5-25 11:35
赞!很认真、好详细,收藏

回复

使用道具 举报

senghu  初级技师

发表于 2019-5-25 21:23:14

好棒!解决了我这几天的困惑
回复

使用道具 举报

DFHkDpTTc1I  学徒

发表于 2020-2-9 10:54:34

好棒!学习中!
回复

使用道具 举报

阿水阿难  见习技师

发表于 2020-3-4 15:51:10

楼主,有完整的样例代码吗?
回复

使用道具 举报

DFHk-0ykaN8  见习技师

发表于 2020-3-4 16:04:05

楼主,感谢真的香!
回复

使用道具 举报

阿水阿难  见习技师

发表于 2020-3-5 19:35:44

楼主,上报的topic要修改吗?我在做一个土壤湿度传感器和继电器的东西,传感器topic只要上报就行了,但继电器要上报和订阅,这俩个怎么改啊?能教教吗?
回复

使用道具 举报

柳春晓  高级技师

发表于 2020-5-13 17:03:50

我也被这个问题折磨了很久,我一直以为是我云端创建产品的时候有问题才导致下发的时候params里只有一个状态
回复

使用道具 举报

PIEMON  学徒

发表于 2020-12-1 10:35:37

版主您好,如果是土壤湿度、温湿度检测加灯光、喇叭实现报警检测,如何实现共联
回复

使用道具 举报

大叔1  学徒

发表于 2021-3-3 16:54:07

本帖最后由 大叔1 于 2021-3-3 20:13 编辑

我按代码里填了三元组,编译成功后,上传到 ESP32 CAM 主板, 但是一直连不上MQTT:

阿里云IoT套件测试5:管理多个设备(单个ESP32)图1


到底是什么原因?
回复

使用道具 举报

大叔1  学徒

发表于 2021-3-3 22:14:18

本帖最后由 大叔1 于 2021-3-3 23:07 编辑

阿里云IOT studio 做的界面里还有个很奇怪的问题,就是无论什么开关,点一下,会发送两次消息,一次开,一次关,同时发送了,而且开关会弹回原位
回复

使用道具 举报

狮山闲人  见习技师

发表于 2022-4-23 16:04:23

我今年才买了一套阿里云套件,教材里几个例子,单个设备调试都正常,也遇到楼主的问题,但想把多个设备一起上云,程序不好修改。楼主的思路值得借鉴。已经过了二三年了,不知道有没有新的比较简洁的办法出现?
回复

使用道具 举报

狮山闲人  见习技师

发表于 2022-4-27 11:35:34

大叔1 发表于 2021-3-3 22:14
阿里云IOT studio 做的界面里还有个很奇怪的问题,就是无论什么开关,点一下,会发送两次消息,一次开,一 ...

我也出现了这个现象,而不是开关二次,而是重复不停的开关。不清楚原因.
回复

使用道具 举报

柳城科创  学徒

发表于 2022-6-14 11:27:23

赞!很认真、好详细,收藏
回复

使用道具 举报

helloworld_dfro  学徒

发表于 2022-10-18 00:06:03

大叔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的工程师@绿水无痕来修改的。
回复

使用道具 举报

helloworld_dfro  学徒

发表于 2022-10-18 00:07:51

狮山闲人 发表于 2022-4-27 11:35
我也出现了这个现象,而不是开关二次,而是重复不停的开关。不清楚原因. ...

我也遇到了和你同样的问题。今天和阿里云IOT的一个工程师沟通了半天,结果发现是,当阿里云平台发送指令(类似于 /sys/hrkchWpcCEv/LED/thing/service/property/set)后,原本我们的esp32应该发送一个且仅有一个reply给云平台的。再DFRobot提供的程序里面,是通过callback()函数的。
但是不知道什么原因,这个callback函数被不停的调用,多次上报给云平台,而因为设备端短时间上报次数过多导致了云平台的限流,所以没有上报成功,从而web项目报错, 为保护设备,自动恢复到开始的状态。
阿里云IoT的工程建议我,修改下设备端上报逻辑。
我自己通过调看串口,的确发现短时间内,callback函数被调用了50多次。

但是我们这边如果需要修改callback()函数的化,工作量不是一般的大。只能请DFRobot的工程师@绿水无痕来修改的。
回复

使用道具 举报

helloworld_dfro  学徒

发表于 2022-10-30 09:39:22

大叔1 发表于 2021-3-3 22:14
阿里云IOT studio 做的界面里还有个很奇怪的问题,就是无论什么开关,点一下,会发送两次消息,一次开,一 ...

这个是代码callback函数问题的。
回复

使用道具 举报

helloworld_dfro  学徒

发表于 2022-10-30 09:42:19

helloworld_dfro 发表于 2022-10-18 00:07
我也遇到了和你同样的问题。今天和阿里云IOT的一个工程师沟通了半天,结果发现是,当阿里云平台发送指令 ...

和DFRobot的工程师沟通了半天,并没有解决。后面找了一个计算机的高手。修改了回调函数,解决了问题。不得不吐槽一下下DFRobot的工程师,这么大的bug放在那里几年了,还没有解决,一直说是他们的套件没有问题,验证了无数次。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

为本项目制作心愿单
购买心愿单
心愿单 编辑
[[wsData.name]]

硬件清单

  • [[d.name]]
btnicon
我也要做!
点击进入购买页面
上海智位机器人股份有限公司 沪ICP备09038501号-4

© 2013-2024 Comsenz Inc. Powered by Discuz! X3.4 Licensed

mail