Display wont show another screen - OLED, TINYGPS++

Hi, so I have got a small GPS SD Logger with an OLED display. Everything works fine. But know Im trying to make the OLED display a drawFail if this if is false - gps.location.isUpdated()) && (gps.location.isValid()) && (gps.altitude.isUpdated(). And I am forever stuck at the drawFail screen and dont know why. Any help would be appreciated!

// pripojeni gps knihovny
#include <SoftwareSerial.h>
#include <TinyGPS++.h>

// připojení knihovny U8glib
#include "U8glib.h"

// SD
#include <SD.h>
#include <SPI.h>

// GPS prijimac pripojeny na piny 2 (GPS TX) a 3 (GPS RX)
SoftwareSerial gpsModul(2, 3);
TinyGPSPlus gps;

// inicializace oled
U8GLIB_SSD1306_128X64 mujOled(U8G_I2C_OPT_NONE);

// inicializace SD
int CS_PIN = 10;
File file;

void setup() {
  gpsModul.begin(9600);
  initializeSD();
}

void loop() {
  // Pokud z GPS prichazeji nejaka data, posli je do knihovny TinyGPS++
  if (gpsModul.available()) {
    gps.encode(gpsModul.read());
  }

  // Pokud knihovna nasla polohu, vypis ji 
  if ((gps.location.isUpdated()) && (gps.location.isValid()) && (gps.altitude.isUpdated())) {
    mujOled.firstPage();  
    do {
      draw();
    } while( mujOled.nextPage() );

    // open the file. note that only one file can be open at a time,
    // so you have to close this one before opening another.
    // int datum = gps.date.value();
    File dataFile = SD.open("test.txt", FILE_WRITE);
  
    // if the file is available, write to it:
    if (dataFile) {
      dataFile.print(gps.location.lat(), 6);
      dataFile.print(", ");
      dataFile.print(gps.location.lng(), 6);
      dataFile.print(", ");
      dataFile.print(gps.altitude.meters());
      dataFile.print(", ");
      dataFile.print(gps.speed.kmph());
      dataFile.print(", ");
      dataFile.print(gps.date.value());
      dataFile.print(", ");
      dataFile.println(gps.time.value());
      
      dataFile.close();
    } else {
   //   Serial.println("error opening datalog.txt");
    }   
  } else {
      mujOled.firstPage();  
      do {
        drawFail();
      } while( mujOled.nextPage() );
  }
}

void draw() {
  // graphic commands to redraw the complete screen should be placed here  
  mujOled.setFont(u8g_font_unifont);
  mujOled.setPrintPos(0, 10);
  mujOled.print("Age: ");
  mujOled.print(gps.location.age());
  mujOled.setPrintPos(0, 25);
  mujOled.print("Alt: ");  
  mujOled.print(gps.altitude.meters());
  mujOled.setPrintPos(0, 40);
  mujOled.print("Sat: ");   
  mujOled.print(gps.satellites.value());
  mujOled.setPrintPos(0, 55);
  mujOled.print("Time: ");   
  mujOled.print(gps.time.value());
}

void drawFail() {
  // graphic commands to redraw the complete screen should be placed here  
  mujOled.setFont(u8g_font_unifont);
  mujOled.setPrintPos(0, 10);
  mujOled.print("Age: ");
  mujOled.print(gps.location.age());
  mujOled.setPrintPos(0, 25);
  mujOled.print("Neni signal");  
  mujOled.setPrintPos(0, 40);
  mujOled.print("Sat: ");   
  mujOled.print(gps.satellites.value());
  mujOled.setPrintPos(0, 55);
  mujOled.print("Time: ");   
  mujOled.print(gps.time.value());
}

void initializeSD() {
  //  Serial.println("Initializing SD card...");
  pinMode(CS_PIN, OUTPUT);

  if (SD.begin()) {
    mujOled.firstPage();  
    do {
      drawSDReady();
    } while( mujOled.nextPage() );
    } else
    {
    mujOled.firstPage();  
    do {
    drawSDFail();
    } while( mujOled.nextPage() );
  }
}

void drawSDFail() {
  mujOled.setFont(u8g_font_unifont);
  mujOled.setPrintPos(0, 10);
  mujOled.print("SD Fail");
}

void drawSDReady() {
  mujOled.setFont(u8g_font_unifont);
  mujOled.setPrintPos(0, 10);
  mujOled.print("SD Ready");
}

I am forever stuck at the drawFail screen and dont know why

are you sure you are stuck there or it’s working fine but the   if ((gps.location.isUpdated()) && (gps.location.isValid()) && (gps.altitude.isUpdated())) { never turns true again so you keep drawing the fail?

One way to check this out would be to add a button on a INPUT_PULLUP pin (will go low when pressed) and instead of doing the test   if ((gps.location.isUpdated()) && (gps.location.isValid()) && (gps.altitude.isUpdated())) { just do a   if (digitalRead(buttonPin) == LOW) { this way you can check a bit better what happens and decide which display function is called.

One of the idea I have about why this is failing is that when your GPS fails, because display takes time, your call at the begining

  if (gpsModul.available()) {
    gps.encode(gpsModul.read());
  }

might have missed data because you read 1 char by one char and then go display on the Oled. I would suggest you do

  while (gpsModul.available()) {
    gps.encode(gpsModul.read());
  }

to at least empty the input serial buffer

I would also add a boolean variable set to true if the error message is already on display to not display twice the fail message. The goal is to not slow down serial data acquisition for the GPS you have at the beginning of the loop. if you mess that up, then you will never get an update from your GPS and thus will be stuck in the display Fail portion of your code

Note that gps.location.isValid() returns true even when the fix is lost for some time. The isValid() method will tell you whether the object contains any valid data and is safe to query but this function only test the checksum. The way to use this is if (gps.location.isValid() && gps.location.age() < 1500). If the age is a value greater than 1500 or so, it may be a sign of a problem like a lost fix

Thank you very much for your tips! Im gonna look through them and try them out. I feel like the

if ((gps.location.isUpdated()) && (gps.location.isValid()) && (gps.altitude.isUpdated())) {

might be a problem, but was not sure, how to check it out! Thanks again

There can be issues getting location, speed and altitude at the same time with that library, because those pieces are in different sentences (see this table). It can also lose GPS data, depending on when you print information to the Serial Monitor window, the OLED display, and/or the SD data file.

I kept running into the same problems, so I wrote the smallest, fastest, most reliable and most accurate GPS library: NeoGPS. It is very careful to save all the information that comes from the GPS device, once per second. Multiple sentences will get parsed, and those pieces will be stored in one coherent fix structure. Here is a NeoGPS version of your sketch:

// připojení knihovny U8glib
#include <U8g2lib.h>
U8G2_SSD1306_128X64_NONAME_1_HW_I2C mujOled( U8G2_R0 );

// SD
#include <SdFat.h>
#include <SPI.h>
const int CS_PIN = 10;
SdFat SD;
File dataFile;

// pripojeni gps knihovny
#include <NeoSWSerial.h>
#include <NMEAGPS.h>

// GPS prijimac pripojeny na piny 2 (GPS TX) a 3 (GPS RX)
NeoSWSerial gpsModul(2, 3);
NMEAGPS     gps;
gps_fix     fix;


void setup() {
  //Serial.begin( 9600 );
  mujOled.begin();
  mujOled.setFont(u8g_font_unifont);
  gpsModul.begin(9600);
  initializeSD();
}

void loop() {
  // Pokud z GPS prichazeji nejaka data, posli je do knihovny NeoGPS
  if (gps.available( gpsModul )) {
    fix = gps.read();

    // Pokud knihovna nasla polohu, vypis ji
    mujOled.firstPage();
    do {
      draw();
    } while( mujOled.nextPage() );

    // if the file is available, write to it:
    if (fix.valid.location && fix.valid.altitude && dataFile.isOpen()) {
      dataFile.print( fix.latitude(), 6 );
      dataFile.print( F(", ") );
      dataFile.print( fix.longitude(), 6 );
      dataFile.print( F(", ") );
      dataFile.print( fix.altitude() );
      dataFile.print( F(", ") );
      if (fix.valid.speed)
        dataFile.print( fix.speed_kph() );
      dataFile.print( F(", ") );
      printDateTo( dataFile );
      dataFile.print( F(", ") );
      printTimeTo( dataFile );

      dataFile.flush(); // faster than closing and re-opening
    }
  }
}

void draw() {
  // graphic commands to redraw the complete screen should be placed here

  if (fix.valid.location && fix.valid.altitude) {
    // OK
    mujOled.setCursor(0, 25);
    mujOled.print( F("Alt: ") );
    mujOled.print( fix.altitude() );
  } else {
    // FAIL
    mujOled.setCursor(0, 10);
    mujOled.print( F("Neni signal") );
  }

  mujOled.setCursor(0, 40);
  mujOled.print( F("Sat: ") );
  if (fix.valid.satellites)
    mujOled.print( fix.satellites );

  mujOled.setCursor(0, 55);
  mujOled.print( F("Time: ") );
  printTimeTo( mujOled );
}

void initializeSD() {
  //  Serial.println( F("Initializing SD card...") );
  pinMode(CS_PIN, OUTPUT);

  const __FlashStringHelper *message;

  if (SD.begin( CS_PIN )) {
    // open the file. note that only one file can be open at a time,
    // so you have to close this one before opening another.
    dataFile = SD.open("test.txt", FILE_WRITE);
    if (dataFile.isOpen())
      message = F("SD Ready");
    else
      message = F("Error opening datalog.txt");

  } else {
    message = F("SD Fail");
  }

  //Serial.println( message );

  mujOled.firstPage();
  do {
    mujOled.setFont(u8g_font_unifont);
    mujOled.setCursor(0, 10);
    mujOled.print( message );
  } while( mujOled.nextPage() );
}

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

void printDateTo( Print & to )
{
  if (fix.valid.date) {
    to.print( fix.dateTime.full_year() );
    to.print( '-' );
    if (fix.dateTime.month < 10)
      to.print( '0' );
    to.print( fix.dateTime.month );
    to.print( '-' );
    if (fix.dateTime.date < 10)
      to.print( '0' );
    to.print( fix.dateTime.date );
  }

} // printDate

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

void printTimeTo( Print & to )
{
  if (fix.valid.time) {
    if (fix.dateTime.hours < 10)
      to.print( '0' );
    to.print( fix.dateTime.hours );
    to.print( ':' );
    if (fix.dateTime.minutes < 10)
      to.print( '0' );
    to.print( fix.dateTime.minutes );
    to.print( ':' );
    if (fix.dateTime.seconds < 10)
      to.print( '0' );
    to.print( fix.dateTime.seconds );
  }

} // printTime

Note that it uses several newer libraries:

  • SdFat, not the old SD library that is part of the IDE

  • u8g2, not the old u8g

These new libraries have many bug fixes and improvements that you should be using. u8g2 uses a little more program space and RAM, but NeoGPS saves about the same amount, so your original sketch and this sketch are about the same size (program space and RAM).

The sketch also avoids using SoftwareSerial. It is very inefficient, because it disables interrupts for long periods of time. This will interfere with other parts of your sketch, or with other libraries. Read this for alternatives. The above sketch uses NeoSWSerial so you can use the same pins, but Serial (pins 0 & 1, which would save program space and RAM) or AltSoftSerial (pins 8 & 9) would be even better. Strongly recommended.

It saves RAM by using the F() macro around “double-quoted” strings that you print.

If you want to try it, NeoGPS, AltSoftSerial, NeoSWSerial, u8g2 and SdFat are all available from the Arduino IDE Library Manager, under the menu Sketch → Include Library → Manage Libraries.

Cheers,
/dev