搜索
bottom↓
回复: 0

《I.MX6U嵌入式Qt开发指南》第二十四章 智能家居物联网项目

[复制链接]

出0入234汤圆

发表于 2021-7-20 12:27:43 | 显示全部楼层 |阅读模式
本帖最后由 正点原子 于 2021-8-11 12:23 编辑

1)实验平台:正点原子i.MX6ULL Linux阿尔法开发板
2)  章节摘自【正点原子】《I.MX6U嵌入式Qt开发指南》
3)购买链接:https://item.taobao.com/item.htm?&id=603672744434
4)全套实验源码+手册+视频下载地址:http://www.openedv.com/docs/boards/arm-linux/zdyz-i.mx6ull.html
5)正点原子官方B站:https://space.bilibili.com/394620890
6)
正点原子Linux技术交流群:1027879335 1.png

2.jpg


3.png


第二十四章 智能家居物联网项目

       本章介绍使用Qt开发智能家居中的一个物联应用。简单直白的说就是通过云服务器远程控制设备(与设备通信等)。本章可以直接做毕设,是毕设物联网项目的一大福音!本章将实现远程点亮开发板LED作为一个项目实例。
       在生活中,我们可能使用过WIFI智能插座这款产品。智能家居中常用来控制电器开关。比如远程开热水器,远程打开空调,窗帘等等。这些WIFI智能插座的原理就是将WIFI插座注册到云服务器上,然后通过手机的APP来访问云服务器,然后控制WIFI插座。嗯,原理我们懂了。本章就是模仿?不,或者说是直接开发这样的一个项目。包括WIFI连网,注册到云服务器上,编写Qt UI通过网络来与云服务器通信,然后再下发指令到这个连网的设备,与之通信。恩本章的流程就是这些,带着这个项目流程,然后一步步看编者是如何通过Qt实现的吧!
       本章需要读者对正点原子的wifi模块ATK-ESP8266串口转WIFI有一定的了解。正点原子提供了STM32与ESP8266模块通信的例程,如果学习过STM32与ESP8266模块通信的例程的内容,理解起来则会更容易。建议参考文档:ATK-ESP8266 WIFI用户手册_V1.x.pdf及原子云平台API文档V1.2.pdf。
24.1 项目硬件
必备硬件
       本章需要正点原子ATK-ESP8266串口转WIFI模块(免费接入原子云)。另外还需要加上一个USB-TTL模块,外加一根T口USB连接线,可接入PC(电脑)调试。
第二十四章 智能家居物联网项目637.png

        T口连接线连接USB-TTL模块再连接ATK-ESP8266模块到PC(电脑),用于在PC(电脑)上直接使用串口调试/测试此模块。
第二十四章 智能家居物联网项目706.png

        这里可能会有部分读者会问是否可以用其他WIFI模块,比如正点原子Linux USB WIFI模块,或者直接使用开发板联网接入到云设备呢?答案是不可以的!只有一些特定的设备,需要刷能接入云的固件才能接入服务器。
        又有读者问是否可以直接购买一些WIFI插座来使用呢?答案是不可以的!因为这些WIFI插座也是一样,也是刷了固件,而且这些设备是连接到啊里云的。需要与特定的手机APP结合使用才能注册到啊里云服务器。也就是不能拿来二次开发了!
        恰好我们正点原子有物联网模块ESP8266与4G DTU模块。本章主要讲解如何正点原子的串口转WIFI ESP8266模块来开发一个物联网的项目应用!
可选配件
        本项目可以在正点原子I.MX6U ALPHA | mini开发板直接使用,下图为正点原子ALPHA开发板在底板上预留的ATK MODULE接口(串口接口)上的接法图。
第二十四章 智能家居物联网项目1094.png

24.2 测试WIFI模块
       要实现物联网功能,需要使用正点原子的ATK-ESP8266 WIFI模块。首先我们先测试正点原子的ATK-ESP8266 WIFI模块是否正常使用,及能否正常连接原子云服务器。ATK-ESP8266 WIFI用户手册_V1.3.pdf手册第2.2.3小节硬件连接,将ATK-ESP8266 WIFI模块连接到PC(电脑),再查阅ATK-ESP8266 WIFI用户手册_V1.3.pdf手册的第2.2.9.1小节,注册原子云帐号后,添加设备,然后按2.2.9.1小节测试连接本地WIFI(自己的路由器发出的WIFI,注意不要使用中文名或者有空格的WIFI,确保路由器的WIFI能上网!)。请自行完成并成功连接到原子云。
24.3 WIFI模块连接原子云
       请先测试个人的ATK-ESP8266模块是否正常使用,及正常连接云。原子云的设备需要先分好组,各个设备命名如下。注意需要和编者命名的名字一样,也就是至少有一个分组及一个名字为“客厅灯”的设备,并记住编号及密码(密码由云生成,默认“12345678”)。
第二十四章 智能家居物联网项目1564.png

       源码路径4/01_smarthome/esp8266/esp8266.cpp,内容如下。默认使用的WIFI模块串口通信波特率为115200。在Ubuntu上设试WIFI模块时,一般串口名称为“ttyUSB0”,默认是没有权限访问这个/dev/ttyUSB0设备的。所以我们需要使用下面的指令修改权限。(注意:本例适用于I.MX6U Linux开发板与Ubuntu,Windows不作测试!)。
  1. sudo chmod 777 /dev/ttyUSB0
复制代码

       修改完成后查看源码内容如下。先看源码,不要急着运行!
  1.    /******************************************************************
  2.     Copyright © Deng Zhimao Co., Ltd. 1990-2021. All rights reserved.
  3.     * @projectName   esp8266
  4.     * @brief         esp8266.cpp
  5.     * @author        Deng Zhimao
  6.     * @email         <a href="mailto:1252699831@qq.com" target="_blank">1252699831@qq.com</a>
  7.     * @net            <a href="www.openedv.com" target="_blank">www.openedv.com</a>
  8.     * @date           2021-05-27
  9.     *******************************************************************/
  10. 1   #include "esp8266.h"
  11. 2   #include <unistd.h>
  12. 3   #include <QDebug>
  13. 4  
  14. 5   Esp82266::Esp82266(QWidget *parent)
  15. 6   {
  16. 7       Q_UNUSED(parent)
  17. 8       /* 串口对象,用于与Esp8266模块通信 */
  18. 9       serialPort = new QSerialPort(this);
  19. 10
  20. 11      /* 定时器对象,用于定时发送设备在线的心跳包 */
  21. 12      timer = new QTimer();
  22. 13
  23. 14      /* led对象,用于串口接收到云发过来的数据,然后控制板子的LED */
  24. 15      led = new Led(this);
  25. 16
  26. 17      /* 设置串口名 */
  27. 18  #if __arm__
  28. 19      serialPort->setPortName("ttymxc2");
  29. 20  #else
  30. 21      serialPort->setPortName("ttyUSB0");
  31. 22  #endif
  32. 23
  33. 24      /* 设置波特率 */
  34. 25      serialPort->setBaudRate(115200);
  35. 26
  36. 27      /* 设置数据位数 */
  37. 28      serialPort->setDataBits(QSerialPort::Data8);
  38. 29
  39. 30      /* 设置奇偶校验 */
  40. 31      serialPort->setParity(QSerialPort::NoParity);
  41. 32
  42. 33      /* 设置停止位 */
  43. 34      serialPort->setStopBits(QSerialPort::OneStop);
  44. 35
  45. 36      /* 设置流控制 */
  46. 37      serialPort->setFlowControl(QSerialPort::NoFlowControl);
  47. 38
  48. 39      if (!serialPort->open(QIODevice::ReadWrite))
  49. 40          qDebug()<<"串口无法打开!可能正在被使用!"<<endl;
  50. 41      else {
  51. 42          qDebug()<<"串口打开成功!"<<endl;
  52. 43      }
  53. 44
  54. 45      /* 开始连接云 */
  55. 46      connectToClound();
  56. 47
  57. 48      connect(serialPort, SIGNAL(readyRead()),
  58. 49              this, SLOT(serialPortReadyRead()));
  59. 50
  60. 51      connect(timer, SIGNAL(timeout()),
  61. 52              this, SLOT(onTimerTimeOut()));
  62. 53  }
  63. 54
  64. 55  void Esp82266::serialPortReadyRead()
  65. 56  {
  66. 57      /* 接收缓冲区中读取数据 */
  67. 58      QByteArray buf = serialPort->readAll();
  68. 59
  69. 60      QString temp = QString(buf);
  70. 61      readData.append(temp);
  71. 62      qDebug()<<temp<<endl;
  72. 63
  73. 64      if  (readData.contains("ready")) {
  74. 65          /* 如果复位成功 */
  75. 66          sendCmdToEsp8266("AT+CWMODE=1");
  76. 67          readData.clear();
  77. 68      }
  78. 69
  79. 70      if (readData.contains("OK") && readData.contains("AT+CWMODE")) {
  80. 71          qDebug()<<"设置STA模式成功"<<endl;
  81. 72          sendCmdToEsp8266("AT+CWJAP="ALIENTEK-YF","15902020353"");
  82. 73          qDebug()<<"开始连接WIFI"<<endl;
  83. 74          readData.clear();
  84. 75      }
  85. 76
  86. 77      if  (temp.contains("WIFI GOT IP")) {
  87. 78          qDebug()<<"连接WIFI成功"<<endl;
  88. 79          sleep(2);
  89. 80          /* 原子云的设备号及密码 */
  90. 81          sendCmdToEsp8266("AT+ATKCLDSTA="02314701717851074890","12345678"");
  91. 82          qDebug()<<"开始连接原子云请等待"<<endl;
  92. 83      }
  93. 84
  94. 85      if (temp.contains("CLOUD CONNECTED")) {
  95. 86          qDebug()<<"连接原子云成功"<<endl;
  96. 87          sleep(2);
  97. 88          /* 15s就发送一次心跳包 */
  98. 89          timer->start(15000);
  99. 90      }
  100. 91
  101. 92      if (temp == "开")
  102. 93          led->setLedState(true);
  103. 94      else if (temp == "关")
  104. 95          led->setLedState(false);
  105. 96
  106. 97  }
  107. 98
  108. 99  Esp82266::~Esp82266()
  109. 100 {
  110. 101     serialPort->close();
  111. 102     delete timer;
  112. 103     timer = nullptr;
  113. 104 }
  114. 105
  115. 106 void Esp82266::sendCmdToEsp8266(QString cmd)
  116. 107 {
  117. 108     cmd = cmd + "\r\n";
  118. 109
  119. 110     QByteArray data = cmd.toUtf8();
  120. 111     serialPort->write(data);
  121. 112 }
  122. 113
  123. 114 void Esp82266::connectToClound()
  124. 115 {
  125. 116     /* 重启模块,注意若已经连接上原子云,
  126. 117      * 需要重新上电或者短接RST脚来复位模块 */
  127. 118     sendCmdToEsp8266("AT+RST");
  128. 119 }
  129. 120
  130. 121 void Esp82266::sleep(int second)
  131. 122 {
  132. 123     usleep(second * 1000000);
  133. 124 }
  134. 125
  135. 126 void Esp82266::sendTextMessage(QString message)
  136. 127 {
  137. 128     serialPort->write(message.toLatin1());
  138. 129 }
  139. 130
  140. 131 void Esp82266::onTimerTimeOut()
  141. 132 {
  142. 133     sendTextMessage("online");
  143. 134     qDebug()<<"发送设备在线心跳包"<<endl;
  144. 135 }
复制代码

        查看源码,我们可以知道以下重要内容。
        第72行,“ALIENTEK-YF”是编者处的路由器发出的WIFI热点名称,密码是“15902020353”。请修改为自己的WIFI名称及连接密码!没有路由器,用手机开热点也可以。
        第81行,是原子云上的“客厅灯”设备的编号“02314701717851074890”,及密码“12345678”,请填写自己的设备编号,及密码,注意这个设备需要命名为“客厅灯”,后面程序需要使用到它!按照上面的程序就容易地可以连接上原子云了。也无需熟读ATK-ESP8266 WIFI用户手册_V1.3.pdf手册。
24.4 智能家居物联UI界面开发
       项目路径为4/01_smarthome/01_smarthome/01_smarthome.pro,先看项目界面。项目界面如下,采用暗黑主题设计,结合黄色作为亮色,让用户一目了然。界面编者从一些智能家居界面中找到灵感的,编写设计完成的效果不错!请自行查阅源码,掌握了本教程前面第七章的内容,就可以理解这个界面是如何设计的。
第二十四章 智能家居物联网项目6049.png

24.5 原子云API接口
       我们想要与原子云通信,那么必须先了解原子云平台的API接口。请参阅原子云平台API文档V1.2.pdf文档。原子云平台API写的非常详细了,请自行翻阅。需要我们从原子云平台了解原子云API的通信流程。
       下图是原子云平台API的使用流程图。
第二十四章 智能家居物联网项目6185.png

        我们写Qt应用就应该重点放在HTTPS与WebSocket方向上。查阅原子云平台API可以知道,下面是重点!一些帐号信息,与设备信息是通过HTTPS协议接口获取的,通信用WebSocket协议接口。那么我们就按原子云平台的协议流程编写应用程序。
        源码路径为4/01_smarthome/webapi/webapi.cpp。内容如下。
  1. <font size="4"> </font><font size="2">  /******************************************************************
  2.     Copyright © Deng Zhimao Co., Ltd. 1990-2021. All rights reserved.
  3.     * @projectName   webapi
  4.     * @brief         webapi.cpp
  5.     * @author        Deng Zhimao
  6.     * @email         <a href="mailto:1252699831@qq.com" target="_blank" style="">1252699831@qq.com</a>
  7.     * @net            <a href="www.openedv.com" target="_blank" style="">www.openedv.com</a>
  8.     * @date           2021-05-27
  9.     *******************************************************************/
  10. 1   #include "webapi.h"
  11. 2   #include <QUuid>
  12. 3   #include <QRegularExpression>
  13. 4  
  14. 5   Webapi::Webapi(QObject *parent)
  15. 6   {
  16. 7       this->setParent(parent);
  17. 8       /* 数组清空 */
  18. 9       groupID.clear();
  19. 10      deviceID.clear();
  20. 11      deviceNumber.clear();
  21. 12
  22. 13      timer = new QTimer();
  23. 14      connect(timer, SIGNAL(timeout()), this, SLOT(onTimerTimeOut()));
  24. 15
  25. 16      networkAccessManager = new QNetworkAccessManager(this);
  26. 17
  27. 18      orgURL = "https://cloud.alientek.com/api/orgs";
  28. 19      /* 请填写自己的token信息!!! */
  29. 20      api_token = "bf591984c8fa417584d18f6328e0ef73";
  30. 21
  31. 22      /* 获取账号机构列表 */
  32. 23      getOrgURL();
  33. 24
  34. 25      QUuid uuid = QUuid::createUuid();
  35. 26      random_token = uuid.toString();
  36. 27
  37. 28      webSocket = new QWebSocket();
  38. 29      /* 需要加一些安全配置才能访问https */
  39. 30      QSslConfiguration config;
  40. 31      config.setPeerVerifyMode(QSslSocket::VerifyNone);
  41. 32      config.setProtocol(QSsl::TlsV1SslV3);
  42. 33      webSocket->setSslConfiguration(config);
  43. 34
  44. 35      connect(webSocket, SIGNAL(connected()),
  45. 36              this, SLOT(webSocketConnected()));
  46. 37      connect(webSocket, SIGNAL(binaryMessageReceived(QByteArray)),
  47. 38              this, SLOT(onBinaryMessageReceived(QByteArray)));
  48. 39  }
  49. 40
  50. 41  Webapi::~Webapi()
  51. 42  {
  52. 43      delete timer;
  53. 44      delete webSocket;
  54. 45      webSocket = nullptr;
  55. 46  }
  56. 47
  57. 48  void Webapi::getOrgURL()
  58. 49  {
  59. 50      getDataFromWeb(QUrl(orgURL));
  60. 51  }
  61. 52
  62. 53  /* 获取设备分组列表 */
  63. 54  void Webapi::getGroupListUrl()
  64. 55  {
  65. 56      getDataFromWeb(QUrl(groupListUrl));
  66. 57  }
  67. 58
  68. 59  /* 获取设备的信息 */
  69. 60  void Webapi::getDevOfGroupUrl()
  70. 61  {
  71. 62      getDataFromWeb(QUrl(devOfGroupUrl));
  72. 63  }
  73. 64
  74. 65  /* 获取设备连接状态  */
  75. 66  void Webapi::getConStateUrl()
  76. 67  {
  77. 68      getDataFromWeb(QUrl(conStateUrl));
  78. 69  }
  79. 70
  80. 71  /* 从云服务器获取数据 */
  81. 72  void Webapi::getDataFromWeb(QUrl url)
  82. 73  {
  83. 74      /* 网络请求 */
  84. 75      QNetworkRequest networkRequest;
  85. 76
  86. 77      /* 需要加一些安全配置才能访问https */
  87. 78      QSslConfiguration config;
  88. 79      config.setPeerVerifyMode(QSslSocket::VerifyNone);
  89. 80      config.setProtocol(QSsl::TlsV1SslV3);
  90. 81      networkRequest.setSslConfiguration(config);
  91. 82
  92. 83      /* 设置访问的地址 */
  93. 84      networkRequest.setUrl(url);
  94. 85
  95. 86      /* 网络响应 */
  96. 87      networkRequest.setHeader(QNetworkRequest::ContentTypeHeader,
  97. 88                               "application/json;charset=UTF-8");
  98. 89
  99. 90      /* 参数二为原子云帐号的token信息,填写自己的 */
  100. 91      networkRequest.setRawHeader("token", api_token.toLatin1());
  101. 92
  102. 93      QNetworkReply *newReply =
  103. 94              networkAccessManager->get(networkRequest);
  104. 95
  105. 96      connect(newReply, SIGNAL(finished()),
  106. 97              this, SLOT(replyFinished()));
  107. 98      connect(newReply, SIGNAL(readyRead()),
  108. 99              this, SLOT(readyReadData()));
  109. 100
  110. 101 }
  111. 102 void Webapi::replyFinished()
  112. 103 {
  113. 104     QNetworkReply *reply = (QNetworkReply *)sender();
  114. 105
  115. 106     if (reply->url() == QUrl(orgURL)) {
  116. 107         /* 设备分组列表ID */
  117. 108         getID(dataString, reply);
  118. 109     }
  119. 110
  120. 111     if (reply->url() == QUrl(groupListUrl)) {
  121. 112         /* 列表ID */
  122. 113         getID(dataString, reply);
  123. 114
  124. 115         /* 获取到组ID再开启定时器 */
  125. 116         if (!timer->isActive())
  126. 117             timer->start(2000);
  127. 118     }
  128. 119
  129. 120     /* 设备的信息 */
  130. 121     if (reply->url() == QUrl(devOfGroupUrl)) {
  131. 122         getID(dataString, reply);
  132. 123         getNumber(dataString);
  133. 124         getName(dataString);
  134. 125     }
  135. 126
  136. 127     /* 设备的连接状态 */
  137. 128     if (reply->url() == QUrl(conStateUrl)) {
  138. 129         getConnectState(dataString);
  139. 130     }
  140. 131
  141. 132     reply->deleteLater();
  142. 133     reply = nullptr;
  143. 134 }
  144. 135 void Webapi::readyReadData()
  145. 136 {
  146. 137     QNetworkReply *reply = (QNetworkReply *)sender();
  147. 138     QByteArray data = reply->readAll();
  148. 139     dataString =  QString(data);
  149. 140     qDebug()<<dataString<<endl;
  150. 141 }
  151. 142
  152. 143 /* 获取ID,包括分组id,设备id */
  153. 144 void Webapi::getID(QString data, QNetworkReply *reply)
  154. 145 {
  155. 146     /* 多个匹配,因为可能有多个合适的字段 */
  156. 147     QRegularExpression pattern(""id":(\\d+)");
  157. 148
  158. 149     QRegularExpressionMatchIterator i = pattern.globalMatch(data);
  159. 150     while (i.hasNext()) {
  160. 151         QRegularExpressionMatch match = i.next();
  161. 152         if (match.hasMatch()) {
  162. 153             if (reply->url() == QUrl(orgURL)) {
  163. 154                 org_id =  match.captured(1);
  164. 155                 groupListUrl = "https://cloud.alientek.com/api/orgs/"
  165. 156                         + org_id + "/grouplist";
  166. 157                 getGroupListUrl();
  167. 158                 /* Socket连接 */
  168. 159                 webSocket->open(QUrl(QString("wss://cloud.alientek.com/connection/%1/org/%2?token=%3")
  169. 160                                      .arg(api_token).arg(org_id).arg(random_token)));
  170. 161             }
  171. 162
  172. 163             if (reply->url() == QUrl(groupListUrl)) {
  173. 164                 group_id = match.captured(1);
  174. 165                 /* 存储组ID,再由定时器根据组的ID获取设备信息 */
  175. 166                 groupID.append(group_id);
  176. 167                 qDebug()<<"组ID:"<<group_id<<endl;
  177. 168
  178. 169             }
  179. 170
  180. 171             if (reply->url() == QUrl(devOfGroupUrl)) {
  181. 172                 device_id = match.captured(1);
  182. 173                 /* 存储设备ID,再由定时器根据设备的ID获取连接状态 */
  183. 174                 deviceID.append(device_id);
  184. 175                 qDebug()<<"设备ID:"<<device_id<<endl;
  185. 176             }
  186. 177         }
  187. 178     }
  188. 179 }
  189. 180
  190. 181 void Webapi::getNumber(QString data)
  191. 182 {
  192. 183     QRegularExpression pattern(""number":"(\\d+)"");
  193. 184
  194. 185     QRegularExpressionMatchIterator i = pattern.globalMatch(data);
  195. 186     while (i.hasNext()) {
  196. 187         QRegularExpressionMatch match = i.next();
  197. 188         if (match.hasMatch()) {
  198. 189             device_number = match.captured(1);
  199. 190             deviceNumber.append(device_number);
  200. 191             qDebug()<<"设备编号:"<<device_number<<endl;
  201. 192         }
  202. 193     }
  203. 194 }
  204. 195
  205. 196 void Webapi::getName(QString data)
  206. 197 {
  207. 198     /* 匹配中文字符,设备起名需要为中文 */
  208. 199     QRegularExpression pattern(""name":"([\u4e00-\u9fa5]*)");
  209. 200
  210. 201     QRegularExpressionMatchIterator i = pattern.globalMatch(data);
  211. 202     while (i.hasNext()) {
  212. 203         QRegularExpressionMatch match = i.next();
  213. 204         if (match.hasMatch()) {
  214. 205             device_name = match.captured(1);
  215. 206             deviceName.append(device_name);
  216. 207             qDebug()<<"设备名称:"<<device_name<<endl;
  217. 208         }
  218. 209     }
  219. 210 }
  220. 211
  221. 212 /* 获取设备的连接状态 */
  222. 213 void Webapi::getConnectState(QString data)
  223. 214 {
  224. 215     QString pattern = ""data":"(\\S*)"";
  225. 216     QRegularExpression regularExpression(pattern);
  226. 217     QRegularExpressionMatch  match = regularExpression.match(data, 0);
  227. 218     if(match.hasMatch()) {
  228. 219         qDebug()<<"设备连接状态"<<match.captured(1);
  229. 220         deviceConnectState.append(match.captured(1));
  230. 221     }
  231. 222 }
  232. 223
  233. 224 void Webapi::webSocketConnected()
  234. 225 {
  235. 226     qDebug()<<"webSocket连接原子云成功"<<endl;
  236. 227 }
  237. 228
  238. 229 void Webapi::onBinaryMessageReceived(QByteArray str)
  239. 230 {
  240. 231
  241. 232     QString temp(str);
  242. 233     if (temp.contains("online")) {
  243. 234         for (int i = 0; i < deviceNumber.count() ; i++) {
  244. 235             if (temp.contains(deviceNumber)) {
  245. 236                 /* 发送如客厅灯在线信号*/
  246. <span style="font-style: italic;"><span style="font-style: normal;">237                 emit deviceStateChanged(deviceName + "|在线");
  247. 238                 qDebug()<<deviceName</span><span style="font-style: normal;"> + "|在线"<<endl;
  248. 239                 break;
  249. 240             }
  250. 241         }
  251. 242     }
  252. 243 }
  253. 244
  254. 245 /* 延时函数 */
  255. 246 void Webapi::sleep(double second)
  256. 247 {
  257. 248     usleep(second * 1000000);
  258. 249 }
  259. 250
  260. 251 void Webapi::onTimerTimeOut()
  261. 252 {
  262. 253     static int i = 0;
  263. 254     if (i < groupID.count()) {
  264. 255         /* 获取分组下的设备列表 */
  265. 256         devOfGroupUrl = "https://cloud.alientek.com/api/orgs/"
  266. 257                 + org_id + "/groups/"
  267. 258                 + groupID</span><span style="font-style: normal;"> + "/devices";
  268. 259         dataString.clear();
  269. 260         getDevOfGroupUrl();
  270. 261     } else if (i >= groupID.count()
  271. 262                && i < groupID.count() + deviceID.count() ) {
  272. 263         timer->start(1000);
  273. 264         conStateUrl = "https://cloud.alientek.com/api/orgs/"
  274. 265                 + org_id + "/devicestate/"
  275. 266                 + deviceID[i - groupID.count()];
  276. 267         getConStateUrl();
  277. 268
  278. 269     } else {
  279. 270         /* 订阅设备的消息 */
  280. 271         for (int j = 0; j < deviceNumber.count(); j++) {
  281. 272             QByteArray cmd;
  282. 273             cmd[0] = 0x01;
  283. 274             sendCmd(deviceNumber[j], cmd);
  284. 275         }
  285. 276
  286. 277         timer->stop();
  287. 278     }
  288. 279
  289. 280     i++;
  290. 281 }
  291. 282
  292. 283 /* 订阅指定设备的消息,cmd = 0x01 */
  293. 284 void Webapi::sendCmd(QString number, QByteArray cmd)
  294. 285 {
  295. 286     QStringList list = number.split("");
  296. 287     for (int i = 0; i < list.count(); i++) {
  297. 288         if (!list</span><span style="font-style: normal;">.isEmpty()) {
  298. 289             cmd.append(list</span><span style="font-style: normal;">);
  299. 290         }
  300. 291     }
  301. 292
  302. 293     webSocket->sendBinaryMessage(cmd);
  303. 294 }
  304. 295
  305. 296 /* 发送消息到指定设备,cmd = 0x03 */
  306. 297 void Webapi::sendCmdMessage(QString number,
  307. 298                             QByteArray cmd, QString message)
  308. 299 {
  309. 300     QStringList list = number.split("");
  310. 301     for (int i = 0; i < list.count(); i++) {
  311. 302         if (!list</span><span style="font-style: normal;">.isEmpty()) {
  312. 303             cmd.append(list</span><span style="font-style: normal;">);
  313. 304         }
  314. 305     }
  315. 306
  316. 307     cmd.append(message);
  317. 308
  318. 309     webSocket->sendBinaryMessage(cmd);
  319. 310 }
  320. 311
  321. 312 void Webapi::whichDeviceNameSendCmd(QString name,
  322. 313                                     QString message) {
  323. 314
  324. 315     for (int i = 0; i < deviceName.count(); i++) {
  325. 316         if (name ==  deviceName</span><span style="font-style: normal;">) {
  326. 317             QByteArray cmd;
  327. 318             cmd[0] = 0x03;
  328. 319             sendCmdMessage(deviceNumber</span><span style="font-style: normal;">, cmd, message);
  329. 320             break;
  330. 321         }
  331. 322     }
  332. 323 }</span></span></font>
复制代码

        第20行,需要填写自己的原子云平台帐号api_token信息,请在原子云》帐号信息中查看!
        剩余的代码都按照原子云平台API文档编写,首先是通过网络请求networkRequest,访问需要访问的地址,然后通过网络回应对象newReply来接收网络回复的结果。结果是JSION格式的文本,编者使用正则表达式提取回复的内容,作为下一个地址的参数,如此反复,就可以将原子云服务器的帐号下的设备信息提取出来。
        第159行,提取出来的信息转交webSocket对象,让webSocket获取原子云平台的鉴权,就可以实现通信了。
        流程都是按照原子云平台API文档的走,剩下的就是webSocket通信了,与TCP,UDP的socket通信相似,这里就不多解释了,和第十一章的TCP/UDP Socket通信内容相似。重点是流程,再参考代码看。
24.6 物联网项目综合测试
        打开4/01_smarthome/01_smarthome/01_smarthome.pro项目,此项目为智能家居物联网UI界面控制端。
        打开4/01_smarthome/esp8266/esp8266.pro项目,此项目设备端(被控端)。
        打开上面两个项目如下。
第二十四章 智能家居物联网项目16869.png

        项目文件夹下内容解释:
        01_smarthome项目下:
webapi文件夹为原子云平台的应用程序,主要用来与原子云通信。
Headers文件夹为界面设计的头文件。
Sources文件夹为界面设计的源文件。
esp8266项目下:
led文件夹为I.MX6U开发板控制LED的接口程序。
Headers文件夹为esp8266通信的头文件。
Sources文件夹为esp8266通信的源文件(使用串口通信)。
24.6.1 Ubuntu上运行
       运行esp8266.pro项目前请确认ESP8266 WIFI模块已经使用USB-TTL模块通过T口USB连接线连接到Ubuntu上,并赋予/dev/ttyUSB0权限才能访问这个串口。运行后串口终端打印信息如下。若模块已经连接上原子云,重新运行程序时,需要将模块断电复位才能再次通信!
       注意,需要修改程序中的个人的本地WIFI帐号及密码,以及原子云上的设备的编号及密码,特别注意,这个设备必须在一个新增的分组下。设备在分组才能被后面的原子云API接口获取到。
       运行esp8266.pro项目,Qt Creator的应用程序窗口输出如下。连接原子云成功后就会启用定时器,每15s向原子云服务器发送一次心跳包,webSocket应用程序收到后就会显示此设备在线。
第二十四章 智能家居物联网项目17425.png

        运行01_smarthome.pro项目前请确认原子云》帐号信息处的API TOKEN信息,请填写自己的原子云帐号API TOKEN。否则您将访问到编者的原子云帐号的设备信息。
         第二十四章 智能家居物联网项目17519.png


        点击“客厅”开关按钮,可以看到esp8266设备应用程序收到开关信息。这个信息是通过UI界面应用程序发送到原子云服务器,然后原子云服务器转发给WIFI模块设备的。
第二十四章 智能家居物联网项目17604.png

24.6.2 ALPHA/Mini开发板运行
       正点原子的ALPHA/Mini Linux开发板交运行上面的01_smarthome项目和esp8266项目后,运行的结果与Ubuntu上面的是一样的,注意,主控开发板需要先连网(通过网线|USB WIFI模块|4G模块|SDIO WIFI模块),可以将正点原子的ESP8266 WIFI插在同一块开发板上进行实验,这样,点击UI界面上的开关按钮,就相当于发信息到原子云上,原子云再转发给ESP8266 WIFI模块,进而控制开发板上的LED。这样就实现了将开发板实现连接到原子云里了。


阿莫论坛20周年了!感谢大家的支持与爱护!!

月入3000的是反美的。收入3万是亲美的。收入30万是移民美国的。收入300万是取得绿卡后回国,教唆那些3000来反美的!
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-4-26 11:10

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表