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

article/2025/9/13 6:00:04

一、环境介绍

MCU:  STM32F103C8T6

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

开发软件: Keil5

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

如果想使用新版本的标准MQTT协议连接OnetNet请参考这里:  https://blog.csdn.net/xiaolong1126626497/article/details/107385118

完整源代码下载:  https://download.csdn.net/download/xiaolong1126626497/18245206

二、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"//产品ID
char PROID[]="231174";//鉴权信息
char AUTH_INFO[]="1234567890";//设备ID
char DEVID[]="523369555";//API KEY
char 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					MC203.3V	------>	V_IOGND		<----->	GNDPA3		<------	GSM_TXPA2		------>	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接收,超时时间为20msUSART_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

 

 

 


http://chatgpt.dhexx.cn/article/WRTpQ0Qt.shtml

相关文章

OpenCV-最大极值稳定区域MSER分析

最大稳定极值区域MSER是一种类似分水岭图像的分割与匹配算法&#xff0c;它具有仿射不变性。极值区域反映的就是集合中的像素灰度值总大于或小于其邻域区域像素的灰度值。对于最大稳定区域&#xff0c;通过局部阈值集操作&#xff0c;区域内的像素数量变化是最小的。 MSER的基…

【转载】项目实战—文档区域MSER检测实战(十)

上次已经讨论过相关的理论&#xff0c;这次我们来进行相关的实战。 OCR相关工作都有一个第一步&#xff0c;那就是检测图像中的文本区域&#xff0c;只有找到了文本区域&#xff0c;才能对其内容进行识别&#xff0c;也只有找到了文本区域&#xff0c;才能更有针对性地判断该文…

opencv 中将 MSER 修改成 Hierarchical MSER 方法

http://code.opencv.org/issues/1577 描述 I attach a patch implementing the Hierarchical MSER. It extends the OpenCV MSER implementation to return the underlying component tree used by the MSER algorithm. The tree is returned in the CvSeq tree fields (h_next…

OpenCV实践之MSER/MSCR极值区域检测算法

MSER/MSCR极值区域检测算法 OpenCV中features2d.hpp中MSER类接口实现了MSER极值区域检测算法&#xff0c;MSER类根据输入参数判断是否为彩色or灰度图像进行不同的算法检测。若输入为灰度图像&#xff0c;那么采取MSER极值区域检测算法&#xff0c;若输入为彩色图像&#xff0c;…

MSER仿射不变特征匹配算法

MSER原理简述 个人博客 OpenCV实践之MSER仿射匹配算法 已更新讲述MSER仿射匹配算法代码      区域检测(Region Detection)方法是根据图像中具有某种同类性质的像元进行分类(例如相同像素值大小的点),然后把具有相同性质的像元合并成区域,实现区域的检测即图像分割。MSER…

OpenCV用MSER 算法提取特征区域

计算图像 MSER 的基础类是 cv::MSER&#xff0c;继承自 cv::Feature2D 类&#xff0c;cv::MSER 类的实例可以通过create 方法创建。我们在初始化时指定被检测区域的最小和最大尺寸&#xff0c;以便限制被检测特征的数量&#xff0c;调用方式如下&#xff1a; cv::Ptr<cv::M…

车牌定位之MSER — 文本检测

最大稳定极值区域&#xff08;MSER-Maximally Stable Extremal Regions&#xff09;可以用于图像的斑点区域检测。它是基于分水岭的概念。 SIFT和SURF算法高效实现了具有尺度和旋转不变性的特征检测&#xff0c;但这些特征不具有仿射不变性。区域检测针对各种不同形状的图像区域…

图像局部特征(十四)--MSER特征

原文: http://blog.csdn.net/zhaocj/article/details/40742191 最大稳定极值区域&#xff08;MSER-Maximally Stable Extremal Regions&#xff09;可以用于图像的斑点区域检测。该算法最早是由Matas等人于2002年提出&#xff0c;它是基于分水岭的概念。 MSER的基本原理是对…

【AI实战】手把手教你深度学习文字识别(文字检测篇:基于MSER, CTPN, SegLink, EAST等方法)

文字检测是文字识别过程中的一个非常重要的环节&#xff0c;文字检测的主要目标是将图片中的文字区域位置检测出来&#xff0c;以便于进行后面的文字识别&#xff0c;只有找到了文本所在区域&#xff0c;才能对其内容进行识别。 文字检测的场景主要分为两种&#xff0c;一种是…

MSRCR

带色彩恢复的多尺度视网膜增强算法&#xff08;MSRCR&#xff09;的原理、实现及应用。 Retinex这个词是由视网膜(Retina)和大脑皮层(Cortex) 两个词组合构成的。Retinex理论主要包含了两个方面的内容&#xff1a;物体的颜色是由物体对长波、 中波和短波光线的反射能力决定的&a…

mser场景文字检测及筛选

发现很多人都用mser,swt等进行场景文字的检测&#xff0c;最近也去实现了一下&#xff0c;虽然swt较新的算法&#xff0c;但实现过程中传统的mser算法反而更稳定&#xff0c;速度也会更快&#xff0c;可能是我还没有完全领会swt的精髓。ps:暂时只做水平及水平倾斜的文字 demo …

mser python篇

之前一直在matlab上用这个函数&#xff0c;现在转移到python上面使用 ———————————————————————————————————— 1、代码 I cv2.cvtColor(I, cv2.COLOR_BGR2GRAY); mser cv2.MSER_create() regions,boxes mser.detectRegions(I) for bo…

MSER — 自然场景文本检测

MSER是最大稳定极值区域&#xff1a;是对一幅灰度图像&#xff08;灰度值为0&#xff5e;255&#xff09;取阈值进行二值化处理&#xff0c;阈值从0到255依次递增。阈值的递增类似于分水岭算法中的水面的上升&#xff0c;随着水面的上升&#xff0c;有一些较矮的丘陵会被淹没&a…

文字检测与识别1-MSER

导语 文字识别在现实场景中的用途非常广泛&#xff0c;现在已经有很多公司将这项技术用于实际中。比如车牌识别&#xff0c;图片转换成文档&#xff0c;拍照搜题&#xff0c;拍照翻译等。这让很多人有了错觉&#xff0c;感觉文字识别的技术已经炉火纯青&#xff0c;可以广泛应…

MSER常见参数

MSER用于文本检测已经成熟了&#xff0c;现简单使用来识别车牌号。 目录 MSER参数最大最小区域固定 MSER参数 默认&#xff1a;int delta 5, int min_area 60, int max_area 14400, double max_variation 0.25, double min_diversity .2 * Full constructor for %MSER d…

MSER算法

最稳定极值区域介绍 如把灰度图看成高低起伏的地形图&#xff0c;其中灰度值看成海平面高度的话&#xff0c;MSER的作用就是在灰度图中找到符合条件的坑洼。条件为坑的最小高度&#xff0c;坑的大小&#xff0c;坑的倾斜程度&#xff0c;坑中如果已有小坑时大坑与小坑的变化率…

【MSER】基于MSER算法的交通标志分割仿真

1.软件版本 MATLAB2021a 2.本算法理论知识 [1]钱坤. 基于MSER和遗传优化SVM的交通标志识别的研究[D]. 大连理工大学. [2]王斌, 常发亮, 刘春生. 基于MSER和SVM的快速交通标志检测[J]. 光电子.激光, 2016. 3.部分源码 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%…

MSER相关总结

最近做项目用到了MSER&#xff0c;特地在这做总结。 以前提到字符检测首先会想到Tesseract&#xff0c;但是tesseact对图像的二值化要求过高&#xff0c;比较适合于白底黑字的字符识别&#xff0c;对于复杂情况就无能为力了&#xff1b; 于是就想到用轮廓检测&#xff0c;这种…

最大稳定极值区域(MSER)检测

Lowe和Bay提出的SIFT和SURF算法高效实现了具有尺度和旋转不变性的特征检测&#xff0c;但这些特征不具有仿射不变性。 区域检测针对各种不同形状的图像区域&#xff0c;通过对区域的旋转和尺寸归一化&#xff0c;可以实现仿射不变性。 MSER&#xff08;Maximally Stable Extr…

MSER最稳定极值区域源码分析

最稳定极值区域介绍 如把灰度图看成高低起伏的地形图&#xff0c;其中灰度值看成海平面高度的话&#xff0c;MSER的作用就是在灰度图中找到符合条件的坑洼。条件为坑的最小高度&#xff0c;坑的大小&#xff0c;坑的倾斜程度&#xff0c;坑中如果已有小坑时大坑与小坑的变化率。…