diff --git a/README.md b/README.md
index 2a64007..6b233a4 100644
--- a/README.md
+++ b/README.md
@@ -104,6 +104,10 @@ Please check the module datasheets for details on what clock speeds and data rat
For I2C communication, please be sure to remove all additional pull-ups on the I2C bus. u-blox modules include internal pull-ups on the I2C lines (sometimes called DDC in their manuals). Cut all I2C pull-up jumpers and/or remove them from peripheral boards. Otherwise, various data glitches can occur. See issues [38](https://github.com/sparkfun/SparkFun_Ublox_Arduino_Library/issues/38) and [40](https://github.com/sparkfun/SparkFun_Ublox_Arduino_Library/issues/40) for more information. We recommend running the I2C bus at 100kHz.
+## Compatibility
+
+v2 of the library provides support for generation 8, 9 and 10 u-blox GNSS modules. For generation 6 and 7, please see [this example (depricated)](https://github.com/sparkfun/SparkFun_Ublox_Arduino_Library/tree/master/examples/Series_6_7/Example1_GetPositionAndTime_Series_6_7).
+
## Contributing
If you would like to contribute to this library: please do, we truly appreciate it, but please follow [these guidelines](./CONTRIBUTING.md). Thanks!
diff --git a/examples/ZED-F9P/Example18_PointPerfectClient/Example18_PointPerfectClient.ino b/examples/ZED-F9P/Example18_PointPerfectClient/Example18_PointPerfectClient.ino
new file mode 100644
index 0000000..d9f8fd0
--- /dev/null
+++ b/examples/ZED-F9P/Example18_PointPerfectClient/Example18_PointPerfectClient.ino
@@ -0,0 +1,174 @@
+/*
+ Use ESP32 WiFi to get SPARTN data from PointPerfect (broker) as a Client
+ By: u-blox AG / Michael Ammann
+ Date: January 27th, 2022
+ License: MIT. See license file for more information but you can
+ basically do whatever you want with this code.
+
+ This example shows how to obtain SPARTN data from a PointPerfect Broker over WiFi
+ and push it over I2C to a ZED-F9x.
+ It's confusing, but the Arduino is acting as a 'client' to the PointPerfect SSR correction service.
+
+ You will need to have a valid u-blox Thingstream account and have a PointPerfect Thing and payed plan.
+ Thingstream offers SSR corrections to SPARTN cabalble RTK receivers such as the u-blox ZED-F9 series
+ in continental Europ and US. There Network is planned to be expanded to ther regions over next years.
+ To see sign up go to https://portal.thingstream.io/app/location-services/things
+
+ This is a proof of concept to show how to connect via MQTT to get SPARTN SSR correction.
+ Using WiFi for a rover is generally a bad idea because of limited WiFi range in the field.
+ You may use this exmaple in combination with a cell phone with hotspot mode enabled.
+
+ For more information about MQTT, SPARTN and PointPerfect Correction Services
+ please see: https://www.u-blox.com/en/product/pointperfect
+
+ Feel like supporting open source hardware?
+ Buy a board from SparkFun!
+ ZED-F9P RTK2: https://www.sparkfun.com/products/16481
+ RTK Surveyor: https://www.sparkfun.com/products/18443
+ RTK Express: https://www.sparkfun.com/products/18442
+
+ Recommended Hardware:
+ MicroMod GNSS Carrier Board: https://www.sparkfun.com/products/17722
+ ESP32 Micromod https://www.sparkfun.com/products/16781
+
+ Hardware Connections:
+ Plug a Qwiic cable into the GNSS and a ESP32 Thing Plus
+ If you don't have a platform with a Qwiic connection use the SparkFun Qwiic Breadboard Jumper (https://www.sparkfun.com/products/14425)
+ Open the serial monitor at 115200 baud to see the output
+*/
+#include
+#include
+#include
+#include "secrets.h"
+
+#include //http://librarymanager/All#SparkFun_u-blox_GNSS
+SFE_UBLOX_GNSS myGNSS;
+
+//Global variables
+//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+long lastReceived_ms = 0; //5 RTCM messages take approximately ~300ms to arrive at 115200bps
+int maxTimeBeforeHangup_ms = 10000; //If we fail to get a complete RTCM frame after 10s, then disconnect from caster
+//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+
+void setup()
+{
+ Serial.begin(115200);
+ while (!Serial);
+ Serial.println(F("PointPerfect testing"));
+
+ Wire.begin(); //Start I2C
+
+ if (myGNSS.begin() == false) //Connect to the Ublox module using Wire port
+ {
+ Serial.println(F("u-blox GPS not detected at default I2C address. Please check wiring. Freezing."));
+ while (1);
+ }
+
+ Serial.println(F("u-blox module connected"));
+ myGNSS.setI2COutput(COM_TYPE_UBX); //Turn off NMEA noise
+ myGNSS.setPortInput(COM_PORT_I2C, COM_TYPE_UBX | COM_TYPE_NMEA | COM_TYPE_SPARTN); // Be sure SPARTN input is enabled.
+
+ myGNSS.setNavigationFrequency(1); //Set output in Hz.
+ Serial.print(F("Connecting to local WiFi"));
+ WiFi.begin(ssid, password);
+ while (WiFi.status() != WL_CONNECTED) {
+ delay(500);
+ Serial.print(F("."));
+ }
+ Serial.println();
+
+ Serial.print(F("WiFi connected with IP: "));
+ Serial.println(WiFi.localIP());
+
+ while (Serial.available()) Serial.read();
+}
+
+void loop()
+{
+ if (Serial.available())
+ {
+ beginClient();
+ while (Serial.available()) Serial.read(); //Empty buffer of any newline chars
+ }
+
+ Serial.println(F("Press any key to start MQTT/SPARTN Client."));
+
+ delay(1000);
+}
+
+WiFiClientSecure wifiClient = WiFiClientSecure();
+MqttClient mqttClient(wifiClient);
+
+void mqttMessageHandler(int messageSize) {
+ uint8_t spartnData[512 * 4]; //Most incoming data is around 500 bytes but may be larger
+ int spartnCount = 0;
+ Serial.print(F("Pushed data from "));
+ Serial.print(mqttClient.messageTopic());
+ Serial.println(F(" topic to ZED"));
+ while (mqttClient.available())
+ {
+ char ch = mqttClient.read();
+ //Serial.write(ch); //Pipe to serial port is fine but beware, it's a lot of binary data
+ spartnData[spartnCount++] = ch;
+ if (spartnCount == sizeof(spartnData))
+ break;
+ }
+
+ if (spartnCount > 0)
+ {
+ //Push KEYS or SPARTN data to GNSS module over I2C
+ myGNSS.pushRawData(spartnData, spartnCount, false);
+ lastReceived_ms = millis();
+ }
+}
+
+//Connect to STARTN MQTT broker, receive RTCM, and push to ZED module over I2C
+void beginClient()
+{
+ Serial.println(F("Subscribing to Broker. Press key to stop"));
+ delay(10); //Wait for any serial to arrive
+ while (Serial.available()) Serial.read(); //Flush
+
+ while (Serial.available() == 0)
+ {
+ //Connect if we are not already
+ if (wifiClient.connected() == false)
+ {
+ // Connect to AWS IoT
+ wifiClient.setCACert(AWS_CERT_CA);
+ wifiClient.setCertificate(AWS_CERT_CRT);
+ wifiClient.setPrivateKey(AWS_CERT_PRIVATE);
+ if (!mqttClient.connect(AWS_IOT_ENDPOINT, AWS_IOT_PORT)) {
+ Serial.print(F("MQTT connection failed! Error code = "));
+ Serial.println(mqttClient.connectError());
+ return;
+ } else {
+ Serial.println(F("You're connected to the PointPerfect MQTT broker: "));
+ Serial.println(AWS_IOT_ENDPOINT);
+ // Subscribe to MQTT and register a callback
+ Serial.println(F("Subscribe to Topics"));
+ mqttClient.onMessage(mqttMessageHandler);
+ mqttClient.subscribe(MQTT_TOPIC_KEY);
+ mqttClient.subscribe(MQTT_TOPIC_SPARTN);
+ lastReceived_ms = millis();
+ } //End attempt to connect
+ } //End connected == false
+ else {
+ mqttClient.poll();
+ }
+ //Close socket if we don't have new data for 10s
+ if (millis() - lastReceived_ms > maxTimeBeforeHangup_ms)
+ {
+ Serial.println(F("SPARTN timeout. Disconnecting..."));
+ if (mqttClient.connected() == true)
+ mqttClient.stop();
+ return;
+ }
+
+ delay(10);
+ }
+
+ Serial.println(F("User pressed a key"));
+ Serial.println(F("Disconnecting..."));
+ wifiClient.stop();
+}
diff --git a/examples/ZED-F9P/Example18_PointPerfectClient/secrets.h b/examples/ZED-F9P/Example18_PointPerfectClient/secrets.h
new file mode 100644
index 0000000..4353c50
--- /dev/null
+++ b/examples/ZED-F9P/Example18_PointPerfectClient/secrets.h
@@ -0,0 +1,38 @@
+//Your WiFi credentials
+const char ssid[] = "";
+const char password[] = "";
+
+// Below infomation you can set after signing up with u-blox Thingstream portal
+// and after add a new New PointPerfect Thing
+// https://portal.thingstream.io/app/location-services/things
+// in the new PointPerfect Thing you go to the credentials page and copy past the values and certificate into this.
+
+// -> Credentials -> Hostname
+const char AWS_IOT_ENDPOINT[] = "pp.services.u-blox.com";
+const unsigned short AWS_IOT_PORT = 8883;
+// -> Credentials -> IP key distribution topic
+const char MQTT_TOPIC_KEY[] = "/pp/key/ip";
+// -> Credentials -> IP correction topic for EU/US region
+const char MQTT_TOPIC_SPARTN[] = "/pp/ip/us"; // choice of {eu, us}
+
+// -> Credentials -> Amazon Root Certificate
+static const char AWS_CERT_CA[] PROGMEM = R"EOF(
+-----BEGIN CERTIFICATE-----
+
+-----END CERTIFICATE-----
+)EOF";
+
+// -> Credentials -> Client Certificate
+static const char AWS_CERT_CRT[] PROGMEM = R"KEY(
+-----BEGIN CERTIFICATE-----
+
+-----END CERTIFICATE-----
+)KEY";
+
+// Get this from Thingstream Portal
+// -> Credentials -> Client Key
+static const char AWS_CERT_PRIVATE[] PROGMEM = R"KEY(
+-----BEGIN RSA PRIVATE KEY-----
+
+-----END RSA PRIVATE KEY-----
+)KEY";
diff --git a/keywords.txt b/keywords.txt
index b40d37e..32d0fcb 100644
--- a/keywords.txt
+++ b/keywords.txt
@@ -597,6 +597,7 @@ SFE_UBLOX_STATUS_DATA_OVERWRITTEN LITERAL1
COM_TYPE_UBX LITERAL1
COM_TYPE_NMEA LITERAL1
COM_TYPE_RTCM3 LITERAL1
+COM_TYPE_SPARTN LITERAL1
COM_PORT_I2C LITERAL1
COM_PORT_UART1 LITERAL1
diff --git a/src/u-blox_structs.h b/src/u-blox_structs.h
index eb51598..e7588b4 100644
--- a/src/u-blox_structs.h
+++ b/src/u-blox_structs.h
@@ -1463,6 +1463,33 @@ typedef struct
UBX_RXM_RAWX_data_t *callbackData;
} UBX_RXM_RAWX_t;
+// UBX-RXM-PMP (0x02 0x72): PMP raw data (D9 modules)
+const uint16_t UBX_RXM_PMP_MAX_LEN = 528;
+
+typedef struct
+{
+ uint8_t version; // Message version (0x00 for this version)
+ uint8_t reserved0[3]; // Reserved
+ uint32_t timeTag; // Time since startup when frame started : ms
+ uint32_t uniqueWord[2]; // Received unique words
+ uint16_t serviceIdentifier; // Received service identifier
+ uint8_t spare; // Received spare data
+ uint8_t uniqueWordBitErrors; // Number of bit errors in both unique words
+ uint8_t userData[504]; // Received user data
+ uint16_t fecBits; // Number of bits corrected by FEC (forward error correction)
+ uint8_t ebno; // Energy per bit to noise power spectral density ratio : 2^-3 dB
+ uint8_t reserved1; // Reserved
+} UBX_RXM_PMP_data_t;
+
+typedef struct
+{
+ ubxAutomaticFlags automaticFlags;
+ UBX_RXM_PMP_data_t data;
+ bool moduleQueried;
+ void (*callbackPointer)(UBX_RXM_PMP_data_t);
+ UBX_RXM_PMP_data_t *callbackData;
+} UBX_RXM_PMP_t;
+
// CFG-specific structs
// UBX-CFG-RATE (0x06 0x08): Navigation/measurement rate settings