STM32 GPS定位数据上报服务器 - (第三十讲)
视频讲解如下:
工程源码下载:GPS定位系统系列教程源码下载
在上一节中,我们已经把下位机中A9G的AT命令集写好了。关于a9g.c文件的接口调用代码如下:
#include <string.h>
#include <stdlib.h>
#include "stdio.h"
#include "config.h"
#include "delay.h"
#include "usart2.h"
#include "usart3.h"
#include "A9G.h"
#include "GpsCoord.h"
// A9G模块的GPS和GPRS采用的同一个输出口,这就导致了TCP透传和GPS定位无法同时开启。
int PackNum = 0;// 每个包的包号
int dou_post(char **str_ptr)
{
int post_num = 0;
while((**str_ptr >='0')&&(**str_ptr <= '9')){
post_num = post_num * 10;
post_num += (**str_ptr - 48);
(*str_ptr)++;
}
if(**str_ptr == '.') (*str_ptr)--;
return post_num;
}
double dou_point(char **str_ptr)
{
double point_num = 0;
if((**str_ptr >='0')&&(**str_ptr <= '9')){
point_num = **str_ptr - 48;
(*str_ptr)++;
if((**str_ptr >='0')&&(**str_ptr <= '9')){
point_num += dou_point(str_ptr);
}
}
if(**str_ptr == '\0') (*str_ptr)--;
return point_num / 10;
}
// 字符串转double
double str_to_dou(char *str_ptr)
{
int post_num = 0;
double point_num = 0;
int point_flag = 0;
while(*str_ptr != '\0')
{
if(*str_ptr == '.')
point_flag = 1;
else if(point_flag == 0)
post_num =dou_post(&str_ptr);
else if(point_flag == 1)
point_num =dou_point(&str_ptr);
str_ptr++;
}
return (double)post_num + point_num;
}
// TCP通讯的和校验算法
u8 GetSum(u8 *data, int length)
{
int i = 0;
int value = 0;
for (i = 0; i < length; i++)
{
value ^= data[i];
}
return (value & 0x00FF);
}
// 上传服务器
// Order 上传的数据
// length 数据长度
// oTime 等待超时时间
int SendServer(char *Order,int length, int oTime)
{
int i = 0;
int siz = 0;
char Receive[700]={0}; // 服务器反馈数据
// 发送AT命令
printf("Send Sever:");
for( i =0;i<length;i++)
{
printf("%02X ", Order[i]);
}
printf("\r\n");
usart3_Send((u8 *)Order, length);
usart3_FreeBuff(); // 把之前残留的垃圾清理掉
// 等待接收,超时时间:oTime(秒)
for(i = 0;i < oTime * 5; i++)
{
delay_ms(200); // 等待200ms,给串口中断留一些数据接收的时间
siz = 0;
memset(Receive,0,sizeof(Receive)); // 清空缓存数据
siz = usart3_Receive((u8 *)Receive, sizeof(Receive)); // 接收串口数据
if(siz <= 0) continue;
// 解析串口数据,如果接收到指定的字符了,则认为命令发送成功了
// CC 01 00 01 00 07 01 00 CA
if(Receive[0] == 0xCC && siz == 9)
{
// 发送AT命令
printf("Recv:");
for( i =0;i<siz;i++)
{
printf("%02X ", Receive[i]);
}
printf("\r\n");
printf("Send Sever OK\r\n");
// 我这里只对第一个字符做校验,其他的校验忽略。
return 0;
}
}
printf("Send Sever ERROR\r\n");
return -1;
}
// GPS数据中提取坐标信息
int GetGpsLocation(char *data, int length, double *location)
{
int i = 0, j = 0, a = 0, b = 0;
char *p = NULL;
char gnrmc[700] = "";// 原始GNRMC字符串
char temp[12][15]={0,0}; // 保存解析到分割数据
if(data == NULL || sizeof(data) == 0)
printf("[%s]\r\n", data); //打印一下我们接收到的字符串
// $GNRMC,112119.000,A,2559.8845,N,11921.6583,E,1.324,159.42,280922,,,A * 4F
//我们GPS初始化的时候,定义的是GPS+BD模式,所以我们需要在 $GNRMC 里面提取出我们的坐标数据
p = strstr(data,"$GNRMC");
if(p == NULL)
return -1; // 没找到我们需要的字符串
while((p- data <= length))
{
gnrmc[i] = *p;
if(gnrmc[i] == '\n')
{
gnrmc[i+1] = '\0';
break;
}
p++;
i++;
}
//时间、经纬度
if((p = strstr(gnrmc,"$GNRMC")) != 0)
{
p+=6;
for(j = 0;j<12;j++)
{
p++;
i = 0;
while((*p != ',') && (*p != '*')){
if(*p == '\0'){ j = 12; break;}
temp[j][i++] = *(p++);
}
}
}
if(temp[1][0] != 'A') //定位失败
{
printf("定位失败!\r\n");
return -1;
}
location[0] = str_to_dou(temp[2]);
location[1] = str_to_dou(temp[4]);
a = (int)(location[0]/100.0);
b = (int)(location[1]/100.0);
location[0] = a + (location[0] / 100.0 - a) * 100.0 / 60.0;// 纬度
location[1] = b + (location[1] / 100.0 - b) * 100.0 / 60.0;// 经度
// 将GPS坐标转换成百度坐标
wgs2bd(location[0],location[1], &location[0], &location[1]);
printf("定位成功:%s,%s\r\n",temp[2], temp[4]);
return 0;
}
// 发送坐标信息
void SendLocation(int user, double *location)
{
// 拼装命令
char oData[255] = {0};
int ret = 0;
long a = 0, b = 0;
oData[ret++] = 0xCC; // 网络识别码
oData[ret++] = 0x01; // 设备类型
oData[ret++] = 0x00; // 设备号H
oData[ret++] = 0x01; // 设备号L
if(PackNum > 1000) PackNum = 0;
oData[ret++] = (PackNum >> 8); // 包号H
oData[ret++] = (PackNum & 0x00FF); // 包号L
oData[ret++] = 0x11; // 功能码
// 用户ID
oData[ret++] = (user >> 8); // 用户ID H
oData[ret++] = (user & 0x00FF); // 用户ID L
// 经度
a = (long)(location[1] * 100000);
oData[ret++] = ((a >> 24) & 0x000000FF);
oData[ret++] = ((a >> 16) & 0x000000FF);
oData[ret++] = ((a >> 8) & 0x000000FF);
oData[ret++] = (a & 0x000000FF);
// 纬度
b = (long)(location[0] * 100000);
oData[ret++] = ((b >> 24) & 0x000000FF);
oData[ret++] = ((b >> 16) & 0x000000FF);
oData[ret++] = ((b >> 8) & 0x000000FF);
oData[ret++] = (b & 0x000000FF);
// 校验
oData[ret++] = (char)GetSum((u8 *)oData, ret);
// 上传命令,应该走的是GPRS网络,等待可以久点,我这里是5秒
SendServer(oData, ret, 5);
}
int main(void)
{
char GpsBuff[1000]="";
double location[2]={0};
int siz = 0;
delay_init();//延时函数初始化
usart2_Configuration(115200); // Printf的输出口(本来是打算用usart1来做打印接口的,但是我的板子usart1好像坏了。。。。)
// 经过测试,GPS单次数据包大约有228个字节,我这里直接在.h文件中给串口2开辟300字节的缓存空间
// A9G虽然有个独立的GPS输出口,但是在开启GPS定位时,AT命令口GPS口都会同时输出定位信息,这搞的GPS口很鸡肋。
usart3_Configuration(115200); // GPRS、GPS
// 串口测试用,将 A9G-USART 输出的数据直接透传到USART2口
// while(1)
// {
// delay_ms(500);
// siz = 0;
// memset(GpsBuff,0,sizeof(GpsBuff));
// siz = usart3_Receive((u8 *)GpsBuff, sizeof(GpsBuff));
// if(siz <= 0) continue;
//
// usart2_Send((u8 *)GpsBuff, siz);
// }
// 输出一些日志信息,方便调试
printf("等待GPS设备启动完成...\r\n");
// 等待GPS启动完成
A9G_Start();
// 初始化GPS,但是先别启动GPS,因为我们后面还需要初始化GPRS
printf("初始化GPS...\r\n");
A9G_Init_GPS();
printf("初始化GPS完成!\r\n");
// 初始化GPRS,采用透传模式上传我们的数据,初始化完成之后,先别开起来。
printf("初始化GPRS...\r\n");
A9G_Init_GPRS("103.46.128.21", "39343");
printf("初始化GPRS完成!\r\n");
usart3_FreeBuff(); // 把之前残留的垃圾清理掉
A9G_Start_GPS(); // 启动GPS
// 开始采集
while(1)
{
// 延时等待一下,反正也不需要那么快上报
delay_ms(500);
// 清理一下缓存
siz = 0;
memset(GpsBuff,0,sizeof(GpsBuff));
siz = usart3_Receive((u8 *)GpsBuff, sizeof(GpsBuff));
if(siz == 0) continue;
// 提取GPS坐标,如果提取成功,说明定位成功了
memset(location,0,sizeof(location));
if(GetGpsLocation(GpsBuff, siz, location) == -1)
{
// 定位失败
continue;
}
// 停止GPS
A9G_Stop_GPS();
// 启动GPRS
A9G_Start_GPRS();
// 发送GPS坐标,用户的ID是2
SendLocation(2, location);
// 停止GPRS
A9G_Stop_GPRS();
// 启动GPS
A9G_Start_GPS();
}
}