Retro Uhr mit ESP32 und rundem TFT Display GC9A01A - AZ-Delivery

Retro-style watch with TFT display and ESP32

This blog post is about the implementation of a retro-style watch, which is based on an ESP32 microcontroller and uses a round TFT display for display. With the possibility of the time over NTP (Network Time Protocol) Synchronizing automatically can be dispensed with.

Hardware

Since we want to represent a retro watch on the display, the round LCD display is predestined for it. To control displays, you need a microcontroller with sufficient RAM, that's for that ESP32 Mini S2 Board with 2MB RAM The ideal solution because it even has WLAN connection.

Required components:
ESP32 microcontroller
TFT display (GC9A01A)

Connect the components as in the following sketch:

RST

GPIO 3

CS

GPIO 5

DC

GPIO 7

Sda

GPIO 9

Scl

GPIO 11

Gnd

Gnd

VCC

3V3

software

If you are programming with an ESP32 for the first time, copy the following link to Arduino IDE under Fille-> Preferences -> Additional Boards Manager URLS: https://dl.espressif.com/dl/package_esp32_index.json
And install the ESP32 package in the board management.

To flash the board, select the "ESP32S2 DEV Module". Hold down the "0" button and press the reset button once. This makes the board in boot mode and a COM port should be visible.

Required library: TFT-ESPI

The library is installed as usual in the Arduino IDE at: Sketch -> Include Library -> Add .zip library

If the library is installed, only the User_setup File for the display driver in the Library directory (SketchBook Directory/Libraries/TFT_ESPI/User_setup.H) can be adjusted:
First copy the existing one User_setup.h, delete the content and copy the following lines into the file:

#define User_setup_info "User_Setup"
 
#define Gc9a01_driver
 
#define Tft_width  240
#define Tft_height 240
 
#define Tft_mosi 9
#define Tft_sclk 11
#define Tft_cs   5
#define Tft_dc   7
#define Tft_rst  3
 
#define Load_glcd   // Font 1. Original Adafruit 8 Pixel Font Needs ~ 1820 Bytes in Flash
#define Load_font2  // Font 2. Small 16 pixel high font, Needs ~ 3534 bytes in Flash, 96 Characters
#define Load_font4  // Font 4. Medium 26 Pixel High Font, Needs ~ 5848 Bytes in Flash, 96 Characters
#define Load_font6  // Font 6. Large 48 Pixel Font, Needs ~ 2666 bytes in Flash, Only Characters 1234567890:-. APM
#define Load_font7  // Font 7.7 segment 48 Pixel Font, Needs ~ 2438 bytes in Flash, Only Characters 1234567890:-.
#define Load_font8  // Font 8. Large 75 Pixel Font Needs ~ 3256 Bytes in Flash, only 1234567890:-.
//#define load_font8n // font 8. Alternative to font 8 above, Slightly Narrower, so 3 Digits Fit A 160 Pixel TFT
#define Load_gfxff  // Freefonts. Include Access to the 48 Adafruit_GFX Free Fonts FF1 to FF48 and Custom Fonts
 #define Smooth_font
 
#define Spi_Frequency  27000000
#define Spi_read_frequency  20000000

Alternatively, you can also the file here download.

code 

Sketch download

First we bind the libraries that are required for SPI communication, the TFT display and for the network connection:

#include "Spi.H"
#include "Tft_espi.h"
#include <Wifi.h>
#include "Time.H"

Next we define some variables that contain the position of the center of the watch, as well as the time (hours, minutes, seconds) and other configurations, such as the address of the NTP server. The PI value is needed to calculate the positions of the pointers in the circle because the pointers' coordinates run on a circular path. The constants SSID and password must be changed to your network access data.

intimately clock_center_y = 120;
intimately clock_center_x = 120;
intimately minute = 45;
intimately Hours = 6;
intimately seconds = 45;
const char* SSID = "";
const char* password = "";
const char* ntpserver = "pool.ntp.org";
const long GMTOFFSET_SEC = 0;
double pi = 3.14159;
Tft_espi TFT = Tft_espi();
time_t snow;
TM TM;

In the set up() the WiFi connection is built up and the time is called up using the NTP server. The dial is then drawn.

void set up() { 
  Wifi.Begin(SSID, password);
  while (Wifi.status() != Wl_connected) {
    delay(500);
  }
  configime(3600, 3600, ntpserver);  // time is synchronized
  struct TM TM;
  IF (!getlocalime(&TM)) {
    return;
  }
  seconds = TM.tm_sec;
  minute = TM.tm_min;
  Hours = TM.tm_hour;
  TFT.init();
  TFT.Fillscreen(0x000000);  // fill the display black
  draw_clock_face();  // draw dial
}

In order to visualize the clock, of course we also have to create a dial with the hourly markings. Here we draw the hour markings (1 to 12) on the dial. This is done by calculating the X and Y coordinates for each point based on the circle geometry.

void Drawclockface(){
  for (intimately I = 1; I < 12; I++) {
    y = (120 * cos(pi - (2 * pi) / 12 * I)) + clock_center_y;
    X = (120 * sin(pi - (2 * pi) / 12 * I)) + clock_center_x;
    Y_1 = (110 * cos(pi - (2 * pi) / 12 * I)) + clock_center_y;
    X_1 = (110 * sin(pi - (2 * pi) / 12 * I)) + clock_center_x;
    TFT.drawline(X_1, Y_1, X, y, Tft_white);  // drawing hour markings
  }
  TFT.SettextSize(2);  
  TFT.SettextColor(Tft_white); 
  TFT.setcursor(clock_center_x - 10, 0);  
  TFT.print(F("12"));  // add "12" above
}                                                        

Redrawclockface () Draws the 12 o'clock marker and the circle in the middle, since parts of the markings are also deleted when the individual hands are deleted.

void redrawclockface elements(){
    TFT.drawcircle(clock_center_x, clock_center_y,3, Tft_white);
    TFT.Fillcircle(clock_center_x, clock_center_y,3, Tft_white);
    TFT.setcursor(clock_center_x-10, 0);
    TFT.SettextColor(Tft_white);
    TFT.SettextSize(2);
    TFT.print(F("12"));
}

Now come the actual clock handers, which are displayed on the basis of the current time. The Draw_Hour ()- and the Draw_minute ()-The methods draw the hour and minute hands on the dial. The parameter fashion Determine whether the pointer is drawn or deleted.

void drawshour(intimately house, intimately minute, intimately fashion){
  intimately L = 70;
   y= (L*cos(pi-(2*pi)/12*house-(2*PI)/720*minute))+clock_center_y;
   X =(L*sin(pi-(2*pi)/12*house-(2*PI)/720*minute))+clock_center_x;
   IF (fashion==1){
    TFT.drawline(clock_center_x,clock_center_y,X,y,Tft_orange);
    TFT.drawline(clock_center_x+1,clock_center_y+1,X+1,y+1,Tft_orange);
   }
   Else{
    TFT.drawline(clock_center_x,clock_center_y,X,y,Tft_black);
    TFT.drawline(clock_center_x+1,clock_center_y+1,X+1,y+1,Tft_black);
   }  
}
void Drawminute(intimately minute, intimately fashion){
  intimately L  = 110;
   y= (L*cos(pi-(2*pi)/60*minute))+clock_center_y;
   X =(L*sin(pi-(2*pi)/60*minute))+clock_center_x;
   IF (fashion==1)TFT.drawline(clock_center_x,clock_center_y,X,y,Tft_cyan); 
   Else TFT.drawline(clock_center_x,clock_center_y,X,y,Tft_black);
}

The second hand is shown somewhat differently - as a small circle.

void Drawsecond(intimately second, intimately fashion){
  intimately L = 100;
  double wheel = pi-(2*pi)/60*second;
  y= (L*cos(wheel))+clock_center_y;
  X =(L*sin(wheel))+clock_center_x;
  IF (fashion==1) TFT.drawcircle(X, y, 3, Tft_white);
  Else TFT.drawcircle(X, y, 3, Tft_black);
}

The method Date () Depending on the current date, depending on the position of the hour pointer, or at the center.

void date() {
  struct TM TM;
  while(!getlocalime(&TM)){
    return;
  }
  IF((TM.tm_hour == 3 || TM.tm_hour == 9 || TM.tm_hour == 15 || TM.tm_hour == 21) && (TM.tm_min == 0 && TM.tm_sec <= 3)) TFT.Fillscreen(0x000000);
  IF((TM.tm_hour > 3 && TM.tm_hour < 9) || (TM.tm_hour > 15 && TM.tm_hour < 21)) { // date above -> pointer below
    TFT.Fillrect(70,90,110,25,Tft_black);
    TFT.setcursor(60,97);
    TFT.SetteextColor(Tft_pink);
    TFT.SettextSize(2);
    TFT.printf("%02d.%02d.%04d", TM.tm_mday, TM.tm_mon + 1, TM.tm_year + 1900);
  }
  Else {
    TFT.Fillrect(70,130,110,25,Tft_black);
    TFT.setcursor(60,137);
    TFT.SetteextColor(Tft_pink);
    TFT.SettextSize(2);
    TFT.printf("%02d.%02d.%04d", TM.tm_mday, TM.tm_mon + 1, TM.tm_year + 1900);
  }
}

In the Loop ()-Function is continuously updated and the pointers are drawn accordingly. First, the prior pointer is deleted to avoid deleting the complete display. The current time is then displayed.

void loop() {
  struct TM TM;
  while (!getlocalime(&TM)) {
    return;
  }
  Drawsecond(seconds, 0);
  Drawminute(minute, 0);
  drawshour(Hours, minute, 0);
  seconds = TM.tm_sec;
  minute = TM.tm_min;
  Hours = TM.tm_hour;
  Drawsecond(seconds, 1);
  Drawminute(minute, 1);
  drawshour(Hours, minute, 1);
  redrawclockface elements();
  date();
  delay(500);
}

Result

The project can of course also be soldered on a board for long-term operation or installed in a 3D printed housing, here there are no limits to creativity. Furthermore, the colors of the clockwise can easily be changed, or other information is displayed instead of the time. Since the synchronization of the time of the NTP Time Server is queried on the Internet, a WLAN connection (2.4GHz) must be available.

Have fun recovery :)

DisplaysEsp-32Für arduinoProjekte für anfänger

18 commenti

OE6LME

OE6LME

Hallo Andreas Wolter
Habe die gleichen Fehler wie alle anderen. Du hast ja den Artikel geschrieben und auch das Programm mit den oben angeführten Komponenten in Betrieb genommen. Warum funktioniert es bei dir ? Hast du den GPIO0 Fehler nicht? warum nicht ?
Was hast du genau gemacht das es funktioniert. Mit den heruntergeladenen Programm und den S2 Chip geht das nicht. Also was hast du genau gemacht. Bitte um genaue Anleitung.
Bedanke mich im Voraus für deine Hilfe.
lg. Manfred

Bastian Brumbi

Bastian Brumbi

Bei Problemen mit dem Upload des Programms überprüfen Sie, dass das richtige Board (ESP32S2) am richtigen Port ausgewählt ist. Des weiteren muss die upload Method auf internal USB gestellt werden.
Bei Problemen mit dem Display überprüfen Sie die UserSetup Datei in der Library und die verkabelung zum Display
Ich hoffe ich konnte ihnen weiterhelfen.

Bernd-Steffen

Bernd-Steffen

Hallo zusammen, ich hab das Ganze mit dem GC9A01A-TFT-Display aufgebaut, aber als Controller einen ESP32 Mini D1 verwendet, programmiert mit der Arduino IDE als ESP 32_WROOM-DA-Board. Der hat etwas andere GPIO rausgeführt – z.B. 7, 9, 11 gibt es da nicht auf dem Breakout, das ich hier habe. Nach ein wenig suchen nach den Einstellungen hab ich die Anpassung dazu in der Datei “User-Setup.h” gefunden, die die IDE parallel zum Sketch in einem separaten Ordner aufmacht. Dort die richtigen Anschlüsse eingetragen, (bei mir DC an GPIO17, SDA an GPIO21, SCL an GPIO22, RST an GPIO23, CS an GPIO05 kann so bleiben) und schon funktionierts. Freu! Viele Grüße von Bernd-Steffen

Frank Beutler

Frank Beutler

Bei denen wo es nicht geht in den Einstellungen insbesondere die Pin’s für den SPI-Bus prüfen.

Ich verwende aktuell ein ESP32DevKit und habe in der User_Setup.h in der Library TFT_eSPI folgende Konfiguration:

#define USER_SETUP_INFO “User_Setup”
#define GC9A01_DRIVER
#define TFT_HEIGHT 240
#define TFT_WIDTH 240
#define TFT_MISO 19 // Daten vom SPI-Device – hier ungenutzt
#define TFT_MOSI 23 // Daten zum SPI-Device, oft SDA benannt
#define TFT_SCLK 18
#define TFT_CS 2
#define TFT_DC 4
#define TFT_RST 0
#define LOAD_GLCD
#define LOAD_FONT2
#define LOAD_FONT4
#define LOAD_FONT6
#define LOAD_FONT7
#define LOAD_FONT8
#define LOAD_GFXFF
#define SMOOTH_FONT
#define SPI_FREQUENCY 27000000
#define SPI_READ_FREQUENCY 20000000

DIe GPIO’s für SPI sind von Board zu Board unterschiedlich …

Arduino-Studio verwendet meines Wissens globale Librarys. Ändert man dort was schlägt das auf alle Projekte durch. PlatformIO nutzt projektspezifische Librarys so das man da freier ist.

Alternativ die Konfiguration im Kopf der .ini bzw .cpp definieren.

Programm läuft – nach ein paar kleinen Optimierungen – super, geht aber auch mit dem vorgestellten Quellcode.

Thomas

Thomas

Hallo zusammen,
Leiter läuft es bei mir gar nicht :-(
Getestet habe ich das Display mit einem 8266 > Display ist i.O,
Den Sketch kann ich hochladen und nach dem Reset verbindet sich der esp32 mit dem Router (Protokoll des Routers). Die Hintergrund Erleuchtung des Displays ist an. Habe den esp32 auch schon neben den Router gestellt um Verbindungsprobleme zu vermeiden. Verkabelung zig mal geprüft. Gibt es in dieser Konfiguration an andere Möglichkeit das Display zu prüfen?

Heiend

Heiend

Hallo Stefan,
ich muss bei mir den Button 0 nicht drücken um den Code flashen zu können, die Software regelt das alles irgendwie selbst. Dann wirft die Arduino IDE bei mir auch nicht mehr den Fehler und alles läuft wie erwartet.

Manfred Lampl

Manfred Lampl

Hallo
Hab das gleiche Problem wie Stefan. Finde auch keine Einstellungen im “Werkzeug” wo dann der Fehler GPIO0 behoben ist. Weiters habe ich es mit mehreren ES32 Modulen Probiert. Der ESP32 bootet nach dem flashen ständig. Habe herausgefunden das die tft.init(); Anweisung Schuld daran ist. Anscheinent gibts Problem mit der Libaray.
Fazit: Es funktioniert leider nicht und man muss schon ein Profi sein und das Projekt zum laufen zu bringen. Vielleicht gibt es einen User der Tips geben kann und es tatsächlich geschafft hat die Uhr zum laufen zu bringen. Würde mich über Tips freuen. Einen schönen tag noch an alle

Matti

Matti

Ich habe das gleiche Problem wie Stefan, gibt es inzwischen eine Lösung?

Sascha

Sascha

Habe das gleiche Problem wie Stefan, der Upload zählt durch bis 100%, dann kommt die Meldung “chip was placed into download mode using GPIO0” und “Fehlgeschlagenes Hochladen”. Habe schon viele ESP32-Boards geflasht, das Problem kenne ich bisher nicht. Hat jemand eine Idee ?

Manfred

Manfred

Habe den gleichen Fehler wie Stefan. Hat jemand die richtigen Einstellungen im “Werkzeug” gefunden? Bitte um Info. Wäre schade wenn die Uhr nicht funktionieren sollte.
Danke im voraus für die Hilfe

Frank Beutler

Frank Beutler

Schönes Beispielprogramm. Leider in der englischen Version massig Übersetzungsfehler.
Mit ein paar Anpassungen weitestgehend “flickerfree” …

Thomas

Thomas

Leider bekomme ich es nicht ans laufen.
Der Code ist hochgeladen, der ESP verbindet sich auch mit dem Router, das Display bleibt schwarz nach dem Reset :-(
Verkabelung nochmals geprüft. Der ESP ist wohl auch richtig in der Arduino IDE konfiguriert. Das Beispiel mit der blinkenden LED läuft prompt.

Andreas Wolter

Andreas Wolter

@Wouters Marc: not at this moment, sry

@Jürgen: ich würde es einfach ausprobieren. Eventuell müssten für SDA und SCL andere Pins angeschlossen werden.

@Udo Brendel: danke für den Hinweis. Ich habe die .INO Datei korrigiert.

@Stefan: eventuell muss in den Board-Einstellungen unter WERKZEUGE etwas angepasst werden. Ansonsten interpretiere ich die Meldung so, dass etwas an GPIO0 angeschlossen ist, was den Upload verhindert. Manche Boards brauchen auf bestimmten Pins einen bestimmten Pegel (HIGH oder LOW), damit der Upload funktioniert. Der Zustand könnte sich ändern, wenn etwas an diese Pins angeschlossen ist. Dafür muss man nur das entsprechende Kabel für den Upload entfernen.

@oe1kaw: can you tell me what you have changed? I have corrected the mistake that Udo Brendel told us about. The compilation process works for me without errors now.

Grüße, best regards,
Andreas Wolter
AZ-Delivery Blog

Jürgen

Jürgen

Hallo Bastian,
kann ich anstelle eines ESP32-S2 auch einen ESP32-D1 verwenden?
Den hätte ich bereits hier herumzuliegen.
Danke

Stefan

Stefan

Die “Uhr” geht leider nicht, weil nach dem kompilieren folgende Fehlermeldung kommt:
Leaving…
WARNING: ESP32-S2FNR2 (revision v0.0) chip was placed into download mode using GPIO0.
esptool.py can not exit the download mode over USB. To run the app, reset the chip manually.
To suppress this note, set —after option to ‘no_reset’.
Fehlgeschlagenes Hochladen: Hochladefehler: exit status 1

Kann mir jemand weiterhelfen?

Udo Brendel

Udo Brendel

für Bastler gibt es hier immer mal wieder ein schönes Projekt so auch diese Uhr.
Der Sketch zum Herunterladen enthält leider einen Fehler im Setup Teil, Zeile 41 da sollte folgendes stehen: drawClockFace();

oe1kaw

oe1kaw

Nice project, but there are some TYPOs and missing declarations in the sketch. Very easy to correct, even for beginners

Wouters Marc

Wouters Marc

Hello,
Does there exist also the program in Python ?
Thanks !
Regards, Marc

Lascia un commento

Tutti i commenti vengono moderati prima della pubblicazione