Enabling $GPZDA NMEA sentence on Adafruit Ultimate GPS

I’ve been using an Adafruit Ultimate GPS module as a time source which with their standard GPS Library provides the $GPRMC NMEA 0183 sentence containing the time and date however I had a preference for the $GPZDA sentence for the simple fact it provides the year as four digits instead of only two.

This forum post suggested it was possible on the MTK3339 chip by sending an unsupported variant of the command that enables the various sentences:


However, this didn’t work. The datasheet provided by Adafruit doesn’t document the majority of the data fields in this command, just identifying them as reserved. I found another datasheet elsewhere which I think is just an older revision of the same document and this has slightly better information. In fact, the command was only slightly wrong, it’s in fact this:


It’s the 18th field, not the 17th as the forum post originally suggested. Sending this command to the GPS module now returns the $GPZDA NMEA sentence. It can be enabled with other sentences, you just need to update the XOR checksum after the * using something like this online tool.

Tags: , , , ,

4 Responses to “Enabling $GPZDA NMEA sentence on Adafruit Ultimate GPS”

  1. gmg says:

    Exactly what I was looking for with DS3231 and Adafruit GPS but can’t get it working. I’ve debugged char c and see a solid stream of $GPZDA (or, alternately, $GPRMC) messages that pass checksum but
    “if (strstr(nmea, “$GPZDA”)) { ” is never true, and it goes no further. I’m stumped.

    • Matt says:

      Do you have your code available?

      • gmg says:

        // http://bodgitandscarper.co.uk/category/arduino/ bootstraps DS3231 from GPS
        I’ve uploaded the sketch I wrote to GitHub: https://github.com/bodgit/ds3231-from-gps
        The sketch uses the undocumented $GPZDA NMEA sentence to retrieve the time from the GPS module and
        reprograms the DS3231 on receipt of every new sentence so you just need to run the sketch long
        enough to get the message that the clock is set. Assuming your battery is good, the clock will
        keep ticking and maintain time accurately after you pull the power.

        // #include
        //SDA → Analog 4 on Arduino DS1307 or DS3231 RTC
        //SCL → Analog 5 on Arduino
        SoftwareSerial mySerial(3, 2);
        Adafruit_GPS GPS(&mySerial);
        #define GPSECHO true

        uint8_t day_of_week(uint16_t y, uint8_t m, uint8_t d)
        static int t[] = { 0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4 };
        y -= m < 3;
        return (y + y/4 – y/100 + y/400 + t[m-1] + d) % 7;

        uint8_t dec_to_bcd(int val)
        return (uint8_t) ((val / 10 * 16) + (val % 10));

        void setup()
        Serial.println("DS3231 time setting from GPS");

        // GPS.sendCommand(PMTK_SET_NMEA_OUTPUT_RMCONLY);
        GPS.sendCommand(PMTK_SET_NMEA_UPDATE_1HZ); // 1 Hz update rate

        OCR0A = 0xAF;
        TIMSK0 |= _BV(OCIE0A);

        char c = GPS.read();
        #ifdef UDR0
        if (GPSECHO)
        if (c) UDR0 = c;
        // writing direct to UDR0 is much much faster than Serial.print
        // but only one character can be written at a time.

        void loop()
        char *nmea;
        uint8_t hour, minutes, seconds, month, day, dow;
        uint16_t year;

        if (!GPS.newNMEAreceived())

        nmea = GPS.lastNMEA();

        if (nmea[strlen(nmea)-4] == '*') {
        uint16_t sum = GPS.parseHex(nmea[strlen(nmea)-3]) * 16;
        sum += GPS.parseHex(nmea[strlen(nmea)-2]);

        for (uint8_t i = 1; i 2000) ? 0x80 : 0x00));
        Wire.write(dec_to_bcd(year % 100));

        Serial.println(“DS3231 programmed with current time”);

        • Matt says:

          The site will defang anything that looks like an HTML tag so anything between ‘< ' and '>‘ is removed hence half that code is missing. Assuming the missing code is the same as in my original sketch I would try printing the nmea variable to the serial port after the checksum block and see what it’s set to.

Leave a Reply