|
| 1 | +/* |
| 2 | + Use ESP32 WiFi to get SPARTN data from PointPerfect (broker) as a Client |
| 3 | + By: u-blox AG / Michael Ammann |
| 4 | + Date: January 27th, 2022 |
| 5 | + License: MIT. See license file for more information but you can |
| 6 | + basically do whatever you want with this code. |
| 7 | +
|
| 8 | + This example shows how to obtain SPARTN data from a PointPerfect Broker over WiFi |
| 9 | + and push it over I2C to a ZED-F9x. |
| 10 | + It's confusing, but the Arduino is acting as a 'client' to the PointPerfect SSR correction service. |
| 11 | +
|
| 12 | + You will need to have a valid u-blox Thingstream account and have a PointPerfect Thing and payed plan. |
| 13 | + Thingstream offers SSR corrections to SPARTN cabalble RTK receivers such as the u-blox ZED-F9 series |
| 14 | + in continental Europ and US. There Network is planned to be expanded to ther regions over next years. |
| 15 | + To see sign up go to https://portal.thingstream.io/app/location-services/things |
| 16 | +
|
| 17 | + This is a proof of concept to show how to connect via MQTT to get SPARTN SSR correction. |
| 18 | + Using WiFi for a rover is generally a bad idea because of limited WiFi range in the field. |
| 19 | + You may use this exmaple in combination with a cell phone with hotspot mode enabled. |
| 20 | +
|
| 21 | + For more information about MQTT, SPARTN and PointPerfect Correction Services |
| 22 | + please see: https://www.u-blox.com/en/product/pointperfect |
| 23 | + |
| 24 | + Feel like supporting open source hardware? |
| 25 | + Buy a board from SparkFun! |
| 26 | + ZED-F9P RTK2: https://www.sparkfun.com/products/16481 |
| 27 | + RTK Surveyor: https://www.sparkfun.com/products/18443 |
| 28 | + RTK Express: https://www.sparkfun.com/products/18442 |
| 29 | + |
| 30 | + Recommended Hardware: |
| 31 | + MicroMod GNSS Carrier Board: https://www.sparkfun.com/products/17722 |
| 32 | + ESP32 Micromod https://www.sparkfun.com/products/16781 |
| 33 | +
|
| 34 | + Hardware Connections: |
| 35 | + Plug a Qwiic cable into the GNSS and a ESP32 Thing Plus |
| 36 | + If you don't have a platform with a Qwiic connection use the SparkFun Qwiic Breadboard Jumper (https://www.sparkfun.com/products/14425) |
| 37 | + Open the serial monitor at 115200 baud to see the output |
| 38 | +*/ |
| 39 | +#include <WiFi.h> |
| 40 | +#include <WiFiClientSecure.h> |
| 41 | +#include <ArduinoMqttClient.h> |
| 42 | +#include "secrets.h" |
| 43 | + |
| 44 | +#include <SparkFun_u-blox_GNSS_Arduino_Library.h> //http://librarymanager/All#SparkFun_u-blox_GNSS |
| 45 | +SFE_UBLOX_GNSS myGNSS; |
| 46 | + |
| 47 | +//Global variables |
| 48 | +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= |
| 49 | +long lastReceived_ms = 0; //5 RTCM messages take approximately ~300ms to arrive at 115200bps |
| 50 | +int maxTimeBeforeHangup_ms = 10000; //If we fail to get a complete RTCM frame after 10s, then disconnect from caster |
| 51 | +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= |
| 52 | + |
| 53 | +void setup() |
| 54 | +{ |
| 55 | + Serial.begin(115200); |
| 56 | + while (!Serial); |
| 57 | + Serial.println(F("PointPerfect testing")); |
| 58 | + |
| 59 | + Wire.begin(); //Start I2C |
| 60 | + |
| 61 | + if (myGNSS.begin() == false) //Connect to the Ublox module using Wire port |
| 62 | + { |
| 63 | + Serial.println(F("u-blox GPS not detected at default I2C address. Please check wiring. Freezing.")); |
| 64 | + while (1); |
| 65 | + } |
| 66 | + |
| 67 | + Serial.println(F("u-blox module connected")); |
| 68 | + myGNSS.setI2COutput(COM_TYPE_UBX); //Turn off NMEA noise |
| 69 | + myGNSS.setPortInput(COM_PORT_I2C, COM_TYPE_UBX | COM_TYPE_NMEA | COM_TYPE_SPARTN); // Be sure SPARTN input is enabled. |
| 70 | + |
| 71 | + myGNSS.setNavigationFrequency(1); //Set output in Hz. |
| 72 | + Serial.print(F("Connecting to local WiFi")); |
| 73 | + WiFi.begin(ssid, password); |
| 74 | + while (WiFi.status() != WL_CONNECTED) { |
| 75 | + delay(500); |
| 76 | + Serial.print(F(".")); |
| 77 | + } |
| 78 | + Serial.println(); |
| 79 | + |
| 80 | + Serial.print(F("WiFi connected with IP: ")); |
| 81 | + Serial.println(WiFi.localIP()); |
| 82 | + |
| 83 | + while (Serial.available()) Serial.read(); |
| 84 | +} |
| 85 | + |
| 86 | +void loop() |
| 87 | +{ |
| 88 | + if (Serial.available()) |
| 89 | + { |
| 90 | + beginClient(); |
| 91 | + while (Serial.available()) Serial.read(); //Empty buffer of any newline chars |
| 92 | + } |
| 93 | + |
| 94 | + Serial.println(F("Press any key to start MQTT/SPARTN Client.")); |
| 95 | + |
| 96 | + delay(1000); |
| 97 | +} |
| 98 | + |
| 99 | +WiFiClientSecure wifiClient = WiFiClientSecure(); |
| 100 | +MqttClient mqttClient(wifiClient); |
| 101 | + |
| 102 | +void mqttMessageHandler(int messageSize) { |
| 103 | + uint8_t spartnData[512 * 4]; //Most incoming data is around 500 bytes but may be larger |
| 104 | + int spartnCount = 0; |
| 105 | + Serial.print(F("Pushed data from ")); |
| 106 | + Serial.print(mqttClient.messageTopic()); |
| 107 | + Serial.println(F(" topic to ZED")); |
| 108 | + while (mqttClient.available()) |
| 109 | + { |
| 110 | + char ch = mqttClient.read(); |
| 111 | + //Serial.write(ch); //Pipe to serial port is fine but beware, it's a lot of binary data |
| 112 | + spartnData[spartnCount++] = ch; |
| 113 | + if (spartnCount == sizeof(spartnData)) |
| 114 | + break; |
| 115 | + } |
| 116 | + |
| 117 | + if (spartnCount > 0) |
| 118 | + { |
| 119 | + //Push KEYS or SPARTN data to GNSS module over I2C |
| 120 | + myGNSS.pushRawData(spartnData, spartnCount, false); |
| 121 | + lastReceived_ms = millis(); |
| 122 | + } |
| 123 | +} |
| 124 | + |
| 125 | +//Connect to STARTN MQTT broker, receive RTCM, and push to ZED module over I2C |
| 126 | +void beginClient() |
| 127 | +{ |
| 128 | + Serial.println(F("Subscribing to Broker. Press key to stop")); |
| 129 | + delay(10); //Wait for any serial to arrive |
| 130 | + while (Serial.available()) Serial.read(); //Flush |
| 131 | + |
| 132 | + while (Serial.available() == 0) |
| 133 | + { |
| 134 | + //Connect if we are not already |
| 135 | + if (wifiClient.connected() == false) |
| 136 | + { |
| 137 | + // Connect to AWS IoT |
| 138 | + wifiClient.setCACert(AWS_CERT_CA); |
| 139 | + wifiClient.setCertificate(AWS_CERT_CRT); |
| 140 | + wifiClient.setPrivateKey(AWS_CERT_PRIVATE); |
| 141 | + if (!mqttClient.connect(AWS_IOT_ENDPOINT, AWS_IOT_PORT)) { |
| 142 | + Serial.print(F("MQTT connection failed! Error code = ")); |
| 143 | + Serial.println(mqttClient.connectError()); |
| 144 | + return; |
| 145 | + } else { |
| 146 | + Serial.println(F("You're connected to the PointPerfect MQTT broker: ")); |
| 147 | + Serial.println(AWS_IOT_ENDPOINT); |
| 148 | + // Subscribe to MQTT and register a callback |
| 149 | + Serial.println(F("Subscribe to Topics")); |
| 150 | + mqttClient.onMessage(mqttMessageHandler); |
| 151 | + mqttClient.subscribe(MQTT_TOPIC_KEY); |
| 152 | + mqttClient.subscribe(MQTT_TOPIC_SPARTN); |
| 153 | + lastReceived_ms = millis(); |
| 154 | + } //End attempt to connect |
| 155 | + } //End connected == false |
| 156 | + else { |
| 157 | + mqttClient.poll(); |
| 158 | + } |
| 159 | + //Close socket if we don't have new data for 10s |
| 160 | + if (millis() - lastReceived_ms > maxTimeBeforeHangup_ms) |
| 161 | + { |
| 162 | + Serial.println(F("SPARTN timeout. Disconnecting...")); |
| 163 | + if (mqttClient.connected() == true) |
| 164 | + mqttClient.stop(); |
| 165 | + return; |
| 166 | + } |
| 167 | + |
| 168 | + delay(10); |
| 169 | + } |
| 170 | + |
| 171 | + Serial.println(F("User pressed a key")); |
| 172 | + Serial.println(F("Disconnecting...")); |
| 173 | + wifiClient.stop(); |
| 174 | +} |
0 commit comments