Arduino Uno with NEO GPS and OLED

OLED with GPS Data

This project displays the latitude and longitude from a GPS sensor on an OLED display using the I2C protocol.

In the video below you can see a splash screen is displayed with some information while the GPS is getting a fix on your location. When your location is known the screen updates to show the number of satellites in the fix and your latitude and longitude.

Below you can see the I2C OLED and the blox GPS sensor I used.

OLED

Blox Neo-7m

For the software I used probably the best GPS and display libraries. NeoGPS for GPS signal processing and the U8g2  monochrome graphics library for outputting to the OLED display. I used the U8x8 version of U8g2 as I only needed simple text and it’s also faster.

These are both easy to install into the Arduino IDE as they are available in the Library Manager. You also need to install the Altsoftserial.h library. To do this open the library manager (Sketch > Include Library > Manage Libraries) and search for and then install each of the following: NeoGPS, Altsoftserial and U8g2.

To see the Satellites tracked (TKD), Available (AVL) and the time (TME)  at the bottom of the screen a few edits have to be made to one of the configuration files, In Windows, find this file:
C:\Users\#YourUserName#\Documents\Arduino\libraries\NeoGPS\src\NMEAGPS_cfg.h, or just search your PC for NMEAGPS_cfg.h and uncomment the following three items (remove the preceding //):
//#define NMEAGPS_PARSE_GSV
//#define NMEAGPS_PARSE_SATELLITES
//#define NMEAGPS_PARSE_SATELLITE_INFO
and save the file.

The wiring is pretty simple as well.



Wiring for UNO, GPS and OLED

Wiring diagram for the UNO, GPS and OLED

Photo of the wiring

Photo of the wiring

The code is below

#include <NMEAGPS.h>
#include <U8x8lib.h>

//-------------------------------------------------------------------------
//  The GPSport.h include file tries to choose a default serial port
//  for the GPS device.  If you know which serial port you want to use,
//  edit the GPSport.h file.
#include <GPSport.h>


//------------------------------------------------------------
// This object parses received characters
//   into the gps.fix() data structure
static NMEAGPS  gps;


//------------------------------------------------------------
//  Define a set of GPS fix information.  It will
//  hold on to the various pieces as they are received from
//  an RMC sentence.  It can be used anywhere in your sketch.
static gps_fix  fix;

// the OLED used
U8X8_SSD1306_128X64_NONAME_HW_I2C u8x8(/* reset=*/ U8X8_PIN_NONE);


uint32_t timer;
bool screencleared = false;

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

  u8x8.begin();
  u8x8.setFont(u8x8_font_chroma48medium8_r);

  // Start up screen on OLED
  u8x8.fillDisplay();
  delay(1000);
  for (uint8_t r = 0; r < u8x8.getRows(); r++ )
  {
    u8x8.clearLine(r);
    delay(100);
  }
  delay(100);
  u8x8.println("Doing");
  delay(500);
  u8x8.println("Some");
  delay(500);
  u8x8.print("Stuff");

  timer = millis();
}

//----------------------------------------------------------------
//  This function gets called about once per second, during the GPS
//  quiet time. 

static void doSomeWork()
{
  // timer = millis(); // reset the timer

  //----------------------------------------------------------------
  //  This section is run before a fix is made to show sat info (Available, Tracked, Time)

  // Count how satellites are being received for each GNSS
  int totalSatellites, trackedSatellites;
  totalSatellites = gps.sat_count;
  for (uint8_t i = 0; i < totalSatellites; i++) {
    if (gps.satellites[i].tracked) {
      trackedSatellites++;
    }
  }

  u8x8.inverse();

  u8x8.drawString(0, 6, "TKD");
  u8x8.drawString(5, 6, "AVL");
  u8x8.drawString(10, 6, "TME");

  u8x8.noInverse();

  enum {BufSizeTracked = 3}; //Space for 2 characters + NULL
  char trackedchar[BufSizeTracked];
  snprintf (trackedchar, BufSizeTracked, "%d", trackedSatellites);
  u8x8.drawString(0, 7, "  ");
  u8x8.drawString(0, 7, trackedchar);

  enum {BufSizeTotal = 3};
  char availchar[BufSizeTotal];
  snprintf (availchar, BufSizeTotal, "%d", totalSatellites);
  u8x8.drawString(5, 7, "  ");
  u8x8.drawString(5, 7, availchar);

  if (fix.valid.time) {

    enum {BufSizeTime = 3};
    int hour = fix.dateTime.hours + 2;
    int minute = fix.dateTime.minutes;

    char hourchar[BufSizeTime];
    char minutechar[BufSizeTime];
    snprintf (hourchar, BufSizeTime, "%d", hour);
    snprintf (minutechar, BufSizeTime, "%d", minute);
    if ( hour < 10 )
    {
      snprintf (hourchar, BufSizeTime, "%02d", hour);
    }
    if ( minute < 10 )
    {
      snprintf (minutechar, BufSizeTime, "%02d", minute);
    }

    u8x8.drawString(10, 7, hourchar);
    u8x8.drawString(12, 7, ":");
    u8x8.drawString(13, 7, minutechar);
  }

  //----------------------------------------------------------------

  // Once the location is found the top part of the screen is cleared and the fix data is shown
  if (fix.valid.location) {

    if (!screencleared) // do once
    {
      int r;
      for ( int r = 0; r < 5; r++ )
      {
        u8x8.clearLine(r);
      }
      screencleared = true;
    }

    u8x8.inverse();

    u8x8.drawString(0, 0, "FIX");
    u8x8.drawString(0, 2, "LAT");
    u8x8.drawString(0, 4, "LNG");
    u8x8.noInverse();

    enum {BufSize = 3}; // Space for 2 digits
    char satchar2[BufSize];
    snprintf (satchar2, BufSize, "%d", fix.satellites);
    u8x8.drawString(4, 0, "  ");
    u8x8.drawString(4, 0, satchar2);

    char latchar[10]; // Buffer big enough for 9-character float
    dtostrf(fix.latitude(), 3, 7, latchar); // Leave room for large numbers
    u8x8.drawString(4, 2, latchar);
    char longchar[10];
    dtostrf(fix.longitude(), 3, 7, longchar);

    u8x8.drawString(4, 4, longchar);

  }

}


//  This is the main GPS parsing loop.
static void GPSloop()
{
  while (gps.available( gpsPort )) {
    fix = gps.read();
    doSomeWork();
  }
}

void loop()
{
  GPSloop();

  // until we get a fix, print a dot every 5 seconds
  if (millis() - timer > 5000 && !screencleared) {
    timer = millis(); // reset the timer
    u8x8.print(".");
  }
}



33 Replies to “Arduino Uno with NEO GPS and OLED”

  1. Gabe Noriega says:

    hi, mine is getting stuck on the “Doing Some stuff” part. i am not sure whats going on here.

    1. WordBot says:

      Hi, Is the same hardware? You can use this software to test you are receiving data – u-blox.com/en/product/u-center-windows

      1. Gabe Noriega says:

        yeah i have the same hardware but its stil not working. do i need the software in order for this to work? im a little confused on what the software doe. thanks

        1. WordBot says:

          The software takes a reading from the GPS and shows you all sorts of information. It’s the best way to check the GPS is working.

          1. Gabe Noriega says:

            ok thanks

  2. Gabe Noriega says:

    hey i got U-Center, but its not displaying anything. do you think my gps is not working?

    1. WordBot says:

      Does it show anything when you have the GPS connected? How are you connecting it?

      1. Gabe Noriega says:

        no nothing is shown. i dont think its connecting

        1. Gabe Noriega says:

          how should i connect it?

          1. WordBot says:

            You need a USB to Serial converter. I used one of these: https://www.ebay.co.uk/itm/For-Arduino-Serial-FT232RL-USB-HOT-Converter-3-3V-to-Module-Adapter-TTL-FTDI-5V-/222898816179. I chose ‘Receiver > Port’ in U-Center and it started working. There are other USB to Serial converters around but they sometimes have driver problems. You’ll see this in device manager in Windows.

  3. Abhishek says:

    Hi
    I followed through every single steps as you described using same hardware but I got an error.
    exit status 1
    ‘class NMEAGPS’ has no member named ‘sat_count’
    can you help me solving this plzz

    1. WordBot says:

      You probably just need to do this…

      find this file:
      C:\Users\#YourUserName#\Documents\Arduino\libraries\NeoGPS\src\NMEAGPS_cfg.h, or just search your PC for NMEAGPS_cfg.h and uncomment the following three items (remove the preceding //):
      //#define NMEAGPS_PARSE_GSV
      //#define NMEAGPS_PARSE_SATELLITES
      //#define NMEAGPS_PARSE_SATELLITE_INFO
      and save the file.

      1. Richard says:

        Your suggestion solved my compile issue with gps.sat_count.
        Thanks

  4. Abhishek says:

    C:\Users\#YourUserName#\Documents\Arduino\libraries\NeoGPS\src
    After this src folder shows empty
    no idea what to do

    1. WordBot says:

      What happens if you search your PC for NMEAGPS_cfg.h ? Is it Windows 10 you are using? Maybe the files are being hidden.

      You could try reinstalling the NeoGPS library.

      1. Abhishek says:

        Plzz reply something
        It is important project for me
        Thanks

        1. WordBot says:

          I can only suggest that you remove the NeoGPS folder from here: C:\Users\YOURUSERNAME\Documents\Arduino\libraries, Then install it again. I’ve posted the NMEAGPS_cfg.h file that works for me here: https://robotzero.one/wp-content/uploads/2018/04/NMEAGPS_cfg.h

  5. Mark says:

    Lat and Long will not work (stuck at the programs pre-set), and should be removed from the program. Your Time zone is easily corrected by adding or subtracting the ‘+’ number.
    If anyone responds I’ll gladly give more detail.

    To fix Lat Long…

    char latchar[10]; // Buffer big enough for 9-character float
    dtostrf(fix.latitude() + 0, 0, 7, latchar); // Leave room for large numbers
    // dtostrf(77.848544, 3, 7, latchar); // Leave room for large numbers
    u8x8.drawString(4, 2, latchar);
    char longchar[10];
    dtostrf(fix.longitude() + 0, 0, 7, longchar);
    //dtostrf(166.680378, 3, 7, longchar);

    ______________________________________________
    To fix Timezone…

    enum {BufSizeTime = 3};
    int hour = fix.dateTime.hours + 11;// was +2 TIME ZONE CORRECTION SETTING!
    int minute = fix.dateTime.minutes;

    1. WordBot says:

      Hi,
      I’ve removed the code dtostrf(77.848544, 3, 7, latchar); because it was setting the variable latchar to a hardcoded value rather than the value from the method fix.latitude to get your actual latitude. The code still worked.. you just have to be in range of enough satelites for fix.valid.location to be satisfied.

      Good point about the int hour = fix.dateTime.hours + 2;. You have to change this number to get the correct hour for your timezone to appear on the screen.

      1. Mark says:

        Hi WordBot, cheers for the reply. Only issue I am having now is the fact that when 2400 hours is reached (midnight), the time continues counting past that. ie at actual 0700 hours this morning I got a displayed time of 3000 plus hours. I had to reset my ‘TIME ZONE CORRECTION SETTING!’ (as above) to bring the time back to normal, (seconds were fine). I’m still trying to figure that one out. Any suggestions welcome from you. Other than that, wonderful piece of kit! Thankyou!

        1. WordBot says:

          I’m not sure exactly how to do it but with some maths like hours%24 modulus you can probably fix it. To do it properly you have to allow for daylight savings and all that stuff. Maybe this library will help – https://github.com/JChristensen/Timezone Bit too much extra info for this tutorial.

          1. Mark says:

            It really is a lovely little project, I’ll have to work something out to fix it, then it’ll be perfect. Perfect for my tracking telescope that demands position and time to track stars. Thankyou so much. I’m sure it’s a simple task, just gotta find the time! Cheers!

  6. Richard says:

    Got this setup running with little trouble, however there seems to be severe error in LAT and LNG. I am located at near N33.3, W86.7 but display shows LAT 56.3 and LNG 50.2. TRK = 12 AVL = 12 FIX = 9. Time is correct.
    Richard

    1. WordBot says:

      Ah.. I had put in some extra numbers so the screen and video examples didn’t show my exact lat and long. I left them in the code though so not very secret! I think this must have been added in a recent update when I pasted in some old code to fix another problem.

      This section needs to be replaced:

      char latchar[10]; // Buffer big enough for 9-character float
      dtostrf(fix.latitude(), 3, 7, latchar); // Leave room for large numbers
      u8x8.drawString(4, 2, latchar);
      char longchar[10];
      dtostrf(fix.longitude(), 3, 7, longchar);

  7. Richard says:

    Working great now!!
    Thanks

  8. Nils Petersen says:

    Hi, I’m using an Arudino Mega, I’m recieving gps-data on serial monitor,but not on my display… how can I change the gpsPort, so that it runs GPSloop() and doSomeWork()???
    Thank you for fast reply.

    1. WordBot says:

      I’ve not tried it but apparently…
      // The GPSport.h include file tries to choose a default serial port
      // for the GPS device. If you know which serial port you want to use,
      // edit the GPSport.h file.
      Options for serial pins: https://www.arduino.cc/reference/en/language/functions/communication/serial/

  9. Jason Pitcher says:

    I keep getting this when trying to compile… any ideas?

    ‘class U8X8_SSD1306_128X64_NONAME_HW_I2C’ has no member named ‘println’

    1. WordBot says:

      Hi. I’m not sure. Maybe it needs

      #include {Arduino.h}

      (replacing the {} with <>) at the top of the sketch?

      1. Jason Pitcher says:

        I’ll give it a try. Thank you!

      2. Jason Pitcher says:

        I added the statement at the top that you suggested but the issue persisted.

        I am wondering if the issue is that Im trying to compile this for an Arduino Nano Every instead of an Uno? I figured they would be the same but maybe not?

        When I review the output further, there are actually a LOT of failures:

        sketch_feb02a:46:8: error: ‘class U8X8_SSD1306_128X64_NONAME_HW_I2C’ has no member named ‘println’
        u8x8.println(“Doing”);
        ^~~~~~~
        sketch_feb02a:48:8: error: ‘class U8X8_SSD1306_128X64_NONAME_HW_I2C’ has no member named ‘println’
        u8x8.println(“Some”);
        ^~~~~~~
        sketch_feb02a:50:8: error: ‘class U8X8_SSD1306_128X64_NONAME_HW_I2C’ has no member named ‘print’
        u8x8.print(“Stuff”);
        ^~~~~

        sketch_feb02a:68:25: error: ‘class NMEAGPS’ has no member named ‘sat_count’
        totalSatellites = gps.sat_count;
        ^~~~~~~~~
        sketch_feb02a:70:13: error: ‘class NMEAGPS’ has no member named ‘satellites’; did you mean ‘parseSatellites’?
        if (gps.satellites[i].tracked) {
        ^~~~~~~~~~
        parseSatellites

        1. WordBot says:

          From what I read the code should be the same. Maybe it’s not finding the libraries. Try removing the include for the screen and see if the error message changes.

  10. Bernd says:

    Hi,
    This is a master piece.
    Thanks a lot for publishing the code.
    It took me several hours to get it up and running.
    Now with
    #include
    and defining the ports (D2/D3) it works perfectly.
    I was not able to get it running on the TX/RX ports of a Nano.
    ATGM336H module is supported 🙂

    Cheers!

Leave a Reply to WordBot Cancel reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.

scroll to top