一、簡介
BC26 是一款高性能、低功耗、多頻段 LTE Cat NB1/Cat NB2*無線通信模塊。其尺寸僅為 17.7 mm × 15.8 mm × 2.0 mm,能最大限度地滿足終端設(shè)備對小尺寸模塊產(chǎn)品的需求,同時有效幫助客戶減小產(chǎn)品尺寸并優(yōu)化產(chǎn)品成本.BC26 在封裝設(shè)計上兼容移遠通信 GSM/GPRS 系列 M26 模塊以及 NB-IoT 系列 BC28/BC25/BC260Y-CN 模塊,方便客戶快速、靈活的進行產(chǎn)品設(shè)計和升級。BC26 提供豐富的外部接口和協(xié)議棧,同時可支持中國移動 OneNET/Andlink、中國電信 IoT/ AEP以及阿里云IoT等物聯(lián)網(wǎng)云平臺,為客戶的應(yīng)用提供極大的便利。
BC26 采用更易于焊接的 LCC 封裝,可通過標(biāo)準(zhǔn) SMT 設(shè)備實現(xiàn)模塊的快速生產(chǎn),為客戶提供可靠的連接方式,并滿足復(fù)雜環(huán)境下的應(yīng)用需求。
憑借緊湊的尺寸、超低功耗和超寬工作溫度范圍,BC26 成為 IoT 應(yīng)用領(lǐng)域的理想選擇,常被用于煙感、無線抄表、共享單車、智能停車、智慧城市、安防、資產(chǎn)追蹤、智能家電、可穿戴設(shè)備、農(nóng)業(yè)和環(huán)境監(jiān)測以及其它諸多行業(yè),以提供完善的短信和數(shù)據(jù)傳輸服務(wù)。
BC26資料:鏈接:https://pan.baidu.com/s/1n8rcRCna8wnMPFwikY3l0g?pwd=vgcm 提取碼:vgcm
二、AT指令
發(fā)送數(shù)據(jù)時務(wù)必勾選:“加回車換行符”
。否則模塊不會響應(yīng)。在本文中,僅顯示響應(yīng),省略回車換行符。
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
設(shè)置命令用于將 MT 附著于 PS 域。命令完成后,MT 保持在 V.250 命令狀態(tài)。如果 MT 已經(jīng)處于請求狀態(tài),則忽略該命令,并且仍將響應(yīng) OK。如果 MT 無法實現(xiàn)請求狀態(tài),將響應(yīng) ERROR 或+CME ERROR。
AT+CGATT=1
OK
2.4 AT+CGATT?
查詢命令返回當(dāng)前 PS 域服務(wù)狀態(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> 整型。禁止或允許上報網(wǎng)絡(luò)注冊狀態(tài)等信息。
- 0 禁止上報網(wǎng)絡(luò)注冊狀態(tài) URC
- 1 允許上報網(wǎng)絡(luò)注冊狀態(tài) URC +CEREG: <stat>
- 2 允許上報網(wǎng)絡(luò)注冊狀態(tài)和位置信息 URC +CEREG: <stat>[,[<tac>],[<ci>],[<AcT>]]
- 3 允許上報網(wǎng)絡(luò)注冊狀態(tài)、位置信息和 EMM 原因值 URC +CEREG: <stat>[,[<tac>],[<ci>],[<AcT>][,<cause_type>,<reject_cause>]]
- 4 對于請求 PSM 的 UE,允許上報網(wǎng)絡(luò)注冊狀態(tài)和位置信息 URC +CEREG: <stat>[,[<tac>],[<ci>],[<AcT>][,,[,[<Active-Time>],[<Periodic-TAU>]]]]
- 5 對于請求 PSM 的 UE,允許上報網(wǎng)絡(luò)注冊狀態(tài)、位置信息和 EMM 原因值 URC +CEREG: <stat>[,[<tac>],[<ci>],[<AcT>][,[<cause_type>],[<reject_cause>][,[<Active-Time>, [<Periodic-TAU>]]]]
<stat> 整型。EPS 注冊狀態(tài)。
- 0 未注冊,MT 當(dāng)前未搜索網(wǎng)絡(luò)
- 1 已注冊,歸屬網(wǎng)絡(luò)
- 2 未注冊,但 MT 當(dāng)前正在嘗試附著或搜索網(wǎng)絡(luò)以進行注冊
- 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
該命令用于打開套接字服務(wù)。
- AT+QIOPEN=?:查詢命令參數(shù)。
- AT+QIOPEN=<contextID>,<connectID>,<service_type>,<IP_address>/<domain_name>,<remote_port>[,<local_po CONNECTrt>[,<access_mode>]] :打開 Socket 服務(wù)。
- <contextID> :整數(shù)類型。上下文ID。范圍是1-16。
- <connectID> :整數(shù)類型。套接字服務(wù)索引。范圍是0-11。
- <SERVICE_TYPE>:字符串類型。套接字服務(wù)類型。
- “ TCP ” :作為客戶端啟動TCP連接
- “ UDP ”:作為客戶端啟動UDP連接
- “TCP LISTENER” :啟動TCP服務(wù)器以偵聽TCP連接
- “UDP SERVICE” :啟動UDP服務(wù)
- <IP_address>:字符串類型。
- 如果<service_type>是TCP或UDP ,則表示遠程服務(wù)器的IP地址,例如 “220.180.239.212”。
- 如果<service_type>是TCP LISTENER或UDP SERVICE 地址,請輸入“127.0.0.1”。
- <domain_name>:字符串類型。遠程服務(wù)器的域名地址。
- <remote_port> :遠程服務(wù)器的端口,僅在<service_type>為“TCP”或“UDP”時有效。范圍是0-65535。
- <LOCAL_PORT> :本地端口。范圍是0-65535。
- 如果<service_type>是“TCP LISTENER”或“UDP SERVICE”,則此參數(shù)必須指定。
- 如果<service_type>是“TCP”或“UDP”。如果<local_port>為0,那么本地端口將是自動分配。否則,將按指定分配本地端口。
- <access_mode> :整數(shù)類型。套接字服務(wù)的數(shù)據(jù)訪問模式。
- 0: 緩沖區(qū)訪問模式
- 1:直推模式
- 2:透明訪問模式
- <err>:整數(shù)類型。操作的錯誤代碼。請參閱第4章。
AT+QIOPEN=1,0,\"TCP\",\"180.97.81.180\",53540,0,1
OK
+QIOPEN: 0,0
Buffer模式,Push模式,透傳模式。通過參數(shù)<access_mode>進行配置。
2.8 AT + QISEND
如果指定套接字服務(wù)的<access_mode>是緩沖區(qū)訪問模式或直接推送模式,則數(shù)據(jù)可以是通過AT + QISEND發(fā)送。如果數(shù)據(jù)成功發(fā)送到模塊,將返回“ SEND OK ” 。否則它將返回“ SEND FAIL ” 或“ ERROR ” 。“ SEND FAIL ” 表示發(fā)送緩沖區(qū)已滿客戶可以嘗試重新發(fā)送數(shù)據(jù)。“ERROR”表示在發(fā)送過程中遇到錯誤 數(shù)據(jù)。客戶應(yīng)該延遲一段時間來發(fā)送數(shù)據(jù)。最大數(shù)據(jù)長度為1460字。“SEND OK”并不意味著數(shù)據(jù)已成功發(fā)送到服務(wù)器。客戶可以查詢數(shù)據(jù)是否通過AT + QISEND = <connectID>,0命令到達服務(wù)器。透傳模式下不需要AT指令發(fā)送數(shù)據(jù)
三、TCP應(yīng)用時序圖
四、復(fù)位模塊
通過拉低 RESET 引腳至少 50ms 可以使模塊復(fù)位。
五、移植文件
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接收標(biāo)志串口接收完成標(biāo)志
char g_bc26Buf[1024] = {0}; // 接收緩存
volatile uint32_t g_bc26Cnt; // 接收計數(shù)
/*********************************************************************
* 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秒內(nèi),無法識別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網(wǎng)絡(luò)
{
step++;
}
else
{
printf("Err:AT+CEREG?\r\n");
step++;
}
case 4:
if(sendCmd("AT+CGATT?\r\n", "+CGATT: 1", NULL, 85, 3)) // 查詢當(dāng)前PS域服務(wù)狀態(tài)
{
step++;
}
else
{
printf("Err:AT+CGATT?\r\n"); // 如果3次都沒停止成功或超過85秒沒有回應(yīng),則重啟模塊
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秒沒有回應(yīng),則重啟模塊
reset();
break;
}
}
return result;
}
/**
@brief 復(fù)位引腳配置
@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; // 設(shè)置引腳工作模式為通用推挽輸出
gpioInitStructure.GPIO_Speed = GPIO_Speed_50MHz; // 設(shè)置引腳輸出最大速率為50MHz
GPIO_Init(GPIOB, &gpioInitStructure);
GPIO_SetBits(GPIOB, GPIO_Pin_8);
}
/**
@brief 連接TCP服務(wù)器
@param 無
@return 無
*/
void BC26_Connect(void)
{
if(sendCmd("AT+QICLOSE=0\r\n", "OK", NULL, 20, 1)) // 關(guān)閉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(); // 沒有響應(yīng)重啟模塊
}
}
}
/**
@brief 發(fā)送數(shù)據(jù)到TCP服務(wù)器
@param pString -[in] 發(fā)送數(shù)據(jù)
@return 無
*/
void BC26_Send(char *pString)
{
if(sendCmd("AT+QISEND=0\r\n", ">", NULL, 30, 2)) // 等待60秒,沒有響應(yīng)重啟模塊
{
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ā)送數(shù)據(jù)成功,對方收到數(shù)據(jù) */
}
else
{
printf("Err:AT+QISEND=0\r\n");
if(sendCmd("AT+QICLOSE=0\r\n", "OK", NULL, 20, 1)) // TCP連接出現(xiàn)異常,關(guān)閉TCP連接
{
printf("AT+QICLOSE\r\n");
BC26_Connect();
}
}
}
else
{
BC26_Reset(); // 等待60秒,沒有響應(yīng)重啟模塊
}
}
else
{
BC26_Reset(); // 等待60秒,沒有響應(yīng)重啟模塊
}
}
/**
@brief 從TCP服務(wù)器接收數(shù)據(jù)
@param pRecvDataBuf -[out] 接收數(shù)據(jù)
@return 接收數(shù)據(jù)長度
*/
uint32_t EC200S_Receive(char *pRecvDataBuf)
{
uint32_t recvDataLen = 0;
if(g_isUsart2RecvFinish) // 如果串口接收完成
{
if(strstr((const char *)g_bc26Buf, "+QIURC: \"recv\",0,") != NULL) // 如果檢索到關(guān)鍵詞
{
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ā)送次數(shù)
@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) // 如果檢索到關(guān)鍵詞
{
printf("%s", g_bc26Buf);
return 1;
}
else if(strstr((const char *)g_bc26Buf, pRes2) != NULL)) // 如果檢索到關(guān)鍵詞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接收標(biāo)志串口接收完成標(biāo)志
extern char g_bc26Buf[1024]; // 接收緩存
extern uint32_t g_bc26Cnt; // 接收計數(shù)
/*********************************************************************
* 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) // 任務(wù)都是一個無限循環(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接收標(biāo)志
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)相關(guān)AT指令——UDP/TCP傳輸
NB260軟件設(shè)計手冊