【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_ */

五、测试验证

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

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


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/944059.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

STM32完全学习——FATFS0.15移植SD卡

一、下载FATFS源码 大家都知道使用CubMAX可以很快的将&#xff0c;FATFS文件管理系统移植到单片机上&#xff0c;但是别的芯片没有这么好用的工具&#xff0c;就需要自己从官网下载源码进行移植。我们首先解决SD卡的驱动问题&#xff0c;然后再移植FATFS文件管理系统。 二、SD…

【知识】cuda检测GPU是否支持P2P通信及一些注意事项

转载请注明出处&#xff1a;小锋学长生活大爆炸[xfxuezhagn.cn] 如果本文帮助到了你&#xff0c;欢迎[点赞、收藏、关注]哦~ 代码流程 先检查所有GPU之间是否支持P2P通信&#xff1b;然后尝试启用GPU之间的P2P通信&#xff1b;再次检查所有GPU之间是否支持P2P通信。 test.cu&…

Mysql大数据量表分页查询性能优化

一、模拟场景 1、产品表t_product,数据量500万+ 2、未做任何优化前,cout查询时间大约4秒;LIMIT offset, count 时,offset 值较大时查询时间越久。 count查询 SELECT COUNT(*) AS total FROM t_product WHERE deleted = 0 AND tenant_id = 1 分页查询 SELECT * FROM t_…

go语言的成神之路-筑基篇-对文件的操作

目录 一、对文件的读写 Reader?接口 ?Writer接口 copy接口 bufio的使用 ioutil库? 二、cat命令 三、包 1. 包的声明 2. 导入包 3. 包的可见性 4. 包的初始化 5. 标准库包 6. 第三方包 ?7. 包的组织 8. 包的别名 9. 包的路径 10. 包的版本管理 四、go mo…

Qt 应用程序转换为服务

一、在 Windows 上将 Qt 应用程序转换为服务 方法1&#xff1a; 创建一个 Windows 服务应用程序&#xff1a; Windows 服务应用程序是一个没有用户界面的后台进程&#xff0c;通常由 Win32 Service 模板创建&#xff0c;或者直接编写 main() 函数以实现服务逻辑。 修改 Qt 应…

【HarmonyOS之旅】HarmonyOS概述(一)

目录 1 -> HarmonyOS简介 2 -> HarmonyOS发展历程 3 -> HarmonyOS技术特性 3.1 -> 硬件互助&#xff0c;资源共享 3.1.1 -> 分布式软总线 3.1.2 -> 分布式设备虚拟化 3.1.3 -> 分布式数据管理 3.1.4 -> 分布式任务调度 3.1.5 -> 分布式连接…

5、栈应用-表达式求值

本章内容使用上述栈结构函数&#xff0c;来完成表达式求值操作。 表达式例如&#xff1a;3*(7-2) 或者 (0-12)*((5-3)*32)/(22) 。 1、实现思路 a、建立OPTR&#xff08;运算符&#xff09;和OPND&#xff08;数字&#xff09;两个栈&#xff0c;后输入字符串以结束 b、自左向…

【软件】教务系统成绩提交工具使用步骤

【软件】教务系统成绩提交工具使用步骤 零、快速开始 安装 与大多数软件一样&#xff0c;安装步骤很简单&#xff0c;一直点击“下一步”即可快速完成安装&#xff0c;安装完成后&#xff0c;在桌面会有一个软件图标&#xff0c;双击即可打开软件主界面。 导入成绩到Excel中…

书签管理工具的使用技巧

分类与筛选技巧 多层级分类&#xff1a;创建多层级的文件夹结构&#xff0c;如先按大的主题分类&#xff0c;再在每个主题下细分小类。例如&#xff0c;先创建 “工作”“学习”“生活” 等大文件夹&#xff0c;在 “工作” 文件夹下再细分 “项目文档”“办公软件”“行业资讯…

如何安全获取股票实时数据API并在服务器运行?

以下是安全获取股票实时数据 API 并在服务器运行的方法&#xff1a; 选择合适的券商或交易平台 评估自身需求&#xff1a;明确自己的交易策略、交易品种、交易频率等需求&#xff0c;以及对 股票api 的功能、性能、稳定性等方面的要求。调研券商或平台&#xff1a;了解不同券商…

图像处理-Ch2-空间域的图像增强

Ch2 空间域的图像增强 文章目录 Ch2 空间域的图像增强Background灰度变换函数(Gray-level Transformation)对数变换(Logarithmic)幂律变换(Power-Law)分段线性变换函数(Piecewise-Linear)对比度拉伸(Contrast-Stretching)灰度级分层(Gray-level Slicing) 直方图处理(Histogram …

告别 Shuffle!深入探索 Spark 的 SPJ 技术

随着 Spark > 3.3&#xff08;在 3.4 中更加成熟&#xff09;中引入的存储分区连接&#xff08;Storage Partition Join&#xff0c;SPJ&#xff09;优化技术&#xff0c;您可以在不触发 Shuffle 的情况下对分区的数据源 V2 表执行连接操作&#xff08;当然&#xff0c;需要…

解决Springboot整合Shiro自定义SessionDAO+Redis管理会话,登录后不跳转首页

解决Springboot整合Shiro自定义SessionDAORedis管理会话&#xff0c;登录后不跳转首页 问题发现问题解决 问题发现 在Shiro框架中&#xff0c;SessionDAO的默认实现是MemorySessionDAO。它内部维护了一个ConcurrentMap来保存session数据&#xff0c;即将session数据缓存在内存…

【蓝桥杯——物联网设计与开发】基础模块8 - RTC

目录 一、RTC &#xff08;1&#xff09;资源介绍 &#x1f505;简介 &#x1f505;时钟与分频&#xff08;十分重要‼️&#xff09; &#xff08;2&#xff09;STM32CubeMX 软件配置 &#xff08;3&#xff09;代码编写 &#xff08;4&#xff09;实验现象 二、RTC接口…

API安全学习笔记

必要性 前后端分离已经成为web的一大趋势&#xff0c;通过TomcatNgnix(也可以中间有个Node.js)&#xff0c;有效地进行解耦。并且前后端分离会为以后的大型分布式架构、弹性计算架构、微服务架构、多端化服务&#xff08;多种客户端&#xff0c;例如&#xff1a;浏览器&#x…

2011-2020年各省城镇职工基本医疗保险年末参保人数数据

2011-2020年各省城镇职工基本医疗保险年末参保人数数据 1、时间&#xff1a;2011-2020年 2、来源&#xff1a;国家统计局 3、指标&#xff1a;省份、时间、城镇职工基本医疗保险年末参保人数 4、范围&#xff1a;31省 5、指标解释&#xff1a;参保人数指报告期末按国家有关…

【蓝桥杯——物联网设计与开发】拓展模块4 - 脉冲模块

目录 一、脉冲模块 &#xff08;1&#xff09;资源介绍 &#x1f505;原理图 &#x1f505;采集原理 &#xff08;2&#xff09;STM32CubeMX 软件配置 &#xff08;3&#xff09;代码编写 &#xff08;4&#xff09;实验现象 二、脉冲模块接口函数封装 三、踩坑日记 &a…

Kubernetes Gateway API-2-跨命名空间路由

1 跨命名空间路由 Gateway API 具有跨命名空间路由的核心支持。当多个用户或团队共享底层网络基础设施时,这很有用,但必须对控制和配置进行分段,以尽量减少访问和容错域。 Gateway 和 Route(HTTPRoute,TCPRoute,GRPCRoute) 可以部署到不同的命名空间中,路由可以跨命名空间…

Windows Powershell实战指南(未完成)

目前只作简单了解&#xff0c;开始吧。 一、初识Powershell 目标 初步认识 Powershell和其集成环境 Ise&#xff0c;学会基本设置 实验 我们从简单的例子开始&#xff1a;希望你能从控制台和ISE的配置中实现相同的结果。然后按照下面五步进行。 &#xff08;1&#xff09;选…

Android着色器SweepGradient渐变圆环,Kotlin

Android着色器SweepGradient渐变圆环&#xff0c;Kotlin import android.content.Context import android.graphics.Canvas import android.graphics.Color import android.graphics.Paint import android.graphics.Path import android.graphics.SweepGradient import android…