Thursday, August 29, 2024

DDC/CI, Arduino ve Python ile monitör ayarlarının analog kontrolü.

Günümüzün modern monitörlerinde en sevmediğim şey menü ergonometrisi ve ayarlara ulaşmanın zorluğu. 

Mevcut Dell monitörümde parlaklık - kontrast gibi en temel ayarları yapmak için kullanılan menü:

Bu tarz menülerde en basit ve en çok kullanılan iki ayar olan brightness ve contrast ayarını yapmak çok zahmetli.

Bu noktada imdadımıza DDC/CI protokolü yetişiyor. DDC/CI ile monitöre işletim sistemi üzerinden ulaşıp görüntü ayarlarını yazılımla kontrol etmek mümkün. Tabii monitörün bu protokolü desteklemesi gerektiğini söylemeye gerek yok. 

Bu iş için Windows masaüstünde yıllardır ScreenBright aracını kullanıyorum. Yazının devamında bahsedeceğim donanım çözümüyle uğraşmak istemeyenler için gayet yeterli. 


Yazının başında modern monitör ifadesini özellikle kullandım. 80'ler ve 90'larda tüplü monitörler analog şase tabir edilen yapıda olur ve en azından parlaklık ve kontrast ayarları basitçe kasa üzerindeki analog potansiyometrelerle yapılırdı. O günlerde bilgisayar kullanmış olanlar bu kontrol yapısının ne kadar kullanışlı olduğunu hatırlayacaklardır.



İşte böyle çok daha pratik bir yöntemi günümüzün monitörlerinde simüle etmek için DDC'yi analog potansiyometrelerle kontrol eden bir devre tasarladım. Devre Arduino geliştirme boardu ve işletim sistemi tarafında Python kullanıyor. Kabaca Arduino analog girişlerine bağlı potansiyometre voltaj değerlerini okuyup digital değerlere çevirdikten sonra bu değerleri seri iletişimle PC'ye gönderiyor. PC'deki python kodu da bu değerleri alıp DDC'yi kontrol ediyor. Neyse ki python'ın sonsuz evreninde gerekli tüm kütüphanaler mevcut. 

Devre için gerekenler 
  • Arduino Uno (bendeki klon),
  • İki adet 10kΩ potansiyometre, 
  • Breadboard, arduino - pc arası usb kablo ve birkaç tane erkek-erkek jumper kablosu.
Python kütüphaneleri:
  • tkinter
  • monitorcontrol
  • serial
  • threading
her biri command prompt veya vs code console gibi bir yerde
pip install library_adı
komutuyla kurulacak. Eğer sisteminizde pip kurulu değilse pip kurulumu için tıklayın. Bu kılavuzu yazdığım gün Win 11 altında Python 3.11.7 versiyonu kuruluydu ve herhangi bir sorunla karşılaşmadım.

Arduino bağlantı şeması:


Arduino kodu:

// Potansiyometre giriş pinleri
const int brightnessPotPin = A0; // Parlaklık 
const int contrastPotPin = A1;   // Kontrast 

void setup() {
  Serial.begin(9600); // Seri iletişim başlat
}

void loop() {
  // Potansiyometrelerden analog değerleri oku
  int brightnessValue = analogRead(brightnessPotPin);
  int contrastValue = analogRead(contrastPotPin);

  // Analog değeri 0-1023 aralığından 0-100 aralığına dönüştür
  int brightnessPercentage = map(brightnessValue, 0, 1023, 0, 100);
  int contrastPercentage = map(contrastValue, 0, 1023, 0, 100);

  // Değerleri seri port üzerinden gönder
  Serial.print("B:"); 
  Serial.print(brightnessPercentage);
  Serial.print(" C:"); 
  Serial.println(contrastPercentage);

  delay(500); // 100 ms bekle (ben en iyi sonucu 500ms'de aldım)
}
Python kodu:

import tkinter as tk
from monitorcontrol import get_monitors
import threading
import serial

# Monitör kontrolü için monitorcontrol kütüphanesi
monitors = get_monitors()

# Seri portu aç 
ser = serial.Serial('COM3', 9600)  # COM3 yerine doğru portu girin

# Tkinter ana pencere 
root = tk.Tk()
root.title("OSD")
root.geometry("200x100")
root.overrideredirect(True)  # Pencere kenarlıklarını ve üst barı kaldır
root.attributes("-topmost", True)  # Pencere her zaman üstte olsun
root.attributes("-alpha", 0.7)  # Transparanlık ayarı (0.0 - 1.0)

# OSD pozisyonunu ekranın sağ üst köşesine ayarla
screen_width = root.winfo_screenwidth()
root.geometry(f"+{screen_width-220}+50")  # Sağ üst köşe (+x+y)

# Zamanlayıcı için bir referans
osd_timer = None

# OSD güncelleme fonksiyonları
def update_osd_brightness(brightness):
    brightness_label.config(text=f"Brightness: {brightness}")
    with monitors[0] as monitor:
        monitor.set_luminance(brightness)
    show_osd()

def update_osd_contrast(contrast):
    contrast_label.config(text=f"Contrast: {contrast}")
    with monitors[0] as monitor:
        monitor.set_contrast(contrast)
    show_osd()

# OSD göster ve zamanlayıcı başlat
def show_osd():
    global osd_timer
    root.deiconify()  # OSD'yi göster

    if osd_timer:
        root.after_cancel(osd_timer)  # Mevcut zamanlayıcı varsa iptal et
        # 3 saniye sonra OSD'yi gizle
    osd_timer = root.after(3000, hide_osd)

# OSD gizle
def hide_osd():
    root.withdraw()  # OSD'yi gizle

# Etiketler (label)
brightness_label = tk.Label(root, text="Brightness: 50", font=("Helvetica", 12), bg="black", fg="white")
contrast_label = tk.Label(root, text="Contrast: 50", font=("Helvetica", 12), bg="black", fg="white")

brightness_label.pack(pady=5)
contrast_label.pack(pady=5)

# Arduino'dan gelen verileri dinleyen fonksiyon
def read_from_arduino():
    while True:
        if ser.in_waiting > 0:
            data = ser.readline().decode('utf-8').strip()
            if data.startswith("B:") and "C:" in data:
                parts = data.split()
                brightness = int(parts[0].split(":")[1])
                contrast = int(parts[1].split(":")[1])
                print(f"Brightness: {brightness}, Contrast: {contrast}")
                update_osd_brightness(brightness)
                update_osd_contrast(contrast)

# Arduino'dan gelen verileri işlemek için ayrı bir thread başlat
arduino_thread = threading.Thread(target=read_from_arduino)
arduino_thread.daemon = True  # Ana program kapandığında thread'i durdur
arduino_thread.start()

# Başlangıçta OSD penceresini gizle
root.withdraw()

# Tkinter ana döngüsü
root.mainloop()

Potansiyometrelerden gelen verilerin serial monitor'de izlenmesi:


OSD (On screen display) penceresindeki değişim:



Breadbord fotosu:


Yapılacaklar:    
  • Threading seri data akışıyla sürekli tetiklendiği için OSD 3 saniyede kaybolmak yerine sürekli ekranda kalma problemi düzeltilecek,
  • Uygulama Win32 platformu için tekrar yazılarak optimize edilecek, 
  • Prototip olarak kullanılan Arduino ekosistemini yerine bağımsız MCU devresi tasarlanıp üretilecek,
  • Aynı projenin STM/ARM - HID versiyonu geliştirilecek.