Round watch face on an Arduino Uno
An analog watch face on a GC9A01 round display: hour, minute and second hands from a DS3231 clock, redrawn each second. The whole build, an Arduino Uno and 2 more parts, runs here in your browser on the firmware below; open it in the editor to change the wiring or the code and run it again.
IntermediateRuns in your browser. Free, and no account needed.
The code
The firmware exactly as the editor opens it. Change a line there and press Run: it compiles in the browser.
// Round watch face: an analog clock on a 1.28 inch GC9A01 round display,
// with the time from a DS3231, the way a desk watch or a smartwatch face is
// built.
//
// GC9A01 SCL -> 13 SDA -> 11 CS -> 10 DC -> 9 VCC -> 5 V GND -> GND (RES left free)
// DS3231 SDA -> A4 SCL -> A5 VCC -> 5 V GND -> GND
//
// The display is a 240x240 square of memory with round glass on it, so the
// face is centered at (120, 120) with a radius of 120. Each second the sketch
// rubs out the hands where they were, by drawing them again in black, and
// draws them where they are now: redrawing the whole face would take this
// board a long time over its SPI bus.
//
// The hands are straight lines from the center, and their ends come from a
// table of sines in steps of 6 degrees (one minute of the dial), because this
// board has no floating point. The time is the DS3231's own registers, read
// over Wire: three bytes of BCD from register 0.
#include <SPI.h>
#include <Wire.h>
#include "Adafruit_GFX.h"
#include "Adafruit_GC9A01A.h"
Adafruit_GC9A01A tft(10, 9); // CS, DC; no reset pin
const uint8_t DS3231 = 0x68;
uint8_t fromBcd(uint8_t b) { return (b >> 4) * 10 + (b & 0x0f); }
// Seconds, minutes and hours: the DS3231's first three registers, in BCD.
// False when the clock does not answer.
uint8_t now[3];
bool readClock() {
Wire.beginTransmission(DS3231);
Wire.write(0x00);
Wire.endTransmission();
if (Wire.requestFrom(DS3231, 3) != 3) return false;
for (uint8_t i = 0; i < 3; i++) now[i] = fromBcd(Wire.read() & 0x7f);
return true;
}
const int16_t CX = 120;
const int16_t CY = 120;
const uint16_t FACE = GC9A01A_BLACK;
// sin(6 degrees x i) x 127 for i = 0 to 15, a quarter of the dial.
const int8_t SINE[16] = {0, 13, 26, 39, 52, 64, 75, 85, 94, 103, 110, 116, 121, 124, 126, 127};
int16_t sine60(uint8_t i) {
i %= 60;
if (i <= 15) return SINE[i];
if (i <= 30) return SINE[30 - i];
if (i <= 45) return -SINE[i - 30];
return -SINE[60 - i];
}
// The point `radius` pixels from the center at dial position `i` (0 is 12 o'clock).
int16_t dialX(uint8_t i, int16_t radius) { return CX + radius * sine60(i) / 127; }
int16_t dialY(uint8_t i, int16_t radius) { return CY - radius * sine60(i + 15) / 127; }
void hand(uint8_t i, int16_t radius, uint16_t color) {
tft.drawLine(CX, CY, dialX(i, radius), dialY(i, radius), color);
}
void drawFace() {
tft.fillScreen(FACE);
// A tick a minute, longer every five.
for (uint8_t i = 0; i < 60; i++) {
int16_t inner = (i % 5 == 0) ? 100 : 108;
tft.drawLine(dialX(i, inner), dialY(i, inner), dialX(i, 112), dialY(i, 112), GC9A01A_WHITE);
}
}
uint8_t lastSecond = 255;
uint8_t hourPos, minutePos, secondPos;
void setup() {
tft.begin();
drawFace();
Wire.begin();
}
void loop() {
if (!readClock() || now[0] == lastSecond) {
delay(20);
return;
}
bool first = lastSecond == 255;
lastSecond = now[0];
// Rub out the old hands, then draw the new ones, longest last.
if (!first) {
hand(secondPos, 95, FACE);
hand(minutePos, 85, FACE);
hand(hourPos, 55, FACE);
}
hourPos = (now[2] % 12) * 5 + now[1] / 12;
minutePos = now[1];
secondPos = now[0];
hand(hourPos, 55, GC9A01A_WHITE);
hand(minutePos, 85, GC9A01A_WHITE);
hand(secondPos, 95, GC9A01A_RED);
}
Parts list
4 parts, plus the jumper wires. Every one is in the editor's parts bin.
How it is wired
8 connections, pin by pin, read from the circuit itself. Each line is one set of pins joined together, by a jumper wire or a breadboard strip.
- Arduino Uno R3 pin 13; Round display, GC9A01 pin SCL
- Arduino Uno R3 pin 11; Round display, GC9A01 pin SDA
- Arduino Uno R3 pin 10; Round display, GC9A01 pin CS
- Arduino Uno R3 pin 9; Round display, GC9A01 pin DC
- Arduino Uno R3 pin 5V; Round display, GC9A01 pin VCC; Real-time clock, DS3231 pin VCC
- Ground: Arduino Uno R3 pin GND; Round display, GC9A01 pin GND; Real-time clock, DS3231 pin GND
- Arduino Uno R3 pin A4; Real-time clock, DS3231 pin SDA
- Arduino Uno R3 pin A5; Real-time clock, DS3231 pin SCL
Change it and keep it
Open it in the editor, change the circuit or the code, and keep your version in a free account.