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