当前位置: 首页 > article >正文

【STM32】RTT-Studio中HAL库开发教程十:EC800M-4G模块使用

文章目录

    • 一、简介
    • 二、模块测试
    • 三、OneNet物联网配置
    • 四、完整代码
    • 五、测试验证

一、简介

  EC800M4G是一款4G模块,本次实验主要是进行互联网的测试,模块测试,以及如何配置ONENET设备的相关参数,以及使用STM32F4来测试模块的数据上报功能。EC800M4G模块资料

使用的工具如下:

  • 串口助手
  • EC800M4模块以及模块资料
  • STM32F407单片机最小系统板
  • ONENET物联网设备创建

二、模块测试

1.模块测试 :主要使用串口+模块(EC800M)+MQTT.fx进行模块功能的验证。
(1)模块硬件连接
  将配套的 USB 转串口模块的 RX、TX、VCC、GND 分别与模块 TX、RX、VCC、GND 连接,注意在上电之前,先把SIM 卡正确插入卡座内,连接示意图如下:

在这里插入图片描述
(2)测试模块通信
  将串口接入PC端后,使用串口助手通过发送指令的方式验证模块通信。串口配置:波特率115200,8位数据位,1位停止位,没有校验位。

  • 指令测试:
  • (1)开机,本模组为上电后自动开机,回复:RDY 则视为正常情况。
  • (2)发送:AT\r\n,回复:模块回 OK,表示正常。
  • (3)发送:AT+CIMI\r\n,回复:获取 SIM 卡的 CIMI 号,模块回 15 字节数据,切记 SIM 的插卡方向,如无法获取卡号。
  • (4)发送:AT+CEREG?\r\n,回复:检查模块是否注网成功,模块回+CEREG:0,1表示注网成功,如果模块断电冷启动,可能刚刚开始需要一些时间进行注网,切记在注网成功之后,才能进行网络的相关操作。
  • (5)发送:AT+CSQ\r\n,回复:获取 NBIOT 信号强度,模块回+CSQ:23,99,前面一个十进制数表示信号前度,一般情况下要求高于 16,否则可能存在通信失败的情况。
  • 通过以上测试如果都回复正常,则可以进行MQTT正常的模块验证测试。

2.MQTT.fx :相关配置
(1)MQTT安装包-----MQTT.fx-V1.7.1-Windows
(2)MQTT配置
  a):进入设置界面,进行相关配置。

在这里插入图片描述

  b):进入设置界面,配置服务器参数,包括协议类型、服务器地址、端口号等内容。

在这里插入图片描述

  c):进入设置界面,配置用户名、密码、安全设置、和网诺代理配置。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

3.MQTT和串口通信 :通过MQTT和串口进行数据发送和接收
(1)点击连接服务器,进行配置

在这里插入图片描述
(2)先确保模块可以正常通信和注网成功,再连接MQTT、配置用户名和订阅主题。

在这里插入图片描述

(3)发送数据到MQTT上位机,即可收到串口发送的数据。

在这里插入图片描述
在这里插入图片描述

(4)上位机发送数据到模块。

在这里插入图片描述
在这里插入图片描述


三、OneNet物联网配置

1.创建产品
(1)点击左上角的产品服务,看到物联网开放平台,点进去。

在这里插入图片描述

(2)进入到产品开发-创建产品

在这里插入图片描述

(3)产品种类根据具体产品填写,接入协议也是根据自己的需求选择,我们这里使用的是MQTT协议,数据协议选OneJson,联网方式选择NB,开发方案为标准方案就行,然后点击确定创建成功。

在这里插入图片描述
在这里插入图片描述

(4)此时我们产品创建好了,但是产品下还没有设备,我们来新建一个设备,点击设备管理。填写相关信息,点击确定,设备就添加好了。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

(5)设备连接前,可以在设备管理-设备详情里,查询设备所属产品ID和密钥,这些信息在设备连接平台时需要使用,如下:

在这里插入图片描述

(6)设置物模型:进入产品开发-详情页面,这里我们删除了用不上的属性并自定义了下列属性属性。

在这里插入图片描述

2.连接NB-IOT
(1)连接OneNET时,接口地址和端口号:183.230.40.96,1883是平台默认的MQTT接入服务地址和端口,token是通过key计算出来的,通过使用计算工具计算出来,计算方式入下:OneNET-token生成工具安装包

在这里插入图片描述

//配置协议版本信息
AT+QMTCFG="version",0,4
OK

//打开MQTT通道,需要等+QMTOPEN: 0,0 URC
AT+QMTOPEN=0,183.230.40.96,1883
OK
+QMTOPEN: 0,0

//连接MQTT,需要等+QMTCONN: 0,0,0 URC
AT+QMTCONN=0,"EC800816","WtF1aQE659","version=2018-10-
31&res=products%2FWtF1aQE659%2Fdevices%2FEC800816&et=2537256630&method=md5&sign=VJfBWuWM43wPgj%2BH7B0G8w%3D%3D"
OK

(2)订阅通信主题:物联网平台中,服务端和设备端通过通信主题Topic实现消息通信,设备可以通过发布消息到系统 topic 调用服务接口,也可以订阅系统 topic 用于接收服务消息通知,服务提供的系统 topic可在物模型 -下一步 - topic管理 - 物模型topic中查看。

设备侧需要收到平台下发的数据topic为:

$sys/{pid}/{device-name}/thing/property/set

属性上报的topic为:

$sys/{pid}/{device-name}/thing/property/post

订阅这2个topic的AT指令如下:

AT+QMTSUB=0,1,"$sys/WtF1aQE659/EC800816/thing/property/post/reply",0,"$sys/WtF1aQE659/EC800816/thing/property/set",0

接收到OK +QMTSUB: 0,1,0,0,0则表示订阅成功。

(3) 事件上报:我们把设备信息上报到平台,对应的AT指令:

//发布消息,133为数据⻓度,出来> 后发送数据,需要等+QMTPUBEX: 0,0,0 URC才能发下
⼀条数据
AT+QMTPUBEX=0,0,0,0,"$sys/WtF1aQE659/EC800816/thing/property/post",110
> {"id":"19","version":"1.0","params":{"humidity":{"value":54.23},"temp":{"value":42.56},"key":{"value":true}}}
OK
+QMTPUBEX: 0,0,0

上报数据成功后,订阅的属性上报会返回success:
在这里插入图片描述
进入设备管理-设备详情,可以查看设备上报的属性数据。
在这里插入图片描述

(4)设置属性:在页面设备管理-详情-设备调试-应用模拟器-属性期望值设置,可设置设备属性。串口收到如下数据:

+QMTRECV: 0,0,"$sys/WtF1aQE659/EC800816/thing/property/set","{"id":"20","version":"1.0","params":{"humidity":65,"key":false,"temp":55}}"

在这里插入图片描述
综合上述的所有操作即可实现串口数据到OneNET数据的下发和上报功能。


四、完整代码

1.main.c 主函数用来进行初始化和执行完之后LED等闪烁功能。

#include <rtthread.h>
#include <drv_common.h>
#define DBG_TAG "main"
#define DBG_LVL DBG_LOG
#include <rtdbg.h>
#include "ec800m.h"

int main(void)
{
    int count = 1;

    rt_pin_mode(GET_PIN(E, 12), PIN_MODE_OUTPUT);

    create_work_test(LTEDemoPath_ONENET);   // 创建工作测试

    while (count)
    {
        rt_pin_write(GET_PIN(E, 12), PIN_HIGH);
        rt_thread_mdelay(1000);

        rt_pin_write(GET_PIN(E, 12), PIN_LOW);
        rt_thread_mdelay(1000);
    }

    return RT_EOK;
}

2.ec800m.c 包含两部分代码:串口线程和IOT的任务函数。
(1)串口线程:主要是串口的初始化配置,串口发送数据函数,以及串口接收线程。当接收到数据之后,需要和校验数据进行校验,只有校验通过之后,才表示发送数据设置成功。

#include "ec800m.h"
#include "lte_at.h"

/*=====================================================##### 串口线程 #####==================================================*/
/**
 * @brief IOT发送数据
 * @param buffer:发送的数据
 * @param size:数据大小
 */
void Iot_Send_Data(const void *buffer, rt_size_t size)
{
    rt_device_write(g_ec800m.dev, 0, buffer, size);
}

/**
 * @brief 接收回调函数
 * @param size:接收数据的大小
 * @return
 */
rt_err_t Rx_Data_Callback(rt_device_t dev, rt_size_t size)
{
    rt_sem_release(g_ec800m.sem);

    return RT_EOK;
}

/**
 * @brief 接收线程回调函数
 */
void Iot_Thread_Entry(void *parameter)
{
    rt_err_t result;
    char ch;
    iot_t *pIot = (iot_t *)parameter;

    while (1)
    {
        result = rt_sem_take(pIot->sem, RT_WAITING_FOREVER);
        if (result == RT_EOK)
        {
            rt_device_read(pIot->dev, 0, &ch, 1);
            g_ec800m.rx_data[g_ec800m.rx_cnt++] = ch;
//            rt_kprintf("%c", ch);
            if (rt_strstr(g_ec800m.rx_data, g_ec800m.check_data) != NULL)
            {
//                rt_kprintf("success!!\n");
                memset(g_ec800m.recv_data, 0, MAX_AT_RESPONSE_LEN);
                memcpy(g_ec800m.recv_data, g_ec800m.rx_data, strlen(g_ec800m.rx_data));
                g_ec800m.check_flag = CHECK_OK;
                g_ec800m.rx_cnt = 0;
                memset(g_ec800m.rx_data, 0, MAX_AT_RESPONSE_LEN);
            }
        }
        else
        {
            g_ec800m.rx_cnt = 0;
            memset(g_ec800m.rx_data, 0, MAX_AT_RESPONSE_LEN);
        }
    }
}

/**
 * @brief IOT模块初始化
 */
static int Iot_EC800M_Init(void)
{
    // 初始化参数
    g_ec800m.rx_data = rt_malloc(MAX_AT_RESPONSE_LEN);
//    memcpy(g_ec800m.check_data, IOT_CHECK_READY, strlen(IOT_CHECK_READY));

    // 查找设备句柄
    g_ec800m.dev = (rt_device_t) rt_device_find(IOT_UART_DEV_NAME);
    RT_ASSERT(g_ec800m.dev != RT_NULL);

    // 打开设备
    rt_err_t ret = rt_device_open(g_ec800m.dev, RT_DEVICE_FLAG_INT_RX);
    RT_ASSERT(ret == RT_EOK);

    // 串口初始化
    struct serial_configure config = RT_SERIAL_CONFIG_DEFAULT;
    config.baud_rate = BAUD_RATE_115200;
    config.data_bits = DATA_BITS_8;
    config.parity = PARITY_NONE;
    rt_device_control(g_ec800m.dev, RT_DEVICE_CTRL_CONFIG, (void *)&config);

    // 接收回调函数
    rt_device_set_rx_indicate(g_ec800m.dev, Rx_Data_Callback);
    // 创建信号量
    g_ec800m.sem = rt_sem_create("rx_sem", 0, RT_IPC_FLAG_PRIO);
    // 创建线程
    g_ec800m.thread = rt_thread_create("usart_rx", Iot_Thread_Entry,
                                           &g_ec800m.dev, 2048, 16, 20);
    if (g_ec800m.thread == RT_NULL)
    {
        rt_kprintf("thread create fail...\n");
    }
    else
    {
        // 启动线程
        rt_thread_startup(g_ec800m.thread);
    }

    return RT_EOK;
}
INIT_ENV_EXPORT(Iot_EC800M_Init);
/*=====================================================#######  END  #######=================================================*/

(2)IOT任务函数:主要是创建IOT初始化以及数据上报的整个流程函数,并且使用的是ONENET数据上报。

/*=====================================================##### IOT任务函数 #####===========================================*/
/**
 * @brief 创建的ONENET任务
 */
void lte_onenet_task_entry( void )
{
    int32_t  atRet = AT_FAILED;
    uint32_t count = 0;
    char     sendData[256];
    float    humidity = 56.45, temp = 24.23;
    char     key[10] = "false";

    rt_kprintf("\n[INFO]This is lte_onenet_task_entry!\n");

    /* 1.初始化模块 */
    atRet = lte_mqtt_init();

    /* 2.打开MQTT通道 */
    atRet |= lte_mqtt_open(IOT_ONNET_brokerAddress, IOT_ONNET_port, "3.1.1");
    if (atRet != 0)
    {
        rt_kprintf("[ERROR]lte_mqtt_init fail \r\n");
        return;
    }

    /* 3.连接MQTT */
    atRet = lte_mqtt_conn(IOT_ONNET_cientId, IOT_ONNET_userName, IOT_ONNET_password);
    if (atRet != 0)
    {
        rt_kprintf("[ERROR]lte_mqtt_conn fail \r\n");
        return;
    }

    /* 4.订阅消息 */
    atRet = lte_mqtt_sub( IOT_ONNET_subtopic);
    if (atRet != 0)
    {
        rt_kprintf("[ERROR]onenet Mqtt Subscriber error \r\n");
        return;
    }

    /* 5.更改数据并上报 */
    while ( 1 )
    {
        count++;

        /* 数据sendData */
        /* {"id":"19","version":"1.0","params":{"humidity":{"value":98.33},"temp":{"value":55.23},"key":{"value":true}}} */
        sprintf(sendData, IOT_MESSAGE, humidity, temp, key);

        rt_kprintf("\r\n[INFO]**********************************************************************************\r\n");
        rt_kprintf("[INFO]Onenet send %d-->%s\r\n", count, sendData);

        /* 发送数据 */
        atRet = lte_mqtt_pubex(IOT_ONNET_pubtopic, sendData);
        if (atRet != 0)
            return;

        /* 数据变化 */
        temp += 0.1;
        temp = temp > 38.0 ? 24.0 : temp;

        humidity += 0.5;
        humidity = humidity > 99.9 ? 24.0 : humidity;

        if (count % 2 == 0)
        {
            strcpy(key, "false");
        }
        else
        {
            strcpy(key, "true");
        }

        if (count > 10)
        {
            count = 1;
            break;
        }

        /* 上传至少3S */
        rt_thread_mdelay(5000);
    }
}

/**
 * @brief LTE任务
 * @param protocolType:连接方式
 * @return 工作状态
 */
uint32_t lte_task( LTEAppDemoPath_t protocolType )
{
    uint32_t uwRet = 0;
    void     (*threadLTE_entry)();

    switch (protocolType)
    {
        case LTEDemoPath_UDP:       threadLTE_entry = NULL;                  break;
        case LTEDemoPath_TCP:       threadLTE_entry = NULL;                  break;
        case LTEDemoPath_MQTT:      threadLTE_entry = NULL;                  break;
        case LTEDemoPath_ONENET:    threadLTE_entry = lte_onenet_task_entry; break;
        case LTEDemoPath_ALIYUN:    threadLTE_entry = NULL;                  break;
        case LTEDemoPath_ALIYUN_GPS:threadLTE_entry = NULL;                  break;
    }

    threadLTE_entry();

    return (uwRet);
}

/**
 * @brief 创建工作测试
 * @param protocolType:连接方式
 * @return 返回工作状态
 */
uint32_t create_work_test( LTEAppDemoPath_t protocolType )
{
    uint32_t uwRet = 0;

    uwRet = lte_task(protocolType);
    if (uwRet != 0)
    {
        return (0);
    }

    return (uwRet);
}
/*=====================================================#######  END  #######=================================================*/

3.ecm800.h 主要是暴扣使用的一些头文件

#ifndef APPLICATIONS_EC800M_H_
#define APPLICATIONS_EC800M_H_

#include <rtthread.h>
#include <drv_common.h>
#include <string.h>
#include <stdio.h>

/**====================================================###### 宏定义 ######==================================================*/
#define IOT_CHECK_READY            "RDY"                // 就绪

#define IOT_ONNET_brokerAddress    "183.230.40.96"      // ONENET地址
#define IOT_ONNET_port             (1883)               // ONENET端口号
#define IOT_ONNET_cientId          "EC800M"             // 设备名称
#define IOT_ONNET_userName         "4S62S0ii0j"         // 产品ID
#define IOT_ONNET_password         "version=2018-10-31&res=products%2F4S62S0ii0j%2Fdevices%2FEC800M&et=2537256630&method=md5&sign=7afdqXTkvNhV%2F4k%2BFHis8Q%3D%3D"
#define IOT_ONNET_subtopic         "$sys/4S62S0ii0j/EC800M/thing/property/set"  // 设置主题
#define IOT_ONNET_pubtopic         "$sys/4S62S0ii0j/EC800M/thing/property/post" // 上传主题
#define IOT_MESSAGE                "{\"id\":\"20\",\"version\":\"1.0\",\"params\":{\"humidity\":{\"value\":%2.2f},\"temp\":{\"value\":%2.2f},\"key\":{\"value\":%s}}}"

#define CHECK_OK       1            // 校验通过
#define CHECK_FAILED   0            // 校验失败

#define MAX_AT_RESPONSE_LEN 512     // AT响应数据的最大长度
#define TIMEOUT_TIME_SEC    3       // 超时时间
/**====================================================#######  END  #######=================================================*/

/**====================================================### 全局变量定义 ####=================================================*/
#define IOT_UART_DEV_NAME  "uart2"

/* LTE通信模式 */
typedef enum
{
    LTEDemoPath_UDP,
    LTEDemoPath_TCP,
    LTEDemoPath_MQTT,
    LTEDemoPath_ONENET,
    LTEDemoPath_ALIYUN,
    LTEDemoPath_ALIYUN_GPS,

} LTEAppDemoPath_t;

typedef struct
{
    rt_device_t dev;            // 串口设备
    rt_sem_t sem;               // 创建信号量
    rt_thread_t thread;         // 创建线程
    rt_size_t rx_cnt;           // 接收计数
    rt_uint8_t check_flag;      // 校验标志位

    uint8_t mqttOpenOkFlage;    // 打开成功标志
    uint8_t mqttConnetOkFlage;  // 连接成功标志

    char *rx_data;              // 接收数据临时缓冲区
    char check_data[256];       // 校验数据
    char recv_data[512];        // 接收的有效数据
    char send_data[512];        // 发送数据缓冲器

} iot_t;

iot_t g_ec800m;
/**====================================================#######  END  #######=================================================*/

/**==================================================##### 函数及变量声明 #####==============================================*/
extern void Iot_Send_Data(const void *buffer, rt_size_t size);      // IOT发送数据
extern uint32_t create_work_test(LTEAppDemoPath_t protocolType);    // 创建工作测试
/**====================================================#######  END  #######=================================================*/

#endif /* APPLICATIONS_EC800M_H_ */

4.lte_at.c 主要是包括AT发送指令函数,发送指令,API接口函数
(1)AT发送函数:主要是用来发送每条AT指令,并且需要接收数据的返回,并且进行数据校验,校验成功才能正常进行。

#include "lte_at.h"

/*======================================================##### AT指令 #####==============================================*/
/**
 * @brief 发送函数
 * @param cmd        :发送的指令
 * @param ack        :应答数据
 * @param waittime   :超时时间
 * @param outResponse:接收数据缓冲区
 * @return AT_OK--校验通过   AT_FAILED--校验失败
 */
uint8_t LTEAT_sendCmd( char *cmd, char *ack, uint32_t waittime, uint8_t *outResponse )
{
    uint8_t  ret     = AT_OK;
    uint16_t sendLen = 0;

    g_ec800m.check_flag = 0;
    memset(g_ec800m.check_data, 0, sizeof(g_ec800m.check_data));
    memcpy(g_ec800m.check_data, ack, strlen(ack));
    memset(g_ec800m.send_data, 0, sizeof(g_ec800m.send_data));
    sendLen = sprintf(g_ec800m.send_data, "%s\r\n", cmd);
    /* 发送命令 */
    Iot_Send_Data(g_ec800m.send_data, sendLen);

    /* 需要等待应答 */
    if (ack && waittime)
    {
        /* 等待倒计时 */
        while (--waittime)
        {
            rt_thread_mdelay(1);

            /* 接收到期待的应答结果 */
            if (g_ec800m.check_flag == CHECK_OK)
            {
                g_ec800m.check_flag = 0;
                if (outResponse != NULL)
                {
                    memcpy(outResponse, g_ec800m.recv_data, strlen(g_ec800m.recv_data));
                    rt_kprintf("[%d][DATA]%s\n", waittime, outResponse);
                }
                else
                {
                    rt_kprintf("[%d][DATA]%s\n", waittime, g_ec800m.recv_data);
                }
                memset(g_ec800m.recv_data, 0, MAX_AT_RESPONSE_LEN);
                memset(g_ec800m.check_data, 0, sizeof(g_ec800m.check_data));
                break;
            }
        }

        /* 等待超时 */
        if (waittime == 0)
        {
            ret = AT_FAILED;
            memset(g_ec800m.recv_data, 0, MAX_AT_RESPONSE_LEN);
            memset(g_ec800m.rx_data, 0, MAX_AT_RESPONSE_LEN);
            memset(g_ec800m.check_data, 0, sizeof(g_ec800m.check_data));
        }
    }

    return ret;
}

/**
 * @brief AT指令发送函数调用
 * @param cmd     :指令
 * @param len     :指令长度
 * @param suffix  :校验字符串
 * @param resp_buf:接收缓冲区
 * @param resp_len:接收长度
 * @return AT_OK--校验通过   AT_FAILED--校验失败
 */
int32_t at_cmd(int8_t *cmd, int32_t len, const char *suffix, char *resp_buf, int* resp_len)
{
    int result;

    rt_kprintf("[SEND]%s\n", cmd);

    result = LTEAT_sendCmd((char *) cmd, (char *) suffix, AT_CMD_TIMEOUT, (uint8_t *) resp_buf);
    if (result == 0)
    {
        if (resp_len != NULL)
            *resp_len = strlen(resp_buf);
    }
    return (result);
}
/*=====================================================#######  END  #######=================================================*/

(2)发送指令:主要是包含所有的发送指令

/*======================================================##### 发送指令 #####=================================================*/
/**
 * @brief 等待准备就绪
 * @param waittime:超时时间(s)
 * @return AT_OK--校验通过   AT_FAILED--校验失败
 */
uint8_t Iot_Wait_Ready(uint32_t waittime)
{
    uint8_t ret = AT_FAILED;
    memcpy(g_ec800m.check_data, IOT_CHECK_READY, strlen(IOT_CHECK_READY));

    while (waittime--)
    {
        if (g_ec800m.check_flag == CHECK_OK)
        {
            g_ec800m.check_flag = CHECK_FAILED;
            ret = AT_OK;
            memset(g_ec800m.recv_data, 0, MAX_AT_RESPONSE_LEN);
            rt_kprintf("[INFO]Check Passed And Ready\r\n");
            break;
        }
        rt_thread_delay(1);
    }

    /* 等待超时 */
    if (waittime == 0)
    {
        ret = AT_FAILED;
        memset(g_ec800m.recv_data, 0, MAX_AT_RESPONSE_LEN);
        memset(g_ec800m.rx_data, 0, MAX_AT_RESPONSE_LEN);
        memset(g_ec800m.check_data, 0, sizeof(g_ec800m.check_data));
    }

    return ret;
}

/**
 * @brief 模块复位
 * @return AT_OK--校验通过   AT_FAILED--校验失败
 */
int32_t LTE_pwrdown( void )
{
    int32_t ret = 0;

    ret = at_cmd((int8_t *) AT_IOT_QPOWD, strlen(AT_IOT_QPOWD), "OK", NULL, NULL);
    if (ret == 0)
    {
        rt_kprintf("[INFO]Restart Response Success\r\n");
    }
    else
    {
        rt_kprintf("[INFO]Restart Response Fail\r\n");
    }

    return (ret);
}

/**
 * @brief 状态查询
 * @return AT_OK--校验通过   AT_FAILED--校验失败
 */
int32_t LTE_checkDevice(void)
{
    int32_t ret = 0;

    ret = at_cmd((int8_t *) AT_IOT_AT, strlen(AT_IOT_AT), "OK", NULL, NULL);
    if (ret == 0)
    {
        rt_kprintf("[INFO]Reboot Response Success\r\n");
    }
    else
    {
        rt_kprintf("[INFO]Reboot Response Fail\r\n");
    }

    return (ret);
}

/**
 * @brief 设置回显
 * @param enable:使能状态
 * @return AT_OK--校验通过   AT_FAILED--校验失败
 */
int32_t LTE_setATI(uint8_t enable)
{
    int ret;
    char buf[64] = {0};

    if (enable == 0)
    {
        ret = at_cmd((int8_t *) AT_IOT_ATE0, strlen(AT_IOT_ATE0), "OK", NULL, NULL);
        if (ret == 0)
        {
            rt_kprintf("[INFO]Close ECHO Success\r\n");
        }
        else
        {
            rt_kprintf("[INFO]Close ECHO Fail\r\n");
        }
    }
    else
    {
        ret = at_cmd((int8_t *) AT_IOT_ATE1, strlen(buf), "OK", NULL, NULL);
        if (ret == 0)
        {
            rt_kprintf("[INFO]Open ECHO Success\r\n");
        }
        else
        {
            rt_kprintf("[INFO]Open ECHO Fail\r\n");
        }
    }

    return (ret);
}

/**
 * @brief 查询卡号
 * @return AT_OK--校验通过   AT_FAILED--校验失败
 */
int32_t LTE_getCimi(void)
{
    char recvBuf[64];
    int  recvLen;
    int  ret;

    ret = at_cmd((int8_t *) AT_IOT_CIMI, strlen(AT_IOT_CIMI), "OK", recvBuf, &recvLen);
    if (recvLen < 64)
    {
        recvBuf[recvLen] = '\0';
//        rt_kprintf("[INFO]CIMI:%s", recvBuf);
    }

    return (ret);
}

/**
 * @brief 查询设备识别码
 * @return AT_OK--校验通过   AT_FAILED--校验失败
 */
int32_t LTE_getCGSN(void)
{
    char recvBuf[64];
    int  recvLen;
    int  ret;

    ret = at_cmd((int8_t *) AT_IOT_CGSN, strlen( AT_IOT_CGSN), "OK", recvBuf, &recvLen);
    if (recvLen < 64)
    {
        recvBuf[recvLen] = '\0';
//        rt_kprintf("[INFO]CGSN:%s", recvBuf);
    }
    return (ret);
}

/**
 * @brief 查询当前 PS 域状态
 * @return AT_OK--校验通过   AT_FAILED--校验失败
 */
int32_t LTE_netstat(void)
{
    char *cmd = "AT+CGATT?";
    return (at_cmd((int8_t *) cmd, strlen(cmd), "CGATT: 1", NULL, NULL));
}

/**
 * @brief 查询信号强度
 * @return AT_OK--校验通过   AT_FAILED--校验失败
 */
int32_t LTE_checkCsq(void)
{
    char *cmd = "AT+CSQ";
    return (at_cmd((int8_t *) cmd, strlen(cmd), "+CSQ:", NULL, NULL));
}

/**
 * @brief 查询网络注册状态
 * @return AT_OK--校验通过   AT_FAILED--校验失败
 */
int32_t LTE_getCereg(void)
{
    char recvBuf[64];
    int recvLen;
    int ret;

    ret = at_cmd((int8_t *) AT_IOT_CEREG, strlen( AT_IOT_CEREG), "0,1", recvBuf, &recvLen);
    if (recvLen < 64)
    {
        recvBuf[recvLen] = '\0';
//        printf("[INFO]Cereg:%s\r\n", recvBuf);
    }

    return (ret);
}

/**
 * @brief 显示 PDP 地址
 * @return AT_OK--校验通过   AT_FAILED--校验失败
 */
int32_t LTE_queryIp(void)
{
    char *cmd = "AT+CGPADDR=1";
    return (at_cmd((int8_t *) cmd, strlen(cmd), "+CGPADDR", NULL, NULL));
}

/**
 * @brief 发送AT指令打开MQTT通道
 * @param brokerAddress:地址
 * @param port         :端口号
 * @param protocolVer  :版本信息
 * @return AT_OK--校验通过   AT_FAILED--校验失败
 * @note AT+QMTOPEN=0,123.207.210.43,1883
 */
int32_t LTE_EC800_mqttOpen(char* brokerAddress, uint16_t port, char* protocolVer)
{
    int  ret;
    char buf[256];

    // 配置协议版本信息
    if (protocolVer != NULL && strstr(protocolVer, "3.1.1") != NULL)
    {
        snprintf(buf, sizeof(buf), "AT+QMTCFG=\"version\",0,4");
        ret = at_cmd((int8_t *) buf, strlen(buf), "OK", NULL, NULL);
        if (ret < 0)
        {
            rt_kprintf("[ERROR]QMTCFG ERROR\n");
            return (ret);
        }
    }

    // 打开MQTT通道
    snprintf(buf, sizeof(buf), "AT+QMTOPEN=0,%s,%d", brokerAddress, port);
    ret = at_cmd((int8_t *) buf, strlen(buf), "+QMTOPEN: 0,0", NULL, NULL);
    if (ret < 0)
    {
        rt_kprintf("[ERROR]MQTTOPEN ERROR\n");
        return (ret);
    }

    return (AT_OK);
}

/**
 * @brief 设置连接用户信息
 * @param clientID:连接ID
 * @param userName:用户名
 * @param password:用户密码
 * @return AT_OK--校验通过   AT_FAILED--校验失败
 * @note AT+QMTCONN=0,"clientExample"
 */
int32_t LTE_EC800_mqttConnet(char *clientID, char *userName, char *password)
{
    int  ret;
    char buf[256];

    if (userName != NULL && password != NULL)
    {
        snprintf(buf, sizeof(buf), "AT+QMTCONN=0,%s,%s,%s", clientID, userName, password);
    }
    else
    {
        snprintf(buf, sizeof(buf), "AT+QMTCONN=0,%s", clientID);
    }

    ret = at_cmd((int8_t *) buf, strlen(buf), "+QMTCONN: 0,0,0", NULL, NULL);
    if (ret < 0)
    {
        printf("[ERROR]MQTTCON ERROR\n");
        return (ret);
    }
    return (AT_OK);
}

/**
 * @brief 订阅MQTT消息
 * @param topic:主题
 * @return AT_OK--校验通过   AT_FAILED--校验失败
 * @note AT+QMTSUB=0,1,"topic/example_8808",0
 */
int32_t LTE_EC800_mqttSub(char* topic)
{
    int ret;
    char wbuf[256];

    snprintf(wbuf, sizeof(wbuf), "AT+QMTSUB=0,1,\"%s\",0", topic);
    ret = at_cmd((int8_t *) wbuf, strlen(wbuf), "+QMTSUB: 0,1,0,0", NULL, NULL);
    if (ret < 0)
    {
        return (ret);
    }

    return (ret);
}

/**
 * @brief 发布MQTT消息、用于长数据发送
 * @param topic :主题
 * @param msg   :数据
 * @param msgLen:数据长度
 * @return AT_OK--校验通过   AT_FAILED--校验失败
 */
int32_t LTE_EC800_mqttPubEX(char* topic, char* msg, uint32_t msgLen)
{
    int  ret;
    char wbuf[256];

    snprintf(wbuf, sizeof(wbuf), "AT+QMTPUBEX=0,0,0,0,\"%s\",%d", topic, strlen(msg));
    ret = at_cmd((int8_t *) wbuf, strlen(wbuf), ">", NULL, NULL);
    if (ret != AT_OK)
    {
        return (ret);
    }

    snprintf(wbuf, sizeof(wbuf), "%s", msg);
    ret = at_cmd((int8_t *) wbuf, strlen(wbuf), "+QMTPUBEX: 0,0,0", NULL, NULL);
    if (ret != AT_OK)
    {
        return (ret);
    }

    return (ret);
}
/*=====================================================#######  END  #######=================================================*/

(3)API函数:主要是用来对接初始化或者数据上报流程的函数

/*=====================================================##### API函数 #####==============================================*/
/**
 * @brief 模块上电通用初始化
 * @return AT_OK--校验通过   AT_FAILED--校验失败
 */
static int lte_comm_init(void)
{
    int ret;
    int timecnt = 0;

    /* 等待就绪 */
    ret = Iot_Wait_Ready(POWER_ON_WAIT_TIME);
    if (ret == AT_FAILED)
    {
        rt_kprintf("[INFO]Restart IOT Module...\n");
        /* 复位模块 */
        for (int i = 0; i < 3; ++i)
        {
            ret = LTE_pwrdown();
            if (ret == AT_OK)
            {
                Iot_Wait_Ready(10000);
                break;
            }
        }

        if (ret == AT_FAILED)
        {
            return ret;
        }
    }
    /* 延时缓冲 */
    rt_thread_delay(2000);

    /* 状态查询 */
    ret = LTE_checkDevice();

    /* 关闭回显 */
    LTE_setATI(0);

    /* 查询卡号、设备识别码信息 */
    while (1)
    {
        ret = AT_OK;
        ret |= LTE_getCimi();
        ret |= LTE_getCGSN();

        if (ret == AT_OK)
            break;
        rt_thread_delay(500);
    }

    /* 延时缓冲 */
    rt_thread_delay(2000);
    /* 等待注网成功 */
    while (timecnt < 10)
    {
        ret = LTE_netstat();
        rt_kprintf("[INFO]Waiting for join network\n");
        ret = LTE_checkCsq();
        rt_kprintf("[INFO]LTE_checkCsq\n");
        ret = LTE_getCereg();
        rt_kprintf("[INFO]LTE_getCereg\n");
        ret |= LTE_queryIp();
        rt_kprintf("[INFO]LTE_queryIp\n");

        if (ret == AT_OK)
        {
            break;
        }

        rt_thread_delay(500);
        timecnt++;
    }

    rt_kprintf("[INFO]Join network success!\r\n");

    return (ret);
}

/**
 * @brief MQTT通信方式下,模块初始化
 * @return 返回初始化状态
 */
int lte_mqtt_init(void)
{
    int ret;
    ret = lte_comm_init();
    return (ret);
}

/**
 * @brief 打开MQTT通道
 * @param brokerAddress:地址
 * @param port         :端口号
 * @param protocolVer  :版本信息
 * @return AT_OK--校验通过   AT_FAILED--校验失败
 */
int lte_mqtt_open(char * brokerAddress, uint16_t port, char* protocolVer)
{
    int ret;
    ret = LTE_EC800_mqttOpen(brokerAddress, port, protocolVer);

    if (ret == AT_OK)
        rt_kprintf("[INFO]LTE_EC800_mqtt open\r\n");

    return (ret);
}

/**
 * @brief MQTT通信方式下,模块连接
 * @param clientID:
 * @param userName:
 * @param password:
 * @return AT_OK--校验通过   AT_FAILED--校验失败
 */
int lte_mqtt_conn(char *clientID, char *userName, char *password)
{
    int ret;

    ret = LTE_EC800_mqttConnet(clientID, userName, password);
    if (ret == AT_OK)
        rt_kprintf("[INFO]LTE_EC800_mqtt set config success\r\n");

    return (ret);
}

/**
 * @brief MQTT通信方式下,模块订阅消息
 * @param topic:主题
 * @return AT_OK--校验通过   AT_FAILED--校验失败
 */
int lte_mqtt_sub(char* topic)
{
    int ret;

    ret = LTE_EC800_mqttSub(topic);
    if (ret == AT_OK)
        rt_kprintf("[INFO]lte_mqtt_sub sub success\r\n");

    return (ret);
}

/**
 * @brief MQTT通信方式下,模块上报数据
 * @param topic:主题
 * @param buf  :数据缓冲区
 * @return AT_OK--校验通过   AT_FAILED--校验失败
 */
int lte_mqtt_pubex(char* topic, char* buf)
{
    return (LTE_EC800_mqttPubEX(topic, buf, strlen(buf)));
}
/*=====================================================#######  END  #######=================================================*/

5.lte_at.h 主要是AT指令所用的宏定义

#ifndef APPLICATIONS_INC_LTE_AT_H_
#define APPLICATIONS_INC_LTE_AT_H_

#include <rtthread.h>
#include <drv_common.h>
#include "ec800m.h"

/**=====================================================###### 宏定义 ######=================================================*/
#define AT_OK              0            // AT成功
#define AT_FAILED          1            // AT失败

#define POWER_ON_WAIT_TIME 3000         // 上电等待时间
#define RESTART_WAIT_TIME  8000         // 重启等待时间
#define AT_CMD_TIMEOUT     1000         // AT指令超时时间

#define AT_IOT_AT          "AT"                 // 模块状态
#define AT_IOT_ATE0        "ATE0"               // 关闭回显
#define AT_IOT_ATE1        "ATE1"               // 打开回显
#define AT_IOT_QPOWD       "AT+QPOWD"           // 模块复位
#define AT_IOT_CIMI        "AT+CIMI"            // 查询卡号
#define AT_IOT_CGSN        "AT+CGSN=1"          // 查询设备识别码
#define AT_IOT_CEREG       "AT+CEREG?"          // 查询网络注册状态

#define DELAY_us(x)        rt_hw_us_delay(x)    // us延时函数

/**====================================================#######  END  #######=================================================*/

/**==================================================##### 函数及变量声明 #####==============================================*/
extern void lte_onenet_dataIoctl(const char *buf, unsigned long size);              // 数据处理函数
extern int lte_mqtt_init(void);                                                     // MQTT通信方式下,模块初始化
extern int lte_mqtt_open(char * brokerAddress, uint16_t port, char* protocolVer);   // 打开MQTT通道
extern int lte_mqtt_conn(char *clientID, char *userName, char *password);           // MQTT通信方式下,模块连接
extern int lte_mqtt_sub(char* topic);                                               // MQTT通信方式下,模块订阅消息
extern int lte_mqtt_pubex(char* topic, char* buf);                                  // MQTT通信方式下,模块上报数据
/**====================================================#######  END  #######=================================================*/

#endif /* APPLICATIONS_INC_LTE_AT_H_ */

五、测试验证

  通过验证初始化过程中需要合理使用延时函数,因为有时候会初始化失败,需要等待时间才能初始化完成,实际测试的效果如下所示,测试的结果可证将数据正常上报,并且每个初始化的数据也都和实际过程中接收的数据一致,所以测试成功。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述



http://www.kler.cn/a/455918.html

相关文章:

  • Oracle 11G还有新BUG?ORACLE 表空间迷案!
  • SQL-leetcode-180. 连续出现的数字
  • 【ETCD】【实操篇(十五)】etcd集群成员管理:如何高效地添加、删除与更新节点
  • STM32 I2C通信协议
  • Python einops库介绍
  • .NET周刊【12月第3期 2024-12-15】
  • 记录使用uim4的自定义头部
  • 黑马点评——基于Redis
  • Java全栈项目-校园公告管理系统开发实践
  • Kubernetes 安装 Nginx以及配置自动补全
  • SQL 表连接操作
  • 0055. shell命令--useradd
  • 【知识】cuda检测GPU是否支持P2P通信及一些注意事项
  • day20-yum精讲
  • Unity3D如何优化物理模拟?
  • Please refer to dump files (if any exist) [date].dump, [date]-jvmRun[N]……解决
  • springboot+mybatis的整合
  • 力扣矩阵-算法模版总结
  • LaTeX 是一种基于标记的排版系统,广泛用于创建高质量的文档,特别是在需要复杂数学公式、表格、文献引用等的场景中
  • 源码分析之Openlayers中GeometryCollection类
  • S5700 V600R024C00 配置指南-工业网络 PROFINET
  • docker项目部署流程(Vue+Spingboot)
  • this的指向问题
  • JVM 及内存管理:掌握 Java 8 的内存模型与垃圾回收机制
  • 【Spring】基于XML的Spring容器配置——FactoryBean的使用
  • 【MySQL】SQL 优化经验