本文主要是介绍端点物联开发教程之(二)开发演示,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
目录
一、产品定义
二、基础资源下载
三、嵌入式端开发
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--PB9RCC_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: 300implicitHeight: 200Text{id:id_headText //头部标题height: 40width: 160anchors{horizontalCenter:parent.horizontalCentertop:parent.top}text: headTextfont.pointSize: 18font.family: Qt.platform.os === "windows" ? "宋体" : "黑体"color: "white"verticalAlignment: Text.AlignVCenterhorizontalAlignment: Text.AlignHCenter}Text{id:id_valueText //数值height: 100width: 150anchors{horizontalCenter:parent.horizontalCentertop:id_headText.bottomtopMargin:10}text: valueTextfont.pointSize: 50font.family: Qt.platform.os === "windows" ? "宋体" : "黑体"color: valueColorverticalAlignment: Text.AlignVCenterhorizontalAlignment: Text.AlignHCenter}Text{id:id_unionText //单位height: 40width: 60anchors{right:id_headText.righttop:id_valueText.top}text: unionTextfont.pointSize: 15font.family: Qt.platform.os === "windows" ? "宋体" : "黑体"color: "white"verticalAlignment: Text.AlignVCenterhorizontalAlignment: Text.AlignRight}Text{id:id_threshText //阈值height: 40width: 40anchors{horizontalCenter:id_valueText.horizontalCentertop:id_valueText.bottom}text: threshTextfont.pointSize: 15font.family: Qt.platform.os === "windows" ? "宋体" : "黑体"font.bold: id_mouseArea.pressedcolor: "white"verticalAlignment: Text.AlignVCenterhorizontalAlignment: Text.AlignRightMouseArea{id:id_mouseAreaanchors.fill: parentonClicked: {siqThreshClicked()}}}Text{id:id_union2Text //单位height: 40width: 60anchors{left:id_threshText.rightverticalCenter:id_threshText.verticalCenter}text: unionTextfont.pointSize: 15font.family: Qt.platform.os === "windows" ? "宋体" : "黑体"color: "white"verticalAlignment: Text.AlignVCenterhorizontalAlignment: 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
这篇关于端点物联开发教程之(二)开发演示的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!