端点物联开发教程之(二)开发演示

目录

一、产品定义

二、基础资源下载

三、嵌入式端开发

3.1 工程搭建

3.2 模型文件修改

3.3 头文件定义

3.4 模型功能开发

3.5 mqtt应用层配置

3.6启动任务

四、用户后端开发

4.1 功能分析

4.2 创建模型文件

4.3 添加基础功能

4.4 数据更新

4.5 阈值设置

4.6 模型添加

五、用户前端开发

5.1 界面分析

5.2 简易模型

5.3 完整模型

六、模型联调


本项目的交流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/89425313

一、产品定义

        这是开发的第一步,首先要明确开发内容和目标。作为演示项目,我们定义为一个温湿度计,它的功能较为简单,就是定时上报温湿度数据,同时用户可以设置高温和高湿报警阈值,当温度或湿度超过阈值时候用户端界面会变成红色。

        那么,有上述定义可知,主要变量就是温度、湿度和两个各自的阈值,还有各自的报警状态,以下是字段定义表格:

        表格最后一列中的N是转正基数,避免在传输时出现负数;M是精度值,如果精度是小数点后一位那么M就等于10,后两位M就等于100,以此类推。这样做的好处在之前的净化器文章中有说过了,核心目的是为了保证传输的数据类型都是正整数,便于字节流传输时分解与整合,因为长度大于1字节的数据在网络传输中会出现网络序大小端的问题,不同的CPU架构大小端是不一样的,具体可以看下这篇文章。所以我们在设计传输协议的时候要规避这个问题,把所有的数值都转成正整数,然后统一高位先传输的原则转成字节流,这样就不存在大小端的问题了。

        有了具体字段之后就可以定义命令类型了,具体看下面表格:

        有了以上这些定义,我们就可以进行设备端的开发了。

二、基础资源下载

资源文件下载https://download.csdn.net/download/ypp240124016/89425313

        上图是资源结构图,我们现在要使用的是嵌入式端源码这个压缩包,把它解压出来就可以进行下一步的开发了。

三、嵌入式端开发
3.1 工程搭建

解压后目录如下:

点开Project文件夹,内容如下图所示,我们复制 Project-净化器(M2M) 整个文件夹在同目录下粘贴。

然后将副本改名为 演示温湿度计。

接着把下图这几个名字都改下就可以打开工作空间了,

最后打开目录里的工作空间,此时还没有温湿度计的项目,需要添加进来,顺序如下所示。同时把项目名称也改下,这样就完成了新项目的基础搭建了。

3.2 模型文件修改

        接下里就可以进入正式的开发了,首先删除app_ap01.c文件,这是原来净化器项目的文件,我们这里需要根据型号重新定义一个文件,型号就定义为TH01,所以新文件就是app_th01.c了,修改后的文件结构如下图所示,Keil的文件编辑格式最好改成UTF-8的,便于汉字注释。

        下图的两个头文件名称也改下,这样就可以编译了,编译后原来工程的相关内容会报错,删除或者注释掉就行,再编译一次,应该就能编译通过了,一个具体的工程模板就完成了,剩下的是具体产品内容定义了。

        

3.3 头文件定义

        模型文件从app_th01.h开始,根据第一节的产品定义,头文件定义如下:

        其中的温湿度报警阈值需要掉电保存,所以定义了一个存储结构体,为了确保数据的准确性,读取时候采用CRC校验,如果校验出错就使用默认的阈值。

3.4 模型功能开发

        首先进行配置参数读取,这里就是温湿度的报警阈值了。如果是第一次读取或者存储出错,那么就是用默认阈值,默认报警温度是50℃,默认报警湿度是95%。

        传感器部分只有温湿度了,这里沿用净化器项目的配置,接线是SDA--PB8,SCL--PB9。

        剩下的就是数据传输的内容了,根据定义,数据上行有状态数据和阈值数据,具体代码如下,其中状态数据发送前对报警状态做了判别,数据流根据定义里的N M值做了转换,发送时命令值也是根据头文件的定义传入。

        下行数据有三种类型,其中阈值数据并不需要实时发送,只有当用户端需要查看时再立刻发送即可,所以在用户端的物模型前端里设置请求指令进行请求一次即可,这样可以减少无效数据传输。这种需求在传统的物联网开发中比较难以实现,倒不是技术上有什么难度,而是沟通成本太高,正常嵌入式开发人员很难直接跟前端人员进行需求沟通的;那么,对于我们端到端的开发模式,这个需求可以内部自行消化。

        阈值设置就是赋值+保存了,没什么特别的;最后再即时返回当前阈值即可。

        最后就是将整个任务运行起来就行了。

        模型app_th01.c的所有C代码如下:


#include "app_th01.h" 
#include "app_mqtt.h" 

Th01SaveStruct g_sTh01Save={0};
Th01WorkStruct g_sTh01Work={0};

/*		
================================================================================
描述 : 配置参数读取
输入 : 
输出 : 
================================================================================
*/
void app_th01_read(void) 
{
  EEPROM_Read(TH01_EEPROM_ADDR, (u8 *)&g_sTh01Save, sizeof(g_sTh01Save));
  
  if(g_sTh01Save.crcValue!=drv_crc16((u8*)&g_sTh01Save, sizeof(g_sTh01Save)-2))
  {
    g_sTh01Save.temp_thresh=1500;//高于50℃报警
    g_sTh01Save.humi_thresh=950;//高于95%报警
    app_th01_write();
    printf("app th01 read new!\n");
  }  
  printf("alarm temp=%.1fC, humi=%.1f%%\n", (g_sTh01Save.temp_thresh-1000)/10.f, g_sTh01Save.humi_thresh/10.f);
}

/*		
================================================================================
描述 : 配置参数保存
输入 : 
输出 : 
================================================================================
*/
void app_th01_write(void)
{
  g_sTh01Save.crcValue=drv_crc16((u8*)&g_sTh01Save, sizeof(g_sTh01Save)-2);
  EEPROM_Write(TH01_EEPROM_ADDR, (u8 *)&g_sTh01Save, sizeof(g_sTh01Save));  
}

/*		
================================================================================
描述 : SHT30温湿度初始化
输入 : 
输出 : 
================================================================================
*/
void app_sht30_init(void)
{
  //SDA--PB8   SCL--PB9
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);//开启引脚时钟
  I2cDriverStruct *pIIC=&g_sTh01Work.tag_sht30.tag_iic;
  
  //引脚赋值
  pIIC->pin_sda=GPIO_Pin_8;
  pIIC->port_sda=GPIOB;
  
  pIIC->pin_scl=GPIO_Pin_9;
  pIIC->port_scl=GPIOB;  
  
  drv_sht30_init(&g_sTh01Work.tag_sht30);//初始化
}

/*		
================================================================================
描述 : 整体初始化
输入 : 
输出 : 
================================================================================
*/
void app_th01_init(void)
{
  app_th01_read();//参数读取
  app_mqtt_init();  //mqtt应用层配置初始化
  app_sht30_init(); //温湿度传感器初始化
} 

/*		
================================================================================
描述 : 发送设备的状态数据
输入 : 
输出 : 
================================================================================
*/
void app_th01_send_status(void)
{
  u8 cmd_buff[20]={0};
  u16 cmd_len=0;
  u16 tmp_u16=0;
  
  tmp_u16=g_sTh01Work.tag_sht30.temp_value*10+1000;
  u8 temp_alarm=tmp_u16>=g_sTh01Save.temp_thresh;//报警检测
  cmd_buff[cmd_len++]=tmp_u16>>8;
  cmd_buff[cmd_len++]=tmp_u16;  //温度
  
  tmp_u16=g_sTh01Work.tag_sht30.humi_value*10;
  u8 humi_alarm=tmp_u16>=g_sTh01Save.humi_thresh;//报警检测
  cmd_buff[cmd_len++]=tmp_u16>>8;
  cmd_buff[cmd_len++]=tmp_u16;  //湿度
  
  g_sTh01Work.alarm_type=temp_alarm<<4 | humi_alarm;
  cmd_buff[cmd_len++]=g_sTh01Work.alarm_type;//报警类型  

  drv_server_send_msg(TH01_CMD_DATA, cmd_buff, cmd_len);//底层发送  
}

/*		
================================================================================
描述 : 发送阈值数据
输入 : 
输出 : 
================================================================================
*/
void app_th01_send_thresh(void)
{
  u8 cmd_buff[20]={0};
  u16 cmd_len=0;
  u16 tmp_u16=0;
  
  tmp_u16=g_sTh01Save.temp_thresh;
  cmd_buff[cmd_len++]=tmp_u16>>8;
  cmd_buff[cmd_len++]=tmp_u16;  //温度阈值
  tmp_u16=g_sTh01Save.humi_thresh;
  cmd_buff[cmd_len++]=tmp_u16>>8;
  cmd_buff[cmd_len++]=tmp_u16;  //湿度阈值

  drv_server_send_msg(TH01_CMD_THRESH, cmd_buff, cmd_len); //底层发送    
}


/*		
================================================================================
描述 : 设备解析服务器下发的数据
输入 : 
输出 : 
================================================================================
*/
u16 app_th01_recv_parse(u8 cmd_type, u8 *buff, u16 len)
{
  u8 *pData=buff;
  switch(cmd_type)
  {
    case TH01_CMD_THRESH://请求阈值数据
    {
      app_th01_send_thresh();//返回阈值数值
      break;
    }
    case TH01_CMD_SET_TEMP://设置温度阈值
    {
      u16 temp_thresh=pData[0]<<8|pData[1];
      pData+=2;
      g_sTh01Save.temp_thresh=temp_thresh;
      app_th01_write();//保存
      app_th01_send_thresh();//返回阈值数值
      break;
    }        
    case TH01_CMD_SET_HUMI://设置湿度阈值
    {
      u16 humi_thresh=pData[0]<<8|pData[1];
      pData+=2;
      g_sTh01Save.humi_thresh=humi_thresh;
      app_th01_write();//保存
      app_th01_send_thresh();//返回阈值数值
      break;
    }   
  }
  return 0;
}

/*		
================================================================================
描述 : 温湿度计总任务线程
输入 : 
输出 : 
================================================================================
*/
void app_th01_thread_entry(void *parameter) 
{
  u16 run_cnts=0;
  printf("app_th01_thread_entry start ****\n");
  delay_os(1000);
  app_th01_init();//初始化
   
  while(1)
  {
    app_mqtt_main(); //MQTT主程序
    delay_os(20);//延时,每个任务线程都要添加,才不会阻塞,最小延时5ms, 即delay_os(5);
    
    if(run_cnts%200==0)//20*200=4000ms 执行一次
    {
      drv_sht30_read_th(&g_sTh01Work.tag_sht30);//读取温湿度值
      
      app_th01_send_status();//上报状态数据
    }
  
    run_cnts++;
  }
  
}


3.5 mqtt应用层配置

        根据自己的应用需求,需要对app_mqtt.c的内容做配置修改,首先是测试的SN码要根据定义的设备型号进行设置,我们这里定义的型号值是A108,测试的SN是A1080123。

        其它要修改的就是模型的命令解析函数,通过函数接口注册即可。至于通讯密码,暂时跟之前的一样就行了,自己也可以随机生成,只要到时候跟QT的C++后端有对应就行了。

3.6启动任务

        最后,就是在user_app.c中启动任务就行了,至此,嵌入式端的开发就基本完成,剩下的就是等用户端的物模型完成后进行联调即可。

四、用户后端开发
4.1 功能分析

        下图是参考界面,我们只要在这个界面的基础上再加一个阈值设置即可。所以主要功能就是解析温湿度数据并发送到前端显示,同时根据报警类型设置提示文字和改变颜色;最后就是温湿度的阈值设置功能。

4.2 创建模型文件

        首先创建C++模型文件ModelTh01,该类继承于BaseModel类,

        

4.3 添加基础功能

        首先定义跟嵌入式端一样的命令类型,这个务必保持一致,命令值有修改需要两端同时修改;然后是基本的模型显示、隐藏函数和数据接口函数,这些接口都是通用的,不过不同的物模型需要自定义,具体如下图所示,都有注释。

        这些函数的具体代码如下,有些还不完善:

#include "ModelTh01.h"

ModelTh01::ModelTh01(QObject *parent) : BaseModel(parent)
{
    m_secTickets=0;
    checkTimer = new QTimer(this);
    checkTimer->setInterval(1*1000);//心跳检测
    checkTimer->start(); 
    connect(checkTimer, SIGNAL(timeout()),this,SLOT(slotCheckTimeout()));
}


void ModelTh01::slotCheckTimeout(void)
{
        m_secTickets++;
        if(m_onlineState>0)//离线检测
        {
            if(m_secTickets - m_onlineTime>40)
            {
                m_onlineState=DEV_STATE_OFF_LINE;       
            }
            else
            {
                m_onlineState=DEV_STATE_ON_LINE;
            }
//            emit siqUpdateOnlineState(m_onlineState);   
        }

}

QByteArray ModelTh01::takeModelPassword(u8 index)
{
    static u8 passwd_table[5][16]={
        0x9D, 0x53, 0x09, 0xBF, 0x75, 0x28, 0xDE, 0x94, 0x4A, 0xFD, 0xB3, 0x69, 0x1F, 0xD2, 0x88, 0x3E, 
        0xF4, 0xAA, 0x5D, 0x13, 0xC9, 0x7F, 0x31, 0xE7, 0x9D, 0x53, 0x06, 0xBC, 0x72, 0x28, 0xDB, 0x91,
        0x47, 0xFD, 0xB3, 0x66, 0x1C, 0xD2, 0x88, 0x3B, 0xF1, 0xB5, 0x75, 0x39, 0xFA, 0xBE, 0x7E, 0x42,
        0x03, 0xC7, 0x88, 0x4B, 0x0C, 0xD0, 0x91, 0x54, 0x15, 0xD9, 0x9A, 0x5E, 0x21, 0xE2, 0xA6, 0x67, 
        0x2A, 0xEB, 0xAF, 0x70, 0x34, 0xF4, 0xB8, 0x79, 0x3D, 0xFD, 0xC1, 0x82, 0x46, 0x06, 0xCA, 0x8B, 
    };
    if(index>=5)index=0;
    
    QByteArray ba;
    ba.setRawData((char*)&passwd_table[index][0], 16);
    return ba;
    
}


void ModelTh01::showModel(QObject *parent)
{
    hideModel();
    m_modelParent=parent; 
    if(m_modelEngine==nullptr)
    {
        m_modelEngine=new QQmlApplicationEngine(this);
        m_modelEngine->rootContext()->setContextProperty("theModelTh01", this);
        m_modelEngine->rootContext()->setContextProperty("theCenterMan", this->parent());
    }
    m_modelEngine->load("qrc:/qmlRC/modelQml/TH01/ModelTh01.qml");
}

void ModelTh01::hideModel(void)
{
    if(m_modelEngine)
        delete m_modelEngine;
    m_modelEngine=nullptr;
    m_modelParent=nullptr;
}


void ModelTh01::showSimple(QObject *parent)
{
    m_simpleParent=parent; 
    if(m_simpleEngine==nullptr)
    {
        m_simpleEngine=new QQmlApplicationEngine(this);
        m_simpleEngine->rootContext()->setContextProperty("theModelTh01", this);
        m_simpleEngine->rootContext()->setContextProperty("theCenterMan", this->parent());
    }
    m_simpleEngine->load("qrc:/qmlRC/modelQml/TH01/SimpleTh01.qml");
}

void ModelTh01::hideSimple(void)
{
    if(m_simpleEngine)
        delete m_simpleEngine;
    m_simpleEngine=nullptr;
    m_simpleParent=nullptr;
}

int ModelTh01::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 TH01_CMD_DATA://状态数据
        {

            break;
        }
        case TH01_CMD_THRESH://阈值数据
        {
    
            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;
}
4.4 数据更新

        现在,我们需要对温湿度本身的功能进行完善,首先是定义信号函数,把状态数据发送到前端显示,内容有温度值、温度报警状态,湿度值、湿度报警状态;另一方面,还有温度阈值和湿度阈值,所以在此定义了两个信号函数:

        这两个函数是在数据解析函数里调用的,数据解析跟嵌入式端的数据组合正好是相反的过程,具体看下面的代码;同时,我们对设备的在线状态进行了更新。


int ModelTh01::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 TH01_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;
            u8 alarm_type=pData[0];
            pData+=1;
            
            QString temp_str=QString::asprintf("%.1f", temp_f);
            QString humi_str=QString::asprintf("%.1f", humi_f);
            emit siqUpdateSensorValues(temp_str, humi_str, alarm_type>>4, alarm_type&0x0F);
            break;
        }
        case TH01_CMD_THRESH://阈值数据
        {
            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;
            
            QString temp_str=QString::asprintf("%.1f", temp_f);
            QString humi_str=QString::asprintf("%.1f", humi_f);
            emit siqUpdateThresh(temp_str, humi_str);
            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;
}
4.5 阈值设置

        阈值设置需要提供函数接口给前端调用,根据QML的特性,C++端的函数加上Q_INVOKABLE关键字后就可以暴露给前端QML了,传入参数一般是字符串,具体内容让C++方面来判断比较方便,最后把结果返回显示就行。对于阈值设置,根据文档定义组合数据,具体的以下两个函数就解决了。


QString ModelTh01::setTempThresh(QString temp_str)
{
    float temp_f=temp_str.toFloat();
    if(temp_f<0.f || temp_f>120.f)
    {
        return QString("输入范围有误!");
    }
    
    u16 temp_u16=temp_f*10+1000;
    u8 make_buff[100]={0};
    u16 make_len=0;
    make_buff[make_len++]=temp_u16>>8;
    make_buff[make_len++]=temp_u16;
    emit sigSendDownMsg(m_appID, m_devSn, m_downPackNum++, TH01_CMD_SET_TEMP, make_buff, make_len);
    return "";
}

QString ModelTh01::setHumiThresh(QString humi_str)
{
    float humi_f=humi_str.toFloat();
    if(humi_f<20.f || humi_f>100.f)
    {
        return QString("输入范围有误!");
    }
    
    u16 humi_u16=humi_f*10;
    u8 make_buff[100]={0};
    u16 make_len=0;
    make_buff[make_len++]=humi_u16>>8;
    make_buff[make_len++]=humi_u16;
    emit sigSendDownMsg(m_appID, m_devSn, m_downPackNum++, TH01_CMD_SET_HUMI, make_buff, make_len);
    return "";
}

        这样,物模型的C++后端就完成了,整体看来,跟C++也没多大关系,所以只要有点C语言的基础,开发物模型很容易就上手了。

4.6 模型添加

        基本模型开发完成后可以添加到主程序内,这样便于后续前端的调试开发,根据下面图片步骤在CenterMan类中操作即可。


      密码添加

五、用户前端开发
5.1 界面分析

        再次看下这个界面,主要就是一个渐变色背景+数值显示,温湿度有各自的状态提示;最后就是在合适的位置加入阈值设置的功能。

5.2 简易模型

        先搞定网格内的简易模型,对于温湿度计较为简单,就是图片替换,底部温湿度值显示即可。那么,我们要先准备好图片和创建简易模型文件,简易模型继承于BaseSimpleView,这个文件已经有了基本布局,只要修改内容即可。添加或者删除QML文件后需要鼠标右键项目工程,点击  执行qmake 才能正确编译。

        然后再加入一些模型自身的内容,即温湿度显示,主要就是接收来自后端的更新数据了,在温湿度显示那里,我们对报警状态用红色字体显示。

有了这个基本模型,就可以添加看看效果了。在主页的右上角 手动添加即可,SN要输入正确,具体效果如下,照片可以选择自己喜欢的。

        

5.3 完整模型

        完整模型也需要再建立文件,该文件继承于BaseModelView,文件主要是对颜色进行了配置,整体效果如下。

        然后就是数据显示模块了,由于温湿度显示结构基本一致,主要是内容差异,所以显示单元可以做成一个组件模块,然后在主页面里配置即可。该模块的效果和代码如下:

import QtQuick 2.7
import "../base"

//数据显示单元
Item {
    
    signal siqThreshClicked()
    property var headText: "温度 | 正常"
    property var valueText: "26.3"
    property var valueColor: "white"
    property var unionText: "℃"
    property var threshText: "50.0"
    implicitWidth: 300
    implicitHeight: 200
    
    
    Text{
        id:id_headText //头部标题
        height: 40
        width: 160
        anchors
        {
            horizontalCenter:parent.horizontalCenter
            top:parent.top
        }
        text: headText
        font.pointSize: 18
        font.family: Qt.platform.os === "windows" ? "宋体" : "黑体"
        color: "white"
        verticalAlignment: Text.AlignVCenter
        horizontalAlignment: Text.AlignHCenter
    }
    
    Text{
        id:id_valueText //数值
        height: 100
        width: 150
        anchors
        {
            horizontalCenter:parent.horizontalCenter
            top:id_headText.bottom
            topMargin:10
        }
        text: valueText
        font.pointSize: 50
        font.family: Qt.platform.os === "windows" ? "宋体" : "黑体"
        color: valueColor
        verticalAlignment: Text.AlignVCenter
        horizontalAlignment: Text.AlignHCenter
    }
    
    Text{
        id:id_unionText //单位
        height: 40
        width: 60
        anchors
        {
            right:id_headText.right
            top:id_valueText.top
        }
        text: unionText
        font.pointSize: 15
        font.family: Qt.platform.os === "windows" ? "宋体" : "黑体"
        color: "white"
        verticalAlignment: Text.AlignVCenter
        horizontalAlignment: Text.AlignRight
    }
    
    Text{
        id:id_threshText //阈值
        height: 40
        width: 40
        anchors
        {
            horizontalCenter:id_valueText.horizontalCenter
            top:id_valueText.bottom
        }
        text: threshText
        font.pointSize: 15
        font.family: Qt.platform.os === "windows" ? "宋体" : "黑体"
        font.bold: id_mouseArea.pressed
        color: "white"
        verticalAlignment: Text.AlignVCenter
        horizontalAlignment: Text.AlignRight
        MouseArea
        {
            id:id_mouseArea
            anchors.fill: parent
            onClicked: 
            {
                siqThreshClicked()
            }
        }
    }
    Text{
        id:id_union2Text //单位
        height: 40
        width: 60
        anchors
        {
            left:id_threshText.right
            verticalCenter:id_threshText.verticalCenter
        }
        text: unionText
        font.pointSize: 15
        font.family: Qt.platform.os === "windows" ? "宋体" : "黑体"
        color: "white"
        verticalAlignment: Text.AlignVCenter
        horizontalAlignment: Text.AlignLeft
    }
    
}

        底部比较小的数值即阈值,点击文本就会弹出设置对话框。这里的对话框与主程序的对话框类似,因为物模型的qml和主程序的qml是隔离的,所以主程序的基础文件可以根据需要迁移到物模型这边过来。

        那么,对于阈值对话框就可以从BaseEditDialog这边继承过来了。

        在完整模型文件内,对模块参数进行配置,定义了温度模块和湿度模块,设置对话框根据打开类型进行温湿度设置的区分,具体如下:

        完整模型内,数据更新是通过后端发送过来的数据进行判断显示,具体如下:

        最后,在打开完整模型后,前端应该主动请求一下阈值数据,

        这样,整个物模型的开发基本完成了。

六、模型联调

        接下来就是整个物模型的联调了,首先设备端直接用代码配置一下app_id和dev_sn,然后注释再重新编译烧录,这样设备就变成有设备号的初始状态了。

        QT端重新执行qmake,然后编译运行,等设备差不多联网成功后,手动添加设备,SN为A1080123,添加成功后简易模型和完整模型的界面如下所示:

                     

        下面是演示视频:

温湿度计

        如果设置和颜色变化没问题,那这个模型基本上也就成了。

        总体来讲,整个物模型的开发和上线还是挺简单的,相比于用json描述物模型,直接代码操作灵活性会更好;在编写物模型的过程中,虽然用到了C语言和QT的C++、QML,但是后端内容跟C++关系并不大,照着其它已有模型做更改就行了,只要遵循一些C++的代码格式即可;而QML类似于js语言,js语言与C语言本质上属于同一语系,语法上很相似,只要多做几个界面,QML很容易就上手了。

        所以,端点物联的开发套件很适合嵌入式端的开发人员做一些自己的创意产品,有C语言基础之后,对照着其它模型、按自己的想法更改,就能快速开发、上线一款自己的物联网产品。相比于米家、涂鸦、阿里云这些大平台,流程和难度降低了很多,很适合个人开发者。

本节的嵌入式工程:https://download.csdn.net/download/ypp240124016/89428338

本节的QT工程:https://download.csdn.net/download/ypp240124016/89428344

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/705325.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

HCIA6以太网基础基于MAC划分VLAN

&#xff08;简写的命令可以敲Tab按键补全剩余&#xff09; 1.组网需求 场景&#xff1a;公司的网络中&#xff0c;管理者将同一部门的员工划分到VLAN10。要求只有本部门员工的PC接入才能互访&#xff0c;其他PC接入交换机属于其他VLAN&#xff08;666&#xff09;。可以配置…

Android WebSocket长连接的实现

一、为什么需要 WebSocket 初次接触 WebSocket 的人&#xff0c;都会问同样的问题&#xff1a;我们已经有了 HTTP 协议&#xff0c;为什么还需要另一个协议&#xff1f;它能带来什么好处&#xff1f; 答案很简单&#xff0c;因为 HTTP 协议有一个缺陷&#xff1a;通信只能由客…

【Three.js】知识梳理十九:线性雾(Fog)、指数雾(FogExp2)和范围雾(RangeFog)

雾是3D图形中创建深度和氛围的重要工具。Three.js提供了多种类型的雾&#xff1a;线性雾&#xff08;THREE.Fog&#xff09;&#xff0c;指数雾&#xff08;THREE.FogExp2&#xff09;和范围雾&#xff08;RangeFog&#xff09;。本文将探讨这三种类型的雾&#xff0c;通过代码…

OLED柔性屏的显示效果如何

OLED柔性屏的显示效果非常出色&#xff0c;具有多方面的优势。以下是关于OLED柔性屏显示效果的详细分析&#xff1a; 色彩表现&#xff1a;OLED柔性屏的每个像素都可以独立发光&#xff0c;因此色彩准确性极高。黑色呈现得非常深邃&#xff0c;而亮部则展现出鲜明而生动的细节。…

Python-docx将Word文档的目录或文本框作为普通段落读入

&#x1f4e2;作者&#xff1a; 小小明-代码实体 &#x1f4e2;博客主页&#xff1a;https://blog.csdn.net/as604049322 &#x1f4e2;欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1f4dd; 欢迎讨论&#xff01; 昨天我们处理Word文档的自动编号&#xff0c;详见《Python解析Wo…

maven 显式依赖包包含隐式依赖包,引起依赖包冲突

问题&#xff1a;FlinkCDC 3.0.1 代码 maven依赖包冲突 什么是依赖冲突 依赖冲突是指项目依赖的某一个jar包&#xff0c;有多个不同的版本&#xff0c;因而造成类包版本冲突 依赖冲突的原因 依赖冲突很经常是类包之间的间接依赖引起的。每个显式声明的类包都会依赖于一些其它…

springbot 界面美观的超市收银管理系统。

springbot 界面美观的超市收银管理系统。 功能&#xff1a;登录&#xff0c;用户管理&#xff0c;权限菜单管理&#xff0c;首页订单&#xff0c;收入&#xff0c;用户统计&#xff0c; 收银台&#xff0c;销售账单&#xff0c;库存管理&#xff0c;商品分类&#xff0c;供应…

如何在浏览器书签栏设置2个书签实现一键到达网页顶部和底部

本次设置浏览器为&#xff1a;Chrome浏览器&#xff08;其他浏览器可自行测试&#xff09; 1&#xff0c;随便收藏一个网页到浏览器书签栏 2&#xff0c;右键这个书签 3&#xff0c;修改 4&#xff0c;修改名称 5&#xff0c;修改网址&#xff1a; javascript:(function(…

Vue3中使用深度选择器不起作用

问题&#xff1a; 想要给这个菜单设置高度100%&#xff0c;使用深度样式选择器无效 这样写无效 但是如下在控制台写是有效果的 解决&#xff1a; 参考 解决方法是给这个组件增加一个根元素&#xff0c;然后再使用深度选择器

【Linux】线程(一)

谈论之前需要先谈论一些线程的背景知识 其中就有进程地址空间&#xff0c;又是这个让我们又爱又恨的东西 目录 背景知识&#xff1a;地址空间&#xff1a; 背景知识&#xff1a; 地址空间&#xff1a; 说在前边&#xff0c;OS通常分为4个核心模块&#xff1a;执行流管理&…

IDEA项目上传Github流程+常见问题解决

一、Github上创建仓库 项目创建好后如图所示 二、IDEA连接Github远程仓库 管理远程 复制远程地址 定义远程 登录Github 点击进入File->Settings->Version Control->Github登录自己的账号并勾上“√” 三、推送项目 点击推送 修改为main 点击确定&#xff0c;打开远程…

Python基础教程(十六):正则表达式

&#x1f49d;&#x1f49d;&#x1f49d;首先&#xff0c;欢迎各位来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里不仅可以有所收获&#xff0c;同时也能感受到一份轻松欢乐的氛围&#xff0c;祝你生活愉快&#xff01; &#x1f49d;&#x1f49…

Linux服务器快速下载GoogleDriver小技巧——利用gdown工具

Linux服务器快速下载GoogleDriver小技巧——利用gdown工具 1. 安装gdown pip install gdown安装好后如果在终端输入gdown显示如下错误&#xff1a;gdown: command not found&#xff0c;则说明gdown默认安装的位置需要软链接一下&#xff0c;执行以下命令&#xff1a; sudo …

Qt全局快捷键QGlobalHotKey的自研之路

这两天对Qt的快捷键格外感兴趣。 前两天在使用QHotKey的过程中&#xff0c;发现不能定义小键盘键盘码&#xff0c;自己二次修改了该库之后已经可以设置小键盘快捷键了。文章在这里&#xff1a;Qt第三方库QHotKey设置小键盘数字快捷键。 昨天突发奇想&#xff1a;目前所有的快…

前端项目打包部署

打包 vue-cli脚手架的前端项目&#xff0c;点击npm脚本中的第二条编译命令&#xff0c;即可将项目编译&#xff0c;生成一个dist的文件夹&#xff0c;里面存放的就是编译好的前端项目文件&#xff0c;没有脚手架就在终端敲击npm run build命令编译前端项目 部署 Nginx 介绍:…

D咖饮品机入驻奇轩商贸,为DF101大规模入驻荆州拉开序幕

荆州&#xff0c;一座历史悠久的城市&#xff0c;如今正焕发着新的活力与魅力。而这股活力的源泉之一&#xff0c;正是奇轩商贸的一次创新尝试——D咖智能饮品机的入驻。这不仅仅是一次机器设备的更新&#xff0c;更是一场技术与美味的碰撞&#xff0c;为DF101大规模入驻荆州市…

汽车EDI:BRP EDI项目案例

项目背景 BRP Inc.使用EDI&#xff08;电子数据交换&#xff09;来处理其与供应商、客户和合作伙伴之间的业务交流。通过EDI&#xff0c;BRP可以在各种业务流程中自动化数据交换&#xff0c;例如采购订单、发货通知、发票、付款和库存信息等&#xff0c;从而提高操作效率、降低…

【小白学Python】自定义图片的生成(二)

Python学习 【小白学Python】自定义图片的生成&#xff08;一&#xff09; 目录 1. 文件内容2.生成图片规则3. 修改代码2.1 尝试一行汉字展示3.1 读取txt文件3.2 解决文字过长问题3.3 删减指定文字 4. 总结 1. 文件内容 正如上篇文章所说&#xff0c;我需要读取txt文件的文字内…

KUKA机器人KRC5控制柜面板LED显示

对于KUKA机器人新系列控制柜KRC5控制柜来说&#xff0c;其控制柜面板LED布局如下图&#xff1a; 其中①②③④分别为&#xff1a; 1、机器人控制柜处于不同状态时&#xff0c;LED显示如下&#xff1a; 2、机器人控制柜正在运行时&#xff1a; 3、机器人控制柜运行时出现的故障…

数据结构重要知识总结

数组 数组&#xff08;Array&#xff09; 是一种很常见的数据结构。它由相同类型的元素&#xff08;element&#xff09;组成&#xff0c;并且是使用一块连续的内存来存储。 我们直接可以利用元素的索引&#xff08;index&#xff09;可以计算出该元素对应的存储地址。 数组…