Go Down

Topic: GPS neo 6m problem - no fix (Read 602 times) previous topic - next topic

TruliParadajz

Aug 11, 2017, 11:17 am Last Edit: Aug 11, 2017, 04:29 pm by TruliParadajz
Hello

I'm currently trying to build a clock which is based on GPS neo 6m and a Real Time Clock DS1307 and want to display it all on 6 pieces of 8x8 LED matrix.

GPS gets fix without a problem, and passes it to RTC when i print data on serial port. My problems begin when i try to display digits on matrices (8x8). GPS suddenly doesn't get any fix. I tried bringing it outside to have a clear view of sky but it didn't help.

I'm new here and started my project just recently. I lost a week trying to solve the problem, but had no luck.

Here's my code. It's a bit rough since i tried a lot of things to get it work.
Code: [Select]
#include <TinyGPS.h>
#include <SoftwareSerial.h>
#include <TimeLib.h>
#include <DS1307RTC.h>
#include "max7219.h"

#define NBR_MTX 6

TinyGPS gps;
MAX7219 max7219=MAX7219(7,6,5, NBR_MTX);

SoftwareSerial mySerial(10, 11);

const int led_pin = 13;

volatile byte sat, minuta, sekunda;
unsigned long Age;
volatile int syncCounter = 0;
int timer1_counter;
tmElements_t clockElements;

void setMatrix();

int Sat=0, Minuta=0, Sekunda=0;

void setup()
{
  Serial.begin(115200);
  mySerial.begin(9600);
  
  pinMode(led_pin, OUTPUT);

  setMatrix();

  noInterrupts();           // disable all interrupts
  TCCR1A = 0;
  TCCR1B = 0;
  timer1_counter = 59286;    //preload timer 65536-16MHz/256/10Hz
  
  TCNT1 = timer1_counter;   // preload timer
  TCCR1B |= (1 << CS12);    // 256 prescaler
  TIMSK1 |= (1 << TOIE1);   // enable timer overflow interrupt
  interrupts();
}
ISR(TIMER1_OVF_vect)        // interrupt service routine
{
  TCNT1 = timer1_counter;   // preload timer
//  printData();
  syncCounter++;
  
}

void loop()
{
//  RTC.read(clockElements);  
//  Serial.print(clockElements.Hour); Serial.print(":");
//  Serial.print(clockElements.Minute); Serial.print(":");
//  Serial.print(clockElements.Second); Serial.println();

  RTC.read(clockElements);

  
  Sat = int(clockElements.Hour);
  Minuta = int(clockElements.Minute);
  Sekunda = int(clockElements.Second);

//  printData();
  
  while (mySerial.available())
  {
    int c = mySerial.read();
    gps.encode(c);
  }
  
  gps.crack_datetime(NULL, NULL, NULL, &sat, &minuta, &sekunda, NULL, &Age);

  if (syncCounter == 10)
  {
  
    GPSsync();
      Serial.print(sat); Serial.print(":");
  Serial.print(minuta); Serial.print(":");
  Serial.print(sekunda); Serial.println();
    syncCounter = 0;
  }
}

void setMatrix()
{
  for (int i=0; i< NBR_MTX; i++){
    max7219.shutdown(i,false);
    max7219.setIntensity(i,5);
    max7219.clearDisplay(i);
  }
  max7219.clearAll();
}

void GPSsync()
{

  if( Age != TinyGPS::GPS_INVALID_FIX_TIME && Age<2000)
  {

    clockElements.Hour = sat;
    clockElements.Minute = minuta;
    clockElements.Second = sekunda;
    RTC.write(clockElements);
    static boolean output = HIGH;
    digitalWrite(led_pin, output);
    output = !output;
  }  
}

void printData()
{  


   max7219.setChar(0, char(sekunda%10));
   max7219.setChar(1, char(sekunda/10));
   max7219.setChar(2, char(minuta%10));
   max7219.setChar(3, char(minuta/10));
   max7219.setChar(4, char(sat%10));
   max7219.setChar(5, char(sat/10));
  
}

pylon

I guess that the problem is in the wiring. Please post a wiring diagram of the complete setup.

TruliParadajz

#2
Aug 11, 2017, 02:27 pm Last Edit: Aug 11, 2017, 02:32 pm by TruliParadajz
Here's wiring diagram. Sorry it's made in paint :/ .



I forgot to mention that when I hook both matrices and GPS and upload and compile, matrices show this: J 8 9 6 7 2 . In serial monitor it's 198:96:72.

EDIT: It seems i have to paste a link.
http://imgur.com/a/zyCfk

-dev

#3
Aug 11, 2017, 03:02 pm Last Edit: Aug 11, 2017, 03:03 pm by -dev
Nope, try this:

    [img]https://i.imgur.com/LBwaWWP.jpg[/img]

The image:



I don't see the RTC in your schematic.

-dev

What MAX7219 library are you using?  Post a link to the library.

TruliParadajz

#5
Aug 11, 2017, 03:26 pm Last Edit: Aug 11, 2017, 03:41 pm by TruliParadajz
Sorry about that, here's the one with RTC .



Just in case : http://imgur.com/a/6pt3U

As for the matrix library, i can't find a link. I'll try to google it and put it here as soon as possible.

EDIT: Couldn't find it so I uploaded it :

https://ufile.io/np4g6

-dev

Nope.  :D



Right-click on the image and select "Copy Image Address".  Then use it here inside [img] tags.

To attach the H and CPP files (or a zip of the whole library subdirectory), use the Attachments and other options in the lower left corner of the post editor.  We won't download files from external sites (except github).

TruliParadajz

Sorry.
Oh, and I noticed I made a mistake when pasting a code. I experimented with different pins so I left pins 10 and 11 in code and I actually use 4 and 3.

Here's the library:

-dev

#8
Aug 11, 2017, 04:31 pm Last Edit: Aug 11, 2017, 04:33 pm by -dev Reason: code for pins 4/3
I think the main problem is that you are not reading GPS data all the time.  If you wait to read it, the Arduino input buffer overflows.  Then the Arduino loses the remaining characters sent by the GPS device, until your sketch starts reading them again.  When characters are lost, GPS data will never be decoded, and you will never get an updated time.

Instead of using a timer interrupt to measure the elapsed time (and wait to read), just use millis() or (even better) use the GPS 1-second update interval.  The GPS device will send data exactly once per second.  Conveniently, that is also how often you need to update the display.  You don't need a timer nor millis(); just wait for updated GPS information.

A secondary problem could be SoftwareSerial.  SoftwareSerial disables interrupts for long periods of time, forcing your Arduino to twiddle its thumbs while each character arrives.  At 9600 baud, it could have executed 10000 instructions instead of waiting for each character.  :-/  This can interfere with other parts of your sketch, or with other libraries.  I have listed the other alternatives in the revised sketch below.

I would also suggest my NeoGPS library.  It is smaller, faster, more reliable and more accurate than all other libraries.  There are lots of tips on the Troubleshooting Page.  Picking a serial connection is also discussed here, so you might want to read that, too.

Here is a NeoGPS version of your sketch:

Code: [Select]
#include <TimeLib.h>
#undef DAYS_PER_WEEK // Bad TimeLib!
tmElements_t clockElements;

#include <Wire.h>
#include <DS1307RTC.h>


#include <max7219.h>
const int NBR_MTX = 6;
MAX7219 max7219( 7,6,5, NBR_MTX );

#include <NMEAGPS.h>
NMEAGPS gps;

//-----------
// AltSoftSerial on pins 8 & 9 would be BEST:
//#include <AltSoftSerial.h>
//AltSoftSerial gpsPort;

// NeoSWSerial is next best (any pins)
#include <NeoSWSerial.h>
NeoSWSerial gpsPort(4, 3);

// SoftwareSerial NOT RECOMMENDED
//-----------

const int LED_PIN = 13;


void setup()
{
  Serial.begin(9600);
  gpsPort.begin(9600);
  
  pinMode(LED_PIN, OUTPUT);
  
  initMatrix();
}


void loop()
{
  if (gps.available( gpsPort )) // read some GPS characters (a fix structure may become available)
  {
    gps_fix fix = gps.read();  // get all the GPS pieces of information in one fix structure

    if (fix.valid.time)
      GPSsync( fix.dateTime ); // set the RTC from the GPS time
    else
      RTC.read( clockElements ); // get the time from the RTC

    printData();

    digitalWrite( LED_PIN, !digitalRead(LED_PIN) ); // toggle LED
  }
}

void initMatrix()
{
  for (int i=0; i< NBR_MTX; i++){
    max7219.shutdown(i,false);
    max7219.setIntensity(i,5);
    max7219.clearDisplay(i);
  }
  max7219.clearAll();
}


void GPSsync( const NeoGPS::time_t & gpsTime )
{
  clockElements.Hour   = gpsTime.hours;
  clockElements.Minute = gpsTime.minutes;
  clockElements.Second = gpsTime.seconds;
  RTC.write( clockElements );
}

void printData()
{
  max7219.setChar(0, clockElements.Second % 10);
  max7219.setChar(1, clockElements.Second / 10);
  max7219.setChar(2, clockElements.Minute % 10);
  max7219.setChar(3, clockElements.Minute / 10);
  max7219.setChar(4, clockElements.Hour   % 10);
  max7219.setChar(5, clockElements.Hour   / 10);

  Serial.print(clockElements.Hour  ); Serial.print(':');
  Serial.print(clockElements.Minute); Serial.print(':');
  Serial.print(clockElements.Second); Serial.println();
}

Once I got it to compile, the original sketch uses 10780 bytes of program space and 1125 bytes of RAM.
The NeoGPS version of the sketch uses 9498 bytes of program space and 960 bytes of RAM, a significant savings.

NeoGPS, NeoSWSerial and AltSoftSerial are all available from the Arduino IDE Library Manager, under the menu Sketch -> Include Library -> Manage Libraries.  

Cheers,
/dev

TruliParadajz

Thanks, I'll give it a try (it might take time since I'm newbie :D ).

The main reason I used timer interrupt in the first place is because I'm supposed to fill RTC with GPS data every hour. and that's what my professor told me to do. He said that using millis() or delay causes time rift
(it's 1 second delay or going  second ahead).
Would you mind pointing me in the right direction now that I told you how often should I update RTC? I will try your version however :D

TruliParadajz

Well, the code you provided worked pretty well.

Can you explain why GPS doesn't get fix every second (even though blue LED is blinking) when I move printData out of that if like this:

Code: [Select]
void loop()
{
  if (gps.available( gpsPort )) // read some GPS characters (a fix structure may become available)
  {
    gps_fix fix = gps.read();  // get all the GPS pieces of information in one fix structure

    if (fix.valid.time)
      GPSsync( fix.dateTime ); // set the RTC from the GPS time
    else
      RTC.read( clockElements ); // get the time from the RTC

    printData();

    digitalWrite( LED_PIN, !digitalRead(LED_PIN) ); // toggle LED
  }
}


I tried making a timer to update every 10 secs (1st step to my goal which is 1 hour) but then it never gets a fix (blue LED still blinking).

-dev

Quote
He said that using millis() or delay causes time drift
Yes, the Arduino crystal (and therefore millis()) is not nearly as accurate as the GPS atomic clock.  Over time, you might update the RTC a little too soon or a little too late.

To do the GPSsync just once per hour, simply count the number of GPS updates you have received:

Code: [Select]
unsigned int fixCount    = 0;
unsigned int lastGPSsync = 0;
const unsigned int GPS_SYNC_INTERVAL = 10; // for testing.  Later, use NeoGPS::SECONDS_PER_HOUR;

void loop()
{
  if (gps.available( gpsPort )) // read some GPS characters (a fix structure may become available)
  {
    gps_fix fix = gps.read();  // get all the GPS pieces of information in one fix structure
    fixCount++;

    if (fix.valid.time) {
      // Get the time from the GPS fix structure
      clockElements.Hour   = fix.dateTime.hours;
      clockElements.Minute = fix.dateTime.minutes;
      clockElements.Second = fix.dateTime.seconds;

      if (fixCount - lastGPSsync > GPS_SYNC_INTERVAL) {
        lastGPSsync = fixCount;
        RTC.write( clockElements );  // set the RTC from the GPS time
      }
      
    } else {
      // Get the time from the RTC
      RTC.read( clockElements );
    }

    printData();

    digitalWrite( LED_PIN, !digitalRead(LED_PIN) ); // toggle LED
  }
}

You don't need the separate GPSsync routine any more.

Quote
Can you explain why GPS doesn't get fix every second (even though blue LED is blinking) when I move printData out of that if statement?
Yes.  The GPS device reports an update once per second, even if you don't read it because you're printing too much information.

When you move the printData call outside the if statement, you are making it print the time data as fast as it can.  Why would you do that?  loop gets called over and over, as fast as it can be called.  At 16MHz, that's tens or hundreds of thousands of times per second (depends on how much you do in loop.

The Arduino can queue up characters to print much, much faster than they can go out the Serial port.  Each character takes about 1ms to send (at 9600).  The Arduino could queue up thousands and thousands of characters during that 1ms.  However, the Arduino only has room for 64 characters for output.  When your sketch tries to send the 65th character, it can't get queued up until the first character is finally goes out, and there's room for one more character.  This is the classic Printing Too Much problem.  The Arduino spends all its time waiting to send more characters, and it eventually starts losing GPS data.

Many of the other libraries' examples print too much data, and they frequently break when you modify them.

So... don't do that.  :)

TruliParadajz

#12
Aug 11, 2017, 09:24 pm Last Edit: Aug 11, 2017, 11:12 pm by TruliParadajz
Thank you, you saved me. Now I'll just have to go through your library to at least try to understand what you did :D .

TruliParadajz

#13
Aug 26, 2017, 09:29 pm Last Edit: Aug 27, 2017, 02:12 pm by TruliParadajz
I have another problem now. I put fix.status >= gps_fix::STATUS_STD as you said but sometimes my clock goes 3 seconds ahead, an sometimes it acts normal.
I added more than few lines to fulfill requests of the project (Force sync, Time Zones, Manual Setting).

My code is in the attachment since it exceeds 9000 characters.

EDIT:
I noticed it happens less often( a bit) if I use USB power supply instead of external power supply (5V 2A).

-dev

It's probably because you are using delay, here and elsewhere:

Code: [Select]
boolean debounce(boolean last, int whichPin) //Function for debouncing
{
  boolean current = digitalRead(whichPin);
  if (last != current)
  {
    delay(5);
    current = digitalRead(whichPin);
  }
  return current;
}

I would suggest using the Bounce2 library instead of delay -- just call upButton.update() in loop (and downButton.update(), etc.), and then test for upButton.rose() or upButton.fell().

The menuProcedure routine makes the above problem even worse:

Code: [Select]
void menuProcedure()
{
  mainMenu = 0;
  while(1)

You stay in this routine, debouncing buttons, which calls delay several times.  GPS characters keep coming in, and it will eventually lose data or get a corrupted time sentence.  This is called a "blocking" approach.  The Bounce2 library would let you use a "non-blocking" approach, without delay.

Go Up