精品课网站制作,拉新十大推广app平台,网络推广有哪些网站,淄博网站建设优化运营目录
一、项目配置
二、MQTT连接
三、数据解析
四、数据更新
五、数据发送
六、指令下发 一、项目配置 按常规新建一个Quick空项目后#xff0c;我们需要对项目内容稍微改造、规划下。 首先根据我们的需要在.pro文件内添加必要的模块#xff0c;其中quick就是qml了我们需要对项目内容稍微改造、规划下。 首先根据我们的需要在.pro文件内添加必要的模块其中quick就是qml了core那是核心模块必须的network是网络模块MQTT需要使用网络。 然后就是为QML文件和图片文件各自建立一个资源文件这样编译的时候会把这些资源带上否则的话打包发布的时候你需要把QML文件和图片文件放到指定文件夹内这在安卓里就不方便了。 最后就是如何加载前端QML文件的问题了如下图所示后端通过QML加载引擎QQmlApplicationEngine把QML主文件加载进来就能显示界面了下一行是前端到后端的设置接口名称这样在QML文件里就可以用theMainInterface这个名称引用后端的函数了完成开关、调速等动作。 在新工程默认的文件里加载前端文件这一步是在main.c文件里完成的我们这里为了整体前后端的交互特意建了一个MainInterface的类在主函数中直接定义这个类的变量即可这样整个工程结构比较清晰。 二、MQTT连接 QT标准库里没有mqtt需要自己单独下载GitCode - 开发者的代码家园我的项目里已经集成了只要右键添加进来即可这里主要是要写个自己的MQTT管理类把状态保活、话题订阅等任务内部处理掉就是BaseMqtt类了里面还有个内容是域名解析需要处理。
#include BaseMqtt.hBaseMqtt::BaseMqtt(QObject *parent) : QObject(parent)
{isConnectedfalse;checkTimer new QTimer(this);checkTimer-setInterval(1*100);//心跳检测checkTimer-start();m_heartTime0;connect(checkTimer, SIGNAL(timeout()),this,SLOT(slotCheckTimeout()));m_mqttClientnullptr;m_hostAddress;
}
BaseMqtt::~BaseMqtt(void)
{qDebug()~BaseMqtt, hostNamem_connectParam.hostName;
}void BaseMqtt::slotLookUpHost(QHostInfo info)
{if(info.error() QHostInfo::NoError){foreach (QHostAddress address, info.addresses()){m_hostAddressaddress.toString();qDebug()m_connectParam.hostName mqtt found ip m_hostAddress;break;}}
}void BaseMqtt::slotCheckTimeout(void)
{m_heartTime;if(m_hostAddress.isEmpty()){if(m_heartTime%100){qDebug(mqtt start dns!);QHostInfo::lookupHost(m_connectParam.hostName, this, SLOT(slotLookUpHost(QHostInfo)));}return;}if(m_mqttClientnullptr){if(!m_connectParam.certPath.isEmpty())//使用SSL连接{QFile file;QByteArray client_key_text, client_crt_text, root_crt_text;QString certPathm_connectParam.certPath;QSslSocket ssl_socket;file.setFileName(certPath/client.key);file.open(QIODevice::ReadOnly | QIODevice::Text);client_key_text file.readAll();file.close();file.setFileName(certPath/client.crt);file.open(QIODevice::ReadOnly | QIODevice::Text);client_crt_text file.readAll();file.close();file.setFileName(certPath/rootCA.crt);file.open(QIODevice::ReadOnly | QIODevice::Text);root_crt_text file.readAll();file.close();QSslKey client_key(client_key_text, QSsl::Rsa);QSslCertificate client_crt(client_crt_text); ssl_socket.setPrivateKey(client_key);ssl_socket.setLocalCertificate(client_crt);QSslConfiguration ssl_configQSslConfiguration::defaultConfiguration();ssl_config.setCaCertificates(QSslCertificate::fromData(root_crt_text)); //QSslCertificate::fromPath(certPath/rootCA.crt); ssl_config.setPrivateKey(ssl_socket.privateKey());ssl_config.setLocalCertificate(ssl_socket.localCertificate());ssl_config.setPeerVerifyMode(QSslSocket::QueryPeer);ssl_config.setPeerVerifyDepth(10);ssl_config.setProtocol(QSsl::TlsV1_2);m_mqttClient new QMQTT::Client(m_hostAddress, m_connectParam.hostPort, ssl_config, true, this);
// qDebug()\n###SSL PrivateKeyssl_config.privateKey();
// qDebug()###SSL Certificatessl_config.localCertificate();
// qDebug()###SSL rootCAssl_config.caCertificates();
// qDebug()hostNamem_hostAddress, hostPortm_connectParam.hostPort;
// qDebug()userNamem_connectParam.userName, passWordm_connectParam.passWord, clientIDm_connectParam.clientID;}else//普通连接{QHostAddress host(m_hostAddress);m_mqttClient new QMQTT::Client(host, m_connectParam.hostPort, this);}signalSlotInit();m_mqttClient-setUsername(m_connectParam.userName);m_mqttClient-setPassword(m_connectParam.passWord);m_mqttClient-setClientId(m_connectParam.clientID);m_mqttClient-setAutoReconnect(true);m_mqttClient-setCleanSession(true);m_mqttClient-setKeepAlive(30);m_mqttClient-connectToHost();}else if(this-mqttIsConnected()){for(auto iter : m_subTopicList)//订阅话题{if(iter.isSubedfalse){mqttSubscribeMessage(iter.subTopic, iter.qos);break;}}if(m_heartTime%2000)//保持连接{
// qDebug()mqtt send keep;mqttPublishMessage(dev/sub/data1, QByteArray(heart));}}else{
// qDebug()BaseMqtt no connected!;}
}void BaseMqtt::mqttConnect(QString hostName, u16 hostPort, QString userName, QByteArray passWord, QString clientID, QString certPath)
{clientIDclientIDQString(_)takeHostMac().remove(:);m_connectParam.hostNamehostName;m_connectParam.hostPorthostPort;m_connectParam.userNameuserName;m_connectParam.passWordpassWord;m_connectParam.clientIDclientID;m_connectParam.certPathcertPath;u8 *pData(u8*)hostName.toUtf8().data();if(pData[0]0 pData[0]9)//判断是否为域名,使用域名时 域名的第一个字符不能是数字{m_hostAddresshostName;}
}bool BaseMqtt::mqttIsConnected(void)
{
// if(m_mqttClient!nullptr)
// return m_mqttClient-isConnectedToHost();return isConnected;
}void BaseMqtt::mqttPublishMessage(QString topicFilter, QByteArray msgBa)
{if(m_mqttClientnullptr || m_mqttClient-isConnectedToHost()false)return;QMQTT::Message message;message.setTopic(topicFilter);message.setPayload(msgBa);m_mqttClient-publish(message);
}void BaseMqtt::mqttPingresp(void)
{
// m_mqttClient-pingresp();
}void BaseMqtt::mqttSubscribeMessage(QString topicFilter, quint8 qos)
{if(m_mqttClientnullptr)return;m_mqttClient-subscribe(topicFilter, qos);
}void BaseMqtt::mqttUnsubscribeMessage(QString topicFilter)
{if(m_mqttClientnullptr)return;m_mqttClient-unsubscribe(topicFilter);
}void BaseMqtt::signalSlotInit(void)
{connect(m_mqttClient, SIGNAL(connected()), this, SLOT(slotMqttConnected()));connect(m_mqttClient, SIGNAL(disconnected()), this, SLOT(slotMqttDisconnected()));connect(m_mqttClient, SIGNAL(error(QMQTT::ClientError)), this, SLOT(slotMqttError(QMQTT::ClientError)));connect(m_mqttClient, SIGNAL(pingresp()), this, SLOT(slotMqttPingresp()));connect(m_mqttClient, SIGNAL(published(QMQTT::Message,quint16)), this, SLOT(slotMqttPuslished(QMQTT::Message,quint16)));connect(m_mqttClient, SIGNAL(received(QMQTT::Message)), this, SLOT(slotMqttReceived(QMQTT::Message)));connect(m_mqttClient, SIGNAL(subscribed(QString,quint8)), this, SLOT(slotMqttSubscribed(QString,quint8)));connect(m_mqttClient, SIGNAL(unsubscribed(QString)), this, SLOT(slotMqttUnsubscribed(QString)));
}void BaseMqtt::mqttAddTopic(QString topic, u8 qos)
{for(auto iter : m_subTopicList){if(iter.subTopictopic){qDebug()have the same topictopic;return;}}SubTopicStruct tag_subTopic;tag_subTopic.subTopictopic;tag_subTopic.isSubedfalse;tag_subTopic.qosqos;m_subTopicList.append(tag_subTopic);qDebug()mqttAddTopictopic;
}void BaseMqtt::mqttDelTopic(QString topic)
{int i0;for(auto iter : m_subTopicList){if(topiciter.subTopic){if(iter.isSubedtrue){this-mqttUnsubscribeMessage(topic);}m_subTopicList.removeAt(i);qDebug()remove topictopic;return;}i;}
}void BaseMqtt::slotMqttConnected(void)
{isConnectedtrue;emit sigMqttConnected();qDebug()clientIdm_mqttClient-clientId()connected;
}void BaseMqtt::slotMqttDisconnected(void)
{isConnectedfalse;int nSizem_subTopicList.size();for(int i0; inSize; i){m_subTopicList[i].isSubedfalse;}qDebug()clientIdm_mqttClient-clientId()disconnected;emit sigMqttDisconnected();
}void BaseMqtt::slotMqttError(const QMQTT::ClientError error)
{qDebug()clientIdm_mqttClient-clientId()slotMqttErrorerror;
}void BaseMqtt::slotMqttPingresp(void)
{
// qDebug(BaseMqtt::slotMqttPingresp);
}void BaseMqtt::slotMqttPuslished(const QMQTT::Message message, quint16 msgid)
{msgidmessage.id();msgidmsgid;
// qDebug(BaseMqtt::slotMqttPuslished, msgid%d , msgid);
// qDebug()msgmessage.payload().toHex();
}void BaseMqtt::slotMqttReceived(const QMQTT::Message message)
{emit sigtMqttReceived(message);}void BaseMqtt::slotMqttSubscribed(const QString topic, const quint8 qos)
{int i0;
// qDebug()slotMqttSubscribed, topictopic, qosqos;for(auto iter : m_subTopicList){if(iter.subTopictopic){m_subTopicList[i].isSubedtrue;break;}i;}emit sigMqttSubscribed(topic, qos);
}void BaseMqtt::slotMqttUnsubscribed(const QString topic)
{int i0;for(auto iter : m_subTopicList){if(iter.subTopictopic){m_subTopicList[i].isSubedfalse;break;}i;}emit sigMqttUnsubscribed(topic);
}QString BaseMqtt::takeHostMac(void)
{DrvCommon drv_com; return drv_com.takeRandMac();
} 对于应用层就很简单了就是创建对象、连接和添加订阅话题即可。其中有个槽函数slotMqttReceived就是用来接收设备发来的数据的。 三、数据解析 数据解析跟嵌入式端是差不多的下面是代码像剥洋葱一样查找帧头、校验、根据命令类型执行解析。 void MainInterface::slotMqttReceived(const QMQTT::Message message)
{QByteArray msg_bamessage.payload();u8 *pData(u8*)msg_ba.data();
// qDebug()mqtt recvmsg_ba.toHex(:);u8 head[2]{0xAA, 0x55};pDatadrv_com.memstr(pData, msg_ba.size(), head, 2);if(pData!nullptr){u16 total_lenpData[2]8 | pData[3];u16 crcValuepData[total_len]8 | pData[total_len1];if(crcValuedrv_com.crc16(pData, total_len)){pData4;u32 device_snpData[0]24|pData[1]16|pData[2]8|pData[3];pData4;m_currDevSndevice_sn;u8 cmd_typepData[0];pData1;qDebug(recv device_sn%08X, cmd_type%d, device_sn, cmd_type);m_keepTimem_secCounts;switch(cmd_type){case AIR_CMD_HEART:{break;}case AIR_CMD_DATA:{int temppData[0]8|pData[1];//温度 原始数据float temp_f(temp-1000)/10.f;//温度浮点数据pData2;int humipData[0]8|pData[1];float humi_fhumi/10.f;pData2;int pm25pData[0]8|pData[1];pData2;u8 speedpData[0];pData1;u8 statepData[0];pData1;qDebug(temp_f%.1f C, humi_f%.1f%%, pm25%d ug/m3, speed%d, state%d, temp_f, humi_f, pm25, speed,state);QString dev_sn_strQString::asprintf(%08X, device_sn);QString temp_strQString::asprintf(%.0f, temp_f);QString humi_strQString::asprintf(%.0f, humi_f);QString pm25_strQString::asprintf(%03d, pm25);int alarm_level0;if(pm2520)alarm_level0;else if(pm2530)alarm_level1;else alarm_level2;emit siqUpdateSensorValues(dev_sn_str, temp_str, humi_str, pm25_str);emit siqUpdateAlarmLevel(alarm_level);emit siqUpdateSwitchState(state);break;}case AIR_CMD_SET_SPEED:{break;}}}}
}这里我们需要把数据送到前端去显示所以定义了几个信号内容如下图所示从上到下依次是状态数据污染等级和开关状态这些数据都是设备端发送上来的通过后端处理加工后发到前端显示。这里对于污染等级的数值可以自定义我这边为了方便测试是20、30两个分界线小米的净化器应该是30和80两条线。 四、数据更新 对于前端显示这里先提一下如何接收后端发来的数据的如下图所示。以Connections对象为基础设置它的属性target为theMainInterface这个其实就是我们加载QML文件时候设置的前后端交互名称这里用上了相当于是信号发射者信号接收器就是C文件里定义的信号函数前面加个on然后首字母改成大写就可以了这里是s改为S这样这里就能接收到后端发送过来的传感器数据了很简单吧。至于如何显示放到前端部分再讲解。 五、数据发送 数据发送底层就是跟嵌入式端一样组合后通过mqtt发送出去就行了有点区别就是这时候要带上目标的序列号dev_sn这样带有序列号的话题设备端才能收到数据。 void MainInterface::airSendLevel(u32 dev_sn, int cmd_type, u8 *cmd_buff, u16 cmd_len)
{u8 make_buff[500]{0};u16 make_len0;make_buff[make_len]0xAA;make_buff[make_len]0x55;make_buff[make_len]0;make_buff[make_len]0;make_buff[make_len]dev_sn24;make_buff[make_len]dev_sn16;make_buff[make_len]dev_sn8;make_buff[make_len]dev_sn;make_buff[make_len]cmd_type;memcpy(make_buff[make_len], cmd_buff, cmd_len);make_lencmd_len;make_buff[2]make_len8;make_buff[3]make_len;u16 crcValuedrv_com.crc16(make_buff, make_len);make_buff[make_len]crcValue8;make_buff[make_len]crcValue;QByteArray msg_ba((char*)make_buff, make_len);QString topicQString::asprintf(air/dev/sub/%08X, dev_sn);if(m_mqttClient){m_mqttClient-mqttPublishMessage(topic, msg_ba);}
}六、指令下发 在应用层主要就是开关和调速两个功能这里要看下这两个函数的定义比较特别在头文件定义的函数名称前多了Q_INVOKABLE添加了这个关键字后这个函数就可以在QML文件里直接调用了是不是很方便。置于函数的内容应该比较简单了就是组合下报文给底层函数发送出去就行了这里的命令定义跟嵌入式端是一样的两边有改动的话一定要及时同步不然就乱了。对于调速设置函数的输入是0~1的浮点数当小于0.1的时候会强制等于0.1这样调速最低时才不会停掉只是慢速转动。 对于这里的后端总的来说没什么难点主要还是做好前后端数据交互的准备。这里有个细节提一下细心的也会发现有个定时器槽函数slotCheckTimeout()虽然这里没什么用但是在其他有复杂任务或者多线程的时候很有用它可以定时执行任务相当于局部的main函数了以后有机会再慢慢学习。 本项目的交流QQ群:701889554 写于2024-4-2