GPS Module and OLED - Don't play well together

Hey guys,

I have a sketch that is supposed to get the speed from a NEO-6M GPS module and display it on a i2c OLED display.

The code that reads the GPS data works fine (separately) and outputs the speed to the serial monitor. The code that displays the variable on the OLED also works fine by itself but when I have the two together in the same sketch, nothing shows up on the OLED and the serial monitor stops updating.

Anyone have any ideas on what I’m doing wrong?

#include <TinyGPS++.h>
#include <SoftwareSerial.h>
#include <Wire.h>
#include <Adafruit_SH1106.h>
#define OLED_RESET -1
Adafruit_SH1106 display(OLED_RESET);

int RXPin = 8;
int TXPin = 9;
float GPS_speed;

TinyGPSPlus gps;
SoftwareSerial gpsSerial(RXPin, TXPin);

void setup()
{
  Serial.begin(9600);
  gpsSerial.begin(9600);
  display.begin(SH1106_SWITCHCAPVCC, 0x3C);
  display.clearDisplay();
  display.display();
}


void loop()
{
  // This sketch displays information every time a new sentence is correctly encoded.
  while (gpsSerial.available() > 0)
    if (gps.encode(gpsSerial.read()))
      displayInfo();
      displayOLED();   


  // If 5000 milliseconds pass and there are no characters coming in
  // over the software serial port, show a "No GPS detected" error
  if (millis() > 5000 && gps.charsProcessed() < 10)
  {
    Serial.println("No GPS detected");
    while(true);
  }
}

void displayInfo()
{
  if (gps.location.isValid())
  {
    GPS_speed = gps.speed.kmph();
    Serial.print("Speed (km/h): ");
    Serial.println(GPS_speed,1); // Speed in kilometers per hour (double) 
  }
  else
  {
    Serial.println("GPS: N/A");
  }
  delay(1000);
}

void displayOLED()
{
  if (gps.location.isValid())
  {
    GPS_speed = gps.speed.kmph();

    display.setTextColor(WHITE);
    display.setTextSize(2);
    display.setCursor(40, 40);
    display.print(GPS_speed,0);
    display.display();
      }
   delay(1000);
}

And the key bit of information please, which Arduino are you using ?

What do you see on the Serial monitor and how soon after a reset does the output to the Serial monitor stop ? Is it after about 5 seconds ?

void loop()
{
   // This sketch displays information every time a new sentence is correctly encoded.
   while (gpsSerial.available() > 0)
      if (gps.encode(gpsSerial.read()))
         displayInfo();  // this ends the while and if statements
   displayOLED(); // this is executed unconditionally every time through loop()

Here is your loop() function after using the IDE autoformat tool. Note that, without curly brackets on the while or if, the displayOLED() function runs every time through loop. There is a 1 second delay in that function that will be called every time. Is that intended?

There are several obvious errors, the delays of course, updating the display all the time, the use of serial prints to the console and of course the way the program is using GPS encode.

Not forgetting that if its a ATMega328P, its unlikley to work reliably at all.

srnet:
And the key bit of information please, which Arduino are you using ?

I'm using an UNO right now but want to move this to a Pro Mini for the final version.

UKHeliBob:
What do you see on the Serial monitor and how soon after a reset does the output to the Serial monitor stop ? Is it after about 5 seconds ?

It's hard to say exactly but that looks about right...

groundFungus:

void loop()

{
  // This sketch displays information every time a new sentence is correctly encoded.
  while (gpsSerial.available() > 0)
      if (gps.encode(gpsSerial.read()))
        displayInfo();  // this ends the while and if statements
  displayOLED(); // this is executed unconditionally every time through loop()




Here is your loop() function after using the IDE autoformat tool. Note that, without curly brackets on the while or if, the displayOLED() function runs every time through loop. There is a 1 second delay in that function that will be called every time. Is that intended?

Ok, thanks, I didn't realize. How do I make them both be in the same if loop? I can't figure out how to put the brackets.

srnet:
There are several obvious errors, the delays of course, updating the display all the time, the use of serial prints to the console and of course the way the program is using GPS encode.

Not forgetting that if its a ATMega328P, its unlikley to work reliably at all.

I'll admit that I'm kind of a hack and a newbie and I'm just learning as I go so I'm not surprised that I'm making mistakes.

How do I make them both be in the same if loop? I can't figure out how to put the brackets.

In the same if statement:

if (gps.encode(gpsSerial.read())) {
   displayInfo();  
   displayOLED(); 
   }

The while loop needs its own set of curly braces.

czm8:
I'm using an UNO right now but want to move this to a Pro Mini for the final version.

So you are getting the same warning I get with the code;

Sketch uses 17912 bytes (55%) of program storage space. Maximum is 32256 bytes.
Global variables use 1862 bytes (90%) of dynamic memory, leaving 186 bytes for local variables. Maximum is 2048 bytes.
Low memory available, stability problems may occur.

I have a version of the same code, a GPS speedo

You may want to look at the u8g2 library for driving the display, it can work using much less memory if you do not use a display buffer.

The mistakes I can spot are;

  1. The use of gps.encode(gpsSerial.read() means that you are attempting to update the display for every NMEA sentence received. But only 2 of the 10 or so NMEA sentences typically sent every second have location\speed data. So most of the display updates are a waste of time.

  2. The delays in the display updates, combined with 1 above, means that you can get into a situation where you are always missing the NMEA sentence that contains the wanted location\speed data.

  3. Using Serial.print whilst software serial is running can cause software serial to miss characters, this corrupts a NMEA senetence and so you can also get into the situation that you are missing the senetences with location\speed data.

  4. Excessive display updates can also cause sentences to be missed and there is no point in updating the display, or sending stuff to serial monitor, unless there is actual updated information to display.

  5. Get rid of the Adafruit SH1106 library, its a memory hog. The U8G2 library will cut memory useage from 90% to 54%.

Thank you all for your feedback, I really appreciate it!
I got rid of the serial.print parts and am now only using the OLED and I cleaned up the code and it is working better now.

I still have the warning about the low memory so I think I will try using he U8G2 library instead. I will need to play around with it because it’s hard for me to understand how to get it to do what I want but I will try.

Here is my new code (that works much better than the original):

#include <TinyGPS++.h>
#include <SoftwareSerial.h>
#include <Wire.h>
#include <Adafruit_SH1106.h>
Adafruit_SH1106 display(-1);

int GPS_speed;

TinyGPSPlus gps;
SoftwareSerial gpsSerial(8, 9);

void setup()
{

  gpsSerial.begin(9600);
  display.begin(SH1106_SWITCHCAPVCC, 0x3C);
  display.clearDisplay();
  display.display();
}

void loop()
{
  while (gpsSerial.available() > 0)
    if (gps.encode(gpsSerial.read()))
    {
      if (gps.speed.isValid())
        {
        GPS_speed = gps.speed.kmph();
        display.setTextColor(WHITE);
        display.setTextSize(7);
        display.setCursor(1, 10);
        display.print(GPS_speed);
        display.display();
        display.clearDisplay();
        }       
    }
}

Hey guys,

Thanks again for your help thus far. I worked on my project some more today and changed over to the U8g2 OLED library. The good news is that I no longer have the memory warnings and I was able to use a neater looking font.

I’m quite happy with how it’s working now except that the display is slow to refresh when changing from one number to another. You can see how the display redraws from top down and I would like it if the numbers changed faster. I’ve attached a short video that shows what I’m talking about.

Can anyone please help me with this?

Here is my code:

#include <TinyGPS++.h>
#include <SoftwareSerial.h>
#include <Arduino.h>
#include <U8g2lib.h>
#include <Wire.h>
U8G2_SH1106_128X64_NONAME_1_HW_I2C u8g2(U8G2_R0, /* reset=*/ U8X8_PIN_NONE);


int GPS_speed;
int GPS_speed_NEW;

TinyGPSPlus gps;
SoftwareSerial gpsSerial(8, 9);

void setup()
{
  gpsSerial.begin(9600);
  u8g2.begin(); 

  u8g2.firstPage();
  do {
    u8g2.setFont(u8g2_font_6x13_tf);
    u8g2.drawStr(10,35,"FW Version: 0.0.7");
  } while ( u8g2.nextPage() );  

delay(2000);  
}

void loop()
{
  while (gpsSerial.available() > 0)
    if (gps.encode(gpsSerial.read()))
    {
      if (gps.speed.isValid())
        {
        GPS_speed_NEW = gps.speed.kmph();
        
if (GPS_speed_NEW != GPS_speed) {
GPS_speed = GPS_speed_NEW;  
  u8g2.firstPage();
  do {
//    u8g2.clearBuffer(); // clear the internal memory
    u8g2.setFont(u8g2_font_fur49_tn); //fur = regular, fub = bold, 49 pixels high. coordinate = bottom left of text
    u8g2.setCursor(45,55); // set cursor position
    u8g2.print(GPS_speed); // write something to the internal memory
//    u8g2.sendBuffer(); // transfer internal memory to the display
    } while ( u8g2.nextPage() );
}    

        
/*
       // display.setTextColor(WHITE);
        display.setTextSize(7);
        if (GPS_speed < 10) {
         display.setCursor(45,10);         
        }
        else if (GPS_speed < 100) {
        display.setCursor(25,10);
        }
        else {
        display.setCursor(2,10);
        }       
        display.print(GPS_speed);
        display.display();
        display.clearDisplay();
        */
        }       
    }
}

refresh2.mpg (1.89 MB)

Suggestions;

Use a faster Arduino
Use a smaller font
Use a different display, LCD for instance.
Persuade the author of the U8G2 library to somehow make it a lot faster.