GPS Speedo using Oled for a HUD

Im trying to build a speedo Head up Display for my motorcycle helmet. Ive worked out the physical side using a convex lens from an old pair of binoculars and some semi reflective film.

Ive managed to get an Oled screen to work using the example software and a GPS unit using its example script.

My problem is now taking the data for speed put out by the GPS and then displaying that on the Oled. I started to try and copy and paste from the respective example scripts what I would need to do this but I’m getting bogged down. Can anyone help.

This is the scripting I’ve managed so far.

#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include “TinyGPS++.h”
#include “SoftwareSerial.h” // for testing purposes

#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 32 // OLED display height, in pixels

// Declaration for an SSD1306 display connected to I2C (SDA, SCL pins)
#define OLED_RESET 4 // Reset pin # (or -1 if sharing Arduino reset pin)
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

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

display.clearDisplay

display.gpsspeeddata

{
while(serial_connection.available())//While there are characters to come from the GPS
{
gps.encode(serial_connection.read());//This feeds the serial NMEA data into the library one char at a time
}
if(gps.location.isUpdated())//This will pretty much be fired all the time anyway but will at least reduce it to only after a package of NMEA data comes in
{

Serial.println(“Speed MPH:”);// This is not code i know that but its essentially what I want to know how to do.
Serial.println(gps.speed.mph());

}

Any help appreciated. Im aware the above is a mess. I mostly was just cutting out of the example scripts what was clearly not necessary for my aim.

Am i right in thinking though that my project does require a very simple extraction of data (speed), then transfer of that data to display? Once i have that basically done i can fiddle around with things like the size of the display font, colour etc.

Im aware the above is a mess.

You could at least post code that compiles.

Am i right in thinking though that my project does require a very simple extraction of data (speed), then transfer of that data to display?

If all you want to know is speed, yes.

Using code tags is also favourable….

Am i right in thinking though that my project does require a very simple extraction of data (speed), then transfer of that data to display?

You do not need the whole TinyGPS library if all that you want is speed. If you just get the RMC sentence, speed is the 7th item in the sentence and the 2nd item is whether the fix is 'A’ctive or 'V’oid. GPS NMEA sentences.

// Robin2's Example 2 - Receive with an end-marker modified to work with software serial
// retrieve speed from the $GPRMC sentance from GPS data string.
// by groundfungus aka c. goulding

#include <SoftwareSerial.h>

SoftwareSerial ssPort(4, -1);

const byte numChars = 64;
char receivedChars[numChars];   // an array to store the received data
char rmcLine[64];
boolean newData = false;

char *strings[16];
char *ptr = NULL;
float knots2MPH = 1.1507794;
float speed = 0;
boolean speedIsValid = false;

void setup()
{
   Serial.begin(115200);
   ssPort.begin(9600);
   Serial.println("<Arduino is ready>");
}

void loop()
{
   recvWithEndMarker();
   getRMCdata();
}

void recvWithEndMarker()
{
   static byte ndx = 0;
   char endMarker = '\n';
   char rc;

   while (ssPort.available() > 0 && newData == false)
   {
      rc = ssPort.read();

      if (rc != endMarker)
      {
         receivedChars[ndx] = rc;
         ndx++;
         if (ndx >= numChars)
         {
            ndx = numChars - 1;
         }
      }
      else
      {
         receivedChars[ndx] = '\0'; // terminate the string
         ndx = 0;
         newData = true;
      }
   }
}

void getRMCdata()
{
   if (newData == true)
   {
      if (strstr(receivedChars, "$GPRMC") )
      {
         strcpy(rmcLine, receivedChars);
         //Serial.print("This just in ... ");
         //Serial.println(rmcLine);
         byte index = 0;
         ptr = strtok(rmcLine, ",");
         while (ptr != NULL)
         {
            strings[index] = ptr;
            index++;
            ptr = strtok(NULL, ",");
         }
         if (*strings[2] == 'A')   // if the fix is valid print speed
         {
            speed = atof(strings[7]) * knots2MPH;
            speedIsValid = true;
            Serial.print("Speed = ");
            Serial.print(speed);
            Serial.println(" MPH");
         }
         else
         {
            Serial.println("Speed not valid");
            speedIsValid = false;
         }
      }
      newData = false;
   }
}

Thanks Groundfungus.

What I get from the script you’ve given is that it will print to the serial monitor the speed value in MPH. I don’t get how but although I’m sure its more efficient in coding terms it seems it just does what the TinyGPS example code does. I appreciate what you’ve shown me but what im needing is how to join up the dots of a GPS unit----an Arduino------an Oled. The last time I did anything that involved writing any kind of code was writing programs in BASIC on a ZX81 in 1982. So im very behind on these things.

print to the serial monitor the speed value in MPH

It also saves the value of speed in a variable named speed that is of the float data type that you can print to the OLED.

Have you wired the OLED and tested it, by itself, using library examples?

Yes, I've run the Oled using an example script which showed snowflakes and lines and printed the Hello World message. What's in my script came from that one but with all the unnecessary snowflake stuff etc stripped out.

I'm guessing the line speed = atof(strings[7]) * knots2MPH; is where the speed variable is created? That atof(strings[7]) extracts the seventh part of the GPS output? and that * knots2MPH is a call for some part of the library to do the calculation of knots to MPH?

Im going to have to print out your script to look it over on the easier old fashioned paper stuff.

I'm guessing the line speed = atof(strings[7]) * knots2MPH; is where the speed variable is created?

Yes that is right. The speed variable is of the float data type and is of global scope so it is visible to all parts of the program.

knots2MPH is the conversion factor for knots to MPH. It is not a library function.

The references for the string functions that are used can be found here.

Hello Dkenp.

What you want to do is not overly ambitious, programming wise. You do have your own learning curve to climb. Lots of examples to follow. GPS projects call for patience and perseverence.

Do you realise the image will be reversed (left to right) when reflected off the visor? The OLED will need to display the numerals reversed. Do you use the lens to do the reversal?

John.

I'm using a GPS speedo in my car. Works great.

I tried three types of display: LCD (very poor readability), OLED and 4-digit 7-segment LED.

The OLED is fun to play with cos you can draw anything on it. But slow to refresh and not great in daylight.

The LED display is the best. Needs shading and can be too bright at night. Instant refresh. It reflects well off the windscreen, but in reverse with a double image, so I'm just looking directly at the display. I have the display on the dash in line with the steering wheel. I read the speed with the briefest of glances.

I use TinyGPS++. It provides the speed in kph or mph. You simply use their variable name. I display the speed at a rate of 1Hz. Faster than that is not needed.

Good luck. Be careful.

John.

I lately built a speedometer for use in museum trains. A NEO-6M works fine as long as trees are not all around. From earlier experienes of self built devices I felt that 2 Hz of update frequency, maybe 3, feels comfortable. Yes, Tiny GPS++ works perfectly.

Hi Railroader.

Have you found weather effects reception? I mean cloudy or rainy conditions?

Steam trains? Wouldn’t it be fun to have a vintage steam train running a generator to provide power to a device that receives data from satellites 20,000 km in space?

John.

No, I can't say that Clouds have been an issue. Only that dence forrest, especially with whet trees, lovers the quality of the readings. They drop to 1/2 or so.

Another stupid question but, what does the - 1 mean. 4 is the pin to connect Tx from the gps, yes?

SoftwareSerial ssPort(4, -1);

HillnanImp; I'll reverse the image using a mirror first and leave reversing in software for later, ive enough on my plate understanding just getting this stage working.
I'd like to use LED but Oled is what ive got plus eventually might want to display more than just speed info so may as well make the effort with the Oled.

Railroader Im going with Groundfungus's serial read of the GPS because id like the script to take up as little space as possible so i can use the smallest Arduino possible as it would be nice to have it all fit in my bike helmet.

Another stupid question but, what does the - 1 mean

Since there is no reason to connect the GPS module RX pin (because we never talk to the GPS) use -1 to tell the SoftwareSerial library that no pin will be used for software serial TX. So we do not waste a pin for nothing.

I used SoftwareSerial in an UNO. Some 50% of the memory was used and the code does more than show speed.

My code occupies only 44% of the Nano's memory. Like Railroader my code is doing a heck of a lot more than just displaying teh speed. I'm using TinyGPS++.

John.

Ive tried the code on my UNO R3 and it works. so then i tried using a MEGA 2560 but it dod not work. I tried changing the serial read pins to (1,-0) and also to (28,-0) and made sure i had the right baud rates set. I also made sure my IDE was set to the MEGA 2560 board as well. The GPS unit is getting a lock as its blinking.
Is there a key difference between the UNO R3 and a MEGA 2560 which would fox the script?

The only message i get in the serial monitor is

The Mega has 3 extra hardware serial ports so one should not use software serial. Here is the previous code modified to use a Mega and the Serial1 port instead of a software serial port. GPS TX connected to Serial1 RX (Mega pin 19). GPS RX not connected. Tested with my Mega and Neo6m GPS and works.

// Robin2's Example 2 - Receive with an end-marker modified to work with Mega Serial1
// retrieve speed from the $GPRMC sentance from GPS data string.
// by groundfungus aka c. goulding

const byte numChars = 64;
char receivedChars[numChars];   // an array to store the received data
char rmcLine[64];
boolean newData = false;

char *strings[16];
char *ptr = NULL;
float knots2MPH = 1.1507794;
float speed = 0;
boolean speedIsValid = false;

void setup()
{
   Serial.begin(115200);
   Serial1.begin(9600);
   Serial.println("<Arduino is ready>");
}

void loop()
{
   recvWithEndMarker();
   getRMCdata();
}

void recvWithEndMarker()
{
   static byte ndx = 0;
   char endMarker = '\n';
   char rc;

   while (Serial1.available() > 0 && newData == false)
   {
      rc = Serial1.read();

      if (rc != endMarker)
      {
         receivedChars[ndx] = rc;
         ndx++;
         if (ndx >= numChars)
         {
            ndx = numChars - 1;
         }
      }
      else
      {
         receivedChars[ndx] = '\0'; // terminate the string
         ndx = 0;
         newData = true;
      }
   }
}

void getRMCdata()
{
   if (newData == true)
   {
      if (strstr(receivedChars, "$GPRMC") )
      {
         strcpy(rmcLine, receivedChars);
         //Serial.print("This just in ... ");
         //Serial.println(rmcLine);
         byte index = 0;
         ptr = strtok(rmcLine, ",");
         while (ptr != NULL)
         {
            strings[index] = ptr;
            index++;
            ptr = strtok(NULL, ",");
         }
         if (*strings[2] == 'A')   // if the fix is valid print speed
         {
            speed = atof(strings[7]) * knots2MPH;
            speedIsValid = true;
            Serial.print("Speed = ");
            Serial.print(speed);
            Serial.println(" MPH");
         }
         else
         {
            Serial.println("Speed not valid");
            speedIsValid = false;
         }
      }
      newData = false;
   }
}

Thanks. I didnt realise there was that much difference in the boards. Have to read up.