Adafruit GPS shield and XBee serial port conflict

Hi Arduino friends,

So my project is to use an arduino to control a GPS module (I’m using the Adafruit MTK3339 GPS shield and logger) and an Xbee transmitter to transmit GPS data wirelessly. I’ve attached a picture of my setup for visuals (wire coloring is wrong, but I didn’t have time to sort them out at the time)

The problem is that the Arduino unos are only capable of one active serial port at a time, so if I try to plug in the GPS module and the Xbee using the SoftwareSerial library to enable extra serial ports, neither of them will work properly. I could just get an Arduino mega and just solve the problem there, but that’s too easy and I think there is some really viable workarounds.

Some solutions I thought of were to code some sort of delay, where the Xbee and GPS module will take turns using the serial port, though I’m not exactly sure yet on how to code this (maybe using the listen() object in the SoftwareSerial library?). Another solution was to use the miniSD slot on my GPS shield, have it write to a text file in the miniSD, then have the Xbee read that text file and transmit the data that way, only problem is i’m not sure if that would still require the serial port. Third is to use another serial library called AltSoftSerial, but that can only be used on pins 8, 9, and the manufacturer of the GPS shield set pins 8,7 as the RX, TX connections into the hardware itself.

Sorry for the wall of text. How can I go about programming the arduino to have the GPS module and the Xbee take turns, or would that not be possible since the GPS module works asynchronously? If I use the miniSD in this manner, would I still have to deconflict the usage of the serial port between the GPS module and the xbee? I was hoping I could just get the arduino to work via direct connect and stream the raw NMEA data rather than parsing it (parse it at the receiving station instead). Thanks in advance!

Do you really need wireless? What is the XBee communicating with?

If the Arduino is not connected to a PC with the regular USB cable you should be able to use HardwareSerial (pins 0 and 1) for the Xbee - but you would need to disconnect it when uploading a new program, and it would make debugging more difficult.

...R

Could you not drive the XBee from the hardware serial port ?

UKHeliBob: Could you not drive the XBee from the hardware serial port ?

Parallel thoughts. :)

...R

the Xbee and GPS module will must take turns using the serial port… maybe using the listen()?

Yes, as long as (1) it’s ok to miss an Xbee message when you’re listening to the GPS for up to 2 seconds, and (2) it’s ok not to know the current GPS fix while you’re listening to the Xbee.

If that’s ok, you can do something like this:

#include <XBee.h>
XBee xbee;

// BEST: GPS baud rates 9600, 19200 or 38400.
#include <NeoSWSerial.h>
NeoSWSerial gpsPort( 8, 7 );
NeoSWSerial xbeePort( 2, 3 );

// WORST:  If you can't use baud rates 9600, 19200 or 38400 (are you sure?)
//        SoftwareSerial disables interrupts for long periods of time.
//        *** This will interfere with other parts of your sketch or libraries! ***
//#include <SoftwareSerial.h>
//SoftwareSerial gpsPort( 8, 7 );

#include <NMEAGPS.h>

NMEAGPS  gps;
gps_fix  fix;
uint8_t  fixCount;
uint32_t lastFixSent;

enum state_t { READING_XBEE, READING_GPS };
state_t state = READING_XBEE;

static const uint32_t LOCATION_INTERVAL = 5000; // ms

void setup() {
  Serial.begin( 9600 );
  gpsPort.begin( 9600 );
  xbeePort.begin( 9600 ); // begin calls listen

  xbee.begin( xbeePort );
}

void loop()
{
  switch (state) {

    case READING_XBEE:
      // Listen for XBee messages...

      // If I get a request for a location...
      //     if (xbee.something received...)
      // ...or it's time to send a location...

      if (millis() - lastFixSent >= LOCATION_INTERVAL) {
        // switch to the GPS device
        gpsPort.listen();
        fixCount = 0;
        state    = READING_GPS;
      }
      break;

    case READING_GPS:
      // Get a GPS location and send it
      while (gps.available( gpsPort )) {
        fix = gps.read();

        Serial.print( F("Latitude : ") );
        if (fix.valid.location)
          Serial.print(fix.latitude(), 6);
        Serial.print( F(" :: Longitude : ") );
        if (fix.valid.location)
          Serial.println(fix.longitude(), 6);
        Serial.flush(); // reduces interrupts during XBee operations

        if (fix.valid.location) {
          // Got a location, send it
          char dtframe[64];
          dtostrf(fix.latitude(), 4, 6, &dtframe[0] );
          strcat(dtframe, ",");
          dtostrf(fix.longitude(), 4, 6, &dtframe[ strlen(dtframe) ] );

          xbeePort.listen();
          //  XBee stuff here... just write to the xbeePort for now:
          xbeePort.print( dtframe );

          lastFixSent = millis();
          state            = READING_XBEE;

        } else if (fixCount >= 2) {
          // Wait a few seconds before going back to XBee

          Serial.println( F("No fix yet...") );
          lastFixSent = millis();
          state            = READING_XBEE;
          xbeePort.listen();
        }

      }
      break;
  }
}

This Finite-State Machine just alternates between the two states. Normally, it’s READING_XBEE, but once every 5 seconds it switches to READING_GPS. It stays there long enough to get a fix, then it switches back.

You can decide what to do when it doesn’t get a fix:

* Send no message back (as above)
* Send an Xbee message that means “no location yet”,
* Wait until it does get a fix (maybe forever?)

Notice that it never uses delay; it just runsrunsruns until it’s time to switch. This is a “non-blocking” approach, and it allows you to do other things while the time goes by. See the “Blink Without Delay” or “How to do several things” on the Useful Links page.

The above sketch uses my NeoGPS library. It is faster, smaller and more accurate than other libraries, and the example programs are structured correctly. You can also configure it to parse only the fields that you really use, making it even smaller and faster. If you want to try it, NeoGPS is available from the Arduino IDE Library Manager, under the menu Sketch → Include Library → Manage Libraries.

You should also try my NeoSWSerial. It is much, much more efficient than SoftwareSerial. This could help you later, if you add more features to your program.

Cheers,
/dev