写点什么

STM32+ 移远 MC20 模块采用 MQTT 协议登录 OneNet 上传 GPS 数据

作者:DS小龙哥
  • 2022 年 8 月 07 日
  • 本文字数:5733 字

    阅读完需:约 19 分钟

这篇文章介绍,利用 STM32+移远 MC20 模块对接 OneNet 平台的步骤,采用 MQTT 协议登录 OneNet 上传 GPS 数据,演示移远 MC20 模块基本使用方法,详细介绍 onenet 物联网平台的配置流程等。

一、环境介绍

MCU: STM32F103C8T6


GSM 模块: 移远 MC20 (MT2503D)(GSM+GPS 共存)功能很强大


开发软件: Keil5


MQTT 协议采用 OneNet 的旧版协议,登录 OneNet 控制台创建应用时要选择旧版本。


二、MC20 模块




MC20 模块采用联发科技最新推出的多功能通信定位芯片研制而成。它是一款集成 LCC 封装、四频段 GSM/GPRS 和先进算法 GNSS 引擎于一体的全功能通信模块,具有超小体积、低功耗、双卡单待等优势。MC20 不仅内嵌丰富的网络协议(如 TCP、UDP、PPP、FTP、HTTP 以及 SSL),还集成了多星座卫星系统(如北斗、GPS、QZSS),因此能提供无线移动通信以及精准的导航定位功能。


除具备 GSM/GPRS 无线通信功能外,MC20 模块还支持先进的 GNSS 技术。它集成了 EPOTM(用户无需自设服务器,直接从 MTK 服务器获取 EPO 数据)、秒定等技术,能够实现快速首次定位。由于支持北斗、GPS、QZSS 等多星座卫星系统解调算法,其定位更加精准,抗多路径干扰能力更强,比传统 GPS 模块具有更多优势。另外,MC20 模块中内置 LNA 和低功耗算法:前者使其接收灵敏度提升至-149dBm;后者使其在低功耗模式(GLP Mode)下的耗流仅为正常工作模式的 40%。


MC20 模块较传统 GSM+GNSS 方案体积减少 40%,使其在各种应用中占具更大优势。其主要应用领域为:可穿戴设备(智能手表)、宠物追踪、财产追踪及行车记录仪等等。主要优势● 超小体积: 18.7mm × 16.0mm × 2.1mm● 多卫星导航系统: GPS/BeiDou/QZSS● GNSS 接收机通道: 99 路捕获通道/33 路跟踪通道● 支持多种 AGPS 技术,如 EASYTM 、EPOTM 、秒定等● 内置 LNA 大大提升 GNSS 接收机灵敏度(-167dBm@跟踪模式):可使用无源 GNSS 天线而无需任何外部低噪声放大器● 支持增强型 GNSS 功能,如 SDK 命令、LOCUSTM 、AIC 和 GLP● 多功能四频段 GSM 模块: 850/900/1800/1900MHz● 内嵌丰富网络协议: TCP/UDP/PPP/HTTP/FTP/SSL● 支持语音、短信、QuecFOTATM 、双卡单待以及 OpenCPU 功能● 支持蓝牙 V3.0 以及 SPP & HFP-AG 配置文件

三、代码功能

使用 STM32F103C8T6 通过串口+AT 指令控制 MC20 模块+MQTT 协议,登录 OneNet 服务器上传 GPS 数据,LED 控制(网页按钮控制开发板上的 LED 灯)。

四、核心代码

4.1 main.c

#include "stm32f10x.h"#include "beep.h"#include "delay.h"#include "led.h"#include "sys.h"#include "usart.h"#include <string.h>#include <stdio.h>#include "timer.h"#include "mc20.h"
//网络协议层#include "onenet.h"
//协议封装文件#include "dStream.h"
//产品IDchar PROID[]="231174";
//鉴权信息char AUTH_INFO[]="1234567890";
//设备IDchar DEVID[]="523369555";
//API KEYchar API_KEY[]="k6vtrrEd1H7UMddiF3DzripS47w=";
//缓冲区char onenet_http_cmd[1024];
//服务器IP地址#define TCP_SERVER_IP_ADDR "183.230.40.39"
//服务器端口号#define TCP_SERVER_PORT 6002

//数据流结构DATA_STREAM data_stream[1]={ {"gps","88.88",TYPE_JSON,1},};

/* STM32开发板接线说明: STM32 MC20 3.3V ------> V_IO GND <-----> GND PA3 <------ GSM_TX PA2 ------> GSM_RX*/
int main(){ u32 time_cnt=0; u32 cnt=0; double Longitude; //经度 double latitude; //纬度 LED_Init(); BEEP_Init(); USART_X_Init(USART1,72,115200); TIM2_Init(72,20000); //辅助串口2接收,超时时间为20ms USART_X_Init(USART2,36,9600); //连接着MC20(GPS+GPRS) printf("串口准备就绪.....\r\n"); DelayMs(500); printf("程序修改时间: %s\r\n",__TIME__); while(1) { u8 stat; /*初始化MC20,并连接到指定服务器*/ MC20_InitConnect(TCP_SERVER_IP_ADDR,TCP_SERVER_PORT); /*登录OneNET服务器,上线设备*/ stat=OneNet_DevLink(); if(stat)printf("ERROR:%d,接入OneNET失败:%d\r\n",stat,cnt++); else break; //登录成功 LED1=!LED1; delay_ms(200); break;//失败也退出继续运行下面代码 } printf("6. OneNET服务器登录成功!\r\n"); delay_ms(100); while(1) { /*6. 向OneNet服务器5秒发送一次数据*/ time_cnt++; DelayMs(1); if(time_cnt>=5000) { time_cnt=0; /*获取一次GPS输出的经纬度信息*/ switch(MC20_GetGPS_Data(&Longitude,&latitude)) { case 0: printf("经度:%f,纬度:%f\r\n",Longitude,latitude); break; case 1: printf("ERROR:GPS数据接收失败!\r\n"); break; case 2: printf("ERROR:GPS定位数据解码失败!<请将GPS拿到空旷位置定位>\r\n"); break; } //组装数据格式 sprintf(onenet_http_cmd,"{\"lon\":%f,\"lat\":%f}",Longitude,latitude); data_stream[0].dataPoint=onenet_http_cmd; //赋值GPS数据 //向云端发送数据流 OneNet_SendData(FORMAT_TYPE1,DEVID,API_KEY,data_stream,1); } /*实时接收MC20收到的数据,进行解析*/ if(USART2_RX_FLAG) { USART2_RX_BUFF[USART2_RX_CNT]='\0'; printf("USART2_RX_BUFF=%s\r\n",USART2_RX_BUFF); //向串口打印信息 //解析平台返回的数据 OneNet_RevPro(USART2_RX_BUFF); USART2_RX_CNT=0; USART2_RX_FLAG=0; memset(USART2_RX_BUFF,0,sizeof(USART2_RX_BUFF)); } }}
复制代码


4.2 mc20.c

#include "mc20.h"/*函数功能:向MC20模块发送指令函数参数:        char *cmd  发送的命令        char *check_data 检测返回的数据返回值: 0表示成功 1表示失败*/u8 MC20_SendCmd(char *cmd,char *check_data){   u16 i,j;   for(i=0;i<5;i++) //测试的总次数   {      USART2_RX_FLAG=0;      USART2_RX_CNT=0;      memset(USART2_RX_BUFF,0,sizeof(USART2_RX_BUFF));      USART_X_SendString(USART2,cmd); //发送指令      for(j=0;j<500;j++) //等待的时间(ms单位)      {          if(USART2_RX_FLAG)          {              USART2_RX_BUFF[USART2_RX_CNT]='\0';              if(strstr((char*)USART2_RX_BUFF,check_data))              {                  return 0;              }              else break;          }          delay_ms(10); //一次的时间      }   }   return 1;}

/*函数功能: MC20初始化检查*/u8 MC20_InitCheck(void){ return MC20_SendCmd("AT\r\n","OK\r\n");}

/*函数功能: 开启GPS功能返 回 值:0表示成功 1表示失败*/u8 MC20_StartGPS(void){ //先判断GPS功能是否启动 if(MC20_SendCmd("AT+QGNSSC?\r\n","+QGNSSC: 1")) { //没有启动就启动GPS功能 if(MC20_SendCmd("AT+QGNSSC=1\r\n","OK\r\n")) { return 1; //GPS功能启动失败 } } return 0;}

/*函数功能:从buf里面得到第cnt个逗号所在的位置返 回 值:0~254,代表逗号所在位置的偏移.255,代表不存在第cnt个逗号*/u8 GPS_GetCommaOffset(char *buf,u8 cnt){ char *p=buf; while(cnt) { if(*buf=='*'||*buf<' '||*buf>'z')return 255;//遇到'*'或者非法字符,则不存在第cx个逗号 if(*buf==',')cnt--; buf++; } return buf-p; //计算偏移量}

/*函数功能: 获取GPS经纬度数据值函数参数: double *Longitude :经度 double *latitude :纬度返回值: 0表示定位成功,1表示定位失败
说明: 解析$GNRMC命令,得到经纬度$GNRMC,023705.000,A,2842.4164,N,11549.5713,E,1.73,91.65,150319,,,A*41
转换公式示例:经度: dddmm.mmmm 东经 11408.4790 114+(08.4790/60)=114.141317纬度: ddmm.mmmm 北纬 2236.9453 22+(36.9453/60)= 22.615755*/u8 GPS_GNRMC_Decoding(char *gps_buffer,double *Longitude,double *latitude){ u8 Offset; u32 int_data; double s_Longitude,s_latitude; char *p;
/*1. 确定下定位是否成功*/ p=strstr(gps_buffer,"$GNRMC"); if(!p)return 1; Offset=GPS_GetCommaOffset(p,2); if(Offset==255)return 2; if(*(p+Offset)!='A')return 3; //定位不准确 /*2. 得到纬度*/ Offset=GPS_GetCommaOffset(p,3); if(Offset==255)return 4; sscanf(p+Offset,"%lf",&s_latitude); s_latitude=s_latitude/100; int_data=s_latitude;//得到纬度整数部分 s_latitude=s_latitude-int_data;//得到纬度小数部分 s_latitude=(s_latitude)*100; *latitude=int_data+(s_latitude/60.0); //得到转换后的值 /*3. 得到经度*/ Offset=GPS_GetCommaOffset(p,5); if(Offset==255)return 5; sscanf(p+Offset,"%lf",&s_Longitude); s_Longitude=s_Longitude/100; int_data=s_Longitude;//得到经度整数部分 s_Longitude=s_Longitude-int_data; //得到经度小数部分 s_Longitude=s_Longitude*100; *Longitude=int_data+(s_Longitude/60.0); return 0;}

/*函数功能: 获取一次GPS经纬度数据函数参数: double *Longitude :经度 double *latitude :纬度返回值: 0表示定位成功,1表示数据接收失败,2表示定位失败*/u8 MC20_GetGPS_Data(double *Longitude,double *latitude){ /*1. 发送获取GPS数据的指令*/ if(MC20_SendCmd("AT+QGNSSRD=\"NMEA/RMC\"\r\n", "OK\r\n"))return 1; /*2. 对GPS数据进行解码*/ if(GPS_GNRMC_Decoding((char *)USART2_RX_BUFF,Longitude,latitude))return 2; //解码成功 return 0; }
/*函数功能: 连接服务器函数参数: char *server_ip 服务器IP地址 u16 port 服务器端口号返回值: 0表示连接成功,1表示连接失败*/u8 MC20_Connect_TCP_Server(char *server_ip,u16 port){ char send_buf[100]={0}; sprintf(send_buf,"AT+QIOPEN=\"TCP\",\"%s\",\"%d\"\r\n",server_ip,port); //连接至服务器 if(MC20_SendCmd(send_buf, "CONNECT")) { return 1; //连接失败 } return 0; //连接成功}

/*函数功能: 向服务器发送数据函数参数: u8 *buffer 发送的数据 u32 len 发送的长度返 回 值: 0表示发送成功,1表示准备发送失败,2表示数据发送失败*/u8 MC20_ClientSendData(u8 *buffer,u32 len){ char send_buf[2]; /*1. 准备发送数据*/ if(MC20_SendCmd("AT+QISEND\r\n",">")) { printf("AT+QISEND->ERROR Info:%s\r\n",USART2_RX_BUFF); return 1; } /*2. 开始发送数据*/ USART_X_SendData(USART2,buffer,len); delay_ms(20); /*3. 发送结束符*/ send_buf[0] = 0x1a; send_buf[1] = '\0'; if(MC20_SendCmd(send_buf,"OK\r\n")) { printf("发送结束符->ERROR Info:%s\r\n",USART2_RX_BUFF); return 2; } return 0;}

/*函数功能: MC20初始化检查并连接至服务器 */#include "led.h"void MC20_InitConnect(char *server_ip,u16 port){ /*1. MC20模块初始化检查*/ while(MC20_InitCheck()) { LED1=!LED1; printf("ERROR:MC20模块初始化检查失败!\r\n"); delay_ms(100); } printf("1. MC20模块初始化成功!\r\n"); delay_ms(100); /*2. 查询是否有PIN码锁定*/ while(MC20_SendCmd("AT+CPIN?\r\n","READY")) { LED1=!LED1; printf("ERROR:PIN码锁定检查失败!\r\n"); delay_ms(100); } printf("2. PIN码锁定检查成功!\r\n"); delay_ms(100); /*3. 查询SIM卡网络注册信息*/ if(MC20_SendCmd("AT+CREG?\r\n",",1")) //本地SIM卡 { if(MC20_SendCmd("AT+CREG?\r\n",",5"))//漫游SIM卡 { printf("ERROR:查询SIM卡网络注册信息失败!\n"); } else printf("3. 漫游SIM卡网络注册成功!\n"); } else printf("3. 本地SIM卡网络注册成功!\n"); delay_ms(100); /*4. 启动GPS功能*/ if(MC20_StartGPS()) { printf("ERROR:GPS功能启动失败!\n"); } else printf("4. GPS功能启动成功!\n"); delay_ms(100); /*5. 连接指定服务器*/ while(MC20_Connect_TCP_Server(server_ip,port)) { printf("ERROR: 连接TCP服务器失败!\r\n现在正在断开服务器,进行重连!\r\n需要保证服务器地址正确,并且SIM卡可以上网\r\n"); /*先断开服务器连接 (如果之前没有连接过服务器,这里就会出现错误)*/ MC20_SendCmd("AT+QICLOSE\r\n","OK\r\n"); delay_ms(100); MC20_SendCmd("AT+QIDEACT\r\n","OK\r\n"); delay_ms(100); } printf("5. 连接TCP服务器成功!\n"); delay_ms(100);}
复制代码


4.2 mc20.h

#ifndef  _MC20_H#define  _MC20_H#include "stm32f10x.h"#include "usart.h"#include "delay.h"#include <string.h>u8 MC20_SendCmd(char *cmd,char *check_data);u8 MC20_InitCheck(void);u8 MC20_StartGPS(void);u8 MC20_GetGPS_Data(double *Longitude,double *latitude);u8 MC20_Connect_TCP_Server(char *server_ip,u16 port);u8 MC20_ClientSendData(u8 *buffer,u32 len);

void MC20_InitConnect(char *server_ip,u16 port);#endif
复制代码


五、OneNet 创建产品

链接地址: https://open.iot.10086.cn/develop/global/product/#/console



发布于: 22 小时前阅读数: 2
用户头像

DS小龙哥

关注

之所以觉得累,是因为说的比做的多。 2022.01.06 加入

熟悉C/C++、51单片机、STM32、Linux应用开发、Linux驱动开发、音视频开发、QT开发. 目前已经完成的项目涉及音视频、物联网、智能家居、工业控制领域

评论

发布
暂无评论
STM32+移远MC20模块采用MQTT协议登录OneNet上传GPS数据_8月月更_DS小龙哥_InfoQ写作社区