Built-in project · Arduino Uno R3

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.

Round watch face · Arduino Uno R3live0.000 s 0.00x
Click to open it in the editor
The circuit itself, running here on the simulator. Press what can be pressed; click anything else to open it in the editor.

The code

The firmware exactly as the editor opens it. Change a line there and press Run: it compiles in the browser.

sketch.ino · Arduino Uno R3
// 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.