目录
本项目的交流QQ群:701889554
物联网实战--入门篇https://blog.csdn.net/ypp240124016/category_12609773.html
物联网实战--驱动篇https://blog.csdn.net/ypp240124016/category_12631333.html
物联网实战--平台篇https://blog.csdn.net/ypp240124016/category_12653350.html
嵌入式文件 https://download.csdn.net/download/ypp240124016/89409505
APP文件 https://download.csdn.net/download/ypp240124016/89409506
一、底层数据解析
手机端从MQTT服务器收到消息后,首先需要根据协议对数据进行解析,把最终的应用层数据找出并传递到具体的设备模型中去,具体过程如下。
cpp
void CenterMan::parseDeciceRecv(QByteArray msg_ba)
{
#define OUT_BUFF_SIZE 1500
#define IN_BUFF_SIZE 1500
u8 head[]={0xAA, 0x55}, *pBuff=NULL, *pData=NULL;
u8 pack_num=0, cmd_type=0;
u32 app_id, curr_dev_sn, next_dev_sn, parent_sn;
u16 in_len, out_len, crcValue, data_len=0;
u8 out_buff[OUT_BUFF_SIZE]={0}, in_buff[IN_BUFF_SIZE]={0};
if( (pBuff=drv_com.memstr((u8*)msg_ba.data(), msg_ba.size(), head, 2)) != NULL )
{
ServerHeadStruct *pSeverHead=(ServerHeadStruct*)pBuff;
crcValue=pSeverHead->crc_h<<8|pSeverHead->crc_l;
data_len=pSeverHead->data_len_h<<8|pSeverHead->data_len_l;
pData=pBuff+8;//指到app_id地址
if(data_len<8 || crcValue != drv_com.crc16(pData, data_len))//CRC校验
{
qDebug("deviceRecvProcess: crc error!\n");
return;
}
app_id=pData[0]<<24|pData[1]<<16|pData[2]<<8|pData[3];
pData+=4;
curr_dev_sn=pData[0]<<24|pData[1]<<16|pData[2]<<8|pData[3];
parent_sn=0;
pData+=4;//指向数据区
// qDebug("deviceRecvProcess: app_id=%u, gw_sn=%08X", app_id, curr_dev_sn);
if(app_id==0 || curr_dev_sn==0)
{
qDebug("deviceRecvProcess error: app_id==0 || curr_dev_sn==0");
return;
}
in_len=data_len-8;//加密数据长度
if(in_len>IN_BUFF_SIZE)//数据包过长
{
qDebug("deviceRecvProcess error: in_len>DATA_BUFF_SIZE, in_len=%d", in_len);
return;
}
memcpy(in_buff, pData, in_len);//复制数据
u8 deep_counts=1;
while(curr_dev_sn>0)
{
u8 encrypt_mode=ENCRYPT_MODE_DISABLE;
u8 encrypt_index=0;
u8 passwd_buff[20]={0};
if(deep_counts==1)//网关设备
{
encrypt_index=pSeverHead->encrypt_index;//密码索引
encrypt_mode=takeDevicePasswd(curr_dev_sn, encrypt_index, passwd_buff);//获取该设备的通讯密码
}
switch(encrypt_mode)
{
case ENCRYPT_MODE_DISABLE: //不加密
{
out_len=in_len;
if(out_len>OUT_BUFF_SIZE)
out_len=0;
memcpy(out_buff, in_buff, in_len);
break;
}
case ENCRYPT_MODE_TEA: //TEA加密
{
out_len=drv_com.TEA_DecryptBuffer(in_buff, in_len, (u32*)passwd_buff);
if(out_len>OUT_BUFF_SIZE)
out_len=0;
memcpy(out_buff, in_buff, out_len);
break;
}
case ENCRYPT_MODE_AES: //AES加密
{
out_len=drv_com.aes_decrypt_buff(in_buff, in_len, out_buff, OUT_BUFF_SIZE, passwd_buff);
break;
}
default: return;//出错
}
if(out_len>0)
{
pBuff=pData=out_buff;
data_len=pData[0]<<8|pData[1];
pData+=2;
pack_num=pData[0];
pData++;
cmd_type=pData[0];
pData++;//指向数据区或者下一个数据单元
// qDebug("data_len=%u, pack_num=%u, cmd_type=%u", data_len, pack_num, cmd_type);
if(data_len<4 || (data_len>IN_BUFF_SIZE))
{
qDebug("deviceRecvProcess error: data_len<4 || data_len>IN_BUFF_SIZE, data_len=%d, pack_num=%d", data_len, pack_num);
return;
}
crcValue=pBuff[data_len]<<8|pBuff[data_len+1];
if(crcValue != drv_com.crc16(pBuff, data_len))//单元内校验
{
qDebug("deviceRecvProcess error: crcValue failed!");
return;
}
if(cmd_type==100)//是否为数据包命令
{
next_dev_sn=pData[0]<<24|pData[1]<<16|pData[2]<<8|pData[3];
// qDebug("next_dev_sn=0x%08X", next_dev_sn);
pData+=4;
parent_sn=curr_dev_sn;
curr_dev_sn=next_dev_sn;
in_len=data_len-8;//下一个数据区的长度
memset(in_buff, 0, IN_BUFF_SIZE);
memcpy(in_buff, pData, in_len);//复制下一个数据单元
}
else//本机数据,进行处理
{
// QByteArray msg_ba((char*)pData, data_len-4);
// qDebug()<<"msg_ba="<<msg_ba.toHex(':');
if(app_id==m_currAppWork.appID)//本项目设备
{
WorkDevStruct *pWorkNode=searchWorkNode(curr_dev_sn);
if(pWorkNode)
{
if(parent_sn>0 && pWorkNode->parentSn != parent_sn)//节点父序列号改变
{
pWorkNode->parentSn=parent_sn;
// requestUpdateWorkName(curr_dev_sn, pWorkNode->devName);//更新父序列号
}
if(parent_sn==0)
{
pWorkNode->encrypt_index=pSeverHead->encrypt_index;//记录网关的加密模式,方便数据下发
}
if(pWorkNode->pModel)
{
pWorkNode->pModel->setRawData(app_id, curr_dev_sn, pack_num, cmd_type, pData, data_len-4);
}
return;
}
else//设备未存在
{
if(m_secCounts>10)//可能是还未从服务器请求下来
{
InitTypeStruct *pInittype=takeInitType(curr_dev_sn);
if(pInittype)
{
requestAddDevice(curr_dev_sn, parent_sn);//请求添加设备
}
}
}
}
curr_dev_sn=0;
return;
}
deep_counts++;
if(deep_counts>=5)//嵌套层数
{
qDebug("deviceRecvProcess error: deep_counts>DEEP_MAX! parent_sn=%u", parent_sn);
return;
}
}
else
{
qDebug("deviceRecvProcess error: out_len<=0!");
return;
}
}
}
else
{
qDebug("no found head AA 55");
}
}
二、物模型后端
数据协议解析完成后,就进入应用数据解析了,就是模型的setRawData函数,这是数据进入物模型的接口,至于模型内的数据和功能,完全是根据产品本身的定义决定的,与嵌入式端对应。
cpp
int ModelAp01::setRawData(u32 app_id, u32 dev_sn, u8 pack_num, u8 msg_type, u8 *msg_buff, u16 msg_len)
{
if(dev_sn!=m_devSn)
return 0;
u8 *pData=msg_buff;
msg_len=msg_len;
if(m_upPackNum==pack_num)
return 0;
m_upPackNum=pack_num;
m_appID=app_id;
// qDebug()<<"msg_type="<<msg_type;
switch(msg_type)
{
case AP01_CMD_HEART:
{
break;
}
case AP01_CMD_DATA:
{
int temp=pData[0]<<8|pData[1];//温度 原始数据
float temp_f=(temp-1000)/10.f;//温度浮点数据
pData+=2;
int humi=pData[0]<<8|pData[1];
float humi_f=humi/10.f;
pData+=2;
int pm25=pData[0]<<8|pData[1];
pData+=2;
u8 speed=pData[0];
pData+=1;
u8 state=pData[0];
pData+=1;
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_str=QString::asprintf("%08X", m_devSn);
QString temp_str=QString::asprintf("%.0f", temp_f);
QString humi_str=QString::asprintf("%.0f", humi_f);
QString pm25_str=QString::asprintf("%03d", pm25);
int alarm_level=0;
if(pm25<20)alarm_level=0;
else if(pm25<30)alarm_level=1;
else alarm_level=2;
emit siqUpdateSensorValues(dev_sn_str, temp_str, humi_str, pm25_str);
emit siqUpdateAlarmLevel(alarm_level);
emit siqUpdateSwitchState(state);
break;
}
}
QDateTime current_date_time = QDateTime::currentDateTime();
m_updateTime=current_date_time.toString("hh:mm:ss");
m_onlineTime=m_secTickets;
m_onlineState=DEV_STATE_ON_LINE;
return 0;
}
后端模型文件基本上就是一个类文件,较为简单,开发展也只需集中精力完善这个模型即可。
三、物模型前端
前端模型分为两部分,一个是简易网格模型,一个是具体模型,这个在上一节演示视频的APP界面上显而易见了。至于其它AP01开头的QML文件,就是延续了净化器项目的各个前端模块,比如开关、气泡等等。
详细模型的代码如下:
javascript
import QtQuick 2.7
import QtQuick.Controls 1.4
import "../base"
BaseModelView
{
id:id_baseModelView
parent: theModelAp01.takeModelParent()
devName: theModelAp01.takeDeviceName()
devSnStr: theModelAp01.takeDevSnStr()
ScrollView {
id:id_zoneView
implicitWidth: 400
width: parent.width
clip: true
anchors
{
top:parent.top
topMargin:50
bottom:parent.bottom
}
verticalScrollBarPolicy:Qt.ScrollBarAlwaysOff
horizontalScrollBarPolicy: Qt.ScrollBarAlwaysOff
Rectangle
{
id:id_centerRect
width: parent.width
height: 800
color: "#FAFAFA"
Ap01CenterShow//中心圆圈画面
{
id:id_airCenterShow
width: parent.width
height: parent.height*0.6
anchors
{
top:parent.top
}
}
Ap01SwitchItem//开关栏
{
id:id_switchItem
width: parent.width*0.96
anchors
{
horizontalCenter:parent.horizontalCenter
top:id_airCenterShow.bottom
}
}
Ap01ControlItem //风速控制
{
id:id_airControlItem
anchors
{
top:id_switchItem.bottom
topMargin:5
horizontalCenter:parent.horizontalCenter
}
}
}
}
Component.onCompleted:
{
id_zoneView.width=width
id_centerRect.width=width
// console.log("width=", width)
}
Connections
{
target: theModelAp01
onSiqUpdateSensorValues:
{
id_airCenterShow.funUpdateThText(temp, humi)//更新温湿度数值
id_airCenterShow.funUpdatePm25Text(pm25)//更新PM2.5数值
}
onSiqUpdateOnlineState:
{
funSetDevState(state)//更新在线状态
}
onSiqUpdateDeviceName:
{
devName=dev_name
}
}
}
四、数据下行
像设置开关、调速的指令都是通过信号从模型发送到处理中心的,这个在添加工作设备的时候已经进行信号槽的连接了。
这个槽函数的内容就是对数据进行封装下发
cpp
void CenterMan::slotSendDownMsg(u32 app_id, u32 dev_sn, u8 pack_num, u8 msg_type, u8 *msg_buff, u16 msg_len)
{
// qDebug()<<"slotSendDownMsg****";
u8 data_buff[1500]={0}, make_buff[1500]={0};
u16 make_len, data_len;
data_len=msg_len;
if(msg_len>sizeof(data_buff))
{
qDebug()<<"slotSendDownMsg error, msg_len>sizeof(data_buff)";
return;
}
memcpy(data_buff, msg_buff, data_len);
u32 currDevSn=dev_sn;
if(app_id==m_currAppWork.appID)//自身设备
{
int deep=0;
while(currDevSn>0)
{
WorkDevStruct *pWorkNode=searchWorkNode(currDevSn);
if(pWorkNode==nullptr)
{
qDebug()<<"pWorkNode==nullptr";
return;
}
if(deep>0)
{
msg_type=200;//转发指令为200
}
qDebug("down curr_sn=0x%08X, parent_sn=0x%08X", pWorkNode->devSn, pWorkNode->parentSn);
if(pWorkNode->parentSn==0)//网关设备
{
make_len=makeGwSendBuff(app_id, currDevSn, pack_num, pWorkNode->encrypt_index,
msg_type, data_buff, data_len, make_buff, sizeof(make_buff));
publishDownMsg(app_id, currDevSn, make_buff, make_len);//发布
currDevSn=0;
}
else//节点设备
{
make_len=makeNodeSendBuff(currDevSn, pack_num, msg_type, data_buff, data_len, make_buff, sizeof(make_buff));//组合节点数据
if(make_len>0 && make_len<sizeof(data_buff))
{
data_len=make_len;
memcpy(data_buff, make_buff, make_len);
memset(make_buff, 0, sizeof(make_buff));
}
else
{
qDebug("error01, make_len=%u", make_len);
return;
}
currDevSn=pWorkNode->parentSn;//父节点序列号
}
deep++;
}
}
}
//发送设备下行数据
void CenterMan::publishDownMsg(u32 app_id, u32 gw_sn, u8 *msg_buff, u16 msg_len)
{
QString topic=QString(TOPIC_HEAD)+QString::asprintf("dev/sub/data/%u/%08X", app_id, gw_sn);
QByteArray msg_ba((char*)msg_buff, msg_len);
// qDebug()<<"000 pub topic="<<topic<<"\nmsg_ba="<<msg_ba.toHex(':');
emit sigMqttPushMessage(topic, msg_ba);
}