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