Reading Two Serial Devices

I'm trying to connect two GPS modules (Adafruit Ultimate GPS) to an Arduino Uno. The modules use serial communication. The Uno has only one hardware serial port, which needs to be left open for programming and talking to the serial monitor, so I'm trying to use two software serial ports. After some reading around, it seems like you cannot listen to two soft serials at simultaneously, but you can use the .listen() function to listen to one at a time, which is fine for my application. The issue is the .listen() function listens until it catches the '?' character, and there is no '?' character at the end of the GPS messages. There is, however, a '\n' character at the end, so I could use that if I could figure out how to.
That being said, I'm open to solutions other than using the .listen() function for two soft serial ports.
My current code is below, it sorta works, as in I can get complete messages sometimes, but I get a lot of partial messages too.
Let me know if I can provide any missing info and I appreciate any suggestions.
PS. I know the Mega would make short work of this, but at the moment I don't have one and can't get a hold of one quickly.

#include <SoftwareSerial.h>

SoftwareSerial GPS1(3,2);
SoftwareSerial GPS2(8,7);

bool x = 0;
byte counter;

void setup() {
  while (!Serial); // wait for Serial to be ready
  Serial.begin(115200);
  Serial.println("Starting");
  GPS1.begin(57600); 
  GPS2.begin(57600);
  setupGPS();
  GPS1.listen();
}

void loop() {
  char data1[100] = {0};
  while(!GPS1.available());
  while (GPS1.available()) {
    if(!x) {
      //Serial.print("GPS 1 ");
      counter = 0;
      x = 1;
    }
    data1[counter] = GPS1.read();
    counter++;
  }
  if(x){
    if(data1[counter-1] == '\n') {
      Serial.println("GPS 1 Good String");
    }
    else {
      Serial.println("GPS 1 Invalid String");
    }
    x = 0;
    GPS1.stopListening();
    GPS2.listen();
  }
//***************************************************************
  char data2[100] = {0};
  while(!GPS2.available());
  while (GPS2.available()) {
    if(!x) {
      counter = 0;
      x = 1;
    }
    data2[counter] = GPS2.read();
    counter++;
  }
  if(x){
    if(data2[counter-1] == '\n') {
      Serial.println("GPS 2 Good String");
    }
    else {
      Serial.println("GPS 2 Invalid String");
    }
    x = 0;
    GPS2.stopListening();
    GPS1.listen();
  }
}

void setupGPS() {
  GPS1.println("$PMTK251,57600*2C");//set baud rate of gps
  GPS2.println("$PMTK251,57600*2C");
  //GPS1.println("$PMTK251,9600*17");
  //GPS2.println("$PMTK251,9600*17");
  //GPS1.flush();
  //GPS2.flush();
  //GPS1.begin(57600);
  //GPS2.begin(57600);
  //Serial.println("Configuring GPS Dataflow");
  GPS1.println("$PMTK314,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0*29");//set data output of gps
  GPS2.println("$PMTK314,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0*29");
  //GPS1.println("$PMTK220,100*2F");//10hz
  //GPS2.println("$PMTK220,100*2F");//10hz
  //GPS1.println("$PMTK220,200*2C");//5hz
  //GPS2.println("$PMTK220,200*2C");//5hz
  GPS1.println("$PMTK220,1000*1F");//1hz//set data rate of gps
  GPS2.println("$PMTK220,1000*1F");//1hz
}

Only one of your two software serials can be active at a time.

Paul

If you are trying to read data from two serial devices where you cannot control when each of them sends data then you should use an Arduino Mega which has 3 spare HardwareSerial ports.

...R
Serial Input Basics - simple reliable ways to receive data.

Paul_KD7HB:
Only one of your two software serials can be active at a time.

Is there a way to alternate them so only one is active at a time? Turn one on, listen for a message, turn it off, turn the other one on, listen for a message, turn it off, and so on?

Robin2:
If you are trying to read data from two serial devices where you cannot control when each of them sends data then you should use an Arduino Mega which has 3 spare HardwareSerial ports.

...R
Serial Input Basics - simple reliable ways to receive data.

Sometimes I wonder if people bother to read the original post before responding?

I have an AdaFruit Ultimate GPS on an ESP32. I use TinyGPS++ to get the info from the GPS unit. I use TinyGPSPlus GPS; to create a GPS object. I am sure you can use TinyGPSPlus GPS0; and TinyGPSPlus GPS1; to create two GPS objects. What makes sense to me is then you'd create your 2 serial ports in setup() and then create two gps parsers, which could be streamlined at a later time.

void fGPS_Parse(  void *pvParameters )
{
  for (;;)
  {
    xEventGroupWaitBits (eg, evtGPS_Parse, pdTRUE, pdTRUE, portMAX_DELAY) ;
    if ( xSemaphoreTake( sema_GPS_Gate, xTicksToWait0 ) == pdTRUE )
    {
      //query GPS: has a new complete chunk of data been received?
      if ( GPSSerial.available() > 1 )
      {
        if ( GPS.encode(GPSSerial.read()) )
        {
          if (  GPS.location.isValid())
          {
            if ( xSemaphoreTake( sema_Posit, xSemaphoreTicksToWait ) == pdTRUE )
            {
              xPosit.Lat = GPS.location.lat();// store data into structure
              xPosit.Lon = GPS.location.lng();
              xSemaphoreGive( sema_Posit );
            }
          } // if (  GPS.location.isValid())
          if (GPS.speed.isValid())
          {
            if ( xSemaphoreTake( sema_Posit, xSemaphoreTicksToWait ) == pdTRUE )
            {
              xPosit.MPH = GPS.speed.mph();
              xPosit.KPH = GPS.speed.kmph();
              xSemaphoreGive( sema_Posit );
            }
          } //  if (GPS.speed.isValid())
          if (GPS.time.isValid())
          {
            if ( xSemaphoreTake( sema_Time, xSemaphoreTicksToWait ) == pdTRUE )
            {
              xTime.iSeconds = GPS.time.second();
              xTime.iMinutes = GPS.time.minute();
              xTime.iHours = GPS.time.hour();
              xSemaphoreGive( sema_Time );
            }
          } // if (GPS.time.isValid())
          if (GPS.date.isValid())
          {
            if ( xSemaphoreTake( sema_Date, xSemaphoreTicksToWait ) == pdTRUE )
            {
              xDate.iMonth = GPS.date. month();
              xDate.iDay = GPS.date.day();
              xDate.iYear = GPS.date.year();
              xSemaphoreGive( sema_Date );
            }
          } // if (GPS.date.isValid())
          if (  GPS.altitude.isValid() )
          {
            if ( xSemaphoreTake( sema_Posit, xSemaphoreTicksToWait ) == pdTRUE )
            {
              xPosit.Alti = GPS.altitude.meters();
              xSemaphoreGive( sema_Posit );
            }
          } //  if (  GPS.altitude.isValid() )
          if ( GPS.course.isUpdated() )
          {
            if ( xSemaphoreTake( sema_Posit, xSemaphoreTicksToWait ) == pdTRUE )
            {
              xPosit.Hdg = GPS.course.deg();
              xSemaphoreGive( sema_Posit );
            }
          } // if ( GPS.course.isUpdated() )
          if ( xSemaphoreTake( sema_Posit, xSemaphoreTicksToWait ) == pdTRUE )
          {
            xQueueOverwrite( xQ_Posit, (void *) &xPosit );
            xSemaphoreGive( sema_Posit );
          }
        } // if ( GPS.encode(GPSSerial.read()))
      } // if ( GPSSerial.available() > 0 )
      xSemaphoreGive( sema_GPS_Gate );
    }
  } // for (;;)
  vTaskDelete( NULL );
} // void fGPS_Parse(  void *pvParameters )

Example of a GPS parser for a ESP32.

Josh_Blackburn:
Sometimes I wonder if people bother to read the original post before responding?

What part did I miss?

...R

Robin2:
What part did I miss?

...R

This part:

"PS. I know the Mega would make short work of this, but at the moment I don't have one and can't get a hold of one quickly. "

But hey, you took to time to try to help me out, so I can't complain too much :slight_smile:

Idahowalker:
I have an AdaFruit Ultimate GPS on an ESP32. I use TinyGPS++ to get the info from the GPS unit. I use TinyGPSPlus GPS; to create a GPS object. I am sure you can use TinyGPSPlus GPS0; and TinyGPSPlus GPS1; to create two GPS objects. What makes sense to me is then you'd create your 2 serial ports in setup() and then create two gps parsers, which could be streamlined at a later time.

void fGPS_Parse(  void *pvParameters )

{
  for (;:wink:
  {
    xEventGroupWaitBits (eg, evtGPS_Parse, pdTRUE, pdTRUE, portMAX_DELAY) ;
    if ( xSemaphoreTake( sema_GPS_Gate, xTicksToWait0 ) == pdTRUE )
    {
      //query GPS: has a new complete chunk of data been received?
      if ( GPSSerial.available() > 1 )
      {
        if ( GPS.encode(GPSSerial.read()) )
        {
          if (  GPS.location.isValid())
          {
            if ( xSemaphoreTake( sema_Posit, xSemaphoreTicksToWait ) == pdTRUE )
            {
              xPosit.Lat = GPS.location.lat();// store data into structure
              xPosit.Lon = GPS.location.lng();
              xSemaphoreGive( sema_Posit );
            }
          } // if (  GPS.location.isValid())
          if (GPS.speed.isValid())
          {
            if ( xSemaphoreTake( sema_Posit, xSemaphoreTicksToWait ) == pdTRUE )
            {
              xPosit.MPH = GPS.speed.mph();
              xPosit.KPH = GPS.speed.kmph();
              xSemaphoreGive( sema_Posit );
            }
          } //  if (GPS.speed.isValid())
          if (GPS.time.isValid())
          {
            if ( xSemaphoreTake( sema_Time, xSemaphoreTicksToWait ) == pdTRUE )
            {
              xTime.iSeconds = GPS.time.second();
              xTime.iMinutes = GPS.time.minute();
              xTime.iHours = GPS.time.hour();
              xSemaphoreGive( sema_Time );
            }
          } // if (GPS.time.isValid())
          if (GPS.date.isValid())
          {
            if ( xSemaphoreTake( sema_Date, xSemaphoreTicksToWait ) == pdTRUE )
            {
              xDate.iMonth = GPS.date. month();
              xDate.iDay = GPS.date.day();
              xDate.iYear = GPS.date.year();
              xSemaphoreGive( sema_Date );
            }
          } // if (GPS.date.isValid())
          if (  GPS.altitude.isValid() )
          {
            if ( xSemaphoreTake( sema_Posit, xSemaphoreTicksToWait ) == pdTRUE )
            {
              xPosit.Alti = GPS.altitude.meters();
              xSemaphoreGive( sema_Posit );
            }
          } //  if (  GPS.altitude.isValid() )
          if ( GPS.course.isUpdated() )
          {
            if ( xSemaphoreTake( sema_Posit, xSemaphoreTicksToWait ) == pdTRUE )
            {
              xPosit.Hdg = GPS.course.deg();
              xSemaphoreGive( sema_Posit );
            }
          } // if ( GPS.course.isUpdated() )
          if ( xSemaphoreTake( sema_Posit, xSemaphoreTicksToWait ) == pdTRUE )
          {
            xQueueOverwrite( xQ_Posit, (void *) &xPosit );
            xSemaphoreGive( sema_Posit );
          }
        } // if ( GPS.encode(GPSSerial.read()))
      } // if ( GPSSerial.available() > 0 )
      xSemaphoreGive( sema_GPS_Gate );
    }
  } // for (;:wink:
  vTaskDelete( NULL );
} // void fGPS_Parse(  void *pvParameters )


Example of a GPS parser for a ESP32.

Thanks! I'll try that out!

Josh_Blackburn:
This part:

"PS. I know the Mega would make short work of this, but at the moment I don't have one and can't get a hold of one quickly. "

Sorry, I did miss that bit - short attention span.

However I'm not sure there is an easy alternative.

You could perhaps try using SoftwareSerial for one connection and NeoSWserial or AltSoftSerial for the other connection - I have no experience of them myself but they are supposed to be much better than the standard SoftwareSerial.

...R

Robin2:
However I'm not sure there is an easy alternative.

Yeah that was the general feel I've been getting from reading around.

Robin2:
You could perhaps try using SoftwareSerial for one connection and NeoSWserial or AltSoftSerial for the other connection - I have no experience of them myself but they are supposed to be much better than the standard SoftwareSerial.

I've not heard of either of those, but I will definitely look into them, thanks!