Günümüzde sesli asistanlar, akıllı cihazlarla etkileşim kurmanın en yaygın yollarından biri haline geldi. Ancak yalnızca sesli komutlarla çalışmak yerine, görsel geri bildirim sağlayan fiziksel bir arayüz ekleyerek kullanıcı deneyimini daha sezgisel hale getirmek mümkün. Bu projede, sesli bir asistanın fiziksel bir arayüzle nasıl desteklenebileceğini ele alacağız.

Projenin Amacı
Projemiz, sesli bir asistana fiziksel bir geri bildirim arayüzü kazandırmayı amaçlamaktadır. Kullanıcılar, sesli komutlar verirken ekranda görsel bildirimler alacak ve RGB LED halkasıyla etkileşim deneyimi artırılacaktır. Böylece sesli asistanın komutları algıladığı ve işlem yaptığı süreç daha anlaşılır hale gelecektir.
Kullanılan Malzemeler
Bu projede aşağıdaki bileşenlere ihtiyacınız olacak:
- [17202] NodeMCU V3 ESP8266 ESP-12E Geliştirme Kartı – CH340
- [13711] WS2812 NeoPixel 16’lı Halka
- [18504] 0.96 inç I2C OLED Ekran – SSD1306
- [15934] Tinylab 3D 1.75 mm Şeffaf (Naturel) PLA Filament
- [11959] 40 Pin Ayrılabilen Dişi-Erkek M-F Jumper Kablo – 200 mm
Devre Şeması ve Bağlantılar

Bu projede kullanılan bileşenlerin bağlantıları şu şekildedir:
- NodeMCU – WS2812 NeoPixel Halka:
- 5V → VIN
- GND → GND
- D1 → D4
- NodeMCU – OLED Ekran:
- VDD → 3V
- VCC → GND
- SDA → D2
- TCL → D1
3D Baskı ve Tasarım

Fiziksel arayüzü destekleyen bir kasa tasarlamak için tinylab 3D PLA Filament kullanarak bir 3D baskı gerçekleştirilmiştir. Kasa, projenin bileşenlerini güvenli bir şekilde barındıracak şekilde tasarlanmıştır. Gövde bölümü, OLED ekranın ve WS2812 halka LED’in düzgün bir şekilde monte edilmesini sağlarken, alt ve üst kapaklar sayesinde bileşenlerin korunması ve estetik bir görünüm kazanması amaçlanmıştır. Üst kapak, gerektiğinde kolayca çıkarılabilir şekilde tasarlanmış olup, bakım veya güncellemeler sırasında iç bileşenlere erişim imkanı sunar. Alt kapak ise devre kartını ve bağlantıları güvenli bir şekilde yerinde tutarak projenin sağlamlığını artırır.
Yazılım ve Kodlama
NodeMCU kartı üzerine yüklenen kod, sesli asistandan gelen komutları işleyerek, OLED ekranda görüntüleme ve LED halkasında ışık animasyonları oluşturma işlemini gerçekleştirir. Arduino IDE kullanılarak geliştirilen kodun ana işlevleri şunlardır:
- Wi-Fi Bağlantısı Kurma: ESP8266’nın sesli asistan ile bağlantı kurmasını sağlar.
- Komutları Algılama: Kullanıcının verdiği sesli komutları işler.
- OLED Ekrana Bilgi Gönderme: Algılanan komutları ekranda gösterir.
- LED Halka Animasyonu: Asistanın aktif olduğunu göstermek için RGB LED halkasında dinamik ışık efektleri oluşturur.
Kodlar:
#include <ESP8266WiFi.h> // ESP8266 Wi-Fi kütüphanesi
#include <ESP8266HTTPClient.h> // HTTP istemcisi
#include <Adafruit_GFX.h> // GFX kütüphanesi
#include <Adafruit_SSD1306.h> // OLED ekran kütüphanesi
#include <ArduinoJson.h> // JSON ayrıştırma için kütüphane
#include <SPI.h>
#include <Wire.h>
#include <Adafruit_NeoPixel.h>
// Wi-Fi ayarları
const char* ssid = ""; // Wi-Fi ağ adı
const char* password = ""; // Wi-Fi şifresi
// OpenWeatherMap API bilgileri
const char* apiKey = ""; // OpenWeatherMap API anahtarı
const char* city = "Istanbul"; // Şehir
const char* country = "TR"; // Ülke kodu
// API URL'sini oluştur
String apiUrl = "http://api.openweathermap.org/data/2.5/weather?q=" + String(city) + "," + String(country) + "&appid=" + String(apiKey) + "&units=metric";
// OLED ekran boyutu
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 32
#define OLED_RESET -1 // Reset pin # (or -1 if sharing Arduino reset pin)
#define SCREEN_ADDRESS 0x3C
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
#define LED_PIN 2
#define LED_COUNT 16
Adafruit_NeoPixel strip(LED_COUNT, LED_PIN, NEO_GRB + NEO_KHZ800);
void setup() {
// Seri iletişim başlatılıyor
Serial.begin(115200);
strip.begin(); // NeoPixel başlatılıyor
strip.show();
delay(1000);
dsadhqwuıhdauıhwauhhduıwadıuwadjkawndwmna
for (int brightness = 0; brightness <= 2; brightness++)
{
for (int i = 0; i < LED_COUNT; i++) {
strip.setPixelColor(i, strip.Color(brightness, 0, 0)); // Kırmızı renkte ve artan parlaklık
}
strip.show();
delay(10);
}
// OLED ekran başlatma
if (!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS))
{
Serial.println("SSD1306 ekran başlatılamadı!");
while (true);
}
display.display();
display.clearDisplay();
// Wi-Fi bağlanma
Serial.println("Wi-Fi'ye bağlanılıyor...");
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(1000);
Serial.print(".");
}
// Bağlantı başarılı
display.clearDisplay();
display.setTextColor(SSD1306_WHITE);
display.setTextSize(1);
display.setCursor(0, 0);
display.println("Wifi baglanti basarili!");
display.display();
Serial.println("\nWi-Fi bağlantısı başarılı!");
Serial.print("IP Adresi: ");
Serial.println(WiFi.localIP());
delay(2000);
display.clearDisplay();
display.setTextColor(SSD1306_WHITE);
display.setTextSize(3);
int16_t x1, y1;
uint16_t textWidth, textHeight;
display.getTextBounds(":)", 0, 0, &x1, &y1, &textWidth, &textHeight);
int x = (SCREEN_WIDTH - textWidth) / 2;
int y = (SCREEN_HEIGHT - textHeight) / 2;
display.setCursor(x, y);
display.print(":)"); // Metni ekrana yazdır
display.display();
}
void getTextBounds(const char *string, int16_t x, int16_t y, int16_t *x1, int16_t *y1, uint16_t *width, uint16_t *height);
void loop() {
if (Serial.available()) {
char data = Serial.read();
if(data == 'p')
{
if (WiFi.status() == WL_CONNECTED)
{
WiFiClient client;
HTTPClient http;
// HTTP isteği gönder
Serial.println("API'ye bağlanılıyor...");
http.begin(client, apiUrl);
int httpResponseCode = http.GET();
// Yanıt başarılıysa
if (httpResponseCode == HTTP_CODE_OK)
{
String payload = http.getString();
Serial.println("Veri alındı:");
Serial.println(payload);
// JSON ayrıştırma
DynamicJsonDocument doc(1024);
DeserializationError error = deserializeJson(doc, payload);
if (error)
{
Serial.print("JSON Ayrıştırma Hatası: ");
Serial.println(error.f_str());
return;
}
const char* weather = doc["weather"][0]["main"]; // Hava durumu
float temp = doc["main"]["temp"]; // Sıcaklık
int humidity = doc["main"]["humidity"]; // Nem oranı
Serial.println(weather);
Serial.println(temp);
Serial.println(humidity);
display.clearDisplay();
display.setTextColor(SSD1306_WHITE);
display.setTextSize(1);
display.setCursor(0, 0);
display.println("Hosgeldiniz!");
display.display();
delay(2000);
display.clearDisplay();
display.setCursor(0, 0);
display.println("Sehir: Istanbul");
display.print("Durum: ");
display.println(weather);
display.print("Sicaklik: ");
display.print(temp);
display.println(" C");
display.print("Nem: ");
display.print(humidity);
display.println(" %");
display.display();
}
else
{
Serial.print("HTTP Hatası: ");
Serial.println(httpResponseCode);
}
http.end();
}
else
{
Serial.println("Wi-Fi bağlantısı kaybedildi!");
}
}
if (data == 'b')
{
turnOffAllLEDs();
animateToRed();
fadeToRedOff();
fadeToWhite();
}
else if (data == 'c')
{
speechAnimation();
}
else if (data == 'v')
{
fadeToOrangeOff();
}
}
}
void turnOffAllLEDs()
{
for (int i = 0; i < LED_COUNT; i++)
{
strip.setPixelColor(i, strip.Color(0, 0, 0));
}
strip.show();
}
void animateToRed()
{
for (int i = 0; i < LED_COUNT; i++)
{
strip.setPixelColor(i, strip.Color(255, 0, 0));
strip.show();
delay(30);
}
}
void fadeToRedOff()
{
for (int step = 255; step >= 0; step--)
{
for (int i = 0; i < LED_COUNT; i++)
{
uint8_t red = step; // Kırmızı değeri azalır
strip.setPixelColor(i, strip.Color(red, 0, 0)); // Kırmızıdan sönme
}
strip.show();
delay(5); // Kırmızıyı sönme hızını ayarlayabilirsiniz
}
}
// Kırmızıdan beyaza animasyonlu geçiş
void fadeToWhite()
{
for (int step = 0; step <= 255; step++)
{
for (int i = 0; i < LED_COUNT; i++)
{
// Kırmızıdan beyaza geçiş (kırmızı azalır, beyaz artar)
uint8_t red = 255 - step; // Kırmızı yavaşça azalır
uint8_t white = step; // Beyaz yavaşça artar
strip.setPixelColor(i, strip.Color(white, white, white)); // Beyaz renk
}
strip.show();
delay(5); // Geçiş hızı
}
}
void speechAnimation()
{
// Turuncu renge geçiş yap
for (int i = 0; i < LED_COUNT; i++)
{
strip.setPixelColor(i, strip.Color(255, 150, 0)); // Turuncu
}
strip.show();
delay(50); // Turuncuya geçiş için kısa bir süre
for (int i =0;i <= 5; i++)
{
// Parlaklık seviyesini hızla artırıp azaltarak animasyon yap
for (int brightness = 200; brightness <= 255; brightness += 15)
{ // Parlaklığı %20'den %100'e kadar artır
for (int i = 0; i < LED_COUNT; i++)
{
strip.setPixelColor(i, strip.Color(brightness, 150, 0)); // Turuncu renk (R:255, G:165, B:0)
}
strip.show();
delay(70); // Hızlı geçiş için kısa bir süre
}
for (int brightness = 255; brightness >= 200; brightness -= 15)
{ // Parlaklığı %100'den %20'ye kadar azalt
for (int i = 0; i < LED_COUNT; i++)
{
strip.setPixelColor(i, strip.Color(brightness, 150, 0)); // Turuncu renk
}
strip.show();
delay(70); // Hızlı geçiş için kısa bir süre
}
}
}
void fadeToOrangeOff()
{
for (int step = 150; step >= 0; step--)
{
for (int i = 0; i < LED_COUNT; i++)
{
// Kırmızıdan azalırken, yeşil ve mavi komponentleri sıfır
uint8_t orange = step; // Kırmızı değeri azalır
strip.setPixelColor(i, strip.Color(orange, orange, 0)); // Kırmızıdan sönme
}
strip.show();
delay(5); // Kırmızıyı sönme hızını ayarlayabilirsiniz
}
Projenin Sonuçları
Projemiz sayesinde, sesli asistanların kullanıcı deneyimini artıran fiziksel geri bildirim mekanizmaları geliştirilebilir. OLED ekran ve WS2812 LED halkası sayesinde komutların algılanıp algılanmadığını görmek mümkün hale gelir. Böylece sadece sesli yanıt yerine görsel destek ile kullanım daha keyifli hale getirilir.
Bu tarz projeler, akıllı ev sistemleri ve IoT tabanlı asistanlar için önemli bir gelişim alanı sunmaktadır. Projenizi geliştirerek kendi sesli asistan ara yüzünüzü oluşturabilirsiniz!
Keyifli Seyirler!