GPS neo 6m problem - no fix

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.

#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));
  
}

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

Here's wiring diagram. Sorry it's made in paint :confused: .

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.

Nope, try this:

    [nobbc]![](upload://oeyPyC1Pz4FW9Oya9j3b19i2f99.jpeg)[/nobbc]

The image:

I don't see the RTC in your schematic.

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

Sorry about that, here's the one with RTC .

Just in case : Imgur: The magic of the Internet

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

Nope. :smiley:

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).

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:

max7219-master.zip (7.58 KB)

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:

#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

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

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 :smiley:

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:

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).

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:

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.

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. :slight_smile:

Thank you, you saved me. Now I'll just have to go through your library to at least try to understand what you did :smiley: .

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).

Project_Atomic_Clock_Filip.zip (2.93 KB)

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

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:

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.

Ok, I put Bounce2, but the problem still occurs sometimes (mostly when I plug it in, but sometimes later). When I use ForceSync, the time is sometimes late 1 sec (3 out of 5 times).
After just being plugged in, the even the data directly from GPS (fixed values) are 3 seconds ahead and that lasts about half a minute. What's wrong here? :open_mouth:
EDIT:
When using ForceSync, which I think caused delay of 3 sec, I opened Serial Monitor and noticed fixed values (values directly from GPS) are still precise - the time is late on matrices.
EDIT2:
After a few tries it's official that Force Synchronization causes time rift which is mostly 3 sec. I don't know what I'm doing wrong in the routine.

Project_Atomic_Clock_With_Bounce2.zip (3.13 KB)

What's wrong here? :open_mouth:

It's like I said before, you are staying inside menuProcedure until all the buttons have been pressed. You can't stay in a while loop without losing the current time. Either GPS data gets dropped (input buffer overflow because gps.available doesn't get called) or you don't have the latest clockElements (from the RTC or the GPS).

You have to use the Bounce2 library to constantly check for button presses. When a "significant" button press happens, you can change some variables based on the current state (e.g., displaying time or displaying sub menus). And then return immediately, until another button is pressed. This is a Finite-State Machine.

Because this is an assignment, I'm trying not to just give the answer, so here's some clues:

First, always call menuProcedure from loop. If no button presses have happened, it will just quickly return:

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 (mainMenu == -1) {
      printData();
    } // else
    //   menuProcedure() is doing something
  }

  // Check all the buttons, all the time
  debouncerMenu.update();
  debouncerUp.update();
  debouncerDown.update();
  debouncerOK.update();

  menuProcedure();
}

Now make menuProcedure use the current state to determine how button presses change that state. Let's say one state variable is mainMenu. It can be -1 to mean "no menu displayed, just show the time" (used in loop to call printData). Values 0..4 can represent displaying your "MENI", "SYNC"... "EXIT" menus. It looks like you want the MENU button to always start the MENU display, so that can be one of the first tests in menuProcedure:

void menuProcedure()
{
  // Whenever the MENU button is pressed, start at menu 0 (the submenus).
  if (debouncerMenu.rose())
    Menu0();

The Menu0 function can display the word MENI and set some state variables:

void Menu0() //Menu
{
       max7219.clearDisplay(0);
       max7219.setCharString(1, 'I');
       max7219.setCharString(2, 'N');
       max7219.setCharString(3, 'E');
       max7219.setCharString(4, 'M');
       max7219.clearDisplay(5);
       mainMenu = 0;
       choice   = 0;
}

The choice variable will be used to track which sub menu is displayed when the OK button is pressed. Now you can make menuProcedure scroll through the choices when a button is pressed:

void menuProcedure()
{
  // Whenever the MENU button is pressed, start at menu 0 (the submenus).
  if (debouncerMenu.rose())
    Menu0();

  // Do something based on the current menu (-1 means no menu)
  switch (mainMenu) {

    case -1: // just displaying time in loop
      break;

    case 0: // menu choices
      if (debouncerUp.rose())
      {
        choice++;   //Going up the menu
        if (choice > MAX_MENU) {
          choice = 0;
        }
        showSubMenu();
      }

When the UP button is pressed, the showSubMenu function will display the new choice:

void showSubMenu()
{
  switch (choice) {
    case 0:
      Menu0(); //Shows "MENU", has no real function
      break;
    case 1:
      Menu1(); //Shows hour in current zone
      break;
  ...

It doesn't wait for the next button press inside this function. It just displays the new choice and returns. This allows the GPS/RTC processing to continue, even though the time is not being displayed.

menuProcedure will get called again very soon, and it keeps checking for xxxButton.rose(). If the buttons aren't pressed, it just returns. When the OK button is pressed, it will change some of the state variables:

void menuProcedure()
{
  // Whenever the MENU button is pressed, start at menu 0 (the submenus).
  if (debouncerMenu.rose())
    Menu0();

  // Do something based on the current menu (-1 means no menu)
  switch (mainMenu) {

    case -1: // just displaying time in loop
      break;

    case 0: // menu choices
      if (debouncerUp.rose())
      {
        choice++;   //Going up the menu
        if (choice > MAX_MENU) {
          choice = 0;
        }
        showSubMenu();
      }
      if (debouncerDown.rose())

          ... similar for the down button ...

      if (debouncerOK.rose()) {
        mainMenu = choice;
        okSubMenu();
      }

When a submenu is finally selected, you may want to update the display with choices specific to that submenu. For example, if the ZONE submenu is selected, display the zone. And if the EXIT menu is selected, reset the state variables so that loop displays the time again:

void okSubMenu()
{
  switch (mainMenu) {
    case 1:  // select a time ZONE
      showZone();
      break;
          ...
    case 4: // EXIT back to time display
      mainMenu = -1;
      printData();
      break;
  }
} // okSubMenu

This approach is very efficient and allows your sketch to do multiple things at the same time (see the Useful Links page). In your case, your sketch needs to process GPS characters, update the RTC and watch for button presses, all at the same time. By making all the routines check for something instead of waiting for something, it looks like it is doing many things at the same time.

Unfortunately, this turns your code "inside-out". This is a common problem when converting from a blocking approach (delays and while loops that force a sequential process) into a non-blocking approach (constant tests for important events, without delay or while).