BC35 NB模組AT指令開發總結

2020-08-10 12:19:21

一.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一致