Tuesday, September 30, 2025

Another Arduino GPS Clock

After finishing my first Arduino-based GPS Clock just a few weeks ago (see here: Arduino GPS Clock), I immediately began planning my next one. 

I had purchased from Amazon some large, Red, 7-segment, common-anode displays, and I thought they could make a nice large clock.

Note:  Although the Amazon ad states that the character height is 1.5 inches (see first bullet point), character height is actually about 1.8 inches, as you can see per the display's data sheet, below: 

For a large display I needed a large box.  Searching through some old miscellaneous cases, I came across this HP product (empty of electronics).  Looked perfect!

But how to drive the four seven-segment digits?

For my first clock I used a display that utilizes a TM1637 7-segment driver for four-digit, seven-segment displays.  Why not use the same chip for the new clock?

I had purchased a pack of 5 displays (see ad, below) to experiment with colors.  

Because this new clock was going to use four red digits, I decided to sacrifice the red version of the five displays and unstuff from its PCB the four-digit red-led module.  In its place I'd insert wire-wrap pins, to which I would wire-wrap the appropriate TM1637 signals to each of the new sockets for the four new, larger, digits.

Below is the schematic showing how I wired the TM1637 display board (after removing its original display) to the four new LED digits.  

Note that four of the TM1637 board's pins will drive the four different anodes (one anode for each digit).  These pins are identified with the labels '1' to '4' (you need to remove the installed 4-digit display to see these annotations on the PCB, as well as to see the annotations for segments a - g and the Decimal Point).   Digit 1 represents the Most Significant time digit, while Digit 4 represents the Least Significant time digit.

I built the new front panel on a piece of vector board.  Below you can see the back of the board.  The TM1637 board (minus its original display) is mounted on the right, and I've inserted wire-wrap pins through the original display's pin-holes on the TM1637 board.  You can see the labels I added to identify which signal each pin represents.  You can also see labels on the two rows of pins for the new Digit 1, identifying its pins, per the data sheet above.

Here's the front side of the front-panel board, showing the "socket" side of the wire-wrap pins used for wiring up the digits.

With wire-wrapping finished, the two images below show the board mounted in the case's front bezel.  First, the back...

...and here's the front view:


For the clock electronics, I decided against building and wiring the circuit as I usually do, but instead to layout a PCB, using KiCAD, that would make assembly much more straightforward.  After I completed layout and routing, I shipped the resulting Gerbers to JLCPCB in China and ordered 5 boards be fabricated.  (My first time doing this -- it was very exciting when the package arrived from overseas!).

Below is one of the five boards, stuffed.  Note that this isn't the board I used in this clock -- the board below is stuffed with a NEO6M GPS receiver module, and I'm using a GT-U7 GPS receiver module for this clock.


I ordered my GT-U7 GPS receiver modules from Amazon:


The circuitry for this PCB is a bit different from that in my original clock.  Here's the original clock's schematic:


And here is the new schematic for the clock PCB:


Notes:
  • There are footprints (overlapping) for both the NEO6M and the GT-U7 GPS receiver modules.
  • J14 let's me attach a SparkFun CH340 USB Serial module to talk with the GPS receiver module, should the module, itself, not have a USB interface (e.g. the NEO6M module), and if I don't have appropriate software written for the Nano.  Very useful for debugging.
  • In addition to J1 and J12 (for attaching either the NEO6M or the GT-U7 module), connector J2 allows modules with other pinouts to be attached (just insert the module into either J1 or J12 and wire J2 to route the signals accordingly).
  • J5 and J11 bring two unused Nano I/O pins to connectors, just in case there's a future need (I wish I'd thought to bring the Nano's two I2C pins to connectors, too -- oh well!)
  • JP1 and JP2 allow selection of the GPS receiver's VCC voltage (5V or 3.3V), as well as its I/O voltage (again, 5V or 3.3V).
  • And finally, J6 let's me solder a small loop of wire to the board's ground for attaching scope-probe grounds, jumpers, etc. during the debug process.


Below is a picture of the inside of the case.  Lots of space!  Note that the antenna is mounted internally --- the case is plastic, so there should be little attenuation of the GPS satellite signals.

If you look closely, you an see the GT-U7 GPS receiver module...


(The GPS antenna is stuck to the small piece of copper-clad PCB material with a piece of double-sided adhesive foam tape).

Here's the back of the case.  The two menu buttons are on the left, and a USB connector is on the right.  (The pre-existing AC connector may someday be used if I decide to incorporate a 5V supply within the case).


Below is the front of the case.  I purchased a rectangle of transparent colored acrylic plexiglass from the local Tap Plastics in San Jose (size 8 1/8" by 3 1/4", color "Dark Smoke" (transmissivity 36%)), and drilled the 6 holes it required back at my house.  Note that the ambient-light detecting photoresistor pokes through the plastic on the left-hand side, and the 3-position single-pole toggle switch for selecting GMT, Daylight-Savings-Time, or Standard Time, is mounted to the plastic on the right-hand side. Four black screws mount it to the front panel

Here is the front panel, before and after the installation of the "Dark Smoke" plexiglass:



I think it gives the clock a bit of a classy look!



ChatGPT to the Rescue:

With my original clock, I had configured the NEO6M receiver's baud rate to 38.6 kbaud and stored this value in the receiver itself so that it would be pre-configured at this rate upon power up.

But, although I tried to do the same configuration-and-store procedure with the GT-U7 receiver, it wouldn't hold the configuration after I powered the unit down.

What to do?  

I decided to have the Nano configure the receiver's baud rate at power up.  I did some Google research and found this site:  Configuring Ublox GPS receivers, which had some useful information and additional links, but the code I wrote (using UBX messages) did not work entirely as planned, and so I thought I'd let ChatGPT have a run at it.  Below is my prompt and ChatGPT's reply...


And here's ChatGPT's code, after a couple of tweaking prompts:


// Arduino NANO code to find and set a GPS Receiver's baudrate,
// assuming that the NANO is using a Software UART.
// --Code created by ChatGPT, prompted by K6JCA
//
// Features:
// 1.  Tries each possible baud, looking for a line starting with $GP… (NMEA).
// 2.  Once found, compares to desiredBaud (e.g. 9600).
// 3.  If not equal, sends the UBX-CFG-PRT message (at the old baud).
// 4.  Waits, then switches Nano’s software UART to the desiredBaud.
// 5.  Verifies baud rate change by checking again for $GP…
// 6.  Retries automatically (up to three times) if the check fails.
// 7.  Finally, passes NMEA to USB serial for debugging.

#include <SoftwareSerial.h>

#define GPS_RX 6   // Nano receives GPS data on pin 6
#define GPS_TX 11  // Nano transmits GPS data on pin 11

SoftwareSerial gpsSerial(GPS_RX, GPS_TX);

// Possible GPS baud rates (try these for detection)
const long supportedBauds[] = {4800, 9600, 19200, 38400};
const int numBauds = sizeof(supportedBauds) / sizeof(supportedBauds[0]);

// User-chosen Nano/GPS baud
const long desiredBaud = 9600;  // (change to what you want)

// -----------------------------------------------------------------
// Build and send UBX-CFG-PRT message to set GPS baud dynamically
// -----------------------------------------------------------------
void sendUBXsetBaud(long baud) {
  uint8_t msg[] = {
    0xB5, 0x62,             // sync
    0x06, 0x00,             // CFG-PRT
    0x14, 0x00,             // payload length = 20

    // payload (20 bytes)
    0x01, 0x00, 0x00, 0x00, // portID=1, reserved, txReady=0
    0xD0, 0x08, 0x00, 0x00, // mode = 0x08D0 (8N1), little endian
    0x00, 0x00, 0x00, 0x00, // baud placeholder
    0x07, 0x00,             // inProtoMask: UBX+NMEA
    0x03, 0x00,             // outProtoMask: UBX+NMEA
    0x00, 0x00,             // flags
    0x00, 0x00              // reserved
  };

  // Fill baud (little-endian) at payload[8..11]
  msg[14] = (uint8_t)(baud & 0xFF);
  msg[15] = (uint8_t)((baud >> 8) & 0xFF);
  msg[16] = (uint8_t)((baud >> 16) & 0xFF);
  msg[17] = (uint8_t)((baud >> 24) & 0xFF);

  // Fletcher checksum
  uint8_t ckA = 0, ckB = 0;
  for (int i = 2; i < sizeof(msg); i++) {
    ckA += msg[i];
    ckB += ckA;
  }

  // Send message
  for (int i = 0; i < sizeof(msg); i++) gpsSerial.write(msg[i]);
  gpsSerial.write(ckA);
  gpsSerial.write(ckB);
}

// -----------------------------------------------------------------
// Try a baud rate, return true if NMEA sentences detected
// -----------------------------------------------------------------
bool detectBaud(long baud) {
  gpsSerial.begin(baud);
  unsigned long start = millis();
  String sentence = "";
  while (millis() - start < 1000) { // 1 second timeout
    while (gpsSerial.available()) {
      char c = gpsSerial.read();
      if (c == '\n') {
        if (sentence.startsWith("$GP") || sentence.startsWith("$GN")) {
          return true; // NMEA detected
        }
        sentence = "";
      } else {
        sentence += c;
      }
    }
  }
  return false;
}

// -----------------------------------------------------------------
void setup() {
  Serial.begin(115200);
  delay(2000);
  Serial.println(F("GPS Baud Auto-Detect & Configure"));

  long currentBaud = -1;

  // Detect current GPS baud
  for (int i = 0; i < numBauds; i++) {
    if (detectBaud(supportedBauds[i])) {
      currentBaud = supportedBauds[i];
      Serial.print(F("Detected GPS baud: "));
      Serial.println(currentBaud);
      break;
    }
  }

  if (currentBaud == -1) {
    Serial.println(F("No GPS detected at supported bauds!"));
    return;
  }

  // If GPS baud not what we want → change it
  if (currentBaud != desiredBaud) {
    Serial.print(F("Attempting to change GPS baud to "));
    Serial.println(desiredBaud);

    bool success = false;
    for (int attempt = 1; attempt <= 3; attempt++) {
      Serial.print(F("  Attempt "));
      Serial.println(attempt);

      gpsSerial.begin(currentBaud); // talk at old baud
      sendUBXsetBaud(desiredBaud);

      delay(500); // let GPS switch
      gpsSerial.begin(desiredBaud);

      if (detectBaud(desiredBaud)) {
        Serial.println(F("  ✔ Baud change verified OK!"));
        success = true;
        break;
      } else {
        Serial.println(F("  ✘ Verification failed"));
      }
    }

    if (!success) {
      Serial.println(F("❌ Could not change GPS baud after 3 tries."));
    }
  } else {
    Serial.println(F("GPS already at desired baud."));
  }
}

void loop() {
  // Pass GPS data to USB Serial for monitoring
  while (gpsSerial.available()) {
    Serial.write(gpsSerial.read());
  }
}

I incorporated this code, where appropriate, into my existing sketch, along with some modifications, such as displaying the status of this baud-rate process on the TM1637 display.

(Note: at some point I'll try to get my updated code to my GitHub site -- it'll be Rev 5 (or higher)).

Finally, here's a shot of the clock in my ham shack -- it's on the right, on top of my homebrew Automatic Antenna Tuner.  Note my homebrew HF Transceiver on the left, and homebrew 500W PA in the middle).


And finally...


Standard Caveat:

As always, I might have made a mistake in my equations, assumptions, drawings, or interpretations.  If you see anything you believe to be in error or if anything is confusing, please feel free to contact me or comment below.

And so I should add -- this information is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

Sunday, September 14, 2025

Arduino GPS Clock

This project started on a whim -- years ago I'd done some work with GPS receivers and decoding their NMEA-0183 serial data stream, and recently I thought it might be fun to make a very accurate clock using the "Time" field contained within the NMEA-0183 message stream.

The GPS receivers I've used in the past sent a packet of NEMA-0183 messages from their serial port once a second.  These messages contain information such as data and time, longitude and latitude of the receiver, and the satellites that it is receiving.  

The GPS time field contains a very accurate representation of time, formatted as HHMMSS.xxx, where HH is hours, MM is minutes, SS is seconds, and xxx represents fractions of a second.  Hours is referenced to GMT.

I only wanted to display hours and minutes -- displaying ever-changing seconds, too, would make the display too "busy", in my opinion.  So although I receive a new time from the GPS satellite network every second, the display only changes on a minute-by-minute basis.

On the front panel, in addition the four digit time display, I've mounted a photoresistor for measuring the ambient light level at the clock's front panel.  This measurement allows the display brightness to change with a room's ambient light level, i.e. minimum brightness for a dark room to maximum brightness for a very bright room.  There are four brightness steps.

I've also included a software menu system that lets me, via the front panel's four digit numerical display and two switches on the receivers back panel, select and change the following clock parameters:

  • Standard-Time offset, in hours, from UTC (e.g. set to -8 hours in California).
  • Select 12 or 24 hour format.
  • Select the Display Brightness Level (1 of 4 steps).
  • Enable Display Brightness to change with ambient light level (or remain fixed).
  • Enable the display's colon (":") between the Hour and Minute fields to "blink" (on for a second, off for a second), or remain ON constantly.
  • View the number of satellites the GPS receiver is tracking (for debugging).
  • View the analog value representing the ambient light level (for debugging).

In addition, there is a third 3-position toggle switch on the back panel that lets me select between Standard Time, Daylight Savings Time, or GMT.  Although these could have been menu items, I preferred the convenience of using a switch to select between them.

The clock is powered by +5 VDC via the Arduino Nano's mini-USB port.


Project Description:

Hardware:

GPS satellite data is received via a u-blox GY-NEO6M Rev2 GPS module using a NEO6M GPS receiver (purchased from Amazon -- interestingly, the seller disappeared several weeks later!).

This module sends a serial data-stream once a second to an Arduino Nano processor, which decodes the data-stream, strips out the time field, and displays it on a 4-digit seven-segment TM1637 numerical display.

Although the Nano receives serial data from the NEO6M receiver, the Nano does not transmit data back to the NEO6M (there really is no need to do this during normal operation of the clock).  

However, for changing the NEO6M receiver's parameters, such as its baud rate, I've included a 5-pin header onto which a CH340 USB adapter can be temporarily mounted (for this application, I use a SparkFun Serial Basic Breakout CH340G board):


The Nano's and the GPS receiver's Serial Ports operate at different logic levels -- the Nano's is 5V logic while the Receiver's is 3.3V logic.  Given the voltage drop introduced by the Nano's 5V regulator, etc., the signaling-voltage margins can be on the edge of acceptability. I did not feel comfortable with the narrowness of these margins, and so I decided it better to include simple 3-component bi-directional level shifters between the Nano's 5V I/O and the GPS Receiver's 3.3V I/O.

Such level shifters consist of two resistors and a single MOSFET transistor (e.g. BSS138) per channel: 

They can be purchased, pre-assembled on PCBs, from Amazon:

There is a photoresistor, whose resistance changes with the level of ambient light, mounted on the front panel.  The Nano reads an analog value representing a voltage generated with this photo resistor, and, using this, it can adjust the display's brightness accordingly.

I wasn't sure what value of resistance for normal ambient light I'd need, so I purchased a selection of 5 different types of photoresistors to allow for experimentation:


I chose to use the "5506" (i.e. GL5506) photoresistor because its resistance, of the five choices, seemed the least likely to be loaded-down by the inherent resistance of the Nano's Analog port.

The display is a TM1637 Module, which uses a 2-wire Clock/Data interface (there is an Arduino library of TM1637 functions).  Unsure of what color to use, I purchased a pack of five modules, each with a different color:


In summary -- a very basic design consisting of three HW modules mounted on a thru-hole board (Nano, Level-Shifter, and GPS Receiver), and a separate Display module.

I brought the antenna connection out to the clock's back panel (via a uFL-male to SMA-female cable), rather than mounting a small antenna module within the clock's case -- the inside of the this case (scavenged from a product) had been sprayed with a conductive paint (I'm sure for EMC reasons), and I doubted an antenna mounted inside the resulting Faraday cage would have worked very well.

Below is the schematic of the clock.

Notes:

1.  There are two jumpers on the board, JP1 and JP2.  For normal operation, these should be installed on header J1 per the schematic.

  • JP1 connects the NEO6M's serial output pin to the Nano's serial input.  
  • JP2 connects the board's +5VDC to the NEO6M receiver
2.  To test a different receiver module with the board, remove Jumper's JP1 and JP2, then connect the new Receiver's module to the board as follows:
  • New Receiver's GND to J1.1.
  • New Receiver's TXD (serial data out, 3.3V level) to J1.3.
  • New Receiver's VCC (+5VDC) to J1.5.
With these connections made, the new GPS receiver should communicate with the Nano (be sure to ensure that the Nano's and the Receiver's baud rates are identical).


Software:

There are two timing loops in the software.  

The first loop is a 20 msec loop, and its primary task is switch debouncing and performing the resulting required actions.  

Note that if a clock parameter (e.g. 12/24 hour display formatting, GPS baud rate, etc.) is changed during this 20 msec loop, the Nano's EEPROM is updated with the new value.  These EEPROM values are used for system initialization upon power-up.

The second loop is a 1 second loop, and its tasks are:

  • Receive the NMEA-0183 message stream, strip off the time field, and write the time to the front panel display.
  • If displaying a menu item in lieu of time, update the menu's display with any changes to a menu's selection that occurred during the 20 msec loop.
  • Blink (if enabled) the display's "colon" between the hour and minutes fields.
  • Read ambient light level via the Nano's analog input A0.

Note that this code only  receives data from the GPS receiver, it does not transmit data back to it.  Therefore, to change any of the receiver's parameters, such as its baud rate (from its default of 9600 baud to, say, 38,400 baud), this change must be done using an external CH340 USB-to-Serial adapter and external software such as u-block's u-center (a very useful tool, by the way, for verifying if the receiver is working, if you are unsure of your Arduino code).

There are 9 possible menu items that can be displayed via the 4-digit front panel display.  These menu items can be stepped through using the momentary-contact MENU toggle switch.  Within each menu item there are usually a number of  choices available (such as different baud rates in the baud rate menu).  These choices can be stepped through using the NEXT pushbutton. 

The MENU items, and the range of possible choices for each MENU item, are shown, below:

  • Menu Item 1:
    • Displays current Time.
    • The NEXT button does not function for this menu item.
    • Display:

  • Menu Item 2:
    • Allows setting of the local time zone's offset, in hours, from GMT.  Note that the offset is per Standard Time, not Daylight Savings Time.  E.g. it would be -8 for California, irrespective of the month.
    • The NEXT button steps from 0 to -23, then wraps back around to 0.
    • Display

  • Menu Item 3:
    • Allows setting of the "Brightness" of the 4-digit LED display.
    • The NEXT button steps through the 4 possible brightness values, from 0 to 3, and then wraps back around to 0.
    • Display:

  • Menu Item 4:
    • Selects displaying time in a 12 or a 24 hour format.  "12" will be displayed if the 12-hour format is selected, and "24" will be displayed if the 24-hour format is selected.
    • The NEXT button toggles between the 12 and 24 hour selections.
    • Display:

  • Menu Item 5:
    • Determines if the colon dividing the 2-digit hours field from the 2-digit minutes field will blink (1 second ON followed by 1 second OFF), or if it is ON continuously.
    • The NEXT button toggles between the BLINK or ON states.
    • Display:

  • Menu Item 6:
    • Selects of one of five possible baud rates to use for the Nano's Software UART that communicates with the GPS Receiver.
    • The NEXT button steps through the following baud rates, and then wraps around to the start:
      • 9,600
      • 19,200
      • 38,400
      • 57,600
      • 115,200
    • Display:

  • Menu Item 7:
    • Displays the number of satellites tracked by the GPS receiver (used for debugging).
    • The NEXT button does not function for this menu item.
    • Display:

  • Menu Item 8:
    • Enables the display brightness to change as a function of the ambient light level, or keeps the display brightness fixed at the level set in Menu item 3, irrespective of the ambient light level.
    • The NEXT button toggles between enabling the display brightness to track ambient light levels, or the display brightness remaining fixed.
    • Display:

  • Menu Item 9:
    • Displays the Nano's ADC input value from the Ambient Light detector (useful for debugging).  Values range from 0 (dark) to 1023 (max light).
    • The NEXT button does not function for this menu item.
    • Display:


The MENU switch steps through the items from 1 to 9, and then will cycle back to 1. 

Similarly, the NEXT button steps through a menu item's possible values from first to last, and then cycles back to the first. 

Note that my GPS Clock Arduino code can be found here, on GitHub:  Arduino Clock Code.


The Build:

I used a small plastic box within which I mounted the electronics and the display.  I'd purchased this box (which apparently had been a shipping product) sometime in the dim-past, I'm sure because I thought I might someday be able to use its case for a project.

For my new application of this case, I constructed both the front panel and the back panel from blank double-sided PCB stock.

The front panel simply has the time display and a small photoresistor, the latter is used to detect a room's ambient light level and with it, adjusts the display's brightness accordingly (i.e. max display brightness for a bright room, to minimum display brightness for a dark room).

The rear panel (see image below) has three switches, an SMA connector for the GPS antenna, and a hole to allow a mini-USB connector to be attached to the Nano within the box.

The three switches on the back panel are:

Leftmost switch:  3-position single pole toggle.  This switch selects between the following items:

  • UP:  Display time as Daylight Savings time (add an hour to Standard Time).
  • MID: Display time as Standard Time (no Daylight Savings Offset).
  • DOWN:  Display GMT time with 24-hour format.
Middle switch:  3-position momentary contact single pole toggle which is the MENU switch. This switch performs the following functions:
  • MID (default position): Idle
  • UP or DOWN (momentary contact):  Upon moving the switch to either its up or down position and then releasing it back to its idle (mid) position, the menu will increment to the next menu item on the list of menu items.  At the end of the list it will wrap around to the first menu item, which simply display's time.

Rightmost switch:  Normally Open push-button, called the "NEXT" switch.  Upon the press-and-release of this pushbutton, software will increment to the next value choice for the menu-item that has been selected..  For example, if in the "GPS Baudrate Select" menu and the current entry is 9600, upon the press-and-release of this pushbutton, the baud rate will increment to the next choice, in this case 19,200.  At the last choice the next press-and-release will wrap back around to the first choice.

The electronics are mounted on a small prototyping board:


This board, in turn, is mounted onto another piece of raw double-sided PCB stock that is large enough to let me attach it to the case's four mounting holes).




An overhead view of the final build:


Regarding the front-panel display, I found it unattractive that unilluminated segments of the display's numerals were visible from the front, so I mounted a small piece of "smokey" colored Gel Film between the display and the front panel to better hide any non-illuminated display segments.


Note that if using this type of Gel film, there is a protective layer on each side of the Gel film that will need to be peeled off.


Software Generation:

My wife suggested that I use ChatGPT to create the Arduino's code, so I thought, 'Why not', and gave it a try, wondering what the experience would be like.

I created a free account on the ChatGPT website and decided I would start by prompting ChatGPT to create Arduino Nano code that would read and debounce two buttons (my MENU and NEXT switches).  Without any forehand knowledge on how best to use ChatGPT to write code, I plunged right in.

I quickly discovered, in an attempt to format my request, that pressing "Enter" immediately sends whatever I'd typed to ChatGPT, as you can see in the first line below.  My next attempt you can see right below it -- a long paragraph written "stream-of-consciousness" fashion without formatting for readability.  But it worked!


I continued in the same vein, incrementally adding features and then testing them.  I was amazed at how quickly I was generating code with ChatGPT as a partner (although perhaps I should rephrase that as being amazed at how quickly ChatGPT was generating code with me as a partner).  I figured I'd have the sketch completely finished by the afternoon.

Almost, but not quite...ChatGPT is not without its flaws!

As ChatGPT continued to generate code with my descriptions of new features or requests to move blocks of code around, errors began to creep into already established code.

For example, when stepping through the menus, I usually defined the first two characters to be a shorthand ID for that menu item, e.g. 'br' would display for the "Brightness" menu (see image, below).  I had defined for ChatGPT which specific display segments (e.g. segments E, F G, etc.) of the first two seven-segment numbers, to turn on for each menu item that a shorthand ID .

The resulting code looked great -- and when tested by stepping through the menus, the ID characters for each menu item were properly displayed on the 4-digit seven-segment display.  

Great!  I continued on my way, assuming the code for that task was finished.

Later I went back to test a menu function, and, while stepping through the menus, I discovered the ID characters had become gobbledygook.

Somehow, while I was asking ChatGPT to add other features to the code, ChatGPT had decided to replace the working "letters-defined-with-segments" code to instead define the letters with Hex values.

A perfectly acceptable change to make, except...the Hex values chosen by ChatGPT did not represent those same segments, as I show, below:

Returning the code to the original "define-with-segments" code fixed the problem, but it should never have occurred in the first place.

Another issue that took me a frustrating amount of time to debug had to do with switch-case statements.

In the Menu code, ChatGPT initially defined the menu selection tree using nested if-else statements.  For purposes of readability, I much prefer to use case statements, and I requested that ChatGPT change its code do use them, as you can see by the following conversation.

First, ChatGPT's original code:


Next, my request to use Case statements and ChatGPT's reply...


...and the code it generated:


Looks good and it worked as expected.

Much later, during the verification of some other code that ChatGPT had added, I needed to step through my menus to get to the appropriate item to verify.  But as I stepped through them, I could not step from the Brightness menu to the next menu item.  What was going on?

After scratching my head for awhile, I finally throw up my hands in frustration and instead queried Google's AI as to what the problem might be.  Its reply:


Aha!  I looked at ChatGPT's code again -- it had declared a variable within a Case statement, as I show below.


I moved the declaration to the top of the sketch to make it a global variable, not local, and the problem went away.

This isn't a problem I would have had if I'd written the code myself.  Unless I'm stretched for memory, I usually define most of my variables as globals and place them at the top of my code, and whenever I do define local variables, they are at the start of a function call (i.e. subroutine), rather than in-line further into the body of a function's code.

And because it's something I wouldn't do, myself, I didn't realize that it shouldn't be done.  Thank goodness for Google.

Anyway...by this time I decided that I was spending too much time fixing ChatGPT errors and I'd be better off completing the code myself.  And so I did.

In summary...

I'd estimate that the code generation effort was 90% ChatGPT and 10% me.  With ChatGPT, it took about a full day to create my first fully-featured version of the code (Rev. 1).  I'm sure it would have taken me longer if I had had to write all of the code, myself.

In this respect, ChatGPT is an incredibly useful aid for creating code -- I avoided all the usual typos I would normally generate if I had been typing the code in.

But, surprise surprise, ChatGPT can create errors.  This is especially worrisome to me, if ChatGPT decides, unprompted, to change working code into something that no longer works.  Sounds like a debugging nightmare!

Also -- there's a certain "I know it like the back of my hand" sense that I get from writing my own code.  If something isn't working properly, I know right where to start looking for the cause, because I am intimately familiar with the code and how it operates.

With ChatGPT's code I don't have the same sense of familiarity.  Which means that coding errors, if they exist, could be more difficult to uncover.


Other Issues with the Clock:

Incorrect Time (Infrequent):

With my first version of code, I noticed that sometimes (very infrequently), there'd be an error in the displayed, for example, time appearing as 5:22 when it was actually a different time of day.

Could this be a problem with the Nano's software serial port somehow losing data?

The GPS receiver's baud rate had been set to 9600 baud.  I hypothesized that NMEA 0183 data might be being dropped (corrupted) by the Nano's software serial port because other software tasks might have had temporary priority in lieu of serial port reception and decoding.

So I decided to bump up the baud rate from 9600 to 38,400 baud and see if the errors still occurred.  

I've seen no errors since the modification.  That seems to have fixed the problem.  If it reoccurs, I'll do some more debugging (probably assign an I/O pin or two to be high during certain routines, thus allowing me to measure, with a scope, how much time they take to perform).

Only 4 levels of Display Brightness:

Documentation suggested that the TM1637 displays have 8 steps of brightness (i.e. values 0 to 0x7).  The displays that I purchased only seem to have 4 steps (0 to 0x3), and brightness seems to be unchanged when incrementing the brightness field from 0x3 to 0x7.  So I limited the brightness adjustment range to the four steps from 0 to 3.


That's all!


P.S.  For another of my Arduino GPS Clock designs, go here: Another Arduino GPS Clock.


Standard Caveat:

As always, I might have made a mistake in my equations, assumptions, drawings, or interpretations.  If you see anything you believe to be in error or if anything is confusing, please feel free to contact me or comment below.

And so I should add -- this information is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.