想要使用arduino每5秒获取一次GPS数据

时间:2017-03-24 18:35:08

标签: arduino gps delay

我用头文件TinyGPSPlus在Arduino uno中编写了以下代码,并使用GPS SKG 13BL(GPS模块)。

#include <TinyGPS++.h>
#include <SoftwareSerial.h>
  /*
    This program sketch obtain and print the lati,logi,speed,date and time
    It requires the use of SoftwareSerial, and assumes that you have a
    9600-baud serial GPS device hooked up on pins 4(rx) and 3(tx).
   */
static const int RXPin = 4, TXPin = 3;
static const uint32_t GPSBaud = 9600;

   // The TinyGPS++ object
TinyGPSPlus gps;

   // The serial connection to the GPS device
SoftwareSerial ss(RXPin, TXPin);

void setup()
    {
     Serial.begin(9600);
     ss.begin(GPSBaud);

     Serial.println(F("GPS LOADING....."));
     Serial.println(F("Obtain and print lati,logi,speed,date and time"));
     Serial.println(F("Testing by : "));
     Serial.println(F("Billa"));
     Serial.println();
      }

 void loop()
    {
       // This sketch displays information every time a new sentence is correctly encoded.
      while (ss.available() > 0)
          if (gps.encode(ss.read()))
            displayInfo();

      if (millis() > 5000 && gps.charsProcessed() < 10)
        {
         Serial.println(F("No GPS detected: check wiring."));
         while(true);
         }
    }

 void displayInfo()
     {
       Serial.print(F("Location: ")); 
       if (gps.location.isValid())
         {
          Serial.print(gps.location.lat(), 6);
          Serial.print(F(","));
          Serial.print(gps.location.lng(), 6);
      }
else
   {
    Serial.print(F("INVALID"));
    }

Serial.print(F("  Speed: ")); 
if (gps.speed.isValid())
  {
   Serial.print(gps.speed.kmph());
   Serial.print(F(" KMPH "));
   }
else
  {
   Serial.print(F("INVALID"));
   }

Serial.print(F("  Date : "));
if (gps.date.isValid())
  {
   Serial.print(gps.date.month());
   Serial.print(F("/"));
   Serial.print(gps.date.day());
   Serial.print(F("/"));
   Serial.print(gps.date.year());
   }
else
   {
    Serial.print(F("INVALID"));
    }

Serial.print(F("  Time : "));
if (gps.time.isValid())
  {
   int hour= gps.time.hour() + 5;
   if (hour < 10) Serial.print(F("0"));
   if(hour > 12) hour-=11;
   Serial.print(hour);
   Serial.print(F(":"));
   int minute = gps.time.minute() + 30;
   if(minute >= 60) minute-=60;
   if (minute < 10) Serial.print(F("0"));
   Serial.print(minute);
   Serial.print(F(":"));
  if (gps.time.second() < 10) Serial.print(F("0"));
  Serial.print(gps.time.second());
  }
else
  {
   Serial.print(F("INVALID"));
   }

Serial.println();
 }

它获得了所需的输出。在串行监视器上连续显示数据行。                             但现在我需要每隔5秒准确地获取这些数据(即 每隔5秒,上面的代码应按照那个瞬间产生输出 )。我试过使用延迟执行此操作并重写循环代码如下

 void loop()
{
   delay(5000);
   // This sketch displays information every time a new sentence is correctly encoded.
  while (ss.available() > 0)
      if (gps.encode(ss.read()))
        displayInfo();

  if (millis() > 5000 && gps.charsProcessed() < 10)
    {
     Serial.println(F("No GPS detected: check wiring."));
     while(true);
     }
}

但是这并没有按照需要获得输出。任何人都可以帮我解决这个问题。我应该编辑哪些内容以及应该做些什么更改。

3 个答案:

答案 0 :(得分:6)

这是我写NeoGPS的原因。所有其他库的示例程序结构不正确。我在看着你,smartDelay() ......

NeoGPS的结构是从GPS设备接收完整的fix。这通常需要收到几个句子。其他GPS库只会在收到一个句子时告诉您。此外,很难判断两个句子是来自相同的1秒更新间隔,还是两个连续的间隔。

您希望每5秒钟显示一次信息,但可能每20个句子显示一次。根据Arduino millis()时钟,它将 5000毫秒,但不完全millis()会在GPS间隔期间漂移,具体取决于您的水晶的准确程度。 GPS间隔非常准确,达到了原子钟,串行波特率和GPS设备计算时间的极限。

这是你的草图,修改为使用NeoGPS:

#include <NMEAGPS.h>
  /*
    This program sketch obtain and print the lati,logi,speed,date and time
    It requires the use of SoftwareSerial, and assumes that you have a
    9600-baud serial GPS device hooked up on pins 4(rx) and 3(tx).
   */
#include <NeoSWSerial.h>
static const int RXPin = 4, TXPin = 3;
NeoSWSerial gpsPort(RXPin, TXPin);
static const uint32_t GPSBaud = 9600;

NMEAGPS gps;
gps_fix fix;
uint8_t fixCount = 0;

void setup()
{
  Serial.begin(9600);
  gpsPort.begin(GPSBaud);

  Serial.println(F("GPS LOADING....."));
  Serial.println(F("Obtain and print lati,logi,speed,date and time"));
  Serial.println(F("Testing by : "));
  Serial.println(F("Billa"));
  Serial.println();
}

void loop()
{
  while (gps.available( gpsPort )) {
    fix = gps.read();

    // Once every 5 seconds...    
    if (++fixCount >= 5) {
      displayInfo();
      fixCount = 0;
    }
  }

  if ((gps.statistics.chars < 10) && (millis() > 5000)) {
     Serial.println( F("No GPS detected: check wiring.") );
     while(true);
  }
}

void displayInfo()
{
  Serial.print(F("Location: ")); 
  if (fix.valid.location) {
    Serial.print( fix.latitude(), 5 );
    Serial.print( ',' );
    Serial.print( fix.longitude(), 5 );
  } else {
    Serial.print(F("INVALID"));
  }

  Serial.print(F("  Speed: ")); 
  if (fix.valid.speed) {
    Serial.print(fix.speed_kph());
    Serial.print(F(" KMPH "));
  } else {
    Serial.print(F("INVALID"));
  }

  // Shift the date/time to local time
  NeoGPS::clock_t localSeconds;
  NeoGPS::time_t  localTime;
  if (fix.valid.date && fix.valid.time) {
    using namespace NeoGPS; // save a little typing below...

    localSeconds = (clock_t) fix.dateTime; // convert structure to a second count
    localSeconds += 5 * SECONDS_PER_HOUR + 30 * SECONDS_PER_MINUTE; // shift timezone
    localTime = localSeconds;              // convert back to a structure
  }

  Serial.print(F("  Date : "));
  if (fix.valid.date) {
    Serial.print(localTime.month);
    Serial.print('/');
    Serial.print(localTime.date);
    Serial.print('/');
    Serial.print(localTime.year);
  } else {
    Serial.print(F("INVALID"));
  }

  Serial.print(F("  Time : "));
  if (fix.valid.time) {
    Serial.print(localTime.hours);
    Serial.print(':');
    if (localTime.minutes < 10) Serial.print('0');
    Serial.print(localTime.minutes);
    Serial.print(':');
    if (localTime.seconds < 10) Serial.print(F("0"));
    Serial.print(localTime.seconds);
  } else {
    Serial.print(F("INVALID"));
  }

  Serial.println();
}

此草图显示每五分之一fix,而不使用不准确的millis()或讨厌的delay()。是不是没有人有时间使用dat!

每个修复都是从每个1秒间隔的所有句子中累积出来的,无论是一个句子(RMC?)还是8个句子(GGA,GLL,RMC,GSV * 3,GSA)和VTG)。在NeoGPS中,计数修正等同于计算秒数。

注意:如果您需要超精确的5秒间隔,请考虑使用PPS引脚(如果有)。

本草图中的当地时间正确计算,即使时区转换为新的小时,日,月或年。您的草图未正确跨越日界。如果我正确阅读你的草图,可以5.5小时轮班。

我是否应该提到NeoGPS比所有其他库更小,更快,更准确? :)它可以从Arduino IDE Library Manager,菜单 Sketch - &gt;下获得。包含图书馆 - &gt;管理图书馆

您还应考虑使用SoftwareSerial以外的内容。这是非常低效的,因为它会长时间禁用中断。这可能会干扰草图的其他部分或其他库。

最好的软件串口库是AltSoftSerial。如果你可以切换到引脚8&amp; 9,我强烈建议你这样做。

如果你无法切换引脚(你确定吗?),你应该使用我的NeoSWSerial。它适用于任何两个引脚,几乎同样有效。它支持您使用的9600波特率。

答案 1 :(得分:4)

使用此库时应避免使用常规延迟功能,因为它需要定期更新。在示例代码中,您将找到一个名为smartDelay()的函数,将此函数复制到您的代码中并使用它。它看起来像这样。

static void smartDelay(unsigned long ms)
{
  unsigned long start = millis();
  do 
  {
    while (ss.available())
      gps.encode(ss.read());
  } while (millis() - start < ms);
}

将此代码放在代码底部,电话号码smartDelay(5000);,而不是delay(5000);底部的void loop() 您还应该像displayInfo();一样拨打smartDelay()这样的电话。

    void loop()
    {
      while (ss.available() > 0)
          if (gps.encode(ss.read()))

      if (millis() > 5000 && gps.charsProcessed() < 10) {
         Serial.println(F("No GPS detected: check wiring."));
         while(true);
      }
      smartDelay(5000);
      displayInfo();
    }

编辑:更好的方式

更好的方法是使用millis(),尤其是当您在显示数据时喜欢做其他事情时。这也将每5秒更精确地调用一次。 你必须在顶部声明一个变量才能工作。它看起来像这样。

long timeToDisplay = 0; // Declare this at the top

void loop()
{
  while (ss.available() > 0)
      if (gps.encode(ss.read()))

  if (millis() > 5000 && gps.charsProcessed() < 10) {
     Serial.println(F("No GPS detected: check wiring."));
     while(true);
  }
  if(timeToDisplay <= millis()) {
    timeToDisplay = millis() + 5000;
    displayInfo();
  }
}

答案 2 :(得分:2)

slash-dev给我一个关于如何从GPS获取5秒间隔数据的答案。他还提供了一些改进的方法,当我跟随时获得完美的输出。它解决了我提供的代码中的一些错误。链接到答案:

https://stackoverflow.com/a/43009260/7699452

结果: https://i.stack.imgur.com/pzvsu.png