Skip to content

Instantly share code, notes, and snippets.

@BrentonPoke
Created November 3, 2025 19:17
Show Gist options
  • Select an option

  • Save BrentonPoke/7e8cedb5bf8ed3c7f204223e53c99a1d to your computer and use it in GitHub Desktop.

Select an option

Save BrentonPoke/7e8cedb5bf8ed3c7f204223e53c99a1d to your computer and use it in GitHub Desktop.
DuckGPS Original
//
// Created by brent on 7/7/2023.
//
#include "DuckGPS.h"
void DuckGPS::readData(unsigned long ms) {
unsigned long start = millis();
do
{
while (GPSSerial.available())
gps.encode(GPSSerial.read());
} while (millis() - start < ms);
printData();
}
std::time_t DuckGPS::tmConvert_t(int YYYY, byte MM, byte DD, byte hh, byte mm, byte ss)
{
std::tm tmSet{};
tmSet.tm_year = YYYY - 1900;
tmSet.tm_mon = MM - 1;
tmSet.tm_mday = DD;
tmSet.tm_hour = hh;
tmSet.tm_min = mm;
tmSet.tm_sec = ss;
std::time_t t = std::mktime(&tmSet);
return mktime(std::gmtime(&t));
}
std::time_t DuckGPS::epoch(){
return tmConvert_t(
gps.date.year(),
gps.date.month(),
gps.date.day(),
gps.time.hour(),
gps.time.minute(),
gps.time.second());
}
void DuckGPS::printData(){
// Printing the GPS data
logdbg_ln("--- GPS ---");
logdbg_ln(std::string("Latitude : "+std::to_string(lat())).c_str());
logdbg_ln(std::string("Longitude : "+std::to_string(lng())).c_str());
logdbg_ln(std::string("Altitude : "+std::to_string(altitude(AltitudeUnit::meter))).append("M").c_str());
logdbg_ln(std::string("Satellites : "+std::to_string(satellites())).c_str());
logdbg_ln(std::string("Raw Date : "+std::to_string(gps.date.value())).c_str());
logdbg_ln(std::string("Epoch : "+std::to_string(epoch())).c_str());
logdbg_ln(std::string("Raw Date : "+std::to_string(speed(SpeedUnit::kmph))).append("KPH").c_str());
logdbg_ln("**********************");
}
double DuckGPS::lat() {
return gps.location.lat();
}
double DuckGPS::lng() {
return gps.location.lng();
}
std::string DuckGPS::geoJsonPoint(){
std::string geojsonpoint = "{\"type\": \"Point\", \"coordinates\": [";
return geojsonpoint
.append(std::to_string(lat()))
.append(",")
.append(std::to_string(lng())
.append("]}"));
}
double DuckGPS::altitude(AltitudeUnit u){
switch(u){
case AltitudeUnit::meter: return gps.altitude.meters();
case AltitudeUnit::feet: return gps.altitude.feet();
case AltitudeUnit::miles: return gps.altitude.miles();
case AltitudeUnit::kilo: return gps.altitude.kilometers();
default: return -1.0;
}
}
double DuckGPS::speed(SpeedUnit u){
switch(u){
case SpeedUnit::kmph: return gps.speed.kmph();
case SpeedUnit::mph: return gps.speed.mph();
case SpeedUnit::mps: return gps.speed.mps();
case SpeedUnit::knots: return gps.speed.knots();
default: return -1.0;
}
}
uint32_t DuckGPS::satellites() {
return gps.satellites.value();
}
uint8_t DuckGPS::makeUBXPacket(uint8_t class_id, uint8_t msg_id, uint8_t payload_size, const uint8_t *msg)
{
// Construct the UBX packet
UBXscratch[0] = 0xB5; // header
UBXscratch[1] = 0x62; // header
UBXscratch[2] = class_id; // class
UBXscratch[3] = msg_id; // id
UBXscratch[4] = payload_size; // length
UBXscratch[5] = 0x00;
UBXscratch[6 + payload_size] = 0x00; // CK_A
UBXscratch[7 + payload_size] = 0x00; // CK_B
for (int i = 0; i < payload_size; i++) {
UBXscratch[6 + i] = pgm_read_byte(&msg[i]);
}
uint8_t CK_A = 0, CK_B = 0;
int length = payload_size + 8;
for (size_t i = 2; i < length - 2; i++) {
CK_A = (CK_A + UBXscratch[i]) & 0xFF;
CK_B = (CK_B + CK_A) & 0xFF;
}
// Place the calculated checksum values in the message
UBXscratch[length - 2] = CK_A;
UBXscratch[length - 1] = CK_B;
return length;
}
DuckGPS::GPS_RESPONSE DuckGPS::getACK(uint8_t class_id, uint8_t msg_id, uint32_t waitMillis) {
uint8_t b;
uint8_t ack = 0;
std::array<const uint8_t,2> ackP{class_id, msg_id};
std::array<uint8_t,10> buf{0xB5, 0x62, 0x05, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00};
uint32_t startTime = millis();
const char frame_errors[] = "More than 100 frame errors";
int sCounter = 0;
// Calculate checksum for ACK message
for (int j = 2; j < 6; j++) {
buf[8] += buf[j];
buf[9] += buf[8];
}
for (int j = 0; j < 2; j++) {
buf[6 + j] = ackP[j];
buf[8] += buf[6 + j];
buf[9] += buf[8];
}
while (millis() - startTime < waitMillis) {
if (ack > 9) {
#ifdef GPS_DEBUG
LOG_DEBUG("\n");
LOG_INFO("Got ACK for class %02X message %02X in %d millis.\n", class_id, msg_id, millis() - startTime);
#endif
return GNSS_RESPONSE_OK; // ACK received
}
if (GPSSerial.available()) {
b = GPSSerial.read();
if (b == frame_errors[sCounter]) {
sCounter++;
if (sCounter == 26) {
return GNSS_RESPONSE_FRAME_ERRORS;
}
} else {
sCounter = 0;
}
#ifdef GPS_DEBUG
LOG_DEBUG("%02X", b);
#endif
if (b == buf[ack]) {
ack++;
} else {
if (ack == 3 && b == 0x00) { // UBX-ACK-NAK message
#ifdef GPS_DEBUG
LOG_DEBUG("\n");
#endif
logwarn("Got NAK for class %02X message %02X\n", class_id, msg_id);
return GNSS_RESPONSE_NAK; // NAK received
}
ack = 0; // Reset the acknowledgement counter
}
}
}
#ifdef GPS_DEBUG
LOG_DEBUG("\n");
logwarn_f("No response for class %02X message %02X\n", class_id, msg_id);
#endif
return GNSS_RESPONSE_NONE; // No response received within timeout
}
//
// Created by brent on 7/7/2023.
//
#ifndef CLUSTERDUCK_PROTOCOL_DUCKGPS_H
#define CLUSTERDUCK_PROTOCOL_DUCKGPS_H
#include "utils/DuckLogger.h"
#include <include/cdpcfg.h>
#include <TinyGPS++.h>
#include <ctime>
#include <memory>
#include <array>
class DuckGPS {
public:
#if defined(CDPCFG_GPS_RX) && defined(CDPCFG_GPS_TX)
DuckGPS() : GPSSerial(1){
GPSSerial.begin(9600, SERIAL_8N1, CDPCFG_GPS_RX, CDPCFG_GPS_TX);
};
#endif
DuckGPS(int Rx, int Tx) : GPSSerial(1) {
GPSSerial.begin(9600, SERIAL_8N1, Rx, Tx);
};
void setup(){
// memset(&info, 0, sizeof(struct GnssModelInfo));
std::array<uint8_t,768> buffer = {0};
delay(100);
GPSSerial.write("$PCAS03,0,0,0,0,0,0,0,0,0,0,,,0,0*02\r\n");
delay(20);
clearBuffer();
int msglen = 0;
std::string hardwareVersion;
std::copy(info.hwVersion.begin(),
info.hwVersion.end(),
hardwareVersion.begin());
loginfo("Configuring GNSS module, HW Version: %s\n", hardwareVersion.c_str());
if (std::equal(hardwareVersion.begin(), hardwareVersion.end(),"00040007")) { // The original ublox 6 is GPS only and doesn't support the UBX-CFG-GNSS message
if (std::equal(hardwareVersion.begin(), hardwareVersion.end(), "00070000")) { // Max7 seems to only support GPS *or* GLONASS
logdbg("Setting GPS+SBAS\n");
msglen = makeUBXPacket(0x06, 0x3e, _message_GNSS_7.size(), _message_GNSS_7.data());
GPSSerial.write(UBXscratch.data(), msglen);
} else {
msglen = makeUBXPacket(0x06, 0x3e, _message_GNSS_8.size(), _message_GNSS_8.data());
GPSSerial.write(UBXscratch.data(), msglen);
}
if (getACK(0x06, 0x3e, 800) == GNSS_RESPONSE_NAK) {
// It's not critical if the module doesn't acknowledge this configuration.
loginfo("Unable to reconfigure GNSS - defaults maintained. Is this module GPS-only?\n");
} else {
if (std::equal(hardwareVersion.begin(), hardwareVersion.end(), "00070000")) {
loginfo("GNSS configured for GPS+SBAS. Pause for 0.75s before sending next command.\n");
} else {
loginfo("GNSS configured for GPS+SBAS+GLONASS+Galileo. Pause for 0.75s before sending next command.\n");
}
// Documentation say, we need wait atleast 0.5s after reconfiguration of GNSS module, before sending next
// commands
}
delay(1000);
msglen = makeUBXPacket(0x06, 0x39, _message_JAM.size(), _message_JAM.data());
GPSSerial.write(UBXscratch.data(), msglen);
if (getACK(0x06, 0x39, 300) != GNSS_RESPONSE_OK) {
logwarn("Unable to enable interference resistance.\n");
}
msglen = makeUBXPacket(0x06, 0x23, _message_NAVX5.size(), _message_NAVX5.data());
GPSSerial.write(UBXscratch.data(), msglen);
if (getACK(0x06, 0x23, 300) != GNSS_RESPONSE_OK) {
logwarn("Unable to configure extra settings.\n");
}
// ublox-M10S can be compatible with UBLOX traditional protocol, so the following sentence settings are also valid
msglen = makeUBXPacket(0x06, 0x08, _message_1HZ.size(), _message_1HZ.data());
GPSSerial.write(UBXscratch.data(), msglen);
if (getACK(0x06, 0x08, 300) != GNSS_RESPONSE_OK) {
logwarn("Unable to set GPS update rate.\n");
}
//
msglen = makeUBXPacket(0x06, 0x01, _message_GGL.size(), _message_GGL.data());
GPSSerial.write(UBXscratch.data(), msglen);
if (getACK(0x06, 0x01, 300) != GNSS_RESPONSE_OK) {
logwarn("Unable to disable NMEA GGL.\n");
}
msglen = makeUBXPacket(0x06, 0x01, _message_GSA.size(), _message_GSA.data());
GPSSerial.write(UBXscratch.data(), msglen);
if (getACK(0x06, 0x01, 300) != GNSS_RESPONSE_OK) {
logwarn("Unable to Enable NMEA GSA.\n");
}
msglen = makeUBXPacket(0x06, 0x01, _message_GSV.size(), _message_GSV.data());
GPSSerial.write(UBXscratch.data(), msglen);
if (getACK(0x06, 0x01, 300) != GNSS_RESPONSE_OK) {
logwarn("Unable to disable NMEA GSV.\n");
}
msglen = makeUBXPacket(0x06, 0x01, _message_VTG.size(), _message_VTG.data());
GPSSerial.write(UBXscratch.data(), msglen);
if (getACK(0x06, 0x01, 300) != GNSS_RESPONSE_OK) {
logwarn("Unable to disable NMEA VTG.\n");
}
msglen = makeUBXPacket(0x06, 0x01, _message_RMC.size(), _message_RMC.data());
GPSSerial.write(UBXscratch.data(), msglen);
if (getACK(0x06, 0x01, 300) != GNSS_RESPONSE_OK) {
logwarn("Unable to enable NMEA RMC.\n");
}
msglen = makeUBXPacket(0x06, 0x01, _message_GGA.size(), _message_GGA.data());
GPSSerial.write(UBXscratch.data(), msglen);
if (getACK(0x06, 0x01, 300) != GNSS_RESPONSE_OK) {
logwarn("Unable to enable NMEA GGA.\n");
}
if (std::equal(hardwareVersion.begin(),hardwareVersion.end(), "00080000")) {
msglen = makeUBXPacket(0x06, 0x17, _message_NMEA.size(), _message_NMEA.data());
clearBuffer();
GPSSerial.write(UBXscratch.data(), msglen);
if (getACK(0x06, 0x17, 500) != GNSS_RESPONSE_OK) {
logwarn("Unable to enable NMEA 4.10.\n");
}
}
}
}
enum AltitudeUnit{
meter,
kilo,
feet,
miles
};
enum SpeedUnit{
kmph,
mps, // Speed in meters per second
mph, // Speed in miles per hour
knots
};
/**
* @brief continuously reads data from GPS chip
*
* @param unsigned long value for number of milliseconds to read data for
* @return none
*/
void readData(unsigned long ms);
double lat(), lng();
/**
* @brief helper method for getting [lat,lng] as a GeoJSON object.
*
* @param none
* @return std::string with GeoJSON object that can be parsed by the developer
*/
std::string geoJsonPoint();
/**
* @brief helper method to return time from GPS data
*
* @param none
* @return std::time_t in epoch seconds
*/
std::time_t epoch();
/**
* @brief helper method to return speed
*
* @param DuckGPS::SpeedUnit
* @return double value for chosen speed unit
*/
double speed(SpeedUnit u);
/**
* @brief helper method to return number of satellites
*
* @param none
* @return uint32_t value for number of satellites
*/
uint32_t satellites();
/**
* @brief helper method to return altitude
*
* @param DuckGPS::AltitudeUnit
* @return double value for chosen altitude unit
*/
double altitude(AltitudeUnit u);
protected:
void printData();
std::time_t tmConvert_t(int YYYY, byte MM, byte DD, byte hh, byte mm, byte ss);
uint8_t makeUBXPacket(uint8_t class_id, uint8_t msg_id, uint8_t payload_size, const uint8_t *msg);
private:
TinyGPSPlus gps;
HardwareSerial GPSSerial;
std::array<uint8_t,250> UBXscratch = {0};
void clearBuffer()
{
int x = GPSSerial.available();
while (x--)
GPSSerial.read();
}
struct GnssModelInfo {
std::array<char,30> swVersion;
std::array<char,10> hwVersion;
uint8_t extensionNo;
std::array<std::array<char,30>,10> extension;
} info;
typedef enum
{
SFE_UBLOX_PACKET_VALIDITY_NOT_VALID,
SFE_UBLOX_PACKET_VALIDITY_VALID,
SFE_UBLOX_PACKET_VALIDITY_NOT_DEFINED,
SFE_UBLOX_PACKET_NOTACKNOWLEDGED // This indicates that we received a NACK
} sfe_ublox_packet_validity_e;
typedef enum {
GNSS_RESPONSE_NONE,
GNSS_RESPONSE_NAK,
GNSS_RESPONSE_FRAME_ERRORS,
GNSS_RESPONSE_OK,
} GPS_RESPONSE;
GPS_RESPONSE getACK(uint8_t class_id, uint8_t msg_id, uint32_t waitMillis);
struct ubxPacket
{
uint8_t cls;
uint8_t id;
uint16_t len; // Length of the payload. Does not include cls, id, or checksum bytes
uint16_t counter; // Keeps track of number of overall bytes received. Some responses are larger than 255 bytes.
uint16_t startingSpot; // The counter value needed to go past before we begin recording into payload array
uint8_t *payload; // We will allocate RAM for the payload if/when needed.
uint8_t checksumA; // Given to us from module. Checked against the rolling calculated A/B checksums.
uint8_t checksumB;
sfe_ublox_packet_validity_e valid; // Goes from NOT_DEFINED to VALID or NOT_VALID when checksum is checked
sfe_ublox_packet_validity_e classAndIDmatch; // Goes from NOT_DEFINED to VALID or NOT_VALID when the Class and ID match the requestedClass and requestedID
};
std::array<const uint8_t,58> ubx_cfg_gnss = {
0x00,0x08,0x10,0x00,0x01,0x00,0x01,0x01, // GPS, Min/Max Channel Resources, ENABLED, L1, BIT24 (per uCenter/Query, cut-n-paste of hex frame)
0x01,0x01,0x03,0x00,0x01,0x00,0x01,0x01, // SBAS
0x02,0x04,0x08,0x00,0x01,0x00,0x01,0x01, // GALILEO
0x03,0x08,0x10,0x00,0x00,0x00,0x01,0x01, // BEIDOU (DISABLED)
0x04,0x00,0x08,0x00,0x00,0x00,0x01,0x01, // IMES (DISABLED)
0x05,0x00,0x03,0x00,0x01,0x00,0x01,0x01, // QZSS
0x06,0x08,0x0E,0x00,0x01,0x00,0x01,0x01, // GLONASS
0x30,0xAD }; // Fletcher checksum, correct for preceeding frame
std::array<const uint8_t,68> _message_GPSGLONASSGAL = {// GPS + GALILEO + GLONASS wo / SBAS
0xB5,0x62,0x06,0x3E, 0x3C, 0x00,
0x00,0x00,0x20,0x07,
0x00,0x00,0x10,0x00,0x01,0x00,0x01,0x01,
0x01,0x00,0x00,0x00,0x00,0x00,0x01,0x01,
0x02,0x00,0x08,0x00,0x01,0x00,0x01,0x01,
0x03,0x00,0x00,0x00,0x00,0x00,0x01,0x01,
0x04,0x00,0x00,0x00,0x00,0x00,0x01,0x01,
0x05,0x00,0x00,0x00,0x00,0x00,0x01,0x01,
0x06,0x00,0x10,0x00,0x01,0x00,0x01,0x01,
0xF5,0x8A};
std::array<uint8_t,20> _message_GNSS_7 = {
0x00, // msgVer (0 for this version)
0x00, // numTrkChHw (max number of hardware channels, read only, so it's always 0)
0xff, // numTrkChUse (max number of channels to use, 0xff = max available)
0x02, // numConfigBlocks (number of GNSS systems), most modules support maximum 3 GNSS systems
// GNSS config format: gnssId, resTrkCh, maxTrkCh, reserved1, flags
0x00, 0x08, 0x10, 0x00, 0x01, 0x00, 0x00, 0x01, // GPS
0x01, 0x01, 0x03, 0x00, 0x01, 0x00, 0x00, 0x01 // SBAS
};
std::array<uint8_t,44> _message_GNSS_8 = {
0x00, // msgVer (0 for this version)
0x00, // numTrkChHw (max number of hardware channels, read only, so it's always 0)
0xff, // numTrkChUse (max number of channels to use, 0xff = max available)
0x05, // numConfigBlocks (number of GNSS systems)
// GNSS config format: gnssId, resTrkCh, maxTrkCh, reserved1, flags
0x00, 0x08, 0x10, 0x00, 0x01, 0x00, 0x01, 0x01, // GPS
0x01, 0x01, 0x03, 0x00, 0x01, 0x00, 0x01, 0x01, // SBAS
0x02, 0x04, 0x08, 0x00, 0x01, 0x00, 0x01, 0x01, // Galileo
0x05, 0x00, 0x03, 0x00, 0x01, 0x00, 0x01, 0x01, // QZSS
0x06, 0x08, 0x0E, 0x00, 0x01, 0x00, 0x01, 0x01 // GLONASS
};
std::array<uint8_t,20> _message_NMEA = {
0x00, // filter flags
0x41, // NMEA Version
0x00, // Max number of SVs to report per TaklerId
0x02, // flags
0x00, 0x00, 0x00, 0x00, // gnssToFilter
0x00, // svNumbering
0x00, // mainTalkerId
0x00, // gsvTalkerId
0x01, // Message version
0x00, 0x00, // bdsTalkerId 2 chars 0=default
0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // Reserved
};
std::array<uint8_t,28> _message_GNSS = {
0x00, // msgVer (0 for this version)
0x00, // numTrkChHw (max number of hardware channels, read only, so it's always 0)
0xff, // numTrkChUse (max number of channels to use, 0xff = max available)
0x03, // numConfigBlocks (number of GNSS systems), most modules support maximum 3 GNSS systems
// GNSS config format: gnssId, resTrkCh, maxTrkCh, reserved1, flags
0x00, 0x08, 0x10, 0x00, 0x01, 0x00, 0x01, 0x01, // GPS
0x01, 0x01, 0x03, 0x00, 0x01, 0x00, 0x01, 0x01, // SBAS
0x06, 0x08, 0x0e, 0x00, 0x01, 0x00, 0x01, 0x01 // GLONASS
};
std::array<const uint8_t,8> _message_JAM = {
// bbThreshold (Broadband jamming detection threshold) is set to 0x3F (63 in decimal)
// cwThreshold (CW jamming detection threshold) is set to 0x10 (16 in decimal)
// algorithmBits (Reserved algorithm settings) is set to 0x16B156 as recommended
// enable (Enable interference detection) is set to 1 (enabled)
0x3F, 0x10, 0xB1, 0x56, // config: Interference config word
// generalBits (General settings) is set to 0x31E as recommended
// antSetting (Antenna setting, 0=unknown, 1=passive, 2=active) is set to 0 (unknown)
// ToDo: Set to 1 (passive) or 2 (active) if known, for example from UBX-MON-HW, or from board info
// enable2 (Set to 1 to scan auxiliary bands, u-blox 8 / u-blox M8 only, otherwise ignored) is set to 1
// (enabled)
0x1E, 0x03, 0x00, 0x01 // config2: Extra settings for jamming/interference monitor
};
std::array<const uint8_t,40> _message_NAVX5 = {
0x00, 0x00, // msgVer (0 for this version)
// minMax flag = 1: apply min/max SVs settings
// minCno flag = 1: apply minimum C/N0 setting
// initial3dfix flag = 0: apply initial 3D fix settings
// aop flag = 1: apply aopCfg (useAOP flag) settings (AssistNow Autonomous)
0x1B, 0x00, // mask1 (First parameters bitmask)
// adr flag = 0: apply ADR sensor fusion on/off setting (useAdr flag)
// If firmware is not ADR/UDR, enabling this flag will fail configuration
// ToDo: check this with UBX-MON-VER
0x00, 0x00, 0x00, 0x00, // mask2 (Second parameters bitmask)
0x00, 0x00, // Reserved
0x03, // minSVs (Minimum number of satellites for navigation) = 3
0x10, // maxSVs (Maximum number of satellites for navigation) = 16
0x06, // minCNO (Minimum satellite signal level for navigation) = 6 dBHz
0x00, // Reserved
0x00, // iniFix3D (Initial fix must be 3D) = 0 (disabled)
0x00, 0x00, // Reserved
0x00, // ackAiding (Issue acknowledgements for assistance message input) = 0 (disabled)
0x00, 0x00, // Reserved
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Reserved
0x00, // Reserved
0x01, // aopCfg (AssistNow Autonomous configuration) = 1 (enabled)
0x00, 0x00, // Reserved
0x00, 0x00, // Reserved
0x00, 0x00, 0x00, 0x00, // Reserved
0x00, 0x00, 0x00, // Reserved
0x01, // useAdr (Enable/disable ADR sensor fusion) = 1 (enabled)
};
std::array<const uint8_t,6> _message_1HZ = {
0xE8, 0x03, // Measurement Rate (1000ms for 1Hz)
0x01, 0x00, // Navigation rate, always 1 in GPS mode
0x01, 0x00, // Time reference
};
std::array<const uint8_t,8> _message_GGL = {
0xF0, 0x01, // NMEA ID for GLL
0x01, // I/O Target 0=I/O, 1=UART1, 2=UART2, 3=USB, 4=SPI
0x00, // Disable
0x01, 0x01, 0x01, 0x01 // Reserved
};
std::array<const uint8_t,8> _message_GSA = {
0xF0, 0x02, // NMEA ID for GSA
0x01, // I/O Target 0=I/O, 1=UART1, 2=UART2, 3=USB, 4=SPI
0x01, // Enable
0x01, 0x01, 0x01, 0x01 // Reserved
};
std::array<const uint8_t,8> _message_GSV = {
0xF0, 0x03, // NMEA ID for GSV
0x01, // I/O Target 0=I/O, 1=UART1, 2=UART2, 3=USB, 4=SPI
0x00, // Disable
0x01, 0x01, 0x01, 0x01 // Reserved
};
std::array<const uint8_t,8> _message_VTG = {
0xF0, 0x05, // NMEA ID for VTG
0x01, // I/O Target 0=I/O, 1=UART1, 2=UART2, 3=USB, 4=SPI
0x00, // Disable
0x01, 0x01, 0x01, 0x01 // Reserved
};
std::array<const uint8_t,8> _message_RMC = {
0xF0, 0x04, // NMEA ID for RMC
0x01, // I/O Target 0=I/O, 1=UART1, 2=UART2, 3=USB, 4=SPI
0x01, // Enable
0x01, 0x01, 0x01, 0x01 // Reserved
};
std::array<const uint8_t,8> _message_GGA = {
0xF0, 0x00, // NMEA ID for GGA
0x01, // I/O Target 0=I/O, 1=UART1, 2=UART2, 3=USB, 4=SPI
0x01, // Enable
0x01, 0x01, 0x01, 0x01 // Reserved
};
};
#endif //CLUSTERDUCK_PROTOCOL_DUCKGPS_H
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment