joi, 19 martie 2026

Ceas analogic - OLED GC9A01 si Wemos D1 mini

Acesta este proiectul unui ceas analogic. Se poate configura intr-o multitudine de "fețe". In poze sunt preferatele mele. Enjoy !

 

 


#include <ESP8266WiFi.h>
#include <WiFiUdp.h>
#include <NTPClient.h>
#include <TFT_eSPI.h>
#include <TimeLib.h>

// --- CONFIGURARE WIFI ---
const char *ssid = "yourSSID";
const char *password = "yourPSW";

TFT_eSPI tft = TFT_eSPI();
TFT_eSprite spr = TFT_eSprite(&tft);
WiFiUDP ntpUDP;
NTPClient timeClient(ntpUDP, "pool.ntp.org", 0, 86400000); // Sincronizare la 24h

uint16_t palettePtr[16];

// --- PROTOTIPURI ---
void drawStickHand(float deg, int len, int tail, int width, int colIdx);
int getOffset(time_t t);

void setup() {
tft.init();
tft.setRotation(0);
tft.fillScreen(TFT_BLACK);

tft.setTextColor(TFT_WHITE);
tft.setTextDatum(MC_DATUM);
tft.setFreeFont(&FreeSansBold12pt7b);
tft.drawString("Conectare...", 120, 120);

WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) delay(500);

timeClient.begin();
timeClient.update();

// --- PALETĂ (4 biți = 16 culori) ---
palettePtr[0] = TFT_BLACK;
palettePtr[1] = tft.color565(230, 0, 0); // Roșu intens ace
palettePtr[2] = TFT_WHITE; // Cifre / Gradații
palettePtr[3] = tft.color565(80, 80, 80); // Gradații minute (Gri)
palettePtr[4] = tft.color565(140, 140, 140); // Dată (Gri discret)
palettePtr[5] = tft.color565(0, 255, 120); // Verde Neon pentru secundar

spr.setColorDepth(4);
spr.createSprite(240, 240);
spr.createPalette(palettePtr, 16);
}

void loop() {
timeClient.update();
time_t rawTime = timeClient.getEpochTime();
time_t localTime = rawTime + getOffset(rawTime);

int h = hour(localTime) % 12, m = minute(localTime), s = second(localTime);
float sDeg = s * 6, mDeg = m * 6 + s * 0.1, hDeg = h * 30 + m * 0.5;

spr.fillScreen(0);

// 1. GRADAȚII MARGINE
for (int i = 0; i < 60; i++) {
float rad = (i * 6 - 90) * 0.0174532925;
float w = (i % 5 == 0) ? 0.035 : 0.018;
int rIn = (i % 5 == 0) ? 105 : 112;
int x1 = 120 + cos(rad - w) * rIn; int y1 = 120 + sin(rad - w) * rIn;
int x2 = 120 + cos(rad + w) * rIn; int y2 = 120 + sin(rad + w) * rIn;
int x3 = 120 + cos(rad - w) * 120; int y3 = 120 + sin(rad - w) * 120;
int x4 = 120 + cos(rad + w) * 120; int y4 = 120 + sin(rad + w) * 120;
spr.fillTriangle(x1, y1, x2, y2, x3, y3, (i % 5 == 0) ? 2 : 3);
spr.fillTriangle(x2, y2, x3, y3, x4, y4, (i % 5 == 0) ? 2 : 3);
}

// 2. CIFRE MARI
spr.setFreeFont(&FreeSansBold18pt7b); spr.setTextColor(2); spr.setTextDatum(MC_DATUM);
spr.drawString("12", 120, 42); spr.drawString("06", 120, 198);
spr.drawString("09", 42, 120); spr.drawString("03", 198, 120);

// 3. DATA (zz-ll-aaaa)
spr.setFreeFont(&FreeSans9pt7b); spr.setTextColor(4);
char dataStr[12]; sprintf(dataStr, "%02d-%02d-%04d", day(localTime), month(localTime), year(localTime));
spr.drawString(dataStr, 120, 155);

// 4. ACE ORE & MINUTE
// Parametri: Unghi, Lungime vârf, Lungime, Lățime, Culoare
drawStickHand(hDeg, 58, 18, 10, 1); // Ore: Grosime 10px, coadă 18px
drawStickHand(mDeg, 105, 18, 6, 1); // Minute: Lung (până la marcaje), subțire 6px, coadă 18px

// 5. SECUNDARUL
drawStickHand(sDeg, 110, 25, 2, 5); // Secundar: Foarte subțire 2px, coadă lungă 25px

// 6. CENTRUL (Cerc alb cu contur negru)
spr.fillCircle(120, 120, 4, 2);
spr.drawCircle(120, 120, 4, 0);

spr.pushSprite(0, 0);
delay(150);
}

// --- FUNCȚIE PENTRU ACE DREPTUNGHIULARE ---
void drawStickHand(float deg, int len, int tail, int width, int colIdx) {
float rad = (deg - 90) * 0.0174532925;
// Vector normal pentru lățime constantă
float nx = -sin(rad) * (width / 2.0);
float ny = cos(rad) * (width / 2.0);

// Calculăm cele 4 puncte: coada acului se extinde în direcția opusă vârfului
int x1 = 120 - cos(rad) * tail + nx; int y1 = 120 - sin(rad) * tail + ny;
int x2 = 120 - cos(rad) * tail - nx; int y2 = 120 - sin(rad) * tail - ny;
int x3 = 120 + cos(rad) * len + nx; int y3 = 120 + sin(rad) * len + ny;
int x4 = 120 + cos(rad) * len - nx; int y4 = 120 + sin(rad) * len - ny;

spr.fillTriangle(x1, y1, x2, y2, x3, y3, colIdx);
spr.fillTriangle(x2, y2, x3, y3, x4, y4, colIdx);
}

int getOffset(time_t t) {
tmElements_t tm; breakTime(t, tm);
if (tm.Month < 3 || tm.Month > 10) return 7200;
if (tm.Month > 3 && tm.Month < 10) return 10800;
int dSun = tm.Day - ((tm.Wday - 1 + 7) % 7);
if (tm.Month == 3) return (dSun >= 25) ? 10800 : 7200;
return (dSun >= 25) ? 7200 : 10800;
}

Niciun comentariu:

Trimiteți un comentariu