STM32F103學習筆記(9)——NB-IoT模塊BC26使用

一浑度、簡介

BC26 是一款高性能、低功耗、多頻段 LTE Cat NB1/Cat NB2*無線通信模塊专普。其尺寸僅為 17.7 mm × 15.8 mm × 2.0 mm,能最大限度地滿足終端設備對小尺寸模塊產品的需求码耐,同時有效幫助客戶減小產品尺寸并優(yōu)化產品成本.BC26 在封裝設計上兼容移遠通信 GSM/GPRS 系列 M26 模塊以及 NB-IoT 系列 BC28/BC25/BC260Y-CN 模塊追迟,方便客戶快速、靈活的進行產品設計和升級骚腥。BC26 提供豐富的外部接口和協(xié)議棧敦间,同時可支持中國移動 OneNET/Andlink、中國電信 IoT/ AEP以及阿里云IoT等物聯網云平臺束铭,為客戶的應用提供極大的便利廓块。

BC26 采用更易于焊接的 LCC 封裝,可通過標準 SMT 設備實現模塊的快速生產契沫,為客戶提供可靠的連接方式带猴,并滿足復雜環(huán)境下的應用需求。

憑借緊湊的尺寸懈万、超低功耗和超寬工作溫度范圍拴清,BC26 成為 IoT 應用領域的理想選擇,常被用于煙感会通、無線抄表口予、共享單車、智能停車涕侈、智慧城市沪停、安防、資產追蹤裳涛、智能家電木张、可穿戴設備、農業(yè)和環(huán)境監(jiān)測以及其它諸多行業(yè)端三,以提供完善的短信和數據傳輸服務舷礼。

BC26資料:鏈接:https://pan.baidu.com/s/1n8rcRCna8wnMPFwikY3l0g?pwd=vgcm 提取碼:vgcm

二、AT指令

發(fā)送數據時務必勾選:“加回車換行符”技肩。否則模塊不會響應且轨。在本文中,僅顯示響應虚婿,省略回車換行符旋奢。

2.1 AT

測試AT指令功能是否正常,等待模塊返回 OK然痊。

AT

OK

2.2 AT+CIMI

該命令用于查詢(U)SIM 卡的國際移動用戶識別碼(IMSI至朗,無雙引號的字符串)。IMSI 允許 TE 識別連
接到 MT 的 USIM剧浸。

AT+CIMI

460001357924680

OK

2.3 AT+CGATT=1

設置命令用于將 MT 附著于 PS 域锹引。命令完成后矗钟,MT 保持在 V.250 命令狀態(tài)。如果 MT 已經處于請求狀態(tài)嫌变,則忽略該命令吨艇,并且仍將響應 OK。如果 MT 無法實現請求狀態(tài)腾啥,將響應 ERROR 或+CME ERROR东涡。

AT+CGATT=1

OK

2.4 AT+CGATT?

查詢命令返回當前 PS 域服務狀態(tài)。

AT+CGATT=?

+CGATT: <state>
如:+CGATT: 1

OK

<state> 整型倘待。PDP 上下文激活狀態(tài)疮跑。

  • 0 去附著
  • 1 附著

2.5 AT+CEREG?

AT+CEREG?

+CEREG: <n>,<stat>
如:+CEREG: 0,1
如:+CEREG: 1,1

OK

<n> 整型。禁止或允許上報網絡注冊狀態(tài)等信息凸舵。

  • 0 禁止上報網絡注冊狀態(tài) URC
  • 1 允許上報網絡注冊狀態(tài) URC +CEREG: <stat>
  • 2 允許上報網絡注冊狀態(tài)和位置信息 URC +CEREG: <stat>[,[<tac>],[<ci>],[<AcT>]]
  • 3 允許上報網絡注冊狀態(tài)祖娘、位置信息和 EMM 原因值 URC +CEREG: <stat>[,[<tac>],[<ci>],[<AcT>][,<cause_type>,<reject_cause>]]
  • 4 對于請求 PSM 的 UE,允許上報網絡注冊狀態(tài)和位置信息 URC +CEREG: <stat>[,[<tac>],[<ci>],[<AcT>][,,[,[<Active-Time>],[<Periodic-TAU>]]]]
  • 5 對于請求 PSM 的 UE啊奄,允許上報網絡注冊狀態(tài)渐苏、位置信息和 EMM 原因值 URC +CEREG: <stat>[,[<tac>],[<ci>],[<AcT>][,[<cause_type>],[<reject_cause>][,[<Active-Time>, [<Periodic-TAU>]]]]

<stat> 整型。EPS 注冊狀態(tài)菇夸。

  • 0 未注冊整以,MT 當前未搜索網絡
  • 1 已注冊,歸屬網絡
  • 2 未注冊峻仇,但 MT 當前正在嘗試附著或搜索網絡以進行注冊
  • 3 注冊被拒絕
  • 4 未知(例如:超出 E-UTRAN 覆蓋范圍)
  • 5 已注冊,漫游狀態(tài)

2.6 AT+QIPADDR

查詢 UE 的 IP 地址

AT+QIPADDR

+QIPADDR: fe80:0:0:0:3c:ffb8:f4c9:1207

+QIPADDR: 2001:14bb:170:4c91:3c: ffh8:f4c9:1207

+QIPADDR: 178.55.211.180

+QIPADDR: 127.0.0.1

OK

2.7 AT+QIOPEN

該命令用于打開套接字服務邑商。

  • AT+QIOPEN=?:查詢命令參數摄咆。
  • AT+QIOPEN=<contextID>,<connectID>,<service_type>,<IP_address>/<domain_name>,<remote_port>[,<local_po CONNECTrt>[,<access_mode>]] :打開 Socket 服務。
    • <contextID> :整數類型人断。上下文ID吭从。范圍是1-16。
    • <connectID> :整數類型恶迈。套接字服務索引涩金。范圍是0-11。
    • <SERVICE_TYPE>:字符串類型暇仲。套接字服務類型步做。
      • “ TCP ” :作為客戶端啟動TCP連接
      • “ UDP ”:作為客戶端啟動UDP連接
      • “TCP LISTENER” :啟動TCP服務器以偵聽TCP連接
      • “UDP SERVICE” :啟動UDP服務
    • <IP_address>:字符串類型。
      • 如果<service_type>是TCP或UDP 奈附,則表示遠程服務器的IP地址全度,例如 “220.180.239.212”。
      • 如果<service_type>是TCP LISTENER或UDP SERVICE 地址斥滤,請輸入“127.0.0.1”将鸵。
    • <domain_name>:字符串類型勉盅。遠程服務器的域名地址。
    • <remote_port> :遠程服務器的端口顶掉,僅在<service_type>為“TCP”或“UDP”時有效草娜。范圍是0-65535。
    • <LOCAL_PORT> :本地端口痒筒。范圍是0-65535宰闰。
      • 如果<service_type>是“TCP LISTENER”或“UDP SERVICE”,則此參數必須指定凸克。
      • 如果<service_type>是“TCP”或“UDP”议蟆。如果<local_port>為0,那么本地端口將是自動分配萎战。否則咐容,將按指定分配本地端口。
    • <access_mode> :整數類型蚂维。套接字服務的數據訪問模式戳粒。
      • 0: 緩沖區(qū)訪問模式
      • 1:直推模式
      • 2:透明訪問模式
    • <err>:整數類型。操作的錯誤代碼虫啥。請參閱第4章蔚约。
AT+QIOPEN=1,0,\"TCP\",\"180.97.81.180\",53540,0,1

OK

+QIOPEN: 0,0

Buffer模式,Push模式涂籽,透傳模式苹祟。通過參數<access_mode>進行配置。





2.8 AT + QISEND

如果指定套接字服務的<access_mode>是緩沖區(qū)訪問模式或直接推送模式评雌,則數據可以是通過AT + QISEND發(fā)送树枫。如果數據成功發(fā)送到模塊,將返回“ SEND OK ” 景东。否則它將返回“ SEND FAIL ” 或“ ERROR ” 砂轻。“ SEND FAIL ” 表示發(fā)送緩沖區(qū)已滿客戶可以嘗試重新發(fā)送數據斤吐∩裕“ERROR”表示在發(fā)送過程中遇到錯誤 數據『痛耄客戶應該延遲一段時間來發(fā)送數據庄呈。最大數據長度為1460字【势牛“SEND OK”并不意味著數據已成功發(fā)送到服務器抒痒。客戶可以查詢數據是否通過AT + QISEND = <connectID>颁褂,0命令到達服務器故响。透傳模式下不需要AT指令發(fā)送數據


三傀广、TCP應用時序圖

四、復位模塊

通過拉低 RESET 引腳至少 50ms 可以使模塊復位彩届。


五伪冰、移植文件

5.1 board_bc26.c

/*********************************************************************
 * INCLUDES
 */
#include "stdlib.h"
#include "string.h"
#include "stm32f10x.h"
#include "FreeRTOS.h"
#include "task.h" 

#include "board_bc26.h" 

static uint8_t sendCmd(char *pCmd, char *pRes, char *pRes2, uint32_t timeOut, uint8_t sendNum);
static void clearBuffer(void);
static void reset(void);

/*********************************************************************
 * GLOBAL VARIABLES
 */  
uint8_t g_usart2RecvFinish = 0;                                                 // 串口2接收標志串口接收完成標志
char g_bc26Buf[1024] = {0};                                                     // 接收緩存
volatile uint32_t g_bc26Cnt;                                                    // 接收計數                                 

/*********************************************************************
 * PUBLIC FUNCTIONS
 */
/**
 @brief 初始化
 @param 無
 @return 1 - 成功;0 - 失敗
*/
uint8_t BC26_Init(void)
{       
    printf("BC26_Init\r\n");
    uint8_t result = 0;
    uint8_t step = 0;
    switch(step)
    {
        case 0:
            if(sendCmd("AT\r\n", "OK", NULL, 10, 6))                            // 測試AT指令功能是否正常
            {
                step++;
            }
            else
            {
                printf("Err:AT\r\n");
                reset();
                break;
            } 
        case 1:
            if(sendCmd("AT+CIMI\r\n", "OK", NULL, 20, 1))                       // 查詢SIM卡是否正常樟蠕,返回OK則表示SIM卡正常
            {
                step++;
            }
            else
            {
                printf("Err:AT+CIMI\r\n");                                      // 20秒內贮聂,無法識別SIM狀態(tài),重啟模塊
                reset();
                break;
            }
        case 2:
            if(sendCmd("AT+CGATT=1\r\n", "OK", "+IP:", 60, 1))                  // 激活PDP場景
            {
                step++;                                                 
            }
            else
            {
                printf("Err:AT+CGATT=1\r\n");        
                step++;                                                         
            }
        case 3:
            if(sendCmd("AT+CEREG?\r\n", "+CEREG: 0,1", "+CEREG: 1,1", 60, 3))   // 查詢模組是否注冊上EPS網絡
            {
                step++;                                                 
            }
            else
            {
                printf("Err:AT+CEREG?\r\n");      
                step++;                                                         
            }
        case 4:
            if(sendCmd("AT+CGATT?\r\n", "+CGATT: 1", NULL, 85, 3))              // 查詢當前PS域服務狀態(tài)
            {
                step++;
            }
            else
            {
                printf("Err:AT+CGATT?\r\n");                                    // 如果3次都沒停止成功或超過85秒沒有回應寨辩,則重啟模塊
                reset();
                break;
            }
        case 5:
            if(sendCmd("AT+QIPADDR\r\n", "+QIPADDR:", NULL, 60, 3))             // 查詢本機IP地址
            {
                BC26_Connect();
                result = 1;
            }
            else
            {
                printf("Err:AT+QIPADDR\r\n");                                   // 如果3次都沒停止成功或超過60秒沒有回應吓懈,則重啟模塊
                reset();
                break;
            }
    }
    return result;
}

/**
 @brief 復位引腳配置
 @param 無
 @return 無
*/
void BC26_GpioConfig(void)
{
    GPIO_InitTypeDef gpioInitStructure;     
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);                       // 使能GPIO
    gpioInitStructure.GPIO_Pin = GPIO_Pin_8;                                    // 選擇要初始化的GPIOB引腳PB8
    gpioInitStructure.GPIO_Mode = GPIO_Mode_Out_PP;                             // 設置引腳工作模式為通用推挽輸出      
    gpioInitStructure.GPIO_Speed = GPIO_Speed_50MHz;                            // 設置引腳輸出最大速率為50MHz
    GPIO_Init(GPIOB, &gpioInitStructure);       

    GPIO_SetBits(GPIOB, GPIO_Pin_8);
}

/**
 @brief 連接TCP服務器
 @param 無
 @return 無
*/
void BC26_Connect(void)
{
    if(sendCmd("AT+QICLOSE=0\r\n", "OK", NULL, 20, 1))                          // 關閉TCP連接
    {
        if(sendCmd("AT+QIOPEN=1,0,\"TCP\",\"180.97.81.180\",53540,0,1\r\n", "+QIOPEN:", 150, 5))    
        {
            printf("Connect Success\r\n");
        }      
        else
        {
            printf("Err:AT+QIOPEN=1,0\r\n");
            reset();                                                            // 沒有響應重啟模塊
        }       
    }
}

/**
 @brief 發(fā)送數據到TCP服務器
 @param pString -[in] 發(fā)送數據
 @return 無
*/
void BC26_Send(char *pString)
{
    if(sendCmd("AT+QISEND=0\r\n", ">", NULL, 30, 2))                            // 等待60秒,沒有響應重啟模塊                            
    {
        vTaskDelay(500);                                                        // 等待500ms
        char sendBuf[1024] = {0};
        sprintf(sendBuf, "%s\r\n\x1A", pString);
        if(sendCmd(sendBuf, "SEND OK", "OK", 30, 2))
        {
            if(sendCmd("AT+QISEND=0,0\r\n", "+QISEND:", NULL, 5, 24))           // 2分鐘后(每5秒查詢一次靡狞,共24次)
            {    
                /* 發(fā)送數據成功耻警,對方收到數據 */
            }
            else
            {
                printf("Err:AT+QISEND=0\r\n");                                      
                if(sendCmd("AT+QICLOSE=0\r\n", "OK", NULL, 20, 1))              // TCP連接出現異常,關閉TCP連接
                {
                    printf("AT+QICLOSE\r\n");
                    BC26_Connect();
                }
            }
        }
        else
        {
            BC26_Reset();                                                       // 等待60秒甸怕,沒有響應重啟模塊
        } 
    }
    else
    {
        BC26_Reset();                                                           // 等待60秒甘穿,沒有響應重啟模塊    
    }
}

/**
 @brief 從TCP服務器接收數據
 @param pRecvDataBuf -[out] 接收數據
 @return 接收數據長度
*/
uint32_t EC200S_Receive(char *pRecvDataBuf)
{
    uint32_t recvDataLen = 0;
    if(g_isUsart2RecvFinish)                                                    // 如果串口接收完成
    {
        if(strstr((const char *)g_bc26Buf, "+QIURC: \"recv\",0,") != NULL)      // 如果檢索到關鍵詞
        {
            memcpy(pRecvDataBuf, g_bc26Buf, g_bc26Cnt);
            recvDataLen = g_bc26Cnt;
        }
        else if(strstr((const char *)g_bc26Buf, "+QIURC: \"closed\",0,") != NULL)
        {
            BC26_Reset();
        }
        clearBuffer();
    }   
    return recvDataLen;
}

/**
 @brief 重啟模塊
 @param 無
 @return 無
*/
void BC26_Reset(void)
{
    reset();
}

/*********************************************************************
 * LOCAL FUNCTIONS
 */
/**
 @brief 發(fā)送AT命令
 @param pCmd -[in] 命令字符串
 @param pRes -[in] 需要檢測的返回命令字符串
 @param pRes2 -[in] 需要檢測的返回命令字符串
 @param timeOut -[in] 等待時間
 @param sendNum -[in] 命令發(fā)送次數
 @return 1 - 成功;0 - 失敗
*/
static uint8_t sendCmd(char *pCmd, char *pRes, char *pRes2, uint32_t timeOut, uint8_t sendNum)
{
    uint8_t i = 0;
    uint32_t time;
    clearBuffer();                                                              // 清空緩存 
    for(i = 0; i < sendNum; i++)
    {
        time = timeOut * 10;
        USART_SendString(USART2, pCmd);
        while(time--)
        {
            if(g_usart2RecvFinish)                                              // 如果串口接收完成
            {
                if(strstr((const char *)g_bc26Buf, pRes) != NULL)               // 如果檢索到關鍵詞
                {
                    printf("%s", g_bc26Buf);
                    return 1;
                }
                else if(strstr((const char *)g_bc26Buf, pRes2) != NULL))       // 如果檢索到關鍵詞2
                {
                    printf("%s", g_bc26Buf);
                    return 1;
                }
            }   
            vTaskDelay(100);                                                   // 等待100毫秒
        }
        clearBuffer();
    }
    return 0;
}

/**
 @brief 清空緩存
 @param 無
 @return 無
*/
void clearBuffer(void)
{
    memset(g_bc26Buf, 0, sizeof(g_bc26Buf));
    g_bc26Cnt = 0;
    g_usart2RecvFinish = 0;
}

/**
 @brief 重啟模塊
 @param 無
 @return 無
*/
void reset(void)
{
    printf("reset\n");
    
    GPIO_ResetBits(GPIOB, GPIO_Pin_8);
    vTaskDelay(60);
    GPIO_SetBits(GPIOB, GPIO_Pin_8);
}

/****************************************************END OF FILE****************************************************/

5.2 board_bc26.h

#ifndef _BOARD_BC26_H_
#define _BOARD_BC26_H_

/*********************************************************************
 * INCLUDES
 */
#include "stm32f10x.h"

/*********************************************************************
 * GLOBAL VARIABLES
 */  
extern uint8_t g_usart2RecvFinish;      // 串口2接收標志串口接收完成標志
extern char g_bc26Buf[1024];            // 接收緩存
extern uint32_t g_bc26Cnt;              // 接收計數

/*********************************************************************
 * API FUNCTIONS
 */
uint8_t BC26_Init(void);
void BC26_GpioConfig(void);
void BC26_Connect(void);
void BC26_Send(char *pString);
uint32_t BC26_Receive(char *pRecvDataBuf);
void BC26_Reset(void);

#endif /* _BOARD_BC26_H_ */

六梢杭、使用方法

BC26_GpioConfig();
BC26_Init();
while(1)                                                            // 任務都是一個無限循環(huán)温兼,不能返回
{
    BC26_Send("TEST");
    vTaskDelay(10000);  
    char recvDataBuf[256] = {0};
    int recvDataLen = BC26_Receive(recvDataBuf);           
}
/**
 @brief 串口2收發(fā)中斷
 @param 無
 @return 無
*/
void USART2_IRQHandler(void)
{
    if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET)                           // 接收中斷
    {
        g_usart2RecvFinish = 1;                                                   // 串口2接收標志

        if(g_bc26Cnt >= sizeof(g_bc26Buf))
        {
            g_bc26Cnt = 0;                                                        // 防止串口被刷爆
        }

        g_bc26sBuf[g_bc26Cnt++] = USART2->DR;
        
        USART_ClearFlag(USART2, USART_FLAG_RXNE);
    }                                                            
}

? 由 Leung 寫于 2022 年 8 月 26 日

? 參考:移遠BC35-G模組(NB-IoT 通信模組)AT指令測試 UDP 通信過程
    NB-IOT(BC26)相關AT指令——UDP/TCP傳輸
    NB260軟件設計手冊

?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市武契,隨后出現的幾起案子募判,更是在濱河造成了極大的恐慌,老刑警劉巖咒唆,帶你破解...
    沈念sama閱讀 211,948評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件兰伤,死亡現場離奇詭異,居然都是意外死亡钧排,警方通過查閱死者的電腦和手機,發(fā)現死者居然都...
    沈念sama閱讀 90,371評論 3 385
  • 文/潘曉璐 我一進店門均澳,熙熙樓的掌柜王于貴愁眉苦臉地迎上來恨溜,“玉大人,你說我怎么就攤上這事找前≡阍” “怎么了?”我有些...
    開封第一講書人閱讀 157,490評論 0 348
  • 文/不壞的土叔 我叫張陵躺盛,是天一觀的道長项戴。 經常有香客問我,道長槽惫,這世上最難降的妖魔是什么周叮? 我笑而不...
    開封第一講書人閱讀 56,521評論 1 284
  • 正文 為了忘掉前任辩撑,我火速辦了婚禮,結果婚禮上仿耽,老公的妹妹穿的比我還像新娘合冀。我一直安慰自己,他們只是感情好项贺,可當我...
    茶點故事閱讀 65,627評論 6 386
  • 文/花漫 我一把揭開白布君躺。 她就那樣靜靜地躺著,像睡著了一般开缎。 火紅的嫁衣襯著肌膚如雪棕叫。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,842評論 1 290
  • 那天奕删,我揣著相機與錄音俺泣,去河邊找鬼。 笑死急侥,一個胖子當著我的面吹牛砌滞,可吹牛的內容都是我干的。 我是一名探鬼主播坏怪,決...
    沈念sama閱讀 38,997評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼贝润,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了铝宵?” 一聲冷哼從身側響起打掘,我...
    開封第一講書人閱讀 37,741評論 0 268
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎鹏秋,沒想到半個月后尊蚁,有當地人在樹林里發(fā)現了一具尸體,經...
    沈念sama閱讀 44,203評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡侣夷,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,534評論 2 327
  • 正文 我和宋清朗相戀三年横朋,在試婚紗的時候發(fā)現自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片百拓。...
    茶點故事閱讀 38,673評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡琴锭,死狀恐怖,靈堂內的尸體忽然破棺而出衙传,到底是詐尸還是另有隱情决帖,我是刑警寧澤,帶...
    沈念sama閱讀 34,339評論 4 330
  • 正文 年R本政府宣布蓖捶,位于F島的核電站地回,受9級特大地震影響,放射性物質發(fā)生泄漏。R本人自食惡果不足惜刻像,卻給世界環(huán)境...
    茶點故事閱讀 39,955評論 3 313
  • 文/蒙蒙 一畅买、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧绎速,春花似錦皮获、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,770評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至萌京,卻和暖如春雁歌,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背知残。 一陣腳步聲響...
    開封第一講書人閱讀 32,000評論 1 266
  • 我被黑心中介騙來泰國打工靠瞎, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人求妹。 一個月前我還...
    沈念sama閱讀 46,394評論 2 360
  • 正文 我出身青樓乏盐,卻偏偏與公主長得像,于是被迫代替她去往敵國和親制恍。 傳聞我的和親對象是個殘疾皇子父能,可洞房花燭夜當晚...
    茶點故事閱讀 43,562評論 2 349

推薦閱讀更多精彩內容