Ublox M8n Speedometer+ Oled

Hallo there!

I spent a lot of time in programming an oled- gps- arduino- speedo.
I use arduino nano device+ ublox m8n (banggood)+ oled 128x 64 i2c.
This is my basic:

But my device won´t work normally.
The first spinner works (arduino program is runnning).
After about an hour it displays the numbers of satellites.
I can't get in speed information. Its alwas 0 or weird numbers after some time,...

Any ideas? maybe about ublox configuration?

Here is my code:

//Berni Neu
#include <SoftwareSerial.h>
SoftwareSerial serial = SoftwareSerial(1,0);
/*
 Connections:
 GPS TX -> Arduino 0 (disconnect to upload this sketch)
 GPS RX -> Arduino 1
 Screen SDA -> Arduino A4
 Screen SCL -> Arduino A5
*/

#include "U8glib.h"

U8GLIB_SSD1306_128X64 u8g(U8G_I2C_OPT_DEV_0|U8G_I2C_OPT_NO_ACK|U8G_I2C_OPT_FAST);	// Fast I2C / TWI 

#define GPS Serial

const unsigned char UBLOX_INIT[] PROGMEM = {
  // Disable NMEA
  0xB5,0x62,0x06,0x01,0x08,0x00,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x23, // GxGGA off
  0xB5,0x62,0x06,0x01,0x08,0x00,0xF0,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x2A, // GxGLL off
  0xB5,0x62,0x06,0x01,0x08,0x00,0xF0,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x31, // GxGSA off
  0xB5,0x62,0x06,0x01,0x08,0x00,0xF0,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x38, // GxGSV off
  0xB5,0x62,0x06,0x01,0x08,0x00,0xF0,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x3F, // GxRMC off
  0xB5,0x62,0x06,0x01,0x08,0x00,0xF0,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x46, // GxVTG off

  // Disable UBX
  0xB5,0x62,0x06,0x01,0x08,0x00,0x01,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x17,0xDC, //NAV-PVT off
  0xB5,0x62,0x06,0x01,0x08,0x00,0x01,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x12,0xB9, //NAV-POSLLH off
  0xB5,0x62,0x06,0x01,0x08,0x00,0x01,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x13,0xC0, //NAV-STATUS off

  // Enable UBX
  0xB5,0x62,0x06,0x01,0x08,0x00,0x01,0x07,0x00,0x01,0x00,0x00,0x00,0x00,0x18,0xE1, //NAV-PVT on
  0xB5,0x62,0x06,0x01,0x08,0x00,0x01,0x02,0x00,0x01,0x00,0x00,0x00,0x00,0x13,0xBE, //NAV-POSLLH on
  0xB5,0x62,0x06,0x01,0x08,0x00,0x01,0x03,0x00,0x01,0x00,0x00,0x00,0x00,0x14,0xC5, //NAV-STATUS on

  // Rate
  0xB5,0x62,0x06,0x08,0x06,0x00,0x64,0x00,0x01,0x00,0x01,0x00,0x7A,0x12, //(10Hz)
  0xB5,0x62,0x06,0x08,0x06,0x00,0xC8,0x00,0x01,0x00,0x01,0x00,0xDE,0x6A, //(5Hz)
  0xB5,0x62,0x06,0x08,0x06,0x00,0xE8,0x03,0x01,0x00,0x01,0x00,0x01,0x39 //(1Hz)
};

const unsigned char UBX_HEADER[] = { 0xB5, 0x62 };

struct NAV_PVT {
  unsigned char cls;
  unsigned char id;
  unsigned short len;
  unsigned long iTOW;          // GPS time of week of the navigation epoch (ms)

  unsigned short year;         // Year (UTC) 
  unsigned char month;         // Month, range 1..12 (UTC)
  unsigned char day;           // Day of month, range 1..31 (UTC)
  unsigned char hour;          // Hour of day, range 0..23 (UTC)
  unsigned char minute;        // Minute of hour, range 0..59 (UTC)
  unsigned char second;        // Seconds of minute, range 0..60 (UTC)
  char valid;                  // Validity Flags (see graphic below)
  unsigned long tAcc;          // Time accuracy estimate (UTC) (ns)
  long nano;                   // Fraction of second, range -1e9 .. 1e9 (UTC) (ns)
  unsigned char fixType;       // GNSSfix Type, range 0..5
  char flags;                  // Fix Status Flags
  unsigned char reserved1;     // reserved
  unsigned char numSV;         // Number of satellites used in Nav Solution

  long lon;                    // Longitude (deg)
  long lat;                    // Latitude (deg)
  long height;                 // Height above Ellipsoid (mm)
  long hMSL;                   // Height above mean sea level (mm)
  unsigned long hAcc;          // Horizontal Accuracy Estimate (mm)
  unsigned long vAcc;          // Vertical Accuracy Estimate (mm)

  long velN;                   // NED north velocity (mm/s)
  long velE;                   // NED east velocity (mm/s)
  long velD;                   // NED down velocity (mm/s)
  long gSpeed;                 // Ground Speed (2-D) (mm/s)
  long heading;                // Heading of motion 2-D (deg)
  unsigned long sAcc;          // Speed Accuracy Estimate
  unsigned long headingAcc;    // Heading Accuracy Estimate
  unsigned short pDOP;         // Position dilution of precision
  short reserved2;             // Reserved
  unsigned long reserved3;     // Reserved
};

NAV_PVT pvt;

void calcChecksum(unsigned char* CK) {
  memset(CK, 0, 2);
  for (int i = 0; i < (int)sizeof(NAV_PVT); i++) {
    CK[0] += ((unsigned char*)(&pvt))[i];
    CK[1] += CK[0];
  }
}

long numGPSMessagesReceived = 0;

bool processGPS() {
  static int fpos = 0;
  static unsigned char checksum[2];
  const int payloadSize = sizeof(NAV_PVT);

  while ( GPS.available() ) {
    byte c = GPS.read();
    if ( fpos < 2 ) {
      if ( c == UBX_HEADER[fpos] )
        fpos++;
      else
        fpos = 0;
    }
    else {      
      if ( (fpos-2) < payloadSize )
        ((unsigned char*)(&pvt))[fpos-2] = c;

      fpos++;

      if ( fpos == (payloadSize+2) ) {
        calcChecksum(checksum);
      }
      else if ( fpos == (payloadSize+3) ) {
        if ( c != checksum[0] )
          fpos = 0;
      }
      else if ( fpos == (payloadSize+4) ) {
        fpos = 0;
        if ( c == checksum[1] ) {
          return true;
        }
      }
      else if ( fpos > (payloadSize+4) ) {
        fpos = 0;
      }
    }
  }
  return false;
}

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

  u8g.setColorIndex(1);

  // send configuration data in UBX protocol
  for(unsigned int i = 0; i < sizeof(UBLOX_INIT); i++) {                        
    GPS.write( pgm_read_byte(UBLOX_INIT+i) );
    delay(5); // simulating a 38400baud pace (or less), otherwise commands are not accepted by the device.
  }
  
}

long gSpeed = 0;
int numSV = 0;
unsigned long lastScreenUpdate = 0;
char speedBuf[16];
char satsBuf[16];

char* spinner = "/-\\|";
byte screenRefreshSpinnerPos = 0;
byte gpsUpdateSpinnerPos = 0;

void loop() {
  if ( processGPS() ) {
    numSV = pvt.numSV;
    gSpeed = pvt.gSpeed;    
    gpsUpdateSpinnerPos = (gpsUpdateSpinnerPos + 1) % 4;
  }

  unsigned long now = millis();
  if ( now - lastScreenUpdate > 100 ) {
    updateScreen();
    lastScreenUpdate = now;
    screenRefreshSpinnerPos = (screenRefreshSpinnerPos + 1) % 4;
  }
}

void draw() {
  //u8g.setScale2x2(); don't do this!
  u8g.setFont(u8g_font_courB24);
  u8g.drawStr( 36, 45, speedBuf);
  //u8g.undoScale();
  u8g.setFont(u8g_font_fur11);
  u8g.drawStr( 2, 12, satsBuf);
}

void updateScreen() {
  
  int kmh = gSpeed * 0.0036;
  sprintf(speedBuf, "%3d", kmh);
  sprintf(satsBuf, "%c %c %d", spinner[screenRefreshSpinnerPos], spinner[gpsUpdateSpinnerPos], numSV);
  
  u8g.firstPage();
  do {
    draw();
  } while( u8g.nextPage() );
}

The adresses are matching with the ublox u-center.
maybe someone else had the same problms?
Thanks alot!

Perhaps the byte order in the UBLOX record is different from the native Arduino byte order. That would cause any value larger than a byte to come out strange. I would print the record to Serial in hex to see if all of the fields make sense.

SoftwareSerial serial = SoftwareSerial(1,0);

It is stupid to do software serial on the hardware serial pins.

It is equally stupid to define an instance of a class that you don't use.

It is not terribly bright to connect the GPS to the hardware serial pins until you KNOW that you are getting good data from it.

I don't recommend using the binary UBX protocol. It's much harder to debug, especially for beginners. Instead, just use the text NMEA sentences that contain speed and satellites. According to this table, you need to process the RMC and GGA sentences.

I would also suggest using a GPS parsing library. Your main sketch file will be much shorter and easier to read.

Here is a version of your sketch that uses my GPS library, NeoGPS. It is smaller, faster, more accurate and more reliable than all other libraries. If you'd like to try it, NeoGPS is available from the Ardino IDE Library Manager, under the menu Sketch -> Include Library -> Manage Libraries.

//Berni Neu
/*
 Connections:
 GPS TX -> Arduino 0 (disconnect to upload this sketch)
 GPS RX -> Arduino 1
 Screen SDA -> Arduino A4
 Screen SCL -> Arduino A5
*/

#include <NMEAGPS.h>
NMEAGPS gps;
gps_fix fix;

#include "U8glib.h"

U8GLIB_SSD1306_128X64 u8g(U8G_I2C_OPT_DEV_0|U8G_I2C_OPT_NO_ACK|U8G_I2C_OPT_FAST); // Fast I2C / TWI 

#define gpsPort Serial

void setup() 
{
  u8g.setColorIndex(1);

  gpsPort.begin(9600);

  gps.send_P( &gpsPort, F("PUBX,40,GLL,0,0,0,0,0,0") );
  gps.send_P( &gpsPort, F("PUBX,40,GSV,0,0,0,0,0,0") );
  gps.send_P( &gpsPort, F("PUBX,40,GSA,0,0,0,0,0,0") );
  gps.send_P( &gpsPort, F("PUBX,40,VTG,0,0,0,0,0,0") );
  gps.send_P( &gpsPort, F("PUBX,40,ZDA,0,0,0,0,0,0") );
  gps.send_P( &gpsPort, F("PUBX,40,RMC,0,1,0,0,0,0") );
  gps.send_P( &gpsPort, F("PUBX,40,GGA,0,1,0,0,0,0") );
}

char spinner( uint8_t pos )
{
  static const char spinnerChars[] PROGMEM = "/-\\|"; // saves RAM!

  return (char) pgm_read_byte( &spinnerChars[ pos % 4 ] ); // ...but requires special read
}

uint16_t kph              = 0;
uint16_t lastScreenUpdate = 0;
byte     screenRefreshes  = 0;
byte     gpsUpdates       = 0;

void loop() {

  uint16_t now = millis();

  if ( gps.available( gpsPort ) ) {
    fix        = gps.read();
    gpsUpdates++;

    if (fix.valid.speed)
      kph = (((uint32_t) fix.spd.whole) * 1852) / 1000; // knots to kph, integer math
    else
      kph = 0;
    lastScreenUpdate = now - 102; // force a redraw
  }

  if ( now - lastScreenUpdate > 101 ) {
    updateScreen();
    lastScreenUpdate = now;
    screenRefreshes++;
  }
}

void draw() {
  //u8g.setScale2x2(); don't do this!
  u8g.setFont(u8g_font_courB24);
  u8g.setPrintPos( 36, 45 );
  if (kph < 100)      // alternative to sprintf, just print leading spaces
    u8g.write( ' ' );
  if (kph < 10)
    u8g.write( ' ' );
  u8g.print( kph );

  //u8g.undoScale();
  u8g.setFont(u8g_font_fur11);
  u8g.setPrintPos( 2, 12 );
  u8g.write( spinner( screenRefreshes ) );
  u8g.write( ' ' );

  u8g.write( spinner( gpsUpdates ) );
  u8g.write( ' ' );

  if (!fix.valid.satellites || (fix.satellites < 10))
    u8g.write( ' ' );
  if (fix.valid.satellites)
    u8g.print( fix.satellites );
  else
    u8g.write( ' ' );
}

void updateScreen() {

  u8g.firstPage();
  do {
    draw();
  } while( u8g.nextPage() );
}

Your original sketch (194 lines) uses 23700 bytes of program space and 738 bytes of RAM.
The NeoGPS version (104 lines) uses 22454 bytes of program space and 442 bytes of RAM, a significant savings.

Notes on the NeoGPS version:

* It sends NMEA configuration commands to disable all NMEA sentences except RMC and GGA.

* It never uses floating-point numbers. Instead, it calculates the KPH from the integer speed stored in the fix structure.

* It avoids the sprintf function by just displaying the individual pieces: a few leading space characters when necessary, and the integer speed value.

* It checks to make sure that the speed and satellites fields are valid. If you do not have good satellite reception, those fields may not be valid.

Cheers,
/dev

Hello,
any possibility getting a 10HZ update ?
How to send 10Hz configuration message please ?

Regards

I get a response that says class NMEAGPS' has no member named 'send_P'

dmitri_gorchev:
I get a response that says class NMEAGPS' has no member named 'send_P'

It compiles without error for me. Do you have the latest NeoGPS library installed? Check using Tools->Manage Libraries...

dmitri_gorchev:
I get a response that says class NMEAGPS' has no member named 'send_P'

dmitri_gorchev:
I get a response that says class NMEAGPS' has no member named 'send_P'

Do you want answeres in this old post or the new one you also just raised ?

Yes, i have 4.2.9 which as far as i can tell is the newest version

also the question i just raised

Thank you it works now
sorry I did not think that a library could overrule another

Do you know how I could change the frequency to say 10hrtz