• STM32F103学习笔记(9)——NB-IoT模块BC26使用


    一、简介

    BC26 是一款高性能、低功耗、多频段 LTE Cat NB1/Cat NB2*无线通信模块。其尺寸仅为 17.7 mm × 15.8 mm × 2.0 mm,能最大限度地满足终端设备对小尺寸模块产品的需求,同时有效帮助客户减小产品尺寸并优化产品成本.BC26 在封装设计上兼容移远通信 GSM/GPRS 系列 M26 模块以及 NB-IoT 系列 BC28/BC25/BC260Y-CN 模块,方便客户快速、灵活的进行产品设计和升级。BC26 提供丰富的外部接口和协议栈,同时可支持中国移动 OneNET/Andlink、中国电信 IoT/ AEP以及阿里云IoT等物联网云平台,为客户的应用提供极大的便利。

    BC26 采用更易于焊接的 LCC 封装,可通过标准 SMT 设备实现模块的快速生产,为客户提供可靠的连接方式,并满足复杂环境下的应用需求。

    凭借紧凑的尺寸、超低功耗和超宽工作温度范围,BC26 成为 IoT 应用领域的理想选择,常被用于烟感、无线抄表、共享单车、智能停车、智慧城市、安防、资产追踪、智能家电、可穿戴设备、农业和环境监测以及其它诸多行业,以提供完善的短信和数据传输服务。

    BC26资料:链接:https://pan.baidu.com/s/1n8rcRCna8wnMPFwikY3l0g?pwd=vgcm 提取码:vgcm

    二、AT指令

    发送数据时务必勾选:“加回车换行符”。否则模块不会响应。在本文中,仅显示响应,省略回车换行符。

    2.1 AT

    测试AT指令功能是否正常,等待模块返回 OK。

    AT
    
    OK
    
    • 1
    • 2
    • 3

    2.2 AT+CIMI

    该命令用于查询(U)SIM 卡的国际移动用户识别码(IMSI,无双引号的字符串)。IMSI 允许 TE 识别连
    接到 MT 的 USIM。

    AT+CIMI
    
    460001357924680
    
    OK
    
    • 1
    • 2
    • 3
    • 4
    • 5

    2.3 AT+CGATT=1

    设置命令用于将 MT 附着于 PS 域。命令完成后,MT 保持在 V.250 命令状态。如果 MT 已经处于请求状态,则忽略该命令,并且仍将响应 OK。如果 MT 无法实现请求状态,将响应 ERROR 或+CME ERROR。

    AT+CGATT=1
    
    OK
    
    • 1
    • 2
    • 3

    2.4 AT+CGATT?

    查询命令返回当前 PS 域服务状态。

    AT+CGATT=?
    
    +CGATT: <state>
    如:+CGATT: 1
    
    OK
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    整型。PDP 上下文激活状态。

    • 0 去附着
    • 1 附着

    2.5 AT+CEREG?

    AT+CEREG?
    
    +CEREG: <n>,<stat>
    如:+CEREG: 0,1
    如:+CEREG: 1,1
    
    OK
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    整型。禁止或允许上报网络注册状态等信息。

    • 0 禁止上报网络注册状态 URC
    • 1 允许上报网络注册状态 URC +CEREG:
    • 2 允许上报网络注册状态和位置信息 URC +CEREG: [,[],[],[]]
    • 3 允许上报网络注册状态、位置信息和 EMM 原因值 URC +CEREG: [,[],[],[][,,]]
    • 4 对于请求 PSM 的 UE,允许上报网络注册状态和位置信息 URC +CEREG: [,[],[],[][,[,[],[]]]]
    • 5 对于请求 PSM 的 UE,允许上报网络注册状态、位置信息和 EMM 原因值 URC +CEREG: [,[],[],[][,[],[][,[, []]]]

    整型。EPS 注册状态。

    • 0 未注册,MT 当前未搜索网络
    • 1 已注册,归属网络
    • 2 未注册,但 MT 当前正在尝试附着或搜索网络以进行注册
    • 3 注册被拒绝
    • 4 未知(例如:超出 E-UTRAN 覆盖范围)
    • 5 已注册,漫游状态

    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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    2.7 AT+QIOPEN

    该命令用于打开套接字服务。

    • AT+QIOPEN=?:查询命令参数。
    • AT+QIOPEN=,,,/,[,[,]] :打开 Socket 服务。
      • :整数类型。上下文ID。范围是1-16。
      • :整数类型。套接字服务索引。范围是0-11。
      • :字符串类型。套接字服务类型。
        • “ TCP ” :作为客户端启动TCP连接
        • “ UDP ”:作为客户端启动UDP连接
        • “TCP LISTENER” :启动TCP服务器以侦听TCP连接
        • “UDP SERVICE” :启动UDP服务
      • :字符串类型。
        • 如果是TCP或UDP ,则表示远程服务器的IP地址,例如 “220.180.239.212”。
        • 如果是TCP LISTENER或UDP SERVICE 地址,请输入“127.0.0.1”。
      • :字符串类型。远程服务器的域名地址。
      • :远程服务器的端口,仅在为“TCP”或“UDP”时有效。范围是0-65535。
      • :本地端口。范围是0-65535。
        • 如果是“TCP LISTENER”或“UDP SERVICE”,则此参数必须指定。
        • 如果是“TCP”或“UDP”。如果为0,那么本地端口将是自动分配。否则,将按指定分配本地端口。
      • :整数类型。套接字服务的数据访问模式。
        • 0: 缓冲区访问模式
        • 1:直推模式
        • 2:透明访问模式
      • :整数类型。操作的错误代码。请参阅第4章。
    AT+QIOPEN=1,0,\"TCP\",\"180.97.81.180\",53540,0,1
    
    OK
    
    +QIOPEN: 0,0
    
    • 1
    • 2
    • 3
    • 4
    • 5

    Buffer模式,Push模式,透传模式。通过参数进行配置。





    2.8 AT + QISEND

    如果指定套接字服务的是缓冲区访问模式或直接推送模式,则数据可以是通过AT + QISEND发送。如果数据成功发送到模块,将返回“ SEND OK ” 。否则它将返回“ SEND FAIL ” 或“ ERROR ” 。“ SEND FAIL ” 表示发送缓冲区已满客户可以尝试重新发送数据。“ERROR”表示在发送过程中遇到错误 数据。客户应该延迟一段时间来发送数据。最大数据长度为1460字。“SEND OK”并不意味着数据已成功发送到服务器。客户可以查询数据是否通过AT + QISEND = ,0命令到达服务器。透传模式下不需要AT指令发送数据

    三、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状态,重启模块
                    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域服务状态
                {
                    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 发送数据到TCP服务器
     @param pString -[in] 发送数据
     @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次)
                {    
                    /* 发送数据成功,对方收到数据 */
                }
                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 发送AT命令
     @param pCmd -[in] 命令字符串
     @param pRes -[in] 需要检测的返回命令字符串
     @param pRes2 -[in] 需要检测的返回命令字符串
     @param timeOut -[in] 等待时间
     @param sendNum -[in] 命令发送次数
     @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****************************************************/
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219
    • 220
    • 221
    • 222
    • 223
    • 224
    • 225
    • 226
    • 227
    • 228
    • 229
    • 230
    • 231
    • 232
    • 233
    • 234
    • 235
    • 236
    • 237
    • 238
    • 239
    • 240
    • 241
    • 242
    • 243
    • 244
    • 245
    • 246
    • 247
    • 248
    • 249
    • 250
    • 251
    • 252
    • 253
    • 254
    • 255
    • 256
    • 257
    • 258
    • 259
    • 260
    • 261
    • 262
    • 263
    • 264
    • 265
    • 266
    • 267
    • 268
    • 269
    • 270
    • 271
    • 272
    • 273
    • 274
    • 275
    • 276
    • 277
    • 278
    • 279
    • 280
    • 281
    • 282
    • 283
    • 284
    • 285
    • 286
    • 287

    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_ */
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    六、使用方法

    BC26_GpioConfig();
    BC26_Init();
    while(1)                                                            // 任务都是一个无限循环,不能返回
    {
        BC26_Send("TEST");
        vTaskDelay(10000);  
        char recvDataBuf[256] = {0};
        int recvDataLen = BC26_Receive(recvDataBuf);           
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    /**
     @brief 串口2收发中断
     @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);
        } 				 											 
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    • 由 Leung 写于 2022 年 8 月 26 日

    • 参考:移远BC35-G模组(NB-IoT 通信模组)AT指令测试 UDP 通信过程
        NB-IOT(BC26)相关AT指令——UDP/TCP传输
        NB260软件设计手册

  • 相关阅读:
    JavaScript 67 JavaScript HTML DOM 67.4 JavaScript - HTML DOM 元素
    uniapp小程序实现上下固定中间滑动布局(附源码和实现过程讲解)
    数组补全(秋季每日一题 10)
    Keras深度学习实战——推荐系统数据编码
    C语言--五子棋项目【图文详解 经典】
    进程(2)——进程状态(僵尸,睡眠……)【linux】
    Google Earth Engine-04(GEE中的JavaScript和原始JavaScript的异同和联系)
    【C#】【SAP2000】OAPI文档案例详解
    基于ECS搭建云上博客WordPress,使用Apache+MariaDB+PHP环境
    Python 小贴士(3)
  • 原文地址:https://blog.csdn.net/qq_36347513/article/details/126544371