/*
Forum: https://forum.arduino.cc/t/linienstarke-in-ili9341-library/1417355
Wokwi: https://wokwi.com/projects/449696644780222465
ec2021
2025/12/07
*/
#include "SPI.h"
#include "Adafruit_GFX.h"
#include "Adafruit_ILI9341.h"
#include "PointerBarClass.h"
constexpr byte TFT_DC {2};
constexpr byte TFT_CS {15};
Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC);
constexpr struct clockData {
uint16_t x {120};
uint16_t y {140};
uint16_t innerR {110};
uint16_t outerR {118};
uint16_t innerMark {102};
uint16_t outerMark {114};
uint16_t secondR {100};
uint16_t secondSize {5};
uint16_t minuteR {84};
uint16_t minuteSize {5};
uint16_t hourR {62};
uint16_t hourSize {5};
uint16_t backColor {0x5AEB};
} myClock;
constexpr float ZeroAngle {-90.0};
unsigned long lastUpdate = 0;
static uint8_t conv2d(const char* p) {
uint8_t v = 0;
if ('0' <= *p && *p <= '9')
v = *p - '0';
return 10 * v + *++p - '0';
}
// Get H, M, S from compile time
uint16_t hours = (conv2d(__TIME__) + 1) % 24, minutes = conv2d(__TIME__ + 3), seconds = conv2d(__TIME__ + 6);
void DrawAngledLine(int x, int y, int x1, int y1, int size, int color) {
// Source: https://github.com/G6EJD/GFX-Library-Any-Thickness-and-Angle-Line-Drawing
// In the GitHub reference dx and dy where defined the other way round
// however that leads to bars getting smaller and wider depending on the
// angle used
float dx = (size / 2.0) * (y - y1) / sqrt(sq(x - x1) + sq(y - y1));
float dy = (size / 2.0) * (x - x1) / sqrt(sq(x - x1) + sq(y - y1));
tft.fillTriangle(x + dx, y - dy, x - dx, y + dy, x1 + dx, y1 - dy, color);
tft.fillTriangle(x - dx, y + dy, x1 - dx, y1 + dy, x1 + dx, y1 - dy, color);
};
// Function call to draw a line from (x0,y0) to (x1, y1) in color col
void drawLine(int x0, int y0, int x1, int y1, int size, uint16_t col) {
// Insert the library specific function or routines here:
// The example uses tft.drawLine()
if (size == 1) {
tft.drawLine(x0, y0, x1, y1, col);
} else {
DrawAngledLine(x0, y0, x1, y1, size, col);
}
};
PointerClass sekundenZeiger(&drawLine),
minutenZeiger(&drawLine),
stundenZeiger(&drawLine);
// Function call to fill a rectangle from (x,y) with a width w and length l) in color col
void fillRect(int x, int y, int w, int l, uint16_t col) {
tft.fillRect(x, y, w, l, col);
}
BarClass sekundenBar(&fillRect),
minutenBar(&fillRect),
stundenBar(&fillRect);
void setup() {
Serial.begin(115200);
Serial.println("Graphics Clock Demo");
sekundenZeiger.setZeroAngle(ZeroAngle);
minutenZeiger.setZeroAngle(ZeroAngle);
stundenZeiger.setZeroAngle(ZeroAngle);
sekundenZeiger.setXYRM(myClock.x, myClock.y, myClock.secondR, myClock.secondSize);
minutenZeiger.setXYRM( myClock.x, myClock.y, myClock.minuteR, myClock.minuteSize);
stundenZeiger.setXYRM( myClock.x, myClock.y, myClock.hourR, myClock.hourSize);
sekundenBar.setBackColor(myClock.backColor);
minutenBar.setBackColor(myClock.backColor);
stundenBar.setBackColor(myClock.backColor);
tft.begin();
tft.setRotation(0);
paintClockFace();
choose();
}
void loop() {
if (millis() - lastUpdate >= 1000) {
lastUpdate = millis();
handleTime();
}
}
void handleTime() {
seconds++;
if (seconds >= 60) {
choose();
seconds = 0;
minutes++;
if (minutes >= 60) {
minutes == 0;
hours++;
if (hours >= 24) hours = 0; // Advance hour
}
}
sekundenBar.draw(seconds, ILI9341_RED);
minutenBar.draw(minutes, ILI9341_YELLOW);
stundenBar.draw(hours, ILI9341_WHITE);
float sdeg = seconds * 6;
float mdeg = minutes * 6;
float hdeg = hours * 30 + mdeg * 0.0833333 + sdeg * 0.01666667;
stundenZeiger.draw(hdeg, ILI9341_WHITE);
minutenZeiger.draw(mdeg, ILI9341_WHITE);
sekundenZeiger.draw(sdeg, ILI9341_RED);
tft.fillCircle(myClock.x, myClock.y, 3, ILI9341_RED);
tft.setCursor(70, 280);
tft.setTextSize(2);
tftTime( hours, ':');
tftTime(minutes, ':');
tftTime(seconds, ' ');
}
void tftTime(int val, char c) {
if (val < 10 ) tft.print("0");
tft.print(val);
tft.print(c);
}
void paintClockFace() {
PixelCoord dot, ending;
tft.fillScreen(myClock.backColor);
tft.setTextColor(ILI9341_WHITE, myClock.backColor);
tft.fillCircle(myClock.x, myClock.y, myClock.outerR, ILI9341_BLUE);
tft.fillCircle(myClock.x, myClock.y, myClock.innerR, ILI9341_BLACK);
// Zeichne die Stunden-Marken
for (int i = 0; i < 360; i += 30) {
dot = CoordFromAngleRadius(i, myClock.innerMark);
ending = CoordFromAngleRadius(i, myClock.outerMark);
tft.drawLine(dot.x, dot.y, ending.x, ending.y, ILI9341_BLUE);
}
// Zeichne die Sekunden- und die Viertelstunden-Marken
for (int i = 0; i < 360; i += 6) {
dot = CoordFromAngleRadius(i, myClock.innerMark);
tft.drawPixel(dot.x, dot.y, ILI9341_WHITE);
if (i % 90 == 0) tft.fillCircle(dot.x, dot.y, 2, ILI9341_WHITE);
}
}
PixelCoord CoordFromAngleRadius(float Angle, int rad) {
return getPixelCoord(Angle, myClock.x, myClock.y, rad, ZeroAngle);
}
void choose() {
static byte choice = 0;
switch (choice) {
case 0:
sekundenBar.setXYWD(5, 240, 10, Direction::topDown);
minutenBar.setXYWD(20, 240, 10, Direction::topDown);
stundenBar.setXYWD(35, 240, 10, Direction::topDown);
break;
case 1:
sekundenBar.setXYWD(5, 300, 10, Direction::bottomUp);
minutenBar.setXYWD(20, 300, 10, Direction::bottomUp);
stundenBar.setXYWD(35, 300, 10, Direction::bottomUp);
break;
case 2:
sekundenBar.setXYWD(5, 270, 10, Direction::leftToRight);
minutenBar.setXYWD( 5, 285, 10, Direction::leftToRight);
stundenBar.setXYWD( 5, 300, 10, Direction::leftToRight);
break;
case 3:
sekundenBar.setXYWD(65, 270, 10, Direction::rightToLeft);
minutenBar.setXYWD( 65, 285, 10, Direction::rightToLeft);
stundenBar.setXYWD( 65, 300, 10, Direction::rightToLeft);
break;
}
choice = ++choice % 4;
}