• 【正点原子Linux连载】第二十四章 智能家居物联网项目 摘自【正点原子】I.MX6U嵌入式Qt开发指南V1.0.2


    1)实验平台:正点原子阿尔法Linux开发板
    2)平台购买地址:https://item.taobao.com/item.htm?id=603672744434
    3)全套实验源码+手册+视频下载地址:http://www.openedv.com/thread-300792-1-1.html
    4)对正点原子Linux感兴趣的同学可以加群讨论:935446741

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

    本章介绍使用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(电脑)调试。
    在这里插入图片描述

    T口连接线连接USB-TTL模块再连接ATK-ESP8266模块到PC(电脑),用于在PC(电脑)上直接使用串口调试/测试此模块。
    
    • 1

    在这里插入图片描述

    这里可能会有部分读者会问是否可以用其他WIFI模块,比如正点原子Linux USB WIFI模块,或者直接使用开发板联网接入到云设备呢?答案是不可以的!只有一些特定的设备,需要刷能接入云的固件才能接入服务器。
    又有读者问是否可以直接购买一些WIFI插座来使用呢?答案是不可以的!因为这些WIFI插座也是一样,也是刷了固件,而且这些设备是连接到阿里云的。需要与特定的手机APP结合使用才能注册到阿里云服务器。也就是不能拿来二次开发了!
    恰好我们正点原子有物联网模块ESP8266与4G DTU模块。本章主要讲解如何通过正点原子的串口转WIFI ESP8266模块来开发一个物联网的项目应用!
    
    • 1
    • 2
    • 3

    可选配件
    本项目可以在正点原子I.MX6U ALPHA | mini开发板直接使用,下图为正点原子ALPHA开发板在底板上预留的ATK MODULE接口(串口接口)上的接法图。
    在这里插入图片描述

    特别提醒:如果反复实验不正确时,因为ATK-MODULE这个接口,KEY和LED脚如果有其他程序在使用,那么很可能会影响ESP8266模块的功能。刚好那两个脚接到了ESP8266的烧录固件IO-0脚与复位RST脚上,所以我们可以用杜邦线将模块重新连接到这个座子上,只接VCC、GND、TX和RX脚即可!
    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”)。
    在这里插入图片描述

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

    sudo chmod 777 /dev/ttyUSB0
    
    • 1

    修改完成后查看源码内容如下。先看源码,不要急着运行!

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

    24.4 智能家居物联UI界面开发

    项目路径为4/01_smarthome/01_smarthome/01_smarthome.pro,先看项目界面。项目界面如下,采用暗黑主题设计,结合黄色作为亮色,让用户一目了然。界面编者从一些智能家居界面中找到灵感的,编写设计完成的效果不错!请自行查阅源码,掌握了本教程前面第七章的内容,就可以理解这个界面是如何设计的。
    在这里插入图片描述

    24.5 原子云API接口
    我们想要与原子云通信,那么必须先了解原子云平台的API接口。请参阅原子云平台API文档V1.2.pdf文档。原子云平台API写的非常详细了,请自行翻阅。需要我们从原子云平台了解原子云API的通信流程。
    下图是原子云平台API的使用流程图。
    在这里插入图片描述

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

    第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项目,此项目设备端(被控端)。
    打开上面两个项目如下。
    在这里插入图片描述

    项目文件夹下内容解释:
    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应用程序收到后就会显示此设备在线。
    在这里插入图片描述

    运行01_smarthome.pro项目前请确认原子云》帐号信息处的API TOKEN信息,请填写自己的原子云帐号API TOKEN。否则您将访问到编者的原子云帐号的设备信息。

    点击“客厅”开关按钮,可以看到esp8266设备应用程序收到开关信息。这个信息是通过UI界面应用程序发送到原子云服务器,然后原子云服务器转发给WIFI模块设备的。
    在这里插入图片描述

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

  • 相关阅读:
    Python操作Mysql
    200道Java面试题
    rsync远程同步
    Flowable 已经执行完毕的流程去哪找?
    SSM--关联关系映射
    28岁转行软件测试真的很难吗?按照我整理出的这份3000字学习指南就没问题...
    JavaScript 数据结构与刷题基础
    【Axure高保真原型】3D柱状图_中继器版
    python+django协同过滤算法的音乐推荐系统研究vue
    rabbitMq 针对于当前监听的队列,来控制消费者并发数量,不影响其他队列,代码示例
  • 原文地址:https://blog.csdn.net/weixin_55796564/article/details/125868069