一.BC35 NB模块简介
BC35-G 是一款高性能、低功耗的 NB-IoT 模块,支持如下表格中所列的六个频段。通过 NB-IoT 无线电通信协议(3GPP Rel. 14),BC35-G 模块可与网络运营商的基础设备建立通信。
供电 :
VBAT 供电电压范围:3.1V~4.2V
典型供电电压:3.6V
发射功率:
23dBm±2dB
串口:
主串口:用于 AT 命令通信和数据传输,支持的波特率为 4800bps、9600bps(默认)、57600bps、115200bps、230400bps 和 460800bps,用于固件升级,支持的波特率为 115200bps 和 921600bps
调试串口:用于软件调试,仅支持波特率 921600bps
网络协议特性 :
支持 IPv4/IPv6/UDP/CoAP/LwM2M/Non-IP/DTLS/TCP/MQTT 协议
天线接口:
50Ω 特性阻抗
二.TCP传输数据流程
1.模块上电开机----->2.模块联网----->3.建立TCP连接----->4.TCP发送数据----->5.等待返回数据并处理----->6.模块断电关机。
详细程序流程:
相关支持函数:
//串口1,printf 函数
//确保一次发送数据不超过USART1_MAX_SEND_LEN字节
void nb_printf(char* fmt,...)
{
u16 i;
va_list ap;
va_start(ap,fmt);
vsprintf((char*)USART1_TX_BUF,fmt,ap);
va_end(ap);
i=strlen((const char*)USART1_TX_BUF);//此次发送数据的长度
/*采用串口寄存器操作发送数据*/
// for(j=0;j<i;j++)//循环发送数据
// {
// while((USART1->ISR&0X40)==0); //循环发送,直到发送完毕
// USART1->TDR=USART1_TX_BUF[j];
// }
/*采用串口HAL库函数发送数据*/
// HAL_UART_Transmit(&huart1, (uint8_t*)USART1_TX_BUF,i, 1000); //发送接收到的数据
// while(__HAL_UART_GET_FLAG(&huart1, UART_FLAG_TC) != SET); //等待发送结束
/*采用DMA方式发送数据*/
HAL_USART1_DMA_TX(&USART1TxDMA_Handler,DMA_FLAG_TC2,&huart1,USART1_TX_BUF,i);//使用DMA发送数据
memset(USART1_TX_BUF,0,sizeof(USART1_TX_BUF));//清空缓存
}
//NB_MODULE发送命令后,检测接收到的应答
//str:期待的应答结果
//返回值:0,没有得到期待的应答结果
// 其他,期待应答结果的位置(str的位置)
u8* nb_send_check_cmd(u8 *str)
{
char *strx=0;
USART1_RX_BUF[USART1_RX_COUNT]=0;//添加结束符
strx=strstr((const char*)USART1_RX_BUF,(const char*)str);
return (u8*)strx;
}
//向NB_MODULE发送指定数据
//data:发送的数据(不需要添加回车了)
//ack:期待的应答结果,如果为空,则表示不需要等待应答
//waittime:等待时间(单位:100ms)
//返回值:0,发送成功(得到了期待的应答结果)
// 1,发送失败
u8 nb_send_cmd(u8 *data,u8 *ack,u16 waittime)
{
waittime=waittime*100;
nb_printf("%s\r\n",data); //需要发送的是命令
if(ack&&waittime) //需要等待应答
{
while(--waittime) //等待倒计时
{
delay_ms(1);
if(USART1_IDLE_FLAG) //接收到期待的应答结果
{
if(nb_send_check_cmd(ack))
{
//printf("%s ack: %s\r\n",data,(u8*)ack);
Feed_Dog(); //喂狗
memset(USART1_RX_BUF,0,sizeof(USART1_RX_BUF));//清空USART1缓存
USART1_IDLE_FLAG=0;
USART1_RX_COUNT=0; //串口1接受数据清零
return 0; //ack正确,返回1
}
memset(USART1_RX_BUF,0,sizeof(USART1_RX_BUF));//清空USART1缓存
USART1_IDLE_FLAG=0;
USART1_RX_COUNT=0;//串口1接受数据清零
}
}
}
return 1; //ack错误,返回0
}
//向NB_MODULE发送指定数据,并读取返回参数值
//data:发送的数据(不需要添加回车了)
//ack:期待的应答结果,如果为空,则表示不需要等待应答
//waittime:等待时间(单位:100ms)
//返回值:0,发送成功(得到了期待的应答结果)
// 1,发送失败
u8 nb_send_cmd_return(u8 *data,u8 *ack,u16 waittime,u8 *parameter)
{
nb_printf("%s\r\n",data); //需要发送的是命令
if(ack&&waittime) //需要等待应答
{
while(--waittime) //等待倒计时
{
delay_ms(100);
if(USART1_IDLE_FLAG) //接收到期待的应答结果
{
if(nb_send_check_cmd(ack))
{
*parameter=USART1_RX_BUF[2];
printf("%s ack: %s\r\n",data,(u8*)ack);
Feed_Dog(); //喂狗
memset(USART1_RX_BUF,0,sizeof(USART1_RX_BUF));//清空USART1缓存
USART1_IDLE_FLAG=0;
USART1_RX_COUNT=0; //串口1接受数据清零
return 0; //ack正确,返回1
}
memset(USART1_RX_BUF,0,sizeof(USART1_RX_BUF));//清空USART1缓存
USART1_IDLE_FLAG=0;
USART1_RX_COUNT=0;//串口1接受数据清零
}
}
}
return 1; //ack错误,返回0
}
2.模块联网
/**
* @brief NB模块联网
* 参考手册:p160 手动网络连接
*
* @param void
*
* @return 0:连接成功
* 1:连接失败
*/
u8 NB_Connect_To_Internet(void)
{
u8 i=0;
for(i=0;i<3;i++)
if(nb_send_cmd((u8 *)"AT+NRB",(u8 *)"REBOOT_CAUSE_APPLICATION_AT",100)==0)break;
if(i==3)
{
printf("模块重启失败...\r\n\r\n");
return 1; //失败,返回1
}
else printf("模块重启成功...\r\n\r\n");
for(i=0;i<3;i++)
if(nb_send_cmd((u8 *)"AT+NBAND=5",(u8 *)"OK",10)==0)break;
if(i==3)
{
printf("设置频段失败...\r\n\r\n");
return 1; //失败,返回1
}
else printf("设置频段成功...\r\n\r\n");
for(i=0;i<3;i++)
if(nb_send_cmd((u8 *)"AT+CFUN=1",(u8 *)"OK",40)==0)break;
if(i==3)
{
printf("设置最大功能模式失败...\r\n\r\n");
return 1; //失败,返回1
}
else printf("设置最大功能模式成功...\r\n\r\n");
i=0;
for(i=0;i<3;i++)
if(nb_send_cmd((u8 *)"AT+CEDRXS=0,5",(u8 *)"OK",10)==0)break;
if(i==3)
{
printf("关闭模组eDRX功能失败...\r\n\r\n");
return 1; //失败,返回1
}
else printf("关闭模组eDRX功能成功...\r\n\r\n");
for(i=0;i<3;i++)
if(nb_send_cmd((u8 *)"AT+CGATT=1",(u8 *)"OK",20)==0)break;
if(i==3)
{
printf("附着网络失败...\r\n\r\n");
return 1; //失败,返回1
}
else printf("附着网络成功...\r\n\r\n");
for(i=0;i<10;i++)
if(nb_send_cmd((u8 *)"AT+CGATT?",(u8 *)"+CGATT:1",20)==0)break;
if(i==10)
{
printf("网络连接失败...\r\n\r\n");
return 1;
}
else
printf("网络连接成功...\r\n\r\n");
return 0;
}
3.建立TCP连接
/**
* @brief 建立TCP连接
*
* @param ip:需要连接的ip地址
* port:需要连接的端口
* socket:连接成功后返回创建的socket值
*
* @return 0:发送成功
* 1:发送失败
*/
u8 NB_TCP_Connect(const u8* ip,const u8* port,u8* socket)
{
u8 i=0;
u8 p[50];
for(i=0;i<3;i++)
if(nb_send_cmd_return((u8 *)"AT+NSOCR=STREAM,6,0,1",(u8 *)"OK",100,socket)==0)break;
if(i<3)printf("创建socket成功...\r\n\r\n");
else
{
printf("创建socket失败...\r\n\r\n");
return 1;
}
*socket=*socket-48;
printf("tcp_socket=%d\r\n",*socket);
sprintf((char*)p,"AT+NSOCO=%d,%s,%s",*socket,ip,port);
for(i=0;i<3;i++)
if(nb_send_cmd((u8 *)p,(u8 *)"OK",100))break;
if(i<3)printf("TCP连接成功...\r\n\r\n");
else
{
printf("TCP连接失败...\r\n\r\n");
return 1;
}
return 0;
}
4.TCP发送数据
/**
* @brief TCP发送数据
*
* @param socket:套接字号
* data:发送的数据
* len:发送的数据长度
*
* @return 0:发送成功
* 1:发送失败
*/
u8 tx_buffer[2000]={0}; //定义最终发送数据缓存区
u8 p[2000]; //封装成最终要发送的帧格式
u8 NB_TCP_Send_Data(u8 socket,u8* data,u16 len)
{
u8 i=0;
HexArrayToString(data,(char*)USART1_TX_BUF,len); //先将原始数据转成字符串
HexArrayToString(USART1_TX_BUF,(char*)tx_buffer,2*len); //再讲字符串转成ASCII码
sprintf((char*)p,"AT+NSOSD=%d,%d,%s,%s,%s",socket,2*len,(char*)tx_buffer,"0x100","101");
memset(USART1_TX_BUF,0,sizeof(USART1_TX_BUF));//清空缓存,因为下面nb_send_cmd()也需要用到USART1_TX_BUF,因此需要清空
memset(tx_buffer,0,sizeof(tx_buffer)); //清空缓存,因为下面nb_send_cmd()也需要用到USART1_TX_BUF,因此需要清空
for(i=0;i<3;i++)
if(nb_send_cmd((u8 *)p,(u8 *)"101,1",50)==0)break;
memset(p,0,sizeof(p));//清空缓存
if(i<3)
{
printf("TCP发送数据成功,且确认被服务器收到...\r\n\r\n");
return 0;
}
else
{
printf("TCP发送数据失败...\r\n\r\n");
return 1;
}
}
5.等待返回数据并处理(部分程序)
while(1) //等待云端回数据倒计时
{
delay_ms(5); //延时5ms判断是否收到数据
if(USART1_IDLE_FLAG) //串口1接收数据完毕,与NB模块通信
{
/*判断是否有来自服务器的数据*/
str1=nb_send_check_cmd((u8 *)"+NSONMI:");//判断接受到的数据是否有+NSONMI:
if(str1) //确定接收到了+NSONMI:
{
receive_socket=*(str1+8)-48; //获得当前socket编号
if(USART1_RX_COUNT-14==1)receive_num=*(str1+10)-48;
else if(USART1_RX_COUNT-14==2)receive_num=(*(str1+10)-48)*10+*(str1+11)-48; //获得当前收到的字节数
sprintf((char*)q,"%d,%s,%s,%d",receive_socket,IP_address,portnum,receive_num);//组成一个包
receive_data_flag=1; //接收到数据标志位置位
}
/*判断是否收到服务器的数据*/
str2=nb_send_check_cmd((u8*)q); //判断接受到的数据是否有+NSONMI:
if(str2) //确定接收到了数据
{
HexStrToByte(str2+strlen(q)+1,Receive_Buffer,2*receive_num); //将字符装成HEX并存在Receive_Buffer中,待处理
read_data_flag=1; //数据处理标志位置位
}
memset(USART1_RX_BUF,0,sizeof(USART1_RX_BUF));//清空缓存
USART1_RX_COUNT=0; //将接受字节数清零
USART1_IDLE_FLAG=0; //空闲中断标志复位
}
if(receive_data_flag) //确认模块接收到来自服务器的数据
{
sprintf((char*)p,"AT+NSORF=%d,%d",receive_socket,200);
nb_printf("%s\r\n",p); //发送接收数据指令
receive_data_flag=0; //接收到数据标志位复位
}
/*需要进行数据处理了*/
if(read_data_flag) //确认需要进行数据处理
{
for(i=0;i<receive_num;i++)
printf("%x ",Receive_Buffer[i]);
printf("\r\n");
waittime=4000; //接收到一次数据后恢复计时时间
break;
}
}
三.UDP传输数据流程
1.模块上电开机----->2.模块联网----->3.建立UDP连接----->4.UDP发送数据----->5.等待返回数据并处理----->6.模块断电关机。
详细程序流程:
相关支持函数:
同上面TCP一致
2.模块联网
同上面TCP一致
3.建立UDP连接
/**
* @brief 建立UDP连接
*
* @param ip:需要连接的ip地址
* port:需要连接的端口
* socket:连接成功后返回创建的socket值
*
* @return 0:发送成功
* 1:发送失败
*/
u8 NB_UDP_Creat_socket(u8* socket)
{
u8 i=0;
for(i=0;i<3;i++)
if(nb_send_cmd_return((u8 *)"AT+NSOCR=DGRAM,17,0,1",(u8 *)"OK",100,socket)==0)break;
if(i<3)
{
printf("创建socket成功...\r\n\r\n");
*socket=*socket-48;
printf("udp_socket=%d\r\n",*socket);
return 0;
}
else printf("创建socket失败...\r\n\r\n");
return 1;
}
4.UDP发送数据
/**
* @brief UDP发送数据
*
* @param socket:套接字号
* data:发送的数据
* len:发送的数据长度
*
* @return 0:发送成功
* 1:发送失败
*/
u8 NB_UDP_Send_Data(u8 socket,const u8* ip,const u8* port,u8* data,u16 len)
{
u16 i=0;
HexArrayToString(data,(char*)USART1_TX_BUF,len); //先将原始数据转成字符串
HexArrayToString(USART1_TX_BUF,(char*)tx_buffer,2*len); //再讲字符串转成ASCII码
sprintf((char*)p,"AT+NSOST=%d,%s,%s,%d,%s,%d",socket,ip,port,2*len,(char*)tx_buffer,100);
memset(USART1_TX_BUF,0,sizeof(USART1_TX_BUF));//清空缓存,因为下面nb_send_cmd()也需要用到USART1_TX_BUF,因此需要清空
memset(tx_buffer,0,sizeof(tx_buffer)); //清空缓存,因为下面nb_send_cmd()也需要用到USART1_TX_BUF,因此需要清空
for(i=0;i<3;i++)
if(nb_send_cmd((u8 *)p,(u8 *)"100,1",100)==0)break;
memset(p,0,sizeof(p));//清空缓存
if(i<3)
{
printf("UDP发送数据成功...\r\n\r\n");
return 0;
}
else
{
printf("UDP发送数据失败...\r\n\r\n");
return 1;
}
}
5.等待返回数据并处理
同上面TCP一致
四. 感谢支持
完结撒花!希望看到这里的小伙伴能点个关注,我后续会持续更新,也欢迎大家广泛交流。
码字实属不易,如果本文对你有10分帮助,就赏个10分把,感谢各位大佬支持!