Hi there,
ROCK SOLID performance from the L76K GSS unit. seems to be a TAD faster and fresher than the Grove connected AIR530 Unit.
I was able to run each device with each method Hardware/Software Serial on either Port.
The key seems to be SoftSerial works better on pins that are already defined as a Uart channel. Hardware Serial can make changes to the interconnected PPI in the chips Peripherals to better support a Brute force or bitbanged io approach to serial comms.
Baud rates also play a role so don’t try to fast at first, test , tune and turn up as needed.
I added the labels and posting current code and output. for others to duplicate.
Serial Output Monitor…
16 0.8 26.041662 -80.220924 224 02/23/2025 20:13:50 224 -11.60 134.26 0.00 SE 12348 16.02 NNE 98018 26178 0 GPS 2- L76K GSS
16 0.8 26.041662 -80.220924 227 02/23/2025 20:13:51 227 -11.50 134.26 0.00 SE 12348 16.02 NNE 98025 26180 0 GPS 2- L76K GSS
16 0.8 26.041662 -80.220924 224 02/23/2025 20:13:52 224 -11.50 134.26 0.00 SE 12348 16.02 NNE 98033 26182 0 GPS 2- L76K GSS
16 0.8 26.041662 -80.220924 233 02/23/2025 20:13:53 233 -11.50 134.26 0.00 SE 12348 16.02 NNE 98040 26184 0 GPS 2- L76K GSS
16 0.8 26.041662 -80.220924 233 02/23/2025 20:13:54 233 -11.50 134.26 0.00 SE 12348 16.02 NNE 98048 26186 0 GPS 2- L76K GSS
16 0.8 26.041662 -80.220924 228 02/23/2025 20:13:55 228 -11.40 134.26 0.00 SE 12348 16.02 NNE 98055 26188 0 GPS 2- L76K GSS
16 0.8 26.041662 -80.220924 231 02/23/2025 20:13:56 231 -11.40 134.26 0.00 SE 12348 16.02 NNE 98063 26190 0 GPS 2- L76K GSS
16 0.8 26.041662 -80.220924 236 02/23/2025 20:13:57 236 -11.40 134.26 0.00 SE 12348 16.02 NNE 98071 26192 0 GPS 2- L76K GSS
16 0.8 26.041662 -80.220924 245 02/23/2025 20:13:58 245 -11.40 134.26 0.00 SE 12348 16.02 NNE 98078 26194 0 GPS 2- L76K GSS
16 0.8 26.041662 -80.220924 251 02/23/2025 20:13:59 251 -11.40 134.26 0.00 SE 12348 16.02 NNE 98086 26196 0 GPS 2- L76K GSS
16 0.8 26.041662 -80.220924 254 02/23/2025 20:14:00 254 -11.40 134.26 0.00 SE 12348 16.02 NNE 98093 26198 0 GPS 2- L76K GSS
16 0.8 26.041662 -80.220924 254 02/23/2025 20:14:01 254 -11.40 134.26 0.00 SE 12348 16.02 NNE 98101 26200 0 GPS 2- L76K GSS
15 1.0 26.041656 -80.220879 247 02/23/2025 20:14:02 247 -5.50 308.12 2.76 NW 12348 16.02 NNE 96082 26202 0 GPS 1- Air530
15 1.0 26.041656 -80.220879 268 02/23/2025 20:14:03 268 -5.50 308.12 0.00 NW 12348 16.02 NNE 96089 26204 0 GPS 1- Air530
15 1.0 26.041656 -80.220879 267 02/23/2025 20:14:04 267 -5.50 308.12 0.00 NW 12348 16.02 NNE 96097 26206 0 GPS 1- Air530
15 1.0 26.041656 -80.220879 267 02/23/2025 20:14:05 267 -5.50 308.12 0.00 NW 12348 16.02 NNE 96104 26208 0 GPS 1- Air530
15 0.8 26.041662 -80.220924 277 02/23/2025 20:14:06 277 -11.40 134.26 0.00 SE 12348 16.02 NNE 98139 26210 0 GPS 2- L76K GSS
15 1.0 26.041656 -80.220879 279 02/23/2025 20:14:07 279 -5.20 308.12 0.54 NW 12348 16.02 NNE 96119 26212 0 GPS 1- Air530
15 0.8 26.041662 -80.220924 290 02/23/2025 20:14:08 290 -11.40 134.26 0.00 SE 12348 16.02 NNE 98154 26214 0 GPS 2- L76K GSS
15 0.8 26.041662 -80.220924 295 02/23/2025 20:14:09 295 -11.40 134.26 0.00 SE 12348 16.02 NNE 98161 26216 0 GPS 2- L76K GSS
15 0.8 26.041662 -80.220924 293 02/23/2025 20:14:10 293 -11.40 134.26 0.00 SE 12348 16.02 NNE 98169 26218 0 GPS 2- L76K GSS
15 0.8 26.041662 -80.220924 301 02/23/2025 20:14:11 301 -11.40 134.26 0.00 SE 12348 16.02 NNE 98177 26220 0 GPS 2- L76K GSS
15 0.8 26.041662 -80.220924 303 02/23/2025 20:14:12 303 -11.40 134.26 0.00 SE 12348 16.02 NNE 98184 26222 0 GPS 2- L76K GSS
15 0.8
Scroll right to the end.
FQBN: esp32:esp32:XIAO_ESP32C3
Using board 'XIAO_ESP32C3' from platform in folder: C:\Users\Dude\AppData\Local\Arduino15\packages\esp32\hardware\esp32\3.0.7
Using core 'esp32' from platform in folder: C:\Users\Dude\AppData\Local\Arduino15\packages\esp32\hardware\esp32\3.0.7
**EDIT... for brevity...**
Using library TinyGPSPlus at version 1.0.3 in folder: D:\Arduino_projects\libraries\TinyGPSPlus
Using library EspSoftwareSerial at version 8.1.0 in folder: D:\Arduino_projects\libraries\EspSoftwareSerial
"C:\\Users\\Dude\\AppData\\Local\\Arduino15\\packages\\esp32\\tools\\esp-rv32\\2302/bin/riscv32-esp-elf-size" -A "C:\\Users\\Dude\\AppData\\Local\\arduino\\sketches\\C46D996DDDD904C0FBA2B540A6CCB432/sketch_feb23f_Posted_3_Serial_portsC3.ino.elf"
Sketch uses 284502 bytes (21%) of program storage space. Maximum is 1310720 bytes.
Global variables use 12700 bytes (3%) of dynamic memory, leaving 314980 bytes for local variables. Maximum is 327680 bytes.
"C:\Users\Dude\AppData\Local\Arduino15\packages\esp32\tools\esptool_py\4.6/esptool.exe" --chip esp32c3 --port "COM28" --baud 921600 --before default_reset --after hard_reset write_flash -z --flash_mode keep --flash_freq keep --flash_size keep 0x0 "C:\Users\Dude\AppData\Local\arduino\sketches\C46D996DDDD904C0FBA2B540A6CCB432/sketch_feb23f_Posted_3_Serial_portsC3.ino.bootloader.bin" 0x8000 "C:\Users\Dude\AppData\Local\arduino\sketches\C46D996DDDD904C0FBA2B540A6CCB432/sketch_feb23f_Posted_3_Serial_portsC3.ino.partitions.bin" 0xe000 "C:\Users\Dude\AppData\Local\Arduino15\packages\esp32\hardware\esp32\3.0.7/tools/partitions/boot_app0.bin" 0x10000 "C:\Users\Dude\AppData\Local\arduino\sketches\C46D996DDDD904C0FBA2B540A6CCB432/sketch_feb23f_Posted_3_Serial_portsC3.ino.bin"
esptool.py v4.6
Serial port COM28
Connecting...
Chip is ESP32-C3 (revision v0.3)
Features: WiFi, BLE
Crystal is 40MHz
MAC: a0:76:4e:40:08:ec
Uploading stub...
Running stub...
Stub running...
Code running as Posted…
// This is a 3 serial port demo using GPS units from Seeed, 2 GPS setup with ESP32C3
// Hardware Serial & Software Serial are implimented.
// Compiled with BSP 3.0.7
// added a column at the end for which GPS unit is sending the data
// tested with AIR530 V1.0 & L76K GNSS Seeed Xiao GPS unit.
// Either unit can be used in either Port position.
// See the Seeed Support form for pictures and errata.
#include <TinyGPSPlus.h>
#include <SoftwareSerial.h>
#include <HardwareSerial.h>
// ------------------ GPS Configurations ------------------
// For GPS 1 (HardwareSerial on UART1)
const int gps1RXPin = 9; // Connect GPS1 TX to ESP32 RX (pin 7) AIR530 Grove GPS
const int gps1TXPin = 10; // Connect GPS1 RX to ESP32 TX (pin 6)
const uint32_t gps1Baud = 9600;
// For GPS 2 (SoftwareSerial)
const int gps2RXPin = D7; // Connect GPS2 TX to ESP32 RX (pin 9) L76K GNSS SEEED Studio
const int gps2TXPin = D6; // Connect GPS2 RX to ESP32 TX (pin 10)
const uint32_t gps2Baud = 9600;
// ------------------ Objects ------------------
TinyGPSPlus gps1; // For HardwareSerial GPS
TinyGPSPlus gps2; // For SoftwareSerial GPS
// Use UART1 for GPS 1:
HardwareSerial SerialGPS1(1);
// Use SoftwareSerial for GPS 2:
SoftwareSerial SerialGPS2(gps2RXPin, gps2TXPin); // (RX, TX)
// ------------------ Function Prototypes ------------------
static void smartDelay(unsigned long ms);
static void printFloat(float val, bool valid, int len, int prec);
static void printInt(unsigned long val, bool valid, int len);
static void printDateTime(TinyGPSDate &d, TinyGPSTime &t);
static void printStr(const char *str, int len);
static void printGPSSource();
void setup() {
Serial.begin(9600);
delay(2000);
Serial.println("\nFullExample.ino");
Serial.println("An extensive example of many interesting TinyGPSPlus features");
Serial.print("Testing TinyGPSPlus library v. ");
Serial.println(TinyGPSPlus::libraryVersion());
Serial.println("by Mikal Hart, tweaked for 2 GPS by PJG");
Serial.println();
Serial.println("Sats HDOP Latitude Longitude Fix Date Time Date Alt Course Speed Card Distance Course Card Chars Sentences Checksum GPS");
Serial.println(" (deg) (deg) Age Age (m) --- from GPS ---- ---- to London ---- RX RX Fail");
Serial.println("----------------------------------------------------------------------------------------------------------------------------------------");
// Initialize GPS 1 (HardwareSerial) on UART1 with assigned pins.
SerialGPS1.begin(gps1Baud, SERIAL_8N1, gps1RXPin, gps1TXPin);
Serial.println("GPS-1 HardwareSerial initialized on pins 6 (RX) and 7 (TX).");
// Initialize GPS 2 (SoftwareSerial) on pins 9 & 10.
SerialGPS2.begin(gps2Baud);
Serial.println("GPS-2 SoftwareSerial initialized on pins 9 (RX) and 10 (TX).");
Serial.println();
}
void loop() {
// Process incoming GPS data for both GPS units.
// For GPS1 via HardwareSerial:
while (SerialGPS1.available() > 0) {
char c = SerialGPS1.read();
gps1.encode(c);
}
// For GPS2 via SoftwareSerial:
while (SerialGPS2.available() > 0) {
char c = SerialGPS2.read();
gps2.encode(c);
}
// Define a reference location (London in this example).
//static const double LONDON_LAT = 51.508131, LONDON_LON = -0.128002;
static const double PITTSBURGH_LAT = 40.4406, PITTSBURGH_LON = 79.9959;
// We'll decide which GPS to display.
// Priority: If one has valid location and the other doesn't, choose the valid one.
// If both are valid, choose the one with a lower "age" (i.e. more recent fix).
bool valid1 = gps1.location.isValid();
bool valid2 = gps2.location.isValid();
TinyGPSPlus *activeGps = nullptr;
if(valid1 && !valid2) {
activeGps = &gps1;
} else if(valid2 && !valid1) {
activeGps = &gps2;
} else if(valid1 && valid2) {
activeGps = (gps1.location.age() <= gps2.location.age()) ? &gps1 : &gps2;
} else {
activeGps = &gps1; // default to gps1 if none valid
}
// Print output using the active GPS data:
printInt(activeGps->satellites.value(), activeGps->satellites.isValid(), 5);
printFloat(activeGps->hdop.hdop(), activeGps->hdop.isValid(), 6, 1);
printFloat(activeGps->location.lat(), activeGps->location.isValid(), 11, 6);
printFloat(activeGps->location.lng(), activeGps->location.isValid(), 12, 6);
printInt(activeGps->location.age(), activeGps->location.isValid(), 5);
printDateTime(activeGps->date, activeGps->time);
printFloat(activeGps->altitude.meters(), activeGps->altitude.isValid(), 7, 2);
printFloat(activeGps->course.deg(), activeGps->course.isValid(), 7, 2);
printFloat(activeGps->speed.kmph(), activeGps->speed.isValid(), 6, 2);
printStr(activeGps->course.isValid() ? TinyGPSPlus::cardinal(activeGps->course.deg()) : "*** ", 6);
unsigned long distanceKmToLondon = (unsigned long)TinyGPSPlus::distanceBetween(
activeGps->location.lat(),
activeGps->location.lng(),
//LONDON_LAT, LONDON_LON) / 1000;
PITTSBURGH_LAT, PITTSBURGH_LON) / 1000 ;
printInt(distanceKmToLondon, activeGps->location.isValid(), 9);
double courseToLondon = TinyGPSPlus::courseTo(
activeGps->location.lat(),
activeGps->location.lng(),
//LONDON_LAT, LONDON_LON);
PITTSBURGH_LAT, PITTSBURGH_LON);
printFloat(courseToLondon, activeGps->location.isValid(), 7, 2);
const char *cardinalToLondon = TinyGPSPlus::cardinal(courseToLondon);
printStr(activeGps->location.isValid() ? cardinalToLondon : "*** ", 6);
printInt(activeGps->charsProcessed(), true, 6);
printInt(activeGps->sentencesWithFix(), true, 10);
printInt(activeGps->failedChecksum(), true, 9);
// Print additional column indicating source: "GPS 1" or "GPS 2".
Serial.print(" ");
if(activeGps == &gps1) {
Serial.print("GPS 1- Air530 ");
} else {
Serial.print("GPS 2- L76K GSS ");
}
Serial.println();
smartDelay(1000);
if (millis() > 5000 && activeGps->charsProcessed() < 10)
Serial.println(F("No GPS data received: check wiring"));
}
// Modified smartDelay() that feeds both GPS streams.
static void smartDelay(unsigned long ms) {
unsigned long start = millis();
do {
while (SerialGPS1.available() > 0)
gps1.encode(SerialGPS1.read());
while (SerialGPS2.available() > 0)
gps2.encode(SerialGPS2.read());
} while (millis() - start < ms);
}
static void printFloat(float val, bool valid, int len, int prec) {
if (!valid) {
while (len-- > 1)
Serial.print('*');
Serial.print(' ');
} else {
Serial.print(val, prec);
int vi = abs((int)val);
int flen = prec + (val < 0.0 ? 2 : 1); // . and -
flen += vi >= 1000 ? 4 : vi >= 100 ? 3 : vi >= 10 ? 2 : 1;
for (int i = flen; i < len; ++i)
Serial.print(' ');
}
smartDelay(0);
}
static void printInt(unsigned long val, bool valid, int len) {
char sz[32] = "*****************";
if (valid)
sprintf(sz, "%ld", val);
sz[len] = 0;
for (int i = strlen(sz); i < len; ++i)
sz[i] = ' ';
if (len > 0)
sz[len - 1] = ' ';
Serial.print(sz);
smartDelay(0);
}
static void printDateTime(TinyGPSDate &d, TinyGPSTime &t) {
if (!d.isValid()) {
Serial.print(F("********** "));
} else {
char sz[32];
sprintf(sz, "%02d/%02d/%02d ", d.month(), d.day(), d.year());
Serial.print(sz);
}
if (!t.isValid()) {
Serial.print(F("******** "));
} else {
char sz[32];
sprintf(sz, "%02d:%02d:%02d ", t.hour(), t.minute(), t.second());
Serial.print(sz);
}
printInt(d.age(), d.isValid(), 5);
smartDelay(0);
}
static void printStr(const char *str, int len) {
int slen = strlen(str);
for (int i = 0; i < len; ++i)
Serial.print(i < slen ? str[i] : ' ');
smartDelay(0);
}
HTH
GL PJ
Xiao ESP32C3 is All you neeed