Community project
Mam Taki Sprzet 40 Szt Zestaw Kabli Do
This guide builds a Wi-Fi connected clock and weather display using an ESP32 microcontroller and a 1.69-inch TFT LCD screen. The display fetches real-time data over the internet and renders it on the 240×280 pixel display with custom graphics and color-coded information.
The project includes a complete wiring diagram showing how to connect the ST7789V3 display to the ESP32 via SPI, a full parts list for the cable kit and components, step-by-step assembly instructions, and ready-to-upload firmware that handles Wi-Fi connectivity, weather API integration, and display rendering.
Wiring diagram

Gather all the parts
| Qty | Component |
|---|---|
| 1 | TFT LCD 1.69″ 240×280 ST7789V3, SPI, 8-pin 1.69 in, 240×280 Mały kolorowy ekran, który pokazuje godzinę i aktualną pogodę pobraną przez Wi‑Fi. |
Assemble it in 5 steps
1. Odłącz zasilanie i przygotuj ekran
Odłącz kabel USB‑C od ESP32. Włóż płytkę ESP32 i ekran `tft_169` w płytkę stykową tak, aby metalowe piny nie dotykały przypadkowo sąsiednich rzędów.
- Napisy przy pinach ekranu są bardzo małe — użyj światła z telefonu, aby je odczytać.
- Jeżeli ekran ma osobny pasek ośmiu pinów do przylutowania, poproś o pomoc przy lutowaniu przed dalszym montażem.
- Nie podłączaj ekranu ani przewodów, gdy ESP32 jest zasilane z USB — przypadkowe zwarcie może uszkodzić płytkę lub kabel.
2. Podłącz zasilanie ekranu
Użyj dwóch przewodów. Połącz `tft_169` VCC z pinem 3V3 ESP32 (zasilanie) oraz `tft_169` GND z pinem GND ESP32 (wspólny powrót prądu).
- Szukaj dokładnie napisu „3V3”, a nie „VIN” lub „5V”.
- Wspólne GND jest konieczne, aby ekran rozumiał sygnały z ESP32.
- Nie zamieniaj VCC z GND — zamienione zasilanie może uszkodzić ekran.
- Nie łącz VCC ekranu z pinem 5V/VIN, gdyż ten projekt używa bezpiecznego zasilania 3,3 V.
3. Podłącz przewody obrazu
Połącz `tft_169` SCL z GPIO18 ESP32 (zegar danych) oraz `tft_169` SDA z GPIO23 ESP32 (dane obrazu). Następnie połącz `tft_169` CS z GPIO4 ESP32 (wybór ekranu), DC z GPIO16 (rodzaj przesyłanej informacji) i RES z GPIO17 (reset ekranu).
- Na niektórych ekranach SCL jest opisany jako SCK, a SDA jako MOSI — są to te same dwa przewody w tym projekcie.
- Dobierz różne kolory przewodów i zapisz je, aby łatwo znaleźć ewentualną pomyłkę.
- Nie podłączaj przewodu oznaczonego SDA do GPIO21 — w tym ekranie SDA oznacza dane SPI, a nie zwykłe przewody I2C.
- Wpięcie przewodu o jeden rząd obok może sprawić, że ekran pozostanie czarny mimo poprawnego programu.
4. Włącz podświetlenie
Połącz pin BLK, LED albo BL na `tft_169` z pinem 3V3 ESP32 (włącza światło za ekranem).
- Jeśli Twój moduł ma napis „BLK”, jest to właśnie pin podświetlenia.
- Po tym kroku sprawdź jeszcze raz osiem nazw pinów na ekranie i porównaj je z przewodami.
- Nie łącz pinu BLK z GND — ekran może wtedy pozostać ciemny i wyglądać na niesprawny.
5. Uruchom zegar i połącz go z Wi‑Fi
Podłącz ESP32 do komputera przewodem USB‑C. Po wgraniu programu przez przycisk Deploy użyj telefonu lub komputera, aby połączyć się z siecią Wi‑Fi „Zegar-ESP32”; otwórz stronę logowania, wybierz swoją sieć domową i wpisz jej hasło. Ekran sam pobierze czas i pogodę dla Warszawy.
- Sieć ustawień jest potrzebna tylko przy pierwszym połączeniu lub po zmianie domowego Wi‑Fi.
- Jeśli strona nie otworzy się sama, po połączeniu z „Zegar-ESP32” otwórz w przeglądarce adres 192.168.4.1.
- Wpisuj hasło tylko na stronie otwartej po połączeniu z własną siecią „Zegar-ESP32”; nie udostępniaj go osobom postronnym.
Review all connections
1. Connections between "tft_169" and "ESP32"
| Function | tft_169 | ESP32 |
|---|---|---|
| power | VCC | 3V3 |
| ground | GND | GND |
| spi | SCL | GPIO 18 |
| spi | SDA | GPIO 23 |
| digital | RES | GPIO 17 |
| digital | DC | GPIO 16 |
| digital | CS | GPIO 4 |
| power | BLK | 3V3 |
Deploy the firmware
#include <Arduino.h>
#include <SPI.h>
#include <WiFi.h>
#include <WiFiManager.h>
#include <HTTPClient.h>
#include <ArduinoJson.h>
#include <time.h>
#define TFT_CS 4
#define TFT_DC 16
#define TFT_RST 17
#define TFT_MOSI 23
// Forward declarations
void segment(int x,int y,int n,int s,uint16_t c);
void number(const String &v,int x,int y,int s,uint16_t c);
void header();
void drawWeather();
void getWeather();
void clockUpdate();
constexpr int TFT_SCLK = 18;
constexpr uint16_t NAVY = 0x0861, SKY = 0x5DDF, PALE = 0xE73C, WHITE = 0xFFFF, YELLOW = 0xFFE0, RED = 0xF800;
constexpr unsigned long WEATHER_INTERVAL_MS = 600000UL;
class ST7789Direct {
public:
void begin() {
pinMode(TFT_CS, OUTPUT); pinMode(TFT_DC, OUTPUT); pinMode(TFT_RST, OUTPUT);
SPI.begin(TFT_SCLK, -1, TFT_MOSI, TFT_CS);
digitalWrite(TFT_CS, HIGH); digitalWrite(TFT_RST, LOW); delay(20); digitalWrite(TFT_RST, HIGH); delay(120);
cmd(0x01); delay(150); cmd(0x11); delay(120); cmdData(0x3A, 0x55); cmdData(0x36, 0x00);
const uint8_t porch[] = {0x0C, 0x0C, 0x00, 0x33, 0x33};
const uint8_t power[] = {0xA4, 0xA1};
cmdData(0xB2, porch, sizeof(porch));
cmdData(0xB7, 0x35); cmdData(0xBB, 0x19); cmdData(0xC0, 0x2C); cmdData(0xC2, 0x01);
cmdData(0xC3, 0x12); cmdData(0xC4, 0x20); cmdData(0xC6, 0x0F); cmdData(0xD0, power, sizeof(power));
cmd(0x21); cmd(0x29); delay(20); fill(0, 0, 240, 280, NAVY);
}
void fill(int x, int y, int w, int h, uint16_t c) {
if (x < 0) { w += x; x = 0; } if (y < 0) { h += y; y = 0; }
if (x + w > 240) w = 240 - x; if (y + h > 280) h = 280 - y; if (w <= 0 || h <= 0) return;
uint8_t d[4]; d[0]=x>>8; d[1]=x; d[2]=(x+w-1)>>8; d[3]=x+w-1; cmdData(0x2A,d,4);
d[0]=y>>8; d[1]=y; d[2]=(y+h-1)>>8; d[3]=y+h-1; cmdData(0x2B,d,4); cmd(0x2C);
digitalWrite(TFT_DC,HIGH); digitalWrite(TFT_CS,LOW); SPI.beginTransaction(SPISettings(40000000, MSBFIRST, SPI_MODE0));
uint8_t hi=c>>8, lo=c; for (uint32_t i=0;i<(uint32_t)w*h;i++) { SPI.transfer(hi); SPI.transfer(lo); }
SPI.endTransaction(); digitalWrite(TFT_CS,HIGH);
}
private:
void cmd(uint8_t c) { digitalWrite(TFT_DC,LOW); digitalWrite(TFT_CS,LOW); SPI.beginTransaction(SPISettings(40000000,MSBFIRST,SPI_MODE0)); SPI.transfer(c); SPI.endTransaction(); digitalWrite(TFT_CS,HIGH); }
void cmdData(uint8_t c,uint8_t v) { cmdData(c,&v,1); }
void cmdData(uint8_t c,const uint8_t *p,uint8_t n) { cmd(c); digitalWrite(TFT_DC,HIGH); digitalWrite(TFT_CS,LOW); SPI.beginTransaction(SPISettings(40000000,MSBFIRST,SPI_MODE0)); while(n--) SPI.transfer(*p++); SPI.endTransaction(); digitalWrite(TFT_CS,HIGH); }
};
ST7789Direct tft;
const uint8_t segs[] = {0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F};
void segment(int x,int y,int n,int s,uint16_t c) { uint8_t m=segs[n]; int w=3*s, l=10*s; if(m&1)tft.fill(x+w,y,l,w,c); if(m&2)tft.fill(x+w+l,y+w,w,l,c); if(m&4)tft.fill(x+w+l,y+2*w+l,w,l,c); if(m&8)tft.fill(x+w,y+2*(w+l),l,w,c); if(m&16)tft.fill(x,y+2*w+l,w,l,c); if(m&32)tft.fill(x,y+w,w,l,c); if(m&64)tft.fill(x+w,y+w+l,l,w,c); }
void number(const String &v,int x,int y,int s,uint16_t c) { for (unsigned i=0;i<v.length();i++) { char q=v[i]; if(q>='0'&&q<='9') segment(x,y,q-'0',s,c); else if(q==':') { tft.fill(x+3*s,y+8*s,3*s,3*s,c); tft.fill(x+3*s,y+17*s,3*s,3*s,c); } else if(q=='.') tft.fill(x+3*s,y+25*s,3*s,3*s,c); else if(q=='-') tft.fill(x,y+13*s,10*s,3*s,c); x+=17*s; } }
void header() { tft.fill(0,0,240,280,NAVY); tft.fill(0,0,240,40,SKY); tft.fill(14,157,212,2,SKY); tft.fill(14,233,212,2,SKY); }
String weather = "--.-"; int weatherCode = -1; unsigned long lastWeather=0; String lastTime;
void drawWeather() { tft.fill(0,164,240,64,NAVY); number(weather,18,170,2,YELLOW); if(weatherCode==0) { tft.fill(175,178,32,32,YELLOW); } else if(weatherCode>=51) { tft.fill(165,182,50,16,PALE); for(int x=170;x<215;x+=12)tft.fill(x,204,3,12,SKY); } else { tft.fill(165,184,52,22,PALE); } }
void getWeather() { if(WiFi.status()!=WL_CONNECTED)return; HTTPClient h; h.begin("http://api.open-meteo.com/v1/forecast?latitude=52.2297&longitude=21.0122¤t=temperature_2m,weather_code"); int r=h.GET(); if(r==HTTP_CODE_OK){JsonDocument d; if(!deserializeJson(d,h.getStream())) { weather=String((float)(d["current"]["temperature_2m"]|0.0),1); weatherCode=d["current"]["weather_code"]|-1; drawWeather(); }} h.end(); }
void clockUpdate() { struct tm tm; if(!getLocalTime(&tm,50)) return; char b[6]; strftime(b,sizeof b,"%H:%M",&tm); String now(b); if(now!=lastTime){lastTime=now; tft.fill(0,48,240,102,NAVY); number(now,18,67,3,WHITE);} }
void setup() { Serial.begin(115200); tft.begin(); header(); WiFiManager wm; wm.setConfigPortalTimeout(180); wm.autoConnect("Zegar-ESP32"); configTzTime("CET-1CEST,M3.5.0,M10.5.0/3","pool.ntp.org","time.nist.gov"); clockUpdate(); getWeather(); lastWeather=millis(); }
void loop() { clockUpdate(); unsigned long n=millis(); if(n-lastWeather>=WEATHER_INTERVAL_MS){lastWeather=n;getWeather();} delay(50); }Remix this project
Make it yours in one click
Open a full copy of this project in your own Schematik workspace — diagram, code, parts, and assembly steps included. Swap the sensor, add features, or redesign the whole thing with AI. The author's original stays untouched.




