使用arduino解码GPS NMEA代码

Decode GPS NMEA code using arduino

本文关键字:NMEA 代码 GPS 解码 arduino 使用      更新时间:2023-10-16

我知道这个问题被问了很多次,但我真的找不到我真正在搜索的内容。

我正在使用一个Arduino Uno和一个通过串行显示GPS数据的GPS屏蔽。

这是我上传到Arduino的代码,用于连接GPS屏蔽:

void loop() // run over and over
{
    while(!(mySerial.available())){}
      Serial.write(mySerial.read());
}

这只是代码。尽管如此,当它在串行监视器上连续循环时,它也会每秒输出GPS数据。

这是它每秒的输出:

$GPGGA,013856.000,000.9090,N,9090.90,E,1,09,1.1,316.97,M,0.00,M,,*66
$GPGSA,A,3,07,08,11,1ÿ3,16,19,23,27,42,,,,2.8,1.1,2.5*3F
$GPRMC,013856.000,A,000.9090,N,9090.90,E,0.0,038.1,310814,,,A*62
$GPGSV,ÿ3,1,12,16,26,059,33,27,33,025,44,08,30,330,32,07,31,326,34*7A
$GPGSV,3,2,12,19,58,354,31,01,33,186,18,23,32,221,24,11,5ÿ9,198,31*70
$GPGSV,3,3,12,42,60,129,32,13,38,253,27,32,06,161,,31,01,140,*7E

当它每秒更新一次时,坐标会变为最小值,这意味着GPS盾牌正在工作。

这里的问题是,我想解析GPS数据,尤其是仅在GPGGA线路上,而忽略其他线路。我想解析状态、纬度、N/S指示器、经度和E/W指示器。

我已搜索NMEA图书馆(http://nmea.sourceforge.net/),但我不知道如何使用它。

有人能帮我吗?非常感谢。

NMEA数据是GPS风格(ddmm.sss)格式,谷歌希望它是十进制风格(dd.mmsss),这一步骤的代码底部有一个覆盖函数。

我写这篇文章是因为我不喜欢大型复杂的库做简单的小事,尤其是当我试图弄清楚它是如何工作的时候。这将解析GLL句子,但如果需要,您可以更改它要查找的句子并重新排列部分。

String ReadString;
void setup() {  
  Serial.begin(9600);  //Arduino serial monitor thru USB cable 
  Serial1.begin(9600); // Serial1 port connected to GPS
}
void loop() {  
  ReadString=Serial1.readStringUntil(13);   //NMEA data ends with 'return' character, which is ascii(13)
  ReadString.trim();                      // they say NMEA data starts with "$", but the Arduino doesn't think so.
  // Serial.println(ReadString);         //All the raw sentences will be sent to monitor, if you want them, maybe to see the labels and data order.
  //Start Parsing by finding data, put it in a string of character array, then removing it, leaving the rest of thes sentence for the next 'find'
   if (ReadString.startsWith("$GPGLL")) {   //I picked this sentence, you can pick any of the other labels and rearrange/add sections as needed. 
      Serial.println(ReadString);     // display raw GLL data in Serial Monitor
     // mine looks like this: "$GPGLL,4053.16598,N,10458.93997,E,224431.00,A,D*7D"
//This section gets repeated for each delimeted bit of data by looking for the commas
     //Find Lattitude is first in GLL sentence, other senetences have data in different order
      int Pos=ReadString.indexOf(',');   //look for comma delimetrer
      ReadString.remove(0, Pos+1); // Remove Pos+1 characters starting at index=0, this one strips off "$GPGLL" in my sentence
      Pos=ReadString.indexOf(','); //looks for next comma delimetrer, which is now the first comma because I removed the first segment   
        char Lat[Pos];            //declare character array Lat with a size of the dbit of data
           for (int i=0; i <= Pos-1; i++){    // load charcters into array
            Lat[i]=ReadString.charAt(i);           
           }   
            Serial.print(Lat);          // display raw latitude data in Serial Monitor, I'll use Lat again in a few lines for converting   
//repeating with a different char array variable        
       //Get Lattitude North or South
        ReadString.remove(0, Pos+1);               
        Pos=ReadString.indexOf(',');    
        char LatSide[Pos];           //declare different variable name
           for (int i=0; i <= Pos-1; i++){
            LatSide[i]=ReadString.charAt(i);  //fill the array          
            Serial.println(LatSide[i]);       //display N or S
           }
          //convert the variable array Lat to degrees Google can use
          float LatAsFloat = atof (Lat);            //atof converts the char array to a float type
          float LatInDeg;
           if(LatSide[0]==char(78)) {        //char(69) is decimal for the letter "N" in ascii chart   
               LatInDeg= ConvertData(LatAsFloat);   //call the conversion funcion (see below) 
           }
           if(LatSide[0]==char(83)) {        //char(69) is decimal for the letter "S" in ascii chart   
               LatInDeg= -( ConvertData(LatAsFloat));   //call the conversion funcion (see below) 
           }
           Serial.println(LatInDeg,15); //display value Google can use in Serial Monitor, set decimal point value high
//repeating with a different char array variable               
       //Get Longitude
        ReadString.remove(0, Pos+1);               
        Pos=ReadString.indexOf(',');    
        char Longit[Pos];             //declare different variable name
           for (int i=0; i <= Pos-1; i++){
            Longit[i]=ReadString.charAt(i);      //fill the array  
           }   
            Serial.print(Longit);      //display raw longitude data in Serial Monitor      
//repeating with a different char array variable 
            //Get Longitude East or West
        ReadString.remove(0, Pos+1);              
        Pos=ReadString.indexOf(',');    
        char LongitSide[Pos];         //declare different variable name
           for (int i=0; i <= Pos-1; i++){
            LongitSide[i]=ReadString.charAt(i);      //fill the array          
            Serial.println(LongitSide[i]);        //display raw longitude data in Serial Monitor
           }       
           //convert to degrees Google can use  
          float LongitAsFloat = atof (Longit);    //atof converts the char array to a float type
          float LongInDeg;
         if(LongitSide[0]==char(69)) {        //char(69) is decimal for the letter "E" in ascii chart
                 LongInDeg=ConvertData(LongitAsFloat);   //call the conversion funcion (see below
         }    
         if(LongitSide[0]==char(87)) {         //char(87) is decimal for the letter "W" in ascii chart
                 LongInDeg=-(ConvertData(LongitAsFloat)); //call the conversion funcion (see below
         }             
           Serial.println(LongInDeg,15);  //display value Google can use in Serial Monitor, set decimal point value high
//repeating with a different char array variable 
            //Get TimeStamp - GMT
        ReadString.remove(0, Pos+1);                
        Pos=ReadString.indexOf(',');    
        char TimeStamp[Pos];          //declare different variable name
           for (int i=0; i <= Pos-1; i++){
            TimeStamp[i]=ReadString.charAt(i);         //fill the array     
            }
           Serial.print(TimeStamp);   //display raw longitude data in Serial Monitor, GMT
           Serial.println("");       
   }
}
//Conversion function
float ConvertData(float RawDegrees)
{ 
  float RawAsFloat = RawDegrees; 
  int firstdigits = ((int)RawAsFloat)/100; // Get the first digits by turning f into an integer, then doing an integer divide by 100;
  float nexttwodigits = RawAsFloat - (float)(firstdigits*100);
  float Converted = (float)(firstdigits + nexttwodigits/60.0);
  return Converted;
}

我写了这段不错的代码,它最多可以工作到小数点后两位。

代码:

String gpsData;
String LATval = "######";
String LNGval = "######";
char inChar;
String gpsData;
String latt;
String la;
String lonn;
String lo;
float lattt;
float lonnn;
int latDeg;
int lonDeg;
float latMin;
float lonMin;
float latttt;
float lonnnn;
String sGPRMC;
void setup() {
   Serial.begin(9600);
}
void loop() {
  while (Serial.available()) {
    inChar = Serial.read();
    gpsData += inChar;
    if (inChar == '$') {
      gpsData = Serial.readStringUntil('n');
      break;
    }
  }
  Serial.println(gpsData);
  sGPRMC = gpsData.substring(0, 5);
  if (sGPRMC == "GPRMC") {
    Serial.flush();
    latt = gpsData.substring(18, 28);
    la = gpsData.substring(29, 30);
    lonn = gpsData.substring(31, 42);
    lo = gpsData.substring(43, 44);
    Serial.print("latt:");
    Serial.println(latt);
    Serial.print("la:");
    Serial.println(la);
    Serial.print("lonn:");
    Serial.println(lonn);
    Serial.print("lo:");
    Serial.println(lo);
    lattt = latt.toFloat();
    lonnn = lonn.toFloat();
    Serial.print("lattt:");
    Serial.println(lattt);
    Serial.print("lonnn:");
    Serial.println(lonnn);
    if (la == "N" and lo == "E") {
      latDeg = float(int(lattt / 100));
      latMin = float(lattt - (latDeg * 100));
      latMin = latMin / 60;
        
      lonDeg = float(int(lonnn / 100));
      lonMin = float(lonnn - (lonDeg * 100));
      lonMin = lonMin / 60;
          
      latttt = latDeg + latMin;
      lonnnn = lonDeg + lonMin;
      LATval = String(latttt);
      LNGval = String(lonnnn);
      Serial.print("latDeg:");
      Serial.println(latDeg);
      Serial.print("latMin:");
      Serial.println(latMin);
      Serial.print("lonDeg:");
      Serial.println(lonDeg);
      Serial.print("lonMin:");
      Serial.println(lonMin);
      Serial.print("LATval:");
      Serial.println(LATval);
      Serial.print("LNGval:");
      Serial.println(LNGval);
    }
  }
}

我在互联网上搜索过,最好的答案是使用Arduino的"TinyGPS++"库。几乎所有与GPS相关的代码都已包含在图书馆中。

您可以使用TinyGPS来解析NMEA字符串。如果你只对一句话感兴趣。您可以为该句子编写如下自定义解析器。

int handle_byte(int byteGPS) {
buf[counter1] = byteGPS;
//Serial.print((char)byteGPS);
counter1++;
if (counter1 == 300) {
  return 0;
}
if (byteGPS == ',') {
    counter2++;
    offsets[counter2] = counter1;
    if (counter2 == 13) {
      return 0;
    }   }   if (byteGPS == '*') {
    offsets[12] = counter1;   }
  // Check if we got a <LF>, which indicates the end of line   if (byteGPS == 10) {
    // Check that we got 12 pieces, and that the first piece is 6 characters
    if (counter2 != 12 || (get_size(0) != 6)) {
      return 0;
    }
    // Check that we received $GPRMC
    // CMD buffer contains $GPRMC
    for (int j=0; j<6; j++) {
      if (buf[j] != cmd[j]) {
        return 0;
      }
    }
    // Check that time is well formed
    if (get_size(1) != 10) {
      return 0;
    }
    // Check that date is well formed
    if (get_size(9) != 6) {
      return 0;
    }
    SeeedOled.setTextXY(7,0);
    for (int j=0; j<6; j++) {
      SeeedOled.putChar(*(buf+offsets[1]+j));
    }
    SeeedOled.setTextXY(7,7);
    for (int j=0; j<6; j++) {
      SeeedOled.putChar(*(buf+offsets[9]+j));
    }
    // TODO: compute and validate checksum
    // TODO: handle timezone offset
      return 0;   }   
return 1; }

试试这个可以帮助你

#include <SoftwareSerial.h>
#include <TinyGPS.h>
TinyGPS gps;
SoftwareSerial ss(3,4);

static void smartdelay(unsigned long ms);
static void print_float(float val, float invalid, int len, int prec);
static void print_int(unsigned long val, unsigned long invalid, int len);
static void print_date(TinyGPS &gps);
static void print_str(const char *str, int len);
void setup()
{
  Serial.begin(9600);
  ss.begin(9600);
}
void loop()
{
  float flat, flon;
  unsigned short sentences = 0, failed = 0;
  gps.f_get_position(&flat, &flon);
  Serial.print("LATITUDE: ");
  print_float(flat, TinyGPS::GPS_INVALID_F_ANGLE, 10, 6);
  Serial.println(" ");
  Serial.print("LONGITUDE: "); 
  print_float(flon, TinyGPS::GPS_INVALID_F_ANGLE, 11, 6);
  Serial.println(" ");
  Serial.print("altitude: ");
  print_float(gps.f_altitude(), TinyGPS::GPS_INVALID_F_ALTITUDE, 7, 2);
  Serial.println(" ");
  Serial.print("COURSE:");
  print_float(gps.f_course(), TinyGPS::GPS_INVALID_F_ANGLE, 7, 2);
  Serial.println("");
  Serial.print("DIRECTION: ");
  int d;
  print_str(gps.f_course() == TinyGPS::GPS_INVALID_F_ANGLE ? "*** " : TinyGPS::cardinal(gps.f_course()), 6);
  d=gps.f_course(); 
  Serial.println();
  Serial.println();
  smartdelay(1000);    
}
static void smartdelay(unsigned long ms)
{
  unsigned long start = millis();
  do 
  {
    while (ss.available())
      gps.encode(ss.read());
  } while (millis() - start < ms);
}
static void print_float(float val, float invalid, int len, int prec)
{
  if (val == invalid)
  {
    while (len-- > 1)
      Serial.print('*');
    Serial.print(' ');
  }
  else
  {
    Serial.print(val, prec);
    int vi = abs((int)val);
    int flen = prec + (val < 0.0 ? 2 : 1); // . and -
    flen += vi >= 1000 ? 4 : vi >= 100 ? 3 : vi >= 10 ? 2 : 1;
    for (int i=flen; i<len; ++i)
      Serial.print(' ');
  }
  smartdelay(0);
}
static void print_int(unsigned long val, unsigned long invalid, int len)
{
  char sz[32];
  if (val == invalid)
    strcpy(sz, "*******");
  else
    sprintf(sz, "%ld", val);
  sz[len] = 0;
  for (int i=strlen(sz); i<len; ++i)
    sz[i] = ' ';
  if (len > 0) 
    sz[len-1] = ' ';
  Serial.print(sz);
  smartdelay(0);
}
static void print_str(const char *str, int len)
{
  int slen = strlen(str);
  for (int i=0; i<len; ++i)
    Serial.print(i<slen ? str[i] : ' ');
  smartdelay(0);
}

我已经更改了上面的代码,它运行得很好。它只返回GPGGA数据开关,可以在下面的站点进行解码:https://rl.se/gprmc完整的代码是:

  1. #包括<软件序列.h>#包括<string.h>char inChar;字符串gpsData;字符串sGPRMC;

    //创建一个软件串行端口;gpsSerial";软件序列gpsSerial(8,9);

    void setup(){//在9600启动Arduino硬件串行端口波特串行开始(9600);

    //以GPS的默认波特启动软件串行端口
    gpsSerial-begin(9600);}

    void loop(){

    while(gpsSerial.available()){inChar=gpsSerial.read();if(inChar=='$'){gpsData=gpsSerial.readStringUntil('\n');打破}}

    //Serial.println(gpsData);sGPRMC=gpsData.substring(0,5)
    如果(sGPRMC=="GPGGA"){Serial.flush();Serial.print("$");Serial.print(gpsData);}}