Bu projede XIAO ESP32-C6 geliştirme kartı, 1.8 inç SPI TFT ekran ve bir buton kullanarak tepki süresini ölçen mini bir refleks oyunu yapacağız. Ekranda yeşil renk ve “BAS!” yazısı göründüğü anda butona basacak, tepki süremizi milisaniye cinsinden öğreneceğiz.
Proje; SPI haberleşmesini, TFT ekran kullanımını, buton okumayı, rastgele süre oluşturmayı ve millis() ile zaman ölçmeyi öğrenmek için eğlenceli bir başlangıç uygulamasıdır.
Refleks Oyunu Nasıl Çalışır?
- Ekranda “Başlamak için bas” mesajı görüntülenir.
- Oyuncu butona basınca yeni oyun başlar.
- Ekranda “Hazır ol...” mesajı görünür.
- Sistem rastgele olarak 2–5 saniye bekler.
- Ekran yeşile döner ve “BAS!” yazısı görünür.
- Oyuncunun butona basması için geçen süre ölçülür.
- Beş turun sonunda en iyi ve ortalama süre gösterilir.
Oyuncu yeşil ekran görünmeden butona basarsa ekranda “Çok erken!” uyarısı çıkar. Aynı tur yeniden başlatılır ve bu sonuç skora eklenmez.
247 ms
COK IYI!
Gerekli Malzemeler
- 1 adet XIAO ESP32-C6 Geliştirme Kartı
- 1 adet 1.8″ 128×160 SPI TFT LCD Ekran
- 1 adet 6×6 mm Anlık Push Buton
- Breadboard
- Erkek-erkek jumper kablolar
- USB Type-C veri kablosu
Kullanacağımız TFT Ekran
Projede kullanılan 1.8 inç TFT ekran 128×160 piksel çözünürlüğe ve ST7735R ekran sürücüsüne sahiptir. Görüntü bilgileri XIAO ESP32-C6 kartından ekrana SPI haberleşmesiyle gönderilir.
Ekran dokunmatik değildir. Bu nedenle oyuncunun tepki vermesi için ayrı bir fiziksel buton kullanacağız.
TFT ekran üzerindeki önemli pinler
- VCC: Ekranın besleme bağlantısıdır.
- GND: Devrenin ortak eksi bağlantısıdır.
- SCK veya CLK: SPI saat sinyalidir.
- SDA veya MOSI: Görüntü verisinin ekrana gönderildiği pindir.
- CS: Kullanılacak SPI cihazını seçer.
- DC veya A0: Komut ve görüntü verisini birbirinden ayırır.
- RST veya RESET: Ekranı sıfırlar.
- LED veya BL: Ekran arka aydınlatmasıdır.
XIAO ESP32-C6 ile TFT Ekran Bağlantısı
Bağlantıları yapmadan önce XIAO ESP32-C6 kartını USB kablosundan ayırın. Tüm kabloları kontrol ettikten sonra karta güç verin.
| TFT ekran pini | XIAO ESP32-C6 pini | Görevi |
|---|---|---|
| VCC | 3V3 | Ekran beslemesi |
| GND | GND | Ortak eksi bağlantısı |
| SCK / CLK | D8 | SPI saat sinyali |
| SDA / MOSI | D10 | XIAO'dan ekrana veri gönderir |
| CS | D3 | Ekranı seçer |
| DC / A0 | D2 | Komut ve görüntü verisini ayırır |
| RST / RESET | D1 | Ekranı sıfırlar |
| LED / BL | 3V3 | Arka aydınlatmayı çalıştırır |
TFT ekran yalnızca veri aldığı için ekranın MISO bağlantısına ihtiyaç duyulmaz. XIAO ESP32-C6 kartının donanımsal SPI pinleri D8=SCK, D9=MISO ve D10=MOSI şeklindedir.
Buton Bağlantısı
| Buton bağlantısı | XIAO ESP32-C6 pini |
|---|---|
| Butonun bir tarafı | D0 |
| Butonun karşı tarafı | GND |
Kod içerisinde D0 pini INPUT_PULLUP olarak kullanılacaktır. Böylece ayrıca bir pull-up direnci bağlamamız gerekmez. Buton serbestken pin HIGH, butona basıldığında ise LOW okunur.
Arduino IDE Kurulumu
1. Arduino IDE'yi yükleyin
Arduino IDE'nin güncel kararlı sürümünü bilgisayarınıza indirip kurun. Ardından Arduino IDE'yi açın.
2. ESP32 kart adresini ekleyin
Arduino IDE içinde Dosya > Tercihler bölümünü açın. “Ek Kart Yöneticisi URL'leri” alanına aşağıdaki adresi ekleyin:
https://espressif.github.io/arduino-esp32/package_esp32_index.json3. ESP32 kart paketini kurun
- Araçlar > Kart > Kart Yöneticisi bölümünü açın.
- Arama alanına esp32 yazın.
- Espressif Systems tarafından hazırlanan ESP32 paketini kurun.
- Kart olarak XIAO_ESP32C6 seçeneğini seçin.
- XIAO kartının bağlı olduğu doğru seri portu seçin.
4. Ekran kütüphanelerini kurun
Araçlar > Kütüphaneleri Yönet bölümünü açarak aşağıdaki iki kütüphaneyi kurun:
- Adafruit GFX Library
- Adafruit ST7735 and ST7789 Library
Arduino IDE ek bağımlılıkların kurulmasını sorarsa Install All seçeneğini kullanabilirsiniz.
Mini Refleks Oyunu Arduino Kodu
Aşağıdaki kodu Arduino IDE'ye yapıştırın. Kart ve port seçimlerini kontrol ettikten sonra Yükle butonuna basın.
#include <Adafruit_GFX.h>
#include <Adafruit_ST7735.h>
#include <SPI.h>
#include <esp_system.h>
#include <limits.h>
// TFT ekran bağlantıları
#define TFT_CS D3
#define TFT_DC D2
#define TFT_RST D1
// Refleks butonu
#define BUTTON_PIN D0
// Oyun ayarları
const uint8_t TOTAL_ROUNDS = 5;
const unsigned long MIN_WAIT_TIME = 2000;
const unsigned long MAX_WAIT_TIME = 5000;
// TFT ekran nesnesi
Adafruit_ST7735 tft = Adafruit_ST7735(TFT_CS, TFT_DC, TFT_RST);
// Oyunun bulunabileceği durumlar
enum GameState {
START_SCREEN,
WAITING,
GO_SIGNAL,
ROUND_RESULT,
EARLY_PRESS,
GAME_FINISHED
};
GameState gameState = START_SCREEN;
unsigned long stateStartedAt = 0;
unsigned long randomWaitTime = 0;
unsigned long goStartedAt = 0;
unsigned long reactionTime = 0;
unsigned long totalReactionTime = 0;
unsigned long bestReactionTime = ULONG_MAX;
uint8_t currentRound = 1;
// Ekranın ortasına yazı yazdırır
void drawCenteredText(
const String &text,
int16_t y,
uint8_t textSize,
uint16_t color
) {
int16_t x1;
int16_t y1;
uint16_t textWidth;
uint16_t textHeight;
tft.setTextSize(textSize);
tft.setTextColor(color);
tft.setTextWrap(false);
tft.getTextBounds(
text.c_str(),
0,
y,
&x1,
&y1,
&textWidth,
&textHeight
);
int16_t x = (tft.width() - textWidth) / 2;
tft.setCursor(x, y);
tft.print(text);
}
// Butondaki mekanik titreşimleri filtreler
bool buttonPressed() {
static int lastReading = HIGH;
static int stableState = HIGH;
static unsigned long lastChangeTime = 0;
int currentReading = digitalRead(BUTTON_PIN);
if (currentReading != lastReading) {
lastReading = currentReading;
lastChangeTime = millis();
}
if (
millis() - lastChangeTime > 25 &&
currentReading != stableState
) {
stableState = currentReading;
if (stableState == LOW) {
return true;
}
}
return false;
}
void showStartScreen() {
tft.fillScreen(ST77XX_BLUE);
drawCenteredText("MINI REFLEKS", 22, 2, ST77XX_WHITE);
drawCenteredText("OYUNU", 45, 2, ST77XX_WHITE);
drawCenteredText(
"Baslamak icin",
81,
1,
ST77XX_YELLOW
);
drawCenteredText(
"butona bas",
96,
1,
ST77XX_YELLOW
);
}
void showWaitingScreen() {
tft.fillScreen(ST77XX_YELLOW);
drawCenteredText("HAZIR OL...", 42, 2, ST77XX_BLACK);
drawCenteredText(
"Tur " + String(currentRound) + "/" + String(TOTAL_ROUNDS),
78,
1,
ST77XX_BLACK
);
}
void showGoScreen() {
tft.fillScreen(ST77XX_GREEN);
drawCenteredText("BAS!", 38, 4, ST77XX_BLACK);
}
void showEarlyPressScreen() {
tft.fillScreen(ST77XX_RED);
drawCenteredText("COK ERKEN!", 32, 2, ST77XX_WHITE);
drawCenteredText("Yesili bekle", 70, 1, ST77XX_WHITE);
drawCenteredText("Tur tekrarlanacak", 88, 1, ST77XX_WHITE);
}
void showRoundResult() {
tft.fillScreen(ST77XX_BLACK);
drawCenteredText("TEPKI SUREN", 16, 2, ST77XX_CYAN);
drawCenteredText(
String(reactionTime) + " ms",
50,
3,
ST77XX_WHITE
);
String message;
if (reactionTime < 200) {
message = "INANILMAZ!";
} else if (reactionTime < 300) {
message = "COK IYI!";
} else if (reactionTime < 450) {
message = "IYI!";
} else {
message = "DAHA HIZLI!";
}
drawCenteredText(message, 91, 1, ST77XX_YELLOW);
}
void showFinalScreen() {
unsigned long averageTime =
totalReactionTime / TOTAL_ROUNDS;
tft.fillScreen(ST77XX_MAGENTA);
drawCenteredText("OYUN BITTI", 9, 2, ST77XX_WHITE);
drawCenteredText(
"En iyi: " + String(bestReactionTime) + " ms",
45,
1,
ST77XX_WHITE
);
drawCenteredText(
"Ortalama: " + String(averageTime) + " ms",
63,
1,
ST77XX_WHITE
);
drawCenteredText(
"Tekrar oynamak icin",
91,
1,
ST77XX_YELLOW
);
drawCenteredText(
"butona bas",
106,
1,
ST77XX_YELLOW
);
}
// Rastgele bekleme aşamasını başlatır
void beginWaiting() {
randomWaitTime = random(
MIN_WAIT_TIME,
MAX_WAIT_TIME + 1
);
showWaitingScreen();
stateStartedAt = millis();
gameState = WAITING;
}
// Bütün skorları sıfırlayıp yeni oyun başlatır
void startNewGame() {
currentRound = 1;
totalReactionTime = 0;
bestReactionTime = ULONG_MAX;
beginWaiting();
}
void setup() {
pinMode(BUTTON_PIN, INPUT_PULLUP);
// ESP32'nin rastgele sayı üreticisini kullanır
randomSeed(esp_random());
// XIAO ESP32-C6 donanımsal SPI pinleri
SPI.begin(D8, D9, D10, TFT_CS);
// ST7735R ekranı başlatır
tft.initR(INITR_BLACKTAB);
tft.setRotation(1);
showStartScreen();
}
void loop() {
bool pressed = buttonPressed();
switch (gameState) {
case START_SCREEN:
if (pressed) {
startNewGame();
}
break;
case WAITING:
// Oyuncu yeşil ekran gelmeden bastı
if (pressed) {
showEarlyPressScreen();
stateStartedAt = millis();
gameState = EARLY_PRESS;
}
// Rastgele bekleme süresi tamamlandı
else if (millis() - stateStartedAt >= randomWaitTime) {
showGoScreen();
// Yeşil ekran gösterildiği anı kaydet
goStartedAt = millis();
gameState = GO_SIGNAL;
}
break;
case GO_SIGNAL:
if (pressed) {
reactionTime = millis() - goStartedAt;
totalReactionTime += reactionTime;
if (reactionTime < bestReactionTime) {
bestReactionTime = reactionTime;
}
showRoundResult();
stateStartedAt = millis();
gameState = ROUND_RESULT;
}
break;
case ROUND_RESULT:
// Sonucu 2,5 saniye ekranda tut
if (millis() - stateStartedAt >= 2500) {
if (currentRound >= TOTAL_ROUNDS) {
showFinalScreen();
gameState = GAME_FINISHED;
} else {
currentRound++;
beginWaiting();
}
}
break;
case EARLY_PRESS:
// Erken basma ekranından sonra aynı turu tekrarla
if (millis() - stateStartedAt >= 2000) {
beginWaiting();
}
break;
case GAME_FINISHED:
if (pressed) {
startNewGame();
}
break;
}
}Kod Nasıl Çalışıyor?
SPI ekran bağlantılarının tanımlanması
#define TFT_CS D3
#define TFT_DC D2
#define TFT_RST D1Bu satırlar TFT ekranın CS, DC ve RESET bağlantılarının hangi XIAO pinlerine bağlı olduğunu programa bildirir.
Butonun dahili pull-up direnciyle kullanılması
pinMode(BUTTON_PIN, INPUT_PULLUP);XIAO'nun dahili pull-up direnci etkinleştirilir. Bu nedenle butona basıldığında D0 pini LOW olarak okunur.
Rastgele bekleme süresi
randomWaitTime = random(2000, 5001);Her turda 2000–5000 milisaniye arasında farklı bir bekleme süresi oluşturulur. Böylece oyuncu yeşil ekranın ne zaman çıkacağını önceden tahmin edemez.
Tepki süresinin ölçülmesi
goStartedAt = millis();
reactionTime = millis() - goStartedAt;Yeşil ekran gösterildiği anda başlangıç zamanı kaydedilir. Oyuncu butona bastığında güncel zamandan başlangıç zamanı çıkarılarak tepki süresi hesaplanır.
Neden delay() kullanılmadı?
Oyun sırasında uzun süreli delay() kullanılsaydı XIAO beklerken butonu kontrol edemezdi. Kodda millis() kullanıldığı için bekleme sırasında yapılan erken basışlar da algılanabilir.
Oyunu Çalıştırma
- Devre bağlantılarını yeniden kontrol edin.
- XIAO ESP32-C6 kartını veri aktarabilen USB kablosuyla bilgisayara bağlayın.
- Arduino IDE'de XIAO ESP32-C6 kartını ve doğru portu seçin.
- Kodu karta yükleyin.
- Ekranda başlangıç mesajı göründüğünde butona basın.
- “Hazır ol...” ekranındayken bekleyin.
- Yeşil ekran ve “BAS!” yazısı çıkar çıkmaz butona basın.
Karşılaşılabilecek Sorunlar
Ekran tamamen beyaz kalıyor
- CS, DC ve RESET kablolarını kontrol edin.
- SCK pininin D8'e bağlı olduğundan emin olun.
- SDA/MOSI pininin D10'a bağlı olduğundan emin olun.
- GND bağlantısının ortak olduğunu kontrol edin.
Ekranın arka ışığı yanmıyor
TFT modülündeki LED veya BL pinini kontrol edin. Bu pin boş bırakılmışsa ekran görüntü üretiyor olsa bile arka ışık yanmayabilir.
Görüntü renkleri yanlış
Bazı ST7735 ekran sürümlerinde başlangıç ayarı farklı olabilir. Aşağıdaki satırı:
tft.initR(INITR_BLACKTAB);sırasıyla şu seçeneklerden biriyle deneyebilirsiniz:
tft.initR(INITR_GREENTAB);
// veya
tft.initR(INITR_REDTAB);Görüntü ters veya yan duruyor
Aşağıdaki değeri 0, 1, 2 veya 3 yaparak ekran yönünü değiştirebilirsiniz:
tft.setRotation(1);Buton sürekli basılı görünüyor
Dört bacaklı tact butonda kabloları aynı taraftaki iki bacağa bağlamış olabilirsiniz. D0 ve GND bağlantılarını butonun karşılıklı taraflarına bağlayın.
Projeyi Nasıl Geliştirebiliriz?
- Yeşil ekran gösterildiğinde ses çıkaran bir buzzer eklenebilir.
- Kolay, normal ve zor olmak üzere farklı oyun modları hazırlanabilir.
- En iyi skor kalıcı olarak ESP32 hafızasına kaydedilebilir.
- İkinci bir buton eklenerek iki oyunculu yarış yapılabilir.
- Her turda ekranda farklı şekiller veya renkler gösterilebilir.
- XIAO ESP32-C6'nın kablosuz özelliği kullanılarak skorlar web sayfasında gösterilebilir.
- LiPo pil ve açma-kapama anahtarı eklenerek taşınabilir oyun cihazı yapılabilir.
Sonuç
Bu projede XIAO ESP32-C6 ile ST7735R tabanlı SPI TFT ekranı kontrol ettik, fiziksel bir butondan giriş aldık ve oyuncunun tepki süresini milisaniye cinsinden ölçtük. Aynı zamanda rastgele sayı oluşturma, buton titreşimini filtreleme ve millis() ile gecikmesiz zamanlama gibi birçok önemli programlama konusunu uygulamalı olarak öğrenmiş olduk.
.png)





