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:
- 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:
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.
No comments:
Post a Comment