野火电子论坛

 找回密码
 注册

QQ登录

只需一步,快速开始

查看: 8675|回复: 2

NB-IoT模块 移远BC35全网通接入OneNET平台MQTT协议实现数据传输

[复制链接]
发表于 2019-3-29 09:50:56 | 显示全部楼层 |阅读模式
本帖最后由 nnhpTCH 于 2019-3-29 10:08 编辑

网上很多NB-IOT模块的资料都是使用LWM2M、COAP协议去连接onenet平台
因为现在很多的NB-IOT模块都支持TCP协议,因此就想使用TCP协议然后转MQTT协议连接onenet平台,经过试验证明是可以的。
首先我们先分析一下如何登陆接入Onenet平台。


先从它数据格式开始分析。首先我们要从后台取出三个信息,我们以这个为例。
鉴权信息:test(具体的看你设置的)设备ID:123456789 产品ID:789123  (具体是什么看你自己的设备)
MQTT接入都是发十六进制的数据。
那么我们发送的时候就是这样子的一串数据
0x10 0x24 0x00 0x04 0x4D 0x51 0x54 0x54 0x04 0xC0 0x00 0x78 0x00 0x09 0x31 0x32 0x33 0x34 0x35 0x36 0x37 0x38 0x39 0x00 0x07 0x37 0x38 0x39 0x31 0x32 0x33 0x20 0x00 0x04 0x74 0x65 0x73 0x74

[ 0x10 ] // MQTT类型=1 DupFlag=0 QosLevel=0 Retain=0
[ 0x22 ] // remainedLength=34  以下的数据长度  0x00 04......0x74 0x65 0x73 0x74这个之间的长度,您可以数一下看看
[ 0x00 0x04 ] // protocolNameLength=4 这是发固定的MQTT这4个字母,长度为4.但是需要两个字节保存长度
[ 0x4D 0x51 0x54 0x54 ] // protocolName=MQTT
[ 0x04 ] // protocolLevel=4  固定的一种类型
[ 0xC0 ] // userFlag=1 passwordFlag=1 willFlag=0 willRetainFlag=0 willQosFlag=0 clenSessionFlag=0 clenSessionFlag=0  固定的一种类型
[ 0x00 0x78 ] // keepAlive=120  这是保持连接的时间为120S
[ 0x00 0x08] // clientIdentifierLen=8 这是设备ID的长度 用两个字节记录
[ 0x31 0x32 0x33 0x34 0x35 0x36 0x37 0x38 ] // clientIdentifier=12345678 设备ID
[ 0x00 0x06 ] // userNameLen=6 这是产品ID的长度 用两个字节记录
[ 0x37 0x38 0x39 0x31 0x32 0x33] // userName=789123
[ 0x00 0x04 ] // userPasswordLen=4 这是鉴权信息的长度 用两个字节记录
[ 0x74 0x65 0x73 0x74 ] // userPassword=test


这里面有个坑,长度的十六进制是十进制转换为十六进制。而“MQTT” ,test(具体的看你设置的)设备ID:123456789 产品ID:789123 这些信息都是字符串转十六进制
下面我们直接上指令
AT
OK
AT+NRB//重启模块的指令,在单片机里面最好进行重启一下
AT+CMEE=1
OK
AT+CGSN=1
+CGSN:867725030027605
OK
AT+NBAND?
+NBAND:8 //移动的返回+NBAND:8,电信的返回+NBAND:5
OK
AT+NBAND=8 //设置为移动的,电信的指令为AT+NBAND=5
OK
AT+CIMI //查询卡号
460042437100213
OK
AT+CSQ //查询信号指令,如果返回+CSQ:99,99 说明还没有信号
+CSQ:29,99
OK
AT+NSOCR=STREAM,6,56000,1 //做TCP socket创建准备
AT+NSOCO=1,183.230.40.33,6002//连接服务器。6002为服务器端口号
OK

注意:NB发送数据的时候数据内容需要转换为十六进制。大家可以使用在线转换工具
发送数据的指令为:AT+NSOSD=1,发送内容的长度,内容。//注意长度不可以多或者少

AT+NSOSD=1,36,102200044D51545404C00078000831323334353637380006373839313233000474657374
OK
+NSONMI:1,4//这是服务器返回来的数据。
AT+NSORF=1,4//查询接收内容的指令203是需要查询的长度。可以多,
1,183.230.40.33,80,203,20200000,0
OK
这个时候已经连接上服务器了,那么现在就发送数据,原始数据如下
{"datastreams":[{"id":"temp","datapoints":[{"value":50}]}]}

但是需要增加一些数据头之类的格式
这是转换后的数据
0x30 0x43 0x00 0x03 0x24 0x64 0x70 0x01 0x00 0x3B 0x7B 0x22 0x64 0x61 0x74 0x61 0x73 0x74 0x72 0x65 0x61 0x6D 0x73 0x22 0x3A 0x5B 0x7B 0x22 0x69 0x64 0x22 0x3A 0x22 0x74 0x65 0x6D 0x70 0x22 0x2C 0x22 0x64 0x61 0x74 0x61 0x70 0x6F 0x69 0x6E 0x74 0x73 0x22 0x3A 0x5B 0x7B 0x22 0x76 0x61 0x6C 0x75 0x65 0x22 0x3A 0x35 0x30 0x7D 0x5D 0x7D 0x5D 0x7D
数据头的内容:
3043000324647001003B

各个数据的含义
[ 0x30 ]   // MQTT类型=3  DupFlag=0 QosLevel=0 Retain=0  选用类型为3
[ 0x43 ]   // remainedLength=67
[ 0x00 0x03 ]   // TopicNameLen=3
[ 0x24 0x64 0x70 ]   // TopicName=$dp 这是固定的数据
[ 0x01 ]   // DpType=1 TimeSet=0
[ 0x00 0x3B ]   // JsonStrLen=59 这是json数据的长度

[ 0x7B 0x22 0x64 0x61 0x74 0x61 0x73 0x74 0x72 0x65 0x61 0x6D 0x73 0x22 0x3A 0x5B 0x7B 0x22 0x69 0x64 0x22 0x3A 0x22 0x74 0x65 0x6D 0x70 0x22 0x2C 0x22 0x64 0x61 0x74 0x61 0x70 0x6F 0x69 0x6E 0x74 0x73 0x22 0x3A 0x5B 0x7B 0x22 0x76 0x61 0x6C 0x75 0x65 0x22 0x3A 0x35 0x30 0x7D 0x5D 0x7D 0x5D 0x7D ]   // JsonStr={"datastreams":[{"id":"temp","datapoints":[{"value":50}]}]}
下面我们开始发送数据到服务器
AT+NSOSD=1,69,3043000324647001003B7B226461746173747265616D73223A5B7B226964223A2274656D70222C2264617461706F696E7473223A5B7B2276616C7565223A35307D5D7D5D7D
OK
+NSONMI:1,4//这是服务器返回来的数据。
AT+NSOCL=1 //关闭连接
OK


完整的步骤
AT
OK
AT+NRB//重启模块的指令,在单片机里面最好进行重启一下
AT+CMEE=1
OK
AT+CGSN=1
+CGSN:867725030027605
OK
AT+NBAND?
+NBAND:8 //移动的返回+NBAND:8,电信的返回+NBAND:5
OK
AT+NBAND=8 //设置为移动的,电信的指令为AT+NBAND=5
OK
AT+CIMI //查询卡号
460042437100213
OK
AT+CSQ //查询信号指令,如果返回+CSQ:99,99 说明还没有信号
+CSQ:29,99
OK
AT+NSOCR=STREAM,6,56000,1 //做TCP socket创建准备
AT+NSOCO=1,183.230.40.33,6002//连接服务器。6002为服务器端口号
OK
AT+NSOSD=1,36,102200044D51545404C00078000831323334353637380006373839313233000474657374
OK
+NSONMI:1,4//这是服务器返回来的数据。
AT+NSORF=1,4//查询接收内容的指令203是需要查询的长度。可以多,
1,183.230.40.33,80,203,20200000,0
OK
AT+NSOSD=1,69,3043000324647001003B7B226461746173747265616D73223A5B7B226964223A2274656D70222C2264617461706F696E7473223A5B7B2276616C7565223A35307D5D7D5D7D
OK
+NSONMI:1,4//这是服务器返回来的数据。
AT+NSOCL=1 //关闭连接
OK


附上C语言转十六进制函数
int Str2Hex(char *str,char *hex)
{
int high = 0;
int low = 0;
int temp = 0;
if (NULL==str || NULL==hex){
//printf("the str or hex is wrong\n");
return -1;
}
if (0==strlen(str)){
//printf("the input str is wrong\n");
return -2;
}
while(*str)
{
temp = (int)(*str);
high = temp>>4;
low = temp & 15;
*hex = Value2Hex(high);
hex++;
*hex = Value2Hex(low);
hex++;
str++;
}
*hex = '\0';
return 0;
}


附上发送MQTT连接服务器的数据及发送数据的格式化函数
void ClearRAM(u8* ram,u32 n)
{
u32 i;
for (i = 0;i < n;i++)
{
ram = 0x00;
}
}
//构建MQTT连接包
u16 mqtt_connect_message(char *mqtt_message,char *PROID,char *AUTH_INFO,char *DEVID)
{
//        char HTTP_Buf[400]; //HTTP报文缓存区
char str[500];
char strHEX[500];
u16 PROID_length = strlen(PROID);
u16 DEVID_length = strlen(DEVID);
u16 AUTH_INFO_length = strlen(AUTH_INFO);
u16 MQTT_length = strlen("MQTT");
u16 packetLen;
u16 i,baseIndex;
ClearRAM((u8*)str,200); //清空commandAT数据包缓存,发送命令包含ip端口和长度
ClearRAM((u8*)strHEX,200); //清空locationHex数据包缓存,转换为hex 需要发送的真实数据
packetLen = 12;//固定的数据头长度
if(PROID_length > 0)
packetLen = packetLen + 2 + PROID_length;
if(DEVID_length > 0)
packetLen = packetLen+ 2 + DEVID_length;
if(AUTH_INFO_length > 0)
packetLen = packetLen+ 2 + AUTH_INFO_length;
strncpy((char*)(mqtt_message+strlen((char*)mqtt_message)),"10",2);
sprintf(strHEX,"%02x",(packetLen-2));
strcat(mqtt_message,strHEX);
ClearRAM((u8*)str,200); //清空commandAT数据包缓存,发送命令包含ip端口和长度
ClearRAM((u8*)strHEX,200); //清空locationHex数据包缓存,转换为hex 需要发送的真实数据
strcat(mqtt_message,"00044D51545404C00100");        
//装载设备ID到数据包        
sprintf(str, "%02x%02x",((0xff00&(DEVID_length))>>8),(0xff&DEVID_length));
strcat(mqtt_message,str);
ClearRAM((u8*)str,200);
sprintf(str, "%s",DEVID);
Str2Hex((char*)str,(char*)strHEX);
strcat(mqtt_message,strHEX);        
ClearRAM((u8*)str,200);
ClearRAM((u8*)strHEX,200);
//装载产品ID到数据包        
sprintf(str, "%02x%02x",((0xff00&(PROID_length))>>8),(0xff&PROID_length));
strcat(mqtt_message,str);
ClearRAM((u8*)str,200);
sprintf(str, "%s",PROID);
Str2Hex((char*)str,(char*)strHEX);
strcat(mqtt_message,strHEX);        
ClearRAM((u8*)str,200);
ClearRAM((u8*)strHEX,200);
//装载产品ID到数据包        
sprintf(str, "%02x%02x",((0xff00&(AUTH_INFO_length))>>8),(0xff&AUTH_INFO_length));
strcat(mqtt_message,str);
ClearRAM((u8*)str,200);
sprintf(str, "%s",AUTH_INFO);
Str2Hex((char*)str,(char*)strHEX);
strcat(mqtt_message,strHEX);        
ClearRAM((u8*)str,200);
ClearRAM((u8*)strHEX,200);
Str2Hex((char*)str,(char*)strHEX);
strcat(mqtt_message,strHEX);
strcat(mqtt_message,"\r\n");        
return packetLen;
}
//构建MQTT数据包
u16 mqtt_SendData_message(char *mqtt_messageHEX,char *SendData,short length)
{
char str[200];
char strHEX[200];
u16 packetLen=length+8;
u16 SendData_length = strlen(SendData);
strncpy((char*)(mqtt_messageHEX+strlen((char*)mqtt_messageHEX)),"30",2);
sprintf(strHEX,"%02x",(packetLen));
strcat(mqtt_messageHEX,strHEX);
ClearRAM((u8*)str,200); //清空commandAT数据包缓存,发送命令包含ip端口和长度
ClearRAM((u8*)strHEX,200); //清空locationHex数据包缓存,转换为hex 需要发送的真实数据
strcat(mqtt_messageHEX,"01000324647001");        
//装载json数据到数据包        
sprintf(str, "%02x%02x",((0xff00&(SendData_length))>>8),(0xff&SendData_length));
strcat(mqtt_messageHEX,str);
ClearRAM((u8*)str,200);
sprintf(str, "%s",SendData);
Str2Hex((char*)str,(char*)strHEX);
strcat(mqtt_messageHEX,strHEX);        
ClearRAM((u8*)str,200);
ClearRAM((u8*)strHEX,200);        
return packetLen;
}        


到此结束。有什么不对的地方还望指正
MQTT ONEnet测试调试工具 simulate-device.rar (2.49 MB, 下载次数: 48)




回复

使用道具 举报

发表于 2019-3-30 08:32:18 | 显示全部楼层
1
回复

使用道具 举报

 楼主| 发表于 2019-4-11 10:14:42 | 显示全部楼层
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

联系站长|手机版|野火电子官网|野火淘宝店铺|野火电子论坛 ( 粤ICP备14069197号 ) 大学生ARM嵌入式2群

GMT+8, 2024-4-20 07:24 , Processed in 0.031753 second(s), 26 queries , Gzip On.

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表