前言

参考文章/博主

NB-loT

硬件

【NB-IoT模组】

  • 型号: 移远BC260Y-CN,链接:移远
  • 网络类型:支持中国移动、中国联通和中国电信的NB-IoT网络。电信 & 联通的NB-IoT卡由于限制多,故需要实际测试,不保证100%支持
  • 通信速率: Single Tone: 25.5 (DL)/16.7 (UL) Multi Tone: 25.5 (DL)/62.5 (UL),或者Max. 127(DL)/158.5(UL)
  • 支持的通信协议: UDP/ TCP/ LwM2M/ MQTT/ SNTP/TLS/ SSL/ PPP/ HTTP/ HTTPS/CoAP
  • 耗流:

0.8μA @PSM

0.038mA @ 空闲模式 (eDRX=40.96s)

0.11mA @ 空闲模式 (DRX=2.56s)

【物联网卡】

(1)默认配套:中国移动 NB-IoT上网卡
(2)300M/年,可用1年,满足测试需求

开发板支持中国移动NB-IoT卡

NB-IoT卡属于物联网卡的一种,但NB-IoT卡≠物联网卡,故物联网卡并非都可用于本模块

中国电信 & 联通卡的NB-IoT卡由于限制条件多,故非100%支持

卡激活后,只能在所激活的省份内使用

NB-IoT卡一旦激活后,就不能拔出来插入其它设备,否则卡会锁定

【硬件】

简介

平常所讲的2G、3G、4G中的G其实是Generation的意思,例如2G是指第二代移动通信技术。每一代移动通信技术都有对应的网络制式:

  1. 中国移动
  • 2G -> GSM
  • 3G -> TD-SCDMA
  • 4G -> TD-LTE
  1. 中国联通
  • 2G -> GSM
  • 3G -> WCDMA
  • 4G -> TD-LTE、FDD-LTE
  1. 中国电信
  • 2G -> CDMA1X
  • 3G -> CDMA2000
  • 4G -> TD-LTE、FDD-LTE

WPAN
WPAN的全称是Low-rate wireless personal area network(低速无线个人区域网络),典型的代表有 ZigBee、蓝牙 等,其特点是低复杂度、低功耗、低速率、低成本

LPWA
LPWA的全称是Low Power Wide Area(低速无线广域网),典型的代表有 NB-IoT、eMTC、Lora和SigFox 等,具有广覆盖、低功耗、低成本和大连接的特点

NB-IoT是基于LTE制式的一种移动通信技术,也就是说,它是4G技术的一种

下图是LTE制式通信技术的各个分支对比:

LTE Cat-NB即NB-IoT,是基于LTE网络制式而设计的一种 具有低功耗、低速率、上行通信时延低和下行通信时延较高(相对) 特点的通信技术,满足对低功耗要求高但是对通信速率要求低的应用场景。上图中,我们还可以看到LTE Cat-M,它支持的通信速率比NB-IoT更高,可以应用于对通信速率要求更高的应用场景,应用场景:

  • 环境温湿度、光照度、气体成分等信息的采集上报
  • 火灾、煤气泄漏、水浸等环境异常状态监控与报警场景
  • 外部设备状态信息采集与上报场景,例如远程抄表

NB-IoT的下行通信时延较高,约15秒左右(需要以实际测试为准),所以如果把NB-IoT用于下发指令或数据给终端设备,需要考虑这个时延问题。如需对下行通信速率有更快速的要求,可以采用eMTC或CATn等技术

模块常用指令测试

需要打开 ATCmdsTool V5.2.0 软件,模块使用 USB转串口进行连接,4根线即可,3.3V供电

AT指令 参数 返回值 作用
AT+QSCLK= 整型。
0 禁用休眠模式。
1 启用轻休眠(Light Sleep)和深休眠(Deep Sleep),并通过 PSM_EINT(下降沿)唤醒深休眠
2 仅启用轻休眠,并通过主串口唤醒
OK 配置休眠模式
ATI / Quectel_Ltd(模块制造商)
Quectel_BC260Y-CN(模块型号)
Revision: BC260YCNAAR02A02(制造商修订信息)
读取模块完整信息
AT+CGMM / Quectel_BC260Y-CN

OK
读取制造商模块号
AT+CGMR / Revision: BC260YCNAAR02A02

OK
读取制造商修订信息
AT+CGSN= snt: 要读取产品的哪种序列号
0: 唯一码
1: 国际移动设备识别码
2: 国际移动设备识别码的软件版本号
3: 软件版本号
MP823G70607252204

OK
读取模块唯一码
同上 同上 +CGSN: 862745063975087

OK
查询IMEI
同上 同上 +CGSN: 8627450639750802

OK
查询识别码和软件版本号
同上 同上 +CGSN: 02

OK
查询软件版本号
AT+CSCON? 第二个返回数值代表状态,1:连接,0:空闲状态;如果没有数据交互,连接状态持续20秒,之后进入到空闲状态中,如果仍然没有数据交互,10秒后从空闲状态进入到PSM睡眠模式,这时候模块不再接收如何下行数据,模块只有在连接和空闲的状态下才能交互数据! +CSCON: 0,0

OK
读取信令连接状态
AT+CEREG? +CEREG:,[…]
Integer type, indicates the EPS registration status
1 - Registered, home network
其他数值 - 注册不成功
+CEREG: 0,1

OK
读取网络注册状态
AT+CGATT? 0:没有附着
1:附着成功
+CGATT: 1

OK
读取网络附着状态
AT+CGPADDR? / +CGPADDR: 0,“100.67.249.195”

OK
读取设备IP地址
AT+CSQ 模块返回的第1个参数表示强度,越大表示信号越好
0:-113dBm 或更低
1:``-111dBm<br>2~30: -109dBm~-53dBm`
31:-51dBm 或更高
99:未知或无法检测
+CSQ: 8,0

OK
读取信号强度
AT+QBAND? / +QBAND: 5,8,3

OK
查询模块的工作频段
AT+QBAND=n 根据NB-IoT所属的营运商选择对应的频段
3: 中国联通 5: 中国电信 8: 中国移动
OK 设置模块的工作频段
AT+CCLK? +CCLK:[<yy/MM/dd,hh:mm:ss>[<±zz>]
格式: 年月日, 时分秒, 时区
+CCLK: “2023/12/14,09:01:13+32”

OK
获取当前时间
AT+QRST=1 / OK

RDY

+CFUN: 1

+CPIN: READY

+IP: 100.85.29.214
重启模块

程序

结合上面,有上传和下发

  • MX配置

串口2打开,中断打开,DMA中断不打开,DMA接收循环模式,注意要上拉!

  • 程序编写
AllHead.h
#ifndef __ALLHEAD_H
#define __ALLHEAD_H
#include "main.h"
#include "tim.h"
#include "usart.h"
#include "gpio.h"

#include "CallBack.h"
#include "Public.h"
#include "System.h"
#include "System_Init.h"
#include "Task.h"

# include <string.h>
# include <stdarg.h>
# include <stdlib.h>
# include "stdio.h"
#include "stdint.h"
#include "math.h"

#include "bsp_4GCat_uart.h"
#include "bsp_4GCat.h"

#endif
CallBack.c
/***************************************************************************
 * File: CallBack.c
 * Author: Luckys.
 * Date: 2023/06/19
 * description: store interrupt function
****************************************************************************/
#include "AllHead.h"

/* Public variables==========================================================*/
extern uint8_t ucUart1_Rec_Buff[128];
char AT_String[255];
extern const char *TOPIC_PROPERTY_SUB;
/*
* @function: HAL_TIM_PeriodElapsedCallback
* @param: None
* @retval: None
* @brief: timer callback function
*/
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
  if (htim->Instance == htim6.Instance) // 1ms
  {
    static uint8_t cat_uart_rx = 0;
    static uint16_t cat_reset_cnt = 0;  // 模块复位所需时间计数

    cat_uart_rx++;

    if (cat_uart_rx >= 50)  // 串口接收回传校验
    {
      cat_uart_rx = 0;
      bsp_4GCat_uart.Bsp_4GCat_Uart_Handler();
    }
    if (bsp_4GCat.Reset_Status != RESET_OVER) // 复位计数
    {
      cat_reset_cnt++;

      if (cat_reset_cnt >= BSP_4GCAT_RESET_TIME)
      {
        cat_reset_cnt = 0;
        bsp_4GCat.Reset_Status = RESET_OVER;  // 复位完成
      }
    }

    System.Task_Marks_Handler();
  }
}

void USART2_IRQHandler(void)
{
	if(__HAL_UART_GET_FLAG(&huart2,UART_FLAG_IDLE) != 0x00u)
	{
		bsp_4GCat_uart.Bsp_4GCat_Uart_Data_Parse();
		__HAL_UART_CLEAR_IDLEFLAG(&huart2);
	}		
  HAL_UART_IRQHandler(&huart2);
}

void USART1_IRQHandler(void)
{
	if(__HAL_UART_GET_FLAG(&huart1,UART_FLAG_IDLE) != 0x00u)
	{
		__HAL_UART_CLEAR_IDLEFLAG(&huart1);
    HAL_UART_DMAStop(&huart1);   // 串口停止DMA接收
		if (strstr((char*)ucUart1_Rec_Buff, (char*)"MQTT_DISABLE") != NULL) // 断开MQTT连接
		{
			bsp_4GCat.bsp_4GCat_Step_Status = TASK_CIOT_MQTT_DISCONN; // 断开
		}
    if (strstr((char*)ucUart1_Rec_Buff, (char*)"GPS_GET") != NULL)
    {
      bsp_4GCat_uart.Bsp_4GCat_Uart_Send_String((const uint8_t *)"AT+CGPSINFO\r\n");
    }
    if (strstr((char*)ucUart1_Rec_Buff, (char*)"CLOSE_SUB") != NULL)  // 退订
    {
      sprintf(AT_String, "AT+CMQTTUNSUB==0,%d,9\r\n", strlen(TOPIC_PROPERTY_SUB));           
      bsp_4GCat_uart.Bsp_4GCat_Uart_Send_String((const uint8_t *)AT_String);
    }
    if (strstr((char*)ucUart1_Rec_Buff, (char*)"CLOSE_SUB_Theme") != NULL)  // 退订
    {       
      bsp_4GCat_uart.Bsp_4GCat_Uart_Send_String((const uint8_t *)TOPIC_PROPERTY_SUB);
    }
    HAL_UART_Receive_DMA(&huart1, ucUart1_Rec_Buff, 128);    
	}
	
	HAL_UART_IRQHandler(&huart1);
}
System_Init.c
/***************************************************************************
 * File: System_Init.c
 * Author: Luckys.
 * Date: 2023/06/23
 * description: 存放系统初始化
****************************************************************************/
#include "AllHead.h"

/*====================================static function declaration area BEGIN====================================*/
static void Hardware_Init(void);
/*====================================static function declaration area   END====================================*/
System_Init_t System_Init = 
{
    .Hardware_Init = &Hardware_Init
};

/* Public variables==========================================================*/
uint8_t ucUart1_Rec_Buff[128] = {0};    // 串口1接收缓存数组



/*
* @function: Hardware_Init
* @param: None
* @retval: None
* @brief: 硬件模块初始化
*/
static void Hardware_Init(void)
{
#if 1	// 串口1空闲中断+DMA
    __HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE); // 使能串口空闲中断
    HAL_UART_Receive_DMA(&huart1, ucUart1_Rec_Buff, 128);	
#endif	
    bsp_4GCat_uart.Bsp_4GCat_Uart_Init();   // 模块串口初始化
    printf("Hello\r\n");

	HAL_TIM_Base_Start_IT(&htim6);
}
Task.c
/***************************************************************************
 * File: Task.c
 * Author: Luckys.
 * Date: 2023/06/23
 * description: 
****************************************************************************/
#include "AllHead.h"

/* Public variables==========================================================*/
extern const char *TOPIC_PROPERTY_SUB;

/*====================================static function declaration area BEGIN====================================*/
static void TasksHandle_200MS(void);
static void TasksHandle_1S(void);
static void TasksHandle_2S(void);

/*====================================static function declaration area   END====================================*/

Task_t Task[] =
{
				{FALSE, 200, 200, TasksHandle_200MS}, // task Period: 200ms
				{FALSE, 1000, 1000, TasksHandle_1S},	// task Period: 1s
				{FALSE, 2000, 2000, TasksHandle_2S},	// task Period: 2s
};

/*====================================variable definition declaration area BEGIN===================================*/

uint8_t ucTasks_Max = sizeof(Task) / sizeof(Task[0]);

/*====================================variable definition declaration area   END===================================*/

static inline void TasksHandle_200MS(void)
{
	bsp_4GCat.bsp_4GCat_Running();
	bsp_4GCat.bsp_4GCat_Sub_Theme(TOPIC_PROPERTY_SUB);
}

static inline void TasksHandle_1S(void)
{
	bsp_4GCat.bsp_4GCat_Data_Upload_1();
}

static inline void TasksHandle_2S(void)
{ 
	bsp_4GCat.bsp_4GCat_GPS_Handler();
}
bsp_4GCat.h
#ifndef __BSP_4GCAT_H
#define __BSP_4GCAT_H

// 使用调试打印(可以打印发送与接收回传数据到上位机串口助手)
#define USE_Debug_Print 0
// 使用正常打印
#define USE_Normal_Print 1
// 模块复位完成所需时间(大概10s)
#define BSP_4GCAT_RESET_TIME (uint16_t)10000
// 等待回传的计数值(测试发现大概正常情况下收到回传是330000左右,所以需要设置比它大几倍即可)
#define BSP_4GCAT_WAIT_COUNT 2000000
// 全自动模式(断开连接后自动重新复位连接)
#define BSP_4GCAT_AUTO_CONNNECT_MODE 1

/* CAT1模块工作步骤列表 */
typedef enum
{
    TASK_CIOT_AT_TEST = 0,  // 【AT】测试模块是否正常
    TASK_CIOT_AT_TEST_RSP,

    TASK_CIOT_CLOSE_ECHO,   // 【ATE0】关闭命令回显
    TASK_CIOT_CLOSE_ECHO_RSP,

    TASK_CIOT_CGATT,    // 【AT+CGATT?】查询网络附着状态  0--未附着 1--已附着成功
    TASK_CIOT_CGATT_RSP,

    TASK_CIOT_CGREG,    // 【AT+CGREG?】查询网络注册状态  0,1--已注册本地网络 0,5--已注册,但是处于漫游状态
    TASK_CIOT_CGREG_RSP,

    TASK_GPS_START, // 【AT+CGNSSPWR=1】 启动GPS
    TASK_GPS_START_RSP,

    TASK_CIOT_MQTT_START,   // 【AT+CMQTTSTART】启动MQTT功能
    TASK_CIOT_MQTT_START_RSP,

    TASK_CIOT_MQTT_OPEN,    // 【AT+CMQTTACCQ=0,[CLIENT_ID],0】打开MQTT连接,CLIENT_ID是上面定义的那个
    TASK_CIOT_MQTT_OPEN_RSP,

    TASK_CIOT_MQTT_CONNECT, // 【AT+CMQTTCONNECT=0,[DOMAIN],60,1,[USERNAME],[PASSWORD]】连接MQTT服务器
    TASK_CIOT_MQTT_CONNECT_RSP,

    TASK_CONNECT_SUCCESS,   // 连接MQTT服务器成功(结束)

    TASK_CIOT_MQTT_DISCONN, // 【AT+CMQTTDISC=0,120】断开MQTT连接
    TASK_CIOT_MQTT_DISCONN_RSP,

    TASK_CIOT_MQTT_CLOSE,   // 【AT+CMQTTREL=0】释放MQTT相关的资源
    TASK_CIOT_MQTT_CLOSE_RSP,

    TASK_CIOT_MQTT_STOP,    // 【AT+CMQTTSTOP】关闭MQTT功能
    TASK_CIOT_MQTT_STOP_RSP,

    TASK_CIOT_RESET,    // 【AT+CRESET】 模块复位
    TASK_CIOT_RESET_RSP,

    TASK_IDLE,  // 空闲状态(空转)
} bsp_4GCat_Mode_Steps_et;

// 返回状态枚举
typedef enum
{
    RET_PASS = -1,    // 成功
    RET_FAIL = 0    // 失败
} bsp_4GCat_Return_Status_et;

// 复位状态枚举
typedef enum
{
    RESET_NOT = 0,   // 未复位
    RESET_OVER = 1,  // 复位完成
} bsp_4GCat_Reset_Status_et;

// 模块接收状态
typedef enum
{
    Rec_Status_None = 0, // 不接收
    Rec_Status_CheckCmd_Plan1 = 1, // 校验指令回传 --- "OK"
    Rec_Status_CheckCmd_Plan2 = 2, // 校验指令回传 --- "+CGATT: 1"
    Rec_Status_CheckCmd_Plan3 = 3, // 校验指令回传 --- "+CGREG: 0,1 / +CGREG: 0,5"
    Rec_Status_CheckCmd_Plan4 = 4, // 校验指令回传 --- "+CMQTTCONNECT: 0,0"
    Rec_Status_CheckCmd_Plan5 = 5, // 校验指令回传 --- ">"
    Rec_Status_CheckCmd_Plan6 = 6, // 校验指令回传 --- "+CGNSSPWR:READY!"
    Rec_Status_CheckCmd_Plan7 = 7, // GPS数据
    Rec_Status_CheckCmd_Plan8 = 8, // 订阅主题成功 --- "+CMQTTSUB: 0,0"
    Rec_Status_MAX = Rec_Status_CheckCmd_Plan8 + 1,     // 枚举成员数量(当做数组的大小)
} bsp_4GCat_Rec_Status_et;

// GNSS信息结构体
typedef struct
{
    float latitude; // 存储纬度信息
    char NS;        // 纬度方向(北纬或南纬)

    float longitude; // 存储经度信息
    char EW;         // 经度方向(东经或西经)

#if 1 /* Ignores */
    struct
    {
        uint16_t year;        // 年份
        uint8_t month;        // 月份
        uint8_t day;          // 日期
        uint8_t hour;         // 小时
        uint8_t minute;       // 分钟
        uint8_t second;       // 秒
        uint16_t millisecond; // 毫秒
    } UTC;                    // 存储协调世界时(UTC)时间信息
#endif

    float altitude; // 存储海拔信息

    float speedOverGround; // 存储地面速度信息
} GNSSInfo_t;

typedef struct
{
    uint8_t bsp_GPS_Relay_Flag; // GPS装备完成标志位
    float Latitude;  // 纬度
    float Longitude; // 经度
} bsp_GPS_st;

typedef struct
{
    uint8_t bsp_4GCat_Sub_Pass_Flag; // 订阅主题成功标志位
    bsp_4GCat_Reset_Status_et Reset_Status; // 复位状态
    uint8_t bsp_4GCat_MQTT_Connect_Flag;    // 连接MQTT服务器标志位
    int8_t bsp_4GCat_Ret_Status_buf[Rec_Status_MAX];    // 存储返回值数组
    bsp_4GCat_Rec_Status_et bsp_4GCat_Rec_Status;   // 模块接收状态
    int16_t bsp_4GCat_Step_Status;  // AT模块工作步骤标记状态

    uint8_t bsp_4GCat_Pub_Buf[254]; // MQTT数据上报存储数组
    uint8_t bsp_4GCat_Pub_Len;  // MQTT数据上报长度

    void (*bsp_4GCat_Running)(void);   // 模块运行
    void (*bsp_4GCat_Data_Upload_1)(void); // 数据上传1
    int8_t (*bsp_4GCat_GPS_Get_Data)(uint8_t*);   // 获取GPS数据
    void (*bsp_4GCat_GPS_Handler)(void);   // GPS处理函数
    void (*bsp_4GCat_Sub_Theme)(const char*);   // 订阅主题
    void (*bsp_4GCat_Sub_Data_Analyze)(uint8_t *);    // 订阅主题数据解析
} bsp_4GCat_st;

extern bsp_4GCat_st bsp_4GCat;
extern bsp_GPS_st bsp_GPS;

#endif
bsp_4GCat.c
/***************************************************************************
 * File: bsp_4GCat.c
 * Author: Yang
 * Date: 2023/12/06
 * description: 
 -----------------------------------
通过使能宏【BSP_4GCAT_AUTO_CONNNECT_MODE】决定是否要复位后或者/断开MQTT后重新进行连接
如果开启了MQTT功能需要关闭后下一次才能正常进行连接到MQTT服务器,不能直接复位!!!!
 -----------------------------------
****************************************************************************/
#include "AllHead.h"

/* Private variables=========================================================*/

/* 腾讯云MQTT服务器信息 */
// 域名与端口
static const char *DOMAIN = "tcp://J7X2YMW6IU.iotcloud.tencentdevices.com:1883";
// 客户端ID
static const char *CLIENT_ID = "J7X2YMW6IU";
// 用户名
static const char *USERNAME = "J7X2YMW6IUTH_Sensor_Test;12010126;0M3Y7;1992171084";
// 密码
static const char *PASSWORD = "a88fc461af8202b6933524ec9b5c9d2fac226a35cbc09b9d4150a1d00000406f;hmacsha256";
// 属性发布主题
static const char *TOPIC_PROPERTY_PUB = "$thing/up/property/J7X2YMW6IU/TH_Sensor_Test";
// 订阅MQTT主题
const char *TOPIC_PROPERTY_SUB = "$thing/down/property/J7X2YMW6IU/TH_Sensor_Test";
// 模块超时计数
int16_t bsp_4GCat_Timeout_Count;    
// 模块AT指令字符串
char bsp_4GCat_AT_String[255];  

/* Private function prototypes===============================================*/
static void bsp_4GCat_Running(void);
static void bsp_4GCat_Data_Upload_1(void);

static void bsp_4GCat_Ret_Check(int8_t ret_status);
static void bsp_4GCat_BeforeSending_Parameter_Init(bsp_4GCat_Rec_Status_et rec_status, uint8_t* str);
static void bsp_4GCat_Moduel_Reset(void);
static void bsp_4GCat_Upload_Data_To_Pub(const char* pub_string, const char* pub_data_string);
static void bsp_4GCat_Sub_Theme(const char* sub_string);
static void bsp_4GCat_Sub_Data_Analyze(uint8_t * rec_data);

static void bsp_4GCat_GPS_Handler(void);
static int8_t bsp_4GCat_GPS_Get_Data(uint8_t* data_Str);
static int8_t bsp_4GCat_GPS_Data_Analyze(const char *format, GNSSInfo_t *info);
/* Public variables==========================================================*/
bsp_GPS_st bsp_GPS = 
{
    .bsp_GPS_Relay_Flag = FALSE,
    .Latitude = 0.0,
    .Longitude = 0.0
};

bsp_4GCat_st bsp_4GCat = 
{
    .bsp_4GCat_Sub_Pass_Flag = FALSE,
    .Reset_Status = RESET_NOT,
    .bsp_4GCat_MQTT_Connect_Flag = FALSE,
    .bsp_4GCat_Ret_Status_buf = {RET_FAIL},
    .bsp_4GCat_Rec_Status = Rec_Status_None,
    .bsp_4GCat_Step_Status = TASK_CIOT_AT_TEST,

    .bsp_4GCat_Pub_Buf = {0},
    .bsp_4GCat_Pub_Len = 0,
    
    .bsp_4GCat_Running = &bsp_4GCat_Running,
    .bsp_4GCat_Data_Upload_1 = &bsp_4GCat_Data_Upload_1,
    .bsp_4GCat_GPS_Get_Data = &bsp_4GCat_GPS_Get_Data,
    .bsp_4GCat_GPS_Handler = &bsp_4GCat_GPS_Handler,
    .bsp_4GCat_Sub_Theme = &bsp_4GCat_Sub_Theme,
    .bsp_4GCat_Sub_Data_Analyze = &bsp_4GCat_Sub_Data_Analyze
};

/*=========================================== 应用层函数 ===========================================*/

/*
 * @function: bsp_4GCat_Running
 * @param: None
 * @retval: None
 * @brief: 模块运行
 */
static void bsp_4GCat_Running(void)
{
    // 未复位成功则退出
    if (bsp_4GCat.Reset_Status != RESET_OVER)
    {
        return;
    }
    switch (bsp_4GCat.bsp_4GCat_Step_Status)
    {
    case TASK_CIOT_AT_TEST:	// 【发送AT测试是否正常】
    {
        /*状态复位*/
        bsp_4GCat_BeforeSending_Parameter_Init(Rec_Status_CheckCmd_Plan1, (uint8_t *)"AT\r\n");    
        break;
    }
    case TASK_CIOT_AT_TEST_RSP:	// 【判断回传--- "OK"】
    {
        bsp_4GCat_Ret_Check(bsp_4GCat.bsp_4GCat_Ret_Status_buf[Rec_Status_CheckCmd_Plan1]);
        break;
    }
    case TASK_CIOT_CLOSE_ECHO:  // 【关闭命令回显】
    {
        /*状态复位*/
        bsp_4GCat_BeforeSending_Parameter_Init(Rec_Status_CheckCmd_Plan1, (uint8_t *)"ATE0\r\n");     
        break;
    }
    case TASK_CIOT_CLOSE_ECHO_RSP: // 【判断回传--- "OK"】
    {
        bsp_4GCat_Ret_Check(bsp_4GCat.bsp_4GCat_Ret_Status_buf[Rec_Status_CheckCmd_Plan1]);        
        break;
    }
    case TASK_CIOT_CGATT:   // 【查询网络附着状态】
    {
        /*状态复位*/
        bsp_4GCat_BeforeSending_Parameter_Init(Rec_Status_CheckCmd_Plan2, (uint8_t *)"AT+CGATT?\r\n");       
        break;
    }
    case TASK_CIOT_CGATT_RSP:   // 【判断回传--- "+CGATT: 1"】
    {
        bsp_4GCat_Ret_Check(bsp_4GCat.bsp_4GCat_Ret_Status_buf[Rec_Status_CheckCmd_Plan2]);       
        break;
    }
    case TASK_CIOT_CGREG:   // 【查询网络注册状态】
    {
        /*状态复位*/
        bsp_4GCat_BeforeSending_Parameter_Init(Rec_Status_CheckCmd_Plan3, (uint8_t *)"AT+CGREG?\r\n");             
        break;
    }
    case TASK_CIOT_CGREG_RSP:   // 【判断回传--- "+CGREG: 0,1"/"+CGREG: 0,5"】
    {
        bsp_4GCat_Ret_Check(bsp_4GCat.bsp_4GCat_Ret_Status_buf[Rec_Status_CheckCmd_Plan3]);        
        break;
    }
    case TASK_GPS_START:    // 启动GPS
    {
        /*状态复位*/
        bsp_4GCat_BeforeSending_Parameter_Init(Rec_Status_CheckCmd_Plan6, (uint8_t *)"AT+CGNSSPWR=1\r\n");        
        break;
    }
    case TASK_GPS_START_RSP:    // 【判断回传--- "+CGNSSPWR:READY!",这里需要等久点大概10s,而且不能直接单片机复位,需要断电再上电或者模块复位才能有RELAY】
    {
        if (RET_PASS == bsp_4GCat.bsp_4GCat_Ret_Status_buf[Rec_Status_CheckCmd_Plan6])
        {
            bsp_GPS.bsp_GPS_Relay_Flag = TRUE;
            bsp_4GCat.bsp_4GCat_Rec_Status = Rec_Status_None;
            bsp_4GCat.bsp_4GCat_Step_Status++;
        }
        else
        {
            ++bsp_4GCat_Timeout_Count;

            if (bsp_4GCat_Timeout_Count >= 50)
            {
#if USE_Normal_Print
                printf("GPS OPEN ERROR\r\n");
#endif          
                bsp_GPS.bsp_GPS_Relay_Flag = FALSE;      
                bsp_4GCat.bsp_4GCat_Step_Status++;  // 强制跳过
            }
        }
        break;
    }
    case TASK_CIOT_MQTT_START:  // 【启动MQTT】
    {
        /*状态复位*/
        bsp_4GCat_BeforeSending_Parameter_Init(Rec_Status_CheckCmd_Plan1, (uint8_t *)"AT+CMQTTSTART\r\n");
        break;
    }
    case TASK_CIOT_MQTT_START_RSP:   // 【判断回传--- "OK"】
    {
        if (RET_PASS == bsp_4GCat.bsp_4GCat_Ret_Status_buf[Rec_Status_CheckCmd_Plan1])
        {
            bsp_4GCat.bsp_4GCat_Rec_Status = Rec_Status_None;
            bsp_4GCat.bsp_4GCat_Step_Status++;
        }
        else
        {
            ++bsp_4GCat_Timeout_Count;

            if (bsp_4GCat_Timeout_Count >= 5)
            {
                bsp_4GCat_Timeout_Count = 0;
                // 直接复位(重发不生效)
                bsp_4GCat_Moduel_Reset();    // 复位
            }
        }        
        break;
    }
    case TASK_CIOT_MQTT_OPEN:   // 【打开MQTT连接】
    {
        /*状态复位*/
        Public.Memory_Clear((uint8_t*)bsp_4GCat_AT_String, strlen((char*)bsp_4GCat_AT_String)); // 清0
        sprintf(bsp_4GCat_AT_String,"AT+CMQTTACCQ=0,\"%s\",0\r\n",CLIENT_ID);
        bsp_4GCat_BeforeSending_Parameter_Init(Rec_Status_CheckCmd_Plan1, (uint8_t *)bsp_4GCat_AT_String);
        break;
    }
    case TASK_CIOT_MQTT_OPEN_RSP:   // 【判断回传--- "OK"】
    {
        bsp_4GCat_Ret_Check(bsp_4GCat.bsp_4GCat_Ret_Status_buf[Rec_Status_CheckCmd_Plan1]);       
        break;
    }
    case TASK_CIOT_MQTT_CONNECT:    // 【连接MQTT服务器】
    {
        /*状态复位*/
        Public.Memory_Clear((uint8_t*)bsp_4GCat_AT_String, strlen((char*)bsp_4GCat_AT_String)); // 清0
        sprintf(bsp_4GCat_AT_String,"AT+CMQTTCONNECT=0,\"%s\",60,1,\"%s\",\"%s\"\r\n",DOMAIN,USERNAME,PASSWORD);
        bsp_4GCat_BeforeSending_Parameter_Init(Rec_Status_CheckCmd_Plan4, (uint8_t *)bsp_4GCat_AT_String);        
        break;
    }
    case TASK_CIOT_MQTT_CONNECT_RSP:    // 【判断回传--- "+CMQTTCONNECT: 0,0"】
    {
        if (RET_PASS == bsp_4GCat.bsp_4GCat_Ret_Status_buf[Rec_Status_CheckCmd_Plan4])
        {
            bsp_4GCat.bsp_4GCat_Rec_Status = Rec_Status_None;
            bsp_4GCat.bsp_4GCat_Step_Status++;
        }
        else
        {
            ++bsp_4GCat_Timeout_Count;

            if (bsp_4GCat_Timeout_Count >= 5)
            {
                bsp_4GCat_Timeout_Count = 0;
                // +(重发不生效)
                bsp_4GCat_Moduel_Reset();    // 复位
            }
        }         
    }
    case TASK_CONNECT_SUCCESS:  // 连接MQTT服务器成功
    {
        bsp_4GCat.bsp_4GCat_MQTT_Connect_Flag = TRUE;
        bsp_4GCat.bsp_4GCat_Step_Status = TASK_IDLE;
#if USE_Normal_Print
        printf("MQTT CONNECT SUCCESS\r\n");
#endif        
        break;
    }
    case TASK_IDLE: // 空闲状态
    {
        break;
    }
    case TASK_CIOT_MQTT_DISCONN:   // 【断开MQTT连接】
    {
        bsp_4GCat.bsp_4GCat_MQTT_Connect_Flag = FALSE;
        bsp_4GCat.bsp_4GCat_Sub_Pass_Flag = FALSE;
        bsp_4GCat_BeforeSending_Parameter_Init(Rec_Status_CheckCmd_Plan1, (uint8_t *)"AT+CMQTTDISC=0,120\r\n");
        break;
    }
    case TASK_CIOT_MQTT_DISCONN_RSP:    // 【判断回传---"OK"】
    {
        bsp_4GCat_Ret_Check(bsp_4GCat.bsp_4GCat_Ret_Status_buf[Rec_Status_CheckCmd_Plan1]);
        break;
    }
    case TASK_CIOT_MQTT_CLOSE:  // 【释放MQTT相关的资源】
    {
        bsp_4GCat_BeforeSending_Parameter_Init(Rec_Status_CheckCmd_Plan1, (uint8_t *)"AT+CMQTTREL=0\r\n");
        break;
    }
    case TASK_CIOT_MQTT_CLOSE_RSP:  // 【判断回传---"OK"】
    {
        bsp_4GCat_Ret_Check(bsp_4GCat.bsp_4GCat_Ret_Status_buf[Rec_Status_CheckCmd_Plan1]);
        break;
    }
    case TASK_CIOT_MQTT_STOP:   // 【关闭MQTT功能】
    {
        bsp_4GCat_BeforeSending_Parameter_Init(Rec_Status_CheckCmd_Plan1, (uint8_t *)"AT+CMQTTSTOP\r\n");
        break;
    }
    case TASK_CIOT_MQTT_STOP_RSP:   // 【判断回传---"OK"】
    {
        bsp_4GCat_Ret_Check(bsp_4GCat.bsp_4GCat_Ret_Status_buf[Rec_Status_CheckCmd_Plan1]);
        break;
    }
    default:break;
    }
}

/*
* @function: bsp_4GCat_Data_Upload_1
* @param: None
* @retval: None
* @brief: 数据上传1
*/
static void bsp_4GCat_Data_Upload_1(void)
{
    static float a = 0,b = 0,c = 0,d = 0;
    Public.Memory_Clear((uint8_t*)bsp_4GCat.bsp_4GCat_Pub_Buf, strlen((char*)bsp_4GCat.bsp_4GCat_Pub_Buf));
    sprintf((char *)bsp_4GCat.bsp_4GCat_Pub_Buf, "{\"method\":\"report\",\"params\":{\"temp\":%.1f,\"humi\":%.1f,\"longitude\":%f,\"latitude\":%f}}", ++a, ++b, ++c, ++d);
    bsp_4GCat_Upload_Data_To_Pub(TOPIC_PROPERTY_PUB, (char*)bsp_4GCat.bsp_4GCat_Pub_Buf);
}

/*=========================================== 中间层函数 ===========================================*/

/*
* @function: bsp_4GCat_Ret_Check
* @param: None
* @retval: None
* @brief: 回传结果检测
*/
static void bsp_4GCat_Ret_Check(int8_t ret_status)
{
    if (RET_PASS == ret_status)
    {
        bsp_4GCat.bsp_4GCat_Rec_Status = Rec_Status_None;
        bsp_4GCat.bsp_4GCat_Step_Status++;
    }
    else
    {
        ++bsp_4GCat_Timeout_Count;

        if (bsp_4GCat_Timeout_Count >= 8)
        {
            bsp_4GCat.bsp_4GCat_Step_Status--;
        }
    }
}

/*
* @function: bsp_4GCat_BeforeSending_Parameter_Init
* @param: None
* @retval: None
* @brief: 参数初始化且发送
*/
static void bsp_4GCat_BeforeSending_Parameter_Init(bsp_4GCat_Rec_Status_et rec_status, uint8_t* str)
{
        /*状态复位*/
        bsp_4GCat.bsp_4GCat_Rec_Status = rec_status;
        bsp_4GCat.bsp_4GCat_Ret_Status_buf[rec_status] = RET_FAIL;
        bsp_4GCat_Timeout_Count = 0;

        bsp_4GCat_uart.Bsp_4GCat_Uart_Send_String((uint8_t *)str);
        bsp_4GCat.bsp_4GCat_Step_Status++;
}

/*
* @function: bsp_4GCat_Moduel_Reset
* @param: None
* @retval: None
* @brief: 模块复位
*/
static void bsp_4GCat_Moduel_Reset(void)
{
    bsp_4GCat.bsp_4GCat_MQTT_Connect_Flag = FALSE; // 标志位置0
    bsp_4GCat.Reset_Status = RESET_NOT; // 未复位
    bsp_4GCat.bsp_4GCat_Sub_Pass_Flag = FALSE;  // 订阅主题完成标志位置0
    bsp_4GCat_uart.Bsp_4GCat_Uart_Send_String((uint8_t *)"AT+CRESET\r\n");
#if BSP_4GCAT_AUTO_CONNNECT_MODE
    bsp_4GCat.bsp_4GCat_Step_Status = TASK_CIOT_AT_TEST;    // AT模式
#endif
#if USE_Normal_Print
        printf("RESET...\r\n");
#endif
}

/*
* @function: bsp_4GCat_Upload_Data_To_Pub
* @param: pub_string -> 发送到的目的地主题 pub_data_string -> 要上传到主题的数据内容
* @retval: None
* @brief: 上传数据到MQTT主题
*/
static void bsp_4GCat_Upload_Data_To_Pub(const char* pub_string, const char* pub_data_string)
{
    uint8_t step = 0;    // 运行到哪步计数
    uint32_t timeout = 0; // 重发超时时间计数
    static uint16_t error_count = 0; // 错误计数(复位)

    if (bsp_4GCat.bsp_4GCat_MQTT_Connect_Flag)
    {
        switch (step)
        {
        case 0: // 设置待发布的主题的字符串长度(回车后会出现一个尖括号)
        {
            timeout = 0;
            bsp_4GCat.bsp_4GCat_Rec_Status = Rec_Status_CheckCmd_Plan5;
            bsp_4GCat.bsp_4GCat_Ret_Status_buf[Rec_Status_CheckCmd_Plan5] = RET_FAIL;
            Public.Memory_Clear((uint8_t*)bsp_4GCat_AT_String, strlen((char*)bsp_4GCat_AT_String)); // 清0

            // 设置待发布的主题的字符串长度
            sprintf(bsp_4GCat_AT_String, "AT+CMQTTTOPIC=0,%d\r\n", strlen(pub_string));
            bsp_4GCat_uart.Bsp_4GCat_Uart_Send_String((uint8_t *)bsp_4GCat_AT_String);

            step++;
        }
        case 1: // 判断">" 后输入主题(不能有回车),会返回 "OK"
        {
            // 超时等待
            while (1)
            {
                if (RET_PASS == bsp_4GCat.bsp_4GCat_Ret_Status_buf[Rec_Status_CheckCmd_Plan5])
                {
                    // 发送目的地主题
#if 0                    
                    printf("-----%d------\r\n",timeout);
#endif                    
                    error_count = 0;
                    timeout = 0;

                    bsp_4GCat.bsp_4GCat_Rec_Status = Rec_Status_CheckCmd_Plan1;
                    bsp_4GCat.bsp_4GCat_Ret_Status_buf[Rec_Status_CheckCmd_Plan1] = RET_FAIL; 

                    bsp_4GCat_uart.Bsp_4GCat_Uart_Send_String((const uint8_t *)pub_string);
                    
                    step++;
                    break;  // 退出循环
                }

                timeout++;
                if (timeout >= BSP_4GCAT_WAIT_COUNT)
                {
                    error_count++;
                    goto error_handling;
                }                
            }
        }
        case 2: // 发送完成判断回传 "OK" 再继续操作
        {
            while (1)
            {
                if (RET_PASS == bsp_4GCat.bsp_4GCat_Ret_Status_buf[Rec_Status_CheckCmd_Plan1])
                {
                    timeout = 0;
                    bsp_4GCat.bsp_4GCat_Rec_Status = Rec_Status_None;

                    step++;
                    break;
                }

                timeout++;
                if (timeout >= BSP_4GCAT_WAIT_COUNT)
                {
                    error_count++;
                    goto error_handling;
                }                
            }
        }
        case 3: // 设置要发送的数据长度(会出现一个尖括号)
        {
            timeout = 0;
            bsp_4GCat.bsp_4GCat_Rec_Status = Rec_Status_CheckCmd_Plan5;
            bsp_4GCat.bsp_4GCat_Ret_Status_buf[Rec_Status_CheckCmd_Plan5] = RET_FAIL;
            Public.Memory_Clear((uint8_t*)bsp_4GCat_AT_String, strlen((char*)bsp_4GCat_AT_String)); // 清0

            // 设置要发送的数据长度
            sprintf(bsp_4GCat_AT_String, "AT+CMQTTPAYLOAD=0,%d\r\n", strlen(pub_data_string));
            bsp_4GCat_uart.Bsp_4GCat_Uart_Send_String((uint8_t *)bsp_4GCat_AT_String);

            step++;
        }
        case 4: // 判断 ">" 后将要发送的消息(不需要回车),会返回 "OK"
        {
            // 超时等待
            while (1)
            {
                if (RET_PASS == bsp_4GCat.bsp_4GCat_Ret_Status_buf[Rec_Status_CheckCmd_Plan5])
                {
                    // 发送目的地主题
                    error_count = 0;
                    timeout = 0;

                    bsp_4GCat.bsp_4GCat_Rec_Status = Rec_Status_CheckCmd_Plan1;
                    bsp_4GCat.bsp_4GCat_Ret_Status_buf[Rec_Status_CheckCmd_Plan1] = RET_FAIL;

                    bsp_4GCat_uart.Bsp_4GCat_Uart_Send_String((const uint8_t *)pub_data_string);

                    step++;
                    break;  // 退出循环
                }

                timeout++;
                if (timeout >= BSP_4GCAT_WAIT_COUNT)
                {
                    error_count++;
                    goto error_handling;
                }                 
            }            
        }
        case 5: // 发送完成判断回传 "OK" 再继续操作
        {
            while (1)
            {
                if (RET_PASS == bsp_4GCat.bsp_4GCat_Ret_Status_buf[Rec_Status_CheckCmd_Plan1])
                {
                    timeout = 0;
                    bsp_4GCat.bsp_4GCat_Rec_Status = Rec_Status_None;

                    step++;
                    break;
                }
                
                timeout++;
                if (timeout >= BSP_4GCAT_WAIT_COUNT)
                {
                    error_count++;
                    goto error_handling;
                }                
            }            
        }
        case 6: // 发送数据到指定主题,会返回 "OK"
        {
            // 向指定的主题发布消息
            bsp_4GCat_uart.Bsp_4GCat_Uart_Send_String((uint8_t *)"AT+CMQTTPUB=0,1,60\r\n");            
            break;
        }
        default:
            break;
        }
    }
    // 错误处理
    error_handling:
    {
        if (error_count >= 10) // 长时间发送失败
        {
            error_count = 0;
            bsp_4GCat_Moduel_Reset();   // 复位
        }
        return;
    }    
}

/*
* @function: bsp_4GCat_Sub_Theme
* @param: sub_string -> 待订阅的主题
* @retval: None
* @brief: 订阅主题
*/
static void bsp_4GCat_Sub_Theme(const char* sub_string)
{
    uint8_t step = 0;    // 运行到哪步计数
    uint32_t timeout = 0; // 重发超时时间计数
    static uint16_t error_count = 0; // 错误计数(复位)

    if (bsp_4GCat.bsp_4GCat_MQTT_Connect_Flag)
    {
        if (FALSE == bsp_4GCat.bsp_4GCat_Sub_Pass_Flag) // 未订阅主题则进行订阅
        {
            switch (step)
            {
                case 0: // 配置订阅的主题(回车后会返回一个 ">")
                {
                    /*状态复位*/
                    timeout = 0;
                    bsp_4GCat.bsp_4GCat_Rec_Status = Rec_Status_CheckCmd_Plan5;
                    bsp_4GCat.bsp_4GCat_Ret_Status_buf[Rec_Status_CheckCmd_Plan5] = RET_FAIL;    
                    Public.Memory_Clear((uint8_t*)bsp_4GCat_AT_String, strlen((char*)bsp_4GCat_AT_String)); // 清0

                    sprintf(bsp_4GCat_AT_String, "AT+CMQTTSUBTOPIC=0,%d,1\r\n", strlen(sub_string));           
                    bsp_4GCat_uart.Bsp_4GCat_Uart_Send_String((uint8_t *)bsp_4GCat_AT_String);
                    step++;
                }
                case 1: // 等待 ">" 然后发送需要订阅的主题
                {
                    // 超时等待
                    while (1)
                    {
                        if (RET_PASS == bsp_4GCat.bsp_4GCat_Ret_Status_buf[Rec_Status_CheckCmd_Plan5])
                        {       
                            error_count = 0;
                            timeout = 0;

                            bsp_4GCat.bsp_4GCat_Rec_Status = Rec_Status_CheckCmd_Plan1;
                            bsp_4GCat.bsp_4GCat_Ret_Status_buf[Rec_Status_CheckCmd_Plan1] = RET_FAIL; 

                            bsp_4GCat_uart.Bsp_4GCat_Uart_Send_String((const uint8_t *)sub_string);
                            
                            step++;
                            break;  // 退出循环
                        }

                        timeout++;
                        if (timeout >= BSP_4GCAT_WAIT_COUNT)
                        {
                            timeout = 0;
                            error_count++;
                            goto error_handling;
                        }                
                    }                    
                }
                case 2: // 返回 "OK"
                {
                    while (1)
                    {
                        if (RET_PASS == bsp_4GCat.bsp_4GCat_Ret_Status_buf[Rec_Status_CheckCmd_Plan1])
                        {
                            timeout = 0;
                            bsp_4GCat.bsp_4GCat_Rec_Status = Rec_Status_None;

                            step++;
                            break;
                        }
                        
                        timeout++;
                        if (timeout >= BSP_4GCAT_WAIT_COUNT)
                        {
                            timeout = 0;
                            error_count++;
                            goto error_handling;
                        }                
                    }  
                }
                case 3: // 订阅主题【返回"+CMQTTSUB: 0,0"】
                {
                    bsp_4GCat.bsp_4GCat_Rec_Status = Rec_Status_CheckCmd_Plan8;
                    bsp_4GCat.bsp_4GCat_Ret_Status_buf[Rec_Status_CheckCmd_Plan8] = RET_FAIL;                    
                    bsp_4GCat_uart.Bsp_4GCat_Uart_Send_String((const uint8_t *)"AT+CMQTTSUB=0\r\n");
                    step++;
                }
                case 4: // 判断回传 "+CMQTTSUB: 0,0"
                {
                    while (1)
                    {
                        if (RET_PASS == bsp_4GCat.bsp_4GCat_Ret_Status_buf[Rec_Status_CheckCmd_Plan8])
                        {
                            timeout = 0;
                            error_count = 0;
                            bsp_4GCat.bsp_4GCat_Sub_Pass_Flag = TRUE;
                            bsp_4GCat.bsp_4GCat_Rec_Status = Rec_Status_None;
                            break;
                        }

                        timeout++;
                        if (timeout >= BSP_4GCAT_WAIT_COUNT)
                        {
                            timeout = 0;
                            error_count++;
                            break;
                        }                 
                    }                    
                    break;
                }
                default:break;
            }           
        }
    }  
    error_handling:
    {
        if (error_count >= 10) // 长时间发送失败
        {
            error_count = 0;
            bsp_4GCat_Moduel_Reset();   // 复位
        }
        return;    
    }       
}

/*
* @function: bsp_4GCat_Sub_Data_Analyze
* @param: rec_data -> 接收的数据
* @retval: None
* @brief: 订阅主题的数据解析
*/
static void bsp_4GCat_Sub_Data_Analyze(uint8_t * rec_data)
{
    if (FALSE == bsp_4GCat.bsp_4GCat_Sub_Pass_Flag)
    {
        return;
    }
    uint16_t len = 0;
    len = sizeof((char *)rec_data); // 长度
    uint8_t temp[len];

    Public.Memory_Copy((char *)temp, (char *)rec_data, len); // 复制

    // 数据判断
    if (strstr((char *)rec_data, "\"switch_1\":1") != NULL)
    {
        HAL_GPIO_WritePin(GPIOA,GPIO_PIN_0,GPIO_PIN_RESET);
    }
    if (strstr((char *)rec_data, "\"switch_1\":0") != NULL)
    {   
        HAL_GPIO_WritePin(GPIOA,GPIO_PIN_0,GPIO_PIN_SET);
    }    
}

/*=========================================== GPS相关函数 ===========================================*/
/*
* @function: bsp_4GCat_GPS_Handler
* @param: None
* @retval: None
* @brief: GPS处理函数
*/
static void bsp_4GCat_GPS_Handler(void)
{
    uint8_t step = 0;
    uint32_t timeout = 0; // 重发超时时间计数
    static uint16_t error_count = 0; // 错误计数

    if (FALSE == bsp_GPS.bsp_GPS_Relay_Flag)    // GPS未准备则退出
    {
        return;
    }
    switch (step)
    {
        case 0: // 手动获取经纬度,会返回 经纬度数据和"OK"
        {
            timeout = 0;
            bsp_4GCat.bsp_4GCat_Rec_Status = Rec_Status_CheckCmd_Plan7;
            bsp_4GCat.bsp_4GCat_Ret_Status_buf[Rec_Status_CheckCmd_Plan7] = RET_FAIL;
            bsp_4GCat_uart.Bsp_4GCat_Uart_Send_String((const uint8_t *)"AT+CGPSINFO\r\n");            
            step++;
        }   
        case 1: // 提取经纬度数据
        {
            while (1)
            {
                if (RET_PASS == bsp_4GCat.bsp_4GCat_Ret_Status_buf[Rec_Status_CheckCmd_Plan7])
                {
                    timeout = 0;
                    error_count = 0;
                    bsp_4GCat.bsp_4GCat_Rec_Status = Rec_Status_None;
                    break;
                }

                timeout++;
                if (timeout >= BSP_4GCAT_WAIT_COUNT)
                {
                    error_count++;
                    bsp_GPS.Latitude = 0;
                    bsp_GPS.Longitude = 0;
                    break;
                }                 
            }
            break;
        }
        default:break;
    }
    // 错误处理---直接GPS准备完成标志位置0
    if (error_count >= 10)
    {
        error_count = 0;
        bsp_GPS.bsp_GPS_Relay_Flag = FALSE;
    }
}

/*
* @function: bsp_4GCat_GPS_Get_Data
* @param: data_Str -> 接收数组
* @retval: 成功--RET_PASS 失败--RET_FAIL
* @brief: 获取GPS数据
*/
static int8_t bsp_4GCat_GPS_Get_Data(uint8_t* data_Str)
{
    uint16_t len = 0;
    GNSSInfo_t info;

    len = strlen((char *)data_Str); // 长度
    uint8_t temp[len];

    Public.Memory_Copy((char *)temp, (char *)data_Str, len); // 复制

    if (bsp_4GCat_GPS_Data_Analyze((char *)temp, &info) != RET_PASS)
    {
        return RET_FAIL;
    }

    float lat, lon;
    float minutes;

    lat = ((uint16_t)info.latitude) / 100;
    minutes = info.latitude - lat * 100;
    lat += minutes / 60;

    lon = ((uint16_t)info.longitude) / 100;
    minutes = info.longitude - lon * 100;
    lon += minutes / 60;

    if (info.NS == 'S')
    {
        lat = -lat;
    }
    if (info.EW == 'W')
    {
        lon = -lon;
    }
    // 获取经纬度
    bsp_GPS.Latitude = lat;
    bsp_GPS.Longitude = lon;

    return RET_PASS;
}

/*
* @function: bsp_4GCat_GPS_Data_Analyze
* @param: None
* @retval: None
* @brief: 数据解析
*/
static int8_t bsp_4GCat_GPS_Data_Analyze(const char *format, GNSSInfo_t *info)
{
    char *start = strstr(format, "+CGPSINFO: ");

    if (NULL == start)
    {
        return RET_FAIL;
    }

    if (strlen(start) < 35)
    {
        return RET_FAIL;
    }

    start += 11; // Skip the header.

    uint8_t flag = 0;
    uint8_t finish = 0;

    char value[24];
    Public.Memory_Clear((uint8_t*)value, sizeof(value));

    for (uint8_t counter = 0; *start != 0; start++)
    {
        if (*start == ',')
        {
        }
        else if (*start == '\r')
        {
            finish = 1;
        }
        else
        {
            value[counter++] = *start;
            continue;
        }

        switch (flag++)
        {
            case 0:
            {
                info->latitude = (float)atof(value);
                break;
            }
            case 1:
            {
                info->NS = value[0];
                break;
            }
            case 2:
            {
                info->longitude = (float)atof(value);
                break;
            }
            case 3:
            {
                info->EW = value[0];
                break;
            }
            case 4:
            {
                info->UTC.year = (uint16_t)atoi(&value[4]);
                value[4] = 0;
                info->UTC.month = (uint8_t)atoi(&value[2]);
                value[2] = 0;
                info->UTC.day = (uint8_t)atoi(&value[0]);
                break;
            }
            case 5:
            {
                info->UTC.millisecond = (uint16_t)atoi(&value[7]);
                value[6] = 0;
                info->UTC.second = (uint8_t)atoi(&value[4]);
                value[4] = 0;
                info->UTC.minute = (uint8_t)atoi(&value[2]);
                value[2] = 0;
                info->UTC.hour = (uint8_t)atoi(&value[0]);
                break;
            }
            case 6:
            {
                info->altitude = (float)atof(value);
                break;
            }
            case 7:
            {
                info->speedOverGround = (float)atof(value);
                break;
            }
            default:
            {
                finish = 1;
                break;
            }
        }

        if (finish)
        {
            break;
        }
        counter = 0;
        Public.Memory_Clear((uint8_t*)value, sizeof(value));
    }

    return RET_PASS;
}
bsp_4GCat_uart.h
#ifndef __BSP_4GCAT_UART_H
#define __BSP_4GCAT_UART_H
#include "AllHead.h"

// 模块使用的串口
#define BSP_4GCat_USE_Serial huart2
// 模块使用定时器
#define BSP_4GCat_USE_Timer htim7
// 串口接收最大长度
#define BSP_4GCat_Rec_MAX_LEN 168

typedef struct
{
	uint8_t ucUart_Rec_Over_Flag;	// 串口接收完成标志位
	uint16_t usUart_Rec_Len;	// 串口接收数据长度
    uint8_t *pucRec_Buffer; // 接收缓存指针
    void (*Bsp_4GCat_Uart_Init)(void); // 串口初始化
    void (*Bsp_4GCat_Uart_Send_String)(const uint8_t *); // 串口发送字符串
    void (*Bsp_4GCat_Uart_Data_Parse)(void);   // 串口数据解析
    void (*Bsp_4GCat_Uart_Handler)(void);  // 串口处理函数
} bsp_4GCat_uart_st;

extern bsp_4GCat_uart_st bsp_4GCat_uart;

#endif
bsp_4GCat_uart.c
/***************************************************************************
 * File: bsp_4GCat_uart.c
 * Author: Yang
 * Date: 2023/12/06
 * description: 
 -----------------------------------
串口接线:
        PA2(TX) --- 模块RX
        PA3(RX) --- 模块TX
        GND --- GND
        5V --- 5V
 -----------------------------------
****************************************************************************/
#include "AllHead.h"

/* Private variables=========================================================*/
static uint8_t uc4GCat_Uart_Rec_Temp_Arr[BSP_4GCat_Rec_MAX_LEN];        // 串口接收临时缓存数组
static uint8_t uc4GCat_Uart_Rec_Arr[BSP_4GCat_Rec_MAX_LEN];   // 模块串口接收数组

/* Private function prototypes===============================================*/
static void Bsp_4GCat_Uart_Init(void);
static void Bsp_4GCat_Uart_Send_String(const uint8_t *pStr);
static void Bsp_4GCat_Uart_Data_Parse(void);
static void Bsp_4GCat_Uart_Handler(void);
/* Public variables==========================================================*/
bsp_4GCat_uart_st bsp_4GCat_uart =
{
				.ucUart_Rec_Over_Flag = FALSE,
				.usUart_Rec_Len = 0,
				.pucRec_Buffer = uc4GCat_Uart_Rec_Arr,
				.Bsp_4GCat_Uart_Init = &Bsp_4GCat_Uart_Init,
				.Bsp_4GCat_Uart_Send_String = &Bsp_4GCat_Uart_Send_String,
				.Bsp_4GCat_Uart_Data_Parse = &Bsp_4GCat_Uart_Data_Parse,
				.Bsp_4GCat_Uart_Handler = &Bsp_4GCat_Uart_Handler
};

/*
 * @function: Bsp_4GCat_Uart_Init
 * @param: None
 * @retval: None
 * @brief: 串口初始化
 */
static void Bsp_4GCat_Uart_Init(void)
{
    __HAL_UART_ENABLE_IT(&BSP_4GCat_USE_Serial, UART_IT_IDLE); // 使能串口空闲中断
    HAL_UART_Receive_DMA(&BSP_4GCat_USE_Serial, uc4GCat_Uart_Rec_Temp_Arr, (uint16_t)BSP_4GCat_Rec_MAX_LEN);                                                       // 接收清0
}

/*
 * @function: Bsp_4GCat_Uart_Send_String
 * @param: None
 * @retval: None
 * @brief: 串口发送字符串
 */
static void Bsp_4GCat_Uart_Send_String(const uint8_t *pStr)
{
        HAL_UART_Transmit(&BSP_4GCat_USE_Serial, pStr, strlen((const char *)pStr), 5000);
#if USE_Debug_Print				
		HAL_UART_Transmit(&huart1, pStr, strlen((const char *)pStr), 5000);
#endif				
}

/*
 * @function: Bsp_4GCat_Uart_Data_Parse
 * @param: None
 * @retval: None
 * @brief: 串口接收数据解析
 */
static void Bsp_4GCat_Uart_Data_Parse(void)
{
	HAL_UART_DMAStop(&BSP_4GCat_USE_Serial);   // 串口停止DMA接收
	bsp_4GCat_uart.usUart_Rec_Len = BSP_4GCat_Rec_MAX_LEN - __HAL_DMA_GET_COUNTER(&hdma_usart2_rx);
	Public.Memory_Copy((char*)bsp_4GCat_uart.pucRec_Buffer, (char*)uc4GCat_Uart_Rec_Temp_Arr, bsp_4GCat_uart.usUart_Rec_Len);
	bsp_4GCat_uart.ucUart_Rec_Over_Flag = TRUE;
}

/*
 * @function: Bsp_4GCat_Uart_Handler
 * @param: None
 * @retval: None
 * @brief: 串口接收处理函数
 */
static void Bsp_4GCat_Uart_Handler(void)
{
	if (bsp_4GCat_uart.ucUart_Rec_Over_Flag)
	{
		switch (bsp_4GCat.bsp_4GCat_Rec_Status)
		{
			case Rec_Status_None:
			{
				break;
			}
			case Rec_Status_CheckCmd_Plan1:
			{
				if (strstr((char*)bsp_4GCat_uart.pucRec_Buffer, (char*)"OK") != NULL)
				{
					bsp_4GCat.bsp_4GCat_Ret_Status_buf[Rec_Status_CheckCmd_Plan1] = RET_PASS;
				}
				else
				{
					bsp_4GCat.bsp_4GCat_Ret_Status_buf[Rec_Status_CheckCmd_Plan1] = RET_FAIL;
				}
				break;
			}
			case Rec_Status_CheckCmd_Plan2:
			{
				if (strstr((char*)bsp_4GCat_uart.pucRec_Buffer, (char*)"+CGATT: 1") != NULL)
				{
					bsp_4GCat.bsp_4GCat_Ret_Status_buf[Rec_Status_CheckCmd_Plan2] = RET_PASS;
				}
				else
				{
					bsp_4GCat.bsp_4GCat_Ret_Status_buf[Rec_Status_CheckCmd_Plan2] = RET_FAIL;
				}				
				break;
			}
			case Rec_Status_CheckCmd_Plan3:
			{
				if (strstr((char*)bsp_4GCat_uart.pucRec_Buffer, (char*)"+CGREG: 0,1") != NULL)
				{
					bsp_4GCat.bsp_4GCat_Ret_Status_buf[Rec_Status_CheckCmd_Plan3] = RET_PASS;
				}
				else if (strstr((char*)bsp_4GCat_uart.pucRec_Buffer, (char*)"+CGREG: 0,5") != NULL)
				{
					bsp_4GCat.bsp_4GCat_Ret_Status_buf[Rec_Status_CheckCmd_Plan3] = RET_PASS;
				}
				else
				{
					bsp_4GCat.bsp_4GCat_Ret_Status_buf[Rec_Status_CheckCmd_Plan3] = RET_FAIL;
				}				
				break;
			}
			case Rec_Status_CheckCmd_Plan4:
			{
				if (strstr((char*)bsp_4GCat_uart.pucRec_Buffer, (char*)"+CMQTTCONNECT: 0,0") != NULL)
				{
					bsp_4GCat.bsp_4GCat_Ret_Status_buf[Rec_Status_CheckCmd_Plan4] = RET_PASS;
				}
				else
				{
					bsp_4GCat.bsp_4GCat_Ret_Status_buf[Rec_Status_CheckCmd_Plan4] = RET_FAIL;
				}				
				break;
			}
			case Rec_Status_CheckCmd_Plan5:
			{
				if (strstr((char*)bsp_4GCat_uart.pucRec_Buffer, (char*)">") != NULL)
				{
					bsp_4GCat.bsp_4GCat_Ret_Status_buf[Rec_Status_CheckCmd_Plan5] = RET_PASS;
				}
				else
				{
					bsp_4GCat.bsp_4GCat_Ret_Status_buf[Rec_Status_CheckCmd_Plan5] = RET_FAIL;
				}				
				break;
			}
			case Rec_Status_CheckCmd_Plan6:
			{
				if (strstr((char*)bsp_4GCat_uart.pucRec_Buffer, (char*)"READY") != NULL)
				{
					bsp_4GCat.bsp_4GCat_Ret_Status_buf[Rec_Status_CheckCmd_Plan6] = RET_PASS;
				}
				else
				{
					bsp_4GCat.bsp_4GCat_Ret_Status_buf[Rec_Status_CheckCmd_Plan6] = RET_FAIL;
				}				
				break;
			}
			case Rec_Status_CheckCmd_Plan7:
			{
				bsp_4GCat.bsp_4GCat_Ret_Status_buf[Rec_Status_CheckCmd_Plan7] = bsp_4GCat.bsp_4GCat_GPS_Get_Data(bsp_4GCat_uart.pucRec_Buffer);
				break;
			}
			case Rec_Status_CheckCmd_Plan8:
			{
				if (strstr((char*)bsp_4GCat_uart.pucRec_Buffer, (char*)"+CMQTTSUB: 0,0") != NULL)
				{
					bsp_4GCat.bsp_4GCat_Ret_Status_buf[Rec_Status_CheckCmd_Plan8] = RET_PASS;
				}
				else
				{
					bsp_4GCat.bsp_4GCat_Ret_Status_buf[Rec_Status_CheckCmd_Plan8] = RET_FAIL;
				}				
				break;
			}
			default:break;
		}
		// 订阅主题发送过来的数据解析
		bsp_4GCat.bsp_4GCat_Sub_Data_Analyze(bsp_4GCat_uart.pucRec_Buffer);
		bsp_4GCat_uart.ucUart_Rec_Over_Flag = FALSE;

#if USE_Debug_Print		
		printf("%s\r\n",bsp_4GCat_uart.pucRec_Buffer);
#endif

		Public.Memory_Clear(bsp_4GCat_uart.pucRec_Buffer,bsp_4GCat_uart.usUart_Rec_Len);
		bsp_4GCat_uart.usUart_Rec_Len = 0;
		HAL_UART_Receive_DMA(&BSP_4GCat_USE_Serial, uc4GCat_Uart_Rec_Temp_Arr, (uint16_t)BSP_4GCat_Rec_MAX_LEN);
	}
}
  • 实验现象

正常

使用UDP与私有服务器通信

PuTTY登陆的话还是跟4G一样,使用那个IP,用户名,密码

使用移远串口调试助手

  • 打开串口助手

  • 登陆远程服务器

登陆后输入指令进入UDP服务

./udpserver
  • 串口助手输入指令
AT	// 测试
# OK
AT+QSCLK=0	// 禁止模块休眠
# OK
AT+QIOPEN=0,0,"UDP","1.15.27.206",12301	// 创建UDP链接
# OK
# +QIOPEN: 0,0
AT+QISEND=0,13	// 配置待发送数据的长度,会出现一个>
# >
{"value":123}	// 输入数据(没有回车换行)
# OK
# SEND OK
AT+QICLOSE=0	// 关闭连接
# OK
# CLOSE OK

使用TCP与私有服务器通信

  • 登陆远程服务器

登陆后输入指令进入TCP服务

./tcpserver
  • 串口助手输入指令
AT	// 测试
# OK
AT+QSCLK=0	// 禁止模块休眠
# OK
AT+QIOPEN=0,0,"TCP","1.15.27.206",12300	// 创建TCP链接
# OK
# +QIOPEN: 0,0
AT+QISEND=0,13	// 配置待发送数据的长度,会出现一个>
# >
{"value":123}	// 输入数据(没有回车换行)
# OK
# SEND OK
AT+QICLOSE=0	// 关闭连接
# OK
# CLOSE OK

使用MQTT与私有云服务器通信

  • 启动MQTT服务,puTTY输入:
./killall
./mosquitto -v
  • 使用MQTT.fx连接云服务器

然后点击连接即可,然后订阅主题

  • 使用模块连接云服务器进行发布与订阅测试
AT	// 测试
# OK
AT+QSCLK=0	// 禁止模块休眠
# OK
AT+QMTOPEN=0,"1.15.27.206",1883	// 打开MQTT连接
# OK
# +QMTOPEN: 0,0
AT+QMTCONN=0,"iotdevice"	// 连接服务器
# OK
# +QMTCONN: 0,0,0
AT+QMTSUB=0,1,"topic/report",2	// 订阅了“topic/report”这个主题
# OK
# +QMTSUB: 0,1,0,2
# +QMTRECV: 0,1,"topic/report"," this  that "
// 测试
AT+QMTSUB=0,1,"topic/write",2	// 订阅
# OK
# +QMTSUB: 0,1,0,2
AT+QMTUNS=0,2,"topic/write"	// 退订
# OK
# +QMTUNS: 0,2,0

MQTT.fx往订阅的主题发布消息,则串口助手会收到

串口助手发送消息到MQTT.fx:

AT+QMTPUB=0,0,0,0,"topic/pub",13	// 向 "topic/pub" 主题发送消息数据,数据长度为13
# >
{"value":123}	// 不能有回车
# OK
# +QMTPUB: 0,0,0

上传数据到腾讯云

  • 腾讯云部分跟4GCat上传数据一样步骤即可

  • MX配置

跟4G配置一样

任务调度所需定时器,1ms

  • 程序编写

也是跟4G模块程序大致一样,部分不一样如下

CallBack.c
/***************************************************************************
 * File: CallBack.c
 * Author: Luckys.
 * Date: 2023/06/19
 * description: store interrupt function
****************************************************************************/
#include "AllHead.h"

/* Public variables==========================================================*/
extern uint8_t ucUart1_Rec_Buff[128];
char AT_String[255];
extern const char *TOPIC_PROPERTY_SUB;
/*
* @function: HAL_TIM_PeriodElapsedCallback
* @param: None
* @retval: None
* @brief: timer callback function
*/
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
  if (htim->Instance == htim4.Instance) // 1ms
  {
    static uint8_t cat_uart_rx = 0;
    static uint16_t cat_reset_cnt = 0;  // 模块复位所需时间计数

    cat_uart_rx++;

    if (cat_uart_rx >= 50)  // 串口接收回传校验
    {
      cat_uart_rx = 0;
      bsp_NBLOT_uart.Bsp_NBLOT_Uart_Handler();
    }
    if (bsp_NBLOT.Reset_Status != RESET_OVER) // 复位计数
    {
      cat_reset_cnt++;

      if (cat_reset_cnt >= BSP_NBLOT_RESET_TIME)
      {
        cat_reset_cnt = 0;
        bsp_NBLOT.Reset_Status = RESET_OVER;  // 复位完成
      }
    }

    System.Task_Marks_Handler();
  }
}

void USART2_IRQHandler(void)
{
	if(__HAL_UART_GET_FLAG(&huart2,UART_FLAG_IDLE) != 0x00u)
	{
		bsp_NBLOT_uart.Bsp_NBLOT_Uart_Data_Parse();
		__HAL_UART_CLEAR_IDLEFLAG(&huart2);
	}		
  HAL_UART_IRQHandler(&huart2);
}

void USART1_IRQHandler(void)
{
	if(__HAL_UART_GET_FLAG(&huart1,UART_FLAG_IDLE) != 0x00u)
	{
		__HAL_UART_CLEAR_IDLEFLAG(&huart1);
    HAL_UART_DMAStop(&huart1);   // 串口停止DMA接收
		if (strstr((char*)ucUart1_Rec_Buff, (char*)"MQTT_DISABLE") != NULL) // 断开MQTT连接
		{
			bsp_NBLOT.bsp_NBLOT_Step_Status = TASK_CIOT_MQTT_DISCONN; // 断开
		}
    HAL_UART_Receive_DMA(&huart1, ucUart1_Rec_Buff, 128);    
	}
	
	HAL_UART_IRQHandler(&huart1);
}
System_Init.c
/***************************************************************************
 * File: System_Init.c
 * Author: Luckys.
 * Date: 2023/06/23
 * description: 
****************************************************************************/
#include "AllHead.h"

/*====================================static function declaration area BEGIN====================================*/
static void Hardware_Init(void);
/*====================================static function declaration area   END====================================*/
System_Init_t System_Init = 
{
    .Hardware_Init = &Hardware_Init
};

/* Public variables==========================================================*/
uint8_t ucUart1_Rec_Buff[128] = {0};    // 



/*
* @function: Hardware_Init
* @param: None
* @retval: None
* @brief: 
*/
static void Hardware_Init(void)
{
#if 1	
    __HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE); // 
    HAL_UART_Receive_DMA(&huart1, ucUart1_Rec_Buff, 128);	
#endif	
    bsp_NBLOT_uart.Bsp_NBLOT_Uart_Init();   // 
    printf("Hello\r\n");
	HAL_TIM_Base_Start_IT(&htim4);
}
Task.c
/***************************************************************************
 * File: Task.c
 * Author: Luckys.
 * Date: 2023/06/23
 * description: 
****************************************************************************/
#include "AllHead.h"

/* Public variables==========================================================*/
extern const char *TOPIC_PROPERTY_SUB;

/*====================================static function declaration area BEGIN====================================*/
static void TasksHandle_200MS(void);
static void TasksHandle_1S(void);
static void TasksHandle_2S(void);

/*====================================static function declaration area   END====================================*/

Task_t Task[] =
{
				{FALSE, 200, 200, TasksHandle_200MS}, // task Period: 200ms
				{FALSE, 1000, 1000, TasksHandle_1S},	// task Period: 1s
				{FALSE, 2000, 2000, TasksHandle_2S},	// task Period: 2s
};

/*====================================variable definition declaration area BEGIN===================================*/

uint8_t ucTasks_Max = sizeof(Task) / sizeof(Task[0]);

/*====================================variable definition declaration area   END===================================*/

static inline void TasksHandle_200MS(void)
{
	HAL_GPIO_TogglePin(LED_GPIO_Port,LED_Pin);
	bsp_NBLOT.bsp_NBLOT_Running();
}

static inline void TasksHandle_1S(void)
{
	bsp_NBLOT.bsp_NBLOT_Data_Upload_1();
}

static inline void TasksHandle_2S(void)
{ 

}
bsp_NBLOT.h
#ifndef __BSP_NBLOT_H
#define __BSP_NBLOT_H

// 使用调试打印(可以打印发送与接收回传数据到上位机串口助手)
#define USE_Debug_Print 1
// 使用正常打印
#define USE_Normal_Print 1
// 模块复位完成所需时间(大概5s)
#define BSP_NBLOT_RESET_TIME (uint16_t)5000
// 等待回传的计数值(测试发现大概正常情况下收到回传是330000左右,所以需要设置比它大几倍即可)
#define BSP_NBLOT_WAIT_COUNT 2000000
// 全自动模式(断开连接后自动重新复位连接)
#define BSP_NBLOT_AUTO_CONNNECT_MODE 1

/* CAT1模块工作步骤列表 */
typedef enum
{
    TASK_CIOT_AT_TEST = 0,  // 【AT】测试模块是否正常
    TASK_CIOT_AT_TEST_RSP,

    TASK_NBIOT_DISABLE_PSM,   // 【AT+QSCLK=0】禁止模块休眠
    TASK_NBIOT_DISABLE_PSM_RSP,

    TASK_NBIOT_DISABLE_ECHO,    // 【ATE0】关闭回显
    TASK_NBIOT_DISABLE_ECHO_RSP,

    TASK_NBIOT_SET_BAND,    // 【AT+QBAND=1,8】设置模块的工作频段  3: 中国联通 5: 中国电信 8: 中国移动
    TASK_NBIOT_SET_BAND_RSP,

    TASK_NBIOT_DHCP,    // 【AT+CGPADDR?】读取设备IP地址
    TASK_NBIOT_DHCP_RSP,

    TASK_CIOT_MQTT_OPEN,    // 【AT+QMTOPEN=0,[CLIENT_ID],1883】打开MQTT连接,CLIENT_ID是上面定义的那个
    TASK_CIOT_MQTT_OPEN_RSP,

    TASK_CIOT_MQTT_CONNECT, // 【AT+QMTCONN=0,[CLIENT_ID],[USERNAME],[PASSWORD]】连接MQTT服务器
    TASK_CIOT_MQTT_CONNECT_RSP,

    TASK_CONNECT_SUCCESS,   // 连接MQTT服务器成功(结束)

    TASK_CIOT_MQTT_DISCONN, // 【AT+CMQTTDISC=0,120】断开MQTT连接
    TASK_CIOT_MQTT_DISCONN_RSP,

    TASK_CIOT_RESET,    // 【AT+CRESET】 模块复位
    TASK_CIOT_RESET_RSP,

    TASK_IDLE,  // 空闲状态(空转)
} bsp_NBLOT_Mode_Steps_et;

// 返回状态枚举
typedef enum
{
    RET_PASS = -1,    // 成功
    RET_FAIL = 0    // 失败
} bsp_NBLOT_Return_Status_et;

// 复位状态枚举
typedef enum
{
    RESET_NOT = 0,   // 未复位
    RESET_OVER = 1,  // 复位完成
} bsp_NBLOT_Reset_Status_et;

// 模块接收状态
typedef enum
{
    Rec_Status_None = 0, // 不接收
    Rec_Status_CheckCmd_Plan1 = 1, // 校验指令回传 --- "OK"
    Rec_Status_CheckCmd_Plan2 = 2, // 校验指令回传 --- "+CGPADDR:  / OK"
    Rec_Status_CheckCmd_Plan3 = 3, // 校验指令回传 --- "+QMTOPEN: "
    Rec_Status_CheckCmd_Plan4 = 4, // 校验指令回传 --- "+QMTCONN: "
    Rec_Status_CheckCmd_Plan5 = 5, // 校验指令回传 --- ">"
    Rec_Status_CheckCmd_Plan6 = 6, // 校验指令回传 --- "+QMTPUB: "
    Rec_Status_MAX = Rec_Status_CheckCmd_Plan6 + 1,     // 枚举成员数量(当做数组的大小)
} bsp_NBLOT_Rec_Status_et;

typedef struct
{
    bsp_NBLOT_Reset_Status_et Reset_Status; // 复位状态
    uint8_t bsp_NBLOT_MQTT_Connect_Flag;    // 连接MQTT服务器标志位
    int8_t bsp_NBLOT_Ret_Status_buf[Rec_Status_MAX];    // 存储返回值数组
    bsp_NBLOT_Rec_Status_et bsp_NBLOT_Rec_Status;   // 模块接收状态
    int16_t bsp_NBLOT_Step_Status;  // AT模块工作步骤标记状态

    uint8_t bsp_NBLOT_Pub_Buf[254]; // MQTT数据上报存储数组
    uint8_t bsp_NBLOT_Pub_Len;  // MQTT数据上报长度

    void (*bsp_NBLOT_Running)(void);   // 模块运行
    void (*bsp_NBLOT_Data_Upload_1)(void); // 数据上传1
} bsp_NBLOT_st;

extern bsp_NBLOT_st bsp_NBLOT;

#endif
bsp_NBLOT.c
/***************************************************************************
 * File: bsp_NBLOT.c
 * Author: Yang
 * Date: 2023/12/15
 * description: 
 -----------------------------------

 -----------------------------------
****************************************************************************/
#include "AllHead.h"

/* Private variables=========================================================*/

/* 腾讯云MQTT服务器信息 */
// 域名
static const char *DOMAIN = "J7X2YMW6IU.iotcloud.tencentdevices.com";
// 客户端ID
static const char *CLIENT_ID = "J7X2YMW6IU";
// 用户名
static const char *USERNAME = "J7X2YMW6IUTH_Sensor_Test;12010126;0M3Y7;1992171084";
// 密码
static const char *PASSWORD = "a88fc461af8202b6933524ec9b5c9d2fac226a35cbc09b9d4150a1d00000406f;hmacsha256";
// 属性发布主题
static const char *TOPIC_PROPERTY_PUB = "$thing/up/property/J7X2YMW6IU/TH_Sensor_Test";
// 模块超时计数
int16_t bsp_NBLOT_Timeout_Count;    
// 模块AT指令字符串
char bsp_NNBLOT_AT_String[255];  

/* Private function prototypes===============================================*/
static void bsp_NBLOT_Running(void);
static void bsp_NBLOT_Data_Upload_1(void);

static void bsp_NBLOT_Ret_Check(int8_t ret_status);
static void bsp_NBLOT_BeforeSending_Parameter_Init(bsp_NBLOT_Rec_Status_et rec_status, uint8_t* str);
static void bsp_NBLOT_Moduel_Reset(void);
static void bsp_NBLOT_Upload_Data_To_Pub(const char* pub_string, const char* pub_data_string);
/* Public variables==========================================================*/

bsp_NBLOT_st bsp_NBLOT = 
{
    .Reset_Status = RESET_NOT,
    .bsp_NBLOT_MQTT_Connect_Flag = FALSE,
    .bsp_NBLOT_Ret_Status_buf = {RET_FAIL},
    .bsp_NBLOT_Rec_Status = Rec_Status_None,
    .bsp_NBLOT_Step_Status = TASK_CIOT_AT_TEST,

    .bsp_NBLOT_Pub_Buf = {0},
    .bsp_NBLOT_Pub_Len = 0,
    
    .bsp_NBLOT_Running = &bsp_NBLOT_Running,
    .bsp_NBLOT_Data_Upload_1 = &bsp_NBLOT_Data_Upload_1
};

/*=========================================== 应用层函数 ===========================================*/

/*
 * @function: bsp_NBLOT_Running
 * @param: None
 * @retval: None
 * @brief: 模块运行
 */
static void bsp_NBLOT_Running(void)
{
    // 未复位成功则退出
    if (bsp_NBLOT.Reset_Status != RESET_OVER)
    {
        return;
    }
    switch (bsp_NBLOT.bsp_NBLOT_Step_Status)
    {
    case TASK_CIOT_AT_TEST:	// 【发送AT测试是否正常】
    {
        /*状态复位*/
        bsp_NBLOT_BeforeSending_Parameter_Init(Rec_Status_CheckCmd_Plan1, (uint8_t *)"AT\r\n");    
        break;
    }
    case TASK_CIOT_AT_TEST_RSP:	// 【判断回传--- "OK"】
    {
        bsp_NBLOT_Ret_Check(bsp_NBLOT.bsp_NBLOT_Ret_Status_buf[Rec_Status_CheckCmd_Plan1]);
        break;
    }
    case TASK_NBIOT_DISABLE_PSM:  // 【禁止模块休眠】
    {
        /*状态复位*/
        bsp_NBLOT_BeforeSending_Parameter_Init(Rec_Status_CheckCmd_Plan1, (uint8_t *)"AT+QSCLK=0\r\n");     
        break;
    }
    case TASK_NBIOT_DISABLE_PSM_RSP: // 【判断回传--- "OK"】
    {
        bsp_NBLOT_Ret_Check(bsp_NBLOT.bsp_NBLOT_Ret_Status_buf[Rec_Status_CheckCmd_Plan1]);        
        break;
    }
    case TASK_NBIOT_DISABLE_ECHO:   // 【关闭回显】
    {
        /*状态复位*/
        bsp_NBLOT_BeforeSending_Parameter_Init(Rec_Status_CheckCmd_Plan1, (uint8_t *)"ATE0\r\n");        
        break;
    }
    case TASK_NBIOT_DISABLE_ECHO_RSP:   // 【判断回传--- "OK"】
    {
        bsp_NBLOT_Ret_Check(bsp_NBLOT.bsp_NBLOT_Ret_Status_buf[Rec_Status_CheckCmd_Plan1]);
        break;
    }
    case TASK_NBIOT_SET_BAND:   // 【设置模块的工作频段】
    {
        /*状态复位*/
        bsp_NBLOT_BeforeSending_Parameter_Init(Rec_Status_CheckCmd_Plan1, (uint8_t *)"AT+QBAND=1,8\r\n");       
        break;
    }
    case TASK_NBIOT_SET_BAND_RSP:   // 【判断回传--- "OK"】
    {
        bsp_NBLOT_Ret_Check(bsp_NBLOT.bsp_NBLOT_Ret_Status_buf[Rec_Status_CheckCmd_Plan1]);       
        break;
    }
    case TASK_NBIOT_DHCP:   // 【读取设备IP地址】
    {
        /*状态复位*/
        bsp_NBLOT_BeforeSending_Parameter_Init(Rec_Status_CheckCmd_Plan2, (uint8_t *)"AT+CGPADDR?\r\n");             
        break;
    }
    case TASK_NBIOT_DHCP_RSP:   // 【判断回传--- "+CGPADDR: 0,"xxx.xxx.xxx.xxx""】
    {
        bsp_NBLOT_Ret_Check(bsp_NBLOT.bsp_NBLOT_Ret_Status_buf[Rec_Status_CheckCmd_Plan2]);        
        break;
    }
    case TASK_CIOT_MQTT_OPEN:   // 【打开MQTT连接】
    {
        /*状态复位*/
        Public.Memory_Clear((uint8_t*)bsp_NNBLOT_AT_String, strlen((char*)bsp_NNBLOT_AT_String)); // 清0
        sprintf(bsp_NNBLOT_AT_String,"AT+QMTOPEN=0,\"%s\",1883\r\n",DOMAIN);
        bsp_NBLOT_BeforeSending_Parameter_Init(Rec_Status_CheckCmd_Plan3, (uint8_t *)bsp_NNBLOT_AT_String);
        break;
    }
    case TASK_CIOT_MQTT_OPEN_RSP:   // 【判断回传--- "+QMTOPEN: "】
    {
        if (RET_PASS == bsp_NBLOT.bsp_NBLOT_Ret_Status_buf[Rec_Status_CheckCmd_Plan3])
        {
            bsp_NBLOT.bsp_NBLOT_Rec_Status = Rec_Status_None;
            bsp_NBLOT.bsp_NBLOT_Step_Status++;
        }
        else
        {
            ++bsp_NBLOT_Timeout_Count;

            if (bsp_NBLOT_Timeout_Count >= 5)
            {
                bsp_NBLOT_Timeout_Count = 0;
                // +(重发不生效)
                bsp_NBLOT_Moduel_Reset();    // 复位
            }
        }              
        break;
    }
    case TASK_CIOT_MQTT_CONNECT:    // 【连接MQTT服务器】
    {
        /*状态复位*/
        Public.Memory_Clear((uint8_t*)bsp_NNBLOT_AT_String, strlen((char*)bsp_NNBLOT_AT_String)); // 清0
        sprintf(bsp_NNBLOT_AT_String,"AT+QMTCONN=0,\"%s\",\"%s\",\"%s\"\r\n",CLIENT_ID,USERNAME,PASSWORD);
        bsp_NBLOT_BeforeSending_Parameter_Init(Rec_Status_CheckCmd_Plan4, (uint8_t *)bsp_NNBLOT_AT_String);        
        break;
    }
    case TASK_CIOT_MQTT_CONNECT_RSP:    // 【判断回传--- "+QMTCONN: "】
    {
        if (RET_PASS == bsp_NBLOT.bsp_NBLOT_Ret_Status_buf[Rec_Status_CheckCmd_Plan4])
        {
            bsp_NBLOT.bsp_NBLOT_Rec_Status = Rec_Status_None;
            bsp_NBLOT.bsp_NBLOT_Step_Status++;
        }
        else
        {
            ++bsp_NBLOT_Timeout_Count;

            if (bsp_NBLOT_Timeout_Count >= 5)
            {
                bsp_NBLOT_Timeout_Count = 0;
                // +(重发不生效)
                bsp_NBLOT_Moduel_Reset();    // 复位
            }
        }         
    }
    case TASK_CONNECT_SUCCESS:  // 连接MQTT服务器成功
    {
        bsp_NBLOT.bsp_NBLOT_MQTT_Connect_Flag = TRUE;
        bsp_NBLOT.bsp_NBLOT_Step_Status = TASK_IDLE;
#if USE_Normal_Print
        printf("MQTT CONNECT SUCCESS\r\n");
#endif        
        break;
    }
    case TASK_IDLE: // 空闲状态
    {
        break;
    }
    case TASK_CIOT_MQTT_DISCONN:   // 【断开MQTT连接】
    {
        bsp_NBLOT.bsp_NBLOT_MQTT_Connect_Flag = FALSE;
        bsp_NBLOT_BeforeSending_Parameter_Init(Rec_Status_CheckCmd_Plan1, (uint8_t *)"AT+QMTDISC=0\r\n");
        break;
    }
    case TASK_CIOT_MQTT_DISCONN_RSP:    // 【判断回传---"OK"】
    {
        bsp_NBLOT_Ret_Check(bsp_NBLOT.bsp_NBLOT_Ret_Status_buf[Rec_Status_CheckCmd_Plan1]);
        break;
    }
    default:
    {
        bsp_NBLOT.bsp_NBLOT_Step_Status = TASK_IDLE;
        break;
    }
    }
}

/*
* @function: bsp_NBLOT_Data_Upload_1
* @param: None
* @retval: None
* @brief: 数据上传1
*/
static void bsp_NBLOT_Data_Upload_1(void)
{
    static float a = 0,b = 0,c = 0,d = 0;
    Public.Memory_Clear((uint8_t*)bsp_NBLOT.bsp_NBLOT_Pub_Buf, strlen((char*)bsp_NBLOT.bsp_NBLOT_Pub_Buf));
    sprintf((char *)bsp_NBLOT.bsp_NBLOT_Pub_Buf, "{\"method\":\"report\",\"params\":{\"temp\":%.1f,\"humi\":%.1f,\"longitude\":%f,\"latitude\":%f}}", ++a, ++b, ++c, ++d);
    bsp_NBLOT_Upload_Data_To_Pub(TOPIC_PROPERTY_PUB, (char*)bsp_NBLOT.bsp_NBLOT_Pub_Buf);
}

/*=========================================== 中间层函数 ===========================================*/

/*
* @function: bsp_NBLOT_Ret_Check
* @param: None
* @retval: None
* @brief: 回传结果检测
*/
static void bsp_NBLOT_Ret_Check(int8_t ret_status)
{
    static uint16_t error_counter = 0;

    if (RET_PASS == ret_status)
    {
        error_counter = 0;
        bsp_NBLOT.bsp_NBLOT_Rec_Status = Rec_Status_None;
        bsp_NBLOT.bsp_NBLOT_Step_Status++;
#if 0        
        printf("1234\r\n");
#endif        
    }
    else
    {
        ++bsp_NBLOT_Timeout_Count;

        if (bsp_NBLOT_Timeout_Count >= 8)
        {
            error_counter++;
            bsp_NBLOT.bsp_NBLOT_Step_Status--;
        }
    }

    if (error_counter >= 10)
    {
        bsp_NBLOT_Moduel_Reset();   // 复位
    }
}

/*
* @function: bsp_NBLOT_BeforeSending_Parameter_Init
* @param: None
* @retval: None
* @brief: 参数初始化且发送
*/
static void bsp_NBLOT_BeforeSending_Parameter_Init(bsp_NBLOT_Rec_Status_et rec_status, uint8_t* str)
{
        /*状态复位*/
        bsp_NBLOT.bsp_NBLOT_Rec_Status = rec_status;
        bsp_NBLOT.bsp_NBLOT_Ret_Status_buf[rec_status] = RET_FAIL;
        bsp_NBLOT_Timeout_Count = 0;

        bsp_NBLOT_uart.Bsp_NBLOT_Uart_Send_String((uint8_t *)str);
        bsp_NBLOT.bsp_NBLOT_Step_Status++;
}

/*
* @function: bsp_NBLOT_Moduel_Reset
* @param: None
* @retval: None
* @brief: 模块复位
*/
static void bsp_NBLOT_Moduel_Reset(void)
{
    bsp_NBLOT.bsp_NBLOT_MQTT_Connect_Flag = FALSE; // 标志位置0
    bsp_NBLOT.Reset_Status = RESET_NOT; // 未复位
    bsp_NBLOT_uart.Bsp_NBLOT_Uart_Send_String((uint8_t *)"AT+QRST=1\r\n");
#if BSP_NBLOT_AUTO_CONNNECT_MODE
    bsp_NBLOT.bsp_NBLOT_Step_Status = TASK_CIOT_AT_TEST;    // AT模式
#endif
#if USE_Normal_Print
        printf("RESET...\r\n");
#endif
}

/*
* @function: bsp_NBLOT_Upload_Data_To_Pub
* @param: pub_string -> 发送到的目的地主题 pub_data_string -> 要上传到主题的数据内容
* @retval: None
* @brief: 上传数据到MQTT主题
*/
static void bsp_NBLOT_Upload_Data_To_Pub(const char* pub_string, const char* pub_data_string)
{
    uint8_t step = 0;    // 运行到哪步计数
    uint32_t timeout = 0; // 重发超时时间计数
    static uint16_t error_count = 0; // 错误计数(复位)

    if (bsp_NBLOT.bsp_NBLOT_MQTT_Connect_Flag)
    {
        switch (step)
        {
        case 0: // // 向 "xxx" 主题发送消息数据,数据长度为xx
        {
            timeout = 0;
            bsp_NBLOT.bsp_NBLOT_Rec_Status = Rec_Status_CheckCmd_Plan5;
            bsp_NBLOT.bsp_NBLOT_Ret_Status_buf[Rec_Status_CheckCmd_Plan5] = RET_FAIL;
            Public.Memory_Clear((uint8_t*)bsp_NNBLOT_AT_String, strlen((char*)bsp_NNBLOT_AT_String)); // 清0

            // 设置待发布的主题和要发送的数据长度
            sprintf(bsp_NNBLOT_AT_String, "AT+QMTPUB=0,0,0,0,\"%s\",%d\r\n", pub_string, strlen(pub_data_string));
            bsp_NBLOT_uart.Bsp_NBLOT_Uart_Send_String((uint8_t *)bsp_NNBLOT_AT_String);

            step++;
        }
        case 1: // 判断回传 ">" 发送数据
        {
            // 超时等待
            while (1)
            {
                if (RET_PASS == bsp_NBLOT.bsp_NBLOT_Ret_Status_buf[Rec_Status_CheckCmd_Plan5])
                {
                    // 发送数据
#if 0                    
                    printf("-----%d------\r\n",timeout);
#endif
                    error_count = 0;
                    timeout = 0;

                    bsp_NBLOT.bsp_NBLOT_Rec_Status = Rec_Status_CheckCmd_Plan6;
                    bsp_NBLOT.bsp_NBLOT_Ret_Status_buf[Rec_Status_CheckCmd_Plan6] = RET_FAIL;

                    bsp_NBLOT_uart.Bsp_NBLOT_Uart_Send_String((const uint8_t *)pub_data_string);

                    step++;
                    break; // 退出循环
                }

                timeout++;
                if (timeout >= BSP_NBLOT_WAIT_COUNT)
                {
                    error_count++;
                    goto error_handling;
                }
            }
        }
        case 2: //判断回传 "OK"/"+QMTPUB: 0,0,0"
        {
            // 超时等待
            while (1)
            {
                if (RET_PASS == bsp_NBLOT.bsp_NBLOT_Ret_Status_buf[Rec_Status_CheckCmd_Plan6])
                {           
                    error_count = 0;
                    timeout = 0;

                    bsp_NBLOT.bsp_NBLOT_Rec_Status = Rec_Status_None;
                    bsp_NBLOT.bsp_NBLOT_Ret_Status_buf[Rec_Status_None] = RET_FAIL; 

                    step++;
                    break;  // 退出循环
                }

                timeout++;
                if (timeout >= BSP_NBLOT_WAIT_COUNT)
                {
                    error_count++;
                    goto error_handling;
                }                
            }
        }
        default:
            break;
        }
    }
    // 错误处理
    error_handling:
    {
        if (error_count >= 10) // 长时间发送失败
        {
            error_count = 0;
            bsp_NBLOT.bsp_NBLOT_Step_Status = TASK_CIOT_MQTT_DISCONN; // 断开
        }
        return;
    }    
}
bsp_NBLOT_uart.h
#ifndef __BSP_NBLOT_UART_H
#define __BSP_NBLOT_UART_H
#include "AllHead.h"

// 模块使用的串口
#define BSP_NBLOT_USE_Serial huart2
// 串口接收最大长度
#define BSP_NBLOT_Rec_MAX_LEN 168

typedef struct
{
	uint8_t ucUart_Rec_Over_Flag;	// 串口接收完成标志位
	uint16_t usUart_Rec_Len;	// 串口接收数据长度
    uint8_t *pucRec_Buffer; // 接收缓存指针
    void (*Bsp_NBLOT_Uart_Init)(void); // 串口初始化
    void (*Bsp_NBLOT_Uart_Send_String)(const uint8_t *); // 串口发送字符串
    void (*Bsp_NBLOT_Uart_Data_Parse)(void);   // 串口数据解析
    void (*Bsp_NBLOT_Uart_Handler)(void);  // 串口处理函数
} bsp_NBLOT_uart_st;

extern bsp_NBLOT_uart_st bsp_NBLOT_uart;

#endif
bsp_NBLOT_uart.c
/***************************************************************************
 * File: bsp_NBLOT_uart.c
 * Author: Yang
 * Date: 2023/12/06
 * description: 
 -----------------------------------
串口接线:
        PA2(TX) --- 模块RX
        PA3(RX) --- 模块TX
        GND --- GND
        3.3V --- 3.3V
 -----------------------------------
****************************************************************************/
#include "AllHead.h"

/* Private variables=========================================================*/
static uint8_t NBLOT_Uart_Rec_Temp_Arr[BSP_NBLOT_Rec_MAX_LEN];        // 串口接收临时缓存数组
static uint8_t NBLOT_Uart_Rec_Arr[BSP_NBLOT_Rec_MAX_LEN];   // 模块串口接收数组

/* Private function prototypes===============================================*/
static void Bsp_NBLOT_Uart_Init(void);
static void Bsp_NBLOT_Uart_Send_String(const uint8_t *pStr);
static void Bsp_NBLOT_Uart_Data_Parse(void);
static void Bsp_NBLOT_Uart_Handler(void);
/* Public variables==========================================================*/
bsp_NBLOT_uart_st bsp_NBLOT_uart =
{
				.ucUart_Rec_Over_Flag = FALSE,
				.usUart_Rec_Len = 0,
				.pucRec_Buffer = NBLOT_Uart_Rec_Arr,
				.Bsp_NBLOT_Uart_Init = &Bsp_NBLOT_Uart_Init,
				.Bsp_NBLOT_Uart_Send_String = &Bsp_NBLOT_Uart_Send_String,
				.Bsp_NBLOT_Uart_Data_Parse = &Bsp_NBLOT_Uart_Data_Parse,
				.Bsp_NBLOT_Uart_Handler = &Bsp_NBLOT_Uart_Handler
};

/*
 * @function: Bsp_NBLOT_Uart_Init
 * @param: None
 * @retval: None
 * @brief: 串口初始化
 */
static void Bsp_NBLOT_Uart_Init(void)
{
    __HAL_UART_ENABLE_IT(&BSP_NBLOT_USE_Serial, UART_IT_IDLE); // 使能串口空闲中断
    HAL_UART_Receive_DMA(&BSP_NBLOT_USE_Serial, NBLOT_Uart_Rec_Temp_Arr, (uint16_t)BSP_NBLOT_Rec_MAX_LEN);                                                       // 接收清0
}

/*
 * @function: Bsp_NBLOT_Uart_Send_String
 * @param: None
 * @retval: None
 * @brief: 串口发送字符串
 */
static void Bsp_NBLOT_Uart_Send_String(const uint8_t *pStr)
{
        HAL_UART_Transmit(&BSP_NBLOT_USE_Serial, pStr, strlen((const char *)pStr), 5000);
#if USE_Debug_Print				
		HAL_UART_Transmit(&huart1, pStr, strlen((const char *)pStr), 5000);
#endif				
}

/*
 * @function: Bsp_NBLOT_Uart_Data_Parse
 * @param: None
 * @retval: None
 * @brief: 串口接收数据解析
 */
static void Bsp_NBLOT_Uart_Data_Parse(void)
{
	HAL_UART_DMAStop(&BSP_NBLOT_USE_Serial);   // 串口停止DMA接收
	bsp_NBLOT_uart.usUart_Rec_Len = BSP_NBLOT_Rec_MAX_LEN - __HAL_DMA_GET_COUNTER(&hdma_usart2_rx);
	Public.Memory_Copy((char*)bsp_NBLOT_uart.pucRec_Buffer, (char*)NBLOT_Uart_Rec_Temp_Arr, bsp_NBLOT_uart.usUart_Rec_Len);
	bsp_NBLOT_uart.ucUart_Rec_Over_Flag = TRUE;
}

/*
 * @function: Bsp_NBLOT_Uart_Handler
 * @param: None
 * @retval: None
 * @brief: 串口接收处理函数
 */
static void Bsp_NBLOT_Uart_Handler(void)
{
	if (bsp_NBLOT_uart.ucUart_Rec_Over_Flag)
	{
		switch (bsp_NBLOT.bsp_NBLOT_Rec_Status)
		{
			case Rec_Status_None:
			{
				break;
			}
			case Rec_Status_CheckCmd_Plan1:
			{
				if (strstr((char*)bsp_NBLOT_uart.pucRec_Buffer, (char*)"OK") != NULL)
				{
					bsp_NBLOT.bsp_NBLOT_Ret_Status_buf[Rec_Status_CheckCmd_Plan1] = RET_PASS;
				}
				else
				{
					bsp_NBLOT.bsp_NBLOT_Ret_Status_buf[Rec_Status_CheckCmd_Plan1] = RET_FAIL;
				}
				break;
			}
			case Rec_Status_CheckCmd_Plan2:
			{
				if (strstr((char*)bsp_NBLOT_uart.pucRec_Buffer, (char*)"OK") != NULL)	// 寻找子串"OK"
				{
					char *start = strstr((char*)bsp_NBLOT_uart.pucRec_Buffer, (char*)"+CGPADDR: ");	// 寻找子串"+CGPADDR: "

					if (NULL == start)
					{
						bsp_NBLOT.bsp_NBLOT_Ret_Status_buf[Rec_Status_CheckCmd_Plan2] = RET_FAIL;
					}
					else
					{
						char *ip = start + 13;	// 获取 IP 地址的起始位置
						char *end = strchr(ip, '"');	// 获取 IP 地址的结束位置

						if (NULL == end)
						{
							bsp_NBLOT.bsp_NBLOT_Ret_Status_buf[Rec_Status_CheckCmd_Plan2] = RET_FAIL;
						}
						else
						{
							*end = 0;	// 在 IP 地址的结束位置处添加 '\0' 字符,表示字符串的结束

							if (strlen(ip) < 7)	// 如果 IP 地址的长度小于 7
							{
								bsp_NBLOT.bsp_NBLOT_Ret_Status_buf[Rec_Status_CheckCmd_Plan2] = RET_FAIL;
							}
							else
							{
								bsp_NBLOT.bsp_NBLOT_Ret_Status_buf[Rec_Status_CheckCmd_Plan2] = RET_PASS;
							}
						}
					}
				}
				else
				{
					bsp_NBLOT.bsp_NBLOT_Ret_Status_buf[Rec_Status_CheckCmd_Plan2] = RET_FAIL;
				}				
				break;
			}
			case Rec_Status_CheckCmd_Plan3:
			{
				if (strstr((char*)bsp_NBLOT_uart.pucRec_Buffer, (char*)"+QMTOPEN: ") != NULL)
				{
					bsp_NBLOT.bsp_NBLOT_Ret_Status_buf[Rec_Status_CheckCmd_Plan3] = RET_PASS;
				}
				else
				{
					bsp_NBLOT.bsp_NBLOT_Ret_Status_buf[Rec_Status_CheckCmd_Plan3] = RET_FAIL;
				}				
				break;
			}
			case Rec_Status_CheckCmd_Plan4:
			{
				if (strstr((char*)bsp_NBLOT_uart.pucRec_Buffer, (char*)"+QMTCONN: ") != NULL)
				{
					bsp_NBLOT.bsp_NBLOT_Ret_Status_buf[Rec_Status_CheckCmd_Plan4] = RET_PASS;
				}
				else
				{
					bsp_NBLOT.bsp_NBLOT_Ret_Status_buf[Rec_Status_CheckCmd_Plan4] = RET_FAIL;
				}				
				break;
			}
			case Rec_Status_CheckCmd_Plan5:
			{
				if (strstr((char*)bsp_NBLOT_uart.pucRec_Buffer, (char*)">") != NULL)
				{
					bsp_NBLOT.bsp_NBLOT_Ret_Status_buf[Rec_Status_CheckCmd_Plan5] = RET_PASS;
				}
				else
				{
					bsp_NBLOT.bsp_NBLOT_Ret_Status_buf[Rec_Status_CheckCmd_Plan5] = RET_FAIL;
				}				
				break;
			}
			case Rec_Status_CheckCmd_Plan6:
			{
				if (strstr((char*)bsp_NBLOT_uart.pucRec_Buffer, (char*)"+QMTPUB: ") != NULL)
				{
					bsp_NBLOT.bsp_NBLOT_Ret_Status_buf[Rec_Status_CheckCmd_Plan6] = RET_PASS;
				}
				else
				{
					bsp_NBLOT.bsp_NBLOT_Ret_Status_buf[Rec_Status_CheckCmd_Plan6] = RET_FAIL;
				}				
				break;
			}
			default:break;
		}
		bsp_NBLOT_uart.ucUart_Rec_Over_Flag = FALSE;

#if USE_Debug_Print		
		printf("%s\r\n",bsp_NBLOT_uart.pucRec_Buffer);
#endif

		Public.Memory_Clear(bsp_NBLOT_uart.pucRec_Buffer,bsp_NBLOT_uart.usUart_Rec_Len);
		bsp_NBLOT_uart.usUart_Rec_Len = 0;
		HAL_UART_Receive_DMA(&BSP_NBLOT_USE_Serial, NBLOT_Uart_Rec_Temp_Arr, (uint16_t)BSP_NBLOT_Rec_MAX_LEN);
	}
}
  • 实验现象