一、环境介绍
MCU: STM32F103C8T6
GSM模块: GA6--果云
开发软件: Keil5
完整源码下载: https://download.csdn.net/download/xiaolong1126626497/18245590
其他参考文章: STM32+ESP8266使用MQTT协议连接阿里云物联网服务器
其他参考文章:STM32+ESP8266使用标准MQTT协议(MQTTS)连接中国移动OneNet物联网服务器
二、GA6-GSM模块介绍与调试
说明: GA6-B模块供电必须5V,采用电脑USB供电可能不稳定(没有5V,只有4.8V左右),导致模块使用不稳定,发送AT指令没有反应,调试阶段,可将开发板的USB线连接充电宝或者手机充电插头取电。
2.1 GA6-B模块概述
GA6-B 模组:
1. GA6 尺寸 22.8mm x 16.8mm x 2.2 mm;
2. 正常工作温度:-30°C ~ +80°C,
3. 受限工作温度:-40°C ~ -30°C 及+80°C ~+85°C*
4. 工作电压 3.5V-4.2V;
5. 开机电压>3.5V;
6. SLEEP 模式下的耗流为 0.9mA;
7. 四频:GSM850, EGSM 900 和 DCS 1800,PCS1900 可以自动的搜寻 四个频段。 l 符合 GSM Phase 2 / 2+ l GPRS Class 10;
8. 灵敏度<-107;
9. 支持语音通话;
10. 支持 SMS 短信;
11. 支持移动和联通2G,以及全球的GSM网络
12. GPRS 数据特性,最大数据速率,下载 85.6Kbps,上传 42.8Kbps;
13. 支持符合 GSM 07.10 协议的串口复用功能
14. 支持 2 个串口,一个下载串口,一个 AT 命令口;
15. AT 命令支持标准 AT 和 TCP/IP 命令接口;
16. 支持数字音频和模拟音频,支持 HR,FR,EFR,AMR 语音编码;
17. 支持FCC,CE认证;
18. SMT 42PIN 封装;
GA6模块的优势
广域覆盖:GPRS在全国34个省均有良好覆盖,更是全球通行的2G通讯标准。基本上在手机可以打电话的地方都可以通过GPRS无线上网;
永远在线:只要激活GPRS应用后,将一直保持在线,类似于无线专线网络服务。
按量计费:GPRS服务虽然保持一直在线,但您不必担心费用问题;因为只有产生通信流量时才计费。
高速传输:目前GPRS可支持85.6Kbps的峰值传输速率,理论峰值传输可达100余Kbps。
价格便宜 :相对于 SIM 系列的模块 价格只有其的一半。大大降低了物联网设备的入门门槛凭借超小的尺寸,超低功耗和宽工作温度范围,GA6是M2M应用的理想解决方案,适用于车载、工业及PDA、个人跟踪、电力环境检测、无线POS、智能计量以及其它M2M的应用,为其提供完善的GSM/GPRS短信、数据传输及语音服务。
物联网卡:
2.2 GA6模块调试
模块默认波特率: 115200
电压: 5V
TX---URX(GA6模块)
RX---UTX(GA6模块)
1.模块上电串口助手收到的消息:
注意: 电压必须5V否则,给模块发送AT指令没有用。
- 正常情况下,模块插上SIM电话卡,供电达到5V时,模块上电会返回以下提示信息。
当模块出现 “SMS Ready”字符串提示时,说明模块已经可以正常的接收“AT”指令了。
2.3 基础常用的指令介绍使用
(指令结尾发送都需要加\r\n)
1. 检测模块是否正常
2. 查询SIM卡是否在卡槽内
3. 查询模块是否注册到网络
4. 关闭指令回显
2.4 GPRS网络通信相关指令介绍
1. 查询网络连接状态
2. 附着GPRS网络 (进行网络通信前,需要先附着GPRS网络才可进行正常通信)
3. 激活GPRS网络
4. 建立TCP连接: 连接TCP服务器
说明: 上面的指令是采用TCP协议连接,183.230.40.33服务器,端口号是80。
5. 发送数据
发送数据示例:
三、STM32代码调试GA6模块
3.1 通过STM32串口代码测试GA6模块
直接接在STM32F103C8T6最小系统板上面:
注意: 如果是使用USB给开发板供电,为了防止电压不够,USB线不要接分线器,直接接电脑的USB口。
因为GA6模块的电压必须5V才可驱动,4.8V都不行。
STM32F103C8T6最小系统板使用串口3与GA6-B模块连接:
出现以上提示之后,发送AT能返回OK就说明模块已经正常工作了。
模块上电会返回以下信息: (注意: 电压一定要保证是5V)
3.2 通过STM32封装标准函数(使用HTTP协议连接OneNet服务器)
#include "ga6_gprs.h" #define GS6_GSM_CHECK_CNT 10 /* 函数功能:向GA6_GPRS发送指令 函数参数: char *cmd :发送的指令 char *check :检查返回的字符串 u32 wait_time :等待的时间(100ms)为单位 说明:该函数只是适用于成功后返回OK的指令 返回值: 0表示成功 1表示失败 */ u8 GA6_GSM_SendCmd(char *cmd,char *check,u32 wait_time) { u32 i,j; for(i=0;i<GS6_GSM_CHECK_CNT;i++) //测试的总次数 { USART3_RX_FLAG=0; USART3_RX_CNT=0; memset(USART3_RX_BUFF,0,sizeof(USART3_RX_BUFF)); USART_X_SendString(USART3,cmd); for(j=0;j<wait_time;j++) //等待的时间(ms单位) { delay_ms(100); //一次的时间 if(USART3_RX_FLAG) { USART3_RX_BUFF[USART3_RX_CNT]='\0'; if(strstr((char*)USART3_RX_BUFF,check)) { return 0; } else break; } } } return 1; } /* 函数功能: 检查GA6的状态 返 回 值: 0表示成功,其他值表示失败 */ u8 GA6_GSM_StateCheck(void) { /*1. 检查模块是否正常*/ if(GA6_GSM_SendCmd("AT\r\n","OK",50)) { return 1; //模块不正常 } /*2. 关闭回显功能*/ if(GA6_GSM_SendCmd("ATE0\r\n","OK",50)) { return 2; //回显没有关闭成功 } /*3. 查询卡是否插上*/ if(GA6_GSM_SendCmd("AT+CPIN?\r\n","READY",50)) { return 3; //卡没有查上 } /*4. 查询卡是否注册到网络*/ if(GA6_GSM_SendCmd("AT+CREG?\r\n","+CREG: 1,1",50)) { return 4; //卡没有注册到网络 } return 0; } /* 函数功能: 连接TCP服务器 函数参数: char *server_ip : 服务器地址 u32 port :服务器端口 返 回 值: 0表示成功连接服务器,其他值表示服务器连接失败 */ u8 GA6_GSM_ConnectServer(char *server_ip,u32 port) { char cmd_buffer[50]; /*1. 检查服务器连接状态*/ if(GA6_GSM_SendCmd("AT+CIPSTATUS\r\n","CONNECT",50)) { /*2 附着GPRS网络*/ if(GA6_GSM_SendCmd("AT+CGATT=1\r\n","OK",50))return 1; /*3 激活GPRS网络*/ if(GA6_GSM_SendCmd("AT+CGACT=1,1\r\n","OK",50))return 2; /*4 连接指定的服务器*/ //组合命令 snprintf(cmd_buffer,sizeof(cmd_buffer),"AT+CIPSTART=\"TCP\",\"%s\",%d\r\n",server_ip,port); //连接服务器 //服务器连接成功的情况下该指令会返回两种状态: ALREAY CONNECT ,CONNECT OK if(GA6_GSM_SendCmd(cmd_buffer,"CONNECT",50))return 3; } return 0; } /* 函数功能: 向服务器发送数据 函数参数: u8 *data:发送的数据首地址 u32 len :数据长度 */ u8 GA6_GSM_SendDataToServer(u8 *data,u32 len) { char end_char[2]; end_char[0] = 0x1A;//结束字符 end_char[1] = '\0'; /*2.1 启动数据发送*/ if(GA6_GSM_SendCmd("AT+CIPSEND\r\n",">",50))return 1; /*2.2 发送实际要发送的数据*/ USART_X_SendData(USART3,data,len); /*2.3 结束数据发送*/ if(GA6_GSM_SendCmd(end_char,"SEND OK",100))return 2; return 0; }
GA6_GPRS.h文件代码:
#ifndef GA6_GPRS #define GA6_GPRS #include "stm32f10x.h" #include "usart.h" #include "delay.h" u8 GA6_GSM_StateCheck(void); u8 GA6_GSM_SendDataToServer(u8 *data,u32 len); u8 GA6_GSM_ConnectServer(char *server_ip,u32 port); u8 GA6_GSM_SendCmd(char *cmd,char *check,u32 wait_time); #endif
Main.c代码
#include "stm32f10x.h" #include <string.h> #include <stdio.h> #include "ga6_gprs.h" #include "usart.h" #include "timer.h" #include "led.h" #include "key.h" //u8 onenet_http_cmd[]= //{ // "POST /devices/517704007/datapoints HTTP/1.1\r\n"\ // "api-key:vvQAUiBG=HwKzqGicH=RxBvFCDw=\r\n"\ // "Host:api.heclouds.com\r\n"\ // "Connection:close\r\n"\ // "Content-Length:65\r\n"\ // "\r\n"\ // "{\"datastreams\":[{\"id\":\"ds18b20\",\"datapoints\":[{\"value\":88.88}]}]}" //}; u8 onenet_http_cmd[]= { "POST /devices/517620924/datapoints HTTP/1.1\r\n"\ "api-key:OCZ6ghYPdky3=FJQCOEVZbByHRM=\r\n"\ "Host:api.heclouds.com\r\n"\ "Connection:close\r\n"\ "Content-Length:62\r\n"\ "\r\n"\ "{\"datastreams\":[{\"id\":\"temp\",\"datapoints\":[{\"value\":88.88}]}]}" }; //应用发布地址: https://open.iot.10086.cn/iotbox/appsquare/appview?openid=905ef1b56ba526fdeee0c69a0787f176 /* 以下程序正确运行返回的数据: +NITZ:19/03/20,14:45:27,32 Call Ready +CREG: 1 SMS Ready 发送一次数据! GA6_GSM_StateCheck=0 GA6_GSM_ConnectServer=0 GA6_GSM_SendDataToServer=0 SEND OK HTTP/1.1 200 OK Date: Wed, 20 Mar 2019 14:45:40 GMT Content-Type: application/json Content-Length: 26 Connection: close Server: Apache-Coyote/1.1 Pragma: no-cache {"errno":0,"error":"succ"} CLOSED */ int main() { u8 key,state; LED_Init(); KEY_Init(); BEEP_Init(); TIM1_Init(72,20000); //辅助串口1接收,超时时间为20ms USART_X_Init(USART1,72,115200); TIM2_Init(72,20000);//辅助串口2接收,超时时间为20ms USART_X_Init(USART2,36,9600); TIM3_Init(72,20000);//辅助串口3接收,超时时间为20ms USART_X_Init(USART3,36,115200); printf("UART1 OK.....\n"); while(1) { if(USART3_RX_FLAG) { USART3_RX_BUFF[USART3_RX_CNT]='\0'; //printf("buff=%s,cnt=%d\n\n",USART3_RX_BUFF,USART3_RX_CNT); printf("%s",USART3_RX_BUFF); USART3_RX_CNT=0; USART3_RX_FLAG=0; memset(USART3_RX_BUFF,0,sizeof(USART3_RX_BUFF)); } key=KEY_Scanf(); if(key) { LED0=!LED0; LED1=!LED1; printf("发送一次数据!\n"); /*1. 检查GSM工作状态*/ state=GA6_GSM_StateCheck(); printf("GA6_GSM_StateCheck=%d\n",state); if(!state) { /*2. 连接服务器*/ state=GA6_GSM_ConnectServer("183.230.40.33",80); printf("GA6_GSM_ConnectServer=%d\n",state); if(!state) { /*3. 向服务器发送数据*/ state=GA6_GSM_SendDataToServer(onenet_http_cmd,strlen((char*)onenet_http_cmd)); printf("GA6_GSM_SendDataToServer=%d\n",state); } else { //手动断开服务器连接 printf("断开服务器连接:%d\r\n",GA6_GSM_SendCmd("AT+CIPCLOSE\r\n","OK",5000)); } } } } }
3.3 GA6-B模块使用HTTP协议连接OneNet服务器上传GPS经纬度
为了提高效率,通过GPS配置软件,可以将GPS模块配置成功以下选项:
主要修改的地方:
- GPS模块默认波特率为9600,配置成115200
- 输出的语句,只是输出RMC(推荐定位信息),因为现在只需要经纬度信息即可。
- 系统设置热启动状态,提高定位速度
GPS.c文件代码示例:
#include "gps.h" /* 函数功能:从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); // printf("转换前的纬度:%lf\r\n",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); //得到转换后的值 // printf("转换后的纬度: %lf\r\n",*latitude); /*3. 得到经度*/ Offset=GPS_GetCommaOffset(p,5); if(Offset==255)return 5; sscanf(p+Offset,"%lf",&s_Longitude); // printf("转换前的经度:%lf\r\n",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); // printf("转换后的经度:%lf\r\n",*Longitude); return 0; }
GPS.h代码示例
#ifndef GPS_H #define GPS_H #include "stm32f10x.h" #include <string.h> #include "usart.h" u8 GPS_GNRMC_Decoding(char *gps_buffer,double *Longitude,double *latitude); #endif
Main.c代码示例
#include "stm32f10x.h" #include <string.h> #include <stdio.h> #include "ga6_gprs.h" #include "usart.h" #include "timer.h" #include "led.h" #include "key.h" #include "gps.h" char onenet_http_cmd[1024]; //应用发布地址: https://open.iot.10086.cn/iotbox/appsquare/appview?openid=fd1307a02210acbef4b34de89d6cfe21 /* GPS 接线方式: 将 GPS 模块的 TX 脚与 PA3 相连接。 (串口 2 的接收脚) GPS 模块波特率默认为 9600 (为了提高速度,可以将GPS的波特率设置成115200---可直接通过上位机软件设置) GPS 模块型号: ATGM336H-5N 电 源: 3V */ /* GA6-GSM 接线方式: 将 GA6-GSM 模块的 UTX 脚与 PB11 相连接,URX 脚与 PB10 相连接。 (串口 3 的接收脚) GA6-GSM 模块波特率默认为 115200 GA6-GSM 模块型号: 果云GA6-B 电 源: 5V */ int main() { double Longitude,latitude; u8 state; u32 time_cnt=0; u16 data_tx_len=0; char temp_buff[50]; LED_Init(); KEY_Init(); BEEP_Init(); TIM1_Init(72,20000); //辅助串口1接收,超时时间为20ms USART_X_Init(USART1,72,115200); TIM2_Init(72,20000);//辅助串口2接收,超时时间为20ms USART_X_Init(USART2,36,115200); //接GPS模块 TIM3_Init(72,20000);//辅助串口3接收,超时时间为20ms USART_X_Init(USART3,36,115200); //接GSM模块 printf("UART1 OK.....\n"); while(1) { //接收GPRS模块的返回值 if(USART3_RX_FLAG) { USART3_RX_BUFF[USART3_RX_CNT]='\0'; //printf("buff=%s,cnt=%d\n\n",USART3_RX_BUFF,USART3_RX_CNT); printf("%s",USART3_RX_BUFF); USART3_RX_CNT=0; USART3_RX_FLAG=0; memset(USART3_RX_BUFF,0,sizeof(USART3_RX_BUFF)); } //接收GPS模块的返回值 if(USART2_RX_FLAG) { USART2_RX_BUFF[USART2_RX_CNT]='\0'; //printf("USART2_RX_BUFF=%s",USART2_RX_BUFF); //解析GPS数据,得到经纬度 if(GPS_GNRMC_Decoding((char*)USART2_RX_BUFF,&Longitude,&latitude)) { printf("GPS定位失败! 请到空旷地方定位\r\n"); } else //定位成功 { if(time_cnt>=8000) //8秒一次 { data_tx_len=71; sprintf(temp_buff,"%lf",Longitude); data_tx_len+=strlen(temp_buff); sprintf(temp_buff,"%lf",latitude); data_tx_len+=strlen(temp_buff); //得到发送的数据长度 snprintf(onenet_http_cmd,sizeof(onenet_http_cmd), "POST /devices/517704007/datapoints HTTP/1.1\r\n"\ "api-key:vvQAUiBG=HwKzqGicH=RxBvFCDw=\r\n"\ "Host:api.heclouds.com\r\n"\ "Connection:close\r\n"\ "Content-Length:%d\r\n"\ "\r\n"\ "{\"datastreams\":[{\"id\":\"gps\",\"datapoints\":[{\"value\":{\"lon\":%lf,\"lat\":%lf}}]}]}", data_tx_len,Longitude,latitude ); printf("data_tx_len=%d\r\n",data_tx_len); printf("经度:%lf,纬度:%lf\r\n",Longitude,latitude); time_cnt=0; LED0=!LED0; LED1=!LED1; /*1. 检查GSM工作状态*/ state=GA6_GSM_StateCheck(); printf("GA6_GSM_StateCheck=%d\n",state); if(!state) { /*2. 连接服务器*/ state=GA6_GSM_ConnectServer("183.230.40.33",80); printf("GA6_GSM_ConnectServer=%d\n",state); if(!state) { /*3. 向服务器发送数据*/ state=GA6_GSM_SendDataToServer((u8*)onenet_http_cmd,strlen((char*)onenet_http_cmd)); printf("GA6_GSM_SendDataToServer=%d\n",state); } else { //手动断开服务器连接 printf("断开服务器连接:%d\r\n",GA6_GSM_SendCmd("AT+CIPCLOSE\r\n","OK",5000)); } } } } USART2_RX_CNT=0; USART2_RX_FLAG=0; memset(USART2_RX_BUFF,0,sizeof(USART2_RX_BUFF)); } time_cnt++; delay_ms(1); } }
- 网页上显示的效果: