Friday, 8 May 2015

Arduino GPS clock with MAX7219 driven 7 segment displays.

This is the start of an on-going time standard project.

The idea is to have three clocks, one MSF clock being received from the NPL on 60KHz, one DCF77 clock on 77.5 KHz and one GPS clock.

This is the first part of the clock, the GPS clock.

I'll be using the Ublox NEO receiver I've used before in the fast GPS-logger project here. The receiver is configured in exactly the same manner as described in that article, so pop along there and learn how to use u-center to configure your receiver.

I purchased some great MAX7219 display boards from eBay. There was a bit of a wait, as they came from China, but they were much less expensive than others, and the build quality is OK.

 Note on some board the CS pin is marked up LOAD.

I'm not going to draw a schematic, as it really is simple. (Leave me a comment if you get stuck!)
The wiring is commented in the code below.

The code is also simple, we're not doing anything particularly clever. There's a bit in the code which writes "no GPS" on the display, and this is defined as a bitmap. The bitmap refers to the segments as follows:

Bit 1(LSB) is G
Bit 2 is F
Bit 3 is E
Bit 4 is D
Bit 5 is C
Bit 6 is B
Bit 7 is A
and finally, bit 8 is the decimal point.

Here's a quick video of the clock in action:

... and here's the code:-

 7-segment GPS clock 
 Version 0.9
 Written by Andy Doswell 2015
 License: The MIT License (See full license at the bottom of this file)
 Schematic can be found at
 You will need the LedControl library from
 and the very excellent TinyGPS++ library from
 pin 12 is connected to the DataIn 
 pin 11 is connected to the CLK 
 pin 10 is connected to CS
 pin 0 (RX)  is connected to the GPS TX pin

#include "LedControl.h"
#include <TinyGPS++.h>

LedControl lc=LedControl(12,11,10,1); //Tells LedControl where our hardware is connected.
TinyGPSPlus gps; //TinyGPS++ class
static const uint32_t GPSBaud = 57600; //Our GPS baudrate. 
int hourTen; //tens of hours
int hourUnit; //units of hours
int minTen; // you get the idea..
int minUnit;
int secTen;
int secUnit;
int centTen; //centiseconds.
int centUnit;
unsigned long timer =0;

void setup() {

  lc.shutdown(0,false); // Wake up the MAX 72xx controller 
  lc.setIntensity(0,8); // Set the display brightness
  lc.clearDisplay(0); //Clear the display
  Serial.begin(GPSBaud); // start the comms with the GPS Rx

// This contains the bit patterns for the "no GPS" display.

void displayNoGPS() { // displays "noGPS" if the GPS lock isn't valid

void displayNoSerial() { // Displays "noSeriAL" in the event of serial comms fail.

// Displays the time on our LEDs
void displayTime() { 
  if (gps.time.isValid()) {
    timer = millis(); // reset the serial comms timer
    hourUnit = (gps.time.hour()%10);
    hourTen = ((gps.time.hour()/10)%10);
    minUnit = (gps.time.minute()%10);
    minTen = ((gps.time.minute()/10)%10);
    secUnit = (gps.time.second()%10);
    secTen = ((gps.time.second()/10)%10);
    secUnit = (gps.time.second()%10);
    secTen = ((gps.time.second()/10)%10);
    centUnit = (gps.time.centisecond()%10);
    centTen = ((gps.time.centisecond()/10)%10);
    lc.setDigit (0,7,hourTen,false);
    lc.setDigit (0,6,hourUnit,false);
    lc.setDigit (0,5,minTen,false);
    lc.setDigit (0,4,minUnit,false);
    lc.setDigit (0,3,secTen,false);
    lc.setDigit (0,2,secUnit,false);
    lc.setDigit (0,1,centTen,false);
    lc.setDigit (0,0,centUnit,false);
    delay (2000);

void loop()
  // If the GPS data is OK, then display it. If not display "no GPS"
  while (Serial.available() > 0)
    if (gps.encode({
  if (millis() > timer+1000 ) // detects if the serial comms has failed.


 * Copyright (c) 2015 Andrew Doswell
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.


  1. Wow great project which I have recreated using the same components. However, I cannot get the milliseconds to show anything other than 00 whereas in your video they increment in 25ms chunks.

    Is there any chance you would be able to add some code to show the milliseconds counting from 00 to 99?

  2. Hi Ian,

    This is most likely a problem with the GPS receiver module, as the code simply reads the data as and when it's available. If you load up the uBlox uCenter software, click on the CFG drop down, as detailed in my post at , and check the rate is set to 250mS (or as fast as your chipset allows). Don't forget to upload this CFG back up to the uBlox chipset afterwards, the dtails are in the GPS logger post.... Sadly you won't get 0-99(9?)mS... the uBlox chipset just won't go that fast. I suppose we could implement a counter using an interrupt to check the arduino millis timer, and adjust it every 250mS or so, but that's a job for another day! Let me know how you get on!

  3. Hi Andy,

    Many thanks for your response and especially the link to your datalogger project. Using the uBlox uCenter software my gps module was set to 1000ms measurement period and 9600 baud. I experimented with lower values down to 200ms and varying baud rates but all these resulted in data errors and satellite fix times of over an hour in some combinations so I have left things as per the default. I have ordered another gps module (at £7 it's worth a shot) in the hope that it is more forgiving of settings etc. Thanks again.

  4. Hi Ian,

    You must retain the baud rate at 56700, otherwise you will need to change the GPSBaud variable in the sketch to suit. I chose I higher baud rate than 9600 just to ensure we've got enough time to process the data before the next 100mS. Although there's plenty of time in reality. It should also be noted that you cannot programme the arduino with the GPS receiver connected, as the hardware serial interface used is shared between the FTDI (or ATMEGA) USB interface on the Arduino itself, and , undoubtedly, you'll have bad luck because the GPS will attempt to transmit the instant your IDE tries to upload. I wonder if these are the sources of errors you are seeing?
    £7 , what's not to like!

  5. Hi Andy,

    My new gps module has arrived and it accepted a measurement period of 200ms and baud rate of 57600 1st time with fast fix and lock so it would appear my original gps module is a bit suspect.

    Again many thanks for your superb project.

  6. Hi!
    How to adjust time zone?

    1. Now there's a question.... This isn't as straightforward as you'd think, especially if you are trying to use daylight saving time... See the timelord library I used in the pond pump controller... or "isBST" function in the Arduino analogue clock post.

  7. I have tried to incorporate a +9:30 time offset into this wonderfully concise and effective code without spoiling it to display my local time. Even though the modifications compile,UTC continues to be displayed.Is there an effective way to add minor adjustments without denigrating your code?

    1. The gps. values are fixed by the library. You'll have to copy them into another bunch of hh,mm and ss variables and manipulate those, and deal with rolling over of the hours etc. I'm not sure how much of time.h is implemented in arduino (I've never relied on it) , but there is a UTC to localtime function listed. I'd start there.