最近在搞GPRS模块,SIM800C,具体流程是:GPRS先连接TCP服务器,再连接阿里云的MQTT物联网套件,上传给服务器一些消息,服务器收到以后再回复一些指令,或者可以通过手机微信小程序主动下发一些指令给设备。
代码是基于RTthread写的,STM32F103,系统tick 1000,标准库。
分为几个部分:
GPRS.c //SIM800C连接TCP服务器以及MQTT服务器过程
MQTTClient.c //MQTT的应用层接口函数
/**
* @file GPRS.c
* @author yufei_qf@foxmail.com
*/
#include "GPRS.h"
#define AT_RESP_END_OK "OK"
#define AT_RESP_END_ERROR "ERROR"
#define GPRS_CMD_AT "AT\r\n" //1.AT
#define GPRS_CMD_ATE0 "ATE0\r\n" //2.关闭回显
#define GPRS_CMD_AT_CPIN "AT+CPIN?\r\n" //3.AT+CPIN? (查看SIM卡的状态)回车,返回:+CPIN:READY OK(正常)。
#define GPRS_CMD_AT_CREG "AT+CREG?\r\n" //4.网络注册及状态查询; 0, 1, 2, 3, 4, 5 ,
// 其中 0 代表没有注册网络同时模块没有找到运营商,
// 1 代注册到了本地网络, 2 代表找到运营商但没有注册网络,
// 3 代表注册被拒绝, 4 代表未知的数据, 5代表注册在漫游状态.
#define GPRS_CMD_AT_IFC "AT+IFC=0,0\r\n" //5.本地流控, 无流控, 2,2: 硬件流控 1,1 软件流控
#define GPRS_CMD_AT_CIPMODE "AT+CIPMODE=1\r\n" //6.设置为透传模式
#define GPRS_CMD_AT_CGATT "AT+CGATT?\r\n" //7. 查询是否成功附着了GPRS网络
#define GPRS_CMD_AT_CGATT_1 "AT+CGATT=1\r\n" // 模块开机后首先查询模块是否附着GPRS网络,不要主动设置AT+CGATT=1,除非待机状态
// 下主动上报+CGATT: 0,此时可以设置AT+CGATT=1,否则不要设置。
#define GPRS_CMD_AT_CSTT "AT+CSTT\r\n" //8. 设置APN,使用默认的APN
#define GPRS_CMD_AT_CIICR "AT+CIICR\r\n" //9. 激活移动场景激活移动场景模块超时设置40s,如果40s还没有激活成功,模块端会自动
//上报+PDP:DEACT。如果前面CSQ、CREG、CGATT、AT+CSTT状态都
//是正常的,移动场景激活失败,那肯定是网络侧原因所致。当然CSQ值过
//低,比如10以下,移动场景激活成功概率较低。
//如果移动场景激活失败,执行AT+CIPSHUT(关闭移动场景),模块设置超时时间20s。
#define GPRS_CMD_AT_CIFSR "AT+CIFSR\r\n" //10.获取本地IP,若指令正确,模块将返回随机IP。
//必须有这一步才能使IP STATE变成IP STATUS,后续去建立具体的连接时才能成功。
#define GPRS_CMD_AT_CIPSTATUS "AT+CIPSTATUS\r\n" //11.查看模块的IP STATE是什么状态。
#define MAX_ERR_Times 4
#define MAX_WAIT_TIME 3000 //3s
#define MAX_READ_TIME 5000
#define GPRS_DEVICE_NAME "uart4"
#define GPRS_buff_size_max 1024
typedef struct {
rt_device_t dev; //设备描述符
struct rt_semaphore r_tx; //读串口信号量
char buff[GPRS_buff_size_max];
unsigned short buff_i;
unsigned char errtime;
}GPRS_DEV ;
static GPRS_DEV GprsDev;
ALIGN(RT_ALIGN_SIZE)
static rt_uint8_t GPRS_stack[ 1024 * 2 ];
static struct rt_thread GPRS_thread;
rt_err_t GPRS_Poweron(void);
rt_err_t GPRS_config(void);
/*
串口接收的回调函数
函数中置信号量
串口一旦接收到了数据, 就调用该函数.
*/
static rt_err_t GPRS_rx_ind(rt_device_t dev, rt_size_t size)
{
rt_sem_release(&(GprsDev.r_tx));
return RT_EOK;
}
/*
* 读取数据知道遇到"\r\n"然后再解析收到的数据中是否具有我们想要的包
* 最大等待时间maxtime,
* 返回值:
* 0: 读取超时;
* 1: 读取成功
* -1: 读取失败,CRC错误.
*/
int GPRS_waitRead(int maxtime, char *str1, char *str2)
{
rt_tick_t timeout_tick = 0;
char ch = 0, last_ch = 0;
rt_err_t ret;
RT_ASSERT(GprsDev.dev);
timeout_tick = rt_tick_get(); //rt_tick_get 获取当前tick.
/* 清空接收参数.,做接收准备 */
GprsDev.buff_i = 0;
memset(GprsDev.buff, 0, GPRS_buff_size_max);
while(1)
{
if (rt_tick_get() - maxtime > timeout_tick)
{
GprsDev.errtime++;
return 0; //读取超时
}
/* 等待信号量(信号量在回调函数中被release.) */
if ((ret = rt_sem_take(&GprsDev.r_tx, 1000)) != RT_EOK)
{
rt_kprintf("[GPRS]: Wait Serial sem timeout. reply:%s\r\n", ret);
continue;
}
/* read one character from device */
while (rt_device_read(GprsDev.dev, 0, &ch, 1) == 1)
{
if (GprsDev.buff_i < GPRS_buff_size_max)
{
GprsDev.buff[GprsDev.buff_i++] = ch;
}
else
{
/* 检查是否具有我们想要的数 */
if (str1)
{
if (strstr(GprsDev.buff, str1) != 0)
return 1;
}
if (str2)
{
if (strstr(GprsDev.buff, str2) != 0)
return 1;
}
return -1;
}
if ((ch == '\n' && last_ch == '\r'))
{
//检查是否具有我们想要的数据
if (str1)
{
if (strstr(GprsDev.buff, str1) != 0)
return 1;
}
if (str2)
{
if (strstr(GprsDev.buff, str2) != 0)
return 1;
}
}
last_ch = ch;
}
rt_thread_delay(100);
}
}
/**
* @brief GPRS_TCP_Response_Check透传模式下连接TCP的反馈结果判断.
* @return
* 0: 没有收到消息
* 1: 收到了连接成功的消息
* -1: 收到了错误消息, 命令发送的错误
* -2: 收到了服务器关闭的消息.
*/
int GPRS_TCP_Response_Check()
{
if (strstr(GprsDev.buff, AT_RESP_END_OK) != 0)
{
if (strstr(GprsDev.buff, "TCP CLOSED\r\n") != 0 || strstr(GprsDev.buff, "CONNECT FAIL\r\n") != 0)
{
rt_kprintf("[GPRS]: Connect Server Fail:%s\r\n", GprsDev.buff);
return -1;
}
else if (strstr(GprsDev.buff, "CONNECT\r\n") != 0)
{
rt_kprintf("[GPRS]: Connect Server Sucess:%s\r\n", GprsDev.buff);
return 1;
}
}
else if(strstr(GprsDev.buff, "ALREAD