GPS data won't display in i2c SSD1306

Hello everybody

i'm currently building a GPS speedometer for my motorcycle. and for that i'm using a NEO-6m GPS module and a SSD1306 i2c display.

The problem i'm having is that, when i'm implementing the GPS in the final, and otherwise functional code, it stops working.
the display writes everything on the display, but all the places it's suppost to write some sort of GPS data, it just display zeros.

Can you please help me?

#include <SPI.h>
#include <Wire.h>
#include <TinyGPS++.h>
#include <SoftwareSerial.h>
#include <Adafruit_SSD1306.h>

#define OLED_RESET 4
Adafruit_SSD1306 Display(OLED_RESET);

static const int RXPin = 4, TXPin = 3;

// The TinyGPS++ object
TinyGPSPlus gps;

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

int r = 0;        //variable for fuel gauge
int G1 = 0;       //gear 1
int G2 = 1;       //gear 2
int G3 = 2;       //gear 3
int G4 = 5;       //gear 4
int G5 = 6;       //gear 5

void setup()   {

  ss.begin(9600);

  pinMode(G1, INPUT);           // set G1 to input
  pinMode(G2, INPUT);           // set G2 to input
  pinMode(G3, INPUT);           // set G3 to input
  pinMode(G4, INPUT);           // set G4 to input
  pinMode(G5, INPUT);           // set G5 to input
  digitalWrite(G1, HIGH);       // activate pullup risistor
  digitalWrite(G2, HIGH);       // activate pullup risistor
  digitalWrite(G3, HIGH);       // activate pullup risistor
  digitalWrite(G4, HIGH);       // activate pullup risistor
  digitalWrite(G5, HIGH);       // activate pullup risistor

  Display.begin(SSD1306_SWITCHCAPVCC, 0x3C);  // initialize with the I2C addr 0x3C (for the 128x32)

  Display.clearDisplay();
  Display.display();

  DrawConstants();

}


void loop() {

    if (ss.available() > 0){                //following code display the GPS part
    if (gps.encode(ss.read()))
    {
     int SPEED = gps.speed.kmph();          //convert the speed to zero decimal
     Display.setTextSize(1);
     Display.setTextColor(WHITE);
     Display.setCursor(0, 9);
     Display.println("Clock");
     Display.setCursor(34, 9);
     Display.println(gps.time.hour()+2);
     Display.setCursor(44, 9);
     Display.println(":");
     Display.setCursor(48, 9);
     Display.println(gps.time.minute());
     Display.setTextSize(5);
     Display.setTextColor(WHITE);
     Display.setCursor(0, 28);
     Display.println(SPEED);
     Display.setTextSize(1);
     Display.setCursor(0, 18);
     Display.println("Sat");
     Display.setCursor(34, 18);
     Display.println(gps.satellites.value());
     Display.display();
    }
    }
  
  r = analogRead(A0);                     //the following code display the fuel gauge
  r = r / 7.98;

  //draw the bar graph
  Display.fillRect(r + 30, 0, 97 - r, 4, BLACK); // (r, 50, 128 - r, 10, BLACK)
  Display.fillRect(30, 0, r, 4, WHITE); //(3, 50, r, 10, WHITE)
  Display.drawPixel(54, 4, 1);
  Display.drawPixel(78, 4, 1);
  Display.drawPixel(79  , 4, 1);
  Display.drawPixel(103, 4, 1);

  if (digitalRead (G1) == 0)              //the last bit of code display the gear i'm in
{
  Display.fillRect(100,25,20,30,BLACK);
  Display.setTextSize(3);
  Display.setTextColor(WHITE);
  Display.setCursor(100, 25);
  Display.println("1");
}

else if (digitalRead (G2) == 0)
{
  Display.fillRect(100,25,20,30,BLACK);
  Display.setTextSize(3);
  Display.setTextColor(WHITE);
  Display.setCursor(100, 25);
  Display.println("2");
}

else if (digitalRead (G3) == 0)
{
  Display.fillRect(100,25,20,30,BLACK);
  Display.setTextSize(3);
  Display.setTextColor(WHITE);
  Display.setCursor(100, 25);
  Display.println("3"); 
}

else if (digitalRead (G4) == 0)
{
  Display.fillRect(100,25,20,30,BLACK);
  Display.setTextSize(3);
  Display.setTextColor(WHITE);
  Display.setCursor(100, 25);
  Display.println("4");
}

else if (digitalRead (G5) == 0)
{
  Display.fillRect(100,25,20,30,BLACK);
  Display.setTextSize(3);
  Display.setTextColor(WHITE);
  Display.setCursor(100, 25);
  Display.println("5");
}

else
{
  Display.fillRect(100,25,20,30,BLACK);
  Display.setTextSize(3);
  Display.setTextColor(WHITE);
  Display.setCursor(100, 25);
  Display.println("N");
}

  // now that the display is build, display it...
  Display.display();
}

void DrawConstants(void) {                  // this void display the constants on the display

  Display.setTextSize(1);
  Display.setTextColor(WHITE);
  Display.setCursor(90, 57);
  Display.println("Km/h");

  Display.setTextSize(1);
  Display.setTextColor(WHITE);
  Display.setCursor(96, 10);
  Display.println("Gear");

  Display.drawRect(29,0,99,6,1);
  
  Display.setTextSize(1);
  Display.setTextColor(WHITE);
  Display.setCursor(0, 0); //19
  Display.println("Fuel");
  Display.display();

}

Several things are interfering with the sketch:

  • Every time through loop, you draw everything. This takes a long time, and it will cause you to lose GPS characters (read this).

  • You are using SoftwareSerial, which is very inefficient. It disables interrupts for the entire time that GPS characters are received. This prevents your sketch from doing anything else, like updating the display.

To address the first problem, make sure you only draw the screen when something changes: a new GPS speed comes in (once per second), or a button changes state. You should also "debounce" the switches.

To address the second problem, use my NeoSWSerial library. It is much more efficient than SoftwareSerial, because it lets the Arduino continue to do things while GPS characters are received. It would be even better to use AltSoftSerial, but you would have to put the GPS on pins 8 (to GPS TX) and 9 (to GPS RX).

Actually, since you aren't using Serial for debug statements, why not connect the GPS TX pin to Arduino RX pin 0? That would be the BEST way to connect the GPS. You could still print debug statements, but you would have to disconnect the GPS from the RX pin to upload a new sketch.

I would also suggest taking a look at my NeoGPS library. It's smaller, faster, more accurate and more reliable than all other libraries. It can be configured to parse only the fields and sentences that you really use in your sketch (e.g., just time, speed & satellites). Everything else is quickly skipped.

NeoGPS also marks the individual GPS pieces with a valid flag. You may be displaying data that is not really valid.

Here are some projects that used NeoGPS, NeoSWSerial and an SSD1306 display:

Here is a NeoGPS+NeoSWSerial version of your sketch:

#include <SPI.h>
#include <Wire.h>
#include <NMEAGPS.h>
#include <NeoSWSerial.h>
#include <Adafruit_SSD1306.h>

#define OLED_RESET 4
Adafruit_SSD1306 Display(OLED_RESET);

static const int RXPin = 4, TXPin = 3;

// The NeoGPS object
NMEAGPS gps;

// The serial connection to the GPS device
NeoSWSerial gpsPort(RXPin, TXPin);

int r = 0;        //variable for fuel gauge
int G1 = 0;       //gear 1
int G2 = 1;       //gear 2
int G3 = 2;       //gear 3
int G4 = 5;       //gear 4
int G5 = 6;       //gear 5

void setup()   {

  gpsPort.begin(9600);

  pinMode(G1, INPUT);           // set G1 to input
  pinMode(G2, INPUT);           // set G2 to input
  pinMode(G3, INPUT);           // set G3 to input
  pinMode(G4, INPUT);           // set G4 to input
  pinMode(G5, INPUT);           // set G5 to input
  digitalWrite(G1, HIGH);       // activate pullup risistor
  digitalWrite(G2, HIGH);       // activate pullup risistor
  digitalWrite(G3, HIGH);       // activate pullup risistor
  digitalWrite(G4, HIGH);       // activate pullup risistor
  digitalWrite(G5, HIGH);       // activate pullup risistor

  Display.begin(SSD1306_SWITCHCAPVCC, 0x3C);  // initialize with the I2C addr 0x3C (for the 128x32)

  Display.clearDisplay();
  Display.display();

  DrawConstants();

}


void loop() {

  if (gps.available( gpsPort )){          //following code display the GPS part
    gps_fix fix = gps.read();

    // Make sure we have a valid time
    if (fix.valid.time) {
      adjustTime( fix.dateTime ); // to local time
      Display.setTextSize(1);
      Display.setTextColor(WHITE);
      Display.setCursor(0, 9);
      Display.println( F("Clock") );
      Display.setCursor(34, 9);
      Display.println(fix.dateTime.hours);
      Display.setCursor(44, 9);
      Display.println( ':' );
      Display.setCursor(48, 9);
      Display.println(fix.dateTime.minutes);
    }

    // Make sure we have a valid speed value (low speeds not accurate)
    if (fix.valid.speed && (fix.speed_kph() > 5)) {
      int SPEED = fix.speed_kph();          //convert the speed to zero decimal
    
      Display.setTextSize(5);
      Display.setTextColor(WHITE);
      Display.setCursor(0, 28);
      Display.println(SPEED);
      Display.setTextSize(1);
      Display.setCursor(0, 18);
      Display.println( F("Sat") );
      Display.setCursor(34, 18);
      Display.println(fix.satellites);
      Display.display();
    }

    // Only check once per second, when a new GPS fix is available
    r = analogRead(A0);                     //the following code display the fuel gauge
    r = r / 7.98;

    //draw the bar graph
    Display.fillRect(r + 30, 0, 97 - r, 4, BLACK); // (r, 50, 128 - r, 10, BLACK)
    Display.fillRect(30, 0, r, 4, WHITE); //(3, 50, r, 10, WHITE)
    Display.drawPixel(54, 4, 1);
    Display.drawPixel(78, 4, 1);
    Display.drawPixel(79  , 4, 1);
    Display.drawPixel(103, 4, 1);

    if (digitalRead (G1) == 0)              //the last bit of code display the gear i'm in
    {
      Display.fillRect(100,25,20,30,BLACK);
      Display.setTextSize(3);
      Display.setTextColor(WHITE);
      Display.setCursor(100, 25);
      Display.println( '1' );
    }

    else if (digitalRead (G2) == 0)
    {
      Display.fillRect(100,25,20,30,BLACK);
      Display.setTextSize(3);
      Display.setTextColor(WHITE);
      Display.setCursor(100, 25);
      Display.println( '2' );
    }

    else if (digitalRead (G3) == 0)
    {
      Display.fillRect(100,25,20,30,BLACK);
      Display.setTextSize(3);
      Display.setTextColor(WHITE);
      Display.setCursor(100, 25);
      Display.println( '3' ); 
    }

    else if (digitalRead (G4) == 0)
    {
      Display.fillRect(100,25,20,30,BLACK);
      Display.setTextSize(3);
      Display.setTextColor(WHITE);
      Display.setCursor(100, 25);
      Display.println( '4' );
    }

    else if (digitalRead (G5) == 0)
    {
      Display.fillRect(100,25,20,30,BLACK);
      Display.setTextSize(3);
      Display.setTextColor(WHITE);
      Display.setCursor(100, 25);
      Display.println( '5' );
    }

    else
    {
      Display.fillRect(100,25,20,30,BLACK);
      Display.setTextSize(3);
      Display.setTextColor(WHITE);
      Display.setCursor(100, 25);
      Display.println( 'N' );
    }

    // now that the display is build, display it...
    Display.display();
  }
}

void DrawConstants(void) {                  // this void display the constants on the display

  Display.setTextSize(1);
  Display.setTextColor(WHITE);
  Display.setCursor(90, 57);
  Display.println( F("Km/h") );

  Display.setTextSize(1);
  Display.setTextColor(WHITE);
  Display.setCursor(96, 10);
  Display.println( F("Gear") );

  Display.drawRect(29,0,99,6,1);
  
  Display.setTextSize(1);
  Display.setTextColor(WHITE);
  Display.setCursor(0, 0); //19
  Display.println( F("Fuel") );
  Display.display();

}

static void adjustTime( NeoGPS::time_t & UTCtime )
{
  NeoGPS::clock_t seconds = UTCtime;

  // Set these values to the offset of your timezone from GMT
  static const int32_t         zone_hours   = +2L;
  static const int32_t         zone_minutes =  0L;
  static const NeoGPS::clock_t zone_offset  =
                    zone_hours   * NeoGPS::SECONDS_PER_HOUR +
                    zone_minutes * NeoGPS::SECONDS_PER_MINUTE;

  seconds += zone_offset; // adjust!

  UTCtime = seconds; // convert back to a structure
}

The original sketch uses 17458 bytes of program space and 1765 bytes of RAM.
The NeoGPS+NeoSWSerial version uses 17714 bytes of program space and 1546 bytes of RAM, a significant savings.

Notice that it only updates the display when a new GPS fix arrives, once per seconds. It adjusts the local time correctly, possibly going into a different day with the offset. It also uses the F macro to save a little RAM.

If you want to try it, NeoGPS is available from the Arduino IDE Library Manager, under the menu Sketch -> Include Library -> Manage Libraries.

Cheers,
/dev

Oh, yes... be sure to try a simple echo test:

#include <NeoSWSerial.h>
NeoSWSerial gpsPort(RXPin, TXPin);

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

void loop()
{
  if (gpsPort.available())
    Serial.write( gpsPort.read() );
}

If you see something like

   $GPRMC,123519,A,4807.038,N,01131.000,E,022.4,084.4,230394,003.1,W*6A

... then you have connected the GPS properly, the baud rate is connect, and the GPS is receiving satellite signals (reception & antenna ok).

If you see something like

   $GPRMC,123519,V,,,,,,,230394,,*33

... then the wires and baud rate are ok, but it is not receiving any satellite signals. Move closer to a window or go outside. The first fix can take 15 minutes or more. NMEAsimple.ino should show empty fields until it gets a fix.

Much of this is described on the Troubleshooting page.