I admit it – I’m easily entertained. I can sit and watch the output of the Adafruit Ultimate GPS sensor on a PuTTY screen for hours (well, minutes, anyway).

The folks at Adafruit did a great job with this sensor, and the freeware that they provide with it for the Arduino controllers. There are, however, a few minor issues that need to be understood when connecting it to an Industrial Shields PLC.

Adafruit Ultimate GPS Sensor

SW Serial, HW Serial, but not RS232

The first issue is connecting the Ultimate GPS sensor to the Arduino PLC. Since there are a variety of PLCs, there are a variety of ways to connect the sensor to the PLC (basically three), but importantly not on the lines marked RS232!

The Adafruit Ultimate GPS board has RS-232 connections that are at 3.3Vdc or 5Vdc levels. As I explained in a previous article (Serial Communications on Industrial Shields PLCs), the Industrial Shields PLCs RS232 lines operate at higher voltages (+/-12Vdc) rather the TTL signals (+/-5Vdc or +/- 3.3Vdc) that the GPS board uses. Consequently, the GPS and PLC don’t play well together, and no output from the GPS is received by the PLC; consequently, no messages are available for parsing. So, if your output displayed is all zeroes, you aren’t talking to the GPS; if the Date / Time stamp is not zero, but everything else is, you aren’t hearing the satellites.

I purchased three 8″ x 40 rainbow jumper wires (M-M, M-F, and F-F). I peeled off two sets of four wire M-F strands (the minimum lines needed), one for ARDBOX and Spartan, the other for M-Duino PLCs. The four essential lines are VIN (5Vdc), GND, RX, and TX. I removed all of the black plastic housing blocks on one end and replaced them with a four-place housing block. If you don’t have that piece, leave those single housing blocks in place. On the other end, remove the single housing blocks as the pins will be inserted into the PLC connector. You’ll also need to insulate the pins since the housing blocks are gone – I like using 1/16″ (1.5 mm) shrink tubing, but small bits of vinyl electrical tape work well also. The ARDBOX and Spartan cable plugs into a single connector, while the M-Duino cable plugs into two connectors – one for power and the other for serial communications. See the figure below.

Cables and Connectors for Adafruit Ultimate GPS to M-Duino 21+ and ARDBOX HF+ Relay

External Antenna (or not)

A second issue (a question really) is whether to connect an external antenna. The Adafruit documentation explains how to connect an active (powered) antenna, but I was able to acquire as many as 10 satellites using their internal antenna. Passive antennas won’t provide any benefit over the internal antenna.

Adafruit Ultimate GPS Library and Examples

Before we get to the three options, we need to cover the basics – installing the Adafruit Ultimate GPS library and examples. Assuming you have your PLC up and running, simply click on the Tools | Manage Libraries… option from the drop-down menu, and wait. Once all the installed libraries have been scanned, you can search for your new sensor, and install its Adafruit library. For example, if you search for “GPS”, you should find a couple dozen options from Adafruit, and many others. Select the Adafruit GPS Library option (the install button should appear in the lower right corner when you hover on the Adafruit option; you may be given an option to select a particular version.

Now that the Adafruit Ultimate GPS library and examples are installed, it’s time to build the sketch. I’ve merged and tweaked a couple of the Adafruit sketches, below. Basically, I combined their HW Serial and SW Serial Parsing versions into one, and with compiler directives (HARDWARE_SERIAL, SOFTWARE_SERIAL, and GPSSerial), build it for the proper configuration.

I’ve also added two display options (in addition to the one provided by Adafruit): GPSECHO prints out the messages from the GPS verbatim, handy when you’re trying to debug; VT100 overwrites the output in the same position, rather than scrolling to infinity; and DMS allows you to select the output format either as DD° MM’ SS” or DDMM.MMMM. Please note, if you choose to use PuTTY, it cannot be active when the sketch is being uploaded (the upload fails), or when the Serial Monitor is active (Serial Monitor will close it or not allow it to open).

Generic_Ultimate_GPS_Parsing.ino Sketch

// Copyright (c) 2021 Daedalus Logic Systems Corporation All Rights Reserved
//
// Test code for the Adafruit Ultimate GPS Using Hardware Serial and Software Serial
// Thanks to lady ada and Adafruit for the original code!
//
// Tested and works great with the Adafruit GPS FeatherWing
// ------> https://www.adafruit.com/products/3133
// or Flora GPS
// ------> https://www.adafruit.com/products/1059
// but also works with the shield, breakout
// ------> https://www.adafruit.com/products/1272
// ------> https://www.adafruit.com/products/746                <<<< This is us!
//
// Pick one up today at the Adafruit electronics shop
// and help support open source hardware & software! -ada

// On M-Duino 21 GPRS-GSM, for HW Serial connect to RX1 (19) and TX1 (18) - located between RX/TX and RX0/TX0, labeled NC / NC
// On M-Duino 21 GPRS-GSM, for SW Serial connect to SI (51) and SCK (52)  - same pin positions, different connector, as HW Serial
// For HW Serial, compile using M-Duino family, not M-Duino GPRS family! Serial1 not defined for the M-Duino GPRS board.

// On ARDBOX HF+ Relay WiFi, connect to MISO (14) and MOSI (16)

#define HARDWARE_SERIAL
//#define SOFTWARE_SERIAL

#if defined HARDWARE_SERIAL

#include <HardwareSerial.h>
#define GPSSerial Serial1

#elif defined SOFTWARE_SERIAL

#include <SoftwareSerial.h>
SoftwareSerial GPSSerial( 51, 52 );  // M-Duino: Rx - 50, 51, 52; Tx - 3, 50, 51, 52
//SoftwareSerial GPSSerial( 14, 16 );  // ARDBOX, Spartan 16RDA: 8, 9, 10, 11, 14 (MISO), 15 (SCK), 16 (MOSI).

#endif

#include <Adafruit_GPS.h>  // uncomment line 42 in Adafruit_GPS.h to #define USE_SW_SERIAL

// Connect to the GPS on the hardware port
Adafruit_GPS GPS(&GPSSerial);

// Set GPSECHO to 'false' to turn off echoing the GPS data to the Serial console
// Set to 'true' if you want to debug and listen to the raw GPS sentences
#define GPSECHO false

// Set VT100 to 'true' to turn on VT100 features (cls, home)
#define VT100 true

// Set DMS to 'true' to turn on DD° MM' SS"
// Set DMS to 'false' to turn on DDMM.MMMM
#define DMS true

uint32_t timer = millis();


void setup()
{
  while (!Serial);  // uncomment to have the sketch wait until Serial is ready

  // connect at 115200 so we can read the GPS fast enough and echo without dropping chars
  // also spit it out
  Serial.begin(115200);
  Serial.println("Adafruit GPS library basic parsing test!");

  // 9600 NMEA is the default baud rate for Adafruit MTK GPS's- some use 4800
  GPS.begin(9600);
  // uncomment this line to turn on RMC (recommended minimum) and GGA (fix data) including altitude
  GPS.sendCommand(PMTK_SET_NMEA_OUTPUT_RMCGGA);
  // uncomment this line to turn on only the "minimum recommended" data
  //GPS.sendCommand(PMTK_SET_NMEA_OUTPUT_RMCONLY);
  // For parsing data, we don't suggest using anything but either RMC only or RMC+GGA since
  // the parser doesn't care about other sentences at this time
  // Set the update rate
  GPS.sendCommand(PMTK_SET_NMEA_UPDATE_1HZ); // 1 Hz update rate
  // For the parsing code to work nicely and have time to sort thru the data, and
  // print it out we don't suggest using anything higher than 1 Hz

  // Request updates on antenna status, comment out to keep quiet
  GPS.sendCommand(PGCMD_ANTENNA);

  delay(1000);

  // Ask for firmware version
  GPSSerial.println(PMTK_Q_RELEASE);
}

void loop() // run over and over again
{
  // read data from the GPS in the 'main loop'
  char c = GPS.read();
  // if you want to debug, this is a good time to do it!
  if (GPSECHO)
    if (c) Serial.print(c);
  // if a sentence is received, we can check the checksum, parse it...
  if (GPS.newNMEAreceived()) {
    // a tricky thing here is if we print the NMEA sentence, or data
    // we end up not listening and catching other sentences!
    // so be very wary if using OUTPUT_ALLDATA and trying to print out data
    //Serial.print(GPS.lastNMEA()); // this also sets the newNMEAreceived() flag to false
    if (!GPS.parse(GPS.lastNMEA())) // this also sets the newNMEAreceived() flag to false
      return; // we can fail to parse a sentence in which case we should just wait for another
  }

  // approximately every 2 seconds or so, print out the current stats
  if (millis() - timer > 2000) {
    timer = millis(); // reset the timer

    if( VT100 )
    {
      // clear screen, go to home; GPSECHO set true can be annoying
      Serial.print( "\033[2J\033[0;1H" );  // works with PuTTY, a few garbage chars with Serial Monitor
    }

    Serial.print("Date / Time: ");
    Serial.print("20"); if( GPS.year  < 10 ) { Serial.print('0'); } Serial.print(GPS.year,  DEC);
    Serial.print('.');  if( GPS.month < 10 ) { Serial.print('0'); } Serial.print(GPS.month, DEC); 
    Serial.print('.');  if( GPS.day   < 10 ) { Serial.print('0'); } Serial.print(GPS.day,   DEC);
    Serial.print(' ');
    
    if( GPS.hour    < 10 ) { Serial.print('0'); } Serial.print(GPS.hour, DEC); Serial.print(':');
    if( GPS.minute  < 10 ) { Serial.print('0'); } Serial.print(GPS.minute, DEC); Serial.print(':');
    if( GPS.seconds < 10 ) { Serial.print('0'); } Serial.print(GPS.seconds, DEC); Serial.print('.');
    if( GPS.milliseconds < 10 )   { Serial.print("0"); }      // Always .000
    if( GPS.milliseconds < 100 )  { Serial.print("0"); }      // Always .000
    Serial.print(GPS.milliseconds); Serial.println( " UTC" ); // Always .000
    
    if (GPS.fix) 
    {
      Serial.print("Location:    ");

      if( DMS )
      {
        int   latDegs = GPS.latitude / 100;
        int   latMins = GPS.latitude - ( 100 * latDegs );
        float latSecs = 60 * ( GPS.latitude - ( 100 * latDegs ) - latMins );
        Serial.print( latDegs ); Serial.print( "° " ); Serial.print( latMins); Serial.print( "\' " ); Serial.print( latSecs, 2 ); Serial.print( "\" " ); Serial.print(GPS.lat);
      }
      else
      {
        Serial.print(GPS.latitude, 4); Serial.print(GPS.lat);
      }
      
      Serial.print(", ");

      if( DMS )
      {
        int   lonDegs = GPS.longitude / 100;
        int   lonMins = GPS.longitude - ( 100 * lonDegs );
        float lonSecs = 60 * ( GPS.longitude - ( 100 * lonDegs ) - lonMins );
        Serial.print( lonDegs ); Serial.print( "° " ); Serial.print( lonMins); Serial.print( "\' " ); Serial.print( lonSecs, 2 ); Serial.print( "\" " ); Serial.print(GPS.lon);
      }
      else
      {
        Serial.print(GPS.longitude, 4); Serial.print(GPS.lon);
      }

      Serial.print( ", " ); Serial.print(GPS.altitude); Serial.println( " m" );

      Serial.print("Speed (knots): "); Serial.println(GPS.speed);
      Serial.print("Compass Angle: "); Serial.println(GPS.angle);
      Serial.print("Satellites: "); Serial.println((int)GPS.satellites);
    }
    else
    {
      Serial.print("Fix: "); Serial.print((int)GPS.fix);
      Serial.print(" quality: "); Serial.println((int)GPS.fixquality);     
    }
  }
}

Option 1: SW Serial on an ARDBOX or Spartan PLC

The first option is connecting the GPS sensor to an ARDBOX or Spartan PLC, and using the SW Serial library. To get started with this option, uncomment
#define SOFTWARE_SERIAL

and comment out
//#define HARDWARE_SERIAL

Also, uncomment
SoftwareSerial GPSSerial( 14, 16 );

and comment out
//SoftwareSerial GPSSerial( 51, 52 );

This will enable SWSerial on the MISO (14) and MOSI (16) lines. Plug the connector into the socket on the Right Zone, and the housing block(s) onto the VIN, GND, RX, and TX pins on the GPS. You should see a red LED flash about 1 Hz if the VIN and GND are connected properly. Compile and load the sketch. Open the Serial Monitor and set the Baud Rate to 115200. If all goes well, you should see something like this:

Date / Time: 2021.06.13 05:32:42.000 UTC
Location:    33° 13' 39.70" N, 111° 50' 32.46" W, 371.50 m
Speed (knots): 0.01
Compass Angle: 175.63
Satellites: 8

The garbage characters that precede the Date / Time are the control characters that make the PuTTY VT100 work, but Serial Monitor doesn’t recognize. To eliminate these when using the Serial Monitor, set
#define VT100 false

Option 2: SW Serial on an M-Duino PLC

The second option is connecting the GPS sensor to an M-Duino PLC using the SW Serial library. To get started with this option, uncomment
#define SOFTWARE_SERIAL

and comment out
//#define HARDWARE_SERIAL

Also, uncomment
SoftwareSerial GPSSerial( 51, 52 );

and comment out
//SoftwareSerial GPSSerial( 14, 16 );

This will enable SWSerial on the MISO (51) and MOSI (52) lines. Here is the verbose output, with the Location printed in the DDMM.MMMM format.

$PGTOP,11,2*6E
$GPGGA,062853.000,3313.6653,N,11150.5373,W,1,09,0.98,383.5,M,-26.8,M,,*52
$GPRMC,062853.000,A,3313.6653,N,11150.5373,W,0.01,239.77,130621,,,A*7A
$PGTOP,11,2*6E
$GPGGA,062854.000,3313.6653,N,11150.5373,W,1,09,0.98,383.5,M,-26.8,M,,*55
$GPRMC,062854.000,A,3313.6653,N,11150.5373,W,0.02,201.67,130621,,,A*74
Date / Time: 2021.06.13 06:28:54.000 UTC
Location:    3313.6652N, 11150.5371W, 383.50 m
Speed (knots): 0.02
Compass Angle: 201.67
Satellites: 9

Option 3: HW Serial on an M-Duino PLC

The third option is connecting the GPS sensor to an M-Duino PLC using the HW Serial library. To get started with this option, uncomment
#define HARDWARE_SERIAL

and comment out
//#define SOFTWARE_SERIAL

The
SoftwareSerial GPSSerial( xx, xx );

lines are not relevant since they are enclosed by the SOFTWARE_SERIAL preprocessor #defines, and not seen by the compiler.

As mentioned above, be sure to select the M-Duino family (not the M-Duino GPRS family) to build the sketch. This will enable HWSerial on the Serial1 Rx and Tx lines (erroneously labelled NC/NC), and the output should look like this on the PuTTY screen, refreshed every two seconds.

Generic_Ultimate_GPS_Parsing.ino Sketch and PuTTY with VT100 and DMS Enabled