Trouble reading data from Venus GPS and MS5803-14BA Pressure Sensor

I'm trying to get GPS and pressure readings from my Arduino Uno. However, when I try to get pressure/temperature readings off of the pressure sensor (code snippet below), the GPS starts sending malformed packets to my serial monitor. I'd like to be able to get good GPS and pressure readings from my Arduino. (Hardware details provided below)

 float temperature_c, temperature_f;
 double pressure_abs, pressure_relative, altitude_delta, pressure_baseline;
  
 String dataString = ""; 
   
 pressure_abs = sensor.getPressure(ADC_4096); //IF THIS LINE REMOVED, GPS SENDS GOOD DATA.
}

Here's the full code:

#include <SoftwareSerial.h>

//pressure sensor libraries
#include <Wire.h>
#include <SparkFun_MS5803_I2C.h>

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

// set up datalogger
const int chipSelect = 4;

/*-----------GPS-----------*/
SoftwareSerial gpsSerial(10, 11); // RX, TX (TX not used)
const int sentenceSize = 80;

char sentence[sentenceSize];

/*------------Pressure sensor-------*/
// Begin class with selected address
// available addresses (selected by jumper on board) 
// default is ADDRESS_HIGH

//  ADDRESS_HIGH = 0x76
//  ADDRESS_LOW  = 0x77

MS5803 sensor(ADDRESS_HIGH);

// Create Variable to store altitude in (m) for calculations;
double pressure_baseline;
double base_altitude = 1655.0; // Altitude of SparkFun's HQ in Boulder, CO. in (m)

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

  /*----------Pressure sensor stuff----------*/
    Serial.println("\ntemperature (C), temperature (F), pressure (abs), pressure (relative), change in altitude");
    //Retrieve calibration constants for conversion math.
    sensor.reset();
    sensor.begin();
    
    pressure_baseline = sensor.getPressure(ADC_4096);
    Serial.println(pressure_baseline);
    Serial.println("\n");
}

void loop()
{
  static int i = 0;
  if (gpsSerial.available())
  {
//    Serial.print("GPS Serial is available\n");
    char ch = gpsSerial.read();
    if (ch != '\n' && i < sentenceSize)
    {
      sentence[i] = ch;
      i++;
    }
    else
    {
     sentence[i] = '\0';
     i = 0;
//     Serial.println("\n");
     Serial.println(sentence);
//     Serial.println("\n");
     displayGPS();
    }
  }

  /*----------------------PRESSURE SENSOR-------------------------*/
  float temperature_c, temperature_f;
  double pressure_abs, pressure_relative, altitude_delta, pressure_baseline;

  String dataString = ""; 

  pressure_abs = sensor.getPressure(ADC_4096); //IF THIS LINE REMOVED, GPS SENDS GOOD DATA.
}

/*--------------------------Pressure sensor methods----------------*/
// Thanks to Mike Grusin for letting me borrow the functions below from 
// the BMP180 example code. 

 double sealevel(double P, double A)
// Given a pressure P (mbar) taken at a specific altitude (meters),
// return the equivalent pressure (mbar) at sea level.
// This produces pressure readings that can be used for weather measurements.
{
  return(P/pow(1-(A/44330.0),5.255));
}

double altitude(double P, double P0)
// Given a pressure measurement P (mbar) and the pressure at a baseline P0 (mbar),
// return altitude (meters) above baseline.
{
  return(44330.0*(1-pow(P/P0,1/5.255)));
}

void displayGPS()
{
  char field[20];
  getField(field, 0);
  if (strcmp(field, "$GPGGA") == 0)
  {
    Serial.print("Lat: ");
    getField(field, 2);  // number
    Serial.print(field);
    getField(field, 3); // N/S
    Serial.print(field);
    
    Serial.print(" Long: ");
    getField(field, 4);  // number
    Serial.print(field);
    getField(field, 5);  // E/W
    Serial.println(field);

    Serial.print("Altitude: ");
    getField(field, 9);
    Serial.println(field);
    
    Serial.print("Number of satellites: ");
    getField(field, 7);
    Serial.println(field);
    Serial.println("-----------------------\n");
  }
}

void getField(char* buffer, int index)
{
  int sentencePos = 0;
  int fieldPos = 0;
  int commaCount = 0;
  while (sentencePos < sentenceSize)
  {
    if (sentence[sentencePos] == ',')
    {
      commaCount ++;
      sentencePos ++;
    }
    if (commaCount == index)
    {
      buffer[fieldPos] = sentence[sentencePos];
      fieldPos ++;
    }
    sentencePos ++;
  }
  buffer[fieldPos] = '\0';
}

When the line to get pressure is commented like so...

  float temperature_c, temperature_f;
  double pressure_abs, pressure_relative, altitude_delta, pressure_baseline;
    
  String dataString = ""; 
    
  // pressure_abs = sensor.getPressure(ADC_4096); //IF THIS LINE REMOVED, GPS SENDS GOOD DATA.
}

...my GPS gives me proper packets of data:

$GPGSA,A,1,,,,,,,,,,,,,0.0,0.0,0.0*30
$GPRMC,120208.000,V,24006,,,N*71
$GPVTG,000.0,T,,M,000.0,N,000.0,K,N*02
$GPGGA,120209.000,2400.0000,N,12100.0000,E,0,00,0.0,0.0,M,0.0,M,,0000*61

Lat: 2400.0000N Long: 12100.0000E
Altitude: 0.0
Number of satellites: 00
-----------------------

$GPGSA,A,1,,,,,,,,,,,,,0.0,0.0,0.0*30
$GPRMC,120209.000,V,24006,,,N*70
$GPVTG,000.0,T,,M,000.0,N,000.0,K,N*02
$GPGGA,120210.000,2400.0000,N,12100.0000,E,0,00,0.0,0.0,M,0.0,M,,0000*69

Lat: 2400.0000N Long: 12100.0000E
Altitude: 0.0
Number of satellites: 00
-----------------------

$GPGSA,A,1,,,,,,,,,,,,,0.0,0.0,0.0*30
$GPRMC,120210.000,V,240006,,,N*78
$GPVTG,000.0,T,,M,000.0,N,000.0,K,N*02
$GPGGA,120211.000,2400.0000,N,12100.0000,E,0,00,0.0,0.0,M,0.0,M,,0000*68
Lat: 2400.0000N Long: 12100.0000E
Altitude: 0.0
Number of satellites: 00
-----------------------

But when my line to get pressure is uncommented...

  float temperature_c, temperature_f;
  double pressure_abs, pressure_relative, altitude_delta, pressure_baseline;
    
  String dataString = ""; 
    
  pressure_abs = sensor.getPressure(ADC_4096); //IF THIS LINE REMOVED, GPS SENDS GOOD DATA.
}

My GPS packets become malformed:

$GPGGA,120125.000,2400.0000,N,12100.0000,E,0,00,0.0,0.0,M,0.0,M,,$0,0T,$GPGGA,12
Lat: 2400.0000N Long: 12100.0000E
Altitude: 0.0
Number of satellites: 00
-----------------------

126.000,2400.0000,N,120,00..
$GPGGA,120127.000,2400.0000,N,100,$GPGGA,120128.000,2400.0000,N,1200,20*0$GPGGA,
Lat: 2400.0000N Long: 100$GPGGA
Altitude: 1200
Number of satellites: 2400.0000
-----------------------

20129.000,2400.0000,N,120G.4,,*$GPGGA,120130.000,2400.0000,N,1060$GPGGA,120131.0
0,2400.0000,N,12,*..E
0$GPGGA,120132.000,2400.0000,N,120A00.0
$GPGGA,120133.000,2400.0000,N,1M,
0,,$GPGGA,120134.000,2400.0000,N,1200,1070$GPGGA,120135.000,2400.0000,N,10,,,0,0
GPGGA,120136.000,2400.0000,N,120G0,0V,$GPGGA,120137.000,2400.0000,N,1,,30000
$GP
GA,120138.000,2400.0000,N,0G00V0VK$GPGGA,120139.000,2400.0000,N1,,VP,6,$GPGGA,12
140.000,2400.0000,N,1.,0*.0.
$GPGGA,120141.000,2400.0000,N,0A*00*N$GPGGA,120142.000,2400.0000,N,.,,110,0$GPGG
Lat: 2400.0000N Long: 0A*00*N$GPGGA120142.000
Altitude: ,110
Number of satellites: N
-----------------------

,120143.000,2400.0000,N,1,,P$02,$GPGGA,120144.000,2400.0000,N,10.0$GPGGA,120145.
00,2400.0000,N,10A*00000$GPGGA,120146.000,2400.0000,N,0G.020,*$GPGGA,120147.000,
400.0000,N0,,20,0$GPGGA,120148.000,2400.0000,N,120P,,0P,$GPGGA,120149.000,2400.0
00,N,1.S0000*$GPGGA,120150.000,2400.0000,N,1.,,,0,0$GPGGA,120151.000,2400.0000,N
100,50E,$GPGGA,120152.000,2400.0000,N,120S.00,N$GPGGA,120153.000,2400.0000,N,1.A
0002$GPGGA,120154.000,2400.0000,N,1.,,00N,$GPGGA,120155.000,2400.0000,N,1E*..0A0
GPGGA,120156.000,2400.0000,N,120S0400N$GPGGA,120157.000,2400.0000,N,1.A0.000$GPG
A,120158.000,2400.0000,N,100,2.N.$GPGGA,120159.000,2400.0000,N,100,906,$GPGGA,12
200.000,2400.0000,N,120,*000*$GPGGA,120201.000,2400.0000,N,10,,,.GN$GPGGA,120202
000,2400.0000,N,11,,C160$GPGGA,120203.000,2400.0000,N,12,6.00
,$GPGGA,120204.000
2400.0000,N,100,00*,$GPGGA,120205.000,2400.0000,N,120,300.
$GPGGA,120206.000,2400.0000,N,1.,R,0M$GPGGA,120207.000,2400.0000,N,12,00007N$GPG
Lat: 2400.0000N Long: 1.R
Altitude: N
Number of satellites: 120207.000
-----------------------

Hardware:

Arduino board: Arduino Uno R3

GPS: Venus GPS (made by Sparkfun)

Pressure sensor: Ms5803-14BA Breakout (made by Sparkfun)

Data logger: OpenLog (made by Sparkfun)

Here's my setup:

What's causing my GPS to send bad packets? Evidently, the pressure sensor is somehow interfering with the GPS, but how is it doing that and how can I avoid interference?

String dataString = "";

Use of Strings very often leads to memory problems, which might explain what you are seeing.

Avoid use of Strings, and use the built-in character array processing routines of C/C++.

What's causing my GPS to send bad packets?

Well, it's sending good packets, but the Arduino is too busy doing other things (pressure library) to watch the GPS characters come in. It's all because SoftwareSerial is a CPU hog. Other libraries, like AltSoftSerial (works on fewer pins), or the recent gSoftSerial would be better choices.

Do you really need to receive GPS while you're getting a pressure reading? Why not read GPS for two seconds to get a fix, then stop it and get the pressure reading. Stop the pressure interface and start the GPS again. Rinse and repeat.

I'll also throw in my obligatory pitch for NeoGPS. If nothing else, the examples show a different program structure that would help immensely... you may be able to take the pressure reading during the GPS quiet time, without stopping and starting the GPS and pressure interfaces.

jremington:
Avoid use of Strings

Are you bashing String again? Sad. Oh wait, I also recommend staying away from String. @Codehawk, You'll thank us later. :smiley:

Cheers,
/dev

Sounds like you really need a 2nd hardware serial port. Atmega1284P is great for that, plus it has 16K SRAM for storing long strings of incoming characters if needed.
I offer boards in several form factors, this one being most like an Uno, and others being smaller or offering more features.
http://www.crossroadsfencing.com/BobuinoRev17/

Yes! Solved it! Thanks for all the help. @/dev, your link to NeoGPS gave me the hint. I ended up taking data from the pressure sensor when the GPS wasn't sending anything and listening to the GPS when it did send something (ie when gpsSerial.available() == true)
Changed code (internal_temperature, pressure_abs, and altitude_delta are global variables):

void loop()
{ 
  static int i = 0;
  if (gpsSerial.available())
  {
//    Serial.print("GPS Serial is available\n");
    char ch = gpsSerial.read();
    if (ch != '\n' && i < sentenceSize)
    {
      sentence[i] = ch;
      i++;
    }
    else
    {
     sentence[i] = '\0';
//     Serial.print("sentence: ");
//     Serial.println(sentence);
     i = 0;
     displayData();
    }
  }
  else {
    /*----------------------PRESSURE SENSOR-------------------------*/
    // store result in variable and print when printing GPS data to consolidate data.
    // Or just update measurement data here and print measurements when printing GPS
    internal_temperature = sensor.getTemperature(CELSIUS, ADC_512);
    pressure_abs = sensor.getPressure(ADC_4096);

    altitude_delta = altitude(pressure_abs, pressure_baseline);
  }
}

Again, thanks so much for the help!
Oh yeah, and I took @jremington's recommendation and replaced all my strings with char arrays. I understand now why they're bad. :slight_smile:

LOL, that's not exactly what I was suggesting, but if it works...

I'm a little surprised, because gpsSerial.available() doesn't actually tell you when you're in the GPS quiet time. It can return false 100's of times between each character, so you may be sneaking in a pressure reading between many of the characters.

You might want to pace the pressure reading in some way, like once per second, because that's all the quicker you display the GPS data. You could set a flag in displayGPS when you get the GGA:

void displayGPS()
{
  char field[20];
  getField(field, 0);
  if (strcmp(field, "$GPGGA") == 0)
  {
    getAltitude = true;

And then test it before doing the pressure reading. Clear it after doing the reading:

  else if (getAltitude) {
    /*----------------------PRESSURE SENSOR-------------------------*/
    // store result in variable and print when printing GPS data to consolidate data.
    // Or just update measurement data here and print measurements when printing GPS
    internal_temperature = sensor.getTemperature(CELSIUS, ADC_512);
    pressure_abs = sensor.getPressure(ADC_4096);

    altitude_delta = altitude(pressure_abs, pressure_baseline);
    get_altitude = false;
  }

But I'm glad it's working for you! If it ain't broke, there's no reason to fix it.

Cheers,
/dev


P.S. Just for fun, I made a NeoGPS version of your sketch:

#include <Arduino.h>

//pressure sensor libraries
#include <Wire.h>
#include <SparkFun_MS5803_I2C.h>

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

// set up datalogger
const int chipSelect = 4;

/*-----------GPS-----------*/
#include "NMEAGPS.h"
#include <NeoSWSerial.h>
NeoSWSerial gpsSerial(10, 11); // RX, TX (TX not used)

static NMEAGPS  gps;

/*------------Pressure sensor-------*/
// Begin class with selected address
// available addresses (selected by jumper on board) 
// default is ADDRESS_HIGH

//  ADDRESS_HIGH = 0x76
//  ADDRESS_LOW  = 0x77

MS5803 sensor(ADDRESS_HIGH);

// Create Variable to store altitude in (m) for calculations;
double pressure_baseline;
double base_altitude = 1655.0; // Altitude of SparkFun's HQ in Boulder, CO. in (m)

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

  /*----------Pressure sensor stuff----------*/
    Serial.println("\ntemperature (C), temperature (F), pressure (abs), pressure (relative), change in altitude");
    //Retrieve calibration constants for conversion math.
    sensor.reset();
    sensor.begin();
    
    pressure_baseline = sensor.getPressure(ADC_4096);
    Serial.println(pressure_baseline);
    Serial.println("\n");
    Serial.flush();
}

void loop()
{
  while (gpsSerial.available()) {

    if (gps.decode( gpsSerial.read() ) == NMEAGPS::DECODE_COMPLETED) {

      if (gps.nmeaMessage == NMEAGPS::NMEA_GGA) {
        /*----------------------PRESSURE SENSOR-------------------------*/
        // store result in variable and print when printing GPS data to consolidate data.
        // Or just update measurement data here and print measurements when printing GPS
        float internal_temperature = sensor.getTemperature(CELSIUS, ADC_512);
        float pressure_abs         = sensor.getPressure(ADC_4096);
        float altitude_delta       = altitude(pressure_abs, pressure_baseline);
        displayGPS( altitude_delta );
      }
    }
  }
}

/*--------------------------Pressure sensor methods----------------*/
// Thanks to Mike Grusin for letting me borrow the functions below from 
// the BMP180 example code. 

 double sealevel(double P, double A)
// Given a pressure P (mbar) taken at a specific altitude (meters),
// return the equivalent pressure (mbar) at sea level.
// This produces pressure readings that can be used for weather measurements.
{
  return(P/pow(1-(A/44330.0),5.255));
}

double altitude(double P, double P0)
// Given a pressure measurement P (mbar) and the pressure at a baseline P0 (mbar),
// return altitude (meters) above baseline.
{
  return(44330.0*(1-pow(P/P0,1/5.255)));
}

void displayGPS( float altitude_delta )
{
  Serial.print( F("Lat: ") );
  if (gps.fix().valid.location)
    Serial.print( gps.fix().latitude(), 6 );
  
  Serial.print( F(" Long: ") );
  if (gps.fix().valid.location)
    Serial.print( gps.fix().longitude(), 6 );
  Serial.println();

  Serial.print( F("Altitude: ") );
  if (gps.fix().valid.altitude)
    Serial.print( gps.fix().altitude(), 2 );
  Serial.print( F(" delta:") );
  Serial.print( altitude_delta, 2 );
  Serial.println();
  
  Serial.print( F("Number of satellites: ") );
  if (gps.fix().valid.satellites)
    Serial.println(gps.fix().satellites);
  Serial.println( F("-----------------------\n") );
}

You'd need the NeoGPS files in your sketch directory (see Installation instructions).

Also, I used the NeoSWSerial library instead of SoftwareSerial. It's much more efficient and reliable. AltSoftSerial is even better, but it's limited to a very particular set of pins. :wink: