模块学习4:(1)通过MQTT协议和电信云平台的通信(内附MQTT协议V3.1.1的原版和中文参考资料)
这里就不对MQTT协议本身作过多的讲解了,网上也有很多更加详细的对MQTT博文的讲解,当然更直接的就是参看协议文件。
这里直接贴一篇写的比较完整的博客和菜鸟教材对协议的讲解,可以参考学习。
菜鸟教程,MQTT协议入门
MQTT协议详解
之前买了一块超纬电子出的一款开发板,进行ESP8266、以太网、WiFi和MQTT协议连接云平台相关实验的学习。原本MQTT协议的源代码是一个比较大的的工程,适用于多个平台,所以在使用的时候实际上也只用了一部分功能,它对其进行了单独封装,所以可读性和适用性更强,所以我主要参看了这部分代码,实现了我的目的。实现我手上的开发板和电信云平台的交互功能!
MQTT工程包括这么多文件。
主函数
前面的联网,使用以太网或者WIFI都可以,具体过程这里就省略了。直接从连接后的代码开始,当初学习的代码,注释很详细。
switch(getSn_SR(SOCK_TCPS)) //获取TCP链接端口的状态
{case SOCK_INIT: if(Connect_flag==0){ ret1 = connect(SOCK_TCPS,ServerIP,ServerPort); //链接服务器printf("%d.%d.%d.%d\r\n",ServerIP[0],ServerIP[1],ServerIP[2],ServerIP[3]); //串口输出信息printf("ServerPort = %d\r\n",ServerPort); //串口输出信息printf("连接服务器返回码:%d\r\n",ret1); //串口输出信息}break;case SOCK_ESTABLISHED: // Socket处于连接建立状态if(getSn_IR(SOCK_TCPS) & Sn_IR_CON) {printf("连接已建立\r\n"); MQTT_Buff_Init(); //初始化接收,发送,命令数据的 缓冲区 以及各状态参数setSn_IR(SOCK_TCPS, Sn_IR_CON); // Sn_IR的CON位置1,通知W5500连接已建立Connect_flag = 1; //链接标志=1}ret2 = recv(SOCK_TCPS,gDATABUF,DATA_BUF_SIZE); //接收数据if(ret2 > 0){ //如果ret大于0,表示有数据来 memcpy(&MQTT_RxDataInPtr[2],gDATABUF,ret2); //拷贝数据到接收缓冲区MQTT_RxDataInPtr[0] = ret2/256; //记录数据长度MQTT_RxDataInPtr[1] = ret2%256; //记录数据长度 MQTT_RxDataInPtr+=RBUFF_UNIT; //指针下移 if(MQTT_RxDataInPtr==MQTT_RxDataEndPtr) //如果指针到缓冲区尾部了MQTT_RxDataInPtr = MQTT_RxDataBuf[0]; //指针归位到缓冲区开头}if(Connect_flag==1){ /*-------------------------------------------------------------*//* 处理发送缓冲区数据 *//*-------------------------------------------------------------*/if(MQTT_TxDataOutPtr != MQTT_TxDataInPtr){ //if成立的话,说明发送缓冲区有数据了//3种情况可进入if//第1种:0x10 连接报文//第2种:0x82 订阅报文,且ConnectPack_flag置位,表示连接报文成功//第3种:SubcribePack_flag置位,说明连接和订阅均成功,其他报文可发if((MQTT_TxDataOutPtr[2]==0x10)||((MQTT_TxDataOutPtr[2]==0x82)&&(ConnectPack_flag==1))||(SubcribePack_flag==1)){ printf("发送数据:0x%x\r\n",MQTT_TxDataOutPtr[2]); //串口提示信息MQTT_TxData(MQTT_TxDataOutPtr); //发送数据MQTT_TxDataOutPtr += TBUFF_UNIT; //指针下移if(MQTT_TxDataOutPtr==MQTT_TxDataEndPtr) //如果指针到缓冲区尾部了MQTT_TxDataOutPtr = MQTT_TxDataBuf[0]; //指针归位到缓冲区开头} }/*-------------------------------------------------------------*//* 处理接收缓冲区数据 *//*-------------------------------------------------------------*/if(MQTT_RxDataOutPtr != MQTT_RxDataInPtr){ //if成立的话,说明接收缓冲区有数据了 printf("接收到数据: \r\n");/*-----------------------------------------------------*//* 处理CONNACK报文 *//*-----------------------------------------------------*/ //if判断,如果一共接收了4个字节,第一个字节是0x20,表示收到的是CONNACK报文//接着我们要判断第4个字节,看看CONNECT报文是否成功if(MQTT_RxDataOutPtr[2]==0x20 && ConnectPack_flag == 0){ switch(MQTT_RxDataOutPtr[5]){ case 0x00 : printf("CONNECT报文成功\r\n"); //串口输出信息 ConnectPack_flag = 1; //CONNECT报文成功,订阅报文可发// MQTT_Subscribe(S_TOPIC_NAME,1); //发送缓冲区添加订阅topic,等级1
// printf("new MQTT_TxDataOutPtr[6]: %x\r\n", MQTT_TxDataOutPtr[6]);break; //跳出分支case 0x00 case 0x01 : printf("连接已拒绝,不支持的协议版本,准备重启\r\n"); //串口输出信息NVIC_SystemReset(); //重启break; //跳出分支case 0x01 case 0x02 : printf("连接已拒绝,不合格的客户端标识符,准备重启\r\n"); //串口输出信息NVIC_SystemReset(); //重启break; //跳出分支case 0x02 case 0x03 : printf("连接已拒绝,服务端不可用,准备重启\r\n"); //串口输出信息NVIC_SystemReset(); //重启break; //跳出分支case 0x03case 0x04 : printf("连接已拒绝,无效的用户名或密码,准备重启\r\n"); //串口输出信息NVIC_SystemReset(); //重启break; //跳出分支case 0x04case 0x05 : printf("连接已拒绝,未授权,准备重启\r\n"); //串口输出信息NVIC_SystemReset(); //重启break; //跳出分支case 0x05 default : printf("连接已拒绝,未知状态,准备重启\r\n"); //串口输出信息 NVIC_SystemReset(); //重启break; //跳出分支case default } } //if判断,如果一共接收了5个字节,第一个字节是0x90,表示收到的是SUBACK报文//接着我们要判断订阅回复,看看是不是成功else if(MQTT_RxDataOutPtr[2]==0x90){ switch(MQTT_RxDataOutPtr[6]){ case 0x00 :case 0x01 : printf("订阅成功+++\r\n"); //串口输出信息SubcribePack_flag = 1; //SubcribePack_flag置1,表示订阅报文成功,其他报文可发送//TIM3_ENABLE_30S(); //启动30s的PING定时器break; //跳出分支 default: printf("订阅失败,准备重启\r\n"); //串口输出信息 NVIC_SystemReset(); //重启break; //跳出分支 } }//if判断,如果一共接收了2个字节,第一个字节是0xD0,表示收到的是PINGRESP报文else if(MQTT_RxDataOutPtr[2]==0xD0){ printf("PING报文回复\r\n"); } //if判断,如果第一个字节是0x30,表示收到的是服务器发来的推送数据//我们要提取控制命令else if(MQTT_RxDataOutPtr[2]==0x30){ printf("服务器等级0推送\r\n"); //串口输出信息 MQTT_DealPushdata_Qs0(MQTT_RxDataOutPtr); //处理等级0推送数据} MQTT_RxDataOutPtr += RBUFF_UNIT; //指针下移if(MQTT_RxDataOutPtr==MQTT_RxDataEndPtr) //如果指针到缓冲区尾部了MQTT_RxDataOutPtr = MQTT_RxDataBuf[0]; //指针归位到缓冲区开头 }//处理接收缓冲区数据的else if分支结尾 /*-------------------------------------------------------------*//* 处理命令缓冲区数据 *//*-------------------------------------------------------------*/if(MQTT_CMDOutPtr != MQTT_CMDInPtr){ //if成立的话,说明命令缓冲区有数据了 if(Upload_data_succeed == 0){Upload_data();printf("Upload_data succeed+++ \r\n");}printf("命令:%s\r\n",&MQTT_CMDOutPtr[2]); MQTT_CMDOutPtr += CBUFF_UNIT; //指针下移if(MQTT_CMDOutPtr==MQTT_CMDEndPtr) //如果指针到缓冲区尾部了MQTT_CMDOutPtr = MQTT_CMDBuf[0]; //指针归位到缓冲区开头 }if(Upload_data_succeed == 0 && SubcribePack_flag == 1){Upload_data();Upload_data_succeed = 1;}} break;
case SOCK_CLOSE_WAIT: // Socket处于等待关闭状态disconnect(SOCK_TCPS); break;
case SOCK_CLOSED: // Socket处于关闭状态socket(SOCK_TCPS,Sn_MR_TCP,5000,0x00); // 打开Socket0,打开一个本地端口 mqtt1_flag=0;NTP_Timeouttimer_Start=1;break;
}
下面是对应的功能函数,基本上是从原来的工程文件中提取出来的。
云平台的相关参数初始化
/*----------------------------------------------------------*/
/*函数名:云初始化参数,得到客户端ID,用户名和密码 */
/*参 数:无 */
/*返回值:无 */
/*----------------------------------------------------------*/
void IoT_Parameter_Init(void)
{ memset(ClientID,128,0); //客户端ID的缓冲区全部清零sprintf(ClientID,"%s",PROJECTID); //构建客户端ID,并存入缓冲区ClientID_len = strlen(ClientID); //计算客户端ID的长度memset(Username,128,0); //用户名的缓冲区全部清零sprintf(Username,"%s",DEVICENAME); //构建用户名,并存入缓冲区Username_len = strlen(Username); //计算用户名的长度memset(Passward,128,0); //密码的缓冲区全部清零sprintf(Passward,"%s",DEVICESECRE); //构建密码 Passward_len = strlen(Passward); //计算密码的长度memset(ServerName,128,0); sprintf((char*)ServerName,"chongqing-mqtt.ctwing.cn"); //构建服务器域名ServerPort = 1883; //服务器端口号1883printf("服 务 器:%s\r\n",ServerName); printf("客户端ID:%s\r\n",ClientID); printf("用 户 名:%s\r\n",Username);printf("密 码:%s\r\n",Passward);
}
连接服务器报文
/*----------------------------------------------------------*/
/*函数名:连接服务器报文 */
/*参 数:无 */
/*返回值:无 */
/*----------------------------------------------------------*/
void MQTT_ConectPack(void)
{ int temp,Remaining_len;Fixed_len = 1; //连接报文中,固定报头长度暂时先=1Variable_len = 10; //连接报文中,可变报头长度=10Payload_len = 2 + ClientID_len + 2 + Username_len + 2 + Passward_len; //连接报文中,负载长度 Remaining_len = Variable_len + Payload_len; //剩余长度=可变报头长度+负载长度temp_buff[0]=0x10; //固定报头第1个字节 :固定0x01 do{ //循环处理固定报头中的剩余长度字节,字节量根据剩余字节的真实长度变化temp = Remaining_len%128; //剩余长度取余128Remaining_len = Remaining_len/128; //剩余长度取整128if(Remaining_len>0) temp |= 0x80; //按协议要求位7置位 temp_buff[Fixed_len] = temp; //剩余长度字节记录一个数据Fixed_len++; //固定报头总长度+1 }while(Remaining_len>0); //如果Remaining_len>0的话,再次进入循环temp_buff[Fixed_len+0]=0x00; //可变报头第1个字节 :固定0x00 temp_buff[Fixed_len+1]=0x04; //可变报头第2个字节 :固定0x04temp_buff[Fixed_len+2]=0x4D; //可变报头第3个字节 :固定0x4Dtemp_buff[Fixed_len+3]=0x51; //可变报头第4个字节 :固定0x51temp_buff[Fixed_len+4]=0x54; //可变报头第5个字节 :固定0x54temp_buff[Fixed_len+5]=0x54; //可变报头第6个字节 :固定0x54temp_buff[Fixed_len+6]=0x04; //可变报头第7个字节 :固定0x04temp_buff[Fixed_len+7]=0xC2; //可变报头第8个字节 :使能用户名和密码校验,不使用遗嘱,不保留会话temp_buff[Fixed_len+8]=0x00; //可变报头第9个字节 :保活时间高字节 0x00temp_buff[Fixed_len+9]=0x64; //可变报头第10个字节:保活时间高字节 0x64 100s/* CLIENT_ID */temp_buff[Fixed_len+10] = ClientID_len/256; //客户端ID长度高字节temp_buff[Fixed_len+11] = ClientID_len%256; //客户端ID长度低字节memcpy(&temp_buff[Fixed_len+12],ClientID,ClientID_len); //复制过来客户端ID字串 /* 用户名 */temp_buff[Fixed_len+12+ClientID_len] = Username_len/256; //用户名长度高字节temp_buff[Fixed_len+13+ClientID_len] = Username_len%256; //用户名长度低字节memcpy(&temp_buff[Fixed_len+14+ClientID_len],Username,Username_len); //复制过来用户名字串 /* 密码 */temp_buff[Fixed_len+14+ClientID_len+Username_len] = Passward_len/256; //密码长度高字节temp_buff[Fixed_len+15+ClientID_len+Username_len] = Passward_len%256; //密码长度低字节memcpy(&temp_buff[Fixed_len+16+ClientID_len+Username_len],Passward,Passward_len); //复制过来密码字串TxDataBuf_Deal(temp_buff, Fixed_len + Variable_len + Payload_len); //加入发送数据缓冲区
}
SUBSCRIBE订阅topic报文
/*----------------------------------------------------------*/
/*函数名:SUBSCRIBE订阅topic报文 */
/*参 数:QoS:订阅等级 */
/*参 数:topic_name:订阅topic报文名称 */
/*返回值:无 */
/*----------------------------------------------------------*/
void MQTT_Subscribe(char *topic_name, int QoS)
{ Fixed_len = 2; //SUBSCRIBE报文中,固定报头长度=2Variable_len = 2; //SUBSCRIBE报文中,可变报头长度=2 Payload_len = 2 + strlen(topic_name) + 1; //计算有效负荷长度 = 2字节(topic_name长度)+ topic_name字符串的长度 + 1字节服务等级temp_buff[0]=0x82; //第1个字节 :固定0x82 temp_buff[1]=Variable_len + Payload_len; //第2个字节 :可变报头+有效负荷的长度 temp_buff[2]=0x00; //第3个字节 :报文标识符高字节,固定使用0x00temp_buff[3]=0x01; //第4个字节 :报文标识符低字节,固定使用0x01temp_buff[4]=strlen(topic_name)/256; //第5个字节 :topic_name长度高字节temp_buff[5]=strlen(topic_name)%256; //第6个字节 :topic_name长度低字节memcpy(&temp_buff[6],topic_name,strlen(topic_name)); //第7个字节开始 :复制过来topic_name字串 temp_buff[6+strlen(topic_name)]=QoS; //最后1个字节:订阅等级TxDataBuf_Deal(temp_buff, Fixed_len + Variable_len + Payload_len); //加入发送数据缓冲区printf("存储订阅成功=================\r\n");
}
等级0 发布消息报文
/*----------------------------------------------------------*/
/*函数名:等级0 发布消息报文 */
/*参 数:topic_name:topic名称 */
/*参 数:data:数据 */
/*参 数:data_len:数据长度 */
/*返回值:无 */
/*----------------------------------------------------------*/
void MQTT_PublishQs0(char *topic, char *data, int data_len)
{ int temp,Remaining_len;Fixed_len = 1; //固定报头长度暂时先等于:1字节Variable_len = 2 + strlen(topic); //可变报头长度:2字节(topic长度)+ topic字符串的长度Payload_len = data_len; //有效负荷长度:就是data_lenRemaining_len = Variable_len + Payload_len; //剩余长度=可变报头长度+负载长度temp_buff[0]=0x30; //固定报头第1个字节 :固定0x30 do{ //循环处理固定报头中的剩余长度字节,字节量根据剩余字节的真实长度变化temp = Remaining_len%128; //剩余长度取余128Remaining_len = Remaining_len/128; //剩余长度取整128if(Remaining_len>0) temp |= 0x80; //按协议要求位7置位 temp_buff[Fixed_len] = temp; //剩余长度字节记录一个数据Fixed_len++; //固定报头总长度+1 }while(Remaining_len>0); //如果Remaining_len>0的话,再次进入循环temp_buff[Fixed_len+0]=strlen(topic)/256; //可变报头第1个字节 :topic长度高字节temp_buff[Fixed_len+1]=strlen(topic)%256; //可变报头第2个字节 :topic长度低字节memcpy(&temp_buff[Fixed_len+2],topic,strlen(topic)); //可变报头第3个字节开始 :拷贝topic字符串 memcpy(&temp_buff[Fixed_len+2+strlen(topic)],data,data_len); //有效负荷:拷贝data数据TxDataBuf_Deal(temp_buff, Fixed_len + Variable_len + Payload_len); //加入发送数据缓冲区
}
处理服务器发来的等级0的推送
/*----------------------------------------------------------*/
/*函数名:处理服务器发来的等级0的推送 */
/*参 数:redata:接收的数据 */
/*返回值:无 */
/*----------------------------------------------------------*/
void MQTT_DealPushdata_Qs0(unsigned char *redata)
{int re_len; //定义一个变量,存放接收的数据总长度int pack_num; //定义一个变量,当多个推送一起过来时,保存推送的个数int temp,temp_len; //定义一个变量,暂存数据int totle_len; //定义一个变量,存放已经统计的推送的总数据量int topic_len; //定义一个变量,存放推送中主题的长度int cmd_len; //定义一个变量,存放推送中包含的命令数据的长度int cmd_loca; //定义一个变量,存放推送中包含的命令的起始位置int i; //定义一个变量,用于for循环int local,multiplier;unsigned char tempbuff[RBUFF_UNIT]; //临时缓冲区unsigned char *data; //redata过来的时候,第一个字节是数据总量,data用于指向redata的第2个字节,真正的数据开始的地方re_len = redata[0]*256+redata[1]; //获取接收的数据总长度 data = &redata[2]; //data指向redata的第2个字节,真正的数据开始的 pack_num = temp_len = totle_len = temp = 0; //各个变量清零local = 1;multiplier = 1;do{pack_num++; //开始循环统计推送的个数,每次循环推送的个数+1 do{temp = data[totle_len + local]; temp_len += (temp & 127) * multiplier;multiplier *= 128;local++;}while ((temp & 128) != 0);totle_len += (temp_len + local); //累计统计的总的推送的数据长度re_len -= (temp_len + local) ; //接收的数据总长度 减去 本次统计的推送的总长度 local = 1;multiplier = 1;temp_len = 0;}while(re_len!=0); //如果接收的数据总长度等于0了,说明统计完毕了printf("本次接收了%d个推送数据\r\n",pack_num);//串口输出信息temp_len = totle_len = 0; //各个变量清零local = 1;multiplier = 1;for(i=0;i<pack_num;i++){ //已经统计到了接收的推送个数,开始for循环,取出每个推送的数据 do{temp = data[totle_len + local]; temp_len += (temp & 127) * multiplier;multiplier *= 128;local++;}while ((temp & 128) != 0); topic_len = data[local+totle_len]*256+data[local+1+totle_len] + 2; //计算本次推送数据中主题占用的数据量cmd_len = temp_len-topic_len; //计算本次推送数据中命令数据占用的数据量cmd_loca = totle_len + local + topic_len; //计算本次推送数据中命令数据开始的位置memcpy(tempbuff,&data[cmd_loca],cmd_len); //命令数据拷贝出来 CMDBuf_Deal(tempbuff, cmd_len); //加入命令到缓冲区totle_len += (temp_len+local); //累计已经统计的推送的数据长度local = 1;multiplier = 1;temp_len = 0;}
}
PING报文,心跳包
/*----------------------------------------------------------*/
/*函数名:PING报文,心跳包 */
/*参 数:无 */
/*返回值:无 */
/*----------------------------------------------------------*/
void MQTT_PingREQ(void)
{temp_buff[0]=0xC0; //第1个字节 :固定0xC0 temp_buff[1]=0x00; //第2个字节 :固定0x00 TxDataBuf_Deal(temp_buff, 2); //加入数据到缓冲区
处理发送缓冲区
/*----------------------------------------------------------*/
/*函数名:处理发送缓冲区 */
/*参 数:data:数据 */
/*参 数:size:数据长度 */
/*返回值:无 */
/*----------------------------------------------------------*/
void TxDataBuf_Deal(unsigned char *data, int size)
{int i;memcpy(&MQTT_TxDataInPtr[2],data,size); //拷贝数据到发送缓冲区 MQTT_TxDataInPtr[0] = size/256; //记录数据长度MQTT_TxDataInPtr[1] = size%256; //记录数据长度printf("MQTT_TxDataInPtr=");for(i = 0; i<size; i++){printf("%x ", MQTT_TxDataInPtr[i]);}MQTT_TxDataInPtr+=TBUFF_UNIT; //指针下移if(MQTT_TxDataInPtr==MQTT_TxDataEndPtr) //如果指针到缓冲区尾部了MQTT_TxDataInPtr = MQTT_TxDataBuf[0]; //指针归位到缓冲区开头}
处理命令缓冲区
/*----------------------------------------------------------*/
/*函数名:处理命令缓冲区 */
/*参 数:data:数据 */
/*参 数:size:数据长度 */
/*返回值:无 */
/*----------------------------------------------------------*/
void CMDBuf_Deal(unsigned char *data, int size)
{memcpy(&MQTT_CMDInPtr[2],data,size); //拷贝数据到命令缓冲区MQTT_CMDInPtr[0] = size/256; //记录数据长度MQTT_CMDInPtr[1] = size%256; //记录数据长度MQTT_CMDInPtr[size+2] = '\0'; //加入字符串结束符MQTT_CMDInPtr+=CBUFF_UNIT; //指针下移if(MQTT_CMDInPtr==MQTT_CMDEndPtr) //如果指针到缓冲区尾部了MQTT_CMDInPtr = MQTT_CMDBuf[0]; //指针归位到缓冲区开头
}
总结
具体的功能代码可以根据自己的需要进行改写,如果需要相关学习源代码和学习视频可以私信我,对应的MQTT协议可在我的资源里面寻找!
模块学习4:(1)通过MQTT协议和电信云平台的通信(内附MQTT协议V3.1.1的原版和中文参考资料)相关推荐
- micropython mqtt_MicroPython使用MQTT协议接入OneNET云平台
MicroPython使用MQTT协议接入OneNET云平台 [复制链接] 本帖最后由 hanyeguxingwo 于 2016-11-22 11:33 编辑 之前使用Arduino+ESP8266使 ...
- 物联网云平台用到的那些基本协议
MQTT 最大的优点是简单易用,针对物联网开发,详细可搜索MQTT相关资料. 基本使用场景: 终端设备和云平台之间可采用此通讯协议,需要具备联网能力,一般为网关或带网络模块的终端设备. 例如:监控设备 ...
- 关于微赞,微擎,微动力模块安装时出现 版权保护,未在云平台注册 的解决办法
关于微赞,微擎,微动力模块安装时出现 版权保护,未在云平台注册 等如下之类提示: 您的程序需要在微赞云服务平台注册你的站点资料, 来接入云平台服务后才能使用相应功能. 此模块已设置版权保护,您只能通过 ...
- 微擎跳过云平台_微擎最新版本2.09“此模块已设置版权保护,您只能通过云平台来安装”版权保护问题的解决方法...
在微擎的本地搭建及测试时,有时会遇到模块安装时出现,版权保护,未在云平台注册等等一类的问题.现在我将解决方法贴出,与大家分享. 不管是新版本,还是老版本,问题解决根本方法是修改微擎内部的cloud.m ...
- MQTT网关连接阿里云平台案例教程
MQTT协议网关网口连接西门子SMART200PLC 前言:MQTT是一个基于客户端-服务器的消息发布/订阅传输协议.MQTT协议的特点是轻量.简单.开放和易于实现的,同时,西门子PLC广泛应于工业控 ...
- 骐俊CAT1模组 - MQTT接入腾讯云平台篇
本次实验使用骐俊ML110S系列模组及开发底板,通过MQTT协议采用密钥的方式接入腾讯云平台,实现消息的发布及订阅,可分为接入注册及动态注册两种方式. 设备注册(接入注册) A.进入腾讯云平台注册 ...
- Esp32读取温湿度数据通过mqtt上传阿里云平台
目录 前言 一.esp32刷MicroPython固件库 二.创建阿里云产品 1.注册阿里云账号并登录控制台 2.找到物联网平台中的公共实例进入 3.创建产品及设备 4.编辑物模型并发布 5.最后找到 ...
- 交通部809协议服务器代码,部标平台检测(三).交通部部标809协议测试和运行测试 | 车载GPS和视频平台产品经理...
本身交通部在制定jt/t 809协议文档时,过度设计,采用双链路的复杂的通信架构,文档中文字抽象,而且歧义是很多的,开发者很容易疑惑,产生各种不确定和疑惑,又没有人答疑,全靠摸索.在加上交通部部表80 ...
- 视频联网云平台EasyCVR集成海康EHome协议:Ehome协议预览流程
之前我们讲了EasyCVR视频平台集成了海康EHome协议系统配置,EasyCVR集成海康EHome私有协议内容繁杂琐碎,测试内容众多,所以我们特地开辟一个系列,如果大家有兴趣,可以翻阅以往的博客了解 ...
最新文章
- 【CSON原创】HTML5游戏框架cnGameJS开发实录(外部输入模块篇)
- 打马赛克就安全了吗?AI消除马赛克,GitHub开源项目上线三天收获近7000星
- MyBatis SQL语句操作Mysql
- 她说要介绍10000个开源项目?来!一起监督他!
- 改变Error tip的背景色.
- 翘课老黄历——设计文档
- 云炬随笔20211016(4)
- sgu 126 Boxes
- 为什么文本文件以换行符结尾?
- PowerBuilder 2018
- 京东分类页面部分的实现
- Maven使用本地jar包(三种方式)
- 服务器系统试用,“雪豹”安装篇(3)
- 华东师范计算机模拟考试题答案,《计算机入门》模拟卷C答案-华东师范大学
- python操作pdf——pdfplumber/PyPDF2
- 人生一定要知道的十大“博弈”!
- BlockingQueue的用法
- Java ScriptEngineManager
- 分析Adobe Illustrator CC(AI)中的橡皮擦和直线工具
- 中国人民银行招聘计算机水平,2019中国人民银行招聘计算机模拟试题及答案
热门文章
- MIT6.828学习之homework9:Barriers
- 微软2011年4月最有价值专家(MVP)名单 51CTO十七位用户当选
- mysql的cpu使用率突然增高_mysql cpu使用率过高解决方法
- 精进3步:破除我执,重塑我想,实现我行,普通人逆袭必看
- Workspaces can only be enabled in private projects.
- 仿网易云音乐部分UI实现
- 面向2018年的设计趋势
- 蓝桥杯嵌入式开发经验分享(1.嵌入式学习准备)
- 物理竞赛应该怎么准备?3个步骤教你敲响名牌大学门!
- 数组循环向左移动k位的算法